-- File: IPCTGMakerImpl.mesa
-- Last Edited by: CSChow, January 23, 1985 9:53:38 pm PST
--Documentation will come later after consolidating the design --
DIRECTORY
OrderedSymbolTableRef,
IPParams,
Misc,
IPCoTab,
IPCTG,
IPCoVerifier,
IPCTGMaker;
IPCTGMakerImpl: CEDAR PROGRAM
IMPORTS OrderedSymbolTableRef, Misc, IPParams, IPCTG, IPCoTab, IPCoVerifier
EXPORTS IPCTGMaker = BEGIN OPEN IPCTGMaker;
--This piece of code was written with the assumption that channels do not have zero width.
--So all the error checkings '%Debugging%' and some internal routines uses that information.
--But this assumption is no longer valid. An easy way out is by add the 2 lines below
-- marked by: '***Added to allow chWidth = 0***'
--This is not an efficient implement, a lot of design and efficiency improvements
-- can be made to it. However it is ample for the size of problems we are
-- dealing with and since it is only called once (ie. to enter the topological domain)
-- the pay off is not great. But if it ever becomes necessary to increase the efficiency,
-- I can give you a lot of pointers on how to do it.
--CSChow: January 23, 1985 9:45:19 pm PST
Create: PUBLIC PROC[] RETURNS[Ref]= {
RETURN [NEW[Rep ← [comps:, horPreChs: OrderedSymbolTableRef.CreateTable[preChCompareProc], verPreChs: OrderedSymbolTableRef.CreateTable[preChCompareProc], xMin:, yMin:, xMax:, yMax:]]]}; --Create --
Destroy: PUBLIC PROC [r: Ref]= {
DeletePreChs[r];
r.horPreChs.DestroyTable[];
r.verPreChs.DestroyTable[];
}; --Destroy --
ConstructCTG: PUBLIC PROC[r: Ref, cV: IPCoVerifier.Ref, chWidth: NAT ← IPParams.ChDefaultWidth, yPrimary, maxChWidth: BOOLTRUE] RETURNS[needGeom: BOOLFALSE, ctg: IPCTG.Ref]= {
preChs: OrderedSymbolTableRef.Table;
mPreCh: PreCh ← NIL;
wSpan: Misc.Interval ← IF yPrimary THEN cV.GetBRect.x ELSE cV.GetBRect.y;
cl: Clusters ← ClCreate[];
primClCnt: NAT;
chWidth ← chWidth.SUCC;
--***Added to allow chWidth = 0***
--The rest of the code can now proceed with chWidth > 0
--(0) Initialize the input-
AcceptCoVerifier[r, cV, chWidth];
--(1) Form primary clusters--
preChs ← IF yPrimary THEN r.horPreChs ELSE r.verPreChs;
WHILE (mPreCh ← GetNextPreCh[preChs, mPreCh]) # NIL DO
mWind: Misc.Window ← Misc.WindCreate[wSpan.min, wSpan.max];
mRange: Misc.Interval ← mPreCh.range;
mSpan: Misc.Interval ← mPreCh.span;
tPreCh: PreCh ← GetNextPreCh[preChs, mPreCh];
tSpan: Misc.Interval;
cluster: LIST OF PreCh ← NIL;
IF mPreCh.posComp THEN {
IF mPreCh.cluster = 0 THEN ERROR ELSE LOOP}; --%Debugging%--
WHILE tPreCh # NIL DO {
IF mWind.WindCurtainsCoverIntl[mSpan] THEN EXIT;
tSpan ← tPreCh.span;
IF (NOT tPreCh.posComp) OR mSpan.IntlIntersect[tSpan] = NIL THEN GOTO nextTest;
IF mWind.WindPassesIntl[tSpan] OR mRange.IntlContainPt[tPreCh.coord]
THEN {cluster ← CONS[tPreCh, cluster]; mRange ← mRange.IntlIntersect[tPreCh.range]}
ELSE GOTO nextTest;
IF mRange.IntlOneOf = interval THEN GOTO nextTest; --%Debugging%--
ERROR;
EXITS
nextTest => {mWind.WindAddCurtain[tSpan];
tPreCh ← GetNextPreCh[preChs, tPreCh]}}
ENDLOOP;
ClClusterize[cl, mPreCh, cluster];
ENDLOOP;
--(2) Form secondary clusters--
primClCnt ← cl.count;
FOR i: NAT IN [1..primClCnt] DO
mCl: Cluster ← cl.clusters[i];
nPs: LIST OF PreCh;
pPs: LIST OF PreCh;
IF mCl = NIL THEN LOOP;
nPs ← mCl.negPreChs;
pPs ← mCl.posPreChs;
mCl.negBndCl ← ClMerge2[cl, nPs.first.negBnd, pPs.first.negBnd];
IF cl.clusters[mCl.negBndCl].range.IntlOneOf # interval THEN needGeom ← TRUE;
IF nPs.first.negBnd.posComp # pPs.first.negBnd.posComp THEN {
negP: PreCh ← nPs.first;
posP: PreCh ← pPs.first;
IF negP.negBnd.negBnd = negP AND posP.negBnd.negBnd = posP THEN {
cl.clusters[mCl.negBndCl].negBndCl ← i;
GOTO ok;};
IF negP.negBnd.posBnd = negP AND posP.negBnd.posBnd = posP THEN {
cl.clusters[mCl.negBndCl].posBndCl ← i;
GOTO ok;};
ERROR; -- %Shouldn't Happen%--
EXITS
ok => NULL;};
DO
IF nPs.rest = NIL THEN EXIT;
cl.clusters[ClMerge2[cl, nPs.first.posBnd, nPs.rest.first.negBnd]].posBndCl ← i;
nPs ← nPs.rest;
ENDLOOP;
DO
IF pPs.rest = NIL THEN EXIT;
cl.clusters[ClMerge2[cl, pPs.first.posBnd, pPs.rest.first.negBnd]].negBndCl ← i;
pPs ← pPs.rest;
ENDLOOP;
mCl.posBndCl ← ClMerge2[cl, nPs.first.posBnd, pPs.first.posBnd];
IF cl.clusters[mCl.posBndCl].range.IntlOneOf # interval THEN {needGeom ← TRUE};
IF nPs.first.posBnd.posComp # pPs.first.posBnd.posComp THEN {
negP: PreCh ← nPs.first;
posP: PreCh ← pPs.first;
IF negP.posBnd.negBnd = negP AND posP.posBnd.negBnd = posP THEN {
cl.clusters[mCl.posBndCl].negBndCl ← i;
GOTO ok;};
IF negP.posBnd.posBnd = negP AND posP.posBnd.posBnd = posP THEN {
cl.clusters[mCl.posBndCl].posBndCl ← i;
GOTO ok;};
ERROR; -- %Shouldn't Happen%--
EXITS
ok => NULL;};
ENDLOOP;
chWidth ← chWidth.PRED;
***Added to allow chWidth = 0***
-- Nullifies the effect eariler
ctg ← ClToCTG[cl, chWidth, maxChWidth];
FOR indx: NAT IN [1..r.comps.count] DO
InsertComp[r.comps.compItems[indx], cl]
ENDLOOP;
}; -- ConstructCTG--
--########### Private procedures ###########--
PosInfinity: INT = LAST[INT];
NegInfinity: INT = FIRST[INT];
preChCompareProc: OrderedSymbolTableRef.CompareProc ={
p1: PreCh ← NARROW[r1];
p2: PreCh ← NARROW[r2];
IF p1 = p2 THEN RETURN [equal];
IF p1.coord> p2.coord THEN RETURN [greater];
IF p1.coord < p2.coord THEN RETURN [less];
IF p1.negBnd.coord > p2.negBnd.coord THEN RETURN [greater];
IF p1.negBnd.coord < p2.negBnd.coord THEN RETURN [less];
ERROR; --%Shouldnt Happen%--
}; --preChCompareProc--
AcceptCoVerifier: PROC[r: Ref, cV: IPCoVerifier.Ref, chWidth: NAT]= {
OPEN rc: r.comps;
aP: IPCoVerifier.EachCompItemAction ={
quit ← FALSE;
rc.compItems[(rc.count ← rc.count + 1)] ← NEW[CompItemRep ← [cI.comp]];
AcceptCompItem[r, cI, rc.count]}; --aP--
DeletePreChs[r];
r.comps ← NEW[CompItemsRep];
AcceptBRect[r, cV.GetBRect, chWidth];
cV.CompItems[aP];
}; --AcceptCoVerifier --
DeletePreChs: PROC[r: Ref] ={
preChList: LIST OF PreCh ← NIL;
p: PROC[i: REF] RETURNS[BOOL] ={pCh: PreCh ← NARROW[i];
preChList ← CONS[pCh, preChList];
RETURN [FALSE]}; --p--
IF r.horPreChs.Size < r.verPreChs.Size
THEN {r.horPreChs.EnumerateIncreasing[p]}
ELSE {r.verPreChs.EnumerateIncreasing[p]};
WHILE preChList # NIL DO
PreChSetBnds[preChList.first, NIL, NIL];
preChList ← preChList.rest;
ENDLOOP;r.comps ← NIL;
r.horPreChs.DeleteAllItems[];
r.verPreChs.DeleteAllItems[];
}; --DeletePreChs--
AcceptBRect: PROC[r: Ref, bRect: Misc.Rect, margin: NAT] ={
I: PROC[min, max: INT] RETURNS[Misc.Interval] = Misc.IntlCreate;
south, east, north, west: PreCh;
r.xMin ← bRect.xMin - margin;
r.yMin ← bRect.yMin - margin;
r.xMax ← bRect.xMax + margin;
r.yMax ← bRect.yMax + margin;
south ← NewPreCh[hor, FALSE, r.yMin, I[r.xMin, r.xMax], I[r.yMin, PosInfinity], NIL];
east ← NewPreCh[ver, TRUE, r.xMax, I[r.yMin, r.yMax], I[NegInfinity, r.xMax], NIL];
north ← NewPreCh[hor, TRUE, r.yMax, I[r.xMin, r.xMax], I[NegInfinity, r.yMax], NIL];
west ← NewPreCh[ver, FALSE, r.xMin, I[r.yMin, r.yMax], I[r.xMin, PosInfinity], NIL];
PreChSetBnds[south, west, east];
PreChSetBnds[east, south, north];
PreChSetBnds[north, west, east];
PreChSetBnds[west, south, north];
InsertPreCh[r, south];
InsertPreCh[r, east];
InsertPreCh[r, north];
InsertPreCh[r, west];
}; --AcceptBRect--
AcceptCompItem: PROC[r: Ref, cI: IPCoVerifier.CompItem, compIndex: CompItemIndices] ={
OPEN h: cI.hint;
I: PROC[min, max: INT] RETURNS[Misc.Interval] = Misc.IntlCreate;
co: IPCoTab.Component ← cI.comp;
south, east, north, west: PreCh;
swC, seC, neC, nwC: CornerPreChs ← NIL;
bR, swR0, seR0, neR0, nwR0, swR1, seR1, neR1, nwR1: Misc.Rect;
southSpan, eastSpan, northSpan, westSpan: Misc.Interval;
epsilon: NAT ← 1; --This has very subtle effects--
hC, vC: PreCh;
bR ← IPCoTab.GetBRect[co];
[swR0, seR0, neR0, nwR0] ← IPCoTab.GetCornerRects[co, 0];
[swR1, seR1, neR1, nwR1] ← IPCoTab.GetCornerRects[co, 1];
southSpan ← northSpan ← bR.x;
eastSpan ← westSpan ← bR.y;
IF h.sw THEN {southSpan ← southSpan.IntlSubtract[swR1.x];
westSpan ← westSpan.IntlSubtract[swR1.y]};
IF h.se THEN {southSpan ← southSpan.IntlSubtract[seR1.x];
eastSpan ← eastSpan.IntlSubtract[seR1.y]};
IF h.ne THEN {northSpan ← northSpan.IntlSubtract[neR1.x];
eastSpan ← eastSpan.IntlSubtract[neR1.y]};
IF h.nw THEN {northSpan ← northSpan.IntlSubtract[nwR1.x];
westSpan ← westSpan.IntlSubtract[nwR1.y]};
south ← NewPreCh[hor, TRUE, bR.yMin, southSpan, I[NegInfinity, bR.yMin], co];
east ← NewPreCh[ver,FALSE,bR.xMax, eastSpan, I[bR.xMax, PosInfinity], co];
north ← NewPreCh[hor, FALSE, bR.yMax, northSpan, I[bR.yMax, PosInfinity], co];
west ← NewPreCh[ver, TRUE, bR.xMin, westSpan, I[NegInfinity, bR.xMin], co];
IF h.sw THEN {
hC ← NewPreCh[hor, TRUE, swR0.yMax, swR1.x, swR0.y, co];
vC ← NewPreCh[ver, TRUE, swR0.xMax, swR1.y, swR0.x,co];
PreChSetBnds[hC, west, vC];
PreChSetBnds[vC, south, hC];
swC ← NEW[CornerPreChsRep ←[hor: hC, ver: vC]];
InsertPreCh[r, hC];
InsertPreCh[r, vC];};
IF h.se THEN {
hC ← NewPreCh[hor, TRUE, seR0.yMax, seR1.x, seR0.y, co];
vC ← NewPreCh[ver, FALSE, seR0.xMin, seR1.y, seR0.x, co];
PreChSetBnds[hC, vC, east];
PreChSetBnds[vC, south, hC];
seC ← NEW[CornerPreChsRep ←[hor: hC, ver: vC]];
InsertPreCh[r, hC];
InsertPreCh[r, vC];};
IF h.ne THEN {
hC ← NewPreCh[hor, FALSE, neR0.yMin, neR1.x, neR0.y, co];
vC ← NewPreCh[ver, FALSE, neR0.xMin, neR1.y, neR0.x, co];
PreChSetBnds[hC, vC, east];
PreChSetBnds[vC, hC, north];
neC ← NEW[CornerPreChsRep ←[hor: hC, ver: vC]];
InsertPreCh[r, hC];
InsertPreCh[r, vC];};
IF h.nw THEN {
hC ← NewPreCh[hor, FALSE, nwR0.yMin, nwR1.x, nwR0.y,co];
vC ← NewPreCh[ver, TRUE, nwR0.xMax, nwR1.y, nwR0.x, co];
PreChSetBnds[hC, west, vC];
PreChSetBnds[vC, hC, north];
nwC ← NEW[CornerPreChsRep ←[hor: hC, ver: vC]];
InsertPreCh[r, hC];
InsertPreCh[r, vC];};
PreChSetBnds[south, (IF h.sw THEN swC.ver ELSE west), (IF h.se THEN seC.ver ELSE east)];
PreChSetBnds[east, (IF h.se THEN seC.hor ELSE south), (IF h.ne THEN neC.hor ELSE north)];
PreChSetBnds[north, (IF h.nw THEN nwC.ver ELSE west), (IF h.ne THEN neC.ver ELSE east)];
PreChSetBnds[west, (IF h.sw THEN swC.hor ELSE south), (IF h.nw THEN nwC.hor ELSE north)];
r.comps.compItems[compIndex].prinPreChs ← [south: south, east: east, north: north, west: west];
r.comps.compItems[compIndex].cornerPreChs ← [sw: swC, se: seC, ne: neC, nw: nwC];
InsertPreCh[r, south];
InsertPreCh[r, east];
InsertPreCh[r, north];
InsertPreCh[r, west];
};--AcceptCompItem--
NewPreCh: PROC[type: IPCTG.ChType, posComp: BOOL, coord: INT, span, range: Misc.Interval, comp: IPCoTab.Component] RETURNS [PreCh] ={
RETURN [NEW[PreChRep ← [type, posComp, coord, span, range, comp]]];
}; --NewPreCh--
InsertPreCh: PROC[r: Ref, p: PreCh] ={
SELECT p.type FROM
hor => {r.horPreChs.Insert[p]};
ver => {r.verPreChs.Insert[p]};
ENDCASE => ERROR; }; --InsertPreCh--
GetNextPreCh: PROC[tab: OrderedSymbolTableRef.Table, p: PreCh] RETURNS [PreCh] ={
IF p = NIL
THEN RETURN [NARROW[tab.LookupSmallest]]
ELSE RETURN [NARROW[tab.LookupNextLarger[p]]]};--GetNextPreCh--
--Operations on individual PreCh and CompItem--
PreChSetBnds: PROC[p, negBnd, posBnd: PreCh] ={
IF negBnd # NIL AND negBnd.type = p.type THEN ERROR;
IF posBnd # NIL AND posBnd.type = p.type THEN ERROR;
p.negBnd ← negBnd;
p.posBnd ← posBnd;
}; --PreChSetBnds--
InsertComp: PROC[cI: CompItem, cl: Clusters] ={
OPEN p: cI.prinPreChs, cc: cI.cornerPreChs;
swC, seC, neC, nwC: IPCoTab.CornerChannels ← NIL;
IF cc.sw # NIL THEN swC ← NEW[IPCoTab.CornerChannelsRep ← [hor: ClGetCh[cl, cc.sw.hor], ver: ClGetCh[cl, cc.sw.ver]]];
IF cc.se # NIL THEN seC ← NEW[IPCoTab.CornerChannelsRep ← [hor: ClGetCh[cl, cc.se.hor], ver:ClGetCh[cl, cc.se.ver]]];
IF cc.ne # NIL THEN neC ← NEW[IPCoTab.CornerChannelsRep ← [hor: ClGetCh[cl, cc.ne.hor], ver:ClGetCh[cl, cc.ne.ver]]];
IF cc.nw # NIL THEN nwC ← NEW[IPCoTab.CornerChannelsRep ← [hor: ClGetCh[cl, cc.nw.hor], ver:ClGetCh[cl, cc.nw.ver]]];
IPCoTab.InsertComponent[cI.co, ClGetCh[cl, p.south], ClGetCh[cl, p.east], ClGetCh[cl, p.north], ClGetCh[cl, p.west], swC, seC, neC, nwC]
}; --InsertComp--
-- ########## Cluster abstraction ########## --
Clusters: TYPE = REF ClustersRep;
ClustersRep: TYPE = RECORD[count: NAT ← 0, clusters: ARRAY ClusterIndices OF Cluster ← ALL[NIL]];
Cluster: TYPE = REF ClusterRep;
ClusterRep: TYPE = RECORD[type: IPCTG.ChType, range: Misc.Interval, negPreChs, posPreChs: LIST OF PreCh ← NIL, negBndCl, posBndCl: NAT ← 0, ch: IPCTG.Channel ← NIL, primOrdering: INT];
ClusterIndices: TYPE = [1..MaxClusterCnt];
ClCreate: PROC RETURNS [Clusters] ={RETURN [NEW[ClustersRep]]}; --ClCreate--
ClNewCluster: PROC[cl: Clusters, type: IPCTG.ChType, range: Misc.Interval, order: INTFIRST[INT]] RETURNS [ClusterIndices] ={
cl.clusters[(cl.count ← cl.count + 1)] ← NEW[ClusterRep ←[type: type, range: range, primOrdering: order]];
RETURN [cl.count]}; --ClNewCluster--
ClClusterize: PROC[cl: Clusters, pCh: PreCh, lp: LIST OF PreCh] ={
cIndex: NAT ← 0;
FOR l: LIST OF PreCh ← lp, l.rest UNTIL l = NIL DO
cIndex ← MAX[cIndex, l.first.cluster];
ENDLOOP;
IF cIndex = 0 OR cl.clusters[cIndex].primOrdering < pCh.range.min
THEN {cIndex ← ClNewCluster[cl, pCh.type, pCh.range, pCh.range.min]};
ClAddPreCh[cl, cIndex, pCh];
FOR l: LIST OF PreCh ← lp, l.rest UNTIL l = NIL DO
IF l.first.cluster = cIndex THEN LOOP;
IF l.first.cluster = 0
THEN {ClAddPreCh[cl, cIndex, l.first]}
ELSE {ClMergeClusterInto[cl, l.first.cluster, cIndex]};
ENDLOOP;
IF cl.clusters[cIndex].range.IntlOneOf # interval THEN ERROR; -- %Debugging%--
}; --ClClusterize--
ClMerge2: PROC[cl: Clusters, p1, p2: PreCh] RETURNS [ClusterIndices] ={
nCl: NAT;
IF p1.type # p2.type THEN ERROR;
IF p1.cluster # 0 AND p2.cluster # 0 THEN {
IF p1.cluster = p2.cluster THEN RETURN[p1.cluster] ELSE ERROR}; -- %Shouldnt Happen%--
nCl ← MAX[p1.cluster, p2.cluster];
IF nCl = 0 THEN nCl ← ClNewCluster[cl, p1.type, p1.range];
ClAddPreCh[cl, nCl, p1];
ClAddPreCh[cl, nCl, p2];
RETURN [nCl]
};--ClMerge2--
ClMergeClusterInto: PROC[cl: Clusters, from, into: ClusterIndices] ={
fCl: Cluster ← cl.clusters[from];
FOR l: LIST OF PreCh ← fCl.negPreChs, l.rest UNTIL l = NIL DO
ClAddPreCh[cl, into, l.first];
ENDLOOP;
FOR l: LIST OF PreCh ← fCl.posPreChs, l.rest UNTIL l = NIL DO
ClAddPreCh[cl, into, l.first];
ENDLOOP;
cl.clusters[from] ← NIL;
};--ClMergeClusterInto--
ClAddPreCh: PROC[cl: Clusters, i: ClusterIndices, pCh: PreCh] ={
chList: LIST OF PreCh;
pLess: PROC[p1, p2: PreCh] RETURNS [BOOL] = {
RETURN [p1.negBnd.coord < p2.negBnd.coord]
};--pLess--
IF cl.clusters[i].type # pCh.type THEN ERROR;
IF pCh.cluster = i THEN RETURN;
pCh.cluster ← i;
cl.clusters[i].range← cl.clusters[i].range.IntlIntersect[pCh.range];
IF pCh.posComp
THEN {IF (chList ← cl.clusters[i].posPreChs) = NIL OR pLess[pCh, chList.first] THEN {cl.clusters[i].posPreChs ← CONS[pCh, chList]; RETURN}}
ELSE {IF (chList ← cl.clusters[i].negPreChs) = NIL OR pLess[pCh, chList.first] THEN {cl.clusters[i].negPreChs ← CONS[pCh, chList]; RETURN}};
FOR l: LIST OF PreCh ← chList, l.rest DO
IF l.rest = NIL THEN {l.rest ← LIST[pCh]; EXIT};
IF pLess[pCh, l.rest.first] THEN {l.rest ← CONS[pCh, l.rest]; EXIT}
ENDLOOP;
};--ClAddPreCh--
ClGetCh: PROC[cl: Clusters, p: PreCh] RETURNS [IPCTG.Channel] ={
RETURN [cl.clusters[p.cluster].ch]}; --ClGetCh--
ClToCTG: PROC[cl: Clusters, chWidth: NAT, maxChWidth: BOOL] RETURNS [IPCTG.Ref] ={
ctg: IPCTG.Ref ← IPCTG.Create[];
makeCh: PROC[c: Cluster] ={
r: Misc.Interval ← c.range;
width: NATIF maxChWidth THEN MAX[r.IntlLength, chWidth] ELSE chWidth;
coord: INTIF r = NIL
THEN (c.negPreChs.first.coord + c.posPreChs.first.coord)/2
ELSE (r.min + r.max)/2;
c.ch ← ctg.CreateChannel[c.type, width, coord];
}; --makeCh--
makeChBoundary: PROC[index: ClusterIndices] ={
c: Cluster ← cl.clusters[index];
eP: PROC [end: {neg, pos}] RETURNS [IPCTG.Intersection] ={
ch: IPCTG.Channel;
iType: IPCTG.IntersectionType;
ch ← SELECT end FROM
neg => cl.clusters[c.negBndCl].ch,
pos => cl.clusters[c.posBndCl].ch,
ENDCASE => ERROR;
SELECT end FROM
neg => {SELECT index FROM
cl.clusters[c.negBndCl].posBndCl => iType ← -1;
cl.clusters[c.negBndCl].negBndCl => iType ← 1;
ENDCASE => iType ← 0;};
pos => {SELECT index FROM
cl.clusters[c.posBndCl].posBndCl => iType ← -1;
cl.clusters[c.posBndCl].negBndCl => iType ← 1;
ENDCASE => iType ← 0;};
ENDCASE => ERROR;
RETURN [IPCTG.NewIn[ch: ch, type: iType]]
}; --eP--
sP: PROC [side: {neg, pos}] RETURNS [LIST OF IPCTG.Intersection] ={
iListHd, iListTl: LIST OF IPCTG.Intersection;
iType: IPCTG.IntersectionType ← (SELECT side FROM neg => -1, pos => 1, ENDCASE => ERROR);
iListHd ← iListTl ← CONS[NIL, NIL];
FOR pChs: LIST OF PreCh ← (SELECT side FROM neg => c.negPreChs, pos => c.posPreChs, ENDCASE => ERROR), pChs.rest UNTIL pChs.rest = NIL DO
ch: IPCTG.Channel ← cl.clusters[pChs.first.posBnd.cluster].ch;
IF pChs.first.posBnd.cluster # pChs.rest.first.negBnd.cluster THEN ERROR; --%Debugging%--
iListTl.rest ← LIST[IPCTG.NewIn[ch: ch, type: iType]];
iListTl ← iListTl.rest
ENDLOOP;
RETURN [iListHd.rest]}; --sP--
IPCTG.SetBoundary[c.ch, eP[neg], eP[pos], sP[neg], sP[pos]]
}; --makeChBoundary--
FOR i: NAT IN [1..cl.count] DO
IF cl.clusters[i] # NIL THEN makeCh[cl.clusters[i]];
ENDLOOP;
FOR i: NAT IN [1..cl.count] DO
IF cl.clusters[i] # NIL THEN makeChBoundary[i];
ENDLOOP;
RETURN [ctg];
}; --ClToCTG--
END.