DIRECTORY AMTypes USING [TV], BBContext USING [GlobalFrameSearch], BBObjectLocation USING [EntryLocation, ExitLocation, Location, TVToLocation], BBVOps USING [LocationFromSelection, SourceError], Buttons USING [Button, ButtonProc, Create, ReLabel], Commander USING [CommandProc, Register], Containers USING [ChildXBound, ChildYBound, Container], Convert USING [Parse, Value], Graphics USING [ black, Box, ClipArea, Context, CurveTo, DrawArea, DrawBox, DrawRope, DrawStroke, FontRef, GetBounds, MoveTo, NewPath, Path, Rotate, SetColor, SetCP, SetPaintMode, Translate, white], GraphicsBasic USING [ClipperRef], GraphicsOps USING [BitmapRef, DrawBitmap, GetClipper, NewBitmap, NewContextFromBitmap, SetClipper], Icons USING [DrawIconProc, IconFlavor, IconRef, IconRep, NewIcon], IO USING [char, Close, CR, GetOutputStreamRope, int, Put, rope, ROS, STREAM], Labels USING [Create, Label, Set], LongString USING [AppendString], PSB USING [PsbIndex, PsbNull], Rope USING [Equal, Flatten, Index, Length, ROPE, Text], Runtime USING [GetBcdTime, IsBound], SpyClient USING [ ClearBreaks, ClearUserBreaks, DataType, DisplayData, InitializeSpy, SetStartBreak, SetStopBreak, SetUserBreak, StartSpy, StopSpy], SpyLog USING [spy], Time USING [Append, Unpack], TypeScript USING [Create], VFonts USING [EstablishFont, Font, GraphicsFont], ViewerClasses USING [InitProc, Viewer, ViewerClass, ViewerClassRec], ViewerIO USING [CreateViewerStreams], ViewerOps USING [ChangeColumn, CreateViewer, DestroyViewer, FetchViewerClass, OpenIcon, PaintViewer, RegisterViewerClass], ViewerTools USING [GetContents, GetSelectionContents, MakeNewTextViewer, SelPos, SetSelection]; SpyViewerImpl: MONITOR IMPORTS BBContext, BBObjectLocation, BBVOps, Buttons, Commander, Containers, Convert, Graphics, GraphicsOps, Icons, IO, Labels, LongString, Rope, Runtime, SpyClient, SpyLog, Time, TypeScript, VFonts, ViewerIO, ViewerOps, ViewerTools SHARES IO = BEGIN OPEN IO; ROPE: TYPE = Rope.ROPE; TV: TYPE = AMTypes.TV; -- Global Variables -- typescript: IO.STREAM _ NIL; typescriptBox: ViewerClasses.Viewer _ NIL; spyViewer: Containers.Container _ NIL; iconFlavor: Icons.IconFlavor; iconBitmap: GraphicsOps.BitmapRef; CreateSpyClass: PROCEDURE = BEGIN icon: Icons.IconRef; context: Graphics.Context; spyClass: ViewerClasses.ViewerClass; iconBitmap _ GraphicsOps.NewBitmap[64, 64]; context _ GraphicsOps.NewContextFromBitmap[iconBitmap]; PaintMagnifier[context, "Spy"]; icon _ NEW[Icons.IconRep _ [bits: ALL[0], proc: PaintSpyIcon]]; iconFlavor _ Icons.NewIcon[icon]; spyClass _ NEW[ViewerClasses.ViewerClassRec _ []]; spyClass^ _ ViewerOps.FetchViewerClass[$Container]^; ContainerInit _ spyClass.init; spyClass.init _ CreateSpyViewer; spyClass.icon _ iconFlavor; ViewerOps.RegisterViewerClass[$Spy, spyClass]; Commander.Register["Spy", Spy, "performance measurement tool."]; END; ContainerInit: ViewerClasses.InitProc; PaintSpyIcon: Icons.DrawIconProc = CHECKED BEGIN Graphics.SetCP[context, 0, 64]; GraphicsOps.DrawBitmap[context, iconBitmap, 64, 64]; END; PaintMagnifier: PROCEDURE[context: Graphics.Context, name: Rope.ROPE] = BEGIN box: Graphics.Box; vfont: VFonts.Font; clipper: GraphicsBasic.ClipperRef; cx1, cy1, r1, cx2, cy2, r2: REAL; timesroman10B, timesroman18B: Graphics.FontRef; cx1 _ 28; cy1 _ 40; r1 _ 20; cx2 _ 24; cy2 _ 37; r2 _ 20; box _ Graphics.GetBounds[context]; Graphics.DrawBox[context, box]; box.xmin _ box.xmin+1; box.ymin _ box.ymin+1; box.xmax _ box.xmax-1; box.ymax _ box.ymax-1; Graphics.SetColor[context, Graphics.white]; Graphics.DrawBox[context, box]; vfont _ VFonts.EstablishFont["TimesRoman", 10, TRUE]; timesroman10B _ VFonts.GraphicsFont[vfont]; Graphics.SetCP[context, cx1+5, cy1-2]; Graphics.SetColor[context, Graphics.black]; Graphics.DrawRope[context, name,,,timesroman10B]; Graphics.SetColor[context, Graphics.white]; Graphics.DrawArea[context, CreateCircle[cx1, cy1, r1]]; Graphics.SetColor[context, Graphics.black]; [] _ Graphics.SetPaintMode[context, invert]; Graphics.DrawArea[context, CreateCircle[cx1, cy1, r1]]; Graphics.DrawArea[context, CreateCircle[cx2, cy2, r2]]; vfont _ VFonts.EstablishFont["TimesRoman", 18, TRUE]; timesroman18B _ VFonts.GraphicsFont[vfont]; clipper _ GraphicsOps.GetClipper[context]; [] _ Graphics.SetPaintMode[context, opaque]; Graphics.DrawStroke[context, CreateCircle[cx1, cy1, r1], 0, TRUE]; Graphics.ClipArea[context, CreateCircle[cx2, cy2, r2]]; Graphics.SetCP[context, cx1-3, cy1-4]; Graphics.DrawRope[context, name,,,timesroman18B]; GraphicsOps.SetClipper[context, clipper]; Graphics.Translate[context, cx1, cy1]; -- center Graphics.Rotate[context, 30]; box _ [-3, -r1-20, 3, -r1]; Graphics.DrawBox[context, box]; END; CreateCircle: PROCEDURE[x, y, r: REAL] RETURNS[path: Graphics.Path] = BEGIN h: REAL _ 4*r/3; path _ Graphics.NewPath[]; Graphics.MoveTo[path, x-r, y]; Graphics.CurveTo[path, x-r, y+h, x+r, y+h, x+r, y]; Graphics.CurveTo[path, x+r, y-h, x-r, y-h, x-r, y]; END; Spy: Commander.CommandProc = TRUSTED BEGIN IF spyViewer = NIL OR spyViewer.destroyed THEN spyViewer _ ViewerOps.CreateViewer[flavor: $Spy, info: [name: "Spy", scrollable: FALSE, iconic: TRUE]] ELSE IF spyViewer.iconic THEN ViewerOps.OpenIcon[spyViewer] ELSE ViewerOps.PaintViewer[spyViewer, all]; IF spyViewer.offDeskTop THEN ViewerOps.ChangeColumn[spyViewer, left]; END; CreateSpyViewer: ViewerClasses.InitProc = TRUSTED BEGIN x:INTEGER = 10; -- upper left corner of commands y:INTEGER = 10; sp:INTEGER = 6; line:INTEGER = 20; -- height of each line title: STRING = "Spy of "L; herald: STRING _ [40]; IF spyViewer # NIL AND ~spyViewer.destroyed THEN RETURN; LongString.AppendString[to: herald, from: title]; Time.Append[herald, Time.Unpack[Runtime.GetBcdTime[]]]; herald.length _ herald.length - 3; ContainerInit[self]; typescriptBox _ TypeScript.Create[ info: [wx: 0, wy: y + 4*line, ww: 70*sp+x-5, wh: 40*line, parent: self]]; typescript _ ViewerIO.CreateViewerStreams["Spy.Log",typescriptBox].out; Containers.ChildXBound[self, typescriptBox]; Containers.ChildYBound[self, typescriptBox]; spyOnButton _ Buttons.Create[ proc: ToggleSpy, documentation: "Spy records data only when this is 'on'.", info: [name: "Spy: {off} ", wx: x, wy: y, border: FALSE, parent: self]]; SetSpyButton[]; dataTypeButton _ Buttons.Create[ proc: ToggleType, documentation: "Gives the resource to be monitored. Toggle through for list.", info: [name: "Watching: {CPU} ", wx: x + 15*sp, wy: y, border: FALSE, parent: self]]; SetTypeButton[]; processBoxX _ 475 - 34*sp; processBoxY _ y + 1; processBoxW _ 8*sp; processBoxH _ line; bracketLabel _ Labels.Create[ info: [name: " ", -- used to print the trailing bracket when dataType = process wx: processBoxX + processBoxW, wy: processBoxY, border: FALSE, parent: self]]; tablesLabel _ Labels.Create[ info: [name: " ", -- indicates when tables are being built wx: processBoxX + processBoxW + 3*sp, wy: y, border: FALSE, parent: self]]; displayButton _ Buttons.Create[ fork: TRUE, documentation: "Displays the current data on a separate typescript.", proc: DisplayData, info: [name: "DisplayData!", wx: x, wy: y + line, parent: self]]; cutoffButton _ Buttons.Create[ proc: ToggleCutoff, documentation: "Specifies percentage cutoff for printing.", info: [name: "cutoff: {100}", wx: x + 15*sp, wy: y + line, border: FALSE, parent: self]]; SetCutoffButton[]; setUserBreakButton _ Buttons.Create[ proc: SetUserBreak, documentation: "Spy will count the given breaks as they are encountered.", info: [name: "SetUserBreak!", wx: x, wy: y + 2*line, parent: self]]; clearUserBreaksButton _ Buttons.Create[ documentation: "Clears the user breaks given.", proc: ClearUserBreaks, info: [name: "ClearUserBreaks!", wx: x + 15*sp, wy: y + 2*line, parent: self]]; setStartBreakButton _ Buttons.Create[ proc: SetStartBreak, documentation: "Select a source or a rope of the form ModuleImpl.Proc.", info: [name: "SetStartBreak!", wx: x, wy: y + 3*line, parent: self]]; setStopBreakButton _ Buttons.Create[ proc: SetStopBreak, documentation: "Spy will record data as long as the number of start breaks exceeds the number of stops.", info: [name: "SetStopBreak!", wx: x + 15*sp, wy: y + 3*line, -- 31 parent: self]]; clearBreaksButton _ Buttons.Create[ proc: ClearBreaks, documentation: "Clears the breaks given by user; resets the mode.", info: [name: "ClearBreaks!", wx: x + 31*sp, wy: y + 3*line, -- 47 parent: self]]; END; break: BOOLEAN _ FALSE; spyOn: BOOLEAN _ FALSE; displayButton: Buttons.Button _ NIL; spyOnButton: Buttons.Button _ NIL; tablesLabel: Labels.Label _ NIL; bracketLabel: Labels.Label _ NIL; SetSpyButton: PROCEDURE = BEGIN SELECT TRUE FROM break AND spyOn => { IF ~Rope.Equal[spyOnButton.name, "Break: {on}"] THEN typescript.Put[char[CR], rope["Start breaks enabled."], char[CR]]; Buttons.ReLabel[spyOnButton, "Break: {on}"]}; break AND ~spyOn => { IF ~Rope.Equal[spyOnButton.name, "Break: {off}"] THEN typescript.Put[char[CR], rope["Start breaks disabled."], char[CR]]; Buttons.ReLabel[spyOnButton, "Break: {off}"]}; ~break AND spyOn => { IF ~Rope.Equal[spyOnButton.name, "Spy: {on}"] THEN typescript.Put[char[CR], rope["Spy started."], char[CR]]; Buttons.ReLabel[spyOnButton, "Spy: {on}"]}; ENDCASE => { IF ~Rope.Equal[spyOnButton.name, "Spy: {off}"] THEN typescript.Put[char[CR], rope["Spy stopped."], char[CR]]; Buttons.ReLabel[spyOnButton, "Spy: {off}"]}; END; ToggleSpy: Buttons.ButtonProc = TRUSTED BEGIN SELECT spyOn FROM TRUE => StopSpy[]; FALSE => StartSpy[]; ENDCASE => ERROR; END; StopSpy: PROCEDURE = BEGIN spyOn _ FALSE; SpyClient.StopSpy[]; SetSpyButton[]; END; StartSpy: PROCEDURE = BEGIN error: ROPE _ NIL; process: PSB.PsbIndex _ PSB.PsbNull; Labels.Set[tablesLabel,"initializing"]; IF dataType = breakProcess AND ~break THEN error _ "Watching break process but no breaks set!"; IF dataType = process THEN { process _ ReadOctal[ViewerTools.GetContents[processBox]]; IF process = PSB.PsbNull THEN error _ "No process specified."}; IF error = NIL THEN error _ SpyClient.InitializeSpy[ dataType: dataType, process: process]; Labels.Set[tablesLabel, NIL]; IF error # NIL THEN {typescript.Put[char[CR],rope[error],char[CR]]; RETURN}; spyOn _ TRUE; SetSpyButton[]; SpyClient.StartSpy[]; END; ReadOctal: PROCEDURE[r: ROPE] RETURNS[CARDINAL] = BEGIN value: Convert.Value; value _ Convert.Parse[[rope[r]], [unsigned[0,8]]].value; WITH v: value SELECT FROM unsigned => RETURN[v.unsigned]; signed => IF v.signed < 0 THEN RETURN[0] ELSE RETURN[v.signed]; ENDCASE => RETURN[0]; END; dataType: SpyClient.DataType _ CPU; dataTypeButton: Buttons.Button _ NIL; processBox: ViewerClasses.Viewer _ NIL; processBoxX, processBoxY, processBoxW, processBoxH: CARDINAL; SetTypeButton: PROCEDURE = BEGIN Buttons.ReLabel[dataTypeButton, SELECT dataType FROM CPU => "Watching: {CPU}", process => "Watching: {process number: ", breakProcess => "Watching: {break process}", pagefaults => "Watching: {pagefaults}", allocations => "Watching: {allocations}", wordsAllocated => "Watching: {wordsAllocated}", spaces => "Watching: {spaces}", userDefined => "Watching: {user breaks}", ENDCASE => ERROR]; IF dataType = process AND (processBox = NIL OR processBox.destroyed) THEN { processBox _ ViewerTools.MakeNewTextViewer[ info: [wx: processBoxX, wy: processBoxY, ww: processBoxW, wh: processBoxH, border: FALSE, scrollable: FALSE, parent: spyViewer]]; Labels.Set[bracketLabel, "}"]; ViewerTools.SetSelection[processBox]}; IF dataType # process AND processBox # NIL AND ~processBox.destroyed THEN { ViewerOps.DestroyViewer[processBox]; Labels.Set[bracketLabel, " "]}; END; ToggleType: Buttons.ButtonProc = TRUSTED BEGIN dataType _ SELECT mouseButton FROM red => IF dataType = LAST[SpyClient.DataType] THEN FIRST[SpyClient.DataType] ELSE SUCC[dataType], blue => IF dataType = FIRST[SpyClient.DataType] THEN LAST[SpyClient.DataType] ELSE PRED[dataType], ENDCASE => CPU; SetTypeButton[]; END; cutoff: CARDINAL _ 3; cutoffButton: Buttons.Button; ToggleCutoff: Buttons.ButtonProc = TRUSTED BEGIN SELECT mouseButton FROM blue => IF cutoff > 0 THEN cutoff _ cutoff - 1; yellow => cutoff _ 3; red => IF cutoff < 100 THEN cutoff _ cutoff + 1; ENDCASE; SetCutoffButton[]; END; SetCutoffButton: PROC = BEGIN stream: IO.STREAM; stream _ IO.ROS[]; stream.Put[rope["cutoff: {"], int[cutoff], rope["}"]]; Buttons.ReLabel[cutoffButton, IO.GetOutputStreamRope[stream]]; stream.Close[]; END; DisplayData: Buttons.ButtonProc = TRUSTED BEGIN OPEN BBObjectLocation; IF spyOn THEN StopSpy[]; IF control THEN SpyLog.spy _ TRUE; SpyClient.DisplayData[cutoff, NIL, NIL]; IF control THEN SpyClient.DisplayData[cutoff, NIL, NIL]; END; setStartBreakButton: Buttons.Button _ NIL; setStopBreakButton: Buttons.Button _ NIL; clearBreaksButton: Buttons.Button _ NIL; ClearBreaks: Buttons.ButtonProc = TRUSTED BEGIN SpyClient.ClearBreaks[]; typescript.Put[char[CR], rope["Start and Stop Breaks cleared."], char[CR]]; break _ FALSE; StopSpy[]; END; SetStartBreak: Buttons.ButtonProc = TRUSTED BEGIN msg: ROPE; ok: BOOLEAN _ FALSE; loc: BBObjectLocation.Location; [loc, msg] _ LocationFromSelection[entry: TRUE]; IF loc # NIL THEN [ok, msg] _ SpyClient.SetStartBreak[,loc]; typescript.Put[char[CR], rope[msg], char[CR]]; IF ~ok THEN RETURN; break _ TRUE; StopSpy[]; END; SetStopBreak: Buttons.ButtonProc = TRUSTED BEGIN msg: ROPE; ok: BOOLEAN _ FALSE; loc: BBObjectLocation.Location; [loc, msg] _ LocationFromSelection[exit: TRUE]; IF loc # NIL THEN [ok, msg] _ SpyClient.SetStopBreak[,loc]; typescript.Put[char[CR], rope[msg], char[CR]]; IF ~ok THEN RETURN; break _ TRUE; StopSpy[]; END; setUserBreakButton: Buttons.Button _ NIL; clearUserBreaksButton: Buttons.Button _ NIL; ClearUserBreaks: Buttons.ButtonProc = TRUSTED BEGIN SpyClient.ClearUserBreaks[]; typescript.Put[char[CR], rope["User Breaks cleared."], char[CR]]; IF dataType = userDefined THEN StopSpy[]; END; SetUserBreak: Buttons.ButtonProc = TRUSTED BEGIN msg: ROPE; ok: BOOLEAN _ FALSE; loc: BBObjectLocation.Location; [loc, msg] _ LocationFromSelection[]; IF loc # NIL THEN [ok, msg] _ SpyClient.SetUserBreak[,loc]; typescript.Put[char[CR], rope[msg], char[CR]]; IF ~ok THEN RETURN; StopSpy[]; IF dataType = userDefined THEN RETURN; dataType _ userDefined; SetTypeButton[]; END; LocationFromSelection: PROC[entry, exit: BOOLEAN _ FALSE] RETURNS[loc: BBObjectLocation.Location _ NIL, msg: Rope.ROPE _ NIL] = BEGIN selection: Rope.ROPE _ ViewerTools.GetSelectionContents[]; IF Runtime.IsBound[BBVOps.LocationFromSelection] THEN { loc _ BBVOps.LocationFromSelection[NIL, entry, exit ! BBVOps.SourceError => CONTINUE]; IF loc = NIL THEN msg _ "couldn't get a location out of the selection."}; IF loc = NIL THEN { tv: AMTypes.TV; module, proc: Rope.ROPE; [module, proc] _ Split[selection]; IF proc # NIL THEN tv _ BBContext.GlobalFrameSearch[NIL, module, proc].tv; IF tv # NIL THEN loc _ BBObjectLocation.TVToLocation[tv]; IF loc # NIL AND entry THEN loc _ BBObjectLocation.EntryLocation[loc]; IF loc # NIL AND exit THEN loc _ BBObjectLocation.ExitLocation[loc]}; IF loc = NIL AND msg = NIL THEN msg _ "Please load BugBane.bcd."; END; Split: PROC [name: ROPE] RETURNS [Rope.Text, Rope.Text] = INLINE { length: INT _ name.Length[]; pos: INT _ name.Index[0, "."]; IF pos = length THEN RETURN[name.Flatten[], NIL]; RETURN[name.Flatten[0, pos], name.Flatten[pos + 1, length]]}; CreateSpyClass[]; [] _ Spy[NIL]; END. LocationFromSelection: PROC RETURNS [loc: BBObjectLocation.Location] = { {-- stolen from Russ's BBVExec (who should export this?) OPEN BBObjectLocation; ENABLE {UNWIND => NULL; ANY => GO TO oops}; start: LONG INTEGER; selPos: ViewerTools.SelPos; gf, tv: AMTypes.TV _ NIL; viewer: ViewerClasses.Viewer; module, proc: ROPE _ NIL; viewer _ ViewerTools.GetSelectedViewer[]; proc _ ViewerTools.GetSelectionContents[]; selPos _ ViewerTools.GetSelection[viewer]; IF viewer = NIL THEN GO TO oops; start _ selPos.start; [module,] _ Split[viewer.name]; [gf, tv] _ BBContext.GlobalFrameSearch[NIL, module, NIL]; IF gf # NIL THEN {-- we found such a frame! loc: Location _ SourceToLocation[gf, Inline.LowHalf[start]]; proc: TV _ TVFromLocation[loc]; RETURN[loc]}; module _ Rope.Cat[module, "Impl"]; [gf, tv] _ BBContext.GlobalFrameSearch[NIL, module, proc]; IF tv = NIL THEN { -- perhaps the selection is of the form Module.Proc? [module, proc] _ Split[proc]; IF proc # NIL THEN [gf, tv] _ BBContext.GlobalFrameSearch[NIL, module, proc]}; IF tv # NIL THEN RETURN[TVToLocation[tv]]; EXITS oops => {}}; RETURN [NIL]}; Split: PROC [name: ROPE] RETURNS [Rope.Text, Rope.Text] = { length: INT _ name.Length[]; pos: INT _ name.Index[0, "."]; IF pos = length THEN RETURN[name.Flatten[], NIL]; RETURN[name.Flatten[0, pos], name.Flatten[pos + 1, length]]}; ~SpyViewerImpl.mesa Edited by Maxwell, April 28, 1983 1:04 pm ************************************************** class and icon initialization ************************************************** erase old write in name overwrite magnifier invert for magnifier write in NAME draw in handle ************************************************** spy viewer creation ************************************************** typescript first line second line third line fourth line ************************************************** turning the spy off and on ************************************************** ************************************************** determine the data type ************************************************** ************************************************** displaying data ************************************************** ************************************************** setting and clearing start and stop breaks ************************************************** set your breaks and then turn the spy on as long as there have been more start breaks than stop breaks the spy will keep on counting turning the spy off will stop the counting (although not the breaks). hit ClearBreaks to delete breaks. ************************************************** setting and clearing user breaks ************************************************** ************************************************** utility procedures ************************************************** foundOne: SAFE PROC [lgf, ltv: TV, actualName: ROPE] RETURNS [BOOLEAN] = CHECKED {gf _ lgf; tv _ ltv; module _ actualName; RETURN [TRUE]}; maybe its a definition file? Ê~˜Jšœ™Jšœ)™)J˜šÏk ˜ Jšœœœ˜Jšœ œ˜$Jšœœ7˜MJšœœ&˜2Jšœœ'˜4Jšœ œ˜(Jšœ œ'˜7Jšœœ˜šœ œ˜J˜µ—Jšœœ˜!šœ œ%˜6J˜-—Jšœœ7˜BJš œœœ'œœ˜MJšœœ˜"Jšœ œ˜ Jšœœ˜Jšœœ!œ˜7Jšœœ˜$šœ œ˜J˜‚—Jšœœ˜Jšœœ˜Jšœ œ ˜Jšœœ%˜1Jšœœ1˜DJšœ œ˜%Jšœ œk˜zJšœ œN˜_—J˜šœ˜š˜Jšœlœr˜à—Jšœœ˜ J˜—Jš˜Jšœœ˜J˜Jšœœœ˜Jšœœ œ˜J˜JšÏc˜J˜Jšœ œœœ˜Jšœ&œ˜*Jšœ"œ˜&J˜J˜J˜"J˜Jšž2™2Jšœ™Jšž2™2J˜šÏnœ œ˜Jš˜J˜J˜J˜$J˜+J˜7J˜Jšœœœ˜?J˜!Jšœ œ$˜2J˜4J˜J˜ J˜J˜.J˜@Jšœ˜—J˜šÏb œ˜&J˜—š  œ˜"Jšœ˜ J˜J˜4Jšœ˜J˜—šŸœ œ'œ˜GJš˜J˜J˜J˜"Jšœœ˜!J˜/J˜J˜Jšœ ™ J˜"J˜J˜-J˜-J˜+J˜Jšœ ™ Jšœ/œ˜5J˜+J˜&J˜+J˜1Jšœ™J˜+J˜7J˜+Jšœ™J˜,J˜7J˜7Jšœ ™ Jšœ/œ˜5J˜+J˜*J˜,Jšœ<œ˜BJ˜7J˜&J˜1J˜)Jšœ™Jšœ'ž ˜0J˜J˜J˜ Jšœ˜J˜—šŸ œ œ œœ˜FJš˜Jšœœ ˜J˜J˜J˜3J˜3Jšœ˜—J˜Jšž2™2J™Jšž2™2J˜š œœ˜*šœ œœ˜*šœ2˜6Jšœ œ œ˜5—šœœ˜Jšœ˜"Jšœ'˜+——Jšœœ)˜EJšœ˜J˜—š œ˜)Jšœ˜ Jšœœž ˜0Jšœœ˜Jšœœ˜Jšœœž˜)Jšœœ˜Jšœœ˜Jš œ œœœœ˜8J˜1J˜7J˜"J™Jšœ ™ J˜˜"˜J˜J˜——J˜GJ˜,Jšœ,˜,J˜Jšœ ™ ˜J˜J˜:˜J˜Jšœœ˜J˜——J˜˜ J˜J˜O˜+J˜Jšœœ˜J˜——Jšœ˜J˜J˜J˜J˜˜šœž=˜PJ˜/Jšœœ˜——˜šœ!ž(˜IJ˜,Jšœœ˜——J˜Jšœ ™ ˜Jšœœ˜ J˜EJ˜˜J˜J˜——˜J˜J˜;˜J˜Jšœœ˜J˜——Jšœ˜J˜Jšœ ™ ˜$J˜J˜J˜J˜J˜——˜'J˜/J˜˜ J˜J˜——J˜Jšœ ™ šœ%˜%J˜J˜I˜J˜J˜——˜$J˜J˜j˜Jšœž˜$J˜——˜#J˜J˜D˜Jšœ ž˜%J˜——Jšœ˜—J˜Jšž2™2J™Jšž2œ™3J˜Jšœœœ˜Jšœœœ˜Jšœ œ˜$Jšœœ˜"Jšœœ˜ Jšœœ˜!J˜šŸ œ œ˜Jš˜šœœ˜šœœ ˜šœ.˜0Jšœœ'œ˜G—Jšœ-˜-—šœœ ˜šœ/˜1Jšœœ(œ˜H—Jšœ.˜.—šœœ ˜šœ,˜.Jšœœœ˜>—Jšœ+˜+—šœ˜ šœ-˜/Jšœœœ˜>—Jšœ,˜,——Jšœ˜J˜—š  œ˜Jšœ˜ šœ˜Jšœ˜Jšœ˜Jšœœ˜—Jšœ˜J˜—šŸœ œ˜Jšœ˜Jšœœ˜J˜J˜Jšœ˜J˜—šŸœ œ˜Jš˜Jšœœœ˜Jšœ œ œ ˜$J˜'šœœœ˜+J˜4—šœœ˜J˜9Jšœ œ œ"˜?—šœ œœ!˜4J˜J˜—Jšœœ˜Jš œ œœœœœ˜LJšœœ˜ J˜J˜Jšœ˜J˜—š Ÿ œ œœœœ˜1Jš˜J˜J˜8šœ œ˜Jšœ œ ˜Jš œ œœœœœ ˜?Jšœœ˜—Jšœ˜J˜—J™Jšž2™2J™Jšž2™2J˜Jšœœ˜#Jšœ!œ˜%Jšœ#œ˜'Jšœ4œ˜=J˜šŸ œ œ˜Jš˜˜šœ ˜Jšœ˜Jšœ)˜)Jšœ,˜,Jšœ'˜'J˜)J˜/J˜J˜)Jšœœ˜——š œœœœœ˜Lšœ+˜+˜(J˜"Jšœœœ˜!Jšœ˜——Jšœ˜Jšœ'˜'—š œœœœœ˜KJ˜$Jšœ ˜ —Jšœ˜J˜—š  œ˜ Jšœ˜ šœ œ ˜"šœœ œ˜.Jšœœœœ ˜3—šœœ œ˜0Jšœœœœ ˜2—Jšœœ˜—J˜Jšœ˜—J˜Jšž2™2Jšž™Jšž2™2J˜Jšœœ˜Jšœ˜J˜š  œ˜#Jšœ˜ šœ ˜Jšœœ œ˜/J˜Jšœœœ˜0Jšœ˜—J˜Jšœ˜J˜—šŸœœ˜Jš˜Jšœœœ˜Jšœ œœ˜Jšœ6˜6Jšœœ˜>J˜Jšœ˜—J˜š  œ˜!Jšœ˜ Jšœ˜Jšœœ ˜Jšœ œœ˜"Jšœœœ˜(Jšœ œœœ˜8Jšœ˜—J˜Jšž2™2Jšž*™*Jšž2™2J˜Jšœ&œ˜*Jšœ%œ˜)šœ$œ˜(J˜—Jšœ(™(Jšœ=™=Jšœ™JšœE™EJšœ!™!J˜š  œ˜!Jšœ˜ J˜Jšœœ0œ˜KJšœœ˜J˜ Jšœ˜J˜—š  œ˜#Jšœ˜ Jšœœ˜ Jšœœœ˜J˜Jšœ*œ˜0Jšœœœ+˜