DIRECTORY Atom, Basics, BTree, BTreeSimple, BTreeVM, Commander, CommandToolExtras, FS, FSBackdoor, IO, Log, LupineRuntime, PrincOps, PrincOpsUtils, ProcessProps, RefTab, RefText, Rope, ThNet, UserProfile; WPBTreeImpl: PROGRAM IMPORTS Atom, Basics, BTree, BTreeSimple, BTreeVM, Commander, CommandToolExtras, FS, FSBackdoor, IO, Log, LupineRuntime, RefTab, PrincOpsUtils, ProcessProps, RefText, Rope, ThNet, UserProfile 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: Log.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: "///Strowger/WPTree"]; wpState.treeRootName _ FS.ExpandName[fileName].fullFName; IF extIntMapName=NIL THEN extIntMapName _ UserProfile.Token[key:"ThrushWPMappings", default: "/Indigo/Voice/BTree/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: Log.WhereToReport_$System] RETURNS [ok: BOOL_FALSE] = TRUSTED { ENABLE BTree.Error => { Log.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 { Log.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 => Log.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: Log.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 { Log.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 { Log.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 _ CommandToolExtras.NextArgument[cmd]; treeRoot, intExt: ROPE; IF sourceFile=NIL THEN { Log.Problem["Source file not specified", $System]; RETURN; }; s _ FS.StreamOpen[sourceFile]; treeRoot _ CommandToolExtras.NextArgument[cmd]; intExt _ CommandToolExtras.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. RWPBTreeImpl.mesa Last Edited by: Swinehart, February 8, 1985 11:49:55 am PST Reads and write white pages data base records. 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 ΚΗ– "Cedar" style˜headšœ™Ibody™;L™.code2šΟk ˜ M˜M˜Mšœ˜Mšœ ˜ Mšœ˜M˜ M˜Mšœ˜Mšœ ˜ M˜M˜M˜M˜ M˜M˜ M˜M˜Mšœ˜Mšœ˜Mšœ ˜ ——šœ ˜MšœJœœ\˜ΏMšœœ˜Mšœ˜Mšœœ˜Mšœœœ˜Mšœœ˜Mšœ œ˜"—™™Jšœ œ˜Jšœœ˜*—™ Mš œ œœœœœœ˜:—™M˜Mšœ œœ/˜GMšœœ ˜#M˜Mš œœœœœ˜,šœœ˜J˜J˜Jšœ œœ œ˜0J˜——J˜J™8™š Οn œœœœœ˜Mšœ œ7˜BMšœ œ3˜@M˜—MšœI˜PM˜—Mšœ œœ œœ œœœœ˜EM˜M˜M˜ M˜3Mšœn˜nMšœœ˜$MšœO˜OMšœW˜WMšœ!œ ˜GMšœœ˜J˜J˜—šžœ œœ=œœœœ˜‰Jš œ œœœœ˜.Mšœ@œœ˜RMšœ@œœ˜RM˜Mšœ˜šœ˜%Mš œHœ œœ2œ˜­—Mš œœ œœœ˜FMš œœ œœœ˜JMš œœ œœœ˜LMšœœ˜Mšœœ˜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šœ&œ˜-J˜—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˜——šœ™š žœœœ'œœ˜dMšœœœ˜ Mšœ œœ˜2Mšœ œœ˜3Mšœœ˜M˜Mšœ œ˜Mšœ œ'˜7Mšœœ˜šœ œœ˜Mšœ3œ˜=—Mšœœ˜Mšœ/˜/Mšœ-˜-MšœC˜Cšœ œ œ ˜'Mšœœœ˜Mšœœœ!˜/Mšœœœ˜Mšœ œ˜Mšœœ˜ Mšœœœ˜Mš œ œ œœœ œœ ˜Mšœœ˜šœ œœ˜Jšœœ"œœ˜BM˜%Mšœ"˜"Mšœœ˜Mšœ œ ˜Mšœœœ%˜MM˜—Mšœ œ˜Mšœ˜M˜—Mšœ œœ˜-Mšœœœ˜Mšœ œ˜ Mšœ-œœ˜OšœœŸ'˜?Mšœœœ3˜BM˜—šœ˜M˜*Mšœœ%˜1Mšœœœ ˜Mšœœœ˜.Mšœœœ˜5Mšœœœ˜7M˜—Mšœœœ˜*Mšœ˜—M˜9M˜ M˜#M˜$M˜$Mšœ6˜6Mšœ7˜7M˜—Mš ž œ œœœœœ˜Xš žœ œœœ œœ˜NM˜%M˜—šœ%œ˜.M˜M˜—šœ!œ˜*M˜M˜—šœœ œœ˜0M˜ΧM˜—M˜«M˜§Mšœ˜——…—=lR