From c90f550de0652cd418266c21a5807da104316ceb Mon Sep 17 00:00:00 2001 From: Even Rouault Date: Tue, 24 Sep 2024 15:34:01 +0200 Subject: [PATCH] Add read-only raster driver, SNAP_TIFF, for Sentinel Application Processing GeoTIFF files Such files are formulated in a way that makes it difficult to read them with the generic GTiff driver: - single strip of uncompressed Float32 data type (1.7 GB on the sample file I worked on) - more than 65,535 GCPs in the GeoTIFF TiePoints tag (> 1 million), which make it impossible to read with libgeotiff. Furthermore those tiepoints form actually a geolocation array, and are thus best represented using the GDAL GEOLOCATION formalism - a unknown TIFF tag of code 65000, which happens to contain XML DIMAP metadata of several megabytes. Part of it contains information on the nodata value, offset and scale, and thus must be parsed to report appropriate band properties All that combined calls for a specialized driver, instead of cluttering even more the generic GTiff driver. To help for that, I've developed a simple TIFF reader in https://github.com/libertiff/libertiff and included it here. --- .../expected_gdalinfo_formats.txt | 1 + ...indows_conda_expected_gdalinfo_formats.txt | 1 + .pre-commit-config.yaml | 3 +- ...1F9E2_E974_tnr_empty_truncated.tif.zip.zip | Bin 0 -> 138519 bytes autotest/gdrivers/snap_tiff.py | 134 ++ doc/source/drivers/raster/index.rst | 1 + doc/source/drivers/raster/snap_tiff.rst | 39 + frmts/CMakeLists.txt | 1 + frmts/drivers.ini | 1 + frmts/gdalallregister.cpp | 4 + frmts/snap_tiff/CMakeLists.txt | 9 + frmts/snap_tiff/snaptiffdriver.cpp | 727 ++++++++ gcore/gdal_frmts.h | 1 + third_party/libertiff/libertiff.hpp | 1599 +++++++++++++++++ 14 files changed, 2520 insertions(+), 1 deletion(-) create mode 100644 autotest/gdrivers/data/snap_tiff/S1A_IW_GRDH_1SDV_20171009T141532_20171009T141557_018737_01F9E2_E974_tnr_empty_truncated.tif.zip.zip create mode 100755 autotest/gdrivers/snap_tiff.py create mode 100644 doc/source/drivers/raster/snap_tiff.rst create mode 100644 frmts/snap_tiff/CMakeLists.txt create mode 100644 frmts/snap_tiff/snaptiffdriver.cpp create mode 100644 third_party/libertiff/libertiff.hpp diff --git a/.github/workflows/ubuntu_24.04/expected_gdalinfo_formats.txt b/.github/workflows/ubuntu_24.04/expected_gdalinfo_formats.txt index 13517709f757..3940ca1d8b77 100644 --- a/.github/workflows/ubuntu_24.04/expected_gdalinfo_formats.txt +++ b/.github/workflows/ubuntu_24.04/expected_gdalinfo_formats.txt @@ -2,6 +2,7 @@ Supported Formats: (ro:read-only, rw:read-write, +:update, v:virtual-I/O s:subda VRT -raster,multidimensional raster- (rw+v): Virtual Raster (*.vrt) DERIVED -raster- (ro): Derived datasets using VRT pixel functions GTI -raster- (rov): GDAL Raster Tile Index (*.gti.gpkg, *.gti.fgb, *.gti) + SNAP_TIFF -raster- (rov): Sentinel Application Processing GeoTIFF (*.tif, *.tiff) GTiff -raster- (rw+vs): GeoTIFF (*.tif, *.tiff) COG -raster- (wv): Cloud optimized GeoTIFF generator (*.tif, *.tiff) NITF -raster- (rw+vs): National Imagery Transmission Format (*.ntf) diff --git a/.github/workflows/windows_conda_expected_gdalinfo_formats.txt b/.github/workflows/windows_conda_expected_gdalinfo_formats.txt index 2d8091390b21..4c384b47ac5a 100644 --- a/.github/workflows/windows_conda_expected_gdalinfo_formats.txt +++ b/.github/workflows/windows_conda_expected_gdalinfo_formats.txt @@ -2,6 +2,7 @@ Supported Formats: (ro:read-only, rw:read-write, +:update, v:virtual-I/O s:subda VRT -raster,multidimensional raster- (rw+v): Virtual Raster (*.vrt) DERIVED -raster- (ro): Derived datasets using VRT pixel functions GTI -raster- (rov): GDAL Raster Tile Index (*.gti.gpkg, *.gti.fgb, *.gti) + SNAP_TIFF -raster- (rov): Sentinel Application Processing GeoTIFF (*.tif, *.tiff) GTiff -raster- (rw+vs): GeoTIFF (*.tif, *.tiff) COG -raster- (wv): Cloud optimized GeoTIFF generator (*.tif, *.tiff) NITF -raster- (rw+vs): National Imagery Transmission Format (*.ntf) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7ea6c49bfc82..bf500940536a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -36,7 +36,8 @@ repos: autotest/cpp/data/| autotest/gdrivers/data/| swig/| - third_party/| + third_party/fast_float/| + third_party/LercLib/| autotest/ogr/data/| alg/internal_libqhull/| apps/argparse/| diff --git a/autotest/gdrivers/data/snap_tiff/S1A_IW_GRDH_1SDV_20171009T141532_20171009T141557_018737_01F9E2_E974_tnr_empty_truncated.tif.zip.zip b/autotest/gdrivers/data/snap_tiff/S1A_IW_GRDH_1SDV_20171009T141532_20171009T141557_018737_01F9E2_E974_tnr_empty_truncated.tif.zip.zip new file mode 100644 index 0000000000000000000000000000000000000000..521325d7b95de1addb8a5eb9c13aa5ef5d955003 GIT binary patch literal 138519 zcmV(#K;*wrO9KQH000080C8tES@iK_QTH1H0HAjR0ABzA08=qRUrASAM^Z#cUolfe zR$nqOF*h+VFga8)G%+NFVRU6=E_7*TE_!KjRCjqil<)U`ku8Zxl$gp^h$IPPrVSPH zjwE3wgk;M;7&F$CwXDf9MWt+$B-=2^l6CB4nZ-8t83r?Bwy)3czu)Wm<9R*jb?)n& z``p)ko^$IfQ_l+r}7Rd8Ipyz;kLPKm6FpBdR&`dVd7O~q!?{U6 z{5h^CxI3Qy`%Yv?r`eCKpXM9_vcEn(XtQm6sLYH@!HQxptMj1Plp!AH6qj{ERa6Kr z2CY>1T@{CJta#NjJ{f}IrRL};6DZ4Q1%|Zds7TAW02TK*BUX7-T&)!FUq;pgnMY}$ zPntjP!b+)MEoM7ojpN#{CbD_Fnu$j{7OhjiecagISO)0WPQX8O z0UKWEveA?r>;*OZDA!qV_#~Hy-GAo4$)DO8ln;3KHf$=LHV)372rO z1b=PDU~_)GxGJU%;f9D<$;i?nA}2mHIf6;9&n$I&P_@??w~Q0|tt71|DkzJ|956|G z>=J7yh#>G>Hj4aKLChwf+R5NItV=EwM%K98sTKf^e-NvRSH&cpsUleZUFu)0Y2oQ{ z3jgJcAfLQ0-QC#DjK%Th-j^IfSXe$Mr-`5|-~OXuT^@0q1*mdnhT@%;u!|ef;&Wp* zygfw?v#~vt5cXo4mYrLxC+!lGrogluxZTZ<`AqX@8WTYx-nmY>c?-2trXO7^KZ03P z))JZ%LI3HQX9dj9M=_1ko7KA-SyuvDp$?ex8{OZm2$`%DzK%`_h9cE~k-`>&HFEKU9T2mf37Hc5X#B#HX zl}VV^@i@z1jrVzu-Dh|Pyl2A~o((&=#I}T&e%|ORpmqe1pTAlPm4X!4E?k}?OKWiT z*%PsPIX&}XZyT(uZglg(N1REa5mMZ}=^NG4I$vXvdw8n>ari9GOs`xOvkX)jPj$d| zmSn27xa-D;_*ufLLS2)PQPlM^o=9AbR`*6)Y5`SmQKt?+&+WZB;V!^+ZBDrIUBnl} z%jtgqc?kkh*Q<@JTX;FJ70s8LoWFkB&gwu zDVPEJK8YSy!7T8pj90OPa>#Ng>6GG8(UAyMjCA(bG^*NcSQZ_)NlwvYpoWj%)3bzm z2CIx?;jQ}23SUX9QR{lg+xG?yH#gCK=Ma!BG+OYlP$bI6YNZrJ@^-si2c3`iHi6Ve zL1neQS7f&DYkqnY+=jDt%F6xxSP%v(Zp_YC#jKmF(6d_&uq?l6B6H{Bs_n;+y}B^8 zfS=L%(@856^WqT3oeunn2r@CIi}UT}*;`5^3C6lylW|v3NRuqPJGK^a+$S49*>Mo^ zBIW#eyohgS%RJXcxFRBV^eA`W+ow$ZF!G-Ci|jY67H4ZbGasH{xcC-^k?%96s@_ji z4ws|QnXo8u`ar4dEBwOs=E$Rd5o+v$hp;6+2Cbk}C&ZFrSoIzVNYTvcvFR7*{(LjG z60+o&#`JkEIZhi~hm+~Hk$N_}4IzH5g#7mTQ2&Rn5&mPxs8^>>bvzzysMvC`R<0ly zsTdZc#rY_Id&+m7w@w9I4&|PXI8(~LB#vR#S_yQh?p7$6DX9Gq5|-&sxY@$GQujvM z942&M1y@>x?nh=5M7bW1;&^wr>5n5G$)DSYyw!2JDT$>#i4>gr8yk|>H_!a~*RQ8^ z;V9$TYJ`a_>Kb0h;kcHC11=$&9xkIHz`Hs8)nhp}J2yEsEsj#~P(2(tOliz9f7?uvbYtFsD*bwq`Nb|LeXmVu(iNla<2WbV@`Z!B0x z7^xoI#kxU#1^%t&bDh#W9}%K)$=-Keo$8jXQiJ}e<)S2eH5U2cUGRa~@lvFg)3NLc~z}^dVf0*?~sIbo!1TT|OM$!uqA6>P=FE7XZ(*@;VK^-Df=3FL3VI zu?`D4(K#zZ(NFJUabTGpKjt;4zA2X6`6%vNiBfZ4F_KWliQkw5gxXnCMUc2r{`)q5 z)d>n?k9UBefBA!Zd=tB@tx;3Il?HAKQx{=h54L2WMR+e&(ix;S?s+?wCKZ}zPnn~! zNoRdaKcw7j^~63_;Ag`6DCxRiZ!PWP$*Folk-T}Ovo?nLSgSd{y{&Prtlt~w;LR*s z)v#t9_sPoxv;|Y0oHkX}pID>PI9Nkg@fnw*!+U?cu+?%>B7|O_pmh*qVPjJiS`^|Y ziOZ9PmS`N>v?~8-GT{qHflA&^NZYAyqirF-JpQ0&hsiel^f9alPAHLpm0aDae;f|LwVW3iSn`0!{s-&HmOD#Nqq+i zFFC_(rEMF7E?Y@Y5#QtexrcC5ll>8mb8;$dseQ!q68nx~87HF9Voeb6mKK?(tedir z*YVKiVMZix8e-oOkI%ddEPFDjFr*~P9jBFC)7rMFE<_e43KQu6{9l&GKAgFd*xDJO zf&r+eGnn&9%iUO;{l1jpu^tI2piOdxXPI#Si|9*adnzzdGEn6cwo?fYs`E8sS;4sD z7gtl>VxzbVO^31vlv+_U!xx^4K+arv*;6b+0q5R@BZf@0tz`@1gU;1pxWlGeI2c?K zM^5LGfyQSE=qbN#{0M!CqWy+!>6UZq^_}SK#RemoAgAZ{yLN6Hjrpu-_#hWXjTiE5 zDlMQM9L{Ousdlon{Ln&3;M|L^r}wIpdfdmb+~Xl@bkxhn(eL^WM>I{e{wP7ODQEHO zeY6>(-5>6U53Kgu=s)s%OTDLi%!%6cO~;|!Id&)KK)4FC{kwW&%{GZ!qQg-_`b+Iy zdJS^*mEz)Y!|H}DQ}Y|yM@*ruN&tOn9s6H}Copr3j(m@yhnL=k&ReIMF%qP!`fsrv z$bKe)DfC$JXD(^jycH?VrOPrHia`s7Hc0dIovGrHvD_=h z7w7+|b#P=Id#!;2jYkzRlP0ibv+QgmA=H|_VFzEMQ966+SJuu!-3|_8cI}E~8Aad~ z^ZoR@dJ~wZcvAzFTcZANyQlR)gh)Dk&~YZp*OymZ?)*qgH`ZR1WTiu8_%vQ?cqnot%f)vfQyvl zXT%Zz`p>Xzq(CsiboQosT~?M%o9YhVTB?={S61W9g|HX){G{;Yb}gAGnTi1G#hb{t zcbmRnok8?>%TlLqQ8t2&(cydRl#IAo==w~iga~Bi!@{XDoRHzAF3Zu+g;iX$14tNp zQ$u~5^wl<|?YIa!XU^}c7<*Gc423Pya!tdP*fow;-xWZ9H7*sw$wY?(dI{OYdkj^_ z@CvX15_C@&8`28SZ&Bje^f0>iOoZ63F&~Ry5g=)p0zuv39^(~LK!(3LcT4<;*8j_{ zY&T-Yv89t(ZdhZm`f>cCZD30N9@-XqUfD1N42xI&&BtIPSDKZuk7SH=Jv}bLLKYI7 zuWpkZ0eaqVl`w^a>iQk@PIrZx-@GDk_1vEr`oR>%REbG>4eJe$G(s%Kh2yYyhT_V@VbYWGVUM429cI@EPM`3C9L^+#Fx z=P)FsB?cKo_`T6))2oN7)cKBjUhgy5{R&=uWA+x&;qG?yoE$I=Qy!RvSzrKb- zD6yhyxasS|#XFv|b8*R8JJBClnzD9{Jl^_zy zET{YCD`rm!83mdPLI8!@UWwIYr+3ua53E%vGRw?T&rlxDzF>5}Ogwf!OVj)rxM$HL z4aSj*&v$@G@l{-4!LrJIMiCx5p%R5dwx83vMEc`?`8zbf@ia<-a|9k&;mq#V7k~NIh#I^ClfXAKOi6|5`+$KAuh4$IcWMH%JF*bqt)CxH~c4=)aj$Pr0 z_u$#e`rZ~^Po2=s-!JCH^&*3!7A?qxd*r4`o0H0pfj8lR;+IvTZPSU|H#;QZi1HZk znl}tPL!%&1wK<&n{4?oSn3apiGd<7%$(F=*^{MzxliL}_=$UzwR^)oWVV@{M zuP3^ne@XrQ2tV#3BY>{mCYQ__9xd0#zm|oq{TR!7jly{^f*drCLby-btdjK3>AxQl z$H~uJqerO2GR06jzdy>{o7HkZJ4+78rjIAJ(uyAxg`^(tkb4)OGa`qp`^MZ(j2ssW zW<+nAeCC!)@|&GQfMh%hH&1Q#nH1?zfRFa)J}VT^cBS3`b_k;{PEgs8QbgRORV7hp z&PJT#5`7y7%iF?So(ZIfEi%8`;ffm%QP%Rl)g0iQnc7wg*vG^E+$$kVQwvzUA5NHK zx`}r=G|W8lcTN_O^H}`VQzP&T&b4T=J6^F=79hi%3AY&MN@h@r8gN|*4j71);+szJxVtL8?h zq|*-}?2wPECSy}`+COv4sm6GdKH zct27aG;*sR3*g5#(#U2>Le59B?$DMbH>*;N@hb+eksm(i1xixk`W=~fBkVhi7rd;c z8Gp`E6lR%j=0||{>jB3=>!t$_{~~i!bnm<;F`t-Ar-#+vpK1MFv`EBF+|KV<)pxtI zoVBNa4l%~8XJTbcTTn8@cMRWFF!`Akc--*}(!}Q|EXFoi-Li=AGIZfHeLqX}LNZN9 zfW5P-IIK{V?PBFSZc{t;pCBbs#k0w}QFlq~H0@^fYlhu8ak)@Qh`MXsp81|&6yj=y z8V~A!&~=y(Nq8Et_F!B46Ezxpi}Cua>AHmYmCyiY_*0{m(ESvfe7fjQnxMp(pKc#A ziU8d%aY+f(83$2|v(V=WBTWeZj@ZdN%%s5LD!s(CZ>%i5#82V5_SQ4Ku z#K~wrIV2yj$p@9%fV|oaDEnFT34h&y6!8BW0cV|`{4vZlB{pE8 zfq!TX6RVJ<*ko!o;lwxKLGUazKpL0oOxYVI%gA8+!C(0|Zw*gGHFh)DsS(Ewgw8D? z8}ZS};%k(9cXX?cwtHb?xpv1R@SjxummGhp!FJ;@W|auJ+_Zs7&Du!nVFOW<{VAOg z6#qpi!p|TAROtRR1U_X!O(di?b;2(EzDf^;qlHQN{6Kk~7PLRex-71#Ar@pgnE1DB zWAOkM5#VwAz@D#G`C997VW<_B`r`zw(nLFZQ3RmZ_zHv^T;20Ockv|KXR28pvs8C- z`DY6jf?Sz4T}8%#&ss!7X5Cw5af~{< z_HBNO+_n&Ch-|pGLbf?~B5K^>D>eLT*aGF23!qiR*NFvFY_sjFZ_n9hB$ROrCA7?9 zj23(G`3B}-n~yav|61EBZjZI>A@c$Ia<3DrKvV+l z32|?JO{f*goDVoufa~igZ;MgkN}68U5%@rlmbH#?dULq{hE`HLUkG~JE_q3S=M38+7e*XsLJ>|ED5Fz!8hX0S4kDmZERIk6dx!oc9_dj;j& zp<~MKtBbcgaO%plKB9{`t|L&;zgS8@|YN5LV|7 z?SWR9c!Bj5Lpid=Y$MS-WKW*tY0Xx=)}rMldF07!P;V57qQltwz0)B`h1V$HAKgT) zjBR^vb|N_oOhvdokvZIo^+`LrXt=4Ixb^+gy%xhizsq6>+ z0RTl^i~=Dsr=9#J%cU`Cq#=Vco+|Y-;Hs6;ZAMxr=wB1jGQiLrsL)I!50Nvo8`4fe z_NM}4LslYqS5(SJxC-O^&m-s1M zJiBHXfq(IswWO>%$CBdCJwCNts`{C$zwqm0J{2md-vN$J74O|_N3ddtJhqu@nN0LE z8XyLD?fkR2orKu1+6V&oEQT7{xGP3wDng!2KtBvLM1pXQh0Tv9yo8!sJ-ZQWzUH!X z@&saK234>WjCm~D6xNw|x~xX2|M7hk58{U>z(!8OX=du9v?UqsZr>>pfFDU$Xny>n zqk9OlZB^&NQ^g1f{7YNY^9dpLz&PPPeIU_y6QN@pM|C9SxJdZ^f(P_Mq?5Qr%{}

