DIRECTORY Atom, Basics, BTree, BTreeSimple, BTreeVM, Commander, CommandTool, FS, FSBackdoor, IO, LupineRuntime, PrincOps, PrincOpsUtils, ProcessProps, RefTab, RefText, Rope, ThNet, UserProfile, VoiceUtils ; WPBTreeImpl: PROGRAM IMPORTS Atom, Basics, BTree, BTreeSimple, BTreeVM, Commander, CommandTool, FS, FSBackdoor, IO, LupineRuntime, RefTab, PrincOpsUtils, ProcessProps, RefText, Rope, ThNet, UserProfile, VoiceUtils EXPORTS ThNet SHARES Rope = BEGIN OPEN IO; ROPE: TYPE = Rope.ROPE; Attrs: TYPE = ThNet.Attrs; WPListing: TYPE = ThNet.WPListing; WPState: TYPE = ThNet.WPState; WPStateRecord: TYPE = ThNet.WPStateRecord; WPValue: TYPE = LONG POINTER TO RECORD [ linkIndex: INT ]; EntSize: TYPE = [0..LAST[BTree.PageSize]-BTree.reservedWordsPerPage+1]; lastEnt: EntSize = LAST[EntSize]-1; Entry: TYPE = LONG POINTER TO WPEntryRecord; WPEntryRecord: TYPE = RECORD [ recordSize: EntSize, recordData: SEQUENCE COMPUTED EntSize OF INTEGER ]; CompareRopes: PROC[s1: ROPE, s2: ROPE, substringEqual: BOOL] RETURNS[res: BTreeSimple.Comparison] = { ss1: INT=Rope.Size[s1]; r2: ROPE_IF substringEqual THEN Rope.Substr[s2, 0, ss1] ELSE s2; RETURN [Rope.Compare[s1, r2, FALSE]]; }; wpAttrs: RefTab.Ref; InitWhitePagesDatabase: PUBLIC SAFE PROC[ fileName: ROPE, extIntMapName: ROPE, accessOptions: FS.AccessOptions _ $read, howToComplain: VoiceUtils.WhereToReport_$System] RETURNS[wpState: WPState_NIL] = TRUSTED { buffer: REF TEXT = RefText.ObtainScratch[100]; s: IO.STREAM; wpState _ NEW[WPStateRecord]; wpState.accessOptions _ accessOptions; IF fileName=NIL THEN fileName _ UserProfile.Token[key: "ThrushWPTreeName", default: "WPTree"]; wpState.treeRootName _ FS.ExpandName[fileName].fullFName; IF extIntMapName=NIL THEN extIntMapName _ UserProfile.Token[key:"ThrushWPMappings", default: "IntExt.map"]; IF wpAttrs=NIL THEN { wpAttrs _ RefTab.Create[]; FOR attr: Attrs IN Attrs DO IF keysForAttributes[attr] # $unassigned THEN []_RefTab.Store[wpAttrs, keysForAttributes[attr], NEW[Attrs_attr]]; ENDLOOP; }; s _ FS.StreamOpen[extIntMapName]; wpState.wpExtInt _ RefTab.Create[]; WHILE ~s.EndOf[] DO ENABLE IO.EndOfStream=>EXIT; int: ATOM _ Atom.MakeAtomFromRefText[s.GetCedarToken[buffer: buffer].token]; ext: ATOM; []_s.GetChar[]; ext _ Atom.MakeAtomFromRefText[s.GetLine[buffer]]; []_wpState.wpExtInt.Store[ext, int]; -- external version is the key. ENDLOOP; s.Close[]; RefText.ReleaseScratch[buffer]; []_OpenWhitePagesDatabase[wpState, howToComplain]; }; OpenWhitePagesDatabase: SAFE PROC[ wpState: WPState, howToComplain: VoiceUtils.WhereToReport_$System] RETURNS [ok: BOOL_FALSE] = TRUSTED { ENABLE BTree.Error => { VoiceUtils.Problem["Trouble opening BTree", howToComplain]; CONTINUE }; treeName: ROPE_wpState.treeRootName.Concat[".Tree"]; dataName: ROPE_wpState.treeRootName.Concat[".TreeData"]; feepTreeName: ROPE_wpState.treeRootName.Concat[".FTree"]; treeFile, dataFile, feepFile: FS.OpenFile; init: BOOL; IF wpState=NIL THEN RETURN[FALSE]; IF wpState.wpOpen THEN RETURN; wpState.wpTree _ BTreeSimple.New[]; wpState.wpFeepTree _ BTreeSimple.New[]; { ENABLE FS.Error => IF error.group = user THEN { VoiceUtils.Problem[error.explanation, howToComplain]; treeFile_FS.nullOpenFile; CONTINUE; }; SELECT wpState.accessOptions FROM $read => { treeFile_FS.Open[name: treeName]; feepFile_FS.Open[name: feepTreeName]; dataFile _ FS.Open[name: dataName]; }; $create => { treeFile_FS.Create[name: treeName, keep: 2, setKeep: TRUE, pages: 100]; feepFile_FS.Create[name: feepTreeName, keep: 2, setKeep: TRUE, pages: 100]; dataFile _ FS.Create[name: dataName, keep: 2, setKeep: TRUE, pages: 300]; }; $write => { treeFile_FS.OpenOrCreate[name: treeName, keep: 2, pages: 100]; feepFile_FS.OpenOrCreate[name: feepTreeName, keep: 2, pages: 100]; dataFile _ FS.OpenOrCreate[name: dataName, keep: 2, pages: 300]; }; ENDCASE => VoiceUtils.Problem["Don't request $append option for BTree", howToComplain]; }; IF treeFile=NIL OR dataFile = NIL OR feepFile=NIL THEN RETURN[FALSE]; wpState.wpFile _ treeFile; wpState.wpFeepFile _ feepFile; wpState.wpDataVMFile _ dataFile; wpState.wpDataVMLen _ FS.GetInfo[dataFile].bytes/2; wpState.wpDataVM _ BTreeVM.Open[file: FSBackdoor.GetFileHandle[dataFile], filePagesPerPage: 1, cacheSize: 10]; init _ FS.GetInfo[dataFile].bytes=0; BTreeSimple.Open[tree: wpState.wpTree, file: wpState.wpFile, initialize: init]; BTreeSimple.Open[tree: wpState.wpFeepTree, file: wpState.wpFeepFile, initialize: init]; IF wpState.accessOptions = $create THEN wpState.accessOptions _ $write; ok _ wpState.wpOpen _ TRUE; }; CloseWhitePagesDatabase: PUBLIC SAFE PROC[wpState: WPState, howToComplain: VoiceUtils.WhereToReport_$System] RETURNS [ok: BOOL_TRUE] = TRUSTED { IF wpState=NIL OR ~wpState.wpOpen THEN RETURN; BTreeSimple.SetState[wpState.wpTree, closed!BTree.Error => { ok_FALSE; CONTINUE}]; BTreeSimple.SetState[wpState.wpTree, closed!BTree.Error => { ok_FALSE; CONTINUE}]; wpState.wpDataVM.FlushCache[]; wpState.wpDataVM.FreeBuffers[]; IF wpState.accessOptions # $read THEN FS.SetByteCountAndCreatedTime[wpState.wpDataVMFile, wpState.wpDataVMLen*2!FS.Error => IF error.group=user THEN { VoiceUtils.Problem[error.explanation, howToComplain]; CONTINUE; }]; wpState.wpFile.Close[!FS.Error => IF error.group>=lock THEN CONTINUE]; wpState.wpFeepFile.Close[!FS.Error => IF error.group>=lock THEN CONTINUE]; wpState.wpDataVMFile.Close[!FS.Error => IF error.group>=lock THEN CONTINUE]; wpState.wpDataVMValid _ TRUE; wpState.wpOpen _ FALSE; }; SetProperLength: SAFE PROC[name: ROPE] = CHECKED { pages: INT; file: FS.OpenFile _ FS.Open[name, $write]; pages _ FS.GetInfo[file].pages; FS.SetByteCountAndCreatedTime[file, FS.BytesForPages[pages]]; FS.Close[file]; }; WhitePagesLookup: PUBLIC SAFE PROC[ wpState: WPState, name: ROPE, feep: BOOL_FALSE ] RETURNS [ listing: WPListing_NIL ] = TRUSTED { FindOne: UNSAFE PROC[key: BTreeSimple.EntryKey, value: BTreeSimple.EntryValue] RETURNS [continue: BOOLEAN_FALSE] = { index: EntSize; entry: Entry; wpValue: WPValue = LOOPHOLE[@value[0]]; recordKey: ATOM; recordValue: ROPE; SELECT CompareRopes[name, BTreeSimple.KeyFromEntry[key], TRUE] FROM greater => RETURN[TRUE]; equal => NULL; ENDCASE => RETURN[FALSE]; IF feep AND listing#NIL THEN { listing _ NIL; RETURN; }; IF (entry _ GetLinkedEntry[wpState, wpValue, $read]) = NIL THEN RETURN[feep]; listing _ RefTab.Create[]; [index, recordKey, recordValue] _ RopeFromWPEntry[entry, 0]; WHILE index#lastEnt DO []_listing.Store[recordKey, recordValue]; [index, recordKey, recordValue] _ RopeFromWPEntry[entry, index]; ENDLOOP; ReleaseLinkedEntry[wpState, wpValue]; RETURN[feep]; }; IF ~wpState.wpOpen AND ~OpenWhitePagesDatabase[wpState] THEN RETURN; []_BTreeSimple.EnumerateEntries[ tree: IF feep THEN wpState.wpFeepTree ELSE wpState.wpTree, key: name, relation: less, Proc: FindOne ]; }; WhitePagesEntry: PUBLIC SAFE PROC[ wpState: WPState, name: ROPE_NIL, key: ATOM_$officeNumber, feep: BOOL_FALSE, listing: WPListing _ NIL] RETURNS [fullRName: ROPE_NIL, entry: ROPE_NIL, newListing: WPListing_NIL] = TRUSTED { IF listing = NIL THEN listing _ WhitePagesLookup[wpState, name, feep]; IF listing = NIL THEN RETURN; newListing _ listing; fullRName _ NARROW[listing.Fetch[$rName].val]; IF key#NIL THEN entry _ NARROW[listing.Fetch[key].val]; }; WhitePagesEnter: PUBLIC SAFE PROC[wpState: WPState, listing: WPListing] = TRUSTED { IF ~wpState.wpOpen AND ~OpenWhitePagesDatabase[wpState] THEN RETURN; WhitePagesDo[wpState, listing, enter]; }; WhitePagesPrint: PUBLIC SAFE PROC[wpState: WPState, listing: WPListing] = TRUSTED { WhitePagesDo[wpState, listing, print]; }; WPFn: TYPE = { print, enter, note }; WhitePagesDo: SAFE PROC[wpState: WPState, listing: WPListing, fn: WPFn] = TRUSTED { dataPage: BTree.PagePtr; dummyData: ARRAY [0..4) OF CARDINAL; -- For collecting length statistics entry: Entry _ LOOPHOLE[LONG[@dummyData[0]]]; name, feepName: ROPE; linkIndex: INT_ wpState.wpDataVMLen; linkPage: INT; inPageIndex: INT; refType: BTree.ReferenceType _ $write; doIt: BOOL_FALSE; EPA: RefTab.EachPairAction = TRUSTED { realKey: ATOM _ NARROW[key,ATOM]; SELECT fn FROM enter => RopeToWPEntry[entry, realKey, NARROW[val], doIt]; print => PrintWPEntry[realKey, NARROW[val]]; ENDCASE; RETURN[FALSE]; }; name_NARROW[listing.Fetch[$rName].val]; feepName _ ThNet.FeepName[name]; IF name=NIL THEN ERROR; SELECT fn FROM note => { (GetCommanderHandle[]).out.Put[rope[name], rope["\n"]]; RETURN; }; ENDCASE; entry.recordSize_0; []_RefTab.Pairs[listing, EPA]; entry.recordSize_entry.recordSize + (1+1); -- one for size field, one for ending 0 IF fn#enter THEN RETURN; IF ~([linkPage, inPageIndex, ]_GetLinkValues[wpState, linkIndex]).ok THEN RETURN; IF inPageIndex=0 OR inPageIndex+entry.recordSize>FS.WordsForPages[1] THEN { refType _ $new; IF inPageIndex#0 THEN { linkPage_SUCC[linkPage]; inPageIndex _ 0; linkIndex _ FS.WordsForPages[linkPage]; }; }; dataPage _ BTreeVM.ReferencePage[wpState.wpDataVM, linkPage, refType]; entry _ dataPage+inPageIndex; entry.recordSize _ 0; doIt _ TRUE; []_RefTab.Pairs[listing, EPA]; entry[entry.recordSize] _ 0; entry.recordSize _ entry.recordSize + (1+1); wpState.wpDataVMLen _ linkIndex+entry.recordSize; BTreeVM.ReleasePage[wpState.wpDataVM, linkPage, endOfUpdate]; { SetVal: PROC[value: BTreeSimple.EntryValue] = { LOOPHOLE[value, LONG POINTER TO CARDINAL]^ _ 2; -- Compensate BTree bug! LOOPHOLE[@value[0], LONG POINTER TO INT]^ _ linkIndex; }; BTreeSimple.UpdateEntry[tree: wpState.wpTree, key: name, valueLength: 2, Proc: SetVal]; BTreeSimple.UpdateEntry[tree: wpState.wpFeepTree, key: feepName, valueLength: 2, Proc: SetVal]; }; }; PrintWPEntry: PROC[key: ATOM, val: ROPE] = { h: Commander.Handle = GetCommanderHandle[]; h.out.PutF["%g: %g\n", atom[key], rope[val]]; IF key=$rName AND h.out#h.err THEN h.err.Put[rope[val], rope["\n"]]; }; GetCommanderHandle: PROC RETURNS[handle: Commander.Handle] = { p: Atom.PropList = ProcessProps.GetPropList[]; IF p=NIL THEN RETURN[NIL]; RETURN[NARROW[Commander.GetProperty[$CommanderHandle, p]]]; }; GetLinkedEntry: SAFE PROC[wpState: WPState, wpValue: WPValue, access: FS.AccessOptions] RETURNS[entry: Entry _NIL] = TRUSTED { linkIndex: INT _ wpValue.linkIndex; linkPage: INT; inPageIndex: INT; pagePtr: BTree.PagePtr; IF ([linkPage, inPageIndex,]_GetLinkValues[wpState, linkIndex]).ok=FALSE THEN RETURN; pagePtr _ BTreeVM.ReferencePage[wpState.wpDataVM, linkPage, SELECT access FROM $read => $read, $create => $new, ENDCASE => $write]; entry _ pagePtr+inPageIndex; }; ReleaseLinkedEntry: SAFE PROC[wpState: WPState, wpValue: WPValue] = TRUSTED { linkPage: INT; IF ([linkPage,,]_GetLinkValues[wpState, wpValue.linkIndex]).ok=FALSE THEN RETURN; BTreeVM.ReleasePage[wpState.wpDataVM, linkPage, endOfUpdate]; }; GetLinkValues: SAFE PROC[wpState: WPState, linkIndex: INT] RETURNS[linkPage, inPageIndex: INT, ok: BOOL_FALSE] = TRUSTED { IF linkIndex<0 OR linkIndex> wpState.wpDataVMLen THEN { VoiceUtils.Problem["BTree structure error"]; RETURN; }; ok_TRUE; linkPage _ FS.PagesForWords[linkIndex+1]-1; inPageIndex _ linkIndex - FS.WordsForPages[linkPage]; }; RopeFromWPEntry: PROC[entry: Entry, index: EntSize] RETURNS[newIndex: EntSize_lastEnt, key: ATOM, rope: ROPE] = { code: INTEGER; keyRope: ROPE; IF index=lastEnt THEN RETURN; code _ entry[index]; SELECT code FROM 0 => RETURN; <0 => { key _ keysForAttributes[LOOPHOLE[-code]]; index_index+1; }; ENDCASE => { [index, keyRope] _ RFromWPE[entry, index]; key _ Atom.MakeAtom[keyRope]; }; [newIndex, rope] _ RFromWPE[entry, index]; }; RFromWPE: PROC[entry: Entry, index: EntSize] RETURNS[newIndex: EntSize_lastEnt, rope: ROPE] = { length: INTEGER; wfc: LONG CARDINAL; textRope: Rope.Text; IF index=lastEnt THEN RETURN; length _ entry[index]; rope _ textRope _ Rope.NewText[size: length]; wfc _ LupineRuntime.WordsForChars[length]; PrincOpsUtils.LongCopy[ from: @entry[index+1], to: BASE[DESCRIPTOR[textRope.text]], nwords: wfc ]; newIndex _ index+wfc+1; }; RopeToWPEntry: PROC[entry: Entry, key: ATOM, rope: ROPE, doIt: BOOL] = { index: EntSize _ entry.recordSize; attr: REF Attrs _ NARROW[wpAttrs.Fetch[key].val]; IF attr#NIL THEN { IF doIt THEN entry[index]_-LOOPHOLE[attr^, INTEGER]; entry.recordSize _ index+1; } ELSE RToWPE[entry, Atom.GetPName[key], doIt]; RToWPE[entry, rope, doIt]; }; RToWPE: PROC[entry: Entry, rope: ROPE, doIt: BOOL] = { index: EntSize _ entry.recordSize; wfc: LONG CARDINAL; len: INTEGER _ rope.Length[]; IF doIt THEN entry[index] _ len; wfc _ LupineRuntime.WordsForChars[len]; IF doIt THEN PrincOpsUtils.LongCopy[ to: @entry[index+1], from: BASE[DESCRIPTOR[Rope.Flatten[rope].text]], nwords: wfc ]; entry.recordSize _ index+wfc+1; }; WhitePagesFill: SAFE PROC[cmd: Commander.Handle, accessOptions: FS.AccessOptions_$write] = TRUSTED { s: IO.STREAM; lineBuffer: REF TEXT = RefText.ObtainScratch[100]; tokenBuffer: REF TEXT = RefText.ObtainScratch[100]; listing: WPListing_NIL; wpState: WPState; numEntries: CARDINAL _ 0; sourceFile: ROPE _ CommandTool.NextArgument[cmd]; treeRoot, intExt: ROPE; IF sourceFile=NIL THEN { VoiceUtils.Problem["Source file not specified", $System]; RETURN; }; s _ FS.StreamOpen[sourceFile]; treeRoot _ CommandTool.NextArgument[cmd]; intExt _ CommandTool.NextArgument[cmd]; wpState _ InitWhitePagesDatabase[ treeRoot, intExt, accessOptions]; IF wpState#NIL THEN WHILE ~s.EndOf[] DO ENABLE IO.EndOfStream=>EXIT; line: REF TEXT _ s.GetLine[buffer: lineBuffer]; lineS: IO.STREAM; extKey, key: ATOM_NIL; value: ROPE; keyVal: REF TEXT; KeyProc: IO.BreakProc = TRUSTED { RETURN[IF char=': THEN sepr ELSE other]; }; IF line.length=0 THEN { IF listing#NIL THEN { IF ~wpState.wpOpen AND ~OpenWhitePagesDatabase[wpState] THEN EXIT; WhitePagesDo[wpState, listing, note]; WhitePagesEnter[wpState, listing]; key _ extKey _ NIL; numEntries _ SUCC[numEntries]; IF Basics.BITSHIFT[numEntries, 8]=0 THEN []_CloseWhitePagesDatabase[wpState]; }; listing _ NIL; LOOP; }; IF listing = NIL THEN listing _ NewListing[]; lineS _ IO.TIS[line]; keyVal _ NIL; keyVal _ lineS.GetToken[KeyProc, tokenBuffer!IO.EndOfStream => CONTINUE].token; IF lineS.EndOf[] THEN { -- no key, extension of previous value. IF key#NIL THEN value _ value.Cat["\n", Rope.FromRefText[keyVal]]; } ELSE { extKey _ Atom.MakeAtomFromRefText[keyVal]; key _ NARROW[wpState.wpExtInt.Fetch[extKey].val]; IF key=NIL THEN key_extKey; []_lineS.GetChar[!IO.EndOfStream => CONTINUE]; []_lineS.SkipWhitespace[!IO.EndOfStream => CONTINUE]; value _ lineS.GetLineRope[!IO.EndOfStream => CONTINUE]; }; IF key#NIL THEN List[listing, key, value]; ENDLOOP; (GetCommanderHandle[]).err.PutRope["-- Updates done.\n"]; s.Close[]; RefText.ReleaseScratch[lineBuffer]; RefText.ReleaseScratch[tokenBuffer]; []_CloseWhitePagesDatabase[wpState]; SetProperLength[wpState.treeRootName.Concat[".Tree"]]; SetProperLength[wpState.treeRootName.Concat[".FTree"]]; }; NewListing: PUBLIC SAFE PROC RETURNS [WPListing] = TRUSTED { RETURN[RefTab.Create[]]; }; List: PUBLIC SAFE PROC[listing: WPListing, key: ATOM, value: ROPE] = TRUSTED { []_RefTab.Store[listing, key, value]; }; FillCmdInit: Commander.CommandProc = TRUSTED { WhitePagesFill[cmd, $create]; }; FillCmd: Commander.CommandProc = TRUSTED { WhitePagesFill[cmd, $write]; }; keysForAttributes: ARRAY ThNet.Attrs OF ATOM = [ $unassigned, $name, $rName, $officeNumber, $officeDDDNumber, $outsideNumber, $officeAddress, $officeLocation, $organization, $homeAddress, $frequency, $mailSystem, $primaryKey, $unassigned, $unassigned, $unassigned ]; Commander.Register["WPFill", FillCmdInit, "WPFill (opt)> creates new set of tree files from entries in textEntriesFile"]; Commander.Register["WPRefill", FillCmd, "WPRefill (opt)> adds to set of tree files from entries in textEntriesFile"]; END. ŽWPBTreeImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Last Edited by: Swinehart, October 28, 1985 12:05:26 pm PST Representation primitives Top-level State Key Value: Data Page Values Used in enumeration routines to determine actual matches Tables of mappings between small integers (wpAttrs) or external strings (wpExtInt) and attribute names ThNet Procedures name must be in form rr.xxxxxxx Returns first listing for which name is a substring, case unimportant, of the listing Put in something new, or replace something old. Start Here New Page needed. Subsidiary procedures Swinehart, May 16, 1985 9:16:38 am PDT Cedar 6.0 changes to: DIRECTORY, WPBTreeImpl, WhitePagesFill Swinehart, October 28, 1985 12:05:20 pm PST Log => VoiceUtils changes to: DIRECTORY, WPBTreeImpl, InitWhitePagesDatabase, OpenWhitePagesDatabase, CloseWhitePagesDatabase, GetLinkValues, WhitePagesFill Κό– "Cedar" style˜šœ™Jšœ Οmœ1™Kšœ žœ7˜BKšœ žœ3˜@K˜—KšžœP˜WK˜—Kšžœ žœžœ žœžœ žœžœžœžœ˜EK˜K˜K˜ K˜3Kšœn˜nKšœžœ˜$KšœO˜OKšœW˜WKšžœ!žœ ˜GKšœžœ˜J˜J˜—šŸœž œžœDžœžœžœžœ˜Jš žœ žœžœžœžœ˜.Kšœ@žœžœ˜RKšœ@žœžœ˜RK˜Kšœ˜šžœž˜%Kš žœHžœ žœžœ9žœ˜΄—Kš œžœ žœžœžœ˜FKš œžœ žœžœžœ˜JKš œžœ žœžœžœ˜LKšœžœ˜Kšœžœ˜J˜J˜—š Ÿœžœžœžœžœ˜2Jšœžœ˜ Jšœžœ žœž˜*Jšœžœ˜Jšžœ"žœ˜=Jšžœ ˜J˜J˜—šŸœžœžœžœ˜#Jšœžœžœžœ˜0Jšžœžœžœ˜.Jšœ™J™UšŸœžœžœ:˜NJšžœ žœžœ˜%J˜J˜ Jšœžœ ˜'Jšœ žœ˜Jšœ žœ˜šžœ3žœž˜CJšœ žœžœ˜Jšœ žœ˜Jšžœžœžœ˜—Jš žœžœ žœžœ žœžœ˜8šžœ5ž˜:Jšžœžœ˜—Jšœ˜Jšœ<˜<šžœž˜Jšœ)˜)Jšœ@˜@Jšžœ˜—Jšœ%˜%Jšžœ˜ J˜—Jšžœžœ"žœžœ˜D˜ Jšœžœžœžœ˜:J˜(J˜—J˜—J˜šŸœžœžœžœ˜"Jš œžœžœžœžœžœ˜LJšœžœ˜Jšžœ žœžœ žœžœžœžœ˜UJšžœ žœžœ2˜GJšžœ žœžœžœ˜J˜Jšœ žœ˜.Jšžœžœžœ žœ˜7J˜—J˜š Ÿœžœžœžœ)žœ˜SJšžœžœ"žœžœ˜DJšœ&˜&J˜J˜—š Ÿœžœžœžœ)žœ˜SJšœ&˜&J˜J˜—J˜$J˜šŸ œžœžœ3žœ˜SJ™/J˜Jšœ žœžœžœ #˜HJšœžœžœ˜-Jšœžœ˜Jšœ žœ˜$Jšœ žœ˜Jšœ žœ˜J˜&J˜Jšœžœžœ˜J˜šžœžœ˜&Jšœ žœžœžœ˜!šžœž˜Jšœ'žœ ˜:Jšœžœ˜,Jšžœ˜—Jšžœžœ˜J˜—J˜JšΟb ™ Jšœžœ˜'Jšœ ˜ Jšžœžœžœžœ˜šžœž˜JšœBžœ˜LJšžœ˜—J˜Jšœ˜Jšœžœ˜Jšœ+ '˜RJšžœ žœžœ˜JšžœCžœžœ˜Qšžœžœžœžœ˜KJ™J˜šžœžœ˜Jšœ žœ ˜J˜Jšœ žœ˜'J˜—J˜—J˜FJ˜J˜Jšœžœ˜ Jšœžœ˜Jšœ˜Jšœ,˜,Jšœ1˜1J˜=˜šŸœžœ#˜/Jš žœžœžœžœžœ ˜HJš žœ žœžœžœžœ˜6J˜—šœ8˜8Jšœ˜—šœ@˜@Jšœ˜—J˜—J˜J˜—šŸ œžœžœžœ˜,J˜+J˜-Jšžœ žœ žœ"˜DJ˜—J˜šŸœžœžœ˜>J˜.Jš žœžœžœžœžœ˜Jšžœžœ.˜;J˜—J˜šŸœžœžœ-žœ˜WJšžœžœžœ˜&Jšœ žœ˜#Jšœ žœ˜Jšœ žœ˜J˜JšžœAžœžœžœ˜U˜;Jšžœžœ"žœ ˜G—Jšœ˜J˜—J˜šŸœžœžœ'žœ˜MJšœ žœ˜Jšžœ=žœžœžœ˜QJšœ=˜=J˜—J˜šŸ œžœžœžœ˜:Jš žœžœžœžœžœ˜?šžœ žœ žœ˜7Jšœ-žœ˜4J˜—Jšœžœ˜Jšœ žœ˜+Jšœžœ˜5J˜—J˜šŸœžœ˜3Jšžœ!žœžœ˜=Jšœžœ˜Jšœ žœ˜Jšžœžœžœ˜Jšœ˜šžœž˜Jšœžœ˜ ˜Jšœžœ ˜)J˜J˜—šžœ˜ Jšœ*˜*J˜J˜——Jšœ*˜*J˜—J˜šŸœžœ˜,Jšžœ"žœ˜2Jšœžœ˜Jšœžœžœ˜Jšœ˜Jšžœžœžœ˜Jšœ˜J˜-J˜*˜J˜Jšœžœž œ˜$J˜ J˜—J˜J˜—J˜š Ÿ œžœžœžœžœžœ˜HJšœ"˜"Jšœžœ žœ˜1šžœžœžœ˜Jšžœžœžœžœ˜4Jšœ˜J˜—Jšžœ)˜-J˜J˜—J˜šŸœžœžœžœ˜6Jšœ"˜"Jšœžœžœ˜Jšœžœ˜Jšžœžœ˜ J˜'šžœžœ˜$Jšœ˜Jšœžœž œ˜0J˜ J˜—Jšœ˜J˜——šœ™š Ÿœžœžœ'žœžœ˜dKšœžœžœ˜ Kšœ žœžœ˜2Kšœ žœžœ˜3Kšœžœ˜K˜Kšœ žœ˜Kšœ žœ!˜1Kšœžœ˜šžœ žœžœ˜Kšœ:žœ˜D—Kšœžœ˜Kšœ)˜)Kšœ'˜'KšœC˜Cšžœ žœž œ ž˜'Kšžœžœžœ˜Kšœžœžœ!˜/Kšœžœžœ˜Kšœ žœ˜Kšœžœ˜ Kšœžœžœ˜Kš œ žœ žœžœžœ žœžœ ˜Mšžœžœ˜šžœ žœžœ˜Jšžœžœ"žœžœ˜BK˜%Kšœ"˜"Kšœžœ˜Kšœ žœ ˜Kšžœžœžœ%˜MK˜—Kšœ žœ˜Kšžœ˜K˜—Kšžœ žœžœ˜-Kšœžœžœ˜Kšœ žœ˜ Kšœ-žœžœ˜Ošžœžœ '˜?Kšžœžœžœ3˜BK˜—šžœ˜K˜*Kšœžœ%˜1Kšžœžœžœ ˜Kšœžœžœ˜.Kšœžœžœ˜5Kšœžœžœ˜7K˜—Kšžœžœžœ˜*Kšžœ˜—K˜9K˜ K˜#K˜$K˜$Kšœ6˜6Kšœ7˜7K˜—Kš Ÿ œž œžœžœžœžœ˜Xš Ÿœž œžœžœ žœžœ˜NK˜%K˜—šœ%žœ˜.K˜K˜—šœ!žœ˜*K˜K˜—šœžœ žœžœ˜0K˜ΧK˜—K˜«K˜§Kšžœ˜—code™&M™ Mšœ Οr&™2—™+M™Mšœ ’~™Š—M™—…—=|T