DIRECTORY ConvertUnsafe USING [EqualSubStrings, SubString], FileParmOps USING [], FileParms USING [AcquireOp, ActualId, BindingOp, BindingProc, ForgetOp, Name, nullActual, Ops, ReleaseOp, SymbolSpaceRep], FileParmsPrivate USING [FileRecord, SymbolSpaceRep], Host: TYPE MachineParms USING [bitsPerAU, bitsPerChar], MimCommandUtil USING [KeyValue, PairList], MimSysOps USING [Map, MapBase, MapLength, MappedFile, UnMap], MimZones USING [permZone, RegisterForReset, tempZone], MobDefs USING [Base, FTSelf, Mob, MobBase, MobOffset, MTIndex, MTRecord, NameRecord, NameString, NullVersion, SGIndex, SGNull, VersionStamp], MobMapper USING [AlterMob], Rope USING [Concat, Equal, Find, Flatten, Length, ROPE], Symbols USING [HTIndex, MDFirst, MDIndex], SymbolSegment USING [Base, biases, bodyType, ctxType, ExtFirst, extType, FGHeader, htType, ltType, mdType, seType, ssType, STHeader, stType, treeType], SymbolTable USING [Handle], SymbolTablePrivate USING [SymbolTableBaseRep]; FileParmsImpl: MONITOR IMPORTS ConvertUnsafe, MimCommandUtil, MimSysOps, MimZones, MobMapper, Rope EXPORTS FileParms, FileParmOps, SymbolTable = { ROPE: TYPE = Rope.ROPE; MappedFile: TYPE = MimSysOps.MappedFile; Name: TYPE = FileParms.Name; ActualId: TYPE = FileParms.ActualId; BindingProc: TYPE = FileParms.BindingProc; nullActual: ActualId = FileParms.nullActual; VersionStamp: TYPE = MobDefs.VersionStamp; SymbolSpace: TYPE = REF SymbolSpaceRep; SymbolSpaceRep: PUBLIC TYPE = FileParmsPrivate.SymbolSpaceRep; FileIndex: TYPE = NAT; nullFileIndex: FileIndex = FileIndex.LAST; bytesPerUnit: NAT = Host.bitsPerAU / Host.bitsPerChar; BindingOpImpl: FileParms.BindingOp = { i: FileIndex ¬ nullFileIndex; name: ROPE ¬ FileName[formalId, defaultLocator]; IF name # NIL THEN { file: MappedFile ¬ NIL; version: MobDefs.VersionStamp; start: INT; length: INT; FOR i IN [0 .. nextFile) DO space: SymbolSpace ¬ fileTable[i].space; IF space # NIL AND Rope.Equal[name, fileTable[i].name, FALSE] THEN IF Rope.Equal[formalType, fileTable[i].type, FALSE] THEN GO TO found; ENDLOOP; file ¬ MimSysOps.Map[name].mf; IF file = NIL THEN GO TO empty; [version, start, length] ¬ ReadHeader[file, formalType]; IF version = nullActual.version THEN { MimSysOps.UnMap[file]; GO TO empty; }; i ¬ SearchCache[version]; IF i = nullFileIndex OR NOT Rope.Equal[formalId, fileTable[i].formal] THEN { space: SymbolSpace ¬ NEW[SymbolSpaceRep ¬ [ file: file, start: start, length: length]]; i ¬ NewCacheEntry[version: version, space: space, name: name, formal: formalId, type: formalType]; GO TO found; }; MimSysOps.UnMap[file]; GO TO found; EXITS found => {binder[[fileTable[i].version, fileTable[i].name]]; RETURN}; empty => {}; }; binder[nullActual]; }; AcquireOpImpl: FileParms.AcquireOp = { i: FileIndex ¬ SearchCache[actual.version]; space: SymbolSpace ¬ NIL; IF i = nullFileIndex THEN i ¬ NewCacheEntry[version: actual.version, space: NIL, name: actual.locator, formal: actual.locator, type: name]; space ¬ fileTable[i].space; IF space = NIL THEN { file: MappedFile ¬ MimSysOps.Map[fileTable[i].name].mf; IF file # NIL THEN { version: MobDefs.VersionStamp; start: INT; length: INT; [version, start, length] ¬ ReadHeader[file, fileTable[i].type]; IF version # fileTable[i].version THEN ClearCacheEntry[i] ELSE space ¬ fileTable[i].space ¬ NEW[SymbolSpaceRep ¬ [ file: file, start: start, length: length]]; }; }; RETURN [space]; }; ReleaseOpImpl: FileParms.ReleaseOp = { NULL; -- ref counts? }; ForgetOpImpl: FileParms.ForgetOp = { i: NAT ¬ 0; WHILE i < nextFile DO name: ROPE ¬ fileTable[i].name; IF fileTable[i].version = actual.version OR (name # NIL AND Rope.Equal[name, actual.locator, FALSE]) THEN { ClearCacheEntry[i]; nextFile ¬ nextFile - 1; IF i = nextFile THEN EXIT; fileTable[i] ¬ fileTable[nextFile]; fileTable[nextFile] ¬ []; LOOP; }; i ¬ i + 1; ENDLOOP; }; aList: MimCommandUtil.PairList; SetAList: PUBLIC PROC [map: MimCommandUtil.PairList] = {aList ¬ map}; ClearAList: PUBLIC PROC = {aList ¬ NIL}; Initialize: PUBLIC PROC RETURNS [FileParms.Ops] = { Finalize[]; AdjustFileTable[16]; nextFile ¬ 0; RETURN [[BindingOpImpl, AcquireOpImpl, ReleaseOpImpl, ForgetOpImpl]]; }; Finalize: PUBLIC PROC = { IF fileTable # NIL THEN { FOR i: NAT IN [0..nextFile) DO ClearCacheEntry[i] ENDLOOP; MimZones.tempZone.FREE[@fileTable]; }; Cleanup[]; }; FileName: PROC [key: Name, default: Name] RETURNS [ROPE] = { t: ROPE ¬ MimCommandUtil.KeyValue[key, aList]; IF t = NIL THEN t ¬ default; IF t = NIL THEN t ¬ key; RETURN [NormalizeFileName[t]]; }; NormalizeFileName: PROC [formal: Name] RETURNS [s: ROPE ¬ NIL] = INLINE { IF NOT Rope.Equal[formal, "$"] THEN { dotIndex: INT ¬ Rope.Find[formal, "."]; s ¬ IF dotIndex < 0 THEN Rope.Concat[formal, ".mob"] ELSE formal; }; }; FileRecord: TYPE = FileParmsPrivate.FileRecord; FileTable: TYPE = RECORD [ SEQUENCE length: FileIndex OF FileRecord ]; fileTable: REF FileTable ¬ NIL; nextFile: NAT ¬ 0; SearchCache: PROC [version: MobDefs.VersionStamp] RETURNS [FileIndex] = { FOR i: FileIndex IN [0 .. nextFile) DO IF fileTable[i].version = version THEN RETURN [i]; ENDLOOP; RETURN [nullFileIndex]; }; NewCacheEntry: PROC [version: VersionStamp, space: SymbolSpace, name: Name, formal: Name, type: Name] RETURNS [i: FileIndex] = { WHILE nextFile >= fileTable.length DO AdjustFileTable[fileTable.length + 16] ENDLOOP; i ¬ nextFile; fileTable[i] ¬ [version: version, space: space, name: name, formal: formal, type: type]; nextFile ¬ nextFile + 1; }; AdjustFileTable: PROC [newSize: NAT] = { newTable: REF FileTable ¬ NIL; oldSize: NAT = IF fileTable = NIL THEN 0 ELSE fileTable.length; IF newSize = 0 THEN newTable ¬ NIL ELSE { i: FileIndex; newTable ¬ MimZones.tempZone.NEW[FileTable[newSize]]; FOR i IN [0..MIN[oldSize, newSize]) DO newTable[i] ¬ fileTable[i] ENDLOOP; FOR i IN [oldSize..newSize) DO newTable[i] ¬ [version: nullActual.version] ENDLOOP; }; MimZones.tempZone.FREE[@fileTable]; fileTable ¬ newTable; }; ClearCacheEntry: PROC [i: FileIndex] = { space: SymbolSpace ¬ fileTable[i].space; fileTable[i] ¬ []; IF space # NIL THEN { file: MappedFile = space.file; IF file # NIL THEN MimSysOps.UnMap[file]; }; }; ReadHeader: PROC [file: MappedFile, formalId: Name] RETURNS [version: MobDefs.VersionStamp, start: INT, length: INT] = { IF file # NIL THEN { MakeBase: PROC [p: LONG POINTER, units: INT] RETURNS [MobDefs.Base] = INLINE { RETURN [LOOPHOLE[p+units, MobDefs.Base]]; }; mob: MobDefs.MobBase = LOOPHOLE[MimSysOps.MapBase[file]]; IF mob # NIL AND mob.nConfigs = 0 THEN { limit: CARD = MimSysOps.MapLength[file] / BYTES[UNIT]; IF MobMapper.AlterMob[mob, LOOPHOLE[mob], limit] # badVersion THEN { nString: MobDefs.NameString ¬ LOOPHOLE[mob + mob.ssOffset.units]; d: ConvertUnsafe.SubString ¬ [base: LOOPHOLE[nString], offset: 0, length: 0]; typeId: ConvertUnsafe.SubString ¬ [ base: LOOPHOLE[formalId ¬ Rope.Flatten[formalId]], offset: 0, length: Rope.Length[formalId]]; ftb: MobDefs.Base ¬ MakeBase[mob, mob.ftOffset.units]; mtb: MobDefs.Base ¬ MakeBase[mob, mob.mtOffset.units]; sgb: MobDefs.Base ¬ MakeBase[mob, mob.sgOffset.units]; mti: MobDefs.MTIndex ¬ MobDefs.MTIndex.FIRST; sSeg: MobDefs.SGIndex; UNTIL mti = mob.mtLimit DO name: MobDefs.NameRecord ¬ mtb[mti].name; d.length ¬ nString[name].ORD; d.offset ¬ name+1; IF ConvertUnsafe.EqualSubStrings[typeId, d] THEN EXIT; mti ¬ mti + MobDefs.MTRecord.SIZE; REPEAT FINISHED => IF mob.nModules = 1 THEN mti ¬ MobDefs.MTIndex.FIRST ELSE GO TO badFile; ENDLOOP; version ¬ IF mtb[mti].file = MobDefs.FTSelf THEN mob.version ELSE ftb[mtb[mti].file].version; sSeg ¬ mtb[mti].sseg; IF sSeg = MobDefs.SGNull OR sgb[sSeg].units.units = 0 THEN GO TO badFile; start ¬ LOOPHOLE[sgb[sSeg].base]; length ¬ LOOPHOLE[sgb[sSeg].units]; RETURN; }; }; EXITS badFile => {} }; version ¬ MobDefs.NullVersion; start ¬ 0; length ¬ 0; }; STB: TYPE = REF SymbolTableBaseRep; SymbolTableBaseRep: PUBLIC TYPE = SymbolTablePrivate.SymbolTableBaseRep; Handle: TYPE = SymbolTable.Handle; STBMissing: PUBLIC ERROR [Handle] = CODE; IllegalSTB: PUBLIC ERROR [base: STB] = CODE; Cleanup: PUBLIC ENTRY PROC = { ENABLE UNWIND => NULL; WHILE header # NIL DO node: CachePointer ¬ header; header ¬ node.next; ReleaseCacheEntry[node]; ENDLOOP; WHILE free # NIL DO node: CachePointer ¬ free; free ¬ free.next; ReleaseCacheEntry[node]; ENDLOOP; }; AcquireSTB: PUBLIC ENTRY PROC [handle: Handle] RETURNS [base: STB] = { ENABLE UNWIND => NULL; node: CachePointer ¬ MakeCacheEntry[handle]; IF freeTables = NIL THEN {base ¬ MimZones.permZone.NEW[SymbolTableBaseRep]; Bump[@stats.nNew]} ELSE {base ¬ freeTables; freeTables ¬ freeTables.link}; base.link ¬ busyTables; busyTables ¬ base; InstallTable[base, node]; Bump[@stats.nAcquire]; }; ReleaseSTB: PUBLIC ENTRY PROC [base: STB] = { ENABLE UNWIND => {NULL}; cacheNode: CachePointer = LOOPHOLE[base.cacheInfo]; FOR node: CachePointer ¬ header, node.next UNTIL node = NIL DO IF node = cacheNode THEN { prev: STB ¬ NIL; FOR table: STB ¬ busyTables, table.link UNTIL table = NIL DO IF table = base THEN { IF prev # NIL THEN prev.link ¬ table.link ELSE busyTables ¬ table.link; FreeCacheEntry[node]; base.link ¬ freeTables; freeTables ¬ base; Bump[@stats.nRelease]; RETURN; }; prev ¬ table; ENDLOOP; RETURN WITH ERROR IllegalSTB[base]; }; ENDLOOP; RETURN WITH ERROR IllegalSTB[base]; }; LockedSTB: PUBLIC ERROR [handle: SymbolTable.Handle, nLocks: NAT] = CODE; ForgetSTB: PUBLIC ENTRY PROC [handle: SymbolTable.Handle] = { ENABLE UNWIND => {NULL}; space: SymbolSpace ¬ NARROW[handle.space]; nLocks: INT ¬ 0; prev: CachePointer ¬ NIL; node: CachePointer ¬ header; WHILE node # NIL DO next: CachePointer ¬ node.next; IF space = node.space THEN { IF node.refCount # 0 THEN nLocks ¬ nLocks + node.refCount ELSE { IF prev = NIL THEN header ¬ next ELSE prev.next ¬ header; ReleaseCacheEntry[node]; node ¬ prev; }; }; prev ¬ node; node ¬ next; ENDLOOP; node ¬ free; prev ¬ NIL; WHILE node # NIL DO next: CachePointer ¬ node.next; IF space = node.space THEN { IF node.refCount # 0 THEN nLocks ¬ nLocks + node.refCount ELSE { IF prev = NIL THEN free ¬ next ELSE prev.next ¬ free; ReleaseCacheEntry[node]; node ¬ prev; }; }; prev ¬ node; node ¬ next; ENDLOOP; IF nLocks # 0 THEN RETURN WITH ERROR LockedSTB[handle, nLocks]; }; STBToHandle: PUBLIC ENTRY PROC [base: STB] RETURNS [SymbolTable.Handle] = { ENABLE UNWIND => NULL; cacheNode: CachePointer = LOOPHOLE[base.cacheInfo]; RETURN [[cacheNode.space, FALSE]]; }; busyTables: STB ¬ NIL; freeTables: STB ¬ NIL; cachePageLimit: INT ¬ 0; -- number of pages for symbol tables STBCacheSize: PUBLIC PROC RETURNS [pages: CARDINAL] = { RETURN [cachePageLimit]; }; SetSTBCacheSize: PUBLIC ENTRY PROC [pages: CARDINAL] = { ENABLE UNWIND => NULL; cachePageLimit ¬ pages; }; takingStatistics: BOOL = TRUE; Count: TYPE = INT ¬ 0; stats: RECORD [nAcquire, nNew, nRelease: Count] ¬ []; Bump: PROC [p: POINTER TO Count, delta: Count ¬ 1] = INLINE { IF takingStatistics THEN p­ ¬ p­ + delta; }; CacheNode: TYPE = RECORD [ next: CachePointer ¬ NIL, space: SymbolSpace ¬ NIL, refCount: INT ¬ 0, nUses: INT ¬ 0]; CachePointer: TYPE = REF CacheNode; header: CachePointer ¬ NIL; free: CachePointer ¬ NIL; unused: CachePointer ¬ NIL; nNodes: NAT ¬ 0; mappedPages: INT ¬ 0; MakeCacheEntry: INTERNAL PROC [handle: Handle] RETURNS [node: CachePointer ¬ NIL] = { space: SymbolSpace ¬ NARROW[handle.space]; prev: CachePointer ¬ NIL; node ¬ header; WHILE node # NIL DO next: CachePointer ¬ node.next; IF node.space = space THEN GO TO found; node ¬ next; ENDLOOP; node ¬ free; WHILE node # NIL DO next: CachePointer ¬ node.next; IF node.space = space THEN { IF prev = NIL THEN free ¬ next ELSE prev.next ¬ NIL; GO TO first; }; prev ¬ node; node ¬ next; ENDLOOP; node ¬ GetEmptyNode[]; node.space ¬ space; GO TO first; EXITS first => { node.next ¬ header; header ¬ node; node.refCount ¬ 1; node.nUses ¬ node.nUses + 1; }; found => { node.refCount ¬ node.refCount+1; node.nUses ¬ node.nUses + 1; }; }; FreeCacheEntry: INTERNAL PROC [node: CachePointer] = { IF (node.refCount ¬ node.refCount-1) = 0 THEN { each: CachePointer ¬ header; lag: CachePointer ¬ NIL; WHILE each # NIL DO next: CachePointer ¬ each.next; IF each = node THEN { IF lag = NIL THEN header ¬ next ELSE lag.next ¬ next; node.next ¬ free; free ¬ node; RETURN; }; lag ¬ each; each ¬ next; ENDLOOP; ERROR; }; }; ReleaseCacheEntry: INTERNAL PROC [node: CachePointer] = { space: SymbolSpace ¬ node.space; IF space # NIL THEN node.space ¬ NIL; node.refCount ¬ node.nUses ¬ 0; node.next ¬ unused; unused ¬ node; }; GetEmptyNode: INTERNAL PROC RETURNS [node: CachePointer] = { IF unused = NIL THEN {node ¬ MimZones.permZone.NEW[CacheNode]; nNodes ¬ nNodes + 1} ELSE {node ¬ unused; unused ¬ unused.next}; node.refCount ¬ node.nUses ¬ 0; node.next ¬ NIL; }; InstallTable: INTERNAL PROC [base: STB, node: CachePointer] = { b: LONG POINTER = MimSysOps.MapBase[node.space.file]+node.space.start; tB: SymbolSegment.Base = LOOPHOLE[b]; p: LONG POINTER TO SymbolSegment.STHeader = b; base.cacheInfo ¬ LOOPHOLE[node]; base.hashVec ¬ b+p.hvBlock.offset; base.htb ¬ tB + p.htBlock.offset - SymbolSegment.biases[SymbolSegment.htType]; base.ssb ¬ b + p.ssBlock.offset - SymbolSegment.biases[SymbolSegment.ssType]; base.seb ¬ tB + p.seBlock.offset - SymbolSegment.biases[SymbolSegment.seType]; base.ctxb ¬ tB + p.ctxBlock.offset - SymbolSegment.biases[SymbolSegment.ctxType]; base.mdb ¬ tB + p.mdBlock.offset - SymbolSegment.biases[SymbolSegment.mdType]; base.bb ¬ tB + p.bodyBlock.offset - SymbolSegment.biases[SymbolSegment.bodyType]; base.tb ¬ tB + p.treeBlock.offset - SymbolSegment.biases[SymbolSegment.treeType]; base.ltb ¬ tB + p.litBlock.offset - SymbolSegment.biases[SymbolSegment.ltType]; base.stb ¬ tB + p.sLitBlock.offset - SymbolSegment.biases[SymbolSegment.stType]; base.extb ¬ tB + p.extBlock.offset - SymbolSegment.biases[SymbolSegment.extType]; base.mdLimit ¬ Symbols.MDFirst + p.mdBlock.size; base.extLimit ¬ SymbolSegment.ExtFirst + p.extBlock.size; base.mainCtx ¬ p.outerCtx; base.stHandle ¬ p; IF p.fgRelBase = 0 OR p.fgCount = 0 THEN { base.sourceFile ¬ NIL; base.fgTable ¬ NIL; } ELSE { q: LONG POINTER TO SymbolSegment.FGHeader = LOOPHOLE[b + p.fgRelBase]; source: LONG STRING = LOOPHOLE[q + SIZE[SymbolSegment.FGHeader[0]] - SIZE[StringBody[0]]]; base.sourceFile ¬ source; base.fgTable ¬ DESCRIPTOR[q + q.offset, q.length]; }; }; bytesPerAU: NAT = BYTES[UNIT]; MimZones.RegisterForReset[Finalize]; }. Τ FileParmsImpl.mesa Copyright Σ 1985, 1986, 1987, 1988, 1991 by Xerox Corporation. All rights reserved. Satterthwaite, June 3, 1986 11:13:40 am PDT Russ Atkinson (RRA) June 21, 1989 11:45:33 am PDT JKF August 16, 1988 10:37:20 am PDT Willie-s, September 24, 1991 1:37 pm PDT Exported to FileParms primary operations for read-only access Mapping will result in the same file if already mapped. Usage counts are maintaed via MimSysOps, so play the game straight. If not found, then not mapped, so flake out now. The file has been mapped, but it does not have a valid version. Now that we have the version, check it against the cache. This is really a new version (or at least a new formal name), so it needs to stay mapped. This is an old version, already in the cache, so unmap the new file to keep the usage counts OK. command line arguments initialization/finalization interpretation of file names (Pilot PreCascade conventions) low-level file manipulation and cache management file table management file setup Stuff formerly in SymbolCacheImpl public interface statistics internal cache management Cache Organization There are three lists: header => cache nodes that are being used free => cache nodes with spaces but not in use unused => cache nodes without spaces Try to find a busy cache node with the proper file and span. Try to find a free cache node with the proper file and span. At this point there is no free cache node with the proper file and span. symbol table setup Κ†–(cedarcode) style•NewlineDelimiter ™headšœ™Icodešœ ΟeœI™TLšΟy+™+L™1L™#L™(—˜šΟk ˜ LšœŸœ˜1Lšœ Ÿœ˜Lšœ Ÿœk˜zLšœŸœ˜4LšœŸœŸœ˜7LšœŸœ˜*Lšœ Ÿœ.˜=Lšœ Ÿœ(˜6LšœŸœ€˜Lšœ Ÿœ ˜LšœŸœ(Ÿœ˜8LšœŸœ˜*LšœŸœ„˜—Lšœ Ÿœ ˜LšœŸœ˜.——šΟn œŸ˜LšŸœD˜KLšŸœ(˜/L˜LšŸœŸœŸœ˜L˜Lšœ Ÿœ˜(L˜LšœŸœ˜Lšœ Ÿœ˜$Lšœ Ÿœ˜*L˜,LšœŸœ˜*L˜Lšœ ŸœŸœ˜'šœŸ œ#˜>Lšœ™—L˜Lšœ ŸœŸœ˜Lšœ%Ÿœ˜*L˜LšœŸœ%˜6L˜—Lšœ'™'˜š  œ˜&Lšœ˜LšœŸœ&˜0šŸœŸœŸœ˜LšœŸœ˜Lšœ˜LšœŸœ˜ LšœŸœ˜ šŸœŸœŸ˜Lšœ(˜(š Ÿœ ŸœŸœ%ŸœŸ˜BLš Ÿœ+ŸœŸœŸœŸœ˜E—LšŸœ˜—šœ˜Lšœ|™|—š ŸœŸœŸœŸœŸœ˜L™0—Lšœ8˜8šŸœŸœ˜&L™?Lšœ˜LšŸœŸœ˜ L˜—šœ˜L™9—šŸœŸœŸœ+Ÿœ˜LL™YšœŸœ˜+Lšœ+˜+—Lšœb˜bLšŸœŸœ˜ L˜—Lšœ]Ÿœ™`Lšœ˜LšŸœŸœ˜ šŸ˜Lšœ=Ÿœ˜EL˜ —L˜—Lšœ˜Lšœ˜L˜—š  œ˜&L˜+LšœŸœ˜šŸœŸ˜Lšœ2Ÿœ<˜q—Lšœ˜šŸœ ŸœŸœ˜Lšœ7˜7šŸœŸœŸœ˜Lšœ˜LšœŸœ˜ LšœŸœ˜ Lšœ?˜?šŸœ˜!LšŸœ˜šŸ˜šœŸœ˜3Lšœ+˜+———L˜—L˜—LšŸœ ˜šœ˜L˜——š  œ˜&LšŸœΟc˜Lšœ˜—L˜š  œ˜$LšœŸœ˜ šŸœŸ˜LšœŸœ˜š Ÿœ'ŸœŸœŸœ"ŸœŸœ˜kL˜L˜LšŸœŸœŸœ˜L˜#L˜LšŸœ˜Lšœ˜—L˜ LšŸœ˜—Lšœ˜L˜——šœ™L˜Lšœ˜L˜Lš œŸœŸœ0˜EL˜Lš  œŸœŸœ Ÿœ˜(L˜—Lšœ™˜š  œŸœŸœŸœ˜3Lšœ ˜ Lšœ˜L˜ LšŸœ?˜ELšœ˜L˜—š œŸœŸœ˜šŸœ ŸœŸœ˜Lš ŸœŸœŸœŸœŸœ˜:LšœŸœ ˜#L˜—Lšœ ˜ Lšœ˜L˜——Lšœ;™;˜š œŸœŸœŸœ˜šŸœŸœ˜LšœŸœŸœ˜š ŸœŸœŸœ ŸœŸ˜<šŸœŸœ˜LšŸœŸœŸœŸœ˜GLšœ˜L˜L˜L˜LšŸœ˜Lšœ˜—L˜ LšŸœ˜—LšŸœŸœŸœ˜#L˜—LšŸœ˜—LšŸœŸœŸœ˜#L˜L˜—Lš   œŸœŸœ&ŸœŸœ˜IL˜š  œŸœŸœŸœ!˜=LšŸœŸœŸœ˜LšœŸœ˜*LšœŸœ˜LšœŸœ˜Lšœ˜šŸœŸœŸ˜Lšœ˜šŸœŸœ˜šŸœ˜LšŸœ ˜$šŸœ˜LšŸœŸœŸœŸœ˜9Lšœ˜L˜ L˜——Lšœ˜—L˜ L˜ LšŸœ˜—L˜ LšœŸœ˜ šŸœŸœŸ˜Lšœ˜šŸœŸœ˜šŸœ˜LšŸœ ˜$šŸœ˜LšŸœŸœŸœ Ÿœ˜5Lšœ˜L˜ L˜——Lšœ˜—L˜ L˜ LšŸœ˜—Lš Ÿœ ŸœŸœŸœŸœ˜?Lšœ˜L˜—š   œŸœŸœŸœŸœŸœ˜KLšŸœŸœŸœ˜LšœŸœ˜3LšŸœŸœ˜"Lšœ˜L˜—Lšœ ŸœŸœ˜Lšœ ŸœŸœ˜L˜LšœŸœ‘$˜>L˜š   œŸœŸœŸœ Ÿœ˜7LšŸœ˜Lšœ˜—L˜š  œŸœŸœŸœ Ÿœ˜8LšŸœŸœŸœ˜L˜L˜L˜——Lšœ ™ ˜LšœŸœŸœ˜LšœŸœŸœ˜LšœŸœ(˜5L˜š  œŸœŸœŸœŸœ˜=LšŸœŸœ˜)Lšœ˜L˜——Lšœ™˜šœ ŸœŸœ˜LšœŸœ˜LšœŸœ˜Lšœ Ÿœ˜LšœŸœ˜L˜—LšœŸœŸœ ˜#L˜LšœŸœ˜LšœŸœ˜LšœŸœ˜LšœŸœ˜Lšœ Ÿœ˜L˜—šœ™L˜™L™)L™.L™$L™—š  œŸœŸœŸœŸœ˜ULšœŸœ˜*LšœŸœ˜Lšœ˜L™<šŸœŸœŸ˜Lšœ˜LšŸœŸœŸœŸœ˜'L˜ LšŸœ˜—L™