DIRECTORY Asserting USING [Assertions, Terms, Assert, AssertFn, AssertFn1, FnVal, Union], Atom, Basics, BasicTime, Buttons, Containers, Convert, FS, HashTable, Icons, IO, LichenDataStructure, LichenDataOps, LichenSetTheory, List, PieViewers, Process, ProcessProps, RedBlackTree, RefText, Rope, RopeHash, ViewerClasses, ViewerOps, ViewerTools; LichenFromExt: CEDAR MONITOR LOCKS dr USING dr: DesignReading IMPORTS Asserting, Atom, BasicTime, Buttons, Containers, Convert, FS, HashTable, Icons, IO, LichenDataStructure, LichenDataOps, LichenSetTheory, List, PieViewers, Process, ProcessProps, RedBlackTree, RefText, Rope, RopeHash, ViewerOps, ViewerTools = BEGIN OPEN Asserting, LichenDataStructure, LichenDataOps, LichenSetTheory; REFTEXT: TYPE = REF TEXT; Viewer: TYPE = ViewerClasses.Viewer; SourceList: TYPE = LIST OF Source; Source: TYPE = RECORD [stream: IO.STREAM _ NIL, name: ROPE _ NIL]; Reader: TYPE = REF ReaderRep; ReaderRep: TYPE = RECORD [ keyword: ROPE, read: PROC [s: Source, reader: Reader, cr: CellReading], data: REF ANY]; readers: Mapper _ CreateHashDictionary[TRUE]; DesignReading: TYPE = REF DesignReadingRep; DesignReadingRep: TYPE = MONITORED RECORD [ design: Design, wDir: ROPE, cellTypesByName: Mapper, fetTypes: HashTable.Table, unkosherArrays: Set, toMerge: RedBlackTree.Table, mostRecentPathToMerge: Path _ NIL, impossibleMerges: ImpossibleMergeList _ NIL, buffer: REFTEXT, pacifier, pie, label, pause: Viewer, stop: BOOL _ FALSE, stack: SourceList _ NIL, curCellTypeName, suffix: ROPE _ NIL, curArray: BOOL _ FALSE, curCellFileLength: REAL _ 0, change: CONDITION]; undefinedINT: INT = FIRST[INT]; ImpossibleMergeList: TYPE = LIST OF ImpossibleMerge; ImpossibleMerge: TYPE = RECORD [ arrayInstance: CellInstance, path1, path2: Path]; CellReading: TYPE = REF CellReadingRep; CellReadingRep: TYPE = RECORD [ dr: DesignReading, ct: CellType, name: ROPE, firstMerge: BOOL _ TRUE, resistClasses: INT _ undefinedINT, rScale: REAL--ohms-- _ 1.0E-3, cScale: REAL--farads-- _ 1.0E-18, lUnits: REAL--meters-- _ 1.0E-8, scalingDefined: BOOL _ FALSE, fetCount: INT _ 0, waitingMerges: PathPairList _ NIL ]; PathPairList: TYPE = LIST OF PathPair; Path: TYPE = LIST OF REF ANY--UNION [ROPE, REF Range2]--; VertexArray: TYPE = REF VertexArrayRep; VertexArrayRep: TYPE = RECORD [ shape: Size2, vertices: SEQUENCE length: NAT OF Vertex]; Use: TYPE = RECORD [childName: ROPE, as: ArraySpec]; ArraySpec: TYPE = RECORD [ variant: SELECT kind: * FROM scalar => [], array => [ range: Range2, sep: Int2 ] ENDCASE]; FetTerminal: TYPE = RECORD [ name: ROPE, length: INT, attrs: Assertions]; IntBox: TYPE = RECORD [xmin, ymin, xmax, ymax: INT]; TransformAsTerms: TYPE = Terms; PathPair: TYPE = REF PathPairPrivate; PathPairPrivate: TYPE = RECORD [p1, p2: Path]; pacifierIcon: Icons.IconFlavor _ Icons.NewIconFromFile["Lichen.icons", 0]; labelHeight: INTEGER _ 17; pauseWidth: INTEGER _ 60; pieDiameter: INTEGER _ 50; ReadDesign: PROC [rootCellFileName: ROPE, oldDR: DesignReading _ NIL] RETURNS [dr: DesignReading] = BEGIN Doit: PROC = { dr.stack _ NIL; [] _ ReadCellType[dr.design, rootCellFileName, dr]}; IF oldDR = NIL THEN { cp: FS.ComponentPositions; fullFName, designName: ROPE; [fullFName, cp, ] _ FS.ExpandName[rootCellFileName]; designName _ fullFName.Substr[start: cp.base.start, len: cp.base.length]; dr _ NEW [DesignReadingRep _ [ design: NEW [DesignPrivate _ [ cellTypes: CreateHashSet[], other: Assert[nameReln, LIST[designName], NIL] ]], wDir: fullFName.Substr[len: cp.base.start], cellTypesByName: CreateHashDictionary[TRUE], fetTypes: HashTable.Create[hash: HashFetType, equal: CompareFetTypes], unkosherArrays: CreateHashSet[], toMerge: RedBlackTree.Create[Id, ComparePaths], buffer: RefText.New[200], pacifier: Containers.Create[info: [name: designName.Cat[" pacifier"], icon: pacifierIcon]], pie: NIL, label: NIL, pause: NIL ]]; TRUSTED { Process.InitializeCondition[@dr.change, Process.SecondsToTicks[60]]; Process.EnableAborts[@dr.change]}; dr.pause _ Buttons.Create[info: [name: "Pause", parent: dr.pacifier, wx: 0, wy: 0, ww: pauseWidth, wh: labelHeight], proc: TogglePause, clientData: dr]; dr.label _ ViewerTools.MakeNewTextViewer[info: [parent: dr.pacifier, wx: dr.pause.wx+dr.pause.ww, wy: 0, ww: 100, wh: labelHeight]]; Containers.ChildXBound[container: dr.pacifier, child: dr.label]; dr.pie _ PieViewers.Create[parent: dr.pacifier, x: 0, y: dr.label.wy+dr.label.wh, diameter: pieDiameter, total: 1.0, divisions: 100]; Containers.ChildXBound[container: dr.pacifier, child: dr.pie]; Containers.ChildYBound[container: dr.pacifier, child: dr.pie]; ViewerOps.SetOpenHeight[dr.pacifier, dr.pie.wy+dr.pie.wh]; TRUSTED {Process.Detach[FORK Pacify[dr]]}; } ELSE { dr _ oldDR; }; ProcessProps.PushPropList[List.PutAssoc[$WorkingDirectory, dr.wDir, NIL], Doit]; END; Pacify: PROC [dr: DesignReading] = { lastStack: SourceList _ NIL; lastIndex: INT _ 0; WHILE NOT dr.pacifier.destroyed DO WithLock: ENTRY PROC [dr: DesignReading] = { ENABLE UNWIND => NULL; IF dr.stack = NIL THEN { IF dr.stack # lastStack THEN { lastStack _ dr.stack; ViewerTools.SetContents[dr.label, "Idle"]; PieViewers.Set[dr.pie, 0]; }; } ELSE { index: INT = dr.stack.first.stream.GetIndex[]; IF dr.stack # lastStack OR index # lastIndex THEN { lastStack _ dr.stack; lastIndex _ index; ViewerTools.SetContents[dr.label, IO.PutFR["%g%g[%g]%g", [rope[dr.curCellTypeName]], [rope[IF dr.curArray THEN "[]" ELSE ""]], [integer[index]], [rope[dr.suffix]]]]; PieViewers.Set[dr.pie, index/dr.curCellFileLength]; }; }; }; Process.Pause[Process.MsecToTicks[pacifyPeriod]]; WithLock[dr]; ENDLOOP; }; pacifyPeriod: Process.Milliseconds _ 2500; TogglePause: Buttons.ButtonProc = { dr: DesignReading = NARROW[clientData]; Flip: ENTRY PROC [dr: DesignReading] = { ENABLE UNWIND => NULL; dr.stop _ NOT dr.stop; BROADCAST dr.change}; Flip[dr]; Buttons.ReLabel[dr.pause, IF dr.stop THEN "Continue" ELSE "Pause"]; }; PrintImpossibles: PROC [to: IO.STREAM, dr: DesignReading] = { FOR ims: ImpossibleMergeList _ dr.impossibleMerges, ims.rest WHILE ims # NIL DO im: ImpossibleMerge = ims.first; to.PutF["%g: %g & %g\n", [rope[Describe[im.arrayInstance, dr.design]]], [rope[FmtPath[im.path1]]], [rope[FmtPath[im.path2]]]]; ENDLOOP; }; fromExtClass: CellClass _ NEW [CellClassPrivate _ []]; DoPushed: PROC [dr: DesignReading, cellTypeName: ROPE, array: BOOL, s: Source, Proc: PROC [Source]] = { cooler: SourceList = dr.stack; oldCellTypeName: ROPE = dr.curCellTypeName; oldArray: BOOL = dr.curArray; oldSuffix: ROPE = dr.suffix; oldCellFileLength: REAL = dr.curCellFileLength; newSuffix: ROPE = FmtStack[cooler]; newCellFileLength: INT = MAX[s.stream.GetLength[], 1]; Push: ENTRY PROC [dr: DesignReading] = { ENABLE UNWIND => NULL; dr.stack _ CONS[s, dr.stack]; dr.curCellTypeName _ cellTypeName; dr.curArray _ array; dr.suffix _ newSuffix; dr.curCellFileLength _ newCellFileLength; }; Pop: ENTRY PROC [dr: DesignReading] = { ENABLE UNWIND => NULL; dr.stack _ cooler; dr.curCellTypeName _ oldCellTypeName; dr.curArray _ oldArray; dr.suffix _ oldSuffix; dr.curCellFileLength _ oldCellFileLength; }; Push[dr]; Proc[s !UNWIND => Pop[dr]]; Pop[dr]; }; ReadCellType: PROC [design: Design, cellFileName: ROPE, dr: DesignReading] RETURNS [ct: CellType] = { cp: FS.ComponentPositions; fullFName, cellTypeName: ROPE; from: IO.STREAM; s: Source; cr: CellReading; Pushed: PROC [s: Source] = {PushedRead[dr, cr, s, FALSE]}; [fullFName, cp] _ ExpandName[cellFileName, "ext"]; cellTypeName _ fullFName.Substr[start: cp.base.start, len: cp.base.length]; ct _ LocalCreateCellType[design, dr, cellTypeName, TRUE, NIL, NIL]; cr _ NEW[CellReadingRep _ [dr: dr, ct: ct, name: cellTypeName]]; s _ [from _ FS.StreamOpen[fullFName], fullFName]; DoPushed[dr, cellTypeName, FALSE, s, Pushed]; from.Close[]; }; TryArrayFile: PROC [cr: CellReading] = { fullFName: ROPE = ExpandName[cr.name, "aext"].fullFName; s: Source = [FS.StreamOpen[fullFName], fullFName]; Pushed: PROC [s: Source] = { PushedRead[cr.dr, cr, s, TRUE]; FinishWaitingMerges[cr]; }; IF cr.dr.toMerge.Size[] # 0 OR cr.waitingMerges # NIL THEN ERROR; DoPushed[cr.dr, cr.name, TRUE, s, Pushed]; s.stream.Close[]; }; PushedRead: PROC [dr: DesignReading, cr: CellReading, s: Source, nested: BOOL] = { from: IO.STREAM = s.stream; ct: CellType = cr.ct; DO keyword: ROPE; reader: Reader; Process.CheckForAbort[]; [] _ from.SkipWhitespace[]; IF from.EndOf[] THEN EXIT; IF dr.stop THEN Wait[dr]; keyword _ from.GetTokenRope[TokenBreak].token; reader _ NARROW[readers.Map[keyword]]; IF reader # NIL THEN reader.read[s, reader, cr] ELSE { terms: Terms _ GetLineTerms[from]; reln: ATOM _ Atom.MakeAtom[keyword]; ct.otherPublic _ Assert[reln, terms, ct.otherPublic]; }; ENDLOOP; IF dr.toMerge.Size[] # 0 THEN { IF cr.firstMerge THEN ERROR; DoMerges[s, cr]; } ELSE IF cr.firstMerge THEN {cr.firstMerge _ FALSE; TryArrayFile[cr]}; IF nested THEN RETURN; ct.publicKnown _ TRUE; ct.privateKnown _ TRUE; {CleanupChild: PROC [ra: REF ANY] = { ci: CellInstance = NARROW[ra]; childType: CellType = ci.type; IF childType.asArray # NIL THEN { IF childType.useCount # 1 THEN ERROR; <> IF IsIncompleteArray[childType] THEN ERROR; TrimArray[childType.asArray]; }; }; ct.asUnorganized.containedInstances.Enumerate[CleanupChild]; }}; FinishWaitingMerges: PROC [cr: CellReading] = { DO progress: BOOL _ FALSE; wml: PathPairList _ cr.waitingMerges; cr.waitingMerges _ NIL; FOR wml _ wml, wml.rest WHILE wml # NIL DO progress _ MergeWork[cr, wml.first.p1, wml.first.p2] OR progress; ENDLOOP; IF NOT progress THEN EXIT; ENDLOOP; IF cr.waitingMerges # NIL THEN ERROR; }; Wait: ENTRY PROC [dr: DesignReading] = { ENABLE UNWIND => NULL; WHILE dr.stop DO WAIT dr.change ENDLOOP}; FmtStack: PROC [stack: SourceList] RETURNS [suffix: ROPE] = { suffix _ NIL; FOR stack _ stack, stack.rest WHILE stack # NIL DO full: ROPE; cp: FS.ComponentPositions; [full, cp, ] _ FS.ExpandName[stack.first.name]; suffix _ IO.PutFR["%g, %g[%g]", [rope[suffix]], [rope[full.Substr[cp.base.start, cp.base.length]]], [integer[IO.GetIndex[stack.first.stream]]]]; ENDLOOP; suffix _ suffix; }; GetLineTerms: PROC [from: IO.STREAM] RETURNS [terms: Terms] = { tail: Terms _ terms _ NIL; WHILE NOT from.EndOf[] DO peek: CHAR _ from.PeekChar[]; SELECT peek FROM '\n => {IF from.GetChar[] # peek THEN ERROR; RETURN}; IN [0C .. ' ] => IF from.GetChar[] # peek THEN ERROR; ENDCASE => { this: LORA _ LIST[from.GetRefAny[]]; IF tail = NIL THEN terms _ this ELSE tail.rest _ this; tail _ this}; ENDLOOP; }; ExpandName: PROC [fileName, defaultExtension: ROPE] RETURNS [fullFName: ROPE, cp: FS.ComponentPositions] = { [fullFName, cp, ] _ FS.ExpandName[fileName]; IF defaultExtension.Length[] > 0 AND cp.ext.length = 0 THEN { fileName _ FS.ConstructFName[[ server: fullFName.Substr[cp.server.start, cp.server.length], dir: fullFName.Substr[cp.dir.start, cp.dir.length], subDirs: fullFName.Substr[cp.subDirs.start, cp.subDirs.length], base: fullFName.Substr[cp.base.start, cp.base.length], ext: defaultExtension, ver: fullFName.Substr[cp.ver.start, cp.ver.length] ]]; [fullFName, cp, ] _ FS.ExpandName[fileName]; }; }; GetName: PROC [s: Source] RETURNS [name: ROPE] = { from: IO.STREAM = s.stream; [] _ from.SkipWhitespace[]; SELECT from.PeekChar[] FROM '" => name _ from.GetRopeLiteral[]; ENDCASE => name _ from.GetTokenRope[TokenBreak].token; }; TokenBreak: PROC [char: CHAR] RETURNS [cc: IO.CharClass] = {cc _ SELECT char FROM '\n => break, IN [0C .. ' ] => sepr, ENDCASE => other}; EndLine: PROC [from: IO.STREAM, buffer: REFTEXT] = { IF NOT from.EndOf[] THEN [] _ from.GetLine[buffer]; }; ReadTech: PROC [s: Source, reader: Reader, cr: CellReading] = { ct: CellType = cr.ct; from: IO.STREAM = s.stream; techname: ROPE _ GetName[s]; ct.otherPublic _ Assert[$tech, LIST[techname], ct.otherPublic]; EndLine[from, cr.dr.buffer]; }; ReadTimestamp: PROC [s: Source, reader: Reader, cr: CellReading] = { ct: CellType = cr.ct; from: IO.STREAM = s.stream; unixTime: INT _ from.GetInt[]; time: BasicTime.GMT _ BasicTime.Update[unixOrigin, unixTime]; ct.otherPublic _ Assert[$Source, LIST[cr.name.Cat[".mag"], IO.PutFR["%g", IO.time[time]]], ct.otherPublic]; EndLine[from, cr.dr.buffer]; }; unixOrigin: BasicTime.GMT _ BasicTime.Pack[[ year: 1970, month: January, day: 1, hour: 0, minute: 0, second: 0, zone: 0--GMT, I hope--, dst: no]]; ReadVersion: PROC [s: Source, reader: Reader, cr: CellReading] = { ct: CellType = cr.ct; from: IO.STREAM = s.stream; version: ROPE _ GetName[s]; deriver: ROPE = "UCB's Magic .extractor"; ct.otherPublic _ Assert[$DerivingProgram, LIST[deriver, version], ct.otherPublic]; EndLine[from, cr.dr.buffer]; }; ReadScale: PROC [s: Source, reader: Reader, cr: CellReading] = { ct: CellType = cr.ct; from: IO.STREAM = s.stream; rscale: INT _ from.GetInt[]; cscale: INT _ from.GetInt[]; lscale: INT _ from.GetInt[]; meters: ROPE = "meters"; IF cr.scalingDefined THEN Warn[s, "More than one scale statment"]; cr.scalingDefined _ TRUE; cr.rScale _ rscale * cr.rScale; cr.cScale _ cscale * cr.cScale; cr.lUnits _ lscale * cr.lUnits; ct.otherPublic _ AssertFn[$scale, LIST[NEW[REAL_cr.lUnits], meters], ct.otherPublic]; EndLine[from, cr.dr.buffer]; }; ReadResistClasses: PROC [s: Source, reader: Reader, cr: CellReading] = { ct: CellType = cr.ct; from: IO.STREAM = s.stream; n: INT _ 0; IF cr.resistClasses # undefinedINT THEN Warn[s, "More than one resistclasses statment"]; DO token: ROPE = from.GetTokenRope[TokenBreak].token; IF token.Equal["\n"] THEN EXIT; n _ n + 1; ENDLOOP; cr.resistClasses _ n; }; keepCruft: BOOL _ FALSE; ReadNode: PROC [s: Source, reader: Reader, cr: CellReading] = { ct: CellType = cr.ct; from: IO.STREAM = s.stream; nodeName: ROPE = GetName[s]; R: INT = from.GetInt[]; C: INT = from.GetInt[]; x: INT = from.GetInt[]; y: INT = from.GetInt[]; ok: BOOL = SkipNTokens[from, cr.resistClasses*2, cr.dr.buffer]; attrs: Assertions = ReadAttrs[s]; nv: Wire _ NARROW[LookupPart[ct, nodeName]]; IF nv = NIL THEN { nv _ CreateWire[ containingCT: ct, other: Assert[nameReln, LIST[nodeName], NIL] ]; } ELSE { }; IF keepCruft THEN nv.other _ Asserting.Union[nv.other, Assert[$R, LIST[NEW[REAL _ R*cr.rScale]], Assert[$C, LIST[NEW[REAL _ C*cr.cScale]], Assert[$locHint, LIST[NEW[INT_x], NEW[INT_y], $UnspecifiedLayer], attrs]]] ]; EndLine[from, cr.dr.buffer]; }; ReadAttrs: PROC [s: Source, zeroNIL: BOOL _ FALSE] RETURNS [allTogetherNow: Assertions] = { from: IO.STREAM = s.stream; toke: ROPE _ NIL; allTogetherNow _ NIL; IF zeroNIL THEN { [] _ from.SkipWhitespace[]; IF from.PeekChar[] = '0 THEN { IF from.GetChar[] # '0 THEN ERROR; RETURN}; }; {DO attr: ROPE _ NIL; toke _ from.GetTokenRope[AttrBreak !IO.EndOfStream => EXIT].token; IF toke.Equal[","] THEN {Warn[s, "Extra comma"]; LOOP}; IF NOT toke.Equal["\""] THEN GOTO Return; from.Backup['"]; attr _ from.GetRopeLiteral[ !IO.Error, IO.EndOfStream => {Warn[s, "not a rope literal"]; CONTINUE}]; IF attr # NIL THEN allTogetherNow _ Assert[$attr, LIST[attr], allTogetherNow]; toke _ from.GetTokenRope[AttrBreak !IO.EndOfStream => EXIT].token; IF NOT toke.Equal[","] THEN GOTO Return; ENDLOOP; EXITS Return => { FOR i: INT DECREASING IN [0 .. toke.Length[]) DO s.stream.Backup[toke.Fetch[i]]; ENDLOOP; }; }}; AttrBreak: PROC [char: CHAR] RETURNS [cc: IO.CharClass] = {cc _ SELECT char FROM ',, '\n, '" => break, ENDCASE => sepr}; ReadEquiv: PROC [s: Source, reader: Reader, cr: CellReading] = { ct: CellType = cr.ct; from: IO.STREAM = s.stream; name1: ROPE = GetName[s]; name2: ROPE = GetName[s]; v: Vertex _ LookupPart[ct, name1]; otherName: ROPE _ name2; IF v = NIL THEN {v _ LookupPart[ct, name2]; otherName _ name1}; IF v = NIL THEN ERROR; KnowVertexName[v, otherName]; EndLine[from, cr.dr.buffer]; }; ReadFet: PROC [s: Source, reader: Reader, cr: CellReading] = { ct: CellType = cr.ct; from: IO.STREAM = s.stream; type: ROPE _ GetName[s]; xl: INT _ from.GetInt[]; yl: INT _ from.GetInt[]; xh: INT _ from.GetInt[]; yh: INT _ from.GetInt[]; area: INT _ from.GetInt[]; perim: INT _ from.GetInt[]; sub: ROPE _ GetName[s]; GATE: FetTerminal _ GetFetTerminal[s]; T1: FetTerminal _ GetFetTerminal[s]; T2: FetTerminal _ GetFetTerminal[s]; sct: CellType; tv: CellInstance; DoTerm: PROC [portIndex: INT, ft: FetTerminal] = { nv: Wire = GetNet[s, ct, ft.name]; Connect[tv, nv, portIndex]; }; sct _ GetFetType[cr.dr, type, [xl, yl, xh, yh], area, perim, T1.length+T2.length]; tv _ Instantiate[sct, ct, Assert[nameReln, LIST[IO.PutFR["Q%g", IO.int[cr.fetCount _ cr.fetCount + 1]]], NIL]]; DoTerm[0, GATE]; DoTerm[1, T1]; DoTerm[2, T2]; EndLine[from, cr.dr.buffer]; }; Connect: PROC [ci: CellInstance, wire: Wire, portIndex: INT] = { [] _ AddEdge[vs: [cellward: ci, wireward: wire], port: ci.type.port.SubPort[portIndex]]; }; GetFetTerminal: PROC [s: Source] RETURNS [ft: FetTerminal] = { from: IO.STREAM = s.stream; ft.name _ GetName[s]; ft.length _ from.GetInt[]; ft.attrs _ ReadAttrs[s, TRUE]; }; GetFetType: PROC [dr: DesignReading, className: ROPE, innerGate: IntBox, area, perim, sumChannelLengths: INT] RETURNS [ct: CellType] = { design: Design = dr.design; ft: FetType = NEW [FetTypeRep _ [className, area, perim, sumChannelLengths]]; rft: FetType; rft _ NARROW[dr.fetTypes.Fetch[ft].value]; IF rft = NIL THEN { cellTypeName: ROPE = IO.PutFR["%g[%g,%g,%g]", IO.rope[ft.className], IO.int[ft.area], IO.int[ft.perim], IO.int[ft.twiceLength]]; Set: PROC [type, mode: ATOM] = { ft.ct.otherPublic _ AssertFn1[$MOSFETFlavor, LIST[type, mode], ft.ct.otherPublic]; ft.ct.otherPublic _ AssertFn1[$EquivClass, Rope.Cat["MOSFET", Atom.GetPName[type], Atom.GetPName[mode]], ft.ct.otherPublic]; }; rft _ ft; ft.ct _ LocalCreateCellType[design, dr, cellTypeName, FALSE, AssertFn1[$MOSFETShape, LIST[NEW[REAL _ ft.twiceLength/2.0], NEW[REAL _ area*2.0/ft.twiceLength]], NIL], NIL]; FetPort[ft.ct.port]; SELECT TRUE FROM className.Equal["nfet"] => Set[$n, $E]; className.Equal["pfet"] => Set[$p, $E]; ENDCASE => ERROR; IF NOT dr.fetTypes.Insert[ft, ft] THEN ERROR; }; ct _ rft.ct; }; FetPort: PROC [fp: Port] = { [] _ AddPort[[parent: fp, other: Assert[nameReln, LIST[R["gate"]], NIL]]]; [] _ AddPort[[parent: fp, other: Assert[nameReln, LIST[R["ch1"]], NIL]]]; [] _ AddPort[[parent: fp, other: Assert[nameReln, LIST[R["ch2"]], NIL]]]; }; R: PROC [r: ROPE] RETURNS [r2: ROPE] = INLINE {r2 _ r}--stupid goddam anachronism--; FetType: TYPE = REF FetTypeRep; FetTypeRep: TYPE = RECORD [ className: ROPE, area, perim, twiceLength: INT, ct: CellType _ NIL]; HashFetType: PROC [ra: REF ANY] RETURNS [hash: CARDINAL] --HashTable.HashProc-- = { ft: FetType = NARROW[ra]; hash _ RopeHash.FromRope[ft.className]; hash _ (hash + 3*ft.area + 11*ft.perim + 101*ft.twiceLength) MOD 65536; }; CompareFetTypes: PROC [r1, r2: REF ANY] RETURNS [equal: BOOL] --HashTable.EqualProc-- = { k1: FetType = NARROW[r1]; k2: FetType = NARROW[r2]; equal _ k1.className.Equal[k2.className] AND k1.area = k2.area AND k1.perim = k2.perim AND k1.twiceLength = k2.twiceLength; }; ReadUse: PROC [s: Source, reader: Reader, cr: CellReading] = { ct: CellType = cr.ct; from: IO.STREAM = s.stream; typeName: ROPE = GetName[s]; useId: ROPE = GetName[s]; ok: BOOL = SkipNTokens[from, 6, cr.dr.buffer]; u: Use = ParseUseDef[useId]; type: CellType = EnsureType[cr.dr, typeName, u.as, ct, u.childName]; ci: CellInstance = FullyInstantiate[type, ct, Assert[nameReln, LIST[u.childName], NIL]]; EndLine[from, cr.dr.buffer]; }; ParseUseDef: PROC [useId: ROPE] RETURNS [u: Use] = { in: IO.STREAM = IO.RIS[useId]; u.childName _ in.GetTokenRope[UseNameBreak].token; IF in.EndOf[] THEN { in.Close[]; RETURN [[u.childName, [scalar[]]]] } ELSE { as: ArraySpec.array _ [array[range: ALL[[0, 0]], sep: [0, 0]]]; Get: PROC [d: Dim] = { IF in.GetChar[] # '[ THEN ERROR; as.range[d].min _ in.GetInt[]; IF in.GetChar[] # ': THEN ERROR; as.range[d].maxPlusOne _ in.GetInt[]+1; IF in.GetChar[] # ': THEN ERROR; as.sep[d] _ in.GetInt[]; IF in.GetChar[] # '] THEN ERROR; }; Get[Foo]; Get[Bar]; IF NOT in.EndOf[] THEN ERROR; in.Close[]; RETURN [[u.childName, as]]; }; }; UseNameBreak: PROC [char: CHAR] RETURNS [cc: IO.CharClass] --IO.BreakProc-- = { cc _ SELECT char FROM '[, '], ': => break, ENDCASE => other; }; EnsureType: PROC [dr: DesignReading, typeName: ROPE, as: ArraySpec, parent: CellType, childName: ROPE] RETURNS [ct: CellType] = { design: Design = dr.design; WITH as SELECT FROM x: ArraySpec.scalar => { ct _ NARROW[dr.cellTypesByName.Map[typeName]]; IF ct = NIL THEN ct _ ReadCellType[design, typeName, dr]; }; x: ArraySpec.array => { ec: ROPE = typeName.Cat[FmtAS[as]]; cellTypeName: ROPE = IO.PutFR["%g(%g.%g)", IO.rope[ec], IO.rope[CTName[parent]], IO.rope[childName]]; eltType: CellType _ EnsureType[dr, typeName, [scalar[]], NIL, NIL]; size: Size2 = RangeShape[x.range]; ct _ LocalCreateArray[design, dr, cellTypeName, eltType, size, [1, 1], [[1, 1], [1, 1]], AssertFn1[$EquivClass, ec, NIL], AssertFn1[$ArraySpec, NEW [ArraySpec.array _ x], NIL]]; }; ENDCASE => ERROR; }; EmptyRange2: PROC [r: Range2] RETURNS [empty: BOOL] = { empty _ r[Foo].maxPlusOne<=r[Foo].min OR r[Bar].maxPlusOne<=r[Bar].min; }; NameElt: PROC [i: INT] RETURNS [eltName: ROPE] = {eltName _ IO.PutFR["[%g]", IO.int[i]]}; NameElt2: PROC [f, b: INT] RETURNS [eltName: ROPE] = {eltName _ IO.PutFR["[%g, %g]", IO.int[f], [integer[b]]]}; FmtAS: PROC [as: ArraySpec] RETURNS [r: ROPE] = { r _ WITH as SELECT FROM scalar => "scalar", array => IO.PutFLR["[%g:%g:%g][%g:%g:%g]", LIST[ IO.int[range[Foo].min], IO.int[range[Foo].maxPlusOne-1], IO.int[sep[Foo]], IO.int[range[Bar].min], IO.int[range[Bar].maxPlusOne-1], IO.int[sep[Bar]]]], ENDCASE => ERROR; }; FmtShape: PROC [shape: Size2] RETURNS [r: ROPE] = { r _ IO.PutFR["[Foo: %g, Bar: %g]", IO.int[shape[Foo]], IO.int[shape[Bar]]]; }; FmtPath: PROC [path: Path] RETURNS [r: ROPE] = { r _ NIL; FOR path _ path, path.rest WHILE path # NIL DO step: ROPE _ WITH path.first SELECT FROM x: ROPE => x, x: REF Range2 => IO.PutFR["[%g:%g,%g:%g]", [integer[x[Foo].min]], [integer[x[Foo].maxPlusOne-1]], [integer[x[Bar].min]], [integer[x[Bar].maxPlusOne-1]]], ENDCASE => ERROR; r _ (IF r # NIL THEN r.Concat["/"] ELSE r).Concat[step]; ENDLOOP; }; ReadMerge: PROC [s: Source, reader: Reader, cr: CellReading] = { ct: CellType = cr.ct; from: IO.STREAM = s.stream; IF cr.firstMerge THEN {cr.firstMerge _ FALSE; TryArrayFile[cr]}; {name1: ROPE = GetName[s]; name2: ROPE = GetName[s]; path1: Path = ParsePath[s, ct, name1]; path2: Path = ParsePath[s, ct, name2]; IF PathCompare[path1, cr.dr.mostRecentPathToMerge]#equal THEN { DoMerges[s, cr]; cr.dr.toMerge.Insert[path1, path1]; }; cr.dr.toMerge.Insert[path2, path2]; cr.dr.mostRecentPathToMerge _ path2; EndLine[from, cr.dr.buffer]; }}; DoMerges: PROC [s: Source, cr: CellReading] = { from: CellType = cr.ct; dr: DesignReading = cr.dr; lastPath: Path _ NIL; DoAMerge: PROC [data: REF ANY] RETURNS [stop: BOOL _ FALSE] --RedBlackTree.EachNode-- = { path: Path = NARROW[data]; IF lastPath # NIL THEN [] _ MergeWork[cr, lastPath, path]; lastPath _ path; }; dr.toMerge.EnumerateIncreasing[DoAMerge]; dr.toMerge.DestroyTable[]; dr.mostRecentPathToMerge _ NIL; }; ParsePath: PROC [s: Source, from: CellType, asRope: ROPE] RETURNS [p: Path] = { in: IO.STREAM = IO.RIS[asRope]; t: Path _ p _ NIL; Append: PROC [ra: REF ANY] = { this: Path _ LIST[ra]; IF t = NIL THEN p _ this ELSE t.rest _ this; t _ this}; GetRange: PROC RETURNS [x: Range] = { x.maxPlusOne _ (x.min _ in.GetInt[]) + 1; SELECT in.PeekChar[] FROM ': => { IF in.GetChar[] # ': THEN ERROR; x.maxPlusOne _ in.GetInt[] + 1; }; ',, '] => NULL; ENDCASE => ERROR; }; WHILE NOT in.EndOf[] DO toke: ROPE _ in.GetTokenRope[PathNameBreak].token; ras: REF ArraySpec.array = NARROW[FnVal[$ArraySpec, from.otherPrivate]]; SELECT TRUE FROM toke.Equal["/"] => LOOP; toke.Equal["["] => { food: BOOL = ras.range[Foo].min # ras.range[Foo].maxPlusOne-1; bard: BOOL = ras.range[Bar].min # ras.range[Bar].maxPlusOne-1; r2: Range2 _ ras.range; twoD: BOOL _ FALSE; IF NOT (food OR bard) THEN ERROR; IF bard THEN r2[Bar] _ GetRange[] ELSE r2[Foo] _ GetRange[]; toke _ in.GetTokenRope[PathNameBreak].token; SELECT TRUE FROM toke.Equal["]"] => NULL; toke.Equal[","] => { twoD _ TRUE; r2[Foo] _ GetRange[]; toke _ in.GetTokenRope[PathNameBreak].token; IF NOT toke.Equal["]"] THEN ERROR; }; ENDCASE => ERROR; IF twoD # (food AND bard) THEN ERROR; Append[NEW [Range2 _ r2]]; from _ from.asArray.eltType; }; toke.Equal["]"] => ERROR; toke.Equal[":"] => ERROR; toke.Equal[","] => ERROR; ENDCASE => { Append[toke]; WITH LookupPart[from, toke] SELECT FROM ci: CellInstance => from _ ci.type; w: Wire => {IF NOT in.EndOf[] THEN ERROR; from _ NIL}; ENDCASE => ERROR; }; ENDLOOP; in.Close[]; }; PathNameBreak: PROC [char: CHAR] RETURNS [cc: IO.CharClass] --IO.BreakProc-- = { cc _ SELECT char FROM '[, '], ':, '/, ', => break, ENDCASE => other; }; SubType: PROC [ct: CellType, subscript: INT] RETURNS [sct: CellType] = { ci: CellInstance = NARROW[LookupPart[ct, NameElt[subscript]]]; sct _ ci.type}; MergeWork: PROC [cr: CellReading, path1, path2: Path] RETURNS [success: BOOL] = { ct: CellType = cr.ct; IF ct.asArray # NIL THEN ERROR; IF cr.dr.curArray AND path1.rest # NIL AND path2.rest # NIL THEN WITH path1.first SELECT FROM x: ROPE => WITH path2.first SELECT FROM y: ROPE => IF x.Equal[y] THEN { ci: CellInstance = NARROW[LookupPart[ct, x]]; IF ci.type.asArray # NIL THEN { IF ci.type.useCount # 1 THEN ERROR; IF NOT (success _ ArrayMerge[cr, ci, x, path1.rest, path2.rest]) THEN cr.waitingMerges _ CONS[NEW [PathPairPrivate _ [path1, path2]], cr.waitingMerges]; RETURN; }; }; y: REF Range2 => ERROR; ENDCASE => ERROR; x: REF Range2 => ERROR; ENDCASE => ERROR; MergeFinal[ct, path1, path2]; success _ TRUE; }; flg1: BOOL _ TRUE; --do we avoid making some self-connections because of edge conditions? flg2: BOOL _ FALSE; --do we look only at the critical edge? ArrayMerge: PROC [cr: CellReading, arrayInstance: CellInstance, instanceName: ROPE, path1, path2: Path] RETURNS [success: BOOL] = { act: CellType = arrayInstance.type; a: Array = act.asArray; ras: REF ArraySpec.array = NARROW[FnVal[$ArraySpec, act.otherPrivate]]; et: CellType = a.eltType; rr1: REF Range2 = NARROW[path1.first]; rr2: REF Range2 = NARROW[path2.first]; r1: Range2 = rr1^; r2: Range2 = rr2^; size: Size2 = RangeShape[r2]; xlate: Int2 = [-ras.range[Foo].min, -ras.range[Bar].min]; s1: Range2 = Range2Off[r1, xlate]; s2: Range2 = Range2Off[r2, xlate]; D: Int2 = Int2Sub[Range2Min[r2], Range2Min[r1]]; p1: Port = PortGet[et, path1.rest, TRUE]; p2: Port = PortGet[et, path2.rest, TRUE]; IF RangeShape[r1] # size THEN ERROR; success _ TRUE; SELECT TRUE FROM D = [0, 0] => IF NOT PathEquiv[et, path1.rest, path2.rest] THEN { IF a.size[Foo] < 2 AND a.size[Bar] < 2 THEN ERROR nyet--the following won't work because there are no joints--; FOR d: Dim IN Dim DO FOR side: End IN End DO lowRange: Range2 _ s1; tooMuch: BOOL; SELECT side FROM low => lowRange[d].maxPlusOne _ MIN[lowRange[d].maxPlusOne, a.size[d]-1]; high => lowRange[d] _ RangeOffClip[lowRange[d], -1]; ENDCASE => ERROR; tooMuch _ flg1 AND (SELECT side FROM low => lowRange[d].min = 1 AND (flg2 OR lowRange[d].maxPlusOne = a.size[d]-1), high => lowRange[d].maxPlusOne = a.size[d]-2 AND (flg2 OR lowRange[d].min = 0), ENDCASE => ERROR); IF NOT (tooMuch OR EmptyRange2[lowRange]) THEN { MakeArrayConnection[act, d, lowRange, [side, p1], [side, p2]]; success _ TRUE}; ENDLOOP; ENDLOOP; }; ABS[D[Foo]]=1 AND ABS[D[Bar]]=1 => { success _ FALSE; FOR d: Dim IN Dim DO o: Dim = OtherDim[d]; lowRange: Range2 _ s1; highRange: Range2 _ s2; lowPort, midPort, highPort, pf, pt: Port; Do: INT _ D[o]; SELECT D[d] FROM 1 => {lowPort _ p1; highPort _ p2}; -1 => {lowPort _ p2; highPort _ p1; lowRange _ s2; highRange _ s1; Do _ -Do}; ENDCASE => ERROR; midPort _ CrossATie[act, d, lowPort, low]; IF midPort # NIL THEN { lowRange[d] _ RangeOff[lowRange[d], 1]; SELECT Do FROM 1 => {pf _ midPort; pt _ highPort}; -1 => {pf _ highPort; pt _ midPort; lowRange[o] _ RangeOff[lowRange[o], -1]}; ENDCASE => ERROR; MakeArrayConnection[act, o, lowRange, [low, pf], [high, pt]]; success _ TRUE; EXIT; }; midPort _ CrossATie[act, d, highPort, high]; IF midPort # NIL THEN { lowRange _ highRange; lowRange[d] _ RangeOff[lowRange[d], -1]; SELECT Do FROM 1 => {pf _ lowPort; pt _ midPort; lowRange[o] _ RangeOff[lowRange[o], -1]}; -1 => {pf _ midPort; pt _ lowPort}; ENDCASE => ERROR; MakeArrayConnection[act, o, lowRange, [low, pf], [high, pt]]; success _ TRUE; EXIT; }; ENDLOOP; }; ABS[D[Foo]] + ABS[D[Bar]] = 1 => { SELECT TRUE FROM D=[-1, 0] => MakeArrayConnection[act, Foo, s2, [low, p2], [high, p1]]; D=[ 1, 0] => MakeArrayConnection[act, Foo, s1, [low, p1], [high, p2]]; D=[0, -1] => MakeArrayConnection[act, Bar, s2, [low, p2], [high, p1]]; D=[0, 1] => MakeArrayConnection[act, Bar, s1, [low, p1], [high, p2]]; ENDCASE => ERROR; }; ENDCASE => { cr.dr.impossibleMerges _ CONS[[arrayInstance, path1, path2], cr.dr.impossibleMerges]; }; success _ success; }; MergeFinal: PROC [ct: CellType, path1, path2: Path] = { w1, w2, nw: Wire; w1 _ PathGet[ct, path1, TRUE]; w2 _ PathGet[ct, path2, TRUE]; nw _ MergeNets[w1, w2].merged; }; VAFetch: PROC [va: VertexArray, f, b: NAT] RETURNS [v: Vertex] = { v _ va[f*va.shape[Bar]+b]; }; VAStore: PROC [va: VertexArray, f, b: NAT, v: Vertex] = { va[f*va.shape[Bar]+b] _ v; }; PathGet: PROC [from: CellType, path: Path, mayAdd: BOOL] RETURNS [w: Wire] = { WITH path.first SELECT FROM r: ROPE => { child: Vertex = LookupPart[from, r]; WITH child SELECT FROM x: Wire => w _ x; ci: CellInstance => { childPort: Port = PortGet[ci.type, path.rest, mayAdd]; IF childPort # NIL THEN { w _ FindTransitiveConnection[ci, childPort]; IF w = NIL THEN ERROR; }; }; ENDCASE => ERROR; }; x: REF Range2 => ERROR; ENDCASE => ERROR; }; PortGet: PROC [from: CellType, path: Path, mayAdd: BOOL] RETURNS [port: Port] = { WITH path.first SELECT FROM x: REF Range2 => { ras: REF ArraySpec.array = NARROW[FnVal[$ArraySpec, from.otherPrivate]]; a: Array = from.asArray; eltPort: Port = PortGet[a.eltType, path.rest, mayAdd]; IF eltPort=NIL THEN port _ NIL ELSE { index: ArrayIndex = Int2Sub[Range2Min[x^], Range2Min[ras.range]]; FOR d: Dim IN Dim DO IF x[d].min+1 # x[d].maxPlusOne THEN ERROR ENDLOOP; port _ GetArrayPort[a, index, eltPort]; IF port = NIL AND mayAdd THEN { name: ROPE = FmtIndex[a, index].Concat[Describe[eltPort, a.eltType.port]]; port _ FullyAddPort[[parent: from.port, other: Assert[nameReln, LIST[name], NIL]]].port; SetArrayPort[a, index, eltPort, port]; }; }; }; x: ROPE => { child: Vertex = LookupPart[from, x]; WITH child SELECT FROM w: Wire => IF path.rest # NIL THEN ERROR ELSE { port _ PGet[from, w, NIL, mayAdd].port; }; ci: CellInstance => IF path.rest = NIL THEN ERROR ELSE { childPort: Port = PortGet[ci.type, path.rest, mayAdd]; IF childPort = NIL THEN port _ NIL ELSE { w: Wire = FindTransitiveConnection[ci, childPort]; port _ PGet[from, w, NIL, mayAdd].port; }; }; ENDCASE => ERROR; }; ENDCASE => ERROR; port _ port; }; FmtIndex: PROC [a: Array, index: ArrayIndex] RETURNS [asRope: ROPE] = { asRope _ SELECT TRUE FROM a.size[Foo]=1 => Rope.Cat["[", Convert.RopeFromInt[index[Bar]], "]"], a.size[Bar]=1 => Rope.Cat["[", Convert.RopeFromInt[index[Foo]], "]"], ENDCASE => Rope.Cat["[", Convert.RopeFromInt[index[Foo]], ", ", Convert.RopeFromInt[index[Bar]], "]"]; }; Range2Min: PROC [r2: Range2] RETURNS [min: Int2] = { min _ [Foo: r2[Foo].min, Bar: r2[Bar].min]}; WithTestRA: PROC [x: REF ANY] RETURNS [kind: ROPE] = { WITH x SELECT FROM ci: CellInstance => RETURN ["CellInstance"]; v: Vertex => RETURN ["Vertex"]; ENDCASE => RETURN ["ENDCASE"]; }; WithTestV: PROC [x: Vertex] RETURNS [kind: ROPE] = { WITH x SELECT FROM ci: CellInstance => RETURN ["CellInstance"]; v: Vertex => RETURN ["Vertex"]; ENDCASE => RETURN ["ENDCASE"]; }; PGet: PROC [ct: CellType, internal: Wire, ci: CellInstance, mayAdd: BOOL] RETURNS [port: Port, external: Wire] = { See: PROC [p: Port, v: Vertex] = { IF IsMirror[NARROW[v]] THEN port _ p; }; IF internal.containingCT # ct THEN ERROR; internal.EnumerateTransitiveConnections[See]; IF port = NIL THEN { IF mayAdd THEN { [port, external] _ FullyAddPort[ [parent: ct.port, wire: internal, other: Assert[nameReln, LIST[Describe[internal, ct.asUnorganized.internalWire]], NIL]], ci ]; }; } ELSE IF ci # NIL THEN external _ FindTransitiveConnection[ci, port]; }; GetNet: PROC [s: Source, from: CellType, name: ROPE] RETURNS [w: Wire] = { path: Path _ ParsePath[s, from, name]; w _ PathGet[from, path, TRUE]; }; ReadCap: PROC [s: Source, reader: Reader, cr: CellReading] = { ct: CellType = cr.ct; from: IO.STREAM = s.stream; EndLine[from, cr.dr.buffer]; }; SkipNTokens: PROC [from: IO.STREAM, n: INT, buffer: REFTEXT] RETURNS [ok: BOOL] = { WHILE n > 0 DO token: REFTEXT = from.GetToken[TokenBreak, buffer].token; IF RefText.Equal[token, "\n"] THEN RETURN [FALSE]; n _ n - 1; ENDLOOP; ok _ TRUE; }; Warn: PROC [s: Source, msg: ROPE, v1, v2, v3, v4, v5: IO.Value _ [null[]]] = { IF s.name # NIL THEN msg _ IO.PutFR["At %g[%g]: %g", [rope[s.name]], [integer[s.stream.GetIndex[]]], [rope[msg]]]; msg _ IO.PutFR[msg, v1, v2, v3, v4, v5]; Log["%g", msg]; }; LocalCreateArray: PROC [design: Design, dr: DesignReading, cellTypeName: ROPE, eltType: CellType, size, jointsPeriod: Size2, borders: ARRAY Dim OF ARRAY End OF NAT, otherPublic, otherPrivate: Assertions _ NIL] RETURNS [ct: CellType] = { ct _ CreateArray[design, cellTypeName, fromExtClass, eltType, size, jointsPeriod, borders, otherPublic, otherPrivate]; IF dr.cellTypesByName.SetMapping[cellTypeName, ct] THEN ERROR; }; LocalCreateCellType: PROC [design: Design, dr: DesignReading, cellTypeName: ROPE, internals: BOOL, otherPublic, otherPrivate: Assertions _ NIL] RETURNS [ct: CellType] = { ct _ CreateCellType[design, cellTypeName, fromExtClass, internals, otherPublic, otherPrivate]; IF dr.cellTypesByName.SetMapping[cellTypeName, ct] THEN ERROR; }; LookupPart: PROC [ct: CellType, name: ROPE] RETURNS [v: Vertex] = { pbn: Mapper = NARROW[FnVal[partsByNameKey, ct.otherPrivate]]; v _ NARROW[pbn.Map[name]]; }; CTName: PROC [cellType: CellType] RETURNS [name: ROPE] = { name _ NARROW[FnVal[nameReln, cellType.otherPublic]]; }; PathEquiv: PROC [from: CellType, path1, path2: Path] RETURNS [equiv: BOOL] = { Work: PROC [from: CellType, path1, path2: Path] RETURNS [equiv: BOOL, p1, p2: Port] = { WITH path1.first SELECT FROM r1: ROPE => WITH path2.first SELECT FROM r2: ROPE => { v1: Vertex = LookupPart[from, r1]; v2: Vertex = LookupPart[from, r2]; IF ISTYPE[v1, Wire] # (path1.rest=NIL) OR ISTYPE[v2, Wire] # (path2.rest=NIL) THEN ERROR; IF path1.rest=NIL OR path2.rest=NIL THEN { w1: Wire = WITH v1 SELECT FROM w: Wire => w, ci: CellInstance => PathGet[from, path1, FALSE], ENDCASE => ERROR; w2: Wire = WITH v2 SELECT FROM w: Wire => w, ci: CellInstance => PathGet[from, path2, FALSE], ENDCASE => ERROR; p1 _ PGet[from, w1, NIL, FALSE].port; p2 _ PGet[from, w2, NIL, FALSE].port; equiv _ w1=w2; } ELSE { ci1: CellInstance = NARROW[v1]; ci2: CellInstance = NARROW[v2]; w1, w2: Wire; IF ci1=ci2 THEN { [equiv, p1, p2] _ Work[ci1.type, path1.rest, path2.rest]; IF equiv OR p1=NIL OR p2=NIL THEN RETURN; w1 _ NARROW[FindTransitiveConnection[ci1, p1]]; w2 _ NARROW[FindTransitiveConnection[ci2, p2]]; } ELSE { w1 _ PathGet[from, path1, FALSE]; w2 _ PathGet[from, path2, FALSE]; }; p1 _ PGet[from, w1, NIL, FALSE].port; p2 _ PGet[from, w2, NIL, FALSE].port; equiv _ w1=w2; }; }; x2: REF Range2 => RETURN [FALSE, NIL, NIL]; ENDCASE => ERROR; s1: REF Range2 => WITH path2.first SELECT FROM r2: ROPE => RETURN [FALSE, NIL, NIL]; s2: REF Range2 => { a: Array = from.asArray; IF RangeShape[s1^] # [1, 1] OR RangeShape[s2^] # [1, 1] THEN RETURN [s1^ = s2^, NIL, NIL]; {ep1: Port = PortGet[a.eltType, path1.rest, FALSE]; ep2: Port = PortGet[a.eltType, path1.rest, FALSE]; D: Int2 = Int2Sub[Range2Min[s2^], Range2Min[s1^]]; Try: PROC [d: Dim, lowIndex: ArrayIndex, rp1, rp2: RoledPort] RETURNS [connected: BOOL] = { equiv _ connected _ ArrayEltPortsConnected[a, d, lowIndex, rp1, rp2].really}; p1 _ GetArrayPort[a, Range2Min[s1^], ep1]; p2 _ GetArrayPort[a, Range2Min[s2^], ep2]; SELECT TRUE FROM D = [0, 0] => { FOR d: Dim IN Dim DO FOR side: End IN End DO lowIndex: ArrayIndex _ Range2Min[s1^]; IF side = high THEN lowIndex[d] _ lowIndex[d] - 1; IF Try[d, lowIndex, [side, ep1], [side, ep2]] THEN RETURN; ENDLOOP; ENDLOOP; }; ABS[D[Foo]] + ABS[D[Bar]] = 1 => { d: Dim = IF ABS[D[Foo]] = 1 THEN Foo ELSE Bar; lowPort, highPort: Port; lowS: REF Range2; SELECT D[d] FROM 1 => {lowS _ s1; lowPort _ ep1; highPort _ ep2}; -1 => {lowS _ s2; lowPort _ ep2; highPort _ ep1}; ENDCASE => ERROR; [] _ Try[d, Range2Min[lowS^], [low, lowPort], [high, highPort]]; }; ENDCASE => ERROR nyet; }}; ENDCASE => ERROR; ENDCASE => ERROR; }; IF path1 = NIL OR path2 = NIL THEN ERROR; RETURN [Work[from, path1, path2].equiv]; }; Id: PROC [data: REF ANY] RETURNS [ans: REF ANY] = { ans _ data}; ComparePaths: PROC [k, data: REF ANY] RETURNS [c: Basics.Comparison] = { k1: Path = NARROW[k]; k2: Path = NARROW[data]; c _ PathCompare[k1, k2]}; PathCompare: PROC [path1, path2: Path] RETURNS [c: Basics.Comparison] = { DO IF path1 = path2 THEN RETURN [equal]; IF path1 = NIL THEN RETURN [less]; IF path2 = NIL THEN RETURN [greater]; WITH path1.first SELECT FROM r1: ROPE => WITH path2.first SELECT FROM r2: ROPE => c _ r1.Compare[r2]; x2: REF Range2 => c _ less; ENDCASE => ERROR; s1: REF Range2 => WITH path2.first SELECT FROM r2: ROPE => c _ greater; s2: REF Range2 => IF (c _ IntCompare[s1[Foo].min, s2[Foo].min]) = equal THEN IF (c _ IntCompare[s1[Foo].maxPlusOne, s2[Foo].maxPlusOne]) = equal THEN IF (c _ IntCompare[s1[Bar].min, s2[Bar].min]) = equal THEN c _ IntCompare[s1[Bar].maxPlusOne, s2[Bar].maxPlusOne]; ENDCASE => ERROR; ENDCASE => ERROR; IF c # equal THEN RETURN; path1 _ path1.rest; path2 _ path2.rest; ENDLOOP; }; PathPairEqual: PROC [k1, k2: REF ANY] RETURNS [equal: BOOL] = { pp1: PathPair = NARROW[k1]; pp2: PathPair = NARROW[k2]; equal _ (PathCompare[pp1.p1, pp2.p1] = equal AND PathCompare[pp1.p2, pp2.p2] = equal) OR (PathCompare[pp1.p1, pp2.p2] = equal AND PathCompare[pp1.p2, pp2.p1] = equal); }; HashPathPair: PROC [key: REF ANY] RETURNS [hash: CARDINAL] = { pp: PathPair = NARROW[key]; hash _ HashPath[pp.p1] + HashPath[pp.p2]; }; HashPath: PROC [path: Path] RETURNS [hash: CARDINAL] = { hash _ 0; FOR path _ path, path.rest WHILE path # NIL DO WITH path.first SELECT FROM r: ROPE => hash _ hash + RopeHash.FromRope[r]; x: REF Range2 => hash _ hash + HashInt[x[Foo].min] + HashInt[x[Foo].maxPlusOne] + HashInt[x[Bar].min] + HashInt[x[Bar].maxPlusOne]; ENDCASE => ERROR; ENDLOOP; }; HashInt: PROC [i: INT] RETURNS [hash: CARDINAL] = { ln: Basics.LongNumber = [li[i]]; hash _ ln.lowbits + ln.highbits; }; IntCompare: PROC [i1, i2: INT] RETURNS [c: Basics.Comparison] = { c _ SELECT i1 - i2 FROM >0 => greater, =0 => equal, <0 => less, ENDCASE => ERROR; }; Lg: PROC [i: INT] RETURNS [lg: CARDINAL] = { IF i < 0 THEN ERROR; IF i = 0 THEN RETURN [0]; lg _ 1 + (SELECT i FROM <=00000001H => 00, <=00000002H => 01, <=00000004H => 02, <=00000008H => 03, <=00000010H => 04, <=00000020H => 05, <=00000040H => 06, <=00000080H => 07, <=00000100H => 08, <=00000200H => 09, <=00000400H => 10, <=00000800H => 11, <=00001000H => 12, <=00002000H => 13, <=00004000H => 14, <=00008000H => 15, <=00010000H => 16, <=00020000H => 17, <=00040000H => 18, <=00080000H => 19, <=00100000H => 20, <=00200000H => 21, <=00400000H => 22, <=00800000H => 23, <=01000000H => 24, <=02000000H => 25, <=04000000H => 26, <=08000000H => 27, <=10000000H => 28, <=20000000H => 29, <=40000000H => 30, ENDCASE => 31); }; Register: PROC [keyword: ROPE, read: PROC [s: Source, reader: Reader, cr: CellReading], data: REF ANY _ NIL] = { r: Reader _ NEW [ReaderRep _ [keyword, read, data]]; IF readers.SetMapping[keyword, r] THEN ERROR; }; Start: PROC = { Register["tech", ReadTech]; Register["timestamp", ReadTimestamp]; Register["version", ReadVersion]; Register["scale", ReadScale]; Register["resistclasses", ReadResistClasses]; Register["node", ReadNode]; Register["equiv", ReadEquiv]; Register["fet", ReadFet]; Register["use", ReadUse]; Register["merge", ReadMerge]; Register["cap", ReadCap]; }; Start[]; END. ΦLichenFromExt.Mesa Mike Spreitzer January 11, 1987 3:50:50 pm PST Consumes newline at end of line. name1: ROPE _ GetName[s]; name2: ROPE _ GetName[s]; C: INT _ from.GetInt[]; Register["adjust", ReadAdjust]; Κ;L– "cedar" style˜code™K™.—K˜šΟk ˜ Kšœ œ@˜OKšœ7œœ­˜ό—K˜šΠbx œœ˜Kšœœ˜ Kšœ;œœ˜χK˜Kšœ˜—K˜Kšœœ@˜JK˜Kšœœœœ˜K˜Kšœœ˜$K˜Kšœ œœœ˜"Kšœœœ œœœœœ˜BK˜Kšœœœ ˜šœ œœ˜Kšœ œ˜šœœ.˜8K™ —Kšœœœ˜—K˜Kšœ'œ˜-K˜Kšœœœ˜+šœœ œœ˜+K˜Kšœœ˜ K˜Kšœ˜K˜K˜Kšœœ˜"Kšœ(œ˜,Kšœœ˜Kšœ$˜$Kšœœœ˜Kšœœ˜Kšœœœ˜$Kšœ œœ˜Kšœœ˜Kšœ œ˜—K˜Kšœœœœ˜K˜Kšœœœœ˜4šœœœ˜ Kšœ1˜1—K˜Kšœ œœ˜'šœœœ˜K˜K˜ Kšœœ˜ Kšœ œœ˜Kšœœ˜"KšœΟcœ ˜KšœŸ œ ˜!KšœŸ œ ˜ Kšœœœ˜Kšœ œ˜Kšœ˜!Kšœ˜—K˜Kšœœœœ ˜&K˜Kš œœœœœŸœ˜9K˜Kšœ œœ˜'šœœœ˜Kšœ ˜ Kšœ œ œœ ˜*—K˜Kšœœœ œ˜4K˜šœ œœ˜šœ œ ˜K˜ šœ ˜ K˜K˜ Kšœ˜—Kšœ˜ ——K˜šœ œœ˜Kšœœ˜ Kšœœ˜ K˜—K˜Kšœœœœ˜4K˜Kšœœ ˜K˜Kšœ œœ˜%Kšœœœ˜.K˜K˜JK˜Kšœ œ˜Kšœ œ˜Kšœ œ˜K˜š Οn œœœœœ˜cKš˜š œœ˜Kšœ œ˜Kšœ4˜4—šœ œœ˜Kšœœ˜Kšœœ˜Kšœœ˜4KšœI˜Išœœ˜šœœ˜Kšœ˜Kšœœœ˜.Kšœ˜—Kšœ+˜+Kšœ&œ˜,KšœF˜FKšœ ˜ Kšœ/˜/K˜K•StartOfExpansionΔ[info: ViewerClasses.ViewerRec _ [class: NIL, wx: 0, wy: 0, ww: 0, wh: 0, cx: 0, cy: 0, cw: 0, ch: 0, lock: [process: PROCESS#0B, count: 0B (0)], tipTable: NIL, name: NIL, file: NIL, label: NIL, menu: NIL, icon: 177777B?, column: left, caption: FALSE, scrollable: TRUE, hscrollable: FALSE, iconic: TRUE, border: TRUE, newVersion: FALSE, newFile: FALSE, visible: TRUE, offDeskTop: FALSE, destroyed: FALSE, init: FALSE, saveInProgress: FALSE, inhibitDestroy: FALSE, guardDestroy: FALSE, paintingWedged: FALSE, spare0: FALSE, spare1: FALSE, spare2: FALSE, spare3: FALSE, spare4: FALSE, spare5: FALSE, spare6: FALSE, position: 0, openHeight: 0, link: NIL, parent: NIL, sibling: NIL, child: NIL, props: NIL, data: NIL], paint: BOOL _ TRUE]˜[Kšœœ˜ Kšœœ˜ Kšœ˜ Kšœ˜—šœ˜ KšœD˜DK˜"—K–κ[info: ViewerClasses.ViewerRec _ [class: NIL, wx: 0, wy: 0, ww: 0, wh: 0, cx: 0, cy: 0, cw: 0, ch: 0, lock: [process: PROCESS#0B, count: 0B (0)], tipTable: NIL, name: NIL, file: NIL, label: NIL, menu: NIL, icon: 177777B?, column: left, caption: FALSE, scrollable: TRUE, hscrollable: FALSE, iconic: TRUE, border: TRUE, newVersion: FALSE, newFile: FALSE, visible: TRUE, offDeskTop: FALSE, destroyed: FALSE, init: FALSE, saveInProgress: FALSE, inhibitDestroy: FALSE, guardDestroy: FALSE, paintingWedged: FALSE, spare0: FALSE, spare1: FALSE, spare2: FALSE, spare3: FALSE, spare4: FALSE, spare5: FALSE, spare6: FALSE, position: 0, openHeight: 0, link: NIL, parent: NIL, sibling: NIL, child: NIL, props: NIL, data: NIL], proc: Buttons.ButtonProc, clientData: REF ANY _ NIL, fork: BOOL _ TRUE, font: ImagerFont.Font _ NIL, documentation: REF ANY _ NIL, guarded: BOOL _ FALSE, paint: BOOL _ TRUE]šœ˜˜˜Kšœ„˜„K–B[container: Containers.Container, child: ViewerClasses.Viewer]˜@Kšœ…˜…K–B[container: Containers.Container, child: ViewerClasses.Viewer]˜>K–B[container: Containers.Container, child: ViewerClasses.Viewer]˜>K˜:Kšœœ˜*K˜—šœ˜K˜ K˜—KšœDœ ˜PKšœ˜—K˜š œœ˜$Kšœœ˜Kšœ œ˜šœœ˜"š œœœ˜,Kšœœœ˜šœ œœ˜šœœ˜Kšœ˜Kšœ*˜*Kšœ˜K˜—K˜—šœ˜Kšœœ$˜.šœœœ˜3Kšœ˜K˜Kš œ"œ7œ œœ-˜₯Kšœ3˜3K˜—K˜—K˜—Kšœ1˜1Kšœ ˜ Kšœ˜—K˜—K˜Kšœ*˜*K˜š  œ˜#Kšœœ ˜'š œœœ˜(Kšœœœ˜Kšœ œ ˜Kš œ ˜—K˜ Kšœœ œ œ ˜CK˜—K˜š œœœœ˜=šœ:œœ˜OKšœ ˜ Kšœ~˜~Kšœ˜—K˜—K˜Kšœœ˜6K˜š  œœ#œ œ  œœ˜gK˜Kšœœ˜+Kšœ œ˜Kšœ œ ˜Kšœœ˜/Kšœ œ˜#Kšœœœ˜6š œœœ˜(Kšœœœ˜Kšœ œ˜Kšœ"˜"Kšœ˜Kšœ˜Kšœ)˜)K˜—š œœœ˜'Kšœœœ˜K˜Kšœ%˜%Kšœ˜Kšœ˜Kšœ)˜)K˜—K˜ Kšœœ ˜K˜K˜—K˜š  œœ œœ˜eKšœœ˜Kšœœ˜Kšœœœ˜K˜ Kšœ˜Kš œœ&œ˜:Kšœ2˜2KšœK˜KKšœ3œœœ˜CKšœœ8˜@Kšœ œ#˜1Kšœœ ˜-K˜ Kšœ˜—K˜š  œœ˜(Kšœ œ)˜8Kšœ œ#˜2š œœ˜Kšœœ˜K˜Kšœ˜—Kš œœœœœ˜AKšœœ ˜*K˜K˜—K˜š  œœ9œ˜RKšœœœ ˜K˜š˜Kšœ œ˜K˜Kšœ˜K˜Kšœœœ˜Kšœ œ ˜K˜.Kšœ œ˜&šœ ˜Kšœ˜šœ˜K˜"Kšœœ˜$Kšœ5˜5K˜——Kšœ˜—šœœ˜Kšœœœ˜Kšœ˜K˜—Kšœœœœ˜EKšœœœ˜Kšœœ˜Kšœœ˜š œ  œœœœ˜%Kšœœ˜K˜šœœœ˜!Kšœœœ˜%šœ&˜&Kšœ"˜"—Kšœœœœ˜DKšœ˜Kšœ˜—K˜—Kšœ<˜—K˜š  œœ1˜@K˜Kšœœœ ˜Kšœœ˜Kšœœ˜K˜"Kšœ œ ˜Kšœœœ0˜?Kšœœœœ˜Kšœ˜K˜K˜—K˜š œœ1˜>K˜Kšœœœ ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœ"˜&K˜$K˜$K˜Kšœ˜š œœ œ˜2K˜"Kšœ˜K˜—K˜RKš œ+œœœ'œ˜oKšœ œ˜Kšœ˜Kšœ˜K˜K˜—K˜š œœ+œ˜@KšœX˜XK˜—K˜š œœ œ˜>Kšœœœ ˜K˜K˜Kšœœ˜K˜—K˜š   œœ œ5œœ˜ˆK˜Kšœœ<˜MK˜ Kšœœ˜*šœœœ˜Kš œœœœœœœ˜€š œœœ˜ Kšœ-œ!˜RKšœ|˜|Kšœ˜—K˜ Kšœ6œœœœœœœœ˜«K˜šœœ˜K˜'K˜'Kšœœ˜—Kšœœœœ˜-K˜—K˜ K˜—K˜š œœ˜Kšœ2œ œ˜JKšœ2œ œ˜IKšœ2œ œ˜IK˜—K˜Kš œœœœœœ Ÿœ˜TK˜Kšœ œœ ˜šœ œœ˜Kšœ œ˜Kšœœ˜Kšœœ˜—K˜š  œœœœœœŸœ˜SKšœœ˜Kšœ'˜'Kšœ=œ˜GK˜—K˜š œœ œœœ œŸœ˜YKšœœ˜Kšœœ˜Kšœ)œœœ!˜{K˜—K˜š œœ1˜>K˜Kšœœœ ˜Kšœ œ˜Kšœœ˜Kšœœ&˜.Kšœ˜KšœD˜DKšœ?œœ˜XK˜K˜—K˜š  œœ œœ ˜4Kš œœœœœ˜K˜2šœ œ˜K˜ Kšœ˜"Kšœ˜—šœ˜Kšœ$œ˜?š œœ ˜Kšœœœ˜ K˜Kšœœœ˜ K˜'Kšœœœ˜ Kšœ˜Kšœœœ˜ K˜—Kšœ ˜ Kšœ ˜ Kšœœ œœ˜K˜ Kšœ˜K˜—K˜—K˜š   œœœœœ Ÿœ˜Ošœœ˜Kšœ˜Kšœ ˜—K˜—K˜š   œœœ.œœ˜K˜šœœ˜šœ˜Kšœœ#˜.Kšœœœ)˜9K˜—šœ˜Kšœœ˜#Kš œœœœ œœ˜eKšœ9œœ˜CKšœ"˜"Kšœtœœœ˜±K˜—Kšœœ˜—K˜—K˜š  œœ œ œ˜7Kšœ&œ˜GK˜—K˜š  œœœœ œ˜0Kšœ œœ ˜(—K˜š  œœœœ œ˜4Kšœ œœ˜:—K˜š œœœœ˜1šœœœ˜K˜šœ œ œ˜0Kšœ˜Kšœ˜ Kšœ˜Kšœ˜Kšœ˜ Kšœ˜—Kšœœ˜—K˜—K˜š œœœœ˜3Kšœœœœ˜KK˜—K˜š œœœœ˜0Kšœœ˜šœœœ˜.šœœœ œ˜(Kšœœ˜ Kšœœ œ†˜™Kšœœ˜—Kš œœœœœ˜8Kšœ˜—K˜—K˜š  œœ1˜@K˜Kšœœœ ˜Kšœœœ˜@Kšœœ˜Kšœœ˜K˜&K˜&šœ7œ˜?Kšœ˜Kšœ#˜#K˜—K˜#Kšœ$˜$K˜K˜—K˜š œœ!˜/Kšœ˜Kšœ˜Kšœœ˜š œœœœœœœŸœ˜YKšœ œ˜Kšœ œœ$˜:Kšœ˜K˜—Kšœ)˜)Kšœ˜Kšœœ˜K˜—K˜š  œœ%œœ˜OKš œœœœœ ˜Kšœœ˜š œœœœ˜Kšœ œ˜Kšœœœ œ˜,K˜ —š œœœ˜%K˜)šœ˜˜Kšœœœ˜ K˜K˜—Kšœ œ˜Kšœœ˜—K˜—šœœ ˜Kšœœ(˜2Kšœœœ'˜Hšœœ˜Kšœœ˜˜Kšœœ4˜>Kšœœ4˜>K˜Kšœœœ˜Kš œœœœœ˜!Kšœœœ˜K˜—K˜š  œœ'œ œ˜QK˜Kšœœœœ˜šœœœœœœœ œ˜]šœœœ œ˜'šœœœ œ˜Kšœœ˜-šœœœ˜Kšœœœ˜#Kš œœ;œœœ7˜˜Kšœ˜K˜—K˜—Kšœœ œ˜Kšœœ˜—Kšœœ œ˜Kšœœ˜—K˜Kšœ œ˜K˜—K˜KšœœœŸF˜YKšœœœŸ'˜;K˜š   œœ>œœ œ˜ƒK˜#K˜Kšœœœ&˜GK˜Kšœœ œ˜&Kšœœ œ˜&Kšœ˜Kšœ˜Kšœ˜K˜9K˜"K˜"KšΠgnœ/˜0Kšœ#œ˜)Kšœ#œ˜)Kšœœœ˜$Kšœ œ˜šœœ˜šΟgœ œœ'œ˜AKš œœœœŸ8œ˜ošœœ˜šœ œ˜K˜Kšœ œ˜šœ˜Kšœ œ&˜IKšœ4˜4Kšœœ˜—šœœœ˜$Kšœœœ'˜OKšœ-œœ˜OKšœœ˜—šœœ œœ˜0Kšœ>˜>Kšœ œ˜—Kšœ˜—Kšœ˜—Kšœ˜—š œ’œ œœ’œ ˜$Kšœ œ˜šœœ˜K˜K˜K˜K˜)Kš‘Πdnœœ’œ˜šœ’œ˜K˜$KšœC’Οdœ’€œ˜MKšœœ˜—Kšœ*˜*šœ œœ˜K˜'šœ’€œ˜K˜$K˜MKšœœ˜—K˜=Kšœ œ˜Kšœ˜K˜—K˜,šœ œœ˜K˜K˜(šœ’€œ˜K˜LK˜#Kšœœ˜—K˜=Kšœ œ˜Kšœ˜K˜—Kšœ˜—K˜—šœ’œ œ’œ˜"šœœ˜Kš’œE˜FKš’œE˜FKš’œE˜FKš’œE˜FKšœœ˜—K˜—šœ˜ Kšœœ8˜UKšœ˜——Kšœ˜K˜—K˜š  œœ'˜7K˜Kšœœ˜Kšœœ˜Kšœ˜K˜—K˜š œœœœ˜BK˜K˜—K˜š œœœ˜9Kšœ˜K˜—K˜š œœ&œœ˜Nšœ œ˜šœœ˜ K˜$šœœ˜K˜˜K˜6šœ œœ˜Kšœ,˜,Kšœœœœ˜K˜—K˜—Kšœœ˜—K˜—Kšœœ œ˜Kšœœ˜—K˜—K˜š œœ&œœ˜Qšœ œ˜šœœ ˜Kšœœœ'˜HK˜K˜6š œ œœœœ˜%K˜AKšœœœœœœœ˜HK˜'šœœœœ˜Kšœœ@˜JKšœ@œœ ˜XK˜&K˜—K˜—K˜—šœœ˜ Kšœ$˜$šœœ˜š œ œ œœœœ˜/Kšœœ˜'K˜—š œœ œœœœ˜8Kšœ6˜6š œ œœœœ˜)Kšœ2˜2Kšœœ˜'K˜—K˜—Kšœœ˜—K˜—Kšœœ˜—K˜ K˜—K˜š œœœ œ˜Gšœ œœ˜K˜EK˜EKšœ_˜f—K˜—K˜š  œœœ˜4K˜,—K˜š   œœœœœœ˜6šœœ˜Kšœœ˜,Kšœ œ ˜Kšœœ ˜—K˜—K˜š  œœ œœ˜4šœœ˜Kšœœ˜,Kšœ œ ˜Kšœœ ˜—K˜—K˜š œœ:œœ!˜rš œœ˜"Kšœ œœ ˜%K˜—Kšœœœ˜)Kšœ-˜-šœœœ˜šœœ˜šœ ˜ Kšœ:œ5œ˜yK˜Kšœ˜—K˜—Kšœ˜—Kšœœœœ/˜DK˜—K˜š œœ#œœ˜JK˜&Kšœœ˜K˜—K˜š œœ1˜>K˜Kšœœœ ˜Kšœœ™Kšœœ™Kšœœ™K˜K˜—K˜š  œœœœœ œœœ˜Sšœ˜Kšœœ+˜9Kšœœœœ˜2K˜ Kšœ˜—Kšœœ˜ K˜—K˜š œœœœ˜NKšœ œœœU˜rKšœœ ˜(K˜K˜—K˜š œœ3œ9œœœœœ*œœ˜μKšœv˜vKšœ1œœ˜>K˜—K˜š  œœ3œ œ*œœ˜ͺKšœ^˜^Kšœ1œœ˜>K˜—K˜š  œœœœ˜CKšœœ)˜=Kšœœ˜K˜—K˜š œœœœ˜:Kšœœ(˜5K˜—K˜š  œœ&œ œ˜Nš œœ&œ œ˜Wšœ œ˜šœœœ œ˜(šœœ˜ K˜"K˜"Kšœœœœœœœœ˜Yš œ œœ œœ˜*šœ œœ˜Kšœ ˜ Kšœ)œ˜0Kšœœ˜—šœ œœ˜K˜ Kšœ)œ˜0Kšœœ˜—Kšœœœ˜%Kšœœœ˜%K˜K˜—šœ˜Kšœœ˜Kšœœ˜K˜ šœ œ˜K˜9Kšœœœœœœœ˜)Kšœœ$˜/Kšœœ$˜/K˜—šœ˜Kšœœ˜!Kšœœ˜!K˜—Kšœœœ˜%Kšœœœ˜%K˜K˜—Kšœ˜—Kš œœ œœœœ˜+Kšœœ˜—šœœ œ œ˜.Kš œœœœœœ˜%šœœ ˜K˜Kš œœœœ œœ˜ZKšœ,œ˜3Kšœ+œ˜2Kš‘œ1˜2š œœ5œ œ˜[KšœM˜M—Kšœ*˜*Kšœ*˜*šœœ˜š’œ˜šœœ˜šœ œ˜K˜&Kšœ œ˜2Kšœ,œœ˜:Kšœ˜—Kšœ˜—K˜—šœ’œ œ’œ˜"Kš œ œœ’œ œœ˜.K˜Kšœœ˜šœ’œ˜Kšœ1˜1Kšœ1˜1Kšœœ˜—Kšœ@˜@K˜—Kšœœ˜—K˜—Kšœœ˜—Kšœœ˜—K˜—Kš œ œœ œœœ˜)Kšœ"˜(K˜—K˜š œœœœœœœ˜3K˜ —K˜š   œœ œœœ˜HKšœ œ˜Kšœ œ˜Kšœ˜—K˜š  œœœ˜Iš˜Kšœœœ ˜%Kšœ œœœ˜"Kšœ œœœ ˜%šœ œ˜šœœœ œ˜(Kšœœ˜Kšœœ˜Kšœœ˜—šœœ œ œ˜.Kšœœ˜šœœ ˜Kšœ4˜:KšœB˜HKšœ4˜:K˜7—Kšœœ˜—Kšœœ˜—Kšœ œœ˜K˜K˜Kšœ˜—K˜—K˜š   œœ œœœ œ˜?Kšœœ˜Kšœœ˜Kšœ-œ&œ&œ&˜§K˜—K˜š   œœœœœœ˜>Kšœœ˜K˜)K˜—K˜š œœœœ˜8K˜ šœœœ˜.šœ œ˜Kšœœ'˜.Kšœœ}˜ƒKšœœ˜—Kšœ˜—K˜—K˜š  œœœœœ˜3K˜ Kšœ ˜ K˜—K˜š  œœ œœ˜Ašœœ ˜K˜K˜ K˜ Kšœœ˜—K˜—K˜š  œœœœœ˜,Kšœœœ˜Kšœœœ˜šœ œ˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜K˜Kšœ˜—K˜—K˜š œœ œœ5œœœ˜pKšœ œ%˜4Kšœ œœ˜-Kšœ˜—K˜š œœ˜K˜Kšœ%˜%Kšœ!˜!K˜Kšœ-˜-K˜K˜Kšœ˜K˜K˜K™Kšœ˜Kšœ˜—K˜K˜K˜Kšœ˜—…—(ΩJ