From a37c08c11ba097742d2eb68d55f55e29ec9e0e7b Mon Sep 17 00:00:00 2001
From: simon987 <me@simon987.net>
Date: Sun, 26 Apr 2020 14:46:20 -0400
Subject: [PATCH] Fix whash for images with invalid sizes, add remove_ll option
 for whash, remove multi hash

---
 CMakeLists.txt                |  40 +------
 README.md                     |  11 +-
 bench/README.md               |   3 -
 bench/benchmark.py            |  12 --
 bench/results/multi_large.png | Bin 17671 -> 0 bytes
 bench/results/multi_small.png | Bin 16164 -> 0 bytes
 bench/run.py                  |   2 -
 benchmark.cpp                 |  18 +--
 fastimagehash.cpp             | 209 ++++------------------------------
 fastimagehash.h               |  22 +---
 imhash.c                      |  22 +---
 11 files changed, 38 insertions(+), 301 deletions(-)
 delete mode 100644 bench/results/multi_large.png
 delete mode 100644 bench/results/multi_small.png

diff --git a/CMakeLists.txt b/CMakeLists.txt
index d9e6570..e50d990 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -25,12 +25,6 @@ add_library(
         fastimagehash.cpp fastimagehash.h
 )
 
-add_library(
-        fastimagehash_debug
-        SHARED
-        fastimagehash.cpp fastimagehash.h
-)
-
 target_include_directories(
         fastimagehash
         PUBLIC
@@ -39,14 +33,6 @@ target_include_directories(
         ${FFTW_INCLUDE_DIRS}
 )
 
-target_include_directories(
-        fastimagehash_debug
-        PUBLIC
-        ${CMAKE_SOURCE_DIR}/thirdparty/wavelib/header/
-        ${OpenCV_INCLUDE_DIRS}
-        ${FFTW_INCLUDE_DIRS}
-)
-
 target_link_libraries(
         fastimagehash
         ${OpenCV_LIBS}
@@ -55,35 +41,18 @@ target_link_libraries(
         pthread
 )
 
-target_link_libraries(
-        fastimagehash_debug
-        asan
-        ${OpenCV_LIBS}
-        ${FFTW_LIBRARIES}
-        wavelib
-        pthread
-)
 
 target_compile_options(
         fastimagehash
         PRIVATE
         -fPIC
         -Ofast
-        #        -march=native
+        -march=native
         -fno-stack-protector
         -fomit-frame-pointer
         -freciprocal-math
 )
 
-target_compile_options(
-        fastimagehash_debug
-        PRIVATE
-        -fPIC
-        -g
-        -fsanitize=address
-)
-
-
 add_executable(bm benchmark.cpp)
 target_link_libraries(
         bm
@@ -99,7 +68,7 @@ set_target_properties(
 add_executable(imhash imhash.c)
 target_link_libraries(
         imhash
-        ${CMAKE_SOURCE_DIR}/libfastimagehash_debug.so
+        ${CMAKE_SOURCE_DIR}/libfastimagehash.so
 )
 target_compile_options(
         imhash
@@ -109,13 +78,12 @@ target_compile_options(
 
 set_target_properties(fastimagehash PROPERTIES PUBLIC_HEADER "fastimagehash.h")
 INSTALL(
-        TARGETS fastimagehash fastimagehash_debug
+        TARGETS fastimagehash
         LIBRARY DESTINATION /usr/lib/
         PUBLIC_HEADER DESTINATION /usr/include/
 )
 
 add_dependencies(fastimagehash wavelib)
-add_dependencies(fastimagehash_debug wavelib)
 add_dependencies(bm fastimagehash)
 add_dependencies(bm benchmark)
-add_dependencies(imhash fastimagehash_debug)
+add_dependencies(imhash fastimagehash)
diff --git a/README.md b/README.md
index 625e106..bcd3aaa 100644
--- a/README.md
+++ b/README.md
@@ -18,6 +18,8 @@ replacement for C/C++.
   <img src="bench/results/phash_large.png"/>
 </p>
 
+*[\*See all benchmarks](bench/README.md)*
+
 ### Example usage
 
 ```C
@@ -30,15 +32,6 @@ int main() {
 }
 ```
 
-For slight additional performance gains, `libfastimagehash` can
-compute all hashes at once instead of decoding the same 
-image at each step.
-<p align="center">
-  <img src="bench/results/multi_small.png"/>
-</p>
-
-*[\*See all benchmarks](bench/)*
-
 
 ### Build from source
 
diff --git a/bench/README.md b/bench/README.md
index 7d4842c..b0bb287 100644
--- a/bench/README.md
+++ b/bench/README.md
@@ -30,6 +30,3 @@ fastimagehash v0.1
 ![ahash_s](results/ahash_small.png)
 ![ahash_l](results/ahash_large.png)
 
-**multi_hash**
-![multi_s](results/multi_small.png)
-![multi_l](results/multi_large.png)
diff --git a/bench/benchmark.py b/bench/benchmark.py
index b937dce..6cfc56d 100644
--- a/bench/benchmark.py
+++ b/bench/benchmark.py
@@ -37,15 +37,3 @@ print_result("ahash", timeit.timeit(
     number=COUNT
 ))
 
-print_result("multi", timeit.timeit(
-    setup="from imagehash import average_hash,phash,whash,dhash \n"
-          "from PIL import Image",
-    stmt="im = Image.open('%s');"
-         "size = %d;"
-         "average_hash(im.copy(), hash_size=size);"
-         "phash(im.copy(), hash_size=size);"
-         "whash(im.copy(), hash_size=size, remove_max_haar_ll=False);"
-         "dhash(im.copy(), hash_size=size);"
-         % (IMAGE, SIZE),
-    number=COUNT
-))
diff --git a/bench/results/multi_large.png b/bench/results/multi_large.png
deleted file mode 100644
index 3d86b5577ff19c0a63d24d697084abcf4f1402fe..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 17671
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

diff --git a/bench/results/multi_small.png b/bench/results/multi_small.png
deleted file mode 100644
index acf68a9e174fef4d1406a9155910b1714aa1c01a..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 16164
zcmc(GbyQqi^Cb!H?iL_{;1=A1ySux)yVJOY1cEyuxVyW%y9b)!!D(cgm-pG4_hx3z
z%x|Xu=>_-pJ?GTfRl91}xe*F-;>ZXD2oMku$dVEwN)QmQbs->LsldU2|3hLL;RgZ1
ze<&#;sN#`+yaw-sv`-3iX8y&dnmkD)oG11(qT^eMxhd|TUfCTx2{<8Yf8-+C!a|E3
zHFecFbX9etGIFfBPwc6j+UiBw?;?mkOH@BEH3kt8_8%?#wr9y3?PsKHwDO!Djekjz
z#iLG?l7}e!C<6Xe#KfP?Dc#;{%n*~2lao)Iilv@FprfP5^BVxcf0KWP5XOD^6AG_6
z9Q=9PIVlSM)!SF03@=yVnOwjZ(Io?NqQDm=UL)zgJQL&p>Q85JqO!G+(bipPS>|2o
zol-={+{s;Il2TnK3{#^h3V3V+e({jT-xnjV<l?rCp684^-pqEy2XE~;BM76A_U(p7
zQXLCGCy1^vp7N&B#G(4PN1W}Ue!srg%;_oKHUEkCemMKHuWh3{2yG_FXVZv`)@8|D
z(`d2%EuHm+2RGsZN_l6iDB*bz<M}P9!)1`pMzP9_qV|*z$rzF+H#EYk2^5|=O;pxV
z6cvtXJy}#r&=Lwn?wJ(5L?E#_hK<s6b-Xq~JG#&+&R&WB<}p&9X~rX_CHQlP;-*{Z
z64-*wfapI!b@w_+DtHQgafS`CDa<TK8JNXbZZk>1dM)3TM-+NAyK;ND02ksvg?l3K
z0MPf#h`x`Tz=NS@HdRMU#a=|@DrWb1xFzw+Tqem7mg~&d6hJ~Gfmxv0^Z0bh@pOBj
zU79=zf5<M|by9d>VWx#}GSgu61h0O1%uGs^$KR{X!hDSoe+}L@FX-=nkJ$KyX%&4i
zviDHg5&d2w%OcA=JM8Twgwgs$L5}kc1ep`G>~g2wGm>!1lC*%L*Fs69L^@h^LM1-3
zwyj=0B|g@Raj8+zAWB13*dFnb<($CpRJ>6KQ*DD&Kx!z4mH-JNy>0jvpNyY;4sXS-
z@Lo5NY$eyv;|f46BD$6s4L-zG&04ZN-Pr2e9NKG7XMD_`!)ulp=p}2E<10O14IEQ#
z?;AW@<8&W@a6?`nIC8yEcm@u@C0D^yRqS{6H&IZQX~aS-Om}+?Fc&zn8n*FCXr-L0
zIQj6>aA?C!@D*v?jpFYK&{>&ADTb-UQROz<kf)Be(a~jIVl>2&uzVQqy1ODZy1l%*
zy*fqGP;thU(-=7uJ#8RqSD2AAyviTaXx-s_l931;ZD!HY0+H|5ylGV*dQ%~0b)=rm
zY02-F-Wsk=4O3Zs6f_UX-_<J5ot-NPUw;)1MQ5Q1O-d=-)g(}mE!WO<({L@^bOJgT
zoOPwMvCO}|&icOY6>;l2B;r%;!4v!J3d^;p3c!3e7)>S&ai`_g0<Ob{#KHCM^T=<x
zm9r78U-Xz`jxrq_$0_<6v$J>j3ynE4*lEYb)#xrBR6^%<yS=#%tt2f$06B(Q34HBh
zag9S|>sKWn#=KziZ6Lcp!2@VnRj9TOkEP}!<U0Hq)-^1CO9rHD&ul?y81W6T@5w^e
zLB-GZNiyZkXe8R(`$bh&y_|=(%7evM;2*pARI_#eQ$A3_n#m<THeQ&a)qi6RBc$mG
zvFdQb-oVck+KY`aA}1rRr>x6^F5>C6y(zJ{LfSkW(K;HjAAXqCyY*P)hkAWnKb?wb
z#RkfqhAgo>F-z+lzJqVxc9$R<B-ztoW_{9Lr9Nbvj1Op-urVURUj>XD?vO-cZGy>G
zbeXQwbb&i2G3kUIn-^QbTaWG3a-{gdKbK&c%O4Fy*i)h^{D46XbS#d?Nr`v@-GsW>
z=4gC5^{Fdf?dI5%=AGVn94?sT<8oal0|Ungi%o{GO2=V_3W!@}`A{qVp<0KgJF}&3
z7F>4(TA@A>J-$DM&nI4uV!!^n?>)r%3q;hI_SSf$G8V6;2BE+70iPl@dFOf}>b#??
z#8Hy7Yk`?v#rf^*(tO^I%^}`H#f%J{KogeSH#@_X!FeG6do*P2W7UgW1!JALqpMTx
zhHa~&df@cD${5@17q|0Vv@EF{Ptg*evL=sD@no0n5HL>fRY2Sq<|2=uA*1}(&dMbX
z=VU`VuYA&|d0XlMMm=gaQMf`$BP9Z`<hEUtqTJLyjgUd#TcaY2v&xAdCo6tPJXDqt
z-II_&nA6T-&~L7cz0wcq-%P+LRNJ129n7w*4NUn7>q4QNK&_4QwPJC9^eu<XGG6qx
z=e`Us6!cBk17FGcA!+Aky_kpjJaeynU8u1u;jZp;p&};P(@53N9=`Bqs`T{J^xmNr
z7>h+?yk#~I56Tj~3q+fW1>%LV5y6xNYuEZKqYWEx<c}alNHlauf-m2>1#D#2B9P;Y
z9PT&Xd{Dz~FL=7n${F{<Z$abUe=Bu`6|_O6OLB6U8eHXb#YJRQ4UuL02({t57@D~8
z7&c7NKEO-b(CT}sb_LLI+M@c9?25_aI-rqZ%Xd#461`fVT%XO6e!(!SBFOPvURdk(
zt&-IA%r<ZQJg2`=t8nHZjnf4vpFI?2QS{#LAc#6XErVWUJj`kOx#ycchvD>q_9s&T
zqNn35y%?c!vrC4$rcD-m|3D}*y5&cANDdMR)#KyyqpN0E33(}DwI|9H=21G?tn^PZ
z+6|#R>=ZOV3miO_+m0~Fr@4`I^V8CGHh=BDwT@;nm4b8|cc}MkmPlbS4JZ|PTY!l9
zjcsyZpUn&Qbhkso&=%QxwP&q`OQB6=EXICkGd&L7;XR7_v$UvMEXFV>qPw-ZF|YYT
zxsK1Pd7?8Pbt81bQ^3U<M6&L?f{*>P?jr!hwj_X7NNtU&a|MYX9i5ZMm0Jb3<#U5=
zXC??4jpc00{Hs*;<HM{b;|X5uH;5D46@E;;=+%|n#2-2$Pd-W#R_Eck7{U}B4d!yc
z%1=LpX$oc;(nNEw{PLWwxsRFR)W-=-#4i05&ExJgyP8a}Q>+GT4h(_XlCW1pXScGu
zgXTg9lErzHD+d0|#5{Y<wX}#A7{#yeN~d5AA=O1NFnq2=b>dQK5i3&RZ|HPGqMJd~
z{>70<62ezxo#`hwT2D@cK>J+ar39v@H87@|C389v1<r71DIZ*(ySW6FWn|AsW5`nI
zPKOoNC24PXySW=gC;KRGO%A*+t##19Qie7*v@`yUZn<&r?D0zWctcRv)6x0!RG?>b
z64kMzmQM>AsUNrIQDH-9%oe-wLo*T(CW;{q-O-D?fSYOygzSBmeP<5T$9%Wp=l8QT
zDIIB!l={}v1p2+$4}HyuBRVq1)z*kr@0*atLbUEn=RCTQLl(gCMNnZ)*??oE3UvZa
zw#XLfoa<+FiB<?H35IIQL^0GM??5GSckG1Fi1-&6O0`0bH4-;7=nP65g%(TY5A?;E
zw%97@q2znr&?Y_aHiin`6_E0VcZw?~E!-NNxldHkMW-a5m8!@`N`!&4upjZ*J|Jp7
z;)BmO-yrtc4ad%rTr}E{$@nt${F{>Ep>{jUm6u=f1$AJx2bA)dI{;e%aQ9gP%lTgi
z<|^9GX|k>rUGM@jO*^{w#?F(iaAS}`PE&4@`YNLxlOuK=jaMYlR5wdg8k0k}eOF^N
zAFprV$q3BI=w)4l={B@iI-a*Xs4%UfYq(d0gmOSdBD2H`Mv029gpv~>SnsCo(#4KM
zg*iKR;7Y{HG&PF}y$gz0ABAF4V+ivchq6SwOv<K;f~n_E*8`4kn)3#}OxhyutCeNa
zsj(MlkBkb1%Xqo4rT$*1r^mFEw4B(J^7W784TYnLWvO?1puXz-w{Hbe2-AF$qDUTM
z=MIeku*kp4=hr&Cu;K@t&fT)ye+7N2iosk)(T;@eC>Q^}L*)Ry?%<WkKe*8oa6@Z!
zHDAW5b^wQh=i1BBmi5HGGGeD+cBR9+X^2A9mKu_ZLVtD{t4Gd|HTLmN>g%Tz?yjLX
zxXe`k!PO0*yotCU+k@?pjn5{YZ2g)Uwux_Duw8u$nah6`g-3Rek2E_{FdFbbwB)ip
zFi#+k?W=d?mIxS|yi?U?kjsd~HERPj^gdt@yiUnk3Hdm?bdKMTq#4#vc%Qd?VV*wP
z;qYN^KC0vibar8YO@rlWoC=Hq=a&fsB(kXq!q+#}Rogb=NG0?=b;5{DRy{G~{1kZs
zb#+A!Za6|c75>?#O~r-ZPbyLq6Kc?)=-N9^-ev80Jhm4m;|05Z5&m>$hpLgm7rqgv
zFKE?AoV@7IbtK2oSt=;2I7bKjoVgwfXi-)u5|?hh8qQ$)IP%I@(e>OcTFVXPTZ^5@
zf{$j`?Yx{JNdKz`$g0eI`V}U>T{I<`FjIEFj|^}(nk7XYt?t1%S~E4)k;;D{;a-<{
z(3%K_0Y6l6{9<dP5X(-4zBiwbh4*o14~!qJ6(w{3BF7ioxsUuPuA=9uwb3NJ-9|$(
zg~}bs<0+L5^9wH+;TLj9GE{(Z4LP2pPj0_tp+V9L&$RMYD@7YlyY)R?e{HuQ@U~(a
z@02S^w5Wi!(sPQ-g@6sM9J#?bSs(X9nJHx6<;pGV9z?xQ4ZQfNis**uv$vDB^1H`P
zL6JOm%G%xiCW(d@zhV{x;cpoII(AI{p-8fm!0uqN6IaP7Ao9tZsk#PHuVig-$O4-U
z5p%f~FeAQ#&ieSx-?h}@E8kgPXxx^7AH(u|N|4fAT9^4WfzJ{THk-dd&_pP^=w1-!
zt)(_K)L069*4%;vRSzC}BJtVyrtG4?72jaghE$b_EKK%>wqf#P8ruW^mDk>dOy~(K
z;Fwmb)%R+iRp@b(6kXMfoRaZccqHyw*sZ9#aH$cp;5aLW`AU`ai81O`iQD$_8M6k_
zA`ET9yhq6ogOIV)xG7>GQd`*!M>TWppVZg43g$Yj^F`Rngf53LAo(Q271RUZ)Q_z;
zVa<%H#d<2-9V&(%qz%_W8f8q`u(F;TR-P?D$E*TIQyALxmO@SqeHiZ(k41kWVI^`$
zi+yLVsC6aVgTKaC2UtzY`9g&m>{GZc`|&|Rj~1h+mBxd`7F$vlD<si{W`J!VaI6=c
zp!C0**-lKp)+t#?oyD~~S0QnR8fLyDI9h&V0-_G#ZP2N8TFnzLhkKNcnDn{8k_~b=
zJIyx#>hG75bj(V`FWCC)3)L4@?ocE90bE*nuG9c(`GNw&yc@(ssn)XYxt{O13AeSK
zo??|Z40fQ3YCZf9#8pV3kP4wh+d#=Q|DOB@+p4V16l>AGSa9%CvKPSd_ZgfeRM%&V
zE$ZK4s++;MozMv=(hbUd2%P;z-@lcXw-c!|=WL3RKpVC4aVW`G0wyigm)W)M>|6iz
zFd`x|D>-3C2SFH2AEVr*m?5}|o`$c-<Fv<7Ymw~=E4hE;G#K@a{b^oVuWuN2f3u>&
z+`zcQDfI+79tf_n^^zzs6=tltPOxf{w;$~tFGl1(yF|F831%oRR`cW7ow^SoE#xIn
z1KHM2{ODO6jI7*!Jmv|}srs|sB>g<U0Fl8AdD;8{MM&ic4(rC8#nkK};;GIlBcdaL
zm2C}iXwj>zJMn|;&9>?LO&pBi%l%Uj#sOL=L+*m8VGPand>^B@h=^C<e_=uY?=nIL
zqAo|CnepuHkJ~#XawprgZblzAGuJ}66eh-ZX3Vc&3sr%9SdeT9JI&{5LO`?AZ5c1J
zg_d~3ul{4#v%_ESY}AOmH~46`Hu&<+^eO}k@AmnTs`x|Tb%j20dkU1_@w_>_mGSVp
z7D=|I3nKck<_MN8rd_$Qo1J2h``U+t(N<~*q5%g|#Ab%I-kPNwZiK<sWdkq(#@x`R
zPMNw&aVAQyFvH=38MxG+rCXVpU@fG4rf3rS8^++6=*brtXsIOjP5`1m`Bs<aTOO3a
zoiCnFoSd`0O}DHH-CU>F6kvU1>M&P1XQa|oycR?S$J#+0pn9s_-vKHxTtXLKt#Bc=
zw6y$0jm0$7&{|YPT@ARFDcoLCj*pLLoc%gvB2=K<e)9cg)%M}j-=y<g59h5IE0l8(
z>wT+?4n?DrCrRr133I$aRlw<Wtdg=aLo+iNQzw=a_$W<YEnaGBcqiuz^PuF&q@)GJ
zZ>oZ&*={c;kcZaIr;fA(Fbo>ptYi6*vi_L5gbtY{j0@!>HdOD2pU*iDSAR|7d4r*h
zj_Zy;ZpYACCjp*BMTQ-qJmL7P>;|94el-S-1G1MM1Q+!M#!&gY(kFc6j{;2`LVF-`
z@=5#y#Tn)s1BzR~!jwxz1Xv$J1*!pAPD3JCuQwsH8Y(%N3HM@knY|bGEmWo0W*~D`
z%qcoMSc{>kg!G0WgBNT?vfDyIGT=eI1RI2s&Rnc+^h1ri)dgh^QTvvGIT$zKhlWe+
z3;m%`xf%Tz3syiKpwz7;j2KK=&w&bl0*vYTHPC?$$pVShP)};q2ns61=;qD@Z<#${
zqEr>`rs$hDPat@HwXt?TViLySj_Pp|Dddf$rKL^uK?rL<-TG?p7trU=&?sHt1%K{7
zHtwAUl<u%@9V}rQmRG?!O}Y%c*lghdoK9oJ-O{=Hyn$j!VWO^>hM_-1>X$!{P$($>
z6R5koS3&;?)Y#uZEt^#RPQR>vi-4Y1(Nm*t%FB7fcl|U6N34|~*o|r)f5ZtDE06QF
zrz?kYye}&dW89N020Uk=>r~mK=_=8T`c+rU+L0lq2F#K>^5oHs1l0Via^EbN6ropN
ze4^4)Q^|SgkyE;j0R{K}*r$a)z}Yny)M*<B^IRUC)6-HE^D%d}{Wn$(o4>@Rl!=K+
z;15J=DljT(V&wqk`N1s?wO;O}xUZ9Pm~1tMFdvr_g+Qcvh8&%Lfl9_zt;=7(ymIPk
z9I$8Qz*xC2%EvW!p>ffiD~Yo*cQ?LBiO1VlAsF7>S@hZzXuRhZp6@1j++BxDIgJYc
zXr3=FJFprYvx%!GKC?y%o&#St*tbrUt8Aad@fKQ0zM2%4>!);8ff_V;in$Fmi2n+u
z1w67MB9~St1!h|v-YL2Br6#%v->{ppF+a!-To+L5z%uqcbpb}p1hB00!lYK-k(1{x
z&|N!`yY;;d*P5x2MdnnifA1%#lokv4LsvZpGb4B@|K&1k0N9tyAaj72=Q3Ydco64%
zP{|DqrSp5~#6Es}@0KMWnqv!jJE{e6$kO7j$9!=%!UMH+U_A~#zIB$IdEe$v=;NoA
z2KY~&aG1>@Z(M)*>0G#foIg)FyF7|uTF`*vvM3(`FC?GTh1P|S0@l$v)JXaQz&R)L
z>PP$4V)mKnEE8^Wk(oD?ajrXFfem*|cF}n12^~0Q9&oN9>pqwE0s+`e$1d6lIF$D3
zd{HI?WDY*n0YoS$Y>o_1NC^(1GR}NpbAop*FNc;;d_QzMt1E44;o4AION+n+0b1r4
zy{VN|q)iT=vPbEp2*-;{*#pghQX8>niKd%3;>LKqRxGTn!7N<(Hz5N!am$}Se}2Qk
z;ZGS}BqAn;qfuY^5-w$%y*6}i?WnEBfIthK=rOdqg7%rA$t#0rlm~?|LNd0@`Xw%|
zF76K`=Te;%r+P*-+uIC1OJhF(+EIFXdW^Fw(B(nP;K4r}e_W;4iIfCCprwU}h*le`
zBb$WwpbswtN%wm!iKtx9$>6heL^TfNq&x#u)m?iyoQs&(tjj3hBAnlr>xX%H@NA|@
zx$&Ey?)FI&YHGZ%5x%beb?D@ld<mVRRKn^>_9uDLuZ|0@!i;P$6^1~<FMIOkH^;C1
zKhW^O$a$OXokPn-kOhl0(AwrtzSfAq8ydEAXlbjcmYj%DgZJuH&Y~|rerP@NCaX1e
zx3AT~rraCwT5a=;Jt^QfkT3d`X8Jf&Cy9U9+jbA0)KnH|6GpppM*l6*+rJpfwHdql
z2`sy>92#$5Wmc-g&CJ~!xXNU;Y7YAzRtauJ6&f-yDYJR}2mX%OUhwy74MF$2UynU@
zN%qWirh~rs>jvbK>>ra%A#t{mAoB_GB}X`-EIPWdU}4$xC;`ZK>0~no4KMh*0f%fU
z8IfNbVUDwAq}N&QAxB3Dx3$0eleKWqCI10SyMMxRaDfCLU(?{O`wxH|{PcC+YWY^k
zmY0|3$jmN0n|dz?X1_YNQmkWUmqu2VFw*o;!=MS5%T;HLoLDmjN~)dANt<(v^Zfj`
zgJWY^@J2K*kV{bepFs}1z&fj(`Nzk`3`$;x$4bc}s9h(nqx5tGVuCmi!{vX&F`xkl
zyfR?j446>r1RgfhBAdQk=d88XS2ngHT#zRKYwO8O=#i%6Phvj(cbHwXe%T60@Kz|G
z2&>ul>igv>G#t?n4$q3UY#!g1w*9P5f)#3*5DV}(SAmxcY5fcfQ?U~M(OdQ=1&o}_
zKyXy-%yyz0U842DW}2vdQYeifq4CTDUki6(_@e`5N%8-vA>~5na@>_Yol(g@fEOyi
zoX&h4hX*wWqhy3b@#YOy%LG-=Wc@22vofJZd3xOy_!mC|xX{}3N(d==IgfU@;!zCq
zaWG!|E$K_xzaS_MoG3|Q2;r6*m|ktu{n+|fs3hPGVxHnbQfAK(n^ZB6;E@84a4*&%
zq;5ox5mRpT*lCvRwoAi8ci8DX%NqjyLFDIBjf5M~>j5gnk*!Sy%>C_&kMQP$9~Nz2
z_QV&wCo)!fzR&Z8+zv$T+V)`ynEIUqGnj$cj0R5sdq`EU%S;BTPEdV`q{L)${m9wc
z@EpWd{wGZB&Mo|ulrVPp_9md1u^AaFaHUFKs|y~3SCQ)W<^+e~4?<j9BaTg^kJML)
zOT09S2B!qD4cm@K&Ct9AssP>!(TLJgSSEJ%(2OsbrNTSt6;QmtPTm!eGn}xbujzPL
z$Ae*^$CH7e3zIZ&V9;WW_t1mlvuz0I-N`cShrztOEr&Oum&(anB_zA>MMnolUltxD
zdsm`AbRSwYue|ISEI56@XHSb51Q;_fFW_I;oA%TO(Ke56#BkZFc2ocZCy#5;H#hKA
zgD}Y|b_2T(*~usMX4+n|2VZdZAbsU{+e}K=C%9Ge=FgBD0Rlf)B;WaW$mL=F_qM5C
z<<*&Hse}}I-QIV<vTUaL4PZ{kL^^mC2(Blm{^-t6p@nSULG>Pcaq)+Qe~t6$kaCc^
z!)Aj`tzQd{$-#c5>ylUR-mGxN7T=Z;r6lT$t9+0OnoJBT^P^{K+SU^3&_!1aJT;UF
zLWNlx6a3!Q6dnacesWKJndaeJINc6|{i^F^|0-;mQ~NIUjfH_|+6$)1UsS7wRp?W|
z81{+|%$Fq*yc;_f6&A)D4gi7bjs1lg>8dc66QtfdJH^vV!u~O=bkO7+QbroG=tmIA
zd*^#RUR)m(?bW{cBC<v04OC}+|KGRC1x=d9mY2k4Kg9MlDsCZqwCSo@w8SJ-^Dq9I
zd!y0ugn|YR%a-XM8gOH1vrkq|m^$tlx8>Hyt*fsrUh6c4^j5Peh<%p(<E~h!99sAm
zF?P|cx`@guT1d>Ehl^$7uBJPSn|mI?mo+MWez-Ogne|?D^=4kDzPfL?7WsvG=6c>c
zE(bc>s1EBOP5CQUI%n*~K*y$FrbpzF4+lM;HL28z^boWz2;K_^wJJi|&wYS0sqmMU
ze`kD?JaxO2-#t?>Dx5s|4yx$O%}#0uWOeLB$ai!3kfwg}LBZj6bo4e)mTD)*pHiO-
z*=$E;MasnUkHOY=W!j^lsCBc34cn7@uh0EVd1$Dx_;D87Gb*B`-{8_yV3hEdK9u*q
zOUBcKkNv$5I@>6;r$5vN1s-Nw50mMPC6ufyP800Q4o!T1<I-n59_|`Yf=ZKA=XJuW
zi-3nyzdRzf3Q)|OFzh`^EUdX(p&En#2dC8z{wq>gGWLS49={&{#$`<;XiY<;G%NgP
zg|BwRBD>Xx^4QgwI#(n|vW@iG+7E+R2)iB0w!di&v-tK#<t4M9d&%slT`iZKw!A$R
zt@R!b8AT|2!?l$4b=&M2d>XLo%#@Tq7hb9OEw<nyGz9`(H?S~5$_vgcn@Q5*p2Hkv
zM<V@AX|s4%UNb)}zdzE|hMhnyN=d;_7#eD%;`Mv*=PO2e-_*pF`K%K7QsUOB29~^(
zxW1o0B?khJ_eIJyYt-mntW<Jtp^G$Nx9zr+WP%FW%1TQEv=4IUZB7s1FO>Ro=V$Xj
z>qUc?`4~y+E^he4+$~wJAEkc6r=W!5tDa^MO0EqRrhwgQ%&tIOu58R%>sQzzj!(&J
zq}TpWr~)!hL9Q0O+lc|<>utR%y?wf&HED?U^cdjU-<nDgXcdC4Z$I#{Cnz`OU2$YR
zG3Mn`ayb8;*7%r<=*6L7WA6PIbkx|t3`ybhkl?VA+bx0CTxy`d@$<^QfYS*z#4ala
z;(5FfBOT839V(Ju1A5?f>IvRU-U6BN)RK*o)w|u{GxKNkiMa{`d*Wr1T-YDg#Y<xl
z42=J}>SD5L@>_2Vskwp4Bmz-WIt-+>xJPVO+`AsayjmuXztnZ=e(O3CU|lB$8@ORi
zkxm}FlZAr)$;}CeyABwEZ>Nr?b@R8h`L9}8X}<3hoAp$V@H;%jFa0r~Y~y2UPEQ8N
z;og6d=+`T_evwlqnI#tjLFEj#)!&*Bm<wsxggZ!3Noh#mZgrPC*hm2b&dabR7M*a0
zVud$vO$@n+h^)4C9`N69A*F;izf`>UU}a)~B8AYar}f!vSEI%7GLjxo0lqQ$>+c$Q
zBn4a}Yg<fUk<fma(|qAvL~!mdb1(9yM4h1gxgN#Xt7!+a*Esf1K#w+f)av|`Arl7&
z2VWoJ%*%me@^tl&?wen^^R2fF`r$I+=->+EA4Q4m9G8-fz|k5aVD;l=vpoWs`z+1L
zt4@Sek$!5SBIHdM9`Pelv=wB7%D>z$5B$%o=zLTR3|6$Z$Mixr>-l1UTnx>N!xD$L
z0Hm4WbLVTTt6$rFi_)rT`%X0~jUT0@{6)%(S?s%zdeQECpXKNGc1LTmLt`UbU5TgX
zSL@yuoa^}2ID0^<8?iwjeGw>b3O+0DJrWzJjx+Am6ZRL>zIx&7F*Bq6+&HHB4*p>(
zlg`dug7uRQe0}5+y3x|;zoXn~dFq1{PTwB_iq>lM_BqPxN)in~6`+zYQTsZqbfx|+
zO}OP_js_v0$~L(BsVFFy*%pdlG7+ieJcK7-TdJ=*n<`>7RrQBQ<Y)Y!o^q1FFuNFe
zl`^!AO{b2F_Y~%FrN{c$jQyWSr2@tQ<^HG>qMNs(oM-!dgLKw;Ym0XpzYLW%;4c0$
zxm^35T)Kjj%eS`O3Kh^yx;TIi(0pgGvLSG_6}F)1F?fQIxI2V_Od_L|bm4*cldBIU
zFP}I0C>xF-O6m#ci`lusHGn4D*;BXuWUIYw0tTh@oIfcsG1cbe9oGAqdE?-2T*V$8
zg95Gje`l6?hkr^_H2W}Fg7Q>}3*=y-7SqMnGB#xJ&xF(Yhl4|5g!8$1b_lBp0g&Nk
zJXG*_ce@WYh<V(;2zi<UMk>ggO8>|+jsIB@O{$2$q`{0p6QC6T9rXHX8<Oew6XD%A
z;&=$QW@f*W$+N}nDJMxu$xmr%0+_uj<P;Q$rP>-iV0~74<SXLFNR-yaNd`~uyfT*~
zIRW*;dipbt7@hiQ8sY}MXju`rcqBa!=+!RyytJfbR}KeowbVI>_Q%43#loSu%k4E@
zL7VYK{@VD^|G-<ye``+v<}GEmDyLHoqGbnrd$C`yN+Pu`WF7vQ)(#ING$t}w$x={|
zIsFJ%pIBYh$=lxpvkq8nOC$k2w{;L8t38rIm78OBl=eYfe(|O-X=fyVodFR2ZhX=u
zc7|AymoxstDJ6qaV|_835nxxenL;pKZosYEGv)?s+HQ<JktNTagDt%CyVebaPZZ@o
zQ?LI@u?ppu21l=ox1e^6298qPx`Lc<*uqKNNZxiLvDIgHe-g(5H~1!z%W%2hvXpJ#
z-x5b{f0gqy*^Xej#nmX5n1la_R;?HK`9a`>x3)j9aYaIA1fxt3z=8nwhG~NT1pgmL
z3=fcMc3C^WA#UT;*+b)<xky&TioYNR2~^`87!2D>PmlnWtbx;lr|9^<<fPnq%_mzo
zKk_D@^Xe**Wk0H9uYKX7W{yHfLyKJ7|6*vd0>NLSMz;F>E5ixYOI&8*{>m|3Y3`v6
zwMH2Jb-Xj>p@s$3P$&s4Uv53l`Uz!_n;$bXlMj85AnFaQ>{ZeWqXNK{DSk2X6G}&;
zYVR^TW){5eaL?cA3IsnH!TD=Tn)QNUit!sQz8wT?gPCt77FWQr9OIl3EIB*5?hZ-D
zQAtkk-+YrViUJ#!%8Ba_BgKj62-anRqR(_%LFox9Q$JHU+>C+K%G@~32uWZ6<Qm(8
zf`UPuWmdpIq|Y(9eE>cGzBD7e*YVj8daEVJ;_BpE{+TQ7=USVlv$obkw$t?{3M;<E
z-Tb$U;X14!!TLgh<c&6O^~;@Ke@GCG{%iu0i%CW!<9-lL;aAf$Goq!}lYLSCTeH?)
z8<T01rvGR(#L<(~6}=>3cdUXkeIp}>fGbtTPhh=Eg10Jd;td78bgdUo_u<OOh-|Ib
zJslY{<kZSUgx1Wm7<Qapl-A5N*fFTBB=Lq~K7Aq6<FmH5Zl(WqxBE|}EYV7T9fM;Q
z4<A0BR=m}#IEpI3L!C_3YQ7XeV-+he*;BI~!oWjCr4iOop$G4=`#AVHvaxZ*!OeNA
zOccDgUdNY|E<;*Kf76>-0B_|Vy@}za==E=~24O$9Ah!-b@nP)*RzG<=_!?UOb-MKd
zy^Z0973?j07K+yM6`ClgY_+&`l;{Vr;3yZ`h890nGP(4pC&BZ8E8K+mD+of=-bk&P
z?{%2qbG2<);2TByZfVJ|pTC(zYSjo{u`tC)-EjDTC&a<*60di6HNkUxtdv{GcBsMv
zKG;=N$QA(>nEsC>y9m1biIzpjZBj++>+8ZGuWPm3`M0@AJE_%($upX(x$7gZzGrUF
zza=m0nS57$l&-TnF^t||R<1Pb^|m~U14B{D?!6XYQ~WC{^?IB#l86z7JBUHF8-KQs
za$g&-!X;?I)ooiYytIFJS?`CbpQ@G2J@SUWq9x$I;_^#hQKE-od?kb@j0(KqsUi=&
z)U+tAFX4K~e;zcxq;dK7R|2$F2WI((oS2zdb0HgHyD2Otx<J@VbP}DpZY9h7`yy*5
zgLZj5Z{LCL$zIXAn4dNO{s_&Ilu(nrGP|IVt%k;3SnRGmcb-nQ1!cFs9&8HmA!skm
z=7g|?KiTgSF57>IF0$_OPSvH}ANwKE5eZ&X;%v{&7>8Etyg3J#a9`}w`x3*)z#Rbm
zb_n*2mT>UOB$5tffl`0v)WQW_?Uq5up#>j@*`|J)4rVx>H?~<^snBWH3;l515iJ7?
zxcSuJ$xKgAzfpc~@VR1ab{1<%$h+W7Bff%<g{8Wa08qD|lwu91F74=UU95<cKD-#N
zb&+iJcGqoN_h5L4StvE4aRQVIfHs<2+<#}7K!Mvo@NqVE?q>=)mAc|Xkd@M4wd1Nu
zD0o<w)3i6vCf179PpPS)I`ISFo8#e&H6zs-si<K6PQyXVl8HRqjdhJXU-aYxkD5=z
zwVWrqf&&6Q2E1aGC|lj4MN7Vaj*pL!B9EJp{4!qYBfV<;xDMNW$fMrs<u0AFojX{O
zfwaZ9IH$BaaZrG_mo3QUIIxe~H^3C&;XlH1y2`4w>O4VgO_;9a?1LnAOEB~AL56e#
zN?J-PXm#}=<xRo%j~`dv_r;y63Otsuv2kuYw-$`CF)v>nt_=OV-#Kzd{*t=A-2c#r
z^1(Mq&l^G954EAQY~Z2?j!VR)#x}tcb}_cb7PN?gapm>VUI=%W1a6I=@YPd>;yX_C
zUs5D^t#%ZzkJ;9kUl;@S9<v$hPyd#64}KwpPkMlIb;TwpQ(5=G$j<XAJ0jBKzN$aI
z)TYl3fl6OWN=gb^K7tcH3y#>h>+QvtFE`qQ+y6zEUe~xe1N~A2K2K>ahEl$vnLHUv
zPe|^AgM;fkxfo)qwS~yiEI~U_rnRq(N)<O!*zS*GWn!^_j@6M14y>c#9heySycMHJ
zKtwnOwp9Zko=P+3{65~!f&bc3w55OVn?m*han@de1>l9oL67sSpyeWKD|OHbvWT?#
zrp%LON*Gg)G@KIE=>VHQtO;=zy!=EaoO6t*!WH=*m&MN}U@{@?WXFIQrzp{Y05Q#*
z<6Sxzt0_#@)$%vB3<F|2ocznjf*7Bt(~O_Wdhiranmm2%X95-+%BL=>jDTJgLkhNM
z?zeqq<n#3@x1)%H|B^S%iecgmWG+#95@f%hSBY<31l^102429sq*+H&XoHm?A@b3c
z(az;F5{2?>ksg2@CmJi{)sJq^Lt4g0pS^ItjtNmrfS6mr_1~6=BC#?3jy|>8pV68P
zplM5R&ClCpF<)!}(qMLJi<jBj-Fy3xcG;aCQuAl)RBQLo9M>UFX6qs$G?Zy{HG*{7
z7~QoK{nzY!sM)LjTleKR-Tv5ncGe*|7x$aE9W!`r2MA(EtG~pEfuD<JksiHxeG0Ms
ztU4-biPA8*4ScMjG3p&`KE&?itzlrsTex5!!m{_&QmDrpYQkpb?UB)ClPTS;7rffe
zvcB{!#cOtv`A4s-?sv5*PiLfOwyq+4dBR8pEDkd(o2+SrzZdI+Xxr%9OuVQZTSIL_
z-RS&bs9ja%_(XHrI*Qz8xbDyDRJz9G)rW&r2&J>TXn`CX_m!n6=K4`P?`Nmutz8?j
zlAO;gQh9kOwbp<kBGR&7)%yg#q_m+7#K!-g3A}svPSDN{<&b<YWmCX#7>1o)-da;j
zYx`ub1(k~C*1%>T-ow2K@@s|V*%`DTfaL%)uvE{o@o9xS`rt<_Dq0*D5n)-^`IOR}
zm(htjUzQ9_s!ZqXtF5#3C&ax*C!gA(k<33A9{n!~*rfRDKXexwEN1!8k?u>|)pq&^
z<txK79`}=6o26>cIMQB#(L-!O@~%4xnQ=qzK*O3rS-g4S+Vr@H(Hfu_Z?=vEOYEEC
zk}sZ^m#pB8F4cJ*>VVkH3uwE_JcwHCUd4&2l4d9*4i0-k7#kb6H&~`55u^S#0);g*
z#9#H5VXLJ86Oe8Q`rj)pf50OlI|eUhWN0*qN8I4B;ly4lCo>zM7f<j3G<6xt+Vs?;
zZ%=G(j_w{Ra25Zyn%2Z4bdHS)1!Fa>>l+*1;t3wib0uV!hX&dCt$A`)7ZhK+HF>l=
zb!;4t=aO?wdmOg#LfU(CR<9(I0OH~>)Q}3{YlUdo;rr9BD?k}~voHJW2cA4B2lCxS
zUM7z<F{tGdwafQD>)3M->=x>dua@UP*w3qDqA~0GJ82C4oLrs|UsW@L1$<Os-ML0}
zrU)6@LJf^C%Fe22OTXL?WiQDb>0F}s-!F*Yg&^T+sPwEna_p2CCIV6tx!4K%!ErIP
z`&NX3C~B&GJlUU?umD?U%Jj)@)UxEBuYtTjVCTZCHP$&M^N)ECR^pPgcwuv|{BHbc
zZF>eV>pXOHMF*H`(@xw&HeARi*)>5V*#{%!e{4hjYS3|}iWVDF@M4|Ut^yj7!Qdk!
zoctKxvS7H9fZ;kU4*yMQ#y302(WaJgK)l7#XPu~-Pwk&YNp9HDz)rqXYk{{Q^w#eH
zy>r{jb!h<hHE@vs|M3#kUyKTL1vWcSd>g#0qfHf9ad?LmQ$pHx3%jzJH|Bk^NB*G#
z4Zb6x?Y2`U%DSDh0b|C2nyhH@&dpRR&kMTbJ2H{FqxIfK)`E1SeFZ6OwGv{MeqGa9
z`M1G^Pk^18<?Mm<879JZ=>7d#uOYd8a=RI+w++ORd-Id!#@n*^Hw$lh&KagB(y3kA
zSLboQSuG4LbQ=p<TeS~+2gXkCd`I!Q+vy|Up8$1UErrrhoCyb!N*!M5&G^9@R~Yp#
zQvk+qERREmAkzeFHyf?uGK2$H903!g&e)ZusTEro*LIvR4eulu=bu%_u^cs?hcGnQ
zy<CERFz2u-85=tqhTG>{*mAoIj-5f@X?e(ey58PW##s28{kW5#c?O+2`vk4qJ?3bl
zOvIog5i&DSaz6FQh~5l(&7kWWu$Qw^dH-nD9f{9b;|1ko{uu5rd!#VnO_!Q}6qg)W
zw8&NX0pnV(|5Mz;mW&gMQ#4JU?3t6#!Qty2qxlx+2gGQSme*9Ld)*3(@U}r;w`=(8
z&d<05$n1{r0AE=fwl8CdR4y`uW%W@-=pnJ%+g528dClMoj_(OIVV1gw#)x}x83x41
z=tz$Px~*}FlT!f#VKV8DR-7<ni(${Z?1k%oZ&#o3A{$Ya(5SED_J|ff$I(VEW;W(2
zhjdhxYKzm|v*MysvWw--jG(c$%t;@Gsgwe#XYUSCr0U!|0rGLiu3r)vJ!P=!^j!>A
z>z=MgjfDg@5Z8&2bQz@?!g!4tWAS<o_XDj4BOf5JTik;mMg%H(ipi%tF2I2ABm_Ot
z&9=28^t{fUx(9NFG~@4V|7!Oz=ifZBhBG<oy}eQIvU{{V`l$j#9VVW)<xRS6UGaD}
zL1)yKf6ClCv(Kb?PCM>W5uPqRGS#H$K3awwHpE_xLef3IShYypfyUl2pHnhpG0MY<
zCY^@c&eyRa0NBX9UvxJUSe11;KRq!(ir@@KO?3sZMHb%aZM8_=DWkJRd7-GGW`VrB
zowL>!u{au##b(|Glx$e@n{bs|#aPEroL<E$@T;O%y26(8eCWTbbk^8Wb?Jy)MtvgO
zH%#KilJyT4RiMuqPXxa#GsK*;vs3<9VNQa1f2Vi0X8`nihdkA!&9-F@nVpbFaYVWr
z;$9J{@Tg!wDWl%*2fqLqa78&CnBs1$oIc)?cVT;cobr3Xd0R{=!)-XXv7cRA86cBf
zYq157ddL3Yi9dK>a&}dHW8bfK_{2%wil@55QMwg|zD{a5y*HCgl&!Ul?<-yxSXRu6
zy)H^SAaeA~^KI~Wh(=BD$BOp(90y2Y7(_gK%t^?@z31N!nC*4W@*VkJm@3K_7H9n&
zLc!K-Xt^`(IkN0Z9D9Pju=)A@jv7&{wd0P~?6rbiYyA5v^<`91#na@%lAWC=vGz<)
z8Q{Km;8$>t_0EjR+sAZuQo+NJmm}j#Fwxdl29JAoZB^jd&Ya1d;u;C6{CHz9aIP!4
zjSI)!$+R1h9XhGniJ7SZcy$7P)8WWkpnJs+l0^S*Y=|ka)Vw>dh~>TV-gzVp6B^F&
z&=9qgw?j%uo3@nk__QVPzJt?b{<?p0V&8am#Wbo&Glbj<=ZffLk@sd*5yx~yD0i~Y
zk={)wYB^J^ZP@R9q}M>mcpQlE&TEf3C+ie?IU+~;wJEq;^K!!fN-fU`TD_b<J*m!_
zmwQaAHwaUpr=c1CQw!$fXee$=rfpy3r;a%U&~^d&^Xc(!lV|&6J60Xv+~Wg{Eq^!p
z7UGioIj9<SvK)lfxTY0^!}xfFSh3U^zW?d&7-RE9cNLpt<wxCWXG{ogq;&#+ob}Yo
z4ADWH{Z&H+;?5KJy|~|YtTzIF4#-_vv2fsiO(-8XKs6rP*y@GsH>{?c4s-GhVpJMo
z`?zCO9DId>;@^xUFfu*G%+@e?g|vPgVfXah)4JAES(-<TG%aU*o{X;$s}a_T0J!;9
zc&w|tgn62E6jv72oEM~sKkHN+G(D{IUVp1eUw5f(GF6m6Y>$4^&k(ykHZy?h1VQJ~
zjJba86+wrPes96V%kF<T_;wQ<$Q+G)O2({srQEh<z0}SS4NANHhdre#gw=h$X)W1R
ztU12|o+8OI*(&>&Ny^*Aj6SX*b5eZis$O_2v%!Q%Y~_n89TjQ8dgn|>FbN3>QzzP%
zRgc|FLf2v_xvT2KS&wZMH!dbuPEF(uFwo>Plz66Zr4_ZaU$JTRcle++dkeQD2E#<7
zj4C|OC_bX@@pH}#7F(~TaNmFvX#_Vt#C}5SR?G~br7O1cC`PDwNqXl|SX>6Z+%osx
zr9H2Wy8&f3dtgUv_qzS$>=(eg9@B5v{k>27>g5lR>Z~8r*++L${D)<gZu=QjBmIS>
z!}jvH7D~MdrxmBllSMGunUy0)>SPV{65i`&=UsBUAlsPgS7ZM{?hic!Zh@S+NIrIY
z?WV;1f<FB_@Xn2K5uHVjp>Zc;17x>p)0Oo&ek@G(vD6?21F5Iu%Ui^u5fZ*~-9fL{
zX$8iWCrQkV*B5u~T`0z}@6R2EjOuTW9i!=iO}Lsha5yJ;+8SfInUFGy{=E&iw!J$C
zhj7;1N@X0Ds7b?~`c^xQI7&5eeY_p$aX02qz9q44hCdn82B29dOnEct>^gA0=$5<8
z*F<l5Q`~%-jD;2kbRjGDrx_Ru>ntJ&r1|@eg{amMT@v)d7>4psKR(kK8{8km`y5yX
z_XdxguL+zQDCI5Gl1K*cNWJTF{JwvlJjsKktHgBG{k<g4U{FI{h1d*&!kOxFy%GsB
z66Cs^q$@9Fj+1S?G1sU#c!WdNVIa7#^w~|4g-Bhp<+6XLapuB921p#rAc>c4l{8T)
zfBV^t51uQg|739r!={j>pHy9_g+b0)r|2!`iM-{`p(ZBy{=Ye|@C;jaq|*n*bOQ~3
Q`yE13R8FK^$ROyy0flhervLx|

diff --git a/bench/run.py b/bench/run.py
index 1fb061b..6abda3b 100644
--- a/bench/run.py
+++ b/bench/run.py
@@ -34,6 +34,4 @@ for f in files:
                 method = "ahash"
             if "whash" in m:
                 method = "whash"
-            if "multi" in m:
-                method = "multi"
             print("%s_%s,%s" % (f, method, t))
diff --git a/benchmark.cpp b/benchmark.cpp
index dc61db4..0e4ff96 100644
--- a/benchmark.cpp
+++ b/benchmark.cpp
@@ -35,7 +35,7 @@ static void BM_whash(benchmark::State &state) {
     void *buf = load_test_file(&size);
 
     for (auto _ : state) {
-        whash_mem(buf, size, tmp, state.range(), 0, "haar");
+        whash_mem(buf, size, tmp, state.range(), 0, 0, "haar");
     }
 
     free(buf);
@@ -74,27 +74,11 @@ static void BM_mhash(benchmark::State &state) {
     free(buf);
 }
 
-static void BM_multi(benchmark::State &state) {
-    size_t size;
-    void *buf = load_test_file(&size);
-
-    multi_hash_t *m = multi_hash_create(state.range());
-
-    for (auto _ : state) {
-        multi_hash_file(filepath, m, state.range(), 4, 0, "haar");
-    }
-
-    multi_hash_destroy(m);
-
-    free(buf);
-}
-
 BENCHMARK(BM_phash)->ArgName("size")->Arg(8);
 BENCHMARK(BM_whash)->ArgName("size")->Arg(8);
 BENCHMARK(BM_dhash)->ArgName("size")->Arg(8);
 BENCHMARK(BM_ahash)->ArgName("size")->Arg(8);
 BENCHMARK(BM_mhash)->ArgName("size")->Arg(8);
-BENCHMARK(BM_multi)->ArgName("size")->Arg(8);
 
 
 int main(int argc, char **argv) {
diff --git a/fastimagehash.cpp b/fastimagehash.cpp
index 1e48e62..185dcd6 100644
--- a/fastimagehash.cpp
+++ b/fastimagehash.cpp
@@ -10,6 +10,7 @@
 #include <sys/stat.h>
 
 static void init() __attribute__((constructor));
+
 void init() {
     fftw_make_planner_thread_safe();
 }
@@ -36,7 +37,7 @@ double median(uchar *arr, size_t len) {
     std::sort(arr, arr + len);
 
     if (len % 2 == 0) {
-        return (double)(arr[(len / 2) - 1] + arr[len / 2]) / 2;
+        return (double) (arr[(len / 2) - 1] + arr[len / 2]) / 2;
     } else {
         return arr[(len + 1 / 2)];
     }
@@ -200,19 +201,20 @@ int dhash_mem(void *buf, size_t buf_len, uchar *out, int hash_size) {
     return FASTIMAGEHASH_OK;
 }
 
-int whash_file(const char *filepath, uchar *out, int hash_size, int img_scale, const char* wname) {
+int whash_file(const char *filepath, uchar *out, int hash_size, int img_scale, int remove_max_ll, const char *wname) {
     size_t size;
     void *buf = load_file_in_mem(filepath, &size);
     if (buf == nullptr) {
         return FASTIMAGEHASH_READ_ERR;
     }
 
-    int ret = whash_mem(buf, size, out, hash_size, img_scale, wname);
+    int ret = whash_mem(buf, size, out, hash_size, img_scale, remove_max_ll, wname);
     free(buf);
     return ret;
 }
 
-int whash_mem(void *buf, size_t buf_len, uchar *out, const int hash_size, int img_scale, const char *wname) {
+int whash_mem(void *buf, size_t buf_len, uchar *out, const int hash_size, int img_scale, int remove_max_ll,
+              const char *wname) {
     Mat im;
     try {
         im = imdecode(Mat(1, buf_len, CV_8UC1, buf), IMREAD_GRAYSCALE);
@@ -246,6 +248,10 @@ int whash_mem(void *buf, size_t buf_len, uchar *out, const int hash_size, int im
 
     int dwt_level = ll_max_level - level;
 
+    if (dwt_level < 1) {
+        dwt_level = 1;
+    }
+
     try {
         resize(im, im, Size(img_scale, img_scale), 0, 0, INTER_AREA);
     } catch (Exception &e) {
@@ -260,6 +266,21 @@ int whash_mem(void *buf, size_t buf_len, uchar *out, const int hash_size, int im
         data[i] = (double) pixel[i] / 255;
     }
 
+    if (remove_max_ll) {
+        // Remove low level frequency
+        wave_object w_haar_tmp = wave_init("haar");
+        wt2_object wt_haar_tmp = wt2_init(w_haar_tmp, "dwt", img_scale, img_scale, ll_max_level);
+
+        double *coeffs = dwt2(wt_haar_tmp, data);
+
+        coeffs[0] = 0;
+
+        idwt2(wt_haar_tmp, coeffs, data);
+
+        wt2_free(wt_haar_tmp);
+        wave_free(w_haar_tmp);
+    }
+
     wave_object w = wave_init(wname);
     wt2_object wt = wt2_init(w, "dwt", img_scale, img_scale, dwt_level);
 
@@ -346,183 +367,3 @@ int phash_mem(void *buf, size_t buf_len, uchar *out, const int hash_size, int hi
     }
     return FASTIMAGEHASH_OK;
 }
-
-multi_hash_t *multi_hash_create(int hash_size) {
-    auto multi_hash = (multi_hash_t *) malloc(sizeof(multi_hash_t));
-    auto data = (uchar *) malloc((hash_size + 1) * 5);
-
-    multi_hash->ahash = data;
-    multi_hash->phash = data + (hash_size + 1);
-    multi_hash->dhash = data + (hash_size + 1) * 2;
-    multi_hash->whash = data + (hash_size + 1) * 3;
-    multi_hash->mhash = data + (hash_size + 1) * 4;
-
-    return multi_hash;
-}
-
-void multi_hash_destroy(multi_hash_t *h) {
-    free(h->ahash);
-    free(h);
-}
-
-int multi_hash_file(const char *filepath, multi_hash_t *out, int hash_size,
-                    int ph_highfreq_factor, int wh_img_scale, const char* wname) {
-    size_t size;
-    void *buf = load_file_in_mem(filepath, &size);
-
-    if (buf == nullptr) {
-        return FASTIMAGEHASH_READ_ERR;
-    }
-
-    int ret = multi_hash_mem(buf, size, out, hash_size, ph_highfreq_factor, wh_img_scale, wname);
-    free(buf);
-    return ret;
-}
-
-int multi_hash_mem(void *buf, size_t buf_len, multi_hash_t *out,
-                   int hash_size, int ph_highfreq_factor, int wh_img_scale,
-                   const char*wname) {
-
-    if (strcmp(wname, "haar") != 0 && strcmp(wname, "db4") != 0) {
-        throw std::invalid_argument("wname must be either of 'haar' or 'db4'");
-    }
-
-    Mat im;
-    try {
-        im = imdecode(Mat(1, buf_len, CV_8UC1, buf), IMREAD_GRAYSCALE);
-    } catch (Exception &e) {
-        return FASTIMAGEHASH_DECODE_ERR;
-    }
-
-    Mat ahash_im; // Also used for mhash!
-    Mat dhash_im;
-    Mat phash_im;
-    Mat whash_im;
-
-    int ph_img_scale = hash_size * ph_highfreq_factor;
-
-    if ((hash_size & (hash_size - 1)) != 0) {
-        throw std::invalid_argument("hash_size must be a power of two");
-    }
-
-    if (wh_img_scale != 0) {
-        if ((wh_img_scale & (wh_img_scale - 1)) != 0) {
-            throw std::invalid_argument("wh_img_scale must be a power of two");
-        }
-    } else {
-        int image_natural_scale = (int) pow(2, (int) log2(MIN(im.rows, im.cols)));
-        wh_img_scale = MAX(image_natural_scale, hash_size);
-    }
-
-    int ll_max_level = (int) log2(wh_img_scale);
-    int level = (int) log2(hash_size);
-
-    if (ll_max_level < level) {
-        throw std::invalid_argument("hash_size in a wrong range");
-    }
-
-    int dwt_level = ll_max_level - level;
-
-    try {
-        im = imdecode(Mat(1, buf_len, CV_8UC1, buf), IMREAD_GRAYSCALE);
-
-        resize(im, ahash_im, Size(hash_size, hash_size), 0, 0, INTER_AREA);
-        resize(im, dhash_im, Size(hash_size + 1, hash_size), 0, 0, INTER_AREA);
-        resize(im, whash_im, Size(wh_img_scale, wh_img_scale), 0, 0, INTER_AREA);
-        resize(im, phash_im, Size(ph_img_scale, ph_img_scale), 0, 0, INTER_AREA);
-    } catch (Exception &e) {
-        return FASTIMAGEHASH_DECODE_ERR;
-    }
-
-    auto pixels = new double[MAX(ph_img_scale, wh_img_scale) * MAX(ph_img_scale, wh_img_scale)];
-
-    // ahash
-    double avg = mean(ahash_im).val[0];
-
-    uchar *pixel = ahash_im.ptr(0);
-    int endPixel = ahash_im.cols * ahash_im.rows;
-
-    // mhash
-    uchar mhash_sorted [ahash_im.cols * ahash_im.rows];
-    mempcpy(mhash_sorted, pixel, endPixel);
-    double m_median = median(mhash_sorted, endPixel);
-
-    for (int i = 0; i < endPixel; i++) {
-        set_bit_at(out->ahash, i, pixel[i] > avg);
-        set_bit_at(out->mhash, i, pixel[i] > m_median);
-    }
-
-    //dhash
-    int offset = 0;
-    for (int i = 0; i < dhash_im.rows; ++i) {
-        pixel = dhash_im.ptr(i);
-
-        for (int j = 1; j < dhash_im.cols; ++j) {
-            set_bit_at(out->dhash, offset++, pixel[j] > pixel[j - 1]);
-        }
-    }
-
-    //phash
-    pixel = phash_im.ptr(0);
-    endPixel = phash_im.cols * phash_im.rows;
-    for (int i = 0; i < endPixel; i++) {
-        pixels[i] = (double) pixel[i] / 255;
-    }
-
-    double dct_out[ph_img_scale * ph_img_scale];
-    fftw_plan plan = fftw_plan_r2r_2d(
-            ph_img_scale, ph_img_scale,
-            pixels, dct_out,
-            FFTW_REDFT10, FFTW_REDFT10, // DCT-II
-            FFTW_ESTIMATE
-    );
-    fftw_execute(plan);
-    fftw_destroy_plan(plan);
-
-    double dct_lowfreq[hash_size * hash_size];
-    double sorted[hash_size * hash_size];
-
-    int ptr_low = 0;
-    int ptr = 0;
-    for (int i = 0; i < hash_size; ++i) {
-        for (int j = 0; j < hash_size; ++j) {
-            dct_lowfreq[ptr_low] = dct_out[ptr];
-            sorted[ptr_low] = dct_out[ptr];
-            ptr_low += 1;
-            ptr += 1;
-        }
-        ptr += (ph_img_scale - hash_size);
-    }
-
-    double med = median(sorted, hash_size * hash_size);
-
-    for (int i = 0; i < hash_size * hash_size; ++i) {
-        set_bit_at(out->phash, i, dct_lowfreq[i] > med);
-    }
-
-    //whash
-    pixel = whash_im.ptr(0);
-    endPixel = whash_im.cols * whash_im.rows;
-    for (int i = 0; i < endPixel; i++) {
-        pixels[i] = (double) pixel[i] / 255;
-    }
-
-    wave_object w = wave_init(wname);
-    wt2_object wt = wt2_init(w, "dwt", wh_img_scale, wh_img_scale, dwt_level);
-
-    double *coeffs = dwt2(wt, pixels);
-
-    memcpy(sorted, coeffs, sizeof(double) * (hash_size * hash_size));
-
-    med = median(sorted, hash_size * hash_size);
-
-    for (int i = 0; i < hash_size * hash_size; ++i) {
-        set_bit_at(out->whash, i, coeffs[i] > med);
-    }
-
-    wt2_free(wt);
-    wave_free(w);
-    free(coeffs);
-    delete[] pixels;
-    return FASTIMAGEHASH_OK;
-}
diff --git a/fastimagehash.h b/fastimagehash.h
index 11d69b6..ec62585 100644
--- a/fastimagehash.h
+++ b/fastimagehash.h
@@ -1,37 +1,21 @@
 #ifndef FASTIMAGEHASH_FASTIMAGEHASH_H
 #define FASTIMAGEHASH_FASTIMAGEHASH_H
 
-#define FASTIMAGEHASH_VERSION "3.0"
+#define FASTIMAGEHASH_VERSION "4.0"
 
 
 #include <stdio.h>
 
 typedef unsigned char uchar;
 
-typedef struct multi_hash {
-    uchar *ahash;
-    uchar *phash;
-    uchar *dhash;
-    uchar *whash;
-    uchar *mhash;
-} multi_hash_t;
-
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-multi_hash_t *multi_hash_create(int hash_size);
-
-void multi_hash_destroy(multi_hash_t *h);
-
-int multi_hash_file(const char *filepath, multi_hash_t *out, int hash_size, int ph_highfreq_factor, int wh_img_scale, const char*wname);
-
 void hash_to_hex_string_reversed(const uchar *h, char *out, int hash_size);
 
 void hash_to_hex_string(const uchar *h, char *out, int hash_size);
 
-int multi_hash_mem(void *buf, size_t buf_len, multi_hash_t *out, int hash_size, int ph_highfreq_factor, int wh_img_scale, const char* wname);
-
 int mhash_mem(void *buf, size_t buf_len,uchar *out, int hash_size);
 
 int mhash_file(const char *filepath, uchar *out, int hash_size);
@@ -44,9 +28,9 @@ int dhash_file(const char *filepath, uchar *out, int hash_size);
 
 int dhash_mem(void *buf, size_t buf_len, uchar *out, int hash_size);
 
-int whash_file(const char *filepath, uchar *out, int hash_size, int img_scale, const char* wname);
+int whash_file(const char *filepath, uchar *out, int hash_size, int img_scale, int remove_max_ll, const char* wname);
 
-int whash_mem(void *buf, size_t buf_len, uchar *out, int hash_size, int img_scale, const char*wname);
+int whash_mem(void *buf, size_t buf_len, uchar *out, int hash_size, int img_scale, int remove_max_ll, const char*wname);
 
 int phash_file(const char *buf, uchar *out, int hash_size, int highfreq_factor);
 
diff --git a/imhash.c b/imhash.c
index 8f43de2..35d31c8 100644
--- a/imhash.c
+++ b/imhash.c
@@ -29,8 +29,8 @@ int main(int argc, char *argv[]) {
             do_mhash = 1;
         } else {
 
-            uchar hash[9];
-            char hashstr[17];
+            uchar hash[256];
+            char hashstr[512];
 
             if (do_phash) {
                 if (phash_file(argv[i], hash, 8, 4) == 0) {
@@ -51,7 +51,7 @@ int main(int argc, char *argv[]) {
                 }
             }
             if (do_whash) {
-                if (whash_file(argv[i], hash, 8, 0, "haar") == 0) {
+                if (whash_file(argv[i], hash, 8, 0, 1, "haar") == 0) {
                     hash_to_hex_string_reversed(hash, hashstr, 8);
                     printf("%s\tw:%s\n", argv[i], hashstr);
                 }
@@ -62,22 +62,6 @@ int main(int argc, char *argv[]) {
                     printf("%s\tm:%s\n", argv[i], hashstr);
                 }
             }
-
-            multi_hash_t *m = multi_hash_create(8);
-            multi_hash_file(argv[i], m, 8, 4, 0, "haar");
-
-            hash_to_hex_string_reversed(m->phash, hashstr, 8);
-            printf("%s\tmp:%s\n", argv[i], hashstr);
-            hash_to_hex_string_reversed(m->ahash, hashstr, 8);
-            printf("%s\tma:%s\n", argv[i], hashstr);
-            hash_to_hex_string_reversed(m->dhash, hashstr, 8);
-            printf("%s\tmd:%s\n", argv[i], hashstr);
-            hash_to_hex_string_reversed(m->whash, hashstr, 8);
-            printf("%s\tmw:%s\n", argv[i], hashstr);
-            hash_to_hex_string_reversed(m->mhash, hashstr, 8);
-            printf("%s\tmm:%s\n", argv[i], hashstr);
-
-            multi_hash_destroy(m);
         }
     }
 }
\ No newline at end of file