DIRECTORY Atom USING [ DottedPairNode ], Commander USING [ CommandProc, Register ], IO, Lark -- USING [ -- CommandEvents, ConnectionSpec, Event, LarkModel, Passel, StatusEvents ]--, LarkFeep, LarkFeepRpcControl USING [ ExportInterface ], LarkSmartsMonitorImpl, MBQueue USING [ Create, QueueClientAction ], Nice, RefID USING [ ID, nullID, Reseal, Unseal ], Rope USING [ ROPE ], RPC USING [ EncryptionKey, ExportFailed ], ThParty USING [ Alert, ConversationInfo, CreateConversation, DescribeParty, GetConversationInfo, GetKeyTable, GetParty, RegisterServiceInterface, SmartsInterfaceRecord ], ThPartyPrivate USING [ RegisterLocal ], Thrush USING [ ConversationID, ConvEvent, Credentials, InterfaceSpec, PartyID, NB, noAddress, nullConvID, nullID, ROPE, SHHH, SmartsID, StateInConv ], ThSmartsPrivate USING [ AssessDamage, ChangeState, ComputeConnection, ConvDesc, Deregister, EnterLarkState, ForgetConv, GetConv, GetConvDesc, GetSIC, GetSmartsInfo, LarkFailed, LarkInfo, LarkState, OpenConversations, QueueFeeps, SmartsInfo, SmartsInfoBody ], ThSmartsRpcControl, Triples USING [Make ], TU, VoiceUtils USING [ CmdOrToken, CurrentPasskey, MakeRName, Problem, ProblemFR, Report, ReportFR ] ; LarkTrunkSmartsImpl: CEDAR MONITOR LOCKS root IMPORTS Commander, root: LarkSmartsMonitorImpl, IO, LarkFeepRpcControl, MBQueue, Nice, RefID, RPC, ThParty, ThPartyPrivate, ThSmartsPrivate, ThSmartsRpcControl, Triples, TU, VoiceUtils EXPORTS ThSmartsPrivate, LarkFeep --, ThSmarts via Interface record -- SHARES LarkSmartsMonitorImpl = { OPEN IO; ConvDesc: TYPE = ThSmartsPrivate.ConvDesc; Reseal: PROC[r: REF] RETURNS[RefID.ID] = INLINE {RETURN[RefID.Reseal[r]]; }; ROPE: TYPE = Thrush.ROPE; SHHH: TYPE = Lark.SHHH; -- Encrypts conv. if first arg to RPC PROC PartyID: TYPE = Thrush.PartyID; nullID: RefID.ID = Thrush.nullID; nullConvID: Thrush.ConversationID = Thrush.nullConvID; SmartsInfo: TYPE = ThSmartsPrivate.SmartsInfo; StateInConv: TYPE = Thrush.StateInConv; PD: TYPE = RECORD [ timeoutNoAction: INT_30, doReports: BOOL_FALSE, doNice: BOOL_FALSE ]; pd: REF PD _ NEW[PD_[]]; larkFeepSpec: Thrush.InterfaceSpec _ [interfaceName: [type: "LarkFeep"], serviceID: RefID.nullID]; larkFeepUser: ROPE; larkFeepPassword: RPC.EncryptionKey; Report: PROC[what: ROPE, info: SmartsInfo] = { IF NOT pd.doReports THEN RETURN; VoiceUtils.Report[what, $Lark, info.larkInfo]; }; RegisterTrunk: PUBLIC ENTRY PROC[ hostSmartsID: Thrush.SmartsID, hostInfo: SmartsInfo, partyRname: ROPE ] RETURNS [ nb: Thrush.NB_$success, smartsID: Thrush.SmartsID_nullID ] = { ENABLE UNWIND => NULL; localSmarts: ThParty.SmartsInterfaceRecord; info: SmartsInfo; smarts: REF; credentials: Thrush.Credentials; nbLocal: Thrush.NB; exportRequired: BOOL _ larkFeepSpec.interfaceName.instance = NIL; localSmarts _ ThSmartsRpcControl.NewInterfaceRecord[]; localSmarts.clientStubProgress _ LarkTrunkProgress; localSmarts.clientStubSubstitution _ LarkTrunkSubstitution; [nb, credentials] _ ThPartyPrivate.RegisterLocal [ rName: partyRname, type: $trunk, interfaceRecord: localSmarts, properties: [role: $voiceTerminal, netAddress: hostInfo.larkInfo.netAddress] ]; IF nb # $success OR (smartsID_credentials.smartsID) = nullID THEN { VoiceUtils.ProblemFR["Can't register Lark trunk: %g",$System,NIL, rope[partyRname]]; RETURN; }; smarts _ RefID.Unseal[smartsID]; IF smarts = NIL THEN { VoiceUtils.Problem["No smarts already?"]; RETURN[$noSuchSmarts]; }; info _ NEW[ThSmartsPrivate.SmartsInfoBody _ [ smartsID: smartsID, otherSmartsID: hostSmartsID, partyType: $trunk, ParseEvent: LarkTrunkParseEvent, NoteNewStateP: NoteNewTkState, requests: MBQueue.Create[], larkInfo: hostInfo.larkInfo ]]; info.larkInfo.larkTrunkSmartsInfo _ info; Triples.Make[$SmartsData, smarts, info]; larkFeepSpec.serviceID _ smartsID; -- identify self. [nbLocal, larkFeepSpec] _ ThParty.RegisterServiceInterface[credentials: credentials, interfaceSpecPattern: larkFeepSpec]; SELECT nbLocal FROM $success, $stateMismatch => NULL; ENDCASE => GOTO CantExportFeep; IF exportRequired THEN LarkFeepRpcControl.ExportInterface[ larkFeepSpec.interfaceName, larkFeepUser, larkFeepPassword! RPC.ExportFailed => GOTO CantExportFeep] EXITS CantExportFeep => { VoiceUtils.Problem["Couldn't export LarkFeep from TrunkSmarts"]; RETURN; }; }; EventSpec: TYPE = REF EventSpecBody; EventSpecBody: TYPE = RECORD [ smartsInfo: SmartsInfo, sEvent: Lark.StatusEvent ]; LarkTrunkParseEvent: PROC[ smartsInfo: SmartsInfo, sEvent: Lark.StatusEvent] = { smartsInfo.requests.QueueClientAction[ QdLarkTrunkParseEvent, NEW[EventSpecBody _ [smartsInfo, sEvent]]]; }; QdLarkTrunkParseEvent: ENTRY PROC[r: REF] = { -- r is the EventSpec eventSpec: EventSpec = NARROW[r]; smartsInfo: SmartsInfo = eventSpec.smartsInfo; { ENABLE { UNWIND=>NULL; ThSmartsPrivate.LarkFailed => { VoiceUtils.ProblemFR["%g: Lark Failed", $Smarts, smartsInfo, TU.RefAddr[smartsInfo]]; GOTO Failed; }; }; sEvent: Lark.StatusEvent _ eventSpec.sEvent; SELECT sEvent.device FROM ringDetect => SELECT sEvent.event FROM Lark.enabled => CmdRinging[smartsInfo]; Lark.disabled => CmdStopRinging[smartsInfo]; ENDCASE => ERROR; ENDCASE; EXITS Failed => ThSmartsPrivate.Deregister[smartsInfo.otherSmartsID]; }; }; CmdRinging: INTERNAL PROC[info: SmartsInfo] = { partyID: PartyID; -- self calledPartyRname: ROPE; calledPartyID: PartyID; cDesc: ConvDesc; convEvent: Thrush.ConvEvent; nb: Thrush.NB; { SELECT ThSmartsPrivate.GetSIC[info] FROM $idle => NULL; ENDCASE => RETURN; -- not a valid time for this call. Ignore. Host system will handle. cDesc _ ThSmartsPrivate.GetConv[info, nullConvID, TRUE]; partyID _ cDesc.situation.self.partyID; -- self [nb, calledPartyRname] _ ThParty.DescribeParty[partyID: partyID, nameReq: $owner]; IF nb # $success OR calledPartyRname=NIL THEN GOTO Failed; -- Enter error state? Forget Conv? [nb, calledPartyID] _ ThParty.GetParty[partyID: partyID, rName: calledPartyRname]; SELECT nb FROM $success => NULL; $noSuchParty2 => GOTO Failed; -- Enter error state? Forget Conv? $voiceTerminalBusy => GOTO Failed; -- can't ring in ENDCASE => GOTO Failed; -- Enter error state? Forget Conv? [nb, convEvent] _ ThParty.CreateConversation[ credentials: cDesc.situation.self, state: $initiating ]; IF nb # $success THEN { ThSmartsPrivate.AssessDamage[nb, cDesc, convEvent]; GOTO Failed; } ELSE NoteNewTkState[cDesc, convEvent]; nb_ThParty.Alert[credentials: cDesc.situation.self, calledPartyID: calledPartyID]; IF nb # $success THEN GOTO Failed; -- enter error state? EXITS Failed => ThSmartsPrivate.ForgetConv[cDesc]; }}; CmdStopRinging: INTERNAL PROC[info: SmartsInfo] = { nb: Thrush.NB; cDesc: ConvDesc=ThSmartsPrivate.GetConvDesc[info]; IF cDesc=NIL THEN RETURN; SELECT cDesc.situation.self.state FROM $initiating, $ringback => NULL; -- ringing stopped before called party answered. ENDCASE => RETURN; -- already active or something; ignore end of ringing nb _ ThSmartsPrivate.ChangeState[cDesc, $idle, $terminating]; }; LarkTrunkProgress: PUBLIC ENTRY PROC[ interface: ThSmartsRpcControl.InterfaceRecord, shh: Thrush.SHHH, convEvent: Thrush.ConvEvent ] = { ENABLE UNWIND => NULL; -- RestoreInvariant; info: SmartsInfo = ThSmartsPrivate.GetSmartsInfo[smartsID: convEvent.self.smartsID]; IF info=NIL THEN { Report["No info at Progress", NIL]; RETURN; }; info.requests.QueueClientAction[QdLarkTrunkProgress, convEvent]; }; QdLarkTrunkProgress: ENTRY PROC[r: REF] = { ENABLE UNWIND => NULL; -- RestoreInvariant; convEvent: Thrush.ConvEvent = NARROW[r]; info: SmartsInfo = ThSmartsPrivate.GetSmartsInfo[smartsID: convEvent.self.smartsID]; cDesc: ConvDesc; convID: Thrush.ConversationID = convEvent.self.convID; whatNeedsDoing: WhatNeedsDoing; IF info=NIL THEN { Report["Smarts Info Missing at Progress", NIL]; RETURN; }; cDesc _ ThSmartsPrivate.GetConv[ info, convID, TRUE ]; IF convEvent.self.partyID = convEvent.other.partyID THEN { -- own state changed NoteNewTkState[cDesc, convEvent]; SELECT info.currentConvID FROM convID, nullConvID => NULL; ENDCASE => DoRejectCall[cDesc, convEvent]; RETURN; }; IF convID#info.currentConvID THEN { Report["Strange progress report", cDesc.info]; ThSmartsPrivate.ForgetConv[cDesc]; RETURN; }; whatNeedsDoing _ whatNeedsDoingIf[cDesc.situation.self.state][convEvent.other.state]; SELECT whatNeedsDoing FROM $noop, $ntiy => NULL; -- No action is needed, or we don't know what one to take. $imp => ERROR; -- This is supposed to be impossible! $xrep => Report["Didn't expect state change report", info]; $frgt => ThSmartsPrivate.ForgetConv[cDesc]; $invl => { Report["Invalid state transition", info]; []_ThSmartsPrivate.ChangeState[cDesc: cDesc, state: $idle, reason: $error, comment: "System Error: Invalid state transition"]; }; $idle => { nb2: Thrush.NB; cInfo: ThParty.ConversationInfo; [nb2, cInfo] _ ThParty.GetConversationInfo[convID: convEvent.self.convID]; IF nb2 # $success OR (cInfo.numParties-cInfo.numIdle) <= 1 THEN []_ThSmartsPrivate.ChangeState[cDesc, $idle, IF nb2#$success THEN $error ELSE $terminating]; }; $actv => [] _ ThSmartsPrivate.ChangeState[cDesc, $active]; $reac, $deac => NULL; ENDCASE => ERROR; }; LarkTrunkSubstitution: PUBLIC ENTRY PROC[ interface: ThSmartsRpcControl.InterfaceRecord, shh: Thrush.SHHH, convEvent: Thrush.ConvEvent, oldPartyID: Thrush.PartyID, newPartyID: Thrush.PartyID ] = { ENABLE UNWIND => NULL; -- RestoreInvariant; info: SmartsInfo = ThSmartsPrivate.GetSmartsInfo[smartsID: convEvent.self.smartsID]; IF info=NIL THEN { Report["No info at Substitution", NIL]; RETURN; }; info.requests.QueueClientAction[QdTkSubstitution, convEvent]; }; QdTkSubstitution: ENTRY PROC [r: REF] ~ { ENABLE UNWIND => NULL; -- RestoreInvariant; convEvent: Thrush.ConvEvent = NARROW[r]; info: SmartsInfo = ThSmartsPrivate.GetSmartsInfo[smartsID: convEvent.self.smartsID]; cDesc: ConvDesc; IF info=NIL THEN { Report["Smarts Info Missing at Substitution", NIL]; RETURN; }; cDesc _ ThSmartsPrivate.GetConv[ info, convEvent.self.convID, FALSE ]; IF cDesc=NIL THEN RETURN; NoteNewTkState[cDesc, convEvent]; }; QdNotification: ENTRY PROC [r: REF] ~ { ENABLE UNWIND => NULL; -- RestoreInvariant; cDesc: ConvDesc = NARROW[r]; IF cDesc.situation.self.state = $notified THEN [] _ ThSmartsPrivate.ChangeState[cDesc, $active]; }; DoRejectCall: INTERNAL PROC[cDesc: ConvDesc, convEvent: Thrush.ConvEvent] = { []_ThSmartsPrivate.ChangeState[cDesc, $idle, $busy]; }; NoteNewTkState: INTERNAL PROC[cDesc: ConvDesc, convEvent: Thrush.ConvEvent] ~ { OPEN now: cDesc.situation.self; nb: Thrush.NB; larkStateData: LIST OF REF ANY_NIL; state: StateInConv _ convEvent.self.state; previousState: StateInConv = cDesc.situation.self.state; info: ThSmartsPrivate.SmartsInfo = cDesc.info; larkInfo: ThSmartsPrivate.LarkInfo = info.larkInfo; cInfo: ThParty.ConversationInfo; larkState: ThSmartsPrivate.LarkState _ $none; savedCurrentConvID: Thrush.ConversationID; cDesc.situation _ convEvent^; IF state = previousState THEN RETURN; -- No conceivable value in acting. IF info.currentConvID=nullConvID THEN info.currentConvID _ cDesc.situation.self.convID; savedCurrentConvID _ info.currentConvID; SELECT state FROM $notified => info.requests.QueueClientAction[QdNotification, cDesc]; $idle => { IF larkInfo.forwardedCall THEN larkState _ $idle; ThSmartsPrivate.ForgetConv[cDesc]; }; $neverWas => ThSmartsPrivate.ForgetConv[cDesc]; $active => { phoneNumber: ROPE; [nb, phoneNumber] _ ThParty.DescribeParty[partyID: now.partyID, nameReq: $address]; IF nb = $success THEN [nb, cInfo] _ ThParty.GetConversationInfo[convID: now.convID]; larkState _ IF nb # $success OR phoneNumber=NIL OR cInfo.originator=now.partyID THEN trunkTalking ELSE trunkSignalling; larkStateData _ LIST[ ThSmartsPrivate.ComputeConnection[cDesc], NEW[Atom.DottedPairNode _ [$phoneNumber, phoneNumber]]]; }; ENDCASE; IF state > $idle AND cDesc.keyTable=NIL THEN { [nb, cDesc.keyTable] _ ThParty.GetKeyTable[credentials: now]; IF nb # $success THEN cDesc.keyTable_NIL; }; IF state>$idle AND cDesc.keyTable#info.larkInfo.keyTable THEN { larkStateData _ CONS[cDesc.keyTable, larkStateData]; larkState _ larkInfo.larkState; }; IF now.convID = savedCurrentConvID AND larkState # $none THEN ThSmartsPrivate.EnterLarkState[larkInfo, larkState, larkStateData]; }; Feep: PUBLIC ENTRY PROCEDURE [ -- Exports to voice service interface LarkFeep shhh: Thrush.SHHH, serviceID: Thrush.SmartsID, convID: Thrush.ConversationID, requestingParty: Thrush.PartyID, number: Rope.ROPE, noisy: BOOLEAN _ FALSE, on, off: CARDINAL_ 177777B] RETURNS [nb: Thrush.NB_$success] = { info: SmartsInfo = ThSmartsPrivate.GetSmartsInfo[smartsID: serviceID]; cDesc: ConvDesc; IF info = NIL THEN RETURN[$noSuchSmarts]; cDesc _ ThSmartsPrivate.GetConv[info, convID, FALSE]; IF cDesc = NIL THEN RETURN[$notInConv]; IF cDesc.situation.self.state # $active THEN RETURN[$convIdle]; IF noisy THEN number _ IO.PutFR["A%g", rope[number]]; IF on#177777B THEN number _ IO.PutFR["%gO%g", char[LOOPHOLE[on/10]], rope[number]]; IF off#177777B THEN number _ IO.PutFR["%gF%g", char[LOOPHOLE[off/10]], rope[number]]; ThSmartsPrivate.QueueFeeps[info, number]; }; LarkTrunkSmartsInit: Commander.CommandProc = { larkFeepSpec.interfaceName.instance _ NIL; larkFeepSpec.hostHint _ Thrush.noAddress; larkFeepUser _ VoiceUtils.MakeRName[style: rName, name: VoiceUtils.CmdOrToken[cmd: cmd, key: "ThrushServerInstance", default: "Strowger.Lark"]]; larkFeepPassword _ VoiceUtils.CurrentPasskey[VoiceUtils.CmdOrToken[ cmd: cmd, key: "ThrushServerPassword", default: "MFLFLX"]]; VoiceUtils.ReportFR["Initialize[LarkTrunkSmarts.Lark, %s]", $System, NIL, rope[larkFeepUser]]; }; WhatNeedsDoing: TYPE = ATOM; -- { whatNeedsDoingIf: ARRAY StateInConv OF ARRAY StateInConv OF WhatNeedsDoing _ [ [ $imp, $frgt, $frgt, $frgt, $frgt, $frgt, $frgt, $frgt, $frgt, $frgt, $frgt, $frgt], --neverWas [ $imp, $noop, $frgt, $noop, $noop, $noop, $noop, $noop, $noop, $noop, $noop, $ntiy ], -- idle [ $imp, $imp, $frgt, $imp, $imp, $imp, $imp, $imp, $imp, $imp, $imp, $imp ], -- failed [ $imp, $imp, $frgt, $imp, $imp, $imp, $imp, $imp, $imp, $imp, $imp, $imp ], -- reserved [ $imp, $imp, $frgt, $imp, $imp, $imp, $imp, $imp, $imp, $imp, $imp, $imp ], -- parsing [ $imp, $idle,$frgt, $invl, $invl, $invl, $xrep, $xrep, $xrep ,$actv, $actv, $actv ], -- initiating [ $imp, $idle,$frgt, $invl, $invl, $invl, $xrep, $noop, $noop, $ntiy, $noop, $ntiy ], -- notified [ $imp, $idle,$frgt, $invl, $invl, $invl, $invl, $invl, $invl, $invl, $invl, $invl ], -- ringback [ $imp, $idle,$frgt, $invl, $invl, $invl, $invl, $invl, $invl, $invl, $invl, $invl ], -- ringing [ $imp, $idle,$frgt, $invl, $invl, $invl, $xrep, $xrep, $noop, $ntiy, $noop, $ntiy ], -- canActivate [ $imp, $idle,$frgt, $invl, $invl, $invl, $xrep, $xrep, $noop, $ntiy, $reac, $deac ], -- active [ $imp, $idle,$frgt, $invl, $invl, $invl, $xrep, $xrep, $noop, $ntiy, $ntiy, $ntiy ] -- inactive (current ^) ]; ViewCmd: Commander.CommandProc = TRUSTED { Nice.View[pd, "Lark Trunk PD"]; }; Commander.Register["LarkTrunkSmarts", LarkTrunkSmartsInit, "LarkTrunkSmarts \nInitialize and Export LarkTrunkSmarts"]; Commander.Register["VuLarkTrunkSmarts", ViewCmd, "Program Management variables for Lark Trunk Smarts"]; }. ΚLarkTrunkSmartsImpl.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Last modified by D. Swinehart, June 1, 1986 4:56:44 pm PDT Note: Incoming calls are those from the Etherphone to the outside party, whom this system represents. Outgoing calls are those from the outside world to an Etherphone. Remember; trunk party is a representative of people in the public telephone system. See LarkSmartsImpl and LarkSmartsSupImpl for various comments guiding the structure of this module; it's simpler, but similar. Types and Constants Initialization There's no provision here at all for reregistration. Yet. Events RecordTrunkEvent handles events from the EtherPhone Trunk connection. The only interesting events at present are "ring" (place trunk->station call) and "tones F" (station->trunk call being "answered") The upstream code produces a single "enabled" event when the line starts ringing, and a single "disabled" event when it appears that ringing has stopped. This "disabled" event must be anticipated and ignored if the station answers (calling trunk can't hang up on completed call!) No idea what to do! This is pretty catastrophic, since no one else know about conv Complain about nb # $success here? Some party has changed state in a conversation we know about. (new conversation and we're idle) or (this is the one we like) We have to reject this, since we're already dealing with another conv. We are (still) willing to seriously consider only one conversation at a time. Situation is that someone else reported a change of theirs concerning a conversation we've expressed no interest in. I don't believe this should happen. Someone else's state changed in a conv. we're interested in; see if it means anything to us! A poacher has substituted for a poachee, or the other way around. Update state so that if it's us, we know who we are. Poacher has substituted for Poachee or vice/versa. No conversation state has changed, so just update our notion of our state and quit. Just codes to dispatch on in Supervisor; explained there $noop, $idle, $actv, $frgt, $reac, $deac, -- cases explained in code $invl, -- considered an invalid request $xrep, -- we got a report we feel we shouldn't have got $ntiy, -- not implemented yet $imp -- this situation should not arise even in the face of invalid requests }; If we're in the state identified by the row, and someone else in the conversation reports a transition onto the state identified by the column, what should we do? never idle failed resrv pars init notif rback ring canAc activ inact -- _ (other) (Clip this table to view without these comments.) This situation arises when we've forgotten about the conversation that somebody else is still reporting on. The actions of other parties are not of interest to us yet, since they're not in this conv. Ditto. They can either enter ringing to indicate interest, or go active without ringing We don't expect to hear from others while we're deciding whether to play They have earlier expressed interest noopringing), and are now joining the fray The only thing that interests us here is everybody else quitting. Debugging nonsense << Comments left over from previous system. Think about it all sometime. was in section involving analysis of new pending state. >> <> nb _ ChangeState[info, busy, "Sorry, trunk in use"]; RETURN; }; Swinehart, October 28, 1985 10:01:46 am PST Log => VoiceUtils, Handle => ID changes to: DIRECTORY, LarkTrunkSmartsImpl, Reseal, PartyID, nullID, nullConvID, RegisterTrunk, LarkTrunkParseEvent, CmdCall, TrunkSupervise Swinehart, May 17, 1986 5:37:22 pm PDT Cedar 6.1 changes to: RegisterTrunk Κa˜šœ™Icodešœ Οmœ1™™>—šžœ#˜*JšœF™FK™M——Kšžœ˜K˜—šžœžœ˜#J™™J˜.Jšœ"˜"Jšžœ˜Jšœ˜—J˜Jšœ\™\KšœU˜Ušžœž˜KšœžœŸ:˜PKšœžœŸ%˜4Kšœ;˜;K˜+šœ ˜ Kšœ)˜)šœ:˜:KšœC˜C—Kšœ˜—šœ ˜ Kšœ žœ"˜0KšœJ˜Jšžœžœ'ž˜?šœ,˜,Kšžœžœžœ˜/——K˜—Kšœ:˜:Kšœžœ˜Kšžœžœ˜—J˜J˜—š œžœžœžœ˜)J˜.Jšœ žœ˜Jšœ˜J˜J˜Jšœ˜KšžœžœžœŸ˜-J™wJšœT˜TJš žœžœžœ%žœžœ˜EJšœ=˜=J˜J˜—š œžœžœžœ˜)K™‡KšžœžœžœŸ˜.Kšœžœ˜(JšœT˜TJ˜Jš žœžœžœ1žœžœ˜QJšœ>žœ˜FJšžœžœžœžœ˜J˜!K˜J˜—š œžœžœžœ˜'KšžœžœžœŸ˜.Kšœžœ˜šžœ(ž˜/Kšœ1˜1—K˜J˜—š  œžœžœ2˜MK˜4Kšœ˜K˜—š œžœžœ2˜OKšžœ˜Kšœ žœ˜Kšœžœžœ˜#K˜*K˜8K˜.K˜3K˜ K˜-K˜*K˜KšžœžœžœŸ"˜HKšžœžœ2˜WKšœ(˜(šžœž˜KšœD˜D˜ Kšžœžœ˜1K˜"K˜—K˜/šœ ˜ K˜K˜SKšžœžœ?˜Tšœ ˜ Kš žœžœ žœžœžœžœ˜k—šœžœ˜Kšœ)˜)Kšžœ5˜8—K˜—Kšžœ˜—šžœžœžœžœ˜.Kšœ=˜=Kšžœžœžœ˜)K˜—šžœ žœ'žœ˜?Kšœžœ ˜4K˜K˜—šžœ!žœž˜=KšœC˜C—K˜——˜š  œžœžœž œŸ.˜MJšœ žœ]˜nJš œ žœ žœžœ žœ ˜FJšžœ žœ˜$JšœF˜FJšœ˜Jšžœžœžœžœ˜)Jšœ.žœ˜5Jšžœ žœžœžœ ˜'Jšžœ&žœžœ ˜?Jšžœžœ žœ˜5Jšžœ žœ žœžœ˜SJšžœ žœ žœžœ˜UJ˜)J˜J˜—š œ˜.Jšœ&žœ˜*J˜)šœ7˜7JšœX˜X—šœC˜CJ˜;—JšœEžœ˜^Jšœ˜J˜——šœžœžœŸ˜!J™8Jšœ*Ÿ™DJšœŸ ™'JšœŸ0™7JšœŸ™JšœŸH™MJ™J˜—š œžœ žœžœ žœ˜NJ™’J™JšΟfΠfsX™Yš£ZΡcfs ˜dJšœ1™1Jšœk™k—Jš£[€˜bJš£Z€ ˜dš£Z€ ˜eJšœ[™[—š£Z€ ˜dJšœ™—š£Ρbfs£,₯£₯£₯£₯£₯£€ ˜eJ™P—š£₯£M€ ˜eJ™H—š£₯£L€ ˜dJ™O—š£₯£M€ ˜dJ™A—Jš£₯£M€˜hJš£₯£=₯£₯£€ ˜cJš£₯£L€˜pJ˜—J˜J˜™šœ!žœ˜*Jšœ˜Jšœ˜—šœ¦˜¦J˜—Jšœg˜g—J˜J™J™J™…J™νJšœ5žœ™?™+K™Kšœ Οr€™Œ—™&K™ Kšœ ¦ ™—K™—…—;ŒZ·