DIRECTORY AbbrevExpand USING [Event], Ascii USING [Lower], Atom USING [GetPName, MakeAtom], Basics USING [BITSHIFT, BITXOR], BasicTime USING [GMT, nullGMT], Convert USING [RopeFromInt], FS USING [Error, FileInfo], IO USING [Close, GetIndex, GetRefAny, RIS, STREAM], MessageWindow USING [Append, Blink], PrincOpsUtils USING [], Process USING [GetCurrent], RefTab USING [Create, Fetch, Pairs, Ref, Store], Rope USING [Cat, Concat, Equal, ROPE, Size, Translate, TranslatorType], RopeEdit USING [AlphaNumericChar, BlankChar, PunctuationChar, Substr, UpperCase], RopeReader USING [Backwards, CompareSubstrs, FreeRopeReader, Get, GetIndex, GetRopeReader, Peek, PeekBackwards, ReadOffEnd, Ref, SetPosition], RuntimeError USING [UNCAUGHT], TextLooks USING [Looks, noLooks], Tioga USING [AddLooks, AllCaps, ChangeFormat, Copy, DeleteText, FetchLooks, FirstChild, FromFile, LastWithin, MakeNodeLoc, MakeNodeSpan, MoveText, Next, Node, NodeFormat, ReplaceText, Root, Size, Span, World], UserProfile USING [ListOfTokens]; AbbrevExpandImpl: CEDAR MONITOR IMPORTS Ascii, Atom, Convert, EditSpan, FS, Basics, IO, MessageWindow, RefTab, Rope, RopeEdit, RopeReader, Process, RuntimeError, Tioga, UserProfile EXPORTS AbbrevExpand = BEGIN Node: TYPE = Tioga.Node; maxLen: INT = LAST[INT]; ROPE: TYPE = Rope.ROPE; dictTable: RefTab.Ref _ RefTab.Create[mod: 5]; Entry: TYPE = REF EntryRec; EntryRec: TYPE = RECORD [ next: Entry, hash: CARDINAL, keyRope: ROPE, node: Node, commands: LIST OF REF ANY ]; LoadInternal: PROC [dict: ATOM, fileID: FileID, start: INT _ 0, len: INT _ maxLen] RETURNS [ok: BOOL _ FALSE, count: NAT _ 0] ~ { rdr: RopeReader.Ref ~ RopeReader.GetRopeReader[]; root: Node _ NIL; Locked: PROC ~ { [] _ RefTab.Store[dictTable, dict, NoDictAtom]; -- clears the dictionary FOR node: Node _ Tioga.FirstChild[root], Tioga.Next[node] UNTIL node=NIL DO IF node=NIL OR node.rope=NIL OR node.comment THEN LOOP; AddToDict[root, node, dict, rdr]; count _ count+1; ENDLOOP; [] _ RefTab.Store[fileForAbbr, dict, fileID]; }; Empty: PROC ~ { [] _ RefTab.Store[dictTable, dict, NoDictAtom]; -- clears the dictionary }; IF fileID = NIL THEN {DoLocked[Empty]; RETURN}; MessageWindow.Append[Rope.Cat["New ", fileID.name, " . . . "], TRUE]; root _ Tioga.FromFile[fileID.name, start, len ! FS.Error => { MessageWindow.Append[error.explanation, FALSE]; MessageWindow.Blink[]; CONTINUE; }]; IF root = NIL THEN RETURN; DoLocked[Locked]; MessageWindow.Append[Convert.RopeFromInt[count], FALSE]; MessageWindow.Append[" entries ", FALSE]; RopeReader.FreeRopeReader[rdr]; ok _ TRUE; }; Load: PUBLIC PROC [fileName, dictName: ROPE, start: INT _ 0, len: INT _ maxLen] RETURNS [count: NAT _ 0] = { dict: ATOM = GetDictAtom[dictName]; [count: count] _ LoadInternal[dict, GetFileID[dict], start, len]; }; AddToDict: ENTRY PROC [root: Node, node: Node, dictAtom: ATOM, rdr: RopeReader.Ref _ NIL] = { ENABLE UNWIND => NULL; dict: Entry _ GetDict[dictAtom]; keyRope: ROPE _ node.rope; keyStart: INT = 0; hash: CARDINAL; entry: Entry; keyLen: NAT; freeRdr: BOOL _ FALSE; resultStart: INT; commands: LIST OF REF ANY; IF rdr=NIL THEN { rdr _ RopeReader.GetRopeReader[]; freeRdr _ TRUE }; keyLen _ FindKeyLen[keyRope,rdr]; IF keyLen=0 THEN { IF freeRdr THEN RopeReader.FreeRopeReader[rdr]; RETURN }; -- not an entry hash _ KeyHash[keyRope,keyStart,keyLen,rdr]; entry _ LookupInternal[dict,hash,keyRope,keyStart,keyLen,rdr]; IF entry = NIL THEN { -- add new entry entry _ NEW[EntryRec]; IF dict # NIL THEN { entry.next _ dict.next; dict.next _ entry } ELSE [] _ RefTab.Store[dictTable,dictAtom,entry] }; entry.hash _ hash; entry.keyRope _ RopeEdit.Substr[keyRope,keyStart,keyLen]; entry.node _ node; [resultStart,commands] _ ParseCommands[keyRope,keyStart+keyLen,rdr]; entry.commands _ commands; Tioga.DeleteText[root,node,0,resultStart]; IF freeRdr THEN RopeReader.FreeRopeReader[rdr]; }; ParseCommands: PROC [keyRope: ROPE, start: INT, rdr: RopeReader.Ref] RETURNS [end: INT, commands: LIST OF REF ANY] = { OPEN RopeReader; ENABLE ReadOffEnd => GOTO Bad; blank: BOOL; SetPosition[rdr, keyRope, start]; IF RopeEdit.BlankChar[Peek[rdr]] THEN { blank _ TRUE; [] _ Get[rdr] } ELSE blank _ FALSE; IF Peek[rdr] = '( THEN { -- parse command list h: IO.STREAM _ IO.RIS[RopeEdit.Substr[keyRope,start _ GetIndex[rdr]]]; commands _ NARROW[IO.GetRefAny[h]]; start _ start+IO.GetIndex[h]; IO.Close[h]; SetPosition[rdr, keyRope, start]; IF RopeEdit.BlankChar[Peek[rdr]] THEN { blank _ TRUE; [] _ Get[rdr] } ELSE blank _ FALSE; }; IF Peek[rdr] = '= THEN { -- check for blank [] _ Get[rdr]; IF blank AND Peek[rdr] = ' THEN [] _ Get[rdr]; -- skip blank after = if there was one before end _ GetIndex[rdr]; } ELSE end _ Rope.Size[keyRope]; EXITS Bad => RETURN [Rope.Size[keyRope],commands]; }; Clear: PUBLIC ENTRY PROC [dictName: ROPE] = { ENABLE UNWIND => NULL; [] _ RefTab.Store[dictTable, GetDictAtom[dictName], NIL]; }; IllFormedDef: PUBLIC ERROR = CODE; GetDictAtom: PROC [dictName: ROPE] RETURNS [dict: ATOM] = { ForceLower: Rope.TranslatorType = { RETURN [Ascii.Lower[old]] }; RETURN [Atom.MakeAtom[Rope.Translate[base~dictName, translator~ForceLower]]]; }; Expand: PUBLIC PROC [world: World, keyNode: Node, keyEnd: INT, dict: ROPE] RETURNS [foundIt: BOOL, keyDeterminesDict: BOOL, keyStart, keyLen, resultLen: INT, commands: LIST OF REF ANY] = { FindEntry: PROC [dictAtom: ATOM] RETURNS [BOOL] = { dict: Entry _ GetDict[dictAtom]; IF dict#NIL THEN entry _ Lookup[dict,hash,keyRope,keyStart,kLen,rdr]; RETURN [entry # NIL]; }; rdr: RopeReader.Ref; dictAtom: ATOM = GetDictAtom[dict]; keyRope: ROPE _ keyNode.rope; kLen: NAT; kStart: INT; hash: CARDINAL; entry: Entry; foundIt _ keyDeterminesDict _ FALSE; resultLen _ 0; rdr _ RopeReader.GetRopeReader[]; keyEnd _ MAX[0,MIN[keyEnd,Rope.Size[keyRope]]]; kStart _ keyStart _ FindKeyStart[keyRope,keyEnd,rdr]; kLen _ keyLen _ keyEnd-keyStart; hash _ KeyHash[keyRope,keyStart,keyEnd,rdr]; RopeReader.SetPosition[rdr,keyRope,keyStart]; IF keyStart > 0 AND RopeReader.Backwards[rdr]='. THEN { -- try to get the dictName from the key nameStart: INT _ FindKeyStart[keyRope,keyStart-1,rdr]; IF nameStart < keyStart-1 THEN { dictName: ATOM _ Atom.MakeAtom[RopeEdit.Substr[keyRope,nameStart,keyStart-1-nameStart]]; [] _ FindEntry[dictName]; keyDeterminesDict _ TRUE; keyLen _ keyEnd - (keyStart _ nameStart); }; }; IF entry = NIL THEN [] _ FindEntry[dictAtom]; IF entry = NIL THEN { RopeReader.FreeRopeReader[rdr]; RETURN }; { -- do it CheckCaps: PROC = { allcaps _ FALSE; RopeReader.SetPosition[rdr,keyRope,kStart]; initialcap _ RopeReader.Peek[rdr] IN ['A..'Z]; FOR i: INT IN [0..kLen) DO IF RopeReader.Get[rdr] NOT IN ['A..'Z] THEN RETURN; ENDLOOP; allcaps _ TRUE; }; node: Node = entry.node; root: Node = Tioga.Root[node]; keyRoot: Node = Tioga.Root[keyNode]; nodeRope: ROPE _ node.rope; looks: TextLooks.Looks _ Tioga.FetchLooks[keyNode,kStart]; allcaps, initialcap: BOOL _ FALSE; format: ATOM; child: Node _ Tioga.FirstChild[node]; textLen: INT _ Rope.Size[nodeRope]; CheckCaps; RopeReader.FreeRopeReader[rdr]; [,resultLen] _ Tioga.ReplaceText[world: world, destRoot: keyRoot, sourceRoot: root, dest: [keyNode, keyStart, keyLen], source: [node, 0, textLen]]; IF resultLen # textLen THEN ERROR; IF looks # TextLooks.noLooks THEN Tioga.ChangeTextLooks[world: world, root: keyRoot, text: [keyNode, keyStart, textLen], remove: noLooks, add: looks]; IF allcaps THEN Tioga.ChangeTextCaps[world: world, root: keyRoot, text: [keyNode, keyStart, textLen], how: allCaps] ELSE IF initialcap AND textLen > 0 THEN -- make first letter uppercase Tioga.ChangeTextCaps[world: world, root: keyRoot, text: [keyNode, keyStart, 1], how: allCaps]; SELECT format _ Tioga.NodeFormat[node] FROM NIL, Tioga.NodeFormat[keyNode] => NULL; ENDCASE => Tioga.SetFormat[world: world, node: keyNode, formatName: format, root: keyRoot]; IF child # NIL THEN { -- insert as children of keyNode new: Tioga.Span _ EditSpan.Copy[world: world, destRoot: keyRoot, sourceRoot: root, dest: Tioga.MakeNodeLoc[keyNode], source: Tioga.MakeNodeSpan[child, Tioga.LastWithin[node]], where: after, nesting: IF textLen=0 THEN 0 ELSE 1]; last: Node _ new.end.node; IF last # NIL AND keyStart+textLen < Tioga.Size[keyNode] THEN -- move text after key to end of last new node [] _ Tioga.MoveText[world: world, destRoot: keyRoot, sourceRoot: keyRoot, dest: [last, maxLen], source: [keyNode, keyStart+textLen, maxLen]]; }; foundIt _ TRUE; commands _ entry.commands; }; }; KeyHash: PROC [keyRope: ROPE, keyStart, keyEnd: INT, rdr: RopeReader.Ref] RETURNS [h: CARDINAL] = { len: NAT _ keyEnd-keyStart; RopeReader.SetPosition[rdr,keyRope,keyStart]; h _ 0; FOR i: NAT IN [1..MIN[3,len]) DO -- use the first 3 characters h _ Basics.BITXOR[Basics.BITSHIFT[h,2], RopeEdit.UpperCase[RopeReader.Get[rdr]]-0C]; ENDLOOP; IF len >= 2 THEN { -- use the last 2 characters RopeReader.SetPosition[rdr,keyRope,keyEnd]; FOR i: NAT IN [len-2..len) DO h _ Basics.BITXOR[Basics.BITSHIFT[h,2], RopeEdit.UpperCase[RopeReader.Backwards[rdr]]-0C]; ENDLOOP; }; }; Lookup: ENTRY PROC [dict: Entry, hash: CARDINAL, keyRope: ROPE, keyStart: INT, keyLen: NAT, rdr1, rdr2: RopeReader.Ref _ NIL] RETURNS [entry: Entry] = { ENABLE UNWIND => NULL; RETURN [LookupInternal[dict,hash,keyRope,keyStart,keyLen,rdr1,rdr2]]; }; LookupInternal: PROC [dict: Entry, hash: CARDINAL, keyRope: ROPE, keyStart: INT, keyLen: NAT, rdr1, rdr2: RopeReader.Ref _ NIL] RETURNS [entry: Entry] = { FreeReaders: PROC = { IF free1 THEN RopeReader.FreeRopeReader[rdr1]; IF free2 THEN RopeReader.FreeRopeReader[rdr2]; }; free1, free2: BOOL _ FALSE; pred: Entry _ NIL; IF keyLen=0 THEN RETURN [NIL]; IF rdr1=NIL THEN { free1 _ TRUE; rdr1 _ RopeReader.GetRopeReader[] }; IF rdr2=NIL THEN { free2 _ TRUE; rdr2 _ RopeReader.GetRopeReader[] }; entry _ NIL; -- will stay nil if don't find key in dict FOR e: Entry _ dict, e.next UNTIL e=NIL DO IF e.hash = hash AND Rope.Size[e.keyRope] = keyLen THEN { -- check the ropes IF RopeReader.CompareSubstrs[keyRope,e.keyRope, keyStart,keyLen,0,keyLen,rdr1,rdr2,FALSE] = equal THEN { IF pred # NIL THEN { -- move entry to front pred.next _ e.next; e.next _ dict.next; dict.next _ e; }; entry _ e; EXIT; }; }; pred _ e; ENDLOOP; FreeReaders[]; }; FindKeyLen: PROC [keyRope: ROPE, rdr: RopeReader.Ref] RETURNS [len: NAT] = { size: INT _ Rope.Size[keyRope]; IF size=0 THEN RETURN [0]; RopeReader.SetPosition[rdr,keyRope,0]; IF RopeEdit.PunctuationChar[RopeReader.Peek[rdr]] THEN RETURN [1]; len _ 0; WHILE len0 AND RopeEdit.AlphaNumericChar[RopeReader.Backwards[rdr]] DO start _ start-1; ENDLOOP; }; NoDictAtom: ATOM = $NoDictionaryAvailable; GetDict: PROC [dictName: ATOM] RETURNS [entry: Entry] = { prop: REF ANY; prop _ RefTab.Fetch[dictTable, dictName].val; WITH prop SELECT FROM x: Entry => RETURN [x]; x: ATOM => IF x = NoDictAtom THEN RETURN [NIL] ELSE ERROR; ENDCASE => { -- try to load it ok: BOOL _ FALSE; [ok: ok] _ LoadInternal[dictName, GetFileID[dictName] ! RuntimeError.UNCAUGHT => CONTINUE]; IF ok THEN RETURN [GetDict[dictName]] ELSE RETURN [NIL]; }; }; defaultSearch: LIST OF ROPE _ LIST["[]<>Commands>", "[]<>"]; FileID: TYPE ~ REF FileIDRep; FileIDRep: TYPE ~ RECORD [name: ROPE _ NIL, time: BasicTime.GMT _ BasicTime.nullGMT]; Same: PROC [a, b: FileID] RETURNS [BOOL] ~ { RETURN [a.time = b.time AND Rope.Equal[a.name, b.name, FALSE]] }; GetFileID: PROC [shortName: ATOM] RETURNS [FileID] ~ { dirs: LIST OF ROPE _ UserProfile.ListOfTokens["Tioga.StyleSearchRules", defaultSearch]; name: ROPE ~ Rope.Concat[Atom.GetPName[shortName], ".abbreviations"]; fileName: ROPE _ NIL; created: BasicTime.GMT _ BasicTime.nullGMT; WHILE fileName = NIL AND dirs # NIL DO [fullFName: fileName, created: created] _ FS.FileInfo[name: name, wDir: dirs.first ! FS.Error => CONTINUE]; dirs _ dirs.rest; ENDLOOP; IF fileName = NIL THEN RETURN [NIL]; RETURN [NEW[FileIDRep _ [fileName, created]]]; }; abbrLockProcess: UNSAFE PROCESS _ NIL; abbrLockCount: CARDINAL _ 0; abbrLockFree: CONDITION; DoLocked: PUBLIC PROC [action: PROC] ~ { me: UNSAFE PROCESS ~ Process.GetCurrent[]; Lock: ENTRY PROC ~ { UNTIL abbrLockProcess = me OR abbrLockCount = 0 DO WAIT abbrLockFree ENDLOOP; abbrLockProcess _ me; abbrLockCount _ abbrLockCount + 1; }; Unlock: ENTRY PROC ~ { abbrLockCount _ abbrLockCount - 1; IF abbrLockCount = 0 THEN {abbrLockProcess _ NIL; NOTIFY abbrLockFree}; }; Lock[]; action[ ! UNWIND => Unlock[]]; Unlock[]; }; fileForAbbr: RefTab.Ref ~ RefTab.Create[5]; ValidateAll: PUBLIC PROC RETURNS [changed: BOOL _ FALSE] ~ { Locked: PROC ~ { Action: PROC [key: REF, val: REF] RETURNS [quit: BOOLEAN] ~ { IF Validate[NARROW[key]] THEN changed _ TRUE; RETURN [FALSE] }; [] _ RefTab.Pairs[fileForAbbr, Action]; }; DoLocked[Locked]; }; Validate: PUBLIC PROC [name: ATOM] RETURNS [changed: BOOL _ FALSE] ~ { Locked: PROC ~ { fileID: FileID ~ GetFileID[name]; oldFileID: FileID ~ NARROW[RefTab.Fetch[fileForAbbr, name].val]; IF oldFileID = NIL OR fileID = NIL OR Same[fileID, oldFileID] THEN changed _ FALSE ELSE { [] _ LoadInternal[name, fileID]; changed _ TRUE }; }; DoLocked[Locked]; }; END. `AbbrevExpandImpl.mesa Copyright c 1985, 1986 by Xerox Corporation. All rights reserved. written by Bill Paxton, May 1981 Paxton, November 8, 1982 1:31 pm Russ Atkinson, September 26, 1983 1:21 pm Michael Plass, May 1, 1986 11:56:28 am PDT Rick Beach, May 30, 1985 3:02:37 pm PDT Doug Wyatt, September 22, 1986 6:14:31 pm PDT Implements abbreviation expansion in Tioga. Declarations Operations Note that fileName is ignored. -- clear the specified abbreviation dictionary -- force dictName lowercase before create the atom -- see if have '. before -- hash must be independent of case of chars Called from elsewhere in Tioga when something changes that may have changed any abbr. Does not attempt to refresh screen. Called from elsewhere in Tioga when something changes that may have changed an abbr. Does not attempt to refresh screen. Κ$˜codešœ™Kšœ Οmœ7™BKšœ ™ Kšœ ™ K™)K™*K™'K™-—K˜Kšœ+™+K˜šΟk ˜ Kšœ žœ ˜Kšœžœ ˜Kšœžœ˜ Kšœžœžœžœ˜ Kšœ žœžœ ˜Kšœžœ˜Kšžœžœ˜Kšžœžœžœžœ˜3Kšœžœ˜$Kšœžœ˜Kšœžœ˜Kšœžœ$˜0Kšœžœžœ#˜GKšœ žœC˜QKšœ žœ~˜ŽKšœ žœžœ˜Kšœ žœ˜!KšœžœΖ˜ΡKšœ žœ˜!—K˜KšΠblœžœž˜Kšžœ!žœ žœ^˜”Kšžœ ˜Kšœž˜headšœ ™ Kšœžœ˜Kšœžœžœžœ˜Kšžœžœžœ˜K˜K˜.K˜Kšœžœžœ ˜šœ žœžœ˜K˜ Kšœžœ˜Kšœ žœ˜K˜ Kšœ žœžœžœž˜K˜——šœ ™ šΟn œžœžœžœ žœ žœžœžœ žœ ˜K˜1Kšœ žœ˜š œžœ˜Kšœ0Οc˜Hšžœ7žœžœž˜KKšžœžœžœ žœžœžœžœ˜7K˜!K˜Kšžœ˜—Kšœ-˜-Kšœ˜—š œžœ˜Kšœ0‘˜HKšœ˜—Kšžœ žœžœžœ˜/Kšœ?žœ˜Ešœ0žœ ˜=Kšœ(žœ˜/Kšœ˜Kšžœ˜ Kšœ˜—Kšžœžœžœžœ˜Kšœ˜Kšœ1žœ˜8Kšœ"žœ˜)K˜Kšœžœ˜ Kšœ˜K˜—š œžœžœžœ žœ žœ žœ žœ ˜lKšœ™Kšœžœ˜#KšœA˜AK˜K˜—š   œžœžœ$žœžœ˜]Kšžœžœžœ˜K˜K˜ Kšœ žœ ˜Kšœ žœ˜Kšœžœ˜K˜ Kšœžœ˜ Kšœ žœžœ˜Kšœ žœ˜Kš œ žœžœžœžœ˜K˜Kšžœžœžœ/žœ˜EK˜!Kš žœ žœžœ žœ!žœ‘˜\K˜,K˜>šžœ žœžœ‘˜&Kšœžœ ˜Kšžœžœžœ.˜@Kšžœ/˜3—K˜K˜9K˜K˜DK˜K˜*Kšžœ žœ ˜/K˜K˜—š  œžœ žœ žœžœžœ žœžœžœžœ˜vKšžœ ˜Kšžœžœ˜Kšœžœ˜ K˜!Kš žœžœ žœžœ žœ˜Yšžœžœ‘˜.Kš œžœžœžœžœ1˜FKšœ žœžœ˜#Kšœžœ ˜Kšžœ ˜ K˜!Kš žœžœ žœžœ žœ˜YK˜—šžœžœ‘˜+K˜Kšžœžœžœ‘-˜]K˜K˜—Kšžœ˜Kšžœžœ˜2K˜K˜—š  œžœžœžœ žœ˜-Kšœ.™.Kšžœžœžœ˜Kšœ4žœ˜9Kšœ˜K˜—Kšœžœžœžœ˜"K˜š   œžœ žœžœžœ˜;Kšœ2™2Kš  œžœ˜@KšžœG˜MK˜K˜—š œžœžœ'žœžœžœ žœžœžœ žœžœžœžœ˜ΌK˜š   œžœ žœžœžœ˜3K˜ Kšžœžœžœ5˜EKšžœ žœ˜Kšœ˜K˜—K˜Kšœ žœ˜#Kšœ žœ˜Kšœžœ˜ Kšœžœ˜ Kšœžœ˜K˜ Kšœžœ˜$K˜K˜!Kšœ žœžœ˜/K˜5K˜ K˜,Kšœ-™-K˜-šžœžœžœ˜7Kš‘'˜'Kšœ žœ(˜6šžœžœ˜ Kšœ žœJ˜XK˜Kšœžœ˜K˜)K˜—K˜—Kšžœ žœžœ˜-K˜Kšžœ žœžœ#žœ˜?Kšœ‘˜ ˜š  œžœ˜Kšœ žœ˜K˜+Kšœ"žœ ˜.šžœžœžœ ž˜Kš žœžœžœ žœžœ˜3Kšžœ˜—Kšœ žœ˜Kšœ˜K˜—K˜K˜K˜$Kšœ žœ ˜K˜:Kšœžœžœ˜"Kšœžœ˜ K˜%Kšœ žœ˜#K˜ K˜K˜“Kšžœžœžœ˜"šžœž˜!K˜t—Kšžœ žœd˜sš žœžœ žœ žœ‘˜FKšœ^˜^—šžœ!ž˜+Kšžœžœ˜'KšžœT˜[—šžœ žœžœ‘ ˜6KšœΗžœ žœžœ˜γK˜šžœžœžœ(ž˜=Kš‘.˜.Kšœ˜K˜——Kšœ žœ˜K˜K˜—˜K˜——š  œžœ žœžœžœžœ˜cKšœ,™,Kšœžœ˜K˜-K˜š žœžœžœžœ žœ‘˜>šœ žœžœ˜'K˜,—Kšžœ˜—šžœ žœ‘˜/K˜+šžœžœžœž˜šœ žœžœ˜'K˜2—Kšžœ˜—Kšœ˜—šœ˜K˜——š œžœžœžœ žœ žœ žœžœžœžœ˜˜Kšžœžœžœ˜Kšžœ?˜EKšœ˜K˜—š œžœžœ žœ žœ žœžœžœ˜šš  œžœ˜Kšžœžœ!˜.Kšžœžœ!˜.Kšœ˜—Kšœžœžœ˜Kšœžœ˜Kšžœ žœžœžœ˜Kšžœžœžœ žœ&˜EKšžœžœžœ žœ&˜EKšœžœ‘*˜7šžœžœžœž˜*šžœžœžœ‘˜LšžœQžœ žœ˜hšžœžœžœ‘˜+K˜K˜K˜K˜—Kšœ ˜ Kšžœ˜Kšœ˜—Kšœ˜—K˜ Kšžœ˜—K˜K˜K˜—š   œžœ žœžœžœ˜LKšœžœ˜Kšžœžœžœ˜K˜&Kšžœ0žœžœ˜BK˜šžœ žœ0ž˜DKšœ ˜ Kšžœ˜—šœ˜K˜——š   œžœ žœ žœžœ žœ˜]Kšžœžœ žœžœ˜8K˜+Kšžœ9žœžœ ˜RK˜šžœ žœ6ž˜IKšœ˜Kšžœ˜—šœ˜K˜——Kšœ žœ˜*K˜š œžœ žœžœ˜9Kšœžœžœ˜K˜-šžœžœž˜Kšœ žœ˜Kšœžœžœžœžœžœžœžœ˜:šžœ‘˜Kšœžœžœ˜KšœEžœžœ˜[Kš žœžœžœžœžœžœ˜8Kšœ˜——Kšœ˜K˜—K˜š œžœžœžœžœ˜Kšœ˜—š  œžœ žœžœ ˜6KšœžœžœžœE˜WKšœžœ;˜EKšœ žœžœ˜Kšœžœ˜+š žœ žœžœžœž˜&Kšœ*žœ)žœ žœ˜kKšœ˜Kšžœ˜—Kš žœ žœžœžœžœ˜$Kšžœžœ#˜.Kšœ˜K˜—Kšœžœžœ˜&Kšœžœ˜Kšœž œ˜š œžœžœ žœ˜(Kšœžœ˜*š œžœžœ˜Kš žœžœžœžœžœ˜MKšœ8˜8Kšœ˜—š œžœžœ˜Kšœ"˜"Kšžœžœžœžœ˜GKšœ˜—Kšœ˜Kšœ žœ˜Kšœ ˜ Kšœ˜K˜—šœ+˜+K˜—š   œžœžœžœ žœžœ˜