DIRECTORY BasicTime, BoolEx, CommandTool, FileNames, FS, RefTab, SymTab, IO, PLAOps, REFBit, RefText, Rope, RopeList; PLAOpsImplD: CEDAR PROGRAM IMPORTS CommandTool, BoolEx, FileNames, FS, RefTab, SymTab, IO, PLAOps, RefText, Rope, RopeList, REFBit EXPORTS PLAOps = BEGIN OPEN PLAOps; OpIndex: TYPE = BoolEx.OpIndex; CopyPLA: PUBLIC PROC[old: PLA] RETURNS[new: PLA] = { new _ NewPLA[old.data, old.out, old.name]; new.time _ old.time; new.termList _ CopyTermList[old.termList]}; plas: SymTab.Ref _ SymTab.Create[]; FlushPLACache: PUBLIC PROC = {SymTab.Erase[plas]}; ReadPLAFile: PUBLIC PROC [name: IO.ROPE, log:IO.STREAM _ NIL, useCache: BOOL _ FALSE] RETURNS[pla: PLA] = { ENABLE FS.Error => IF error.group = user THEN { log.PutRope[error.explanation]; CONTINUE }; fullPathName: IO.ROPE; plaName: IO.ROPE; rope: IO.ROPE; inName: IO.ROPE; outName: IO.ROPE; iForm: Format; oForm: Format; iFormat: Format; oFormat: Format; checkedOnce: BOOL _ FALSE; length: CARDINAL _ 0; time: BasicTime.GMT; inStm: IO.STREAM; aliasTab: SymTab.Ref _ NIL; IF log = NIL THEN log _ IO.noWhereStream; IF useCache AND SymTab.Fetch[plas, name].found THEN { pla _ CopyPLA[NARROW[SymTab.Fetch[plas, name].val]]; log.PutF["PLA: %g\n", IO.rope[name]]; RETURN[pla]}; fullPathName _ FileNames.FileWithSearchRules[ root: name, defaultExtension: ".ttt", searchRules: LIST[initialWorkingDirectory]].fullPath; plaName _ FileNames.GetShortName[fullPathName]; plaName _ plaName.Substr[0, plaName.Index[0, "."]]; inStm _ FS.StreamOpen[fileName: fullPathName]; IF inStm=NIL THEN RETURN[NIL]; DO ENABLE IO.EndOfStream => EXIT; rope _ IO.GetCedarTokenRope[inStm].token; -- Skips comments SELECT TRUE FROM Rope.Equal[rope, "Name"] => {plaName _ IO.GetTokenRope[inStm].token}; Rope.Equal[rope, "Date"] => {[ ] _ IO.GetChar[inStm]; time _ IO.GetTime[inStm]}; Rope.Equal[rope, "InType"] => {inName _ IO.GetTokenRope[inStm].token; IF inName.Equal["LIST"] THEN DO rope _ IO.GetTokenRope[inStm].token; inName _ inName.Cat[rope]; IF rope.Equal["]"] THEN EXIT; inName _ inName.Cat[" "] ENDLOOP}; Rope.Equal[rope, "OutType"] => {outName _ IO.GetTokenRope[inStm].token; IF outName.Equal["LIST"] THEN DO rope _ IO.GetTokenRope[inStm].token; outName _ outName.Cat[rope]; IF rope.Equal["]"] THEN EXIT; outName _ outName.Cat[" "] ENDLOOP}; Rope.Equal[rope, "Alias"] => { alias: IO.ROPE; rope _ IO.GetTokenRope[inStm].token; alias _ IO.GetTokenRope[inStm].token; -- IF aliasTab=NIL THEN aliasTab _ SymTab.Create[]; []_SymTab.Store[aliasTab, rope, alias]}; Rope.Equal[rope, "Terms"] => {[ ] _ IO.GetChar[inStm]; length _ IO.GetCard[inStm]}; Rope.Equal[rope, "BitFormat"] => { WHILE NOT Rope.Equal[IO.GetTokenRope[inStm].token,"BitTotal"] DO ENDLOOP; [ ] _ IO.GetChar[inStm]; [ ] _ IO.GetTokenRope[inStm]}; Rope.Equal[rope, "TermList"] => {[ ] _ IO.GetChar[inStm]; EXIT}; ENDCASE => {LOOP}; ENDLOOP; IF outName=NIL THEN {log.PutRope["\nI couldn't find 'OutType'\n"]; RETURN[NIL]}; IF inName=NIL THEN {log.PutRope["\nI couldn't find 'InType'\n"]; RETURN[NIL]}; pla _ NewPLA[inName, outName, plaName]; IF pla=NIL THEN {log.PutRope["\nProblem with 'InType' or 'OutType'\n"]; RETURN[NIL]}; pla.time _ time; DO ENABLE IO.EndOfStream => EXIT; rope _ GetLine[inStm]; IF (rope _ TranslateAlias[rope, aliasTab])=NIL THEN LOOP; IF NOT checkedOnce THEN { checkedOnce _ TRUE; [iFormat, oFormat] _ RopeToFormat[rope]; iForm _ REFBit.Desc[pla.data].fieldForm; oForm _ REFBit.Desc[pla.out].fieldForm; }; AppendTerm[RopeToTerm[rope, pla.termList, iForm, oForm], pla.termList]; IF (pla.termList.length MOD 100)=0 THEN log.PutRope["!"] ELSE IF (pla.termList.length MOD 10)=0 THEN log.PutRope["."]; ENDLOOP; IF pla.termList.length # length THEN log.PutF["Number of terms changed to: %g\n", IO.int[pla.termList.length]]; log.PutF["Reading pla on file: %g\n", IO.rope[name]]; log.PutRope[PLAHeader[pla]]; [] _ SymTab.Store[plas, name, CopyPLA[pla]]}; GetLine: PROC[stream: IO.STREAM] RETURNS[line: IO.ROPE _ NIL] = { buffer: REF TEXT = RefText.ObtainScratch[256]; bLen: NAT _ 0; chars: INT _ 0; { ENABLE UNWIND => RefText.ReleaseScratch[buffer]; DO char: CHAR _ stream.GetChar[! IO.EndOfStream => IF chars > 0 THEN EXIT ELSE REJECT]; IF char = IO.CR THEN EXIT; IF char = IO.LF THEN EXIT; chars _ chars + 1; IF bLen = 256 THEN { buffer.length _ bLen; line _ Rope.Concat[line, Rope.FromRefText[buffer]]; bLen _ 0}; buffer[bLen] _ char; bLen _ bLen+1; ENDLOOP}; buffer.length _ bLen; IF bLen # 0 THEN line _ Rope.Concat[line, Rope.FromRefText[buffer]]; RefText.ReleaseScratch[buffer]; RETURN [line]}; TranslateAlias: PROC[rope: IO.ROPE, aliasTab: SymTab.Ref] RETURNS[new: IO.ROPE _ NIL] = { IF rope=NIL OR rope.Length=0 THEN RETURN[NIL]; IF aliasTab=NIL THEN new _ rope ELSE { ris: IO.STREAM _ IO.RIS[rope]; DO ENABLE IO.EndOfStream => EXIT; item: IO.ROPE; skipped: INT; [item, skipped] _ IO.GetTokenRope[ris]; IF item=NIL OR item.Length=0 THEN EXIT; IF SymTab.Fetch[aliasTab, item].found THEN item _ NARROW[SymTab.Fetch[aliasTab, item].val]; IF skipped#0 THEN new _ new.Cat[" "]; new _ new.Cat[item] ENDLOOP} }; WritePLAFile: PUBLIC PROC [pla: PLA, fileName: IO.ROPE_NIL, log: IO.STREAM _ NIL] = { out: IO.STREAM; IF fileName=NIL THEN fileName_pla.name; IF NOT fileName.Substr[fileName.Length[]-4, 4].Equal[".ttt"] THEN fileName _ fileName.Cat[".ttt"]; out _ FS.StreamOpen[fileName, $create]; IF log=NIL THEN log _ IO.noWhereStream; log.PutF["Writing pla on file: %g\n", IO.rope[fileName]]; log.PutRope[PLAHeader[pla]]; WritePLA[pla, out]; out.Close[]}; WritePLA: PUBLIC PROC[pla: PLA, out: IO.STREAM] = { out.PutRope[PLAHeader[pla]]; out.PutRope[REFBit.FormatListing[pla.data]]; out.PutRope[REFBit.FormatListing[pla.out]]; out.PutRope["\nTermList:\n"]; ListTermList[ pla.termList, FALSE, FALSE, out, REFBit.Desc[pla.data].fieldForm, REFBit.Desc[pla.out].fieldForm]}; PLAToExpression: PUBLIC PROC[pla: PLA] RETURNS[expression: Exp] = { outForm: Format _ REFBit.Desc[pla.out].bitForm; inForm: Format _ REFBit.Desc[pla.data].bitForm; mach: LIST OF REF; name: IO.ROPE _ IF pla.name=NIL THEN "MachineName" ELSE pla.name; termIndex: INT _ 0; termTab: RefTab.Ref _ RefTab.Create[]; TermID: TYPE = RECORD[name: IO.ROPE, expr: Exp]; FOR term: Term _ pla.termList.end, term.last WHILE term#NIL DO termDef: LIST OF REF; and: LIST OF REF _ NIL; andEx: REF _ NIL; termID: IO.ROPE _ MakeIndexedId["Term", termIndex, pla.termList.length]; termIndex _ termIndex+1; FOR in: INT DECREASING IN [0..inForm.size) DO q: Qrt; IF (q _ GetInQrt[term, inForm[in].firstBit]) = dontcare THEN LOOP; IF inForm[in].name#NIL THEN IF q = one THEN and _ CONS[inForm[in].name, and] ELSE and _ CONS[LIST[BoolEx.OpNm[not], inForm[in].name], and] ELSE IF q = zero THEN and _ CONS[inForm[in].nameInv, and] ELSE and _ CONS[LIST[BoolEx.OpNm[not], inForm[in].nameInv], and] ENDLOOP; andEx _ IF and.rest=NIL THEN and.first ELSE CONS[BoolEx.OpNm[and], and]; []_RefTab.Store[termTab, term, NEW[TermID _ [termID, andEx]]]; termDef _ LIST[ BoolEx.OpNm[var], termID, andEx]; mach _ CONS[termDef, mach]; ENDLOOP; FOR outIndex: INT DECREASING IN [0..outForm.size) DO out: LIST OF REF; or: LIST OF REF _ NIL; orEx: REF _ NIL; FOR term: Term _ pla.termList.end, term.last WHILE term#NIL DO IF GetOutQrt[term, outForm[outIndex].firstBit]=one THEN { termID: REF TermID _ NARROW[RefTab.Fetch[termTab, term].val]; or _ CONS[termID.name, or]}; ENDLOOP; orEx _ IF or.rest=NIL THEN or.first ELSE CONS[BoolEx.OpNm[or], or]; out _ LIST[ BoolEx.OpNm[out], outForm[outIndex].name, orEx]; mach _ CONS[out, mach]; ENDLOOP; mach _ CONS[BoolEx.OpNm[mach], CONS[name, mach]]; AddSharedInverters[mach]; expression _ mach}; MakeIndexedId: PROC[id: IO.ROPE, index, size: CARDINAL] RETURNS[IO.ROPE] = { RETURN[SELECT size FROM <10 => IO.PutFR["%g%01g", IO.rope[id], IO.card[index]], <100 => IO.PutFR["%g%02g", IO.rope[id], IO.card[index]], <1000 => IO.PutFR["%g%03g", IO.rope[id], IO.card[index]], <10000 => IO.PutFR["%g%04g", IO.rope[id], IO.card[index]], ENDCASE => ERROR]}; MakeDummyRef: PROC[id: IO.ROPE, size: INT] RETURNS[ref: REF] = { name: IO.ROPE _ "LIST["; FOR i: INT IN [0..size) DO name _ name.Cat[" ", MakeIndexedId[id,i,size] ] ENDLOOP; name _ name.Cat[" ]"]; ref _ REFBit.NEWFromName[name]}; SplitPLA: PUBLIC PROC[pla: PLA] RETURNS[andPla, orPla: PLA] = { termRef: REF _ MakeDummyRef["Term", pla.termList.length]; inForm: Format _ REFBit.Desc[pla.data].bitForm; termForm: Format _ REFBit.Desc[termRef].bitForm; outForm: Format _ REFBit.Desc[pla.out].bitForm; defs: LIST OF REF _ NIL; defaultName: IO.ROPE _ "MachineName"; index: INT _ 0; tempAnd: Term; tempOr: Term; andPla _ NewPLA[pla.data, termRef, pla.name.Cat["AndPlane"]]; orPla _ NewPLA[termRef, pla.out, pla.name.Cat["OrPlane"]]; tempAnd _ NewTerm[andPla.termList.inBits, andPla.termList.outBits]; tempOr _ NewTerm[orPla.termList.inBits, orPla.termList.outBits]; FOR term: Term _ pla.termList.begin, term.next WHILE term#NIL DO SetOutQrt[one, tempAnd, termForm[index].firstBit]; tempAnd.in _ term.in; ACopy[tempAnd, andPla.termList]; SetOutQrt[zero, tempAnd, termForm[index].firstBit]; index _ index+1 ENDLOOP; FOR out: INT IN [0..outForm.size) DO index _ 0; SetOutQrt[one, tempOr, outForm[out].firstBit]; FOR term: Term _ pla.termList.begin, term.next WHILE term#NIL DO IF GetOutQrt[term, outForm[out].firstBit]=one THEN SetInQrt[one, tempOr, termForm[index].firstBit]; index _ index+1 ENDLOOP; ACopy[tempOr, orPla.termList]; FOR inWd: INT IN [0..tempOr.in.wdSize) DO tempOr.in[inWd] _ initInQrtWdc ENDLOOP; SetOutQrt[zero, tempOr, outForm[out].firstBit]; ENDLOOP; [ ] _ ConvertTermListToCompleteSum[andPla.termList, FALSE, FALSE]; [ ] _ FindAMinimalCover[andPla.termList, 120]; [ ] _ ConvertTermListToCompleteSum[orPla.termList, FALSE, FALSE]; [ ] _ FindAMinimalCover[orPla.termList, 120]}; Array: TYPE = REF ArrayRec; ArrayRec: TYPE = RECORD[ maxGates: INT, colNm: SeqNm, seq: SEQUENCE size: CARDINAL OF Row]; Row: TYPE = REF RowRec; RowRec: TYPE = RECORD [expr: LIST OF IO.ROPE, marked: INT, smallest: INT, seq: SEQUENCE size: CARDINAL OF BOOL]; SeqNm: TYPE = REF SeqNmRec; SeqNmRec: TYPE = RECORD[SEQUENCE size: CARDINAL OF IO.ROPE]; Factor: PUBLIC PROC[pla: PLA] RETURNS[mach: Exp] = { inForm: Format _ REFBit.Desc[pla.data].bitForm; outForm: Format _ REFBit.Desc[pla.out].bitForm; andXDim: INT _ 2*inForm.size; andYDim: INT _ pla.termList.length; orXDim: INT _ pla.termList.length; orYDim: INT _ outForm.size; badRow: INT _ -1; levelSum: INT _ 0; defs: LIST OF REF; andNm: IO.ROPE _ "Factor"; termNm: IO.ROPE _ "Term"; orNm: IO.ROPE _ "Sum"; machName: IO.ROPE _ IF pla.name=NIL THEN "MachineName" ELSE pla.name; index: INT _ 0; gateOp: OpIndex _ nand; -- The last AND plane gateOp is the first OR plane gateOp. andAry: Array _ NEW[ArrayRec[andYDim]]; orAry: Array _ NEW[ArrayRec[orYDim]]; andAry.colNm _ NEW[SeqNmRec[andXDim]]; orAry.colNm _ NEW[SeqNmRec[orXDim]]; FOR col: INT IN [0..andXDim) DO input: INT _ IF col < inForm.size THEN col ELSE col-inForm.size; name: IO.ROPE _ inForm[input].name; nameInv: IO.ROPE _ Rope.Cat["~", name]; andAry.colNm[col] _ IF col < inForm.size THEN name ELSE nameInv ENDLOOP; FOR col: INT IN [0..orXDim) DO name: IO.ROPE _ MakeIndexedId[termNm, col, orXDim]; orAry.colNm[col] _ name ENDLOOP; FOR row: INT IN [0..andYDim) DO andAry[row] _ NEW[RowRec[andXDim]]; andAry[row].expr _ NIL; andAry[row].marked _ 0 ENDLOOP; FOR row: INT IN [0..orYDim) DO orAry[row] _ NEW[RowRec[orXDim]]; orAry[row].expr _ NIL; orAry[row].marked _ 0 ENDLOOP; FOR term: Term _ pla.termList.begin, term.next WHILE term#NIL DO FOR in: INT IN [0..inForm.size) DO q: Qrt _ GetInQrt[term, inForm[in].firstBit]; andAry[index][in] _ q=one; andAry[index][in+inForm.size] _ q=zero; ENDLOOP; FOR out: INT IN [0..outForm.size) DO q: Qrt _ GetOutQrt[term, outForm[out].firstBit]; orAry[out][index] _ q=one; ENDLOOP; index _ index + 1 ENDLOOP; FOR initOp: OpIndex IN [and..or] DO ary: Array _ IF initOp=and THEN andAry ELSE orAry; baseNm: IO.ROPE _ IF initOp=and THEN andNm ELSE orNm; maxExposed: INT _ 0; IF initOp#and AND initOp#or THEN LOOP; maxExposed _ 0; FOR row: INT IN [0..ary.size) DO cnt: INT _ 0; FOR col: INT IN [0..ary[row].size) DO IF ary[row][col] THEN cnt _ cnt+1 ENDLOOP; maxExposed _ MAX[cnt, maxExposed] ENDLOOP; ary.maxGates _ 1; WHILE maxExposed > 4 DO maxExposed _ (maxExposed-1)/4+1; ary.maxGates _ ary.maxGates*4 ENDLOOP; FOR level: INT _ 0, level+1 DO new: Array; nextColHeads: LIST OF IO.ROPE; newCols: INT _ 0; gateCnt: INT _ 0; levelSum _ levelSum + 1; FOR row: INT IN [0..ary.size) DO []_GetRowParmsAndSetSmallest[ary, row] ENDLOOP; DO nameIf2OrMore: IO.ROPE _ IO.PutFR["%g%gx%03g", IO.rope[baseNm], IO.int[level], IO.int[gateCnt]]; def: LIST OF IO.ROPE _ AddBestGate4[ary, nameIf2OrMore]; IF def = NIL THEN EXIT; IF def.rest=NIL THEN { name: IO.ROPE _ InvertName[def.first]; IF RopeList.Memb[nextColHeads, name] THEN ERROR; -- check assumption nextColHeads _ CONS[name, nextColHeads]} ELSE { list, term: LIST OF REF; FOR def _ def, def.rest WHILE def#NIL DO list _ CONS[def.first, list] ENDLOOP; list _ CONS[BoolEx.OpNm[gateOp], list]; term _ LIST[BoolEx.OpNm[var], nameIf2OrMore, list]; defs _ CONS[term, defs]; nextColHeads _ CONS[nameIf2OrMore, nextColHeads]; gateCnt _ gateCnt+1}; ENDLOOP; IF GetMaxGates[ary] NOT IN (ary.maxGates/4..ary.maxGates] THEN ERROR; IF (ary.maxGates = 1) THEN { FOR row: INT IN [0..ary.size) DO name: IO.ROPE _ IF initOp=and THEN orAry.colNm[row] ELSE outForm[row].name; def: REF _ ary[row].expr.first; IF ary[row].expr.rest#NIL THEN ERROR; IF (initOp=or) AND ((levelSum MOD 2)=1) THEN {inv: LIST OF REF _ LIST[BoolEx.OpNm[not], def]; def _ inv}; def _ LIST[BoolEx.OpNm[(IF initOp=and THEN var ELSE out)], name, def]; defs _ CONS[def, defs]; ENDLOOP; EXIT}; gateOp _ IF gateOp=nand THEN nor ELSE nand; -- must be after test for loop exit new _ NEW[ArrayRec[ary.size]]; newCols _ RopeList.Length[nextColHeads]; new.colNm _ NEW[SeqNmRec[newCols]]; new.maxGates _ ary.maxGates/4; FOR col: INT IN [0..newCols) DO new.colNm[col] _ nextColHeads.first; nextColHeads _ nextColHeads.rest ENDLOOP; FOR row: INT IN [0..ary.size) DO new[row] _ NEW[RowRec[newCols]]; new[row].expr _ NIL; new[row].marked _ 0; FOR col: INT IN [0..newCols) DO new[row][col] _ RopeList.Memb[ary[row].expr, new.colNm[col]] ENDLOOP; ENDLOOP; ary _ new; ENDLOOP; ENDLOOP; defs _ CONS[BoolEx.OpNm[mach], CONS[machName, defs]]; AddSharedInverters[defs]; BoolEx.Sort[defs]; mach _ defs}; AddBestGate4: PROC[ary: Array, nameIf2OrMore: IO.ROPE] RETURNS[gate: LIST OF IO.ROPE] = { gateExpr: IO.ROPE _ NIL; bestFanIn: INT _ 1; fanCnts: ARRAY [1..4] OF INT _ ALL[0]; fanCols: ARRAY [1..4] OF INT _ ALL[-1]; FOR fanIn: INT IN [1..4] DO maxCol: INT _ -1; maxCnt: INT _ 0; FOR col: INT IN [0..ary.colNm.size) DO cnt: INT _ 0; FOR fc: INT IN [1..fanIn) DO IF fanCols[fc]=col THEN GOTO Loop REPEAT Loop => LOOP ENDLOOP; FOR row: INT IN [0..ary.size) DO IF NOT ary[row].marked=(fanIn-1) THEN LOOP; IF ary[row][col] THEN cnt _ cnt + 1 ENDLOOP; IF cnt > maxCnt THEN {maxCnt _ cnt; maxCol _ col} ENDLOOP; IF maxCnt = 0 THEN IF fanIn=1 THEN RETURN[NIL] ELSE EXIT; fanCnts[fanIn] _ maxCnt; fanCols[fanIn] _ maxCol; FOR row: INT IN [0..ary.size) DO IF ary[row].marked=(fanIn-1) AND ary[row][maxCol] THEN ary[row].marked _ ary[row].marked+1 ENDLOOP; ENDLOOP; FOR row: INT IN [0..ary.size) DO FOR fanIn: INT IN [1..4) DO IF ary[row].marked < fanIn THEN LOOP; IF fanCols[fanIn]#-1 AND ary[row].smallest > fanIn THEN fanCnts[fanIn] _ fanCnts[fanIn]-1 ENDLOOP ENDLOOP; FOR fanIn: INT IN (1..4] DO IF fanIn*fanCnts[fanIn] >= bestFanIn*fanCnts[bestFanIn] THEN bestFanIn _ fanIn; ENDLOOP; -- use larger gates if equal FOR fanIn: INT IN [1..bestFanIn] DO gate _ CONS[ary.colNm[fanCols[fanIn]], gate] ENDLOOP; gateExpr _ IF bestFanIn>1 THEN nameIf2OrMore ELSE InvertName[gate.first]; FOR row: INT IN [0..ary.size) DO IF bestFanIn IN [ary[row].smallest .. ary[row].marked] THEN { ary[row].expr _ CONS[gateExpr, ary[row].expr]; FOR fanIn: INT IN [1..bestFanIn] DO ary[row][fanCols[fanIn]] _ FALSE ENDLOOP}; []_GetRowParmsAndSetSmallest[ary, row]; ary[row].marked _ 0; ENDLOOP}; AddSharedInverters: PROC[mach: LIST OF REF] = { ConvertInvToList: PROC[ref: REF] RETURNS[REF] = { WITH ref SELECT FROM rope: IO.ROPE => IF rope.Fetch[]='~ THEN RETURN[LIST[BoolEx.OpNm[not], ConvertInvToList[rope.Substr[1]]]] ELSE RETURN[rope]; elist: LIST OF REF => { FOR elist _ elist.rest, elist.rest WHILE elist#NIL DO elist.first _ ConvertInvToList[elist.first] ENDLOOP; RETURN[ref]}; ENDCASE => ERROR}; RemoveDoubleInvs: PROC[ref: REF] RETURNS[REF] = { WITH ref SELECT FROM rope: IO.ROPE => RETURN[rope]; elist: LIST OF REF => { op: OpIndex _ BoolEx.NmOp[NARROW[elist.first]]; SELECT op FROM not => { elist.rest.first _ RemoveDoubleInvs[elist.rest.first]; WITH elist.rest.first SELECT FROM rp: IO.ROPE => RETURN[ref]; el: LIST OF REF => { op _ BoolEx.NmOp[NARROW[el.first]]; SELECT op FROM not => RETURN[el.rest.first] ENDCASE => RETURN[ref]}; ENDCASE => ERROR}; ENDCASE => { FOR elist _ elist.rest, elist.rest WHILE elist#NIL DO elist.first _ RemoveDoubleInvs[elist.first] ENDLOOP; RETURN[ref]} }; ENDCASE => ERROR}; UseInvDefs: PROC[ref: REF] RETURNS[REF] = { WITH ref SELECT FROM rope: IO.ROPE => RETURN[ref]; elist: LIST OF REF => { op: OpIndex _ BoolEx.NmOp[NARROW[elist.first]]; SELECT op FROM not => { elist.rest.first _ UseInvDefs[elist.rest.first]; WITH elist.rest.first SELECT FROM rp: IO.ROPE => { invNm: IO.ROPE _ IO.PutFR["NOT%g", IO.rope[rp]]; IF SymTab.Insert[invTab, rp, invNm] THEN { invDef: LIST OF REF _ LIST[BoolEx.OpNm[var], invNm, ref]; defs _ CONS[invDef, defs]}; RETURN[invNm]}; el: LIST OF REF => RETURN[ref] ENDCASE => ERROR}; ENDCASE => { FOR elist _ elist.rest, elist.rest WHILE elist#NIL DO elist.first _ UseInvDefs[elist.first] ENDLOOP; RETURN[ref]} }; ENDCASE => ERROR}; invTab: SymTab.Ref _ SymTab.Create[]; defs: LIST OF REF _ mach.rest.rest; FOR dfs: LIST OF REF _ defs, dfs.rest WHILE dfs#NIL DO def: LIST OF REF _ NARROW[dfs.first]; def.rest.rest.first _ ConvertInvToList[def.rest.rest.first]; ENDLOOP; FOR dfs: LIST OF REF _ defs, dfs.rest WHILE dfs#NIL DO def: LIST OF REF _ NARROW[dfs.first]; def.rest.rest.first _ RemoveDoubleInvs[def.rest.rest.first]; ENDLOOP; FOR dfs: LIST OF REF _ defs, dfs.rest WHILE dfs#NIL DO def: LIST OF REF _ NARROW[dfs.first]; def.rest.rest.first _ UseInvDefs[def.rest.rest.first]; ENDLOOP; mach.rest.rest _ defs}; InvertName: PROC[name: IO.ROPE ] RETURNS[IO.ROPE] = {RETURN[IF name.Fetch[]='~ THEN name.Substr[1] ELSE Rope.Cat["~", name]]}; RopesToRefs: PROC[ropes: LIST OF IO.ROPE] RETURNS[refs: LIST OF REF] = { IF ropes=NIL THEN RETURN[NIL]; RETURN[CONS[ropes.first, RopesToRefs[ropes.rest]]]}; GetMaxGates: PROC[arry: Array] RETURNS[max: INT _ 0] = { FOR row: INT IN [0..arry.size) DO max _ MAX[max, RopeList.Length[arry[row].expr]] ENDLOOP}; GetRowParmsAndSetSmallest: PROC[arry: Array, row: INT] RETURNS[nofGates, exposed: INT _ 0] = { FOR col: INT IN [0..arry[row].size) DO IF arry[row][col] THEN exposed _ exposed+1 ENDLOOP; FOR list: LIST OF IO.ROPE _ arry[row].expr, list.rest WHILE list#NIL DO nofGates_nofGates+1 ENDLOOP; arry[row].smallest _ MAX[1, (exposed - (arry.maxGates-nofGates-1)*4)]; IF arry[row].smallest>4 THEN ERROR}; ShowArray: PROC[ary: Array, log: IO.STREAM] = { -- for debugging FOR row: INT DECREASING IN [0..ary.size) DO nofGates, exposed: INT; [nofGates, exposed] _ GetRowParmsAndSetSmallest[ary, row]; log.PutF["%2g g:%g e:%2g s:%g m:%2g ", IO.int[row], IO.int[nofGates], IO.int[exposed], IO.int[ary[row].smallest], IO.int[ary[row].marked]]; FOR col: INT IN [0..ary[row].size) DO log.PutChar[IF ary[row][col] THEN '1 ELSE '.] ENDLOOP; log.PutChar[IO.CR] ENDLOOP}; CTCompressPLA: PUBLIC PROC[pla: PLA, complete: BOOL _ FALSE] RETURNS[new: PLA] = { name: IO.ROPE _ (IF pla.name.Length[]=0 THEN "Temp" ELSE pla.name); cmdLine: IO.ROPE; cmdLine _ IF complete THEN IO.PutFR["PLAOpsCompress -c %g", IO.rope[name]] ELSE IO.PutFR["PLAOpsCompress %g", IO.rope[name]]; PLAOps.WritePLAFile[pla]; []_CommandTool.DoCommand[cmdLine, NIL]; new _ PLAOps.ReadPLAFile[name.Cat[".com.ttt"], IO.noWhereStream]}; FactorExpression: PUBLIC PROC[mach: Exp] RETURNS[newMach: Exp] = { pla: PLA _ ExpressionToPLA[mach]; newMach _ Factor[pla]}; CompressExpression: PUBLIC PROC[mach: Exp, complete: BOOL _ FALSE] RETURNS[newMach: Exp] = { machName: IO.ROPE _ NARROW[NARROW[mach, LIST OF REF].rest.first]; pla: PLA _ ExpressionToPLA[mach]; IF complete THEN pla _ CTCompressPLA[pla, TRUE] ELSE { [] _ ConvertTermListToCompleteSum[pla.termList, FALSE, FALSE]; PLAOps.WritePLAFile[pla]}; newMach _ PLAToExpression[pla]}; ExpressionToPLA: PUBLIC PROC[expression: Exp] RETURNS[pla: PLA] = { TwoTermIns: TYPE = RECORD[in0, in1: PLAOps.QWSeq]; inName: IO.ROPE _ "LIST[ "; outName: IO.ROPE _ "LIST[ "; inNames: LIST OF IO.ROPE _ NIL; outNames: LIST OF IO.ROPE _ NIL; outForm: Format; inForm: Format; temp: Term; terms: PLAOps.TermList _ NIL; machName: IO.ROPE; inTab: SymTab.Ref; outTab: SymTab.Ref; tranStateTab: SymTab.Ref _ SymTab.Create[]; uniqueIdx: INT _ 0; MkInNm: SymTab.EachPairAction = {IF val=key THEN inNames _ CONS[key, inNames]}; MkOutNmAndCollectTranStates: SymTab.EachPairAction = { outNames _ CONS[key, outNames]; val _ CollectTranStates[key, val]; IF val#NIL THEN { -- pretend the remaining expression had a Term Def uniqueNm: IO.ROPE _ IO.PutFR["uniqueName%g", IO.int[uniqueIdx]]; uniqueIdx_uniqueIdx+1; IF ~SymTab.Insert[inTab, uniqueNm, val] THEN ERROR; AppendSymTabRpItem[tranStateTab, uniqueNm, key]}}; CollectTranStates: PROC[out: IO.ROPE, e: Exp] RETURNS[Exp] = { WITH e SELECT FROM rope: IO.ROPE => {AppendSymTabRpItem[tranStateTab, rope, out]; RETURN[NIL]}; elist: LIST OF REF => IF BoolEx.NmOp[NARROW[elist.first]]#or THEN RETURN[e] ELSE { ls: LIST OF REF _ elist; WHILE ls.rest#NIL DO ls.rest.first _ CollectTranStates[out, ls.rest.first]; IF ls.rest.first=NIL THEN ls.rest _ ls.rest.rest ELSE ls _ ls.rest ENDLOOP; RETURN[IF elist.rest=NIL THEN NIL ELSE elist]}; ENDCASE => ERROR}; AppendSymTabRpItem: PROC[tab: SymTab.Ref, key, item: IO.ROPE] = { list: LIST OF IO.ROPE _ NARROW[SymTab.Fetch[tab, key].val]; list _ CONS[item, list]; []_SymTab.Store[tab, key, list]}; AddTranStateOutsToPla: SymTab.EachPairAction = { Build: PROC[e: REF] RETURNS[be: TermList] = { WITH e SELECT FROM in: REF INT => { be _ NEW[TermListRec _ [inBits: pla.termList.inBits, outBits: pla.termList.outBits]]; SetInQrt[one, temp, inForm[in^].firstBit]; ACopy[temp, be]; SetInQrt[dontcare, temp, inForm[in^].firstBit]}; rope: IO.ROPE => RETURN[Build[SymTab.Fetch[inTab, rope].val]]; elist: LIST OF REF => { op: OpIndex _ BoolEx.NmOp[NARROW[elist.first]]; be _ SELECT op FROM or, nor => OrFalse, and, nand => AndTrue, not => NIL, ENDCASE => ERROR; FOR elist _ elist.rest, elist.rest WHILE elist#NIL DO arg: TermList _ Build[elist.first]; SELECT op FROM or, nor => be _ Or [arg, be]; and, nand => be _ And [arg, be]; not => be _ Not [arg]; ENDCASE => ERROR ENDLOOP; SELECT op FROM nor, nand => be _ Not[be] ENDCASE}; ENDCASE => ERROR}; outs: LIST OF IO.ROPE _ NARROW[val]; outIdxs: LIST OF INT _ NIL; FOR outIdx: INT IN [0..outForm.size) DO IF RopeList.Memb[outs, outForm[outIdx].name] THEN outIdxs _ CONS[outIdx, outIdxs] ENDLOOP; FOR ls: LIST OF INT _ outIdxs, ls.rest WHILE ls#NIL DO SetOutQrt[one, temp, outForm[ls.first].firstBit] ENDLOOP; terms _ Build[SymTab.Fetch[inTab, key].val]; FOR term: Term _ terms.begin, term.next WHILE term#NIL DO [temp.in, term.in] _ TwoTermIns[term.in, temp.in]; ACopy[temp, pla.termList]; [temp.in, term.in] _ TwoTermIns[term.in, temp.in] ENDLOOP; FOR ls: LIST OF INT _ outIdxs, ls.rest WHILE ls#NIL DO SetOutQrt[zero, temp, outForm[ls.first].firstBit] ENDLOOP}; IF expression=NIL THEN RETURN[NIL]; expression _ BoolEx.Copy[expression]; [machName, inTab, outTab] _ BoolEx.GetExpressionTables[expression]; []_SymTab.Pairs[inTab, MkInNm]; []_SymTab.Pairs[outTab, MkOutNmAndCollectTranStates]; inNames _ RopeList.Sort[inNames, RopeList.IgnoreCase]; outNames _ RopeList.Sort[outNames, RopeList.IgnoreCase]; FOR inNames _ inNames, inNames.rest WHILE inNames#NIL DO inName _ inName.Cat[inNames.first, " "] REPEAT FINISHED => inName _ inName.Cat["]"] ENDLOOP; FOR outNames _ outNames, outNames.rest WHILE outNames#NIL DO outName _ outName.Cat[outNames.first, " "] REPEAT FINISHED => outName _ outName.Cat["]"] ENDLOOP; pla _ NewPLA[inName, outName, machName]; outForm _ REFBit.Desc[pla.out].bitForm; inForm _ REFBit.Desc[pla.data].bitForm; temp _ NewTerm[pla.termList.inBits, pla.termList.outBits]; FOR index: INT IN [0..temp.in.wdSize) DO temp.in[index] _ initInQrtWdc ENDLOOP; FOR in: INT IN [0..inForm.size) DO IF SymTab.Store[inTab, inForm[in].name, NEW[INT _ in]] THEN ERROR ENDLOOP; []_SymTab.Pairs[tranStateTab, AddTranStateOutsToPla]}; verbose: BOOL _ FALSE; PLAHeader: PUBLIC PROC [pla: PLA] RETURNS[header: IO.ROPE] = { out: IO.STREAM _ IO.ROS[]; out.PutRope["\n"]; IF pla.name#NIL THEN -- notice this is just for next line out.PutF["Name: %g\n", IO.rope[pla.name]]; out.PutF["Date: %g\n", IO.time[pla.time]]; out.PutF["InType: %g\n", IO.rope[REFBit.Desc[pla.data].typeName]]; out.PutF["OutType: %g\n", IO.rope[REFBit.Desc[pla.out].typeName]]; out.PutF["Terms: %g\n", IO.card[pla.termList.length]]; header _ IO.RopeFromROS[out]}; StripDirFromName: PROC[ file: IO.ROPE] RETURNS[IO.ROPE] = { index: INT_0; WHILE index>=0 DO index _ file.Find[s2: ">"]; file _ file.Substr[start: index+1] ENDLOOP; RETURN[file]}; DefaultCMDLine: PUBLIC PROC[ cmdLine, default: IO.ROPE] RETURNS[IO.ROPE] = { DO ENABLE IO.EndOfStream => EXIT; default _ IO.GetTokenRope[IO.RIS[cmdLine]].token; EXIT; ENDLOOP; RETURN[default]}; initialWorkingDirectory: IO.ROPE _ FileNames.CurrentWorkingDirectory[]; EqualExpr: PUBLIC PROC[e0, e1: Exp] RETURNS[equal: BOOL] = { pla0: PLA _ ExpressionToPLA[e0]; pla1: PLA _ ExpressionToPLA[e1]; nm0: IO.ROPE _ IF e0=NIL THEN NIL ELSE NARROW[NARROW[e0, LIST OF REF].rest.first]; nm1: IO.ROPE _ IF e1=NIL THEN NIL ELSE NARROW[NARROW[e1, LIST OF REF].rest.first]; IF e0=e1 THEN RETURN[TRUE]; IF e0=NIL OR e1=NIL THEN RETURN[FALSE]; IF NOT Rope.Equal[nm0, nm1] THEN RETURN[FALSE]; RETURN[EqualPLA[pla0, pla1]]}; EqualPLA: PUBLIC PROC[pla0, pla1: PLA] RETURNS[equal: BOOL] = { inForm: Format; outForm: Format; IF pla0=pla1 THEN RETURN[TRUE]; IF pla0=NIL OR pla1=NIL THEN RETURN[FALSE]; inForm _ REFBit.Desc[pla0.data].bitForm; outForm _ REFBit.Desc[pla0.out].bitForm; pla1 _ ReconfigurePLA[pla1, pla0.data, pla0.out]; IF pla1=NIL THEN RETURN[FALSE]; FOR out: INT IN [0..outForm.size) DO t0, t1: Term; be0: BoolExpr _ GetOutputBE[pla0, outForm[out].firstBit]; be1: BoolExpr _ GetOutputBE[pla1, outForm[out].firstBit]; [] _ ConvertTermListToCompleteSum[be0, TRUE, TRUE]; [] _ ConvertTermListToCompleteSum[be1, TRUE, TRUE]; IF be0.length # be1.length THEN RETURN[TRUE]; t0 _ be0.begin; t1 _ be1.begin; FOR term: INT IN [0..be0.length) DO IF NOT EqualTerm[t0, t1] THEN RETURN[FALSE]; t0 _ t0.next; t1 _ t1.next ENDLOOP ENDLOOP; RETURN[TRUE]}; EqualTerm: PUBLIC PROC[term0, term1: Term] RETURNS[equal: BOOL] = { IF term0=term1 THEN RETURN[TRUE]; IF term0=NIL OR term1=NIL THEN RETURN[FALSE]; IF term0.in.wdSize # term1.in.wdSize THEN RETURN[FALSE]; IF term0.out.wdSize # term1.out.wdSize THEN RETURN[FALSE]; FOR in: INT IN [0..term0.in.wdSize) DO IF term0.in[in]#term1.in[in] THEN RETURN[FALSE] ENDLOOP; FOR out: INT IN [0..term0.out.wdSize) DO IF term0.out[out]#term1.out[out] THEN RETURN[FALSE] ENDLOOP; RETURN[TRUE]}; END. ΈPLAOpsImplD.mesa Copyright c 1984 by Xerox Corporation. All rights reserved. Don Curry April 30, 1987 10:20:23 pm PDT Last Edited by: Don Curry July 18, 1988 2:22:02 pm PDT IF iFormat.size # iForm.size THEN {SIGNAL InvalidTermRope[rope]; EXIT}; IF oFormat.size # oForm.size THEN {SIGNAL InvalidTermRope[rope]; EXIT}; FOR i: NAT IN [0..iFormat.size) DO IF iFormat[i].bitSize#iForm[i].bitSize THEN {SIGNAL InvalidTermRope[rope]; EXIT}; REPEAT Exit => EXIT ENDLOOP; FOR i: NAT IN [0..oFormat.size) DO IF oFormat[i].bitSize#oForm[i].bitSize THEN {SIGNAL InvalidTermRope[rope]; EXIT}; This doesn't seem to be a very useful function. Or plane: Making each term correspond to independent outputs can't help minimize the or plane. And plane: Making the terms correspond to inputs just seems to increase the connections in the ouput plane. Init row params Find fanCols and fanCnts; Reduce fanCnts for smaller fanIns; Find best fanIn; Build Gate Add Gate Κ+˜šΠbl™Jšœ<™Jšœ žœžœžœ˜Jš œžœžœžœžœ˜Jšœžœžœ˜Jšœžœžœ9˜HJšœ˜š žœžœž œžœž˜-Jšœ˜Jšžœ6žœžœ˜Bšžœž˜šžœžœ˜Jšžœžœ ˜/Jšžœžœžœ*˜>—šžœžœ ˜Jšžœžœ"˜1Jšžœžœžœ,˜@——Jšžœ˜—Jš œžœ žœžœ žœžœ˜HJšœžœ˜>Jšœ žœ#˜1Jšœžœ˜Jšžœ˜—š žœ žœž œžœž˜4Jšœžœžœžœ˜Jš œžœžœžœžœ˜Jšœžœžœ˜šžœ*žœžœž˜>šžœ1žœ˜9Jšœžœ žœ"˜=Jšœžœ˜—Jšžœ˜—Jš œžœ žœžœ žœžœ˜CJšœžœ2˜—šžœžœžœž˜ Jšžœžœžœžœ˜+Jšžœžœžœ˜,—Jšžœžœžœ˜:—Jšžœ žœžœ žœžœžœžœžœ˜9Jšœ˜Jšœ˜šžœžœžœž˜ šžœžœ˜1Jšžœ%žœ˜1——Jšžœ˜—Jš£"™"šžœžœžœž˜ šžœžœžœž˜Jšžœžœžœ˜%šžœžœ˜2Jšžœ#žœžœ˜7———Jš£™šžœžœžœž˜Jšžœ6žœ˜OJšžœ‘˜%—Jš£ ™ šžœžœžœž˜#Jšœžœ"žœ˜5—šœ žœ ˜Jšžœ˜Jšžœ˜—Jš£™šžœžœžœž˜ šžœ žœ(žœ˜=Jšœžœ˜.Jš žœžœžœžœžœžœ˜N—Jšœ'˜'Jšœ˜Jšžœ˜ ——J˜š  œžœžœžœžœ˜/š  œžœžœžœžœ˜1šžœžœž˜šœžœžœžœ˜%Jšžœžœžœ5˜EJšžœžœ˜—šœžœžœžœ˜šžœ žœžœž˜5Jšœ,žœ˜4—Jšžœ˜ —Jšžœžœ˜——š  œžœžœžœžœ˜1šžœžœž˜Jšœžœžœžœ˜ šœžœžœžœ˜Jšœžœ˜/šžœž˜šœ ˜ Jšœ6˜6šžœžœž˜!Jšœžœžœžœ˜šœžœžœžœ˜Jšœžœ ˜#šžœž˜Jšœžœ˜Jšžœžœ˜——Jšžœžœ˜——šžœ˜ šžœ žœžœž˜5Jšœ,žœ˜4—Jšžœ ˜———Jšžœžœ˜——š   œžœžœžœžœ˜+šžœžœž˜Jšœžœžœžœ˜šœžœžœžœ˜Jšœžœ˜/šžœž˜šœ ˜ Jšœ0˜0šžœžœž˜!šœžœžœ˜Jš œžœžœžœžœ ˜0šžœ"žœ˜*Jš œžœžœžœžœ˜9Jšœžœ˜—Jšžœ ˜—Jš œžœžœžœžœ˜Jšžœžœ˜——šžœ˜ šžœ žœžœž˜5Jšœ&žœ˜.—Jšžœ ˜———Jšžœžœ˜——Jšœ%˜%Jšœžœžœžœ˜#š žœžœžœžœžœžœž˜6Jš œžœžœžœžœ ˜%Jšœ=žœ˜E—š žœžœžœžœžœžœž˜6Jš œžœžœžœžœ ˜%Jšœ=žœ˜E—š žœžœžœžœžœžœž˜6Jš œžœžœžœžœ ˜%Jšœ7žœ˜?—Jšœ˜—J˜š  œžœžœžœžœžœžœ˜4Jš œžœžœžœžœ˜J—J˜š  œžœžœžœžœžœžœžœžœžœ˜HJš žœžœžœžœžœ˜Jšžœžœ)˜4—J˜š  œžœžœžœ ˜8šžœžœžœž˜!Jšœžœ'žœ˜9——J˜š œžœžœ˜6Jšžœžœ ˜'Jšžœžœžœžœžœžœžœ˜Zš žœžœžœžœžœžœž˜DJšžœžœ˜—Jšœžœ.˜FJšžœžœžœ˜$—J˜š   œžœžœžœ‘˜@š žœžœž œžœž˜+Jšœžœ˜Jšœ:˜:šœ(˜(Jšžœ ˜ Jšžœ˜Jšžœ˜Jšžœ˜Jšžœ˜—šžœžœžœž˜%Jš œ žœžœžœžœ˜6—Jšœ žœžœžœ˜——J˜š  œžœžœžœ žœžœžœžœ˜RJš œžœžœžœžœžœ ˜DJšœ žœžœ˜šœ žœ ˜Jšžœžœžœ ˜4Jšžœžœžœ ˜3—Kšœ˜Jšœ"žœ˜'Kšœ/žœ˜B—J˜š€œžœžœ žœ˜BJšœžœ˜!Jšœ˜—J˜š  œžœžœžœžœ˜BJšžœ˜Jšœ žœžœžœžœžœžœžœ˜AJšœžœ˜$šžœ ˜ Jšžœžœ˜#šžœ˜Jšœ0žœžœ˜>Kšœ˜——Jšœ ˜ —J˜š  œžœžœžœžœ˜CJšœ žœžœ˜2Jšœ žœžœ ˜Jšœ žœžœ ˜Jš œ žœžœžœžœžœ˜ Jš œ žœžœžœžœžœ˜ Jšœ˜Jšœ˜Jšœ ˜ Jšœžœ˜Jšœ žœžœ˜Jšœ˜Jšœ˜Jšœ+˜+Jšœ žœ˜Jš œžœ žœ žœ˜Oš œ˜6Jšœ žœ˜Jšœ#˜#šžœžœžœ‘2˜DJš œ žœžœžœžœ˜@Jšœ˜Jšžœ&žœžœ˜3Jšœ2˜2——š  œžœžœžœ žœ ˜>šžœžœž˜šœžœžœ˜Jšœ.žœžœ˜;—š œžœžœžœžœ žœ˜˜>—Jšœ*˜*Jšœ˜Jšœ0˜0—Jšœžœžœžœ'˜>šœžœžœžœ˜Jšœžœ˜/šœžœž˜Jšœ˜Jšœ˜Jšœ žœ˜ Jšžœžœ˜—šžœ žœžœž˜5Jšœ#˜#šžœž˜Jšœ˜Jšœ ˜ Jšœ˜Jšžœžœžœ˜——šžœž˜Jšœžœ˜$——Jšžœžœ˜——Jš œžœžœžœžœžœ˜%Jš œ žœžœžœžœ˜šžœ žœžœž˜'šžœ*˜,Jšžœ žœžœ˜-——š žœžœžœžœžœžœž˜6Jšœ1žœ˜9—Jšœ,˜,šžœ%žœžœž˜9J˜2Jšœ˜Jšœ2žœ˜:—š žœžœžœžœžœžœž˜6Jšœ2žœ˜;——Jš žœ žœžœžœžœ˜#Jšœ%˜%JšœC˜CJšœ ˜ Jšœ5˜5Jšœ6˜6Jšœ8˜8šžœ!žœ žœž˜8Jšœ'˜'Jšžœžœžœ˜4—šžœ$žœ žœž˜Jš œžœžœžœžœ˜Jš’˜Jš Πfk’œ’₯’₯’Πcf$˜9Jš’₯’œ’˜.Jš’₯’˜.Jš’₯’'˜DJš’₯’&˜CJš’₯’˜9Jšœ žœ˜—J˜š œžœžœžœžœžœžœ˜;Jšœžœ˜ šžœ ž˜Jšœ˜Jšœ#žœ˜+—Jšžœ˜—J˜š œžœžœžœžœžœžœžœ˜Lšž˜JšžœžœΟržœ˜Jš œ žœžœžœžœžœ˜@—Jšžœ ˜—J˜Jšœžœžœ'˜GJ˜š   œžœžœžœžœ˜>Jšœžœ˜ Jšœžœ˜ Jšœžœžœžœžœžœžœžœžœžœžœžœžœ˜RJšœžœžœžœžœžœžœžœžœžœžœžœžœ˜RJšžœžœžœžœ˜Jšžœžœžœžœžœžœžœ˜'Jš žœžœžœžœžœ˜/Jšžœ˜—J˜š  œžœžœ žœžœžœ˜BJšœ˜Jšœ˜Jšžœ žœžœžœ˜Jšžœžœžœžœžœžœžœ˜+Jšœ(˜(Jšœ(˜(Jšœ £œ˜6Jš žœžœžœžœžœ˜šžœžœžœž˜$Jšœ ˜ Jšœ9˜9Jšœ9˜9Jšœ'žœžœ˜3Jšœ'žœžœ˜3Jšžœžœžœžœ˜-Jšœ˜Jšœ˜šžœžœžœ˜ Jš žœžœžœžœžœžœ˜JJšžœžœ˜——Jšžœžœ˜—J˜š   œžœžœžœžœ˜EJšžœ žœžœžœ˜!Jšžœžœžœžœžœžœžœ˜-Jšžœ#žœžœžœ˜8Jšžœ%žœžœžœ˜:šžœžœžœž˜&Jš žœžœžœžœžœ˜8—šžœžœžœž˜(Jš žœžœžœžœžœ˜<—Jšžœžœ˜—J˜Jšžœ˜—J˜—…—lšΧ