DIRECTORY Basics, CD, CDBasics, CDCells, CDDirectory, CDInstances, CDLayers, CDOps, CDPanelFonts, CDProperties, CDRects, CDSatellites, CDSequencer, CDTexts, Core, CoreOps, CoreGeometry, ExtractOps, GList, IO, Rope, RopeList, IconConstruction, Sisyph, TerminalIO, PWCore, Convert, RuntimeError; IconConstructionImpl: CEDAR PROGRAM IMPORTS Basics, CD, CDBasics, CDCells, CDDirectory, CDInstances, CDLayers, CDOps, CDPanelFonts, CDProperties, CDRects, CDSatellites, CDSequencer, CDTexts, CoreOps, CoreGeometry, ExtractOps, GList, IO, Rope, RopeList, Sisyph, TerminalIO, PWCore, Convert, RuntimeError EXPORTS IconConstruction ~ BEGIN NewShell: PUBLIC PROC [name: Rope.ROPE, pins: ARRAY CoreGeometry.Side OF LIST OF Rope.ROPE, design: CD.Design, pinLayer: CD.Layer _ CD.commentLayer, boxLayer: CD.Layer _ CD.commentLayer] RETURNS [shell: IconConstruction.Shell] ~ { pinWidth: CD.Number = CDLayers.LayerWidth[design, pinLayer]; shell _ NEW [IconConstruction.ShellRep _ [ name: name, pinLayer: pinLayer, boxLayer: boxLayer, boxWidth: CDLayers.LayerWidth[design, boxLayer], pinRect: [x: pinWidth, y: 4*pinWidth], guard: 4, grid: CDOps.GetGrid[design], font: CDPanelFonts.CurrentFont[design], pins: pins]]}; ShellObject: PUBLIC PROC [shell: IconConstruction.Shell] RETURNS [obj: CD.Object] ~ { obj _ ShellObjectInner[shell]}; ShellObjectInner: PROC [shell: IconConstruction.Shell, minSize: CD.Position _ [0,0]] RETURNS [obj: CD.Object] ~ { GridUp: PROC [x: CD.Number] RETURNS [v: CD.Number] ~ {v _ IF x>=0 THEN ((x+grid-1)/grid)*grid ELSE (x/grid)*grid}; GridDown: PROC [x: CD.Number] RETURNS [v: CD.Number] ~ {v _ IF x>=0 THEN (x/grid)*grid ELSE ((x-grid+1)/grid)*grid}; GridRectUp: PROC [r: CD.Rect] RETURNS [v: CD.Rect] ~ {v _ [x1: GridDown[r.x1], x2: GridUp[r.x2], y1: GridDown[r.y1], y2: GridUp[r.y2]]}; MakeTitle: PROC [text: Rope.ROPE, font: CDTexts.CDFont] RETURNS [inst: CD.Instance, size: CD.Position] ~ { fName: Rope.ROPE = font.supposedName; obj: CD.Object; IF Rope.IsEmpty[text] THEN RETURN [NIL, [0,0]]; IF Rope.Find[fName, "I", Rope.Length[fName]-1, FALSE]=-1 THEN font _ CDTexts.MakeFont[fName.Concat["I"], font.scaleI]; obj _ CDTexts.Create[text, font]; size _ CDBasics.SizeOfRect[obj.bbox]; inst _ CDInstances.NewInst[obj, [[-size.x/2, font.origin.y - font.height/2], original]]; size.x _ GridUp[(3*size.x)/2]; -- increase a bit the area taken by the title size.y _ GridUp[2*size.y]}; -- increase a bit the area taken by the title BuildSideGeometry: PROC [names: LIST OF Rope.ROPE, pinOrient, textOrient: CD.Orientation, textOffset: CD.Position] RETURNS [iList: CD.InstanceList, size: CD.Position]~ { pin: CD.Object = CDRects.CreateRect[shell.pinRect, shell.pinLayer]; -- all pins are the same step: CD.Position = CDBasics.OrientedSize[[GridUp[shell.font.height], 0], pinOrient]; -- between pins pinPos: CD.Position _ [0, 0]; iList _ NIL; WHILE names#NIL DO pinInst: CD.Instance = CDInstances.NewInst[pin, [pinPos, pinOrient]]; textInst: CD.Instance = CDInstances.NewInst[CDTexts.Create[names.first, shell.font], [CDBasics.AddPoints[pinPos, textOffset], textOrient]]; pinPos _ CDBasics.AddPoints[pinPos, step]; CDSatellites.Associate[master: pinInst, text: textInst]; iList _ CONS[pinInst, CONS[textInst, iList]]; names _ names.rest; ENDLOOP; IF iList#NIL THEN size _ CDBasics.SizeOfRect[GridRectUp[CDInstances.BoundingRectO[iList]]] ELSE size _ [0, 0]}; AddBoxSide: PROC [length: CD.Number, at: CD.Position, orient: CD.Orientation] ~ { CDProperties.PutInstanceProp[ CDCells.IncludeOb[cell: obj, ob: CDRects.CreateRect[[shell.boxWidth, length], shell.boxLayer], trans: [at, orient], mode: dontResize].newInst, Sisyph.mode.extractProcProp, $ExtractNull]}; AddInstances: PROC [insts: CD.InstanceList, moveBy: CD.Position] ~ { WHILE insts#NIL DO insts.first.trans.off _ CDBasics.AddPoints[insts.first.trans.off, moveBy]; [] _ CDCells.IncludeInstance[cell: obj, inst: insts.first, mode: dontResize]; insts _ insts.rest; ENDLOOP}; Center: PROC [mid, left, right, tot: CD.Number] RETURNS [v: CD.Number] ~ { v _ GridUp[(tot+left-mid-right+shell.font.height)/2]}; grid: CD.Number = shell.grid; fontCorrection: CD.Number = shell.font.origin.y - shell.font.height/2; -- CDTexts are crazy... guard: CD.Number = shell.guard+shell.boxWidth; -- the real guard margin: CD.Number = GridUp[2*(shell.pinRect.y+shell.boxWidth)]; -- left/right safety margin iLeft, iRight, iTop, iBottom: CD.InstanceList; -- list of instances for pins on all 4 sides sLeft, sRight, sTop, sBottom: CD.Position; -- bbox for the 4 sides title: CD.Instance; titleSize: CD.Position; box: CD.Rect; width, height: CD.Number; obj _ CDCells.CreateEmptyCell[]; [iTop, sTop] _ BuildSideGeometry[names: shell.pins[top], pinOrient: original, textOrient: rotate270, textOffset: [fontCorrection, -guard]]; [iBottom, sBottom] _ BuildSideGeometry[names: shell.pins[bottom], pinOrient: rotate180X, textOrient: rotate90X, textOffset: [fontCorrection, guard]]; [iRight, sRight] _ BuildSideGeometry[names: shell.pins[right], pinOrient: rotate90X, textOrient: mirrorX, textOffset: [-guard, fontCorrection]]; [iLeft, sLeft] _ BuildSideGeometry[names: shell.pins[left], pinOrient: rotate90, textOrient: original, textOffset: [guard, fontCorrection]]; [title, titleSize] _ MakeTitle[shell.name, shell.font]; width _ MAX[sLeft.x+sRight.x+MAX[sBottom.x, sTop.x, titleSize.x], minSize.x]; height _ MAX[sLeft.y+margin, sRight.y+margin, sBottom.y+sTop.y+titleSize.y, minSize.y]; width _ GridUp[width]; height _ GridUp[height]; box _ [ -- touching all pins on the inside x1: IF iLeft=NIL THEN 0 ELSE shell.pinRect.y, x2: shell.pinRect.x + (IF iRight=NIL THEN width ELSE width-shell.pinRect.y), y1: IF iBottom=NIL THEN 0 ELSE shell.pinRect.y, y2: shell.pinRect.x + (IF iTop=NIL THEN height ELSE height-shell.pinRect.y)]; AddInstances[iBottom, [Center[sBottom.x, sLeft.x, sRight.x, width], box.y1]]; AddInstances[iTop, [Center[sTop.x, sLeft.x, sRight.x, width], box.y2]]; AddInstances[iLeft, [box.x1, Center[sLeft.y, 0, 0, height]]]; AddInstances[iRight, [box.x2, Center[sRight.y, 0, 0, height]]]; AddBoxSide[box.y2-box.y1, [box.x1, box.y1], original]; -- left AddBoxSide[box.y2-box.y1, [box.x2, box.y2], rotate180]; -- right AddBoxSide[box.x2-box.x1, [box.x2, box.y1], rotate90]; -- bot AddBoxSide[box.x2-box.x1, [box.x1, box.y2], rotate270]; -- top IF title#NIL THEN -- reposition the title in the center of the inside of the icon AddInstances[LIST[title], [(width+sLeft.x-sRight.x)/2, (height+sBottom.y-sTop.y)/2]]; CDCells.ToSequenceMode[obj]; [] _ CDCells.ResizeCell[design: NIL, cell: obj]}; CellTypePinsSrcPos: PUBLIC PROC [cell: Core.CellType] RETURNS [pins: ARRAY CoreGeometry.Side OF LIST OF Rope.ROPE] ~ { EachWire: CoreOps.EachWireProc ~ { EachPin: CoreGeometry.EachPinProc ~ {elemsOn[side] _ CONS [NEW[ElemRep _ [name, min]], elemsOn[side]]}; name: Rope.ROPE = CoreOps.GetShortWireName[wire]; IF Rope.IsEmpty[name] THEN TerminalIO.PutRope["*** Warning: Unnamed public.\n"] ELSE [] _ CoreGeometry.EnumerateSides[Sisyph.mode.decoration, cell, wire, EachPin]; RETURN[FALSE, FALSE]}; Increasing: GList.CompareProc ~ { -- Inverted order because of later list copy !!! RETURN [Basics.CompareInt[NARROW [ref2, Elem].at, NARROW [ref1, Elem].at]]}; Elem: TYPE ~ REF ElemRep; Elems: TYPE ~ LIST OF Elem; ElemRep: TYPE ~ RECORD [name: Rope.ROPE, at: INT]; elemsOn: ARRAY CoreGeometry.Side OF Elems _ ALL [NIL]; [] _ CoreOps.VisitWireSeq[cell.public, EachWire]; -- build list for each side FOR side: CoreGeometry.Side IN CoreGeometry.Side DO -- sort each side & build icon list: Elems _ NARROW[GList.Sort[elemsOn[side], Increasing]]; pins[side] _ NIL; WHILE list#NIL DO IF NOT RopeList.Memb[pins[side], list.first.name] THEN pins[side] _ CONS [list.first.name, pins[side]]; list _ list.rest; ENDLOOP; ENDLOOP}; CellTypePinsAlpha: PUBLIC PROC [cell: Core.CellType] RETURNS [pins: ARRAY CoreGeometry.Side OF LIST OF Rope.ROPE] ~ { EachWire: CoreOps.EachWireProc ~ { EachPin: CoreGeometry.EachPinProc ~ {pins[side] _ CONS [name, pins[side]]}; name: Rope.ROPE = CoreOps.GetShortWireName[wire]; IF Rope.IsEmpty[name] THEN TerminalIO.PutRope["*** Warning: Unnamed public.\n"] ELSE { [] _ CoreGeometry.EnumerateSides[Sisyph.mode.decoration, cell, wire, EachPin]; RETURN[FALSE, FALSE]}}; -- use highest level name only [] _ CoreOps.VisitWireSeq[cell.public, EachWire]; -- build list for each side FOR side: CoreGeometry.Side IN CoreGeometry.Side DO -- sort and remove multiple entries pins[side] _ RopeList.Sort[pins[side], RopeList.IgnoreCase]; pins[side] _ RopeList.Reverse[pins[side]]; FOR list: LIST OF Rope.ROPE _ pins[side], list.rest WHILE list#NIL DO WHILE list.rest#NIL AND list.first.Equal[list.rest.first] DO list.rest _ list.rest.rest; ENDLOOP; ENDLOOP; ENDLOOP}; CellTypePinsLayout: PUBLIC PROC [cell: Core.CellType] RETURNS [pins: ARRAY CoreGeometry.Side OF LIST OF Rope.ROPE] ~ { [] _ PWCore.Layout[cell]; FOR i: INT IN [0..cell.public.size) DO EachWire: CoreOps.EachWireProc ~ { EachPin: CoreGeometry.EachPinProc ~{ IF pins[side]=NIL OR pins[side].first#name THEN pins[side] _ CONS [name, pins[side]]}; IF wire.size=0 THEN [] _ CoreGeometry.EnumerateSides [PWCore.extractMode.decoration, cell, wire, EachPin]}; name: Rope.ROPE = CoreOps.GetShortWireName[cell.public[i]]; [] _ CoreOps.VisitWire[cell.public[i], EachWire] ENDLOOP; FOR side: CoreGeometry.Side IN CoreGeometry.Side DO -- sort and remove multiple entries pins[side] _ RopeList.Sort[pins[side], RopeList.IgnoreCase]; pins[side] _ RopeList.Reverse[pins[side]]; FOR list: LIST OF Rope.ROPE _ pins[side], list.rest WHILE list#NIL DO WHILE list.rest#NIL AND list.first.Equal[list.rest.first] DO list.rest _ list.rest.rest; ENDLOOP; ENDLOOP; ENDLOOP}; CreateIconCommand: PROC [comm: CDSequencer.Command] ~ { schName, shortName, iconName: Rope.ROPE; icon: CD.Object; cell: Core.CellType; shell: IconConstruction.Shell; minSize: CD.Position; nms: ARRAY CoreGeometry.Side OF LIST OF Rope.ROPE; selected: CD.Instance = CDOps.TheInstance[comm.design, "CreateIcon\n"]; IF selected=NIL THEN RETURN; -- nothing to do schName _ CDDirectory.Name[selected.ob, comm.design]; IF schName.IsEmpty[] THEN {TerminalIO.PutF["*** Selected schematic has no name.\n"]; RETURN}; IF NOT Rope.Match["*.sch", schName] THEN TerminalIO.PutF["*** Convention for schematics is to suffix them with '.sch'.\n"]; shortName _ TerminalIO.RequestRope["Type icon short name: "]; IF shortName.IsEmpty[] THEN shortName _ IF Rope.Match["*.sch", schName] THEN schName.Substr[0, schName.Length[]-4] ELSE schName; iconName _ shortName.Concat[".icon"]; IF CDDirectory.Fetch[comm.design, iconName]#NIL THEN { -- maybe offer replace ? TerminalIO.PutF["*** The icon %g already exists!\n", IO.rope[iconName]]; RETURN}; cell _ ExtractOps.ExtractCDInstanceCellTypeAndReport[selected, comm.design, Sisyph.mode]; IF cell=NIL THEN RETURN; -- message already printed, service of ExtractOps nms _ SELECT comm.key FROM $CreateIconAlpha => CellTypePinsAlpha[cell], $CreateIconSrcPos => CellTypePinsSrcPos[cell], $CreateIconLayout => CellTypePinsLayout[cell], ENDCASE => ERROR; SELECT comm.key FROM $CreateIconLayout => { scale: INT _ RequestIntDefault["Type icon scale factor (default 100): ", 100]; minSize _ CD.InterestSize[PWCore.Layout[cell]]; minSize.x _ minSize.x/scale; minSize.y _ minSize.y/scale}; ENDCASE => minSize _ [0,0]; shell _ NewShell[shortName, nms, comm.design]; shell.grid _ CDOps.GetGrid[comm.design, comm]; -- default might not be perfect... icon _ ShellObjectInner[shell, minSize]; CDProperties.PutObjectProp[icon, Sisyph.mode.extractProcProp, $SisyphExtractCellIcon]; CDProperties.PutObjectProp[icon, $IconFor, schName]; CDCells.SetSimplificationTreshhold[cell: icon, val: 30, inPixels: TRUE]; IF NOT CDDirectory.Include[comm.design, icon, iconName] THEN ERROR; -- Abnormal [] _ CDOps.IncludeObjectI[comm.design, icon, comm.pos]; TerminalIO.PutF["%g generated\n", IO.rope[iconName]]}; RequestIntDefault: PROC[prompt: IO.ROPE, default: INT] RETURNS[i: INT] = { DO ok: BOOL _ TRUE; r: Rope.ROPE _ TerminalIO.RequestRope[prompt, 0]; IF r.Length[]=0 THEN RETURN[default]; i _ Convert.IntFromRope[r ! RuntimeError.UNCAUGHT => {ok_FALSE; CONTINUE}]; IF ok THEN RETURN; TerminalIO.PutRope[" ?? (integer), please repeat: \n"]; ENDLOOP}; IsWire: PROC [inst: CD.Instance] RETURNS [BOOL] ~ { RETURN [inst.ob.layer=CD.commentLayer AND CDRects.IsBareRect[inst.ob]]}; FiddlePos: PROC [inst: CD.Instance, ir: CD.Rect] = { MoreZero: PROC [a, b: INT] RETURNS [INT_0] = { IF a>=0 AND b>=0 THEN RETURN [MIN[a, b]]; IF a<=0 AND b<=0 THEN RETURN [MAX[a, b]]; }; r: CD.Rect _ CDInstances.InstRectI[inst]; inst.trans.off.x _ inst.trans.off.x + MoreZero[ir.x1-r.x1, ir.x2-r.x2]; inst.trans.off.y _ inst.trans.off.y + MoreZero[ir.y1-r.y1, ir.y2-r.y2]}; SwitchWireSizeCommand: PROC [comm: CDSequencer.Command] ~ { shrunk: INT _ 0; enlarged: INT _ 0; smallW: CD.Number = CDLayers.LayerWidth[comm.design, CD.commentLayer]; largeW: CD.Number = 3*smallW; TerminalIO.PutRope["switch size of selected wires\n"]; FOR list: CD.InstanceList _ CDOps.InstList[comm.design], list.rest WHILE list#NIL DO inst: CD.Instance _ list.first; -- instance may be modified ! IF inst.selected AND IsWire[inst] THEN { size: CD.Position = CD.InterestSize[inst.ob]; delta: CD.Number; SELECT MIN [size.x, size.y] FROM smallW => delta _ smallW; largeW => delta _ -smallW; ENDCASE => delta _ 0; IF delta#0 THEN { -- modify the instance goal: CD.Rect = CDBasics.Extend[CDInstances.InstRectI[inst], delta]; oldRect: CD.Rect = CDInstances.InstRectO[inst]; inst.ob _ CDRects.CreateRect[CDBasics.AddPoints[size, [2*delta, 2*delta]], inst.ob.layer]; inst.trans.off _ CDBasics.SubPoints[inst.trans.off, [delta, delta]]; FiddlePos[inst, goal]; -- move instance to fit goal: transf are the plague of CD CDOps.Redraw[comm.design, CDBasics.Surround[oldRect, CDInstances.InstRectO[inst]]]; IF delta>0 THEN enlarged _ enlarged + 1 ELSE shrunk _ shrunk + 1 } }; ENDLOOP; TerminalIO.PutF ["Enlarged %g wire(s), shrunk %g wire(s)\n", IO.int[enlarged], IO.int[shrunk]]}; CDSequencer.ImplementCommand[$CreateIconAlpha, CreateIconCommand]; CDSequencer.ImplementCommand[$CreateIconSrcPos, CreateIconCommand]; CDSequencer.ImplementCommand[$CreateIconLayout, CreateIconCommand]; CDSequencer.ImplementCommand[$SwitchWireSize, SwitchWireSizeCommand]; END. †IconConstructionImpl.mesa Copyright Σ 1985, 1986, 1987 by Xerox Corporation. All rights reserved. Jean-Marc Frailong December 7, 1987 8:49:00 pm PST Don Curry January 22, 1988 2:50:01 pm PST Create icon from schematic Build a shell structure based on parameters and defaults extracted from the design. Create a ChipNDale object for this shell. This function is extremely tricky due to the geometry manipulation (for which CD does not really help..). If you want to modify it, get brand new pad, pencil and eraser for a few drawings... The following basic invariants should help the intrepid: - the outermost left/bottom point of pins are grid aligned (+pin width on top & right) - BuildSideGeometry creates the instances in such a way that the first pin has its lower-left point at (0,0) and that texts may be edited by CD and still stick to the pin - CD texts need some massaging so that a horizontal text is exactly on the middle of the horizontal axis (c.f. fontCorrection...) - the numerous orientations used are critical as they allow easier positionning & permit the correct edition of texts Round a number up to the grid Round a number down to the grid Return a gridded rectangle surrounding the argument Create the instance for the title centered around the origin and return an inflated size Create geometry (pins and names) along an edge of the icon. The size is extended to the grid size. Move instances and insert them into the cell Center a block between two others taking the height of texts... (grumble) Create the object into which instances will be later added Compute the instances for the 4 sides and the title (not well placed yet) Compute a reasonable size for the nice box and prepare reposition. Move the sides at their final locations, add box and title if present Update the object Return list of publics on each side, sorted in increasing coordinates. A name may appear on multiple sides depending on the source. Returns publics on each side sorted in alphabetic order. A name may appear on multiple sides depending on the source. Returns publics on each side based on Layout. User interface to create icon from schematic Get name of source and target Get the source cell type Get the names Get minSize Build the object, instantiate it & return Big wires versus small wires Return TRUE if the object of the instance is a schematics wire The command to enlarge/shrink all selected wires Initialization Κ™˜šœ™JšœH™HJšœ/Οk™2Icode™)—J™š ˜ Jšœ˜JšœΉœV˜“J˜—šΡblnœœ˜#Jšœ œ³œC˜ŠJšœ˜Jšœ˜—head™šΟnœœœ œœœœœœ œœ œœ œœ$˜ζJ™SJšœ œ0˜<šœœ˜*Jšœ ˜ Jšœ˜Jšœ˜Jšœ0˜0Jšœ&˜&Jšœ ˜ Jšœ˜Jšœ'˜'Jšœ˜—J˜—š Ÿ œœœ!œœ ˜UJšœxœn™θ™8J™VJšœœ™ͺJšœœ}™J™u—Jšœ˜—J˜šŸœœ*œ˜TJšœœ ˜š Ÿœœœ œœ ˜4J™Jšœœœœ˜=—š Ÿœœœ œœ ˜6J™Jšœœœœ˜=—š Ÿ œœœœœ˜4J™3JšœS˜S—š Ÿ œœ œœœœ˜jJ™XJšœ œ˜%Jšœœ˜Jšœœœœ ˜/šœ-œ˜=Jšœ8˜8—Jšœ Οt˜!Jšœ%˜%Jšœ! œ œ ˜XJšœΟc-˜LJšœ‘-˜I—šŸœœ œœœœœ œ œœ ˜©Jšœb™bJšœœ=‘˜\JšœœN‘˜eJšœœ˜Jšœœ˜ šœœ˜Jšœ œ:˜EJšœ œ˜‹Jšœ*˜*Jšœ8˜8Jšœœ œ˜-J˜Jšœ˜—šœ˜ JšœI˜MJšœ˜——š Ÿ œœ œ œœ˜Qšœ˜JšœŽ˜ŽJšœ˜Jšœ˜——šŸ œœ œœ˜DJ™,šœœ˜JšœJ˜JJšœM˜MJ˜Jšœ˜ ——š Ÿœœœ œœ ˜JJ™IJšœ6˜6—Jšœœ˜Jšœœ5‘˜^Jšœœ&‘˜@Jšœœ6‘˜[Jšœœ‘,˜[Jšœœ ‘˜BJšœœœ ˜+Jšœœ˜ Jšœœ˜šœ:™:J˜ —šœI™Išœ8˜8Jšœ+˜+Jšœ&˜&—šœA˜AJšœ-˜-Jšœ%˜%—šœ>˜>Jšœ*˜*Jšœ&˜&—šœ;˜;Jšœ*˜*Jšœ%˜%—J˜7—šœB™BJšœœœ-˜MJšœ œK˜WJšœ˜Jšœ˜šœ‘"˜*Jš œœœœœ˜-Jš œœœœœ˜LJš œœ œœœ˜/Jš œœœœœ˜M——šœE™EJšœM˜MJšœG˜GJšœ=˜=Jšœ?˜?Jšœ7‘˜>Jšœ8‘˜@Jšœ7‘˜=Jšœ8‘˜>š œœœ‘-Πbc‘ ˜QJšœ œD˜U—Jšœ™Jšœ˜Jšœ œ˜1—J˜—šŸœœ˜Jš œœœœœœœ˜VJ™„šŸœ˜"šŸœ˜#Jšœœœ)˜C—Jšœ œ"˜1šœ˜Jšœ5˜9JšœO˜S—Jšœœœ˜—šŸ œ‘0˜RJšœœœ˜L—Jšœœœ ˜Jšœœœœ˜Jš œ œœ œœ˜2Jš œ œœ œœ˜6Jšœ2‘˜Mšœœœ‘˜RJšœœ(˜Jšœœœ˜HJ™—šŸ œœœœ ˜4š Ÿœœœœœ˜.Jš œœœœœ˜)Jš œœœœœ˜)J˜—Jšœœ$˜)JšœG˜GJšœH˜HJ˜—šŸœœ ˜;J™0Jšœœ˜Jšœ œ˜Jšœœ+œ˜FJšœœ˜Jšœ6˜6š œœ7œœ˜TJšœœ‘˜=šœœœ˜(Jšœœ œ˜-Jšœœ˜šœœ˜ Jšœ˜Jšœ˜Jšœ˜—šœ œ‘˜(Jšœœ<˜DJšœ œ$˜/JšœZ˜ZJšœD˜DJšœ‘9˜PJšœS˜SJšœ œœ˜E——Jšœ˜—šœ˜Jšœ-œœ˜P———™JšœB˜BJšœC˜CJšœC˜CšœE˜EJ˜J˜——Jšœ˜J˜—…—7ΔPγ