DIRECTORY
CoreFlat,
CoreOps,
HashTable,
Histograms,
Icons,
IO,
LinearSystem,
Mint,
PlotGraph,
Real,
Rope,
Schedule,
TerminalIO,
TypeScript,
ViewerIO;

MintImpl: CEDAR PROGRAM
IMPORTS
CoreFlat,
CoreOps,
HashTable,
Histograms,
Icons,
IO,
LinearSystem,
PlotGraph,
Schedule,
TerminalIO,
TypeScript,
ViewerIO

EXPORTS
Mint
SHARES
Schedule
~ BEGIN OPEN Mint;
PathList: TYPE = LIST OF NodeList;
Caminos: TYPE = LIST OF Camino;
Camino: TYPE = LIST OF RECORD[set: Set, node: Node];
Delay: TYPE = REF DelayRec;
DelayRec: TYPE = RECORD [tup, tdown: ps];
ExtendedCircuit: TYPE = REF ExtendedCircuitRec;
ExtendedCircuitRec: TYPE = RECORD [circuit: Circuit, data: REF ANY];

gndName: Rope.ROPE _ "public.Gnd";
vddName: Rope.ROPE _ "public.Vdd";
VddVal: mVolt _ 5000.0;
GndVal: mVolt _ 0.0;
VtVal: mVolt _ 2000.0;
threshold: mVolt _ 2500.0;
gamma: REAL = 0.4*31.62277;	-- 31.62277 = SqRt[1000.0] due to unit = mV
phi: mVolt _ 1000.0;
sqrtPhi: REAL _  31.62277;
timeSpecs: ps _ 50.0;
tInc: ps _ 100.0;

totalSets: CARDINAL _ 0;
separateView: BOOL _ FALSE;
StdOut: PUBLIC IO.STREAM _ NIL;
debug: PUBLIC BOOL _ FALSE;
verbose: PUBLIC BOOL _ FALSE;
debugCircuit: Circuit _ NIL;
debugNode: Node _ NIL;
debugNodeList: NodeList _ NIL;
debugHList: LIST OF Schedule.History _ NIL;
statsNumberOfSimulations: CARDINAL _ 0;
nSimInc: CARDINAL _ 50;

SimulateSet: PROC [ref: REF ANY, t: ps, data: REF ANY] ~ {
circuit: Circuit ~ NARROW[data];
set: Set ~ NARROW[ref];
agenda: Schedule.Agenda ~ circuit.agenda;
subsets: SetList;
nodeList: NodeList;
finished, firstTime: BOOLEAN _ FALSE;
inputHistories: LIST OF Schedule.History;
timeList: LIST OF ps;
tmin, tmax, tmaxmin, tmaxmax: ps;
IF set=NIL THEN RETURN;
IF set.done THEN RETURN;
set.done _ TRUE;
IF debug THEN {IO.PutF[StdOut, "\n\n%5d ~~~>",IO.real[t]]; PrintSetType[set.type]; IO.PutF[StdOut, "\nInputs :"];};
nodeList _ ScanHistoriesOfNodeList[set.inList, t, agenda, FALSE];
FOR inode: NodeList _ nodeList, inode.rest UNTIL inode=NIL DO
IF verbose THEN IO.PutF[StdOut,"\n Unknown Input: %g ", IO.rope[RopeFromNode[inode.first, circuit]]]
ENDLOOP;
nodeList _ ScanHistoriesOfNodeList[set.lNodes, t, agenda];
firstTime _ nodeList#NIL;
FOR inode: NodeList _ set.inList, inode.rest UNTIL inode=NIL DO
IF debug THEN PrintNode[inode.first, circuit];
inputHistories _ CONS[inode.first.history, inputHistories];
ENDLOOP;
FOR inode: NodeList _ set.fixedV, inode.rest UNTIL inode=NIL DO
IF ~inode.first.input THEN ResetInput[inode.first, set]
ENDLOOP;
FOR inode: NodeList _ set.lNodes, inode.rest UNTIL inode=NIL DO
IF inode.first.input THEN SetInput[inode.first, set]
ENDLOOP;
IF debug THEN IO.PutF[StdOut, "\nOutputs:"];
timeList _ Schedule.Schedule[inputHistories];
tmin _ t;
UNTIL timeList.first>=tmin OR timeList.rest=NIL DO
timeList _ timeList.rest;
ENDLOOP;
tmaxmin _ timeList.first;
IF set.type.solve#NIL THEN tmaxmin _ set.type.solve[set, tmin, circuit]
ELSE UNTIL finished DO
[finished , tmin] _ FlipSwitches[set.lFets, tmin, tmaxmin];
IF firstTime THEN finished _ FALSE;
firstTime _ FALSE;
IF ~finished THEN {
tmaxmax _ tmaxmin;
subsets _ ClipSet[set];
FOR isubset: SetList _ subsets, isubset.rest UNTIL isubset=NIL DO
tmax _ RCSolve[isubset.first, tmin, circuit];
tmaxmin _ MIN[tmaxmin, tmax];
tmaxmax _ MAX[tmaxmax, tmax];
IF debug AND verbose THEN {
IO.PutF[StdOut, "\nsubset between %g and %g", IO.real[tmin], IO.real[tmax]];
PrintNodeList[isubset.first.lNodes, circuit];
};
ENDLOOP;
KillSetList[subsets, TRUE];
}
ELSE {
tmin _ tmaxmin;
UNTIL timeList.first>tmin OR timeList.rest=NIL DO
timeList _ timeList.rest;
ENDLOOP;
IF MAX[tmaxmax, timeList.first]> tmaxmin THEN finished _ FALSE;
tmaxmin _ IF timeList.first>tmaxmin THEN timeList.first ELSE tmaxmax;
};
ENDLOOP;
FOR inode: NodeList _ set.lNodes, inode.rest UNTIL inode=NIL DO
FOR setList: SetList _ inode.first.setList, setList.rest UNTIL setList=NIL DO
Schedule.InsertInAgenda[agenda, setList.first, Schedule.NextTimeOfHistory[inode.first.history, t+1]];
setList.first.done _ FALSE;
ENDLOOP;
ENDLOOP;
circuit.info.nbOfSimulations _ circuit.info.nbOfSimulations+1;
IF verbose THEN IF (circuit.info.nbOfSimulations MOD nSimInc) = 0 THEN
IO.PutF[StdOut, "\n%5d   t : %5d,  No of events : %7d", IO.int[circuit.info.nbOfSimulations], IO.real[agenda.list.first.t], IO.int[agenda.nbOfEvents]];
};

ScanHistoriesOfNodeList: PROC [nodeList: NodeList, t: ps, agenda: Schedule.Agenda, cut: BOOLEAN _ TRUE] RETURNS [uninitialized: NodeList] ~ {
CutHistory: PROC[node: Node] RETURNS [quit: BOOLEAN _ FALSE] ~ {
IF node.history=NIL THEN RETURN;
IF node.input THEN RETURN;
t1 _ Schedule.NextTimeOfHistory[node.history, t+tInc];
IF t1>t AND Schedule.LastTimeOfHistory[node.history]>t1 THEN {
node.history _ Schedule.AddToHistory[node.history, t1, Schedule.VFromHistory[node.history, t1]];
FOR iSetList: SetList _ node.setList, iSetList.rest UNTIL iSetList=NIL DO
Schedule.InsertInAgenda[agenda, iSetList.first, t1];
iSetList.first.done _ FALSE;
FOR inodeList: NodeList _ iSetList.first.lNodes, inodeList.rest UNTIL inodeList=NIL DO
quit _ CutHistory[inodeList.first];
ENDLOOP;
ENDLOOP;
};
};
t1: ps _ t+tInc;
FOR inode: NodeList _ nodeList, inode.rest UNTIL inode=NIL DO
IF inode.first.history=NIL THEN {
uninitialized _ CONS[inode.first, uninitialized];
inode.first.history _ Schedule.CreateHistory[t, GndVal+0.0314159];
}
ELSE {
IF ~inode.first.watched THEN inode.first.history _ Schedule.ForgetBeginings[inode.first.history, t];
IF cut THEN [] _ CutHistory[inode.first];
};
ENDLOOP;
};

FindInputs2: PROC [set: Set] ~ {
found: BOOLEAN;
FOR ifet: FetList _ set.lFets, ifet.rest UNTIL ifet=NIL DO
found _ FALSE;
FOR inode: NodeList _ set.lNodes, inode.rest UNTIL inode=NIL DO
IF inode.first=ifet.first.gate THEN {
found _ TRUE;
EXIT;
};
ENDLOOP;
IF ~found THEN {
found _ FALSE;
FOR inode: NodeList _ set.inList, inode.rest UNTIL inode=NIL DO
IF inode.first=ifet.first.gate THEN {
found _ TRUE;
EXIT;
};
ENDLOOP;
IF ~found THEN set.inList _ CONS[ifet.first.gate, set.inList];
};
ENDLOOP;
};

NextFetList2: PROC [set: Set, nodeList: NodeList] RETURNS [fetList: FetList _ NIL] ~ {
FOR iNodeList: NodeList _ nodeList, iNodeList.rest UNTIL iNodeList = NIL DO
FOR iFetList: FetList _ iNodeList.first.fetList, iFetList.rest UNTIL iFetList = NIL DO
IF NOT iFetList.first.done THEN 
IF iFetList.first.gate # iNodeList.first THEN {
fetList _ CONS[iFetList.first, fetList];
set.lFets _ CONS[iFetList.first, set.lFets];
iFetList.first.done _ TRUE;
};
ENDLOOP;
ENDLOOP;
};	--NextFetList2


NextNodeList2: PROC [set: Set, fetList: FetList] RETURNS [nodeList: NodeList _ NIL] ~ {
FOR iFetList: FetList _ fetList, iFetList.rest UNTIL iFetList = NIL DO
IF NOT iFetList.first.ch1.done THEN {
nodeList _ CONS[iFetList.first.ch1, nodeList];
set.lNodes _ CONS[iFetList.first.ch1, set.lNodes];
iFetList.first.ch1.done _ TRUE;
};
IF NOT iFetList.first.ch2.done THEN {
nodeList _ CONS[iFetList.first.ch2, nodeList];
set.lNodes _ CONS[iFetList.first.ch2, set.lNodes];
iFetList.first.ch2.done _ TRUE;
};
ENDLOOP;
};	--NextNodeList2

