From fa2d79a15b441230ae9998d0856754aa955fc2ec Mon Sep 17 00:00:00 2001 From: Elena Gvozdeva Date: Mon, 24 Feb 2014 10:44:54 +0400 Subject: [PATCH] Added IPP Async converters, doc and sample --- CMakeLists.txt | 6 +- cmake/OpenCVFindIPPAsync.cmake | 45 ++++++ cmake/OpenCVFindLibsPerf.cmake | 11 ++ cmake/templates/cvconfig.h.in | 3 + doc/conf.py | 4 +- .../how_to_use_ippa_conversion.rst | 156 +++++++++++++++++++++ .../images/How_To_Use_IPPA_Result.jpg | Bin 0 -> 62891 bytes .../images/How_To_Use_IPPA.jpg | Bin 0 -> 6991 bytes .../table_of_content_core.rst | 21 +++ modules/core/doc/core.rst | 1 + modules/core/doc/ipp_async_converters.rst | 62 ++++++++ modules/core/include/opencv2/core/ippasync.hpp | 92 ++++++++++++ modules/core/test/test_ippasync.cpp | 116 +++++++++++++++ .../core/ippasync/ippasync_sample.cpp | 149 ++++++++++++++++++++ 14 files changed, 664 insertions(+), 2 deletions(-) create mode 100644 cmake/OpenCVFindIPPAsync.cmake create mode 100644 doc/tutorials/core/how_to_use_ippa_conversion/how_to_use_ippa_conversion.rst create mode 100644 doc/tutorials/core/how_to_use_ippa_conversion/images/How_To_Use_IPPA_Result.jpg create mode 100644 doc/tutorials/core/table_of_content_core/images/How_To_Use_IPPA.jpg create mode 100644 modules/core/doc/ipp_async_converters.rst create mode 100644 modules/core/include/opencv2/core/ippasync.hpp create mode 100644 modules/core/test/test_ippasync.cpp create mode 100644 samples/cpp/tutorial_code/core/ippasync/ippasync_sample.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 442edf3..6c36347 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -157,7 +157,7 @@ OCV_OPTION(WITH_OPENCLAMDFFT "Include AMD OpenCL FFT library support" ON OCV_OPTION(WITH_OPENCLAMDBLAS "Include AMD OpenCL BLAS library support" ON IF (NOT ANDROID AND NOT IOS) ) OCV_OPTION(WITH_DIRECTX "Include DirectX support" ON IF WIN32 ) OCV_OPTION(WITH_INTELPERC "Include Intel Perceptual Computing support" OFF IF WIN32 ) - +OCV_OPTION(WITH_IPP_A "Include Intel IPP_A support" OFF IF (MSVC OR X86 OR X86_64) ) # OpenCV build components # =================================================== @@ -923,6 +923,10 @@ else() status(" Use IPP:" WITH_IPP AND NOT IPP_FOUND THEN "IPP not found" ELSE NO) endif() +if(DEFINED WITH_IPP_A) +status(" Use IPP Async:" HAVE_IPP_A THEN "YES" ELSE NO) +endif(DEFINED WITH_IPP_A) + status(" Use Eigen:" HAVE_EIGEN THEN "YES (ver ${EIGEN_WORLD_VERSION}.${EIGEN_MAJOR_VERSION}.${EIGEN_MINOR_VERSION})" ELSE NO) status(" Use TBB:" HAVE_TBB THEN "YES (ver ${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR} interface ${TBB_INTERFACE_VERSION})" ELSE NO) status(" Use OpenMP:" HAVE_OPENMP THEN YES ELSE NO) diff --git a/cmake/OpenCVFindIPPAsync.cmake b/cmake/OpenCVFindIPPAsync.cmake new file mode 100644 index 0000000..0b5751b --- /dev/null +++ b/cmake/OpenCVFindIPPAsync.cmake @@ -0,0 +1,45 @@ +# Main variables: +# IPP_A_LIBRARIES and IPP_A_INCLUDE to use IPP Async +# HAVE_IPP_A for conditional compilation OpenCV with/without IPP Async + +# IPPAROOT - root of IPP Async installation + +if(X86_64) + find_path( + IPP_A_INCLUDE_DIR + NAMES ipp_async_defs.h + PATHS $ENV{IPPAROOT} + PATH_SUFFIXES include + DOC "Path to Intel IPP Async interface headers") + + find_file( + IPP_A_LIBRARIES + NAMES ipp_async_preview.lib + PATHS $ENV{IPPAROOT} + PATH_SUFFIXES lib/intel64 + DOC "Path to Intel IPP Async interface libraries") + +else() + find_path( + IPP_A_INCLUDE_DIR + NAMES ipp_async_defs.h + PATHS $ENV{IPPAROOT} + PATH_SUFFIXES include + DOC "Path to Intel IPP Async interface headers") + + find_file( + IPP_A_LIBRARIES + NAMES ipp_async_preview.lib + PATHS $ENV{IPPAROOT} + PATH_SUFFIXES lib/ia32 + DOC "Path to Intel IPP Async interface libraries") +endif() + +if(IPP_A_INCLUDE_DIR AND IPP_A_LIBRARIES) + set(HAVE_IPP_A TRUE) +else() + set(HAVE_IPP_A FALSE) + message(WARNING "Intel IPP Async library directory (set by IPP_A_LIBRARIES_DIR variable) is not found or does not have Intel IPP Async libraries.") +endif() + +mark_as_advanced(FORCE IPP_A_LIBRARIES IPP_A_INCLUDE_DIR) \ No newline at end of file diff --git a/cmake/OpenCVFindLibsPerf.cmake b/cmake/OpenCVFindLibsPerf.cmake index 4b80b1f..7d7b239 100644 --- a/cmake/OpenCVFindLibsPerf.cmake +++ b/cmake/OpenCVFindLibsPerf.cmake @@ -19,6 +19,17 @@ if(WITH_IPP) endif() endif(WITH_IPP) +# --- IPP Async --- + +if(WITH_IPP_A) + include("${OpenCV_SOURCE_DIR}/cmake/OpenCVFindIPPAsync.cmake") + if(IPP_A_INCLUDE_DIR AND IPP_A_LIBRARIES) + ocv_include_directories(${IPP_A_INCLUDE_DIR}) + link_directories(${IPP_A_LIBRARIES}) + set(OPENCV_LINKER_LIBS ${OPENCV_LINKER_LIBS} ${IPP_A_LIBRARIES}) + endif() +endif(WITH_IPP_A) + # --- CUDA --- if(WITH_CUDA) include("${OpenCV_SOURCE_DIR}/cmake/OpenCVDetectCUDA.cmake") diff --git a/cmake/templates/cvconfig.h.in b/cmake/templates/cvconfig.h.in index 3f316da..c02d9f6 100644 --- a/cmake/templates/cvconfig.h.in +++ b/cmake/templates/cvconfig.h.in @@ -94,6 +94,9 @@ /* Intel Integrated Performance Primitives */ #cmakedefine HAVE_IPP +/* Intel IPP Async */ +#cmakedefine HAVE_IPP_A + /* JPEG-2000 codec */ #cmakedefine HAVE_JASPER diff --git a/doc/conf.py b/doc/conf.py index 0112725..08c6d12 100755 --- a/doc/conf.py +++ b/doc/conf.py @@ -416,5 +416,7 @@ extlinks = { 'background_subtractor' : ('http://docs.opencv.org/modules/video/doc/motion_analysis_and_object_tracking.html?highlight=backgroundsubtractor#backgroundsubtractor%s', None), 'background_subtractor_mog' : ('http://docs.opencv.org/modules/video/doc/motion_analysis_and_object_tracking.html?highlight=backgroundsubtractorMOG#backgroundsubtractormog%s', None), 'background_subtractor_mog_two' : ('http://docs.opencv.org/modules/video/doc/motion_analysis_and_object_tracking.html?highlight=backgroundsubtractorMOG2#backgroundsubtractormog2%s', None), - 'video_capture' : ('http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html?highlight=videocapture#videocapture%s', None) + 'video_capture' : ('http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html?highlight=videocapture#videocapture%s', None), + 'ippa_convert': ('http://docs.opencv.org/modules/core/doc/ipp_async_converters.html#%s', None), + 'ptr':('http://docs.opencv.org/modules/core/doc/basic_structures.html?highlight=Ptr#Ptr%s', None) } diff --git a/doc/tutorials/core/how_to_use_ippa_conversion/how_to_use_ippa_conversion.rst b/doc/tutorials/core/how_to_use_ippa_conversion/how_to_use_ippa_conversion.rst new file mode 100644 index 0000000..bbdaa24 --- /dev/null +++ b/doc/tutorials/core/how_to_use_ippa_conversion/how_to_use_ippa_conversion.rst @@ -0,0 +1,156 @@ +.. _howToUseIPPAconversion: + +Intel® IPP Asynchronous C/C++ library in OpenCV +*********************************************** + +Goal +==== + +.. _hppiSobel: http://software.intel.com/en-us/node/474701 +.. _hppiMatrix: http://software.intel.com/en-us/node/501660 + +The tutorial demonstrates the `Intel® IPP Asynchronous C/C++ `_ library usage with OpenCV. +The code example below illustrates implementation of the Sobel operation, accelerated with Intel® IPP Asynchronous C/C++ functions. +In this code example, :ippa_convert:`hpp::getMat <>` and :ippa_convert:`hpp::getHpp <>` functions are used for data conversion between hppiMatrix_ and ``Mat`` matrices. + +Code +==== + +You may also find the source code in the :file:`samples/cpp/tutorial_code/core/ippasync/ippasync_sample.cpp` +file of the OpenCV source library or :download:`download it from here +<../../../../samples/cpp/tutorial_code/core/ippasync/ippasync_sample.cpp>`. + +.. literalinclude:: ../../../../samples/cpp/tutorial_code/core/ippasync/ippasync_sample.cpp + :language: cpp + :linenos: + :tab-width: 4 + +Explanation +=========== + +#. Create parameters for OpenCV: + + .. code-block:: cpp + + VideoCapture cap; + Mat image, gray, result; + + and IPP Async: + + .. code-block:: cpp + + Ptr src, dst; + hppAccel accel = 0; + hppAccelType accelType; + hppStatus sts; + hppiVirtualMatrix * virtMatrix; + +#. Load input image or video. How to open and read video stream you can see in the :ref:`videoInputPSNRMSSIM` tutorial. + + .. code-block:: cpp + + if( useCamera ) + { + printf("used camera\n"); + cap.open(0); + } + else + { + printf("used image %s\n", file.c_str()); + cap.open(file.c_str()); + } + + if( !cap.isOpened() ) + { + printf("can not open camera or video file\n"); + return -1; + } + +#. Create accelerator instance using `hppCreateInstance `_: + + .. code-block:: cpp + + accelType = sAccel == "cpu" ? HPP_ACCEL_TYPE_CPU: + sAccel == "gpu" ? HPP_ACCEL_TYPE_GPU: + HPP_ACCEL_TYPE_ANY; + + //Create accelerator instance + sts = hppCreateInstance(accelType, 0, &accel); + CHECK_STATUS(sts, "hppCreateInstance"); + +#. Create an array of virtual matrices using `hppiCreateVirtualMatrices `_ function. + + .. code-block:: cpp + + virtMatrix = hppiCreateVirtualMatrices(accel, 1); + +#. Prepare a matrix for input and output data: + + .. code-block:: cpp + + cap >> image; + if(image.empty()) + break; + + cvtColor( image, gray, COLOR_BGR2GRAY ); + + result.create( image.rows, image.cols, CV_8U); + +#. Convert ``Mat`` to hppiMatrix_ using :ippa_convert:`getHpp <>` and call hppiSobel_ function. + + .. code-block:: cpp + + //convert Mat to hppiMatrix + src = getHpp(gray); + dst = getHpp(result); + + sts = hppiSobel(accel,src, HPP_MASK_SIZE_3X3,HPP_NORM_L1,virtMatrix[0]); + CHECK_STATUS(sts,"hppiSobel"); + + sts = hppiConvert(accel, virtMatrix[0], 0, HPP_RND_MODE_NEAR, dst, HPP_DATA_TYPE_8U); + CHECK_STATUS(sts,"hppiConvert"); + + // Wait for tasks to complete + sts = hppWait(accel, HPP_TIME_OUT_INFINITE); + CHECK_STATUS(sts, "hppWait"); + + We use `hppiConvert `_ because hppiSobel_ returns destination + matrix with ``HPP_DATA_TYPE_16S`` data type for source matrix with ``HPP_DATA_TYPE_8U`` type. + You should check ``hppStatus`` after each call IPP Async function. + +#. Create windows and show the images, the usual way. + + .. code-block:: cpp + + imshow("image", image); + imshow("rez", result); + + waitKey(15); + +#. Delete virtual matrices and accelerator instance. + + .. code-block:: cpp + + if (virtMatrix) + { + sts = hppiDeleteVirtualMatrices(accel, virtMatrix); + CHECK_DEL_STATUS(sts,"hppiDeleteVirtualMatrices"); + } + + if (accel) + { + sts = hppDeleteInstance(accel); + CHECK_DEL_STATUS(sts, "hppDeleteInstance"); + } + + We shouldn't delete hppiMatrix_ because we use :ptr:`Ptr <>` and so `hppiFreeMatrix `_ will be called implicitly. + +Result +======= + +After compiling the code above we can execute it giving an image or video path and accelerator type as an argument. +For this tutorial we use baboon.png image as input. The result is below. + + .. image:: images/How_To_Use_IPPA_Result.jpg + :alt: Final Result + :align: center \ No newline at end of file diff --git a/doc/tutorials/core/how_to_use_ippa_conversion/images/How_To_Use_IPPA_Result.jpg b/doc/tutorials/core/how_to_use_ippa_conversion/images/How_To_Use_IPPA_Result.jpg new file mode 100644 index 0000000000000000000000000000000000000000..2bdeb830cb4309307fb476b860d44c6db1793b00 GIT binary patch literal 62891 zcmbUIbyOQ)7%mEj;!ujaq{WLCr&wAjUZhamS{y=f*A|Mq7bwu8#Vx^Iix-FB!HOr) zLhvuYbI*6~UFV-GJ2Pu#B{Q?%XUnV4JS;wJ0LavoRFwc&SXcmG%m?tW3{U_(!ovFR z_@50M7yG}4hl7KSi;sto|KCYKNK8mTKtzC#PeejQMEn@j@Cl!gl01I$-}nC>^55=(X+|PML#l$5fUrH&wQB+b^QB~7@ucvQdXk=_E-R?>*pW#B|IWBDmo@5H7z|O^J`XiL19sGN$JnB^7;lStg)%NrM0)Oe_(Lv*YL>n z%?zxX*;}C|TVm9Pk($3p07x|Mbvk_-`_mns`xAP zE9dI7XzFaAkeo6dYs-OHA*(#C?}z&4SV)qt9E1h&pLv4o?x~Mwo>)HY=Zw0pIhLW% znVZ5PKNOrttTG0CPX{KK*EjQzf{J`eHBnG=_WPOHcCnImW37^Xuy;&+E8T3@*JA5) zwp47Ugx8Xidw)S_zS3?|=5;8!3Hbh5c8`I@*tKF+H|(sKhV_qVCJvj@dK=#hE3^J% z?ZJ+>N>=iej>Y!8&N&91*SCob84S>lJ(`&y%H`^&g_GMvhX!;D68l}GntiJ3x!7Q| z=#3B3X4}Ugp3ilZqKL$vgP+}ptX^QHISGP~&j5M?v-kpHJa(}~LTVzt(q;;qGBzV4 z8--e+bDL!-6_jYtr-L_%v}jrehGaGWA_6} z?JX7O?4-bbA|?T96bEVFdraEZXo=lDr7)ZdN2)i0uaW_QAI9Um}`&udj;Xxw%OQ z?Fc_YWP%_1M?eGmFcPKgpoo#_OSOa3P{;wZUzq&SxK#zAi zW;Rw4qEg=miG4Dr;gYeRGggk>sUIQf>abL`Rrs*N)G$xT4iuQ*Xi1s7)ev5On{h=W zzI7}tw^4!T3<}m92F*D?0Q9&n85#Jz36I&;i_Lhu#x839_YOgu7QP|(pQM2I#G4NQ z1bFZ?WTOc9#B=}eY{v2+2qh8E0kxUl^!zHP7LVBY=@d9X_?Rl<=_3-z(*3eIRcErE zs0(BDO%^}4ddX{w)3|3=na$WAD7YpsHGR48swFG=V6kt+-eF{+zPBp^My_FI0qgez z0BJ$`0H9R9eaZkh{B9qD7W^Me;aNKdR?O|o+D4-t3rdc%4141ZxN#J#uZ>EvLF%!z zc17!+8;3}+?LF}i3`)l_IRfOL2S8THYs_PuK$xwNJ9$f`wK-S5)X<0co{20T zXse2EAS?{T{TQ3t^Q#CsbW7#3IT`2dgqTL4%nlE@>y59xY0%BemOl@Ir{XI|4oV9} zoeMeW2z|#`R(=&T%y%95pL9I{2!=k1Ei+!pJpaC0VYJdy>!5!eIt~A}Q7ui5o>LD= zSbLH^LY$9&9SGcBp}WA%={qwG+{PWRH-&YBUW|Ngs*z6nur(=0y6n;28j-rC zF7m^Ov&KtyHH}7S$)s(RV*DbaRnhcH*N!yzb2Q@iKjPGqh$Dhtgb9A^>~xO zN>aTqrmLpz;N$kO7oh`zD>Sl&zeAi{u-4bUK1J>gq)Vo#+SnII5H!Y*@2V{n zh|1!Rb!XUMbIdZfmL9)DZkZsf#x6U7=R6|%wTq;;Y#{qV33>1OiFU>E&t**?;?Mu9P60Y^i z2v(zqvFZP@9Y`&FMCo3{{O$xEW{^8%+vIhCy=-uCd}X z%oA^?8V3pTT4;v^!%$TteH)XE8W%4g3loE*b~$?6AlliV`4*KV0V#OOIFwlx!E+A) zY*90*v)vG4^iIZzhZC|pc*>10%vqn?s>^|MaEH%IpXiAywipsQ`T+2f4D!!$p7&X{ z&}?&g0O0>C{}z-sN~r{OrFa+W6Ti^mwV>B{sJu75W`p!VR43jj>|0PnoZIPH zzlsBWkMWO>bxcg3e;N=K=n{1w9I31>5>6s`8gpIr0O)N)n;=54(VSX@4y=d$7yC5X z!^N+~uc%0cN|TXG9BEl}b++Do9>Zt(c)`=_1oskwTeu!iwWcWd#^Z1xxR z_GLQy7nOGDpM-_CNy-Q1+;tNnsK`NPu5O0#N*^hfwlXvL;!>OGVpZNE&Y)tvhgiQ> zb6V1EMV!yZ=Zh>wS_Qm-@_fhM=oHPb&5IY%craM%33S$ZR8tL7$2kgH03I6G4ZG2$}vR-%O2;uYGg^3QxQ zTU!Ji@c_`8fr3@4ZA|Rr@OBm3wF{4q9}S)*5s5l}2`%U#&{H%2JD14t-Uh-tQLhzY zPN(nyV4aKvAr0^!0OWeRXt&L9;2FQK2gcaAhM=gE!Kj!+gRWT!M22K5ry^IJuG%(6 z@Tk#&HLY{l66k#Q*P2ZneJQXa?)tjC4^jN@zL@%(9097OEql%62aV`h!4Ox5-sG}( zQTQd#V82LO(Dx%JpCR&yLw2`Ty2KsZ;>l0#FT(B>yPiA%HtI?5IUA}2$u}!eo0Fu8 zHxd8&1#`?MLsApDxP_~?a_G;tUi8IH4KcgxS`pwSE2ZRu2=R7Owkrp|&)O6^^oAsL zE!4-iCO;4SzcnVIwfd)5x*JbW{DUcmd`J3}iA~m#Y;3?@g}c7i){oIW|59FL0I8?> z>Oi3~GFrE>*ZB9uAOF+8?RmJ!D~ut;3&-gDKNv$eiM;G5egM$=ItE#2AqD#h|K8aD z$WtXr&z4()%CLL0((rNM{fIP{~nf)P`5I#g*j$yg}DxVyLN2W|8I=0&%_lq#hx?22iU7@hi zR+}ds9|O3){AGk?Oz)C}sWk;+Tnl_$w_C3B^6dQn(rHm*5^XfN-v<*G5PFr4mzwTN zMP7W-VQY6GT3(k{ss1^j7E1TH+MUO1VH+ivUb=!&tj`hj1?}wt(l6O&J-_Za{0{y{ zo~iw~SoqDi_%FhpL`g5IqIxF8b(Ci9Eq`!u(Y$s+E%Zzqy><^I-ZG?DpQ^m!|0T)UK;TT!{OD;!G?vU&g3-hS!D5qh5hc{;-as#yUmzsgYAEc#0&!{5kg70|3vT zJZBs+8btZ}#se*gc&neIuY*_WoPbkJ)Rf`%bX_RuYEV4G-8*z{XGGGwQ!=l}#`tnO zbHf$LBq#10pm3KTXcU-7GbM0x#4>HaHb1G>>PyfJ9RrJ%^S$|&(amQt#$$-+^r^e6 zuYxg>)uLsJ&dBPZ*^uwr{c+BGudwUbKVfmPRRtycAxMA&28ln+(3A-N8+HGA3X|b7 zlUBbvaiob<#v^BW9fh;H_{P+_I=sy9XycnVH$kPn4*-^N%i0yqbHyO1sW*)`x5y(g z*`L4Oeb#?gZ;+VfadtKT&VX89q;DaPLhataOYj^dzbU}mz4O&E&8=BNUk6fJQ12kH ze^OIu{`0C35qZp55C}ps(Of&{*dci7k8vUb6lYEYTY6aSzYZ~3`@NNcxwi=bL70lZn@{l+ovg%H|i;O98LraRl`M!@T`U8`=j%yU{$kJHa zzBPtfs8O{jV@u~;fEa9O`I(`L-23EJ%|V$~cOgcW@GKSjjWb8&P>`iw!Pag0UDc9* zj_o(IPc^9_b~C-hdJlm7_=;PD1%nw4<~DXHNEWnFEA{P~Yv=*+SwbH<>GKR7Qe?Z* zeFj%!oh|+v-B{s~#`q$^C%%nN$Y4|M(SDp%7q)$+Mr!Kkr21yK>oTm7(~;J%pv38Pk2-# z+uAGkQ=5@8thC(yT?8h6y zv+WscVTF486t3a$65mD6XYd2HJ-U>IFsO;49990YEKq*-_Ibfth~xgHx3Kh;_C!LD z9+ifaDrJ^h0d|`!d+qt_ky%k+8Lo|`f4pXo1St;WII%-D^bRP$mBvIB?jj_|}-ul;{rPfY$8J7pTs zT-(eF$7`OCy-{yg6Lo))I~RNPvvBl{WvPe($@g20=|I9Q6V=j%%-7+Nb=^GXcM?YH zSICtS0S2YM)8sjVm14@H`AGL(OR+s5{jG+B7fb#RL*FLF6imS6sPbXj$~U~PP*GGw zZHm?b@GLgyTlKrgR-;6}QF$Wh3J>xbt{cJVs#AfI>_2y~-ImaaV-7!Cm4RM-@}m4h zYN|$TTlsaFe4kG}v!*bHDTk(iONTXUZtc=3O$KLj=@t|{LC6c7_DthY63EdugNPw} z#(aI&`)=0Oc=?(v5?H795qWvz1E;7 za`dA|3Ut^fjO$+rzTCEy1)9y?D*Jf+oD_{X*yVoK+WsnVUpWKEX(wmL25v;@&-zLH z(+?`;n)@m9r}Lhp6@`BM-!9#`ve>}8QeR~N3ZW2rL8jN98VVZ7m#uhIh!R_-_0Cq_Zxw5?}d9C=yk^m z5Ii&10I`!ZA$!BmfKM*f`l*h}vbv9sKI154f>#Y*6hhZP?U0?*da#WL@Qeep?x8UDAQ{_cIc6O)!UkOS!kA zAvq^~gAzRM;EMYj5G0~K0}Y|FXNb&#+$H+GywQt`QFwD?rEzbx&NY1g$j*y2x3V)H z=^o~+;Rm@3mYpD)71B;G#JRn^?;=eeweP|SW(PA+ftIp0GXpjcQ@y;v5C`rgj$e=& zLJokW=NaOqj;p1@4E5f(ly2zNaaWFEp$^v z)r}jmo-f0EV?4PzE*~UyX(nYGUea$fb!?zvWanTQ$6Lp?tw}KItQYV401$xZBP?#I zu=}$!;sf~HJ4$lPGCn)sPx0vry*Dg$57j67Zntfby64*S*?lg2zy|tpT<6TCW=$6>C5sGQt zPI+umE=ix-b?dY}@v)KGM7Imhs^~+7ZPr{ZGIeVs)^j~$FDT8wwNVr&}sq&bH&r!p$8-Pk`}+}+Z)|D%4KY|Uw>r| z1H0hhvJ0p4d1nt1_M_F1UN0!(6&EtpOs+v(ccy=8`WCZQgKSYQSLsl*8qD~2%N?N> z1_d8ua&0Op@Gb4ySpkNC;jMtEDj2bfUIE2b1u%OGH#{xfp?->Rk0UMt(Ht+0$beqV z|8@{8TGT3W>{D;(>S{N#R?qpw;uB#-LA&HMb3)iFG8Aph5mh>zolta1YDbkFKkIs8 z#oYULfM1t9Bb`pkFqAopn1$>YoyR!1Cay+|-|E;M?f=|G$A~@UXmy1+`*oq4`{ z=95au?xs?}9K)>lo|C{q&LSuH37XV!;oqoq)mylU76b_22|b&9pQCOi=_EeXI0hL# zIa_7L`l!t)c9Kun4k`k#lY5aUsKYXi=Ko&VEc-}1nyA`&doXwF^TZu}iuNXJ`9LSR zde%~&;*&CU+z?=*xjG|N2A6t$&{cCVq*bs8KTOubeE@{%s?99~ou`U`eZ=^?WrWmmWwA50i zkT*3@RBcz!u~Zdz4E1XrgCTbMzyE60R#xy=RlXaN$^bqyNN~3lgk3V{ITONP1cz|v zm|%j%C$Qo{xN2-~p3Cu-t9f<*`HyF! zaYrZc<={x&8?qid%XOA5*sK_y!#(4{0;Q3uX&<+NO?=>4T_gDl_sEfU#ZGy{PsZSB zsmu$~sr6gg4m-6^aQWs&R=-4gqR!l5#$uL(F z_V-6A?2mA}dEi2AeWlaeVAJWlJi@5B;d+boh~LB4C9kSd`{wSQb~M^G7F#XCq0dE+ z)y*;ovSHzgQO;{{4cU+OM*1jzDAp0_*`6s)w+q@tyiWAV*dE1>Y^+Lm|b*c zN-5AfNHh3>_wQkX&d9#`qy5I+X*yzlq6iIm<15thUwM){@t@@Qr`X4x9< z2kNGSS0IJcM@Qy^Du3@t z&fJT?U`zd}u^q*FWl@922;qib48Hm+_3V2!=;)etFkdV2Yp>0#y_RS}&h~_fWsSC$ zh?8w%&ztuG)zzo2o0jLrgiP|PAAKA?0+vwKC6^BXOfIHQAW*T;SI{J1EtEj8U~Xr6 zBENRIpCi&wmF*daN7`8rr6`16xKM&0C9UsQxn{E? zRyU!iz0EUxOM<3~+@vL%K;TIqrAD6m*216$sdDSozj)oAV3FAV>%W=%MPT9m^GTbM zNjoc#0MTfnWuGvPoHl~l!Kv=}CjV~mmKIs?(mEvqg;#;7(v&_8cU>H|3Gkvodxta} zcuy@Y_B|;Ubzix%a5OAv_pRJ(c|%B)DcDKRjs2J9v^N_c;|#2mn5cU4!_+rR)aCN+ zlPv7TU3nDQ!7qp|WM1NMph^v(5 z7wOV1$&H~Y!=JXxKaHHp$D!WV6$I;`6Yq&-w|3Ba9+m8!KqxF`>n(1z=pR9@Y3GxY zjK@xrQRWkl_^K~J` z2Mir&k_N2-(#!U810>K)5-ikkjhs<2=iI`C!gGiMdvChID;66J|lZ1%3em+fTCL?eUL|zX%taDR5HlBNpX=R#cO-U+6f9da%0@LzJ6jf7-Iem)sOD>;nO}w21z7E}cA~)oTOK31l z=d@OY*D3L>Uc)RO3`k<8O41eODXZ}z*E$*MSKJo z({8olLS?_k-GWjI-vE-T+j^3c9$q`mUuM#v>Qwliy>Hed0f2E5xPls9joA3nFLLA8 z1-?hOIoNGt2aur>N(&abC6Y&)vfI7?e74QkZg0(^TIqS5Np?cgsYog^z~tbZbB8M$ zq-=zGlJ)cO618;bF9xd`SWEJ1^qKw;#VZ)BwN zHFSoKEYD}FZWXO6R4(BuJ#JvH@B0F+bKl+-f6E}Nic_-z%cjzsX&T{-87lV;ji5_a z<^n~Kw(9nQFKObLnrJCkYhs+2HN!Nz-WE+wMgsPyUbE}}*>~0Z0v4ECoYuJ7tGHD~ zqfl1;Tt>*QFZZ+u44`;^lCj2M_O@DB!-DA2s*Tp_L8g#Qtn1@M?=3>%c6aV1r;nPX zU%KdS6%n$WX21a!3S?+U&exnDq%}gq68~A{-@;ogr&Ua1Ho2- za;U`q=I^tePkbx-Xh*juiHyxm_w&dy1_z~okqoqFMyN|yz}`3B$Oy8J?wInnr$#i% zSr!WZ{Im8HhW6QVAuwD=kC14ow&(4diI}%%!(E8b`Uv>e+qV20kZxmbK$ZMUOWPNT z{5X6U%Qi_3qpyHP_e|(`TPHzJ#(4Z!&C}4;4}Zij>fq`&RzeYLjLQ|iGziJ5?K8WU zr0Tbt{P`X@F`I`L-Ad$w6WYvZ=rG}HW5_XAFYJ34U1WIfh~1iG}ao$lph zb_ZrF1L~`fn5uP;yo=oPK%JZc7Ebi@0)-l1@Idw#ckuJ_wiOcj{vqcxGY$5~oC!~n zg*{Y+J=TV|sTc3A)i)C9EF3{kjsvyI_u;$62;wLK7>SG4G_7uvhOjj;-$LX1GVzCt zy8hE9OJN%Qv)jh8IKYeGr-8a{Xny3`$nIaXouUyxqz@FuzZnqH@b-;`Wz&lF#BIsXQ3bdQuvHOR~-E??{$!%(CbW%bgV?1cR41KWQ~*0#wRo_V{w+#9XWpOf>v zO@S3~0}pQ7$1Q=x!DGUQ4gB|2K98l|OEs2-h!;bAShA>IQK(6a4W}^rrRegGgt)QV z`Q)~0lfHr&^e)H|B=|xEoe^~_&Yh#B18!8_uF9;d!{ly{OkR+(u1E{!-V7>Eran$O z_t%Poh^>MT0C`_TpiT01foIX!5SrIV@RA5D}ad)T@+S@p;vUsPH2O=s5`W!|jg zdWpEXY|dxHFXlX{G`7UODP|v|9*5gJ^Io+&{i5m8|ML79Omiip1l395XVuN@szn$=|k$x@Wu!wj1t3 zzRM+6nv)X`WWAq*o`Yt2-gBac{jG!uCcUp|PB|E?~L5LF0Mk;0ShPzj?=tiRx3;v$4=uhS>wJ)ubyhJX(WXtRmn(6PyYX<8q1Bq3^n5*?Y)wJf15)Ajli%0*|3M3Y z(r-K3@;(wLrDmzoeACFQ3KK5~%3YRS6tzxU5)r`*tFI~am!T}C^-l9Q=HWZU@tkCpJwyn zSykVzh=4U+mS@wJE>vAk7M-z7a%+4RkWrw?S)#RT;w9nkbBG zvh~H_^(wyxE-IQ|`CkrmOJZ`#AmUqTmm9@HDNyvO<=iwg+1tiUd(f1N&cTwiWnfAo zpZDNSwzfyO_lJbaihp~b0|Q(({%90sTAVSNP{y4qmyzywG=wnb?9ZU3-+zx-M2ukYO_D)z-h5j+Xf9; z=ncVU0%Cim=j#k_B(Bb%bjW&Je23(Y3=oxn5suWW%wRC_)zw$WC3-e%3k@{{xPB*a zwvIpuRP-oze~|wqx%*EmG=u>YxR6F0`=cj~epmCojoz(fI8G~bt|YXKru374`>4eo za1|gNal0Na35s&{h3gLKMb;OO+u;lNXSSJMPThO!FL?Z0Igb}+vLz~(;WIAN%~W-V zGc|Vc!%PC#s^{x|l-vqpDr@#GVNG(p$kiOQa(}jtqU=cyb!F(Z*})Nq%3^xL%g{^< z!KYEnQBywoe8}K-Pl&lqyP;*9E&Y4mMn&GUbk|^ctze>Wp;-7pWAFo@p)>48(ARt9 zTnA;)cf~sW_pnUBGwQUorE8A3_pFk|2pS>Txl@AuOP|9w@Fkni1jA&wj07Tt1-)#J z+!>gQ2rK%MH&^3hvP#IrmOgk?Q9)1Xy`b}pfVjm-4pZ*cul%qlD;Odjv_1`z8{J}< z_IxhqTyfi}Z2thDYFEcCFWxP)cCrhlvJb1tV(ovWX{G%r4l8u%nZYOgQM(O`NGG|c zz8zxBIs;x+O5jFyY5FOdA^^=fQC`fG)$#l^%CkC~Dt_!u+XnVLmPEsdDd^vr)qZbR zsEC;=Q)NtjBP%TqEuD~BA9iFc-{_hQ{Io}lOz*W@wrR6CcL<2wCsYmZ!)4avRj(jOzz;+6h%T)=))$IT{P_3lS@z&~p!R;I0kXOtux`TP z1qtX7H3c1u+=%{JBS%S3mi7?^L7%qRx)E^HW%5X_<{p2Oluo3lPDk!-?B;O%gBlo{ z#px69bLp1zh_aZr;1pJGa_Xfguvr2>SkchMFkJjVfMV9Tv+u3Etn=+f#s!MPZEMMg zX(w4L-?B*xCG|rctyhlTLzqi4Yl_o^8436*opxJ|oQ{uDa1$<0g-VwAf2`>#E&340 zrUB{{b{qGB$AtunMpar#n&7hKTZ+Vc^2x}|;HM33nTiqfRt@@1jgi_ZzMr*NrNQ*h z7fLTYoe+jluOU`sJ{sq*on@UOH_~RbcCbu5Nw@JEpP>3Cv|M|=5a$IH>@!f}{^vsM zSi>DP9zf`D@^w!nDBDySy2W$+)cQ5uYSU9crJBvdAhvgAw!MT-tTFb#@>OAxI(bHV z(KmX>(|vqrN;gW21=0q56;SDHBpWq-nFqMM)j(^<`d`}|GgXDe;hq28kire2V_A(d z>fl5JqzId}MSE1ib_P1O<9+pMEHOYQJ(c7}eXW3Q9-Z9P_?K?f%T38@e#h@hZ00UU76?C8%~{bvY)R zF3%IMpqp*r#XUd!-b~1~-!993<}0@P0N8&!436shS1(qA#x6F@0wL^z8s2>TdDmq0 z=|HOZSDu8bzWvGNB~t#A>6)EeX`G8qRB8biLoz0#{1!|w6} zWF#(s2bC411cawCe*KVgOd%e-t4wrqrm-8m8yWXEb`tNFF|!~307#S(6TC26UOV*H z%qrqfuYwnz*yakKQZ+zb+0=!{$yGikW78}PeuOIrpjvrBG{0Q_jlcW{vD5l2`tt}D zQnwH9kH#bhxUx-U8%)W$qaXMAg?Yxvl%*M?lQ%;cEdoX6ZpAjojXXgN!{7iH2cO`& z18j}<{@S4Wrwvy-4EP8mHWxl~8rRXN{R`u{^z}aJAmx7Xh5^}&l5CTGA5uuMiLW2r z%On~6^a1e37F_r^)YtH(z~D>r*n#)K^lP_rv$t$DUCWb|N0b z>{5pF9a}@|+oSC=B)OhDUL>VANkc5NZU<});%zYbCOuSXxYs(#@@tz)IJ)k~Ia_na z<~+X=BBb6_i&9Ir;AsNfRD>^i=-JrD0ApEv!C45sJp*k-FuS2|!8|YN6JI&$Lkrr) zb*_jHkyS`2L76_(lG^Va{4kCL>_IAhiyG_lV+14oynb41u)v0+Wir?BIX%DucF z05a9U;yng}#nFm4&u-t&iJ1<|)!Eb>1OsotBh0HtRw;8Q>AyuXGAF$$Ai}MK@_@i& zjgICLSOUAUkAFoua!R9 z{qrKPeR$e%g(tn->Lr@(d#~!lF2ZXtd>pLcNsMzSmKj;oZh?pn6x)=g*>QrA1mB5C zk1GqoW>z?NW7LaOkUyi$g&4GO0@#Sgu+VN*z9#*^`pcmT5D7-D^olU=wiKJ}mj&2h zPdeFVTJ_1iftI|a5TUH?9i>g9bR>IyZD4lSBFB!d+2~z*jnW@_CjrNe|C`McU2JSv z4ar_L?>*Z(`iUa26o`vuue4NpHATKtS`NSO?j*->rV?Y<$z`a)a$!IA>y|8ht&E#Y z?xc3SZgOQ$cV~|UT$o9+WtS_3#JXEmzom2H={1{A_FCZlm=S#dMAGI{J6C#w9uZJr zD4&YR;PjwPj2?NHaUI~nJhtzfDWPl9U{(|=<{R4IpmCEPZU;bVq1i*iJ=I$b5Sk5} z>KzIub0+zHELWmBjYc~#@%Hnk*gzj7mm(@C zGeU-IYgIxGHHImKO_JSZ=uc-VCm_HkU=zqNPajT$in(oyE}QJM((-odckF!rRvD0}#(VzVMqr1)?f&gOx-u)+0OI zZ`kKr7cJ-}!7m^OP#6d*5z#JEjR_Hml)^Um4HRc~VkY?Ub|(lRC;aTWY_?4)OW(iH zB#ZMZJNl|xVwmvGlW>E+E~FkPx|1A+B>m#G{uu19SbR}x=T-g(X}Y?S?#mX@^CPN7 z{pHDbyLG;!UXxyLZ0^b5=2RFipOA0Yg&_(ymj49gq@mue??1bs8x*nCUAwllR!*&$ zji7F&p_CZjfu_Dr&FHOGu_&-Yo<`i_7TQyyFA@562n}ZkPUvjAV(BYU-YzmNCLQ`8 zCT4=;&588qAh&Hf%QLubf)(Q@Kib~>h|KKCehaeU;zftfXDEqYV8k_7ALAr_uIAp2 zrVEAfRTIM(nuWJASx=Vu5P6&PT0FMV1ZmGBI&IP61r8v}&gUeWhkfRBun@`>j?ySE zEn54gd5S|@#PtDfg71&;qcz0Krz@8cbK7UuoQ8`H)HhcBEK<{ANqJ0jfus)K(fad@ z%+iX;-7HuBs+0CQk>UdWqDP!9hGVpZI22IwhR>`@XUcn~cbT0Oebpp_dL0QP2R2ic z>2^&S_InY6FNZSb&qa7D$9}KK*ZnGwaxEMsSAIngYOp&p+nB@;ou7KoNA+*2PS9yUB(Np<`y_C7dQdp!hIbzd)ni|9gA8 zU30UzTlL1hVfH)M@`<%T;Uf!}OeL28!q$_rjbn0%)2-d6OT*H>1zhQ3PkxSXD~&Ou z6&mUIGrY_5z`H+T0c6j^rX|LF?8_4*d_H7sp2E+WVx~Ho`IGQN>%6B|bA*vId%Z>d zGOY4oEii>(X5zIchLM6ZU*xym8wC?2TSWKuWh*6?`bPHE=HzO4-rTgb%iF$oe%hce z*zht&649FcCKW$ELuJpt#Cny(3zOA`ZEePTn~}bs{YPTChH}#>lC zO3Xl3R@~RSB_#;3P5@Tycu#A_XoB>r6bF0w*8z{(tROx(Q^Po+bQBV~`W)T%HVMLQ z?j~bi6`L+p@I!O5Iq0$(1CZnaV1@RuXbO797O{M!M4z$pi$6u#-YOjt6wf)IIbz47 zY1GsgLLT@4U}I8(u8*F}F=DoA<6&#*mrjvemOR=R{wE>ft!9l?&q8T`V};@+IG>J%qp*VSxzOWr=V!ud)x+e#L-PJF zr>0hJe-qKq^W#{x;z2}Wyu8G!YJo4N|8DvHY<~eaPH$-w<{Q)=`8Apftr^q)n2941 z6DU1rRW}Ez5p{Ql{yt>Ms6Wv6_I5p%ZVbwCS3%k8`=%EfDPvEjVdsya%fEC%4<;Ok zP1K!_TCczh=5Uq-N>dqAnMvcwAt{*!{Z6`g8S0-aO9iS_gf5(eDUuU1iH+ks16=g) zu#%_+7(Wo^@{8@EBGrwV}KU#BbttHL~sh{`F#SR(&sotJiP)l=eIOCy~57 z_W59T-()f3F2V7-@Kv^^mRbIPvf|qiknIYYjCPu~!D#&O5K&As`GGl4Tcket(2P|g zRd;T7-F)ppJ%}ZZO%OD}0e{VK5(H=A z&Y=g-qt<~m6vwQaN?=Q_xmSOAr(Igg)hk>9BATqjYIF-L+Nysn4||sHZ)}y)glKcV zVfKk1%e0cK#WAJQ@h1+t1cV527OVD4|6RGK3Ppj6T{F*J6E&vrjQE3+8V%F(JtlA9)Rv`kMRzhvOODFj$$bD2+wKiY*}xhL2Blwn&~=1w1=M6b$LlxIOHf?; z)~wMZ(MAogZ1d=3xP3kYvag8~_qS;m$lkyf| zferelRmBjACcF(?@BsMa3p>#5vmn>i)bm+TZ21#RbN91rQ$jpssq4wDuJ^pi3I*I| zpidF@Rs7$}(W@}`6juE`dZyHQ-w<7NIEqmR+BhRa$?`dH>Sx5INe1HOi=v3v7iC`` zhsAe?zOQ}B6%=6C2Z~^MewQQ4{e4D2pxvrK8M^Y?Z=#e42XN)m+lr(sv?_$i;s!Bq z@=Hw)NbyNvYE{r=tifqQCNCUos~;pY-xmI)q1gocy4EI4M?L2{ck{Xe&S%jv-@s8- zT>p5qJ=dm9u2zl2F8xh!YteU==%$U5(o!Q|48!eiH=qCSnbr?e#}s&HEITUGee7bY z(kZZaa=USEk86ybK|^6<@0L4pN&H|>_xwb&m=656IT?=Q(x|>8Z6D7Q7_`6eiuzH>X@Q#v1X1qM|VWfRga{Phh zzhTln-j{g{X??sd*vA*C&F59QX=PVMWWSyW%LS`iC4L@hIPetl-v@aqfut+;*FCGR zuYf;MrH>Hht~*j^5lzlhW^9~I`z4LAsv3Ri3;w%Qj5&h)bhrA%PZSpUx{us%?lr=u z_=>?x)~d|(yB~(B_*{J$JvElL6Vn7|lkNwv2O~H89P`8%jv%cXR7sUdW6h?w4WfDL z61o{HCT?7q>Xq9ApyKnc99W3M$J+TRlilY9dReZR zKn59gmt1rGS*264!px}G%p*@ow^E0%4gO9TrGF@wrz(wzO;2BjfQv8ALGd1@L$r|< zI#o<>3aM5L8I5@NgT%c5$+6v)R0Z>(71SeuB!M=_)=`_rMMb{a{e@^U{tl}wvKK(# z<|pOr67=0QENH9JNsk(hQk7Eu`K_ellk!W;V+fnh$#StNc1xeot1EW$h!xcvWkh0R zI~#??zQbm&v{?)J3FhTXLuCI3;6w>KuzTZn?bE_=2rbCAO1DRQv|{T53VOplZjUKX{~gZ0kZ(>dSCJkC=QD5o~MXlrX6^6bTA zEu_KzS9P`R5wf7sB}l;LGJ(%To_36d5Su~5{*Lz2B2|RE0=2dOnFdAgrG6uf7S9CC zulw$E^=+|9Pbunbm=GvFpM;xi{Y=FR=>KALE%5&bBSGB0G5ESQZ70N$=|fJ^t!!Mw zJ*2mAM;@NXc5Y#jCLU>d5doG>@s%hU<3v}l%rO-`jZRnnUq$8leuvq`Il}Qh@4=o7 z{?f3q(tJO8;md49sF%qqT+ekVC-+iJizKn~2vteb7$72&K;D>ehR$FG^WbDK9MVY#Q)K3IuS$h806$m4slvafZm* zUprph%iYrU>u*=D_2qHKPB+*3`@f~!wWnzE_;bYi#ogAaai>9Ys%3`XRhr*bwzwn) zjny{^VqySKFtRB^1`kgw_^qtycfKgQ)OFj~EOgyY=_a?EQ1LW38hlgk%yB)Oml6%z zf)G+Dh#+I-ARkom9+RqT9t)4`de4YwNdEwTX0X__F-W#mpH*@P~jLHl5jkq z#rD>Td8l}T`@~)xyYX3$NQLFZ*0=N9PEadLX&S>D3Ce6TG>ztMPy}v41Igrea_M_M zzvcPAn>CEp(!SrX-gY0dMZvrA@9h&COXp0#33YjAG8w+mB>4~T0}~e^gQRJ{{U%6;m?RVuCd}>cf?0p)9(%5rDvhuq^oIc{!&H#u@n~TaV)UOaWqJWC2_5DZfx(#Besr6ouWr~bdi?wXPFbrO|c>pp-GZoD)?FP;>*R_9@-AX~p&W2pyCgEa(4(olmoh~uRw}IS0pvsD{{Z|lOm+}z(^%cd ztoWAuTGV8?@dx&U-a|YoAiav}NM&g4WHx4RDriz!qVkm%ZSp$%Z^Q2qKZdOAd_#4i z_}c#f#2Ovmp*^0Z;ypCkTEiUBsd=sMBe9TTc3~lVwYC#7vZ&Z474h%wA>s`u;;)2t zkBqw4hWuUPEjz(h7dq{&sn#1z7q;m$+dNaqR&gxiL|0p9^5wO)jY*PZ!XKXXpNU@s z{{Y~q+Ke6@*RL&p6ZpCVb3T`;+(8)9u5Jvd$kWb~nD1m*jH%~bK?T5#_@3%dD&2rT zP<|2q(E5IhWqW6@MXcFP7MUE);$IPHuW5N>Ec0EFZ;dfp2^38vD2r?$bW*J(c`EX8 zl6+*;ymjO4E5u$J(zNdzrmK5-YhmIW4MxkvdMZh6Yd!6(Qp;&|2bC`}GTTdJmkif2 zgqn7UGJXL3Q21}H{1Nz}u6Tb^g1~EfLrAQ)lBCc`0~IX`mGa86ZDnw$2tdeesQn=S z0D^FM>HItU8TgyUmk~>QX>qC9L2aej+Iex_CYgC{Dr|5aUN}YUurQN$VZFldXj^Cp z*?$)FDgGAz&y)B`^_YAy28E(FqvHKPQ;=%+G9~{2kR(`^{&8z-Y7#kPhT9~ayugnz zcdh$nc!J;H-+}xos$E{oABems;yoqfcx@IPKTfxX+So;=pjZ+MrIE$L0}Z;y9u!hj z$Q!@_mgKNnK)+6FjfFC5$S=8x`Eq zvG7m$C=cyZu4+HA{{Zam;7|CFYC2WcjBl>5@2wo_R}w>W6i&g&MOdbu38V#4j@-M+ zAd@5m^fY+a!#)J?9+$04tv-cx`W5y3n!cN*y|v?5Tdu8pX>o6#H3ZFLb9pSI7XTP` zjq^nm4d(v<7ykg@tsV*ekNhR!IN_22j}zOUnLN*FEv1GTd1&i??}HhU}MdH}*t*-{B;;jj8 zY-G2zB5@pB9{T9q`Gu|;?n{3)Jh?Z`4;A=1ZTnjIw?ljVJHi@`ri3pd38*!!3nOZQ zSsF*VJnmemkgFF|b!Hso*VJFL=BxWbd>^^d_1_WvOYt9sd?TvCaV@5cs%jQ?53DGG zyp(G@nPZOGbId9&_kPp%;F9mA8Jk_tfA>w<3sc2SOmD~@f>gjiXHl`5C?SBs zeQ_*ER^Br4+FeYssYVh)8*^=vIKBb+5&KALcRI$pF1{w{XI-Afqp{NT-NkgVDTYY| z)1<~M7-BHGAZC&7Zl{%CXLf(9e`o&yh;8xr!!~+kdPa?H;H^^XEjwNC#lDKymV4#5 zoTbZ7B7-S3maxWl18xM3D0O9ws0VR&} zq;@d}p9?IjDg|VjPa)#3j`2!u{BNe&cw@qM)_UuBdLIyYlH%6b z>Kc8d`)({PPwy^#grCTcIkJ++HN^h_yNC%HfH+(4k3IwVm86|7!5$&A)@)-pcYkHR z)kL?R9=Wh~xq?QL*voL&5+qNVq`8YLYT%PEofzr-BjU|>#l8vAw7=}VH&oH=ik9%? zUK%FDR<*S&6tLOFBPPkCfnaRPpna;}XO)6hPbSp9B3Sr`#wmMq;E8-)t4pL^uC1x* zUL{LAT_O*)OBAt*T3E|Ur7ajl!W(Vu>Of_VTIxOrc;i_3eSLZ1-9O>8cz;N?p8Q@x zpz5&RF^O%wsST>NzS&v4clu8-g&VKf%Heb?Bu zzYfJVt#G!{#XL~8#hS?X5yYtq6u5{yk8K{p<#vuq zgG?ZeWQn9cV0LDas&v?Q&5;v6T zyJMAgV~FwxW8nHHjP+e>MYx_D-8R;H7dKa0J&=ydJLz(OpC)EYK7LamlrUC5A(c_q zz9Wk0$>tTB>eBT0Qrq*}*Hf!2v%^7uTlYP0!(KGg=h6QFwvUEvbzM8d%H}(1*G$$- zQnZMY{i+sV1*~dI ze4^Nu>JBh#i?;Z?tv0IKu7Mn48B#Qo(o1VAE4d(8^C*#1%d}|K)zuwj+s;Pgv<(jp zc)wE9wT(wt(X~tQrv0EdiNs)5)aFt4ZKGm?H%!M9}iFG{etLup$)vL zZG5phvp1P=-eH_GE_TNoZBv!O4PR$`1Bd%R!dA9X$t1Dql1ZoSoa`&)xrs>_$;v7p zp&cvw7lH5+h6<%gd@|8@_h0-I@?JK@sMe!SEvYAW^IAWZpWbi$G}I;6G(}s)j9puw zBJ%*R?^lA#4?9jn4w+XUTJC%UVGe<-n{CEBTh_@7@`NO3J#o}$IqDCsb92~#WBB1_ zjb%$sQ3A+Re5&Alt@#o#c|R}EZh5U~D#l%v+#S*mF)5!}&G5f$weS7S|&j9r&=ILG;`%BnItLqWz z_OLIJU;@YGp=@mdGJ_fWpkSZjz`^NXLw?#12DR~b!0og5e`lWHl818q$+UsoWZ-kb z?bkj9;|Z2e7PK-v4GC{DRgjWj<^*%xXOo^Y&N^fG!^FIN;hbWX?RmrSryV~p_$T^p z;D$?1d>@08k2f@GFO;iKUzWE0E?T#U*?czBU<&s#F6n>^fU0)#Kqq!~jFF6X0C7`l z+D!f|_)n}@D!fqro=Kxvhz*rT+q4e*fWgjrBLr2?6y6i+|fKORN&@dnME4eyNSi={%j& zK28g=&d!m@bN-lK!e=jINkODEhjJjQu{hs1M z{+DvRyh?wCeF^pF^sPUGS|m0;0kphk7Vz6y#LD9s2owR^oa6rhtx;V3;iZvYORI-2 zs0kYeGtXRbM>*qUz4J>?ce;5@;fr86-HBCTeqeE05i>}(e&Skx^2ztf1_Gj zE6V2?5uUDcbAya?oO9B=tK!#({7vzr;l=gLUVe+G#3gvF#@3cn7j$hQU_ye!?t#c} zy~!2rdiIE(EZ4rq#0y(SS!QBJC6F*>>T~koaqH=WRQzW;=^h!HIIW~_A~GRJ@ESlk zUAR1MJpm)HJq>+cCk-537l%)l7S`H7>-uNH)2l2L{;=0IM)bb6`F~zV#hwrF^@oad zTOAu$(%$j+iCQg06B=#=kKX*D)RB;rF95j8mRCKmh5QtJLvg0hr|Yk!$KmN(3m@$Z zSv=k4oA*qvUNIB4Jh3BX#y382S(bOFN7k=2JIHO*S-Wl9e2A_DY z8QOR~{{V)k(fnQENdCxtKXJ2UdYn!bMr``@2;!BpAY`mbM||i*sd(?Nw~LIj4+5%0cB%}fqoF3$_7*^FPO$7gTqsu z0^Un0C6`W)ZKe%Q=^={Z7vPxM21QgO?uc8nOy zlraEXV=_39;|k%#sB+=9gSx&K8HRlAsII@SqQB3`_h{Mb**247X{T#in!^T*KAR=8 zO>qgilkE6izdgh^P_$rRs(`L~w$t+9dB?+_71&RAdiFQBcRm!3dEkQ9)tG5Fvqqp| z0>yT?WN(@ixx*D8t5-{R;fOp>c;R&vmK`E&PLgi4!hF9vCz%j@0)KfcJLPh!Nd~x2 z9!qkvU2Faz)UGWT#0o6ozR^XrvrQ~&V^xvm5h4N{E>+}k0u^9Lka!j7N~4wIYpp!L zuT2h^s&Gm5*tPp2X^?2T_s3gU#q@Vkcmq%uuv|#et*qGx%U5_~VnVX+1i4d?TO(-t z>q?9JGxoo**7VEh7sYm-9$zAeDJKtBo_?ey` z{p(KOqCFn!4HLzG75@OW^o4Q^|Hv^>{Q-%Qa%sJxNIAz6wmT%%5f zcF6H7`P0qu2g9BPi{i1+^{A~z`9TdPMh3!uPxM{e0J z;$ZejeldJVm%&%bsOiZ1PLtvMYfr1_9v-r~(CxI?e8-ME$pC_Ae7k#y z-QaxguB*hK6Ex2lX%-P*KB=Tje#;h{bE4mvuI=Xu81n6;^PTkxA|6tgND*L;ON4*k z8DUI+5cn%X)Vx^ZgiO3Lht68 zxaEK0bWQNr;l0kdx^9oJ$)m;(_T9F*D7&+;NUW|cq@GhU-br?6k~l6c+DOvi#Mn*q z7$gAiF1%T9s%gys01n~Sb|%6{ zgm9VS-5NzCdw@P0_?!O#1sw5@?Ee7&00l(xrlqdkUg|nehxNTj!S~u({r#lYmiH#o zdqf|)gb&=7B|@t*wsIY-=5O1J;7*tOIsVZ1Ryr*HPlNRYi(T-|)wScSQt5X}vRlds z8Iku16mQwgYC@{uA5Q-Of~o1z_+$3R*KVTw7uY;sYiF(L`ifm#GhPe3Sp4^O+)>Y( zE{;^Hmdh5I924h$FYyojE|1w~OYr`tmXqpV4X!-BGWCRV-OFopC^uHQExe*hvDKNF z0L&AO%0M_9T}=E!u!_lU;=8-jtybR7IAUoexdKz2hju~9ZXyexG0Svq-(CL7J_b*S zp9MY+==Ya)x=xYeABoaIJo>!SLu2-K(kvB|&LE#H;J=hegXWOQ8ng4YzBusR<<5oT z*=`}TDs7r8c#>vWBe;r4g1j5qM0~(_-HulqR5J?tm%?&gXrHl{!vTLR`@A#b{{R<7 zZ1KanrDd}4>&N7mLRWOG6qEBKBLFuI$N^LKul=F5e~7;kwM`EDSiaQs`LCqZF0}nP zHHM9Ubsf~x%W(y~q_I&w{mij$+yI%m`R1-4_M7pvK0Ww*;B6XxH^f67jnLZ$X()U{Jp0VNdv;NGN#u|m8n@);(B(T;c zjTNn>-tOc|S;h%QQhsd40wCZ%A!_&QYaOPlZ6nknl1nW_d57&5N+wXj+=T&HJ2In2 z7zZJ?f(n3pU+ih)E19mmICYD?TFJa!Z+&SDQq3jBrQ%%K-&s2X00MR0a;n*4BxNIO z=XWjt00leMFT6qfdwg2FyoY7y{3c!?NdTAa47tCTW@T}-Z9`C@7 z5PU=MzlUOiM7+Q81f^Qi=6UCh8aTX_6IzA&RAuB=+&3$hL{)0zKWXg(`^7)Dcf_lE z*O=(t@xRu=?N9h?yut8peLBz`BMn+ zU4&5_5gyaLxeQs1d2O!2y#D~ju`h_VE4$dvr)i{VrERU=@J3~V6kVwwlviG0@FW8&u4uk7O0d`&&jwuxa`WkVX>TL+V4%N4kEK?j%S%7%RI3HAPi z<0%f6q{MVPJ#$jI(lpIWP7&!AcRJ69uH&3FhT!CDrw_iAJ4b;huR&^3{xytgRY(dl{Km1RaF_ocu5Sq%M9X zd@(v?x=qiB{5yZP?(J5^wPCU_*Kpp$G_2*n(S(g9n} zX?icNEp4rDwJkHl+D@IStHWsvEv4;>%x+xmh&8;B{hkJ7P%h#~<8*mj@lrn!_*O3x zKY;vOtX!)>73IyUYTBzLc3xC7++XRpP~JP-GEE#4PYtn8wHc*}PngRrnBE%DJU{SH z!oCx>*EF9T>AIGe;?t?wTU*&Kqjwu!!xW0wPp3xf7~s)+@#n0=}-G-)w_zQvMA_ZA?t`IAl8f&M!A{!He^e+B%%G<-w< z00khsj>FEzQd@1jg&>!)*j)2L~DXqgL^jvn2MCT@7Z- z!QN^THrqjcxPrM;cNkZnXeF|xfX2qgE9sB;DK?3%YQ8i0X!;zmL3tN~bmJV}Q?&N% zE^iLyVH>KcUBS00&m3{Me7o@l^}RE}HhNEt_02lg?@N|9wVE9&Tf?);eSpOvHuHRp z6;Ku0zj1J_UmL_6zuI*gvv*#77Qfe?hoP3Xu(Z=!H2e2G+S|weAhq!7TIlyP*y+TW zg}mu@jjc$58_Ef8JoLDkQcp3FILd^TvTLIFWp!(=X%f$->N?x%HW7>Htgp3P!0jL+ zL{zxUo>XC7cgqlGWWu>1=jT5L>3SD{{15h&AKF@4IvrN`O$!Wg%_vZ-hRKg)crJo5##;5;X&3V9dNFsoksRumQ9h#r0JwkL#AT90m0gP-Sm5pm z4&mjVGK9W?Cb{CD5l3xppRB@PL3NFmg%GWQB%7IouCZf%T8X+gSWJdEw1>&0?|6wsOAz0GG;}1K1E7V`P)U zn*ONB=tl{aVc`15U;BGqPr$gAtg)H3Wl!u`FZ*|=-gVR3jZaU3+Eoi}BA!MV0(d__ zPv=_qFaYpeVnr?rKIHj9=z09VI%bJ#Cew~mPv558WPSVr*PQ-7qP66V-S`$c$X(X% zRGq(dhC$D{^X1%=>%KLT;D*nbUO| zXDhyQ@_?*($m5{rsKt8M#E0@UYiM;lJ4#u&=edp9cvV2*2zpbg530*_!=@$0bFd)dXNoh8$dWt`zjC*)sK`HcSn z^{TLx-|MqO{;811Zn$iGp+VW_|llfO2;x~s-@pM{vQ6gHe z+9riaa*L9R#0Ky49tgo)*K@6EYj1BLVH(DX$T`7YSdxGJRmONhqwxiW*NCNxNGF}} z7DgEmsQCc^oxCo1=PXBT*VN!9<4=}fX!xAgnw?&F?G^ohne$ieZKmn}0PwVIpwu#V-&;rp04rbEasal^X8qIXr}NcSYoq6ar8jjPB#+au^T8_?Hn0W|h)b_nxcz zXXt!8N~ALSGQPI*dLI7(@VCZsJ+`XawYvFja!eC7#L`R)hGha4-UARDKOtX~Zf4+$ z+GVDbsA;<81y&_FZPq?1;Wl7_P*^%!YE)|W$ zW-^CyAP1GUlYpS}Un7X7s?EY$>-}l3<@X;^hMT6jmv8It{+UC@8Uot*O%mSUSGYHm zAV^KLmlr~J+l!dsl2uG8+&*aJkYoms50<>^;=Y-t>iPufbZpG_)*(gxovoX@;mCKG z$rs5WZo4FB1xQr|Ks&xUpH%SYh4pU@t?rlop{Y6DZ>OtSw5+M+`8QKa5KH$@8<>ar zKm^wtsd%GMPq68lm8mxmZRJT7gm&%as^e=;u3O8KJBHG+$rf|F83V({sY&xM_y=34 z`FT^lk3{{2r_@$&jh5Q7gLQB4_fQc^x1M521|kWzJ&WngxcSefE9&2h{u$Hm{xB}9 z;vE`YPs8>aC5-<7YC0@BoEj|fT}tBQ#__N+Zg@Zp!w4p1R&_Y}zxGAbrPIg8Xwf%m z*4_Zrpn`a}eVSGRHgO*EUm=MsSmc(@Im!AT<2T074}2o{vi|_uvfE$k@1s~D`&{y7M%GoRzoBY`rCwl4mf03KupNbNEQuzI= z==z+?r+gCAEUZ&X7Xnyji&>iD*(106H2IPM7CBI2A>gHWA!1JTM{iJgC^JNiq0;{t7*P zX`=qX{v%xnSn&qEsZVQnqupF;6U5J~M!L?Q92$s;43H#}L2-9&`?lSQlzDOVPp^J3 z{4v%%CE!hF+rnNfk4gA~`fa>-cb8gR-)d7H{{Y%JJl{+6_u`*DLx|q z0D_eGTT<~a!i`JehN%UXovwIm!E57d$h7|ev|ZcW-OH|OQC`j@5yvUm*1B}e?i7X$ z!FI_bI0wgnwO@!Xej$Fs-vc~lC61*#cwWxyz*@emE2*`fD^n!45Ub~NNU^NNi5`dV zV7JWv0@UU&qsG7sJ~u>~ZaWmkWja~kZtRq(S>@h`zWV^x#>5aiRmIjG5RJfGS#uAzH!BTI1* zS)0qALgYHf3aR_Vd2k$`DEKYlYoCa}6z%kD7=%|>^Eu!9qZ*DUp z?{p-U`n&d+@uWU5_&o%ED7UcEHJ>GI(@)c&jM=G38RQf0T$YYYu%JM@5hO8!>lkGK z^56I-pM|w8YxaupcAaYsT7lNJ`*^o3%_NSVWJ@Z?oy!^6<=C?DtGgh_R)1;@T_o}6 z#!nTCtbf8&<1ItO`dsfkes7SsE^i-bIgMs!fTIOF#8QT1$U9%~PR|m};!oQ*!1kw5 z)n=aO#CjN#H+6+BEF-;vI9Q0@3&|q{Rb~oUu0eGkTl+vY-mmckLeMAu#r1`&%{t8n zZg<_yE#!rjK;HiVziq08w{8Fy=|CLLm8V!*c$F@$luaCW)1%Agq!Qi7xmN~K=OR2V zNDH*F>UOYai?8mr9YWQvWtMdFoI+xPG?HH^{`K7m4J5m_BX0*dIKT$AF7IH5IrSTR zIW8nf%zA8M=W{%bEUmQwAf2DNA2t-MY{~$WDTmdi)*9Z%6gcx* zSJ)HGcIGs}Qb@=ur01vr9^v4>1L}ST_$_tfsH_c*hl!l{a?4d)ak--cN#veF;nqlA zOe3KIibPd*k;mHq0PssF^|?M9cuw!jw1>jUbhkHpwzuL*bhPmd){{Xu*=$RIvP%-j zbX+%)xh@tr+{+f1z7l6TVDxyOH-3o(0nOv_84zxvM`C{hE_=@kpzvjdChd7 z9#5CM4w53L=r`iSNVsd%GClFIYNvT3?i&Hj^Z;*%}x*0(2lxQP@( zhA8)Wf+Q0moHv-X0rV!F@n^&Ec#hLe)h}%eIujt|vxrEtY?bI+E#s4{vlk9T&p$KB?nfY3;w^A7LfM{C5`@8{aZPLSj4~8`F5_~4MAKAM0wPUC}(%yJx@@MfDxg7S^@fU_GJx1YUgvT;L zJVsIWSnbtvJd&<1>YoWb8>;*$@jaU9b{c-YABOc9o((2zCb+z5JiCjJHWg^*lHfhO z)2+1XVhwMfIC%+l02fd3e~GoZj)P?vgYDPtHy4q@n#;uAWFKWqnV^pOAhU+)Rya~5 znm0&5GCn3gNw4tR_P&3M{xQ^iK72DKo#2~ILhDYp@cY}^iEkPSM0W5q3-?!o86swy zXk#(%Z!>Npa`5Q?0JeAhExk@}1ZX-RhwS`EqTeml_syuq4!1RwaQu*8s>Wh0@Wikz zYjH8k+pLedwpU;KMQaw{9XuVSXqr#<*M&S+;|(!qhQ`BYk$rh$rek#kGu+2)(OrCz znUYT{5zTKLaLF1gFa-Yq6g1z5{xr7M;=8fXG!GN_3dZ`z@wGUO{GVXS@_x@WnTO_$ zQ6;(|rQH*GZjSM@@_zxWH6IRqOY!c9s%TbvHRg+>YB96u{vx`)(5_Z#&C<1{tnkC+ zu@nCKWsC`CSwe(J(ipg(i$55=E2H?*Z41Vq2xY$U7O;yXw)ZP-;w8J&*xK7isB29f z?9Xe66weUyA@U`Fzm(omme&1|n@^j^K0KF7w~;jA;p>YyC;MFY*0%-%Jms~XeU4&^ zd1Zl=LmCNSk(9bR5!ZqKI{E%g=Ei>o{J%7L$NUrrDHEkuam%!R`-9px4 z=4s#UH+vO9c-SxA&c{|feMiaP9DGsa29DcWweUhlkVCBB>M61lNUWOgPqMdbXf+x4C`o?J6|)B1`$$ zBFrN(yjM#A#uS1ZZ%+RJbYi~F)7IDG*TQcp8aZRUfo@^kg@lW@Ao15Ao}GB>U!DH| z@IbGL>tKE*_!8trzCB7C`)$r0T^le=5MbZ~9DEaxoDq@5eeduF)jTKS9b>~Vvw4~| z=a3dqcbFz$GCryf0d0fSMfv{#Ol9?{Bl3~-TcqMmqd87(calEJgHTRxgl2vfPdgvod zH-2_&@ITbw3FyWV6}<(2RU0+`0D)ZhFO0XZb>Lke&b1J_MIfb1k}7m@Z@VTO$o5EUn(F~SI^p6 zO9u>2d113WZRgNTlj_HyDYYDwHs(TMM7{f((yoAP#!0*rnf{#E^FjB`_zqvap-{Lk|b2N*hWg!Xrn{Fmf;_r@j-e`BI5 z8Kl(Z26(f8kOm4=j1h(nn#a<}6@eIVIPD{c!ZdJzMs-W_AE=l*5yK_{2Blv~aLXrq1Nafb$7O<>mfV`O; z0&qY;NzbKyE(e-THTF^Q`O1YlZktwfKRADB9~azs*W>2BbXM!^RxZNhWt?-PLW%pg zB1H*>J2@nk&fu+{eJjEm#*y(4L=X$>-9BrJh;cNRt84wCf&00qjTrAm186E$iAF3V zBMs%39w5^q`0;x>S=o8llgk~0#%@aPXLJK>X9ae1^8CY`mfg0x%X#i#y|LA`?J?I^ zhA3hZLu_T8ldhH?Yu`a$Es;_ zX^4vgNvCN3YP_r3Fwf^iO7a>x7{|4^RUj`JtbAp;()4MaZD5+mSb#}vf>($F3Ewc4 z928|>$L1T6)jI4iJG1KP};>7nWxyvu`gzg=DG7wxGK}kgu^fg0cAxk^6g;F zxSs}CnN;|;{=B~<(xpPmdIQ+-PR07%o~Q3RLvHdgO*a#?Z}q?y`9`IVKN zZ6v8A2PH*+3trp!qd_xUS>Cmbtk##Q_JGT5K?Sm3av=FbW6UkV+(=b5l=^O&YvIjN zv^!lYHW#12xVX2HDW;7)>lkZ&bpkui8ZoG z!mCD65mzB{(1r(c{J@ec6;H1J0N2dkN-~cscK*Mw!1}ZHF>Nx}MfkyOqh3dSV0;6q z%p_QZOs(<_uAz<_7|u?3?Z$nd_LrZ**Z%;tPJvt+H1}8jbmN%#o{Z2(2O);3#js z!2bZqraD_+)scn&0C`<>{SnysDJe>pE^M{UBTCdJ)a@=WyfJ&I&lTIh z_G+=XlJ@G{T(#4*HgStcmN?9DnVLQJ@|5{E!@n9}(|k4JJAFRy#2zHquk?8JJ#IaE z?kK;qboP~DiZ8b9nh|+$36ex;<7pB&_Q@M$k7tj^dcgQo9sw@R&V2|Ikra}b=gWn)y2l=hEohfcoKJVkhiNYWzkR-+?J;s%=_3iJsmkK1xe1q85&L&Qs;Mg#7m}#%mo{;%C9Too7wcU&8+YAATG7 zqrg`(>3WK45NTHDS=Fbt)1thSX61mTrM#O_PU%;0VYVaOe%IPxk02cOFI{pt?YFheKyW{64)R4F5?nnIb|KJ+HZ^$I2srz4IZEfO)-)AKG3O@V2kyybUeo&Y5t# zRF5g761!|83zBacRH~fD%1f#hkKX6;jlaVW*k{0Y+MIXIb91HNYt}Pak!}`wFYc`g zmT4nDx)522(HEq=vpFFb&3q&*7vc7=eQM`YwofBkOGmZ__RZqCOL^ZNz**!6VcH3l z9jc&;KeY6pXZ@A_4B7b*&m1~E=8)1{L;jPeOf|hy`^vzTMqFLS#k{rvkfFu^U<|L> zLK}O3j54W#T+=hLL9TBRS8@Kp!%H!8>mv*Z%-+Z-G`;_Tt*|ZyV?{qg`F% z3zkhz*`taz3A`8b+-=>ljJt5VRMtPl+kdqDe!ag)rvCtmev@x!Z6(W#vvTbOXuJ$7 zNU%s{!k1vt=N&O`{t3r=vB4vf>jsr+KCIE8I9;GyP1tgC z!OIIi5o_-Od{owaNvE4#I`_i5gp*t0n8j^8A7_CMT!5P-k|)FDKG_iDt|$Y|{9|r3 z{{RzPhMG+_e>v=Aj!2_vQsfPiJ8e?i01it4#FLGqD_=$a#~%(O@N`}!_>rjI-OG4w zt}VPbCczoDx@e?po7RzlkIRPKG1{btX?ATYtzIFccuwcyCx|15%7uJOt6dnSwrfu< zAX%PCfrcHgi(i<2ffHbhlmAARW zGVN(3XVg650fJ4*@Mr7;@ZVMVh2p;x_=`=7<4m97=9@LmmEzn=rpo?uE$v~qD(b@G zS*}xIR$Q|-2;01U-|&A_(Yz7+L3p3TJ`}g`f@?a3#oe}x4a64`CBo_XjqPqHv$u-o z+3n-gpt=CfEKjw8bLZ_KA5Q#h)HMBPzzL&XXgZdQq4@2r^;;|GCYm^S{{V)(VQs72YOvqjU+9()>9;z!h40eKQ}L;1iQu=t zXd(Mbzuj{@uvE8I-S`MqN9VBc1eZQEu zms3X1W?jUgOU?+2TTvT1C{zGVFUH>v__FU@i(7g83v*!=h5UM#i0tEvUk+c+vs&3t z9G6XRcOkiwLI(NJAdMhJf<;G?8GMii$c7i`FZd=WjWs_K{AKvd9r}2# z7Csw}YpAs-QqsXL;}Th)vD>L)iS}!2K;C1sZdMr}16_C@dA#AcCg3FM^zYHX1l)~*jwCq=fg8h`(7^tX)7LMM!_@c9E_Zn0BBjSJ{{acrfA+5(50~!k8?J;4Bmac!pfiOV38T4 zmA11ipS*7@l0;yj59o08saKkuo1d$){B?h?nZ=L8)TZq0zpuFRp{JR=K|Q_Hy2i0_ z_HQyvTZ_?bvp&^%8KGuv<&hb;fOjYv1hA)R-agbX{4uE;Jx5i7+UPWL*-IIbZ5ddF ziY&1^2G!h7(}rNc?HJ~@eMV0O%eDxtu2NZy=&;1E9BkpzIOA5sI0do8Hcl9l$_UZ$ ze}?s2$X@uqhkk99A5evEl0&B;RtzVB_>CJlXK+_>07yv!ze3{vabHeFqifptBz?h;^tQ_hSB1N*Y{ zAlL1jDTs`+#Yw+yQ`dw=u$^Gc}FNq8Ni*cmtjATe8 zo)(uMGPq{i8A#+FeQ{R)BWWMm5w@b4^K`qEx<^t*E3=$_?jEv#1ryysb{0_dD+J>Xxbh?g6?=<_%P@Y`Ct<(wv=RYsa(~dKP z-nl)UT2fJ-oUO(AulOl5(1)?B4{08;QeMCC?D9|B^T8`|CYh_kVha-$ZOix3inknn zPdFSN{8!F?621P<@oL5jN9B;H(&0frC}LY3KKD*}z^|zP0BOm`iGOFrmUcsRrJ_Q^ zA%n;p$|M%|e2%A&%j8`uBySO`?=mV#Tm;I4gWP~mUchAjHT=KgJ`Pw;FQbU3ywn?h zYVrLOKha;K=3=qj0W2j=sLHGtqiSpa086p-Cyu@@ja$PL{_@b)ty3*H>ph6jG(bo*CvCoGY}d$CX+1sMSBemFWDhbLhLZ z7WO_H(G(Fq`^kGhRxXj@7oK{wfd2qYQhZ##oBk0v^mw+cy4~NCDOUdgS8E;t_dQU5 zz`NE@hP+YzyYVkqWD(4o3&o?v+s33xiw zOpUX3I0jvfw1M9}bI%0({VV2w+W!FJ+Wa#3W#bPJ!6HNbfoNlE z9r$hG?*wU{ZmV=97dP@pB4n@yM6!-uyvYoY^6fkf9zZ^gSyjO0b!7fKH}iXcUQGGA z%AP+mrlP&hZdK-%t?T9Z$sZ*6GUM!8joyWCuFD*<4UtS?l1GDMVeS{@XN^^Pz#EyQ z7{=EJ*|d*|`n8puaYGfo)y;*pMomW2FSR6*d4N3gyYE&dhbBf22|yHMaq$m^mp|~1 zO+2?6mYCN{#yqS>IniHXWs#(T6B%4^&C6qu4SNT}4SLG^O;f6PiS6RHx{>Vd2Cp3Q zg5wNWnN*OZ?$)2z_4l0>tw`E*O86kJ5Y?M$NCAPh8=kcF| zBi2Th2AyLvn@N<}v^J8dW+ym1M1|GHP>yqz0eJDH7tGr`;}qhpv3~2tcAh4G84;hjh_MzSC9wPU`xw$rB5t$Zt{!FRsm-|dq5`h~<%rMwP?SsXh9n3-^IfW=!LU<2?HuQ?=k8Pf3u~iFJ~a4aS+;{tmVGw5P65y5;I@!q+uct7l( z3f8=5rFhy4_+L-gV@N;I*hhH`cE4zf)+nbF*_e>4NWlG?Kot^63hvl=m*eNhFNhvy zlYQdtdgENzS5mapEOgs13|x4UQm|Ty7U7aeOiK(AMD?#dABX-i{BXDM zcZV#s--Mnj*Jq1VhSy!1L3!f1A`nWJtR}Q)2qs8oRw*Jz%W%O0tC7rl$4&6p#hU(` z@SoxijX#b2FXL|%c#`e3Jx=4pLg!Mo)9&T-t>C}ZZ(iLk?hfR3kz<5JBYEy5mPla{ zrO$wRcZ)6lHu&F0u>Syrn(g%4dwovc#`DFGqgYzbQ7^CFYb2RPr{hR(9AISVTp!&N4Aa_;z;f;u@b;T;qsw5g|*^1ZOBo)`$heeek}NE*Fy1Ufpp)3pAhu@H&dGTT!zCM zDd`a+g#9t6ROYvvI*PaJ`ds&Lp zOxLf3H#Sz7b8~Cvz_&kWfXR0b+^wM?J=W$^y$wUXxAMMiaUk!EizaA^#Rr0(5790%;r6~2-2pGHkeThA|7yj>?DD*T)I4_?l}~y z33kTPy0#y34b08Gq?^S06_3O#&yV-|Seic$+SpC6gDmh!VU^`D!5ang<5=Jt+9~u#(!4kS00`~OQ)X*hd);bytpqW58=vhhBNAjJ{X4II&Ta6w_F~d){{Z0{x$#}vz%_ddjXK`pXV8^qol*Qd0zzqQgQT_?k^ z!Kp<9YV*krvKLVQ0An$+h(;Rv-cm$z$X{#rQ}MTpJYnE(jb9ctZ7$nX@t4M{);<=| z?`KU+|N72gKU;l644t zQQ_9pQME4SMI zlS$Wg*tLCHIDXA|=T4gL@q`yv>g9-8qkD+vIB=prHFz%vOM9+q-UZY3uZUXo&*OV_ z)9tjAth7tvpE2HPp}&nKG9|3RV;0PAH$<^2s!1^29>i!?_rjMk@ z7N)ko4!U3MJ8O|Ff@|NfTwANhZ5Uy@?F?1YHCV(dpXlHjekb^=rr52&!z;fNU1~aA z^vQ8&uV`0x^WAAy)7#4pkb=_Q-V2L)l^S7p1&jTu#9zBn=CB_-XdXN8?DxJI()=0W z?+RX6{5{lUxW3mkok~k^BD8FAF+(sWSl09FZfiWRZpc0El{@#NQD7 zEVI|VN8t%H{{Rp8h%LsOsB5}y?5llueHF|sUuZEU(N|?enWc6t<8;nJgOif+{3my! zcvjxZ=06i&UB?a8pNJr`wbP6eMC~NgL+3>_@Veby#zc-JF+*s~1Yw~789x(Fv*I6* zgJ1Bru6#G)+pR?`Ev_cgZM1kSIWz;?_ z{iMXYx@Z`?w@Z!fY^zlsjt};td~}>yR_3b zz4n(cnJHOZ5Y22|VA!bHQz6=T$@*LV2vMwB>t7RoH_4_OgQPEot)sOsa3owWoV!L9 z0onir?Hm;Zvt_HV1N?RK{F%*+{tNkjX!|$EejXa`y`g0zCO-t|;T~1oh{kIxbdK#>Xj-Ck7*?vvW zpFdF<9fmX4>GVA2sqnJXUDx22OG%})vfUw;=0lle;4cRoK;(mi&rD}E{Kw)hIaA`i zWG1$8v%md2hnM;KAE$7NgUV>BzK^H(pPD{v)h%@|6C?isZd=-Ew(?2@D>LnoMoGiA z-@DXcY~gnvazXG9+S9~u@Z!bvFAF8L_M?7_9%b^4s3-0$kT@|&e(}V`i7Sq)Uv&IN z@NS*qy+TXpiryOyEhG~>%EmI}jo5r;a&ij~nTGzH)U%BMhksT{^mUn<|5O&Xu6a-m9*CaEWjSx;#HO~k_BVs&nld4NYs&A z{tozU;Vl!va9vpVhSuj^wJ&h`!MVTFre@%IM&2l1P~egCnG`X01%?H7Hy^Ue)I29A z+B8pzS|Ue#`m~G<&ZU z-z~I;&&Ha@fhlWC{T>ULue5NWP9bPrA&ia6&zT}(+lbu9<{x{0#vdK7Z~Qf+YF3iP zVg0LXZKh8gpS(*Jj56EI+b{|g-E4N?`H>>9%L@4e#NH9ov@eM>=@;+e-wa!%Dv;St zbE)ZQlA0^3{LCJ3BAL`m4i! zJ6OD7uKX#~RcsB5dDiy^aPg&_ie@js5V9Y*uO+INX{iQekNQi2<}96>B? z6ryCfEwNC@E;cJ*`Eh_rB}w;RhnJRL3N;TI>i#Oahe)*7HCb+LbjwXZ+}&Kp>bp?J ztW^tPhDI_r?mesC&2hL)Eg!7nYAC@iT)o}4->tOsD~IxY#~W2(a5QC7q^)Sf-__|W zTU49ZqDdm3?KR>?w(#Dkpxi|nTieJrNG0F~Eg#+aKtFhr$Bcd46M}p*!8$;PR8uF} z`j!%045xmlkUO4p(z>sU{ylGq-X^%dbG8Q}8%PU{*61<<@zVpj>(Aqz;LSN-RSsm3 zc_3{iOCP*5jAOa4<6bDvf3~=b>vXk>X)g9%{{S`Wf2dv>@RYb`gkhG~(xZ!W?aw8u z>*SxI(D>oQ$pj&r{T3EU0=sY0Brr=e+D?~HMqgs2 zPBuKGf3i^X*A7QLypvgluk|^kC8d>{<}zfusqe`dBAPc)<YlZ|QoHovcL>-vA@7CR4C3Z)A^X4*CZFOD8fsUc*}6A^?q<`hzt$_?T@mHn9yWoo2#rQg46X9?lmt3mEMZ$Lwcm0iGz4QNUc~)Um@1?9S8p{d!iY;-vI@U;MwXL+)WW zI7U{}`u@Mnk*Dy-iKVzcBDs@O5wi%dG|MEma^&ZHfmtJAyEyV;h!R-5oSn?IeL~|# z`$dJs)9IgUxwn}T(-Gd_DQ}z0Qs)IX790578B22qh;&Q+5HFSXCHqaGBtbm8T&Nhx zeDVf1vbNKV9&uEBJFZD*G}m|9z5bqtHF#hS$tB&+KZLgB1h)g_^vM_%^OdPNRcWhz z=dDUA+|kt?)K@nX+s!S)>DLw$zQbAlKWGt`creQ5HlF5h|6N|W66-}olXGsC6)W?O_Q zB78W%A9Rd@RPatR21w66KJl!t_%0WS?mQd&L-=b|iaiqID<1~IG|=8&NgU1v<;n$) zIM_&4jH7gs;gl!dP)&Bfuzjh6MEJ$?PD1=Y)YVpY-qHU6t0XI6ZbR7n8uFj`FZYO> z!=JQQf#+)^)NPZ)7RvVSQZ-`NcX-Q|Bn%<>Jf}zK#{+5mJh59Sie2C4&IkVd{Xax} z{{Z0Mh1%DRFE9K;E$zRsW)@y#>ajy9}d50 zSUdsnrhNw9)=}d9Z^+Z3gTwlaVkx1)CP|%l-4=;FsK|y$K`c%RukXF@_Hgkc_L8q(F9Ff5cYF%Srot5NS3ZhGi=^smYXX0;) zJ`?yV=0622{wulGJTZN0d@-st_g)y1%l98(EnyOQOc5nO@kJ8u5SwKS85=!o;DY=< z@jcF&@dEz8H&k63wUT7WVq9OK#FbEH-wMN`a%b zA1>J4E`P%=>37$+8qb7V&hW>Dr;|a^^uG*zAl0>K@7OalJBGDn2SJS@BeQIEF_E_e$w^p}`+(4-e zXAnNmJAjfz00)44H26*XIs6FtXJzA$417tqzxbV_SzIc3^IM-<(nhOvCz{@Kdjx45 zpvgPI582P!oUj>IS&Y*Ao{{2RbK=*5JU4mcTU`U=pNYIJdY8Ipzu~w{7e!^mOKP^# zc`-{P#SHNjn{0Ph#w2M0Z$5t7{{Z0c!i&54d^v65UkP}=V+==Ahr?P`gc_6qL`^Ce z^N`PSVDe;QCo&+0DVWI_jwhqzU)i_91LED^!=a^V9(2}vUa>VFP3S-e-_>Ga8?gId-t zE^T8nx=4-WG}22Ih29bTwF=Ot?<_L;AI9Hu4ln3rIwg!7}-B{h=%B{{RQ**FOwCE_f4NyPh8t_=CcikEYF~ z*b6!1WOzifC7~ieD((Vr0gyur+}zsF3oJ?HM0n4NUkZL1pM`!S@HUC!C_DuhhxIw{ z6I{?$HOv112;0RQMIy^Qu}U1hwsyf_$#a&sMLuE{pbyQj+h^gw{3BYIhqb%k4#xg2 z@s^(2exIb=OMB)e^{%0HX+5}%OaOJ786``1m5fD-$O`#rbw6o;ikc6HKV!`r;_6sG zw7hxY+f5@>y0+D!x=7&DHGkd36nj?aNiEH^GEVLSByl!mca;hH8~zFtytaz)$8fVxeiEbm3X+?rv`5K<3aW3aXvA2cb2{CCWNhFn{{N(+# zz9`syP}Vg2-3;8>=oU7zX*yPwdv^@?ailjg`IFtOM{>_A$n4pS0?zHd{`7zxr|h*g z--o^@#r~hB>GwAfNFkQ?#tWCcc9+XLM8nBeJA(#&yoD;kfHmp=02(}Lbq(I1;jK#k z`7Uk#({Fccduwp5Zy%nLtV_A&Bw`qgAu*paAQWH;51f9_7FM1d@kBbQmtNBjizTh} zb6ONLTL_=b^J3hhJp7A>aybe&a#f=Eji_8|y7sj-z_haosl5#$*|708c*pz_bM~{g_zU}4M|FF9Yw|*Et$y9F%cf};mvO@?Sj9cw z)<_fWa;q$@8ICzbZ7Zy0-9E7WlfE45e;$8te;w;SGuPk1-ZIwo%iSR~*<~7S&xhc- zYg<_^Ao)Z^?d{CV2}xyQ>ltmxjPwD=e%%&Yr^RoBUKjA+!|5f}?PJsRO?D}6^%!jy z4LaJ+%T9>FXwEJpib!;;>Eno_GySPu{I`+f9^v~Ue#Ras{gJ*1cz@xSgKjJ}e;@ox z)F;=xO4@`I+3dBmnk#E^fuvtO8ffk%wE?44e{a+7VA3eYknKB z)bC`2OVMv-DK488ylWnT4VMbwU+sa?aX$7Lih4V285c9TgONYloW%M3EG2km#4*WN-!YZlNB2ghC=@GrqF zGsCiNlE=WF7|^vDyel4?XxHCmRgT(7ZljhK-)np3TM32A$rzOsmMJgV<&VNXB!1aH z5p++5z8Uck!)YPWw74#o(IN1Dk8gFU*usz?VJ@w7(y(TYL@t}5B1To+8lRi#eRJWT zi~Moo2(?Wn-^BNR9q_)X_88>Sv;{gv%xZ#JE#b6f7nf}u@p)@=D~3ZmubrK)esl2` z#$7+)E}`Qu2;D)X_{&7p)M^)NV`T*Q-XvsfUTMR`8ZV0cL!$VR^&4wle@L~tv$WNrwYicOiqaH< zFD@9Zt|fhuH=X8A{%*k1XUhCjYvP~T?h9Qb#Xc^v(Y3u+&fzs}FHo@l(2mv^p%+Vc zB#?lwG><6>EJl5%S!VNNb&ZE*@UKe|nbJWt_$FTyukoyUfBI|~^j)5Y@I z#c?a%Tf55{ibc0ro(UxLWQ=^$5+z#mPaSHSoWB>fynAIM4a_6Z(jR{-LCed7tNG<{=Aw9)jtJredySVWdLEp0WkSW790 zl`<)n9L*BS(#JUkHvm3JN7_H|MlT53=-xi~v#MIfcViB*;V6tYcN$vQIaFzP&Q(d> zzE_cQLh}(DR|Qw$j~{$klgCRwnyah$e@nBrg2MRyjwxdiz*=u5-Y|~n#pDU*WlH?A zN@FUc?Ee7xA$*qec)#N8uv^>PeWSw3AQ9a|GfJW6Rgr{wAdsW)voo;C|yJ&!W|re?Fe(+#u>A{dorkziB2 zg>B@LIl$VZAH}%!uYx{0Xi(l-Sb3UOnKz0x7M0^m8*5-}W{~+X#*yF|N&=Wg*jdRT zlZyNQ0QQE`Ep`6@2+T*zc{a#rRhR^oPSt431A+3F_Ra@$Uk~eR74O9@PfxuUQb7fx zUpAp-3x(6(Mqvz1DEVR;l166@$CUd@l*M=WZHiAn{@W;SV{hH173%j%seW5iN?&5*4aS}8#GH+%ZSSw*lkX^Yo^ai%q-X^h{ zGjBbbCerrTR}VW}Mi*+55=8PI5@OVnoOV%}unY>A=TB|HqH@7!e z7WVtb=#)hxN?9`Hl+2`V5fFB`KJTPGrjKX+lL3=g)**;o8#hU``&&RTN%GY^pu^~hOrx?r<6Ub|DMxpuNvex7emnrQo6{I3y$28^Dq#ij%GH+G5co)KW*uEgGM*ZH=(*7)c>HD}aD7Jq$V*_HD_y*2JGiV~f?)42#A(5oEv$Xqc+DtL* zZH7?+#2Ms?mIWD59OM({pAPj%d~IcCsA=|kmF<7XWw5hox52707&a)AJ{lZQOYA|!ZuM>OXRd#_ii`q zE7&tHu0iN|6>mkeX!qRhVdcqyquY`WNE{CRdiLp= z@F(_z*E~sQcc6HhA#38D01-4hztQgF18BI1JeZrV-r2@JVP2!*ja$S1A=9UG3PW=4 zqieZLq4}GkKQ~}<0OL5xuX8NP^2a-2>T*4Fll)WJoE5l}9XdYO5l5S4?)2AZ7pi^F zI=V&{+$crz)HzH^<&F+G0|(cS;;jz|{3X^rVFdTkY0^EavCOUrRU;()$AQzi{HwR0 z#kyC7^ecfBHkS5QP^(LKKGp~$`L`2{0D6=8b?3%){{V;|6F+FVnY8U{Wqia{s zrrcOCzqY!9GR-Wij}c#&B`_s2jC{W%V{b~w@b7|k9|YJl-*}qqUyYElZAD`;_2{NG zUO5Mxo_f_!7wY3z((RLY%uDjRD()XZ2ft5h^>Q9K$8hE{5u-NgT_;V+$}Kn2`o;OH zYP%mR#vT@B{AHI;Sn5%ZBF~npjAJ;oos(+nOQTJ_9**V~yWra$ZQ!!KnBCp!f9YG) zNN$V18e54WQpnGo5*b(kQy2hmz<&W;UFz~IHhK`Tg@1h_XjTUBMvb&>x{V@s`O^Va zj=~D=2X@}R)5Y<}1@5qF)vNY?SAUuQ zbl1Sup;lC6_IAIYU-Lvd-n*w=*$p?uaUT=>s$rFH8cUBYv5n6lc}@!9QdfN7gt&wP zTdpuKhT%^qN3yZBkN!NiY7#7xf}pELZglw{g(}(OgTdyb&CU1QZf`t2Z+U$2F_T%+ zZmtK~K|ss)<(UiHzTcH(nVvA-Rs<`#ms`LZUAC)bDq4%JGV*WU+eJxy$oBQvtDWU7MEf{s!u=KBr;f9 zUC%c|pDxxo?q(cvL?TuvadEfi;r0Ijg|`}PH=3TEYo}^jCdZQE^49d-!D%WT%qE6b zGsP~_5ASxcCp!t``xl9R7(uPr6Befxzyz|zA{4knLBjcNfVcbru>*{fL9a2_Y%Y8a z=ccXkw2=L3^>2vAL=G@B*i-__|E(a~ouioj~ z9jZSIrk!q7T1B=C*xe#~r7_Nn(W0I{Q{@>7@!?r^{n8(AXW>mIT^CZfvS*IoYgTB~ zAk1wZ1`oI@dmfo1HNjU4$#=Twcl%ehta^X!b(wUZi<01kli}{9Q*il|{F~JFIB)0D zzIgutgNd(oNIz-6hq2h(!6Y*HMl@%gqew)Jc{(6+%syN*9x;q^c9V~+ego)HXg0qW zBY=ia_&~KzRA4s|;PcP<`q#*x@NvX~_x6wQKA65zwfBSV!@H?$C)@4-<8T}kySXJ) zbSA!wE&l+KM16l&aYd($y6yTS;I!8p!}`iuS>5SZ_b^;MwwDTmDI;>q#zJwmLV|yH zkU zzE!$f;_u0~j!B<^i4-wNquQjgP!94jh2398{>b)| z=sK%ug|)kX5oj_G?43f!7K${ISi}i!ETMeV%r}Iem6<%dd=)ZcusuWd6X6fW*t~i1 zyGHR%oL(l?A=Y&hqWEI&&dSqG)~T^IMwXznC1kfxD@*o#8hEF-(P2N+kJ@2l0hQcneZkA zBO->|ynO!v`%zzL7uOBrU0Tb-y4IPb-OZ%WdvR-HtCJh3cv|w}#K{e&#fDgIH zF_OYGmDQ8%&xij26fU(*3gYhXPVv@<;cY37ocdOur_3XYC}eZz+qIlxceapPt{s*O zeT+~94$#H{@z?EB`$JFRZ;3t@wD_%MqxcHa%TklXI!3+WT@EYN)S1dV$#oH*YdJ@`zbSq&{?@ji6@Dh(YJMfuwV3pqO+!%r#nLpGjfS0WB$`*Q9tq;8%%tjV9CLez|vJbhBG((k+~ND?i#Kjox=s zWk2!na)^`HeWlDUEv%IgdG52Ld1r9Kf~w!ZPVS)Y zGy&=Qhr(@3#ddF~#{&U+60+9kb(Di?AiNPX?{HLr1z&t)O2kaLpJSF z$41lqLu+Yh*A7LttD@ao$7iWqv)aNWn#rwYc^Qdq@j)2iKW|Uj&imkH)sK(71*xxz z{0pusTHHsa>pyO`(XM9;EHN$X6LE88G-1uOlN6m%CW?Gd1dkvG%)bcyJEqB`-(O$8 zp`lM@b@tfhmKh~^!QbUX?q*DdMs0>NUBst$JiY5zS6S?=FSN^D1N&N3kj!AyFGa+5 z3Z-L?RdEm`?sv}CBp1Qiu$uL+0{Fw><>r-im&o(Squ5PnXl9X?=T^B`&yy6OB#Rxa z{{U#&GnF{|Fi|WmxXH!*IPr&yelGk@J_z`Cp*Er7zYuD1wUlP!DD3oTqjr-q%&rnU zcw>)q+%ttlj#p!~q@WIeNBxZadE*G6)#uP|p#IZ&ZXkyCKe9BC#^-#C3l-cMgrs?E zj4t9(0!LloKa4*SeivvjrCIo!S~_K%lBKPlvtqiHo}Q2UhK@0F8|+7nyiep19Bxy; zWqqab2gM%;zhEn9d@tgk7{~FC;$7{h{8d)A)^_(2>C=3&2$3(ru+!j;a7ntfzrA$_ zd{#k>b@FfREAel^pR`t;4!z;uhTa|UB>H8eTk6*KHulJM3B1&dyoqaVX=@9jw(Y{- z?-TRnDo_X1-|$W^+X6jSTlo?%haL;o+W!Ddu!~28JQl}Jd8CHqTfB@-!3#M507vs3 z?mXDQk&-DAPkPe+F$;|h{yR%0webC{GG1wZ9J9ND<#w@=ts+Tm3S0+1QjaVuBE;;g zwLm>H#(xwqz8_q8GHVYBcuP(Z!>QifUg^42R$5JqJj*0=+ZEHAM3X*QK!s-zT)dCD zV&0atzmNX_3VeOx-DG@2@U`cO^>`rr9nQCL;mK}oEcEMvA~oR1^jN&E}z5tY})ULW|LUI4IP|1Y7L15SDJ7|ST5j>Nupyvn6q1} zq1^(=%KrdLX`1b?$4T{#A6@XDh+@9HFzC11q@EqM{?5~{)#b#bK3Z6Fcvrqj<|u*G|8CXXP!g*w$uEHr1hM;FcYV zEErQ+=yN|(kz3_dCN{_{`OHQQ*Ti%=8Y>6bUqpEbw|?;2aOupUtY zOvfDR=w*#sz6RC)2akx_;%ZhJo|ocbbt0{;y|voKac?6<43G=EY2IJ5#?q{k8>g4& za^VDn?tpza`%M1H9whj4;k`@6kMP$=zSJ&_-S(lXXnLz^UK!LPh1y5^MwN3kuV)b( z5#&PRDPmZ_R}eh^00nCoo*)`FnWx*`U1(R6i1pn!PYvWWQcr5n1=pIJqKRW)y}SY; zk(l8b0QxJyf3>&9kAs?xhL7=EMDb^cv^^lpw$EDr)bRfRi8R8o$dW;$>QcNnl1t_R z5P|2J9aYfAz&?0S+J4L7&xw3Z;0+Jqhl}(t51$a}^IYiq#XTh0$mdSgtsIoo zuQv}Y&Pv%_i%}pj4eH<@H(YDix~7M5Ewr&(-RZkD`-QW#SRuDCIwWbr@&;V7P{(`7 z?7+Tj?yvYDzlT#m@z=(s339VX;fO7#^CD#0(fO(Y$zrdJmO{j?Ks}3m2da1?PXv5z z*0e7a+8b%3+o$S7QHnpcMFD8W zc=_6qI33Rw751ZleS2@hoQaBgFH`=UmM^F~q<(oD>*`ZUHL5f3m)quakApgXhLS9+N@V zVYAfq>o548&g^jwyjHL+@jeEz_y&E6BAUhlVq&$$2S`?^BUQvzVD>NMqW8iVTuWnd*LHxMgy2@mx2EjtZ>Q zU%RsJe*XY1&$Z2|{bLH8^p>A4r9BtM+J=|m+xr`ftIao6zp^@m$qaH7wzoj7<;dHk zQu8x89Bw{hsLd0o4*qdS2f zKX0!$gEaNmH4PoLQx2m#ECJKbiD89MMIJ!`i@b{@#HuJlO9QbFcRz?cLF4Z#L3v^{ zSgrh+tt{sTJw@fWo#VucOSW+ycS*wTV%czO^!$^a8HGA^CGIV4ecRPvvbL>teJp%U zOh2^4IV6{#`FZrzq46)kw)fg2S>0brHl1+OURi09F^<9^3bV;^aQr)vQSz-Q-UORS zK_qqZMc2cPYs8-y@3qS<3sZwfw6rg6tZTQA4b|PUFj%ex5*Y2Qw8mp9#O4T=Pn4!f ze=qGpWp$)@`pM&qP1LWfWU|ukW0Ox=;qd*Fs0JuFDED&79DgFXjwqbF1CO2G8~z38 z-xItu{{RUVjFvZd3x6i1e5rgcOqaDXZkO}JW-W#Mh2cIpWjPcyK0r*ntI;KJ+>|QpGA{Vn$J+v(l|9+ zCi5+&b;uy&|Qou*x^ zcSW25&cF!ZeqZN`-nG9L9}m2jjfL~h_K{<%sK!ZNy&XV5UVsev`F3EdI*roiOILSq zy34QPeI|L7r7A8pIb|oyqP_iY^|rr>v8y|Hy7TN)w*4B-)r@g~e%k7I&U%dWTpW>; z+Ko=#ypV!d8Bq8#D(=oc?-&^A)9Iev+v_c-%Az>X7{Cj;bF}by2Ou1Q&O0Aku)ZT# z^P3GAlbHe>`t$P+xaa&h#w$6(RFt7_d-}3opYV0m@1c`b!^SF_y8U{$=KlcTX=F=s zC9v5v@}nyfR6gUs19wbw$;YSV#dTSAbeWOV%mPM_A!SYO!4Nc^6D)AK`^;5FNdlws zA~uxdOR^xvm40yBeLw)?10%8H2cfE(^tLy@YeOB%L#M|l_RWmZJKRBR14lXXcx#Jn z299J}nzTIMZ@YJX zo$Q}8tLpdh=`%io_S;+CCFFw4>F51A=4C*wYJik*B+C_zO2}O$P_jAM98)86bu`rA#VE#Ikq(Fa4gi-yL3R zwq8}fui{-Tz%8rYHhfiTcBD6JG-X2FNTF2CDiJtU5~r99v-R)r;vH|{bXwPoG@UZ* z#X4QA7Aj|EON;wu-zWBNpo&>#ak0xg5wXFU83&s?e@r^62`N67w{(A-KFWBivX!rQ zcKcoL_nt%HFWHX&0K}S&_MfY1nsgRbUBwA2tKjpSD*QsyNnh0R zOnXc*qsmeo&yb;se5(A}+q>y6*>~estMF6e4!5uCu<1toRnunEwAt>2(cMcBkjRT0 zMe@fQi*xghzbu#%9f`ZW`{{V(3@du4;JV~$1WT~gxME6a8;x$V$QW)TZJTa6JJh3AWyjE5Kq;j(H9?|-9 z`yu$U)xGySTwnb5D-qEmqDCKGM2j8_lIT$;A?hg6%NV_k7$ijR1VIqdB08h@-icm@ zVbtg|2qT8ind|xfa-MUp>-qVdzhLjZ*4lf&*Sg>L>t1_7NJT7cG4J4=J};bxYn1es z48msA{EjcGZ+;1Svc8zEpD5&i^SjpRlD0uCo+gO(vu4k?Vr>uYhg zz+y@K`}7AI9!IiYOMPK(b}8$K@BP8d20kF#8wkex!W&arfVu4R%gTr;#(^sKTl&{7 zj$?jnKzw>oU=y1JL4MTfuR>j5ssxQ#o*)-zn*PV<%8?lr=o1>}ET!a|)?dQ~2#=TO zDr!{xt82q&U~-2RO5u3!rD(2Pqxd0b=BeLpZewfjZYQi*w{T1A(Ir@cPYw-J&_hHS zRBzC@p|!QSW7a`c9IP4FVc)&D9LRHZm-AGL!mmzZ3g=m}7eWH}g#3cYJ4WDv_HDO) zGh+GE6^Cw8rUF;or#vlptf`=$2sL^)`&@9%R-O!iKRhAF5e@i6@Px4TwM2Z*a!4$N zMX(#|VIS1Epv2}xj*s)+=4zXuqe~hZ(zkzrWTdkEQl!&GLIu9uQC|PvNdT)yDZ7Be zTe4kp>8Co;JeW4`r9aC9ML&fF`ReGwCC$!EMfLG*)`eN`fme17XWupZJ}35w5zv2A zO4Kl9pS)6Dx$}pc`0^AJ@O~Dwa8S1;KH0n;kx1_~PWFY!0?oYC?q3$6+?aR2)1_Uz zOG&W>=Zp;wp&j@aQNWNG4i&|V`IlI%EcQZ0>dQyjrZsb4BUPt0ZD`FUEe;)%zQ;GM zAsuhYoiVYdbJBTHDm^GcY{_DLTb6S8xy9=zQt>X}bo&U&=XbTi@0!4$MEbt2>v9VP zSX&d}ixV$0rVrr)w#AbW7u)oE;3J^Uf}l5hW77T!vpeoG;~vTI+u28ddUVB>E;qu* zGosJ)NZIe6SWQVQ6qHyvaljV<&Y=M`B&CMFG<84+=q?r8?|xxxW*JrhAY?talc7y~ z<4DEq6v%2_;qouIal>H!NZB-jU8N_Bjd9e2uN~2nCzJHGFqu0>OJ}W>!r0SL`5ELN zM=N@hWnW&j$o(Rm=BijWWpy))FIU1&N7Rhd94cq`VC7C0(ci?!aFN;>py$TuTiykE zWrBnI{F*WrXC0WCpKuJ*C#D_xdG?I+$|E3im4~DUzbe~R#R!H|O#d}$6O{hj8N>Gl zyuUn!q#LzAI_R`(AtV!c?My8Jo>6n1EOgFxgPn zUZZ&j%BJ8E-L`m7N^a#$o{!vd!VxMg$L2|ZK_CsbWw*Q0xcVk*;-4S!R@+DQpD9ZA z(LA^JyO#Ct8>-)5N8DL9hXn!EN=0(x0|MnW6A2wkw`$|dP_0q0korNJqe!#qSGRYl z{#RSL%Pv_f@+zOQaldQ5Y`s~XqZ0Gi_RXlI$CSc6+ongT_jHp_9*@TulEQ@x6x48; z)<0q4VpN~$Ojf=$UJL1=-YI-hE!RG4ejMm#kmR+fO3ePaAGB1qA^QpF_%NBk4I^>M z^1V4**^}4HmSopBbj$kGcnaO9_p0u>)ZvM9<;qoa8YP$b&aRB7tGW=wIv!1JB+~9@ zv95|%t%GkwO;)%Ac*fHMfM*JtKV+gsR@3=OCqynY*lt^chw|>joiUAF^4^PP2Q1n! zp;rOEWva8*x2v~P^$C(JEhMj?#i8W+G0IQ17-iwTyvdcvkXv%9YatwQw=!dWCmT+s z7bT53i9MgS0QBe5&Yo26&cXq;as??Urb6b~^Bwy@M^o~2Ip~JnnbMmzDBK<^8)?;- zF5h4LbG{{fo{+4!O6k|qkaYsn+{qYuVcn}HFjn{x*fZUitSkJUMZP$*7#F%=nX=o< z;+9!>Ad}}>|8;q=TxBBz!uxTM@7oALZFEx2??Q+_+IXhLTT3mEv5d7Y>bCQ>B3*J? zp3HP~mdbq`y04L$J1w|L$@WmpXNaO-yj$DCL> zkVUf8C9AR&$~WGT5oqg47m&Vja+eoAHVhG@Q4#hWi#f>lAHdlcKstz~r31xiaBG^8 z{T$r62Sznc+pTkzMZO77z()~BoL|+WYt^;5Mg1%Wkm(Hd?;nI94bbH%&X$=9A6*mN z2T%|1=9%+8%#g8=UYZ4!rer|=JPce4taOb?qFK>H^b??#Q&%InukKGX3 z?L_=WWGiIyH8E~WN+qlEr_6o!r)`^BQ3Z~dn{K38!H9zy;DNX_i~zd6Gn&7%Ly7tN z(Oi`j6I{pz+YezKNgnoSytV~F4iKlXzdopYJJI~1*` z4j|+|ztJ?>8Msg=O6EEJ^yFIf?w##pD$35uZ{b8JZ0Dg*2WOZu*6I^bw*+?C^A9@4 zW0<0utvLr5akcM>SJC~F$=Rs&N-UE(oomOsXhqYiu5&%ie;_1QnVlF=i`gx~7Jlx*2T`udo*%*D5Bc~@fUBs~Dd~j=j^Y~4ei7eeO z^;%Jt};l1vc>6P;w_Y8k%4%lwv4h9wzvF*LH2$&8r|Lb!D*Duo+-5Th`U6ZA8 z#^Lyi=gfq?!;?dOiwmF6+@&zzWc7Go>mo<#&Laz%Ro;~I5$|YP2mNE*_N_sjcZa@b zzxOT6!U-291{P_arrA1ccAzqSWFKGB>V#jsyt4a}z||}HC1jP$B0TNRzqo{)*I%R0 zt{aR)MA`}BCNfY6=<;r-h-vL#AhPjK=82Jfn>@{&`6%z{8Y(-x)Nq;3?v(7+QlE;X zREpGi6PMUFqmEt9T*uu-!Vo_$bNS#H$u)ZnJ&!f~3^5J>gMB5SDU?F6G#e9ixb~g< zd&F1PIioUOO0>BC`c=@|(3@RIJI8>KHxCyC9FXcfIwoJ#OxwdK)z4SwmqVJ08jPwTdbWpwEtwl$0uI8z&3i~7i{TR*CQ7pNg8V?v48w6+o-m- zQ}uEQdCRirz!&;DC$2L(&4bKC9B)=Y3T)NOFDnNJ*4T|;e9hBiNU7b>fhC>5hJjM4 zl;P8LqFc|nXV*OflnF29YjBG#B~h6_Lg!q0n^GX5DN>^IJ5tbtP?O|Z@x%+QF6s3k z5bw=4laj{VTBl`uA`;yP3_9Giu}d?l&~H3FpQ<=MSvdo+z@N=?&EQ{t@Y$Pk{T|r) z1brSM;iUo7oVt@mtnM@G4yKZg~uGj#BXz+s~17Vc5g45m_3wI+{ zCMhL0mZ6GOfNl29%lqT`o|!Cze&HNo>8C;~dJj&Q>sktj;Mrdb?QS-%#7FYey2v*y zjNjpI3J-Fr>gS4TbMH0=bIewzZ9$pzF4!`EJunSH>eb8N)lBQyJc>4w ze4JJ9zlv{+$w-T$vDYGPQTbb$6t1eIuCL9E)JPTZ>t4nJmeUqII+%$3f)J-gHQ(Lr zpCbJK5vjlB>PnGfHA&_^na&t zq<3BhFZR=7-U~SzKLl1QYGG6Z(yJXDn5Gs!}>?+|AU2muZ6@TzJj)l?0%C_Pp9< zF|iH*AJOkN0KoxcJcWRaztRD&&klMbSntuNun^57O ziV~||F65<_+?0>AFUe?_ZvROX4)W7n254F_4;{{IN-reb3IS+gF=H;q2Zh={ks=-W zQu8eDV~=FtsId-Quh5|s?~C)D{8L4`juuLj?V}wd{FLH1b5RpaggFdD!MOn9LOxe^Q$6r zag6$78U9oO{%*j0Nfl?v9ee0jp{2|F-u_u`x-@YX{bq`N&?>iclv?;EP$JR$%l z?6kpG_AOkof)57NRl7)6tpGsc^=uF+$PFkLLWtL$nyLEy+Z;Jwp+SOqQ^TYU z`9L-HUiF3}r>dvgf}hY+H`6PGQhg2sND!3(K(h;-+58H{RNEN`jo zvapP*f313w(5Wpr4um8&NMAsUMf<=iL@QnYvfG^L5?)tDew1^SJL9;%OcPl=iwa$u zV>&g;fvNYK)Mp}7+!Z#p3&1V~idX=-!&7W&Sev0&EJ&7ROs_UFR!4J^B z6f$2%Hf%p43E*+C$wv0V!Ph2h!98Y!AKTbvvMXHH$b8D6!dj)sy$mX!_YPgHO_1iS zC;T}_@mqd_aI%aP-kK9PDJ62a#zGqay8(z&dNx~mgy+M|&*)>m?J2feMOV6W?5lUQ zTt73^+PT(65CW6u=sqNsq^7nmf-g=myZu2LdcWb4zm{EYS!R7=m++e7Rm+3?+1A`c z=?xNB*uL+UU$>1K<;F zcjs%wQq{L_c_B3(eNnXubJep?*#IF%d?WqD%f&kDvHpjKnCXzj527U%7A_s!*vW{$ zkfIsHFrUA^ZNl@|m4I9wSwYw3v@et_N6&a=s((pni3=+C>iYM00I~N_oHGPUUM|xV zTm$+rgQ}tYH>^Y>fhiZl3T{oW);#8J3J}5f(Sq1sE|NlLfR<|4mV^G+%5c_1mDxk( zS|4h5-1LMl*fdwgHD2mgXMsx6>@~`@q4ZwVqqgK>anrW%b|fEJH^r7@jc~~r{p&s@ zD#R@cOB(4bqHIjE7VtEaV>tVb zH73}@fAc5C^S;mD$EriMED6fv+A5OU^eUe}5b$(EPve-^L9>g=**%yKr`#ivNU_+x zk%u#uH_DY3yWd+}=nOR4Svt-sPVh(pF;wLNi(Ms}ul#LjnB8N_eUN3bYtd!{_0p(} zd|0d&p&fC~r>tE=9ZIPi7=rln0xP>z6grplngHRpl1>s%cp(B)z9lCid@kk1`VRmP zO~fJzbnFTf7ZlNSRhIMl8Bko<*qHn1+qnE|>)gcHt=CQ+8G|WZsOBP2eUz%2o{3NYjx~3i^h_f&(v>wVfGyEyr0N^$2K>{~@mSdwMVCKKe|i z40EQ6>&9;CLKfz&EZb%`>ixu&@;o;T-1)XPX7rmPsiX-Bwjm`nr@|>7|>iyRk54UB^W6Z45Vz0WeYw3b$@-~4-jqJ3t9KIr4qByi@4DDq~ zIXS@S&KUm?`VQiKmY|(5NoT7E_4X~ZM?m1z{u(CfPX@&E0__ZRov9cyu|H$x6|H-m z7!k_wIQS0y8K$wP>aoj_^kt+CR%6K(Z~a|3DIi!qMPy5OK1I7e?wN(gPhGa!kwz7r z>-%;OLe(yBxO;mnA7LWn>!}1@VHQHGcvF-gg*br)+h+Lb+S~h#ANAJ1dns3&tJU_| zR3pt(p2Yi<$PvzhGYe+JGa)b$@f%dy{S7?o5xcrgQ{2~7XU^i2yq=c#;`-SdJ5+yh zr@uImST4H)JsoD7>vVOG6GAs<^e)_TWb0xkqNevFa0hox3+vaUPDd#Ur-damdE*cs z0fWI;bgoG?^BwiIgbymz+XVAPALiflA$4fpz+^Qj)PnDXgGU%bpR1E(6T+=}?>_Mx z-!};(71%~R{lTEIy!+vBiBm3P4j-m6{S+mWPn~0vPQ`{9R!n^z1K&w`arS!t`mF#W znnp&0*YhI`G}tZypv1qNf(w+mJi;ZaVcSFZIRG%UuF|z}O>>0MXO_b7gg4r}eK!@f zs%g=bk*tUCyOG*BgH2?aHZ{4+g}h)Z%va%${ah}&zJPN<0%e`@lKqzzAWaJ++Kq^R z+obLfo);UDiiLm>)wAnq2mpaE=Yo3W8))jAeM2#E%Gi|bW^D&TnCbOzfkblcB%?F4 zp1*sbX#1A%KPT*T{7j|k*0mGaAzA9t9I4hfKSZStp1?0UqJ}>te^I~}@;mS`C65fY z5e2v(b^HDb9`AVSPO0ns19s4LVeEOZ>{noK| zCt*FK0lvynqCS&U)Kq&JbSVCx9IFn^nfA~s&S+D&=w1h>rNXzT_Z+qmfX^2f!2&UJ zcd)rBsitAPS^Ie)pEb1vyey3WrY<4oUL}z6p;zgJ;*_4Bzl8vpG9(lkE zeH9_y`2I#li?4RWS9iLW>;D1%Bf5X>aiLfwRg8N!++%Vy~HO;OJEV3mf`%=LQ)w*(S;kPuV~4Kn30GrZ-kvscV**6 z5@s@5I^MS1e_1Tz@3`Lph-B|~P=O9Y^2$KCQBUO=sne2#jB>2kU!u+MdW={cR|3;y zXd84JdLQnTO3X#!@$uo^U8jM7LEgEYO!$FZhXvq#Z8d+>x<>R{^3tR`!xyIo(L^Sj zV0}$4&3mced#7ppW(yx@N%)(w{+oF+hM_wl?*ZQjZSB?7jyB4$6Z(zi8^#qMmNjhCf ztQxM2z3mTktx-xk*dBUe9=7x@1tt(aXodz~xZ^S%8bc?$I;0V2vp*q0(sOYU?`lBHs;*PTtn*Z0A|#@OCkYq~>@+uKh@obMwe;~wa2VG)V2 zo1CLI@AWbfMZC&-9zeA8S2qc&)%PYe| zM?9%{Yf2_W)3kRS1URY1@)FUJQJghdigklKYJ78x!hG2uehjne7<#rmt2ti0o3mfw zp(_2CZ!P+L?lOZFCG3icYT^K$z3rzIc?n}4-WzmNaD=cRG8!uAIRMy0Px$t_-#~0(1DIvHAomFC8H@19O{RUc^AG?eheuxF%-G%u{jm`

uJXevDIm(#1`7|v#AD1u4K{f5kGzM5MN0O1pxf1;b}DQKp9%wLX|{~M z5*2TG32HrRKJ&foN_+$>=$2(Pe(RRZYhg3L$^T;dD)`;#Y`W|{v(dW5^bN~DLbbCb zmwnXaR>M+sA?}%nRrRcXrX=m>#zv<|q1V4cU>kBQvdD;=K366Qcn8vZ15jT%Pbjn! zlMM&D0TCy6E5+-v+kRKo)IT?4!d_$V$+Mjf13m)-f!Q*V8NL3M6J+;(@;ztX{Ew(o ztIC-CRR<$uJhSP4M7{)b3`VXa0K}{mV5n!cVM&OSdW2)}E@+H^zovyMTyr8kHDK$* zwjEmZ16EazrZiG>OF+=kj3L8@@DfP-4gX z`)iLNk-r@c$c0&PABDeX%6mNv4>0Et9+VhM6I-!U^y9h_naff{*=W>X8- zqk3hfyB)&QeAQSsLe&>>ynOPysiQAkrmU9P@b?kwoi_a^t?TS9>LmRp(~c*AC=NF5 z*tu(+`InAF>Q$@{o+aOta6?9{6mo>|HpsgjSQ6v1JvRjT8gPM|_O+qt%JO^`@KbQJ zXrb>_+LvQFq7-#9&f`->;|pKh%D)2;6w^CsL#?E-XgkzPILh`2Oii?Uebv0~ZT_zL zm-S8t&b}mlZO2_$orC37q5FPp)=oM1oT%6XquTyF1X7p1Hk!$cAugu#Yxll{-0&70 zyz#t3_&l10KY3fh!R{p@Wh_p7aE$HYx8UMNAFHL3&4}uSH;~SUaDohL0WFjX_I~}V z#KB{A0@bwbOKpl{!F1y8_rt*Xgvz>}2(^$triD7p8=qE`L(_`9#DOtH$VB^Y=KcG@nT?&ahi$f0<3o0c zoFz+f@}aDEI*9il(k4NFAi)Rgr2UF2JuDS@cp)(kv!TmqncU7LWSkbw-DiC=a_Szz zVMt)b$>AXO>*JXM+}ll~DYTM(lZBGBl7rU6V0b7g;8lS1InGu3gJ*5=?e47xWsY3C zUB}<;%iO7JYW(Y3TyzoKQfBn2t~2J$KSkIzD%G>p z#L6j{qOL9X6gf#wlHb{E8kdl8mqhpYZrRvj6pvL})XYt!5;1HL%^#0VG{kCz`mT|g zt-|GJMewUycL#;b&T=Z6Qcror4~Y&e!wB-X&`k````QGhrW{s@u%^CXFDd}9WoVu} zysmvcR?7M6UKyU0s}*bYYSRcN_Ce|IMPv3KkAZjjv6oh@2V03FgiP3r%Xhkb$Ch~J z@QGBuqEC@z#v`94%9y^}Oev8mv=!^esLyj46n-t1m&tP)urPvb*UVt~H{9+I^*kzn zBzkp6eT7tS^wZ_vNIZ@9rg~z@sJm?dnk;>T`l(aNtUEj3>EaQGR~H|LjU*LX<7=hW``krAalB06Pe%0n+E;a}0kL&{b51-?eX7(za{89fg2$AEdX5mJC|&A1 zdSh#f>7OX6LaH|E%T)Obr>-!!?FO(RKmLjI`uNhu&Vi}x?nxX0kbgvv4aY z2GlRFI-dbruTU|p$1=?IuVq4iAZk_#qwoSa)EDff04Dq1s9Wnb63djxa)yI+$yXir z{=&(E7V5=lae2MDPm~_c5n^A6!J!l2%pGmmSVJ(xyzZs9up4*uMQye9a|L?`G@kF8 z6z)Aul@R+ASJqUSs?XA+t*bT@CVVrZ749!)&?9^^i~?9k-Dj$h*%vbN!GdsR1g5dK3_xzqE`O;%TfRN#Fk3+3b}XyTcV?5WCCU@} zoJZ1~1lXWyVbhexgG)HLB`d&QZ1$X%w{(6w_65;g*_wnPR@I?LJRWOHBeyT9>BD&( z6@uLuqRG$lj4JR|$F~3a#dfSdNOe$$bZg@!H#@_Y8^iIhbDw4OXDR-js6l5RKz^Pd zP=ceN%(nLFg693toJ9?@bhmmiKin)eI$%{7bvVd1cwYqF$+*XO$-!A2DxxMeN#pdJ z*U0#>g>#liJ6>th ztZr^6E0B`5_UdV}V(pQS{}9*hTd7XCwO}Vh=XzEiA|v{wK1g1S1_mGyoa?^wr?=E^ z-yoJNDr#9FcYGMhAN~=^CZ|)-XjQV+G_*)8GrA>sX#Z!{6#dt$2E-x(6!vqK=go*k zHT=w8i*eZbmCxvC*`AwZNQcv#>iQTh85OqFA&;SCb$0$M0tWR@@ST}_pGZSQ-e7}R z@qR8K_WC7ov0hVml(03V%Uo9NXl-ltb*9c*j!qoU%%^@lz`;u3n6uC`-W<&Q&H!h>UsN zvT(d1aqMx*^Q4|v&|SRgcr$Dq@W0le_1_q)v5xcL0w)0DAzmD-(VcF|(F$23)C4^r z`il^>@&PIJhzqFCqZReT+*iO_UN6a~jLqOrw1b&&){APwy?w%|U-n#P2`qIYtgENX zb>nZtD?y$=om?)BHjZ-0l+5})*=T?|G9A5?U(RgLl;m;kgsIBADeVvp%6R=agwjWu zil=aGX_=~jAkE%UX*^N*%^(Ef_Hh3w^z_$v_+g@3&LEzqY7_XuZ?Oulbh5QVo}y?I zsMh8{Gsu)wlqnG!f&M0UC+^!LZE>Xv@Rij{?*V;hX3(E=z~kUqV?ez_vp=LH{lzJ-}50rJ(($p53aKziJcZ5urFe_MJ=fjIXv7ecC}uR`A4KLnHQcjuM| z$&hp<%6a$qbGg&gFGk9fM^yKz+UJ-0Xl{NmHiT->)QQ5WH$a2VA!zvy5?2RI(DpyI zz%8d|NEL6W^L0h#HsMIbMtSv{_Kc^Gz0hBc`2Y2SSaxnIKD||@U6ji5DdK9AU!4ZI zyQY$Jf7EO^5FOg0;ugi^Ti8-$Ye}`K5GFMWH@A7@6{P(ROC4U2edL|FNw>;$qLFtW z86FLte>VL?q0LS>;Em=(FY5pMyQIXQjb=z_M_;##z01Bo#(v%!QvPSLUO}G9MF-Ys*-Q_6a1}n3HVQlBgMb*I!pTa65){n zO;c63o@bOJ0GAVzj(ChQo~C4DK6~Rl!=x3{kBST+iEqfKK&@y8)r#@z*7|)?xlhjD zw;U6mZ{MrY(n^_!;{)|uRpU0MDQ}1mmH1}e{ytI7D%!9Jm@!vX&F*itZi|X@^)I8( zdf5u3`=6ph>3?d9$spNYLJM~aauVn_y|*k6g@9uN$6m5#ay&^}9irkAm+vHL7Vw+1 zQ6f~Mq0~47O^jnq@Y7|d-=8BuB*;mhoo!xkk4YtrEg3NBc@Jqa>>Am4XUu}Iz}SP8 z03?l(`OO%S_Rmwj7qPL>SErQGpVeO_5ml3zEQ|?1Grmj|a=c8KJa@;_8C*~i`m6#s zLZVwPmskdlRtkD|HC1D*qwmiqJ+YfO5l*=y=T^=@kzD@m$59oC-`l3l zz$Z)JLjI~?0nfJq)$W})Zt;!C53()UTQ2%rR8-M+L@!f4=>JrM&8hI|kDnCuL_|as z1TE|HCE+%|&sPvK!o5P`>5Ypt))RjK33$N1jGrMrfrK`s-w>n!h`O4U2NH20-?e{K zE^D~Q0=OwPCp!Qp_n`BfWv&w=3c?oL(yjX6hM6_qk__KUy?sH77ic-TxnJ*&r%c5= z{l6~s<_@7R^|+P5OsW9?M|79Kpk^V^W*0*KNSB4Io9At+(G&fjFHiLUhW8tUo3Jo(MI~uIYX(m(aLoDmK;+db>-5O2kgpv zO-%?yePd2R#5~h(GEah({@owH!VL*iN&HETAp_rj6LfG6m3!~Hcdhr=d++-@XRY5_zkPmdf7afo>~s0^at(0vu@+bhKte(S2)KFx zmx}-mz%>$*f9<~oBm@4_by89w8ToZ`@_(IzlA4l&f{KEioazP@6*bM3$SG;*ZqU&F zd;j;7f5ZQcx@t5OUJOBU**_F5d9R6>S zTmzDlUB7aPiu%f+`R0}TK;X42XURxOudG9_z5__<$r$(~)vq%e+LGV)Vv>qTEu`Ro zSl`WTG>HL8+j+w&scx~bvat&Y3f;LYEF&u?ub_Be|$CSpP%ze{j)Xab3HL z0V(-EE|P2hR~<-CO2#L7ok87@+}4Znwp0WK)5Fxl`ff^oX(J4?o%bZwEs)H*0QMi+ zf0F&*fx-X3$o>cHf4LR_G(eK8-~s6YssOy1J`r)jw%|H$53L0YbVhoK=T({`B0!^~ z^soEmS#2sf?5;n19U2Mou)Cpu6SmYv1si6bezN24Sv#3I`99Az^vJ}Z0ZsgPpGV^b zXa~lFoH|YB4wUmxyp4-AZYOyrRrWilbm{4Il-TC`(#-ff^8`(p23BJ562KVHg8B_c zOuPFIZ8@hsYv(PTXU#IT+oLCWK9cz~V|&zV*y!QQdz`8H37jV~$M~QS z_UQ9rJ)^r$OH88ojmMGs_8d$H_};RB?T;XsVSp8yIXh~zu@S2s@mjIDic)2UW-gCe z0AZ>sa$`&0d!uFZ<=7%?*06=r8(bq+zR&OP`P*ui-XO^-6vLOaek!m%l@;-ml8$zo zsh~OSkp)_m^BO)Q>aQ!(_GzMkX;V=;LVI@DcXvVe@aHi)1?6lpS&QbY(pB<3?p6uU}N%~vNN6hu258}KmGK^ zGvqPVVVmR-eddMrn@2)he{#GR<+9v}nRuRM58NDThZhq66V`7pxM*Rq;2-$e%J^A5;ydGFdhV8oguBP>G{5h2BL?RA^R(9w zBFBgE$k8bcXsP`dX()vs%pdw#8EW6tl zgK0l@FGz@sC^~5HjI9d2(`W4__2u;yrt3W~XsJOYit)AKOxE5D_1te1ZFsSYgKzP6 za@9}2cjyTIWtVsW!YeK5v1<#jKli!I;|KZ?f0l`B@~D;Nz)O(1E_jPI?@Tul)V+|) z!`CbWkA(MKN{x_-Q-vvYJxgCuY)Z8 z`Am~y&6C}sT;HV8>`W7{^gA3W#R^nfl38TxLRLMiH(m#;(ztwHULLHl(S$JQ`FfRo zZM1GxW7h_Tk9q5GI^_DL)Xp6bj`);*e!(bM$$4{6Jr@71Olyt?5(i{Fl?PbiZFg0o9Ja(b3GK zOYwfD$yQ&cI%dAiW6fg5+XD^0RY;T4Fw9Lgl2mO_CC=0k*#y_Fc7feN^4tuu zuAOYS1oUWztRc1c#vUEXfoK4Lw>^@)l>^H|>}{8T=mPRBTn=8(d_tx>G0NDSU`jJ7FhYpULY2>0K$7FO@!mrq6)x-m#Us_jgoUJer`Z#9J zKk5i6T%gIxVx?3)>NkAsodZeja z<>yPls8F}fM;`6(os@E!>(qxl%kptfn5258pSxM@<_JChz9|h)?4y1ykPZ{P8po^2 z5f0M(NaVsl$5|MopT>st>3Pv6L+++NgYBc@O%wBjBq7DW?!#1~gnDe`&4^hc^6LZB zlTBwWX^TUNjGlDT>-?f6e1TRzDhk~yZ$eGAGuNbP&RH`0ZHx>zCtdDz)QHX|3f4H~ zwg+)%Gp{$&R7D*f+^#t7du+YX(K@nhQdb=O>kMK%sOpp_lFay=B^DL}3tC={he3EDru1k96;u0!Kj6gs9SVKZEbO;yxwQGA=2;$|@vxSrAe6Os1b&&Fei5V7*_y=WQRC1I zrpdyLZ*@~1&xe%tHt84^B^so?v#`FkAQi@a30SM(VRpd9;PvKlPUrfK$aL`BUX3M6 zC-uPn&<;N0<;8?8Di9A2T=_Y$m~shlF0NcE=g|ooT)hwNt&8rWRklLYF$EMY>NVD5 z%@&*W?0#Ek=xxkD*X7K9OEcoQ5Yo@)yWB#m&K{j5v!m+xvyHq4jo1`H{ZDL9^RDf0ZNq{6-^sTIQ>(O%|7eE(!~-&vA}(nZ0@I} zg}X;GjHB%pU?~jHJi~U&Vz?UD1jc2ep*@@P$)BlWhV9)^-B-UvCJ_n zFE2f+7eg1xtk`@h5s~3jd(z&w=LLTQJY~_vnw@3$<10GnA-O1@2t0!XD> z3Z!HXwhY3f1eLE{Csp} z?HH%PT@~YOmOB--_Qx@gX52W=!m$oFxH^A@X!zcKD(J;2?y;r6@>$6+AueGsjoA*m zUiXZcjB+V^ zN@dff$X3l@Evhm0EyL(P_Y!V4kWYZYYw($0tC+{ox9I`XecTFGmB{VJA7W6>HxvR4 zF|DT53F}eK-P^z|1*%qHa`;ZavrknpzL8MyD^S^%O;>c&X07YUA!vKw#^G;F|ImJB z%AX)mGCXjt5ukVpfGE`2VP22RcT3RezMs-4i)Bpk@Rm;S2aR}&42QkS+4uo_uzRl> z<~&ch3yN>1W@FMk{ybQy<#q;`t071x3F27RX43s;8yixGRbNvijD>MksIq?mCQGoO zvDEuu;;!i>K>D1ZC5TsIu)?v{m88B33~`c?CnZDD4Ski?#CZ3fOu7!ihcF~ zhYL5$`XwN4qVQ_*)(2(BGyN^A5BB5or;{BYm79{wBz0SOL5$j966}zecOE!v$z@5d z^}REzzlSXwI?Bm37!jYh^X4pCXEXeU0+JwDt-0AVnXPBWU;A7FrW|1EnbR3}rmyDx zUmnkZ0>j3cNm=F@S(l~`j^MV7B{{9hhDNT2Kf2sMOpj^%QU)ICebLe_N73!apcG}h z7IT9Kl6Y{r0U7%D0PY!!5SA?6e7cOcMxx=YH=iX$vk_rGE)?Xl*WR0%Vu)p>USU3D z{hLi{p_OHcFOT^cM(Q|A-*|tOuehcv;UY>+8Fu>;;6g1wa|w7gs`bip*{!*x4GfB~ zljv0P;x*3CVv&=^B1qemX(8WLGLGx0H2Cwzy-}zY)VIT@*c(mjy5KEht^n_GdVc7P zkOyuB0$NP2EW=0KW#%pa4mEy1c_=EQm9O-(gH z4s*S17Q5J6XAw|X8qk-xDLOUGI+CLDLdA(nYWbe0G4oy+=E>qM%<7;7)!5!J^v3&H zgx`?&ZOV(%t0Ynk+13>qxYuyoQKvO|Gz01QbigFlA=!OUr2|84ZnYYr zLY3o?tC^=>?>SxfUIYkb8|{QSoqBBNo*Ydi{^f?*Vv09aad%1mZ>^G_I@TwxSXGtT zdfo5dT5hs!uuo_N#B~j*i8aEO^3j8;YYE&EN1aju?HpN-&9hB;U@^5+j(5(jQ5j^a z64_i^`{kVJ2}k;qT^OUV=v7x7UB!!R_T7 zvb-h{X^Gft#P$r#9&ET;ZhnK;BZs}19N3&ZDSuXPrf31|cy1hvIB%-nSbwULU-cBZ z0Df9x6=Y#iwp#ux$l4yBQK?NAV?C7{33gpb6VUGaBbgolH*|6vw^*Co3$fc2MTmra_PS3e;f652` zPT}(UL=B%Syj~XOx+g6-wRsI{`&IckGF+#dmqD!_8;=rsfm3&cgKJfmX~5ppnRt~+ z(0E$_70n!DE%B$MxX*e0Nj@;|C6*JM?3vq?n<2ZJISj5n_k)fapB%CUSV{MI-qNM| zvG5v&Hq(z9*X)t{RozyEJXtQ@d=cH={|gsbE;g1$I=oR-_{Iky*U-R@`@gxD!;&^U!Dx}U4%xkBkK8L7i(G$o zsM$6-IFN)Z4g@DDOuoi0b)l|S3=7-}J7u??|BKuQ&x1#IRM>X3d(SPmnmWaE&sO*6 za--4qphhbZ(44%~o4CLQ2|3~P)0VTWu@oz~ z>Hef+tJ)_WQ3QMS*(IQ3l|#0O`DJy|>a(!!2X06uQ~^AQE>;3*Q}e13R|A@c^_K7K zsOEvDTNYn!r#hHKWzmc93ujvS@jYiYXuSkrjG^%tK%CH;RlJR{5fX`gBwf6dIyE5fs`btVy`JLz060kj^-z&b8*J^N?PKST)+nC^1uv{b#U zPY2m4=jTLLA#@f1V@!AWH}vO)P@Q=gG>SVCg39R}FUDo-ASde%%3@2Bc$#(jas%lD z)6c!{aWS=g-K$`$I@Qp{2xem$*96`pV)or-XPrZX@cQ*LV*7`u+O7V`z%=vi?jyo{ zEw$pG&G3Ek#f5NX-LtOQWwdY!23||6hKsS4e^eJ7Q0J%z>zKItO+n(6z*U4gp_OoqKFMuu(o1-46LVoZg}JFMmEf~1S;b%z2vO$+g(ro_p>!ru6H^hB*xd13c+^ky9| zu|ftctICGs=re6O4oy1hn;-Fuab|O|Pubt#6^K4ldiv`tn^JNc!7XdiaB7&<~b{EQ%3XBbozXg z{yz8p+>-bq2jjP4ZCOjS8$#S`d-Qf+2(LSInsV6RPt zBqH!PrD>F2?t0RMtB-~h*Kg4H#1XG66u2kni($&^i0u<-)~PeY#1Cm%1%b0+-O3yq zm^lLD#i4$hz>ku4Z}FnssRy6$5Wl8$grnSSq*`W&O*14$z?`ZC=1&hK*6RpN$r{+K zHi&)Tx &->PV&C2Fjn&^a6Emg0lb{aDrB?khSd&qUIW&eFY5g{Sg)aKq4DBI07 z;_~bBRkaV55d;q>8fNJo4ZT?(GBreS+g?}ws0!(t@9Mn-G{-~~VxVtpJj6`uwvq<> z3>IWsE^c-_imKfMA!AuZ832ya%Ab$bEezjj(QX&PUhJ%h#(aIwiH%Aej6V5dY@7=$ zd}Jn^qQUWaE+}36QQRM$1mT)ZK1QXg&xw#z;02{D#^b#25>P(~_ucni!MIwMrZ!R5 zEH=Se-xJ2A4#t=r5@&yE)UQ?y$U6+c)LSkAaFsB3+~OXFpgC!_{;id z|L{&t7B*Ss#k;X7!00h+Ij`F@3)RO(b2Ty(kqp)@407oo12}q3+rwh47ZX)hw)FY! zmj6%~TFSIIt14f)zJ z>RxXBa!?t{u6b3jGf~00raMGJ=ke_0mjKpCO!@ihvCt0JQ}orIYH&U@F~|JJ1@9x% zo>?9(UV_&@YiTCk_fe&n?Od`N%2OY6c1}IoUjS!+)IleoN}ow5zBp6G8I6UsK*?Q5 zs|eNQdj=SFP~=`!%oqFx?+AY8&&{lc0e4#dAb58HTwoaUqzD zGvnq;30a_xTW)KJzXZfqCDXQ>a9R33*bbd`2@<#8kwkjM_P?}^s3IF>%A84earbwJ zId4l0(j}R8xue z7oCap+lu$2zjhczbHu?F(PM0+;&eM$oaVZ`&G>p4Z6i13eK+Mx0J=FRblpax`@_A- ztGqo~ydkt-zf?2$bAO6XS-8*uueO|!?_8z_$#n4UReb8JSSqr-uvljr^2P17Q<`Ce z6=eGtOzINQtqK#@Ta#E|)cDfc38$0VN5lWv5oZ#LsGFmf#J^W&Eq#i~^sIuLn@z5= zb-CMUoPmjzeDS8uF{W2Dk`eEHgB@!NsX+*jA)>JLY=r0-@3nVX(%e!}Vc^bHRn^YC ztu?}SuIs8^^6Av+1-4EbFOOf1A>6!}yg0l{PXJ*J1KkhC^se9TnfoAvW&ZZQ&SIqH z*V$8#nwJ*F!!8=>Uvg&G5!&w}0*6h=M*O>OsjhvzU?7$td1?aWdya<5BTk62HBK`J z9}tgbDk($S2po?2jqS`$#EzR9nTeLf zq*~ZGo OpenCV 2.0 + + *Author:* |Author_ElenaG| + You will see how to use the IPP Async with OpenCV. + + =============== ====================================================== + + .. |IPPIma| image:: images/How_To_Use_IPPA.jpg + :height: 90pt + :width: 90pt + .. |Author_ElenaG| unicode:: Elena U+0020 Gvozdeva + =============== ====================================================== .. raw:: latex \pagebreak @@ -219,3 +239,4 @@ Here you will learn the about the basic building blocks of the library. A must r ../discrete_fourier_transform/discrete_fourier_transform ../file_input_output_with_xml_yml/file_input_output_with_xml_yml ../interoperability_with_OpenCV_1/interoperability_with_OpenCV_1 + ../how_to_use_ippa_conversion/how_to_use_ippa_conversion diff --git a/modules/core/doc/core.rst b/modules/core/doc/core.rst index 73bfc35..669fddd 100644 --- a/modules/core/doc/core.rst +++ b/modules/core/doc/core.rst @@ -16,3 +16,4 @@ core. The Core Functionality clustering utility_and_system_functions_and_macros opengl_interop + ipp_async_converters diff --git a/modules/core/doc/ipp_async_converters.rst b/modules/core/doc/ipp_async_converters.rst new file mode 100644 index 0000000..6fac234 --- /dev/null +++ b/modules/core/doc/ipp_async_converters.rst @@ -0,0 +1,62 @@ +Intel® IPP Asynchronous C/C++ Converters +======================================== + +.. highlight:: cpp + +General Information +------------------- + +This section describes conversion between OpenCV and `Intel® IPP Asynchronous C/C++ `_ library. +`Getting Started Guide `_ help you to install the library, configure header and library build paths. + +hpp::getHpp +----------- +Create ``hppiMatrix`` from ``Mat``. + +.. ocv:function:: Ptr hpp::getHpp(const Mat& src) + + :param src: input matrix. + +This function allocates and initializes the ``hppiMatrix`` that has the same size and type as input matrix, returns the ``Ptr``. +Supports ``CV_8U``, ``CV_16U``, ``CV_16S``, ``CV_32S``, ``CV_32F``, ``CV_64F``. + +.. note:: The ``hppiMatrix`` pointer to the image buffer in system memory refers to the ``src.data``. Control the lifetime of the matrix and don't change its data, if there is no special need. +.. seealso:: :ref:`howToUseIPPAconversion`, :ocv:func:`hpp::getMat` + + +hpp::getMat +----------- +Create ``Mat`` from ``hppiMatrix``. + +.. ocv:function:: Mat hpp::getMat(hppiMatrix* src, hppAccel accel, int cn) + + :param src: input hppiMatrix. + + :param accel: accelerator instance. + + :param cn: number of channels. + +This function allocates and initializes the ``Mat`` that has the same size and type as input matrix. +Supports ``CV_8U``, ``CV_16U``, ``CV_16S``, ``CV_32S``, ``CV_32F``, ``CV_64F``. + +.. seealso:: :ref:`howToUseIPPAconversion`, :ocv:func:`hpp::copyHppToMat`, :ocv:func:`hpp::getHpp`. + + +hpp::copyHppToMat +----------------- +Convert ``hppiMatrix`` to ``Mat``. + +.. ocv:function:: void hpp::copyHppToMat(hppiMatrix* src, Mat& dst, hppAccel accel, int cn) + + :param src: input hppiMatrix. + + :param dst: output matrix. + + :param accel: accelerator instance. + + :param cn: number of channels. + +This function allocates and initializes new matrix (if needed) that has the same size and type as input matrix. +Supports ``CV_8U``, ``CV_16U``, ``CV_16S``, ``CV_32S``, ``CV_32F``, ``CV_64F``. + +.. seealso:: :ref:`howToUseIPPAconversion`, :ocv:func:`hpp::getMat`, :ocv:func:`hpp::getHpp`. \ No newline at end of file diff --git a/modules/core/include/opencv2/core/ippasync.hpp b/modules/core/include/opencv2/core/ippasync.hpp new file mode 100644 index 0000000..b17a803 --- /dev/null +++ b/modules/core/include/opencv2/core/ippasync.hpp @@ -0,0 +1,92 @@ +#ifndef __OPENCV_CORE_IPPASYNC_HPP__ +#define __OPENCV_CORE_IPPASYNC_HPP__ + +#include "opencv2/core.hpp" +#include +#include + +namespace cv +{ + void DefaultDeleter::operator () (hppiMatrix* p) const + { + hppiFreeMatrix(p); + } + +namespace hpp +{ + //convert OpenCV data type to hppDataType + inline int toHppType(const int cvType) + { + int depth = CV_MAT_DEPTH(cvType); + int hppType = depth == CV_8U ? HPP_DATA_TYPE_8U : + depth == CV_16U ? HPP_DATA_TYPE_16U : + depth == CV_16S ? HPP_DATA_TYPE_16S : + depth == CV_32S ? HPP_DATA_TYPE_32S : + depth == CV_32F ? HPP_DATA_TYPE_32F : + depth == CV_64F ? HPP_DATA_TYPE_64F : -1; + CV_Assert( hppType >= 0 ); + return hppType; + } + + //convert hppDataType to OpenCV data type + inline int toCvType(const int hppType) + { + int cvType = hppType == HPP_DATA_TYPE_8U ? CV_8U : + hppType == HPP_DATA_TYPE_16U ? CV_16U : + hppType == HPP_DATA_TYPE_16S ? CV_16S : + hppType == HPP_DATA_TYPE_32S ? CV_32S : + hppType == HPP_DATA_TYPE_32F ? CV_32F : + hppType == HPP_DATA_TYPE_64F ? CV_64F : -1; + CV_Assert( cvType >= 0 ); + return cvType; + } + + inline void copyHppToMat(hppiMatrix* src, Mat& dst, hppAccel accel, int cn) + { + hppDataType type; + hpp32u width, height; + hppStatus sts; + + CV_Assert(src!=NULL); + + sts = hppiInquireMatrix(src, &type, &width, &height); + + CV_Assert( sts == HPP_STATUS_NO_ERROR); + + int matType = CV_MAKETYPE(toCvType(type), cn); + + CV_Assert(width%cn == 0); + + width /= cn; + + dst.create((int)height, (int)width, (int)matType); + + size_t newSize = (size_t)(height*(hpp32u)(dst.step)); + + sts = hppiGetMatrixData(accel,src,(hpp32u)(dst.step),dst.data,&newSize); + + CV_Assert( sts == HPP_STATUS_NO_ERROR); + } + + //create cv::Mat from hppiMatrix + inline Mat getMat(hppiMatrix* src, hppAccel accel, int cn) + { + Mat dst; + copyHppToMat(src, dst, accel, cn); + return dst; + } + + //create hppiMatrix from cv::Mat + inline Ptr getHpp(const Mat& src) + { + int htype = toHppType(src.type()); + int cn = src.channels(); + + CV_Assert(src.data); + hppiMatrix *dst = hppiCreateMatrix(htype, src.cols*cn, src.rows, src.data, (hpp32s)(src.step)); + + return Ptr(dst); + } +}} + +#endif \ No newline at end of file diff --git a/modules/core/test/test_ippasync.cpp b/modules/core/test/test_ippasync.cpp new file mode 100644 index 0000000..61e407a --- /dev/null +++ b/modules/core/test/test_ippasync.cpp @@ -0,0 +1,116 @@ +#include "test_precomp.hpp" +#include "opencv2/ts/ocl_test.hpp" + +#include "opencv2/core/ippasync.hpp" + +using namespace cv; +using namespace std; +using namespace cvtest; + +namespace cvtest { +namespace ocl { + +PARAM_TEST_CASE(IPPAsync, MatDepth, Channels, hppAccelType) +{ + int type; + int cn; + int depth; + hppAccelType accelType; + + Mat matrix, result; + Ptr hppMat; + hppAccel accel; + hppiVirtualMatrix * virtMatrix; + hppStatus sts; + + virtual void SetUp() + { + type = CV_MAKE_TYPE(GET_PARAM(0), GET_PARAM(1)); + depth = GET_PARAM(0); + cn = GET_PARAM(1); + accelType = GET_PARAM(2); + } + + virtual void generateTestData() + { + Size matrix_Size = randomSize(2, 100); + const double upValue = 100; + + matrix = randomMat(matrix_Size, type, -upValue, upValue); + } + + void Near(double threshold = 0.0) + { + EXPECT_MAT_NEAR(matrix, result, threshold); + } +}; + +TEST_P(IPPAsync, accuracy) +{ + if (depth==CV_32S || depth==CV_64F) + return; + + sts = hppCreateInstance(accelType, 0, &accel); + CV_Assert(sts==HPP_STATUS_NO_ERROR); + virtMatrix = hppiCreateVirtualMatrices(accel, 2); + + for (int j = 0; j < test_loop_times; j++) + { + generateTestData(); + hppMat = hpp::getHpp(matrix); + + hppScalar a = 3; + + sts = hppiAddC(accel, hppMat, a, 0, virtMatrix[0]); + CV_Assert(sts==HPP_STATUS_NO_ERROR); + sts = hppiSubC(accel, virtMatrix[0], a, 0, virtMatrix[1]); + CV_Assert(sts==HPP_STATUS_NO_ERROR); + + sts = hppWait(accel, HPP_TIME_OUT_INFINITE); + CV_Assert(sts==HPP_STATUS_NO_ERROR); + + result = hpp::getMat(virtMatrix[1], accel, cn); + + Near(5.0e-6); + } + + sts = hppiDeleteVirtualMatrices(accel, virtMatrix); + CV_Assert(sts==HPP_STATUS_NO_ERROR); + sts = hppDeleteInstance(accel); + CV_Assert(sts==HPP_STATUS_NO_ERROR); +} + +TEST_P(IPPAsync, conversion) +{ + sts = hppCreateInstance(accelType, 0, &accel); + CV_Assert(sts==HPP_STATUS_NO_ERROR); + virtMatrix = hppiCreateVirtualMatrices(accel, 1); + + for (int j = 0; j < test_loop_times; j++) + { + generateTestData(); + hppMat = hpp::getHpp(matrix); + + sts = hppiCopy (accel, hppMat, virtMatrix[0]); + CV_Assert(sts==HPP_STATUS_NO_ERROR); + + sts = hppWait(accel, HPP_TIME_OUT_INFINITE); + CV_Assert(sts==HPP_STATUS_NO_ERROR); + + result = hpp::getMat(virtMatrix[0], accel, cn); + + Near(); + } + + sts = hppiDeleteVirtualMatrices(accel, virtMatrix); + CV_Assert(sts==HPP_STATUS_NO_ERROR); + sts = hppDeleteInstance(accel); + CV_Assert(sts==HPP_STATUS_NO_ERROR); +} + +INSTANTIATE_TEST_CASE_P(IppATest, IPPAsync, Combine(Values(CV_8U, CV_16U, CV_16S, CV_32S, CV_32F, CV_64F), + Values(1, 2, 3, 4), + Values( HPP_ACCEL_TYPE_CPU, HPP_ACCEL_TYPE_GPU))); + +} +} \ No newline at end of file diff --git a/samples/cpp/tutorial_code/core/ippasync/ippasync_sample.cpp b/samples/cpp/tutorial_code/core/ippasync/ippasync_sample.cpp new file mode 100644 index 0000000..f7bc80a --- /dev/null +++ b/samples/cpp/tutorial_code/core/ippasync/ippasync_sample.cpp @@ -0,0 +1,149 @@ +#include + +#include "opencv2/core/utility.hpp" +#include "opencv2/core/ippasync.hpp" +#include "opencv2/imgproc.hpp" +#include "opencv2/highgui.hpp" + +#define CHECK_STATUS(STATUS, NAME)\ + if(STATUS!=HPP_STATUS_NO_ERROR){ printf("%s error %d\n", NAME, STATUS);\ + if (virtMatrix) {hppStatus delSts = hppiDeleteVirtualMatrices(accel, virtMatrix); CHECK_DEL_STATUS(delSts,"hppiDeleteVirtualMatrices");}\ + if (accel) {hppStatus delSts = hppDeleteInstance(accel); CHECK_DEL_STATUS(delSts, "hppDeleteInstance");}\ + return -1;} + +#define CHECK_DEL_STATUS(STATUS, NAME)\ + if(STATUS!=HPP_STATUS_NO_ERROR){ printf("%s error %d\n", NAME, STATUS); return -1;} + +using namespace std; +using namespace cv; +using namespace hpp; + +static void help() +{ + printf("\nThis program shows how to use the conversion for IPP Async.\n" +"This example uses the Sobel filter.\n" +"You can use cv::Sobel or hppiSobel.\n" +"Usage: \n" +"./ipp_async_sobel [--camera]=, \n" +" [--file_name]=\n" +" [--accel]=\n\n"); +} + +const char* keys = +{ + "{c camera | | use camera or not}" + "{fn file_name|baboon.jpg | image file }" + "{a accel |cpu | accelerator type: auto (default), cpu, gpu}" +}; + +//this is a sample for hppiSobel functions +int main(int argc, const char** argv) +{ + help(); + + VideoCapture cap; + Mat image, gray, result; + + Ptr src, dst; + hppAccel accel = 0; + hppAccelType accelType; + hppStatus sts; + hppiVirtualMatrix * virtMatrix; + + CommandLineParser parser(argc, argv, keys); + bool useCamera = parser.has("camera"); + string file = parser.get("file_name"); + string sAccel = parser.get("accel"); + + parser.printMessage(); + + if( useCamera ) + { + printf("used camera\n"); + cap.open(0); + } + else + { + printf("used image %s\n", file.c_str()); + cap.open(file.c_str()); + } + + if( !cap.isOpened() ) + { + printf("can not open camera or video file\n"); + return -1; + } + + accelType = sAccel == "cpu" ? HPP_ACCEL_TYPE_CPU: + sAccel == "gpu" ? HPP_ACCEL_TYPE_GPU: + HPP_ACCEL_TYPE_ANY; + + //Create accelerator instance + sts = hppCreateInstance(accelType, 0, &accel); + CHECK_STATUS(sts, "hppCreateInstance"); + + accelType = hppQueryAccelType(accel); + + sAccel = accelType == HPP_ACCEL_TYPE_CPU ? "cpu": + accelType == HPP_ACCEL_TYPE_GPU ? "gpu": + accelType == HPP_ACCEL_TYPE_GPU_VIA_DX9 ? "gpu": + accelType == HPP_ACCEL_TYPE_OCL ? "ocl": "?"; + + printf("accelType %s\n", sAccel.c_str()); + + virtMatrix = hppiCreateVirtualMatrices(accel, 1); + + for(;;) + { + cap >> image; + if(image.empty()) + break; + + cvtColor( image, gray, COLOR_BGR2GRAY ); + + result.create( image.rows, image.cols, CV_8U); + + double execTime = (double)getTickCount(); + + //convert Mat to hppiMatrix + src = getHpp(gray); + dst = getHpp(result); + + sts = hppiSobel(accel,src, HPP_MASK_SIZE_3X3,HPP_NORM_L1,virtMatrix[0]); + CHECK_STATUS(sts,"hppiSobel"); + + sts = hppiConvert(accel, virtMatrix[0], 0, HPP_RND_MODE_NEAR, dst, HPP_DATA_TYPE_8U); + CHECK_STATUS(sts,"hppiConvert"); + + // Wait for tasks to complete + sts = hppWait(accel, HPP_TIME_OUT_INFINITE); + CHECK_STATUS(sts, "hppWait"); + + execTime = ((double)getTickCount() - execTime)*1000./getTickFrequency(); + + printf("Time : %0.3fms\n", execTime); + + imshow("image", image); + imshow("rez", result); + + waitKey(15); + } + + if (!useCamera) + waitKey(0); + + if (virtMatrix) + { + sts = hppiDeleteVirtualMatrices(accel, virtMatrix); + CHECK_DEL_STATUS(sts,"hppiDeleteVirtualMatrices"); + } + + if (accel) + { + sts = hppDeleteInstance(accel); + CHECK_DEL_STATUS(sts, "hppDeleteInstance"); + } + + printf("SUCCESS\n"); + return -1; +} \ No newline at end of file -- 2.7.4