From 0e0586d880b540ac1e7236569f360ff7d2806698 Mon Sep 17 00:00:00 2001
From: Manuel Guenther <manuel.guenther@idiap.ch>
Date: Wed, 6 May 2015 14:48:16 +0200
Subject: [PATCH] Added BIC algorithm

---
 bob/bio/base/algorithm/BIC.py            | 150 +++++++++++++++++++++++
 bob/bio/base/algorithm/__init__.py       |   1 +
 bob/bio/base/config/algorithm/bic.py     |  13 ++
 bob/bio/base/test/data/bic_enroller.hdf5 | Bin 0 -> 27664 bytes
 bob/bio/base/test/data/bic_model.hdf5    | Bin 0 -> 17472 bytes
 bob/bio/base/test/data/iec_enroller.hdf5 | Bin 0 -> 10593 bytes
 bob/bio/base/test/test_algorithms.py     | 123 +++++++++++--------
 setup.py                                 |   1 +
 8 files changed, 235 insertions(+), 53 deletions(-)
 create mode 100644 bob/bio/base/algorithm/BIC.py
 create mode 100644 bob/bio/base/config/algorithm/bic.py
 create mode 100644 bob/bio/base/test/data/bic_enroller.hdf5
 create mode 100644 bob/bio/base/test/data/bic_model.hdf5
 create mode 100644 bob/bio/base/test/data/iec_enroller.hdf5

diff --git a/bob/bio/base/algorithm/BIC.py b/bob/bio/base/algorithm/BIC.py
new file mode 100644
index 00000000..ff052834
--- /dev/null
+++ b/bob/bio/base/algorithm/BIC.py
@@ -0,0 +1,150 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+# Manuel Guenther <Manuel.Guenther@idiap.ch>
+
+import bob.io.base
+import bob.learn.linear
+
+import numpy
+import math
+
+from .Algorithm import Algorithm
+from .. import utils
+
+import logging
+logger = logging.getLogger("bob.bio.base")
+
+class BIC (Algorithm):
+  """Computes the Intrapersonal/Extrapersonal classifier using a generic feature type and feature comparison function"""
+
+  def __init__(
+      self,
+      comparison_function, # the function to be used to compare two features; this highly depends on the type of features that are used
+      maximum_training_pair_count = None,  # if set, limit the number of training pairs to the given number in a non-random manner
+      subspace_dimensions = None, # if set as a pair (intra_dim, extra_dim), PCA subspace truncation for the two classes is performed
+      uses_dffs = False, # use the distance from feature space; only valid when PCA truncation is enabled; WARNING: uses this flag with care
+      read_function = utils.load,
+      write_function = utils.save,
+      **kwargs # parameters directly sent to the base class
+  ):
+
+    # call base class function and register that this tool requires training for the enrollment
+    Algorithm.__init__(
+        self,
+        requires_enroller_training = True,
+
+        comparison_function = str(comparison_function),
+        maximum_training_pair_count = maximum_training_pair_count,
+        subspace_dimensions = subspace_dimensions,
+        uses_dffs = uses_dffs,
+        read_function=str(read_function),
+        write_function=str(write_function),
+
+        **kwargs
+    )
+
+    # set up the BIC tool
+    self.comparison_function = comparison_function
+    self.read_function = read_function
+    self.write_function = write_function
+    self.maximum_pair_count = maximum_training_pair_count
+    self.use_dffs = uses_dffs
+    if subspace_dimensions is not None:
+      self.M_I = subspace_dimensions[0]
+      self.M_E = subspace_dimensions[1]
+      self.bic_machine = bob.learn.linear.BICMachine(self.use_dffs)
+    else:
+      self.bic_machine = bob.learn.linear.BICMachine(False)
+      self.M_I = None
+      self.M_E = None
+
+
+  def _sqr(self, x):
+    return x*x
+
+
+  def _trainset_for(self, pairs):
+    """Computes the array containing the comparison results for the given set of image pairs."""
+    return numpy.vstack([self.comparison_function(f1, f2) for (f1, f2) in pairs])
+
+
+  def train_enroller(self, train_features, enroller_file):
+    """Trains the IEC Tool, i.e., computes intrapersonal and extrapersonal subspaces"""
+
+    # compute intrapersonal and extrapersonal pairs
+    logger.info("  -> Computing pairs")
+    intra_pairs, extra_pairs = bob.learn.linear.bic_intra_extra_pairs(train_features)
+    # limit pairs, if desired
+    if self.maximum_pair_count is not None:
+      if len(intra_pairs) > self.maximum_pair_count:
+        logger.info("  -> Limiting intrapersonal pairs from %d to %d" %(len(intra_pairs), self.maximum_pair_count))
+        intra_pairs = utils.selected_elements(intra_pairs, self.maximum_pair_count)
+      if len(extra_pairs) > self.maximum_pair_count:
+        logger.info("  -> Limiting extrapersonal pairs from %d to %d" %(len(extra_pairs), self.maximum_pair_count))
+        extra_pairs = utils.selected_elements(extra_pairs, self.maximum_pair_count)
+
+
+    # train the BIC Machine with these pairs
+    logger.info("  -> Computing %d intrapersonal results", len(intra_pairs))
+    intra_vectors = self._trainset_for(intra_pairs)
+    logger.info("  -> Computing %d extrapersonal results", len(extra_pairs))
+    extra_vectors = self._trainset_for(extra_pairs)
+
+    logger.info("  -> Training BIC machine")
+    trainer = bob.learn.linear.BICTrainer(self.M_I, self.M_E) if self.M_I is not None else bob.learn.linear.BICTrainer()
+    trainer.train(intra_vectors, extra_vectors, self.bic_machine)
+
+    # save the machine to file
+    self.bic_machine.save(bob.io.base.HDF5File(enroller_file, 'w'))
+
+
+  def load_enroller(self, enroller_file):
+    """Reads the intrapersonal and extrapersonal mean and variance values"""
+    self.bic_machine.load(bob.io.base.HDF5File(enroller_file, 'r'))
+    # to set this should not be required, but just in case
+    # you re-use a trained enroller file that hat different setup of use_DFFS
+    self.bic_machine.use_DFFS = self.use_dffs
+
+
+  def enroll(self, enroll_features):
+    """Enrolls features by concatenating them"""
+    return enroll_features
+
+
+  def write_model(self, model, model_file):
+    """Writes all features of the model into one HDF5 file, using the ``save_function`` specified in the constructor."""
+    hdf5 = bob.io.base.HDF5File(model_file, "w")
+    for i, f in enumerate(model):
+      hdf5.create_group("Feature%d" % i)
+      hdf5.cd("Feature%d" % i)
+      self.write_function(f, hdf5)
+      hdf5.cd("..")
+
+
+  def read_model(self, model_file):
+    """Loads all features of the model from the HDF5 file, using the ``load_function`` specified in the constructor."""
+    hdf5 = bob.io.base.HDF5File(model_file)
+    i = 0
+    model = []
+    while hdf5.has_group("Feature%d" % i):
+      hdf5.cd("Feature%d" % i)
+      model.append(self.read_function(hdf5))
+      hdf5.cd("..")
+      i += 1
+    return model
+
+
+  def read_probe(self, probe_file):
+    """Loads the probe feature from file, using the ``load_function`` specified in the constructor."""
+    return self.load_function(bob.io.base.HDF5File(probe_file))
+
+
+  def score(self, model, probe):
+    """Computes the IEC score for the given model and probe pair"""
+    # compute average score for the models
+    scores = []
+    for i in range(len(model)):
+      diff = self.comparison_function(model[i], probe)
+      assert len(diff) == self.bic_machine.input_size
+      scores.append(self.bic_machine(diff))
+    return self.model_fusion_function(scores)
diff --git a/bob/bio/base/algorithm/__init__.py b/bob/bio/base/algorithm/__init__.py
index 0866bd1e..d22a30f2 100644
--- a/bob/bio/base/algorithm/__init__.py
+++ b/bob/bio/base/algorithm/__init__.py
@@ -1,3 +1,4 @@
 from .Algorithm import Algorithm
 from .PCA import PCA
 from .LDA import LDA
