From 8ae055639457c0d76be99cf7bbd474eef5b25add Mon Sep 17 00:00:00 2001 From: Grant Sanderson Date: Thu, 24 Jan 2019 21:47:40 -0800 Subject: [PATCH] First pass at SceneFileWriter refactor --- .../clacks/all_questions_scenes.py | 8 + .../{clacks_names.py => clacks/name_bump.py} | 3 +- .../{clacks.py => clacks/question.py} | 5 +- .../clacks/question/480p15/NameIntro.mp4 | Bin 0 -> 38500 bytes .../NameIntro/00000.mp4 | Bin 0 -> 11456 bytes .../NameIntro/00001.mp4 | Bin 0 -> 11285 bytes .../NameIntro/00002.mp4 | Bin 0 -> 8645 bytes .../NameIntro/00003.mp4 | Bin 0 -> 9475 bytes .../NameIntro/partial_movie_file_list.txt | 4 + .../solution1.py} | 7 +- big_ol_pile_of_manim_imports.py | 2 +- manimlib/config.py | 39 +-- manimlib/constants.py | 1 + manimlib/extract_scene.py | 60 ++-- manimlib/scene/scene.py | 305 ++-------------- manimlib/scene/scene_file_writer.py | 325 ++++++++++++++++++ ...utput_directory_getters.py => file_ops.py} | 54 ++- stage_scenes.py | 3 +- 18 files changed, 432 insertions(+), 384 deletions(-) create mode 100644 active_projects/clacks/all_questions_scenes.py rename active_projects/{clacks_names.py => clacks/name_bump.py} (97%) rename active_projects/{clacks.py => clacks/question.py} (99%) create mode 100644 active_projects/clacks/question/480p15/NameIntro.mp4 create mode 100644 active_projects/clacks/question/480p15/partial_movie_directory/NameIntro/00000.mp4 create mode 100644 active_projects/clacks/question/480p15/partial_movie_directory/NameIntro/00001.mp4 create mode 100644 active_projects/clacks/question/480p15/partial_movie_directory/NameIntro/00002.mp4 create mode 100644 active_projects/clacks/question/480p15/partial_movie_directory/NameIntro/00003.mp4 create mode 100644 active_projects/clacks/question/480p15/partial_movie_directory/NameIntro/partial_movie_file_list.txt rename active_projects/{clacks_solution1.py => clacks/solution1.py} (99%) create mode 100644 manimlib/scene/scene_file_writer.py rename manimlib/utils/{output_directory_getters.py => file_ops.py} (61%) diff --git a/active_projects/clacks/all_questions_scenes.py b/active_projects/clacks/all_questions_scenes.py new file mode 100644 index 00000000..c29d03ce --- /dev/null +++ b/active_projects/clacks/all_questions_scenes.py @@ -0,0 +1,8 @@ +from active_projects import clacks + +output_directory = "clacks_question" +all_scenes = [ + clacks.NameIntro, + clacks.MathAndPhysicsConspiring, + clacks.LightBouncing, +] diff --git a/active_projects/clacks_names.py b/active_projects/clacks/name_bump.py similarity index 97% rename from active_projects/clacks_names.py rename to active_projects/clacks/name_bump.py index ef0abbff..7ef69b59 100644 --- a/active_projects/clacks_names.py +++ b/active_projects/clacks/name_bump.py @@ -1,7 +1,7 @@ #!/usr/bin/env python from big_ol_pile_of_manim_imports import * -from active_projects.clacks import BlocksAndWallExample +from active_projects.clacks.question import BlocksAndWallExample class NameBump(BlocksAndWallExample): @@ -72,7 +72,6 @@ class NameBump(BlocksAndWallExample): block.label.set_fill(YELLOW, opacity=1) - # for name in names: # file_name = name.replace(".", "") # file_name += " Name Bump" diff --git a/active_projects/clacks.py b/active_projects/clacks/question.py similarity index 99% rename from active_projects/clacks.py rename to active_projects/clacks/question.py index 327abed7..71701e4b 100644 --- a/active_projects/clacks.py +++ b/active_projects/clacks/question.py @@ -346,10 +346,7 @@ class BlocksAndWallScene(Scene): self.counter_mob.set_value(n_clacks) def create_sound_file(self, clack_data): - directory = get_scene_output_directory(BlocksAndWallScene) - clack_file = os.path.join( - directory, 'sounds', self.collision_sound, - ) + clack_file = os.path.join(SOUND_DIR, self.collision_sound) output_file = self.get_movie_file_path(extension='.wav') times = [ time diff --git a/active_projects/clacks/question/480p15/NameIntro.mp4 b/active_projects/clacks/question/480p15/NameIntro.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..ac3fb91eb490cd31a79f57e34c4578e634681927 GIT binary patch literal 38500 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

