From da1818a1339d88e45e867e835a91568f7fc21cf5 Mon Sep 17 00:00:00 2001 From: Wen-Tian-Pineapple <74345454+Wen-Tian-Pineapple@users.noreply.github.com> Date: Wed, 8 Nov 2023 23:18:12 -0500 Subject: [PATCH] Machine Learning Optimization Template (#238) Squash and merge of ML template PR #238 --- openfasoc/MLoptimization/README.md | 46 ++++ openfasoc/MLoptimization/eval.py | 118 +++++++++ openfasoc/MLoptimization/gen_spec.py | 41 ++++ openfasoc/MLoptimization/image1.png | Bin 0 -> 75531 bytes openfasoc/MLoptimization/image2.png | Bin 0 -> 50214 bytes openfasoc/MLoptimization/model.py | 47 ++++ openfasoc/MLoptimization/run_training.py | 286 ++++++++++++++++++++++ openfasoc/MLoptimization/sample_spec.yaml | 100 ++++++++ 8 files changed, 638 insertions(+) create mode 100644 openfasoc/MLoptimization/README.md create mode 100644 openfasoc/MLoptimization/eval.py create mode 100755 openfasoc/MLoptimization/gen_spec.py create mode 100644 openfasoc/MLoptimization/image1.png create mode 100644 openfasoc/MLoptimization/image2.png create mode 100644 openfasoc/MLoptimization/model.py create mode 100644 openfasoc/MLoptimization/run_training.py create mode 100644 openfasoc/MLoptimization/sample_spec.yaml diff --git a/openfasoc/MLoptimization/README.md b/openfasoc/MLoptimization/README.md new file mode 100644 index 000000000..7e661dbc3 --- /dev/null +++ b/openfasoc/MLoptimization/README.md @@ -0,0 +1,46 @@ +# Machine Learning Optimization +Code for reinforcement learning loop with openfasoc generators for optimizing metrics + +## Code Setup +The code is setup as follows: + +The top level directory contains two sub-directories: +* model.py: top level RL script, used to set hyperparameters and run training +* run_training.py: contains all OpenAI Gym environments. These function as the agent in the RL loop and contain information about parameter space, valid action steps and reward. +* eval.py: contains all of the code for evaluation +* gen_spec.py: contains all of the random specification generation + +## Training +Make sure that you have OpenAI Gym and Ray installed. To do this, run the following command: + +To generate the design specifications that the agent trains on, run: +``` +python3.10 gen_specs.py +``` +The result is a yaml file dumped to the ../generators/gdsfactory-gen/. + +To train the agent, open ipython from the top level directory and then: +``` +python3.10 model.py +``` +The training checkpoints will be saved in your home directory under ray\_results. Tensorboard can be used to load reward and loss plots using the command: + +``` +tensorboard --logdir path/to/checkpoint +``` + +## Validation +The evaluation script takes the trained agent and gives it new specs that the agent has never seen before. To generate new design specs, run the gen_specs.py file again with your desired number of specs to validate on. To run validation: + +``` +python3.10 eval.py +``` + +The evaluation result will be saved to the ../generators/gdsfactory-gen/. + +## Results +Please note that results vary greatly based on random seed and spec generation (both for testing and validation). An example spec file is provided that was used to generate the results below. + +
+ +
diff --git a/openfasoc/MLoptimization/eval.py b/openfasoc/MLoptimization/eval.py new file mode 100644 index 000000000..cb617e57a --- /dev/null +++ b/openfasoc/MLoptimization/eval.py @@ -0,0 +1,118 @@ +#training import +import numpy as np +import gym +import ray +import ray.tune as tune +from ray.rllib.algorithms.ppo import PPO +from run_training import Envir +from ../generators/gdsfactory-gen/sky130_nist_tapeout import single_build_and_simulation +import pickle +import yaml +from pathlib import Path +import argparse + +def unlookup(norm_spec, goal_spec): + spec = -1*np.multiply((norm_spec+1), goal_spec)/(norm_spec-1) + return spec + +specs = yaml.safe_load(Path('newnew_eval_3.yaml').read_text()) + +# +#training set up +env_config = { + "generalize":True, + "num_valid":2, + "save_specs":False, + "inputspec":specs, + "run_valid":True, + "horizon":25, + } + +config_eval = { + #"sample_batch_size": 200, + "env": Envir, + "env_config":{ + "generalize":True, + "num_valid":2, + "save_specs":False, + "inputspec":specs, + "run_valid":True, + "horizon":25, + }, + } + +parser = argparse.ArgumentParser() +parser.add_argument('--checkpoint_dir', '-cpd', type=str) +args = parser.parse_args() +env = Envir(env_config=env_config) + +agent = PPO.from_checkpoint("/home/wentian/ray_results/brandnewBound_1/PPO_Envir_7fc09_00000_0_2023-08-18_20-40-42/checkpoint_000015") + + + +norm_spec_ref = env.global_g +spec_num = len(env.specs) + + +rollouts = [] +next_states = [] +obs_reached = [] +obs_nreached = [] +action_array = [] +action_arr_comp = [] +rollout_steps = 0 +reached_spec = 0 +f = open("newnewnew_eval__3.txt", "a") + +while rollout_steps < 100: + rollout_num = [] + state, info = env.reset() + + done = False + truncated = False + reward_total = 0.0 + steps=0 + f.write('new----------------------------------------') + while not done and not truncated: + action = agent.compute_single_action(state) + action_array.append(action) + + next_state, reward, done, truncated, info = env.step(action) + f.write(str(action)+'\n') + f.write(str(reward)+'\n') + f.write(str(done)+'n') + print(next_state) + print(action) + print(reward) + print(done) + reward_total += reward + + rollout_num.append(reward) + next_states.append(next_state) + + state = next_state + + norm_ideal_spec = state[spec_num:spec_num+spec_num] + ideal_spec = unlookup(norm_ideal_spec, norm_spec_ref) + if done == True: + reached_spec += 1 + obs_reached.append(ideal_spec) + action_arr_comp.append(action_array) + action_array = [] + pickle.dump(action_arr_comp, open("action_arr_test", "wb")) + else: + obs_nreached.append(ideal_spec) #save unreached observation + action_array=[] + f.write('done----------------------------------------') + rollouts.append(rollout_num) + print("Episode reward", reward_total) + rollout_steps+=1 + #if out is not None: + #pickle.dump(rollouts, open(str(out)+'reward', "wb")) + pickle.dump(obs_reached, open("opamp_obs_reached_test","wb")) + pickle.dump(obs_nreached, open("opamp_obs_nreached_test","wb")) + + f.write("Specs reached: " + str(reached_spec) + "/" + str(len(obs_nreached))) + print("Specs reached: " + str(reached_spec) + "/" + str(len(obs_nreached))) + +print("Num specs reached: " + str(reached_spec) + "/" + str(1)) \ No newline at end of file diff --git a/openfasoc/MLoptimization/gen_spec.py b/openfasoc/MLoptimization/gen_spec.py new file mode 100755 index 000000000..60c5023f2 --- /dev/null +++ b/openfasoc/MLoptimization/gen_spec.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +## Generate the design specifications and then save to a pickle file + +import numpy as np +import random +import yaml +import os +import argparse + +def gen_data(env, num_specs): + + specs_range = { + "gain_min" : [float(1000338000.0), float(3000338000.0)], + "FOM" : [float(5*10**11), float(5*10**11)] + } + specs_range_vals = list(specs_range.values()) + specs_valid = [] + for spec in specs_range_vals: + if isinstance(spec[0],int): + list_val = [random.randint(int(spec[0]),int(spec[1])) for x in range(0,num_specs)] + else: + list_val = [random.uniform(float(spec[0]),float(spec[1])) for x in range(0,num_specs)] + specs_valid.append(tuple(list_val)) + i=0 + for key,value in specs_range.items(): + specs_range[key] = specs_valid[i] + i+=1 + + output = str(specs_range) + with open(env, 'w') as f: + f.write(output.replace('(','[').replace(')',']').replace(',',',\n')) + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('--num_specs', type=str) + args = parser.parse_args() + + gen_data("newnew_eval_3.yaml", int(50)) + +if __name__=="__main__": + main() \ No newline at end of file diff --git a/openfasoc/MLoptimization/image1.png b/openfasoc/MLoptimization/image1.png new file mode 100644 index 0000000000000000000000000000000000000000..b94630ef7ad88bd814ccd718743b42f3701db562 GIT binary patch literal 75531 zcmZ_0WmH^G_BBihgeD0dAh^3b1b26r;O;aYEJ%>x?(QDk-Q8USje8T^o+k5~`Omy- zy?p4q`qEvu?zvT`_St(^hbYL2BfQ6c4+RBSOKTX2Y+`YIM$c1w&f#W?PZwM*Im!<_vIuaB@C+?Uo?1JZn^62CtSg5U|*^dV8)50+pyyI|5pdz`4Wb=IZ43C z^bGj3m&sP3%;#xVotXOVZWW;|9Bys|caU0L_Y=$(e-B3MGTs_^iyfYuj4tZ|qZmdY zo>{hNc0^I&xcs}zPJ~dE-!qj23ObC&!we|=z6><0b+HlXWeP1^4^6Ii3pcRhVEk9) zw})X4eBA^ixHOD_1;35g!4yoQ)zi4kpgX{)z4 og28nx_m(tdf)cOIqDH%>7X?U@Zwu$5Z*x_U-4LrS5q{;Hy zX%fq3_}4Ix0w2$>!q5WQ17_sc#!-G<47hd4Cxt<$a)=iPeO4}DE5z@Bo7QGp>Ly46 z?MEqIFS$v(J~lXNd=a@x8+8p{0i`>^M0|R4Q^lhMvvTN$AI!suG7kz1AkFzfDnTJP zy@jM$fBCDABL=#d#UN8gYNn0egK-8C!Z$__#pb5@qEUz~XE#n1@}5swqH20S-zmay zEeKt}c?I~tY;Z!ei+yG><{&(Hu9*;v#ab1dlu&|8{8$@jzAJhBvVJ4xJt#+wGr+Fw zA%j1OpDC-hw5JlQ^>SkN-pg(P#a#JSTBUjvw+9CNYLlRY$p#S(3$rclg?)n%hgj8M z1j*r$t%!!1)ze-AK-Ao?xPn!8v)X0&E)kyN@bjD@&|?zA#20DOvbULx8yq)}-Z~3H zoxw!v;XbE*nBt{JqZxR2*egkaqvsx+2YHk}G}zLP=v5m5vB{&6crE L}p&)z=`K6;&F;Q6w@>c}{6e0mGnevkRb>>Qsf8>^xKjCi0id~KGYM+1_I zHLF0!HMszw$7DajyzGh2qo9z7c@W>ZFC{E04U|DiEgT#Y3jVn{wb^K}O#H=m>NnfU zjVt8fSmb^jP|QUb&b#8$&vpqE@y5@z;!+MuOJF0iWEHT-fMJzOdHX#ChH0*5PUVV< zg@4&E5tvBRsegRBV1d|)zeC2Z8dMXRobPYV#5RxtyaCUV4Em}jk%iM&!emVmOCxOY zNs`rK{~=^O7W~@6G$5ns2RxA?js{YG B5*EI2_Z9+%fWW-dUI>4U&>N81tQd8zbSSiU4xoJX3T D%Zz=l;QRS_&;16X&T|jS2B=_XgIE~mMKM2xU&VSQb4r3@ zGo#`7w4j}+q^nnZUX`H1BhqJZfPFv9pX_HmHlJr*hWN9)eAFCB;{1y6st z*5DB#6R5NAmU+lgYTt-0o#Cmc xLte qI`Iq;ER zksJamjmN&ITDqPp*qC69gQFyK2B-?q84VL9i85Bzzn4+}UBL{x6waFxIPNk^DGz z$=tCGaqr(4zpwlTO~ugm=}t|EI$3AlEO3w+9D@F%nU-Y)8i`g?8Uy~(#H%{_y}`yV z!09t&Zn}Ke&To&q>c*hk`I$}w>M}tZ4xC1aiA^WDYwF5mj+<*yAE7zYi>`XxKhmen z5N|+a|Hg#8G62p*#~i0CPwwgZwh7yxTz8 2PB(w%~I)#n+XkHW)b8-yX$ifMY_ji5TtnZWDm`<18sdsOXRF#bn zJLS* LKD9-a`;wza^Sy8%-`Ar=XR!D)K-2wh5dDDEZ z^s|qaxV~~B-{n87+}VVqwY?g2JbtfLcbIm_9P&$ofnXJ* Iu$|3OC%WQO>zfhN#S0&yDgtrvjz#@a>#Zb%ThD>S;_Hn+MKuv;8QkA;N`UUO6jF&U)eSU(z#7tgJ-y;Q2rkj$zd^Xj_6S~qJn4&tv@9%VgE8R@0F zK#CAp5Q1_y^!--<8JhgOf6zn+F}n_=!5!gc`~FO8d!VvjnFzw?<=dI*d `x=vi{Mso z-gC0cI&hcb;r5@af3Ia>eZ| zbwQo|Y^jWtmBRcp+Z^03xsq>`5k2SV0<0b-(7^PDE`~1mexw74AtCzWnT#=LE?4C9 z-8*QyP@Dj3$t=&BZi70q!Y%G&3h?>%l8l2}J%qN8EkT@5rxjK_OPE-nIrN4|I48an zgEknrsFf8l#I-Jq4{rBp0ML*MN^GC>nwSK^^l_g6oMc~@&Oct_3Q97Kwj1=-Hrjbi z#%huN+;^+UIae^U{1nmBqvNfU_yyWU+Bh=1a6-^mxH3xS@9@7#u%ZJ(ehSZ;e&uvk z fOOwpasT;3&78;R0^9QVcYP}lM5L#^vQ4eA4X#OtNagdq`Q4?*kG%4p vla(^dv%A-U7lApe;~&>E0c zBDV^ Pm<=OTkgw cye7mwVKb8>)z|C~OS4IW&|GCZ;r3oz@F=5Vkm{8An-i(N5jhb+ zJ8~6YsP^XaC?gSNiKH9Ef2$GLVQiCafjGy&95rcSR3Pe{K$lpSs23qYCdBs#_T&xu z3338es!K(5+VYoT+-1FHn`NpVJuOPIT(}WmeBZdhc7;&K3kbIba# K5^)V*ApL)$J2FSrfv~xoEt~wwiNw6bW>vWZBO;{_NRtd$RoQUXth$2)b{oACF z|ENs$yi?NAO~fnb|4Pp1zk~Kezns|WBX-@ui dbuS#O zTR2Q o&(10ifPQsT-cMgCEw(8{;9#(3vl&p; zE!E74#P_}DiH~1KA`AVks;*9g&K9qDpYzsPNpV&FTjdJ%lbUT*`9N6W@geeBF_;e* z<=G&n@MLVq);8Sc-(G;!d{4cmd#~z=5EE$xa=QhTeJB8FtdKDw$l+Kc8UbhaxQ#+4 z>p?z)mP;lpS>K4gFhjPRMUr}3gac6z$j=Q`yDlo8MM4zS-lHqtTyj0B&KiT B&GyaRFSq*{a#* zNq+2(k+5e?#Tj8AK5c8+IoV#P7j^Hl)>Wd`G+3&om7vW_Xiv6REz3%bzDBKv=PF0w zQ|K#gkhV3i(GI2`NL#1l=7CwP#FnuX(~^1G{L2yea$0P&FeBc*i{{z>NcEwz*>M~a zXtc8g!uUZonp!)C*e1`0oY)-=uMU66)3)ygH1>moiLCS94C0K$T~O0U1mi-}$sHoM zdIlXw_aJO>__PmF6^aZ5+-n{q!7_{AXI=KE7i#qss`HmiPD;WBTqEbGE2$EDT%RcV z3`!U15Z8!}|CG?5R4|Rz1^CFJfwFletN`%uuN?DSb?uhwTnjU3xGB`BftJQcyXfHQ zl1U(8H*YiM;HIv>iM~dp&XCoe7}Y4v?Dm$Hs!iwX=3S?+Fk7)|52MCk;*J@Y=0`u7 zQ&Sxm3Y;E0*5 =8FYkzdu1_z$LeGt&DD)^Dr?ZEUWei zJw8sROQn8K$}wA9!AV%^QW@iY_hfvZbx>Vs8JWg+=yZS%{F fILJ@P4;HK~j@wbM~9Au)}EA*M8HrMr8uFjk+4{IVDXgbQ42IR(_hLGF%y zkN&d*XmQaDAu!~_Y?P 5ioDe@nr@(uMk T9O;;Jv|$T zXQ6O;Qp0`vIH}%BDl=o9$3Qd&M-^V3;i)8<6*HT39n)@zI1tc&BM|?mVjSL`p=G4M z>Mf~ylSRTk&}HP1{Gs$_Wd2E; Nh$6!yLGvMjnp#atOhc @V1TeeSsG{kpzDE{T(xtLAJ(l7QoUNFQM#`JR+M z*n@nrKMdQQIGYN^{4T%fm1=?xY*F+pvI VZH>{OW eLTe`jymOLxDbmMO?GtDB_8AeD6A zdH!qaPM|{>eES7X%9&!mmCh;qCV2cvYg|{HeIPdaVeQ{p{JQ{Ff_>YKGL}KZr!P6q z^t9j5xPhS @!g2P)q+iW}}fWk`&1(P96E@Di&>ueFD^N zma%eLyz{O!x3?3|Sti<-#h!}O$z(+I!D4^to0n;W!(E16$GTMFgq}Yero*LTb{A8z zsC5Hgiklj{zy5|dXv50UR4SD{LSWBfDcO~J>7Nj#^I1(q5z-eX-HU3-A}O0-sH@3% zk9Vl8Xm(Oq5*m4I2RfJ-fH}^{l^x6))OjP&OiAu22QAkU-nh=BZ`7SPB(&EBh-4%Z zq6F@zxiy#{CXDPa+V17888vCy&s_O iRFE!o@!8|h$&WLHW!PqqCY>f-}uRCE~ z1tVR*cr43F+Gce`w@(;ht4~bh6e9{4`LX$$1_x@n&8jmUMAwFSUHs{mzX#biI;K7? zK^5lEiNbAot&(sjEVB-ILc=h7JNJYdA&aXdXbZ-UL8vdq!fbQ`x_vFlbjA1CLk!zE zb*ab$WFW<(b FvNfs{WWG^^-)*H;}kP z4mG8>&QOI%L unD=pcZK6{Nc-RN()ot(V%rO=~;6h3t_8dRP$V1qSoYFtRXbIhe2~;va)Hv z%NXdR)*ReIpsOqgW~an7)tQJvm$kjw9THAp)Za2Y8v5pB1WFSNl=+gJBDM2r1OX!y z|6RMLwT?-ul*6W+=U>Ak-LQ$mCS{~(W&=aklsrFl$Ty`pB_Y+NwyUzV&DRw3^cC`2 z!47Uj#Z2tx04X&kW$?DuHkw|`N#ebblVF>n-cT+rd2On^T zsjfYAxqQ6}#A4NYM9Oas&f9knm=;1)u`GZoH+cm(-=`%xi*M66i33xwLycXiIOy6K z1( #q=dz>+T< zmxM=&%(;BI@wcOw!_)y(uMw#xPKEEh$TtcitD98%7yLKVH|Klq9Cs%jJ)VfpBLhu& z`_(Zt>II!`@Kq?Eup5Md4#syAZMhQA%?!@ycQ3$U{quV9`7ECQ5m%x-GeHI)M;mqo z&BWd+ |-trnSUh#jjX-H(fFw75w47K{2LOMInPU2!2!oF9p(; zr$|$)eAI_K;^I?}8Znlm9O1q4>(m!JIe=!D*0mz>P4JJWzLPg9gpiJtd4dc*Y87de zq@^fAg^@0%If$Q{-szV&iwzkbgtA3gQKu1WTS*nUuPrN+UK%UmVNGS$3`tqXi)hW4 zVUnt)24#;21T!slsu>~^?{ps{B(Jb6Pblv_Yz+3lZY-;`iFvyn&@h2pD_qiE9*&T6 ztGilFUSUcosT|Sysi?~fNiPJ!W>*68raQ0N%oWqA-StHz`7ykR%<&xxSm&yVko_i* znh;t2`>dFf|G6lriBJY#`zYp+>Oh#%>2-vcJk4=Z3s>3;;X-t0bc3rJE7nI2()c<- zM^7U5f}?28LFk2RWn8$zj+bb{_VTFfb^-hi+y jEI$^H|SnbA;W?UTBt>d(o4AFAiG%v#j)iK{_S%w|Vn4e&{>7!C-wqSl`_`Df4 z_;0fMUxN0Lz<;^@NMt-pxDXsYy55tx2GdEl?;WD?-3iMRNKH!c`AC14ni|=Z30%{= z4Hso>5Boa&W&aCmSzmD97kehR2_XcHv{<0Mj1utIiIH!2N85&`pW!D#7Nn#}{)usA zps`i2Q_#+qPyLnj-Z?Wcm^CvVoXOwyNBsAKfM4XVwyDD;O!U>XBYiq#0&fynBGWsa z$f_zKdT@`~s^MV1@RBPd%O=Y#MgNK<-`vG>+mT2YgWg5Uv0|R?DQ4)c!k$@!HV!Vx z^dQFgw^`6#Zy%jjKh#sT$!ev3OB!9E5zmF4wbJYhz9YgnTcih0B-m1-NdDT2hF|?W zRUjn%_LNFAc=bFfN1ywcyR!60pWo#^WK5hi@x!!)yex$|``+g1pYWK>j=1KTI1|pI z-rgf@3)R!C0W7INaBH{@&1fa$V`{?D$DD-ACyK)(%h6~0w4)n3&-Ln72EmoAH*a_m znb{n>U;im(zgn=`mk^BonCl{EE^H;xznUacvXrNSm;bf+__SAW^c6Rc0FM~0Lx9_t zcP{vVNF5*f{?vDq0_lSUk_}=s^XM*Z?Y5qfbi;yRtbm+H8rvM>udY9HfoY1J3R7<; zgh|JDXNBm$Iru6zX`iHa2{6KTH~H!sWjxFDFP%uz=Zxrxl(GKiyh!<|n>Vh%&)0+A zqbxctOV3wp-}`u7j`SH+;sfF8=FI0L(HQVwaCLa-s=|tS>Gy}&V(7Hv&F8EW9*4ed z7lD^=_ hOD#KFyb6?sc0SRGGI4 ;Woa&IWWIh*efRuI0n*38aG73I* zp)d~CVT1{(`3giDD~HuOAq(Qjg2po)KpMrMUL}n=>^o^Nc` (rSL0K#&5G- rS|^o`F$Dwo%3@#U^Xjq5|o1Vu2|&uzzW6L-;& zoC1HET5kvHqAPqAA%@_U^Rc}N!~g29)+i`+c}F=CvRg w-^dH+cD&>(jwW_~hzHXqI#)pOU!)%y}|2K}E_CrpX!f zYyy}D%MdPpt*km3F^+K#W+U5nE%*%+#vc+TWs5Qs-rz9j5c`;)afwR&2NN|V(&fPg zQWJ+qcTW@jEwuDptn;O8H|68>bTF~2kxAF#i8}SlFk?}%zZ=n;*LFs8sg*dpUq+6j z>?D@3Ed^0G@xnMy&lr&>VjTm7&w6Wef-l8Hiw~bD1fo6DRe)HWgSuZN%5|<%&t)tF zyEUU!SblE}J+IPY86Hgt%Z_W8zy0lus*Cb`4jD3N5(?8LvxXph9TUsIh^7&&v2~rB z(^n+4?mYqY*aM$XNs6L&Qu`E=TvdaqO~Sv_bNh2Kl|XWkoP;2aF;ZdqI4YM8!M07+ z8FspFYZN4KiLrY8Mx5ucm;)ZA7hcqb2G(S43s#Tj&5lL>|kZ|HeJ; zh({~F0>=+tUX`>TOj&2Q{E6ghpZ1YbH`(!$?rp)aGy4e%M%B78WoYhZ*jIW!Um3#V zFQHFZM{6F8J~%js{WhaK &m9>__@pp3@DdMvLUHZ2b2@EZrf|{675sYxxwmv6Gm&%m z1@7`}Jo7>f#7|v{5I;&3w!%NicCkZf`9`#kbiu!>yp+Z$eLPMrvh$-{bixxY$TLRS zCHhfORP~M845NywVT7oJ&-vv&nKWe_3pyNjhP`gzuW45gWK)jJTg3@;nAp;6 W)x|F;xS#)aGb)s12(bnLJi)w-_~B{dqBo1XS+$?-da;EEY7SO zc%dk-$9OH{!06(Pe)=P?%-5$h40pJK+0qt=9;#Lh=WJMZOykXIC_m|?0@<`Ov0H2- z#Zq-&b*_duxl@K`w8rtbWTa+ZGw@voc2U5oPH3n&FO}g{O*Y34xq-><8Bx5QInOXN zbSlQZ+sE_dL*ac)YDIjfZrG56(<)v6ZU3a7v8TZ?0Tk ju2sd;@a7LeSePo_48z+4AEA^*Q)VWQChd9sEM9xVlq z0iOs4521?%;RsJa;2%@HP1{ZP$qwoM_YW@!v+Mu0BJ%i`%%v!}1R_L$Zp0lNZEMy# zEf)C?hy8i*&;m?3Q8s5+eGB&@=3A*y9=OXr;xci=FMPN^i8&QLO}NzZH3Ychey(lG zo{u;E1Md$Mo;FxXP!Firzsj$IIbG1hq_qnwu513@c`A`p27A?6+{j}W?WV3k$t md}5<;#VkvTjw=NJudYdO_y`sfF= z#30U!6pn!dQSp8|LVttb^ %?%d#4ODxA?UQ4E2woZ?cv=fh zBPv;pSOl(-4VMc^ZgkkUpjtjL4%DS}LNCvcWDg6CzZi7{E&3SFtD{b^2$t$C)Nkp) zP}EIht&Oh4DVLu$B)aikD%MGV>W6-i>8+}`WDt(5aeEo(0S?!@>+O0!F_5sSysTPc z(8S&u3d{_ev>Q={a@l52D`qk3pdP9$qpr@YoF4smUBaYAchB6}9o9KpcjoO7@D$~t z%sQ?%92dCHOb}zr!EUU8@)(6rb!M+3rtz=e)3NtjGGA|>)~|6+_|k)atVj4e*{bL% ztNL}v=$~ms(89u(BFu7ZL>03D{;)Br)u_|p_ISd3z$nnJk6k{e-Xad5HXC<@L{PlE zv}ahORjxuxj}J_a<||5!s){eR1yU=hd&z~7IEM^= ft2LBG2T#UmrBdPO53=zge5-rO`& zGF#QR(j1Kj i_JoRHR6XQ_@)9OdeJ)GqhhFsX2f_0Gi%?ED$e!b5RvVbf!v9*N?>&Yf=JMWayck zHrox7A)+ec(EDObNg&=aE*cJHiYay7tRd9=5tT=mxJ^vm_Xz$SAq>Eb`?tG2wff!< z8>L6ng_JLuz9Bh5jzuOWa0R%!^$qAaPxd6N@rZbq LL%pJesQMjLp*|6P4~9HJi}a@G9mFed+$bA)UC=N&}bxKyusGjt1Ilq zYi|`vIDLlg4W^bFk}d`@iZA2L7#Pzv;OSti7=8)G&2Nsrnp%gt`3|%v)UZeFJG*0f zHR^l{DYGFqx(og9Tz5$jADIvzwSZq2xvnAUkkf(dJ0F=JR|1OnAj2o|(BcOqn`Vdu zs6&_DA;sK2;?Ue&tM1NJ*f;s|LlFOMWe@3)MVbNKy|_0C%O3D6qJlY76Iu4Q&S-Ye z9a-Vm);cuI-wPU-6ep*Cg#mzo=o}3bR1~&hxnGrhwX&99KtM=En+L=uRC*flRnH(a zxk)0gH_u<@;B8mdGl?nqZPVdGfn5lPU2zUgXZX8T*toVs;a}c2pWvQ_duv;$6NA7l z8Ue{I2}Z93R?HruUOFP2Wzux&&q&^JkWx?}d|IQ9Yo #^c&CB4Bf_kYW9BOvd#=Zw>l&--kD4VxTDlc zzPNo@+_li?R?XarD0Ki@Pit=Cud<0G@#f4BK|a6^0@2TncL?^=Gf1JO6@NlSR_9Wy zXEc3~X=GSIvA8$lLZvP;N|4~Yy2zFyU79s5O>-s5*n7hU#~TiH#q3wNkB-79;e{*M z$L|+q^#Q~VMzF5dk0?XU$-|Y$CYr^6K}Ye~*L)R(4?AWjGXEzNLZY!2$np4whH4r% z z8_@#B5{uru*Qu34G}Mfe!AZ1F335^rjGj 2xM?p zlNE@GK<45QiQDm|6{z#O+o6)z8=g)6yWToiJM0 Wb#}t{nV}qqg;A|_lf8~E2 zi*cfisO)k~iU$zb^>&(3HC~fJyN9f*YNGJNX(#MdbcrBUm{Z)Vlsv7Bf5oCH-vo=! zd^RwL&$rl~X{q#@}MWVGvG+q&i%Hfu1@GQ5PzSdm^NNrFAmnJ@ryi zeTt9H @+Hh}T>Df8((7Mnr6<)q!+g-#3 zwnNN%HOs+Q0VETilF7M@G0*+A8!g1LveyiDoN`!}hMH}<1n3x*nDr2|p9}wc-`u=V z3li2|^%&zJlRF8roT)~4lWCj@=v81+IYawAA)ls)tLE+*4l}IlN&M$Ca$zEzUlL>> zG{aoEtO(T0sAW&%yv(i%W2hR2DMW)eQxQ^9jYrv_6()qm^*!QyZjnHmE$2;3firv8 z+Gm`m9yd8wJTC^!s}0I?aXZX2Q0(|u!w^I*Q9K-(gL0=bpL5ZpTjRMJkUzgh-2Ls` zL!w1! yk)I|~0M$LX-0Hebn0FJ3q^e19r53k>zHMu28(hv(-V#fRQXw&2 z!I0^z)kmKhD|aM4ioJS`7o5_{*IL(Q-`UZ%=l)dHju|0gIFO$ko;|PHS4Lm>{>m_m z2#}5bo-0!xu#?M+kTm#1rub%9$W)kW)+ZgHb2-qLR`-` O>-9n3m_!X+I_4oH9CBD8=O1vAWijj_e03!u8{G*k2o P%*^mG01*kcT0OoD^XM6a%tZ3}oIxYQfzx>yiPd_wFW|%5;e`OuYjf$eU zb6mc%|00Ty+<^!cr7nUPN}b(%99GxrTki;zOFy|Wgm#s7l *Zw7v;{0>R>?B>aRXQ0QAURB=*yt3RPUY7c)~5 zP%6PRVGQ`~dN(*VLmu}MK*$Sw&7` -S&&Qdb4yJQStA)7GIH|rjf!JZEA?ai^5^I0q_&&<-*a|I zTO3zeGTjn0Yh<5u));?fp82T$h5+>ed{)xuiL1CXy;(*YX(rFN<2gL{owA_kjgwJj z@zjK$% 8u)U73bMUpYjUeY!|u}<8u#2hSun%7ahEHNpw&{?w<>b^>c>noELyY`{jwPMYY zWm4e(%(3xg4o-&+Bu$;6 YP~XNyiLyR<7ieN$ZsvS5j~ zi9mPZ_neB$??yHsPid9^A)yI@6kw7s+trLLu`4SsSJs6P12(^_eMxW9xhFG{QOJ26 z$1_TdAo#EaJ(4%K@2<67t*$K77~Kh4zaFv`X>6sL@E8;-FXC>YLaowL(&Eu+v`In3FEjXDivj9mXo-^V zZ;>yaf&XQNQRlX=ePmL%hDhqpgXq!Q_LuFsI}Iu2Q?pi!12X41gli1+I6|_s$<>J| z0@{pX-%;aUVj0N;@+BB)eC6a0QsNV^0AC3QCGMy(r1Lt2Ngvc-kL>V?O|`P{$e#~M zoL^{W!Ho6B@5H%ZIIn$7ejn}0k~k4UKlJtM2dO`pL2QH-Ip{bH{yx?+X(zhXvhA3H zrUI}(pVC2Qn3Ve08*%bsdRD8JW-)ex{- =xI!$`>--e3-j-1}D zP~Y<1?rWK}6yF`WTOMY*IGj0K<#}D7&A)z420*G?ZRY#!=;H;*qTx$f0pZg!#kL|d zP17>>vq |0#;7v0KbpA>@)9pj>(Q8qGS% -fO269 zTe3Mty(@l=mEuDV^278^!~R}7WB||JaH4X%V%M!sJ5lI!>h~&$G(oXlco1#J+4t;Y zIf3H#&GRj;ylo`G4aeWZy{^tJCFVd#TAFE<_62bzG x?n!pa|gN@=E)xy1Al<5UYr1_ZX<08 zjeO6fxMt48YiihkJwdi+vSC;)Y&{ZLAiZ5F5ML|#N!tN4m+P-gu8N-cXb+m)jwdzV z`C N4^tpYMwl-}yZY0>snx*(mPPtx>!5aunE z5?F!c&fHj9EteUWm8Ez{=1Y8I*`Lo=;mqS|AB!haJPgqETe^SPg%1zD09srPEszd| zX_0!O4OM?JsH?LtsvhCATW2CsVG|7?3@;N|#|wz=!D#I_C(76;^1@) a}(?6YuTbo0%kNl-Pl>FtfA4RQJ&`1~A++|ohAvWc9xv kS2nGlM0*+~ zq= {jf5QoJJ!Pgh=c@_gTBot;WXnk`U$K zC171@cA9&ZcinZ}DR-j=mThk9sQ(uyaE!nBH~!xZ9ulJG>h_cPTL^q tMx ziOWT2!*l*av6(80A}K0JG)o`byp3!4Vq(fd8vY9G)_xtfoSGu=f@Ha5?&EU%M~Jp$ zD0kvA>Vo}pr|PVBWzyX6YQiL%9wHR~g3}EFBH{C{)Ibv%+24Ph{J^6d-y!o6PzSm> zsbbN4Uj%H2|NfA|?ynwINhTr(N*FixQcmydd1EAZ{$8A^ HYOQ*j5t04-l#S|Ds*sCJ^uuH zR^O=DGJ4{CF9- rlD!z{QQ~x91)Um1v0|;@Lc>n)VDUuhHYJ(joH}=TD7Y6-UeLN zbZyvueZA^aEj{<%3%v6Fq kdD>*JR^2rC{tDEjkk312nCHoZv-6APz0zU49$N1~z4E>zM5uZjT{+|%e z%Sen8QgoZ%(vvvoi$q=3a3ehw }W zII?8YKQETy9)IdPML0|c`B8G)FWZ+qhPTkE(ma`cN-A@kb^W{k?Lu)drz-`f!A;=X z&aC^aQP=EKNtkKhiKT*>3Z?b8q)VA_*gMZ$?||LGiLsZXgHu4V_up!^T6%y|TZYUk zDK`^lEOJ? ZBouz^AG1A8O~-`=ez^u0bvsNfi9ZG!%Lv?+YG}LjA 5%QkB z3XifpU934ouie8_KW=rt7O=$Yh(zt|D_P6@wuc{i`Pn(2_P{x2B_)8P_F~KBuzE}1 zo}svJa^*L^)N1KF$a_q}3nbhfPHu}FKV#h#|Jh!KaKt)rWwhOF05{r8R#0Jjy|h1` zQpk37?da&pvN>cFe$>3?XzOXupQr&S&EcQF5S1+9ryRf#{2NJ@iuoU!#x3{g>iVq(bV7_{bU(3#(JU!W#z0bxvbk1d zVpS!DpH1n~pS7TDE~;0DU$8I$ND ~W(fdM zeRXyX5Z AEd+8KJjn1E{Cb)FlcPgp1pnTiN{M`(nfD@FhA70GRMa2rk@o_ z`4B>=qK|h-c8&2FRS<>pdA#V`pmf0SdfyJ687Pg{)Xl>qbK|~1D*ABT94?m0Hv=2H zT1Nzw^IN$PnN9r6AV0C+52l)w6Ku!S@2Yn%(b(1`DFgY$0kLrP{LfmdN2-!n_8U9v z-P3$OjiQ5%G?&`j4?rSi(~D&LBwA!6F7R;`PKQSac3~Lh)jn5l r!#ZJMS_!Jj0o=Kf(cHctO*#$H- J%2K>gD**l(B{ zvNw>!e zPpd3Q;~f$j2}W>)gCj{K7x-Q$ijXOLvlG%}fYZ2*FoeORf8Kh)dA12o|HnQ-d|{#r z;8ZblaxvOrTu>4(bY9c+1oE5ajrwF_5ntH8POd_N=;pH*$X$W8;5MGneuOUzTzc5I zQfN-YDW%x5^RYPHbAoJiX>SiD3Xh`sD-6{^w;{SAf|~5_7{YW?&nIAcivyCQIIAVL zhZMu-h|IJlPynk=oE=7fr_d!dBfZ#g2MEng1>Z)svVW@<`*NUBWS9W vPOWKf+J+;h+L+ZNkXl)bi4%7I@@j`c6Zx7cz% g6pr8)EhYX4@+Vg8g>RfAr=yFHj-5{QE9Kv!pg71!Lu9SzQ2 z8$(QGEK>?Hq<0b2zk#GvE*fC?FKvaPPs(*2_bScF*Co~}m$ j2YHRwbZVrNPX)87ObQO&11zgT@c+qcs-H%sB%b11X_OjV}vt z>#`)-NkA!a`sW8 Em5-9T>xK5DH_GFirGfj<+jr$MnqB#L9DZ6B}{9l8y(i227)Vl49gA#lbU(wX|{e;5b>d8&7hm!ZY`XCEPFKruS zZn7o4N$Js~kJ}5(!#ZHOE9OINrX)=~oN6I3Xj|rN?dSXFaEHCbmF)$!balqoEe3Xx zLXEhF_pQLbc>Xzj&U=VW8 ~DW~U)<1fziqzU0% zN-Un|6?<3W{P(OJK$Buzy#LQ&A8}#3RUyqtlUUk^ `l qB6j`~ C zx2?bul_-v3sT1Av# TwZ-?@4f+x3PR=B0W?J68k;pgxMfyH%Vgld$xZf@ zpR+d9UfE^b8jdyY>irTzbB=9hQ*j5;0nB4Wn%&+19jH@`Y$sGxIi|!5(RWY5ySlrT zl~q=flDm83;cBN|PKZ&R=uTe?2zBCV=H;xO6Bp)$#)9V<&s(RfGxEeha~NI8P^`RT z^1tiFe;g?sl2g67u@X(mm+yi`oTw7uSGeviBvY(njqD=dCP9t~bnIu;XNQ!BTJ+TJ zH5n>uDQvWa6f1RKg*5ly)0uz#Had iX%S0r~V+76DtuHo;e9?%IAK@$K)RS(AI>h7`n?# zxmh|<_9kQ#Le`z&0ILim38TXamsFBMWb0-h*)Z7DwYxW?eJ#Ne7;ycqmmullop537 zL-R^`jMt5MAo_V|7v}$7KBpK8*hmLQp2q|#P2hB@55|Nr{o4}x`@^2e&ek?Y@_4av z`24d36T<}IQG>Uz%zHkRplN{YcEqeN#J}ZFL|wf_i7W<C%jE%-8y6JFu`M)Q=5Y4k?AJ3VhFMv^yXaRZhi~6q(&7nH z?^n6gE++t?W5 ixnevDdm zCm&Eh{>f^hp}?;)$nOldQHp3IVSH^+@#{2axQX3+qRFzm&vXM9?m|xv;L|1jsSE$< z&QTk98i(t@#pC}2mhd!zZ<{0UHb$0iN|RzJRIW|x{4=ogV%r=w7! W=RnIKD0mt#UcBCDOnhZ`Ti}|F S4AMzI)_;B@Sa=5+&!=LAW_f@0-3NpxW>;-1y8x0YXp5C>u zJyzd2mjY#45OxX@ z3jR2o`%sU<^LFt==A;=uYidpSue#ehd#fDtCih`u=UmwjGy |4V_x?1-P!d<^&)Im3!VNtBFdA%`?o9fBt=dLm`%*|D)j2O+>3R5N;u-S-1V zsxsv?aM@8da?EKZ#~XS)VFo9IQ>|9Z4K5Sei`KHWUw<49@pk{$n|$-`KPyfm<`xt; zEB_$JKL1CxT O2prM-#NZTA8nJr^I QDqMQd|=K|3~%;qc8bm8)m!_$SC=j-?8ht zOu&WAIg%G@^vFBJxpNuG$;mnB65D)(B!JJ-9h9l@=RPuw#tKouKc(j?bj22xPAh}M zN;79H-JzlgQi(}52Jx|8Nd~c~Df3|d{RZ;1&^RTVwo0`j+Z}l$<%(Aop#ZYp!({uU z<;ya~ZAfpVLDCh$CBC+e!H#t}`P;mNV>C2&&~2T|0Gq6x#-4>(hJF`<6mB!;d$J<) zA>4u%^~~S|;QokL) <99XFK6eP4|2#0%B0=+^=cmI64Hfb$5#{vc_ec4+Sm{^!%QC;&Zy`9c4L2R3` z3~%=K6CYzJyUHPMC*qQ=()4&s2llBrFxj0sk0@&b-sE1ef5r*1mJy*Z=?3^${O^m} zNA?s38(U?3&v=D|!P6@X-@=f1Tg{{zPF?Q-sc2#;(~oMq_8QS=FD>aBs6Z)>D2@IE zxH2!&%}#jBOtJyUS?Gu!bcu3{j&CV7VVnBpASDXtJN~Tg)73P@@9^z9PtD?Fi)4tY zZXy6Y+QPxcgkyk1*ZF>wDTP@x_8Rv(Gc+bG1jU_Lt=oogb&aXJaxCqIX0-Wjtx0RC zCf8|{8GA-W`LU+0a@m}h+P8|!1xTiu%|>^~!Ka}pj}eph96ftjGXF;D_5bw(^c?Jd zGN-uKYYjJRGux(c`FC~yA$i=_T_d p_ z-V5N&2G6t9+g(G9f`eaZuO9gROD_a1go`dm7A3KYg77H#%?a&lp_Ft{U0uTUos}8_ z9Yv%E3H(aEd`r&+sUF=4?Lwz{p~c-zN?W12*$^%kd`%fWa@kE;MxD&*R{{f?>~m@v z^k{O= =iBcJN7$z6}`%rSWNR;#>wj_%E`{g^BDikE%6lL=B0vUeTKgVXY zGv6R`N_in9W6|v$7+^YuuGv;h6V{Lp(}=!g$kf*jW8H8Av1=?`P2`AoJKu@CJ2Uk< z>S%-(oCyWEuWL(Ff2hRo*>opUlFcA3uu;FHQ Al1Vb1Tm`Gm(s_9rcb zz~M|t^S;%lm5!?)Zspu#I!t!5uk`1%zE1a)TnBfBmQFq{J6 ~*{_=TDes0Z7bK??O>WeUclWy{=Nzvawa!7aC ze?)4=dk0C-GVoq7scKg7)s(uuE|7b6DV(Bq6^}R6+d;Z8+pN<>?x*8u= q`l@Z>sjojI3OV?gD<4n-=OMN z{V(#%zQN<3@5JGwExA7;S|EwFnEk7DjomY*8Bo4Kf)D#D0o5tgaB0RpP(!+H;0u8J z>_(_2R0rTfsgA$H&+K}{fB)wUnQhpiLvaSDCiH*vJF;_`Gw_6eD6dvjm(~3=x$Z e_d4 jxZ!29koxJJ$k!;g0q5)42=%}Fa}7vJ>P>#JMj5#(pxk*c$hh6Jk!fA)XA-u zNkGmxTStcJ&K8$<3KSxyCl^8Qhaz)lQEu#;vy))^fJ`r3znb3?>u&U_u(w&JmC;ng z-6$3vOCx4Zk!NI1-0mO7d2F{ZX@BC lgNw zsH@lQM)(zlQS+Fa#Qc6h8vZriF-LGp5-GjLB!_uJu`WO1K1g|{1Li|6J0owH{C>}` z=zMl%Ny_6I?n;Jj4&u-}dX%1-WtD vdgrRR%{vKe;l zqRD= 5!;pEv_G`gQtu&=kj=u;#MwsUgK 3jC$xMv|0q6a(55)Zy@NI29S0Bbp-O)3 z$Iw>3Q;vZ %CK{C-$I%IN<@L0hfb-5Gy5HdfYFJ3u@nWR!KLfi!Oc5i8|G0hwG75` zg0zWR6B1>m !VMQdQLAn`2y1PRd8tDe5 zyIVl%W*E8!hVE{VkPhhvX&Aa2p5goa?*H3)F>9T(_St)Xu3X{nIX^!eP1-Ij?i>uG z>9zE`{vO$m94E0DG?e;#ZH%%nb8Xs!{G~qTyTWIQ0$mRt;To4|jB$9fz2S)DRCsh$ z{SS&3wEqlAD58sgbe+wbHOkc*cG97XP+l3N<;c9-av}dq_JH%j%4AzwN2?<1uFCsa zJi -Tf#u}&O3zgNmxHrS&YXr|5d#yl^gY2g~5?-c)4T83__%M z$!c4n{_N!RZ2N?n_;6>4Z!8IvA3H*^ST)O#CV5_0;v8hJ@oAkAIF>j|GAg<{3DEHc zs3sUtl?#v7 osXNyj(7w zk8Td0>pSPwF}w|UBNj#R&UB}_ZNpjT*W1PY6IP7J$-}MHPUsWSP|@!(y9%5)GAg45 zYL# E(l@H8D%-Eq9#3ALE2n2?L#SETe?ZkVJAP;h zs18CRWe#r|{KuzrfE^U(n0!NfdtsMR7z677K}d1CjCW{_t51jQ9|}rY24Pzj4$hny z49tX n5oy>+jxhoPc;O zMB!21JPb@I{=<( 5df~oIF5bFLVU$zf EN;oxMgq`xv05PATS*$O?^H_CXCt&fc zz4rb{^ zFAUuleVZb#gaIe94qZ{l=Qw$v> zMdtFA#Ma8vvhVLjK;K2N-LAr4Uad2LG9D;0+XhjFk-&34x)H$h3Ie zR~E!%9i9K3)y6zjun#?aQBa5T_v4i4Q@pE(2XXl0YoEk_t%0y=^yV_%db#Jr@#m50 z&FPEnD *DW >G%p`xrmb(!GWLm2;lfOd{#kSoYV+fE-1 5a-ETw@<1xW8+KbV^Z#$&?Kr)_E6I3w$bOG$73dj4F2 S!q84p3q)_69UMr~qBeU_iZkhF!Yed2)A@A+ zA5B1kiz`h_QRJB-DTPtOg@-G+MTB8#DWyXH4B2Q%;liV sZej6dh_ zb61B1cC-7u29ZuIRpP`JxG4r+1(8vGPrA?I%lhN;)n>3+)~a5L|3}jj^-Ssy_u j$>xA6v+`h_3Enxf3~^Zcq9VC;X8tc_$^WaNth>Z29ifc8!cE?C CS)mF={03SM>8?pzLpdd1QyY(d95Nn) zavoL1KLe$tZK 64M7KmWTC0#n)XXNL^P#|Xp zW^yR!XMVN|RK2)mT(pj){)Ck^AQN5r)qmQRN*sW+by;dM8O Wm_1~fM@dAlWnU1#^ckdZT-g_?mW|=|a z&1U=JcJ);Ge^3MY&n9engxe+iRz;DE)bRoT$A15T`>TU(jF(f4ZD?s_k_?%b6p!5^ z7hopoEY#Oq=8b3=9MnjJFg$IAR*YIf6$dmQRXT01pz#lbi6p5s4SPG+{Hl9gPK|g- zov=Sv(%)&8oRBbKqP;a=jV4*JLWd=hMu$UmnFYsraho_%`xwvc6OpN~;-s4@exZ9v zy_G@wmWDxUi5rOXNCz`!u!u5}HZf|_;ee6&s$Vu%dKwFm*KA 70K@Xz gRDs8c`2|1E)%588q1h<| akmL0(a%&FqU8ewR;zDqIe+%POv=5+d;{PXQfD!k{NWG(W)5Q(` zyB>ZruVM~aSOv}FmC;Lmaj*a5CiyT;vOX30-GGekw@aQRH6sfdqSHquc?C|{1c2oP zeBf0vLn^hsS#4kPcU#GDTO%+)=P6Hha@i);<$MV-zbId;L@@D(CDYKu9QE+nkSiJ` z1Zu&IHCd8%A1|LALGm|aGtJI|ni67e3$;<_KA^NZbV#7235y3X5Y9BDmww2v%^lca zBubs|kCPQ<5)LA9sWr1C#}_FraTqnpYWf}wfmjd~b5;E6oJEvSgT-7JsK!Es@0;|6 zKy(YKcX9;N2%HLH#pn~=D4BertmHmAhC=tbaU1~iK7UF0Q6>8j{0fULV%k5Rv74ts z5%}SxyEuv+%K{am)-FHF=!=kLnJjMkWjt8J-tK;8IlU?Wp1qKXU7Kip#uqJ4(tHgp zccL~~8uDV?o?X-f+%`4j#;(6y|8En%rNZ (A`01IMqcd=E z!Wt_x?x{FoIk%dW)#67e3st}W>Mpxe2Nj8{sq4=3E?Q9PwoHa>6*p?qFMLXj=t%>A zku}Rz8hDpVKdBwZNQxq7re&1LH2Zsdp`@UIdDs%p>r3U6lv_hu+lk@6-<20XSeVl1 z9(`~kjwcb_3gkA^s*H{*_tmEHP+x_%vV5*en!k8*a`x-G^Ij#VgQNAzTblP?L}1G= z>5H~x0u23f7pD2%(!_UxFHR-2<$$)hz7dkWq$k2B)yyOS?nBzPm`18X5%m;xI*3hD zEZ+-y8X!ytk8=W*-!b|&CJt;c&x_8)lF={;BqqBJA_(Bxyxa;XZe^30=I$p27WyF- z7-niQqg>#R9baI`f8`g=MR=TDTm#Es2wAJj9)D2~ `&Cl zFFl-}HEbF?n8-}E*{eJdlsb twfL|UMZp2 WEh{-9<>Fr^$BdVWV>Eu~!$kO!@yNXFySBPN4% z#6sDAFS@L6%`y9(?9$YvdoaRrMFn{AT8bX3bzWjyz{{(syR$po5bn)iu BxmT)SrKs47M=GP9I;`oQ?ZX2A70ImdpE^%-_P~ZbZx=G zD+AVTNz_Istgh=884FQ+*~;qXFEXq7qpI(WPJ5MJa-@R7dG9Y1vM+kZ@&)n5xF3V* z59QZvmDoVmxpZTwfuw -!uFFCICYmCwjvc@I&E+_=^AXcoL#IU|L(9RLyG zOXGis7~ji(^=8UOb>vkG{yR)iaaa40&amGPRH8{`9i1h4( zoQ?YZ;&R|MdTxl<^MwbfdHjnQ<4Tyvj7+_HlJR5ZkAM}_rRvPCZJ4qdMDGnEd$G<; zfp7eVtZf~!=cY8%-V+DTkxfRy^SBrk2>EY$EiA#LTdjfsu&^OV5-xK*8ZI$YFQ_%7 zjy*jRkem@HTYH7W_10-uMi_}^PNd16C%g5e^}kn?GWz<81Y}Xh_{Jy^D;=p5#J?dM z&+8fb9M$uP-|Mf_tw~HkutVRIsPXXHH;_zX%AxSBwya2$C0I(6)YM_7V}A?Hq`ZQ* zLYaHVfBzD2v;2BalSQqgpM=Z8IT<>Xc5SOV>+iW>Z?*Uh;^_Dtn4i9>nw7$w_fB%; zl=9qM*R#CLe-7#tr|L`+PzVqP%+fM=ZLngSV7LB7hD<_>)ao6}A%|tj$ZnT$AFovB z{KS7|jG7kZh0BFZ)f>=c5)D~WNxthY_3(j670XkS6}{}^G$du9WG^t(qsSfdSq#uD zc!CVc4us9E9mbMvjN-Ju&aJ4LK;77!eXMjMMGp?S-74r0+=;$pG*PQ#&-h`2V9% zJ=I=CE>fz BFG1>>B_--dOt6-EOP=H)vfL5(eYvvAU+GMg zGV^p`#{0oQWjRCXtT-SN0tA!-3R)89Kn^)6@l9@XynGK(=W|LhO-vCcnpA)Y0~ iM+>5U99MpJ%Jdh|vm~g)#(!{z)hL(g*sLlp22{Q-$rvQUP7FGN@aEpqb z8Ai(FMp2=KWzoNr^sOn(GbEE81}}v?&=}Z>L_dp@y?aCSRU<$>MfcYZ7LmQoMb?eJ zI%5w%gPTTxnKPgI%KAgd6HYvKzC+XaSh6E$21?24f&!NkRkC-DG|^RC$$7~ku>nbO z6lVF}huTL659Y#_0=4q=?ZSiB|KV@Uuf*sWPE&EQEX)rIfHeb}3pRhhbEdT5^XjIA z>*?u{=0HS{0N3e68fFOCWAh@#gPZ#?L@KQJ>7o=qme!)c2e3j 0K;)FRTdoCM zPT7d%HNrQyesWEdn25vK#iGr>jK8ktZp(+_4bHuR;+ r@bslpV6S`z`NkKNyDj}Jb1st4?NMrT#HtsVD%}@O^@9Y$6)y{h!s&G3y zRl$VftbO9G#6$aH&2Uqt2s!1R>tA(>BKui!Wc+`f#59vDlA<+;?fmJ=Ge#jCL^jV2 z$ _D?7eXM5Nrj{f>r>F-@4FICP}X4Vb)gpQ@eeJ_bRjH*HA_jAYz= zWUX@!6=wa~ZVXg!2jZz(JxaMa46p~90u$8o=aJLkTqP7gsoFQmR Nf+eFF~Lqrb{8zS$dOyiN8;3x>U%SKePWvg}lKcMe* z?vH;JrU=bId>N1 x8yKH`+-+LXHSqv)y>VS&Oj@~J!M5!|( zx?j!_Qcd;jtt*~3fY^06wSI_@EUOailfx76dK!c5Z7-dv^O9!ja?NLKuGM1A>9+Nf z^Vy=e?I~YiEj5W=+viI*#~kAqT0} qMsovg|pA0BxnrHAp zM6Ax#fgnw&VC!c(=0D*`s-Y$6zK*}xiTfSbER)URyP=3~9Slou*`}#dCGB5k=8}zA ztQ~ENNw}Me5tc?k;gX@38Irrn1=iOy7n 6QFg5C>P{a-O3 z>n{SALrymTovU9x3H)!5f5mE*UKj4U`oHU|tJFMnoO(tO!SpuY?Je>nyt8!osUNK7 zkxgQrgkI8_FR>SVWZqu{i9S+lS1bk6IQ 69qB{4?-XTA+ zX^0X9Ne #R-o`n^c`XOcDX#Tf4}IWEyw9 zTb|^#h9yev6R#OpKMV!MRccwt;OJlHiNND>z7zPj=sMr>Fnx3|wh0Nm7yaKn`6^%a zBz>hqKmglwB*Dwq3Elbe)`^k?E1qjiu)MrH$!! gA)I=9M K+?_h7^ZE*=Js^FdgO&Dwn-wj3ngGjlWic)$UI!9_p|UKF$B zj|x1PQs2eYDTJSpRa+65b3b(ea_qbnoVMxL{r!Jh0P}bi1 r1(_xT z>GT2FbjgLE*BmbY|M3pC3MpSvOGLijb9tkCFq2+`!s`gFW9UfhGb>3uJTxSc`I~Vb zU-Hd&5<-1Jhw?v_z3T{}M2DYN+_mP^|5-bPtx&2qU6~y)OXtUx&=1TO8@u A-T9mFt@QsTm)Dts5pZI?vzXx%Js=XMj`Mk}}=>u!=cPR m-6NB=8l*;ai+VF$ff5N??Qpp-+oxcs_z3SE<~3eI0217r!PxtAbpXg(=xM$ z=C5aH&PTa6(MW<4J_Y^0f%bQ!|07RfI^ e z5J5`~RL;xurAOM8LZU3REY(#>bY2zRCQTYhECte^kcLj1q&EaWKEZVAbKP-+|0?wg z$li|MWtEJKr?S3FTOxK2sl{r@@&rqAWJ196Y12L@LpMalF23P(Rw!hf`2f~I43Fo( z_jPra#i_(8>Z&LQD&w15m;X8tN3*W%B3JRi`qsj7LJwXH`cK~f?+Y5?c?IVUEYo1A z);;I1*5ZoJ=X<1PS4gk`NKWRBIkD#%v=a6Zz;3u430f6_1vBSK7p6MQh8VsZPz%<$ z-6U;?!^{IytrAG3S^>F%uy-k`hulM?%U1 F;RUKVU z& ^JAI;bA%;zp$VnD>o8<6^gW=suU)5o@CajjX>IcL~ =_&{`Y`vh5yO5fnSXVr4;E|Sdv~lfs@=2;bgy3 z^{chP8bZ$-m*!qVAdxYysc89fA9$JPscnYmhSn>3q<0+m#DgR6i8-X4u7hH@nvE7Y zkZZsL1*^2h2W6b+{cD(-8)5=Jl5+@0IrbKv4bMDYE$dd1cKalT6c%-#MVt<4c{S|7 z30ofl4VV2f#cov=%P{Kgc+IbKx&E3(zS;5y!93-y;E=YFhsTQ(`Xw=ar5=87|CriC zj{xL1`rIqQv 7XHpL2E#~InlZqGRR z(6_$(WXC+?4su+@s1+uuPb#SZCmOO()f}W`vp-n>-x7lfqOo5^zC!I^tX@Xf9;08a zrxhLd)^|^9FTVWlhba)7#f~sjB>I${fR3YvrLC##=Q^)2AC23C$}b@JW;RiDNozqw zG7Hu{B+yc<08`_2 n8SkIPWS}&crCM&41>B*%mX{ zHHNQ^f~K6gnA^ccebqCE95xR9{A*?7>O36=I5;!qb!#KTcjLdJ>|L;0NJ0)%;D0>2 zH%D<|x(pm|@+;7bDk|mTwZvmv@%0t{f1kbnz< )BVo INM}W#ak`R&x*tX;Uiiy_DABDBA-hv z=j1?h%7PMQ7*swQlKn(^_wyg;X3~mF|3x1!wuz?EUI2}uNq6wiHc|Q5-|4ZiA;s*} zH6U=X^W~xQ)!JekV9u5!#^lg;);I9xVn_09=Y}HfcLwz2-+eK{S6)Z8SSHSSVF`57 zVpK3o aXD@f`L`a9d9nfeL&&w}d{ zmj>5e5BD5DcJJOr1mZbKAgeEptBQtwq8YkGKKXKAwSoU0G4^72l!T661CIoQf#BI* zp~Dn_Z4T3MJaQ@`DC)~4n&Jyqi> `%67WW@hFZr%ma* o>2jS3g`l>* ze{xP+jwMDO65;^_zTmphEwl&V043C6E=e$HDck6SR5eAr7`i)ILe@IjQfQYbHq|HF z%CJog9e1Ra9;vTK&`=fW$0G@RClXlfj615^yvXGr((xzh8(}vVply`InmVWi8IohQ8c2iTHZPqqk8mhF32 pVLTyT;Irr 5B+BGHWW?`ZcFu;2V#7oD& i~5Xy%9ORQa*LmA}w%dtg|p@>ObEPC^{2T$=R4hwIv!hG9d5Z&>>u{~*L#|RMK z)|Op~uttlvrCZtkrI$+LbcB-(t2>f?>n>z>I~c$^U