ClipSet: PROC [totalSet: Set, cutNodes: NodeList _ NIL] RETURNS [setList: SetList _ NIL] ~ {
set: Set;
nodeList: NodeList;
fetList: FetList;
FOR iFetList: FetList _ totalSet.lFets, iFetList.rest UNTIL iFetList = NIL DO
iFetList.first.done _ ~iFetList.first.switch;
ENDLOOP;
FOR iNodeList: NodeList _ totalSet.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO
iNodeList.first.done _ FALSE;
ENDLOOP;
FOR iNodeList: NodeList _ totalSet.fixedV, iNodeList.rest UNTIL iNodeList=NIL DO
iNodeList.first.done _ TRUE;
ENDLOOP;
FOR iNodeList: NodeList _ cutNodes, iNodeList.rest UNTIL iNodeList=NIL DO
iNodeList.first.done _ TRUE;
ENDLOOP;
FOR inodeList: NodeList _ totalSet.lNodes, inodeList.rest UNTIL inodeList=NIL DO
IF inodeList.first.done THEN LOOP;
set _ NEW[SetRec];
nodeList _ LIST[inodeList.first];
set.lNodes _ nodeList;
inodeList.first.done _ TRUE;
UNTIL nodeList=NIL DO
fetList _ NextFetList2[set, nodeList];
nodeList _ NextNodeList2[set, fetList];
ENDLOOP;
FOR iNodeList: NodeList _ totalSet.fixedV, iNodeList.rest UNTIL iNodeList=NIL DO
iNodeList.first.done _ FALSE;
ENDLOOP;
FOR iFetList: FetList _ set.lFets, iFetList.rest UNTIL iFetList=NIL DO
IF iFetList.first.ch1.input THEN IF ~iFetList.first.ch1.done AND iFetList.first.switch THEN {
iFetList.first.ch1.done _ TRUE;
set.fixedV _ CONS[iFetList.first.ch1, set.fixedV];
};
IF iFetList.first.ch2.input THEN IF ~iFetList.first.ch2.done AND iFetList.first.switch  THEN {
iFetList.first.ch2.done _ TRUE;
set.fixedV _ CONS[iFetList.first.ch2, set.fixedV];
};
ENDLOOP;
FOR iNodeList: NodeList _ totalSet.fixedV, iNodeList.rest UNTIL iNodeList=NIL DO
iNodeList.first.done _ TRUE;
ENDLOOP;
setList _ CONS[set, setList]; 
ENDLOOP;
};

CheckSwitch: PROC [fet: Fet, t0, t1: ps] RETURNS [change: BOOLEAN _ FALSE, changet: ps _ 0] ~ {
status: BOOLEAN;
vGate1: mVolt _ Schedule.VFromHistory[fet.gate.history, t1];
SELECT fet.type FROM
nE => status _ (vGate1 >= threshold);
pE => status _ (vGate1 <= threshold);
ENDCASE => ERROR;
IF status#fet.switch THEN {
UNTIL t1-t0<timeSpecs DO
ti: ps _ (t0 + t1)/2;
IF CheckSwitch[fet, t0, ti].change THEN t1 _ ti
ELSE t0 _ ti;
ENDLOOP; 
change _ TRUE;
changet _ t1;
};
};

FlipSwitches: PROC [fetList: FetList, t0, t1: ps] RETURNS [finished: BOOLEAN _ TRUE, newt: ps] ~ {
t: ps;
change: BOOLEAN;
previousFets: FetList _ NIL;
newt _ t1;
FOR iFetList: FetList _ fetList, iFetList.rest UNTIL iFetList=NIL DO
[change, t] _ CheckSwitch[iFetList.first, t0, t1];
IF change THEN {
iFetList.first.switch _ ~iFetList.first.switch;
finished _ FALSE;
IF t<newt-timeSpecs THEN {
FOR iprevList: FetList _ previousFets, iprevList.rest UNTIL iprevList=NIL DO
iprevList.first.switch _ ~iprevList.first.switch;
ENDLOOP;
previousFets _ NIL;
t1 _ t;
newt _ t;
};
previousFets _ CONS[iFetList.first, previousFets];
};
ENDLOOP;
};

ResetSets: PROC [set: Set] RETURNS [ok: BOOLEAN]~ {
set.done _ FALSE;
FOR iFetList: FetList _ set.lFets, iFetList.rest UNTIL iFetList=NIL DO
iFetList.first.switch _ TRUE;
ENDLOOP;
RETURN[TRUE];
};

Coeff: PROC [matrix: LinearSystem.MatrixN, fet: Fet] ~ {
i1, i2: CARDINAL;
i1 _ fet.ch1.index;
i2 _ fet.ch2.index;
matrix[i1][i1] _ matrix[i1][i1] - fet.rOnInv/fet.ch1.cap;
matrix[i1][i2] _ matrix[i1][i2] + fet.rOnInv/fet.ch1.cap;
matrix[i2][i2] _ matrix[i2][i2] - fet.rOnInv/fet.ch2.cap;
matrix[i2][i1] _ matrix[i2][i1] + fet.rOnInv/fet.ch2.cap;
fet.done _ TRUE;
};

XchIndexes: PROC [matrix: LinearSystem.MatrixN, i, j: CARDINAL] RETURNS [xChged: LinearSystem.MatrixN] ~ {
rowN: LinearSystem.RowN;
elt: REAL;
xChged _ LinearSystem.Copy[matrix];
rowN _ xChged[i];
xChged[i] _ xChged[j];
xChged[j] _ rowN;
FOR k: INTEGER IN [0..xChged.nrows) DO
elt _ xChged[k][i];
xChged[k][i] _ xChged[k][j];
xChged[k][j] _ elt;
ENDLOOP;
};

RCSolve: PROC [set: Set, t: ps, circuit: Circuit] RETURNS [nextTime: ps] ~ {
matrix, subMatrix: LinearSystem.MatrixN;
cstTerm, vInit, vFinal, tau, vFinal2: LinearSystem.ColumnN;
tFinal: ps _ 0.0;
vTemp: REAL _ VddVal/2.0;
triState: BOOLEAN _ TRUE;
nNodes, nActiveNodes, lastAct: CARDINAL _ 0;
IF set.fixedV=NIL THEN RETURN[t]; -- this cell is isolated (triState).
FOR iNodeList: NodeList _ set.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO
iNodeList.first.index _ nNodes;
nNodes _ nNodes+1;
ENDLOOP;
nActiveNodes _ nNodes;
FOR iNodeList: NodeList _ set.fixedV, iNodeList.rest UNTIL iNodeList=NIL DO
iNodeList.first.index _ nNodes;
nNodes _ nNodes+1;
ENDLOOP;
matrix _ NEW[LinearSystem.MatrixSeq[nNodes]];
cstTerm _ NEW[LinearSystem.VecSeq[nNodes]];
vInit _ NEW[LinearSystem.VecSeq[nNodes]];
tau _ NEW[LinearSystem.VecSeq[nNodes]];
FOR iNodeList: NodeList _ set.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO
vInit[iNodeList.first.index] _ Schedule.VFromHistory[iNodeList.first.history, t];
ENDLOOP;
FOR iNodeList: NodeList _ set.fixedV, iNodeList.rest UNTIL iNodeList=NIL DO
vInit[iNodeList.first.index] _ Schedule.VFromHistory[iNodeList.first.history, t];
ENDLOOP;
FOR iFetList: FetList _ set.lFets, iFetList.rest UNTIL iFetList = NIL DO
iFetList.first.done _ FALSE;
ENDLOOP;
FOR iRow: CARDINAL IN [0..nNodes) DO
matrix[iRow] _ NEW[LinearSystem.VecSeq[nNodes]];
cstTerm[iRow] _ 0.0;
FOR iColumn: CARDINAL IN [0..nNodes) DO
matrix[iRow][iColumn] _ 0.0;
ENDLOOP;
ENDLOOP;
FOR iFetList: FetList _ set.lFets, iFetList.rest UNTIL iFetList = NIL DO
IF ~iFetList.first.done AND iFetList.first.switch THEN Coeff[matrix, iFetList.first];
ENDLOOP;
FOR i: CARDINAL IN [nActiveNodes..nNodes) DO
IF matrix[i][i]#0.0 THEN {
triState _ FALSE;
EXIT;
};
ENDLOOP;
IF triState THEN ERROR;
FOR i: CARDINAL IN [0..nActiveNodes) DO
FOR j: CARDINAL IN [nActiveNodes..nNodes) DO
cstTerm[i] _ cstTerm[i] - matrix[i][j]*vInit[j];
ENDLOOP;
ENDLOOP;
FOR iNodeList: NodeList _ set.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO
iNodeList.first.history _ Schedule.AddToHistory[iNodeList.first.history, t, vInit[iNodeList.first.index]];
ENDLOOP;
IF nNodes=nActiveNodes+1 THEN {
vFinal _ NEW[LinearSystem.VecSeq[nActiveNodes]];
FOR i: CARDINAL IN [0..nActiveNodes) DO
vFinal[i] _ vInit[nActiveNodes];
ENDLOOP;
}
ELSE vFinal _ LinearSystem.SolveN[LinearSystem.Copy[matrix], cstTerm, nActiveNodes];
lastAct _ nActiveNodes-1;
IF lastAct#0 THEN FOR i: CARDINAL IN [0..nActiveNodes) DO
subMatrix _ XchIndexes[matrix, i, lastAct];
vInit[lastAct] _ vTemp; --this node becomes a V source
FOR k: CARDINAL IN [0..lastAct) DO
cstTerm[k] _ 0.0;
FOR j: CARDINAL IN [lastAct..nNodes) DO
cstTerm[k] _ cstTerm[k] - subMatrix[k][j]*vInit[j];
ENDLOOP;
ENDLOOP;
vFinal2 _ LinearSystem.SolveN[LinearSystem.Copy[subMatrix], cstTerm, lastAct];
cstTerm[lastAct] _ 0.0;
FOR j: CARDINAL IN [0..lastAct) DO
cstTerm[lastAct] _ cstTerm[lastAct] + subMatrix[lastAct][j]*(vTemp-vFinal2[j]);
ENDLOOP;
FOR j: CARDINAL IN (lastAct..nNodes) DO
cstTerm[lastAct] _ cstTerm[lastAct] + subMatrix[lastAct][j]*(vTemp-vInit[j]);
ENDLOOP;
tau[i] _ (vTemp-vFinal[i])/cstTerm[lastAct];
tFinal _ tFinal+tau[i];
ENDLOOP
ELSE { tau[0] _ -1.0/matrix[0][0]; tFinal _ tau[0] };
nextTime _ t+tFinal;
FOR iNodeList: NodeList _ set.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO
iNodeList.first.history _ Schedule.AddToHistory[iNodeList.first.history, nextTime, vFinal[iNodeList.first.index]];
IF debug THEN IO.PutF[StdOut,"%g : %dmV @ %dps\n", IO.rope[RopeFromNode[iNodeList.first, circuit]], IO.real[vFinal[iNodeList.first.index]], IO.real[nextTime]];
ENDLOOP;
};