zyqEfp*T@mVufukB#2tanS|zw&ohfUv9a{6?LoV}Y9Z?S~3s#8+ty}#mv_lI+<=mAN1Q4poy)%n3 zZ@!E#(fo?Ed;`S*(b@eNKOvUB)N0=!mf9=sjx8BE^f#VSyz{y>7z9M!TdV$p8%8H+ z{C6yhe0O!Tzw%+Po>b@Q-pl#=!ss(9DDGj*l&Q%u6+s6t_DBc52Q3 z?ETF^1Ydt%S%ilE9P?nC$sMhDrR(ZXl)n$j{zF46*>tSKHlRrFZU$1UkVp~tUT$Hw zD9v1{2M{N_l|_-jIW^V_e^hME54Lp+1EI1ZX_&-xv4ZK&_`dAx^$%BVUVjIVW^& zASnByc+a12ooF@9P-5PK4|RYpY#i7HtM+USailwx8@1hMn{naKCtfk1ZgnllK<4EY z;d(3`&{1llfL>1ib>utLM(3rR`hKk9A$E7!yU=Us4{(>cG1gU+SZL_P>PaAAGByR2 z+6LG8=O;RYbvhTi+yrs}dlv#U&F0wmf6^(HkqZT}RlORbqWG(EHP8jp^a< z9|LPR!kaczh?KKZz3%D5uAaucHVHgAGt@xGwE<5#dC#BSS4 zb`jbo_A=bnR-(xV|0NSh95ov=b8gC;F1}Ase-EF5Z>%GULHEV3^xx#oVeaM|6R_O9 zXqOjy#nF1FQ|eza9S^gV>v{9_uPe9Gyr^z9+wLT$%VDjeJa9hsu=s)$J+fv2BBPcF z8OYnPt@C+%PSHaQuU&`~UrfC)kA;L+t=+slIVkySRn0HN{ z+*OO>0!U@*Cd<-agz$!(dm+VgnR{|L^91Hme6zdO`1gui6#3Bbhk4(?x%c+^!WRXa z&c7i%m58_vobh!j;YJ%JZmwc8mYx1EH#4?>&4m(B68J*zA<-aewFQo^5_{!~M*Id& zd&iY|%;s=YAGI}jE&Gngc?jo>atw932RwO$O&BwD?X%BATP~LtvO>*QcX!tt4k5N5 zWP>_r7r^+4D8oo96!%;F25@-S#c}Cj650Bn+%gwXB@n!ml`mQ{egipYl>dx z%056|l@bZvSvb$gxmQ}_3`D6-n9)UZE*#H1kKqSnS2^nY>uXYB{fwIp*4{?-2{66#ZoS;@p~XR z&F=e}H6fU4-)3N}U0|t8mPl^8x9?fM1OGJ^LvTFxFm4lGG7PUrE_Xb1r+Xw($Wb~^ zWG)q0?vV1GmG?n?%aVTA5CkBIKmpMX+V^)oONe5*G@C5P2%^o}B)|oT`cS!>-}+65 zG#a0r4LFGIk~$x~34g&9tOu{WS~%T!v4j{@bftTftyleEZ8>2=xRrI-v!dWA{*H6b z)qO;c;WDB`ZaG0!@L3B1Hnk@@#~ksy)2Su3LzWBb=sG7MUPG{&bNZf7_0Vx$=9bDq zBW@xY(IGGFR8x0QzuHZ|BwUr)aiqTnu>UBlM_aXKl(q zPy(ku9Q;j(?@TKoKgdM}XgPUnq<_7QoAv7!d5Nn@>&e|=-)lCld2is)mSyytyg4I1FUPH0~I8s64I@r~$A zW(L*kD>kaE;7&ZS-rgVxXrb{D3N(6P@v(X&uF(9y_2CF=cR4ui2k7kvEqT;^)Txzz z{``xcnDGH(UNlE%`5kr%0q*foVHrratp2)an{XY~^&C}qhhft+o1NAS9h4AjMPRoR zasrVb7x0)J>)jYVPs2+ItSdvgivk-*f2v|ka6jMb5WRKp-z8$6?KPjPM-2=m1VjPn ztK?I>1?dk5cn%%12hOmi9{j#rL^QhM_2ebNRf=?POf_1@Uuf!Y#N+LfH&DM4{Hc(`OfOSU zR!8wQUM?xT|H;$kqpjISA!D)S#qzKc?=)N#zyn-q%OA05!vt8A z@WNI-<~mMFSrT{qxk% zEO0sMvo|M`AK@DQ&$*YNlifV}`}H@lz5B0<@Y-XTw{A4!GVy1J1%^3=V`A3KM;iyU zCP8UgQ3&@oqb`H}YH}iGFvKuw!>_MDNKD^F(zSZ*h#P6pL-Z_uX?nb+Jc*Tf*|PXD zpGIrqhol@oIY%ZCT#{T!X)b+ERW%5XXrZn4=1Rz`?O$=ZKbWf(3&hdt zlzVfCc60K*dgN$FHF^EuU#Njr;PY@IJn*a5+_n2}se^gO235^+FB~b4<3S}+8zI#f zWGouo4&ZS5Lvb!=*d&mYB3CKvvC3y z{A;TP;-nBk-wZ9)U?JtLU-n6G6#j+f;r&Hzt=qaeJY;0q1zy0UWk%xWIsw6XCR(N#ISz87X;4zKsB@#4$83hk@;uW;{up12EgXzvRqn_6r$lt?1Pghcfu=+L0(vBBpbdlF3-Ps}n@uqr9YZvZV}7O9;m&S1Y#5*}DDFe%V4rnp zMuS^}|BDym9J8&*PFDD>Gch<~s$4+gEavX+$=d1D`)SThJZO0Qfa*R8{xQom+f|0v z16WsULjUW3K_xHDmU<1^H-}ii+HdP8M7vJLkw?R0j(7rkLw-7*TjH)y-P5xFV$cjF zXAigRS?;D!Y}R;r#bPh-s&|QEGRcw;rbDvj^Ka6_3BmT&&!Eb%@G;P%vSL?v?MeEW z1jxjPrD{#}5xii-fcI2rd}jUewS8aWrXNXvfmx5CG^{Ni?%)${r3>T;CdY_pJQ!-( zgYOdU>rw2Z_U%O<&8eijJ;s&bi!2$%eB~8f$QazB|NJ2U7XDt>vlaV(^gbO$_*YXF z_EL}$INqxUq=Z-gau=c;-~U09%hqJH1E`^MDWLbMn8#pqdMTk&8$x}0l;JNgE#GSi zUEAx%qbx^I6b38tK2Cu5J1qJ9m^$P%^De!H(4}Tm-^t)yxr=b?Ge%#nJKK}BUm_27 zpv_A;23@<(<}o(Hsrf$r4}n=Z()d+|O`Ht}UBfb4{WTB0+~Mst0EK1&6Hz>8q>0cf zNy{O0iOX!TufPn(vKKgLt<~GuBKlmzr>?DJY9}1Dk(K#&?~+A8QnheI@2PqMx8lJd z5D`SpvoW?2A8mkic5%rkhu8?jD8HYfXt zHDZ~zp%#q1WzS_Ul2sk*n|H3otRQSHhAg08HLiW)nFNB++&OZVDrBa)^pySfPh(Vp z%#ZQKf^qT9T_Iy_ZS5r00LJalI`+F#_(%S<1X-$Tj7rrkJh1~{J5;^j?5tjJlIoGfqKwRINcj69SdpSqsVe}7vhWr%j{ zC|xI26n_x#0%Lk)t}(CYV@SU;o2Kca+_3`~)=re3nf?*uzkZm+E?#+d25(s zzfMG0)1S?;vN@BtJj9ZB(yq2KFV9b3V&_(8b9o2rERs!q(Ba??*vly02ELvXwZNT} z03aCe0{=Tg{^SpFuq{EDkI2dA=1b}*Dt)e3K@;8D8NgzOpb>cXTEX^xv8{!QoC-=Q z6{t-9vXJ7XQwUoA^=)VPK%2NDt#dBIrx0EJiENSgyAn5UCxm#DQhl^wocs&#cBEF4 z8~g2!jWEMz)ZbYaxfnP#OuKM@eB?N@KN5-!5mDa=$DMq&6e{4(BMzE#~CM+`zIcJS{vsbQ_ zuc)=fJNiR2+NiX9{_bG6;{&{%ejPorZjA%zvg$l(mTy2EYJhgRpRf4ZV`Av&7l`h| zIOao5==CPAa#@o2^oi|CL3EYkun-RGKMwjr_RPZ>f&T`GW1QX>9o?-V`@m1s>DNV4 zYgV-0zmL?LvpV*Np%db%WRyC0`U*3+@BmD1O1G@m_iBh`QfFHHy;z(bfZZ*J`7=l8T&p=x&bomaUqGmh3z0qj7t(u+PlZTui!D4q%E2gU;Re@`8KrsmaI9LY z3D;^*4D@&{Np_dAP~!eVOdfla;jN4DPGYHFelD-xk6wVlZs3IK&R2OEHZww%{SoNy z8_1l^Ic1RV-}c8;hCHk($n}E^^C>qsyzzB`s{iJ-UH=^gs=y4b+pj=*yu@6|y~U`t zbH;zMPY8N}R3$N2Gu@E6&3@O@!-$zpY_<(YkF?P~PKPH2|K$mTCqRAoj=*WffA1vK z1OOU~$e9lMZe;NvF%*NJO!YCOKW9}|^s}&5?NmqFCQbfuUj}=14?cDWr&rhQFsYbz zh%1-)OKL-^KLS4#q8RlJ)lfiZ>XHvLz$MbU3}BbV@<;L~hxP8uZZ|+vMn)utztgrB z2&2Dxe`LxrrlU8uw;4xzKK6%<^zqX&0xx{%!P;n@0^*nfUHh`1mG%ICW`$N!uG*B* zLaPe@mw5IO97-Nb8K6C?!1|~vOo&0AzUw**wCwWc99Ig?_(mq!=RF@eIv!B-V~`Rr z3tb3C7Lltq-h>A21DoSCvw8xK6iULG<#Gfr)*Qp6R@H!ZWTxs!%jNNEwm17Hpxfyq4JAvB) z6A$A}fg-#T=QBU~*mh(K)tA^n1)O;pWDP$6o0s`(TE7GOEO_b@^s4>OjnNWhX~B&Jxy++Gr=VBF2H4)!Ud{`>D(yV<)KEe&D8AL=sS} zWjELVR4h;aV>(S3-rZ}THsSlWxGJh3wE;V?j2w}pYW4R8c5r_IZ;^%qR^vsm99zpD z@{C!p)F;!nS?+2kxZxRF>BFLK4m>n0OQSR5dFa^G8TnsI>>X=^2M)y53gGCzA^@`T zx7jlhmZsdKM^x{Hn-`FdJfCm{-;+vPeJqjhMrffHdc{3w&gvdLt&qI4f!#PNH+=*# z;y+HpR`4P@N&GikQ#D2wV1kIU-_$|qICA1fSQ9Qk0M*=)FKK0z9-I1$7sTmQ7<5fM z*Pl8aSFx)W6yqLJc5H8T&(h{2`QVIeOl#kng5?}KWPNXceZO|x1Jzkfmm|5l0;|Dj zZ>aa@)pN}*{v!$tbAamIV{NioKs)pcjY( zOKD42;1nL%l{W8Zd9~bC;*&(v40HDK-{P=fP(Fw{zC#E|DX$(E>;n`_{Y)MlD<}ib|uZiaTV) zkeyObH5|}`@R(k<%S=;oYpIq9`(_kat~ieA$UU8IkO(ns1;Zy*H4fv?m;n#t=C(q8 z254ytZ+sOOU&UaXF45%{=a@Yj{{7kgs_dz|^?!uTVqeL3jhJ5#fxUzXu$MbQ zz1g&Kl(*N1;5XEUtDgTZSK#_TE&->JnZAGR#K1(VF-*y@tAPrz&?ep&!U!Y&H_htJeH0SUWk0hfA_M9 z*++2tz6~!WH+)IF=oW+e@&&VlUIk}ut}FFh8i5r`aFOu3Z1tki-F@qhfRQ-sWq-2i zj)eF&w=?L|*1l0SwK6h>D%YAit|5r+cpA&v=iBGk1*iyEtq(#&l_945^~rt|^gZjq zUvFst;=_g^D6lwU^>EhGJ`3ybh2eKU#k^k)QJ{wRu46etoSMchu$plm83g|t@i-jB z;2#a3bAvc^Oc6N%a3ZW%I9mw)gH+iA9p?!b%`9Fc83rUKoFPzbDxnbi{sq_{4apI0 zdk^l$@?%W?B=E7>`FvR?I--?Ywd`l~I~dHBEu#HetgHqt&VFq*5jLV2xYA zYh+li;A65(pj&N@8ev3sr4akh|C`X9+9?^@?TULJQGTu=sDHdSNXNJki!VWqEW z6q3;Ztq{6bg%D5KTG3N(k7Xt+HXBQ@ZiDtP5L~x%ZZwBqAT@iCHkpb`a)ryLzg@`< zCJqQrQyxmxrad64u={qlyxyj6`x%h)Iz#89sk+;6x{Gge%8UeZ_aa1_AfXCF zWC8}k)N4CYn=|@EX076jP3EJwzY7Y4kPgkxJ|eQ@}-NIW(9exh_GkCI+pW5oSaE&aRSL z_El{xWlIv=&p<%FPyUJG+>FyXzQhbcU0BG3#}R#dEi*c}rLA>IbMhBk(TpPPG7yFtDoc??5CYDnEpZ+XY88i4+hDweH)jh zA44x8*Qc>ayNO!GJ*S?7ziOF&mqV}jX zuJGT=@{h`f$&z2O*rHIK6wlY(JXJzTjkmUd*6^>d$HCa2KS0s=8e+}ca(Jg2)ij&+ zb6@N_h5w=`R~E<0%zRf7rrTmy;_9ZfL0W6cMRXm;1&BiaA$mC0ZA4x%kFIsvmA$Jc zg}=mjqyQ9W1(|j#QfZ;0omNW`M-dg(q9<8htmNOPf@jGtCo0{RZXK{-Jm3A{8{R^w z{Z}o!>S(OWHlAGYmRHuRPx8nuL^BhEp=TPyf|p<$uNcchCrh#&O$C z!o20PD=un8qm(uEHf~|z!WTob(ywEPpqWmAq}6|&MjV*}fV9ZN0hTiRi~ic;Qj~bb z)b==8^tcH2iT?ZY~bvA0B;$1sF0_3!C;d8-C;0}nw(dxO5lUm zF<H#LQPGM`9@1g)A2Z zLG(Rg=sO5Qa;q=uAMg|xxCG!8(t{3dMeOP7Z!H97qLd1(h`GXW|$qL7ye{0g*BU?Dza^Jq~jR(#rL!zd06fT6hsy(U4<^D zqYl+oCFmI)q+Gjo=O{}xk?b9o{_w&L#^X&_f0^b6$~B_}p)kmUFzCJiMl(G|sa$z# z(|pFKXhi#1Aog2!EI?Qe;~)t73!XdNZz?0Y@sVD7sH&#X{xFkqy=@hNNYYC%4i=gT zdZdUsU>PO_L-jq^ zwmwgHJr_|@f`VQJHI346R{$Qw0YdwT-9%=-rFx;Da8Fz_7 z{VP{y;hvSp*jsD2o0u6jyDmmRoDe8HY^HM$zL~7E4azB=t&*G2i{aTh_#a?-CwS|g z6x|d-!_U}Ww!Hb`W%oo4p=ZGG#W2gg;FNuwU0n=H`PbD!$;O6$2jKw`$~%88%0ZZ> z?n2@O&Tg&Tlu{acN)H}kV!-&~6Oug#`Zaqdrc#`An@1bA^&HLUXhHXzRkXtzuC>0# z`_cmML_nJn)Kzdu9o!h;+bc_DjmN(PN}h@(Gl^N%qP!258u#^z=%iTTCWjyL8=WmP zB`gGy>EegCFo`Ol3Kb_4mIbhcu4_kG-xGSumO1)oWr9O6FNCzRrTq`G(h~{O38L4o zZ-e8fuU?otdI;%U8+Kt#HBoM3h!uP@+GX~>CO^8#x!W)=TVvzpGkByJDWCzP(83C& zwrW2x%`?^92>3#$#s1>B+q5%wv#YZQe@q?QGt~4Flf(+?0&!uG6+@CZ=uFG5=3=h0`px#Z zo3FX#f!Md>R*#5(Ky8#(T)Nt^BgJ&lxF(7Te=DW1UH(uE4Tw$$1d+35PW9YrQ=qBd- z?W=Kk=(hP)Grxx?*{i=J@0H=Q&wot~G+R9eAt%=QNikl9qJ2mhNLQCv7-6ugVU-Cw z%}1zAbv4|uR3Gdv|33s4S(EqTGv#z z+4er(FghEr<66j0_h^c~28BL(Yi4Bf*Yh$ac9OpHh+{eb1?U34usu4wH-$GEZhoH6 z(61|)=gY_5dgp;|DE7>3PI~Go+OQkLh|<|kiGH$L^wGSMJ6p4@gsA*#8{vyPxrZ8w zEoM~K!ax6R?HW``TZ0McQ78w{l zR$XFu=V3g_%Dt@+DcP{*I^go)^4~R@iJMVblnZy9@I1OQk!);4jY%0(XSRQOmRsS# z5QRtF^cPrFmR_Gf1eSjr-3NRkwi{FM3csi&S880m;a5i)qClUJyf?C>j+!IzBH7Yt z=2R<4F18~TCz^(mfN!;k4Cff5V?7l{WT*=j;?28l=e4D*(MhyV()=!K5Q_z2sYhvd zVl~5del>xLWU|AO_kxtAj^Q2KI;LeSFObWvI~>deAfR=c<-+heW9l>?x@qw&H}i}U zvnkm2*!O7e?Cv?F*ntX6Rs{YKU(bt2$h6^vNji++1~(caZ2$XCku_6%&wtDX$ueoG z*6D8g2DH=8EJVYEYM&n0TeY-Y| z>0cP70=+hmm-n)laGKTR9XkZ?47_p5!_4p$+Uh&FnM2AxxDcD(fVqHfxjm@qXq#XJpC zA9aK2=5nF2AAOP%63(Umxk+$t%MB@gynNET^nLON{vr&0SyQ(R*-Um4q(3attr_;~z@^$ANyj2gP1I-d8=Oe=k=ygG+{F zU$D-qoPCl=ui{!_w__ZQ9>i!=*0a`p$%s%Ff)uN_f)(vOk6x{^?f$ejOAC@P@x{GL zkW6CM^MrHomJ4v#hAM9TrWij}OsqG!sOzg6aS>sc&Cl0WQ35}y;M68a8y+3vcbttz z&E8ypyGZ#NM&)fCR@4sps5>RFE3niZzKi(pX`RYvKBnWszI>uhd+qKZVv4Zz?{#^l zhxEp^z$pZA72Dd$>Aa-tG(*h~+4#Z?9T=~JPlRA!Se|>>Nl&cnv_5QW%C#D6e6JRl zcAc!4dNf;?)!5LZdwDg$gYZD)+yJ)(e#RpXo@3UqBhc!ibgx5hvGWw^@VCb8Tx3pw zce2Z=g9eps5qv#13Rpl?uje|*sS{H=1pYja!85#i;*=?*WU-~j8(%16k*5D*MLx-W zIfW5G7RJB;I|s5lhjC~ZVGHnD(G#Aw;7qcWE=bP8UDWVjDCU#gCn;1!A3P@e1^qps z6~Cipga3|I!&3N{W%`7$!^c*Dpwr~l(9wxA;$m1(^HcbX12wPc+lNpDF7bng0_M3y zn!R+&(Lh~=q#eGEIUa}k;aB{QjPIvBRKnftJXpMO5EKnZhfW=T)I(r>2+*{y*oH0Z zvrDkJ-BCU=?lw@ZF0QIrjKbbu;J!|a8SWQSyEufkoISm+%kNwX&ha`sLSgZ!QoRv4 z^#U}fB%el8^dd{2qBDh33&jA57Yf`}SNeoOj66io6 zNPFC7U%1GxY_YcOA790o)yjRk}YP z>>d>L*`!{bY&w0=u&3Hb1zJ8gZTq<(UzCttL%D;uNyfpgb&C088z`4=`yA$rY@T@= zHA|wI*ZWMwDJSMvzCP;M7Xz(BlK1{vQw+~z7+-TbB;ZPubK|bcFlU*c5QRG zs9HKClRd}_Mu+q+QXIH9B$-kYZoQ*9)R8TbUkZassemss+z(t;mZByU&;B?K0#{HE zPex-rV(S++I^D4e0KM@p2qR+` zM|v%i1jc(qo>7XTpbsHtK!*i7{J2GD2$wB$=hbDeFL9*TzgB1&hi@(@UCn+tw#deF;F3}6!oO@-PmE<$Y+ks zcTO(t-Dn;a6s6TYtIIop+a0aG9-inUgFxQ1z+=KT4V1&O(aIlgQfZZGk|a@=lE{G? z`Mb&rz)!{lvsXhC`TUWrF8dF^UBK1IKXd*}%(%*zN1Ndv#*NzSRbqCbHRFPdo*ke)$|xv+>sbMXs6_GFTL_H&VbvO~ zymuRkxsZSVKE#v!J0p!&Ei`q0Nr3sv@b(v>$}YCo(>ADaIHZsUeuQFaL9cI{d(012 za_9=H#9K{kaZCs^7XewF(1?RuP-5_CYy>M4x4>zQRv}w>bfmX>@O%$$j`Mx0X- zG%n?~m}^SBkoOv`7`2u8aY(f)%2-?>mNG>vI$@d_%1pJdn|9z(6bFhNAojZuW4tf5 z0@TBY3RC%q+a;`!T`9DuLY$JxoP{qjk460@*_@9SC4;HdmBhsB(Xgi*r`7w-dx#*v z3pJWYQ#86#q6+F(Wa|sV)xPIbKQ7dhHlg5X6MX)o@F2(7N*1b6w+(A9$QogqD+7AP zGy%kVa&xGuXiyivvVgeNdFw_iT>Zxr?+0JbMCP;f6Po~t#n8WLRfWtvHz*yANL469 zAVa2<7LH^Ld?L=2J_^wyz}ZNY-c9lzw5H%4`+_>NQ4N)TozHq;AmC8p<)ov82dQk) zjDWx5LmB#pDDG^HjV0(%A7`hR>hm{Iv)@(sXLz4uJNy@&mg2N*Aj{ zYf#|$6=tyCS)}D=oO1rn7|B)tES=4D2*MYxx9{|`9l5af_cbx5BrNk${1p{&J`g`o z8+1Ww1o;%6yaj8@LSyok21=|w0c{?gFY-y`vaIg&E3ua`4|710SNQS8X^_n$>Yow# z&L<+R)FoQ;+>s!)J0K?XI;LiPN9Q>GAF&Z%6^{oa+i26 zXr$v|XTCce#i|_3WUfIvMK(-thFi8LHKiiE4tDy|za)cCEhySgHeQEE3NMQX@4MopipcSB1tc04s z%!_dUnfR6W_!qfZHG*j?5trEsCEw>DyQWc5ki_S@cll^|LGO<{K@QrHRW@Tq)NrKD z!Ry=4?~(@XUF?}}UlRWyBcWO~t#qG82j<-<%UjL+Qw}UK=gsoG|Drba&b2FY{5SjG zCti`hard>Sg0zTS-Y>20yaCHQk`GoFW{BV_T#G}0I2~4%y%UO=`ay)^TQORUSp1Lr zUP^X7;ogc^5g#JdB3g?1ZJ<8b;-LC)4K}1r-kz*@i*i{*NQX&~2?oow0lP+F5w+V? zGVbqxE0+t9*Ju@yO32qEjDn*%b=L%eUEmADO*&aTAt!^VfRA^R7v)L3#B<%J!cIX2AkT|aR?@XEk!TSd|Bv}ummiF%{@;G)`xHfadtu@ll@ z>OzkOBbF!TLkca^i<@B1_ojP)X3Ty0!l!t~=|hal^}n< z3!>mP*)<-=5syLoxu9&Y+`13%s37G-ugl=Ohn@jLwMWdHien09P1O#IWp(sp_+?JT zBlDv`Jm`eLw_|}nra_Na1C7vSg*5lW37a>77MZ{OO&+k2yzW|=ppR!r=8w?4T38{4 z4xa@e?gnycnKnBEYNg;cSwNVlNB@2@YB8;{xO@o0y~!CqWyJ#hvXG#!3Aa7BaTh&R zSgaXUkL1|(e}@6ya0H@X%^V$_XD4lk-O!qD_lp zzF|`{wRde|?y7fRW@wa0NaQm?WA4dGpIV#^#1sCzaA+fU5qcNxQxpRR8wmUc&(j|z z2A1RBC%v2mLydA92OseqQ({DR0sc39+(Gx6!#JWCQGro|VbV&wrxgknDv?>HQTE@i zrC$f<&#Hinj{w-E2Y!RKF14}N3Q;c6MdK<5PgJoMKurO+So5}Pl18gH-O%b_-(MxG z>nLQD{O)-QtS?6RB4<-Gi817DWnsc!sbE_!kuJNv+12dY{az++x;xi z23^GOqeId`=`od0E`MZb)wmIhJRV{lT2u4gsE?s*>`|B)bdOl~sW*)+ElL1^#kS`m zn{0abQC<6ZpSqoM+pKMv31yC@gpdUHeJF{Tzigq?Sm93<($(@2BrIjYKX!4RU|RW+ zK)qLVyf7%+F=bD`S?2}9l-}xZiN6yGqxXrSp{JPu0K(R$EK^rRqQi=fIHxn6#hsAaix4$*2yV0A z{R^rFy`}bMPe1A~CzaiV2@1H;u@HWWLA7$KICX=c{2RYaBS>U}VG>eY)CR9%4Q8?Q zclH3&H5mQBCGw!?A_MuCZ~W_H30c^b2|9t%UTtYPB1~%KT@k(xy*7*=aISkd!l1VM zFTRVBMCCu!{S-&`i1O8lg3%MsO9eE$W7FO+hJKSi7R+B%y`9YrU07OL5|)l{6hycs zG85ar6osKDD5tb}aiMgMOhr(nj|ku2H8n4op=LqBpE!;fdQ;@B(@X}$mU<{;(2$l{ z!y@OdjlHFYJM}SIDx|bF`CqIeIY(|;8P>XyRKd(lLc|n}V~ke%{4$Th;zI_NU0Yu% zU3pNQ14VrEop_?N+wLXCdmc2>`MZb00CBm@P`tTz7vM@h?nqH5^8!Z~8c7Zl1>@VT zoe3#r#(7EiLRlqd&aghz1T(H8lSNUac%ab-$21zr5EL+R2J0eI4SBQ24*76$#^sWW z(zjpjVx+k;xTOm8zv0|l)tJ?qO6n3#V)s>Airl}my!!;Ho~eA*^}m7Gb)r3E27b%s z`ddf*U}K}XN+MekF2+op(z0e;noi@}hb$6OFehR=!oO%RbIExmHI;DSp;N%l6%TGc z<^3GmbeOg8iF`q26Jiuy!0Hs*5Im^2Y|JiAWgg@Yl~l^Iwhb%cB?Jc^kO%803-HC@LSupj>2ozK`UIL$#M8gp z?FLhD@(^!!rmY9<779(GOmp7arX2te3;BBJeDJvoOS%DZ@Pn8k!#Rw}l-=EGFpSsk z*zEAcuJN?n>xG_8<*O96*yts5VE`UB+^3;>F$gILfnRPGr@zggz@NlL4Ig()rfLq~ z*RX#;h#-wiY0pqucO7ba0H0jA<{#!cf|PSBk$=wZuM`z#x1((VAIGvYX_t5>Iq+=& zvTGWB$am};JTb8Oog-NJ`jhYU5TVH85+d9kDKYP@HUs*EUH((?GYCW&@w{3Sl|tna z3W2nthF4T@=Xbk*vV{agGoc}hGvH5`BsY&bXHhzJD~InhBFJfu!^+prxdptYwBoil zbtW{24I<`m5z4MT8Qm;U7a$p@d2M~8>m$n_R5OGVxr~jfx#+7a4z1ojF6g_2`fHBU z<(i7;NweAS#BELgbFq=ySQNhH9O`;(zR+GRO zflTxs-&t5uXao@B(lQ8+d&9I4ADtQH;_DBW7u+FO!IryddT0@5?8@fpS9a0L`j>!P zQN2GurjUhc?E-bkdu|i#tIz#W$QZp;%5aoWU1p`rS%O*^qKXp;TK=lGDtsvO*AtZb z$=6s~(J)TA_2%)#rKsrl0(SUo_P9^wEPGJ^%36v1dC(YqTaprScDw&iI_nxWhl}%X#W$~8< z*(_}>2aa3>i&0fV2JLntPy^Ah&jBs}WWz_Ht$_jQM>(J154QBrZCq&mCE`PG+&MuK zm8yf^uWdTO>+j-;5A-$~ZZRGoa|_lvTOh6&PuB61dUFbNMkKPGT^|r6WYLnJV)uD> zRnx+n7ZB>LzY8kyXC2mkU7Y!-cNArU$CcMs5_+N1om1kX2MN?x8@t-2c8g{CC!Tuc znuvwy0U7Mn11$yZWL@Hk8O&!*Ls{K^cG4pxwA2mB+WPlhrG(IwHqt|mwZ<&iQH=O# zbhD}#dad&XxS|cj>5Q{778xoVLyAjnK9c7B)e0Fz;bS~KQlWc?-VSxJ2fbXavhG0= zM<1FsXt!El(+@Q5;Bs0d*TK6ISj9BkW_N}bEk|z#T(TVYg8tncYx)E#)J{HeEWuq@ zWEEoC3-!CUtCVQC;|3CHC0?0k;2NXM){>Mxe0}DbB{bv)oYi_s~0<3s9N= z!v4p$978}wauDW({=TT43k6Ye8w$8cx*xH0^)St&r$~m<8QWlXZ2BK7DUz?6Z%4xQ z;m`Y%OTZY-i(Mz!;a{U3pCT%&ls0G5ynu$Sdd)mLW9R|t%Gv4R;oRW7N(=<5ypxz zn*RfzNe#<1?zVj|JK&CktHQyh%@-y4XA@)`+1mC#?f?%XTq_T+Q0Cu%(Cn{y8T+|$ zqU!VMqX(}OHSA0k!XYhztzx@Q=UT*S#XD{B#)NKJ{xj$nO#lg+@=J0&?Rg5 zFei#6sqL5}XX~(?ez|LM))Q1{?wxMaWs2>T20rRO=F(qMva3A&Y>9etd*9Y(lSyYsjgfPBoH>{|E85`#%hhutOv*xEkT|p_56u~T)6UWoa+=$I)VYeoDLa<79 zB_o>~MtK@>hScBkVaW{X?U*V<5y|qYbW5pVL0dp~EP{Q3=6L@J#@eCrwo-XaW{8?6aw0Z2f(L;6A`)TV12sCd>Slaz zVVCfbqw;V1CdYj7)ca6PjJ_Yrx~rW1HQc@R{obX*t(vP@eIAdFNeSQAMy=9)Ot%<7LnlXiPn=kcr)Lxh%#=V zHAQlgpI@$HoPCkA9CBKqgKmUBN3DmyG&8?5NfgoR*trLYgD?1q$B|`=sj4$IiCPBT zp)$H~I%mR8!bf_4`86pb`*kbvV}CpcbY{O!vtY_QXx$SgzlvT>*FS|nsu4LILpIg5 z9=t>ZTrZq7aPeYA);`n&YKD^iTu4x!yQxC2JQC-39MkbmR+0k`^c|7PzND* zuzZOmdJSX_dvXfrCIa*8p`8>;>O?xfrj}ro*5lVeL?^J~47}pbSUF_mG|3`oU9gsp zI$tS61X+rv?6jQ3IWVHw_hxJXKFR9uXb8FLFuu1$7FhVsGwCgWCoe>rXl$uFqeLq| z6InBqw0XVFJrN>BfmtD1bRZKBSo~@!qaOK2mRvaJVrJ%~fg}m+wnxkov4|@c4X?I@ ztyTU*%-v%r?Mfx3;4XvKxtk?ksL_6LW!%Af*o-NtAU#YdMg(CGnoW~l^yS^&=j}3& z(&3{fI~Z57+^-d~?6F)gf>|j%d7;n@T92)g^}Q73xZ$_&o8D5o^7yoSY2pfu@!~+2 zIOU+p6R2pm&|YvIOhn!e<#yu;nhlQNqa{=80z+UYDjp6Wi7eOC=H4nVJqEc~Z%I-L zYyXP7IB=7VVe>huIp;yDR?)q*D^K4_G}aMV8F~RmN|QCYo0MMqlcy#80QGscwxyq) zJ_X9Iffue2;ol$|A zsa`AhKlvuinEvZnD>R2?29_eW{=Pr{AnMj}@hZ#CnCp&+;SSgh=bba4!~%VQl{PED zdt5k?LHj#>Wd2l82J2(z)#KLsrrG7?9H2v7y$U(XPyO_xus%pU-dEusA!2FZO#pua z#oe{}&AT%@aL*4(nG_$*uX?b>M{zFwz@O^EQG7gg5~AIV`QW56H$^(P;f5ze)5NuS?zSd~o3NFx{tJ4(l*SPP6Oaj5N#@yw@NdC? zcATLU=9+|fBDDlMAa2@6F?kT=)|=xK2ZvWeKmeRgZ(_iyVSsI0G^Aae9BR=+4|5bD zAFT%gPZl)IgbU(A^8zh;X&^_h{D2#c@ewNa8)-)pt6A%wAf26^oR)B@cx2P>8)u0Y zD&TABlvhp9?obLYIg2$-QZ&yUoGzVuSjP~X3ax40^ut1Ur`L1w*TI9AiNe9faNDui zIKYYQ-ojMsyp|_YcPHVQp~0I3Ztl zj7ypf7BUm2vb(p0iEXETAV*3z!`^D-Z|2E!#PZ@JxHrTGb~`;mz@opOT9z)s#Uce8 zpR!OI^Gt<$X(qa=IoReOC|qIVx%SwXA{BQD_@%;OzMKR~#DDj79uBdQas53sGlc7m zPk?cpPfw`Nr?#$#&L#xPQmgj+Lszz4goNN(6$!V}nkz($JzE};eRY(Er(aDcO?6sVtZ&Gw$nZL!xPlrmu2cW`55N_ zX7qQKN2r46hC5SV$G7W@Z?ZuTy^b7Nm-(hLY25PLY6^!|<7!-;1)M94W>ZkbJ4%Zj zWar`|MQ-P3o$ub3mt9}a?}5I`svV5JqZ$uC*0r{t%>H*6{@Sv^1(hFP-!@TBY6~X$x)}-gL*!g?rbYVh!maTYJx% zguv{xD4*W)39!`2$9&s?Q(7(Zw;FAQ7Ud+zxTy3q8cLH6x$`Z(w5L|!#I8qg+0k>V z?^0=4; zB9-|N#*@nt^%=rw%84?->bXA!vV43g5*6)4?S%nCe6CLikp6HpLC@XXL(=j z5^>53{?I1?2tb(P1~s<~zvc#|ZwcE$91ag`e6Z|AptJ!4Xq`&4M9pz?Ftq&aZranl zv|74_%KZB+WjiNPcmN-n?z;S|jbaO{&h=%EzluPSzL7OfV0ADdAlX!IRs1r=Z1T;z zG;>W0xGrru#71Ayu@XKh8&WN_jH+E~H>GE> zUBvv-(;$XVCznGV$a}CQMFUgB)ppx*lx@>YD1W<$mY_D#sKjvMh&_nWvpD1{e+;@! z7^_^b(Ltsj$3UI5TLDL7ypOm;knY=uRzraa@%VYt8vNL*vpUn0G5D8JU3e{y6cqGX zhUu+Np_qamTFMWK-J}Kggxc%77D#YsR3k6DPo0ZTRsUi~!yu}^;JtZgv`r^{Y#G{P z&j`I|w}UcuYVhVKXjq?zOHyJcuL|J{S@v)vmEIm+TioT>BczF@li7oF0bbnHPvyo* z-xv+Q7IkL;-x~(amL>$TIb%gbUGxOrilk*|zxG>4HM4N+)Zn$Ez&J$J#Tn3d6W>MH z>->1k!1nu?6a-B=1m-<&QjrCyVXqOIo!B)1K!5*2A)t|IJ$_9b?Ka+^+b+GdsJ0pvrv>upjj&A znH(GbdWiSw!?ojj0JPxM0jvi(k9;Dw-%j_0O&p^pYc7bRK*{})5{cBM7<*qR2Qv5g z`=g0sD9X@w%KIbK^0fGUf=87PsdllHRYJpMO_~6=(dCamc$)j4;S}Sm@;vVz;c-ae zAHGTn>)%|NG>40x);Qc>HpX!VRAI=_QWy4}TFc&^R(hKp-Ed>MOuw0X`0jtsjv1nN z2r&WEgV1m<7SgY`l9GbyrhpO-bvAJ0vVr=ti~fpNRJ<+tOC;^(7GM?|1zb%M-*tq0 zgAO;p^d#mk)+S&h#!fk%nIxPi9T)y7ng%@1xM)Pk3?OOBm;dA>c}r%=+rqz)NaK{) zb8ard9XXkeneXH9Ju1y{G=R`66;H;@y?8|aWK0~sIdX=RHX;w~sHu23qHz)TFhl~) zMeR9$60=1t|F1iP=67+Ta=0KK2U6HCTRwl>Vg2oli5TSJbi&+|Y&REgengnLFh5fd zk#9X=vt4|P)|Z1A0;aWa@48du55n)`<>LYBsupp?sL1S3A0!D8RyVI?(t<9!NMCqx zhk6~cn_nWZ<4{h@_WKl?@eSOZq4qP$fd>)N)Oc<(NzVRHFTFVhMhI;E%?_=K`jc(i z(`dG0{#=}9pyW5d7J}6<^#P;kt%*Y?>o7YxS+J}WEL39BmA|^{*xZ1aAK*WDf>MB7 z+b+P1a3E3{h?40V+tC~&0*>a+ApN-o*rhZ?I<`a054J)z{!;DmHRJZo_K9|sgbRbBUIeb(i}GY( zlppB{t7oynzP3B_Q?%^NCGM;&@P#4Maw}HBwxYnHO)rn7j&zpm8~`YlnX!dxZv3q z?|y|`540OodcJv*!UBhQlixcoPt{#$%Hm*@g5};q?8o96H|1BQ+8CAmqIIBs4gG~s zi6&GI^h3;rO-AJ)f(HM=Luo{6;gOH`0sx;c6nT;UQa00P>*?WecQ zCv~j1S?bo-`*S~M%ZEn$j&Px0-@WIi40HnlHt#751)d_Y$k=5bpvIob6}6Lr**2gV z#&OJkSxX@%9z>82IZddq4dfp`50WB9L6qTYqr!{i)7@ARg#u;od9lkBFBmw2$B_*; z+hcb`nHmMX5z=)3Pbcd?Lk;6|resawNTT(OS}v?soHw2JBR^*d;<|S>5&)}Fp04TV z6{0q6Uu{>ejLO+OY`=r0siz)vM_smld1@xDg>1WCjN(IaLxU|$?*Z*!-YCDiE6(R5 z*U841#^IF@`CL2SzN59|7c0H=_G4jKt}F{yCe)w#5^}XYtx_vZ#gK1*CFA9WwccDp z*Mp2p6o`g~0*~_Z*r+SpU88Q5GT)pZ3>FSv-O2=uH{XPDhXWB-)lsR@yqhUEXCx3_ zSLHVb<*}H0z=vWC$7bPE>=ni57`y&bucY#*lnnQXpI0{gD|NN%;E_qa6S@BB%4yey z{e2dJH?8TP_uQ3vC>surQO48qaWx7Q*MvQ-iYGT_U12W80<3~Q(Nk=l4c!a`$Mm06pH z5D8dD>TCj}SZTf30;A?SFKpBjSftf$=TFwy?>!f}z^*5vAR(VaW3 zSw|5@LloLe2))V$S+hrGr~V9^3aVt(A7}N@w?JJ1CIPOjHQs2iII^&RWL{qdfH`2h z=VIhk`{Jk0XaN(C1RMUlF*8i-&Yt|9atf5TV?-se(U8-Z<%^`tPpt;PM9pnx23Vks z_Kt*aE~$!biCYZ%lKbPoj&Hi}4c?3VqdDB4UD}uV54MA#_K3Oo0%s}1Z6_KEcxRO9 z-To<-61TCYYdHnC^pfE1o@+%NRPS=rt9Q;t4v2SLkN~OWE-ASzq5!>}QhHTYl26 z4$RWW@y>hEio5i*?BH79fc4)ygc`@WI*|zKXcvYmBAmL}7;j!&mFiejx(#%hR80@{>d>9K|O)W@ev}w~@DU8v`67 z*FLGv85YBj!4(TY?uN;o!f06zNPR%ey((AnU9ZN$%PK%OfBfmWi3cOav#Af~-Yp2| z`ozCGKbbJ|^Cwts_4I1MtkP@(w*qUMdT}>Ul-zpkGyd?zK|{bt&ZQl5yy;jG;N&=E znwwDVh&N?o)UDc;^s!(j*BBC$#xqx6rvuL%t~Gp3f?w0~`cBzeNQbo%^(n>gIeC~E z5K-wR0c^sREY1;R7GxRwvGXglNd(`DSbxG-f8G;%>!8<1`VBw!zBiYhhz_G@!&8kv zpp2_&md|Ntp6Up`zN6rVZh*LqIsdqcl{v>*{R=Ywlv@?k%g=;)hO1iNhoa;~fgut! z`I#~S-(C}$f!PKOcIjyE=L77KY3hXtNxYBG zF^z{=M@O`K-lhXK^*$^EGKen5Yuf#UKw^DB~v5SYc)8Q<;KFQr8 z^iY^V!3DX#OKYQI`UQvs0aVM6EFV|B01E;ls-%DFzv1UcJmR1`|2oD`OZ1y)5f#@`@7>-^LwI7i@G{G_I;P7nAJ2`kb`0=SFwEc(|gmP0i}|@1m@ffC0?}4dWv0- zDfSF5dOp~c2VJ_v_++b`iqxjkmZKgkQ!VwFG z&r!tku4M47w)LUg7rB|IIV@8Rf6tQ^ZU}Q9@_95Kn`)kU#036O|1(UR3ga|Tn2>*5 zvKI5_UncGs75v_EKp(xB^P2+=oaS+={Q8i2d@P8cKZ&ctc8!JKo;K|~ z4e)r_UmfY|iv7Mj&$q@E8%iuJ_uZ2rQH$F}NX(Si1=D$Z%Nyuq%N1kwF)SU}V^+qhP4gMIXgzlH-%O0}^pX(&v zr~aF#^d0(E)YaW;)$gI#k=Sc`rswQ#jTAFZ99;35h)2HH4Un8<*XnnfPWixWIYYsA z>4`c&DgOfoVTN3)G;tn*r7_d?nN{o(^TN{PEVRGD;q^)-1XHGS34p4o0ICrduj7nP zIC4()yWn;I9#%OZCjBv2Z23sh)U?Z>S&Wh5b?MzSeJ-}~IM{{C>zg|Jvp23FH3gZs zc@ji#z__TPE35v~aMWmYp^G4MmNG0aEGGik;n_Lp8$40l1N{a6_%sFoq9%+J=>)8r zQzbkotlm8T706ehh{ttMYFg)Rd@Q?4U{8#(GWOEMP2(_81Ki zCD%shU+4WTa)|RWp)b6KR*Gio<`)o|wxEnJzl_<8t4Z#E@;gd&=p*0&eUkYWx8Muj zw~=^BU5es5-(bn1%O2s!7ns_Ma3AfhQjPr1(Xd%pJ_DgZ_epmGtU={jzVk zxE;s^-S~M^cB^c$X{;p{!%UpvJmy>bi50XdI%q2A$e)XHF9=)%AMrvq0FE`94n)yy~1MZ0E2L4hml zuK!YOHI{) zG~zL|9W%+H*%0x`M7yud&c_I-Lua9^ksXR)qSySCqiH(?_SH#xYu>E|cx7hFFW&`VX;C5g!V4$i5%~`x`?0;iJSv zNC<4BnriNvT-Z2f6b!$86s(iX91D74dCkF~_y}6$vgWFA!FzAx6G7ekt$+3@N4e-am>iu&{$egdkecrbNOPP~e|)P{ zxnZbwd59Qupn4b_-6&;eBrWX^Be-ylBTe8NOtrUxmSU4m#^Kd?9b;I?;tQ2O#h9ni zW~tD`FZhU;L~{wIWa8HBigcKay5wbcJusg-dq!LFPT>=~%;fV0+WoX`a~3-t{(N0C1IhyM(T!W`WB9b|>?m2`I*LQJTPNgU&!l#pL+ z$a24j@5r6|_--21QK-FVUB@)WvsO4^5e9IV8J-k{*y5#xs77~6^6h$_UcERH>0X; z84F6;ATesnb+8WBB*y_F5xIn)Wq%A`R4S}pL_d5kuD2U$@O?-NtN2twuojfw^4z|L zxtoaf99opzMqKe63n@NDdDZ0aZ5WNdJTNP;3(eNHWrh7wI+iE&2Q%{hp+{1;F`{ep zklzNvq*-!+PPAtuyVDAVl31E(&IUvu+I?nl$&6Y2$ht@3qBE8qwm$9Fn$+SD{QE1D zS^(PazZnV(soR905#(~p2E6Y7_X-o*b2EbBvJ+8}naGpawbpJeluyyl)^D2k zTt`Jm1YMF@yP~-LCl>OT$Iiodjpl@Z>wR_q2@zq&-|xRU#=fui@v==;r!8PX+`&*J z_Dp2I#?OiT_%h-)<~7ZAZi!*W*Um%z;hUn0Z4+E91bL($=~XXr)mS}bru~>SGiHAd zH3i^k{sim{M>j$8IGC*Vc_)dO;b8}z$t)_%!+{s)fVvW@&zvc{V3ke z|3_(Ei1~I3j0xEc9J5nzWOQBB1b0F{v#+0Kve&fay%Q;dor+A2`IHcwyE6$dV@?4n z^G7lLH{vjw(A!rDlwhf&W68yv z5m$tQV`j#qgh?^ZhF0I4+r8{dQSruD30;1S9}l&7=Mg3dbTY>SvN-C+6w{jg?Xk1L zSSiKQIfe|nqR#GYE_0g~^Aaoi#6$iOyjeR@Licgl;} zgNB=|POopxxMBH}#HnR2A7*b?fkY|{$2hifkYXp;+A5<~Heo{*n6hf^q?E$O5*`w% zDKLTbD7W5pPGBiCFs=V8MbQHn;LBD!=wD>Q(xy(S(4DMlVlQKe>*!dgmGYc@5;hPY zYtiKEYppUF(=6rlh0~9tO;%UwNMSeN_HD)%d?7`L`Pm2gm-^!;(|-}Xq@7@s{t<}+ znP|86_?ds`&kr8q20slkZX0f2I80kLa;%&HY5qh1iu_70EU%WmQSH~RI4&4TtFtOu{)Y@n%GLkS!tvL5W{ymb8^P*2NxYbhs5 z8m4Hd!dMwZe)%e@_wRiAz-n_@2Wum4X51AejdmXfCHZ$VFX(z?COlBUONEt;DFlfp zHje8Aquk5*SrZW)b-*nWnM`U6?GHc)k5rK053q$zobqS z`!1dF%9MK_r{S;i6$SZ-P-H#+)%TCir3YKvDK@O&X4nybCHSC$J5{3gf)3dD;*|Yo zQn=1;cc}E#0P{r> zspGLrqnZdr^qGctsPex!^!lfXwO-6Eoj0(%;#-cM$&@dV2^~K5 zv;Ug*vW}m~dkdj>CR%YG!ziM<0JR)`vthU~Uq(>+uo_WGU{m*mn zOizu$8HjnBhhBPwZuD?>%s(!E?KfzQHL}6cp~kF4z^*9#t#?lHKc&|UPfg!#9kw&9 zG7f?!;zaW)_n!RE!Ij4|{kUA1&9VfJXj$Hqvy-$F4I!alcLF z5T|^H?wWsAf$iNIb$Oj}#oC}-!vif%`c0`#IRAeOyd%V1Ab+kbyGfb`BA-7KVrFyv zzZ}_3{IpZzp{qAy;p33*rjJMEwu1iy?|I4fsulrQenhWyw&CoIt?}O72mjIiNY=K7 zLM?^VZ4k2p3iQ@}dGJ4MT`AaE5C29S3d1ajEQz6}Ri0%?8ijPGh?z;b# zYwo?5kKk7Iw>JTs_CG-6-#AYs2>9QF?H9Yl>^KZKFkDYtp7A%7cu%QwbwKYuP3BYS z_h_W^{PqL>ZEfR#=Kt&03t;bwMnf8G#ssN1-C=%NWz+x7q`%898v=B=Wk#Wv^J_Lg zN`~Z-?2|HL=$%*1vJ82}Q=#J&6}xvpi<%$H82Jw*FB~HLDviltU z=wCe3GpysW^Y=IZ&Cdb6sMxKOxiYnx;|lR?EJ|l@qIUkjOjL?^tz}O}hoWwK#&CdU zDnTQn)c*u3j~$bI%!~$6lpXojRL$%Bo?4zKs{n)}nJ`u6;PuyYd%Nk`46T{4c9_*OQ;B zZ(;Berzx^)l_=63TjTlvTL#-_5$(}VudgMU_P1@rho?WFOdatB36XN_sc8Y&LJ6&> z67qS>zTToBU>|UDT90yHR7=nsCf}K?XAsyN&<6}Q!o&0&4=iyv)0DfWwSQ0Teb1Uz zR0ME~UlJ8ZSJFwl6Gi3rTYqL7jdsE1zb0%MUT+s^w)SZ>mKZCddGuui=w75S$bAv5 zsU;AtndisY8hc;1HD5okUx5{g2(XS^6*)h5qz-=<#5N*hdzW>*dk4l@@ z{YJg-B7n;o2q21)yK7@N?t&_jUBs}}IK9{2O}yyyRxgzI!i@V1MiWGi&5Ge^IK$EYQYZBbIMXu`@3vD;lFg6!r7H6H2k1qdA6?6S%wV~P zrxsh4i3sDac>GgjTDqxxyU8p$!1~xWT)~+{{;Lpq*kHRw<3AarbF7a{M598nm$&%|H<`)3y3kg_e zk=DAK)~E!m!B2{REnY^|Tz3;+_Ys4Yl-BIw0s1Y*Of*1xe07jC2Z*_+()j?%f=PeWG3Vub+_^t5?VT zi{kZ=q?s>Wxgj@%=*O&dEXWZw^3?S{t2c`@Q8zN|yD)2Sqgj*Qj89t%0xWo_Zq8z5_-5(*npFBG^I6E!9s*pkS;oI42XPi>CRO%7r5plK1eWLGt$~3 zNkpSR6`g&?gYv%A#Md7R?IIf^c7eUR?_!v*eP!#1g_udGvk}r3znLh*d{*{OIAJ65 z+2B?L?c;2a`^Mw3I5Cm{r#ImVZ?^w-i}LZRoAG2IiA|~g^3;r+u?rjMh)0ST$Y&$SP9zXGAB%5#kIae*KCYE>k7)0Hzt& z1c_rX(y(A@>0z9g1vAzlaSvm4p&?_x{`%2ahWB#yl7YifI=!X~xlgj3m|@9WCU}fM za-x_?InqzgrZAH50zhKftZMTlJ^=l*x_`Q|-;%TG+MSYsL71L?$WJsjKKDZ7n&KGp^lj@{`%xw(y~MU zkR6b0EW3&n`gVk0pHTd4t*)Ik1IenAxp};Yge12)0gurq#2i)UX`7@zB5)F&r?og} zLdZB^D&Y}{L7z?D9y>|?l{YO(ih@k++>@hC3BRT^qIZ#>%%(*dGG1-PqbJuv2mkqf z*{zp?k_5#jPeBOJlH6Fwhy#2m#&jqXSvFdP<$>4Nk65#C{D~z3^Xk!9eE7D6I6bmc zd72!7R&6E1r@41%oLfd9d5~%$daB7{uh? z>9<7iyMARKcr*q8k)2RD2xHLmM(%SxcB@7ZmF*r}J!44Yil=K};? zBv?t2#lgA)Cpev;cMDQTdDfFuvjIuEBe7jNyEx*fm)Quyl7PPVMd5`or+#nf`EUkc zdQJ*^T!h|*g7LDx*j^Lb%QB7Cp-Hwg6N3AtJ#*m`)a_gl!2w&@hBq3 zrXvD>1^tO&UY-WOLbyTknwa7PElwX3euIzk-%7}M(;sN#^h^-GD>23og$p6zmqt`Y zVE-gT2MRG-nd=R*L<@lRRzk&_!T5A;uMm7uW-azxsM$1sr?1*9%;5XGvX$Hethh%( z*Rvt;?jU%-)&eFC27;Jt3F|dB?b7gnS!pYaw-e|9Wu-#8d07;IKJ!4^v_D=(B_}_p*j0 z1}Xm*-cT4vlY(M)p5GsMk#8UfgGo)oTjCYmCNDY+u@(IoDe9 z4Q&2#RtoXC_j~Y(MnRSZ3HRU!5j2`V>U5~zEA>VW<@fXG6us3azv1Z4dPaMxO40l$ zmqS{CQqua7fA;>&>$rAH=3tOn&&xD~=`|pI?t;u$9oU>}>PgG=YfgmfZS~7!xa3Iw z`kMp9>g#)d{DlGhNJKGx@7{txB_aOCr(d+f3?4|xSrXl1p{F4YsT@tJ`PYr)F$IA$BCwH^;s>QMz6HZwN+a`}SAbN~4O&aX1T zJ0fReL%iqL=^Ke3nATy<4nxDctvPylg*pt=w`RlVfK6?6^WM)3dg?=8$ zh?yu_)UVZw#-FT$I4Fd<8qfdt;S?(27oK+&?^N;6Igava)*ZGjraKnt(@17-*C^M4 ze@8x|TrlV1uEvW3-Z}H!$i*K=hbxqimYwBJSE3qUY$pv^VUS@4#>a^d zltZ?hQt-IL72yJ{ym2UAaMT-77Cg#>02AoT}ox-Nr z<6zrEZIh`VJV`uo&*HvfjlZ4t8^_`VaGqPIMCi@xGLHdv?O}!Alz@!&2aLOuTJwniZkXdFF zD6;jpBISKC=q&SIC?DYLs)qp|iaAMqVdkHIhQ2_m8Jcv%Kuo%7zZucscN% zjV`X}VM}f&bx#GlT%xn`Ln&P}Z-rl;%)MyD{S{CY7PQx(UW@cO=YQ()NRaUMP&e5* zIR2-i+(uh5@P6fRAA0dDxe`^_xGy_jGVgmV8h@;Tb@Mn`jkjE^l56(s6WF|U?(XFW zoiKtf4Mx5KN?QgLe`Bi)aQ+CF5~K;B*Ik@;B0#ydm~;QIv*vxADC~M}qau*|sj&VV z`p0AS` zAXk%XeUeA$X$3aFaTtx|>+nBBu5CQHdGT6Ubd3Kwve+NFir;KwtetJ)QuP5?gxjC# zi-e43M7Si9s|x{AGxI-~GjSw7c$ur0B#`SRa=R(}U^GO1CQL^GT1)jQqO*|cYzhxr_?q8nSqJW z9cUfy1WF2Z(E(y&l2#XJK&3(c;kEWH7@W#p1b{wR%4sVy7^Bi*r2- zciL@XhU7OV*`T|il&7vLTmM|&V@R-(o*=%KZbIc@AQ!3zq#A0hRbf=$k(?sapLFfx zvcmQ)L~72fbPewW2p48yS;5smTTY<&OWd#iu)(&RkG|RCR53?MsRwF(e*`aw{;#==?nn~<6wgs!!{{yq0LOzIkJ=VW-o@woqn*E ze9GxE3wCZUdHeQXT50nt-?4qr)8_Zj`j1i$_k#tXT!K*OzO>X0G>E~B!;EvhYYw^l`@P1NHmd5y?AuV0cGe=jK0|&i zGX^l_g|+3GTF^PFAGb?Xr)mT*^ltLwZ$T{DA}=Lu-1vPk;xg-*#$m+e_a=Z}iu+p= zk$d?wm_6aRHPo-BBV28<*Lx?3H^C^x5z9oTP7T~I#TDT z+~ZourzJ}meM32_C9f87eP3sU+okygMRMvL*ThOZv%?oi^__anwLPE93-_GoTv@Mx zZap+#3c@<0r7bL5FlLWHGoo{;jhL+L35Gb)HPw$&f}IkyP0bY|TJ#NM zdJB$MWbG)$ZOfe@cd3i1y6_`d^VxLug&z7aWutq?P|b&*^!zu?V0TC*7*))Y7lBQQ zSSTx!{iGtm4?wW0PvVUn#J%4`=V9_>Lmf?;2O?ry&#K|1mK3z(n?`;ZhN++iNudC{ zZ2I3(W9Cht$6&2_0H<5Lg?5pu6`}dken+6CtB)?JkRNnuD}M`BC86v_Ws)Ilny=IN zIhkASSF=-W47LMJ20mUVD65PBpklDW<-(NXAk`^)D)sfeu||X!Ch|7LWXbAW|5Mc_ zAqNq;=!zL-ij^$gtodb&5%}{uB)EHp9e!~+pzA! z$=n|g;>yR9_O5ML6CX9tx9^c($~S2FeAfq*b+C22WQR@Sy978FKYElJlOa=AiajEY z8c(MFNXZVE4NJV#;xH6K8a_Fyn%Wq4y`=2v0ThCkI_HglC-GX)2jnUBcGTf7jU?F(0tcU38irUpk1@?_V0nQ2LYtX`ctsK^G)56@a z!!zxN@I-uQ4r!Rr-NW&j{%V36@k+H#uJj?g{0dJb-Q6d6Ta`!v4j$F=u7W>;lpf{g z$xje~%M$>=QtWRIa(E?8q3;wQzHZz?W_xS$T4WAl>C&xZ;$7jH`R%(mRSy&9 zBXr8YBAr%+QvW#xoj(NaXw&jf=0Qr=VNNjb+zNGmN7e6Ckg;YTi^dNA%7067Ecruj zwCFbYSTE|c{gP6=>oN63XO&t~m;A`Qf6HZ+9BW6g;rD%HuVrT*dR3ewEC58o5-T;x z**ALe-<*oV8{bn+JJ*ptGS~Sav|usewYHMhfT_&=ji&6AXXJ5mA8^b60_56Ts>)hq z@=LK^^9&w*qvxu}wAZJ-J- zBO%+#-a_!WwG(RV6*h9ntD^w0N_tJZ1^jk>K*$r--O$1t*y;Iac@_klEUVN_z6#cL z)I-Vb#IQ%r+mEuy!|{%oiXbD3DBE$+;O1KM#-DAwBS9H}m-J%4o`pa04Jl`Qos6CF zFK$i$FixKfeYEn+qgkWx(+x(k6kXv@QRP+4nrllXCNkW+PXCelaM$XNnxHP>?HL(w z0w=E~2%?4v;6rSb6GaTj(j}avK(zJeG07_U>QRoF-f0D>Y3hT(VT+-drnB6qd`P!X z(oMy>gdy+*RyYH!v#h?8xd0Q}$*N+DR0rrz?_TBhQjHKwWoG}k#=AyYcJ7XyE zO4r`H8Y6SY9FAe`0wsS(lDyLg(-7LxkP7wLYt6`La1rDpx3*@q>ybHaIGR7``AJB* z*i7L3p-maIBNm#bpK+}Pm+xX3`0B+cE45;X=wFmF2l(QAx4h< z?h@DlGIMi%`$Nmr#x|$2;3zb_WE{kHTTvPQ)m|;&zJ(r*MMPE9oULi4T$r(o|6yfH zy3qYETmt4%Pb)^`62mOI0jU_*911xPjt97#Bn9M}&rI!)NmEEfLj_xejPSG$V^Pci zE;l(T0zk8%7K$oCIMo_G3*_!Jcsy#JGH&|*^z+wWSDSzSQ4lxtAHo~mF`LSsXUjoA2q8QKsi5=v10UIl%)q_4kEeLY5gVe$4F^8SyBmtn+$r z!lSO;>7R#gpizn`ml=HhRMhrTyt+H~THjq4$uIRTZRk^?&@yrNO71;y+fh5<(Nye^Ox+Z*Z|sO+uNWF@NbCha4?$W|F&!5@!mdTijp1YaI5Pht$h~*-rjL-3;mIy z3_|2ng~D-~Wg;Iirk@yot!6EM$JOmxBwJO3I@8Yca|$t3;(j8gg_h)hC?ec63#oqQAHJE;oz1P4Q-8Q=z!mtce?IvWn*hkDz-PG_#H5|fg*CNy!Pu{ruK>}v^Et!9>*izw00ri+< zq5&B=%Q6#Z8Uz!lZ*IAEccq-9@6M;Y1>nAUYpgl^UXu-es$bR#{hLo&yYa|#3F`=KQBt@7pSmT zVu|#()mFF1J^zOUsb7p9jWDZOUR#I{uMUC3P~)I6-OWRt_*_Ed(iz!S#&^esPYHed zL~!P4yqPU-^=^w(I}znXa&R(;fe2$;ZXyuZdCUoC^=)+?mMr}$VJ;w{i=t=AF<@~H+wV=6CJUh|Wv@oID|^-aY&?Xsk3}0JPPPDO&UEw`56r!z6pqT(HcPMJbOzWv&Yo~4OoZMB&r{zV z{z0UJkDy-kWW7X3ReU~sJbFymvk&Z|^#~g0y7uG(`K{J>0dn}CXQVm0#t~F5J!HiS z6Z#t6oHC}n^bgu@h(4A;rU=muM><}_@(vPr!_4-cS#&i6(Z^BD&c4f)31_PBd7QB^}f!v(t%@k zn5kaY-;0Q`yP9q;lAiJ(KF!)opi(WKNv+mCe9Oh!eIO*AkTcv z8rcY~HI36VRN*Wg6*~XbQ<=$_&OR6TQ5xwX1wXi_m--YJ7pWT!4w?=*Mr-xZnBC>B zUm|~W7B#Jo#v+5;d6>pRFBx0Ya#-(fA^ zJy*~&8~~tG&SDL33%?c-ix6%Z7yzE22+m2jtvJ8g_w$YWKa?3SJ=qy3xXe9NZ;&+F zV}7J&8ud(p%ucosUdGGS=f6`9I7Rp6%|PN_B!<#=;*e$JwQNW8vd)MFm2acEB6LAJ)U9RwF|S^=iUbZN z|7K%4iwqk{+D=fEzltkQ8YP*lx9qxd6 zB+B~+jJ6BOt6R>;xqu(_X6TG74FBg$z=Cf50e95Sw{a)JB4Y>l&|J<5W3$G8L-LJg zjUhkh-O$j5VUPAZTg>%ve;e{x#z+c=?TGk5atqPi-bi|4{@*6RV>E_&w0Hb*s~Z)BW@Q3;L68B_O#RAqW*<9hTG?HpY198 zE!j#E0A%X>-2Hv#+JWP zAj0%OOT(RJ4=HkGHKf=>@diuA*Tvs^)YxWDgh&JjOQ=~y+QVWtD`(Y5xZ6v*23+Uf zM!>eGR|ug642wQohSh`Qe?GA+Xx7vCJkN0ZPKu?d`Oqe-8~{(gq^q&qzMG?(X-%uo}l z;m>lp0kAfn*Xfa=-U!M(xGBwwP$Umg&?Lx+cUN24Y%g+gWys{xfnYxizfn0XAELcSrX+{nn*5H-N66 zc86FLXxSuCe>Fnth|!i>q5ytnonb0Qmtu*9S%w_o4KU7*Fe zV8%HDm0o-A0zfrz>5&WK>4V6HObL{~obA0nHrXuuY;VuuyN6i9GpT^4sQM+u;S|ob zgf+T%bGhaB_?t0GWQlU#gW2^eoA>mpKkW|?O`i{+^9T`>AD59_%@ZcyOmSMRx#EcXVz56e2|jv(WUPkPK*qLYZ*gbjyQK<`n|^7soA{ zINNS@lcT7-hyCb<4ZzQ7%kq&*HoUDYplI|vdpmVV2>lxKml4bb)A3M;f#tRNC$oGGeK6NG~x@+|J$L4 zm6NYwUq#Enb*w)8F>yRCF1+4~6bLY`{On6c{O$B3vEX}IcPo;4o$c2w83 ztog}DVSIj^dp%gt2w*Eev`z1p#T2xo?X+QJ!<0`nf*M-P=-vA5UV(J310k`9c#8< zrF50BOGj2SuFfv+o*mXYqYzj?9dWGvuDaXGLY@eS7`81j?1bd9;&n<4(&OEd_;b zYukJjVP(Zq(ktP%IAhGvN4p&)5_Zn|_#IwO{v8M9y*5>VY$0_6JSfzbgy7>An%)Ai z0UxyNzF(_3xrYU>eiXbS%L51-_$*9Cc7;Npn6pyR*1*<$jOr3HJSiOgXvjDqN`tHA zugv;qF4Q0Y)_KbD83yiDHGkO!yW!G%}D@qF!;6Cqq$;>7;7j7hK@71cvTp!qj zWgW2GI0{7?hUCoXiBYb6Km_;!_7T$dtD^Gnae8a72yi(xZCVYy#_;`x%hu&+6#vtE zpzI(@(fQD69N4-KkPyew1Zk-I=OQ$v|Ejr9x1P2O*I#2G_}YjZ6poAVX+VDZUH{SJ z-6e9oAnoTD_a{g6#%H3#>1l#%E0Mgeea$-Elqkq(yO-lj)Ze0J5zwv_sxL=;-Gnfv zK1TM4{H#6M*&># zyV_J0|6nAV#4y zFPNQoHp3}3BC|+yRn;NeDA3}A0Q$Va=mL-oWIhtm=ISku1cyWBCuZZ`z0fCs;sb@h z0=yG5ibe_&PsgD3DMNR~$Pa9~7ye1AE~XkZM{`p&Z!7GvPyktWpyo^am)>CGD07CPOGk3vIG7nGx0UWem)F3|_ zg+-W27C(|3U@C{))Bh7eiK>_!K?zWdZYACbJuQ2Leckl^3eECMu8+^bHwP^v{jFC# z!tw2X#HFmh0HF8k-6ze40*n(+`(rd(kxcN)AX;9^$X5XB&m>rGR?t!8)A?rYd;2Tpt1-8SpCksqW1nhDe2Mz}?Y z%bY(+|DdL$vWd|eU#gR5gKrf#7tkQ<;iu2!&zen~n~OMeghq2G%haJP4>?>K$T z<%R2WRV;fA!EWS_xQt4Oe`CXQezIJz%I!Ry&YgBs_RSUP7$?!ssgshdI)f#Jbz(tc zWC88zx_`P|gb==B$=oYa<$lYp?+eA4K6_aKZrW2@9}%*!F3B99jCyghImLtPeFLln z)A|g4_CczR^?7)v)$g(v_y}yNWHdcA+KDokWC^Mn=6|rmNsfH!T|4SRsMsDo`xX6o zwes4$n&c|C$JCbWWo*P-@%Yfam++1fb?u#&l$QrIrX>1R`+nUhqG@B5sJDKn_Yau} zpo8=-SqMN=rL2mlFLAEwGy@g_;CGf;Wf<2pVoiXnobyDi&4Re`aRnN+pBMMQx6>s% zqj1%0fO$n?^D4hTn2$R*l3ZwnyrJJSm8LV6K*$G9Jr?HU)@e|X?2_AAm&3V#zZN_z zIhl>kVoNBbc2YvVOIb-Yz9K{vUv0j|1EgCpuERjZE$Uxs>GH&9_8(dfz=p@~h_4qH zhNlM}EI8o4?K1;CK<@G~QM})ry;t%9=l|Sn?mIUri0c)<>^jd-r>>?)<8JHmSFOhOUQ3Vwv8+TBvCKYKJTo-9E6RiQ_|TR_ zj2_0fVLv`Qy~nkasVfkl;Kg#;uMTZi7jtCuX^q)$^oIpz+5f8UR{1FGdJQ(H1RMm2 z;M!`Lk2J_VNOycJyHannYloBJ@Zx)!|7r5mwWj1bCdk*Iew9=%Bfu@QdGS%7e5>ke z#(ppRp3-X8S{klDJ$XqMI+2k|dSITb(p_Ktrg9FA(&fJQuqoz4)9U9v%+Q5pOU1OF zY{TP*L=->gZ)@y%BZTb~52CzcRsf-g8U7$gA0pw=Gz<8YQaGT`xe3OpRKP%vryuBK z|I;&ma5idD1n1}Sv;7320-4->Hx`w5wi&&|3R~mE25`I^DR;^RXdQ#eT7c(GZKvta z-U5QWcq*_tW3L!>-d-Kck%mN-`uNM}D8r`bK0IrkH9b#PZIj(OS|lAc-_fFZ%vAfB zZ5!q4WFK`0p4~He9-XR#F~ttui()E!(+rl}U1?c_#VaA1Edn^U9myxVvrzWgSK7+G z^NI{bxCE4xZKcz|{ovUm-hIg-#!$O)hWS8@inQKhl2y4JGAJFxs4ILP zfBC|$#^A%`A!by0=b+grNU&u#?14_M|IjTv1yY^z(v9-^Cd0|g?5V-glHV8(!X;U> z8MN%9^}$t=`1WH9e{gLBKIe~7r}i@?dQ`sCqg`ImYW~Puc!Bq0nUzS3G5OR7!UR`U zNlDvwhKtL_F?*x0hhRZpo>I>dymED9E~1laMxXA$^QIOOYaixfuwqNj$?UOO zEHh$7?guM{f#zy)VVlaO!TY&y?)7_P(R^voz%?8|VH9aymS+c*F8wlPEc?GZ?9Vf zd&qpFZN@D|MqVOC03}g=X>$B5-Fm0$O=U!8_5F}HgdhkR@PrRn>rrxG*JH@P^R2>Fs^upsA0P26 zh#d1qB@YLCu(!DJwHf1O*L**LA+xgib*E)D$uRwv7&S@@HM}iPytINljCiS&5Vo8| zUkPipisnj}&L@QvUT}|xqp6k*18UF&hD!h>tXlzJ1sNwWR+2zt+@tAy_#eJGv0(x$ z+Fd)q^`0;-`3#&;i*8cI&Z%uRZ+U)+7<_i++$%LQ;)WB$nAbt&cZb6U4-W|6liJ^5gXbRGN zoL8Z?g1aEyD^sD=!W*aQp}Jm*pZHKNlsJK2TJK$U7ETMEezI7`O_()O%kkf0WmbL- z-9J6WTAh|16?S>uZLEe5G7Dv=gq9;0oL(Y#?Z!p-A=PBE>ZH36=k?fJnyp0wqLqFg z?XL&Dwesr8ho7?ue1n68;B{=iT#$#Bn%8OEM8E&qt52qGJH>z+I~#C(&)U zx2PNP1>~lG<RS-HsO(jfO#6|fJ?)8A`Yl2RhhPp>Hi%0T^6N=yR*nlBrDdoY5R zk%Y#PD6ccaR%lP|g=3f4wpSSv;rMCF?Jt}0=Lj;I0K)HZP`+P%z}s_Qy~rqTez@Bq zvW>p~mtyPK;AWFJVureu1)#bbK16dDsK4ZSO!D{R`>&{LBdrD#yv z`>+GZ&R*l}t^Q*%5X~=DqAd`#-S+yuBqFlcQhldof6g!+KL3sag`Rt^(ofRK_^>`q6)1F|cTRM%eTYo}GcHK06)?Akrc$AHR@WRn@&i8e($%3)UDamz&9J zWtBLkb;(EHL8zbBo(iqVOs_*uf+&CMsRR8G9er_cM3iB8BDFp?S~54h_5}5Krb)`~ zmZt4VsFPG-tZ;OS$-T^1>{45cph+;{#^15O^dO9CWA@Hf#P{A#$D4MCsY_XTLhpx~ zqOp6q+~fPnD>kU%VzQDMb#KwpTGF=~64@iRGEI%Og|Au`HA$_CePWo5@0}zzF>EKG zfGvtk1erK>hPnFyecfZj%rk-K^8FiaGgmtf zLl46eSIQ`G3>Zo8J-oKqJ1tp*_&o*q`mEW9Ks;-QGCUTh;&snrIS7|*&~?IxAN{xW z+JqEin`hF0c4jWN<#QrN>oN7&ANVy-w|`kvuD~OxH-qP5OEDGB(5L774RsN5gPen; z+r2x*zZqq|L$SYjcPjp_FR|!ZBAiQiZ;YQH)FW=zdo77&gQsQ&$)lYPc67+ zR_?!x^iR3VN&FK4)SW$j80X|0KL&uk4gc_C<$4Xb&GgTE%a}+Ea`=XRD^dHYgcsW; zlVI8hK-LR#?)~Vs99H$x>@Hv{p*&x4VP>-Q()q7V_J|L)dQKrBP|el~Z~MAvl6Tgw z`&Qv6t9NJIFEVYQ3P;~Gdq|*g4#(UJytTPNg2HeE=s{#o@Xwgt3bM8Do$E0a-zhOI zO7S-k_E!03A87U>xi%>^j=&n)tqUYe_iI$&UVDNTtJv}=rNg3Wu0dcCCdhg9ZTjJ? zSDc;*G3Rk^jwFmcGfMr0NGqJMW_m#VQ`Y-p_VP5&nqhk)FyV;2&3Y2^0}ZCWD-S%k-?_GXfgVsFd~ ztR7;W!sp$s%f4)y@ib3%$-%2*>mU3i(6DslyaqouXRAqwJy<>HsUVGJMiG3M)Dm-q z;Je7jO>^(CSr*KAI$bf7FF5`(Q)$bA^b?=e@wq;eJN_sJTyH&}!UJDvRQ2GZD%7P1 z;mW{Z5zkwQD4EdL%~8ys&eY5D{Q5E6Db-Rj8kqTKp-x~&u6OUuWo9Z0QWf&;P>all zj=9Y%=sVKv-{_3PsP&O$^P8lao(dzsH_RK(ZKY2@sWhJoY9b+G62AP#QZU!BN;Q$I zzI@;elz&%c<^cgbe$}sYPcO?$VFR(vIR+jRG6L6?cGN-%EWIoQzed%~WP@tPAfmF0FS(Vv^$a(6u#UkbigIvs>I zjfUX^acP=p(u8@LL=1Yds<)e$PK-%(4dmoTUVFdm&$yrE&;u7^$(_lUf#!Ar06{>$ zzguOCKOJJJp05yRMsc6)6#|&{6F!|!&>51MQ|Fw`Vboh%^0D8c}Smd(_qVA_~> zQy$5=ePKTWZn}J#qe;;(TOjt&n<~v=<2bqif-}yV>DgZq?=~K$A*zwy|2VpUx4CJz zBlsa~k0P-pq-PT@n_u`vOepwy1neMvX`-p=A69+JA!)aiaa=#(GVB;RXPcHXAEw4+ z=X@=~zTz#5Z?OMIX&v@MO@rYio;A&vncqT(1RSDe)VQ3LchRktYyNV7OS%>J;o zvt1g+|FmmidkVwzk2_@t;YKYV@M+%(q>4O_Ix8Dt9YHCDcc5iG_04+!?dEMG=xvZVTlSQn_C@_eAD? zXLFtV#>_Um{r3I+`+U51-sgE<=XqYw@!_|HdOyg=`{{pjV80W2Zb-D-wtP*C!rrpd zK1Dt=g*xwnkiizMNSt;=_xUt+#gHEd3+K2Noj!C!-N!3ZT)8VlH~%SZLEMBv#Y`9@ zqj6h4^ax{D<8H$zK$iNDgJs3%K@fd=0I$6-L#^r(>D&#^?I2|xuy>m@Rpswn@P93^|(za8q{X+l!n|Qj*$hb_+I}q3-HKUeP@rP^A0zi>WV$Msr+L_i^ z))a7+^yrr^F|Kk?|M*4Js`@r?nbM-{DyB=2P6_BKa)#d-xGDsX#nHv}W*c~^#f2Xx zhJcsc!jEm6?BBm_w~=#|_+xWS{TJ(RY6?zfqT~`asa8+pQWT|`0pmTu1PI+7VckV0 ze~T@#;AgdXWt=^HjL&(q={Z$)msrP7*s|f(zu@wdi)8IT`oSGlXmKEQWfjG(zlC|q zdvm$+Yldouum}vDgnVasU?92CTroB2wT*v3gIU|60xcP&vD+Tk6 z(P!UP;lfl{{EeV~`(D-SUzi^m#YCm186jPr2H;tIgO*s=qhyXm>=nA#?*-=6|9zZR)56VQCdZzGRC=nS54`pe zqYclyJaKgIwBRL{!vNjW{I+5n%Fa%0TY^~V=Roj9sLOrHI0)HGX~N|Qll}3_$uC_# znWTw~hte1>2J9{H!QvljDn-XFA*VHx{M`JRz&8I1=#gfNml%=s(dUeBYew35QN2a( zAPAACp|$BQOKi{ZGssNEY<^`H>O(SWLZ9+5{{9Je=@Bh3lbax}?!3lmgI)@E>eSzAXwi&np-O`mD{+>V|(7K%F4l2V`mi+ z068-skBW=?7%odvmr-LBifZqZskPUvTg|pY|Mjq!X{Xhay33g}pZ*B=j*lE?21G_j z8$eDILneHt-(gI57PYFu){%`TJT*w>rf^$%FkP4_U7wb5XsQSwam@IFzy zvTUcG8|i;H;M`{Avx*PG%ahB@i;u z_|_0zmuyzrO|t(nqu=^^mlKPLlgqgpmm>*|vkr00<=KgZYVcHMgujWTaTH+M@n1Xi z!A^X~`53uql&Ak=4SVwi%F(e|?i}zkpG+b1U#o6%L(_9f;)vj-s=KMepflsinBf1WhxX)Eqou$-|^dIV~ZO6c@XU$(NYHi{w9I$pt>*bwFE>KB3SlHB^{^nH=$`Vw;OJ+ zA!8Df--Pt=AijN>6~o9uoB68Q_K)Zz(r4TvQDYe%QRL9P$lf7S)mWC+CppXeQUmd* zAgBRgfuaieC9y3ouBg9Y;C`Igy$B)u$>i3}`j;&#IAd>7(E~aid1(9V`eS$WjOVB2 zs!`8~Hw{SRE%kuRP&*~a(&F~eMC)$Rbp$bc=;eGS+La18XU~z__`Uo08MaQKV{{m-* zd{9rmaNrYT5`ikti? z2jM0-^_%~eIj81;te{aMtjh8XmE?`iC z@1>ujpx3qX5khwAuP**JGSBx)*l(lN3x5#*bdOGdeSUW5)=N2}I>!5R%;B$Q;;6u* z=cgD+@qq8ETp&W*e~$fyy@{AF%p->79hc@iNRzS**y_g<%Oe3es>K@8P(RT1`xP#f zn)e$4auk$Ct42J(H=PCPphSUtyw7*jOl6urxeRjl`-yBMoDXg)UMP$gho&n|LsVWc zn8;pYbKv%(DK&{62^GX@@x-zB@(pf|+|UAHSY#IHF$6hbZB7@7+p%rF(0H6$Xp*P# z1$@U5GB&yR%7A_T`}a%Zy)+ueJ)4bY`OM!_+IBnlolxt;my_A3JnfO`zycsarTHFe zX@j8n)@#-|b4M8D$STFrIy|X%kQJYQy7_vC@BB_ z0cv0UF-B!Ex^k<*&hDAtTBB|dT}NX78?w+uKtno1+&Fgiw*D<>af5iPtbXZnN-nAv zd0=xg?HttO1lcrmWWM#!cQdd|HQ%rctW>bxHrapm0%P2^SfbI<(A;gm z&^(%c88&j7UPha)@<99{%37(1{NT^K82(p`iP$5r{&igE#K3}%c<#>>p4P3vnI=SN z8gDi71yyToe(xU63+N`l00Ut?cN02Zb4Gi1kkq9ri?gq3HW|cR!NQMkgCtnr{k`aV zxF#7%gfVJ`f$!7g!yX0H)GNl4Sm$9dV_td`4PreEPhXJ5C+S{U7Hw?U(lBp>KVkKZ zMYIcXP@C};?~y>_Y-MN%n?{GHeqkzIcD0d9-{1z#6w^+?El8KYXb)fA$DICPIyy1Q zg(8}6c+Xn&T1-BqI~kvExUnuhN|-Acs!a1mXn|b34k5A}x8$#^)Je?O&6SPFcrHPK zsOLJ{NQvEQ>t7NZQ#*pM|3*(D27pr%wzV09AQNh0ZC5B4s>0hxXh4Bfb}$IfK5e&= zjKsm$GQ2*0PdTh+bho@JZRW5o*kyJPV$ll0qZLN%4MyD+Adlt|IFnhtb5HPpSsHzvw4poQV2ca7D|h1lD6-1!c&1=!2~pV>ml-*-3$DK{0OW4Euh2 z@%lDUa%y5Y;>yX;Oc&*Fzt(3Lf?Re_n{oX0;5j)_NeLZZ;z9lbu!-7InohalSy5=PmqBRH%-Y0KZ^Pwv<;RFqEEQU4v?fgZ>Ytw zN_dO+r;V&dIA;&MZ061Q^~VpxFN~vj@q+HCk&+`7&wvs*%UnaN#}@y6aq3^qGhRa$ z`nS$1;~DgZ%I$_BMAPk=cLyd(o;x^j0k`f5Axtz6B(Sf!`9IHu{cXIaq=^j7jNchT zM{IDX$yFmg_xE%>OEV)YO;0VFfM&SMiV1yHoB`j@!=c(AquKqE_wMN3j%wlAMoC7j zQ~0aGa7~~4b?OmMi8&)IxqZw~k(AI4aW2pk@?G0gG>uo)V6Xk`@a@0Ei1p8ddshIP zucK2ccle)*9OBpNAK|Z1^$#8r9&3!=WhKgO6C@qRt6t7Y;qdQz6BcyqE^yCZo%r9^ zk!C9pXr%aZ9$OHD;XSTj4e;*Qb+DZ0!e#D;*b7}oF7&CcU7`Lv-FMRB2+FwrxyTMm zT$DP(`lb3e!Y-*Q5?ei8DTYbol?7`GoIHwvB#t|s#??XKTkgrQF`H?~t?u26;6WbT zN=R;ISHw(ow=c^jjk7KGXOqP^!>&fEdI=tJDt3f^w=aE&JU*^}f{5+HS55ffw zo#Veraai_StPfo%L4=Pg*8~`A}q3xR|&%I-!+t1$3*Awe(_1S8oLLAScL8LrC8RlI9S=(yVEARG_ ze4SCc-VZJ%T}_bE$HUdfrRG~R-PSvz@bg^FZZACF$?g4KsRlr{1`D59X%fiI7SI4` z=A;0{T5n*Ivk7!7-D|9kCBb-?H!)oilLp@;r%@WO`oKHR0=SM=h2p~2YNBXyuEl7! zUvV&Es8s;=EH_r@P)a-xQpAKW?PLQs{~ghD0no+#r8Q>R7mX<@C_=^~RuSz;gIye5^K_Au(nd@jcT@GE#4yu>YhLQrYZkXaZ-Q)>iD5ylo4@8D8qkmAS^^PBk$=u82QXJL~Eg6-LUQ{ z?yPS6RcChdPmj`ov2*zd^T9rFf0{63A?4s=eWkjvzcQ(@JE$;TNDm37vP08mQBH)f7V!kpUbuPD@p23(3PeAn^{kYDZ;z4o~D zOFyl$$cZq#eumRk7ov`X-1tVuo)|ovE=)&S9h}}ZF z32R`RD~g8nyK>jzdw-)Th{|tdRq^u5MC+Z|=dT`a&0KYaAiD5Jnse_P^5!8E$9soZ z!*9o>q?4GDAtUOx$z+eAj23~LW1P*kQ&<@JFrH*Z@Y+e!sS)ziA)huogE<-eD`PWCaJ0B33jeixmth^ad~F+?tJz98h)F}oBmd|{(th48 zPS@h=WUr*f<~gWic=TC=yl$hy;trZ-bO$*fU@K0T90WPc+wtF`h&kx=*}cCII@VA; zli#}eFBAH^|2S<1?}6C55;0a7uWD;v0D`xXje zf$0Y$<|iPN5oZQWTLcUCTqZHV->o~mfG5#z6&W%OJg}MQVP?sMl7IbOIz%pLZvND! zFbs&y5E-|;3uIx;*HN&&auq-R0fLSi86uIFnJ_>ipje4;M3A(ryAU5!IrhtmIhV9C7ix{^J6iy!vf6-ajE8 zDekSUpAeQ^U}QNhtUHww;`u|E*!Ml_U;);AZ6w;|y%ABOr=e*42&$-a@HDeC;Y5W8 zo49<4H9~fdG`1 z2**-p1F$+i&bR*#l$l*ki2{jcP*5{mR3VLNr0z-%JEfHyXL0n|`L2l11*}YjIsEcI zMm|pZrLc394QYKY9!n`uwV1}}c14H;JQJfT$lOXUK`G_!)f(UW!13POVN8hTF@{oR z*S8X+V&1wT9a;SG$_r(sqpVh+H`g}C67doJjQ-qT<5MsWeBk9D=9-eFVN< z8gS54;>!cc2aClqyBE`=z;5fO>9wUFg7cX<^zA8!?bBOwqLuUq^s-_Zr=zVZeDmb) z*p|skh50jlh$9^1S%v2jGUU+Vj`CGdE@E6rbaN7ns&fcs4cSh?q>-b#z-WtVr%N`3ol;IE%FVQ`x zag4D){zzv(Q$fhHh`BwY@%HaTIX~QdyYN+s$Zf(&lJNpB!|r{_W!t|dY?n}6+7Ia1B&aal7n}_gZ!KT+eoC$wVLb4 zH+on}_ksL=GEW%WDOhiP&e=S#YtAdjnjel%>wTKAex_$Y-!87ZqVzF3427_Gv{&W7 zoZT(Wr^8{;J$k=NATbiO7XBfL{L~L`G>rtgyk6WdRK%rYt4}zAzJ@;k8`F4+1A395 za`D7Y9efObbz%@w5t@^IK*H4bpU*orT`K0CUCPKc0VbksF)H z>Zu!@(nL5N>;FpsB@yHG=`4}CjrgvAAr|`a9dFedEUKRt5P&Iu8(&O4k;S=Q7N3q!?xRr zJin8v+POMJ8 z-yc5eGWC{4`RaFw`t3BGrDe}XK%N!3oh1mf;IB`YcFcE7&Ho)Y5b{ToLVE%=ktgrbp)7Uq z^V#$Q($(LFd6i|6q)GISo56!g$cwdC-{`cdyJH1YfJl%peePy?$hpIrHCVz{vK9AT zWF)9a(K8Nf8Taet92>LHqEk~P6?(RmB2!=4sWNj1Z@2u$L^yGs3*oVl;5&L*RWW3k z6n*~g521(>e*PaR)U(d0M5n6k0o0!c_|Mu6Ibw*Xbsdg|bdoc{i2Ij6Gkt<%suNX| z-g}*g%0ab(m+=uVgR2CHMexm|yYxQa8B@$<|Nm?{XRhrAW^nPIJj{}49veyfTz3=nucEstW z4yUmB)Fy{ryXvzdWcR_hzw9M`ga@FEF=4qb~Tn!GQ}i)f3PA-X7mWmd3pdDs{bMu`2{cEurTdeIueQ5H~SomcQf966k1+?UNok1eFg~Yr3h;cKlx_Ot`=iFqGO3 zL4DY29cq>rOL`h~9G86yKWh+i^Rj)?0H9ariSnZ3Nmx&`p>+x1`7*K?uLI5MJNn>R z6hTTkG@fK+yn9!>@(RW{(HSn(;*kZ(DXdO${4{*F^L!cW4o@Dom1)gV+eYU_AGftfN})*wc=8A zn67b0P}uxO)Yod_J9VN$a(bWWMXZ0?fJHA>K5Ju9sJU}BK#u1IcDtc!`#}b*MF~EHy;o%RH^{kpn)4e)SzFWF3~l zC~5h@kE2uZz8hnqTNoANz%`l;{B1{?!Xp$zi}tJZC?!<_K^TE5)AX=OftRB|ZsCO@ zcyj;!u>I!p=gEJhFW<*idNLP{nI{9L4Yk?dZttz0Z2_abq(>a%m@lqYa_NB;G|z;s zKF}gIW^2D3Vn_A5-NSc@x(fn65RT4=vRc$j;$NINH$tk9c019us?0s_T!NPw7wG3r zS%ZD%M$nV6LOks9rv&*!h`M<85UDA;9HLN>52r04|v`#Lg3zPubxfH7zZ8 zLI0TLbn-xt{Qe7ZosA5mHaPZT`oXzARctd0sGpe7%1zIWW)MX}!ib zkBQDK)~)HG9kXovoM$6;z)y$cxm$NGR{}V%hj9HcP3JB*VzxpH#I}%%7u`oX$pjbT zv+rgvZ>@T9V=KCBqR2qfq=0s1@fvl+r1mmeSUPC^E>nfHtPm#iG%!A>?#u?I?RfU; zKqr*TvMYChBJXbtdwF#h88d=!5e<)FXjz28pNf|FJ+Kw0P8kYNz5vun!&2J9CD%0E zK^O^~xHUAdgYj~8EM&jArsU=AEG52X_k=;7(LPm3=n(Brmt{yIsQd_=93Ns{pMn|P z%g0xBDR}eJmAO!D#-jj|^`{WfAk?sHuU{e_7KIJojF3F2{M6XGuUlsuG4v@)nCNd} z^TdhyXOvP%dYka?M_kkGmRlwDEW=BA7N5FV`;0oOPf6cfV_ZtoL|!}dYLVTMT(>9Z z*_54uLUfs^Kuw|JM-Pd6n8ZYd5q4Z(7lMwA$bTF#b@rZ`{;nq;%AA75KThck6ez?Gy-Ty6;x0vJ zkITG5>&mZ|9n%KC&F|P_UOdJ~J7w>ZgHi#T43WIzs!FV6bO|xZtDhN@$QNN6P)F=yoGb$jMgwMpGI5>3)mdK)6U@lH1g2O^EPeuvEGW zDp}&wJxtH%5sdj8oG$QA>5#VHAB0%}DeseWYtW2wR7&7_HVZ?N$uq!=e5Qw4{?)|T zET#+UPokWSi-%~B9wzC{D>Kn$>tq zA>W&Z@d zuAc!5YsH-JT@B;HV%vgWYkc^G=u}kxLi%nS9pHuP|! zH{;$?v#Bje%~T?=_3Y-uHz}8ihG~~R@(%(t?RvkGc@$gx5_^61&o9>a2Qu(X>y9GQ zPuo`yZ1U#$9dS=s*Y`7BZovx4eC3ZUc%8Ix;*~iWv2<#G#(&zNM620G-CjQGOJ=x5 zA^S*G#k+C2pW1ci^6aiEwmpS8g!4zlIoPl(Mm zU0`hx;NV6QN08u4XM~4=;${&B5zso802Z z^On!K1m;cG*~=O=gDDX&Tbep#y>8vZsn z%O|T~<&@>?taw{d|JWzTi_5D<)?jsng6Uro*hoMkWUG^Me3KFQ6hw-!$-(bx`Jd>p z+{)Cy(*oF36VBzQFuCheb#ef4Zh5F{@91iZNfOobt4+HSH3h|JOd9bW10UfZAeIZD z%q=Y)U|DE^qzoBV1IO@N6&RY#++|JwW7M;+xy>dmp3h|);{QdkTf^%&FA>i`QnH`3 z6TZpa-h{SNw4R7&Z~=p6oRoK}s!noN@W5}3biW_OmU9_%pXt05irrO`2h(ER*bkHY z3r)zq83Y_RQ6#+%%yU>*jp!(Niw3YRg5N?PVd-*d3IkAnAg-5yS10{7%UEW_vTz#xg?Ar%N^@FohWjB7=9nW@W zTG1Gvb}JYY_{}mN>3!7=De5 z{J#9Whc@EpGq*+hSxI!}+e%xg1dUtbDb{KyaAW7?00uk~IGQg7?H1Cm{HgoL;#Q3a zRSXcrRSvL|{ar0nu8Vl@2Es>B>Fx2RwkcJAgNJBwD=49i;C*hk+heC&8!mQM{?+jE zXnGAqbabL)3CqXPsONinG3k_obw{rc2@y>cDvBFEK6F`x zMQg3wDI|#~{AeVewdV#QO~521#h%Xf6luL%&x*f&jM#3cueinkyHRKuns|2pKkz;- zT;um}!Sfyv#uR(;DaJZ=zjI9^C)0R*Taro?qur6!M*b@JzV(IdkNakvF>pKLnAiTb z3PjmCyDHy+^Tgjn@#OzlJyi|KL(t^(#CO?Rtdp0+g8)weCh*^C6`5rpmES2cxtl+H zrRa|We>GSP(^AsijLP@mT!`OFz;on{Es0`}2#0TZ1NW_^h5~b2Y|A` z2U%}UD4Pc#ZFRL0(^!?RvREGq`?HZLHRIjNgMEhm7IWx!FvTC(>J}9HlCgUZr_*w@ z?=h%XztUn;uFMX*CFZw-j0&JHKZf+Tdf&rGS%)N+j`{O|T5+$A3`|T)c302u@goQv3F11)Gst+UD?_ulZv+T_lJ9*FT>%-4TFb>dTF1Xu`RN^dwrgw}Sms z;8S(K>0)qQl+Bwp%|xbe?Y6o$yW04P6`ODLa$$Us*w^Qv_dXbp$}>>qn@!x5OtmOb zgSq2*`bBRy6K!D4Dq|8Rp^16-ojDrZ7WSSw`}VWq!~R0AcPY1~OphSCC@U)C6W$@> z+Tfb*4xNIvYs9)y%gQf~iOg;va6M0|xTVOP3oSDMH7EY6uO}>No0&7AkFh@CrRw{3q!X&EbxFA)GP0JnzrveH77j7@e<7 zoVS2)w=*vUzZ1_Dw)X9xDUib$TD%e-isN8j|0IHYK!ng}_5Q;j1K|%vI%dv)j1t#g%Sj?13s*@Vz*kIY71b~mh<*HVTVwA5 zW^bP5T5vFPVQKtAT&QtV&=C|+_SyZwJV?ag+U(v%oCaVG(-l$np;vyfedaB!;4oSw zE9ns+$%ioPa|iYD%+3>Sb{#7dv8QW=TbTp(&F^W!@wI?>elb#NM z@aFYAaCIAowh1~H-;_$>sbo}H%8*TE1FG;3&tBO}kv!nNnk~gux$(UsnLZ#Z5U>VY z;7fW-3eM$qjGzP6x5U$rt=I{IF0T#=in-OqUXMgg`I4l^fwBd>fiiwEA->-)=mK3R zTUjE$?7A0s7oOu!rB;r<*L@jvBZty`^6QQ3kC1l18$S&IUzO;G*yE*%u2y72s=j>c zb59NTGptF$%lYeCk1)wowq-zc80rO2_JGbS(PgoZQKaQhBaK-VC2aZB>!nIca(mg5 zbq>v$b^?q(g1GqtwD84Dh&G^M_n}d3#xWw{p#o>Ctu(6LdeU~M9nrMQMffnbil)CU zZTtc-fBTF#w|I7~=!eF%0PK<9`o!~PiuLN(&NHNc-Ggh4412VRWR7w>3HsLgW-e;> z&!?L5&w{XmYfaM3*#{pzUo3E4rJ4(NI1Lc8Q^TYA@m;b#UhB=PMG}4whaAx2Ybu?7 z;4jNhBL29AILpjc2M=siwWBkS$>>GG-DTaR)?xUf7>)jBEdugyb@XPzA0)(aH#wUq z3~5ZIM*KWY<*P8Xrs^p*%o^bDXy|=zVx573yQh!(&lfuWGFmFu5hWgF?`GVjYY_F0 z(=C6oE&Z#K22x%(jPi(KuyyaQ+og1EY0#9QqvNCp7iR$ zPS7xP#0&wM*{tM&hrRfIieroY#{ML$H_*T;->@Zy8V>(#wnduq+Vo`D8f?SWu1)&> z0vec=RgyreKa)1!+B9V}Z(TL5S2yD;BPGskGz5^IJ?N*e>u$Qnl-Ir_GNr*4XXt0h za9L{vuKrGcVK(@9pUnyvD#}#5T?9IFQJdYU_20yZI)A7K``)cp!pBKrn_Q;RGW2Fu zK==TS8)Xj-&^XX|kYlpmytOJbWN3%EOP3g}CRm7V z3jL9+%(KAO;66@FCU^0Bg{)ppnoTxB`;Cr!p3HgkT_PZ9?v zGT}x{s7ui)C^m%Bqrr{ZkEA?m>_}vebmx?Q5Lk?Okcuj-s;Wzp`~v%S_mnK-wxCUcy^Y&&a8c;=os`+%wKBm>7( zWupg%+(h4iOFr}2yA9$%*2DsSZ%hgI3OP|*VIk#{k4r&&n?A&cOkwHb%bu~#al%BHO(!g))mg%e3qGHtOng`E#Xb7`03T`Jj|aZa_0GMB;}U(;ED8Y}R@U6a- ze~H|zxe2~P?qwxG@cpl+7`5P&uyG)nua)GPqdO1Wx9oBijbr6@@rGG?D-V#~ui1`9R{x6vdPey`ebX}%Mpq7=#V8jU)< z=tO;XH#f1VbmlDGEHJY$cxmM*YCDqDSMPZb#DN)+T%;=Z}kUv`AAN zK>_*Uh8PE*S@7+Y-{c|!=fY9eHslk{T=49^*LO87Vq^l0$*&dKAoQAmwllorgu;up zB#j-*!XBh__y?0U6U0*M3xDSm#OdBei^sqR7P|ZsI-Ju!3!865!*RksG?>)p+5K=l z=Hmm753sept&;NsjD<|u;7M>H=y_7&Rn8}+TW^sg0mL4QquWNzb9$uVo^q*J_`XJu zuq5vy+CC-gUk5BHZz)u#B8oFCVl`c6-=(Mlcf4Ae0Z1`TsVgKA4)op+@j2IBb5q*) zL7*|PK~W5qG>Bywvk$^qS8#Kr>`SVMVw-yXl-AoE*7SyOD~2R4qQ=r&@qMKqsWH!- zqpEn5my!!N>|T7~i39s}iyz;QMqJ|1tIWFB-*f~v+NG!dE6=dfEnWCHbB&gW0+dB7a6&e1QBMhhdq;(!U3e>bpeTJvL5R z``0?0>3Ix3LnxJt>Be#)DL1w{LA%CfVE*&BkGB3;^mxuqd|54$?FZs!8lo#KH#u%3 z%(2Z5AxEgwNU36%h?x1aUqkjp+aNkB>(T!HszgEkXlZsQvgP6>*S8#xp+96M`(vXZ zS?pWlyue2XM|4rwl{&nD~U=sMSLw0zck{Zm395C`J<#;#cyfastD9C?_^4sw~% z_ld>^gv@$~T?n>2to#8^TB1^6uHf7i1 z&DMWj<|x`{u%)oTTnXOUp}r-($qHVcsQ$_cyqF3kK(wECzJvBydOSS)hJZu1Hb;-P zU(!f}2ayzUa^q`w_tou1v&t(RKO)cg3DmrZ5;j%SOr&RL1)B09x8*pkUr*jZjB<9G z#D1Ys$}Q>Z;g6O~*G$->pmQ)=K5DGE%i|Rn&iX#5tc!Hagz`{Fm&jmWkVD%2 zv4ZHw99`Y0FMgP7R@SSVI4cxigjh3oh9v)29#`!mTt_YE%L3!Bg^vvt!sGAMV=ZOjzIAv(W$-m#ZS!HwmR+DT`|L_QGKqSYj=_T6x zuY>X!lC$Syb`8_qmfk8TaFhjG?G}a8qX2apA_ky)X%-^}ChD9nC*n~-X0NYr=)zDx z96ITZ@{vQ{fcqTktJ=R0J+BZgfx1-NWZ1ocPur%P$fiUuX7gW^S^ca!T9Qa{86k&E zXGC+%2KfeUZmQ)15r*C651q0Lp7JJ_a^eM*B<=lBI|{7Zc<%s7zqT6jx>uI-(K?x;A-4u%U>QAN_r5dx@wbK$h|e2$IS{~wWD5me z{eFz%Ig0(wo5FIWOwJ42F=JM|agQ6IN1i@dn3SbsQgQb#76{^&SO(qd!Zxah>5!%`BepJU?%d-L$V z9=kjG?q2d&V*nCVF&<1^JR>%*`UPmI00|CI@b}w)T#&m`TD(YanOh!<}%}YmT&93cq`gEE?un1n}0mw7h8p zt?VqlDsQl#iyfHNUgXRrkN|kz1)i{`=%4Lj= zb~l(Ct~lK*g|ATUG_nO}qA9e%bhF2g@hfy7OMKk$TvZoC{VHcQD2&#lKfQ=p$US2j zB1$$m`S!D9=>tytB_EB402VkmMd~3pK3-j{f3?xef;f}BbMIqT8E>=EtfFi)Rw5BE zI&aPyVv`h7+>iS_@ZAEX(omFoM&gVKoMU8@xqsa^JEFj;zJWpmnXZ5O(}d|MgDEme z3Uw-Rq>K>q8Hg|kWv_hqI;TbVsb~$cQ;`r}dQkUd@nW#OSc-9Yf&SmC9 zruaCgO@!!W2g)ILWeCLP()f(T_dXcLmg5w}KAa+do6#R`w7P#X$8BKn{eN|hhMX|f zLad#_Ucy<-qTsxnGwUQRMIqHJ5G-HeDqXKJ)Y@z2RJa_EonU_E{plOt_14TJyK|EBpn{AlD98?)Hk|46AaA|5t7tzZWFSDHlx@ zD>j{=W_yX`?84nXRk|rruMowWcC@;W0N5oz#0epBV|?SceYpwgx|cbBxGCd^kq&yU zQ`uU}#!b%eGc6@w#YD?JTY5_hzYutTd-PHjAv$V|t)apP#cB=9WrGvG*T z{@TLbQB#%Ky1xE3&WhGkSIc+2oUheGp+$h4a1v-$zrzV`za^!PR$msWHsjrP4>a#2 zm^>35$1y{FhglVY5=HpW@mU;8+W1Qxm&Gv-YZ+CB_mUcp-gomQr|a0AGt%J(YbGi9 zjY@SbZU$z%xRAqob`W-}(~M(Qjn&TcR=g~W6+t-1W*RjY^f-HE55L=9nIWUUr_35A zLe0XS2V3xj%Ay_hNSBSu}kX~IizFFZYM}Ai^CFw*YC3fP{n|cp0g&z8FT7EVrU1c zUyj*HI|Na}$Mb~+=Q%Uh*)o@@i{wIiQl#?`JCtY4wMKuA-xvM-i{x~XFe%f_c;m}p z_s|mmPq?~VYG?7@iA5*9 zU9#0FUXj>72mMHr&$$|tAFkd1n}fPUglQB#l`vRpJA}wX78gCBeC99*PxDTwyy2$` zX*890L*w4@sgEBC<0kAGYetb7=dn%Sx4L)yp?7mj9&Yhu$NkJ=;QIMY7xYkKHi+-v8vIJl|Xvq4*$RGCAXDldIZ4G zwLM3ns~mb4HyAx)*53HR>~3Bu$IJaUWW8`~4{Wz&QNnbWocP>Kv{6I%%+D3ZkOR6? zs$KB`#oN*M^SoQqEI9sCd0M!qC zL?62G!;Rduvvowjm~MFp!Y_34jvu+njjdbUZuUTDo-cWI8+0`d|KZz^+;?n7x6<0n z2!a4{?B#LrbSggNnZG;N7{qkRabC4GQ;0!Ie>JlBUfkbvY^d;{&|_BTW6aU2D1^wJ zH1)f1?J`~CZIhAul(`Sy(NrWsy7EKql|APljh;3CV?E=esbF!Enmj`CX3-c`b4Az8 z{@40Wz2OGsf(&jmLT53BrC$^qp0pU|3Cikr$eQ}ttqtP8an8f_50L2V5RmY{ChN@W zuFV`__*xGHYynio!+k!OedQ=Sizqjq$+a4l^HVxTf5((x-Sdj+voD3)OQukljyMJ3 zcwyT`&zt{!+=c#h7pno7Ej@nL=&P}bMY6)N54I{*SD3Z}?~XUmr}v+t>dF1nzh3#8 zq7z_tghBcr`JPs*?G6ywCf5pYQvAwt0l<$_rFvhw3;- zElvHg((3ai&QKN zQ)NS$`4eS(=e#(8A7d9W%S$l0)#g?X$SdF1SoI|DU}I*2UK#|S#yRtKH1GzvY@alu zNACspBFc><{pB8~&*(^FJjV9_ESjG?s-Z%#nIqmr3%I$S&%d?S1abA4ZBG=-?1 z;lzY=rI@WG3h=Afbc+!Ff8bz#t<$BvG{atp%wUcIdFNSmKlOhVfNi!PoSUFp$IW~E z|A#(i=X;vX|3pseQ!{EWl3d(|TJ7Md-2tN*tHkN_uiI?>>ifO|kZU_{Zhl0bkE`Rh z*1-4qwLTKv{rV?g`-7f$K4sPS1daULRW^*9kw+#z#;d`t3f^@tC)I z)9?dUn3dYzu&6JQ7Oetn>^;4&f?V%8#g7KkQ7=4S_JZKthstn*+&%-k{mvd2ixg?p ztkFn&*dW5gY5ktrKD%lqT|QHf1v!}r6AB0pDqQ;0%)bv^w>-k#!$z2;27uU+v>XA1W z!Dn?@S}tb!@J33q9HTnNm-+ds5hs?mbd=S|Ng^yWLtuLfzm$sA$(BOh)^@2XtF{vnU%FCyC~KRQ|L zOPOE5wP{lzKJxVwCk~#>edNo{9?*6^^duhg3hQx#(8p!T4Qz6WT*pxwcSohF=Eh{S z;q7q@ZRFC&1P1!H=iCO{YreG;UeCcnI<|V9}rwzcC#+ z^afpy@LZ0r@I_&@q^wZX5NxOox1DWenEeC?=-P};BPWVm_SUhHPR6_JHFqCe*j!>$ zP5b>5ooi(8UHFk$G04;?_0 z+25NMq92d}$yi524r%_kr*yqJH22Tb-SmGP>NB8i?m)JSEEy@}c{g?gxZa4qg(oos z8a*!_Li0nvcYIj;2rrPH@FJ#v-hm+wHGDjPywP<{gqa)Bp}_Hs?e3(QA4gRm?=wsy zG7U27H|S1IP_K?cnXq^;nc;khp|fG8&x)fe4d~z17o};XPZ;;c;G*phH*k{%;F>@? zJ5dCKQs?y%#3f3 z`ji{CSkqC=C@`}UuwJjwbvN_|ke5rR8n<4@FDiu%XrQ(13B&e|^w)pR+1 zkm%tmi~w(35SrD7MXM#_U3%jxB6=cloz-2~PR<*`*-LvyH#Go_Zf+)mwBvQUYFoLj zC%fk9lV^1Pjrs6E-c9pf+qD4SuST9=wLNtmqS7P|aD&(Gn~i6-TF;Z(S6@#PryN8e zoG-cR_C)-O45|QaJ5L=BpJZ?pXgjt;!jR_lY^e40= z845|?55&f;g}2s;C2)(hHeNF2jQ$Z!h0!zZch85-xQ8sjkH>%JR2Eor)(>U=HMJAC_1T!^igSp7B@eXrZ^Sgp z(6aEF*w4tJ*el6o3%xrblYjY8)noXTYoHXp0N^0BgIwuWR1@%$pJe%JARrj}9{p4t zpS9Kc%uw_}{G?>0!M8++WaO@c)_$O2=u`EsO#j`sVl(BBNan#Xee53Epx8c_2f=C0 zI5-Plk$-A#WR*`cB7$KqUa3Z;ud4Dw!I%{I`DAi1mxrq;;*WB}w4b0KqkpDmXjneS zM{}og*Nfv&GtI`+k;^ayQaG6$2D5wBa)m%7crz9cP6)AF2v2lPrD#IU{v$r9*s||2 zDdZ$xQ~kU~Cj52<#*feV-pfFF#!KGB)PKy6We_4^lRkmM;$lyn-3A%kJ@2GLu736? z!JQ{Hwi*YSaPQqqITVXA%mVM-Hn<=_Tk6$(T*Hw(H85`k`mEb9EV~dEV?NM7qmMpy z`qVq1?YjYhw8M1EK^iZ-i;>6O{*|aO!sT;bc|w152vTwzuir!d^$D@TE_CAT?F{4Z zBDr0*oCw{81J9xg-p!)Z(iF0vhMoqLHv-ml#;)Hy$nJDY_B*G-4> zk#d+s>7U&6^p73N#-p(veUnf4+wCr%Q=&K>j^&?XXlh{RN-OUkf-Fx82MzhEunf8` zF*1An8^aH9EA>f-w(8zQeec*JI?_uzrY|>D=gP$aDIplwB`5Li^!D|lo*OB8pIpWA zvMwM~X2UY(Kt(%W39p%ViNw-n{D)#s%|CDx#21)LF*)9t1ctd<_cCrTk)KAyy&J$@ z{29|GO)In<10N@vSuab1*JNC|=d*9ydj$&+8Xv%n{3R3`DbL9+@aU-!1`K6uT-zwe zwzTI_Sh&E(D$m+RCCFsXWxOD}7O%U6hAL|WohQFRQ;y+O%^P3LbH;MV^t*xt147Nv zbNtYobJN^3s-P$H^Wv^+2mSGn>T?5+wMeyk-ftenEj~Pu9EC>h&7I7Q!c79eUtVr+ z4#yx9O|n$@G2Fqn%wbfJ+oedI34C#BNQ#8mR#;X0AvDNKJ)7h{q*q+;vT3=Wl4M$W zf+I(|BF1trgTEBVN!rRi)tq|UD@|_~ z6T+;CjY$zI8s*OZWBx8jq|W#@H=<&@kormV@S&&T+g10U(hSh2aeOwDN?jcLAr+T?p+ZF=z*EZtrJ3%QZh?gHubi|_FjLqFVA->%(YK-YJ zYSKQQ<$q0DAH>~DRosZX?1YJEKzx)Wp-tNyf?<}G=aXr(ec&sT*QIEBf#-hR#isrZ zc}O(;yi@MhAZ>J@eK~<`wvpZ|7?OfdTmK2LbrpkTXX=PiuGeTQ)!_Xc>9$|5d`eH? zev5YEBWQ*2DlEXfj16C8tCCE@4lRdxg7c0NwsXvJGO>RSmhy2M1F&cFz(*TXP1^5O z9vPhz!DgTNSasohA|k2m*Dw4he$I_`StWgLPSF!fKuCdko?bGm<6m=B)I&AWkt>9P zMaY+?qb_@#5Dgf)TUx{zmpuf|NnA(;`n^5OvHxUAw<4?nAAGYLhQ~z zCZ&w!Ma0`5L>b;hp4DLk;3xW&xmRYT<8UHf+@T62K)uLV_ZmYd7ABjdHy=l80E$I% z*Z&;Y#j9;BysgeTtDzT=M1CF(yxglqNE0~*o?;f3A$AA_N1|Xkjn-r0UhO%!VP)i{ z3u{+41*rm;TsUuB5ARUb>ZnA!=+Z*$AGdgzengn+1nfvbaG#L%lY<7J=a8Hn?q$_!GBEaG6)BPF)%|pn|jKdwGHR@E9_WgstLeV-r8P(e*$yj+4Y8+4RurtJ39PZ zhmt!A=@Q8iSx+NA3hmQNPfHI@3ovxI;ZxgNHjt%X61=3{c#LQJvoC+{k@|zUefKjoXj?9X~g6 zvztNA#{};|{50V2ni-EizH+Vm{COv)5;U7Xq|606xa9_`9E-{*HRfif@*)?MW1-qv zTpH9IqlfDTeoXXzJGt~$dn^hX9V*6$ycE@q2PZg>C$^$cX;7}IvN+vP3++*ORy?ZB4dxvTb|@O+-D6$W zuuZqbK?RMh!Q=xy}mOU$3YhH`p`5*j7bg0HnbDEh0m2hlhfq~VJH+rNM|B3NFB zSr=(1Gvdpy&-7*a;}j7S?OkMz3lzy%jzrd^QtcGva8#spTJ|iK4WSF zMq<`Ce`&C={l1#4qDHL{?c%ep=#R)#C3kArlTJ{#y)OYX3Dr{avk7E;Ws77e6nY+(IL85f-Uio2Mf_UeFTm7Ru-p7)%6{A>^z4vY-qfLGy3D5 z1A5Fok%gA>%NxFeW%S(HyN}4I>@LJSFwe$JU67&ua_`M)s#czg(Vc;tEemC(zB=LQ zuVm<0`kZ|G9b13g_sy$y&92sOWo`LCL-%fO$f#q%oqI^TXn`5S`)>hl`4izr$MGKO zphY{h!M9Bv^)epXREnh)E^0+w{lT6t7GITLAQZzoh zp!|{;yYP&r))XU91I}W(XPhTh$UV@#$5u8=HECOYWbLcBs7cC2fX3`>KR(#(pXha< zkAQlLneooIXO_I4BLK;aKNpm0vQt0sA!Avt$9kO4P#6C(OfJFUfYjM=b~|D^<(}=nYlz7++(zoSTp!QmT)>+R0EOO=;%gt%%V-S!BX?X(ZGZ85*Ss1bGm`~F_ZOZM;a%h{e1m`EoM zevX1xS#U*AFJG;ig+$({hrny zc*}O#4rFwHb`HuuMN)x^V1IDK!5`{vo=bw@!L(RA!4Y(%k)hzq0u;lH)gPC)H64NG znF~P=X4h);R8RvwC#F2ry3p#O%Z)?Nq9}8(I}QZwB4??1JGjwklM1^d4#%y7MJh70 zsAD*qY^)^?_VKL7`T=N8$18`|&O{L24t-gsMuM1n!1Fuxfy4+L!A;T;&yxh@4l+eU zoev=AZK_VM=l}T{QU?1(Y+mdt5k>4|ba+~-skx-w2mxrurWe( zH8wM0dz~*(oPx;|=K=ZK@WE694coN?k7E-Wj;s)L`cGn<&X13TV zM&+ocbMGOj=ii#dVJb-VOwyG|InF_ji)lGko-#1%Uk_o|y{iO{qnYO+@GG5=77 z1wnMR3lX>VNb5%&k8B%3X!Y;f&=~^~PW(@vkL-DW{X+)Mm6?+DyG9aZNTf+#@k z>QD2y8$AkcO(#4=5VI-PxM*B@%IfMpNUm~t!L0U==}Uu6NfhQsymZyIxO+Y3z7r^G z-k^F{t7v&rsh>k#F!V;xNW~H-F{^QJWi}E8UXW1iA%je?)YsuK_fz83pusF5 zDJ^}h%A&V$GvNO1%`+H5JS-KhTyb$j{h|A7Z`H^A?JCgF6M5=d%3jtY$I-s-sj5`% z%H~%VKUL$A(awLRm}g`U-#jUKO^PNkgo!4#H_nb)Hg?58%|qpyvG?03>S4lLy&ss* zj>MAM2m8Fz!am-_rJ1c8qu76UR=;L{_T*=NT<7f8U=PM$` z64L)=bsRd-{&jL`^7PJJ_!9J0_{etONG?i`ALM7wz1|}k5Vv5=%UyXzwYx$()J^G* z!ulM%oWazd;A>QP`2GNP;us*60Ut7`d^+`EU`814_T;osJAV3dtFb@JFF{;t@n!Icnc9P8CkUrxe>-j*`fneQp>cu8*K z!0qYTm{iDsQ7Ce4JaMBDHqY{#yVs9MjM*51+%d?>Mg_%f-%?sAx#MF`@mjU$t8}}z zWt)GI;qF}~!Wv;665Cz$zUZ>oWX6xVhT|;X#ZhxOjSB)hK>2=lrQ>0%A}^Z*L8oy* zF{+-AxTbYVx26hs8JkDQ^44aGP+SxFRRT z3kn`(Y#$&+ETTgzGO{tYTiH_wlVzP>@e;A?xsJ<7C;=((=?%(FY1D=sLrpgdL)B&k|8F{8hICOGCKO|{21AGa? zhPHw`)e|${<_2ppAH&xWM<9H>@Sk>N!$Pz#?R?jpkY@*Ha%PcrRT!g~zhbmx| zPQ41>VDB8|y#BEn!8wd8b0Oc3j zsICm5*1-%kcMwCoHIEMU^L-e^!$FzS9rJo~+0Ws#a9}IyVuS#JtYGubx;G{?f_!<8 z^FRancMLnPL{k5$Vq!=9G-sGOiv|W9B}&41S><5JUKt*(lyQ%UFWJeq)KL{mKuWqO zC6ViutUXP)xCL$798K;ioQF^KhX;u58}6UlU>^oR40+vWJgoZAG@Cq`-A75h&HH)M zz7(kuqD75QpGsl0S>)>epjJ50d9o(D^l>V!gyjnW*q3tCE$tbRT#u|!A!E!L+5wDA z`k}v5Op|g%Uh7jf0Oyz_;ocGFnSQpukNwE7>iLTsp18WLp*O#1>lvNUJ?JfsAQvAZ z50%I)5*yCa=dNd@D>uUeBR7jpSHp1?1(@|;jG5xoj(Z#n4S1Kc=LG>8-r`p2ywRow zb*P{Hq;=g#y)=>sOY`cNLjVBMc@KzsUv?Db#`17RFzh=0CV&l2FZh?qT*NmG0k*Gk zIGjux(D$`EK*z9#O0}m8Oulw-L3d6H>53fbqqbx+*YFKrgWcf#&_}HuUgM=ex$XPJ z(&8QS@H2AuC~hEYG@bGxum9H)huLh^b|qK2z)$LDpLBQB2@9jdHdxw##Vt-7S)X*$ zOI-d(0laQOw;2pOXQ9@!zmV!~(I#6TDB5)-u zjO)zd;oMYrf#1W1+-b?8808u7ZmF)9hkj!IV+NdNMSp*rEf9zTZ?v&R(KC4%p4x(W zR$`@9NE-?qVxW=0D9g)G-h&>~KvL4Y`+RV5rrBq_fTtpN_+7Zcm`6WGWOlY2-nIhO zNr>UBk6jqh^<~9}1>3m?S=Qam_9mpE^Fs|6?GWP!?Af&8baq{zy zLGnOQCwnFW`!rl-Jtr*E>McbYk9GfX11133!=VpvIIqZQJ^26kGQPtnvm_F<+dm*B zDgDiN{&)j>1m+Qt6-Qjaib1&-A(vFnYIr7a>1popfq3i;2{SojN+GNH>pw9uvwi-9!<~*6S{rVb%p*B*H>IJJ`D)qqQ+4PVV%=|=AUP>isq#j2yWKG1DO4qpSi6RPFni;104bj4J5@UXq;mg&f$* z-gA}j)VEklQ~O8eD~H&~MQU_$H?^BM71w|!?UYlsO8xW0(X_=d}7t7#I0h z<0Fl1bqsV$%>))%9tULsX;&wxHR@HekHzY zdxf)!v~=;nWr5T^K+rhHvpK7u01WRE`pwV@aa zE)py6FEvbEJQOunmA0*~-G+LpWAn}q7UAzcJ{v3+mAZf)`KPT(3R5BU=0P`}>K`CZ zCf8XSG0gic>ZarNVb=APH+NRzdkak=Vk79YO#su0T!X>n6`2e1kgf9qQ?)aI9A6pLtfI0}I2dtHsP z$o_}zVgAIC<7x|r4Wg4C;JtA~4Z6WSP$SNEMIuu4I(w@5VNe}HkmRQ-A{4@Ln12tc zw5)b7<7VL<6&El*^zJGekfLXNzG3)v4=1*8+i>D) z7%#DDeZor?H)HASiDGvrO(=Q;3#`5rI4n5eVjIf0dO2o=QkrdQ<8Y7Ad`$AScJ6#^ zd7;9(h$y=~lM;I_ZToxS9aQ>bLzR^6==-~pk-L9@{G48|{(t<^_2OF&)i%46M==0( z0{dL*uJh3j`jqldt*=KZN0PeF(qBs_I7=Eb?(;^~{IRhAh?O>mU!M^2oVc@;t0P8> zo++5}LYDt9I}3G7)Jtg8y*Ud%#CrEfTyb1fdM`DT@;p24yT9qR_TS2_fu@24z9g@A zXZa2!*xIaZ%?YwvE@s?Z?LGTR!)yZJ`Q49X@4|z4x3d#5-guO)Uj0Y;W*G>)#X2Ju zCP-8Cu{6_{D7+B?bg5n)P~qfRNz&Q|go5tDemo-F$=J}{@6l}s`uk`hQjE`p?*-1; z>0-+Xy_P427ud^^q{}{3pgy={^?Yo+Z~|x}w~(J6-rHY$`b?#|%+!VoLD@s`(o;ue z__}^^-nK2rVW|>c2z$QY&a^KQBT3A%MF*Ea%`M-vJD}?mwk<0h5o(_x#=2Ptz>YL) zyi4FTNJcS=DAJp@!q*5U!Q|wn6^rF%u1#+4zb> zp&UtXP$u938@bx7;sTF0Q+9fAZYfeR(n(jVlX25Nb*2M9DkFl3@Epx?#SycKiyd)a z&Da=kAYPg<(JAeN0y$iOC6i}Tdro-ho*ai)({^qNdmLzQnpKsd2zEPGXHwwK4qv=< z3=^vDMI@%4vtXp3FQwMe&i>SI?n(mf?W}gO9(;FgWM9mvCIB6GBU%`eX}N5(8zvoO zEjB%JY8*ZnRWuu59uEU!5XbR{rHTEg2v&2a20#3U^I@xY0u{)u3!U`J1F5JW=G|eM z1T9FKb!$BN=*;^JO4$>&>+0*S2>^^SXhtmBH$|KxLh;orz0E2owMT~Dt`zNxd0Ybn zx**q&g`b{M7N+&j)Sn#%gCVJZ-()J2qHS7rKk{k2hBztWy^fZ)sS-TqRzrY+&I#H7 zwtnnyHr?Holrycutb0{x16q52s%D~jY6c8rGSSrs6#W2!rR3C+(z;?tK4U4ce zMoZ1#!Xv&{o$g0LABAv(Y6~Xfh~^cs#CU8=+i0~Sy(YwMif`X&TGL+IF;#GXHH~w< zSm29k>6OTEYPhYUFPk&{&1hyc-Q^MtFXt_;4&du(7nL%mUs;9(x zSjB^izMX%)4SV^H_EV;M-wvti&)OwUaEO(7 zwf4jeW&-v5ExR5X^3*+17ZBv{+Wk=3rNMmVZvsd3L8Yh6|N12V&`(G&3UK<%1Gr6J zkm=#7N?h)bS*(aI_K39Mg@0zOsqUYtGJpE&Jw8RUey5~XiC3-LCW#CN9NR9xUu`6KHp}4i8Y+ zS)5=e#qJlM-j+0z_9q_3c@)VzQmT>v&|(4<`wx=W(8RNUXQE+tfZJEF2|mFmNzk!R z-K(fZL5M+LI$~D3)^mCh)6*qgNv#R^d0A|6)1f4Qqh`A=qx9Z~zQcYf7*N(ogc|wY zU+5j>uaXDUGJT#!WX(N_;3ua#NJY&;_P^a0qfiKZ3k&DWwX0Pt&>b85VjiQ-h`G>X z6=iWpZ{Ve_wON;ovOcx`A1nRKutbszIhyg=t*D^t@`ZvG^*y)Q2Lg#v3AC?5ix!?clm(J<^bL0ag$RGg? z>vKOX##$GA8S@%#MF{9)1f##MnCd;j=>=_p-;lpsxxwoMuS(LuhzKb{g0uCx5Hpa~ zTDG1yuzB*3K^7qz6)=3r+wvr6#L{=a`0BrmF`c{w5bj}%99}2Ont62s9=qo5cetG& zu|4_M_&70Zm^gLvmqi&UHs7+1s-;9*>@iX-E7izknom^3(sevH)_E{1A&Vax`!w0d z8182Kk#o8KE*#j#_mT-mI_)LgzXHuC8e{+7L?3OIKv=Pow96W`4Lg_(UHNvfJ;s-r zLbXGP$z2b*J*`!QYz~g)&X9MUJ{QIH; ztuYpzT?j97zxt|@OZB{jboeGudsCDjwvEt|@$8p-dksh3JhWvi#Hrhzt<%nctJv;| zM$HtIEMGUGhnz=H{bYAUC(C4zlP5@=(Vc>0sPqsStzV4W&Eqk(NR(lHj62xFtSS{f zftAH?zSn&G&iZVj6s@Hi?R?$nw+`^G)G0t?-%Ed(!KEXS~#r9+K{k^$~=p*vo;}>w*+A)9M#4Ls?-mu6M5@GGh^PXEq z4GLkVp2u)P*>m2=XA)JFHw}-{#9$jH`xTY?!7c+gY;YbiQr+`$M2Cid+p_H3W%clW zjxgNq(n$T_0c57Jg(80J16(ks;&g(xlY>w9B@bo;b+&`tz$)UIvYqcVI^)leHp#vg z^GiF=*I~<_^=KHO&&xGziW_jm4X@BQyJN1?b>yaC^M-Zjfw2dotcaccP3WISDfLfo zB%rG4t3^yfS{-t!nS{_vKd;WzfrOU^#CgX-E$Wajny}xDCjLC52A)` zgOzqmnSp&AaqNM*f{{xoX5h__iTFUe$;9H-G5`ZZ9UOZRRjzqH>MZ zUX#+~8U!UL_Cb#!Ph!AnN~lJIHWCewV9Q@wJs&Ba5cHv?yU7v5{okRKwSMWhblil=iS`eEgj9x=NL$ zboib1%3EDAvE3tQYhk@e^$>4Mc~U@Xj~WS4^}9dh)6@26141NZS;)Nu$lEQ~08CM7 z2HhKJi~5uCRzn{5x4PF@!KC00WS3A_$L|^V@@C|D(zKDUz&4^v-^F}SNZ_Mi^=cxE zI#xLL?V-MQ$--p<+UlR)QC!Tt^I<~tdA}U{6ntQJw`(o@^a(ZSud^w=#yLcz2z%k^ z${eAz$bk;%^T@}dt7WD*@Ojo#7Z*c7SXTnss+R!O#tJI*EPf;KTwIW|F~5* zpcZ?20$b?8lq3kt@r&O#&2@WCrFLT2klHw!4-Ii8g`6RUB+2h`3;;V<`k-Z~&pAA; z$q7ikVh3@2>FxL7ZN|qe%Nm7@?bzgpGNhc+y`7ws+@|ae_(4d-#DI$=Epd7!vf?f5 zmqw7}Iys@OW_VpP2=eW{QAQHG>0nP}Emq^k=lj`1ia7X{L##_d%|(Id@3);RECBp` zeAZTJq0_b++w?ma<7`HioPtY#X%D4)YT~f{)|mL1omWA{?V@+2*16X##HyKBIXmTJ z1jgmpSdAAw{m}29H8BvPdOUab9Q`of~ro z+wL9FtHg($y>)?qF~{=oway@HwG=J#l6W4)-kz{RsBXB5yt_Pm#v6I}ci-MOAIEt3 z*BV<{N_f`0j+q?oYzI2dz;^#z#H>FLykRaR#*B#Vw|F=xv=%^U%&Nel zDd%%hfHIlEWLjY?o%2hh(%D(#RVMqAJ%aI&27ab^=)+n=vF|d@&p7jQnkRF)N(Yd2_xi8z^4*cU zk->iQE6%{*JlHsrC}qV>V((fy=17dhT9-po?)mP`wV~wyJ%+xaJC`IGO#x(1sy29k zk%}7oG;OG_{c~q%U0r>t{V+{YWBvsj<#ukCm=aSuU`6PMw!HN!rGI-J4~kuDJBLZn zaz3i8h`5EDdoWTB7DRLgXqFTEVuMtf#On{>;Uz&rn7HNYm!V6~vWj&f%1gP*s>LyH z)xU)^V_HUQ;BK8F;!m@vEew^;g$Qsn^-2S0SX%(-$1Yq7`oLTQ7v z^*pMQIV!HoGX&Ncl-QCdsqE%nN56di&*uOb(z*rQo75oe_H%yymh{p59*nC!ySYJDQ-*tiY8`ZT|l5huC$s~xF-Kr_Et5$V~(4b z+^Opg^7{A!bGg76muy;WRnzX&9sWu6^C?m<(EYI*A9vDK19CL_SC}`~>tpOhwr{kC zKaltD)Wm9f*^y?{(>2NoQV8J9jXhrN+N&Aq+n921?ixkP9v*lCxg~PmptB45^mZ^KZ~ff$_Gq?$eg<5dG#S>b zv;Zmm)_1EGOo?F;)e~G%*kjv!9gqf%tx>Fy&U5H0Wyj|gJ~ZC8_LE;CIq93jPCsXY zK$JKY^pM^5r($8^KCoHi871rpqja)&);C;1&^V~QQgW;G`+&9z^M23`@2I;ieBcOW zA|FHN+kmDEpT00+feFz5aDYoIaW$TBu2}c)=S;~n#)i5kc)6k7sV7vHz{dS``ZlX* zj6QO?+kb+NLij&^15MU=2%HLvRT;9pIU!El`><8@k$a~e`TFl|mEEu)Z*vU(1+Aep zva?whok8?F#S9BVL>W<)mxw^VYf^D#liwI4?`9NElEBdDzy*0)89O@D3h?fbKx{^k&s1N|XWg`!_vh?r*Jlb~% z7v7~IZe(-bzX{{VAT}1}_;Gk^I^q5+)~r%3SifVjw-|8}m%V!BpjTUwYb|UBZK=cy z^PNKJrTE4;V`#FsQxLoR64fv2u)CJZ!tgnZ1WF}4uA+J1d$t~T@SJ*%-SUO zH}NW3?uNyi$dxuvjgju0aHYU=wG1w%Z51HrVZd5 zPOJYVp1xxZhiRnRh_J&r+uIXyol&jRp$De`ia9z>pUaeJqKW?Ew00+W65RJbFacIS zYkF)U6Eiqn0{Msh9wDj1ELTCwn&YoR#|hG3k3Wx@^lx-o`v8-0q?h?|f++Iw65bC} zz1qQ>DmHsyi>k9SGPik+A0*6rI4_xSyhOT05G$XdOkq+@ufx&bSXV{Wn|lEkE@zSU zm-rz&ZQTO@(#zg}J2Zl&qO?t7a*mQDkUeF^?LQfq&|0M7`V-WUZ!mXnr8<|)OHNw9 zB}*=86k#Nk4eao>f#S6zg>@dAv|oL*A(ZY9I3xuUf_O}gr>{m(3?m;H=Z`(I-7-Lx8 zq2nPq0Z>BG-rv6d@>Pa!t@Cp-MltAYr*THqkzqiQXHkr4=7ukle&xh(Rk5hTc8Od& zq-$pz-l_+#$Ipq4?#vrRq3`;*r$OgG^)vf8)YUam>2llwD2A@4sI_F~vYB7Z?`dN> zvm`>YdbOt`p`!iWc5l!kKvqEbJHO`#!c@o*AmEs6R+EglWYl0h-hjl~ts48cEk-HQ z(R}lkO&Oik`1;8NG1~aIdX|Se{I~L9+?*!A@ixA|ATr(=R-Yt6Xw%8HdCR7a{&j%b zR>n^j_K!Ha@VF}xiVV7*hj<8e_i0S7QPg2@% ziMxxzNk+D~J2pv?coW<+r#RyUNVA-t#Env6`bw1^h;Y_2KAp5;n8snq!2_1)e2vf> zqEF2a0Nh~FM+qW2%Fs(QvixyYuT!>1_-aw5DQQtSXxpcCqoc!D z|M572-oVyP$WA=qJa5M2=}qoLEoR$Vo9WM0K9f2(k7gd3nmEXcS5K1IW`jC5#pkfN z{)vN>5*J+Z!$8>-O2c7eDvXX%-SI7OAJ=Jh=ZC(2@?nx^mk%(;mMlOt=x_Q;v{?`SRs@hHmU@YqFE!am8QJfrnLJAvwo8Ku2`@o7HK7Lww^uz}HNN zg<9a9V@zoaMoD5dJ$N@90)FKrju97n)WQ8MA3QA$sUjA%ok!}`{#9?`b~=`u`LOPj zgv8}G61P|TY+mhDcRvS5#61?9ttRg_UP(tq*?FUdTfkj)HussX9h{GP6RnYw6WSKy zDLJ#F+LIE{j&Y{uR?a;8a_9}CIq#QalBT{%_E?;`IH>k%AtS46R7{QK6vDi3>8ZBTZZ#HRr zyKk!eDfg9chBWfQ<6qdORj?L@sv_h2zFVu=k~U=`cr+jpN#07h^bsrFye>3HTxkBb zJpE1<2e_fR7EgJWF@gMpN!zlf07pQ$zwvzsy?REzU(gvJD6t)996AVS0ril}?}sMq z;^5eimn89rO}F|*VSoCcktUrY0>clo2tgCk_uHbE=1;c3d%|MbH`C{hG9!tT@a85Z z^MbRtFAJYwCF{N%@kPNfZD@zrU{lY^FU%k#i70Qac=i=^_}-C0H7tJk(~P0O=yUkl zZ^ti7LWsrq!0oonJJ*17)r-+)1W~j*N`k~{;v*6>Ml3CyqpBZm8Tx(NEqKQZU8TNb z-$fr^`)*042Fr#2bCcbwi@uQ0HGD(&%V^g+$`>vSD@Wcj&J@%tDse zqidA39LB?M;gkHI1LEc`Y#gVRY??RC(+w`x3(W;}ol*Di+m*&m+SF5iMOiGNF|P#u zhvR(fU%UmA*h>`~(UQ-%KlRN*pTds#=5JdS+=<6B%pM#sK&x0uuc4-&g93YrakeO8 zUZq?zb+~Vuy9!ksxud;VH4lAX{Wr9TAKO^$^kS(;h9=3Wd2N$Bky!23q@sxC+E*oy zvTgGKkrN1!T3yXdDnKQJ!Sl$P~NUh5W0&oO`E?JLgjn@?d`G9H%P?r#a8pxIx??sBDd zVqTHyngJrPq2IY-3cvKQyI=YKqu|Qpng0KHQYb}8NbFleF3b|rQr5ugi z>?kTBd`pfTTahbQ$jxTXv?mB zMpYvGMELmAees?x;I(w0hU47Q9x_i`^Q8L=SC`jFy&r@JreKSWw_uE303k?@8M*q& znMf$oG3cWEk9^b%0?Ww^W9|*3iQv)3XR%z5oZHT7Cl{nJJ6nFq9@u_l_42TgDcZuV zGLMa~Gkjo&vfs~}UVCCv0eLo3+Lujyb(?8>W>Ze<03V*dDKG=cQGAih9<4htE}ogk zWFap=7GJTgKGXX%d=1Xx^b(wsLum!HSHubByL7qVKJfD=BS!%M z7(?waCxflk*nDm(TLiOyZx#-GjS?H?`nxCEn5^w1O>-!xbF=)Bv4^W1O$VfS_iRel z^nhJrY9*2Dm-EPWfE(z(`DHPXAjw$}NP-0@hJIf8g{Ob9;L&^53Z9x{_ak=hUDn>z zx?4mdBTTf+1a%(}&F_5;x@~rfkyeOKJ1*!e0xo+@h&LzJ9?|~L#Z|t*I5~?ZJ=y&4 zevG)%%FOfZ~r_S(MDbDw1hGdB1Mw1xofxS}DAQ$OA`RnJH!$IU{^mFO49*fZXZ!F() zNRFxn_9rZ(q5r`PB^YTd?)^d7ua0=J2jR&_F*g~t8Ue`IF~pOv{7=7;2e+J zh}kyNVu$_AHC@}I9bDz9CAS`->7Z5+^6$WFYieyUPx~bOV-5-Sl`Ye(&!4(ocz|bK z#km=Vjdz!{=S=&`&r`UJoJcS=N%7Rfcfdufwn5n3af2@FEWIO~;TJumd7fl5FaE(- zJ1eHUtCa6?B=7I9o*tlnb0;q|EF-dsO^8JZRbl#V8IJMaO7q~i*FW0f^kDZ}qmq$S zkZtKi-2tu=EeNbUK9*f}&pGJAQo$+&CrFTNW;gxY3g?T8Fg&Tj#O$>QNW=o*r8B zy8OnY%?6NKw(fDkM~4W{sdFTAqUyRVeIFz38N<2(o4n?wmhe89XodC*=>(W!j2rhP zTkxuUI%i{a;`nn%=A}Y`-$x^cxnCdfmBko04uUNmmSWgeK~8fezqA&u zQ-m|J^q5>vQ8MrD3#o+ETEl&azsuba9mV4s7aT4PsB->>Ad?9Du=De%0|2I-+6uz% zMF?~~`Oya(QN0DoO`!Zd=i%H)B+ynHW94Gi%{VCGzulvNRA!7Dz$uwYseuaX<8Nmz zgL`qTJA-`e@mJL3)Wqc0J?9Dh(+G)Wzx9BYSEiQ6`sU z@{zwjr?Q)=KQn&V((oX(m3uDV#;1Xw!oHJ0eFmsX^-Tl$5#rj1{2bHl-^dX3lZ6n= zBfJqxD4z(|zFsUftJMQvq^-%!GxGeOw#hlJd&%Yw|Ghe3XWI&v`bA)wFyr^uXQL<^ zIuT|oJeSS>loMZyTOdZ^v&VXE-^=Chtas45-|(OO>i#80EU!vO$J55rZ*o@<4>k#* ztMA%aKmF++Z*ETDbvy0ZoD{u)HxSC4hn*3#5p-lSjz!W56dvR@n*OR(+nIiaJomLx zaoMCp{q1!1pm(dmss9E&jxkjqdxzKYJDpaBxfs*?Wvr=yi%gjdx6G)VQ=YmnJErE4 zhxjdmN%M#KPt$rg)&>ar?>5Dk=g&1)*PAfKJI}6qV(#>sU#k}axSAg)Q@hHECK`t{ zTMz*{mPg5mM~bbiv4~t;==9=pEO+zKZCg3hGXPh+=Nb%q9BF3EnYvoR#aNxRf(gYj>|0Po78b23aBEfD{W4-A;*o_CSztwJ>)DhK&oq6u;@X`?P=Q`uuaM z=i#yN`ADnY`1&{@+c#L&02EqBkdB+uoOnx0kWG3Ugsd_k%_%qE5Cl6ww?tt4B${i^ zxqbh@(iX&DTy5bSdEL-i##_Vstw>209#!lpMmAJ{*^DMd(m2PGYZEr{%i+H( zhQA%5R{xr{G;oIYgeYQNY?i>0b2dM^rv)(mzapO)g3c}z6dTZ)1;|XaJiTSyHFQjz zQ0wWyXbXM~5PKZ|p)Xs2CmRE2|0G=T&Ab}KH^6@yMuf0&=q%>VCH-Vt)uzNw4U}Xvq4Vb? zCy)Pb6C8z^Git!jOqa4#JGAx_DjkyoWNl*=a?fG^S@k2hQ!^^%c8AZn3hvx8)#|w2 zaEp-}$-V&nX$<3En|A!%+{Gz#8qPe~oGJz2DhY-9qYhwZUxS3T6%|azKW;)8`abaL zyN=Wi%2}J1@S7D@((@uYb7NMl=lLT?IJy+``ySvjr+CXCtVubdNXNAmhdmP_-U1tR z$}AY-l(oZ$eQp_`Pa2|Sj8f;oZ`S}cjmo<&@7aKS) z@c`z-|J6WRuG<-Sc=RN@R7K}55xWlwY{-Q zm?Tg$1Z1q`U^KOs^4>pE0MSnb1v@33kmsaq3a`Ms`{;ByRn+S&ynRPs-}Gc6A28Wk zpWW>82BPB&5u1w|=EB))(L$|w_bQykSlgon8OE9p zR!!f=f+4?s7p_gXb(FK~K{B6@kk3ph5z#)z+ebF+(Y$wdZq08-_FAI_psLW1yd{$!9ARc7`EXfnK=SGe;seIS zU@a(`4W(@cD0+Nb$<#*$xZN0;-g6Il9{jH`=jyWfZH6Thb$Av#IL?jysFekdw_P`Z z#<7F~mQPMY6M-&(d;a>a(@libRYUYd;XU_*u$BaZS`_#Kk0WoshFyq!n&oFgSK5eW z=cD)h3Pev52{P)&(Oi#(n$Yj;t9#_L+2aHu9zEy$a6ofH)!M~y_-TLwuc6%z30LZ@ z%O6D|T%#XjGHW{oo#nV2<+d;ex6>YCi|TY;1W78#Sa?KN`JD0AF-{96aiao#eI^AH z#J`E{iY@IT?Bn>*!4=tIX%#rl%Ye+)KaSJ%ALh&D{W& z%*F}@zbT{;zuciWB+FKT5pZNyoId;M+Vk6Ah-toX?qLFG0h1`Mm0`IA#!kbBVUr?M zpIvm{k!#?&|2EOA%?xc1s;B3$8mvoS#s?OGbYZ5Fx#nc49GAPKopJtOfA8G$! z?@|L{xv@?eEj%lT9iHWl2cT}uXjM%^rT_{kyg5|evCTzgGqgcFG4k32o_Yhg04Z95 z-#AJXr~bav#>lGMs<)hd8O!nF^Baz6gzw}1$H3Y7&%qze?r%UBV87J&JnbMLN7962 zej;?;{bKt{oq%lv$;~!*SSHH1gSOi^OU#;{6&{+W`kkTJ#<+9Qrlw@yIG?FmOa#9w ztldG5l@7-_QN8%5Ok_!+U5uLV?6-R{>R8h-HD&PBvS6tAEOwha z_u0mhh`IC9o7szZDp!EMNE^3J1E7&rq@<+#M94*1`*_wIEj4BZQ9xF+)2SyjOTub_> z@gd|wU>8@BIDVKU-od*2mCtm#pPZ?y$W~To5zBc)&Ny~lf`rVd)nQY+)rlMF% zUMaFR;vF}2akzN}Q6*42Ui{oCnoz$KdKd$3=Wnew-AVY36r%>1BP2H=dCjY5^k^I@ zG#|BTEC1hiRl`d&rm@C@Wl#7MSop(@H|l5jMqlrEp>1TSJI3e+ZE0@9u?Do?L)Xlx z8xsVpu5D+TcNFL$?{EGyDqCp5mP5`1<-S8e5+Rw=qQ%d$X z_iSyV1o7woX#-=n#EEFk$T#UZ1F2qVJgXh9`)u?WyvvFR2~pSX2zwUwL~NE~utH3A zeD{$^K%5AL8Sd58$5Soi137*z@`!EjX9K#%&M^ zL+_P{(4sL~Z)Cr(SW zG$uTXU%W9d@?l(+S!*-sZnXp!Mk=7T9vna*7}5!01ZxrU4aQY5UcFC1>;lQI%xrES zJ4xB%8REI81)y;It+bL{pGAQOo=%%JfmQ5R6yt8F0_}(ajo%X?bzg*Mnz?EtGVYXH zG6H$$jIvJoAK_G-(9qDvWEGk}nw?IvXx``=vemrHBig!e`I`4n+xQw}Z1PVH-3*4d z-Jtd@e?Z4^*CZPg(ZLjnyHNRykH&tDf^ldA4UbDGk`!BZfSYr58M5)8;sH$k>y3Y5 zNUKwiFKxBo&6(4GoJpNNZ$Q-FYS^qwcBP zEM;%l9xGbj8I5eb*8^I!lQbx?KYrsFFJm=D_hk#xp2$AA7!YfsPUcOZ*As#qh#1K3 znaJJf>=jDw;aAe{sUTFzr}M8VA5r0~lY3phD~Kud{xi9leqn;`ORDUysgL*b<;9Dc~HJ`O%3R3*en6!C!i zn6AG9p0r`C@k}pN|NY@XjE8sVQn_GS-fUT6G%);0*WiE6OL4Y=1mgg)J(Mt+H-`?L zs(W625ZvQje-q)7gUxAr^&829b?i`QUlU1Ybp&~~j>@}J1?<tOcvn9>vrUSMJOww|U5ZhY#} zKSFqD?7>3f;x-RM)X5z_-d(D@2PguQuaNtZwqM0F=1kDZ4k>(^QJNK(^QGkrT@ zN#8Fkx^HX%Q(Vil_DQkd0m2p4yqoa*L)uym*u6C6IZ8@(OYPv4nK=D);JWf&&e^)DDUN1Zl2X>Gm99*L%r@nphfou(!Mz^0F+fSF7cFXq?wJGD5O4cSr zaqJ~8m#IJ`?Ah5xb5DKJx6pVcDm%{)^i$$GlxaiA2%8!YA0nS~N^nkR=WFe;MQ@IW>{@F)N^v zv&Gl=-JuN-xKn9%jnEAC&owQ$=FS!VUwrG6##j!Cswj}hAYzMKphy#=f0}{=jNQ4=1UkA&X4AuuZrh7rQ$uYo7FhIBVl zA0nf_zhAJ8wglGCd4t8S#Mf*@_{LosT~oBG^52&*X3I!bSUftnFzKeB-=#Fz^iERPTf~ zX8L1`SE^l#6#BcYFW7%$(~$}n2Ykn6sns$Bzjj3H&6HI(@~k8a5Ak|IzL+n<`BoK^ zLS)~(ch#x^%^yFCawdpHgQu82Cotk7_IfLfq4EMNZU}5Y!9VBiaW+fD<=0m>9~q4D zzc4HVOP0vH%Tzq~Y&Z@9;hyxyKIR;ul8#_x-&5S8#Qavm1t3_s+)u$VbeuAG4@>@l zd1|s}w(HXzL7RL*EZ^LnKfh~xA_!`Wmw7*sL7zmNVy;bV)xGAPIpCr|W=xAjuZ^D& zj>bBev5${|4($c!lO!xk{wui~xsQj6h&1^yE)sN!%$R#GXtwjBja#wc|91KMI_V$T znG_V{*nC}nzQAsLTrz7@t-{^?>v%Q-lt>Lu)IW59zfk@>P@8v7b}$(gdh9e49;#4M zD{U~mblnMjPIfpRVVihvsMgK=6i0Z$O^MokL;IV78R&H7ENXD-FVUpq`H>dJG2C~| zHaqaUJFzCN7vRanw_2FuMV7^K2(?OXL*)&QuRyw^FF>2vsJHw<`pGkG z!Wu}k1F&x)ezr4O2dsk04C-%?3HzGO0r0i9dEMn?k^4vTNG2Bg82)sF67x&_s(c2= z++}5$Lb?k-2@k~_;M=zT)lb(t`}5>=eDwPa)3s;Ln#>M!1vnL^`BUyPw$`!~W5hMj z5?A}7Kq+gXe}6c9E&^dO@@+&c)h@wCEk*44s8)LnF(bh|rkO zOGOz-{Kam(omZlo+*U9(k>kP_$qddpTBCxM!7IX5wvEKH%ptMc-$`~BDI>Oqp<*1~ z$z00W9OTF=1V3rh@T#-@J{}P(kgScgCTho`yd=yxE}S<##H1VAd#|~e0|f~yVVAc} zC*8n-RMoH;wD>hX$3B90ZM%jyHry2r9u3&l{(u$Hlpm(OU5R(y-kRq*8POl+$TC84 z!BP?K*SCGpf5ta(kY^rkYey7*UQOtph}Dx)5fTgd#{(J@srs^%Fr1^PfT_@L|&v1GJCnx$Xs^egXc>hViHh zNud1Nc2iQi@TlmgOD)(C>Ua4uwL_fL0ov#ESI?WL)DR$w%P*(*te7ZqD>CaJoo<<` zG^8M({8>%f38W$7e!UCw=+Cvea=R8}-YiQ30~R$e|4NE&_x7Gt+Y#iK?s1<_KwOSt z;u6Q(6DzapcMmdoNH(XFUiy!>o*D9N$Tu&N_Jr1js|%0^9rW~n`adO7+BXZC`p0+I z{)kXcdul`O{`V^tXH3R~C5s*!uaF_8z@YUD7 z87QcvyoUCi^g%@D>etJ%Ac^mj1GL-;g4y)rECF!jpAo}eq!f(sFaBz*U2SULYr4}D z+^hFy#e?{%8(o4sM+pyF+;)|r)`OyS+3*|}Y3V-@)7k1%ZiT?Gr@LX@=>IPpMbZ?+84Mmc4{A=uD zb`%EBy;pFEPk;L(B&bOp;}gKOu%dQ}8D5g2Uwxw?Hb93gbe*(t-4RAsy1)fcj5mH_ zgS2XfmW#(GWI9$bcAI^aqES`ofB<9*B~kghLUEHXK5$ zm77%yp{4nRrte99^F-ZkPR1Ml?d|CuAx^H~_8u{^ z@(x8^0c5Qm&vOM!a)!O<=4J7ciH5fL#+yIM+x`6~IabI{C@x&CRY!&#_NcXcv`g6^ zUD_bcs4(j%N#3rr#&_Ke+kI3s=0*iY*hI$c^tax0Tw!`%xq(^sjihEnua$Q4uC*48 zuY?;PB75qpKOr`P(#zk_B)*Z48oPhF6S-c^QlP>eW8LQ?G5*UM5xm=vl75>hnc~DR zx)_PG+eAro`HPvcQpU0;ejNmtAg`ZCLEnl}2IvuzX{Gj!N7#2AN;3N3>js(+B9ang zR5l|~6H`L4c?uR-YzqB5;1L+xg+44pUu~@Ib6qx;3RC+PcB9Kys@bwUa8Q`vHN9nJ zsO9i%CmkjHIXx`s#tla*f6n-#jdOF~y5#dcy2W7T=asVdDe^@sHapzuU z4NldqrQA7qOp%=|Qfo9woBV#dr++0uTfg+azISc{{lMEs_W}C5PfyLWNrwoo!Noj- zoag}p<5MC@#g92#-JwG@DGHvB<%+)*h78hUKlOhIQasFPTvfzw7C3)Jj-({=Rg6Fo zbJhtam(VRwgWIkUZ{u}u9ZrD4Z^*%BtIUEkN*oK!R>A^?7%22m&1>+>$sclI$m1x+ zcM?c>6gpLV`T!?3TG6+I0h)iJWsFfD*c#u+5ax}-I_Mni#J_k?Nv0gr)PEn+F5X>*iTVd7aigp8U@JYd0S7OQDDd48 zDv6QAh~<+E4#j+Zn_7eP%sxV3 zN{3u8^4Rr`0eZn3u#IGD8_#J2y!276lkqknAP5He9<=!DyDAgpGqgZ6yOVz07GEs6 z!+lzZljf9HxOB*ZWa6aAuzepty5B60pe}-3LU0I`D_>rrpvsko9OL7=x!&DlT~e7a z{PQcu$bW+jy*KL1i3RfcuKFw#Hl7^rl*4vwEF+8Y%JrKSw875l%6aso7L@TWTQy;@ zR^QgaPS9KIlWmUr$9DGi1dEs!Zkia8h(Kla+a62NmrW##@UXe$@N~So`mH>YU#v-= zhE5dyUczdVxx!CK*-!QCey?LmVZ;We5z{ZCTyA(Nvh2@SQeZ5JUo&+O$@6! z1=R4=Mz#!}a{{}~>0`q24&y4l&Jd~24J z0B@P%gCy>$pL}o&ua$hjy=;WHkN2Y7Yn5phzx{{IITowl5tz3?3jaONJ_barbb%c1 z_300;hNOlAR}>OpP0+Yb`$5*(Ax(BQOz1W88+KH&Wc-9p;Wtw5P0N^AizFP@ozp8Z z?lt%w8N`bX3h0Ut7j41H?3wLj)8g{ShUp{U8^=zLiwW{4`w(O%;@m~QuXKm+j%Ghj z2H}u?&9{rX8KzdTD6Gm(VYurdQuoR7yLe$h8l<8Dmd4P;7kR`7L4Neh(3YZu|pkIQb8+RplLjzeDT0}qAI9$kkjxlBf(D5<8;mhvX!b}j@^ z0>KZ^*HTMYIA0zQFLtK*&lTR(@BTa=?)s}t&rDo%(ztB(-3cb)5}V%^{{GB#O%TRr zD;5Q9#Bm!`OnMq1$&~QG$Oy!PtznxQ8R1tDBI)teQsms+$A0`x-U>mjQG^pW4A%EB z%YGILH1LC$8F6<&n^k%{%YvE)EY!v&G7PnpagF5YC7U_+L;M#aeywWdj$?#*b_v@q zclI#WuRgi3o8e1H=;wb-=uw*%z+$W2FuX$FEi?4WWqeig7x_AkFd+oRWU3k1OoH-d zvbD}mN6!7|QKt&OZ~`A()3T6>7^YJm8+>=WLX0dN1(BLC-3(ZscsArLeJC;eiwMtY z2Tl5ke7|xX9N~^q_o$5q&4hZZD*G6ah0nb_^)E@Od&@k*?+v$bY?%mLd_R|Mf#Yt(8USyU26$bSoQxN}>6d_{C&=nBN?HKl8+F@R8eJ zDLGaolWspXmp?yn5dQSjqswjYAH)pM*-HgBS9YCWVF3qBwizQCL+bpw6>dL2I;&dG?r_ysmrvyziQ#hjar!%F-G6w*=Pkmr3}i! zC|$xX9_G$z8?BBL0*6NLW>TAPe|~-if7N=F&NQ47flaOl z+hvwGVy(%rs*%Z^wI=shd^c`HXcIP`XI(&2&41xuXfD(5SZxHk5~mHe`wUL*x3w~3 z%(&UmxH5`8N$qhfMwBb{nt zzAcPUG(+p`bY-(YiFM}lN8sXGEim7^v-^$t8r8JgyQ3=9>5U+TH#A7a(3J?3rwbw0 zITwzj7+-SqbmbML;s7ZszvS(k;9`4WPkzvHJ$?edusC< z*-reZu;m_w{k6e)_`x<-*kzD~AS*SnRU@LX##21~v8wpsQZg%rOs{oGgf9Jj}-iL0(^rkw1x+lL&aWZVU_f2bLppb z6_<_FXZQ1BVxY}bQziF$G>Y;?;@PE@PB0a$g*Qy1L_X??4&pt2x9;-bkpVUL(zMGw zOq15owrx7_A+EQ%)U?IcI^&WeJ-)rhk__vCx?N{z2MB^$TVuOU<)`O-ZSmq<{k?6~ z4)!hr`aBgr7CecPOX6PWkkK5Z z+hWC6okp<2%;uONe;mw_Cfz~5po9P0NZvE?{s|c&KejcBqZ!f?e72iLC}Wf(!}p`h zNxwXcO!gAT-=f*aXe$)e5#4NjH27(sr&+JpF#`4`-jT}->Zo_{pDWHB(Y-Cbh+XKa zEnvGTFL%KBEBWL5`0l|fLmBo)dw2zMcnQJI=dA($HJFrIrZtCc*9;Yej7tHydUUZ6 z>;1^?DCsn+xpGLeEgssp&ilm}j3$=9fWI*74D5g^ibXa>r&}f)m94}-6lRWnIa@lf zcd(lS*yrEgt{{U}BSI8)C@RA~oGn@#4ovf4z}L~|#e(xm-A`4r_~Yh698orEqjOJ; z=9m^A1D52*>C*3^Iy40No$uS7Cz7t5;>MxC?A&V9iygKqK_hW40&nQyo*#49X~Y{kBWOj9OJ7TE4(esx$SUauUr! z)A7#So~v`YEB$4 zJ*W7F)h3x|yKPE9W1waRD)O0Q#pgG6+L%Qa8bLQeS09-V+J4L6U?iD-57hEKuwplR zw6-|}^tBT|t^xA&(KJtgPYS;|O;ydFi46<7Z-SPX4#H6!?0jN(oIfQ>Y&Q|rxhGEe zjo`*ZYWVSB+nZ^Hw@)^|XIVN2j%8WBqK9w?j1q(cxM|z(^GTv!e!KLYB0;46hC%AN&XnF_Sw+=&opxh8rv6mS; zMo+11CD^X49pIHj-A7G8Z{=iK;I;bqCdWE2gE-eh%d9V2Kp=HyKB9vlcG}S1T5xj< z#3&DbY1j3?566e;iO8L!%oP!A($0M&gEUW5$BXwrowqu@Y{0$f&3C-Zcg2z$o}O5a zu~JbPrhh~_`>u>R(+G_|t+QrMc;zqZ21#g}Z2~XYaN_&cai;La$V&@w=SNw$@OHdl z>~Ps9E)j&KuKdy6?iI zsd1#m%BPSx3hr7-qX_-3vKn%bo?Rsq6!_CXfZunQ0S!a^=d^qTHuY%*gldMfGhI3; zJ&mv1YCMn^x*G`FrYq}K9z`DXc(q=9H zvJtb$B>MV8UTJ$7vwct+2-h+a@1;DUwTcWub0p}5voW{GtP!KmyR2A$UJ;+oqMs`9 zN}EVap~Rh8_WSF!v+;LGob#z~c*!a0ywP1?2kkd{YS439mXnbPF?eY9amiZxl)?M3 zp#Izm!L|Exnfm7r_Mgjq$9sD+f){-I4pOEHOsxHtX!LqV?2}qGSq&mZZ+f{TY_UgW z4-Hi8H0>Hv9A4%oqSCVMzI+|oF=x)gr9d9YL$RK1Ijq_DVcV24dm^lSl<()A&yc0{8jOw$dfn8mj8vDBsA)w`g35nsj?wI*FqDDP41$ zLj&V)qc2U0@`|u|#r}vdXD{oB^}nHBy*GE=ISNTmou$B6L?Z|2fB4^%ZWycz@if|g zaJ^ToB2P#YPVde0i_FIVuud|?D-#PJVnc7noiHPCph?@b6iQ2I!-G&k0$y1T?prwu zRDf_O*H2Y(Kf$l$Drj9*oi&(*is6M$zRi@+#WDKt?6$Du&o*M!I}%rF`JsebTh-0i zAphN7i(W_yXEdKwz;D*mr+hfT8JIu$(Xib4-q&Z8TkX002*_qds0{ONw@lhX)7_ih z$nB;bY%Dk*?;?iybTtz+0k*lP!wgI}8pQLVxHrwKmbkQ|m>N-n)LFf&E3EZ;zcTY+ zn|=Juubv8HYByJRI-A5*7Y6IERxVtZi$Vvk>?$wvS@)j6BT$i>>d`+`c|;kpl`#c& z_l?mQ?5^z9k?G|ZP2cRak8tAB*;*CI{9mItB6yLTU$U;jp|2oVN1sa=%FXHXC{@64 zH{-i^m9BN>C#*K3-k>gHhh-z&VVh~LWm9Bg?NJK$tLUCvms{w_*;T(lEMFoG$HG?h z=#wt3V^uedUZb?o)7p81a%1SuIX59c+@8pDkl?6j0RZeyF^Om+a>fejv|wdSt(CCs z5PgMygr2Il8ZN)1f5a?$XJ_bgviMY*>A z-nf6nJNkWLZJ(6cFW--%yoK5dh_<_uUiw_1o6i}=Y{v(fj?hEQV(aV?D(4@)jMMIZ zR{+vNo&2f3=FGJvVJ{EUE5?3f4s<1pW0`#uI1U}ZVd15J!ozL(?z2G z_{lhdPKFk*oYiBodQk2ErHO{J9^xQYE;US=0?)a`60b!t?iW9i0S<)m~fL%IOKwMdww_LX)~-3DFlRm znSZs~D?f^VuZku;2l+04KZJsfd2cCWEm3>;lqgT$@wp}jd8@WWa`&b3s-NiCk+sR? zgG|bYxg%}N%AlBzznWhF6x<-a!EzlZej`OxB6@gUmEc@KlVRp$t|n8>&zaRApzw2dp{{q_ ze?8xlq1p-ODRf1+l5e_le|Vq4I2RhRiN~Dy^2h_MH{`F#kgJK0uDApuN8;&w{wQBg zBQ?r+Gju3E6Geim05Qvq5p?Z12x-2HiRdDHsMz&B$y5pW9$FUG%$8-2#&o&k7zfCG zer^|^SeQ0Y7bDl?r@L5U>I?PpS>%p;r={oqR#$m(rezv$5a zbNA>KVl|W#L({1qMx8$2Z9_888XeoId5Z<}CESIvuAEWf3BL2{uL8~G|Dz18I9Cjsj66ZdJM_7i2tzwDhU)m zN0m&!?eiL{&))Ti;XeyamBUK^UmW-;Z0Gg~W+2vYGMd#r^xtB3cK$J1U(SfU$@G!^ zNHV5~XGpQ$>p0CD6gZ>kW}hAT|9^_R)>Ae*!M}AfU~4A37RWvzv}#S!wFSaAL|*J9 z@DX-})mzeBw)5@w z+<1oOA$g=Yr|m@JCI7hu`v?ybtr)`uti`@qCbsVV^|FqgVIV$=I@)A(W`(fqDyt=)xE<=DN})O?ri$aJdjz!&8#ab9GKfFRZEvP(edpBxqtcQ@IyxU4-I z^s@HPiACq3jL6Zzet;9@+m)quq0yjkX~a0zm7zUh%u^6hwwo)3O334D9%C!+0MxCke zEz`Vh;PeR*j(QBAL*ts(XXng{0)gv=9UGbyX3V*2Ea$6)TmX=Uj^AVQqxmF4gnUG9R-~1Bwmm*!9 zgeCKGTij6;mHS!#X&a+x#(VhX(JYx{(-yy%mSBK$`)8AgKtZO$+BWGS;>y-Pj6Y-H z04vW2*_aS2`~st{=%qB9byhrlv9#=Jdv4EE=qbz-5R)t1{Z3DAYg+bka=De>73h$y464BE zY5;@EFE&IA@`u*tRezcrhhJS%U|u-{X~@ZS zI>76L%3~Ky-!5qkin?V3Q)cTT8bde^#tXAQxDX@lw-P7r_n!yAuaWfkAnGll-bF^W z;JwI&R&)?BeAKSL&^C>bg^hP1%RP{vmbizyHv3c-2Bk%>2sTNty+Iw|7`=a#Gz(f3iD7R5*PhII0ak)GuDw(rIGISz}fSvHjtC3V!`CnTd zEQRhD&e!OB|3^MAqZY>(#M%winN1`aOzKYib>0Fvsa(Y~BR>)uS&z*m>1m(fi0KxA z+Iq;SUng#~h{)iF<;gCFcJq*I+3ONJH4dI``&C-^%5!{<6GtDL9qCO;@B{X(;?y4_ zA6~Je{=B!vhAc$7yu88dlaPAPd^)P=({|n;W&h>jOn@QEF&TTjU!gJ{pv8?38woLE z+F`6OOuRx^==c@@S&mMdgU~Urz-^C*<=4|tr|h=s@&BJn7kJO$PopC$E>S?`6|@9IaklXfFh{vud^Y=$ez}JnD)A>jPs%>9Fn3*IOn=l@S z0yf1_bZCuc^p548B%8 zW(Hn<|BZfcIbM?fVpD*0RJvT5W;}tvlRtF90P_mSeUiUW2HDS~bU1)%&DZh=JC(hU z0Z}g&s`H0LIkr7PSyb+wv4&X}r(=f*p>A4(PpL|uWXjsY;!2tyar7jT$|PWwy|3xf zw_4+JxjODzYcn)HFEEpRtr|WwpX^AUIZEh9d)UQzqdDo5!GU+d z*b59j;?RtY{VmExq`>JY-2PQm^@=VUrIH5KT`quBX1!> zO`bs5T5?QY=7IAzlr#0C2yIW71Rp@3yqk)9b#9)KAYauh#zA1!9f7l%IZB4wsfD#% zuS<`yI3KDov~ECDY-3k&*r+t|wL1kzt(CKMZDBE=n5?43iC zE=;g)ciXnP+qSLUwr$(CZQHhO+j`r!-RHaaPn^k_RMa3x88xWLcycYz4jNb%AN!Y- zuL~8R>6R&wSAS=3nwj}ofp8-zlFgl_0}C2HpW;I4N-P_Va47<;*#ldTQY}itg-u@c zz5>W!o#5`ux^%(F{lk;t_O&U`O!{@ka$ZB=z75NsOxXk~utzy0GZ86LHD}o4b+8TW zIq)!guEPHg?&Fq|A{<_kRg5yt*&^`D9au@PDHubQX@ZRVoXpP7C*a#Ph5Ypl+ ztAz&D{=F=)bzj|Dzp3l)b^r-2U@P1Kk^8Y!c>JfcxSm2&qYJkN7a{y}M>D##10?Kl zsm`H~_Dag`%viCDLSK(Vr-%{?f9^AGluH$WB}sw6U2AV;tl7tVZls$?_;S00CqrHw zS}xV%2~|J(`EC1xxd7oX99@rS*#2zmHoF2~pQV_U)g}!-#Xq&yCvt8 z=+V11vjagG%oRC6sh-;_tcaKE(|RAd!bW0BHnUOtH#vM54@&%cT0Ng0z54=Ug9E9Z%fHB6}_|PZZ-k>a|MM3ux+qLX*xK$&U4SH8xhi;ns64erd?)d z;2oQUm|K6kTg+qT{6H(Cj>%OC#3XoT-dgmQ%5DRFfB zb)>Jga>Ym;vNOlY{>-0?k3P!E`_GihBmd)PW^TfCT@)OtrUzl%jU{5O2QpHEuuzVy;|vwjz%z|%cdu5seUF5>jJX4GkY!SaqX zg2PZ(EpW0W%VXyoWvAszA-!26H|HZUgPjXx*G6ZsgJ;5hbCcuMuEbB0*@>NGoxMv_ zJvIV&a8wnxz}WY@;zApv&LOSLyW&#=c5kivJskYs3RmRB@=)fopqZcR z5nPJ?600PPD_q1W*yMlC;yIUsU*BzLDt|$UE)9!&YE=xN^x8Q-WQBNq--&PUQ z8L%&=q0gly&?~g&zYk>Lzf!(V9Rkax%y`{m>;*HcIkt)`Cf%e_eK^st>}vzK1*bQJ z_1m%piTls;I|q;WV~x@ntqkJ(`*yrVrbj>Q^K{?dLF*tq4g-ZikiGWOAlPkd{+oua zd7kceeDy74h;<*w=?Qgb=RVTh<~F@3_wC#s?;YpTmci&CNKw2ifA66yq=Tz1zxckq z?yvVfn*n?oxcnkdh)7d@!gHW(WIh{~U+n7o%5*5{^43Fg=O9=UV3Xy0)t|0={npg& zJ_f=$XnpFX>7f^{^NyY8uph3YhhcPlzdGb?!Yi9N(yIa=?_%PouYT?=J)L@(w zk5w?P1=Zu0HHwOQfpZ?qW zhVVe1v})^mA*8AOakM0Y^ouhdDC}i!BG-t`;~!ffbkx=D1T7 zrm2)!%kimgfL<_O{)%}7B70V3C;Q&lqhE~%tv6=_sh(4Nl;HpfXL3cEm40W3CT(Tr zvVOc7c62(sSGF+;CbddnVd>NGb)(dgtM78cz!-g>?G;ysMqK?4srh(CgY7C?hbWQQ z8(a>QD4Yp}n`AulgE=-|)9}eC&CPHfi}86f4f2b4hEOZQh(kIu>3Q z&Hc_AyvJ>CoQY8GR4HK@k!IuY_&#l`_G50=g;BpYSu@(WBYd`fMF>x)s`0P-55lDi zXjk|6^qgtN_CQ4W?6TP0C>&HGE9j1Uf$^zR&UOVNCEFPc1n2dAHuL`lF!BK)Rz=tr zXr*tuw(a#A6r}3)d3o>qI@W5Gv&xdqFGhgs)3)@snu(f4cuQ!T?tn)2zTzM)GMlw} z^(KlNM)%tfbE`!XA5>yiw7M9lSAi0|bjfRmA*GmXw}xQ(3V(fvU51t7#GF@$>g={> zqi7M<`L^E;f7r%jDcx7O=q5_&J%AF_+b3stc5iNJLonlAo~iB0x&QU3*rQwHeFeVV zvc2O6@YZAR`OB%hw#FFK&t06Ds8{}U6`QE}%=5=O>SH9!=LNmkv72NUUhKcuQJRqF z<$j3)Vq9}BBKl5C$|n|?7^{ES$56J`uL?YPR(%bn40U^VzHrmRB@8nCUop!c2x~K5 z65CN!krQIa{4wr6e#fFfpgnvO%+_jk$kE%Ue!OI_f&cvQSF!K@3>^R5j!6z>>}>g& ze>GYhHy}J5|6UFL`P2UkCDtu~^#3k!6u^J}tOKC}{rPkCC-TpqKdyg7|2w(;iU0iZ z$NT>^@cEE`{^&!WF(lZ1J{j7WODz3>t{v7}o~Zer$Vs@R(JTiyR~D!1*raQL6RB)`#0`}u<{oEI4`*$$ zLEF_yIiWT}_V`!_>a7nA>&Ll!`Tb~}Rf)tqH0%ITapANEWjDUJr8%FU88+sh%#GM5 zZG!?2wYp6l!iBl>FM0{6D0ATVx}Wy8O=v|OiBlBCaKPN}!-eL$Y+v2%mx)5i$t0(s zt%9*PsyjcdO^n?4IHZ30i zeSh6MI{l0mia$_7bIYr>xwq#H{UqBQnxEi8qo9^%5M2rc#jg%5J)=iV^2+^oA=+oa zolEpqg(}VULdA+lSy#HZbq(UtbvJdyv!mB>tC{#?vjI`)P7&w;<@UFB$&scfbS}(L z=V6!(7F}Sh!3h-hk^dqH3#nU0Pt+4Pm1b(=tDgmTCrp^51yHh3!&eotC1*(+UGuqU zP?ZX}#oUO@*Qg7l^M*X+xGAMAl$~tA6ohjM2G2qi1xJV^oHK$LTi(+I;8q_C>RDj1 zDx3r{g1J8t*ofArMIciJ6eCe5>LKZrhY0Ac7X{czx;+nLQ+5wWV_2?)J^8nkt_zz@ zFz;h({)&~-^}gL=ohddkrkPd-j7-ME{5Bfi=?eFxZvA$4aJFZ4c}f0ui-rX=h?Cfn zy+9E}1JpsdQ-56P!#KYdqpGtjE;mu^k7ht8cm&*`Uqj7xgn6?Q=OL?MLWpBlR)|Tj8dbpuPT3RyJt|S6u7``La_L+nDCxJChJW!A?G`zC+-M|#B8$=cJ_cyPhB#f z>0)`7h67CfnJVW^t#`6Z({Mwnvo#Mw*4lghxE{dvt3!v;5lS7qx=1E^+aReocenCZ zO8D^c4s6J{)6Nqn7!yhB_gvg~urB;?BWkcxAS{CB z^|r49sHvdTe4(>GgjgbP^Z9uu;r#tLWt=WvhglGRWX-pJ9FFwZ>+!S)a5awwf7@Wd zE`No1Pyk3z=1%8JL*EtaQmcb7P_7E*%ach@?bmp$S63bN-hr)pE4ogZ-Gy1Zf^Ld( zrr3WY>-l^DpUm?V!40omdIi>W|5Wl`|!ogQ*cxJ<9Y2ySgRLX(ksYb@g2Zx;Okh z9?|u|yeSpsh#%KDChqB1Q8;kg?3|;vD|e_u!4w}0J)+OHxU;uxU5|E{o$eTkS9;T& zZ0e!b`9gChSArdTy1h(oF7T+LZOKpVJ46OH%PlvTvlEC0aB)*CRX_#F z8{pC@7F`^X^-^L%{W+wYUn0D4A>}I>cH_)kg+hxajv6DFb$ppsBZ5`mK@gRSdbs`; zmWW0MQmOVp>^!ryN2iZIuze61Y&lGzh;pG3SHs}JIopuBZa`p`nMS|ETyx{zJtBeY z|Isf1Be{=r%DztOSejoNeQu?>XH6}O5~XXABo#ep3Hb{mA>cQ2S7PG-LIGUDREs~L zmvfD;VO%^aNv^UJ4G>OTjX~$|5g!lo1)RkSy7%1^o`@gfv|c9ghs|+X*@k@0am%A@ zRPC?GJ-mjR+tymI_?ZK~f_E)9f+*6S2Sby>X9Zo~5xMSv93c_RlxN3$0#y!_yo5X9tEMp5yN04$w1i$8se0%S#2oM>Iux)MBX7KyUw$wL(A~wayt)Wx;dJ(QvN8D7zSuj zEY@&6 zER-L~hCjpw;e04jUb7v`qil-Xj!)70;oNM0fjPefaZXVVGb{<``HM!U!B$%`Xl^OqJD4_e z=FMYQ$=7BR1ga$omRqyYzs6r<@&h61JZ|LuSRr(=RwUr(*CX6F3jL=fk!l+1zJwJQ z=@dq)aWkY%89*;;z*%bk^lTeJf_4_qMQDDDXO9cm6QOL{17 zwVqL)fpq^p?4N%gI*X{!rn2bPpNUi$Hhn{|>pF+t-SN(Knu{^4lbolYXJU*2iJgbp z70a)}CZN876rg{N)$3~Ne~KuD&ig%)Mjx$IY7u`Kl%&Zt3*Qu_WK<;q2G}XZHxn|$njXr2ZUGs_kMp#RF*Z}S%g;zum~-V=w;ty;@2dU{4j=u&18@^ zq7z3dO}U<)q6QRY%)Zn+jo4Ml|I-s!89FUmZHat`pCh z7*f^wg|asudAEvhd!Ctu;q*oz+}HV5K7cjdml=MIuq>BrJm##@d4Ab_OiJ|G(7T+} zL!PaEsH41NUW)sLD67X|eDMrSoem}^+dm9QP(4r`bKfkAXZOIWBH2E1gsb)vOGr+? ze@jzg@5m;ZL0Z#06fl=GE3MtSA4$6@5*luT;x!xQlpwD2Mf z)v;!nm>$^3yI>(b0mS#QFQ4jy*9#9gL2O6n1wJ-KwcF{R7y`b~+9+05z7xG}y+&F< z7=lMVZ8qD`Hczm7frEI?5XUHK+Wpo~Sl{c)PW_O8XCfReoFAdZ(U)&|5yW#}07EMz}Ex8XArOs6X`s1<+iFH8Rb@)w+@LBRpr+$jC z7$>jTc3D{ycz-b;gm5>ks$_d0(}KqEtn21KWZ_08_8b0PHrNDwFe;qj=JoFyqcvPx z?CklSQP(XjVLQ;mx^kTSI}9%4o}gb9ynYqkmU3#QuhAeTiU~?Hj9XJ_R3)Zlh_bOXXttnN2Z|uZ=Wu>a=5)gJZpHn$I`qxNT8$rqJ{xFeqjY?p9i-8z zyI4wY39)t&(WhnIq%;I7%+4cu5XE|nS0YzHfjZlfIJ>ZuyG=E=4uYJqd2+|}WSETO ze>v;DHuJ=Ie}p5x=nV@bUYL|v-Mlr19Rs(sFSga~9zAC;+JUmU->YD}ZuF~vt^luc zDF3n5?!Qw!wocywfxvX9SLjx&0RwLMid2H)D4O2kAK@j?l0FTR1M`l|4;!EyV>!!k z`EB4Q#U0$*<_xto80A;Vo|oy+??O^p7hUJ0W?jasxepbr4hJ)ppbOdgBRXra>wnBFuUcB z=#-O^v=0l#4-Cc5_6S~M&m2T zQX^Vpc@ya2{yW<%$|6{bmu;;J-4MG5(1Kv zr0GPz?U5HP_tl$~2eq^tdhrVZ>o3pd_;39pq)rXGcq1mvU)uLa3ok_PkzxF%5H{GI z{!QS2i_WX3ACCe|?4EJp3aQj)X4A7f=y*<$)GyCiHq+XWh>PO?DH4c1Hb~f>iq)Nr z73;r;@8KzTK4AB;(@3Nn2kR@k7EuyfPNVi_x875)-X3AExq z&;^cyhFsl0XjgYjk9asMItH#+L<vDTQz{+BjYCeA9e|TvR%^$>PKG;^ zCBHFb0LYQNUx#S*K)5P*BfKcG9rm$Xq$iiZ?40;zM?v`PLO@r-@xDNFC&RE~UtTsDa#^NCaWWvqYCx62zzPqzmMg09|K zA7IvTQgzDQ=OfcRAdJ~wW5ew2a^P?kL8S$k(f(a3SU(KuOW9*4_M$o8(tY|Ca)q`g z65#O>yDLJIF>+)utj34ii)m+US&V?pZcg z^yfQ@f?$-41Qz|hF6ya=Y)2##lBi%YfOQ26gf|Ee4DMk!M|_478Puf%(+M7Y^51HV zIdSRLwdY*bW|T?)dsmSaP=qw>|1U#Kd1CWo2>>;ddCa;~Gi|zw7Bll`$}D-iOL;Iu z4B=1{@L=8~w+V7`E1?`CI4_mSSfwJj+B$`KRUT?wY@EsZ6luQa#NG`l6YwzlADW5i z6d~YQi@RzKK{iw7@&n49;8Bs|B#XWJvj9$8%O!hdEv_gT^DiIGGo0aWTF@uI@Z|LS zeM2Svm=OL-uNS@@{E%m!I}fEttcbC~mlp7`$yU{PPO&!K&;tW?vCA9U&G*Mp`cgDU zSBd=8oYgoc9xVLC^sb-h!$n~K32n~QjQW&G@9nXgkV6JPu)9`CVNNBu$YQXdbLgc3 zHs}N9>S+Dso6!O?rPZV+=OPKg!xLhR8}xve>BGhQyOu6}ZQ8bxZl|f~vpHe=`O?dQ z_*m5BmvNbnBMJYL9sl{?{Sj($3h|9zSE-0pi}zE@79m!<{Yg>NgG@B%y=apg)65OR zkE0fy@RGhL@B)}kJ`XxiJgHCL{Wqf8%UeET3?rJfii5=F7 z3Zi?485vy^Ue|;)N3-sQ)jQ%yQY3FSNscNSq-^f>xEOtdt|sjXZvIT)Bc9|>WTnoP z6i&JrLbI?!mt?FJ8D}qg}KQtdmruz!i*R zLSyFZqrMoX<<_@Ela*bj$lZm4I7i&e*pRXl^O+{ykI6we9T)ElQLhP#{h?}G~h{9 z|B5T#RGQ)ns=$(N(bk~PWVKLY-wF7d>YbQz?LL%W-f1eNyEa5*D$}Qe5hXpX#6|Rq z1jR>dzOJGB#0b85rx<#tLAl4)q4N{<=MV^+r5rS{g1*twc=T%+p>W|6yN+NyLNhBe zb$LOWz{w&J+#S-*l7x-M4B+>nUUvW~rX#O}Q8j!2!}tV0ql_|T3~QSN zM)W+3PlXo})G8GrcK%*HRSRW+?8VL0CR(cg($!n2$nSG|^Jp=SEoP(@c?ej)qLv<+ zV}c@Xx)s)hSMcuEq6PEO-z@5h@W{ABH2Qc)X#p~oK!DiM_(W<Sz;?hN~}3J6?jDO?cAfnm#wv@ua93Hi#brq(}RTtS~~v7h$7 z3}Znie-B+&sHHHl%i9_ye#~L@b1f7G(Royw6a&05wr(WxL^Ln@a9oQ&r$T6$&9!`X z-(Te6UKCXx;rCiMC7^*9fG@M|77&g;2PX5x$-+Hy4$ldaN7TS7_p+1#V6#Po)mQ)7 z_cXOwsG{2v?Mcs!T1g<>l%_zg7-V-wg@B6tI39V^5Rc?Xxvap=<%SdD$X{RmddZDcc24GvOQ1ComwBoG@QWMdztQcX>5L=|r|&O7X}_WyY4J(s2b1RO?$%XF7Uk zYW?clMGjPG#Qgypg%~D&l1ovddq?=;o7;#O(%y=EJP&|GCqC zpGC|k+EO%)WIYwuGqOhmXx|Xx=}1HM$DuAOXJo2XM@=gWcl^+*O%@CBpe5jr_6*5Q zHO^P#YM?=WYRf>TXpqzk<#EfWzS#k24Qhuw zgE~fhCvPlZwBb7s&NeHEC5d;tWXaPqzPv7j!y01kJ7h_Kj)`P1)&2xwSO)fFq%2qU zKS{9g*hd=eM60U59q#A586Sz+TceZ2gh|=5xqHVsXUELebp>#Zp$GzFIF9@$;@~rx z*ZM9wcnL6jkOIG!fSk=a-k*w3WIsE=z+8{m+}SZkLn#yb4ZB)eEg?X5!#iS#;=w_c z5JblUNJZ~<^2LcBQoJ=xTk9G&L{bdy$^HkmP?8{h8;)jb7TqS||4u1w1Jrm@N*Z|5 zKUcvHshV~KoZ&0YBMKYS`WI3-l9T+qV5+R6U%k-Dm9loC>*s-bxh*-FbnmQ_>|oC? zw&g8HH|cW`Soy$%6IWJw7r`qX>GgM$#^SNZZ!^q?VD`$D8>&rUd)5)s?k!9QDp{ww zx3^8zT4n~xL>?GALmC|OI3NY#V?iy7D0Mcv%r;GS7k9 z=r3!l-GtjA(bYu3m;ilZn!W;PEg{_btiU_Z49Y?&g@dZ7U`3YxRHTjJ>_WP644-Fx z-Mt67!`irqXk#<1G;Q*HV5-0Ber8kVE>#DLWl zSu(Q;Bd(4?a2&;yN=bt77y$1IoDHJ%f;WPtYhkLqD-b+GP|ARp;apO8u?@)7VFi9Zc5$Hch4uVgJbh3zy3(f8;nA?iFeUU88%`2^&Z9x4oao_0Age+>b_yxeXQ953&~waV zpbKX!-+Q@^Xd_T~a&DxF*`zc1!f5k;UU=8R;q#z855dc=hr&m^wB%P6=hH9Yc__Su zs%3O@Z+xwwUCTDuj-8MsaL}sQ-}h?IoiJQSASj1?My70U8L8=Cod*fWYteI}{3*M0 z)^nB?bk=3ZglNfPWOc;F1Ols+HUIPi>!oTakso@-q$e99Klu_IfRI0Jt~G>?=F+`K zl!IjP@W706YdPJ#lZG?IsQaePvI6x< z5bBH+YILE#RV!vJWN%jS=Q7CGb2AU@I7GJZ zzAd6>9rNkRBsJJg?SzB|kFmN@uj-0mm;0>UK)2rg9<0V*JYe-J-g#fLWRkF<6XSFc4?fao_jI@P@QL}Fp$B*=p zLA0wN2vxCyzcE_F>ig3-0M)|YLogyWaiwPgE6F=+Hz5#~pnMF>s``#p1(Q~Jqi_-V zxpBIe3GKK&M$g!Z6ZAvW2@uOx95W6iIdJ}Eyf<`%`WpbydbG-?S5N7Bz!`|P?T=*f4OkgJC<-~ zsU<^p>MJrQJ$n?y2*4h1*uRlHzb<;;h3xK(ny77E<9^-x@7JEY`{N^8Ji|g?orBCb z-!CGa#c-AYHESY8b@DBV5dE2eC&H&1Kk zQ{k=oTdee*Ohq@XVRnXJ<^-*%m#@3}npPG(Ysr|jL6JeE?robA-lyFF#0!%Sm!}Ye z+mz*v(rX8~`j4k>KO@KJQA_i`r>%CM7~5JHIQQ(j?rSy}3+?W+{>9dOKG2ShKTg+!1^52PC|!EM&#DL%X+evGk(c zf$)phB3$n`*P?J%tMj%SbGMjQgZjR}#T2NiV{(!$dbWd@wZ%{^^x&Nl1uWrJpV=d%H?d-EVDc27`s z;IvaZr7XJF_klfdLlK_!ywf5NIKRHFJ?%n@TcP^tYf*zP&$JKK;vVrFPHB5fHyf;HLw+JQ_-J#9 z#MID(EiPdp%~djmNAu-b90RF1T4zD?f=Su3wGSt;N>XDa;%Fd zgev*!h`_kX6?1d=2RAWc>~l@E9Cax*LZM^>EVww*`D7@}I^90iljQhW~WuOBB$^6NUZqXSA!M=lZ4Ca1AAgoL-FhccQCFP-h1ih5N|V|UWf7}!h$r+ zHM=^YoWRytvHEG@{SF#iamXt?_o~fwi}iW5N|ko%vzqxCsG(}`!S(rRCZ6Fhm&K-I z*n7-_Qq+rlJGA&R7XVn+xf(#7573y zWF7^Bz;RSlgyt03$mv~PYe)BsGszBgou8#wW{{9Bm)wNc`dj)~u+?q4T8qr`Q93hb zoUVm5y?0-ruAOM87wK&M-`aIB&Rvl|S2JdKDmz{qq{Fx9oNrgW5%6k<;!gGGxukYP z@{^dH%NEMaZ$iI^Eig%)zwKE~zf&hreWbgA);OfWT5`9nruJ`AV8)Km=OU0GRq!Cy zVpu6#2wv%xWZn*X_3ExS`CJ$7_(|fk-o^;Br;^&}eY%Rbj=FX@Wc5x~miLpm&?c

