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;
<<we should no longer have to do this:
RetractFalseHypotheses[childType];
Instead we only check:>> 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˜—K˜�š œœ˜/š˜Kšœ
œœ˜Kšœ%˜%Kšœœ˜šœœœ˜*Kšœ5œ
˜AKšœ˜—Kšœœ
œœ˜Kšœ˜—Kšœœœœ˜%K˜—K˜�š œœœ˜(Kšœœœ˜Kšœ	œœœ˜)—K˜�š œœœ
œ˜=Kšœ	œ˜
šœœ	œ˜2Kšœœ˜Kšœœ˜Kšœœ˜/Kšœ	œbœ!˜Kšœ˜—K˜K˜—K˜�š
 œœœœœ˜?Kšœœ˜šœœ˜Kšœœ˜šœ˜Kš	œœœœœ˜5Kšœœœœ˜5šœ˜Kšœœœ˜$Kšœœœœ˜6K˜
——Kšœ˜—K˜—K˜�š 
œœœœ
œœ˜lKšœœ˜,šœœœ˜=šœœ˜Kšœ<˜<Kšœ3˜3Kšœ?˜?Kšœ6˜6Kšœ˜Kšœ2˜2Kšœ˜—Kšœœ˜,K˜—K˜—K˜�š œœ
œœ˜2Kšœœœ˜K˜šœ˜K˜#Kšœ/˜6—K˜—K˜�š
 
œœœœœ
˜:šœœ˜K˜
Kšœ˜Kšœ˜——K˜�š
 œœœœ
œ˜4Kšœœœ˜3K˜—K˜�š œœ1˜?K˜Kšœœœ˜Kšœ
œ˜Kšœœ˜?K˜K˜—K˜�š 
œœ1˜DK˜Kšœœœ˜Kšœ
œ˜Kšœœ*˜=Kšœ!œœ
œ˜kK˜K˜šœœ˜,Kšœ˜K˜K˜K˜K˜
K˜
KšœŸœ˜K˜
——K˜�š œœ1˜BK˜Kšœœœ˜Kšœ	œ˜Kšœ	œ˜)Kšœ*œ$˜RK˜Kšœ˜—K˜�š 	œœ1˜@K˜Kšœœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ)˜BKšœœ˜Kšœ˜Kšœ˜Kšœ˜Kšœ"œœœ&˜UK˜Kšœ˜—K˜�š œœ1˜HK˜Kšœœœ˜Kšœœ˜Kšœ!œ1˜Xš˜Kšœœ'˜2Kšœœœ˜K˜
Kšœ˜—Kšœ˜Kšœ˜—K˜�Kšœœœ˜K˜�š œœ1˜?K˜Kšœœœ˜Kšœ
œ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ7˜?Kšœ!˜!Kšœœ˜,šœœœ˜˜K˜Kšœœœ˜,K˜—K˜—šœ˜K˜—šœœ%˜6Kšœœœœ˜)Kšœœœœ˜)Kšœœœœœœ˜AKšœ˜Kšœ˜—K˜K˜—K˜�š
 	œœœœœ!˜[Kšœœœ˜Kšœœœ˜Kšœœ˜šœ	œ˜K˜šœœ˜Kšœœœ˜"Kšœ˜—K˜—šœ˜Kšœœœ˜Kšœ$œœ˜BKšœœœ˜7Kšœœœœ˜)K˜Kšœœœ0œ˜dKšœœœ œ˜NKšœ$œœ˜BKšœœœœ˜(Kšœ˜—š˜šœ˜š	œœ
œœ˜0Kšœ˜Kšœ˜—Kšœ˜——K˜—K˜�š
 	œœœœœ
˜9Kšœœœœ
˜>—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šœœ˜˜Kšœœ˜Kšœ˜Kšœ,˜,Kšœœœœ˜"K˜—Kšœœ˜—Kšœœœœ˜%Kšœœ˜K˜K˜—Kšœœ˜Kšœœ˜Kšœœ˜šœ˜K˜
šœœ˜'K˜#Kšœœœœœ	œ˜6Kšœœ˜—K˜——Kšœ˜—K˜K˜—K˜�š 
œœœœœŸœ˜Pšœœ˜Kšœ˜Kšœ
˜—K˜—K˜�š œœœœ˜HKšœœ%˜>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��