Initialize: PROC [circuit: Circuit] RETURNS [t: ps _ 1e30] ~ {
SetEvent: HashTable.EachPairAction ~ {
node: Node _ NARROW[value];
t0: ps;
IF ~node.input THEN RETURN;
t0 _ Schedule.FirstTimeOfHistory[node.history];
t _ MIN[t, t0];
FOR setList: SetList _ node.setList, setList.rest UNTIL setList=NIL DO
Schedule.InsertInAgenda[circuit.agenda, setList.first, t0];
ENDLOOP;
};
Schedule.InsertInAgenda[circuit.agenda, NIL, -1e30];
[] _ HashTable.Pairs[circuit.nodeTable, SetEvent];
[] _ VisitLibrary[circuit.library, ResetSets];
SetTime[t, circuit];
};

CircuitSimulate: PUBLIC PROC[circuit: Circuit, from: ps _ 0.0] RETURNS [t: ps] ~{
gndNode, vddNode: Node;
lastNodes: NodeList;
circuit.info.nbOfSimulations _ 0;
gndNode _ NodeFromRope[gndName, circuit];
gndNode.history _ Schedule.CreateHistory[from, GndVal];
vddNode _ NodeFromRope[vddName, circuit];
vddNode.history _ Schedule.CreateHistory[from, VddVal];
circuit.agenda _ Schedule.CreateAgenda[SimulateSet, circuit];
t _ Initialize[circuit];
t _ Schedule.ExecuteAgenda[circuit.agenda];
[t, lastNodes] _ LastTime[circuit, 1];
IO.PutF[StdOut, "\nfinished after %d set simulations at %d ps(%g)\n",  IO.int[circuit.info.nbOfSimulations], IO.real[t], IO.rope[RopeFromNode[lastNodes.first, circuit]]];
};	-- CircuitSimulate

SimulateAndPlot: PROC [ref: REF ANY, t: ps, data: REF ANY] ~ {
plot: PlotGraph.Plot ~ NARROW[data];
rect: PlotGraph.Rectangle _ [plot.lowerBounds.x, 0.0, plot.upperBounds.x - plot.lowerBounds.x, 5000.0];
SimulateSet[ref, t, plot.data];
PlotGraph.RefreshPlot[plot: plot, within: rect, eraseFirst: TRUE];
};

InteractiveSimulate: PUBLIC PROC [circuit: Circuit, watchList: NodeList, from: ps _ 0.0] ~ {
t: ps;
gndNode, vddNode: Node;
plot: PlotGraph.Plot;
circuit.info.nbOfSimulations _ 0;
gndNode _ NodeFromRope[gndName, circuit];
gndNode.history _ Schedule.CreateHistory[from, GndVal];
vddNode _ NodeFromRope[vddName, circuit];
vddNode.history _ Schedule.CreateHistory[from, VddVal];
plot _ DrawNodeList[watchList, circuit];
circuit.agenda _ Schedule.CreateAgenda[SimulateAndPlot, plot];
t _ Initialize[circuit];
t _ Schedule.ExecuteAgenda[circuit.agenda];
};
NextSets: PROC [c: RECORD[set: Set, node: Node], setCache: HashTable.Table] RETURNS [camino: Camino] ~ {
set: Set ~ c.set;
node: Node ~ c.node;
value: HashTable.Value;
boolVal: BOOLEAN;
subSets: SetList;
[boolVal, value] _ HashTable.Fetch[setCache, set];
IF boolVal THEN RETURN[NARROW[value, Camino]];
FOR iFetList: FetList _ set.lFets, iFetList.rest UNTIL iFetList=NIL DO
IF iFetList.first.gate.history#NIL THEN {
boolVal _ GetNode[iFetList.first.gate];
iFetList.first.switch _ IF boolVal THEN iFetList.first.type=pE ELSE iFetList.first.type=nE;
}
ELSE iFetList.first.switch _ TRUE;
ENDLOOP;
subSets _ ClipSet[set];
FOR iSetList: SetList _ subSets, iSetList.rest UNTIL iSetList=NIL DO
boolVal _ FALSE;
FOR iFetList: FetList _ iSetList.first.lFets, iFetList.rest UNTIL iFetList=NIL DO
IF iFetList.first.gate=node THEN {boolVal _ TRUE; EXIT};
ENDLOOP;
IF boolVal THEN {
FOR iNodeList: NodeList _ iSetList.first.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO
FOR iSetList2: SetList _ iNodeList.first.setList, iSetList2.rest UNTIL iSetList2=NIL DO
camino _ CONS[[iSetList2.first, iNodeList.first], camino];
ENDLOOP;
ENDLOOP;
};
ENDLOOP;
KillSetList[subSets, TRUE];
[] _ HashTable.Store[setCache, set, camino];
};

SimulateOrRegister: PROC [ref: REF ANY, t: ps, data: REF ANY] ~ {
set: Set ~ NARROW[ref];
extdCirc: ExtendedCircuit _ NARROW[data];
startTable: HashTable.Table _ NARROW[extdCirc.data];
FOR inode: NodeList _ set.inList, inode.rest UNTIL inode=NIL DO
IF inode.first.history=NIL THEN {
[] _ HashTable.Store[startTable, set, $Start];
RETURN;
};
ENDLOOP;
SimulateSet[ref, t, extdCirc.circuit];
};

MaxFreqEvaluate: PUBLIC PROC [circuit: Circuit, clkList: NodeList, from: ps _ 0.0] RETURNS [worst: ps _ 0.0, setList: SetList]~ {
EraseHistory: HashTable.EachPairAction ~ {
node: Node _ NARROW[value];
FOR inodeList: NodeList _ clkList, inodeList.rest UNTIL inodeList=NIL DO
IF inodeList.first=node THEN RETURN;
ENDLOOP;
node.history _ NIL;
IF ~node.input THEN RETURN;
FOR setList: SetList _ node.setList, setList.rest UNTIL setList=NIL DO
Schedule.InsertInAgenda[circuit.agenda, setList.first, from];
ENDLOOP;
};
CreateStart: HashTable.EachPairAction ~ {
set: Set _ NARROW[key];
liveCaminos _ CONS[LIST[[set, set.inList.first]], liveCaminos];
};
n: INT _ 0;
found, finished: BOOLEAN _ FALSE;
gndNode: Node = NodeFromRope[gndName, circuit];
vddNode: Node = NodeFromRope[vddName, circuit];
startTable: HashTable.Table _ HashTable.Create[];
caminos, liveCaminos: Caminos _ NIL;
camino: Camino _ NIL;
extdCirc: ExtendedCircuit _ NEW[ExtendedCircuitRec _ [circuit, startTable]];
setCache: HashTable.Table _ HashTable.Create[];
setListCache: HashTable.Table _ HashTable.Create[];
hist: Histograms.Histogram _ Histograms.Create1D[100.0, 0.0]; --in ns
circuit.info.nbOfSimulations _ 0;
circuit.agenda _ Schedule.CreateAgenda[SimulateOrRegister, extdCirc];
[] _ HashTable.Pairs[circuit.nodeTable, EraseHistory];
gndNode.history _ Schedule.CreateHistory[from, GndVal];
vddNode.history _ Schedule.CreateHistory[from, VddVal];
[] _ VisitLibrary[circuit.library, ResetSets];
FOR inode: NodeList _ clkList, inode.rest UNTIL inode=NIL DO
IF inode.first.history=NIL THEN inode.first.history _ Schedule.CreateHistory[from, GndVal];
FOR setList: SetList _ inode.first.setList, setList.rest UNTIL setList=NIL DO
Schedule.InsertInAgenda[circuit.agenda, setList.first, from];
ENDLOOP;
ENDLOOP;
[] _ Schedule.ExecuteAgenda[circuit.agenda];
[] _ HashTable.Pairs[startTable, CreateStart];
UNTIL liveCaminos=NIL DO
n2: INT _ 0;
oldCaminos: Caminos _ liveCaminos;
liveCaminos _ NIL;
FOR iCaminos: Caminos _ oldCaminos, iCaminos.rest UNTIL iCaminos=NIL DO
thisCamino: Camino _ iCaminos.first;
nextOnes: Camino _ NextSets[thisCamino.first, setCache];
oneAdded: BOOLEAN _ FALSE;
FOR iNext: Camino _ nextOnes, iNext.rest UNTIL iNext=NIL DO
found _ FALSE;
FOR iCamino: Camino _ iCaminos.first, iCamino.rest UNTIL iCamino=NIL DO
IF iNext.first=iCamino.first THEN {found _ TRUE; EXIT}
ENDLOOP;
IF ~found THEN {
oneAdded _ TRUE;
liveCaminos _ CONS[CONS[iNext.first, thisCamino], liveCaminos];
n2 _ n2+1;
};
ENDLOOP;
IF ~oneAdded THEN {
caminos _ CONS[thisCamino, caminos];
n _ n+1;
};
ENDLOOP;
KillCaminos[oldCaminos];
IO.PutF[StdOut, "total %d, in use %d\n", IO.int[n], IO.int[n2]]
ENDLOOP;
setCache _ HashTable.Create[HashTable.GetSize[setCache]];
FOR iCaminos: Caminos _ caminos, iCaminos.rest UNTIL iCaminos=NIL DO
delay: Delay _ ThruTime[iCaminos.first, setCache, setListCache];
IF delay.tup>worst OR delay.tdown>worst THEN {
camino _ iCaminos.first;
worst _ MAX[delay.tup, delay.tdown];
};
Histograms.ChangeTransformed[hist, delay.tup];
Histograms.ChangeTransformed[hist, delay.tdown];
ENDLOOP;
[] _ Histograms.Show[hist];
setList _ LIST[camino.first.set];
camino _ camino.rest;
{iSetList3: SetList _ setList;
UNTIL camino=NIL DO
iSetList3.rest _ LIST[camino.first.set];
camino _ camino.rest;
iSetList3 _ iSetList3.rest;
ENDLOOP;}
};

ThruTime: PROC [camino: Camino, setCache, setListCache: HashTable.Table] RETURNS [delay: Delay] ~ {
value: HashTable.Value;
found: BOOLEAN;
setDelay, prevDelay: Delay;
[found, value] _ HashTable.Fetch[setListCache, camino];
IF found THEN RETURN[NARROW[value, Delay]];
delay _ NEW[DelayRec _ [0.0, 0.0]];
setDelay _ DelayOfSet[camino.first.set, setCache];
IF camino.rest=NIL THEN RETURN;
prevDelay _ ThruTime[camino.rest, setCache, setListCache];
delay.tup _ setDelay.tup+prevDelay.tdown;
delay.tdown _ setDelay.tdown+prevDelay.tup;
[] _ HashTable.Store[setListCache, camino, delay];
};

