From 9fbc3c91f771fd8dbf96d3ad16633655590a6587 Mon Sep 17 00:00:00 2001 From: Chris McCarthy Date: Tue, 8 Aug 2023 20:22:18 +0100 Subject: [PATCH] #1706 - Finished up the Node and Switch MVP. Added full extensive documentation on what's happening at each step. --- CHANGELOG.md | 7 + docs/_static/four_node_two_switch_network.png | Bin 0 -> 90397 bytes docs/source/simulation.rst | 2 +- .../network/base_hardware.rst | 635 ++++++++++++++++-- .../network/transport_to_data_link_layer.rst | 13 + .../simulator/network/hardware/base.py | 74 +- .../network/{ => hardware}/nodes/__init__.py | 0 .../simulator/network/nodes/switch.py | 0 .../network/test_frame_transmission.py | 58 +- .../network/test_link_connection.py | 4 +- 10 files changed, 702 insertions(+), 91 deletions(-) create mode 100644 docs/_static/four_node_two_switch_network.png rename src/primaite/simulator/network/{ => hardware}/nodes/__init__.py (100%) delete mode 100644 src/primaite/simulator/network/nodes/switch.py diff --git a/CHANGELOG.md b/CHANGELOG.md index dd7e3466..dd8afbce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- Network Hardware - Added base hardware module with NIC, SwitchPort, Node, Switch, and Link. Nodes and Switches have +fundamental services like ARP, ICMP, and PCAP running them by default. +- Network Transmission - Modelled OSI Model layers 1 through to 5 with various classes for creating network frames and +transmitting them from a Service/Application, down through the layers, over the wire, and back up through the layers to +a Service/Application another machine. +- system - Added the core structure of Application, Services, and Components. Also added a SoftwareManager and +SessionManager. - File System - ability to emulate a node's file system during a simulation ## [2.0.0] - 2023-07-26 diff --git a/docs/_static/four_node_two_switch_network.png b/docs/_static/four_node_two_switch_network.png new file mode 100644 index 0000000000000000000000000000000000000000..4283910787c45b0fb9148d92432e19f033d80c85 GIT binary patch literal 90397 zcmeFZcT|+w*ELwSt+oLX6p*Z_Ah}U;6h#maP!!3aAlU+uoM|JH6$z3hDmiB;0~AUI zg+ii&faFX8RTOh>Y5(4L=C4_^zO}xYHE;EzyQ}I6_nxrN-sj$@o?g*Zq1nf>4}-zb zs9sXi#$fi*V=%j7|Jn_o9I0}62md){eo0#cgYo3XVEi9qFq`m^|4$6YLvyw z9fQFz-HorhDhpriHd9wo!fc~|l4~;~;FG;~FX=mBFhVEL-wyY51y}fRkF%=A#XS>$ z{dGW`&t~QVeD9A@RZ_g}-ZwMo<<2zrQewd{Vi#9E%Yy*DBVyUF1CqrJrVCTfc{FE7 zGDh`f{dM1rfwm;?o2}tYmV2Jwo4}r|Af4E)G_@wB1VQzx??}%v;hE zhoXn^7oMxG`^Xczr_V%KSy?(q9wGGa8w}=B=&|Gf zd6)LL;`#r)xpeD3nBw;<{QaMM|8tFFkM`34@$Wb1_8mU?=f7|Kk8of5>)$sR-pk>K z{_{pV=6`Mkf%ZS9LlN;m!GNsrzaSV2R?_xkj*0mcm5FLn)P1`wI_m2PKA9EYU$?17 z|4vxZ!shY3P@I5iv@^44p%soGxHfb8{NvF7mfc2&x`!I!^62G{b$QRh+{(xm93Xi!+SL)>sG4$PU);uE zu4IR9kBt6?z;lM--SUEy^wy~-cVheva<^=+b)EWk3CtDRN32cHh5ixelbiel$}+5lZ*{G~r0` zk1*bIxvuS()T{ojAXCS!CdLgrLucclT3pH2Qg=S_`z!1HOLb&EWM%>E0q`27s%RZo ze50N+Y+WazwnT{9_is%S{bxG{$DL?YCVr-0%yhn7SKc^j#iC0Yiq<*SwJ_njKtJ#A zA;1C9%fBZt?T%ATg?2|+&!_(<_WOT9!K0=`cojG2%LM1WtyzPibgU9STf?+t3=OQi zSR;gfhgkAePiM8^xe0d#e%_$LGhJLF8FBO17ARK9xEf#3I;x>9lL8hPMe5~g6VutelPcmLnTS0+`NFd^O8%o0h_YM(9( z4DZOBvFYbRzmhUpgSFQuGff?%T_3lLmCMgqzsurW9M`2waW2-bqzZ8f8L-olC@M8${&!`||Hud^GylI5M!z^( zf0)>O9i1DFWYa)kRmTOkRnM`oyY=zbZM52p%@OK+NWIl0i4!MMFLXu!f^=Ssh9)iZ ztxcLmi>p^7=X3S19px0>wmOn)%*5VT8Wa#9jN=RXC@nHsw8Nhp6<*Po_sho{YWJJI zS+8=MFg9PE5~GoClb2fywfAxkDJtsfEwOQ8>&bL0aUclE8yX)nu6BJIzqR%>AYjXB z?XWzRTO6~9e327o(Y(#fDt;#*Ape1%1CHeDJUJl6#f8oZV#Kizuj3^^bo(_lty@nP zVHFlVAgT*$=|K7#B!oo?Wv?7FA0qE64V~{wcOp;wk}fLMAK$}y6kRbV$C%3s=ab?? zTI+P$j!zT*Rgb6AOL7F3p6m#nfEDNpqi1oRM+4 zy!ngYT%t?2uvlD^nZM{uY0@(;;2t7+6aQ86&^Wa}h$tQS?Bu5)o8mRwIxe3lp$<@}ZErV_<@pKzumKgvY`U((=dGI?2V zW0Ei7&mI0Z)Isncqe$G=8|qvbB5m_?aaFm_(vxP3wPuov9af~*-=b?tAEi>~E4Prh zzsCiQMvxg6d-~_`6QTA81qp_*Lr8VYiq5P~2qwi{=l^8zq>K^_iEX zwcrmjZm-v?N@lXxmB-sUUtM_8o9pCSO1jfqA;KtNbVO#Z;zC+_x(X#Qad(qO;l5MEmjqS^_|4)~@?UF0u4dOxUIg4gp1jJE^k`z-eore@j^$p*f;H#4$De5MCCh7>5e zw=FG|=S}uBAkXq7+%;P1t@cVOr828K?L(wv!pJFww%t}D+aVI>V@*0R=3F6T=|62h8z}8@qrEb1Ro8ctGHZgUngv>h3v?TKT>Tu`+Q}V8f(_Y zJx6>wwAwv|hJ1-zH_9(NW@;v+5ES<)5eBQ3dW5Q#^1j}gY^B+#BMjzQTlXN(txOf!& zsBEKe!{&XZy;s2s_wnP;aAz9~M&~aOUP?-qnV~3`bSH0Dn2lu?&W!p|W0`g-s-&0x z6J+(%8rtak!&>qT5qZ_Tnv!!ej`g>m;NR}x+0Q_QgL*={!?yH;-L(nq5pefsBXm>h7+Cf;RL#~1~?v%e18 zCoGRNi%c{`k3Ke>d;J%~8fk9tv#)Q=yUM9e%H)O?kHs*#Fx4>6*ARULj7loL zw&5&Z|4j_|Fk<+G_;j9@<=ZU=WY~g1UP1mKE35XW9{Iuny5ntOBO@b>GHz|YB>XL( zgHH#gtr~Cn(1s+ECQkauU@);EpncCB{@Iad%oge8p5FGzp>EX`u(sBHIk$YYwS34v zYLC(KxNv~SGE@DFH=jpjhUL#}uEh7kQZd-`{8lrt5>fE z#g}W%n*g+5NFlu2qnsrF#Jz1FChZ6^r=|8#=|bZN&9Hy~qWs4oG_(Ac1cCtjepASm z#ie1>5E7^7$vU4O-yO*!Dvf&^eRxo+!s|*V1fK{g-^q+}U&0&!LxQv+?J{+J@jyA@ zog9Dext*BsKqM*+F12qtrA$zy7DsPo>dGE5ELl%jCuQeLOtO|ZY>@~s`G5$ zsm(R{lWkJdjfE}gojW#D?(ld8qT zsEN6;$YJ~)$gJ>LeQ$u?wRMl0g|6nsk>PQC&Bp%N88%KqV@5Z!uff0~$>y0?{Vfsa zk8d2IcB`6FvoHJ)gaKjgG0c7*maaKQm09|2&tAS{Zsc^jz9Re0G!6YG-;oDGm&DouSwdHU5<1AxS0ErYCgZ1jwxX!{h1a04GP@=(jgqD^4t)p=$h& zn~1#3I5~~Hn+$7EQI8heSk(Fr90ta_Fv%~ewz%p;D|d2}!}F4o5;dQ{JLK2re03_@ zsECq7Ydn~#?8+)yD8Bf`Mm7c?PfmIsqRjEG&t1>!u4DXXk@McVetS4tcbJEbm{9TK z{j&L*=|sJ`R?EP6xo>l{T;1{<$$Wvh*(i0Q?a*l|WOH$iys=6Q0>UDNDv5?`3%n$2 z=8rqJl!wuCz?7-0C;iVLl15A3Js3fMwX{nkoeJ%};}ZV_1qF@1QjlZRk{9FGlwYin zbo=p=icHbehFeJiB-CPNV`2bK_N)wqY4`2MErp$0q4Kq~PygA{Qro28fzghljywy} zNn~+JJq6G^F@D3D3<**`Ir}XoC1rg%_~qgR^V!jdqoJYd!Yr)RqGLgY+twKXl2D#R zvU*FbT;`|Hm&L<%j#I=vO1SyNZw(d@2IqkL+?0tv6-YCv5R6r>NuXzqy4ES)6E03T~v3 z{9N_ zfv`mzx%&=mzx}XCU^2B`iNgcc5pKi%oamfRPITO`VA5Zr_QMP+#U3R!*7~`hPV}KP zoaW=}%P_ZqpeqlPmGzbOer@#tQXm+HiJ_xIt`25D%Y_eRrh}ZX)M(}q_MNDeG_Ut? zeDA2z__5e`Jl#{Tt^R1x|mGmXa-LuhCO8{E4_!I66%(1=w+_R9N#38>``&)ir$5J zD^8Ctq|C$NcRAjjEV4MX$78Ya35Wg4hlZtd-+8w*1CkOX_*`52PED%s1Vj42SVA?L z{aPmCIQ}0M(Dz z<;KOXPwny&4_HUY^A~Alj$tBk`7@P!*PVo7`wV%6ChE6Cd_D0U>sy0<0ai(VB7jIN&cRTuE51j` zj7;kLg)FOtEgYjNtJt??d3@S=1VuTy@|~DV#}UAZ8XyuhS+P7i@XQLITdoz7X(>ne zW-fqSyPc8*C6k>!+EiSKD$t71;F4QyvM}G#=L*d7qMma}5!A`vN0aTfe3>APuWuH9 z3b(ah`Ot*?y38aH_-=TbGgT#ldD6y)YH0{Ou1H_^6`cbnrz2k@`|iIRwCD z7x%|Q9yT{;BhSv>UNO69BwYRfL`%ONKbm$v;uPGHw67k`Ly7ia&9w5Kj*;hvpVA!N zlZJfC=)Gok6e@2S6CpMoNngCofMkcr<9|n!nm@z_bdzCd%(I`(^D8zsO&bS1@*7_>g8y z{>l!4Ei1o8>)6i85v)QX-+mBfz$+va%4%PEA~A85P~kb!{tJwx&?P%kdK?ChUYo0s zp~#wBriM}Tn(~tl(ODiJIHcsf47V{H_+~WztRGvLth<&s>Ej8Ont6!+pkfu%BSiA| zcWJ_Cz8Hg+(X*ELZT6)I0hlru<(*w@Xv z(HJ~VfBpJ&#@6>&?vzK-<|AX#NH<}CkmHK=oGW5Sqc0FS_Q_iE#h`PFxsiwc_^F>QszQ|-xZ|gl*6RYQY+uD zE7K$S#_EuO;3EAh$qn+j?Wp)kM3^RFjVYOi+HHTzBEPcYzEJl@gshS&;XeF?vZ5D% z*3zhksA$slYn8Eo29@ZAV!SAyQZFd5~9`;n)@vG=evr3+O5;N)x8iJeE7CFHjGkhYo86K^faUudwhUVg$#zWghP| z9u`q|1cT{24Iqs%2w55nRf(69J#+RfYsq|)RZH;MHhC>K_zDhr;YC@v885e);*O(h zFTEEgDZt@lZE(4U{2X!XuIN92wxBN3xooGlmkOaO95pDXVA;Hnlf(8d(|gcf>=GuVY-;3k7`tg9PxKy4<3W*fou~L(W6Zr~ z7ZJCiX~#j0@`fy(?CIM13x1vg6nO{V%T-gusf|NGY+));y{-T+E2p^Hl0EP(yXX6n z0naXFI`(~2R5b+()6dKf4z}(NZIY9vOjD}O*26R%I(!kOoG@Nlu9%>u<@TY1=pi~f z)SR&j+0;)x|L}vKvnQFD>UV?-(n24RrH&~VYl09eJH!DGJH&ZC=ZRy{ruY&C*&`?L zMn@pKVplBCpu(wX5#dlL*T(F06@}h|wN@Z??n{Mx%x7ZgI>DdFWB#)*5QJitA^b+_ zs(t&v%{`p=gE55Folf)|uS^r(0ax=svW#j*SUU^5mh8s$2W!(n-i_{k_i$WZ#f1W@ zt8ShF`?aN0?I((;A}Wvv6=pgr<}&)ty0f}h`|=@4xQ*Se)c_$wI2%z%)jDNPz!ODQ z^AzgAzB%tmiFXG&9mg0(U5>%D8-5}W$2kMlUNj{RGgPXu^(1ehOeg<{Up5DF@!P}w zZ;VQ9Lhf^&!^p?WYPxstkJ6Fa+No_QrS)fc2>cXxOc{X5hn}X1{|kX5!Z7*{wL;`2&y@EnAUz) zd0nnv?m0?H{XAW(1uAdgZZo^c_bEHX2!9PJIiw0=1Ro$WI+B{+%u+IpN-t?{6U*lL z@f~&kyqai&vrWAsCq@!w974bV1g|7iaZoHBVaV6`HKe|9#Bu^em>N*TnEGy5)Drsv zkQTqyy5ZAD+rm~#N~lmstzeWDsQzf`DIhynhbG|WW*-;m7V&Wor;O#%6tQm+UIK8` zlQ@5fS>q5$am2W)poR^#EzCf@kPr&njDdT`!IDnywM2yG`-SRcIg&)qPA;MNvQ9@J zemJj=8>tjvUs*)oL0qMktowX|h-*s#H43v(!MR!Oin85nu0{;{3)=HO=!Eq#2W}M` zef=&_k5CZ;kvEk?nVAqSDw4KtE+o5u8ycQGkHF#-1k_vCe57K(`CJ?I-*TF&Z3hNB zl5#15TB=_aqhFGFQWfZ6o41w^P%075sL0E~* zwi%A@d9bJ3D+&6^L8$UU7xTB3_M#4gDq-kIL|*M1MRn$+&8>eN+Y`!_+IQCsfMQTZ zh3{Cpk@Ot7VzJNkBJ_`@#Of+4BvIQJT~LCCQ& zm(aK#B;4@`1^5LN1uvXXvi!X8*$n6=L;FT+$tnI6)R003q@f~nlbV)#P|+-kQgIKwF^GhB!xaYUOAP$_JzSkveHd zVzYUaP@4rg*x(^@@Rw`we&io7Kc->%r2q&8(?V@(upLuGkba^uIOr6oU^Zbr!EH#2 zn=-!;{kcH9#M?9;?y?UB2%;yeY$t)@9$rDB|Mwi;cCfy zH7Yh^W0ptwS6SkqQKqYPlv8DlBhmS5xb{IGfCt!vCD!o_5;TU`RHu1Vfma17?0 zI=>*l-Iag>2s6hCYy@JcH}|7b+M(3+;i1fYjZ=bo^M{8Zs3L?=DkirYAeZ-RwdcA8 z{0$3vqo_V|c)4j+L08RMhZ-|`eJ1)V7po2P^W9<=?gt4IULs^krn|wE^R&}kIE2i= z4~RjMAO^p=W*^-4wVE46ZHYAXzV)6ngSCpc&Ns6mkQQy}vS=QS7h$?M^hAA1j8Y(9 zJ6c!m5OxsAIrStp>PZJ!Sh!{Fe(d@cma7HMUzd(LeP4$)zKq|VS!+JY37 z_-8T&Y-=vWW;)ToK`pzu!%&UXBp2qA96upanUJ8((@q)1`D z=lr$Jgk#irJ4EA^@f;!pSMlS%jxYHy}mK679q65AYPKUgFBah@JRbabji9$_3VqpGY)!~4q4Qs43?kZq2iC! z+6I$pX#ZRVzmTlo^)3slVJjqty!rDgrX*Gm(oql?B9S1(>d#VB=y`$bd(Po%i+at; z%{x23Tns;C9RXYL0>Mad6CA;ZNAW$kc9~SlaEsP+p;W|s?@U75G3COOgCdiCaZ_xh zg;cG2-1rPDBBZ}?)u5%>FNq0Px0ZqY@04@s___XtnF`2*K#jSXAa*EM?Lg`qT*U>ilC`IXO+)Ihj=!nbkxEJOqA(5HpxISzqj=QpGi8 zWin-r6=i7hUTP5E=Qj4ai29DwjSLJ74L_5N8IfRd{siY>T2o#fe_uUUfqa394L~q_ zat1I4wOx!SDo`JX+@$)e7EohfTRmEk31tYisR4wonO``}hxoO+%@M6v{hKK2)Pw2a z;4p-pwdWtA!Z6_1e&jfUOEYRZ?n}!|Z!pGHpMn*(^0q>hF9ahu*a?c};K zTd6s1@DlJVY?!e*VkyLx@L@;`87-15-8J0r7Vqp_*B515fWeCnx*M9pNDln7V|v0;vP_ zrw11{c*Qw!8~=3h45ZuDs3SfMI6q?Z>Hc`Q4Jy4->ly<9HC4P}bdpGeMq zg3R1`={T--tnaAYT9;2?$uhni8JN(dRw^@ zN{iRZ!)>#v4kcP}Im7D($4w~C7hr2)cIk)l);J67KjK!cn?=2)7Yw>A!bk8mC444E zmGYBk3?k=UhI#EJvz)pt-nwSLcE`p$uEn4_(c=i557ofhN`%BG=UzSG4C1|(*=;w2 zx{m*3x=YdfXIR+3yr&*i7dWp_d|vl`t}Ln6zR*gg_Gdt^2<&N2&fQ!sFFJvJl9r9p zRz;01aQRbUHI+S%K-C;OEp+Rtq#v2`(yeyff&^qua`(??8^R*VBu&`!nTysjM7!+S zyUc$2CLjZeAa=}@C{fhrn7zNJ{`E5clref{B|HUHE5fjBx`{ayp5qYEB>_pCu?);r z4d+T%($iY?bgOtNv8K%|yIe2*;3S4_7nzftur^)MIOATuD&9GFVsl!vbpik8 z?FKg@dAR&K_u8Ns$F?>0#ZNz)l4Mw333MCsQ9vz%+|elS5&>?Lt+*KHRfb<-o)%M` zu_c0*q@^2YxARH(g;$w8#zGyJ8KJUlOt<7NmO8jz4l6aq+Ac0fy}*X9jxUwz_Z_73 z+i*9oA$jy0(AW@zOWYT#jrr?_axWOfyPUHm&n1*e=+d-epR~qFkhY^rm^0f9=s3L> zb$#?*GILCYcRNR<8}(Tb*TP(Sf4`e` zBzQtWqw2u11`YW$GHCyGSDEiwg{Nc}3)SczzW;c#phN`Q;>T8I2b_sl2sW&e*Jj)K zv8*M!jq3ylvU*qg4&I;mX+L%lSAdP>nw9+EheYQ}xKD?5dV2b^q$JZxzekTAsYdTQ z$9`~nK%U}a<#hU75yhMm%h&JgqU73Tp_qLF_Q2lLn^@~?a}W$KAPos;70qrMaf<#E zg?fWA&fPc_V2rt=XeDsO^m6bjYk$u@gr8UJyR&pe+!wy7ZC8JP%=%ntzrW{t`Ihu4 z<3GYj)SAR~Z5W+a=GaplT&vU6AKbWjGDzIQcrv56f_IY7|GF*|MYQR@t8AyM4by|Q zKHkYL|07=jRu+TQgy_U-PMQUz~cFC+M;q!ALCb-eDvB;<>{KLJ6`6z_z>Mj#wV-3lQ56m`K zCRI(juE$Vrb(V4dd6O0P{Z~+0o3@AMdX5V|o{HVaK&8qfpDci&?l{IpAwC%YaY9D- zPXB+!l5m6&?IlH~U@4x^t+uN!p?1S{K3(YmlII>+SXeZqsYdW&r(ur|cqLWPq((B) zWBg@ceq$&*tG>Q|h`fo7jm^NlX2CxmmPjY%2!?b|yPW*f;7Mk5eozvp0b{kxrR-ku z!uh<#brri*L0P`};YQTutS^gU`C_#>Z1mS;VE@{ZQs7snvhn3<948 zXIgP9!cR|C3Le8?f~PZ%?wRSzGwW|ZICP5QHQD#kRJV*qWw&+w1zIV|NqR6;vi|Mc zx9k0P>;#WJa!o5FJ|6bh)#}8_dAUZg z^9u_L<6%y_cI|?7lItQ?7gb?{yV{_uTJ|a_DeA?=YnN*+ONR@4x=UKDp4m2us|0(E z6+>c1nqJ!IrmM!U>u$;HyZ^9+IjHB(<*J9C%q0ZPNa+_?EIB*de7sR-=QAiLd8-v- zTdAvqa%XMjhFii2H@=qtPFI>fD`IdT@y%ov>^|7|ZbBoiTlrlo0m8J^ID(^zKTu*H z-?qsY7c3p!9=RpQ(*Ef_w`sc;Tb#L3XXaY@&gT5wykp}%|Fl2;Q-bv{H`ZEijF@?R zbh_A7segOtnC||b>l?`)i^r?sDUyEc@9!TiemoI&z0dWlMKt!a;a3#QZ&C^h{ijV7Vw;@L=1PpbkLPVGtHuDj6J`tF-aUiYilB{-n!0*iiPN`Q;*cS) zQcOYu`-R5`oy8gPF{dd}zmX^4Lh4Z`0M#B__lz@DM$4r^f3(P;( zH85ZaViFGhT5Q!=+%At|s}tw9{d2I|E#7x?@>!yZ%VrkX907=0zbA2y9q4y%X(*XY zj*)V*6fmwT>00||=N|m`C!C2Nhcy5+7k{RPiF;@Oj*7U=4Y>b&^~7wb#zWB8*pUD9 z>3~lWJdHm-MVye3kZ6-54*cxS)-6>Sj{UM$WaI1WD_~G^+Qf4*-F^I}6_<>RjHdj? z4|K@NG$G$)f>%HwSl84?j8WKxO+8BBO@+f2i5QlXljF&5_x|DD$U33zS^+r5Ypybr z>$=CF3!#BDkK+tt6byX-{yksgOHmu`hT?oq%EFV*Osyp4K_cvlYv?UBPc1085*}WU zG|sOYB^qbX{@HNV$daDrj9=`F$1REkhWY{ND__IES(?93qsh(8^Y{__$19gvUBiOn z+@qu=D;?k6*XoK552aJrTieBSU+x zADJF{r2c8QuS2ubkoR({<5;9FUZj!j+C=X?6dNnWLf~fD z;EGSV7?j0s>KoR??ln_LQ`aaZB_(3hl@@p^J$pa@d3CSZVj9(G%#~cI-#W1kYz6Ao zM*)zq@+bH9n>At;{uIS&^+e&PR^!UH&S6@Qp+dgM8Tp|N@H%M6pqpBiD8}GvBZrs3 zZdwku2YYDqx)f6jA=1q{k`*%1fIT92dJEjaN~vjSX^ImlOCk?_?*UHB z#AM=rtCUy(^|(!8o6K_IAvGf$NAY4m=QrRcRVE748TZz{S~c#ZmAyj8^|0r6RZLdK z>FbF(Z!cdcPKoMHy;Yrg;yCtwJOd5s1buS);8M@TVuT+QdhF7(gwgyYfiQaP-dFE- zA2@nw=k5cSs)EI{A%&%#e|F>y{!~*_8-aLQ^sh?f@FOpB%iO>kgCfNk6vx?Dy-Ari z(A3m~pix#->>i_mEa8}lchXF)suVV=_!}R>s%zop>gpPNR3xP@^e#xE5C@-W!w?g- zG9ien?%l&pb_R9c9+9}k49x_VJsj>g(}mo})Xys{eLNxNTFM_C5pmcm!Q}=U+*+_N zS6)s|4hX_wK=7we5A#5noi*z#vFWD7FE1_ez4wOv&L`fhGknb5yfdhKFbtmkZWyR^ z%66uYhmu&Tu0YEwF5tO3rBir)sV4&lo0;foo6vqqb!P_j9SwLk|Edb1)=RkZo59reXaKI5`N! zSReg<0G^}bcl3Vz_)%|@nZO8PS{7_i1JypRWdA$hM_CE}V0`OY6dzx|esx@IU}^+v zkODz62W88)da`zFtzS&&lwm|yS69VapI!DHsCuxqMN;I&iR*gv0Z7*ZfsLoKl6b>iN2A zu6oIvZ~c$TxZZ)xN>@w13$WlqNqj*F2PZ>&z++{qoBu|x0RxaQ2I6f%=x&Z``DVEb zhK1Ix(7?vL{_Hc))KIW>#=QKX4MRUN{o=4XK^dNfOly9?$aCpZc6pxjHSdK6UDfET z4ye@yZ5!ihqT!PFhFx~qtnfg=n@_|yvsEmfdDyGE3H?P5*S2SRDYE2Sj=}N^tda88 z2Y**vdF%6JhYswp4-wV^tKeaTW$z<`klXLyZ(~)4<0rDh06+r*^onk)I!}G!t#Y31 zN~3)=?RjW;071&qk53{VOFurgwuU&h%Sk}Ub>AV%Dkvzx{7N;5g=DweohcVPtKH@U z_iSif?XPl~Hp@(K&06u2uZy!@Nd|co4`Lp?# z*H=4hy@-5%pKAbEXb#f1CCXK+g$wZTWHvpo8?15(qHK|L_uU&C8@mWqGGb39qmYqa zek{;Lv)+PRk(9dj=H{T9nwpQFK0ST@{5e1ifUV=ZU9cHOg+21;+3-FTfELlljNr@= zz@g)F;6Yo{DpZo;1nCD4wPY8S(s5(eF4K&}Auk6u!0bpos5fB^T++Pl*;TL9^>;5L|uIgE8BG8LB~ie zv`xFKo|~Gly-_uG>gZ0eGyI}sgbzsCJ%HQV;$a`pgKfv5C~4Lz2M8L^?E!f~ z-XKKWRtIR9abr)GalYDJ$>tIni2>FN>zgy>wY>do-M$_1O9_J`C04rJ#vG#ACqgAt zx(J9$*yUAn17#d7Y~%=PA?&9u?{kY>Bx#dVx?CwZ;sI zjhM;TsMA?_iHJhAN(>u~UZDJ@6|4+5`GJn;7h85$h+ff#6xZ!EHv1TUT*SH~dAHo@ z;Z5Rc;6Za0qoJqaI9B_LSNNpa3dhl@_|vHF@LtRK!cxHocga<^8k?BVozxRC#|QuH zN>?{>>DGyys-DiPHiO{l=E$sRK3^FdC6m=0g}b1ou;rW@3b4}gyr{T1dn+!x>Pios z#Oh6sHt~GZRQ6d@CbzHfFpp+%p?TwtzMh!$7t?3F)@D2jq6kamz1?eV`L_GLV5qIy zZcd+og6!D28%Y*mk-~|7lnq-M?H+Cj&%Oy>W@hGRadCzgUq=Az0vEftOlVj{U*nw; zT>ETL&ZzZI9X`pX;9C>PddcInf`D4p!`(J5;a%e7YvM21dV>t#0L zXAXC+OU*xSNfN)95c0MBxKjtzzM~SjRgaM2?UJ+{#nJNX1h%Q3M*GWhK(KY)LN{j! zUlhC2feAzE(33BepB^r%H4{?09Mk8OAy9EocQj^gy~x?6x=H_i3A(<<*aS z4tFjr_E(u2iY<@2h%EVsIKCB{DR-QL>1+sO+!`*Q1rB&k7jpd2PmX~MC zC$Sqa*OCSj z35oKrJamw=S-Dz#2xT#g|T*KU#R^)kOK`dBt;OCPFyf|&gvGo5xTkDa>t z>gFuJwURC0KmqT*I2&*C@hc!w%*UUVTj0vF?l{#~=*kD}!S$DRtr><63D%Jml9UAH zTy9oF6D9jbm5meGqVgL}M;Y1nT`V7`sGqO(4cS`n1lriNU}SWdgOSy@+~nkAmhFkg z^Bp}sOUuinGsW$B#1G?35(9B(FY8?kD9Cs+f{K;cW&PpuWy7k&ipt8$Moz6_B90^V zK&q8P2I&y5IPV=YdB9^bJ-TOOIRS_vN+zACM}e>X0`97#tPrkSPcex&mS4MOAiMc9 zwbe1mZsqMx+LFF>WL?|O*}D0*<+@CXc9)53Q|G9lQ_U z)0ljHRW+Qn>D>G!B>cM7Oa0_rkJOvyHQOFzmLk6huvpOX(ioo<9m5jkIK93^dztj^ z_1fn}tzu=KZJ@btr_Ej&t?RChd=5V?aU0qKeT%?xx~qQt-D>rDJV4?GJjj&LuX~5o zANc&}dTWB2m*2L1vmJM7q(V#RdX-n-E+(TZqdQ099O^2sD!vEuVSC}>zYJxuk&{!n zX5oC~*dti~?z?lV8j26!@%J+`Gcji@)S%d0e>hcEL)-m`R~0TBE;7OJlT@_EFBs%MJS8u@v7 z--s(oLNz)nYJCMtbvpjkYaXpcstQk9Xcny4!AAUFOYVdV>@fv5)kmZi0z)e zz5Rm^A%^tO#m8tXUf5|$N5|>Aeyhtu!ce1h%SRd(<^pj^&D-O-xy>f@Q=XPrgk)r@ z+i(rluCpv5SFeA$*PK(9K9=!HO13Bs`Z>vpwsB4&>z;eRO=TE=b`9v2OC7KL9y@Ml z7pJ9J+C5|tH?9{fVQW~W5; zcygBmBjXL$FVNp?-L9C(7Yvcum-YAt$+A4ip52b(7vZ;+mexn>TfC>c=0GE$F7$!2 z{V=Z3&OnU~7NXqGXJOfQNOOtIA{!sux6K6%KvSMOt_!Uf8FP$_`$m4Bimlr7&b$eSM35MA zH*Y9JiUb~64S9A#O) z?b^416QQqli8WDqZweELWiKKl8Js5CLK=dY8t47CNi4em#7Mi?UQUoM9ce&dD_dx_ zhsNV-(Q#u2Q@^d%Kr2B*(nyeS)*XIYj(tSMX4KP{ho3otru{XR{(ioH!hy20T;a-w%5l^qe|2oetMOQBwv2vzc83C?WWSCIHOw78{63~4r zDlV@7n@_0RS}&j9{yan7E%hY;WO%qT3lXnkSKoYlsR^Cz8#<)>`o>~#B^4O;NT|$Q z0OABmwQ0`tqhV&HE4=&teGI7Aguy*d_wJ39 zeAcvE2zqX~y@gvPmX(bazZ@7C;A=l@>-Em%)ALh0W!SCzr>}gqOTu0PaL6pwP@9Tv z&ap`>lPBGfkT4?1nrkeVFx$w8+S*q*Osu)I`8dew8WILvG?w4z8XFU}!V7GSwk>ZR zSRdDXR+l>GohGDs-XQl^Yo(vWmHc+CX^)0>t1oF*_tYi#FIQD`WXc?d>vTAJ$H&IR zB-wNaFDy-Qw5cd1(B{Rdd3t)*O1EO!YjW%=GdfdMkQqC(s%U$3p?QS3s3YoMn1{*T zm@TR=l>*rS&*%=edp3VF1?3VsKQTVu<4$|_%$cl&%RqW_ip>>qzFP)!kD;5N7JzpS zLxMdL+pe(fUE0o;HGNsZm%Prh(>6rMSOdYy+M|*w+rFw|N)*uwX&OZ1$2!`IJJ zU2-NZkS97UKEPEGpMVuYcV%q2Z9N=n;wF*2wzFp*LJegIw4Q21Q*(@Y00af=Wka|aUTYSWt&jb)Y^&_!?{5SzR3ebdzO%3Jo%^=HKWSwR&&+f z)k1jL%1k8PYvL8u{tS#W)fDF&Y0C$-uq*=vd|{i@e;0W$OF4Vo)|zcJy%5-WS+DgJ zXpP6ToE$3bF4rT->{|;TXyM_TBooIRDDLhvg^gLY5{7jv8y`3+1x?FOQ4<5rolp1) zBNP~tz^MPugVs@AqE6#2wii-6qF&hdvH9uO+4s}_8GfkM?2?ORjddx9zIM&*o*kFM zfx1=2N-ygWxqYvlHZ7A>9iz=lWf!sB_%&)v=Hp zV7&D4dRbz?S8bQW++r>{0t9GL^7S(c8eE_DvA_HTtD690X%G<6t(aA~lWC#;~w!no_6GQA1wyxkV08 zT^>}eS-`3cl(55%Y)+RW$i{WKAfC*TTD$!*oEuk;dWA?7j#znoIu(7LB=9Y#xGihN z1+qUY=g70@XeQKy)T0|QrHq-H0bZfL3}0g$G@T=Ij*8s<_LlE>g9@jQ=x!R8BXp+Q z0<8DiLB=2D*Uf{@-Nw?G4!(A!OI@eTWwx)h@8HXhqts=ZKmD;O`Ul=bz@abo9yt_mAnaYAtcbneg- zPG9$Iy|oHxE}kJIepz(`XXXls0$4$K#FGgY$;Ixctc!1%Y6N1+P zw_zWh^A?6r_LtuzNL8H%9IBd$M`}+W{ zvz(3YsEr9TM(x4aIf2H}{p$KzLqez8bYnRx7KWw-01*3wCCl)iSINoADS05d2^HQ> zE>qR?^=I2PwK+p2NMMf)u*YSv$7E@{_(|hGTJMZE+S!qZ;K!j@R%-(S zfCHLTq${HvLwd%mCS&=6xvN8j8=N7d54c6cditI^ zOdq-habV;&=n~0+kBW-w*|;ge!^88hItLZsP-62ri>4L2FbyEmxkjd+te}4518D}) z$lnVhxwQSw^Nr!hFR*;k5K&OTT~(N$k~Xaazm^I$8x7KHxd@`*JvY5vJ-j8veL3dJ zgu4CMTpel7(yyl@CN_4&fxNP^@)N`(WH^tSdffKLJnBDi>sM8XgQ=gQE^Uw3OIXW7 z0&}gQ*?zF9L9l#)9!FWtpWb?I1`HQz9t~G4^xcp$pC8_1&7a|?_riIvXlm6svt4Ya zAaDP`&&uyn+Wql%N=n}!YlzwBV{udCb|-9;wDL5u#ITIae~tXff^z&$Fy8kq`n9*s zR&v_ai5$D(*fuFjH{Rut=jnV&jwZYHB0_1m-sdNM!_dfx8{VwxvCD4*t|>OvH*~u9 zsKXJ_oG~wG(R_4xyHO{va;8}?4P@S{tW)YyqMsbge0w)r&bEJ9FYufmGe)aS##PQX zU3>aBHa2w161vcc=p^^(v;{s?50 z?u2qZz#7No%F0oJp6JoBdNhSSxfcXz{+cjuPSqHI~eeGuw(^+XSC66ZNVEZ%u0v^^#UY{9(L z_4o`>7sN~T?}xOqFW9|tQc@al?Z-Mc3zpZz@|xkP19}{r*;t!z=AlyDmTmF+qf_)_ z05hXa{Q1G~Sq5ID+a>PKN3k!izSipU`i3mo1c$!0o$1MVao#s}Ld6*#eVa3e3HY|N zzU*ao>%NHF_ec#*joVJgR_EE6RJ?M^Gvvq@ar!_rm|5sFEt+U4lEw!GKcPz_ZdACL zm8o9M1&H6ZvW;mUR&&EOtZK-c!n+?bPTyHDxe`emdqv;T@x#vV-BOhG(d?R1IatEu zqjgugb4vH?cA9js`ZM*=Hn=ysi8^#MasoZfc3KM^oFHG)c%nK z%fyw&y@y>1d{xJLuFwB&gAZlweHmNzR~?98;5c993aUQc2N8)l!J=39VEcFs<_&)b zZ2l;alZ3HA--|i2tebJB&vT~7Pu&&pwN1oOO-m~b*0dU4Urj|yFG|pm5#TgfGeNom zQVOm93(_AOsLEJk+HI(=fxQY5)8O-aYOV9823v^=-P`17U>10_CM-NPSVXJj29n19hxmarCH!y-^ylROm~gjRxGB@p=|_0CM?! zVB*lu<$*n)H5KHg=4=589|X)&Lm-A?$2btqy??Yn+c_Jwa&IDRS`a;*e4WsIcg&c` zdV-%nyXTNOy@9y~RtKnG*@i{aQj&^!qSc-0_9E|XY~^m8R>|%#0h?H+k2?(Z13$P3 z;zRm3hI3BtBh8<1cxZ%{eE-1u#pf75KfiJa3xidXkE;Oe{owHU@ys^AymIV!%|mRb*m)ri z7@{o52_z4DdY_7bs8{=Y9(9oDi&~(04w6(E%FO5!1@bNUxq_m;|AVpj4vQ*T!p7O_ zs=I4IRI-4gh>Cznl&lg|1XLsoD5K;ck~3=}4WJ^DBq&G@k~0`!P_jfN3L_wK7!Zcc zulgMJhVT3S`1w5dKCW}-Om}tlTh-N7Z^fyC?+|v=n1GTD>Y9g{s}>b^vYtLt>P+DT zH^8I9dj!x4W%0NLW!m&&=f@dP*LPi@TG5j_2*GmRc>VS)|KC>_zqKTa1Ri2sns}sT zy6me6m!B;^JX#)(z>&oCdQXro+=Piv3&Wxuop@3723=F2A;@w-35go+Z-Kzz!9+E5JwnY zSC>{8ZLHB3I#NpF4|6_mWmDAWq<;ghwS^%1!d7cl@pAqJx)nr08x?_)#LnO+!}sj% zvHY0UyFUP$4H^7EC*vKXA*dJ1kq++iMWfyFfiCyH0k;&4C#vR&1^Drrn|{0r82O$& zfU7QdD=6Vl@ZMP0EbI_XN(#1yBcxrUXjk>SzqgQ6lVbzuiGbcGH)3 zN_ozn^$=Z!$oX-&58K-;yJDr7T#fDp(09Oz&Q7_7&OP7%-cHg`m(d)P75ibBbRVpE z@l;`t@pNwq%7X~72}M^jtrvM~{CQ(_OnpBiyAAiedmxyQNp(H~YV5W6k*kyC0&#KK z*?MB2etESVZuqd1B{*&ph9IB-`CFh82kfdDcKyY#rG5Uq)ta{@tk(6U2M6UpZaU-w zKu{SK1i1g~pFH#U-c+C#-EQO&=De5^~5n^rT&*H4XMb8tcM}D_eG`bH*Fk;@b8H>IczHQZe2SgNw zV>f^{lP;=bUfIMFE^9mRm>_XZKCkWML$+#>&J5|bUaiOS zxAEfjsErGy*W-g+@kER#en$K{`_y0NDnk}-9wIKqbLGT=i&vi8qXm`<4qfN3QoD;6 zNCQfCc2g=L!BD;kiFnp6p#UW?A8wJrTkeIh2=87p@&LX)iI}C2!ltY`CglTH?7*CuU&>!poSx^(Jcpss`TC-g?K8|^*$>G@z}1MrRzS$B?8x2bUsNUzIn&35OHZCC{3nBl?g&IR&@acQqLl3QxyL=@2WSax+iOvA zbLoxvT~TEgo*>#IYI!V$%J&~0882x5-IVU%6D92u@3Xl`h}Tucvv~G`L)?=9{L}{v zWJ;{8;hXt?4ReXmLw4xcHM;RT?O#=fVw?xE9UJaI*(j>;FTD;we}UB==)}|;-Lg7` zUWjZftidl!R6+S#PN3Tkm!an}F#o8A-H@Zw|I&dWF$wfBfkuGZfd@*TF7zDA?Y1=Q z*7MZ6#&l<0&OJJRLwvnvyJ~#nu@i>ua;wxcP)sz}Q;VnoeK%a3A#oqiF1Dhf)c1{r zwyO}k8rQ20lCUqfc_^PC6QRAotNdyNuxFnuijhvX&V(yf<-u*g^%ai)z0bMp`MIB`WA*xcuQTNcnw+GHieA;V}5~@4_Qc3cffKB_l6|F~Q62s55|qfS%|RGU3;1J&`J;$7x*Z-k4KIgw>pY< zNRA)^8KB*G;*bhU^tO*pXyi32mh7(8X$A6=l$u~*#06|I0J@I-6}!iECn8YkFrtVU zyN1p2*CH{}@iAgV&_F5qDUinpFH&vdl6l=E)YX3%Qc)qG_0Gu@3bO4+`)FNfriXSk zt00w#55qbP6n;izEKt6!H4#w*R9~PbvG{NdB7a4*zb-C@5E80vK`a{R19?8IJkcs| zM>Ch^XvBm~DgW4MY%mzb*qmVHjfn_wOyQdU1~xHycC5(3Di86hA?Axr0%R+2(95jq zV9B%ZoNmoFYK$Kco^24u>r|oiHvczN{~;e*{OffN5H`Wg<)jyrb)aMnPq>YCLM#-B zBGU14_5(K()O_!qQ7u5hnM1J72!@_({HmQmF0CdZUYOFiaXk#?!DiC~3M4&=ytbi4z z{aRzyFfF%Hk%^q7=iN*K%CRQtCm+FB6Z6lYJqLsvDUO#BW6w(lQ|yBl5t@cM9+6)w+?m2`@vLNPd$rK8N}bze?3 zY=4^xPe(4uXXq7_b_#)}up)vNY_-^rpb8U_5nhiQ^w zx*h)|k04;KDRf5~{wU7-sROJ3z!utXJ};bry~8nhX^Jv^sq`{@*KFQvrL63AN=izn zkQ|Hju}!(ihaNkw%Z0wp!vhJWTMypBn`K;N{;h>2M0Wi)&~KJ88ikbIbhte=8-yMf zj`I>*pLzE&C+Y3mR@t>mcMp%zlwOGiDP=_^CEsl^wqJ&vfduUlMmqPAM}@DD%T6bb zMdbUB#cBUqB~@- zH|_Ay#K^HWF4xy}2S&YC7J#~MA^32Wqs{@(_98Zitk&Gz2wL}jpf#Jz`5vSgHWK3d zQcPkS{W#akRL|it{cENbWhQrOoYf@?J34k)8U6m}-(nMK7rSaFt17Qh=M(5v1N)pN z4ur1{$&K$kIT;nkNE;C5$#q&%iWxn=x*AwKypS5!q}e9q?bQDXm4N&Ag+6;WFSKkL zuJxB>=kQQmQ0n5{-E^u`h=giRFwIl<&AwnMNkkO1IZ=`2`zi#g6s1Gbq*Zf+6Oj|a zC-M~@qh$|Hth;w*WFR66W03by`HtGI63Q9VH3_d}@~ip3ng#V%)h`NrM&};VPSE&+W>sp}qWiS_DS@>b zZv}wyOmb4+#+8@OgNu^qT^WqB7qsF^$RiGOw#lSLzAPKz`G58Ba==PhP6R)M<idPo19GVg`me zL;XGUa?1)~Gsb%j*3r-Q+7CPkqkMr%s3(ToNnrD{zhabbQ@e^ZQmZiuk4WDy>qaDOtL@R0>%z06{3SGnX!P*2>A=*xM|#aV=~( zIJ?x?BZ#lYTEapdf8E9Bo!fXX$wo^*3 z-zm;Ts={UbPFYc9LZ)<@^Ss)M*XnfP=dgvPyPsPv2HKDD^QG_QE{EFZu;A#} z7fi}r^O?o-k1dDN_)ZjktAwTMTw=Pc;l5h6acykEzG>MG6%t-L^kLTBj_g>K*2*zn zuwddv{iD^24r~9B;H6)uaJK+ zRV-L%(x|M$`uT=8SAg)qN?Tz4Tg1=4iFmfEfwHc%LO=4~hZSnl1}*w6cbs_i2kg>f zlDHl>ccuGKPL8FP)*EX|`HEMd0nJzCg_gPfnyuXG*&uRTTkFuvJg*z(Jw5z7f^?~- z)sY`9*SvrlWOaR7?ufxrWUJYnHwORKtBQjP*>FP!ObX&|po=+yB z5;%e|mMWF4KGuk;cKk-HILzfSh^$qbV&7cFf>fra3-DfVjP(~NYMA7rW-=_BF+c>O zS*;e>sCo=A<=9jp_K1;BE;IMKon;pD*L5i7v+hK6&UqWl=W5`h=;pFg%!)Nfd{1ep z(cq}j_2qX=3^}y^z6t4RRIguPdCOi!T_ru2XlDS~SNdV7s%3d%gaCkSuLm&>NE%uhLd)tk)M zP(l@NzEkocCtba(dr3amJy*?cB|PXBt<$|`cf;J#m* zh(I=Z)17YlA-gdYs5C{}__{)yNvjjSwOS>5)$hoqKaAgPNxptvjbV>nwcCgLZ|>w9 z-?FOml-ZqG$RX7+wt7OiP3;w>!7MpArtmy{VVm8tLOsv1mVnoay#M<8-3xzy`*!i*=eohU5`pa8uC>VmN`X!{uj{m@oG;}Q6N4SxK8PCH_T~c> zVg1if9@<%Y(GHj7wd0!xB_jK3_{fy>7r^rFf)sr^*-fT3GQWE{1h_^Y~$_mb6GkCNp3;3&#rU& zR;ESp;u~!-bJZG|l1A}s^=gv8`6S&-`JP?LT`FhOd!0NV!6%R@I#OyW%P<$~nzcON z)-21lFMI7aX>NVCt1308W`nOSNgX*1u$~95LQRgW=ihHXy+`aKzw|^S^0=&V* zK5s79$s)U@HwkEDa1K%R?EAwW;*2k%<)R&;=vu5ynq6P_7ku^edlYEFbbzyDLe)u2 zi^!q{)kk3p3f0f<@LauWcktdl$u3t|nzZ6ku1bRhhDJfJ*)^t;>6b5?zEl~-jq^w7 zyI0DuK+Q%=*6M1NNr%+Pm)ebF(!@m8l0v4?XpGds(&16jr~+;xW-o}t4ST~z)8Mhi!&7NA)a~9vd1v=EOlD*(wldpf zdR0h;1yrq-*(%=A)XdBQ6|!I)>2T~HxqCZs+OM?7A$egkDBEj_LVj9nk{okA%b@6$ z;=OG?=d(&@bB~>zd}`MqXd?S^bL7w$$%lSzn8PSDZ|owxaJ9q0DBEtbTW-VUw&|S? zHmTb!uA-gt-pg|vS@vrZ1*Wzh6koYi$Nm@31qPHkSFF<;nau`r5x2a@;jtrh!6cHZ z{!qzQ@8zVCYD?bgD8~tc>*qS1oO`ObIpP?3W1jC@S(pl8X6E-dYFO2jb_43BxuQOn z2TfAEQ%mmiCy5(NYZ?&L{W$HVUldj|x#7xPNO9pSbvSce%%*!Ut#9MOVlqV)N*VAc2TKXO2+zpy8c1r?e_emkz<5K7qHy+Xk;J;o{0N;$HWcp3)(!MC z^YCePoG~FM@l(epvi#Lb1<}F>TlLTCtxjy&0HIb}%CdSp4f-W&Z1p)h|J}RuO7Do#m}*n`GGsBLmFS--y+?!9I>zfk4dJ11`BU*6lBKPs209O5!Q zw$zbLpUFiT*ib_?5wVd~2iM1>=&NZPWGJeC&%h-XxKuR|)jedHXrMNJjnL=*Wr$~2 zU6FT{ER|+d)e|Mqayu9rG|C-iP~s8pue{KG?Jb~iSsD!Wcuh>GE8c& zZj38CM2={Wt)OZkUIPTvYdU>KSQ^+XZ^V*A@`F!Hu(~pZ`3hh!^hZG+&Ff19U`)@au-gEdd zW0zDR5(SLdn#%pb&kq2Bnn3YR?w2lu{m#swq_FxeWe1x>6rZ zzB7ZM$E}%_L1-jEWuIW(O5RoGYI?5XqW4Qp(Q_%>=i)zukCU%ECb^e$JU+ZTPn+tE z9nAc}fyzNdKR-T!szl8j^mH5_3v;K0sE->swgd7k6oX0I7K^$_=n3}y@!QW&!IKw? zXClvZcpK(es7kL*-v65CY&)+vT9cqe`hIqlv%|&$VPmJh6k#~6AjfTLS!ca2;1i`= z6e#5Rw#7<)PigMC(X9sv{2Huk-f2cS@>w?z^v;Fk%+!RB6KOxc$Z$r_$6ZW@k{r6) z+jB3vhVsO{K^@s;Jq>!POVR8uN_1xW=!;Jd=(9sCJ@Z5Iy0m@KdtiYf1CXM-2>s~tMjVuN;b>v0&%PvOw)dKN+5oVtxs__W2Oqbq3UN2q#d5X2lW01LsN zbRuEUhlMxh9Dh$;q4>m7S?oQ?zIISshU3rY8;-+wsG)Ik`|sS58b2@>7)f6%=dm`8 zYnGDv1@R9P>mKDc59yw~1%-2&$c$-RW=)RmmdVY_1u}6Tw*7nmC6eGT-rJioy5XBW zz`@0CuiJ`T(4$g(d|8#_b&KD;*K(VL)SIHYyw`?%4ht5kv;vC;)a8pNvJaZd&5b3t z{qD1yl3C;=wjxP6EX+lWeU!}OnMx=66}4S6(>!Y6Z4J3}cVKuBQjd$YITG#s%1 z##71wuk!!$bWc2MP{~lMiH!HHTOo;p40KU93~j=Te|{K5km4BZIU>qyM+@4g^(abK9u z^y-^`uEEV)`)~(c_XF!YJrjMB#^YVvt1}r;_JW`v4nq{l596AI8%|d& zX$SY~f)}t4QrZ$inm@BtExz7iO@U}-{$GEgNV(PoI0&IahFXP(z2s#Fk@X1CARRY!+J6ot@Pq+LnBj&F5JZ$Qdgh^|)Whh#@cqVN8(KDZ^WK?BE zjxds4jmBgjza^U1Fvm)+Ilryuj43ldU4CrnVuxTS3Y6Y;Aqh20R~^LU_A|cI`dH92 z#`M#{so6b1jVON&Qy6O{&mOt3uqnTRW=~wgKYcj8e&i6TA3ku}O+BPy2>9d$wx*2| zqm!?H_9Q!#AY>~ehrjq{9c>C71)3#B$b?#KDdc1Dch@;~-Jb4$>+(7G*g(}VD9+_!maXM>a`(Bt5xrF_&{Px+hlDX3)L70t$EVUY&dP*oZ z8r3n6A%CIO)%UEm<%_MYxKtkKi4s>3KfTmWTpb`6eQM0IS%-7p>OPDG|zNR|>g8mb1i z$;WA0J47}XJ78&tl%2q5_6D0-C@Hvvn^1ncChcoXcPM5V@(ESZQi6ol*E@WbG1naF z(m!v$de~Pv+wc)+x7TzVE(oatGivKGz8>)A68*c5V%q)S7}^Zo8BaepH?w%pj)BZz z!^%sEI`<2+l08>`1=}|X;99!vD!O^d*Ck{XixA2k$VYJ*{P@q}jfWUSVk6s?gpE0` ziAxey)5O{lr%=B}Gm9#>UsSi+%Zt^;n5@lC;c^u#cQGR^QsS{gb9sJ5rc%J`7uwE z{Y(p=B$6tTfazMU*pJEp1nGP|tQ2%078`63*l!ryT-M@G9#(R56_6|JM}yt|pNaij zD--Z_D_1uVmi>Bi79wmuR#kU1q(@+V#rw+Wc_T){xaM{MNjKtmIY;P`-`8&fQP zPE=OKWNzb7PhnGBTLIGxHUr01>ju7bEmNY=sd5QSw!!|`CNW?=v${&8c35`t#&E+B ztUtZ3Q6Wrp<`M}u5e^0zrXARasYu!B_3`x6q;LTv25`%8eP##Ra8OeCEybeMEbQOz zUdCW!>QY?4sS;RqNit-c0S@7NQ=XkVq@p5B%3k_WoTveMFJXeAos((%){nxZIQKz% zT>aT+R!Y>B0boc%pzS9yl?sl;#@ttOb8(54oZgwCBB=;t?)Irs0Za%vAaru}UKuvs zcS880N`-s=8iLa9OyNpLOWll~D^d8iHHI0?+gPrC`I3NH*Z7P0CVW#bP;!j0I%XQH zj=fqhCwZhX+gYESUTYABeN`2=bOMa@tS^NsUB)2h0<(x`bRVn)ujL6#<}hNp5h~NRj0@i%a!l8!M1p@If5qMSQcg@hg$&q{5oIX<-tB^ zdAhpppL`lKifKaOxRnUS^-Q1HKth_ZS`+@_D7Ju^Epvh=>sYG5=Xe?uwmD`cTrCXT zKG)#Zd3X7W4Q{Ujo&z*u=GkO2f^Sn8*cNkPnPvktqr0eZ_Ih_FM0pZYysMC5bUM5F z)m5jaeXQgPH%3y*(~s;fz{KlQcozGqEKi4DDMTu)i|7ox*L3(jy(_Dkg-g=xCZ_!& zXKi>cMd%uxO256sNA4zrR$Xyt#K!ZtM1$F;Se zhuP@4P^C%V^5%N#+)zI>_-XLtGXz@ky7=N$AV`Nq;utE};GuVA6nqSScF&GaTHBsVVdB87AnDghwEkNECgscvKY-yT~ z6RNam?8e4$@a(*`<&6R6cBJB`@weTjDl(f8v-R?1u}ys8({y|6vA*KtWC)wF$6yND zh%fe`m-bFq!3=k>3c}}rqc~FUo$0;*2;Z>a>fp=$4V#=KQAIhVjwO!Pu!{fuZ-SNi zA_R{X*Daeez_db=Xur+l2*CqRgt$a^NX<-_q4U=1U{?DYSVuT(wPavY;x!@FKoNTLK078(Fx{cH^N9w@r)Sh8qJ`$ItJ7 zV-jiM#^2iXGf<#`#b|Lz1s9Ysa)jToi{Ck5UvmcnlgKkv#6|xyFL2qEXxqJNflZ;M zAy)tGDMC5yHzXQNpYs2}XD_@wbd{An7iYxsZx9yND_Rd>C4UUqj$ycbAchPM5^$<` zjcwT%C;2fGP8ap%*>Lu4B0Y!nDNHDO_!;&*n?t`3k@}|I(ENKOXq%ZOiVWoDaFxv+7BA;2fWUb6FiR2%{ zXDfp!682-u!Vrb=HHu};4Uh39Zy7^Koz8+5fq8A;k5JbqNjVb_EE?mHV z|J5!|7aP)IS_hmKkOz8nwqX^7>bsjW0)q^H939Laj2}O8BNbE9KAk)pPtu}K-U`0r zn;O^sZm#Jwi&4=l5q!?v2uWG0W^+UEv28x42S;oO(_S_80J6QDOe=Rx!R`{#*#eH% zk_ge2z~pl8T{j7&fQ_36BITp9{%a0p215cmda>k5XkRMx@2ZtwETA zyc5!^IyBepRMCu1#dJT5@wMrX|9jNrKk9y$XsBBvNm!j#QO2iZy!jYDo!pEg*mNYe z!D+k}cHTIVnQ6ffMH?{xdGl#Qd{*FDm+rz*=-pK}Og(*fN(y3c*o*n`2f3ThtQ=+~ z1E0n-d@gT_oTlqov@sBNKsrgs{n}`tpOrk%FNw^rn>FlA(qhTz&I<@~d=xMw^z&o* z7$8hCg0bV{y99c9n-~YUi|OnUOugh4c1lxT5~=g-mlEL2k85|LBl{&`v~4b7@2lQ? zAM7EPE9%<=IxIV}snG@HBU~jlBfvxs@m@?7zkAQiP7sOI<-!jDtnu@%=9s6=X78V$#QooFvY_^*SN3nKj3Nc9-8$ttsm%=8ke zJoT3_tL(E=%+^bk;Bi+*js}x=5ZQd)N`~-h(!keJQS7^)zBgv}oF=JLI)wk{UC6qS zT;SZ6l97g;FzLqMqIGbgww%RRH4PKT1p?fxfydpo>2Z(01+;`IRLUdI^$Y$C`9~<6 z@iAkcuLe@Ps#TcK5d2xIn;h?jgwTG|4^AQR%Y{cnBVGndsE57q;sS zyD({D)gD??!Ch0V>x;dxS{?2M>-WfpKsNO({Lt2;SXFOG`Wb zf4=9;nHWH3aT^Rw=yuP+71eDbZK8)4UuzyrZ&=&AGL1*G`tbK`=oOfF#^i9ylq$z! zBW%5f(C_X}q~l}zVP1ZIaMC1zfp8=>OOlEB-YvjZ@%Aaq3WFFI$ zeH)wpX$hB^J|fk*KNW*aI&S07xzC8yjFa`o@N!4$KJb3zw|(^8)yN{)Qz?Uu)Xxtz z-jqlWc=gMO2SsR3y9Ef~j zmL7TCnONk@XXm)S*$ME5H-BwyWum`(k!V=)ObuA2<25ndyb=OFzA2H?TOfr!CYg_6 zH6&uhVCb3|g>7QD+js7m601ra^)V%KFxyVg*}y`%*Lo3G6+FV3Qu`fQI)IUv*q1rF z?>`Fs?|p~`0qEvkXcf-F|JfFj`J8=arbODfCa&NA{N_$+8dwrl{X)$9k@sB~8iH`i z+VTRXvOZea>0e%^)JcYiusT&#k6}std8xsN`{3}1j>Z^nVUeMDVE;oN%5Xu~=3^^R zt>#GD);=f90V2azku6arytGUkB5@ZaWnho)wuT%4N7C#)`%p||9hk^k=pcM~hiJ>! z6N}HXb%a0r4$?qegnBZV2JV`HlI%M~ii@S`mS$u@G~CL{r3vNr7kM!YC7+I2xA@-# z*v@bhJm#YuaZw(sD0grrErLI{iMyMR_4kLDd{orzSlN6m=4EuaP=C7iEqq(M;WPMt ze{dA-vRwQaAepGGm08C^=_*qH-^YHGH*i2#>oWXU7=N5Y;_fLpt-!4Ziq_c1U+_Tp@3<)1 z6O?I^1SzM<%eY)k!H*?Uy*%A<2Phh|e(`_HwpMwg>ms_l5gfL6)~{)YhpOQ&gBW(s zmfyl5?Q)9X0T)~N8ST6-7^44@)juz~XD{rW>jL7&b^19N8ex+A{E5q$|MAbuc5#Rt zBb2`$lE>zJmj(55qhrI)lk!_Sy6ov9t$F>xC%Czj!FPT4PHX{GkF>qegO5crF<|!H&GsBqUF*l#0xBKjyhb~BJ&C^n zo-xziKHw>if#T-3gIb_;m`KI0azT-Q?s1~1fns~EOyRgCjG>aqIZuP_0Lz$b<+J-% zWA`pNRXm0a1+)vJG4COs0Ud@3tKZ*0*wo^e-4$?3=1sQ8^^O&v&)^TZ45C5?$>vk# zkg5ZMR*S)xy%9SiwEQv8f1O`f3-@X{K0sy!EOMOdrk(reRps{0O>lct;g0=+!yi!2 z_?e{$Ife2cF-O$wp-tbcFp0hrVnj&!9scNXE)R1+No!M^^7#p+I;6$iXN;TUc@DWd zrdHK&hZ5Vs2AFAV3V_D7slW=g zDI6@;3Qzio4P&0m$}d^mbLrlXuisBhV(>)E;JmB`rWpiWgU*#XB|pRVUY|QT2pnVE z@bLeBYy49PhZ~w&&ArgZL9DN5b=;Db>`tr15#Blg*MV_Ood|5w9Nl(yH%N<6bc^HL zzaIe77;JYsm?b+2eq!OeyXpDoAIDY=MhAc5w_qTN{|6mM1pabKmzkR+7s%ehHKxnZ zo7{*gteG}0yH?Qf(dZnoJ@)E^Lruk{T?#Aa4Qgvi8%hLWQ>b3HVJq_9*-&h?@*6wK_V2c{RO~e zc@*Eyo8LymCQ@PahcRI3F3_~>>|mXMtHIVWz>fSgxcZJg&*BAZxxO5|OBxJ6ooc4` zEr4nG+dogFj+!UmAp!%+=YO6+m7mG*M9awLEQFhWh6#DyGc?4M5LRwo?r(17*&(_+G*X6ZKVJ%9#hBML|OBxevn461e}-eE`=jE6Z<% z*joI2`L0~46YfW9^M6XLU&a>Sk!7JV4hXkbocgnkR3smnJRSHu zJ7!@tHG~$>R<_9a8#sC+S^3A8xPn$ALhUZNdn7gX{6^<9@*{Z{j5p!Q@&|hS*9nqi zx8*`X8AODz42%CCGrz+dgPzKN$R2Qyyn0{lV)9cz+weqg$CK&m&m6(b1|3ITa6p|-6|Ew%&5R*@{|IK4 z4lvF+!RuaN{x}$%wSL;r-Cs|Go35@t!q_W=u?w-SxR?6n)_!FUn=^<1)KTV_EET09 zYav&f%XJwTHM#OXyYFQXTF(fLk`D3nJCj|}c5+mv^;ONMLtA6?e#}MHzf$}>#*^w& zWf!s!HnthR{q?zpUkyF_+tHlkMKMsyw^)h^4sjQp?Ef)ZvGOJ+r+^{QP<;Pz&0=1u zh$|;yq@0y8=*I6>{Inz7Q9StXbi(i4D6RPvz3oTMdr2n0IW-B{g({kthjT~2N%Pdq zf$Yh!ws=L$z-M&w&-CgKbd<};%q@&Ge<=sI(rg0L{+7X)9v|$e+7C)Q$Ib4&PmP)I zq)-gKc{?ed+28D?qq6vzpp&+lIwoelChA3Aq!0X46kkWFaqBNh23PhZJ2hD>9N1`9 zo((=xdmes_uB9^)EqFe@iyb63imjXzcZ=CrmVQ*Niq}WK*!W_gy3$~XT>F-1pj(C_ zrf&mZyN3S?w!!HnUlt9l9Guq67uK4Tbh%DxraWi#7EQ4{FtF3|*%q}khmOuSOO4ay z%5$VSzm->w?b`w!hfMl`POjx1IE84K&t?|fxvGCXyWXqu(x`&*TUBQFKEdZ4DMHgf z_5}y7C^?3#s)-gUvR`XCUMVv3e6dK=J^EyF-n~3R_tvfEgpIlJ^~JskO1tFxgD=Ti z8{dqPiUeUQQjfKX^VvPIM$erTo_N|x!gZyBen^UMAu%XOpuykYGsKH8?_&&1RcL;7 zH9v>3Y&Xd}>_x5q_B4w%`%mg!71ly~0VF@8#fV?RASV92`^E!5TYmMLkL>jm2lBiq z?o``!c%$lg95YOLFyfxXdhvxG86NJTj#-+q+r<<88}ZXLbx&7qX(~wybapBX*Wg^_ zMYN=8UiaK%Zwei){tQ6YF(K!#UE8XWU|*Gmbh_dgEzwb0bn$ zuWGt09`DOZDBMhum?ZnwfMHCNbG+I(VD_wN_4Ju0$rU^7pFO<9~QAva}wd;F&6Dmk}rUO*{p zV8yVth#vB68hY5BK)pg@@PT7Ml5dgvfKuBhIF;sD{%XfFp0eFZp5BDUlYPv>Dm0F# zA204Qe6s3v@x&DOO`te1*6a`xT`$x~>wAZhIW)9LLF6FB_ zrQWK2*0ms7*#ouwB0O_W*hVMd7|C7g*d)0SdgS}}6|QQ7K&PajUB`d!H)3#o=+V9;%Hi~pwxg;#}rC^PF#1*xkoEAFAGIGU1;sTjd^p4ao79BX1N!UXo@gYHn3(h*zkAW6XtWZDkwm|KWy=n1`r@aT#hvOBt9p)~K7G)QPSif7J3mMJ<>E-7jz*4LAEk;0~8-%3D`aWy~?#y%ell2nE@e}KrgNEy0$dXxRo7E?r` zC_z(KSJyJyB)Ab)B5LvmaB|#Dh#N!9R~@o#`Enyu1(8L(9}zko6vouwx;3P;k|W)@ zialJtTg_8g*vKk6DX8Cs!$~oc$tBr-kF0qP_?cW*c)19 zlq==6GQRFcxoB^jyig0e5+_%}e{`WS$R@_Z+uNGgwu_@-?Wuxn@VHpmj?2KQVPf z+&REP>1J>3ZyL4GSY9DEw{ROnj@x+dPiUH~pG)PWytGm0+iC8`;Sl!$%TC2+QcB8l zyk>8xA3ddhgWtSYE8hCsdzzddTFy8_H@(cSPwuPPk(FnS`#S?7dN&??lB^slG#@am zl0$&!8hC~RZcsf@sa0{yiHj+Agnjy^5nhXyjAv%G;$zaTh> zLrM(w9$15;*B2_8Yol1}{U9`JnBYGmeQ%`;1bK{~-#T{{*oN4EgV0}@)Ah(xYZJ)q zWJiRoz+Mv}VP_1D5pq7SuZL(^dL>QNS%Uyooz`}FCIQRupx##Bbgj&^q9<=l;>-aq z7P?^{5U_n9j`?3LzxR7l9z-n9W<@xiJ)8Np!_?8=GmNX6rbP*o+U}ewyFNpj)u~MP zbZ|N3O6#>(Z-|j9BIN*!nF6fa>ZGY$;Bb9-*Uw8@*RStCtVHw!Y9-;^JmHOi6c7ie z=dUj=Ksnaxv|ZXE3li}YnvAo6q6{_7vN#YeX01nL7thjq+BW$u&oW4+7n&Ja5UtI` zD-{EoIwYOe=DWHiotB2fyG|Ur0rcA>d#;kT34}yIQfOXQBdP>-V$v`w>RjALdB^^{ zz$3^9Z1oI?M+c<0IK*7VlAsinMmuAX@xmoZJ45H{gz#=TZFkosxrLmN4mYOole*8S zy?_*%)XO0U&YVU{1}x>)S8V7qaqEqTxZ?dYz(`7ie*>OQI^)XqI02!GBhWC=)BA>} zNw`kV#7)2wh)T6JAwUhUS{=@>@dgIAt3WaX6AggQAn{Ef($VCNHs*~?PZ~=(WC5Ap ziOSiq?Dn4F$xk#+%N&FAebBbc?&B_TsI~!7?t?d&7mdk`C5BpX)VIG8=v&AJzCLZl zPN*DX39Ts_qQ$fO`R@h>1O%tn z+!!$XX)n%a>8QjI$^bgu%_y!e2J{pE;L zuRaGJm^i^LeIv=V6gVJ{s>k-GG+Rvyd*1%d(B9&w@ESDSO7yffK#I!t=!I|W7@Xw` zzZU!^B95JbqA_Pop^0a)adrrYqhawDBIy1Qw0Nyu`y2`FAW) zdG6G}x^+2Fixm`31}(mwv6$%06fM-q1T*Troa~(@>I9^6)Nt>maKOHh2w_vT?zu)V zWr$qJR;X{VXP#;QegzQ3{DgU1lA^-$xsT^_{3O!uS6sf9dcE7Sba_0zEm*y1&5}^s zg;orB>5M^K^Gu&aN5wwu0%nx`n0^4DvKHz07g%cd0(&1qM=d$Q2ita8JbOvsm+)A+ z4b(&QuL(fhHd%E5i(2TOM0xRXU~SqtJy2f1UfAI3)u#(l$i(K8Lnc#n;KhLE{(ex~ zqa|W)Lw8Xa9Re4z95}j`>S+nukH!9|B0_a)%+Sj$-cCQqDL5kNzvOCDxyHm}&HxJQ zjHo7o<&^`<6%mDLF_6VuFlIJJ3MVVo7C@()juU&?&)mBm#80q=9sr8~F6^k2fTd$9 zu*I#Q#(J#wQswU~!1#;))wr9{>J4A1#pPFqgzF#)=&cnhCg!m?gT^b{^>ugP#ti+& z6@!^bHP{dcB+ovIWF4Q5^+NGlT{CUw|kzVkL z<^H{#6T)k26z{5jONr|WU!!8jvc2Zh%?ZGbW!_CymTlWu&fbW+FnB^<LU{+lB&dT1s1$JQ#+)0 z`4{rSGKIX?y1Wh2Nn!xI`=)qtW5t*=mQ7cyS}aqL*6O+47n#c$1C zPM4d1JwL7yH5s|h%o_9KEU-&2I@Q|_w?-q{H$FZh-RhI7NnRNyfrqT58O3yzOD4a8{TkX&br6F&Vl1bEw`v=Vw6eA6_=yuI zhU$&h?{zo%AU6|{olC~rtaJgP`gDXn^sBmbQs?bKubBs23c#i^RAaU)3={_TN-&}$ z1vAkmORsv!fVskNId04$d%`^lSnzL5xahkk0iPHdd$rnAP-x_J03+_D zwKZA%DY$;25EKh2>hETi>hIPiyTCoA%hbF$v@seWa>}R$sqw}}EUcl}w$r3b`ttm= zOzPJXd*}Iy-d=$mNKocd4o2awHUsm ztjn#LfAr;n==y=cdG`So5t_R9Z1cV~kKn!!rXz_m(7vGmyi)K1-E<9ISR+JO8_XeP z5kvt(P0|6(wSD9Qh9j*gTDLW30e1i8Sm_e2r@Ga?TeW>{X&x6!r;L(SqdSi%di$3C z4C0cj{1Wp$)@8`wtw<{t*tSs@hN}ej1b{u%_KMge$`uGp4c61rbNZF38z55wsf%`* z9(tE_<^=z`?7guvVEaqhlgdcmSK$;4M{5p6_oyOzRtLlKjdkCc{pylqi4qCg&3=9o z!_Py>Kgzpf_S3Ij9!RK7r9OS_^)$4tL}?Ww@;|%hpMRpbU3%RGId6ENDgQJ|Qt2Z+JNsvI=Ka}T6jqk#sXqMWHFB( zDx=Fdi;NR!$J&P_FRL1LEMSZcQH4IjA+-Oj`FyJx|NDA$aRngKD_Bk;ZhY zGw9CO>g=uYXYz|&R{}IGEy@i&o{yUQogIzYS5jiY%{QTL{|siS1Qd|3Z7KbtUh34& zCIjkO4^&2{tk&dPm40k|Kpd+}x&unPEt4kx1cC@8O)X7C!_5ledu4^{dX;cxxK~n4 z-ltB48@vF|m41K31=?wS2kPk3FDCb-4LeK9Lg>tLcFra{B_M5E)^B$>nCg^2FiVk;jgTS{hQj~{! z73V!G3-ZD4fI{kK?!)xSW(Gdqy)71z90% z`Lc5*`Zk2*?wmNWyZUh}J+rzqMXe@xm~W^-SS_TezMe4OJ#Pm$cnX@DcQS5N2rN(r z;v7n+>be5w2&=jVQLE(;ApG{d=3U#pT;u*llH*y6lsRC0k1pqJbTt|&FO-}1GVjWX zONoR?O|{Xkakixx3&FZ1kT&2e>6Iu6P92N2cgo0>SNkl3uuz+DzU5`Xi;CNCgeJ09 zER94vGSPGB?%B`KQDYCBre?oPr~1l@I^lBvL!#|jJy->Fw5VKav^6u_-k(`+epgD6u-MsLyvg|+-TK8LVdbXv-i5XqMzJKji6-^1!`}8 zjq9>N$^cPVjlx>dERCY^(55WwYI~Kfg2GG35!OP&Hb@0Tx26#8oB8k#RT{s4upPjt z++}{gi%_4QtsE`@#l(=>l54C9Wo()4;oD;OLZsO&k8*YgyDrWQHeUOwqkZU>LG!OyuUi~Aj)Fa7KB;qMLI>z-qgJDWHBW>EWP46o z76hZi38%~VzqR_p_Q$`8C@M05(lot_z>iRq&ssY&GD3=<+oqtU<+KyhCMV&}wfM)- z)h}>024zj1Z5^Q5dVE!i(C@o14a{F_P&A=Awj#&j8z1z5bOpQi449;|XyMIt^N|p< zCkk9PIx;skTt+VN3^ONWPo)!W1qV2cB!bmqXhbX?BV)KnS#uApFh=pz#Vu2%jdf7q?&I~tKv0+FyV`RxnzA%Y;8`CY0Zfiw*<0c+~; zAw;{3+8x1BIL}twGaqEx737-K=ICn`)cgw$KbDTZCyyPwqVUH|RYQ+dFl5tcI!oPv z1Z0M*_U^-ob(Z?%EbM4qA%~WD7k4Aq8>d!1^p9_$d$%zg4N>Oqd?l;iP?%-@2#&vI=n*4iQJVdniDC5&T-&~YWROWEd|6da2dyTJd`>+9om`}s<5 zlpWl}(EUOjx~q%~sQDF(jGIG3a{>G_ z-2zWaJ5w)gA#%N}YNbpec5Fh$(%O~k}f72jm_j%&<*1q6zNN0}cNB3^>>8MiOs=J&Aigkh^{mCzPswV+* zm%c)#$W<+^jiF=m8pxS4FMFn-P<};=A;ii?hhgq;G_AJ8K>c!AO?Uo|zlL6n7It+T z?+Qk3Glzh1-`{(E)H@jU$b_R`Yj8B`Pu7L{9!M{Ke;^w099r8EpyL_NKRps%O13{fz0{K z>fm1}#*IO{&(87FuAraU-f*(5)n5_b)v_1CAxbIt?z-MyW#l0EDda<&o5G-ZvLfxi z&6gru+n%Fa=vr*hW$(eOkUp66|B>|`P)%l0*Kim|9Y+U51r!8E5iAIZNUw^34G>X5 zs)EvsbdVA|O@fMw(h;Q>krFx=eCML`e*gFWS<5wR6p|o zd+!6-X++>XA6dR5Rv{LKDddC&eOSA_a}>JEb4QLo0eikI*azKRcN$4-d10FW;K*89 z#+M1_vOUUoVtB1%NKz#pP~Ch80}n#1L{L#-hx)AEa<}38AfTy+~UMEEdDY1+USlE$O59Sz^S?d77RY6DzJ55a%qOVOn#|x630<=V7q$CI^??RI- zP(lXd5{w7kQb2bY4b&t$-Zj3Y9zO<^Q#{dn`Tkb%)GKGa^PAn1W7rfvE+{y&I-ObO zF{B~g^S-b+h!iih--6aR$&kg)^RR(=3hhn zK>hUjK`Vao8i-&^^F+dd-<2{iCp}Xd_k8Ys732mT-u~;&Wnr7AY$-9@!P92uMW0E5 zI{B|A*|h?Tv=_2_O@(G(h~hL(?{(Y!LWt*p zttxV^jG4&FnB2Qg-(DPsjBpGuBqlDto>f7}4Ov1ysTs}2gfm~Mm@Z~CaonaQVVojq z2N_$)`ov(>6oVWy4;3M?ENSI-En~x;xcD)lk$d@K!&CWO{63_BcL3Bz(Am?c=UIg> z0F{&Lwm98Wu6RYXFhJdQ5&&*G2s7g#`?P|aF8W47v`?n7Mk{HCCiY~CUUjn8&ZyNbLRwJu7;XPZ@3ol3@%Ynz;i=CWuUD$>6WS6Q|}Rha@6 zJV^L|E3D z!RD?Nrn7BntWIk5{PnBv2$(is9autXFde70yJO*x;!734J~1;y@7KIA1B;f zoN1SjKtRB2asg(d=fJGmIz3auk9h1TFDjXjzO7DLTTPONdgW_SyAKVp@{K(AK5F$1 zOh;PQQ*P(fVqNe_X5)oL^an6})^h#JKpugBJCyitUSnX?!6XN(GE*}Fqy_~z+;OKs zpyP$_)REfD3x<{aWk1dzka1*JtZ!|l}O@k_Lok+QWe*z5jI3W)q78*aZJ{DVLe#r z(|BJoji3^D3$qIv^o%3AT%o6Jf85!*9Eh-&z2wSP+gb}K|LZb67$3s}?785kSVbW5 zO`x*DS@TmyKwi1bvnK#s^I(0vCG6&d&2fCdD%rz>+bb|IQ@TzEa*R233NjKKpfDIh zU`eluHtuu(d>!(mZ3@n6P(Io0(pt+&U#OTV0+KE=gfQpyueS2na_3`#24-``s9ssV zGC{xLsN>S3DEH83VW`XKeRGy6()=6MKvC6%9@z-=ltI&zti|oWs&#UnA=~rpqyxuOrC3QZ|%W5n}<=p1aVYe z?5lJp%K<}a8?Uz$&~o;gzk@x}L#6M0H>lfa)rn?UsE^h3=jC-ohh^JUz-Gt{FMP|f}ZdnV<4t=WgCNzaaJ($ z#dLEWt4kmQ&@?*#10SFF5f^@MB$jI2vNz%%s3R&o(!5qNaXk;KX>+Y|6Fb27gP zkYIWx8g-kyEBS|L)T}$(!@IjxA7MqCMLJX)PvmoC+2V-Xho`c_eH0y$xYt{tcp$Yn`@M)fhZr^JN=6Z9Y^kU4 zvSOJK{&W+J5@8b@6GW#1`0h2SdOW9*|(MIBxaE$~~**TVqX+#mX`A@$p^EgIQXi+E(4-GObTDyL<-)zxhAuTp9o#%j<=5gD$cduapS(~j|1pR{g z%5_q}9AHP*zya9SH_4YCa_p|`x^&$T=Hg@&eE>-lIa&;xq+d_VZppG9+C0>M)(NFq z7eSH@(Jsx*J}Sw)a0&^Fmem46(7X2Zlb`=Rn~}QwFY>(o3f~8Jw@9za4zWJ0sjaR( zH*Jg}SVk{u(fW5%SSx3ivkFNH7rF9eUO_XxvGU(uqv^A!t1`n@-cAHxni7gHdIrS^`P1%ZhvLxkX#L3}F* zLqE`(EYDBz0|3FLt~!bG>sBhktGu0;qjl#1A{082ZJrzQEeQHBO;g`txrstrXDT&B=)na>nGeQpPLUS?!X@iD(?nXy+v)pEJbK-r5dQv zdyupDF9mIebLhf1QUkK9Bxt$hI;{y>*3B5~ASa^*)!fF%>+c)S^nrfBtVb%JVNV z2pWnxe#|+rrmlW!N0KHxV)7UWD&`B%q)XdF-49<-`>GZ=xYbl+tYA26VUd#)Zea6m zq5^w)%#JB(Zj-b42u*9! z2DV!CEUDZ3NkPd8NC3OXi939Lgi2KNIXgK%6MF{8e^h;<7=$w`gMx~H@J|Jm7{dEV z69+!9*{j3IX1_mp2Z5E1Jv$e>xaq>V%xid+b5}FZ9SDbLK@xcGh%aXW!NqQGl6*zy z`3$%9glQwl9$5Hxm*>x$mXA1SO96_Th9jZ*p;1S8(y+l^l1yZL3EQ{)d6 z=bZxu6iFbsA@MlsYT|>43iy)z2)}xFlM%Q)6c%$!T{GAvBStqP+vg0U6QVh4={4- z{9B}M;4cb(W*v{|(E}0*^tsg$%eBX0Xf07Of_rThD72Q4aZ=~Zl#{$i6V~bO_&;$W z8_(LT9X<00W_YS)`3*keAtIxZq-HG#mQ`zj!+siE6!%hps6@#z^r!|VGDMmNm}(IE zMyx9k@=6>v*(D)iDiCUBMzpBh6&i$0?eRbSdq=i0&cygO-9;)$@0>TE*z>=>lXMSU za&i9Cj~LV%?OZsE+)MoFVo%}C5%(cMT@*x+0u)Dr4a+_KkD6BO2O%lQXT|#{X0?JO9x6f_0%bpU^5si(&hr9DKaBqk zukgN%G-;N(;Slk`5xC1f(( zhht6g2J#I0gJPe&05d52NlvcrqtkOO9qw(z+j0|c1%wdOcG zKYHNq&3zzoKJVWxnAdpq32Ty$aX#du$X#$RUGgHUlp`vr%hz`kZ$S* zptg+PNOjheSOR-Y#k<7?ffx;;ZAcVJJ@%?(fPw*|=VfH|L)|BNYmxx#Ms*2d81nL7 zZX>8)fi49rRvBgA1IXGoRsFo7Zdvt{Qrk2WibRPqcb6|iD+yFWs-cNQ08I%bZ)3HR ziHry7YdtBZ9M^b+!R$e$Ct^M*8P(i>BvDxVsv0>PpjdFR0}>t}m5C6UQ9&9e9Ar&o zC%9_^L;lVIt*dpJ2njwQ5Uwx8VN{;!C|iOE^j4npwSFL^ZBwAd6uHmESC58j9p3vV zdNUiVQhi@gG2Kc9<(8T+Jmz+V%}Z6C1{(CIpItg%*RM|VVE#qB<)=hqwg8kT%rvN5J61Ny|i9Xh>#*Tf&qZ)I0L81<1yLQZSO31ARCy zgYfd>W9K*hBI_q~8Zp0pd#yYYY_+nnbsrME*ao@~ND~j_gQDhc0hSt6uWKNfsVTFL z3kA}FY&~}^Jb_E=qpy}A7GnXHaMoNLqZW#?octrkX8y_o%R@PqOCc{2`S+R%wMF8A zCc7XvQbqZ1ncz+DYN9hH;J=Ec480d_ymYw-EfW8UV@}0%sr5@yf(Mh(i#B=(9`p&2 z0viJasT!{;86w#z(_>0CCN8!ys_wVjBGKR~jM`2Vny!C%YyJ7?A+H9cs0Q+EyzzTg z#n+pQCK#A-24UKnD$q<}to5f*7j;$mp|?Oh8n35x^Wru|>={45$=&4Lk7O*m6*nhQ z${}I7>-pn}K5*_JPl!G49GDD{GHvH2fbW5fO9(U~aaqRoQFHz12ra*TR@fG^Y!MG6Ndb8lAW(kLx zSwZ4Q29O>JU~Ef|ASi$rC#R&{;7_DfiH?H)>@Ye8QJJY9;N%>-mV3l%b*|F=Fjzk# z)T`y8)k(Tp{mpy&k%l%?yVBYNdqKq}?}Q3e&nuQZXg zFX#bjFAOb$hy-ITLYDwKunM4`6%s1JyC=S6A!1z_sloyFADxsFZ_pV|QDxD*Z*`9M zloVv1ZH@(NMS@1+{A<=Oy;>4F%V;PM9G3^MhZN!#Di!`D`H`87shERs?#EFKL-3nc zH~kwcIorK%nIOAI1wobP6>!V03#RRjP&ejj_8ne=ubTerGa_*UTwo!DE0*GUj(}N* zm$nFyJ^PQ1l@#4*^FVCz24?(0N?po`)>w@;!JPzO%G;H+ivA$+JQJ@*M$w)AD`?BJ zh%SPDpgMW>AR8uMc|ztz&DU_H^#fRk7q0*nIzrG5bt=di9u|1WZdJ!uN^Qi0)DmpB zsw!;`QH_X)XFY8c;IdQTC;E>(0wL{QS|w;8S}>L#P($M@rIEzfobVr*4n(}N`G8y{ z=+p@`tKg5xIQtZwZT1IcAWizyd-QdYL7=U!&!WMoZ8m_*urhY`bOf!|4SuEuNj$FW zHlF_zlZt?%ssCeQpwS&YR|d&VH6gp~sEFSw`oHT-lhqBk7Co9U0z-Qxf*>i?c5MRc zIAO$zkw4%Au?!-kKJ7y1YUpdFZJT+>X+J|H@6SIeXf0dCD(#W9D0+p>fT|Y(q$Hn! zd`QQEk$Sk(wg?LH#ji{rFfZAR*?|(N)T{8wx{cA~ca@mnAW$mdH1L~y&ghV)u2b>N zMb_VHZ{9HD6q8V6!X00F!e+ts$n%^3G%=;lQ3B>)7aaXe-wdqMLDp7+1-#FkR9 zE);0KfBjufL?eIS@6%irF*__eGll4=yv~W*7Ghd5m|9( zQaFJ&++_b;9OgPJQ%Y1>r~Sr>y34VOzciH8HoJWv@S;{?mnKA3gf*)I2R3V&pE!>E zWTE(`GoRr7CFzf&Gt9$5MU^q!QF&G&%V<5gUYhdFFjqZ3dy-YjhEWT=L5j-+4Q7|` zpYHJzh+jR}vW)dT52Nvz8R9IP{yD5u5Yln4I02P}bskc}EhM8M*x>!)EMB7Gzvva| zlA;(Hlv<7Ykx>++v34UVa+NMKZX?tL%YZV3b)4yTBnRx%tQ6^a0kp>{*^0EF*(Uz) zUdC#9$PFO_teAV$N3mwfOU2z;SW059`GI#^wOWW_G)~IXb0s6IK_RgQiDLQ(gFi%e zPb@aLnvEl1S4u^eYAmY)`wp4VThCJ@MEt+u1=xrQs1k2YBLQq@zGIrZ9`{)&d`+fe zrJQv}0sJ6w-|T|rEK;g27i1prOu4j&;0Se$il0NJ5V}zqO{%Pp8yY7LNmU@d#9X ziin}@G%#QHGM$z=7na*Fa$nL?Jj1oDR1DWj0M_h*TMLh0wN{$xnG9-?r`fy*RdweJ zE0Kms@a8i3Jau4e)jX(ty+?Iz2`f9U3>CU6gUW?Py7Qv8u7ZC;>;y<>qEL+T_-uEz zW!?xNPgHh7b4GD|Hp?ha+=-P@sdo_^?H2IrElok* zY1{vEYy!p@6ZGOIE}s1HLLxvKNglzHV~BqN%bxfKS}8C#V4wQqDj2-^TqnZ}F(UxQ z?NcC(i!tVj%cv4`tyJaPH_xgs z8lg~Pq38)Efy_D50te-3mE>93_rcvdK@tN2_T`Iz1KINi$%z!#)ZQ$fN?!({-Sz}O z6~(1xqu6b1&tHH^O*2*-C5H-GJ9U+nRRiX}OIQ$aNutNQ6X6O&Jp8E!fIHap+k;e| z=+(^Bm!+ppMHvIs`^C$b)%Q1U-T(5O#%Y8RM#FhGZQG6Jy#TQ}|0#^CsPbRLs!|tj z49v_@L+ZtYw@?XPqlO^(!G-l97+5Z{m|h3=Tl|o6J04&Lj`_9s*G4dtl_}?{_^^_H zsVf0X6J&H4$elS@dISv(NT9%$cgc~_016UrqPFJ;JNb*~ehLWMcNKpqf<$nA2n1Du zqZ>v)Fjli9QdodrR1v5M)c-&dn`3oag z@@GFACnp_LW#&DFlm=^Y=1_4Kbo(rkxS_O zA}!{G&J18#jKr<9cO3^zuVV77<3NdYvUW5#z$|q5R4;cJ0U8{E9B@mscf0ICP=Z11 zz%~?Z_o-nwedt-5-RE zqs!Z$@x{h14I1#Qx!BF)0~C9hOyIKEUCD{EA}{8WAYa*`VptW%mq=QZoR!xRE_24r z0R5$ezlw<2N}W~ZusiCvuZ}0i=chLXLAl|PkMCMTpK4jNmRnBvq=V0R+JLc=*WG24 zfU(gc=u{vx%k zmWL-1BL?6l)i51#oWIn*I~)DWE^h8|oxi3~X)_CV08nom_bC1Bms=59%fL@4xJpi= zpc4R|IgFmy_51fzzSY}{pUE~UkfGimd}i#Vaau_Vd(v;{y#BbKE*KB*)|8z&u`LM` zlXt!yrgNCHZhxjFUaQP@!5KTPUWfs#PFSuMDX9`BW>f=6sbIPlDcJJ?0m70+^OBza z0e?yF%vtk)z%2~2xmhKp=a9LrCaT-HZWZ2`0?ZW9-$ zz;u_fbJ)hM+-N{`8~zWQQ=#T&SM4QdumMt1Go?&hJttHtw6qitb=Fyn_WsEuW$&Q7 z(2{9xAaN$(9k!BptUAd$&cVZa$VYA^~9djK%+|Hu|-0qi6-8kKWs9! z*3lUmH{ljHGO;{41G>f6M!wj>j-hD{=9ZGjO}n2*tHKCrbVZ|Jrnwvj5cttHtJW2L z;-#mE5m69CFK)h&UT}R8{BL^K{ZTacgx3^cq&8$!e~s>YWniAcoLy4)Kg?&t2lCxv zXzj*M00VZI@opy!DE{~5x$1E2DivYW`|nI-0gHt=8Seva>Y6JCrd)V(fG=Qa5e29X zpLiu55Qc7BDK*58x6Ws|Apj@<$|-2LG$Oc6SF)Ig4joFJCw;|V-9!xZ;A0w6=h3yL zz_USUzslm9vyz~SM7o2vqWOqw6! zCE)r+*r6=mSf(rmF;5=c&S5LEOUw4pbs!A?S5jfe9|uz3zTJOG%P*-!D;#u^)>2qX zK*vO6nEm_VqB?eGaT-Kb27owYU)BUbw2OpN^^pWM8rBJT2~{lsZM{aO#g)z<2ILq6EE6r%Oud9^x>FFey5VKFcfzBmP*#id zHDGPIGf1{7apY!oi0ax~7z%Po2N2kInt?312Q+ekA*!lcSyFHQ>uS(AUG-YhGMyl9 zc+r{NTSCD<0BignxO=) zNA<{w166Ko>s!^b$xQ7@Qfx?s&bPEq&cWxQT3000ZjE1ZWxS={5-gWP4Z!KwGi~+t zlx9uSrwg+j=0PxXrKlIbMX@aO{OPJl=Sndux4DqSZf>}W?A=4J<$3yV10;D(Gt451 zj*%omjUO-uMav`s(wmL2Db2q&Wfmf5!?h7f1CuD14EqE}UrO*ni}8*$X2?xr7u&ef) z{|_kqKb$P_`;nS-TN%`~`8%>diA#c(NI_U;Xj6H0g!gfdt;!`ZzdOrCqrQJzaqF2Y-|SV5gg{ z5-kfz3lkD8-2qJ+1aIi_wK9?W1{nL;0bShWiI1KGkMl!ACCbY(mz&`t>h3X{db6~( z%}EqgN)l>L^$YWa+jaRy5cBX?559!pw1E)^`AQLsWSh&1^`dl43wnAgLw2x-EQO>G zosI{(D_fw*ikpF2VzUb+Z}5WA=>DB28{k)0i)KUhaZ?X5Th80@2QF9xELIW-if7(m zlJ1HHEX-S&H}8Vjd!^Zbs`cHpx4e6G6o$YD0nP>A^d$#SsWreMcDWF*2d0czh5bGw zv3o+lA~Z6rqSLdifN%ssM&QudJTIV&qQAX(&j8%D((2Ezl0Gwoua$gUYxaLZd-;gR ztY@+tp~@5#IS_Ib5KYwQ=f#5|d>l!|tB8wEh%Igk&d}1{_;Vb8NmM9twv;Dob5C)cYNk|_vZUPi%AI@!w%-gD5uZP z)4Cxz{mLW2u~=)Fm%&;EY2E!GQ8e$%;c;5N4M6TN$_lF9G85%KK6Nv^IRvQA)ntnV zk{_UXK1hKb!t!zdX1L@!eL*Ba0jR2Iq03?NloU$PQUm;9ED!JHqgyap+VJ2?fTCXq z(i00Za}j#BVr4oMth)-}k9N{Iz!nWdXjw#Y=ppXIG@jqLPfaw>qB_LL^zZhiX|GD( z%a3kBD$?l>q~xsm4Qf?^rb9UQ)ax`uEXFnymg}G;TYqAI#m`3)@4^5CjaI8aU}egs zPB^j(e1`D|hcxVgBrLVzhwCT%J-SWBW*W;8e&;v&PV&~d0kXHsoO&;VPNfo5nJ!5o zbJIms+#TADs=(@pV{s|!8d+oSe6Q|T=}#}tu8a^81IACqe3rBHn7`xStT|oQ<%u5P zxaQT6*y zGTNA|wxVxnxEn~uS#(`>uMPLRChg5vj4s3~NFxh@U|cTQZ2-$!`-jw+0lXUcTI{9r zZ23|UjTyaB*LVm^1CiS50d(algsyOl8M$AMr?;qcycbI?1%{SoQ;dA>_0RY7A2~`{ zH#xNWx}=^TfGfbF^{s{n2@%qGHE`fH0#>Pl1hjq-bu{)&A;_Mj@51D3Y5S`{m=%9! zz83*{9q|KK{lh#w4)_0Nt{{s>$D-`#m#y=0D;GI`0Q%Vy-W&b0LNj)7_A)@K5XY~b zlm`^s>ff+xgpbX~9{i`ydAh&2QyzFoGAM$EGlv5_&^YeiSQFM;>+@qL`~Hg%H`%h* zb|1U077@Vyv<$omqguDp++OcE|>~X8j|cZ4hq2 z4g#g3BPhqDpm4c4P~sGB_f?a%5qEw)5Pbiz+-zXtc{Y@sac@MX8M|R+L4G2YwAj_O z96AVyKtT|uXev|A`!z3XK;QIq4Zj;MHJaa zraU}vm{WR-9#BkVQs10bM{DE3&DqsyXaOu#muBU~1O9v%n^R~D6Tzb^A|HeZiU2TM z=Vw}^7ua{pEF)(Qqz1hShQ6*c7ecE}PEG)*S@WuRtMOc(tL+L zV%=!~2X4L`ZAn%x_{TKn?0JduUBFIGoefNRCZtsZ%2a}g36I{SZfh&9E73j%HW~fC zoOf~ra_Xz-z##@D0?i>p91s9a?faiSdds0acQMOX4*cSHB?!_0 zYrcAJti7FidnwD=KjX8hly!3)e|w5Fp{iLfgeo6MQc7{5*0CGcPjvY4R+S4cSn<~q zxW06j-%YI-utO-a9q%?UBh74mo~4aY6Hv-c*a#pc?JQdoGf+bU-mEyO#QrASB*=Qm z6jLEKVia5)-i#lCmLQLT9Q-D)3`K5;p*pp>ke~zjWggs}!0d(TUh8{k6N&G;1{ zw`6)a%na7qZp9;8S6%6CPB%AqtrLEB?!8f_J(;JHQElXdAe$g_p+6DWxje1$i$^L6 zWZfQ-id(Pa;|1{pp2q#|?b^9^o#~9$@PHS&c^0O?1Bou&@B?w&9k2NfE(!V!J~N_~ z4!tsN3()xm#_t>*#6wmFWIQd8L@U$gMsNZ~8j{vWUY;|4hcZ^x)gK@f#bKifa#fa; zg7_!F-2unZ9KPoApMlernWyCF5aBVT+!T=f67E;rfqH{ap083^i6H4TltaNDKLoeSA^A0VxFt z4n&*!BmFu?iu0Oj*2o_=rifw4o3nN6R(ro*MaU8&bXE3%V+?}KY4-`6izau*-g__w z*4c%H{*5P?PhnRLOWbWI`|vrM;u|(?v!V0j;Oerhec^AuD*nLVYJLKxB*c<99w$05D-R^J@t%e^ zQbO=Ogdrw+<**bwa8`)Y4 zp%-U!3s{_Q76OAw<-VX+aKdpQeDx$0Sy~cK+7h_IO~X0dS;_-hK&DiVP$S4EG?@Sc zcMpfsPqhcw?}}|d`b1J9;^WQN)bCi<$eae3v5SX?zyTx!Wb(ZIkAA{s_!t}b0XScmy)3ea4>-X!UhJp#1^{UZ+JEu1?5H*G#_2lmz8iIMNR;-2^xdPwM zx$%F3RAD`!ua@|m%F2^D@W+{V!gQYfS1J*LQBFTfH*{E|0N*Set{udw`gO75@)dWW zOj4CKtC}JfX9UpbEVrgOWkt&$KY&kgoV)EmikrYZQQdOy^8VFl-TpS4(+}$>SHaEb zc_BX6W1?%_g_~v>JwVV{iXlzC2Dnfpnk6C-XeN8p zKLI8GaE;$ll(xDfBh;oE59Otd0#8Y=>HgOsT#=OJ+nC7M(A@#?-G;8#$&Vdr{ufS}-0ba&O%6P>c3c~_>gUbS7WHbz_^y(G=*-)W%p z+`P`~WBV-qi21giAg$HV^H^?9yV#xl(=O=8*TC*Q=e@8T9x)dTw#!>o1wD!P_;xs@ zL*XjCoN;G)#?}}FRh^YkPK}3ZzrjxH4FiacP&dTBDiX*F&|I{4eB{B9v4#FbFti2` z2KiMb0IwO4*%ft-gaI}IRo1nt0z;n$av$IzAD@B*u%NbJar_!2?%z7?kwbEL)(lL% znq$taowEz7kg4;sO{>$+)kYwPc0OK50R$W&iE=A{EE}7k5SBZW) z_K{H_QgCqSFOm#kd=-?Lpgluk4gHAS15^*2xnjP&P3ziNzWf`= zw2)ZAKSzjBKzG)|Hhtw#jVTO5E@6sS8oQt;Z+?akDZxr}Fu(-Kqh8tH=cV4(u~c zDT7R;%aAYCe$5dAEqZ%AGrtQEPT#;7!2MktSsDgS17J(rxA-F&g06&-a^aURUcB?a zn)x1+FNg}9Ps8|Rq1%vW3k%LE7+^d#T}s2XR1@T{AshlQg!cc$zoaAeRXw>BTIft%c=ctDvK4<+Evjp-O3|a*0fJ)i9$Nr zN7n4lQl~bxZ+?o;A3q?X@J&z4?@qg_gh{>}{42iKUchfOSB?>s8_o2Z3Rf7>lY?@* zDG6`A;_vIPuHLj`-^Eq5@Ib{}S74dUC7|+(cJ_wBzFXq)B_$=uxh3@xp6uC)BpXoe zL%?@3SF`%_m5i$eeG2MVu3U+z&H`yC=rw=@94PG=I~BkG&M`&yn{AYY5Sj7KpyK(W z2eC-gWn~IaIGhsmQA1J5DHxPnYapjVIglhUHp(7VfNKpOUcwlfm-2H|suXV@dw_ zePN#Ly`w>`tVQxzq*;J;+-iM$G}G@3YcbrBsgQXsVkPp`K>=28BXjkWdiZd8&hq2O zj8^UF$o_DpGH;R>5UZBF$xJp{-&U{g0c=m^Y45fzgu|Stc#fk*h8$zBTZ1Gb+R6>T zwxob8huy~uJsnN%jkb0NzoJ^htw__tv_puG1O4P0Yz{i|PLU&iz`Go3$fqwZeSQ2- zD6Cj{T^muWF-Bf$#7G~)_zOUq2Bn&vkUE_Uc-s=_K_e-%%MU(ev8J?$MF0pZFEhB2<+ z4u?7Aq^CocGnWErAotOh9;;|ai%)mi_&<91pRNMkw&;ew%WUG^9JLS(qP>$FiE*<4 z8%82cCzc*GrC-Ugfa@BSM37kB1<){Q=LiSKCMxH4ewYM@0!-ZNgz@%R2s!qGCRf&S z;#w|2z0+9U!d1EtUyWsb#OS!7iOL^FLz?afsKc}&_)QWA%v z+=%6q)j>8FLagAb|MH-D9LfE%(dl+Vx?>uvXsX+#?!|2MUcM3el@U`zuSr$~67^yOGD zDp7E4B^lJ6@Xmf~lV8G@{#1wrKXHO6=?n^e&U2*`C@uZ@<11gIDTM72TEyY@4t9TV z(~jiYKV2V>nYMY!pzEDdtgY78OzM}L6?WvX)wjsWE{j7}vG z_E^VoqWg)wIxL$-d^E$LhC9zJK!#q>x?BgjcE0V}_xWyfXZJ>+m$_!g?ggEvFwYVb z`1q514niI`on!4bJ~2V1gi1KgNXNkz1!nE}qeXJp7PjLs#NE@*y$=af9E|g}ND}-6 zwA7Dj?s)pdAH#cQ7f(8C28gzN;9=W+T1%Q z#>bgCXlrzLO9$HV8{4;2r~t5~gu zZ7Y>FdYu_CGVnU!YXjKE15Z*~-3qNUW5g=dhmU8E(2T^<0KXh%nYGym9L^P=y89_+ z``87fE%F}qfQ222p}<*X8qaHE~^Ht-YO9_a0R7Djp(Ra$I#V#3&! zy)Tm>}0wXPHOodDo+dJbX{{QON)3M?*%@so&B}S;~Hms^k@? zy8cth#XSCpj~i&XxS&^pwy0Gq;_Phq;2|_$oR>$ED@33u=iEfF9iQmzxbsabY>$-4 z(zDO7-1JzQUrJrsPLT^iU}py#H>>sOANcc-#UlrOmL96Uz&1EsO@_H6A?-SfVu<9a zt=gi*WKgx4$K1c)+w9phi@Jzq@Bv7j0ASVzu+aov>~gzE==EY1`K}jX@mKjEWKx=K zkji4_8yTZcwiJk7;8d1JWkkPc85JBsk`l5cZzOP~>F@8qx`3~AoEyUj1xa9+>Z8|| zo=7#>%$0!85q>3R%nkNROcuO80rm_jtGzoG9^VEp=t_g~>(#ZpcaR_Q@kOaH^!6MXLftPv&!6zvaVa~=P5)bDnsl@fT7~^8 zJ$f|c1X}CPK((MhdBUw2b^vYf+CA2!Omn7B(71@sL zr$9L9c%`E7(v>S*I}hqOxT>=YpSqiU{@M0_ALuAfnGj=`|=@p<_+Yi(kvUqU!DxjB4h3 zTKt_(eU&+Ar+ zb^ckVI)`$8-8Mc|Ht(6bgFLImp(8tFu4Fo#J0I|j%{@SUaX^u|;)SnPjhD`CeM^LM zD?4?r?S)$4Wyx zoW?P;OFJSj$g6ImeRNB5HOcNCNflDz=ZRSiyFXo-ndud+B!R({h|~I_NBmbd$HlSw zUy--8qzx?(IM{nqM61B@N2riNEz-*{Im})XRtTx+RKe}oeHhDQ25XLrn53QYf9Jv? zjO?`^_rVs~5HFAA7t=l`cNu|GRUES4%SnfftjYjn6GaG6@Fm?)bp4}+j{J2h>@tch z(G4wA>JLJg@?OJya3{w=e#Rd)`&IGa@JixOlvze}4?=(tY>AUkt`ltP`wBH0>Zc7H z4&8#gU`7cK+Q`#Y$*ks=@6+~zra|4cqm2d?DCv1_xhS0YoW244N0lcxr*nKLa`+w0;bY_y68y!hHi3tI#E`zJjhs#8na zSf|TQu565(^)N8!R$U73i|Ut_=>Fr!8sW##sfrn-p{Akok;&^j^-~+dsaPg)iy} zGMP5xzPEDlIVA^vde)*=H;gcXTeP^UD#_CU8d21qW2Md zfV}50KzZxgtlVHYFKB4MseZ3r{|sID5X#H=PO_=ldq^A(exT`poSk>RE*0)M!%|Pj z)v2s2xfa#mf*98m9VfDUs~7qf?dg)Ybd}^Xs(sK;a+~9+clnLn8_^QSEH$S%-KpTC z3B@O_Oqbi{=4LxN9>rQ}06$BlkY><2S-{8d-!XcXpVJc}Bs6#xwCRg+8-wR&2H4>zZ<8fy}%V!fj8QT zj%?f6gVkpb#^zrOc7QMwB}`U=Iq%(M(NzLE3!*fUEu4axT(O{;B}UZ9g}?4idgNxC z{;ml6c-&-N>;ACYOEW{-ul~}4SOVz{wVgVQ;r~jW!)*D7U*QB~K*Z5P6BPygQ+e=h zwens7Zk4p}%0vlN85B@*LFf=kFCtB4Pe`P8E4bSPI-19eE&|4m0-^T6+~ML!TX|}% zRc7DpmbSZGN*nIJ@pGScY+C=bGCjhk8;*gdnOpX`yY>*^WwIr%rf^H{zFvFtTHzuL086Bs-58|D_ zm%R=N#^*oC&Y>0dovxC}MLU?Uu9Zt<_!*}g-Ia#l&KFtp9w{qeLw>mN^>5N>qOe;=dOE83H9Fl#ioQnJMeo4O@sT$(shhN7uk0eEjUP-=aYRYdNC~@1NFl(ii$VXP;+@j`l(H zm*Zw%S;JRpZoI6#aKqSj3I)=u40={Y0QI2QI!oYt5>9!cM^meX1PvdQ!ZbSh4!^OM zdHGeQDJM16CX(YVhoDNC9_JKHqH@ePyJT)Ii25N$^ScGj4yI>k$FiZ0s9X@^)+8T2pbMc2>gb0J2YBm0gaV)@6?xPckFIOl z0UW4@73RbT75maM&-KqKXLm&f} zmJ#!Xj7?2VSNkg0jFaqtt_2_F`L$wv=-&e;yu zH;|wE6(X#ii)1V|Pire4Z=xmYoQAUd;l^kl)N`?f!y<1^fM;&RNlo|Roso!a>$8^l zr3Pz@-@eV^2x$P)x-hd!_%XSu{PsdQto}hy@Ut55C)CR}Gb6)(W}+YOOfCYw*68wp z(UwG+4Q!+P;g+(=rGZYGqo+BD2f2Rk>;3g5^8?Y@K{bC=J^9!>h4h_KU^&A1jW5(` zDiQ2TuUL}uU`Ic!LPc*YLA$YU2Vb~Q9DLLDZX;x)`Mr{Dk9LF4EX~;m4^oY8);^&P7gzIt$P}=Cbx#h$AWk53BoQB{85^kuX zaJif3l+B!L>P~6NDF%Pvod6USjGA7F_xP2mJ1kUb*bTX3T0F21c1hFv?pYj%Xa|Kz zNdvtzFVg~++v5xCb8GmduQ)jz=?;@669H=WgrcAh41-?3Hbp z`?m2o+r|F%RjwB=J?imi)a}v4z~R*&yD%khA%c8oN@IQ36G9BiPuw#V{D)*vBNqzr z(!M4?!I`1cED49qvr!7ZMG>sJ zy#=>tG%nxi8@L6eK8zv|t8i>K_!PLX3YWTVEbHh=grghO><glBcO;ltd z2>2c~^BH={0NK+WsNS2X9#{h!HM^ma)qB}98Z=Qly1JZ7oKQXERc;R`LMe-vPJTqe zXc-j2Tp%q65y@1&B#U_eg~O>WJ>|ZOh&_)^DpZ`F8$#DbX`#mw@`1&Eh=6wih|GA7 zMpm`n=R)ltt_XBfW+Irv&#xTe;1diB9j>8-Cl}Oux!wIM2+mu`))-SQ{6Bm!zKs75 zOphz>2I&7F_!O*b>_rN94)Ze4rSlL;mBg zNDrh1B|%U}^c+3-FWbKb{F^7AD!`JqY@?yUGa(Q5@B6jB7>9eROnNvfPs@~6f*HRL zIEPWIIrQK9f8s`$9>R5aYy+MSWOl!P@2=P}*!|sdx37=GM9QqfE91Z45B{U3Fdzd) zvCrc#j7_lY)v`UJtc9zgIehuEI=QA~M|}dwA;T--!BO)HzdY4Na6fRCq7}F??g}9f zF}uPFCb|heBj&rBmGBAG-8hxW1Rw_2wdUoYm`4=t_1Ea-k`#caJn~+Ge)58sMA%DF zIt!5SVLnd*Ix?48mBM{<;Ga1IiOC+U6cu&@^bC#xvZStM=R5M0w!t#HMYdg5T3O* z5F(MtZR$17qw{17RX2QESU4^IxB0^Pzj)x!g!W>qxPvH#IrWk92)3&YB<#>`p&0hb9VTUo_)k_>9D^efdj@eHCtXD&halcVEV|OJq zTXBBt!I~lKY)&R;Fhn?bF9A8Av>+lO=6gHx7_NZ&`t^P`E zWf5hC=f7|LFV!gTh6V^4n0U?K>WTuw=oYp!nH7oqvSU0=8!8s>ahu96e|SK7yY-+} zb6Z=7?)oi>xvN8o8k3@Uc{SU$N=G!wE9%HOIgF2Dp1y!(sVmE+G`{xxAxtMygg2V& zo0$phY+9aPwZ*b4Qhd4stI2QfWs9Mzq(q;ZR$4Js#WvO*}qW_MsJ|kE{E}#s!yQYa`ERX zTpC3;zb#4HRozQVf?vLbemT>$QboMn&q!%?H2&|3PHlCeKZ?-T+I*KCeeF`;(PsqeX4SHNc6N4uR}6PZ#O&T# z6E84u{@kVi&d*kx#<6R@v;yl@o8_12bbv9~7{Ce2&){RObuor=um=D8tF3=}%f|m% zm-{0I(-p=hZbQPYIToepu?Ns&i+5$43y#EM^^|7&@88eq^{D+`G`4K3XV#_opT(7Y zkavp@2~9EMDkvz}*hm{F^;%^-wzsJLeiAKVDYE-UF|&_HlTBPT$_l;De(v52X#%6E z(!c({EpQJ)$F0-(g1EPKhAwCtF^hLnf9eW8S5Sa?XkfL(j!9pEpb4w~z0%Us8_lK0 zV)CdDsWr(sIQnQAYdU04RK0A^4qe|yX?=0V)xExR&(H%zqdjcw|4GBw0IIw6{s=S~ zB(fPyjofa5m+%AYZ}9Yn7fD$(J5D9(`%<8w=j{t?q(^C%@XLA!{=6RA32%N&m2sor zm-Tg{dESV?OP7JpNG+dhv<~{Olko5Vv;QZsb2m0u3-g$6N81EjDVBWZuTR|9N(21; zmPE}%Z(;q*zuT|79@o!!_<{9jxZ8LXR*5-eKT-`s)LWxeZAM}#*7*MI%ID7`dk}jI z_K<;AQoLxi?f&_Pz(=Gv22-g$N#PgxVbBSBMa^}k-cbI?Bd7Nw6w!E0U+Z?Aqrjy+xKAe``_z{@yP<4uLx+`NT<~2 z>^gyMX;Q5Ir`s&;!e45wXg#Z~$SZ0W%4Y&MHj^E@KfBCow>ysg-V43r!5Lc@+8PJX z34=G=?^?ijR|~zcL>i*EB<1 z)`eomN5*W#?B5kuZSvNHErc-b|GsBoZLC*w$QwM<8vMZtMZD|cfW>fFw_MiNyKm7t z<=NR85DY3M<;{%C-X$zSgb_U39J@OZyZjW4d;NnGw98iQuEa?BgdE?8F?|jn>Vp=y zX$^lk*LBGF|7XhUzoZs@$Fe$_-fXS?MB2lnvFVD)p9!xWUMgTPvE9gt8C3cNd=~!l zZNMv(%$!L%2NW~(az6{@|9!|8L)|2$xA+Q(2s{iPsV2@(=o2>GwSqXO<=sK1l5Eo( zC1G$O<@bka<=t_Tvo-c_%QC%S>(*Z_PLH<%S5DTE|v{mJffEYF7c;~0k;+(XQs#aNmtH;3wS%Kc$>qEAZE529$2d9^2;=Jj7z zOL40kYNwD^1jTKpTeZS)p%O7M@XMzkLad%#Fu3^*+PgQhW;@ZbBW(Khi8>P#> z+LLH~txXt>Q+JP+!0Fg1XsvXcz~)k4scZ1xm-@pUYBa3XF>OgW_kTyRG8Csnz?(f) zkiqPMhU-&FG5ID2TAxT3-BS3}ug|f$7x(@>J3;$*1xcKgF`zQn@szaQ=;ubZ+_%$P5Dd)1} z@3C8Q;#7nL?hMjlM{ECuZ?etiZ17Lm1kwL~m~RYzxZA|%>^4~U`@^j7fwEN~1bB(C z>0isej@$lEd+!0%)Ykru;;|hoVnd}Vh@c!mkRB;k6r?C3QgZ~ACLj_B5Fl8NB1pi3 zC`Aw{B2A>%Sb(T>K{|p|b#%YpK~y`OIHm|l)&@wX#V^k!ukH>f&RZUF3(;`E zSon1PY<#$2HXb9xG+VjbbMzV8ibjWI#JC*+YZgz~pv9j6qn)TKrQUqz-OIZ!B4ts$ z2533jS1d3e^41>MQaU{*<=V=-)|_{R|5L!`{y(hzat@?Ny1M-b!Nygljl{YmZ9`=3 z;2?{pV7P^bH0W3%!bj0O&oA#(7_6YU#Kgqbf+Z5n2%`EvKf~0tuBt5k-VN~ zEzHQUjBGaP;p6u0AH>;FL@_`pmpJoStt&rMuJ@M9HU;cd32zyow2shDf0_7pC@U$& zZ(OETiI^53r|g^UG7cUih#QP%O(b%BJtZz3LlIPx^dx=Q&+j9Jm`R{l{~zF!1{%VS zj3|!^6C0qEi-&XXNKtu}K*oJgf<^u2S!s9r`Sm4<-svw&wX^XT#e8@|=RYqx40aH4 zauye9cpT|or8mB3T?A^piTz-vRwD-3c|!l;rr6I=!OKSMPegPxBAFwDl^i4y<01)< z^h9tLi8LC6%5C)mx5Txp9BzdHdDmVQ8vua5^vtZO1iEvc z*sy&u%$yxc7|K;D)I)Kj8I)(Kpu8za+xv)9C15)rA6q~F=x9&ms`KuyOxd#`nCuKMCWL@bt4m>Z8xnbfj>0qf+{MjRJ z6lfEL{ZO9iF*gf3Eh!J$7Tp0B2@0c$C-wC7vfS7M>taG|LM|*~RQlx%^#eUs4F=M7 z&fhP+*>;!>(Mcc>QwLOC6jCvDYY^;6 zCk{NlN`-<-xuS{HW{!i!MS32!_g1*`72v;{{bC_btzu3JSb^Bb5JvQ0MqtxOra%<2 z(8c3$VN*|GifoYIw)OUKi(Xl7;vCJ8ee|qb` zjuw@vezB#MZbmTI#D^Vsi^>LC$k~nOOPaZj4aehcHRS!l3#%> zp-GfRa1Q(qH9in}2*8+HNThm|s;4rfD|h)CKZnE1&8>nz(+?G5WlL`0?3VP}c$k1(0#CA_)&M0eqU({s64tY<*CFVTb z34g&C26aKr02ua%TkuV8*Z}en8VXBYZ3dwxs}DJ&005?lRmmRj_CslN2(qrYAmAUY}vXd?8pP^9`n^LE)Us3eG* z6RlXZep9oEr9pqf!Yhy{9^CW&cJtdz&>~!Q+r11}y7AXoit%Vsk*y-i<)bM&kmD>a zs0)S<0)IGcIeqQF)NBQaVOuK#VZi6?Rxh!;BAX-xm6hBVr6@>sXhOE)?Mvo%1xA=j zN_PdGeHw5Ra}dUZgD1Q$0bx>n``$h znvF(`k`}Cz*59Qa6M$^j7%u)o^^yKhZeq>?w)6zj`AZAFC^mOK1Jfde&@{TSF`Xcl zURv7fMZ%A?>`?4}za*|Q&PyOW@+o9~KS#_j^Qa*xi<%i>WIO=HD|SOvvt|wf(uFz7 zcc(TGcYL}#VqVt)m#goGMRVIsXSCzt^M~ruSbf;ZU_Dq)At1S_MJU*L1E8k@5@wOn zc|Xd-;oboBDItFUjR;s)AYl}KeoVQ#1~qV}wj%Vy?Im!QPqc*{u^LoIka4+j3FxnG z8AAUSED?VNqOzaSS#&b4+%yR<=MJ}!AkS{7OQ;+~KNDgN9D?N#@kY>9Kg^?&B+{oG1;bIZ?geEX0HX86P`yrx(69V{-;bIqq*^`_s z7woU%43u6pI|gSNDp9tG=H{a3lwb~R4}pXN&tX3(Y?_M1)_@#TfF|e*!5xO2R-$kT zpEpU|s0LKTg-yYeIPn<_2YQ=*6EVs6gndQnQE-J*{*6KG66ss2Uh}Q4poR!Qr-9qx zTK^c9C^FuC(LL|CzX4dZZ*Z~mAm7O<{dvlUJ65a_+~t;KLn&c=MNt`9^c*R06~ViD z`_EM}YaG(ZSIDZc=A9A><7(KA+bY&B@wHOtniaijx0!=Eu_{5{RuEp*EUrkeYFha8h%Ef~IR9SBONzYZkzMvwQHv&E2AA)2+$(ra=85+Ywv)NEIY z6G)cxEZ2VUn9*9~v@1$+^e{KpbL#f8VH%<=Cdy2|H-oL7r`pg7QPYPiZ2y5PN2WLw1eJ$PzqO6oV zROIx#-N>!e)Z<`{YDOvZL^c7mE{U*4OA4l^U_+ao937&N0a}3RXAU!^eiJ%q>>YWu zz$T=xA>Mh@$4pWq%50q@^K>^biL5-19XhNgLc) zvMObIa2lw?CAV4`zrLCVB=lq4SfF>oka~!3X}-|*ee)=m_?6C z6fz9LAU#A`uVTeIu=JAdElk>DwF8TdP`HoJ!6IN9s*}YQUe2GIjf{wP`w;JvNZ{Um z#uLhsgOlqucT=a4Xb!xl2pD)Jta>^CK`E}lAlVJed&7^Gx|gS@00@kl`%T3__l8LD z7cxD7?`Rs1%D}t(EMsuQ8$7tz`y6z z3_nPE>`v&LJp^_q!1jfCD7MCSqvl@##7h>S&K?|U1M=w3L-v z{SavA1xSCqECm0J71@NJ`8ohR<93YEx##0>nLG+J+{CLs<_Q?}6BPO~x&1pG&UtHs zf+AD_nP1WXj82TI0|xw6YP1{EW#kM;QfwJwexHC~jcXG`Le^d0bUV+WpC z?6$pl`UVp71CH3;65SJO4~dEprm(jHJ?d4+E}{0xPQ;dR9)Eq>A?*jw9yL99KY75D z2xLZW9OXjn?|5y}i$}kQKuS1~>f~v}UEwJ4+V|S9+~>xWx=p+qvlq##h`&^{u~mGj z|G4Wv`;SFLA7X|u5Uk+5KFKu2Kj47!c&{vczk8ALc2Ss*uGKZq853DRwO^3D(Yo5n z{_)C-bWh~qUmBSdjU{4k4F{)HlH^oLF_3m3D6i_@3-WTttG`}ijKB*qCD_~WrKuF& zLHgzb87Yr-*!?&UL?)j@k?75UHS-1NsMh`V_v^^ST)Wwf)~FtE172G~qz7*H?n8c~ zm6V@CA_rsfKF-5(OLWKVN2?4>LuFU%u?<9YfC2sBUf-;GHXfcdMi6mrdC4*1i<6g0 zfSt^Oe8OH40RlGJiUT>0P1ESB;h7t2Ig^7zFt&8zm^ZbyR^S2B!7VV3>NhbI9ON= zTNhO5o?x_m`d-)n$7?eLWx`wNOb6+V(Oz`5S4i`xkyso|en~Pc+X%}&0xa*XuTe!{ z1Ouyu@&=)_D9fEJf$pYK3{xr&1}3dn0Tej-bcJPK_IB~!0K!`zW{f!D@H?QNdl_uMm&20c!4mnHemEDn|uM?IpMTVS6PqI z(x^{tLNw&*h#dlC+XBnG9EkL)KHf3zMdF=r{46Ou6uqSQiAW+3>{D;@%;Kx!$OObf zYQfw2^-0iiJ$IUQ^@s|che9A()omNd2h?%v#1*lruK#C3UjA0EO{hU3e#A5O@c2KS zJ9*3@Jnetfr!k2kj~WCYJ_zPAe#u~+{zuT|u;Y|3GCo28@wu5VAVpA(xqVK1k(Q}o zQUoA=0je0)9*$Jor*#$d-LeMCRkgTOS8y-{Z~S__B+&A-*J2whQKe9;;Y7%C%jA#1 z6mE>*e?2Vj`NX?p@Zy56?O#Svv&Lk(`HdH`!=c-KoNi-Yuyo0z$26`eALTCJzY+2N zK@6W`YodsU!xrEC3Q0X{2qU;~U`yDwL|Puc(d37#`-ITot|R4cm|NlD6GvlppDQv; zi+f@;E*-})RAguQ zVt$Jerm}_*6|)D;zS@n4NBYHI=B5rFGX(-E2a> zpfr%Ikk}s>FGT3&=mWOn1bPmYv_$zIV!sy-J4--pV|5|lRrsJU@Q${SoEjht_lfw$ zUGBSy45UAMq`!mEITB8}6+;bttwodSdETGTvv#PClGd^tQOL02}(j{CooQ18K$oltwM#-YX?GEp@(;f+=Zf90w21Ck#SWQ?f z9KyJ*IpOHEz}`xepMtV*r2ap_!2geH&Di$Wkh9!Jvhs4jH0a4=`BZd|AzXSp{unWq zzwS5kEeMU>&11Z~T7S|IJb#^R8IAM~<+rUlCo-hJph_85xJ1IA+<9uP8!l}Rjj~~A zPG3j5go37FqgC2hqWdI>^h5BQxR3PP9>Ve)glgPuD?+EeKqtErkll-=J|}7rca4)$ zTI69ik`C~)wveUr(nx5T9jEr+z9zfDZsH&VpzLb?`2 zx}K~3c*2W>ZFj%EB;?sgVH;qSja!Vjn)_jY88C^GX6vkcR5>Q#`O-ww-cmF~YE`GG z5|F&*jje=IeBe1f+8}qLkR@o5B3)=+SQW-uT)aR^8n9B1-AXmmUeSLJR4i^{dW##t zj5X1BN!VkK+Eam*bFrN`SYmzcXKn`U5 z)+e!|5j7S1bfNnYkg$ICMrPL9$dKO4@US zxCOB@v_PUlNJ5O)cmYI%dD{w}Ppln)-n?kn)ZBq3M#Tox4qyM9JQ{|_Nde#E0Is(N zx3zr-v;lGaJwco5XnjV@7B^@p>5V|r#fi2@#XV3`-@62wZ5K^V>BT+m69bPyH0lAW z11r$svb88Ha#f`E!qft5L3{9OolynjoW44ZR`W<62bLio8Fl*C^1BIyixXC8OObJt zG+m+FO1bzGAuY*y><+GhF)T)ot&m3xa~zGdtL`LG%=jyt8lft|EPxDab5mGFoTbVKaz; zzrCh6xFb80Iqw=Px`(gJ&L;L$@f5OBCT)E5H#sCA2lgDxf^)tAbcT%xzkbb*DUT6| zOfqf>qwDl%jnDrSLeJ#EZkiR6u**g3tmj-X>%_-nyux8if<%iiFdFPrpF0CwKA{JS zw>HJ3xmSoz^dwF9>tP9UBaB*P7bzh`f&j_X4U z___5`5JDg>b5e6sDnNDNBqH(L0b`?SIV4azG<=T+ng)XPsT(LUF?x(;JzhN$^Lt*Y zuyMkRq+CqggUkxd8pgYp2=;t&R(qAM|L`yFX?nWM3>^9X3Ah33AqSrTfR~2KVpw^R zC|-;bWT*>_M))MpxaK*sg#xB? za?gi-MUyl4o+-UU7ire`VgCxgyCFG7n%gm^cy6jys;|oT02QHWMfx!h$RR{vy55$` zd4!W`vpce$rW3vxr0dL`-+vUgd8p+S7P3$ov^qSqk?+!D_1u|z8@-~0yu;%C-f+7N zE%O(er3&FWw7(O7fQhLpRJ05M+9sEng|RR85ZRp`#Ta45AqqW|l$<5Bhud!FcQ_F^ zP9XmJR+%hz^qet$;A|c2f7LpR`n#pBk?-%v)Rg8trVY!<>Dc*w( z3rsvsgKa9sJsiFUgf=)C=eU|1J53xI*zSwVrp>#!ZxFbq^JzO+U$q>KE4^MMI254d ze%vnp((P~Djh8&x_rSlYT9Cw-rqCj;CPF^?!K>h#alnf7WHIhak!|pxP-;0QDcchX zi~q*;4U-Wcq`1fTYvsCPiN&8{_ag$_1mD;!?zz2+28M9)m#sM?usi5yL!Ds^wipTC z0^WXkpQhVApGiYIchU(7^>6n~W-a6tW5r9=Q(b%pY-qT#?$m@7o3Y0!=QNE2Lxn(# zoa(#9lf|??OwpTP9TT>_XSFtaVUY5xMZz@n_rdm0-tuW+HZcxTCZ%C+*T8c$BHgZT zOvP)uFNishe5$wQvB5vL?$;Jrej)_~Hd*Zb)fX97B8V83s_S&e3VG+w%n`?GW?y^L z>6t#k%6^N(^mQo@!nYugvtMNT*5*=FuQz@BE(iQnv#rUa#1g>kMcAptr^8pRkER3M%un0xp2nh8QRYEc_(7 zt9siSA=rg<1Cosk&(>h5gJl9>t8z^fU>vY{;YY2r114u5Vp&(N9Q8+}f8n2FUk{%x z0EY_>8b%@p0?~^!xQsz|Nfgvx`Z5sDjaWK=5-~IhxIrGCg7YQ0;CH~+Qn(r4tKsq< z;b}(Nb|ymtO2_R_!vW=I?54ttrHOmo_i~4d-Im*$2w6`FsNAVphd1PrE<86gdI8KH zRH=K37=WR;BK_VA6w;Nft!qeT(yrnVL$x)%Cl{WQ<)5P+`=B9Ej_yOPn~ zVIwd**5QZZhef)BM5GLaURhS-9W93`8QRB>gA6IS3-%i#XqQH0&(Z%wZ^Lc1kM8oq zSsrWr?s7_Kd0e4~J9*UDQJ{PB`((0>$4-|F$AB*erD19(i*}uB@@;^fc$_EOVvZ@$ zU0rxrnkd$CRI2$WS}Hv`CF?~;g_XChfQAD-BT);D{1t-6q?zpU;GXXEC=4?Lm&AxAuy zq0+VZ!~d&)Tm*yIuSO8ccDGQC6Lnv|TTElL{mLTc>;9;Hp(5$NVz-#gv!Vl6Wz8R} zt=hT`Vv@zVY2Af9(Z7HEOM`#qfahOb00-+|Irx_bU=RM4gMVp&xCQ_JlLL1HA^-HA zu3mT7;o;uog$fT(-5OJaRcbmA92sdNUi_)ZWXG0G?vA_8{f?8&oYi;os7XVF_Wz*6 z1k>S>^z+4C+g74eD2`)+PZ|d&HBA=c4*w7cq%)6YJc{4>G{gSXoIDQ(vU&y|1IIqooq-XEO2257ajIoiUutTQs#J3Vp+0>oqyHgtUKioy+4h$vr#l!S z%Z*5}0Z%YNJKnmtd4C;B88RZSxBMuZ;?P~4V_Eb>_4-hDg2}1V4?|Dp4xQcn((xGI z>AA$Ll$IQpNOzfKr4gzBAOxkSg}5_}?Z;5wm=8C$&kB~8Gp@C(+(=5ZVf(d8K7nvR z&3#4x^?}??O9bP2WK^)Ktt?P+(zWfFi^p`2AMPRrX=0=~C-c&~lPNdzC2xu3g|U@= zL?K@3jGzWZbPyqrF^zINd*E4qUyittWDh!h4 zi|p?#F+U?aKgNi9S&n4{>_zd}B(_Fz@)OE6FH>%(l{ zqEV{HYVUxAtyU7G8nW+yd*qDdzmjc2FRAP12FlV+g_TRDtp+8z3wW=Mgo?u=Ld3IG z?T$jWm8&(B^RX6rKZLXek8SjB=E3YlQ*7IJ+D+}hF(a) zvtn5zvv*%R{_@Qi&kYO^7VqqC&1)`?5M^S$e>90_ z;)yRJ=ZmSi39c>vFGl+VbCO>jNdkaKNrRdA^znj1@W_wc>~)uOx=hr=YuMd;k%>QH zUxm)ma97|g!2b-hA=0sjC1H|4Sa$g$#Of%*u1z>+^TL5#T}@LyZ5CtbC3G+mU$<;c3)s zl)I|<`T5eUV>c&CQ5mChL;M?u;N(5#K;WHg4~Z-%OUUuUwTAN@6)vn&O`oF1Ozx2K zw60#qJeEFBZrzyT206!p!D?rLB3=4)ixN9jZ5L)2(w-t~TGEUCqi)qawUekkQ{e)9 zSYqF|2*~l)N^M(nv!<(Poa~HgHx@ha@d)-8Y(_Clo1fb-dhd!OKuF8aHY&v)(;mFq zB?hhpKpw4qs$CoV&kXKTDhsjlw9RUt>c~|&PB zMD^que=lPhM{0&Ya^^!#d?g08!nF0srNH4RGgERL$#EoNCZ(wy(w>o>O@PF%kO7wV zjtWi;PPMDI@tu~#^(J={O`A7u5ZHx&ENkl)()@_p($SojehJdCIL-b-!1Vw&D<)$4 z8)=?SNm9>~4$dsPCY1H`fCd*gG4T=?8WRYHp*}-!W#;1;i|_rpR^pv;E+HK_Wn<`L zP@~Y(>_?B~{2BLG?$v!wCFX8=MzvCLGm}G!!cuXQL&IsN4K6gdYtGduN^@{}7fH5+ zzsEy1goc}^Fa~F0`UQAPt*DL$efu|mb8rj1yl!w#%Db$^(wi!XpAIfO>7wS*Z@ifi z(zzc#3o;j(phwnti)C!rv~p`ce*D!t%Zx|P(tc6FX0sOsELBon$c`ZqlishC{i=Jb z7@pF;glgZ;tJ0Pw1Hs)bmbnCbG3GXfywDjDzme+dXvi`J>o`eQZ{Zv5{!)}}=Xbp9 zeVRGD#PY91O?J)I6CVS3$&J%v_8Zoe_tnO?&3wHl= zZ}+*c@IQ&jQR9q@i%0vEx^6dGgDkPA@KqPjk24zi;50Sqieq;eR!KI=Z@{Kk?)uQ9 z3uH0OhQK@7h~e#lk3j^iR7*ljrv?+#I#~^e0Q}5dSX@<6`#hJyPs*&&no_tHFg@Sk zqFgw~{3K=O>Qd(}x%t}xUSgM?NS&8c2VwSAO{YtHV?DQ>{D(DIEn6l0_t|NH9pxe# zq>d{tE%#0&tonkqBbuI%RQp5AD(k5dnczJvQ)0so{I=M*FgveVQ3bRF)Y`@PRlCgG z`PL5xs@zs@jyXD5gongmgIQ^z25>bRTP)qEc&$H_bklljE{Q^5t69Iln&-># zr*+3OO^$;>9^g)_FtN3=DV_iq)R|L*r~j7Dmj;lOnKb%G-)Sb|?z0?>`-9E|Mj{8u zl7_9e=NY&D9W*?Ud+F0E+yA->R_TfE+l>Ud*Wr0q2C)e^iV3;?F=1-sF@6nm0o0&!~xcZl;PD!1K7{9>! zAaTlH6cM9*{IGtobM03F%@bTWjulHw#%tcC%rrO3@vNQtUcxMEX0&=_wF8SfdzHGr zoOl=-oZB{5v|D$OLWw;N#q?gsUEP}Oq{Mm}bvVyy(I{Bfpy&HkEMxOo(+P}j7e{4^ zv>OOXk4-!47~mu%j_n^($s=m*4S%%C&COtUbt+jig&*2B-{5lDtn2O~zk+6cx3qQ2dD}~T+G=3e6>d{LZKY4tDmT7DOI94;{obtwgtf1GN=vlia>5Z;h zn&n5{RWEDK{w%^C)YOu$Z&Maro+opkNg8=FUTCUoqCB%IF4R#%bLPS;<;{qL-*9rO z5?4JY=%O)Z$#U-#H{VF>+_TC+VW#BMKM0l z)#T&y*~wEvE7dHJ8<*^I>rxjGLCO6=To%_!c;Uw4$f{)ztpo=?D;TS=@j~$4ZRX!3 zRz_D_qS$Hf5Wal6ng_qOK#*4B_@=oeX1u!oeVQAZpKlaUO~HKtOsDhccqLZvc-ifY z=53ay87?7d^X#b+jn8k!YPL+*4v?QaKT!zYY-RoAX7pvRZH0z#Y2&5WRZrCjdoNd6 z-_2}%e*dMl^^DTcjtv#AC+n?G_Uv*j7k%>XOW^G|!+m=sZ7`=|&8}_2p3<^99}!;X zYZD(CA4%4<@K){gEpl0&XW&*#9p|qtsP!G83zw@kjh7H54piz_GQF{k7d?y~UF|Xn z{qO{?JgV&6m?L<9e9wLE^vFy#-?(YXHAQT5w)XsR~)56|qwv;xWr+Rab);mdq=LPLd1CerU|#7^|< zaO;8ByQ8kJesaKa&6izAjuz%9GTK$@eD=ar1CK6Fb4!*p+O18TXX`)dcPXfO zs5$XAQLku)zec8K%4%-)&!2qbqfqEd4w*Eroi180sdnV(n3?Fs<(JE_zCB z&x`;ge}3>!; zJ2A;MnH5P-RM}pC!^;Eq;YWm}ur!^b6>EP*=iP9vPFhvrf1}!)8~1sSeXQYakhy7f z@p_jj^bfE4?r>oZnn1H<+npUAtWKIOIzI5|X{@d+ZK`hdkc-?!-JhhE> zUS_xX1)9cIx^K%PkDZ1Jx9(sD60qtM%UZ{g@!+ktD_q%_aMaIV{?4H*u_9a7KaOv2 z{9_Q0iknxD?AjfP5$7_hpIqo$4Z;+<)^g4ME6~?FKXzBPF3+jpMeOQi&MO7)^dJ5f z7J5$n)G3MDYivE>RlLrk$fTDrE(L8F@juhRedFVYPidKojH2vHo>O6qy;$P7ERNSV zIjZw;_ntM88|Rg1n|Wqe!ftoYbk*EGLQd??lYK{yZhrb_4cuk2*j7l$LACo1%Ac(b zZS}m`R@ULO1?_|Il;$Xpor3ogh%Bvju9COP%{2ou2x&S+-1yYrkITdNu7#7ftG~(I zJWOu>KJp~Tf$=im{-~}`H)d2y=aW4Y^quVakFX7KKd$@C^p=3?)MZ?I=I*gxviUtf zXJ$X1y05kdt|LFWSj>J|Hu|G6>zmAS{k32IY%9rTegb;+W4EKmxYrl$7CjmO2ab)4 zc*}8xI_E?&d*jhK*YN9Np{13zs_L+4&eQ+nM;Clo+QkjnwNrVfO7nWLocNK@#T(ps zc;u_f^jXrsJlHL~4 zeRs>fz*C*zTl?OtvXF=op2toT5A9p{t#f!dKZJb~!$|dak)888#1v5%>(|r$LYa!Wm zPiNA43aib#C1i=af(tAZl=`fH7EsIb9 zl)Cltq*{G*BO|wBVvLYW)*X#mddO<*KZ;LFDg=b!^ z{Dm9%P9DG3UMl9CKiYI_Dmf?te#mEyyLq$bAtDPPGW+sqdzPO_5Zhy@K*9GFOx?>u zrZ0{^EInVDG%qPC>X%!p)CBhmcHgvvmGs!a*Jp-J@lCzn(rIbYe)nB5ogfHtrGDtc zlXJG#?NdUu3=!Yh;pz;dz)QWlnuHBG0_~13e8^?_zgQ7)1!;rLF(b)p?Zfr9_ll#l*9N&S%bOxEw{6kV)D#T2H2LP$ut^ogeyE zj3c^7=9R&p5&239SI=1-c5?6;3^_@<+ZLS3nlX-^N*0T&Wn7Jnh(LY!9-e1}X4O2v zdJG4$vnKhG7czm19X?D;Uo){#=_I(OE+@fqh2gFdxGtj1wOqRw^(GIf>s&gQUiOIs zy!PyBOCy>I@tJ#y+I;gPq5^1P);*0PeoeDEoj7Smru$(GTm7ung{Fa=^g`wI zxzRuw>K|O0iqe^CqzAXUrPIb>qoq|{FW)DG2L;Wwx)zmr1|8pLXPRgSuIK+Kbtyk# zR7=ldW?q|F2TZ5uh7_Sn3yOL|(B@h1Y8_VTL?WB$BGiWhi-{eQvQk0#*(qh4>+bH% zoZA~VSmwSWk4@qC9=&7au&=x$C1dbL=im`z*GlC_ypKW5Pl8^(VZ;?}x^%n@ehq|G zsQJ2Z=jeYfOm>)m^z6M-lCZwgb<=s)8yV_+!);!PighE*3uJc*f{4H&+cO3h%&x(3 z<}fNRJGX-IV)jV488&IynD{A&2sB?$*y9E4t z4Qr3y)xZ;V27&S?=^)=z({H!%$;}k6xj!0VVig=&I+aK3QJ=)2)c=If6iMJL7^B+4 zfuN%BrPZ}>UOii7VuD?(wEc!G!M9~$_n?Zg@%i(Z!lul69gRaW1u7uP^%~?{Uu|o) z`%K`^(bBAW);Hm1L5*v6f0@r-K7M>X+(G;GYoIX2?cKGx^4zlNZhND3-W21kjO=^s zw+0hnQ@4d{D_+{pie&gBlUN)O(2_kvEA#AoFwp5Sg?jVuo#89rpB_@TPLN$~G@^H~XK(Xv?ujlU>pfUU>rnP-soMcliMjiy zn5a@oSjTdD%k_ORN9p|UM*%|v`#`O?D`bQk{z!IiYh1VXMY|lPrS6$-k~ zAMNp4-YeGJ^t;4FeL)8ZGGj5K(Hj;3P|`_Apm_p&J0)dH&rtzdc6M%}rJCVKcs;Ha zsCSDBsCeo@%4*{rc7cuUr{<=)!?cWr9f(?*uH-UT`|vs`#m5rD&CDsfW~j%2)|xGQ zSIm~|IaZ@nbjON0xmpB+8qv40k)e$(f^K?p@>_kMdm9zSE4EYwpMFp)EEm^4!!vOv zbdzKRmVzh7dI^baMCH25$V%y2r1-u{k99XUGq*9ABexh4^Jz(PVLye6(RyE2jyNI)Y$o#1+$@fomt*XIey$APiV^ouE3iM!QW$_9S%KU~Xn+&0AcKs8vEn(CPseyn! z2>t}yACd_ItD{G=0<(LuF2mkqq$7Gq2pH9zh{aVV*S)8F4fl)M!7DH|TIOkJY;4W? zN-VihSLl`z$)82F5D3Tn>7R*oHH}eBhN-Xs4?v!IpQ<-YOfGBP3)9hid6O8EmZA56 z{WB~2_8s(Wci8>Wz%b2WkLssQP19r}%~KYUprqfK^>PPIKS#;p2ZEBD7LFYn{`HSS z(tziic7Wa;sJ@2#s$pTj75FK|7TY9C^Rh>UBI%@xGR4n?JOj3J6iZ=w#C%_U`7t8&AhA7v zA3Ii6;%tT3HrH$y0Odv}@@C}@PseU`#A^wMS@Pxi*jQQndus8rn$a3JVPC7NTWqSBS~ z3k!`lWwsD#u{-z>6Z)6W&rw8UaoCSw7&%LTC4*eG_|@_Rc4gFYiKUr zoMk&_N}d$h+qJ_c&F;G(5xJpQ(pzzQk#7AqK)4Nv`(*KGUO765Bf%X}27ofdQWG!xIaB%#_Rjt#t)4{r7RM zZ(qKw#DPsawTGLk;EDaIC#b13QYyA{Zxw$kLV-@eVU0nZ>AE?_UbQVAwqUwDd`W4( z!lDlX5PN=ageY;fwlFSshJBDtcrAQ>8(`FwSDKd6lO#(X!#41_%h`T|_ovadEXO4M zTfkeJ3UPkYKh);3mDJ{~J`=ydg9s5fIK0adKkHp=%D52D`v{Q|TFkyGSrUZ$oU6FJ1-b+sAy zMbiCL9VoAdvO^9d{vC=c+eXBbO6>d}m)BRkKix2@Lb;+FGraYuqk{v1Yk~D0NF|_* zAiSSLv-F>DveI>Q9H!uRi;CvF7voM)3q9bOJqi;TQlR#e>9KF#Kpdd5KL&SzR;{Ga z7gta)^W^n5NonAzFIdvJZUOa0j zao{m5J@ML0!KA;&_ZF~@D|iS1y9+O9#k3g~$w4%#auSKhCK4bljD0phT60OC7er!FDCT8^GzTUc{ zg@;7@(p)~jZGiip+P=yBA-Mt6{C|X>L*hw5owh6D!^srrX|=KOWD$J9(kNzqo-G0|EzM`P`E2|aje~+RxIt8=lS;!xUcG8 zg8WMZ(2jrQ;D1kpd0FMPx1)sIdg6gt;+PW`F_$l4uBh4ExB~yjBY#l-kn}+n_|JJc zc{N1^H3fO8g9p_P9&Ej~G5x<9IJsQ5zv}hBZtz+z7fAt1E{%Y(ce>(+x#V>Hf8B 80:af:f2:f6:58:b7 + 2023-08-08 15:50:08,413 INFO: Added MAC table entry: Port 6 -> 84:20:7c:ec:a5:c6 + + + +**switch_2 sys log** + +.. code-block:: + + 2023-08-08 15:50:08,374 INFO: Turned on + 2023-08-08 15:50:08,381 INFO: SwitchPort aa:58:fa:66:d7:be enabled + 2023-08-08 15:50:08,383 INFO: SwitchPort 72:d2:1e:88:e9:45 enabled + 2023-08-08 15:50:08,384 INFO: SwitchPort 96:77:39:d1:de:44 enabled + 2023-08-08 15:50:08,411 INFO: Added MAC table entry: Port 6 -> 80:af:f2:f6:58:b7 + 2023-08-08 15:50:08,412 INFO: Added MAC table entry: Port 2 -> 84:20:7c:ec:a5:c6 diff --git a/docs/source/simulation_components/network/transport_to_data_link_layer.rst b/docs/source/simulation_components/network/transport_to_data_link_layer.rst index 9332b57c..4961d337 100644 --- a/docs/source/simulation_components/network/transport_to_data_link_layer.rst +++ b/docs/source/simulation_components/network/transport_to_data_link_layer.rst @@ -55,6 +55,19 @@ PrimAITE-specific metadata required for reinforcement learning (RL) purposes. Data Link Layer (Layer 2) ######################### +**ARPEntry:** Represents an entry in the ARP cache. It consists of the following fields: + + - **mac_address:** The MAC address associated with the IP address. + - **nic_uuid:** The NIC (Network Interface Card) UUID through which the NIC with the IP address is reachable. + +**ARPPacket:** Represents the ARP layer of a network frame, and it includes the following fields: + + - **request:** ARP operation. Set to True for a request and False for a reply. + - **sender_mac_addr:** Sender's MAC address. + - **sender_ip:** Sender's IP address (IPv4 format). + - **target_mac_addr:** Target's MAC address. + - **target_ip:** Target's IP address (IPv4 format). + **EthernetHeader:** Represents the Ethernet layer of a network frame. It includes source and destination MAC addresses. This header is used to identify the physical hardware addresses of devices on a local network. diff --git a/src/primaite/simulator/network/hardware/base.py b/src/primaite/simulator/network/hardware/base.py index 739fb933..eb406521 100644 --- a/src/primaite/simulator/network/hardware/base.py +++ b/src/primaite/simulator/network/hardware/base.py @@ -6,6 +6,8 @@ from enum import Enum from ipaddress import IPv4Address, IPv4Network from typing import Any, Dict, List, Optional, Tuple, Union +from prettytable import PrettyTable + from primaite import getLogger from primaite.exceptions import NetworkError from primaite.simulator.core import SimComponent @@ -136,22 +138,23 @@ class NIC(SimComponent): if self.connected_node: if self.connected_node.operating_state == NodeOperatingState.ON: self.enabled = True - _LOGGER.info(f"NIC {self} enabled") + self.connected_node.sys_log.info(f"NIC {self} enabled") self.pcap = PacketCapture(hostname=self.connected_node.hostname, ip_address=self.ip_address) if self.connected_link: self.connected_link.endpoint_up() else: - _LOGGER.info(f"NIC {self} cannot be enabled as the endpoint is not turned on") + self.connected_node.sys_log.error(f"NIC {self} cannot be enabled as the endpoint is not turned on") else: - msg = f"NIC {self} cannot be enabled as it is not connected to a Node" - _LOGGER.error(msg) - raise NetworkError(msg) + _LOGGER.error(f"NIC {self} cannot be enabled as it is not connected to a Node") def disable(self): """Disable the NIC.""" if self.enabled: self.enabled = False - _LOGGER.info(f"NIC {self} disabled") + if self.connected_node: + self.connected_node.sys_log.info(f"NIC {self} disabled") + else: + _LOGGER.info(f"NIC {self} disabled") if self.connected_link: self.connected_link.endpoint_down() @@ -161,7 +164,6 @@ class NIC(SimComponent): :param link: The link to which the NIC is connected. :type link: :class:`~primaite.simulator.network.transmission.physical_layer.Link` - :raise NetworkError: When an attempt to connect a Link is made while the NIC has a connected Link. """ if not self.connected_link: if self.connected_link != link: @@ -169,11 +171,9 @@ class NIC(SimComponent): self.connected_link = link _LOGGER.info(f"NIC {self} connected to Link {link}") else: - _LOGGER.warning(f"Cannot connect link to NIC ({self.mac_address}) as it is already connected") + _LOGGER.error(f"Cannot connect Link to NIC ({self.mac_address}) as it is already connected") else: - msg = f"Cannot connect link to NIC ({self.mac_address}) as it already has a connection" - _LOGGER.error(msg) - raise NetworkError(msg) + _LOGGER.error(f"Cannot connect Link to NIC ({self.mac_address}) as it already has a connection") def disconnect_link(self): """Disconnect the NIC from the connected Link.""" @@ -293,12 +293,14 @@ class SwitchPort(SimComponent): if self.connected_node: if self.connected_node.operating_state == NodeOperatingState.ON: self.enabled = True - _LOGGER.info(f"SwitchPort {self} enabled") + self.connected_node.sys_log.info(f"SwitchPort {self} enabled") self.pcap = PacketCapture(hostname=self.connected_node.hostname) if self.connected_link: self.connected_link.endpoint_up() else: - _LOGGER.info(f"SwitchPort {self} cannot be enabled as the endpoint is not turned on") + self.connected_node.sys_log.info( + f"SwitchPort {self} cannot be enabled as the endpoint is not turned on" + ) else: msg = f"SwitchPort {self} cannot be enabled as it is not connected to a Node" _LOGGER.error(msg) @@ -308,7 +310,10 @@ class SwitchPort(SimComponent): """Disable the SwitchPort.""" if self.enabled: self.enabled = False - _LOGGER.info(f"SwitchPort {self} disabled") + if self.connected_node: + self.connected_node.sys_log.info(f"SwitchPort {self} disabled") + else: + _LOGGER.info(f"SwitchPort {self} disabled") if self.connected_link: self.connected_link.endpoint_down() @@ -317,7 +322,6 @@ class SwitchPort(SimComponent): Connect the SwitchPort to a link. :param link: The link to which the SwitchPort is connected. - :raise NetworkError: When an attempt to connect a Link is made while the SwitchPort has a connected Link. """ if not self.connected_link: if self.connected_link != link: @@ -326,11 +330,9 @@ class SwitchPort(SimComponent): _LOGGER.info(f"SwitchPort {self} connected to Link {link}") self.enable() else: - _LOGGER.warning(f"Cannot connect link to SwitchPort ({self.mac_address}) as it is already connected") + _LOGGER.error(f"Cannot connect Link to SwitchPort {self.mac_address} as it is already connected") else: - msg = f"Cannot connect link to SwitchPort ({self.mac_address}) as it already has a connection" - _LOGGER.error(msg) - raise NetworkError(msg) + _LOGGER.error(f"Cannot connect link to SwitchPort {self.mac_address} as it already has a connection") def disconnect_link(self): """Disconnect the SwitchPort from the connected Link.""" @@ -815,16 +817,34 @@ class Node(SimComponent): super().__init__(**kwargs) self.arp.nics = self.nics - def turn_on(self): - """Turn on the Node, enabling its NICs if it is in the OFF state.""" + def show(self): + """Prints a table of the NICs on the Node..""" + from prettytable import PrettyTable + + table = PrettyTable(["MAC Address", "Address", "Default Gateway", "Speed", "Status"]) + + for nic in self.nics.values(): + table.add_row( + [ + nic.mac_address, + f"{nic.ip_address}/{nic.ip_network.prefixlen}", + nic.gateway, + nic.speed, + "Enabled" if nic.enabled else "Disabled", + ] + ) + print(table) + + def power_on(self): + """Power on the Node, enabling its NICs if it is in the OFF state.""" if self.operating_state == NodeOperatingState.OFF: self.operating_state = NodeOperatingState.ON self.sys_log.info("Turned on") for nic in self.nics.values(): nic.enable() - def turn_off(self): - """Turn off the Node, disabling its NICs if it is in the ON state.""" + def power_off(self): + """Power off the Node, disabling its NICs if it is in the ON state.""" if self.operating_state == NodeOperatingState.ON: for nic in self.nics.values(): nic.disable() @@ -934,6 +954,14 @@ class Switch(Node): dst_mac_table: Dict[str, SwitchPort] = {} "A MAC address table mapping destination MAC addresses to corresponding SwitchPorts." + def show(self): + """Prints a table of the SwitchPorts on the Switch.""" + table = PrettyTable(["Port", "MAC Address", "Speed", "Status"]) + + for port_num, port in self.switch_ports.items(): + table.add_row([port_num, port.mac_address, port.speed, "Enabled" if port.enabled else "Disabled"]) + print(table) + def describe_state(self) -> Dict: """TODO.""" pass diff --git a/src/primaite/simulator/network/nodes/__init__.py b/src/primaite/simulator/network/hardware/nodes/__init__.py similarity index 100% rename from src/primaite/simulator/network/nodes/__init__.py rename to src/primaite/simulator/network/hardware/nodes/__init__.py diff --git a/src/primaite/simulator/network/nodes/switch.py b/src/primaite/simulator/network/nodes/switch.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/integration_tests/network/test_frame_transmission.py b/tests/integration_tests/network/test_frame_transmission.py index 27545edc..3840c302 100644 --- a/tests/integration_tests/network/test_frame_transmission.py +++ b/tests/integration_tests/network/test_frame_transmission.py @@ -2,15 +2,17 @@ from primaite.simulator.network.hardware.base import Link, NIC, Node, Switch def test_node_to_node_ping(): + """Tests two Nodes are able to ping each other.""" + # TODO Add actual checks. Manual check performed for now. node_a = Node(hostname="node_a") nic_a = NIC(ip_address="192.168.0.10", subnet_mask="255.255.255.0", gateway="192.168.0.1") node_a.connect_nic(nic_a) - node_a.turn_on() + node_a.power_on() node_b = Node(hostname="node_b") nic_b = NIC(ip_address="192.168.0.11", subnet_mask="255.255.255.0", gateway="192.168.0.1") node_b.connect_nic(nic_b) - node_b.turn_on() + node_b.power_on() Link(endpoint_a=nic_a, endpoint_b=nic_b) @@ -18,22 +20,24 @@ def test_node_to_node_ping(): def test_multi_nic(): + """Tests that Nodes with multiple NICs can ping each other and the data go across the correct links.""" + # TODO Add actual checks. Manual check performed for now. node_a = Node(hostname="node_a") nic_a = NIC(ip_address="192.168.0.10", subnet_mask="255.255.255.0", gateway="192.168.0.1") node_a.connect_nic(nic_a) - node_a.turn_on() + node_a.power_on() node_b = Node(hostname="node_b") nic_b1 = NIC(ip_address="192.168.0.11", subnet_mask="255.255.255.0", gateway="192.168.0.1") nic_b2 = NIC(ip_address="10.0.0.12", subnet_mask="255.0.0.0", gateway="10.0.0.1") node_b.connect_nic(nic_b1) node_b.connect_nic(nic_b2) - node_b.turn_on() + node_b.power_on() node_c = Node(hostname="node_c") nic_c = NIC(ip_address="10.0.0.13", subnet_mask="255.0.0.0", gateway="10.0.0.1") node_c.connect_nic(nic_c) - node_c.turn_on() + node_c.power_on() Link(endpoint_a=nic_a, endpoint_b=nic_b1) @@ -45,30 +49,38 @@ def test_multi_nic(): def test_switched_network(): - node_a = Node(hostname="node_a") + """Tests a larges network of Nodes and Switches with one node pinging another.""" + # TODO Add actual checks. Manual check performed for now. + pc_a = Node(hostname="pc_a") nic_a = NIC(ip_address="192.168.0.10", subnet_mask="255.255.255.0", gateway="192.168.0.1") - node_a.connect_nic(nic_a) - node_a.turn_on() + pc_a.connect_nic(nic_a) + pc_a.power_on() - node_b = Node(hostname="node_b") + pc_b = Node(hostname="pc_b") nic_b = NIC(ip_address="192.168.0.11", subnet_mask="255.255.255.0", gateway="192.168.0.1") - node_b.connect_nic(nic_b) - node_b.turn_on() + pc_b.connect_nic(nic_b) + pc_b.power_on() - node_c = Node(hostname="node_c") + pc_c = Node(hostname="pc_c") nic_c = NIC(ip_address="192.168.0.12", subnet_mask="255.255.255.0", gateway="192.168.0.1") - node_c.connect_nic(nic_c) - node_c.turn_on() + pc_c.connect_nic(nic_c) + pc_c.power_on() - switch_1 = Switch(hostname="switch_1") - switch_1.turn_on() + pc_d = Node(hostname="pc_d") + nic_d = NIC(ip_address="192.168.0.13", subnet_mask="255.255.255.0", gateway="192.168.0.1") + pc_d.connect_nic(nic_d) + pc_d.power_on() - switch_2 = Switch(hostname="switch_2") - switch_2.turn_on() + switch_1 = Switch(hostname="switch_1", num_ports=6) + switch_1.power_on() - Link(endpoint_a=nic_a, endpoint_b=switch_1.switch_ports[1]) - Link(endpoint_a=nic_b, endpoint_b=switch_1.switch_ports[2]) - Link(endpoint_a=switch_1.switch_ports[24], endpoint_b=switch_2.switch_ports[24]) - Link(endpoint_a=nic_c, endpoint_b=switch_2.switch_ports[1]) + switch_2 = Switch(hostname="switch_2", num_ports=6) + switch_2.power_on() - node_a.ping("192.168.0.12") + link_nic_a_switch_1 = Link(endpoint_a=nic_a, endpoint_b=switch_1.switch_ports[1]) + link_nic_b_switch_1 = Link(endpoint_a=nic_b, endpoint_b=switch_1.switch_ports[2]) + link_nic_c_switch_2 = Link(endpoint_a=nic_c, endpoint_b=switch_2.switch_ports[1]) + link_nic_d_switch_2 = Link(endpoint_a=nic_d, endpoint_b=switch_2.switch_ports[2]) + link_switch_1_switch_2 = Link(endpoint_a=switch_1.switch_ports[6], endpoint_b=switch_2.switch_ports[6]) + + pc_a.ping("192.168.0.13") diff --git a/tests/integration_tests/network/test_link_connection.py b/tests/integration_tests/network/test_link_connection.py index 50abed77..92909cf6 100644 --- a/tests/integration_tests/network/test_link_connection.py +++ b/tests/integration_tests/network/test_link_connection.py @@ -6,13 +6,13 @@ def test_link_up(): node_a = Node(hostname="node_a") nic_a = NIC(ip_address="192.168.0.10", subnet_mask="255.255.255.0", gateway="192.168.0.1") node_a.connect_nic(nic_a) - node_a.turn_on() + node_a.power_on() assert nic_a.enabled node_b = Node(hostname="node_b") nic_b = NIC(ip_address="192.168.0.11", subnet_mask="255.255.255.0", gateway="192.168.0.1") node_b.connect_nic(nic_b) - node_b.turn_on() + node_b.power_on() assert nic_b.enabled