J6j=RLh-9?TbnHfB(SwUM`_oh!?1dHG zYm63v6-?w(ho2|`-rW^CPxD)6GB~N^ikeAzr#bZ%ds85CYm7`@-S5RW5u2!pUUUWF zztQB+>NPTK?Rt+}+|}#+bN2k^w5_BUuNn8TBMGg`mu-+|)*-$3GE&@TJ1P14PC2a% zvsx#Hx6$3azFG_o?lEkqc1rdMT3!1jmuag0CL=XXvcggGm2Vc2Z|QG8Ymim(d1kBs zqGZikGyr~fjZTN-arFKJ32bU%=7zkUOQLASW29dDp2`D_ZFx|=qyliEx}Pl*@@{GD zpYR&qdH*?tQ)^dV{%{x503U3FT>kDOU#MCy)rx&?$|8{fxJlkU)spw_)qA9^df>}| z^7JufdAl{Gkp+zskeAL-c*g6RmYXDX(0{Rmdq>>g4CNwbNcp`J=uA=EI(?Fo>-Jz9 zpZE+g3I(w$O#mH|m|6vH!J83nFS`6FAP>?oAu8J4GMNA2f)ZY!`#`lM5=X7GT z@*2=s7^gZ|fruoh!!Ktq#%XBXPr&rep1^!tUMG zbP{VX2{pQ2gj13v_HY@UF)ks)`kT%V5O{--J!Z%k;trqpoJ09e$EW9K`hcpOu?s5B zdojc+$zatBOg@TDq|~-M?4GW_8h*M$3wItX`wUVKzxA{_@t^a}N`&EVwk>@`ZZqwu za?6PG68TRMGHNH4{!WHHjog+#PCe_bvSO6}tZG4!5*7hO0zi*?zr*`65MthKr_-v2|~shq79C7>o#T)b~$9e@`t-$8Y=i= z<-#Wn-`**LG^u*O)}+1K_Ff_)o3in{M%~ zgh{0a+S`-x@x_k9vqL;ki6riGUlXEAhRGW-cIiRQ8b@(uT-9z=EuQ96UOUA@M;JYI zy=g;X>FX_7n#mF<2g7@V=)?6dgl@4h9Ju^GQD*R>PFh(g4@x{op|wHDrBHHrxX?97 z`%=;(i^wESi{$9>4O5w$*Plg9dI#sg;LByl^bbK%544H3u=9+6O>;oz!-Q=U`GOX8 z#4UeGA?WeKp=2MWaT&VZoK=W&bdsB0;llT;V$_C;2{}D9tF~MJY$_GFTnD1jO+lI2 z^!@koiBDa?^sJsB4}y(bSL{Vu)_{Gnh0^H&Xr}%bbw>`inCRFeG_Eo`jz*)GMh+08 z;KS5#&xS*x8q6rJwd(mCa(={Dp@-e2NJChjWBY*J0KEP_$Ok2HMGeSZY`qd;M5hJq-Z^G9jE zeB`yyWyK)R(OG9Cm=DUqUm%8t1y*9H@mX3C3kl8RRiBq4?#mZ(gmo4_Lq{BpyMBq$ z;clh|9=COG&8`H;R43JR=}wx3=??=NsdD$j7bpygexo%?1T(DulRA_IL{WipzWxK8 z02~KZEx`wEtnZTrtQZ4U9wItf4>Z_ovzE0#es}oVFr*Tg#VIhQ{E+`jTTjG$_8~1+ zI)AXs{sw@J+ z|7TS%EP#Ti0JiSnj@+HujuJ$}^O(z8Y9yi|OODm9sEkSjH{!$D*KbQcIh+TS6sWgK zb$0JGTs4044Wmo!{1HF%fZ!5^wa5;a-QNgk-S1_Sp1PlJqf-jRO{@F2ErqMz_em^yQzni7l{MpKgTY=0z)#WGF0VuyE zYv`WzTYmm~b9?1?8p$Uc&GFq`o`M-w|2_Cy$S4xBx<;&#D;BcBB1GGTm2jMtPnXC(mhR)M3T`*s z=jLsr;$lsKM4vYMVQNLam##*76dg)4NVF7tMOYs;M#QWY<9k}gk+P{ZU0XSjT8lvv z!@w`)SVE4brJMV;z|5vr+IF8*QTjV%nbw}e!Yc|k#bm>hLKfpp*K$kpw&5+oz=PC4ZkJ9r#PQ&WtpsB5SmVcOncBfuhuYl zpe?pEs*`bGmgy06Rt66>?yFOq@01v94p6Rc=Jpglx>{{ff4Jyose zpkv<^%_nE~0D|+hP+~4b0irqFymJtn$hbn#)`(_4>JLbbNB3iyY~3-(AHK*80TP(Z zQK8^yNKA=^@T;ws+(m-uBeG|}d$mMPB|Ic8H!ro5Z!b=3Ko7q7@)h-zB5TK`osn-& zjWXt5n#}lr;BAo&(8$Lwy%1kHMbq<0O>+L^$xSA`-6RGn>IB;B;EC>fFQx;NKjYL{ z{gS}iJOVzcQE+H-f!AYS|1DYHwRVhB0&HNISt*sayq1YfxPt)F9ZZWfbh*AyBmi>t z_TU*LpY8X1e&9KA+cG0VxU>VBRFxh{hfA;`je#g>l(JB>UFQ4)+RBNpTo#k`=93R! zX5E)dYc4yt@@ctcsd5H#Jv=ykP&dj5xc*&KNG>DJ5ce?94r(MIe;>{Vs|*-7@Kj%k z`0_+#J`BZg_xXI8I`IwVZ{d3=|J+B)N)-nN4-vB#$i#^ODZQ{&$@h}z|HoJU|M<%P zPvR?KGymZ$S-$a==UrfIs3YI_O4`9SDSJNZLVtfZ)^BJV2!LTk;olAH3GZ%Q5R6K@|| z^wfH}XBTY43CiwqWf#4HVzjN0Jhhc1cXT)S%EZ##Iip@*`aAk~Jz&fpT?H$HRF{Ak ztH#cAmd|qFK5Cg`Nha}GdqX1Vs?jJ0wRHfh%#yb_4udt}bJXvZg7{Tu&*sRb#+j*9 znqK}0qLQRKkwNX3o5;uu$(!5oJ|Bn)CcqqS-Gb0Km?V>g>6vzDZ{4ogK z6u0M~%d{lN%CY|}RNWKt&|Zn_Ss&rBFcdo^$&h?c?41(Ir=#r?`xsXB_hw~yMew%j zZ5Gza%YdR0IE-3Blxg3EsYGikqdao@{TF~h;T)$8zU|h`BR8*9m#6CEHDdH_vIeE7 z=6)cAe*h~|Dn|~_cOo}u9WP#pDD73Ve}It{SAkKheXMPAj1@2A3uO(Cz_03=`A} zXY96lp7P3KH`tluy1Q}@8l1V#f%&7#Zzj(GGz1$tWbXWOIgs#(Ky41%f!fWIeO)B# z#i%wK0t^4vW7Ji{aX;|mmh#@v-;iA~J{aTF4c; zk6w;o-n&Wj!|}^194s+t0h+#seEF;J?&tWyk@>|Qi$0olF>b?HtY-Z^EY^ot=uD2P zr#`gb*OxY(sm0u2*|kxqV(%V(_Is|A5|ByZ1oilqa-=<8 zl@6cRpg%@Ya861vGObEO{Zj`}0FN}z*ybFiV ziQ41IYkysQj{fJ8f~)h5Mb$nKYojWK{5G8J_H8Sw0Q9xU?mvN-o)bqP2M$wkf1r|S z`LL}&7m#{jC&Mx8L+0<2$LL&L7&a5z#WXnkr+seJaIZ!|8GFh*JN`V0vXAuPc@Gdu z2&lE^$gTg{fuC}u)Tks<=Eou25EfY{HS#D8Arnwfr9okRqbZX&_QGrZVNC6;3VDz7 zbqOVt5~e#0dX3?e1cpms&v%jfq_Hail$Ujy5pnB}Pq!rcCyn<7$ap-EN3m}NY2eR5 zUu_XpnA9tP1le~CcSLJm+&tQYfYl9ZhxgNPKo=%+sC&wMw32%R*4b|L5TC&q?72WW z7!04;d3HJ7GBW;n`O~W3@^;Re1?hW5r5bai6~^ z+PISOSvPQj1E+Omvh4&aIhF|I=C7{yMm9>6jbAw+Bh+HSC7H6Xo|^oXHHYt>U6Ah3 zG{gjzyVEq?GCsxacq;ZH#Ie}bgB+M+@J;nZxWhfZPpX3eh-a$W==?0CgalJMK86B~ zN-Nbzt12pPap_Om(OEFZLsnteI(;3UH=O15!E2lk)8ay|-NTp=3=~ifBNr|0=tv5) z?ZcWk{OZ0xb#qZ6Uc)h9TxfzF>?GV*1~W3y5j96EhP^z;!_tMrA@?r9PPEhlu$4`= zhgdS4r4Pi>Y_=Z5W;+u2U%i#jg4=k53 z?VN%-kyv@69!Ind5ZxKr=UL5beyx^Q2)eJiL}FSMtr=E1eTL^GT46}@%k;hfNP54; zWkC%nl4rk(PQFmPaD+;Wcyw23hCZk3ou@!yo3%Kxr!coYk$2=DmLxYpMwVOoI4E+H zpuw?7Pe<+tF`j%=AXE6X77}TEV@T%=9=k7oU>RBn&z6*({!EyioGw3BY^DR|B!A~) z-oA7G0R-5Rl{{&#FArp`VN%`h0iGk=B=P8`lCUKf!UGW=Kw*X>wz;}U1lIFy z>+4<}{l0edFfI4j0E*w-bfenU4;MN}i$$4H!qv!1DlEojPVzZEI1)GNqvH$N*FjTd zdO0aqztoLmxad#kZ)s1O`{Lc;7p3}#cf*Wh2q;X2st+-fJpX4x2gBFbT~E&if(L~JK{kWe4QV3=kU43b0~@CCK-Ns)zh2HbvgaavsBC^ z;3#pq#1hd>mI~2&(-smi?<2jV~Z-v{3a|_bivtNSEq^dU9~oL9oF2>FtX6xg!WTi5^tw zhWsMLsI98CFf5P)N^MovTY%sKuN48A+^FZEKt8R;1qaga#4G&t2J~y(H>7eZ275K- zMn%=!lyUvxnbdC%4Dyt9UriAzo)i1!xqYB-0 zBlQnIj8{>O=tZ*2)NAn`8zaQ3uSY{`DGOF_@4d8)$cwTyyF5%`jHVnVVVa1+)5QuT`WH=J; zM%elY^=lC>V8h=vm5F_V{S+yXK1t`Age{cyYHD)^ge}e6i21Yx#TYw_g57>XQRCga z_N%b^=bI*CAmzTwJ|GmOccF1Uv=V$~v8?P6G{kCy6DK|Lnh<~%-sbZ5Tyq351cpeR zS>|_E?`_A>#fG=fM!E(a*OVf+@Ohu%4j>Mi%8q;tb|mg!Vn<)~<3AP-GKabF_XNV1 zi8&UMT4PnOxi2GS73u{2jts)q#`Py>xgr~6Z^-Sxbgmd`(jhDD`22I46vHj74fIw>yqH7wcQZDGr~#C?++jc6K51}M3YgLa2Fl9Uc`bQf{JiRvHw2cIlu-jISA z$B$VG(c&RlybB7jg9^*5Yl@)WP%H<4gu2mPsVgNWi6C$y_`W7&2e~{))i~sD$#0II zBU9=oHUn>=AuyA~4E5I9sX?p!g%^+aS(#L1?qpW>4MFB_e{T_w8{zZ$upW<|NR#{1 zd<>XlOS96CTun<7;!#g#+f_T)onc6XmZny0M&W&bmw*1e7ZAl~@dHOR;&PJ%E!<<& zhCdI!58O^dfnUxUmGNS1_xI4jS2XUw3V0*2iO3OvDaRXQ{`d_wq1itbQX#XIB5_~H zR=ooLjAs^LvP|#QYq$7-9a25H0d`ud_No8Jft~@5DzaV` zKpafhrtiFd4(<5H(1XnZFx>gKf%tzvv<1H*bg_czq}IaxpV^52wFg=Dc}KUa1jzC1 zSjoHRV;u+>d3zO6QHe(^5Ko)|%acxzPFq9}`8etkwWp{A3WzdVaS2+uA3^!yKc(){ z2)(Y9L4l?ely#pxvW&1z`lvao(Dh7|c^iE!YewE0IobRwb%%lnrj_>$NKvU9*puHL zb0l9NAF}SXb{>ucP{``G4l@@72LB+*;i}0yweWC!u3D(7jEFtG1mWMC=K{x=FG4J6 zEc%^WK8OgJbf2u^+_4fII@FLr6WDA@$;bKwOS0RVhFnHwO0MXrx39CtsRuu8-{VQs zR)s`}r^x|fO~svF8*6fH2U{X>5LVz=uq25PmdmwfwHsbW{JrP8PWq^b@$4J z4E4K+9S{50#Y;NoR9jb>Eq>r~@~mi4d(jxNq$DZM;If9QX*1fTqr zt5bEYsG|#wKJbUOYB6a+#O3xv|fwso1mLs9Tok{dy~Hom-%2R^rC zJ@vtjU0NQ>wo1Gs>e>zun>bV?QI}c$<*2!wO#P1Gx81SLQ%|r&50#*(;Ng4kTffUbjv>P?rm3{=mJp>t{}_K6e2Pl}Q8ZcTfW(VtC z5M#%DsZPNQYVbeNtLu|~JaKSnMz=}RALGe0$4enB(S!de>ek7~FBXs}GlPNh24Wnh zNjfP2U`LqVKUg+Lst&>;EsB4l=ke?rdj%oC)%)!p<}vYL{&{ZBT^l{oC4bI~=-$nu z8H|rdCZR<9sU`aY!fdKkwMxiLvgMhybM*MO^}(Ua4~}i_e_5hQkoiY~CdYqK1i#)M z3a%yuiALOY2x6|W>G~CbSf(rb6ZAYROMpsiR6IDn{U~q_tpEv+cF^n~IDZV)=d>p# z+}TGE0$K88Yr-MRqwRict__&!HP636C+re)dYR|<+Q9qdiAm=b z$vO`~CHG@0se0nwZR_M~1XzwfofczJ*7+1N86UnTxX;t^WjNG;e&QQ}+N18SaqN6) z=VAE{jHMD&ze!KJVe<1k7VxpXv)Y7lAm}5IX`0>baQ2rM&_0ba*mf1tgQHg*x#DX) zLTNV5kpw6pX#O`B9G6uGXfLsm&@MqSw3mwf)?4XZL?S^@j|+z~|MFRPB?;&pPn^^s zI^PGWf)QyVY5JNip}tmzO|OT4>7zNE0HUUN;wP21L)bTr)6KMJoHt$)izV;o?h zoOW7w5FPhQRS0Q{5RBd}LU2JH3O8E0ao<|#ee6dxy|UbWVQ5~`HJ`c%N-1unGu_mF z|C}o!m@VWU>i1tMyj0St+OCgxF&7Ze9V}&}NNC2RhKa7f{S;+UyXJfdWL7y$`Za{vm4nU|D@etqqi2D&@psnM<^0UpW*ISkO)+kj-4(3F}{N`z&Vb1qZdq^`okve)y z2}tRPGpbGFVBTEmv>vDm5KKB9k93WG43`>&aPLM5I?#oxcd0AGq?leF`WCwyDQLiQ zZ)z%<_skOb=FaBB7_uR`OSQWVNbvl~%m~|<%SOvyahQ#k=}&DGw-_L)`^|Le@w?>+ zt6Avw^iC>=3qvh!WRhTnGTC+UwYR=c7O%*aX|wu527XyJ`VJaQBc{;T2T*5ce$F;qx|2{o>F) zl9$hoJX6ItZ^LDN*RP_d3}ZN_jGbze&9@6@1~qPqD!71hM1uXn8XLYK1~x*5nH$8G zmD4fTXixn&U?F1%vQcMVq!y0^jAskX?98I`KD;Z1v#pRRB3gmPsjbmb-&{J+h5M1I z4>WD6KCC}U)-tIxSWi=T6k~MIw`|LDSmVbyQ$li@xDV}G83t0Gz9Xdi#KN)N!+7^0 z*BTB247;6>EDtN(1GTDriW%vt+z193O|Ki7~B37Pw2vTOQ1 z;&EneylcDduF2*U!BKuM`Z@h#E@Gu6VNL~cP+R)QiC2fMz@r=mB}a+gkTg{qEUdrl z>5Cca9Jjz=^(>hhFg67xde#nQL=rNMCnc^)nwgiEVUZd8_{>;{3yO!9>-u*tPPfJ3b)!7{?B1@xdY1 zyD-_LDX6P3hW8~L6cjh;N55qI0=U}a#tAyP_EZdIqVU0$qy*SVX%P#VyFKh5q5bDC zPY=T39AbE~2ds{*M0~|V|LE*jWs#TkO(lvJszKuX4=L6eIXsp+f#uP*^0?w*pUA*c z2isz*K^Tv&dm0XK-a(O}w|bbrZa|q)uy&dOL>pJ0i+mD;RY?%GMaz!Iz> z#&LW>q72#jEgcXb?={BA1 zUf{!EzI>~sEmrQW_@(~HcSttaJZ9Q-_CmXhT2f32tn4b6lH5`5K;(Xf8gaq?a=Dx3 zN@fAwe8g!W;XmH7$nS5is-D@lpO*|Jp7wh2R3`kRUw8mj6H#(0uF02(wWL!!$t*zCNQ@&7- z!002XbUrWFxuo^HrL)<}I(?`uJllV2o#2^QREv zF-Q8I*B`?;KU$1@Mm0A3(IEIk=B6!;LmM!AMu*psRPOi%2gSJWrPI&IQpwreA))ZA z%Omv``Ent7(Uj%;{ES0INy_^BEE|`ApH1Z@5+e~%X8Hy;Y5x~{?-*oR&~1zEvTdWw zwr$(CZCl-C+qUhhF59-dY;@t)r}J){7w^9RH%{z-D`Mx&T$zz`t}(`(b2Oa#_KUvb zSv2|Ed4Ny@0Dv)|977kOuYdZIRo10wJHy1-#~s(>l0hsv_0jWCC|*p2mk1!eW?6dw zg&q z71-imy9SzjDVvKy=#NVmORVcfs#{Zu4K@K>W9UGk$`Po)X^$rnk(E1(`c?*UQqXd5 z6z6*6UZZ}a_51fF+htYg!&ekl=EdOr}nS7 zHaO%Pqs5obnuO&*mZ+j?Kc)hvr?f+$iE#m#j&)BqOOYgiC5eeFF;;p zXZ2Hz!w_u4==EV-fWUUecrSfwS@ff-SAF(r9C^UpnKkUiAg_OSbX(7wW8o;ir~q`C zW3f|IPi3_z3UyZ1qLj57r_i&q!FPyXbX)glg(f(L>|_TJEDLypRcSzI^r2~9+A`i& z6A_%`8rTwIqRNK@9OBI4Rpb871cf}zvs+$I6)-Wgl+yH*4F{tAux0$x{)gTqf&yPy zA&rB#z6t%#V+N^!ZlchV#%hiS!D`#$Cj*&2!~^yc^mGNuJQLJ)yeX>MZkfVzDIrjz zv|}Ic0Rr(X0D!6Zt$%eC%rKKI6SPQ|LA~kyH#-gvqvbrMjP8X95Wat_t7^wY{VLsr z0?uX3YrnYiA+8&ZRs}gL3}c2_l!8Nu&S@1hWFm z(aE11yrJP-&B5b+Ub=_UwFj>i-e;3FtbX+As_AC)?f3{HJgy)fvPPDmq|9uVIG3Os z#X=3ZKAy`78=x6b+xoQaNxa<@WG~+DpX_VMbQr-YeNr*dTsMfn9K_Q%r*ev$yn+E{ zX#kIkHPn_S>S@KKYW?hgoq?-Pm|Ji99wNR&lx;ErJa?)ODVZp*2yI-t_j&>oZJPSJZYbJUe0<*06uX6m>MEf0WOawQW7nmU z>z_1xOF^`@(DmoT6__ld?p;v`Bq|{>p`4!_J$1jH$hgh&eWrJhd6#7hHTvdSjF8=C zP0!4J{caV>)+XC`!zb>n{5No>4gzp=ZNtfHV}xOLc5|a_Xj+t$V~~oRc_h&SCKYw8 zH~1S)=Qh2t1_jw@ytR#)9;q)B9Sh!JtOe4Dnja_HE2RPn@YC-H@E_$7hKBHQr4zv= zSzkL2z68#Jb8^Q8m+hBa5#>vfAPqHE-k>-pkvP8f2n3p~`aUXtQ0&>VrTm(2SK{E= zayli^6DIF@0}-}9L$k)fuJ2PXw<>8~7q@T0DbQ~j%J-(yGHy&sGuLVm=hwBj#;tq@B?EHo1R>r6e zr*I7@{$h`Vp9bcy-wjx&KY94hSsM)@l*nHS4dtR^bH;LjC5fYo_3YB+CbEynfP^*^ z&%z5P4unj$RtMb1srVw#yLA!N3?pSr;*NPca-(p_xICQeWI9K~UWjSo(-*^wmC#r7 zv34~(a}IZEoDrDRar?TM3{kQrJ6`Td{^~15Wg$JF4uvxI)Y62H$z&e}M3@6nGm64} znMQMLgDPH!#LM{NWth5+34HR&weFq`>43zrRCk>H>lOb5OV)hDH;o_+%~?BoKiKW+@g*C`T)a3nYQXM+L`|} zihJ=#QDmm4B|LQTfm*G&(2Ybk10_6xAh5Zfk}ZEu*m77|rs+>2_4y;$V!1=KRKPPp zKU^;>M?{eEy?{0_gJQ-P5xp42y7b6xG788q;l7Ho|8wmLu5N7Du>^W5GJS;@0D|3eC>9qxVcnqOF=s;o zlMn!aPycpa=|DN5|GDo^uDaZCb}sE?e+mC$(u>C7qSgk$)hxBu7MB`jw544s_OhST9xwK1#m;eY0l|;PnUi(*WX}7v!bR&*eY3;8(`9)%fPsTE1Gz~@> ztN=}lEOU=d4SKvfLh-`Y(3~gl9~R;0&+H8Ahq*4{Q;2&~f%N&OyCGpBFq&ci@T8L8 zm5YEH1T(@V_n?(b8U9Ny1kSAG9KWK7jR;Qg|M{$WbE&U#V5S?J!jKjF&jwEmvKhU7rT=)%R0g z+X!_Crq*xXP!VSmKdA9-%;{Hkt$3Fa=^t=UA2bcSX2_PSQ!BCVfl;8PJqeZqF*V_} zPwG%Q*O=5=l93OO>QDG5*1!Dc)Qs|U0UH#4u8k*nL=n%lpNr}iVJ9W)xwhxtiOIs< zOhvSXqVXg}xr!E?e8gU@V)G4u{T-})UmLvv^ z@gH^_1G?y}C`s-UtADQ>Z1^l57kk3Q>t{^ww>Qc(>HYcXFAS=XE509^hgB``eSWY6 zGbSbX5XhAOpIS!Xf3oZ|W*GlGWmYQ?z@A zEmdL-5|2`$yc9Q327~ooV{JK$pQj}9a8$+%r%)XdZd;_VaK}HkYDj%2bPsu-C@tdI zm3;ISxUv#VPN$Ob8~!|DR?-x_d)k`m5CYhMiR%~Yr510e3(%@SDp|_f%H;o zjA}_8l`lV)Liyn%Ti<_glbc}9A5kP8=6?pRz>UC|N;ivA%~`6C>n5;(WG-XA`$7)d zIhb^Pmee4^MT8u_KE6_fB23T(j54f36ItZO$*#zw5yhhCtYLJ)@VpBsHE*&sZ5Dcz zO=G-bp}G)c4M?!bF7xN~eds-MBDl%5h<^mko!^#ufO4V!9WaBqY{;IZ@sB&pjgX0t zSLy`;MHL20gK0XQbq-)=wfYy2DJ4b}JC%ZIkvCxm004*%-~Sf|%7yv&oduGYK3zy0 z@<8ESeLIs^Easi;3*iaN{xhn$0b%wbFUB8!eEW!?lh@6p89;rPt>4Ek@{ij^yzhIM zOTkRIf5lsn`d|N!w;;&1eBk&6AV(j)M8B#14452e+|sWi760Ibh$T?JOWV(>L=<4t zdONmD5R$r7wg;sWTF*AL7K_VcZ}W{}JTKeGlg(p)r5P5X1~cBvwwctq$uF#S=-5T^ z=y}Qc;>Qk}KL`XqSqe?chTTS|W#cdd^Q5)bHa{K%yKGcK>|@*R`yW*=2t!VYE`h^Y zWId@`3yqiBns0>*b`E79GXlqc{jw@t8PU5KDg1Q!L17rHiqwCodmPd)J?ucBgNjl| zt9{$nhy6i9JD{gxV)l2%d8sko8}2&rd%<#onRNf{I)%VVUqEw!(KAVg*)v9OTw;aL zP(wkjukFNElU*f98kCXmnpqFoBG1&m*brk?*-u-MrK0(mCtMO!{l1|8M^yMfMH~O` zEZPXW@qZ8%{uFoy{pZ*IzbHWdvfv+K`)Js_|9mDF0GkKN0B!(L2W^4qfYry{XAiU! zgbU2;=UHbWtqb^>!BrIbr@7{ZfwRSAOLg>v;m7RvwUR?G!8oLDj#ieyF+{a83KEV{fl<6xr}L1JQkPhfn!`1nkX!oZ$aMPG$x9h zG8h6S0MsR9MgcScBd?iy&UeHa#{!?9Ew3)u2cc(+QH>3inL|Cu?*|pzopUzapQsqq zmQ73O?Rkd8M6%4abh8QtM3uV%r9^jKF1CuPw*`S=7J<-W7f3!K| zKN)-+=glA?}|AK3NKgJKh6EMomiywEn#~xd{e#yv|m{iUGqF+9eDV} zXGu4|nkzNyW#Wz6n9I|VrvLOquH9x%>W$a3XC6!ip%HSEhA4(Oo#oDJYI~#3D5C8W zP=xB)mBH0U2V7t55$ztn8#xt1LZ#wA8_QxNhQVTcJ=T8O{Mey`Ei#+TQwS9 z&2F4&7PPkqhws}f$5fuHb;Eya0K&D}-?NY`hD?nF=VNvG3&ja&7eY?aTR4xT zh!dwwi$;6t`ki+`30^>*Vj?rv73cv6K9kLC59yeRQ5q|CN9pBmhqIC~jjmyd4yWl! z0%Zau1Vwsit~3uaV0{_o_du@uIczTD2oEDq6fHaHoi&4g5U#ThxgcM@KlqkLrUfKr zw3E0crTmv{1jQBT_1LHl%8JVC;j-BP*daGJG;W$@laFw$tF15|Xe7Mp%)2>O)o@!~ zd)%r#OCsUQa$%2{SdrPlQTktMGEq$&U8%nJHsxG5C-wPn1+)ekBCR~Ru0XrKYsMs}@WoTg_$vZ) z`wSV|S{Zhw_cr|)BnKo1x51L}9m|@e!-al=wTaB%i zW3HQk+fnb5nzRMq##0*W$(J;6BlUV_)e&h>?4+XO8`4q((c82r z3HQUux^?}M*drbnP{oCEU{qRvkwe*5ilzC%k^rcL#JsYDTDauzed>S74AoLJVFu7+ z(x6DTwm@FKnuOi*$%5rqi_gRA000|~_!PdW|RElJz z$rwJe8x^=ahv6%UZ_q&43#+W#_BqiIE}7{tD=$KscKd-hE%9n8aIGPO*+7bPYS@F2 zGTJV09j$&HU|z#C$RTG0xh&q|=*(frQUf8oL#heD+kE(&<^$g}Uw4BYmLdc{=~fDw z9A*98Ku0}0ybdj97XYC7KNy+n@rP&srJS?rx2z9~zu;-XrZ9r&`_pRDnsMlP@By;G z7^R;Jpi3xlep+bp8=0GNnZ<1yvKzD$GKv+MpD$GHdVX<1FAzsd1f1@)6@{OJ;XLtfv^H4MvIE{1l~UFl0O&Kg-l={alD%8E55 zeD5oPt#X%*Qs1>7i9KSyqY0JQOmQZ4X>?yU(l~8!H-Vk1Ee8quNR*CFERn8lgyS}S z24-~mM=;D70rniiN-d zzOS8)9VuR}Cd6J2eG4f{*{lzIb2lwIyg&U8V$JAkpZ|>iZn>g;C_Lm~vr^qXH_wv3jejRxQ)u1Ct@dwo=D)wh1sR=J zEba=~OG!qKDc*{CT8+)HI*e^-q1_L6UYceLpsxawe-=8CsL~VxfxMrstAYq-igVoSHi-IdA-QO{9)*VpZDCCPzh)41B@+8pf6uHm`b)Fqlle8oAG; zzp3iGmLj<)ji8pmk;I;Tzm{ZJ75gv!SAoIAn)sBND$|EgN$9QeS{z|4FL)%D=GP5s`^6&{-!ZdM(SIkqggKXU0tR!ec?o36G)3!{I6bws#5 zXs7q=+PAHN6H%P-=(o(I{EjynVyBitkHoDuk(TQ{Yr9RXpiH8(*BFvW1|uk$c8rS| zu#5adGCE*cya1gMi7uuaYb-?>d@c_7QqQkHGI-fJ6)a4p!+$pCE|e_ii0A8k2JA)< zl8NZVWL!WzPW_e3IiOmSlPBzf>=x>IM~BY=FJPn07uWTjj$#0-N`7_LE|EBxA41Oe z7!UHTO=^-p|G@*Q<_B!JG$gTe-5+=I3>ua}EF!iik^6O4^sp;67K267mR3p}Lu`N0 z?HcWDKxhzFZNEf^smN(JhX9W;A9T)lg z&R?<#8at8;yw7|HG?ic^24Xzo$(D0e#Ba3_!m16Fk@zNA#Af{9=8G!bGpXZ?V8_R4 zauChkc?#;HV+H@rn{#_)VxO6);raGZ-GP)3S!dW$w&w5oxs#^m7sU>vlUpOCQcoc%bwM*2LgG~w2H;xMbqdqJ_Z;g=}$Ty2&C|jS{%irsSV}8nx+g3}y_s9UJ zX4RqZ9PL6V476%zAvFv-wYoOrvnDS@*m5?{JfA38I(=SKDkau~ocrwAn^j9bZ^g&aE=*PXQk02Ncl{-4Uk0X?9L{C4l9)cx_ z2`2JjMrf3VwCigwYW_U`N7~-(iGu9`%3spW;<393u5&BBzf49stgtvn4Xp|OjZ2fy z(;BC$hUiyu5&MzGi>z5Kd%cFrTUUF2XwPN~TK?{}5s{fj){|FAXQJ{}Lf!1VXEdyA zIny%xe*!h3cS(PBEls&0u=O6TA*hFKWKn9wsV!<~5>e7AVf<1>D`WAv8YOf3l*AFM z_=dbGo(yX(M-d-qO%6R40~5rzhWbBQE8#4JgwV*+TULBQYfVDXRV5!i5i{9Isb8@^ zR09B@s9wKeb^riv4uF;fz}x{45BN@R0+s`S8315s0eFM}^#C9&0GR2w{)I*`8HS<>stuCunxzZEFf2imwMx1=EplFW^r1cX>cz6wuy7IT0k}L?I8g=A79#$Aj z{xQ}Khyw16J2FI9DO@Xr$$Ksfe0*a^sVqu%$9QPdWtQ>L{g525+%F>A!Fk^8m!sRS zh7{McG)K{V>ju_`#!QD7pWWrUYRcdZRJ)ZjbENqL$%ws`(u>W zUD8VH>dFNIE9Cc@mj}lbWG7WSBGv}-0{7^DW1xsmO|}Ich*U$IEX|09@v}e+EXUbG z{z)mf=0D6lX%2od1C4~P+qYVa`JtaV2`nn^O9YciC5AOs3EA9x5S*&O$@k9Wxg(Be zly|lo@w>HZ0y$vyAmQB6WZjJGs_~irnB{ZtxSTj|rB*~7gO){!u;mGVVAj8^WSRF? z@<%#zS0Au}+EF_?4IFnaCzRR@&^IWVZ?W!Sb|@Fi z9}VSn-F~k|6Y;E&bFX|sJPgbkIS9kCl3|7s9o24%6Gotnl-h{`8`eMDwp?Td;e;ZMy?obaez#c*O>MrYqSP0! zUZEXs!>Wj;)Q`3fkg zsa%X?CGuW)^if0_C2Poa4If3x&k83U=anjTFb@lYHP}@6iLeJX{K4me9{+Tlw|6%v zS=s7EYz+z7_NZcc>(v~Kt1x^+C|3G7TgKk`dlLm%LK1IHZ%Pf z>DU6Ljtv+7H?IYG!g=PtcYks8UHbw$nFR#KpMr|+lmn5lEr@k zRfi!5LCSWHn1Ql7yXRSL4thSI1V~TU=keYM^1xU z=eG4w@`7Hz3EmFnF)o3QG55OFPW@q5e^-|zcSDY#m^B>79-e>|hj8An_J;IB1C#SG zVfwfW6%3kCA3)*wCaFP-+zPxrzzID4=Trs*HZhp{7TEChpRv~tk3o=m{Qhq zr|NniEXk}1^6#*4Do{reZVk^JP=j2mThZ82zgNuy7pGfNEqE6Nw$*-!ReGUq3K`Ua zT`dnk$PvN`)g#yBAvJSD(Y}7LTn+ebLZ(AyycJJOBeA}yT3Od3xXtJd8n5ph^2f?t zLiZ@9qe6g=<_RU>8|Wn$%n|&LUUK#-`qMn4cuFq-q=CgI!p0Ws^$V_lQ;N^l{%9&M zX8?p8}6{8whGnvNMlpZ>@)2YJ}Pp0vNXJwWDD&Q3TU#YZJ7HhOBPsYt&UTe&a3!H z2;*e>Faq;Z99m)!<8QUJ01^nkJ6}@0DmN6gy&KSkr%*(wXBw|Euk6K3V?-+!!bU%4SN02xz6 z;*m%3>0Ls;KP2oUDTJ<&b>DW%Wn?U$KWk6YBLbHh&TUi~rXZ%wCizk?kTg#i7V z*EBY72U|XNveNevmwQfA^}bt)vh?8h3;tamOS{VyQ=p_|65K!~e8j9o3b38iajWV~ zKJnu+BLpCR9j*?BlE+PFM~rg~`aRC{mLI<|2o9v(AK}cx6(v~P1sa zm<_W7B(6Pv?O%18Dp=zz+DBqXTq8zcc-^b_Ang5~r?e-QS1OOyYJ2pUQjUD7i-6AV zSNM1d`G9_cHm=Ldt&;IR=HjnCFljtwToqyMlo z6SoCNX5;eJ<+}im*>w~g8pS!zl8z{0dzdhGNeOZBgr5j&B3Zek3Br89kO|mJO}~BV~yhyFL;2hkeRl_wHW@!kAbhgd72f$ z43qyCF(rM-N$*apc8a`f^3EQ~h%A7Fze{cfzr2LCfkn2S1gw`aO(q0KA)DN0$qJd9 zEC^Cy_agcWZp0NX*}^{z`HAIVZTQo1qzBO~eH2$E%-47NarQwb1TwJ|NBV>4dW*~d z9ek(-ydYk+JD!`V(1TQ+zn-L|NWPSXsI>rnvO;&@ZJs|Atzr*Xb~+l#S1TzlSXgoG za3INM(7*2Vzd+9BX;@QFA)`VB(EzNf=$3wY`MtCZ!Hm%V zPC-e!$pGTPh&b|Qlsyl+?HtLOSA1>C8EB_c(-OWVF8*Jq;yXHsr-byJtmo{(A5VFt z2xnf2V8f!-_t#c2ClR`52<$<<$Un+z;>W{d;M6%C_S8;N+7?nIJu8GTd4(MtymG+a zZj5yGN-bobqviuVhP0k;+E4j^L|?AyT(Xp2YI}7ik$%t4L@=Y|U(9vre}%@%%Xc$M z^WByc38zkS=@ijU?IP0brI94aL>?xsSTg`1SHe~gLq3>h(84!)_u}pHO=82BC*6$< zq|@6od2)Yf45is=9r0%Uq=wQDpD#6{Z#&;guaST8(%-4||4Iu2cVC?=&b1s)sNRl+ z5mN9TXMMsdDDGR8u=p&6?5v47aGP}cbUQAN{ot?s{HPIvUMrxNJ0elikr7qMM+qx0 z>02|u)U(4|OMJ{3syRsltU}phHBOZvmq|?u`;mVewHC}c`=?Q`e~`ZaF)P1%K@d@^ zXD;OH%OarfSVO|>_6Q`%Sm@m|o{R@wdi~Z{SAw%4wK`tHC2JM6S_b6hN7q`(i;ZdB z_Jbw8lpU=(a5*VSzs|WvfVcoohR0~T!t6NU+nfkcF64iNC3k@(kB!P0N{xivws}2*KTyJr?hG^e_W1dBfF?H!G z1N&?IIUBbd%Hs5>wkyc~4>To$Cj-!~d2FZQd}0+(5xR=ay$L1gvom&Ya` zf_^si@ETkP+9zecmXTtu_JQvLF#uJ;OuPSXCiDMfrVuvkTZ-R{6oA~UW}(Trap-Vi zF&57#{^phLm`3G>?9rlm{9UH%e@Np0A&LJlC5eOoK@xL+eLlQF~FPDE!C_m77t48Yq!6+mj=zhA&sAd8S~h`ZDRPT@PW zcoLfcv=6Zb=1J9uT(mLe`eKcq`qhMF<2^k3qf8`VP4Dc|!E-zfBAJNVJuEu#Me19U z`BSLnCmuE@RAlJ~@V{sgtv@@7nW&1QkMAL>TeI?gq7z)W6T*D%Yek(FhYaN~OEVvz zJw!(0gscq+?k^&_MyBL3vx8E0HWC8FTAw(&P8=La81)H4wueZTLt zCQGvTw?~bB^MQ#=dzT%rB5MyYSafv#rD1(dt`$UTi&EnpBr^sB&U4mJeaSfoUD=u$ zQs0fHsqsJ_8FJzv?0XN8}pvxNQb*{{`=0TZ#G_f;5!kh84%< zsla~Xd`rN%&vcc=x$;qldZ(3^Qu&1pUmYutkI(dm>}KTUp(?lwdr>koMe|>9YEBH- z{`t?wb0XMN$p^=KKOorF0DZN51Da)ESeprC9CV5#czAzD=uXQ!PAQ!`9oy|HwoLzd z{O*$Ka`v|QZJFdg#C^8aLo{bK&xqD?D)#0&91!GGk{=gA(VCinu)O0GKX;}NfEh2q zl{tXC4Je{)VeW8*7eXdSgjnb1rB-(;ZFvH>%RsN`i1@B96i0HNPCLQ7Q=@k{&9)sn z4{c!MfR!lAGXE+qSf|SmU=hDDfl78GAGie}EOd|D}u4I;BS< z|LS1Dbgf&2>aMs|S5SYob@8u0ols?f%VuP;a}yWZx3dG$puj+O`za=b>C|oXi`Oer zSgzRjXYc&%#IfK#=!|wH$aAweX$;;U(DdlxkvPTgJ*-e=_gV>UI88_EDV+c;Rd1-4CF*ErI8^@eq`0dkA3)hQcC zh$i?n@6}=?EurADTzG0tAIfn$T39`PxGd5s;=qK}8E~m}9m~mP;EE80cuLKl!ksa2 z7x}|h_4&O9E}tq zew-}`a9^07hA)?V(g7s({HddSD^XE_y zq<9jDA`p^IM(lRLOlvkSakAf@Jo)gn+_k%2*jj$1{N=qm=8q)-RrVb(DF%gf!KCtv_}jOivQVxJ#s zUo~KsV9Tq~?QB1_H{4kQw8Ur}1}&1w7s$r*c!zVTITX2XWILwD zMgl@wOj5O$!^HF8<8X!5uI_r4wzB6IPi&Z1!{4qo5liIjuX{dhGUZDeg=U96H`B#5 z(m^0#Jf#8%-jp74*DXwuk3QL>2S{7r>gHTkfwES@QffgAL(1f4!ZZGONMf?a9D0wH z+s)aSsg<`z1Nqm7#$jT_4{j-ki9=D=tpbgp)`knnY z+-YV&4ED=V{3537^r@J@E>b%BxPE%@{S0;i)h(6XUI~-8vf@(WV=|3$*FLT)TG$&6 zKtb2xt27M{Qx3>FOoL&9yNvBTN6D5!!lz1?0*U%D9Bg5#7?+XEtZ%D-&948>0}ZnA zd>wIucy^4Hz==}1=~Jl^Fy&pvv+5Fu1k>0CLhT14YC%;;!3+Jp)n}i!eaoh4#vZ<6 zsh-YvNER@e@9fYz#N*OIebY$MGtj7G9)2;F?@B0Qap>!OvPx&;s{J;?h=I_6%2An> z@HZ=lCBy=R1v;^G(yS!o_Sju`*(lM{q&P1B!=8SEZVhY5#y2&i4FNBFdg|HPy2Flq zH#$Gy#nYvOEaPS$2v;$ z+CzYM{US*nsjau;amnW0i)Nyo)A#@YumzmMGtLN~o1?U)E#L8%!7%8f<8eC`K$BSx zd`>>pGpQ`@)IIiumgYyJ@{N7lhpFFOXl0~{aYhxg>NFVe?Tc*86c`K=?g|-g%+;7rZc)NOeGMommZ(iAQPpTE)Dgu!P@=ifB@qbM z7^t9NV{&7WcVXDq*)Z!LMv}#TU?5iSD=g>D?w=4oDwUZ*$B*_%&1j?$OAv*}EFKgA zxXhUrJnct~9!|Qe;Io#Q(Q3!%NKD@$+A+dPL=F2#{GsPzIKa1+9XqvNqi_O%;w8$l z+P8L3HJ7qn;2%GMtHupAr&v3C4(h+CJsIE#KZDaA!5ZrZDHwb6F>U zw)YzIJMt|mMBR-DpEMgsc@?g|azi&glhBJ0U9_?;O%6*0n+StG|2Yf?1V4#DNxbjeXRbZ-#=nv3&M0PMzymPl|5DK@YI! z%_~l5#ceA*{xRkphHM<$NXg??XK6f+>$Z9P6@FeyMvT+kLV`69`Z)Nly!~{j0n#Or zrQBI4TPmOHExaeY&=>bUQ;1tTpfU<$ue~nygGHTngT!|tpZQGwapOFO=k@Z7Tw|;d zafK)ljT(kOy1%Z{hE{gc^Yj7lVdYrPLb-Wl-W6ofA8F(xjB`!kGyhTfi11i*q7h zgx9YM1F;GwHy%?39hk}yU#x`9VpO<_aE=oesCK=#bUBK4Zax@GDCxu0woDU{-bVe) zP20p^PThrk z1($mi&$wcP$+Ohn=ZSxH|BJG-JRr0rC@h}HE+JLdq>PDkM5~P!Ga_>`jJXPx>FWUt zc(<=%n8GIGVV{396|m=Oz(*?o824LO96noY=60sWO@*g9+fMnrPqikgFE*WbVt?7)csEz4l;k?LXG0aRt12 z4JJOgSyFuG;~WtcogFCz0PzF>!vD7qi2sjjGb-OG1_YB}2C#ow%p+r%q+U@?~@-)+8Azb{)L-Pu%TeeY|u55{nDa#a>`rZk; z4`FvGhIyko6nAxVi5`3mDh4CXaxGG)g7-U;xb5&&HP($pf0_6EjC385x2dwL_z7X& zrH3d+(cm2`@W1&cy=n~EXO-%iM<2;DY-8v@)fQjwdd z+k>T{?lXUn@i!|Z9YbO^!st?epLcWcuZ3wliyh|6B~I5N#tw_7%sb$I_b=8ctcD0n z&SVSP{pDvUgivH29BKuH(p8g-vuGK|goh$hnlGL>^4S7HU&THH^ub}||K`CWR`qJT zpTe(jvWKKX1Wn#WK7B$yy&VT?40$ma`Xnv2%P_1RrcBF6xIpVr(D=ZchjlqajZ_N0t#lK0E9reTrt~Mz`9|F6< z*~P%ck|hxnO(){DHZW>6D?FfUw?9q}1O?#YHj<;8fl%XS@f!`_`k|ipUTR=yDTv49 zX#A7@K*9Dpz-hC44*A^2o*JZ_74fYRI?(D{lMI4w4YpFiS)yQk%`Q6Q(`I6n|CGgA zvEVmuGtZ<$(R@{-d2rr6<_!7C)Cr|>Pi!JIl~Zk9(d+bETJ_wafwl;`2($9A3<)9` zd6wFuT+wy|CP%FLu;2D=(MW zT13}f$@b(+m1pS8PL{#|)Dcu(EhZVX=N()G?MNqP+rjCMS^phymkRm&3fNsXZPM3@ z7qe3aMJ(7~@3zw(xW)aP9_VeL=k}30*L;j@^JCC*|7Qu7P$x4cLrpn6vYut*OmP56 zt|C7rB0!+0W5nC!kZD{QJl>}$`x-gjVz><_AQ?QeD@Y|M@demU>@CNyNpRtdZ3*G1 zT&qfDhU6i>x8Vfxnhg(we3;Xxt^F%WY^wr=iwC z9lm+qqvBza11W-*6or>1;TSv%D>n@+~E_!sR0hIj&N6 z5@I1g(*c7^I=2rZE$k32eVa;nB+EBV?nZB)MN#Af1oG{J^q8IqhV*#9YhV3tXaCe< z9&_$V7T+U*E;pKmO(sjIqg(9x)v$0(t0jDHCG|V%zQCXO2q%w-;=pRk-kJ)*t>}2A{Pa;(Fx4V)FJbBFm4NKW83y0eNavp% zLDavZPs}hsE$pFrahLp|8RBALWIIy1qq%KW73CR|%o$j4Bx|$h!XZ#;84F3759vYt z+}{3vcdV(I$m~9idw@Ee2v&XC;lq?^T9-3MN>U?@Ny|$J+uN|Z|0BA=pzRX64Fr@O z3(OT?=|dqoE>H7!?gi>?Pw0=9$xojpmAq~qYA@U3lLWdX_~qtc61_H0OH&%saOQCH zW`#tGG_pbENk{S@eiQB9S?upJ%2S-;k6Bo#qF9R8O9(i;uDt7h+(n0R0ql$H9S8Wb z+F+7NBQ&u%RCdy?dWjV0-PnQ@H38mtb&P+fJ_aSq?dEzQ8ldJ_ZbQap=Jj+hK!d5( zAe`3Ex{5vxq+=%1<1gb;4g=Z>Oofo0_(LTP0tP9{R`fzU>ilCC8l=M03&9E(_Pu|K zODL#*?NP#(F@*gUQ2xXDyr$8s_#*xLzQ~TMy9wdxD zS=OFfP?082pinB-Tx6ZcATlye(FYY<3BUW_NPd?IAQj9I<{?U?4EY8g|K0UI09RH4 zX!zlPoJxMyJOsJlyf&U#G1+ta$US;gH^&o*j(mfd=Kve;QDx*_Y+f*g!y6}Rs(=^1 z{BK)F>g?E`P#!3!8(LiCtp*M3z|!kcH5@|`^_{Z*%1XHF}*iX~9@4sx(V|37AqM^FQKccafb{99^If*QCeXzpAWlPwvLPdLnT;G$J{V zn$fj%t9Np9G(-_$&4%YgoN4pX$B%xLwrd8A zV7z8oV-~GVG^ZFbS)MV5l2r%8??pOo!(# z>J5j1kyNvsNcnL^37r9iubR1nefI zn9VZN-t5&(1x6ae84u+$bzCbM$APhto8BODCdE^*vGbcEqfbTd%idc@bcf=B9KsMX zDB|j=o1rq>tu&(FvfhH17XDU_9=nq|rE>bqyEg^Wtjb1ykaXA8@10!DQzltDuH)~^ zVGd5k%2cm14ykHW#?!A9BK)iX!E`0CqVU0tiKaJc(sK^sAAD68oASyVy->`O&j_ zl}lDM5)jGwG~HO&)FL>|$6_SN;{IexPByqF6TEz-#Iyy>2;G@I_v+DKuh;d;7Qr*{ ze#UqY4^opuggs_J#tNi$V?=zmTl`<(vB7*Q=ua47US4{7afWi!Ryh2?4U)H-LVwyi z&VIn_L#hfSlP<5pZ0-4-V*zkd?>1fnOTx|bgmI>d-s|ZCTohIFk#QrYz@>coRUlP5 zeiM+FbG5(?ia}&sDBm|uGzcE2lC`^*Shafe<{cnp8O3~b5Q8c$>|x7SknoWSPoof{ zO&u1O5`k7ZI@2Um9ZPfKTaNcQ(NH4kfo>>NPo(bolg!gVM6RvIS%z?>s}!;rNYIRP zr8L?I&(=ARjVLefy*(1={!ltU&7x|+C+mB9mlBO^w%`7Ii-88pKY`}*VLWHMTqO6m zp!yxc^+KqciX%@(U~pw@J7YJUBYkAiz<|Iykbuu^jL{mHjQd9eIbIF_p3Rk<&KsL2 zwf6E|x-iS#L%~!~E3j^Jz6^K1UI$QgP_5=MhN7VcrZIJTJv;U+2b$>)a%CG0YB8q8 z_ASJq{D+L-pV`m<)e{I1fNP!ytkrx%)i){6qg4=rsPXQZSNI#d_tL%WPSr}jn^##G z8Qaxu8DCgP=Cl-EIPuTE?{@NW%>FNCzd}qm(1asx>^9oxJYniZU9&#O10US=D;w5n z3MF<+Q?wEuke!#}=ea4(ueLW-t7T3zp537zvkr5#>(~@_?MYzq=Vg3%e~qB#tevk* zOP9=b2RFPQz_cXjgvn*|)ao?#Ai+Z5WTxO}wu!!?xL<;d>wAZbsKcsT=(D_0yjZ!i z)mN>ZH-`fa39K^loLn z*;{YYCg%3Dk0?{CS|`>KmtF3>oxi0_V)C~YdB2u@>XRu#xl=-oyELr6&g(-(JG1Rr zBhxF%Ajz(yu*J`{F}dmUti#!YH&qpzIJ5rg%vQPtQ<6eF+$5dJ;*EBs^fpn$b7kwq zf{~{nt&~M+Px=o^j^z5F*NUvQLaFVztu5LsS>yTPlh$XPwz8hPHRtrugFJCz^za*3 zjL&n($>kqVaIIzZ6LUrT439HPZhGopZiT_=3yiFm?QB`C8@_L8PNkLG1-49l?V&V& zRiWs>-Yiz^hU1<#UU?-EfwvBAd{ag(2u%ozk1Muuu5>k;Y$!=N3yFm;I?(`+!#ng| z5QGRTch#<#ImFMUtLf%1@ASKT3$*g%Nrna+y=@1E=}Gr-#0~?y?JGSV3_JSBHSjgT zB1v(rI^yDHmh9EB!M8$W_IJz&jXQwG7E5bHvI;38z(3*0nQ(G|`b|+gA5>RlQk(WR zpQ9XN8b(%epVKE4`L+8@rTGr)=Da6AeoMIIR*Rg;3l=jnV!S_wjGt4qh7p6%5diXn zN9ffH7otDc(WK~wW07Z~@MUw|?ols_^hTVKwMcqO51WuL7!1u8eLV2TI9YR0pzmV4 zhX!tC(;Jhj9Xnm+FMnoJ!Q*kf&O-I}AzjYbPhJ)EOi-?Nj4U=_%`^ToWnA9r zXrEQCidHnc$dyT6YUAnJWq)K|(j=2kSs!b%p9(AnFe{p?sX)qTu--({p2tyU>uq`YVoAh{SKR zduE)M8^-?jDWd7APU47rbFyzb=6Lt`e9lr@TP%g5?8 zp0tu%J5}r{myv1fgi$(&0`%rTVCKlOvmuDzj|(ic+TI5lUw^4rm2}`#vVN7{my+KT zq%*RF_hN?DQL?N88gg@=%H3-t9i^rT@|4Hc{-f@gPkx-38i7owH4G@7GtE@soDSE7 zooo(A03ldxK{y?_1`!(&B4;21oDei>zb`<;cZ~=w)YSb^)`FO0`CMuQGzIY^R59TW z)mQ5C266jSzc~LDm*@-WyipliGU$9BNG1#xkKgACJYc`NeK`|T*>olq+QzWy{r7z? zRJWp)_f?jZa5{^tl3*iX(%~_PZ{!PQjtg}?oyEX9#3gLz`ZXXJ3fL;AnrU0od)rLt zTn5&`gxd#4aam(}Wg>~k4`YFRl*i|(4+z$YrwVAcz%gn1R8w&G9@&9cM*Fc9t&3_QAk6X00$wt%cAs3!(M zs<7xzO9V)PvIPKs1GM3mE=d7!hx#V4^#-Fqflmd%M-h=SPEzZ_<*e#>}kd2TQOR7eAGmm5ZZ2$QUZhMdj)OwzIQ_Kp{a- zL31-#C}8GnF9w8~05bP>bO4KSbFp!8QCWZ>uEtIfTWhChj=vN*I~hA#SU|w8V(i>h zu2wEk38*4&DmzCw9|Vxf!R1dUX4du~*Jnel9bCaKb|5Gd z6mM$h<^u9EHgmLh0=YulX3#lubpcsBKvh5)T|mz~7A_!rFa$bVrp8WQP}tgBj2jAr z%t21i!!R{AwFW_+JF#{F{~4GE*xJ&{)f7r|bOJjVTRJ*Hsed7zprW>5FQ{%YZa(1O zps~HRgBTYd6~qke05)@T72^dyo9P02o>Lbv#0o0zVrKlG;Xcz{%*4!GEU4^Fp%eQ& zENDZFn}-8P<@`JtF(3y&6m)u4{HF$ait!6U5fE3flNc|RwG(uepdJBTM5r#1Gt>c| zmlF^ObzxudyHJ3@)8pmwBLe?hc-1uQp`C0V$`1U-W?q4|VE_Q`pC51fs6UT~O?Noe zGll_xhzw@}2ZWnN;6)ZhTP0r=2n>SBgEyQQMJT&x&iqH}KjQxl=zoK58CuCqYCEZb z$CA)z2^cxtqkbii>5v;ex0dI30NL zlZC;f<#9rmYG|;uZFTC&D!ZQz-`juroh-Ei6s=5$9Q2dx#XEfB6#|f#Q5EO~jRfD~ zCND-*VU)YUmCkp84IZuxUUmCABVfw#qQU3g@C~-uCQlVDqO}od5o;Q7Id8K7#_#Jn zii}rD;n?{iyqqigTi+`$WGlHm?On<+HKz6Xjd*=qpVYuTl zAr^6r_M)ckiD+ng@r8tiaq8ob^%3>{H<-3t8L`U>M-FF~d(@|Kvo0nvbc>k~oI{L2D zxZ!r}J6;B*cIh>yDInYK*(WqRp>9$vnfXd#4Oxo%nBvL|Z za2VPC1y}A278ny+X;Z38}Nx3p`@_(fYcS$D3TbGeDH(iA~~WzMglhe5-y zM!F-CBV1xIL;qD{DbZsxptnV`b{42(k$@h~VgzOa~dA4a8(u6)*hK1SrNFcg7 zzIdHRu}^)x<2lhN)eZcdB)Fxu6Wz@AtTbV-A+;Bnc!<`M(fB{LFg5L6uOD8c`Y=_C z<$Ocfww`%~S6J^p5%jxC_Au$>lu9pGvCg7ds~fDsmO@S%j%}+A_Ml% z{;Cn-CX1%JYYXp~Xv1bS~nBA3u5&kF_G?#3%A&xCl`7Yx1IXgQ(9z>bmht_==@3v zC&9|(=L;lyl+wH6jAIwA23Mt3LUO!`lSfs0HnIRIhF*3RZXTC(-b(4+1};8nY3zI8;p4t^=m%V(A_DrLK8l2r9q6 z2A|H?CYOas+6Z_M@Z4PtMIwCW8y7}cGhnM`QC{md-7)qiKT$YTdJ8biYI z1$+-&&N}9a$U72e8rs-euY@5D>@o318j^VuYj^V@-wW*6pEwy$0Y8` zrCr|rh=?J0F6@zX z<3&i;jC4Q=JNzk=3ISOrq%eUPQp&F^bQ)rXBu_Nh{utNuz&ZwT7gU#C^2%`dPhlLz zfTa*P21$9*W#5H*jqD4Slm4-fJTS?)*&j^8yB9^gQ%##Ng*$~$-;4jkxVxLg;4&f= z7PE6m@5UqRNn|{ti{$LK?9~(=%5>ng@|fofYS(AsAN)-@pC&+vhy_OLYizV~W3ob9 zjZ8_a?~rR`6_nmerykGy>@?WjD2$ujH1sw_p219}TiY+-JmPc?#_+tld9QHi1dX`> zc8z1MRvpqZ*Fad4{qinR;OeQD4VyK(;Phiru7NC8@!3vcg)b}VPGeiG!N(9w91l3VwkEj1Q<%(vfY92*eFfP_RVlsllZt!N# z;A=EJvK)4g8Rt}&l{epFPq$%K0sb_$wC)WzR&SAA$bnDac(|Oo8)0aeE&*u-o-Kj<%x0!u#S~P*T?!J4(}Kk?%TfjYxL_le!7iBcP6Ma z!&fg-$2UO?^w}{jRdWcLS*#yaBWr6ivlbJ4;NfHmvv2bV57CJOYwj=fv8BlCAfso+;}#*APL{c?#7&<1O$B^H zKl*9ok1t03gq81VSe471E^hF4geO%_V2-1e!Kc=PuzOUxKyVtCHWG~yM@gXD#S^`D zCDDZ@)Q(LZ#aHPLC zBTUVvo>Qw198vToRunA)bFvseEtN7zhr4Q3TTv~2#ZQzRRr?;D-MlbUh5JVAHPA_M z2`IdE_3`qYKX%nncbS8gQm}XHtYKK8o8|q5BCSHmEko}RpPsP|*=^ytJcy7M)?Ix* z(<;qgoYvO32z=Gxc4$aUx}C4iNRvWG4)+34WLA#)gogDaxeZM|C`8S6yLdEVUrn*uk)67Q}Kql z2ZqeF(||a^VIriWtawYWbjb_#2_}Hnm_+12Hgq~RG=f{-qRYh7Hr?TT@j(ub+y^Bq z(x(KLloc}v)uhdD8H45Js?)LWk5lGeD`KS_X|i#F)mNBrj9&*xBjLQdbe*3p@eRLa z!N%&idcqS|m8Zr}Hlnn?Nijd*3Kc{XIxVwQh5O`Y%z=88U?{3X-=m>$88r96<>nQu z7>JvV)G2pSoj-CV{Ni`$B0%>}_bx-rkVVngsIS7W<4r=Pg0Yi^rU&6W+`!{vlgc)J zq6|YN*s0h32P-{?J~d3s3csM`ZQoblh`BNr@%5E@*|l z#jQ6&TQaKPbPHmpqk{xu(DzvDVEGh_!oyC;MBCnF>;C!)BQSkQlC8NS=lwve)94>c z^Xa;6`ayip$kTlIiKn4lcr05DC`%1li&ieySxhMLj!R4_R0l=RcrY7UP|`IAnN)o_ z=f@3GLd}_v+DSAep9^b@D`mJ$d*L2|nwe&Ihv65+l?#t>xLZe?gwpP6SLM1g7_N0v zS}m4s!T~2a+o(Fya%J`|_o6gt{ui@G!-3|&4guwAScO`2BQ*WxT#R!AzZ zaz*wWdA2Opi6~9?Hs7h<#9561-F|;7#WBr#iEtFgk%Is0@j4oEu5+?x*uH_Yyu(&?r54yf&Z-Y!L?tqu zqd^brK(jdHi0?}MB;>Fw4(TTjeoS$tcQ5+LJa6l=Q%ZaKCFlbI`4*`>V9GIN-^N}u zVvKS@bUq3tiet)3@fugDB{SpR2bVZT%^<~{TVk`!qak+o^4BdzpN1j2>;Tsz|J&N| z$Gr@Zb2u!Hn7HL@tShdz0t$focO!P;9GZ^u_me--GFeDz7DH;ECKIuY-_<;si$wOW zLNIN*hzGR%M?FLB$FAXvEg!9;d8*udw|9em?X7IY6MyR(U`1gur?E84&tKqA5tJ-$ z8W(EtmQcrgXokh(jhC}Ew$t5?;uL_b5>hC#AC5v<-U;;gLFV&H3&F{$SW`Y&*OhuD zY?+P-_FdQ)5K1L#bQ)~cd8|7aaQp!3+w4(l001FGGPCc`rnN(PHOh?sKUXan&ub*S z`vYWz%C)Sc%DKUCNsZOv+Ka@|f zBlpUtqT^=-@v&i>OhI*$7OIU^eogI@@8`cYW_F{kIEz+SZzk9K(_?eiDWBSLQGPe- z`EH5ZX=2=u%fiw4lYhoFOFld@6w-((+B)VkMgA?9+GK#j`Qk06;tGB!WfWPu&nJ2D_(+N1tLqvONpM>J-FH|dAcMJnI)TPMyh{Eox9 z))9uF_f~_!id}AIIwm1^55@$5(ZDN!CciMxxY}P;+VY?zwCm2Zqit=qkM0vr6)+?cC!nnN#b{YS@*>v#T7YFZ(IzfI`Dby& z8++Pr)WeAhuh?%<)(TNoj20-{UBfeh5#&TdSX=JP%Klx9!6R3^q*7O@0z6x|2dpui zVqCZpUgPOeB$#n6=o%~>%4w4nBN{mxavj&UPlDcg64ht4Lsst$sd7sB3JgY@3q{~l zwh3X58!;GFwP$P~u#~%`bH_LH`xsXV17Tri`}`I523t$9KYk^&j__Ve4+Fv+P^uU; zJuGy?*sc+S#`YEXG6R0n6IujOJ0u8ajP?2wK`ux*aPo7P1u%9wX*^W zgbOXj>{(g40$h|7e)U!0OzKG2$*>wquDz3ZL1*N?ck#tXa%{|)cu`;?!RCDHgLw34 zu`<2L&!MXN{jW1Mp2!~}9$%)nw+z-Y)YxVnA*U6^zg>};uB*>XFzC?X)o&TvbNaqH zOKbr_{)JGhE_&k@dm1+aCQ*E-O`7z&plR;-5EM~lcINZ`-D6+`YHr^c0aFT5z4tJA zuMDj+-5R0lo>VoBUZpLHAVaZ|@D@SIiD)RxtW@&dL{H2S`3)??$3)(i8x^iz(l>B( zqFHD^#KD0yI(`fDOHT<#rzi#5FQjcT2n7PtgxHz#6K8P^(2){;HV?6@5(ZTby9T3x40Yi96S#F1_(GOP!K0ap?` zsbeh5(`1cBC;e+AJ+oax0BzrOi4=iD?3-)Il}w%0Gd zchdM{_NRwXG9Z2|6GoSxn$5V#Of+(cz zPaLJ3X^bM7VPEEqhy?kY^qPGitscW1r9215k{BK4c4^phCs<&ou<#Mj3p5=|{B#6= zipg6_tSMu0n0eRmAfh(^J_<5WKPJ1IjQePcvYWH|VTOQV%mM2Q~ zJ5|TUOW&8rvS+`ur}ef^@#nuR#P!}cYrExD?lEoRad{EbBFui1cK6viO5t@9dqC7g znLb&pYq@OysSu!ojkiBMaxUYs1i6|)fs2qps7a^q(1$)p0A|c)OVv7*I%4!1&r6X{!jjsmK zol^R4sR`-PR~yy!yvsza!g&8Mj)VFU{C@mLlJPCCN5H4ivF`Vta}-{jObSguZ@~6E zg$YuuJPpWO2It!!`g+B@Zd&!$j6}U>xQt|`tC{K?g|(7dH|3U+O^Uq0MOvThqxZ$s z6$FinneF#-+eL&B>D{niX_5uLKaOq+X4{bjBl{Tfwd zci;Lq?TwUY!P zIK224te_Cl`Op4yKU3SvIarj2`F|1g-m;)GN+ikh^J0;Idqt#vGq5{2C(~GpMH^-p z{BVnI#WO&LHZncmcDD0-u7mjswucY?F?TN1-wTDUhVeZTxm?Met?@1_V#rT*ht~}E z^7SLDs58?T=k1u>O_5b-KiM|$_((rA2K0g0F68cww7I^pBEad)DHS@QHM^u_#(G{;e=2p0HmuHQIs3>~Zz7O5vStN*89|Ac@=?wA&w{AqB7XSQ;R_F-u67 zqz_>p_J|kDsG!$v?D0!#+A8~F<=MQWdq?+ERiW9MdQYk=y1e8Gp5GT8PUEAOqqQ|s zmC2*QErJYu)x#of0&4In-!e{f%t-UmOiLc<|U>2@`fX6hVy z;k*(DmZhMlsf0y`gK20RROPn&{uqetA~Kj;$e~xdkmWxZGjr7cb15@w2Qn>DMnbL9 z9wE-PAGO>}Bj`i;Bg!|UZyH=_XKh1DNJ9|(5=ap{46M3$S&9C^RLkLi@iWp2SeAzC z=PhVjH|O^P1FOeA*qo-hS+NVj|+vFUqB%~KgS@P%90 z-7MeO&3mSnC6ndQZoUQdL#W^ED|cDJ7s(f9J!_KNaMZ>N>o<)vPcqAq>&r|d_IETJ z!6WMhoz#P|+82zc0m4rvCW^bI51%78h+zw%XPlg57V`h^jC-Mq;MiCq z%Fm8imV5VhmwO>b#&FT^EN5teq{6a~LUIO0(zyd9AFm;gIk#6PhM{+(KTCNSQ)BAY z1ZZ1*+4RMIz`~RiXCngmo1;<$MIT1ak3niN2;fr&;~(`Y2J1V%``(6sR%`yDU7U2< zijt6!Gr9f6xo5I=lvnowRV+ESbh25Ut3j%@Fy=?3^}r@Glrrn(;9qa<@YFjs%k6`l zCi<1&16^(buD8=kP7Eq4;b`CU>4Hq0cqk#ooq5 zxP4h4jPNH}aju=U^8m%(XEOO-EMY>H^1KTqMG4}iy%x<{P+qK=#pabUa&s^<2DK5V zzr#n1l&B9~IF$?Db>N32%>>`$lMbS*qkV~D=v+s28%^|#wX};}t`9&})nH)U(DP{* zzs%+x=Vbw2WIUqPN-*840Z@IsI+A}UgrlG6TtRTiCX{p2YeS4OuU7zpEm{U9hvUKm zKJ2H*FPZ(~*;Zb79JOmVzp=iG_p4Lx{S74jUXl{ARM{jc87&;qF@BnRmTg1b1t~uzefD3djn8+b^iDi`U`745f{y54k;BsJT(b1sX!fzliOO_33&7R zEE4YCVmWInf6X80!hCU!8yLn<+9jLQ))XMmp|;d_S6sT6qJLDpCjQ=7@56qKbQKys zlA+^Z0zqi8Zg+-eos*QBXSLZRtSVSJz5j{{;M-Adcr~%S|AsW3K}|O$e>e9_`T8)N z@;s2sr{li(Bzg%C%ZI%%G74X1|BFtRgX6dDMo;b9z})qKaRXb$sbk;c75luhnk3Nx&gd#TvMYxhr|{?&W^KGGV;PoFN%;_EO%?QuTc`>A}?`trJoQJI#a-w$Pieh>1hilEfW z_j0FLX`wMDYY`$8fv7D)5Pn;q9-YY_%)e%lVi?#dJ_!_))-`P;5&7q`&635J!=HWk zEOJJXdaTG9@}(uhQ#~lu8t5@{MoJV(A67e@ETKt8E(HKk(gsl6*>1nk5M6D>h&qw&FvIEgMJr<-ImN^{_7}#9~;GeB|*LN_q$lwuwx8V8x?65 zKulHT=&y*ye$5Qy!y9L2C3)Dk_c1Y{+^z}o#O^P;&6Cq|SEf@YY{3}zc|ToZ&S(2J z$(r7>5_e@7ojMz>e@k(gTsu1&N#XrQ%R*or4M|ZT=`Uk9C!^wAxHIH~!vKY?b0go- zwEErWZmP7oTwPyt1XbpKKV%j?gY)%8 zl(AkL#dU#Yi4^+DijAN2=?6#VyfT{nWbqx$tePS9y`hM!fz#5<;cMeLUcyrS%0^5D zQHGu$h%#(K*5T`iV=&g8$WJ9}$G7)*m;`L#U-8&!5B?YuqrwM236~j5qz+C^r1RQu z&G)_{e$%bxV_Ir0HI%^`vg-(VP?2-b!s(RYS6Hw<(rrjY->!Hf}=?`#oXG%6-PsrJ^S?b;2m`A_W zzinZ`yNyrg{slW6`TkKa^rS;>IcZ3}j_q_O^2b!`$Nf!mc=;5O1Aj1e1?SL-tbk&# zZncY?2ANg|o}OsKu9&0RrVFaEw(*Fml;ZKS66;XO3un`FP~7Ii6K03(cT)6%E*Wmb zqBb_A7hX){F0bZ$6{f=(Jki7Fk)2BEimYQ&gsfBJek@wNIl0v@;xhhq0E;+Nw#{)7 z5_`dxT!S_N+_T7qn~0EdNPZbIWhiI!?lq>d7&>mwR)(;t$H{)Vj9?ps%U&mypWSf zrh_H_=d;7Jt1Eq74r{fkA__$P;E~mkNJ{{T)GQdm#9gGZNRNdJc~Xra4*Vc5w`0H7 zmZyDon&UhfzFpd~FUMV4KRn|eh)U%fot0p^a?g07G?g}HPAaXXLaLBwX?KRh>uoH= zlVl%L`|ftzdxoiEStAg1xb0_%YNzs1ND;4i?r7}y`z)6p8*M#hJj{ywFOo+2wKA_Q zMdbT~yx)1R4nX3an?;Ws5X#YE1M&Lb!1#B_s@#WjI`N8UwW~V?%JY37o8uWvsZLeG zG)v;kpzz)Z#8lY|mSW*7j3qs|RD4;0GygcF7UDfP=8BE_z|{K=&eXKfRu=DlwqEpP zQp1nR$KTSPGlz*(wbuIK1J35{?_}A@mePZ_n>&`rW<%qvQmWgHhAbmY`eDtqM7l8w zRr_S1(HbR^S+;*49U2lcc(9~Te+NzgwnHkXF+vs>Ht2(9%>lC)(QUlHbOdU$XAB;q zTK(-g(4>p{JtwcH9$f$-*7^l%#L#seO4er>%`u%e!+_>D4jE zJ-i|3hCVG2(Z3D4^sJIuF@KxNBO&!~QxC0SLWcnko`^0YZ8OCiN{W-^X(|)-^znB>DPStB2?^%Vb119PQrv=2L_48rSlcn_NH)lNWDA_#E zl~S%cu1}QP!b81@=*EQ@HKbqV`@uSM(KSzlmxW_O*VI-oaB1sEBR)tr=;BVzoj=H6 zJLLu|p(wn!{v7f;+1SdQRhgG3wD~b)nvfQr+TH`399{4Y?9q#^+ z6s{*O2o<8&@o>0qSTQRu)|E*1Yj)~qQ8T`2|H^@D$YhC(pXQ{7 z=Eo1j%APepXHy#}8=g0EP=>3q?xi&ie$q&w60Dy-IiC;yP~XJX;+HPNafb7zsdMw= z5u*&&FDw7<)hwJG33ZvzIt9K`ii1ai`(%!jW{b%&%ETW`V=B?ZPp&}lRk=28Re8R3 zQ3{B|Le~yTTy{5@^*bRJodj)1^Ms-(Ha;}@W4ndxljo+jtz+?BgTbvGN0tEjg)!)h zWEfw`tl__%6(0t^lRDt<;1cw!%7t74lCz?T)SOO0Xuck9{Gkj$CRapk9BB!M>=lX~ zmZhKe1@_mi^Y0({JL0z={BbygGk{qID80=(7F0_dec-i;q8~%~lE$ zic)jy!TsC;(Q!!GmO%fWwCx=o-J$23oxQu2Ikf&)g4==8cK|TZRr(k9|877D{}&Me zkoY(6f5WiQYgiC8K{2eu(>M) z3Xy^BAkYBOKMAVgSugV6HpAT5n_GjR6e@f3|2=k5C|VSt)$>P=-OAj~DIX|cW;`j^wuafBL3EFy& zsg;5;2T(k%0f;T3b;X>-tk~At^Th?K0}eoeNx+^813?k*u%a3^@MWp|O_i@O939^5@haDoI2ELSd06+vhLU`o^xr2G_odkFR006or z3TWF4Kn?4XveKt3KGeoj6fUST-V z+SS!bjGNoT!-LD+1_E`o137Rx!mPL-d*QNnwYP)II6Ar7I6An90l^>(5Lkj22!mQm z@B<-G3p+=!tpu+aj~EXS%yl@RO7|6j2D#0rR1Y5%#?LlU6RbHSg3~Fa*;{wM- zJVhX2S2zN8wwK_6y8wcCJ32rm_;@*Zd4ZN77gsYU7h4;rM~S}-I6Ij+T3Wh5T_rgA zfUedsxCY!1AJERx(H3M4Z<+l=7#P0=BUSxjs5#ooDmk=CvdNlk8f;=UJMBxM%SE!SO0MNz>K1=Y7fKMXa7RVW%0FToN z2m}DIv!D?n0RD&ji~SB1A>u*JR058^1!bq_Y(;^A9s->J01|+EQ1cO>TvDGdN~16- zH0f z9j_<+Y_EWS!*l3$?(3(D_cbRB((VQg@)*k-$S?Wxr-!k~NBp*Wd{XfrYMq2&VtS%k z+NBxg7f3qEx+9rv0eMM066g{s-8ip#gLy$71P|Wdc5(RxMyC$n)F1MI3Gr1?S=3Xd zDxNB=aM|&Z@hVEn27j|UnhtMVn9rCDCPR_pi?nOTYv@Zuz{oeo|3Ut6?_Tsx-%Ot} z{wd<92I;!|h=kosMu;o_F*Eb=YTpJ{JN{MHjir}A(^`T-({Z~N5}%5j-lR+d-{sX^ zA7*31y0aUqo*)-*Nf4K3M7>#9?M}NJI0MKggQ?}r?{Ic@O%P%#3R(bb3YUpOP zny9$?tT$A$mpocISDo5_BrkW7`kf42`ftOYULFA|O&)E~I%&mkI}nK{Qd}ZgXvhdd zXRtQamX9~BL)9v=MDJ$hT9@K-?GcD?bmuv1mb4VpQ{mD0MJSnwi$9NefKgVE4n;aI zVqyzV?}Q)1NA1!hrE$K!VSbhOutv5;p~#sU@M_gu^yX>I;k7=eQDQ|L6@S5|<2g+@ z8L}>Dy>{2YaxML`ixxg~?UTH2Y9=`f7M%%lcrQ-<-tmJ=MoA@rLzh*yMLU zVc&9fz0ENL9v+N2Eo~lES0?rD@%6Bs*}WWO&__Q@(JzyK#R{YLzz-?fL3=qDOT!gv`@f-riPsDJMMG zP3_wYRFu+d%~fBFSj8QKu^3d)y_BQ`F3G-FWCVMahCY?jPh%$%xM45TfcQ{<5`?h1 z>Y|@fJ{)4GriPmiARm+ZC8JQ$Iz2x~yEIsnLdehF&5ZgLNW(cJSC=kvjQ|-Aex}tu zNYGo?+g(%48m{~sz(DB#g=)}fQo%abi&nq`V^!kbqSq8FtN-(u^-zWQ61J=3{3Z=I z1m&vmWe5rlpX>P70-~6ozDbQ&=8=p{N@-e1h#%6VH+R+;E;OjH)xEH-WEEFXx#t)fM}ch$$hy~>n>@RroL%fYro6L!UnI7+85c@Qge(Gf zGct~iX0}?{tiGYC>aMGuhza!>h(i%0k!}-a5I+KaQ^ky`-kP)MdbT~FQZx{H<`&|@ z!6t=W(de+b+$uovPANO56W1&E2+3y$t;30=e)}0JiNt`&inoF786amu)Av&Nz!Pai zuN?eg6^5ZM?-FZxaose+Xjss}+OPjJO0)&R9!E;*#HwFzIrR-$4YeAlAbZzF49U^Z z((Bqhpn_eWD`=;Zl~ZNo_=5I|#jj_iB+X4826Qr_yD-&IqQisp=1-}yy z98cHgqQ}(6J4tsDs}B&BVkYj-yu<9sKEh=6C_0j7&%{l)vadlZ@8Uql(w@ZXui@WR zEc!l=SvD6Q>snUYkk3cJvJ@4Ijc7eMZ;_srea6_es9|C;Z8~IuRr2t>AKB<#Q|@9p zPVoN5>lrdfmpAPM0?%SD28}`Zp4`Hd0@l?L^W`6;Jr~@f$!v=kEGk{@BXg5%NmV3O zyKe=PZ+}to69*M*2%p5J9OxauFw&zgJaw8d_c^;JnTdqPZFZcPtsQnWV3Gq$Oi)5X z^|`x^Vh=@jN}G%f!X6~Z=#A!6&3ikU7Y#Su8KNOy&fcH%_fJLUD5-tDPaGfpTE4B- z#EB@%bo-od<XQeqcf{U7QsoTScg?!w)HQ(a80qR6Nli z8lK8Ielu2PmYaGMp$~}@# ziE)H_CN}XZY3X%XTOdzsi+*==8>~TEZfV&XMvY$hu)py+_qoSA5pB7=WQ#kWdM<1A z4wqN?ueGvhMM~Tb$zPbJD2E!|7|TdmcQgLVT%3F%E1ahktXeHS1NAo>s)!jB&p z&R7pEQnErwi90y(+Ck*9enplNtGCyy>p`IjZ5sTpa=BLf=Jlloxmh{2H7KU2 z6I1jw-_5mMTj)uVnD02 z2MKK$H3f%3yaBsDO8@wU7^217>WZXp=^o}(bid?%LtryYy`J6#2-w`Tf?vQv`<`@l zTKaQOiQ(b(zr`bKs-H4M=J%@R%J zt$EgX@_5|QCgXg!0=_G-u<4IC_YIsMP25w9Jt8Ol2HFrr*elu!NQDW+BjmRp>W2lV zHnIl9Xx617XX!7RlXb#~1bO%6wG0O}fz7S7lrXZahQ8qo;uGfCAfzx4&Le&8q~sTPqImwl6La3U zUBy&87c6UhAKoWqF->YhTqeQeV~FeTs&UlA)cJ|4@Xl{;REw{j@1=hzp=9fAvqHk) zbH6*g;n2r$Es?jBs-BP`K z^EziPZXDL=dR+j02xp7A>*78p?1sCOLIhy3`uG6x|Nj|F0{|%T(ix0)vXb{X_3 zX8mrTI934EpRL+zcovXvNP&HQidk6|qNeCa_7F7~zYcx1iK6p^nbEbT>I6&4@z{z< z+jwqa1khZjt+C7eo-4yZPAx3^ymP@nC^YJ)2h-u~94Ye!-_06E{{p+%N0fSE;39Hp zcn78G)b_4xtbBnnO)>S`b=kQNBmkJzOg|0q6$Xb0GUSR?=kMBh!LBN&Dl4MmcMp*y z*C)kLNhgc(3mb~x=9Ty1qbHxG=(x7cg@pBgOk@vkvSfKKAV{wC*^Zq_Rdr0gc&n?s zz1pRdCjE!krJ;ikgUqu=XS@X+PcCEf(S=pa7g|IYCQJf4bR~8jnr4D_TTkyJ+C~cK zHJj8dCC)l=?My^^2V+29PE+AY`Wh_poxG<>6s4GSs54(P=j3C<9Hza!IvIM)_O6-~ zM?a`CZuskaR!n8;pGK`BF?bTTxBT3+(VQVVu}NQdTbEP&GjL* z>ls}g*DL;zomc9$y6zup*@DgO#qb9qmouk5^;}sl+jqgz2qf~s7{(M1Zj+}(nt|Ht zg9OKr!?SmY`>zFd{ixz+X8Ut&lTPV6RwBbkHnkY^Rp%pMhO;Sbx1<3p%}eMjA&yIz zb0bN&nhP8$5Hmd`yuG0@~-=*i;)ScTiZ6xW1+YZ2S*ZAy!jZKL7i}SVIc#djl={OAIwSQ z)RF;SwYQ5Z)ud=&wx6fDgiPt-zIswspDcJOB4QZZqQbRJt-<$B8E=LQRj~N8NoK)& zDaA4?EDT=+o&omcok9Rbl;zp^>!xVkK5T+%h5IM@&(*8klPDED{mBKpku*8ry zt4@(UH*op|O}!HaPIV{&yMlW^+C)R+@oxZrnW@|>6igLIP@z zKC6w8f-l5=yPa{7u71*Jgi1ThBhJ}gEoaLUEr`6o^8-JcgddW9oqhIpq4#!l2b%TO z^@sV3piSgDR>?p{rCKj(ZO?61<$C(H6}yyUJZzC}lV*sVz+~!ERlnzrIQP55vpB>- zJ@hA1HCvxMhA9eETn9ATNM}kc14g|$2blIx`BA^D@6{v@BVcYJePv%;i4?xRM(#E! z!*11K+}Qd}ss8?$8mJ;H=O?G%7ud}bShlc%QD@c7-}IX(}=!^8^|MOA_a@Hv0jQ$Hf$yO zGT$e>+{)$Oc7Nb?43lfn9irh`?+}cT)M{!!$qm(sI^oL`4)h(*zsD1Lh@$^l=x3}v zf^92Wd47-sE*}J>`|SMW*$rmd|8Yc#OeFeB8Mzb7oO>^xQ^2hw`%WloL$gv$8WuK(%RV92`|+x577olle?OmR zc%43c=zhlAhRc=DIo3UZe1=J|*?vmfLPlq$d`;(wwCIcsioJP+5rqcF`~LBx4!rJa zoJL>oHaF&cG`^;vTPGBx;{G}c?<7ihgLZRfA=TYS2Fb@_gQ(`3}Kc3xPY zT>jvm@ZU7HGq(o!z}ia+?qQ^=1ebV+HAb!`QZ=zW)!8NM`;Ly|M+JRYms zlh&YM)5}o%miN%>ZSxj+lgzK(Q{`MSQoZ!SQQAS46!+=hUG?2+)Wz<+->VK(ac6a7 zPccyp;u^p~=Nv7zgbud9m^=(KQq)$$$%%xqAdTkkI+!RG?7{A>&M%U#Kl+au3>I@J z)bP?P##EN7|8zx>`Enbs8-3V@P}QkTp3laLDWUYM(RRO{g;N35o+!j{Dl9=@dc&}- zsQ>n%=WClae(n4Cw-s!YrSzN0%Yz+>30+2hIN3t-D}-u77C)$-I<>e6U%&<|{T#3R^klc{8`1O#i#wD`IIlBe-sk?{cXrt?{a+o69CThbU3_9=Ey7+laxLIPXBp{0ZkaIacOm}{%5TI8W2_;C zEQ5-|=XMmhg>l$Xs(j)|4g#XEJn*{yTfkK2Dtd#-`TCTL5qFipbB)Lw7e-q%S``i+Le;xi+IhlcJQl~2 zB)f5>q=+(DTFn2{(+Rsy=Ev28w-<0Ammc@&IeFW15{=eoU~JCsmoKg}zH2i#v-Z&! z+$jruQ>W&ym6{!DDNlGm;1?ZS>g@2IwGYdy2@*f4`vo$RN4BYE#hDR9W?H#c5J0jQ(rD`NjlN z3vZ@ct$3-drKl5z489@(8ONe|t0zDwe>Aet_G_k^%>$}&$0_Ggl8elTS_}7&-$OkG zukA?WvnTsBUTL2#H{^UoS}vQjWr{M6@7PK*BJ@fp7)d!@db|7Sq`$=go%9QWq1sTj z^GsfBzC=R!CyW=v%DU+-pDaW*ip*%icS(LF@kj<^xk>(5y%Isu99%i*7DvjvY{Af_ zl%o6JDgxCv`K@-_cMCyZo(&kjlEL2=$yoQPvq%WwAT8k06LK1QgS*MsxNNX#kK)AR z{5v#F{N0qZRswwM^eVbkX*{oACi3WTOTSs8Tt-7Kb@r?vb5l!+rT#42-Yw`=V|j_< zU=)UxImkYJFr5A5fxG2WE*XCFKw<{~07D2lAXk}(KlhS3!RZ(WP-5Kqird!v9&&a4 z!M#un2{OE^2n5pxHSU1P4n_#mIMG=kq7=`KGkFx4W)9h>6tu57rF5Nz5j*C-rS*e(e%k`3A%0jOXb$)%4a?2)><%X!FY9lQO z*aE0^uS)_$V>Y09X*1N{KhohS&7(}?C+gfe;oyIrI;>w`8D`PIx^&O$s6eFWSJoc8 zuoJ;|?6*mnUcWOP!P9&mR>H{i@1x<{6eIvD zmXXWo%O~FW0NK;uH5H%8h#yLpFc5Qj^FHk#`qI}7#*1|HnVk=Djmt;YQ%B6Z8CdDQ zj7F`qup`>*g`wrL^Svm*Kq4zAcZLOsqt?~!={b4cPN;OSx^+-D;rcEKhSd&U9DY1r z`n|P@79L-afL^6W`@Yoj8+|TqJCU^>Vr_yLi~?X7Slx87>_xx)G1yU|Dzv9rQlN*rq?SzGHezNW7of{%kM9r<13{v#F#CK(q%uiC+ zYw5tvHn(3tuko)6j1tEjAISrS?dDA_9T$Gh5!l;h`z`v$@0RrQ+&7lVoh{aXzTODO;DhWer%E1`}-UEKQ&KVQ>jhZ^T&>4Cwib zl3cTDG9q5|o}n_7MSwuK@g>7=-e*0mIQh}K1dXe26&!pz5Q`|AjzX%ddhLTu35lZg ztHq-=Y%=rJ@y8I(Rx6P-{C%i^nyqg9K4utlo7Wv-@7wncjOcbddUc|c=DmON`%Mbw z?~IqHP!&~oSmFGSy_x$b3_Ho>yK&7}%yw?Gh|wgg$KbCn+%mx*Otx-vkHoDi%1`U{ zJ80(LD5->-(gLaa6)0D3FvVYzR)@2=1r|RD$K#G6O4hChE^u$3v52}4h5!}wr$a-< zxP)BDov<0>+2Xv~OvLFNBQnr2Efun;-_Qr4C)?@+PU3a^2q)azXsh}OvX!WYe4NFJ zL{weR_KY)KVzBq+jcB-wapm80S3VbPYj6?mZ#CGZh17`qxkCC_g_4~Q)|B3MmJka( z{lOlJ0roO7#Es1q?gvCzA+Ynx;XFWNM2JU|9FbIfX2K2>q7G-w+O`w&2If4|8$up9pE&e@5A9+GfBei45$;<2$ zYM5DdW6P|YxsQI3LSAxKY{a-o?nH}EF%(5*dfDJ&7XQ$zR**SX%;sUmrIkjsGS#-1 z%n6$bE6s#P(&jWLrp05szinlfT#I^PU6;k{m?z_ z#6+HJ!1*xk)Ah6bBKeIAu<8&;okfNqz>p=&%5%wpo4N+5P&gNwbK%2a9scA}m}g-l z*EM_;e@!`vJAY?2BX0<5r0nXM; z`=w~@ioZpwn1&M!Zj&;I>hY|+v)be7flWt>em#XFi+ux<2{<% z^Khq0eT~?S#)O>ai)b!r&PhD>c@uCjyQtaxwD9Dp;`dL<$Xl{LwP|P%osk-^EQtHCs{JQGT9hL+!S%m~U~em{71}JG{zUci zB{6VlKyN(Uc`e`GJ%}*9rYKM#uO~Zy&mPL<7MohlIB@4#d5?P`cq6&T&a1%{xX9w~ zR=-V67jemXKc#OSw*7t~*M83@Fxf_jcaCxbLKYf=$?wl z%6(mNd`mOQSMAsy%`@+bzHOmrvRNdV%2Fw!XeWk4NP~W@v=<*ZQ@-gUzSQv`OO7ba z^0VP-u>P2Vr|0)|z#?5Ij-V+(PX5a?2ltKK9=~5++>Xs}pE^;gd?|ycRG4%|snQy5 zWKw5Xz~3)DQRV#D&FHmzzgId+6UQ;zVY};}bv)q@2}Hoj(5thHFd}BLO;r+#%Os<9 zaN}#QuT#gSf#vUU>#lrH#}!3EEmopOjt%H{8$#G`Eq|ZfDPMbA(^Gd}{6+qPjJk_A zz01)e6Y@FoK0p>(Ggo0fG>^Pe3Z6gY(itO4Yj{jA5lJ8Z$)6h|-zP3-fMixVSEF8R z2`r0k^0m3sePsd1pop6R+z#$_h<=xCX}tLwVP!m(B4E)UY7;9L*%>o5CWPJ~K4k3{ z%`D+mox8F_am6c&-|W-az(YH<$mL@7VU`r3yg;myjexa*zCX%=jExsp@t0m{CQ*lP zx?F2;C{u|Q{&prn+WEMqd@)%s0WsxON573uoO$AADu%b#B2mv9Gq*T<(c;Td#XL%# zFNLGTPVY_{mC32T+Njk%A#uTKogl33Yo;ld^$xN#noLvo^MdRHN5QR^y`?b_ zAcRO?J6Wqdbuv~!Qi1eAfEm}i(D5zOXEuXDBjwGqnTrw(e;PvDQ}{HwOXvKQBY4LD zXXA=kk4UC{Jf+-_rF*^r!3HFYfuEliig1o&rJD()1_P(?a-MfSFTpD!Lw-kEN?r(2 zBdi~3i`pE}D!R<-e#bCWS8_OoI`k3xiZp3}L&i-DFu!0mVbwi4n!H z5)>omk7R)W0KOAkUlt)3>+dxSDKCAjkUr#$#ijD(YhJOuPqH7H7q)O@ba5Ttx3j$1 z0NgLjo3yPG?vMrm`>$-XPT?1itBXP>+{}S=CeB~`Ep)Sg(gTgz$QMUa3Vrb0TMqtd zKLabrg{tJCNGIUUyNGFI{{#E`(L^FdXsv_L0TN?Ps?eGCdrq%rl1A%;Eg!4JVrp;u zsO_b#0PU{=GIf>`2hCqo$44H>KVimADa1@DO%$KEaQwuh_07`!_!87^u~Yg5t9#;U zwXGe)c~G03PKaY%^J&+)?mjSNo9+NHTtLl>t*H<^-P~{@Td=aBcAgP5^!DxR!r1}S z{ei-JCk7T!oGwAvhRIe)mrB1AtuZlC4d<(q=1z(`M$T?i9f;-4tjlzLx)07#5IkUY z=}fNwSWeL>(ZC@8czB6aO+Fz6x z)LBoPRb%70n*dJB>%tdkq$YbuM|ZeoJ9~F)2ps;aAnm~UI{*au-|%15|Ji^u{s$8P zkovdm|HcvFCluG<=ge&3EMr&OKYhZ#!~9qI+uCE_|55&s({xkMxaPl*NZtovGPHTuA><F37UwS-eDMG9i2sKFv>%&~I)7}zA@;x9@TB=i?*FOx zj~o@;7w{j8|LqIh&p$qC!g=0*^Bw`5j`tG&jV}rQPFWS+9)*kf!2NK8Pf+?0=%BUt2VsC;|4pz;bVD>nFn4i+#s$XI5e_{{(*25K=A{d%o|#&%OI$&0e$0yVhQN&t#ZAAP@+G^bWwHyfE${5CI6^10)6W zb&_<)%1DAhATlHlfdGNHNZjEtZ@{L8g!ufta$0+>2CSVCiH~|Vx3^o9bKzc)@ zBqXFor6eTf0ilbxH&#(x+|SQX%ohbmV9+oRF$~UG93Mr@#oHYX*kG{UD2#`fBE$*i z2y=o;LU0HqR2l+DIHEC5Zcs@@2}KDA%maoF@IpW({G}Bo{3Ru&A?^sM3&J1b<>Lr& z@(`?70N@H79dK}{q?iQY1RNmlD1QXp0gosNcsSr-9?l4;q#VS_1&48mIRLJb5N{j; zjYfF^+*SXpa3^noaq@JBN&qRq;DHzq1XM~=L{buhgn4;8V7=T>SiHp_08gv~28r}S zctb^{Al@!Gzyko1f}k-NH<$}>=J2;r3gU%EIRVN1Es%hC;C=^jLb=1d@rj^3yb(Aw z3@`%Zj%XhoEWp7D-J70C*G}Dh2Q` zI1Gy)hNFWc3g(3mg2Eww56lmNa(3}{1aufI!o$HCg9WsIgjm4U4G{ofL#1RT{$d^6 zQ65l9S%{Yt!UN&t;|-OOz^93W;cJRRc)0-XI46gHhKtwZoS;rPB*fhjC@g+hzzI}J zT1*1si60D9LQD=|v3TI$J+a28uT%K;IcC(x_%Z(wZwK`w6md1YE&nY zL-EJeUJDAI=m?b&V+IojO1=Nw{-lhJoT zQN3XfN`KUOen02(>tW^h&754kYpL7!8!D~q43ZsRN|$HS4t{GVo7~CnztvkKpS?H` z&InQBk2Hq6}+umXLV9GepeL3ouM;Ud^gV3$-3q12nCyuycGV(`Uhqs^H>bNXO zIHsSPVE?*R89b1w`t8zgp6Jt_m@0XvY-ISt0yS6A8zZUme4B06o!f+pBh9yUs`pw) z(yq~6p{2g-x^bT#2c9Q2GQFc&!1q{zz05|$c5Lee+F^D<&FXSrcA^jX2m6I=IphNE zS1HNstdHi*j&|o1DkU?-o{fpI*B|K8_Yp?YU+?-@T0j>1d7S=jN50=WRla1mvZQjn zs679!n}kC+hnJn8x=LXP$;{1c7c!v-I31Lb@ws8_$N0@~K>_WH*! zPBV1QNmf=74Sdx_P`_mM~=rLIlJ32OrJUEoyf4^*0R{$|W^q}aRABn2U9*AenXPvDZdkdVT zGE^4_+bx)c>nMJ;h{=n{uz6ZuDs}#hbb7c2TKP_CP24Pk@;REuQKgij$eOYa&ok*-PlCJHep0j$b-|tQ`%W3w$wNUT2FMr^n ztew^~Q&r3Bf^Q%iVT^)hDrixL^qU!$3?;0@N0_J+*`C3++A0~}*p=ix-8w?qvnCJX zIQhQBBdQjQj1!$(tgmcoNh8Lp70Pw#z?Ob?*SX+Y?6vyNAl*Qs@L z?QAQDKXz3N2%LLd`H)cfgunY1ogD4hGoD|A+I&hsx<9+sja}R4R0LOizY!Ln(f$}) zHj|EWUK~>+($yWlfwhd$d;8^zig@wOr*4+%)|4Gg2^r*kGc$xo=k=Ptg#r=A6D8}> zwIC3sI3cXM!`y=4lxp>8Tf_N?@yvSbc zmV`el{shezGX&rCB-6V@r#P8fy735u3oleVp2j*ac-qw3WaSqG58tUD>lro zeJe*wY-u%;GtJJ8Y7AXS^cR-CV9vHg#7I?llwPTom0#b}y; zc5lOpK*o@E(l~!-t}k3epbNtlQ4>pCAf0 zcz)0O!M0#sgW@|X+45_Q+bxwr+`Y1KPlN)7(2cUJ1Dbn5x@_jVx%73fzr?Y1yUkgX z7c^X6)8BC1E9-tfczd^x?UAh!8PPsZCKIo2rmZ8p@8BA-aXVS`mU1N_PK)KW$p7j!U>%hdLK1_jq0@om!F|*?$WGu=Pul89SY+k zmkJa@KBwAwbBW}_vkN_+pH$uB>9v6Iby<4O9Ee2k$z1ij1KKyAjycZVjj`#Q@p7Uo z`bnbUTg;qe?igI|$n3|*G#P(NXS|QIEXAH^?C~AByxFQT&Pp8&WX`MJ z6*juVm1#}eEUH6t-2LVmVz^aO_4c=b=6kePq7$Bl{92h5B)^VMt9j^pSWGz_C%@ zqVSlJF{G8OMWgkE=|Ub!k(^MWj(yNzyae&nw4YwKPjnZDyXXr2d&7fjQtPj;Us5Kr z3?_h$b?MG+wI{6{5=Ce6YF%5<%|Bmz{9|7KeF7`r%ZyUq1YXQyi_ZhE+mPGQRhTg` z7E=X}E`RK{V1$;{%D%l4N$HI7XS#OAw;wJ$Z^Zasy;lJy5mOXAnL8Hf6Y=2t&Uscy z%4i-zE5Xaj+y7#n;i6FwEzJa)+)-y*Aa**rbNa|a?2zB{NWJ-VH7#6ZcRih9)U!hM z!!NIZP@zkY^2SdJ0gDZIbJ-EHvs#Q@9>09vIU1M#gWx zs+vYs^Lc+<^BAR_Xd^lsr90o54YhHg_R#ETIFGUsqX`YO`Bfzj@9H_bnB(UOg)_X? zMqHIMBMV9r<~ps5PPsKHc?BGlBhi)Xz#AA6xN>!Pz(dL1#`w+hZT9L3Y}-2Hca3PG zr*k8ZqA4C#)nuJj7es2Jh6yz~EtJn8I{k9&5!z zmU>