*Q+c!%6w0)_2&_13;ypA+Sfb#YN3P7B{ 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 X1qJ{B000310RW8v0049x0ssI27VU&J literal 0 HcmV?d00001 diff --git a/autotest/gdrivers/snap_tiff.py b/autotest/gdrivers/snap_tiff.py new file mode 100755 index 000000000000..e8e54455d12c --- /dev/null +++ b/autotest/gdrivers/snap_tiff.py @@ -0,0 +1,134 @@ +#!/usr/bin/env pytest +# -*- coding: utf-8 -*- +############################################################################### +# Project: GDAL/OGR Test Suite +# Purpose: SNAP_TIFF driver testing. +# Author: Even Rouault +# +############################################################################### +# Copyright (c) 2024, Even Rouault +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the "Software"), +# to deal in the Software without restriction, including without limitation +# the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +# DEALINGS IN THE SOFTWARE. +############################################################################### + +import pytest + +from osgeo import gdal + +pytestmark = pytest.mark.require_driver("SNAP_TIFF") + + +def test_snap_tiff(): + ds = gdal.Open( + "/vsizip/vsizip/data/snap_tiff/S1A_IW_GRDH_1SDV_20171009T141532_20171009T141557_018737_01F9E2_E974_tnr_empty_truncated.tif.zip.zip" + ) + assert ds.GetDriver().GetDescription() == "SNAP_TIFF" + assert ds.RasterXSize == 25548 + assert ds.RasterYSize == 16716 + assert ds.RasterCount == 1 + assert ds.GetRasterBand(1).DataType == gdal.GDT_Float32 + assert ds.GetGCPCount() == 4 + assert ds.GetGCPSpatialRef().GetAuthorityCode(None) == "4326" + gcps = ds.GetGCPs() + assert len(gcps) == 4 + assert gcps[0].GCPPixel == 0.5 + assert gcps[0].GCPLine == 0.5 + assert gcps[0].GCPX == -121.18662152623274 + assert gcps[0].GCPY == 39.655540466308594 + assert gcps[3].GCPPixel == 25547.5 + assert gcps[3].GCPLine == 16715.5 + assert gcps[3].GCPX == -124.43485147116212 + assert gcps[3].GCPY == 38.550738598352105 + assert ds.GetRasterBand(1).GetNoDataValue() == 0 + assert ds.GetRasterBand(1).GetDescription() == "Intensity_VV" + assert ds.GetRasterBand(1).GetUnitType() == "intensity" + assert ds.GetRasterBand(1).GetScale() == 1 + assert ds.GetRasterBand(1).GetOffset() == 0 + assert ds.GetMetadataDomainList() == [ + "", + "DERIVED_SUBDATASETS", + "GEOLOCATION", + "SUBDATASETS", + "xml:DIMAP", + ] + assert ds.GetMetadata() == { + "IMAGE_DESCRIPTION": "S1A_IW_GRDH_1SDV_20171009T141532_20171009T141557_018737_01F9E2_E974_tnr" + } + assert ( + ds.GetMetadataItem("IMAGE_DESCRIPTION") + == "S1A_IW_GRDH_1SDV_20171009T141532_20171009T141557_018737_01F9E2_E974_tnr" + ) + assert ds.GetMetadata("GEOLOCATION") == { + "LINE_OFFSET": "0", + "LINE_STEP": "16.025886864813039", + "PIXEL_OFFSET": "0", + "PIXEL_STEP": "16.02697616060226", + "SRS": 'GEOGCS["WGS 84",DATUM["WGS_1984",SPHEROID["WGS ' + '84",6378137,298.257223563,AUTHORITY["EPSG","7030"]],AUTHORITY["EPSG","6326"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AXIS["Latitude",NORTH],AXIS["Longitude",EAST],AUTHORITY["EPSG","4326"]]', + "X_BAND": "1", + "X_DATASET": 'SNAP_TIFF:"/vsizip/vsizip/data/snap_tiff/S1A_IW_GRDH_1SDV_20171009T141532_20171009T141557_018737_01F9E2_E974_tnr_empty_truncated.tif.zip.zip":GEOLOCATION', + "Y_BAND": "2", + "Y_DATASET": 'SNAP_TIFF:"/vsizip/vsizip/data/snap_tiff/S1A_IW_GRDH_1SDV_20171009T141532_20171009T141557_018737_01F9E2_E974_tnr_empty_truncated.tif.zip.zip":GEOLOCATION', + } + assert ds.GetMetadataItem("LINE_OFFSET", "GEOLOCATION") == "0" + assert ds.GetMetadata("SUBDATASETS") == { + "SUBDATASET_1_DESC": "Main content of " + "/vsizip/vsizip/data/snap_tiff/S1A_IW_GRDH_1SDV_20171009T141532_20171009T141557_018737_01F9E2_E974_tnr_empty_truncated.tif.zip.zip", + "SUBDATASET_1_NAME": 'SNAP_TIFF:"/vsizip/vsizip/data/snap_tiff/S1A_IW_GRDH_1SDV_20171009T141532_20171009T141557_018737_01F9E2_E974_tnr_empty_truncated.tif.zip.zip":MAIN', + "SUBDATASET_2_DESC": "Geolocation array of " + "/vsizip/vsizip/data/snap_tiff/S1A_IW_GRDH_1SDV_20171009T141532_20171009T141557_018737_01F9E2_E974_tnr_empty_truncated.tif.zip.zip", + "SUBDATASET_2_NAME": 'SNAP_TIFF:"/vsizip/vsizip/data/snap_tiff/S1A_IW_GRDH_1SDV_20171009T141532_20171009T141557_018737_01F9E2_E974_tnr_empty_truncated.tif.zip.zip":GEOLOCATION', + } + assert ds.GetMetadata("xml:DIMAP")[0].startswith(" + +#include "cpl_port.h" +#include "cpl_minixml.h" +#include "cpl_vsi_virtual.h" +#include "gdal_pam.h" +#include "rawdataset.h" + +#define LIBERTIFF_NS GDAL_libertiff +#include "../../third_party/libertiff/libertiff.hpp" + +#include +#include + +constexpr const char *SNAP_TIFF_PREFIX = "SNAP_TIFF:"; + +// Non-standard TIFF tag holding DIMAP XML for SNAP TIFF products +constexpr uint16_t DIMAP_TAG = 65000; + +// Number of values per GCP in the GeoTIFFTiePoints tag +constexpr int VALUES_PER_GCP = 6; + +constexpr int GCP_PIXEL = 0; +constexpr int GCP_LINE = 1; +// constexpr int GCP_DEPTH = 2; +constexpr int GCP_X = 3; +constexpr int GCP_Y = 4; +constexpr int GCP_Z = 5; + +/************************************************************************/ +/* SNAPTIFFDataset */ +/************************************************************************/ + +class SNAPTIFFDataset final : public GDALPamDataset +{ + public: + SNAPTIFFDataset() = default; + + static int Identify(GDALOpenInfo *poOpenInfo); + static GDALDataset *Open(GDALOpenInfo *poOpenInfo); + + char **GetMetadataDomainList() override; + char **GetMetadata(const char *pszDomain = "") override; + const char *GetMetadataItem(const char *pszName, + const char *pszDomain = "") override; + + const OGRSpatialReference *GetGCPSpatialRef() const override + { + return m_oSRS.IsEmpty() ? nullptr : &m_oSRS; + } + + int GetGCPCount() override + { + return int(m_asGCPs.size()); + } + + const GDAL_GCP *GetGCPs() override + { + return gdal::GCP::c_ptr(m_asGCPs); + } + + private: + VSIVirtualHandleUniquePtr m_poFile{}; + std::unique_ptr m_poImage{}; + + //! Whether this dataset is actually the geolocation array + bool m_bIsGeolocArray = false; + + //! Content of "xml:DIMAP" metadata domain + CPLStringList m_aosDIMAPMetadata{}; + + CPLStringList m_aosGEOLOCATION{}; + int m_nGeolocArrayWidth = 0; + int m_nGeolocArrayHeight = 0; + + CPLStringList m_aosSUBDATASETS{}; + + std::vector m_asGCPs{}; + OGRSpatialReference m_oSRS{}; + + bool GetGeolocationMetadata(); + + void ReadSRS(); +}; + +/************************************************************************/ +/* Identify() */ +/************************************************************************/ + +int SNAPTIFFDataset::Identify(GDALOpenInfo *poOpenInfo) +{ + if (STARTS_WITH(poOpenInfo->pszFilename, SNAP_TIFF_PREFIX)) + return true; + + if (poOpenInfo->fpL == nullptr || poOpenInfo->nHeaderBytes < 16 || + // BigEndian classic TIFF + memcmp(poOpenInfo->pabyHeader, "\x4D\x4D\x00\x2A", 4) != 0) + { + return false; + } + + struct MyFileReader final : public LIBERTIFF_NS::FileReader + { + const GDALOpenInfo *m_poOpenInfo; + + explicit MyFileReader(const GDALOpenInfo *poOpenInfoIn) + : m_poOpenInfo(poOpenInfoIn) + { + } + + uint64_t size() const override + { + return m_poOpenInfo->nHeaderBytes; + } + + size_t read(uint64_t offset, size_t count, void *buffer) const override + { + if (offset + count > + static_cast(m_poOpenInfo->nHeaderBytes)) + return 0; + memcpy(buffer, + m_poOpenInfo->pabyHeader + static_cast(offset), + count); + return count; + } + + CPL_DISALLOW_COPY_ASSIGN(MyFileReader) + }; + + auto f = std::make_shared(poOpenInfo); +#ifdef DEBUG + // Just to increase coverage testing + CPLAssert(f->size() == uint64_t(poOpenInfo->nHeaderBytes)); + char dummy; + CPLAssert(f->read(poOpenInfo->nHeaderBytes, 1, &dummy) == 0); +#endif + auto image = LIBERTIFF_NS::open(std::move(f)); + // Checks that it is a single-band Float32 uncompressed dataset, made + // of a single strip. + if (!image || image->nextImageOffset() != 0 || + image->compression() != LIBERTIFF_NS::Compression::None || + image->sampleFormat() != LIBERTIFF_NS::SampleFormat::IEEEFP || + image->samplesPerPixel() != 1 || image->bitsPerSample() != 32 || + image->isTiled() || image->strileCount() != 1 || image->width() == 0 || + image->width() > INT_MAX / sizeof(float) || image->height() == 0 || + image->height() > INT_MAX || image->rowsPerStrip() != image->height() || + !image->tag(LIBERTIFF_NS::TagCode::GeoTIFFPixelScale) || + !image->tag(LIBERTIFF_NS::TagCode::GeoTIFFTiePoints) || + !image->tag(LIBERTIFF_NS::TagCode::GeoTIFFGeoKeyDirectory) || + !image->tag(DIMAP_TAG)) + { + return false; + } + + return true; +} + +/************************************************************************/ +/* Open() */ +/************************************************************************/ + +GDALDataset *SNAPTIFFDataset::Open(GDALOpenInfo *poOpenInfo) +{ + if (poOpenInfo->eAccess == GA_Update || !Identify(poOpenInfo)) + return nullptr; + + bool bIsGeolocation = false; + // Check if it is SNAP_TIFF:"filename":{subdataset_component} syntax + if (STARTS_WITH(poOpenInfo->pszFilename, SNAP_TIFF_PREFIX)) + { + const CPLStringList aosTokens(CSLTokenizeString2( + poOpenInfo->pszFilename, ":", CSLT_HONOURSTRINGS)); + if (aosTokens.size() != 3) + return nullptr; + bIsGeolocation = EQUAL(aosTokens[2], "GEOLOCATION"); + if (!bIsGeolocation && !EQUAL(aosTokens[2], "MAIN")) + return nullptr; + GDALOpenInfo oOpenInfo(aosTokens[1], GA_ReadOnly); + if (!Identify(&oOpenInfo)) + return nullptr; + std::swap(poOpenInfo->fpL, oOpenInfo.fpL); + if (!poOpenInfo->fpL) + return nullptr; + } + + struct MyFileReader final : public LIBERTIFF_NS::FileReader + { + VSILFILE *m_fp; + mutable uint64_t m_nFileSize = 0; + + explicit MyFileReader(VSILFILE *fp) : m_fp(fp) + { + } + + uint64_t size() const override + { + if (m_nFileSize == 0) + { + m_fp->Seek(0, SEEK_END); + m_nFileSize = m_fp->Tell(); + } + return m_nFileSize; + } + + size_t read(uint64_t offset, size_t count, void *buffer) const override + { + return m_fp->Seek(offset, SEEK_SET) == 0 + ? m_fp->Read(buffer, 1, count) + : 0; + } + + CPL_DISALLOW_COPY_ASSIGN(MyFileReader) + }; + + auto f = std::make_shared(poOpenInfo->fpL); +#ifdef DEBUG + // Just to increase coverage testing + char dummy; + CPLAssert(f->read(f->size(), 1, &dummy) == 0); +#endif + + auto poDS = std::make_unique(); + + poDS->m_poImage = LIBERTIFF_NS::open(std::move(f)); + if (!poDS->m_poImage) + return nullptr; + poDS->m_poFile.reset(poOpenInfo->fpL); + poOpenInfo->fpL = nullptr; + + poDS->nRasterXSize = static_cast(poDS->m_poImage->width()); + poDS->nRasterYSize = static_cast(poDS->m_poImage->height()); + poDS->SetDescription(poOpenInfo->pszFilename); + + if (bIsGeolocation) + { + poDS->m_bIsGeolocArray = true; + if (!poDS->GetGeolocationMetadata()) + { + return nullptr; + } + poDS->nRasterXSize = poDS->m_nGeolocArrayWidth; + poDS->nRasterYSize = poDS->m_nGeolocArrayHeight; + + const auto *psTag = + poDS->m_poImage->tag(LIBERTIFF_NS::TagCode::GeoTIFFTiePoints); + + for (int iBand = 1; iBand <= 2; ++iBand) + { + auto poBand = std::make_unique( + poDS->m_poFile.get(), + psTag->value_offset + + (iBand == 1 ? GCP_X : GCP_Y) * sizeof(double), + int(sizeof(double)) * VALUES_PER_GCP, + int(sizeof(double)) * VALUES_PER_GCP * poDS->nRasterXSize, + GDT_Float64, !poDS->m_poImage->mustByteSwap(), + poDS->nRasterXSize, poDS->nRasterYSize, + RawRasterBand::OwnFP::NO); + if (!poBand->IsValid()) + return nullptr; + if (iBand == 1) + poBand->SetDescription("Longitude"); + else + poBand->SetDescription("Latitude"); + poDS->SetBand(iBand, std::move(poBand)); + } + + return poDS.release(); + } + + poDS->ReadSRS(); + poDS->GetGeolocationMetadata(); + + { + bool okStrileOffset = false; + auto poBand = std::make_unique( + poDS->m_poFile.get(), + poDS->m_poImage->strileOffset(0, okStrileOffset), + int(sizeof(float)), int(sizeof(float)) * poDS->nRasterXSize, + GDT_Float32, !poDS->m_poImage->mustByteSwap(), poDS->nRasterXSize, + poDS->nRasterYSize, RawRasterBand::OwnFP::NO); + if (!poBand->IsValid()) + return nullptr; + poDS->SetBand(1, std::move(poBand)); + } + auto poBand = poDS->papoBands[0]; + + const auto &psImageDescription = + poDS->m_poImage->tag(LIBERTIFF_NS::TagCode::ImageDescription); + if (psImageDescription && + psImageDescription->type == LIBERTIFF_NS::TagType::ASCII && + !psImageDescription->invalid_value_offset && + // Sanity check + psImageDescription->count < 100 * 1000) + { + bool ok = true; + const std::string s = + poDS->m_poImage->readTagAsString(*psImageDescription, ok); + if (ok) + { + poDS->GDALDataset::SetMetadataItem("IMAGE_DESCRIPTION", s.c_str()); + } + } + + const auto *psDimapTag = poDS->m_poImage->tag(DIMAP_TAG); + if (psDimapTag && psDimapTag->type == LIBERTIFF_NS::TagType::ASCII && + !psDimapTag->invalid_value_offset) + { + try + { + bool ok = true; + // Just read the first 10 kB max to fetch essential band metadata + const std::string s = poDS->m_poImage->readContext()->readString( + psDimapTag->value_offset, + static_cast( + std::min(psDimapTag->count, 10000)), + ok); + const auto posStart = s.find(""); + if (posStart != std::string::npos) + { + const char *markerEnd = ""; + const auto posEnd = s.find(markerEnd, posStart); + if (posEnd != std::string::npos) + { + const std::string osSubstr = s.substr( + posStart, posEnd - posStart + strlen(markerEnd)); + CPLXMLTreeCloser oRoot(CPLParseXMLString(osSubstr.c_str())); + if (oRoot) + { + const char *pszNoDataValueUsed = CPLGetXMLValue( + oRoot.get(), "NO_DATA_VALUE_USED", nullptr); + const char *pszNoDataValue = CPLGetXMLValue( + oRoot.get(), "NO_DATA_VALUE", nullptr); + if (pszNoDataValueUsed && pszNoDataValue && + CPLTestBool(pszNoDataValueUsed)) + { + poBand->SetNoDataValue(CPLAtof(pszNoDataValue)); + } + + if (const char *pszScalingFactor = CPLGetXMLValue( + oRoot.get(), "SCALING_FACTOR", nullptr)) + { + poBand->SetScale(CPLAtof(pszScalingFactor)); + } + + if (const char *pszScalingOffset = CPLGetXMLValue( + oRoot.get(), "SCALING_OFFSET", nullptr)) + { + poBand->SetOffset(CPLAtof(pszScalingOffset)); + } + + if (const char *pszBandName = CPLGetXMLValue( + oRoot.get(), "BAND_NAME", nullptr)) + { + poBand->SetDescription(pszBandName); + } + + if (const char *pszUnit = CPLGetXMLValue( + oRoot.get(), "PHYSICAL_UNIT", nullptr)) + { + poBand->SetUnitType(pszUnit); + } + } + } + } + } + catch (const std::exception &) + { + } + } + + // Initialize PAM + poDS->SetDescription(poOpenInfo->pszFilename); + poDS->TryLoadXML(); + + // Check for overviews. + poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename); + + return poDS.release(); +} + +/************************************************************************/ +/* GetMetadataDomainList() */ +/************************************************************************/ + +char **SNAPTIFFDataset::GetMetadataDomainList() +{ + return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(), + TRUE, "GEOLOCATION", "SUBDATASETS", + "xml:DIMAP", nullptr); +} + +/************************************************************************/ +/* GetGeolocationMetadata() */ +/************************************************************************/ + +// (Partially) read the content of the GeoTIFFTiePoints tag to check if the +// tie points form a regular geolocation array, and extract the width, height, +// and spacing of that geolocation array. Also fills the m_aosGEOLOCATION +// metadata domain +bool SNAPTIFFDataset::GetGeolocationMetadata() +{ + const auto *psTag = m_poImage->tag(LIBERTIFF_NS::TagCode::GeoTIFFTiePoints); + if (psTag && psTag->type == LIBERTIFF_NS::TagType::Double && + !psTag->invalid_value_offset && (psTag->count % VALUES_PER_GCP) == 0 && + // Sanity check + psTag->count <= uint64_t(nRasterXSize) * nRasterYSize * VALUES_PER_GCP) + { + const int numGCPs = int(psTag->count / VALUES_PER_GCP); + + // Assume that the geolocation array has the same proportion as the + // main array + const double dfGCPArrayWidth = + sqrt(double(nRasterXSize) * numGCPs / nRasterYSize); + const double dfGCPArrayHeight = + sqrt(double(nRasterYSize) * numGCPs / nRasterXSize); + if (dfGCPArrayWidth > INT_MAX || dfGCPArrayHeight > INT_MAX) + { + return false; + } + + const int nGCPArrayWidth = int(std::round(dfGCPArrayWidth)); + const int nGCPArrayHeight = int(std::round(dfGCPArrayHeight)); + constexpr int NUM_LINES = 3; + if (nGCPArrayWidth * nGCPArrayHeight != numGCPs || + nGCPArrayHeight < NUM_LINES) + { + return false; + } + + bool ok = true; + // Just read the first 3 lines of the geolocation array + const auto numValuesPerLine = nGCPArrayWidth * VALUES_PER_GCP; + const auto values = m_poImage->readContext()->readArray( + psTag->value_offset, numValuesPerLine * NUM_LINES, ok); + if (!ok || values.empty()) + return false; + + if (values[GCP_LINE] != 0.5 && values[GCP_PIXEL] != 0.5) + return false; + + constexpr double RELATIVE_TOLERANCE = 1e-5; + constexpr double PIXEL_TOLERANCE = 1e-3; + + // Check that pixel spacing is constant on all three first lines + const double pixelSpacing = + values[VALUES_PER_GCP + GCP_PIXEL] - values[GCP_PIXEL]; + if (!(pixelSpacing >= 1)) + return false; + if (std::fabs(pixelSpacing * (nGCPArrayWidth - 1) - + (nRasterXSize - 1)) > PIXEL_TOLERANCE) + return false; + + double adfY[NUM_LINES]; + for (int iLine = 0; iLine < NUM_LINES; ++iLine) + { + adfY[iLine] = values[iLine * numValuesPerLine + GCP_LINE]; + for (int i = iLine * numValuesPerLine + VALUES_PER_GCP; + i < (iLine + 1) * numValuesPerLine; i += VALUES_PER_GCP) + { + if (values[i + GCP_LINE] != + values[i - VALUES_PER_GCP + GCP_LINE]) + { + return false; + } + const double pixelSpacingNew = + values[i + GCP_PIXEL] - + values[i - VALUES_PER_GCP + GCP_PIXEL]; + if (std::fabs(pixelSpacingNew - pixelSpacing) > + RELATIVE_TOLERANCE * std::fabs(pixelSpacing)) + { + return false; + } + } + } + + // Check that line spacing is constant on the three first lines + const double lineSpacing = adfY[1] - adfY[0]; + if (!(lineSpacing >= 1)) + return false; + if (std::fabs(lineSpacing * (nGCPArrayHeight - 1) - + (nRasterYSize - 1)) > PIXEL_TOLERANCE) + return false; + + for (int iLine = 1; iLine + 1 < NUM_LINES; ++iLine) + { + const double lineSpacingNew = adfY[iLine + 1] - adfY[iLine]; + if (std::fabs(lineSpacingNew - lineSpacing) > + RELATIVE_TOLERANCE * std::fabs(lineSpacing)) + { + return false; + } + } + + // Read last line + const auto lastLineValue = m_poImage->readContext()->readArray( + psTag->value_offset + uint64_t(nGCPArrayHeight - 1) * + numValuesPerLine * sizeof(double), + numValuesPerLine, ok); + if (!ok) + return false; + + if (!m_bIsGeolocArray) + { + // Expose the 4 corner GCPs for rough georeferencing. + + const uint32_t nShift = numValuesPerLine - VALUES_PER_GCP; + m_asGCPs.emplace_back("TL", "Top Left", values[GCP_PIXEL], + values[GCP_LINE], values[GCP_X], + values[GCP_Y], values[GCP_Z]); + m_asGCPs.emplace_back( + "TR", "Top Right", values[nShift + GCP_PIXEL], + values[nShift + GCP_LINE], values[nShift + GCP_X], + values[nShift + GCP_Y], values[nShift + GCP_Z]); + m_asGCPs.emplace_back("BL", "Bottom Left", lastLineValue[GCP_PIXEL], + lastLineValue[GCP_LINE], lastLineValue[GCP_X], + lastLineValue[GCP_Y], lastLineValue[GCP_Z]); + m_asGCPs.emplace_back( + "BR", "Bottom Right", lastLineValue[nShift + GCP_PIXEL], + lastLineValue[nShift + GCP_LINE], lastLineValue[nShift + GCP_X], + lastLineValue[nShift + GCP_Y], lastLineValue[nShift + GCP_Z]); + } + + m_nGeolocArrayWidth = nGCPArrayWidth; + m_nGeolocArrayHeight = nGCPArrayHeight; + + if (!m_bIsGeolocArray) + { + if (!m_oSRS.IsEmpty()) + { + char *pszWKT = nullptr; + m_oSRS.exportToWkt(&pszWKT); + m_aosGEOLOCATION.SetNameValue("SRS", pszWKT); + CPLFree(pszWKT); + } + + m_aosGEOLOCATION.SetNameValue( + "X_DATASET", CPLSPrintf("%s\"%s\":GEOLOCATION", + SNAP_TIFF_PREFIX, GetDescription())); + m_aosGEOLOCATION.SetNameValue("X_BAND", "1"); + m_aosGEOLOCATION.SetNameValue( + "Y_DATASET", CPLSPrintf("%s\"%s\":GEOLOCATION", + SNAP_TIFF_PREFIX, GetDescription())); + m_aosGEOLOCATION.SetNameValue("Y_BAND", "2"); + m_aosGEOLOCATION.SetNameValue("PIXEL_OFFSET", "0"); + m_aosGEOLOCATION.SetNameValue("PIXEL_STEP", + CPLSPrintf("%.17g", pixelSpacing)); + m_aosGEOLOCATION.SetNameValue("LINE_OFFSET", "0"); + m_aosGEOLOCATION.SetNameValue("LINE_STEP", + CPLSPrintf("%.17g", lineSpacing)); + } + + return true; + } + return false; +} + +/************************************************************************/ +/* ReadSRS() */ +/************************************************************************/ + +// Simplified GeoTIFF SRS reader, assuming the SRS is encoded as a EPSG code +void SNAPTIFFDataset::ReadSRS() +{ + const auto &psGeoKeysTag = + m_poImage->tag(LIBERTIFF_NS::TagCode::GeoTIFFGeoKeyDirectory); + constexpr int VALUES_PER_GEOKEY = 4; + if (psGeoKeysTag && psGeoKeysTag->type == LIBERTIFF_NS::TagType::Short && + !psGeoKeysTag->invalid_value_offset && + psGeoKeysTag->count >= VALUES_PER_GEOKEY && + (psGeoKeysTag->count % VALUES_PER_GEOKEY) == 0 && + // Sanity check + psGeoKeysTag->count < 1000) + { + bool ok = true; + const auto values = + m_poImage->readTagAsVector(*psGeoKeysTag, ok); + if (values.size() >= 4) + { + const uint16_t geokeysCount = values[3]; + constexpr uint16_t GEOTIFF_KEY_DIRECTORY_VERSION_V1 = 1; + constexpr uint16_t GEOTIFF_KEY_VERSION_MAJOR_V1 = 1; + if (values[0] == GEOTIFF_KEY_DIRECTORY_VERSION_V1 && + // GeoTIFF 1.x + values[1] == GEOTIFF_KEY_VERSION_MAJOR_V1 && + geokeysCount == psGeoKeysTag->count / VALUES_PER_GEOKEY - 1) + { + constexpr uint16_t GeoTIFFTypeShort = 0; + constexpr uint16_t GeodeticCRSGeoKey = 2048; + constexpr uint16_t ProjectedCRSGeoKey = 3072; + uint16_t nEPSGCode = 0; + for (uint32_t i = 1; i <= geokeysCount; ++i) + { + const auto geokey = values[VALUES_PER_GEOKEY * i]; + const auto geokeyType = values[VALUES_PER_GEOKEY * i + 1]; + const auto geokeyCount = values[VALUES_PER_GEOKEY * i + 2]; + const auto geokeyValue = values[VALUES_PER_GEOKEY * i + 3]; + if ((geokey == GeodeticCRSGeoKey || + geokey == ProjectedCRSGeoKey) && + geokeyType == GeoTIFFTypeShort && geokeyCount == 1 && + geokeyValue > 0) + { + nEPSGCode = geokeyValue; + if (geokey == ProjectedCRSGeoKey) + break; + } + } + if (nEPSGCode > 0) + { + m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); + m_oSRS.importFromEPSG(nEPSGCode); + } + } + } + } +} + +/************************************************************************/ +/* GetMetadata() */ +/************************************************************************/ + +char **SNAPTIFFDataset::GetMetadata(const char *pszDomain) +{ + if (!m_bIsGeolocArray) + { + if (pszDomain && EQUAL(pszDomain, "xml:DIMAP")) + { + if (m_aosDIMAPMetadata.empty()) + { + const auto *psDimapTag = m_poImage->tag(DIMAP_TAG); + if (psDimapTag && + psDimapTag->type == LIBERTIFF_NS::TagType::ASCII && + !psDimapTag->invalid_value_offset && + // Sanity check + psDimapTag->count < 100 * 1000 * 1000) + { + try + { + bool ok = true; + const std::string s = + m_poImage->readTagAsString(*psDimapTag, ok); + if (ok) + { + m_aosDIMAPMetadata.AddString(s.c_str()); + } + } + catch (const std::exception &) + { + CPLError(CE_Failure, CPLE_OutOfMemory, + "Out of memory in GetMetadata()"); + } + } + } + return m_aosDIMAPMetadata.List(); + } + else if (pszDomain && EQUAL(pszDomain, "GEOLOCATION")) + { + return m_aosGEOLOCATION.List(); + } + else if (pszDomain && EQUAL(pszDomain, "SUBDATASETS")) + { + if (m_aosSUBDATASETS.empty() && GetGeolocationMetadata()) + { + m_aosSUBDATASETS.SetNameValue("SUBDATASET_1_NAME", + CPLSPrintf("%s\"%s\":MAIN", + SNAP_TIFF_PREFIX, + GetDescription())); + m_aosSUBDATASETS.SetNameValue("SUBDATASET_1_DESC", + std::string("Main content of ") + .append(GetDescription()) + .c_str()); + + m_aosSUBDATASETS.SetNameValue( + "SUBDATASET_2_NAME", + m_aosGEOLOCATION.FetchNameValue("X_DATASET")); + m_aosSUBDATASETS.SetNameValue( + "SUBDATASET_2_DESC", std::string("Geolocation array of ") + .append(GetDescription()) + .c_str()); + } + return m_aosSUBDATASETS.List(); + } + } + + return GDALPamDataset::GetMetadata(pszDomain); +} + +/************************************************************************/ +/* GetMetadataItem() */ +/************************************************************************/ + +const char *SNAPTIFFDataset::GetMetadataItem(const char *pszName, + const char *pszDomain) +{ + if (!m_bIsGeolocArray && pszDomain && + (EQUAL(pszDomain, "GEOLOCATION") || EQUAL(pszDomain, "SUBDATASETS"))) + { + return CSLFetchNameValue(GetMetadata(pszDomain), pszName); + } + return GDALPamDataset::GetMetadataItem(pszName, pszDomain); +} + +/************************************************************************/ +/* GDALRegister_SNAP_TIFF() */ +/************************************************************************/ + +void GDALRegister_SNAP_TIFF() +{ + if (GDALGetDriverByName("SNAP_TIFF") != nullptr) + return; + + GDALDriver *poDriver = new GDALDriver(); + poDriver->SetDescription("SNAP_TIFF"); + poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES"); + poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, + "Sentinel Application Processing GeoTIFF"); + poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, + "drivers/raster/snap_tiff.html"); + poDriver->SetMetadataItem(GDAL_DMD_MIMETYPE, "image/tiff"); + poDriver->SetMetadataItem(GDAL_DMD_EXTENSIONS, "tif tiff"); + poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); + + poDriver->pfnOpen = SNAPTIFFDataset::Open; + poDriver->pfnIdentify = SNAPTIFFDataset::Identify; + + GetGDALDriverManager()->RegisterDriver(poDriver); +} diff --git a/gcore/gdal_frmts.h b/gcore/gdal_frmts.h index 2bf67d7527f9..490933cd5b52 100644 --- a/gcore/gdal_frmts.h +++ b/gcore/gdal_frmts.h @@ -246,6 +246,7 @@ void CPL_DLL GDALRegister_BASISU_KTX2(void); void DeclareDeferredBASISU_KTX2Plugin(void); void CPL_DLL GDALRegister_NOAA_B(void); void CPL_DLL GDALRegister_NSIDCbin(void); +void CPL_DLL GDALRegister_SNAP_TIFF(void); CPL_C_END #endif /* ndef GDAL_FRMTS_H_INCLUDED */ diff --git a/third_party/libertiff/libertiff.hpp b/third_party/libertiff/libertiff.hpp new file mode 100644 index 000000000000..7ea0191ffbd6 --- /dev/null +++ b/third_party/libertiff/libertiff.hpp @@ -0,0 +1,1599 @@ +// SPDX-License-Identifier: MIT +// Copyright 2024, Even Rouault + +// Canonical URL: https://github.com/libertiff/libertiff/blob/master/libertiff.hpp + +#ifndef LIBERTIFF_HPP_INCLUDED +#define LIBERTIFF_HPP_INCLUDED + +////////////////////////////////////////////////////////////// +// libertiff = libre TIFF or LIB E(ven) R(ouault) TIFF... ? // +////////////////////////////////////////////////////////////// + +#if __cplusplus >= 202002L +#include // std::endian +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef LIBERTIFF_NS +#define LIBERTIFF_NS libertiff +#endif + +/** Libertiff is a C++11 simple, header-only, TIFF reader. It is MIT licensed. + * + * Handles both ClassicTIFF and BigTIFF, little-endian or big-endian ordered. + * + * The library does not (yet?) offer codec facilities. It is mostly aimed at + * browsing through the linked chain of Image File Directory (IFD) and their tags. + * + * "Offline" tag values are not loaded at IFD opening time, but only upon + * request, which helps handling files with tags with an arbitrarily large + * number of values. + * + * The library is thread-safe (that is the instances that it returns can + * be used from multiple threads), if passed FileReader instances are themselves + * thread-safe. + * + * The library does not throw exceptions (but underlying std library might + * throw exceptions in case of out-of-memory situations) + * + * Optional features: + * - define LIBERTIFF_C_FILE_READER before including libertiff.hpp, so that + * the libertiff::CFileReader class is available + */ +namespace LIBERTIFF_NS +{ + +#if __cplusplus >= 201703L +#define LIBERTIFF_STATIC_ASSERT(x) static_assert(x) +#define LIBERTIFF_CONSTEXPR constexpr +#else +#define LIBERTIFF_STATIC_ASSERT(x) static_assert((x), #x) +#define LIBERTIFF_CONSTEXPR +#endif + +template +std::unique_ptr make_unique(Args &&...args) +{ + return std::unique_ptr(new T(std::forward(args)...)); +} + +/** Returns whether the host is little-endian ordered */ +inline bool isHostLittleEndian() +{ +#if __cplusplus >= 202002L + return std::endian::native == std::endian::little; +#elif (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && \ + (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) || \ + defined(_MSC_VER) + return true; +#elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \ + (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) + return false; +#else + uint32_t one = 1; + char one_as_char_array[sizeof(uint32_t)]; + std::memcpy(one_as_char_array, &one, sizeof(uint32_t)); + return one_as_char_array[0] == 1; +#endif +} + +/** Byte-swap */ +template inline T byteSwap(T v); + +/** Byte-swap a uint8_t */ +template <> uint8_t byteSwap(uint8_t v) +{ + return v; +} + +/** Byte-swap a int8_t */ +template <> int8_t byteSwap(int8_t v) +{ + return v; +} + +/** Byte-swap a uint16_t */ +template <> uint16_t byteSwap(uint16_t v) +{ + return uint16_t((v >> 8) | ((v & 0xff) << 8)); +} + +/** Byte-swap a int16_t */ +template <> int16_t byteSwap(int16_t v) +{ + uint16_t u; + LIBERTIFF_STATIC_ASSERT(sizeof(v) == sizeof(u)); + std::memcpy(&u, &v, sizeof(u)); + u = byteSwap(u); + std::memcpy(&v, &u, sizeof(u)); + return v; +} + +/** Byte-swap a uint32_t */ +template <> uint32_t byteSwap(uint32_t v) +{ + return (v >> 24) | (((v >> 16) & 0xff) << 8) | (((v >> 8) & 0xff) << 16) | + ((v & 0xff) << 24); +} + +/** Byte-swap a int32_t */ +template <> int32_t byteSwap(int32_t v) +{ + uint32_t u; + LIBERTIFF_STATIC_ASSERT(sizeof(v) == sizeof(u)); + std::memcpy(&u, &v, sizeof(u)); + u = byteSwap(u); + std::memcpy(&v, &u, sizeof(u)); + return v; +} + +/** Byte-swap a uint64_t */ +template <> uint64_t byteSwap(uint64_t v) +{ + return (uint64_t(byteSwap(uint32_t(v & ~(0U)))) << 32) | + byteSwap(uint32_t(v >> 32)); +} + +/** Byte-swap a int64_t */ +template <> int64_t byteSwap(int64_t v) +{ + uint64_t u; + std::memcpy(&u, &v, sizeof(u)); + u = byteSwap(u); + std::memcpy(&v, &u, sizeof(u)); + return v; +} + +/** Byte-swap a float */ +template <> float byteSwap(float v) +{ + uint32_t u; + LIBERTIFF_STATIC_ASSERT(sizeof(v) == sizeof(u)); + std::memcpy(&u, &v, sizeof(u)); + u = byteSwap(u); + std::memcpy(&v, &u, sizeof(u)); + return v; +} + +/** Byte-swap a double */ +template <> double byteSwap(double v) +{ + uint64_t u; + LIBERTIFF_STATIC_ASSERT(sizeof(v) == sizeof(u)); + std::memcpy(&u, &v, sizeof(u)); + u = byteSwap(u); + std::memcpy(&v, &u, sizeof(u)); + return v; +} +} // namespace LIBERTIFF_NS + +namespace LIBERTIFF_NS +{ +/** Interface to read from a file. */ +class FileReader +{ + public: + virtual ~FileReader() = default; + + /** Return file size in bytes */ + virtual uint64_t size() const = 0; + + /** Read 'count' bytes from offset 'offset' into 'buffer' and + * return the number of bytes actually read. + */ + virtual size_t read(uint64_t offset, size_t count, void *buffer) const = 0; +}; +} // namespace LIBERTIFF_NS + +namespace LIBERTIFF_NS +{ +/** Read context: associates a file, and the byte ordering of the TIFF file */ +class ReadContext +{ + public: + /** Constructor */ + ReadContext(const std::shared_ptr &file, + bool mustByteSwap) + : m_file(file), m_mustByteSwap(mustByteSwap) + { + } + + /** Return if values of more than 1-byte must be byte swapped. + * To be only taken into account when reading pixels. Tag values are + * automatically byte-swapped */ + inline bool mustByteSwap() const + { + return m_mustByteSwap; + } + + /** Return file size */ + inline uint64_t size() const + { + return m_file->size(); + } + + /** Read count raw bytes at offset into buffer */ + void read(uint64_t offset, size_t count, void *buffer, bool &ok) const + { + if (m_file->read(offset, count, buffer) != count) + ok = false; + } + + /** Read single value at offset */ + template T read(uint64_t offset, bool &ok) const + { +#if __cplusplus >= 201703L + static_assert( + std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v); +#endif + + T res = 0; + if (m_file->read(offset, sizeof(res), &res) != sizeof(res)) + { + ok = false; + return 0; + } + if LIBERTIFF_CONSTEXPR (sizeof(T) > 1) + { + if (m_mustByteSwap) + res = byteSwap(res); + } + return res; + } + + /** Read a unsigned rational (type == Type::Rational) */ + template + double readRational(uint64_t offset, bool &ok) const + { + const auto numerator = read(offset, ok); + const auto denominator = read(offset + sizeof(T), ok); + if (denominator == 0) + { + ok = false; + return std::numeric_limits::quiet_NaN(); + } + return double(numerator) / denominator; + } + + /** Read a signed rational (type == Type::SRational) */ + double readSignedRational(uint64_t offset, bool &ok) const + { + return readRational(offset, ok); + } + + /** Read length bytes at offset (typically for ASCII tag) as a string */ + std::string readString(std::string &res, uint64_t offset, size_t length, + bool &ok) const + { + res.resize(length); + if (length > 0 && m_file->read(offset, length, &res[0]) != length) + { + ok = false; + res.clear(); + return res; + } + // Strip trailing nul byte if found + if (length > 0 && res.back() == 0) + res.pop_back(); + return res; + } + + /** Read length bytes at offset (typically for ASCII tag) as a string */ + std::string readString(uint64_t offset, size_t length, bool &ok) const + { + std::string res; + readString(res, offset, length, ok); + return res; + } + + /** Read an array of count values starting at offset */ + template + void readArray(std::vector &array, uint64_t offset, size_t count, + bool &ok) const + { +#if __cplusplus >= 201703L + static_assert( + std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v); +#endif + + array.resize(count); + const size_t countBytes = count * sizeof(T); + if (count > 0 && + m_file->read(offset, countBytes, &array[0]) != countBytes) + { + ok = false; + array.clear(); + } + else if LIBERTIFF_CONSTEXPR (sizeof(T) > 1) + { + if (m_mustByteSwap) + { + if LIBERTIFF_CONSTEXPR (std::is_same::value) + { + uint32_t *uint32Array = + reinterpret_cast(array.data()); + for (size_t i = 0; i < count; ++i) + { + uint32Array[i] = byteSwap(uint32Array[i]); + } + } + else if LIBERTIFF_CONSTEXPR (std::is_same::value) + { + uint64_t *uint64Array = + reinterpret_cast(array.data()); + for (size_t i = 0; i < count; ++i) + { + uint64Array[i] = byteSwap(uint64Array[i]); + } + } + else + { + for (size_t i = 0; i < count; ++i) + { + array[i] = byteSwap(array[i]); + } + } + } + } + } + + /** Read an array of count values starting at offset */ + template + std::vector readArray(uint64_t offset, size_t count, bool &ok) const + { + std::vector array; + readArray(array, offset, count, ok); + return array; + } + + private: + const std::shared_ptr m_file; + const bool m_mustByteSwap; +}; +} // namespace LIBERTIFF_NS + +namespace LIBERTIFF_NS +{ +/** Type of a TIFF tag code */ +typedef uint16_t TagCodeType; + +/** TIFF tag codes */ +namespace TagCode +{ +constexpr TagCodeType NewSubfileType = 254; +constexpr TagCodeType SubfileType = 255; + +// Base line and extended TIFF tags +constexpr TagCodeType ImageWidth = 256; +constexpr TagCodeType ImageLength = 257; +constexpr TagCodeType BitsPerSample = 258; +constexpr TagCodeType Compression = 259; +constexpr TagCodeType PhotometricInterpretation = 262; +constexpr TagCodeType ImageDescription = 270; +constexpr TagCodeType StripOffsets = 273; +constexpr TagCodeType SamplesPerPixel = 277; +constexpr TagCodeType RowsPerStrip = 278; +constexpr TagCodeType StripByteCounts = 279; +constexpr TagCodeType PlanarConfiguration = 284; +constexpr TagCodeType Predictor = 317; +constexpr TagCodeType TileWidth = 322; +constexpr TagCodeType TileLength = 323; +constexpr TagCodeType TileOffsets = 324; +constexpr TagCodeType TileByteCounts = 325; +constexpr TagCodeType SampleFormat = 339; + +// GeoTIFF tags +constexpr TagCodeType GeoTIFFPixelScale = 33550; +constexpr TagCodeType GeoTIFFTiePoints = 33922; +constexpr TagCodeType GeoTIFFGeoKeyDirectory = 34735; +constexpr TagCodeType GeoTIFFAsciiParams = 34737; +} // namespace TagCode + +#define LIBERTIFF_CASE_TAGCODE_STR(x) \ + case TagCode::x: \ + return #x + +inline const char *tagCodeName(TagCodeType tagCode) +{ + switch (tagCode) + { + LIBERTIFF_CASE_TAGCODE_STR(NewSubfileType); + LIBERTIFF_CASE_TAGCODE_STR(SubfileType); + LIBERTIFF_CASE_TAGCODE_STR(ImageWidth); + LIBERTIFF_CASE_TAGCODE_STR(ImageLength); + LIBERTIFF_CASE_TAGCODE_STR(BitsPerSample); + LIBERTIFF_CASE_TAGCODE_STR(Compression); + LIBERTIFF_CASE_TAGCODE_STR(PhotometricInterpretation); + LIBERTIFF_CASE_TAGCODE_STR(ImageDescription); + LIBERTIFF_CASE_TAGCODE_STR(StripOffsets); + LIBERTIFF_CASE_TAGCODE_STR(SamplesPerPixel); + LIBERTIFF_CASE_TAGCODE_STR(RowsPerStrip); + LIBERTIFF_CASE_TAGCODE_STR(StripByteCounts); + LIBERTIFF_CASE_TAGCODE_STR(PlanarConfiguration); + LIBERTIFF_CASE_TAGCODE_STR(Predictor); + LIBERTIFF_CASE_TAGCODE_STR(TileWidth); + LIBERTIFF_CASE_TAGCODE_STR(TileLength); + LIBERTIFF_CASE_TAGCODE_STR(TileOffsets); + LIBERTIFF_CASE_TAGCODE_STR(TileByteCounts); + LIBERTIFF_CASE_TAGCODE_STR(SampleFormat); + LIBERTIFF_CASE_TAGCODE_STR(GeoTIFFPixelScale); + LIBERTIFF_CASE_TAGCODE_STR(GeoTIFFTiePoints); + LIBERTIFF_CASE_TAGCODE_STR(GeoTIFFGeoKeyDirectory); + LIBERTIFF_CASE_TAGCODE_STR(GeoTIFFAsciiParams); + default: + break; + } + return "(unknown)"; +} + +#undef LIBERTIFF_CASE_TAGCODE_STR + +/** Type of a TIFF tag type */ +typedef uint16_t TagTypeType; + +/** TIFF tag data types */ +namespace TagType +{ +constexpr TagTypeType Byte = 1; /*! Unsigned 8-bit integer */ +constexpr TagTypeType ASCII = 2; /*! Character */ +constexpr TagTypeType Short = 3; /*! Unsigned 16-bit integer */ +constexpr TagTypeType Long = 4; /*! Unsigned 32-bit integer */ +constexpr TagTypeType Rational = + 5; /*! Positive number as a ratio of two unsigned 32-bit integers */ +constexpr TagTypeType SByte = 6; /*! Signed 8-bit integer */ +constexpr TagTypeType Undefined = 7; /*! Untyped 8-bit data */ +constexpr TagTypeType SShort = 8; /*! Signed 16-bit integer */ +constexpr TagTypeType SLong = 9; /*! Signed 32-bit integer */ +constexpr TagTypeType SRational = + 10; /*! Signed number as a ratio of two signed 32-bit integers */ +constexpr TagTypeType Float = 11; /*! 32-bit IEEE-754 floating point number */ +constexpr TagTypeType Double = 12; /*! 64-bit IEEE-754 floating point number */ + +// BigTIFF additions +constexpr TagTypeType Long8 = 16; /*! Unsigned 64-bit integer */ +constexpr TagTypeType SLong8 = 17; /*! Signed 64-bit integer */ +constexpr TagTypeType IFD8 = 18; /*! Unsigned 64-bit IFD offset */ +} // namespace TagType + +#define LIBERTIFF_CASE_TAGTYPE_STR(x) \ + case TagType::x: \ + return #x + +inline const char *tagTypeName(TagTypeType tagType) +{ + switch (tagType) + { + LIBERTIFF_CASE_TAGTYPE_STR(Byte); + LIBERTIFF_CASE_TAGTYPE_STR(ASCII); + LIBERTIFF_CASE_TAGTYPE_STR(Short); + LIBERTIFF_CASE_TAGTYPE_STR(Long); + LIBERTIFF_CASE_TAGTYPE_STR(Rational); + LIBERTIFF_CASE_TAGTYPE_STR(SByte); + LIBERTIFF_CASE_TAGTYPE_STR(Undefined); + LIBERTIFF_CASE_TAGTYPE_STR(SShort); + LIBERTIFF_CASE_TAGTYPE_STR(SLong); + LIBERTIFF_CASE_TAGTYPE_STR(SRational); + LIBERTIFF_CASE_TAGTYPE_STR(Float); + LIBERTIFF_CASE_TAGTYPE_STR(Double); + LIBERTIFF_CASE_TAGTYPE_STR(Long8); + LIBERTIFF_CASE_TAGTYPE_STR(SLong8); + LIBERTIFF_CASE_TAGTYPE_STR(IFD8); + default: + break; + } + return "(unknown)"; +} + +#undef LIBERTIFF_CASE_TAGTYPE_STR + +/** Type of a PlanarConfiguration value */ +typedef uint32_t PlanarConfigurationType; + +/** Values of the PlanarConfiguration tag */ +namespace PlanarConfiguration +{ +constexpr PlanarConfigurationType Contiguous = 1; /*! Single image plane */ +constexpr PlanarConfigurationType Separate = + 2; /*! Separate planes per sample */ +} // namespace PlanarConfiguration + +#define LIBERTIFF_CASE_PLANAR_CONFIG_STR(x) \ + case PlanarConfiguration::x: \ + return #x + +inline const char * +planarConfigurationName(PlanarConfigurationType planarConfiguration) +{ + switch (planarConfiguration) + { + LIBERTIFF_CASE_PLANAR_CONFIG_STR(Contiguous); + LIBERTIFF_CASE_PLANAR_CONFIG_STR(Separate); + default: + break; + } + return "(unknown)"; +} + +#undef LIBERTIFF_CASE_PLANAR_CONFIG_STR + +/** Type of a PlanarConfiguration value */ +typedef uint32_t PhotometricInterpretationType; + +/** Values of the PhotometricInterpretation tag */ +namespace PhotometricInterpretation +{ +constexpr PhotometricInterpretationType MinIsWhite = 0; +constexpr PhotometricInterpretationType MinIsBlack = 1; +constexpr PhotometricInterpretationType RGB = 2; +constexpr PhotometricInterpretationType Palette = 3; +constexpr PhotometricInterpretationType Mask = 4; +constexpr PhotometricInterpretationType Separated = 5; +constexpr PhotometricInterpretationType YCbCr = 6; +constexpr PhotometricInterpretationType CIELab = 8; +constexpr PhotometricInterpretationType ICCLab = 9; +constexpr PhotometricInterpretationType ITULab = 10; +} // namespace PhotometricInterpretation + +#define LIBERTIFF_CASE_PHOTOMETRIC_STR(x) \ + case PhotometricInterpretation::x: \ + return #x + +const char *photometricInterpretationName( + PhotometricInterpretationType photometricInterpretation); + +const char *photometricInterpretationName( + PhotometricInterpretationType photometricInterpretation) +{ + switch (photometricInterpretation) + { + LIBERTIFF_CASE_PHOTOMETRIC_STR(MinIsWhite); + LIBERTIFF_CASE_PHOTOMETRIC_STR(MinIsBlack); + LIBERTIFF_CASE_PHOTOMETRIC_STR(RGB); + LIBERTIFF_CASE_PHOTOMETRIC_STR(Palette); + LIBERTIFF_CASE_PHOTOMETRIC_STR(Mask); + LIBERTIFF_CASE_PHOTOMETRIC_STR(Separated); + LIBERTIFF_CASE_PHOTOMETRIC_STR(YCbCr); + LIBERTIFF_CASE_PHOTOMETRIC_STR(CIELab); + LIBERTIFF_CASE_PHOTOMETRIC_STR(ICCLab); + LIBERTIFF_CASE_PHOTOMETRIC_STR(ITULab); + default: + break; + } + return "(unknown)"; +} + +#undef LIBERTIFF_CASE_PHOTOMETRIC_STR + +/** Type of a Compression value */ +typedef uint32_t CompressionType; + +/** Compression methods */ +namespace Compression +{ +constexpr CompressionType None = 1; +constexpr CompressionType CCITT_RLE = 2; +constexpr CompressionType CCITT_FAX3 = 3; +constexpr CompressionType CCITT_FAX4 = 4; +constexpr CompressionType LZW = 5; +constexpr CompressionType OldJPEG = 6; +constexpr CompressionType JPEG = 7; +constexpr CompressionType Deflate = 8; +} // namespace Compression + +#define LIBERTIFF_CASE_COMPRESSION_STR(x) \ + case Compression::x: \ + return #x + +inline const char *compressionName(CompressionType compression) +{ + switch (compression) + { + LIBERTIFF_CASE_COMPRESSION_STR(None); + LIBERTIFF_CASE_COMPRESSION_STR(CCITT_RLE); + LIBERTIFF_CASE_COMPRESSION_STR(CCITT_FAX3); + LIBERTIFF_CASE_COMPRESSION_STR(CCITT_FAX4); + LIBERTIFF_CASE_COMPRESSION_STR(LZW); + LIBERTIFF_CASE_COMPRESSION_STR(OldJPEG); + LIBERTIFF_CASE_COMPRESSION_STR(JPEG); + LIBERTIFF_CASE_COMPRESSION_STR(Deflate); + default: + break; + } + return "(unknown)"; +} + +#undef LIBERTIFF_CASE_COMPRESSION_STR + +/** Type of a SampleFormat value */ +typedef uint32_t SampleFormatType; + +/** Sample format */ +namespace SampleFormat +{ +constexpr SampleFormatType UnsignedInt = 1; +constexpr SampleFormatType SignedInt = 2; +constexpr SampleFormatType IEEEFP = 3; +constexpr SampleFormatType Void = 4; +constexpr SampleFormatType ComplexInt = 5; +constexpr SampleFormatType ComplexIEEEFP = 6; +} // namespace SampleFormat + +#define LIBERTIFF_CASE_SAMPLE_FORMAT_STR(x) \ + case SampleFormat::x: \ + return #x + +inline const char *sampleFormatName(SampleFormatType sampleFormat) +{ + switch (sampleFormat) + { + LIBERTIFF_CASE_SAMPLE_FORMAT_STR(UnsignedInt); + LIBERTIFF_CASE_SAMPLE_FORMAT_STR(SignedInt); + LIBERTIFF_CASE_SAMPLE_FORMAT_STR(IEEEFP); + LIBERTIFF_CASE_SAMPLE_FORMAT_STR(Void); + LIBERTIFF_CASE_SAMPLE_FORMAT_STR(ComplexInt); + LIBERTIFF_CASE_SAMPLE_FORMAT_STR(ComplexIEEEFP); + default: + break; + } + return "(unknown)"; +} + +#undef LIBERTIFF_CASE_SAMPLE_FORMAT_STR + +/** Content of a tag entry in a Image File Directory (IFD) */ +struct TagEntry +{ + TagCodeType tag = 0; + TagTypeType type = 0; + uint64_t count = 0; // number of values in the tag + + // Inline values. Only valid if value_offset == 0. + // The actual number in the arrays is count + union + { + std::array charValues; + std::array uint8Values; + std::array int8Values; + std::array uint16Values; + std::array int16Values; + std::array uint32Values; + std::array int32Values; + std::array float32Values; + std::array + float64Values; // Valid for Double, Rational, SRational + std::array uint64Values = {0}; + std::array int64Values; + }; + + uint64_t value_offset = 0; // 0 for inline values + bool invalid_value_offset = true; // whether value_offset is invalid +}; + +// clang-format off + +/** Return the size in bytes of a tag data type, or 0 if unknown */ +inline uint32_t tagTypeSize(TagTypeType type) +{ + switch (type) + { + case TagType::Byte: return 1; + case TagType::ASCII: return 1; + case TagType::Short: return 2; + case TagType::Long: return 4; + case TagType::Rational: return 8; // 2 Long + case TagType::SByte: return 1; + case TagType::Undefined: return 1; + case TagType::SShort: return 2; + case TagType::SLong: return 4; + case TagType::SRational: return 8; // 2 SLong + case TagType::Float: return 4; + case TagType::Double: return 8; + case TagType::Long8: return 8; + case TagType::SLong8: return 8; + case TagType::IFD8: return 8; + default: break; + } + return 0; +} + +// clang-format on + +namespace detail +{ +template +inline std::vector readTagAsVectorInternal(const ReadContext &rc, + const TagEntry &tag, + TagTypeType expectedType, + const T *inlineValues, bool &ok) +{ + if (tag.type == expectedType) + { + if (tag.value_offset) + { + if (!tag.invalid_value_offset) + { + if LIBERTIFF_CONSTEXPR (sizeof(tag.count) > sizeof(size_t)) + { + if (tag.count > std::numeric_limits::max()) + { + ok = false; + return {}; + } + } + return rc.readArray(tag.value_offset, + static_cast(tag.count), ok); + } + } + else + { + return std::vector( + inlineValues, inlineValues + static_cast(tag.count)); + } + } + ok = false; + return {}; +} + +template +std::vector readTagAsVector(const ReadContext &rc, const TagEntry &tag, + bool &ok); + +template <> +std::vector readTagAsVector(const ReadContext &rc, const TagEntry &tag, + bool &ok) +{ + return readTagAsVectorInternal(rc, tag, TagType::SByte, + tag.int8Values.data(), ok); +} + +template <> +std::vector readTagAsVector(const ReadContext &rc, const TagEntry &tag, + bool &ok) +{ + return readTagAsVectorInternal( + rc, tag, tag.type == TagType::Undefined ? tag.type : TagType::Byte, + tag.uint8Values.data(), ok); +} + +template <> +std::vector readTagAsVector(const ReadContext &rc, const TagEntry &tag, + bool &ok) +{ + return readTagAsVectorInternal(rc, tag, TagType::SShort, + tag.int16Values.data(), ok); +} + +template <> +std::vector readTagAsVector(const ReadContext &rc, + const TagEntry &tag, bool &ok) +{ + return readTagAsVectorInternal(rc, tag, TagType::Short, + tag.uint16Values.data(), ok); +} + +template <> +std::vector readTagAsVector(const ReadContext &rc, const TagEntry &tag, + bool &ok) +{ + return readTagAsVectorInternal(rc, tag, TagType::SLong, + tag.int32Values.data(), ok); +} + +template <> +std::vector readTagAsVector(const ReadContext &rc, + const TagEntry &tag, bool &ok) +{ + return readTagAsVectorInternal(rc, tag, TagType::Long, + tag.uint32Values.data(), ok); +} + +template <> +std::vector readTagAsVector(const ReadContext &rc, const TagEntry &tag, + bool &ok) +{ + return readTagAsVectorInternal(rc, tag, TagType::SLong8, + tag.int64Values.data(), ok); +} + +template <> +std::vector readTagAsVector(const ReadContext &rc, + const TagEntry &tag, bool &ok) +{ + return readTagAsVectorInternal(rc, tag, TagType::Long8, + tag.uint64Values.data(), ok); +} + +template <> +std::vector readTagAsVector(const ReadContext &rc, const TagEntry &tag, + bool &ok) +{ + return readTagAsVectorInternal(rc, tag, TagType::Float, + tag.float32Values.data(), ok); +} + +template <> +std::vector readTagAsVector(const ReadContext &rc, const TagEntry &tag, + bool &ok) +{ + return readTagAsVectorInternal(rc, tag, TagType::Double, + tag.float64Values.data(), ok); +} + +} // namespace detail + +/** Represents a TIFF Image File Directory (IFD). */ +class Image +{ + public: + /** Constructor. Should not be called directly. Use the open() method */ + Image(const std::shared_ptr &rc, bool isBigTIFF) + : m_rc(rc), m_isBigTIFF(isBigTIFF) + { + } + + /** Return read context */ + const std::shared_ptr &readContext() const + { + return m_rc; + } + + /** Return whether the file is BigTIFF (if false, classic TIFF) */ + inline bool isBigTIFF() const + { + return m_isBigTIFF; + } + + /** Return if values of more than 1-byte must be byte swapped. + * To be only taken into account when reading pixels. Tag values are + * automatically byte-swapped */ + inline bool mustByteSwap() const + { + return m_rc->mustByteSwap(); + } + + /** Return the offset of the this IFD */ + inline uint64_t offset() const + { + return m_offset; + } + + /** Return the offset of the next IFD (to pass to Image::open()), + * or 0 if there is no more */ + inline uint64_t nextImageOffset() const + { + return m_nextImageOffset; + } + + /** Return width of the image in pixels */ + inline uint32_t width() const + { + return m_width; + } + + /** Return height of the image in pixels */ + inline uint32_t height() const + { + return m_height; + } + + /** Return number of bits per sample */ + inline uint32_t bitsPerSample() const + { + return m_bitsPerSample; + } + + /** Return number of samples (a.k.a. channels, bands) per pixel */ + inline uint32_t samplesPerPixel() const + { + return m_samplesPerPixel; + } + + /** Return planar configuration */ + inline PlanarConfigurationType planarConfiguration() const + { + return m_planarConfiguration; + } + + /** Return planar configuration */ + inline PhotometricInterpretationType photometricInterpretation() const + { + return m_photometricInterpretation; + } + + /** Return compression method used */ + inline CompressionType compression() const + { + return m_compression; + } + + /** Return predictor value (used for Deflate, LZW, ZStd, etc. compression) */ + inline uint32_t predictor() const + { + return m_predictor; + } + + /** Return sample format */ + inline SampleFormatType sampleFormat() const + { + return m_sampleFormat; + } + + /** Return the number of rows per strip */ + inline uint32_t rowsPerStrip() const + { + return m_rowsPerStrip; + } + + /** Return the number of strips/tiles. + * Return 0 if inconsistent values between ByteCounts and Offsets arrays. */ + inline uint64_t strileCount() const + { + return m_strileCount; + } + + /** Return whether image is tiled */ + inline bool isTiled() const + { + return m_isTiled; + } + + /** Return tile width */ + inline uint32_t tileWidth() const + { + return m_tileWidth; + } + + /** Return tile width */ + inline uint32_t tileHeight() const + { + return m_tileHeight; + } + + /** Return number of tiles per row */ + uint32_t tilesPerRow() const + { + if (m_tileWidth > 0) + { + return uint32_t((uint64_t(m_width) + m_tileWidth - 1) / + m_tileWidth); + } + return 0; + } + + /** Return number of tiles per column */ + uint32_t tilesPerCol() const + { + if (m_tileHeight > 0) + { + return uint32_t((uint64_t(m_height) + m_tileHeight - 1) / + m_tileHeight); + } + return 0; + } + + /** Convert a tile coordinate (xtile, ytile, bandIdx) to a flat index */ + uint64_t tileCoordinateToIdx(uint32_t xtile, uint32_t ytile, + uint32_t bandIdx, bool &ok) const + { + if (m_isTiled && m_tileWidth > 0 && m_tileHeight > 0) + { + const auto lTilesPerRow = tilesPerRow(); + const auto lTilesPerCol = tilesPerCol(); + if (xtile >= lTilesPerRow || ytile >= lTilesPerCol) + { + ok = false; + return 0; + } + auto idx = uint64_t(ytile) * lTilesPerRow + xtile; + if (bandIdx && + m_planarConfiguration == PlanarConfiguration::Separate) + { + idx += uint64_t(bandIdx) * lTilesPerCol * lTilesPerRow; + } + return idx; + } + ok = false; + return 0; + } + + /** Return the offset of strip/tile of index idx */ + uint64_t strileOffset(uint64_t idx, bool &ok) const + { + return readUIntTag(m_strileOffsetsTag, idx, ok); + } + + /** Return the offset of a tile from its coordinates */ + uint64_t tileOffset(uint32_t xtile, uint32_t ytile, uint32_t bandIdx, + bool &ok) const + { + const auto idx = tileCoordinateToIdx(xtile, ytile, bandIdx, ok); + return ok ? strileOffset(idx, ok) : 0; + } + + /** Return the byte count of strip/tile of index idx */ + uint64_t strileByteCount(uint64_t idx, bool &ok) const + { + return readUIntTag(m_strileByteCountsTag, idx, ok); + } + + /** Return the offset of a tile from its coordinates */ + uint64_t tileByteCount(uint32_t xtile, uint32_t ytile, uint32_t bandIdx, + bool &ok) const + { + const auto idx = tileCoordinateToIdx(xtile, ytile, bandIdx, ok); + return ok ? strileByteCount(idx, ok) : 0; + } + + /** Return the list of tags */ + inline const std::vector &tags() const + { + return m_tags; + } + + /** Return the (first) tag corresponding to a code, or nullptr if not found */ + const TagEntry *tag(TagCodeType tagCode) const + { + for (const auto &tag : m_tags) + { + if (tag.tag == tagCode) + return &tag; + } + return nullptr; + } + + /** Read an ASCII tag as a string */ + std::string readTagAsString(const TagEntry &tag, bool &ok) const + { + if (tag.type == TagType::ASCII) + { + if (tag.value_offset) + { + if LIBERTIFF_CONSTEXPR (sizeof(tag.count) > sizeof(size_t)) + { + if (tag.count > std::numeric_limits::max()) + { + ok = false; + return std::string(); + } + } + return readContext()->readString( + tag.value_offset, static_cast(tag.count), ok); + } + if (tag.count) + { + std::string res(tag.charValues.data(), + static_cast(tag.count)); + if (res.back() == 0) + res.pop_back(); + return res; + } + } + ok = false; + return std::string(); + } + + /** Read a numeric tag as a vector. You must use a type T which is + * consistent with the tag.type value. For example, if + * tag.type == libertiff::TagType::Short, T must be uint16_t. + * libertiff::TagType::Undefined must be read with T=uint8_t. + */ + template + std::vector readTagAsVector(const TagEntry &tag, bool &ok) const + { + return detail::readTagAsVector(*(m_rc.get()), tag, ok); + } + + /** Returns a new Image instance for the IFD starting at offset imageOffset */ + template + static std::unique_ptr + open(const std::shared_ptr &rc, + const uint64_t imageOffset, + const std::set &alreadyVisitedImageOffsets = + std::set()) + { + // To prevent infinite looping on corrupted files + if (imageOffset == 0 || alreadyVisitedImageOffsets.find(imageOffset) != + alreadyVisitedImageOffsets.end()) + { + return nullptr; + } + + auto image = LIBERTIFF_NS::make_unique(rc, isBigTIFF); + + image->m_offset = imageOffset; + image->m_alreadyVisitedImageOffsets = alreadyVisitedImageOffsets; + image->m_alreadyVisitedImageOffsets.insert(imageOffset); + + bool ok = true; + int tagCount = 0; + uint64_t offset = imageOffset; + if LIBERTIFF_CONSTEXPR (isBigTIFF) + { + const auto tagCount64Bit = rc->read(offset, ok); + // Artificially limit to the same number of entries as ClassicTIFF + if (tagCount64Bit > std::numeric_limits::max()) + return nullptr; + tagCount = static_cast(tagCount64Bit); + offset += sizeof(uint64_t); + } + else + { + tagCount = rc->read(offset, ok); + offset += sizeof(uint16_t); + } + if (!ok) + return nullptr; + image->m_tags.reserve(tagCount); + for (int i = 0; i < tagCount; ++i) + { + TagEntry entry; + + // Read tag code + entry.tag = rc->read(offset, ok); + offset += sizeof(uint16_t); + + // Read tag data type + entry.type = rc->read(offset, ok); + offset += sizeof(uint16_t); + + // Read number of values + if LIBERTIFF_CONSTEXPR (isBigTIFF) + { + auto count = rc->read(offset, ok); + entry.count = count; + offset += sizeof(count); + } + else + { + auto count = rc->read(offset, ok); + entry.count = count; + offset += sizeof(count); + } + + uint32_t singleValue = 0; + bool singleValueFitsInUInt32 = false; + if (entry.count) + { + if LIBERTIFF_CONSTEXPR (isBigTIFF) + { + image->ParseTagEntryDataOrOffset( + entry, offset, singleValueFitsInUInt32, singleValue, + ok); + } + else + { + image->ParseTagEntryDataOrOffset( + entry, offset, singleValueFitsInUInt32, singleValue, + ok); + } + } + if (!ok) + return nullptr; + + image->processTag(entry, singleValueFitsInUInt32, singleValue); + + image->m_tags.push_back(entry); + } + + image->finalTagProcessing(); + + if LIBERTIFF_CONSTEXPR (isBigTIFF) + image->m_nextImageOffset = rc->read(offset, ok); + else + image->m_nextImageOffset = rc->read(offset, ok); + + image->m_openFunc = open; + + return std::unique_ptr(image.release()); + } + + /** Returns a new Image instance at the next IFD, or nullptr if there is none */ + std::unique_ptr next() const + { + return m_openFunc(m_rc, m_nextImageOffset, + m_alreadyVisitedImageOffsets); + } + + private: + const std::shared_ptr m_rc; + std::unique_ptr (*m_openFunc)( + const std::shared_ptr &, const uint64_t, + const std::set &) = nullptr; + + std::set m_alreadyVisitedImageOffsets{}; + uint64_t m_offset = 0; + uint64_t m_nextImageOffset = 0; + uint32_t m_width = 0; + uint32_t m_height = 0; + uint32_t m_bitsPerSample = 0; + uint32_t m_samplesPerPixel = 0; + uint32_t m_rowsPerStrip = 0; + CompressionType m_compression = Compression::None; + SampleFormatType m_sampleFormat = SampleFormat::UnsignedInt; + PlanarConfigurationType m_planarConfiguration = + PlanarConfiguration::Contiguous; + PhotometricInterpretationType m_photometricInterpretation = + PhotometricInterpretation::MinIsBlack; + uint32_t m_predictor = 0; + + const bool m_isBigTIFF; + bool m_isTiled = false; + uint32_t m_tileWidth = 0; + uint32_t m_tileHeight = 0; + uint64_t m_strileCount = 0; + + std::vector m_tags{}; + const TagEntry *m_strileOffsetsTag = nullptr; + const TagEntry *m_strileByteCountsTag = nullptr; + + Image(const Image &) = delete; + Image &operator=(const Image &) = delete; + + /** Process tag */ + void processTag(const TagEntry &entry, bool singleValueFitsInUInt32, + uint32_t singleValue) + { + if (singleValueFitsInUInt32) + { + switch (entry.tag) + { + case TagCode::ImageWidth: + m_width = singleValue; + break; + + case TagCode::ImageLength: + m_height = singleValue; + break; + + case TagCode::Compression: + m_compression = singleValue; + break; + + case TagCode::SamplesPerPixel: + m_samplesPerPixel = singleValue; + break; + + case TagCode::RowsPerStrip: + m_rowsPerStrip = singleValue; + break; + + case TagCode::PlanarConfiguration: + m_planarConfiguration = singleValue; + break; + + case TagCode::PhotometricInterpretation: + m_photometricInterpretation = singleValue; + break; + + case TagCode::Predictor: + m_predictor = singleValue; + break; + + case TagCode::TileWidth: + m_tileWidth = singleValue; + break; + + case TagCode::TileLength: + m_tileHeight = singleValue; + break; + + default: + break; + } + } + + if (entry.count && + (entry.type == TagType::Byte || entry.type == TagType::Short || + entry.type == TagType::Long)) + { + // Values of those 2 tags are repeated per sample, but should be + // at the same value. + if (entry.tag == TagCode::SampleFormat) + { + bool localOk = true; + const auto sampleFormat = + static_cast(readUIntTag(&entry, 0, localOk)); + if (localOk) + { + m_sampleFormat = sampleFormat; + } + } + else if (entry.tag == TagCode::BitsPerSample) + { + bool localOk = true; + const auto bitsPerSample = + static_cast(readUIntTag(&entry, 0, localOk)); + if (localOk) + { + m_bitsPerSample = bitsPerSample; + } + } + } + } + + /** Final tag processing */ + void finalTagProcessing() + { + if ((m_strileOffsetsTag = tag(TagCode::TileOffsets)) && + (m_strileByteCountsTag = tag(TagCode::TileByteCounts)) && + m_strileOffsetsTag->count == m_strileByteCountsTag->count) + { + m_isTiled = true; + m_strileCount = m_strileOffsetsTag->count; + } + else if ((m_strileOffsetsTag = tag(TagCode::StripOffsets)) && + (m_strileByteCountsTag = tag(TagCode::StripByteCounts)) && + m_strileOffsetsTag->count == m_strileByteCountsTag->count) + { + m_strileCount = m_strileOffsetsTag->count; + } + } + + /** Read a value from a byte/short/long/long8 array tag */ + uint64_t readUIntTag(const TagEntry *tag, uint64_t idx, bool &ok) const + { + if (tag && idx < tag->count) + { + if (tag->type == TagType::Byte) + { + if (tag->count <= (m_isBigTIFF ? 8 : 4)) + { + return tag->uint8Values[size_t(idx)]; + } + return m_rc->read( + tag->value_offset + sizeof(uint8_t) * idx, ok); + } + else if (tag->type == TagType::Short) + { + if (tag->count <= (m_isBigTIFF ? 4 : 2)) + { + return tag->uint16Values[size_t(idx)]; + } + return m_rc->read( + tag->value_offset + sizeof(uint16_t) * idx, ok); + } + else if (tag->type == TagType::Long) + { + if (tag->count <= (m_isBigTIFF ? 2 : 1)) + { + return tag->uint32Values[size_t(idx)]; + } + return m_rc->read( + tag->value_offset + sizeof(uint32_t) * idx, ok); + } + else if (m_isBigTIFF && tag->type == TagType::Long8) + { + if (tag->count <= 1) + { + return tag->uint64Values[size_t(idx)]; + } + return m_rc->read( + tag->value_offset + sizeof(uint64_t) * idx, ok); + } + } + ok = false; + return 0; + } + + template + void ParseTagEntryDataOrOffset(TagEntry &entry, uint64_t &offset, + bool &singleValueFitsInUInt32, + uint32_t &singleValue, bool &ok) + { + LIBERTIFF_STATIC_ASSERT( + (std::is_same::value || + std::is_same::value)); + assert(entry.count > 0); + + const uint32_t dataTypeSize = tagTypeSize(entry.type); + if (dataTypeSize == 0) + { + return; + } + + // There are 2 cases: + // - either the number of values for the data type can fit + // in the next DataOrOffsetType bytes + // - or it cannot, and then the next DataOrOffsetType bytes are an offset + // to the values + if (dataTypeSize > sizeof(DataOrOffsetType) / entry.count) + { + // Out-of-line values. We read a file offset + entry.value_offset = m_rc->read(offset, ok); + if (dataTypeSize > + std::numeric_limits::max() / entry.count) + { + entry.invalid_value_offset = true; + } + else + { + const uint64_t byteCount = uint64_t(dataTypeSize) * entry.count; + + // Size of tag data beyond which we check the tag position and size + // w.r.t the file size. + constexpr uint32_t THRESHOLD_CHECK_FILE_SIZE = 10 * 1000 * 1000; + + entry.invalid_value_offset = + (byteCount > THRESHOLD_CHECK_FILE_SIZE && + (entry.value_offset >= m_rc->size() || + entry.value_offset > m_rc->size() - byteCount)); + } + } + else if (dataTypeSize == sizeof(uint8_t)) + { + // Read up to 4 (classic) or 8 (BigTIFF) inline bytes + m_rc->read(offset, size_t(entry.count), &entry.uint8Values[0], ok); + if (entry.count == 1 && entry.type == TagType::Byte) + { + singleValueFitsInUInt32 = true; + singleValue = entry.uint8Values[0]; + } + } + else if (dataTypeSize == sizeof(uint16_t)) + { + // Read up to 2 (classic) or 4 (BigTIFF) inline bytes + for (uint32_t idx = 0; idx < entry.count; ++idx) + { + entry.uint16Values[idx] = + m_rc->read(offset + idx * sizeof(uint16_t), ok); + } + if (entry.count == 1 && entry.type == TagType::Short) + { + singleValueFitsInUInt32 = true; + singleValue = entry.uint16Values[0]; + } + } + else if (dataTypeSize == sizeof(uint32_t)) + { + // Read up to 1 (classic) or 2 (BigTIFF) inline bytes + entry.uint32Values[0] = m_rc->read(offset, ok); + if (entry.count == 1 && entry.type == TagType::Long) + { + singleValueFitsInUInt32 = true; + singleValue = entry.uint32Values[0]; + } + if LIBERTIFF_CONSTEXPR (std::is_same::value) + { + if (entry.count == 2) + { + entry.uint32Values[1] = + m_rc->read(offset + sizeof(uint32_t), ok); + } + } + } + else if LIBERTIFF_CONSTEXPR (std::is_same::value) + { + if (dataTypeSize == sizeof(uint64_t)) + { + // Read one inline 64-bit value + if (entry.type == TagType::Rational) + entry.float64Values[0] = m_rc->readRational(offset, ok); + else if (entry.type == TagType::SRational) + entry.float64Values[0] = + m_rc->readSignedRational(offset, ok); + else + entry.uint64Values[0] = m_rc->read(offset, ok); + } + else + { + assert(false); + } + } + else + { + // fprintf(stderr, "Unexpected case: tag=%u, dataType=%u, count=%u\n", entry.tag, entry.type, entry.count); + assert(false); + } + + offset += sizeof(DataOrOffsetType); + } +}; + +/** Open a TIFF file and return its first Image File Directory + */ +template +std::unique_ptr open(const std::shared_ptr &file) +{ + unsigned char signature[2] = {0, 0}; + (void)file->read(0, 2, signature); + const bool littleEndian = signature[0] == 'I' && signature[1] == 'I'; + const bool bigEndian = signature[0] == 'M' && signature[1] == 'M'; + if (!littleEndian && !bigEndian) + return nullptr; + + const bool mustByteSwap = littleEndian ^ isHostLittleEndian(); + + auto rc = std::make_shared(file, mustByteSwap); + bool ok = true; + const int version = rc->read(2, ok); + constexpr int CLASSIC_TIFF_VERSION = 42; + if (version == CLASSIC_TIFF_VERSION) + { + const auto firstImageOffset = rc->read(4, ok); + return Image::open(rc, firstImageOffset, {}); + } + else if LIBERTIFF_CONSTEXPR (acceptBigTIFF) + { + constexpr int BIGTIFF_VERSION = 43; + if (version == BIGTIFF_VERSION) + { + const auto byteSizeOfOffsets = rc->read(4, ok); + if (byteSizeOfOffsets != 8) + return nullptr; + const auto zeroWord = rc->read(6, ok); + if (zeroWord != 0 || !ok) + return nullptr; + const auto firstImageOffset = rc->read(8, ok); + return Image::open(rc, firstImageOffset, {}); + } + } + + return nullptr; +} +} // namespace LIBERTIFF_NS + +#ifdef LIBERTIFF_C_FILE_READER +#include +#include + +namespace LIBERTIFF_NS +{ +/** Interface to read from a FILE* handle */ +class CFileReader final : public FileReader +{ + public: + explicit CFileReader(FILE *f) : m_f(f) + { + } + + ~CFileReader() override + { + fclose(m_f); + } + + uint64_t size() const override + { + std::lock_guard oLock(m_oMutex); + fseek(m_f, 0, SEEK_END); + return ftell(m_f); + } + + size_t read(uint64_t offset, size_t count, void *buffer) const override + { + std::lock_guard oLock(m_oMutex); + if (fseek(m_f, static_cast(offset), SEEK_SET) != 0) + return 0; + return fread(buffer, 1, count, m_f); + } + + private: + FILE *const m_f; + mutable std::mutex m_oMutex{}; + + CFileReader(const CFileReader &) = delete; + CFileReader &operator=(const CFileReader &) = delete; +}; +} // namespace LIBERTIFF_NS +#endif + +#endif // LIBERTIFF_HPP_INCLUDED