From f7d2978e0a79b96d69e5accd53d39464df191635 Mon Sep 17 00:00:00 2001 From: Robert Underwood Date: Wed, 1 Dec 2021 11:10:05 -0500 Subject: [PATCH 1/2] Arc BUILD system and reliability improvements + introduced a cmake build system to handle installation for spack + introduced more robust linking for library dependencies + in-lined the parity matrices and syndrome tables to remove need to load/install them at runtime + cache_resource_location where runtime files are stored now defaults to one of the following locations in order from greatest priority rather than a hard coded path that would not exist after installation: + ${XDG_RUNTIME_DIR}/arc + ${TMPDIR}/arc + /tmp + fixed undefined behavior in arc_reed_soloman_encode + removed broken makefiles that always required manual editing Known bugs: + in some yet to be determined circumstances, ARC will fail to select a configuration resulting arc_configurations[i].ecc_algorithm to be 0 resulting in a unsigned underflow on line 930 of arc.c (possibly also other places). Additional work needs to be done to identify why this occurs and how to properly fix it. --- CMakeLists.txt | 58 ++++++++ Makefile | 41 ------ examples/CMakeLists.txt | 17 +++ examples/Makefile | 36 ----- examples/arc_pressio_example.c | 6 +- lib64/libarc.a | Bin 91840 -> 0 bytes src/arc.c | 180 ++++++++++--------------- src/res/.gitignore | 1 + src/res/Hamming_1_Syndrome_Table | 16 --- src/res/Hamming_8_Syndrome_Table | 72 ---------- src/res/Hamming_SECDED_1_Parity_Matrix | 4 - src/res/Hamming_SECDED_8_Parity_Matrix | 7 - src/res/README.md | 0 src/res/SECDED_1_Syndrome_Table | 16 --- src/res/SECDED_8_Syndrome_Table | 72 ---------- src/res/cache/README.md | 24 ---- test/CMakeLists.txt | 12 ++ test/Makefile | 34 ----- 18 files changed, 161 insertions(+), 435 deletions(-) create mode 100644 CMakeLists.txt delete mode 100644 Makefile create mode 100644 examples/CMakeLists.txt delete mode 100644 examples/Makefile delete mode 100644 lib64/libarc.a create mode 100644 src/res/.gitignore delete mode 100644 src/res/Hamming_1_Syndrome_Table delete mode 100644 src/res/Hamming_8_Syndrome_Table delete mode 100644 src/res/Hamming_SECDED_1_Parity_Matrix delete mode 100644 src/res/Hamming_SECDED_8_Parity_Matrix delete mode 100644 src/res/README.md delete mode 100644 src/res/SECDED_1_Syndrome_Table delete mode 100644 src/res/SECDED_8_Syndrome_Table delete mode 100644 src/res/cache/README.md create mode 100644 test/CMakeLists.txt delete mode 100644 test/Makefile diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f14e8f9 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,58 @@ +cmake_minimum_required(VERSION 3.18) +project(ARC VERSION 0.0.2 LANGUAGES C) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +option(BUILD_SHARED_LIBS "prefer shared libraries" ON) + +#correct was to set a default build type +# https://blog.kitware.com/cmake-and-the-default-build-type/ +set(default_build_type "Release") +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "No build type was set. Setting build type to ${default_build_type}.") + set(CMAKE_BUILD_TYPE ${default_build_type} CACHE + STRING "Choose the type to build" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" + "MinSizeRel" "RelWithDebInfo") +endif() + + +include(GNUInstallDirs) +include(CTest) + +find_package(OpenMP REQUIRED) + + +add_library(arc + src/arc.c + src/galois.c + src/jerasure.c + src/reed_sol.c + ) +target_link_libraries( + arc + PUBLIC + OpenMP::OpenMP_C + m + ) +target_include_directories( + arc + PUBLIC + $ + $ + ) + +install(TARGETS arc EXPORT ARCConfig + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ) +install(EXPORT ARCConfig NAMESPACE ARC:: DESTINATION ${CMAKE_INSTALL_DATADIR}/ARC/cmake) +install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ARC) + +if(BUILD_TESTING) + add_subdirectory(test) +endif() +option(BUILD_EXAMPLES "build example codes" OFF) +if(BUILD_EXAMPLES) + add_subdirectory(examples) +endif() diff --git a/Makefile b/Makefile deleted file mode 100644 index 2c00cfa..0000000 --- a/Makefile +++ /dev/null @@ -1,41 +0,0 @@ -## ************************************************************************ -## Makefile for the ARC: Automatic Reliability for Compression library. - -## COMPILER -CC = gcc -CFLAGS = -O2 - -## TARGETS -all: arc_lib.o - -arc.h: include/arc.h - rm -f src/arc.h ; cp include/arc.h src/. ; chmod 0444 src/arc.h - -galois.h: include/galois.h - rm -f src/galois.h ; cp include/galois.h src/. ; chmod 0444 src/galois.h - -jerasure.h: include/jerasure.h - rm -f src/jerasure.h ; cp include/jerasure.h src/. ; chmod 0444 src/jerasure.h - -reed_sol.h: include/reed_sol.h - rm -f src/reed_sol.h ; cp include/reed_sol.h src/. ; chmod 0444 src/reed_sol.h - -galois.o: galois.h - $(CC) -c src/galois.c - -jerasure.o: jerasure.h galois.h - $(CC) -c src/jerasure.c - -reed_sol.o: reed_sol.h jerasure.h galois.h - $(CC) -c src/reed_sol.c - -arc.o: arc.h jerasure.h reed_sol.h galois.h - $(CC) -c src/arc.c -fopenmp - -arc_lib.o: arc.o galois.o jerasure.o reed_sol.o - ar -rc lib64/libarc.a arc.o galois.o jerasure.o reed_sol.o - rm arc.o galois.o jerasure.o reed_sol.o - -clean: - rm -f src/arc.h src/galois.h src/jerasure.h src/reed_sol.h - rm lib64/libarc.a \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..092633f --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,17 @@ + +find_package(LibPressio REQUIRED) +find_package(std_compat REQUIRED) +find_package(SZ REQUIRED) +find_package(PkgConfig REQUIRED) +find_package(ZLIB REQUIRED) +pkg_search_module(ZSTD IMPORTED_TARGET GLOBAL libzstd) +pkg_search_module(FFTW3 QUIET IMPORTED_TARGET GLOBAL fftw3) + +add_executable(arc_pressio_example arc_pressio_example.c) +target_link_libraries(arc_pressio_example PRIVATE arc LibPressio::libpressio SZ) + +add_executable(libpressio_example_sz libpressio_example_sz.c) +target_link_libraries(libpressio_example_sz PRIVATE arc LibPressio::libpressio SZ) + +add_executable(libpressio_example_zfp libpressio_example_zfp.c) +target_link_libraries(libpressio_example_zfp PRIVATE arc LibPressio::libpressio SZ) diff --git a/examples/Makefile b/examples/Makefile deleted file mode 100644 index 264bd06..0000000 --- a/examples/Makefile +++ /dev/null @@ -1,36 +0,0 @@ -## ************************************************************************ -## Makefile for arc_sz_example that demonstrates the use case of arc in -## conjunction with libPressio. - -## PLEASE UPDATE THESE VARIABLES BEFORE COMPILING - -## COMPILER -CC = gcc - -## Libpressio flags -LIBPRESSIO_INCLUDE_ol7_sandybridge = /zfs/ftdice/spack/opt/spack/linux-ol7-sandybridge/gcc-8.2.0/libpressio-0.38.1-6bphbkmsnbfp4zxvgxn3a6al6cmmk4jl -LIBPRESSIO_SO_PATH_ol7_sandybridge = /zfs/ftdice/spack/opt/spack/linux-ol7-sandybridge/gcc-8.2.0/libpressio-0.38.1-6bphbkmsnbfp4zxvgxn3a6al6cmmk4jl/lib64 -## SZ flags -SZ_INCLUDE_ol7_sandybridge = /zfs/ftdice/spack/opt/spack/linux-ol7-sandybridge/gcc-8.2.0/sz-2.1.8.1-hnjxctv4winw52xm2eeyucatpizag4cl -SZ_SO_PATH_ol7_sandybridge = /zfs/ftdice/spack/opt/spack/linux-ol7-sandybridge/gcc-8.2.0/sz-2.1.8.1-hnjxctv4winw52xm2eeyucatpizag4cl/lib64 -## ZFP flags -ZFP_INCLUDE_ol7_sandybridge = /zfs/ftdice/spack/opt/spack/linux-ol7-sandybridge/gcc-8.2.0/zfp-0.5.5-dycqslvmlpomptwjg52nt4zeoiqtcmun -ZFP_SO_PATH_ol7_sandybridge = /zfs/ftdice/spack/opt/spack/linux-ol7-sandybridge/gcc-8.2.0/zfp-0.5.5-dycqslvmlpomptwjg52nt4zeoiqtcmun/lib64 -## FPZIP flags -FPZIP_INCLUDE_ol7_sandybridge = /zfs/ftdice/spack/opt/spack/linux-ol7-sandybridge/gcc-8.2.0/fpzip-1.3.0-hzhhfannwjcokyjokrs4q7oojxphwya6 -FPZIP_SO_PATH_ol7_sandybridge = /zfs/ftdice/spack/opt/spack/linux-ol7-sandybridge/gcc-8.2.0/fpzip-1.3.0-hzhhfannwjcokyjokrs4q7oojxphwya6/lib64 -## ARC flags -ARC_INCLUDE = /home/dakotaf/ARC/include -ARC_SO_PATH = /home/dakotaf/ARC/lib64 - -## Compilation Includes -FLAGS_ol7_sandybridge = -I $(LIBPRESSIO_INCLUDE_ol7_sandybridge)/include/libpressio -I $(SZ_INCLUDE_ol7_sandybridge)/include/sz -I $(ZFP_INCLUDE_ol7_sandybridge)/include -I $(FPZIP_INCLUDE_ol7_sandybridge)/include -I $(ARC_INCLUDE) -L $(LIBPRESSIO_SO_PATH_ol7_sandybridge) -L $(SZ_SO_PATH_ol7_sandybridge) -L $(ZFP_SO_PATH_ol7_sandybridge) -L $(FPZIP_SO_PATH_ol7_sandybridge) -L $(ARC_SO_PATH) -llibpressio -lSZ -lzfp -lfpzip -larc -lm - -## TARGETS -all: arc_pressio_example - -arc_pressio_example: arc_pressio_example.c - $(CC) -Wall -g -rdynamic -o arc_pressio_example arc_pressio_example.c $(FLAGS_ol7_sandybridge) -clean: - rm arc_pressio_example - diff --git a/examples/arc_pressio_example.c b/examples/arc_pressio_example.c index d4d0f07..1baea70 100644 --- a/examples/arc_pressio_example.c +++ b/examples/arc_pressio_example.c @@ -83,7 +83,7 @@ int main(int argc, char* argv[]) { } // Utilize ARC library - arc_init_w_timing(1); + arc_init(1); // Get a pointer to uint8_t data from libPressio size_t compressed_size; @@ -98,8 +98,10 @@ int main(int argc, char* argv[]) { double time_constraint = atof(argv[2]); uint8_t * arc_encoded_data; uint32_t arc_encoded_data_size; + int num_choices = 1; + int ecc_choices[] = {ARC_ANY_ECC}; gettimeofday(&start, NULL); - ret = arc_encode(data, (uint32_t)compressed_size, memory_constraint, time_constraint, &arc_encoded_data, &arc_encoded_data_size); + ret = arc_encode(data, (uint32_t)compressed_size, memory_constraint, time_constraint, ecc_choices, num_choices, &arc_encoded_data, &arc_encoded_data_size); gettimeofday(&stop, NULL); encode_time_taken = (double)(stop.tv_usec - start.tv_usec) / 1000000 + (double)(stop.tv_sec - start.tv_sec); if (ret == 0){ diff --git a/lib64/libarc.a b/lib64/libarc.a deleted file mode 100644 index 4e41e7f72c841cc767de4ea1200669d319559f79..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 91840 zcmeEv3w%}8mG=z^5*~V^1*KK=qF7Ok5n=02jaQ@l*P z;Ac=E82)^LHwgTqzlY-HL^qZsFqiipw@=-=!6$HO^abW3Z-_>7Y^N#pRV%cLggq5c%ceDvgyWxx)~Y zJBX@cM6J5Atg^haBIxFgBe;D?jNQC~a&y&`R8@x7muv{txb-TktSBl(L^f0*|AM!d z)>M0fRmIhnp{k0lGhj41lN|{snNDPVoU9C;?SwFPRKSh{X33|JBzETk@cc*Dcy{5MImyk zys)OKbdybVd&IknE0ru|*%1LYo*|cRD7(w15RuZ0LrC&dRlFVv9o$rjvLXRgmz1tk zN#hPxBbD;5$Y^Ym&}tOOvf@O=1)I!mWl$1jZEfvx zD~Xp(vdVITmr~SMCz?s#6gkOjdEp%>KQx-O@pg{6e8+R75WtF+oWoYmCuOe?lJZvY zPWiK=Clq8%D*bH3rrBJYYf2K@DlMXOQl=!*%L>1Ys&8v!lapdlU0$dRj#cX83Yuly z5r_4`I}0n;7FWUkuVv+}Mhe~@VyU&wl+9hdsiqiQYva6FQL?$jpd?h|+wTg_qOsS_ zjb(K<#@FXGOz{;vfvIY4Pzp?vCLmeqVh8MwZM;A9&+}$E-E*JaKL;{Z|9no~?0NGP z|C({=rCd~rJ~E$|KluHJ=>BA&i|9f3hFwGt`qdSc41^2S6~m%2>V1aa(VfiDHyTT}r;{@vK097>+uNhcFmKlL;G9Hcw*{ zAxIEv@KfP95l2F^~bbbs%zsZ^uCo&i;E(xxy zDl9M7#KioV4*z_5EqUSi{KYV{M04oBvj7;5O4pOdc|82#VW7k}KjiVu1AmDKRhtgZ`Ka^&CsAfik}(k2ORQ;|2@MTG>yJ+ZJ)=x zbqz#z8i!bW!nKEhSco@&)adsY1^T>CP0Wr44!!b0_>GLn!ARh@-lsyp^*1iM0{PYK z=?ACBGW%A(>N4+_W1GRF=C?-PgGy*n?U))9W!tJ$-0ZYXX$ddEm^t)j zxOULvZJ45bFA6#VbQ;Gfz%<%o=Vme42j~pf4rF;7X<1b48!w>`e`CRGqLx3(DGSe* zz-#`Z&_HCuEYoO>8Ux|B4AU5hy};x^s!4QfdrhN7zc(&AT`68WoZ>&mv_~4n{{)h# zU@N{SR)*s0D4r9i_+#(^#a~TS{8d}==3y1zZ7KdZ1)0MtenNW1H>Xg1SL{cqeCZVb zec4EgKQgT1O_t($BP$+2SZPxHloX2ZjC}wr;wV1G+jyB%ROkaK-W8wXD4|{cqS^t! zIp+h@*x{(&np{}6TdE&m8VsN2-?$^K>W{;BVqw`9yCP0?e_Ua?3&Nm!XT0iL0-bhY z+3F~sqLfE0#aEB4_|LzXR`FRW6t9onha`5=JYMlO2peAUy5SUWxEq;Z>HcdZf|YEe zkAc7z1GSC7Y}f=u)Hvqf+(AnO3u+lqWxqBLG=U8kK3qGL;oZs>25;lMU-RJ&mTkLB zwiupf-nGr(z??)N*__<=mKNN z+juVwS-AEn?A;n-l@HU{88vpA?f&-HQKVkhk%h_bGzPWT!3D-nZ}WX_nVF{q5(U~E6sh`kTXgOD=fLqh4^krYy&JER?HL+VQt(iR=k zsQ8e!M~&@CA@#aL+MYI~-ZUYdu0zU<4{2M}*p?Jhw>zY5X+!Ex6Vh&&Kcrq(d`PHb zTa!ZSa)-1vZAe{dLRzFl8XX@}Bx*#GLTYn|6iFLWTbhtwLitCWPKXbwK5En_h1B8> zsXlE;Eonju>X637hlF}wmlRU7JEXd_AvLE7i8Taq%Jw#t;!Wz4vKdkJHz~YbuJ9B` z+8}o&2ieJrwMhp#HaBNn#gF<&C&XxNi)fW#f0S`J z8ms#A@#9ClIis&$>+u}%p4^ZPDxn9kv%!bI3@!XT-?F`p>&KHVXY^!Y&rxh=2wSv) zZ_2VgfIe~fc}4IxaAc}_#b!eZ7@~olX5(7C#Ohfx6c-GWaP|2ut{uX4v&{vgB3!*T z*LLB$$mW7U5H56GCB$vQm1%RKG7DFi&9zmydR3gMJu0Gbwb@(|;rfBig-Rq`EjCxZ zaNTQjp#lh3v&~f}T%WVKPc*ti<+%NrY3F}#L;t9i@&nfa>OFbzlu zmV_ppMV71yp&B8alF$sq(sOWF3RSPneD!)bV6nN7#pvbxf6x zZ6oXW+-;vw$5!fCf|Swe2-(bl zQtCQBp87VbI^HOC{6$^IpVW2yNvn?Ui?8E*sPB&uu+|rL^80WMtM9XHm!4tfr&qao zWR+Ln`UzFuO_dS)2qxAaeV&%mHiLb)sP?mw?dCe~MP1&8>5$>wayI@(8)#t7b}@pt zHP8UEEhck}b{~dRIozB~VPz_jq%fVr4Q9w=1IBhyK0<*UxM>Ap zK@${w;tNi~rB<-PEw7349=GMe+9fC$99EuEaHU(|P71ui76{Xnpx{Zv3S@+RZh1RY zgpp`E!Z0HV3cm39;UcUQ{5=YU@HN<--c3g3r0-ZBr4p<)Kbq5ue3V zhLxujjJO4Er9g-np~kSYu=i#i_iR-Gavo#_h#D-Fy(pEh;Ma}Hv)}twAH>mv>GGhf zRW2yrE$0dSOG0-9b-Rjpql7d18e#Jc8}30T+>>1%Qic2eYF8$z2*icUa8!~+RZm-0 zRUdI%k8)nps-29YKU>Mak7gi_a&p5E5*8ebvr$u(iO`6X7iY&Y{*M;Xxajk!UsBj* zDj9LU8gb6?qUf#!)oh3$C7MSmW1E8KM#WPVQ>@tlW@aFyOy->ed&>bxp$Smjy?>*5U4fL8P^KbNI^I+UW zRy2U|T?Pg>eg3FtpC=<)(6jjs=y1IKcazl&mLe>UY_KTDJ?+2CiKC3te$WAE|6O(* zB~ww@Gl)ZHMGN{<2QpeH$$^}pDJcROqbNy%WJe3SQwK6uD9M40)07l}j8~MTKqhn= z2To*pG1~AKpo<`CbS*G?yy4f-Amcq6=n2O%=A&=HrH^oJx5rzLVLM3OrqK~QLB00l zHEOhBy83I4c-0yFHW2S#>w@n%qN1E;?WyXG@6v+*=C>(Q>S_zB4T_uT)P`dhIwAk7UR1S<7p%dD`|{t;NGWu-98Aa9g*FTNL=FoaHVbMWc{7yZSAUS+;kInkXpVgkG&_kGZ<&LFANrmo zZlKNkRPFbaQTczRV+M8n)nASu-(ozC+KnoVvVFjOHIkJF7~Y#<795Bcw3r20VryMs zw0XmM5+SUIwr-h-21!-#Xy}0XP6Vr?p#xZ+G(!i#uI8g!V?VK*M59Zgx?)ohV4}U+ zh|Y&?ueQmYKh0&hV}sjPde2&Htyg{od-+O%E!DP{kHqGtu$P}=pI z+gm?{vBv7C!(@xw8T)M}YuE?|c5F%mtHaT26$qz+MHHekjh;-SJKU!2E{$t%G+R|p zkKgQ+<`C%$Fir zvjEv91P(+4yUf6U&?0UjAWr0i&DC2+C}u1 ztWl2YiF|G=Z5gD26b5PkO}6R4qVaGB>0{WVr8n3@Z3B|p9!>q6&52emjXnBp3iI~= zi9OPG1X=r6d*rsHf3-*KN)F$L`>Q=lWsfen(Pa=mf<4+ikfWQb9A{W*1`g+A=hkvF zV2v!X&cumbZ-b9kxN*_nEw@9l3{*otdvKf3-L?B{6E*X1Tq-qiCK!9@&{>)TD&BIu z_1w~cH=noOr+plBeAwa_Qv9bOzJ;x<$q{O%aoB7#_o8o!g)399CzzNr{AV-bEHg9XV)+GWdp+&S< zB7kv;IGh>iqKKBL(UEDiWg4xSMhklKIUJG)a-tattJN66>(M&4>S45=VjxX|9<8e( zwrh+l6pYqMVgJh-tqTRCbx_y?)1!4kBap)h18JdPw5}-0fnc;wN{T=*S_dU5kZkl- zQU`(&jiMw6g3&rDDFVT09h9U%CSXvMI*=2Ek{rlMnvx_Vv|uMj-|cT?{&+DM&VSAPz!kaNj?WeL?AEaBKeziDiPPc>M_5N4-JoSm`n zfECIp*CVl71`{7XSQi-G-tfoMV6v@&dK*rIZ@V?n3Aoqa*$ynG!b3>H+9rQ=;fMal z&^Fb>M_Z()^R4{-3TI4fcSl2qQ5`LoO_XXOvDtv`<=A<&JQ|2}W=(_9*SqMV1-ml~ z4kHE42h|1{^;Z=3NnjhKx7s+gJVd*%+f_MnuT4Z zft_j+MDIya`zh2sNFvj|tZYwIkvTo0UQ|wDPx#JQ6Jij9WQHpx`^!MNl|d;?r;IL& zBPbKc^of*dSOb-)WM{)I@)lLN1!|9@xBd+FHaZ(tSZwY>u7`Mvh8!yaOY}iX8KJau z6-SqL^(JLaiP8R9=9M6)*hlFn`aIQ8B_`7F1-x!ie=#Kp%XCFFu*cjV$vO>Cjc4~n z8yPt>(1Z-o@8R}rP7SaporZ+N0*X_BdJB0#Q0;I0dMZDE2cH zWXWMn45~@DYq3o$u|Ku;TD$XQHe^ALYt*^9gWb7Xe`3#nZ5~*|6xT}zSST|3@Q1B? z0p>viU6yQX-~EeM+On;lm$5bWPD|Sott|y`Os#Yg6ss`()bdKnlN+ z*j@1J{T!ALE;9PO_ge+apy*!KViHWd7DTLL>E_8(TI>H_hVZG^u z^`{V47yC7g03tGaRlF`-KJ{(^P7yDSm(3Q(yTymU=mo!-ELQC$j5Ru*n|1ggl7mas3L~93_$bB$-t=Ko+VygP1$|`z-;o3G#Jn9L- zHmo6I)g6Vc6?3qV5`$XLKm#jml;^={XdsH&*+2{IUpMSv*Alg%Yq>UUh6&tvSmBnN zXJF3<_ajJ99Wh?ffbd4y;bDn+t_}}@J?f-^6AoG_6^Jg^wxk!}3z9NzNmyKVByAX8 z5)FYZ2`6bNDa)3GDK|&b!r>*Yc1!X>5~6XYH4!3IA-YS3v`A` zA&$$+F^)jVN}PBJH6tr`o60mvT=s-(_t;sv2m2p%RyLzuqFPJRWF@*TyOXlgkBI54 z_z0(^(4Am6cCAUhJ%#1HRZF(CKsKBDg)VR^SlKj~zd*iuVX| ztI2Gb=>)RNm6tm})#pGkXdFFbJ_e=S!N2QW?*(0GXaKt3^{(EJk!07q-V58&{R-D^ z!$hXe;76hbO$c}q3}VW&)!JQ&;r+|y zA1A!Yc6fZ#;Z1ahw-0HEK95~&cSH-eBUbCvg|Z_W*q#)tY&#Ub=}_3L*+m4a+03Mq zA+@Cr@8&*pZ;C{-1V5vA_S+VY+ah$T@lDG=iZ@$6M0#SoMyaI3j-kuoL~uWa<$oa? z?_$Emyjc}O*-w4+!xj|OGO$TVWbFJ%GVD_#TuR{LE3yzZ^Aeqfwt1H#GY&FS#6F54 zMv5ZC?XNoWeAAKVwpCl_{fHs5&@L#Fg^w&u6H3Y~P@0&Kp)vM)x3-Ksr{ z8V3~mK#Uupk?fhq5LSgv1LKWD3yeM9dryTl3^w~a84yLn$arF9N+hz>>{6S4IQhAL zRzfyOhVIYkjus4s-x!m@1!wFBP@|`z4QUntyP-M58TNFX5^2C0F zKdQ926!{hEOF6qaD`Ou8OBOLy5w+(PQZ;=4#Qg~uH2}tUs_bIGVM-ubx^VB2O z$4f3ru8+H894#f*+-p*B?uxAer{CUGXbhs>TDuB;&`Ot!I{nty>EWAILe5R_iUHQ* zZ0~(HqWC08lx>Rh<0zxY&Qv{Q?ABZD$DiA})Zi1OI1lv1Z44ftJQw(iB z7lSRJ1rvr%ir&0N(~UvVY=h;d)Gjq1yzds^;4}}6tP#up7nO)Om89&iNTTR+=2pcq#O|P8K&I+6W9}kJ zC_c7JVV#hWtw<7<@c2B)k??PFC@E4TwHEI1<|S&OEcw|fwWxB!Qsq!3xh{9ukzgs9 zLXEhLaZ*F{P$4!e0j|bM8S;l5u_{Ca#_UL;h$IKLV6#Y!uAO1)180EdZv_(LX4-(& zdO!Jnjv|sR1hMl{Dsr-ob@4i;J?XxV6*Bq8t`?@;tR+?Zs*9U2p(b57qVO-_0Oo1re)t+=MFonA>I;yA$>?Lr~&AW+g85K4y^8?PCTh-9BcJ z((PjgDcwG1kkajA1}W7(W}vf?4zLcceayR%n5Q5?tbNREO8zVNG4EFUn0LGOF*|vM zeaz62c@@8p`Tv2Au$jBnK4w^?r@T)Eo<{e*!H=ZDTCmz2(Xs$W)xCMS#l3mCRTgtv z)nX2IHnyl?SBu`fd_LsB293MOwjIcmJ}e|*=1-YJF<&u`KxEw3p#*fq`i{Alc~k?_ zxaiN05i*Uf@XS2a1Nu6=-TD*jJ`*@buxIa1w{@~@==Rulw5y5s>{Lg4s4ceRf44ou z%DcUZyW82s{lg2B(`;*O4eXNkpch}@!P<>I!|0iTfq4JOyRpR}%3%(i#!$PA4{$8m zi|!r}%20AqOqsw|*)q7`hcbEyd&s0?t&$;V;7qYUyL2D(+I~FtC`eS-pK2Yv_}Gu z$36w37#C}c#~qkU2mfQxJH0#9cIi3TR^6dYdxyn-+#~EWKMFf~Ny0w;Be1IxxK_qDJo`Dd(D)BH zSN5UqSYmH_hxzj2$eha@g^}KV?w^9j)wr5lC&x~&7;ol>vN_XjWjl=>@Q&k`+&B}i-GOWEzM|y0OZ^V3)CWKU z6ZSH(?~VcpO?Si<-lyFfyGr?va4b8HF|L&FP)+EL*jI)r^}5ytv+A$(43(vC}?q zHSlUusd3RYj%e9oR6cm*Nh;i$MkGmw0Bxkn(12l@EFBuH@g4XP5$YS|6kSQS7XR(TZq3l z`NMbbhi~%FVebi34CmOzgx!qIuh*gYw7)+lvOiMrJz~PI-^2Is!kB$mZ}Jf%{2f;# zzH^`PZP>uTcW^cAPV@=tkxw`r`t1zZMN0WL2PV=yyy%2zTOeB8I1BV4Ga5UR9Ntc2 zJ7mH-)0%~Ax1&#TKio2Ph%neE*=||qB9Op@KFLD7#q~+HtIhQ#kfJx)skf1OCfkSY znQT{9aeJ&7LPzMC^f;#MuRW7=otXd3o(cDUS&d(b&o1nC(c>P-xS|(EU)a_7aRo?s zPcC35`M;}s!Xk?b5Og%wl`*IN{VdkU2k6}V`X5lb>HG$&=Qm8XM^&ol{jT1}wtdFe zSv`r{HvfIAdVZbNlY+j^>WPpT;ss8KR$JS^9cHmRv_>o4^a%W*O0EJuTpLN*VTxE) zQg@glaUG^eTt6sXhbb~_hbf{eU*w~7m_B!=o&Upjn7EDBDl^|a%l2cJndOOP=6WQ9 ztIRM}9H-N3>KRJzHGRdAuMZ`~&5X1`Qr!CL=7AN=2fhC_X$sy4cI4qJSpBodamRo| z9?fH%>M-GHcbTNyHX5FU*=(2tuK(S=CW+mNiSx%NS(C-i{IS=S7R ze0J~f3amyXmJsd>jNLZgZZ5H#0~3|049vcbc1aXld7Km)n)+-*yqnIIWk8yE$O$1J@T<Fbzjy=9o^S#YIwEQrfsBrPrIV6GYuY(}ry;fj!o_9<288QWE!Jp*k6# z;;LhHfQ7?Ot+;(P0?#|_(1(SrVI&U=sogb91*Y~W=g5y5mD_ivV2uG2c5}xvV|H`Y z?Y?^j+lJ&MQ3q}ekkyPHb$LLm+Cs96Ma5{+he}|fuy&y)ko4`*Ob5&cEijtA^;>Aa zR5RMD$LNbAPgYaQK&zU2X~l60_Q~i+I(_KLq1gxmVI50Q$A(&+gB06S9UI~%PhDKq zn{|+a!m)W=jgB$tZFSzhMQ(jXsBS_Vs(|r6)kx*MPd!Li-a*VaG<-?p2{;Q^{|`_s z{5(4v*mXFLYTHL^^o_MDzqo%vr9rp%-ZS+33~roKM|u23`k+qM{V~0BV`nC=O--a} zX>9M*9EH_H6=BX)Dzn{0O@uZo8q$cz8ZazVZ4z`)E#}VH7qPp_)|(q_vFit)6_A{> z^6OKIpbkylJE-(-RsNB5*tNY!iplAU;zHa31h%xJt{37>%C4R;nGb}NfXu)S+-0Md z!GP&gQ#CEtR1G$pVVMPIJgvg~JR+s8SjkpjKd=s!6gUB>%7Ob@K5-Dw=^${Z6hXvL z8IUN5?^qR}m2j^k;bQM&S_p0JLfBhL{Al12To572hoZCzvLs_Wvsk=P#&@Ypg;1|w z)5pG49^nYsYEU~>M+!lr0I>_gH=JIJ5L&tMXr@z&o=48aTL5)Qqra$;9WvU1&IWc> z=s&@DtOg2oNoyF`5ndRP<|A5lLYuc)~CtRv#xehyk~4$9Qddf*3n- zcuG*6<&uMfI%PW5nRP_V%1nqFR2w99gYYr)G&8iz$x}ZfHGG~b4sQbk`FLt0Ls^(5 zL;0GJq5Vt_PS5{mW+-=u>kL(IA2&l&=Rk5)kOjR|pSj`~*6XqSi4~4$2zyKqST&sI zO>87gJKf90!bes8=XuU0YD9!{OtJ+XwRjpTDqVgve_D2D{lnT zqEBXScP}@$$QjWjCUr71)S_0VVC~T$V=M6X*u#hv^ihpXbIcVYSk!@m8G$woT<`ra z2>Kxet@2bd)-rDgt-KaI_6evp_!s_!Y_O)HWn5MNDLWg?25w}I8`yCa=bjGto@L#q zk-YgcT$C6M_4;4= zcYhuaX%E7eN~OcGR-`K1{Ybm_se|${sXuH9{2>zHtXa|rqU3%^tc?vtj1Lnv{sU@( z#8_)Q7-I*q6T8I~II1<)-zS+32ZHzza~&T>3yzwB0Y_mRIv;rD1HIn(q4Plj9@wUr za9;*jBLA`_@E1op~%w z#&>A&Nh{h6zuv8l7!JW8L<;_hX=40pKb-pE;>Z!W_Zffk)YbkG(ZhrnG?mc`w|R!xVWhv5bIhKZ8H!YhC?COEgNmJ)XWRgk>=xX%<{U>&wUbp5etM zmF2}3uPwZzvZip|#YKfYt_tEjB;RfjfgsI00fUh6BY z@zs=+R{QYD>f(zox@eroFJJb)(O8;q0#t0vX1lEN{uKZ4QEpP<|V{$#MmWxKexURIKxV*Tc##dH+XK|SiW#abYnvKQ96}~(l zzD(=On|YBB|8y9u)Mw9qH`d_ewhV@)OXDsBd*wRcJm1pF@(pFhNz_@xQfCiKoii-; zl3}THhoxRREOp*6RNqzjCVVwMH9d8lXC=O>n|ouW`iQ%4mHp-Q%Y5gSt@G7X5?HyM zmMoPiEnQc~wB!J^DG!yq=)M(|)z$pYvr=Q6Cs4R)1P;3ZsgGv+RF$yH3*<}b@-fqI zFJ4!P;=T(xq^vg=ma3QB=4FD|dFy2}+5OLQ*EW|eQf z`ks9i3}0b|&nnfH;8n{4#xP>FGRb0xl_||@W6`;x3(NUuBmP`I&f_p@qmz4X$c>cZ znt;`XR3UBk;)@Z>LZNSM>AH2rRWv$D*)`7NR3O+AtH7j)%0~>L#;%X9q(yZo-GulL z8$ettCz!Q-`AWKz`cgq{t*)vJRlq`8^*Rx+Q`L!tQ4QJV^Rvq?n{G!XA&N^&ii_?T zsoS7h0v4x)=#TRxAl&vc(UYaYI1j%l?L)0Lu3oup)y+O*<;v?*v+1m&~mO=vT_ORg|Si<^BZkRQqD^&HiT-beH+o57b^<^lR@Ftz8SEa zYk?Ez!bQGWw`qq2ECHpA$A}Sj3QFn{y{fX(7bvW_3*C`I_E@WZ*P-Ep=|;zb6@(RI zxSUT@*;JPn{#pD`$I@AzxHu_Qi&3Gg-4!XBg)*()A(u4d;RFpHYxt&J_}e6JoU6f; zME42itTgn+LjM-_w59Uj$8{GxPw|9P3i(Zw72b2U=1U5{8w>Az!fE8MIeU2eAXk#{ z9KswRo@Du4Qpe-fX_Z9pyG+yh!CX8^^bK56!t<|b^k0#$>0j4All=D?n%HnjwM{J7MklzbBq?;Sg}JgHA9`Q|V!zdthHE#ND8RP#ywN-2*Y zko7&G`J|quQr}YY^?+{!=iBi}y-Uf*LwM8oYCfrdDfy;h@8dRp(*%#y z!<2l>!8fl<^GSV7$yWnDPq*fidYO`MC-|~|uKA>XrsV4YU)x)nPwHt(z5(zJ37^#0 zlzcqYTlZTnPwH(-zJ=f$5gr(_?xtXl^pbcgk7WmFd*r71Cm~$?Opksu#3e13Xu42Z70iT z3;sgEpDFlvVTZ{-07(8<#r?9l+W{HweS*J7@LvK%F)u&4#N#;+{*SNMa)tmY=U0F; zfR6y;zcX0hkHCkht%>P5Z9|r2lioeTKL%x<%8^10?;^fQ)a3;6Fh7M*M(o z=gNNoNIf4D{0>0!^HY-N0ncy0k$#oXKP~Q4#QiiX7?!tkzXIq({@w~m{+j?9&LVNo z6?c|~xkK2J0ebFF0m%8xZ;)wMcf|coaeoyMeV*J6 zf-e<(0Ps}c=L-H}!G8*H0`SMs$)Vgo0#feJ0Z)N@ui&2({I|uuUEF^o?v3Iu1!VgC zYN^L_5%S?RKr~B}TE+dSzyiPvK)+7hvjt8O{7YAQJXqLX)Coww9|-)G;I9^#EAUi- ztxG(fS>S&Xkp3SMSTFeV1^)U9&37Zsxbp;lM&Pe6*L*Jms&ofr`s@LOzw+-1 zeuv<<05bkx!`9nO@QoID+XC(O0#Z&kAmw~;ndbiqAn~n$#Pc@8Pr+Rx_(H)OfF}a~ zS;2os@EL%o0sqE)E$3B0%4q?d1pHlsFB1GJ!GButCkp=G=V|^Q0aBmG0V(Hi02yvj z@HYznGC+oVy5PNnf9Fyy=Se`yX#%92`vrfK;I9$<48af1)%=G6$^RT6^{*3rjo=pn zGJP)roDBLDz>@(-0W!QlU!wiL1W0$MxLW|3uHP2?cEMKwGJZD*p0~3x{Aqxv0Y66Y z9>M=&j@IjYfRz6LAo(i+sn06G2Lzu7$a0n`_+u!XlJ1=Li44E%1c%wL5#dEnEK2T{0zaL3W%rtz-P6*e+H!grvb@-10eeWFPy8};Wg)IJ^G+D z!~Z!T`JWT_9&tYgNdNZ>ev9Dm24r{(1%HX)&jTcX`&8Y2UId7GlG}Zz4sQh@!&?T( z@Meg6nz&B|r2h=Te}Kl6d;@?C?;io_{}I7|6OjDhMI#Qr+z(FI`Lz1eI=nxlGLZi$ zAmzLTNdAL@-!J%9K*|qkdgC2{qdcC*VnF&|4M_jX#Jy15x#ISTI|q>M&RD@8LuFC= z1F{`?6Oi(H1>XgTsg-2dj;MANPVvXWIWCj{8@mM%SuoB`x7<&H9*RH8Ibg5 zK+^wS==TZzZvYwZpwL$feXh_i61rFD*+PHwQ(FEn0U7TX04e`zKMwbA@ub^ z&lmbaK&Imqp`R-Bqo-*31AwI8BJNdysA9PTCu{m)Kvw0K0FeZ_+r>R!=y~EkUFe5% zG`|VR^w})#I{=Y&xr@d9rdQ$zNco+Br0*2>5~0r*_c?$kfH*d2c{a*k{zen6f;+`b#_mTOe|05vzzb@|8 zfOFtp0(ddt96VNd6pTHd#%4g`{~nO|zXLoE?gs$R z1*`;|igHo{I34a=0m(NDkp5a$#`vEEc3P8%ABluiE%AX2I`G1CSJ`3`X0#beq zkn&#ur2MDF{e3{n-zjchDoXkFfRuli;Hv;Be;pv@UkT`g`~`rNKNFDhM*~v+hbWA6 z_XAS?Z^YdLNclenr2IC)KLbelKLDhB6OiM#djTn*m+ep<=gKJG5ckD^l+Sa7#;7$K@Tk9x!;_il#;j>xX);h^Ji;ham=O6K-e;Fz_E z`FsUG<;Hl5&mxg$t<&rierw&T#0u{Vn*SZ4TkB2_3f)?#IaAzg&er_*128;m9f{?P z?j}w4FdOK$9+pSuPo3ls=gjdxD@PbO*G{)z+=Os*u0=hw@t5>@;@%xtUz6a5Q|W67n6K$}{8k|S$YVV}Pw?NKK(6-9+Lp5TTmY;X(SURqP_ zQB=LfDOibp9p%_MQl+uZwxmQ#33lsJ%j(KdRZ(#edrs7TJWp^PH{W`K*g}T|e@`&T zx1tSq1&d1VpbO!+T%;A&Xc~1a;%*)&r+4-0?QB|;xb1cbxLS7Jd4eUfC)nbyE-u2# zyG>RRT?f6YiwY|Ui^?jo4^Smt!bV-BUE-!y@+I!{Wf~{r)*;8Vs-nHx1$RUCTXRDT1}|Nx`}aRpoi0U%WksQ~!kS_$e?usO^Ac&63Mq-AOG=u= zpR~DihLf61b%_PhGY-!=c^Bu+@;Oi5+?liH&%Jc!d>}8GnFqqGnWW~;oH=*S9KcI1 zovr!Tj9Xt=R#{qok+QrmfAISc$G|k8`BL_R_LXgw$A=#t^gTS&=}z-Zo}~$0!qbFK z2Or2(KlCxw6AacAZ>kXoKrpz4$!ah_a=pgcM*0c{xuMVTpD1ro^qM2_Y?&4>&+)0>g25sf4{Tx)LSlTU z!9SmyMBV^8eeOrlHVX2vCA_exCK#&NSX!|*7+juj6Y)|f{(a0yJpS|5e2HghuFgZC z;zS06#U;UYRfXlniajYmn&4kOl4vEEhZ4~Wjp18~a45iHfT;?dt|wve#vdLAN_^X; zYMkgCX)Fhhv1I~YsI9YdKTNI^cu`HW_@J3%{ocV@Ly1uI$+gy$Um!`Ut9IEf4kvbJ~| zm*d5yO?2XBmkY<(+Tr1_IT7CsbpjAh7x4t{hJZ_l^HtZ$5*@dM+1lOnW+4TK6%AY+TiG9 zugVTY&y{BW=!E@BhphW~&4H7cLO5(HLVF*MllH}PoRhVMSDV%&aHN^r@9eBCE=k~n z-x#CqB<+vXxzodfbaAUpq?L1}p3b&{!_hh06c+gvtwk&fkZ3AFmSipjO z!jjwM-g|uW$qXGe3kGq>5NSh$5UxFH*O+6BaBPW=-k?=6RrxCz($T}|RaHojX3mM2 z_`X09PFqR^)`Wp53CD1V)_rJD&gr_scZI6V0f$dYE2+I&m#aULE;EMJr9T?#Pu9pf zhYI5~gm6#tZq-)fn9`}pEAV?VjoJ&W6 zHilsB_BzR~f;RKt;!;BwuA^cE-9j7+lM%<1)eSXAQ3YsM zSOsu?)@_5Y{RE<^jxE|b4g6h%*lAF21T>f!(5HbPVc%Q%MNOWSe4&@T1Q#5NIc#oQbMcOLgNWka>7^Tp_ zjEVo`FWRr0kGO(R6o4;W{2|Hjq|xUPB{8pq>VWZJjMp;AjSBW*d#gU?n6<}DXq!|V zv1}e+9M5M|od&D%k|9jT<-LfL&)urbO1pAYIg|!&p}N={i)KY&B6FP8aG`o;|a z8W&Yk!oSO~kMmFMR*mJAwl_AZ`a9*86b3BSNVTAiE(9A<#TVl&hYrM+pfg`xvdH{b zFR=imxjzo>R{ozTLuuW@n)OPbzR*VBaC45jcUft>0mM#qEe6?~%YXFEDOOS#l%^6a z+Nkf@P#*z5XX}GZki0Ha)07;gGnupqr7o{tiTyqQK#;ai(S*qG3JV>CUw?zjA03M; z$A|p$51Q&KA#a0D7vAvW-`vp#LAU-yl}qpVgmdIGU2SCgHky-_D9u*e8(z41ytm9;ZAQ$3W3h*{#k2UJ)`{MR$3D4Q%^0YpQmZemTIgAQyq=2m4r0HK*Al9q*kel7gM!Z8#N3&4=7kUdc-H?UX@n&=Ruo zmsn6tm0nign%_X0$dC}f0 zqgi$GofI_~l|Hbms{DuR&(F}iOO^2bPR6@}OIv>~OjUmls3cD8FK2)?xxf526WOr~ zAIAjbr-?#(6Of{=4c`+#7GvKlZp48MJsxA(NRA5@FHsAQMnhH=7*KTrU$D0j9A$8T zW0wa^HlY1x{>F(@fpBiaRZNiO6TJ0^a{>~z{!=R8xN53Up&@i{RIkFSlRA9#9$fbj zx$Jjhp8dz;>0HC2`%9Y#W=>_bl3}~_7Y2UC{z5DP*^{ zP69gi?_<~x`@3f>UT+5<9kAl@FbExU}Vo(LVU&Wq(Qs8QdR6 z5L`g)G}`bN1{xm~8uT_U#XEvnOjMv1eU)Bx?%TXiEu59{zQ5>Tbiy{(7ce^Ww-}v> zNjK_xTYe-f2LhCdZj1dBYi>IP;O0gZ#YUqUQN#6{_?bpiLWpW$q67un{29Ei3Afhb z2Oo0dD?YBXz}V?+^e5>S4Yg&4TKq-0h-%Jd8SkW0un7vbXa!r8f-SM#EI?Ey>cNdq z&B!9?g^w-cj?gxKTkdv70AYDhM(O}k%vbm~4{RfYU8j-9Bjk6TJHP7?ejTFNjh5|i zxxvBn-ut-QOIyGqIK#EfAkMmjvzy6=PL+BsI*8kQGyEAub_)^r!x>Hf1;!3<_zdl( z$=`_=<-5s`){+upuUZj{orEA!)b1gRSM>ycoP$@Vs9mW1iuGVoI|cNlqNcup;gmLi zbaWm4VaTV3qsb-iYal1&KW7u5KFV33&HMC3fA|AvM6;@68)#eC$))Ak0)^56;F4mN zJMYtTZZO}$yi#VU)ePa%I*_uVkjk<8EghYKz zP_oCR4=(*h^1BNvigXJYP>E1bw|X1z)7-u4E8i%d2fa^ESfM^JCGpyZ$hJtlu2Rf) zyrKndnFa831ZnXlT+GB_{hnySp3H()e6!pg#fgX_uBn3UpfD$jRxz*8#k@%ubCmW# zlPcz0rI`Ed(m>r&)>)#R@$O6(Zoe5CGz-wO4yeh6!{o%|;{s#A+jtJ&1BYcO22+9l zTnH1dKXhg%43R+!u_bAdFN)JKl!-yONaDR>GmA9rIQ8_=PLr_(6I?T3ynvC3%|?iE;ag#7f>Z@7Nxp8C11tE$vFkr%Dh=?`Zm2|4w6zLB$v{A9 zcUaaeoPmEJy=i++zdTvq`%33{) zQvDLu;>rzr>UKvJdslyteQsFCA(eP)#>X9;jE8jp@vq@|>;IF|bMruxDjib$RR0tP zr`HVim<4Fs0$us|G%@m|7kSfyo=X>61vXJYwfJRDX}osB6zjPtnb^WteUZQ2xWRE0 zFM($x2?V6JzkziS)tEYCclNf1Y4$dcO<@w$T$kpodbU>Wz1wH_{Gws>YXu zxKO_j1Y*B6y1b#!4AeXK#mjBT_^t?aDtRvM93Icw_=L89b_M|I9{`ZLpUD;t;~3TFWZmnM_paC;WT`#hGSjYu zvbL|6&)SjW3B#TV2o$RV@O)f--4F{az@U~5;>Op_cthta%VV!*)mpQRSX0B81=CEr zX@Dt5eZ)i(S{L-R2hpgV3zXt;tyGE}n=__6F*oCz;IPg$V23*RX@KuDiX|K_MX!hr!r~D4=nRH<(bQ96lW3WZ{>MAFa3# zzs2~~;YX$aD;c2aaR%dl7-S}XWANh){8{+1CAbGaGV}8G<6t-rhT~v34u<1kI1UC% zEc~v(?;iY)^Ou2NCVpe^^Wt|Fepldk4}Opk|4<+5!WNBV1QyJt z_?h@k0r6xc;8-N!Xe8i`NWjy5prb%%*1->q3$=OGL5&LauFF`l}`c&{6yasmO#!^MBIOq>30)to;U%io)+LQ!Ty*Z*3w^h%zgOm7e$5wdzg4|olY9AQjweZ!ne(uFbUp4%bNNHmDnn&!E6y7(a^pNAA#d);5}cUY-byR( zEUv1N-K>}4+AD0s+oW#b(-hw3Id!VdK6}>4p{Nu}ofUeQ8w!^^Uq}6(>R}%%{@F&u zfTvo6Bzg|&A)f{flITrBr+hp~^qDk*cwkESB+;8kpwC35L z^d?qXJkM*8L|?-Sh-an-N%U<(|D~oR(Kpb*6<-yOeyqzpCjC%_sFQB_Ge!%=?YzlX{qvZ#VcB3!l`-lzhCuYUr?*C-pKV zAMdYP@uudJ`k9iC_gC!@KB=cE`FJL;S?-4#E*>@DYY{%Fw<+cE{;E2;_ett+Nf#z*i@HQvZ|q?w+d7FXo;Fh^A=D-}&_U$ZrDDT`chPfYWh4aXKK{ z*4*(LUtS3Nb2i*d0nfp4wkL6zlKy@)MZ+#EQJf2U3m~?_=l%dN3+Edj2Al@|2LRE8 z<$e_q{>o1Rq?~_)T|{zumt{DR!1Z4c$0V(Gx zK+4$-NI5$JsecrZ{EK05sn;Xu_!CwEq8d&q0;HUq04e8MK+0JHNI7|cjF%6P{`R2V zV|=#(GX2*9GTm+l#Q)rJ_>&3x`)FSwXUcv+hEoAZJ_8U*mpca#Ns{}^9Cgk%_n!gj zZU!6!{%;Gs6%bjPyBrW+az_C&J$}If72tkA>i;|-^=}5G{tp9E{|5k>4t0R!zXFi* zPXlB;#sJd)2k39m|L+0mzYmc9y8#*h=K;yT3y}V60GS>+fGl@kLw|>IHUm;l4It&L z2c(>3fQ)w@ApM^Sh$5SN5+MC$0Mg%43|#2%w}ABb8X(K*PXSp@p9Q4Hi0S^xp(X|JwkW4z+;f zF9D?gvjJHShCpQg_9GDH|7(EoKjm^j;y(k(aK`~MoWCG44Ce?S!|4ZPIK6;i$^BxW|i|p3Z|lN8t&tQ(}O0{}PaLUc?{jEB66R z`Ig|f0+O#z+_wYL|K|Xw1HS-}dYvrv69oS|BnI(afIv;*IVZ+*1t8-+fJ~vg1CVZ> zqo;c{ApTD|LExJ}5`PeXh<{ih&-+5ul&=6XoSOmhf68L~p}bju=R&`80Y3{k77+ZC z{s?CJ>jMO8%0WQ#KMgny`Sv&2*Nd z{|Au%9tLE3eFKpEB|^Vd@BzWk5ImIt-~ae??_WS9xbAM9cl$HqcM{?@I8{gVYN0n@ zq}`{0Mta@Z+Wm^~=bWS6lZC(SV(sR=0+er^Q$Gvu^xq`>8F(kXFHiGVfZu)2`$BOy zp-}LNSahFqdU%fl`K|HI=frJ|S1QD9jc-`~$Zw68UK4rN_}43RYrOa?1VjEN8GjuX zerr7ZlJHyO@9zn}HC}&G=+^kKUg*|%mgSl9t?|(o;kU-4?+e`;f5NHwt?~Bvgx?zf zmRWSzSw7F;r}RgA!3W(2<+jG7bHr_pKUav`8qfVR0%Vxh^A^H$yZJ>pxBEC4-EN*X zaJ%ah{J)>z#yN34%T`@dx(>52din|n#s$4Gd0xn&CC~UcwB&gjo0c#?W#bvRng&Z& z1M@~{DE4faqdJagmj^dgmN~JIIi}l7D+;Ubvd_C$Z1e<+H76pEW}_%I z5hoLpsYsk!Ok_`iN}AHN!xM@fD?Ke}lVyr-QzZ`6^C&!y@>k*9JYNfo?%=`q;5vNn zz~-kuwnM$IvktJw<-1BC9%C1*?beaJYlB0=y#hfyp==MtItdbocG4scZ6^!P(5tiV zaS9=XQXV_Eka|ip0m}e&u}C05c0AslK7AN%1xR_t5ZfIVaM z%OzWGb1`eXuvZSNz4*3vxVF`2or-S8Fu@bM5`Xgk)PnJk$HdBiZ8vs-wlD@VwTse*gTD(sL+A;%sxV3ZQHt-p4z9xuM>z8}f*KI+*z)jwX z9S(}z6643^T?t_=oHL!V$7qAIYHokdQtzYI-*=Twy;!`(7hK^5(db`*t+kEM;a%^l zovN{a`(EvU+Q%l*dMMu&tv>f&IBd4PP5H*|s(S7FaH8*<+`bPXMX=`Ej}#fq#2P+Q z1P6y37tMDwVJZ2rit^#uHyP!|MU&m+9w@HJ{jvEZ%c+k#D5B0ypu8+bFtS%3=Yg>k zr++y%&Mn6t=1!xVe4R!Q0SZ$aR+1MOL*B-PbYm?UyV`p2?Q?9^VIK~gU$8qMhg)yd zaqMmta(9frK~t`tSPS$(#Jo>%Phb}|SK=K1%PNr(0`?B5oj)8JTU!8ldQxW*dW^Dr+3R&vCq>75og-sBU8|ulN z@I%eb+7N>cNtC_>C`j*@IQK;7+&?3Pc!h>CLkQ?4>`7ID*CO!Rq0Cy~^jU5z;2$Ai zl{g70bgELQ7TYjFz4~;Ou4zo(5H?@DCq=Rchk^9LgT=1z>8?PHM>dA~&4R-w_8(&3 zVc?LRl^moY?!60)!`?<1q_~tl}(J9N>(Zg?}$ruFEuQOX_aP?`R!Ibj=i0c%v3je@&B8c37^ zRGbuS+enlGlq1DX`q<~K6xX^yFw;Y;7p`r~@YZv%jotFmfLJZftW7tCx z3%5l>t?;Fa7V~iQP@8W0%`0rE>#DlFeLF#Ib|PNh#ZOKNS~or@$?>@G8#O|J56t1XSE^r4{kV zJQiE+;R1BXxt*|vHc zu0l3}O*iVgNoLO>5_s9&B*SXBy-Ab&@)qOeR86wgMbJ&M>WEl3y&&0-$G!%2+{GFi zKC5$2T7PJ$nkqKNXe!u5i3WmJxDX-}>dOoPryFI>8Um{et;>YOJ5DN!N|IDG5UHdB z9WT`}M3-45$xcP&4zsxaAM7|uFm|ff-wjei3aquc<8{?hb%pM3+>%)171gYcuG=hK zrPNy)b&Z9$;&3R|{uieKITWrvgsrt4DkSvy{-;I}?Zu(kXAx_sy|DY&`W!>l@GXxP zOkN(nHNK_D$M#!}oUq$Cd%1aQ({i(5hiPp0*87k;K<0Ssxp<5>pLN5T?uY)8{ZMrw z0EQp>kUmC3JO|N&YUmyf{Ydp#hn#+oyQH8AUR>8k*H(SrwI7x-!UaculZt zv=i#oPd(=y#P^W)%@H$(Yw+anGGw2NLr7R+ZPxuWvK_+;PQ;UKxD#=L_?;lK$q(yIGE5{9{tWHB*=2} z3|G=6q}Z3iz>qD)beUC$7=X5mnl6nN+FeYbB2w*`ePF|TRvl3?UpEuy+HmR;842@z zG!y49aZUr*!l_I*UsIV0TY%FRwY@0qTx>!O9HNBSOZbD1JmzcMnTC;$1zOUXFOV(?NAj*n3CpcEo1zMU8YZ zF%>Q7iUzc2z$4xZ+7rFHbH7!U#@5*D=u{`9(uVj{LW^>3JO&dS^NIQIPd7G&?crE9 zT6Ht9&0Ei5jTGnY;r<*G$H7&}b8tSKi}TiF?Z6Y$(=B>lK;+-QT{P<|)niNT z6jd`G$i8Hrvqt7&=B3X&(ay?(dX8&0cho_w8m!dg#kNl?w0**ZF#O6xQ_7!2lw)By2_ddKw70ry#a*pnAPKy|`Q zW(NiuM>E;Z=(#S`t`;q#MV(Nug~MHx2E2{SfZ}{+R}|;z1AFA2GxOC~-ip@lnfhM% zxW{YkMZ19c&I34ykNGT&4|+0=EHB!x&r4Zp^pB_`82aI4{0nIC^~oyKZ?p@l9Jj=1 z#c+l?(>Q=AIJaZWRcJOzAR%_R0ir|s=!lI{)eh#?v3q*37?=KQ)N}+5AFn5IP0!=< z2o%E(%hDaSE#1*nmhOZLQ(3w;Rp{I^Xy`(&R%ymI^%zg79R8Hs7*V~ z!M}a)I2K*d!`_x}7NExIPOq{~yt20Ol9ez_wCm5D`+s{oAMm`Z>%PB6!Wh>?q9Y4U zYVtyGV-wzDn?Ex+1VN=#p;6*AXdzvgY$Wj~_+ME{83GkcOYMhm%>Qg6Evu5=+ zuXgf~XC{LngBPoyB_g9i&+4pd>NZkP6T{SE5+2y+d+xcv-|zj0fp+^nw$6M1pL_1V zbI(2ZTF0n1k&l8F1;$8~vH~@^#jj2`bGR6Yd84G7}P)I$@1NdSKmU21e7y z*?2k@SJ44|>WgD=B$bXv_DIGMJXT(uWEi%y=Y>gf4``_aC-TCT?MJnLdKKnG)-%M% z*^S$N_+9NaRvR-{JRB>0yiSFuLxpuY6k`xH`?V>-r`t>KmDEXD&cJJg&f9UkR^>@< z^`4&9`BFUbYfa|!7RjKn*eiZhW!C!a{@fU;)JkwqCsbUeopga@QQ^T z3qm0TNm%T7BH>I_ z&9t+M{B~og>)I33Q4^72jS3UDjb@KKti3diO(($O~goscIBuG5~dRq zXu<}kH7ygPK^%$DDuqQ)2)oUhLhM8YYS8YT$GA&gQ9zLMdz(;SD?@VrEFEYoJ3NXf zBFT=WT+Z)1)lhbN!LVkE*t))2(^3;HLspI8_j*}A6 zZvx4zUIJ{6*wD~ZvEB1>g9PhOHi-X!o|z+9$z!KygKTD2()H@d@N(J6@#6RIc;uv> zZe}mce;;K5KV(#1E@=b;G1yrN#e9Z=Uzui~b_}7|B(_IO7z!`R+(%frS-=O1sZA-C z(lOS#F}TMd@tUXXS7%=_iPs4h{-bUCDT}(>3d*!(jAK~*N!r~p5f-K(@!D_I-A^=< zdY_30dO_lKjA7xjDy2RVQU{5b%46qhk}jYs$);(4BJtXf1f`J8gxIjKd;o1h;l*tO<5*ncrdmTJ=~}_rkT?y&sbxQ z5W|MpqvKGBTto6T7PH7^$L~Y{60`0lYon_-NRWK(&+Qt%hoYN)*9DKV<%aOfJU@G% zf|3o01j`l*ov}4#GT$18t^9nN+8>bkp+oqL~*liI}kt ziBPPy_=yXf!@AkG-vk!>EBeIMU`7kmuKz`7$&f^O+l^Lqyf)#gPPn&e_$}dg(Toz$ zs{0b64rm@F`wR6)VFo5lL>7DE0ZNS(?wnQ>>a{o2D@dzCy_3*!=d=m-MSW%FR-= zDu{Z!VrBYy=>AnwGQ1IVNAGM;17}D%&-PS9@LyHzPxY$Ejiol5e4tbH&KWm$qJp!$r{1(5VZCtYc z&sr1o9}37Ktnsqu$=OIdc;rUt8rMhP?~9Ms>@5QNTAu9NO{7JuG9XQih3HW0d-SnA z2pjh>CL&vXrj-_TpF-JjbA1pLgN0XmW#9;Cil_{Bmhz4wPi=FA7RRPdUhkxjh0kOX%2<8Ayg@D`KLqY zo?j(3p`mSLzsO=osRq4msH9RLI^(2m6Ou7Cvp8 z5GAlgA#UZ`d=Lt-2)B)7g#z?Hy$VXu9udb#G1+uq5rVDyhh%fyHWH+3E?EojxD+1^ z6D`J9zo1_}c*l*{MKe}!Qwr1m<$?YqCheM(cy7Vd$Yl8ezH-3f9c?@aBGlVA$Fq-- z=d4XeQ4ZWTVoL*}`~!WUB9^Ux4=+VbzQ45OfmV9iwM_>wz4}UcX69#zh?0ozS$uSz zdROsid-40AEl22M#rxX%?L;^Fl{!Wuy}_^8F_J2QJPuzUqP_;X_kkQF@7w333+3ow zulU{KeXU8!W(>2VtWk!VhQBO|mDPJ+wmQ_C#dy9I2|!2p_;pPOF8f?sZ}cJOMFLDK z&3&Nhz)KOf7av`dt-TzEm1U($x5k=(z92+^neFvM{|3Kwr8B|pEqGN7 z1Q?UhZIcI$PO@_lTB!$3FlW86_-J0VqQjNGpkJ}KoolE>R_k|h*%VVT&wSWoQT23O zGx1i8-;Y<1-yt8q)3J2EF>yHY=zFrkYL_M)wQ}YBjMUA zLi8PiML^O@)c!eMk?Q^q20SxS&2DL20so|eq+>RbkIjmtPUT4bGox1p_5U#4zQ;M~ z?QGxefNJPnps5MHZK$Db0CAQ0mJ-@)%}1L$q(F4t}{lYke~(iY!@TEXw=nWYKp7`EMfBHDG(0zGG3)mHQM~j*_;V} zr>*Erg!b+aEUHelq%6@?S?N2QY4w#>d_UPC>q4KT)G=oy1J;rln%VtiCg~?hpO}@9 zhFNQ_ro>VVnm1)+@#ZBW9X!%fCLdj2Cfv`MONWnuyF@pXP3hoWjU2)Uh8(q!lsPLc zLQ9ZI4S5FjoNideEf#wKL$1M{i?H#o4j@-$)b>Wj)mAKOfryVcl!uD&^rmGRVEhH4M>&TSCR^;^+o@TVyGTJ-4nAAdr!J)y-f6<2S z$=J7NfC$*rbl+n0gBi?TMY8q^k~8FFT;Ryw9-3(y{(4w}T(&+Ft^@@=P0dxvb`6Q? zAd*jvX!|yYWYdzNq{&=Q^HdhJgGk=&Q%6NKRcZDjYv)wh4Te3TS}BGm@RpvYO;QO4 z?hSzuL>y-}n`z)dNEgb7sC^)t^>!uHW3ziS=L>2Wq#%)#*9;?laA%9{?n$ju+ld*jR$cjagfFt1aGX^k>(ZSAN;`=k@Q6 zGU;44-89^9UK9L<#z?m~9{3o)A??Yak_NFR!bwITw^$dHawp{{k$#vwnMIjCp7F3U z_5H{ad?_Va-hHJ>hI*$MFb>@zCdzrLu_qq*s0 zD76G-mP$wgWp;E2=hg)Qm@6MuoWTcSd(eWABm#lb7`$U_BThk5I&!4vyLDO?3Wm7} zs&!6C3ZvN-u-48l^wE-fHE zSO~JFbHxZV?FP&$R;So>nbpbSH?$q0Z^qSWGOkXvS-Ji@_YK2&h!YY5!qanHVa&eX z3`~g`sW_dyGBu@kP8suXb-@x69If zY7uNhRc;?y0yoK=n&VcHyFKdQ_(&WrEo$i4TkP0N0dO;8jaEo3^w?`}gouHJA3{NlGrqN{_4U^trw@`nBa&eo$LTXwBa3lb?djumMB{WQjMJeS<8-7dH;vN=%f@NNdi5Sr zS|j;&jmnm0(Sm**=meb`X~Thl(Le=8t3vndC(^^!h=rL)H8Lapmc=(d4K@|IX#S~H zuVmt(lo?jjF~mM2*OktBfFWnYE|I*v#OC2p&Gyq4i<(&bg&uQAO^FCWx8#Q;ym*~t zF%s8kxKp*}7bF!3b=R-gS@%=4>D$z%)=+5Xusqtl%t+paESwD#Lq!#5!V%WgpW!f7 zb2!wd+C?E%4XQPC?k%$6NoFLiyG$dr(6}a;wT+)|8vbQcn*=XRO*RW9F>KDVBaA89 zV=dZp!&>yfkqo%ztr=rCGJ;}fn34$41P}tIX5Xob-}Cb~xWtSl>%WRg7TAokeCDK@ z5~q>3XdTM3ceXxBzhIYDPlu53U{aNLRaBird{9@wx;xHoAN;E`+A+Hi|HTwL5_G=X z6HRYv*$!X+(+25T@SfSj%gD0gfzt<#+b~@}7@Cf`KX~ya>#=K%Sk7Rs0S5#k;DVfP z`0~H>41=0sYx#4-do07088Kaj+sQ2)!NM0H&5B2`$T!%+@IQM5%ORJJU?q>3RPA-Z zNu_t$`Rh%HvHa3Pdia_$3MJYNqKbB$?WOk*;b&o)c^B~?d%g!#`bN+9xS;_9Reipv zr7?G*BkBzh$R*6!{Hn4ePqnpGWp^K0ch0F(6n5(DCJA5p%HvGS}=DBG}ON zE?@T~PZ|!jayevqy2LXr>LK%fh$OM7)Hn%63Mrc<9+|y{wy@fS!#wV<7)yFRksPN` zmmzh_H&eh@)!3PlRwfgI|6bOY%jcF|)Q8!~Wj(%d_{i4 z=AK|MUzYCIT%2EZMIM{>m#$u%OOo7t`;JX!yuGVu%Z{zvw)9|le*5ir_b%&Re&?(p4NC;xTQ zo=|wb^txEY_ZI`4nl|CGS7@@T~zCK9Vb zpRE^!gm*%}1-S)P=}%}+!uMf9YK6Bf58=&q@LMM?g75S*3{>U6UP_*PvZujU6~1S+ z`R9JSPI&WWA>0m-Yos63`7_@S>!d&UFG6@dJO3xvgm4L)_^R+*Cy^cB^>y+em98z{ z=jw#7d~XQp`A=RK!gbfE7X9-2Q}k8NW7W<8sho9P&3{mD(BG<&*z7l z@s1O1&0`^+&l5G{ErCXpkB4|ZU(}3u8SzejJH+#Oqh>s5{hW~ADxc3EHRBBtul*^H z$8Y;;&~HCsqZ1*X&nGqW(>CtL{}JN(yizlsREsW$WQZ68C}{3ZAMoAlk)$*sSr=|P+7dTG@k6} zPx^ZgJ|ERAS9bId{YA*n=cStQWJf=~li>4H&3Lk-zt7)4@Oi3cJlWCD`FjOEU)78! zJNmo)9Ri=XYQ~ct{dF^;9zK87jMoTlZ<`JAd>*SA?_%POdOV-cYR0>Pc;i0~`T4w7 zGv23(w{kAT^ZBi2ygw%1K7S8D^ITc`X8OSd@h1OI$j|4ys{9tdKbK?4Dg38ad*i9_ z>aUr7%EIS?ELDXe;0oY2Al>DyHv!*`o#_@2ztqFe_waK({GsbYzWW^Rb-2gjEkKsC z!oijR|KOSc|N4CaKEph(@NYUCb$GwSUWW}1*RsV^{O_@T2<`!@UKaru19!9T$p6}~ z{w%!`cp7%BR{z?7m`Ur<#_vaSl= z0aUzq02Thir2&uKK;iKrV1wd?b$V&H!`}p6fdAz{m0NIl9`HT*UrDB_Pr>2&4&MTV zLRy1CUp`y+GxEuDcfk&&K@|B#&fK_%cx8KccXA0Y3wj z|3@9Z9SBQWc$LiL|F;gW0xJKX(^=&IB#^CO;Ub{?&j+3b{1%;NG4OLh`R@RVUe^Fc zkMBY(`5$ulM-G1ncn1D2LUe^c2bBA_fGY1oclTZx!awe?!{Hj>TZy;W-LFtN<@47- zrGMDnUv~GS5JC9g3{<>!pzwc(!>1uKRVo|-YFs=FJdGj_0IAB=_XCygZihEIJe0?i ze+RktEBF~8Bri0;uP&gye-2+OI0uB(g@*&5yYy=gM}eSFSO`?SSAIFbp8^$M?{n}i z9dmd9sQ8z>Bj9@hQ1~tZ3g6R!!uR*yZuU+KR{&M76+p#152$$O02OcZFX73*&%ob{ zZh0W#OV4$9CXg;z_?x%oa&*DMVIWD0d4`>GZCK+;^NE z!n+)P)ZzPppj2pfcca7E=8*m#8>*i2P*v!-2Hubf5Y8>>h5j8ONoCAkRet0g!@xn%Wn*P$$H_a z!vTlu9kw{kJIpzprf{Vjb2#d7z~Op_Ee`Vza}K9TZ0Q}2IvjAg-eHTwyu+NsY4GRg z!ZC-V4hI~rci7@E?+{?==1DBO{m~w>-hPyO=Q_op&%1lT-G3rCXc?usAx?`OF?$~*Lq zg~Fr!q|@<@@{F&$JIW^(MK|-7K9*xkANQ|6b$66k{JFbZC_~>ZUS4n^+KlyQu;^1X z;S=}!c`q;SZ?!KleBW!)bDzfTS(}I6G03_{D%_u}a1WAyHoo3*$hu|jIqUvOMfi`v zKO5d!5x%J+|F3u*1NOPxhbr8ktZ?67;hw3;@5PGpZmw|Wx)Ql$B310^l`ykwe0>D2u_|*NN^1Zg~H3Q$(C0YoGC1Zg$EgueD$sXZyW5`p2?IJ zi*4G|wPkDXHha=E8)L_o9UC@wZR*|Bz4flHoA27uTOG&~3Uxwmy(I?t(*SiIUD&>3 z+s59_wH{ySibeAtgTg^%JN0na_V5BjaLT`LYuDBt+qQS{CTpf*v6XUN9_RL+`caZs z9pdW_2?4in+Q93oNl)pbV?^R*N!qvR=2J$xwRf{WlAH zL^o{en|iO}*|RJ=%a5fXcP@OzT6s0*1_a{Q)z!0M$A;+2G5T7sz2Vx_J>~hZow08n zc(QcV{dix3aZbB>jIOR5+HSldjXv-Beb~J=dc9+e%DhkYUPb#`Jf$zLW8}wDn>)ci z1(si5S(c}IucDoOguWYNerzI?U;N;+Z;QVL#g4F#32S?;_bS*oDf}RtnMv{#pT3U& z`eNG(PZi5n+m%CQI%KkW>80; z;YheF!52x8Rix%Bp7zBYm(HL#a6B5ZOI}fU7NM61&U+vRQbba65kT+Qt~On+-~H3r zc*z=snwByH>N8xrYUn(UqWN{j#(Sfx@^QOuXSt_0E%T;w+M3#DNt7Hc4>Y9bOZ{`j z{v*X_xt}#dsX3I?CaLRWWnI5w=*e&Z8hMq}iMMIR)q+B*q8V@2_E>6ZB5?0CV-<$` zC{b&!5Ys}rpxq-Sl4)HD>O5{ri^r*dP(z=US-G-;?1VbGVOplko6(2%`^PAGK-#sa zh3+nE&kr*tge#t02a41{rLljyk{Zj1>AIpQkcOtiZVK;ke35a5?s>RT0Vx_}{eOZR z4``GG?RjkU&hL^zn_mnymWo+0F7e>wyE@CP*r=mgW)qf2ROr)T59)pDD!Z?+Oit%7 zlt+hT9+K)_4JsthEzafIR{dGqt|u$hM}iKq<$!toJFSpoToYSyxY#j~s$N-rG)X~e zu7pL)D$=>v&OzgAjz8?xxgIALTvnKZ;*33P|{{fr#znSN!%|k^W z;AGZ~H2}NOr^_s|?>B8_6j^h$?#S%=;Ekk{tF-9wU!gF}kLG)SsBBR?<92=1kl2Hu z!KQm(%oEZa;}<^|P?OxPSjDxc&Y9w3{cRt79OdQC8ME+CZB}M=>9YD?D=X{sDEU1$ zz@JbbE$N{?SZX|7=Be>u6k#uPNGm6O`xmUHp}sf|PcOo%DO6}z}de_dUC zW>#aon&Nf)kI_3f5a}&0@{Q8FIZJQa=k?M%n@$+K;dK9R>D}~CLGN|#(H>t`&9Rk< z(6swXSBgDrFRem9kh>niEFV*j_yY#2rZ#(GYNgzoAh0K?#zrNUo0{XntC?$!)@JEM zD8=054y}^yv`)g6hQS}htG+9^PR27Dd9_x_#LrJT+8i zU-pREmwlvZ=vRs7vjBd1bS0aHE|K3g3C=qgao^F3nhnfHz$o!NzgA2RA*6@N-`uPg zi%{24kMjDjTU15>dthaa4Sy*@PzhZ|)y1D$^J&804))KT*EF10f{r;!A$fGpR;n_F z?<)k)oE7IP;tgTV+WUCI8|WD)hgg=%@!HbLGCtOt;{COabmP0vmM7rDo^McV`n zuwwelQ1F#x_U|pyJXb)%GS0(WaT(VVIlDl2^e*s>!`)bzmgI?*n7cGiRCG3m=tGI8 zRg^f0cgo&!!`}<(Hh<}_5FBwZZc!bASzDtaJYaO9p%d2Wg<|8^O8w8!MIvL(B$HP7 zTcG5fF0P@w9ZPy}TtiuP?3xiFHNPl-Kmc`X*Kea~+PiJj7XI?gsP^7B_wBfS%Vrlj zqCt7F{ab;uX7`r9jXj&+y(7PA!yUc+A@WWwiJ5P)>RK+UHjn1upL@YvWuoj5&Tc@T ze5*FYeBB|WDtvm;YlpA%di-9U^e43O=KE2daKr`nEx7wP za|?DhFKAwJ#-csMy%1l?K1S?L^Q*5qeX8_06t9*41J8#hb2`z zzQoA>=bTgC%Dl)XuW*=lfYycGK>ii(w)QU`3 zTKYVZVW5_N1t|AkhxY|I{7Db*ao7>y@M@s)x!B=b0~}rg6rRu1&j{ai5J>fw?g1kI zS$Z#!F1~cNyDxV5)&PfpM*mg#yu%*^IQ%$J`8?wAs{s!01**JzgL~7j0Qr`FFN~9= z6F`N3Ex0#b=kBwCFj0m3sl38FfpR|t(sG~a@L78Ttl}dWS6z^9}))PWSM2 zmLBDc(p!}Km{tNlz1uDKw7b9T?&I!$x4UQDeZ<{!?%wC_6Yl;c^3-`?)U&(a-Em&H zgfNB2dFTdr$9d;2cgK0`4o?rT@9+4l^l={gb$7>ka}qbf$@kwY-1{rs|GUEdqYC%e zE8GjnJe$9MXWehB2(Nr8Iz763BdbfUD~X+5hd4IWaalZ~O5$w0qN`p|@6H{)EMYxm z>sid8?9uT$r%UB$NXhw5cq%)KK|HpoeFhO3> pjY#DWzSoIV{_=Z>v}wckJ9tT+JoSrg_$5>>bsxf3C1Fx2_`j_EPI>?U diff --git a/src/arc.c b/src/arc.c index 5be0a64..9e6d533 100644 --- a/src/arc.c +++ b/src/arc.c @@ -1,4 +1,6 @@ // Libraries +#include +#include #include "arc.h" #include "jerasure.h" #include "reed_sol.h" @@ -49,18 +51,57 @@ int RS_ID = 4; // Resource Variables Section // ########################### // Resource Folder Location -char *resource_location = "/home/dakotaf/ARC/src/res/"; -// Cache Resource Folder Location -char *cache_resource_location = "/home/dakotaf/ARC/src/res/cache/"; + +// path to cache directories +char *cache_resource_location = NULL; // Set configuration information cache string char *thread_resource_file = "_information_cache.csv"; // Hamming & SECDED Resource Variables -uint8_t H_S_1_Parity_Matrix[4]; -uint64_t H_S_8_Parity_Matrix[7]; -uint8_t H_1_Syndrome_Table[16]; -uint8_t H_8_Syndrome_Table[72]; -uint8_t S_1_Syndrome_Table[16]; -uint8_t S_8_Syndrome_Table[72]; +const uint8_t H_S_1_Parity_Matrix[4] = { + 0x5B, + 0x6D, + 0x8E, + 0xF0 +}; +const uint64_t H_S_8_Parity_Matrix[7] = { + 0xAB55555556AAAD5B, + 0xCD9999999B33366D, + 0xF1E1E1E1E3C3C78E, + 0x01FE01FE03FC07F0, + 0x01FFFE0003FFF800, + 0x01FFFFFFFC000000, + 0xFE00000000000000 +}; +const uint8_t H_1_Syndrome_Table[16] = { + 0x03, 0x05, 0x06, 0x07, 0x09, 0x0A, 0x0B, 0x0C, + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 +}; +const uint8_t H_8_Syndrome_Table[72] = { + 0x03, 0x05, 0x06, 0x07, 0x09, 0x0A, 0x0B, 0x0C, + 0x0D, 0x0E, 0x0F, 0x11, 0x12, 0x13, 0x14, 0x15, + 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, + 0x1E, 0x1F, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, + 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, + 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, + 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, + 0x3F, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 +}; +const uint8_t S_1_Syndrome_Table[16] = { + 0x13, 0x15, 0x16, 0x07, 0x19, 0x1A, 0x0B, 0x1C, + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 +}; +const uint8_t S_8_Syndrome_Table[72] = { + 0x83, 0x85, 0x86, 0x07, 0x89, 0x8A, 0x0B, 0x8C, + 0x0D, 0x0E, 0x8F, 0x91, 0x92, 0x13, 0x94, 0x15, + 0x16, 0x97, 0x98, 0x19, 0x1A, 0x9B, 0x1C, 0x9D, + 0x9E, 0x1F, 0xA1, 0xA2, 0x23, 0xA4, 0x25, 0x26, + 0xA7, 0xA8, 0x29, 0x2A, 0xAB, 0x2C, 0xAD, 0xAE, + 0x2F, 0xB0, 0x31, 0x32, 0xB3, 0x34, 0xB5, 0xB6, + 0x37, 0x38, 0xB9, 0xBA, 0x3B, 0xBC, 0x3D, 0x3E, + 0xBF, 0xC1, 0xC2, 0x43, 0xC4, 0x45, 0x46, 0xC7, + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80 +}; // ARC Decision Variables Section // ############################### @@ -138,103 +179,6 @@ void arc_help(){ // return: // x - Success=1, Failure=0 int arc_resource_init(){ - FILE *fp; - int i; - char * resource_file = "Hamming_SECDED_1_Parity_Matrix"; - // Setup H_S_1_Parity_Matrix with resources file - char * file_location = concat(resource_location, resource_file); - fp = fopen(file_location, "r"); - if(fp == NULL){ - printf("Error opening res/Hamming_SECDED_1_Parity_Matrix\n"); - return 0; - } - uint8_t Parity_1; - for (i = 0; i < 4; i++){ - fscanf(fp, "%hhX", &Parity_1); - H_S_1_Parity_Matrix[i] = Parity_1; - } - fclose(fp); - free(file_location); - - // Setup H_1_Syndrome_Table with resources file - resource_file = "Hamming_1_Syndrome_Table"; - file_location = concat(resource_location, resource_file); - fp = fopen(file_location, "r"); - if(fp == NULL){ - printf("Error opening res/Hamming_1_Syndrome_Table\n"); - return 0; - } - uint8_t Snydrome_1; - for (i = 0; i < 16; i++){ - fscanf(fp, "%hhX", &Snydrome_1); - H_1_Syndrome_Table[i] = Snydrome_1; - } - fclose(fp); - free(file_location); - - // Setup S_1_Syndrome_Table with resources file - resource_file = "SECDED_1_Syndrome_Table"; - file_location = concat(resource_location, resource_file); - fp = fopen(file_location, "r"); - if(fp == NULL){ - printf("Error opening res/SECDED_1_Syndrome_Table\n"); - return 0; - } - for (i = 0; i < 16; i++){ - fscanf(fp, "%hhX", &Snydrome_1); - S_1_Syndrome_Table[i] = Snydrome_1; - } - fclose(fp); - free(file_location); - - // Setup H_S_8_Parity_Matrix with resources file - resource_file = "Hamming_SECDED_8_Parity_Matrix"; - file_location = concat(resource_location, resource_file); - fp = fopen(file_location, "r"); - if(fp == NULL){ - printf("Error opening res/Hamming_SECDED_8_Parity_Matrix\n"); - return 0; - } - uint64_t Parity_8; - for (i = 0; i < 7; i++){ - fscanf(fp, "%"PRIx64, &Parity_8); - H_S_8_Parity_Matrix[i] = Parity_8; - } - fclose(fp); - free(file_location); - - // Setup H_8_Syndrome_Table with resources file - resource_file = "Hamming_8_Syndrome_Table"; - file_location = concat(resource_location, resource_file); - fp = fopen(file_location, "r"); - if(fp == NULL){ - printf("Error opening res/Hamming_8_Syndrome_Table\n"); - return 0; - } - uint8_t Snydrome_8; - for (i = 0; i < 72; i++){ - fscanf(fp, "%hhX", &Snydrome_8); - H_8_Syndrome_Table[i] = Snydrome_8; - } - fclose(fp); - free(file_location); - - // Setup S_8_Syndrome_Table with resources file - resource_file = "SECDED_8_Syndrome_Table"; - file_location = concat(resource_location, resource_file); - fp = fopen(file_location, "r"); - if(fp == NULL){ - printf("Error opening res/SECDED_8_Syndrome_Table\n"); - return 0; - } - for (i = 0; i < 72; i++){ - fscanf(fp, "%hhX", &Snydrome_8); - S_8_Syndrome_Table[i] = Snydrome_8; - } - fclose(fp); - free(file_location); - - // Set INIT to True printf("ARC Resource Files Initialized\n"); INIT = 1; return 1; @@ -253,6 +197,22 @@ int arc_init(uint32_t max_threads){ if(err == 0){ return 0; } + const char* base; + const char* xdg_runtime_dir = getenv("XDG_RUNTIME_DIR"); + if( xdg_runtime_dir == NULL) { + const char* tmp_dir = getenv("TMPDIR"); + if(tmp_dir == NULL) { + base = tmp_dir; + } else { + base = "/tmp"; + } + } else { + base = xdg_runtime_dir; + } + cache_resource_location = concat(base, "/arc"); + if(mkdir(cache_resource_location, S_IRWXU | S_IRWXG) && errno != EEXIST) { + return 0; + } // Turn off print outs PRINT = 0; @@ -2812,7 +2772,7 @@ int arc_reed_solomon_encode(uint8_t* data, uint32_t data_size, uint32_t data_dev int remainder_block; int longs_processed = 0; int current_blocks_longs; - long temp_l; + uint64_t temp_l; uint32_t current_block_data_devices; // Set current block size (data devices for this block) @@ -2842,14 +2802,14 @@ int arc_reed_solomon_encode(uint8_t* data, uint32_t data_size, uint32_t data_dev (longs_processed == current_blocks_longs-1 && blocks_processed == block_count-1 && remainder_long_in_data == 0)){ temp_l = 0; for (j = 0; j < 8; j++){ - temp_l = temp_l | ((long)data[current_block_data_index] << (64-(8*(j+1)))); + temp_l = temp_l | ((uint64_t)data[current_block_data_index] << (64-(8*(j+1)))); current_block_data_index++; } longs_processed++; } else { temp_l = 0; for (j = 0; j < remainder_long_in_data; j++){ - temp_l = temp_l | ((long)data[current_block_data_index] << (64-(8*(j+1)))); + temp_l = temp_l | ((uint64_t)data[current_block_data_index] << (64-(8*(j+1)))); current_block_data_index++; } longs_processed++; @@ -3275,5 +3235,3 @@ int arc_reed_solomon_decode(uint8_t* encoded_data, uint32_t encoded_data_size, u // Return resulting array and decode success value return decode_success; } - - diff --git a/src/res/.gitignore b/src/res/.gitignore new file mode 100644 index 0000000..afed073 --- /dev/null +++ b/src/res/.gitignore @@ -0,0 +1 @@ +*.csv diff --git a/src/res/Hamming_1_Syndrome_Table b/src/res/Hamming_1_Syndrome_Table deleted file mode 100644 index 460d509..0000000 --- a/src/res/Hamming_1_Syndrome_Table +++ /dev/null @@ -1,16 +0,0 @@ -03 -05 -06 -07 -09 -0A -0B -0C -01 -02 -04 -08 -10 -20 -40 -80 diff --git a/src/res/Hamming_8_Syndrome_Table b/src/res/Hamming_8_Syndrome_Table deleted file mode 100644 index 8079a2d..0000000 --- a/src/res/Hamming_8_Syndrome_Table +++ /dev/null @@ -1,72 +0,0 @@ -03 -05 -06 -07 -09 -0A -0B -0C -0D -0E -0F -11 -12 -13 -14 -15 -16 -17 -18 -19 -1A -1B -1C -1D -1E -1F -21 -22 -23 -24 -25 -26 -27 -28 -29 -2A -2B -2C -2D -2E -2F -30 -31 -32 -33 -34 -35 -36 -37 -38 -39 -3A -3B -3C -3D -3E -3F -41 -42 -43 -44 -45 -46 -47 -01 -02 -04 -08 -10 -20 -40 -80 diff --git a/src/res/Hamming_SECDED_1_Parity_Matrix b/src/res/Hamming_SECDED_1_Parity_Matrix deleted file mode 100644 index 53aac01..0000000 --- a/src/res/Hamming_SECDED_1_Parity_Matrix +++ /dev/null @@ -1,4 +0,0 @@ -5B -6D -8E -F0 \ No newline at end of file diff --git a/src/res/Hamming_SECDED_8_Parity_Matrix b/src/res/Hamming_SECDED_8_Parity_Matrix deleted file mode 100644 index 38b1359..0000000 --- a/src/res/Hamming_SECDED_8_Parity_Matrix +++ /dev/null @@ -1,7 +0,0 @@ -AB55555556AAAD5B -CD9999999B33366D -F1E1E1E1E3C3C78E -01FE01FE03FC07F0 -01FFFE0003FFF800 -01FFFFFFFC000000 -FE00000000000000 \ No newline at end of file diff --git a/src/res/README.md b/src/res/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/src/res/SECDED_1_Syndrome_Table b/src/res/SECDED_1_Syndrome_Table deleted file mode 100644 index 5f836ac..0000000 --- a/src/res/SECDED_1_Syndrome_Table +++ /dev/null @@ -1,16 +0,0 @@ -13 -15 -16 -07 -19 -1A -0B -1C -01 -02 -04 -08 -10 -20 -40 -80 diff --git a/src/res/SECDED_8_Syndrome_Table b/src/res/SECDED_8_Syndrome_Table deleted file mode 100644 index a3e0396..0000000 --- a/src/res/SECDED_8_Syndrome_Table +++ /dev/null @@ -1,72 +0,0 @@ -83 -85 -86 -07 -89 -8A -0B -8C -0D -0E -8F -91 -92 -13 -94 -15 -16 -97 -98 -19 -1A -9B -1C -9D -9E -1F -A1 -A2 -23 -A4 -25 -26 -A7 -A8 -29 -2A -AB -2C -AD -AE -2F -B0 -31 -32 -B3 -34 -B5 -B6 -37 -38 -B9 -BA -3B -BC -3D -3E -BF -C1 -C2 -43 -C4 -45 -46 -C7 -01 -02 -04 -08 -10 -20 -40 -80 diff --git a/src/res/cache/README.md b/src/res/cache/README.md deleted file mode 100644 index 02c4f43..0000000 --- a/src/res/cache/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# ARC Training Cache - -For ARC to be able to accurately determine which error-correcting code approach to use, ARC uses stored training data that is collected during the first run of the arc_init function. These results are stored within this folder using the following file format: -``` -{#_of_threads}_information_cache.csv -``` -The contents of these files are stuctured as follows: -``` -ecc_algorithm,ecc_parameter_a,ecc_parameter_b,num_threads,memory_overhead,throughput_overhead -``` -Such that, -``` -ecc_algorithm : 1 - 4 (1=Parity,2=Hamming,3=SECDED,4=RS) -ecc_parameter_a : The first parameter for the desired ecc algorithm. Ususally in the form of block size -ecc_parameter_b : The second parameter for the desired ecc algorithm. Used primarily with RS encoding and holds the number of code devices in this case. -num_threads : number of threads used, same as #_of_threads in file name -memory_overhead : The amount of memory overhead introduced from using this ECC configuration. -throughput_overhead : The average bandwidth achieved when using this ECC configuration. -``` -By seperating the training data based on the number of threads used, ARC is able to reuse the data obtained when training on a lower number of threads when using a higher number of threads at a later instance. For example, if training was done initially with a maximum of 4 threads but later was changed to a maximum of 8 threads, the training results from threads 1-4 would be reused and only training on threads 5-8 would be done, therefore saving training time. - -### Note: - -In order to fully retrain on a system, all results within this folder must be deleted. diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..c28aa2a --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,12 @@ + +add_executable(arc_test arc_test.c) +add_executable(interactive_arc_test interactive_arc_test.c) +add_executable(interactive_arc_front_test interactive_arc_front_test.c) +add_executable(openmp_test openmp_test.c) +target_link_libraries(arc_test PRIVATE arc) +target_link_libraries(interactive_arc_test PRIVATE arc) +target_link_libraries(interactive_arc_front_test PRIVATE arc) +target_link_libraries(openmp_test PRIVATE arc) + +add_test(arc_test arc_test) +add_test(openmp_test openmp_test) diff --git a/test/Makefile b/test/Makefile deleted file mode 100644 index 5bde9db..0000000 --- a/test/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -## ************************************************************************ -## Makefile for arc_sz_example that demonstrates the use case of arc in -## conjunction with libPressio. - -## PLEASE UPDATE THESE VARIABLES BEFORE COMPILING - -## COMPILER -CC = gcc - -## ARC flags -ARC_INCLUDE = ../include -ARC_LIB_PATH = ../lib64 - -## TARGETS -all: arc_test interactive_arc_test interactive_arc_front_test #openmp_test - -arc_test: arc_test.c - $(CC) -Wall -g -o arc_test arc_test.c -I $(ARC_INCLUDE) -L $(ARC_LIB_PATH) -larc -lm -fopenmp - -interactive_arc_test: interactive_arc_test.c - $(CC) -Wall -g -o interactive_arc_test interactive_arc_test.c -I $(ARC_INCLUDE) -L $(ARC_LIB_PATH) -larc -lm -fopenmp - -interactive_arc_front_test: interactive_arc_front_test.c - $(CC) -Wall -g -o interactive_arc_front_test interactive_arc_front_test.c -I $(ARC_INCLUDE) -L $(ARC_LIB_PATH) -larc -lm -fopenmp - -openmp_test: openmp_test.c - $(CC) -Wall -g -o openmp_test openmp_test.c -I $(ARC_INCLUDE) -L $(ARC_LIB_PATH) -larc -lm -fopenmp - -clean: - rm arc_test - rm interactive_arc_test - rm interactive_arc_front_test - #rm openmp_test - From 49d4a5df53a082f15a6959aef434224fd7b9beac Mon Sep 17 00:00:00 2001 From: Robert Underwood Date: Wed, 1 Dec 2021 14:20:01 -0500 Subject: [PATCH 2/2] make headers c++ compatible --- include/arc.h | 10 +++++++++- include/galois.h | 6 ++++++ include/jerasure.h | 6 ++++++ include/reed_sol.h | 6 ++++++ 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/include/arc.h b/include/arc.h index 33e4ca0..dbe54ff 100644 --- a/include/arc.h +++ b/include/arc.h @@ -1,3 +1,7 @@ +#ifdef __cplusplus +extern "C" { +#endif + #include #include #include @@ -53,4 +57,8 @@ uint8_t arc_calculate_secded_uint8 (uint8_t byte); int arc_secded_encode(uint8_t* data, uint32_t data_size, uint32_t block_size, uint32_t threads, uint8_t** encoded_data, uint32_t* encoded_data_size); int arc_secded_decode(uint8_t* encoded_data, uint32_t encoded_data_size, uint8_t** data, uint32_t* data_size); int arc_reed_solomon_encode(uint8_t* data, uint32_t data_size, uint32_t data_devices, uint32_t code_devices, uint32_t threads, uint8_t** encoded_data, uint32_t* encoded_data_size); -int arc_reed_solomon_decode(uint8_t* encoded_data, uint32_t encoded_data_size, uint8_t** data, uint32_t *data_size); \ No newline at end of file +int arc_reed_solomon_decode(uint8_t* encoded_data, uint32_t encoded_data_size, uint8_t** data, uint32_t *data_size); + +#ifdef __cplusplus +} +#endif diff --git a/include/galois.h b/include/galois.h index b08fd94..cf165a1 100755 --- a/include/galois.h +++ b/include/galois.h @@ -1,3 +1,6 @@ +#ifdef __cplusplus +extern "C" { +#endif /* Galois.h * James S. Plank @@ -109,3 +112,6 @@ void galois_w32_region_multiply(char *region, /* Region to multiply */ int add); /* If (r2 != NULL && add) the produce is XOR'd with r2 */ #endif +#ifdef __cplusplus +} +#endif diff --git a/include/jerasure.h b/include/jerasure.h index 8cc25ca..e3f5b8c 100755 --- a/include/jerasure.h +++ b/include/jerasure.h @@ -1,3 +1,6 @@ +#ifdef __cplusplus +extern "C" { +#endif /* jerasure.h - header of kernel procedures * James S. Plank @@ -298,3 +301,6 @@ int *jerasure_matrix_multiply(int *m1, int *m2, int r1, int c1, int r2, int c2, void jerasure_get_stats(double *fill_in); #endif +#ifdef __cplusplus +} +#endif diff --git a/include/reed_sol.h b/include/reed_sol.h index 741c317..f0371c4 100755 --- a/include/reed_sol.h +++ b/include/reed_sol.h @@ -1,3 +1,6 @@ +#ifdef __cplusplus +extern "C" { +#endif /* reed_sol.h * James S. Plank @@ -57,3 +60,6 @@ extern int *reed_sol_r6_coding_matrix(int k, int w); extern void reed_sol_galois_w08_region_multby_2(char *region, int nbytes); extern void reed_sol_galois_w16_region_multby_2(char *region, int nbytes); extern void reed_sol_galois_w32_region_multby_2(char *region, int nbytes); +#ifdef __cplusplus +} +#endif