From 7a940f423cfef38ea957b76a1bf69fc6f66341d1 Mon Sep 17 00:00:00 2001
From: Oscar Gustafsson <oscar.gustafsson@gmail.com>
Date: Thu, 16 Feb 2023 10:15:45 +0100
Subject: [PATCH] Fix resource-related issues

---
 b_asic/process.py                             |   2 +
 b_asic/research/interleaver.py                |  44 ++++++----
 b_asic/resources.py                           |  77 +++++++++++-------
 .../test_draw_matrix_transposer_4.png         | Bin 0 -> 21765 bytes
 test/fixtures/resources.py                    |   9 +-
 test/test_resources.py                        |  32 +++++++-
 6 files changed, 110 insertions(+), 54 deletions(-)
 create mode 100644 test/baseline/test_draw_matrix_transposer_4.png

diff --git a/b_asic/process.py b/b_asic/process.py
index d48fa0ee..f25de759 100644
--- a/b_asic/process.py
+++ b/b_asic/process.py
@@ -138,6 +138,8 @@ class PlainMemoryVariable(Process):
         if name is None:
             self._name = str(PlainMemoryVariable._name_cnt)
             PlainMemoryVariable._name_cnt += 1
+        else:
+            self._name = name
         super().__init__(
             start_time=write_time, execution_time=max(self._life_times)
         )
diff --git a/b_asic/research/interleaver.py b/b_asic/research/interleaver.py
index 83eb4b3b..b64a79b9 100644
--- a/b_asic/research/interleaver.py
+++ b/b_asic/research/interleaver.py
@@ -6,6 +6,7 @@ import random
 from typing import Optional, Set
 
 from b_asic.process import PlainMemoryVariable
+from b_asic.resources import ProcessCollection
 
 
 def _insert_delays(inputorder, outputorder, min_lifetime, cyclic):
@@ -14,9 +15,7 @@ def _insert_delays(inputorder, outputorder, min_lifetime, cyclic):
     outputorder = [o - maxdiff + min_lifetime for o in outputorder]
     maxdelay = max(outputorder[i] - inputorder[i] for i in range(size))
     if cyclic:
-        if maxdelay < size:
-            outputorder = [o % size for o in outputorder]
-        else:
+        if maxdelay >= size:
             inputorder = inputorder + [i + size for i in inputorder]
             outputorder = outputorder + [o + size for o in outputorder]
     return inputorder, outputorder
@@ -24,7 +23,7 @@ def _insert_delays(inputorder, outputorder, min_lifetime, cyclic):
 
 def generate_random_interleaver(
     size: int, min_lifetime: int = 0, cyclic: bool = True
-) -> Set[PlainMemoryVariable]:
+) -> ProcessCollection:
     """
     Generate a ProcessCollection with memory variable corresponding to a random
     interleaver with length *size*.
@@ -48,15 +47,19 @@ def generate_random_interleaver(
     inputorder = list(range(size))
     outputorder = inputorder[:]
     random.shuffle(outputorder)
-    print(inputorder, outputorder)
     inputorder, outputorder = _insert_delays(
         inputorder, outputorder, min_lifetime, cyclic
     )
-    print(inputorder, outputorder)
-    return {
-        PlainMemoryVariable(inputorder[i], 0, {0: outputorder[i]})
-        for i in range(size)
-    }
+    return ProcessCollection(
+        {
+            PlainMemoryVariable(
+                inputorder[i], 0, {0: outputorder[i] - inputorder[i]}
+            )
+            for i in range(len(inputorder))
+        },
+        len(inputorder),
+        cyclic,
+    )
 
 
 def generate_matrix_transposer(
@@ -64,7 +67,7 @@ def generate_matrix_transposer(
     width: Optional[int] = None,
     min_lifetime: int = 0,
     cyclic: bool = True,
-) -> Set[PlainMemoryVariable]:
+) -> ProcessCollection:
     r"""
     Generate a ProcessCollection with memory variable corresponding to transposing a
     matrix of size *height* :math:`\times` *width*. If *width* is not provided, a
