DIRECTORY Atom USING [GetPropFromList], Basics USING [BYTE], BasicTime USING [GMT], Commander USING [CommandProc, Register], CommandTool USING [ParseToList], Convert USING [CardFromRope, Error, RopeFromTime], FileNames USING [CurrentWorkingDirectory], FS USING [EnumerateForNames, NameProc, Error, StreamOpen], Graph USING [CaretIndex, CaretSpec, ColorIndex, Entity, EntityGroup, EntityGroupList, EntityGroupRec, EntityHashSize, EntityList, EntityRec, FontIndex, GRAPH, GraphHandle, GraphProc, HashIndex, LastEntityColor, NestedEntities, NestedEntitiesList, NestedEntitiesRec, NumberOfColors, NumberOfFonts, ROPE, SegmentDataList, TargetSpec, Text, TextRec, Texts, ValueList, Viewer, XY], GraphFileKeys, GraphOps USING [AddCrossSection, AddText, EnlistEntity, Lock, NewGraph, Unlock], GraphPrivate USING [BYTE, CharPair, FourChars, GraphStreamProc, MakeTable, PaintAll, ReadAsciiFile, RopeList, ShowChart, STREAM], GraphUtil USING [AppendEGL, HandleNotNil, InitSegAll, InitSegEnd, NewEntityId, NewGroupId, NewTextId, ReverseEntityList, ReverseTexts, ReverseValueList, UseMyColors, UseMyFonts, VanillaHandle], IO USING [Close, EndOf, EndOfStream, Error, GetChar, GetIndex, int, PeekChar, PutFR, rope], List USING [DReverse, Kill], RefText USING [New, ObtainScratch, ReleaseScratch], Rope USING [Equal, Fetch, Find, FromRefText, IsEmpty, Length, Substr], ViewerOps USING [DestroyViewer]; GraphRead: CEDAR PROGRAM IMPORTS Atom, Commander, CommandTool, Convert, FileNames, FS, IO, List, GraphOps, GraphPrivate, GraphUtil, RefText, Rope, ViewerOps EXPORTS GraphPrivate = { OPEN Graph, GraphFileKeys, GraphOps, GraphPrivate, GraphUtil; ReadBeyondEOF: PUBLIC SIGNAL[message: ROPE _ NIL] = CODE; SyntaxError: PUBLIC SIGNAL[message: ROPE _ NIL] = CODE; DataError: PUBLIC SIGNAL[message: ROPE _ NIL] = CODE; FileFormat: TYPE = {old, binary, ascii, illegal}; HandleFromFile: PUBLIC PROC[file: ROPE _ NIL] RETURNS [handle: GraphHandle _ NIL, msg: ROPE _ NIL] = { handle _ VanillaHandle[]; Lock[handle]; handle.userEditAllowed _ FALSE; msg _ GetGraph[handle, file]; handle.userEditAllowed _ TRUE; Unlock[handle]; }; -- HandleFromFile GetGraph: PUBLIC PROC [handle: GraphHandle, file: ROPE] RETURNS [msg: ROPE _ NIL] = { IF file.IsEmpty[] THEN RETURN["No file specified."]; IF HandleNotNil[handle] THEN { s: STREAM _ NIL; s _ FS. StreamOpen[file ! FS.Error => {msg _ error.explanation; CONTINUE} ]; IF msg = NIL THEN { ENABLE { SyntaxError => { msg _ IO.PutFR["Syntax errorat [%g], %g.", IO.int[s.GetIndex[]], IO.rope[message]]; CONTINUE; }; ReadBeyondEOF => { msg _ IO.PutFR["End of file at [%g], reading %g.", IO.int[s.GetIndex[]], IO.rope[message]]; CONTINUE; }; DataError => { msg _ IO.PutFR["Error at [%g], %g.", IO.int[s.GetIndex[]], IO.rope[message]]; CONTINUE; }; IO.EndOfStream => { msg _ "End of file reached"; CONTINUE; }; IO.Error => { msg _ IO.PutFR["%g at [%g].", IO.rope[SELECT ec FROM SyntaxError => "Syntax error", Overflow => "Overflow in input conversion" ENDCASE => IO.PutFR["IO Error # %g", IO.int[LOOPHOLE[ec, CARDINAL]]]], IO.int[s.GetIndex[]] ]; CONTINUE; }; ABORTED => { msg _ "Graph aborted. ... "; CONTINUE; }; }; IF handle.controller # NIL THEN IF handle.controller.table # NIL THEN { ViewerOps.DestroyViewer[handle.controller.table]; handle.controller.table _ NIL; }; handle.userEditAllowed _ FALSE; msg _ SELECT CheckFormat[s] FROM old => ReadPlotFile[handle, s], binary => ReadGraphFile[handle, s], ascii => ReadAsciiFile[handle, s], ENDCASE => "Illegal graph file."; }; handle.userEditAllowed _ TRUE; IF handle.controller # NIL THEN IF handle.controller.table = NIL THEN MakeTable[handle, IF handle.controller.tableVisible THEN 0 ELSE 1000, 0]; IF s # NIL THEN s.Close[]; }; }; -- GetGraph ReadPlotFile: PROC[handle: GraphHandle, s: STREAM] RETURNS [msg: ROPE _ NIL] = { OPEN handle; groupId, valuesLength, nCurves: INT; title: ROPE _ GetRope[s]; time: ROPE _ Convert.RopeFromTime[GetTime[s]]; file: ROPE _ NARROW[Atom.GetPropFromList[s.propList, $Name]]; [handle, groupId] _ NewGraph[ fileName: file, groupName: NIL, comment: file, xName: "X", autoBounds: FALSE, bounds: [GetReal[s], GetReal[s], GetReal[s], GetReal[s]], oldGraph: handle ]; [] _ GraphOps.AddText[handle: handle, rope: title, place: [0.5, 1.1], fontIndex: 2, justifX: center, justifY: bottom]; [] _ GraphOps.AddText[handle: handle, rope: time, place: [1.0, -0.35], fontIndex: 1, justifX: right, justifY: bottom]; [] _ GraphOps.AddText[handle: handle, rope: file, place: [0, -0.35], fontIndex: 1, justifX: left, justifY: bottom]; nCurves _ GetCardinal[s]; FOR i: INT IN [1..nCurves] DO [] _ EnlistEntity[handle: handle, groupId: groupId, name: GetRope[s], colorIndex: ((i-1) MOD LastEntityColor) + 1]; ENDLOOP; valuesLength _ GetCardinal[s]; FOR i: INT IN [1..valuesLength] DO x: REAL _ GetReal[s]; yvalues: ValueList _ NIL; FOR j: INT IN [1..nCurves] DO yvalues _ CONS[GetReal[s], yvalues]; ENDLOOP; AddCrossSection[handle, x, yvalues, groupId]; ENDLOOP; }; -- ReadPlotFile ReadGraphFile: PROC [handle: GraphHandle, s: STREAM] RETURNS [msg: ROPE _ NIL] = { OPEN handle; graph.fileName _ NARROW[Atom.GetPropFromList[s.propList, $Name]]; WHILE NOT s.EndOf[] DO SELECT GetByte[s] FROM NameKey => [] _ GetRope[s]; -- graph.fileName is already initialized above. TextsKey => { allTexts _ GetTexts[s, handle]; graph.texts _ ReverseTexts[allTexts, FALSE]; graph.texts _ ReverseTexts[graph.texts, TRUE]; }; CaretsKey => GetCarets[s, graph]; ShowSlopeKey => graph.showSlope _ GetBool[s]; TargetsKey => GetTargets[s, graph]; GridsKey => GetGrids[s, graph]; DivisionsKey => GetDivisions[s, graph]; BoundsKey => GetBounds[s, graph]; ColorsKey => GetColors[s, graph]; FontsKey => GetFonts[s, graph]; EntityGroupListKey => GetEntityGroupList[s, handle]; EndOfRecordKey => EXIT; -- later we can read more than one graphs at a time. ENDCASE => SIGNAL SyntaxError["Graph"]; ENDLOOP; FOR egltemp: EntityGroupList _ entityGroupList, egltemp.rest UNTIL egltemp = NIL DO InitSegEnd[egltemp.first.x]; ENDLOOP; InitSomeCurves[handle]; IF handle.chart.viewer = NIL THEN ShowChart[handle] ELSE { UseMyColors[handle]; UseMyFonts[handle]; PaintAll[handle]; }; }; -- ReadGraphFile GetTexts: PROC [s: STREAM, handle: GraphHandle] RETURNS [texts: Texts _ NIL] = { DO SELECT GetByte[s] FROM EndOfListKey => EXIT; TextKey => texts _ CONS[GetText[s, handle], texts]; -- 1 ENDCASE => SIGNAL SyntaxError["Texts"]; ENDLOOP; texts _ ReverseTexts[texts, TRUE]; }; -- GetTexts GetText: PROC [s: STREAM, handle: GraphHandle] RETURNS [text: Text _ NIL] = { text _ NEW[TextRec _ []]; DO SELECT GetByte[s] FROM NameKey => text.text _ GetRope[s]; -- 1 PlaceKey => text.place _ [GetReal[s], GetReal[s]]; -- 2 FontIndexKey => text.fontIndex _ GetByte[s]; -- 3 ColorIndexKey => text.colorIndex _ GetByte[s]; -- 4 RotationKey => text.rotation _ GetReal[s]; -- 5 JustificationsKey => { -- 6 WHILE NOT s.EndOf[] DO byte: BYTE; SELECT GetByte[s] FROM XKey => { -- 1 byte _ GetByte[s]; IF byte IN [LeftKey..RightKey] THEN text.justifX _ SELECT byte FROM -- 1, 2, 3. LeftKey => left, CenterKey => center, ENDCASE => right ELSE SIGNAL SyntaxError["X Justification"]; }; YKey => { -- 2 byte _ GetByte[s]; IF byte IN [TopKey..BottomKey] THEN text.justifY _ SELECT byte FROM -- 1, 2, 3. TopKey => top, CenterKey => center, ENDCASE => bottom ELSE SIGNAL SyntaxError["Y Justification"]; }; EndOfRecordKey => EXIT; ENDCASE => SIGNAL SyntaxError["Justifications"]; ENDLOOP; }; TextIdKey => {text.id _ NewTextId[handle, GetInt[s]]}; -- 7 EndOfRecordKey => EXIT; ENDCASE => SIGNAL SyntaxError["Text"]; ENDLOOP; }; -- GetText GetCarets: GraphStreamProc = { byte: BYTE; WHILE NOT s.EndOf[] DO SELECT (byte _ GetByte[s]) FROM EndOfRecordKey => EXIT; IN [PrimaryKey..TextCaretKey] => { -- 1, 2, 3. index: CaretIndex _ SELECT byte FROM PrimaryKey => primary, SecondaryKey => secondary, ENDCASE => text; GetCaret[s, graph.caret[index]]; }; ENDCASE => SIGNAL SyntaxError["Carets"]; ENDLOOP; }; -- GetCarets GetCaret: PROC [s: STREAM, spec: CaretSpec] = { WHILE NOT s.EndOf[] DO SELECT GetByte[s] FROM EndOfRecordKey => EXIT; PlaceKey => spec.place _ [GetReal[s], GetReal[s]]; -- 2 OnKey => spec.on _ GetBool[s]; -- 12 ENDCASE => SIGNAL SyntaxError["Caret"]; ENDLOOP; }; -- GetCaret GetTargets: GraphStreamProc = { byte: BYTE; WHILE NOT s.EndOf[] DO SELECT (byte _ GetByte[s]) FROM EndOfRecordKey => EXIT; IN [XKey..YKey] => { -- 1, 2. xy: XY _ IF byte = XKey THEN x ELSE y; GetTarget[s, graph.target[xy]]; }; ENDCASE => SIGNAL SyntaxError["Targets"]; ENDLOOP; }; -- GetTargets GetTarget: PROC [s: STREAM, spec: TargetSpec] = { WHILE NOT s.EndOf[] DO SELECT GetByte[s] FROM EndOfRecordKey => EXIT; ValueKey => spec.value _ GetReal[s]; -- 1 WidthKey => spec.width _ GetReal[s]; -- 2 ColorIndexKey => spec.colorIndex _ GetByte[s]; -- 4, need proctection. OnKey => spec.on _ GetBool[s]; -- 12 ENDCASE => SIGNAL SyntaxError["Target"]; ENDLOOP; }; -- GetTarget GetGrids: GraphStreamProc = { byte: BYTE; WHILE NOT s.EndOf[] DO SELECT (byte _ GetByte[s]) FROM EndOfRecordKey => EXIT; IN [XKey..YKey] => { -- 1, 2 xy: XY _ IF byte = XKey THEN x ELSE y; WHILE NOT s.EndOf[] DO SELECT GetByte[s] FROM EndOfRecordKey => EXIT; OnKey => graph.grids[xy] _ GetBool[s]; -- 12 ENDCASE => SIGNAL SyntaxError["Grid"]; ENDLOOP; }; ENDCASE => SIGNAL SyntaxError["Grids"]; ENDLOOP; }; -- GetGrids GetDivisions: GraphStreamProc = { byte: BYTE; WHILE NOT s.EndOf[] DO SELECT (byte _ GetByte[s]) FROM EndOfRecordKey => EXIT; IN [XKey..YKey] => { -- 1, 2 xy: XY _ IF byte = XKey THEN x ELSE y; WHILE NOT s.EndOf[] DO SELECT GetByte[s] FROM EndOfRecordKey => EXIT; AutoKey => graph.auto[divisions] _ GetBool[s]; -- 9 DivisionsKey => graph.division[xy] _ GetByte[s]; -- 7 ENDCASE => SIGNAL SyntaxError["Division"]; ENDLOOP; }; ENDCASE => SIGNAL SyntaxError["Divisions"]; ENDLOOP; }; -- GetDivisions GetBounds: GraphStreamProc = { byte: BYTE; WHILE NOT s.EndOf[] DO SELECT (byte _ GetByte[s]) FROM EndOfRecordKey => EXIT; IN [XKey..YKey] => { -- 1, 2 xy: XY _ IF byte = XKey THEN x ELSE y; WHILE NOT s.EndOf[] DO SELECT GetByte[s] FROM EndOfRecordKey => EXIT; AutoKey => graph.auto[bounds] _ GetBool[s]; -- 9 MaxKey => IF xy = x THEN graph.bounds.xmax _ GetReal[s] -- 1 ELSE graph.bounds.ymax _ GetReal[s]; MinKey => IF xy = x THEN graph.bounds.xmin _ GetReal[s] -- 2 ELSE graph.bounds.ymin _ GetReal[s]; ENDCASE => SIGNAL SyntaxError["Bound"]; ENDLOOP; }; ENDCASE => SIGNAL SyntaxError["Bounds"]; ENDLOOP; }; -- GetBounds GetColors: GraphStreamProc = { byte: BYTE; WHILE NOT s.EndOf[] DO SELECT (byte _ GetByte[s]) FROM EndOfRecordKey => EXIT; IN [1..NumberOfColors] => { index: ColorIndex _ byte - 1; GetColor[s, graph, index]; }; ENDCASE => SIGNAL SyntaxError["Colors"]; ENDLOOP; }; -- GetColors GetColor: PROC [s: STREAM, graph: GRAPH, index: ColorIndex] = { WHILE NOT s.EndOf[] DO SELECT GetByte[s] FROM EndOfRecordKey => EXIT; RKey => graph.color[index].R _ GetReal[s]; -- 1 GKey => graph.color[index].G _ GetReal[s]; -- 2 BKey => graph.color[index].B _ GetReal[s]; -- 3 ENDCASE => SIGNAL SyntaxError["Color"]; ENDLOOP; }; -- GetColor GetFonts: GraphStreamProc = { byte: BYTE; WHILE NOT s.EndOf[] DO SELECT (byte _ GetByte[s]) FROM EndOfRecordKey => EXIT; IN [1..NumberOfFonts] => { index: FontIndex _ byte - 1; GetFont[s, graph, index]; }; ENDCASE => SIGNAL SyntaxError["Fonts"]; ENDLOOP; }; -- GetFonts GetFont: PROC [s: STREAM, graph: GRAPH, index: FontIndex] = { WHILE NOT s.EndOf[] DO SELECT GetByte[s] FROM EndOfRecordKey => EXIT; NameKey => graph.font[index].family _ GetRope[s]; -- 1 BoldKey => graph.font[index].bold _ GetBool[s]; -- 2 ItalicKey => graph.font[index].italic _ GetBool[s]; -- 3 VFontSizeKey => graph.font[index].vFontSize _ GetInt[s]; -- 4 PFontScaleKey => graph.font[index].pFontScale _ GetReal[s]; -- 5 ENDCASE => SIGNAL SyntaxError["Font"]; ENDLOOP; }; -- GetFonts GetEntityGroupList: PROC [s: STREAM, handle: GraphHandle] = { OPEN handle; egl, t1, t2: EntityGroupList _ NIL; DO SELECT GetByte[s] FROM EndOfListKey => EXIT; EntityGroupKey => t1 _ CONS[GetEntityGroup[s, handle], t1]; -- 4 ENDCASE => SIGNAL SyntaxError["EntityGroupList"]; ENDLOOP; FOR t2 _ t1, t2.rest UNTIL t2 = NIL DO egl _ CONS[t2.first, egl]; ENDLOOP; entityGroupList _ AppendEGL[entityGroupList, egl]; TRUSTED {List.Kill[LOOPHOLE[t1]]}; }; -- GetEntityGroupList GetEntityGroup: PROC [s: STREAM, handle: GraphHandle] RETURNS [eg: EntityGroup] = { eg _ NEW[EntityGroupRec _ []]; DO SELECT GetByte[s] FROM EntityKey => eg.x _ GetEntity[s, handle, eg, NIL]; -- 2 NestedEntitiesKey => eg.ys _ GetNestedEntities[s, handle, eg, NIL]; -- 5 GroupIdKey => eg.id _ NewGroupId[handle, GetInt[s]]; -- 6 LengthKey => eg.length _ GetInt[s]; -- 7 CrossSectionsKey => GetCrossSections[s, eg]; EndOfRecordKey => EXIT; ENDCASE => SIGNAL SyntaxError["EntityGroup"]; ENDLOOP; }; -- GetEntityGroup GetEntityList: PROC [s: STREAM, handle: GraphHandle, group: EntityGroup, father: NestedEntities] RETURNS [el: EntityList _ NIL] = { -- without getting the values, for now. DO SELECT GetByte[s] FROM EndOfListKey => EXIT; EntityKey => el _ CONS[GetEntity[s, handle, group, father], el]; -- 2 ENDCASE => SyntaxError["EntityList"]; ENDLOOP; el _ ReverseEntityList[el, TRUE]; -- resume the order. }; -- GetEntityList GetEntity: PROC [s: STREAM, handle: GraphHandle, eg: EntityGroup, father: NestedEntities _ NIL] RETURNS [entity: Entity _ NIL] = { hashIndex: [0..EntityHashSize); entity _ NEW[EntityRec _ [group: eg, parent: father]]; DO SELECT GetByte[s] FROM NameKey => entity.name _ GetRope[s]; -- 1 CommentKey => entity.comment _ GetRope[s]; -- 11 ColorIndexKey => entity.colorIndex _ GetByte[s]; -- 4 MarkKey => { -- 3 byte: BYTE _ GetByte[s]; IF byte IN [NoneKey..PercentKey] THEN -- 1 .. 7 entity.mark _ SELECT byte FROM NoneKey => none, RoundKey => round, SquareKey => square, DiamondKey => diamond, CrossKey => cross, DollarKey => dollar, ENDCASE => percent ELSE SIGNAL SyntaxError["Mark"]; }; WidthKey => entity.width _ GetReal[s]; -- 2 ValuesKey => [entity.oldValues, entity.lastValue] _ GetValueList[s]; -- 5 EntityIdKey => entity.id _ NewEntityId[handle, GetInt[s]]; -- 7 EndOfRecordKey => EXIT; ENDCASE => SIGNAL SyntaxError["Entity"]; ENDLOOP; hashIndex _ entity.id MOD EntityHashSize; handle.entityHash[hashIndex] _ CONS[entity, handle.entityHash[hashIndex]]; }; -- GetEntity GetValueList: PROC [s: STREAM] RETURNS [vl, last: ValueList _ NIL] = { length: INT _ GetInt[s]; FOR i: INT IN [0..length) DO vl _ CONS[GetReal[s], vl]; ENDLOOP; [vl, last] _ ReverseValueList[vl, TRUE]; }; -- GetValueList GetNestedEntitiesList: PROC [s: STREAM, handle: GraphHandle, group: EntityGroup, father: NestedEntities _ NIL] RETURNS [nel: NestedEntitiesList _ NIL] = { tnel: NestedEntitiesList _ NIL; DO SELECT GetByte[s] FROM EndOfListKey => EXIT; NestedEntitiesKey => tnel _ CONS[ -- 5 GetNestedEntities[s, handle, group, father], tnel]; ENDCASE => SyntaxError["NestedEntitiesList"]; ENDLOOP; FOR ttnel: NestedEntitiesList _ tnel, ttnel.rest UNTIL ttnel = NIL DO nel _ CONS[ttnel.first, nel]; ENDLOOP; WHILE tnel # NIL DO next: NestedEntitiesList _ tnel.rest; tnel.first _ NIL; tnel.rest _ NIL; tnel _ next; ENDLOOP; }; -- GetNestedEntitiesList GetNestedEntities: PROC [s: STREAM, handle: GraphHandle, group: EntityGroup, father: NestedEntities _ NIL] RETURNS [ne: NestedEntities] = { ne _ NEW[NestedEntitiesRec _ [parent: father]]; DO SELECT GetByte[s] FROM NameKey => ne.name _ GetRope[s]; -- 1 CommentKey => ne.comment _ GetRope[s]; -- 11 EntityListKey => ne.entityList _ GetEntityList[s, handle, group, father]; -- 3 NELKey => ne.children _ GetNestedEntitiesList[s, handle, group, ne]; -- 4 EndOfRecordKey => EXIT; ENDCASE => SIGNAL SyntaxError["NestedEntities"]; ENDLOOP; }; -- GetNestedEntities GetCrossSections: PROC [s: STREAM, group: EntityGroup] = { AddValuesToEL: PROC [entityList: EntityList] = { FOR el: EntityList _ entityList, el.rest UNTIL el = NIL DO vl: ValueList _ CONS[GetReal[s], NIL]; el.first.lastValue.rest _ vl; el.first.lastValue _ vl; ENDLOOP; }; -- AddValuesToEL AddValuesToNEL: PROC [nestedEntityList: NestedEntitiesList] = { FOR nel: NestedEntitiesList _ nestedEntityList, nel.rest UNTIL nel = NIL DO AddValuesToEL[nel.first.entityList]; AddValuesToNEL[nel.first.children]; ENDLOOP; }; -- AddValuesToNEL DO -- error if eof is hit inside the loop; SELECT GetByte[s] FROM EndOfListKey => EXIT; TailsKey => { -- 8 vl: ValueList _ CONS[GetReal[s], NIL]; group.x.lastValue.rest _ vl; group.x.lastValue _ vl; AddValuesToEL[group.ys.entityList]; AddValuesToNEL[group.ys.children]; }; ENDCASE => SyntaxError["CrossSection"]; ENDLOOP; }; -- GetCrossSections InitSomeCurves: GraphProc = { OPEN handle; InitSomeEntity: PROC [ne: NestedEntities, graf: GRAPH, count: INT] RETURNS [newCount: INT] = { newCount _ count; FOR el: EntityList _ ne.entityList, el.rest UNTIL el = NIL OR newCount >= LastEntityColor DO entity: Entity _ el.first; InitSegAll[entity]; graf.entityList _ CONS[entity, graf.entityList]; newCount _ newCount + 1; ENDLOOP; FOR nel: NestedEntitiesList _ ne.children, nel.rest UNTIL nel = NIL OR newCount >= LastEntityColor DO newCount _ InitSomeEntity[nel.first, graf, newCount]; ENDLOOP; }; -- InitSomeEntity curveCount: CARDINAL _ 0; FOR egl: EntityGroupList _ entityGroupList, egl.rest UNTIL egl = NIL OR curveCount >= LastEntityColor DO curveCount _ InitSomeEntity[egl.first.ys, graph, curveCount]; ENDLOOP; graph.entityList _ ReverseEntityList[graph.entityList, TRUE]; }; -- InitSomeCurves CheckFormat: PROC [stream: STREAM] RETURNS[format: FileFormat _ illegal] = { firstChar: CHAR _ stream.PeekChar[]; IF firstChar = 0C THEN { rope: ROPE; rope _ GetRope[stream! ReadBeyondEOF => {rope _ NIL; CONTINUE}]; IF NOT rope.IsEmpty[] THEN SELECT TRUE FROM rope.Equal["* binary"] => format _ old; rope.Equal["binary graph"] => format _ binary; ENDCASE; -- illegal } ELSE format _ ascii; }; -- CheckFormat myScratch: REF TEXT _ RefText.New[256]; GetRope: PROC [stream: STREAM] RETURNS [rope: ROPE] = { length: NAT = GetCardinal[stream]; text: REF TEXT _ IF length > 256 THEN RefText.ObtainScratch[length] ELSE myScratch; FOR i: NAT IN [0..length) DO IF stream.EndOf[] THEN SIGNAL ReadBeyondEOF[ IO.PutFR["%gth character of a rope of length %g.", IO.int[i+1], IO.int[length]]]; text[i] _ stream.GetChar[]; ENDLOOP; text.length _ length; rope _ Rope.FromRefText[text]; IF length > 256 THEN RefText.ReleaseScratch[text]; }; -- GetRope GetTime: PROC [stream: STREAM] RETURNS [BasicTime.GMT] = { RETURN[LOOPHOLE[GetLongWord[stream, " a BasicTime.GMT"], BasicTime.GMT]]; }; -- GetTime GetBool: PROC [stream: STREAM] RETURNS [bool: BOOL _ FALSE] = { SELECT GetByte[stream] FROM TrueKey => bool _ TRUE; FalseKey => bool _ FALSE; ENDCASE => SIGNAL SyntaxError["Boolean"]; }; -- GetBool GetByte: PROC [stream: STREAM] RETURNS [BYTE] = { RETURN[LOOPHOLE[stream.GetChar[], BYTE]]; }; -- GetByte GetInt: PROC [stream: STREAM] RETURNS [INT] = { RETURN[LOOPHOLE[GetLongWord[stream, " an INT"], INT]]; }; -- GetInt GetReal: PROC [stream: STREAM] RETURNS [REAL] = { RETURN[LOOPHOLE[GetLongWord[stream, " a real number"], REAL]]; }; -- GetReal GetCardinal: PROC [stream: STREAM] RETURNS [CARDINAL] = { pair: CharPair; pair.high _ MyGetChar[stream, "1st byte of a cardinal"]; pair.low _ MyGetChar[stream, "2nd byte of a cardinal"]; RETURN[LOOPHOLE[pair, CARDINAL]]; }; -- GetCardinal GetLongWord: PROC [stream: STREAM, info: ROPE] RETURNS [chars: FourChars] = { chars.lh _ MyGetChar[stream, "1st byte of", info]; chars.ll _ MyGetChar[stream, "2nd byte of", info]; chars.hh _ MyGetChar[stream, "3rd byte of", info]; chars.hl _ MyGetChar[stream, "4th byte of", info]; }; -- GetLongWord MyGetChar: PROC [stream: STREAM, info1, info2: ROPE _ NIL] RETURNS [CHAR] = { IF stream.EndOf[] THEN SIGNAL ReadBeyondEOF[ IO.PutFR["%g%g.", IO.rope[info1], IO.rope[info2]]]; RETURN[stream.GetChar[]]; }; -- MyGetChar GraphFiles: Commander.CommandProc = { argList: RopeList; length: NAT; msg _ NIL; [argList, length] _ CommandTool.ParseToList[cmd]; IF length <= 0 THEN msg _ "To review graph files, type: Graph " ELSE { wDir: ROPE _ FileNames.CurrentWorkingDirectory[]; nGraphs: NAT _ 0; maxGraphs: NAT _ 12; allVersions: BOOL _ FALSE; FOR arg: RopeList _ argList, arg.rest UNTIL arg = NIL OR msg # NIL DO IF arg.first.Fetch[0] = '- THEN { IF arg.first.Length[] >= 2 THEN SELECT arg.first.Fetch[1] FROM 'A, 'a => allVersions _ TRUE; 'H, 'h => allVersions _ FALSE; IN ['0..'9] => { max: NAT _ Convert.CardFromRope[arg.first.Substr[1] ! Convert.Error => max _ maxGraphs]; -- don't change it maxGraphs _ max; }; ENDCASE; -- simply ignors illegal switches } ELSE { fileList: RopeList _ FileListFrom[arg.first, wDir, allVersions]; IF fileList = NIL THEN msg _ "No such file." ELSE FOR file: RopeList _ fileList, file.rest UNTIL file = NIL OR msg # NIL DO IF nGraphs >= maxGraphs THEN msg _ IO.PutFR[ "Note: max. %g plots for each command. You may change it by a switch.", IO.int[maxGraphs]] ELSE [, msg] _ HandleFromFile[file: file.first]; nGraphs _ nGraphs + 1; ENDLOOP; }; ENDLOOP; }; IF msg = NIL THEN msg _ "done."; }; -- GraphFiles FileListFrom: PROC [pattern, wDir: ROPE _ NIL, allVersions: BOOL _ FALSE] RETURNS [fileList: RopeList _ NIL] = { root, lastRoot: ROPE _ NIL; LinkIt: FS.NameProc -- PROC [fullFName] RETURNS [continue: BOOL] -- = { excl: INT _ fullFName.Find["!"]; continue _ TRUE; IF fullFName.Substr[excl-6, 6].Equal[".press", FALSE] THEN RETURN; IF ~allVersions THEN { root _ fullFName.Substr[0, excl]; IF root.Equal[lastRoot, FALSE] THEN { fileList.first _ fullFName; RETURN; }; lastRoot _ root; }; fileList _ CONS[fullFName, fileList]; }; -- LinkIt FS.EnumerateForNames[pattern, LinkIt, wDir]; TRUSTED {fileList _ LOOPHOLE[List.DReverse[LOOPHOLE[fileList]]]}; }; -- FileListFrom Commander.Register["Graph", GraphFiles, "Graph , reviews the graphs in the files. See GraphDoc.tioga for more information."]; Commander.Register["Plot", GraphFiles, "Plot , reviews the graphs in the files. See GraphDoc.tioga for more information."]; }. CHANGE LOG. SChen, created at October 9, 1985 6:21:05 pm PDT. ΪGraphRead.mesa, Copyright c 1985 by Xerox Corporation. All rights reserved. Last Edited by: Sweetsun Chen, November 20, 1985 2:16:46 pm PST assume handle is already locked. if no problem then return msg=nil. assume handle is already locked. title, time, and file name. create graph with bounds add texts enlist curves add cross-sections ReadPlotFile: PROC[handle: GraphHandle, s: STREAM] RETURNS [msg: ROPE _ NIL] = { OPEN handle; assume handle is already locked. nNamesMax: INT; xEntity: Entity _ NIL; entityGroup: EntityGroup _ NIL; entityList: EntityList _ NIL; v1, v2: ValueList _ NIL; x1, x2: REAL _ 0.0; texts: title, time, and file name. title: Text _ NEW[TextRec _ [ text: GetRope[s], place: [0.5, 1.1], colorIndex: 15, fontIndex: 2, justifX: center, justifY: bottom, id: NewTextId[handle, 0]]]; time: Text _ NEW[TextRec _ [ text: Convert.RopeFromTime[GetTime[s]], place: [1.0, -0.35], colorIndex: 15, fontIndex: 1, justifX: right, justifY: bottom, id: NewTextId[handle, 1]]]; file: Text _ NEW[TextRec _ [ text: (graph.fileName _ NARROW[Atom.GetPropFromList[s.propList, $Name]]), place: [0, -0.35], colorIndex: 15, fontIndex: 1, justifX: left, justifY: bottom, id: NewTextId[handle, 2]]]; graph.texts _ AppendTexts[graph.texts, CONS[title, CONS[time, CONS[file, NIL]]]]; allTexts _ AppendTexts[allTexts, CONS[title, CONS[time, CONS[file, NIL]]]]; bounds, nNamesMax, entityHash, and entityGroupList. graph.bounds _ [GetReal[s], GetReal[s], GetReal[s], GetReal[s]]; graph.auto[bounds] _ FALSE; nNamesMax _ GetCardinal[s]; xEntity _ NEW[EntityRec _ [name: "X", id: NewEntityId[handle, 0]]]; entityHash[xEntity.id MOD EntityHashSize] _ CONS[xEntity, entityHash[xEntity.id MOD EntityHashSize]]; entityGroup _ NEW[EntityGroupRec _ [ x: xEntity, ys: NEW[NestedEntitiesRec _ [comment: graph.fileName]], id: NewGroupId[handle, 0] -- should update length later. ]]; xEntity.group _ entityGroup; entityGroupList _ AppendEGL[entityGroupList, CONS[entityGroup, NIL]]; allocate EntityRec for each curve, construct the entity list entityGroupList.ys and graph.entityList, and create the graph viewer to show the axes, title and time. FOR i: INT IN [1..nNamesMax] DO index: HashIndex _ i MOD EntityHashSize; entity: Entity _ NEW[EntityRec _ [ name: GetRope[s], colorIndex: ((i-1) MOD LastEntityColor) + 1, group: entityGroup, id: NewEntityId[handle, i] ]]; entityHash[index] _ CONS[entity, entityHash[index]]; entityList _ CONS[entity, entityList]; ENDLOOP; entityGroup.ys.entityList _ ReverseEntityList[entityList, FALSE]; graph.entityList _ AppendEntityList[graph.entityList, ReverseEntityList[entityList, FALSE]]; IF chart.viewer = NIL THEN { ShowChart[handle]; -- will unlock handle right before viewer is opened up. No more (11/13/85). Lock[handle]; } ELSE PaintAll[handle]; add tails entityGroup.length _ GetCardinal[s]; FOR i: INT IN [1..entityGroup.length] DO vl: ValueList; x2 _ GetReal[s]; vl _ CONS[x2, NIL]; IF xEntity.lastValue = NIL THEN xEntity.oldValues _ xEntity.lastValue _ vl ELSE {xEntity.lastValue.rest _ vl; xEntity.lastValue _ vl}; v2 _ NIL; FOR el: EntityList _ ReverseEntityList[entityList, FALSE], el.rest UNTIL el = NIL DO y2: REAL _ GetReal[s]; yEntity: Entity _ el.first; v2 _ CONS[y2, v2]; vl _ CONS[y2, NIL]; IF yEntity.lastValue = NIL THEN yEntity.oldValues _ yEntity.lastValue _ vl ELSE {yEntity.lastValue.rest _ vl; yEntity.lastValue _ vl}; ENDLOOP; v2 _ ReverseValueList[v2]; IF i > 1 THEN PaintTails[handle, paint, v1, v2, x1, x2]; -- v1 and v2 are in reversed order. v1 _ GraphCleanUp.CleanUpVL[v1]; v1 _ v2; x1 _ x2; ENDLOOP; v1 _ NIL; v2 _ GraphCleanUp.CleanUpVL[v2]; correct order of oldValues for xEntity and each entity on graph.entityList xEntity.oldValues _ ReverseValueList[xEntity.oldValues]; FOR el: EntityList _ graph.entityList, el.rest UNTIL el = NIL DO el.first.oldValues _ ReverseValueList[el.first.oldValues]; ENDLOOP; initialize each segment data InitSegEnd[xEntity]; FOR el: EntityList _ entityList, el.rest UNTIL el = NIL DO InitSegAll[el.first]; ENDLOOP; lastTextId _ 1; lastEntityId _ nNamesMax; lastEntityGroupId is 0. entityList _ GraphCleanUp.CleanUpEL[entityList, FALSE]; nNamesMax _ nNamesMax MOD LastEntityColor; lastEntityColor _ IF nNamesMax = 0 THEN LastEntityColor ELSE nNamesMax; handle should be unlocked by caller. }; -- ReadPlotFile assume handle is already locked. The format could have been more compact. But it is easier to allow for upward/backward compatibility this way. AutoKey => graph.auto _ [GetBool[s], GetBool[s]]; initialize segments for x entities. draw some of the curves. handle should be unlocked by caller of this proc. reads in graph.texts in the reversed order. reverse it. append it cleanup t1 NameKey => eg.name _ GetRope[s]; -- 1 reverse it. clean up. reading routines scratch for GetRope and for historical reasons Κ!˜JšœΟmœ1™Lšœ™Icode™/—J˜šΟk ˜ Jšœžœ˜Jšœžœžœ˜Jšœ žœžœ˜Jšœ žœ˜(Jšœ žœ˜ Jšœžœ%˜2Jšœ žœ˜*Jšžœžœ2˜:Jš œžœžœŒžœHžœ˜ωJ˜Jšœ žœB˜PJšœ žœžœažœ˜Jšœ žœ²˜ΑJšžœžœS˜[Jšœžœ˜Jšœžœ&˜3Jšœžœ<˜FJšœ žœ˜ —J˜šœ žœž˜Jšžœ3žœžœC˜ƒJšžœ˜—J˜Jšžœ9˜=J˜Jš œžœžœ žœžœžœ˜9Jš œ žœžœ žœžœžœ˜7Jš œ žœžœ žœžœžœ˜5J˜Jšœ žœ!˜1J˜šΟnœžœžœžœžœžœžœžœžœ˜fJšœ˜J˜ Jšœžœ˜Jšœ˜Jšœžœ˜J˜JšœΟc˜J˜—šŸœžœžœžœžœžœžœ˜UJ™ Jšœ"™"Jšžœžœžœ˜4šžœžœ˜Jšœžœžœ˜Jšœžœžœ$žœ˜Lšžœžœžœ˜šžœ˜˜Jšœžœ"žœžœ˜SJšžœ˜ J˜—šœ˜šœžœ*˜2Jšžœžœ˜(—Jšžœ˜ J˜—˜Jšœžœžœžœ˜MJšžœ˜ J˜—šžœ˜Jšœ˜Jšžœ˜ J˜—šžœ ˜ šœžœ˜šžœžœž˜J˜J˜*Jš žœžœžœžœžœ˜F—Jšžœ˜Jšœ˜—Jšžœ˜ J˜—šžœ˜ Jšœ˜Jšžœ˜ J˜—J˜—š žœžœžœžœžœžœ˜GJšœ1˜1Jšœžœ˜J˜—Jšœžœ˜šœžœž˜ J˜J˜#J˜"Jšžœ˜!—J˜—Jšœžœ˜šžœžœžœ˜ šžœžœžœ˜7Jšžœ žœžœ ˜7——Jšžœžœžœ ˜J˜—Jšœ  ˜J˜šŸ œžœžœžœžœžœžœ˜]J™ Jšœ žœ˜$J™J™Jšœžœ˜Jšœžœ$˜.Jšœžœžœ*˜=J˜J™šœ˜Jšœ˜Jšœ žœ˜Jšœ˜Jšœ ˜ Jšœ žœ˜Jšœ9˜9Jšœ˜J˜J˜—J™ ˜%JšœP˜P—˜%JšœP˜P—˜%JšœM˜M—J˜J™ Jšœ˜šžœžœžœž˜šœE˜EJšœžœ˜-—Jšžœ˜—J™J™Jšœ˜šžœžœžœž˜"Jšœžœ˜Jšœžœ˜šžœžœžœž˜Jšœ žœ˜$Jšžœ˜—Jšœ-˜-Jšžœ˜—Jšœ ˜J˜—šŸ œžœžœžœžœžœžœ™]J™ Jšœ žœ™Jšœžœ™Jšœžœ™Jšœžœ™Jšœžœ™Jšœžœ™J™J™"šœžœ ™Jšœ™JšœR™RJšœ™—šœ žœ ™Jšœ'™'JšœS™SJšœ™—šœ žœ ™Jšœžœ+™IJšœP™PJšœ™—Jš œ'žœžœžœžœ™QJš œ!žœžœžœžœ™KJ™Jšœ4™4Jšœ@™@Jšœžœ™Jšœ™Jšœ žœ6™CJšœžœžœ žœ™ešœžœ™$Jšœ ™ Jšœžœ0™7Jšœ ™8Jšœ™—J™Jšœ-žœžœ™EJ™Jšœ£™£šžœžœžœž™Jšœžœ™(šœžœ™"Jšœ™Jšœžœ™,J™Jšœ™Jšœ™—Jšœžœ™4Jšœ žœ™&Jšžœ™—Jšœ:žœ™AJšœTžœ™\šžœžœžœ™Jšœ K™^J™ J™—Jšžœ™J™Jšœ ™ Jšœ$™$šžœžœžœž™(Jšœ™Jšœ™Jšœžœžœ™Jšžœžœžœ+™JJšžœ7™;Jšœžœ™ š žœ0žœ žœžœž™TJšœžœ™J™Jšœžœ ™Jšœžœžœ™Jšžœžœžœ+™JJšžœ7™;Jšžœ™—Jšœ™Jšžœžœ, #™\J™ Jšœ™Jšœ™Jšžœ™—Jšœžœ™ J™ ™JšœJ™JJšœ8™8šžœ,žœžœž™@Jšœ:™:Jšžœ™——J™J™Jšœ™šžœ&žœžœž™:Jšœ™Jšžœ™—J™J™Jšœ™Jšœ™Jšœ0žœ™7Jšœžœ™*Jšœžœžœžœ ™GJšœ$™$Jšœ ™J™—š Ÿ œžœžœžœžœžœ˜RJšžœ˜ J™ J™oJšœžœ*˜Ašžœžœ ž˜šžœ ž˜Jšœ /˜Kšœ ˜ Jšœ˜Jšœ%žœ˜,Jšœ(žœ˜.Jšœ˜—Jšœ!˜!J˜-Jšœ#˜#Jšœ˜J˜'Jšœ!˜!Jšœ1™1Jšœ!˜!Jšœ˜Jšœ4˜4Jšœžœ 4˜LJšžœžœ˜'—Jšžœ˜—J™J™#šžœ:žœ žœž˜SJšœ˜Jšžœ˜—J˜J™Jšœ˜Jšžœžœžœ˜3šžœ˜J˜J˜Jšœ˜J˜—J™1Jšœ ˜J˜š Ÿœžœžœžœžœ˜PJ™+šž˜šžœ ž˜Jšœžœ˜Jšœžœ ˜8Jšžœžœ˜'—Jšžœ˜—Jšœžœ˜"Jšœ  ˜J˜—š Ÿœžœžœžœžœ˜MJšœžœ˜šž˜šžœ ž˜Jšœ# ˜'Jšœ3 ˜7Jšœ- ˜1Jšœ/ ˜3Jšœ+ ˜/šœ ˜šžœžœ ž˜Jšœžœ˜ šžœ ž˜šœ  ˜Jšœ˜šžœžœžœ˜2šžœžœ  ˜Jšœ&žœ ˜6——Jšžœžœ ˜+J˜—šœ  ˜Jšœ˜šžœžœžœ˜2šžœžœ  ˜Jšœ$žœ ˜5——Jšžœžœ ˜+J˜—Jšœžœ˜Jšžœžœ˜0—Jšžœ˜—J˜—Jšœ7 ˜;Jšœžœ˜Jšžœžœ˜&——Jšžœ˜Jšœ  ˜ J˜—šœ˜Jšœžœ˜ šžœžœ ž˜šžœž˜Jšœžœ˜šžœ!  ˜.šœžœž˜$Jšœ2žœ ˜B—J˜ J˜—Jšžœžœ˜(—Jšžœ˜—Jšœ  ˜J˜šŸœžœžœ˜/šžœžœ ž˜šžœ ž˜Jšœžœ˜Jšœ3 ˜7Jšœ ˜$Jšžœžœ˜'—Jšžœ˜—Jšœ  ˜—J˜—šœ˜Jšœžœ˜ šžœžœ ž˜šžœž˜Jšœžœ˜šžœ ˜Jš œžœžœ žœžœ˜&J˜J˜—Jšžœžœ˜)—Jšžœ˜—Jšœ  ˜J˜šŸ œžœžœ˜1šžœžœ ž˜šžœ ž˜Jšœžœ˜Jšœ% ˜)Jšœ% ˜)Jšœ/ ˜FJšœ ˜$Jšžœžœ˜(—Jšžœ˜—Jšœ  ˜J˜——šœ˜Jšœžœ˜ šžœžœ ž˜šžœž˜Jšœžœ˜šžœ ˜Jš œžœžœ žœžœ˜&šžœžœ ž˜šžœ ž˜Jšœžœ˜Jšœ' ˜,Jšžœžœ˜&—Jšžœ˜—J˜—Jšžœžœ˜'—Jšžœ˜—Jšœ  ˜J˜—šœ!˜!Jšœžœ˜ šžœžœ ž˜šžœž˜Jšœžœ˜šžœ ˜Jš œžœžœ žœžœ˜&šžœžœ ž˜šžœ ž˜Jšœžœ˜Jšœ/ ˜3Jšœ1 ˜5Jšžœžœ˜*—Jšžœ˜—J˜—Jšžœžœ˜+—Jšžœ˜—Jšœ ˜J˜—šœ˜Jšœžœ˜ šžœžœ ž˜šžœž˜Jšœžœ˜šžœ ˜Jš œžœžœ žœžœ˜&šžœžœ ž˜šžœ ž˜Jšœžœ˜Jšœ, ˜0šœ žœžœ  ˜žœ ˜HJšœ5 ˜9Jšœ$ ˜(Jšœ,˜,Jšœžœ˜Jšžœžœ˜-—Jšžœ˜—šœ ˜J˜——š Ÿ œžœžœCžœžœ '˜«šž˜šžœ ž˜Jšœžœ˜Jšœžœ+ ˜EJšžœ˜%—Jšžœ˜—Jšœžœ ˜6Jšœ ˜J˜š Ÿ œžœžœAžœžœžœ˜‚Jšœ˜Jšœ žœ*˜6šž˜šžœ ž˜Jšœ% ˜)Jšœ+ ˜0Jšœ1 ˜5šœ  ˜Jšœžœ˜šžœžœžœ  ˜/šœžœž˜Jšœ8˜8Jšœ>˜>Jšžœ ˜——Jšžœžœ˜ J˜—Jšœ' ˜+JšœE ˜IJšœ; ˜?Jšœžœ˜Jšžœžœ˜(—Jšžœ˜—Jšœžœ˜)Jšœžœ'˜JJšœ  ˜—J˜š Ÿ œžœžœžœžœ˜FJšœžœ ˜šžœžœžœ ž˜Jšœžœ˜Jšžœ˜—Jšœ"žœ˜(šœ ˜J˜———š ŸœžœžœDžœžœžœ˜šJšœžœ˜šž˜šžœ ž˜Jšœžœ˜šœžœ ˜&Jšœ3˜3—Jšžœ&˜-—Jšžœ˜—J™ šžœ.žœ žœž˜EJšœžœ˜Jšžœ˜—J™ šžœžœž˜Jšœ%˜%Jšœ žœ˜Jšœ žœ˜J˜ Jšžœ˜—Jšœ ˜J˜—š ŸœžœžœDžœžœ˜‹Jšœžœ'˜/šž˜šžœ ž˜Jšœ! ˜%Jšœ' ˜,JšœJ ˜NJšœE ˜IJšœžœ˜Jšžœžœ˜0—Jšžœ˜—Jšœ ˜J˜—šŸœžœžœ˜:šŸ œžœ˜0šžœ&žœžœž˜:Jšœžœ žœ˜&Jšœ˜J˜Jšžœ˜—Jšœ ˜—šŸœžœ+˜?šžœ6žœžœž˜KJšœ$˜$Jšœ#˜#Jšžœ˜—Jšœ ˜—šžœ '˜*šžœ ž˜Jšœžœ˜šœ ˜Jšœžœ žœ˜&Jšœ˜J˜Jšœ#˜#Jšœ"˜"J˜—Jšžœ ˜'—Jšžœ˜—Jšœ ˜—J˜—šœžœ˜*š Ÿœžœžœ žœžœ žœ˜^Jšœ˜šžœ(˜+Jšžœžœžœž˜0J˜Jšœ˜Jšœžœ˜0Jšœ˜Jšžœ˜—šžœ0˜3Jšžœžœžœž˜1Jšœ5˜5Jšžœ˜—Jšœ ˜J˜—Jšœ žœ˜šžœ1˜4šžœžœžœž˜3Jšœ=˜=—Jšžœ˜—Jšœ7žœ˜=Jšœ ˜J˜——šŸ œžœ žœžœ"˜LJšœ žœ˜$šžœžœ˜Jšœž˜ Jšœ0žœžœ˜@š žœžœžœžœžœž˜+Jšœ'˜'Jšœ.˜.Jšžœ  ˜—J˜—Jšžœ˜Jšœ ˜——J˜šœ™J™Jšœ žœžœ˜'J˜š Ÿœžœ žœžœžœ˜7Jšœžœ˜"Jš œžœžœžœžœžœ ˜Sšžœžœžœ ž˜šžœžœžœ˜,Jšžœ1žœ žœ˜Q—Jšœ˜Jšžœ˜—Jšœ˜Jšœ˜Jšžœžœ˜2Jšœ  ˜ J˜—š Ÿœžœ žœžœ žœ˜:Jšžœžœ4žœ˜IJšœ  ˜ J˜—š Ÿœžœ žœžœžœžœ˜?šžœž˜Jšœžœ˜Jšœžœ˜Jšžœžœ˜)—Jšœ  ˜ J˜—š Ÿœžœ žœžœžœ˜1Jšžœžœžœ˜)Jšœ  ˜ J˜—š Ÿœžœ žœžœžœ˜/Jšžœžœ!žœ˜6Jšœ  ˜ J˜—š Ÿœžœ žœžœžœ˜1Jšžœžœ(žœ˜>Jšœ  ˜ J˜—š Ÿ œžœ žœžœžœ˜9J˜Jšœ8˜8Jšœ7˜7Jšžœžœžœ˜!Jšœ ˜J˜—š Ÿ œžœ žœžœžœ˜MJšœ2˜2Jšœ2˜2Jšœ2˜2Jšœ2˜2Jšœ ˜J˜—šŸ œžœ žœžœžœžœžœ˜Mšžœžœžœ˜,Jšžœžœžœ˜3—Jšžœ˜Jšœ  ˜——J˜šΟb œ˜%Jšœ˜Jšœžœ˜ Jšœžœ˜ Jšœ1˜1Jšžœ žœ3˜Fšžœ˜Jšœžœ'˜1Jšœ žœ˜Jšœ žœ˜Jšœ žœžœ˜š žœ#žœžœžœžœž˜Ešžœžœ˜!šžœžœžœž˜>Jšœžœ˜Jšœžœ˜šžœ˜šœžœ+˜3Jšœ% ˜7—J˜J˜—Jšžœ !˜*—J˜—šžœ˜Jšœ@˜@Jšžœ žœžœ˜,š žœžœ&žœžœžœžœž˜Nšžœžœžœ˜,JšœG˜GJšžœ˜—Jšžœ,˜0Jšœ˜Jšžœ˜—J˜—Jšžœ˜—J˜Jšžœžœžœ˜ —Jšœ  ˜J˜šŸ œžœžœžœžœžœžœžœ˜pJšœžœžœ˜š œžœ  Πck  ’  ’ œ˜GJšœžœ˜ Jšœ žœ˜Jšžœ-žœžœžœ˜Bšžœžœ˜J˜!šžœžœžœ˜%Jšœ˜Jšžœ˜J˜—Jšœ˜J˜—Jšœ žœ˜%Jšœ œ˜ —J˜Jšžœ*˜,Jšžœ žœžœ˜AJšœ ˜—J˜—JšœŽ˜ŽJšœ™šœŒ˜ŒJ˜—Jšœ˜J˜šžœžœ˜ Jšœ-žœ˜1——…—V4Š