DIRECTORY Atom, Basics, CD, CDProperties, CDRects, CDCells, CDDirectory, CDOps, CDPolygons, CDViewer, CMos, IntDefs, IntStorageDefs, IntTransDefs, IntUtilityDefs, IO, IODefs, OutputDefs, ParserTypeDefs, PolygonDefs, ReadCif, ReadCifUserCmd, Real, RealFns, Rope; CIFIntPhase2: CEDAR PROGRAM IMPORTS Atom, CD, CDProperties, CDRects, CDCells, CDDirectory, CDOps, CDPolygons, CDViewer, IntTransDefs, IntStorageDefs, IntUtilityDefs, IO, IODefs, PolygonDefs, ReadCif, ReadCifUserCmd, Real, RealFns, Rope EXPORTS IntDefs, OutputDefs = BEGIN OPEN IntUtilityDefs, IntStorageDefs; CIFMap: TYPE = RECORD [ size: NAT _ 0, map: SEQUENCE maxSize: NAT OF RECORD[ cifName: ATOM, cdLevel: CD.Layer, compensation: INT]]; cifMap: REF CIFMap; CIFDestRec: TYPE = RECORD [ -- from BrandyCIFter.mesa, wasn't in an interface... cifDest: Rope.ROPE, deltaRadius: INT -- nanometers, + means cif is bigger ]; design: CD.Design _ NIL; unknownLayers: LIST OF ATOM _ NIL; nanometersPerCifUnit: INT = 10; nextCellNumber: INT _ 0; numSides: CARDINAL _ 12; ErrorMsgType: TYPE = ReadCifUserCmd.ErrorMsgType; Map: PUBLIC PROCEDURE [layerName: Rope.ROPE] RETURNS [n: CARDINAL] = BEGIN cm: REF CIFMap; cifLayerAtom: ATOM = Atom.MakeAtom[layerName]; cdLevel: CD.Layer; compensation: INT; -- first time through, initialize data structures IF cifMap = NIL THEN BEGIN cifMap _ NEW[CIFMap[1]]; cifMap.size _ 1; BEGIN -- initialize design with "proper" technology based on first char of layer name c: CHAR _ Rope.Fetch[layerName, 0]; r: Rope.ROPE; technology: ATOM; SELECT c FROM 'C => {technology _ $cmos; r _ "Cmos"}; 'N => {technology _ $nmos; r _ "Nmos"}; ENDCASE => {IODefs.WriteLine[Rope.Cat["Unknown technology for layer ", layerName]]; Abort[]}; IF CD.FetchTechnology[technology] = NIL THEN BEGIN IODefs.WriteLine[Rope.Cat["Error: Chipndale technology ", r, " not loaded"]]; Abort[]; END; design _ CDOps.CreateDesign[CD.FetchTechnology[technology]]; design.name _ ReadCif.designName; ReadCif.cifUnitsPerLambda _ ReadCif.cifUnitsPerLambda/design.technology.lambda; END; END; --if layer is defined in map, return the mapped number FOR n IN [0..cifMap.size) DO IF cifMap[n].cifName = cifLayerAtom THEN RETURN; ENDLOOP; FOR l: LIST OF CD.Layer _ design.technology.usedLayers, l.rest WHILE l#NIL DO r: Rope.ROPE; property: REF _ CDProperties.GetLayerProp[l.first, $CDxCIFName]; DO WITH property SELECT FROM layer: Rope.ROPE => {r _ layer; compensation _ 0}; layer: REF CIFDestRec => {r _ layer.cifDest; compensation _ layer.deltaRadius}; layer: LIST OF REF ANY => {property _ layer.first; LOOP}; --if it's a list use the first property ENDCASE => {NULL}; -- not defined, ignore EXIT; ENDLOOP; IF Atom.MakeAtom[r] = cifLayerAtom THEN {cdLevel _ l.first; EXIT}; REPEAT FINISHED => FOR u: LIST OF ATOM _ unknownLayers, u.rest WHILE u#NIL DO IF u.first = cifLayerAtom THEN RETURN[LAST[CARDINAL]]; REPEAT FINISHED => BEGIN unknownLayers _ CONS[cifLayerAtom, unknownLayers]; IODefs.WriteLine[Rope.Cat["CIF layer ", layerName, " is not registered with ChipNDale, objects on this layer will be deleted"]]; RETURN[LAST[CARDINAL]]; END; ENDLOOP; ENDLOOP; cm _ NEW[CIFMap[cifMap.size+1]]; -- make space for a new layer cm.size _ cifMap.size; FOR i: NAT IN [0..cifMap.size) DO cm[i] _ cifMap[i]; ENDLOOP; cifMap _ cm; cifMap[cifMap.size] _ [cifName: cifLayerAtom, cdLevel: cdLevel, compensation: compensation/nanometersPerCifUnit]; n _ cifMap.size; cifMap.size _ cifMap.size+1; END; Instantiate: PUBLIC PROCEDURE [] = BEGIN ClearBinding: PROCEDURE[sym: STEntry] = BEGIN sym.bound _ FALSE; END; IncludeObjectsNotDrawn: PROCEDURE[sym: STEntry] = BEGIN IF NOT sym.bound THEN InstantiateSymbol[sym: sym, root: FALSE]; END; CIFtoCDPosition: PROCEDURE[call: Call] RETURNS [position: CD.Position, orientation: CD.Orientation] = BEGIN a11, a21, a12, a22, a33: REAL; IntTransDefs.Push[]; IntTransDefs.ApplyLocal[call.t]; IntTransDefs.Scale[1, ReadCif.cifUnitsPerLambda]; position _ LOOPHOLE[IntTransDefs.TransformPoint[0,0]]; IntTransDefs.Pop[]; a33 _ call.t.a33; a11 _ call.t.a11/a33; a21 _ call.t.a21/a33; a12 _ call.t.a12/a33; a22 _ call.t.a22/a33; IF a11 # 0 THEN BEGIN SELECT TRUE FROM a11>0 AND a22>0 => orientation _ 0; a11>0 AND a22<0 => orientation _ 5; a11<0 AND a22>0 => orientation _ 1; a11<0 AND a22<0 => orientation _ 4; ENDCASE => ERROR; END ELSE BEGIN SELECT TRUE FROM a21>0 AND a12>0 => orientation _ 3; a21>0 AND a12<0 => orientation _ 6; a21<0 AND a12>0 => orientation _ 2; a21<0 AND a12<0 => orientation _ 7; ENDCASE => ERROR; END; RETURN[position, orientation]; END; OrthogonalTransform: PROCEDURE[call: Call] RETURNS [BOOL] = BEGIN -- checks transformation to make sure any rotation is a multiple of 90 deg IF (call.t.a11 = 0 AND call.t.a22 = 0) OR (call.t.a21 = 0 AND call.t.a12 = 0) THEN RETURN[TRUE] ELSE RETURN[FALSE]; END; InstantiateSymbol: PROCEDURE[sym: STEntry, root: BOOL] = BEGIN ReportError: ReadCifUserCmd.ReportErrorProc = BEGIN IODefs.WriteLine[r]; SELECT msgType FROM FatalError => curCell.errorInSubCell _ TRUE; Warning => curCell.warningInSubCell _ TRUE; ENDCASE => ERROR; END; IncludePolygon: PROCEDURE[poly: Polygon] = BEGIN pd: PolygonDefs.PolygonDescriptor; IncludeTrapezoid: PROCEDURE[lowerLeftX, lowerRightX, lowerY, upperLeftX, upperRightX, upperY: REAL] = BEGIN IF upperLeftX # lowerLeftX OR upperRightX # lowerRightX THEN BEGIN points: LIST OF CD.Position _ NIL; ob: CD.Object; offset: CD.Position; IF ~curCell.warningInSubCell THEN ReportError["Non-rectilinear geometry", Warning]; points _ CONS[ [Real.RoundLI[(lowerLeftX+cifMap[poly.layer].compensation)/ReadCif.cifUnitsPerLambda], Real.RoundLI[(lowerY+cifMap[poly.layer].compensation)/ReadCif.cifUnitsPerLambda]], points]; points _ CONS[ [Real.RoundLI[(lowerRightX-cifMap[poly.layer].compensation)/ReadCif.cifUnitsPerLambda], Real.RoundLI[(lowerY+cifMap[poly.layer].compensation)/ReadCif.cifUnitsPerLambda]], points]; points _ CONS[ [Real.RoundLI[(upperRightX-cifMap[poly.layer].compensation)/ReadCif.cifUnitsPerLambda], Real.RoundLI[(upperY-cifMap[poly.layer].compensation)/ReadCif.cifUnitsPerLambda]], points]; points _ CONS[ [Real.RoundLI[(upperLeftX+cifMap[poly.layer].compensation)/ReadCif.cifUnitsPerLambda], Real.RoundLI[(upperY-cifMap[poly.layer].compensation)/ReadCif.cifUnitsPerLambda]], points]; [ob, offset] _ CDPolygons.CreatePolygon[points, cifMap[poly.layer].cdLevel]; [] _ CDCells.IncludeOb[ design: IF curCell.cdCellObj = NIL THEN design ELSE NIL, cell: curCell.cdCellObj, ob: ob, position: offset, orientation: 0, cellCSystem: originCoords, obCSystem: interrestCoords, mode: dontPropagate]; END ELSE [] _ CDCells.IncludeOb[ design: IF curCell.cdCellObj = NIL THEN design ELSE NIL, cell: curCell.cdCellObj, ob: CDRects.CreateRect[ size: [Real.RoundLI[(upperRightX-upperLeftX-2*cifMap[poly.layer].compensation)/ReadCif.cifUnitsPerLambda], Real.RoundLI[(upperY-lowerY-2*cifMap[poly.layer].compensation)/ReadCif.cifUnitsPerLambda]], l: cifMap[poly.layer].cdLevel], position: [Real.RoundLI[(upperLeftX+cifMap[poly.layer].compensation)/ReadCif.cifUnitsPerLambda], Real.RoundLI[(lowerY+cifMap[poly.layer].compensation)/ReadCif.cifUnitsPerLambda]], orientation: 0, cellCSystem: originCoords, obCSystem: interrestCoords, mode: dontPropagate]; END; pd _ PolygonDefs.PolyCreate[]; FOR l: LIST OF ParserTypeDefs.Point _ poly.p.first, l.rest WHILE l#NIL DO PolygonDefs.PolyVertex[polygon: pd, x: l.first.x, y: l.first.y]; ENDLOOP; PolygonDefs.PolyGenerate[polygon: pd, outputTrapezoid: IncludeTrapezoid]; END; IncludeWire: PROCEDURE[wire: Wire] = BEGIN OnAxis: PROC[p1, p2: ParserTypeDefs.Point] RETURNS[BOOL] = BEGIN IF p1.x = p2.x OR p1.y = p2.y THEN RETURN[TRUE] ELSE RETURN[FALSE]; END; FOR l: LIST OF ParserTypeDefs.Point _ wire.p.first, l.rest WHILE l.rest#NIL DO IF OnAxis[l.first, l.rest.first] THEN [] _ CDCells.IncludeOb[ design: IF curCell.cdCellObj = NIL THEN design ELSE NIL, cell: curCell.cdCellObj, ob: CDRects.CreateRect[ size: IF l.first.x = l.rest.first.x THEN [(wire.width-2*cifMap[wire.layer].compensation) / ReadCif.cifUnitsPerLambda, (ABS[l.first.y - l.rest.first.y] - 2*cifMap[wire.layer].compensation) / ReadCif.cifUnitsPerLambda] ELSE [(ABS[l.first.x - l.rest.first.x] - 2*cifMap[wire.layer].compensation) / ReadCif.cifUnitsPerLambda, (wire.width-2*cifMap[wire.layer].compensation) / ReadCif.cifUnitsPerLambda], l: cifMap[wire.layer].cdLevel], position: IF l.first.x = l.rest.first.x THEN [(l.rest.first.x - (wire.width/2) + cifMap[wire.layer].compensation) / ReadCif.cifUnitsPerLambda, ((IF l.first.y < l.rest.first.y THEN l.first.y ELSE l.rest.first.y) + cifMap[wire.layer].compensation) / ReadCif.cifUnitsPerLambda] ELSE [((IF l.first.x < l.rest.first.x THEN l.first.x ELSE l.rest.first.x) + cifMap[wire.layer].compensation) / ReadCif.cifUnitsPerLambda, (l.rest.first.y - (wire.width/2) + cifMap[wire.layer].compensation) / ReadCif.cifUnitsPerLambda], orientation: 0, cellCSystem: originCoords, obCSystem: interrestCoords, mode: dontPropagate] ELSE BEGIN RealPoint: TYPE = RECORD[x,y: REAL]; vector, unitVector: RealPoint; h0, h90, h180, h270: RealPoint; length: REAL; poly: IntStorageDefs.Polygon _ NEW[IntStorageDefs.PolygonRec]; poly.p _ NEW[ParserTypeDefs.PathRecord]; vector _ [l.rest.first.x-l.first.x, l.rest.first.y-l.first.y]; length _ RealFns.SqRt[vector.x*vector.x + vector.y*vector.y]; unitVector _ [vector.x/length, vector.y/length]; h0 _ [unitVector.x*wire.width/2, unitVector.y*wire.width/2]; h90 _ [-h0.y, h0.x]; h180 _ [-h90.y, h90.x]; h270 _ [-h180.y, h180.x]; poly.p.first _ CONS[[Real.RoundLI[l.first.x+h270.x], Real.RoundLI[l.first.y+h270.y]], poly.p.first]; poly.p.first _ CONS[[Real.RoundLI[l.rest.first.x+h270.x], Real.RoundLI[l.rest.first.y+h270.y]], poly.p.first]; poly.p.first _ CONS[[Real.RoundLI[l.rest.first.x+h90.x], Real.RoundLI[l.rest.first.y+h90.y]], poly.p.first]; poly.p.first _ CONS[[Real.RoundLI[l.first.x+h90.x], Real.RoundLI[l.first.y+h90.y]], poly.p.first]; poly.layer _ wire.layer; IncludePolygon[poly]; END; ENDLOOP; BEGIN -- fix up the endpoints with flashes or boxes prevSegOnAxis: BOOLEAN _ TRUE; nextSegOnAxis: BOOLEAN _ FALSE; flash: IntStorageDefs.Flash _ NEW[IntStorageDefs.FlashRec]; box: IntStorageDefs.Box _ NEW[IntStorageDefs.BoxRec]; flash.layer _ wire.layer; flash.diameter _ wire.width; box.layer _ wire.layer; box.length _ wire.width; box.width _ wire.width; box.xRot _ 1; box.yRot _ 0; box.bb.left _ 0; box.bb.bottom _ 0; box.bb.right _ wire.width; box.bb.top _ wire.width; FOR l: LIST OF ParserTypeDefs.Point _ wire.p.first, l.rest WHILE l.rest#NIL DO nextSegOnAxis _ OnAxis[l.first, l.rest.first]; IF prevSegOnAxis AND nextSegOnAxis THEN BEGIN box.center.x _ l.first.x; box.center.y _ l.first.y; IncludeBox[box]; END ELSE BEGIN flash.center.x _ l.first.x; flash.center.y _ l.first.y; IncludeFlash[flash]; END; prevSegOnAxis _ nextSegOnAxis; REPEAT FINISHED => BEGIN IF prevSegOnAxis THEN BEGIN box.center.x _ l.first.x; box.center.y _ l.first.y; IncludeBox[box]; END ELSE BEGIN flash.center.x _ l.first.x; flash.center.y _ l.first.y; IncludeFlash[flash]; END; END; ENDLOOP; END; END; IncludeFlash: PROC [flash: Flash] = BEGIN theta: REAL _ 360.0/numSides; poly: IntStorageDefs.Polygon _ NEW[IntStorageDefs.PolygonRec]; poly.p _ NEW[ParserTypeDefs.PathRecord]; FOR i: NAT IN [0..numSides) DO poly.p.first _ CONS[[Real.RoundLI[flash.diameter/2.0*RealFns.CosDeg[theta/2 + i*theta] + flash.center.x], Real.RoundLI[flash.diameter/2.0*RealFns.SinDeg[theta/2 + i*theta] + flash.center.y]], poly.p.first]; ENDLOOP; poly.layer _ flash.layer; IncludePolygon[poly]; END; IncludeMBox: PROC [box: MBox] = BEGIN [] _ CDCells.IncludeOb[ design: IF curCell.cdCellObj = NIL THEN design ELSE NIL, cell: curCell.cdCellObj, ob: CDRects.CreateRect[ size: [(box.bb.right-box.bb.left-2*cifMap[box.layer].compensation)/ReadCif.cifUnitsPerLambda, (box.bb.top-box.bb.bottom-2*cifMap[box.layer].compensation)/ReadCif.cifUnitsPerLambda], l: cifMap[box.layer].cdLevel], position: [(box.bb.left+cifMap[box.layer].compensation)/ReadCif.cifUnitsPerLambda, (box.bb.bottom+cifMap[box.layer].compensation)/ReadCif.cifUnitsPerLambda], orientation: 0, cellCSystem: originCoords, obCSystem: interrestCoords, mode: dontPropagate]; END; IncludeBox: PROC [box: Box] = BEGIN IF (box.xRot=0 AND box.yRot#0) OR (box.xRot#0 AND box.yRot=0) THEN BEGIN xLength: LONG CARDINAL _ IF box.xRot#0 THEN box.length ELSE box.width; yWidth: LONG CARDINAL _ IF box.xRot#0 THEN box.width ELSE box.length; [] _ CDCells.IncludeOb[ design: IF curCell.cdCellObj = NIL THEN design ELSE NIL, cell: curCell.cdCellObj, ob: CDRects.CreateRect[ size: [(xLength-2*cifMap[box.layer].compensation)/ReadCif.cifUnitsPerLambda, (yWidth-2*cifMap[box.layer].compensation)/ReadCif.cifUnitsPerLambda], l: cifMap[box.layer].cdLevel], position: [(box.center.x-xLength/2+cifMap[box.layer].compensation)/ReadCif.cifUnitsPerLambda, (box.center.y-yWidth/2+cifMap[box.layer].compensation)/ReadCif.cifUnitsPerLambda], orientation: 0, cellCSystem: originCoords, obCSystem: interrestCoords, mode: dontPropagate] END ELSE ReportError["Only Manhatten boxes implemented", FatalError]; END; pending: LIST OF Object _ LIST[sym.guts]; objectCount: INT _ 0; curCell: Cell _ NEW[CellRec]; Cell: TYPE = REF CellRec; CellRec: TYPE = RECORD [ cdCellObj: CD.Object, name: Rope.ROPE _ NIL, errorInSubCell: BOOLEAN _ FALSE, warningInSubCell: BOOLEAN _ FALSE]; IF NOT root THEN -- we are including a cell into the directory only, i.e. it is not an application, i.e. it does not appear on the screen BEGIN sym.expanded _ TRUE; curCell.cdCellObj _ CDCells.CreateEmptyCell[]; curCell.name _ NIL; END; WHILE pending # NIL DO WHILE pending.first # NIL DO WITH pending.first.first SELECT FROM call: Call => BEGIN SELECT TRUE FROM call.callee.bound => BEGIN position: CD.Position; orientation: CD.Orientation; IF ~OrthogonalTransform[call] THEN ReportError[IO.PutFR["Cell %g called with non-orthogonal cell placement", IO.card[call.callee.symNumber]], FatalError]; [position, orientation] _ CIFtoCDPosition[call]; [] _ CDCells.IncludeOb[ design: IF curCell.cdCellObj = NIL THEN design ELSE NIL, cell: curCell.cdCellObj, ob: NARROW[call.callee.spare, Cell].cdCellObj, -- Obj REF is hung on spare position: position, orientation: orientation, cellCSystem: originCoords, obCSystem: originCoords, mode: dontPropagate ]; curCell.errorInSubCell _ NARROW[call.callee.spare, Cell].errorInSubCell; -- propagate errors and warnings curCell.warningInSubCell _ NARROW[call.callee.spare, Cell].warningInSubCell; END; call.callee.defined AND ~call.callee.expanded => BEGIN call.callee.expanded _ TRUE; pending.first _ CONS[curCell, pending.first]; -- push the current cell curCell _ NEW[CellRec]; curCell.cdCellObj _ CDCells.CreateEmptyCell[]; -- get a new one pending.first _ CONS[call.callee, pending.first]; -- CONS return symbol pending _ CONS[call.callee.guts, pending]; -- CONS contents of cell LOOP; -- don't pop the stack END; call.callee.expanded => ReportError[IO.PutFR["Cell %g calls itself, inner call ignored.", IO.int[call.callee.symNumber]], FatalError]; ~call.callee.defined => ReportError[IO.PutFR["Cell %g is undefined, call ignored.", IO.int[call.callee.symNumber]], FatalError]; ENDCASE => ERROR; END; symbol: STEntry => BEGIN prevCell: Cell _ curCell; symbol.expanded _ FALSE; -- returning from symbol call symbol.bound _ TRUE; symbol.spare _ curCell; -- keep the obj ref on spare IF curCell.name = NIL THEN curCell.name _ IO.PutFR["CifCell #%g", IO.int[nextCellNumber _ nextCellNumber + 1]]; IF curCell.errorInSubCell THEN ReportError[Rope.Cat["*** Error in ", curCell.name], FatalError]; IF curCell.warningInSubCell THEN ReportError[Rope.Cat["* Warning in ", curCell.name], Warning]; [] _ CDCells.RepositionCell[curCell.cdCellObj, NIL]; [] _ CDDirectory.Include[ design: design, object: curCell.cdCellObj, alternateName: curCell.name]; pending.first _ pending.first.rest; -- remove called symbol curCell _ NARROW[pending.first.first]; -- restore context END; box: Box => IF box.layer # LAST[CARDINAL] THEN IncludeBox[box]; box: MBox => IF box.layer # LAST[CARDINAL] THEN IncludeMBox[box]; flash: Flash => IF flash.layer # LAST[CARDINAL] THEN IncludeFlash[flash]; poly: Polygon => IF poly.layer # LAST[CARDINAL] THEN IncludePolygon[poly]; wire: Wire => IF wire.layer # LAST[CARDINAL] THEN IncludeWire[wire]; userOb: UserOb => ReportError["UserOb not defined", Warning]; userCmd: UserCmd => BEGIN IF userCmd.command = 9 AND pending.first.rest = NIL THEN curCell.name _ NARROW[userCmd.data] ELSE ReadCifUserCmd.ParseUserCmd[userCmd, curCell.cdCellObj, design, ReportError]; END; ENDCASE => ERROR; pending.first.first _ NIL; -- help the garbage collector pending.first _ pending.first.rest; objectCount _ objectCount+1; IF objectCount MOD 1000 = 0 THEN IODefs.PostIt[IO.PutFR["Objects read: %g", IO.int[objectCount]]]; ENDLOOP; pending _ pending.rest; ENDLOOP; IF NOT root THEN -- we are including a cell into the directory only BEGIN sym.expanded _ FALSE; sym.bound _ TRUE; sym.spare _ curCell; -- keep the cd name on spare IF curCell.name = NIL THEN curCell.name _ IO.PutFR["CifCell #%g", IO.int[nextCellNumber _ nextCellNumber + 1]]; IF curCell.errorInSubCell THEN ReportError[Rope.Cat["*** Error in ", curCell.name], FatalError]; IF curCell.warningInSubCell THEN ReportError[Rope.Cat["* Warning in ", curCell.name], Warning]; [] _ CDCells.RepositionCell[curCell.cdCellObj, NIL]; [] _ CDDirectory.Include[ design: design, object: curCell.cdCellObj, alternateName: curCell.name]; END END; -- InstantiateSymbol MapSymbols[proc: ClearBinding]; -- clear symbol table InstantiateSymbol[sym: rootSymbol, root: TRUE]; -- instantiate top level design MapSymbols[proc: IncludeObjectsNotDrawn]; -- includes cells which are not drawn [] _ CDViewer.CreateViewer[design: design] END; FinishOutput: PUBLIC PROCEDURE[] RETURNS[BOOL] = BEGIN cifMap _ NIL; design _ NIL; unknownLayers _ NIL; nextCellNumber _ 0; RETURN[TRUE]; END; Abort: PROCEDURE[] = BEGIN [] _ FinishOutput[]; ERROR ABORTED; END; END. zCIFIntPhase2.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. by Jim Gasbarro March 12, 1985 12:38:00 pm PST Ripped off from: File IntPhase2.mesa Last Edited by: Gasbarro, December 18, 1985 9:55:52 am PST McCreight, April 14, 1986 2:05:17 pm PST Frank Bowers April 21, 1986 9:09:25 am PST -- layer not in map, see if there is a corresponding Chipndale Level -- no corresponding Chipndale Level, add to unknownLayers if necessary -- layer was not in map, but was known to Chipndale, add to map if last object on list (first cif command after a DefineStart) is a UserCmd = 9 then assume that it is the cell name Κ>˜Jšœ™Jšœ Οmœ1™Jšœ˜šžœžœžœž˜!Jšœ˜Jšžœ˜—Jšœ ˜ Jšœq˜qJšœ˜Jšœ˜Jšžœ˜—J˜š  œžœž œ˜#Jšž˜š  œž œ˜'Jšž˜Jšœ žœ˜Jšžœ˜J˜—š œž œ˜1Jšž˜Jšžœžœ žœ#žœ˜?Jšžœ˜J˜—š  œž œ žœ žœžœ˜fJšž˜Jšœž˜Jšœ˜Jšœ ˜ Jšœ1˜1Jšœ žœ#˜6Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜J˜šžœ ž˜Jšž˜šžœžœž˜Jšœžœ˜#Jšœžœ˜#Jšœžœ˜#Jšœžœ˜#Jšžœžœ˜—Jšžœž˜Jšž˜šžœžœž˜Jšœžœ˜#Jšœžœ˜#Jšœžœ˜#Jšœžœ˜#Jšžœžœ˜—Jšžœ˜—Jšžœ˜Jšžœ˜J˜—š œž œ žœžœ˜;JšžœŸJ˜QJšžœžœžœžœžœžœžœžœžœžœ˜sJšžœ˜J˜—š œž œžœ˜8Jšž˜šΟb œ"˜-Jšž˜Jšœ˜šžœ ž˜Jšœ'žœ˜,Jšœ&žœ˜+Jšžœžœ˜—Jšžœ˜J˜—š œž œ˜*Jšž˜Jšœ"˜"J˜š œž œCžœ˜eJšž˜šžœžœž˜Jšœ žœ˜(Jšœ>˜>Jšœ=˜=Jšœ0˜0Jšœ<˜Jšœ žœ˜(šžœžœžœž˜Jšœžœ»˜ΞJšžœ˜Jšœ˜—Jšœ˜Jšžœ˜J˜—š  œžœ˜Jšž˜šœ˜Jš œžœžœžœžœžœ˜8Jšœ˜šœ˜JšœΆ˜ΆJšœ˜—Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜—Jšžœ˜J˜—š  œžœ ˜Jšž˜š žœ žœ žœ žœ ž˜BJšž˜Jš œ žœžœžœ žœ žœ ˜FJš œžœžœžœ žœ žœ ˜Ešœ˜Jš œžœžœžœžœžœ˜8Jšœ˜šœ˜Jšœ“˜“Jšœ˜—Jšœ°˜°Jšœ˜Jšœ˜Jšœ˜Jšœ˜—Jšž˜Jšžœ=˜A—Jšžœ˜J˜—Jšœ žœžœ žœ ˜)Jšœ žœ˜Jšœžœ ˜Jšœžœžœ ˜šœ žœžœ˜Jšœ ž œ˜Jšœ žœžœ˜Jšœžœžœ˜ Jšœžœžœ˜#—J˜šžœžœžœŸx˜‰Jšž˜Jšœžœ˜Jšœ.˜.Jšœžœ˜Jšžœ˜—šžœ žœž˜šžœžœž˜šžœžœž˜$šœ ˜ Jšž˜šžœžœž˜šœ˜Jšž˜Jšœ žœ ˜Jšœ žœ˜Jšžœžœx˜šJšœ0˜0šœ˜Jš œžœžœžœžœžœ˜8Jšœ˜Jšœžœ&Ÿ˜KJšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜—Jšœžœ*Ÿ ˜iJšœžœ+˜LJšžœ˜—šœžœ˜0Jšž˜Jšœžœ˜JšœžœŸ˜FJšœ žœ ˜Jšœ/Ÿ˜?JšœžœŸ˜GJšœ žœŸ˜CJšžœŸ˜Jšžœ˜—šœ˜Jšœ žœ4žœ*˜n—šœ˜Jšœ žœ.žœ*˜h—Jšžœžœ˜—Jšžœ˜—˜Jšž˜Jšœ˜JšœžœŸ˜6Jšœžœ˜JšœŸ˜4Jš žœžœžœžœžœžœ+˜oJšžœžœB˜`Jšžœžœ?˜_Jšœ4˜4šœ˜Jšœ˜Jšœ˜Jšœ˜—Jšœ$Ÿ˜;Jšœ žœŸ˜9Jšžœ˜—˜ Jšžœ žœžœžœ˜3—˜ Jšžœ žœžœžœ˜4—˜Jšžœžœžœžœ˜9—˜Jšžœžœžœžœ˜9—˜ Jšžœžœžœžœ˜6—˜Jšœ+˜+—˜Jšž˜Jšœt™tšžœžœžœžœ˜9Jšœžœ˜#—šž˜JšœM˜M—Jšžœ˜—Jšžœžœ˜J˜—JšœžœŸ˜9Jšœ#˜#Jšœ˜šžœ žœ ž˜ Jšœžœžœ˜A—Jšžœ˜—Jšœ˜Jšžœ˜—šžœžœžœŸ2˜CJšž˜Jšœžœ˜Jšœ žœ˜JšœŸ˜1Jš žœžœžœžœžœžœ+˜oJšžœžœB˜`Jšžœžœ?˜_Jšœ/žœ˜4šœ˜Jšœ˜Jšœ˜Jšœ˜—Jšž˜—JšžœŸ˜—J˜Jšœ Ÿ˜5Jšœ)žœŸ˜PJšœ*Ÿ%˜OJšœ+˜+Jšžœ˜J˜—š   œžœž œžœžœ˜0Jšž˜Jšœ ž˜ Jšœ žœ˜ Jšœžœ˜Jšœ˜Jšžœžœ˜ Jšžœ˜J˜—š œž œ˜Jšž˜Jšœ˜Jšžœžœ˜Jšžœ˜J˜Jšžœ˜——…—FT^