@@ -101,12 +104,19 @@ def generate_matrix_transposer(
         for col in range(height):
             outputorder.append(col * width + row)
 
-    print(inputorder, outputorder)
     inputorder, outputorder = _insert_delays(
         inputorder, outputorder, min_lifetime, cyclic
     )
-    print(inputorder, outputorder)
-    return {
-        PlainMemoryVariable(inputorder[i], 0, {0: outputorder[i]})
-        for i in range(width * height)
-    }
+    return ProcessCollection(
+        {
+            PlainMemoryVariable(
+                inputorder[i],
+                0,
+                {0: outputorder[i] - inputorder[i]},
+                name=f"{inputorder[i]}",
+            )
+            for i in range(len(inputorder))
+        },
+        len(inputorder),
+        cyclic,
+    )
diff --git a/b_asic/resources.py b/b_asic/resources.py
index eaed928f..121b892d 100644
--- a/b_asic/resources.py
+++ b/b_asic/resources.py
@@ -1,3 +1,4 @@
+import re
 from typing import Dict, List, Optional, Set, Tuple, Union
 
 import matplotlib.pyplot as plt
@@ -8,6 +9,16 @@ from matplotlib.ticker import MaxNLocator
 from b_asic.process import Process
 
 
+# From https://stackoverflow.com/questions/2669059/how-to-sort-alpha-numeric-set-in-python
+def _sorted_nicely(to_be_sorted):
+    """Sort the given iterable in the way that humans expect."""
+    convert = lambda text: int(text) if text.isdigit() else text
+    alphanum_key = lambda key: [
+        convert(c) for c in re.split('([0-9]+)', str(key))
+    ]
+    return sorted(to_be_sorted, key=alphanum_key)
+
+
 def draw_exclusion_graph_coloring(
     exclusion_graph: nx.Graph,
     color_dict: Dict[Process, int],
@@ -79,14 +90,23 @@ class ProcessCollection:
 
     Parameters
     ----------
-    collection : set of :class:`~b_asic.process.Process` objects, optional
+    collection : set of :class:`~b_asic.process.Process` objects
+        The Process objects forming this ProcessCollection.
+    schedule_time : int, default: 0
+        Length of the time-axis in the generated graph.
+    cyclic : bool, default: False
+        If the processes operates cyclically, i.e., if time 0 == time *schedule_time*.
     """
 
-    def __init__(self, collection: Optional[Set[Process]] = None):
-        if collection is None:
-            self._collection: Set[Process] = set()
-        else:
-            self._collection = collection
+    def __init__(
+        self,
+        collection: Set[Process],
+        schedule_time: int,
+        cyclic: bool = False,
+    ):
+        self._collection = collection
+        self._schedule_time = schedule_time
+        self._cyclic = cyclic
 
     def add_process(self, process: Process):
         """
@@ -101,7 +121,6 @@ class ProcessCollection:
 
     def draw_lifetime_chart(
         self,
-        schedule_time: int = 0,
         ax: Optional[Axes] = None,
         show_name: bool = True,
     ):
@@ -110,9 +129,6 @@ class ProcessCollection:
 
         Parameters
         ----------
-        schedule_time : int, default: 0
-            Length of the time-axis in the generated graph. The time axis will span [0, schedule_time-1].
-            If set to zero (which is the default), the ...
         ax : :class:`matplotlib.axes.Axes`, optional
             Matplotlib Axes object to draw this lifetime chart onto. If not provided (i.e., set to None), this will
             return a new axes object on return.
@@ -133,26 +149,19 @@ class ProcessCollection:
         # Draw the lifetime chart
         PAD_L, PAD_R = 0.05, 0.05
         max_execution_time = max(
-            [process.execution_time for process in self._collection]
+            process.execution_time for process in self._collection
         )
-        schedule_time = (
-            schedule_time
-            if schedule_time
-            else max(p.start_time + p.execution_time for p in self._collection)
-        )
-        if max_execution_time > schedule_time:
+        if max_execution_time > self._schedule_time:
             # Schedule time needs to be greater than or equal to the maximum process life time
             raise KeyError(
-                f'Error: Schedule time: {schedule_time} < Max execution time:'
-                f' {max_execution_time}'
+                f'Error: Schedule time: {self._schedule_time} < Max execution'
+                f' time: {max_execution_time}'
             )
-        for i, process in enumerate(
-            sorted(self._collection, key=lambda p: str(p))
-        ):
-            bar_start = process.start_time % schedule_time
+        for i, process in enumerate(_sorted_nicely(self._collection)):
+            bar_start = process.start_time % self._schedule_time
             bar_end = (
                 process.start_time + process.execution_time
-            ) % schedule_time
+            ) % self._schedule_time
             if bar_end > bar_start:
                 _ax.broken_barh(
                     [(PAD_L + bar_start, bar_end - bar_start - PAD_L - PAD_R)],
@@ -164,7 +173,7 @@ class ProcessCollection:
                         [
                             (
                                 PAD_L + bar_start,
-                                schedule_time - bar_start - PAD_L,
+                                self._schedule_time - bar_start - PAD_L,
                             )
                         ],
                         (i + 0.55, 0.9),
@@ -175,7 +184,10 @@ class ProcessCollection:
                         [
                             (
                                 PAD_L + bar_start,
-                                schedule_time - bar_start - PAD_L - PAD_R,
+                                self._schedule_time
+                                - bar_start
+                                - PAD_L
+                                - PAD_R,
                             )
                         ],
                         (i + 0.55, 0.9),
@@ -190,6 +202,7 @@ class ProcessCollection:
 
         _ax.xaxis.set_major_locator(MaxNLocator(integer=True))
         _ax.yaxis.set_major_locator(MaxNLocator(integer=True))
+        _ax.set_xlim(0, self._schedule_time)
         return _ax
 
     def create_exclusion_graph_from_overlap(
@@ -332,12 +345,14 @@ class ProcessCollection:
         coloring = nx.coloring.greedy_color(exclusion_graph)
         draw_exclusion_graph_coloring(exclusion_graph, coloring)
         # process_collection_list = [ProcessCollection()]*(max(coloring.values()) + 1)
-        process_collection_list = [
-            ProcessCollection() for _ in range(max(coloring.values()) + 1)
+        process_collection_set_list = [
+            set() for _ in range(max(coloring.values()) + 1)
         ]
         for process, color in coloring.items():
-            process_collection_list[color].add_process(process)
+            process_collection_set_list[color].add(process)
         return {
-            process_collection
-            for process_collection in process_collection_list
+            ProcessCollection(
+                process_collection_set, self._schedule_time, self._cyclic
+            )
+            for process_collection_set in process_collection_set_list
         }
diff --git a/test/baseline/test_draw_matrix_transposer_4.png b/test/baseline/test_draw_matrix_transposer_4.png
new file mode 100644
index 0000000000000000000000000000000000000000..3e7546972ed9c12dd8441ff39d432604c5a5c4db
GIT binary patch
literal 21765
zcmeAS@N?(olHy`uVBq!ia0y~yU}|7sV0^&A#=yW}dhyN^1_lPN64!{5;QX|b^2DN4
z2H(Vzf}H%4oXjMJvecsD%=|oKJ##$+y_D24Lo*{I?ewH%Bhxf<6CH(&l9GaAD}DW3
zxDLJiqICT^pFYiJU|`@Z@Q5sCVBk*#Va65q%QG1mLJ~b)978JRyuDjl5%TrtzmL^v
zMl;_iawqaDXmsS9TC_mvgj#^$#Uxg3z0l~zqM4#AT$?)@s#lxnZPl~izk^##K_IXx
z#PMc`OTYx*!%~xc#EfR1nR8#><l+nmh0}A+%=!L$*PaBs=RVKsT72Zr%_)|fohZ@9
zz`&3oS+<#(fq_9`Lk0sQ1A|T*n*amDfuvCmZNfn9%o~P-XWoC;wziJmxN+mAn>lI6
z85tPV4;t`@iioU8Jw0vH?X{IUJ!~8JHf`Q~b(yd9=41wjhNv66%hyM3&&w_23BJH^
z%`qoujY{V8b923Xd`v8TA5`p((~aL3bGVIn^5vH%)_w;9%uAygOM|zI%2*b;yuH1B
z{SV7LSCfX)fPWkFb8^-^di2Q0PuL=EeRyDCU{`nd<}1={Ak`pkjn`jKRq>v`FZC?{
zK2U<_p<;q?2snM>M8NlVcSXg;m+#nN@%8m}fBBjZjz_<x&#w(*X6FluiJ6mQHv7}(
z&%QplEm~&EDu~Rnu5LYbXwlg|K?A+^h3&t*VlPiW`FQh8S@nc{^DWQ3;b3Tp&oD3D
zsIs!+x7|&v-q1Idx~iVhX6rAUxZe}j{rca+56g0&{!5z6vvu<0Ib}iDyrup`OzT`J
z-9EvVsbWz*FDQt%->=(kRr+eli4z`6mo0mBzW$%_e<cP61rFCzoA8OUzfR1&;dSmv
zn3qJs+BXjcKc(bPRyq20r_aadf5X;Io9p9utD@zN;DKvvqqEP=v9$X8<#MV-TU^yk
z)!4F|ske3%F6KELP*%20uJVas>B~#5yUX8SlmGw2JvBZ3_1f+CjI6B~7*?nrejoht
z+SO0h-cPcw-h5-x8+O91VR~Fu=F;Pv)6NFDxwTze6S-JAe@|fk{=a2MxADnZWn5bm
zx$D`i?2xdqWg9k3_<Ns;!NEhzc8mA#|M%llr%XL1Cbx3R>Gy#ZMXPRZl+yJsv9g=*
zpTF<tvaYVKE9>L;XMI0;{(N{$%pK0z_Wj3|`;P~Iyz#cods0l9Ghch){)N(p)9<eM
zvO2V4?>(>mQLNw;QeJTF?XRQf?|oRNd-dC~`V;Rp!wz2iua*AsaaQfwoSn1&-C_Cl
zul&*Mt95mu$I~R-PW+4i@$2>atLx+Kr%suop{C~c`T6<lCnhSdepCMAL*mz0SG}*t
z6!)f`omF~g{q%YB)=3(tg+$J`uiv+5(V|zT)(i|Q_Ex{&8-8}S`SrEi??r9=_Vs#v
z_OCB5tKRK=UUJ#jJXGe-#zhnCm~N~LR?oe)#q;OSpH;8dZZ|1><dT}2y3%{Pp6Kr#
zck_CsOub@vl>}B*{o>qzZ%gK7lgdv|yw2xdUe+sjBO`p~<%<ESCRTd87tY-Kch<gh
zdj0pU!Wr}TJWonZOUrt3VPTQg+_dAI66>#f-@PLHX@#}L>vyyEz1ea5+cWRoJw|VT
z{dqqB{L$V0fB#Kf@AOUT#$>Teya^Q*JFLpyXn4N5x_bJ)ACI{IK9>J~!T7w5vZt1Y
z#s$^sF^g{8h|rDLu;AGKKYG)>^`@^rwd;6(@XXe{_Mmf*w@1D*Gk98WKgTaUU|--h
z(b8z{(rE7u*JA$}YnZ=wb)9!DI<e6AslM&1MY`IhCEu@i$(HZ8v724fy7QCh(^Hc-
z`%elfC|JKFxt#SO#{$+LfB$tA|G1O4y^UX9Z~Ez_-|toX3knLVEoWw6*!ocW)b)q1
z{U^gqT*|_?9Gbl?_|%TyqBsBLcm^F@rh4lC`_-bq)A!HQ^ZfOong7dj`@bi9PAucT
z$yRV{OQw`zQcF;9aA-ilf}fwCXFokPHELVVOpryq(#sw_O4|GBly=eXyQ1PHCb_pl
z_}WYD&apBm+)(Y`^SI^q>=*+_U%eRtA1&6b@vxTL>m{t_V{ve^Q}hY526leAD_gI}
zt*-t3P1DXU?(XjL>zh(fo0Pl=SR1o5DEs=lsY|;0J5Im7vorYZt*zEeuSZ$uZkRM_
z(ktz%#m~Q1ZB02Tl<OxiH$`6ZO4x=tRd$BnSU0yeBO{|N+1K?vFGZ%$T?$HB%5FUq
zRHjau@<PA<XLnrnThmG#IbM0YnE$`7@86PrZbj7II6uF>N%Cvge>td`B7NZb{Q7@8
zZnN>ptoXI($IYz84<tI-7*^=NU8c9La7y&KO_f%=lIKox`|J8}U54(dGZ!AkHDCEN
zt3CVcti)8;uaQmxkBlT37!p<q_ZEGw?h5%4x=cuKU*N>t>JwM@ES}`<H|hEE{HH%N
zSKqwYEXJ$Az>s0Wv)|t}dXtvh&dP83)BmJ)<y_yKB%dB6ZTV={smZ>~5z&gj1q~P&
zIOL8?KMgDSz<yFtt=h$B>0jB4zE8AInQG>KSj@=6z_6@XZCg<H>;Ibjmq#4E9rpUe
zqvKa|{>W@jZq<=;U|>kFntfO^{m{m4hnTb0t4i*F*NXT)Z_&9u0iULxj1P)<sj*<a
zhJW7+md%gYY|nwr>+MUFFsg~$Ijelin?$Q!zfX7N-kT%;v{-)9>eO^$VK(zV1||lE
z;862ZXE#63KH9%KzASu8(k}kcckchEDJzAUg-<Cmo<D7MYWmKoiwxJmo;3*dU3^D~
z{r(bVv)z2UH34nsS9g4Su95j-t=e+FJd=j62f8LZFfjaBdUJF7>$&CkE?!$3Eh;Iw
z^55Uz+3)V`e061I@KHPKvNspv|NjbK8@D$~uHSunOi|}GyPr#DosFy$T3=APH#Jp=
z0~E3=n#+R4y(jVQ`8aLSd6@+_-d*=@^FO^g?%JY#;;WwaN^WX*KM^0bs8;`uTDn)Y
zspMR}<x5=W?^#rSeqX@Z`u{6(wjGF_w&CBp&rIK<dPT(>85kO@vd!Mr9W9Yw^kG@9
z=)9s?licoV&i_5Zi$7<&N@d-xMVtOKd&yNU2j#Ge%Vl12rOUm(=Z2qrx8BDu-`1po
zfnmYDpSH)Jy`6S{*U=BM*0m>|6@<M>E^hHQn{8uXC;X#9%$=Eof#KEE#2-_4KeF5z
zRT@}vxonb3_~~<-ES0D1{>1uouj;4i(=@(6ey)00KVDJTmZ`Uxfsw&suhk|S=cVms
ztADn%Yh9BG{Fr5xwyIC(|8%Zv`}J?;biFH@cxZO%gd8*B%TdDM&@p#6GA=1OE}msp
z*LUdAB<<Ypocg|~yPxuZc70CWY@O2Iaq-^rb%7z1pQ$a~ss~C!0qJdt8s85u5B+mD
zJ*(_%j-~&FE0&8Msd~DX*K5`X?ptIvPkP0SAMy~j?ncHTTU5O@lmF{gSA-m`tThXt
z^2Tqv%E}L`eJm&6Iw2>j@Bc0AY2A-CNxx&%(u1;%7e@<$UAFm9ueXXPd)(ZxOxw&S
zDo0DCl{`Pm{q)?qI&RYcjD?3D1&iFzpOf^vhqEN41Jr)G(!4EL?CK8xLkC_@G5D~n
zjlWsyA_Egc!(^$a+v@+<-7UQyd+N-Y5J$&GP%FUw{Z9Arn8Mc1lm4-OetkCuebSA2
z4jWjzY$)1&x2wNDynX+kb(=P1Y<|qrb2{<G!hLpk|L$~_n_sGTcX{5cZA`DWh26b>
zUS^r9_p}%9&dc8V{HnIguDIdQ?D=|<c286HuDNu0x3=3(&r6Nhm!E#G8S-d?{V6%o
z^^0`1r|z%X7v)p)=f>NZCYOlFiDfobQ$KzFEGjOZUM3S<pq#<L5b#*T-1F0)FKd&|
z?+jTtZSIqtOEJE`{`-}MZ?XBkef|UO#fFPOxij=n)s)#PJEP{Ve6)7{iSjKDZ4bjQ
zPScUx7v#F6%vf73s}r0oRIm0HO<uBWy6?q><<DOouaDEcDZ8n4=any?s-O5|WX45Y
z;c^EhAJwOe?@V&PADe0WescJe?;@H{SFvA7di1#KwXs|Ky}7F|IqyEh*%bvYXhgN#
zcE<iqSQ?mj?@q1OjSU{2%I^YFcF*JTxAgVZlh6<U+^2OT;InSRg&iM21=otgDYFBg
zO;ov=v)`&LWwZC40F@U7ECLJ+FFexPxb{b`eB%9nmBiC$+GnQTuU_-9SZ!NagcJ8u
zoocUnZ*o69dpG&_=kkzE*^?Lf*`I&%#UfM&<bTt?qRp0vrWB>_`m`zT(!uTNr#{Gq
zMobp-k}6%UWc|fZ+2~b+wBvQhpS=tC<?YriS)u}p&g-wQPEz$YDSF~zIoEIVV-_B0
zqvv|;Wt0Bc-DN9le}B7b_r2Hr-i7@7-?tr(nfih%hsl$73Ysu5aMVRcMtXUBU!G-}
zJ!R%h&Gpw;CmwEFx#GnQ+rPKAWG=4#{Vnv*_Vn{|v9({Xc6D}M>=0D;@#}Zsy4l~5
zZGHK}yr?rKHs#;AxcizW?^>qMy=$W9?q@wl)=Ugnc9#W<%m4O0e|ze_&`Z~1C#l4@
zbN(tk$x^!Gr>5(cmr+kE_1YER&obRq{$lw${uEZd4JQws>N?Yxuxo-XQ^e){O)4*M
zWv{=wE%)}9d)4n>zFxoo+Kr9Lrg?WPa_{W8c;pEK!<GJ5%PRi_o!s~K*3RPPkE(fi
zQ*=z26suVZUS3){Rc`-}N8MhYo)>?8eQla>fWh<9^7(bEdU|-izP%kj>C5;1|6}{@
z|LwT8K7Re?^LEw`W->CY&_6!=*+=i6@p|E}udNlm`OBxWrtvI$acd6qkL;g6e@<F*
z<j9e(t^fZ1e*N`&{Ph)qi?1y46yE#sn6#Fr=EVbz%&Y%O$@RO-^)G+4@%GzkDm&k_
z$?(07@qWBXdTJf7THN~W=gzM^eVxPYitpVpfj6bQSN)VMdC<&L$^SlV_WkoyH_v)o
zx89xg{+dfq!~ci9ZvOChkKUm_*H&$}nzn=M#{2KtUtU~1b^7$>S65eSU;F&&Q_0oP
z@XVi|o^o+>2Zw}AxmWYqmsi?s&8_v5(VC@FI`#|<%OBlLpMUk%*6gnjn)z2nZce-U
z?(@gv^4a(HR95}_`CL?9Uj9MLGc7&6Yd=0d)~yxYpxVc<Z0XX>&(F?Ion`Mi%dU3U
zr%#{0JZzWOvb2ok7T3G7qww*Ulj`$VL~qXv-FFV!MD04Bw>>)d)|QLR(r>kjt)%w!
z9Xph2ZnAlM{N?L6pMGS&?y4~Ra{KjPJLA@e&zw0^%BmzI;J(_UkU;a&XvYoL@0!-m
zT#|g*hEK2Ft@ZY`6=zL1e|_<8+NF1|=5H=P$mLr4X7!IBKNi&3ukSX<KM}`Nc*$uW
z<DR$MZg0uBsI)d}tJm7-?du}$Z%l5VSM^FW_uih!^tq+eR93q8%l&#X+217po{jC_
zFPB62O*{Q`(&?wGHog0JJ47rc{O~pQ{`+Dj;f&XhOYgq?lIN7XK&k89Yfg6$F8#CD
zBE7P$Yg>wD>YMwa&pLu4>*lGPeAYGT?$6KX_4D(#^}YM))){`c{LN3tA9Zo!cHE}B
zj|*?&U63fT>+iSQTN4g4t&Q2a=~S<NcXzj{XIb^rdR7Y=zMp^pH9TcuSg`Mk$=St8
z`_~&i?eGn|l9}$!_0#xZK<|WHhRc^OnbiNQ+57w5?ylb6tN;G~R`vY&@ncs{PssCg
zbFXepcE4M6TDR)SME5CErYvdY7S}R14(=AyHL|p2U|7EH?Uc#Ce@-p9=%N~95@<GC
zvGa8OT8$Lx13f)Fo~w#n-*fBlS@3$@Za=@}!QRt!E?!v~T>5h9^i>-+1jt&KUGdi6
zyJVuW`@BopYv-EXn6&tT*>ZmVQ~h2H3pQHKRAig<<!1W)m-DLMUA(n5`)Ps2mCfn?
zy3yNwCQh8V{7Y3>t^N6vCj&n%PFrpee!{Fly}0o6%jNUefx4~d){FK`c9_w@$k6M(
zdu4a&gWl~Twa*ximhF!-s)?HSb(7fFy#c!UXC~O%I8A!;^JTPb#bdjp-+!-tRA0U{
zr>uRS?T+BN%k?MgzPU1MN5Z@}A>uwvoDbOq7#b2T=iK=_RZhKkSFpXErQ=feZQ(sd
z)|(#P?|NOj^3nO<xku~HExE?z#|o;Gd#|i_-}$raQ^>CT*^ACgt@!)@{rXSO-cC1O
zxpfxLkG)D!^HbA8SS0gWm>C?DL+)y9-?S%L+~nE%dFyA$9ln}#ho{y2^#bPgca`5y
zobTh)o#4#O!Qh}=l2X~`#s4<!(~I5O*G`?!?|)g@32H%BZ07TlshK>db8YC4(q)l>
zf-OHdBp4dpo_VCT^}YMyboSW!z=)Wlqq~&V949^T@msrPzwyf6Q+L{#W(gkbRc>Hl
zFcE4?1XV9x`+xhKtuyyNr*>95m%lq-&E03wsiL|yTi)4zntbR{5R;^+V;?9q+#Vq+
zqe-?lUi%Kk{NyTr`n^iU(`>eHVeR?})qj6$-P#p(?@rFswZH1MtUoPI+Qn~dY1_mp
z&&bSh!DG%DjqN+^rl0&URqOlW`qS}23qCA+JGJPlkEL)Fi;aGhI4B&yzDQYC$s=PC
z5E&Ucb)NX;&yyz0Jr%3Jm#=-7UBDs~)B?_0{D6Vs`1Gk$mu}f&^7Z9q_tezXU$^i7
zTb6jZE%Wp=-LAfWm04GO&F?Px{r>;YW9K1bJlq*!ZU-YFb}YDi<>>Lc%b&}Z=1<*H
zI$h=D^~9#S7$Zm7yt-9MvyT6hFWR#3L&G%tjrZnk_%D2B@dJs>D05Kj)MP=t|7VTx
zEvZYpwyl|>ysJ~m`dett|NAH3^%y8C>44f=4QxSoP53>#-Wdgex~Cn!K`#%^Jr(_P
z(H{4@pi@u3&kz@z<^+mdX3hD}F4i1!o_a00Ixgtr?+?q~KI!nha`5`QiRR_<;h&Wh
zcJM1OFz_x&nsxjtf7q;NfwJM<$>AK+pQf=ZNgWV$U|@JPIZ@)&-w!X7FWan}7n?fE
zz0`~UpMq!65f!r%8wYS(@#`y(G+1Gyy{yt>(pgc}-IrDK-`R=gMy@=;SNo)+C-mjP
z*i(OBSnj-45?bL^J^AusEztPq3U^cA&l@Zcg>3rmExM-Kzb?P?(4$GBrEc{{zyF^r
zS(q3I3X&^}XDzF2IcggE`C~ED6t)#|pjg~rV6^zoDap2l6P4YwUS3)nwIQMLSdXOe
zj{~I%S3MOLFtRW#aJv2__15QC(V(Vb((ZVDNju&1RgvAl;#N#EPdgzO_tWj{JAJWN
zFV;CvS~5F6Zn<YR(|s31LFTOoa}yXCezc}Nei!buU6CvQR@g($<(ipS=6>4Azw*n0
zZ(hHr%KdPBcV1kh@`y@t6H}=JGY7)~&JgocXW#2yeE3~H?9m4MlY)~I!b}!i19{Y<
z?{RO@>DZ6?pw{Aw%jb>tWdhhiev@(ksj>aZ#q#hqqV@a3RIB%e9C|R@$A|3-FQ~H<
z(AyQ5_xO2;)YGs{zrCkEeRuty`npwH{x59Z{50!sYxEukjSCD+3?H~ndyBg5-*4}F
zX}IRnt$qLRENkihzu-h)EwiNP3R6&Dpl;!^z`VX0Em``1IJ&c|f{N=Kk2SCfFf_!k
z$ebcAzy)%o;`5Ycm3N9x>;C$1n1AZrxoc<4keFL`D^u%jWo+-_JF9|cKW9h-wR~IV
ztXZS8bm`KS{`2j$w6!na|M$(DSK7=cdRtClNy!$KmEr5-W*H<l1qBC}et6(`x9oQA
zr&@z(>+*L~4nH*5$+RG`HHTT^L}cIdb8!ndZFo`Tb??VcnKrps)j27Xj;C|K$Tt4O
zaiDX{>|H-@<X7EW_hs#*ZSN+j{Iv49(C@9b;_z92%|*V~|G&NK^=qNSCG#Rnp1W3w
zO=`ZgmRyOBpJ2SOmGdR@`7_^(ja#RR&Xhe^qF60|;8XqIwUL|MX3m`XscNs3S<Z~E
zu2ugQ+J8JEyy@ne&*Jh7YnJWGl$*M3`m~IrU7|&1nPD%d={v08x~bgYJza0@mmf#P
z<3qf>x<Jh@MrO8<HTk!;c=p@<T5)Y{^l~xXH6Qiu7&c6L?E87bk|y5nsk=Y1O5PKa
z&AsZicHKP9!w*f=H3S?KZ!`Rhs;k?lqG@QjF=_3N9TAU@_g~*r`T5G8%CpnoRolo_
z+ROttM<zYlBwb|DQ{{K3V&@yTrSZ%sUw>u$V_N_BR$7_?b7^q7L1F0UxEiFU*yr%u
zhj+y;*}-R<s(&i*(lyp!RR?3X?)#>A^Y9^N(VHP@`{vKqJd?k+YIU+PkMfiM`ak~G
z^~%{sRaRC;?X4<3`b&5F9i{o_m)HOQeSeb5?6YNuiWx3nzHFL&tViqh=jZ3cLqbBD
zQ`i}<+}nKKZuQpe>#q*7%ZF@EJL|V&{q@&ZO|Qo+-m+zj*L)GhFH__hPJu@~=iDsx
zN$gZ^$jr>VwWqRp>9hBHzu&v;E?>K(SK55um+Ldla%ZXe&x-+#iLUmYZB~`AI5ySq
zb&TP&P13V&-d_Lm^_x#Wn5*ACdG_TYW9{DfPgQ$QojG&m!b0b#B~j0nm0m4-mv9N(
z{1X2z8}qM+Yx~;3+1Y8YFW%K%diQw!ywiCv?#TtMJb&ST?eoivII{OFpZ&8gZyBCu
z*#8%c`?n+?=QGW`wB*u1P!Cma&j%-PW9WL(&X}T|F`?IN>t>(QDft>=*?(W`!|G#F
zhxt{OU%shP#3BV9Je)UW^PlILa;be9Q=dNf{8c%t-8_GP*Z$b09p9dx5ENa$*tPxZ
zg-zzE#k-#5Tt6NZe#Gn9p82y+rY}tUUg5Nj(S*kt)anq^iC8dIJA75`?{8DL`Omcq
zt^aXYUTbaX>ubKCZh+q2FP9wLSQ)Ntp0%v<hK%3TKR<f!yuLSi|B`^ji@sO>z0vd9
z`%O{uqOXq!4-06ZJ2NwLmVN!cd$r$Vj~+Xg_5a`BNlSkGsBm?2TUGh_+10oo6KBne
zS{lj1z%qT7T5-!!wUsyX>+P1`erjN{bG?6g{ksXruU~k{@VPaIdB((vik?yy1qn;P
zipNzrUXLx0-MjDFt<2`BQ>KKhkKb(gxoU6Jrj$-d1FVC=q|Ws2ng1syD(`x~@AsCP
zpG9wPZg%&bZFY5rVe*%!XJ&Tw_uD_czWLSfS)s4bA-BS|?%c6+o4CttL(Y~?2?nQ7
zLanc*?PZ$vU)fyCcYAd*2R|*84>_j&`OBY8U$d%y-uL@yy4FhY@TyW{PL&pE2L^`^
zS!VD4dG$|rySq|;{+tDum~V?-T(<hq>}{*Bys#9GT5+-=yR_n>ve6&An{Te|=n`L{
zyRMmy!KBKxw@CZ`aXC5H&+Kzpq#PI=7MCpw%zIjM^yjJnUrdjF*`u=IVS|DzGY3P)
zl|G&G0WTNc^^&VC-+KG+yd>G0?6x_cE3=CJ{a{$m+$8**fr&wCt@2Dst6h(|O{MMK
z6AMp<Zu@O})au8vy_?$IwNl^MZnFHbk!=pozF-Ch(175nzaKJ1>%Y&O6>A%PPA&BP
z?N{GLULTOYoLX#g^-I^wO|7l7Cx~$PnKUpoxIOoHc7E}lHxC0pi9YlCRXJt${opms
z>;0z(J`4Pm|4-|1afsddK+j3GT7BG(4N{=GL~8CVx1CevZmhdhdt1fx=J|Uene)Ty
zYvK|=o@Eji;Rpg5%NBUqgx|C3+s$;-`hPXKH#Ru3va+5!Yg@Kt{j$J3!IgKj@30^E
zc#+{6^XlFv=7z}=@ie6D%xqWg*bxC5LDbv%M96n`*_({{l9T(^SP7k7Rcg!yY8eKc
zzvUZS^TymZKm1piXLPyeq#9YZ7spkFuRMIVT;$@i*Z;rS#ZC&@^jzCB==+yc^*uL>
zoj1PnFfo3ewq)f_P#Z@<!7ptVd&2=xv%qA><eASH6$(IA9`CwknQ{zF-JpRQ-c?|B
zKbru<1+a34VsL=C_+GYAIT=1fJ3esTo}Ir{JeRhYos^SPb&m<QR7&<e&DUZDN(9X-
zGH11`h~`8s`tyHZ)W)|5{;UlB#u)G)Y|Wz-wNjIv2~D$N3=|nM8UBD8P79*cjZdvz
zvc#qS=jr$_x3brteEc!v$A^bezt`8VjNJTS*3s;%of2Vdr9nZSRpJ}J%y#|0r7w)x
zHMaf!I_cr%Piv-cUGcs(t1`Lm%|pRSOXj40E-5)~rP}+e`%vt~s3|iS9^K2f@~0{P
z?3jZq6P4K3wEbpaVpw%G=g!}*mu4;xeQ#^oJT+16-4$&2U2&@14ZW8)Qu+_AH=0`T
zv9~Mdw)47YvQuq}CfV56YM=Ly_Vwd`-~#fC`qS%+@7Vm_?)}fmb=&L6js3<edu0lJ
zpPpRC$RcG73W?rT(odi1pPDL@7@GFmYSHWy;Zw4mycpEG*#sB@l-+hRE^K8JU?^}B
zU3_O<-G)mV=G7%lPqZiRU$WuSzkQ2rEEfqg$qFCP1jSXzX#*bFlV>c<PQ5MPysD?D
z*z~=|&BG-n-=~Xa!$-oOI~->%;M&2<!>}OAw6|#U?nR=PYgZK6|Jp7ixN+0YMN?+~
z+!CW}efnw_n?S}HkRPsE%?!-zY|g(VI6Jmo%ueI*^H7)H#ZyeaTx&>YEy#i^*|woT
zmG}4LNo{{M89EuRh)X#za3rn{TYKp+zx|r8udip{-&YH5cm{9$d!UhdmQiZgH2wH>
zd-mAGRy=H#GEVC;TmJuG(Bgv<oouT)Z?bLJW~}o+v|Dl-wCVFpZ{fvNuTQ8PlVjvv
zfA`AKNlO-6Ut6i79<}cCy6IvzD<I9%K2wXf#Ek#i^;6HvEo)g=uBm9_m3>|J!m;1)
zE+^ZYOua6cna0VO`b@*ZICAT|4V#oUE6>nNS<iIt)TyATs9B(C%`<0wD)+{LT4}DX
z_Wu{&uY5kY>i^&GU!Tp+*V5MZzIpSely%vf&XtS{>RiiStZiZ3AAUvh@9G@w(`Umn
zznQ7>?G9ZhmVRR9l_*W+ywJ?dm0a6WW=;@uSOA)qUFtnOtm@CBZhb9PRoB<o*I#d7
zWS%mA{`%+h>*GL^kw?Yjbv&ycwu+ZLo0*<<c9yBt`#qn}bnr4TCjZzosc-J_@N;RK
zwRLoS+`s1qx@`A8)5W%+vGtAMf%wzs&rerTRa5htbZvdS{`%{y6`k8c-dx^UoDP~r
z-hAH9SXG&UWs>p6tulqaQ>TBORHNEzR1+ur^NsG)Cr?(anGl%gpuv4pxncj4@4rnm
zFR9F}|M&CNt*xt7GXMYk%f-(hK3UCI>+S6|k;ZTD@85rG{o{`nPd-*?$?d+I=Q-(g
z-ie0>Qff8IPe21=wxMzBx7$5t@u_^5a5g^ritL=y-F=_uJ=r2{pI>Zo<yF`Imj@oz
zzdBz3$8Xb3(aZbV&sjc~shwW`XT4z8t6%%X)~tytpK(9xYuIri-h`;AS!Vh7)?8g3
ze*NX^GxKbtjnmGo*!1uF=D%HCpRT^lW?wvW@87Le_a>=&zq0g9NKH$dC3{fhifoMB
ztLW0)o+a7--(T<9x4&k8mVWHF9bH{rpegDfKPo^2?Ylno^zB-z|2{h`aqF(`?!Qv`
zWgL@RYnUr;=WfruxX3kXYu41L?Rl{q@9n89o;q!smcIV_OMwTN+4)w0ngTVdnYXrN
z&a$a2I-0!w_FA*tTQ3@W%cfe&@`gDlKSpYr`Tt$D@$AdP(h8x6Kf{}5x}~!@Uh$pV
zwR@TV>axF2-@mOo9(VV5=$(!6pXx64$yz^oTig5Y-<<DT1JbYVI=p9^CG)!-I9fg5
zUtRU~TOO<(w&sFpc#NXwt!=rpzrDG+xbE+-(BG!9dedX{!l#z@-(P0L9cveV?(z1k
zueLEB{Biip^f`X%7p@0LUy+SDwcq^jQuoklyVocE&3NZ1dDA+zzb7yvrmJh)x@pQ<
zetkPcD*4|Az3WQ9x;;N$HnYBOX&!gbx^3^X51z{^{jGE5jhz1pGf)#2sr3{#=eoGO
z{Q46oJdPee{`#17{*@;uCr_O*W5tdg7I*7@zkPLmy?i(WGl%5T-}|pOhW`)yXz_h<
zz4xR=yZORCt+0NoZL{c3|3BUMz<Dx;oL%qBHvThu>2-JW#WhD5m=19SPFnT%eXDrf
ziW|>gTwHu??e=?FDk>@xfq{X?o=%U?dvkmH`z7(p_V)KDq{!IS>|oT&*yH8x-P_77
zF12*&(lmEYjaSR^Hm9VfO78!8Hor$qH%eks4=cA=$gQ}5fP^JWmrAaU+G?bvq|{@4
z-sbX&6DL+&%{E=Oi@9^x+R*P054ZDry4BU~yBX*syvdz`ckwgr8YLwq&^YAT+2-s0
z)`#BqRPZ^%P@tr4eAanV(KNf2lkUcAW*&L#)s?@D+1mV-|Mq1s@14nUom=-#bMLZH
zb?1QXTVJIKd2rOT@GvY?+T@(?TK1y$&f9tO_l1^R?-reSD|V8|?axA@dA}Uy)!v_5
z#LRI(Y6lM+L(`nqS7%!7`dd6zRAySDgx1Z!Q}^t=*_Bhj?dC-WlOP#JmIj6C7Ek(5
zMSfdS_v?D;q`QhYbG&vvDRbTOGONslp;v@gfq}*H`>*d$Pu^R$EZiqNBBsmhTi&&a
zXO=(h@C-`XJumGz^97EJ3``CJpQ~+8MStBit8cG+pzJS?Nr!@OovtoeerNuEXJ(EC
zQFE9%K+B3I|4H5SGCaKC)|*S7cX$~aObb~A7^HTB#%xa8FSx{PAL8@5VC&0Occ%WI
zcve!+J*La1+I;nk+5J-rKFa&}@GdZ1%*fK<25NTry88#_UGuj3^H2HHbdghn(-*0U
z|BExUb)O+B>7eY+%)!tb<C0arb?&AYJ8sJwa(c;`Y8Esyu-Hl+sM=6=D?wFNb*3z{
zgYwp_t6H{yJ{*qPU$^($+uO@oxy3Th%rF$aoxAOox`EA?3(mV<uiMQu<&(tv>#J40
zr)3oXDzKb;Zq|GOr4_fMN}oS@qVY3ubMOpM^(=MbWM5JC+;9K2Y~R1&e?8^%LHn;q
z`drTbzJHxVWOX0g;&->Mp7pr=0MxkUT>}|X@apg0zCP6Dc3zQL=DiCS86rfsH!?Q3
z9SNVZda?Ds0H5!PY3_^yhcXx#7YJ(J+2i!>gNN(fi}&AXFgQu@DlnKRwZ_-3c%jFf
z%FS(V&D9{_2J*Sf$$MwP<9k&fKI%8Lf$Zybc^E#EEB{{LL(SzgWmz3M9&05qWF(mO
z7EM-x%(rVNUg%9b%95aH(!g-xK$g@~HBZ<3>n8DN?mF$dEQKXO*Q9}=Kqzb0Geyt)
zb-&jx^O?D5%^IDlQ>TK~uvGniyM5{`E3aAhYumylR9~KK>#D!B#!9kj*V<6?3<kyr
zEvM8~u9%!XJbSnQ@A!FZX1sG2wJlqg^DyvMZAJgN`X9oUi)POVKcJbU#3pQ*zzef$
z;`Udm;2PD@JY)@M98yV&VUCfM1H%gC!yjJ1;D0T0y?hPd9=q73?DYW=HO5Ehe~)&Z
zd-4363WfkyP)J&R5`TJH!&dA2t?eR}QTnFR=D~sKUoJ0X<zJO@{(9PR<_0YbCJu(u
z6p1>|pp@gEMXsm!sA%rFnF*T0<T9&W&~YVCZ5eaJDzK_29bJ>(@L5j29THWpwsqq9
z+7nAc7z%1k8W;lH&Dw+4JzKu|%?bah*Y>*CO?q5!Ud7gMtc6X0!LMommla<8e?Upx
z<5%S@_nE&JsA#^svr+5UH(}A7D6RawZFgSZzkc!UeC=;@CZ-)@Ysf0(Q(&0WGHY3-
z%iN3co`>^8|IIR=y8BUNtLnC(%cuV@>$sLaQ&f_n50voyoS;d)<@b>i`DHWS9cG%s
z!I0I@CeWaI#pKl6U9Z<&_SWAU^3pPWe(g51^|9G21tY^`dDE`$N{m&!&2TL@FMXBe
zLPqc`o_A31;yZln{|5@bt=6o3qT(5}<M4&}U$-PX(**CIe{w>1jcSVYfklgyzP`BV
zY<{Pp`B<N9bn5RuS?iL6tl}>~<#~^!v52_%^89^2-9Srtmaty908UTa43^6na!y)e
z(7tenM7+WxMu!8fIm{MGM>u*V3?J>d{eI_jxn3E|Ab#5~0Y{_S`DB>{c^#BPH$BgO
zTU*rhM$oV;?{@6FbK%}z2G(K>SFFA7u^oH=J^SOMqgIc5j7@5O6f9k`<i(xh^H&eG
za-Z#EJD@W0?fJk@D^6y8+a2k(<As&%-OZ84>F1_Ae6FF;Z~;_8`#*Vbuvtq-=gJ(*
z;x7l7`7_?#*%`H~WaY6w+135_|02X=O9Ho^|6F17slrCI|8Cy)CCTdi)qC$Xq~!CQ
z%HA4uR;ZLc`>tMl;M-l7^FnStdu8_EdGFcVS{qZNb7z-tzV-fZ)Vgi9(iW+YAA(vz
zTe}keSI0$%t-pOLr!BGNPx;QXX{$>FJ<7_qMQzPmx_bS-sL#*OufLK1<;BIWzP@W9
zfw$K^{r#`^NE)wNbAG;kd|+T8?<84=ztYBOJ<I&&uKIk=`hCmq*y^`iL8FFk{PJ=O
zQW%&P?f{REh1b>Xd-UkhnZ9H$Z#hOEKfkbuh#9*|UIu0UJ!5?S%B!oZ`+x7ryu2*)
z)02~6@7>yxx!AFpO;b~I<-t#8X7E|N6-)D`JKQ}OS$_RX>eY4o|Ll!@e?#_o?YzAo
zCU9|aZBbDV)V8z#SGVvO(&S(CbFBxx-(zI1)Su7RzOjFQwzj(Z@i!dHHm5L4$jPm<
ztNoR6dt2_*Yx{q{v)-C}``eE5#n4frxqi!+?khYMxP2>2_T8u3jXqA73yplYuj}jL
zo$RS8@2A)rxqa)1c>4c)sO#K|@qIH`DvQ5s+P`MqnsL$TT-jcoQtK&2a{JE)l}`VE
zm+#vC=cTDzcdZS1{jnv7IU*ur#oOE4!)@;EEM9Jwduz#&BQBuU@VedaE`^52E@fn9
z%eb(>5tIh*)&H+u8?*D$o(l|8XFA<>f|~J5*~?cIeCGP;-SwoyGvrKW`s;(9;ja(A
zpCfd=k%7Zd@ixP{=<R;ZY`m)~V*Xuxd!$qN>!WV{SD#MnU%%{czZSGC=zRUZ%~qwa
zmTbRY7cCxB&=@(9@ydggybp2j`s@EJo>%!y()Qnv$Foc_CoKuQ^e6N9|402{`{GxG
zsyj#Q%sb%aw2e{5qF_PRu6<A2r1LKDn%@yv;KGpgI%Rp~6zzZSf}j2^-zj6*x$FO5
z-KwPjkAmxb)sh%EQm5_VS7>-3a&zH!A+|qpf$3lD-`S<IwwJ1WmQ?-!Z2ctLl>fcl
z45d!Y911BCZSn2mw##-DAJ^LR@}vF>CWh6E8Ce93?quJaboO09&iadK?u-p?y=(#w
z9v|M^*ZcnK`xKL+NjYY(=WJ$Y*m{uDh{35T{k{1o@u#ns_q?u;Gx>As?~lrz*Y~cz
zR{yKN@*%^5i3=H71cVmt<_mJ!EjRTV-@d~2i_Y(gFudC~V+t38l)97ygX81tmu*gc
z;$QLQ0RJZUV;VYou6-xs&wkm`eo}C*LVD0^26fhkV<1Odu()W_|0J+{mMAwv6CbZa
z!vevHw_<ab>sY<~Yx$m^VU;^GheAS@)YED6=B>LPSM6K<{at89#g3}4ua-t`PRqPL
zCy?>{DGw8dg*|Kn4$am#1M^&?yHei&tTvUr5z}^k@z&S(LiaO@ufD2#{-mI&{Dm_;
zF3bX_G8h<Hd{Wx)b-lUpd&lj?yLr}pv3U5%e#M8?Pql1!1W$ZhWtL;Xut6Y?g?B-q
zQB54Eq@S%F^-%Ne**<0l#a8782C1o!-p+shp_>2XhpHzXo&lRzr?LHc9M8Z}D#OTf
zVTH?*vheQL|5rY{ZnLK2+uo8FcR_0>XUcLjIC1eRG`Jm=lT%E0^4=L$8usagb;{=3
zp+Bw`ZBAx%@L0&ma$$#L8;|^UHO7WtM=To{n4Y;TzVk+~@YMe|rJ!ZQeKTBs_U#JY
zkT~%aSCPoomrTOJ%nmO=(PE%3^>mtvjqg2W`&SnZiGGqTJuN7?{KK>5tADoa<jTKw
zZOsV=fk_z*jB_Rl`+ojVwfEQW`~TOedQW@NE?*Z=Q?sXyM{<$>^D}{`r|GKpPI~5V
z{ZG99$)|=Z1x7PE7!{@z@+mX~2Ze+zk+ZGJxVNV=bepKSc=q8oUeKZgvz!|X%I{UW
z-`<vc`NhS>z8+i*7U0bQHx-+v-hLZaQ?tkF&(qSWt**0W)e~a)ocX_gefjRZ?57_&
zX~#JwR$u%3_e^5crWDVurSIqcmNwn{N6vNb#rHeTe}D7a57hJstN+!1=uwcARo>o>
zujZSB7SOaz70o=pI9$`-KEAN9FzvV@t2+auiN^lr5tDq&!h3E8GBErHwPO!Tq%J)b
z{j+J&E}jW@MN`XU7!Lfuz`&Ft(6-*E{oY(<PcNxyX+=^D2lOv8Fs&$mdNw}tpX1UP
z)5(kL87`bCkd|Qhx@ytS(`UmruTDQLn0Z!s4I{WoTj1qqa`v#`+!fB;>8rmS;9D9~
zx&F!b)8R9;JvX@@JMpYwmZ&tt2X2!F2EUV?%PLKFChY0Ui`VwN_5IL%ZQJ!Xukvdt
zZLNOL!~|{*9MJ6ac)M;}u$XN5@6K(E3{vcQEV1lQ&716Mer%XDY0{ZKVS~8!;h;qT
zCnhRCEs1)ryf<LwZ<ob)R9?<4zjqN-PJMlS{q>27%D?`6K0kHtT-h(B^H&A@{A?Er
zTFNqE^5n%&o}_r+=deoYXSkLY_i|2G<x+<inV?nfFJJVPYMtzy_Il6jrL6p~o=v}=
zd1==C`#-qN|A(i#2S0gX?HKiI$-0_n#(zIw+V*PJzB56SnfiP^1WMT_#umHHlx5_Q
zQoPMD?Y#Ehhi%d(Wp8eH-LA7-zs0>@?$p_{S7poZEWEhbJ^SviQZ-dJCS`bc$S_ua
zTFIM7d<+g5TsPSYj&ul0878@Gz5VF%<Eb-eMrOWUxqM#G{e87t)6dH-UABx(Nr{1>
zL34-Tm;X<mKR=zed8OxMwKR7|28N6P#cGxhAycMI$v8DdbJwp|tNkmCRvdpk@%ZDF
z6>rLRtIAq0zwr92S;e_yi}r+RU)5o)zpLl|^?J4CTeq#Z-+W8Xez+}p|E@RJcEzpP
z6ZeWwuJq?Uo^S8p-`Z4lH!STZ+lAL(v+nFDG~#iVl$2DzQ~vGE%~^)YZd1R4S{s3Z
z*Q86ozV5%cRw?3j-HQCHTMZ|zv%S0i^RHj4a;ysuERR;Xd+Vsa=d7Ez?=Sc%rTvOe
z&U>29%Fow1uDyT1Yg5$SqeqXXxf{$~Dt+M7v;6Px?*4kQxIb%;NDbPM1gM6awwu={
zEp1sFpKR93+iWb=cVjP458eIm)AzEy$>kxD-=E1w|GqA^Th7vV7HGY>{f>29Yq_W2
zy>RAU-77vN$fBNu|Nh2Chpn}S*6TUU5%>Po{{Q>^*N?~j{;L|%tMl>#&HYP{uXo=m
zd)8J%xqA7ViH2O=-V@(vG}gRx{v;|_-K=9ZXRUnQht((M>cpok(!6>0$;aDG@5(g3
zDoVUw*Z=->!ry=Ev{zrvT^iM|o+5qV&>^SX`}_7j`gm@x_14VGYN~2#t6aOqc6~af
zy*2SLn{Lz=54-vPzrVi+4})ph*xXrj!=c&wqQ>?of#t7WeA{{|@{`NaZ?)X=m!7l+
zJz@>l{`QBH(T|%Ug9A%VK6T!_b)aSGldfz|Kks$^_m9W@rUefUI2>ikTDmM#t}XG!
zzTfXI7oWFXzGsgOXsGPdJTt#@N5V||in?E#xftGU3$d{)OHP>o?%y3n|Es~Qj0}#T
z6*`T~?5|vc-_2-fPbga?J=gn^Gr#SU`hTD2SG`<1{mQ=DYTx;GvFrGsXMh$@baYrO
zy{;uMy4A9F$5K19g?ZUmc_VyJm^C<PXvse>^ReH%>D!I>b(ZJO8u0uz$zPZBJ7%G^
z9rt9r+uHv#g}0P{G|e)87w1~O^Tm`n;~DPFS6vORpY<?dFn#ED!J$L*w7o)n;5wz-
zU)Mxz-o5B))=kfOZ`M9zWo9suGHGz=_$d0!YtoyGzFKRo7tJ~mK1KY814DybJ7^6n
zXhTw?z~c_j2`WG9OSha4pQC<!eIFMS!<tSu0S?2&k7u8B_ywGN=PR0B=fBbB=Itk0
zH*fAqU}$h_Ufs~B@a1);T<TBukg5mmU72zl_v#1#TfSafXW`UCvrK~Tsjt`2Q40)A
z|6*)t%CKPL0!Egm0N=|tkIwCNYTNE&d3xTI{YyLq*Y4Ixz4LcdtKm~N1_x#imZh_n
zRo=)kGu`<?%5>+4Sq#gV7+AQa91f_kva*8e8qh3ZGdusQH=EC2v;X&z|Le=k&v%?R
ze9X2#Y%WjY)m@Ac%)AN$4*TakDFoHLa<|qVdAME6P%SWe-_MBWaRvdp|3B!r9#xw?
zW0R3u5`)8whgt~-SnAfhAHDr=wOaGKaF^NZiqy7*Oy*WQG4p~GtF*m=wH$-Ok&6sW
zX&-*89M+$`_>+70HiPLl_I1fY8Vn7g7Z{k-Htb&XB<p76&-we7ZEBK{teelZ|7XO!
zH)}tMKAk1Y&mi!~f?dGC^HTo5MRWN7Eu8<)G;q^R(R+KE7oC?;IREc$nmaQ?iv`G+
z=UT+F?CSddELHt9T})d?EwKIGT<!G9&mb!7nAm*M`1=DQd=}NHwp#5fZdUTQKmW#q
zp+Vrdpu+(b_xcEZPcHAArJL^5o{Ie`Zz&wDz3jHY<jIT-PO`iT2}1jqM^vtk3yM6L
zXsMJWdUfUgSv)(!%-HWwV`ykQ>MylGtj2EsrOTJUp3+{Q@&Di7tecybuG{_2>hIt8
z_0!Lm6`6bY*?o3$onM*Atj@}?KugMDf!MQib65BD@a+9^$=mAnn$27C?pnpx|NYw4
z-+z7n|3A+k+v!WF2O9|SGC1+`Din0>TBd*b-gWU)_ddxityOwCIbX%~)csAn|9v~d
zWIOHd!8aYDEXj{K88R$lSb45At-d-_>*n93d)(_LsZ5po<EUz%7kDIdua6%agTq-1
zCXOpkcPhhAz1Ix;R$M>jP1(u!;gcEnho6aTKGVm>;Lw}F&={rh@0+k_E@;8;f35V(
z+q7&yG#7>2sJr`2O4`M1W-Z6SpzgxVamDG~DRGhddsDmK6-`vp+>>cNbtk`8{n^=|
zxhS{(Vur>jgFSL5!+zdAp0?S$?fw!M28KB{eGE)n1#WI!|H-~Kwr|HWE`Pt!myNPT
z^&0~W@3zm9<z;99tqI>c!QzS7)7np6pr!GDj{Yn<qN26zIftp`|8h{YE^sqxaL~OG
z8|Wodv{|VAPEMH!1B0I=uY%6%*xhBf_Ewiq{TiLWH}vzfv)1oE&*)%e;3%7x%kVmR
zns!t0?v>rDp8GEHuiE<mdAFGt|NCnPum4}Sy*N7i<Nt3zRi_`m;9T49%k=Tbn$P7e
zyH;8U%!puS=}VP9aO+%=>x%-Hqv2IGC1+=IWnXeyXLi@ei-o}<Az~ZjpD&mFOK+x5
z&%ClC`TwJw>+9ym)qV}F{rB^^ntHP&&-`%L=rFBy=Oy|Wm>HbrrM_prvA@24ZNx^W
z`?cTas=Pd{zaP|3HBCCgF?sSqo}>5IPTapF;9~5~PGbgzFOlCk9$Z-&{IqQM)t$xZ
zmNI-lYwXU>GW8Z#^RakWY$MliBj*nqh}ZS}RJHe0)n49tJ2z^qH;u?Hv+U8zZr>NT
z?gigok-I8^L08{`){RW9+sgH7**gcr&$g;J?qAP3{i-y2=2-)t?7h4Hp3h#tciHZD
zyS(}B|7<vC^;#$Q))vpnYQC3}`)!wX3af|xp7Z5P$=Ml(i%<Rf^YgQ)sOZ%T6E8aO
z$=R&1|M#&UG`)4s_Iu3c#|#Wo5ru^tkM+y1|MREj=+UDyWsi$Y%VoH|C3Er0;N@Dn
zx@(i(HnPiI&|bf1(c0+kVZZ0R>SNfJdD-pvx3|GTK|#&wc{+RI)*Bfco0hz|uwaj@
z*Yz){Q|092Oyhq_Pk*)LcKn31?=0SZjwy3CuKsOWJ2zHtf9W~lC3&y-rks6wnE6-L
zevvotv$v*R-BnyvbY+n%x3_u2w7(oXcI=2Kt9}}+Aj{YO=ilS=MWv`6a@Op-`}b^Z
zP5d$S|JC-ccUBQg-DW(o?tK0`ciJvp56f5E#Ef6%@w`_*_-kKSc-ZROUR&k*Q>71p
z#-28(oej#$S_KZaJ39jVZNJUP+wqWX^J2vYud>`p=W9D8_c1VdZ+sJaz~AoYl68AN
zdEKpgz1HgU8RM_l_x}ssey6BA?cAKqGLtz?^Xq<jUXLm6oiu5Zh@|Aoyt}(H@9rv9
z|NG~}x92O~PH`<#+m>};3Il_Q-ZWb#32Euo>hmg`{B1tA+zbrx-nnz9)&D=AO*1Yi
z)P6X~o^@->%6<R;RjaC~%=u8cb*|Y1$kwdWy-kbrUa<*O>D*>e+Oc)^u85u5@l%eA
zukD{5yR!ejrOL|-8++w*r-uA(wJANAVepDi#BC8{MutfPN0d8gNcVSfNyfvRsc-J;
zfQpCnaw~7ln?3`SjY0ip$J4X7$KUJq7P+3@GwZ9g`uTnDL4$iYW&DnoBrjrTU^wo~
z-0>h)qR909ik9xayF0A+M6!bxjvrj63R?L#ry+GcliP>lBPu6m&WXxAJM)U0-FL;w
zLW~RoG8qhlTVmGkI#K=PxL5W)WqVB>y`!KahJ{_qfn_adh3DkGzCY*hRm{)Z*0pU-
z$iwO0qQ!jS0Za@9N^_Vy7Cg-=Y?~EV*Z*l#T+qGx_|S+NW7U|N$w}v=Qp!vi7^F<4
z99Xul=&KdIx^ewYLk0#8Et3X=(5k9kX=i2xX08oh?ssd~*CXadW{=%=hRvPeaQE6O
z=?2pa42MMCF4LQ)@^tM#zjIGQOm}C69o?q1dv8*8@*x)<Q1!WD0b|S0H++^!em48X
z85pj}OF7I~r(+&%e{Oe}S$puqwN}~>7j@n_#mdmYb|g0;K`Xr5&r7Ouc~G6TbLZi_
zsrv65zWu*1^3(Pxs3=rODP$2?2I`wXXV-iSYRTBA-C3|`?P}56$ulYsGB9u?$}qYt
zPU`QNXuNUisZX2E_AxOyoLI!@viOm{?W#?CYKvUdXUVcMG)y?^e!w8~o%?z1P08`C
zN0+Zy^PuBX`2YDSo3(eHc7@n=k>OAXXgKA^-}a~0ucj#*#jU#*d*#pf{!@aY`JiBP
z6600yx%y?3W|}(#!-1j<hQRgD&dgk#d3o8(KI?ZEg#B#_@BD2lJk%WO-W<6#kAWlA
zq+!L@_<pv}Z+`o=->Y>xx9shg`B$yCZV>D%Ud`>ch>^jGHJovA+z;>mS08@&YrLFv
zJGH;V(3+2d!GclB;YzpiOv$b{7u+YltpfRi;YtIWKxx<(Rqv(0%QYS@>IC<8)En6Z
zN`s!+Em78<7yM~y{Zw6@J8vu)7#^q;2y(nyQu4p-XZPnV?~hv7#hLv1^yaS{tF)Nr
z*Y(^fo2}1qb1*QhTEOTs*JaDwg<|G5p_#hrC+(m3WUbv%ynew?W`+fVix^$z-iQt4
z%D=Vl(yX&DC)p}(J7vYdkRfBi4&KIZ!Nif8lA@uk?7X||?Ie}CrPm^NeZN<2nslT?
z*zWy~$%;;2mL2su!obkNCFQ`K{e8cu?9tobPhQJhlXN~?)AQD*%2v03X<MRVzVo-5
z=SDxRO!GTpAPnl#U)h?s>>9(t#{P!?>F4HHZn~K>%OrEs%|Ir-RgZUqg5}(iup+f>
zL5CW$Q~CQRs8`Rvd69v^f%yT)f+hO<emI@C`P{Rs{C!@T$Ysg8`CZ%BO}ihh6u$Z#
zcso4rfls^%iHQr<d}b_oKCjx3-{wQZ%|f5xs*LI1{_o#^N<(|9u1%2U^!tlHJPFyH
z%*fEdmhh9!Kv&myv0Lw@35w2HISjUls_Es8ql|Iuw>uV_u}nX_;zw=8<!M>fkL{m)
z7g?R~x_;{BAB9`@rEQDxU2WE=w>$7<`053DueLSqx+dH6zV*`ioz158xsiMGv^Pt(
zC9W;s9$)cx>vfatYdW<bkBWD7bX+*l$b5Bky1(tmBf?kT%}3uWVv(D0xBC6wtb2QQ
z=I#9&ws|oFL%{m3?rzh%KNU}(KiAgNJNL$7*@icQ2liHff7NIGE+8#!nUa!{-z&?@
z%X}|CIXPJyG~K^7^Rinr8*flhP>{20u0ibO>9Xaw@6}{Z4_p>IKYZ?!he$Kux+SG5
zrNP@-uWWsXZ30|<^|iT^CnuM&T#nqv7_+}_FQ@`7K4-Z+>Fo2*nO9eZR=rxe{K?0P
zCj}vSbuo~6?DbDeteQVbFaIOPzcW%_>(iEdT;17P^)CZKt+mCw_lLdN@!P*bnxp@I
z+REF>Qx@lCFMJm)-1EM5zsQ>R@ZsTzZHzKD6&v>b`}Nu@`+9u+UaRtVYczwGg=GG7
znz>#BG$^VTe^Kye>_x8pTYj34--W0A-h8%?je$X`AyxXojT;fAZ*OhA6jf5PWzwWc
ztJbvddNQ$mhAblk1IvU|=>q{q)!%aVem-Y?Ygg%NC1vHS`|In^&b1DoYgKCX@N-u6
z@w(N0E{j1s^j2Tvjo{^dpuZ+&=cP9s%h%k!a@6X>F<eWr>x#s8%;9oN3EVDfAjIpS
zkt`C!k%4QUr@6Hh14Bkj27~j2`@t%no7|7gn8U)rVB%)dpm@yS&#Bmtb7I!+(%6$}
z4Qi(}xOK1@9Fd8fxA2nl?x{Av@<8dM!L6&?!Rf@rGd!|)Keo>5oxga)jueRME;a)e
ziMEAz_~(axET6yf=GDzlb1vO^<H5kNKyWc54<pK+$|*IiJ142k*7hn(zk0Ehf#JZd
z3<hV1hsRVWEn!~ow|dJwlOj;N+E7`Dfg$5a27`2G=<2YS58LIh{rdWPYyN#Z-G~hi
z$;WzDeyrU6tY^|Q310aR;%lrl8+NUfW^iKVU2yW2Z>)|dXuq>p(W0kT@2fHoHt+iR
zv~<>%WrhDPBpR`l&gTMEJdd53Z+N(ry;ysKZ|~VY(5Vs)pw(Us5j>#9@(o8pE7{OF
z;f&@&|1K@oI(;|%;|`tw4PN|z6sl8t4m}F;@nK_NaN^}nP!8Pnzgo89YrbfIvHh>@
zDw?}qC#d%Cy1XWWfkA=gB7>Qb;L5vK%T6hQ&d^}sUck5}{qwW4mqE*;{{H^H+Pz<{
z>eET}tK0MMKYl0Q6D+jGN~s$(pUTi;Bz2&w;N2ThE$;5Hi`!S1RD4YKlA5;ch~nHW
zsdsn3GJlY?b<ayl(3Gm|=6eQ0y%RGS8Xg=AJ@EC!-=j`Pze%hE&A-l@v+Bsh%uSZD
zT7RrAzsX==U=Yn<c-{H%^Qw~nWv*M^Ms7^od{gw|y5%<Zxu*IO3=9X>UtqW<9KSKz
z=ufEKt5O%!iR(44&k0~)V7LleZpN@dLh3-(qmHi0Ib}b)K54D}-SzoNho6DE2m^zI
zDah!v4?`+G_i5eyn|gFs;*ZcxuR$Z%W`>ds3=XMx*p|s1{VKWmaJPJT#plbQM#!Dl
z_ohD74fNTr2&!WlRyZ(6^n*IN@rC<7h3!|04_tRB*bj6Pz)ubd1_l!?lZLMeZ?_!u
z^78t1e{tZXd1l6b&6{7PaIC(@%fMj^T7$rlU^p$G;rZFw%gf&0(o|A%dOojuo!$RG
z#haBG85m47yv-Zd@Bg<;&41pS-|u$&OPl2c^vl_TCKhFFDlUM-i1*kf-Upxt|LnY7
zo}e;fSINso%jefc1qKHGI$!@U8C2OfuqEH@cJT7{-dgv!%6GQe+N862+p|whQ2g~~
z^Z8Y^eaOR!yDbj9{%V%<z-<Y5I1#ilju}*gMXbH|>ITnYgEM@IZExP>Y|XmrHNWoH
z%3oh!hi|O=ez#m(TiZMP`nt8BFghIzDj69Z{%SzB_D!8XfBlXf7MmZ>IJC)~Av+n%
zNZ|e*ygo0G*C1Joyjqs04X$P8%#qowJmcIZ-h`r}O)`A#d@>dRnV`jbS@-r-ZarU!
z+CK=~zBK@hBG%?<U#+s4wwFW2#wKQO)z?d1qS{*5{?zRcTN4rZQaO=<p&=morgFn1
zlgdvizH=-B_f~%oyT7mY>k(o96)RSFfI=2jBQr?#ER{a6dfhIsc0Sold%xdX4ayqn
z=jVYY=w_K_r$K}JC0oFxyxnijK-(qKjx#VYa7dh<2il6B5DIE9Gl+pUY=NTRo_&Mf
zsrD5jA|es5<~hZ!4?lYJXwmMwS8^FaW7`gQudUj>V1dJ>^Y+KrcCc;WJM~`W-oD!E
z$&(ov8iJ=zp1k<SkBZI7Gn&{o@CgYEt9n*{d$aLM{DMz{71iJ0ZN0+d3J<wIe`=O4
zTNd_5X1kKq0jmwS`{z1I%FE}k%%4+KSXg)^SMf@&?v-5Ku(iAYJl`Q2xxI*ifuS3;
zK?IzStO|C<fEIi|DcX6aPbdK5wkx@;S8~_xsy~?)@ly(vVYwf1C@?T!FQ9l6bkEK<
z_kVk9Yp{z;3#j7>>T;*KbFHu}es&^lbLOQbo@wrx1$OiOFE95G|NZSP=%517u`SQe
z%v3aYF1orZbk~<l-d8uJdf&`3(~a4&A!+vY*H@=#247hlZSLyszB={vv|ZnBWlx<u
zH#hD<Rnekdr)z(|-7aNYwdK*{$D*>bYe7@I=jYir@<;opou3EdYM7aYX=-YM)^={o
zy&aZ0f6^qOrAwD;YHE5K8XE3;)TLeW-~i)fq3ivVSMHvZC?RE-bfoaVAIs#)E35zi
z`}^zf`}+0I&d%1hvWj{>r?~H4-S1qG;+`I!()aiFK2p2>_t)3U?D91Vo>oOqI`a1a
zEh~L>#dG4siC(_GSLgr#lm7Ji^VxH(3Nje7AA)xEJ~=tL^nUI4%)@QGuP!cbZ{wF=
z*UZj8OIB9_bRvrF=QGBy?(N-uGmt6i-JPAMPMo+9>~Fg?fB)aG;^*hCg2rjp=hs|%
zlks)ciWMHVRbMXbsr<YuM_W(tS{tvlN$sx^(E9H8dp`GV%e}qq;$rvN6SmaNU6JQ`
zYh!Y|-i`-MrSI-UYKO185*}Z>^yTH{+Db}F2RvW<hlNeMbm`KTw6jvFX=$fkf6cnF
zAyG?9Yt@bC=d9mf`Tc(Xdh`1=!I6=Xr_P-N?UVNPi7i(TF5e02=bFtv+cM?r`;=Gv
zzTL{+ntR)9@ArGvzka=5Z(8;yqF377Pue`s#IW-2mb|-B#m~-wHcW2?jkDE!Ji6=6
zrqf@}n%~#3wvN7e^CoDk>6XgRX?uS>>IP-EygeV?HYc<9H3nbXo*%EQtGo8m#`DjO
zqqgN_o|$KR`$$M*U_d~^oVs5x-O|#Q6<%}|mn^?i*nV@O)SVT9i+iN=b}YPf>&C|9
zUQS`P8B<m~^fVIFi6}T!3o3w}CvB|${_a2vSHr7U`(CYD-IKj;=dn2sj71+FI66<7
zICW~OL|fvMr%xsQmS3*;eAZn1+s|*e^LagwrO&TT3p-Qw{q?TXpq=6wS65Bln16rY
z<X>?dW>>Gu%F5>M`Sa=Y)NkHCJ|^?3-|cibo*lr+$$4!5|G)3Ewu5$ayH!-|c=)x7
zdCn;r8Acn|rAwEB7K63%NCs_8+kV?L<%B@(x0~r|YTP|rwwUxvnXU@@yKr6o|9?>%
zlUn!verN4@X?lF!%B!oxvyXHLs;L`;)?DP6&7NhFnN&7~?eX{DTPr>$%`H48DP>o)
zBPb}S<lWBaY=7OG_J{r37@m~0NG0>{udkn~_D<b*-n#tVnk!eXOsJawyY%j*jf?Lh
zwm21UUSzuB)vI^+_wSFZ|63|+S>#f;KYsn*Z?n9-yi5unI7|+@ra%Aua?td)tE=m)
z>+9pU{(b^(!22a8CNlb^^JRxb_VdZx>C8XBJnihP(5U=tYi4fCyPFlX>Hw(EuqE*@
z8>r8a=FYidW$^M-Z_8E}KR;JwA@k{H%`DUGX=*+*63R?2Bh8FCS2g@O*vuXp5dkVw
zUVuid?(8hyTJkceSJpaAzW&cfy<IPqHY;D7dH?<OC7zQ@{{4KuI{W&%Nquwq#pLAT
zmhJ>O;oJNB`A(;Pg~#5#w6TBR@9*!|pVr%b=12+e^NP~_ACF1zdbMgbXvg!04F;~R
zu9*j$SYO@PxVSU(^}E;W_lNDT`x_Dzv}nc*2~cY=c2~*DP4^!jZZ|D{=5x2^^I5ee
zChWWley8s3tu8$-TdrYjY`o%p?W$h$dlk<Azt{g?{`2$m*K0PPyR^)Awn^qCm9059
zgM`)nOswM$RPK!fWtX-@4Q1uSZ!CCEz5nh#NpJTXqq~*Q=T2HO&$jy2Wq<ptm;LQ!
zg^-sd&z4nPuxj=1y^!5nB3GX8z51l0{vN21jf<OS_veGNs=9ji!$Yl7=6Q4Mem)T{
zeSPihk}ajzv7QhWn#FWCZ@XKs)Kb0JT^hEwclT^)O#7C8;oz~yA0_zO7oW0JydGD*
z*0AbJMzIs?fhJaN9?!PJ4_Ex~pOm=yCQtj}#3@s!&XnDFa7*6ZTNSTXF4tOnc8=xc
z7gttJ_Ux3J*H?TmI5TslVcHpq#ns>69ea4VomqZO<KJC*{l}AAT3aQJ)6cEha{q4m
z{oF6_@7o`E`6@mtYSzVFrP+_$<?B{#*_U*b%X!ko*|VcFZB0$DR=nAGoN2~}M49#P
ztl#Ze{K)q0^!LSOyYKFJv8ekDXLNsG`1fybZ}WN{1C`-x`Q>bG?07mYnn`lwL0%C1
z=d;=1-sIx={dLD49Bl4g?A||XhRz2I_75Vh?d_|>*T+R|OmaOs>;C)eU8353)p3p*
z5|AEScJ8e$D<AE@x3@a`Pz&d+Et!*L_}Xs@O8XQRZhZ6Y)O{_!_Qj80ObDGK|MU01
z-|xSI4mQx#Jo(0hSI@>KruNsDl|`p}-tYVEmwS7gsb=V1$+m^J^LG1ki|Jf&m#<y&
z?d|RGTlZFn>s!t}=k>lAv^rs)ZFSSH+I7ozE%%$dYW==nt1kVXqXJsG16r%^;}K(@
zwQBc{9TAgsKH5G1{`=|23Kd!FPv$u{3|iaUU+?{XZ}Xky(KReF+>_P)uN~<WzPh_S
zA9Pp+Xq>XEtE=Si*Xy%o*B(4_>{yspyvw9Tix-17h^5+IiK%+I6qFx98$_=~re{7r
z)?4*v<MEQiyyi1x*T#v8iDiK*^ZxR8cUJn&Hq$gT4AhO<a$#zC+)7?)vy6+2TtEG+
z5q;jIs-^~+5iKoSv@19`xb%8#`O8(S*IjC0WH!mTpr9MOE9CwD|NFw$#bn04umYVM
z^5^I0ukUuh*D^EP_UOEkon73-i4#GSGri{bR-Bxyu5D--SXfvHItJnD`gr@f<@YMp
zmapGC>s218u_#`*WZAN?%vnvIzNO{w?nHu)9njr=XOX*HrHiMhXVi{@g+|84TQe>y
zO}=~`d<0t35sszHmaST_zyY)m;?z{_tvNS0bv;|ZV@JgLJ)gSn<ZZu`w_Rmr=H+FI
zwE>HpPP!{JUz_Z2m$_uwGO33TAG+P&SKDiveQk!U=7Tjw(h}Qm=iVtiF6)++wMtM>
zFmX=Vt;}P#-|r+>*vM^Od=<3Z@5Y3Xr~5mF)uliMaoKE@$B+B%&8(~D&ROxU=)C>^
zJr_47yDKXx9eQzbar?%ChfbfFt~Wpa8@cbpA#Ty%GPYG$c6_}S-P_W_qO7HLYR8{X
zr+aIEf14@3OKy(O-K)0hH}aW_jY{jk-z`4|ss^V_5do=tac^(+)Z2$xW=rSoIGAyJ
z+gd?6xjd8HTUS6Sdt=SdqQeG#|7-P6-q=~ZeCI*tBWV(fX6G0f7#P%#ST-;)z?-%x
f?c5>8`TU=;{BhuW?I#+yKtA(y^>bP0l+XkKK;R77

literal 0
HcmV?d00001

diff --git a/test/fixtures/resources.py b/test/fixtures/resources.py
index 6317e4cb..61c8db25 100644
--- a/test/fixtures/resources.py
+++ b/test/fixtures/resources.py
@@ -16,12 +16,13 @@ def simple_collection():
             PlainMemoryVariable(0, NO_PORT, {NO_PORT: 3}),
             PlainMemoryVariable(0, NO_PORT, {NO_PORT: 2}),
             PlainMemoryVariable(0, NO_PORT, {NO_PORT: 6}),
-        }
+        },
+        8,
     )
 
 
 @pytest.fixture()
-def collection():
+def cyclic_simple_collection():
     NO_PORT = 0
     return ProcessCollection(
         {
@@ -32,5 +33,7 @@ def collection():
             PlainMemoryVariable(0, NO_PORT, {NO_PORT: 3}),
             PlainMemoryVariable(0, NO_PORT, {NO_PORT: 2}),
             PlainMemoryVariable(0, NO_PORT, {NO_PORT: 6}),
-        }
+        },
+        6,
+        True,
     )
diff --git a/test/test_resources.py b/test/test_resources.py
index 67f20cc9..10e401ad 100644
--- a/test/test_resources.py
+++ b/test/test_resources.py
@@ -2,8 +2,11 @@ import matplotlib.pyplot as plt
 import networkx as nx
 import pytest
 
-from b_asic.process import PlainMemoryVariable
-from b_asic.resources import ProcessCollection, draw_exclusion_graph_coloring
+from b_asic.research.interleaver import (
+    generate_matrix_transposer,
+    generate_random_interleaver,
+)
+from b_asic.resources import draw_exclusion_graph_coloring
 
 
 class TestProcessCollectionPlainMemoryVariable:
@@ -15,7 +18,7 @@ class TestProcessCollectionPlainMemoryVariable:
 
     def test_draw_proces_collection(self, simple_collection):
         _, ax = plt.subplots(1, 2)
-        simple_collection.draw_lifetime_chart(schedule_time=8, ax=ax[0])
+        simple_collection.draw_lifetime_chart(ax=ax[0])
         exclusion_graph = (
             simple_collection.create_exclusion_graph_from_overlap()
         )
@@ -27,3 +30,26 @@ class TestProcessCollectionPlainMemoryVariable:
             read_ports=1, write_ports=1, total_ports=2
         )
         assert len(collection_split) == 3
+
+    @pytest.mark.mpl_image_compare(style='mpl20')
+    def test_draw_matrix_transposer_4(self):
+        fig, ax = plt.subplots()
+        generate_matrix_transposer(4).draw_lifetime_chart(ax=ax)
+        return fig
+
+    def test_generate_random_interleaver(self):
+        return
+        for _ in range(10):
+            for size in range(5, 20, 5):
+                assert (
+                    len(
+                        generate_random_interleaver(size).split(
+                            read_ports=1, write_ports=1
+                        )
+                    )
+                    == 1
+                )
+                assert (
+                    len(generate_random_interleaver(size).split(total_ports=1))
+                    == 2
+                )
-- 
GitLab