-- June 13, 1983 5:05 pm -- program junoBodyImpl.mesa, coded July 1981 by Greg Nelson -- This is an experimental interactive graphics program that allows a user to -- specify the positions of points by declaring geometrical contraints that -- the positions are to satisfy. The program solves constraints (which can be -- viewed as simultaneous polynomial equations) by an n-dimensional version of -- Newton's method for finding the root of a differentiable function. DIRECTORY JunoStorage, RealFns, JunoBody, Atom, Rope, Convert, JunoGraphics; JunoBodyImpl: PROGRAM IMPORTS JunoStorage, RealFns, Atom, Rope, Convert, JunoGraphics EXPORTS JunoBody = BEGIN OPEN JunoStorage; InsertPoint: PROCEDURE[p:PointPtr] = BEGIN temp: PointPtr ← pointLpad; WHILE temp.link.x < p.x DO temp ← temp.link ENDLOOP; p.link ← temp.link; temp.link ← p; END; SortPoints: PUBLIC PROC = {p,q,r, temp: PointPtr; IF pointLpad = pointRpad THEN RETURN; p ← pointLpad.link; q ← p.link; p.link ← pointLpad; UNTIL q = pointRpad DO IF q.x >= p.x THEN {temp ← q.link; q.link ← p; p ← q; q ← temp; LOOP}; -- Insert q into list p, link[p], link[link[p]], which is sorted in DESCENDING order. r ← p; WHILE r.link.x > q.x DO r ← r.link ENDLOOP; temp ← q.link; q.link ← r.link; r.link ← q; q ← temp; ENDLOOP; -- Now reverse the list by moving backwards through it: UNTIL p = pointLpad DO temp ← p.link; p.link ← q; q ← p; p ← temp; ENDLOOP; pointLpad.link ← q}; Distance: PROCEDURE[x1, y1, x2, y2: REAL] RETURNS [REAL] = -- this procedure returns the distance between the points (x1,y1) -- and (x2,y2). BEGIN RETURN [RealFns.SqRt[(x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)]] END; FindSelectedPoint: PUBLIC PROCEDURE [x,y:REAL] RETURNS [PointPtr] = -- This procedure finds the selected point closest to the current mouse coordinates. -- It is identical to FindPoint except that slink replaces link. This is a good -- example of why field selectors should be values. BEGIN leftpad: PointPtr = pointLpad; rightpad: PointPtr = pointRpad; p, champ: PointPtr; champdistance, pdistance: REAL; p ← leftpad.slink; IF p = rightpad THEN RETURN[NIL]; champ ← p; champdistance ← Distance[p.x, p.y, x, y]; p ← p.slink; WHILE p # rightpad DO pdistance ← Distance[p.x, p.y, x, y]; IF pdistance < champdistance THEN BEGIN champ ← p; champdistance ← pdistance END; p ← p.slink; ENDLOOP; RETURN [champ]; END; -- Now the procedures that are invoked as commands: -- The move, copy, and delete commands operate on groups, and the former two involve -- linear transformations, so before giving their code, we give the program that -- inverts a matrix. It sets "singular" to TRUE if the matrix is singular. m, m1, mInv: ARRAY [1..3] OF ARRAY [1..3] OF REAL; singular: PUBLIC BOOLEAN; Invertm: PROC = -- Inverts m into mInv by pivoting three times; or sets "singular" flag. BEGIN i, j, k, l: INTEGER; c: ARRAY [1..3] OF INTEGER; pivoted: ARRAY [1..3] OF BOOLEAN ← [FALSE, FALSE, FALSE]; p: REAL; -- k is the row in which we are pivoting. -- l is the column in which we are pivoting. -- i and j are miscellaneous row and column indices respectively -- c[i] is the column of the pivot in the ith row. -- p is the reciprocal of the pivot element; also used as temp for swapping. singular ← FALSE; FOR k IN [1..3] DO -- set l so m[k,l] is largest of m[k,1], m[k, 2], m[k, 3], excluding -- columns in which we have already pivoted. p ← 0; FOR j IN [1 .. 3] DO IF ABS[m[k][j]] >= p AND NOT pivoted[j] THEN {l ← j; p ← ABS[m[k][l]]} ENDLOOP; -- We will pivot at m[k,l], if it is not too small: IF ABS[m[k][l]] < .0001 THEN {singular ← TRUE; RETURN}; c[k] ← l; pivoted[l] ← TRUE; p ← 1.0 / m[k][l]; m[k][l] ← 1.0; -- divide everything in pivot row by the pivot element: FOR j IN [1..3] DO m[k][j] ← m[k][j] * p ENDLOOP; FOR i IN [1..3] DO IF i # k THEN FOR j IN [1..3] DO IF j # l THEN -- for each m[i,j] outside the pivot row and column m[i][j] ← m[i][j] - m[i][l] * m[k][j]; -- note that m[k,j] was already * p. ENDLOOP ENDLOOP; -- Finally process pivot column: FOR i IN [1..3] DO IF i # k THEN m[i][l] ← -m[i][l] * p ENDLOOP; ENDLOOP; -- Now we permute rows and columns: FOR i IN [1..3] DO FOR j IN [1..3] DO mInv[c[i]][j] ← m[i][c[j]] ENDLOOP ENDLOOP; END; MultiplyMatrix: PROCEDURE = -- multiply m1 * mInv to produce m. BEGIN i, j, k: INTEGER; sum: REAL; FOR i IN [1..3] DO FOR j IN [1..3] DO {sum ← 0.0; FOR k IN [1..3] DO sum ← sum + m1[i][k] * mInv[k][j] ENDLOOP; m[i][j] ← sum} ENDLOOP ENDLOOP; END; Identify: PUBLIC PROC[] = -- replace all occurences of p by p.copy if p.copy # NIL. {r, rr: PointPtr; ScanLists[operation:move]; r ← pointLpad; UNTIL r = pointRpad DO IF r.link.copy # NIL THEN {rr ← r.link; r.link ← rr.link; GcPoint[rr]} ELSE r ← r.link; ENDLOOP}; MakeDefBody: PUBLIC PROC[orig, xaxis: PointPtr] RETURNS [REF] = {IF xaxis = NIL THEN RETURN[OtherMakeDefBody[orig]] ELSE {m[1][1] ← orig.x; m[2][1] ← orig.y; m[1][2] ← xaxis.x; m[2][2] ← xaxis.y; m1[1][1] ← 0; m1[2][1] ← 0; m1[1][2] ← 1; m1[2][2] ← 0; m[1][3] ← m[1][1] + m[2][1] - m[2][2]; m[2][3] ← m[2][1] + m[1][2] - m[1][1]; FOR j: INT IN [1..3] DO m[3][j] ← 1.0; m1[3][j] ← 1.0 ENDLOOP; m1[1][3] ← m1[1][1] + m1[2][1] - m1[2][2]; m1[2][3] ← m1[2][1] + m1[1][2] - m1[1][1]; Invertm[]; IF singular THEN ERROR; MultiplyMatrix[]; {p:PointPtr ← pointLpad.link; WHILE p # pointRpad DO p.name ← NIL; p ← p.link ENDLOOP}; BuildLocalList[orig, xaxis]; BuildActionAndPredLists[]; {locals: REF ← IF localList = NIL THEN NIL ELSE Fix[localList, comma]; actions: REF ← IF actionList = NIL THEN $Skip ELSE Fix[actionList, semicolon]; preds: REF; IF predList = NIL THEN preds ← $T ELSE preds ← Fix[predList, $and]; IF locals = NIL THEN RETURN[actions] ELSE RETURN [LIST[$if, LIST[arrow, LIST[st, locals, preds], actions]]]}}}; OtherMakeDefBody: PROC[orig: PointPtr] RETURNS [REF] = {p:PointPtr ← pointLpad.link; WHILE p # pointRpad DO p.name ← NIL; p ← p.link ENDLOOP; BuildLocalList[orig, NIL]; BuildActionAndPredLists[]; {locals: REF ← IF localList = NIL THEN NIL ELSE Fix[localList, comma]; actions: REF ← IF actionList = NIL THEN $Skip ELSE Fix[actionList, semicolon]; preds: REF; IF predList = NIL THEN preds ← $T ELSE preds ← Fix[predList, $and]; IF locals = NIL THEN RETURN[actions] ELSE RETURN [LIST[$if, LIST[arrow, LIST[st, locals, preds], actions]]]}}; arrow: ATOM = Atom.MakeAtom["->"]; st: ATOM = Atom.MakeAtom["|"]; comma: ATOM = Atom.MakeAtom[","]; semicolon: ATOM = Atom.MakeAtom[";"]; approx: ATOM = Atom.MakeAtom["=="]; leftpren: ATOM = Atom.MakeAtom["("]; Fix: PROC [rr: REF, op: REF] RETURNS [REF] = {r: LIST OF REF ← NARROW[rr]; IF r.rest = NIL THEN RETURN [r.first] ELSE RETURN [LIST[op, r.first, Fix[r.rest, op]]]}; localList: LIST OF REF; actionList: LIST OF REF; predList: LIST OF REF; BuildLocalList: PROC [orig, xaxis: PointPtr] = {i: INT ← 2; p: PointPtr ← pointLpad.slink; x, y: REAL; localList ← NIL; UNTIL p = pointRpad DO IF p = orig THEN p.name ← $a ELSE IF p = xaxis THEN p.name ← $b ELSE {a: ATOM; IF i > 25 THEN a ← Atom.MakeAtom[Rope.Cat["a", Convert.RopeFromInt[i - 26, 10, FALSE]]] ELSE a ← Atom.MakeAtom[Rope.FromChar['a + i]]; p.name ← a; i ← i + 1; IF xaxis # NIL THEN {x ← p.x * m[1][1] + p.y * m[1][2] + m[1][3]; y ← p.x * m[2][1] + p.y * m[2][2] + m[2][3]; localList ← CONS[ LIST[approx, a, LIST[$rel, LIST[leftpren, LIST[comma, NEW[REAL ← x], NEW[REAL ← y]]], LIST[leftpren, LIST[comma, $a, $b]]]], localList]} ELSE {x ← p.x - orig.x; y ← p.y - orig.y; localList ← CONS[ LIST[approx, a, LIST[$rel, LIST[leftpren, LIST[comma, NEW[REAL ← x], NEW[REAL ← y]]], LIST[leftpren, $a]]], localList]}}; JunoGraphics.DrawRope[Atom.GetPName[NARROW[p.name]], p.x + 5, p.y + 5]; JunoGraphics.viewerChanged ← TRUE; p ← p.slink ENDLOOP}; BuildActionAndPredLists: PROC = {actionList ← NIL; predList ← NIL; ScanLists[operation: build]}; copiedPoints: PUBLIC PointPtr; Copy: PUBLIC PROCEDURE = BEGIN p, lastnew: PointPtr; -- copy the list of selected points into a new list firstnew, -- firstnew.slink .... copiedPoints ← NewPoint[]; lastnew ← copiedPoints; p ← pointLpad.slink; p.copy ← copiedPoints; copiedPoints.x ← p.x; copiedPoints.y ← p.y; copiedPoints.visible ← p.visible; InsertPoint[copiedPoints]; p ← p.slink; UNTIL p = pointRpad DO lastnew.slink ← NewPoint[]; lastnew ← lastnew.slink; p.copy ← lastnew; lastnew.x ← p.x; lastnew.y ← p.y; copiedPoints.visible ← p.visible; InsertPoint[lastnew]; p ← p.slink; ENDLOOP; lastnew.slink ← pointRpad; ScanLists[operation: copy]; -- scan all edges, arcs, constraints and copy -- any of them that involve copied points. END; ComputeTransform: PUBLIC PROC[a,b,c,sa,sb,sc:PointPtr] = {-- we want m [ sa, sb, sc ] = [ a, b, c], where the points are viewed as -- column vectors with third component 1. Hence we compute the inverse -- of [sa, sb, sc] and multipy on the left by [a, b, c]. But the pairs -- (b, sb), (c, sc) may be missing, in which case they are filled in by default to make -- the transformation a translation (if both are missing) or a Euclidean motion -- (if just (c, sc) is missing). j: INTEGER; FOR j IN [1..3] DO m[3][j] ← 1.0; m1[3][j] ← 1.0 ENDLOOP; m[1][1] ← sa.x; m[2][1] ← sa.y; m1[1][1] ← a.x; m1[2][1] ← a.y; IF b # NIL AND sb # NIL THEN {m[1][2] ← sb.x; m[2][2] ← sb.y; m1[1][2] ← b.x; m1[2][2] ← b.y;} ELSE {m[1][2] ← sa.x + 300; m[2][2] ← sa.y; m1[1][2] ← a.x + 300; m1[2][2] ← a.y}; IF c # NIL AND sc # NIL THEN {m[1][3] ← sc.x; m[2][3] ← sc.y; m1[1][3] ← c.x; m1[2][3] ← c.y} ELSE {m[1][3] ← m[1][1] + m[2][1] - m[2][2]; m[2][3] ← m[2][1] + m[1][2] - m[1][1]; m1[1][3] ← m1[1][1] + m1[2][1] - m1[2][2]; m1[2][3] ← m1[2][1] + m1[1][2] - m1[1][1]}; Invertm[]; IF singular THEN RETURN; MultiplyMatrix[]}; -- end of ComputeTransform. PerformTransform: PUBLIC PROCEDURE[p: PointPtr] = {x, y: REAL; UNTIL p = pointRpad DO x ← p.x * m[1][1] + p.y * m[1][2] + m[1][3]; y ← p.x * m[2][1] + p.y * m[2][2] + m[2][3]; p.x ← x; p.y ← y; p ← p.slink; ENDLOOP}; -- The move, copy and delete commands all involve scanning the lists of edges, -- arcs, and constraints; this scan is done by one procedure (ScanLists) which -- takes as an argument one of the four distinguished values copy, delete, move, mark: Operation: TYPE = {copy, delete, move, mark, build}; AddAction: PROC[op: REF, r1: REF ← NIL, r2: REF ← NIL] = {IF r2 = NIL THEN actionList ← CONS[LIST[op, r1], actionList] ELSE actionList ← CONS[LIST[op, r1, r2], actionList]}; Args: PROC [l: LIST OF PointPtr] RETURNS [REF] = {IF l.rest = NIL THEN RETURN [ArgName[l.first]] ELSE RETURN [LIST[comma, ArgName[l.first], Args[l.rest]]]}; NewArgs: PROC [l: LIST OF REF] RETURNS [REF] = {IF l.rest = NIL THEN RETURN [ArgName[l.first]] ELSE RETURN [LIST[comma, ArgName[l.first], NewArgs[l.rest]]]}; ArgName: PROC [r: REF] RETURNS [REF] = { WITH r SELECT FROM rp: PointPtr => RETURN[rp.name]; ri: REF INT => RETURN[ri]; rr: Rope.ROPE => RETURN[rr]; ra: ATOM => RETURN[ra] ENDCASE => ERROR }; PrenArgs: PROC [l: LIST OF PointPtr] RETURNS [REF] = {RETURN [LIST[leftpren, Args[l]]]}; AddPred: PROC[op: REF, r1: REF ← NIL, r2: REF ← NIL] = {IF r2 = NIL THEN predList ← CONS[LIST[op, r1], predList] ELSE predList ← CONS[LIST[op, r1, r2], predList]}; ScanLists: PROC[operation:Operation] = BEGIN { -- scan the edges: p: EdgePtr ← edgeLpad.link; q: EdgePtr ← edgeLpad; r: EdgePtr; UNTIL p = edgeRpad DO SELECT operation FROM move => {IF p.b1.copy # NIL THEN p.b1 ← p.b1.copy; IF p.b2.copy # NIL THEN p.b2 ← p.b2.copy; p ← p.link}; copy => {IF p.b1.copy # NIL AND p.b2.copy # NIL THEN {r ← NewEdge[]; r.b1 ← p.b1.copy; r.b2 ← p.b2.copy; r.link ← edgeLpad.link; edgeLpad.link ← r}; p ← p.link}; delete => IF p.b1.copy # NIL AND p.b2.copy # NIL THEN {q.link ← p.link; GcEdge[p]; p ← q.link} ELSE {q ← p; p ← p.link}; mark => {p.b1.copy ← NIL; p.b2.copy ← NIL; p ← p.link}; build => {IF p.b1.name # NIL AND p.b2.name # NIL THEN AddAction[$draw, PrenArgs[LIST[p.b1, p.b2]]]; p ← p.link} ENDCASE => ERROR; ENDLOOP}; {-- scan the arcs: p: ArcPtr ← arcLpad.link; q: ArcPtr ← arcLpad; r: ArcPtr; UNTIL p = arcRpad DO SELECT operation FROM move => {IF p.b1.copy # NIL THEN p.b1 ← p.b1.copy; IF p.b2.copy # NIL THEN p.b2 ← p.b2.copy; IF p.b3.copy # NIL THEN p.b3 ← p.b3.copy; IF p.b4.copy # NIL THEN p.b4 ← p.b4.copy; p ← p.link}; copy => {IF p.b1.copy # NIL AND p.b2.copy # NIL AND p.b3.copy # NIL AND p.b4.copy # NIL THEN {r ← NewArc[]; r.b1 ← p.b1.copy; r.b2 ← p.b2.copy; r.b3 ← p.b3.copy; r.b4 ← p.b4.copy; r.link ← arcLpad.link; arcLpad.link ← r}; p ← p.link}; delete => IF p.b1.copy # NIL AND p.b2.copy # NIL AND p.b3.copy # NIL AND p.b4.copy # NIL THEN {q.link ← p.link; GcArc[p]; p ← q.link} ELSE {q ← p; p ← p.link}; mark => {p.b1.copy ← NIL; p.b2.copy ← NIL; p.b3.copy ← NIL; p.b4.copy ← NIL; p ← p.link}; build => {IF p.b1.name # NIL AND p.b2.name # NIL AND p.b3.name # NIL AND p.b4.name # NIL THEN AddAction[$draw, PrenArgs[LIST[p.b1, p.b2, p.b3, p.b4]]]; p ← p.link} ENDCASE => ERROR; ENDLOOP}; { -- scan the strings: p : StringPtr ← stringLpad.link; q : StringPtr ← stringLpad; r : StringPtr; UNTIL p = stringRpad DO SELECT operation FROM move => {IF p.b3.copy # NIL THEN p.b3 ← p.b3.copy; IF p.b4.copy # NIL THEN p.b4 ← p.b4.copy; p ← p.link}; copy => {IF p.b3.copy # NIL AND p.b4.copy # NIL THEN {r ← NewString[]; r.b3 ← p.b3.copy; r.b4 ← p.b4.copy; r.stringText ← p.stringText; r.fontName ← p.fontName; r.fontSize ← p.fontSize; r.bold ← p.bold; r.italic ← p.italic; r.height ← p.height; r.width ← p.width; r.depth ← p.depth; r.link ← stringLpad.link; stringLpad.link ← r}; p ← p.link}; delete => IF p.b3.copy # NIL AND p.b4.copy # NIL THEN {q.link ← p.link; GcString[p]; p ← q.link} ELSE {q ← p; p ← p.link}; mark => {p.b3.copy ← NIL; p.b4.copy ← NIL; p ← p.link }; build => {Pack: PROC[b, i: BOOL] RETURNS [r:INT] = {r ← 0; IF i THEN r ← r + 1; IF b THEN r ← r + 2}; IF p.b3.name # NIL THEN AddAction [leftpren, $print, NewArgs[LIST[p.stringText, p.b3, Atom.MakeAtom[p.fontName], NEW[INT ← p.fontSize], NEW[INT ← Pack[p.bold, p.italic]]]]]; p ← p.link} ENDCASE => ERROR; ENDLOOP}; { -- scan the horizontal constraints: p: HorPtr ← horLpad.link; q: HorPtr ← horLpad; r: HorPtr; UNTIL p = horRpad DO SELECT operation FROM move => {IF p.i.copy # NIL THEN p.i ← p.i.copy; IF p.j.copy # NIL THEN p.j ← p.j.copy; p ← p.link}; copy => {IF p.i.copy # NIL AND p.j.copy # NIL THEN {r ← NewHor[]; r.i ← p.i.copy; r.j ← p.j.copy; r.link ← horLpad.link; horLpad.link ← r}; p ← p.link}; delete => IF p.i.copy # NIL AND p.j.copy # NIL THEN {q.link ← p.link; GcHor[p]; p ← q.link} ELSE {q ← p; p ← p.link}; mark => {p.i.copy ← NIL; p.j.copy ← NIL; p ← p.link}; build => {IF p.i.name # NIL AND p.j.name # NIL THEN AddPred[$hor, PrenArgs[LIST[p.i, p.j]]]; p ← p.link} ENDCASE => ERROR; ENDLOOP}; { -- scan the vertical constraints: p: VerPtr ← verLpad.link; q: VerPtr ← verLpad; r: VerPtr; UNTIL p = verRpad DO SELECT operation FROM move => {IF p.i.copy # NIL THEN p.i ← p.i.copy; IF p.j.copy # NIL THEN p.j ← p.j.copy; p ← p.link}; copy => {IF p.i.copy # NIL AND p.j.copy # NIL THEN {r ← NewVer[]; r.i ← p.i.copy; r.j ← p.j.copy; r.link ← verLpad.link; verLpad.link ← r}; p ← p.link}; delete => IF p.i.copy # NIL AND p.j.copy # NIL THEN {q.link ← p.link; GcVer[p]; p ← q.link} ELSE {q ← p; p ← p.link}; mark => {p.i.copy ← NIL; p.j.copy ← NIL; p ← p.link}; build => {IF p.i.name # NIL AND p.j.name # NIL THEN AddPred[$ver, PrenArgs[LIST[p.i, p.j]]]; p ← p.link} ENDCASE => ERROR; ENDLOOP}; {-- scan the congruence constraints: p: CongPtr ← congLpad.link; q: CongPtr ← congLpad; r: CongPtr; UNTIL p = congRpad DO SELECT operation FROM move => {IF p.i.copy # NIL THEN p.i ← p.i.copy; IF p.j.copy # NIL THEN p.j ← p.j.copy; IF p.k.copy # NIL THEN p.k ← p.k.copy; IF p.l.copy # NIL THEN p.l ← p.l.copy; p ← p.link}; copy => {IF p.i.copy # NIL AND p.j.copy # NIL AND p.k.copy # NIL AND p.l.copy # NIL THEN {r ← NewCong[]; r.i ← p.i.copy; r.j ← p.j.copy; r.k ← p.k.copy; r.l ← p.l.copy; r.link ← congLpad.link; congLpad.link ← r}; p ← p.link}; delete => IF p.i.copy # NIL AND p.j.copy # NIL AND p.k.copy # NIL AND p.l.copy # NIL THEN {q.link ← p.link; GcCong[p]; p ← q.link} ELSE {q ← p; p ← p.link}; mark => {p.i.copy ← NIL; p.j.copy ← NIL; p.k.copy ← NIL; p.l.copy ← NIL; p ← p.link}; build => {IF p.i.name # NIL AND p.j.name # NIL AND p.k.name # NIL AND p.l.name # NIL THEN AddPred[$cong, PrenArgs[LIST[p.i, p.j]], PrenArgs[LIST[p.k, p.l]]]; p ← p.link} ENDCASE => ERROR; ENDLOOP}; {-- scan the line constraints: p: LinPtr ← lineLpad.link; q: LinPtr ← lineLpad; r: LinPtr; UNTIL p = lineRpad DO SELECT operation FROM move => {IF p.i.copy # NIL THEN p.i ← p.i.copy; IF p.j.copy # NIL THEN p.j ← p.j.copy; IF p.k.copy # NIL THEN p.k ← p.k.copy; IF p.l.copy # NIL THEN p.l ← p.l.copy; p ← p.link}; copy => {IF p.i.copy # NIL AND p.j.copy # NIL AND p.k.copy # NIL AND p.l.copy # NIL THEN {r ← NewLine[]; r.i ← p.i.copy; r.j ← p.j.copy; r.k ← p.k.copy; r.l ← p.l.copy; r.link ← lineLpad.link; lineLpad.link ← r}; p ← p.link}; delete => IF p.i.copy # NIL AND p.j.copy # NIL AND p.k.copy # NIL AND p.l.copy # NIL THEN {q.link ← p.link; GcLine[p]; p ← q.link} ELSE {q ← p; p ← p.link}; mark => {p.i.copy ← NIL; p.j.copy ← NIL; p.k.copy ← NIL; p.l.copy ← NIL; p ← p.link}; build => {IF p.i.name # NIL AND p.j.name # NIL AND p.k.name # NIL AND p.l.name # NIL THEN AddPred[$para, PrenArgs[LIST[p.i, p.j]], PrenArgs[LIST[p.k, p.l]]]; p ← p.link} ENDCASE => ERROR; ENDLOOP}; {-- scan the counter-clockwise constraints: p: CCPtr ← ccLpad.link; q: CCPtr ← ccLpad; r: CCPtr; UNTIL p = ccRpad DO SELECT operation FROM move => {IF p.i.copy # NIL THEN p.i ← p.i.copy; IF p.j.copy # NIL THEN p.j ← p.j.copy; IF p.k.copy # NIL THEN p.k ← p.k.copy; p ← p.link}; copy => {IF p.i.copy # NIL AND p.j.copy # NIL AND p.k.copy # NIL THEN {r ← NewCC[]; r.i ← p.i.copy; r.j ← p.j.copy; r.k ← p.k.copy; r.link ← ccLpad.link; ccLpad.link ← r}; p ← p.link}; delete => IF p.i.copy # NIL AND p.j.copy # NIL AND p.k.copy # NIL THEN {q.link ← p.link; GcCC[p]; p ← q.link} ELSE {q ← p; p ← p.link}; mark => {p.i.copy ← NIL; p.j.copy ← NIL; p.k.copy ← NIL; p ← p.link}; ENDCASE => ERROR; ENDLOOP}; { -- scan the algebraic constructions p: LIST OF ApplyRecord ← constructionList; q: LIST OF PointPtr ← NIL; r: LIST OF ApplyRecord ← NIL; UNTIL p = NIL DO SELECT operation FROM move => {q ← p.first.args; UNTIL q = NIL DO IF q.first.copy # NIL THEN q.first ← q.first.copy; q ← q.rest ENDLOOP; p ← p.rest}; copy => {IF AllHaveCopies[p.first.args] THEN AddX[p.first.f, ListOfCopies[p.first.args]]; p ← p.rest}; delete => {IF AllHaveCopies[p.first.args] THEN {IF r = NIL THEN {constructionList ← constructionList.rest; p ← p.rest} ELSE {r.rest ← p.rest; p ← p.rest}} ELSE {r ← p; p ← p.rest}}; mark => {SetCopiesToNil[p.first.args]; p ← p.rest}; build => {IF AllHaveNames[p.first.args] THEN actionList ← CONS[LIST[leftpren, p.first.f, Args[p.first.args]], actionList]; p ← p.rest} ENDCASE => ERROR; ENDLOOP} END; -- finally. AllHaveCopies: PROC[l: LIST OF PointPtr] RETURNS [BOOL] = {RETURN [l = NIL OR l.first.copy # NIL AND AllHaveCopies[l.rest]]}; AllHaveNames: PROC[l: LIST OF PointPtr] RETURNS [BOOL] = {RETURN [l = NIL OR l.first.name # NIL AND AllHaveNames[l.rest]]}; ListOfCopies: PROC [l: LIST OF PointPtr] RETURNS [LIST OF PointPtr] = {IF l = NIL THEN RETURN [NIL]; RETURN [CONS[l.first.copy, ListOfCopies[l.rest]]]}; SetCopiesToNil: PROC [l: LIST OF PointPtr] = {IF l # NIL THEN {l.first.copy ← NIL; SetCopiesToNil[l.rest]}}; Delete: PUBLIC PROCEDURE = BEGIN p: PointPtr ← pointLpad.slink; UNTIL p = pointRpad DO p.copy ← p; p ← p.slink ENDLOOP; ScanLists[delete]; ScanLists[mark]; DeleteOriginals[]; END; DeleteOriginals: PROC[] = BEGIN p, q: PointPtr; p ← pointLpad.link; q ← pointLpad; UNTIL p = pointRpad DO IF p.copy = NIL THEN {q ← p; p ← p.link} ELSE {p.copy ← NIL; q.link ← p.link; GcPoint[p]; p ← q.link}; ENDLOOP; END; END.