DIRECTORY Ascii, Atom, BrineIO, CedarProcess, Core, CoreClasses, CoreDirectory, CoreIO, CoreOps, CoreProperties, FS, GList, IO, ProcessProps, RefTab, RefTabExtras, Rope, SymTab; CoreIOImpl: CEDAR PROGRAM IMPORTS Atom, BrineIO, CedarProcess, CoreClasses, CoreOps, CoreProperties, FS, GList, IO, ProcessProps, RefTab, RefTabExtras, Rope, SymTab EXPORTS CoreIO = BEGIN OPEN CoreIO, BrineIO; ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; coreWriteProcProp: ATOM = $CoreWriteProc; coreReadProcProp: ATOM = $CoreReadProc; calledFromDir: Core.ROPE _ NIL; classRegistry: RefTab.Ref _ RefTab.Create[equal: RefTabExtras.EqualRope, hash: RefTabExtras.HashRope]; UnknownProperty: PUBLIC SIGNAL [propKey: ATOM] = CODE; UnknownCellClass: PUBLIC SIGNAL [class: Core.CellClass] = CODE; ReportSaveCellType: PUBLIC PROC [cellType: Core.CellType, fileName: ROPE _ NIL] RETURNS [props: LIST OF ATOM _ NIL, classes: LIST OF Core.CellClass _ NIL] = { SaveCellType[cellType, fileName ! UnknownProperty => { IF NOT GList.Member[propKey, props] THEN props _ CONS [propKey, props]; RESUME; }; UnknownCellClass => { IF NOT GList.Member[class, classes] THEN classes _ CONS [class, classes]; RESUME; }]; }; SaveCellType: PUBLIC PROC [cellType: Core.CellType, fileName: ROPE _ NIL] = { stream: STREAM; IF fileName=NIL THEN fileName _ Rope.Cat[CoreOps.GetCellTypeName[cellType], ".core"]; stream _ FS.StreamOpen[fileName, $create]; WriteCellType[stream, cellType]; IO.Close[stream]; }; importCellClass: PUBLIC Core.CellClass _ CoreOps.SetClassPrintProc[NEW [Core.CellClassRec _ [name: "Import", recast: RecastImport, layersProps: TRUE]], PrintImport]; ReadFile: PROC [name: Core.ROPE] RETURNS [s: STREAM _ NIL] ~ { s _ FS.StreamOpen[fileName: name ! FS.Error => CONTINUE]; IF s=NIL THEN s _ FS.StreamOpen[fileName: name, wDir: calledFromDir]; }; RestoreCellType: PUBLIC PROC [cellName: ROPE _ NIL, fileName: ROPE _ NIL] RETURNS [cellType: Core.CellType] = { stream: STREAM; wireIDTable: RefTab.Ref; IF fileName=NIL AND cellName=NIL THEN ERROR; IF fileName=NIL THEN fileName _ Rope.Cat[cellName, ".core"]; stream _ ReadFile[fileName]; stream.propList _ Atom.PutPropOnList[stream.propList, $CoreIOFileName, fileName]; [cellType, wireIDTable] _ ReadCellTypePublic[stream]; stream.propList _ Atom.PutPropOnList[stream.propList, $CoreIOWireTable, wireIDTable]; cellType.class _ importCellClass; cellType.data _ stream; }; RecastImport: Core.RecastProc = { stream: STREAM _ NARROW [me.data]; wireIDTable: RefTab.Ref _ GetRefTab[stream, $CoreIOWireTable]; me.data _ NIL; ReadCellTypeRest[stream, me, wireIDTable]; new _ me; }; PrintImport: CoreOps.PrintClassProc = { stream: STREAM _ NARROW [data]; CoreOps.PrintIndent[indent, out]; IO.PutF[out, "Import of : %g", IO.rope[NARROW [Atom.GetPropFromList[stream.propList, $CoreIOFileName]]]]; }; SaveLibrary: PUBLIC PROC [library: CoreDirectory.Library, fileName: ROPE] = { EachLibraryItem: SymTab.EachPairAction = { WriteRope[stream, key]; WriteCellType[stream, NARROW [val] ! UnknownProperty => RESUME; UnknownCellClass => RESUME]; }; stream: STREAM; fileName _ Rope.Cat[fileName, ".corelib"]; stream _ FS.StreamOpen[fileName, $create]; WriteInt[stream, SymTab.GetSize[library]]; [] _ SymTab.Pairs[library, EachLibraryItem]; IO.Close[stream]; }; RestoreLibrary: PUBLIC PROC [fileName: ROPE] RETURNS [library: CoreDirectory.Library] = { stream: STREAM; size: INT; fileName _ Rope.Cat[fileName, ".corelib"]; stream _ ReadFile[fileName]; stream.propList _ Atom.PutPropOnList[stream.propList, $CoreIOFileName, fileName]; size _ ReadInt[stream]; library _ SymTab.Create[(size/2)*2+1]; THROUGH [0 .. size) DO key: ROPE _ ReadRope[stream]; cellType: Core.CellType _ ReadCellType[stream]; [] _ SymTab.Store[library, key, cellType]; ENDLOOP; IO.Close[stream]; }; RegisterClass: PUBLIC PROC [class: Core.CellClass, write: ClassWriteProc, read: ClassReadProc] RETURNS [sameClass: Core.CellClass] = { sameClass _ class; CoreProperties.PutCellClassProp[class, coreWriteProcProp, NEW[ClassWriteProc _ write]]; CoreProperties.PutCellClassProp[class, coreReadProcProp, NEW[ClassReadProc _ read]]; [] _ RefTab.Store[classRegistry, class.name, class]; }; RegisterProperty: PUBLIC PROC [prop: ATOM, write: PropWriteProc, read: PropReadProc] RETURNS [sameProp: ATOM] = { props: Core.Properties _ CoreProperties.FetchProperties[prop]; props _ CoreProperties.PutProp[props, coreWriteProcProp, NEW[PropWriteProc _ write]]; props _ CoreProperties.PutProp[props, coreReadProcProp, NEW[PropReadProc _ read]]; CoreProperties.StoreProperties[prop, props]; sameProp _ prop; }; WriteCellType: PUBLIC PROC [stream: STREAM, cellType: Core.CellType] = { cellTypeIDTable: RefTab.Ref _ GetRefTab[stream, $CoreIOCellTypeTable]; cellTypeID: ROPE _ NARROW [RefTab.Fetch[cellTypeIDTable, cellType].val]; IF cellTypeID=NIL THEN { wireIDTable: RefTab.Ref _ RefTab.Create[]; classWrite: REF ClassWriteProc _ NARROW[CoreProperties.GetCellClassProp[from: cellType.class, prop: coreWriteProcProp]]; IF classWrite=NIL THEN { SIGNAL UnknownCellClass[cellType.class]; cellType _ CoreOps.Recast[cellType]; IF cellType=NIL THEN ERROR; WriteCellType[stream, cellType]; RETURN; }; cellTypeID _ MakeID["C", RefTab.GetSize[cellTypeIDTable]]; IF NOT RefTab.Insert[cellTypeIDTable, cellType, cellTypeID] THEN ERROR; WriteID[stream, cellTypeID]; WriteWire[stream, wireIDTable, cellType.public]; WriteProperties[stream, cellType.properties]; WriteRope[stream, cellType.class.name]; classWrite^[stream, cellType, wireIDTable]; } ELSE WriteID[stream, cellTypeID]; }; ReadCellType: PUBLIC PROC [stream: STREAM] RETURNS [cellType: Core.CellType] = { wireIDTable: RefTab.Ref _ NIL; [cellType, wireIDTable] _ ReadCellTypePublic[stream]; IF wireIDTable#NIL THEN ReadCellTypeRest[stream, cellType, wireIDTable]; }; ReadCellTypePublic: PUBLIC PROC [stream: STREAM] RETURNS [cellType: Core.CellType, wireIDTable: RefTab.Ref] = { cellTypeIDTable: RefTab.Ref _ GetRefTab[stream, $CoreIOCellTypeTable, RefTabExtras.EqualRope, RefTabExtras.HashRope]; cellTypeID: ROPE _ ReadID[stream]; IF Rope.Fetch[cellTypeID]#'C THEN ERROR; cellType _ NARROW[RefTab.Fetch[cellTypeIDTable, cellTypeID].val]; IF cellType=NIL THEN { cellType _ NEW[Core.CellTypeRec]; IF NOT RefTab.Insert[cellTypeIDTable, cellTypeID, cellType] THEN ERROR; wireIDTable _ RefTab.Create[equal: RefTabExtras.EqualRope, hash: RefTabExtras.HashRope]; cellType.public _ ReadWire[stream, wireIDTable]; cellType.properties _ ReadProperties[stream]; }; }; ReadCellTypeRest: PUBLIC PROC [stream: STREAM, cellType: Core.CellType, wireIDTable: RefTab.Ref] = { className: ROPE _ ReadRope[stream]; classRead: REF ClassReadProc _ NIL; cellType.class _ NARROW[RefTab.Fetch[classRegistry, className].val]; classRead _ NARROW[CoreProperties.GetCellClassProp[from: cellType.class, prop: coreReadProcProp]]; classRead^[stream, cellType, wireIDTable]; }; WriteWire: PUBLIC PROC [stream: STREAM, wireIDTable: RefTab.Ref, wire: Core.Wire] = { wireID: ROPE _ NARROW[RefTab.Fetch[wireIDTable, wire].val]; IF wireID=NIL THEN { wireID _ MakeID["W", RefTab.GetSize[wireIDTable]]; IF NOT RefTab.Insert[wireIDTable, wire, wireID] THEN ERROR; WriteID[stream, wireID]; WriteInt[stream, wire.size]; WriteProperties[stream, wire.properties]; FOR w: NAT IN [0 .. wire.size) DO WriteWire[stream, wireIDTable, wire[w]]; ENDLOOP; } ELSE WriteID[stream, wireID]; }; ReadWire: PUBLIC PROC [stream: STREAM, wireIDTable: RefTab.Ref] RETURNS [wire: Core.Wire] = { wireID: ROPE _ ReadID[stream]; IF Rope.Fetch[wireID]#'W THEN ERROR; wire _ NARROW[RefTab.Fetch[wireIDTable, wireID].val]; IF wire=NIL THEN { wireSize: NAT _ ReadInt[stream]; wire _ NEW[Core.WireRec[wireSize]]; IF NOT RefTab.Insert[wireIDTable, wireID, wire] THEN ERROR; wire.properties _ ReadProperties[stream]; FOR w: NAT IN [0 .. wire.size) DO wire[w] _ ReadWire[stream, wireIDTable]; ENDLOOP; }; }; WriteProperties: PUBLIC PROC [stream: STREAM, properties: Core.Properties] = { propCount: INT _ 0; CedarProcess.CheckAbort[]; FOR props: Core.Properties _ properties, props.rest UNTIL props=NIL DO propKey: ATOM _ NARROW[props.first.key]; propVal: REF ANY _ props.first.val; propWrite: REF PropWriteProc _ NARROW[CoreProperties.GetProp[from: CoreProperties.FetchProperties[propKey], prop: coreWriteProcProp]]; IF propWrite#NIL OR ISTYPE[propVal, ROPE] OR ISTYPE[propVal, LIST OF ROPE] OR ISTYPE[propVal, REF INT] OR ISTYPE[propVal, REF NAT] OR ISTYPE[propVal, ATOM] OR ISTYPE[propVal, REF REAL] OR ISTYPE[propVal, REF BOOL] THEN propCount _ propCount + 1; ENDLOOP; WriteInt[stream, propCount]; FOR props: Core.Properties _ properties, props.rest UNTIL props=NIL DO StartProp: PROC [type: ROPE] = { WriteAtom[stream, propKey]; IF type#NIL THEN WriteID[stream, type]; }; propKey: ATOM _ NARROW[props.first.key]; propWrite: REF PropWriteProc _ NARROW[CoreProperties.GetProp[from: CoreProperties.FetchProperties[propKey], prop: coreWriteProcProp]]; IF propWrite=NIL THEN WITH props.first.val SELECT FROM r: ROPE => {StartProp["r"]; WriteRope[stream, r]}; lor: LIST OF ROPE => {StartProp["lor"]; WriteRopes[stream, lor]}; i: REF INT => {StartProp["i"]; WriteInt[stream, i^]}; n: REF NAT => {StartProp["n"]; WriteInt[stream, n^]}; a: ATOM => {StartProp["a"]; WriteAtom[stream, a]}; real: REF REAL => {StartProp["real"]; WriteReal[stream, real^]}; rb: REF BOOL => {StartProp["rb"]; WriteBool[stream, rb^]}; ENDCASE => SIGNAL UnknownProperty[propKey] ELSE {StartProp[NIL]; propWrite^[stream, propKey, props.first.val]}; ENDLOOP; }; ReadProperties: PUBLIC PROC [stream: STREAM] RETURNS [properties: Core.Properties _ NIL] = { propCount: INT _ ReadInt[stream]; CedarProcess.CheckAbort[]; FOR c: INT IN [0..propCount) DO key: ATOM _ ReadAtom[stream]; val: REF ANY; propRead: REF PropReadProc _ NARROW[CoreProperties.GetProp[from: CoreProperties.FetchProperties[key], prop: coreReadProcProp]]; IF propRead=NIL THEN { type: ROPE _ ReadID[stream]; val _ SELECT TRUE FROM Rope.Equal["r", type] => ReadRope[stream], Rope.Equal["lor", type] => ReadRopes[stream], Rope.Equal["i", type] => NEW[INT _ ReadInt[stream]], Rope.Equal["n", type] => NEW[NAT _ ReadInt[stream]], Rope.Equal["a", type] => ReadAtom[stream], Rope.Equal["real", type] => NEW[REAL _ ReadReal[stream]], Rope.Equal["rb", type] => NEW[BOOL _ ReadBool[stream]], ENDCASE => ERROR UnknownProperty[Atom.MakeAtom[type]]; } ELSE val _ propRead^[stream, key]; properties _ CONS [[key, val], properties]; ENDLOOP; }; WriteRecordCell: ClassWriteProc = { rct: CoreClasses.RecordCellType _ NARROW[cellType.data]; WriteInt[stream, rct.size]; WriteWire[stream, wireIDTable, rct.internal]; FOR in: NAT IN [0..rct.size) DO instance: CoreClasses.CellInstance _ rct[in]; WriteWire[stream, wireIDTable, instance.actual]; WriteProperties[stream, instance.properties]; WriteCellType[stream, instance.type]; ENDLOOP; }; ReadRecordCell: ClassReadProc = { rct: CoreClasses.RecordCellType _ NEW[CoreClasses.RecordCellTypeRec[ReadInt[stream]]]; rct.internal _ ReadWire[stream, wireIDTable]; FOR in: NAT IN [0..rct.size) DO instance: CoreClasses.CellInstance _ NEW[CoreClasses.CellInstanceRec]; rct[in] _ instance; instance.actual _ ReadWire[stream, wireIDTable]; instance.properties _ ReadProperties[stream]; instance.type _ ReadCellType[stream]; ENDLOOP; cellType.data _ rct; }; WriteTransistor: ClassWriteProc = { tran: CoreClasses.Transistor _ NARROW[cellType.data]; WriteID[stream, CoreClasses.transistorTypeNames[tran.type]]; }; ReadTransistor: ClassReadProc = { tran: CoreClasses.Transistor _ NEW[CoreClasses.TransistorRec]; type: ROPE _ ReadID[stream]; FOR t: CoreClasses.TransistorType IN CoreClasses.TransistorType DO IF Rope.Equal[CoreClasses.transistorTypeNames[t], type] THEN { tran.type _ t; EXIT; }; REPEAT FINISHED => ERROR; ENDLOOP; cellType.data _ tran; }; WriteUnspecified: ClassWriteProc = {}; ReadUnspecified: ClassReadProc = {cellType.data _ NIL}; WriteSequence: ClassWriteProc = { WriteSequenceSet: PROC [set: CoreClasses.SequenceSet] = { IF set=NIL THEN WriteInt[stream, -1] ELSE { WriteInt[stream, set.length]; FOR i: NAT IN [0..set.length) DO WriteInt[stream, set[i]]; ENDLOOP; }; }; seq: CoreClasses.SequenceCellType _ NARROW[cellType.data]; WriteCellType[stream, seq.base]; WriteInt[stream, seq.count]; WriteSequenceSet[seq.sequence]; WriteSequenceSet[seq.flatSequence]; }; ReadSequence: ClassReadProc = { ReadSequenceSet: PROC RETURNS [set: CoreClasses.SequenceSet _ NIL] = { length: INT _ ReadInt[stream]; IF length >= 0 THEN { set _ NEW[CoreClasses.SequenceSetRec[length]]; FOR i: NAT IN [0..set.length) DO set[i] _ ReadInt[stream]; ENDLOOP; }; }; seq: CoreClasses.SequenceCellType _ NEW[CoreClasses.SequenceCellTypeRec]; seq.base _ ReadCellType[stream]; seq.count _ ReadInt[stream]; seq.sequence _ ReadSequenceSet[]; seq.flatSequence _ ReadSequenceSet[]; cellType.data _ seq; }; [] _ RegisterClass[CoreClasses.recordCellClass, WriteRecordCell, ReadRecordCell]; [] _ RegisterClass[CoreClasses.transistorCellClass, WriteTransistor, ReadTransistor]; [] _ RegisterClass[CoreClasses.unspecifiedCellClass, WriteUnspecified, ReadUnspecified]; [] _ RegisterClass[CoreClasses.sequenceCellClass, WriteSequence, ReadSequence]; calledFromDir _ NARROW[ProcessProps.GetProp[$WorkingDirectory]]; END. rCoreIOImpl.mesa Copyright Σ 1985, 1987 by Xerox Corporation. All rights reserved. Barth, April 16, 1987 4:03:41 pm PDT Bertrand Serlet May 5, 1988 3:29:57 pm PDT Mike Spreitzer February 27, 1987 2:54:07 pm PST Louis Monier September 24, 1987 2:51:06 pm PDT Prerequisites Cell Type IO Library IO IO Registration IO Utilities Cell Class IO Initialization Κ‘– "cedar" style˜codešœ™KšœB™BKšœ!Οk™$Kšœ*™*Kšœ,™/Kšœ+™.K™—š ˜ Kšœ$˜$KšœC˜CKšœ œ3˜@—K˜•StartOfExpansion[]šΠbn œœ˜KšœDœ œ2˜ŠKšœ˜Kšœœœ˜—head™ Kšœœœ˜šœœœœ˜K˜—Kšœœ˜)šœœ˜'K˜—Kšœœœ˜šœf˜fK˜——šœ ™ Kš Οnœœœ œœ˜6šŸœœœœ˜?K˜—šŸœœœ%œœœ œœœœ œœœ˜žšœ!˜!šœ˜K–$[ref: REF ANY, list: GList.List]šœœœ œ˜GKšœ˜K˜—šœ˜K–$[ref: REF ANY, list: GList.List]šœœœ œ˜IKšœ˜K˜——K˜K™—š Ÿ œœœ%œœ˜MKšœœ˜Kšœ œœA˜UKšœ œ˜*Kšœ ˜ Kšœ˜K˜K™—šœœ,œJœ˜₯K˜—š Ÿœœ œœœœ˜>Kšœœœ œ˜9Kšœœœœ1˜EK˜K˜—šŸœœœ œœ œœœ˜oKšœœ˜Kšœ˜Kš œ œœ œœœ˜,Kšœ œœ(˜˜>Kšœ œ˜Kšœ*˜*K˜ K˜K˜—šŸ œ˜'Kšœœœ˜Kšœ!˜!Kšœœœ<˜iJšœ˜K˜——šœ™ šŸ œœœ,œ˜MšŸœ˜*Kšœ˜šœœ˜#Kšœœœ˜9—K˜—Kšœœ˜Kšœ*˜*Kšœ œ˜*Kšœ*˜*Kšœ,˜,Kšœ˜K˜K™—š Ÿœœœ œœ%˜YKšœœ˜Kšœœ˜ Kšœ*˜*Kšœ˜Kšœ œ?˜QKšœ˜Kšœ&˜&šœ ˜Kšœœ˜K˜/Kšœ*˜*Kšœ˜—Kšœ˜K˜——šœ ™šŸ œœœEœ ˜†Kšœ˜Kšœ:œ˜WKšœ9œ˜TKšœ4˜4Kšœ˜K˜—š Ÿœœœœ,œ œ˜qKšœ>˜>Kšœ9œ˜UKšœ8œ˜RKšœ,˜,Kšœ˜Kšœ˜K˜——šœ ™ šŸ œœœ œ˜HKšœF˜FKšœ œœ/˜Hšœ œœ˜Kšœ*˜*Kšœ œœQ˜xšœ œœ˜Kšœ"˜(Kšœ$˜$Kšœ œœœ˜Kšœ ˜ Kšœ˜K˜—Kšœ:˜:Kšœœ6œœ˜GKšœ˜Kšœ0˜0Kšœ-˜-Kšœ'˜'Kšœ+˜+K˜—Kšœ˜!K˜K˜—š Ÿ œœœ œœ˜PKšœœ˜Kšœ5˜5Kšœ œœ1˜HKšœ˜K˜—š Ÿœœœ œœ7˜oKšœu˜uKšœ œ˜"Kšœœœ˜(Kšœ œ0˜Ašœ œœ˜Kšœ œ˜!Kšœœ6œœ˜GKšœX˜XKšœ0˜0Kšœ-˜-K˜—K˜K˜—šŸœœœ œ7˜dKšœ œ˜#Kšœ œœ˜#Kšœœ-˜DKšœ œP˜bKšœ*˜*K˜K˜—šŸ œœœ œ/˜UKšœœœ&˜;šœœœ˜Kšœ2˜2Kšœœ*œœ˜;Kšœ˜K˜Kšœ)˜)šœœœ˜!Kšœ(˜(Kšœ˜—K˜—Kšœ˜K˜K˜—š Ÿœœœ œœ˜]Kšœœ˜Kšœœœ˜$Kšœœ(˜5šœœœ˜Kšœ œ˜ Kšœœ˜#Kšœœ*œœ˜;Kšœ)˜)šœœœ˜!Kšœ(˜(Kšœ˜—K˜—K˜K˜—šŸœœœ œ"˜NKšœ œ˜Kšœ˜šœ1œœ˜FKšœ œœ˜(Kšœ œœ˜#Kšœ œœa˜†Kš<œ œœœ œœœ œœœœœ œœœœ œœœœ œœœ œœœœ œœœ˜υKšœ˜—Kšœ˜šœ1œœ˜FK˜šŸ œœœ˜ Jšœ˜Jšœœœ˜'K˜K˜—Kšœ œœ˜(Kšœ œœa˜†š œ œœœœ˜6Kšœœ+˜2Kšœœœœ0˜AKšœœœ+˜5Kšœœœ+˜5Kšœœ+˜2Kšœœœ2˜@Kšœœœ.˜:Kšœœ˜*—Kšœ œ1˜DKšœ˜—K˜K˜—š Ÿœœœ œœ œ˜\Kšœ œ˜!Kšœ˜šœœœ˜Kšœœ˜Kšœœœ˜ Kšœ œœ\˜šœ œœ˜Kšœœ˜šœœœ˜Kšœ*˜*Kšœ-˜-Kšœœœ˜4Kšœœœ˜4Kšœ*˜*Kšœœœ˜9Kšœœœ˜7Kšœœ&˜6—K˜—Kšœ˜"Kšœ œ˜+Kšœ˜—Kšœ˜K˜——šœ ™ šŸœ˜#Kšœ"œ˜8K˜Kšœ-˜-šœœœ˜Kšœ-˜-Kšœ0˜0Kšœ-˜-K˜%Kšœ˜—K˜K™—šŸœ˜!Kšœ"œ1˜VKšœ-˜-šœœœ˜Kšœ%œ˜FKšœ˜Kšœ1˜1Kšœ-˜-Kšœ%˜%Kšœ˜—Kšœ˜K˜K™—šŸœ˜#Kšœœ˜5Kšœ<˜Kšœœ˜šœœ˜Bšœ6œ˜>K˜Kšœ˜K˜—Kšœœœ˜Kšœ˜—Kšœ˜K˜K˜—šŸœ˜&K™—šŸœ#œ˜7K™—šŸ œ˜!K˜šŸœœ#˜9Kšœœœ˜$šœ˜Kšœ˜šœœœ˜ Kšœ˜Kšœ˜—K˜—K˜K˜—Kšœ$œ˜:Kšœ ˜ Kšœ˜Kšœ˜Kšœ#˜#Kšœ˜K™—šŸ œ˜K˜šŸœœœ!œ˜FKšœœ˜šœ œ˜Kšœœ%˜.šœœœ˜ Kšœ˜Kšœ˜—K˜—K˜K˜—Kšœ$œ"˜IKšœ ˜ Kšœ˜Kšœ!˜!Kšœ%˜%Kšœ˜Kšœ˜——™KšœQ˜QKšœU˜UKšœX˜XKšœO˜OKšœœ*˜@—K˜Kšœ˜K˜K˜—…—3”D—