DelayOfSet: PROC [set: Set, setCache: HashTable.Table] RETURNS [delay: Delay] ~ {
value: HashTable.Value;
found: BOOLEAN;
tup, tdown: ps _ 0.0;
[found, value] _ HashTable.Fetch[setCache, set];
IF found THEN RETURN[NARROW[value, Delay]];
delay _ NEW[DelayRec _ [0.0, 0.0]];
FOR iNodeList: NodeList _ set.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO
IF iNodeList.first.setList#NIL THEN {
FOR iNList: NodeList _ set.inList, iNList.rest UNTIL iNList=NIL DO
iNList.first.done _ FALSE;
ENDLOOP;
FOR iNList: NodeList _ set.fixedV, iNList.rest UNTIL iNList=NIL DO
iNList.first.done _ TRUE;
ENDLOOP;
FOR iFetList: FetList _ set.lFets, iFetList.rest UNTIL iFetList = NIL DO
iFetList.first.switch _ TRUE;
ENDLOOP;
[tup, tdown] _ EvalRC[iNodeList.first, set];
delay.tup _ MAX[delay.tup, tup];
delay.tdown _ MAX[delay.tdown, tdown];
}
ENDLOOP;
[] _ HashTable.Store[setCache, set, delay];
};

FindSetOfNode: PROC [node: Node] RETURNS [set: Set] ~ {
found: BOOLEAN _ FALSE;
FOR iFetList: FetList _ node.fetList, iFetList.rest UNTIL iFetList=NIL DO
FOR iSetList: SetList _ iFetList.first.gate.setList, iSetList.rest UNTIL iSetList=NIL DO
FOR iNodeList: NodeList _ iSetList.first.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO
IF iNodeList.first=node THEN RETURN[iSetList.first];
ENDLOOP;
ENDLOOP;
ERROR;	-- who did some hand editing on the internal data structure ?
ENDLOOP;
};

FindAllPaths: PROC [node: Node, set: Set] RETURNS [pathList: PathList] ~ {
finished: BOOLEAN _ FALSE;
oldPathList: PathList;
level: NAT _ 1;
FOR iNodeList: NodeList _ set.fixedV, iNodeList.rest UNTIL iNodeList=NIL DO
iNodeList.first.index _ 0;
ENDLOOP;
FOR iNodeList: NodeList _ set.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO
iNodeList.first.index _ 0;
ENDLOOP;
pathList _ LIST[LIST[node]];
node.index _ 1;
UNTIL finished DO
level _ level+1;
oldPathList _ pathList;
[finished, pathList] _ IncPaths[set.lFets, oldPathList, level];
ENDLOOP;
};

IncPaths: PROC [fetList: FetList, oldPathList: PathList, level: NAT] RETURNS [finished: BOOLEAN _ TRUE, newPathList: PathList _ NIL] ~ {
nodeList: NodeList;
terminal: BOOLEAN;
FOR iPathList: LIST OF NodeList _ oldPathList, iPathList.rest UNTIL iPathList=NIL DO
nodeList _ iPathList.first;
terminal _ TRUE;
IF ~nodeList.first.input THEN FOR iFetList: FetList _ fetList, iFetList.rest UNTIL iFetList=NIL DO
IF iFetList.first.ch1=nodeList.first THEN 
IF iFetList.first.ch2.index=0 OR iFetList.first.ch2.index>=level THEN {
newPathList _ CONS[ CONS[iFetList.first.ch2, nodeList], newPathList];
iFetList.first.ch2.index _ level;
terminal _ FALSE;
finished _ FALSE;
};
IF iFetList.first.ch2=nodeList.first THEN 
IF iFetList.first.ch1.index=0 OR iFetList.first.ch1.index>=level THEN {
newPathList _ CONS[ CONS[iFetList.first.ch1, nodeList], newPathList];
iFetList.first.ch1.index _ level;
finished _ FALSE;
terminal _ FALSE;
};
ENDLOOP;
IF terminal THEN newPathList _ CONS[nodeList, newPathList];
ENDLOOP;
};

EvalRC: PROC [node: Node, set: Set] RETURNS [tup, tdown: ps _ 0] ~ {
subSets: SetList;
pathList: PathList;
found: BOOLEAN _ FALSE;
tempup, tempdown: ps _ 0.0;
n: NAT _ 0;
FOR iNodeList: NodeList _ set.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO
IF iNodeList.first=node THEN {
found _ TRUE;
EXIT;
};
ENDLOOP;
IF ~found THEN RETURN;
IF set.fixedV=NIL THEN RETURN;
pathList _ FindAllPaths[node, set];
FOR iPathList: PathList _ pathList, iPathList.rest UNTIL iPathList=NIL DO
IF iPathList.first.first.input THEN n _ n+1;
ENDLOOP;
IF n=0 THEN ERROR;
IF n#1 THEN {
FindInputs2[set];
FOR iNodeList: NodeList _ set.inList, iNodeList.rest UNTIL iNodeList=NIL DO
IF ~iNodeList.first.done THEN {
iNodeList.first.done _ TRUE;
FOR bool: BOOLEAN IN BOOLEAN DO
FOR iFetList: FetList _ iNodeList.first.fetList, iFetList.rest UNTIL iFetList = NIL DO
iFetList.first.switch _ IF bool THEN iFetList.first.type=pE ELSE iFetList.first.type=nE;
ENDLOOP;
subSets _ ClipSet[set];
FOR iSetList: SetList _ subSets, iSetList.rest UNTIL iSetList=NIL DO
[tempup, tempdown] _ EvalRC[node, iSetList.first];
tup _ MAX[tempup, tup];
tdown _ MAX[tempdown, tdown];
ENDLOOP;
KillSetList[subSets, TRUE];
ENDLOOP;
};
ENDLOOP;
};
IF tup=0.0 AND tdown=0.0 THEN {
t: ps;
FOR iFetList: FetList _ set.lFets, iFetList.rest UNTIL iFetList = NIL DO
iFetList.first.switch _ TRUE;
ENDLOOP;
[] _ ScanHistoriesOfNodeList[set.lNodes, 0.0, NIL, FALSE];
t _ RCSolve[set, 0.0, NIL];
IF GetNode[node] THEN tup _ t ELSE tdown _ t;
};
};

MaxCapa: PUBLIC PROC [circuit: Circuit, n: NAT _ 1] RETURNS [fatest: REAL, fatNodes: NodeList]~ {
FindFatOnes: PROC[set: Set] RETURNS [ok: BOOLEAN] ~ {
ok _ TRUE;
FOR iNodeList: NodeList _ set.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO
node _ iNodeList.first;
totalC _ totalC+node.cap;
IF node.cap>data.table[0].x THEN 
FOR ind: NAT IN [0..n) DO
IF ind=n-1 OR node.cap<data.table[ind+1].x THEN {
FOR ind2: NAT IN [0..ind) DO
data.table[ind2] _ data.table[ind2+1];
ENDLOOP;
data.table[ind] _ [node.cap, 0.0, node, set];
EXIT;
};
ENDLOOP;
ENDLOOP;
};

DataType: TYPE = REF DataTypeRec;
DataTypeRec: TYPE = RECORD [table: SEQUENCE size: NAT OF RECORD [x, y: REAL, node: Node, set: Set]];
data: DataType _ NEW[DataTypeRec[n]];
node: Node;
totalC: REAL _ 0.0;
FOR ind: NAT IN [0..n) DO
data.table[ind].x _ -1e30;
ENDLOOP;
[] _ VisitLibrary[circuit.library, FindFatOnes];
IO.PutF[StdOut, "\ntotal Capacitance :%5.2fpf (C*V*V/f=%g W @ 25MHz)", IO.real[totalC], IO.real[totalC*VddVal*VddVal*25e-12]];
fatest _ 0.0;
SetTime[0.0, circuit];
FOR ind: NAT IN [0..n) DO
IF data.table[ind].node#NIL THEN {
FOR iNodeList: NodeList _ data.table[ind].set.inList, iNodeList.rest UNTIL iNodeList=NIL DO
iNodeList.first.done _ FALSE;
ENDLOOP;
FOR iNodeList: NodeList _ data.table[ind].set.fixedV, iNodeList.rest UNTIL iNodeList=NIL DO
iNodeList.first.done _ TRUE;
ENDLOOP;
FOR iFetList: FetList _ data.table[ind].set.lFets, iFetList.rest UNTIL iFetList = NIL DO
iFetList.first.switch _ TRUE;
ENDLOOP;
[data.table[ind].x, data.table[ind].y] _ EvalRC[data.table[ind].node, data.table[ind].set];
IO.PutF[StdOut, "\n%g (%5.3fpf), tup: %4.1fns, tdown: %4.1fns", IO.rope[RopeFromNode[data.table[ind].node, circuit]], IO.real[data.table[ind].node.cap], IO.real[data.table[ind].x*0.001], IO.real[data.table[ind].y*0.001]];
fatest _ MAX[MAX[data.table[ind].x, data.table[ind].y], fatest];
fatNodes _ CONS[data.table[ind].node, fatNodes];
};
ENDLOOP;
};

LastTime: PUBLIC PROC [circuit: Circuit, n: NAT _ 1] RETURNS [veryLastTime: ps, slowNodes: NodeList]~ {
FindSlowOnes: HashTable.EachPairAction ~ {
t, v: REAL;
node: Node ~ NARROW[value];
IF node.history=NIL THEN RETURN;
IF node.input THEN RETURN;
[t, v] _ SettledValuesOfNode[node];
IF t <data.table[0].t THEN RETURN;
FOR ind: NAT IN [0..n) DO
IF ind=n-1 OR t<data.table[ind+1].t THEN {
FOR ind2: NAT IN [0..ind) DO
data.table[ind2] _ data.table[ind2+1];
ENDLOOP;
data.table[ind] _ [t, node];
RETURN;
};
ENDLOOP;
};

DataType: TYPE = REF DataTypeRec;
DataTypeRec: TYPE = RECORD [table: SEQUENCE size: NAT OF RECORD [t: ps, node: Node]];
data: DataType _ NEW[DataTypeRec[n]];
FOR ind: NAT IN [0..n) DO
data.table[ind].t _ -1e30;
ENDLOOP;
[] _ HashTable.Pairs[circuit.nodeTable, FindSlowOnes];
FOR ind: NAT IN [0..n) DO
IF data.table[ind].node#NIL THEN slowNodes _ CONS[data.table[ind].node, slowNodes];
ENDLOOP;
veryLastTime _ data.table[n-1].t;
};

PrintSetType: PROC [setType: SetType]  ~ {
IO.PutF[StdOut, " %4d, %4d,", IO.int[setType.nFets[nE]], IO.int[setType.nFets[pE]]];
IO.PutF[StdOut, " %4d, %4d, %4d, %4d", IO.int[setType.nVddTran], IO.int[setType.nGndTran], IO.int[setType.nNodes], IO.int[setType.nInput]];
};

