DIRECTORY Basics USING [LongDiv, LongDivMod, LongMult, LongNumber], BasicTime USING [DayOfWeek, MonthOfYear, Pulses, Unpacked, unspecifiedZone, Zone, ZoneAndDST], BasicTimeExtra USING[DST], File USING [ SystemVolume, Volume], GermSwap USING [switches], PhysicalVolume USING [GetPhysical, Physical, PhysicalInfo, PhysicalRC, WriteTimeParameters], Process USING [Detach], ProcessorFace USING [GetClockPulses, GetGreenwichMeanTime, gmtEpoch, microsecondsPerHundredPulses, SetGreenwichMeanTime], PupDefs USING [GetFreePupBuffer, MsToTocks, PupBuffer, PupSocket, PupSocketDestroy, PupSocketMake, ReturnFreePupBuffer, SetPupContentsBytes], PupTypes USING [allHosts, allNets, fillInSocketID, miscSrvSoc, Pair]; BasicTimeImpl: CEDAR MONITOR IMPORTS Basics, File, GermSwap, PhysicalVolume, Process, ProcessorFace, PupDefs EXPORTS BasicTime, BasicTimeExtra, PhysicalVolume = { CARD: TYPE = LONG CARDINAL; knownTP: BasicTime.ZoneAndDST; knownTPValid: BOOL _ FALSE; OutOfRange: PUBLIC ERROR = CODE; TimeNotKnown: PUBLIC ERROR = CODE; TimeParametersNotKnown: PUBLIC ERROR = CODE; PulsesToMicroseconds: PUBLIC PROC [p: BasicTime.Pulses] RETURNS [ CARD ] = { RETURN [MultThenDiv[p, ProcessorFace.microsecondsPerHundredPulses, 100]]; }; PulsesToSeconds: PUBLIC PROC [p: BasicTime.Pulses] RETURNS [s: REAL ] = { RETURN [p*(ProcessorFace.microsecondsPerHundredPulses*1.0e-8)]; }; MicrosecondsToPulses: PUBLIC PROC [m: CARD] RETURNS [BasicTime.Pulses ] = { RETURN [MultThenDiv[m, 100, ProcessorFace.microsecondsPerHundredPulses]]; }; MultThenDiv: PROC [m1: CARD, m2: CARDINAL, dv: CARDINAL] RETURNS [result: CARD] = TRUSTED { OPEN mm1: LOOPHOLE[m1, num Basics.LongNumber]; t: MACHINE DEPENDENT RECORD [ SELECT OVERLAID * FROM separate => [low, mid, high: CARDINAL], lower => [lowlong: CARD, junk: CARDINAL], higher => [junk: CARDINAL, highlong: CARD], ENDCASE]; t.lowlong _ Basics.LongMult[mm1.lowbits, m2]; IF mm1.highbits # 0 THEN { t.highlong _ Basics.LongMult[mm1.highbits, m2] + t.mid; IF t.high # 0 THEN { OPEN q: LOOPHOLE[result, num Basics.LongNumber]; IF t.high >= dv THEN t.high _ t.high MOD dv; -- overflow; lowbits will be right [quotient: q.highbits, remainder: t.mid] _ Basics.LongDivMod[t.highlong, dv]; q.lowbits _ Basics.LongDiv[t.lowlong, dv]; RETURN }; }; RETURN [t.lowlong/LONG[dv]] }; GMT: PUBLIC TYPE = INT; gmtBaseYear: CARDINAL = 1968; gmtOrigin: GMT = 0; -- representation for midnight, January 1st 1968 GMT alto1968: CARD = -- our time origin in Alto/Pup format -- LONG[ (gmtBaseYear-1901) * 365 + (gmtBaseYear-1901)/4 ] * 24 * 60 * 60; altoExpiry: CARD = LAST[CARD] - alto1968; gmtExpiry: GMT = -- intersection of Alto expiry and 31 bits, minus "nullGMT" representation -- gmtOrigin + MIN[altoExpiry, LAST[INT] - gmtOrigin - 1] - 1; earliestGMT: PUBLIC GMT _ gmtOrigin + LONG[LAST[BasicTime.Zone]] * 60; latestGMT: PUBLIC GMT _ gmtExpiry; Now: PUBLIC PROC RETURNS [GMT] = TRUSTED { prTime: CARD _ LockedGetTime[]; IF prTime = ProcessorFace.gmtEpoch THEN { ok: BOOL; [ok, prTime] _ SetTimeAndTP[]; IF NOT ok THEN ERROR TimeNotKnown; }; RETURN [prTime-alto1968+gmtOrigin ] }; Period: PUBLIC PROC [from, to: GMT] RETURNS [INT] = { RETURN [to-from]; }; Update: PUBLIC PROC [base: GMT, period: INT] RETURNS [GMT] = { IF period IN [gmtOrigin-base .. gmtExpiry-base] THEN RETURN [base+period] ELSE ERROR OutOfRange; }; ToPupTime: PUBLIC PROC [g: GMT] RETURNS [ CARD ] = { RETURN [ alto1968+(g-gmtOrigin) ]; }; ToNSTime: PUBLIC PROC [g: GMT] RETURNS [ CARD ] = { RETURN [alto1968+(g-gmtOrigin)]; }; FromPupTime: PUBLIC PROC [p: CARD] RETURNS [ GMT ] = { IF p >= alto1968 THEN RETURN [(p-alto1968)+gmtOrigin] ELSE ERROR OutOfRange; }; FromNSTime: PUBLIC PROC [p: CARD] RETURNS [ GMT ] = { IF p >= alto1968 THEN RETURN [(p-alto1968)+gmtOrigin] ELSE ERROR OutOfRange; }; monthTable: ARRAY BasicTime.MonthOfYear OF CARDINAL = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]; startWeekday: BasicTime.DayOfWeek = Monday; -- January 1st 1968 was a Monday ControlledUnpack: PUBLIC PROC [time: GMT, zone: BasicTime.Zone, dst: BasicTimeExtra.DST _ unspecified, beginDST: [0..366] _ 0, endDST: [0..366] _ 0] RETURNS [unpacked: BasicTime.Unpacked] = { secs, mins, hrs: CARD; IF time < earliestGMT OR time > latestGMT THEN ERROR OutOfRange[]; secs _ time - gmtOrigin; mins _ secs / 60 - (IF zone = BasicTime.unspecifiedZone THEN 0 ELSE zone); hrs _ mins / 60; unpacked.second _ secs MOD 60; unpacked.minute _ mins MOD 60; unpacked.zone _ (IF zone = BasicTime.unspecifiedZone THEN 0 ELSE zone); unpacked.dst _ no; DO daysSinceBase: CARDINAL = hrs / 24; daysInFourYears: CARDINAL = ( 3*365 + 366 ); fourYears: CARDINAL = daysSinceBase / daysInFourYears; daysBeyondFourYears: CARDINAL = daysSinceBase MOD daysInFourYears; pseudoDaysBeyond: CARDINAL = -- corrected as if every year had 366 days daysBeyondFourYears + (MAX[31+29,daysBeyondFourYears]-(31+29))/365; oddYears: [0..4) = pseudoDaysBeyond / 366; dayOfYear: CARDINAL = (pseudoDaysBeyond MOD 366)+1; -- one's origin, 366-day year unpacked.year _ gmtBaseYear + 4*fourYears + oddYears; unpacked.weekday _ startWeekday; THROUGH [0..daysSinceBase MOD 7) DO unpacked.weekday _ SUCC[unpacked.weekday]; IF unpacked.weekday = unspecified THEN unpacked.weekday _ FIRST[BasicTime.DayOfWeek]; ENDLOOP; FOR month: BasicTime.MonthOfYear IN BasicTime.MonthOfYear[January..December] DO IF dayOfYear <= monthTable[SUCC[month]] THEN {unpacked.month _ month; EXIT}; REPEAT FINISHED => ERROR ENDLOOP; unpacked.day _ dayOfYear-monthTable[unpacked.month]; unpacked.hour _ hrs MOD 24; { yearStart: CARDINAL = fourYears * daysInFourYears + (IF oddYears = 0 THEN 0 ELSE 366 + (oddYears-1)*365); unpacked.daysThisYear _ daysSinceBase - yearStart; unpacked.secondsThisYear _ secs - ((LONG[yearStart] * 24) * 60) * 60; }; IF unpacked.dst = yes OR zone = BasicTime.unspecifiedZone OR dst = no OR (dst = unspecified AND NOT CheckDateGE[unpacked, beginDST, 2]) OR (dst = unspecified AND CheckDateGE[unpacked, endDST, 1]) THEN EXIT; hrs _ hrs+1; unpacked.dst _ yes; ENDLOOP; }; Unpack: PUBLIC PROC [time: GMT] RETURNS [ unpacked: BasicTime.Unpacked ] = { tp: BasicTime.ZoneAndDST = GetZoneAndDST[]; RETURN[ControlledUnpack[time, tp.zone, unspecified, tp.beginDST, tp.endDST]]; }; Pack: PUBLIC PROC [unpacked: BasicTime.Unpacked] RETURNS [time: GMT] = { tp: BasicTime.ZoneAndDST = GetZoneAndDST[]; daysInFourYears: CARDINAL = ( 3*365 + 366 ); yearsSinceBase: CARDINAL = IF unpacked.year < gmtBaseYear THEN ERROR OutOfRange ELSE unpacked.year - gmtBaseYear; oddYears: [0..4) = yearsSinceBase MOD 4; day: CARDINAL = -- day number in year -- monthTable[unpacked.month] + unpacked.day - 1--adjust to zero origin-- - -- monthTable assumes all February's have 29 days, so correct for that -- (IF oddYears#0 AND unpacked.month NOT IN [January..February] THEN 1 ELSE 0); daysSinceBase: CARDINAL = (yearsSinceBase/4) * daysInFourYears + oddYears * 365 + day + (IF oddYears # 0 THEN 1 ELSE 0) --leap day--; zone: [-720-60..+721] _ IF unpacked.zone # BasicTime.unspecifiedZone THEN unpacked.zone ELSE IF tp.zone # BasicTime.unspecifiedZone THEN tp.zone ELSE ERROR TimeParametersNotKnown[]; SELECT unpacked.dst FROM unspecified => { unpacked.weekday _ startWeekday; THROUGH [0..daysSinceBase MOD 7) DO unpacked.weekday _ SUCC[unpacked.weekday]; IF unpacked.weekday = unspecified THEN unpacked.weekday _ FIRST[BasicTime.DayOfWeek]; ENDLOOP; IF tp.zone = BasicTime.unspecifiedZone THEN ERROR TimeParametersNotKnown[]; IF CheckDateGE[unpacked,tp.beginDST,2] AND ~CheckDateGE[unpacked,tp.endDST,2] THEN zone _ zone - 60; }; yes => zone _ zone - 60; no => NULL; ENDCASE => ERROR; RETURN [gmtOrigin + ((LONG[daysSinceBase] * 24 + unpacked.hour) * 60 + unpacked.minute + zone) * 60 + unpacked.second] }; CheckDateGE: PROC [unpacked: BasicTime.Unpacked, dstDay, dstHour: CARDINAL] RETURNS [BOOL] = { day: CARDINAL _ monthTable[unpacked.month] + unpacked.day; SELECT TRUE FROM day < dstDay-6 => RETURN [FALSE]; -- before the interesting week day > dstDay => RETURN [TRUE]; -- after the interesting week unpacked.weekday = Sunday => RETURN [unpacked.hour >= dstHour]; -- critical Sunday ENDCASE => { THROUGH BasicTime.DayOfWeek[FIRST[BasicTime.DayOfWeek]..unpacked.weekday) DO day _ day-1; ENDLOOP; -- calculate day number of preceding Sunday RETURN [day > dstDay-6] -- before/after the critical Sunday } }; --VolumeFormat.--TimeParameters: PUBLIC TYPE = MACHINE DEPENDENT RECORD[ direction(0:0..0): { west(0), east(1) }, -- California is west zone(0:1..4): [0..12], zoneMinutes(1:0..6): [0..60), beginDST(0:5..15): [0..366], -- April 30 is 121 endDST(1:7..15): [0..366] ]; SetTime: PUBLIC PROC = TRUSTED { [] _ SetTimeAndTP[]; }; SetZoneAndDST: PUBLIC ENTRY PROC [tp: BasicTime.ZoneAndDST] = { ENABLE UNWIND => NULL; clientTP: TimeParameters = [ direction: IF tp.zone > 0 THEN west ELSE east, zone: ABS[tp.zone] / 60, zoneMinutes: ABS[tp.zone] MOD 60, beginDST: tp.beginDST, endDST: tp.endDST]; sysPhys: PhysicalVolume.Physical = GetSysPhys[]; IF sysPhys # NIL THEN [] _ PhysicalVolume.WriteTimeParameters[sysPhys, TRUE, clientTP]; ConvertPhysTP[clientTP]; }; GetZoneAndDST: PUBLIC PROC RETURNS [tp: BasicTime.ZoneAndDST] = { ENABLE UNWIND => NULL; IF knownTPValid OR SetTimeAndTP[].ok OR TPFromDisk[] THEN RETURN [knownTP]; RETURN [ [zone: BasicTime.unspecifiedZone, beginDST: 366, endDST: 366] ]; }; SetTimeAndTP: PROC RETURNS [ok: BOOL _ FALSE, secs: CARD _ ProcessorFace.gmtEpoch --NS/Pilot--] = { netTP: TimeParameters; IF GermSwap.switches[c] THEN RETURN; -- => communication package isn't running [ok, netTP, secs] _ TimeFromEthernet[]; IF NOT ok THEN [ok, netTP, secs] _ TimeFromEthernetOne[]; IF NOT ok THEN RETURN; LockedSetTime[netTP, secs]; }; LockedGetTime: ENTRY PROC RETURNS [CARD] = TRUSTED { RETURN [ProcessorFace.GetGreenwichMeanTime[]]; }; LockedSetTime: ENTRY PROC [netTP: TimeParameters, secs: CARD--NS/Pilot--] = { IF NOT knownTPValid THEN { sysPhys: PhysicalVolume.Physical = GetSysPhys[]; IF sysPhys # NIL THEN [] _ PhysicalVolume.WriteTimeParameters[sysPhys, TRUE, netTP]; ConvertPhysTP[netTP]; }; TRUSTED {ProcessorFace.SetGreenwichMeanTime[secs]}; }; TPFromDisk: ENTRY PROC RETURNS [BOOL] = { sysPhys: PhysicalVolume.Physical = GetSysPhys[]; sysPhysStatus: PhysicalVolume.PhysicalRC; physTP: TimeParameters; physTPOK: BOOL; IF sysPhys = NIL OR knownTPValid THEN RETURN [FALSE]; [rootStatus: sysPhysStatus, timeValid: physTPOK, time: physTP] _ PhysicalVolume.PhysicalInfo[sysPhys]; IF sysPhysStatus # ok OR NOT physTPOK THEN RETURN [FALSE]; ConvertPhysTP[physTP]; RETURN [TRUE] -- even although we haven't set the clock value? }; ConvertPhysTP: INTERNAL PROC [physTP: TimeParameters] = { knownTP _ [ zone: physTP.zone*60 + physTP.zoneMinutes, beginDST: physTP.beginDST, endDST: physTP.endDST]; IF physTP.direction # west THEN knownTP.zone _ -knownTP.zone; knownTPValid _ TRUE; }; GetSysPhys: PROC RETURNS [PhysicalVolume.Physical] = { sys: File.Volume = File.SystemVolume[]; RETURN [ IF sys = NIL THEN NIL ELSE PhysicalVolume.GetPhysical[sys] ]; }; TimeFromEthernet: PROC RETURNS [physTPOK: BOOL, physTP: TimeParameters, time: CARD] = { physTPOK _ FALSE; }; TimeFromEthernetOne: PROC RETURNS [physTPOK: BOOL, physTP: TimeParameters, time: CARD] = TRUSTED { id: PupTypes.Pair = LOOPHOLE[ProcessorFace.GetClockPulses[]]; soc: PupDefs.PupSocket = PupDefs.PupSocketMake[ PupTypes.fillInSocketID, [net: PupTypes.allNets, host: PupTypes.allHosts, socket: PupTypes.miscSrvSoc], PupDefs.MsToTocks[500]]; THROUGH [0..10) DO { ENABLE UNWIND => PupDefs.PupSocketDestroy[soc]; send: PupDefs.PupBuffer = PupDefs.GetFreePupBuffer[]; send.pupType _ dateAltoRequest; send.pupID _ id; PupDefs.SetPupContentsBytes[send, 0]; soc.put[send]; DO recv: PupDefs.PupBuffer = soc.get[]; IF recv = NIL THEN EXIT; IF recv.pupType = dateAltoIs AND recv.pupID = id THEN { PupTime: TYPE = MACHINE DEPENDENT RECORD [ timeHigh, timeLow: CARDINAL, direction: { west(0), east(1) }, zone: [0..127], zoneMinutes: [0..255], beginDST, endDST: CARDINAL ]; data: PupTime = LOOPHOLE[@recv.pupWords, LONG POINTER TO PupTime]^; PupDefs.ReturnFreePupBuffer[recv]; physTP _ [ direction: IF data.direction = west THEN west ELSE east, zone: data.zone, zoneMinutes: data.zoneMinutes, beginDST: data.beginDST, endDST: data.endDST]; time _ LOOPHOLE[ Basics.LongNumber[num[lowbits: data.timeLow, highbits: data.timeHigh]]]; GOTO done } ELSE PupDefs.ReturnFreePupBuffer[recv]; ENDLOOP; } REPEAT done => physTPOK _ TRUE; FINISHED => physTPOK _ FALSE ENDLOOP; PupDefs.PupSocketDestroy[soc]; }; TRUSTED { Process.Detach [FORK SetTime[]]; }; }. ŠBasicTimeImpl.mesa: low-level time facilities Copyright c 1985, 1986 by Xerox Corporation. All rights reserved. Russ Atkinson (RRA) March 31, 1986 8:12:42 pm PST Sturgis, March 27, 1986 10:54:01 am PST The monitor locks protects access to ProcessorFace (GetGreenwichMeanTime & SetGreenwichMeanTime) global frame (knownTP, knownTPValid) disk parameters (PhysicalVolume.WriteTimeParameters & PhysicalVolume.PhysicalInfo) Global variables Sampling this need not be locked because it can only change in ONE direction, and that will only happen after knownTP is set correctly. Errors Part I: Fine-grain timer Notice that we sample ProcessorFace.microsecondsPerHundredPulses every time through this routine. The (minor) extra cost is paid to deal with possibly changing clocks. Also, we are biased towards floating point multiplies as being faster than 32-bit multiplies. Have to do triple divide t.high is 0, so let mesa do the work... Part II: Packed times means "unset clock" Returns the time "period" seconds later than "base" (earlier iff "period" is negative). Raises "OutOfRange" if the result would be before 1968 or after 2036. Returns time according to the Alto/Pup time standard. No errors. Returns time according to the Xerox NS protocol time standard. No errors. Accepts time according to the Alto/Pup time standard. Raises "OutOfRange" for times earlier than 1968. Accepts time according to the Xerox NS protocol time standard. Raises "OutOfRange" for times beyond about 2036. Part III: Packing and unpacking once if not dst, twice if dst Raises "OutOfRange" for times before 1968 or after 2036. Part IV: Finding and setting time and zone/dst information This is an ENTRY procedure to protect ProcessorFace.GetGreenwichMeanTime from interference by ProcessorFace.SetGreenwichMeanTime. This is an ENTRY procedure to protect ConvertPhysTP and ProcessorFace.SetGreenwichMeanTime. how often to transmit request ΚΜ˜codešœ.™.Kšœ Οmœ7™BJ™1K™'—˜šΟk ˜ Kšœžœ-˜9Kšœ žœO˜^Kšœžœ˜Kšœžœ˜#Kšœ žœ ˜KšœžœH˜\Kšœžœ ˜Kšœžœf˜yKšœžœ€˜Kšœ žœ7˜E——K˜head2šœžœž˜KšžœH˜OKšžœ,žœ˜5K˜Kšžœžœžœžœ˜K˜šœ$™$Kšœ;™;Kšœ$™$KšœR™R——K˜™K˜Kšœ˜šœžœžœ˜Kšœ‡™‡—K˜—šœ™K˜Kšœ žœžœžœ˜ K˜Kšœžœžœžœ˜"K˜Kšœžœžœžœ˜,K˜—K™˜š Οnœžœžœžœžœ˜LKšžœC˜IKšœ˜—K˜š Ÿœžœžœžœžœ˜IKšœ‡™‡Kšžœ9˜?Kšœ˜—K˜š Ÿœžœžœžœžœ˜KKšžœC˜IKšœ˜—K˜šŸ œžœžœžœžœžœ žœžœ˜[Kšžœžœ˜.š œžœž œžœžœžœž˜4Kšœžœ˜'Kšœžœžœ˜)Kšœžœ žœ˜+Kšžœ˜ —K˜-šžœžœ˜K˜7šžœ žœ˜Kšžœžœ ˜0Kšœ™KšžœžœžœΟc"˜OK˜MK˜*Kšž˜Kšœ˜—Kšœ˜—Kšœ'™'Kšžœ žœ˜Kšœ˜—K˜—Kšœ™˜Kšžœžœžœžœ˜K˜Kšœ žœ˜K˜Kšœ žœ 4˜HK˜šœ žœ (˜9KšžœC˜G—K˜Kšœ žœžœžœ ˜)K˜šœ žœ M˜^Kšœ žœ žœžœ˜;—K˜Kš œ žœžœžœžœ˜FK˜Kšœ žœžœ ˜"K˜š Ÿœžœžœžœžœžœ˜*Kšœžœ˜šžœ!žœ˜)Kšœ™Kšœžœ˜ Kšœ˜Kšžœžœžœžœ˜"Kšœ˜—Kšžœ˜#Kšœ˜K˜—š Ÿœžœžœ žœžœžœ˜5Kšžœ ˜Kšœ˜K˜—šŸœžœžœžœ žœžœžœžœ˜>Kšœž™žšžœžœ#˜/Kšžœžœ˜Kšžœžœ ˜—Kšœ˜K˜—š Ÿ œžœžœžœžœžœ˜4KšœA™AKšžœ˜"Kšœ˜K˜—š Ÿœžœžœžœžœžœ˜3KšœJ™JKšžœ˜ Kšœ˜K˜—š Ÿ œžœžœžœžœžœ˜6Kšœg™gKš žœžœžœžœžœ ˜LKšœ˜K˜—š Ÿ œžœžœžœžœžœ˜5Kšœp™pKš žœžœžœžœžœ ˜LKšœ˜K˜——šœ™K˜šœ žœžœžœ˜5Kšœ=˜=K˜—šœ,  ˜LK˜—š Ÿœžœžœžœmžœ#˜ΏKšœžœ˜Kšžœžœžœžœ˜BKšœ˜Kšœžœ"žœžœ˜JKšœ˜Kšœžœ˜Kšœžœ˜Kšœžœ"žœžœ˜GKšœ˜šž˜Kšœ™Kšœžœ ˜#Kšœžœ˜,Kšœ žœ#˜6Kšœžœžœ˜Bšœžœ *˜GKšœžœ)˜C—Kšœ*˜*Kšœ žœžœ  ˜QKšœ5˜5Kšœ ˜ šžœžœž˜#Kšœžœ˜*šžœž˜&Kšœžœ˜.—Kšžœ˜—šžœžœ)ž˜OKšžœžœžœžœ˜LKšžœžœž˜Kšžœ˜—Kšœ4˜4Kšœžœ˜šœ˜šœ žœ ˜3Kšœžœžœžœ˜5—Kšœ2˜2Kšœ$žœ˜EKšœ˜—šžœ˜Kšžœ!˜#Kšžœ ˜ Kšžœžœ$˜AKšžœžœ!˜;Kšžœžœ˜ —K˜ Kšœ˜Kšžœ˜—Kšœ˜K˜—š Ÿœžœžœžœžœ%˜LKšœ+˜+KšžœG˜MKšœ˜K˜—š Ÿœžœžœžœžœ˜HKšœ8™8Kšœ+˜+Kšœžœ˜,šœžœžœ˜9Kšžœžœ ˜Kšžœ˜!—Kšœ"žœ˜(šœžœ ˜(Kšœ- ˜FKšœ I˜KKš œžœ žœžœžœžœžœ˜L—šœžœ˜Kšœ$˜$Kšœ˜Kš œžœžœžœ  œ˜/—šœ˜Kšžœ*˜,Kšžœ˜šžœžœ$˜+Kšžœ˜ Kšžœžœ˜$——šžœž˜šœ˜Kšœ ˜ šžœžœž˜#Kšœžœ˜*šžœž˜&Kšœžœ˜.—Kšžœ˜—Kšžœ%žœžœ˜Kšžœ%žœ#˜MKšžœ˜—Kšœ˜—Kšœ˜Kšœžœ˜ —Kšžœžœ˜Kšžœžœ\˜vKšœ˜K˜—š Ÿ œžœ0žœžœžœ˜^Kšœžœ-˜:šžœžœž˜Kšœžœžœ ˜@Kšœžœžœ ˜K˜K˜Kšœ ˜/K˜K˜—K˜šŸœžœžœžœ˜ Kšœ˜Kšœ˜K˜—šŸ œžœžœžœ˜?Kšžœžœžœ˜šœ˜Kšœ žœ žœžœ˜.Kšœžœ˜Kšœ žœ žœ˜!Kšœ˜Kšœ˜—Kšœ0˜0Kšžœ žœžœ2žœ ˜WKšœ˜Kšœ˜K˜—šŸ œžœžœžœ˜AKšžœžœžœ˜Kš žœžœžœžœžœ ˜KKšžœC˜IKšœ˜K˜—šŸ œžœžœžœžœžœ  œ˜cK˜Kšžœžœžœ )˜NKšœ'˜'Kšžœžœžœ+˜9Kšžœžœžœžœ˜Kšœ˜Kšœ˜K˜—š Ÿ œžœžœžœžœžœ˜4Kšœ žœq™Kšžœ(˜.Kšœ˜K˜—š Ÿ œžœžœž  œ˜MKšœ žœK™[šžœžœžœ˜Kšœ0˜0Kšžœ žœžœ2žœ ˜TKšœ˜Kšœ˜—Kšžœ,˜3Kšœ˜K˜—š Ÿ œžœžœžœžœ˜)Kšœ0˜0Kšœ)˜)K˜Kšœ žœ˜Kš žœ žœžœžœžœžœ˜5šœ@˜@Kšœ%˜%—Kš žœžœžœ žœžœžœ˜:Kšœ˜Kšžœžœ 0˜>Kšœ˜K˜—šŸ œžœžœ˜9˜ K˜*K˜K˜—Kšžœžœ˜=Kšœžœ˜Kšœ˜K˜—šŸ œžœžœ˜6K˜'Kš žœžœžœžœžœžœ#˜FKšœ˜K˜—š Ÿœžœžœ žœ žœ˜WKšœ žœ˜Kšœ˜K˜—š Ÿœžœžœ žœ žœžœ˜bKšœžœ!˜=šœ/˜/Kšœ˜KšœN˜NKšœ˜—šžœ ž˜šœ˜Kšœ™Kšžœžœ"˜/K˜5Kšœ˜K˜K˜%K˜šž˜Kšœ$˜$Kšžœžœžœžœ˜šžœžœ˜0šžœ˜š œ žœžœž œžœ˜*Kšœžœ˜Kšœ ˜ K˜K˜Kšœž˜K˜—Kš œžœžœžœžœ ˜CK˜"˜ Kšœ žœžœžœ˜8Kšœ˜Kšœ˜Kšœ˜Kšœ˜—šœžœ˜KšœH˜H—Kšžœ˜ Kšœ˜—Kšžœ#˜'——Kšžœ˜Kšœ˜—šž˜Kšœžœ˜Kšžœž˜—Kšžœ˜—Kšœ˜Kšœ˜K˜—šžœ˜ Kšœžœ ˜ Kšœ˜—K˜—Kšœ˜K˜K˜—…—1Ir