snowfall/0000755000175100001440000000000012607473775012137 5ustar hornikuserssnowfall/inst/0000755000175100001440000000000012254301027013066 5ustar hornikuserssnowfall/inst/doc/0000755000175100001440000000000012254301027013633 5ustar hornikuserssnowfall/inst/doc/snowfall.pdf0000644000175100001440000101443612254301027016164 0ustar hornikusers%PDF-1.5 % 3 0 obj << /Length 1915 /Filter /FlateDecode >> stream xڥXK6WTbH[6@RE{HrX?RIw}E49Ȧ͛/]2DV6\&ZUN eSz96UIuˤ~6ṝL4-R)d4nܮG'0ɒ~WMJN E1VS9LgH~Wm?‹v*X bɋ5e fcgOָ 4rۦUay/@zt$GVΔie  ŭ@yObt%*SWoP[ ؖ{uTThP5d;6L\\ZR0n‹U G8zn[Elg[LrK!t:+K^1R1.Ynd?a܈v,( =}֌rWuJUi/uuZfSḐ8w/ q\'%YU i@zS9^Pjѹˈ 궟 Lc`qvS3?eʐ@M]I2k@(*hƽk~ǥ`[>C$=+OI%O++}H"[:DoMS~#VH\ +x1 *- 8X ӝȁ Lnj2C])8by/6@;H᭳8V;=yYb3ո|S+lyNMy&.KsP߰9WY7st<̗ZqKkYq Ȃ %9rRɽFxѫKi!2No Yg,ڕ,R+̚]+-S'v9 G2O?9g@"%Q#ڤ&/~$N 3`y-yxs9ْ|eϢqe7H y)aLYF$/}29Jna"^̭|e>ݷTtHRzb<Nx9Z:J,.2&?pZ%>EOeվٸ;Fz$-3 FXw(S:a+)"B`1;屍Kc7:UncW)݈Zpkj<)# [ {ONVYH_Z L˚aFf!dӯKۗhAfł?X,@*6Pr^8\u g K熿 [bэs܏@{GJ6WAQ@L/C=hC^!78֒-L/0%:nj m[۩wG(שA2 uڙ܊^[>$ЇK5WQ#öO q[q#w` T6yZK]8pc\P8kΡ4y_|[ODW?6a1?;c"s Zsˢ_{W>n|Ym.6 [{ VT$Iu].Ԡ*4^G> stream xYKs6WHΔ ^lOӺv2I~ȢJq@,*v<@Z$poýWoHSjc`x:Z\t5dm.Ye~-t;*ŠPԵy2/jUe Ze봙yi2d06fPHWh,a'oh2Y^hM˦ O&㼨jyazJGI+-4PeƤD~?Bf%fD˂:mGlpdEOFMxV~T<8]hKzvT]};_4_R=1S3NEĂo4q/D6o/D橪_SLyM\ os_-a@噓i}V; /XaG,Gfcnx vW(n]]IvKs c=:"`68b#jvkpJU UR Hm+[ZaTK"*T&:u%qIf#g K)6prr"Ӟjސ=VD>8qR{$ .ew{MYG\$0X! !^i݋u JǬ\"l`VE B&v5?\,іm+ ɟI!f=oQ—^8]0-cS8Ta-7lNhȋ~KWV筗s] |+K2)X j0U,cy+Wz{3_Ƙڬ]LE1ːɔ@P1^T[SߏTZmQJy$t=A5#)84c E^Q-^'Ecayy-֕u_{_hX endstream endobj 23 0 obj << /Length 2124 /Filter /FlateDecode >> stream xڽXK6ϯ Bn^M%L46!$GUjuW7_e*P&&K@iYdi%: /lJpب6: ahiͻo4%.qM(/%31F^VwDn=* ,(2K2TG:sL,] :oբg:D;:x#6^},\q%[h,w p{;s5jHpdb9ogz΅E|QqhI\c/{Q3ң/:RmIr&=Tȅv͍m 6#Pd;MEe 1[D&5#Fᐌ2 b^, wCPbLH6J' P?0O$IGJж)+r/I$#OߣHds/G8-/w8L97N3?6*/8}(g5vVdECfN\˒(ȶ \_$ E9t_70"\EK8+h[%WbIau'6 R^wtƔLJ<bD;pƗº\ORK=fYT{abb|.;g$ӕ: U-_W4 q`L6Vx]3\u#' l^m+IeQT,hrHxzbĀ&ß5AU$#U90i| C`[ԇB:{tc5NyxUҾ8{ >j+ Ce_/p#@2x8^T«aC{wq 픅fcMr*G3Ij^y]9ΡH~4j !>ZrJ|e>`sN p݋1_#ן)Ͳ w~i,}c.ԟC]#TBz^x1̾(i(oi+<F:kOq4V{!x9<+9Pv@bo,}5 XZק,sVLyY(ÐPRdٲ!&]>#1of2痖dX g~]rSILt{]ho^~{ _O$byFہ]m|K[YQ`G'^2q9 {3zqp0V3ך_2&BMazwr:3/&UiQWڐ <s&W5_LK. GRFx5p  dQ:/ ⫸:m8EϹa0ƬRϺ]&>`zJ,3 հET u/}w ;J !B%rSEW/NhQ׋+Iju 8gZ|;`@ pbdO;2oa"3<4:C; ƭ[ZQ i12)d3RûG{A t|Nj(Z>h櫏gX<yN+f%ILdƃ=N4JZKZp/8}3Z,[]pU{<:!h$1*)A<8r-ϋHO%C7_vw8P1^ wqEv"x&#BQB67?ܼω R( L XM3R)2(Q~pkMV@c)Fh==%y=xf`Xx}8zh wT}%p9ЄRqK -)ݣDgnə+y6c#v=}8`i|G4?m; 2L)a};Vt:I_41~pvƔd;yIBhȱi^˻$.̹: yBz% l%P~Bs!@7,4aCQÒØK9l%`|u endstream endobj 30 0 obj << /Length 2159 /Filter /FlateDecode >> stream xڭYKH+rt$HH!f`y8q6fng¬]]]]WOv"\LVE~EaL/t֫[tbij;°ߞg]|0WuizB' "| vHGr?3L̄~D|. b{wyE|!ڸy+CGkm):Q70<17..y$@w)y\^dQEM)[=eKN1Lgႄ"UKtYRx#d{{>HhAIG5SPJ3x륱'$Vxy%_M~9O8M I?6f C:$_zL$[oˆt?)Y-;bBRa;ȳP!}G<3oo~A9c)V-6mLGUڢX(?jJ1 0ٲYpL Z.HOgqyS:JY>2^erYb;&s$Dַ-ee] l$ T%1d'P C#n͒0O8'D6p_5E !6Tw`MJX @G:V-I a\:G'}W0@ZpށW?6IA fZb@ǝJV+,l# +1<Шwƙn@ 5%<$5A]?ɯ(/( "˩ \:4o1te$4":CL2|-x͝:a]Zuq\+M1w9Z) tjFPœњ!&tf>ϱZH7 h6zWYx/X[M-j2sV"IvHȻzVbl?>2EZf ZROF:p yBrţl PWHT6M%,[R3+ɎNXdN݌֥hJNÜC%UGOnaBT7ZY:0Fs,M$ނMCz:y/IԞRgS-Q%[ vuN GP\uR%ErYuwIl=ޢ(E!?ήK6ƤLMs([Um׹q}KS0ELOEf&!Hz鱟v ?ccA +i^h#;ՒZơRm$N* ~ UЋ^*u,sHn~eeD@Oz򁀟 K*=HNp^54MψR$AL͈_]N  v( endstream endobj 33 0 obj << /Length 2810 /Filter /FlateDecode >> stream xڭKs6_4!v6gӝfwrh{%Z"UQN$/PEjfm$ILnֳZ-(ꅞ~^^W^,,_Zu3\0;eQC!V,^G-e ȯٶs-V'A@E{a@%ά]? &~Ku&S6,Д#HGнkpURd7bUlU^naa28?.&%*!'R<9U*0kL>M lBd2&qV6p=%kG+WH"I]x;KU9q XYR-Jo. bΖƪ(.NV9UsE2᠙^$ɼ<9Cuo_7_+Rۜzz-jڨ' EK)k` 3$[2(JB'ulyS)cיV u.⧑a\Ȕj8!SZ&8MdUZ5Wgk)l?95'·C䬜P1" GY0uGR>aB0uU|lHU2AEHEʍ8BJ0#5vCp-D:FZـ1>N7 \ґ^ʦ=Zh;g4 z<8~.ri) zbvF]s8pv3dEuL&Kʾ;lxg,MSȔnơd uI.+JIHz;3cBdQL0^T{K8a'DRmL9RiLޚ^`{Hw dC,qzw࠶@EGg] !ʘ/*Nn0]mx|  > dH4^5C`V9r7a귖\Qb(\ǂHhpCTr"#嵮D}Dqza鄃 }ThDg !SvaGEǏw5ńjaUҲ㤹s" r[?uaCVWL g1S& UkbdRdI䕄sȝ SҡA9@I*kRy#{X[yr^Tlx2MI䕓ߟVw"[n=3]Pק6JY|F3k*V8F띫c*60Kp}MJl…ٓ0'VȤm*WX~G`vD& 6) ay5f"àALbZ1,zȯ{VNU&?X}#e.bMZ))qXLbל|'Bw SN"RVQaoHu]f_qN VڶbX݈9Awe^N3be_=Mbʚ_nLxC0rv{VNMQc?".F8 [6L UrBޝ:BŹ-)wusةv8jd O4ΉDuT)Xd"=i,&n$X]vFݑqdsԇAiQ`7PшۂLNԄPOLj5TknJJ|4Fz|R7]{ ? $hذW6EDƉ'и_&Owu`-IĘyTeS{}=dpPs6VL4rʎrqYOUVtYq]H CNwt|sʊb9l5 U+ ,Hz X4]knl5S auDsO]EW ~r[ӃAK{n}f:A"ASp?L/r"jUC-K ]us+4hg:U_(VfkzPU~0gԲ*/۽+i2mUA]=ӭe"9;yK"{1?n LwyCH.`BV85z{'2H܇n#@TRx ݇YJ;w7>@|r rw$)DB8ns_ IX endstream endobj 36 0 obj << /Length 2051 /Filter /FlateDecode >> stream xڽXK6WjE C)ڴH\e[mvSms遐Hy~37nhVegvY1ṫD qNƟ=vjnVDn >gHdA6x 8)=BL:w[t4jЉBpT'iJ yd,@1+2f6TPb ?nNX, SUZjT=Yle~jF%M?N, I2 kդDyNp[{BUui:vr.xتgN9ܴ}S?|UGvO0_YX>o?nLve,}.{`+"gkv"(÷@V2Gb:8%pC3Ym,9UGN 58R\=B][Gle-n/JKq^X8X v<".=> %PQa7U-[ʐ\!ىV1l#1Mu+6ϣ zr[^)"k^oq@͕G$r}FN@7Fi&"% %og$EԧE-h2'ɢYeR8bDbE+-J"CR ^ {Ng`V>UBvazTk4,QD@&{ʼu+b 3Y},$D'?}!/thP4A|m"qo,  %.N(N,HqVO'U;ګzt9Y q֌JM/ eꇉxȆ#qCV}S^ظTT͝6KLDS;J>uѳv7d኷j}rp|&Xϐ&6iҸw;AG* )~yW0SR>: K)k&-|SL]*aX}q!V{< O[_9v{'N]Cov'P) {!53RW{w"2.;j uԹgtx|_.Yn֗vI GSwNCQɵGXhG[wG]u+R ˦nJ1Eϗq*qP`=U9)4_;(nǍ3q2!} {o ~z|݋ܢ/ze~2ͬ;{Wo7! endstream endobj 39 0 obj << /Length 2052 /Filter /FlateDecode >> stream xڽYI6W! VDR0ـS䠱YZv&A޷ْZQ~~T͖2l3Si͒8 ggy{`|q q!ÓgQA1ZW=LBW2.ءDKS.]J>{hKrWQN4=):{:ZE.doZzRyp۰i[\8|Ws{Pw#ݭ TaH(ړא]n, Dd JEyޖ̼B4eaz`JN ZBTu%6/Sr:amsfyB j)Tʎ[[Bn}s gla(ݢn8d ڎw̺l.VGg3Zl yzH6Mͻ4lzω/<>~UgɦwYrX[}FݨI"Ҹߊ̏UXYkzy'ŭѦU &yE-A1M"mьHLH|}`Q/qwAl-Pf5F VǕ)rBK4p4ִ/6G9>bu/e-M7:ʚosVͮ(00ڡ -2^[9pĨ> stream xn0@fp32acqO9Pjp7"ɷmU %Jz[^L^-s*kel^.>d.u//0ya|kEY0Dx)?ӗX\J;3I85[(('TBlS5PkyG&0+Ĭ#_o]4 DWcWWU-TSQg/s].=J0::7eپl̇V;/yL*'/6TKkwrMeDZPM0&B XTi|{D `hVj`d~:Pq#Qnhܸ .E;k(ƴJƪW,4r:OUuz؃1Jda#p(/[Ads@{KldIo k/p.uor- #bOZU k>CU ^Ob^ vPdkp&9219D6jD,=LDqo{qLF^=DhD`ihmO͠VW \L.=οuޟؙvznm UKCdT(o%Ivq;\$F׏^0gJ#53IaiB=p*ty'bkjkEVUZ;*7ZrעgݙQ"n6#8}ڧn|?T0*3;6"ܤ)-ٵwta^`WR$R\QTw!)ZLkPƵSbs JF8}boVQ'IFIV2>y"BGippdxީm^'VWtwo"t}f 8mۼ:{dduT\׭mB5IdK=8IR2-зkJ.gQ !bӳ@+\`Eber\m\>oӚ53\:PgVP{,/? Fvn%P:h+!}Rrr08d'89?yˠfcɊyd/jZuTD &>G_V|R1x]&I"f==O}7hɩ".}|Uad%!T v:ۆo;StL`uv>"9[[' Fx*~؍0B>v('TƷWs41KO '۹Jۥ{h=nZ#/Q8ޟ{۸mECSfS#ٲ?S]Hbwc35*zBeÐȳI_<"G2Z IK)'f0HNMQ̬ʲ U2;l /80NV&o\>bndkA_WېRFo6`s8 3d`=X F{dר'wsoQ(SCaVl~8$яڗ4#؈*Hg/x{z7\rz&~E.y]_X腶VU]so>%wfD[X`-Y-h4u~mlBm@6ifMrsmVs2sݺem{ɼ[Cp{MduO3 k^ygZR-y6˓] f^=r9.]r> stream xڽXKFWXKEJ*EU`m{^3 67Cbaf8w~~Hhb]$JEE^.r]Dҋuxt$a000?,C;<[YDBGo%I 0:%0&=? E,Z9{-o<1>0Պڽsqx#(ILg_߳1|ν: {2FA_wk3_)7bI+{M!eZ,t=P\*aŃNhX08д\띄zIa97|ݲ=*uW`EBEijjb3~0˴>-u;LJeu1A@4˴ ـY  _#B|}U ?L yy'j㬣gJlr~Qf/M&]zשucH`$#="2̲RzcŁ0}鷢e? |s= M)b tck^?{|b@l`BH8o (M^7atx))IJna"5Ij_]]M=qDMT,!$p4P5q,y޶'W{-A/ySlS?z__rH5<,Sȟ  Y9d)^B5-ppak;,|C]#OK-so\4Q 0-4B(7UFሃMҊ_ֈpH}6 Gv34̮!)M)vCJH܀FolPϞM؏qIG\8hDZzQdUpAm΋2 )`?n*##jt9LM"ک( OmZẃ3ZƈF~7]I.K3uvJ#am- [bk^qM 4怎VT#ب'pI*1mQ،?OC9J;CvKABجOHri19Bh6g-}hanf,LL?X']I?EX? Yu endstream endobj 51 0 obj << /Length 2682 /Filter /FlateDecode >> stream xYo=`r9/c[&@Iva;>e1E(ś,ڢř73}?ܼu}dM˳2MV,B%}uTF%0y ]%M&ylw;ދ8Ya ?"$`nkϛjcG `wsگ^񏛿gRYSLë?ϯ@:4˟Ň'OXMt+nl ^xANISE%狴y"M>/RL^g"/:3CBUfrpx3eP^"AX :M2+utWtQ:AƯᗅϥ0uNG;Ox H6ELXpY(72FTY9=J:S0Rܐ Jur=yj[!{bSE2nIHZ$Ac,5inH [/Y䖥H4mxE?D|o=B*Ant%v%%cbx؉]_ 'Yl1v'wiIQ~litKpKBtZz*9xf8=DKR~c^x]fƗ޳Yemz%s}iٗRLcX^X3{i|Ea1M9 -#_H`\Ͳ Alhᐥ^&z0R+waa9r{Ăߐ?slcQrNҎ p̒.E^>̇*Buw )oE439A!Ycvt{贙eJ7hFK҉nMcXOctH'TSbzp)S#űXYr_dlHz&:+DX\Gr'c eN,?( ;6:$#|@@ q 8KLcj~PggUiC|qL2ykvڈ"BK:k-$+'_ `(*erNG?RI9St$eM~,<_o؇93"CP; AV9,.] b+˵8?mg L"s[X+|szyMV6~*n#{fļX vbOqvZF"%Dc"|Mx"eEkDcu>sGNm(J@AC ]:06.WXP@pɽCpIqL=5 %fC,O8N: 򫐵lAQh>Y&fb3So1s?Ik\A֗MǰY8`Q+8Mw92 )!k9Lʼnregu-fdk7. ~A_ք辺'o.]G^ҸF''Z%E{q0P!M I`Z@)k\o^C_y/7۾ endstream endobj 55 0 obj << /Length 2573 /Filter /FlateDecode >> stream xڝɎF_!̉,⚛㌁1v ΁(RRTyk(@$U}Oo?T: S,ڸ,Ei%z^]+Sڨ=sfi.GC\eF?/+G{,VܪtUF vG/~I{U?L rHd2/,RAE!Np|OӞI{B?DעBC}f6z"}.Wւj/$pĴUg^ r^Mtێkr78^&lD 6@"oD>bz4&VWƖ*ฌ3*#:q;%'d$U䃤Jv$!II#G<.L +Ohp/Zz$EwEyȢ:_-,ϣ,Ҭ{cISvcUu1q" ɋ*z'kK&c`K<3Z5{H /{Oe:9,9ϩ($i٤TlF)H@ qR4F\Su)7*AMLΘ|.'3xe-QGIJ3U%^.w9e¨&:+[hfcVBLhq9:g(a9!N *Vi_3sV͒΍$㽗gM%a|K50Ri[g~YLq6B] ^(sj¹)sn"aS` ލ\(oIh_52^<Δfc b,qb,fJ~%;x*긲,| ;wi\^A!X0Kn@>0yN sTqإ,S.ށaX27Q8O?,$䱸*%E~R@iq6MaaCQfQVD;tlA3DzK2 HC'0?4wg*MmX 31.7G<[5\ lb UoOޚ1ݙ_T :Q%lÝ >~qaf ,zVSP`(@)QI}IW֡?/ X:pOwt΃ ~xxK-V:w_s&YhZ?6:Y";* 8k"-wT r5[]:!a=}E6ۖzMh?։wW4! )C*$ P\go1ZVL!︣ =#B4l[vr ;*MltL6(zK@rJ~`w|48[s NblF p!S)#< q! oo؝UFb/'#eۆ2 jg"q5UL/%H3S !>оiMym#9ᶾ"#WX< Vž4+N L%P pPC;M|ӭ1N2׈~2q}ǜ>;[Fj >BOC*ddR1BiIv3vXI 3o$ JH7FWok<=*;KZ4<:HBVb'6VU/4qވ@)_XHr$'i& ڱ&as-Emd鎞 -{f47x*X-Nh P06 80N|l//tᑧM5ݛ…ŕ_ɰxw7+bk:@.EnyV T'ϣ?9 AA0uS,盝́ǯBJ U::oThI?HKC d@Krew'hhiF-Q*4pMl7;K&1}eMU;?UH>>kPc~s&PDT͚{;| T(ߨTweJ(QV?;)M Bǁ\4rk>IJn<\GK޾ç1Pdf`832d?%)oG[RkmrrQui+ (UWYqJncG{D+E>Su Sn K)|k~n/0t tqI[V3p>1\)InO 9hZL[D^.QAxjq"R}NM#>=sH> stream xڭYIFW*T5`i6W%N%Ǯ899 -,ϿЌN~-[{u6˃thyWS0P@kIA]&;mbY2 9ȦY nQF \$5|ӟM[S,@y~;UIVIm GbrIS e L'YH~%&dOYR:9)4.X7UrJH_ƥ(Hq 6W`H1^Kk> XKj=_X3a} w{Єpg1肀j[cBwZ[5TC$ j/t:&b, u { ",I_'ҷ(? y$B&fxyh9 5hZ1 jS_բT x|B%Y̳֐ KbkZdV!a:5ڢZ/Ӗ]gj] Iԧ$[醙B!s-Wm@𨣇v[W`H7M&4 rmlmW}}t/.jhY[qݳ9G'{]?*G!Iy7~Dou_[6A&_1S T-p)G4IdÍ  t+V<Ϟ;<5>|o> BW)Nɠ۶>y?MN6%.kP~ ]cV~\>¾Gr;76@|aniD{0LDc{2}q~Ta`Bp6pUC]WˇPN?*EcwԬR/O߰KYp  KPa ]`[^+ajۃpL{u>:' xݓʭ endstream endobj 62 0 obj << /Length 1874 /Filter /FlateDecode >> stream xڝXK6W=I@̈ CdhHEӃl^wmɕHIwpًWWh$1jXei>IL8̖Bp q4MV+ߴR6WWY4Z,*OIˠq?:jyj>)T'N̩?`9]Ƀ([E eϏ0QmmZע,QFCk~g[ FCQIlFLoHU\$uS_ M QHy%P}ht*u#ŶCf`TA1&X'KD(Nh%dKn,،A̱p&)8Dljba'󪯢(7 РTLYf2 4Wq˰6 _JehΩ dCt'컠cagIppr+|hCӣ}Ԫ4֧|&Rnv 8()"Y3#Aa}1D 3<;fO4ۧHz^ g;Io>-Cj^|p@DE9"Xܻ -*7ex~(0'v 'zO'D{ bmq. Xmc81s9t/Г\H2?!.!mܽi~½P(a2nLm*A) =twqgnj' ǣ[ ;٢{E5 l b-=ٹS/l(L~¤eU 'v}1)g*.2VBmEűC$_b+s@ƫ=QNWƅ 8;}=]rɾϙˤ2S tFӀ"E 21@W¦qZx3HH>m`FG l") g0 9@9P QρFO[!d]簐vڋ}"r؝`I }P,;"S(Ɇ!|[H۪0Q1)Fe4 RJ;M9".ˆV]w}j(l\>/Yf޹ߝHYB~ؤb ,p;.FPeF6OKc\Hƥ!z"%5{*HqUCHeDxI{=Eޡ ϕ.Xq`Yc PIL,2(YmM=$"S\H'sVeN%OqId|m Ɖ.( bwYCR Ee{b\w{@9gj%d-7W)jS=}H7)lK|~#V="t%ex*d͠M (5V\ȍSծ?p{VJzpD^џbeʱ׬UaGWpOsͻ2 ]*w~S7"g{B (, ^?ě++D:b4;Dӓ*j)/(M.]'{ +WUM\ۨ"> stream xڵXo6~_@dK0 )6Ś={PlYvIe')ϏQcs72u<&/^z7(F&Mf?|x6j޸t5Ee>% GZ8 -&eY09Ú;Ph!߭k[&!%h[>)~?~",2S1\DkPi/ UJH, X`h"A7eu1ZqhY6aG6EX X)(ߙKSoQ8w`%y^>4F7.38?Tvx 8hazŋqo}/#ÑH3.*U$A]菗J,"29 2 Y#YPDZ;N}*"5ފ0Q6<72_m$V8Mdh9aJ,cݶ|d: <5 :"v:=$OnzG&` ^U$ q"Ga/DӲV%Sa=ձK97;(S޴ĥwj[96CA LŒ V|f_lvG\m]"km,f >MyB=ѧ.dOHմkHNJkV3QٖQBj7oeztI%i(S0&eΛQЍp.W\ ĽIu Y6u$ݺpPt`)> stream xXIs6WHD0IIHX4Z,3}Ȳf|A"<<{ ы* ˁ ;>Sd>>pqr"ҥ*||:Z&K`\.< Gd7LR;8@-t1y!0ڒ o{VT/0L}SòI3p-x Mwa (Hcx94-"Ĥ`cu©O[)ށ!C\.O.foChp\L׻7 RHΩZ5E|(MgTzu>n):8coi;1,My˂"ZgF..AVĀYFd2#Kb౫FCoHue6ʀܵ,}0R(CAx[PgD/p8 _>&KtH:)6 V,9aʘ@wCݪs%U0Pq<džC1)AHC/ Kj@EI:[3grV5{y2ݶv}kYhn8tdlCǞ( 8Lm^U ARgT0f`8f:CL2aءs|IclS`~ "zVK@F<9s-$ga;$Ѡ0Yht:Xo}Pp18?F䞼nyBbuDqבGδ&mm.<ߋ4e5MbP}?[*C߃@CZD`rZ783CF9dC @AH&LO;h{1uzEmn*=xL9n endstream endobj 91 0 obj << /Length1 2091 /Length2 16419 /Length3 0 /Length 17677 /Filter /FlateDecode >> stream xڌP\ 4! @w'k X@pwww<ιszckνh*OL&vF );[g&6fV>+J9Ζ Lnhg sqظxXY쬬chZrv '*q;{G 3s|a jr0 @kB ;;󱰸1m̈́nd2@ h75f*ӿvn@G]`ma uzwq59޳TeA2V#17ܿ da3haak0>J)0;;3&@ kѻߥR;s2vwvbv#_a,ik"ngcuvB> G{=X}Vvn^A&0qgQppJ]Gfrpp@9_ S;GN "_"_`,2EzSx,/E鿈zT+S3hA4>#`1/zY7?zbs?Lދ6_j;8rX?{_0,vW?Q}A^cq|/N WlG/uv{ wwoyw9+uK@ w1✝1e}pC(4ծfעw'du;^mI[%WFo/* 8Ǣ DLj"{ޯVm]rT.(1˗G*p#O1EGPe83c\Mc&g@9(`y\TcwƧ#;HÝ*-Z ܗ0+O 1IB&ewWGk q%5 iWUib5PA+j 3.zX<>1;A̭,_G ऴt)ŒGQ<I!B/yL6k|}ƞ>5$uř4bbMt~۞pfHl feC9):eD9duBF4/2yZ.|qy RFԛ`N3% =;kQ'p)HֱIg l^Lk0 KMR` |S?c,ҵz.\ϩAuf0%tMܒ -I"Q\~s?$qt$tgW| ]t eYѹX5#)E"VCέ̒Dޞ]_>ʓ>_MɈ.t Wdd: OYs!v!JI!i[23oaIq$-mkd=B V$;~Z Xdu.aaF K `L?hsN~5FkG&F0.8o&Ti1S.Jq&ՅXћim69*A3-4/7 vC?f:tGo[S1 9Cs5Eھ_S"ՙ%#79r}A)}%[d/:#նMjߘn,4Y.-v郄 _ uףV&~9~~Gt ȉoPs:*uB\јY-HhdVTV">%bV#i8>=M)zOCgě=~.WAGm4E[W&j"DZ~ԍY@H?=phڸ҂+ukr5gOMj!ϥ+~oX(°@Ht@+h4ȡW7lN6k1?$tK!Y hp t-}{)(&%4r:#`5|S$NCG?sZo[@5{KpMPDyC'HxƦ6(mCLq1J\Te˸z]-B<|/|T ; _-ɃV[E#+sg[zriFwZKApaӛ(^dIx!lѨK>hŏ mGWa1Re^XGHfwŁ.dE]]0GҬa\93BMe a𒽻Ma2*RGZh+.V]Ӆ؇Ʋx/Kz90cg?/3*- &2_kZT` ݂J*|G2!9+ggMJhBk(C%a,2 $1#Tb6L<H8٪_/%HfYcq")͝ٵ!]I' v].^4GPeFRSN=$94N+ 2/(<##^7 lt0/JVfmK2=7O9߯[|`GR\ދ iTa%+npM_Gׂmf'=V]--;cB3W 3r~òd;QS,gE=J'HS5n}H?^b0e"*i?2tvn">2wL"Y[_8ЯM!ژWp>rlBRQ(|_^ЬJa?A*p2B3 Y-whF>E{iiV!.(:- 7>6NƹFk"[T8ox x_=Me̺L.̝_o/7jcW s'؍ؔ6x[}H$k?Q&^J9Wyc tmJGs1^#)7˦"1,t2z?u6~XoɣY|Da*f6i~ah𛘆4߆ΎlHWZ;C30(JdacV%'Exkp93'="ɚց|_YFy21` ^=ic$f;]&Ѵ0E!LUPaM6><[ڳKy_O*IKgq߫|<*w- YHĘMR9-!UvZ@=d䧓uj^ij¹ɚï4fmH,#KKEW3؞5F^;,.Ud T\jJ~\|+~&mhP?׷DixE<b֚}l8`N35Hpԓ_^:,gƘŕ5)NsgiYR0@;7:_T_*, \Ŭw#ð^=`s";zkGT8UYքv u9#NbP\N<jZ&Sr38M`> m(|} S\-*Q~L@'6u\4vtRo`?^ ;h7 E^_g4wQ۹~dһMZ)Nc'ЊPFB攒Y F CLIO|.ӡ ,8P$/` CmcHh7ֿYg6RB\lӳ RIa=`h~2 ߁3,q{\KQ`s۔?,osj)?9Uoktj3O H ߯n]yQ96S^x%:VQu8n6`X0{ZՐQ$$\XW!` f _t=߆>ˋ;$X%mSc ["")_>Kj"|(|k_>=?OfekU[UR*\> U{toF&u^RUD "fbPBqw$HKXc͙ה{d.:G9 @/N?).-xAضב!vbc4t(AxSp6dM٥ åYa Zf5Isdgf{hhS?@FI4| 4Q֩š(#$_HFisͿ|+ ,yuxBmZ- rJLob 2뇗Z\V.Y2Өo%/?flBCn?[uFt[&?Uxʨ#DGg49lDi7O˥Jm#c0j7Ky˙{iǗD~c?@^RGNFG=ºs]|>Sy`9$+zXP8<φ_5+>] ȉAJ]x1Z^cԙ_߯h,?:}ضP˩q&k[wUدt{_ɹl)d(HƵH <pUa ^CEJVY>)cRo*:ߝ-P}JpYT18F} u6c~sP!i}j 7wJ]HڱV΢昨Z{}A/內ٽBO ݊ϵ@6ELsaC5$]f5plIB\ kJq pey"t@<)>5rcz[\Bs}R:%Jf*NC=i=_ܺ^Q*Q'}l֚` =~Wzb D*L% -QDM7X# ~UFbۺrU12t\kpѻ3Mq-#dGJƁy#]4}QiSnZ\ :'=GdBI/|\__UOu v2U7Aя2cY/c G2ϡ "\iW8L 1G yud@I3 ofdHBѰrynsnG1JgGu~yaUkgs ~ZwzD-[~mGGYSSZ?#٪ՊmvycK$8'F֟+`ľ ?4tژ1&x6:t8C2[@06 Ow`*%$(+yO0()"Rv2&kUpĨ|{wuܭ< faMNr-a4pl3걓]DQ{SITHM #vF:RfϺx;svթG" ؅LT+c*GWS͵`pD ѶT&g = .QSkN5z|g?: b.ѱsž@[F I Zu$_T8&y*Qr;H-9.}mYJSL߈N##-V_Y7(O*h˶p9m&.aT```$^ rURqpebB&o3*Neo>\[jH|=_ 4^SA$nR,F*L~8)VmUCIgw|]d~$tZ(¼,;,Ԩ$b:z3'=}uV7Y8r~{GZrBl/̈hܙ-!X),23DH~ڑsR>xl ƍ>C.^lemQ623.UX*m`7nja zr\Im=~3Ȟt[l!c(%V9"1}?8v9L@@o/Clc-pu@p7k zʩqo1m<4.[ݵ`9'v"߿h.PF#[ -W[x?,tΏFFO"^ h/#GL9 ŐF/,.Yf6RK3gHuch8҃z4~<&Zo0?ZVlDf_ jyJI  Dm!+SWpW'I QrlӅ P{H F)7+|~VfZJN0D5݆Ű7Έ4SǐE TqAE&,#y!S:u*E3v)%ծR~k+^6Tw'Z(+e 7Rf"-ةVJEr{FM%p,; 5#C[$v6vHp0AYYV8A8 Lf8rᔭrpK YsR^ i,ε"JY7 Ah~z}pvUO7uqvuh(҆20rr=@Ц'r?oVr Axx%?L MA)iqneJ'r\,Q@(Ed1+{oi?w_Q>u]p:fp 2F*;H!hfgW1u Y)x^uqV/xV$)o5ɨ AEviiwTVNUԿxN hh&NFۆI <;mzdthnL~XYoUt#)K= M@Zr71N}~GjwV (.e1qų\=UwvāL26>ͅRѕ3̢4u,3ר#Ga/fZl΍_p_0` r9s6Lc. K,iuI [wiCL0y?0a, h" :*u)4[sUshc} GrmaZ]}j9}.OFoL"P؀UIbdl0vQ _c:jtJ/uN0 7>f6#m"po;N-=B;E]ʣx̳򠷢\&Tj0~׬"'3Yaw(kȩ;%FY]qmF;Ab,z>B@= IJ~MQey-s$7 2.;y o83yi s)^Vko w TiK{A̦n}$|x׏Op|qHd*d>3/6*hGrێVK@ ? Y8wU'Nՠ{M!CF#24ߐiNq$-SXW2g*v+ŎD <}@&u$mq̉ܯ^:zc@I =؂dKRH:iU\x/j %#p~)y]M,R+A9pq*fgIBII5\!(ZrӔҎ`OWj#WN {3߅0Ʊ WSc÷IasŭXGo.8m.觟9d>H#azt{j+ZD VB6y]‡MeX|N>74~D0|T2w%HYv]S8xPUɣ[=_aia+q[ǰR!mރ Y< Gĩoh݂C}A>`Wcekd!lA'Uڇ|0y٧nMdhQt@"r.>;G)ь8r~Ypu=BAb_ҕٕaqo+eiL@kC /􀋲+a3X*%efHpktGA dܐ(Q< c{m*^*LxC@$< -mF\v8ًE2j6 ME8dmJs@1rtzmXFvq5Ln!wv]`9Ϩf)i,6\A4VfZ舾+/(c{m+`&2 [8/4ɑM[.)2&'5Ѥ@4m IՒ+eSú}!SXҨUمn0_ ǹ r,Ъ,Z kgr<݆5T5֒8ΥֺS cwOQ, #tWe*+#u<~/p|d2,1Q ݽ>@۾.11PeLD| /qm2MLuK/g.Y#%?2֝ŘFT+,}DH+W*7XY'+*F~HN "vxr@CJI{a 70"*6شm5ռ6@ m&, \yxS24 E)G"{΂LYٱf@ߤ-to4bT%7PhfSA0!UN R9u@ˋ Pc v˯P8DgkiK5,bW&u@9@#fUEuZ 4<<:[VU%y$c.zc'J]b=ض,*G \CDr-1:F&_`mUG' WĹ!Hw-I%(eLA0D2JS/i*Z ?fz`6?&e暽 |cqN2Ki,rv,0ŚjGpIyO֜VEdδP&&j탞3ZB։1M8>l13% %cNP6 $D"eRF?/f(Q[D*Zv 4U{wK'ʠ|Y GWp|aZY/8y:ktO6[fQO6JZz1F1 &}Dn!U<䶸n8ՓRОͧ[#_akxKӾlQzXbkӽkMYYhB WD@85"EO+7{I/ks$2FvT@AP9k='Ǒ18YWGq7(6)_)ȹCm4T2E4YpH*318ل1 ʍ06?,82xe"AO|Ua9oN=Nhg°tq/0],E=Yaʼn'(~a` 4? )Mp=rV oCOnjvf|uMx+ ͝]![Gْbm[Ю6P/ZΖ4Eҳ U12QҵJԏ1 'R&)f߁ |$N.ѤkW0;⧍`(J3.i)6gsUOh^Խ`!e)Wvh-U<&Ϸ"c }OJ-t"(\VBvzD]Q|gebj Q8Ih=deKJiOEJX,DhwXWMYVh7,*Ԝ|udAU˪j0Ժ<"N d8o6$b{y*03LGEFG/i۞)aS3?v\MAN4csՕ1HM^Sb FhG3q@,s2)Uv:gF{l$&SzY@˂|+Pdjo :!qNcSVYy"OlX;CdZF~ B9L[>w;)Lci g4Am8B9BF_䉊jpفqU^\X_.bG l:;vzEĖѡmsH%g u<<:޳V}@ QN]G//(Ё90 RmP^5/=q${6gOnx8ɄȔ87H[bPa֛vcMRdEcɓ>g"e=;Ӻ%h[') 0v N܄x0TdmМ/ KuV2 O>FŘm""y𡊑Yڠl>\!pNYYR^Хʐ)ø1H]ݵ,J@VVsxp UEkN'UhVO;R"iAVt55$fFo`ԊTȂS+8 H*' nVI3g8xPyCq0WشaQCGVVy}dY7G 3hM(,@,T#w-]7Y9(S>#!(Ueue( V;E;`gM&2ښe&'i9fE$XcUD$eMrYŃuXRH˅Ӛ~+ e !so>_iG*fGxR*mCG|Вυ6|],#t #%&-n(r.|/5Xߢ1]m ۤD{}q()~ʱVg{LfP4%eH%թ>jatЭkOƏaDZ2>uj-$-** $ `r~SbzVGb$$p5ݚ͡ԮVF8=|-va θ`FXv{oJPA[fJ4Fζ%sBlٓy䌮)2^v GHx2Kt 6u֙,ײ|6՘5>BfjWXfɡEN$>L {_i)߫XUt0>\8Ba_|sň㖆[l_3~cȀǔj{ywm:L2c;jLƪ+218&b3ۇ@_( EDeA<K/l+e :+9ArrGBrulmx`O'ۗ#E(WQ eK_ӏ:+bTpQ ^OYHzReQ?mVR6 ?ҳL2UzY`۩Wsij͖TJ|(ޖ`G0DWRe~3aIVhjɼ2EKRt@^lQX!#|ݡ3,_Yh)f%,F&^?_ތ|IH|(1xjM!ƐT.+u̘`T%nhEm^whi?(,ӽ@C: 8S Y\&(ˇDjEN۩ºA)J1缗^f:,=!rl›v޴:%5J,k=,vOMo0U{'[E]`jՓZb6~Dǖ嫐Me& z$郌wލL#;5Hl 2;k>nyT!uc=8ດ6Fx׸6OLz!44VAוH IL `K18`fda3 g ցT(DQ+ TYVF>,uE- ^{zK>r&+*FE^dukÄFB~үP'ߒoپr rD껵G Ϙ|mU}A Nc2p3&gB僎C +^A]8Zb]GĶˑ'+G)9?H|!o MoUgjߏ!ai=g^*g)%(ǥ&?%xYN kpaN] dk)F3:z60?]:4\F`VѦD‹'?W0fmpq\ڸblG,7Hh$k86@0 rʼn~ZQLZNi -4 yP^of|q(It¼G6)Z ^f5 ]\,oL$/{f#V0~dVϫiOͫ^R~ F(-Om&9l+y 0Wt(/IbM|}_]?X5Ȟ0ir0=wJӲ쳦X Ua< ڃ'8InX'̻C:>NO%$jgS%?w(nD<ejpTGeBB߉>AI=N'+2dwZ=Բ??Tcp?yTy2q" σ;8dV㋄Z.@^P3vt0Ckھ{* As]iFjlL tˬ+^~A-)Zo2 zB_HZCvK/']ŁalaM`vX{ΖiWAKTA[-N錄;#Be(+43Gp"FT bJJͿ\kG$)lm"BD Rd_DXy;מL$2t@"-]?QB^Bx^I{Dsk)H> stream xڍP apw'ww!Cp`s+`V޽zw)(ITELAv.L,|1yQ-V6 ; % ?vJ ?bN ˻MNȸX\||,,6@7KS<@@)fdin~>hLh DlAN&@;<d~ job r4..|L@[g&{'sAZ@ rr Pڂ- fa/; x7XC\LANrEݿr"0+;DvMLmvv3K@QRÅ3q-mK$EwldliFҼYTdW}N {2r,Lang 7݄frpps@_y:ve~`0{4Av.N :!L-M\ sK;? w貼+察~0S{;?YD[CLFߒx3r8YnNooձ(mgfGۿ'kC 3z,,&X?/!f,+tO/hkio<_&_ -joc}. 3o-%-=@J.&{rK;_wQq~]#%LM=6N. >`lo%5y=f&;{8_7>NbE,f?,?Af"8?蝩3U ^^;A<f?l_I̦J@o?_n{WSs Of^?{ {}Cd^{Cog2sce__3׻}w-P^N@nPNw~s3X8]~q|:sz_࿿@  `_5"{{NQ`h37EFH x6~iKPnl2׎438Ux"R?@ OĨ&F5d+x e+R>/~) {\?#J(`\0.=Pg1rHd|N؋u6٢־9SCaPy&.zȄF,4 v9jIǦ QwfBcL)EGlTs0rK~ 8+wxETe: Z)}I닷|G#u0yk ?puCpqn7 וԔ\49pՂ4#Y`ƛ 8aY xL[BIЊ0梣ڞki.h&:݊OjL+9j~n@17y@:R 09֫4aPB3X:N7OEA^{|#$( [L3&m8nx)4:_9eΥ׃{=Ku汀9iKʷX5b#\-1֚ܘAl>KmlHmX]Z}.>RTRGb/YtFe*8G`Q}^ g"͔ ԡR*4zmP?ӟ).1HɈWox#U:>e'^< XݴQxF.Mb,D qު9dj<_5ׇœzVoD *n ȣ mc&5cC<g06"vp{!n˼4SiK4'y]qN*ߛ]&FᘤfJ>peKIM{p <^ u8z3G|kt|$~AOUվ뾴PR9XSs_`R~uڦ[?Nf9&T MEOl+ۂĕngKøy,$z h=cC~r.kqCsb\"ieH|jF[kKPlC27NȧM(H0}"0=$[?dsx+Fikso9)BOT$%,/7+I9fDi# #~$?h}+f b%4ZzE-0_gnEغ{j'fqߧGvK F]"S$_ijϐޘ!^*̚G(@Ci[r5uGcFs Sւ+zi'UOxwF2zz"U aY:Mj(F!Dcɂ/7:1e_c"b=vb!Z,orfngOrKzVd>^"L?q~nj^ %Q@Zp{ Wd*mYd1Lv42$Ap`Ǘ<.T8QQf2sq,V [0 S}Gr6nQօy((8ߓqS0=6\ *FTCr.i8q3J_/"ΖE<8h ]TDx__~u3I<=oy?I" >Wc͹/K}sV-<rI-li\e|*痢K娎,'[g=N/I;nغ!S 8ED viZO l #w:1UWZlL2a[ oXN[* *Oר~]A+?)nsɦ}ʚFCTMOpS4]e\W*In=0*ρ[HRZ*5 {Tj612W|Ogg\]$M 5Phsu;$woG*g!No0g>rTf2y]'EFьBwoNp`|T)>?#=l#-6YR_gTxI;5}jM[oJW4HxN58fmqhDE^8Hv6n!e0:oo.u]$ӼdL4f0/]%WuКY"\S2BȨըiq13|S cx:&:߲zurP&Gυ3n1|h/kA-ODQf6;Q0^]q?"hək4S*o>7fHE92dm41r1>~PՁ#K#˻MWֻmFEEPRG Pm2z{ uM;} MKU"qi'r^akK~Յ.W|-aċ[; +y; 3i$[R)jumjBpK ޲dѾ*`8^aW"GlCieA EsNjdUP-8 T`@U*1m <W;W"dL I..Y  bڝw`T: S0u|C!ٷ/xPVsD=+>?bT!+4A0 :?lz>2)큦g͝ł y`ER4iw< Ǎ`G J%_ yj5'>6Y=4wvBb3dIX\hŢ]p'Dإ˻ig"~ArF>ylk=ocMќӕt0DtU[ex1wta4M'"9$c|FE"b1Z]*R"._MHX\7zT8iϜ)|i7&Pע]R:99Vk+K`cӬPlY@KeD4yl#]tXmYH>H0F Ҿ48 ^>}.Yum[g]}o-(;J2/Z~DxN$ZZ,( !w~%Lڣsk@e\k{fRX׼9y%|Yd[9k>n>?i8s'rP_Q0%A/% 3_nRϪz[ $ID1k(EB=2Фj)}a@#&mE񇒗GiET[PIhVPř_qk}IW/[h7xHB߻qDvy; )tQ >LP^W8 cNӀ 9:[n25O]`Q֗u#u@~l3mHkX=4\]WD+uݬ!vѝom| JPc]3WORwrD%\ud6wzq>jv5Ք⪪d5‖*#?9`gtAC CIY I" #.;>jS<'9&mAV ,1\7}Kz.U?M9blpsLPaUnN;8okۼbb ێw3:OsiKo#oiO2#8SQim tm['C8]P$0>?paGmvDë|iT\ 4Dl{Uխ !X MȚ}rT/f rթH>ĦS443 BpW 6a.. "p:ne lY)z[ih]1'g0 OCET ^V@*H;ޅyJx=O{/+}SQu}4ǂytI'?@"cpOyM UH-A95)eֹFr[wS?߂T'܈"~mF2sCF>jSud%,گMt&|x^*~FuV/2 ki@2|(C@5c\%*[cTfȓ1U)>xXb\nt eѻB~y^w F\yhgH׊>BT~=VuJ?sE𧋳] S, #=;Z-bOsmw`8]m4M9)1\,|7+s6oz~zQsʤ}P`6U(jy9z^@-a7۩2_3a 'ش7Z  Gxi;):b7'0O$2 ۷r kJnfUΞ~SUeY R寮E#>Z`%7tܠ,`5.\#EXUkH^ F <-_첳8r?}7Qtl* ;{ 64RoMajl@#3PBd-w9b\?{>-?C "f'jvK]Gb6Zp󹔤%uTwLW:^JNl\ W$ECnb7zT{GwPX(,WAteid(,tYO^="7]xuf^~ao/OMq=6ОȠeP wc ,R7p+  {qjC J_J"K { ėаVLk NNM=|F;nh$~qI7e5'@ -鈛W'j?W!q?F.1멌u1y{SBNz"/G |f6s緸3U$b`g/I]"Oѣ i@V L UMUl>cyF^eebsA36+rud7_J:iJ XM8Ho6:Tb~ ?&TJ⯈9 TֽXtAAīFE{H 7mp9^YiW͢3\]\ϙ~|'xn5װ.s7qȟn"Kc?c֖b!ʚpKG^V|NANl(8ItY]nMȃb.{0YfP"ͿC^˚rv68ˬFxcSXesh蹞{Z%ۑD [vX `iІJ;]@0vی{ ;{:iËxy2^XyBvf{ xV}3fo>~u*bz24-[ ݷq/UFYvu1i%vLrb("gЪuCa{'˧/iBf3}'[J)vWDkv ,pkAw>C/yWH#@в$•?nqqdZ_,N4꬜0hgcgtoJxOώb&|N!1 O';81ˆ,:z$xHK'*Cʂ!0pD a6d;"wp6uYc/;A1K򳡣`y<Ros- 9zAT9@zSLTsLQ7&^MYM =+cVx p}͡-V)ZYˡaU@%Y?^RRH q$r(lOv.1#]9URR mb$~-o6Sž QO}JS(?FrM 7c0RR|ON@W.kj\OIύpfk_Ǎ0u*N=x ۢ΋LjJ4Fuͭ*!a+2ssڑ=-ݕߟ[;YjCv>:p՚E)l|9 ՀGC:AB !(Rq$I;4eWeA~]E ~&xP}n ˧s3 ZXtNϨ7|fdհ"ϗ+y~ &R!4ӬY)*Q\~rW?dFK+q{Q nI5k9[7 8e[u;%83N~/c\M( qW+ 4S,g ozb˽s.K3}|ݵFC.mZd-S`%z$m'iQ/p'As/ҝ. Ѕ-7G=0!OHS$EDVOT͠36-®J[u d;(~ ;Ïͩt1RuG_b]o(>&0$|GZ0k6eùxrįم#iV2ƒD}DrLr]E>BQ):0%a'5>ߜD%>"4SA۴tXI k0!S?̬ou.!p6!B~=,K 49FyCqUgDH] 悹w[]QV&V2(㓌!)"C8u?WQ&D /m:^`AG]zT;aiˎ~6wTX(s@~4pts~m$6.M!+3^͇Zd>CO B{IrF=.؝ :׈K#OhQy.亍)ciÔ!ņoex*Ӫ54~Ũ0uG)E?&0ƶs^q[o$QSȄĘ8 wq[X 0f.Uj2 9>93sN<sj)_?lIsV8m]HXM!>`V+k2{pwal4>V{kH#UkRYéD_$aj~}dN$R`07ոx[萶Pf;S¡}IɎuڴHO///li-3\ Z_ya"ӔJ BK.]i$߳b K0YwR\9z<# G,/4 əèLVx}Y }bc|\,Xuz%@G=8Io%|^0+}ɣ#Qk_ =g}cL"i-ST{y9]LSAƦ8Vu8SJv;K=vPgȶfInB`jGF..A1ІFD97y[)yn`htUЗ'x~xxh~ d*("ɶ&_wb-䩫Gid(3>3ք )AgcܭVh BݟK9dlv.D:mg_)4jP.4*%zAw%~fs~@} K*eIrA5cSp J _W6;pY7`a73Dq.a"%Yu< iV9m9た5G>a9o+Lduc^9FCa[~)AQ[syϳ@8UNZ|ZjbuxL2c=,\=-cSE'ztLW`EnI+㙭oo]0Nw)SL\?m/k5x=1e+ _a Ҋc:+H[J$YN`\%a8-a&ɽ/%1_7i5,nm~ u9tx1Ek ;ڙJ#&խapH=.)DfuxAF#Lg"y= ~hwdbK55ٴ.r Yc&Wp:x /ړ.R5'<~d0|k.U^^#x^֣z7/T#>>XtFX..>ۭiU"$bSScJ0KM۾̼ ?>s;[%e GocҪBy O<. sM7Xx]NwG;ǬLt^}!5,s{O?[4$G/QRBW?eR6atk xؑ{lSX{/M[zoK@r[xv]0@T' & )q֑$Nm*q!3[Ta[IḒ×&@| Xli׎7*WEJ􁽅W#UD딯,ːX ^T;M8^;:.$m\ejla "{eTQp6{7%uё—uw#xijb>|(P9(RF`(ݬ72S %}pa,6޶9j=g[P#QmjQj#ڕd JkjUˢU۴Yc],K2AoE\:Gbsҩ<&9 ӷkRװ`C"B)mD7r'ZG2y1JZ|I9_ wAN!Mwr=zdunb75x?c ̦.=yI~P]:C]g$BZ> stream xڍw4\]6тDFeFE] 3 w;E'z zD I>}5g_w^gؘx H+2 (h@ ^M )&`{E0P@Ah"mD>882D`w PC".l H'/_K5'OLL;@YM5CZà^ !i$zxx]x([iNn uܡ/-#fl};r=p5pC@(zs܀?s;ܟ޿HG'0 Pce ^WOWne `w0 B>9, IsruuQ βtt"\]~OZO`C@l~9 0g7&h?2[+@Wx}/'o%/1 `&@/;r߈Y0?b]|` B{en/w}J54`N^  ||bBaN_K?'* :yp-8NwB{ @B ky~W@npo5o;^[=Hp (#֩!D2 цZAC@._W /zܬ׉ RUP4{K%5k` E.< | znltE6Hz rDV 1o'] BnBS 1PO5 Z"M۳j9:/Rl_ S9y|PmnDx/92-N^&YXS8g/%Q /cT jye7|:> rjPcqv%#?U+Q%NxU:kcT<Ŗk9MsוC}OI7Lj/vb }LO{/tҲҚ0` MVSR]d`1(F va,}a=ͷA kaq:lirjE'~='piI]'$]U(Cj^t@"NT_+N/z&"UҽqK03g`ey錙ZWo:-$?aΖg.'e4c#f݀AmC9aV'lՃdK Utuv璱B9>|+E110F2OjH$+ƒqaI=;PB2 !fjS=*NiB8zMĢ_J>fa30'q<>w[ChTRWHv{7U1:/Nxy;waBca"sG("Uj RSE^:R,OHMz$RyE/o ]z"'aeE VRT^I'i`}} -vep>P#ElDX ~I %n"S(]u:FfDr=P"աUm˙#ҍi:wo RR&r4"YgM&DnIf :hYW+9)5>ɪ L )S[qU=E bw"eH( (=/';ɏ2@Y=0\d[P+zN#BvBS#7l?[$]V(8LeT>O%HN j0yAA>Ƙ-j}aK1' KuOB"~eV`Yt㰨q>.II=!K,wbgt4rWX810& E*%,IL>q2%nL|dBhHءzgGB4Ҳ~ȘiVUVfHLSud:}lNM($J5mC8@1]mGĽhШc6@i_SR~̆}8܂caN^_DsAS_8uI| TmR_r$HTj8= Crqd |SFhTh80dyeHħHJ@i\9-.dqizOHFS`Ӗ:'S1'f-7r{gͲ::_vܰBDnE9y` súz~\#]F4=* O)(z{iGal#ܹp 惡إ"(Gl2AoMz8CdWx ҂շ8v&q"7p,G--wF!c6b, Iݺeΐ0(Y702t_E [Q}7NQ?,,'UC W2I9I=/LcOCukTLT{!P>[y-~D9|u"-njb&Ż58!_ں6hjȱӞ*|aAqL<7C9ֿCܢ1JDs.YW2%V;IbuԹvDh6n1W6#۞4Y4|}]USJ{Ev& ݚ(SMn|֡伆8 d?&BHʶaT~Ɠ\)ұpq02])kMۛ, 3 (B+eR3R'~}н~i;g;UՎP>7`ر l5* fR\SGO;{BklFӖ=@h(@qՄؽC_M] ̲Yh Q/]O@~ʙ%So6b LBqⲧግyB2#Wx a@;lȚJR Kϰר 귳$5"H Y)8fGA936 ۫ƃ\E !Tx4Ni(?^yVk2t`#MF;%'շli>oϞW'R+Ut\MjTxy˷6vKסoP%&sA7]}2:ZI$ -es ^Fɼv?WVs2W:HGHfu^d.@f[=*_R.Q#S[`=\B$y|SȕYde4pz}PPaUb !Vw"y+J B*TְXз+ś*;,ᾤvNV q߬oJ\ ק\I2#}zT{QOh{F|նɠ/t5^L z$*{\+1(Lkzڴq\2ə3-k0g|hﱴMG| 8:ޛZv({Y/((ĸaeR5\{|;Lm:>(]ץ֖A?ͷJ&v$,;GzOɄ//B{=&؏ K"_XH>,JrV8_BrixF&%Ȟ_c04D_.!io6eYvRYiN^Z ZA] dĻL.ϒ;q| 7omg0!=$ڂ{΋Z8S{C_ӻM%TieϾϽ,= &Ukir[wfgUR#w,CI@f2VCU,2UppN.$?o8<#8'h{^p"90zISZ :إsja?4rUk4.笺vnmt1W/LSdز֧aã` z[QE~ - oi3}vA&t*7 g-\K7JKxphN^ymZR[wZ(M߇l(w&שLx@̀Iյ\䢄&Saњ۬e]enP`bR= ZݼJoFʆYzW)Z4d(z*ѐ"xb!M̩ 8Z$ՓWܪo\JS"}87˪`vO^ܣ$RAv% !"y}뜖=(zzbReR{V5[xdoV-)X9+. 6Zh AwŜRǒ!=fRb[r /t ǺT`S{ߚjt7c&rR7:Q9ԋ&`|?V'cWy^m`jHާEP_㋴Qֿ/V[x!, &uf ]lTm"=c>edHohT?/r(.{y(?S:lWrOe j>n"ۣt~N:Zf\ΆB3:!nx˘N^瘮FNQv+6伐EӞmjA7e3]9$q{3٢&VB ]&TH!vf.iW$X|Ft eNݬ&֟Tiud%P x~ݸug.y n%^i/YF˾LNĢ+|D; 0|C'jR$VNy .rx.; éQEY%j._(T[* G+^60B&Q uT s0efT:l W1ܾ~U_YK'xdq<h g2[S=)3^Sdc0wn ¹7m#ҁnRQ${)YEYb,`yn76$te *(F"'^ت> stream xڍwuT.]ҍ tݠHI7 003CH ݨtwtwI)wwkyYk8("! - (?T25T D88 h1"2(#!`O>Dn0(@XBFXRFH "$$ݡ@Aq(#\P{M>6<aiiIEgj盎6`aS[vQ@?vCP;6@ `E2@ء=H@ਛ$7- 0P@0Prf*N ]p/(`A: - m0&7ߋ@z ?lP4 ~UfUgg">(bs^Ƕm~Qus4C] *n\D!h0 x8jbpPA 7/"@# ~>"Bmk=NO7f PO ~=Y̙-M@:|;HDą"bIq1+C\߿*?h_T?om\C͏~4 wM 77/;Ca^bn& }jCJ(o]@X ($A=!PqtA] ؍mnn͡An߾p/)KH$؋fn,qfm! #7)~;JH~[7C())c 2A̛Fn Q!^@g8d.q`LvF)Qv9B>{ _e~bTz{F|HdEgeKsF~Wؐlx]zd-UQ,m* FApOC`N3'UqOQ^ʇsP1r(v6obJ t!=CVJ;8jBCC{Tr m ݲ{1- k޹y6% lbQy{ 8f4Т0aS_% Z/$~C}]`VTNHLe.Ut:U =?cM AM81\VdW=V-aŷUv$Y\\3-ߒUc{b= K6RGwxT¯Z=2G ;w(JK> XX.qo̮Pv`  gU2|ǫ#O mUH[ZY*$z `pkп*\ #Lv)` xgk;a$bc~D^d7%׏vߋeF9Ost}DS1p1C>7_HŬ:SwaDox% q/T O_M šhux$NSsē"NLdU]|9 ^,'#^;U[C&\c ΚKFJSun /SF=iA X0.:c篋2h㦀=I Qtf[o?0 LS<uαt̰zmK; $ދ:QDOwTP!Rs{!LGtMkF塺_n]'R>觮a ;uv6R[Q#* _{Fʳ(k_s##+Ȉ.tEO/ wc 1<=dVBSлӠ@}k?ߜERn)OΗ:#'To-Ohx TO]7Voe]p萈8?ؽpnA6mzf-DFOb_%؟Ezܰ9LKW9R+C9IeøV4jp "p)u:apL!˘hY``i#ջxǬ+II˔v^vT@+`탉 ixT%aUYdgz'q uD]BNY~mZȣ,_6ҩ+y//,%rc2E|ކ tm|1mCw <)x$ ҭ(콖̪V}ь6عkr!}7X:9s&kt[z[t~^O*dp컈aGƌ_H׸~VErlK܊A^YG?1~}LfW$K3y"`Ha> ctљu09bIh6:}fnT߳p; rqޑF4qk`Ƨ_oK%A(bp]3ޗP`guX0])6?Zqc*6 ɧs>1kn35bp'ag!QR`M}ϱ=L@L7)uw1t憳2W>2jԣ\qH҇O^̌Ֆjt\jICGJu')bBb潃TRRDmǹ1p O&<,ANgѸvr*&g϶h NE4M;WwoɘM5``6=-̠~28c ԩ/>!qdIitM[Hz;皖i\v;>Oջ>lASnݓ__ 0QP,"*ߏz>9F4+We% JуwICy4sU_;t^fE6vvk*%(ZB׍өemBZ TRQEGAVFwRyYpI{da*noUH8$ᑣ'1?x> /#KJhx\%hC^ NΔ7Rh~YahH{MjFAVdӗPRn@⦬pR/ōhb~1_Zh#[H$;K!wn;2WsQHʼd7}aJ801O\_*Q(;#&-z[ԁjh1]~%m,Zk]ڝ)agC#l9/}wFkit8:ĭdC2XN}YwqҷOgWE),'x=ɲ_~RT;Zӈc0:T\KHi)Gѥ͒J{b/ۉ?(<}/O~Q-{ߖ\'KYߞņH/i2<uIKn&y].$Rs0pc$ <KQb>gB/H;pQxCUb` CǏ,),dIZze@6' GM"1Y aOC5Via+Oݑs~>XsOUznMů t4d_zR"^L*n\NT~?όGڗz)蔓S]w{b'Ȏ$EX7[ g$`f#Cq|io1zwFne] ?+r5z)dL$nk1\gZ<]v`^,a84k(-f6U֗XaJM/m/(D |L_#]NΫII<$?TNkj 6yΟka0}c (>j~ij%4Blդ>c:{+8IĪ&rpNe02}QoL07%|*X6bsV̧AO@OSsئΊ~?k c]^v A'2(F-UB/]χ%pɝϛv/k#)+@$^+%T΅]hl5Q>w9Ўc_#QoMV0p9/2N,ĦS)'|ZG 9ӥ,M޲U: T~7!9zkhi< #љ~n]Y]#N ~` (=- [Oxîa~P[B(]q6*~i(Tk/&bSW}|7O\CiR5hb,۰Q{]ZB ^$b|-4j{dBu5zwQ:\7q~0TG F`-_ x k24T祠KLb \}t5rڐn+ +7uOa)6YyI1^`bP=,${`) ȁr7ӹwCjf>b`ӗKUh4Gҹ ւ­kՃ\-w͜r"bv˸; D[mm]xz&1YhWj;ÎߢyEd*dLakAU7Tpd`$/Ɏtsz>s]Jr0N99<;P`{ z<׳`kK/.)R񸴧e檣?Ţ+.td)pLjG552irĽPOH.jŹJtd$ 0[}է?Mٶ&./.poO6HΫ!ghje9cL,xE=vוa "kg, %W]'6:=3C3ujl>r+45G? ZvMU r+9ny'#JA8_[^.jc U:?$:ٰZJw  <[|2g#ԦeĺXi7qhTn׻VHD 9>tE{|3%63pUJ9n^ >"m)Qdl{hdv8WʤBv[F-b<]+u~ҏ& \7ai7pbYk4K/&gCSFhy݋|D_}ܠ:0UUrMtu%}=僬l+Mrnʪrvp/-R"(LlwcxVSY~X!]b| xFF_ YK ֎`7ݦӻұzrX>Ce~l^3qhỠ!Y0>B:E3mpr*MN AܳD؁y7uK=S~ 园$wʅZ b;tz+j5.1(Huad"NyyOQwxb(GUUiHLVX\NN0D84M[1g(j 6:MEEʽ5YK!1 4}@0!mriaPAro(7$7^C{ns0Zv^\]>85m'A;0ҼMB Zr8%bG䠁SE~jCT-G4;cU #yeYS])ƂQp<)%v[7}gx6md޼M?B!EӼ;uSa m7>ZB.')ӪQ0+-y!?NRj| `3MԪ{-i@90P +{TPJ1 N&uJSC-8G܎ [ZB7kվzT@Z$4|NvϦ'LYoMԯ f}؇nXxF㖺וK?v0x L-cW# 'fX.q5T*} Q+Ts^Xim1l#JI3U/vPg(ĥuݲACŞx{|0yLeך؍ {O: y\$~`&b"LTX]%Hxb͘H~Ř+hwڲxC$+c%{X,tX ,M_gYjPv'bد],K^l Qq'ssNڱ7Dq\ ]x';@uC=Ù}&+3w_MGL_9oE rDf\X>NKHu*E S<w 3݆]1o8W؟;5 7Iiᝰ=7kqZW 1ZM$2zi]yG3|̦>Ae,=+pfw; XX%G!}fibh]K&LŁiv՗x(qԀidZ/]N'IHbџHfYxHx[ u7Ix魥g<TWV5 } endstream endobj 99 0 obj << /Length1 1450 /Length2 6598 /Length3 0 /Length 7579 /Filter /FlateDecode >> stream xڍwT}?R"!"P@BFIww Pcm ݈t 4 J7~~sw^{~d3䗷FZUX~H@ !H:֓qY?<p(SbqHgGXߗ ߎH@ ꊰh 4p "Faqu~px`q~h ІbN0# CH-eŢ$@777FpC`p   Ё:@ @ H  yl GpZ]/g,W?ѿ!Ca0 p ]-;lA⡮P# u(@E^!C#PX #W1+;[+"X ٯh8 w?upF9{- m~~9#\ՕTd±}!1wx࿍_j/ l2/ }O X#`XL85/74`  0kǿh$ Hw _Pq>oȨl* F:H1H}[;G_YWwG*O۹rP'`qˡĭhm5[ձPܒ; G`Tpk=f# ~]<(l݃9. nf"b ǭ?Pv!(FC=pI"/0ny9 8#&5h11/oITuo oaOh\k)koad'0 Jy&7A)픣$.8̌)W*;*m: .GYs^l5l*klV'.{2ߞl{a*Qcj'2[)ލXFpNk{eq/F^Ȋ/N1KQÓunkJŹUP-Pwp XVeVZa<{,u3JܦuW-Ѯ6!Mw+2x6|ls93gLMR!23AM~(\)Fׁ_{<<mmUrt\Nq RtsZR%sn[ 4E)UWA20z`G偶׉VޖvEVOy޺= ܆r~T=qdKv#Ŏ>8 df= br4^]s6]hcUT{z'& x+> 83KH%P ^ڸAD3s=xBͤJP:( 'hu鳙T>1e3(O3//mRfŖ=ġ3؍g$s E$-ށiLHyݼ Ɠp+_tޭKm%V?iԤ ?۰iaylá`bωÌ*U|30gT)q dH#sN,)/+H@ qȗ9e^Z#\ad}2ŗ NjedoL-@r>" F%BYd5tbW6ݾ Z9iYN #44}qF*S#hk޼D~-()xKf,SZR 9,yeˎNX&&⪹R9N (\[, BH_Q~zڳ`v&l}ՂlO9,|}G;FB*L}GBLRUG0cuJQn pl_+A3x {)cΜޏ>= wQ˨'t3UGUxۢi> D_ h,+D]vº/dlH}Q{]MJroY#v-;wV}~CgÁp(q|̥@AonD0I$N5ғ黥Xj}0IN=trmL HQ,z )xIdIuMI{?980󵙠"T"Z$o @f%="Ypl X׼#w֕!6m4[HJFxNChşO+7O%MT[G_&y 82loL B^'kxhz{.%^ͷW^6.,4m^5jda7nɔWoHΥkR/*Z7ν x%*C@¸覞*m}`n`I-P~3k._;oHwq;+J5,{;~UNO<R1#9#wKMzÐG[K*8ہӕ'O``kէ$~o7B#)vp?=Bny<].S85n m} %mh"f\q&&D]^6*f ԔT3ˆu_;}f+6Qg};FbZ1-aD/"TƸdon,3Y&f.ɉG iC!fXomwyI+3#@̶,WԷ&ʔ|tw(Xxo-b)K_{d3 7 +Wє -h.fQ)M VIV~|dV{Q~cy Gki'g-Q)+Iv܊{jiu*V=kn$t%fl{iWvɥk[dþ5^lJL3daJ2%̞T绬D:zGL;5BlJi[5kW%-Ѿj@yS3IP+Hs_ٳIϑ[/kg$eJ_1(=T),Ɂ Ư\4T$QM\q0x1tqںTYwr 鵿i i~S=F@갹iVNY'(, ĵP4m~Oe(CQJzeJZL6{rhd@FRT-$ȻXqKk˳,o4R8r Iz0 gQQJv*õUWuC0m^srϜn r"5}+íإ('$po*IIVWv՟V_9в͟؆1쉀u 5MiBf+>3WTY\E?Lڡpt)4mKՁ3ej%YI?ͣSvޤ5d֜U>^dJO C?$0OZ6<4zQHTˋo<'/7B>Ea1S(8:Y9HF9gŅ z T}a}IxDq1*+,֗5L !(V_/>\sNwRa);$MJj ٟAZ߷tזB $\{<4gk.T$^f >)' *;6{jNK9=@iP_x%=Eau;w~8&Xt3'gsAs^ˆ_ҶY~yVq_U?P'«Opk,zD⥉ Y+dK[2vb)n 9"r׈3-eӇJJSN?})Fu w_ DR[ {tN_4eϪ k*5VkPr_F.+ϼ*D,in6~{UASQŧB.xs]y=)I[D-1"z>BT$^ajIL6X6ћhXfg{J ۮ:Z܉^&yP[.Pu Poh7_@#-+/ǵ!h8= <3' ޯV8җڣbt!WE`NԞv0A_q0a]t'p #~ -).%R~A(V,f|iR^}8$-HʪVύTb!&!#?t9ܿR|v!VLSb T?,4 1Џ!6i!L֬nn;}}q 6v$LFt6l_jYDk^M;e#!"-z<2/eM&BbǗ'T7e7p FI*yBjٹr [wt[H]vȓzy?PӋrr(zpZ{nEId<҈AmD+\ 'u kV-Y-U&' _EHI8K}k/zՏIjSJAz WN1d"y{Q3G &-clW|xV2Nv]li@\ $}y~|DI'M?LV0W<'zCܱᰓ, ZBe\zVHpv;+>d6sAv5IEKTUO5vKl(Ȱ>PLЛoCO7vnݹtjVtzY98xn9뗴WNlK[) (j`w!\Kň]QPҚ阖6Q#n=U4)(pH"iv&M)>U@c-*ɸˎP!?5i̧QR]+?bɦx{Pkk?Zv~3=ad\pNb}zj5KgĜC2;nEU0WZVN~wy^ǽב.n?Gx^7iR%/uI4{u= w<~Ȉ0+leF+_dUԃ*}[|@AA1L'zL&'y.yޔ(kU : ◊ب!M{J"y]3aWW~pT+4WI endstream endobj 101 0 obj << /Length1 2709 /Length2 22575 /Length3 0 /Length 24098 /Filter /FlateDecode >> stream xڌT   CwwwwKtwttHtIwwJJ7g{[kXkjr5MfqKGs3; @RY ƁDMr#F0teRfn`;eG#+`c@d Pf(8:]%]@6n4 2,fn6@{pF 3;tB6nnN,f,."LO @ tZ~5 P1 5@\ @@W%NДW:6Vۀ l,W _fNf kPQbqrc9X24suyUnW=W ++W€,`)hotpsEUh7ߛ}9XZj݉U,B-x9@g†Wx-o'_J_bpNN+p@psq/BbgX,@k`1o ^ `;߿ߌttm~YUtt_ `E2^wk/(`ׇ?d͠2@ll?F[_j?j3{?`*B|T)+-AW+f>qkry-@n6s[9]A6f|poWRߔ`b^=q|j VG7 ܞ?FyD#o `R?U_`U7F\V\oE7עkQF8oήkF:8o7g-E`N`_<)s37@__n̝I-pqQf!SKGp~WW6~W +?sK7'~8o_F^vfDo7X) zQ/&@p+XgliV_?fvuzps;;G n v\ઝOO?})n@KsDoPN4=8;x;Ae/sq<-W?ӂn6.?dgCst#8?? X?⁽@}~ t;.__?@/¬`m]Xm8'Ψn*={4ՙ!k.^-oI]-=4mMRo{{4y1ӆ4?7Vx(^KH̬%u;Z>֭gW}opn5"c$svap4uy >3 f {F$8"_u -*}|+ _9Ңy"T̯X92A1Mݵ _ٷs?hj˚M\(TqLbj9IZI_tZ~$^䟬L5w>48 1D|ll-%dg1^^ [6ա ~Ŵ,\7=!f!>l70 wx3svF/<*/>pJfFeHwF孾U6NWHy)%nr*⒗ޙ6 /`9ѩnK.P'>ao  ῶ߼IQ3Rb0~C7,/FPNjx TL79屖};t]-GOry)jڢ^~ FRE5AbQWMAu_To P2_Sg.AS|lL(AuO=79E}b׮Ұ m|n+(bM.6*yUSa6cv40 #A [dHPXFTĈ2m%vOYհnJ(a$՚hG9^yң AԳsHJx[EVC,1b fhޥR#cŠXGS@f]詓TPp+KE 8!Ja-BN~S3ʯ%Q]hto#2ig,Rv2?ӆAܰʃ ƚ AS ޔC\d\X_/ԄgH.n QyZ]8ҵ%!`A rnuSԆkΧA\\&75Տ8}1lk$Εs*y'v\r)8 o&Y w2)EJKfA"%^CVPeX#FEepp_̜nЁ8 'D PᠶN6y{0L\уꞘv! 5ўd\ zc7g9n4e[c=pZ_v۩r-U %3 GUgĊt9ɕ,'uXc$j/;}o{*M xnnp9wLDmI؟ 4!̧X" x܄t,HdPK5=iʘje}nK &n ??ڨ7nbR١<,fq`A4G?{Ětg"* +ͩRaM{֒XPIs kJ( pcKfd V~@l H 1٣Gm{e.稌l5;QcTmO_nܲTX d~%&L۲@WVZ1xi CĴ+Dѥ Rבx+A쿧C9"`; ^-9m-&]E-eD-L1kH KjT-+;$S"aKM}s;τXSF;_Bl'ܚDŽIMv'vR8wʌNA+ÈꜸ::y#ShU, v>OG9†b22ZyYWuf-OVƭ7Ϥ|7x3Rgǫ|t)j..$9wp@y߉_YW(0KC`qV]_"5FYJ^ L$>oM` Vj㹐"ΡHj8GDR]>^\(N(MG>["'ZtG4&D+Df97j\qcw,Bqc1*М0Lf mV{)I? 2:zz`m"pKW,'`,6lё5aM.\3l-P_1 6LP#9UzA``xL\X?ۿkj\ĘȢ0 XqމE>zE? ǡYORǭRb)Iv2tp*ŐE]ǐڣ>lbaCv 5ʾC6 9ȷY2C4*Sgs#4< 'g~+[HZӭJR#E;䢣ש&218PLcݕp.O2;]iS:ϕ#U~ nLbSXZdC]D2 -ܟq*kKeMjzVEwiG.EzWXz@ybyjH2GϱgS3ru"b n/)=4_smFPj h̺>ku}8`ZA).E n/x:Re Lҙ .ǦJFKfy-Jk˭\ITX./'.נڄn\3|{"#KJY >:%es@^7w vUKb0ΠQrٌn2ؓjǻ#läN§jmN%t;ԏݯ! f R[{.#! yk ρJs=HB$K goIAYY?)^33 U  $K:JS{qvCΐb-{hڱ-BE++Igk2/u&47M$Y`2 *~#l{{*zP#Am׵niR1ݷCz9,:njݓ%hxJFVޅ2#Y[8w3rwz?dvi3UB~u˥ ȝ~U Y2|}vp_a!oHNcugD7ufgNaׇ Sa7$99`Ч7,WOA,#[]Rm vޏdI(zWƟeݹMd3o؋Xc7ϐֹj+"MDwʭmHK{!NL'[d?zl˻8rZa+VϿLMb깼: xhI QHw@9d.O`¦l5+/l7.qAWL$R,3{Mi=Y5`+ȹ?F.RI|E i{M,68/O{=cDbP~0Gd8ˑ }Ƌ*Iy5ccAaIcQxv frA|_n䷖6EL*:MB-=(%*/\%'{cG7w2x:JL{8υƬe 0א6ww>aRenb(7@}y1[z!]mH Œ%.x}ZVOPjSSrdn nI;~h {BPܧy^{Si9(8/4m1ó 婾dwxմot6eAs;~}e7.#I TmX).7=r9N!%p,6/s sVf]w. UTͻ%z-(zcM fpO'&H}fiqId=8 X`Og!yg nwx2&^q~1߆ɊkϾG6$K [y1y)ggǝj4A8^9-i=P~V:D/ɇ]yI_ufyZ`Җz'޹(E,IZE[_xAL&Vt@U}{I83MdaMI GAw~pݾڲOeE0L0[3R%^.Kڃ۳|W?>9,}1?.ۉWMz;\'3)~(}@"j3 'zf.QM`A?beD %iD?х1npT]]IHEà$FձIWŲoP|o㾸1aޞ"rղd lx+&cn'A3VͨY`|9@IvmK;^yoH&9Numa4\ITϽ9 A0,dO` %" ہ+|X=LAIX9bi{`SAmh>b}ʍ/{wQ:U*7=dI4ڥaKc U9B{$;5{-,'`r|i0Aiގ5|I&u Ǎt|R''.Yrx(Ԓ`UN>I^ɩf]B8o}ۮ}S ړѪ2+mN`}2=GaX%dz,,wdv;dDVOG:Ua!7;orʅM:g~.F^y3 82v~&]DFxP5LP'}A߄@naљG'a6ƽLPƦ }*kޝgsWaXP$[h[hw!$* gaQ{̟خmC(}3~t& 5 ^ 0475S<kӑJqA[d`(t7-_} 7_ fYclu9+wGzmQ< @p㡬?.UWL+ m#`x@#zg]grP9FmDSE\U%Da=*"+O8P`u q/ד!V_9q_K>g'8_E(*WPTlQH\.y!@/Wlx@f<#16 D6 bzQN JYQ;v' .4dp3L:}V۷*ىL]9τC 3 sO[Y18aN :} >2=L,.jbQ=2LT/kѭӅ'k KA4~w,1uM/8M/bIUS|f_# В^+sBskq/b Gt'>S2.o*A7FdK ݤn{n4#kԊHR۱Geź"wWBٓ38 );t4j?31gDj'˲bJXoO) v#yJ$x Ժ=)кgM gNS7V3kIԃb 1NmIۈ[FZDR<, Z>1QNxYwJ7yoko?%do8j~BR8&\™ /Oh ق1[l;T1b5̀(cKTcEHx_'T߸ MжT?n$I)LnEz,aɆi%N!eGxmڦ~Ud& է$GXusV6ƪ㧃uC{nu4=g*C Z  FSz|bp:Cf.Q" p7g[5uO wzBCA> 0|Fd>mddf} ƕ:rB8_&5m24vSrzDGR[ZY,t^k^3u_࿼'Jw|\IoY>Y-XkJ¤B>{5]8 &Seej.K;we? #!_퍡;0"ۘ($aYTtZ/j:>W|DDn:oK_\(~Oc\}k3=>d% lGLx%zg`2 /yE_it+SNWkZ,`'{sn ^$g'$SGf BdBI|rT&D^A\t2ŴLUJ|V7{.ʥY I@KC"Q QZ- Lh>cIUX1uYPw'y!o,{mpz Q4TTR0Am.Qz-[&ADžx>L7e+ZT_a,33J ]4)b ~ s/IV˶t^)d?>`l45x6@}:keB0T!:S5޸DcbsG8&_s Ou8iD*ØaKy[ߧႷvk0CQM"՛}Kc#(6H--f3 *qu]i^(:$<$QCGKrEr;]:H_.pGB`2: C~kcZ9I8Aj: OѴƬq@L7*Og!ki?9.K(ߒE)>ipLO3ŜM83[4z.Ö ;t[a|!c,o়_ S{[2 Rr/;4Fݓq>r! ͡h)!TǣQꬰ`o z1JhcU'Lex5Q;&f}J^ 5$Zqr͟Hs ۔]X+8jvu x}MG+uNM}VYY}%1-wY?ąu$@X+g*$MD/Aɥj }/@ܿޝОjUR@f35z"S愬4aja=(#(2| _^t={ ăBڄ ]=2+y0ZLn~0(xgSgA0mnK)[x1}/O.簏+{h3Nop'y{)щ(n̩<[M <$ayX3[Z ;:q,4izm2Z79r-|[B (uR )R\F ~;F;#":h5Q$~cB~7䙛0 #gC5*^ox'P d%~ICFJKbQ/zx%\r۽.nYwk8Ce!DmLrlp`VT~`q< -st+q=xU.瘇s`Jewrmyk%2#X5.%D$f}ȀR,҇u5Tb~ ! CZ9J0ъDG-5$G51:Y9(1/; ]>.'D+(faUO+Ӑ),2c v,"ߜuWբpzAT8ݼmSB{ь2-:SGu"YGѧ(9T]k<%]Og1DfDTr:obL( UOEiԇddt;te@_.O^_2OBRPG:wt75K3W>2Q? l:v;w-r俭8RI xhjrCIOwrK%4ᅪ072c~輥ps>v bfE%1z!;=6Up'꽆$4TQNnrnrF Q/q׺eͻ#zO> wd,*N}ʴ7G/7ֈQ' G7v5ؐ;?/Kw_ʵTߤg5ǕyJlH{zS~FW1IW۔_Z#pi]'[#%cUYڒ0qX{3&Pg4-H-0KAedyGk~T*Y]?QJKkͽ&F }gm$ӆ,REޭ״SGד@5 &RU1B5,A%HY|Ȼ BMP:g29!o~f=Q|+l"°:!MBq78BWe%!~c̦7i-#f~ ݘUTu dvd8uL'vqN?B^u}I71LrJ_<`m}%zҖ'*Ȳ9_^ i\!Y=XGAػ(Vt!okȞ/5h*>Lxy7jO g}j @ 6gVnM FLio-Z{B{aO>n 芉 iq/$Ȍ5GgG eYLv˹x~e^f!)Rv–23ީné^CU1IH5s$X{Bx3>4:XB)QѠ3 ɰ%cm5 ul}}Y['ٞ/},v)·3iTTO4 &3<c?Хu/3}x1%ϖ?H,UtpK5I.FU76mLc@oOs&{0_nN6qEc壢GL9 #MsAEbkl FT.f 4?w"9cxjXl-WWa劚ƅk"U|322(aH1]M'A#FY q Nbode5yg⳥[';x]1 pL_ϒ[QGniv%*A ,_]LU 3G=+ z5d){[Sn̼2Hw˜#5'3 kJ1Ǥ{f!A5WA}t5dq!wVSlE G`w2W>6lO"$2f( Ŝj}C VבPQ^20m^1 |~-Pg+ZaDz>\ dݫOiwPiOr>!5S(^Ҟ 0<4 3uG 8Dpˬ:@璙@h$i'^hyOy.snn4/4>Ac'["q byuggh0m'rsu`xA9B x72/r+v#˵{Y}/B[?n4`x{zg}GPv=ͨb=Hb'l\w*'NRAE OcbIe[ca,b6R`ӯO^RQyqj U|Zwmf~KЏcØ,u~l6g(\?:=pxLu\  7`2o A@[ ,aPi^0fҗtTAE aͥxAȾ9H5!G*ry ("kGW*89 8VvH,YȖ;aU%إǝkOnqABqJD'q.e|B6mj1:k%"1BdXDSNE/W>n1[#))Oo!l "1مAڙ7 ʚ:qZs,NtUFDث}-osL:KǛ,^Daě$Jj_iGͣE~c&78C1dѻv˼~]L"_2Y{5a9","!O*#nӿ\K`**JNG%4|Ch 8" |VI0]N{"l*Rz ˔Xz{[XAET Wh;k7$xOU=Fo;kq(3Nd;`ǏIHHVd?%a-Bb?k1%eF 6JA?gD"S;jdSKh'+қ.z}-]2 as8d Bº (Yhs~M_עI(ecz65ˈ,icyBˋntjK7VSVjĻKZϹUew{3aiNZTXܙ"ia;'=SjWKtBoG_['MaSm ">vܟFt-\釙4lʮMi}JL:rK.#EWT#;Gwzm"˞^>%ꬾ3!S<ֵQAa'&ρ4W02IR߮t6¼9󡈦.[dX3^* E"mzz;Lܷ)Ї%etsNJkwbs8\* ]DƳ JŭLex#;(5 U ي]x#7i%·\(Ꝋ m|:ԓ$d@0}=3 nc?Zo "~T1] cs'p,<O&+G%< =X_+PF汆rY,K8T e ^IO[<.^OZm}^ LI-/N?_z!oi% &HUkT}$h޹vqPT|9`+cw{ 5|M<ѧװҫw"I2>(I'iJ*r HYjzKX@_˭gx !j^G!ʬޫl9^#@]'zEy GfF)=CE*Dt͐ ѠĽ|.$N4;oY HN3Qx}y2Ÿ/fyߘ.'N2_vLb1t6xOW3I| szuU*6A6OiXED/Tew*p?n^om.X^CwhmC b iz8㶰womaKC.IMB}i?: ꪎдQ:L#TM90Ni]]Hd{h*!EĖK.9"f6c5HJޓɈ #<".,5Sh= 5\H;ED<,l溓 aIu= 32풹4f-z9 dH#8کI0EG|vfUŞdy0r+ Z;׳%Po.T$4al+EؕZ6bprٴ^+Cv/.}_9vFP5:%3Sq4:d@(^F,Q,ߛb,3DG7LȮ.%&EY'x} #o~@CeRs?[f.0 , 6WÖk7UB$JAl+xjD;(]Y*IzxF[dLg$ODqe@M-Ʊŏ1U|cB%"E_ L5eGQ1\e]uidэ|nE7y-r[2V|JwǒRe7*k 9jurlߪu]J5Ωtxl[7;ǁ{kadEyT5zt'&zd`onœSۗ-Ms|yo^*.F^a1!Wjա"EE5usfKUEBs< amOvZ݁,k WLQڽ可Ux" q)YJv 3mvW6sS)EQk!OmO JȻ)VT0& ڕWVDIo{B1FrPT9cY67Df T&Ql@pWx/NM4pZ<49oYq*Ѻ:VTq$U6\`5 L?* JNY"C[?d@#{z6Y8*B_mH@r)Ø$D;$n]qJY y;T )mNXFjddbvE tJ_.[e3ڹ/I$vgZhd]z6ͻ< |4 +޽c7xpeGp"ßԅ SK.|qR3X:8*Le.żֲ!W\ Ϯ rp>n8e.K:U#G/hQ?X[RQm xIKP0Pb !U"eQlEguYFro7(@+E~ ORHӆ,<P Ӑ+B"@#3ѧS QGf} 0^ TjRs?00b'ds`NW'0":gF8|3!KxpC+q,t&I~KLƉ"5 4& "Y|B~N􇢪.8nyUMzAd{`ݘ5kǬO}[GEǼSi0̮G_{`6m\EFs/݈O^y]UHR=5HA6Q10LgG=(QYJ0zi"j̐D\"z'xfpO,pO&Y \6TIPw5yt6s^}p6v!~W  ,v |̲q lt%LH|t麫qTb7ňcTRF+\4r0fvHX Zy>:ڔ ?0 `]'] x f;|`(h3,|_8+iR99pڵSpա.B/v<ˠA[P?cn*XwfE5IF ly4L]{xH <-6Kv#E٭L]`  Ud|zw JTVջ5`vϊSlZY~p:N\{`C?@ W=٭!O hMӗ%fdAFz7ONv~I/X5X9IWHUO0@T J|ҹ+Q_U/n)5ⱸߡZi⡄Hӧ%X`laNokWw^d3&y3o~A6=;[EKꠛ (E'BcrBٰi_xeSoӧh>snWkuIdGw:3'C: =4:D& .d9.Z^p&-unce/n%]+H. [S/Pԩ[/t4UV ]vPJbO;?:Nrx<^B+'ۚzܒѶ:잉PyxTdM~ ~̽8+HYsUPd>bQf폧i3Y&~Lgob ,x"@EVp;'v4f0ogOgz!`dj:%v(9jE3herDӀ%pHˎ.vL6b:z^*o-ODzx9Xh_THA<03j}R&$KG4߶'W{Sgmè^RW qO%ZqiE:KDCYx£W,KK}:+ntH[AZLeZ;@τs' 6UbqXJn--7 9P|C d~]Eo5NIɏgfK:z$OS  86"w__p~p*9Jd6p{g#,7ưDM(@ &;l^0۬+OϺl}bt 2zn#$HB7ݴDlPf5ïuIurn1#; F7Ui?أ<15~Z:_7`1.uF&dkemuX>$Ȓ:z:퐾x62ZIqvkδ9!Vs0%8&HUEܬ&$qÞ{ƋƧ!WIUsu1̨!cGFJBهXKZ@xh* Q0@äo&؛Џ[۱'}c~ ŴG*Ǒtάu)o*̋[a: `"e&թ!F.Bx%%b /ՍrV!t/bвXBނ唂+%7QuGy}=aSEw&DŽ"/am |RaSk|g/[{<{54k_BԹeì&wJd͟!":n )s\|ѶeFY4<~3M}ZlybiβdzCU ƫ(*&R.QjÒl”UvD׼TB}P,Ո 򨙸 =fraHxy[ s{Kd2VU{-s&!}[%ftRتqCHCx2i%אju3Eڲ؃۱B/qPc;tڧۯSfZےdz!΁\~Tvļ`rmd=&1jhW2Q+Š1Mےwؚv0!7L6 uLR kŐ2#t٤8K4xG wV.,١PA *VѸpZ 3m"=ߴR5p8>*z[ 6G}A)աؽ%5;h")DжG&eOAڎgB"iUpQR{'PQ{֯ЧBfrð z:z@NvW.J[§pl; JM55xDYndI>w)! ^;B0M-b`\:tֻȫ1F A@\vu>Оsl/ڵ՗ "d5]uLfs(~ ʮ9R{! OJ[|YBs5I[1|Wl)ypB{GQ|/8K |x2qpŭF P͜3d]6⋃ j@$b#r?gkz B2H,^ɧlB_cnDڿ&Q{hfQhgQ* ԈKJ/0s *d]* 9FSfddtdmw,7L-Haú[X2h$:Aԥ |,41@%SN;T¶܂f osf q^y'Wy{x"ѨIjO-6[YT( M2^ X|Aۧ[0f/bݍ2 .]M?A&D>0>:h($GtZ\ÍW0:>vMOb⦓hk^ٝ9 V#icIufl-YO<aHXtB4S.jS<5 WW# ll{ۿ:c=XW0!z_wVwO\9=@KHNoꫦzGtHvv eT,0hQ;'_})p TEjSlNf=IzBlu-ٸu. 2df>;1h@Q nT=J`k^mt~@]pHVVV ix.&Ƽ-̉=SHKq?weOsR4ՖxI_b ̨sCǡH3L@ pfXuTv|z_-C+v'pym( 1o<9٤잜r.PV! M. = ^\j J]%6߫]$uNJԼ:Qa Uw=c(ن 奧&*C 햼H=cJD@xwN̫gNGУ]>Ky_DO ݦE;Wdqj- B$Fӯ2z7nViY4‰c :k11IDY8/FKtTИ8{;+?n{{@}oHk>e ~I1p[PKĘEK6ue{MxL>" ".NRz1áEPIup`hY_]KK줔" oyZ^3ps hcHؕב4/M`YQ?;gu2t+ endstream endobj 103 0 obj << /Length1 1619 /Length2 8604 /Length3 0 /Length 9668 /Filter /FlateDecode >> stream xڍP-ܡ k$K 4H4n!;!$hp  !ydf{Uto[}>U0ЪirHۛNܜ@7y0 N :`#*i89=)CJζn^hȀ\ eN= msC,'ٌ-,,G9@ eio;LՕdigaB`G0lMɌeqӯio [X 500& oA` ;j ؂r9ܜ Dc=>&qr@NJz$=G38M7evv`#@`DZsy6P{W_jn񛄹3K qp+g v@Aa>v3 ~d ,I!/ OG w{{;77b0[B?ڏ Q^P[_.)]Wrl2; 7pëu6?P {g.awG)(4{?Jd=a?0bW£Bq7 sg*:C ji!r7O m!P#['of6]?ٛ;~c~\PspqBKp7*Rpp\f[܏ m,?[m=[ì3ӆ{n]fm+މ1#ø3)2bDZB]1m<*_ѲҚ`#jHUUe+D_7!l|r&YP g;խ=yz&(YjK=Ajh~ c>{W(^?0<!kdYڑH;nߓj|!dX#d#jNO6jaߨ4ucDWKA@9O1;/<cI7DA0()|#ߍΰjNbw!)!̒v`8bseq;5Vqoz)_ mw38YO13S3"A%xIl)˝ڴ/"mǕaKKbu%6'ેܣ_vje,nL71<&0Y+0ghQ'wTeFm*h85:i%S$nSzf0H+Tܽd^ Ft>~60;~&ayEA<6Ls=`hs5ʛ{ISC CI㭦RԘsa4~=H"[@d[HOƷqˡmwU{N{7DoB$X-h֮b\L2ɞ vIaAmyU/V7"뤲Y>QiCq?"?'?j־l>!`3%q"HtR|rc˧ߠ]@&UD,A8^!Ak[P>cII/@z:"т\(t-'IǔPMzևs%r3l QE ^3}ԡf*vqFJlg|6P y&ci ]Ҿڶy1 5ΕeR`la|+'>SsL |K?V@%_529FZ͏$w8'Dv[̀")ȇ)@O'I֔|j-哞wpD,/ HqcLCtv,+c 1l-vLcln#yv,zFu{AXur .Yv_O0#^S#bjCM,RJoKXT m﬘En cĺLPنhbM2VRnx}c@xw֗T795a U:s絤m4/Dm^GDsqz-5Ez6|@_I,rz !~J> (ADkR2tt6_st}NKa2}Зj"t8K {SUQ_.^bh3 [NQz#10E jfj3AozaD2W4xO$\~yfيRJ"(`V1,aYzHzcNΥkq q!c-`j ]Ļ0{4煥Aʉ"O;#\*؅!Au4' 4) 4R¥@_3Rfc_bt0jd(yu5V"&y.c^LG@}psck_gn!^9#M(>> .JLȯzy;OjTƕRō]cY)И휚+EU߿iݚvh`"O Mk`=A!Ѭ1'#79 .5vJǭ** U 6lh1UTI@裪ZYOu9ژh :;JhTy-Kq6~&?CIy]W`_\E VlSCB|+-gF@hxnޭ s H֕}Ih^i`d0 .dI°A[KƑipIrn)9$,:,v يKp[1ztzwglc_~DīrG̤MZ|GV K10Ňr+a`W~1 EGoX>[~[`>+nl!05tb#$ c)\ }4uP" K{֝duh$M/$0=fB7ST*7φnUַQ)né!j-7}^Yxkɞ0%ym1#\z ףdCq$%;= j#孉=NB$ϼUfeFǧutŒ >(G5 E3lC/󂈁Ch-kf0yP"T.}.Y/nJN5NvjEiݬ*>[(n;|j,MNC'Fˁz{.ܮr.:WGЌ. ó Bgv2:pV-Ma\H b[mqB*kUֺk,V: ;`hOͦ]>"~'=3q'3{Rrz:*{\<0|(X56ek' K]x6^su%bMDJc!.v[u$ }muSo_I{.Y;'$`>' G)TL[p]![;Lj~N[dž\ILf(^l]S6yls3k#+LfЋpWmоJRE2%;aʥEUujuA/d+8@G>P0@cAoMpͻ/"2eROn*?hj5?rUY'v'({@,3"Xp]R$ƴM*[{BO tU¼ejh7Ϸ@%Rќϲ_ l%k{hS'_,VR@c%泯opE%ȅGp3T hkqoqS3 ę?;vLzxko(*oVY }\4KZɺ;z}W WziCJ ⹥@WgCa9Cc6zn?t۰b T-L@CLhG=SzFV![Z2~+aA<[9-~$g6L'&oޘMF֣^7Tn+S[ڃ/s> ߣ,Fڵ`|`/2l?E_2 ;>`sz0[ԫƲx:Qg `Rj{RqD6L 8dG;,aLTǼ6SPrW7>#OoszM)k4qDRWa}Q|w{p.5=ao_x@$O3;3_ӳ%s!˒l6QFW`o KznB-RɲPMD# \d \^ [-anCbxU-Ig`Ǻrձ ȧP )(ݨ/W4Rr6\F盻.+;#HMȹ253lLAQԪY3U7b4&@J j8.x'Z 1Y MsyX_K7~t&N=CzBi1"O W2 +B#"5&Z?(M%Mˢ&@Oz1fˏ1d'vۻ>3Jڗx4pT8T,Pc_3u&e;oyughsS$ 3yR|)<.W9AލKunP2(ߴMf_.hs6\2`gn& mnRy A{Ve^@E"XFn3C>I,hE?:"jg ۜgsG{C+]>-z^OM[T"v&t<Ë@ @("܊ӿ8l"Qcb`=g Hfb{v-x˶R.I )8] x*(Sf']Ka'-ZY+^e5eC4,9V7*j^3Eh -fB&"vq&ӝ_,݁uIU8vSh?Pw 9PڰήCH3hghTS@Bo o_u^QԒ&˩>K & Z/o!>S\Rt:S72FڌR8ZhSlV~~d}_JAEgKRqi'nP1tsv@}PYY['VD.Y+ДLʲ@!#:ְNIp W~j¨gD%4 Z[Tf`1Q޴q/;~Vm<-qDZJ;_{^4W`>4DLdyʄկ|rUݳwZrs2g3"NM<[!m\ #}x4L-W6k#狷YFx=sn-Ѓs;^t 7eoq+vs,2/ejWbUiƴT?I_B3fL}2X?vJXq7]Q[77k S&"f-QW֎ 3 9((}Wv/WMy2W#j0+OXxp773)w{~-LhtUrIcxj¾PYNAy%IԩmwB/);ޗ+Y٪-pHUJY!whl .}v.FgEC@ʛ wsk|`n7<)FM g]'K=c}ؘ5hadf~VݾZ~s?Q_wY/DB4 |@n`0 j?𵎄ʓ$0yh߁0؏'Cu1ü9R}Z?Ļ+Tf'BpSՎZUT>GKm(B3nKpq%r\,`;ogk/S; p4imo.3O׼TK8524]@48%Le-[ ZN9}!>?gXQ_*KG (Z&'NHK-83|ޭt[;xx}nƲ;X/lLcD?{As[ݐ{-Шƨ<4@|g&67'ҕvR2j-q~*2Csch>ƶc!`状xekg5ZL'' Trj3%A,UEЗ:6;\o:X|]v̭| &UER#=cּ:B(XE^'>!'REa<]$ve,p״T9TӞj~$}\/KnHg*;ͫRW8։_Z8݇_%dO:`skӣ[*wjfĥ|glH:DTF*ҝyx/A*s#?_H.adit}{)2rgEG|jft,4Sxޝ?oCktʝU;ixzO~^_%"@C%cƟUc-]"FR!|gϏW!k2T@ِd8Ə#N^|U":hnN(!a`WK|aEe'L% endstream endobj 105 0 obj << /Length1 1608 /Length2 8868 /Length3 0 /Length 9910 /Filter /FlateDecode >> stream xڍT6Ct %!= 00At7H4H"4(%a({yoZ<םz8d`E܃[(#01C<&C;G&oU=@> ((77[_7Q TE&9Ϳ^, V t 6`CG AUEEۛΉpsdezC<`w0P' qˮq P wہ݀́z*@-0`؁ ]l!`.6p_hZ>@@;!}rAnwNw7EevYn'pwCmdoMӅq` pss @ȑwy}_'o? 8b~xn:xxvS ?h= ߿,eC}|t4>YYЏ+  ʿ q ߪ`{bXA e0?7= UG#vt aVMq$ě0͒ѣypPc͜OLӪ&?'&^>_u&g$7%F>#|'-CtޯU~+oj~ `Ar'i\]~|=8sEpsF/F&J>=m0*DNc|9$`QwO2,U/=}J{bdMLpw״ҕ Q?kg u|ɔNN Sؼ0,P~=?kԙ:ky*LHC|Ws%})yqpEthv?9ϦTTw |yc>Co^9CoTh8o#nV -4ovڌNfim0W9?ۣQ ^]S IVݖb֋On|V`3{H*~ S c hAB'UPs*8j<1F2̡ᒏ^Rviw@1 iwMWdw&/yoC:,{U6 p?S{)}"50GJ YdAdXዏWx^pD n [@ckd#“o<2NY/fO͙QT\l;Yެ*eNq 6B:h y[ Ѥʼn EϏ:q7E :CG_XSQӾo=P@aAc /db!;@kvWUa:ۓS摦TԒlQr8ŝ1$;Gi5<$y~\R=»Dwu0l+Ѓ̸3t6ɻ2ǜU!'v-騖,}֥.>*eHI.8)s=wSms JxQT]+I#*L|&oPnuH`v ԇbsK_ P/vw؊Ì^rGgjX W4sY6(sMhoկn_MZur œ 8*F~CJ3#Ҿa6]ĎE钻08TZ:{qzo z6mm`gJ֕֍fFG}'B\ޗƼ!F ma=/i*̾tnɨt𜼽AY m7oZ̿cvBA|㿙\-e6)a4x$1RJ3n *6VE h#ۜ ϫ!&)$)P_;0+0rÓh}zcމsRsU b4>Xu(R!Ly srA}M.Bg{+ѧIlw+*836{Nj6!GeWn֎T s헥i-'MlRf]>~\ ĎyN5.z^̿KH󣽭QEh~ $s>v+gǙ%~hI;v(a- F{e.nfwQTyw|jsmϏ 0PHؙHy{,C&=G?Ngcu׹^G9W¯\r|6}^%Ɲ*;65!%}[-L/!͕xca2u !(:A"n$Hzof}%=fTH%6*! ]ggn:&}MoF-l@J[s]I,-i].j,pG0YJQdped^!֘* `o#m^a2 ڞo%km7P87ΈY\*8ʀxPyA%;{;YS'?C cv|]u+\2:>^U1&H3#|8=a=(鲿B^ysi~/-4? ˭݉cl 3/ *;M& .*F>5tiM)3z%{n-Vri ۨ𒠴q>*Wjԉi)^2ĽEeh^) s^?IE2.J՝\ $ nwMC(!Wyc;݄dxՄ񬼓PhTܒ M8ϛ:ԧT,eL(D wF>,vUZD@xU'Id H^W?jݩjչŹ)0tK_itafh|dHx^_VL̨Kf}ܝN45 ՒI~n>qsHv'jL`5lp,_{%ЃZW:'G¡ݞ'Xg+c)@P[M_մLl γ"Σ7¾ڨ=c'x.ۨXm% sb|jŢ wAW[iXr܍AKy+yR7lF6NJZḠ_;:<̺= hO#n9U|9aVDW=J1wBJn6Mπ5qBg@x;i *ՊDnQ} Ư@6#^OeոU3^hN&3[j2)EYjwv&k?/ݝ5* 0E??J: gqgQV﨔EQr"X0o^_l3ךKl{u$_y]k<ڥ"=2acRS>ߘbsZy쿦kH>;,eIUߟZ.!"'/7Ou^p^蒁ÎsEe"*<]"-OYQF~Ci2u}M0A̻t+)V vogph^;QQ!sX3ypU/deWM*N, (f3#'ŷe '^92}sM3/fD.exR_Ƥ3 mg( iYhj9eX ƠQ .h49k [۩+)`d뎓|2ی9gf5D1eEQR \Qaj=`J8nVp) ![9(ʼI&<O,i9c.z!bZ^*ƉΪfaj! Ә9ܚHү94LEqvPvQ*2ѯsk1z'ăjhOeH"]Λ>1^ Aǵ쮷@z&Nγo5/B-ΰ[NUpm sI#~f `aB3> Mf93_@DJri (5](X9\h+3?vku'#ˇoJa7\\%k؆$Z&ϼ =A2${lz,e\3]╨.ҦLj~Z&kTr<4 Xd-A9sX.)[Uy=ЬO8kj"&Ig#(&<M+I_'SгkءW[G ޔWb,3H5@'P]2 \ ߿0'XsXE\+@ (: m=D}#D=@*@e:wS eYV dQP#~ B6 r; [LS>#\6ZY{B.,J"~ށ{ϊ!?3j}٣ގѺJ,&ZW_yK, dQ+ w=/kRyI˵/(.%{QtZK 8_,Yj+a.ԎtXLrc$*}mA+VS\=6-sC5se }7v>Nsؑ/ͺ))j":?&^, ^$9BB <" ޗ;]Ŷf9ݼQ~Q4"#z9l@E PbGZhIpLA2apdx\,ۂDhG?UE/@ I8?8̩;DI^Ƒh  {GP\r N!%zS&uPKٞV*I8(w]cN!U Ӯz7#II1W賜rW; mxPC"Oziv ?s)PɃy~3x,žW/VpH.V' P*L!( ': JEYl j)G/47nqm}gCؽL5.MP'>3Ǫ j}0x.mK܇(س([YMJTttWhF˗}L F:.`gWINNt2UD[bD_,x.>HsƵ:VB/w!D_ eO԰ŕm{7UŞuh|Yq"B% (<75_n:ݑ+m'_e'WhL?as%D/E܊=j&qF䧞:*caQ)]Rh[ Xdr&Cw /u]?ym`>]nQVRsdP6A.ьS=zAmEr}- ݣaN{>g/j~~%ϗH5.@݀Dc@IZd`>\{h7$㛏ٞ) B+W7!ɻgl˹TdSB%|ƪ؂דa<_3$^]`5yg ѠYwFgMJ'^"Xy[-4cϙsj{> stream xڍwT6 #F)a l86"H "Rt# %! !N=;g{xOnQÐ| )@( J 4 l0cfJ< RR~|B ( (() oq'& vs&8\.oЃcgA ̗yfWe;5o-/C|cl.65ĺG(H0f3anEC H?`»Aa'3K5WSoJRwsBb0!4#1Ak? ĸ0Np~ (/7 " ^˘ ]@@.(g_ACfNkxN̟r!Qd\١ˈon-r+|dni lM5|6hb5qޱmw(_>7 qm?BIRzߕG̬Wi\MśYLӱ G?1E7vͤC/\g"p6Xa"EFoIwrxOiYˈiOinkzF؁cRtԚq˭)%)"9 Zh-)k}7:wwil/O4mʑ d֤*ḓ/V-lyJQ ei]Hg: ,O Lj#PP$Y4GI5D*6⃭-|궋֟j|Գur.Ӳ"$ΝHm-,p0@͉ԞxE!"kZQ}k'tL]5o=7qd8گ\XoF}ҏD͢;R7 v`AlZ{>_D˕zp|a~v?1>ސd`=c/V&if?Kb L񑽊p1X>[S}ceoWu[K*ʆ弼GQ9T x;kWgt%kԸ"(\i? Wm)L2c)h0U@y9f 2 NޓdN5cdL@ʧͣOo [r"WiU}9XCu>rϪJt^+V,vr8zR/2V#ZPgri]z\*_;}1>/nnR+8'kidGQ(NK#c{KS?-Z Ⅱ ݫR^]=qTO7덤#YSylk,S<"{znpe7IVc>+>yɡ{{4BlasX!{L AȬA񊛓mtQBN9ʹ uwA|hih'6_?>vV ^pߢ"l6&wiΟg.r$2W-} k2KNOVZŽU6H&kq|pNC$}~&nQArWs>mǻ &;.q<+ܩ|L=weU.x'\/Ru2eD^ fVP߭jw[m HJz3]Awר&`Fux2P祇u?G"=0+78-7*}Q͚zXosm+F>B6w[\1Fi-A2܂h{lFF֘‚:rOnN?m9bl:!/l '$"XZ@. 7˪lֱ̥r&A7^&Uʿys%JP#Yeu6' sݵM|v IO_\XZ*Cٙ~붍Off&;_tE'xkWLw]s GƓ^LxnM̮N>hn[:YlޙEҷ\{綕TCDlbc}\;޸MH0.~ˠXI5:8Nli(HW2bL7 x^w5x&kHaQm>D*kz%um3i&H P7_:EzkͰSnqQ/eKhj9Xe3]DL%<:YI%sWessgJr'dq7H ExgJ =2fR^i}BVz( cYDQ:s^ZKхsԶ%%~1-b;Wd:8O.ݓtY/oZ< jh.`ߩ ދ8»B%ӟi-3?Q=Z3ɮpߒ=#4KW^*Wc(V JaRʭsz2Z{<Nn3D"Bv[*<# bJPng$jzh<ox,,AУ",gBJϚ߉c^`[`HtڠFpV_Z17R7kV.3 wؑRrtZZ{ZE) eJ|X>m1ثQ+3ՎH)|ΗV$qwq6xRicӝ24K#""6ɦ[S:N=4i7L{ u3'q36g^ۼ8+}.J<eE̡: !f/NJda'r'8+ g{8yi^ylʈ[|9|ĵ ~[YW-`EQ7߱lq]3%:NI fEŷQUvaM)mOnp.VDd7=: {ql6Q;[4دDÎFacli盰[V̊&Gǜ9R礸vCi&쯐_w7s x%^aWd1-nb=k EB&zg^!*®GV]< u3oxpo|.GSip\U&ljMƻwQ=[%۲ͤUc'fAfݼ~X RE[ omWژLg/5:]F u<{Km7mǽuiVmBʋ?kH)Yz|TʵyYGD[GqOH& yKiri1'L <{%q O4(1n35~nGF.tFGbΩZRy~me4ljtЩp!Fa:I[H- ݝ1,@P kvw{?5qb~e' eAڦ)O=J0O3G/)^,M΋Gw@pkGjuN%hj|5Vq\a)(8pʙ* \]%H%4̒>mΜb-8lsV]t K>6g@1qp%CͳsĆa4Li> Ū4]?:+8#DH]H͂3x7&Lۅi;/rldGçTvxJ\hCd>A5jFnp4]--bԆW串StrKG덏XH JOX'oPē",n:S=cSqd4/߯6$&t9/ܽ{&Wa|tˌ5%%d7Fb1{ud(>CJ9\籙h4UJqZ|[=xR4Є]PJ;-<ꮆנCqűDtIHby"b nFcòCv/++Gq=Jb#vwxV·~ouRzqBv"ImQ93^!ESeDtLhYG ~p|ĕHpR ^EB z0xn;զ5 $ + G)[^b"Kl_^9^ԃk7=(_>:I?L#й".;A/( cql`#NQ۝6ogoXp5PXP endstream endobj 109 0 obj << /Length1 1430 /Length2 6437 /Length3 0 /Length 7406 /Filter /FlateDecode >> stream xڍvT>%1:I(nI 16"(%) )H(HKHIHHR|ӷ~?ٞ㹯#Fa!"`Y EH{BQaWX("HBd`(,## T "@4 UE{cNX)-|0~ DFFJw8PA (> wǝM0$|rX,+uAcH3 D@Sg b@Q^o#ĝ 4pA!"+; =($ @5D~X! C}H7wP';/Bbw(GU;O pVWD9"~Qppm?=p J`)0 A{!`\h GDq?@/xÃ:"aX q w8A_W68m9Qnn.@OD7M**h?`PXT @DERE/QC(dF!@?(/>jq4Np ?Ka/YCףXH7?8{cq] >Vm,7('/饁;"0?Wpݐ(!  Cf {JpmFG`h_3'*! b0P0NGo]A"(4 "~JA7p{\@Xg ?0E7Zca14U( /.g N`OHR_gbS{h.(Mr׽#O6j8 :K2Zn2~x\A.ltt+a~o4a>o_egԧeגgbsx6E 98IoĉZ͉L4jaab <4t#Pe-MK`qLwQ<͐2)9dn+N?7k{QBQiW΅{HrlqUj>{.zP7S8w0&}8&95YʯG(9(5߅t"@#KZs gdB:eUXvllLLv\K \n5~ q!bEOW2J<*?;9m'} k?aYk{JLfիv~ %y@bj=XRʲy)i+|c[V3먩ˆy^_#/ާPC;[Ō-HפZ{ѝ%̪V23~4*rģ֩pqP;o4({"HUXLk~msECr9q yRLڙu lgk7M\\{l])Of`;KcZ)UUbz@Wd>^F蝖Z&&g?wv-lo׳ YHбNjgkLAKFz?Qs+|Ss6N&hvG={YþiQLгg.9Iɨߤj[iQxu߼ao >)|CBa HAыjpִ6gqe줕yŤ~[@qba#@ANkuNO\^\0Y؉٤K:GQ׶Fɔ#RП\**m7VxIL^ w6b;nw?R/֤9[\hRriZ}nahmKzT&iqP%eRD.aq8-?MEe]k ⌺]P>vSMط 'f6';hlੁvQS~vV"-¬ۘ7G KeԜ7H5;1ATOmEI_3T1#*թ\>^ʂYLz۪|6wNlYw%+yMvPl[urVTF% PG?O4Nve'Vy_sMfl9Nt>ǞEGp"y5y É~tIouJ.+.-s+p 56cF,Cr 1#&Gyvp<ԟ3 7Vp[ף5ࡳxHe1z&5mxVv_Mk-!+ca6t!0Μu&0ڶ-NZl}ڋU%|ϕMօ[kX`r_=TFGESwNNδm_fM+wtxʃ]k`%^ڔxNCKC=p}7 \.vDC.Qna|H\ߖ&8\adX4v ы<+ bٸ/0=!=_9y^(xl_WX*wKj7Ə(=nz#SYTEXq}+qmɮG(z4n*y hI<|z?rAL02yY7 Yh/aI+.">\dOpWm"hJrԃxs+ J59EHqyNQj4 ?Xs .輾=ݘDҘ쳑ܙLZŎs%Sܺ׿pB<(~PG936!+F?olAQ[t/S "-/^`6 /B#޼ F1#IغCcQ/-Ͳp^ԠRk;̳|4.Ei3vK*_yRTҘ[S_|c33p#V3bU[W߼kGxa+V  $Ov:Dz 68+Wn(vUtR~;"P.s+ޠ9lPFF`rBEnAi]6tmy=r$^.b I7T^Ochi󫱐R7.jn][s)K|ͮZkҘ{LX.b;҇e߆S4>8;k>;RFV}pmZgW"8Dg~;MB"3 n xɚ\>;qg0Gr@^HU/4SHFBqۿ"Bj-=Taui!hQ@R)X꒰Z1<-Mbǔ _*C8=TXNXr+dc1u.}1_3՜l 9#ܝlI͚ջJ[.Pf_/|Cغ\9iJkwksfN8eci].bPS)[psJl:O g/ϱ]/8T&Ёc+cnRP׳(J>-_(oO-3gy>) iO >(I$"Q諸ÃH8f0ó4D{ՅrͶft )Y7~ m(XHTvko") sP9FZ|W&r!3$*+ߞ0 f~yڶ*L=L^p2 ChlEG e[5-JVC <^JZ8"ќub]Jwr&Rh*AzBcGAsocIx3gm+ϋ9l*;VoH{%?2h+ܑM2IKU< 'P|ҧV5q 0EC.PV"ٳsD"b-oU8;bbg rQp5Ta_³3zG.8YVuU_7&7ҕ~i}5;ʕ_J3=~o8{Qg 7!Mj~@g}Bގk ZEDdv*w1iLY2 xf{޺{4鰈EX><{QLU Yի 2'R0rBwCh7}"L{,|r ,~ƻ{=l;p{ʉ/:]q.x )0ȝsw c.~!_XgԋKĚ10=%M5ܜ؏ g6SlV'9~>4Uj_z=Bh1ym60>eOLR8ޭlDDM(~绽1Q>m'ַo)KkܻsSx]2{M%9ݻ <1x g m)Wi(ը-|e7 od#vE?ͪꚒ/D۠m? D}M?.6}_=?ӼxNKA^)1Ãi 4Â*fIuy^^ m |Jzk⾼TLi\>[="&>!-N.':DjD돖K-]F9^%Y׵HƇ,u _N }K)u< ~ڋ/t\(VskfdUǝ[nb;y&)|cfa𕠛,isBn/E}AP|5TT)IbU>'<|J,KU_Ux9j endstream endobj 111 0 obj << /Length1 1912 /Length2 13038 /Length3 0 /Length 14210 /Filter /FlateDecode >> stream xڍPKӆ ,,www5%8ww{~_uNQsLTAAB/djg 9330D䔹LL LL,6Y(ԁNv ?"@#w{ b`f0s0s01Xh5r41@@' ;{GKs ] 6`{9@hib9[mw41ؙX='5=#Ot7Kg 2 4U.@wa U KU̜݌w d t P(A WG`f`o+%F&&vF K9PepvwL 4q{_ojdicdp#轾WdhidiWy?d1->QKG{0} ;7׿,Af`bϨtpJ;݄ `gbbf@w ƿzvm~eog0{/ci|d 8;}t/!03L-M@sK?@;Zt{dOߗ˨%'Gwu ۹Y9,Lf&N&foo念1Q dfW g2\OA^{+K_=.66{rxl-m<b{ k倦.+l>B s;T_ ;xOoc *9Y虙}Lߟ|R dbg̱s</^i t ;%|fv';Q/ӿ(_d0JCF=R0*{CCY4KdqLKl9ߟ:;F?YfiG{f{Um]$|a?b~KqW8\we2?]on3qqt|~ [?sMLxk&g)4?{-;v<|pJE#F}#B65I Ayf aigpPSҫ染\@-*گ{2ZsԷ8 GrцWg=AQq)M6rh"F^xp/[ >GDkD7Z>O %S"wJnD؂ | i2ZKN22DַrY%#,u617G(i`M>^|:؄ Z'!14#1; Onי97}[>jALѫ @N6SswMMO btdƌ%9w7ma_mklǍUɈu O3rrH؁Z+Z|$y}-o髡vN:Zj:=fWD(f+c3@\dch, ҴAcW3=pIٿ6W/eܴtƬn->9ǫ_[-ůj/΋Oͱ SX8~8%)d-u7ؓtQmµ@.E:gc6=$6'Ql!auΟ`$_rdjun\( 2ő[! wRrCEB:)W?oG*ABL: ^ 󵄉86<<,M IƼ|1AGY9$2e" uPUדDϺ/7t#bWI]3*⇯G6dp,i ٦QCvR:?$C#1;V>d.#}Za3=6)VV\=2VDL]:Jܶ$Hd>tXWcQQ׎ lrTI!oP`xocFn]ꗐ6Ngzx .էU:l$GHm9m:zɏP(&mS"׷ULߣZvG08 W[+Jc~ ܭӞ[~u_XI+ɼg\8b?y67ސ '43BDJ~cA8V?R lXpa{oJj۩1q:f\({mI\&+%̇>s%T|_V6o? JpdM=kpR83A RphdCB*/z]6 漪˝fTB1LF&T/d,2i+e#╟j!f{;b햭Y'Qgg@qz")vUɀR~Eq7 PPp翌){;FK[LzO^$]4A,vQN0u>p|~5}xۨ}(ApR?yjħ") lq*-sOU6>\M>jo91pΩv聕Ov#-g<5e<* Hދt7aL\I)} 7fo\;3+RU,EGh6"[*iܻ'2)f4 D] +kZԛf$aa"mkvgʯ=~Y}pиj9I+)UQOҟTl#w`JȔ eݳMLX)gۗl'tu-'1w7cZ>/"0U|11@ tmuy+ 5A K[$̓$S`ŐjQsޚxtwTa7S[6 #"QՎ4gRBيoW7d,I!1ɏƱ}q|>,v,7ژnz;lUu}?/✢+=n2\)/68s]35/F@}vL^V5if y.kFO@iƪ¬tg56#|p=ǃZ wlc33'3};(5!uίJ@l31q3FvpeR$!9 8\%wk f:*_FXF_[ċ BoT90B5$ , fvB/`b8ǩgs&><^Nq톭Yo,M2詏;S'c((W$ɂoE%fQzâX+.${dÓ6|5gA-LqDҷ5_qS(y f8}[Dŀ'SA{0Xn"6ЧUr{6ڢRuSΏ{۳!V 3 ň_(Q#I{FDjJhBV4Lą ia,j7_hΘq2x_ϪOu`QdU3gjVjΨ+//$ #Gȼ%sRc WqN?tX# DSi -ڲ hG+W@.>9_^DYٴc%'~1̅XnI*]>&O㻮ڈ䉥bv"glJɃC˨"kxdëJ5˒f Kv`#nIcW]u;R6MUx^qp ڧ%k5'9B04aJ,("B_MAu>K[܃e>|Db*|)J 1zg$ݗl@7vƖ0y'=l îjC~ϻB #6XFHɊeehl !ЍZ}#3ksx!Ζtԥ CvB; {mUA18xE6'+.'`eoN$kOt*#lNmJaW 6/߉*d\ےan$H *鸸l#aNPTG& C&\/D3oPt=?d-Eʍݯ.- լH~&Oek=O\>n^DXJ7~*]WBWc_.",R_PN`|\e$:ߓR)*+u80G1HvSOYIoe{.BzM{Ыi5hׯN=5Y#Ot,6?;7߹o_lx^ǘ =3#cI.dCK8 &ivܲ2 i5B'eU\97Zqk^]|-8$RC 3j_9 0S0.8}Ti z|%7Mv7JDBԋ S i_<CCk/OJZ4hh.).$fBSh"<&wShfa'C/zy;kڿRg';FvburZ4rHΜ~rqe~{b ۓ{.thBK+g]o Rͧ9R XDk%jo>Q͚GѨ}U1^W޵g|1̭4cB.v_ !l9qYa==ݥVtpŐ\oA<yHY[Y:©]BC3l$/+0գ)esMvy,71=mY'$7s+֗.Q_R&bHů7 uI Zrd$wQ[}M 2\4b`Ƅ4O SDd|HkJϙ5$hi0|: NaےJd>Fˎ>P:وL6΄G5gN%ˠ^+2K45gHùEl}/%3ٞ+xXa(=~>j  xdNMHmaV]Π@<U'uv~% nWI' e 3=|Ђ\?"N(yA9>DpޮL/S=9ȡ *>ݜF蘹O+Z}#Zq?ڜKDZ^pЇpuQך.&V-}ҘclX`Y BcNQDAY볞sqf^ToԎ$`|Mnfq DcLɧ9.;ڇ-5/gs>fM-gp.>DO uQ1uLA\닛~,G?ex18EgTN"U#k5;= 5N<{jd\È~]iR-H9RF!Gr14N͆m喭y9a3|TlӯH)bZCl^bvy ~>l1l3xL7mS .vPGiNb!!j/ɽT=Nd12;AI{uQy mW&畲q/U/+~uPekt!IT)`) irCI>C>ZmȈSkFU| UOkq{Nm]) aDGg\7=5 )+2v`8:-OC>B MyaGW.dm{0~Miw3lJad%l ١uHl8яGM+ 6E1] ҥP{",\PV*;4X5CVQ'q+ ggR}cŃ̘>W}54$̹0E*9m*}^=CشGBr3i X- 8Z%&kBmH+KInzԜmKv1zA}ksUQqMT NLaySxN~BaM?zSӆx #͐`XjSϝJ^L"˅H.ΙFhq L>t?ofswjBlxzjRpgk8*/ESK*@1U 1NuG%]fF3B˙RXɺlWF'qjz>OPί<˝HSq >}O9E%E /8+ĔkC=u|ЊMg$h"&jtALL۹lPUׯgnˠmզ?\t,ܜ_Yq./6;$0B8NlAW-@Ek<ʼn^~faZaR=gDTK: xKj$"f3*oqO_ȎG2F׻W&0M={Rm(L qMTAƉ / 46L3H)hr+ESYJ{/+}w+FjI~ܨBHCov5[X\q4²GgK~(X ~.Nc Ctd)KB@c2;9 T}kc%K &3|-v8(1߮ԀuŒ>;?Q*ris,Ge|I??\YN*"p}N;,x닍Rt($emu=@j0NIY>T˶ߛ^BJM>1dʲ}&;>-tWvhz $1|iJ7,= Ufm2iq[ŅEFXhA%eo wA z^LqdxP J)uԅK~gzs_/ W,-gl$z@{]1Q~Kɜ6]ag>2rx`Gf=G>0jRC48Mk( iilFk)Dwl4:1PY%1:D~A^S'F7/\xItx? -7 I:~s쟄\Q Gϸ!{H"xc>YFvoVݜh7n Wđ+AF-\< _G2u&^LNw#7 /qx~ $xnݢe$/.{1oAl䬝Yfc GԐ?cnऄ^q^'{,eJS^Ep8iop6ŘT3o lS8?(#v0.(jq<fkR?pFNVn'VΎ?;QyzB!CJГXoGw:i\8!dzƪ*^$5 V\[(^.HJ1[5غBv ,J*v} >M3]G*ajQD\Tf lÓ2-WxdDf߰@W%m_v ϲ'}W`Bӷ~2#jy7޲̸ݓ9  lO+L']V>:N>. ѹᙐ}Gy'-c@rYٞRo[qK: c+Xc팔-\=qx=|ﲪj~ 3jc{ܑ\o͗G::K]l0olG)ϕFaíkt},gXYS^ ӦfFx1 u)@%$꫅^>g^it Ln._*Ɋ %ˍn?͊ECWѻĢWr+S3(/^L3iрηkŶXh"lJPyS,CK 4`x7,(jGva|L;!ބϕ5OVK!~ D >r罬Dn+g>/?bw<Gr]DXM1WOXbŔW^؍Fߪ#s%^RqSD&envZ.hB;r[7 ̀wc[ԨWl?Itτ@2|5RZ}ZJ/fkt6=-e" Ôd(ׁ{JeߡZ'}'0A E_!{[~؎ M(uC5_$.rq>\15pq=K@+塅[I*c?8js; Ư=Qv-_}\p/,{PO. eh?|Zt{*uʚj2[k)XN`0씛R:@B5+fzvU'/>ba ԵUmB@J^W՟wxw7k%(6MaOb%v9׶Kr,X 7ڼ <)9Mm@~ ^iM]W|Ƨ+TRk7f2qsb4$}#_N][)nΦ%HQᘖz:m6U+0"Yh p8CQi#U'X(mO- L`!ki>4zk~@=A_CCWS+TUܰf@/[>d_U[C!yPED5)jtNŔt-vМv|03+$EƐp 8Z.Iƥx z*qg,dNISPyn#QC:vI`{=> stream xڌP\w`@Ҹ % !w.9gf^սEU듽T5,́N`&6fV~:J%Epdf73%'G= `ge+?@d Pb;9$]A6,}Zxt9]Af%3 -=@{OZA0ؙӓZ ԁn@W%e3Ę6 NV`O3W M`:9;Z]org_Ɗ03G fNf GkPVd{fٻ9y , -0{vn g,ykG} W[׽Y_V GK?(X;h9\܁rx!#\<| eaGpMogJ?o:;9(AVnf@o"D66% 0Z&Z& -v?ˢ,'UN^_&>;+Q_JU@rVN(4<9:fPvzc 7dbxa?GO-?-v]]8_S_G;_2n"MT-lZ-=5`bce?[{{MO77 0su5F|%Ͻ0;:\oVN̓ "/ `XA'E`QXFoQTAozzzEAoAoAloz#[FXނY8ٿN?$$cP,o)ooJ?]V[vjy _o2п[o uWmoDZߊs'6mzo6}:Y\o\ܝ@Ks}{}_1ۛǿ_-?o7 _-f/O?g0^@ ą_Nuw5bL;cBT;:|\;PR骳\oR{ЗhEI}Z>&==$O!O/>'F b{vj蒧wqEU-ĺX [ͭT1cT6CU`3O f"I$khE,CbxDd6zIG:]F)`وLg|䤫LI"ѽ|K;asqI+TAD$">9,-vkf_m{8λ;&h&Hp'Xzp' 7d-ncs,J@R v_^h ~%vt9M5SQ)QZiXDg, QIE/lzn&D0}ϑW*~@\GgqBmw84?jJ5KpU3>J_v7ܠRt ]\ #&d-ف:8fȿt UxyӺqq% R 0Qfy%q9e5iJA<)QLLC39,\u+#Kt'~W-([(0JJR!tZ?=peƇ-1zZmF_ΠQP'TeoSٶj?щPX,jfEܖڑ N}w}o"9hj'E6wUAC;GWF@0*GHt06xɑ cEGwk;(!TM"og}L;1֏mM"#>N*}duDʒVg ,oiNT Kj&/|#/<=Tc܃́C9C}[e\4Ycܬ䱼bq1 /6& 5:3.=˴:A9`MV;sW3nM.VndvjX7,Ȕڎ{IHkV鐷yP'k蔕a୭`oWV Ș%W*IGtgqI~w G|uM *gg< F^C@ Ҽڏ*PBCC9S,r1IWqFw4-_+nbXl8Ybˆ[៑ZDdMiHmm/tqѣ7a旾Q GPUuHJw ";lŶ먽jy~AM@)5m3 ޵~_t4d?gܷk-5-XKSt[\*ZΙ~uoYH,Y #5 Y#%2-_>#I'¼$I&fDEF_?G4ͱogqK*G*;6 J/D7cz;^qNt4w[!+|}Եqґ~SugFmbAtk nгfW9V 3wٯ ʻ&Gal~=QanuK̚< Վ4=חlnt ޜԗS cQOkn&&cl<`c'hЍx9;DC&ڳ̇]r/z<H#>Fm?7o$ΧC_<:X i<*b oZQf \h#ή=g_ C>X*"*{ Ma`ejϯς-$̥$AߴWJZX)kBEzP8cS&YnvD4wH}~Vrc-u9x`t|H<ۍo_w p}oMN?{5D3}2ZW _ ܧW. huzؐBլ:k.d\-t~;VzéXXnBI:S(9a|vPBΈ#ie/AE8YϋHN\Ud tfȈ74~bNo{;mYWGKR Т\f))ҴRM=viBmTqAy;KG5_ HxA(*Mޅ_MMºBXSy9M 0L|3eG#9$L99AgNONicל:Рgcp'#S'rHٕ0b+Vb9Y}*:6~7gjvC~C|@ ɝܶwvI`S&(ba«Ul&-78IB# Th!b }:q<݆CyRxP3txzkAc!Y#4Wn{칝i9#v>HuRUi'-|'ŘrƮb]s#qo 1A#=s^|GlH^ƪsgtW5Ndun{Q1K.mMTw(r* *!8]tԬ> < + >|z:@SmE'v.9~,G,MZ.yk(/x_9%,U!L?z]b1Q SNu<]C#p$oKX5^ (06\`U-@X~6^}|}"ci@tZ6n pe0Iw=Á? BVVk c$)]ZH#OöħZ9q8+7ډݝHߗN,ܐw"L4B!jJ΅} H%K8|h$N"Ɉ>Z)MhnܼYדs?u*D2–Ku9_,SeYQ~L)o]+7ddKh'zuz}sֈ].߅1 hk>#Q U[U.ú&<}C0}y?S]:y;~iAj3&;Z`WDw%mI#+*9uۮqJ|n/ay.IzLə K NZD'ʌK~ P&͙9|nm.%CMb\O3)dyFwgm+L\\Rzx*q cvaI,^Eg.:ƃ;r;in$hc[={681E~g {\S\ 8 p! m޳809q⳧z)LO1)磉d%eL~յ\,)1lcoxV~=b6e@w'# ݿiwd۱9L.s?P8;lc@,{x+ܡڽx.W)[>X~űmQJ2n_•cgX Zh]>HN#!./I|@p+Ǡ*JGf'] A})΃d_*3;n?"uZ5Q҆obBM!:IP-"֕ 8zUA#Hء F /]:e[N~MT-+N?lPo;;;CÿaoRMͤQ-4Q2>>J yG'ݩѳwFҿSz]bD5k~J_4~Ke-q %0)و]jopRs3t#EB3~Uć|wڟI^mxC-@4*[{+!$h hhDe>ۉܱ`JG@KP!Tp3^t)jTѫnKƏ_WD:?692)v`RȐ- ;1kA/sށoz>33+!B:= tAZW],Iai -1N^5ƪLTpvŔ vx|8Oլn>^ ]ըxjhC-ߛѿ-pivi"r lzwz7)2N{.U|TT7h$X~WW' T\O5K vh5A$q+r?1[~ M3mnx9p~%UUUy iQ`ӝqTk 9!{XmaavM}PIj(u!4M^S'7f&Xyyl3 ȳ. j݆K6e9q +ƔTԋ4> tbNl@=hb\}Ż 2w;#JQn(!ry35,W],j|f#"lm@"diIj)Dxk2w  YƖppjyWDs{Q,na+5Ow"%-#qyȧ!+՗%YOZX 2ƯݛٸJCwA_)ќ^ %ŠT]_҈3 ]Y\ID&䙊lBn9)-*K01'?N3 'ҿ0^nHR9fǣL=9k"ۥL؋uǛ^j=$  ?9n2i8_l0=7&4\D呰Jzy#;h?KIvD܏_^}4Z$U+K펬kgG![V1k Φ EW,M_hDO C<ḗCc|=4rȐޱĔc[ ߵez QoGǎJkOnnmQ:SyrXnMzϡ,y1{S.V V$ۨ=g_B !g8 RlWve$dd<.8gG)WWU\ϯ{Gȉk8h\Z~;iW>l9%68s>b";+.jujx9#~q"@Ȝ*rYof5KfeIU7`8 8M_ź,ӫGKZOׅ+hF8[?9ꥺPBx+g[=cOls6H=JW,4ߨ}⠉Dh8xD,n7D @NQifO(w&r UՑO6'[X)%Z lGNF& ɋ`br\nԶdh&twT<+MoToaV4 I{ui[iɋ0( e.$iVUߏ#FIY-Xf`3l\HCLv>)"3w '.=LL<^tMT քk,|Pg(Ƹ<8mD4C9'F֞d s_WʗVI=;J2#*!b';3;Uo #of3ZeUz1K}\`ruz>UiZX \Ӗ茹خ%?=>|q3hi7:yc;C M!WvdIHR_D6;fy2P1~71X(n8BcDr vMHOPpC` 3$_T<&zg(^dVSeeC 2AX"ZXͲ9(rL"4.Cd2!5n}SHL(nBiLZGP?[t%zq|b=A_XAm:ݴD4BG(m&%0+h^T$~KHbTtpbJ˽0s=D pd&xػIaI)1ѾR7-:U/)TZ-MLO":d !ti$4a}C[aȗ4BLCT~$sB6C5oM ";5ţqb8wUmEݠr nkڏ^.іJ]7~#\by*PҴ4w):Ֆ*!ItZ TV p#`*1oȿpk+Ky"}C葶}Jb߶$7BՈ`-ƜS"ZR6Xߩ֚ݬ%![I"܎j$CX\8)6N,zgSIlP]v:IDdזz3YoKYv 2/̦W7VEiIf/DBB[Hʺ֡+vB#Xo@(=i'+9Jw=#Lb6cU RaƃdSn6~1_KϦO2*ӋaaՔb<-Vn]yTc'|3y5W 7.P({{~AB[=崣e /sQ~_7R F5^_66;O/167s|7s352Gy#& 놼#\AQuGdM_c6?y[i4ڱ!J=?wq{zP,| >mX៾éfC8Y"SՎ/!y6@sйoۂ4{ q)Q3k,Ler'lTKDqb:;Ldn%:Y5sTXpv?SxɊ3lˉʗj|]'~)ziw[F͏:AfׯȞ5@Rg~<ʺ(U#J,m닣ao1L&;HKHCH`@Ig»/KJPJĊz洚`boOxD-/ĭpWw8_`փMI "h';@|N/jb0g8^?SMkW_qh_Hַ斘I62*V?ՏhxJ- Z$ /Ŭdy[?w!z޵Յݟrw]-ZXMötsϚ{UHQ+>lK gJ\d} JtT_E!w@;g . nf1lj^j @3\kIJ w] _OtO掻p7I|@H0*+*p]?.ґ $Mq)+nN\2@]_u,Cq F? DҌ>xrJ?p!_ERd' |WZVcK-_  xg*=39"|`2!, Ȯg3rdi4lh۽@E2:?ۃH1ֺ>G6Z _b$2 hoHݿ]qcDA&RQE;C?"y?9.ޘ>ݠ* 2U7zR6{yD 0ljux U :pb[ct:䡖SzH~38YݹYO(_CtTpU:Bʛ* &,b'kv?:".31+!BdsKA0vȾm0db GK:rOKQ9&7$sN#C+$Т9[A"M MIogej 0!UesOݯ9}oǀxetfVS64Hrɏ,k|>hyXRԿ; Ʉӌf 1$օigJAՂ,uk&R-$'yoҷćc^h\#w;d&iHəg^?-MS<_MJ+vo R:#WNK3Rڇ+Q+?"4]\D&ĜPu[[u4{^MiZ b-vOVD?\ ɐ >K(.pn5bofKZa&hj͕b Y*(iS{.fInɷдI c2ي3YZ >U Mo'#`ֆM|_^4CROC?QVz0dQ U%C/ORnMP7{iϺ]>2jlȇ=QLˢ{ttp;kbWScaaOA[cHRP5l#[Z NC"x䅤Aoo!I>U;"Jb|m-bW$k]}Ini ߭O,u=gA󕓝="DxȮˇG o\.n)s>f&hHl[dji#gH4{q/ÿh5y—) )}k^Xc2so:CۆR?=q9q eȜ=3¹Wو|pj8P^ƻ1o}. _lC` ժpYMe|A{qxQ!"&[PN8d ? -IHb Fz`ݯX`a vn\O L,_*^Y6T֤ӢF pV^l|m8m0pG~sHn>԰- \ iᲸΝڭa(PgtqlȨ/H J@^+MySAR5=B?n]2XRˢ$"١r-\%}7Ԥ(Ty_ ITr;)3LAz3{$4!hٙ: ʱ?>7Jc eFJӺ9A_nR\bKG3lBR_Ckm^;|!گRh+xh*Jخd Qx"/Uهǣ~[CQ&L] ۧww-jNaɜ]vuFsQ-nFIM-5b5znɮRP>È*`A<8b(nN 'BK]-/fA?* v#?#A 꽟zؼCPsa!ufAJh0W5!"䩛&[eHu[$yh^ɣ׳%~8펕oCy;,Q4r'*.jbj#%r~:%ђLL|hIߐSD>}f]t'Xrh׺VQŗ|W/`aYӫ$KbtR0}1\e+kY-_9+sʾE*nO5֕)!J6 ϯ˙|#z[hHĔ ՗8 ? f(Gbwn|-.6n,"Cʇ+3(ig7YϕY }QOUp7'vŜJB]u}/0mzUg>,xϴUޒ3uQ<.UY`x3eV ! \+.F"A.<ϜjC $s̮I~ld6*?;m 'L=烞 @Ļ7Eϋ gE;iw!cܔiQ'z> c~& ]Nƹ+U$| .IyDx(X50N5 XQI w2%kg|),'zdPV # 8KC"  mduuCS,@۔{;`@V +ͷq.##dV_ ]ncDHIGKjL̷lLk8]ѵkO\\'EJeSf5d,cw.Z)DQLNJxTJu R_$>͓"0W=vBj1=)ańY"uԧTv.K=JZ$Kvi[  g, B`r0ϡcǬI* S/+fFQ%,F}BRa2q;_1d5曓% g J q̀ۑEzj vWtoHyW!U5}ʿ=W" îHggX>m900M~%ؑP+@!Y(YeK>¸a#\_S}T^C`=|&UM2Z2I!Ln_>y;p YInدQߍE=g:{].V{ڙ,_gœA-~PfV$_ 0+-p{p\J =|g}]PZ9ָI g6ܞ\ f7!r@%4r"|k3U L|ww?Y~SDzȪ=7.\࿼D<3N^wۗ:cIX'DfZE0 )xTG0(& Կ>$]Ck<]36~R6ta@ue. `,/[›/7́QeY+3Km;{*3~'lW -`!gqn@V)6\m  Hq"~؊`QK@UzV/а=OT;)wGc|e#cTKꡇ% w'~!QUǥp"FvI-Ad)`~ Rã^SnպܳR0PZt؝FeI#l`lTLC޾`:";vJC~^'#EO½?w5EUF 3*rd=-zeϿQ2@)-0p, } g}4+SE3cE0Y I3:$"}kbE'եH1ljE]hjg씌#k63'm.:GL$6qƹHUnsJЧG=o Q̀Dunr$0^&3" 3 Zg)$[7 }y^ BRƂy/ l>a׃FQCZ LB܅4mP1G{vH[4R'CuDP\5Avհ- }j@x~%%UB!4C*`zm0WTQ>ǻcu~ =Ve7}f_ТKk!ټ&f?` ٜь<}rЁSvC}DŢ܂X!FuJ[;r Za.*߉N=N[bVRi%E|<_Fǵ)M>pi/l ̍WZ*\=Ƭއ߸>024ypD8h w[e77yslDyx<)%7knoX u CLvQ}{d/× K:ț.>JJKkPlȹS:pl!v=t$3-6i4zpOZ_>?=S$c0`8E':H2Cʻ~X@Ȳ0FŎC : l#X zt/[K_q$A定Z䫗glqH~sqG4q`qPxUsO%{ev֦|QU(2;6(0͎d-J֐Tb@8zR~lS'eϿTH!n6O9;ܟ3HOn b9;QK*zDn9B3G֏9i1x! #U{(0焀CpIt5<8ڲltA6)|~JMܦ H4˰c z60">8 妀P68-j$ogwVݍNR"NdT_Ljkmj"-G/@OtXr2#0a[ԏ}:1h1Pmzg2?(a5G;=h Q6Z@ ]]K Kʼqn5]pNR&5Df%acVT/h͕^4-:Nj>֩=3 C) xqKX(-y@/IJ|"sMBa4 +mF1Ƣ}b3mزa(Q0\&3#Egd9@<>[ 0Ј3AV'u)qDPBVq3%49lNH,WLxad(\mتG~ɸ J.k:ZyeCU@X#ftY!N?i֫B?t$)iUŪ? ڿf&{lւ)=:g2p"zIZ#2 /^Slp3X_e}O2&׀{,>ڕq3:Ԑc382JR=3z!*H' 8w+hu0,+m~(,D[9|&Ptm-~!mjSX4s^g l2yu*efCO[KnpG`Q} kI m=$ff{F=oG?]Bo eg$d|Hu.Lvx`쳴-C9X=ÓO幢qcB%3BZwHZi뚁x)O^̂A endstream endobj 115 0 obj << /Length1 1796 /Length2 12569 /Length3 0 /Length 13692 /Filter /FlateDecode >> stream xڍPYwwh]5, cfgwf+KM,nhqt3 $ll,llZ rDj+A.@SLfPpsyyll6ttH,,G+" ߟ:sz;??/ӟq{ l ڿe47h:` A'd ; zxxڻ8X3<@`k2@Wk,-k`S M`2:9X]oJU'ÿep, @:;;:x ; @UF f:Xahjon 35{3tS:ÿs5w9]Y\AvGcvt:]O 4;w/ֿ.d r 7'VmP^/72+ :֬$rdCփ |oBdgX3ob]@lo`?_of`Wj#WQJH8z|9lvv.ۇ ?jGDyKGx;7d6Π6@y t?_+qSO/dw{o Btpx~ w`zK ]UKG:3MQJ y Fudh/g♞fF RE?9iKJKeezd&BY21}'㍴\2RӰ4/}']bAL$[clWUy-hrHhQ珤A:yľ<'#lGZ֦xCn,Y˾hcوYĮUJNК_ sb>4IS&K9 ˩-pא!|離#kW3P˸(Rl#sP$a.5(꒞kĚ+P y'ĜTL8Ce<%nyEc&~n-?r_tWQ* Ya^L n4ȒN!7m̞P$%\|,Iʧ|Pah}t-#EIf@G5?NȠ5P˂C9ދ a>K)^/n(8=>v k$@GTX#%`\?t$6MZّ _yckb﷤%z'Ч5Ql&R?vcOXo겊ҕ!~B {XT8OtSv)gP}Y5nObMeߗx+;S3_Q's ƢxBgð8WH䌡S9UIK:榗ׇl@*D4ŗ uq+P!l\znXQ@tJkPXK!wܲOk=/:†ڨx|#c1؀;p @=AK&A-ƻ& 轒Bzx#!,Ƀi#+;9<I*/qXKx",\1:8G(WJEK*\K!\B\pwwO9:V{Ԗi_4C?7B:;?9ŋDAq<\~m^WK(TQ&<"o&Nu sAF(x7V$  H[@ogU^0elVMh6,9~b7*Pd"J௚q鶆4]kZg]RH0J,),Sc^J\׺b~PG~>{Ҙ/tnzok~9[9'ӥ{([7+FiY# d& p|'Jםγ \<.a!QX)smaI@lLP$7`!B4{߬^ ;Ī3cQQ " SqK@,~L,!~]eT f҅AtqcWm[1+K-`*6ֲ*T:w{~we/zjT;TE'XQ||p5<ˏB9miʆ-gvE= IAMw)g"s&;DSFYQLٕޱzԏLBW~RJ0X [#j%TKˆ<ݤEeS:]K>ע^MtQfN3/;z[mH@R;|,w'v]hvq5w˩)tjBSUwvz$r6>2OyrC ɀґ՜ɨ9Iw=l9Id?|f B>93r@ *6NF+p2da߂|eۃPt  Ѫc }ЁK",nu16HD3~4\+1.vD=:hP꽊FKH3<5"i?VK_VNO1?;j Ķ4vўWFAKME ۖծ{)2 Y!|)!hrcSAH"z\aD4`C6m΋A즮/@ux3R&{9Hv?Rc5\SAj.ýp#Ma ˔|Q;mMSHW@Xa Ft(g%t@׎>ۿH#՝& e(T Ɋx Fv拱6iV}gY)N$?ɒZWt,ͶBp̰HB/Y[󱛍 BPJH %ŭSqg{9V DFfk F''f5@m5غ ۹BB Vth59UT#BCM,~]Ӫ9e\Gn~TE0ӯ;GgI44/˻cnZb(06gKygqp1vE;8=h.4Nd/i2i z"OdTxLxgnWlͅϮMLfLp`f|սpЙz WQ;tLQ :6ghY͛!C'l:gWMF9o j\6aQuy^D|#G)Bi "p"-.~N1@z STO:h(ێa*k/Pw$)}Lœ<:螣k|:t*_4^k<VYa]6-QlKn1Av&εn<︶kNS e27SqWEVk#ɅeO}G.|XY?NoŸTiUWQ+*e8 ;PoHrru0*AOh=dR9}Gzӥ'1&.bYQB!^O `E=SDXh7]< 7vk$&Hsb̪å񳑠 v< %=+x8|< ='KjY7z zCs`V/mgBbvƫ&EmS:q Jx=K6mϴv6E‡ { +N Un +1%$F.ƹښw|bOȡ7L_#T$J/:hކNeM_YDF'+YۭФabFj Ͻ}t0c7sZnԦFvIs"-l|aZ H#s;Ms`0_kePGfLFi:L2hp绨ӡY焥6җϺybha<5}I3r]K{q@&gI%[/2z5Hh*!kZIUw'd5a':>NlvIa"J%xNkl=?MU˗ūr9"^m*ԼŸ$OOIAG 6pk;azP/CozއQhqi10CTw7+(57O&9fԟgu#:uպ`Z#[ 0-[$y`GPh'og`JəM̑Ot xâ!:*. H%ETL+@{~}$ɉGFARNC5{DiaXJOoUtSu5:Gw@7s\o2+.j.;^= h _yKl1d CLi|YLx-3: *A V &OK_2¢$$C_\+HK-w-:%p$E"|Hf]xz t JԨYޤ#,$_2뼆'I.$\>n?:U}gHYͪo kbྸV@ 1kBq CTi1/A@IM'y)X&pp*i/BP_)\Ce:N3-ӳXEhb{ԁLyϢ,l5al+> :jO'1Q@ٚ諽鬛,cfm7l{yk$x!4<\rkI KUhvTUA I`no툸4Y|0  YKܩSRL)݈_@!pENV6)*UW^kl IbgQ`]{k7Дy)4nӡ%nt^\tq'SBx޵mi*@h#健LVSh#zk[kr?N۩ϔ^ ^:/Z] ? @jHɖnp0 "> O~-\ł5mHu*kW- aͨFJ]z԰LF؜:wx4a ?jI[ $ggُpۭ˒LyCf]];Tm*e4qOkU1Q& sepPe3p5'.^ }Bl;H8>TIo؁x(qϱR/9E 7(*"Ճ+Ԏ9GHzF*i/#cʚ9KN*44 68ϴ0esm\ݧW6VN\Ct&a>$ëQw-Ѱ*pekR°; R-0VL~>sΕ=Z28iyRI3cszlC5h^znr/Qõi1 2x2L6WXSV-ݩ$^֥7Cx@c}[yVfc GoٍF/ SY:YB,^{[2ЗCgP#ٍ[w^x;h̗TR9|  Q;gMþ?q%,`q gι 7[WDڴXL$3-ӥ;ҝ۲#<]pZ+r'S*mi@nd41rfbĈ2,`H1ճ"?_]ݴ//B㋰EI.Rtp}Ms""\K\>P@DyOQYTWʨhr ;2n5l?8(b9~u;Q9|Q`>ãa­’XCY,;h4dȅJA8[ڋ ͲdG%e|>1*]s䘋-ܘ_u-&~,L(xM9sY&pq뿠2ϬBᘔ+> ^}<;Qc#*#<@' ߘQ٩tAtxoLz[w6#]U xƦD;7ɐX\BBa% q o$uK"@[pIMOz gкWkDiR2&d_5&Ҍ/{yȍSCTmdHD^S9`^j K]JY@{89ɹV%5ՊQu=4qpv,`EnߨA=]_lFxEyфXuYzώEfb\ի)~͉V%aF6d'MF8b;;i :xZtcMEVE6|qvq>q#lnRm#~( d4;;t i %-G*{Յ:;C[1y.>(FL-^:Q}(*uB: 6OF)N1v )fEAF*fxS.Jna%A(rDk$ߩJkKnkg36Zq؆[*>EhbFE9eiSAeyv,eJW2ڻW+#w/렌Y(rz&7WX.21gλI6O>mq2 ߈fj E2d Y?ۘ{#HpĘT~ Q< ̅tnpqjֆ}Dn@.5=aքQ<| G#ZMvYs"j~D}a=!,w5K}W`QOB)pm ,ե*) à@uofri/݆!lJND4o;սN ֢:f⏾O+cp|'P"R.) &Mx>;9-Jza%Ǡnl`C#1fDgdwz=NHFb@G!2կp$n*nޤ: =k$,DOۥ: !=|ROsYKl3r XPXjsii6f(vRG9kv8@, h15VDc//Am\ySãAST)XF+9qٓHGyVM ^=#1F$ :7'aP>}a~ScorI4VyHec.o35r0Tj6f}5sbpJ$L> 'YRC2w[ e+|7(翅2Cl)Vư0N {qPpgӿ:sZu^)oy@EPZ8@LږUS CVV9#%ضbӪD߿rd o"*11,`B֗i;Lj:z¸.#28o@rX97v[d^qbJSgyKu 3eᅵ^c6 uD}x8jF.O8juuh:tpNLB3&nB~H=R -72zandDfvRk)5Jva%`hr0նg(\>JVtu=u)L"fRxv,f̈́Kf(Y Ӝ ),7̋=9?r㌓*T:f-@2bǺdpYF)V*q,sc|ȑ ?sLi# Vن+|:ӴKTotH9=$2?yXObc{1 au|ʂeQE_چM)eeZFpt㡼տqvq3Bo*$}DR-rbb0IaAXXc5:&G⑍ \ݠG 0hu & .w\eЍt᫞_1lJxM6}#4DPH.I9\hF~pi9QY޼*D @ Zȳר~ͱSoqMncJ&LP.1n(1hpG .hbYNYE!)Tbm_blOr74C KL ((1/q i;zh1|n^niΚRrc"}@#K a?_xNjpPM{ CyMk|MCotIK~w"+Q+_u?eP-*=يo~1°%34ĻkW@I5,آjH I^eXpd{*+<>gUG]Xq3뾤M}کm 5 T@[X8KW!9xmՖÂ_$^W{4r}ȉ9,*J$)F!QhG |5 #)z=.K| inrnU'8KΥx5EC?MpM?K!@zTqg07GI3= iiy`yKtɽ-*P\ace0:ēkq굥B4}V |M,blZ\X\p n<댺뭜&zpnveh.)ύ 1"jfyygv(Lyv0u )R+l960e,=`~,bz). L<]90|#~v߻V}?N:R/_`$igVBnU6J,l^* }+ bOE%)ҖyH3S _ 9B[>3|܆O!fιmǮ~5i7fD߶љ'I."^Y8m($kZBGz`9YS#KubGkDH zDfFˁvqhحD[@i[Q3BiȎams6 n .UZMG) zX237%>U8~(8}GrB&BW!H|p.^熷:4T.$K,ݞ>G-E.G:͵mtXYW8ce1f+F:٧Aܷ{dc&Npu ANEd6cyfJD[*4w]@_~+MM^ BPLZg97%0o5gI;#[ju9|'短3bxxl套6Xٿ軝$ ƣ;SP n8r endstream endobj 117 0 obj << /Length1 1495 /Length2 7116 /Length3 0 /Length 8115 /Filter /FlateDecode >> stream xڍwTk۵"-C Cw33 !]4 ҍ HHKII ) |9=YkW5Z\2PL@syErz`//7//Yvi&`6H?\aMSG"*n (( xyE D!p(@DPrHg/W_6kvPDDw:@ 馣5^*&nF;xxxpCPHW[IvNmЁ`0(a 3nf]i7G5 pC@a]@#XNwrS_kk3Gl07 !(M>wX>9( Fq(*ss  @~O v/?&@z |6p 3>rc"f CxyyAB im󫼞3| !n~|Pw߈@h f+`{= ?O7"^/ 㓕Ez|\ ^/ yw\_[ ?wE0 OY2w@h`[f7_NWM} E7Gn q;zp#e7Z#oߡ?VY9ܬ F\@~n^?p"Ղ_G(7M/n歂ofWa> " o o7Ip ] ~UP#oxl<C ?Po:v W &BZׄ62 '=:3\St?;ˋiX>["]u0z`-pM3*(6J f}ߠq"=Nre2kSI{mn"|D-2Y-2`&ԑnYzH`wNEz%SJ(`yS"-94 5+( ^=R>Z@B#X=?-7˅lP=h~Dn^w8^╞>4oRQxۋ)r*RbA9(kX-41.Z9x!.W\؝AXZgu7wgUvz5VTim#hcdmk M/(/_NƈPt$ W] WANLiNR{CMw?M| $5~x~O$;KJ;v[?I4b'#&-X3/|0X^ ,O]YzGk+32NO̶J`1Ya2oPΌbh\׼_ ̲ *뉘yLdW!6UԊP\3a|y#6G鄰|xU{ ԻV|jZ2FE9u'ﯯ-Xk^Xn@?P>d7Czѣpm"bR^^VBҙB zBy^TIf2쫗BAԭZ op.{:õJlXEo ,yԖ+cuHiyr㥭8IZ&6yմXj;ՕbkU UVNZE?O9D }N8ϺH(C5{Խ ޡϕ#^U g9j. t<^4YWz;6wWxf~Ý)P-BPj>`ƯШ˨l[(Hs&\]:і e0OYrU^&23?[-#‰ 9/F۽08@v&@\/۰qkSRr4ªEx}cLNXTKw太%j=vg!ZR*D:3SNcXC̦!yhG.~ v_ӛEdٓJQ;^~$IF >c3<& 5S~Alöj ..9ɢUu#2A7#LڐTGOH}P]2NZRxvk*-*nBar}n>Lda@6{m?Qu+mi4WMwRp:'uIn.JPOFkPUnk1]gMQf{B2,1sOFީy'PH}VG鷇9.Xyd>Y7 OK%zqL S6xIdkwzY ڧkjbl*}_Uyu$U od V<3GfL<k'n5}. 46r"X9qH87ڇ0⣯ve%\+9%.2m׋{ָ4H<ͣ–Bx¾SN,FDy3_VɘV e;qbaV|rkG[2V yoZ3GقS.AQ@pDeeDws ; E9g:fdtq-xdFThș4ȹޒ kc=ȇ.jDmLqK5c"S&/Wzdŝ3{)#}Y53nъAmÍRYcjD}6`M&60=@l_+(( l rP]f'5&hYPfe5ba#]T>6]B>;1 |>FM]T6Rf-q_;|Y-vQ"Y:k*2jD}W516 gM!๊`l.l`.:|!X7 J<)>;Kx'ZJtl8.d 7ȵ-#|$x[ՂDO{Ԇ1dӱR|CǺk/?c? 6~+vFٚk[H4xh%(n2PǚDO˂y`WMѡAbe@%ns˛pq9koo"fdjov .#`dG+Xߟ96Q,W)(M{߼RTYaΊ65:zn'ݸËC=1|]8(&1URt t~,\xi5hEFq˙1 #:-F,{=/wb65os<%E/7q%349K@Z Q1d չkCv=XS ѠnBJM_u}M1 e]evuGYc!p5U~*jjWBpw_NjA4 c]?Pc[80XܚM&bބK?c2i-6w ]5`>>}j,iDjy/dO=f@n*A??{BA3'ֲ )C@w7YB <0>xnAEBa̘:"[8iYqnh^ݔepۃwEY УXl[e;~+Q4FOZ(:8g.1 ybs#i1>WA6E;Y~X\-}\ߞw嘠'E8FMc^ vo>3J)8qO,ϯpwr'\|Mnglw}zQ7BdNac4"\tYYx+ӈDo6wÅQ2BQ1a,~%jn)L* U#˜=ǏNT =ZUCv%[R=ɭ3uO[7/HGpClϼhd,jr<goþɹ~NdqyKBJݻCHdqC(VCqX_M>9&TJ~py!Lt:yW LI(ZI訛b|dTk.('D .I;5t?9|gX:tßqsg 76 r+5B"/ StptԹ p07%~;(%W̤.lA B{0e")о$yD9ywKAXCLn_]|.d=+RD؄h`ݹ?] }J(&Q\xWei2ɗg>3EQVi8 *;~; ,dUgeM`WFqH18W;~h:>vk7AјEdyy#h_})\\Jni7tgB$6s%_*%j^a Ш3{(%* DY<̭ endstream endobj 119 0 obj << /Length1 2622 /Length2 18349 /Length3 0 /Length 19862 /Filter /FlateDecode >> stream xڌP | 5 0; Npw  Cp;}ޢ Ozߩ\EQ(egWTWge3!QQ#Qi@v|X;2 #g-@`ccaΑ a 2(2lNHTv s gp~ИXyy>:LlF@pD#k ?.h,ܘlͅhn g * 4Edd@#XLN#.@G8:@MVl Nda#;{#[9 d (K)09;3lM24v7r5Y N %`f?'G/YThW~ G 4 dkj S{f [ PV daaag@w {V%s񲷳i}@f@$/'#W ++d 0l~{f;z,c'}Z{6*RJZPW)&fb08ٹ\\/r[b'?ښxC\pg.hYZFPO3@{?p^Ho= F6 k, E;~_S-Yh rZYg#ښ[[Hhr6{bwk-P }`dea?:ʙX'pV%mMLZ=6N. x89^5=f&[;g鯖rqEq~#no`x̒"no `2;Y7G~#pt]7GW񀣫Fx8o`V5~#p</X96+`K T#y;f9gouy8Yg9A<|̦@pD \\_ aO^~{[\p`? 8UWhXf3!_[/S߾m]l ՙ~'v~?zmqHm~a dGso#Ob 45,`2sl~YVp&_? f3 ;vGk;g'O2k\l翟d; h4ogdYZȍqw\pjW+kɱ >*#`V4qcuGFd븹>%}SHxǢu}$Č"{ޚV͐rT9.{s;=J&KsKwÜZ:S'%ѩw^bIr ^Ņ Y,L}d#?dYxG)}Z{P3v jЛII)+5 A u7UtWZ9?Y;9g$&G|]`^BYc9qb?_&yꂆ_]M5{SC Q'O77넉BYNc&2K؟Ph C5|oEVL+EZN C>>b5}<l:: 1)Y8\'\SyyG@ 7/Hr(Yn[pIo,̚B*2~ _VIu ʗ重ͨ||E.*xS7J_7eTwKwc}*6f_띄^!D;ݖ9~ ^*>>=SF7LǣrGdl ݢffmT& 29b؛yHtzsgf1W[xOL2¬UBdXZÏX"m;%\;+#ɬO ZKb --e-&Rf]Swg٧}#RF=s~%FL{VnWI(H9sT7LzΓCIשߌ3^H(LST>|NǠȹ(R1u_riP-P|1k]#X9v=Qc%w1H5o{r*tDb!PcuR.5Yr"`i-Ou*'V@U8131/ )n30'{Hjn#ϣ)VCڰ&+Iһ#P)Olmsp&81n(QGeC2»<}j6[z{tQTlf~Ba$ S&*As+{QSSH^LmױMwIUD6 J>-̆C>^zNFTJ 1J\(P\\ΏK7# ycn􎰺zdo 3iGQ|crH遽!ʾ|0*-v]R԰+2NS䭁G|؍|;eStDD'q'P[kAA |$nᦅ\7zlLGcfX嗻Cۅ5u1} LkmYXFTy1#:b 7}|L'1y޼w>Flm_9ICwsZ~2\gn[sR90$2xlt{/a\Hjگ̟gM~%amwqGXOk}!NhμD 'wH (Y #>XXXE4OˬيڿE-c5a8ߥ䞀HܮGdWE=w`j֣ ;^UUk 29[8.ұ@8U )h For*;7{Gs:@_5/<-o$j;H(}Ӱ'~-{!UZՓ)@]2(l "uQq'`Kt.h *u̞;MZ*lUTu_>BM^]ҕ[.Bvc[hIj3xʅ;/\ͤ%1i?}NcD*`e%k`nлw"7hFta$oWN5NL?4—AP7A~SBLgL/Z/{/^z\E)IQr}]FƌF~@-G FS]nB)V(Ҥ'J)btO%2M||v1v^h.Vo ū,u$W%Lgߍ ;l?nؕ3W|71ޮ,\T>1WjT;!r1x~^4Mlt^NKQ'yFԔ!b3 4rBaE2ѷf~fđ0wql ~G\0(t7TthXΦNyxJ&iEOҾceGl$eX;N|FZBo葞 9]6H/+eBq!yulaONxaG~:$7Hν.gapFxe װګn(YC5a3{Whm^"&.Ss*k`/ە-;+7ᵀsN%s)IW]r:#<']poX)ҍFw74Md;ַU! [F%rTO^"}眓q xY?bn$^s3)97bÇ:q2aW䡪㣲F37AWwT3ܶQG,nλz̒lB"O/cㆯ3&V*H,5=cD;3A"TK)g0AlLKqq‘ː * 1h.G9\+5euz{kO'eK跌lظds=(H27b5a~d̫?%ȁ'P=&+l8:BCC- K[K!hRFqHd/dT'o8A+"(j? ޕBQ`4paےWIlJ?g: Ƴ] UʶBv3?Q\go(rǍԧE(%ُ[YhSO: LМ]}zD`|17@&"hLo]NE ꨩcPjaΜޚ[4FM\[{$YxyP;є3UQ2x+:n|A arhJhu1RAZaXd{ן/ 5 FJpBFX9DLK]8qꠎZ28fM&* WXI~Ɋ*^  Ꚑ&ۛ~R}Z]eC9ഫALG#nO=W(e<4 4l 7}KSK6k&M^Nlaw+z^3Л7W=nn84zA@Ur$ 2s!d؅K)>7E _"OSA$v0tJQ=\!yYWdǛ_pBȑkgN5Hwpe۰'a:rŪ6I-f.yP.':z;Ӎ;  el4͙\uEyǪ;cQ?A=k~_ͫFD}mxbL\YPULㆮJO/>LmVQ;:~`Y &APv8};89JƼ;@D5e*z[IJ0 I)]\<_cؕB`0`lQ̛A/Gőw> {.|Xݤg,gh~`dW׏2tדox!cIkpo)2SS(='Ks5OZIogvX0o>5iё+g!Ewp'1p -1vbl O_IK#LhŵC=:ùa)tWKFJЍ ˀUkIxZ8nl\@a.X28c^矽KT:ݿտzz\ AI9 Q= LZVz\/2#plr?ea7PE+3m.S\ שґ>99d6v]Yxڊ:[&رF fP gm$j|>nKBhRBfw^"=G2Ӱcۘ5T!ldE^&)bQ5geuZN+q>{JS(?Ω^S{ݺQNB{F;DkGBdƔIV9G@Ս}I&'q/y$P!ܭĵjde]%lu\6iWc KiJMGʡF#3rqS\Ym;ҙHqYQ4\bכn9* rudA+Y1mUy|6,J %\]䢯N7~jA8۬m'L&+ݲOaJ*طGcbx)g?Lc(86~mzJ´J(į?Ĥ֒ A%F?w n`* }f_jSC [6%xdސ-8h*tq13˿f/Ns(}PX`c ߏ9@(}Yƛ7hL\Oe&°[9S{4b3,v`3>YD mtڀsqBd^h'w!A WFzm3cvsL VO@= @ @|#UbQٔѝ:aYvމ'\uvuπI\4õ֪GTX&̚^2⨽R &Eeid#Q%F{_="Y'KkvI9~ΥN;^}#M;laS6S+Fd~ڭoa6QPdxT,C:F eS'RP8#}Lj hւ+2\c(?#ymtixR\G>5o 9ȗF 9PdCL-|!=` D3FaC/ LloHfdz\7zɊ01 ݉E^蹰iWXMDmI;]^ -Yڏ4gJ^'[zj9[U\a0I훵I39!|G+>eq@2?6lE8QI,{G~ r޶mO4Sgr]!R5ǣט:(F7ta/]"[]jF&0)d{g.na;0GfсaSH4ӦQ(5,֜Y4 Y\*/ kEšAj5C$bmyuT.ƒ #Rrbr[/_XSj,f!lVWc7?x\c?rezgq3wz7 jqf6M.ezC<.?oV(_Q1 ɞ BA)?XM zl:Zzϥ":H\E%t|`&q- GIgeVH{FDRep=l'S $Pxkc@=WBxv}R~A"tvu.ȓltH 2H1ipm i#6/|zh)I-Û{spTN$pV1O:ݱH7 \ۛ#Y/I4| ?'.; .=|KWA87|S: a0\|ke>qQ[\ƅW욯Su8QXZpZ fk2%zA|w<64_uA~.yUL\0[R )g=Kԋ0:h(9Hΰ$c xFZ};8"_' GAM̷:/Z24K2xDW,ء60l1{H85Kn)a8ơl,Vв;-m_4NqU%W"h2tub#/Ⱦ0`f]Q#Cv+٥w+2>0t~ՐCz4[8Gkc4 Xbfc/(<jXʟ91aS_ܓ@!&[wv1n =}>BogOkY.4;J9riRI J76Po_ vg"X(jP 4bNQ-aVif@Z /M맹; vRt|f=xR2f,*{w0LR:* }jD'%)A-ZRaT#Stԛ/Ñ n/c&T2勑w0<] ,R[3@O< vLzS9&<]o7S v Եր_aht"S%7Juz 8KZb{ ^jף[W²n3 +\^g2h?N ~ $U?[ԣq+P^qrj@NH~'OBHmQ~2O0{?]N|;K9bF TQg/?6UWۺEjN  J^_tBy7QCpo1ĀߝA@a;K;ͯl.f- /C_413.R?}iBK=L4޺=^x%%U*ZW(swM-@tܒе^Y>K2oΈ߉)kIn+}X rA_Xa.xъ#AYd 01hEή L%3g͙B@=,CѼH,H3P=P?"rL~I5KޖgTjVs!JDh)D,^6&aG E]ʉ^|x۝惹-oC5da<}N&}YZ᪇܆1J ~9kE!U#X:JO} +{]3\Zh([؋ٸA*=VRXo6>|=>1Kx v{e.ɅUTn*)~%4u]#5lc uY>Qb-Mv\W:=K Ae{usk&^Iv$9/Z.'_;?_-kK})h ڄ3< [xV&jX6p#CH1n[O*؝(yKNv'O}V+aV~of܋s*Zaخz~ Z~δwXy+?V*Ӧac#^%&QҵY~v͔@XzİFfmP$A5#hؾV!eCRiƢ6z0!,)a}.?==2_.kɘ!u5JC~cNbKp9cGs6d8O\<6=ɔbaCD!;٦9+_ti፡.metZx猐-譾R,nax.o<M=N,g"5>=^vTԌp7md m9A[I\t{NҖuB,qYa_;"foHO"r9T{Q."%É3 zC%pw,Um-5.uO'|4}L6٬htLZŇ]of Z}ТQ Q92M?}xbT2Cĺӝy2Gh"xo \_Ao'6Ϧ:ky5~ aj  ^2Q~~Jl\L2FDejtw(&Y`S#"?|}T]W˺MD@aSKIP~6t׉Ŧ*3Xlq!? pXe&y=:`:FPy߷.`GɎvjIڟN0~J5,"O) 0Ӣ:[K=Ku:D4\K%d8:l=#w'/Sw ȦO9RGmx#'$j%*Qb@s#(%]N^F'n?qokMZv=u'O]s(&*ERըXUnyWS3 z%m #JvUdLU+e8x"]B>ca;{fm@HM, yEg7$a$Q h3T/)f:=ݤ7zlWzH])W5$ǩ˛S™FFj5&fdAdaZt_qyќj_'ˍNI.Wo/_v]G;`80Z̠y d>46IOW$OX5by3a <99znvB' c,}yZ䜬bxۗm1iA9LNiy.[iX 5%MiN HgFعLGTī1==9K,slno}.k?:U:4J@g&2"D ,q |iTð}Oն+/eA.6쟂)]H6P؈<~5ou:BNjpݣ-<9dv,^#rOn{t%MGU]b1h rCEM7dq' 5ex2kVI1 Hq֑vXk+R|s_ ː4V=+AGA3-=tơhf%xF[% R?z Q=!Ńobƒ8$(yTC|23^xd[)ţztW==U]ΜwI_2Ö߼aDGqd .i|kA;,): G+J:7`g%wV&/O^ʏ4V /{.=#ʷwemw89LC]Gb}hķv)@O\|Y;xڀagp3mKJ6{rYmOK,, o߫[]sC.$cd^bZu_~mѽa3>WI~쓁W,O{՝Ĭ;6RGFA-lleOX(;!u_礶fR晟~R5(q2+qE FuYBM^ez&F-9G|j,8 ~_6%7E$~-tɤ/5ua:v#ۍT(aczM*WAdNY`Ipk,ɷJkƾ^9㎗Σ"Ob_{$'C/cF?c٘]mW~!Y>8$jqFԭv%'*{GH{ihWC\z2d{,:JN~A|S= y [W`]a^eoo҆}vy Q{T{!F3JPLqI\25?}K^6deqtxțޓoPvR3g{f~|;e}VR=8bD?E\\6 ` / :5yA٦%t}w9]Py66)kpPi yKm:"zL2'Я(̿d}̐VD5pZ_[

KY_APQV5/wtq 3=p <~rP<@bU2Lj1KS@2V EN#x5]Ӟ LZ (jFiDGwnpiWQQs34fTüݸ_9O~M2<҂E!W=gId՗t+ mD5P.Qme? 1\'OaY!hnRb3m5+l),^<2[AvR _DoBMng>f厍l7:UkesO}Q6чqr?3;񪨋Iz(̜щ>Vq켻rZ$FP;To#hi BR&RIXt $}HF eݟY'ma =SLB^ͣk)'xk>gZFt[/"M%sxA` ]lBJIb/Q"|u.Ȫ %5NWt@̤UD8juj%c} IJD[GQ`ASu[]7׭~\hnvWQ%˿ӡPL!dPqhiVJ>9;G#sk"&@E.j-Q̪}-ӂ3c?Ǘ[\%K!Λ+s)T 9S$F::mpɡ#N[pndֈ,ȨS+-q ޸uVv!`Qڱv4v]t7E/: J?](1Pt`rSEzޯI^ZBYN5)׽v^DONKL*\ߖI{HDgo,h`=bW$lZ9% !Ygֲot֣G_w^~rs$4:LY} NfHE$M xbM-ԴZ^yu%bkWµ:sKAt֖CK 츦\֮xރ@e6c&I+-?A-.s8-Rdx }AM4t(gAQNrb@_tD<*t?7>X+mCsyr)I}ֱ>6f@/4\e@$Cj/. FCB Oު͐2ݥ|SfY|*7r:#0hkhO_nVMcGhEnts f@v6ET$H3ACII4~@V9F&=.?eo:o AȔcYc[2j {.E;,c96&r{2_Ŝ =u[! IJ rTt|st)SB~$TJ?x\w3w@Bz5tKWKB`hU4O sHP5~V 3ѕh7Jkn +IYK/E<]Ц L*%!}gH D /hؘޅ`*й(kV#L&FBW 18 D%^ JpPp~6_< { oR/}͈g)V%6T堘UhasMģzzȺc9| qg)\>>{fTzICFzqwcdp^{}iBzŗWi4u1"> _zJ\_.nZ pqtWlOȆdQkK>xg: *rY56B;Ih6ɧNy)e'C<xPƣb"5a{Wj"穬ѦI((rwMb6 25< {`q_e0$;MͻQ0>:x6}ó~B׽XdS/;2{+"aX>$bV;٫6M7i}e٣4gU6l1?$jjJ?VD!}U[&ܙ!~us4DϬkWQLq~BO`cyt;1q;obIȰim\K AсCqtDFXѨ9fS=%3@K$NCEATtFߊg$0-n~xそK-KA kqN=GUˉښ}d9UrƓkqJasl3">K>?g`$lč]&Z#U xEɇw%Ѝ˲Ƶbmgc)GPwܨY@HK!&)8ks!N$osһ12a/kǩ7@D<9[{ g 6ASʖ|OKh.c`r@ v!}6߄V0>YT}.qVLpɣF=b 6g ~1UZ]R Uj"B/[d8qyݟFDٽ5g1Bk g>Zպ9(j7H.8B.ZD5Xc X53p@ 1dՖ nF=xrԦ]{j뎟ibltG#5C( ;-cf|~%r^AY:FBXjD)p tQa~OeADp4i#%d~Wœ`,v2iBm 8uO9:$!CH 2Y aI+}_9:S "z1cZjj)NqidC2}>"޻n R4O(`g8v{j/H)Y7Di.؛R")>YnwX[M;{q\1F1S6 iK|kH>(1(Goo@}>"H/]}f>Z3Cs@8$Kpo5RIVHwތio{NYY-}l7kuP\ 7Hju14i#Ѯp0(_LAb43K]ᆱ>&iJϐm;b#\|>g o'B 0MqA-ASF((w%ٰS˸R 82Q endstream endobj 121 0 obj << /Length1 1480 /Length2 2043 /Length3 0 /Length 2982 /Filter /FlateDecode >> stream xڍT 8T}oS'ٷ7X"kRnƝ13%Ո6VT|x-E/E Yl%Jh.}}<ϝ{s9$aJczCd& ՕH&@ a\a.c 6f"?!,(\hDm 5Qǀk@ ?d %{e"dda_.g`>L!6L юT 00 ȗe<Gl?Tg  P9j8p9.L:GaCu0`*pД@|}D{B02LR, #>3 @qꀂfS(0fNdS'@AP0q` GL-9B,`6DE=?7\?C-:34h,Vl,0 q6@P!,h6HqXL4p0%\v sW C$LoF0?nFφN~D@}D7D!?#ې]7Q43c0 " ijmVבϝ Bgoл49Ѩ_;la  hK`6mL`ƱÌ9́\TLTm79C48 *a|HC!#̥n x{4o1TrT?A5PE2i3#i M "ik0"Q<C\4̌T_)3YH <'S?L"L"32YSlT˳;g? Q1O[T=W#>4^5DFc\)hrL3hiţޣ+H_ܥ~:5zzLݣi.Ի|nM#5R\7iVVx~+#܉|G /j8~tj=="okV`覍"gԤa:rdJeI-K}!ܕ"k7$֑ټ 4+mz:1^zs;^֒\tPKn!<4<3nE`?lR`#het>37m1`^NE&f8/7>N7>/}ktuEIUaQO1T5G7Tv]~}-=@Y}"-"~Zk͓QF1 ki -ld1/8S7t/-o)nf\fby;yW? 8ce恹gՓt5|odᤖ/`踐{=I? | 5:~ T^h}@z}~n[d`O؝_ 2u$.iU>y͗dygyUIlŷ`7XzzK"UI*Ve{L+$L-+wi_**ٲC##)mdcճ)ӀQ7Ku}mA)5]z/w;+x]W`n1r+n]tVNsJ^ќ޲3nE:{ZHw9C胻)Vb:6%i-xݪůȽqzhOLEc2N{XW~nR&|WakF7׈[Ԛ.fI^pKgf]{t/OTԇglH݁q4jTΓ ]!%"uɫނG2-O%#V닾XDBS꒜Z: uNH|:f\FA0C|[g  1k Pޭ,A]%sWz#uqRըe:LxTSipXEgƃ* XѪbHIݹcߪ6B[Y]l&+7BYW]LG׷ٸOQwQVJ6PnRefr(TbڄhG+TY^*ҩo-Q"@ZwqUO&{5cbr_xC~$n4<ŝxyzNo%ҕ==/Oݏ1Lt7@ jKNLrhئY9|xߺC҃\m#ZPɺ/7bz ֖)d^Z}ocB?+[6ϭVr^Xs]@>-iרWR~^jw=z9jq65N +T B)ӊ劎㊟gZs O|dpU:&?FUzZsyyZUuJS9l^r_{26}1_%/s&Hw>/zTەtM1B4VЋ=|tO L>|uB=djb蠇`N}n\Od-DrtV?n,DT}ƞ(߸O&|5IN*ܕBLudByc M=x]wdeK԰CH]aq/E (X[b$tI3†:&,I6{>̈ۚyl'RƈKa r-?~/뾼fmZn5[׵mEk>Q O5w H7\hZxuH}٢V'~SYh_;J=QOc/0v+S=oG endstream endobj 123 0 obj << /Length1 1676 /Length2 4287 /Length3 0 /Length 5319 /Filter /FlateDecode >> stream xڍt 8]ܶR_5b_#[B11Ø11} *Q,wYC-V꩞}}};"' %q(m$(&&"% XDDL$ WE E qXſ 4(Du8,p@"TN`?8" I"h< hgubHq kQ4 $;uE$h_b.$"%p'JΪ Mr("@F9wOe," 3ns"y!((,uD‱>pY:* M{kF 8wupI[_D!q!2A8P vՍU/yD$A"Jј]])ka5p(,Ȳ?M47ͺaq^X_ ut6Ţ(]_%˟3@ y)( KoKBwT>8*vBQX|2 'XP$(g4;5rO@Sk{Pϖj/G|~L͵w9$`R0@FZUP|M[?AbpOԣG)~ 8.NFbo _Zeߌ i{b0{iF1޿ F$QG KQ?toV:Dm4h&!]%P1h,GD4O:mH7kB^^ EEw&# 7 j. #k,ő-U/#^ !9*2!`$ l)PYd0/Hq R[AB[T 7zP7,D^Ŀ,;K݆RSIz3,}8Rka`|u~/voǴx_5׾JESz;cd畛njb"r'QN'lBљkMG_MQ>UlhѬ$+m{d+m`zrųs=o_ [a2# ٠}ω=%Oޝӻ hlVxĆfKv D|؏_5Q*q|-RJT .ޓ23m0JOWB7aL7p<4 { KMiL'6&O]͙h~99RRD b/ ʖytiZ~hWN#G[M66;I%'L2UbWfVtC1bLN 3+T%n qɖ 25m qT F&ϳ˸ :0{g/l`@@'AtxYc{OX>:kFD_Nj~!=3M`{2ܫpDQ7^Zfk\tfzu | l+5"3^|(]lHa{~8ȵ,M@l]⾩MQ/Bgu׫KI̭FM~d]H3 H?m8OSI1o˽nW`HuDVЃwntZ8(uv{oDQP>%6Š> $!'_GW>;6Lsݣo Zc~\M^h\ 23\5` ZW+ײg0lyih`ЍNg&}b%R9(\&MRϡ߁p3gs=txl5U>ΞK2dbd/^_]sqcNpaWRVA+߶t/y݆^MyÂjqm1 4c 9lceN ۿ{7Nᚘ#bJr6ÂJ!%'qT~WWy7k0UAG#~#o1~1K|9/2}z r҅- Ta+Zn1{g8U\T U +{_u+ 2bPuQ2બpC?!lrki=obGtߗ ٳ<8YRsڻm/_YDk)x<$GWV+<(v|։R0x(y`I>M [0S ܲ]}rwT!OG8YUS}4ڳjsMwwhK7RR>MhvիlFJR8Y7 ,NSpp:c}nZH~ :[c]㒀̖>hgWcO=zxC _|.OGFV[[_>Dڐ~'Zx;n}a*enc°rՏ&+P,C-hkSJEbVfj=by 9;E5ګa)VKfW2 mIbrJ+D[$B%rڢzc4UNڞezTtL3c[SܜB6q&}1efw\e) ΁pXQ/uh&YN5vPC@8Z)XkSe>kuݽ!Fjrt[.#U;ʠvyN%mաS#ǂ/w+1xVذT]$@K Մí6aOî-54]&):orgМMDQA^ԢbS( vOj0s~̦`=1ĸo$9"y>wyv̼S)Ksn4vw*=b`]mj6<fXaKp`k KEg [ Ż< kZ>a5fJb~&\4oDj;~[b=&bEc,W\G9wjˡ|>$XR<İסJiWMjr3 v/oue:Z?WLMsRWIkpqap9Y=z+{\o`,#qGogְ>Ɖ{q}Z76n9[𻬳no]Mw&WbF'_tW3d> stream xڍT|6L$EIh)nk؈эttJ7Hw7H)}?}9ۮ__3ҩirHX 8$ R@ Q A!NP\)Gy'#Tp3 < ?Ga4jT*"'jeK_s owjUHk]Fs0 0B≨5i/ sD8Z=caB@ bE ŌԲ:%DX"]5<G]r2=_{O;v`;n ʜH7$; e9!.`( lvgr0PVB#7='sG=҉ EW.-vv8 >i#\Mp{ ,p _$,^¡MDeV$=B7sk_!w =@;o%vo?@ 9hFC,ww @w zY 0ϗKA[[I^/IJ"܀@^^ ?P@)?KпKOn up{)}0,Πdɿo}q?o[3 [Aa-3(Tw_S_:V ; ?m:B jPugwaP8D 9A]kt7*1wF9 `wnxwiq@.N8yc D8~ T@%K !)?#`w#U/,@.? ?[C~'B@.!])]xW!?]n;Yappװ̝t7qg"6U͗T[#b[:I,-,iDK2OΞ/z5`4Ʃ7x}7j̍~ۓ}ġ|KM1)Zɥk[uOP̖vҽE/# ҧ鱐8'gUaxG_㉺X.qj`'F??4)9Alֳ0?bUl6‰290>zG!7so6"X TdavΈ;UP4^e!rUN  t+/tP U<Iﵺ LK,V‘)|o$AD@EzNt]"mWml׾ UR}Y^YetLQ rh_ּ1+Z]spۂe»IFO5MyŻ}|OeE%Q~X:o1{Ggkz2Ec|Ҍl:iܪ9ʭpR[zJW -iļ{Z6 sN#+ցL^(F>󦣢a 8+\׎A˗F~q’g'[ >bS195_FC_D%>KkPQ^Ɋa<⾩;`BXRD,g['Β΄ynki)~ժVM}F{,L՗!37+1+[r oq~Ha S/UW; ({Ɉ4!?L0ahY݀0gecrCQq1R} 3O*g8l$"Ev7E]hOJ7Fl}ep>TQsUe_3tɹ\nF.yXLm!˺z}Mgʸ;#͠,pK(EN;!Ɂ{9[Z-aMAchci\fCg+9Ln|2N1%O=^[z=rYwn^Eߦz)ӟyZzyqy/p<yV`KhR-g~nPҲG& 2;`P.xDvalۃ UXTk0Rd;(cBp*{`~y{@FBkUX?yZ5 }瓕ߴ 4NJpR,+/cND=46?|፼^rҔ?ppL9ֲ͆$mݭww_+{!qy臈XLb/Ke\([KRtZo,4{_2VVny1JAqYih̎@@^ލwnIƀ6B<8:?ɒn[\%x+!xCzb&.v5f uI,*8RۍPnIISiG=-NǑ$JƇ}NW% HPID'}Ԑyd_u؟(fO~hAEl9Fzn_B7KC']p,C7{g 8\qGi`K2(\݂ڝLb(݇Qx6rs"G=+`ww!mL. DqqY?"#$ PxZU߮%72x2(w\`Mz[jKhNf=E)Vf=Po `GhV~%z m_n, j2MZp5=.r#B1Lvz'v$ K@цn[vN*t:I#ʸ^k/X]b-Jf esEB8^$ }:FIYb"j/kPZtw4߉?ށ.kZlW6W|4+IAH(`Θ3ݥ幛}[H8Z;BvϕMCUY |W} ծ$B*9tGR&7N\~!;VCBpɑJ%0b L1?7v@E҃YyL M].4$QV- ڬK 3oYf/o5=yĎOcJ0w!>}̃7u1qL?.*$W†W< bgcLQۆզ"2Nye^ZTiJVpT"}qrIzUyyac8Ըg]OP +{^16 qF׊6Aª &Ͽ){?rG I>7bz6tp7{&n/oIn\KrkL0ℚ̀E]n'meއ~4ͩ" 3fz@2oK Lr1U|72aNT8xhK\O>z깺 + ˸\1dzL°19C28J_翹d^h0ڔI/:S(ÙVqm)|SB "IX `+6e<,qͣu=UU#ʑ.Gi7t[M$ ¦IG,%_*5lv${i#=@J݌.p+23c>saA WQ?A2s4 Sű |EJX ,ak#̢HL;iE2e/FA> t_~O+˪żY^ ݤz!VsfREF#^3VO=b֌C6ag܈3QF÷S7/֤1S;:1i0r :Vά˾R7P|z qP+oj+6yd5'Gd݈^jZ7kW)m͂)k~fS]whT90S4~A'K۝5 Ï_Su<@I|`6[ڒ_yta$|R!w]Gd[էuNT6( MIfj6{z5(?|~b/:a ʇ@h ˪]+R 6~@NbCT3_Q̘|$R30YCyո)a$œ6SݛikL܉i>|E F~$E$<{œXfo/ANW̾ 8i`fR(WNO.Ol,d+"N!EXwE5*-.Wrν3ۄ{ b rM Urh}Z_:eX:-*(ĈUK Svظjv!I;Sh.]LC=YI+ۄ_ A5OU̷.1vke`JX D\w7$tKÏ_`~ m)s`~]<jD1&eY-󲆐w/{*\6Lq쭲[[fڹL!q`PS.#j(ކȗܯ8XC_&cVS5mt$e`O6?|>yR'vJhW !M' zGR ̪xtм%e sH1I޶˛nHIM4RfJrP>G|[LHm""v[P]fW0X\}v?/'D2h*l!w(8g\2HzuulpDz0A hf:4#d:d6Xntbx?]BY)Qu&ZzSpFL29Gk`Y&q\<ͧ7tE]FUU7M%۱3(,\wu`a>d\5 E S<'/$IuA9xkH|zKuﲲ:kT%UzUn9s6X,}ɛoG\uοwj=lM}Y{~we1*i +2MxM8u,*7IhҗZ(rOh-s SeA^ْ:B<ZYv)4~xV,2{mYq~&9ˬ9Jɖ\|#BW.QKcDc "[9X$v.m~u.FjR؇Lضn"ٶ~Knuyƕ#c/NJQU.63՘=̤>3%Λ:l|oa۷Eٶϕ=)7aTٵDk./7ŲʬwS qxeiJ'\I75De8VoEA.1u4:Jb1F`!"vmQR׊_Un;},$k7縺Ļg[@;j aTG''A`δ.Xm1$rc 8~]K913"0vs cf.{kA TTZ؇d@<.N% {4 фG_Lե$Fƨg/ o&6qcOi ԛv:J $Y zG?nqlN=F ]/80ҫu5:B1Jn+VD<*KM;K$O3#tlû|k] qbO]u-K-Xz?<:+=4 ^PRy `>z,ZeY7l33tm#Ok]P4ɠ;9q+l2W0|-&;p`j(h,^QX:t`Ca9wDU*[^ژ0]uDHCn/ڔ&>%^{dE:rU&MSYNCe61K"@9c%fI(vզ+2$%gY{:9e@FLGA ݅qVQ2jT yCQ'?ی6= UJ|cŃ9"mh&WnSgo{̙x_C^;.m2 U@0%;眠2ɖf<b$:@b7g(CB[hvc 3 °3MڬM=yJ4lk* K8N`RLCRX&ՑgcP }c$IbQ>k bEjZwϾ4-%DƁ77zz_r{nZPjmtsPpRPA!|儗׋F7얘͛&(0Z`U/'p^|aU# _2Z|ؘ)eMV[sZI-/#}CoHߣБu߰fr`1Z0!~pUet1iK Ii$eR zFD:#y"?>|W Z5E̯R|й(ʧTqaL]vnZTnzI݃M1ƂLr̪z4~v {eHE *OEM`ai$hFZ1q}đevd&OH"CHzVTcMc%d'Mr$2,\ ֗Zg,aSf3K\uzc^Djqja˘Dp1 aKj!kr{~Z[6nRQz/>oc=7^%<>ȔFؿ2Ft`ؠ(!!;xǽp>'N5d ͳ?!˒MjދvSK ǁ߻ տ즅xZe՛|Yf+򒣍+5*_z#pZV,<^?[?frWye (wG *|+%.̌ϥ"l |4zMąђI|FWf]28捛N82꫗Yù=bTWEmZ?Ph'W|`.o:@uu4kZ} R%vH;ʯ!!cb qgz:47oEoVrУGY+v;O9`P4^ʾ|);wpT-(Wnimg֮c1쟯m yba7˹M_CȀH@f3+vgs< qPR~⦠c#f6ï%#- !;gZsL%<aO{=!j8 O/ۄ"OGר # qM8B= MW4yIz֛7|wXERxEDo>)vg\sAlCqU?>Yܘ(܊q%%M>QM1;2N\Ga=^97y(@,%/PJ [/ 1EРr?1Qz%uȯ cx@XfQ}uu;W%Ibf J>3hx)*nq-GQIͭQV&[xuG1-IϠezOzF$Q$m= .^2mֺŮ1|G&jF*Gdg}eGs~\|2Q{S30 `$x켬0m!,͞'n@Mr38u,UZ4-9nK[ej$uCb1MeԷ0̓M&,?ՃRQmé㢫-,a ON,uW~޻DZUermVM;S+9e)Y{Æ e 1h udA X)s5QG"_ùosIV&u.+$//Ƞu٬ţp vB4&+oњWAxK7iI,6\ ۈv 6׆?>%,a~nuК".Tʿl_:|8aS_bC6y[޴7(bh[Xe/b8C,lsEsQ<ZYoR?DvsCɰxC2 OuSsUPS->K[Sge.y~GZy4@^n)߳i|^~ %aR vzF4ry#n/k+M[߼ :##B H=SK.:^<@iwi e֢>Jh29N6FIa+|JVvd%(l&} endstream endobj 127 0 obj << /Length1 721 /Length2 4672 /Length3 0 /Length 5264 /Filter /FlateDecode >> stream xmrg4ju :ѣ D%.E13 3ѣN"D'щ5DF^7]Zz>쳟˥A!0HDT`n `P<V2`pb 2^ `@D!c ȹ*➋`+\7"=`tBTʹ @F`N6NH@ CqA- p'0h8oM8?Ю,Z-A t4x5â>_//u'!p$ A!dM m<?wt-w p f?wrCQ t1p 0YP_z9 $N醀#VB- ]O?ڏcN;z?<50 ⯽bP? \""X7Oa#i|žc4׻9$ #d |r o Y {igKX /(lok} (V{"B-XOΞuZjuӘ'OM{$ަ,}'OίmE3;1|KyzI!TB3`eda0$3;6/3?=KqrytnEGu2rHtn%MbԈpsڧ BJ ;`e`FX(8WD"Q/]*\ұaRƨoV@~CM…bԙe3'3'>]}TJT!{QyŦr؞{ } 2%.Evpz#J, Jc9u}-*;\pf4ѫ&wϯ,3o;!@ LGl** 7$WWpYQ5Ϛ5# o9-ͰEq?sHf =R=]q'b."_{88  8ixxs=e26R>-MԜy$l$Hr*ReK\w:(_``M:ǦBԲmhR@NP >ѝU%' 13atLjgt4O ")<u@VoYA38IG 4_?)o~[u.ᅬpLw$,ttQ[ \6Qb})Ŏ72K@w>T8~5,N乁c-Tlv#$I2<-fJLZ摳lru^Pd<=.m1MMf+km(=[3/71,(m}!\.·ڔe=D{ωM^ E2 !w/3+H6= M4A'Z,Dƞi*s\F. ONޜՍ 6 ۹,W!#%Xfo߷90 )!Us*@>i}ޟ|Gv-z C-d9Du1N,tA po%ǞMݩvIeʾ&Ĵ6flVk;;v^-YlM.#&l^D3 KYOhlu9ZM:IQtf\jwwŶLaG|-;+qm@٧ N4 8$ZTcg3-KVn*?CmY;S^cyס8'"R\R.E(/^,j&Ny[뙧}x0Q;>vdJKo7f>!ʏs5hr\TesnX͈S)lY,W%!%?b:I9;D>b60*/꘤p&8y\/+5D 8ǒܚsϩRXKIHdݢxN m& V}ih6{͎Q z|yń'<3reh;Xy3E ="A`.jbZ_+2f%vI^ف7Ҥz3q|Po_-g畈 eWGߚ&PJ/$/32pDqDwu&:`O#4) =lp7X\~\m+r-]hQ"eG>xTh "#Ud5i\*!' xAE@}oU4gnş5Y,tl:/IZo8io'"v){gdXߟ;ٺE+u7{</&Uiѝ*v|0l (kN1S#k>w?{Y9Ay|'?8*Yf dW(jP ]~:e!=0iټ౱]PEf-|ѝ6%~R)'ryhz`v,z5bphѵ1[$1ʪ{Jb~Կ s;_<9|9t*ʝX|Jy~>M۩^L(ݡ ֣KHڪzԴDjt³ޘy&m=t9+r[lS3΄QDgy+3f^x_hiޠdd357hm Oڻ;=F!}7;\+9n"jqK5T灁?"(l ,A]Dn,,fhaP)Feɻ3o52i@{;H8dg%lo VUÜ{#gZ#K 2f}{UZIݴzEW1M;7I^_w󱛍^1cŐ=!m endstream endobj 129 0 obj << /Length1 725 /Length2 15948 /Length3 0 /Length 16495 /Filter /FlateDecode >> stream xmctn6vضm۶gNVl۶mZ콿~=zUWWWw9&=#7@E\UHCF&lfj`/jj 039̌L0dG/g+ KW տu[#S+;+g%]ōٍN*ffWK3@DAQKJ^@)!07s6(ZdL]̨4&VBofή3wvȉ +ȫDTEFYۻpmfh#d?X=#01LL\fV0 MMrfxٙ(E\͜rfql濮FvV^C2-?*+q+O3SE+vuv3Z?s#gfjffpϬ:z 1(ȫ+i O,fo`jeoPqO#gQ۬hd /f\<:J=73#' d_pMܜ]M?d2343Y_q0 Nm -+1TYa^A458¹Wß 4ۻܕo&63;& [y-&~W׿SǶ3sMXRb9v*C`6,Gډ_[|ң@3;F )6x_ wCm`YPx_e-8%s-J^;$tŲ!r0Y~ë p )SɫZq77K:C# F .{=jϝ!*)=9B_nu2`A\gvLX9 uTl47/i(i[t"\9;#!E>#}@ٌA4Wg A2ĘKFS젷ПUsU02 _5d xϳ${zf6yi^5U^A S!}w)!h %SF;rB90.3=ltf_<9Ka(:y,op#E}r#丂Y |/xISؙAXgbER^9 s-'p'w٫Y5(ӕ|3uVARb$!.D 1@0]I2 g#^pTNYh߽Y~tl2 W*TXQj*zl}t-f:nVMoPX"*Z_n[7*JSkU{uFs'Ldjig&kh֎ wA3tݽJnKn9筼6[o/[x ]V wAeDH~3 }Mg⺈w;k}b21%:woQPK"F\H1^g pHUcf Uovey1-غ aWڠxCL|JRzV>= ;JHA[;`ك;֣'-A!W^ aehīO1]eV O \ =V' }]^Qc(%OOznu<ĜQ؋TIʪ+eA%8d$ d>#gشgAnK}W;2(G5}3.}ysA4Ξ=pdZaQe͆փ$NLjJ)?ɅLo/IKcR!q1hHSEzsu3Mp[HR9"Wq;ED-ˉA0Qkrl(RDRT2;]b׷}7C輀썩$ s4|ɏE]Txp8TQ*}XWױZs۫ozMZǛst!G{~V7N]j[vjxa{L뽱dKc݉Z]`a2&+Wk Mv^a&nhkS/S#7R-nKv՝fŧϴLBCr=m8p cM7=۩ej H 6y'{H@$_MWӦ{_^gf2  B*|Xv-@!G9L5fI";1uCD(T>'p}ua$cc }bu~땺W"tFB@ ]ӶVc+U?0|7$:NW6U 0Oτ: T|w^)3)2ʿLXUܞ~c]'JP2^Yn9g%:N_1P8-vЍ'~{"瘲dzr~a7kTU(jߎyw\t}ƠD񪉸abR3|g$#A^!M{/pU#_§Em? 0i HGam4pqh@!#Eb. .lXp(#\u8"*57ҕ:S):e%eOÆfpgq| gy%CHNmݺm^˭Ƽ]IߕXx0J*_s~.%#]VBoKd-OSmf=mu\> :b( Xs'Jcr-t#wN%TAx @"t-'3<0zCVm*O_> 3Y%rqC{nf \a /E #!8&ѩE(g{`^ay 0"0ۯE&ymC#@;ܝ`(F[.gEq`Sn\^A=.#x腾*/\{9ؾ %:;vv_=}~ZA\7x- ؈#u))I*hof1ZOe43R"=)g*̱$D'Džs3c11զЂ$`LV@L$ 芋R? 9:X<O@WrqAcZդa,̮17yBt1f gtǵ'&"e mڮ 2y ]E&͊bc:xjt:F!3.\:8nty}\y?Z~*gG:{2 HBHU,-0I6v!rQ\WW0qaXx-ؕF(ngm( 9FbGzG YЬ /uV_l!Iar#?Ol\7"2 xϝdwP"/B`pϷ(-jК)j(rgydLlJ^l% ie9,b EU]#'s @{ܜ辧Mv%Rq A>tZ Zjj7?ݨMy+mI\3z'F`rACDiV-!vy}]!h^ UI!Qʉ`Nf?_ E'B_wZzl-ZB _O#R7|pX5J)(P~$hlOq֗߉UI};uA*8qp)/ sna5;]<7@ "{RL+.HlQn5q-&\<2htԹ2z.Ab·uTA??eSC[@0C6T3n&, bTU_!|Jy(9ExףV6e 4'>qR̭n$𨪼S4?ή0LzLQb]{& }qe&U2 ,N2J!F 7؎zotwq1hu{*nղ|Tk Z {hۜխ#4! 42>9N}p$X-o`kkc@&Ds6j,z}tƟ"*BflUbQktw3|$͹GŪ@U#dƀ6oZ9CPQΏG:j% (0knb>f7`*%FXō("یGJ84P~ e9Ϛf*NMW(s &Q ;H膿 *כ>p*,}KUhHJ{寜BZ=p"꫚(0#%)XI|C%[ݖ@45QJR$AU%>"|{e-A; <]brYZpl0C#bѨ cH-'{ -A e%WN" ih5ј*=(VA8;x_jD]|K~= H"ܞqV_Q6O0!ve~Rmܪ}:;3$qJ;*MR*N±):O'ah9 Zsپ|.nЬ#]Y=J{V_DeQ|x\/sr$7]5NFp(mLB7S.4f`=F|D, *l>ELa rC 1",/hR;Mr B4*Y9r)`ߍ!fD@MvHw>X=:rϿKܻ}&Ր;[&~Fiz)v$5BQac!788\.\kV*tXҷp,qX79bVziO('UU Bڎ ,keX*ck4 3ǫE6-3I"#~ϕ&y"`6(eGP{:kiEc [PP,=cVCM=jph6izoOxSvAb7`KidTi[EԛUSx^~Pk*<g>tT͗*υ "`ew9i-(pL~w5JY)l͟-Pe]R,ŝ%FL&F4=' qD?q )3q뽽ob5y%+ Ҳw_ᶟTl/nH9/@JD|#HCp-b3oDǗN3l$̾.yFH:D£Dv"]O@?OSyqd^D?E$d0P?|||Ղނ@7$" WPHԠ=_>qĦ6! _ˋ&s7'VD9!Kf+>U.Ө I.<ՙ@"g}#HkhmA,r3ϛUUUi>ěGgrz鼥 #,dx{kHn*xȧ1zWI=C0{_wmsHs2ء#Β-cwF5K/eI<*~߁_q*.),+w |(-b{2Q%xLaA,;reJ-JusȫxK8RdWS퍙} ^ `ŰFK$s,%ǔ"C%S;5 `AGE"q\UXx=6~^g9o_sd XW Tߴ:gسFlp9ҕyO}4 s~L;Nn Q-zquk#esmFҊ͔?U7drў"kwc)%;Ñw{=LnjҖIxL {޳lیUv`GH$5wtN$ J8f> Tԉ̿sb~^v7V߱Qb}+H1PǤYb1<:,4^4/#o@ъu524Qx13bˉN&%5%=Q$f5d" {^ lBc8I)ni+Hf= ZLtjl֪ĕ;Q P|LȰP~a90y3M8e U>F@?OYxm$G#̲F;i_3@8@HOeC%Pɕv]Y[}`5(qz;^yWmY1`ڨeO9;za0{VS͞V% WkC aBMݼ-VWҋClUZIY$(M IAbrm휹V1l_aAjKC]P \zb=& <&T WōG*nG]("50]QL%W䲋us 8 X #mdǢxgt%WR +t4 0:JV!sjZ,Tv> "[`X 2.6u0V~)ظdp.nRn"X%\A"8]e|X -~O'78vIQkN\G,^wbnQd" ER>-d 8҅AzyXnkfP3AP('NalҮ%Bj_5 F/"I;!(-'U # 懏rΑ8 ?5X:\tL} "o,CVo=Ymc4-r ƕx\oN;.H R@\/ |sN[fv)GF=9G׏~4KMpv]?m?B <SQonW/ʮy. 'Vv"3R0” g/1| ,MK<.j5_(7;=ANʯ`„q2Tu&"RMX1 >rY[P~rnsF'gB]! $i*21Hy .oJyܕ|}vhvax;yY4Ĥ tx #ݟu3籍Z_FgI}] BTllC1 KK߅@HTY>ٴ̺{n#IKɡ(AMդcqɾ)D]Owfen;E~clp 5 G}V7_%%,x%Op}zP+)5`7·9{5Q H2p!Qh߭1N9>^cDL>ezgIrNpՇ;p،V ̬Y}`C|vuES`qc:~X I7Յ79QH:ס\B/i/V&>DuɬLujŒiMwcEJ7=~A=q'QP*G-_ {5Iz]O+N>ӋKN3 %~0qzPieA G>3/3y]M' ˓pEd8щHT5N~ Av7 Z i '6yTt'T f2=4ynS׶61nwGu%m.\SHP K7Zkږ? EpW @x@W#3? d(bAg^r> (6ohpp.@=Sw>h@-Zw* G-:Ƚf3E^@:=ٹCt %A[vr1b wb /Ζj-p |=,aT\.qXĉ.s>mqN;z)k+㊸aT?/۾3tSl3VES46o<`Z*=Acppw=hWciJc8]֨}EM-ȋD詝%فoW-zm"<$7Ԑ @`"Li -3qVnn_DFc )QƲ3$ji@@IP['RފvtBJ.)U1diGRԛIךl[78uw%ϲjK2 pysv@$G™26dpMH*7Ҿ-de QL2;zxTsMv5tVE# KFmAI+hwN/0@M<'-cXH;@҄7J  9ʗG/{*[ӪR@A j(5,!R0H9]c5٪9"$;}O(:a"N)F;.YږKȟ94}NEb錖1 sOվVsУ=4g܆l"`E$1D1}tĔ*MÐr"&vޛq:v{$ ʓт!]c򙬷᭱ݍCs>嫦ByeDli>-eTa;F;far2лcS(ceX~ubO}tr\JE]Æ\KFiK-?R;Jk\fkyWsHFʧrg,3l0B}$(\ޒؿE4 Mg[7aҵ¹/IɁ1iWK fQ7"oF !B)u4f[nۃYK2@(ÿ!+7gn=VZ :kM9쭿bpbiR5Wܓ5-4gʿ"Y $3l ~Ja`m +^%+"G~G=e}QAR&2$Td+3mB&223 ojvς /});;aŧwKaD\mMU|.hըݾ,ߥ4~[_P)+ӻ ed 6.HY sj^?FuC"i?A/;&ăd&L|Nmm3\! J5{ KK6V3Y?sު{hRkG_A V0iC/mCZDA3CČALx"tj\[eJSX мƇӏ$+WU+=׳됰6%;U'R⍜ 4dmj#$i.}ʦz*6̋cA u=ZuNw9?ȣfWW!&NLOlh5FUn9c I'Q W^o!#n@c?%/4}ӈQ6]ݠm&M)Kokq ~#$DfR;ҩ]_ҪۿV]zv@=ǹ19{9fZ"qxlV% 6& dG3@bLzD vÊBmĚL8qՅzΦggh4O͗ [.*(KϢ̃$l~%n"15ܨBKsb_+g-}m\:$m[f݉'Rz&]hDF5T|s<~™`$(ܔ)3ix4 RBPl^ <D uMKEٌ9* U2Ē5KM`0 HlpR((pujh+v9FwGkz%s%}?}A$w^!:3Zj\@{Ed*UKl`vHf4$ PҚ#>a4+ Bd1rqA9_^qZn,њ~\*1oNHg3u <4_0*gL5NpH86]|Aou}Ai2GE"_a?NW֨ dMLxVO'(G5Skz㽷Y3}l8~x1e.v%z:c^|;PZܧdΜF #۵"~VGحnAŌ&9cm 5P&eHxʨ>-%Ps_ɒ5S)p3%A_zꌰ_ UIl7]@5~D p 9$R`7CVDnSC^=ܧ?bk&uY7 3,.cwHCq<`iq,* ['^P @o fx%r˧gmU8 0((˸R\c$ Gp3_p˧ (B`>ч |Ug{Hv$Y j =&M|ˮ5J8]eh1\}hN Zu_v|wغ)4 |8Z0Rʪ 5\B;U7_fi !R܋~=)[Qde]g v$ߨJ-ݖEjiZq2)0;N0z1R*GI{['gM0皈$ 3 Jb_[@XCw]L'Ӂ['qSpL..IS !U]h9 ^^EE<2XRdljog< VE#SQ x{{w5`(,:x,ْ}^᳢/xk 86y00B6ZVb@p$gƅ\x;~(o[_'a,S-w_$Gi)aԟ ~L(O~Jzc}]~ґ»{cE-Yч8~8hmVЋ>5KNs K讁sRnpR4Ò;zkxРjP *L ڇ4\a].܅TW$/ &1ųf GRil4X$0@kN͇ +{咀1j5~ nTmkr!#<`*O#e]IX^\ch ' 7~x$W7>DpHz0զoc@?1NtJMS_\Fd[Ӛ>,䄤=^,]ƹb"F݁_{3ưJf-ceU܄ϯ냚 d\ "ՏT*$ !JAb+%(Jh1 3}L{4P\/D~+I{ubA-FQ >%)q6 0kp(;@PP_;2sb*ˊ_.|.#]<KIVthK+q)OF |?qIFpܖA0:_PCXX\̹4IrOlefB<2YR,cVp9ώn1^|.o>K&Rf}bF]ЛVΜVbֻD$M&)0)l`("̣c H O{'dA&ΊA?W`d gCm)5NƁ r<@Bl4dW+[gBtiּ;LF(TEpl \,jm$ uM/~[6w],}WiB2[#Ni'ȶ[?%FJ`I"Bl,j۝W(cx;V*TRЏ@9[+M10jJ}'fDPP@Y75B6 rbQ1EOQ0N_9h6k,=ƚY^hlCC  7 ?:7$xa] ֺm|O*&ȪľC}_;ɧJZ0D@)m_3Z9F007YA"dZθi| lN0\lsL8= S]s&"AHT\Eq2D 1!菨 fA6& YrOx;AH/=f_GV_00x_C&uò)GgNjyy-{5 2.SPJqݶR8zwu7r|T+Kz6nAYt*QՊU?:>GsO,\1TeyhRSsQf k? nQm%\5lk~U};lSQ69wםqZd>u-vr+ oP$FM]ySA&Ŝ Y؈ ZʭDcb1;KJ2C!࡟p_Lv^)pIN:|hݝ)1z. endstream endobj 133 0 obj << /Producer (pdfTeX-1.40.14) /Creator (TeX) /CreationDate (D:20131218120807+01'00') /ModDate (D:20131218120807+01'00') /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.1415926-2.5-1.40.14 (TeX Live 2013) kpathsea version 6.1.1) >> endobj 11 0 obj << /Type /ObjStm /N 96 /First 809 /Length 4391 /Filter /FlateDecode >> stream xnG_1XD}_C~̍,$8[G3DR b]]wUWҕTR2RUZWGT夯tՕU02U0NU0ձR@L\VƭvL)Bea;SY_`Th'G0L 6AWT^VyW!AVcPIXjw`Q+]&_ḯLJ0,z@(0eG,Y#5A>[ x:T+"tUL&AI;3yS%["5h5"EHP8` ւy=XbJ/@Cc`N)!4 j0S`zeAzX>@08m2nˢNK40t 8[v%* (8B qo"zyu=s>:STР|7F͏T*xT$pC.z@EvtHċY={ӋE}W"HI=pt2ޝ~KrIC㏄yUUbC8]К]B.FbB%bpzQW>,^)r=V"AȪ%-eT-EAG_ѪF̣_* dziOJ/$Fd[8$ml*@HF)4(e {r٭*)d'7e:bނi଼>z[`{˚;F'3)C~0wtڊ! ,3az^] S2r"]+Do~ .ܙrR^Jon.JNo}60턹:1sum*@ϝ|3t[{80zoSIB؉pt7r'!. p8Y8w V$,G`G #)H w"C¹N<8F3^q$j.uGBXSk5QKbZ8X*j""erӖtHֿUBo/5ѡOP6z| #Fȫ=< /8cL[beBfb@Pmڒ7n0R:>8ņ 0 SH4Px;0h{C)Dc!0~TT2]jJe-Ul߲>Kæc_*BnEYԒo{-f'0Wv[1si6-)໘y$X?P96yafMp&"ӸS`^*ady$S`HjSB<6f aJ2z_'m5nh7%J>Ab%`s;KC X>ցàM(9F(ܩ`)T'DPr8vγ)z$]nsn}HV[qfRьVҸ&.%VJ& C~/#-[xEn1ՏH6H6$vB)5m7uaĕ <()E A! :xDHe-V*Ab@1wr.eq^ ]8~55Y\A3 ie]qۜm˚-ď=̪3۴#6g?v:cm MbLFLFLJq-45pH8,HgTi8ݧ᫤7[e ě~ /a{O/{4DZi#p^2>^qӉnor]6{'Dc iL<1V| $͒DgD4A=?M.3~|f|޿vټ K~pAc~"q~B@>'gc#dp#HEPx>&/vOx,\kFb,>q2=^SQt<,>Ojiz9gⳘsE\^Z&SM\qB,>Z,~K]|Գ?Y 0TD&Mw'֤ФAM*PIwIϺd--~2&-v닾)yi\[W(V:Z}ϞVӀR=# (Շ/ZR|$㡸) ƝT0ܖ2,9&.[…u{ hFgѤ7R Ɍ*%;gzg%UZG珎#ʥP2m#} x BxSEc<k'|l|kH}e dVίp:|rҭ]E|SfDokN&%(ķ>xN C˙˄nrP'OO{ɾ\/߿}trtuLտR2[9/Z9yOI0 PHO֒ɝ)%@nL\gŎls$旣/PL?j1wbh LfEKؖl ׯ_da)w"YHKV>|y%,few#&I:S`E _ .jEZQk%W/>̲ߍn>m[MۤZSR+E{h`˓gG^k>Vg0q{P; {zaURk%և/'=pwF Rk%?xV oCB0ZǕ`Z뻨1Ʉ}+!Q*slr씌WXqrxc=On>9^u8'Y}]mچ  o1mõᘜ58Czcݗ$Qk*`l-5[j#c kVq{5S^݄ &&e o=98Wt#TKY YjB6zC?^pіel,d2,,TO|mH;/N '>-&X]5> x^w{7-U)c2/B>S#vr<_r_iw/Q;mlW q>]cƔV7w#!{C|hwwP]E] ߹[&ot] =Os%"V&Fo]ptiΦӍo@tH+ykDZK&_k2Lni hwO]֎6pΙK|'=u랿3`Ի.˥0a T]7Eb@񝚹K&k$~ڥ:G41|VZ_Ug#2-񷡗x i @ <16F06B395157D495E29FAA03504D52CF>] /Length 374 /Filter /FlateDecode >> stream x%ϽOSqmVkZD*XE 1qp1 &09gM\q!сĄտ{roYϙE@ fqc'mB ?S Y - IhRwKd< $Ht"a Ê!H`zz jV*IAXc2DR'F0UScQ}펓c IN,%')#IR$cd%%r'샯a?7TaI i%]RMwU?ʪ \WN5 jn*7*g5x/R݀V}ͪ~T9c?e5' endstream endobj startxref 267944 %%EOF snowfall/inst/doc/snowfall.Snw0000644000175100001440000006503712254301027016164 0ustar hornikusers% \VignetteIndexEntry{An R Package for easier cluster programming based on snow} % \VignetteKeyword{Parallel Computing} % \VignetteKeyword{Cluster} % \VignetteKeyword{HPC} % \VignetteKeyword{snow} % \VignetteKeyword{LAM} % \VignetteKeyword{MPI} \documentclass[10pt,oneside]{article} \usepackage{url} \begin{document} \pagestyle{empty} \setlength{\baselineskip}{1.25em} \setlength{\parskip}{0.5em} \setlength{\parindent}{0.0em} \begin{titlepage} \title{Developing parallel programs using snowfall} \author{Jochen Knaus} \date{2010-03-04} \maketitle \begin{abstract} \texttt{snowfall} is an R package for easier parallel programming using clusters. Basically it is build upon the package \texttt{snow} \cite{TIERNEY08} using it's network and cluter abilities and therefore offering use of Socket, MPI, PVM and NetWorkSpaces support and can be seen as an "usability wrapper". \texttt{snow} functions can used from within \texttt{snowfall} as well. \texttt{snowfall} offers additional support for implicit sequential execution (e.g. for distributing packages using optional parallel support), additional calculation functions, extended error handling, and many functions for more comfortable programming. Also, \texttt{snowfall} can be configured via command line arguments, making the change of cluster settings easier without program change. This can be used to connect to batch- and workloadmanagers. Finally \texttt{snowfall} can be directly connected to the R-specific cluster manager \emph{sfCluster}. \texttt{snowfall} does not add an technical layer of abstraction to \texttt{snow}. But beside from the connector to \texttt{sfCluster}, it builds an extra layer of usability on the top of \texttt{snow}. It is not thought as an replacement for \texttt{snow}, but an addition for inexperienced users or those who seek more comfort using parallel computing and R. A further introduction to snowfall is published in the R-Journal \cite{Knau:Porz:Bind:Schw:easi:2009}. For additional documentation, help and examples please visit our website: \url{http://www.imbi.uni-freiburg.de/parallel} \end{abstract} \end{titlepage} %% Inhaltsverzeichnis \tableofcontents \newpage \section{snowfall} \subsection{Getting started} \subsubsection{Requirements for sequential execution} Basically, \texttt{snowfall} is able to run without any external library. In this case, it is not possible to use parallel execution of commands. All potential calls to parallel functions will be executed sequentially. Programs written in sequential use with \texttt{snowfall} calls can be running in parallel without any code change. \subsubsection{Requirements for parallel execution: Basics} If you just want to use parallel computing on your local PC or laptop you are just fine with basically installation of \texttt{snowfall} and \texttt{snow}. You can use then a so called socket cluster, for which no additional software needs to be installed. If you are just wanting to use parallel programming on your local workstation, PC or laptop, you are fine. \subsubsection{Requirements for parallel execution: MPI} You have a running MPI cluster (OpenMPI or any other kind of MPI cluster) available. Although snowfall is useable with OpenMPI as well, the management software sfCluster can currently only used with LAM/MPI. \subsubsection{Requirements for parallel execution: LAM/MPI} For using sfCluster with snowfall, currently LAM/MPI is needed. If you are using Debian/Ubuntu Linux, just call\\ \texttt{aptitude install xmpi lam4-dev}\footnote{On other Linux distributions there are similar packages with probably different name. It is important that you install the development version of the LAM package, as the \texttt{Rmpi} package need these files for installation.} Further you need to install the R-packages \texttt{snow} and \texttt{Rmpi}. If your program uses libraries, ensure that these are available on all nodes. If they are not present in R-default path (on given machine), ensure that they are accessible in the same location on all machines (for example \texttt{/home/xy/R.libs}). If you want to run programs only on your (multi core) computer without any cluster of many machines, you do not have to setup the cluster yourself, it will be started implicitly in \texttt{snowfall}s initialisation. Using two or more machines for cluster calculations, you need to setup a LAM/MPI cluster and start cluster explicitely. This is no big thing at all. For example, edit a small textfile like this one: \texttt{machine1.yourdomain.com cpu=4 sched=yes\\ machine2.yourdomain.com cpu=2 sched=yes} Just enter the machines for your cluster and the amount of CPUs. You start a LAM/MPI cluster using\\ \texttt{lamboot hostfile}\\ where \texttt{hostfile} is the little configuration file edited above. To shutdown just call \texttt{lamhalt}. For further details upon LAM/MPI setup, see \cite{burns94:_lam}. Note: All parallel programs you start are running in this cluster. If your program requests 100 CPUs on your private dual-core machine, you get that amount and 100 R processes are spawn, independent or available ressources (memory, cpus). For workgroups or larger clusters, management solutions like \emph{sfCluster} are strongly recommended. \subsubsection{Requirements for parallel execution: PVM/NWS} PVM and NetWorkSpaces/Sleight are supported in snowfall as these are useable with snow. But both are less supported by sfCluster (but at least a managed start can be done using sfCluster), so there is no further documentation about their usage here. \subsection{(Short) introduction to parallel programming} The general goal of paralleling your R program is to vectorize the data or calculation loops (probably with wrapper functions), as all calculation functions of \texttt{snowfall} are kind of reimplementations of R-list/vector functions. A good introduction to parallel programming for statistical purposes can be found in \cite{ROSS_07} and \cite{HANA_STAT04}. \subsection{Introduction to usage of snowfall} Basically, usage of \texttt{snowfall} always works with the following scheme: \begin{enumerate} \item Initialization using \texttt{sfInit()}. Set up the cluster (if needed) and the internal functions. \texttt{sfInit} must be called before using any function of the \texttt{snowfall} package.\footnote{The only exception is the function \texttt{sfSetMaxCPUs()}, which raises or limits the configured maximum CPU count.} \item Export needed variables/objects to all slaves. \item Do some parallel calculations using \texttt{snowfall} calculation functions. Repeat as many times as needed. \item End parallel execution using \texttt{sfStop()}. \end{enumerate} The initialisation differs if you use \texttt{snowfall} alone or with the management tool \emph{sfCluster}. In this chapter we only cover a standalone usage of \texttt{snowfall}. For usage with \emph{sfCluster}, see chapter 2. If you are firm on using the R package \texttt{snow}, starting with or porting your program to \texttt{snowfall} is easy. The complete initialisation is done with a single call to \texttt{sfInit()}. The main arguments are \texttt{parallel}, \texttt{cpus} and \texttt{type}, giving the running mode (parallel execution or sequential execution), the amount of CPUs if executing in parallel mode and the type of the underlying cluster. If running in sequential mode, \texttt{cpus} is ignored (and set to one). Without a given \texttt{type} a socket cluster is started, which does not need any further software installed and therefore most likely runs anywhere immidiately. This is the desired choice for executing on a laptop or single multicore machine, too. Please note, that on Windows an installed Personal Firewall may alert the network access, please allow this. %On calling \texttt{sfInit( parallel=TRUE )} without a running LAM %cluster (but LAM installed), a \emph{local} cluster will be started, %which only contains your local machine. This can be handy on single %multi-core machines. But note you \texttt{sfStop} will not shutdown %this cluster, so you have to stop it yourself manually (if wished). Sequential mode can be useful for developing the program, probably on a single core laptop without installed cluster or running Windows operating system. Also sequential mode is needed to deploy a package using \texttt{snowfall} safely, where you cannot assume a user have an useable cluster installed. Other arguments for \texttt{sfCluster} are \texttt{restore}, \texttt{socketHosts}, \texttt{slaveOutfile} and \texttt{nostart}. See package help for description. If the initialisation fails, probably because of missing base libraries \texttt{Rmpi} and \texttt{snow}, \texttt{snowfall} falls back to sequential mode with a warning message. In sequential and parallel execution, all functions are useable in both modes in the same way and returning the same results. \begin{verbatim} sfInit( parallel=FALSE ) sfLapply( 1:10, exp ) sfStop() sfInit( parallel=TRUE, cpus=5 ) ## Now, index 1 is calculated on CPU1, 2 on CPU2 and so on. ## Index 6 is again on CPU1. ## So the whole call is done in two steps on the 5 CPUs. sfLapply( 1:10, exp ) sfStop() \end{verbatim} Please note: Most of the \texttt{snowfall} functions are stopping the program on failure by default (by calling \texttt{stop()}). This is much safer for unexperienced users. If you want own failure handling, install your own handler \texttt{options(error = ...)} to prevent snowfall from stopping in general. Also most of the functions feature an argument \texttt{stopOnError} which set to \texttt{FALSE} prevents the functions from stopping. Do not forget to handle potential errors in your program if using this feature. The given behavior is not only better for unexperienced users, any other behavior would be very nasty on package deployment. \subsection{Writing parallel programs with snowfall} \subsubsection{General notes and simple example} If you detected parts of your program which can be parallelised (loops etc) it is in most cases a fast step to give them a parallel run. First, rewrite them using Rs list operators (lapply, apply) instead of loops (if they are not yet calculated by list operators). Then write a wrapper function to be called by the list operators and manage a single parallel step. Note there are no local variables, only the data from the list index will be given as argument. If you need more than one variable argument, you need to make the required variables global (assign to global environment) and export them to all slaves. \texttt{snowfall} provides some functions to make this process easier (take a look at the package help). \begin{verbatim} sfInit( parallel=TRUE, cpus=4 ) b <- c( 3.4, 5.7, 10.8, 8, 7 ) ## Export a and b in their current state to all slaves. sfExport( ''b'' ) parWrapper <- function( datastep, add1, add2 ) { cat( ''Data: '', datastep, ''ADD1:'', add1, ''ADD2:'', add2, ''\n'' ) ## Only possible as ''b'' is exported! cat( ''b:'', b[datastep] ) ## Do something return( datastep ) } ## Calls parWrapper with each value of a and additional ## arguments 2 and 3. result <- sfLapply( 1:5, parWrapper, 2, 3 ) sfStop() \end{verbatim} \subsubsection{Basic load balancing using \texttt{sfClusterApplyLB}} All parallel wrappers around the R-list operators are executed in blocks: On one step the first $n$ indices are calculated, then the next $n$ indices, where $n$ is the number of CPUs in the cluster. This behavior is quite ok in a homogenous cluster, where all or mostly all machines are built with equal hardware and therefore offer the same speed. In heterogenous infrastructures, speed is depending on the slowest machine in the cluster, as the faster machines have to wait for it to finish its calculation. If your parallel algorithm is using different time for different problems, load balancing will reduce overall time in homogenous clusters greatly. \texttt{snow} and so \texttt{snowfall} feature a simple load balanced method to avoid waiting times in such environments. If calling \texttt{sfClusterApplyLB} the faster machines get further indices to calculate without waiting for the slowest to finish its step. \texttt{sfClusterApplyLB} is called like \texttt{lapply}. If your local infrastructure is such an heterogenous structure, this function is the way to go. It can also be handy in homogenous clusters where other users spawn processes, too, so sometimes load differs temporarily. A visualisation of basic load balacing can be found in \cite{ROSS_07}. \begin{verbatim} sfInit( parallel=TRUE, cpus=2 ) calcPar <- function( x ) { x1 <- matrix( 0, x, x ) x2 <- matrix( 0, x, x ) for( var in 1:nrow( x1 ) ) x1[var,] = runif( ncol( x1 ) ) for( var in 1:nrow( x2 ) ) x2[var,] = runif( ncol( x1 ) ) b <- sum( diag( ( x1 %*% x2 ) %*% x1 ) ) return( b ) } result <- sfClusterApplyLB( 50:100, calcPar ) sfStop() \end{verbatim} \subsubsection{Intermediate result saving and restoring using \texttt{sfClusterApplySR}} Another helpful function for long running clusters is \texttt{sfClusterApplySR}, which saves intermediate results after processing $n$-indices (where $n$ is the amount of CPUs). If it is likely you have to interrupt your program (probably because of server maintenance) you can start using \texttt{sfClusterApplySR} and restart your program without the results produced up to the shutdown time. Please note: Only complete $n$-blocks are saved, as the function \texttt{sfLapply} is used internally.\footnote{This function is an addition to \texttt{snow} and therefore could not be integrated in the load balanced version.} The result files are saved in the temporary folder \texttt{~/.sfCluster/RESTORE/x}, where x is a string with a given name and the name of the input R-file. \texttt{sfClusterApplySR} is called like \texttt{sfClusterApplyLB} and therefore like \texttt{lapply}. If using the function \texttt{sfClusterApplySR} result are always saved in the intermediate result file. But, if cluster stopped and results could be restored, restore itself is only done if explicitly stated. This aims to prevent false results if a program was interrupted by intend and restarted with different internal parameters (where with automatical restore probably results from previous runs would be inserted). So handle with care if you want to restore! If you only use one call to \texttt{sfClusterApplySR} in your program, the parameter \texttt{name} does not need to be changed, it only is important if you use more than one call to \texttt{sfClusterApplySR}. \begin{center} \begin{verbatim} sfInit( parallel=TRUE, cpus=2 ) # Saves under Name default resultA <- sfClusterApplySR( somelist, somefunc ) # Must be another name. resultB <- sfClusterApplySR( someotherlist, someotherfunc, name="CALC_TWO" ) sfStop() \end{verbatim} \end{center} If cluster stops probably during run of \texttt{someotherfunc} and restarted with restore-Option, the complete result of \texttt{resultA} is loaded and therefore no calculation on \texttt{somefunc} is done. \texttt{resultB} is restored with all the data available at shutdown and calculation begins with the first undefined result. \emph{Note on restoring errors}: If restoration of data fails (probably because list size is different in saving and current run), \texttt{sfClusterApplySR} stops. For securely reason it does not delete the RESTORE-files itself, but prompt the user the complete path to delete manually and explicitly. \subsection{Fault tolerance} Differing from \texttt{snowFT}, the fault tolerance extension for \texttt{snow}, \texttt{snowfall} does not feature fault tolerance (see \cite{HANA_04}). This is due to the lack of an MPI implementation of \texttt{snowFT}. \subsection{Controlling snowfall using the command line} snowfall can be widely controlled via command line arguments. This is useful for fast changing of cluster parameters (e.g. changing the host names in a Socket cluster) on a raw installation and it serves as connection to sfCluster. Of course it can be used as connection to any other workload- or batch managing software, too. On the commandline there are the following parameters: \begin{tabular}{lp{10cm}} parallel & Switch to parallel execution. Default is sequential execution \\ cpus=X & Amount of CPUs wanted. Without {-}{-}parallel, a value $X > 1$ switch to parallel execution. \\ type=X & Type of cluster. Allowed values are SOCK, MPI, PVM and NWS. \\ session=X & Session number. snowfall logfiles contain number, but only needed with sfCluster. \\ restoreSR & Enables restoring of previously saved results from \texttt{sfClusterApplySR} calls. \\ hosts=X & List of hosts for Socket (SOCK) or NetWorkSpaces (NWS) clusters. Entries are comma seperated. Any entry may contain colon seperated value for the amount of processors on this machine. Example: \texttt{{-}{-}hosts=machine1:4,machine2,123.123.12.13:2} (this spawns 4 workers on machine1, one on machine2 and two on 123.123.12.13). \\ tmpdir=X & Specify temporary directory for logfiles and R-output. \\ \end{tabular} For using these arguments, just add these after an \texttt{--args} on the commandline (which forces R not to treat these arguments as R ones). \begin{center} \texttt{R --no-save --args --parallel --cpus=2 < program.R} \end{center} Starts R and forces snowfall to start in parallel mode with 2 CPUs (in this case: using a Socket-cluster, as this is the default). \textit{Note}: arguments on the command line have lower priority as settings from the \texttt{sfInit} call. That means that the above example only works if initialisation is done via \texttt{sfInit()}, but not with \texttt{sfInit( parallel=FALSE )}, as then sequential execution is forced. Further examples should explan the feature: \begin{itemize} \item \texttt{R --no-save --args --parallel --type=MPI --cpus=4 < program.R} (start using 4 workers in an existing MPI cluster. If no MPI cluster exists, a plain one is started on your local machine only. Beware of this, as you have to shutdown this cluster afterwards manually.). \item \texttt{R --no-save --args --parallel --type=SOCK --hosts=localhost:3,singlema,othmach:4 < program.R} (Starts a socket cluster with two machines and 7 CPUs: 3 on \texttt{localhost}, 4 on \texttt{othmach} and one worker on \texttt{singlema}). \end{itemize} \subsection{Traps, Internals} \texttt{snowfall} limits the amount of CPUs by default (to 40). If you need more CPUs, call \texttt{sfSetMaxCPUs()} \emph{before} calling \texttt{sfInit()}. Beware of requesting more CPUs as you have ressources: there are as many R processes spawned as CPUs wanted. They are distributed across your cluster like in the given scheme of the LAM host configuration. You can easily kill all machines in your cluster by requesting huge amounts of CPUs or running very memory consuming functions across the cluster. To avoid such common problems use \emph{sfCluster}. For some functions of \texttt{snowfall} it is needed to create global variables on the master. All these variables start with prefix ``\texttt{.sf}'', please do not delete them. The internal control structure of \texttt{snowfall} is saved in the variable \texttt{.sfOptions}, which should be accessed through the wrapper functions as the structure may change in the future.\section{Using \emph{sfCluster} with \texttt{snowfall}} \subsection{About \emph{sfCluster}} \emph{sfCluster} is a small management tool, helping to run parallel R-programs using \texttt{snowfall}. Mainly, it exculpates the user from setting up a LAM/MPI cluster on his own. Further, it allows multiple clusters per user and therefore executes any parallel R program in a single cluster. These clusters are built according to the current load and usage of your cluster (this means: only machines are taken with free ressources). Also, execution is observed and if problems arise, the cluster is shut down. \emph{sfCluster} can be used with R-interactive shell or batch mode and also feature a special batch mode with visual logfile and process-displaying. For further details about installation, administration and configuration of \emph{sfCluster}, please visit \url{http://www.imbi.uni-freiburg.de/parallel} or run \texttt{sfCluster {-}{-}help} if you installed it yet. \subsection{Starting R using \emph{sfCluster}} An \emph{sfCluster} execution is following these steps: \begin{enumerate} \item Test memory usage of program if not explicitly given. This is done via a default temporary (10 minutes) sequential run to determinate the maximum usage of RAM on a slave. This is important for allocating ressources on slaves. \item Detect free ressources in cluster universe.\footnote{Which are all potentially useable machines.} Take machines with free ressources matching users request. \item Start LAM/MPI cluster with previous built setting. \item Run R with parameters for \texttt{snowfall} control. \item LOOP: Observe execution (check processes, memory usage, and machine state). In monitoring mode: Display state of cluster and logfiles on screen. \item On interruption or regular end: shutdown cluster. \end{enumerate} \subsection{Using \emph{sfCluster}} The most common parameters of \emph{sfCluster} are \texttt{{-}{-}cpus}, with which you request a certain amount of CPUs among the cluster (default is 2 in parallel and 1 in sequential mode). There is a builtin limit for the amount of CPUs, which is changeable using the \emph{sfCluster} configuration. There are four execution modes: \begin{tabular}{lp{3cm}p{9cm}} -b & Batchmode (Default) & Run silent on terminal.\\ -i & Interactive R-shell & Ability to use interactive R-shell with cluster.\\ -m & Monitoring mode & Visual processmonitor and logfile viewer.\\ -s & Sequential execution (no cluster usage) & Run without cluster on single CPU.\\ \end{tabular} To avoid the (time consuming) memory test, you can specify a maximum amount of memory usable per slave via option \texttt{{-}{-}mem}. The behavior on excessing this memory usage is configurable (default: cluster stop). The memory usage limit is very important for not getting your machines into swapping (means: shortage of physical RAM), which would hurt performance badly. So, simple calls to \emph{sfCluster} could be \begin{verbatim} ## Run a given R program with 8 cpus and max. 500MB (0.5 gigabytes) in monitoring mode sfCluster -m --cpus=8 --mem=0.5G myRprogram.R ## Run nonstopping cluster with real quiet output. nohup sfCluster -b --cpus=8 --mem=500M myRprogram.R --quiet ## Start R interactive shell with 4 cores. With 300MB memory (MB is default unit) ## No R-file is given for interactive mode. sfCluster -i --cpus=4 --mem=300 \end{verbatim} For all possible options and further examples for \emph{sfCluster} usage, see \texttt{sfCluster {-}{-}help}. \subsection{The snowfall-side of \emph{sfCluster}} If you start an R program using \texttt{snowfall} with \emph{sfCluster}, the latter waits until \texttt{sfInit()} is called and then starts the observation of the execution. The default behavior if using \emph{sfCluster} is just to call \texttt{sfInit()} without any argument. Use arguments only if you want to explicitly overwrite given settings by \emph{sfCluster}. \subsection{Proposed development cycle} The following development cycle is of course a proposal. You can skip or replace any step depending on your own needs. \begin{enumerate} \item Develop program in sequential mode (start using option \texttt{-s}). \item Test in parallel mode using interactive mode to detect directly problems on parallelisation (start using option \texttt{-i}). \item Try larger test runs using monitoring mode, observing the cluster and probably side effects during parallel execution (start using option \texttt{-m}). Problems arise on single nodes will be visible (like non correct working libraries). \item Do real runs using silent batch mode (start using options \texttt{-b {-}{-}quiet}). Probably you want to run these runs in the background of your Unix shell using \texttt{nohup}. \end{enumerate} \subsection{Future sfCluster} These additions are planned for the future: \begin{itemize} \item Port to OpenMPI \item Faster SSH connections for observing \item Extended scheduler for system ressources \end{itemize} %% History. \section{History of snowfall changes} You can also call: RShowDoc("NEWS", package="snowfall") \begin{itemize} \item 1.83 (API changes: minor additions) \begin{itemize} \item sfIsRunning: new function giving a logical is sfInit() was called or not. Needed, as all other snowfall functions implicitely call sfInit() if it was not called. \end{itemize} \item 1.82 \begin{itemize} \item Internal refactorings. \end{itemize} \item 1.81 \begin{itemize} \item Change in sfInit() MPI startup so sfCluster can run with snow > 0.3 now. \item sfExport now also works in sequential mode (writing to global environment). This prevented sequential execution in some cases. \end{itemize} \item 1.80 (API changes: minor additions) \begin{itemize} \item snowfall passes packages checks of R 2.10.1 without warning or error. Internal state is now only saved in the namespace itself (thanks to Uwe Ligges for the tipp). \item sfExport can now also export objects in a specific namespace (argument 'namespace') \item sfExport: behavior in error case manageable (stopOnError) \item sfExport: smaller bugfixes. \item sfRemoveAll can now also remove hidden names (argument 'hidden') \item sfRemoveAll is more robust now (some minor bugfixes, more checks) \item sfRemoveAll bugfix for multiple removals (thanks to Greggory Jefferis) \item Bugfix on exception list on sfExportAll \item Refactorings in sfTest() \item snowfall now has a NEWS doc ;) \item No warning on Mac OS because of default Mac-R command line arg 'gui' (thanks to Michael Siegel). \end{itemize} \item 1.71 (API changes: none) \begin{itemize} \item Exporting of objects using \texttt{sfExport} is speed up (round 30%) \item Fixed a bug on Windows in \texttt{sfSource} \end{itemize} \item 1.70 (API changes: minor additions, BEHAVIOR CHANGES: logging) \begin{itemize} \item Behavior change: new default: no logging of slave/worker output. \item API change: new argument \texttt{slaveOutfile} on \texttt{sfInit()}. \item API change: new argument \texttt{restore} on \texttt{sfInit()}. \item API change: new argument \texttt{master} on \texttt{sfCat}. \item Windows startup fixed. \item NWS startup fixed. \item sfSapply is working as intended. \item Changing CPU amount during runtime (with multiple sfInit() calls with different settings in a single program) is now possible using socket and NWS clusters. \item Dozens of small glitches inside snowfall fixed (also messages are made more precisly). \item Package vignette slightly extended. \end{itemize} \end{itemize} \bibliographystyle{plain} \bibliography{all-bib} \end{document} snowfall/NAMESPACE0000644000175100001440000000224211334271631013335 0ustar hornikusersexport( "sfInit", "sfStop", "sfParallel", "sfNodes", "sfCpus", "sfType", "sfIsRunning", ## New 1.83 "sfGetCluster", "sfSession", "sfSocketHosts", ## New 1.6 "sfSetMaxCPUs", "sfLibrary", "sfSource", "sfExport", "sfExportAll", "sfRemove", "sfRemoveAll", "sfCat", ## Snow wrappers "sfClusterMap", ## Currently not implemented. "sfClusterApply", "sfClusterApplyLB", ## snowfall addition: intermediate save of results (with auto-restore) "sfClusterApplySR", "sfRestore", "sfLapply", "sfSapply", "sfApply", "sfRapply", ## Currently not implemented. "sfCapply", ## Currently not implemented. "sfMM", "sfClusterSplit", "sfClusterCall", "sfClusterEval", "sfClusterEvalQ", "sfClusterSetupRNG", "sfClusterSetupRNGstream", "sfClusterSetupSPRNG", ## Unit tests "sfTest" ) snowfall/NEWS0000644000175100001440000000276212254102205012614 0ustar hornikusersChanges in snowfall 1.84 =========================== o Bugfix sfExport: if exporting a NULL value, no error is raised. Changes in snowfall 1.83 =========================== o sfIsRunning: new function giving a logical is sfInit() was called or not. Needed, as all other snowfall functions implicitely call sfInit() if it was not called. Changes in snowfall 1.82 =========================== o Internal refactorings. Changes in snowfall 1.81 =========================== o Change in sfInit() MPI startup so sfCluster can run with snow > 0.3 now. o sfExport now also works in sequential mode (writing to global environment). This prevented sequential execution in some cases. Changes in snowfall 1.80 =========================== o snowfall passes packages checks of R 2.10.1 without warning or error. Internal state is now only saved in the namespace itself (thanks to Uwe Ligges for the tipp). o sfExport can now also export objects in a specific namespace (argument 'namespace') o sfExport: behavior in error case manageable (stopOnError) o sfExport: smaller bugfixes. o sfRemoveAll can now also remove hidden names (argument 'hidden') o sfRemoveAll is more robust now (some minor bugfixes, more checks) o sfRemoveAll bugfix for multiple removals (thanks to Greggory Jefferis) o Bugfix on exception list on sfExportAll o Refactorings in sfTest() o snowfall now has a NEWS doc ;) o No warning on Mac OS because of default Mac-R command line arg 'gui' (thanks to Michael Siegel). snowfall/data/0000755000175100001440000000000012254301027013022 5ustar hornikuserssnowfall/data/test.rda0000644000175100001440000004454412607465525014524 0ustar hornikusers7zXZi"6!XEI%])TW"nRʟ0]d;Q\9LnEl 32D(D>yj'+դ/f`Jܰo& k+RsUH2̑}J/_d/]N']ξ珷?ߥp3x^%iC$hufF էcWb邘p.XTe9oԎ77p~"8Tsd+BЦge"s$WƕqQb<=e8Ahm{V%/< >p.{HeKu~yjC*QbF<@埂4F{ ~KXvЬNǺ$+pEr-/@ᒾd36"Ԉtָ?ozX5i# qp$eB[6O>㗤 ޳HT]AQ@v`f!w=Х.LtO|ɴo3r.1W Z+bVie[ TYbte'10L 2=Wܴ ,I˓)H5G !OʹEg 0Ԁ`.TL*;d]Ȱ; 'ė=<&~V,喠, #UO4ܥ-a5Z3l#v ;tLq5XYgdŒj"kCFe~ wv' `ڸ,t~ -`>&KNj` xAѴzދd8e\%nBb*'2rX\iUX_z3BrtFe;ChlGԫVYIb'u_4$̅Gnr.ɇRb̑Ķh ?wG4IDs'}S-kx 5"`y3 V5ʱlRB ) n^_l{р32-LJBB#SՆ^2q;l6\PiHA oir[IP}a05%3K9=U vv*xGxvyl4؛8n #}&xlL&AHtHز?~ҟU>vMO4^1J7`RDO !_tK>[CQQP { DF^C1'ūI->UvYI9߂!)VS  }ZOrb5 {1` E#>N٦WuS4&r_pKTĤFracpnt@Į(/İp90Ұʽ"[`VMREvKlvY60+-hAW@F (Nj;HYbn?b08?8?ZB,1ݣJul߫xh}1iB}͔mYa.{_H5{@eQl뚉P,i:(d[v|~8+_yC{D^# ={U1Iu*k_d,dIx_ dHȬKo09M 7DZ]O{*6z C{}^kyZ`_**I+Rz"s-@A.΀EO4kXB?J1LV3r:2pX \v 4Ʋh߃G|`ovmv8hF֌I@R,sM,CD o0{$4o?U?N,0MBR3CY݄v :.zi2"?;uuf kEpP/lւjbZ$H+y9*JΜ>AIG>6r*гJ$w.j= ZNV&R. ǣM6 n4K#sx(?D|u'6OM!w%>z"b93r%~Ш>i پT8sȺ]o5b9fʙ 2~^TwI'oaHk?[П< f9J!ڃld֘nBE͖sǫ̱ Q $ PVcKm<1sg70Z[TG/FS8.1l?y[?wſ/i T+E0'rC#;er_jG͹8hk+r3W8oP&C#'aH?SӏNCv+}N\lU=@P] +~EOH3?_)l.YpD4ڌBz'؆1gad2ޚ0h-F3"yom`GbY ShR OlϾه irl1<h͓|g珉UP@|DQhʱ:dF؟ڒh /ro!'Bjhdm!|.]xPC"8]8]L(2:NK\j_oRA@S7ic|#ܪYu\=1~S9 =MxN)_@\>u.z`cVυgXQfqҭ˨Ż3:%BW>"U4j&B? B\S-|TstakۥJ;)}6 ^Lb<V37!+A[`.o N4EC?rs_-)U-|vqbcyBHF x[6h?3NJ]-oǔh$E0+ '8JCO;֋ZQ[Fml!TĆc0y6e]S<2h4CǻOXHTgA^'*Lgz'e @hJ/xƐ8پ+$urznY!G7E`Ljo_3'yUUDdg5BB{rà9PFIGһmy-Hnhgc`+=+RLYCE}bVaBѧv2/$' G8t6aUXsԎše೵Q!M)\=3#9FKtX J z˔]"#]PrMOZDєLGv Q6980ɞ>R=UH/}ZkH|Oxfb->v?WM\#l^A=nyoB,%@/W]@a]R CU b<3R9,e2;~Z>i/9%Õ>7FI;++nR:,IGA3Og+9gO)|wl1}ڷs^4ʷp=81 (MY噠JB^[OX OzoهqrɎ沴&9)˻ ,-ԛ1-*Zzdb$5^WN+;R{:-1!}z _{e%>X⹽ES$Re(X^|4@d qᕥ7G%FօR vtW\+?ڎ q N5@-zcĞ̷5HQ}UsV樽䉅^`zd%OJ2+UF x: j#iaȨ'>0W[B&5ag Ws?){dI&oEҕ FGh@LzX14pK!jeZmo?@nyYa\+{as5dDz?Iݚ;>)NHժ%fm'qM#╧JzL3 jUCc>h9NVi&1Z୹Xi*>^C\$XڽKcOiuNEE{,-` ~.F]Lݺ q< ~cj\ 7hL/E^fPvLnj`qM®Ug堠`MA n"vnqN#{`)ΒГD9pԫ've[%,e+`Y9u2V1i4T8p9"?-L)};Mv{.͎;lӃz栋sv#i5!w^QFe\J 5'*%U('հZvr$4WWf¦:wB$ Gvսkڇ{/!宍&)4´_r+}hq`^$m8c=6p&X6BWOՃrߺQD~6RwwGs8NYG|abej`r̵ݡEl6"o[U ӄ |Xب-c)Od2}btd A *olFX[}QKZg $߶(['Ul< {TM/ BC+`?7I?`(U$gɱ;ed|7>OQtH^wg Y(x!O_I3C&5,ӫvj$PLZH>BR G-BZAh3 (o@';-XQi;UoiǂqT44z7e[ hT9pz+:nT{94@F }v5wRczڨ+&j`YW*zp ?H^Ggڙe K_+G>H5ɸx`0>Tᇛ%Ivb:E10dwÕℵ8Ė0qm[cŵְ_1>4VzݥGEȢh!vO %SsZ~BN*-^VJo7%,%}LFp^ $y{:V/f}ysa0]kz@ 嬬}f>\PE.b:4Zt-)KߖtNc'M{1+iquR6l<`|.xTHJN=z1ƕBHC#d#ԂyDXJ9AZp~D!_%I-cep4Rkt@2כ+5 ?&j|;  X^I L%>E H T!d/g3~_ rBh_읔$K!|QA_̆fc4 @1ۓsvtg NS t${A")f+WNA}jit~ЯD](9'C/h9-ܗzΝ8Ŗg9ʒ|޽w!$hyr.hQTJĄLe'E<ʚ A4]٭+ -$/1qoG\ߨN˜q\>ސL;V&LwwUe_ qK2ԡ yM#G7XߡehDoYm~#jhhBHY)gإٷjC]Z 8pNDt2Hsqw;մ[#T !FSL?ZݑRHdLح=&NZ|Y6-le(Y7zv2=ej <ݵʺBq\QN4"?Ac+V|U_ZGK>c7)t1m'MF{^XB8Z<H>ӺD1881IQ~_H} z9E".Jie9 ]NTa$zSMILT@Prؖ'酷'}v 4RF~ PTWH񗎦QUTIχv/ E<$dVׁc9Kg"A4¡3&$<ԝ4\i2U: zDx]J씎nw{,OjOE?! Ս0d|:{}1T o7;ְ!:\ole ۳" DŽ2]hQq,r9ܭ]`JuCEʸK% g v,HIϞ^Kc2vn~9+ lo5ytv{\DrMp [xQK/ap;Lq z?ƓA4 /6"NeH;D D7>hOZNRР>W,0}NM65%-5W fVJa(șmKgMkMA y'ácL+_' ?bwW$K W$@p:4AeɑP`[#}+j qSM(EìKPۅRz1@8.-S$J/ҋ)Cgիv.w"MszMv0G 1Cnˁ(%!OboQazIn7i~o~jnrupY*''01V j~Ir K;QG )c\t7Y9-*H giX|Ck?YVrfmͽ<[GqL70ҴUڀ8}F]zJ5Z܌I x~Q3&G Vy1NUJ|z.j+.u9m-BPܵX _$P{Rt%HEջ9$]yvN*ѧD`Ҭ̝Qr|4ݼC{hz -O(#6SX)m v{Bxnc@!WD#K=Ɛn }NC.VqV74γ¼|cQ^%o y2Q+-sY¶_^7|N\!bV˷|n2 g#`U&gSȉ;x,!9?6y" $gf5 jkR;] kVt.ɣѼ W"~{)t@u-\&f8y!LB.]n_@$j+:p3& tGbȗμ'&NQ,@+ )'_+3+H[_2dV,mXc42)҉(.[w1aUiIHǧ*!#yNw2(ˤF  lxC rUwh+'-Jgw;Y`>nt i# K>-|_}N9V %Wp߂{ z RۃɪT;a|D~L#ш2eU3N 0ESI-L_=@R7nhq^& R8G{./C%⭼–CkOuoX'Ϩ=~$ ؅aGJ_K5 T}ྫྷǘovS*j 1: ,Ư1Qu"6L.V ,C1cHBO<_y semxnz8XGڐ'd=zz|e"<[cX;Kbw $4ǧ e GcTr.%HB/Sk_~=ݼo˓ \ %u ~O"˩V("NQR ;8ZI |Mnj!}@w8Gdb.!s";=\8Ҥ8oN1l>xy ș2H>ӹbJf.?#v4-?Gy (,S[=J aZ4 jx̪cz &°&}2^f2 ZH T]"BSe''\A'rnRw\PyF2.ϬKHx)+q7Cl5޻2IW*x6k(N*wq1E "7a^L ⇅t]7W<1;މք8,hzxYDOɀG><ucPH ]w%8>[V@톆qdrz6GG*u4MXB×~@a+K1}Fw݄]v2>IݹЅq!Uo 1Qs-/D}jc. !{3NytqX@Ȓ\Y؟@CwC25W5h ̵z1Qgꇗm:ct)Zs\adrZ4yM x߲mMy)4-2L .RgEWHӉ5M>F,,xfh~ tZv%ԢY"+n2ƺS _.9OY/X:MJ879G8{؇5"J⏠B}C))iɏx|b_#x-(ԗ5 # G&BFѪ3. VxI)$c})qzP~hޡ&"E&)=?9_"$;Hf%<Ē3 4x7R$ O9E:LYv @:ŖT1kb?t{pVT[щԤ65LQg?*"WLuA}e84Gb}-*j-gHsӌ2gǂƿ(G | /!V^W 5V/"anvq80Mc/W2<]1:?t:|NbGĻ& 5;rUrE|JqeaN-8+Bkk eV J#NLXwb)hs`Q v#Q9vy6zYctRYsLhws f4<bo|2(`穦-#)1z?yoŕL)iyh t9$mdye6coifvsPP9yVO U!%^#dc 7zz/7:4u1˜ lŧ}pn~YV!k@|,oݴT׷s͘bXMjVZR<(W/s1Cr*L3**߸WNy HC7@t?ZD-blLDt[8n[k}?k/v<'5 ұېHl$S 5<LseUB |e^k;8Xtj e#`1&w+I]hn*KZ\%l)QEJ""0{f:nkcvi?.pB{uC?$w x#%PI]{-͔|qίXV@".0aXIVrXgiK>y1͸LRSrɏ5.. >u$MiiV0?$ 1!";ef֢K`M33 )Rbjl(iZJ.lҏ;co4wB:0,_^? v@E\%08t?1 U15mڿKQ-KB/fR/ƩzeHͻ-vGJmhVm9zX "+@-:H&8K)r! ֜,9q/cD+'(Bڲ5VמllULG5+IMaE{Z{1:Ou~}DPXv碅${@͔v~?uJJr.9MU 8kA63߼zQ3d?8" $}M`77WNUD˯ehg9GK&vqB$'ݸ73bfc#Ƃش|3Y!;إ/l'EMS(KSZRXzeQzrwלH[ $CRTY@a>x'}qQxaD85E(}V3)k7rS^Z1?qx)Uh5Rj5lNzDG9VϼNh70KMynURqDX3FmRTo$W,:5:C{'!Jq=̧wj^1C7חFY3 zP2){)P UcjG`_'ԧ[ ~rC}K8<1Q$ mEPh4D3Oا$妤qIUVOebȮ9|:E7Z*C}S=iᣨĩE3"owa^+8/E#OxԨE4u@ޫ}Ou8%>k%O,Pheia` .g Ts)I䆛f\m91^ TeoF+;-9G ꠚB(6ufidF!,\OC~@w^ 7Jq<צ*M7%iEX e\XIBif;7H6<UИW5ǷӶ97Tzn-F Mk^&ʫᅩx .$,'2'RYmAT,1ŕbR9E>M#`Er*ε}…+258"(A8Db,Dp}2 gN@a^䗜MpkTkL=^{KÅĜmDh%|O7%pOW{dcS˂BW|C6÷1 GaJb WbÀ8;.26ߟG^7-/_ڢ%P5&mX1tĝ)+ =u3Qs⧷hv5.)fY=!kJyҹu$^)Q]d}[E9T`^Ė[Ʀ'M4qp=sF7ut?ܴJekm6 $? =!MC|GQ5lyX\*?ӠX-t QQϷ`C*R\$_䭮1Ko|d=nF? Fvd*aSk@JWpJիRLzthRJA&f:7qJAٵpjy,34I`|4Z,6Ӏ*{qpjL}ƽ d]6K +K}XK.ӬUw[ ~7 6mp0{ex/54Ӏ&V H|${8$6 .0%9 WYtCY@!1.a6| ˓W.F79?bH =J,K`W䆲(-  Xqip t2GFߚP%o}{ M7yn#5x F0D*rQ)q˿o0a#`)U@:򢖫@ɿJMo}}c,)2y7Yy4x?\&f@0: TE o6T.zsG!,!x|]lbETmP ֖yf^ol. d0,k[RShQ'CLc h􀊇GmY.ÄD 5fa  N'7Ic51a<˪B,bCIϝ ߳(u鹯J|lû8$^:+ns4ˈY-;KM H3=̐98W؅b= q.d߅.0ߝ}W>m]s؅CKB=Z(t(Uؑ/}.GmsyIUm,u_dFV "{UXϧR/M.r㬷q0 O6RW.*bH:ޝ"~ѱ͒hf|11ހ<z/DMGa#SΑMx;go8dȫA'!"I75܂AFaf>5V| RML.Hi'dp]ZGn]D<Ӭ}F:P%*<*%[[wG"Z>5opG W$qM(pƌrz>0 YZsnowfall/data/config.txt.gz0000644000175100001440000000012012254301027015440 0ustar hornikusersTs u v s T  !.`_?`3 P%7RAvZFsnowfall/R/0000755000175100001440000000000012254102616012315 5ustar hornikuserssnowfall/R/clusterFunctions.R0000644000175100001440000012374112254102616016022 0ustar hornikusers##***************************************************************************** ## Functions which extend Snow usage or implement some higher level usage. ## Wrappers for Snow functions are in snowWrappers.R ## ## Functions: ## sfLoadLib - Load library in cluster (path conversion, flags...) ## sfSource - Load source (path conversion...) ## ## sfExport - Export local and global objects to cluster ## sfExportAll - Export all global objects (with given exception list) ## sfRemove - Remove objects from nodes ## sfRemoveAll - Remove all objects from nodes (with given excpetion list) ## ## sfCat - Cat something on cluster ## sfPrint - Print something on cluster ##***************************************************************************** ##***************************************************************************** ## Load a library depending on sequential or parallel execution. ## ## Should behave most likely the build-in library() function. ## ## Running in sequential mode: normal "library()" command. ## Running in parallel mode: the library is loaded on ALL nodes. ## ## PARAMETERS: ## RETURN: Logical TRUE/FALSE on success. On failure, if noStopOnError is not ## set, stop immidiately. ##***************************************************************************** sfLibrary <- function( package, pos = 2, lib.loc = NULL, character.only = FALSE, warn.conflicts = TRUE, # keep.source is removed in R3, but kept here for back-compatible API # keep.source = getOption("keep.source.pkgs"), keep.source = NULL, verbose = getOption("verbose"), version, stopOnError = TRUE ) { sfCheck(); ## Generate (global) names list with all parameters. # setVar( ".sfPars", list() ) sfPars <- list() ## Help does not make sense. ## if( !missing( help ) ) ## stop( "Help is not allowed in sfLibrary. Use 'library' instead." ) if( !missing( package ) ) { if( character.only ) { if( is.character( package ) ) sfPars$package <- package else stop( paste( "Package", package, "is no character string." ) ) } else sfPars$package <- deparse( substitute( package ) ) } ## package is now a string in any case. sfPars$character.only <- TRUE sfPars$pos <- pos sfPars$lib.loc <- lib.loc sfPars$warn.conflicts <- warn.conflicts # Raw quickfix for R 3: simply remove argument if we are on version 3. # But keep it to not break R 2.x (although default argument changed # there). if( as.integer(R.version$major) < 3 ) { # As new default for keep source is "NULL" (from 1.84-2), we need # to rebuild the old default behavior of the functions arguments. if( is.null( keep.source ) ) keep.source = getOption("keep.source.pkgs") sfPars$keep.source <- keep.source } sfPars$verbose <- verbose ## All libraries are loaded internally with logical.return. sfPars$logical.return <- TRUE if( !missing( version ) ) sfPars$version <- version if( sfParallel() ) { ## On Nodes load location with absolute path. if( !is.null( sfPars$lib.loc ) ) sfPars$lib.loc <- absFilePath( sfPars$lib.loc ) ## Export to namespace. setVar( ".sfPars", sfPars ) ## Weird enough ".sfPars" need to be exorted (else it would not be found ## on slave, although it is a parameter) sfExport( ".sfPars", local=FALSE, namespace="snowfall" ) ## Load libs using require as Exception on nodes doesn't help us here. ## @todo Check on correct execution via logical.return ## @todo Exporting of .sfPars needed? ## CHANGE FOR R-3: attribute 'keep-source' is removed. result <- try( sfClusterEval( do.call( "library", .sfPars ) ) ) # result <- try( sfClusterEval( do.call( "library", .sfPars ) ) ) if( inherits( result, "try-error" ) || ( length( result ) != sfCpus() ) || !all( checkTryErrorAny( result ) ) || !all( unlist( result ) ) ) { if( stopOnError ) stop( paste( "Stop: error loading library on slave(s):", .sfPars$package ) ) else { warning( paste( "Error loading library on slave(s):", package ) ) return( invisible( FALSE ) ) } } else { ## Load message in slave logs. sfCat( paste( "Library", .sfPars$package, "loaded.\n" ) ) ## Message in masterlog. message( paste( "Library", .sfPars$package, "loaded in cluster.\n" ) ) } } result <- try( do.call( "library", sfPars ) ) ## Remove global var from cluster (and local). ## Do before exception checks, as there might by a stop. sfRemove( ".sfPars" ) if( inherits( result, "try-error" ) || !result ) { if( stopOnError ) { warning( paste( "Unable to load library:", package ) ) return( invisible( FALSE ) ) } else stop( paste( "Unable to load library:", package ) ) } else { if( verbose ) message( paste( "Library", package, "loaded.\n" ) ) ## If logical return is requested here it comes. ## In clustermode the programm immidiately stops it a library couldn't be ## load on a slave. In sequentially mode it behaves like library(). return( invisible( TRUE ) ) } } ##***************************************************************************** ## Include a source file. ## @todo Include complete source() parameter list. ## ## Should behave most likely the build-in source() function. ## ## Running in sequential mode: normal "source()" command (relative path) ## Running in parallel mode: the sourcefile is loaded on ALL nodes ## (abs. path and no echo). ## ## PARAMETERS: filename ## RETURN: Logical true on success, false else. is stopOnError=TRUE => stop ##***************************************************************************** sfSource <- function( file, encoding = getOption("encoding"), stopOnError = TRUE ) { sfCheck(); absFile <- absFilePath( file ) if( file.exists( absFile ) ) { if( sfParallel() ) { ## Load source on all nodes (with globalized arguments) success <- sfClusterCall( source, file=absFile, encoding=encoding, echo=FALSE, local=FALSE, chdir=FALSE ) if( inherits( success, "try-error" ) ) { if( stopOnError ) stop( paste( "Try error on cluster source call: 'source ", absFile, "'", sep="" ) ) else { message( paste( "Try error on cluster source call: 'source ", absFile, "'", sep="" ) ) return( FALSE ) } } else { sfCat( paste( "Source", file, "loaded.\n" ) ) message( paste( "Source", file, "loaded in cluster.\n" ) ) } } ## Same as in sfLibrary(): include file on master as well (with ## original filename and echo setting). res <- try( source( file=absFile, encoding=encoding, echo=TRUE, local=FALSE, chdir=FALSE ) ) if( inherits( res, "try-error" ) ) { if( stopOnError ) stop( paste( "Try error loading on master: '", file, "'", sep="" ) ) else { message( paste( "Try error loading on master: '", file, "'", sep="" ) ) return( invisible( FALSE ) ) } } return( invisible( TRUE ) ) } ## File not found? else { if( stopOnError ) stop( paste( "File does not exist:", file ) ) else { message( paste( "File does not exist:", file ) ) return( invisible( FALSE ) ) } } } ##**************************************************************************** ## Export a single variable. ## On slaves, ALL exported variables are global! ## From the master, either global or local variables can be exported. ## ## PARAMETERS: ... - Names or Variables ## local - if TRUE, local vars will be exported ## namespace - also exports from namespace ## debug - with local=TRUE, prints where vars are found ## list - List of variable names (like snows clusterExport) ## RETURN: - ##**************************************************************************** sfExport <- function( ..., list=NULL, local=TRUE, namespace=NULL, debug=FALSE, stopOnError=TRUE ) { sfCheck(); ## Export is only done if running parallel, on master all vars are visible. ## @TODO: Although all vars are visible, they are not surely in global env! ## => export to global env. ## Test if global object of this name exists. If yes: warning, ## if not: assign in global space. if( !sfParallel() ) { warning( "sfExport() writes to global environment in sequential mode.\n" ) ## return( invisible( TRUE ) ) } ## List of given names in dot arguments. names <- fetchNames( ... ) ## If extra list is given (calling style of clusterExport()), just add this ## list. if( !is.null( list ) ) { ## Test from rm, see fetchNames for details. if( !length( list ) || !all( sapply( list, function(x) is.symbol(x) || is.character(x) ) ) ) { if( stopOnError ) stop( "'list' must contain names or character strings" ) else { warning( "Error in sfExport: 'list' must contain names or character strings" ) return( invisible( FALSE ) ) } } names <- c( names, list ) } for( name in names ) { ## Also examine namespace (from snowfall package?). Only needed for internal ## functions. if( !is.null( namespace ) && is.character( namespace ) ) { ## On some strange behavior, this only works with given error ## function. Else errors on not found objects are not caught. val <- tryCatch( getFromNamespace( name, namespace ), ##, pos=-1 error = function(x) { NULL } ) if( !is.null( val ) && !inherits( val, "try-error" ) ) { res <- sfClusterCall( assign, name, val, env = globalenv(), stopOnError = FALSE ) ## Error on export? if( is.null( res ) || !all( checkTryErrorAny( res ) ) ) { if( stopOnError ) stop( paste( "Error exporting '", name, "': ", geterrmessage(), sep="" ) ) else { warning( paste( "Error exporting '", name, "': ", geterrmessage(), sep="" ) ) return( invisible(FALSE) ) } } ## Skip local tests. next } } ## Check if exists before exporting. if( local ) { found <- FALSE # Traverse back through scopes (get() with inherit only finds # global presence of variables). # sys.nframe() at least 1 at this point, globalenv() to check last. for( pframe in seq( 1, sys.nframe() ) ) { ## Var exists in this frame? if( exists( name, inherits=FALSE, envir=sys.frame( -pframe ) ) ) { found <- TRUE ## If debug Messages are wanted, print these (especially for local ## mode with nested functions important, to locate probably ## overwriting variables. if( debug ) { definedIn <- gsub( "\n", "|", as.character( sys.call( -pframe ) ) ) cat( "Export '", name, "' defined in '", definedIn, "'", "\n", sep="" ) print( get( name, envir=sys.frame( -pframe ) ) ) } ## Export it. ## Direct call to assign is far slower as a call to a function ## doing it (however...) # res <- sfClusterCall( simpleAssign, name, # get( name, # envir=sys.frame( -pframe ) ), # stopOnError = FALSE ) ## <= 1.70 res <- sfClusterCall( assign, name, get( name, envir=sys.frame( -pframe ) ), env = globalenv(), stopOnError = FALSE ) ## Error on export? ## 1.84: object can be null if source variable was null, too. if( ( is.null( res ) && !is.null( get( name, envir=sys.frame( -pframe ) ) ) ) || !all( checkTryErrorAny( res ) ) ) { if( stopOnError ) stop( paste( "Error exporting '", name, "': ", geterrmessage(), sep="" ) ) else { message( paste( "Error exporting '", name, "': ", geterrmessage(), sep="" ) ) return( invisible(FALSE) ) } } break } } ## If variable to export is not found. if( !found ) { if( stopOnError ) stop( paste( "Unknown/unfound variable ", name, " in export. (local=", local, ")", sep="" ) ) else { message( paste( "Unknown/unfound variable ", name, " in export. (local=", local, ")", sep="" ) ) return( invisible( FALSE ) ) } } } ## Global export only. else { ## 1.84-3 typo if( exists( name, inherits=FALSE, envir=globalenv() ) ) { # res <- sfClusterCall( simpleAssign, name, # get( name, inherit=FALSE, # envir=globalenv() ), # stopOnError = FALSE ) ## <= 1.70 ## 1.84-3 typo res <- sfClusterCall( assign, name, get( name, inherits=FALSE, envir=globalenv() ), env = globalenv(), stopOnError = FALSE ) if( is.null( res ) || !all( checkTryErrorAny( res ) ) ) { if( stopOnError ) stop( paste( "Error exporting global '", name, "': ", geterrmessage(), sep="" ) ) else { warning( paste( "Error exporting global '", name, "': ", geterrmessage(), sep="" ) ) return( invisible( TRUE ) ) } } } else { if( stopOnError ) stop( paste( "Unknown variable ", name, " in export." ) ) else { warning( paste( "Unknown variable ", name, " in export." ) ) return( invisible( TRUE ) ) } } } } invisible( TRUE ) } ##**************************************************************************** ## Export all GLOBAL variables and functions to the whole cluster. ## Aware of memory usage. ## ## PARAMETERS: [Vector/List Names or variables NOT to export] ## RETURN: Logical Success ##**************************************************************************** sfExportAll <- function( except=NULL, debug=FALSE ) { sfCheck(); if( sfParallel() ) { ## Vector with all global variables. expList <- as.list( objects( pos = globalenv() ) ) ## Now remove all variables which are listed in list except from ## parameters. if( !is.null( except ) ) { if( is.list( except ) ) except <- unlist( except ) if( !is.vector( except ) ) { warning( "sfExportAll: except is not a vector.\n" ) return( invisible( FALSE ) ) } ## Remove those elements which are included in except. ## Reverse matches for correct indices after removing of ## single elements (start removing on right border). ## for( i in rev( match( except, expList ) ) ) ## expList <- expList[-i] ## Nicer version, proposal Greggory Jefferis (1.7.2) ## na.omit is not a must here though. expList <- expList[-na.omit(match(except, expList))] } ## Exporting mode with explicit global mode. sfExport( list=expList, local=FALSE ) if( debug ) { message( "sfExportAll: Following variables are exported:" ) message( paste( expList, collapse=", " ) ) } } else { message( "sfExportAll() ignored in sequential mode.\n" ) return( invisible( TRUE ) ) } invisible( TRUE ) } ##**************************************************************************** ## Remove objects from global environment (on whole cluster cluster) ## or at least from master (sequentially mode). ## ## PARAMETERS: List with Names(!) of the variables. ## RETURN: - ##**************************************************************************** sfRemove <- function( ..., list=NULL, master=FALSE, debug=FALSE ) { sfCheck(); ## List of given names in dot arguments. .sfNames <- fetchNames( ... ) ## If extra list is given (calling style of clusterExport()), just add this ## list. if( !is.null( list ) ) { ## Test from rm, see fetchNames for details. if( !length( list ) || !all( sapply( list, function(x) is.symbol(x) || is.character(x) ) ) ) stop( "list must contain names or character strings" ) .sfNames <- c( .sfNames, list ) } ## If running parallel, remove objects from slaves. if( sfParallel() ) { if( debug ) for( name in .sfNames ) cat( "REMOVE:", name, "\n" ) sfExport( ".sfNames", local=TRUE ) sfClusterEval( rm( list=.sfNames, pos=globalenv() ) ) sfClusterEval( rm( .sfNames, pos=globalenv() ) ) } ## Remove on master as well? if( master ) rm( list=.sfNames, pos=globalenv() ) invisible( NULL ) } ##**************************************************************************** ## Remove all variables from nodes (important: only global vars from nodes ## - NOT the master R process - are deleted). ## To delete on master as well, use sfRemove(). ## ## PARAMETERS: [Vector/List Names of objects NOT to remove]. ## RETURN: Boolean Success (invisible) ##**************************************************************************** sfRemoveAll <- function( except=NULL, debug=FALSE, hidden=TRUE ) { sfCheck(); if( sfParallel() ) { ## @TODO Also hidden vars? if( hidden ) sfTmpAll <- sfClusterEval( ls( pos=globalenv(), all.names=TRUE ) ) else sfTmpAll <- sfClusterEval( ls( pos=globalenv(), all.names=FALSE ) ) if( length( sfTmpAll ) == 0 ) { message( "sfRemoveAll: problems fetching variables from nodes (or none existant)...\n" ) return( invisible( FALSE ) ) } ## Only take result from one node. ## We assume all nodes have exactly the same variables in global space. ## It may be the case, that there are different variables on each node ## (like a node-routine writes different vars on different cases). ## Take that node with the most variables in object space. ## @todo: Merge result lists from all nodes. sfTmp <- sfTmpAll[[which.max(sapply(sfTmpAll,length))]] ## If there are any variables on nodes. if( length( sfTmp ) > 0 ) { ## Now remove all variables which are listed in list except from ## parameters. if( !is.null( except ) ) { if( is.list( except ) ) except <- unlist( except ) if( !is.vector( except ) ) { warning( "sfRemoveAll: except is not a vector.\n" ) return( invisible( FALSE ) ) } ## Remove those elements which are included in except. ## Not very elegant... However. ## Bugfix see sfExportAll for( i in match( except, sfTmp ) ) sfTmp[i] <- NA ## sfTmp <- sfTmp[-i] would fail for multiple removals ## Remove NAs sfTmp <- sort( sfTmp, na.last = NA ) } ## Create a new namespace vector (temporary). # setVar( ".sfTmpList", sfTmp ) if( debug ) { message( "sfRemoveAll: Remove variables from nodes:" ) message( paste( sfTmp, collapse=", " ) ) } ## Export the list to cluster. sfExport( "sfTmp", local=TRUE ) ## Delete all variables in the list. sfClusterEval( rm( list=sfTmp, pos=globalenv() ) ) sfClusterEval( rm( "sfTmp", pos=globalenv() ) ) } else { message( "sfRemoveAll: no variables on nodes.\n" ) return( invisible( FALSE ) ) } return( invisible( TRUE ) ) } ## In sequential mode nothing is done and it counts as success ;) else { message( "sfRemoveAll() ignored in sequential mode.\n" ) return( invisible( TRUE ) ) } } ##**************************************************************************** ## Fast messages on the cluster (all nodes!). ## For testing proposes mainly. ## ## PARAMETER: Vector x Objects to print, ## String sep Separator ## Boolean master Print on master as well ##**************************************************************************** sfCat <- function( ..., sep=" ", master=TRUE ) { sfCheck(); .sfTmpX <- c( ... ) .sfTmpSEP <- sep if( length( .sfTmpX ) == 0 ) return( invisible( NULL ) ) ## ...it's unbelievable... if( sfParallel() ) { sfExport( ".sfTmpSEP", ".sfTmpX", local=TRUE ) sfClusterCall( cat, .sfTmpX, sep=.sfTmpSEP ) sfRemove( ".sfTmpX", ".sfTmpSEP", master=FALSE ) } ## Master&sequential mode. if( master ) cat( .sfTmpX, sep=.sfTmpSEP ) invisible( TRUE ) } ##**************************************************************************** ## Mainly an parallised lapply with intermediate result savings and restore ## on later callback. ## Resultfiles are saved on each step, where a step is defined by the amount ## of CPUs given (e.g. 4 cpus, 100 steps => 25 savings). ## ## Characteristic of the called functions: they are not allowed to return NULL ## values, as these indicate uncalculated potions in the result file. Please ## use NA or any other marker for undefined values. ## ## Files are saved under directory .sfOption$RESTDIR with the form: ## SAVE_file_name ## ## where file : name of current R-file or "DEFAULT" in interactive mode ## name : usergiven name for this current calculation (default: "default") ## If a program uses more than one call to sfClusterApplySR(), ## then name MUST be set! ## ## As this function itself calls sfLappy(), there is no explicit sequential form ## here. ## ## To disable printing of progress, set perupdate to 100. ## ## PARAMETERS: List x, \ Like lapply ## Function fun, | ## ... / ## [String name Name for this call of sfClusterApplySR], ## [perupdate int Percent Update frequency for process report], ## [Logical restore: restore previous results or don't restore] ## RETURN: List ##**************************************************************************** sfClusterApplySR <- function( x, fun, ..., name="default", perUpdate=NULL, restore=sfRestore() ) { sfCheck(); checkFunction( fun ) ## If none or no regular update frequency is given. if( is.null( perUpdate ) || !is.numeric( perUpdate ) || ( perUpdate < 0 ) || ( perUpdate > 100 ) ) perUpdate <- .sfOption$RESTOREUPDATE ## Ensure destination directory's existing, if not: create. if( !file.exists( .sfOption$RESTDIR ) ) dirCreateStop( .sfOption$RESTDIR ) ## No R-file given? if( is.null( .sfOption$CURRENT ) ) setOption( "CURRENT", "DEFAULT" ) ## Abs. file path file <- file.path( .sfOption$RESTDIR, paste( "SAVE_", .sfOption$CURRENT, "_", name, sep="" ) ) ## Mark this file for deletion on (regular) cluster stop - even if the file ## itself does not exist atm. addRestoreFile( file ) ## Resultfile is present: try to load it, check if variable result is included ## and check how many results are present in the file. ## If it seems that the results are ok, take them and continue at their end. if( file.exists( file ) && restore ) { ## Temp global var for saving possible loading errors (in namespace). setVar( ".sfLoadError", "" ) ## Load in current environment. tryCatch( load( file ), error=function( x ) { setVar( ".sfLoadError", x ) } ) if( .sfLoadError != "" ) stop( paste( "Loading error:", .sfLoadError ) ) cat( "Restoring previous made results from file:", file, "\n" ) errMsg <- "\nPlease remove file manually.\n" ## First check the contents of the file. ## If these don't match, do NOT remove file or overwrite it automatically, ## as (due to the weak filenames) mistakes could be done. ## Commit removal to user (with message). ## Variable "result" is loaded? if( length( ls( pattern="^result$" ) ) == 0 ) stop( paste( "Result variable not found in datafile:", file, errMsg ) ) ## Check if variable result is present. if( !is.list( result ) ) stop( paste( "Variable result is no list in datafile:", file, errMsg ) ) ## Check if variable result has correct length. if( length( result ) != length( x ) ) stop( paste( "Variable result from resultfile has different length to data:", length( result ), "<->", length( x ), errMsg ) ) ## Set marker to NA. startIndex <- NA ## Fetch the last non-NULL value in result (which declares the last result ## value which does not have to be recalculated). for( index in seq( length( result ), 1 ) ) { if( !is.null( result[[index]] ) ) { ## Flip to first NULL value => means the first unprocessed value. startIndex <- index + 1 break } } ## Complete unprocessed resultset found? Then start at first element. if( is.na( startIndex ) ) { startIndex <- 1 perCent <- 0 } ## At least some parts in the resultset are given. else { ## Complete processed result? Can happen in programs with more than one ## parallised call. if( startIndex >= length( result ) ) { return( result ) } ## Message for user where restore begins. perCent <- ( ( startIndex - 1 ) * 100 ) / length( result ) cat( "Starting calculation at ", round( perCent, 1 ), "% (", startIndex, "/", length( result ), ")\n" ) } } ## No resultfile given/present: generate clear result with all NULL fields. else { if( !restore ) message( "Restore is not active! No results are loaded." ) message( paste( "Saving results to: ", file, "\n" ) ) ## Resultlist: init with NULL in any element. result <- lapply( 1:length( x ), function( x ) NULL ) startIndex <- 1 # Start at the beginning perCent <- 0 # Nothing done yet } lastPrintPercent <- 0 ## Calculating list parts in cluster. for( sIndex in seq( startIndex, length( x ), by=sfCpus() ) ) { ## Endindex. eIndex <- sIndex + sfCpus() - 1 ## End out of bounds? if( eIndex > length( x ) ) eIndex <- length( x ) ## cat( "Calculating Indizes: ", sIndex, eIndex, "\n" ) newResult <- sfLapply( x[sIndex:eIndex], fun, ... ) ## Fill cells with new results. result[sIndex:eIndex] <- newResult[1:length( newResult )] ## Intermediate save of current results. save( result, file=file ) ## Calculated percentage. perCent <- eIndex * 100 / length( result ) ## If message about process is wanted, print it (also is a connector for ## sfCluster to show the calculation process). ## Also catch the case where mod rarely matches (if amount of CPUs is ## bigger than perUpdate). if( ( ( round( perCent, 0 ) - round( sIndex * 100 / length( result ), 0 ) ) >= perUpdate ) || ( ( ( round( perCent, 0 ) %% perUpdate ) == 0 ) && ( ( round( perCent, 0 ) - lastPrintPercent ) >= perUpdate ) ) ) { cat( "SR '", name, "' processed: ", round( perCent, 1 ), "%\n", sep="" ) lastPrintPercent <- round( perCent, 0 ) } ## cat( "Finished Indizes: ", sIndex, eIndex, "\n" ) } return( result ) } ##**************************************************************************** ## Complete "unit test" or most of the buildin functions. ## Mainly integrated for development, but can be used for testing the ## R functionality on all nodes, too. ## ## PARAMETER: - ## RETURN: Int amount of errors (0: everything is ok). ##**************************************************************************** sfTest <- function() { sfCheck(); if( !sfParallel() ) { message( "Tests only work in parallel mode." ) return( invisible( FALSE ) ) } ##*************************************************************************** ## Basic checks for Calls/Evals. ##*************************************************************************** checkResultBasic <- function( result ) { if( is.null( result ) ) return( c( FALSE, "Result was NULL" ) ) if( !is.list( result ) ) return( c( FALSE, "No proper return type (no list)." ) ) if( length( result ) != sfCpus() ) return( c( FALSE, "No proper return type (wrong length)." ) ) if( inherits( result, "try-error" ) ) return( c( FALSE, "TRY-ERROR raised on result." ) ) if( !all( sapply( result, function( x ) if( inherits( x, "try-error" ) ) return( FALSE ) else return( TRUE ) ) ) ) return( c( FALSE, "Result elements raised TRY-ERROR(s)." ) ) return( c( TRUE, "" ) ) } ##*************************************************************************** ## Checks if each element of a given list is equal to a certain value != ## NA/NULL). ##*************************************************************************** checkAllEqual <- function( result, equal ) { if( !all( sapply( unlist( result ), function( x ) return( x == equal ) ) ) ) return( FALSE ) return( TRUE ) } ##*************************************************************************** ## Test a list of lists against a vector (each sublist must be equal to the ## given list). ##*************************************************************************** checkAllEqualList <- function( result, equal ) { if( is.list( equal ) ) equal <- sort( unlist( equal ) ) else equal <- sort( equal ) for( res in result ) { ## res <- sort( res ) if( ( length( res ) != length( equal ) ) || ( length( which( sort( res ) == equal ) ) < length( equal ) ) ) return( FALSE ) ## i <- 1 ## while( i <= length( res ) ) { ## if( res[i] != equal[i] ) ## return( FALSE ) ## i <- i + 1 ## } } return( TRUE ) } ##*************************************************************************** ## Compare vectors. ##*************************************************************************** checkVecCmp <- function( x, y ) { if( length( x ) != length( y ) ) return( FALSE ) for( i in seq( 1, length( x ) ) ) { ## If NULL or NA, nothing is to compare. But only if both vals are NA/NULL ## If not, compare (throws exception/stop) if( ( is.na( x[i] ) && is.na( y[i] ) ) || ( is.null( x[i] ) && is.null( y[i] ) ) ) next if( x[i] != y[i] ) return( FALSE ) } return( TRUE ) } ##*************************************************************************** ## Testing sfLibrary. ##*************************************************************************** testLib <- function() { ## Package always be installed. if( !sfLibrary( "boot", character.only=TRUE, stopOnError=FALSE ) ) return( c( FALSE, "Unable to load library 'tools'" ) ) ## calcium is a dataframe. result <- sfClusterEval( as.matrix( get('calcium') )[,2] ) ## Compare if all nodes delivered the same data for variable "calcium" ## get needed to avoid R CMD check warnings. for( res in result ) if( !checkVecCmp( res, as.matrix( get( "calcium" ) )[,2] ) ) return( c( FALSE, "Wrong data delivered..." ) ) ## Load surely uninstalled package to test if lib call fail safely. if( try( sfLibrary( "xxxyyyzzz", character.only=TRUE, stopOnError=FALSE ), silent=TRUE ) ) return( c( FALSE, "Irregular return on loading inexisting library." ) ) return( c( TRUE, "ok" ) ) } ##*************************************************************************** ## testing sfSource. ##*************************************************************************** testSource <- function() { sfRemoveAll() ## Find path of the installed snowfall Package. res <- NULL res <- try( find.package( "snowfall" ) ) ## CHG 131712 from .find.package if( inherits( res, "try-error" ) ) return( c( FALSE, paste( "Exception: cannot locate package snowfall.", geterrmessage() ) ) ) if( is.null( res ) ) return( c( FALSE, "Cannot locate package snowfall." ) ) res <- file.path( res, "data", "test.R" ) cat( "PACKAGE...: ", res, "\n" ) con <- file( res, "r", blocking=FALSE ) a <- readLines( con, n=-1 ) debug( "test.R content:" ) debug( a ) result <- sfSource( res, stopOnError=FALSE ) if( inherits( result, "try-error" ) ) return( c( FALSE, paste( "Exception: cannot source on slaves.", geterrmessage() ) ) ) ## get to satisfy R CMD check result <- sfClusterEval( get("f1")(), stopOnError=FALSE ) resBasic <- checkResultBasic( result ) if( resBasic[1] == FALSE ) return( resBasic ) if( !checkAllEqual( result, 999 ) ) return( c( FALSE, "Wrong results on sourced function f1." ) ) ## get to satisfy R CMD check result <- sfClusterEval( get("f2")( 99, 1 ), stopOnError=FALSE ) resBasic <- checkResultBasic( result ) if( resBasic[1] == FALSE ) return( resBasic ) if( !checkAllEqual( result, 100 ) ) return( c( FALSE, "Wrong results on sourced function f2." ) ) return( c( TRUE, "ok" ) ) } ##*************************************************************************** ## Testing sfClusterCall. Allways first test. ##*************************************************************************** testCall <- function() { # Test 1 on Call result <- sfClusterCall( paste, "a", "b", "c", sep="", stopOnError=FALSE ) resBasic <- checkResultBasic( result ) if( resBasic[1] == FALSE ) return( resBasic ) if( !checkAllEqual( result, "abc" ) ) return( c( FALSE, "Wrong results on paste." ) ) # Test 2 on Call sums <- c( 99, 7, 3.4 ) result <- sfClusterCall( sum, sums, stopOnError=FALSE ) if( !checkAllEqual( result, sum( sums ) ) ) return( c( FALSE, "Wrong result on sum." ) ) return( c( TRUE, "ok" ) ) } ##*************************************************************************** ## Testing sfClusterEval ##*************************************************************************** testEval <- function() { # Test 1 on Eval result <- sfClusterEval( sum( sapply( 1:10, exp ) ) ) resBasic <- checkResultBasic( result ) if( resBasic[1] == FALSE ) return( resBasic ) if( !checkAllEqual( result, sum( sapply( 1:10, exp ) ) ) ) return( c( FALSE, "Wrong results on sum." ) ) return( c( TRUE, "ok" ) ) } ##*************************************************************************** ## Testing Export Funktion ##*************************************************************************** ## testExport <- function() { ## ## Needed to have a clean comparison global env. ## sfRemoveAll( hidden=TRUE ) ## ## vars <- sfClusterEval( ls( all.names=TRUE, envir=globalenv() ) ) ## ## print( vars ) ## ## if( length( vars ) != 0 ) ## if( !all( sapply( vars, ## function( x ) return( length( x ) == 0 ) ) ) ) ## return( c( FALSE, "sfRemoveAll() didn't kill everything" ) ) ## ## Setting global variable via assign, as <<- invokes warnings on ## ## package check. ## assign( "var1", 99, pos=globalenv() ) ## assign( "var2", 101, pos=globalenv() ) ## # var1 <<- 99 # Global ## # var2 <<- 101 ## var3 <- 103 # Local ## var4 <- 7 ## ## Setting var in namespace ("snowfall"). ## setVar( ".sfTestVar5", 77 ) ## if( getVar( ".sfTestVar5" ) != 77 ) ## return( c( FALSE, "Access to namespace failed." ) ) ## iTest <- function() { ## var3 <- 88 ## res <- FALSE ## res <- sfExport( "var1", "var2", ".sfTestVar5", ## list=list( "var3", "var4" ), ## local=TRUE, namespace="snowfall", stopOnError=FALSE ) ## if( inherits( res, "try-error" ) ) ## return( c( FALSE, "Exception on export." ) ) ## if( !res ) ## return( c( FALSE, "Unexpected Exception on export." ) ) ## print( "GLOBALENV..." ) ## print( sfClusterCall( ls, envir=globalenv() ) ) ## if( !checkAllEqualList( sfClusterCall( ls, all.names=TRUE, ## envir=globalenv() ), ## c( "var1", "var3", "var2", "var4", ## ".sfTestVar5" ) ) ) ## return( c( FALSE, "Not all vars exported." ) ) ## ## get to satisfy R CMD check ## if( !checkAllEqual( sfClusterEval( get("var1") ), 99 ) || ## !checkAllEqual( sfClusterEval( get("var2") ), 101 ) ) ## return( c( FALSE, "Error exporting global var." ) ) ## ## get to satisfy R CMD check ## if( !checkAllEqual( sfClusterEval( get("var3") ), 88 ) || ## !checkAllEqual( sfClusterEval( get("var4") ), 7 ) ) ## return( c( FALSE, "Error exporting local var." ) ) ## if( !checkAllEqual( sfClusterEval( get(".sfTestVar5") ), 77 ) ) ## return( c( FALSE, "Error exporting namespace var." ) ) ## ## Test removeAll with Exception-List ## sfRemoveAll( except=list( "var2", "var3" ) ) ## if( !checkAllEqualList( sfClusterCall( ls, envir=globalenv() ), ## list( "var2", "var3" ) ) ) ## return( c( FALSE, "Error on removeAll except-list." ) ) ## sfRemoveAll() ## return( c( TRUE, "ok" ) ) ## } ## return( iTest() ) ## } ##*************************************************************************** ## Testing Calculation Function Part 1 ##*************************************************************************** testCalc1 <- function() { size <- 50 mat <- matrix( 0, size, size ) for( var in 1:nrow( mat ) ) mat[var,] = runif( nrow( mat ) ) rSum <- function( row, mat ) { s <- 0 for( col in 1:ncol( mat ) ) s <- s + mat[row,col] return( s ) } cmp <- unlist( lapply( seq( 1, ncol( mat ) ), rSum, mat ) ) sfExport( "rSum", local=TRUE, debug=TRUE ) # Test 1 on Eval result <- sfLapply( seq( 1, ncol( mat ) ), rSum, mat ) ## cat( "FINISHED...\n" ) ## print( result ) if( !checkVecCmp( unlist( result ), cmp ) ) return( c( FALSE, "Wrong results on sfLapply." ) ) ## Testing sfClusterApplyLB result <- sfClusterApplyLB( seq( 1, ncol( mat ) ), rSum, mat ) if( !checkVecCmp( unlist( result ), cmp ) ) return( c( FALSE, "Wrong results on sfClusterApplyLB." ) ) ## Testing sfClusterApplySR result <- sfClusterApplySR( seq( 1, ncol( mat ) ), rSum, mat, name="TEST", restore=FALSE, perUpdate=100 ) if( !checkVecCmp( unlist( result ), cmp ) ) return( c( FALSE, "Wrong results on sfClusterApplySR." ) ) ## As clusterApply only works for #nodes samples, reduce data size depending ## on it. result <- sfClusterApply( seq( 1, min( sfCpus(), ncol( mat ) ) ), rSum, mat ) if( !checkVecCmp( unlist( result ), unlist( lapply( seq( 1, min( sfCpus(), ncol( mat ) ) ), rSum, mat ) ) ) ) return( c( FALSE, "Wrong results on sfClusterLapply." ) ) return( c( TRUE, "ok" ) ) } ##*************************************************************************** ## Testing Calculation Function Part 2 ## Further snow Wrappers. ##*************************************************************************** testCalc2 <- function() { size <- 50 mat1 <- matrix( 0, size, size ) mat2 <- matrix( 0, size, size ) for( var in 1:nrow( mat1 ) ) mat1[var,] = runif( nrow( mat1 ) ) for( var in 1:nrow( mat2 ) ) mat2[var,] = runif( nrow( mat2 ) ) matRes1 <- sfMM( mat1, mat2 ) matRes2 <- mat1 %*% mat2 for( row in seq( 1, size ) ) if( !checkVecCmp( matRes1[row,], matRes2[row,] ) ) return( c( FALSE, "Wrong results on sfParMM." ) ) return( c( TRUE, "ok" ) ) } ##*************************************************************************** ## Run single test (with given functionname) ##*************************************************************************** runTest <- function( fun ) { cat( "Run test: ", fun, "\n" ) res <- c( NA, "" ) res <- try( do.call( fun, list() ) ) if( inherits( res, "try-error" ) ) return( c( FALSE, paste( "TRY-ERROR on testCall()", geterrmessage() ) ) ) if( is.null( res ) || !is.vector( res ) || is.na( res[1] ) ) return( c( FALSE, paste( "Hidden exception on test.", geterrmessage() ) ) ) return( res ) } complete <- list( errors=0, warnings=0 ) ## @todo - Bibliotheken / Source ## @todo - anderen Applies / parMM ## @todo - exportAll ## testExport removed because of a R 3.0.0 warning (not error!) tests <- c( "testCall", "testEval", ## "testExport", "testCalc1", "testCalc2", "testLib", "testSource" ) ## Run tests. for( test in tests ) complete[[test]] <- runTest( test ) ## Print results. cat( "\n\nRESULTS ON TEST:\n\n" ) errors <- 0 for( test in tests ) { ## Align names to same length. if( as.logical( complete[[test]][1] ) ) cat( test, sapply( seq( 1, 13 - nchar( test ) ), function( x ) return( " " ) ), ": ok", "\n", sep="" ) else { cat( test, sapply( seq( 1, 13 - nchar( test ) ), function( x ) return( " " ) ), ": FAILED! (", complete[[test]][2], ")\n", sep="" ) errors <- errors + 1 } } cat( "\n----------------------------\n", errors, "tests failed.\n\n" ) return( invisible( errors ) ) } snowfall/R/sysdata.rda0000644000175100001440000000054312607465525014474 0ustar hornikusersQN0lĨo~]"ȓ!2ZFM] V\{ν9=7̯6x{xOj@Kߍ]*'L\+NFIXTQrAy"/(*.6b\GHfl# RqahEu01ai|+ph ^^b? L>gVK:ghLd'֎(Zd:-ϣA<0')ZQ^h}j?`)x+~:m FV a5HY*vr傪;fd+snowfall/R/init.R0000644000175100001440000010020612607454156013415 0ustar hornikusers##***************************************************************************** ## Function for initialisizing the Cluster. ## ## Also the predefinition of (internal) global variables is done here, ## mainly because of the code check of R. ## ## Compability issue: snowfall needs to know if sfCluster is working (also ## old versions of sfCluster). ## --session could be set by other solutions. ## So, --lockfile is decided to be the sfCluster indicator, as this most ## likely will have no use if not used with sfCluster. ## Therefore setting of --lockfile (LOCKFILE) can cause troubles. ##***************************************************************************** ## These variables are used by internal function. Need to declared for ## the compiler warnings. As these cannot be altered directly, setOption() does ## this (in the namespace). So no global objects are set, internal state is ## kept inside the namespace. DEBUG <- FALSE ## Static switch for debugging messages .sfOption <- list() ## Configuration of this master .sfPresetCPUs <- 0 ## Presetted CPU amount (max. allocatable). ## Some vars needed at specific points, which can handled correctly otherwise, ## but would raise R CMD check warnings if not defined before. .sfPars <- '' ## Tmp. var for sfLibrary. .sfLoadError <- '' ## Tmp. var for loading. .sfTestVar5 <- 0 ## Exporting test in sfTest(). ##***************************************************************************** ## Function for initialisizing the Cluster. ## ## Attention: this package does nasty things with explicit sideeffects (not ## only using "require" and "options" etc.)... ## ## "Nodes" and "CPUs" are used identical (unbeautiful). ## ## PARAMETER: [Boolean parallel - overwrite CMDline settings], ## [Int nodes - overwrites commandline settings / DEPRECATED] ## [Int cpus - overwrites commandline settings] ## [Boolean nostart - If set, no cluster start will be run. ## Needed for nested usage of snowfall] ## [Boolean restore - Globally set restore] ## [String type - {'MPI','SOCK', 'PVM', 'NWS'} ## [Vector socketHosts - List of all hosts used in socketmode] ## [slaveOutfile - filename for output on slaves] ## [useRscript - Startup via R Script or shellscript. Only snow>0.3] ## RETURN: Boolean TRUE ##***************************************************************************** sfInit <- function( parallel=NULL, cpus=NULL, type=NULL, socketHosts=NULL, restore=NULL, slaveOutfile=NULL, nostart=FALSE, useRscript=FALSE ## snow: Default is TRUE. ) { ## Flag for detection of reconnect (means: non-first calls to sfInit()) reconnect <- FALSE ## Get rid of that stupid data load to global env. initEnv <- new.env() ## Saves users from many own if-clauses probably. if( nostart ) return( TRUE ) ## Are options setted? if( length( .sfOption ) == 0 ) { debug( "Setup sfOption..." ) ## Add 1.62: list from sysdata cleared and created again setOption( "parallel", FALSE ) setOption( "session", NULL ) setOption( "priority", 1 ) setOption( "nodes", 1 ) setOption( "stopped", FALSE ) setOption( "init", FALSE ) ## Load configuration file: delivered with package and changeable by user. data( "config", package="snowfall", envir=initEnv ) configM <- as.matrix( t( config ) ) config <- as.list( configM ) names( config ) <- dimnames( configM )[[2]] ## Node count are limited in snowfall as well (as it is useable without ## sfCluster) and you probably don't want an arbitrary amount of CPUs ## requested by a DAU. ## If changed preset exists, take this number. if( .sfPresetCPUs > 0 ) setOption( "MAXNODES", .sfPresetCPUs ) else setOption( "MAXNODES", as.numeric( config[["MAXNODES"]] ) ) ## Startup lockfile (only coming from sfCluster and if available ## signalling that snowfall is started through sfCluster). ## LOCKFILE can only be set through commandline --lockfile setOption( "LOCKFILE", "" ) ## Temporary directory (for logfiles, esp. on the slaves) ## Only if set, if not, take default. if( as.character( config[["TMPDIR"]] ) != "-" ) setOption( "TMPDIR", path.expand( as.character( config[["TMPDIR"]] ) ) ) else { ## Default tempdir on Unix systems is R session tempdir if( .Platform$OS.type == "unix" ) setOption( "TMPDIR", file.path( Sys.getenv( "R_SESSION_TMPDIR" ), "sfCluster" ) ) ## On any non *nix system: take local dir (R_SESSION_TMPDIR unset on Win) else setOption( "TMPDIR", "" ) } ## Addition variables for save/restore (only used in sfClusterApplySR). setOption( "RESTOREFILES", NULL ) ## List with restore files (for cleanup) setOption( "RESTOREUPDATE", 5 ) ## Updates percent output any 5% setOption( "RESTORE", FALSE ) ## Restore previous results? setOption( "CURRENT", NULL ) ## Currently executed R-File ## Default cluster type (unchangeable by config to ensure runnability ## of a specific code in any setting). setOption( "type", "SOCK" ) setOption( "sockHosts", NULL ) ## Restore file directory (for saved intermediate results) - not neccessary ## under/in TMPDIR. ## (As log files prob woul be set in global dir, restore files should be ## stored under users home - as they don't contain a session-ID or something ## generic unique thing to differ them. if( as.character( config[["RESTDIR"]] ) != "-" ) setOption( "RESTDIR", path.expand( as.character( config[["RESTDIR"]] ) ) ) else setOption( "RESTDIR", file.path( Sys.getenv( "HOME" ), ".sfCluster", "restore" ) ) ## Remove config (as data() writes it as global variable). rm( config, envir=initEnv ) #pos=globalenv() ) } ## If .sfOption exists, sfInit() was called before: restart. ## (sfCluster should be able to handle this - although slaves are iterated and ## slave killing is only done through snow). else { reconnect <- TRUE if( .sfOption$stopped && !.sfOption$init ) debug( "Irregluar init state (error on previous init)..." ) ## If not stopped, but initialised. if( !.sfOption$stopped && .sfOption$init ) { message( "Explicit sfStop() is missing: stop now." ) sfStop() } } ##************************************************************************** ## Values for parallel/session can be in the commandline or the environment. ## Function parameters overwrite commandline. ##************************************************************************** searchCommandline( parallel, cpus=cpus, type=type, socketHosts=socketHosts, restore=restore ) if( getOption( 'verbose' ) && !reconnect ) print( .sfOption ) ## If given restore-directory does not exist, create it. ## if( !file.exists( .sfOption$RESTDIR ) ) { ## ## 1.62: removed ## ## .sfOption$RESTDIR <<- path.expand( "~/.sfCluster/restore" ) ## dirCreateStop( .sfOption$RESTDIR ) ## } ## Running in parallel mode? That means: Cluster setup. ## Will be blocked if argument "nostart" is set (for usage of snowfall ## inside of packages). if( .sfOption$parallel && !nostart ) { ## Internal stopper. Running in parallel mode a session-ID is needed. ## For testing purposes can be anything (mainly used for pathnames ## of logfiles). if( startedWithSfCluster() && is.null( .sfOption$session ) ) stop( "No session-ID but parallel run with sfCluster (something went wrong here?)..." ) ## @TODO regenerate session id if missing. ## If amount of nodes not set via commandline, then it will be 2 if( is.null( .sfOption$nodes ) || is.na( as.numeric( .sfOption$nodes ) ) ) setOption( "nodes", 2 ) else setOption( "nodes", as.numeric( .sfOption$nodes ) ) ## Preload required libraries if needed (as an extended error check). libList <- list( "PVM"="rpvm", "MPI"="Rmpi", "NWS"="nws", "SOCK"="" ) if( libList[[.sfOption$type]] != "" ) { if( !require( libList[[.sfOption$type]], character.only=TRUE ) ) { message( paste( "Failed to load required library:", libList[[.sfOption$type]], "for parallel mode", .sfOption$type, "\nFallback to sequential execution" ) ) ## Fallback to sequential mode. return( sfInit( parallel=FALSE ) ) } else message( paste( "Library", libList[[.sfOption$type]], "loaded." ) ) } ## In any parallel mode, load snow if needed. ## CHG 131217: not needed because of package depends. ## if( !require( snow ) ) { ## message( paste( "Failed to load library 'snow' required for parallel mode.\n", ## "Switching to sequential mode (1 cpu only)!." ) ); ## ## Fallback to sequential mode. ## return( sfInit( parallel=FALSE ) ) ## } ## Chg. 1.62 ## Temporary file for output. ## If sfCluster is running (LOCKFILE given): session is taken. ## If sfCluster not running but user setted slaveOutfile option: take arg. ## Else (default): no slave outfiles (writing to /dev/null|nul). if( startedWithSfCluster() ) { tmp <- file.path( .sfOption$TMPDIR, paste( "rout_", .sfOption$session, sep="" ) ) ## Only create temporary directory once and if needed. ## Only needed if running with sfCluster. If user sets it's own ## slaveOutfile, he has to ensure himself about existing pathes. ## If needed create temporary path. Problem: this is executed only on ## master, not on slaves. The clusterstarter needs to manage this. if( !reconnect ) dirCreateStop( .sfOption$TMPDIR ) } else tmp <- ifelse( is.null( slaveOutfile ), '/dev/null', slaveOutfile ) ## @TODO Exception handler. ## @TODO Timeout on init. ## Ebenso: Timeout - das ist extrem hsslich, wenn das Cluster nicht ## korrekt startet und hngen bleibt (z.B. wenn zuviele CPUs fr das ## Cluster angefordert werden - was PVM schluckt, macht MPI anscheinend ## Kopfzerbrechen). setDefaultClusterOptions( type = .sfOption$type ) setDefaultClusterOptions( homogenous = FALSE ) ## On socket connections the list of hosts needs to be given. ## If no is set, use localhost with default R. if( .sfOption$type == "SOCK" ) { ## No host information given: use localhost with wished CPUs. ## Else: host settings overwrite wished CPUs (important for error checks!). if( is.null( .sfOption$sockHosts ) || ( length( .sfOption$sockHosts ) == 0 ) ) setOption( "sockHosts", c( rep( "localhost", .sfOption$nodes ) ) ) else setOption( "nodes", length( .sfOption$sockHosts ) ) setOption( "cluster", try( makeCluster( .sfOption$sockHosts, type = "SOCK", outfile = tmp, homogenous = TRUE ) ) ) } # PVM cluster else if( .sfOption$type == "PVM" ) { setOption( "cluster", try( makeCluster( .sfOption$nodes, outfile = tmp ) ) ) } # Network Spaces else if( .sfOption$type == "NWS" ) { if( is.null( .sfOption$sockHosts ) || ( length( .sfOption$sockHosts ) == 0 ) ) setOption( "sockHosts", c( rep( "localhost", .sfOption$nodes ) ) ) else setOption( "nodes", length( .sfOption$sockHosts ) ) ## Patch Markus Schmidberger (Mail 11/25/2008). setOption( "cluster", try( makeNWScluster( .sfOption$sockHosts[1:.sfOption$nodes], type = "NWS", outfile = tmp ) ) ) } # MPI cluster (also default for irregular type). else { ## 1.81: useRScript must be FALSE. Else sfCluster wont work ## with snow > 0.3 (on older snow Versions this option ## is ignored. Also homogenous is always on. ## 1.83: But for non-sfCluster usage at least it has to be modifyable. setOption( "cluster", try( makeMPIcluster( .sfOption$nodes, outfile = tmp, homogenous = TRUE, useRscript = useRscript ) ) ) } ## Startup successfull? If not: stop. if( is.null( .sfOption$cluster ) || inherits( .sfOption$cluster, "try-error" ) ) stop( paste( "Starting of snow cluster failed!", geterrmessage(), .sfOption$cluster ) ) ## Cluster setup finished. Set flag (used in error handlers and stop). ## Also: no function can be called if init is not set. setOption( "init", TRUE ) setOption( "stopped", FALSE ) if( !reconnect ) { ## As Snow Init spawn all the requires R-processes, the proprietary ## lockfile can be deleted now (if it exists). ## Problem: now all R procs are spawned, but the observer most ## likely didn't catch them until the next time of his observing ## loop. if( !is.null( .sfOption$LOCKFILE ) && file.exists( .sfOption$LOCKFILE ) ) { if( unlink( .sfOption$LOCKFILE ) != 0 ) warning( "Unable to remove startup lockfile: ", .sfOption$LOCKFILE ) else message( "Startup Lockfile removed: ", .sfOption$LOCKFILE ) } if( getOption( 'verbose' ) ) { if( tmp == '/dev/null' ) message( "Slave output suppressed. Use 'slaveOutfile' to activate." ) else message( paste( "Temporary log for STDOUT/STDERR (on each node): ", tmp, "\n", "Cluster started with", .sfOption$nodes, "CPUs.", "\n" ) ) } else debug( paste( "Temporary log for STDOUT/STDERR (on each node): ", tmp, "\n", "Cluster started with", .sfOption$nodes, "CPUs.", "\n" ) ) ## Write R-Version and Time in (slave-)logfiles. .startInfo <- strsplit( Sys.info(), "\n" ); .startMsg <- paste( sep="", "JOB STARTED AT ", date(), # Global Var! " ON ", .startInfo$nodename, " (OS", .startInfo$sysname, ") ", .startInfo$release, "\n" ) sfExport( ".sfOption", ".startMsg", local=TRUE, namespace="snowfall", debug=DEBUG ) sfCat( .startMsg, "\n", master=FALSE ) ## No master sfCat( paste( "R Version: ", R.version$version.string, "\n\n" ) ) ## Remove starting message. sfRemove( ".startMsg" ) } ## @TODO Checken, ob dieser Export wirklich noch bentigt ist (jan 10) else sfExport( ".sfOption", local=FALSE, namespace="snowfall" ) } ## Sequential mode or option "nostart": ## init will be set. If someone calls sfInit with nostart and aims ## it to be started, it's his or her problem. else { ## Cluster setup finished. Set flag (used in error handlers and stop). ## Also: no function can be called if init is not set. setOption( "init", TRUE ) setOption( "stopped", FALSE ) setOption( "cluster", NULL ) } ## Print init Message (esp. print State of parallel and snowfall ## version. if( sfParallel() ) { message( paste( "snowfall ", packageDescription( "snowfall" )$Version, " initialized (using snow ", packageDescription( "snow" )$Version, "): parallel execution on ", sfCpus(), " CPUs.\n", sep="" ) ); } else { message( paste( "snowfall", packageDescription( "snowfall" )$Version, "initialized: sequential execution, one CPU.\n" ) ); } return( invisible( TRUE ) ) } ##***************************************************************************** ## Check if sfInit() was called. ## This function is called before any function which need initialised cluster. ## ## Previous it stops with error, now it calls sfInit() without parameters, ## so sfInit() does not have to be called explicitely (requested from Harald). ## ## (Not exported to namespace). ##***************************************************************************** sfCheck <- function() { if( !sfIsRunning() ) { message( paste( "Calling a snowfall function without calling 'sfInit'", "first or after sfStop().\n'sfInit()' is called now." ) ) return( invisible( sfInit() ) ) } return( invisible( TRUE ) ) } ##***************************************************************************** ## Exported as userfunction. ## Give the user information if sfInit() was called and cluster is not stopped. ## (Maybe helpful inside of packages etc.). ##***************************************************************************** sfIsRunning <- function() { ## Add 1.62: stopped as argument if( ( length( .sfOption ) == 0 ) || !.sfOption$init || .sfOption$stopped ) return( FALSE ) else return( TRUE ) } ##***************************************************************************** ## Stop the (snow)-Cluster. Just calls Snows stopCluster. ## ## PARAMETER: [Boolean nostop: don't stop] ##***************************************************************************** sfStop <- function( nostop=FALSE ) { ## Saves users from many own if-clauses probably. if( nostop ) return( TRUE ); if( exists( ".sfOption" ) && ( length( .sfOption ) > 0 ) ) { ## Only stop if initialisized and running parallel. if( !.sfOption$stopped && .sfOption$init && .sfOption$parallel ) { message( "\nStopping cluster\n" ) ## Stopping snow cluster. ## NO call to sfGetCluster() here, as sfGetCluster sfCheck()s again. stopCluster( .sfOption$cluster ) } ## Reset default values. ##.sfOption$init <<- FALSE setOption( "stopped", TRUE ) setOption( "parallel", FALSE ) ## Delete probably stored resultfiles (can also be used in sequential mode!) deleteRestoreFiles() } invisible( NULL ) } ##***************************************************************************** ## Is programm running parallel? Wrapper for internal Optionblock (therefore ## exported of course). ## Also: get cluster Handler (prob. not exported in the final). ## ## RETURN: Boolean Running in parallel mode ##***************************************************************************** sfParallel <- function() { sfCheck() return( .sfOption$parallel ) } ##***************************************************************************** ## Shall sfClusterApplySR restore results? ##***************************************************************************** sfRestore <- function() { sfCheck() return( .sfOption$RESTORE ) } ##***************************************************************************** ## Receive snow cluster handler (for direct calls to snow functions). ##***************************************************************************** sfGetCluster <- function() { sfCheck() return( .sfOption$cluster ) } ##***************************************************************************** ## Receive amount of currently used CPUs (sequential: 1). ##***************************************************************************** sfCpus <- function() { sfCheck() return( .sfOption$nodes ) } ## getter for amount of nodes. Wrapper for sfCPUs. sfNodes <- function() return( sfCpus() ) ##***************************************************************************** ## Receive type of current cluster. ##***************************************************************************** sfType <- function() { sfCheck() if( sfParallel() ) return( .sfOption$type ) else return( "- sequential -" ) } ##***************************************************************************** ## Receive list with all socket hosts. ##***************************************************************************** sfSocketHosts <- function() { if( sfType() == "SOCK" ) { sfCheck() return( .sfOption$sockHosts ) } else { warning( paste( "No socket cluster used:", sfType() ) ) return( invisible( NULL ) ) } } ##***************************************************************************** ## getter for session-ID. ##***************************************************************************** sfSession <- function() { sfCheck(); return( .sfOption$session ) } ##***************************************************************************** ## Increase max. numbers of CPUs used per process. ## No check for sensefull values (if user wants 1000, you get 1000 :)). ##***************************************************************************** sfSetMaxCPUs <- function( number=32 ) { setVar( ".sfPresetCPUs", number ) } ##***************************************************************************** ## Internal function: ## ## Search commandline arguments for Parallel and Session values. ## If there are arguments on function call, these overwrites the values on the ## commandline. ## ## Basically the arguments on the commandline come from sfCluster, but of ## course set manually or via another load- or sessionmanager. ## ## Commandline arguments: --parallel(=[01])* ## --session=\d{8} ## --nodes=\d{1,2} ## --tmpdir=\/[a-z_].* ## --hosts=((\s+:\d+))+ ## --restoreDir=\/[a-z_].* ## --restoreSR ## --lockfile ## Results will be saved in options .parallel (bool) and .session (8 chars) ##***************************************************************************** searchCommandline <- function( parallel=NULL, cpus=NULL, socketHosts=NULL, type=NULL, restore=NULL ) { # if( !exists( ".sfOption", envir=globalenv() ) ) # stop( "Global options missing. Internal error." ) ## If set, copy to sfCluster data structure. if( !is.null( cpus ) ) { setOption( "nodes", max( 1, cpus ) ) ## For socket/NWS clusters: force rebuild of hostlist (as probably changed). ## (If not overwritten later by users own arguments). setOption( "sockHosts", NULL ) ## If more than one CPU is wanted, parallel mode is forced. ## Probably this is not an intended behavior. # if( .sfOption$nodes > 1 ) { # ## Potential misuse of argument: inform user. # if( !is.null( parallel ) && ( parallel == FALSE ) ) # warning( "Explicit parallel=FALSE, but required >1 CPUs ==> parallel mode forced." ) # # parallel = TRUE # } } ## Defaults come from calling arguments on sfInitCluster. if( !is.null( parallel ) ) { setOption( "parallel", parallel ) if( parallel ) { ## There is a slightly problem: as many users can use sfCluster without ## session-ID, the session number "XXXXXXXX" is not good enough. ## Problem: we need the filename on clusterinit so we cannot use cluster ## here. ## Win: USERNAME, *nix: LOGNAME ## LOGNAME/USER ist not set under Windows (tried Win Server 2003) uname <- ifelse( Sys.getenv( "LOGNAME" ) != "", Sys.getenv( "LOGNAME" ), Sys.getenv( "USERNAME" ) ) if( uname == "" ) uname <- "___" ## Add R for RunSnowMode heterogenous mode. ## XXX Check R version and fill in correct version. setOption( "session", paste( sep="_", "XXXXXXXXR", uname, format( Sys.time(), "%H%M%S_%m%d%y" ) ) ) ## message( "Forced parallel. Using session: ", .sfOption$session, " \n" ) } ## Sequential mode: reduce to one CPU. else { setOption( "nodes", 1 ) ## message( "Forced to sequential mode.\n" ) } } ## If socket hosts are set, take them. if( !is.null( socketHosts ) || is.vector( socketHosts ) ) setOption( "sockHosts", socketHosts ) ## Type of the cluster ({SOCK|PVM|MPI|NWS} are allowed). if( !is.null( type ) ) { if( length( grep( "PVM|MPI|SOCK|NWS", type ) ) > 0 ) setOption( "type", type ) else { warning( paste( "Unknown cluster type:", type, "Allowed are: {PVM,MPI,SOCK,NWS}. Fallback to SOCKet." ) ) setOption( "type", "SOCK" ) } } ## Default value: socket cluster. else setOption( "type", "SOCK" ) ## Global restore setting (for sfClusterApplySR). if( !is.null( restore ) ) setOption( "RESTORE", restore ) arguments <- commandArgs() ## Search for currently executed R-file (if there is any). Detected by ## argument followed to option "-f" ("R CMD BATCH" adds -f implicitely). ## Save filename for options (for save/restore) ## @todo Find a better way to detect R-file (is there any?) ## Last argument to be ignored (as no follow-up exists). if( length( arguments ) >= 2 ) { for( entry in seq( 1, length( arguments ) - 1 ) ) { if( !is.null( arguments[entry] ) && ( arguments[entry] == '-f' ) ) { ## Switch to next entry and check if this is valid. entry <- entry + 1; ## If yes, take it as filename. if( !is.null( arguments[entry] ) && ( arguments[entry] != "" ) ) { setOption( "CURRENT", arguments[entry] ) break } } } } ## No R-file given: set to DEFAULT filename (always occurs in interactive ## mode). if( is.null( .sfOption$CURRENT ) ) setOption( "CURRENT", "DEFAULT" ) ## Go through all arguments from commandline. for( arg in arguments ) { ## Non sfCluster-like argument? Skip. ## (Only empty argument are '--parallel' and '--restoreSR') if( ( length( grep( "=", arg ) ) == 0 ) && !( ( arg == "--parallel" ) || ( arg == "--restoreSR" ) || ( arg == "--restore" ) ) ) next; ## Arguments in form "--name=value" args <- strsplit( arg, "=" ) ## Marker for parallel execution. ## If parallel was set via function arguments, commandline is ignored. if( args[[1]][1] == "--parallel" ) { if( !is.null( args[[1]][2] ) && !is.na( as.numeric( args[[1]][2] ) ) ) cmdParallel <- ifelse( ( as.numeric( args[[1]][2] ) > 0 ), TRUE, FALSE ) ## --parallel is allowed to use without value (means: true). else cmdParallel <- TRUE ## Ask here, instead there will be a warning if used with commandline arg ## --parallel and sfInit( parallel=TRUE ). ## Rise warning if command arguments are overwritten by sfInit() arguments. if( is.null( parallel ) ) setOption( "parallel", cmdParallel ) else if( parallel != cmdParallel ) warning( paste( "Commandline argument --parallel", "overwritten with sfInit argument parallel=", parallel ) ) } ## Marker for general restore (only used in sfClusterApplySR). ## Both --restoreSR/--restore are allowed. else if( ( args[[1]][1] == "--restoreSR" ) || ( args[[1]][1] == "--restore" ) ) { if( is.null( restore ) ) setOption( "RESTORE", TRUE ) else if( !restore ) warning( "Commandline argument --parallel", "overwritten with sfInit argument restore=TRUE" ) } ## Marker for Session-ID. else if( args[[1]][1] == "--session" ) { ## Session-ID is allways 8 Chars long. ## Not anymore since sfCluster >=0.23 if( !is.null( args[[1]][2] ) ) { ##&& ( nchar( args[[1]][2] ) == 8 ) ) { setOption( "session", args[[1]][2] ) } else warning( paste( "Empty or irregular Session-ID: '", args[[1]][2], "'\n" ) ) } ## Amount of CPUs (formerly called "nodes", kept for backward ## compatibility). ## If set via function arguments, commandline is ignored. else if( ( args[[1]][1] == "--nodes" ) || ( args[[1]][1] == "--cpus" ) ) { nodes <- try( as.numeric( args[[1]][2] ) ) if( !is.null( nodes ) && !is.na( nodes ) ) { if( nodes > .sfOption$MAXNODES ) { stop( paste( "Too much CPUs allocated:", nodes, "Max.:", .sfOption$MAXNODES, "\n - Call sfSetMaxCPUs() before sfInit() if you need more." ) ) } else nodes <- max( 1, nodes ) ## Really set amount of CPUs? Rise overwrite warning if needed. if( is.null( cpus ) ) setOption( "nodes", nodes ) else if( cpus != nodes ) warning( paste( "Commandline --cpus=", nodes, " overwritten by sfInit() argument cpus=", cpus, sep="" ) ) } else warning( paste( "Empty or irregular nodes amount: '", nodes, "'\n" ) ) } ## Type of the network. else if( args[[1]][1] == "--type" ) { if( !is.null( args[[1]][2] ) && ( nchar( args[[1]][2] ) > 0 ) ) { if( length( grep( "PVM|MPI|SOCK|NWS", args[[1]][2] ) ) > 0 ) { if( is.null( type ) ) setOption( "type", args[[1]][2] ) else if( type != args[[1]][2] ) warning( paste( "Commandline --type=", args[[1]][2], " overwritten by sfInit() argument type=", type, sep="" ) ) } else { warning( paste( "Unknown cluster type on commandline:", args[[1]][2], "Allowed are: {PVM,MPI,SOCK,NWS}" ) ) } } else warning( "No cluster-type is given as value for argument --type" ) } ## Hosts for socket mode. ## Arguments come in format: ## nodename:cpus -> On node X are Y cpus used. ## nodename -> On node X one cpu is used. ## Any entries are comma seperated (no whitespace allowed!): ## node1:3,node2,node3:2 else if( args[[1]][1] == "--hosts" ) { if( !is.null( args[[1]][2] ) && ( nchar( args[[1]][2] ) > 0 ) ) { cmdHosts <- c() hosts = unlist( strsplit( args[[1]][2], "," ) ) ## Examine single host for( host in hosts ) { info <- unlist( strsplit( host, ":" ) ) ## No CPU amount given: assume 1. if( is.null( info[2] ) || is.na( info[2] ) ) info[2] <- 1 offset <- as.integer( info[2] ) if( offset <= 0 ) offset <- 1 if( !is.numeric( offset ) ) stop( paste( "NOT NUMERIC: '", offset, "'", sep="" ) ) len <- length( cmdHosts ) + 1 ## Insert Host n-times where n is amount of CPUs ## (required for snows argument format). cmdHosts[seq(len,len+offset-1)] <- rep( as.character( info[1] ), offset ) } if( is.null( socketHosts ) ) setOption( "sockHosts", cmdHosts ) else if( paste( cmdHosts, collapse="" ) != paste( socketHosts, collapse="" ) ) { warning( paste( "Commandline --hosts=", args[[1]][2], " overwritten by sfInit() argument hosts=", paste( socketHosts, collapse="," ), sep="" ) ) } } else warning( "No hosts are given as value for --hosts" ) } ## Temporary directory: slave logs. else if( args[[1]][1] == "--tmpdir" ) { if( !is.null( args[[1]][2] ) && ( nchar( args[[1]][2] ) > 0 ) ) setOption( "TMPDIR", args[[1]][2] ) else warning( "No temporary directory given as value for --tmpdir" ) } ## Restore directory: intermediate results are lawn here. else if( args[[1]][1] == "--restdir" ) { if( !is.null( args[[1]][2] ) && ( nchar( args[[1]][2] ) > 0 ) ) setOption( "RESTDIR", args[[1]][2] ) else warning( "No restore/result directory given as value for --restdir" ) } ## Startup lock. ## Add 1.62: ## should only used from sfCluster => is the marker snowfall is started ## though sfCluster! else if( args[[1]][1] == "--lockfile" ) { if( !is.null( args[[1]][2] ) && ( nchar( args[[1]][2] ) > 0 ) ) setOption( "LOCKFILE", args[[1]][2] ) else warning( "No lockfile given as value for --lockfile" ) } ## Unknown option ## Add 1.62 ## Add 1.72: patch from Michael Siegel for Mac OS X, which sets --gui. else if( args[[1]][1] != "--gui" ) { warning( paste( "Unknown option on commandline:", args[[1]][1] ) ) } } invisible( NULL ) } snowfall/R/snowWrappers.R0000644000175100001440000002257112254102065015157 0ustar hornikusers## Wrappers for Snow function. ## ## The wrappers do the following: decide whether we run in parallel or ## sequential mode. ## In parallel mode the according Snow functions are used. ## In sequential mode, if it makes sense, the sequential counterparts ## of the Snow functions are used. ##**************************************************************************** ## Wrapper for: clusterSplit ##**************************************************************************** sfClusterSplit <- function( seq ) { sfCheck(); if( sfParallel() ) return( clusterSplit( sfGetCluster(), seq ) ) ## In sequential mode return a list with everything in element 1 (means: ## everything is run on one node). else return( list( seq ) ) } ##**************************************************************************** ## Wrapper for: clusterCall ## ## Catches for errors. Return them or stop immidiately. ##**************************************************************************** sfClusterCall <- function( fun, ..., stopOnError=TRUE ) { sfCheck(); if( !checkFunction( fun, stopOnError=FALSE ) ) { if( stopOnError ) stop( "No function or not defined object in sfClusterCall" ) else { warning( "No function or not defined object in sfClusterCall" ) return( NULL ) } } if( sfParallel() ) { ## Exec via Snow. result <- clusterCall( sfGetCluster(), fun, ... ) ## Not enough results? ## @TODO Check if this test is needed if( length( result ) != sfCpus() ) { if( stopOnError ) stop( paste( "Error in sfClusterCall (not all slaves responded).\n", "Call from: ", as.character( sys.call( -1 ) ) ) ) else { message( paste( "Error in sfClusterCall (not all slaves responded).\n", "Call from: ", as.character( sys.call( -1 ) ) ) ) return( result ); } } ## Check if snow throw an exception on any of the slaves. if( !all( checkTryErrorAny( result ) ) ) { errorsTxt <- sapply( which( inherits( result, "try-error" ) ), function(x) result[[x]] ) message( "EXCEPTION INFOS:" ) message( paste( errorsTxt, collapse="\n" ) ) if( stopOnError ) { stop( paste( "Error in sfClusterCall (catched TRY-ERROR).\n", "Call from: ", as.character( sys.call( -1 ) ) ) ) } else { message( paste( "Error in sfClusterCall (catched TRY-ERROR).\n", "Call from: ", as.character( sys.call( -1 ) ) ) ) return( result ) } } return( result ) } ## Sequential mode. else return( do.call( fun, list( ... ) ) ) } ##**************************************************************************** ## Wrapper for: clusterEvalQ - renamed as indeed "eval" is executed and not ## "evalq". ##**************************************************************************** sfClusterEval <- function( expr, stopOnError=TRUE ) { sfCheck(); if( sfParallel() ) { return( sfClusterCall( eval, substitute( expr ), env=globalenv(), stopOnError=stopOnError ) ) } else { ## Problems can arise through "enclos", which is default set to parent ## and therefore here, too: on this way local variables (higher environments ## are visible, which badly are not visible in parallel runs...). ## There should be a fix or something. return( eval( expr, envir=globalenv(), enclos=parent.frame() ) ) } } ## Snows clusterEvalQ uses "eval" and not "evalq", so this wrapper is an alias. sfClusterEvalQ <- function( expr ) return( sfClusterEval( expr ) ) ##**************************************************************************** ## Wrapper for: clusterMap. ## Currently not used. ##**************************************************************************** sfClusterMap <- function( fun, ..., MoreArgs=NULL, RECYCLE=TRUE ) stop( "Currently no wrapper for clusterMap" ) ##**************************************************************************** ## Wrapper for: clusterApply (snow parallel) - lapply (sequential) ## Adds additional warnings before the execution (esp. in sequential mode, ## where exec works fine but can cause problems runnin in parallel). ## ## PARAMETERS: Parameters like clusterApply ## RETURN: Result ##**************************************************************************** sfClusterApply <- function( x, fun, ... ) { sfCheck(); checkFunction( fun ) ## However snow limits list size to cluster nodes in "normal" ## execution. ## This is a fatal error in parallel mode and a warning in sequential. if( length( x ) > sfCpus() ) { if( sfParallel() ) stop( "More list entries as nodes => use sfClusterApplyLB instead. See Snow/Snowfall documentation." ) else warning( "More list entries as nodes => causes error in parallel mode. use sfClusterApplyLB instead." ) } if( sfParallel() ) return( clusterApply( sfGetCluster(), x, fun, ... ) ) else return( lapply( x, fun, ... ) ) } ##**************************************************************************** ## Wrapper for: clusterApplyLB (snow parallel) - lapply (sequential) ## ## PARAMETERS: Parameters like clusterApply ## RETURN: Result ##**************************************************************************** sfClusterApplyLB <- function( x, fun, ... ) { sfCheck(); checkFunction( fun ) if( sfParallel() ) return( clusterApplyLB( sfGetCluster(), x, fun, ... ) ) else ## array... korrigieren. return( lapply( x, fun, ... ) ) } ##**************************************************************************** ## Also snow-Handler handling is hidden to the user. ## ## Wrapper for: parLappy (snow parallel) - lapply (sequential) ## ## As lapply parameters were inkonsitent ("x"/"fun") they were corrected to ## ""x"/"fun". ## ## PARAMETERS: Parameters like lapply ## RETURN: Result ##**************************************************************************** sfLapply <- function( x, fun, ... ) { sfCheck() checkFunction( fun ) if( sfParallel() ) return( parLapply( sfGetCluster(), x, fun, ... ) ) else return( lapply( x, fun, ... ) ) } ##**************************************************************************** ## Wrapper for: parSapply (snow parallel) - sapply (sequential) ## ## PARAMETERS: Parameters like sapply ## RETURN: Result ##**************************************************************************** sfSapply <- function( x, fun, ..., simplify=TRUE, USE.NAMES=TRUE ) { sfCheck() checkFunction( fun ) if( sfParallel() ) return( parSapply( sfGetCluster(), x, fun, ..., simplify=simplify, USE.NAMES=USE.NAMES ) ) else return( sapply( x, fun, ..., simplify=simplify, USE.NAMES=USE.NAMES ) ) } ##**************************************************************************** ## Wrapper for: parApply (snow parallel) - apply (sequential) ## ## PARAMETERS: Parameters like apply ## RETURN: Result ##**************************************************************************** sfApply <- function( x, margin, fun, ... ) { sfCheck() checkFunction( fun ) if( sfParallel() ) return( parApply( sfGetCluster(), x, margin, fun, ... ) ) else return( apply( x, margin, fun, ... ) ) } sfRapply <- function( x, fun, ... ) { stop( "sfRapply does not exists yet. Use Snow's parRapply instead." ) return( invisible( NULL ) ); } sfCapply <- function( x, fun, ... ) { stop( "sfCapply does not exists yet. Use Snow's parCapply instead." ) return( invisible( NULL ) ); } ##**************************************************************************** ## Wrapper for: parMM (snow parallel) - %*% (sequential) ## ## PARAMETERS: Matrix a, Matrix b ## RETURN: Result ##**************************************************************************** sfMM <- function( a, b ) { sfCheck(); if( sfParallel() ) return( parMM( sfGetCluster(), a, b ) ) else return( a %*% b ) } ##**************************************************************************** ## Wrappers for the two uniform RNGs used in snow. ## Basically, at the moment these are not used in sequential (means: none ## of the two is included here for sequential execution). ## @TODO Sequential use of the RNGs. ##**************************************************************************** sfClusterSetupSPRNG <- function( seed = round( 2^32 * runif(1) ), prngkind = "default", para = 0, ... ) { sfCheck(); if( sfParallel() ) clusterSetupSPRNG( sfGetCluster(), seed, prngkind, para, ... ) else { warning( paste( "Uniform random number streams (currently) not available in serial execution.", "Random numbers may differ in serial & parallel execution." ) ) set.seed( seed ) } } sfClusterSetupRNGstream <- function( seed=rep( 12345, 6 ), ... ) { sfCheck(); if( sfParallel() ) clusterSetupRNGstream( sfGetCluster(), seed=seed, ... ) else { warning( paste( "Uniform random number streams (currently) not available in serial execution.", "Random numbers may differ in serial & parallel execution." ) ) set.seed( seed[1] ) } } sfClusterSetupRNG <- function( type="RNGstream", ... ) { sfCheck(); if( sfParallel() ) clusterSetupRNG( sfGetCluster(), type=type, ... ) else { warning( paste( "Uniform random number streams (currently) not available in serial execution.", "Random numbers may differ in serial & parallel execution." ) ) } } snowfall/R/socketRequest.R0000644000175100001440000000332212254102065015277 0ustar hornikusers#***************************************************************************** # Functions for handling the connection to the controlling webserver. # # Base socketrequest follows code from post on R-help: # http://tolstoy.newcastle.edu.au/R/devel/06/07/6196.html # # PARAMETER: Array|List Arguments (all Entries must be in format "name=val") # RETURN: Array result from webserver # THROWS: Exception Connection Error #***************************************************************************** connectWebserver <- function( call.args ) { host <- "www.imbi.uni-freiburg.de" path <- "/bib/bib.pl" # Parameters for the call. dat <- paste( call.args, collapse="&", sep="" ) len <- length( strsplit(dat,"")[[1]] ) request <- paste( "POST ", path, " HTTP/1.0\nHost: ", host, "\nReferer:\n", "Content-type: application/x-www-form-urlencoded\n", "Content-length: ", len, "\nConnection: Keep-Alive\n\n", dat, sep="" ) sock <- NULL # Needed in catch-Block for disconnect readSock <- "" # Connect. Catch exceptions regarding to connection errors. exception <- try( { sock <- socketConnection( host=host, port=80, server=FALSE, blocking=TRUE ) write( request, sock ) socketSelect( list( sock ) ) readSock <- readLines( sock ) close( sock ) }, silent=FALSE ) if( inherits( exception, "try-error" ) ) { cat( "Error connecting to: ", host, path, dat, "\n" ) # If socket exists, close it. if( sock != NULL ) close( sock ) stop() } return( readSock ) } ##connectWebserver( c( "kat=ben", "cmd=list", "usr=jo", "usr_sel=JO" ) ) snowfall/R/snowfall-internal.R0000644000175100001440000002443212254102065016102 0ustar hornikusers##***************************************************************************** ## Unordered internal helper functions. ##***************************************************************************** ##***************************************************************************** ## Helpers for managing the internal variables in the package namespace without ## awake the R CMD check for later R versions (which basically blaims many ## global assignings). ## ## The given solution has an advantage: only writing is affected. Reading of the ## objects can remain the same (thanks to Uwe Ligges for the tipp): ## reading: .sfOption$parallel ## writing: setOption("parallel", TRUE) ##***************************************************************************** ##***************************************************************************** ## Set an option in the snowfall option list. ## (Basically this is the setting of a list entry). ## key - character: object name ## val - object (everything is allowed, even NULL) ##***************************************************************************** setOption <- function( key=NULL, val=NULL ) { if( !is.null(key) && is.character( key ) ) { option <- getVar( ".sfOption" ) ## Get from NS option[[key]] <- val setVar( ".sfOption", option ) ## Write to NS return( invisible( TRUE ) ) } stop( "key or val is NULL or key no string." ) } ##***************************************************************************** ## Get a specific variable from the snowfall namespace. ## var - character: object name ##***************************************************************************** getVar <- function( var=NULL ) { if( !is.null( var ) && is.character( var ) ) { tmp <- try( getFromNamespace( var, "snowfall" ) ) if( inherits( tmp, "try-error" ) ) stop( paste( "Object", var, "not found in package" ) ) return( tmp ) } stop( "var is NULL or not a string." ) } ##***************************************************************************** ## Write a specific variable to the snowfall namespace. ## var - character: object name ## arg - object (NULL allowed) ##***************************************************************************** setVar <- function( var=NULL, arg=NULL ) { if( !is.null( var ) && is.character( var ) ) { assignInNamespace( var, arg, "snowfall" ) return( invisible( TRUE ) ) } stop( "var is NULL or no character" ); } ##***************************************************************************** ## Replaces the tilde operator in file/directory names with the system ## depending counterpart. ## Used for configuration files mainly. ## ## PARAMETER: String directory ## RETURN: String directory replaced ##***************************************************************************** fetchDirName <- function( dir ) { return( gsub( "~", Sys.getenv( "HOME" ), dir ) ) } ##***************************************************************************** ## Is this snowfall session started through sfCluster? ## As a backward compatible solution there is only the LOCKFILE option open ## (as there is no default for it and setable through commandline). ## ## PARAMETER: - ## RETURN: Boolean True (running with sfCluster), False ##***************************************************************************** startedWithSfCluster <- function() { if( !exists( ".sfOption" ) ) return( FALSE ) else return( !is.null( .sfOption$LOCKFILE ) && ( .sfOption$LOCKFILE != '' ) ) } ##***************************************************************************** ## Creates a directory (recursive) if needed and stops on failure. ## ## PARAMETER: String directory ## RETURN: Boolean success (true, on fail, execution stops) ##***************************************************************************** dirCreateStop <- function( dir=NULL ) { if( !is.null( dir ) && !file.exists( dir ) ) { if( dir.create( dir, recursive=TRUE ) ) { message( "Created directory: ", dir ) return( invisible( TRUE ) ); } else stop( "UNABLE to create directory: ", dir ) } ## Never reached. return( invisible( FALSE ) ); } ##*************************************************************************** ## Add a file (with absolute path) to remove list after sfStop(). ## Used for save/restore-files. ## ## PARAMETER: file String abs. filepath ##*************************************************************************** addRestoreFile <- function( file=NULL ) { if( !is.null( file ) ) if( is.vector( .sfOption$RESTOREFILES ) ) ## Check if file is already in the list. If yes: no add. if( length( grep( file, .sfOption$RESTOREFILES ) ) == 0 ) setOption( "RESTOREFILES", c( .sfOption$RESTOREFILES, file ) ) else setOption( "RESTOREFILES", c( file ) ) debug( paste( "Added file for delete: ", file, "\n" ) ) return( invisible( length( .sfOption$RESTOREFILES ) ) ) } ##*************************************************************************** ## Clean up save/restore files after successfull cluster shutdown. ##*************************************************************************** deleteRestoreFiles <- function() { if( !is.null( .sfOption$RESTOREFILES ) ) { ## File names are absolute: just unlink all. ## lapply( .sfOption$RESTOREFILES, unlink ) for( file in .sfOption$RESTOREFILES ) { ## Does file exist? if( file.exists( file ) ) { if( unlink( file ) != 0 ) cat( "Unable to delete save/restore file:", file, "\n" ) else cat( "Deleted save/restore file:", file, "\n" ) } } setOption( "RESTOREFILES", NULL ) } } ##*************************************************************************** ## Check if any element of a given list produced a stop or try-error. ## RETURN: Vector of logicals (true: ok, false: try error caught). ##*************************************************************************** checkTryErrorAny <- function( res ) { return( sapply( res, function( x ) { if( inherits( x, "try-error" ) ) return( FALSE ) else return( TRUE ) } ) ) } ##*************************************************************************** ## Check if given argument is a function. ##*************************************************************************** checkFunction <- function( fun, stopOnError=TRUE ) { return( TRUE ) state <- FALSE ## 1.84-3 typo try( if( !exists( as.character( substitute( fun ) ), inherits=TRUE ) || !is.function( fun ) || is.null( get( as.character( substitute( fun ) ), inherits=TRUE ) ) || !is.function( fun ) ) state <- TRUE ) if( !state ) { ## if( !is.function( fun ) ) cat( "FAIL SYMBOL\n" ) ## if( !exists( as.character( substitute( fun ) ), inherit=TRUE ) ) cat( "FAIL EXIST\n" ) ## if( is.null( get( as.character( substitute( fun ) ), inherit=TRUE ) ) ) cat( "FAIL GET\n" ) ## if( !is.function( fun ) ) cat( "FAIL FUNCTION\n" ) if( stopOnError ) stop( paste( "Not a function in sfCluster function call: '", fun, "'" ) ) } return( state ) } errHandler <- function( ... ) { print( "ERROR IN HANDLING STUFF!\n" ) } ##*************************************************************************** ## Treat given three dot arguments as strings (for names listings ## like in sfExport). ## Ripped from buildin R function rm (by XXX). ## Returns list with names, stops on errors. ##*************************************************************************** fetchNames <- function( ... ) { ## Dot argument to list of characters: ripped from rm()... dots <- match.call(expand.dots = FALSE)$... if( length(dots) && !all( sapply( dots, function(x) is.symbol(x) || is.character(x) ) ) ) stop( "... must contain names or character strings in function ", as.character( sys.call( -1 ) ) ) ## end ripp. return( sapply(dots, as.character) ) } ##*************************************************************************** ## Create named list with all parameters from an function call. ## Idea somewhere from R-help (not tracked). ## This does not work if above env is not global env! ##*************************************************************************** getNamedArguments <- function( ... ) { pars <- as.list( substitute( {...} )[-1] ) ## pars <- as.list( substitute( {...} )[-1] ) ## pars <- lapply( pars, function( x ) { ## if( is.atomic( x ) ) ## return( x ) ## else ## return( deparse( x ) ) ## } ) return( pars ) } ##*************************************************************************** ## Ensure a given filename contains an absolute path. ## Kind of silly and lame. But works in most cases. ##*************************************************************************** absFilePath <- function( file ) { ## If not starting with separator, path is most likely relative. ## Make it absolute then. ## On Windows absolute path can contain drive chars. if( .Platform$OS.type == "windows" ) { if( ( substr( file, 1, 1 ) != .Platform$file.sep ) && ( substr( file, 2, 2 ) != ":" ) ) file <- file.path( getwd(), file ) } else if( substr( file, 1, 1 ) != .Platform$file.sep ) file <- file.path( getwd(), file ) return( file ) } simpleAssign <- function( name=NULL, value ) { message( paste( "simpleAssign called: ", name, "VAL:", value ) ) if( is.null( name ) || !is.character( name ) || ( nchar( name ) == 0 ) ) { warning( "NULL assign on simpleAssign()" ) return( NULL ) } else { ## 1.84-4 ## Problem: it is required to write to global env! ## Comment censored :) # assign( name, value, envir = globalenv() ) assign( name, value, pos=sys.nframe() ) return( NULL ) } } ##*************************************************************************** ## Internal debug printer (globally disable using package variable DEBUG). ##*************************************************************************** debug <- function( txt='' ) { if( DEBUG ) message( txt ) } .onLoad <- function( lib, pkg ) { ## options( "error"=errHandler ) } snowfall/vignettes/0000755000175100001440000000000012254301027014121 5ustar hornikuserssnowfall/vignettes/all-bib.bib0000644000175100001440000000457412254102616016116 0ustar hornikusers% This file was created with JabRef 2.3.1. % Encoding: UTF-8 @Article{Knau:Porz:Bind:Schw:easi:2009, author = {Jochen Knaus and Christine Porzelius and Harald Binder and Guido Schwarzer}, title = {Easier parallel computing in {R} with snowfall and {sfCluster}}, journal = {The R Journal}, volume = {1}, pages = {54--59}, year = {2009}} @ARTICLE{TIERNEY08, author = {A.J. Rossini AND Luke Tierney AND Na Li}, title = {Snow : A Parallel Computing Framework for the {R} System}, journal = {International Journal of Parallel Programming}, year = {2008}, note = {Online Publication: \url{http://www.springerlink.com/content/3v37mg0k63053567} (2008-12-23)}, } @ARTICLE{HANA_STAT04, author = {{\v{S}}ev{\v{c}}{\'{\i}}kov{\'a}, Hana}, title = {Statistical simulations on parallel computers}, journal = {Journal of Computational and Graphical Statistics}, year = {2004}, volume = {13}, pages = {886--906}, number = {4}, fjournal = {Journal of Computational and Graphical Statistics}, issn = {1061-8600}, mrclass = {Database Expansion Item}, mrnumber = {MR2113277} } @ARTICLE{HANA_04, author = {Hana {\v{S}}ev{\v{c}}{\'{\i}}kov{\'a} AND A.J. Rossini}, title = {Pragmatic Parallel Computing. Submitted to Journal of Statistical Software}, year = {2004}, owner = {jo}, timestamp = {2008.04.21} } @TECHREPORT{burns94:_lam, author = {Greg Burns and Raja Daoud and James Vaigl}, title = {{LAM}: {A}n {O}pen {C}luster {E}nvironment for {MPI}}, year = {1994}, note = {\url{http://www.lam-mpi.org/download/files/lam-papers.tar.gz}}, booktitle = {Proceedings of Supercomputing Symposium}, pages = {379--386} } @ARTICLE{ROSS_07, author = {A.J. Rossini AND Luke Tierney AND Na Li}, title = {Simple Parallel Statistical Computing in {R}}, journal = {Journal of Computational and Graphical Statistics}, year = {2007}, volume = {16}, pages = {399--420}, number = {2}, fjournal = {Journal of Computational and Graphical Statistics}, issn = {1061-8600} } @OTHER{SNOW_PACK, author = {A.J. Rossini AND Luke Tierney AND Na Li}, note = {\url{http://cran.rproject.org/doc/packages/snow.pdf}}, owner = {jo}, timestamp = {2008.04.22}, title = {The Snow package}, year = {2006} } @comment{jabref-meta: selector_journal:} @comment{jabref-meta: selector_author:} @comment{jabref-meta: selector_keywords:} @comment{jabref-meta: selector_publisher:} snowfall/vignettes/snowfall.Snw0000644000175100001440000006503712254102616016455 0ustar hornikusers% \VignetteIndexEntry{An R Package for easier cluster programming based on snow} % \VignetteKeyword{Parallel Computing} % \VignetteKeyword{Cluster} % \VignetteKeyword{HPC} % \VignetteKeyword{snow} % \VignetteKeyword{LAM} % \VignetteKeyword{MPI} \documentclass[10pt,oneside]{article} \usepackage{url} \begin{document} \pagestyle{empty} \setlength{\baselineskip}{1.25em} \setlength{\parskip}{0.5em} \setlength{\parindent}{0.0em} \begin{titlepage} \title{Developing parallel programs using snowfall} \author{Jochen Knaus} \date{2010-03-04} \maketitle \begin{abstract} \texttt{snowfall} is an R package for easier parallel programming using clusters. Basically it is build upon the package \texttt{snow} \cite{TIERNEY08} using it's network and cluter abilities and therefore offering use of Socket, MPI, PVM and NetWorkSpaces support and can be seen as an "usability wrapper". \texttt{snow} functions can used from within \texttt{snowfall} as well. \texttt{snowfall} offers additional support for implicit sequential execution (e.g. for distributing packages using optional parallel support), additional calculation functions, extended error handling, and many functions for more comfortable programming. Also, \texttt{snowfall} can be configured via command line arguments, making the change of cluster settings easier without program change. This can be used to connect to batch- and workloadmanagers. Finally \texttt{snowfall} can be directly connected to the R-specific cluster manager \emph{sfCluster}. \texttt{snowfall} does not add an technical layer of abstraction to \texttt{snow}. But beside from the connector to \texttt{sfCluster}, it builds an extra layer of usability on the top of \texttt{snow}. It is not thought as an replacement for \texttt{snow}, but an addition for inexperienced users or those who seek more comfort using parallel computing and R. A further introduction to snowfall is published in the R-Journal \cite{Knau:Porz:Bind:Schw:easi:2009}. For additional documentation, help and examples please visit our website: \url{http://www.imbi.uni-freiburg.de/parallel} \end{abstract} \end{titlepage} %% Inhaltsverzeichnis \tableofcontents \newpage \section{snowfall} \subsection{Getting started} \subsubsection{Requirements for sequential execution} Basically, \texttt{snowfall} is able to run without any external library. In this case, it is not possible to use parallel execution of commands. All potential calls to parallel functions will be executed sequentially. Programs written in sequential use with \texttt{snowfall} calls can be running in parallel without any code change. \subsubsection{Requirements for parallel execution: Basics} If you just want to use parallel computing on your local PC or laptop you are just fine with basically installation of \texttt{snowfall} and \texttt{snow}. You can use then a so called socket cluster, for which no additional software needs to be installed. If you are just wanting to use parallel programming on your local workstation, PC or laptop, you are fine. \subsubsection{Requirements for parallel execution: MPI} You have a running MPI cluster (OpenMPI or any other kind of MPI cluster) available. Although snowfall is useable with OpenMPI as well, the management software sfCluster can currently only used with LAM/MPI. \subsubsection{Requirements for parallel execution: LAM/MPI} For using sfCluster with snowfall, currently LAM/MPI is needed. If you are using Debian/Ubuntu Linux, just call\\ \texttt{aptitude install xmpi lam4-dev}\footnote{On other Linux distributions there are similar packages with probably different name. It is important that you install the development version of the LAM package, as the \texttt{Rmpi} package need these files for installation.} Further you need to install the R-packages \texttt{snow} and \texttt{Rmpi}. If your program uses libraries, ensure that these are available on all nodes. If they are not present in R-default path (on given machine), ensure that they are accessible in the same location on all machines (for example \texttt{/home/xy/R.libs}). If you want to run programs only on your (multi core) computer without any cluster of many machines, you do not have to setup the cluster yourself, it will be started implicitly in \texttt{snowfall}s initialisation. Using two or more machines for cluster calculations, you need to setup a LAM/MPI cluster and start cluster explicitely. This is no big thing at all. For example, edit a small textfile like this one: \texttt{machine1.yourdomain.com cpu=4 sched=yes\\ machine2.yourdomain.com cpu=2 sched=yes} Just enter the machines for your cluster and the amount of CPUs. You start a LAM/MPI cluster using\\ \texttt{lamboot hostfile}\\ where \texttt{hostfile} is the little configuration file edited above. To shutdown just call \texttt{lamhalt}. For further details upon LAM/MPI setup, see \cite{burns94:_lam}. Note: All parallel programs you start are running in this cluster. If your program requests 100 CPUs on your private dual-core machine, you get that amount and 100 R processes are spawn, independent or available ressources (memory, cpus). For workgroups or larger clusters, management solutions like \emph{sfCluster} are strongly recommended. \subsubsection{Requirements for parallel execution: PVM/NWS} PVM and NetWorkSpaces/Sleight are supported in snowfall as these are useable with snow. But both are less supported by sfCluster (but at least a managed start can be done using sfCluster), so there is no further documentation about their usage here. \subsection{(Short) introduction to parallel programming} The general goal of paralleling your R program is to vectorize the data or calculation loops (probably with wrapper functions), as all calculation functions of \texttt{snowfall} are kind of reimplementations of R-list/vector functions. A good introduction to parallel programming for statistical purposes can be found in \cite{ROSS_07} and \cite{HANA_STAT04}. \subsection{Introduction to usage of snowfall} Basically, usage of \texttt{snowfall} always works with the following scheme: \begin{enumerate} \item Initialization using \texttt{sfInit()}. Set up the cluster (if needed) and the internal functions. \texttt{sfInit} must be called before using any function of the \texttt{snowfall} package.\footnote{The only exception is the function \texttt{sfSetMaxCPUs()}, which raises or limits the configured maximum CPU count.} \item Export needed variables/objects to all slaves. \item Do some parallel calculations using \texttt{snowfall} calculation functions. Repeat as many times as needed. \item End parallel execution using \texttt{sfStop()}. \end{enumerate} The initialisation differs if you use \texttt{snowfall} alone or with the management tool \emph{sfCluster}. In this chapter we only cover a standalone usage of \texttt{snowfall}. For usage with \emph{sfCluster}, see chapter 2. If you are firm on using the R package \texttt{snow}, starting with or porting your program to \texttt{snowfall} is easy. The complete initialisation is done with a single call to \texttt{sfInit()}. The main arguments are \texttt{parallel}, \texttt{cpus} and \texttt{type}, giving the running mode (parallel execution or sequential execution), the amount of CPUs if executing in parallel mode and the type of the underlying cluster. If running in sequential mode, \texttt{cpus} is ignored (and set to one). Without a given \texttt{type} a socket cluster is started, which does not need any further software installed and therefore most likely runs anywhere immidiately. This is the desired choice for executing on a laptop or single multicore machine, too. Please note, that on Windows an installed Personal Firewall may alert the network access, please allow this. %On calling \texttt{sfInit( parallel=TRUE )} without a running LAM %cluster (but LAM installed), a \emph{local} cluster will be started, %which only contains your local machine. This can be handy on single %multi-core machines. But note you \texttt{sfStop} will not shutdown %this cluster, so you have to stop it yourself manually (if wished). Sequential mode can be useful for developing the program, probably on a single core laptop without installed cluster or running Windows operating system. Also sequential mode is needed to deploy a package using \texttt{snowfall} safely, where you cannot assume a user have an useable cluster installed. Other arguments for \texttt{sfCluster} are \texttt{restore}, \texttt{socketHosts}, \texttt{slaveOutfile} and \texttt{nostart}. See package help for description. If the initialisation fails, probably because of missing base libraries \texttt{Rmpi} and \texttt{snow}, \texttt{snowfall} falls back to sequential mode with a warning message. In sequential and parallel execution, all functions are useable in both modes in the same way and returning the same results. \begin{verbatim} sfInit( parallel=FALSE ) sfLapply( 1:10, exp ) sfStop() sfInit( parallel=TRUE, cpus=5 ) ## Now, index 1 is calculated on CPU1, 2 on CPU2 and so on. ## Index 6 is again on CPU1. ## So the whole call is done in two steps on the 5 CPUs. sfLapply( 1:10, exp ) sfStop() \end{verbatim} Please note: Most of the \texttt{snowfall} functions are stopping the program on failure by default (by calling \texttt{stop()}). This is much safer for unexperienced users. If you want own failure handling, install your own handler \texttt{options(error = ...)} to prevent snowfall from stopping in general. Also most of the functions feature an argument \texttt{stopOnError} which set to \texttt{FALSE} prevents the functions from stopping. Do not forget to handle potential errors in your program if using this feature. The given behavior is not only better for unexperienced users, any other behavior would be very nasty on package deployment. \subsection{Writing parallel programs with snowfall} \subsubsection{General notes and simple example} If you detected parts of your program which can be parallelised (loops etc) it is in most cases a fast step to give them a parallel run. First, rewrite them using Rs list operators (lapply, apply) instead of loops (if they are not yet calculated by list operators). Then write a wrapper function to be called by the list operators and manage a single parallel step. Note there are no local variables, only the data from the list index will be given as argument. If you need more than one variable argument, you need to make the required variables global (assign to global environment) and export them to all slaves. \texttt{snowfall} provides some functions to make this process easier (take a look at the package help). \begin{verbatim} sfInit( parallel=TRUE, cpus=4 ) b <- c( 3.4, 5.7, 10.8, 8, 7 ) ## Export a and b in their current state to all slaves. sfExport( ''b'' ) parWrapper <- function( datastep, add1, add2 ) { cat( ''Data: '', datastep, ''ADD1:'', add1, ''ADD2:'', add2, ''\n'' ) ## Only possible as ''b'' is exported! cat( ''b:'', b[datastep] ) ## Do something return( datastep ) } ## Calls parWrapper with each value of a and additional ## arguments 2 and 3. result <- sfLapply( 1:5, parWrapper, 2, 3 ) sfStop() \end{verbatim} \subsubsection{Basic load balancing using \texttt{sfClusterApplyLB}} All parallel wrappers around the R-list operators are executed in blocks: On one step the first $n$ indices are calculated, then the next $n$ indices, where $n$ is the number of CPUs in the cluster. This behavior is quite ok in a homogenous cluster, where all or mostly all machines are built with equal hardware and therefore offer the same speed. In heterogenous infrastructures, speed is depending on the slowest machine in the cluster, as the faster machines have to wait for it to finish its calculation. If your parallel algorithm is using different time for different problems, load balancing will reduce overall time in homogenous clusters greatly. \texttt{snow} and so \texttt{snowfall} feature a simple load balanced method to avoid waiting times in such environments. If calling \texttt{sfClusterApplyLB} the faster machines get further indices to calculate without waiting for the slowest to finish its step. \texttt{sfClusterApplyLB} is called like \texttt{lapply}. If your local infrastructure is such an heterogenous structure, this function is the way to go. It can also be handy in homogenous clusters where other users spawn processes, too, so sometimes load differs temporarily. A visualisation of basic load balacing can be found in \cite{ROSS_07}. \begin{verbatim} sfInit( parallel=TRUE, cpus=2 ) calcPar <- function( x ) { x1 <- matrix( 0, x, x ) x2 <- matrix( 0, x, x ) for( var in 1:nrow( x1 ) ) x1[var,] = runif( ncol( x1 ) ) for( var in 1:nrow( x2 ) ) x2[var,] = runif( ncol( x1 ) ) b <- sum( diag( ( x1 %*% x2 ) %*% x1 ) ) return( b ) } result <- sfClusterApplyLB( 50:100, calcPar ) sfStop() \end{verbatim} \subsubsection{Intermediate result saving and restoring using \texttt{sfClusterApplySR}} Another helpful function for long running clusters is \texttt{sfClusterApplySR}, which saves intermediate results after processing $n$-indices (where $n$ is the amount of CPUs). If it is likely you have to interrupt your program (probably because of server maintenance) you can start using \texttt{sfClusterApplySR} and restart your program without the results produced up to the shutdown time. Please note: Only complete $n$-blocks are saved, as the function \texttt{sfLapply} is used internally.\footnote{This function is an addition to \texttt{snow} and therefore could not be integrated in the load balanced version.} The result files are saved in the temporary folder \texttt{~/.sfCluster/RESTORE/x}, where x is a string with a given name and the name of the input R-file. \texttt{sfClusterApplySR} is called like \texttt{sfClusterApplyLB} and therefore like \texttt{lapply}. If using the function \texttt{sfClusterApplySR} result are always saved in the intermediate result file. But, if cluster stopped and results could be restored, restore itself is only done if explicitly stated. This aims to prevent false results if a program was interrupted by intend and restarted with different internal parameters (where with automatical restore probably results from previous runs would be inserted). So handle with care if you want to restore! If you only use one call to \texttt{sfClusterApplySR} in your program, the parameter \texttt{name} does not need to be changed, it only is important if you use more than one call to \texttt{sfClusterApplySR}. \begin{center} \begin{verbatim} sfInit( parallel=TRUE, cpus=2 ) # Saves under Name default resultA <- sfClusterApplySR( somelist, somefunc ) # Must be another name. resultB <- sfClusterApplySR( someotherlist, someotherfunc, name="CALC_TWO" ) sfStop() \end{verbatim} \end{center} If cluster stops probably during run of \texttt{someotherfunc} and restarted with restore-Option, the complete result of \texttt{resultA} is loaded and therefore no calculation on \texttt{somefunc} is done. \texttt{resultB} is restored with all the data available at shutdown and calculation begins with the first undefined result. \emph{Note on restoring errors}: If restoration of data fails (probably because list size is different in saving and current run), \texttt{sfClusterApplySR} stops. For securely reason it does not delete the RESTORE-files itself, but prompt the user the complete path to delete manually and explicitly. \subsection{Fault tolerance} Differing from \texttt{snowFT}, the fault tolerance extension for \texttt{snow}, \texttt{snowfall} does not feature fault tolerance (see \cite{HANA_04}). This is due to the lack of an MPI implementation of \texttt{snowFT}. \subsection{Controlling snowfall using the command line} snowfall can be widely controlled via command line arguments. This is useful for fast changing of cluster parameters (e.g. changing the host names in a Socket cluster) on a raw installation and it serves as connection to sfCluster. Of course it can be used as connection to any other workload- or batch managing software, too. On the commandline there are the following parameters: \begin{tabular}{lp{10cm}} parallel & Switch to parallel execution. Default is sequential execution \\ cpus=X & Amount of CPUs wanted. Without {-}{-}parallel, a value $X > 1$ switch to parallel execution. \\ type=X & Type of cluster. Allowed values are SOCK, MPI, PVM and NWS. \\ session=X & Session number. snowfall logfiles contain number, but only needed with sfCluster. \\ restoreSR & Enables restoring of previously saved results from \texttt{sfClusterApplySR} calls. \\ hosts=X & List of hosts for Socket (SOCK) or NetWorkSpaces (NWS) clusters. Entries are comma seperated. Any entry may contain colon seperated value for the amount of processors on this machine. Example: \texttt{{-}{-}hosts=machine1:4,machine2,123.123.12.13:2} (this spawns 4 workers on machine1, one on machine2 and two on 123.123.12.13). \\ tmpdir=X & Specify temporary directory for logfiles and R-output. \\ \end{tabular} For using these arguments, just add these after an \texttt{--args} on the commandline (which forces R not to treat these arguments as R ones). \begin{center} \texttt{R --no-save --args --parallel --cpus=2 < program.R} \end{center} Starts R and forces snowfall to start in parallel mode with 2 CPUs (in this case: using a Socket-cluster, as this is the default). \textit{Note}: arguments on the command line have lower priority as settings from the \texttt{sfInit} call. That means that the above example only works if initialisation is done via \texttt{sfInit()}, but not with \texttt{sfInit( parallel=FALSE )}, as then sequential execution is forced. Further examples should explan the feature: \begin{itemize} \item \texttt{R --no-save --args --parallel --type=MPI --cpus=4 < program.R} (start using 4 workers in an existing MPI cluster. If no MPI cluster exists, a plain one is started on your local machine only. Beware of this, as you have to shutdown this cluster afterwards manually.). \item \texttt{R --no-save --args --parallel --type=SOCK --hosts=localhost:3,singlema,othmach:4 < program.R} (Starts a socket cluster with two machines and 7 CPUs: 3 on \texttt{localhost}, 4 on \texttt{othmach} and one worker on \texttt{singlema}). \end{itemize} \subsection{Traps, Internals} \texttt{snowfall} limits the amount of CPUs by default (to 40). If you need more CPUs, call \texttt{sfSetMaxCPUs()} \emph{before} calling \texttt{sfInit()}. Beware of requesting more CPUs as you have ressources: there are as many R processes spawned as CPUs wanted. They are distributed across your cluster like in the given scheme of the LAM host configuration. You can easily kill all machines in your cluster by requesting huge amounts of CPUs or running very memory consuming functions across the cluster. To avoid such common problems use \emph{sfCluster}. For some functions of \texttt{snowfall} it is needed to create global variables on the master. All these variables start with prefix ``\texttt{.sf}'', please do not delete them. The internal control structure of \texttt{snowfall} is saved in the variable \texttt{.sfOptions}, which should be accessed through the wrapper functions as the structure may change in the future.\section{Using \emph{sfCluster} with \texttt{snowfall}} \subsection{About \emph{sfCluster}} \emph{sfCluster} is a small management tool, helping to run parallel R-programs using \texttt{snowfall}. Mainly, it exculpates the user from setting up a LAM/MPI cluster on his own. Further, it allows multiple clusters per user and therefore executes any parallel R program in a single cluster. These clusters are built according to the current load and usage of your cluster (this means: only machines are taken with free ressources). Also, execution is observed and if problems arise, the cluster is shut down. \emph{sfCluster} can be used with R-interactive shell or batch mode and also feature a special batch mode with visual logfile and process-displaying. For further details about installation, administration and configuration of \emph{sfCluster}, please visit \url{http://www.imbi.uni-freiburg.de/parallel} or run \texttt{sfCluster {-}{-}help} if you installed it yet. \subsection{Starting R using \emph{sfCluster}} An \emph{sfCluster} execution is following these steps: \begin{enumerate} \item Test memory usage of program if not explicitly given. This is done via a default temporary (10 minutes) sequential run to determinate the maximum usage of RAM on a slave. This is important for allocating ressources on slaves. \item Detect free ressources in cluster universe.\footnote{Which are all potentially useable machines.} Take machines with free ressources matching users request. \item Start LAM/MPI cluster with previous built setting. \item Run R with parameters for \texttt{snowfall} control. \item LOOP: Observe execution (check processes, memory usage, and machine state). In monitoring mode: Display state of cluster and logfiles on screen. \item On interruption or regular end: shutdown cluster. \end{enumerate} \subsection{Using \emph{sfCluster}} The most common parameters of \emph{sfCluster} are \texttt{{-}{-}cpus}, with which you request a certain amount of CPUs among the cluster (default is 2 in parallel and 1 in sequential mode). There is a builtin limit for the amount of CPUs, which is changeable using the \emph{sfCluster} configuration. There are four execution modes: \begin{tabular}{lp{3cm}p{9cm}} -b & Batchmode (Default) & Run silent on terminal.\\ -i & Interactive R-shell & Ability to use interactive R-shell with cluster.\\ -m & Monitoring mode & Visual processmonitor and logfile viewer.\\ -s & Sequential execution (no cluster usage) & Run without cluster on single CPU.\\ \end{tabular} To avoid the (time consuming) memory test, you can specify a maximum amount of memory usable per slave via option \texttt{{-}{-}mem}. The behavior on excessing this memory usage is configurable (default: cluster stop). The memory usage limit is very important for not getting your machines into swapping (means: shortage of physical RAM), which would hurt performance badly. So, simple calls to \emph{sfCluster} could be \begin{verbatim} ## Run a given R program with 8 cpus and max. 500MB (0.5 gigabytes) in monitoring mode sfCluster -m --cpus=8 --mem=0.5G myRprogram.R ## Run nonstopping cluster with real quiet output. nohup sfCluster -b --cpus=8 --mem=500M myRprogram.R --quiet ## Start R interactive shell with 4 cores. With 300MB memory (MB is default unit) ## No R-file is given for interactive mode. sfCluster -i --cpus=4 --mem=300 \end{verbatim} For all possible options and further examples for \emph{sfCluster} usage, see \texttt{sfCluster {-}{-}help}. \subsection{The snowfall-side of \emph{sfCluster}} If you start an R program using \texttt{snowfall} with \emph{sfCluster}, the latter waits until \texttt{sfInit()} is called and then starts the observation of the execution. The default behavior if using \emph{sfCluster} is just to call \texttt{sfInit()} without any argument. Use arguments only if you want to explicitly overwrite given settings by \emph{sfCluster}. \subsection{Proposed development cycle} The following development cycle is of course a proposal. You can skip or replace any step depending on your own needs. \begin{enumerate} \item Develop program in sequential mode (start using option \texttt{-s}). \item Test in parallel mode using interactive mode to detect directly problems on parallelisation (start using option \texttt{-i}). \item Try larger test runs using monitoring mode, observing the cluster and probably side effects during parallel execution (start using option \texttt{-m}). Problems arise on single nodes will be visible (like non correct working libraries). \item Do real runs using silent batch mode (start using options \texttt{-b {-}{-}quiet}). Probably you want to run these runs in the background of your Unix shell using \texttt{nohup}. \end{enumerate} \subsection{Future sfCluster} These additions are planned for the future: \begin{itemize} \item Port to OpenMPI \item Faster SSH connections for observing \item Extended scheduler for system ressources \end{itemize} %% History. \section{History of snowfall changes} You can also call: RShowDoc("NEWS", package="snowfall") \begin{itemize} \item 1.83 (API changes: minor additions) \begin{itemize} \item sfIsRunning: new function giving a logical is sfInit() was called or not. Needed, as all other snowfall functions implicitely call sfInit() if it was not called. \end{itemize} \item 1.82 \begin{itemize} \item Internal refactorings. \end{itemize} \item 1.81 \begin{itemize} \item Change in sfInit() MPI startup so sfCluster can run with snow > 0.3 now. \item sfExport now also works in sequential mode (writing to global environment). This prevented sequential execution in some cases. \end{itemize} \item 1.80 (API changes: minor additions) \begin{itemize} \item snowfall passes packages checks of R 2.10.1 without warning or error. Internal state is now only saved in the namespace itself (thanks to Uwe Ligges for the tipp). \item sfExport can now also export objects in a specific namespace (argument 'namespace') \item sfExport: behavior in error case manageable (stopOnError) \item sfExport: smaller bugfixes. \item sfRemoveAll can now also remove hidden names (argument 'hidden') \item sfRemoveAll is more robust now (some minor bugfixes, more checks) \item sfRemoveAll bugfix for multiple removals (thanks to Greggory Jefferis) \item Bugfix on exception list on sfExportAll \item Refactorings in sfTest() \item snowfall now has a NEWS doc ;) \item No warning on Mac OS because of default Mac-R command line arg 'gui' (thanks to Michael Siegel). \end{itemize} \item 1.71 (API changes: none) \begin{itemize} \item Exporting of objects using \texttt{sfExport} is speed up (round 30%) \item Fixed a bug on Windows in \texttt{sfSource} \end{itemize} \item 1.70 (API changes: minor additions, BEHAVIOR CHANGES: logging) \begin{itemize} \item Behavior change: new default: no logging of slave/worker output. \item API change: new argument \texttt{slaveOutfile} on \texttt{sfInit()}. \item API change: new argument \texttt{restore} on \texttt{sfInit()}. \item API change: new argument \texttt{master} on \texttt{sfCat}. \item Windows startup fixed. \item NWS startup fixed. \item sfSapply is working as intended. \item Changing CPU amount during runtime (with multiple sfInit() calls with different settings in a single program) is now possible using socket and NWS clusters. \item Dozens of small glitches inside snowfall fixed (also messages are made more precisly). \item Package vignette slightly extended. \end{itemize} \end{itemize} \bibliographystyle{plain} \bibliography{all-bib} \end{document} snowfall/MD50000644000175100001440000000212512607473775012447 0ustar hornikusers5801700503b43d622d056ab15483df4f *DESCRIPTION 2f2e98aebb7216de714ee3f9945f221b *NAMESPACE d48d163e534e0d5c4de94fab2f3ddce1 *NEWS 0b51233b7236ffc20c8fc68ecd6896d9 *R/clusterFunctions.R a244436455d216d9a29ef33852a7d599 *R/init.R a35e31fe9533fc6f5e892d04df46f352 *R/snowWrappers.R 3bc4f0aa308c788e8a463e00054c5feb *R/snowfall-internal.R d33347446f21ac8b53d84d4b09ef3f15 *R/socketRequest.R 1bb76e59e6f687ecddd7de08f8295305 *R/sysdata.rda 300986acf2e57a8ebc3f83029b9a57dd *build/vignette.rds ca74773497d5ff4ad4ba4ecd641b9e97 *data/config.txt.gz 77060445c8822b20ccdcf0dca6f952ea *data/test.rda 0a19586969fcac3238a562b5b8a489f3 *inst/doc/snowfall.Snw b1dc619edc5ff95de249cacadf72a720 *inst/doc/snowfall.pdf c81c6d6b643b77aaaa91d02de0cf6919 *man/snowfall-a-package.Rd f7de67205fce4f4738aff1e53e712fa3 *man/snowfall-b-init.Rd ccc4dcebbe555aae1f5d2474989e4c8f *man/snowfall-c-calculation.Rd 927cfb30e1702fe1b097c7eaa2c34d60 *man/snowfall-d-tools.Rd 2fad2ee0868985596b79c766c33036a6 *man/snowfall-e-data.Rd 41f93382f016a0396de56fcaf069cfe7 *vignettes/all-bib.bib 0a19586969fcac3238a562b5b8a489f3 *vignettes/snowfall.Snw snowfall/build/0000755000175100001440000000000012254301027013210 5ustar hornikuserssnowfall/build/vignette.rds0000644000175100001440000000042612254301027015551 0ustar hornikusersuJ0ӵm({ Description: Usability wrapper around snow for easier development of parallel R programs. This package offers e.g. extended error checks, and additional functions. All functions work in sequential mode, too, if no cluster is present or wished. Package is also designed as connector to the cluster management tool sfCluster, but can also used without it. Depends: R (>= 2.10), snow Suggests: Rmpi License: GPL Packaged: 2015-10-14 14:48:53 UTC; hornik Repository: CRAN Date/Publication: 2015-10-14 17:42:53 NeedsCompilation: no snowfall/man/0000755000175100001440000000000012254301027012664 5ustar hornikuserssnowfall/man/snowfall-d-tools.Rd0000644000175100001440000002165412103507545016375 0ustar hornikusers\name{snowfall-tools} \alias{snowfall-tools} \alias{sfLibrary} \alias{sfSource} \alias{sfExport} \alias{sfExportAll} \alias{sfRemove} \alias{sfRemoveAll} \alias{sfCat} \alias{sfClusterSplit} \alias{sfClusterCall} \alias{sfClusterEval} \alias{sfClusterEvalQ} \alias{sfClusterSetupRNG} \alias{sfClusterSetupRNGstream} \alias{sfClusterSetupSPRNG} \alias{sfTest} \title{Cluster tools} \usage{ sfLibrary( package, pos=2, lib.loc=NULL, character.only=FALSE, warn.conflicts=TRUE, keep.source=NULL, verbose=getOption("verbose"), version, stopOnError=TRUE ) sfSource( file, encoding = getOption("encoding"), stopOnError = TRUE ) sfExport( ..., list=NULL, local=TRUE, namespace=NULL, debug=FALSE, stopOnError = TRUE ) sfExportAll( except=NULL, debug=FALSE ) sfRemove( ..., list=NULL, master=FALSE, debug=FALSE ) sfRemoveAll( except=NULL, debug=FALSE, hidden=TRUE ) sfCat( ..., sep=" ", master=TRUE ) sfClusterSplit( seq ) sfClusterCall( fun, ..., stopOnError=TRUE ) sfClusterEval( expr, stopOnError=TRUE ) sfClusterSetupRNG( type="RNGstream", ... ) sfClusterSetupRNGstream( seed=rep(12345,6), ... ) sfClusterSetupSPRNG( seed=round(2^32*runif(1)), prngkind="default", para=0, ... ) sfTest() } \arguments{ \item{expr}{expression to evaluate} \item{seq}{vector to split} \item{fun}{function to call} \item{list}{character vector with names of objects to export} \item{local}{a logical indicating if variables should taken from local scope(s) or only from global.} \item{namespace}{a character given a namespace where to search for the object.} \item{debug}{a logical indicating extended information is given upon action to be done (e.g. print exported variables, print context of local variables etc.).} \item{except}{character vector with names of objects not to export/remove} \item{hidden}{also remove hidden names (starting with a dot)?} \item{sep}{a character string separating elements in x} \item{master}{a logical indicating if executed on master as well} \item{...}{additional arguments to pass to standard function} \item{package}{name of the package. Check \code{library} for details.} \item{pos}{position in search path to load library.} \item{warn.conflicts}{warn on conflicts (see "library").} \item{keep.source}{see "library". Please note: this argument has only effect on R-2.x, starting with R-3.0 it will only be a placeholder for backward compatibility.} \item{verbose}{enable verbose messages.} \item{version}{version of library to load (see "library").} \item{encoding}{encoding of library to load (see "library").} \item{lib.loc}{a character vector describing the location of the R library trees to search through, or 'NULL'. Check \code{library} for details.} \item{character.only}{a logical indicating package can be assumed to be a character string. Check \code{library} for details.} \item{file}{filename of file to read. Check \code{source} for details} \item{stopOnError}{a logical indicating if function stops on failure or still returns. Default is \code{TRUE}.} \item{type}{a character determine which random number generator should be used for clusters. Allowed values are "RNGstream" for L'Ecuyer's RNG or "SPRNG" for Scalable Parallel Random Number Generators.} \item{para}{additional parameters for the RNGs.} \item{seed}{Seed for the RNG.} \item{prngkind}{type of RNG, see snow documentation.} } \description{ Tools for cluster usage. Allow easier handling of cluster programming. } \details{ The current functions are little helpers to make cluster programming easier. All of these functions also work in sequential mode without any further code changes. \code{sfLibrary} loads an R-package on all nodes, including master. Use this function if slaves need this library, too. Parameters are identically to the R-build in funtion \code{\link{library}}. If a relative path is given in \code{lib.loc}, it is converted to an absolute path.\\ As default \code{sfLibrary} stops on any error, but this can be prevented by setting \code{stopOnError=FALSE}, the function is returning \code{FALSE} then. On success \code{TRUE} is returned. \code{sfSource} loads a sourcefile on all nodes, including master. Use this function if the slaves need the code as well. Make sure the file is accessible on all nodes under the same path. The loading is done on slaves using \code{source} with fixes parameters: \code{local=FALSE, chdir=FALSE, echo=FALSE}, so the files is loaded global without changing of directory.\\ As default \code{sfSource} stops on any error, but this can be prevented by setting \code{stopOnError=FALSE}, the function is returning \code{FALSE} then. On success \code{TRUE} is returned. \code{sfExport} exports variables from the master to all slaves. Use this function if slaves need acccess to these variables as well. \code{sfExport} features two execution modes: local and global. If using local mode (default), variables for export are searched backwards from current environment to \code{globalenv()}. Use this mode if you want to export local variables from functions or other scopes to the slaves. In global mode only global variables from master are exported.\\ \emph{Note: all exported variables are \emph{global} on the slaves!}\\ If you have many identical named variables in different scopes, use argument \code{debug=TRUE} to view the context the exported variable is coming from.\\ Variables are given as their names or as a character vector with their names using argument \code{list}. \code{sfExportAll} exports all global variables from the master to all slaves with exception of the given list. Use this functions if you want to export mostly all variables to all slaves.\\Argument \code{list} is a character vector with names of the variables \emph{not} to export. \code{sfRemove} removes a list of global (previous exported or generated) variables from slaves and (optional) master. Use this function if there are large further unused variables left on slave. Basically this is only interesting if you have more than one explicit parallel task in your program - where the danger is slaves memory usage exceed.\\ If argument \code{master} is given, the variables are removed from master as well (default is FALSE).\\ Give names of variables as arguments, or use argument \code{list} as a character vector with the names. For deep cleaning of slave memory use \code{sfRemoveAll}. \code{sfRemoveAll} removes all global variables from the slaves. Use this functions if you want to remove mostly all variables on the slaves. Argument \code{list} is a character vector with names of the variables \emph{not} to remove. \code{sfCat} is a debugging function printing a message on all slaves (which appear in the logfiles). \code{sfClusterSplit} splits a vector into one consecutive piece for each cluster and returns as a list with length equal to the number of cluster nodes. Wrapper for \pkg{snow} function \code{clusterSplit}. \code{sfClusterCall} calls a function on each node and returns list of results. Wrapper for \pkg{snow} function \code{clusterCall}. \code{sfClusterEvalQ} evaluates a literal expression on all nodes. Wrapper for \pkg{snow} function \code{clusterEvalQ}. \code{sfTest} is a simple unit-test for most of the build in functions. It runs tests and compares the results for the correct behavior. Note there are some warnings if using, this is intended (as behavior for some errors is tested, too). use this if you are not sure all nodes are running your R-code correctly (but mainly it is implemented for development). } \keyword{package} \seealso{ See \pkg{snow} documentation for details on wrapper-commands: \code{\link[snow]{snow-parallel}} } \examples{ \dontrun{ sfInit( parallel=FALSE ) ## Now works both in parallel as in sequential mode without ## explicit cluster handler. sfClusterEval( cat( "yummie\n" ) ); ## Load a library on all slaves. Stop if fails. sfLibrary( tools ) sfLibrary( "tools", character.only=TRUE ) ## Alternative. ## Execute in cluster or sequential. sfLapply( 1:10, exp ) ## Export global Var gVar <- 99 sfExport( "gVar" ) ## If there are local variables with same name which shall not ## be exported. sfExport( "gVar", local=FALSE ) ## Export local variables var1 <- 1 ## Define global var2 <- "a" f1 <- function() { var1 <- 2 var3 <- "x" f2 <- function() { var1 <- 3 sfExport( "var1", "var2", "var3", local=TRUE ) sfClusterCall( var1 ) ## 3 sfClusterCall( var2 ) ## "a" sfClusterCall( var3 ) ## "x" } f2() } f1() ## Init random number streams (snows functions, build upon ## packages rlecuyer/rsprng). sfClusterCall( runif, 4 ) sfClusterSetupRNG() ## L'Ecuyer is default. sfClusterCall( runif, 4 ) sfClusterSetupRNG( type="SPRNG", seed = 9876) sfClusterCall( runif, 4 ) ## Run unit-test on main functions. sfTest() } }snowfall/man/snowfall-c-calculation.Rd0000644000175100001440000001114311320650361017515 0ustar hornikusers\name{snowfall-calculation} %% Separate alias for cross-references. \alias{snowfall-calculation} \alias{sfClusterMap} \alias{sfClusterApply} \alias{sfClusterApplyLB} \alias{sfClusterApplySR} \alias{sfLapply} \alias{sfSapply} \alias{sfApply} \alias{sfRapply} \alias{sfCapply} \alias{sfMM} \alias{sfRestore} \title{Parallel calculation functions} \usage{ sfClusterApply( x, fun, ... ) sfClusterApplyLB( x, fun, ... ) sfClusterApplySR( x, fun, ..., name="default", perUpdate=NULL, restore=sfRestore() ) sfClusterMap( fun, ..., MoreArgs = NULL, RECYCLE = TRUE ) sfLapply( x, fun, ... ) sfSapply( x, fun, ..., simplify = TRUE, USE.NAMES = TRUE ) sfApply( x, margin, fun, ... ) sfRapply( x, fun, ... ) sfCapply( x, fun, ... ) sfMM( a, b ) sfRestore() } \arguments{ \item{x}{vary depending on function. See function details below.} \item{fun}{function to call} \item{margin}{vector speficying the dimension to use} \item{...}{additional arguments to pass to standard function} \item{simplify}{logical; see \code{sapply}} \item{USE.NAMES}{logical; see \code{sapply}} \item{a}{matrix} \item{b}{matrix} \item{RECYCLE}{see snow documentation} \item{MoreArgs}{see snow documentation} \item{name}{a character string indicating the name of this parallel execution. Naming is only needed if there are more than one call to \code{sfClusterApplySR} in a program.} \item{perUpdate}{a numerical value indicating the progress printing. Values range from 1 to 100 (no printing). Value means: any X percent of progress status is printed. Default (on given value \sQuote{NULL}) is 5).} \item{restore}{logical indicating whether results from previous runs should be restored or not. Default is coming from sfCluster. If running without sfCluster, default is FALSE, if yes, it is set to the value coming from the external program.} } \description{ Parallel calculation functions. Execution is distributed automatically over the cluster.\cr Most of this functions are wrappers for \pkg{snow} functions, but all can be used directly in sequential mode. } \details{ \code{sfClusterApply} calls each index of a given list on a seperate node, so length of given list must be smaller than nodes. Wrapper for \pkg{snow} function \code{clusterApply}. \code{sfClusterApplyLB} is a load balanced version of \code{sfClusterApply}. If a node finished it's list segment it immidiately starts with the next segment. Use this function in infrastructures with machines with different speed. Wrapper for \pkg{snow} function \code{clusterApplyLB}. \code{sfClusterApplySR} saves intermediate results and is able to restore them on a restart. Use this function on very long calculations or it is (however) foreseeable that cluster will not be able to finish it's calculations (e.g. because of a shutdown of a node machine). If your program use more than one parallised part, argument \code{name} must be given with a unique name for each loop. Intermediate data is saved depending on R-filename, so restore of data must be explicit given for not confusing changes on your R-file (it is recommended to only restore on fully tested programs). If restores, \code{sfClusterApplySR} continues calculation after the first non-null value in the saved list. If your parallized function can return null values, you probably want to change this. \code{sfLapply}, \code{sfSapply} and \code{sfApply} are parallel versions of \code{lapply}, \code{sapply} and \code{apply}. The first two use an list or vector as argument, the latter an array. \code{parMM} is a parallel matrix multiplication. Wrapper for \pkg{snow} function \code{parMM}. \emph{\code{sfRapply} and \code{sfCapply} are not implemented atm.} } \keyword{package} \seealso{ See snow documentation for details on commands: \code{\link[snow]{snow-parallel}} } \examples{ \dontrun{ restoreResults <- TRUE sfInit(parallel=FALSE) ## Execute in cluster or sequential. sfLapply(1:10, exp) ## Execute with intermediate result saving and restore on wish. sfClusterApplySR(1:100, exp, name="CALC_EXP", restore=restoreResults) sfClusterApplySR(1:100, sum, name="CALC_SUM", restore=restoreResults) sfStop() ## ## Small bootstrap example. ## sfInit(parallel=TRUE, cpus=2) require(mvna) data(sir.adm) sfExport("sir.adm", local=FALSE) sfLibrary(cmprsk) wrapper <- function(a) { index <- sample(1:nrow(sir.adm), replace=TRUE) temp <- sir.adm[index, ] fit <- crr(temp$time, temp$status, temp$pneu, failcode=1, cencode=0) return(fit$coef) } result <- sfLapply(1:100, wrapper) mean( unlist( rbind( result ) ) ) sfStop() } }snowfall/man/snowfall-b-init.Rd0000644000175100001440000002155212254300762016172 0ustar hornikusers\name{snowfall-init} \alias{snowfall-init} \alias{sfInit} \alias{sfStop} \alias{sfParallel} \alias{sfCpus} \alias{sfNodes} \alias{sfType} \alias{sfIsRunning} \alias{sfSocketHosts} \alias{sfGetCluster} \alias{sfSession} \alias{sfSetMaxCPUs} \title{Initialisation of cluster usage} \usage{ sfInit( parallel=NULL, cpus=NULL, type=NULL, socketHosts=NULL, restore=NULL, slaveOutfile=NULL, nostart=FALSE, useRscript=FALSE ) sfStop( nostop=FALSE ) sfParallel() sfIsRunning() sfCpus() sfNodes() sfGetCluster() sfType() sfSession() sfSocketHosts() sfSetMaxCPUs( number=32 ) } \arguments{ \item{parallel}{Logical determinating parallel or sequential execution. If not set values from commandline are taken.} \item{cpus}{Numerical amount of CPUs requested for the cluster. If not set, values from the commandline are taken.} \item{nostart}{Logical determinating if the basic cluster setup should be skipped. Needed for nested use of \pkg{snowfall} and usage in packages.} \item{type}{Type of cluster. Can be 'SOCK', 'MPI', 'PVM' or 'NWS'. Default is 'SOCK'.} \item{socketHosts}{Host list for socket clusters. Only needed for socketmode (SOCK) and if using more than one machines (if using only your local machine (localhost) no list is needed).} \item{restore}{Globally set the restore behavior in the call \code{sfClusterApplySR} to the given value.} \item{slaveOutfile}{Write R slave output to this file. Default: no output (Unix: \code{/dev/null}, Windows: \code{:nul}). If using sfCluster this argument has no function, as slave logs are defined using sfCluster.} \item{useRscript}{Change startup behavior (snow>0.3 needed): use shell scripts or R-script for startup (R-scripts beeing the new variant, but not working with sfCluster.} \item{nostop}{Same as noStart for ending.} \item{number}{Amount of maximum CPUs useable.} } \description{ Initialisation and organisation code to use \pkg{snowfall}. } \details{ \code{sfInit} initialisise the usage of the \pkg{snowfall} functions and - if running in parallel mode - setup the cluster and \pkg{snow}. If using \code{sfCluster} management tool, call this without arguments. If \code{sfInit} is called with arguments, these overwrite \code{sfCluster} settings. If running parallel, \code{sfInit} set up the cluster by calling \code{makeCluster} from \pkg{snow}. If using with \code{sfCluster}, the initialisation also contains management of lockfiles. If this function is called more than once and current cluster is yet running, \code{sfStop} is called automatically. Note that you should call \code{sfInit} before using any other function from \pkg{snowfall}, with the only exception \code{sfSetMaxCPUs}. If you do not call \code{sfInit} first, on calling any \pkg{snowfall} function \code{sfInit} is called without any parameters, which is equal to sequential mode in \pkg{snowfall} only mode or the settings from sfCluster if used with sfCluster. This also means, you cannot check if \code{sfInit} was called from within your own program, as any call to a function will initialize again. Therefore the function \code{sfIsRunning} gives you a logical if a cluster is running. Please note: this will not call \code{sfInit} and it also returns true if a previous running cluster was stopped via \code{sfStop} in the meantime. If you use \pkg{snowfall} in a package argument \code{nostart} is very handy if mainprogram uses \pkg{snowfall} as well. If set, cluster setup will be skipped and both parts (package and main program) use the same cluster. If you call \code{sfInit} more than one time in a program without explicit calling \code{sfStop}, stopping of the cluster will be executed automatically. If your R-environment does not cover required libraries, \code{sfInit} automatically switches to sequential mode (with a warning). Required libraries for parallel usage are \pkg{snow} and depending on argument \code{type} the libraries for the cluster mode (none for socket clusters, \pkg{Rmpi} for MPI clusters, \pkg{rpvm} for PVM clusters and \pkg{nws} for NetWorkSpaces). If using Socket or NetWorkSpaces, \code{socketHosts} can be used to specify the hosts you want to have your workers running. Basically this is a list, where any entry can be a plain character string with IP or hostname (depending on your DNS settings). Also for real heterogenous clusters for any host pathes are setable. Please look to the acccording \pkg{snow} documentation for details. If you are not giving an socketlist, a list with the required amount of CPUs on your local machine (localhost) is used. This would be the easiest way to use parallel computing on a single machine, like a laptop. Note there is limit on CPUs used in one program (which can be configured on package installation). The current limit are 32 CPUs. If you need a higher amount of CPUs, call \code{sfSetMaxCPUs} \emph{before} the first call to \code{sfInit}. The limit is set to prevent inadvertently request by single users affecting the cluster as a whole. Use \code{slaveOutfile} to define a file where to write the log files. The file location must be available on all nodes. Beware of taking a location on a shared network drive! Under *nix systems, most likely the directories \code{/tmp} and \code{/var/tmp} are not shared between the different machines. The default is no output file. If you are using \code{sfCluster} this argument have no meaning as the slave logs are always created in a location of \code{sfClusters} choice (depending on it's configuration). \code{sfStop} stop cluster. If running in parallel mode, the LAM/MPI cluster is shut down. \code{sfParallel}, \code{sfCpus} and \code{sfSession} grant access to the internal state of the currently used cluster. All three can be configured via commandline and especially with \code{sfCluster} as well, but given arguments in \code{sfInit} always overwrite values on commandline. The commandline options are \option{--parallel} (empty option. If missing, sequential mode is forced), \option{--cpus=X} (for nodes, where X is a numerical value) and \option{--session=X} (with X a string). \code{sfParallel} returns a logical if program is running in parallel/cluster-mode or sequential on a single processor. \code{sfCpus} returns the size of the cluster in CPUs (equals the CPUs which are useable). In sequential mode \code{sfCpus} returns one. \code{sfNodes} is a deprecated similar to \code{sfCpus}. \code{sfSession} returns a string with the session-identification. It is mainly important if used with the \code{sfCluster} tool. \code{sfGetCluster} gets the \pkg{snow}-cluster handler. Use for direct calling of \pkg{snow} functions. \code{sfType} returns the type of the current cluster backend (if used any). The value can be SOCK, MPI, PVM or NWS for parallel modes or "- sequential -" for sequential execution. \code{sfSocketHosts} gives the list with currently used hosts for socket clusters. Returns empty list if not used in socket mode (means: \code{sfType() != 'SOCK'}). \code{sfSetMaxCPUs} enables to set a higher maximum CPU-count for this program. If you need higher limits, call \code{sfSetMaxCPUs} before \code{sfInit} with the new maximum amount. } \keyword{package} \seealso{ See snow documentation for details on commands: \code{link[snow]{snow-cluster}} } \examples{ \dontrun{ # Run program in plain sequential mode. sfInit( parallel=FALSE ) stopifnot( sfParallel() == FALSE ) sfStop() # Run in parallel mode overwriting probably given values on # commandline. # Executes via Socket-cluster with 4 worker processes on # localhost. # This is probably the best way to use parallel computing # on a single machine, like a notebook, if you are not # using sfCluster. # Uses Socketcluster (Default) - which can also be stated # using type="SOCK". sfInit( parallel=TRUE, cpus=4 ) stopifnot( sfCpus() == 4 ) stopifnot( sfParallel() == TRUE ) sfStop() # Run parallel mode (socket) with 4 workers on 3 specific machines. sfInit( parallel=TRUE, cpus=4, type="SOCK", socketHosts=c( "biom7", "biom7", "biom11", "biom12" ) ) stopifnot( sfCpus() == 4 ) stopifnot( sfParallel() == TRUE ) sfStop() # Hook into MPI cluster. # Note: you can use any kind MPI cluster Rmpi supports. sfInit( parallel=TRUE, cpus=4, type="MPI" ) sfStop() # Hook into PVM cluster. sfInit( parallel=TRUE, cpus=4, type="PVM" ) sfStop() # Run in sfCluster-mode: settings are taken from commandline: # Runmode (sequential or parallel), amount of nodes and hosts which # are used. sfInit() # Session-ID from sfCluster (or XXXXXXXX as default) session <- sfSession() # Calling a snow function: cluster handler needed. parLapply( sfGetCluster(), 1:10, exp ) # Same using snowfall wrapper, no handler needed. sfLapply( 1:10, exp ) sfStop() } } snowfall/man/snowfall-e-data.Rd0000644000175100001440000000054711320631665016146 0ustar hornikusers\name{snowfall-data} \docType{data} \alias{config} \alias{sfOption} \alias{f1} \alias{f2} \title{Internal configuration and test data} \description{ Internal configuration and test data. Only used for internal setup and testing. } \usage{ config f1 f2 sfOption } \format{A matrix containing basic predefined configuration informations.} \keyword{datasets}snowfall/man/snowfall-a-package.Rd0000644000175100001440000000744011320650215016613 0ustar hornikusers\name{snowfall-package} \alias{snowfall-package} \alias{snowfall} \docType{package} \title{Toplevel useability wrapper for snow to make parallel programming even more easy and comfortable. All functions are able to run without cluster in sequential mode. Also snowfall works as connector to the cluster management program sfCluster, but can also run without it.} \description{ \pkg{snowfall} is designed to make setup and usage of \pkg{snow} more easier. It also is made ready to work together with \code{sfCluster}, a ressource management and runtime observation tool for R-cluster usage. } \details{ \tabular{ll}{ Package: \tab snowfall\cr Type: \tab Package\cr Version: \tab 1.61\cr Date: \tab 2008-11-01\cr License: \tab GPL\cr } } \section{Initialisation}{Initalisation via \code{sfInit} must be called before the usage of any of the \pkg{snowfall} internal functions. \code{sfStop} stopps the current cluster. Some additional functions give access to build-in functions (like \code{sfParallel}, \code{sfCpus} etc.). } \section{Calculations}{The are plenty of function to execute parallel calculations via \pkg{snowfall}. Most of them are wrappers to the according \pkg{snow} functions, but there are additional functions as well. Most likely the parallel versions of the R-buildin applies are interesting: \code{sfLapply}, \code{sfSapply} and \code{sfApply}. For better cluster take a look at the load balanced \code{sfClusterApplyLB} and the function with restore possibilities: \code{sfClusterApplySR}. } \section{Tools}{Various tools allow an easier access to parallel computing: \code{sfLibrary} and \code{sfSource} for loading code on the cluster, \code{sfExport}, \code{sfExportAll}, \code{sfRemoveAll} and \code{sfRemoveAll} for variable sperading on the cluster. And some more. } \section{sfCluster}{\pkg{snowfall} is also the R-connector to the cluster management program \code{sfCluster}. Mostly all of the communication to this tool is done implicit and directly affecting the initialisation via \code{sfInit}. Using \code{sfCluster} makes the parallel programming with \pkg{snowfall} even more practicable in real life environments. For futher informations about the usage of \code{sfCluster} look at its documentation. } \author{ Jochen Knaus Maintainer: Jochen Knaus , } \references{ \pkg{snow} (Simple Network of Workstations):\cr http://cran.r-project.org/src/contrib/Descriptions/snow.html\cr\cr \code{sfCluster} (Unix management tool for \pkg{snowfall} clusters):\cr http://www.imbi.uni-freiburg.de/parallel\cr } \keyword{package} \seealso{ Snowfall Initialisation: \code{\link{snowfall-init}}\cr Snowfall Calculation: \code{\link{snowfall-calculation}}\cr Snowfall Tools: \code{\link{snowfall-tools}}\cr Optional links to other man pages, e.g. \code{\link[snow]{snow-cluster}} } \examples{ \dontrun{ # Init Snowfall with settings from sfCluster ##sfInit() # Init Snowfall with explicit settings. sfInit( parallel=TRUE, cpus=2 ) if( sfParallel() ) cat( "Running in parallel mode on", sfCpus(), "nodes.\n" ) else cat( "Running in sequential mode.\n" ) # Define some global objects. globalVar1 <- c( "a", "b", "c" ) globalVar2 <- c( "d", "e" ) globalVar3 <- c( 1:10 ) globalNoExport <- "dummy" # Define stupid little function. calculate <- function( x ) { cat( x ) return( 2 ^ x ) } # Export all global objects except globalNoExport # List of exported objects is listed. # Work both parallel and sequential. sfExportAll( except=c( "globalNoExport" ) ) # List objects on each node. sfClusterEvalQ( ls() ) # Calc something with parallel sfLappy cat( unlist( sfLapply( globalVar3, calculate ) ) ) # Remove all variables from object. sfRemoveAll( except=c( "calculate" ) ) } }