PrintFet: PUBLIC PROC [fet: Fet, circuit: Circuit] ~ {
IF fet=NIL THEN RETURN;
IO.PutFL[StdOut, "\n%g %g(%1.2d) g:%g, ch1:%g, ch2:%g", LIST[IO.rope[IF fet.type=pE THEN "P" ELSE "N"], IO.rope[IF fet.switch THEN "ON" ELSE "OFF"], IO.real[1.0/fet.rOnInv], IO.rope[RopeFromNode[fet.gate, circuit]], IO.rope[RopeFromNode[fet.ch1, circuit]], IO.rope[RopeFromNode[fet.ch2, circuit]]]]; -- more than 5 IO.Values => PutFL
};

PrintNode: PUBLIC PROC [node: Node, circuit: Circuit] ~ {
IF node=NIL THEN RETURN;
IO.PutF[StdOut,"\n   %g(%g) -> ", IO.rope[RopeFromNode[node, circuit]], IO.real[node.cap]];
FOR ih: Schedule.History _ node.history, ih.rest UNTIL ih=NIL DO
IO.PutF[StdOut, "t:%d, V:%5d;  ", IO.real[ih.first.t], IO.real[ih.first.v]];
ENDLOOP;
};

PrintFetList: PUBLIC PROC [fetList: FetList, circuit: Circuit] ~ {
FOR ifetList: FetList _ fetList, ifetList.rest UNTIL ifetList=NIL DO
PrintFet[ifetList.first, circuit];
ENDLOOP;
};

PrintNodeList: PUBLIC PROC [nodeList: NodeList, circuit: Circuit] ~ {
IO.PutF[StdOut,"\n   --"];
FOR inodeList: NodeList _ nodeList, inodeList.rest UNTIL inodeList=NIL DO
PrintNode[inodeList.first, circuit];
ENDLOOP;
};

PrintCamino: PROC [camino: Camino, circuit: Circuit] ~ {
IO.PutF[StdOut,"\n   ~~"];
FOR iCamino: Camino _ camino, iCamino.rest UNTIL iCamino=NIL DO
PrintNode[ iCamino.first.node, circuit];
ENDLOOP;
};

PrintCaminos: PROC [caminos: Caminos, circuit: Circuit] ~ {
IO.PutF[StdOut,"\n   **"];
FOR iCaminos: Caminos _ caminos, iCaminos.rest UNTIL iCaminos=NIL DO
PrintCamino[ iCaminos.first, circuit];
ENDLOOP;
};

SetDoneInNodeList: PROC [nodeList: NodeList, done: BOOLEAN] ~ {
FOR inodeList: NodeList _ nodeList, inodeList.rest UNTIL inodeList=NIL DO
inodeList.first.done _ done;
ENDLOOP;
};

Watch: PUBLIC PROC [watchList: NodeList] ~ {
FOR inodeList: NodeList _ watchList, inodeList.rest UNTIL inodeList=NIL DO
inodeList.first.watched _ TRUE;
ENDLOOP;
};

SetInput: PROC [node: Node, set: Set] ~ {
prev: NodeList;
node.input _ TRUE;
node.done _ TRUE;
set.fixedV _ CONS[node, set.fixedV];
IF node=set.lNodes.first THEN {
set.lNodes _ set.lNodes.rest;
RETURN;
};
prev _ set.lNodes;
FOR nodeList: NodeList _ set.lNodes.rest, nodeList.rest DO
IF nodeList.first=node THEN {
prev.rest _ nodeList.rest;
EXIT;
};
prev _ nodeList;
ENDLOOP;
};

ResetInput: PROC [node: Node, set: Set] ~ {
prev: NodeList;
node.input _ FALSE;
node.done _ FALSE;
set.lNodes _ CONS[node, set.lNodes];
IF node=set.fixedV.first THEN {
set.lNodes _ set.fixedV.rest;
RETURN;
};
prev _ set.fixedV;
FOR nodeList: NodeList _ set.fixedV.rest, nodeList.rest DO
IF nodeList.first=node THEN {
prev.rest _ nodeList.rest;
EXIT;
};
prev _ nodeList;
ENDLOOP;
};

VisitLibrary: PROC [lib: Library, action: PROC[set: Set] RETURNS [ok: BOOLEAN]] RETURNS [ok: BOOLEAN] ~ {
UNTIL lib=NIL DO
FOR iSetList: SetList _ lib.first.setList, iSetList.rest UNTIL iSetList=NIL DO
ok _ action[iSetList.first];
IF ~ok THEN RETURN;
ENDLOOP;
lib _ lib.rest
ENDLOOP;
};

SetTime: PUBLIC PROC [t: ps, circuit: Circuit] ~ {
CutHistory: HashTable.EachPairAction ~ {
node: Node _ NARROW[value];
IF node.history=NIL THEN RETURN;
IF node.input THEN RETURN;
IF Schedule.FirstTimeOfHistory[node.history]>t THEN node.history _ NIL
ELSE node.history _ Schedule.AddToHistory[node.history, t,  Schedule.VFromHistory[node.history, t]];
};

[] _ HashTable.Pairs[circuit.nodeTable, CutHistory]
};

NodeFromRope: PUBLIC PROC [id: Rope.ROPE, circuit: Circuit] RETURNS [node: Node] ~ {
flatWire: CoreFlat.FlatWire _ NEW[CoreFlat.FlatWireRec _ CoreFlat.ParseWirePath[circuit.rootCell, id]];
node _ NARROW[HashTable.Fetch[circuit.nodeTable, flatWire].value];
};

RopeFromNode: PUBLIC PROC [node: Node, circuit: Circuit] RETURNS [id: Rope.ROPE] ~ {
id _ CoreFlat.WirePathRope[circuit.rootCell, node.flatWire^];
};

SetNode: PUBLIC PROC [node: Node, val, input: BOOLEAN _ TRUE, t: ps _ 0.0] ~ {
v: REAL _ IF val THEN VddVal ELSE GndVal;
node.history _ IF node.history=NIL THEN Schedule.CreateHistory[t, v] ELSE Schedule.AddToHistory[node.history, t, v];
node.input _ input;
};

GetNode: PUBLIC PROC [node: Node] RETURNS [val: BOOLEAN] ~ {
v: REAL _ Schedule.LastValueOfHistory[node.history];
val _ v>2500.0;
};

SettledValuesOfNode: PUBLIC PROC [node: Node] RETURNS [t: ps, v: mVolt] ~ {
t _ Schedule.LastTimeOfHistory[node.history];
v _ Schedule.LastValueOfHistory[node.history];
};

EditNodeHistory: PUBLIC PROC [node: Node, t: ps, v: mVolt] ~ {
IF node.history=NIL THEN node.history _ Schedule.CreateHistory[t, v]
ELSE node.history _ Schedule.AddToHistory[node.history, t, v];
};

EditNodeInputBool: PUBLIC PROC [node: Node, forcedInput: BOOLEAN] ~ {
node.input _ forcedInput;
};

KillCaminos: PUBLIC PROC [caminos: Caminos] ~ {
cam: Caminos;
UNTIL caminos=NIL DO
caminos.first _ NIL;
cam _ caminos;
caminos _ caminos.rest;
cam.rest _ NIL;
ENDLOOP;
};

KillFetList: PUBLIC PROC [fetList: FetList] ~ {
fList: FetList;
UNTIL fetList=NIL DO
fetList.first _ NIL;
fList _ fetList;
fetList _ fetList.rest;
fList.rest _ NIL;
ENDLOOP;
};

KillNodeList: PUBLIC PROC [nodeList: NodeList] ~ {
nList: NodeList;
UNTIL nodeList=NIL DO
nodeList.first _ NIL;
nList _ nodeList;
nodeList _ nodeList.rest;
nList.rest _ NIL;
ENDLOOP;
};

KillSetList: PUBLIC PROC [setList: SetList, killSets: BOOLEAN _ TRUE] ~ {
sList: SetList;
UNTIL setList=NIL DO
IF killSets THEN {
KillFetList[setList.first.lFets];
KillNodeList[setList.first.lNodes];
KillNodeList[setList.first.inList];
KillNodeList[setList.first.fixedV];
setList.first.type _ NIL;
};
setList.first _ NIL;
sList _ setList;
setList _ setList.rest;
sList.rest _ NIL;
ENDLOOP;
};

KillNode: HashTable.EachPairAction ~ {
node: Node _ NARROW[value];
node.flatWire _ NIL;
Schedule.KillHistory[node.history];
KillFetList[node.fetList];
KillSetList[node.setList, FALSE];
};

KillCircuit: PUBLIC PROC [circuit: Circuit] ~ {
lib: Library;
circuit.rootCell _ NIL;
[] _ HashTable.Pairs[circuit.nodeTable, KillNode];
HashTable.Erase[circuit.nodeTable];
Schedule.KillAgenda[circuit.agenda];
UNTIL circuit.library=NIL DO
KillSetList[circuit.library.first.setList, TRUE];
circuit.library.first.type _ NIL;
circuit.library.first _ NIL;
lib _ circuit.library;
circuit.library _ circuit.library.rest;
lib.rest _ NIL;
ENDLOOP;
circuit.info _ NIL;
};
MintEnum: PROC [plot: PlotGraph.Plot, graph: PlotGraph.Graph, bounds: PlotGraph.Rectangle, eachPoint: PlotGraph.PointProc, data: REF ANY _ NIL] RETURNS [invalidEnumeration: BOOL _ FALSE] ~ {
Action: Schedule.HistoryProc ~ {
quit _ eachPoint[t, v, data];
};
quit: BOOLEAN _ FALSE;
node: Node _ NARROW[graph.data];
to: REAL _ IF bounds.w=0.0 THEN 1e24 ELSE bounds.x+bounds.w;
from: REAL _ IF bounds.w=0.0 THEN -1e24 ELSE bounds.x;
quit _ Schedule.EnumerateHistory[node.history, from, to, Action];
};

AddNodeToPlot: PUBLIC PROC [node: Node, plot: PlotGraph.Plot, r: Rope.ROPE _ NIL] ~ {
circuit: Circuit _ NARROW[plot.data];
rect: PlotGraph.Rectangle _ [plot.lowerBounds.x, 0.0, plot.upperBounds.x - plot.lowerBounds.x, 5000.0];
IF r=NIL THEN r _ RopeFromNode[node, circuit];
PlotGraph.LockPlot[plot];
plot.axis _ CONS[
NEW[PlotGraph.AxisRec _ [
graphs: LIST[NEW[PlotGraph.GraphRec _ [
class: mintGClass,
data: node,
name: ""
]]],
bounds: rect,
name: IF r#NIL THEN r ELSE RopeFromNode[node, circuit],
style: analog,
axisData: [mintAxisData, mintAxisData]
]],
plot.axis];
PlotGraph.UnlockPlot[plot];
PlotGraph.RefreshPlot[plot: plot, within: rect, eraseFirst: TRUE];
};

