DIRECTORY Time, Checksum, Environment, Inline, PDInterpSysCalls, PDQueue, String, Process; PDQueueImpl: MONITOR IMPORTS Time, Checksum, Inline, PDInterpSysCalls, String, Process EXPORTS PDQueue = BEGIN Request: TYPE = PDQueue.Request; readProc: PROC [address: LONG POINTER, nwords: CARDINAL] _ NIL; writeProc: PROC [address: LONG POINTER, nwords: CARDINAL] _ NIL; maxStateWords: CARDINAL _ 16*1024; deferSaving: BOOLEAN _ FALSE; saveDeferred: BOOLEAN _ FALSE; state: LONG POINTER TO StringSeqRep _ NIL; messages: LONG POINTER TO StringSeqRep _ NIL; StringSeqRep: TYPE = MACHINE DEPENDENT RECORD [ nStrings: CARDINAL, end: CARDINAL, mark: CARDINAL, nMarked: CARDINAL, nDiscarded: CARDINAL _ 0, check: CARDINAL, nWords: CARDINAL, stringSpace: SEQUENCE COMPUTED CARDINAL OF WORD ]; StringRep: TYPE = RECORD [ nChars: CARDINAL, char: PACKED ARRAY [0..0) OF CHAR ]; Validate: INTERNAL PROC [s: LONG POINTER TO StringSeqRep] RETURNS [ok: BOOLEAN] = { nStrings: CARDINAL _ 0; nMarked: CARDINAL _ 0; offset: CARDINAL _ 0; IF s.check # CalculateCheck[s] THEN RETURN [FALSE]; IF s.mark > s.end THEN RETURN [FALSE]; WHILE offset < s.end DO words: CARDINAL; nStrings _ nStrings + 1; IF offset > s.mark AND nMarked = 0 THEN RETURN [FALSE]; IF offset >= s.mark THEN nMarked _ nMarked + 1; words _ (s[offset]+Environment.bytesPerWord-1)/Environment.bytesPerWord+1; offset _ offset + words; ENDLOOP; IF offset # s.end THEN RETURN [FALSE]; IF nStrings # s.nStrings THEN RETURN [FALSE]; IF nMarked # s.nMarked THEN RETURN [FALSE]; RETURN [TRUE] }; DiscardMessages: INTERNAL PROC [maxWords: CARDINAL] = { WHILE messages.end > maxWords DO Discard[messages, 1] ENDLOOP; }; Discard: INTERNAL PROC [s: LONG POINTER TO StringSeqRep, n: CARDINAL] = { newStart: CARDINAL _ StringOffset[s, n]; Inline.LongCOPY[from: @(s[newStart]), nwords: s.end-newStart, to: @(s[0])]; s.nStrings _ s.nStrings - n; s.end _ s.mark _ s.end-newStart; s.nDiscarded _ s.nDiscarded + n; }; StringOffset: INTERNAL PROC [s: LONG POINTER TO StringSeqRep, n: CARDINAL] RETURNS [offset: CARDINAL] = { offset _ 0; IF n = 0 THEN RETURN; IF n > s.nStrings THEN ERROR; FOR i: NAT IN [0..n) DO offset _ NextStringOffset[s, offset]; ENDLOOP; }; NextStringOffset: INTERNAL PROC [s: LONG POINTER TO StringSeqRep, offset: CARDINAL] RETURNS [CARDINAL] = INLINE { IF offset >= s.end THEN ERROR ELSE { words: CARDINAL _ (s[offset]+Environment.bytesPerWord-1)/Environment.bytesPerWord+1; IF words > s.end-offset THEN ERROR; offset _ offset + words; RETURN [offset]; }; }; Append: INTERNAL PROC [s: LONG POINTER TO StringSeqRep, string: LONG STRING] = { body: LONG POINTER TO StringRep; chars: CARDINAL _ IF string = NIL THEN 0 ELSE string.length; words: CARDINAL _ (string.length+Environment.bytesPerWord-1)/Environment.bytesPerWord+1; IF s.mark # s.end THEN ERROR; IF s.end+words > s.nWords THEN ERROR; body _ LOOPHOLE[@(s[s.end])]; body.nChars _ chars; FOR i: NAT IN [0..chars) DO body.char[i] _ string[i]; ENDLOOP; IF chars MOD 2 = 1 THEN body.char[chars] _ '\000; s.mark _ s.end _ s.end+words; s.nStrings _ s.nStrings + 1; }; AppendToString: INTERNAL PROC [string: LONG STRING, s: LONG POINTER TO StringSeqRep, offset: CARDINAL] = { body: LONG POINTER TO StringRep; IF offset >= s.end THEN ERROR; IF offset+(s[offset]+Environment.bytesPerWord-1)/Environment.bytesPerWord+1 > s.end THEN ERROR; body _ LOOPHOLE[@(s[offset])]; FOR i: NAT IN [0..body.nChars) DO string[string.length] _ body.char[i]; string.length _ string.length + 1; ENDLOOP; }; MoveToString: INTERNAL PROC [string: LONG STRING, s: LONG POINTER TO StringSeqRep, offset: CARDINAL] = { string.length _ 0; AppendToString[string, s, offset]; }; GetNumberDiscardedMessages: ENTRY PROC RETURNS [CARDINAL] = { IF messages = NIL THEN Restore[]; RETURN [messages.nDiscarded]; }; GetString: ENTRY PROC [string: LONG STRING, s: LONG POINTER TO StringSeqRep, n: CARDINAL] RETURNS [ok: BOOLEAN] = { string.length _ 0; IF n-s.nDiscarded > 32767 THEN RETURN [TRUE]; -- funny condition due to unsigned arithmetic . . . nDiscarded may wrap IF n-s.nDiscarded >= s.nStrings THEN RETURN [FALSE]; AppendToString[string, s, StringOffset[s, n-s.nDiscarded]]; RETURN [TRUE]; }; MoveMarkedStrings: INTERNAL PROC [from, to: LONG POINTER TO StringSeqRep] = { nwords: CARDINAL _ from.end-from.mark; IF to.nWords < to.mark+nwords THEN ERROR; to.nStrings _ to.nStrings - to.nMarked; to.nMarked _ 0; to.end _ to.mark; Inline.LongCOPY[from: @(from[from.mark]), nwords: nwords, to: @(to[to.mark])]; to.nStrings _ to.nStrings + from.nMarked; to.nMarked _ from.nMarked; to.end _ to.end + nwords; from.nStrings _ from.nStrings-from.nMarked; from.nMarked _ 0; from.end _ from.mark; }; CalculateCheck: INTERNAL PROC [s: LONG POINTER TO StringSeqRep] RETURNS [CARDINAL] = { cs: CARDINAL _ Checksum.ComputeChecksum[nWords: 5, p: s]; cs _ Checksum.ComputeChecksum[cs: cs, nWords: 1, p: s+6]; RETURN [cs]; }; Save: INTERNAL PROC = { IF deferSaving THEN {saveDeferred _ TRUE; RETURN}; IF state = NIL THEN ERROR; DiscardMessages[state.nWords-state.end]; messages.nMarked _ messages.nStrings; messages.mark _ 0; MoveMarkedStrings[from: messages, to: state]; state.check _ CalculateCheck[state]; IF NOT Validate[state] THEN ERROR; IF writeProc # NIL THEN { usingDisk _ Process.GetCurrent[]; writeProc[state, SIZE[StringSeqRep[state.end]]]; usingDisk _ NIL; }; MoveMarkedStrings[from: state, to: messages]; messages.nMarked _ 0; messages.mark _ messages.end; saveDeferred _ FALSE; }; Restore: INTERNAL PROC = { IF state # NIL THEN ERROR; IF readProc # NIL THEN { temp: LONG POINTER TO StringSeqRep _ PDInterpSysCalls.AllocateSpace[256]; usingDisk _ Process.GetCurrent[]; readProc[temp, 256]; usingDisk _ NIL; IF temp.check = CalculateCheck[temp] THEN { words: CARDINAL _ SIZE[StringSeqRep[temp.end]]; wordsAllocated: CARDINAL _ MAX[maxStateWords, words+255]/256*256; IF wordsAllocated <= maxStateWords THEN { state _ PDInterpSysCalls.AllocateSpace[wordsAllocated]; usingDisk _ Process.GetCurrent[]; readProc[state, words]; usingDisk _ NIL; state.nWords _ wordsAllocated-SIZE[StringSeqRep[0]]; IF Validate[state] THEN { AllocMessages[]; MoveMarkedStrings[from: state, to: messages]; messages.nMarked _ 0; messages.mark _ messages.end; firstUnprintedMessage _ messages.nDiscarded+messages.nStrings-24; -- it should be ok if this wraps around. } ELSE { PDInterpSysCalls.FreeSpace[state]; state _ NIL }; }; }; PDInterpSysCalls.FreeSpace[temp]; temp _ NIL; }; IF state = NIL THEN InternalReset[]; }; AllocMessages: INTERNAL PROC = { wordsAllocated: CARDINAL _ SIZE[StringSeqRep[state.nWords]]; IF messages # NIL THEN {PDInterpSysCalls.FreeSpace[messages]; messages _ NIL}; messages _ PDInterpSysCalls.AllocateSpace[wordsAllocated]; messages.nWords _ wordsAllocated-SIZE[StringSeqRep[0]]; messages.nStrings _ messages.end _ messages.mark _ messages.nMarked _ messages.nDiscarded _ 0; }; InternalReset: INTERNAL PROC = { IF state # NIL THEN {PDInterpSysCalls.FreeSpace[state]; state _ NIL}; state _ PDInterpSysCalls.AllocateSpace[maxStateWords]; state.nWords _ maxStateWords-SIZE[StringSeqRep[0]]; state.nStrings _ state.end _ state.mark _ state.nMarked _ state.nDiscarded _ 0; AllocMessages[]; }; Reset: PUBLIC ENTRY PROC = {InternalReset[]; Save[]}; newRequest: CONDITION; stringsPerRequest: CARDINAL = 6; requestLimit: CARDINAL _ 1000; ReqNumberFromStringNumber: PROC [stringNumber: CARDINAL] RETURNS [requestNumber: CARDINAL] = { requestNumber _ (stringNumber/stringsPerRequest) MOD requestLimit; }; DecodeCopies: PROC [tag: LONG STRING] RETURNS [copies: CARDINAL] = { copies _ ORD[tag[2]]*256+ORD[tag[3]]; }; QueueRequest: PUBLIC ENTRY PROC [request: Request] RETURNS [requestNumber: INT] = { ENABLE UNWIND => NULL; tag: LONG STRING _ [4]; wordsNeeded: INT _ 2*(INT[4]+request.fileName.length+request.requestTime.length+request.requestor.length+request.requestorPassword.length+request.separator.length) + stringsPerRequest*(SIZE[StringRep]+1); IF state = NIL THEN Restore[]; IF state.nStrings >= stringsPerRequest*requestLimit THEN RETURN [-1]; IF state.end+wordsNeeded+10 >= state.nWords THEN RETURN [-1]; requestNumber _ ReqNumberFromStringNumber[state.nStrings+state.nDiscarded]; String.AppendString[tag, " "]; String.AppendChar[tag, VAL[Inline.HighByte[CARDINAL[request.copies]]]]; String.AppendChar[tag, VAL[Inline.LowByte[CARDINAL[request.copies]]]]; Append[state, tag]; Append[state, request.fileName]; Append[state, request.requestTime]; Append[state, request.requestor]; Append[state, request.requestorPassword]; Append[state, request.separator]; IF stringsPerRequest # 6 THEN ERROR; BEGIN msg: LONG STRING _ [250]; String.AppendString[msg, request.fileName]; IF request.copies#1 THEN { String.AppendString[msg, " ("]; String.AppendDecimal[msg, request.copies]; String.AppendString[msg, " copies)"]; }; InternalLogMessage[msg, requestNumber, request.requestor]; END; Save[]; NOTIFY newRequest; }; reprintCopies: CARDINAL _ 0; reprintCancelled: BOOLEAN _ FALSE; Reprint: PUBLIC ENTRY PROC [copies: CARDINAL] = { reprintCancelled _ FALSE; reprintCopies _ reprintCopies + copies; NOTIFY newRequest; }; CancelReprint: PUBLIC ENTRY PROC = { reprintCancelled _ TRUE; reprintCopies _ 0; }; ReprintCancelled: PUBLIC PROC RETURNS [BOOLEAN] = {RETURN [reprintCancelled]}; printingSuspended: BOOLEAN _ FALSE; GetSuspended: PUBLIC ENTRY PROC RETURNS [BOOLEAN] = {RETURN [printingSuspended]}; SetSuspended: PUBLIC ENTRY PROC [suspended: BOOLEAN] RETURNS [old: BOOLEAN] = { old _ printingSuspended; printingSuspended _ suspended; NOTIFY newRequest; }; abortCurrent: BOOLEAN; FirstRequest: ENTRY PROC RETURNS [requestNumber: CARDINAL] = { ENABLE UNWIND => NULL; IF state = NIL THEN Restore[]; requestNumber _ ReqNumberFromStringNumber[state.nDiscarded]; }; FindRequest: ENTRY PROC [requestNumber: CARDINAL, tag: LONG STRING, request: Request] = { ENABLE UNWIND => NULL; stringNumber: CARDINAL _ requestNumber*stringsPerRequest; offset: CARDINAL _ 0; IF state = NIL THEN Restore[]; WHILE stringNumber < state.nDiscarded DO stringNumber _ stringNumber + requestLimit*stringsPerRequest ENDLOOP; IF stringNumber-state.nDiscarded >= state.nStrings THEN RETURN; offset _ StringOffset[state, stringNumber-state.nDiscarded]; MoveToString[tag, state, offset]; offset _ NextStringOffset[state, offset]; MoveToString[request.fileName, state, offset]; offset _ NextStringOffset[state, offset]; MoveToString[request.requestTime, state, offset]; offset _ NextStringOffset[state, offset]; MoveToString[request.requestor, state, offset]; offset _ NextStringOffset[state, offset]; MoveToString[request.requestorPassword, state, offset]; offset _ NextStringOffset[state, offset]; MoveToString[request.separator, state, offset]; IF stringsPerRequest # 6 THEN ERROR; }; GetRequest: ENTRY PROC [request: Request] RETURNS [requestNumber: INT, copies: CARDINAL] = { ENABLE UNWIND => NULL; tag: LONG STRING _ [4]; IF state = NIL THEN Restore[]; DO offset: CARDINAL _ 0; WHILE printingSuspended OR (state.nStrings = 0 AND reprintCopies = 0) DO WAIT newRequest ENDLOOP; abortCurrent _ FALSE; reprintCancelled _ FALSE; IF reprintCopies # 0 THEN { requestNumber _ -1; copies _ reprintCopies; reprintCopies _ 0; RETURN; }; requestNumber _ ReqNumberFromStringNumber[state.nDiscarded]; MoveToString[tag, state, offset]; offset _ NextStringOffset[state, offset]; MoveToString[request.fileName, state, offset]; offset _ NextStringOffset[state, offset]; MoveToString[request.requestTime, state, offset]; offset _ NextStringOffset[state, offset]; MoveToString[request.requestor, state, offset]; offset _ NextStringOffset[state, offset]; MoveToString[request.requestorPassword, state, offset]; offset _ NextStringOffset[state, offset]; MoveToString[request.separator, state, offset]; copies _ DecodeCopies[tag]; IF stringsPerRequest # 6 THEN ERROR; IF tag[0] = ' THEN EXIT; Discard[state, stringsPerRequest]; WHILE state.nDiscarded >= stringsPerRequest*requestLimit DO state.nDiscarded _ state.nDiscarded - stringsPerRequest*requestLimit; ENDLOOP; ENDLOOP; }; RemoveRequest: ENTRY PROC [requestNumber: CARDINAL] = { ENABLE UNWIND => NULL; IF state = NIL THEN Restore[]; IF requestNumber # ReqNumberFromStringNumber[state.nDiscarded] THEN ERROR; Discard[state, stringsPerRequest]; WHILE state.nDiscarded >= stringsPerRequest*requestLimit DO state.nDiscarded _ state.nDiscarded - stringsPerRequest*requestLimit; ENDLOOP; Save[]; }; DeferSaving: PUBLIC ENTRY PROC = { ENABLE UNWIND => NULL; deferSaving _ TRUE; }; DoDeferredSave: PUBLIC ENTRY PROC = { ENABLE UNWIND => NULL; deferSaving _ FALSE; IF saveDeferred THEN Save[]; }; DoRequest: PUBLIC PROC [action: PROC [request: Request, requestNumber: CARDINAL, abort: LONG POINTER TO BOOLEAN]] = { fileName: LONG STRING _ [80]; requestTime: LONG STRING _ [20]; requestor: LONG STRING _ [80]; requestorPassword: LONG STRING _ [80]; separator: LONG STRING _ [80]; requestNumber: INT; body: LONG POINTER TO StringRep; copies: CARDINAL; aborted: BOOLEAN _ FALSE; [requestNumber, copies] _ GetRequest[[fileName, requestTime, requestor, requestorPassword, separator, 0]]; IF requestNumber < 0 THEN { LogMessage["Reprint."]; action[[NIL, NIL, NIL, NIL, NIL, copies], NAT.LAST, @abortCurrent ! ABORTED => {aborted _ TRUE; CONTINUE}]; LogMessage["Reprint completed."]; } ELSE { body _ LOOPHOLE[@(state[0])]; IF body.nChars # 4 THEN ERROR; body.char[1] _ 'P; LogMessage["Started.", requestNumber, requestor]; action[[fileName, requestTime, requestor, requestorPassword, separator, copies], requestNumber, @abortCurrent ! ABORTED => {aborted _ TRUE; CONTINUE}]; IF aborted THEN LogMessage["Aborted.", requestNumber, requestor] ELSE LogMessage["Completed.", requestNumber, requestor]; RemoveRequest[requestNumber]; }; }; CancelRequest: PUBLIC ENTRY PROC [requestNumber: CARDINAL] RETURNS [ok: BOOLEAN] = { stringNumber: CARDINAL _ requestNumber*stringsPerRequest; body: LONG POINTER TO StringRep; IF state = NIL THEN Restore[]; WHILE stringNumber < state.nDiscarded DO stringNumber _ stringNumber+requestLimit*stringsPerRequest ENDLOOP; IF stringNumber-state.nDiscarded >= state.nStrings THEN RETURN [FALSE]; body _ LOOPHOLE[@(state[StringOffset[state, stringNumber-state.nDiscarded]])]; IF body.nChars # 4 THEN ERROR; IF body.char[0] # ' THEN RETURN [FALSE]; body.char[0] _ 'X; IF stringNumber = state.nDiscarded THEN abortCurrent _ TRUE; Save[]; RETURN [TRUE]; }; RequestStatus: TYPE = PDQueue.RequestStatus; CheckRequest: PUBLIC PROC [requestNumber: CARDINAL, action: PROC [request: Request, status: RequestStatus]] = { tag: LONG STRING _ [4]; status: RequestStatus; fileName: LONG STRING _ [80]; requestTime: LONG STRING _ [20]; requestor: LONG STRING _ [80]; requestorPassword: LONG STRING _ [80]; separator: LONG STRING _ [80]; tag.length _ 0; FindRequest[requestNumber, tag, [fileName, requestTime, requestor, requestorPassword, separator, 0]]; IF tag.length = 0 THEN status _ notFound ELSE IF tag[0] = 'X THEN status _ canceled ELSE IF tag[1] = 'P THEN status _ printing ELSE status _ waiting; action[[fileName, requestTime, requestor, requestorPassword, separator, DecodeCopies[tag]], status]; }; CountRequests: PUBLIC ENTRY PROC RETURNS [requestCount: NAT] = { IF state = NIL THEN Restore[]; requestCount _ state.nStrings/stringsPerRequest; }; EnumerateRequests: PUBLIC PROC [action: PROC [requestNumber: CARDINAL, request: Request, status: RequestStatus] RETURNS [continue: BOOLEAN]] = { firstRequest: NAT _ FirstRequest[]; nRequests: NAT _ CountRequests[]; requestNumber: NAT _ firstRequest MOD requestLimit; THROUGH [0..nRequests) DO continue: BOOLEAN _ TRUE; myAction: PROC [request: Request, status: RequestStatus] = { IF status = printing OR status = waiting THEN continue _ action[requestNumber, request, status]; }; CheckRequest[requestNumber MOD requestLimit, myAction]; IF NOT continue THEN EXIT; requestNumber _ (requestNumber + 1) MOD requestLimit; ENDLOOP; }; RegisterDisk: PUBLIC ENTRY PROC [read, write: PROC [address: LONG POINTER, nwords: CARDINAL], maxWords: CARDINAL] = { IF state # NIL THEN { PDInterpSysCalls.FreeSpace[state]; state _ NIL; }; readProc _ read; writeProc _ write; maxStateWords _ maxWords; }; usingDisk: PROCESS _ NIL; LogMessage: PUBLIC PROC [message: LONG STRING, requestNumber: INT _ -1, userName: LONG STRING _ NIL] = { IF usingDisk = Process.GetCurrent[] THEN { IF writeLineProc = NIL THEN ERROR; writeLineProc["Help!" ! ABORTED => CONTINUE]; writeLineProc[message ! ABORTED => CONTINUE]; } ELSE LogMessageEntry[message, requestNumber, userName]; }; LogMessageEntry: ENTRY PROC [message: LONG STRING, requestNumber: INT _ -1, userName: LONG STRING _ NIL] = { InternalLogMessage[message, requestNumber, userName]; }; InternalLogMessage: INTERNAL PROC [message: LONG STRING, requestNumber: INT _ -1, userName: LONG STRING _ NIL] = { msg: LONG STRING _ [250]; Time.AppendCurrent[msg]; msg[msg.length] _ ' ; msg.length _ msg.length + 1; IF requestNumber >= 0 THEN { String.AppendChar[msg, 'R]; FOR i: CARDINAL _ requestLimit/10, i/10 WHILE i#0 DO String.AppendDecimal[msg, (requestNumber/i) MOD 10]; ENDLOOP; String.AppendChar[msg, ' ]; }; String.AppendString[msg, message]; IF userName#NIL THEN { String.AppendString[msg, " ("]; String.AppendString[msg, userName]; String.AppendChar[msg, ')]; }; IF state = NIL THEN Restore[]; DiscardMessages[messages.nWords-SIZE[StringBody[msg.length]]]; Append[messages, msg]; TryToPrintMessages[]; }; loggingMessages: BOOLEAN _ FALSE; firstUnprintedMessage: CARDINAL _ 0; writeLineProc: PROC[LONG STRING] _ NIL; RegisterTTY: PUBLIC ENTRY PROC [writeLine: PROC[LONG STRING]] = { writeLineProc _ writeLine; TryToPrintMessages[]; }; TryToPrintMessages: INTERNAL PROC = { msg: LONG STRING _ [250]; GetMsg: INTERNAL PROC [n: CARDINAL] RETURNS [ok: BOOLEAN] = { msg.length _ 0; IF n-messages.nDiscarded > 32767 THEN RETURN [TRUE]; -- funny condition due to unsigned arithmetic . . . nDiscarded may wrap IF n-messages.nDiscarded >= messages.nStrings THEN RETURN [FALSE]; AppendToString[msg, messages, StringOffset[messages, n-messages.nDiscarded]]; RETURN [TRUE]; }; IF NOT loggingMessages OR writeLineProc = NIL THEN RETURN; FOR i: CARDINAL _ firstUnprintedMessage, i+1 UNTIL NOT GetMsg[i] DO writeLineProc[msg ! ABORTED => CONTINUE]; firstUnprintedMessage _ i+1; ENDLOOP; }; SetLogState: PUBLIC ENTRY PROC [logging: BOOLEAN] RETURNS [old: BOOLEAN] = { old _ loggingMessages; loggingMessages _ logging; IF messages # NIL THEN TryToPrintMessages[]; }; EnumerateMessages: PUBLIC PROC [action: PROC[message: LONG STRING] RETURNS [continue: BOOLEAN]] = { msg: LONG STRING _ [250]; FOR i: CARDINAL _ GetNumberDiscardedMessages[], i+1 UNTIL NOT GetString[msg, messages, i] DO IF NOT action[msg].continue THEN EXIT; ENDLOOP; }; END. 6PDQueueImpl.mesa Copyright (C) 1984, Xerox Corporation. All rights reserved. Michael Plass, April 10, 1985 5:28:48 pm PST Requests are numbered modulo this number. Records the process using the disk under the monitor, to avoid deadlocks in case of disk errors. Remote debug if it ever gets this bad. ʘJ™J™<™,J™—šÏk œQ˜ZJ˜—šœ ˜Jšœ:˜AJšœ˜Jšœ˜J˜šœ œ˜ J˜—š œ œ œœ œœ˜?J˜—š œ œ œœ œœ˜@J˜—šœœ ˜"J˜—Jšœ œœ˜Jšœœœ˜Jš œœœœœ˜*š œ œœœœ˜-J˜—š œœœ œœ˜/Jšœ œ˜Jšœœ˜Jšœœ˜Jšœ œ˜Jšœ œ˜Jšœœ˜Jšœœ˜Jšœ œœœ˜/Jšœ˜J˜—šœ œœ˜Jšœœ˜Jšœœœœ˜!Jšœ˜J˜—šÏnœœœœœœœœ˜SJšœ œ˜Jšœ œ˜Jšœœ˜Jšœœœœ˜3Jšœœœœ˜&šœ˜Jšœœ˜Jšœ˜Jš œœ œœœ˜7Jšœœ˜/JšœJ˜JJšœ˜Jšœ˜—Jšœœœœ˜&Jšœœœœ˜-Jšœœœœ˜+Jšœœ˜ Jšœ˜J˜—šžœœœ œ˜7Jšœœœ˜>Jšœ˜J˜—šžœœœœœœœ˜IJšœ œ˜(JšœK˜KJšœ˜Jšœ ˜ J˜ Jšœ˜J˜—šž œœœœœœœœ œ˜iJšœ ˜ Jšœœœ˜Jšœœœ˜šœœœ˜Jšœ%˜%Jšœ˜—Jšœ˜J˜—šžœœœœœœœœœœ˜qJšœœ˜šœ˜JšœœE˜TJšœœœ˜#Jšœ˜Jšœ ˜Jšœ˜—Jšœ˜J˜—šžœœœœœœœœ˜PJšœœœœ ˜ Jš œœœ œœœ˜Jšœœœ˜Jšœ œœ ˜Jšœ<˜œ˜nJšœ1œœ˜?Jšœ<˜Jšœ˜Jšœ˜Jšœ˜J˜—Jšœœœ˜!Jšœœ˜$š œœœœœ˜'J˜—šž œœœœ œœœ˜AJšœ˜Jšœ˜Jšœ˜J˜—šžœœœ˜%Jšœœœ ˜š žœœœœœœ˜=Jšœ˜šœœœœ˜4JšŸG˜G—Jšœ,œœœ˜BJšœM˜MJšœœ˜Jšœ˜—Jš œœœœœœ˜:š œœœœ ˜CJšœœœ˜)Jšœ˜Jšœ˜—Jšœ˜J˜—šž œœœœ œœœ˜LJšœ˜Jšœ˜Jšœ œœ˜,Jšœ˜J˜—šžœœœ œ œœœ œ˜cJšœœœ ˜š œœ%œœ˜\Jšœœœœ˜&Jšœ˜—Jšœ˜—J˜Jšœ˜——…—Ida