L iӪdddlZddlZddlZddlZddlZddlZddlZddlZddlZddl Z ddl m Z ddl Z ddl mZddl mZmZmZmZmZmZddlmZddlmZddlmZmZddlmZddl m!Z!m"Z"m#Z#m$Z$m%Z%dd l&m'Z'm(Z(m)Z)d Z*d Z+d Z,d Z-dZ.e%e$dZ/e%e$e)ej dk7ddZ0e%e$dZ1e%dZ2e%e$e'deejfgddgdZ4e%e$e'deejfgddgdZ5e%e$e'dddgdZ6e%e$e'dddgdZ7e%e$e'dddgdZ8e%e$e'dddgd Z9e%e$d!Z:e%e$d"Z;e%e$e'dddgd#Ze%e$e'deejfgddgd&Z?e%e$e'de jde jjd'(dgd)ZCe%e$e'deejfgddgd*ZDe%e$e#e'deejfgddgd+ZEe%e$e#e'deejfgddgd,ZFe%e$e'deejfgddgd-ZGd.ZHe%e$e'deejfgddgd/ZId0ZJe%e$e'd1eKejeejfgeMd2ejddgeNeOeMd2Dcgc]\}}d3j||c}}d4ZQd5ZRd6ZSe%e)ej dk(d7e'd8d9d:gd;ZTe%d<ZUd=ZVe%e$d>ZWe%e$d?ZXycc}}w)@N)sleep)ArrayMemmapForwardReducer_get_backing_memmap _get_temp_dir_strided_from_memmap_WeakArrayKeyMaphas_shareable_memory) make_memmap)_TestingMemmappingExecutor)Paralleldelayed)MemmappingPool)IS_GIL_DISABLEDnp with_dev_shmwith_multiprocessing with_numpy) parametrizeraisesskipifc2tjddy)Ni,T)timeoutexit) faulthandlerdump_traceback_latera/mnt/ssd/data/python-lab/Trading/venv/lib/python3.12/site-packages/joblib/test/test_memmapping.py setup_moduler&s%%c=rc,tjyN)rcancel_dump_traceback_laterrrrteardown_moduler#*s,,.rc t|J|Sr!)r)arrays rcheck_memmap_and_send_backr&.s u % 11 1 LrcV|\}}}tjj|||y)zDummy helper function to be executed in subprocesses Check that the provided array has the expected values in the provided range. Nrtestingassert_array_equalargsdatapositionexpecteds r check_arrayr03s( $D(HJJ!!$x.(;rc|\}}}|||k(sJ||xxdzcc<tjj||d|zy)zDummy helper function to be executed in subprocesses Check that the input array has the right values in the provided range and perform an inplace modification to double the values in the range by two. Nr(r+s rinplace_doubler3>sL $D(H >X %% %NaNJJ!!$x.!h,?rctjj}|jdj}tj |tj dd}dtj|jd|jz|dd|jtj |tj d d d d }tjdj|j|dd|dddddd f}|jdd}tj|}|j}td|jddfd} | |} t| sJt!| tj sJ|| || |} t| sJ|| || |} t| sJ|| || |} t!| tj rJt| sJ|| || |}t!|tj rJt|sJ||||dz}t|rJ| |}t|rJt!|tj rJ|||dztj|}t|rJ| |}t!|tj"sJt|rJ|||y)z9Check that it is possible to reduce a memmap backed array test.mmapiw+dtypeshapemodegrr8N)r+Fr>)r8r9r:orderoffset<r2 2cTc$|\}}||Sr!rxconsr,reducers rreconstruct_array_or_memmapzEtest_memmap_based_array_reducing..reconstruct_array_or_memmapsQZ dT{rr<)rr)r*joinstrpathmemmapfloat64aranger9r8flushreshapeasarrayTrr isinstancendarray)tmpdirr*filenamebufferabb2rHdrNa_reconstructedb_reconstructedb2_reconstructedc_reconstructedd_reconstructeda3a3_reconstructedb3b3_reconstructedrMs @r test_memmap_based_array_reducingrkMs66{{;'//HYYxrzz4 HFryya EEF1I LLN  )$cRS A 99R=  )AaD !B$"ac/A B B 1 A A(fnnc4HG 2!4O  00 0 oryy 11 1*2!4O  00 0*326  0 11 1',2!4O/29955 5  00 0*1!4O/29955 5  00 0* QB#B'' '226#$455 5*BII66 6'Q/ BB#B'' '226 & 33 3#$455 5',rwin32z2PermissionError only easily triggerable on Windows)reasonc|jdj}dj|}tjt j d|gtjtj}|j|j\}}|jdk(sJ|j|dk(sJdj|}||jvsJy) Nr5aif 1: import os import numpy as np import time from joblib.externals.loky.backend import resource_tracker resource_tracker.VERBOSE = 1 # Start the resource tracker resource_tracker.ensure_running() time.sleep(1) # Create a file containing numpy data memmap = np.memmap(r"{filename}", dtype=np.float64, shape=10, mode='w+') memmap[:] = np.arange(10).astype(np.int8).data memmap.flush() assert os.path.exists(r"{filename}") del memmap # Create a np.memmap backed by this file memmap = np.memmap(r"{filename}", dtype=np.float64, shape=10, mode='w+') resource_tracker.register(r"{filename}", "file") # Ask the resource_tracker to delete the file backing the np.memmap , this # should raise PermissionError that the resource_tracker will log. resource_tracker.maybe_unlink(r"{filename}", "file") # Wait for the resource_tracker to process the maybe_unlink before cleaning # up the memmap time.sleep(2) )r\-cstderrstdoutrrz'tried to unlink {}, got PermissionError) rPrQformat subprocessPopensys executablePIPEwait communicate returncodedecode)r[r\cmdpouterrmsgs r2test_resource_tracker_retries_when_permissionerrorrs{{;'//H : !;<  s#JOOJOO AFFH}}HC <<1 *cjjl*  #:: 3 : :8 DC #**,  rc tjj}|jdj}tj |tj dd}tjdj|j|dd|dd}|ddddf}|dddddddf}|d d d }td|jd d fd}||} t| sJt| tj sJ|| |||} t| sJ|| |||} t| sJ|| |||} t| sJ|| |||} t| sJ|| |y)Nr5)drr<r6r7irrFr=rDr<r>rHTc$|\}}||Sr!rrJs rrNzNtest_high_dimension_memmap_array_reducing..reconstruct_array_or_memmaprOr) rr)r*rPrQrRrSrTrVr9rr rY)r[r*r\r^r_rHraerNrbrcrerfe_reconstructedrMs @r)test_high_dimension_memmap_array_reducingrs66{{;'//H ("**4D4PA 99& ' / / 8AaD !BA !QrT' A !Q1* A !Aa%A(fnnc4HG2!4O  00 0 oryy 11 1*1!4O  00 0*1!4O  00 0*1!4O  00 0*1!4O  00 0*rc |jdj}dtjz}tjdz}t j |d||z}t |dd|d|ddd }t|tj sJ|j|k(sJt |dd|d|d zfd |d }t|j|k(sJy) Nr5r=rDr6)r:r9uint8rCF)r8r:rBrAr9stridestotal_buffer_lenunlink_on_gc_collectr2)r2) rPrQmmapALLOCATIONGRANULARITYrrRrrYrBr)r[fnamesizerB memmap_objmemmap_backed_objs rtest__strided_from_memmaprs KK $ , ,E t)) )D  ' '! +F5t4&=AJ%  " J j")) ,, ,    && &,  qyl"  0 1 8 8F BB Brfactorymultiprocessingloky)idsc vtjj}|jdj}|dd|} |j dj}tj |tjdd}|jd |jtt|jd Dcgc]&}t|jd D] }|||fd f (c}}||dtj|jztj |tjd d } |jtt| jd Dcgc]&}t| jd D] }| ||fdf (c}}tj|gk(sJ||dtj|jz|| dtj| jztj |tjddd} t!t"5|jt$t| jd Dcgc]}| |df c}dddt!t&t(f5|jtt| jd Dcgc]}| |df c}ddd|j+~ycc}}wcc}}wcc}w#1swYxYwcc}w#1swY=xYw#|j+~wxYw)z@Check that subprocess can access and update shared memory memmappoolrFr2 max_nbytes temp_folderr5r<r=r6r7?rrD)r=r<rHg@rFr)r8r9r:rBg@N)rr)r*mkdirrQrPrRfloat32fillmapr3ranger9onesoslistdirrAssertionErrorr0 RuntimeError ValueError terminate) rr[r*pool_temp_folderr~r\r^ijr_rHs rtest_pool_with_memmapr(s66||F+33q.>?A);;{+33 IIhbjjT J s   ',QWWQZ'8 T!%PQ BS TQa!Q  T  T 1a"''!''"223 IIhbjjS I  ',QWWQZ'8 T!%PQ BS TQa!Q  T  T zz*+r111 1a"''!''"2231a"''!''"223 IIhbjjCPU V N # I EE+U1771:5FGAs G H I \:. / L EE.aggaj8I"J1Aq#;"J K L E U U H I I #K L L sA>L%+K7 +BL%.+K= B+L%+L/ L ;LL%+L L LL%7 L%LL L%LL"L%%L8c tjj}|jdj}|dd|} |j dj}tj |tjdd}|jd tj|}t|tj rJt|sJ|jtt|jd D cgc]&}t|jd D] } ||| fd f (c} }||dtj |jz||dtj |jzt#j$|gk(sJ |j'~y cc} }w#|j'~wxYw) z?Check that subprocess can access and update shared memory arrayrrFr2rr5rr6r7rrrDN)rr)r*rrQrPrRrrrWrYr rr3rr9rrrr) rr[r*rr~r\r^a_viewrrs r test_pool_with_memmap_array_viewrbs66||F+33q.>?A;;{+33 IIhbjjT J s Afbii000#F+++  qwwqz* qwwqz* !Q% %   1a"''!''"22361rwwqww'7#78zz*+r111 #  s B+F0 )A4F60F66G backendchdj|}tjtjd|gtj tj }|j |j\}}|jdk(s)J|jdz|jzy)Nif 1: import numpy as np from joblib import Parallel, delayed data = np.random.rand(int(2e6)).reshape((int(1e6), 2)) # Build a complex cyclic reference that is likely to delay garbage # collection of the memmapped array in the worker processes. first_list = current_list = [data] for i in range(10): current_list = [current_list] first_list.append(current_list) if __name__ == "__main__": results = Parallel(n_jobs=2, backend="{b}")( delayed(len)(current_list) for i in range(10)) assert results == [1] * 10 r_rorpr ) rsrtrurvrwrxryrzr{r|rr}r~rrs r-test_permission_error_windows_reference_cyclers $ %&  s#JOOJOO AFFH}}HC <<1 BcjjlV3cjjlBB rcdj|}tdD]}tjj }tj j t|d<tjtjd|gtjtj|}|j|j\}}|jdk(sJ||dk(sJd |vrJy) Nakif 1: import os import time import numpy as np from joblib import Parallel, delayed from testutils import return_slice_of_data data = np.ones(int(2e6)) if __name__ == '__main__': # warm-up call to launch the workers and start the resource_tracker _ = Parallel(n_jobs=2, verbose=5, backend='{b}')( delayed(id)(i) for i in range(20)) time.sleep(0.5) slice_of_data = Parallel(n_jobs=2, verbose=5, backend='{b}')( delayed(return_slice_of_data)(data, 0, 20) for _ in range(10)) rr< PYTHONPATHrorqrrenvrrresource_tracker)rsrrenvironcopypathdirname__file__rtrurvrwrxryrzr{)rr}_rr~rrs r3test_permission_error_windows_memmap_sent_to_parentrs ( ),1X .jjooGGOOH5L    ^^T3 '????   ==?S||q %#% czz"#--- .rcdtjtdtd|dfdt dD\}td|dfdt dD\}t j j|t j j|k7sJy)NY@r2rFn_jobsrrc3JK|]}ttdywr\Nr getattr.0rr%s r z6test_parallel_isolated_temp_folders..'F01 +F #rDc3JK|]}ttdywrrrs rrz6test_parallel_isolated_temp_folders..rrrrTintr rrrr)r filename_1 filename_2r%s @r#test_parallel_isolated_temp_foldersrs IIc#h EE81g"EF5:1XFLZF81g"EF5:1XFLZ 77??: &"''//**E EE Ercztjtdtd|d5}|fdt dD\}|fdt dD\}dddt j jt j jk(sJy#1swYKxYw)Nrr2rFrc3JK|]}ttdywrrrs rrz9test_managed_backend_reuse_temp_folder.. O))%<OrrDc3JK|]}ttdywrrrs rrz9test_managed_backend_reuse_temp_folder..rrr)rr~rrr%s @r&test_managed_backend_reuse_temp_folderrs IIc#h E G ;PqOeAhOO OeAhOO P 77??: &"''//**E EE EPPs 9B11B:ctjtd}t}t}d}t j |||f}t j |||f}|j |j |j|jt|dk(sJt|dk(sJ||k7sJy)Nrctddd5}tdD]L}|fdtdD\}|jtjj |N dddy#1swYyxYw)Nrr2rF)rrrc3JK|]}ttdywrrrs rrz]test_memmapping_temp_folder_thread_safety..concurrent_get_filename..s Uq/ww/zBUrrD)r raddrrr)r% temp_dirsr~rr\s` rconcurrent_get_filenamezJtest_memmapping_temp_folder_thread_safety..concurrent_get_filename si fQ2 > 9!2Y 9UERSHUU  bggooh78 9 9 9 9s AA55A>)targetr,rD) rrTrset threadingThreadstartrPlen)r%temp_dirs_thread_1temp_dirs_thread_2rt1t2s r)test_memmapping_temp_folder_thread_safetyrs IIc#h E9   &e5G-H B   &e5G-H BHHJHHJGGIGGI ! "a '' ' ! "a '' ' !3 33 3rc d}gd}|D]\}}}tjtjd|j ||gtj tj }|j |j\}}|j|k(sJ|jd|vrJ|jy)Na>if 1: import os import numpy as np from joblib import Parallel, delayed from joblib.externals.loky.backend import resource_tracker from concurrent.futures import ThreadPoolExecutor, wait resource_tracker.VERBOSE = 0 array = np.arange(int(1e2)) temp_dirs_thread_1 = set() temp_dirs_thread_2 = set() def raise_error(array): raise ValueError def parallel_get_filename(array, temp_dirs): with Parallel(backend="loky", n_jobs=2, max_nbytes=10) as p: for i in range(10): [filename] = p( delayed(getattr)(array, "filename") for _ in range(1) ) temp_dirs.add(os.path.dirname(filename)) def parallel_raise(array, temp_dirs): with Parallel(backend="loky", n_jobs=2, max_nbytes=10) as p: for i in range(10): [filename] = p( delayed(raise_error)(array) for _ in range(1) ) temp_dirs.add(os.path.dirname(filename)) executor = ThreadPoolExecutor(max_workers=2) # both function calls will use the same loky executor, but with a # different Parallel object. future_1 = executor.submit({f1}, array, temp_dirs_thread_1) future_2 = executor.submit({f2}, array, temp_dirs_thread_2) # Wait for both threads to terminate their backend wait([future_1, future_2]) future_1.result() future_2.result() ))parallel_get_filenamerr)rparallel_raiserD)rrrDro)f1f2rpr) rtrurvrwrsrxryrzr{r|)r}functions_and_returncodesrrr{r~rrs r?test_multithreaded_parallel_termination_resource_tracker_silentr&s1 Cd! 8 <B    ^^T3:::#; <????  3||z)73::<7)"#-;szz|;- .s&C67+*+D1C"rD)rrrr rr)rresultr-s @r%test_memmap_returned_as_regular_arrayrsT 773s8 DBxq'cBC;@8CHV v & .. .rc|dk(r4tjjdrtjddj |}t jtjd|gt jt j}|j|j\}}|j}|j}|jdk(s J|d z|zd |vsJ|y) NrwinzVThe temporary folder cannot be deleted on Windows in the presence of a reference cyclerrrorprrresource_tracker)rvplatform startswithpytestxfailrsrtrurwrxryrzr|r{rs r2test_resource_tracker_silent_when_reference_cyclesr s"&S\\44U;   ,  $ %&  s#JOOJOO AFFH}}HC **,C **,C <<1 0cFlS00  S (-#- (rc Ztj|jgk(sJ|dd|jd} tj|jgk(sJtjj |j rJt jdt j}|jdk(sJ|jtt|jdDcgc]}||d f c}tj|jgk(sJt jd t j}|jd k(sJ|jtt|jdDcgc]}||d f c}tjj|j sJtj|j }t!|d k(sJt j"d gd zd}|jt$|g}|drJ |j'tdD]9}t)dtjj |j r8~yt+dj-|j cc}wcc}w#|j'tdD]9}t)dtjj |j r8~wt+dj-|j xYw)z0Check that large arrays are not copied in memoryr<(r2)rrverboser=r;rrrr rDabcobjectrF皙?z#temporary folder {} was not deletedN)rrrQrexists _temp_folderrrrnbytesrr0rr9rSisdirrr%r rrrrs) rr[r~smallrlargedumped_filenamesobjectsresultss r%test_memmapping_pool_for_large_arraysrs ::fnn % ++ + bfnnaHA(zz&..)R///77>>!..111,||r!!! kU5;;q>5JKUAsOKLzz&..)R///2::.||s""" kU5;;q>5JKUAsOKLww}}Q^^,,,::ann5#$)))((E7S=9%%,wi81:~: r A #J77>>!..1   !5<>!..1   !5<5JKUAsOKLzz&..)R/// L sBC* C% '+C*%C**C=ctj} tdt_|dd} |j}d}|j |sJt j j|sJtjdtj}|jdk(sJ|jt|gdztt j|d k(sJtjdtjd z}|jdk(sJ|jt|gdztt j|d k(sJ |j!~t#dD].}t j j|snt%d 0t'd  |t_y #|j!~wxYw#|t_wxYw)z1Check that memmapping uses /dev/shm when possibleg~Ar<rFrz"/dev/shm/joblib_memmapping_folder_rr;r rDr2rz(temporary folder of pool was not deletedN)jmrSYSTEM_SHARED_MEM_FS_MIN_SIZErrrrrrrrrSrridrrrrrr)r orig_sizer~r folder_prefixr^r_rs r'test_memmapping_on_large_enough_dev_shmr*s11I+6-0I) A" %  !~~ @M#..}= ==77>>"23 332::.A88s? "? EE"qcBh rzz"2349 99 2::.2A88s? "? EE"qcBh rzz"2349 99 KKMs MA77>>"23 #J  M !!KL L -6) KKM-6)s$GD+GAGGG G%cltj} tdt_|dd} |j}|j drJ |j ~t jj|rJ |t_y#|j ~wxYw#|t_wxYw)Ng@6Dr<rFr$z/dev/shm) r%r&rrrrrrr)rr(r~rs r$test_memmapping_on_too_small_dev_shmr,s11I6-0J) A" %  !~~ '22:> >>> KKM77>>"23333,5) KKM -6)s"B&B2B&B##B&& B3cftjj}|dd|j} |j tj d}|j }t|rJ||tj d|j~y#|j~wxYw)z:Check that large arrays are not copied in memory in returnr<rFr)r,r.N) rr)r*rQ apply_asyncrgetr r)rr[r*r~resrs r/test_memmapping_pool_for_large_arrays_in_returnr3s66 bfnn=AmmBGG'm2 '...5"''$-0  s ABB0c&t|sJ||zS)z4Multiplication function to be executed by subprocess)r )r^n_timess r_worker_multiplyr6 s  "" " w;rctjj}|dd|j} tjtj dj dddddd ddf}|jt|df j}t|rJ||d|z|j~y#|j~wxYw) zCheck that memmaps with a bad buffer are returned as regular arrays Unary operations and ufuncs on memmap instances return a new memmap instance with an in-memory buffer (probably a numpy bug). r<rFrip)r.r2r<r@)rANrDr/) rr)r*rQrWrTrVr0r6r1r r)rr[r*r~r^r_s r6test_workaround_against_bad_memmap_with_copied_buffersr8s66bfnn=A  JJryy..|'***1a!e$  s BCCc|Sr!r)args ridentityr;/s Jrzfactory,retry_nor<z{}, {}c|jdj}dtjz}tjdz}t |d|d|}|d|j}|j t |f j}t|tjsJ|j|k(sJtjj|||jy) Nr5r=rDr6r)r:r9r8rBr2rr/)rPrQrrr r0r;r1rYrrRrBr)r*r) rretry_nor[rrrBobjr~rs r test_pool_memmap_with_big_offsetr@3s& KK $ , ,E t)) )D  ' '! +F e$d'& QCv~~.A ]]83&] 1 5 5 7F fbii (( ( ==F "" "JJ!!#v.KKMrcd}t||j\}}|dusJ||jdjk(sJt|d\}}tjj dr|dusJ|j |sJy)N test.tmpdirFr=r)rrQrPrvrrendswith)r[pool_folder_name pool_folder shared_mems rtest_pool_get_temp_dirrGSs$+,.funcwsA rrF)rFrFrSr;rr2)rQrrc3HK|]\}}t||ywr!r )rrrTrVs rrz9test_numpy_arrays_use_different_memory..}s)D"(!S  c1Dr)rrzerosr enumerater)r*)rQrarraysrrTrVs @r&test_numpy_arrays_use_different_memoryr\qs test_weak_array_key_map..assert_empty_after_gc_collectsHw A9~" JJL #J   9~"""r*r^r_rDrHctjd}tt5|j |ddd|j |||j ||k(sJt |S#1swY.get_set_get_collectsa GGBK H   EE!H  a uuQx1}}!u   s A22A;r.CPythoni)r)rrrrr1r_r`r_datarrfrrpython_implementationr) rcr^rgr_rHrhr unique_idsmax_len_unique_idss rtest_weak_array_key_maprns#  AAEE!SM 558s?? A 558s??EE!SM 558s?? JJL qww<1   558s?? !!''*  AEE!SM qww<1   558s??   bggbk !!''*tEA)!Q/EFJ%%'94 %4S:!33335# Fs %G)G5)G2ct}ttj5tj|dddy#1swYyxYwr!)rrpickle PicklingErrordumps)rgs r#test_weak_array_key_map_no_picklingrss8A $$ % Qs AA ct|jdtjdd}|j fdd}t dfd |fD}t dfd |fD}tj j||t dfd td D}tj j|d |y)Nzarr.datrFrr;ct5}tj|jdtjd}dddt j dtj dS#1swY0xYw)Nr)accessrBr)r8r]rB)openrfileno ACCESS_READrrZr)fdmmtestfiles r _read_arrayz%test_direct_mmap.._read_arrays^ (^ Nr299;$2B2B1MB Nzz%rxx1EE N Ns 6A22A;c |dzSNr2rrKs rrVztest_direct_mmap..func !t rr2rc3@K|]}t|ywr!rXrrKrVs rrz#test_direct_mmap..;!]WT]1-;c3@K|]}t|ywr!rXrs rrz#test_direct_mmap.. Aaq!1 Arc3>K|]}tywr!rXrrr}s rrz#test_direct_mmap.. JA!5!5!7 JrDr) strrPrrTtofiler r)r*rr[r^rTrefrr}rVr|s @@@rtest_direct_mmaprs6;;y)*H "G$AHHXF  -C (! ;s; ;C!ha AC5 AAGJJ!!'3/!ha Jq JJGJJ!!'!*c2rc2t|jdtjddj dd}|j fdd}t d fd |fD}t d fd |fD}|d jd rJtjj||t d fdtdD}tjj|d |y)Nzarr2.datrFrr;r=r2cRtj}|jddS)Nr=r2)rrRrV)r{r|s rr}z=test_parallel_memmap2d_as_memmap_1d_base.._read_arrays" YYx zz!Qrc |dzSrrrs rrVz6test_parallel_memmap2d_as_memmap_1d_base..funcrrrc3@K|]}t|ywr!rXrs rrz;test_parallel_memmap2d_as_memmap_1d_base..rrc3@K|]}t|ywr!rXrs rrz;test_parallel_memmap2d_as_memmap_1d_base..rrr F_CONTIGUOUSc3>K|]}tywr!rXrs rrz;test_parallel_memmap2d_as_memmap_1d_base..rrrD) rrPrrTrVrr flagsr)r*rrs @@@r(test_parallel_memmap2d_as_memmap_1d_basers 6;;z*+H "G$,,Q2AHHX  -C (! ;s; ;C!ha AC5 AAGqz// /JJ!!'3/!ha Jq JJGJJ!!'!*a0r)Yrr_ itertoolsrrrprrtrvrtimerrrJrLr%rrrrrr joblib.backportsr joblib.executorr TestExecutorjoblib.parallelr r joblib.poolrjoblib.test.commonrrrrrjoblib.testingrrrrr#r&r0r3rkrrrget_memmapping_executorrrrrrrrrrrr rparammarkrr r"r*r,r3r6r8r;listproductrrrrsr@rGrPr\rnrsrr)rKys00rrsr    ()F-&76>/ < @ S- S-l LLG$X* *Z )+ )+X C CD   \99: F# 0  0f   \99: F# $  $N  Y*F34C5 CB  Y*F34*.5 *.Z  Y*F34 F5  F  Y*F34F5 F !4 !4H F< F