DIRECTORY MathBoxTree, MathExpr, MathBox, MathConstructors, MathDB, MathTypes, MathRules, Convert USING [RopeFromAtom], Rope USING [ROPE, Length, Find, Replace, Cat], Imager USING [Context, DoSaveAll, MaskRectangle, MaskStrokeTrajectory, SetColor, SetStrokeWidth], ImagerBackdoor USING [invert], ImagerFont USING [Extents, Find, Font, RopeBoundingBox, Scale], ImagerPath USING [LineToX, LineToY, MoveTo], Vector2 USING [InlineAdd], XRope; MathBoxTreesImpl: CEDAR PROGRAM IMPORTS MathBox, MathExpr, MathDB, MathConstructors, Imager, ImagerBackdoor, ImagerFont, ImagerPath, MathRules, Rope, Convert, Vector2, XRope EXPORTS MathBoxTree ~ BEGIN ROPE: TYPE ~ Rope.ROPE; BoxTreeDataRep: TYPE ~ RECORD [ SELECT type:* FROM pureBoxTree => [ -- display subtree of a leafObject; "pure" since just Boxes, no Objects. root: Box, -- cannot be NIL since visibility/non-visibility is a property of Objects (i.e. only of ancestors of pureBoxTrees) parent: BoxTreeData, -- cannot be NIL since (root of) a pureBoxTree is always a child either of some pureBoxTree, or of some leafObject. children: LIST OF BoxTreeData _ NIL -- All children of a pureBoxTree are pureBoxTrees. ], leafObject => [ -- i.e. a "packed Object", i.e. no subcomponent selectable root: Box _ NIL, -- object visible iff root # NIL parent: BoxTreeData _ NIL, -- root is an absolute Box iff parent = NIL (otherwise relative) children: LIST OF BoxTreeData _ NIL -- All children of a leafObject are pureBoxTrees. object: Object, ], interiorObject => [ -- "unpacked" (to at least one level) Object display root: Box _ NIL, -- bounding, i.e. outermost, box for the display of self. All subBoxes assumed to be contained in self.args and self.opDisplay. parent: BoxTreeData _ NIL, -- root is an absolute Box iff parent = NIL (otherwise relative) op: ATOM, domain: Object, -- Domain or View args: LIST OF BoxTreeData, -- cannot be NIL since this is display of an unpacked Object opDisplay: LIST OF BoxTreeData _ NIL ENDCASE ]; BoxTree: TYPE ~ REF BoxTreeRep; -- inside impl module, use rep MakeAtomicBoxTree: PUBLIC PROC[tag: ATOM, class: AtomClass, value: ROPE, relBox, absBox: BOX, parent: BoxTree _ NIL] RETURNS[BoxTree] ~ { RETURN[NEW[BoxTreeRep _ [tag: tag, relativeBox: relBox, absoluteBox: absBox, parent: parent, expression: NEW[AtomDisplayObjRep _ [atom[class: class, value: value]]]]]]; }; MakeCompoundBoxTree: PUBLIC PROC[tag: ATOM, class: CompoundClass, subExprs: LIST OF BoxTree, relBox, absBox: BOX, parent: BoxTree _ NIL] RETURNS[BoxTree] ~ { RETURN[NEW[BoxTreeRep _ [tag: tag, relativeBox: relBox, absoluteBox: absBox, parent: parent, expression: NEW[CompoundDisplayObjRep _ [compound[class: class, subExprs: subExprs]]]]]]; }; MakeMatrixBoxTree: PUBLIC PROC[tag: ATOM, class: MatrixClass, nRows, nCols: INT, elements: LIST OF BoxTree, relBox, absBox: BOX, openSym, closeSym, space: BoxTree, parent: BoxTree _ NIL] RETURNS[BoxTree] ~ { RETURN[NEW[BoxTreeRep _ [tag: tag, relativeBox: relBox, absoluteBox: absBox, parent: parent, expression: NEW[MatrixDisplayObjRep _ [matrix[class: class, nRows: nRows, nCols: nCols, elements: elements, openSym: openSym, closeSym: closeSym, space: space]]]]]]; }; ASRopeFromBoxTree: PUBLIC PROC[expr: BoxTree, flavor: ATOM _ $AS] RETURNS[ROPE] ~ { WITH expr.expression SELECT FROM a: AtomDisplayObj => SELECT flavor FROM $Reduce => IF a.class.cvtReduceRope # NIL THEN RETURN[a.class.cvtReduceRope[a.value]] ELSE RETURN[a.value]; $SMP => IF a.class.cvtSMPRope # NIL THEN RETURN[a.class.cvtSMPRope[a.value]] ELSE RETURN[a.value]; ENDCASE => IF a.class.cvtASRope # NIL THEN RETURN[a.class.cvtASRope[a.value]] ELSE RETURN[a.value]; c: CompoundDisplayObj => { template: ROPE _ SELECT flavor FROM $Reduce => c.class.cvtReduce, -- template for return value $SMP => c.class.cvtSMP, -- template for return value ENDCASE => c.class.cvtAS; -- template for return value argName: ROPE _ NIL; position: INT _ -1; IF Rope.Length[template] < 1 THEN RETURN[""]; FOR l: LIST OF Argument _ c.class.arguments, l.rest UNTIL l = NIL DO argName _ Convert.RopeFromAtom[l.first.name]; position _ Rope.Find[template, argName]; IF position # -1 THEN template _ Rope.Replace[base: template, start: position, len: Rope.Length[argName], with: ASRopeFromBoxTree[GetBoxTree[l.first.name, c.subExprs], flavor] ]; ENDLOOP; IF c.class.name = $complex THEN { position _ Rope.Find[template, "+ -"]; IF position # -1 THEN template _ Rope.Replace[base: template, start: position, len: 3, with: "- "]; }; RETURN[template]; }; m: MatrixDisplayObj => { Element: TYPE ~ RECORD [ expr: BoxTree _ NIL ]; Row: TYPE ~ REF RowRec; RowRec: TYPE ~ RECORD [ elements: SEQUENCE numberOfCols: NAT OF Element ]; Matrix: TYPE ~ REF MatrixRec; MatrixRec: TYPE ~ RECORD[ rows: SEQUENCE numberOfRows: NAT OF Row ]; a: Matrix; currentRow, currentCol: NAT; result: ROPE _ NIL; -- return value a _ NEW[MatrixRec[m.nRows]]; FOR row:INT IN [0..m.nRows) DO a[row] _ NEW[RowRec[m.nCols]]; ENDLOOP; FOR l: LIST OF BoxTree _ m.elements, l.rest UNTIL l = NIL DO [currentRow, currentCol] _ MathRules.RowColFromAtom[l.first.tag]; a[currentRow - 1][currentCol - 1].expr _ l.first; ENDLOOP; SELECT m.class.name FROM $matrix => { result _ "List[ "; FOR r:INT IN [0..m.nRows) DO result _ Rope.Cat[result, "List[ "]; -- row start char FOR c:INT IN [0..m.nCols) DO result _ Rope.Cat[result, ASRopeFromBoxTree[a[r][c].expr, flavor]]; IF c # (m.nCols - 1) THEN result _ Rope.Cat[result, ", "]; -- col separator ENDLOOP; result _ Rope.Cat[result, " ]"]; -- row end char IF r # (m.nRows - 1) THEN result _ Rope.Cat[result, ", "]; -- row separator ENDLOOP; result _ Rope.Cat[result, " ]"]; -- end with matrix end char }; $vector, $sequence, $set, $point => { result _ "List[ "; -- should select set, sequence, etc. IF m.nRows = 1 THEN { -- row vector FOR c: INT IN [0..m.nCols) DO result _ Rope.Cat[result, ASRopeFromBoxTree[a[0][c].expr, flavor]]; IF c # (m.nCols - 1) THEN result _ Rope.Cat[result, ", "]; -- col separator ENDLOOP; } ELSE { -- column vector FOR r: INT IN [0..m.nRows) DO result _ Rope.Cat[result, ASRopeFromBoxTree[a[r][0].expr, flavor]]; IF r # (m.nRows - 1) THEN result _ Rope.Cat[result, ", "]; -- row separator ENDLOOP; }; result _ Rope.Cat[result, " ]"]; }; ENDCASE => result _ "unknownMatrixType"; RETURN[result]; }; ENDCASE => ERROR; }; BoxTreeFromExpr: PUBLIC PROC[expr: EXPR] RETURNS[BoxTree] ~ { myBoxTree, tempBoxTree: BoxTree; emptyAtom: ATOM _ $expr; -- generic identifier emptyRelBox: BOX _ MathBox.MakeBox[emptyAtom, NIL, other, relative]; emptyAbsBox: BOX _ MathBox.MakeBox[emptyAtom, NIL, other, absolute]; SELECT expr.GetType[] FROM atom => { a: AtomEXPR _ expr.GetAtomExpr[]; RETURN[MakeAtomicBoxTree[emptyAtom, MathExpr.GetAtomClass[a], MathExpr.GetValue[a], emptyRelBox, emptyAbsBox]]; }; compound => { c: CompoundEXPR _ expr.GetCompoundExpr[]; compClass: CompoundClass _ MathExpr.GetCompoundClass[c]; displaySubExprs: LIST OF BoxTree _ NIL; -- cons up list of display expressions FOR l: LIST OF Argument _ compClass.arguments, l.rest UNTIL l = NIL DO tempBoxTree _ BoxTreeFromExpr[MathExpr.GetTaggedExpr[l.first.name, MathExpr.GetSubExprs[c] ! exprNotFound => {ERROR unable[$missingArg]}].expression]; tempBoxTree.tag _ l.first.name; -- set name tag for display expr displaySubExprs _ CONS[tempBoxTree, displaySubExprs]; ENDLOOP; FOR l: LIST OF Symbol _ compClass.symbols, l.rest UNTIL l = NIL DO tempBoxTree _ BoxTreeFromExpr[MathExpr.GetTaggedExpr[l.first.name, MathExpr.GetSubExprs[c] ! exprNotFound => {ERROR unable[$missingSym]}].expression]; tempBoxTree.tag _ l.first.name; -- set name tag for display expr displaySubExprs _ CONS[tempBoxTree, displaySubExprs]; ENDLOOP; myBoxTree _ MakeCompoundBoxTree[emptyAtom, MathExpr.GetCompoundClass[c], displaySubExprs, emptyRelBox, emptyAbsBox]; FOR l: LIST OF BoxTree _ displaySubExprs, l.rest UNTIL l = NIL DO l.first.parent _ myBoxTree; -- set parent ENDLOOP; RETURN[myBoxTree]; }; matrix => { m: MatrixEXPR _ expr.GetMatrixExpr[]; elts: LIST OF MathExpr.TaggedMathExpr _ MathExpr.GetMatrixElements[m]; class: MatrixClass _ MathExpr.GetMatrixClass[m]; displayElts, displayEltsPointer: LIST OF BoxTree _ NIL; nRows, nCols: NAT; openSym, closeSym, space: BoxTree; [nRows, nCols] _ MathExpr.GetMatrixSize[m]; FOR l: LIST OF MathExpr.TaggedMathExpr _ elts, l.rest UNTIL l = NIL DO tempBoxTree _ BoxTreeFromExpr[l.first.expression]; tempBoxTree.tag _ l.first.id; -- set name tag for display expr IF displayElts = NIL THEN displayElts _ displayEltsPointer _ LIST[tempBoxTree] ELSE displayEltsPointer _ displayEltsPointer.rest _ LIST[tempBoxTree]; ENDLOOP; openSym _ BoxTreeFromExpr[class.openSym]; closeSym _ BoxTreeFromExpr[class.closeSym]; space _ BoxTreeFromExpr[class.space]; myBoxTree _ MakeMatrixBoxTree[emptyAtom, class, nRows, nCols, displayElts, emptyRelBox, emptyAbsBox, openSym, closeSym, space]; FOR l: LIST OF BoxTree _ displayElts, l.rest UNTIL l = NIL DO l.first.parent _ myBoxTree; -- set parent ENDLOOP; RETURN[myBoxTree]; }; ENDCASE => ERROR; }; ExprFromBoxTree: PUBLIC PROC[expr: BoxTree] RETURNS[EXPR] ~ { WITH expr.expression SELECT FROM a: AtomDisplayObj => { RETURN[MathExpr.MakeAtomicExpr[a.class.name, a.value]]; }; c: CompoundDisplayObj => { args: LIST OF MathExpr.TaggedMathExpr _ NIL; FOR l: LIST OF BoxTree _ c.subExprs, l.rest UNTIL l = NIL DO args _ CONS[[l.first.tag, ExprFromBoxTree[l.first]], args]; ENDLOOP; RETURN[MathExpr.MakeCompoundExpr[c.class.name, args]]; }; m: MatrixDisplayObj => { elts, eltsPointer: LIST OF MathExpr.TaggedMathExpr _ NIL; FOR l: LIST OF BoxTree _ m.elements, l.rest UNTIL l = NIL DO -- changed 3/4/87 taggedMathExpr: MathExpr.TaggedMathExpr _ [l.first.tag, ExprFromBoxTree[l.first]]; IF elts = NIL THEN elts _ eltsPointer _ LIST[taggedMathExpr] ELSE eltsPointer _ eltsPointer.rest _ LIST[taggedMathExpr]; ENDLOOP; RETURN[MathExpr.MakeMatrixExpr[m.class.name, m.nRows, m.nCols, elts]]; }; ENDCASE => ERROR; }; Copy: PUBLIC PROC[expr: BoxTree] RETURNS[BoxTree] ~ { WITH expr.expression SELECT FROM a: AtomDisplayObj => { RETURN[MakeAtomicBoxTree[expr.tag, a.class, a.value, expr.relativeBox, expr.absoluteBox]]; }; c: CompoundDisplayObj => { exprCopy: BoxTree; -- return value copiedSubExprs: LIST OF BoxTree _ NIL; -- cons up new list of copies FOR l: LIST OF BoxTree _ c.subExprs, l.rest UNTIL l = NIL DO copiedSubExprs _ CONS[Copy[l.first], copiedSubExprs]; ENDLOOP; exprCopy _ MakeCompoundBoxTree[expr.tag, c.class, copiedSubExprs, expr.relativeBox, expr.absoluteBox]; FOR l: LIST OF BoxTree _ copiedSubExprs, l.rest UNTIL l = NIL DO l.first.parent _ exprCopy; -- set parent pointers appropriately ENDLOOP; RETURN[exprCopy]; }; m: MatrixDisplayObj => { exprCopy: BoxTree; -- return value copiedElts, copiedEltsPtr: LIST OF BoxTree _ NIL; -- cons up a new list of copied elements FOR l: LIST OF BoxTree _ m.elements, l.rest UNTIL l = NIL DO IF copiedElts = NIL THEN copiedElts _ copiedEltsPtr _ LIST[Copy[l.first] ] ELSE copiedEltsPtr _ copiedEltsPtr.rest _ LIST[Copy[l.first] ]; ENDLOOP; exprCopy _ MakeMatrixBoxTree[expr.tag, m.class, m.nRows, m.nCols, copiedElts, expr.relativeBox, expr.absoluteBox, Copy[m.openSym], Copy[m.closeSym], Copy[m.space]]; FOR l: LIST OF BoxTree _ copiedElts, l.rest UNTIL l = NIL DO l.first.parent _ exprCopy; -- set parent pointers appropriately ENDLOOP; RETURN[exprCopy]; }; ENDCASE => ERROR; }; Tag: PUBLIC PROC[expr: BoxTree] RETURNS[ATOM] ~ { RETURN[expr.tag]; }; Class: PUBLIC PROC[expr: BoxTree] RETURNS[ATOM] ~ { WITH expr.expression SELECT FROM a: AtomDisplayObj => RETURN[a.class.name]; c: CompoundDisplayObj => RETURN[c.class.name]; m: MatrixDisplayObj => RETURN[m.class.name]; ENDCASE => ERROR; }; GetSubExprs: PUBLIC PROC[expr: BoxTree] RETURNS[LIST OF BoxTree] ~ { WITH expr.expression SELECT FROM a: AtomDisplayObj => RETURN[NIL]; -- atoms don't have any children c: CompoundDisplayObj => RETURN[c.subExprs]; m: MatrixDisplayObj => RETURN[m.elements]; ENDCASE => ERROR; }; AbsBox: PUBLIC PROC[expr: BoxTree] RETURNS[BOX] ~ { RETURN[expr.absoluteBox]; }; Format: PUBLIC PROC[expr: BoxTree, size: Size] RETURNS[BOX] ~ { fontName: ROPE ~ "Xerox/XC1-2-2/TimesRoman-italic"; scale: REAL ~ 1.0; font: ImagerFont.Font ~ ImagerFont.Scale[ImagerFont.Find[fontName], scale]; a: ImagerFont.Extents ~ ImagerFont.RopeBoundingBox[font, "="]; -- for ``axis'' height axisHeight: REAL ~ (a.ascent-a.descent)/2; innerBox: BOX _ InnerFormat[expr, size]; extents: ImagerFont.Extents _ innerBox.Extents; extents.ascent _ extents.ascent + axisHeight; extents.descent _ extents.descent - axisHeight; innerBox _ MathBox.ChangeOffset[innerBox, innerBox.Offset[].InlineAdd[[0.0, axisHeight]]]; RETURN[innerBox]; }; InnerFormat: PROC[expr: BoxTree, size: Size] RETURNS[BOX] ~ { subExprBoxes: LIST OF BOX; myBox, tempBox: BOX; WITH expr.expression SELECT FROM a: AtomDisplayObj => { RETURN[MathBox.Scale[MathBox.MakeBox[tag: a.class.name, aliases: NIL, formatClass: a.class.formatClass, type: absolute, extents: a.class.boxRule[a.value, a.class.style]], MathRules.VecFromSize[size]]]; }; c: CompoundDisplayObj => { subExprBoxes _ NIL; -- cons up list of subexpression boxes FOR l: LIST OF Argument _ c.class.arguments, l.rest UNTIL l = NIL DO tempBox _ InnerFormat[GetBoxTree[l.first.name, c.subExprs ! exprNotFound => {ERROR unable[$missingArg]}], MathRules.ComputeSize[base: size, adjustment: l.first.size]]; subExprBoxes _ CONS[MathBox.MakeBox[l.first.name, l.first.aliases, tempBox.FormatClass[], tempBox.Type[], tempBox.Extents[], tempBox.Offset[], tempBox.SubscriptHint[], tempBox.SuperscriptHint[]], subExprBoxes]; ENDLOOP; FOR l: LIST OF Symbol _ c.class.symbols, l.rest UNTIL l = NIL DO tempBox _ InnerFormat[GetBoxTree[l.first.name, c.subExprs ! exprNotFound => {ERROR unable[$missingSym]}], MathRules.ComputeSize[base: size, adjustment: l.first.size]]; subExprBoxes _ CONS[MathBox.MakeBox[l.first.name, l.first.aliases, tempBox.FormatClass[], tempBox.Type[], tempBox.Extents[], tempBox.Offset[], tempBox.SubscriptHint[], tempBox.SuperscriptHint[]], subExprBoxes]; ENDLOOP; subExprBoxes _ c.class.boxRule[subExprBoxes]; [myBox, subExprBoxes] _ c.class.compBox[subExprBoxes]; myBox _ MathBox.ChangeFormatClass[myBox, c.class.formatClass]; FOR l: LIST OF BoxTree _ c.subExprs, l.rest UNTIL l = NIL DO IF l.first.relativeBox.Type # relative THEN ERROR unable[$wrongBoxType]; l.first.relativeBox _ MathBox.GetBox[l.first.tag, subExprBoxes ! MathBox.boxNotFound => {ERROR unable[$boxNotFound]}]; -- set box ENDLOOP; RETURN[myBox]; }; m: MatrixDisplayObj => { elementBoxes, elementBoxesPtr: LIST OF BOX _ NIL; tempBox, spaceBox, openSymBox, closeSymBox: BOX; FOR l: LIST OF BoxTree _ m.elements, l.rest UNTIL l = NIL DO tempBox _ InnerFormat[l.first, size]; IF elementBoxes = NIL THEN elementBoxes _ elementBoxesPtr _ LIST[ MathBox.MakeBox[l.first.tag, NIL, tempBox.FormatClass[], tempBox.Type[], tempBox.Extents[], tempBox.Offset[], tempBox.SubscriptHint[], tempBox.SuperscriptHint[] ] ] ELSE elementBoxesPtr _ elementBoxesPtr.rest _ LIST[ MathBox.MakeBox[l.first.tag, NIL, tempBox.FormatClass[], tempBox.Type[], tempBox.Extents[], tempBox.Offset[], tempBox.SubscriptHint[], tempBox.SuperscriptHint[] ] ]; ENDLOOP; spaceBox _ InnerFormat[m.space, MathRules.ComputeSize[base: size, adjustment: script]]; openSymBox _ InnerFormat[m.openSym, normal]; closeSymBox _ InnerFormat[m.closeSym, normal]; [myBox, elementBoxes, m.openSym.relativeBox, m.closeSym.relativeBox] _ MathRules.ComposeMatrix[m.nRows, m.nCols, elementBoxes, spaceBox, openSymBox, closeSymBox]; myBox _ MathBox.ChangeFormatClass[myBox, m.class.formatClass]; FOR l: LIST OF BoxTree _ m.elements, l.rest UNTIL l = NIL DO IF l.first.relativeBox.Type # relative THEN ERROR unable[$wrongBoxType]; l.first.relativeBox _ MathBox.GetBox[l.first.tag, elementBoxes ! MathBox.boxNotFound => {ERROR unable[$boxNotFound]}]; -- set box ENDLOOP; RETURN[myBox]; }; ENDCASE => ERROR; }; Paint: PUBLIC PROC[expr: BoxTree, context: Imager.Context, absBox: BOX, selections: LIST OF Selection] ~ { currentBoxTree: BoxTree; IF absBox.Type # absolute THEN ERROR unable[$wrongBoxType]; expr.absoluteBox _ absBox; WITH expr.expression SELECT FROM a: AtomDisplayObj => { a.class.paintRule[a.value, a.class.style, context, absBox]; }; c: CompoundDisplayObj => { FOR l: LIST OF Argument _ c.class.arguments, l.rest UNTIL l = NIL DO currentBoxTree _ GetBoxTree[l.first.name, c.subExprs ! exprNotFound => {ERROR unable[$missingArg]}]; Paint[currentBoxTree, context, MathBox.RelToAbsBox[currentBoxTree.relativeBox, absBox], selections]; ENDLOOP; FOR l: LIST OF Symbol _ c.class.symbols, l.rest UNTIL l = NIL DO currentBoxTree _ GetBoxTree[l.first.name, c.subExprs ! exprNotFound => {ERROR unable[$missingArg]}]; Paint[currentBoxTree, context, MathBox.RelToAbsBox[currentBoxTree.relativeBox, absBox], selections]; ENDLOOP; }; m: MatrixDisplayObj => { FOR l: LIST OF BoxTree _ m.elements, l.rest UNTIL l = NIL DO Paint[l.first, context, MathBox.RelToAbsBox[l.first.relativeBox, absBox], selections]; ENDLOOP; Paint[m.openSym, context, MathBox.RelToAbsBox[m.openSym.relativeBox, absBox], selections]; Paint[m.closeSym, context, MathBox.RelToAbsBox[m.closeSym.relativeBox, absBox], selections]; }; ENDCASE => ERROR; FOR l: LIST OF Selection _ selections, l.rest UNTIL l = NIL DO IF (expr = l.first.expr) THEN { HighlightProc: PROC ~ { fat: REAL _ 3; context.SetColor[l.first.color]; IF NOT l.first.borderOnly THEN { Imager.MaskRectangle[context, [ x: absBox.Offset[].x - absBox.Extents[].leftExtent, y: absBox.Offset[].y - absBox.Extents[].descent, w: absBox.Width[], h: absBox.Height[]]]; fat _ 1; context.SetColor[ImagerBackdoor.invert]; }; Imager.SetStrokeWidth[context, 2.0*fat]; Imager.MaskStrokeTrajectory[context, ImagerPath.LineToX[ ImagerPath.LineToY[ ImagerPath.LineToX[ ImagerPath.LineToY[ ImagerPath.MoveTo[ [ absBox.Offset[].x - (absBox.Extents[].leftExtent + fat), absBox.Offset[].y - (absBox.Extents[].descent + fat)]], absBox.Offset[].y + (absBox.Extents[].ascent + fat)], absBox.Offset[].x + (absBox.Extents[].rightExtent + fat)], absBox.Offset[].y - (absBox.Extents[].descent + fat)], absBox.Offset[].x - (absBox.Extents[].leftExtent + fat)], TRUE]; }; Imager.DoSaveAll[context, HighlightProc]; }; ENDLOOP; }; GetBoxTree: PUBLIC PROC[tag: ATOM, exprs: LIST OF BoxTree] RETURNS[BoxTree] ~ { FOR l: LIST OF BoxTree _ exprs, l.rest UNTIL l = NIL DO IF l.first.tag = tag THEN RETURN[l.first]; ENDLOOP; ERROR exprNotFound; }; BoxTreeFromCoords: PUBLIC PROC[expr: BoxTree, x, y: REAL] RETURNS[BoxTree] ~ { IF expr = NIL THEN ERROR noSelection; IF ~MathBox.Inside[expr.absoluteBox, x, y] THEN ERROR noSelection; WITH expr.expression SELECT FROM a: AtomDisplayObj => { IF Selectable[expr] THEN RETURN[expr] ELSE ERROR noSelection; }; c: CompoundDisplayObj => { FOR l: LIST OF BoxTree _ c.subExprs, l.rest UNTIL l = NIL DO IF MathBox.Inside[l.first.absoluteBox, x, y] THEN RETURN[BoxTreeFromCoords[l.first, x, y ! noSelection => CONTINUE]]; ENDLOOP; IF Selectable[expr] THEN RETURN[expr] ELSE ERROR noSelection; }; m: MatrixDisplayObj => { FOR l: LIST OF BoxTree _ m.elements, l.rest UNTIL l = NIL DO IF MathBox.Inside[l.first.absoluteBox, x, y] THEN RETURN[BoxTreeFromCoords[l.first, x, y ! noSelection => CONTINUE]]; ENDLOOP; IF Selectable[expr] THEN RETURN[expr] ELSE ERROR noSelection; }; ENDCASE => ERROR; }; Selectable: PUBLIC PROC[expr: BoxTree] RETURNS[BOOL] ~ { IF expr = NIL THEN RETURN[FALSE]; WITH expr.expression SELECT FROM a: AtomDisplayObj => { IF a.class.flavor = symbol THEN RETURN[FALSE] ELSE RETURN[TRUE]; }; c: CompoundDisplayObj => RETURN[TRUE]; m: MatrixDisplayObj => RETURN[TRUE]; ENDCASE => ERROR; }; SelectableParent: PUBLIC PROC[expr: BoxTree] RETURNS[BoxTree] ~ { IF (expr = NIL) OR (expr.parent) = NIL THEN ERROR noSelection; RETURN[expr.parent]; }; SelectableChild: PUBLIC PROC[expr: BoxTree] RETURNS[BoxTree] ~ { childExpr: BoxTree _ NIL; IF expr = NIL THEN ERROR noSelection; WITH expr.expression SELECT FROM a: AtomDisplayObj => ERROR noSelection; -- atom has no children c: CompoundDisplayObj => { FOR l: LIST OF Argument _ c.class.arguments, l.rest UNTIL l = NIL DO childExpr _ GetBoxTree[l.first.name, c.subExprs]; IF Selectable[childExpr] THEN RETURN[childExpr]; -- return selected expression ENDLOOP; ERROR noSelection; }; m: MatrixDisplayObj => { ERROR noSelection; }; ENDCASE => ERROR; }; ExtendFunctionSelectSibling: PROC [expr: BoxTree] RETURNS [BoxTree] ~ { emptyAtom: ATOM _ $expr; -- generic identifier emptyRelBox: BOX _ MathBox.MakeBox[emptyAtom, NIL, other, relative]; emptyAbsBox: BOX _ MathBox.MakeBox[emptyAtom, NIL, other, absolute]; arg12Space: BoxTree _ MakeAtomicBoxTree[$arg12Space, MathDB.LookupAtomClass[$space], Convert.RopeFromAtom[$medium, FALSE], emptyRelBox, emptyAbsBox]; arg23Space: BoxTree _ MakeAtomicBoxTree[$arg23Space, MathDB.LookupAtomClass[$space], Convert.RopeFromAtom[$medium, FALSE], emptyRelBox, emptyAbsBox]; arg34Space: BoxTree _ MakeAtomicBoxTree[$arg34Space, MathDB.LookupAtomClass[$space], Convert.RopeFromAtom[$medium, FALSE], emptyRelBox, emptyAbsBox]; comma12: BoxTree _ MakeAtomicBoxTree[$comma12, MathDB.LookupAtomClass[$italicSym], XRope.FromChar[',] , emptyRelBox, emptyAbsBox]; comma23: BoxTree _ MakeAtomicBoxTree[$comma23, MathDB.LookupAtomClass[$italicSym], XRope.FromChar[',] , emptyRelBox, emptyAbsBox]; comma34: BoxTree _ MakeAtomicBoxTree[$comma34, MathDB.LookupAtomClass[$italicSym], XRope.FromChar[',] , emptyRelBox, emptyAbsBox]; newExprs: LIST OF BoxTree; sibling: BoxTree _ BoxTreeFromExpr[MathConstructors.MakePlaceHolder[] ]; sibling.parent _ expr.parent; WITH expr.parent.expression SELECT FROM c: CompoundDisplayObj => SELECT c.class.name FROM $nullaryFunction, $unaryFunction, $binaryFunction, $ternaryFunction => { SELECT c.class.name FROM $nullaryFunction => { c.class _ MathDB.LookupCompoundClass[$unaryFunction]; sibling.tag _ $arg1; newExprs _ LIST[sibling] }; $unaryFunction => { c.class _ MathDB.LookupCompoundClass[$binaryFunction]; sibling.tag _ $arg2; newExprs _ LIST[sibling, arg12Space, comma12] }; $binaryFunction => { c.class _ MathDB.LookupCompoundClass[$ternaryFunction]; sibling.tag _ $arg3; newExprs _ LIST[sibling, arg23Space, comma23] }; $ternaryFunction => { c.class _ MathDB.LookupCompoundClass[$quaternaryFunction]; sibling.tag _ $arg4; newExprs _ LIST[sibling, arg34Space, comma34] }; ENDCASE => ERROR; -- can't extend quaternaryFunction IF c.subExprs = NIL THEN c.subExprs _ newExprs ELSE { p: LIST OF BoxTree _ c.subExprs; WHILE p.rest#NIL DO p _ p.rest ENDLOOP; p.rest _ newExprs; }; }; ENDCASE => ERROR noSelection; ENDCASE => ERROR noSelection; RETURN[sibling]; }; SelectableSibling: PUBLIC PROC[expr: BoxTree] RETURNS[BoxTree] ~ { afterExpr: LIST OF Argument _ NIL; -- sibling after expr in parent's subexpr list siblingExpr: BoxTree _ NIL; IF (expr = NIL) OR (expr.parent = NIL) THEN ERROR noSelection; WITH expr.parent.expression SELECT FROM a: AtomDisplayObj => ERROR noSelection; c: CompoundDisplayObj => { FOR l: LIST OF Argument _ c.class.arguments, l.rest UNTIL l = NIL DO IF l.first.name = expr.tag THEN {afterExpr _ l.rest; EXIT}; ENDLOOP; FOR l: LIST OF Argument _ afterExpr, l.rest UNTIL l = NIL DO siblingExpr _ GetBoxTree[l.first.name, c.subExprs]; IF Selectable[siblingExpr] THEN RETURN[siblingExpr]; -- return selected expression ENDLOOP; SELECT c.class.name FROM $nullaryFunction, $unaryFunction, $binaryFunction, $ternaryFunction => RETURN[ExtendFunctionSelectSibling[expr]]; -- hack ENDCASE; IF c.class.name=$paren THEN RETURN[SelectableSibling[expr.parent] ]; FOR l: LIST OF Argument _ c.class.arguments, l.rest UNTIL l = NIL DO IF l.first.name = expr.tag THEN EXIT; -- already looked thru entire list siblingExpr _ GetBoxTree[l.first.name, c.subExprs]; IF Selectable[siblingExpr] THEN RETURN[siblingExpr]; -- return selected expression ENDLOOP; ERROR noSelection; }; m: MatrixDisplayObj => { ERROR noSelection; }; ENDCASE => ERROR; }; Replace: PUBLIC PROC[expr, old, new: BoxTree] RETURNS[BoxTree] ~ { RETURN[ReplaceN[expr, LIST[[old: old, new: new]]]]; }; ReplaceN: PUBLIC PROC[expr: BoxTree, replacements: LIST OF ReplacePair] RETURNS[BoxTree] ~ { FOR l: LIST OF ReplacePair _ replacements, l.rest UNTIL l = NIL DO IF expr = l.first.old THEN { parent: BoxTree _ l.first.old.parent; new: BoxTree _ l.first.new; IF FALSE THEN { parenArgTag: ATOM = $a; argBoxTree: BoxTree; parenExpr: BoxTree _ BoxTreeFromExpr[MathExpr.MakeCompoundExpr[$paren, LIST[[parenArgTag, MathConstructors.MakePlaceHolder[] ]] ] ]; FOR l: LIST OF BoxTree _ GetSubExprs[parenExpr], l.rest UNTIL l = NIL DO IF Tag[l.first] = parenArgTag THEN { argBoxTree _ l.first; EXIT }; ENDLOOP; parenExpr _ Replace[parenExpr, argBoxTree, new]; parenExpr.tag _ l.first.old.tag; -- replacement has same tag (e.g. $integrand, etc.) parenExpr.parent _ parent; -- replacement has same parent as before RETURN[parenExpr] } ELSE { new.tag _ l.first.old.tag; -- replacement has same tag (e.g. $integrand, etc.) new.parent _ parent; -- replacement has same parent as before RETURN[new] }; }; ENDLOOP; WITH expr.expression SELECT FROM a: AtomDisplayObj => { RETURN[Copy[expr]]; }; c: CompoundDisplayObj => { exprCopy: BoxTree; -- return value copiedSubExprs: LIST OF BoxTree _ NIL; -- cons up new list of copies FOR l: LIST OF BoxTree _ c.subExprs, l.rest UNTIL l = NIL DO copiedSubExprs _ CONS[ReplaceN[l.first, replacements], copiedSubExprs]; ENDLOOP; exprCopy _ MakeCompoundBoxTree[expr.tag, c.class, copiedSubExprs, expr.relativeBox, expr.absoluteBox]; FOR l: LIST OF BoxTree _ copiedSubExprs, l.rest UNTIL l = NIL DO l.first.parent _ exprCopy; -- set parent pointers appropriately ENDLOOP; RETURN[exprCopy]; }; m: MatrixDisplayObj => { exprCopy: BoxTree; -- return value copiedElts, copiedEltsPtr: LIST OF BoxTree _ NIL; -- cons up a new list of copied elements FOR l: LIST OF BoxTree _ m.elements, l.rest UNTIL l = NIL DO IF copiedElts = NIL THEN copiedElts _ copiedEltsPtr _ LIST[ReplaceN[l.first, replacements] ] ELSE copiedEltsPtr _ copiedEltsPtr.rest _ LIST[ReplaceN[l.first, replacements] ]; ENDLOOP; exprCopy _ MakeMatrixBoxTree[expr.tag, m.class, m.nRows, m.nCols, copiedElts, expr.relativeBox, expr.absoluteBox, Copy[m.openSym], Copy[m.closeSym], Copy[m.space]]; FOR l: LIST OF BoxTree _ copiedElts, l.rest UNTIL l = NIL DO l.first.parent _ exprCopy; -- set parent pointers appropriately ENDLOOP; RETURN[exprCopy]; }; ENDCASE => ERROR; }; unable: PUBLIC ERROR[reason: ATOM] = CODE; noSelection: PUBLIC ERROR = CODE; exprNotFound: PUBLIC ERROR = CODE; END. 4î MathBoxTreesImpl.mesa Copyright Ó 1989 by Xerox Corporation. All rights reserved. Arnon, September 7, 1989 10:21:56 am PDT Type Abbreviations from Imported Interfaces BOX: TYPE ~ MathBox.BOX; EXPR: TYPE ~ MathExpr.EXPR; AtomEXPR: TYPE ~ MathExpr.AtomEXPR; CompoundEXPR: TYPE ~ MathExpr.CompoundEXPR; MatrixEXPR: TYPE ~ MathExpr.MatrixEXPR; Style: TYPE ~ MathTypes.Style; Argument: TYPE ~ MathExpr.Argument; Symbol: TYPE ~ MathExpr.Symbol; AtomClass: TYPE ~ MathExpr.AtomClass; AtomFlavor: TYPE ~ MathExpr.AtomFlavor; CompoundClass: TYPE ~ MathExpr.CompoundClass; MatrixClass: TYPE ~ MathExpr.MatrixClass; Size: TYPE ~ MathRules.Size; Selection: TYPE ~ MathBoxTree.Selection; ReplacePair: TYPE ~ MathBoxTree.ReplacePair; Private Type Rep No subtree is selectable (only Objects are selectable) Leaf Box iff children = NIL Root is a leaf Box iff children = NIL This Object (self.object) selectable iff visible Nothing inside self.root, e.g. no subBoxTree or subObject, is selectable self.root and self.children created by the EltDisplay proc of (the domain) self.object.class, with either no "unpacking" possible (e.g. ground domain), or a passed-in environment specifying "no unpacking". Object visible iff root # NIL assert[ elementOf[op, domain.methods] ] assert[ resolvableTo[args, opMethod.dynamicSignature] ] assert[ Object[self] = opMethod[args] ] selecting one of these BoxTreeDatas implies that (logically) all are selected (user feedback should so indicate?). Editing will take place on just the one actually selected, however. All of these BoxTreeDatas should belong to domain "OperatorGlyphs". They should only be replacable by an element of the same domain. If that is done, this determines a new candidate self.op. We then proceed top-down to check the assertions above, and reformat self. I.e. we check elementOf[op, domain.methods It is not assumed that the value of (the data of) each arg here is the same BoxTreeData one would get by application of the EltDisplay proc of the domain of the Object[] of that arg (with the same environment, e.g. unpacking), although at some point in the bottom-up creation of self that BoxTreeData was created; it may be modified by "postprocessing" that we as part of the formatting of this interiorObject The basic editing cycle is: select some (selectable, i.e. Object) BoxTree, possibly change its domain, possibly change its opDisplay (and so its op), possibly replace it with a new Object, possibly copy it somewhere, possibly do nothing; update Object of which selection Object is a part; identify new selection. Replacement with a new Object overrides other changes. The update process first updates the selection Object. If this is a newly inserted Object, nothing to do. If domain and/or op changed, then verify the assertions: assert[ elementOf[op, domain.methods] ] assert[ resolvableTo[args, opMethod.dynamicSignature] ] assert[ Object[self] = opMethod[args] ] and thereby obtain a new BoxTree to replace the originally selected BoxTree. If failure occurs in here, then user is asked what to do - he can either undo the new domain, or delete self. If the latter, then before the selection can possibly be released, some specification of a new Object to replace self must be made. If no such Object is provided, then either a default Object of the new domain, or an Exprs placeholder, is used. We now have a new BoxTree, i.e. a newSelf. We format it. If its domain is the same as old self, and if its bounding box fits inside that of old self, then we paint it (and nothing else) and done. If domain is the same as old self, but newSelf's bounding box doesn't fit inside that of old self (and we decide not to simply rescale it to fit), then we resume an EltDisplay[Object[root]] computation at this point (i.e. so far we can use the existing formattings of siblings, but we invoke a fresh format command ont the parent). If the domain of newSelf is different from old domain, we startup ("continue") an Object[root] computation from this point. First, we check whether newSelf is compatible with the appropriate element of parent.opMethod.dynamicSignature. If so, we (possibly) convert newSelf, format it, and proceed as above, depending on whether its bounding box fits inside the old one. If newSelf's domain is not compatible with the appropriate element of parent.opMethod.dynamicSignature, then we resume an Object[root] computation at this point. E.g. we may lookup parent.op in some other domain, or try to resolve the domains of newSelf and its siblings, etc. If the Object[root] computation succeeds, then the (possibly necessary) reformatting of BoxTrees will occur all the way up to the root, and when we get there and finish, we repaint the entire root Object. A new selection is set (probably at newSelf). If the Object[root] computation fails at some point, we report the failure and do an automatic undo of this editing cycle. Note: EltDisplay methods are assumed to produce a formatted result. I.e. the old Meddle Format step is part of any EltDisplay method. Display Expression Constructors effects: Constructs and returns a new atomic display expression. effects: Constructs and returns a new compound display expression. effects: Constructs and returns a new matrix display expression. Parse/UnParse Routines effects: Returns a ROPE in selected format recursively copy subexpressions Hack for complexes with negative imaginary part. December 11, 1986 11:24:18 am PST convert boxes into a SEQUENCE (ugh!) matrix type instantiate matrix "a" (isn't CLU's array[]$create() much nicer, really?) add a comma if not last col in row add a comma if not last row in matrix add a comma if not last col in vector add a comma if not last row in vector effects: Constructs and returns a new display expression from expr. All Display formatting information is set to default values. local declarations should really combine passed in style with local atomic style recursively generate displayexprs for all subexpressions (arguments & symbols) generate all argument subexpressions generate all symbol subexpressions construct return value now so can set "parent" component of subexpressions set parents of display subexpressions convert elements from expr to displayexpr FOR l: LIST OF MathExpr.TaggedMathExpr _ elts, l.rest UNTIL l = NIL DO tempBoxTree _ BoxTreeFromExpr[l.first.expression]; tempBoxTree.tag _ l.first.id; -- set name tag for display expr displayElts _ CONS[tempBoxTree, displayElts]; ENDLOOP; convert elements from expr to displayexpr -- changed 3/4/87 construct return value now so can set "parent" component of subexpressions effects: Constructs and returns a new EXPR from expr. 2/87 - amounts to just stripping away the formatting info from the BoxTree, i.e. stripping relativeBox, absoluteBox, and parent pointers. local declarations recursively build a list of tagged arg exprs local declarations FOR l: LIST OF BoxTree _ m.elements, l.rest UNTIL l = NIL DO elts _ CONS[[l.first.tag, ExprFromBoxTree[l.first]], elts]; ENDLOOP; effects: Returns a new, distinct copy of expr. local declarations recursively copy subexpressions construct return value now so we can set parent pointers for subexprs local declarations recursively copy elements FOR l: LIST OF BoxTree _ m.elements, l.rest UNTIL l = NIL DO copiedElts _ CONS[Copy[l.first], copiedElts]; ENDLOOP; recursively copy elements -- changed 3/4/87 construct return value now so we can set parent pointers for subexprs Selectors effects: Returns tag associated with expr. effects: Returns class of expr. effects: Returns subexpressions for expr. Returns NIL if no subexpressions exist effects: Returns absoluteBox for expr. Format and Paint Were using axis as baseline, so must shift up here innerBox _ MathBox.ChangeExtents[innerBox, extents]; modifies: expr effects: expr is updated into a "paintable" form (dsa - perhaps in the sense that all subExprs have been given correct relative bounding boxes) Returns a bounding box for expr. SIGNALS unable[reason: ATOM] if InnerFormatting can not be completed. local declarations recursively format all subexpressions (arguments & symbols) format all argument subexpressions format all symbol subexpressions compute corrected boxes using sizing constraints compose layout for boxes using alignment constraints set format class for mybox set relative boxes of display subexpressions local declarations recursively format all matrix elements FOR l: LIST OF BoxTree _ m.elements, l.rest UNTIL l = NIL DO tempBox _ InnerFormat[l.first, MathRules.ComputeSize[base: size, adjustment: script]]; elementBoxes _ CONS[MathBox.MakeBox[l.first.tag, NIL, tempBox.FormatClass[], tempBox.Type[], tempBox.Extents[], tempBox.Offset[], tempBox.SubscriptHint[], tempBox.SuperscriptHint[]], elementBoxes]; ENDLOOP; recursively format all matrix elements format openSym, closeSym, and space compose layout for matrix by using alignment constraints set format class for mybox set relative boxes of matrix elements requires: absBox is an absolute BOX modifies: expr effects: Displays expr in context, bounded by absBox. Updates absolute bounding boxes (viewer coords). SIGNALS unable[reason: ATOM] if Painting cannot be completed. local declarations update absolute coordinates paint all subexpressions paint arguments paint symbols paint all matrix elements paint open & close symbols if selected, highlight region appropriately, using a border and optional fill set proper color depending on selection highlight whole region put thin border around filled region highlight border List Operations effects: Returns the BoxTree in exprs associated with tag. SIGNALS exprNotFound if no association exists. cdr down list looking for tag not found, so signal error Selection Operations effects: Returns the subexpression associated with coordinates [x, y] in expression expr. SIGNALS noSelection if no association exists. see if selection can be further narrowed if above loop does not return, then can't narrow selection any more see if selection can be further narrowed if above loop does not return, then can't narrow selection any more effects: Returns TRUE iff expr is selectable. effects: Returns the parent (enclosing expression) of expr. SIGNALS noParent if no selectable parent exists. complain if no expr or no parent effects: Returns a child expressions (subexpression) for expr. SIGNALS noSelection if no selectable child exists. local declarations keep looking for selectable child if there are no selectable children, signal noSelection ** FOO FIX THIS ** Hack 7/10/87: Assuming expr is the last arg of a function, change its parent to be larger function class and select the new last arg. effects: Returns the "next" sibling expression from expr. SIGNALS noSelection if no selectable sibling exists. local declarations "cycle" thru parent's children and return "next" selectable sibling in list find starting point to begin looking for next selectable sibling expr look for next selectable sibling in exprs after expr in list (i.e. in afterExpr) *** hack 7/16/87 to handle extra inserted parentheses *** end hack if no selectable sibling exists after expr, then wraparound list to before expr if we haven't RETURNed yet, then there are no selectable siblings ** FOO FIX THIS ** effects: Returns a copy of display expression expr with new substituted for old. caveats: Return value must be reformatted before Paint[]'ing. Note that new is used (not a copy). Hence if expr = old, then new returned, i.e. a "copy" returned iff old is a proper subexpression of expr. effects: Returns a copy of display expression expr with new substituted for old for each pair in replacements [old, new]. If expr = old_i, then new_i i.e. a "copy" returned iff each old_i is a proper subexpression of expr. Note that replacements can "interfere" with each other; outcome depends on their ordering. caveats: Return value must be reformatted before Paint[]'ing. Note that "old" components of replace pairs must be the actual pointers to the exprs we wish to replace, i.e. we only replace that actual expr, and not any other expr which is equivalent but physically distinct from it. Note that the "new" components of replace pairs are used (not copies). Hence anyone who holds the pointer to a "new" component can e.g. later ask to have it replaced by something. Note that recursion terminates as soon as we find a match, i.e. if expr = one of the olds (i.e. expr is one of the things to be replaced), we won't examine any subexprs of expr for possible additional matches Parenthesize replacement if its op has lower precedence than parent IF parent #NIL AND MathDB.CompareOps[Class[new], Class[parent] ] = less THEN { -- 7/10/87 -- turn off op precedence for functional notation parsing Create skeleton for parenthesized expression (i.e. dummy arg) Grab onto dummy arg Stuff actual pointer to new into parenExpr Update pointers new.tag _ $a; new.parent _ parenExpr; local declarations recursively copy subexpressions construct return value now so we can set parent pointers for subexprs local declarations recursively copy elements FOR l: LIST OF BoxTree _ m.elements, l.rest UNTIL l = NIL DO copiedElts _ CONS[ReplaceN[l.first, replacements], copiedElts]; ENDLOOP; recursively copy elements - changed 3/4/87 construct return value now so we can set parent pointers for subexprs Signals & Errors Ê#e•NewlineDelimiter ™šœ™Icode™Kšœœ™-Kšœ™K™—Kšœ;™;š œœœ(œœ˜FKšœ2˜2KšœŸ ˜>Kš œœœ$œœ0œ˜•Kšœ˜—K˜K˜)K˜+˜%K˜—KšœJ™JK˜K˜š œœœœœ˜=KšœŸ ˜)Kšœ˜—K˜Kšœ ˜K˜—Kšœœ˜—K™K˜K˜—š žœœœœœ˜=Kšœ6™6Kšœ‰™‰K™šœœ˜ K˜˜Kšœ1˜7K˜—K˜˜Kšœ™Kšœœœœ˜,K˜Kšœ,™,š œœœœœ˜˜>K˜Kšœ,™,š œœœœœ˜˜>K˜Kšœ%™%š œœœœœ˜šœœ˜šž œœ˜Kšœœ˜Kšœ'™'K˜ šœœ˜Kšœ˜Kšœ™K˜­K™$K˜Kšœ(˜(K˜—K™K˜(Kšœëœ˜ôK˜—K˜Kšœ)˜)K˜—Kšœ˜—K˜K˜——šž™šž œœœœ œœ œ ˜OKšœ:™:Kšœ7™7K˜Kšœ™š œœœœœ˜7Kšœœœ ˜*Kšœ˜—K˜Kšœ™Kšœ˜K˜——šž™š žœœœœœ ˜NKšœ\™\K™7K™Kšœœœœ ˜%Kšœ)œœ ˜BK˜šœœ˜ K˜šœ˜Kš œœœœœ ˜=K˜K˜—˜Kšœ(™(š œœœœœ˜<šœ+˜1Kšœ2œ˜C—Kšœ˜—K˜KšœC™CKš œœœœœ˜?K˜—K˜˜Kšœ(™(š œœœœœ˜<šœ+˜1Kšœ2œ˜C—Kšœ˜—K˜KšœC™CKš œœœœœ˜?K˜—K˜Kšœœ˜—K˜˜K˜——š ž œœœœœ˜8Kšœ-™-K™Kš œœœœœ˜!šœœ˜ ˜Kšœœœœœœœ˜@K˜—Kšœœœ˜&Kšœœœ˜$Kšœœ˜—K˜K™—šžœœœœ ˜AKšœ;™;Kšœ9™9K™Kšœ ™ Kš œ œœœœœ˜?Kšœ˜K˜K˜—šžœœœœ ˜@Kšœ?™?Kšœ<™™>K™Kšœ™Kšœ œœ œŸ.˜RKšœœ˜K˜Kš œ œœœœœ ˜>K˜KšœK™Kšœœ˜'Kšœœ ˜'˜Kšœ) œ™Eš œœœ&œœ˜DKšœœœ˜;Kšœ˜—Kšœ* œ!™Pš œœœœœ˜