DrawNodeList: PUBLIC PROC [nodeList: NodeList, circuit: Circuit] RETURNS[plot: PlotGraph.Plot] ~ {
rect: PlotGraph.Rectangle;
plot _ PlotGraph.CreatePlot[CoreOps.GetCellTypeName[circuit.rootCell]];
PlotGraph.LockPlot[plot];
plot.data _ circuit;
plot.lowerBounds _ [1.0e24, 0.0];
plot.upperBounds _ [-1.0e24, 5000.0];
FOR inodeList: NodeList _ nodeList, inodeList.rest UNTIL inodeList=NIL DO
plot.lowerBounds.x _ MIN[plot.lowerBounds.x, Schedule.FirstTimeOfHistory[inodeList.first.history]];
plot.upperBounds.x _ MAX[plot.upperBounds.x, Schedule.LastTimeOfHistory[inodeList.first.history]];
ENDLOOP;
plot.upperBounds.x _ MAX[plot.upperBounds.x, 1.0+plot.lowerBounds.x];
rect _ [plot.lowerBounds.x, 0.0, plot.upperBounds.x - plot.lowerBounds.x, 5000.0];
FOR inodeList: NodeList _ nodeList, inodeList.rest UNTIL inodeList=NIL DO
plot.axis _ CONS[
NEW[PlotGraph.AxisRec _ [
graphs: LIST[NEW[PlotGraph.GraphRec _ [
class: mintGClass,
data: inodeList.first,
name: ""
]]],
bounds: rect,
name: RopeFromNode[inodeList.first, circuit],
style: analog,
axisData: [mintAxisData, mintAxisData]
]],
plot.axis];
ENDLOOP;
PlotGraph.UnlockPlot[plot];
PlotGraph.RefreshPlot[plot: plot, within: rect, eraseFirst: TRUE];
};

mintGClass: PlotGraph.GraphClass _ NEW[PlotGraph.GraphClassRec _ [
insert: NIL,
delete: NIL,
enumerate: MintEnum
]];
mintAxisData: PlotGraph.AxisData _ [1000.0, TRUE, FALSE];
IF separateView THEN {
typeScript: TypeScript.TS _ TypeScript.Create[info: [name: "Mint", iconic: TRUE, icon: Icons.NewIconFromFile["Mint.icons", 1]]];
StdOut _ ViewerIO.CreateViewerStreams[name: "Mint", viewer: typeScript].out;
}
ELSE StdOut _ TerminalIO.TOS[];
IO.PutRope[StdOut, "\nMint loaded\n"];
END. 
��v��MintImpl.mesa
Copyright c 1986 by Xerox Corporation.  All rights reserved.
Christian LeCocq January 14, 1987 4:32:25 pm PST


RealFns,
RealFns,
some usefull definitions
Simulation Computations
evaluates the time interval and verifies inputs
Propagate: PROC [node: Node, action: PROC[node: Node] RETURNS [quit: BOOLEAN _ FALSE]] RETURNS [quit: BOOLEAN _ FALSE] ~ {
Calls action for all the nodes of the sets activated by node.
FOR iSetList: SetList _ node.setList, iSetList.rest UNTIL iSetList=NIL DO
FOR inodeList: NodeList _ iSetList.first.lNodes, inodeList.rest UNTIL inodeList=NIL DO
quit _ action[inodeList.first];
IF quit THEN RETURN;
ENDLOOP;
ENDLOOP;
};

Swap: PROC [A: LinearSystem.MatrixN, b:LinearSystem.ColumnN, i, j, n: CARDINAL] ~ {
tempRow: LinearSystem.RowN;
tempReal: REAL;
tempRow _ A[i];
A[i] _ A[j];
A[j] _ tempRow;
tempReal _ b[i];
b[i] _ b[j];
b[j] _ tempReal;
FOR k:CARDINAL IN [0..n) DO
tempReal _ A[k][i];
A[k][i] _ A[k][j];
A[k][j] _ tempReal;
ENDLOOP;
};

CheckSwitch: PROC [fet: Fet, t0, t1: ps] RETURNS [change: BOOLEAN _ FALSE, changet: ps _ 0] ~ {
status is TRUE if fet is on. Version with substrate effect.
vSource, vTran, vGate, vCh1, vCh2: mVolt;
chMin, chMax, chActive: Node;
status: BOOLEAN;
vGate _ Schedule.VFromHistory[fet.gate.history, t1];
vCh1 _ Schedule.VFromHistory[fet.ch1.history, t1];
vCh2 _ Schedule.VFromHistory[fet.ch2.history, t1];
IF vCh1<vCh2 THEN { chMin _ fet.ch1; chMax _ fet.ch2} 
ELSE {chMin _ fet.ch2; chMax _ fet.ch1};
SELECT fet.type FROM
nE => {
chActive _ chMin;
vSource _ MIN[vCh1, vCh2];
vDrain _ MAX[vCh1, vCh2];
vTran _ vSource+VtVal+gamma*(RealFns.SqRt[vSource+phi]-sqrtPhi);
status _ (vGate > vTran);
SELECT TRUE FROM
vGate<=vSource => status _ FALSE;
vGate>=vDrain => status _ TRUE;
ENDCASE => {
vTran _ vSource+VtVal+gamma*(RealFns.SqRt[vSource+phi]-sqrtPhi);
status _ (vGate > vTran);
};
};
pE => {
chActive _ chMax;
vSource _ MAX[vCh1, vCh2];
vDrain _ MIN[vCh1, vCh2];
vTran _ vSource-(VtVal+gamma*(RealFns.SqRt[VddVal-vSource+phi] - sqrtPhi));
status _ (vGate < vTran);
SELECT TRUE FROM
vGate>=vSource => status _ FALSE;
vGate<=vDrain => status _ TRUE;
ENDCASE => {
vTran _ vSource-(VtVal+gamma*(RealFns.SqRt[VddVal-vSource+phi] - sqrtPhi));
status _ (vGate < vTran);
};
};
ENDCASE => ERROR;
IF status#fet.switch THEN {
UNTIL t1-t0<timeSpecs DO
ti: ps _ (t0 + t1)/2;
IF CheckSwitch[fet, t0, ti].change THEN t1 _ ti
ELSE t0 _ ti;
ENDLOOP; 
change _ TRUE;
changet _ t1;
};
};