+from .BIC import BIC
diff --git a/bob/bio/base/config/algorithm/bic.py b/bob/bio/base/config/algorithm/bic.py
new file mode 100644
index 00000000..cdfda84a
--- /dev/null
+++ b/bob/bio/base/config/algorithm/bic.py
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+
+import bob.bio.base
+import numpy
+
+algorithm = bob.bio.base.algorithm.BIC(
+  # Distance measure to compare two features in input space
+  comparison_function = numpy.subtract,
+  # Limit the number of training pairs
+  maximum_training_pair_count = 10000,
+  # Dimensions of intrapersonal and extrapersonal subspaces
+  subspace_dimensions = (30, 30)
+)
diff --git a/bob/bio/base/test/data/bic_enroller.hdf5 b/bob/bio/base/test/data/bic_enroller.hdf5
new file mode 100644
index 0000000000000000000000000000000000000000..3dada5a66ce8d314d52d457ed812e856c06a87c5
GIT binary patch
literal 27664
zcmeFY2UJzfvM@+gP?01^Qj#J_6eKBH8;K%H5>QY~fM7(Fs3J*%AW3r0IURDYO%_B2
z0TmR%EGPnkpdvEo-cQ%M@7;O#t$FL8f8Lv!eb(C5ySq;B?&|95N;^<jQ=4TK-zu8F
zT}(_g3^Z(iFaEhL|GwDH{Z;el`A_@t-v!#=FyJ@LrTeQuOY^rM4GquldiLM_|9iZH
z##&l5G;}n7`u_ue|BXhD;jhx6-xVbHUj>yv2>(C)e>DYkwbTv&Ho@b+87=qsY)jAl
z-#cwuniYTN-@k4&RPYxk)6)JmkUxC?2h!7s{~eWw%Kv|>V`KUYQa2Zk?TK@jZK!ou
z9G(6K{@%byt^XSy9Ua}@yI`XB|F;4??Q!Zq|91Vo`mZAO&Oych+x55FwQKZDX=v>J
z{`|k|W^%wl^KUfy^VTrVfBojz-_5D^{~9mgcXKJ%zs7U@)8D#(jrZ3o(a^Xu{%gEL
zzneWd{x#nJ>el~^ukQYS;U7r<n=jaf;5P>lG}~9xY+t7T3-LeZqZ3SjVGt|+aSs2?
zM}H{$k68cv%KsTX{w(7k_+O_-+S<QnCH`+F_uq{k|7LLigCA)3=e56|X%Zub&p$Wv
zv3r5sG`4nrHi87-IlG<HHjBtCZ^>)Cm6vGMnUNTuWhUj5!g-jd#7T}(y$-fqeiBdX
zzjH~IBR0$2HanWYNY<QjIDM7@v99*y4;yJILh0N<SD(8A(Nmx_ZQd(Plq3KVv5$+`
zVU<YVWFkx$%k1tHdnrMRRxSr7SV)s~YdGldYV0O=S}+%jdTk?4NRC|jenFauP!-Gd
z7~M>yp834y-V+{jSlIjC+yOcA`p0j&{a%6uWnn_=f#McoMfk)WuVN0u+(tsZ_LczA
zxXJ!R)|eQnt6kmo^~Xli(Rx7e5$6^%p-fCcY^^vk-fmUBF@8O9)wq*G)me;u`qlrT
zN!5CyXT9Cycaj>!iNf8y9FrpC(O*|w7XuWCD~VE78;-3al!n)NU){h@hTi+6!4$fY
zxHhFFwLM>vj9*JJOWL%85FWghf53)|@HuVfpQFo93T^Au?2h9i;Em*-)6(0?#{I;Z
z?|MR{R%q#-Es+X@lJ6zA9SS_;p;5L4Zd+->OTK*I{Kho|N4H;0u89<J=zZPE<yHwI
zX-IS1#VtyNnb-kYY+1yF+a7w-YSM&l>IU6g{9@!I9p`sT6GG%Gd&hY3d3I87H%E`2
zoCvAB@~mV=j6CuDXts%7t2|M5{Q1Z0m$?ab=Eqx(UKAr0jKyvaifkff9xPj{_R$f-
z8agEsqXNWs=~MKX-RsGjuV=DZYks2C;PSz@8#WQkf(!$SA5@6Mz?h#mVmFi8OI_m^
zhD8W|IiZMCnhXS;y49Cog>u9vfu;wypbul%Yx?94t|uSnn9DAE&!cbPkBs`tZAATc
znKy@gHj>-@9b==4MTol>4_-gHO@egd{*~P`Pe)R$Gi!ZbZXhSWzuG4*sX|_6IdoI{
z2oHHDNNM%Y2tLw?USh!ImINXElu77~-X1b>G2t?VZ6nUzIG|0R&rJ?bm6g3xkR#vh
zV|&{r%R$&&=;}4K6(-a)Gn8Vi31YxNE{v{-jU1VFirhOXPs*rpmhOt&N%rjc&Ir6>
z#3_+!4x>_GqIk21gTRn1VZ9_B^V7Q*w23SHI{xg$se}as^WL9uye~kj%$$`lY4d#*
zXs191KjK^CyS$Yc72!W$VM$BY?>cCEnehoeP48bgX(&&e>pOWROkIQ&Ra|?OBTkwu
z@2QxI|0+u4J-g-@Hok+%P}`-#R3b{+oZl$)Q%99pe`{mHqK^RS>3Y_{EMhe=ZuaVE
z7u!~nWxjiMGI$-4ZT9WuC6P5m34hY{<zg}7iE;()n;AKR>F~GA5T->q^F{ikee@>c
zwz%T{%M3yULvgoCjxaBA^ii3nHrqNvyZqRs$8ApXR?6H46H^jZ?tASL6Xqo)UPo@Z
z&A*X|c`sj>t}0Dx>kD?5cC(XJ`9=#q<D6uU!#ZZXDok43chGum%SO<=aa;cJX$x`t
zgQF&=hz!9RROj(5kAXDaKewcQP=a_TB%YXJwShEvctCcC$S+7cWc(x}PLB9Nv+aeo
z!!+coe|!*eb|bN0R?Ta#>}q0#2w$LK(<`|9c_C+`DK9zEKXbx<l$#u>k~_D`X9E!$
zBc&l6JdPi3`#<3$))NPN81}Zz&f{y;-F$jni5%PW$Z3PN6nU&(;@sOhHIjA2Zu1Ua
zNuoY>;Du1yN+LMLVcY0QQPNJaPdM@VDiVCj9@-b&WaK`ES&QN&@Vs8U_A~uva#HD`
z#YCb6(Qvjf|Fzs^;<>M{aIn}G!g^!K*3vbb$ZsbbU;SEo3Ga?L?;On#An(3Em8<lX
zmW=P3d$-5388=y80Q!A0WS+dCKbP1VGWN;YO2-Gh<P-=mdAJCY3uFax(`_xWpQZle
zPiHnF>AZxp_uzJN?UqH4_vb{2RiA_(PYnqZM-P<kQ~bq8&YQ_|=&w;Cj-Tfe+(joq
z+A1%yD{bN?=+@O0*K%wkG*T`cw%#C3sBf6Mn{r_t(fqdP<xyF2GEG+bovGPYLV90s
zkK+?@VljckMnQZ7;o8zU{cFE4vEyof&_VXiq|Yd)<Foy21o<dtK4b+q`LKoc;mSZB
zGE!ae`Z3A|V#EDBr#Wp=LUtb{+w0B~AM`yrgFNR5$El=slLNDawATmO&mX@K;ccGC
znf`^KS*5Prv`+02(J7ol23xICw-hZNE?Ysb-V-F2d)$gvy)P>!_tCLMLJKW@tyljb
zuBD4f;(-OivFw0~R^~LpKJHdO{JfL+AAH#UHyrwZrq}(kZvMdkb@o%&Z$0l%&wml;
zpY=Su|E*N}XL|glhtmFK{?Xk1J(@py?!SNjuj{?Xf15;v;6G0IzaKsR8s>j&U;kwv
z|6jL*x_|Qn(?3rC|HPjD?`;SD;fFu)zs?V-%6}2!f3{t?^7m@~f&X=SJo?QaD*way
zL;UaK{rB?Y4?X_y$)D%{I(^1={Y4m3@*fHG|Fr!0%OAx+qx*YM4($Ipx_>VJMJ*bd
zfPdo+`DZ+)-&I1t{VV^B@mI|sZaMyM-RA!h`v2$Z+l9j^nkr==V0vp;^~`N(w0V+e
z{glLjUv4tyUjxwmB(vH@yHt=?>wGu-v<W4TH}kY;RKvSm$p;s#lkuT4zZ8dGCYGmc
zlo43UL>Xgo(-}$vaQZbrI&Dk>i~S5CFxiAG)^o4StLuTWKcP6AHx)U#-jO-qit(EW
z8{;n8+gP`4BSV2$Go~7s-|`Kw#q)UEe$Dq{z=sv-jE3cKu78b0u0bUZ7LEIg`lQ0#
z-Z{f`?z6~!Xsr2Ac?+zozxB(0s1yezN8X(}S^`O*=6FBsD8aV(S0~MAQ}Ckh8uoh4
z7SO%8@z#NeOth8Sq;8e&j-E5(E?dk~pq2h|-j92kxW{AsB)?T1c!fzv`;#TeW+t%8
z-j&4Bj*C3e=8-5ZQ)Kt)3qt-UC0k8JEm-`#?pt?89EfF9a6Qv0!ytw!>G<6ga5FYF
zy{ixhpN|x6+J7kv1!`vwSZz$h+@m~chW!c1;-%$hxtI@a#^;Xu*rcIyK6!efoJ1Z@
zgWERzRUk8LNq6LGDa`Ep#P8yq1_thtIxoM+V!p)L_O|j&ICSUvqmWN!*v=kq^W?z=
zoQr4<>swia-%QKiXE|2F?3JEQ*{Cv13F_>PSw?hsrVp0*a1%~=<XPF1*(m)uPSnY#
z6dzbg>u8LnVz|TO?02_oAxtN0WCwp3KF!&xw?`rj*t=|Z?}l<{n=$_M{7fa}=tRMV
z9hEr#_Edu@O*7tD?=SbRI0u+EXdYjZbA|(lH$SpELqdPwSnz?AM$GP__cFVZhlnz1
zLsdn1!FdJOU~L-SR5P-(QH}(C?h;X)42PHF^k3=tqd{Y;_oPu<3sfDP&+Zs%f&lHf
z^Jk7^qDa`f?w+T2z|r$V6)}_wS8l6ScG0)M2i}}du1xvZHIip%q(_0~V_PYehnrxN
z+=sKY-@QRv-%`rIy$M!5UvT_zp2V5U`6nDzYQZmT`;7JX6ns5fku$xk424-MH?QBF
z239}bNOydz$IPN{-iHongJ`MsF($bHRDIoCXt!Jn_I~CASu~0GBR96EW+@8NDCcEf
z$fskm<A*G!+wquJRdOkct{flil45%M+yOtbFgk6|EI@o7=-yRS0!|ed*GUJoVQLE}
z(@n8FIFhT8ou*$6ccvCp9v79udWTEbl7v!0tR$*Pk<}k23Xh+z8_R$*J6k+QkF)@}
zBA%g4=L7^6(>UeYw_zFU+Rh;1NI13Ysw;C^0p7cCA^N^}9<sSjIPv6E;3`%2p23MS
z$e1|H>sb{9iuG)%vb2Rztu5sC=6yUIsmqJm7f}sw8#{h=1f_$<fj1YOm%~t``T6nK
zx0$fZ;|BeGp;kPyadj5u6a_yTUe_XiW`o~X=CR_DN?dEH(-7^S1R>_%S-bn{;OQHD
zv|kW$$Hx|vjHm4wb7r%2jan(n^JcbHNtl5ZxGW?tWT712&zbS?7+gL;yL8w)1DMMT
zmi51TL4y5$-e~y@<ScVJ7cmltB45_L%CL=tsyTU<4I>rkAZF#*VNnC7KfSn%{PJP8
zN6WBsp%Odxc0Mobi9zK}^ZErNL1@V0V02~Gc`WbU7@vITBuYJ-%j_^|!bZ3L5JA5x
z9Qx$v@3T}4k^&3de#bK4MBcuO2U-h3EQ)@OO>P-9`^aQB?ybgThv;95v`(PKT9~PL
zJ_&_fe~Dpn6+HTM^3n1d>V9VDWj8%linaGg9myNHFp;uxH+_FFIzBOC<2#%WUdoFe
zz9Rl0eRcIK#*`M&e_t49@U056nSSc0n8l*NYu2fgPF27vGD=}c62a#;w{ySvRSoH}
z(=WfBYQcr08aL&A6X7b~?BzRY)%fB3Y7TCTTx^kHTph=e3te32o4N`p=$Tl-K`TYV
zzJC3y&V6aX<{7qoLew1u`b#Vxf6IqAU+r}dg;v2d<8w{nll6!}fv4nS8=$LkfAD-o
zH0<v0d38Ua3RK`r$SblORQpyhJpYl2r!BiG`#kI6^tuWg&eCj%xUkQ*hcOd=coO#+
zdz-*JKvee9mvHoBaV=Zfn2&GfQhlz=mSN3jfj)=YB7C%@zpCD_0MzNSh8Vn3v0}xW
zxD352H0DnqZ(m!3uGbq@?B<C8HIeZr77hMTqJ7YMdwdqgyxx6w;jsY(-#T%%baOnm
zoHh&fNvgx*0c$f??>lH=b?2Dl`a)z?oxLaQk%W#x%-8Nd?!b*Twso5K8sH#h3WrSV
zfXl@D3)gyIh%#&lD?1qtr=o6Z5;3`;xfGb>wLca5UnlPFk-UXU+Znmy8aj~f{@o}p
zl}J$3{2pUA&;-T%KkqoZ+LtQd55Fi?zKo}qJG|@p{lPAj;aN&^3@XY-KU$fRj%mvr
zJ~ppv@XNTKyUt1qgxYs;cW7k5h3C%a=GDs4>OK#>*N7XO3fOW*?3*n*7QY;O6<>%#
zo$qHQ+B5LZQQvyg)nT|JtbMV~CKl$lpZwabmIX51FLsvEox+0njJ{U$WS~`<XtYdq
zK*rXgO;74-(Ywvncd|7RpWUbQXGvD$`-woCd8sT|5pI{zDqoK6HET8FY#Px$wQFH*
zTr&jl8d!#3D}eyJZq0zNR5^W3oIcb#9s=e$d-k01MHLH|hi*<qpteK?_Gkq{9{uR>
z6m2uqws~kOJjp_xaPRAytRyhFEc)!tj>lN-s?)zN)x-Vm=jgb+8nE+aseEsoBkHzj
zhE9Yy;(I;T4wd>c7}7DWJ(f}imm6HTjc<6v!79C?(4Uup<E7`*SeX>e5&W<{{gekV
zFAU`9_EA8fp6<eFG8R58eYSgbC?A|h9-Q%dk%<H3;!&$l6%eO)Rpqd6F24Wv(&YMB
z9^{@=yQY?vfJ`)n8$YE~!nQywp=72~+)vEwUysT`Gg*y0z3&lP%j6Pv?m;*a{^d>P
znoQ(Y$>D#zyBvM2etgLB&&8(&_F->wi(t9bP)zPbH9iz?_<rUv1+Ml+gb!W|hXa+e
z!ON6-&}<sEE&5Od2`(-5@9aIH_r?03S56{K*q{Bb%Myj3HdJrhZfpn7kFGtp@qIe#
zie=v|{OpczR5Ul6Zx4d3c+T~%Rf*^kayV&ZC<9#LS2D?|Rf4tS9Jle#G<>sbh&e}r
zg1SKqfsdp~l<)rfFe>;G-uv2NsKV2TLY_g#R2D)Y=IUXgcT<H(lh@6yx8w=?ngmSF
zrRIU!GM8|7T@D6)lVg^8>I=?#-<IOfl%s<i!^O+tzWB~McRVI7AGwPnRGtPj!k2`J
zEVsI9FerO0V9}P3isIMPF73GkrYc)RlN{?&v}wcELiQ>&q_3?c&gMeY%2R2)E|qXB
zThMcDN+QOtOFem<?-btuSiR|1QU;VIGcwW8C!y)fRp*N9NVI%+{J6ea9?Cwvdbz5!
z28Yh@>#DIeV6=^uMNNAKwEnF1$R{Xx<L%wVZEnXfY_F1-q-z?=Gj@3HyGGrwcHA{C
z?jh*e5nr37S&Fyb4zK54osY{F*@kb8%D~H^>sys&2-rns53A&6;)l~q<1Ho#R$qcS
z`Xfo`O`f=IDHMxxlB<SQf+|7mwEwU7>k47va@QKx^h?mCP&>A~n1v2&u)3h)3LHLI
zZ={}Bfju6S<SDac93LEDT)Vv#jCpfBj#f3JPvwYRdJhFO+GW^3vL-_#L#)$2(Ezyk
z!rZlAqztb9aPKq?jDQ-~BTHtkff&N{Ys~(33>d!*Z@n2xLh7^#k8VgbBm~rd-7Ow~
zK6?s$Xx&oa>t3!*(VGQmxZQ0JS5OcZm$ckt(W(U-?}=SwZ&JbP&ZC<N#T4LKRHn@O
zMxjka&g<jHOQ2q@ghs+32v~#W0}a=C;k4B7e)bbhn9%Xa!7!y9<QJx74XjG>ySSBz
zBgQ~z#)G<@PfKCPGZW^|1Ig$@k?mWzE))wt7fXbcg@eA&6VJ@V0&JxHWLn2jf;^%_
zQ$EAjV1z%y#8e{|m&4q~<}PHTKhs`a%NuE^YY^~sqPiZly_Gi|q`L%0ZYE>aLs^*e
zY?I(|y#^#`8e&I}w?gqSzkW+Ki3tji7(Ry-!db@6G1q@(;S<`$64tv7n9;LRmYyaO
zP6ti5O&+U6LO1EnW+5lMI(pCfYilOle0Tepxi-RUr|Zly*UOPPd68*@Xb1F%38!9p
znS)VLX{o+uX(-)xmOgPI1by^YPpAo{BF_PufVRpgxV!OHo)S$ZmQCBuIq((XkHeID
z-_Id<UDRCPgtH2mHqRTTU!%b4oj+{lrE{^PdCn@<FbDT>eBFO7#SK@aXC+pRRie_x
zTZGwAJ?{VZI8I;N9YXHxF6gnU#}f9Vm(z+#P}^|ZNKG&d%67B8{9c;}cb%Y4LZKQm
zzOR@xs3}2ap~?Hw0)eP?>yX=a<3t>bDO72%y@jfye7^moHE{C1`th@`D$zvwtmR3o
z2CR8*tDBHtjy<{dvnX{N)E7^$SZ8`0h~&*oPcJlKFCoa~cRm|G?^@or&~Or0sD}uY
zu1<rKnx}h@zD>o*uq)qt4TDfs?V-Qu9TIAvcy(N#zXs0~Lk}e4d9YWZJM{V89q692
z;ars+0t@o$+fF@gz?l)*%9&>=aP`KPpK$*WT-nh7ASyErYe}QKtPhPrUoP12vq%H<
z(RF1tOhtoK`%!Vno6%r)GvIufWfhoxw8=8Kj>v1)-FlJXFl^os;<$OP1|9QO`o+3N
z;;hE$uY&<K5MOpC>U3r{py|E&c^_Zw9Dl#CPBaCJq@tfbj0wekG?b|IP7Ppvjcxr@
zP$0Z_`MljQH6EKcUf3BH9|Grdr@|%}Nict>`K!>Fg2%2Mi02#&z{t6Q+0A;@z`l8l
zf$7;oyeb%Z`9j<cT&xW=HOP&Gw3T1oz^V)!Xsq)t#T7%3S^FC9qj@-)Ub*qh$4ul~
zsg}%WSdVmzr*6D(c0kL03Ce!)RUmd?SJb-`-gr2}?WyLL3Y3~k37eRR#oP?r{tt70
zp!{>$+9b>c`nJSeZ>>thsd}kAt)K>+_q@1oU3?3URt;`d49NqYTg*Nc5ze4v94~iR
zpb^~j$tfj{EQko*yKc2u6Ihv04B<)*etaxkw^8{f?r3~Gvp1>^#CN%}h*m^ln=S3r
z8<QlSe0e#3%avUC8T6j=I4&0}m@llH7EHxuHOm8=srs5uLQvb@o&YMnf2=MEY{Hsv
z8N|}VELa+=$=J2xHl!^d^6u2RjIZQ(vnVuW!&~L(q8^1}^i)1_A#$k{@7i%iYURhk
zbjphZG+vo_#4T_lc55Iq-}n)}<EQ~@Ub}Wq`5fZjuQZy@jFoUkrrn(DR2-~0cgSFI
zx(EypNj#elZ-m#CqbVHKE%-=D=}{5Y?u~gB`|<+2FOCRK&-AQMMz*h>Ci?F(@Tk~I
zw_hyjpyV8STJ8X~-)+-G%i)_?QFxE<XHphUHGHud{^^5s&m*HtO3%SCqo0O5O)Z9Y
ze>CfNX+nP+TH4#LF&Ou<NP(HX5_NwqF`w?djhk)+?9h{ohF~t4ZoTd_R7$=xuzynt
zY`oz*!owbqU#Dt%Vr=u!yKd#}0Kqum^Uc)t{@9Fk^NHOweWjQ-Mt|#OQYR!j8{~<(
zpFzr(=L#S9lwx@I=A<icZ$Vju3Ww}mE{titla|-51*zPPiMi5c*f1sTUdTkkJ9dkq
zIQ?q8y7=IQ&c<lCyEmwIg=_|V*jYa{ARh)VMwQDH_*#%QRdDo1cNNs@bXb-el!L&H
zpJJ{1>+na?&BLl&n!xMQNnyu@B9vXNzFYlcHG25dS-i83$9Rb+oXYQR<Cmn~9LKo~
z>>$foTe)vzB6^yLgx|q9TI0;`shxO6aC2wWU_R;x=E+K|rogciw-0q~@kaZ)I9WdB
zENo?))=n-<1MaPDY%^3pgp<zKt((dzur!0;AEl?Eb<M3+uaAjgt4zzu?R~j$FZQ0S
z&i7kjRIqDA-lzr~tp`^*aovF<T#3=TPs;Htaag8dFdYO?^XTZ^OjujC(M!u97x+F-
ziJv%K0a_!{E?$Z~KoQ~ABpqropkmd-Q_ox>e8Gl)Q&I&Ct6ApzdB%gMheC>$35nY4
zOeQ!tl)<pb=U=+>x4|dw2!GUu9BiqcAB#_{hef&~*_9{!V4}wNw3VbE>>bN`ci*cH
z_B4NwQhlC;t2d3QS6`!`<0G$M>IcdoT4Fjyk}N~VakB*`CUu~XXDo6#(1@eVd)N&s
zGBNe5c=eiJ7r<Zrv6ZKE6{Z~a5y)t)hF+WC-8V#=aLFTq+vIXN_7^xgzwRzTzZq6h
z@0fIWls05!L|r$P+~!DA$1*U{79S%9(?Id%XJzcA;KAK1E#psR;?)-ttc`T()bq$t
z^e1&3yt%uQOs}P)eDbK@aDp3XiD+1VjBCOiuI2M8lC4y|y4Wo`qm-I#_Jk+>KrXJ^
z+7WH;N7c)vZzUN1DuGk0g?Y9FkyxVqt<7Md8q5c1JFjmj#;)V%Rw-9i0N#1pn4DY(
zD~6m@YgWf0fAi&62Rt+2%hYUrUQZmDzxEJPQq9B6(mksiSqi}Jwe%Cqd&=nagfYzL
zVKtUb*>Us5#sd4=^PzzU0`ZPLQ+I)b4{R+Zj|?$nVdf8RAEvD?kl!dYepfXc&N3}4
z7D`d=*6CSkF0MAXy=QnZY^V;J{XCv>YLtP}?Xtqp!#NOizr)$QAQ(Ei+tMPJ;xIyx
zZt_Q&4?1d<Fv;DI#QybM!&xdh@NwX~-cjLf5V@t`qLS+jYYtV?$aRHdX-yx;lQU@`
z8yE9MYP&a_-JEyBo`ov+D?+m<LnZht{s@sdK()sO?lx_>5QHThgxW)@{uIP}>}Ftv
zBltb`_wOQ4W4y)Ek4w@)&^m9?DJnuiv*gGhLYvY-B_i%_$M7vEics&-jqXDK-GgDg
zdRefQk6Xo`H4?2gmiCMbodK4vm8QzF1rXP{Axg@r9m``Lj_Jn7z|)jZrvgSJ;1TWY
zhkE@6lxy4h)OlwHin6|BG`!OcJ^X2uU2bU*BRKQs`glIt%6stM62FAwDfD8kBH>uT
zv%pKyZ-X~Mp^ueKsq#NkV*j45G}Jp7uHvl_4W><*HRoSf0?n}t8fTp9aQBg0w<-5(
zFe;?<+pW9tC>3&@anPFrN^I!~^iKAW@X^GfxFQk)RJLp?n9RWNk6&K(susepwb~iG
zMhd{E-J)Atp#X1XSK?+$2577joF?B^<6174TK`=c`1Zu?&WEoWK+5>U8Xt!|WC^20
z?$nG#m7Ve>J7@Ee{`S-Jh5J(RiIRn)jA09sdfVYqX&LS<x93w5Oox#ef8qRdRj>{U
z@+LBhz+82TJ$1SYj~jWPIn|#AM){0CHdFWc`nQp`JHAtZ^-+}2hhx?F)q$&&C9eWx
zt^4!_bsO-+p<@wu9~B|rwy5(wccbuCf4gS+cr$Xdf8MdzI|RCu8A_Cw12DJbzP<)u
zDsIv{s8sN@6!{xi8jrZPqSw+=S?zm6+_U_n<*HT(tTK+#9I|hR3V-7G$7}geedgLO
zfooOB4efFUTyfwbK!3U9ax4V(I^B@GT!h}nO)^gnaxv80T7FWo1(g}i&nwC&z=9Qv
zU;e{j2oz;FWJePV1pS4am8n!cCDmPhhb9R}?B|W7n0@fYt;KCuinHKdS55@CR~nps
z<!Wr*O7-K|r7MbbgyEzUbISV<VW9d@Ro>FH8;vJ&nX}bSK$C6tmsj6X;qd7&?H1u`
zki1VbC(~Pljn_GQRX0^*!@G~WX>4qucjs5*gLXyWI_Q+DFHwLx^AS@k10%5S!%7*2
zWEW7G3lDzhn*hAKq#4e=xC7rP51G1Mn}Ox0(*4Uv+rcBaJJgx02DTmMJl@t-0bkD=
zJm2_$D(`et3P#-#)OVZD<Zn$!t>M|BjRHB?!BgR?I#7)EHF9sZaQdQ&@4{Q_51yER
z@40h9WHByteBV&KrxKo~r)S$L<O1zM)8wLc(U`;ND69^F;81pVeI<3A`Jc~fcc>@f
z&JmUqDO0Ji(#yX29CIbmSJNF%-rI`DXzo5@i6HT+L7CGW_1sLg9kKLh%SD|}4i@u9
zx$yjnq5=$;VPfu#poLlz?lzL_H#_Qr>6;#`6WLt}%p#?1sVl=k@vDrkP*FBcmgmfl
zQ1$(z1_JFT-Xy}_b~n*a0y!XJ*~(gZy8%amqRz}4h0bY$d)ayN;JV8-HIq$c&=M)V
z`Qzq7JmA^3HYmFdG)%r~<P5OE^oP4;QQg&`G~7rt9~1`rpLGNTwFe{1;cU7xvKns;
z(8Z~%Wa3Q3eOr012z<AJ$s=}q1tdo_Dqmeyh;kpu>~nkbsS42do7=T&@r|(5d$q_y
zJcRO6RnNlEqs!pnEv-1XP<`U5no<(Hwh87vL-jWv+SV!{re24Eks1=7L(%YL+%d`3
zpMr;XYNZ&s*TD5>PW_udQ84LDo!E|KJ5)2BHW<^T;O$pi1|2h~dV%2J`cAo2<npS!
zqdbrVl+s^c(`16dh^+3M9i*VVi`%N1)pb;Vj@0aj*CBYyr+w3Tn^N3S7_efz#|dsm
z3%N+S$3V$rf2P=hSa4V^s6KQf7Aj72rEQ9Hg3b#&Uutcp`p<gw9bETTfT!7L;+GFD
z5Ebik^rl7)#{Tfh*Zfe2l+*SDS>v)GeB|dv@9A2w{<iVS)$~GC-MDT}Y(55Mp4<?+
zW_A~I2PsXn4=Ul>)_0Peo6A7qO7{T8B?}&;Rg0fy4~J*3*+Q<@`C{$8oh)aJNc`|z
zIcqSu8h*~W4{fH#w>abHX)OXXk>7<~Gsm_J0v_*gdp%YSeD@<se4GroC*Ca|=L!Px
zj`z2>T}(nBt&_KO9Gap0jHu0&A;JgQOiBBh+o(^w)jTff7%~k#n~1KY%Imt&Rflxb
z@t52xTUPZn&}AImxic#U<?62|O|44DQ-dBqc|2OtL_7S3&87^Psq1Rv5iN&(iu*rj
zW#oZI%bnxId=>T^-RN6dETZbYduv4}seV<_0(w(zs=XA$t@(a`7*2cZx&54vhtPSK
zzzLfoJa=5DHesp^4=Z;B?0inatfhxFd)=ZzG3&mlGj-j~uF0?TOf5%MpEO<VlvD(#
zk{9vamEfH+y|a8F73UPqldf@Pg5CTVzaKB^P&9U@*~REqd>MUOq4HBZI=L>}AA8gU
zf{JD<zh6!PBi>Ie4O9o1$jz<Vm1U)nEn$!HQ8qvyzCB+v@ir>2n$~z(5{ewmuKD&e
z<q&!5+K+q2S@1RId)m^oa5#G5=XqEE8aQ<L<HpNd%D{4~_xtbbD&WR8#!vhEFM?%K
zp8&Z(5$>DTs80n&;(NlzX63sU5HA-s5bvUZrQb2p&g3{CQdpj9<|jdqmrhW5QxUeh
z<H=FIG7LOxDDU0UhEC_{M^~Q@!*waz(3F;kBikY}Ez-+Sq9pi9U}qLKZOVQjq|yjG
z!2DqGTsb(;D38C?ufR@0pXr@MEatfFJ+Xg#A@)8X15Q|%;<qlR#}T8xC^WvhyKH48
zMjZ6C54suxirsFf&L7?fIrsR6*@p{2SI_QR-ScLACK0#r#?S<`9#pSozgrA<`ISN>
z48w43{z7fq$_T8HIw!Kiss@$U+b1?oQu)b*cdhT0bW{`f34I|}iaD~=y(=js*jCcI
z(u5X(f0SPbXH_&j|Kx9;^(F+26IenivHoDTgXM9iMm(H!FMFwSyACf&atRszs)4#h
zj*-LE_r5dgNY_462u3!hYaiPr;R7-5prc28z##U{X91^LtRG~rdKy^>pHx(flS_**
zvd4d=&aP$*y0726qtgK&i?E4BWVlf6wTMp%?_=QPd2?TNp90*fSab4xT`AbE+^6fb
zr2z!XjuSIN-jM$K+Hj&}1#JA;I&k;;Z8S9AbdT9I9Nu+i@h?R;qkh%$2>IqVwz)Ni
z<gY5g0ge*d`kr*Km%R9F6@M1Cn~<^VE|g)y);)Z)bCn=-`Y^N3qCeD_Rf>5C1;aWH
zTJw(zVR$R;i~rmFP-HmEV_sWC<s&1DQ#4moQSE_Q-3qHpU}|a{(b`=PHTR6(Z<B0B
zjkGaAg2=&0kuO}9ab2L@|D-zll?yPMdF(i}E)^*ZMrOMXUc;*OJi=x|)Ob{M152Jx
zIqvs%WVmtH8{^B*?%?*)CU;5t1$Wn~kYmOVZ`AzYAUS6E4)Mkd6O7WGLrSkyNnH({
z7}pw2;#1!fqbfdWV(BcLW!WiDY#T^yu0Oes*sl0ZQ;Q(T>yIZiZ2DFZDt+7P4=_lO
zGddTHY@0R7pJ&tyYUUM5#q(+B_N@~pcPSV32dxn%4gB0yv--9X96#4_UOXm48vLm6
zf3#7B3|4e(>UB^gX}j#_pFdJ3&Ev&=J*v5h`KYPIIssKesUk%2;9g$x<<_w*tsko7
zI7iO9T54J}*(=g^XLJtsrd2)CO;#mzb%Qyi_VW<K%d$J$7y7Z?clZ3p&)OvW*jp>k
zF-=m-UtvePjwtEZ`PPJONP^s^vgKl#tQ0xmaeCsxU3uc6`;WYb!~%?n9+YA-)+F?6
z&*{Bq;vy~)jD)OLDcVkM-Y`P)5I&W>5?|NsCERV3Un?r8lg8biMmJ>%;)&$$&w-#w
zJdRMwYPqLQ%x{!m&cCNXE*?CRS97ur%Q<Md6c6krYS^8<Y7^>^|B=HDQxSr6j2PnN
z9}_3C&%HUqYAHm%m5$b9dAy7GxvlBM(RM*{mAI@X3M&zB?D)F{PYaN=p2nlc2Na0x
z3wFUPeEEpp%d51W&nplIqr+?U<+X`x=e)la&Z?3UDtDpnm>l``gsK7)n<C+?Xutl<
zJxOwj>J}_2p(AcgjJVnE*CBCQ;`aR!6{6;z_6NyN1bKg?)S|4GpA65ub9mpGZN!_h
zzC8X|UGn93hKK$u=TPC({_TsZ+X(B*dp$o+Z6zIN3O;lmRVUQU%747+T}Sfs@!syf
zwuQhiKTLi-mM2FZIc#0os7&M;>d<k>zee%I!00`Xxyf^xMLYqM%H-SQqs?CwHj`B%
z_a{6(<jA&-TX*)J+d}FkuD@mEE=DFZgbW_IxSbHU8|Ur$NlzyE`Lzv2DUuUksvh*E
zYm-IBS_x6ZTEqYiw~o^b9^#g!^F5|4HDdq06SXd1hae(&7t`01iX`L2WZ+<}A~{bK
zvFx!>C-k$Kjx5&iA(VRk)_#a5$Tb#9Dc23QlR3SnAH+{95bo9?;xevM<hIu#{V#pj
z5UJCh61*R`6XEYY^}>YoN&Bkj)xQq%krMjfyB<{nIbE#O-K!u+B#Ni>^}pUsbQIh?
zOJ=JOqgP}>rkWrhT)U>kJ*z|p1d>`Yj6291O$T1o?pGz5SA^*m&g>@E9hq5>*(OfT
zkM(6fWF3Oc4|$!}Jyc1WiN`Od1Q`hPYLTaJ?(Zke9vKuLTvQ=vg=eUT?K<*)u~JOU
zv0da+JMWZ{?H*!QsUniCK!wD%n1`2x2a(U^p@iL=X%uVUEBrW4p1cxYQ|K=%PZsR^
z%(KgL8!=qcv0}(Zn7ll-deN<1n*0`~*7n2?2#JyF_x;Yw5@o}k`3JmqlA}{zd~Vcq
ze`hy2*()^K<V#v#<F1NT<eo33t)3`PW{P~pLI)LsrFj^8-FB0NB%!Ldor7o$y(!3v
zLS($*cwiaJ1~T*P%N;`N#0X{Y<xe@8YGi(zXoNG05mvu$ytHqWBY2)|ka;`JN%RY@
zi>lwvMOa=6-<L}xPxu6<LyiqC@qDY>$8B4;6K}*6Mpe@l$$;JRNe(x6lYDO{3ExgJ
zLP3ZRYp7}Y8%h|m#tL;v@hGp*flNKJF#pr@ozv9)Khm;g=HVuyzj31|uQe^Xw&7mr
zn!DnJc7*Qt*p!__$f{V?>v1e3t>TwK)=zSz-`VYfPcIYXux-<0Okg8D1EjwXy%Hvw
zf&=&N7THdA4f$++^I;1yt~{5<5XM5v#9QXS-K#?6Up_|{9|c7ClOU7)n@VI(?dbP|
zg>vNZ>h+}``&SUByEFH>FYY0hSq_A(mgFX985)|u+3qE)4gBcMpD2?wM#Voz*mn@y
zKkNQ7&k!alY+`AXatcIoj=-IvZyE&6r_vz{4H?3xbl5@ugb<<h$UJZOy$Vt5#2FNH
zRFgQpts%nl4jV}$wAwJ8Zyfs`59Z!V79vto)<4@Swv7zD7w5M0X+3G|_nOg)6d`;g
zz4k|})+D^zeWyA~<%slJ8#zA)4N|L_!RhO60n+>2^OlXzBcWFJ<ub>Ya%`6SSU6A=
zjCFcu!A+fIXbdwe;~6i&w#EMar)Vfx9yIpQtUV3xlpl50q$5GI>V2B$oeYpRAHCqO
zkp<C)Vn3gF+JlbOi5K41rC@zvrae?F9Yy{7!nA61@#WMJcbSz{IAK<kDW=ti+a_Ws
z?plW6j$)~7W#3{bcWH9vqMmP4@3lL6nk!IgYTY8IL_HLo9<l3rTm@HlTF_?n8-lp^
z3+J=hm2idWSMK7?3XnRnuzJav0(;_W2Q%xk;m0QSE>_(DFm-rA=Dcpf{=Ak$Kl9t*
z0-vniI;uZna@PrqqI4e=taZi%bp=54<e>l6gXQR^wQVdr?Gnb^Z{>a<mjeUD+CFHy
z1JgcXmwVJ3VL@`W-`bQiv=Gwysij*33N;ySdsMt3=UG|>!?h-8zPR*E1q*QvPsB&c
zvsS!+y~0syTLyyK-na+$)chfzwbl!-Qo)ErA*L|96b}#EwFPZS2Ai(Qi{pzqRDC3N
zcrmsXH|LHhy_d>CQPH2A>9<cHjcV+2KwUb%kF?iiPbme7svXKHJ;iutQ^s?bZ63fo
zQF7t!saojEN!!WV7X@m3>G5TP#jumzkB^=jN1~bFV!Kt>h?CqNAGS=D;`LR_YfN8P
zfa$pVhW)+~;9_^BG-q`>aK7K&$+e>eLoempW@qH$gW~&V1>K7=w_KDar3!J(L|(f*
zwH}4F&5A5K^I?Kd$1qf+1Qnkp7c9QYf|&tV&qwDH@x$J=JT-ESc*d5w-#RG+DIOoW
z$xk6r;<!J|N-`f$Bphn;?W#a;_dOFUTJrF|Yhd^z5yXd<p<<GDS8yAX>v(HwBL>8r
zIDM?40F$My?+FRFAiax7-NC|YaEN6K(ans66Ag_U_V$;e^>Ic6i!E`eI&BkY(^n5V
z0YS@OSPDU^`Rccaqj}hyaydh0I334l`SO?a3b4|(!!(Sm9)E6-uaD&K#rh$(P*xEV
zOI7^Vp4^a*K5IAM;yGW7&1Q?U-<b<AS59)SW4R1ohzI)QpDshI9pAXSITE1glkP&K
zY6;qXJ?OXFvIvvuuhFJ{Zp1<sEh!Od-2WS%ar1J^0?{x7wapRK{F_qA98Dqt`Hwjy
z|Ljad-jxfBmwD5ny*?(Pu{{%}558g^dzAqhoa045-o@aU<+mS`+(Ae@^k>#uPhDT#
z^ua3`$@nE}m7Z~W1=xk$4QL&Y#{1>!8W)`NVc_$$>mzD>C}YoO8XJvBxSLCV*z;{6
zYSLWk-7K4m+vCDMl+0v8*p|!_Ho_vXAAPU7vEBlq(_Kw&Dn)qy=GhF<PgMWZJ&OM1
z8(X+EC)LMwr4!k<IJ^qjRsn~EU4)E|mSOO2_rZJJ)V!m_Wnm`Ie7GoatkjO`5A|IA
zI&_We4Rm?5-GY911>77SOD6hi;q4jbOJ`_HL0mMDm*3+K{LE@tn@inq94<Kl-DycE
z#oBcELu3J%eGSZ3s4AdJotbYbH|x>1|D@-Yv`h$K-hJ}t`D)m0EA{i+34Ki6#N?!^
zm5uRi_Isxx3^PPC_v&(FL%~7U8<!X&;n;CW)-cFG>kBfgW*bsb@&1N;49>04<<6_p
zTa$_5`y(DZm6U;=Nh<hS)Pw)^HG8ie@<5l7COUeiaxD97Rdnr2D>W}e=9-0cIa0Q4
z&X=5P1g2f@nyqT$VenV>rt@4y5cKQit;p|n$SQJ4=PG9b2wm7)F&7wx<?}zb_s(Qs
zGwb0^8wFBOLvQEdr=O^H^{WJ;D7O+iqNjDPFjDgt^b=Ond1XMz)_dQtn3ZF!(bVKN
z?nuy(pY8X~qUH;djqSo4+#%>Xy~dW@IDD@?XP%cvK|{s@)4W@0IH|tNxS!Gi#dBNr
zS7(}IW2g|X0$(C*9MN`p@~s4y#eUvYp&>DN)P(H^0||6(ENg9c<-m@g7eB7y&4!%@
zAIgvIJOhdoJD&Mc^XBlyxlxv1uGn^%OWtX$5Td?UsVa%pqK667)l#E4%xFCNW%Eo4
zuBbD}{m7G#WAgsYZ(SQ<3(d8H%NYeQ#rn3m;#&?xne0;YcpU+sC53#V2P>ezYgtx>
z>K7Lx>ziH_XP`T8eran;A-=tOdT7UCZ+QDMeG^Ye4GhRUoprECgV1)@&i0k%ILGZb
z_>eOmMQ)|CixE{QIZ69$z$phQdndd<wg({d=@0hiXG6h|(dcq+Lm@7yn(H5^L5x(_
zs`FE-1##P)$|}t;EWH}E>x?<oUa#vkWtp!A_d0Kx8wW@nxnZndzBL~WO;(;kol0nJ
z?-8jnYJh!fzPFHdEttJW<D4f|N72xsIN6O*<%jCbr&9VV7<c#Cvaul#6JPV0zqwq9
zD;UN`yfTv^O(A(x<a8YbrKO(VAz6-Zw;HoDJc&aE3wK+QGZk3W?U{1;d^F(7Hco*)
z3X+|7?1+pe_@ek=?~P}*pd!F_rg2poK3|I88sBpX-!)XyBsNu{+8&oRb?OaJs~+^;
zz^oM#SG#{OHb}<c6q_QK5--S=J~>EJ5DMZYT5pR_-9lp;!L~l7GSKbXL!;?ajna}d
zccfRQK`LjbEL~d(2nT3$@4c6c_pZ_xgd2zBs%2qiaV~eDp^;PBP~{6XS9{*g-=V-l
z&)^lOYvIV;>nq!#RfgL4T~uQOgW=#8LO9T*4lIS|dJE7M<qa(P4!KmKI@|q=ueW96
zK!#Dspmq}0eUeix>Z(M+(q2}Z(H3~<d*b4Ugak13p%3km@Pqb<rGpPd?m+K$Z(>VP
zGi<4{J9dwfj?N#7r4y<CHRk3+3Brz<FzY5JP%B<QjsG8Iylx%?V@LGcnq^wSTzZ&`
z=4TV~_-)zh;dciJW$|u_S6QenNn<>ze-opB1!$)QQ{}W;N0P)miCj;GmNZp-QSSAj
zW0P0of%u&CR<8x&n929&+7>OSu|SyfRFs0)TIG*{)O^YpG5r!e3aNOEMX7>WB?Zs!
zX;t6sQwr$HxSgl75+jo@#&1$BM%nvS&bd#jQ1e~Koecw}U}$yK-sgQ8D&*T0vhb(l
zoMmv7$&XSzVJ^YtQIZA6>wb2)8W-TJ@7j82srer6+pT5B6;p96w^24sB?Hptay>k<
zE0E1!K-YES31ne7qtL?KfR&Nrd{YdWp!ck8ucxRD=r+0q;I3HcI{R*|Q&uQ2YNyd2
z6;FZh+#3$Dj>N#l^5<Qe7KPv<RvWyRClhGcSAFg}SPvq`q9PwO^T6%yZY?tve^hBn
z+_31BiMk@2Q0nCocsv?$$3m(a;!Gw^I~8_7=Igv>(OpSk9O_qT#zR5g=5?bFJ&Hl>
zB746@bqU;E+<iH$JR20%OP{RDsRtFL74H8PkCtl%_dVT1!EGz6Ijw|C@!s6ceOxCC
zG5%Wb?Q{Nx7)|WbJAWYwCw-o^j6R{j^wILQCn!xAPe1NtJd}Yfel52wM04@ZjlzMC
zizX<+kTtXQN&_a}Q!sCAPXHDL$qX@$9Jn(v5XM0Uz&CxjYwTCb;n!yC2ff_6u)e|w
zYEvlCG<e9nmMRDB8PxA=T4RGL9ywk1`|9D7)xM_k2iZ`4-@%N0Sc@Z*CSt?0H4x0-
zYfdv;0wLE--lV6r;#lF6(shr@pv=ZIW|B4-!avsYNZzgnll@*^Z*Hc5iPy>R*&KK9
zp|tl$8s<9O)VSl_8vhGG=k_ebJ+vHW$}8yx4eGJUVXl9db_TZG+-4$kpa$&T-XoHj
z>%mh!Bzo368=4YUD6vRZV*Wt`CHL7pOnu+}RFcvHSDVKkGk2@O>{yHU`_5trs`z=d
z>{tcPs+Y|Sx~8Coum8z2)mg|DqD<O^)<BSty@1nnEiO7rJ#VG1_rq6wTkQ>_p^g8u
z!FSIBJhx2O8Qkp!OwU-F$EUJjOV1a57xx$(idl7Iuq_i7d^ovZy-h>$IP04cf~jyR
z!qL5xyAtje%{H=*<pS9e)@L12iltwIW|-!x&|4|_iAR1OMiu187dNKDEVop7=GR(0
zXXMVNN7Dc|)ej2<g%!XwJIjeQvr-rg`&7WcryiUtS2S8$Rv`0pM*ft2;dn}1{{n+u
zGYV!ju4CpQu`6L=O3%R=)zu!Ac^~z{wWUm6X5T63rWA6S6OyrN$L-2e32OY2+eNv!
zBN(Jxd@|>y8__qRNQd!B9rPLcSY55C!*Y+R?4y{EI;-XVbTo2tU-X57C(VIyMc?5;
z$c3}07-B6wWswea>FZy}r3M23r03Oi!9r|1LR;#!FBJr@%*3z>=E9`hj_oXKtl?>9
zD2uI35xfzr+>*Vo2A}adFIEnBfS6~zDEl1>20R^WcG@3^X3Z}GXTKJp#@erC(`Tvk
zh|Qjs)un)pGx>P5yc}&PFDlMCv?6-~Nw+xFgnD}H9-&O7@OoWZcH|H>zbf9muyij4
zt~JFBvwn=jRjf%P#yZ)^BbFq~GhKmS=X|<fgw+AZ(<;`8V__h!{={j#pblO&q}4Kw
z$0NfGyXJb;bkKS(xHFfj0`+!C(w*L4fH9MvQ7f}EAZbE&vgt83pQcc0m8^gZ(9IW|
zrx7WKV{YBA+1HeU^MT{;d2gfe=+cd;vIh<D<&0~LZfiF3e=gUtU73OXwo{2>JvG?j
z<iUEywE<au+Iru$QuBXy`yH!aor}Yv0jjRg%OE9KpzD_a1-3aUJ??p2hO@6$bM=0x
z#MM5m?)s92@JzwKX6IxuIz^{4m0z#KoY<JPRb@Hw+JVM>DW?*+Cm9qb$XcwJNF@#Q
zGGOGz=?1+E;Sd+P!Fy{O1t$BgM&M*4wm;p;PW!nPEWR9A5^lN)-Vb9B^3i4ES>5xS
zd8cFH%j=CpGy1i#%Z;bySa&UcZaurXXrd0zXg>InXmbmO_b$d&Qsq8(=hLWf@nyJH
zKH1c_D;6v(GPh7t)8X~F{BeeXeB2ZE>$!?W4jj@r@>Por$BajfO9?I%cwj%<{q1QL
z@bp_Wo88NXIk_E|XO0Jg*U~o)iM%Qdzwxs1ZeRh-dVK2^8@_@muX>)iy(~dLTMG}#
z&Ea@YLSo~C&v#I1tAMl4a2%YH+aTDeSqR5>oS@fdPr~8TDNE_@SCHdLSKs=bIY7JO
zT9;E?82UaO3;42{f)ZaP8M3^xU=S|cqk1RN=Vtn9nNc5f?var<6sZ8Ur&ce@squ}Q
zB@aa|saL_0n3o8{C25G0x%~P2p;l<6@>SKw3^>=^KUt~Z4>Ig2$K3Bn!u}Nt@opO^
z$hI$7$n#AW$k}s@wIxyQE(5FRUZ)7qZRM36pyo9We5UbP(^rodS-l=V;tNNkQpU&a
zt)}qQOm2G5gDQ|XP)Zz(NW(@&Yd@A8Y8+Z_2SvO;5pUWIluWW!Vw3d`jhLZa*iq_z
zB9%2BSBX{bJw<hiEq^`|lD)kgXT#)IgymBpN|!0`nO`Z&WJVO^^(G_3sm#IRz;<}O
zKjdJWj0Rl1dQ1EUe=`1DU)%ijm={XVbvw<zuEvQ&M|n6i>(SBbv<_{P8J0vpfmWej
z5Io`<tP);=<&~Z(N2z++sgH`1H!Mprx8==UT8Tp-^KDU`_u);fuX!h0i{<cOafmjv
zFB#*adX%!OQ!yprT``9a1-8F1czsXa544*O_P)j{EO3e&>>a%emv3zqOeTEMhSN&x
zfs8*eEYdGgqB-!pyIK{~b6+%SoKH(kq<%Lhby49qe>P6EFC24W$v{IQKv^oI7HTSF
z3ol1jp#4STPhK(suq~Iocfq*^Jo_t?wPj1uFwmujf3OrYj=sOywZ9aTR+&!ID^p<V
z)Y0L!rIqL`Ic=&@l7-nt7x_1+#KXIhnA^t3TF~s9;#%X>>)7f>_bat09B3j=J71oP
zfaSyIWW;aA!~0do*2{V1fP}kbZ4iA0MqGKtbna{go;2=p{~ncvmfC(?lS$M#eL%&&
zm9`bgq>{Yn>55X=y1(A?i&Qi8UAbh?VjG7%12h8Q6M-J;wGrNth(4dlAMHjBs1=?U
zHqBRvLraM^>~1kYr;{MJVNC^GG~KDEELDtcOj2}A2TSl|x%1Cs5?PRO=Y{(FkSGXf
z9~kRr^~Vt{i#07&eKg#cJE?U(9bJyFd2X31$6PR@e#R66AxvT4)~GfEGXvASzibf*
z9rvctOx3~W`=UpfESss{<sG`GeWMD#a&kzE-YbFaDXw!W)q(K1_*bxjQz|r8C>E?H
zieaKSy_J%a0X~wyN;dUU<8yh2>aX55qQrorEyJw>SYh9t&viZ%+dt*6df8g~e^hhk
z(NO>Y9w$jE5@m}vMI<RwA}>iIAt_{wvPCFTDuwL(zHejfV;^ShUbe_qiX;&dLM2&B
z`sRM_>Hf~|+;jiF=bro5oY#Cl@6XJfIp@7RU(d%AZ!?;{F8ZAgS^f;dtq&UEnWjSE
zouxE9uEVUoCxqZ5qDl^O8>L`AN8g}6n>T#EXv$^K5d^=oe}s<5R^zSWh^tS(QsGB~
zSpNR$c+_SISC+|)1y=W5K3B;MP}#?kK)ddYcE2~i=CDn}_N6UqB<*l${=wxYtgHe@
zUyW=tbM=6J2d#3y`}U}?Djm!0;02!NUzqy#=0X2x&%;kAl7KuK@ca71r?8pZN#3S0
z8mpIoXgsgZMTRu7t4u28VD7}gwpGLh&TJPxUusE0qmU&Lo|0@lTrp_M#gq+O9@*$E
z&gVi{*9eRcMF5Xep+d=zO3-<yvdO>2AD55S>P4TXp(ED`rjZR%KrUR@H|#<5zrMT@
z5p|*BN||o0E_W7oJJILd91HNQhV*`VKpKR_J={K6L&3d%Heu~LjW8iRl`zX32h^vQ
zRpQGgz|7Vu*}k(34$oW7?g<Hm4OYQhJ{_f^v7onJoG2OZ&kXe?k;<_=V&?4R`wN)y
zb8EDNdOCj5o7R}SScciGog(JmS%klRUYy;s1V%V}8RH)m;P2yy&MFd|ndpYKSs5~P
zkbR~a$Cyk(opbQ~^XUw{tQ;@+r6vWNMlWV*tRsPp)Qs_>R6S%q^9!vd_zf1HUk_f-
z6@qLt+e<OMY-Ic7>Fbskg*?IXIwh->FdrM*D|(~|>biKvLZ4E>lrO?UoA5hysSZhx
zxD!6k%t7ga$A}*!;xEif<N`jWp9v5_5S%DWyzg3yBk7H@e#T{x>tW6^W*i6G+}X!X
zxammgdB>Jp*aS+mLCwO)GH}DpDY7}?*H7TznA>%+9;X7@-1Vk1vG(*5<yvACDr{1e
zx_hD!qi+v4N!%dD-}?SDJ}v{h?3xv{TY@opPyEajosOTzN?S<8?{e?0U{XF2hE}%x
zcP$f>!FGlN?_mM@eCBLT3_!R$*nQgG?;634s9(`$O@(Hil9-K!RIC=+`{SRm7}S?I
zZM4FC1IKwb(YWoa;S!&2b8|-kq!!tzh{`+zd!d3?U%DG`k7wM+kfH==n>})_y|xI=
zUmMyNAHNOZ4QJV7Z0oV->4mY*&&2xd-rZp`WQV6}9u?gvrr^x=Ne&7zZ}as0v1S*n
z!^dxI|GXpg>JjS_IYWU0yvqJ@ZCZLU#(omr-c?u%^BsW>R^##LbVrEqV2l^8?=;ud
z6ZF9H2SXu!=W0>AT=a=$Ul_b?<l652ycjngkaJ<NZo<ny^qlWe@=)$iq4@pKWcZ{!
z@VzZG3nX3pecF9WASU9uwF9B6WWA?H^z9_}wd+5tizmBd`1gLV{qG2__Ky)+*eVx#
zw|fejhsMHY({a~%$yBtebKUAz=8r~Sy%h7Z5;4YTpEyfn9(XvM=nX3jMcUY|-V>|k
zaKiMOxTr}Z3<}OQ)$lgM4mq(ionh%9CK-K7(}fB<a+Q5IIGLe*&>!l$L>l~=W9q)-
zM+MtromM}=Xl&em`}>T17}mX7rKEqY1g%BkG&#u%B;Dm$%R>r=j;h`Vb?fXg{d#Od
zI<XHPXtUpNDm)u`lfEYh5dJ%Bc~+JAXGLhIxM8=vfjhiUV00R~hG_CwA~0ut0uKF<
z*qnbS466U(>*L7Gh1Yf$re<%JqQMES6P?~k=pgtqqJ-p)VVy6t_oh{&e0^Gojd40Q
zU+{=ud0T^*2f|MtD=q~!lj}kmgHORvAY16@kt{G^8#h-UtHWsdgObdb?-G9f*ZeyV
zSi{{Jhj-2#)%ZL$yD@tr4piC?3p#K2$0rk|v$BUMNJ~@h>$sVR<<0qC8oTMx5+)XA
z>Y5H*ufNiYded-~a-xy@2bq|Mea3uBDEQ*|H4BlsVpQnrz3|`=;Xj>Hc385fgFoxZ
zr`oq@uxy|Ifl0X-9%-3xQ2JSg9VL)AYLo$6WL5c#2>;{}7Nw|Nk6cjq1Jhn(PAXhh
zID1|+wi(8F62|P?67YEMEtVZOBj8j|z%lNWG-#|G6kIP=0xN=5YqZf2-btS;7%I+y
zx7s3u+llo%z<kfGK6M%lxM=>eDU8KnmSvS1GsKh^M`iYK$6(QSI}M?a$!N6uTJ4HJ
z33_UI%xRn=6FPibqVbyqsLHU2+RRRe3PB4IYoA(_CGTUnHC+Mpt@Vzn+GW64lI<M6
z8jH`mGwVhIicv>Hxk36>8hmo7@RL;~xb<fdBOO&`uzAyXSCf}JoR!=#z1kK79}i+;
zc|a!Q#4hb54-kH*7mjU*=u|juFkH>gmx7%G9}1r?(gA~(`TFCM;Wwr<Okb{r{qh|T
zF87k47gg+oHaDP5@bh-3@2Rl=eYoNuh8(OsZ&IOpG6(6+*FK6l5$Bb-<y`xaK-4&V
zPvnVE8uGXFG{iZi!b)(TgV&2lNR8aFs5L-=GygDOOjN4Jqv6`7JM#$MCpa+Wgkl`r
zms7~*5hvmK?99DFss+gIAc&3ub+}{{D!Rp{5*PdHQXIFCkx|-)%(qw!Qxof=tpuXM
zTa0&hr92jO2ecWZ3GR>Q^EsP}?*tbj@!^}#Od$^OzqjT{YlXGOUTgdwg@MH0F0X@r
zVbI*?bE7{o9NMqkkPoP-2cADc1*UyDgicp-zj`MXc=b2#^IS2*74O)6Jh5r8VDVaU
zp70?&EDSX&btJebp?bw)t#Z7=ZM{iYJsv~-4%BVzEJ6~AQ(f;#5|%CpTHSU?fqGUE
zi7ifq59j>wnk0fJyECxAW0aYUgy!3>DHe~Zh8s?gj8ox}-Bz~E5&1A+*0rLy)C4EB
zFML?~Nr#zZ{5ucT--miCm$?3n4@_#yx8+9WfNEG<xw&5snlPr%x8P&&YyyFYrxUOu
zDLeOCOf{I)oZFkSh6=l6GtT8D`v85rsFkR06^ivKcDj75hRM8%_&ga3YF%~@=Lob!
zo5NGfACH%S?fu{gk4seGi%WM`jVgt7kF_mQRtS$N{*f!$nc$LL!1YC`4i@L^GUe{o
z0%gZ#lE!2jY??L`e6?@~UM%#hPVP>`ceE})Z;m2h{cv4p>`W=-El~Oc)>Wc|K~}XK
z%>eEipDGm9C3Kt1^`=dZh1eN1w)Ba+3}xkRm)eI?@Q{o`&)aSFu*|-ox%N^H+`Zq!
zL*=Z4jJ&APeLJ5(ovPN?g0f&(pSY5H+PM^jZ>$<`Od|oqeRie>LO*0*x{!d}Iq1cE
zX-Q4r6Tc=JGCy-_fbAw8;zxeff@TJD^;(%i_?jQITH0NVZNgup@9wHc+pt`5%J(D`
zuaqA)w8{pnFA|j#!8D+L{Hb=mu>h~JdhcpILaY~h`fA&Ven@#pb4aru(QupQ{}#tb
z?6+&iLmV%a!BjL?+^>2PraLM)oQk4D<o3wGvs@nF7k7s8`8*XQK8Lye5u$<8&8owT
z?18v{?b^WZ1rk<TSP$h4<%7&7E?+GkB17SzU{bnc4#DNOqcA_=_vcMNaz{86+op9{
zyjly<xQy&t&ZP(M+AxA&k~oJnu*)BBjDWcbMITqCAUr1S9KJs+31-yp{Ayj+00Zte
zEBj4ka5So{-OAnyzw;v#rNj%NPp3MxF{l!M`+9GDG!}{HCV2+iUREG5(4>0?^I$@E
znOvvqfZj)y?k8=D!Jr5R9sMwW;F}xWaL6Pb=XlQ!i4*h99_OE19|lyS9$ZipI8R6I
zW3omCJ&o|nAZb=}$rIKb)pmD$5{;v5gTws-RB&T8*m;(>8l<NhNasvT@S0;ygUr=<
zaJjjM<mDO%L*51p33){zWHb9Pf>ez80tIXwgJ~#Nr|m1Vh){1SayXOVCEk0*ytGCN
z3{_2(wwjiraI#4`B_IweU*SX|r5M#oqZ@CM6Y#ZRz}WqzB>eM5vu&G2A@a2z<*?jO
zN9F|u7kjf9TnYHnxz35`3v@_U#H3MyMUS$MQPYj+OIK&lE7I`A)?%kci(s4|D6~+)
zY?zN?IPi;3ho)m@VVBiQfMWEbgG12}L5R+2A(RALcQmU!(xBj=$r%$TeZpUNwf_<0
zp9+lCHfb2libTFG9JYRX70~n4ZmQPF87?cu7jUrB@a(HI3$x!NaoTs<_TH9K7<j2F
zBefKSK3r)zto(sQ{y|+rv3M;^Sv)?hz9|W7+bu_TIS@W*z0Tjm>?E)V*8knmkPdl5
zcW1V+cz~MWH(Fg@BEdsEzItIc8`KX4KN{Fuj6FWDwHpVhDE}c@I>n$K&Ln+j*kBkB
z`<LpO&eS(!;)vK8hige-v$XAi^4lyJQt;5(p%sRj_XbSwolL;;f_KulT@kg$Zl0Fh
zR0GGkWrYnh&A{42YBqrIeH&h>c2H6$qXRYkf{SVnWNbORMtigY43F}k?c|AtO)cu7
z@2=z`XZCB>2z@Hvt1WZwA^N2=#;u0$tD~^i<PrH*L^&=EeOeaPqN9DwKQ)IrOW>E_
zrbEiQO&F=-tLKKUu-QL2YikIh8)lm7o9%pxmrO$s-W|LHX<cL9o-ZTdd>cza%9SdR
z{q=a5`aA|@-*U`f_D%#}23-Zoo8=I$DVMU>HxpOmU)3hW))IX7L8;SRX|R>PwAgsF
z6oy_|9_ouAgGJNYM$-F6TrW5%?MrY~uS!%({CDMGPMNvD_8a9u`YLeliAp9eO%%7U
z&#nZ)#-X5w_f%LgcG|!<5{u4Lf?Y~h`M9+^GIjr+LO6D|UeVBngq_rV!BLwVK_y0k
zNxw7#MOjWgUtFxikPZpqHCGbh?Xc<?_16=i)n_br3=%r->6a?&lpN7_!7igRxd6r#
zn?^kMH)Dk8)j1PlzhJbB|I#JRblBH&nK|KWDr_!tGArXJgKT%-?{5A&lpih~2(Ago
zy>ojl_lDG>2+grRA=Mpki?lEwsVhXTR(i1KdlDSo*;Xy}C=aQ1$FuhO2I1)31;Lt-
za@1Szre^;*7vml%JsSMu1wNZyKCMghhmSmC-<9lhQDgCU851=PuYTRNkTO6)$y@hz
zI!a=I_Pu>*&E+ug<6WkOY@iYSg@rd?uY_ain-2lelrmhN(|mZArvfY`U}#TB0{&c+
zI_&V7@V#vOIKit-MnY~qan6>=dFeNbWYnbKZ^dar{oYc1ADP$Q_KAv{ayKoB?8=1k
zH;Q35bi#2v{rSb~G9{q@M5|f<BL(Sg1E<PO^HK2ju1<p&`+&Wub;T`(3|lgXmoA@-
z!d|s^zjTOxMoyvs>K+$5?kkx3oWzm=J+%JG#OH;`ma64pUs8?g<04<HlroX=eN#bX
zVj=W@U>QCjP4I>?(^i~)SrBfmwXeRp6dr6A(7nG%1}*b4C5PG+)IXo3lV3!~a$Vhq
zo29<U#C74Hbyq8qcUx)<iIw27SuA~=sAcf@bxYUbCK6hy%U}FSoHJIMNP9P0<PiU>
z+@D8V>frTNEvdDOLAd>r=c2t@Ijmp%aIHJRj|MDia2nKA;>7s{Ztk&6c*|bc)4~&r
z^Fa%E(6|L%KLreLdO-!*=N0rES{7`fl$1?Xhryk?*G;{BF<8a0)F(<OI0H{+-NG}z
zxWTk%RrGx_L{;8+BX<mOfKB9<oeCZ1cU=7;!JCV+Fl@iCBm%!p-m%|T>V-Cg->gR^
zZK3+wC4<i0%^)zcmDA;524>pN)Uc`1q2WM}a->)qSiUrQ!&ps5$}X<M4GX2%&1TW;
zH<=5TVn!<$r81z6Pie!Rh8nE3NTaTe_XmG2t<0;Nb5Y=&zUW82JFv)*)lp>|gC|qg
zFHYSo22P&Q8kXf+xL|enBXw5+Fv)huxrx_c`@2n7o-LQb$0zS}Kg9UKl?LWKw)#3W
z2@`7-Z>B=ejG`3FJHq!(*Y%YCL!4*iL;0^0oR0p+V=D7G<uDpspiNG0!tT4S8pb~}
zA-?$C$M=j?pvv>3$ix3O?CICMqO4<s{0w#GH(ymDZ=ValZxRWnXRVS~+lq1QWWIaH
zPCCre<=Wnw6@dD4Hdg<4ap0=NI$W<E3-rAOE?(tC?#0VHDzZ1zkw;;Vv1oS<xOeMQ
ztuts~#^T&zc!9{BGEVxfT$6#LBV@LT2^w|^r?Pm{a>2>cR_&h`<zTYa!1D@;hNqdw
zSJf@c!18mh#piE%_+#Yl>$<X7oU<;tSp+5E)lw?&M)b9{uKzif)l0<#Jchi>-|ewp
zI%xXO`9f4PS&(oxqr=>Jy-zB_3FzSR;mO*LMC>}hJy*Ow2{@{EQOdoOG1qR)MTx%=
zwoL^baCuw<@~qneZ$FO3t{pjpS|WLH__vn0vE6O>@_8SwXC(N7A>VS%%ib{h-ojVv
zAQ@ctB>Vn4<_`H>Z>s8ve3wAqwM`+LvS43dwYi(?Lr|+}Q0fq_!jvE(m-vNb$eKfM
z11ATFOLCJQoh}95?O7zN)@W$&>D#;I0~Hy|WY4o`s-kP$wwRCCve9&OThkT(45Wry
zR(p4x#wLwHLteuOwCJ%&m3mMQzwC}&Jsp+_!?(Ikg=?Zg{0u{=B~vY4Q$O|QcuW%T
zx%%ezK{bS&Q(ktuREl!?g6eGKY+zD3qS{96V`qelsNtV|f$aQVo&N+4tfv+@pYO=P
z?+^7tcv`X`uJGdeK-m&BA+?3E?53hMS+z|0(o_5(<$uP<nFb;v#S#JGWKeqTJv_u+
z1yanD*>>Y}FfLQ&Ga(g&>6aBXX@c8(Wi#+V=T;Hkm50h0*I0Z&Ih)rw;{;Kbwru0=
zX&5$Jr+n0;8JSs}LN0pxfbV=n{>D@4kj5sK+09Xfr%HG_YY0B*pL5q|l36NYEqCy;
z_-=y7(z(Nanw$p*=YKWFb5n4c6jqUCQjWP9Hb37GxhUtpsQjrJ%|W)G-fdDLb|AT9
zSvIdcA3Sd>Go`zw!{KXNFTOsR0R_X)_C1y;#1|(Lte?E_fb`vGddJNgA<wQ)qJ>li
zBZ1$%WEJ9Y(x=zd^kgB5+FTJVYp8<~$I;^pG!h6z?-=DBN`=f4m&r`MT0Gq&bEl%-
z15>{zPKs{vgvmaoSBAIgcu9FMVcI7hTDt@1mOhZMX`$s??$26Ov0YTO+MEOI2M*SU
z9?652DSDLcEE&Y~wGC#qN^sQg{Wi13>loCh^5Y+JA@;}^nRH5*q20E68LkIWc;B&t
z$OocB^M0Y{fv?Iya5sy?{##jaPu`o$_&gQk6sXMW9{b`5q&9ZmxCKc88n$JeHISFX
z>sz<641F%Kyw9DcgXYLRxgduu2$mm`yUyYUZ^bkDlgqM@{@{GI4^sltoGq-s&F7=)
z(d##NPzZf;pS<&v_F|Z5d`qUvQPGw%7sbw<fd@E)>zo!V;O3=dhj#a9c&l!zH0MU}
zgkr0QvW?2n_|=R^9iuxAMo1n$+)@B45y{ud0{+0senMcD;Cm|@1kPTY$b|3#Ng^4$
z5=9y~UP}vU!}Baw9;!VZ&t~_J_Y-+6_OZ|P_So4#trR75fkws`gX+Q`E|Kwrzgnbb
zWeu*LbnibZM&v&{jn&Ny%fp^Wv&URn3jv0^+Rd%vpinnKan=Qi{5;z6?gk1-rYSo7
zNXx>_b^Em!h@3#xW^eJQzI8Z~#u7=JrsGTX&TXwbh&;M!8Au^=eRez&czdq25+a47
zxQ}iwz*Ro=(2$8-_^oiE+3sjN)ZW;mc+0o~*{yEeN}g>1^A*wLa*ax47w%ykjUe=z
z;Uj&u!z5TdrO8qF<SB-XHw(U~t^hZUwMz$k2(HMobK9FXD*P5GS@``S1t;xNb<V1|
z<M-BW&$e=AqsnBuLyb-x_BPz7aaQFcufrC#XVE3#68R<Zu}A@`@Fe#0P7wR~IIcHB
ztHoH)Dq(-q#T}b|Ts%c{qe0n5H4}N=S}>Svce{Jf6TO$_wmn;K2L)Z%7$WquL3_0`
zz<RO_jIEfQ7iK8<Kl6hBxAJi}Z2lWg`LE>5zt#WD_x!)j$Nl@Q7#MCjI@nlTzjM{x
z?2g%gzIVg^j-%OCI}0=Wzjyqt@0vN@FtfjI!NB0`WN}qlMMdXd<NscFa=zwt%j`d|
q|E(RZ9scVE3zz@?yubCopZB-^_xQi{zsEEF*K;cV^@0CSUjG9u$x}E0

literal 0
HcmV?d00001

diff --git a/bob/bio/base/test/data/bic_model.hdf5 b/bob/bio/base/test/data/bic_model.hdf5
new file mode 100644
index 0000000000000000000000000000000000000000..f465dfb69061f4ca50f375722ab0bd9e38b22683
GIT binary patch
literal 17472
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
O$5ZA_kvvw-!2bb2e8q$S

literal 0
HcmV?d00001

diff --git a/bob/bio/base/test/data/iec_enroller.hdf5 b/bob/bio/base/test/data/iec_enroller.hdf5
new file mode 100644
index 0000000000000000000000000000000000000000..a51d341f414618d3b23c587fc8cec4115ddacd8d
GIT binary patch
literal 10593
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

literal 0
HcmV?d00001

diff --git a/bob/bio/base/test/test_algorithms.py b/bob/bio/base/test/test_algorithms.py
index b9525276..b732b1c9 100644
--- a/bob/bio/base/test/test_algorithms.py
+++ b/bob/bio/base/test/test_algorithms.py
@@ -209,6 +209,76 @@ def test_lda():
     if os.path.exists(temp_file): os.remove(temp_file)
 
 
+
+def test_bic():
+  temp_file = bob.io.base.test_utils.temporary_filename()
+  # assure that the configurations are loadable
+  bic1 = bob.bio.base.load_resource("bic", "algorithm")
+  assert isinstance(bic1, bob.bio.base.algorithm.BIC)
+  assert isinstance(bic1, bob.bio.base.algorithm.Algorithm)
+
+  assert not bic1.performs_projection
+  assert not bic1.requires_projector_training
+  assert not bic1.use_projected_features_for_enrollment
+  assert bic1.requires_enroller_training
+
+
+  # create random training set
+  train_set = utils.random_training_set_by_id(200, count=10, minimum=0., maximum=255.)
+  # train the enroller
+  bic2 = bob.bio.base.algorithm.BIC(numpy.subtract, 100, (5,7))
+  reference_file = pkg_resources.resource_filename('bob.bio.base.test', 'data/bic_enroller.hdf5')
+  try:
+    # train enroller
+    bic2.train_enroller(train_set, temp_file)
+    assert os.path.exists(temp_file)
+
+    if regenerate_refs: shutil.copy(temp_file, reference_file)
+
+    # check projection matrix
+    bic1.load_enroller(reference_file)
+    bic2.load_enroller(temp_file)
+
+    assert bic1.bic_machine.is_similar_to(bic2.bic_machine)
+  finally:
+    if os.path.exists(temp_file): os.remove(temp_file)
+
+  # enroll model from random features
+  enroll = utils.random_training_set(200, 5, 0., 255., seed=21)
+  model = bic1.enroll(enroll)
+  _compare(model, pkg_resources.resource_filename('bob.bio.base.test', 'data/bic_model.hdf5'), bic1.write_model, bic1.read_model)
+
+  # compare model with probe
+  probe = utils.random_array(200, 0., 255., seed=84)
+  reference_score = 0.04994252
+  assert abs(bic1.score(model, probe) - reference_score) < 1e-5, "The scores differ: %3.8f, %3.8f" % (bic1.score(model, probe), reference_score)
+  assert abs(bic1.score_for_multiple_probes(model, [probe, probe]) - reference_score) < 1e-5
+
+  # the same for the IEC
+  bic3 = bob.bio.base.algorithm.BIC(numpy.subtract, 100)
+  reference_file = pkg_resources.resource_filename('bob.bio.base.test', 'data/iec_enroller.hdf5')
+  try:
+    # train enroller
+    bic3.train_enroller(train_set, temp_file)
+    assert os.path.exists(temp_file)
+
+    if regenerate_refs: shutil.copy(temp_file, reference_file)
+
+    # check projection matrix
+    bic1.load_enroller(reference_file)
+    bic3.load_enroller(temp_file)
+
+    assert bic1.bic_machine.is_similar_to(bic3.bic_machine)
+  finally:
+    if os.path.exists(temp_file): os.remove(temp_file)
+
+  # compare model with probe
+  probe = utils.random_array(200, 0., 255., seed=84)
+  reference_score = 0.18119139
+  assert abs(bic1.score(model, probe) - reference_score) < 1e-5, "The scores differ: %3.8f, %3.8f" % (bic1.score(model, probe), reference_score)
+  assert abs(bic1.score_for_multiple_probes(model, [probe, probe]) - reference_score) < 1e-5
+
+
 """
   def test01_gabor_jet(self):
     # read input
@@ -277,59 +347,6 @@ def test_lda():
 
 
 
-  def test05_bic(self):
-    # read input
-    feature = facereclib.utils.load(self.input_dir('linearize.hdf5'))
-    # check that the config file is readable
-    tool = self.config('bic')
-    self.assertTrue(isinstance(tool, facereclib.tools.BIC))
-
-    # here, we use a reduced complexity for test purposes
-    tool = facereclib.tools.BIC(numpy.subtract, 100, (5,7))
-    self.assertFalse(tool.performs_projection)
-    self.assertTrue(tool.requires_enroller_training)
-
-    # train the enroller
-    t = tempfile.mkstemp('bic.hdf5', prefix='frltest_')[1]
-    tool.train_enroller(facereclib.utils.tests.random_training_set_by_id(feature.shape, count=10, minimum=0., maximum=255.), t)
-    if regenerate_refs:
-      import shutil
-      shutil.copy2(t, self.reference_dir('bic_enroller.hdf5'))
-
-    # load the projector file
-    tool.load_enroller(self.reference_dir('bic_enroller.hdf5'))
-    # compare the resulting machines
-    new_machine = bob.learn.linear.BICMachine(bob.io.base.HDF5File(t))
-    self.assertTrue(tool.m_bic_machine.is_similar_to(new_machine))
-    os.remove(t)
-
-    # enroll model
-    model = tool.enroll([feature])
-    self.compare(model, 'bic_model.hdf5')
-
-    # score and compare to the weird reference score ...
-    sim = tool.score(model, feature)
-    self.assertAlmostEqual(sim, 0.31276072)
-
-    # now, test without PCA
-    tool = facereclib.tools.BIC(numpy.subtract, 100)
-    # train the enroller
-    t = tempfile.mkstemp('iec.hdf5', prefix='frltest_')[1]
-    tool.train_enroller(facereclib.utils.tests.random_training_set_by_id(feature.shape, count=10, minimum=0., maximum=255.), t)
-    if regenerate_refs:
-      import shutil
-      shutil.copy2(t, self.reference_dir('iec_enroller.hdf5'))
-
-    # load the projector file
-    tool.load_enroller(self.reference_dir('iec_enroller.hdf5'))
-    # compare the resulting machines
-    new_machine = bob.learn.linear.BICMachine(bob.io.base.HDF5File(t))
-    self.assertTrue(tool.m_bic_machine.is_similar_to(new_machine))
-    os.remove(t)
-
-    # score and compare to the weird reference score ...
-    sim = tool.score(model, feature)
-    self.assertAlmostEqual(sim, 0.4070329180)
 
 
   def test06_gmm(self):
diff --git a/setup.py b/setup.py
index 6a0ee5e7..213ac20a 100644
--- a/setup.py
+++ b/setup.py
@@ -123,6 +123,7 @@ setup(
         'pca               = bob.bio.base.config.algorithm.pca:algorithm',
         'lda               = bob.bio.base.config.algorithm.lda:algorithm',
         'pca+lda           = bob.bio.base.config.algorithm.lda:algorithm',
+        'bic               = bob.bio.base.config.algorithm.bic:algorithm',
       ],
    },
 
-- 
GitLab