DIRECTORY CD USING [Error, ErrorCode, Instance, Number, Object, Orientation, Position, Rect], CDBasics USING [BaseOfRect, Extend, Intersect, Intersection, NonEmpty, ToRect], CDOrient USING [ComposeOrient, DeMapRect, MapRect, original, RectAt, rotate180, rotate270, rotate90], CStitching USING [Area, IsEmpty, ChangeRect, Rect, EEdge, EN, EnumerateArea, ResetTesselation, ChangeEnumerateArea, ListArea, NE, NewTesselation, NEdge, RectProc, TileProc, Region, SEdge, SW, Tesselation, Tile, FindTile, WEdge, WS], PrincOpsUtils USING [], SX USING [AdjustNode, Circuit, CircuitNode, Constraint, ConstraintResolution, FindRootNode, GeometricRule, LogicalCell, LookupNode, MergeNode, nodeIndex, NormalizeCircuit, spaceIndex, SpinifexLayerIndex], SXAccess USING [design, stopFlag, sxTech], SXAccessInternal USING [GetLogicalCell, PutError], SXLayers USING [], SXQuadTree USING [AreaSplit, Create, Dimension, Enumerate, PerRectProc, QuadTree, QuadTreeRoot, Rectangle, Rectangles], TerminalIO USING [WriteRope]; SXLayersImpl: CEDAR PROGRAM IMPORTS CD, CDBasics, CDOrient, CStitching, SX, SXAccess, SXAccessInternal, SXQuadTree, TerminalIO EXPORTS SXLayers = BEGIN TilePtr: TYPE = CStitching.Tile; nothing: REF INT ~ NEW [INT]; Constraint: TYPE = SX.Constraint; Rectangle: TYPE = SXQuadTree.Rectangle; Node: TYPE = SX.CircuitNode; ConflictWorlds: TYPE = ARRAY SX.SpinifexLayerIndex OF CStitching.Tesselation; GeometryWorlds: TYPE = ARRAY SX.SpinifexLayerIndex OF CStitching.Tesselation; ConstraintQueue: TYPE = ARRAY SX.SpinifexLayerIndex OF LIST OF REF CStitching.Region _ ALL[NIL]; conflictWorlds: ConflictWorlds; geometryWorlds: GeometryWorlds; saveTesselations: BOOL _ FALSE; freeTesselations: BOOL _ FALSE; UseLevelOrderHeuristic: PROC [clipRect: CD.Rect, old, new: SXQuadTree.Rectangles] RETURNS [BOOLEAN] = BEGIN ThinLimit: CD.Number = SXAccess.design.technology.lambda * 10; RETURN [(clipRect.x2 - clipRect.x1 > ThinLimit) AND (clipRect.y2 - clipRect.y1 > ThinLimit)] END; -- UseLevelOrderHeuristic ComputeConflicts: PROC [ qt: REF SXQuadTree.QuadTree, conflictWorld: CStitching.Tesselation, layer: SX.SpinifexLayerIndex, cir: REF SX.Circuit] = BEGIN descentDepth: INT _ 1; OccupyByNode: CStitching.RectProc = BEGIN WITH oldValue SELECT FROM a: ATOM => IF a # $Conflict THEN CD.Error [ec: other, explanation: "Tesselation screwed up (alien tile)"]; r: REF Rectangle => IF data # r THEN CStitching.ChangeRect [conflictWorld, rect, $Conflict]; s: SXQuadTree.Rectangles => IF data # s.first THEN BEGIN CStitching.ChangeRect [conflictWorld, rect, data]; Flatten [rect, s] END ELSE CD.Error [ec: other, explanation: "Quad-tree screwed up (subcell found twice)"]; c: REF Constraint => IF NOT SameConstraints [ResolveConstraints [c, data, SXAccess.sxTech.constraintResolutions[layer]], data] THEN CStitching.ChangeRect [conflictWorld, rect, $Conflict]; ENDCASE => IF oldValue = NIL THEN CStitching.ChangeRect [conflictWorld, rect, data] ELSE CD.Error [ec: other, explanation: "Tesselation screwed up (alien tile)"] END; -- OccupyByNode OccupyByConstr: CStitching.RectProc = BEGIN WITH oldValue SELECT FROM a: ATOM => IF a # $Conflict THEN CD.Error [ec: other, explanation: "Tesselation screwed up (alien tile)"]; r: REF Rectangle => IF NOT SameConstraints [ResolveConstraints[NARROW[data], r, SXAccess.sxTech.constraintResolutions[layer]], r] THEN CStitching.ChangeRect [conflictWorld, rect, $Conflict]; s: SXQuadTree.Rectangles => BEGIN conflictWorld.ChangeRect[rect, data]; Flatten [rect, s] END; c: REF Constraint => BEGIN resolution: REF ANY = ResolveConstraints [c, data, SXAccess.sxTech.constraintResolutions [layer]]; IF (NOT SameConstraints[resolution, c] AND NOT SameConstraints[resolution, data]) THEN CStitching.ChangeRect [conflictWorld, rect, $Conflict] ELSE IF NOT SameConstraints[resolution, c] THEN CStitching.ChangeRect [conflictWorld, rect, resolution]; END; ENDCASE => IF oldValue = NIL THEN CStitching.ChangeRect [conflictWorld, rect, data] ELSE CD.Error [ec: other, explanation: "Tesselation screwed up (alien tile)"] END; -- OccupyByConstr OccupyByAppl: CStitching.RectProc = BEGIN WITH oldValue SELECT FROM a: ATOM => IF a # $Conflict THEN CD.Error [ec: other, explanation: "Tesselation screwed up (alien tile)"]; r: REF Rectangle => BEGIN IF NARROW[data, SXQuadTree.Rectangles].first = r THEN CD.Error [ec: other, explanation: "Rectangle has already been processed"]; Flatten [rect, NARROW[data]] END; s: SXQuadTree.Rectangles => IF data # s THEN IF UseLevelOrderHeuristic [rect, s, NARROW[data]] THEN DescendOneLevel [rect, s, NARROW[data]] ELSE BEGIN CStitching.ChangeRect [conflictWorld, rect, NIL]; Flatten [rect, s]; Flatten [rect, NARROW[data]] END; c: REF Constraint => Flatten [rect, NARROW[data]]; ENDCASE => IF oldValue = NIL THEN CStitching.ChangeRect [conflictWorld, rect, data] ELSE CD.Error [ec: other, explanation: "Tesselation screwed up (alien tile)"] END; -- OccupyByAppl Flatten: PROC [clipRect: CD.Rect, applRef: SXQuadTree.Rectangles] = BEGIN appl: CD.Instance = NARROW [applRef.first.nodeInformation]; FlattenCell: PROC [appl: CD.Instance, pos: CD.Position, orient: CD.Orientation] = BEGIN MapRect: PROC [inCellRect: CD.Rect] RETURNS [inWorldRect: CD.Rect] = INLINE BEGIN inWorldRect _ CDBasics.Intersection [ CDOrient.MapRect [itemInCell: inCellRect, cellSize: appl.ob.size, cellInstOrient: orient, cellInstPos: pos], clipRect] END; -- MapRect FlattenTree: PROC [qt: REF SXQuadTree.QuadTree] = BEGIN FOR boxes: SXQuadTree.Rectangles _ qt.boxes, boxes.rest WHILE boxes#NIL DO WITH boxes.first.nodeInformation SELECT FROM node: REF Node => { r: CD.Rect = MapRect [boxes.first.interestBound]; IF CDBasics.NonEmpty[r] THEN CStitching.ChangeEnumerateArea [conflictWorld, r, OccupyByNode, applRef.first, nothing] }; constr: REF Constraint => { r: CD.Rect = MapRect[boxes.first.interestBound]; IF CDBasics.NonEmpty[r] THEN CStitching.ChangeEnumerateArea [conflictWorld, r, OccupyByConstr, constr, nothing] }; subAppl: CD.Instance => IF CDBasics.Intersect [boxes.first.interestBound, mappedClip] THEN { FlattenCell [ subAppl, CDBasics.BaseOfRect [CDOrient.MapRect [ itemInCell: CDOrient.RectAt [pos: subAppl.location, size: subAppl.ob.size, orient: subAppl.orientation], cellSize: appl.ob.size, cellInstOrient: orient, cellInstPos: pos]], CDOrient.ComposeOrient [subAppl.orientation, orient]] }; ENDCASE => ERROR; -- Quad tree srewed up ENDLOOP; IF SXAccess.stopFlag^ THEN ERROR ABORTED; IF mappedClip.y2>qt.midY AND qt.subTrees[north]#NIL THEN FlattenTree[qt.subTrees[north]]; IF mappedClip.y1qt.midX AND qt.subTrees[east]#NIL THEN FlattenTree[qt.subTrees[east]]; IF mappedClip.x1 IF a # $Conflict THEN CD.Error [ec: other, explanation: "Tesselation screwed up (alien tile)"]; r: REF Rectangle => IF data # r THEN CD.Error [ec: other, explanation: "Tesselation screwed up"]; s: SXQuadTree.Rectangles => CStitching.ChangeRect [conflictWorld, rect, data]; c: REF Constraint => CStitching.ChangeRect [conflictWorld, rect, data]; ENDCASE => ERROR END; -- BidByNode BidByConstr: CStitching.RectProc = BEGIN WITH oldValue SELECT FROM a: ATOM => IF a # $Conflict THEN CD.Error [ec: other, explanation: "Tesselation screwed up (alien tile)"]; r: REF Rectangle => IF new.first # r THEN CD.Error [ec: other, explanation: "Tesselation screwed up"]; s: SXQuadTree.Rectangles => IF s # new THEN CStitching.ChangeRect [conflictWorld, rect, data]; c: REF Constraint => BEGIN resolution: REF ANY = ResolveConstraints [c, data, SXAccess.sxTech.constraintResolutions [layer]]; IF NOT SameConstraints [resolution, c] THEN CStitching.ChangeRect [conflictWorld, rect, resolution]; END; ENDCASE => ERROR; END; -- BidByConstr BidByAppl: CStitching.RectProc = BEGIN WITH oldValue SELECT FROM a: ATOM => IF a # $Conflict THEN CD.Error [ec: other, explanation: "Tesselation screwed up (alien tile)"]; r: REF Rectangle => IF NARROW[data, SXQuadTree.Rectangles].first # r THEN CD.Error [ec: other, explanation: "Tesselation screwed up"]; s: SXQuadTree.Rectangles => IF data # s THEN CStitching.ChangeRect [conflictWorld, rect, data]; c: REF Constraint => CStitching.ChangeRect [conflictWorld, rect, data]; ENDCASE => ERROR; END; -- BidByAppl DescendCell: PROC [appl: CD.Instance, pos: CD.Position, orient: CD.Orientation] = BEGIN MapRect: PROC [inCellRect: CD.Rect] RETURNS [inWorldRect: CD.Rect] = INLINE BEGIN inWorldRect _ CDBasics.Intersection [ CDOrient.MapRect [ itemInCell: inCellRect, cellSize: appl.ob.size, cellInstOrient: orient, cellInstPos: pos], clipRect] END; -- MapRect DescendTree: PROC [qt: REF SXQuadTree.QuadTree] = BEGIN IF qt=NIL THEN RETURN; FOR boxes: SXQuadTree.Rectangles _ qt.boxes, boxes.rest WHILE boxes#NIL DO WITH boxes.first.nodeInformation SELECT FROM node: REF Node => { r: CD.Rect = MapRect [boxes.first.interestBound]; IF CDBasics.NonEmpty[r] THEN CStitching.ChangeEnumerateArea [conflictWorld, r, BidByNode, new.first, nothing] }; constr: REF Constraint => { r: CD.Rect = MapRect [boxes.first.interestBound]; IF CDBasics.NonEmpty[r] THEN CStitching.ChangeEnumerateArea [conflictWorld, r, BidByConstr, constr, nothing] }; subAppl: CD.Instance => IF CDBasics.Intersect [boxes.first.interestBound, mappedClip] THEN { IF levelCount > 0 THEN DescendCell [ subAppl, CDBasics.BaseOfRect [CDOrient.MapRect [ itemInCell: CDOrient.RectAt [pos: subAppl.location, size: subAppl.ob.size, orient: subAppl.orientation], cellSize: appl.ob.size, cellInstOrient: orient, cellInstPos: pos]], CDOrient.ComposeOrient [subAppl.orientation, orient]] ELSE { r: CD.Rect = MapRect [boxes.first.interestBound]; IF CDBasics.NonEmpty[r] THEN CStitching.ChangeEnumerateArea [conflictWorld, r, BidByAppl, new, nothing] } }; ENDCASE => ERROR; ENDLOOP; IF mappedClip.y2 > qt.midY AND qt.subTrees[north] # NIL THEN DescendTree [qt.subTrees[north]]; IF mappedClip.y1qt.midX AND qt.subTrees[east]#NIL THEN DescendTree [qt.subTrees[east]]; IF mappedClip.x1 CStitching.ChangeEnumerateArea [conflictWorld, boxes.first.interestBound, OccupyByNode, boxes.first, nothing]; constr: REF Constraint => CStitching.ChangeEnumerateArea [conflictWorld, boxes.first.interestBound, OccupyByConstr, constr, nothing]; appl: CD.Instance => CStitching.ChangeEnumerateArea [conflictWorld, boxes.first.interestBound, OccupyByAppl, boxes, nothing]; ENDCASE => ERROR; ENDLOOP; FOR quad: SXQuadTree.AreaSplit IN SXQuadTree.AreaSplit DO ComputeConflicts [qt.subTrees[quad], conflictWorld, layer, cir] ENDLOOP; IF descentDepth#1 THEN CD.Error [ec: other, explanation: "ComputeConflicts: descentDepth#1"] END; -- ComputeConflicts SameConstraints: PROC [c1, c2: REF ANY] RETURNS [BOOLEAN] = { IF c1 = c2 THEN RETURN[TRUE]; WITH c1 SELECT FROM con1: REF Constraint => { WITH c2 SELECT FROM con2: REF Constraint => RETURN [con1.name = con2.name]; ENDCASE => RETURN [FALSE]; }; ENDCASE => RETURN[FALSE]; }; -- SameConstraints ResolveConstraints: PROC [newConstraint: REF Constraint, oldValue: REF ANY, resolution: REF SX.ConstraintResolution] RETURNS [REF ANY] = BEGIN WITH oldValue SELECT FROM oldConstraint: REF Constraint => { specificNode: REF SX.CircuitNode _ NIL; result: REF Constraint _ resolution[newConstraint.index][oldConstraint.index]; IF result=NIL THEN CD.Error[ec~ other, explanation~ "Invalid ConstraintResolution"]; specificNode _ IF newConstraint.specificCorrespondingNode # NIL THEN newConstraint.specificCorrespondingNode ELSE oldConstraint.specificCorrespondingNode; IF specificNode # NIL THEN { result _ NEW[Constraint _ result^]; result.specificCorrespondingNode _ specificNode}; RETURN [result] }; -- end oldConstraint: REF CircuitConstraint node: REF Node => { result: REF Constraint _ resolution[newConstraint.index][SX.nodeIndex]; IF result=NIL THEN RETURN [node] ELSE { IF newConstraint.specificCorrespondingNode # NIL THEN { result _ NEW[Constraint _ result^]; result.specificCorrespondingNode _ newConstraint.specificCorrespondingNode}; RETURN [result]}; }; rect: REF Rectangle => { result: REF Constraint _ resolution[newConstraint.index][SX.nodeIndex]; IF result=NIL THEN RETURN [rect] ELSE { IF newConstraint.specificCorrespondingNode # NIL THEN { result _ NEW[Constraint _ result^]; result.specificCorrespondingNode _ newConstraint.specificCorrespondingNode}; RETURN [result]}; }; ENDCASE => ERROR END; -- ResolveConstraints BloatConflictsIntoAOIs: PROC [ cir: REF SX.Circuit, layer: SX.SpinifexLayerIndex, conflictWorld: CStitching.Tesselation] RETURNS [cleanedConflictWorld: CStitching.Tesselation] = BEGIN CopyBloated: CStitching.TileProc = BEGIN IF tile.value = $Conflict THEN cleanedConflicts.ChangeRect [ rect: CDBasics.Extend[tile.Area, bloatValue], new: $AOI]; END; -- CopyBloated bloatValue: INT _ SXAccess.sxTech.layerInterestBloat [layer]; cleanedConflicts: CStitching.Tesselation _ CStitching.NewTesselation []; [] _ conflictWorld.EnumerateArea [cir.spinifexLayers[layer].size, CopyBloated]; CStitching.ResetTesselation [conflictWorld]; cleanedConflictWorld _ cleanedConflicts; END; -- BloatConflictsIntoAOIs InstantiateAOIs: PROC [ cir: REF SX.Circuit, qt: REF SXQuadTree.QuadTree, cellBBox: CD.Rect, flatLoc: CD.Position _ [0, 0], flatOrient: CD.Orientation _ CDOrient.original, appl: CD.Instance _ NIL, nameQualifier: LIST OF CD.Instance _ NIL, layer: SX.SpinifexLayerIndex, conflictWorld: CStitching.Tesselation, geometryWorld: CStitching.Tesselation, constraintQueue: ConstraintQueue] RETURNS [newConstraintQueue: ConstraintQueue] = BEGIN InstantiateTree: PROC [qt: REF SXQuadTree.QuadTree, qtBranchBox: CD.Rect] = BEGIN EnqueueConstraintRegion: PROC [cc: REF Constraint, r: CStitching.Rect] = BEGIN constraintQueue[layer] _ CONS [ NEW [CStitching.Region _ [rect: r, value: cc]], constraintQueue[layer]] END; -- EnqueueConstraintRegion branchBox: CD.Rect = CDOrient.MapRect [itemInCell: qtBranchBox, cellSize: (IF appl=NIL THEN [0, 0] ELSE appl.ob.size), cellInstOrient: flatOrient, cellInstPos: flatLoc]; IF conflictWorld.IsEmpty [branchBox] THEN RETURN; FOR boxes: SXQuadTree.Rectangles _ qt.boxes, boxes.rest WHILE boxes#NIL DO mappedDim: CD.Rect = CDOrient.MapRect [ itemInCell: SXQuadTree.Dimension [boxes.first], cellSize: (IF appl=NIL THEN [0, 0] ELSE appl.ob.size), cellInstOrient: flatOrient, cellInstPos: flatLoc]; IF NOT conflictWorld.IsEmpty[mappedDim] THEN { WITH boxes.first.nodeInformation SELECT FROM subAppl: CD.Instance => { cellQt: REF SXQuadTree.QuadTree; subcellBBox: CD.Rect; rect: CD.Rect = CDOrient.RectAt [pos: subAppl.location, size: subAppl.ob.size, orient: subAppl.orientation]; mappedRect: CD.Rect = CDOrient.MapRect [itemInCell: rect, cellSize: (IF appl=NIL THEN [0, 0] ELSE appl.ob.size), cellInstOrient: flatOrient, cellInstPos: flatLoc]; orient: CD.Orientation = CDOrient.ComposeOrient [ itemOrientInCell: subAppl.orientation, cellOrientInWorld: flatOrient]; [size: subcellBBox, geometry: cellQt] _ SXAccessInternal.GetLogicalCell[subAppl.ob].circuit.spinifexLayers[layer]; IF cellQt = NIL THEN LOOP; constraintQueue _ InstantiateAOIs [qt: cellQt, cellBBox: subcellBBox, flatLoc: CDBasics.BaseOfRect [mappedRect], flatOrient: orient, appl: subAppl, nameQualifier: CONS [subAppl, nameQualifier], -- following are the parameters added in the restructuration: -- cir: cir, layer: layer, conflictWorld: conflictWorld, geometryWorld: geometryWorld, constraintQueue: constraintQueue]; }; -- end rectangle is a subcell cNode: REF SX.CircuitNode => { name: REF SX.CircuitNode = IF (nameQualifier=NIL) THEN cNode ELSE cir.FindRootNode [subcircuitNode: cNode, qualifier: nameQualifier, insertIfNotInCircuit: TRUE].node; occupants: LIST OF REF CStitching.Region _ NARROW[geometryWorld.ListArea[mappedDim] ]; newList: LIST OF REF SX.CircuitNode _ LIST[name]; geometryWorld.ChangeRect [rect: mappedDim, new: newList]; WHILE occupants # NIL DO IF NOT conflictWorld.IsEmpty[occupants.first.rect] THEN WITH occupants.first.value SELECT FROM oldList: LIST OF REF SX.CircuitNode => -- oldList forms shared tail in all multiply occupied areas. Subsequent processing allows this. We must ensure that each node appears only once and that the list head is unique. IF ~(oldList.first = name AND oldList.rest=NIL) THEN { old: LIST OF REF SX.CircuitNode _ oldList; replaceList: LIST OF REF SX.CircuitNode; WHILE old#NIL DO IF old.first = name THEN EXIT; old _ old.rest; ENDLOOP; replaceList _ IF old#NIL THEN CONS [oldList.first, oldList.rest] ELSE CONS [name, oldList]; geometryWorld.ChangeRect [rect: occupants.first.rect, new: replaceList] }; ENDCASE => ERROR; occupants _ occupants.rest; ENDLOOP; }; -- end rectangle is a node cc: REF Constraint => EnqueueConstraintRegion[cc, mappedDim]; ENDCASE => ERROR -- This type not expected in quad tree } ENDLOOP; -- instantiate all rectangles in this Quad Tree node { IF qt.subTrees[north] # NIL THEN InstantiateTree [qt.subTrees[north], [x1: qtBranchBox.x1, y1: qt.midY, x2: qtBranchBox.x2, y2: qtBranchBox.y2]]; IF qt.subTrees[south] # NIL THEN InstantiateTree [qt.subTrees[south], [x1: qtBranchBox.x1, y1: qtBranchBox.y1, x2: qtBranchBox.x2, y2: qt.midY]]; IF qt.subTrees[east] # NIL THEN InstantiateTree [qt.subTrees[east], [x1: qt.midX, y1: qtBranchBox.y1, x2: qtBranchBox.x2, y2: qtBranchBox.y2]]; IF qt.subTrees[west] # NIL THEN InstantiateTree [qt.subTrees[west], [x1: qtBranchBox.x1, y1: qtBranchBox.y1, x2: qt.midX, y2: qtBranchBox.y2]]; } END; -- InstantiateTree InstantiateTree [qt, cellBBox]; newConstraintQueue _ constraintQueue END; -- InstantiateAOIs AnalyzeNodesInAOIs: PROC [ cir: REF SX.Circuit, layer: SX.SpinifexLayerIndex, geometryWorld: CStitching.Tesselation] = BEGIN MergeNodeTiles: CStitching.TileProc = BEGIN CountEdge: PROC [nl1, nl2: LIST OF REF SX.CircuitNode] RETURNS [INT] = BEGIN InAButNotB: PROC [a,b: LIST OF REF SX.CircuitNode] RETURNS [c: INT _ 0] = BEGIN FOR s1: LIST OF REF SX.CircuitNode _ a, s1.rest WHILE s1#NIL DO s2: LIST OF REF SX.CircuitNode _ b; WHILE s2#NIL DO IF s1.first=s2.first THEN EXIT; -- Shared sources. s2 _ s2.rest; ENDLOOP; IF s2=NIL THEN c _ c+1; -- node in s1 not in s2 ENDLOOP; END; -- InAButNotB RETURN [InAButNotB[a: nl1, b: nl2] + InAButNotB[a: nl2, b: nl1]] END; -- CountEdge IF SXAccess.stopFlag^ THEN ERROR ABORTED; WITH tile.value SELECT FROM cnList: LIST OF REF SX.CircuitNode => { list: LIST OF REF SX.CircuitNode _ cnList.rest; eastBound: INT = tile.EEdge; northBound: INT = tile.NEdge; westBound: INT = tile.WEdge; southBound: INT = tile.SEdge; r: CD.Rect = tile.Area; extraNodes: INT _ 0; n1: REF SX.CircuitNode = SX.LookupNode [cnList.first]; WHILE list # NIL DO n2: REF SX.CircuitNode = SX.LookupNode [list.first]; IF n1 # n2 THEN cir.MergeNode [to: n1, from: n2]; SX.AdjustNode [node: n1, layer: layer, area: -((r.x2 - r.x1)*(r.y2 - r.y1)), perim: 0]; extraNodes _ extraNodes+1; list _ list.rest; ENDLOOP; FOR tileSouth: TilePtr _ tile.WS, tileSouth.NE WHILE tileSouth.WEdge < eastBound DO IF tileSouth.value#NIL THEN WITH tileSouth.value SELECT FROM cnSouthList: LIST OF REF SX.CircuitNode => { n2: REF SX.CircuitNode = SX.LookupNode [cnSouthList.first]; IF n1 # n2 THEN cir.MergeNode [to: n1, from: n2]; SX.AdjustNode [node: n1, layer: layer, area: 0, perim: -(CountEdge[cnList, cnSouthList] * (MIN[eastBound, tileSouth.EEdge] - MAX[westBound, tileSouth.WEdge]))] }; constraint: REF Constraint => NULL; -- Ignore ENDCASE => ERROR ELSE -- tileSouth.value=NIL IF extraNodes > 0 THEN SX.AdjustNode [node: n1, layer: layer, area: 0, perim: -(extraNodes * (MIN[eastBound, tileSouth.EEdge] - MAX[westBound, tileSouth.WEdge]))] ENDLOOP; -- end FOR tileSouth FOR tileWest: TilePtr _ tile.SW, tileWest.EN WHILE tileWest.SEdge < northBound DO IF tileWest.value#NIL THEN WITH tileWest.value SELECT FROM cnWestList: LIST OF REF SX.CircuitNode => { n2: REF SX.CircuitNode; IF n1 # (n2_SX.LookupNode[cnWestList.first]) THEN cir.MergeNode [to: n1, from: n2]; SX.AdjustNode [node: n1, layer: layer, area: 0, perim: -(CountEdge[cnList, cnWestList] * (MIN[northBound, tileWest.NEdge] - MAX[southBound, tileWest.SEdge]))] }; constraint: REF Constraint => NULL; -- Ignore ENDCASE => ERROR ELSE IF extraNodes > 0 THEN SX.AdjustNode [node: n1, layer: layer, area: 0, perim: -(extraNodes * (MIN[northBound,tileWest.NEdge] - MAX[southBound,tileWest.SEdge]))] ENDLOOP; -- tileWest FOR tileNorth: TilePtr _ tile.EN, tileNorth.SW WHILE tileNorth.EEdge > westBound DO IF (tileNorth.value = NIL) AND (extraNodes > 0) THEN SX.AdjustNode [node: n1, layer: layer, area: 0, perim: -(extraNodes * (MIN[eastBound, tileNorth.EEdge] - MAX[westBound, tileNorth.WEdge]))] ENDLOOP; -- tileNorth FOR tileEast: TilePtr _ tile.NE, tileEast.WS WHILE tileEast.NEdge > southBound DO IF (tileEast.value=NIL) AND (extraNodes > 0) THEN SX.AdjustNode [node: n1, layer: layer, area: 0, perim: -(extraNodes * (MIN[northBound, tileEast.NEdge] - MAX[southBound, tileEast.SEdge]))] ENDLOOP; -- tileEast }; ENDCASE => ERROR END; -- MergeNodeTiles [] _ geometryWorld.EnumerateArea [rect: cir.spinifexLayers[layer].size, eachTile: MergeNodeTiles]; END; -- AnalyzeNodesInAOIs MixRec: TYPE = RECORD [newConstraint: REF Constraint, rect: CD.Rect, resolution: REF SX.ConstraintResolution, tilesToPaint: LIST OF REF CStitching.Region]; CheckRec: TYPE = RECORD [conflictWorld: CStitching.Tesselation, geometryWorld: CStitching.Tesselation, rules: LIST OF REF SX.GeometricRule, cell: REF SX.LogicalCell, cir: REF SX.Circuit]; DesignRuleCheckAOIs: PROC [ cir: REF SX.Circuit, cell: REF SX.LogicalCell, constraintQueue: ConstraintQueue, layer: SX.SpinifexLayerIndex, geometryWorld: CStitching.Tesselation, conflictWorld: CStitching.Tesselation] = BEGIN mixRef: REF MixRec = NEW [MixRec]; -- used to transfer parameters to MixConstraints checkRef: REF CheckRec = NEW[CheckRec]; -- used to transfer parameters to CheckTile FOR newCc: LIST OF REF CStitching.Region _ constraintQueue[layer], newCc.rest WHILE newCc#NIL DO mixRef^ _ [newConstraint: NARROW [newCc.first.value], rect: newCc.first.rect, resolution: SXAccess.sxTech.constraintResolutions[layer], tilesToPaint: NIL]; [] _ geometryWorld.EnumerateArea [rect: mixRef.rect, eachTile: MixConstraints, skip: $Nothing, data: mixRef]; FOR tilesToPaint: LIST OF REF CStitching.Region _ mixRef.tilesToPaint, tilesToPaint.rest WHILE tilesToPaint#NIL DO geometryWorld.ChangeRect [rect: tilesToPaint.first.rect, new: tilesToPaint.first.value] ENDLOOP; ENDLOOP; checkRef^ _ [conflictWorld: conflictWorld, geometryWorld: geometryWorld, cell: cell, cir: cir, rules: SXAccess.sxTech.rules[layer]]; [] _ geometryWorld.EnumerateArea [rect: cir.spinifexLayers[layer].size, eachTile: CheckTile, skip: $Nothing, data: checkRef] END; -- DesignRuleCheckAOIs MixConstraints: CStitching.TileProc = BEGIN mixRef: REF MixRec = NARROW [data]; newval: REF ANY _ mixRef.newConstraint; IF (tile.value = NIL) OR (tile.value # (newval _ ResolveConstraints [mixRef.newConstraint, tile.value, mixRef.resolution])) THEN mixRef.tilesToPaint _ CONS [ NEW [CStitching.Region _ [rect: CDBasics.Intersection [mixRef.rect, tile.Area], value: newval]], mixRef.tilesToPaint] END; -- MixConstraints FindNode: PROC [handle: REF CheckRec, nodeLayer: SX.SpinifexLayerIndex, r: CD.Rect] RETURNS [REF ANY] = BEGIN node: REF SX.CircuitNode; FoundIt: SIGNAL [n: REF SX.CircuitNode]; { ENABLE FoundIt => {node _ n; GOTO Done}; FindNodeInner: PROC [circuit: REF SX.Circuit, rect: CD.Rect, aChain: LIST OF CD.Instance] = BEGIN SearchForNode: SXQuadTree.PerRectProc = BEGIN WITH r.nodeInformation SELECT FROM n: REF SX.CircuitNode => IF CDBasics.Intersect[r.Dimension, rect] THEN { parentNode: REF SX.CircuitNode; newChain: LIST OF CD.Instance; [parentNode, newChain] _ handle.cir.FindRootNode[n, aChain]; IF newChain=NIL THEN SIGNAL FoundIt[parentNode] }; a: CD.Instance => { subcir: REF SX.Circuit = SXAccessInternal.GetLogicalCell[a.ob].circuit; subR: CD.Rect = CDOrient.DeMapRect [itemInWorld: rect, cellSize: a.ob.size, cellInstOrient: a.orientation, cellInstPos: a.location ]; FindNodeInner[subcir, subR, CONS[a, aChain]]; }; ENDCASE; END; -- SearchForNode SXQuadTree.Enumerate [circuit.spinifexLayers[nodeLayer], rect, SearchForNode, NIL] END; -- FindNodeInner FindNodeInner[handle.cir, r, NIL]; RETURN [NEW[INT]]; EXITS Done => RETURN [node]; } END; -- FindNode CheckTile: CStitching.TileProc = BEGIN Quadrants: TYPE = {ne, nw, sw, se}; DesignRuleCheckCorner: PROC [quadVals: ARRAY Quadrants OF REF ANY, pos: CD.Position] = BEGIN CheckArea: PROC [rule: REF SX.GeometricRule, orient: CD.Orientation, flavour: { convex, concave}] = BEGIN FindNodeAtCorner: PROC [n: REF ANY] RETURNS [REF ANY] = BEGIN WITH n SELECT FROM cn: REF SX.CircuitNode => RETURN [cn]; cc: REF Constraint => { IF cc.specificCorrespondingNode # NIL THEN RETURN[SX.LookupNode[cc.specificCorrespondingNode]]; --Bowers, September 4, 1985 2:10:58 pm PDT IF ~cc.hasCorrespondingNode THEN CD.Error[ec: other, explanation: "FindNodeAtCorner: ~cc.hasCorrespondingNode"]; RETURN [FindNode[handle, cc.correspondingNodeLayer, CDBasics.Extend[CDBasics.ToRect[pos, pos], 1]] ] }; ENDCASE => RETURN [NEW[INT]]; END; FindNodeInTile: PROC [n: REF ANY, area: CD.Rect] RETURNS [REF ANY] = BEGIN WITH n SELECT FROM cn: REF SX.CircuitNode => RETURN [cn]; cc: REF Constraint => { IF ~cc.hasCorrespondingNode THEN CD.Error[ec: other, explanation: "FindNodeInTile: ~cc.hasCorrespondingNode"]; RETURN [FindNode[handle, cc.correspondingNodeLayer, area]] }; ENDCASE => RETURN [NEW[INT]]; END; CheckTileValue: CStitching.TileProc = BEGIN CheckValue[tile.value, tile.Area] END; CheckValue: PROC [value: REF ANY, area: CD.Rect] = BEGIN IF SXAccess.stopFlag^ THEN ERROR ABORTED; WITH value SELECT FROM cn: REF SX.CircuitNode => { IF ~rule.trigger2[SX.nodeIndex] THEN RETURN; IF (rule.okIfConnected AND nodeAtCorner#NIL AND FindNodeAtCorner[nodeAtCorner] = SX.LookupNode[cn]) THEN RETURN; }; cc: REF Constraint => { IF ~rule.trigger2[cc.index] THEN RETURN; IF (rule.okIfConnected AND nodeAtCorner#NIL AND cc.hasCorrespondingNode AND FindNodeAtCorner[nodeAtCorner] = FindNodeInTile[cc, area] OR (cc.specificCorrespondingNode # NIL AND FindNodeAtCorner[nodeAtCorner] = SX.LookupNode[cc.specificCorrespondingNode])) THEN RETURN; }; cnList: LIST OF REF SX.CircuitNode => CD.Error[ec: other, explanation: "CheckValue: found a list of circuit nodes where a single one was expected"]; ENDCASE => { IF value=NIL THEN { IF ~rule.trigger2[SX.spaceIndex] THEN RETURN; } ELSE CD.Error [ec: other, explanation: "CheckValue: value # NIL"] }; IF notReportedYet AND conflictWorld.IsEmpty[ rect: CDOrient.MapRect[ itemInCell: [x1: -delta, y1: -delta, x2: len, y2: len], cellSize: [0, 0], cellInstOrient: orient, cellInstPos: pos ], skip: $AOI] THEN { -- FOUND AN ERROR rect: CD.Rect = CDOrient.MapRect [ itemInCell: [x1: 0, y1: 0, x2: rule.extent, y2: rule.extent], cellSize: [0, 0], cellInstOrient: orient, cellInstPos: pos]; SXAccessInternal.PutError [ob: handle.cell.cellObj, r: rect, message: rule.message]; notReportedYet _ FALSE } END; -- CheckValue notReportedYet: BOOLEAN _ TRUE; len: INT = rule.extent; delta: INT = 1; IF len = 0 THEN { IF notReportedYet THEN CheckValue[quadVals[ne], [pos.x, pos.y, pos.x+delta, pos.y+delta]]; IF notReportedYet THEN CheckValue[quadVals[nw], [pos.x, pos.y-delta, pos.x+delta, pos.y]]; IF notReportedYet THEN CheckValue[quadVals[sw], [pos.x-delta, pos.y-delta, pos.x, pos.y]]; IF notReportedYet THEN CheckValue[quadVals[se], [pos.x-delta, pos.y, pos.x, pos.y+delta]]; } ELSE { SELECT flavour FROM convex => { [] _ handle.geometryWorld.EnumerateArea [ rect: CDOrient.MapRect [itemInCell: [x1: -delta, y1: 0, x2: len, y2: len], cellSize: [0, 0], cellInstOrient: orient, cellInstPos: pos], eachTile: CheckTileValue, skip: $Nix]; [] _ handle.geometryWorld.EnumerateArea [ rect: CDOrient.MapRect [itemInCell: [x1: 0, y1: -delta, x2: len, y2: 0], cellSize: [0, 0], cellInstOrient: orient, cellInstPos: pos], eachTile: CheckTileValue, skip: $Nix]; }; concave => { [] _ handle.geometryWorld.EnumerateArea [ rect: CDOrient.MapRect [itemInCell: [x1: 0, y1: 0, x2: len, y2: len], cellSize: [0, 0], cellInstOrient: orient, cellInstPos: pos], eachTile: CheckTileValue, skip: $Nix]; }; ENDCASE } END; -- CheckArea or0: CD.Orientation = CDOrient.original; or90: CD.Orientation = CDOrient.rotate90; or180: CD.Orientation = CDOrient.rotate180; or270: CD.Orientation = CDOrient.rotate270; nodeAtCorner: REF ANY _ NIL; IF SXAccess.stopFlag^ THEN ERROR ABORTED; FOR ruleList: LIST OF REF SX.GeometricRule _ handle.rules, ruleList.rest WHILE ruleList#NIL DO rule: REF SX.GeometricRule = ruleList.first; bitIndex: ARRAY Quadrants OF INTEGER = [8, 4, 2, 1]; quadBits: INTEGER _ 0; nodeAtCorner _ NIL; FOR q: Quadrants IN Quadrants DO WITH quadVals[q] SELECT FROM cn: REF SX.CircuitNode => { IF rule.trigger1[SX.nodeIndex] THEN { quadBits _ quadBits + bitIndex[q]; nodeAtCorner _ cn } }; cc: REF Constraint => { IF rule.trigger1[cc.index] THEN { quadBits _ quadBits + bitIndex[q]; IF cc.hasCorrespondingNode AND nodeAtCorner=NIL THEN nodeAtCorner _ cc } }; cnList: LIST OF REF SX.CircuitNode => { CD.Error [ec: other, explanation: "DesignRuleCheckCorner: 'LIST OF REF SX.CircuitNode' should have been squashed to 'REF SX.CircuitNode' in `AnalyzeNodesInAOIs'.\nPLEASE save a copy of this design and make it available to Giordano. Thanks"] }; ENDCASE => { IF quadVals[q]=NIL THEN { IF rule.trigger1[SX.spaceIndex] THEN quadBits _ quadBits + bitIndex[q] } ELSE CD.Error[ec: other, explanation: "DesignRuleCheckCorner: quadVals[q]#NIL"] }; ENDLOOP; -- end Translate the quadrant contents into bit vector values SELECT quadBits FROM 0 => NULL; 1 => CheckArea[rule, or90, convex]; 2 => CheckArea[rule, or0, convex]; 3 => NULL; 4 => CheckArea[rule, or270, convex]; 5 => { CheckArea[rule, or90, convex]; CheckArea[rule, or270, convex] }; 6 => NULL; 7 => CheckArea[rule, or0, concave]; 8 => CheckArea[rule, or180, convex]; 9 => NULL; 10 => { CheckArea[rule, or0, convex]; CheckArea[rule, or180, convex] }; 11 => CheckArea[rule, or90, concave]; 12 => NULL; 13 => CheckArea[rule, or180, concave]; 14 => CheckArea[rule, or270, concave]; 15 => NULL; ENDCASE => ERROR; ENDLOOP; END; -- DesignRuleCheckCorner handle: REF CheckRec = NARROW[data]; conflictWorld: CStitching.Tesselation = handle.conflictWorld; tileBound: CD.Rect = tile.Area; IF conflictWorld.FindTile[[x: tileBound.x1, y: tileBound.y1]].value = $AOI THEN { IF tile.SW.SEdge = tileBound.y1 THEN { IF tile.WS.WEdge = tileBound.x1 THEN NULL -- This is a (rare I suspect) 4 way corner it is handled at the NE corner. ELSE { IF tile.value = tile.SW.value THEN CD.Error[ec: other, explanation: "CheckTile: violated Max horz rule"]; DesignRuleCheckCorner[ [ne: tile.value, nw: tile.SW.value, sw: tile.WS.value, se: tile.WS.value], [tileBound.x1, tileBound.y1] ]; } } ELSE { -- tile.WS.WEdge = tileBound.x1 IMPLICITLY IF tile.value = tile.WS.value THEN NULL -- No corner really ELSE { DesignRuleCheckCorner [ [ne: tile.value, nw: tile.SW.value, sw: tile.SW.value, se: tile.WS.value], [tileBound.x1, tileBound.y1]]; } } }; IF conflictWorld.FindTile[[x: tileBound.x2, y: tileBound.y2]].value = $AOI THEN { IF tile.NE.NEdge = tileBound.y2 THEN { IF tile.EN.EEdge = tileBound.x2 THEN { neQTile: TilePtr _ tile.EN.NE; WHILE neQTile.SEdge > tileBound.y2 DO neQTile _ neQTile.WS; ENDLOOP; DesignRuleCheckCorner [ [ne: neQTile.value, nw: tile.EN.value, sw: tile.value, se: tile.NE.value], [tileBound.x2, tileBound.y2]]; } ELSE { IF tile.value = tile.NE.value THEN CD.Error[ec: other, explanation: "CheckTile: violated Max horz rule NEast"]; DesignRuleCheckCorner [ [ne: tile.EN.value, nw: tile.EN.value, sw: tile.value, se: tile.NE.value], [tileBound.x2, tileBound.y2]]; } } ELSE { -- tile.EN.EEdge = tileBound.x2 IMPLICITLY IF tile.value = tile.EN.value THEN NULL -- No corner really ELSE { DesignRuleCheckCorner [ [ne: tile.NE.value, nw: tile.EN.value, sw: tile.value, se: tile.NE.value], [tileBound.x2, tileBound.y2]]; } } } END; -- CheckTile AnalyzeGeometry: PUBLIC PROC [cell: REF SX.LogicalCell] = BEGIN cir: REF SX.Circuit = cell.circuit; constraintQueue: ConstraintQueue; IF freeTesselations THEN FreeTesselations[circuit: cir]; FOR layer: SX.SpinifexLayerIndex IN [0 .. SXAccess.sxTech.numSpinifexLayers) DO conflictWorld: CStitching.Tesselation _ conflictWorlds[layer] _ CStitching.NewTesselation[]; geometryWorld: CStitching.Tesselation _ geometryWorlds[layer] _ CStitching.NewTesselation[]; cir.spinifexLayers[layer] _ SXQuadTree.Create [cir.spinifexLayers[layer]]; IF cir.spinifexLayers[layer].geometry = NIL THEN LOOP; IF SXAccess.stopFlag^ THEN ERROR ABORTED; ComputeConflicts [cir.spinifexLayers[layer].geometry, conflictWorld, layer, cir]; conflictWorld _ conflictWorlds[layer] _ BloatConflictsIntoAOIs [cir, layer, conflictWorld]; constraintQueue _ InstantiateAOIs [cir: cir, qt: cir.spinifexLayers[layer].geometry, cellBBox: cir.spinifexLayers[layer].size, layer: layer, conflictWorld: conflictWorld, geometryWorld: geometryWorld, constraintQueue: constraintQueue]; AnalyzeNodesInAOIs [cir, layer, geometryWorld]; ENDLOOP; -- end of FOR "layer: SX.SpinifexLayerIndex IN [0..SXAccess.sxTech.numSpinifexLayers) DO" TerminalIO.WriteRope [". "]; FOR layer: SX.SpinifexLayerIndex IN [0..SXAccess.sxTech.numSpinifexLayers) DO geometryWorld: CStitching.Tesselation _ geometryWorlds[layer]; IF SXAccess.stopFlag^ THEN ERROR ABORTED; FOR tiles: LIST OF REF CStitching.Region _ NARROW[geometryWorld.ListArea[rect: cir.spinifexLayers[layer].size]], tiles.rest WHILE tiles#NIL DO geometryWorld.ChangeRect [rect: tiles.first.rect, new: SX.LookupNode[NARROW[tiles.first.value, LIST OF REF SX.CircuitNode].first]] ENDLOOP; ENDLOOP; SX.NormalizeCircuit [cir]; TerminalIO.WriteRope [". "]; FOR layer: SX.SpinifexLayerIndex IN [0..SXAccess.sxTech.numSpinifexLayers) DO conflictWorld: CStitching.Tesselation _ conflictWorlds[layer]; geometryWorld: CStitching.Tesselation _ geometryWorlds[layer]; DesignRuleCheckAOIs [cir, cell, constraintQueue, layer, geometryWorld, conflictWorld]; ENDLOOP; TerminalIO.WriteRope [". "]; IF saveTesselations THEN freeTesselations _ TRUE ELSE FreeTesselations [circuit: cir]; END; -- AnalyzeGeometry SaveTesselations: PUBLIC PROC [save: BOOL _ FALSE] = BEGIN saveTesselations _ save END; GetTesselation: PUBLIC PROC [conflicts: BOOL _ TRUE, layer: SX.SpinifexLayerIndex] RETURNS [CStitching.Tesselation] = BEGIN RETURN [IF conflicts THEN conflictWorlds[layer] ELSE geometryWorlds[layer]] END; FreeTesselations: PROC [circuit: REF SX.Circuit] = BEGIN FOR layer: SX.SpinifexLayerIndex IN [0..SXAccess.sxTech.numSpinifexLayers) DO CStitching.ResetTesselation [conflictWorlds[layer]]; CStitching.ResetTesselation [geometryWorlds[layer]]; ENDLOOP; TerminalIO.WriteRope [". "]; freeTesselations _ FALSE END; END. Kšžœ*žœ)˜\KšžœÏc˜—š œžœžœ%žœ žœžœžœ ž˜šKšœÜ™ÜM•StartOfExpansiona -- [plane: REF CornerStitching.Tesselation, rect: CD.Rect, oldValue: REF ANY, data: REF ANY] -- šœžœ˜šœ1™1JšÏe œƒ™Jš¢œ¯™½Jš¢œ;™PJš¢œ†™Š—šÏb œž˜)Kšœžœžœžœ™5šžœ žœž˜šœžœžœž˜ KšžœG˜IK™8—šœžœžœ ž˜$Kšœœ ™Kšœ7˜7—šœ˜K™Yšžœžœž˜Kšœ2˜2Kšœ˜Kšž˜—KšžœžœN˜U—šœžœ˜šžœžœdž˜nKšœ7˜7—K™—šžœ˜ šžœ žœž˜KšœB™BKšœ1˜1—KšžœžœF˜M——Kšžœ¡Ðce ˜—š£œž˜+Kšœžœžœžœ™5šžœ žœž˜šœžœžœž˜ KšžœG˜IK™8—šœžœ ˜šžœžœ%žœ=ž˜rKšœ7˜7——šœž˜!Kšœ%˜%Kšœ˜Kšžœ˜—šœžœž˜K™'Kšœ žœžœO˜bKš žœžœ žœžœ$žœ7˜šžœžœžœ ž˜/Kšœ8˜8—Kšžœ˜—šžœ˜ Kšœ-™-šžœ žœž˜KšœB™BKšœ1˜1—KšžœžœF˜M——Kšžœ¡¤˜—š£ œž˜)Kšœžœžœžœ™5Kš œ¢œ¢œ$¢œ¢œ<™šžœ žœž˜šœžœžœž˜ KšžœG˜IK™8—šœžœž˜šžœžœ(ž˜5KšžœH˜J—Kšœžœ˜Kšžœ˜—šœ˜šžœ ž˜šžœ"žœž˜6Kšœžœ˜'—šžœž˜ K™%Kšœ,žœ˜1K™2Kšœ˜Kšœžœ˜Kšžœ˜——K™)—Mšœžœžœ˜2šžœ˜ šžœ žœž˜KšœB™BKšœ1˜1—KšžœžœF˜M——Kšžœ¡¤ ˜—š œžœ žœ)ž˜IKšœžœ žœ!˜;š   œžœžœžœžœž˜Wš  œžœžœžœžœ žœž˜Qšœ%˜%Kšœl˜lKšœ ˜ —Kšžœ¡ ˜—š  œžœžœž˜7Kšœ(™(šžœ5žœžœž˜Jšžœžœž˜,šœžœ ˜Kšœžœ,˜1šžœž˜KšœW˜W—Kšœ˜—šœžœ˜Kšœžœ+˜0šžœž˜KšœR˜R—Kšœ˜—šœ žœ ˜šžœ<žœ˜Dšœ ˜ Kšœ ˜ šœ'˜'Kšœi˜iKšœ˜Kšœ˜Kšœ˜—Kšœ5˜5—K˜——Kšžœžœ¡˜(—Kšžœ˜—M™1Kšžœžœžœžœ˜)šžœžœžœž˜8Kšœ ˜ —šžœžœžœž˜8Kšœ ˜ —šžœžœžœž˜7Kšœ˜—šžœžœžœž˜7Kšœ˜—Kšžœ¡¤ ˜—Mšœžœ˜ Kšœ žœu˜ƒKšœY˜Yšžœ žœž˜Kšœ˜—Kšžœ¡¤ ˜—Mšœ2˜2Kšžœ¡¤˜—š œžœ žœ*ž˜RK™ Kšœ žœ˜š£ œž˜&Kšœžœ$žœžœ™?šžœ žœž˜šœžœžœž˜ KšžœG˜I—šœžœžœ ž˜$Kšžœ:˜<—MšœN˜NMšœžœA˜GMšžœž˜—Kšžœ¡¤ ˜—š£ œž˜(Kšœžœ$žœžœ™?šžœ žœž˜šœžœžœž˜ KšžœG˜I—šœžœžœž˜)Kšžœ:˜<—šœ˜Kšžœ žœ3˜B—šœžœž˜Kšœ žœžœO˜bšžœžœ!ž˜+Kšœ8˜8—Kšžœ˜—Mšžœžœ˜—Kšžœ¡¤ ˜—š£ œž˜&Kšœžœ$žœžœ™?šžœ žœž˜šœžœžœž˜ KšžœG˜I—šœžœ˜šžœžœ(ž˜5Kšžœ:˜<——šœ˜Kšžœ žœ3˜C—MšœžœA˜GMšžœžœ˜—Kšžœ¡¤ ˜—š   œžœžœžœžœž˜Wš  œžœžœžœžœ žœž˜QKšœ0™0šœ%˜%šœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ˜—Kšœ ˜ —Kšžœ¡¤˜—š  œžœžœž˜7Kšœ0™0Kšžœžœžœžœ˜šžœ5žœžœž˜Jšžœžœž˜,šœžœ ˜Kšœžœ,˜1šžœž˜KšœP˜P—Kšœ˜—šœžœ˜Kšœžœ,˜1šžœž˜KšœO˜O—Kšœ˜—šœ žœ ˜šžœ<žœ˜Dšžœž˜šœ ˜ Kšœ ˜ šœ'˜'Kšœh˜hKšœ˜Kšœ˜Kšœ˜—Kšœ5˜5——šžœ˜Kšœžœ,˜1šžœž˜KšœJ˜J—Kšœ˜—K˜——Kšžœžœ˜—Kšžœ˜—šžœžœžœž˜J™§š£ œž˜(Kš œžœžœžœžœžœ™1šžœž˜KšœW˜W—Kšžœ¡˜—Mšœ žœ.˜=KšœH˜HKšœO˜OKšœ,˜,Kšœ(˜(Kšžœ¡¤˜—š œžœ žœžœžœ!žœžœ žœ)žœ žœžœžœžœ žœ žœ„˜‚Jšžœ)ž˜5J™^š  œžœžœ#žœ ž˜Qš œžœžœ#ž˜NKšœžœžœE˜hKšžœ¡˜—Mšœ™Kš œ žœ>žœžœžœžœB˜©Mšžœ#žœžœ˜1šžœ5žœžœž˜JKš œ žœVžœžœžœžœB˜Ášžœžœ"žœ˜.šžœžœž˜,šœ žœ˜Kšœžœ˜ Kšœ žœ˜Kšœžœd˜lKš œ žœ7žœžœžœžœB˜£Kšœžœn˜xMšœs˜sKšžœ žœžœžœ˜Kšœ£žœ¡@œw˜ùKšœ¡˜ —šœžœžœ˜Kšœc™cKšœžœžœžœžœžœžœZžœ˜¦Kš œ žœžœžœžœ%˜VKš œ žœžœžœžœžœ˜1Kšœ9˜9šžœ žœž˜šžœžœ-ž˜7šžœžœž˜&š œ žœžœžœžœ¡³˜Úšžœžœžœžœ˜6Kš œžœžœžœžœ˜*Kš œ žœžœžœžœ ˜(šžœžœž˜K™Kšžœžœžœ˜K˜Kšžœ˜—šœžœžœžœ˜Kšžœ˜#Kšžœžœ˜—KšœG˜GK˜——Kšžœžœ˜——Kšœ˜Kšžœ˜—Kšœ¡˜—Kšœžœ6˜=Kšžœžœ¡&˜7—K˜—Kšžœ¡4˜=—šœ˜šžœžœž˜ šœ$˜$KšœK˜K——šžœžœž˜ šœ$˜$KšœK˜K——šžœžœž˜šœ#˜#KšœK˜K——šžœžœž˜šœ#˜#KšœK˜K——K˜—Kšžœ¡˜—J™Jšœ™Kšœ˜Kšœ$˜$Kšžœ¡˜—š  œžœ žœžœžœ>ž˜~J™‰š œÐckž˜+Jš œžœžœžœžœžœ™0Jšœ,™,Jšœ»™»š  œžœ žœžœžœžœžœž˜LJšœF™Fš  œžœžœžœžœžœžœž˜Oš žœžœžœžœžœžœž˜?Jšœžœžœžœ˜#šžœžœž˜Jšžœžœžœ¡˜3Jšœ ˜ Jšžœ˜—Jšžœžœžœ ¡˜0Jšžœ˜—Jšžœ¡ ˜—Mšžœ:˜@Jšžœ¡¤ ˜—Mšœ™Jšžœžœžœžœ˜)šžœ žœž˜šœžœžœžœ˜'J™=Jšœžœžœžœ˜/Jšœ žœ˜Jšœ žœ˜Jšœ žœ˜Jšœ žœ˜Jšœžœ˜Jšœ žœ˜Jšœžœ/˜6šžœžœž˜Jšœžœžœžœ˜4Jšžœ žœ"˜1J™JšžœU˜WJ˜Jšœ˜Jšžœ˜—™LJ™H—šžœ,žœž˜Sš žœžœžœžœžœž˜<š œ žœžœžœžœ˜,Kšœžœžœžœ ˜;Kšžœ žœ"˜1KšžœYžœžœ˜ŸK˜—Kšœ žœžœ¡ ˜.Kšžœž˜—šž¢˜šžœž˜KšœGžœžœ˜‹——Kšžœ¢˜—šžœ*žœž˜Qš žœžœžœžœžœž˜:šœ žœžœžœ˜+Jšœžœ˜šžœ žœžœ˜2Jšœ!˜!—KšžœXžœžœ˜žJ˜—Jšœ žœžœ¡ ˜.Jšžœž˜—šžœžœž˜KšžœEžœžœ˜‰—JšžœÐew˜—šžœ,žœž˜Sšžœžœžœž˜4KšžœEžœžœ˜‹—Jšžœ¢ ˜—šžœ*žœž˜Qšžœžœžœž˜1KšžœEžœžœ˜‹—Kšžœ¡¤˜—J˜—Jšžœž˜—Jšžœ¡¤˜—Mšœ™Kšœb˜bJšžœ¢˜—L™#Mšœžœžœžœžœžœžœ%žœžœžœ˜›Kšœ žœžœWžœžœžœžœžœžœžœžœ ˜»š œžœ žœžœžœžœ:žœgž˜éšœž™ž™"J™LJ™&—J™å—Kšœžœ žœ ¡0˜SKšœ žœ žœ ¡+˜SM™2š žœžœžœžœ8žœžœž˜`Kšœžœvžœ˜›Kšœm˜mš žœžœžœžœ<žœžœž˜rKšœW˜WKšžœ˜—Kšžœ˜—M™Kšœ„˜„Kšœ|˜|Jšžœ¡¤˜—š£œž˜+Jšœ$™$Jš œžœžœžœžœžœ™1Jšœžœ žœ˜#Jšœžœžœ˜'šžœžœžœdž˜€Kšœžœžœs˜“—Jšžœ¡¤˜—š œžœ žœ0žœžœžœžœž˜mJšœ™Jšœžœ˜Jš œžœžœžœ˜(˜Jšžœžœ˜(š  œžœ žœžœžœžœžœ ž˜aš£ œž˜-Jšœžœžœžœ™,Jšœc™cšžœžœž˜"šœžœžœ'žœ˜HJšœ žœ˜Jšœ žœžœžœ ˜Jšœ<˜˜>Jšžœžœžœžœ˜)šžœžœžœžœžœKžœžœž˜ŽKš œ7žœ žœžœžœžœžœ˜‚Jšžœ˜Jšžœ˜——Jšžœ˜M˜Mš¥œy™~šžœžœ(ž˜MKšœ>˜>Kšœ>˜>MšœV˜VKšžœ˜—M˜Mšžœžœž œ!˜VJšžœ¡¤˜—Lšœ™š  œžœžœžœžœž˜:Jšœ˜Jšžœ˜—š œžœžœ žœžœ žœžœž˜{Jšžœžœ žœžœ˜KJšžœ˜—š  œžœ žœžœ ž˜8šžœžœ(ž˜MJšœ4˜4Jšœ4˜4Jšžœ˜—Jšœ˜Jšœž˜Jšžœ˜—Nšžœ˜šœ4™4K™?—™4Kšœ-™-Kšœ~™~—™5K™—šœ6™6K™—™!Jšœ@Ïrœ© œ™xJšœ ©œG™a—™0J™?JšœaÐenœ™xJšœ © œ© œ1© œ™Jš œ ©œ8© œDªœªœ©Wœ)© œ1™˜—™0Jšœ¦™¦JšœJ™J—™0KšœB™BKšœ ©™—™1JšœO©œ ©œ7™²Jšœ ©Cœ©œ©™£—™1K™:Kšœ ©™—™3Kšœ™Kš œ ©œ©œ©œo©œ©œ©œ™û—™3Kš© œ© ™0—™1K™ —™2K™—™#K™0—™%Kšœ/¢œ¢©L™£—K™K™—…—ŽöU