status is TRUE if fet is on. Version without substrate effect.
Initialisations (no ALL[0.0] for sequences...)
FOR iNodeList: NodeList _ set.lNodes, iNodeList.rest UNTIL iNodeList=NIL DO
FOR iFetList: FetList _ iNodeList.first.fetList, iFetList.rest UNTIL iFetList = NIL DO
IF ~iFetList.first.done AND iFetList.first.switch THEN {
IF iNodeList.first#iFetList.first.gate THEN Coeff[matrix, iFetList.first];
What about a gate connected to ch1 or ch2 ?
};
ENDLOOP;
ENDLOOP;
If fixed voltages are not connected to the set then return.
IF triState THEN RETURN[t];
Fixed voltages are removed from the system to solve. 
tFinal _ tFinal-1.0/matrix[i][i];
mark the point where the value begins to change
Solve the RC system, which as a side effect destroys cstTerm.
mark the point where the value stabilizes
Execution
PROC [key: Key, value: Value] RETURNS [quit: BOOLEAN _ FALSE];
Timing Evaluation
does this set contain the specified node ?
does this set contain one of the power node ?
is there only one path to a power node ?
either no further input specification reduces the poblem, or the set has only one path to power
PROC [key: Key, value: Value] RETURNS [quit: BOOLEAN _ FALSE];
PROC [key: Key, value: Value] RETURNS [quit: BOOLEAN _ FALSE];
Utilities
add node to fixed voltages
and remove it from lNodes
add node to lNodes
and remove it from fixed voltages list
PROC [key: Key, value: Value] RETURNS [quit: BOOLEAN _ FALSE];
PROC [key: Key, value: Value] RETURNS [quit: BOOLEAN _ FALSE];
Plot Management
Ê4j��˜�codešœ
™
Kšœ<™<K™0K™�K™�—šÏk	˜	K˜	K˜Kšœ
˜
Kšœ˜Kšœ˜Kšœ˜Kšœ
˜
Kšœ˜K˜
Kšœ˜Kšœ™Kšœ˜Kšœ	˜	K˜Kšœ˜Kšœ	˜	K˜�—KšÏnœœ˜š˜K˜	K˜Kšœ
˜
Kšœ˜Kšœ˜Kšœ˜Kšœ
˜
K˜
Kšœ™Kšœ	˜	K˜Kšœ˜Kšœ˜K˜�—š˜K˜—š˜Kšœ˜—šœœœ˜Kšœ
œœœ
˜"Kšœ	œœœ˜Kš	œœœœœ˜4Kšœœœ
˜Kšœ
œœ˜)Kšœœœ˜/Kš	œœœœœ˜DK˜�K™Kšœœ˜"Kšœœ˜"Kšœ˜Kšœ˜Kšœ˜Kšœ˜KšœœÏc+˜GKšœ˜Kšœ	œ
˜K˜K˜K˜�Kšœœ˜Kšœœœ˜Kš	œœœœœ˜Kšœœœœ˜Kšœ	œœœ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœœœ˜+Kšœœ˜'Kšœ	œ˜K˜�—head™šžœœœœœœ˜:Kšœœ˜ Kšœœ˜Kšœ)˜)Kšœ˜K˜Kšœœœ˜%Kšœœœ˜)Kšœ
œœ˜Kšœ!˜!Kšœœœœ˜Kšœ
œœ˜Kšœœ˜Kš
œœœœ#œ˜sKšœ8œ˜Ašœ(œœ˜=Kšœ	œœ&œ*˜dKšœ˜—Kšœ:˜:Kšœœ˜šœ*œœ˜?Kšœœ!˜.Kšœœ&˜;Kšœ˜—šœ*œœ˜?Kšœœ˜7Kšœ˜—šœ*œœ˜?Kšœœ˜4Kšœ˜—Kšœœœ˜,Kšœ-˜-Kšœ	˜	šœœœ˜2Kšœ˜Kšœ˜—Kšœ˜Kšœœœ-˜Gš
œ
˜Kšœ;˜;Kšœœœ˜#Kšœœ˜šœœ˜Kšœ˜Kšœ˜šœ*œ	œ˜AKšœ-˜-Kšœ
œ˜Kšœ
œ˜šœœ	œ˜Kšœ,œ
œ
˜LJšœ-˜-K˜—Kšœ˜—Kšœœ˜K˜—šœ˜Kšœ˜šœœœ˜1Kšœ˜Kšœ˜—Kšœœ#œœ˜?Kšœ
œœœ	˜EK˜—Kš˜—šœ*œœ˜?šœ6œ	œ˜MKšœe˜eKšœœ˜Kšœ˜—Kšœ˜—Kšœ>˜>Kšœœœœœ6œ$œœ˜ÞK˜K˜�—š
žœœ;œœœ˜š
ž
œœ
œœœ˜@Kšœœœœ˜ Kšœœœ˜Kšœ6˜6šœœ-œ˜>Kšœ`˜`šœ1œ
œ˜IKšœ4˜4Kšœœ˜šœ=œœ˜VKšœ#˜#Kšœ˜—Kšœ˜—K˜—K˜—K˜šœ(œœ˜=K™/šœœœ˜!Kšœœ˜1KšœB˜BK˜—šœ˜KšœœH˜dKšœœ˜)K˜—Kšœ˜—K˜K˜�—šž	œœœ
œœœœœœ™zK™=šœ1œ
œ™Išœ=œœ™VKšœ™Kšœœœ™Kšœ™—Kšœ™—K™K™�—šžœœ˜ Kšœœ˜šœ&œœ˜:Kšœœ˜šœ*œœ˜?šœœ˜%Kšœœ˜
Kšœ˜K˜—Kšœ˜—šœœ˜Kšœœ˜šœ*œœ˜?šœœ˜%Kšœœ˜
Kšœ˜K˜—Kšœ˜—Kšœœœ˜>K˜—Kšœ˜—K˜K˜�—šžœœ œœ˜Všœ0œ
œ˜Kšœ<œœ˜Všœœœ˜ šœ'œ˜/Kšœ
œ˜(Kšœœ˜,Kšœœ˜K˜——Kšœ˜—Kšœ˜—KšœŸ˜K˜�K˜�—šž
œœœœ˜Wšœ,œœ˜Fšœœœ˜%Kšœœ˜.Kšœ
œ!˜2Kšœœ˜K˜—šœœœ˜%Kšœœ˜.Kšœ
œ!˜2Kšœœ˜K˜—Kšœ˜—KšœŸ˜K˜�—š
žœœ&œœœ˜\Kšœ	˜	Kšœ˜Kšœ˜šœ3œœ˜MKšœ-˜-Kšœ˜—šœ7œœ˜PKšœœ˜Kšœ˜—šœ7œœ˜PKšœœ˜Kšœ˜—šœ0œœ˜IKšœœ˜Kšœ˜—šœ7œœ˜PKšœœœ˜"Kšœœ	˜Kšœœ˜!Kšœ˜Kšœœ˜šœ
œ˜K˜&K˜'Kšœ˜—šœ7œœ˜PKšœœ˜Kšœ˜—šœ.œ
œ˜Fš
œœœœœ˜]Kšœœ˜Kšœ
œ!˜2K˜—š
œœœœœ˜^Kšœœ˜Kšœ
œ!˜2K˜—Kšœ˜—šœ7œœ˜PKšœœ˜Kšœ˜—Kšœ
œ˜Kšœ˜—K˜K˜�—šœS™SKšœ™Kšœ™Kšœ™K™Kšœ™Kšœ™Kšœ™Kšœ™šœ™Kšœ™K™Kšœ™Kšœ™—K™K™�—š
žœœœ
œœ™_Kšœ;™;Kšœ)™)Kšœ™Kšœœ™Kšœ4™4Kšœ2™2Kšœ2™2šœœ%™6Kšœ$™(—šœ
™™Kšœ™Kšœ
œ
™Kšœ	œ
™Kšœ@™@Kšœ™šœœ™Kšœœ™!Kšœœ™šœ™Kšœ@™@Kšœ™K™——Kšœ™—™Kšœ™Kšœ
œ
™Kšœ	œ
™KšœK™KKšœ™šœœ™Kšœœ™!Kšœœ™šœ™KšœK™KKšœ™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˜�—š
žœœ œœœ˜bK˜Kšœœ˜Kšœœ˜Kšœ
˜
šœ,œ
œ˜DKšœ2˜2šœœ˜K˜/Kšœœ˜šœœ˜šœ3œœ˜LKšœ1˜1Kšœ˜—Kšœœ˜Kšœ˜Kšœ	˜	K˜—Kšœœ˜2K˜—Kšœ˜—K˜K˜�—šž	œœœœ˜3Kšœœ˜šœ.œ
œ˜FKšœœ˜Kšœ˜—Kšœœ˜
K˜K˜�—šžœœ-˜8Kšœœ˜Kšœ˜Kšœ˜Kšœ9˜9Kšœ9˜9Kšœ9˜9Kšœ9˜9Kšœœ˜K˜K˜�—šž
œœ&œœ#˜jKšœ˜Kšœœ˜
Kšœ#˜#Kšœ˜Kšœ˜Kšœ˜šœœœ˜&Kšœ˜Kšœ˜Kšœ˜Kšœ˜—K˜K˜�—šžœœ%œ˜LKšœ(˜(Kšœ;˜;Kšœ˜Kšœœ˜Kšœ
œœ˜Kšœœ˜,Kš	œœœœŸ$˜Fšœ2œœ˜KKšœ˜Kšœ˜Kšœ˜—Kšœ˜šœ2œœ˜KKšœ˜Kšœ˜Kšœ˜—Kšœ	œ!˜-Kšœ
œ˜+Kšœœ˜)Kšœœ˜'šœ2œœ˜KKšœQ˜QKšœ˜—šœ2œœ˜KKšœQ˜QKšœ˜—šœ.œœ˜HKšœœ˜Kšœ˜—K™.šœœœ
˜$Kšœœ˜0Kšœ˜šœ
œœ
˜'Kšœ˜Kšœ˜—Kšœ˜—šœ2œœ™Kšœ<œœ™Všœœœ™8Kšœ%œ™JK™+K™—Kšœ™—Kšœ™—šœ.œœ˜HKšœœœ˜UKšœ˜—K™;šœœœ˜,šœœ˜Kšœœ˜Kšœ˜K˜—Kšœ˜—Kšœ
œœ˜Kšœ
œœ™K™5šœœœ˜'Kšœ!™!šœœœ˜,Kšœ0˜0Kšœ˜—Kšœ˜—K™/šœ2œœ˜KKšœj˜jKšœ˜—Kšœ=™=šœœ˜Kšœ	œ$˜0šœœœ˜'Kšœ ˜ Kšœ˜—K˜—KšœP˜TKšœ˜šœœœœœ˜9Kšœ+˜+KšœŸ˜6šœœœ˜"Kšœ˜šœœœ˜'Kšœ3˜3Kšœ˜—Kšœ˜—KšœN˜NKšœ˜šœœœ˜"KšœO˜OKšœ˜—šœœœ˜'KšœM˜MKšœ˜—Kšœ,˜,Kšœ˜Kš˜—Kšœ1˜5K™)Kšœ˜šœ2œœ˜KKšœr˜rKšœœœ˜ŸKšœ˜—K˜K˜�——™	šž
œœœ˜>šžœ˜&Kšœ>™>Kšœ
œ˜K˜Kšœ
œœ˜Kšœ/˜/Kšœœ˜šœ/œ	œ˜FKšœ;˜;Kšœ˜—K˜—Kšœ(œ	˜4Kšœ2˜2Kšœ.˜.Kšœ˜K˜K˜�—šžœœœ#œ˜QK˜K˜Kšœ!˜!Kšœ)˜)Kšœ7˜7Kšœ)˜)Kšœ7˜7Kšœ=˜=Kšœ˜Kšœ+˜+Kšœ&˜&KšœEœ$œ
œ/˜ªKšœŸ˜K˜�—šÏbœœœœœœ˜>Kšœœ˜$Kšœg˜gKšœ˜Kšœ<œ˜BK˜K˜�—šžœœ<˜\Kšœ˜Kšœ˜Kšœ˜Kšœ!˜!Kšœ)˜)Kšœ7˜7Kšœ)˜)Kšœ7˜7Kšœ(˜(Kšœ>˜>Kšœ˜Kšœ+˜+K˜——™šžœœœ3œ˜hKšœ˜Kšœ˜Kšœ˜Kšœ	œ˜Kšœ˜Kšœ2˜2Kšœ	œœœ˜.šœ.œ
œ˜Fšœœœ˜)Kšœ'˜'Kšœœ	œœ˜[K˜—Kšœœ˜"Kšœ˜—K˜šœ,œ
œ˜DKšœ
œ˜šœ9œ
œ˜QKšœœœœ˜8Kšœ˜—šœ	œ˜šœ=œœ˜Všœ>œœ˜WKšœ	œ-˜:Kšœ˜—Kš˜Kšœ˜——Kšœ˜—Kšœœ˜Kšœ,˜,K˜K˜�—š œœœœœœ˜AKšœœ˜Kšœœ˜)Kšœœ˜4šœ*œœ˜?šœœœ˜!Kšœ.˜.Kšœ˜K˜—Kšœ˜—Kšœ&˜&K˜K˜�—šžœœœ7œ'˜šžœ˜*Kšœ
œ˜šœ/œœ˜HKšœœœ˜$Kšœ˜—Kšœœ˜Kšœ
œœ˜šœ/œ	œ˜FKšœ=˜=Kšœ˜—K˜—šžœ˜)Kšœœ˜Kšœœœ(˜?K˜—Kšœœ˜Kšœœ˜!Kšœ/˜/Kšœ/˜/Kšœ1˜1Kšœ œ˜$Kšœœ˜Kšœœ-˜LKšœ/˜/Kšœ3˜3Kšœ>Ÿ˜EKšœ!˜!KšœE˜EKšœ6˜6Kšœ7˜7Kšœ7˜7Kšœ.˜.šœ'œœ˜<Kšœœœ<˜[šœ6œ	œ˜MKšœ=˜=Kšœ˜—Kšœ˜—Kšœ,˜,Kšœ.˜.šœ
œ˜Kšœœ˜Kšœ"˜"Kšœœ˜šœ/œ
œ˜GKšœ$˜$Kšœ8˜8Kšœ
œœ˜šœ&œœ˜;Kšœœ˜šœ0œ	œ˜GKšœœ
œœ˜6Kšœ˜—šœœ˜Kšœœ˜Kšœœœ(˜?K˜
K˜—Kšœ˜—šœœ˜Kšœ
œ˜$K˜K˜—Kšœ˜—Kšœ˜Kšœ=˜?Kšœ˜—K•StartOfExpansionZ[mod: HashTable.SeqIndex _ 17, equal: HashTable.EqualProc, hash: HashTable.HashProc]šœ9˜9šœ,œ
œ˜DKšœ@˜@šœœœ˜.Jšœ˜Jšœœ˜$J˜—Jšœ.˜.Jšœ0˜0Kšœ˜—Kšœ˜Kšœ
œ˜!Kšœ˜Kšœ˜šœœ˜Kšœœ˜(Kšœ˜Kšœ˜Kšœ˜	—K˜K˜�—šžœœ;œ˜cKšœ˜Kšœœ˜Kšœ˜Kšœ7˜7Kšœœœœ˜+Kšœœ˜#Jšœ2˜2Kšœ
œœœ˜Kšœ:˜:Kšœ)˜)Kšœ+˜+Kšœ2˜2K˜K˜�—šž
œœ'œ˜QKšœ˜Kšœœ˜K˜Kšœ0˜0Kšœœœœ˜+Kšœœ˜#šœ2œœ˜Kšœœœ˜%šœ,œœ˜BKšœœ˜Kšœ˜—šœ,œœ˜BKšœœ˜Kšœ˜—šœ.œœ˜HKšœœ˜Kšœ˜—Kšœ,˜,Kšœœ˜ Kšœœ˜&K˜—Kšœ˜—Kšœ+˜+K˜K˜�—šž
œœœ˜7Kšœœœ˜šœ1œ
œ˜Išœ@œ
œ˜Xšœ=œœ˜VKšœœœ˜4Kšœ˜—Kšœ˜—KšœŸ=˜DKšœ˜—K˜K˜�—šžœœœ˜JKšœ
œœ˜Kšœ˜Kšœœ˜šœ2œœ˜KKšœ˜Kšœ˜—šœ2œœ˜KKšœ˜Kšœ˜—Kšœœœ˜Kšœ˜šœ
˜Kšœ˜Kšœ˜Kšœ?˜?Kšœ˜—K˜K˜�—šžœœ2œœœœœœ˜ˆKšœ˜Kšœ
œ˜šœœœ(œœ˜TKšœ˜Kšœœ˜šœœœ,œ
œ˜bšœ#œ˜*šœœ!œ˜GKšœœœ-˜EKšœ!˜!Kšœœ˜Kšœœ˜K˜——šœ#œ˜*šœœ!œ˜GKšœœœ-˜EKšœ!˜!Kšœœ˜Kšœœ˜K˜——Kšœ˜—Kšœ
œœ˜;Kšœ˜—K˜K˜�—šžœœœ˜DKšœ˜Kšœ˜Kšœœ˜K˜Kšœœ˜Kšœ*™*šœ2œœ˜Kšœœ˜Kšœœ˜
Kšœ˜Kšœ˜—Kšœ˜—Kšœœœ˜Kšœ-™-Kšœœœœ˜K™(Kšœ#˜#šœ0œœ˜IKšœœ	˜,Kšœ˜—Kšœœœ˜šœœ˜
Kšœ˜šœ2œœ˜Kšœœ˜Kšœœ˜š	œœœœ˜šœ<œœ˜VKšœœœœ˜XKšœ˜—K˜šœ,œ
œ˜DKšœ2˜2Kšœœ˜Kšœœ˜Kšœ˜—Kšœœ˜—Kšœ˜K˜—Kšœ˜K˜——šœ	œœ˜K™_Kšœ˜šœ.œœ˜HKšœœ˜Kšœ˜—Kšœ.œœ˜:Kšœœ˜Kšœœ	œ˜-K˜—K˜K˜�—šžœœœœœ
œ˜aš œœœœ˜5Kšœ>™>Jšœœ˜
šœ2œœ˜KKšœ˜Kšœ˜šœœ˜!šœœœ˜šœ	œœ˜1šœœœ
˜Kšœ&˜&Kšœ˜—Kšœ-˜-Kšœ˜K˜—Kšœ˜——Kšœ˜—K˜K˜�—Kšœ
œœ
˜!Kšœ
œœ	œœœœœ˜dKšœœ˜%K˜Kšœœ˜šœœœ˜Kšœ˜Kšœ˜—Kšœ0˜0K˜~Kšœ
˜
Kšœ˜šœœœ˜šœœœ˜"šœBœœ˜[Kšœœ˜Kšœ˜—šœBœœ˜[Kšœœ˜Kšœ˜—šœ>œœ˜XKšœœ˜Kšœ˜—Kšœ[˜[KšœÝ˜ÝKšœ	œœ0˜@Kšœœ!˜0Kšœ˜—Kšœ˜—K˜K˜�—š
žœœœœœ+˜gš œ˜*Kšœ>™>Kšœœ˜Kšœ
œ˜Kšœœœœ˜ Kšœœœ˜Kšœ#˜#Kšœœœ˜"šœœœ˜šœ	œœ˜*šœœœ
˜Kšœ&˜&Kšœ˜—Kšœ˜Kšœ˜K˜—Kšœ˜—K˜K˜�—Kšœ
œœ
˜!Kš
œ
œœ	œœœœ˜UKšœœ˜%šœœœ˜Kšœ˜Kšœ˜—Kšœ6˜6šœœœ˜Kšœœœ
œ"˜SKšœ˜—Kšœ!˜!K˜K˜�——™	šžœœ˜*Kšœœœ˜TKš
œ%œœœœ˜‹K˜K˜�—šžœœ!˜6Kšœœœœ˜Kšœ6œœœ
œœœœœœ	œœ(œ'œ)Ÿœ˜ÍK˜K˜�—šž	œœœ#˜9Kšœœœœ˜Kšœ œ$œ˜[šœ.œœ˜@Kšœ œœ˜LKšœ˜—˜K˜�——šž œœ)˜Bšœ,œ
œ˜DKšœ"˜"Kšœ˜—K˜K˜�—šž œœ+˜EKšœ˜šœ0œœ˜IKšœ$˜$Kšœ˜—K˜K˜�—šžœœ'˜8Kšœ˜šœ(œ	œ˜?Kšœ(˜(Kšœ˜—K˜K˜�—šžœœ)˜;Kšœ˜šœ,œ
œ˜DKšœ&˜&Kšœ˜—K˜K˜�—šžœœœ˜?šœ0œœ˜IKšœ˜Kšœ˜—K˜K˜�—šžœœœ˜,šœ1œœ˜JKšœœ˜Kšœ˜—K˜K˜�—šžœœ˜)Kšœ˜Kšœ
œ˜Kšœœ˜Kšœ™Kšœ
œ˜$Kšœ™šœœ˜Kšœ˜Kšœ˜K˜—Kšœ˜šœ5˜:šœœ˜Kšœ˜Kšœ˜K˜—Kšœ˜Kšœ˜—K˜K˜�—šž
œœ˜+Kšœ˜Kšœ
œ˜Kšœœ˜Kšœ™Kšœ
œ˜$Kšœ&™&šœœ˜Kšœ˜Kšœ˜K˜—Kšœ˜šœ5˜:šœœ˜Kšœ˜Kšœ˜K˜—Kšœ˜Kšœ˜—K˜K˜�—šžœœœœœœœ˜išœœ˜šœ6œ
œ˜NKšœ˜Kšœœœ˜Kšœ˜—Kšœ˜Kšœ˜—K˜K˜�—šžœœœ˜2šž
œ˜(Kšœ>™>Kšœ
œ˜Kšœœœœ˜ Kšœœœ˜Kšœ-œ˜FKšœ`˜dK˜—K˜�Kšœ3˜3K˜K˜�—š
žœœœœœ˜TKšœœF˜gKšœœ5˜BK˜K˜�—šžœœ œœ˜TKšœ=˜=K˜K˜�—š
žœœœœœ˜NKš	œœœœœ˜)Kš	œœœœœ+˜tKšœ˜K˜K˜�—šžœœœœ˜<Kšœœ-˜4Kšœ˜K˜K˜�—šžœœœœ˜KKšœ-˜-Kšœ.˜.K˜K˜�—šžœœœ"˜>Kšœœœ,˜DKšœ:˜>K˜K˜�—šžœœœœ˜EKšœ˜K˜K˜�—šÐbnœœœ˜/Kšœ
˜
šœ	œ˜Kšœœ˜Kšœ˜Kšœ˜Kšœœ˜Kšœ˜—K˜K˜�—š¡œœœ˜/Kšœ˜šœ	œ˜Kšœœ˜Kšœ˜Kšœ˜Kšœ
œ˜Kšœ˜—K˜K˜�—š¡œœœ˜2Kšœ˜šœ
œ˜Kšœœ˜Kšœ˜Kšœ˜Kšœ
œ˜Kšœ˜—K˜K˜�—š
žœœœœœ˜IKšœ˜šœ	œ˜šœ
œ˜Kšœ!˜!Kšœ#˜#Kšœ#˜#Kšœ#˜#Kšœœ˜K˜—Kšœœ˜Kšœ˜Kšœ˜Kšœ
œ˜Kšœ˜—K˜K˜�—šžœ˜&Kšœ>™>Kšœ
œ˜Kšœœ˜Kšœ#˜#Kšœ˜Kšœœ˜!K˜K˜�—šžœœœ˜/K˜
Kšœœ˜Kšœ2˜2Kšœ#˜#Kšœ$˜$šœœ˜Jšœ+œ˜1Jšœœ˜!Jšœœ˜Jšœ˜Kšœ'˜'Kšœœ˜Kšœ˜—Kšœœ˜K˜——™šžœœsœœœœœœ˜¾šžœ˜ Kšœ˜K˜—Kšœœœ˜Kšœ
œ
˜ Kš	œœœœœ˜<Kš	œœœœœ
˜6KšœA˜AK˜K˜�—š
ž
œœœ,œœ˜UKšœœ˜%Kšœg˜gKšœœœ!˜.Kšœ˜šœœ˜šœ˜šœœœ˜'Kšœ˜Kšœ˜Kšœ˜K˜—Kšœ
˜
Kš	œœœœœ˜7Kšœ˜Kšœ&˜&Kšœ˜—Kšœ˜—Kšœ˜Kšœ<œ˜BK˜K˜�—šžœœœ(œ˜bKšœ˜KšœG˜GKšœ˜Kšœ˜Kšœ!˜!Kšœ%˜%šœ0œœ˜IKšœœK˜cKšœœJ˜bKšœ˜—Kšœœ-˜EKšœR˜Ršœ0œœ˜Išœœ˜šœ˜šœœœ˜'Kšœ˜Kšœ˜Kšœ˜K˜—Kšœ
˜
Kšœ-˜-Kšœ˜Kšœ&˜&Kšœ˜—Kšœ˜—Kšœ˜—Kšœ˜Kšœ<œ˜BK˜K˜�—šœ#œ˜BKšœœ˜Kšœœ˜Kšœ˜K˜—Kšœ,œœ˜9šœ
œ˜Kšœœ2œ1˜€KšœL˜LK˜—Kšœœ˜Kšœ$˜&—Kšœ˜—�…—����–‚��Úb��