DIRECTORY Basics, CD, CDBasics, CDDefaultProcs, CDDirectory, CDInstances, CDOrient, CDProperties, CDSatellites, CDSymbolicObjects, CedarProcess, Core, CoreClasses, CoreOps, CoreProperties, CoreGeometry, D2Intervals, GList, HashTable, IO, Process, PW, PWObjects, Rope, RopeList, Sinix; SinixImpl: CEDAR PROGRAM IMPORTS Basics, CD, CDBasics, CDDefaultProcs, CDDirectory, CDInstances, CDOrient, CDProperties, CDSatellites, CDSymbolicObjects, CedarProcess, CoreClasses, CoreOps, CoreProperties, CoreGeometry, D2Intervals, GList, HashTable, IO, Process, PW, PWObjects, Rope, RopeList EXPORTS Sinix SHARES CoreGeometry = BEGIN OPEN Sinix; InternalBug: PUBLIC SIGNAL [name: ROPE] = CODE; CallerBug: PUBLIC SIGNAL [] = CODE; FusionPropMismatch: PUBLIC SIGNAL [name: ROPE, prop: ATOM, value1, value2: REF] = CODE; FusionStructureMismatch: PUBLIC SIGNAL [name: ROPE, wire1, wire2: Wire] = CODE; StructureMismatch: PUBLIC SIGNAL [name: ROPE, actual, subPublic: Wire] = CODE; FusionByNameMismatch: PUBLIC SIGNAL [name, msg: ROPE, wire: Wire] = CODE; StructuralLoop: PUBLIC SIGNAL [name: ROPE, wire: Wire] = CODE; ROPES: TYPE = LIST OF ROPE; NameWire: TYPE = RECORD [name: ROPE, wire: Wire]; FusionData: TYPE = REF FusionDataRec; FusionDataRec: TYPE = RECORD [ mode: Mode, -- to avoid passing it around nir: CD.Rect, -- Rectangles for which geometry that is strictly inside (edge excluded) is not promoted public name: ROPE, -- name of the object currently extracted isAbut: BOOL, -- recording publics depend on this property fused: HashTable.Table, -- Association [fused -> root] [Wire -> Wire]. If root=NIL, then this wire is a root. Basically all the wires ever created belong to this table. data: REF, -- different information for Abut and Cell. For abuts it contains the next rects, (REF D2Intervals.Table). For Cell, it is a LIST OF NameWire. This list is not sorted at creation time, and same name may appear several times. rects: SEQUENCE nbOfLayers: NAT OF D2Intervals.Table -- Association (per layer) [fusion geometry -> wire] [D2Intervals.Rect -> IW]. Wire may or may not be a root. ]; AbutData: TYPE = REF AbutDataRec; AbutDataRec: TYPE = RECORD [ inX: BOOL, -- to avoid passing it around nextRects: SEQUENCE nbOfLayers: NAT OF D2Intervals.Table -- Association (per layer) [fusion geometry -> wire] [D2Intervals.Rect -> IW]. Wire may or may not be a root. ]; IW: TYPE = REF IWRec; IWRec: TYPE = RECORD [instance: CD.Instance, wire: Wire]; RectToRect: PROC [cdRect: CD.Rect] RETURNS [rect: D2Intervals.Rect] = INLINE { rect _ [[cdRect.x1, cdRect.x2], [cdRect.y1, cdRect.y2]]; }; InstanceToRect: PROC [inst: CD.Instance] RETURNS [rect: D2Intervals.Rect] = INLINE { rect _ RectToRect[ IF inst.ob.class.interestRect=CDDefaultProcs.InterestRect -- simple rectangles ! THEN CDInstances.InstRectO[inst] ELSE CDBasics.Surround[CDInstances.InstRectO[inst], CDInstances.InstRectI[inst]] ]; }; IWToRect: PROC [table: D2Intervals.Table, value: D2Intervals.Value] RETURNS [rect: D2Intervals.Rect] = { iw: IW _ NARROW [value]; mode: Mode _ NARROW [table.userData]; rect _ InstanceToRect[iw.instance]; }; CreateRoot: PROC [fusionData: FusionData, size: NAT, props: Properties _ NIL] RETURNS [wire: Wire] = { EachProperty: PROC [prop: ATOM, val: REF ANY _ NIL] = { newVal: REF ANY _ SELECT prop FROM fusionData.mode.decoration.pinsProp => NIL, fusionData.mode.decoration.geometryProp => NIL, CoreOps.nameProp => NIL, ENDCASE => val; CoreProperties.PutWireProp[wire, prop, newVal]; }; wire _ CoreOps.CreateWires[size: size]; CoreProperties.Enumerate[props, EachProperty]; [] _ HashTable.Store[fusionData.fused, wire, NIL]; }; InsertInstances: PROC [fusionData: FusionData, transformation: CD.Instance, instances: LIST OF CD.Instance, wire: Wire, copyGeometry: BOOL] = { pins: LIST OF CD.Instance _ CoreGeometry.GetPins[fusionData.mode.decoration, wire]; IF ~HashTable.Fetch[fusionData.fused, wire].found THEN SIGNAL InternalBug[fusionData.name]; IF RootWire[fusionData, wire]#wire THEN SIGNAL InternalBug[fusionData.name]; FOR is: LIST OF CD.Instance _ instances, is.rest WHILE is#NIL DO inst: CD.Instance _ CoreGeometry.Transform[transformation, is.first]; iw: IW _ NEW [IWRec _ [instance: inst, wire: wire]]; layers: LayerRange _ fusionData.mode.instanceLayer[inst]; IF copyGeometry THEN CoreGeometry.PutGeometry[fusionData.mode.decoration, wire, CONS [inst, CoreGeometry.GetGeometry[fusionData.mode.decoration, wire]]]; FOR i: NAT IN [layers.min .. layers.max] DO D2Intervals.Insert[fusionData[i], iw] ENDLOOP; IF NOT CoreGeometry.AtEdge[fusionData.nir, inst] THEN LOOP; IF fusionData.isAbut THEN CoreProperties.PutWireProp[wire, $Public, $Public] ELSE pins _ CONS [inst, pins]; ENDLOOP; CoreGeometry.PutPins[fusionData.mode.decoration, wire, pins]; }; CreateDAGWire: PROC [table: HashTable.Table] RETURNS [wire: Wire] = { wires: LIST OF Wire _ NIL; EachKeyDelete: HashTable.EachPairAction = { WireDelete: PROC [wire: Wire] = { IF ~HashTable.Delete[table, wire] THEN RETURN; -- already done FOR i: NAT IN [0 .. wire.size) DO WireDelete[wire[i]]; ENDLOOP; }; wire: Wire _ NARROW [key]; FOR i: NAT IN [0 .. wire.size) DO WireDelete[wire[i]]; ENDLOOP; }; index: NAT _ 0; SummarizeWires: HashTable.EachPairAction = { wire[index] _ NARROW [key]; index _ index+1; }; [] _ HashTable.Pairs[table, EachKeyDelete]; wire _ CoreOps.CreateWires[HashTable.GetSize[table]]; [] _ HashTable.Pairs[table, SummarizeWires]; IF index#HashTable.GetSize[table] THEN SIGNAL InternalBug[NIL]; }; LayoutFusionByName: PROC [fusionData: FusionData] = { refList: REF LIST OF NameWire = NARROW [fusionData.data]; nameToWire: HashTable.Table = HashTable.Create[mod: 11, equal: HashTable.RopeEqual, hash: HashTable.HashRope]; FOR nameWires: LIST OF NameWire _ refList^, nameWires.rest WHILE nameWires#NIL DO name: ROPE = nameWires.first.name; wire: Wire _ RootWire[fusionData, nameWires.first.wire]; previousName: ROPE = CoreOps.GetShortWireName[wire]; previousWire: Wire _ NARROW [HashTable.Fetch[nameToWire, name].value]; IF previousName#NIL AND ~Rope.Equal[name, previousName] THEN SIGNAL FusionPropMismatch[fusionData.name, CoreOps.nameProp, name, previousName]; IF previousWire#NIL THEN { previousWire _ RootWire[fusionData, previousWire]; IF previousWire#wire THEN { wire _ StructuredFusion[fusionData, wire, previousWire]; PW.WriteF["Fusion by name for '%g' in cell '%g'.\n", IO.rope[name], IO.rope[fusionData.name]]; CoreProperties.PutWireProp[wire, $FusedByName, $FusedByName]; }; }; [] _ HashTable.Store[nameToWire, name, wire]; [] _ CoreOps.SetShortWireName[wire, name]; ENDLOOP; }; NoFusionByName: PROC [fusionData: FusionData] = { refList: REF LIST OF NameWire = NARROW [fusionData.data]; EachNameWire: HashTable.EachPairAction = { uniqueID: INT _ 0; name: ROPE = NARROW [key]; wires: Wires _ NARROW [value]; [] _ CoreOps.SetShortWireName[wires.first, name]; wires _ wires.rest; IF wires=NIL THEN RETURN; PW.WriteF["NO fusion by name for '%g' in cell '%g'.\n", IO.rope[name], IO.rope[fusionData.name]]; WHILE wires#NIL DO [] _ CoreOps.SetShortWireName[wires.first, IO.PutFR["%g$%g$", IO.rope[name], IO.int[uniqueID]]]; uniqueID _ uniqueID + 1; wires _ wires.rest; ENDLOOP; }; nameToWires: HashTable.Table = HashTable.Create[mod: 11, equal: HashTable.RopeEqual, hash: HashTable.HashRope]; -- contains (for every name) the list of roots that have it for name FOR nameWires: LIST OF NameWire _ refList^, nameWires.rest WHILE nameWires#NIL DO name: ROPE _ nameWires.first.name; wire: Wire = RootWire[fusionData, nameWires.first.wire]; wires: Wires _ NARROW [HashTable.Fetch[nameToWires, name].value]; IF NOT CoreOps.Member[wires, wire] THEN wires _ CONS [wire, wires]; [] _ HashTable.Store[nameToWires, name, wires]; ENDLOOP; [] _ HashTable.Pairs[nameToWires, EachNameWire]; }; SchematicsFusionByName: PROC [fusionData: FusionData] = { refList: REF LIST OF NameWire = NARROW [fusionData.data]; nameToWire: HashTable.Table = HashTable.Create[mod: 11, equal: HashTable.RopeEqual, hash: HashTable.HashRope]; names: ROPES _ NIL; FOR nameWires: LIST OF NameWire _ refList^, nameWires.rest WHILE nameWires#NIL DO name: ROPE = nameWires.first.name; wire: Wire _ RootWire[fusionData, nameWires.first.wire]; previousName: ROPE = CoreOps.GetShortWireName[wire]; previousWire: Wire _ NARROW [HashTable.Fetch[nameToWire, name].value]; hasComponents: BOOL = CoreOps.ParseWireName[name].components#NIL; IF previousName#NIL AND ~Rope.Equal[name, previousName] THEN SIGNAL FusionPropMismatch[fusionData.name, CoreOps.nameProp, name, previousName]; IF previousWire#NIL THEN previousWire _ RootWire[fusionData, previousWire]; IF previousWire#NIL AND previousWire#wire THEN wire _ StructuredFusion[fusionData, wire, previousWire] ELSE IF hasComponents THEN names _ CONS [name, names]; [] _ HashTable.Store[nameToWire, name, wire]; IF NOT hasComponents THEN [] _ CoreOps.SetShortWireName[wire, name]; ENDLOOP; names _ RopeList.Sort[names, RopeList.Compare]; FOR list: ROPES _ RopeList.Sort[names, RopeList.Compare], list.rest WHILE list#NIL DO name: ROPE _ list.first; wire: Wire = RootWire[fusionData, NARROW [HashTable.Fetch[nameToWire, name].value]]; base: ROPE; components: ROPES _ NIL; matchingWire: Wire; [base, components] _ CoreOps.ParseWireName[name]; matchingWire _ NARROW [HashTable.Fetch[nameToWire, base].value]; WHILE components#NIL DO index: INT _ -1; IF matchingWire=NIL THEN SIGNAL FusionByNameMismatch[fusionData.name, IO.PutFR["Path name %g does not correspond to any wire", IO.rope[name]], wire]; matchingWire _ RootWire[fusionData, matchingWire]; FOR i: NAT IN [0 .. matchingWire.size) DO subWireName: ROPE _ CoreOps.GetShortWireName[matchingWire[i]]; IF subWireName=NIL THEN subWireName _ IO.PutR1[IO.int[i]]; IF Rope.Equal[subWireName, components.first] THEN { IF index=-1 THEN index _ i ELSE FusionByNameMismatch[fusionData.name, IO.PutFR["Current wire has 2 or more sub-wires with field `%g'", IO.rope[components.first]], wire]; }; ENDLOOP; IF index=-1 THEN SIGNAL FusionByNameMismatch[fusionData.name, IO.PutFR["Current wire does not have a sub-wire with field `%g'", IO.rope[components.first]], wire]; matchingWire _ matchingWire[index]; components _ components.rest; ENDLOOP; [] _ StructuredFusion[fusionData, matchingWire, wire]; ENDLOOP; }; AddNameWire: PROC [fusionData: FusionData, name: ROPE, wire: Wire] = { nameWires: REF LIST OF NameWire _ NARROW [fusionData.data]; nameWires^ _ CONS [[name: name, wire: wire], nameWires^]; }; RootWire: PROC [fusionData: FusionData, wire: Wire] RETURNS [rootWire: Wire] = { IF wire=NIL THEN SIGNAL InternalBug[fusionData.name]; rootWire _ NARROW [HashTable.Fetch[fusionData.fused, wire].value]; IF rootWire=NIL THEN RETURN [wire]; IF rootWire=wire THEN SIGNAL InternalBug[fusionData.name]; rootWire _ RootWire[fusionData, rootWire]; [] _ HashTable.Replace[fusionData.fused, wire, rootWire]; }; PropFusionProc: TYPE = PROC [prop: ATOM, value1, value2: REF ANY, name: ROPE] RETURNS [value: REF ANY]; NameFusion: PropFusionProc = { rope1: ROPE _ NARROW [value1]; rope2: ROPE _ NARROW [value2]; IF rope1#NIL AND rope2#NIL AND ~Rope.Equal[rope1, rope2] THEN SIGNAL FusionPropMismatch[name, prop, rope1, rope2]; value _ IF rope1=NIL THEN rope2 ELSE rope1; }; DefaultFusion: PropFusionProc = { IF value1#NIL AND value2#NIL AND value1#value2 THEN SIGNAL FusionPropMismatch[name, prop, value1, value2]; value _ IF value1=NIL THEN value2 ELSE value1; }; FuseProperties: PUBLIC PROC [mode: Sinix.Mode, fused, root: Wire, name: ROPE] = { EachProperty: PROC [prop: ATOM, val: REF ANY] = { rootValue: REF _ CoreProperties.GetWireProp[root, prop]; SELECT prop FROM mode.decoration.pinsProp => CoreGeometry.PutPins[mode.decoration, root, CDInstances.AppendToList[CoreGeometry.GetPins[mode.decoration, root], CoreGeometry.GetPins[mode.decoration, fused]]]; mode.decoration.geometryProp => CoreGeometry.PutGeometry[mode.decoration, root, CDInstances.AppendToList[CoreGeometry.GetGeometry[mode.decoration, root], CoreGeometry.GetGeometry[mode.decoration, fused]]]; CoreOps.nameProp => CoreProperties.PutWireProp[root, prop, NameFusion[prop, val, rootValue, name]]; ENDCASE => CoreProperties.PutWireProp[root, prop, DefaultFusion[prop, val, rootValue, name]]; }; CoreProperties.Enumerate[fused.properties, EachProperty]; }; DeleteFusioned: PROC [fusionData: FusionData, fused, root: Wire] = { IF ~HashTable.Fetch[fusionData.fused, fused].found THEN SIGNAL InternalBug[fusionData.name]; IF RootWire[fusionData, fused]#fused THEN SIGNAL InternalBug[fusionData.name]; IF ~HashTable.Fetch[fusionData.fused, root].found THEN SIGNAL InternalBug[fusionData.name]; IF RootWire[fusionData, root]#root THEN SIGNAL InternalBug[fusionData.name]; IF fused=root THEN SIGNAL InternalBug[fusionData.name]; -- should never occur FuseProperties[fusionData.mode, fused, root, fusionData.name]; fused.properties _ NIL; -- to help GC [] _ HashTable.Store[fusionData.fused, fused, root]; }; StructuredFusion: PROC [fusionData: FusionData, wire1, wire2: Wire] RETURNS [wire: Wire] = { wire1 _ RootWire[fusionData, wire1]; wire2 _ RootWire[fusionData, wire2]; IF wire1=wire2 THEN RETURN [wire1]; SELECT TRUE FROM wire1=wire2 => wire _ wire1; wire1.size=0 => {DeleteFusioned[fusionData, wire1, wire2]; wire _ wire2}; wire2.size=0 => {DeleteFusioned[fusionData, wire2, wire1]; wire _ wire1}; wire1.size=wire2.size => { wire _ CreateRoot[fusionData, wire1.size]; FOR i: NAT IN [0 .. wire.size) DO wire[i] _ StructuredFusion[fusionData, wire1[i], wire2[i]]; ENDLOOP; DeleteFusioned[fusionData, wire1, wire]; DeleteFusioned[fusionData, wire2, wire]; }; ENDCASE => SIGNAL FusionStructureMismatch[fusionData.name, wire1, wire2]; }; FindTouchingWires: PROC [fusionData: FusionData, transformation: CD.Instance, geometry: LIST OF CD.Instance] RETURNS [touchingWires: LIST OF Wire _ NIL] = { FOR insts: LIST OF CD.Instance _ geometry, insts.rest WHILE insts#NIL DO inst: CD.Instance _ CoreGeometry.Transform[transformation, insts.first]; layers: LayerRange _ fusionData.mode.instanceLayer[inst]; rect: D2Intervals.Rect _ InstanceToRect[inst]; InternalFindTouchingWires: PROC [table: D2Intervals.Table, value: D2Intervals.Value] RETURNS [quit: BOOL _ FALSE] = { iw: IW _ NARROW [value]; instance: CD.Instance _ iw.instance; wire: Wire _ RootWire[fusionData, iw.wire]; IF ~HashTable.Fetch[fusionData.fused, wire].found THEN SIGNAL InternalBug[fusionData.name]; IF CoreOps.Member[touchingWires, wire] THEN RETURN; IF fusionData.mode.touchProc[instance, inst] THEN touchingWires _ CONS [wire, touchingWires]; }; FOR i: NAT IN [layers.min .. layers.max] DO [] _ D2Intervals.Enumerate[fusionData[i], InternalFindTouchingWires, rect]; ENDLOOP; ENDLOOP; }; FusionGeometry: PROC [fusionData: FusionData, transformation: CD.Instance, geometry: LIST OF CD.Instance, copyGeometry: BOOL] RETURNS [wire: Wire _ NIL] = { touchingWires: LIST OF Wire _ FindTouchingWires[fusionData, transformation, geometry]; IF touchingWires=NIL THEN { wire _ CreateRoot[fusionData, 0]; InsertInstances[fusionData, transformation, geometry, wire, copyGeometry]; RETURN; }; wire _ touchingWires.first; InsertInstances[fusionData, transformation, geometry, wire, copyGeometry]; touchingWires _ touchingWires.rest; WHILE touchingWires#NIL DO fused: Wire _ touchingWires.first; wire _ StructuredFusion[fusionData, fused, wire]; touchingWires _ touchingWires.rest; ENDLOOP; }; FusionWire: PROC [fusionData: FusionData, dagTable: HashTable.Table, public: Wire, transformation: CD.Instance, copyProps: BOOL] RETURNS [actual: Wire] = { prevActual: Wire _ NARROW [HashTable.Fetch[dagTable, public].value]; structActual: Wire; publicName: ROPE _ CoreOps.GetShortWireName[public]; pins: LIST OF CD.Instance _ CoreGeometry.GetPins[fusionData.mode.decoration, public]; IF prevActual#NIL THEN RETURN [prevActual]; structActual _ CreateRoot[fusionData, public.size, IF copyProps THEN public.properties ELSE NIL]; FOR i: NAT IN [0 .. public.size) DO structActual[i] _ FusionWire[fusionData, dagTable, public[i], transformation, copyProps]; ENDLOOP; actual _ IF pins=NIL THEN structActual ELSE StructuredFusion[ fusionData, structActual, FusionGeometry[fusionData, transformation, pins, copyProps] ]; IF copyProps AND publicName#NIL THEN AddNameWire[fusionData, publicName, actual]; [] _ HashTable.Store[dagTable, public, actual]; }; FusionWires: PROC [fusionData: FusionData, public: Wire, transformation: CD.Instance] RETURNS [actual: Wire] = { dagTable: HashTable.Table _ HashTable.Create[3]; actual _ CoreOps.CreateWires[public.size]; FOR i: NAT IN [0 .. actual.size) DO actual[i] _ FusionWire[fusionData, dagTable, public[i], transformation, FALSE]; ENDLOOP; }; SortInstances: PROC [instances: LIST OF CD.Instance] RETURNS [sorted: LIST OF CD.Instance _ NIL] = { Eval: PROC [inst: CD.Instance] RETURNS [INT] = { pos: CD.Position _ CDBasics.BaseOfRect[CDInstances.InstRectI[inst]]; RETURN [pos.x+pos.y]; }; Compare: GList.CompareProc = { RETURN [Basics.CompareINT[Eval[NARROW [ref1]], Eval[NARROW [ref2]]]]; }; sorted _ NARROW [GList.Sort[CDInstances.AppendToList[instances, NIL], Compare]]; -- it is important to copy instances, because GList.Sort modifies physically its argument }; cacheProp: PUBLIC ATOM _ PW.RegisterProp[$SinixCache, FALSE, TRUE]; Extract: PUBLIC ExtractProc = { cache: Cache _ NARROW [CDProperties.GetObjectProp[obj, cacheProp]]; CDProperties.PutObjectProp[obj, satellitesProp, CDSatellites.GetSatelliteRopes[obj]]; IF cache#NIL AND cache.mode=mode AND mode.equalProc[obj, cache.properties, cache.userData, properties, userData] THEN RETURN [result: cache.result, props: cache.props]; BEGIN priority: CedarProcess.Priority _ CedarProcess.GetPriority[]; atom: ATOM _ NARROW [CDProperties.GetListProp[properties, mode.extractProcProp]]; extractProc: ExtractProc; IF atom=NIL THEN atom _ NARROW [CDProperties.GetObjectProp[obj, mode.extractProcProp]]; IF atom=NIL THEN atom _ NARROW [CDProperties.GetProp[obj.class, mode.extractProcProp]]; IF atom=NIL THEN extractProc _ ExtractExpand ELSE { refProc: REF ExtractProc _ NARROW [HashTable.Fetch[registeredExtractProcs, atom].value]; IF refProc=NIL THEN { PW.WriteF["*** ExtractProc $%g not registered. You must run the program defining it.\n", IO.atom[atom]]; SIGNAL CallerBug[]; }; extractProc _ refProc^; }; CedarProcess.CheckAbort[]; CedarProcess.SetPriority[background]; Process.Yield[]; [result, props] _ extractProc[obj, mode, properties, userData]; WITH result SELECT FROM wire: Wire => {}; wires: Wires => {}; cellType: CellType => IF NOT CoreGeometry.HasIR[mode.decoration, cellType] THEN SIGNAL CallerBug[]; -- IR decoration missing! ENDCASE => IF result#NIL THEN SIGNAL CallerBug[]; -- probably some ExtractProc is grossly wrong CedarProcess.SetPriority[priority]; CDProperties.PutObjectProp[obj, cacheProp, NEW [CacheRec _ [mode: mode, properties: properties, userData: userData, result: result, props: props]]]; END; }; registeredExtractProcs: HashTable.Table _ HashTable.Create[]; RegisterExtractProc: PUBLIC PROC [key: ATOM, extractProc: ExtractProc] = { IF NOT HashTable.Store[registeredExtractProcs, key, NEW [ExtractProc _ extractProc]] THEN PW.WriteF["ExtractProc overwritten for $%g.\n", IO.atom[key]]; }; ExtractCell: PUBLIC ExtractProc = { cellType: CellType; -- the result ir: CD.Rect _ CD.InterestRect[obj]; publics: HashTable.Table _ HashTable.Create[3]; internals: HashTable.Table _ HashTable.Create[3]; cellPtr: CD.CellPtr _ NARROW [obj.specificRef]; cdInstances: LIST OF CD.Instance _ SortInstances[cellPtr.contents]; fusionData: FusionData _ NEW [FusionDataRec[mode.nbOfLayers]]; currentInstances: LIST OF CoreClasses.CellInstance _ NIL; count: NAT _ 0; FOR list: LIST OF CD.Instance _ cdInstances, list.rest WHILE list#NIL DO count _ count+1 ENDLOOP; PW.WriteF["Extracting [%g] cell %g (size: [%g, %g], instances: %g)\n", IO.rope[IF mode.fusionByName=none THEN IO.PutFR["%g[%g]", IO.rope[mode.decoration.name], IO.int[NARROW [userData, REF INT]^]] ELSE mode.decoration.name], IO.rope[CDDirectory.Name[obj]], IO.int[obj.size.x], IO.int[obj.size.y], IO.int[count]]; fusionData.mode _ mode; fusionData.name _ CDDirectory.Name[obj]; fusionData.nir _ IF mode.fusionByName=none THEN CDBasics.Extend[ir, - NARROW [userData, REF INT]^] ELSE ir; fusionData.fused _ HashTable.Create[3]; fusionData.data _ NEW [LIST OF NameWire _ NIL]; fusionData.isAbut _ FALSE; FOR i: NAT IN [0 .. mode.nbOfLayers) DO fusionData[i] _ D2Intervals.Create[ range: RectToRect[CDBasics.Surround[CDBasics.RectAt[[0, 0], obj.size], ir]], valueRect: IWToRect, userData: mode]; ENDLOOP; CDProperties.PutObjectProp[obj, satellitesProp, CDSatellites.GetSatelliteRopes[obj]]; -- to reinforce invariantes on the object (restriction of CDSatellites) WHILE cdInstances#NIL DO cdInstance: CD.Instance _ cdInstances.first; subUserData: REF _ userData; subResult: REF; subProps: Properties; CDProperties.PutInstanceProp[cdInstance, satellitesProp, CDSatellites.GetSatelliteRopes[cdInstance]]; IF mode.fusionByName=none THEN { UnionOverlap: PROC [rect: CD.Rect] = { interSize: CD.Position; IF overlap+overlap>bbox.x2-bbox.x1 OR overlap+overlap>bbox.y2-bbox.y1 THEN RETURN; interSize _ CDBasics.SizeOfRect[CDBasics.Intersection[rect, CDBasics.Extend[bbox, -overlap]]]; overlap _ MAX [overlap, MIN [interSize.x, interSize.y]]; IF overlap+overlap>bbox.x2-bbox.x1 OR overlap+overlap>bbox.y2-bbox.y1 THEN RETURN; interSize _ CDBasics.SizeOfRect[CDBasics.Intersection[rect, CDBasics.Extend[bbox, -overlap]]]; overlap _ MAX [overlap, MIN [interSize.x, interSize.y]]; }; bbox: CD.Rect _ CDInstances.InstRectI[cdInstance]; overlap: INT _ 0; UnionOverlap[[FIRST[INT], FIRST[INT], fusionData.nir.x1, LAST[INT]]]; UnionOverlap[[fusionData.nir.x2, FIRST[INT], LAST[INT], LAST[INT]]]; UnionOverlap[[FIRST[INT], FIRST[INT], LAST[INT], fusionData.nir.y1]]; UnionOverlap[[FIRST[INT], fusionData.nir.y2, LAST[INT], LAST[INT]]]; FOR list: LIST OF CD.Instance _ cellPtr.contents, list.rest WHILE list#NIL DO IF list.first#cdInstance THEN UnionOverlap[CDInstances.InstRectO[list.first]]; ENDLOOP; subUserData _ NEW [INT _ overlap]; }; [subResult, subProps] _ Extract[cdInstance.ob, mode, cdInstance.properties, subUserData]; IF subResult#NIL THEN WITH subResult SELECT FROM subWire: Wire => { [] _ FusionWire[fusionData, HashTable.Create[1], subWire, cdInstance, TRUE]; }; subWires: Wires => { dagTable: HashTable.Table _ HashTable.Create[3]; WHILE subWires#NIL DO [] _ FusionWire[fusionData, dagTable, subWires.first, cdInstance, TRUE]; subWires _ subWires.rest; ENDLOOP; }; subCellType: CellType => { bbox: CD.Rect _ CDInstances.BoundingRectO[cdInstances.rest]; instance: CoreClasses.CellInstance _ CoreClasses.CreateInstance[ actual: FusionWires[fusionData, subCellType.public, cdInstance], type: subCellType, props: subProps ]; CoreGeometry.PutTransf[mode.decoration, instance, cdInstance]; currentInstances _ CONS [instance, currentInstances]; FOR i: NAT IN [0 .. mode.nbOfLayers) DO DeleteOutsideRange: PROC [table: D2Intervals.Table, value: D2Intervals.Value] RETURNS [quit: BOOL _ FALSE] = { valueInst: CD.Instance _ NARROW [value, IW].instance; IF ~CoreGeometry.Intersect[bbox, valueInst] THEN D2Intervals.Delete[fusionData[i], value]; }; [] _ D2Intervals.Enumerate[fusionData[i], DeleteOutsideRange, [[FIRST[INT], bbox.x1], D2Intervals.universe.y]]; [] _ D2Intervals.Enumerate[fusionData[i], DeleteOutsideRange, [[bbox.x2, LAST[INT]], D2Intervals.universe.y]]; [] _ D2Intervals.Enumerate[fusionData[i], DeleteOutsideRange, [D2Intervals.universe.x, [FIRST[INT], bbox.y1]]]; [] _ D2Intervals.Enumerate[fusionData[i], DeleteOutsideRange, [D2Intervals.universe.x, [bbox.y2, LAST[INT]]]]; ENDLOOP; }; ENDCASE => SIGNAL InternalBug[fusionData.name]; cdInstances _ cdInstances.rest; ENDLOOP; SELECT mode.fusionByName FROM layout => LayoutFusionByName[fusionData]; none => NoFusionByName[fusionData]; schematics => SchematicsFusionByName[fusionData]; ENDCASE => CallerBug[]; --no way to do fusionByName in this mode! BEGIN InternalDagIfy: HashTable.EachPairAction = { WireDagIfy: PROC [wire: Wire, maxIter: NAT _ 32] = { IF RootWire[fusionData, wire]#wire THEN SIGNAL InternalBug[fusionData.name]; FOR i: NAT IN [0 .. wire.size) DO wire[i] _ RootWire[fusionData, wire[i]]; IF maxIter=0 THEN SIGNAL StructuralLoop[fusionData.name, wire];-- probably loop in the Wire structure WireDagIfy[wire[i], maxIter-1]; ENDLOOP; }; wire: Wire _ NARROW [key]; IF value#NIL THEN RETURN; -- we dagify only the roots [] _ HashTable.Store[internals, wire, NIL]; IF CoreGeometry.HasPins[mode.decoration, wire] THEN [] _ HashTable.Store[publics, wire, NIL]; WireDagIfy[wire]; }; [] _ HashTable.Pairs[fusionData.fused, InternalDagIfy]; END; FOR list: LIST OF CoreClasses.CellInstance _ currentInstances, list.rest WHILE list#NIL DO instance: CoreClasses.CellInstance _ list.first; actual: Wire _ instance.actual; FOR i: NAT IN [0 .. actual.size) DO wire: Wire _ RootWire[fusionData, actual[i]]; IF ~CoreOps.CorrectConform[wire, instance.type.public[i]] THEN SIGNAL StructureMismatch[fusionData.name, wire, instance.type.public[i]]; actual[i] _ wire; ENDLOOP; ENDLOOP; cellType _ CoreClasses.CreateRecordCell[ public: CreateDAGWire[publics], internal: CreateDAGWire[internals], instances: currentInstances, name: CDDirectory.Name[obj], giveNames: TRUE ]; CoreGeometry.PutIR[mode.decoration, cellType, ir]; FOR i: NAT IN [0 .. fusionData.nbOfLayers) DO D2Intervals.Erase[fusionData[i]] ENDLOOP; result _ cellType; }; AbutFusionWire: PROC [fusionData: FusionData, dagTable: HashTable.Table, public: Wire, transformation: CD.Instance] RETURNS [actual: Wire] = { RecordEachPin: CoreGeometry.EachInstanceProc = { sides: CoreGeometry.Sides _ CoreGeometry.GetSides[subIr, instance]; IF (~inX AND sides[top]) OR (inX AND sides[right]) THEN thesePins _ CONS [instance, thesePins]; IF (~inX AND sides[bottom]) OR (inX AND sides[left]) THEN { inst: CD.Instance _ CoreGeometry.Transform[transformation, instance]; iw: IW _ NEW [IWRec _ [instance: inst, wire: structActual]]; layers: LayerRange _ mode.instanceLayer[inst]; FOR i: NAT IN [layers.min .. layers.max] DO D2Intervals.Insert[abutData[i], iw] ENDLOOP; }; IF CoreGeometry.TransfedNotAtEdge[transformation, nir, instance] THEN RETURN; CoreProperties.PutWireProp[structActual, $Public, $Public]; }; prevActual: Wire _ NARROW [HashTable.Fetch[dagTable, public].value]; structActual: Wire; abutData: AbutData _ NARROW [fusionData.data]; thesePins: LIST OF CD.Instance _ NIL; -- not transformed mode: Mode _ fusionData.mode; inX: BOOL _ abutData.inX; nir: CD.Rect _ fusionData.nir; subIr: CD.Rect _ CD.InterestRect[transformation.ob]; -- in the transformation coordonnate system IF prevActual#NIL THEN RETURN [prevActual]; structActual _ CreateRoot[fusionData, public.size]; [] _ CoreGeometry.EnumeratePins[fusionData.mode.decoration, public, RecordEachPin]; FOR i: NAT IN [0 .. public.size) DO structActual[i] _ AbutFusionWire[fusionData, dagTable, public[i], transformation]; ENDLOOP; actual _ IF thesePins=NIL THEN structActual ELSE StructuredFusion[ fusionData, structActual, FusionGeometry[fusionData, transformation, thesePins, FALSE] ]; [] _ HashTable.Store[dagTable, public, actual]; }; AbutFusionWires: PROC [fusionData: FusionData, public: Wire, transformation: CD.Instance] RETURNS [actual: Wire] = { dagTable: HashTable.Table _ HashTable.Create[3]; actual _ CoreOps.CreateWires[public.size]; FOR i: NAT IN [0 .. actual.size) DO actual[i] _ AbutFusionWire[fusionData, dagTable, public[i], transformation]; ENDLOOP; }; ExtractAbut: PUBLIC ExtractProc = { cellType: CellType; -- the result publics: HashTable.Table _ HashTable.Create[3]; internals: HashTable.Table _ HashTable.Create[3]; ir: CD.Rect _ CD.InterestRect[obj]; expanded: CD.Object _ CDDirectory.Expand[obj].new; cellPtr: CD.CellPtr _ NARROW [expanded.specificRef]; cdInstances: LIST OF CD.Instance _ cellPtr.contents; range: D2Intervals.Rect _ RectToRect[CDBasics.Surround[CDBasics.RectAt[[0, 0], expanded.size], ir]]; fusionData: FusionData _ NEW [FusionDataRec[mode.nbOfLayers]]; abutData: AbutData _ NEW [AbutDataRec[mode.nbOfLayers]]; currentInstances: LIST OF CoreClasses.CellInstance _ NIL; count: NAT _ 0; fusionData.mode _ mode; fusionData.name _ CDDirectory.Name[obj]; fusionData.fused _ HashTable.Create[3]; fusionData.data _ abutData; fusionData.isAbut _ TRUE; fusionData.nir _ IF mode.fusionByName=none THEN CDBasics.Extend[ir, - NARROW [userData, REF INT]^] ELSE ir; abutData.inX _ obj.class=PWObjects.abutXClass; FOR i: NAT IN [0 .. mode.nbOfLayers) DO abutData[i] _ D2Intervals.Create[range: range, valueRect: IWToRect, userData: mode]; ENDLOOP; FOR i: NAT IN [0 .. mode.nbOfLayers) DO fusionData[i] _ D2Intervals.Create[range: range, valueRect: IWToRect, userData: mode]; ENDLOOP; FOR list: LIST OF CD.Instance _ cdInstances, list.rest WHILE list#NIL DO count _ count+1 ENDLOOP; PW.WriteF["Extracting [%g] abut %g (size: [%g, %g], instances: %g)\n", IO.rope[mode.decoration.name], IO.rope[CDDirectory.Name[obj]], IO.int[obj.size.x], IO.int[obj.size.y], IO.int[count]]; WHILE cdInstances#NIL DO cdInstance: CD.Instance _ cdInstances.first; subResult: REF; subCellType: CellType; subProps: Properties; instance: CoreClasses.CellInstance; [subResult, subProps] _ Extract[cdInstance.ob, mode, cdInstance.properties, userData]; subCellType _ NARROW [subResult]; instance _ CoreClasses.CreateInstance[ actual: AbutFusionWires[fusionData, subCellType.public, cdInstance], type: subCellType, props: subProps ]; IF ~CoreOps.CorrectConform[instance.actual, subCellType.public] THEN SIGNAL StructureMismatch[fusionData.name, instance.actual, subCellType.public]; FOR i: NAT IN [0 .. mode.nbOfLayers) DO table: D2Intervals.Table _ fusionData[i]; fusionData[i] _ abutData[i]; abutData[i] _ table; D2Intervals.Erase[table]; ENDLOOP; CoreGeometry.PutTransf[mode.decoration, instance, cdInstance]; currentInstances _ CONS [instance, currentInstances]; cdInstances _ cdInstances.rest; ENDLOOP; BEGIN InternalDagIfy: HashTable.EachPairAction = { WireDagIfy: PROC [wire: Wire, maxIter: NAT _ 32] = { IF maxIter=0 THEN SIGNAL StructuralLoop[fusionData.name, wire]; -- probably loop in the Wire structure IF RootWire[fusionData, wire]#wire THEN SIGNAL InternalBug[fusionData.name]; FOR i: NAT IN [0 .. wire.size) DO wire[i] _ RootWire[fusionData, wire[i]]; WireDagIfy[wire[i], maxIter-1]; ENDLOOP; }; wire: Wire _ NARROW [key]; IF value#NIL THEN RETURN; -- we dagify only the roots [] _ HashTable.Store[internals, wire, NIL]; IF CoreProperties.GetWireProp[wire, $Public]#NIL THEN [] _ HashTable.Store[publics, wire, NIL]; WireDagIfy[wire]; }; [] _ HashTable.Pairs[fusionData.fused, InternalDagIfy]; END; FOR list: LIST OF CoreClasses.CellInstance _ currentInstances, list.rest WHILE list#NIL DO instance: CoreClasses.CellInstance _ list.first; actual: Wire _ instance.actual; FOR i: NAT IN [0 .. actual.size) DO wire: Wire _ RootWire[fusionData, actual[i]]; IF ~CoreOps.CorrectConform[wire, instance.type.public[i]] THEN SIGNAL StructureMismatch[fusionData.name, wire, instance.type.public[i]]; actual[i] _ wire; ENDLOOP; ENDLOOP; cellType _ CoreClasses.CreateRecordCell[ public: CreateDAGWire[publics], internal: CreateDAGWire[internals], instances: currentInstances, name: CDDirectory.Name[expanded], giveNames: TRUE ]; CoreGeometry.PutIR[mode.decoration, cellType, ir]; BEGIN SetLazyPins: PROC [wire: Wire] = { CoreGeometry.PutRecordLazyPins[mode.decoration, wire, cellType]; }; ClearPublic: CoreOps.EachWireProc = { CoreProperties.PutWireProp[wire, $Public, NIL]; }; CoreOps.VisitRootAtomics[cellType.public, SetLazyPins]; [] _ CoreOps.VisitWireSeq[cellType.public, ClearPublic]; END; result _ cellType; FOR i: NAT IN [0 .. fusionData.nbOfLayers) DO D2Intervals.Erase[fusionData[i]] ENDLOOP; FOR i: NAT IN [0 .. fusionData.nbOfLayers) DO D2Intervals.Erase[abutData[i]] ENDLOOP; }; ExtractExpand: PUBLIC ExtractProc = { newObj: CD.Object _ CDDirectory.Expand[obj, NIL, NIL].new; PW.WriteF["Expanding [%g] object %g of class %g\n", IO.rope[mode.decoration.name], IO.rope[CDDirectory.Name[obj]], IO.atom[obj.class.objectType]]; IF newObj=NIL THEN SIGNAL CallerBug[]; -- no expand proc found for this object that we do not know how to extract! RETURN Extract[newObj, mode, CDProperties.DAppendProps[obj.properties, properties], userData]; }; NameFromSatellites: PROC [obj: Object, properties: CD.PropList] RETURNS [name: ROPE _ NIL] = { objName: ROPE = CDDirectory.Name[obj]; name _ NARROW [CDProperties.GetListProp[properties, $InstanceName]]; FOR ropes: ROPES _ NARROW [CDProperties.GetListProp[properties, satellitesProp]], ropes.rest WHILE ropes#NIL DO IF name#NIL AND NOT Rope.Equal[ropes.first, name] THEN SIGNAL FusionPropMismatch[objName, CoreOps.nameProp, name, ropes.first]; name _ ropes.first; ENDLOOP; FOR ropes: ROPES _ NARROW [CDProperties.GetObjectProp[obj, satellitesProp]], ropes.rest WHILE ropes#NIL DO IF name#NIL AND NOT Rope.Equal[ropes.first, name] THEN SIGNAL FusionPropMismatch[objName, CoreOps.nameProp, name, ropes.first]; name _ ropes.first; ENDLOOP; }; ExtractRect: PUBLIC ExtractProc = { wire: Wire; IF obj.layer=CD.shadeLayer OR obj.layer=CD.errorLayer OR obj.layer=CD.backgroundLayer OR obj.layer=CD.outlineLayer OR obj.layer=CD.selectionLayer OR obj.layer=CD.commentLayer THEN RETURN [NIL]; wire _ CoreOps.CreateWire[name: NameFromSatellites[obj, properties]]; CoreGeometry.PutPin[mode.decoration, wire, CDInstances.NewInst[obj]]; result _ wire; }; ExtractPin: PUBLIC ExtractProc = { wire: Wire; instance: CD.Instance _ CDInstances.NewInst[ob: obj, properties: properties]; pinName: ROPE _ CDSymbolicObjects.GetName[instance]; IF NameFromSatellites[obj, properties]#NIL THEN SIGNAL CallerBug[]; wire _ CoreOps.CreateWire[name: pinName]; CoreGeometry.PutPin[mode.decoration, wire, instance]; result _ wire; }; ExtractAtomic: PUBLIC ExtractProc = { wire: Wire; wire _ CoreOps.CreateWire[name: NameFromSatellites[obj, properties]]; CoreGeometry.PutPin[mode.decoration, wire, CDInstances.NewInst[obj]]; result _ wire; }; ExtractNull: PUBLIC ExtractProc = {result _ NIL}; DefaultInstanceLayer: PUBLIC PROC [CD.Instance] RETURNS [LayerRange] = {RETURN [[0, 0]]}; AlwaysTrue: PUBLIC PROC [obj: CD.Object, properties1: CD.PropList, userData1: REF, properties2: CD.PropList, userData2: REF] RETURNS [BOOL _ TRUE] = {}; AlwaysFalse: PUBLIC PROC [obj: CD.Object, properties1: CD.PropList, userData1: REF, properties2: CD.PropList, userData2: REF] RETURNS [BOOL _ FALSE] = {}; CompareProps: PUBLIC PROC [obj: CD.Object, properties1: CD.PropList, userData1: REF, properties2: CD.PropList, userData2: REF] RETURNS [BOOL] = { FOR p1: CD.PropList _ properties1, p1.rest WHILE p1#NIL DO IF CDProperties.GetListProp[properties2, p1.first.key]#p1.first.val THEN RETURN [FALSE]; ENDLOOP; FOR p2: CD.PropList _ properties2, p2.rest WHILE p2#NIL DO IF CDProperties.GetListProp[properties1, p2.first.key]#p2.first.val THEN RETURN [FALSE]; ENDLOOP; RETURN [TRUE]; }; satellitesProp: PUBLIC ATOM _ PW.RegisterProp[$SinixSatellites]; RegisterExtractProc[$ExtractCell, ExtractCell]; RegisterExtractProc[$ExtractAbut, ExtractAbut]; RegisterExtractProc[$ExtractExpand, ExtractExpand]; RegisterExtractProc[$ExtractRect, ExtractRect]; RegisterExtractProc[$ExtractPin, ExtractPin]; RegisterExtractProc[$ExtractAtomic, ExtractAtomic]; RegisterExtractProc[$ExtractNull, ExtractNull]; END. ˜SinixImpl.mesa Copyright c 1985, 1986 by Xerox Corporation. All rights reversed. Created by Bertrand Serlet August 15, 1985 2:09:54 pm PDT Bertrand Serlet December 9, 1986 1:40:25 am PST Pradeep Sindhu May 5, 1986 5:16:10 pm PDT Barth, April 18, 1986 4:42:48 pm PST Fusion data structure and internal manipulation Geometry can be attached at all levels of a structured wire. copyGeometry indicates if this geometry is only going to be used for fusion and does not have to figure in the geometry and pins properties (real geometry versus geometry at the level below). note that if copyGeometry AND NOT fusionData.isAbut, instances might be duplicated on pins and geometry If a pin is found, either we are extracting an abut, and then we just note that this is a public wire, or we are extracting a cell and we physically add the pin [and that is the test for being public] Fusion external manipulation Creates a DAG Wire from a HashTable, getting rid of wires that have their father also in the list. delete all the sons We first fuse all the nameWire with same name, and at the same time prepare the list of all the names that have components We sort the names, using the trick that foo[2].mumble or foo[2][3] are after foo[2] in the lexicographic order The following PROC implements Fisher-Galler fusion, not for efficiency, but for simplicity This mechanism could be exported if we wanted that. InstancesFusion: PropFusionProc = { instances1: LIST OF CD.Instance _ NARROW [value1]; instances2: LIST OF CD.Instance _ NARROW [value2]; value _ CDInstances.AppendToList[instances1, instances2]; }; Appends the properties of fused to the ones of root, but treating short names specially This proc really does the fusion of fused and root, by wiping fused of some tables, and adding interesting properties of fused to root. Other Internal utilities The complexity of this proc is partly due to the fact that we have a DAG Extraction We detect trivial causes of bugs! Compute the right overlap of theses cells on this instance it is impossible here to check for conformance, since fusion might have been done differently in different parts of the DAG We try to simplify fusionData by getting rid of all pieces of geometry which are outside the bounding box for sure Time to do fusion by name! We replace all components of structured wires by their roots. We compute internals and publics on the way! 32 should be enough to avoid 815 We replace in all instances wires by their roots We build the CellType Let us recover for circular data structures cdInstances _ SortInstances[cdInstances]; -- we know they are already in reverse order We replace all components of structured wires by their roots. We compute internals and publics on the way! 32 should be enough to avoid 815 We replace in all instances wires by their roots We build the CellType Enable the retrieval of pins Let us recover for circular data structures Extraction mode procs Initialization Κ%D– "cedar" style˜codešœ™Kšœ Οmœ7™BK™9K™/K™)K™$—K™šΟk œ˜ K˜Kšžœn˜pKšœ˜Kšœ,˜,Kšœ ˜ Kšœžœ ˜+Kšžœ ˜Kšœ˜Kšœ˜—K˜•StartOfExpansion[]šΠbn œžœž˜Kšžœ žœΠžœ žœ˜ŒKšžœ˜ Kšžœ˜Kšžœžœ˜—head™/Jš Οn œžœžœžœžœ˜/JšŸ œžœžœžœ˜#Jš œžœžœžœžœžœžœ˜WJš  œžœžœžœžœ˜OJš  œžœžœžœžœ˜NJš  œžœžœ žœžœ˜IJš  œžœžœžœžœ˜>J˜Jš žœžœžœžœžœ˜šœ žœžœžœ˜1J˜—Jšœ žœžœ˜%šœžœžœ˜JšœΟc˜.Jšœžœ ‘_˜rJšœžœ‘)˜:Jšœžœ‘,˜>Jšœ‘’˜«Jšœžœ‘γ˜τJšœžœ žœžœ‘p˜₯J˜—J˜J™šžœžœžœž˜!Jšœ˜Jšžœ˜—J˜—Jšœ žœ˜šžœžœžœžœ˜"Jšœ™Jšœ˜Jšžœ˜—J˜—Jšœžœ˜šŸœ˜,Jšœžœ˜J˜J˜—Jšœ+˜+Jšœ5˜5Jšœ,˜,Jšžœ žœžœ žœ˜?J˜J˜—š œžœ˜5Jš œ žœžœžœ žœ˜9Jšœn˜nš žœ žœžœ%žœ žœž˜QJšœžœ˜#Jšœ8˜8Jšœžœ"˜4Jšœžœ+˜Fšžœžœžœ!˜8JšžœžœL˜W—šžœžœžœ˜Jšœ2˜2šžœžœ˜Jšœ8˜8Jšžœ3žœ žœ˜^Jšœ=˜=J˜—J˜—Jšœ-˜-Jšœ*˜*Jšžœ˜—J˜J˜—š œžœ˜1Jš œ žœžœžœ žœ˜9š  œ˜*Jšœ žœ˜Jšœžœžœ˜Jšœžœ ˜Jšœ1˜1Jšœ˜Jšžœžœžœžœ˜Jšžœ6žœ žœ˜ašžœžœž˜Jšœ+žœžœ žœ˜`Jšœ˜Jšœ˜Jšžœ˜—J˜—Jšœp‘D˜΄š žœ žœžœ%žœ žœž˜QJšœžœ˜#Jšœ8˜8Jšœžœ,˜AJšžœžœžœ žœ˜CJšœ/˜/Jšžœ˜—Jšœ0˜0J˜J˜—š œžœ˜9Jš œ žœžœžœ žœ˜9Jšœn˜nJšœžœžœ˜Jšœz™zš žœ žœžœ%žœ žœž˜QJšœžœ˜#Jšœ8˜8Jšœžœ"˜4Jšœžœ+˜FJšœžœ*žœ˜Ašžœžœžœ!˜8JšžœžœL˜W—Jšžœžœžœ3˜Kšžœžœžœ˜*Jšžœ9˜=Jšžœžœžœ žœ˜6—Jšœ-˜-Jšžœžœžœ+˜DJšžœ˜—Jšœn™nJšœ/˜/š žœžœ5žœžœž˜UJšœžœ˜Jšœ"žœ,˜TJšœžœžœžœ˜$Jšœ˜Jšœ1˜1Jšœžœ+˜@šžœ žœž˜Jšœžœ˜Jš žœžœžœžœ'žœ7žœ˜•Jšœ2˜2šžœžœžœž˜)Jšœ žœ-˜>Jš žœ žœžœžœžœ ˜:šžœ+žœ˜3Jš žœ žœ žœ'žœ?žœ ˜©Jšœ˜—Jšžœ˜—Jš žœ žœžœ'žœ@žœ ˜’Jšœ#˜#Jšœ˜Jšžœ˜—Jšœ6˜6Jšžœ˜—J˜J˜—š  œžœ žœ˜FJšœ žœžœ žœ˜;Jšœ žœ(˜9J˜K˜—J™Zš œžœ&žœ˜PJšžœžœžœžœ˜5Jšœ žœ1˜BJšžœ žœžœžœ˜#Jšžœžœžœ˜:Jšœ*˜*Jšœ9˜9J˜—J˜K™3Kšœžœžœžœžœžœžœžœ žœžœ˜gK˜šŸ œ˜Kšœžœžœ ˜Kšœžœžœ ˜Jšžœžœžœžœžœžœžœ.˜rJš œžœžœžœžœ˜+K˜—K˜šŸœ™#Kš œ žœžœžœ žœ ™2Kš œ žœžœžœ žœ ™2Jšœ9™9K™K™—šŸ œ˜!Jšžœžœžœžœžœžœžœ0˜jJš œžœžœžœžœ˜.K˜—J™JšœW™Wš œžœžœ-žœ˜Qš   œžœžœžœžœ˜1Jšœ žœ*˜8šžœž˜Jšœ½˜½JšœΝ˜ΝJšœe˜eJšžœ[˜b—J˜—Jšœ9˜9J˜—J™Jšœ‡™‡š œžœ0˜DJšžœ1žœžœ˜\Jšžœ#žœžœ˜NJšžœ0žœžœ˜[Jšžœ!žœžœ˜LJšžœ žœžœ‘˜MJšœ?˜?Jšœžœ‘ ˜%Jšœ4˜4J˜J˜—š œžœ.žœ˜\Jšœ$˜$Jšœ$˜$Jšžœ žœžœ ˜#šžœžœž˜Jšœ˜JšœM˜MJšœL˜Lšœ˜Jšœ+˜+šžœžœžœž˜!Jšœ;˜;Jšžœ˜—Jšœ(˜(Jšœ(˜(J˜—Jšžœžœ9˜N—J˜—J˜š œžœ*žœžœžœžœ žœžœžœžœ˜œš žœžœžœžœ!žœžœž˜HJšœžœ@˜HJšœ9˜9J–> -- [key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOLEAN]šœ.˜.š Ÿœžœ6žœžœžœ˜uJšœžœžœ ˜Jšœ žœ˜$Jšœ+˜+Jšžœ0žœžœ˜[Jšžœ%žœžœ˜3Jšžœ+žœžœ˜]J˜—šžœžœžœž˜+JšœK˜KJšžœ˜—Jšžœ˜—J˜——™š œžœ*žœžœžœžœžœžœžœ˜œJšœžœžœ@˜Všžœžœžœ˜Jšœ"˜"JšœJ˜JJšžœ˜J˜—Jšœ˜JšœJ˜JJšœ#˜#šžœžœž˜Jšœ"˜"Jšœ1˜1Jšœ#˜#Jšžœ˜—J˜—J˜JšœH™Hš   œžœSžœžœžœ˜›Jšœžœ+˜DJšœ˜Jšœ žœ$˜4JšœžœžœžœE˜UJšžœ žœžœžœ˜+Jš œ3žœ žœžœžœ˜bšžœžœžœž˜#JšœY˜YJšžœ˜—šœ žœžœ˜Jšžœ˜šžœ˜Jšœ˜Jšœ;˜;Jšœ˜——Jšžœ žœ žœžœ-˜QJšœ/˜/J˜—J˜š  œžœ8žœ žœ˜pJšœ0˜0Jšœ*˜*šžœžœžœžœ˜$JšœHžœ˜OJšžœ˜—J˜J˜—š  œžœ žœžœžœ žœ žœžœžœ žœ˜dš  œžœžœ žœžœ˜0Jšœžœ=˜DJšžœ˜J˜—š œ˜Jšžœžœžœ ˜EJ˜—Jšœ žœ1žœ‘Y˜ͺJ˜J˜——™ Jš œ žœžœžœžœžœ˜CJ˜šŸœžœ˜Jšœžœ.˜CJšœU˜UJš žœžœžœžœMžœžœ,˜¨šž˜Jšœ=˜=Jšœžœžœ>˜QJ˜Jšžœžœžœžœ9˜WJšžœžœžœžœ9˜Wšžœžœžœžœ˜3Jšœ žœžœ7˜Xšžœ žœžœ˜JšžœXžœžœ ˜}J˜—Jšœ˜J˜—Jšœ˜Jšœ%˜%Jšœ˜Jšœ?˜?Jšœ!™!šžœžœž˜J˜J˜Jš œžœžœ/žœžœ‘˜~Jš žœžœžœžœžœ‘-˜b—Jšœ#˜#Jšœ+žœf˜”Jšžœ˜—J˜—J˜Jšœ=˜=š œžœžœžœ˜JJš žœžœ.žœžœžœ.žœ ˜˜J˜J˜—šŸ œžœ˜#Jšœ‘ ˜!Jšœžœžœ˜#Jšœ/˜/Jšœ1˜1Jšœ žœ žœ˜/Jšœ žœžœžœ,˜CJšœžœ"˜>Jšœžœžœžœ˜9J–> -- [key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOLEAN]šœžœ˜Jšžœžœžœžœ#žœžœžœžœ˜aJšžœEžœžœžœžœžœžœžœ žœžœžœžœžœžœžœ ˜ΈJšœA˜Ašœžœ˜+Jš žœžœ žœžœžœ˜A—Jš œ:žœžœžœ žœžœ˜ršžœžœžœž˜'šœ#˜#JšœM˜MJšœ˜Jšœ˜—Jšžœ˜—JšœV‘G˜šžœ žœž˜Jšœ žœ˜,Jšœ žœ ˜Jšœ žœ˜%Jšœe˜ešžœžœ˜ š  œžœžœ ˜&Jšœ žœ ˜Jšžœ!žœ!žœž˜RJšœ^˜^Jšœ žœ žœ˜8Jšžœ!žœ!žœž˜RJšœ^˜^Jšœ žœ žœ˜8J˜—Jšœ:™:Jšœžœ*˜2Jšœ žœ˜Jš œžœžœžœžœžœžœ˜EJš œ!žœžœžœžœžœžœ˜DJš œžœžœžœžœžœžœ˜EJš œžœžœžœžœžœžœ˜Dš žœžœžœžœ(žœžœž˜MJšžœžœ1˜NJšžœ˜—Jšœžœžœ ˜"J˜—JšœY˜Yš žœ žœžœžœ žœž˜0šœ˜JšœFžœ˜LJ˜—šœ˜Jšœ0˜0šžœ žœž˜JšœBžœ˜HJšœ˜Jšžœ˜—J˜—šœ˜Jšœžœ4˜<šœ@˜@JšœA˜AJšœ"˜"Jšœ˜—Jšœ{™{Jšœ>˜>Jšœžœ˜5Jšœr™ršžœžœžœž˜'š Ÿœžœ6žœžœžœ˜nJšœ žœ žœ žœ ˜5Jšžœ*žœ*˜ZJ˜—Jšœ@žœžœ&˜oJšœIžœžœ˜nJšœXžœžœ˜oJšœažœžœ˜nJšžœ˜—J˜—Jšžœžœ˜2—Jšœ˜Jšžœ˜—J™šžœž˜Jšœ*˜*Jšœ%˜%Jšœ1˜1Jšžœ‘)˜B—J™kšž˜šŸœ˜,š  œžœžœ ˜4Jšœ ™ Jšžœ!žœžœ˜Lšžœžœžœž˜!Jšœ(˜(Jšžœ žœžœ'‘&˜eJšœ˜Jšžœ˜—J˜—Jšœ žœ˜Jš žœžœžœžœ‘˜5Jšœ&žœ˜+šžœ-˜/Jšžœ%žœ˜.—Jšœ˜J˜—Jšœ7˜7Jšžœ˜—Jšœ0™0š žœžœžœ8žœžœž˜ZJšœ0˜0Jšœ˜šžœžœžœž˜#Jšœ-˜-Jšžœ8žœžœD˜‰Jšœ˜Jšžœ˜—Jšžœ˜—Jšœ™šœ(˜(Jšœ ˜ Jšœ#˜#Jšœ˜Jšœ˜Jšœ ž˜J˜—Jšœ2˜2Jšœ+™+Jš žœžœžœžœ"žœ˜WJ˜J˜J˜—š œžœSžœ žœ˜Žš  œ#˜0JšœC˜Cšžœžœ žœžœ˜3Jšžœ žœ˜,—š žœžœžœžœžœ˜;Jšœžœ=˜EJšœžœžœ0˜Jšœžœ ˜8Jšœžœžœžœ˜9J–> -- [key: RefTab.Key, val: RefTab.Val] RETURNS [quit: BOOLEAN]šœžœ˜Jšœ™žœ˜žšœžœ˜+Jš žœžœ žœžœžœ˜A—Jšœ/˜/šžœžœžœž˜'JšœT˜TJšžœ˜—šžœžœžœž˜'JšœV˜VJšžœ˜—Jšžœžœžœžœ#žœžœžœžœ˜aJš žœEžœžœžœžœžœ ˜½Jšœ*‘,™Všžœ žœž˜Jšœ žœ˜,Jšœ žœ.˜žœžœI˜”šžœžœžœž˜'Jšœ)˜)JšœL˜LJšžœ˜—Jšœ>˜>Jšœžœ˜5Jšœ˜Jšžœ˜—J™kšž˜šŸœ˜,š  œžœžœ ˜4Jšœ ™ Jšžœ žœžœ(‘&˜fJšžœ!žœžœ˜Lšžœžœžœž˜!Jšœ(˜(Jšœ˜Jšžœ˜—J˜—Jšœ žœ˜Jš žœžœžœžœ‘˜5Jšœ&žœ˜+šžœ+žœ˜1Jšžœ%žœ˜.—Jšœ˜J˜—Jšœ7˜7Jšžœ˜—Jšœ0™0š žœžœžœ8žœžœž˜ZJšœ0˜0Jšœ˜šžœžœžœž˜#Jšœ-˜-Jšžœ8žœžœC˜ˆJšœ˜Jšžœ˜—Jšžœ˜—Jšœ™šœ(˜(Jšœ ˜ Jšœ#˜#Jšœ˜Jšœ!˜!Jšœ ž˜J˜—Jšœ2˜2J™šž˜š  œžœ˜"Jšœ@˜@J˜—š  œ˜%Jšœ*žœ˜/J˜—Jšœ7˜7Jšœ8˜8Jšžœ˜—J˜Jšœ+™+Jš žœžœžœžœ"žœ˜WJš žœžœžœžœ žœ˜UJ˜J˜—šŸ œžœ˜%Jšœžœ"žœžœ˜:Jšžœ2žœžœžœ˜’Jš žœžœžœžœ‘K˜rJšžœX˜^J˜J˜—š  œžœžœ žœžœžœ˜^Jšœ žœ˜&Jšœžœ7˜Dš žœžœžœDžœžœž˜oJš žœžœžœžœžœžœB˜Jšœ˜Jšžœ˜—š žœžœžœ?žœžœž˜jJš žœžœžœžœžœžœB˜Jšœ˜Jšžœ˜—Jšœ˜J˜—šŸ œžœ˜#J˜ Jšžœ žœ žœ žœ žœ žœžœ žœžœ žœžœ žœžœžœžœ˜ΑJšœE˜EJšœE˜EJ˜Jšœ˜J˜—šŸ œžœ˜"J˜ Jšœ žœA˜MJšœ žœ'˜4Jšžœ%žœžœžœ ˜CJšœ)˜)Jšœ5˜5J˜Jšœ˜J˜—š  œžœ˜%J˜ JšœE˜EJšœE˜EJ˜Jšœ˜J˜—JšŸ œžœžœ˜1—™Jš  œžœžœžœ žœžœ ˜YJš  œžœžœžœžœžœžœžœžœžœžœ˜˜Jš  œžœžœžœžœžœžœžœžœžœžœ˜šš  œžœžœžœžœžœžœžœžœžœ˜‘š žœžœ!žœžœž˜:JšžœBžœžœžœ˜XJšžœ˜—š žœžœ!žœžœž˜:JšžœBžœžœžœ˜XJšžœ˜—Jšžœžœ˜Jšœ˜J˜—Jšœžœžœžœ ˜@—™Jšœ/˜/Jšœ/˜/Jšœ3˜3Jšœ/˜/Jšœ-˜-Jšœ3˜3Jšœ/˜/K˜Kšžœ˜K˜——…—ŠJ»&