bF{7yyqzPsf*gKUhe}W`h-L6S?wUQu1&K=1D7_dIZi;hp)LMg9cx|g;`U~ z^WT3xHsDJm_hxyL#wvO$N9bmf<>*ao9w9L^=KB_mWitMoJp#BhJ@#uA1>F;u4`4N@ zo{RfK@FoAzuoRt{uJ$w8N@^Dk4Mu^?7oUQNYT%Jf7JB;u7qjL0ul!^^wFm_fF$BJ) zoCASuuYzoJLC#oEG6+lpA}|6G-v&_)fjA)`t57g2h#2_KoJ2%D>k?Yy{45^$^&|w5 z>N9O$*(NyA7Uk&@cK20%Uck1lH$(q%NK$3>4oB4W33=79Ul;CogsAXbdbz_>Hswk{ z{APM_IXd_DRjRd7slrL` zo0ENLJi)7^Wf+rr@nkV&!C_h0{x5m@%ExJb%s*b*OB>v^Kh^84{PI{}j{R`H-AL?9 z6`9U0lrz2cuNDFa3YN?ZIz#h{oAp`HFv}f%q^HO?GD)`2bZK5wtS)qJFFCov7xQ*^ zE-1Vn?8wf@sq5=~`^`VU=41ZNr~ujk7xH8TtBH`JxLr~@Wu^T{U{^L|?=pE;`Y+p7 zpWve`s2(x1=OuCj^W-dYbLKRYw=$*Lwt-jWj3hX~UAp}1_v(X$YdaF8>w43euQ?vM zRq`diIyRN#VQBc`6r8z$|N{>`LiL z?&h6QT3cJL7FJ=r&@kTl-o&uqqB(Z5qabWS{4F!R_CWv3@I|dE(v`6f+R>^q6l&v1 z?quK6%iUC$vR4`+j*vug6t!PmClf9?WcL$j>jd#oW(!|q9jGL0c(WMs$V5TqL@HoT zhtsiOZ6fw<bs((}Ikyo;@_dVziNDJ6MK<<&0xveDA=J)y z$tY3v0Xc4~Y0pw5_Ru76@5XoDcLWM?os?HUWg(d##5W<>qq%R;=C-PuvhA2|w_vHWH0y4Q9Hh`TeYH>KbTmhb*C#ec~#pcgPI=-&Up}^KhA3_c!G%o z3Tt=Y8x|{YU*CG$WL^=)dv2_Epd^a9i3)mjx{OIm6J$d9ti6i}t5|s1+48bP?%91W z=t$>Ud*=7!i-ddhZ=gJ0h6UM--EA6))8np>WYaoSj=zChjazIVI}N%hr6@Tlc8`p6 z|7=BnX#bwcrdI0t?0d-e&!Pg&0_ULJV|E9hw$N8n#U_xlAkJVJldp+KeOs3O(TxpE zsEsQ)GV2rL#QOZBnGm8a>vV1(L~7=KQ@cIeAtkyoUTm;rrUDYuY5)(iRJJN0f3!xf zthQFP6r11KvCYYS%KJz&>HLZdlCq4@_*eF-QG|$?5)tU+iQtY)_}-0-whb5X%@UuQ&ah*2n?seqG>VZKicd+FN-eH~zd-heh_UO&yi7~zT$YpZro1X7~91xgxv7c-% zbU8$Yr4_JHwkZ{x+^%Q%K}4<6e}(9=Z&9E>33ot9a^OQQD|DsE4b|}ZWJ>iKN2AQb zZ$Fv)+RQvaNE(~6vQyRZalfteE`w`L=+x~ncB{0#(Ws}8o|>?5E_g5judt)-T+s_w z$uxQGY!tWMWKyPm96FPCTT3n3@kz#pj8>DK^;^$M%B9TiFWhBJHCIAj+|MWwr>>xb z)#U~h4cxHt6MlWt`6RnKJfztLS7b%6*%*`GVM#Vt`|0MK)t*HTiH_KqMbU{ifxGRG zM6)B&`p8Z@s)_QL#XJr6s9S>8%KS)QdfTlZsEN?GZ~;4EruY*3X1JSW)>Fz6=9-QbJw?{^wrbdbt%YK0?>;Dou^1Y9=Y4wet@On0vxKQ_% z=&d!bJa}=A&m`1g`)7vS9jv>Gyg=zmxA&d`!m4dYw=6T_dDIw|*P zCbe?%dr~x22*^^{#_EHXuO0E9wc7vi@M}nA&YcM()Jo@e+pN~HX>Dxev&s0>&;t+I zq*nOZeEoT;x0Aj?|LupA(HcoVMe|f|?4D`ar`Sz%K3C!rKm~vUr?a+uUuG*9HaTC! zYNKd8iNai2BpIL8QU zS`#XspQ``jA8&MS)$AHK;+^mq5UZI#e`4Mo$*_aaG)+%W@KU0d*YD4L#mZtNo1~~y z^nq`paexjDEbo1~N`8Y{BHCnED zLDvi8W2{rG0-sX}ZeKNC}?nh=!Zch*~3Qm>!&-e5LHZGOn zX)I#uudwg{3Yu0q5WWY^^*P7ezBY1RTC62H@~+S_tXSc$b? zqmd7PLwBjHI=MF{f!ay|yI{GJ{&FN;FQ7t$1*+-!EHIDw*S4emtx^|*!}#G)|1O(@ z?WR@LOAj_C?KWgeH*5o6rSb#W!PT>#=q+5hm)aKfPu*{f?|v{FZb9{>tm2F^FF(LF zX8D2)txnC!4L{&v!Ovd zod$>0XQiii&nDf1O>^c zIe1p{N&D`D)g_DXwDzWPQj~7}YO8(1KBYg=`$W;uHGZND92hLtSP5lgaeH}5CbLwY zu_0_cHPxZkt6Gh>h}uBeakc{aH53S^1ox$|J?`zko6=;R)!dt?MI_-k-azZ* z%eao!b=?To5d8p(4bEJf9F2i=CA^Op86cWhwm-txgk=c8u~yHf{FAn__}`O4LOI^w zKxynUwNAJM+s zLM55;FE`2BPZ`a)4{j5^9%jS6E95l~aC(w7fKL^;-GR*2vqk>QD)Zlo5-(1_ltk4t zHgWT43h>m(3;jf)uk=m1>b_1^4v#51=zm4E8?YVopiOsa(EecEf(h4$h>WA7o3#^} zsft-Q)WQO|PU_i)|Bd_R|BJhq_+BfX%L2r0yK+TgFhSO1&t9F*YU^mf9?ab?#}re| zffYTtq4J`;#HOjE6bNIXp6&79VY2^UVT!48qLXFb(1y}ASh`qCPAy@-ySz`9)cF;d z>6szyOXvT>`fCeNoJZ~$j4u!X?e6OW2l{_C!c{=O3IYQ^8~>92=Lw+r4CFH3<3qr`y#S5_f%XD+ zxqmkR0gp@eSIz_q?r;CK=)CG>l{pNrJe*gM!9^YHy_p^0j z9&j`QZv$QeHZ+j{!{F=w8}#34^TGejM6=0Wb(bTr^Cg>0l6v%lcv;ZyVsj;^qCjuy`nZ6dDkS2+uPB`Qi(XZ+JPL zZaV!RTk=0Lp!mOaz02u;w6Ch`RtN{YX<%HMc z8$K3*19J3%d&7X<#vSpy2Url#zZVIxuy8Oe7C(>wG*Q%_mB?T2jllzQ0-s;tLGYj; S1DG#T_Nthql$fN1#Qy-&Bb0Oi literal 0 HcmV?d00001 diff --git a/active_projects/clacks/question/480p15/partial_movie_directory/NameIntro/00003.mp4 b/active_projects/clacks/question/480p15/partial_movie_directory/NameIntro/00003.mp4 new file mode 100644 index 0000000000000000000000000000000000000000..4b87ccf12d385e6e891d0bb91278dae9f71890a4 GIT binary patch literal 9475 zcmZvC1zc3!*7wleh$tc5F?54;cQ?Z@z{m_SLxXfFB1lT3bP0+`mnaBGhajOy3xXgm z@tyIx&%Muk@3)6__Bt#6YpuQa?-UyZ0Bt%(I zqS6xLEHD{}gbY{=P-qDO2EzJA>Y9S$EXo$DfTarz3Mf>)yn_%hS6>!UFj!1b6f7bI z2;F^sy=8@k0|Eks{NXMzF9gI>$P48rjEN%T?u$eKHeTMoa4%1^EDIFk41p?$u%KYB z3SulSFlU4p)I&i;7Ay;9fp|g?L1>r)I8aO$94I0p#)5u2;)p>M0X!U05KlLlf`}vw)E(u8gg64OA}qcr7yiF++F?tkK0g7^EK{^8iizy2@Q4kdq0<-vF zictUyNdl}l2KY}82~?1j0R(7Yn74vB3)~x+C16DWlL%lze1HXjnNCmy2t*nUONar< zU0fWEFBr!ANMp*h^SdSQfzV zA@)6-DZ*yTul#pW@m~a)v=%@&E;S~_KAImE-WV`_S!&h!ayB&sy38cJmP>_g9g<(( zyUk?9s+Ul=d`k#_ihrMDVv}g*SY8rEORal||NBNl(}OlPQNEI-^w!^ zX6Zdg9G|xBw97I7p>}u3?#Hq`rKJzf?-cPgbweuFXQ^%9g56u&e+x*QWz;ECH9xYD z>E(jrVHcqtvrY@PaX&4N_A0(Jn=Zvkjn(ES)m*x=3RrcJIyCWmF<2lQA^luSP|?Sv z4Y8V7%x1)@($u&q!d|A(V=nkHmp^o{QF7C~%64fv2wHl@aTVFj{2cxi$2R%YUvf@E z`?>VSi_}Mt{S}lhTC&*dJFlszz-XhrOU)Q>Oo`< zW6zF4Z*!UD9EC2C@%%mMADef-rA@qv7z`b$ao;vw%du>`XJb;iqo-IeAs-q#GOxdo z_+!-^-&d+6LyV{KH$K12O~0OrtA=e>(oO9{pI>2Eq)icy7SEgnX0S;SL(~j(Jn7`* z;XcG|@BMRcS zov0*h>+iQhQjD*@jI3ab?BJV9V}&^JJVKHjFsM$(eC$_|7@yFY{ZfsTk6fz2-4kL} z+}$zmFF4+8!|%vS`Xb}H%u|?0X{o-!MYcliZMOzz*Crk6Z z%!%A0omQ*tc+W3>3q7*g6sF?7ZWX8ZtDhq3sM@zQvOGijhSzQmnS;slMZ`V*HC4WO zdg55|+@$t@prJyK>TrIj5S*rJP$4uXGvj<KXXQNQMP( z%Y=m~=OsT@w<_V{Wbj$)8Jr|5V9ldZ@3{eYh>3E12$>u zUuE}=ux!=jLB>}`7T0rRL$XJz0tz+|utnxVk5PpP8T&3Ag%j$Ybx8~9pS%$U$P`kc zNy!DIUkc|-%U7RW-*UUR&kiGe2wsyv4Vf*avQdeyC?w~%4&@yb5+rp{BFp0(W{XUn z9mUuAYzP0?h+P#!@r2j9v-ryZzAyQUg$Ppz7b{kcn=drHDDLt%SGyHcq^YJ!F_K$c zm6*4)WPUGZiN7TztIYdTu!^G>@qNQ>Zn@Ca>Su%CzG80;AtG*pY}#3=m!Nm|j@9Fo z8;$pmSe$2vBS*;ne$C6vSrNULxCyd`7?B}Ak~LDc*EsNV=}pn8yZ%a=EKfN z?39V9{<+bmrkpbB%T1!>WPI?^_B7dYMvy)a55*7%vR6*%j{Z8!3kNFJkPv~AT|W&SPxrgN;`{IlrQ>{>9is8Bw2|e1Ld_jfQ}CuX_gfB-db( zJZrrExoHW4wkEo&fGdzz+F0(Ve(Uf+e?-h1mn@r^IL#vR6h>8x-Gk2Q#r*N94TRWi z{P~_WWtrj2z0ONSu3=e&%2;2}LbirgHVy$vkWR#%bCbiu&+ahlwJWKlx13Gq_2Yi# zSb3k~GV5?4F%<@*@j2fEwdo}uuZ9ei?O(3R23WF6-1TWRP%|QyP6S0p)T&z1sXPw3 z&2b;wg{y)W9iF#df3%n+KLx0Bi0E&Cu)@j-M9|RLDOSVc<4hy+R-clHlKn_-+os9aqhjXE z*1>ti-WTl-Gfv6L5OYL2?nyab$P?qtpX?=UP;RZ?N|m9hUI?u=ix>{6k)QQVp(1H$ z#^DdcQFrI7=TRUArh4lN**yb$3JtukkrjkKpmHb4y^tcdV;j_s>K>s=8YQM32n>4+ zzc8WENTK_zsBf}0Xx@WRrHmv7Re#X?6n}mXJWctWVp2#Wn=>yR+LHX0T>p`Jd3uU$ z_-bd*oenDx!iIAT=lQ$A%)u(e35Sq8AQ5Y_ znAtmX7DsPJRA-gE9u!OSRyZqN%~-cHnXr8ix^OJfw$oWWUmw(N{JPV~`epM;1PI%m z%=?6)U(w%37mie1zjo@wJ@Q#^;R9KV8Q_l_#s;NVq-+KxZwLyoH<?Z(I64vM=~U5DIk6G(v+ujO;DY`vLFWnz+f88uG5wVcCRCz`eVmSXr*5Z67^ zO%~R6<;NOV$m62(nf(I_xx81TnPLUPvK|>%EnQG0AH^x7M+g0?8;#IM!Ns4?O@; z!xY?7*@4SCw}n_7fAOnMk>PDSkB{o}Da3E3iYAPQY*Lntj=n=>`Uo2a<}-`rEX5nv zQ#uew597DXx!4#Q-*DWTz&+~yq|bNd{Q}FLL!bA{WW{vlcvtZ+t{q3skS(K>xfMR= zIH|h5@-R}`F@Hj46!+*=2K!I+08{YkLvE{qV#-gfkvI5Bl#+Vts}X|wAN_u5WN2Du z^UqLfthVP0wW?7!8i=T~rK36Wd766L$iM`h(_gy>Z4N`5U55{jeK6cd6URyk=@iTcaKjO<9N`|saMT_~j` ztBRN25GQJJIL|iVX^~STffMPjW;N%!;B+-O{#Zb!MdJo=uh}$&9zku*87}ETZT?o| z#cNK&EnmqEP{?bKVRZW3`&AzgBIod%(n1z!+0RLl)9@(e zoxLV`zO_v5f{no>f^+eC$=+)NYMG`n7@uozEP|ZP(-(Z4B@}Wt@pkv{O*5)Gi6ci8 zEuW5@<1wOiiI^p0{)9v`X9I9Mx)2^ZS ziYc9jJDSYQvwx`D_}$nFkqRRuX1uaeuCr|vD?8IGs!NMgE)FSHt8q_K!%;s2X`t<} zQk49IK=550Tpm?JO$)MNsrA=9`>kt|8h%rkTlh~52xtP{Y}XV~x6B;cM0vEAZ@y1c zN7XI$^i@8Em8vi@e_VB|;@g+}px2(Sl`{;_^nKYiBy_17Km?v}9;XSgbkpi&zR3r6 zwyw0{L_NPa;7D1DpS6&fPYc((c$SlGr*=MEvHN65Rbl$@k%)TqmwWiXpSg5dfb+Mi zEpyi{GxQ_}=)RP#JP%c~>FtBxZ&PR}BNqZoKjG3zT-aRdEsqTt{k_h-P^Ik?^LRc^ zDC^yJhqTgMoGn^o^_fB1NIhk8pL*tmHzPhLp;d7$L0m?zWYpJ(VGtFH61V8sdqgzt zFA5o^;3=X^G-~C~G}HR8-rxw=NR47$N;`+22a0RdoFTtuDj97oQ<tF$?^^P)!U}8>z*I+F7wIsyx)Rye!k#W#RZ(J{*U+VL?NjW;m@@Y2QkD!Z_*tBfpQx!K z$&-`pd4ZdQ^3x-szfed%qGaAuge>Uag=a$e8d^Ng!0o)iSG%!h_jt)0bs*+=0XoPce7B1op#Sva{d)@ZZF)2s(cupJZC==K z0=9**&fj+KAMqEu4R9@7_QJnCD7&T7CPoi_QAtki0B;yAjld~1R^p`u-R*4c4{=!) z9W=#%5?YeKN1g57*US)+fuGnOWfqloitVX!UJ&+LdGcCvnqsZerp9cPo1K=< zu_|%argc@B*Q7J675jsFn+>ZQPwKkxUUzwOm#3Vgpp=YBcMJw`>=nDrV{l!`d_mK`2h`nFdT zXK|}~`T20_5!W+VMh9KX7>%(aC{pnoPA69bpwH9984N=3Bj zx7fy_GVz}k3+;x!UcAKOjpFq%sJ3T&9!yM{fE*n(#H&pWpP*d`ZV?Q)v8b*$WE0R< zw%)okg#V^&U!O@oGaO6odSgZU*!7!IMij5mj}x4J)dh~yJD()aFXiOItp}zVr)nvV5KnosKD_1s?(5Fw<&5gIGfWJHh@y4TW~&pT9g{MJb&-kclCL&t#9lS7FK>Tw(OJYP#*0; zLxJDpr$pz=D&K8f9)wPt6?Mq-2O~>2QiamVXBwk99GZjRFageZv3R#eqconUT)n0r zeYn}fhFU`Mq%QPKhh;R6@@~IC5FObZ@ z=c()GkjAs!aa)<^MMTI!y~yw3%U-QY&+$%zS489D`!R!h6Atz}xY7I;1gOtj?Il-E zx{rslpZs`2vl`L-9Hv5zQi|1fisET*h7gw)`Np6GGnDaM%sr*UOdD|?fRL<`{C_LJU zAMDBd`5{-m?6jhmHK$5{#u3W()R;~#h}MA~b&x#8ve}XAPGRw3O~GU5g@&UQ0w$Q^tucL-Opfij_s5dSV!NsD6-IlRK+ND)>;#`AU55_zf-9 z#|P#68a@zjB-D$gKaD#)NW=bO^(v;pYnEmA{)?v&II# zuQ#5hT!b&VUu7#L5$)ehM;DFTS$69a+Bj!GZyzX8EO}CwD2aoSOfs-JG!9 zQ(ficdwfsfj#?z;tZv^mD-IIdYNXq75BE6N}$Ui z7FelMlF&W>K-_ra92PP0gOGbO>{Mx{VOLUE9$UDNDikgFjBap^y}ZRbqW|Vuo(Fx& z+)!htvnYH^B$lh{ZiL!#L)1Vpf$j(yL5r@$(o0bt+_pm=cfa;4y9$GnC#i(lsy3kr z_lkNHjmQ(6{NQ4KIIR1iK);HWJbrywrO-^>Jc?QRLC~e~$2T1>Oh0Cm%awG+laOdz zz=tx~cX^4Q#@Xc@4Sm^;x%f_?DEJ)p8=H+)@0!NtII7Qm@q?!bnstbDfmZ`DBf31c zJ6d(~DaDpN?+kxR6PNS~)Y;#mmpYf{I2wvRHx>!4Dm-02Z%t}7UNN9}PmN1CyXg-x zmE6~*44ZM747OojEO3xQS@N1@pI(IUP>q?*Ka0|B-+lL`(0Wr;Ur2TxxHycHncagq zRHxw8d|@xB6Z~Ya^4fif^Y-x(ig*(gd?;zepvR^{{n|nfTNlNQK0jhBWH;H~%;S5-%+-B-p`)JI=(Tip{+Jl+*#@y}sK~bDk16hOcfNNq zvZpNOSqz3;cbs;i$WhOc0^K$DS6~W>x2eo>(3?$>8I0epsGnFc|C03M77Dp7pH=tc zLbo_CAkYY=Y!g|(Fj2uc@zM@dvQldiI6z}%foCa&Vt|_)fU}EC!gU|8kpzLT zc$ITpw3jKkOtG2%aiwr8!nSGL;OqWqC~xyTfzs+CsHphe?XDjI6$j?`3_BD|O{s7D z`Oh$`s_1Q&ldGnE|8{>VWBu{BhhkyH^!~W1YqqzW1y71p`KRn%y9rl989(nLBu02l zq~Nd{W%4**PD@Y5hpn%h&uQ*6T-dr%}+CU413|akX!R^FnBXC1mq0 zXFbpheUVO1#l-=urC(CRoAg+QWQo?bted4f7|Lju{af^LE&68O=xJYos*>8fhXo>G zyKJ&yCz(XdW$w*%u@uIy@bPctMrpXllGJDtlYk1>sDQh{uyRi9{|pwG-WQAT#Gttq zOYY$P`FL$ThstI{_8PJ7hbBT|z4jK78Y|t8FKR=1)TIP-s=lO8Or~un3K1&p`xC!d zxD93Qz~SJ>vw^R!Pwh>0yfTi<@v^I2PYA}Nooer}Ny@^_=YA9ai(*+H*E!Z(bCIso z|6#1i@_fcjbo7hK)w4^Jx7YoZ4-`nce`THhj1}IY7;9;d=g!eM!G}4o_b|6nsjtyL z;;`&InA`a}^;28dAeJk@>Q%Ot3-D41N>k1$`>UWk|C#-ZcR!luBTJd|IwTg1do0RU z8RD+jnKz7Z1L5JN{er>^KY7-03}2jd4FpegFt&12Ji?a99^_A-SAV#y+wHu*W^g!t z0N>C34mU`qN*4V!yl1KXJ#G6^CbQB1>%w%Pm(0;gaPa^$Mm_F6*FlvORdxG<{LjKPc4S92NG~;sRE2dh@6SL6TMobh|VPD^xjiH?bo*b zg2k4++OX(R6NH#u$|Y@coR#RgL&BZ{Vjb;Xu9}_h24QsoF*L9W34B%liV*dFak>(+ z{a~j|FcnVgy?~l=;To|#FcF4YP@j42QheCJs?}KmX;3Jsz=|S_jvYDWMqFVLAW#-S zAOoyIqI>^B3cFl`c7r~tj_4<)f8JS(yZvc|_-66!UdQk8Oh$?dD)PpvVZA#60C(6733Q_iQ~vuj{Mwm_N@&Bfq(>9aOzX zsSpbESN!aq&%;ctwO4#(J zE~0GdbgkH&j%#m;V!s{cfSU~gd0?FZ`B^LH3TQ7Ao4~-VU4N%HfJsl;Uhz1;J%aue zL`F(!mOEY5veEckvV5L4+VsL>`y+nR&0e|2X4N-FzfGo7gC>&pPZ$D-b?qxPqiUdd=Gy!+t>&Mu~g3W{A=moyuNx= zj~QU>{ariy*Pn?Ol2e|gQ7%|sjXi$YE%`FNH-dak@Flm+W_$a`dEFIn)o!MT^BEt6 ztZw(*;ro!3?EzvE+aws56Jzq%$OnM3kX~N?KnMiV-`xf1|Iu(Z0sSTj3%DWtL;8PD z0LA}NfmrU8WyC>f7dP#h~+`%@Ba7+y1O7ye>mWP z+5Pc1kLfKiyFs)do-PO&#s+H@*bd+dFtq;2KcN3io5STV9it1d=K$jjG>o3q4~1a) zi^nQK`#K{4{wdlQ{r7}mY!WaVV1ORe{)zcVfMWratp%_c!nFUf_)GkcBmFgSH34C^ z)u>{*fS9kBLATt1zGm+3tit*-W{Ck9I3Q*UI>AgV5Wf5N$`D&O;6ab}{WGu_C`=Rz z;Kc&N(*pTo1{~8catv8P|6@z^mkb#EKRVKX^?!VU!{2h+f64zo$DCrY{*N6a5DWT8 z^PgBi9)DsQ0lMIS=`bB2r;r6cbpp>dpk@Gv7cdh9_;G-;0Lli)1t1-OOaLkZ$Q~ff zB-;W6)CKhddQ8LA1O~|2&&3x4^fpM?pB`XAtp8jjz`{a7yuC5=`0pkN|7#_3R{DBj b0GWWzOBfIgC`b?D? 0: - segment = segment.append( - AudioSegment.silent(int(np.ceil(diff * 1000))), - crossfade=0, - ) - self.audio_segment = segment.overlay( - new_segment, position=int(1000 * overly_time) - ) - - def add_sound(self, sound_file, time_offset=0): - new_segment = AudioSegment.from_file(sound_file) - self.add_audio_segment(new_segment, 0) + # TODO, Scene file writer now handles sound # Only these methods should touch the camera - def set_camera(self, camera): self.camera = camera @@ -202,9 +140,9 @@ class Scene(Container): mobjects=None, background=None, include_submobjects=True, - dont_update_when_skipping=True, + ignore_skipping=True, **kwargs): - if self.skip_animations and dont_update_when_skipping: + if self.skip_animations and not ignore_skipping: return if mobjects is None: mobjects = list_update( @@ -522,8 +460,6 @@ class Scene(Container): @handle_play_like_call def play(self, *args, **kwargs): - if self.livestreaming: - self.stream_lock = False if len(args) == 0: warnings.warn("Called Scene.play with no animations") return @@ -558,22 +494,11 @@ class Scene(Container): else: self.continual_update(0) - if self.livestreaming: - self.stream_lock = True - thread.start_new_thread(self.idle_stream, ()) return self + # TODO def idle_stream(self): - while(self.stream_lock): - a = datetime.datetime.now() - self.update_frame() - n_frames = 1 - frame = self.get_frame() - self.add_frames(*[frame] * n_frames) - b = datetime.datetime.now() - time_diff = (b - a).total_seconds() - if time_diff < self.frame_duration: - sleep(self.frame_duration - time_diff) + self.file_writer.idle_stream() def clean_up_animations(self, *animations): for animation in animations: @@ -638,202 +563,16 @@ class Scene(Container): return self def add_frames(self, *frames): + self.increment_time(len(frames) * self.frame_duration) if self.skip_animations: return - self.increment_time(len(frames) * self.frame_duration) - if self.write_to_movie: - for frame in frames: - if self.save_pngs: - self.save_image( - "frame" + str(self.frame_num), self.pngs_mode, True - ) - self.frame_num = self.frame_num + 1 - self.writing_process.stdin.write(frame.tostring()) - - # Display methods + for frame in frames: + self.file_writer.write_frame(frame) def show_frame(self): - self.update_frame(dont_update_when_skipping=False) + self.update_frame(ignore_skipping=True) self.get_image().show() - def get_image_file_path(self, name=None, dont_update=False): - sub_dir = "images" - output_file_name = self.get_output_file_name() - if dont_update: - sub_dir = output_file_name - path = get_image_output_directory(self.__class__, sub_dir) - file_name = add_extension_if_not_present( - name or output_file_name, ".png" - ) - return os.path.join(path, file_name) - - def save_image(self, name=None, mode="RGB", dont_update=False): - path = self.get_image_file_path(name, dont_update) - if not dont_update: - self.update_frame(dont_update_when_skipping=False) - image = self.get_image() - image = image.convert(mode) - image.save(path) - - def get_movie_file_path(self, name=None, extension=None): - directory = get_movie_output_directory( - self.__class__, self.camera_config, self.frame_duration - ) - if extension is None: - extension = self.movie_file_extension - if name is None: - name = self.get_output_file_name() - file_path = os.path.join(directory, name) - if not file_path.endswith(extension): - file_path += extension - return file_path - - def get_partial_movie_directory(self): - return get_partial_movie_output_directory( - self, self.camera_config, self.frame_duration - ) - - def open_movie_pipe(self): - directory = self.get_partial_movie_directory() - file_path = os.path.join( - directory, "{}{}".format( - self.num_plays, - self.movie_file_extension, - ) - ) - temp_file_path = file_path.replace(".", "_temp.") - - self.movie_file_path = file_path - self.temp_movie_file_path = temp_file_path - - fps = int(1 / self.frame_duration) - height = self.camera.get_pixel_height() - width = self.camera.get_pixel_width() - - command = [ - FFMPEG_BIN, - '-y', # overwrite output file if it exists - '-f', 'rawvideo', - '-s', '%dx%d' % (width, height), # size of one frame - '-pix_fmt', 'rgba', - '-r', str(fps), # frames per second - '-i', '-', # The imput comes from a pipe - '-c:v', 'h264_nvenc', - '-an', # Tells FFMPEG not to expect any audio - '-loglevel', 'error', - ] - if self.movie_file_extension == ".mov": - # This is if the background of the exported video - # should be transparent. - command += [ - '-vcodec', 'qtrle', - # '-vcodec', 'png', - ] - else: - command += [ - '-vcodec', 'libx264', - '-pix_fmt', 'yuv420p', - ] - if self.livestreaming: - if self.to_twitch: - command += ['-f', 'flv'] - command += ['rtmp://live.twitch.tv/app/' + self.twitch_key] - else: - command += ['-f', 'mpegts'] - command += [STREAMING_PROTOCOL + '://' + STREAMING_IP + ':' + STREAMING_PORT] - else: - command += [temp_file_path] - self.writing_process = subprocess.Popen(command, stdin=subprocess.PIPE) - - def close_movie_pipe(self): - self.writing_process.stdin.close() - self.writing_process.wait() - if self.livestreaming: - return True - shutil.move( - self.temp_movie_file_path, - self.movie_file_path, - ) - - def combine_movie_files(self): - # Manim renders the scene as many smaller movie files - # which are then concatenated to a larger one. The reason - # for this is that sometimes video-editing is made easier when - # one works with the broken up scene, which effectively has - # cuts at all the places you might want. But for viewing - # the scene as a whole, one of course wants to see it as a - # single piece. - partial_movie_file_directory = self.get_partial_movie_directory() - kwargs = { - "remove_non_integer_files": True, - "extension": self.movie_file_extension, - } - if self.start_at_animation_number is not None: - kwargs["min_index"] = self.start_at_animation_number - if self.end_at_animation_number is not None: - kwargs["max_index"] = self.end_at_animation_number - else: - kwargs["remove_indices_greater_than"] = self.num_plays - 1 - partial_movie_files = get_sorted_integer_files( - partial_movie_file_directory, - **kwargs - ) - # Write a file partial_file_list.txt containing all - # partial movie files - file_list = os.path.join( - partial_movie_file_directory, - "partial_movie_file_list.txt" - ) - with open(file_list, 'w') as fp: - for pf_path in partial_movie_files: - if os.name == 'nt': - pf_path = pf_path.replace('\\', '/') - fp.write("file \'{}\'\n".format(pf_path)) - - movie_file_path = self.get_movie_file_path() - commands = [ - FFMPEG_BIN, - '-y', # overwrite output file if it exists - '-f', 'concat', - '-safe', '0', - '-i', file_list, - '-c', 'copy', - '-loglevel', 'error', - movie_file_path - ] - if not self.includes_sound: - commands.insert(-1, '-an') - - combine_process = subprocess.Popen(commands) - combine_process.wait() - # os.remove(file_list) - - if self.includes_sound: - sound_file_path = movie_file_path.replace( - self.movie_file_extension, ".wav" - ) - # Makes sure sound file length will match video file - self.add_audio_segment(AudioSegment.silent(0)) - self.audio_segment.export(sound_file_path) - temp_file_path = movie_file_path.replace(".", "_temp.") - commands = commands = [ - "ffmpeg", - "-i", movie_file_path, - "-i", sound_file_path, - '-y', # overwrite output file if it exists - "-c:v", "copy", "-c:a", "aac", - '-loglevel', 'error', - "-shortest", - "-strict", "experimental", - temp_file_path, - ] - subprocess.call(commands) - shutil.move(temp_file_path, movie_file_path) - # subprocess.call(["rm", self.temp_movie_file_path]) - subprocess.call(["rm", sound_file_path]) - - print("\nAnimation ready at {}\n".format(movie_file_path)) - # TODO, this doesn't belong in Scene, but should be # part of some more specialized subclass optimized # for livestreaming diff --git a/manimlib/scene/scene_file_writer.py b/manimlib/scene/scene_file_writer.py new file mode 100644 index 00000000..4cc56bef --- /dev/null +++ b/manimlib/scene/scene_file_writer.py @@ -0,0 +1,325 @@ +import numpy as np +from pydub import AudioSegment +import shutil +import subprocess +import os +import _thread as thread +from time import sleep +import datetime + +from manimlib.constants import FFMPEG_BIN +from manimlib.constants import STREAMING_IP +from manimlib.constants import STREAMING_PORT +from manimlib.constants import STREAMING_PROTOCOL +from manimlib.constants import VIDEO_DIR +from manimlib.utils.config_ops import digest_config +from manimlib.utils.file_ops import guarantee_existance +from manimlib.utils.file_ops import add_extension_if_not_present +from manimlib.utils.file_ops import get_sorted_integer_files + + +class SceneFileWriter(object): + CONFIG = { + "write_to_movie": False, + # TODO, save_pngs is doing nothing + "save_pngs": False, + "png_mode": "RGBA", + "save_last_frame": False, + "movie_file_extension": ".mp4", + "livestreaming": False, + "to_twitch": False, + "twitch_key": None, + # Previous output_file_name + # TODO, address this in extract_scene et. al. + "file_name": None, + "output_directory": None, + } + + def __init__(self, scene, **kwargs): + digest_config(self, kwargs) + self.scene = scene + self.init_audio() + self.init_output_directories() + self.stream_lock = False + + # Output directories and files + + def init_output_directories(self): + output_directory = self.output_directory or self.get_default_output_directory() + file_name = self.file_name or self.get_default_file_name() + if self.save_last_frame: + image_dir = guarantee_existance(os.path.join( + VIDEO_DIR, + output_directory, + self.get_image_directory(), + )) + self.image_file_path = os.path.join( + image_dir, + add_extension_if_not_present(file_name, ".png") + ) + if self.write_to_movie: + movie_dir = guarantee_existance(os.path.join( + output_directory, + self.get_movie_directory(), + )) + self.movie_file_path = os.path.join( + movie_dir, + add_extension_if_not_present( + file_name, self.movie_file_extension + ) + ) + self.partial_movie_directory = guarantee_existance(os.path.join( + movie_dir, + self.get_partial_movie_directory(), + file_name, + )) + + def get_default_output_directory(self): + scene_module = self.scene.__class__.__module__ + return scene_module.replace(".", os.path.sep) + + def get_default_file_name(self): + return self.scene.__class__.__name__ + + def get_movie_directory(self): + pixel_height = self.scene.camera.pixel_height + frame_duration = self.scene.frame_duration + return "{}p{}".format( + pixel_height, int(1.0 / frame_duration) + ) + + def get_image_directory(self): + return "images" + + def get_partial_movie_directory(self): + return "partial_movie_directory" + + # Sound + # TODO, make work with Scene + def init_audio(self): + self.includes_sound = False + + def create_audio_segment(self): + self.audio_segment = AudioSegment.silent() + + def add_audio_segment(self, new_segment, time_offset=0): + if not self.includes_sound: + self.includes_sound = True + self.create_audio_segment() + segment = self.audio_segment + overly_time = self.get_time() + time_offset + if overly_time < 0: + raise Exception("Adding sound at timestamp < 0") + + curr_end = segment.duration_seconds + new_end = overly_time + new_segment.duration_seconds + diff = new_end - curr_end + if diff > 0: + segment = segment.append( + AudioSegment.silent(int(np.ceil(diff * 1000))), + crossfade=0, + ) + self.audio_segment = segment.overlay( + new_segment, position=int(1000 * overly_time) + ) + + def add_sound(self, sound_file, time_offset=0): + new_segment = AudioSegment.from_file(sound_file) + self.add_audio_segment(new_segment, 0) + + # Directory getters + def get_image_file_path(self): + return self.image_file_path + + def get_next_partial_movie_path(self): + result = os.path.join( + self.partial_movie_directory, + "{:05}{}".format( + self.scene.num_plays, + self.movie_file_extension, + ) + ) + return result + + def get_movie_file_path(self): + return self.movie_file_path + + # Writers + def write_frame(self, frame): + if self.write_to_movie: + self.writing_process.stdin.write(frame.tostring()) + + def save_image(self, image): + file_path = self.get_image_file_path() + image.save(file_path) + self.print_file_ready_message(file_path) + + def begin_animation(self, allow_write=False): + if self.write_to_movie and allow_write: + self.open_movie_pipe() + if self.livestreaming: + self.stream_lock = False + + def end_animation(self, allow_write=False): + if self.write_to_movie and allow_write: + self.close_movie_pipe() + if self.livestreaming: + self.stream_lock = True + thread.start_new_thread(self.idle_stream, ()) + + def idle_stream(self): + while self.stream_lock: + a = datetime.datetime.now() + self.update_frame() + n_frames = 1 + frame = self.get_frame() + self.add_frames(*[frame] * n_frames) + b = datetime.datetime.now() + time_diff = (b - a).total_seconds() + if time_diff < self.frame_duration: + sleep(self.frame_duration - time_diff) + + def finish(self): + if self.write_to_movie: + if hasattr(self, "writing_process"): + self.writing_process.terminate() + self.combine_movie_files() + if self.save_last_frame: + self.scene.update_frame(ignore_skipping=True) + self.save_image(self.scene.get_image()) + + def open_movie_pipe(self): + file_path = self.get_next_partial_movie_path() + temp_file_path = file_path.replace(".", "_temp.") + + self.partial_movie_file_path = file_path + self.temp_partial_movie_file_path = temp_file_path + + fps = int(1 / self.scene.frame_duration) + height = self.scene.camera.get_pixel_height() + width = self.scene.camera.get_pixel_width() + + command = [ + FFMPEG_BIN, + '-y', # overwrite output file if it exists + '-f', 'rawvideo', + '-s', '%dx%d' % (width, height), # size of one frame + '-pix_fmt', 'rgba', + '-r', str(fps), # frames per second + '-i', '-', # The imput comes from a pipe + '-c:v', 'h264_nvenc', + '-an', # Tells FFMPEG not to expect any audio + '-loglevel', 'error', + ] + if self.movie_file_extension == ".mov": + # This is if the background of the exported video + # should be transparent. + command += [ + '-vcodec', 'qtrle', + # '-vcodec', 'png', + ] + else: + command += [ + '-vcodec', 'libx264', + '-pix_fmt', 'yuv420p', + ] + if self.livestreaming: + if self.to_twitch: + command += ['-f', 'flv'] + command += ['rtmp://live.twitch.tv/app/' + self.twitch_key] + else: + command += ['-f', 'mpegts'] + command += [STREAMING_PROTOCOL + '://' + STREAMING_IP + ':' + STREAMING_PORT] + else: + command += [temp_file_path] + self.writing_process = subprocess.Popen(command, stdin=subprocess.PIPE) + + def close_movie_pipe(self): + self.writing_process.stdin.close() + self.writing_process.wait() + if self.livestreaming: + return True + shutil.move( + self.temp_partial_movie_file_path, + self.partial_movie_file_path, + ) + + def combine_movie_files(self): + # Manim renders the scene as many smaller movie files + # which are then concatenated to a larger one. The reason + # for this is that sometimes video-editing is made easier when + # one works with the broken up scene, which effectively has + # cuts at all the places you might want. But for viewing + # the scene as a whole, one of course wants to see it as a + # single piece. + kwargs = { + "remove_non_integer_files": True, + "extension": self.movie_file_extension, + } + if self.scene.start_at_animation_number is not None: + kwargs["min_index"] = self.start_at_animation_number + if self.scene.end_at_animation_number is not None: + kwargs["max_index"] = self.end_at_animation_number + else: + kwargs["remove_indices_greater_than"] = self.scene.num_plays - 1 + partial_movie_files = get_sorted_integer_files( + self.partial_movie_directory, + **kwargs + ) + # Write a file partial_file_list.txt containing all + # partial movie files + file_list = os.path.join( + self.partial_movie_directory, + "partial_movie_file_list.txt" + ) + with open(file_list, 'w') as fp: + for pf_path in partial_movie_files: + if os.name == 'nt': + pf_path = pf_path.replace('\\', '/') + fp.write("file \'{}\'\n".format(pf_path)) + + movie_file_path = self.get_movie_file_path() + commands = [ + FFMPEG_BIN, + '-y', # overwrite output file if it exists + '-f', 'concat', + '-safe', '0', + '-i', file_list, + '-c', 'copy', + '-loglevel', 'error', + movie_file_path + ] + if not self.includes_sound: + commands.insert(-1, '-an') + + combine_process = subprocess.Popen(commands) + combine_process.wait() + # os.remove(file_list) + + if self.includes_sound: + sound_file_path = movie_file_path.replace( + self.movie_file_extension, ".wav" + ) + # Makes sure sound file length will match video file + self.add_audio_segment(AudioSegment.silent(0)) + self.audio_segment.export(sound_file_path) + temp_file_path = movie_file_path.replace(".", "_temp.") + commands = commands = [ + "ffmpeg", + "-i", movie_file_path, + "-i", sound_file_path, + '-y', # overwrite output file if it exists + "-c:v", "copy", "-c:a", "aac", + '-loglevel', 'error', + "-shortest", + "-strict", "experimental", + temp_file_path, + ] + subprocess.call(commands) + shutil.move(temp_file_path, movie_file_path) + subprocess.call(["rm", sound_file_path]) + + self.print_file_ready_message(movie_file_path) + + def print_file_ready_message(self, file_path): + print("\nFile ready at {}\n".format(file_path)) diff --git a/manimlib/utils/output_directory_getters.py b/manimlib/utils/file_ops.py similarity index 61% rename from manimlib/utils/output_directory_getters.py rename to manimlib/utils/file_ops.py index 6f8cc674..c06bd148 100644 --- a/manimlib/utils/output_directory_getters.py +++ b/manimlib/utils/file_ops.py @@ -1,8 +1,6 @@ import os import numpy as np -from manimlib.constants import VIDEO_DIR - def add_extension_if_not_present(file_name, extension): # This could conceivably be smarter about handling existing differing extensions @@ -18,38 +16,38 @@ def guarantee_existance(path): return os.path.abspath(path) -def get_scene_output_directory(scene_class): - return guarantee_existance(os.path.join( - VIDEO_DIR, - scene_class.__module__.replace(".", os.path.sep) - )) +# def get_scene_output_directory(scene_class): +# return guarantee_existance(os.path.join( +# VIDEO_DIR, +# scene_class.__module__.replace(".", os.path.sep) +# )) -def get_movie_output_directory(scene_class, camera_config, frame_duration): - directory = get_scene_output_directory(scene_class) - sub_dir = "%dp%d" % ( - camera_config["pixel_height"], - int(1.0 / frame_duration) - ) - return guarantee_existance(os.path.join(directory, sub_dir)) +# def get_movie_output_directory(scene_class, camera_config, frame_duration): +# directory = get_scene_output_directory(scene_class) +# sub_dir = "%dp%d" % ( +# camera_config["pixel_height"], +# int(1.0 / frame_duration) +# ) +# return guarantee_existance(os.path.join(directory, sub_dir)) -def get_partial_movie_output_directory(scene, camera_config, frame_duration): - directory = get_movie_output_directory( - scene.__class__, camera_config, frame_duration - ) - return guarantee_existance( - os.path.join( - directory, - "partial_movie_files", - scene.get_output_file_name(), - ) - ) +# def get_partial_movie_output_directory(scene, camera_config, frame_duration): +# directory = get_movie_output_directory( +# scene.__class__, camera_config, frame_duration +# ) +# return guarantee_existance( +# os.path.join( +# directory, +# "partial_movie_files", +# scene.get_output_file_name(), +# ) +# ) -def get_image_output_directory(scene_class, sub_dir="images"): - directory = get_scene_output_directory(scene_class) - return guarantee_existance(os.path.join(directory, sub_dir)) +# def get_image_output_directory(scene_class, sub_dir="images"): +# directory = get_scene_output_directory(scene_class) +# return guarantee_existance(os.path.join(directory, sub_dir)) def get_sorted_integer_files(directory, diff --git a/stage_scenes.py b/stage_scenes.py index 7f894f9a..65e055aa 100644 --- a/stage_scenes.py +++ b/stage_scenes.py @@ -8,8 +8,7 @@ from manimlib.constants import PRODUCTION_QUALITY_CAMERA_CONFIG from manimlib.constants import PRODUCTION_QUALITY_FRAME_DURATION from manimlib.config import get_module from manimlib.extract_scene import is_child_scene -from manimlib.utils.output_directory_getters import get_movie_output_directory -from manimlib.utils.output_directory_getters import get_sorted_integer_files +from manimlib.utils.file_ops import get_movie_output_directory def get_sorted_scene_classes(module_name):