From ea31d693d2a72be5c64e1cc7726d320f4b0dbdfe Mon Sep 17 00:00:00 2001 From: Alexey Snigir Date: Wed, 13 Nov 2024 18:58:16 +0100 Subject: [PATCH] component library tests --- .../test-component-library-vizro-core.yml | 39 ++++++++ vizro-core/hatch.toml | 9 ++ vizro-core/tests/__init__.py | 0 .../test_component_library.py | 93 ++++++++++++++++++ vizro-core/tests/helpers/__init__.py | 0 vizro-core/tests/helpers/common.py | 50 ++++++++++ .../tests/screenshots/base_kpi_comp_lib.png | Bin 0 -> 36174 bytes 7 files changed, 191 insertions(+) create mode 100644 .github/workflows/test-component-library-vizro-core.yml create mode 100644 vizro-core/tests/__init__.py create mode 100644 vizro-core/tests/component_library/test_component_library.py create mode 100644 vizro-core/tests/helpers/__init__.py create mode 100644 vizro-core/tests/helpers/common.py create mode 100644 vizro-core/tests/screenshots/base_kpi_comp_lib.png diff --git a/.github/workflows/test-component-library-vizro-core.yml b/.github/workflows/test-component-library-vizro-core.yml new file mode 100644 index 000000000..46cbe21e4 --- /dev/null +++ b/.github/workflows/test-component-library-vizro-core.yml @@ -0,0 +1,39 @@ +name: Integration tests Component Library + +defaults: + run: + working-directory: vizro-core + +on: + push: + branches: [main] + pull_request: + branches: + - main + +env: + PYTHONUNBUFFERED: 1 + FORCE_COLOR: 1 + +jobs: + test-component-library-vizro-core: + name: test-component-library-vizro-core + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: 3.12 + + - name: Install Hatch + run: pip install hatch + + - name: Show dependency tree + run: hatch run tests:pip tree + + - name: Run integration tests + run: hatch run tests:test-component-library diff --git a/vizro-core/hatch.toml b/vizro-core/hatch.toml index a8165f25e..da92b5d9c 100644 --- a/vizro-core/hatch.toml +++ b/vizro-core/hatch.toml @@ -56,6 +56,7 @@ schema-check = ["python schemas/generate.py --check"] # See comments added in https://github.com/mckinsey/vizro/pull/444. test = "pytest tests --headless {args}" test-integration = "pytest tests/integration --headless {args}" +test-component-library = "pytest tests/component_library --headless {args}" test-js = "./tools/run_jest.sh {args}" test-unit = "pytest tests/unit {args}" test-unit-coverage = [ @@ -118,6 +119,14 @@ extra-dependencies = [ features = ["kedro"] python = "3.9" +[envs.tests] +extra-dependencies = [ + "imutils", + "opencv-python", + "pyhamcrest" +] +python = "3.12" + [publish.index] disable = true diff --git a/vizro-core/tests/__init__.py b/vizro-core/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/vizro-core/tests/component_library/test_component_library.py b/vizro-core/tests/component_library/test_component_library.py new file mode 100644 index 000000000..cc8b23d98 --- /dev/null +++ b/vizro-core/tests/component_library/test_component_library.py @@ -0,0 +1,93 @@ +# ruff: noqa: F403, F405 +import dash_bootstrap_components as dbc +import pandas as pd +import pytest +from dash import Dash, html +from vizro.figures.library import kpi_card, kpi_card_reference + +from tests.helpers.common import compare_images + +df_kpi = pd.DataFrame( + { + "Actual": [100, 200, 700], + "Reference": [100, 300, 500], + "Category": ["A", "B", "C"], + } +) + +example_cards = [ + kpi_card(data_frame=df_kpi, value_column="Actual", title="KPI with value"), + kpi_card( + data_frame=df_kpi, + value_column="Actual", + title="KPI with aggregation", + agg_func="median", + ), + kpi_card( + data_frame=df_kpi, + value_column="Actual", + title="KPI formatted", + value_format="${value:.2f}", + ), + kpi_card( + data_frame=df_kpi, + value_column="Actual", + title="KPI with icon", + icon="shopping_cart", + ), +] + +example_reference_cards = [ + kpi_card_reference( + data_frame=df_kpi, + value_column="Actual", + reference_column="Reference", + title="KPI ref. (pos)", + ), + kpi_card_reference( + data_frame=df_kpi, + value_column="Actual", + reference_column="Reference", + agg_func="median", + title="KPI ref. (neg)", + ), + kpi_card_reference( + data_frame=df_kpi, + value_column="Actual", + reference_column="Reference", + title="KPI ref. formatted", + value_format="{value}€", + reference_format="{delta}€ vs. last year ({reference}€)", + ), + kpi_card_reference( + data_frame=df_kpi, + value_column="Actual", + reference_column="Reference", + title="KPI ref. with icon", + icon="shopping_cart", + ), +] + + +@pytest.mark.filterwarnings("ignore:HTTPResponse.getheader():DeprecationWarning") +@pytest.mark.filterwarnings("ignore::pytest.PytestUnhandledThreadExceptionWarning") +@pytest.mark.filterwarnings("ignore:unclosed file:ResourceWarning") +def test_kpi_card(dash_duo): + app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP]) + app.layout = dbc.Container( + [ + html.H1(children="KPI Cards"), + dbc.Stack( + children=[ + dbc.Row([dbc.Col(kpi_card) for kpi_card in example_cards]), + dbc.Row([dbc.Col(kpi_card) for kpi_card in example_reference_cards]), + ], + gap=4, + ), + ] + ) + dash_duo.start_server(app) + dash_duo.wait_for_page(timeout=20) + dash_duo.wait_for_element("div[class='card-kpi card']") + compare_images(dash_duo.driver, "base_kpi_comp_lib.png", "tests_kpi_comp_lib") + assert dash_duo.get_logs() == [], "browser console should contain no error" diff --git a/vizro-core/tests/helpers/__init__.py b/vizro-core/tests/helpers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/vizro-core/tests/helpers/common.py b/vizro-core/tests/helpers/common.py new file mode 100644 index 000000000..3ad032a57 --- /dev/null +++ b/vizro-core/tests/helpers/common.py @@ -0,0 +1,50 @@ +import subprocess + +import cv2 +import imutils +from hamcrest import assert_that, equal_to + + +def comparison_logic(original_image, new_image): + """Comparison process.""" + difference = cv2.subtract(original_image, new_image) + blue, green, red = cv2.split(difference) + assert_that(cv2.countNonZero(blue), equal_to(0), reason="Blue channel is different") + assert_that( + cv2.countNonZero(green), equal_to(0), reason="Green channel is different" + ) + assert_that(cv2.countNonZero(red), equal_to(0), reason="Red channel is different") + + +def create_image_difference(original, new): + """Creates new image with diff of images comparison.""" + diff = original.copy() + cv2.absdiff(original, new, diff) + gray = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY) + for i in range(0, 3): + dilated = cv2.dilate(gray.copy(), None, iterations=i + 1) + (t_var, thresh) = cv2.threshold(dilated, 3, 255, cv2.THRESH_BINARY) + cnts = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) + cnts = imutils.grab_contours(cnts) + for contour in cnts: + (x, y, width, height) = cv2.boundingRect(contour) + cv2.rectangle(new, (x, y), (x + width, y + height), (0, 255, 0), 2) + return new + + +def compare_images(browserdriver, base_image, test_image_name): + """Comparison logic and diff files creation.""" + browserdriver.save_screenshot(f"{test_image_name}_branch.png") + original = cv2.imread(f"screenshots/{base_image}") + new = cv2.imread(f"{test_image_name}_branch.png") + try: + comparison_logic(original, new) + subprocess.call(f"rm -rf {test_image_name}_branch.png", shell=True) + except (AssertionError, AttributeError) as exp: + subprocess.call(f"cp {test_image_name}_branch.png {base_image}", shell=True) + diff = create_image_difference(original=new, new=original) + cv2.imwrite(f"{test_image_name}_diff_main.png", diff) + raise Exception("pictures are not the same") from exp + except cv2.error as exp: + subprocess.call(f"cp {test_image_name}_branch.png {base_image}", shell=True) + raise Exception("pictures has different sizes") from exp \ No newline at end of file diff --git a/vizro-core/tests/screenshots/base_kpi_comp_lib.png b/vizro-core/tests/screenshots/base_kpi_comp_lib.png new file mode 100644 index 0000000000000000000000000000000000000000..bc95505784a983e438a1852f653e572dbdcc930d GIT binary patch literal 36174 zcmc$`1yq#pyDp3(h!PSKN;y)}NJscwaq! z9Ec*cm9(&~)>F{tbX;%PGUI}>Ih{RrymkMvYG}%rddXe>M;(`K&MvPh(ID7fh+Tg{ z&QkE`Xxa%M$3+ckqX73q+I!7y>~Qx(5{Gcu1MvFdK^W!T?~qW+d*B7llk(An^bCF$ z4x^YDw(Ex|?`xUCpXF0uY-72y*j?mxsTAM}`=LC{S8tF~EMHg%dlXKc$?qspuUWTO z+4njXMIAdSCnqOA{iET?^mMn~(w^?cg|ppenvubS2bh@|`UP4WLmgGLlk#e6&+uFh zSMtx6A|kxJe*aE+$<1ADKQRHTwYRdiU7Kuc>*!CW-9|=2dW-&v5h^dTMELYuBFlRJ zB8?9=xD>xD3IiSMVo6@syV#0r)N+c?Xyn`aU`BffM;Erbv~>54w|}MiteUDK zB@2CR4G%934P^f$%AB{xjxC$U3?T3Z8;=l&dtd|gP3u;98Vnk`idg1uDqLFeujthnz!Z+l|8)j z74!3}so}As_+0wZT;9CPX5?r%076A za$qFkgALZR(Hj+9y116;|EIe~vIy)C(hIA6erTubk>j7SqOCX*;eZT3v7s3lzl&qw zXhf>Fo1mb!2@eGFqigwnPPhw2YS<2q!r-hso8`_{^s)Bg($X5X+fnbAMsLdDJ0lFO zQg*9hwtndRmi7t40cqmY$iJi57=w9p_0<%S8EV zQ&mJz>ENgW4VM9}lG5xtQ&UqsT+7)&SqZ4L%gvRjt{l$%epy+0ZFJ1h-Vxd} zetB8h_I#(O7b^|J^b}s+_q#f~WaIeUPNvRP3bj{P35P~TW~A#n5cSMj4HF)7eZga6 zd3k1{qM}XTNiWVE{l7gs7G3R0+`0%}pIFfLMaF|JHQGIXtcQG>lq0L0L;q2(5o`a1voz>07OaH))o6J=<7YSEu3MwMv-Lzux z(TFB9O;J&?L^bdB`r;uZ?S_w*m)B^2ARr}0Z{xxW8TmRaY(PAOY!EsCPOXP_)z27Z zIl1-mroplHqO`Ol;4H4Zyeu28Z!RqnIKyY)6jyAIiAry7ZgTT-KVREqTUvTA`96`~ zBjLw+K(}GgH5M?ZGP?ZN&>HLQi$7{MYG#~}G@Df!|CEzw#Kh3F<>pC;tVhzAnC9k; zU(1{>dtfHN8yg#FL$u{TC=xw?W6->(Lr7>uB`SKO2G-H#L^NNm(z9!sNgcO;wzo3j z(1MuLJUcskeYSTOp6J2Cd9l}kkB^^gnx!C1u3EzGMR&9_Z8{wIaof%=8x9UtQwC#f zrG<92VSHbdsfh^~K2-Yl?Ai;e?bp%Ob*Pe+l~w1y*jjtK+v1J~2Cp8X45yr~b+{c` zFD1e9#~#R!UH5cz;jv{2mFPMRS6CCbc5$qd+;9rpopEb_#K8C`c0i^Li*zJUoyj&T zFtGDmVP6Bc`n4c6D~m|L!$MSaxgqOXSVN|_JufROjmb0dV7R#ke`t7k&qoRg=@Xm!^t5@Q z79xX{l94gL>ETPNFo@NgxW#@N#FYu1Tc_~=TvysM$~lh9?PEw(l!0HyyGNvKfiVHK zPPqvtfq{V@S0{ou7+;kY6{TR6GHB=LZM)}elq|9rwU{X9hF2gMk?hhY7E(51bPfx!dR9}W<@NjvE3LO65{RA+tnjsoOhxhn5K z#LZ4B3T@FN<#k^e+sE_42651VOC@opzOLSd{;R|^Fj51VT*uPG?P zu}h4*I8ji1xl0Y%Kkw@{3f)IckK}y|4$exP?sh$@qTS8|=cqGuX?YnL8TIn~m}7Tl zI?3BJu!NQWlfmwiva*&8+Wf&g0VYjRCDEb0K#yMToWKGRG&u!4r2mgGt|GfHH;#-*f z_R7Xfc9EApz+-alpRU;6x={{Xo~j)ynznm?2T8)zv?e&l&btNMrxvpll`j7NwZt5o zyZUYGM&clVwik@VQsM|7O;id#Ee~-YIVoADKNS;$)+%^iU0(J(gqzP+=O&;RYzi4M z9`l*ezTW>{<#zQe##mIEk|uLVVfzLlI5PPb9L{Kwk8Hc(}BUI$R?y{4`hP`tvpNg zgxqYyul5bE0-USfFOHpg@cOT!opgn z!oei*v&cC4**#g2GSkdyL?Ssyy}`J3GQE1eeSOyCQDBhd#%uw|olJxa-7cx^@j+PL zFn9q?v3P&CWDOkGncD8W&V1?<@fRT}DGM2xrtALRg=&?$pLO;+292_`qthcJ;o4`b z153t({=pc@OcI&Ld!JS0lVA*4Y9LiJmVi_^__ zW+VZludmH7kct2HWM`Jw(2R&fbN@QzR^h`35UJixTwnYG(HT$tHipN|aKhVvXc1|} z-=j^3<=Zz5J3HH@rKN*I^b#9u6I0W5QJz~DtRFS8mYphiToyC$(W>`)(_^_70)gH`L;7lL}?85#aeR*q8nEUgj(`|3L%NxFA)Bsv8g zM~S3i7^ScToVnr6({jr{RYi4mu4Eyn+YW+sbg#-1v=z7e8s`d|9;Td-Pc_(ZaXf8a z+@Gm;k-fdo)ZN)>J+l>kedT(NzS@%z9Up&qTD&;)bMTn>^$`P=Xv;1F{Rt_X^LgN~ z^zQ0eF@%rF!MOP`L2S2PaRKbsQWTgcjwFHI`hjmB%J7-H!}4(~SK(|OPS!6Smey}% z=abBWT=n5#kn%K=P+UlJbJ}KdmK$V|8o2q-)l*LMvmIUZ7<;=k+wN{jd3k&v57LU0 z^(~rPTc+zG|)kg89+j1D0~xE)rM<$c3W$xiMsHziadT%Ai6S}~la8j}T6 zq16j;=;8rCyq%;bCbYQA-OE@gq2L8c_yrW07!vkL_|5Vi3-^Pn%H#2JRR4 zzrr93p_wLo`jmi_+n(;Qqm(H=I8&M=H#-?Jee*UIBSk=4**tTaBgp)M(iR9AT1eGwg0waV;$^;FU~A3pO@3ENAL|m@Nii zMz@j^Th2B_{{7i+3`s8L_~%PhS{mrSLzq_G#U9g72d*C$MoF|77#QFmFjl2Yj3eb_a6B@R(8*;!ZfMeTM9lJro;Mczph*_-Glp$lkXL+E zmqG-KOaMgObx~K>Yhqk-z1qY$J(C{IdV4pv@+bGPiC+X_w4j!~W_{{1#w_i-{b?BBZf>JT|eV8NPOd*1q$MGznG<-23ev+Mo<9tQp@Ap&x8W{MU>A);3F#(Rcaa~na^H3bOk^38%_40(Bf(`Xx&({{i zLDC|t&(H8#zdLt$xH}9+nKz01VheLxPRoQOMMXso#fOD$Y%EBjL0$HWN=gQrrt^0e+}$pVRP#W{IzQRpKiw6_&&kRe zz&GqgmxW3nZ;X3)E#KJeTZ2imbFg2zUUJs?zj&;N{LXl13<(?Ml1i$)BQKxCQ1qR-dFq(~smBdr;prdAFPLEm`xw#E-FrllT19(}Ex)`aa) zP0QA~`9H4mX`b}KcHkPV=~7o$S5#85oG9rY7|?4j(($Zj;N<1v+Fhs~9OC*~4C$Z{ z^WT{$EtE#pL>Dpp@`@3U7|9OotjL6L*wNa4xOhn7m?|5vg^w&mDLJA$f{yzK_+%mQ z$Vj(CWj_C=X98xQnq+rsYHP&ANCRZ0wnnaeWz z#xs^p%)lnsc~P2^QU`l}Pi>uS^A2^uFzxJ!O%08W*GIN_!^0Io@Znk{xGN|=XS|rRVJRtj#a?n#;c~LCRtY;)q8)q`H>@%tE4d4 z&+g~MFKq6kKGCdk6_u0Q9}x98+TB%=mBxz2W3hE~cXuB<6I)e!x&>FS=Hv)__nMQF z$Mwa)So@l;cnxvw%#@?{{D`yKix^+^m3rPpF80NI;Iw`axu7 zZvuxE(Ia*4zY)yqcdIY1@OLsnq3ab>*rfdywU1OhD^yxKzs2PtV_H6}3ZSTcwp#oy zKW(=U7M63@@G?;`j|hLd2y#<#Q=v3Tn3MfQw}miJ^}l%B=1)4@9CcX=ULVBv@eF3T z`s$`tVBV!WUy^{8;nBzj#n98xaQ#BurhUazuTbb4_zQ}oO_Vaz(eqBm7~9cZyz*`g zwRd>S^RNHZG6!O_nzkOLEG%4@ZUFmH%uGs$Kwqvudv;H#DO37<;}T_lUt{Z~!-_O;gNM zOT~b2y7c+Yl`p`Fq`ISXM+@Lu2my3@il}GA&!Dx0FFq1M?|&*qeL|vRs&B4qLB0LG zjAzucxomWJy5S?mvBD~nozwAA-RG01kasZH*|jM?e&q2V{2Ws_lLy&^rNO6H)fU2s zzp?Hob#L@F{u*vklgy383YUq_L%GV#Wy|)&Po)D%3PL>5=MfPud(B8p=~G z&k!#Z084!1egBrnc}q+xFPqz@x8YTeu<(Mpg3Su>pu1D`T%2Yp@u{ZFiBAwDGodf4 z;kF)s##l1q!@*F%_yHr)NYOUNdwq&L{e&#z_M|Q42HAg%r-o%a4)q}>2!d~KHx}Y+ zPr=?WaK;9@ZtO0kcMWksLGncWy7(%$76*@*=SGrOTt$-Dzfq_OR6$GD)km7n8eo=f ze$r(X4iygeiEM&=0ALqR{1(BeS@&lsE*cKMU0PSzsE^^_Z5oD9QBg@oG44)RCW1xE z^&g$GY;fCOpt>#5Z}n4CpBx4A)vT|8hiE%U#-_HVt~+d<#Mh>Zw6Sf+O*tQFHCWpL zY!tH8oIU7$-y3X*^ZA+RwqX0Lx`o9IhyY&V-|X#ob#={mw7uFfx;gWJt7^M9Q929mz@YGCn~ zDJKeY2CHj4oMtca@yqOpIiq9Pc4;!-CGv5wyl5Yom??$#b`G1O^bB}88l2h2#^wh7 zNS+_JA#^`K7f12EL_q zImz4Fl7lP)e&_OJ|JmFuA}ed{cZ(#2NbB}g#WTW=3e=Cw%Ry0MRn;{|+l#5`+2&_M{wb3Bd)G(EI4Cni+ z+Pn@M?D`jqCOD?18GV(@eLnoxg=9#M^wx=Q~1BD^1 zN@H^m19U|c&PHP$nZEMw1?g@6Z&^7G)dB z_JTUsa;B!-ZFOg2LY;!5DKdBTcz?g5vb5fIy>mAxis-rh*6OMxqcs<;@o0iu0@)50 z*2&|81LG%TJ2S-5p^leh+Wy%{s>Nm<-(j`nf=0YaST&}@ zw)3AOHJd8Uf<(*20$2@zIi@7v^SG^O_L7k|gnN|ET@D)cMB8rTax#^rlpOpSKg6S! zR;w`RU;EK?J~>YO%nsx`ppuD?kF#iQwwP`RZ_&HHT0E6rC&u0&dLdd$a3y^Ewb-#a zP>I@5S*36~Npj4rqEsU!q5jgkS~F+KO_|{KB{=79mzaA8`{|No8JV#^)kO1DMd6@s za6a8;E-LU8@#Cp+?qjBridmRj*k9YntD)rL>TT+p>}rE+9UHyk;82p6=6KDi%Al;J zMdkqo2oix*?cT0?lBD~&dGgojVL%qGg?amj{QRjF{Zox!N;dO-PXe!{o*{?R#5b@l zIg}KKKcXY^!KFL(KNO*^!Y|8*<|#b(3yUE_1D0>UWOK7|bNf1XEL*SsdBXL&w8X|X z9p2f~lRVn9xDQ|{qwee0=0CuYMs1T(1}&P`K6rBJp3X?%vE^=Z>1DwI!bC7~B4d$Y zIa{ClJ|{QVAB+5>o*}?+@IB4BZB9t)YnTx+CVGbWiJ@afr&?7bc*J~Uy~9npx4$4) zR0w8uJw3y6+w=D|OIN-@U@bw6UJL6_UX=cmhl+x_O8B(T_lQ>v;lI3$Ka}+aILpDz z-mKq%x{Zxt6Zid=O2|M*SCEsluq!g;DFWW_LCz}!_mPe>(=%)_EIrQ!RSf6rQuT7f z_+MgftTVtvF#fbp&=NrL^;Pr@3&SPX_5gFC)$TQ0(s%{Ur{nzey4VW*A3Te#DxdSJ zJRPK=vIY?UUoC)VleDpMc717WdawxCjjQ33lG5z2=(n1zuS=ifvle7z_|Z8XUzEp( z$EF7(*L!Hxx~G1Pj;t@`B{c)qP>@j1U`|3uPv0_er2NjS7(zCFqxFXURVW!xN^|Qp z9RuBC64syYCve@i??dkZ!3+{jY0R!#hh2x=8HDTc`>o>!_d{xGPK)+AA!p}p^q{~j zK_?Wa+nZ~E!QGy93{qP@V~>aFv#^ARN!%D=9_hClnc+oHw>rJ5zOad{eSR&zor|EvD)? zXx9aTx--z1mzEs)$h;dJZv}G8%F04yEkTj!Tu@h6=UqP?%VoJ9_U(@p0QDs$(Ql&S zNl7=g^4Hhae%+Qpgu;I82VgH;?R#n4?&K8~s=$<_;ehf5AASrldXRcuao8LlJU?{D zHn9Vhr?DT{^6Khh6IrKfYQT)ib}XK6Y+Nmas~vW! zJ{lOPtN$GImHB>Rr-A*Z{Tb2PDF1qPVPRre48Rkiw-cj{P2yN&Ji@`(eZ$W$i{+o= zSjv3AD%FgOYgQ$dcxSSKOgD?G1fgJ401)6Dg^h#FWSw2@uiHFOX5LDsekaXN_B32O zUKOnf2hj9~xzJ^L73~^p4i=rypjt|&SIN&~0JOG(4LA>eA&+Sbv(QM{I$K-SH6%4; zzFX~F2?DRsqgO0ATp`iYLgf=$WR>jR&|tUvShEpH9tXe*H(iB1_G>JrE4B*Z1z;yZ zb<$mf2_n_V$Ozr5R}m_T*uu7(7?0MjZnpJq(?}hm&?6I6% z-=IquJWD$E^`^3`@UJ;fewU*@)56@Oq`j+_Ai&E!cj+*966J=xE4&tuSCLl)g)`ml z8NE+N2KB8Z3W}47RDoP>I0U#lVL{~$w&h#*bU;4m=ff&dJRPL8v=RV@3|TTyW6qog zwLSncNVm2<&d(nLS`_4X!tH>NZmtchf$RgWPMHhI2bHZ1gYN6Pj?Rl5VJD~R@87ZT zW!T-^_)Q2rl64*cIDh+goPgpGDnA$62vkG@P8+s5zjRC_jMqYuoOkxu!U2lV1!pAV zb|r#2Zi`7>o3kzv+3Dwg+choA@I!_8un@ z4=J^RyMh(5g~WC<(b8U+34~0I8Wn6(*`ZW_rmMs%L-JA8&a>z|?`DAR2D{O6Tv(EPMK07;mmjCa=SvH|* zlblAvs6JPN%NWXyFbXae_*j(&!2M}t<8@hxM9vBboo)q!u=OmB#Nm66b(0P|BeNM5 zQGY{mLWK5*JpuY4cVkNxyI=H)XVKd!HpE%t_5+Z>+lu5q%mg3`rxk0t)L zsCn61L0+)tptW&w&o zuK^fX<))ED%k6YNq0W>|SkB{lhOjv$3u)6=S97-xnqUDtZ*IQrzy54!@t*P5%3W;X zyqNiJT4rXzy_c4h%^|{H3kdD$r~6@_CHB9KXD#5B=-YFwh1(_~_AKP(!z)pn4U9}B z(QvBL6B8rTKbo88{}LDTT{gNAoYXTA04yz#8Ck!HyMFita45^`^D^28Vra!z{bSS#nN$)Y{uWM2!{81j2f^eySvmy(i_ZjH)*UpSL? zwasZlWF*xs3N$PwJv}`;I~q7JKm$Yqem76ER5>X|rq+I_{+b+r<1Z-Xab(3sIfHs} zNl#0+Gf`hzwAM8DNlk5h#GIF^!LIM^J@1Dm#>U%0JbQEV;)t85@Nh|Ku$=NwN|H}u zfYb=$`78k_+P$QE18WCH;w!S1IKrq{Yqy~E2Dz`Yh6X6Ae+kmF==8OAYY;N2+3((5 zGikf-v+(nC?nEV8&Vn;LGN?0FRV*GNdV6+Q$Ys%NQaRY*wptJt76u(ntD~<9AO+#l(_3B=u-XCpR&o|H`5W6L-#FU{&vw2ga%TQBU)-vgGAumi9PcYd4i!5kn&xPS5R2ruwDr6M9) zyS*BGg19dJ)E7GUMF=^2m9pOTm<~`s*Lw-QBV0B{ZLO{5lg0GhRKxv^H{Fw!t39`= z1qB4f@Aj<9!&u$T+JRu{zL($m`FV-jR_SW523xe68u_vaxMe?$YMDD-i!eDI6H44d(DoL459sg9-Al^j&;Q}(f7yIM|1SzdV73MQV7ELQahi5t2P$~&$ z61+aE4-7mA{;7H#vg?zR3lUn_S#f@0-J8R)`JF7}A6=Qv^@LLLV#Cnf?ysm|dgX#@ z9OBrxFmQHD9Oo@p3=Qu?2SR39cC> z*|#1*9cyFQ{n_?o^H)|=&*2Fs#XFl1{Ke8h8HOOOv{4;D;=sw@SI#H28n_zk7b%2D=7&rWw)@E+97)mfbU+UBr=^y}A3 zW?1z^Lw!MTImd0~^|7fk4>u19zdax8#+Q_^l;HAXpqpI$(ZcB0D9AE-VCZp5&tGrV zB20Vofbu0HWB162@=IwbJ*SuW_l`fVM=#C`evwjL0nn@-)Z>T{lBZQ1Dla=byW*#v z9jj`^wx=2QThp^Jn^D>E0EPRqaSU5lql)^`TcDh5xnh=0F67%3PFC_fSK58Nw z?$@qQ$at);nmbBLtidP&NoB0iura?5`GK{zxp{tm0$lpcIXWSLqX46!EgR3Lf5GlK zTZMx3CCZ=jk$@@s?^WhA$EmCW05bWZVi{*(0g33IAQeEM)t_jF{~zSo{~22LzxS=L zBx;eR<<*sMdLxOMwGnRcNSKC(Bw92pq`7Az5FsP_ak)BZRG!>0A}Z3kY~G0vTT73{k;EEfbR*+xV5UwQ9|s@~ShRN$5F> zF95BkrD`yCW=F@e^w}(#0#eh;wmgDVZgwZRNimMjx-<;iUW#S8TI2X=PS^t-<*w&@>qxErDX+XaI1xdT>}C43Y`=74W03$$9+7okf2`2s|fe#_*gi2 z#+~e4IhVZ#E&{74*tSBm_ryCgxIB^9)OU0K`M@Y=4u_>mDq zM0^@HC{<%ihQt2qvyR~54)uCSLeY3!XU>NGggT7J!6YN(MyNHFjgSdX_>WX=PaI!z zYzJZX6E+}t)~<7&to7FzoOu#G>a6FD9rpS3rV$|%hj6X0sNiI}>B(=dl})JM8W>lF zN#}pGXVCNMq#^tC?SX zk1Y#x@#c1co=_OA+J@N|h`Q76m&xctn%Y9hMyAVhqcZ%;)17w7b`+h2ba6QPNyz!j z(I5uxzuZwTw&SlR z4mqZ#%^MP+@2W3&rzf)ZeqH2;t~j#i*U>lc%b%Yrkl_l~xc58VeCzND?O1GMxIVrl zU+G;NYfOR!Nm4pCqeaRxdF`26yvcC&o3IX3RMrxb&dSN^^93lx(Nu=Yrxiw|j`p7J zpdf?@TK~Y%XRD%c2{SYp4on7i(u{CabE;0`LT^q>dLfI}(b-WQ=?KS`Z_0uA5E0uC zjSQHzFW^2Rdo8>i9`xcWhWI;eB4oVbb8WICU&H!JHd6lZX60_h&WtupAvGnB&wN4E z#Nbl!Y)w#5E5V|}c%6eIuavM5S6JC%Y(_}Ccd)x!mRXj&y4BV4-JWa3XQ^|uvgIF? z(!oz06qS`m`+Sv#hyOL`FBzSVSGLy`^~?B?pscoONH`Ov(a))i7I3bqyO>QsBj^uZ zi}F1CQycBdb!y7|MCGFAHm>dp&*+xhVc$fA04}i?fb-8Gm;yJ#C|{<8s$`Pd?ZYhG z?;SUzGr?oyW5qGWiPttc>b=Uia5ijC-8U-uAF47$X}L`AsVVRf6Ze-=9`xH(YjXNG zt$beVVyv~j3HAB7yd;meTWX#4mH8$fC0~e!qN%^h?@u)6S8P_Rtygp|4X3-^hsWU( zd#I6?yRkNJ5lAM~dyf}f<&6TaUXu8F!G`%o} zR}4IMH@L^7UBlxE&s8j)Q9i}%Sx$fFbB*X)Hp$AxBxWtDOIcXnj{Q0n z5g&2TEsKsgH=fULcDXXg6_!avM8v9mX66odR#s@kLWO;fFySa@qvCVf4?NvH$I@^5 zKD0>SH1&iESZ_9s;^QF6thGQM>u(A8sOFoxWO23D?99%7@L~dpa0}s=OBh+1y-1mw0$v{XvPi9h5Qhv4&+ISs( zNmi8)_JWS7w9+R}(jcPbgAP$_cWHn9$7rG3{!nFQ<>{*E50#oiLdhP9Gd|d&C@4na z8U#TwdG%Jk(sFhVP10=|pYI=TPoEKiFXQ_j+sBJw21F zK=oBi!k2ex>FMJW6YtY#o5hF+AM2-9r5(YkayM@5iOY(ViHQ5&OR#V@d$T1W?M3Uc z5Do0yM=CP`Qnxv_^t`m8T>sdZU#X=?NH=Trjz@~YCv`XY~G>d6?0Y%828p}Xmgil^3>{~F#`g%@| z$=zi+H8tv@qN4Ko5Y7o)km}uCN=Zz-@WVQ6X?b2)mhdUQWI1!6qLY&o&^?5iU>5gB z0nyBzu>=}rzN7vo(j1KSwvqRPqy-i>`DBT51G(An4^Y}FngOjjg!79wdJC{0)-1IUT0NaTM?T|IPi*1GkI}ojIssH}l^t&; z2b7x%3WK2d>F+nrP@DxZKgzj`V*u=pD>VG8#Uz9~JKt>84|DAHX|5te(P|tZj zu||KN+ZhBV$xZUT?DBpv@CQj585sb4NKGA-%6-*A&rrZ1V%8o<5P-SG^>@<7o@1Y$ zg5JS!iziz6dn-2`wHEZMBQDvJ=6fp!>rEjQn@5p9|0Nst6(8hXnwJbG?DmA z(sf^VV)7egy63F|5fM>)M+ZIu!TX%Xh42m~TJn>GjRF|eFhdz1GH5pNhBS`-a!>8P z6H?&{W(0_LLI?IQZ{xSvOlXD+-iEJlZo(rXBy!3JB%d9Xr8H!u#h_$|nwjY!{;}PE z=`qtAE-lqHlw$ZjGeGtHk7{!`#AeNyTN)qAfi4djzq$j zS4TnF+~GH+HnyaktV?tA6q-zMt>(7T*`YW!aC%6tztujw9b`t(zM^yGw>$9KJLeo3 z=`JRi^Z&=4Fb42P-*uHrXf+28gJRlHT}^andR$8d&UCuJV-8q=u4O}`)u}rC#JI4q zxUd>lJ!5p+bulRnh{1LaK9DqP=-B-gAc7?>dvT&(${9%s)JJ!%0(*NnMl@~wyp_f# zCI`LoEbe&@yIMoGhAXM_sUjhyg0Gzh-l5x+uI#8mUCDW?>)&G+BL2%2*m6~d2ZxWe8Fn8kiBMAxq<5?ARQW;7{ef$(^gXwjn^l`XJw+Hsk9_d zymxaQ6d1^)S;x+5Fx6uoTkmpOQejvSplZF>_akDcY}VskO&TUG3W#Cp2e1FpS4*Jl z$tsumQfAlWm>iU?TX8M=qda8Pu2I#}x(N?5Fchf4!4tnDW{ihwKo3ZTZ9lobSV)?r z`_=jA!9gG!O~~M?1>)LJYpw|A_HsMqXD<9(@Ha6G^adO}cvuVpHj9Lcc;^1(!-YGE z=HJy#z%WLK4ge_!8Na)pez)ec;)q6_fE^8kn#&5wh_ z{lA1W{S(fNg|<3ED?%yPH#RKP5!41#J^-BoVD7n%5qf@w!>Ij*%)R3E&Gmf*0}~U( z2Y{Q$hsXIE<1xm=&BH=?|E%&5P)w&$H$-!R*5txiV_^Ev1_=J@AAj8GA0dl0W>a;M zjXHl~*>a~W46lm%ft1^~~VI%)t6ePrI$Y7$NOl4^~kh4};eV+H&vb*-s z?tSVsBK5R6LFyAP6c^t{9P1tEip-y_asq9Nj74JqA6T7i7jem^#xMdpZA2N5LAxnv zRRM%t`P(O+|H=?_uEzRWnGWU}MP4iWjG10fw6AkM$F~7Gqx{LDuV8u!-1XI?RsVO8 znD4{d%l-XFUCMbeoXhk8s9o71>jPd9NR7`>Fvx&5ED$v0qzB!s&CV7k#E@{(=&bF5 z-q@o3;GW!90rkoS`BYHyR+cC4{JzB&fh|Y%?sp}emBZk*=OcBq1=^5X%{te;y+lwv z0u=S^{N&Md+`ha`l-K_xCVdf+k>bHb0N37OTkScV71SOrUZgoVJOl#IR$sq>dY)!2 z)7Cli&!$f5Hn*43iv!2z8&k8ZwEu>aeXL?g&dlGrVfeu*6i7(~f?nC{d=^eXna|c* z3X+95EH5~P7r=Z*=Jy?ezARC|<)q1BUV+Ag>ZB%#4EG!6zRoS^#Mr~pPjjt%^>11L z{nQ3GS2PHqZE=)V2bSppNp8Bpf?H<&w@>4NZ!kKQ4++0}N=Am*Ctq`3dpzL`ndl)` z*~duPrrx7}<(5BGdda9^wRMi?bQjIpLde%BD>Je(P#I_=i+n5qs6e>E_HHDaoAuZ{ zi3cLdNm;}dby-gtt+p;vwz z@qGE?M|ga z10D0wT5)*=1qFSRo;aTRYIhwa`wsecF!SD+#f6#D8^p?H6EM<$#B7{@1|}M! zpWpr|_oZA1Bm&U$toB%}dmI2s*LeoQaljv>+wZ~LH#>UDma(L)++5%9&+5(F z3Ze%QJ3}3+bZ$t=1+xLhg}JqEU1HqT6?>vMG2SY&z34`=a<}cm^nh6(VN-eezZ9G{ z!IjTg5#Ckq6erV{ry|~dVDv(sB3fWfhst$#%NTB9_QC5ua+T)J55N-XktY>Q)$ze# zFawDXAwd2|3gah%@qZUjG~3dIuK$~;Q%#H~wtSycWaSj6O*YPeg$ViLbdfntrVrGL zH^q+GF1<5RoLGQ~KNII;KRBdf6UIt_*jOmn)n~s?89h{UGRqS%Hytr7R_58?9 z(gPIa5D|Z%Ow8Mk2VHmBE?UQ01J^)_{nK6s1X>U^jaHW)gpla(F12d6<++@K7K0Gc z#7`CZKCNn1j?qAX#LRr#F_;N58Ibb;k>*gI><)I;96;sOs-4Z5{~8zI1c5-Glfwu| zu@3(`#gI9DAX!pZf6w?I3t3TSJV77zLkz64vWngD+TDZ5Tr~#e()#fdM!j=7EdQqY z&rxD|pSCoUK|do9k=m^I4^pWVz<`RDbvsVgj8p0N{AV)~O);Qc(mK%QRJ;|JMLS=z zkp9wV8U~^-tKb#y6J|@WM^Bh{X$KAWLhiHgd88Hd8+RMr*lOqjsjEVGO*KnMX!f8H z$R+@nmQ|B+@h94b&9E0%?mAvH2e^K~SAjm6-bBP_8@oqD?0d(tl}5d=DtG>sK2HhL z41A6V9zDfR7Ft?@hz8Gl-k{l7yTQ5qc5eRm`Utt6fSafH?PaJI5E1vG}C(B_Uy^TjLSXveMJi4#VK#-c7S@-vvExeh&?$MOP9DFn(&+ zp&qoJPf6c+z5%pDxWYUR8$+R__sv)0oPcN=^iONT;y8vlvVNysc5HH)ZHy1vG#AZ* z#$(VzTU${v(%$}gdBe`lwGM<`W?0Pu&`A3Ek%C4)`JgH3_Y>ttiJAVj-rn_Ib*@0c z1-@E?nsP?qWK@?fRGC4eM1OP#BrjqNuy>fw(f?kFc-usXgs-HuAvv8DY6X;UZkK1E z+fZ9k(RO|z9pnu57L!}kwS-E5bokE+6@|-9GtG^46G9#RnV?d2($iw0|8W;kz{MKv z@$Sg;g^A;z>!mB7t2F^l2fj6;14v=v5w<`G3CMQhW5I!=gtC`>*#mJ3l0cq#)}>M? z^(Sw|t@Le@tL>U25V|#bHUX(*R#@JanYhXmjfe1o64sohQ&CB9T@;fxl-cg)wRbLNNf4x^rz}AU3tSaYXz#&Yt^MCV>`nSLcja}zhD89< z-~6W&=n_HLRfIY@p&W}&4L5DB4;m@X1F3*?cXX<7Yd<;xKK>y1q8@y@|C=cAE?_ja zECNM_EfmBxe|RN4gc;toyzh9YZIt4bWljX0v^m0{SrDk&!IUBjjW`b&Bhr-diOYVD zy^&=m2bTcpNN_y;V|+tfMT}=WV6o3YNF?U6On+ZtHmM8bj}C_-vB213eyYI`I3o2= zkNdd{DwW}H7eTQP_(9;Sf!gRmrK_`NW!F{YM*R6?xbqz_Mgi)cl2RKq2$1{v_QbQ9 z)`0I(;FvBJ@!!`M>7u7&(r$R4?nfh&Ff!Ellm8GA+w}14)W*u*`Da4o?eKd!xgQ8= zb8~ag@(-rHojp-&Q9=sCMigjG4}szopO2G%l~<=C_P=2xiG<3RCGvN&FLHv zZ+v9T)YRXxf#Q1~G>y2sH=LZ<552@d^?n|e6|{joO#UhUd4tFcA6H%vEf*Nflc3J| zBnj~5iZS?00c#c}IJD8lmy1OzebE@z@rSTLZ~QgTS38lgl#D zCU_AD6ZG^J{V>SVGDj1>Yy9uRMg`&aYIH(Xvy-Jl>Ve+l{(*s*o8zail~1|XmWL0O zx5_n&N9&xO@A?^b3P9t{#*&DdlasTUAvw9F2660}&y!3FTwF`w>h3zn`sXhkOcQM{ zwyFW71X$g1RkrgkUi-DavD+WiCuvGQK>Hv-YlZO$14~QsZh<&~Mp#13(2(VkYht95 zy!^uaPDXM;k9OO1_wi1B+P7~~lIS)dksyt?myF7nkf>W6tcCm6#(ss4)|e)b$Q(aV z54q46(P7s4H1_(iU!OBJKP&5JZ0xB3{id68R9{=-(#kT9Fe@t=j&K|)>6wvRe|2d? zgO;6A`LCDTW#x|`U{Auv7(NkTjzju@(oUF6f&yC(74^agdou+6`1lwoz5$H1u6+r5 zC)*oE#IuUYYo6ol2ZOff_h{Uo*Y-R`{5N~LyShqQxGu3P$;rRK;^H<&{0#vG{~ir0 zQOCx_ekWVkWEusXhh=59ptXYFe>C@&L3IUPyWl3c6P)0|g1cLQ1Scf8LvVLEI3z%D zcZc8*Jh;2NySux??7a8;re>yUe$1_!Tes@`fPMDq-MzYd^;*w**4inIy?RSB4K^Uh z$-CJm*3H=hRb7Dd8Y=DyF>SHnzePnU_4#uWUzR2WLH3hMI~Kb{l~t; zc?t^Bwdc3&5lS1i>2s{Dw~5eK4|!_j@|Dc(V5hAKXu;ClsWA{Pag_keJ9B zZrZ)=m79!1BTMtpK_BhhAFik8<=u^O7;Wxd?vB5d-=BMc0zc1ycTG_O`a= z1g@Ii+#!^hh=6JW8*;8f5YL9()8*o6%d6XbEf@*Zxf@CO0R?4WPt-Q)kkMKdHFbYa z2UHmg@OjLY2bTqli6e2q&Loc&Rj#Ng5DEw^E!LQM?hL8heT)tf9j_2`uhw{eMi>4p z(aXcbqr^p4sG+QC!AZ^)4l)+GbW83bTPG1YB4$e>(R0#4)ndG%6(D1DqIm<%ZJB8p z>i!Hx#?PO>q!BN`6yKhhMNYoz@fo5Li6f|U>!$)Q7AHQ?%Jh`)YJOL-7 zQLaClM#QIO3WcHF5Z+~`K@oc`oNX191+bVwGAF&OxPEsPb#)P>EWLR4CEfOT0W??K zuJ)LsBpV=ybaB$%$W}8ec@6nW4R8AThW_eMFqQ1AMp5V4MM#-Xv&p`XTB>uM+$oNV z`j-1DfJXpJ)}+l3fKs)#wzf;y=+6J4o+jjg`_aXvaRQl`h=>moocP+|5F+H3!RlOa zgc|coMJ=5>5%0LE0IldWUm&XY(WkZ+(ouc5!Gf03(Yg4BhZIZE?Yp$Jxr&N&_s|Ao zW$xo|DKQ64oS)jJ9LZoZ_nEVm%<9{J+zIBD2r7P@1?7#k6R>$qovn*C3kb5aSA9w2jY&2^_N1DhSTp4iA5~>Ai@A&)dQIB|z=* zhMXLYwRmc1B)JsWtgdL*Si7I+s)C0gNU7!j?0B?VX7lxOXE=p>vBDuiGz$UYY?tSx ze?Fq$uyPcS87wR^!`xaT@A)NvcN*z>vGG)OpB8gO*m#|5I zc5rffei$6x6}s*4MVBLHwKO#~m9(}#sV}%(ZgkduYK=JD=AXdecp%$fr;SPQd|ZyV zS{&r`BRZQ5K5bE}bR3*1De_P*Rw)sB*?VTq7S~|^5I4}uWqo(kczO$#R%Bw6-ap_o zzA8x2aT#&rb#C){R8UYHO%Dle#?PNUgnoJ^O}puvbV$cI)qMNhY}Kelm-e&ClO`xi(Dlc8MlARGK`cAPIUxlOCPoesbsjV zRyOwb=2~@EjdZ>5_d_!|;Ve7FjjENu9)!91uBoc(GOxAfk4AM* z@9b_?KYGjEQ&XwnVCV<8vmZf0LEJUgkGrq&Wf(B)=j!Z_w}*pvOOREDXHeR;mdh$? zY6yA{e*pp_4WVE-PpfhFu*J5_wg-^0XquDs^OTH${t3Tfgb7B1gJt1FO>D47H zjL6+n2o-fq;T7uKx83ZB44(UiV&o5^@BJcs3TUU6!M*T!aoO#9@AS+gf#HD|}X%yLOq+RJ4j6fBy|WTb<&5!rKl z;ZX?LT;?L%t1BR)2;@$gK`>I=mTI*PE$BZu?7-cm>)BG*67ds5RFcsg$sihm+z&(a zDEucSc(We{;Rnh0;Qt5}KmK>UAfmAfZ>u^5o4ovpzA7m#B`3q3G7O}<2v zA|peNcwNYubhI<=oW6Z>H08G5`ike@5l#S(j{|BdFVg-UBr-N70kYM#PsfJ6@2Klt z%SJ)3`J|Z3#N&K%v|T)_>%LB4VnWksNalwz!SOXkbifSM^r>m70cc0JU7->WwT;e#wjL^JES$$JsB$TG%68 z?5RPaaAYz4)ZxVY+xx6uQ!F$z5`YPX0gOnw3>-KGa?sVZIjKFwl*)`Cp(L9Td$9S_ zd#JX{ri=)5AWbJDDbDkj;NTG(k=$?Ut-0cfh#apZ-I!^&UrctkQ&vpO(!;^BNx?Sz zzn*{nnqrS`J|Loi*dv{5K?6)jOS@K1l66iX zFuzCnCnfBuyd~dcd--S(h~T+nV&|9e*zhMq*iTXih_>x@NlzO`zpmy>g3B}fKaT)S zJu-xYH(?DQv$^rRslW!Ee)PB*)vDey0Vr^tGsN!p_LnusOU0A>P*+DQNxQE957B~O z3qN?6?Nc3cSwpSueR$o-h5(@?LOe__=Hef+o7drKUK4Kg9C(*NE2|P zrsJnkrH-k#e9LCNHD>(drPlpPE&|jZ+B1ZxCSFY0<1S}={RN33N$T?og;TwY#U`~Q z&+ZV2t!>>iB=@ZakC872hcg2qBroWeGRl1>%YbIHOsDE4DLpAJJqd@H@U}Vb2elB3c@ox6HzPfZIMXBt^(CGDJhGsVcEdz&3dlT=!2A&inz=12!oI;W3 zh=ZC!Zt{;=jET^NUUB)?++vvkXDhgyLq?|2{$VVve=_5Ilfq)`R~DgKJM#8;u?8+O zJ<64xw;HTQrj$)SG#VEbM@&oz-v-tiU5Tmc5cQ2KWwO$&^*B$OfAMN@dWpvnwagy0 z$}zKdrgQi_WXKkTXu~{8x1-k(70WP^Ng2~$G@@5URV6M8_0lra)qO_~L`N%%{Y;*I zagAwox;4sPKmjL>&+Z|$uJPxa;bLUrx08a6&gq<1l`g!A7!w#@BR?g#Tn+s(pc#Gh zwV~l^Z=#Uefu-NDr-=RTaegk!scOb~q`~cnxWOwHMfKXrpm;Tbz*6jw7q8k0cEAzA z2J0)4?d{2UHP>h;hW+B6J)zk~w1Pr{=WErMGDz%TMv|a*)4{i&d1~+D3ajty!7%G()Ss5u5pV+}2qV=QG5dW6A_BU&<5L<&?VCSRi^DYuQkZn(A_plnb?4gCZ@c3f_Fydf7E?joaQ zq^SBViQ%JZMzBHRYxYrr!tOs(v}wKY0QVt{t_MYr>}m6P^z{RS!H^!yz;@+>7a;<9 zCOAv|d~O>|nU+XYSp>&~o5N;7>J(`rt?JC110 zH8Pw!Togl~6%mxmF$-?6{U&Sr*I)RhW~8A>b2q#yUsy{&cKWT;A-#^vMlL)x6(eZJC>X9z)&dvSy zXr@pdh{F|qhm_nHHmBH`)EEZlstDs1qQs#pB)MfYh7Dl+J8c6|vB6S{prW1oD1!OF z^1{S}kdL_WrEAT-06zq}Rtdn^`ca2|w>a?in-Zo9iGhhtIXyu!z2h^qe=RVGbt`Nl; ziz1jM7RZ@R?b&O(MoA%fDZ1GCg30gYoVVwntY<@-e7kNAri}{6;xovQ>g)WVmR85-ya@(L)|;?v-7&#QdIw{!_>Sj5%6#v^&km^3 z)#J7=e__B%FhzB7s2UT*kGRf_%m5`I_btUu`yP^vjyiOM=sbB*4P-#sS`r z^tp3lYVHhs)B(_A5mVVrR|9ALmcd-n8W~m!C}&5Dc=`~lQ^^`_75Y~zk9Y=6b7#gR zVe#ks($UM(HNzeK_z>~AnVMIipcCmV6@u>3_d)auB1Tbx^Y!N?U+sMf0JH+w%@vbT zkfMRM&%b6`;{T2WZVA*4te+aIPYh+h$OJWFHNb05Fu&5ed?>dis(oR2ywl~%Ntb8Afc}1lGECZKy@j~;$(9-`oY^B@QI-u{GUPN2;qeI2~`d-w2tumdrzPMBieq|x7zv-W^8 zMv9@<6HM4N!v>ej+R6Qgit|9J_5p*VdvYy)@4;yJhP6T3)FK2MtM4tPcb;fA%}8Is zZYcd&h)rI@FTPsecKXQMYWURZM)27(OvlpF^3x|jZ7!TugD6g7uM^@%%a|z&{&>_P z?qqyA!W_1LhySntUo`=^&jHbQ86EUFS^v!icq(BnBChzA!khOb8oo>P6dT;a*qpJ) zeC=HL{a0|XzCxvH+L-Pz$F@b1RHWO7R6fp2ucizG7oWr|7BAnCU86%!Z>P;Wz*m_LX) za>LzHxHA7SD{=0ru_O*RK7;>9fcg`Sn8~MdnqPmncTjganhv)F5bFcJMSkkDnj~sf z!FN@ZWp9g2HIWp`=Zzn;-(*kC7L#LB%4tk3<&D;Ro{UxgTK-zOBPkl9T(xcWBuj9R zlfZRmU-&vmaZ&p@-@8KdAlH_d1PyE>q3R%K(eDd3`E-RQ-5(x>VVHFXZxg~1jf*|G zIlKs8RX5bpLGehml)L!3Rx(3S03(2JBPK5If|Wo z?`zH{Q=cMm2$rcK4&LR(Q}Rgy-o6iOIINP=uap5flyx~F#aTt=#KXM(S>P794*7!5Lz>eK41sb90oASU1`s@u}gJjm{dcm z^9kgkFv+VUeo{77Bn9CtwZM9meI4u|*F}tyvW+KS$68SiKSi&a&Y!zxNQ8Bv9a}(G zY_O|({Kn`Map6+X$XP-}lHR)AbmRCQkfh=q`Wp4yCK3E3I$tQ%g&oFLKVbl0=%CuJ zsDY+z6yS6V@YxRokZ!syXk9bEc5ay#wAXo$XUHwf6Op6mnT0 z>?xW`o33#gUsuEq%0D5o)LLyMdr4T!yL6J$#L`7 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 literal 0 HcmV?d00001