DIRECTORY Ascii, Basics, CD, CDSymbolicObjects, Core, CoreGeometry, CoreProperties, IO, RedBlackTree, Rope, Rsh, RTBasic, RTCoreUtil, SC, SCInstUtil, SCPrivate, SCUtil, TerminalIO, UnixRemoteFile, UserCredentials; SCUtilImpl: CEDAR PROGRAM IMPORTS Ascii, Basics, CoreProperties, IO, RedBlackTree, Rope, Rsh, RTBasic, RTCoreUtil, SC, SCInstUtil, TerminalIO, UnixRemoteFile, UserCredentials EXPORTS SCUtil SHARES SC = BEGIN twDoGlobalRoute: BOOLEAN _ FALSE; RowArray: TYPE = REF RowArrayRec; RowArrayRec: TYPE = ARRAY SCPrivate.MaxChanSr OF SC.Number; fileHost: Rope.ROPE _ "palain-NFS"; cpuHost: Rope.ROPE _ "palain"; serverDir: Rope.ROPE _ "/timberwolf/"; remoteServer: Rope.ROPE _ "/palain/"; remoteServerDir: Rope.ROPE _ Rope.Cat[remoteServer, GuessUnixName[], serverDir]; twCmd: Rope.ROPE _ "/usr/datools/TimberWolfSCrsh "; fileMode: UnixRemoteFile.Mode _ 0666B; FindPin: PUBLIC PROC [object: SCPrivate.Object, pinName: Rope.ROPE] RETURNS [pin: SCPrivate.ObjectPin _ NIL] = BEGIN pins: SCPrivate.ObjectPins _ object.pins; FOR pinIndex: NAT IN [0 .. object.numPins) WHILE pin = NIL DO IF Rope.Equal[pinName, pins.p[pinIndex].name] THEN pin _ pins.p[pinIndex]; ENDLOOP; END; FindPinByWire: PUBLIC PROC [object: SCPrivate.Object, wire: Core.Wire] RETURNS [pin: SCPrivate.ObjectPin _ NIL] = BEGIN pins: SCPrivate.ObjectPins _ object.pins; FOR pinIndex: NAT IN [0 .. object.numPins) WHILE pin = NIL DO IF wire = pins.p[pinIndex].publicWire THEN pin _ pins.p[pinIndex]; ENDLOOP; END; FindInstance: PUBLIC PROC [handle: SC.Handle, instanceName: Rope.ROPE] RETURNS [instance: SCPrivate.Instance _ NIL] = BEGIN structureData: SCPrivate.StructureData _ NARROW[handle.structureData]; instances: SCPrivate.Instances _ structureData.instances; FOR instanceIndex: NAT IN [1 .. instances.count] WHILE instance = NIL DO IF Rope.Equal[instanceName, instances.inst[instanceIndex].name] THEN instance _ instances.inst[instanceIndex]; ENDLOOP; END; IsPowerName: PUBLIC PROC [handle: SC.Handle, name: Rope.ROPE] RETURNS [found: BOOLEAN _ FALSE] = { layoutData: SCPrivate.LayoutData _ NARROW[handle.layoutData]; powerBuses: SCPrivate.PowerBuses _ layoutData.powerBuses; IF Rope.Equal[name, powerBuses[left].name, FALSE] OR Rope.Equal[name, powerBuses[right].name, FALSE] THEN found _ TRUE}; DirectionFromSide: PUBLIC PROC [side: CoreGeometry.Side] RETURNS [CDSymbolicObjects.Direction] ~ { RETURN[SELECT side FROM bottom=> south, right => east, top => north, left => west, ENDCASE => SC.Error[callingError, "Not suppose to happen."]]}; WriteResults: PUBLIC PROC [title: Rope.ROPE, handle: SC.Handle, startArea: SC.Number] RETURNS [area: SC.Number] = { layoutData: SCPrivate.LayoutData _ NARROW[handle.layoutData]; lambda: SC.Number _ handle.rules.rowParms.technology.lambda; width: SC.Number _ layoutData.totWidth/lambda; height: SC.Number _ layoutData.totHeight/lambda; area _ width*height; TerminalIO.PutRope[title]; TerminalIO.PutF1[" width = %g", IO.int[width]]; TerminalIO.PutF1[", height = %g", IO.int[height]]; TerminalIO.PutF1[", area = %g", IO.int[area]]; IF startArea > 0 THEN { reduction: SC.Number _ (startArea - area)*100/startArea; TerminalIO.PutRope[IO.PutFR[" reduction = %g%%\n", IO.real[reduction]]]}; TerminalIO.PutRope["\n"]; }; WriteStructure: PUBLIC PROC [handle: SC.Handle] = { EachInstance: SCInstUtil.EachInstanceProc = { EachPin: SCInstUtil.EachPinProc = { TerminalIO.PutRope[Rope.Cat[" Pin: ", netPin.pin.name, " Net: ", netPin.net.name, "\n"]]}; TerminalIO.PutRope[Rope.Cat[" Instance: ", instance.name, " Object: ", instance.object.name, "\n"]]; [] _ SCInstUtil.EnumeratePinsOnInst[instance, EachPin]}; TerminalIO.PutRope[Rope.Cat["Handle: ", handle.name, "\n"]]; [] _ SCInstUtil.EnumerateAllInstances[handle, EachInstance]}; XYToPQ: PUBLIC PROC [handle: SC.Handle, pos: SC.Pos] RETURNS [pqPos: RTBasic.PQPos] = { layoutData: SCPrivate.LayoutData _ NARROW[handle.layoutData]; rowDirection: SC.Direction _ layoutData.layoutParms.rowDirection; RETURN[RTBasic.XYToPQ[rowDirection, pos]]}; PQToXY: PUBLIC PROC [handle: SC.Handle, pqPos: RTBasic.PQPos] RETURNS [pos: SC.Pos] = { layoutData: SCPrivate.LayoutData _ NARROW[handle.layoutData]; rowDirection: SC.Direction _ layoutData.layoutParms.rowDirection; RETURN[RTBasic.PQToXY[rowDirection, pqPos]]}; DestroyRules: PUBLIC PROC [handle: SC.Handle] ~ { rules: SC.DesignRules _ handle.rules; rules.rowRules _ NIL; rules.sideRules _ NIL; handle.rules _ NIL}; DestroyParms: PUBLIC PROC [handle: SC.Handle] = { parms: SCPrivate.Parms _ NARROW[handle.parms]; parms.libDesign _ NIL; parms.ftObject _ NIL; parms.portObject _ NIL; handle.parms _ NIL}; Memb: PROC [rope: Rope.ROPE, list: LIST OF Rope.ROPE] RETURNS [BOOL] = { UNTIL list = NIL DO IF Rope.Equal[list.first, rope] THEN RETURN[TRUE]; list _ list.rest; ENDLOOP; RETURN[FALSE]; }; GetCoreInvestmentProp: PUBLIC PROC [cellType: Core.CellType, prop: ATOM] RETURNS [SC.HowLongToWork] ~ { value: ATOM _ NARROW[CoreProperties.GetCellTypeProp[cellType, prop]]; investment: SC.HowLongToWork _ SELECT value FROM SC.veryLongValue => veryLong, SC.longValue => long, SC.mediumValue => medium, SC.shortValue => short, SC.veryShortValue => veryShort, ENDCASE => noInvestmentProp; RETURN[investment]}; WriteTWFiles: PUBLIC PROC [handle: SC.Handle] ~ { EachInstance: SCInstUtil.EachInstanceProc ~ { EachPin: SCInstUtil.EachPinProc ~ { IF netPin.net # NIL THEN AddPin[celStream, instance, netPin.pin, netPin.net, ~SeenBefore[netPin.net, netsOnInst], handle]; -- for TimberWolf .cel file netsOnInst _ CONS[netPin.net, netsOnInst]}; netsOnInst: LIST OF SCPrivate.Net _ NIL; IF instance.whichClass = logic THEN { -- for TimberWolf .cel file AddCell[celStream, instance, handle, instNumber, offsetsOnRow]; instNumber _ instNumber+1; [] _ SCInstUtil.EnumeratePinsOnInst[instance, EachPin]} ELSE IF instance.whichClass = io THEN AddPad[padTable, instance]}; attPerCell: CARD _ RTCoreUtil.GetCoreIntProp[handle.coreCellType, $TWattPerCell, 0]; filePathName: Rope.ROPE _ Rope.Cat[remoteServerDir, handle.name]; server: UnixRemoteFile.UnixServerHandle _ UnixRemoteFile.CreateHandle[fileHost]; netStream: IO.STREAM _ UnixRemoteFile.OpenWriteStream[server, [Rope.Cat[filePathName, ".net"]], fileMode]; parStream: IO.STREAM _ UnixRemoteFile.OpenWriteStream[server, [Rope.Cat[filePathName, ".par"]], fileMode]; blkStream: IO.STREAM _ UnixRemoteFile.OpenWriteStream[server, [Rope.Cat[filePathName, ".blk"]], fileMode]; celStream: IO.STREAM _ UnixRemoteFile.OpenWriteStream[server, [Rope.Cat[filePathName, ".cel"]], fileMode]; padTable: RedBlackTree.Table _ RedBlackTree.Create[GetKey, Compare]; parms: SCPrivate.Parms _ NARROW[handle.parms]; layoutData: SCPrivate.LayoutData _ NARROW[handle.layoutData]; instNumber: NAT _ 1; -- used to number instances of cells & pads in the TW file ??? offsetsOnRow: RowArray _ NEW[RowArrayRec _ ALL[0]]; IO.Put[netStream, IO.rope["allnets HVweights 1.0 1.0\l"]]; IO.Flush[netStream]; IO.Close[netStream]; IF attPerCell>0 THEN IO.PutF[parStream, "att.per.cell %d\l", IO.card[attPerCell]]; IO.Put[parStream, IO.rope["rowSep 2.0\l"]]; IO.Put[parStream, IO.rope["indent 0.85\l"]]; IO.Put[parStream, IO.rope["addFeeds\l"]]; IO.Put[parStream, IO.rope["feedThruWidth "], ScaleOut[parms.ftObject.size.p, handle], IO.rope["\l"]]; IO.Put[parStream, IO.rope["implicit.feed.thru.range 0.25\l"]]; IF twDoGlobalRoute THEN IO.Put[parStream, IO.rope["do.global.route\l"]]; IO.Flush[parStream]; IO.Close[parStream]; FOR row: INT IN [1 .. layoutData.lgRows.count] DO IO.Put[blkStream, IO.rope["block height "], ScaleOut[parms.ftObject.size.q, handle], IO.rope[" class 1\l"]]; ENDLOOP; IO.Flush[blkStream]; IO.Close[blkStream]; [] _ SCInstUtil.EnumerateAllInstances[handle, EachInstance]; CopyPadToCel[celStream, padTable, handle, instNumber]; -- pads must go at the end in position order IO.Flush[celStream]; IO.Close[celStream]; UnixRemoteFile.DestroyHandle[server]; }; SeenBefore: PROC [net: SCPrivate.Net, netList: LIST OF SCPrivate.Net] RETURNS [BOOL] ~ { UNTIL netList = NIL DO IF netList.first = net THEN RETURN [TRUE]; netList _ netList.rest; ENDLOOP; RETURN [FALSE]}; AddCell: PROC [celStream: IO.STREAM, instance: SCPrivate.Instance, handle: SC.Handle, number: NAT, offsetsOnRow: RowArray] ~ { middleX: INT _ instance.object.orgin.p + instance.object.size.p/2; middleY: INT _ instance.object.orgin.q + instance.object.size.q/2; layoutData: SCPrivate.LayoutData _ NARROW[handle.layoutData]; IO.Put[celStream, IO.rope["cell "], IO.int[number]]; IO.Put[celStream, IO.rope[" "], IO.rope[instance.name]]; IO.Put[celStream, IO.rope["\l"]]; IF 1 <= instance.fnlRow AND instance.fnlRow <= layoutData.rowChans.count THEN { IO.Put[celStream, IO.rope[" initially fixed "], ScaleOut[offsetsOnRow[instance.fnlRow], handle]]; offsetsOnRow[instance.fnlRow] _ offsetsOnRow[instance.fnlRow] + SCInstUtil.InstWidth[instance]; IO.Put[celStream, IO.rope[" from left of block "], IO.int[instance.fnlRow]]; IO.Put[celStream, IO.rope["\l"]]}; IO.Put[celStream, IO.rope[" left "], ScaleOut[instance.object.orgin.p - middleX, handle]]; IO.Put[celStream, IO.rope[" right "], ScaleOut[instance.object.orgin.p + instance.object.size.p - middleX, handle]]; IO.Put[celStream, IO.rope[" bottom "], ScaleOut[instance.object.orgin.q - middleY, handle]]; IO.Put[celStream, IO.rope[" top "], ScaleOut[instance.object.orgin.q + instance.object.size.q - middleY, handle], IO.rope["\l"]]}; AddPin: PROC [celStream: IO.STREAM, instance: SCPrivate.Instance, pin: SCPrivate.ObjectPin, net: SCPrivate.Net, first: BOOLEAN, handle: SC.Handle] ~ { middleX: INT _ instance.object.orgin.p + instance.object.size.p/2; middleY: INT _ instance.object.orgin.q + instance.object.size.q/2; pinPos: CD.Position _ SELECT pin.pinPos.side FROM bottom => [pin.pinPos.location - middleX, instance.object.orgin.q - middleY], top => [pin.pinPos.location - middleX, instance.object.orgin.q + instance.object.size.q - middleY], left => [instance.object.orgin.p - middleX, pin.pinPos.location - middleY], right => [instance.object.orgin.p + instance.object.size.p - middleX, pin.pinPos.location - middleY], ENDCASE => SC.Error[callingError, "pin on invalid side"]; IF first THEN { IO.Put[celStream, IO.rope[" pin name "], IO.rope[pin.name]]; IO.Put[celStream, IO.rope[" signal "], IO.rope[net.name]]} ELSE IO.Put[celStream, IO.rope[" equiv name "], IO.rope[pin.name]]; IO.Put[celStream, IO.rope[" "], ScaleOut[pinPos.x, handle]]; IO.Put[celStream, IO.rope[" "], ScaleOut[pinPos.y, handle]]; IO.Put[celStream, IO.rope["\l"]]}; Pad: TYPE = REF PadRec; PadRec: TYPE = RECORD [ instance: SCPrivate.Instance, pathName: Rope.ROPE]; AddPad: PROC [padTable: RedBlackTree.Table, instance: SCPrivate.Instance] ~ { pad: Pad _ NEW[PadRec _ [instance: instance, pathName: instance.name]]; RedBlackTree.Insert[padTable, pad, instance]}; CopyPadToCel: PROC [celStream: IO.STREAM, padTable: RedBlackTree.Table, handle: SC.Handle, instNumber: NAT] ~ { WriteNode: RedBlackTree.EachNode ~ { pad: Pad _ NARROW[data]; sideRope: Rope.ROPE _ SELECT pad.instance.curSide FROM bottom => "B", right => "R", top => "T", left => "L", ENDCASE => "NONE"; orient: INT _ SELECT pad.instance.curSide FROM bottom => 0, right => 6, top => 3, left => 7, ENDCASE => 0; IO.Put[celStream, IO.rope["pad "], IO.int[instNumber]]; instNumber _ instNumber+1; IO.Put[celStream, IO.rope[" "], IO.rope[pad.pathName]]; IO.Put[celStream, IO.rope[" orient "], IO.int[orient], IO.rope["\l"]]; IO.Put[celStream, IO.rope[" padside "], IO.rope[sideRope], IO.rope["\l"]]; IO.Put[celStream, IO.rope[" left "], ScaleOut[-pad.instance.object.size.p, handle]]; IO.Put[celStream, IO.rope[" right "], ScaleOut[pad.instance.object.size.p, handle]]; IO.Put[celStream, IO.rope[" bottom "], ScaleOut[-pad.instance.object.size.q, handle]]; IO.Put[celStream, IO.rope[" top "], ScaleOut[pad.instance.object.size.q, handle], IO.rope["\l"]]; IO.Put[celStream, IO.rope[" pin name io signal "], IO.rope[pad.pathName]]; IO.Put[celStream, IO.rope[" "], IO.int[0]]; IO.Put[celStream, IO.rope[" "], ScaleOut[pad.instance.object.size.q, handle]]; IO.Put[celStream, IO.rope["\l"]]}; RedBlackTree.EnumerateIncreasing[padTable, WriteNode]}; GetKey: RedBlackTree.GetKey ~ { pad: Pad _ NARROW[data]; RETURN[pad.instance]}; Compare: RedBlackTree.Compare ~ { instance: SCPrivate.Instance _ NARROW[k]; pad: Pad _ NARROW[data]; instanceSide: Rope.ROPE _ SELECT instance.curSide FROM bottom => "B", right => "R", top => "T", left => "L", ENDCASE => "NONE"; padSide: Rope.ROPE _ SELECT pad.instance.curSide FROM bottom => "B", right => "R", top => "T", left => "L", ENDCASE => "NONE"; result: Basics.Comparison _ Rope.Compare[padSide, instanceSide]; IF result = Basics.Comparison[equal] THEN result _ Basics.CompareInt[instance.fnlPos, pad.instance.fnlPos]; IF result = Basics.Comparison[equal] THEN result _ Rope.Compare[instance.name, pad.instance.name]; RETURN[result]}; ReadTWPlace: PUBLIC PROC [handle: SC.Handle]~ { EachInstance: SCInstUtil.EachInstanceProc ~ { IF instance.whichClass = logic THEN { -- check for TimberWolf .cel file entry IF instance.fnlRow = 0 -- OR instance.fnlPos = 0 -- THEN SC.Signal[programmingError, Rope.Cat["Missing instance in Timberwolf .cell file: ", instance.name]]} ELSE IF instance.whichClass = io THEN { IF instance.fnlSide = none -- OR instance.fnlPos = 0 -- THEN SC.Signal[programmingError, Rope.Cat["Missing public in Timberwolf .cell file: ", instance.name]]}}; server: UnixRemoteFile.UnixServerHandle _ UnixRemoteFile.CreateHandle[fileHost]; placeStream: IO.STREAM _ UnixRemoteFile.OpenReadStream[server, [Rope.Cat[remoteServerDir, handle.name, ".pl1"]]]; [] _ IO.SkipWhitespace[placeStream]; WHILE ~IO.EndOf[placeStream] DO name: Rope.ROPE _ IO.GetTokenRope[placeStream, IO.IDProc].token; left: INT _ ScaleIn[placeStream, handle]; lower: INT _ ScaleIn[placeStream, handle]; right: INT _ ScaleIn[placeStream, handle]; upper: INT _ ScaleIn[placeStream, handle]; orient: INT _ IO.GetInt[placeStream]; row: INT _ IO.GetInt[placeStream]; dontCare: INT _ IO.SkipWhitespace[placeStream]; instance: SCPrivate.Instance _ FindInstance[handle, name]; IF instance # NIL THEN { SELECT instance.whichClass FROM logic => {instance.fnlRow _ row; instance.fnlPos _ left}; io => {SELECT orient FROM 0 => {instance.fnlPos _ left; instance.fnlSide _ bottom}; 6 => {instance.fnlPos _ lower; instance.fnlSide _ right}; 3 => {instance.fnlPos _ left; instance.fnlSide _ top}; 7 => {instance.fnlPos _ lower; instance.fnlSide _ left}; ENDCASE => {instance.fnlPos _ 0; instance.fnlSide _ none}}; ENDCASE} ELSE SC.Error[programmingError, Rope.Cat["Timberwolf .cell file instance is not in extraction: ", instance.name]]; -- the TW file & the extraction disagree... ENDLOOP; IO.Close[placeStream]; UnixRemoteFile.DestroyHandle[server]; [] _ SCInstUtil.EnumerateAllInstances[handle, EachInstance]}; ScaleOut: PROC [int: INT, h: SC.Handle] RETURNS [IO.Value] ~ { lambda: INT _ h.rules.rowParms.technology.lambda; IF ABS[int] MOD lambda # 0 THEN SC.Signal[explanation: "Scaling inaccuracies in TW files"]; RETURN[IO.int[int/lambda]]; }; ScaleIn: PROC [s: IO.STREAM, h: SC.Handle] RETURNS [int: INT] ~ { lambda: INT _ h.rules.rowParms.technology.lambda; RETURN[IO.GetInt[s]*lambda]; }; MyLower: Rope.TranslatorType ~ {RETURN[Ascii.Lower[old]]}; GuessUnixName: PROC RETURNS [unixName: Rope.ROPE] ~ { unixName _ UserCredentials.Get[].name; unixName _ Rope.Substr[unixName, 0, MIN[8, Rope.Index[unixName, 0, "."]]]; unixName _ Rope.Translate[base: unixName, translator: MyLower]; }; TWIt: PUBLIC PROC [id: Rope.ROPE] RETURNS [msg: Rope.ROPE] ~ { msgStream: IO.STREAM _ TerminalIO.TOS[]; msg _ Rsh.RSH[ remoteMachine: cpuHost, command: Rope.Cat[twCmd, id], in: IO.noInputStream, out: msgStream ]; }; translate: PUBLIC ARRAY SC.Side OF ARRAY SCPrivate.OrientationOrNone OF SC.Side; translate[bottom] _ [bottom, bottom, left, top, right, bottom, top, left, right]; translate[left] _ [left, left, top, right, bottom, right, left, bottom, top]; translate[top] _ [top, top, right, bottom, left, top, bottom, right, left]; translate[right] _ [right, right, bottom, left, top, left, right, top, bottom]; END. ΊSCUtilImpl.mesa Copyright Σ 1985, 1986, 1987 by Xerox Corporation. All rights reserved. Bryan Preas, November 3, 1987 3:17:15 pm PST Christian Le Cocq September 27, 1988 4:00:08 pm PDT Cong, August 27, 1987 2:58:51 pm PDT Jean-Marc Frailong October 11, 1987 3:11:18 pm PDT Not yet converted convert a position from x-y to p-q space. convert a position from p-q to x-y space. rules.rowRules.technology _ NIL; rules.sideRules.technology _ NIL; get an investment property from a cell write the timberWolf files PROC [instance: SCPrivate.Instance] RETURNS [quit: BOOL _ FALSE]; PROC [instance: SCPrivate.Instance, pin: NAT, netPin: SCPrivate.PinNet] RETURNS [quit: BOOL _ FALSE]; first the net file next the parameter file next the row definitions finally the connectivity RETURN TRUE iff net is on netList add a cell to the .cel file put instance on specified row add a pin to the .cel file record to save pad information; TimberWolf requires the pads to be sorted insert a pad into an ordered table pads must be at the end of the .cel file; write to .cel from the ordered symbol table PROC [data: UserData] RETURNS [stop: BOOL _ FALSE]; Callback proc: Given the user data in a node, return the key PROC [data: UserData] RETURNS [Key]; Callback proc type: Given a key and the user data in a node, return the comparison of the keys PROC [k: Key, data: UserData] RETURNS [Basics.Comparison]; read timberWolf placement PROC [instance: SCPrivate.Instance] RETURNS [quit: BOOL _ FALSE]; the Timberwolf documentation states positions will not be 0 but Version 4.2 returns valid placements with position = 0; hence the check was removed BTP August 1, 1988 HACK !!! TimberWolf does not provide side information. Have to use orientation BUG: TimberWolf sometimes provides identical locations for pads on same side!! check to see if everything is placed TWOps get rid of the ".pa" & truncate to 8 char, put in lower case. submits the placement to TimberWolfSC on the unix server. Κo˜šœ™JšœH™HJšœ)Οkœ™-Icodešœ3™3Kšœ!™$Kšœ/™2—J˜š ˜ Jšœœ9œ0œM˜Λ—J˜šΡbln œœœ˜Jšœ œ0œ9˜”Jšœ˜Jšœœ˜ Jš˜J˜Kšœœœ˜!K˜Jšœ œœ ˜!Jš œ œœœœ˜;Kšœœ˜#Kšœœ ˜Kšœœ˜&Kšœœ˜%Kšœœ6˜PKšœ œ#˜3Kšœ&˜&—head™š Οnœœœ*œœœ˜nK˜Kš˜Kšœ)˜)š œ œœœœ˜=Kšœ,œ˜JKšœ˜—Kšœ˜K˜—š Ÿ œœœ-œœ˜qK˜Kš˜Kšœ)˜)š œ œœœœ˜=Kšœ$œ˜BKšœ˜—Kšœ˜K˜—šŸ œœœ œœœ!œ˜uKš˜Kšœ)œ˜FKšœ9˜9š œœœœ œ˜HKšœ>œ*˜nKšœ˜—Kšœ˜K˜—šŸ œœœ œœœ œœ˜bK˜Kšœ#œ˜=Kšœ9˜9Jš œ)œœ*œœ œ˜xK˜—šŸœœœœ"˜bšœœ˜Kšœ;œœ1˜y—K˜—šŸ œœœœ œœ œœ ˜sJ˜Jšœ#œ˜=Jšœœ2˜˜SJšœœœ˜3K˜Kšœ™Kšœœ&˜:Kšœ˜Kšœ˜K˜Kšœ™Kšœœœ&œ˜RKšœœ˜+Kšœœ˜,Kšœœ˜)KšœœBœ ˜eKšœœ*˜>Jšœœœœ˜HKšœ˜Kšœ˜K˜Kšœ™šœœœ ˜1KšœœAœ˜lKšœ˜—Kšœ˜Kšœ˜K˜Kšœ™K˜Kšœ<˜œ ˜aKšœœ œ˜KKšœœ œ ˜+Kšœœ:˜NKšœœ˜"J˜—Jšœ7˜7K˜—šŸœ˜Kšœ<™Kšœœ&˜1Kš œœœ œœ9˜[Kšœœ˜K˜K˜—šŸœœœœœ œœ˜AKšœœ&˜1Jšœœ˜K˜K˜——™K˜šŸœœ˜:K˜—šŸ œœœœ˜5Kšœ=™=Kšœ&˜&Kšœ$œ#˜JKšœ?˜?K˜K˜—š Ÿœœœ œœ œ˜>K™9Kšœ œœœ˜(šœ˜Kšœ˜Kšœ˜Kšœœ˜$J˜—K˜K˜—šœ œœœœœœœ˜PJ˜QJ˜MJ˜KJ˜O—J˜—Jšœ˜J˜J˜J˜J˜—…—?4X]