From 4416d9380ebf0d6716fae7d9a882960f97ee3d4d Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Fri, 12 Jun 2026 11:42:15 +0200 Subject: [PATCH] :sparkles: Add initial structure for performance tests --- performance/README.md | 102 ++++ performance/fixtures/test-large.png | Bin 0 -> 120303 bytes performance/fixtures/test-medium.png | Bin 0 -> 30173 bytes performance/fixtures/test-small.png | Bin 0 -> 97 bytes performance/lib/penpot-client.js | 525 +++++++++++++++++++ performance/run.sh | 160 ++++++ performance/scripts/lifecycle.js | 417 +++++++++++++++ plans/2026-06-12-backend-performance-test.md | 494 +++++++++++++++++ 8 files changed, 1698 insertions(+) create mode 100644 performance/README.md create mode 100644 performance/fixtures/test-large.png create mode 100644 performance/fixtures/test-medium.png create mode 100644 performance/fixtures/test-small.png create mode 100644 performance/lib/penpot-client.js create mode 100755 performance/run.sh create mode 100644 performance/scripts/lifecycle.js create mode 100644 plans/2026-06-12-backend-performance-test.md diff --git a/performance/README.md b/performance/README.md new file mode 100644 index 0000000000..ebcbbbb4b1 --- /dev/null +++ b/performance/README.md @@ -0,0 +1,102 @@ +# Penpot Performance Tests + +k6-based load and performance test suite for the Penpot backend. Measures HTTP RPC latency, throughput, and error rates under synthetic user load. + +## Prerequisites + +- **k6** — Install from https://k6.io/docs/get-started/installation/ +- **Running Penpot backend** — Local devenv (`http://localhost:6060`) or a remote instance + +## Quick Start + +```bash +# Smoke test — 1 VU, 1 iteration, demo mode +./run.sh smoke + +# Full lifecycle with 10 VUs, 5 iterations each +./run.sh lifecycle -v 10 -n 5 + +# Use registration flow instead of demo profiles +./run.sh lifecycle -m register -v 5 -n 1 + +# Point to a remote backend +./run.sh lifecycle -u https://penpot.example.com + +# Show all options +./run.sh help +``` + +## Test Scripts + +### `scripts/lifecycle.js` — Full User Lifecycle + +Simulates a realistic user journey from account creation through CRUD operations: + +1. **Register** — Create a new user (demo profile or full registration) +2. **Login** — Authenticate and obtain session cookie +3. **Get Profile** — Fetch current user profile +4. **Get Teams** — List user teams +5. **Create Project** — Create a new project in the default team +6. **Create File** — Create a new design file in the project +7. **Get File** — Fetch the file with its data (pages, objects) +8. **Update File** — Add a rectangle shape (tests optimistic concurrency) +9. **Upload Image** — Upload a PNG to the file's media objects +10. **Delete File** — Remove the file +11. **Delete Project** — Remove the project +12. **Logout** — End the session + +Each VU performs the full flow independently, creating and cleaning up its own artifacts. + +## Configuration + +Options for `run.sh lifecycle`: + +| Flag | Env Variable | Default | Description | +|------|-------------|---------|-------------| +| `-u URL` | `PENPOT_BASE_URL` | `http://localhost:6060` | Penpot backend URL | +| `-v NUM` | — | `1` | Number of virtual users | +| `-n NUM` | — | `1` | Iterations per VU | +| `-m MODE` | `PENPOT_REGISTER_MODE` | `demo` | `demo` or `register` | +| `-k PATH` | `K6` | `k6` | Path to k6 binary | + +### Register Modes + +- **`demo`** (default): Uses the `create-demo-profile` RPC endpoint. Requires the `demo-users` feature flag to be enabled on the backend. Fastest for testing. +- **`register`**: Uses the full two-step registration flow (`prepare-register-profile` + `register-profile`). Works without any feature flags but is slower. + +## Shared Client (`lib/penpot-client.js`) + +The shared client module wraps the Penpot backend RPC API using plain JSON (not Transit). Key features: + +- **JSON transport**: Uses `Content-Type: application/json` for POST bodies and `Accept: application/json` (or `_fmt=json` for GET) for responses. +- **Cookie-based auth**: k6 automatically manages session cookies per VU. +- **Session headers**: Generates `x-session-id` and `x-external-session-id` UUIDs per VU. +- **Tagged metrics**: Every request is tagged with `rpc_command` for k6 metric slicing. + +## Results + +Test results are written to `results//` as JSON. k6 also prints a summary to stdout with percentile breakdowns per RPC command. + +## Thresholds + +The lifecycle script includes built-in thresholds that will cause k6 to exit with a non-zero code if exceeded: + +- `http_req_duration p95 < 5000ms` (global) +- `http_req_failed < 1%` (global) +- Per-command thresholds for login, profile, project, file, and update operations + +## Adding New Flows + +To add a new test flow: + +1. Create `scripts/.js` +2. Import the shared client: `import { createClient } from "../lib/penpot-client.js";` +3. Implement the flow using the client methods +4. Add a command in `run.sh` + +## Architecture Notes + +- The backend supports both Transit JSON and plain JSON. This test suite uses **plain JSON** for simplicity (no Transit encoder needed in k6). +- JSON request keys are in **kebab-case** (matching Clojure conventions). JSON response keys are in **camelCase** (backend's default JSON encoding). +- `update-file` sends the `id` parameter both in the query string and in the POST body, matching the frontend's behavior. +- The backend uses optimistic concurrency control (`revn`) for file updates. The test retries once on conflict. diff --git a/performance/fixtures/test-large.png b/performance/fixtures/test-large.png new file mode 100644 index 0000000000000000000000000000000000000000..fbb82a92acd296db8f106228ebc5dc259fecca3b GIT binary patch literal 120303 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�PMS%w7o^8%HXOiE*p`qX@9*8zEb!XkU)4Yg znpgb0EgdhOAQ%ozuv=^x#;`Fv_0v+OJflqw6`s`$x{THgzZRLREb|A1KpBwrYYXAw zsX)bYQd9tfmpgZMVx${cB_3||^al<|dTpXdWvL9`S?``Iz49ApRml>EF~T#SbFPnY zn>kILv7zUZ*Ozx>b{vZcZx*d7Jh-!XuL0aZO}G(_Ab@(}WO;2OT4=+xtj?|;P(}m^ zzhRjDPUgKKY+jGbrY2y$GJ@VZRTesj6zFq|2?FNhCf6P_4{$i)d!F29Sunh~aeM8g zXEgn%L%=g$QGyE?5JaG5{HrQ-uM-zuO7e&X`@Z>wMRl<$6H(ruTsWFXm?g${j*|QD zU-;E!O#-pJDZB0hfVE(D1(a}ri#fST$MBVvaC-b{IfeyE3Umq?HUn`kBRiP}`>?<7 z-&Q2mZh@SWcOI&3nuYbOwF*mhN-@bPcj_yr4*PRS_&%M&a5lD0>gBSHBAAEy z^;-;&u5)tY#_2QOM(fN>i+#}<&2bYG?pSJWC-VDfy4G=CCO7Cb#7M;S;Bcrr7_{tc znVHhB0TPzwh3Qjw%reH(P88!q$;5Wr`sdnqz{PdGLGyY5&IJkQxV!~}1dTdmb{aAv z>!M6U0`La@_su_9ztkht+JzKX0YeQ+S6iBv#@#B@rZ4ii-OJHk&b9s;m~UoeS!IU| zJYUKu>Cb|@<>QxC<7=UY9O(_DneR^+MBRzTr%2Q5fblpd+tC*Kb~+ahDCP0+&BPZP zTTurLf83oyZ&gKqucKT@51%tsg{;kAH%kjvxP%RXHYi}us)e#qsGu?9$D`D6*PEpc zTF^`Oz57lcVlt9s1-Q6^ zVgITou55r@=NonG{AYFn4}mU&V^r0jnm+`GL}^F|5yrZ}Sz&U7_#$Dj zq4{uDqAP$ms@5avVJupYb4)wxPJ!~wI_!)Xbf;(&6sp9EKjlyQYl2LHbX~+_ z0yLu>Lqlg4O-d$mY3&bzq!n(_2>w)R^nkKSsSf;!3Nk#Q%Fe!W2*P*<_dD z*15$qwqEJ6I5~*w`%Q&mU$nmV>HEf9`Izme#1d3K{rd)yM%%iuIwHBqNg{v0E z8Jv6blbeqK$`eY)5xbojutPNoUjY8@|6>|1sC5ccaA{}8?&Tk_Nc=^#cbzjy~<}j#*a{{4q16fhh@^;oaOs%On>`Of>cy2es)Hj0?8!K zlgrl)hΠ)uF&o*d)pv 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

AVoL^oU%v6UiZ$X38>E^(TI*$(a_QLI5Xc&}4L zRc}5apI&n)D7l~a08>D$zvl#lbSPN}!4jZ#ubzEXjj<;}YwC(;CjsDGT?@69yO(my-C?GhO;3Q!*NcH-a7q_^@r ztf}`@6=8=Xb=IIH?Zp0d^i23yunx;%uy8$4Jh&zo4>ws|i`_79Dlq_Irw!=tD;+OY z9f7WO^SHosnxucGjmSFH#8_e&1YzTM$Yk!Y^b#YfAO`9Eb8XsQ(jb(Z%ZIhP<+E`j z9~MQ@+v5P9UGk&jB#R(gzOzFz&#-6xfQ{#X1R<)^)sR=?QMiMtR$xqw4n|-QB}Gz@ zW8pQR5;ho2ewaI+LL48O6h7kjg)v!YN0+@ZIRo0~4_cQXCDd}5>gVm$N%4!v;c5Zw zOFZ~lB}MlR9M6Htlv@9On!Do0VY};MF)S-?yOUg?!|;rw(3GEYJ!w-ozr`nK*I#gW zN)eUZ0hPt#m=W+qlNUGi_cZwnf+?d>`sX$!w^RJ22kvOuDtccMn;px4Q&(fgSTN}* zH7aZk^L5w8F3;*X;oRUN!dT71`Y?(h-B{4N{1qGHZc;c#=OsrgwLjwt&Q9uZH{0`Q zflP!+-1*cm<&OlVnX)-S2+%e_!SjFc1~!U_>Gw0cK>RXFw|8zQ`)$_Oh*3PDA}K`3 zA_E~sP4)Sq!Lwm(``hnyP}iMO-_+*fYz{1jL}#u>_=$Ru6Y3?;Nchd)g!Y}@4xz+= zJas4w&CSUcPW?%nsyt`4(gPYMg$Km@gWwdz?>Be)8?EuzQz5bLy`;qi!!0T2DBkzz^^Eic*@(p!F~-Y#RqFQzmE18Dt( zOFv8m*2NMT{_dybFS)s?#YSL}{?(q*Adsx-zXPm24xk^mzzJ?a$InErF&uX>x_=Ds z5qfk0Q;pL1_14$q0@;==92+46@K4_TS6Y#+vpcTk5@&>GkT2jT85?)6{1mHnaGj5R z`H}m-9@}cotZH7&Xux-XC?L8b~L)tO&$PvTUV zowpHjYMb`ikEC5-O`~S=^Osyu(&8Y(lY)D&K9aYR&qT)*rLT3lwX>}w%22 zt86ttBXS#RN-1A8+B4_YN475MX)_N?e{V2J-=;_~0373y^(F+if|U}CeH$IZ(bB0W zp=y7@PfB}xYsHP*vWTDDxJd`OnYkM8Y)riEEYV&a_ViTLLAqaSLKM%{O6Nqb5%~op zMH2>*7xTMPLQ3^PpQLgi{j2f!X^R`Pcgpq7;OPcn_F>}B^GFnD(c(UU&b<&VUt+BH zN?Bx!>=JOC7~W0507$%c%AcMkit_s5maUCGbix83VpR&@$B~sG^R=}?t92n=v!q6( z4h~`(?q>7R8Cmf19A$q*I<0HYY7}HZu-op6a>NUjczei3I^d!M-4vGXAqK~=BzG%5 zjZ@I&W(VYf$j4~Gm&Yd`=`vU6>4QG;*`}jHa3Rr3_YsyekvzH8L3w#;c{t7gTNn{| z=SQ9U9)di#EMyw`n5Vl-5d>wkRtmfH5oAM8)O!JG8~%6xA<>0Pjm|SmwE8|&U|~Gh z4rLl%zRr6nER_paFtckJ2ap_6>(eCe`ZHF4eM4}Kzk^0fX)E;qIL2Bzj8XH{x!tYc z3-sAdB7!~9Z=f7UAdz1x3&A};h9eipkff|%0}O&7L5hL5Kfp1a(5imz0Zni5cwKT< ze8S=3483TWOSK{X9zJ5CE>Jzczq3S#e*p16Xa+RSE)AX|HC!P=#XcDV**q9V$MJ~P z@Zbz%kz+OAcW#?_ySd_@53b>v`6~P3HlwT2mB4{`PECf$#dkI{rq=*uQO0M0WExD| zPzjJlfC@RWN+afKI2j`2dTCve^fIqguFUgy9$_@bX( z@*gj#l1)VX6uiIl=}-|Eq<7RB-HULbeOeGS@cPr|C{u}nCnLRa0XIzi9OGlf%v&1K zP5wZHj_3y4@P)bt=po%ocTcuN-m$u>}47g)OyI4?Gujm z9+pQIiZ@7=isIO9Wa;{P34-MmI(!6W=-dU0H!}S13aRPkZZirRSotG#*i~Jky!enL z1b<9HL?t_L+9J>05Ag?`D5U*Sfi2RmgR^SKB@?n{o&>samj04mY!x9H@pg)}&$lFU z4e(aR=$WN|bHGEQDB%xX_A;VhN9TC6 zTO>PXt@6q)o`#nT9;oX1n{XaI0o*V-m|V0&%JpBTLTT)&#_ZrP#K#=3zJbx zj_w47P?wz-9{5tU1MFv>v z{bGP^S%C=rhe*u?J>|3?Fz)oSj_0xva3I>m8lq&#kJL zlM|~2WcyPhXbqh&2$)0A2$9xM9RGHla7R@$yU2EzW%$=gvq5|?uaNScYge|W<+Y%B zb-54Uuz@6C5AlqEe?j;|0bfR6KS_A!1a(hF@*Z5?4;{2eAJ{q%lQ#^-{qpRrU`Kq( zV)yWA&X-C%;;ZpeRtbo5$}QlQUu+xSEh-x|@|mNkL*}Dp{R9kvI1yN8aK%xP6-e0L;r+Jp*x-KXXz*!oLxBo8JNDzdbT1;6-&lK2a7EB-O}iT9KX@?CsLy=Qp$ zf6Gc`45{PIO&RP}#n5~{yR86NGu}wOaks~9hx?FUM1p9Q9cF?^#Zb|4or0Nb1AsXR zy`8Mb1tW9l`&P&QqTbF7`(vK1>pu(aHR=n|w51bR+MDx4L}w?3)0R#bO$Ox3xB1-4 z>9Q$lnRJ(m?c%~#cFV=ke&pIcT>^!){QlAQfgORe56V@tD?co;zuT``Na;Ue&q;&@ zh~4(^aAkR|8%^!SP{n6Ahb?rX5n|?`DVZc%2qGm6#r*ko)uUN>=$`G=K0B+gkn~}j z1OYfEAGSy?Rfh`o*wgmGGsnY}v}b%~>2zp57vP%U%nV{I6!7EfVZ45L(T6CCo-S1O zz4ZrK>PH=9sZ+WnPXOq*uAj{R90ii|wd)o#Lka2-VvI9A$YjWu^Aiuw-^&r@%mnIk774&3H0f?ZUg$@Wkk6 z?zzMerO@B9Nj$@EoAbem3_lvEJ1>z(z?(k;P)ZrQdZeZE3UaYTz*yydT;j?I-{}&| zO1lCn@m0<;FdwGh9>95AeMV#4`+Cf-f*2|EZ9O9`R|`n(R$DsBdm{RmFmgfWd~%Km ztIB<&l;2)|N5tDlgH74MatP&(L&ex=Yb#kx4Fq0s@f^_#WF(Ry>V1F5IZ=0SXD)-U zkK(f_tWbNd;7zg$!|5h-NJMbv2ncyKdY;Iim2?FvsC03wa$rQ*xD*CFStOlt#+j-g zei|Ld?jmQA(GY-r@dC_N`^r$M_uMYMDSZ2xvC|%SC4Y zzI~cR4ye-43+ZyVSIXw1#9axv zxP7XID82qrc>RT5fbEb>rZJ)Wp4+A^m)O6-Y&gmfXkLW(n>GAE+;g@>Mg>xBF`r@+ zO|ncB1wap!Qq&jpSfP1^!TkW^s1n{J#3Fw@O+M=^-d@Pq&hv>!Qh9Wfo1e@hF6*M2 zyy_w<;RTk1o+P?r#Xf_pByS~kO)xmY@)VjixXCvK9r;1sAMV1mB-fQ4*^?0(tdo|p zG(=bSM}<))IN05h^HYj`Pobp!nY%Y|Y5%lka)P^GYAyOX%Ynq;u-!#np6SpK@kOZc z;s=;)p+kTgibfexmAxkYhVMeNV`VIHRFTs_>5tZ_o#FU=?KgK>_qQ&r?M z1+}1u`x+e7_;~_H)j|xg(SCQxgc#@0OH$%1QCiY{J_^t2SihMU;rxLm)J+ABZc{Er z?D%gv>Ft+R_J?5f@KK6J?6N6~48x}#sRX@kH`_EgOW`cE){32`sRgz$8K?gDzQJ)Z}0_g*}5z)T1>#sT^7ZTx>6; zRWkej1~QrL+QqwJu)qxCEJ}*%aXQYGmG)6x9nnh>I{5X_L?i0c6s8X0IzTdVr;+?G?v@?IkP4zUilh(QC62A zdB78#W95mkz2(Z^ewzs^LjI|YQ2OD#o9ACw?HVdgg23&yq4&gTeeyjdW}`gq59v77 zpc^>v8ED|jB5$WtlTI|-jHr8rp|6-z?L>}?;IU`rL=i2JOI2m}T)V^4qy)m%4LvK( zu&m6rFe$MPd8N9&G#nb#%n!EM_w`>jVBZN8i)|4M*?o8XMts;h2LI<)Au_~e132BO zF4h$fiuPVKr)8<}5DIFk<|+6dUj-Vor@99><^~<6UxrC@L$$GoXJms{YgsB-nq>n> zsb_u&Z_FinMMwMC2ZHy8nErJJG(8XF2Km;)_MEM0Wt3rjv+I~}=@cU+7iDG4Drm@T z_NNTj3!olwe7`X{%6RpYwdQ!!RPihBLQD| zhXvEL*6+8%jI}P}AZrtGQc9JWSXDM`m1u84FMM?q9~qQXslWhX4nk!Bt@~XKRt)F1 zP5>DSSf;kgY%1LY-coWqh@O{kC*9R?-jI% zj=?hKa-nn09M+PQ{{h(godO_G#Sc#D8w=$jHx1jD8%kqJw#x_~GLe~|#;ArRB3zAG zNzxwKDt}x(@=>5kC(_*GNo>&frQGP!y_Cyx=JB$t<2WhyLy$V-3EgFH^y!7;PSWCR zRK+-V)gi2L%$*qb$V3-fPY(8-q|scCEqzIkuQx(^C6X$NRw=VfuY#{UfN-2|ORygt z!$1*`G?HR>J` zx>J*&$p#ECQSm+YR}+&6P~#DbWG=H;J1_b^@zq_h%`Uvqd)Um2#jOdErNG7l)KRl^ z#rd9BBJd|AXVQ1GlVLn}d^|5m`WoMoqn`C>3XbwE33?Q3bMrgTB&z^Y?9AXA+k=n; zYY(IL;n^&d2Eg>K^%jt7 zEE@&OZZ`p;v<~dfKY{{=+wVEv9`<{4N;Vx`8y7b_jz&AfvI~#wVsOQS$OYFq2ig*G zcTY=eR9$+%Z0e$>{zdK+szag&OK(apHw{?7pU?(sfh1%hk-LjV-W*=$0q+Q7Bo zo_x*j3EUz5w*&!iCzu?AXaOy?Xd|=(1-NUyVVb0tUa6?dBaI;9ns4XF4l-q5xAZ*3 zwRXEqh0tNz8iih`3WNhAd!Q(kcwdj5x&EJbpIxD~CQwh#sz`BCG#TDb!?8s&6%^`n ztPm$>U9{{Ao02PpNlcDw30qFFu-#{Rp;9Gm(@T3m;Xlr0Gtj?PWM@3tLbRcISPuniacu zW`O`P)sE}9AJq_bW&b0>w4)>nLL-ySrG+(VSXB$iBD7bc4=5<$_Tr!lneGq;%w0#N z7AEKXsweJ{6I*3*(FmQ7_zCR^*4-Mn{KBL`@o9b}Fj1yaM=Rw7INeW`f{Cys%F-%s z`UiZ450LYvb3URLN9mP&d^F`Bm|W15|N98@3U8IH!C54>x2Z+= zgHAZiG`9G)n>q)8rH2{bq?-OqOPL(T+Ri06;~fUzi3SGLE)-4>VLHFP-RpV|KSudZU5A-FR)C$RFmDB*{Obr={OCW7`^^@ufQhlwOHOq=m`9Yi50|^e^Sz zxMGTm3ySCMOYrAT3`hRzC71vVk-&o*{xkA;-8ih~uS|-%QaZ<#R1q&Y8CIr&qeLnx zMmf5l*nMDrHu8Z>;MW&3@9z!38mM(-?;k&S!gSEBAcStvfZSfg9I&6RTqYD%%JM&7 zql^WY{nQ08k2O8&O-!vkS*G@! zJhEzAo!uJ2bT_&nA|)FJPxoiCG?LVj?tPcos;8Axwdn*E&0^+WtCv$Z63?;C2f=jsUklr!qq z@p5k7aGI$5UJpYWX`)j;ADS^dgw-v4bbKXj++~wRZly$yWcfK3jxz1bU#c|&e*+}a zj1`CtbnnbUJ2(p85{-Q*H=9c%IaUC`OM#Ohp$a3_)|Du(3B3KO#VjRtfpI6Ta$a#T zgmy$vP-?r)e2w%~+f|C=HFq%AqE+MKWdid>fdzNUk`xA1wUY$giNomFyCM5Z_y3@9 zP&q?r#HOm=m4r8iWC9N*yZ~=qf(CgQR7P^1XT^(|y~NpO3l_oJpc|5k3CPfzpU}J_ zsqHZMO?DXuMrV#dh?l9S74x1~ND5^6jEN5#1t9^b-S_eta@~W#o0>#UI?m8m-TCNu zmZRNZxbi?jW-FmU&Jnokk|Ut%qdg1~C%m1V>$o6Wg}(cE^obK4O9FCM{TkxLBK3Go zM86$1ytwCeQCmu0d_~O?F6g(CNy%wd9wuz{(-_UEomaUO9T+3(4CFRzl5nCDOJSJ6wz&mj!B{G4<5~`8sC*^!0AR zmz)?m7ce+MQ^LdKdrrhKJpjhzigIb>@^P4(CcXqf@9VjGZ?l6Ip?qRih<;?J3CWb3d~XU;rHKD6TJi$P!GNB+(D%Ss%eJ97OZ6Kt zG-%fx%5(k(q^zB`(X2JAi!F{?aMmWAp$eQob5~EbAczqw1UEhmuo0S_S2Wg~q7gR{ z%X6u=Gy6%*VU>%vqt(7ql>F62p@FvlL`)dH7CkG(3kyfvn&0#4{oOuV7(?#Oe&OV< zvAE)xb~Rwc!{aY5%v85$t6N2M%T+1!X78?l=tK0iGHxfQ!buD9C24^3?yQ*_X>3UT@Hu&|IY%z|84S1Ivi(odT}C2y*kG20zgNrMmhSj`idqLw5CA@OioV&9~lx zP1CV#AE1%(DgNMrG1%DokdDbbtTf#R2nK?YXu5Q-HUJpa@e$R^v7 zxKH(sPAf=0eP$k)dmKF+`?O%_m^N9}2@F!$;E1oqC4)i_#7&mzLJv|&vSZt_qD)T< zpkR_8cLZ6qG_dYF$RMql(dtcM2a}sNyA$IhCu(M}B8A_6xRmV7yp5%q>d{*O*>;h~ z_-yG2UN`i}J@kZTM%SAv_EBEAxjmJ#s0%+_2QLLT99O}86b3X8<#&sIoB(VR*Z}AA zA?#ZC7-5B=9KKc-SAc=ck!tajTui3>`*8%GLp6+~Xi{W)RxWN5BX7i0Ma%A3ey`Uv zAd_O!GLZYH#zy5#ezvV^B!iWm#}%1otH@@N`ZC-dHu35#9o@*gg%rxU4H`;iz0R8| zm=W>lIDtti3!?jMys5JgFKY z(hIBxd8ZKu|IZ^af(z~3=L&Y3?Ftb?%3=4J(U8DRMX?s;h3ei_FMMvJbp|HMg5n?I zAwjwKr#&?4CxMp?Vv<|wqtD`JIW`zS?!?6bUEwZ6)F!Zv61&YR0(EDYJ{=x>VV|yR zU^w%hLP;;(xiKra^e$z_31!$gH9{9pVvZz3r+pJW()RRxpIK zpYIlG9_}~{sSk=8D$ndvd~MBrzB3l>Z@(a-Z z5LtKDstFZ;APS-BQKE`SUJ1drKpW`)mB$!dwa|9+FD=F!;7sCtSiQdd?DL3%0Jv0K z)hs~ZOVI(Qm4w{PnSNolRvuhBlF)^&cf_jM!5LfvZ|~h zhTOPyA&F)kuys*1c{aI!7pKC!hH7>e+5a~+{E=9?D%$R-(!ugG=ej_d7wNOWH-2@D z@P)b+7?_+Ow;Tl95@Q7aoqE7`jm#>g+oSSgl@Ei-e}^Py^9bVb4s~G~ERP{MOvW7C z7RFU6^5NEt%>N3n{+Lz(un->;1QH-k)&k64A3Qz;#PtFc8=X^qOwrSFkFw34trCY3 z^_EMsNI(ZwAatif4e`^4f3I#$yVpKv@Dx9<==GeRE7#afJCdfHiS;Pja@R8aq`+6q zAcQ-+s1GcctLX;@kO(mNVRY)NE;LuyCv`n*t*GS^F4qn*+AO`8Rq-%xO9z;1`P_*Qbnf51$slO;BUzWKQ80CLd} zyBO-4pfNwHTbAr5;VTIf3}pZseXd0FrZVkV$#FrCocwy(6BSFFytiH?m(DZpRqzig zJ8S(wI`~6<&H>)S*o%PtlYA`aV`W4_4$xVS5A9Ovk#DQI#B4$OhkcYr8mS0Vk{klG z6>E{Q9c1Tsz6M*wSg%srBJsW|yMio5TjvlQNg{9CEpAt8Pj2Inl&COLO7GVJ=GNN- z_F!{Q5L@y-#?4Em@&EE!+XIi9u#2GgQGxwC0MB%FX?p#ip+ex#yrDh<1WO ztc^&c1iBz7xSWUPzXX@qy>5e7u9;3v3v^D`0ax{u45jJW=4EQIQxY@i$}TG!-cs(Hv zh`p}qVsbM}fC)8{`_IM8jfdPUsWuHdnWI+XH0fsqj9@Y&VdJF(VI=|5HTH9^u=+mq z^x{xD+ryI{D92l*uVjL_iRki24`cmLq_W#K$Y%bb2sWHQ7gqibddrQ^wDZd%7Pv?M zSYiLLo@Az+@%9`%qgdPp`j^FStYG+vhB<;!`iDWNG`PZ~ znW;_oZ|YtPggW15h*gL^qY0EA1;z@>{09BZK#31QZf+V6UGQ&qq)uC+{0vCU{!k)S zwE8Bv=lga(^{9K#bmPMnaLGH5deKx-U~1AOtl>BFY6Ti&4!pvifGMQT_?%4WqH1C> zdFd$%m+gQEyA2hzu@i)}6QQdk3RM6KK zXhz~|(B8yb3LyMi8YW-F zh!U{Xm1Ri;(y4SjN``t`<{8JKo2=(~hNz1?Z$)?C!z9&Ryj^k-BJ6KshMB5L2Y|Lq zk4hf3Rnz-t2Is*#e?9ZWbgh&X3|B@3QQqm+xHM4o0HFE~QTJgAcU>ME1QTxtEjS+; z8CX_10cG|zt2&f5?o!#ik{l?>T98MzyS#Ds8T2ipno(IAxMoIl@F2hTsGow*ZCr;< zmB`N>pZHvZh)!M%3kNopaxxSm-XG!(-0K&DeFajBH*T^J2EB+`XmGRtktVi1K6rpy zIk_s2;(G+9a_u&-tNl#(#+ouT*g;>$I%Ua7g?E)aEA3^YbxpXOhz_fxiN$$GEM4OY z1;_wL+t)A^nV9j`6Z-tcP}BdFZ+2y`Poo{3%Tl&gwWJSzIj3=}ZXshOY_>uonyUA$ zCgzBEAYRfzDCK@Nn7Zd&+Ps=yrP;{=Jci9QJ=g#`3HX?-QMH@SJg618r#LSe31bJ% zJF%DaFCU}r?jL^aHEB34op~PT=JkE4>Z6v ztYpZ8j&UKwRB;Cle*+JlvsV^TXP^PWbs{X3&t=m6(1M4IAxt?U_Zq`XNjU$VK+^$a zO$P&P3sE|&o<+2uPJT2vNIDR!{01vug^x{T%L7QG)cSZ^RzBx6RDWL@rKy|%V~=&f z)i#eDZVt%NzRP6_4HJI$qp8&9#3^@(T|#zZjEZ;)fb%Y$SolN*u_=sy;uuo6WFZzG)n)qYHyPY2_r(_E$I26l)DM|xysrNmiqo19V}tFMRvXrTI4xU6el6x!G(Xu*%3}wrO7e_AL`EP7r!caa5 z3v;Z9^FlqTy)&d(HDl07Z4N2yNXrQJ+$}(jK^dL^geI!VMIP}__m++vJz2%sTy(?c zfE?7k(Gw3;Td(^Ja+qSVzif&;^T!_b`^ocs)<3sj7M;L-k^AHCgEg_4+hlK=m_8CL zS`J$wn`VSc8{>o>BCELSR2{L!phZ{dC`Q%^?Y2#IkbYL&IUAkb-iTjDwtz3rrjCp#twF45lvH#|={nfEe?d6y72 zF0GLcXJoF~93SfxsGw*F+eG{1D%R3v9ysIu*jmpUflBqAy?yY$&DB)O9nMJ!ojm5h zZxwlRt{M$)=cyl(g-&HGdB`R0aQX3n##4 zlR7F2tqMf!RjJ_VSlue3o_xkRQ~qJIw@Z{`m`+IpspY~}Y{gf3D$zB6tpQ1%ucBJ4 z$mALn$Z50nNJ|#5oG*IsSX(h#;3~|Kb~w$e9v;$%CMVhvR2v{xN;XEdo%>FC>z|Tz z(FZ|QtQR$Jzwa(?@Q7SCn<%323%^4?ABfHZuT#%GI1HGS_|rF%h-Q}tu^%XQ)H9sD z=%ItTR&b%c??9ot0kgVGk0UYx`7EZK1y)#Nl+nyp=25$raEIt5L42sUIII3s;W$9sO!n9$nQ&-Z4HKel=-T~;Xv zf?~}OqShRVdDEMMgTjTHMj<8#(&7gexM~x1SJ5r4FzhrCg9@CD+l9?_O-P;-A)SNx z7p?qorXQ+_065%yHBsZX69CguG^=3@2c`_w_mV&49A|eBdwBP^HEsYiMJ?+Kk&&hB zBOz&mIv-vQ-#o@Ls%+FJ9n}90@5;gU174;+Li087_Iyywr~M=zutAg{(b;z;w!T23 z)at?b-~ptWJ}KD3FD$oelM13rn@8Ew+~m^Q1sd;JJj@1#I(+@viWQXh6>E5$Fadfa zGbO#Y`1TtkaR%|63S5A(!3UIfO$)20 zq?5uJ#oZs}-qDEoE=!ARl+SR6rS=DenXR z$ksBsg0}?PHQ^c!DQ?rCBTHTcAlGNO5Lq`HmLgU^t`sof4ELaz?uOjDNihglAFWr5)ni0H4rOSc} z#SE^>ooYq}8LR~(tN&a7rUBf3(^pPfQm6*7eaNF>FG6IYh=)!ehcAnljoeTciC>+p z|L@8wkrhlY}R`ov02th}9*Q~t8Xy1e|}fHrvy0pKqi z*h_G$ILU1{UWbjvlcdbVs?F2|UJm@)NROZ>c^=R?Nb7Tzs`j3kCX<*{-=vMBa^V!b z@w1DUDyi(9iaxFeSV&N)BEI0jD<;CBv;sMvAK}9-7(~22a5LNm7Cbd7eZ6 ziRPE;q*qiRy{spp&tI5#`bVKf5sPUNFwY&6N7Qxwz4cw3$GNWR(ht1|r0}}&xMWi* zHi8tekATy+SSe$;8oOl~`bS0j6hXi}Lcc-b z`_BS^7hUE!YzI398H=+^uZ0r?Y`QsYjiLO+k(uJ?PsJoE&nVJ_$HU2PUf$etDMjJy z!%UUEo1-kr++Esoui>SamZ(^n53z_j(`05nhg&+G?^}5gVNm87rmC5a1XlQ$FIa$b zPXnn@5Az3wOvT?ZeZue5W1YD}rb?j$YA{R zVclgrI=RBKxSJ;G9ibUUPsRYQ1Q=*2S8BhJcHY-eqjbRgIcrO9sAU36B(AKoUr%j!HmxWT zwlAv{_x3Vb1KN+|=nFUcCb5u)qZ__| z#Xx%82jbOJ-E`xT3B#t}fYZ80BB=LDlc~eWrjOW=ZSZdb;Q|1U%hA;yxl=^aG)0;C zIb;i|QUS_|4In7y2g}`cqL(%cQY=1)4lJOcG|l>BZg%Mam1CpOd0zAI7;VoZa7_HJ zMxyW9@v>ach=1Vz`>aN+n?f_|EyZit|UU zxy9EtUR9VGOy@)5loP?9{}<#Df|M!H3-)0MP4&6{gJDSpw-*l+h;PTN+W`8kjMFN2wIld0QZYjl0&^ZYrM9`2&q8F}gk0)~%prMJqBlMU%;Z z80H!O8}0mf3wc#qTuMCyMm)1jWQm+U>Nj{mQ>o=H;O8&370jGB=>Bs;3~$N?^*fa4 z6KLm$j=%EI?%r)YkUeTMSZiNebea4T6&bV-+u%C5z|U*Mr`#VP68mX}Ul@(J1-Sfd zLD9-(9=Fs@w&F`dX!Hy)iJwt`tZXma1Cz!SH(+Q4{p?rBMoa0k8<)3tOGlc$A4jL_ z&%a{yPq8!(*WZ{d?N+6|F1)98kFjTsu~urkRydZ@KPM<1ys_+$URQpNHGN8)Qqxpy zz@j>`)1{5J&;xU_&ciO5=eJvE98`hYL63}PtMkj3D+&sQmQ(TJOeNwx@;V{)G=%T!pEqrbe zH@qSvbPp3L6skElfr4#W|O);%RJSe62p6(mXZMw~hP zv=2?{WqcvyhO(Qr(_HVi;qvC~WL6}_=V)l-OTwYuX6IHLACsXW$-TB9rz7x6;dXGX z8(%t{Q#1HPEJASNMooi5NMx3SNFCrkSxR~jP_1nS0q{<9OOcF;gN`1pGiBwFft~wAaa`cADtUrRr!Ww z1RHfzyu5S6b11Rwc)rE)d6U2V89L7W3Udy;0Hx;GwP`qG6qRxz|9)LT&8W+Bxx~<=feN+?Ik7;7JOw=1?S6Ly7p}>#l753 zq$T zDA(3sIWc+qy4#TFSrNhh?p0=xPAOQ+x|0bXCJu84Gg~l4gOtu{W*%1LbgoYNoB$VQ zbWtUDO==a=g%lm5E~mw4aJxTw0C${66(V6^8EBqNR;w^mlyX45#H|OCmeH2DxYq-1 zW6ZOUqJ2NE^qqZ~@AbXbmVHn2Xk<>p9FBWf9@soCPCe)Zif!`Ovkj1DVC+MHHs_rd z;Zyh0GWI#vlS&)-fphPnCF}G1oI|@j7A@Va_v0rb$@GQ$=&o)k#7pn>72LwTvn#BU z9)3+s-b|I~s(aAV&jeGQ(KC~~qKm#IIr*=mylzb|vf&80*PO#fwa(yPZ2_mGjh1lI znU8byoc9Lu+h~e1+xR`Z;LAyx56gdUtS#3_&xG)@F@!__tF=~*8PRdhLc7g8Au zE{>rPC*4Jmn4JI(5OnWBLYwMRnoOH=8??;T3ZoxLXB`gA?pTt^W~r>M!+}^z$N)O! zz3c%yF9H>h>X{a`EWVIJz9U{8U++_=J|&Gd+^!SOv*KYlby%$}sJ}+;heOEH^Y1$r zSYu$pOynQy4E~<`P#dpAE&c7Z5yf>${_4wsU_Uohk{8ATfPKu=rThtzJ2kv?r`Ui% zbS24yT0Y-=oXaQ#EXx|nA(9ttrzx}MaT5UYmD0LH zj&_-1D=D762+f|McWs%c&`pg7G;^)phS!n?w?n)a4qNOIFuRG_J-D>01_z>69O z0>|=^?=U1y5jP??-b!B>vcAZVI|6p;1oab{NLVljsM8`+GxoAoxq$c|;cL2-i|9eV zGDAH>3edTv0tfXIHJWSzyqn`I&25f^J);0Nb;dDIoe*c)4L@J-TO-{yVwVsYEASVd<;{{627fQW7{Co;NYO4|frVpo z=AE(D-Bk8-g7+5VN08lJsr%a3LmsTF55f0GLw3dlc&t}pY$?`8_4N-%{eoCud^KSx z>M%xoAKtUEVr8^7<(a{k$DZ8#HIeBxn{>V|)jRrDhytN~fa>N-V<2;MXTUO%Zj_?i zn#BDlFRoy|^>YVkYJ(xd1)IfH8TJvb6DN*G^r5;}&v?6q8>;>eDu2>{SA=tmsO&7h z+0rPLL#`c#&?wC|^e1jz`#FvKH!GvZdk5AT(B3O!FVnn6d8IVMC`4h9+B+vj z$!KX}6BFIBNKJJD1Bu0Pnkm({qS!3oDToz7AXg1%23hNG+h)V9)A7H~#I%E45T{O4 z=Ar|elh>rf<`Fs7Nw%9L&$pN$kCZvX!U?flwc&7#tp;usX-I{M9t0txgHTf`nq^UI z0`ZLPle1um13w(@lo(6C{9(W9Qy!Yh(c_lYmw!aIH^86wgI@d&CGJpisp%Qb-qE(B zONY2}5Pp%`+U98tfg1xFy==g;-|%MtUtQDt#Uw_Fi#VS_ymofi30B<;{`H~#`DMR~ zmC}2PS-o|$@t)dG7~0UTPWp4G!lkxP7aA!4&Bw{x|39SBW(nZBMy}@S@5I6w5eZl2 zYA|~4M%Jk$M5Umv@JGKweuo%bD?J{~l55)F>XC!!?vhg$vJ-7IS@(FsSvE7<_3bJT z%gK!Z<$fGNWJm2Z@U>a--2h|{44<$uSK>6(>MzD7e)bbIJ~0d9XS1?a{DZtbN#Y;@ zy>Dop|C`*Y$MBtwt-JsvL@a(ixYDk)Iq?}37?Soqu1 z!(E$9T4(y-&2Z&^Epigu*Y`9?X!+5Aa%N)NhPIzg<9)e=GStg3j`J}MQ{78JpRyi_ z@Ju(au)ujptV_SULLf+!9!YFEfD3>a?szP8XE#o()B(hMf_c=gmAZtPAjhZs*( zWUTFn=$MHS1u+df%Xt0B>hWy!*VMK zHWljLPI&vjvD)l7S8o?4-~OF*5l%Hh4&TmrSKsG%0KSubPQaMbia$Wd*MAPI(r{Ew zcW@x}@%?t)mw9WVg~}}Qudnl?{}{u;!ND8HwGH>Z$JT4eS#x8FCKn{{vI2~~)Ycns zq1s$x3{I`3zV?>7M8uOla=9%{LT)UXF#&K$|Yw|BLL6w{^u9hi5rP zAvQjkz7W_o;M#wIj|Z_#e-q6@FvhE{|8F(TmMHKM1!}k zliu)H+$@JkiJfeMGv~VfQf?W^B1h7YjKs3JFYa#fM1PS)cS~nas?n~2-NI$|&`$A; zPN9!w^?kiNAhQ61jQiTeQulN2LWa;`qf1;l(R6bBxM0sJh-g>Hy>IZ?PL&%ih&xtO zrIP3he1l>vUd*0UQZ49!l83j-_ijKjHb*$fBJN)g9wi1wUhgcv4r1+kMB7F@lboWtE4;{=v1hJzUTv0{9Z~qo$10t zQD2W)2W2uAliEGwvRl=lZ{?##Jjul9OyD`D3@e}5f3X|~y7VXBX?a{!FD1rTiE0Zl zF91S7y}ub_$*UJ=62`*Mtgim8GK52BE1e4m&d<|(Mnbzd-vU8QYV`bvv>myEv{g<+ zfyV5gQ2<0hyT3>Uxv*CI@UlGsb_09(bxrYAEtwx(Oy3Yalgoajs0hm2*`kpb_d9#+ z4Xf}tJ$6*%Rd=J=(p0AFY+fS5l8~~3JVSP=U;TlVq(J1reD z+kwXwNJlwV<44G}>#Mq0e_Q(8{CQ~kDLvMcs0n2e%BcqgcJdeQ|o5WiSJU*V-@ji&du)b5_0Z_a8^T%-$fNtDay^gsV| zKtRj9^eHHpDEDfY{ps$VO~RqT?5KH=sP2sG-5s%LydO)v7E08UDLC(em}4sf&{CnO~ZQ9l{79$ln=BUA*PlGWg~ zsD8`JtcA3tRU!nSt(S-1c$oJfaa^hq$qhCbpQc*$)GP#8hEMm1ZHy>~K#6ar34}P6 zvv$j6D!W#XdyejM`9q`T<75fncfYyAA`~6Qc$`wN^6=nOz+12S)K5eIQ;sf1nWedO z<5!PP6C702uulX36Zh&PFRr0fOY3R5%So%ZFW0G_VusM{9s6}@FfL6mH3>Kn04#SD zxkv}pW3K&1i++}g5P=U{E#~7|9_o;p_d-YV-h_T3Iq_UocI2i#MCJs7r?F-J^i9Au z-S;KTXSJS&#}%2ncofaNdGJNGC6Tj1P%2N^lH`Ev!9w@Y9uGiHNp`Jnfd(fYu` zqnSz{%)!W!upZzN{bV{IoQYE;4N(9SF3R$goNK}@bEajbL6-*Hu4*l3t4D#&UW?%r zUn2J99YLvQdfNWyGYDKm1j)Kmx*+k8HHf{tR<>c7)dZ?CFrY?p(l>eg;Guh%U4yhL zOSXT)u-J)ZVmdsDzi&t#{q!GXx{U+fex)(M4xcW7x`ALCWZxgyroSvmJVPUk9r?!3 z{eKXZ2IX3uaAVCs&H&kFA6dD1#=}gJ9K~yPlHS=NH~NyebmGqJ=F*#yYIZGEppE*! z!U+os=yLGNR$P%joh3CJOP~n+q+sA}dZq$h2Ho^dXG7>Ug4D+P!SE=UeaoHl_GBSX zh`_x2)e%LDFhQNs3sbwpEZ*6g1S$E59Thup3|{whWKtj6;4Gcm^&es{L((gMNIsAK zuT-&InseG?UP)CX_8qbq;;QQei1&&iQNf5o;O82=cuCjA)a@TQ)WtS4i0{HiMOG|@ z=qNO|Lea~zU}g=-;pcFrk%RN9wb(|dgi!DJKdYc8-0%4m2q24(x7?Nza=p&r**hh% z)sY;c{A{0FQ(U)T$T~mcem;P78WC6-%_nZ#Jwkx%GJX7Q`c8ls5Js4LPlE%m5{N7^ ze;6bvcJl5_k0a%RT|(2Mfv-6mHUK~%IskBIi4^7_hg;jrepvgUV2ARgx_YLs{pH_$ zQF!0lnONzCPfw4ZE#tFl+4t_baMMOT14#IEs~{fX9!!?_z0vdn@b?m4B8K*axSmVw0k&cFG>2jt4`X7Z_qzFXt$+{b&?&X zi73rprnnp={muCFOzCso4!Sib0A{spws@yRr5_p%-D4{i5YBvMC(p;RbLVfO87>fO z6$Q*NtMX<8KU|rPK2Um&SWQ@$>?*RRJ)R^CZM)# z6V~p4$b2W2uTN&%K{W(n(I&>LiE@{LJwo1dQ`TK}^wnsIr^i#>XuWExKvnAMh7vI>XbM@+;bDdbiE5RD3ooe7FOA`PxTU{<#7V#;`uljHhmPn;I4`@3It~| z)5lLIXj)`F&ABUtLx5~cj8RUV1lg9UX?L8sQ<>(5HtlDs5}z(u(0W) z|Hbtu#dDj8|Lf#{hg){F*2&Qek8v4CF7$k&4_{Ir<;Og?7>hf3;Bh$o>dl@J$I@%29NvWKHc+l z0=LCk!CP}&h%B}(dyy3??2PO*Xx)S-L8OU1t_eBJiuLkm^TMDs=4!P^xk>Am=LL_# zsOK)~bK&YDxai_rb^*u&DOw0&R@iF4pmtZP`2Gh*L?TFsg43Tg**L<(iLIY%5A=*J zZakN@ghER3hiilP%}&`dB;h@I$QENkBi`FjLXIWFzR&Yz+XkoKA_T)!KkKO0U)oD) za&UejOPtpC!2kYZ;0ghoYmUD>yumbULscMFk+_UwR19EFt8LdepN??^uq33ZUFG(0 zl^>JQ*0daw-hEkHbym~V6T!%5Dz;JhGUMXY=#$;YRuf{Z(!w8yi^Kc9vKbkI^V3by zM7rhq&^^FP&nHMC0p>RO%T7D8%A~;xP*>S>kMcH*Iw^O`d?@_m-CV{3F?G1}jX&xL z_8%jJy9Cj=0+Gt2@~FI`Tf*Pp%7P-Nx9SHY8tRX+C+vu8CJ%uY2C2+xn1-owOi(QR zge|Tt&eAaYx!hnYY0->k%3e17!iE}Sc1rI+;lCk~OE7|{lHnlP&9<{k6v<-*;G zL?z@81sX7n?6Bs`Qy>gC(R#@fR|C;N*xMO{i~;@tv*sV&JHIi()>#;xZ09bbFqa_- zU&f?Jx#YLl66058hnX0I^=yj;q3I&-uIR65h0bP@WFWj-1z)-uLr1AB)>SIW>0jf( z%#0)g86-q^hl^An`FA1QDr;F4e%hs{p;mXm*I3YmX65H4qUm=27i08C$V5NxRcR)Po#vnK$9hPp@Lj;HNU3KJ^G%Gb+H-4f!H_h^A z;)VU(T2~fXFsg~1LVS0KQ z9J|j*fv2pjWGosIh)%i7p|~)8^7vBvrGd36kaYt}Vv@2?_>0ZhS2fY|qP3{x)dHyU zJLkr21Ww0{5kN4Hy%WxgtJ`)j`a1GkVX-;i3cbc3stBsJFq!E!PhPz`tMsmK6+5_! zA44!7gEKc50Pa_1$x)-35S0jLf8l9a#1REr;#_CE&Zy4TyqDQV9zw!z!mJbq27xZn zww@r=u{$c=WM?$(eRCMid-wd}8kt|SKCf9Dc@5RN*O$EfoI0PSHv4bNx;DSDk$RP^ zS_V!weU?oeSdZ6lWqDj3UGHqAc7r}P;&|aD?3}pzuf$rX@`__?FgVnY<;)@-t6B+I)W(N$pS~PUfGTbp(XgLMI$rvySGa>kjPCM{;xFS^U>Fz z6B^?}4{KFUuf60>P^0)XNIIa>G8$q;rlB0sCf_$!#(N# zkDP+MEcOy`A7{t-a(yN?s)1-0I(8s@kMxpULi~$vr%IYTlyr&-I}Dr!=bVWgIa}-4 zcIL=fP@)c)Mv5bxwOuNLU;`eB571!Mo#CXfEXl3GIDrdbr;COR&khQ#Y+knTm(2Vl zQY8b`YrO8~ec)?^D_`Rpe3iBIYy;&qH62i=O2@C}aOHPom?h;;1hf=_!n)#%#mK=) z;WTI zCyPXF_Zy9^y)$7naf5Nu!X+kSxQw^_?xPG;=(7VKCpslGs{jtUSF3dvPcsGJBNX)f z75|v5(0q*WdgE1k9};U5)^2scHQO3bPD|#CPA&3X@8TsLwhbtDR<)rnO(|$@7=LqP zZIazdSkZt;fdvsY_l{P%GmwV$dS-EK%<~lF+EWR!&Y5%JIx@8RS&wLhiNbZZ=ysWhQce!XD*N`H{{>QB*~zikiTF&SXWx z(lqwQ$z)^!U6U`Mgo@qjgP-R%&Hlc|lDXQPe+e5Yr=uFemL6wrwZv1}kQlVCqb`}d z=tfa9!4am`7p6zVLmWh>2N-qFHeX1zS#o<^EP}%S@N}j zO$`gZ%I}*aNttOBrxT8fXd&@o77NLx_wD-|k1?V0fkTIBOD4D1)=FLXY#R4}dgT!J zYmlYZ_o5;jraT`yIc$VJ5=KvK*Cln2MCm-#9ko>@Cnuc^qAjPcLj;7D4<= zw)L!3T+v#fqL*?!qF6_66F4#Y#JMq-7_}`t(cizhZE1sn)vZV@{mM4n9nUuU^M?bB6z1+VvUoUFrq+ zdud=Q!1T5r<#pDuvN~y}naj?wOGoRka(>R)A0il2t;<#5*yAmc-pXV`?(D3J2&lZb z6k~>s0cy%YrlpXdh4%nJ_5)SUKS?e#qN;FdQ@!BDu?et4qseR@0vQq#c{mK`i#S=` z7z8aFawSI>+P|ho& zhD~`~DAg5@BqH|#2s^(z?&SpLn2K_Q@<=T1Zaa~=y9Bh-o>2%ykBQftPp`d6u2f%= zMH&T^PO_DES}tJp6ZQQ>PmF*!kIZpkUG0#ha15se-}%3xO|tq`i%-7r)rSfe%Grb8 z3_8)4jYfFq1Spgvth*r=vN+-SsPMrmPI87nH>dsKJEQNLi|-fR-*A3bY7 z$mAJ)e?)dN*_^D+f+zeEqz7&s_b`;!6gd*H@>PhJAS@_+_jUzrCm+2r#VViMIt1gy zhIvcPX*H2wg>XqsgNlXyoqiibcWqvnu@ zJhN-S$`H{OoPq);yDgU4>to56qQfj8c#>r=!|}YO4{A05W?j0$V(Id9%=ij1(oC^0d@nb@^beU z$2wV#r@XW~yC88vQ$*L@suc}B^c%I+mlBVr;*9$|gln}WSq2x5us@+%o^i;i-o3{6 zgNJnwOy@Bt>z%6KH>x013%`aFiDjR&?b$=GoFZ~oC6U__^z$Z>Xmh;bgyPY%0j`+; z!2O4atZgZM)%8GT+FSjLOK0x8LJ8bsI)_p&zRZzeL|4JbjA2VvS9WYz>o@Hn&Mw~h0*wNWD$Yd3Sv zbGp7?b}w{$sn%M}u?csl1doZs88#Rw&uL2?02rTCHE+fg`(t4_UA)%FB!^8F#6UTR z5>JVLl|VNAIy|I6O=Le^mM*(p>-E64w_Lb?Pl!~ah~u`U@yA%1TGuw}r&U}(;}lcc zLy4icK}?o&Dc$8m8g-f5EeggGHwhysN@Z<*dGRXM#K4R64Oet-fHB z-RH7;mhErFu2=lr+#o(@H1)+ksMWgxi|`%fQ<4=v2H)?O_dC34f6$a7Ojk4cb;zX(|M@X8T(+%u9f=XfYTl+D-eia8!Om< zwf?j@)QtlTXXLY_ z%OA;9|#9Fqb6d>VvAHlsqcd!8P}Ljr^5cUnL`xWHPq^;TQHd z7ETmTZ;@NN6wWGi33k+2Ja$oo02LhVN7QO2ZdZggF&-LPL9j8^a%_dHY?xg*3BSEJ zBzHa*bQ`uz=L% z9{{gsAizWSYsu4X+)i6H*JHr18w43XWN z2^%_%C}w~88Hw&x%11cHcp|Ou?AI)`?2~b`r#EuLgL()v#E`y6+~HH6d6fGQDV|s6 zG8t3A|DQ_>$IB3l(t6&JY9E7(@7(|CFrnea7tr|w2`*U*3rU$|BPtRHQo3$!pK#2r zlH~?lKhR!x#Hp&;?DQax-oECEnXFS$qZn~$%kC9VRPJKAu;}sRO5;tf(qQ(R)4_d> z61kyk`CPtkdL?a~0Da3B@@Dz$D6bgDC43pgiWVAO0Q_ZPbQ+Ktl>D15*f>sf(7sb; zV|1qcqve8@y;8L3iC1#{=UoqlcyEuWY6x?^G>*se?Kyk14t%?bY-HKVwIqI50LR_v zTr$~LpxmzHgKL=TsI&ta+aMF5?oHIEN`uNMrplz5bUg~RDgoQN)xqF>MpebCwg78u zHbj0swp)D!3#=;H(zQuymvG)xVR zWCX3FY>gieoYLr$2#hu@o1^KxY;mX|w&;r62-NKjVJ}{T-nV4|`A%86t4brFEWL$>c4&!y}d zT!o0cOdLO#JHD40y9F4mFWkQeqDFnHfXNHw1V)l%WRv2J1FR5>h zkw+n%{&Nih4(vMS5kL%D6FPEyMDzzbEH{{7?fPi4+5RM!t*p>pC;-wLmcs*}zr08a6hVT?A(SRcL9GLVhPXO90lB)#`9$=D zC%MP$&u&>Z##L=a*R}gzfrEo#8{*@TgqrY zci_qq@5cz;l|&yzO?*i^@9#NANSEZbqON^cc;fIhJjUK%sf8X>-dG` z?`kA*ZHX{6_7_U;M1QKY@_bLr_aX8BpdDU`V>?I_%qOg^Uu)>@wOmYMvca~oDWN_{ z{iu&H0oVApwRMcbOQ#x@IV2-F%>Z3HTBx;&SGXCAeVLX-*+2`6{4SFa7K5AVx@MSn zc^kmV*v}M=txa~;L~l}5621jpv>#1|+hY%_cJrPI*w{P9joOag-k@f>Qh&PkGgCfK zQuqQqGe&c*<9YaNc3-^{C0J1NFx&A4b3F#9XZ<`8OA4TVW9s~?a3@cVf}A@7{l4(|-uB80KORh9^)l_GFbXyz`qd%W$fb(pNWKsTf`qo476!@FTEd1TGOtw{iS zql?chNCmf?cr$54P`#_z4;eHbIhC%Q_j%ilaqNXi&O~lX(J>;)Wwa$ohTsB@VlhgC z5JDxA95IkiFNZJSGz%y$qmdg_PKzJZfLH~d+GA>03LjtIZ|oco>tTtiVmbsneAz53 zvlPw%p5wK`$}nTqBq$s8%7*B3l@NN{Glde(FpZ*%-VhlxBb3de1871F=lNUDE~wmz zb$mFH;?IxHbaj>2Y7QiqpQ->-yH1I5IZeKcbv<+wdcRwr=2&G31;FH9P^MOsgI#HQ zE-Y$a=l5%kM?K))>GT?ix>QFGsP&G!pqoOrs`YWc zhAWoebD;uCy8<+Y-|66QvuM@X!ras#;}l);@3JQ>3g=aWZJ{>Q^a3>vdHnCH*1X)K zjLr3Hu3uO-=h@ASxEt>*AJLDi>>CtMj>}HF!@>->B_o^ZOrn(I_r*L_-L7&akO2V@ zTzB!?^ljb_yes1(cgB&8tXq89gZa2AK$y-WRIjn3)L(ugbUBGxzr+(_xG`?=XnZw; z)C=4qs0fX?o%Nq;6c17!HiRA3j?Tc1T%3_4R`aEo0B;rUIGQTuoc}hv?2OEZ>r`*v z$SNl5%_NKb2UX;RAA-gtGBxE^h~_?m)w6nx!YNpQ5dkho8Bepfoj&VX&-8R8NokQ4 zf$3B)CGdg0*&a&N@gklP%}fl%XCUIjf#xxRf8Ns*0mtS53J)QCXB1(S2V1xHtSlZW zY#KkXoC~7MR?(-wGy11#vXLht3gWLtrXgfO2<zl^S|P-zD&1tV`~FV@>@|GRDBYXC$Z|s@$}2vtQjJC7A`yNZ91rxkd-p7nRa5cJ z7kuAU$c&6}bwcT{w)cWT!%6t+2eKBUP|VWO`NO=?i44pG1#1p{@17P9 z-f~8XjEEd}Gj)Do^6!gPiC3m9;QhJVsLQE=(LXB2sD<8m!5+3$gQw=LF5nlyleta) z!7IhHvwcTg&fkGq*{LM&+($4%$<%tF0HgE)sWtQY(a>#~TytkfYI?TcQu%D~Ff1xg z0?h%Jw3rXwT4*03C~A>c*SxALMd4zOB(3x_pUijj4hCeYvQ_9C&Z!P6?eR_cdTeM` z^VTppd@z1=!?TS}=6ERAnznTC%-%SIHykZ``kG$2|0puk{xFP3YwL{%1dPp$2xc^n`wVuMJ`0 z&vOBW4eXRP%FlW;Xvp)G(r0`cewB*G>=i@$!35Os5r!CKYL#FH-Vc7FwZVud#pYfb z1l~MLRx}B1N#AcE2P=OW(Ne+DS4?9(HB?*8*W$FluyJGYYNYi1d6~<4GOk8Cg6N=S z*KBDLeY}O}PrrahJU@!BvB5p~4VE$mPZTjJD}oa0MTux3Ap#Qm47mD!{q=umS2to)E`N zXymAt8jAS)_I_6!Lz^{?)1`_0K55R^gJIJ;OCl-vZzchvQz)p@UH@t%Ky-~tRK$Yy z#MGo3eFh<{3Fi1<55KPbkOlsp|L}{`+lTUS|nU;NBlLgCs zMrJbJPs3UA0nxh^QzCHK7!}4BawzkPM)-u0Ji~aLl~&bVz6zBmKc%_Nvh>?C3sZQ6 z+Zh1Bu&02w#AaioPNrSgCZb=%F&d)UM6MRwz!hV~8dlMwKf`sRY4u`5Yn5Fb2?2uq z*35#TP~8pfbrlzA{1%np8hBQt1acYvg7`ek>xNn#A)L3oENKQ?_ z9qm4LqVdDgmwl$lw=;E^?xdV`i^b>b@pS1Tx?TTdaU2@kM)cuG>ntFBrp%4_B39k_ za@VUI@M?TKfe!P71OwWCQ{PG!S~pd33kKRaQ3$VWAH=B1d3C#H!@Fe_MT95aq5DU0 z+?D}l48;4S9x>;sqoSS%mHNcpa2B`TvPY_0$z#15j%JKQ4(+nz@yW*}eE@R^&;mB1 z#o_y7FOEq`i2P+A#7YaVzm{htP_|x*Y!e4wo-tO>5NfKOT%@<46C_|j$BUTwZiDs@ z@RHKFY%y89zgOmE!^vE*ngTl{ReZ!Vg8qUL63u_-C9 zyGw#Cvi9UwsG@ERNK@Yc!n#6uIf;>BUa_s_Ivl8AXz`&sRZdvDZ>!vk(;o)7-8z*m z7l^{luizkt){OgH%5nfdK`GtleD^*|!ff^6UiD`MG?fp!4*nFcD(TMSt|-hW4?+0h zu6uCZE@mqh_DOux=+!w#1pB%B2xCz-ia9Hnb?_Q6{E-WN;$B~Vj#w_Enr{&a=|V?q z*R$g_k=?A_gI-SBAAngm_D;Et5-@_@GE3nwcnqmy(?Z-N;QuOir)eC=w3W!mO;4Hx zr5fUU*tN+hD(8e5^@JrnxwmBwKegau%#?Je4)7N`P`?4S6xYWn12)~b6Om*Zu>+c& zRV<@BKpTI>O4+M$X~3%3m3-WQTLI*~kV&MmRDGCyLysz^$Q0;Fs@($)`EaEOJOimH z1id>bXe*JT2cJ1FyFo@vpttVj1XUB~JG|EputT`OORlCTeAU9yD-as$PmH|2rb;kI zMK!3s&tf)?g${|O--h_%9ZVu^lVZO4_XE<4?( zEbrU`81j+M(l~P;XAF6<#7bH#VXX$thVVoTzwpimBT_DTaT%@eEVwqgac1>LyeVpP z-EHQwlip5Zt@33(LcHZ}a*Gq7Vt)iz*R4(7&8Z4__huFxGSg%v^7=zf2P_3(Sa2&U zAp&6RmGVx$-jmpu`Xzo9=}8IKHUnIHjoSduH^z-^I$viH%O+T(@;NIXLa$o-71}aK zN5>M@Fq2q-pxuipm#kfLAZD6W?qzjm3*qEw?!9fE|BCL8!k7h3Vh6Rg0G%m8I#8WT zY^s9EA|BHQJdYWBcE$bLIYn`^_8{3lQCk09I0EueDi!?Lw^MsU-5?9A=hW?%fVTB-;@Vcv zv^_rc$73oZ_*iduj0s)7#Z%F24#HIFc|ke{>3Q(UzRFN9ssk7Epom{(?C)$2(%Ru5Hk75tM8XQ8oCo;GyEzkJLv=YNKSXEs+axJ zh3&%jpb*AokAd$5i<6jJ{e~L#&)1KblRFu~K2;Z=BSuI|5_$aH=W3cpA)Uf7NJe}& zMVY!Wk`dCpc$XDaT*<}$Z#LNHu@0SJ{fClzBjmb-MRMp*1U38%tyqG!cP~4(=oRTG zhBjbRCIiV7n~5^9a;O_5B9Fn;in?rj=`f=3hIMsem}-k8KU=vpkIaA$NmYh=9!y9C zn&4LDopX?3v&Tg|dOnIM6!bge8Ogu2nk4*bTVt0{sy}7lJm90E)~6xsXoObHO%T{L zmT-o@R1LO==>V^jB|?&m`IBrL2>`4J?P8{4r(s)yo`6lR@ax4oM9%{pZGuKsRlLPQ zjLrS>r6H|(cZm*m=a zs0d911f^cC%Z*UTQkSjXJ7A9L!X}`W0{}N)0WXw#n?6{Ow_pIy?DbAim}hqe%AuT5 zRB|v1ELor+KyYyL2;m+PYadWsj#TVzG0Mq!NRBHBEsXb`Aht$$w@(6PB|?K~@f;}R z%V{{04@~WVDNA8(_g3VYwRN(w2;57ULlTJl(rR}EEhSbAqCbv@jzjMKzvCniJVEAe z_0oo@Jl`j!#Y8lCW{E*! z=Yew6OGRW~_Z$hu*-4FH_Ev{QUEpAs*-#YouTE9r^{?kXkax?he`SX4@Ec}8mKceY z+_>GYpL@skLm!s6&NfXw3-YV=AWzxaJ@X$ea@TmVat{E6-L_CrwJ^ioWjKd*l0IXYz4jS2{qmu2m1}VFk@JqDxpED?N%Pe-djTK6(RqaDjd@N+ zs`0yOx*hJrW@a7^=?_OyoMKpIGwo6QD!G($Nb`*e1iZr ziNN6`{ski6n59#rK)at)S5CV6dhaowkK~m0@ zof&{I3Bd7Bk`8YPs)5?L{^FM6-JmObOWt8$>Tf4GP*HdSj5yF}>C@r@+;&v9$^NK_ za6kKye#B1yGJ`$)SxQWM37XdHN~>!3em0oRT6&iRP$t5I)!F7Ve~nDlK$=5bUW0Q( zQ|vb*H9j1v0@ZE1^!>!jV>aI<-+&svfBM(r z5rQhCIt9!vTB}JP&L5!Va*97%AeN&nimm#l%@?rO5cPZR;3_>_fm05nK3u9Zg<$Agqm&?2rCBj4Jj&r=%FVrt0TC6B zWXh>s(2^_M4x(oi7XY}8FMZRn)}qs|(aOqtWfuffse*0qYBb2@*6g4^p38>H3(8)h zBoK*N8zt$IvYXw_&Rf}*^=1M!UTPq^%EnU5^RKsQ#|qJp$woc*+_KCKIaaehDsOcE z^H}{Eha$qAAIl`V_hH$2x6>#U+(}@?2NB7lIXBw{aWI4}c<#JQ+dfa)h8~9%;8hIS zVtz2F9@6($)r(dINu)on&&WQof2d3wdpHslfSV@PQL!$rveo1 zj*c>tP+oM8>?<6u?14SxYFaC?lA0CtggHh4&^iLFO3*Qq>m~)#uE$@dCz#5yUdhRM zCgAk$J}h?*z&}zhTGD!fQf@fk*%cSC_?n%zd@?o;PXcMp$&CZ{o<4fv=Y2o+;3FnA zZeHNDUnUUQQP3$t#7mG0R>BWU%~O{CN1UP@+_`K8NEmKvA+4G`c4oB?ZR`C7VWgTi zx&EX%g$9d)o2qI1${!!n^6GkR1IEs~u-9ELNWw8-?kdWf34TwB=w-3&4HXjYBQV@5 z=Z-Vr*e;LYXfKi40z4UlPcSuuDq!*L{o^6j{6;L zkIv_oiICp|Wx;&L%~&70<_L&Y=jY=*AYbew!aHR&#WiggI^w^k*u<&nP!bB}EKwID zb$(fqI!&XI*#ae}*?)8{IM{w_+p6@2j?6E-P2(4l4X^r(^6Seg{d zHN|OOY18}Ty#8Oef(Y$6m?$&eQ7^hqc}#ONpCAI0$K^M()GkS11KHcXKIPu_ zOnJ7rCvdi9TI#e%akD6q7IGi*?Vuo;xqhHqZoOBQr zuNFg2+se8~SI&|&e>_i1f^P?g!=T-mkSkz9SsnKz6oE^$!naS5JG39)AVN=AC4^MV zosI0)g(B{FD2g#2_;H-iGM}?ufP9moNtkra0x9|&Y3%R>6N?0@V_Z7(wd}VelFq%2 z!v_7%Nn|yZyX4+HIU7x1wJVpG&yJWZb5+^rAoqA!yw8hV`68#~5;($pmT<|>qBJN# zK=GXAX(RWO49rF}vPAxUEV-=CtDe2)GXJ<%%rA>jT@>M1`*9KMO-mE$3ASc|SprYn zbdbRn`U|Ksm?>2(6LUVHq-tR}!92Bl6lWMyQflG#(VU29FIfXXbmlqu7;w6f1NS6& zKFfn1^~4S)qZA9AaJgL8EQdI1MfVJweKp`<6JSDWG(H$<@hx1r$Dzk(y7^6PBwbZk$W(vo%M335oP36JYyAg_0Od(*H%m^$Y@sel0)kP!*L<3qVjx~U z*bN@+WXBK6PU{K?_Q_bP&|5c;T*WAnQ??+J()_K%Z2-$@XMZbyXL?zg!o?0^_q`H7zTD!NND{5yHmVR}0W5g% zDph*?6BY_g`e55~>v6A`ttJjWW-WySo|>M~hM zW9oB9P!ek#?!F7T^BhFj+7$5;SQP6kQYg#}ofAJ()~p&5bs@RJWu9q{<}*BMp*Lf+ zOZd-d&9zi(0@ zo8H;6CrHlta|qmnbz=E|6;QgTSc(uGaky_th>@e<(SB>BFUZRi|Mg(o$>?J3(Z%OB z=`~_r21<5b()#jPQuWRLOL9?dRRip--8&?;rSasDhvLQ4GZQq`Stbm79!Zq87XqEl z_!@QIGRZ(wROK5NTFx)s-?MF)aa^q*$AriX4lcd5Jj>q$@>|BHbQp5q*PAcYzXv+BGd z^vTiwPY@lzih2N_PfUN>c0bN};t8We^vh-EeW{1X&M=Wgps1Zlrlhb=uX$$v&879f zNP`Y^hcUIUYhlOD8L7Nt;ZZcFwg~Ps*+A*Ol=+HF6~6Sghfb(lk<;o@z{D*xT4M&P z7k_e~2%lB5wra{^u0A3iXtiYIxy^a8$FqAZd9k(HnpQ;e=v!sYBYG%{e4j%BfLEV$ z=-h>X4HXB#6=O?Y!D~95rNY}!q+VFJG{c(1sd4};V3>mqoaE=WEx1;o)5Kh%PPcFu zS}oA_V*#7y(&zGh94l&&j~#RzF&bu<8edzesvOTE@<%{zwRaeDankmES2D)cDMG|I zI9;7$O>9x9r0JEGovYS)y>q{SCmv-~-|Xnv*E~OY0WyG*nUp#hoz)83aq7#;BZq<; z$DwuTa{wT0GajnzH{6?ZET_lb@tKh2smMw%`>Bx%+@kmcO-`1puukdyKX?YS@Y**M zI=jC=Yhji$R%9Tz8ESs=`1gYFnBMYv)CvR`V?YqV%5^65?m9zG8JLkkk37TI7Wr4S ztzwd>`mxJ%mt0BY^k#&Bb!Km4&Ps?H0tzw?Y4Xr2p6y|3T|ghqW^%uCk2M2$gtoG< z95#lmXdL)&8V^}S0UP1reD&R@zq}HYoXK8T24c*s^09puTQy$`LK;#eby&`f11L7h z)kjU@dmk^ir?n98*)e|@r??6mJLK9|5EkuF>i`D1NUoF8WfI$*-@#s!V2wC=hf1rr zm9@QEPcGd0MM8a>4_oTE>S5V>>7b@?M2f@2&HTe9ZqO>~bkNZNtb!b7q}=M6L?=gP z2$f|lDRvMqN+hX>Asy<#W_%G}BZn8HW2@3zXjsNM5CvWE5N1A{nNYAe9XKo&1Od8f;TXd`^+8*0Mnix4m)9p) zRt)SL=V&GRA@rs0%Uy2Z!Tq?y%y8(+c;;qftAXxFjt?OsxH1H+)WbQby}e?gCV8b( z9_z}~`Ot$z{iv13$w6eBh{|0=>+!}a_5-u@4?Q0yYMW)7N!He$Y%oqcr8!M3&WB4F z1sVY4!r@T@yyrR!G5~qKe;+_yqisNJxFS5>!27b01%CQX`n#?Raf0 z70wF=%TF!2iV>vVHl)FOX40-U$Pt6iI#L?js9Rh)GI6v%%Qa)qvHAz4lvh=Q=wq)0 zHPCZ=XWTQS7wcT#*!3gQMWMw`!QHm?G{wB@kiQE`LX8(jGY~3SVeS}B*T^+HyZKhf zu2C^+-mp=IYM5xM+JOoa&alEKtblc{Je?p(mTBMkwGkG_(@<6U{VPv41ymUNn|(kn z9IxFFJmNLXh`o!HJTP0VB;{?6EBn6>UrG0JH9p6#N`JazS63Vyb5e~T4(C)iL^S85 zm09!vSqqt_ujqlNjC<=!C5hQ#_Qoc{VP* z@|f}7)R1L0ZC=eJle?@O09T-m0d|_QCQC4+@fWUG+-t#J;)_~FrJ-)sd#(*nZmp>G z0py0~(x*nuYlLS}jP=+qBA;`Had3IQ4bb(Emx0;pKjn=BrlC;-<}Ui!xoBJ<-~l=} z{PNI~$)3Wdnn)&}{l28*9)J!}%8hHw5<$M8s9duk(mY9|kPa>9y}5dVV)YhRlOqW6Q|5^1XfcEPS$Oc~-Cbl3kxI7)XS{RX z@g(WTqic)vbbPv6HkTq0gzPqT40kjUHYio?h5ZqrlpUwZ>J-9XE53n%$d>f(%r>I+ zm3!aOLAM!KkEJXsu6iO{hka%&HHtO?6|nA6h%NBXqIepGQ|Lx5tg;tj-3>n@(Mu-i zf)dArPgWG?f4{xSK@&qjfRMd0V&;Rk7NJoO z-8}}@*ED0|e3Eme^(QY1Ky{zs>KTvlMHy3KF>Y?;L_7P*e^pE~87z*57HtdDi&*zU zDT0#5dzH&s=JPIsP_vrFe0CcBK=EuLM6cb)#W0e{w#YbxRLImHpmktZ1Zr>s2x-qc zsra9ia)v-KKb_26SqC^7`#@XnJ1EnV`FC^qbX^Zzw`-|()S-|ASntxP)fN1WCDtn@ zb&}DHEv&p`bu#*|90se=D1-|pY>a1=dh{rFfthjRq`J8bTab6&5uG$N5q;u;726pYm!>~P#@LDVi<@!x#5)p9 zx{i{*P~taH6;@i=d~wWH6Dq2-%$EIgG3m@X4d-Wa$0g-t2{qd)NOk&7(6TH;}s zknYGsEJG$LO*8zg1u{g>1xurEP>GZXvps_U4ou6x9KnrDX8gb~alZRp&?{q7k|`!G z(%8fep0X$18kq~Q6jf3B=T^wyf0DFy5Cn|;7jQn1Ho}^Vg=2K5m|vRQ;T?>i28?<6 zW%naMr^_oc1%U9Bt$8*8R2Axb4~_TNgERsBV-} zNlC-eF{D?jg1_NMGh*C9OX1n>SL7G0gC%TWJK&JpPJDaNXwd<$0Qhhso{KO@43*%R z&VQ&}gU-caNh`++Y5@S`Z^@)GD{Ev?>xhT&z7##2UoI5k5h99w{C~OUi3SBTL{Hmu zcNh&PR24}=5)`+n47AEF;Jk;yU)2C5ZdFV*-dqPM!do!$dI$kg=p%TN-$AQ=jgxTQ z-Kk3*f;IwL%?WaT5b49U776ZGdTG8j^9dn^XeU`sML0sskwCLd+f0v*$YJ2{F`>Yy zKOYx70d}C<@JuYJU5*QLc(z3TqR%PjDQD}!`a|vH2Z<I3ceES3z zO`|+fV<5<1*=Kaxvuus{?BsZR#ZqdK053q$zgx3R+t~olgyyVoPqDV^XhV|<=3>CA zU%96gxKb6$Xny$~#_2l_j76rQ`1NK?J>nd zki3AaeocvUyX1!GIlhiLxc&gNf-7*Gvvd=fwsd---`Bk9OWI$x(^t#4s>VyG$rdgG zq~aL_oU~<(Das(^(vygJLIUv6r9*&{`0t6XgR~vY?IMGg-iVg8O@^>O72YbabdPX$ z&c*XJStuCbB?*B=nm7KCz`tHxv&73(x>#xfx^M%qD|cG`1^Qm5n$62kRCg4{JF8Nu z81pV*U}mlCqCsxB=)}BgDV!|p8|?D8?~fC3#sOTLNDwA0;1EQk$4GC&oEOn)(rMWR zzW15(6yekI8Gb7i7N|WrDp&gH-Zp2?=t**Bx7@F|Awr0!s}QE$f4*}9(&FK9mL(?) z;AWgljKPy9FZw!mVaRSAxka-{NIX+WA=^7tY-v=3I*!Ew0`#djHmB(LxDkphHwUK! z{%<>1%2q~Y+0phwYteo`_1_&qZ5$Z2N>F%FmL*cY)?@2|_s>Y()*_h}{}}vZ>mo73 zCHjLre7aBN0ssl~1Z`cA1NS%9UwVxFt)B`4OD~Gu{#4Kh*BmFu4E01@YUC2FNfb6a zof#J|aX(b1Hr1qaWU2Pz!{^S7pb9VtIUHV5PemE3xFT`UE zksH`)6n)K*p|0CwjD-{6rq(xqJx^vRL)vQ;V6B7#H&1llG1hcvYrn(Xvq-OirGIS6% z-{AT|3W>Ra4UU;`F_lct;h$nbU-R);;S0DWAm_&@ZcbV#zvUbu#H(s%L2aIv-6( zZf9YRhTjPv*c5931jcgJg;xGaheI^I#~L+>g^Bu*xi;H{xr555aC0Qf4Q%acHwRk2KP%;FqGa*>^eJARLJ+2- zuAH>F29_%=7W==Af9?ik{cle#2?}%JavXYnck@4HUHTMic2!rcY!{d#5eqq!x=?2f z#R|okOyu=SzmED{CX_T>`wUe`uBt9H6ehnm2tb`!kB!id8OR>6%A?vq?eW9Q<&EWpi5lPeMQue;Vw=x2#7In~2o;Of5Fka>>AK@Z|kxGV*SY=GQ^x6;ZA0GLJR zgwZZ~bL{lPsQB$q=1OI)r2;;7KJHNGd+!eoI?xTl0;kR(~Vx_-n#F7{39Tgc!n|oNBWz6V-gPyfaV>)U+2CEvu?j-rgRsz*w|xle)~- z({A=ykaifP5n|-i-ncbt5JWowUa0CM6sR;!Y>mU<;``Ho*6Ktm}LoOyg%E@4P0 zeJ>*m@3~+0%S({;KCR5NfKM;@QP&bsGHatMS5tL91nZN~->$+m;w@8dR{d*2@$rl* zec*K`|NR5NX|ae69^X+c{*EpR#SZ(+9h9}!wjVjheX;R> zBhr`a2sDbf6Gd<2dbw4>Xt`xlu->n2Jm6~(0wyJLn%)H(b<-}ou8d3D98li-$|9{S zAbfr;7>jm*>W#;zEp0CV)?+VQ&?S!mPn)cTwfvok>w6wuwwr~D3Dqw5;Ig}-!S?fk zMh}S$1-+D%fSez=-M+fABm6o0Fh+DgV+s`eKe|WxK*F_-9YXl=LUf5ANW)Jy zB~Y0T^ZeMSY{z)ko}i$L+VVNh2@21R@=TgZK`C%QqN)xUQ(yq+wE=`V)09iw8h`-p zw|IHdu+E!&Q&gB5Bj{_uaIBf`6_*|x(0N8xdtc7oj?PdhUY&-f8}o(%M9;Y?_SY#v zF_~;10#R%i#KqH8csE1j?69704Oojgj5T%I<_?3ooZ6LP{e3OZ`jGsbQ~txfs4AGk zF;111%C!UBPdLapVX9HZolLYes+4AA^Mwxo;R6!RC5v;Gtt|Um()i;5yC z?Cwg(lnQK1Qh1$6*fwuTP=hTua7dM|NXM+hQ=Aa#+mZR0m)NhhYabZjtl&LuIyWJZlg&$>LY37w>aqC1BpAFtFH4YX2Gb18r#DP*aGX0AbtyV02(6 zMOs*z!%*`)JT(9v@iP4_>kRh3b=%?#L-_6Y_RjFR8L8U+q<6hS`jo8!W5sQ*WL=kM zg`?^_FvMZ;9DK5XWm=z2Y1!liQZpo6;Y?_NJek8w<ZL`({R8B^RG|2wp}KlRO!1uPoV)7^*a~;QiCb|b;L^>t!+q}Vig*c&jj?a z|1-$dIONpj5{`E=Y($mX{?4W;?vkQ}u#)Iasd6r#CLl(S<>iRD$Kgh3^dvEoAu}$p zJ&YT|YNinL*Wli=ak8YW`3j(SWMl+j)#=p? z2XSeWPSJy^T8sw`t@drV`fc^5LF^9gtRSRS?if~k2I%)=yNT5C^|vL0~mH8zjs?9$tnuY_6r??qoKgNmPM@tpgtEyr55oa{78YC|%guv$os_A|-? zl0WCyls2j%hT~Yb#p@&AxvFal3nXjJVBpCP5UrTJvnSCHBP#8VYguAZ z=PXQ&67^Od2z|&+$}i&);rMR+`#iS zw(&OWJxCWm;&=G0`lp(pz$+L~R{edL+3}jEIKOfEid@D|H>jCa->~gZ7Bu(SnmKk5Px`^3wiTo%^?CP89UiYPtgV| zt-Ckh=ElzXnW{K$imyiMaYSnB zfMqxLyh*Z2Tv8%Bi5db#xJwZHh+>l7i$BkULAT3px*3WQ7Pt6uNLg7aa9EF^;xj@{)*%AxRK z|5`Ql_Z%9T~2?A)IjDrsXT^a4JQ8O)Byu(DcU~$+BFi@ zfVZ29D%)RYB19X8M>3Ec3N!1UYH!&sPd#u)>yjS*Zvm0iaP^()v15m4r2oyWx4u*|9Bb}8+-Gne4lOEXtn#?P)M-7wP99W7!X#(2mG zVHu4;Ap}Nn!!K3v__g2$!vkVU2`#KqD5STGG~X*+^YbjvMoz52;v8w&6mzCVB}42y5=DT9l-!OXj6@O4?9z>LFe2qda-s zw~{m1YNFHizs@GHC>91H5=%#e;d%`AjcxSk#5xdrUyt-I=WwIVrq>Z&N^Pq;(EJ%w zx8UP-od2UP%Rl^6W&bE~X`x>V@YFxxh2;dryjkJiHBNw5O9W*{FB~($ES@Vr@yvvw zRQ=3n2iLw75}FY@dEmKD`? zH$hIPu&OaW?N`)oGT~U_MhC{bvptvnYtdubNgGkzFlNm2N~K(3IpYwLz8z%#l^b+{ z^ucgrn&`8bYHD#J-qE`Zby*3bPyQVQ7R&7b&YpSGjb5O!JvJu?&ObU8sD2pmb`3Yr zS*PjsjGJu~ZZEIx4`Fu* z8;Q+>tljkew3W)31#Sy{?OEW{!f)oUoPAs$)RX2El?9>p>}u(ssykT_ODzFCjwH|j zq~eEFOhXj|hJ-Q0+KRvJg~QkKQX!I}OP#P$j?sT)1kijq&k5T=n70?_BgD5ZUIgW#iLe%qGX5&QDzy^3iGW?S-S4 zOAYf{anzybjm+=QG<<~+P)eII4GBgnCwJZx9jxbgz7tO2ngW>y`A;35`^LBlTeBS9 z&llT}0M7qS%pYj|jx*zgsJd)ufV(e{b#LyCBD~~HHd#o~hU?;;;J{%^g-eb4n|n{} z+hQUBI8k$xY`dIZgR`(r&YIPR*MD;VIAt^-)i8gpE8NntEtcsM`z08>p|^w{?TcWQ z+3O=_%71|&%uM9MUQfpUBsa^|EO;+3;fyv6on>S~(GAdBPSySN1PneCDaL-=5evIn znPg?S7yxQlF1XlMt27=wbyYC6rSfD#@)q$M=P-|`MOtcY@D*wex*h@fH3dAisiZQe z@f~gNQ9g?VN4JsJ2FH*)dvN8et5P7iz8qKK=Q&`<^&yL+C2Wf?aS-2bZq8<8!*>LN zhrl7dzQJz(9aZc96j7#sFtSnV*)@&xmBA@l;Hm?y4rkXLVa7UFVHGF&*tifsS{Uo; zQZ7#`NkQ=5j7_g7S)NPU;(ZE%IBq;zMUTob@RQEl^Dfp0X{p-O&nv8y%R*GFKAkXC zPwcwFzTz_|wCWfsXk@Y+&G5^OUt+T)XSvVf^7tg1LGJl+81x~d^NK*%$7O|?vS-FC z4xuSyDh@QOusOtdpC+rYsXakvlT=;Tj8j!S*Qz4h3$Fs^g)O-P>{@fJl~m%VM}7&D zOc4Gk27W$@PCpRDv2hS~C#I;+8+wqFv-%UDpnS9@-R!Rk7?QQoD#E~y>uiZ{MVDND zRJR8S1qGVXZPD*2i>>|3bWs`F>h&?W3XCV!Man@{Qlq9|av`$>74Y%IZ8K91f?dK? zvgvu{qR5KT&PBH>UmzVyl>ne8*in&tOYAefWI#R)iU7#c8cy&FN_9SkyfN(aw#L8m z4nTS;%TRG&bT(OithPlMA|vectqc333B31syGM?eH{SI_B6Nupt8}#NTtKDelSyXC zhpnVk8PgWuS5S4qeFJz|A4o|lYSZHHA$ZfDf@9|2?33V~L_ z9HnY(@n}SY7 zc|m-QY#HBr zfsK6HOci05J23z&Q1;k?1MWf=tv9K>b!{M`k}QFf(JEvTGz1rN8`d0n(P--qZ_+N@ zPDpjFecj1VYW#=<3PBgBTE3T|*p}-s_EYT*Tj*4QCh5GvNlvZb@_d{|DT+_U^!}0H zG$jq7_2GI6yHtsC7ooc;$YMgjkY>uX^}IO(^t{GI%2qp-0V)6Tv%20)Q$OS+m)ZV?Jqw@@ zu4O%q&rvaxGWD|my)87$aoY;&%h6@tIO}Fh@H3jWY%1(_O{OG?q>=`|+@TfeRpnRHWSl#A|A`?I{J+~*(n zA1l=+_7#oZ50|mZ___+8Zwn!3W~~+Otd}b#VNQ`c3PX$7mIc9bl^TE`6i2x)GX_YP zjwoQsDcA2t-1P8W0R4YKly9}sIy1&T=5crLE>PX(|W?wkUt74RPHBQCaYg123TppDS(`K(MY2V68XkZlQB4Ab^HF*eW{ zJ4MFet$U_#Lss_;-LO~hDQkJt_T0KZnm*T;K0KoQ{waaVzUph~w-@RP0`^!s{;EP@ z27uGA+;4s^T%6<$@m&$kAeZfwU^iRKmG*w$0yRL#&Wfd1qs#Sn%M zyzh9YhKI0+KR}<)FsADlL-RueR}XAGHek5AFbZ-U0d`lnmsH!Chk}>)&)hASU8$+~ zdzNB}j{lR_3!uJdXlC>3Nfbm=#?bo_y z%uQcZ?_ylU^%il@?zSLox>&Oh_tmzxq8%NzZI<-}m+~KRkO8Yih$A?Vs#)g`RuIq; z&uz%sP(F4ybrk6*(6&<~w95>$!|`>gM$sP?Bb)iSmyRkolKQIOb$R1-Ze4S+&oCP3 zH4^?!(Sg6gUR5zhMkyW1muc+6N8lQO(Te&<>*RK&*S1`1z+n;8Lg(n{Tx2p%p~gpg zGAY2m+`Jq?LeTlnBjzN5aoVZ3tUL$FFeTl2l8K!=eE?dMXzWGVGG9ki z_oZx9(^APGUl69=7~{*C?Z)OrP@J)tpUc@|3!HKjE@Vq6iArF3hpc?~y9P`%0C-D> zfH8S>OIwDg@%LrGZY(wF(C%+?(3;cjBtsE!IAs zW9_nm?Ti9HoEW}Dt%h+=0Lgc|8d%(E&K`Qg80qxHcnxz}{5*kdd`#)F1mYFa2$Ygk z?9Kvhg4i=9|9s78v#i!>{}nJ8IN`6BO^)>zmr_YhDoaT z{g(7y1Zkp(X8R2H6*0(clq3#GM@MmV1~1=R3^b7DmuaXthH z$)`_ZP9ZUMQ+@}LRh*q9jx^^v{ab62EA>2#OIym+$ za7R>QXDHgcJw455OYSnen?>}{pgPDDY_<*^&GVd?-bKCMr(r}8Z;x)Ti3#D0c#)ee z>jxP4BVHp5=QM#QOli~n*Dg39>Of%nmOu>b2Omw=uv04M=E7nBNNWd zn)9lC=kf8e>0uqY#z-awf%f$^+xb<|b$dpcQusnW5vs{!89~YtGO)9_5iPl;Y@lj5 zVw=I8`@zqAj6ULCHYKa#TmlE)=2OamKZQN(oo?S*=PFGkdGa)vzb$4+Uh1HcCC|Hx{ciD&pAEkInTxD%B>I6B(PM!!E4in03kMIjKcyOxlr9-A-M@)tCYhBZV~e z&RMeEVow1s2qG7asKWZjxlYT^{x*upVBy}KsrZqFK0~oN>u?13Dg3u{v&emD1r!Hg z#kdhK6C9uS5)oRm-aT`0WqHKC_TDEHj1RB$HLpH#MP-^N+1})fUW29{p#aL2Al;!H zxZoj}a`S~@T2H8qu5#Xro@j#iG47XiJK8 zbjPgxCd>6~6M0wob$%=CkSScs37D@naWH=uY|*H(=hNdq6vSE&ebkc75EAY5>v^`& ziiC&dz119e;}_qFy@k)q1>J&y^p2rDztPkk9sK{HW9xonwH>7Cz=l5z_GLym`MLqc!xhRo81 zzva+Dau{L2pSP?L@IWeJYx?sr_WtY_QyylHQT86{5h9IUVCrU|I zM<1{Ky&hIl<|X^6)COx~=jDD`(VXGHSmPvHgbJs>uZK=~Lhw-W1zz#YiaB+4JIW-J zw`Sml`8cDgUbKuFU)_*N>n_-bIFP%4`D1h6z!^{xfdD|WBYBFj?!b=kHZ+AeFb^(> zqX*q8%_gZ9|6VoLAJjLBm6b(G22SzoyMc%_5PGh;PDAF8JDS~zy{I-F|LwO=1^S(2;A@ZkQ)FU&4AnWwp-;d3eQ z>!oXDu-#sD* zkJ`K4Kz{wDxJFbHjbNO5z=v`PO_qEVZwS0e;c$oKF%DTmm6de)59)tv*(7wP-4!uK(b=B&n6;X+VRDYqt^pNp&dvu?2w@ zH%b|q_i=+u0<$6e=b&4}XYG3*NpY&KVqyG5V=1El7G8rYr2;cW(J(!KX!>_a05#tU zHG>P7a~p+%v*f@;XhLXF#t$IkdO95#k-Z;6zlc=umESGDqjut-Jsx%hJuz>hOi1q` zzOebo)n}=Z19Q$_mlN1gbqinWER1|M5EDgBjkFC|tuI~kZi|+#D2skItlBKa^)gxj zorRKjazv?+x>(I#?Ix^G&^j)$3|~0z{2p@~vWS4Qk_gO`zduK&HJOmDgiBi)evt~B z06!sbp83g8MuW1$`!WopkqnUFonl*HL#T4{18@DI2I%%w`UWuvHap>i;(gWk|PBfvDvVg!FJSxE78%HzPgQa*7!IX{`CYu z^yMBXGF@|sn$^RqB3owjD(GM6%k9kZk2p?$Ijz7pB6Dz$oXm0N92AR&(~2+$B)P<& zc~&tsUNmWO9yl7s`r?DY8fO6Wv(Y~;a21>$(Mr|%nkeFqxulcRD6OX9%hFS{z}LZJ z5rEyrF&%VF4z+C<#GMeY>Vz(s6c26>N{Z^&m&GqDbyI9;aN0i5;vNd}ASm_XtqoB% zXNy`9Z3fW1E@mz{E&I%JHaZb=HwOt;$MR;O?GQNS-fAiG(j;ZcAHfPc|C>`3#zHTG zr7snt!9lTznh*CDB%+23HGvxpB1WhNbMC5`svNx*9X6`Hpsy@|7-fR{l=JDpj?QuY z^-D$kkfSQ^=g4~$l6fGkV>&B=HAQ@QIPq<7%a%Z=q$r|t9NA)H^Zt4?1Y~DUDRP?* zZ*19|Qnev2M4d1GV#9lCA%mFw2OlW$~ifGCWS-rbX(sbuL4 z_`Fp`II|RSUJAmh#OV!F{(Sz-D1|OlE%z3C9M<^W;#>U{(^V(rFUKJkK^Gy3qXxPx zjaNcLD2gk)w3<7$WjE@J_XV~KvD2O=;jjym+`EkHq%NF7LIp=NS{?PH*E7&p)41yP zBu`Zr@D3ebv8+UYR&=dD%p5raetAXnF5|`9!j3H4YnexA5avERg|~%@74~(;Azarq zTh3x)WxpPUamMoG?b~ds@k8HD(HOtjBa1vtahf3^Ezwj}pH8Y7_ttmiFgg%=aZ?8W zjz8}x3{RA*BslbTdn48AhSgW>1)&jax)1s{yS3fr+cA2phpNAQMKQjSH&k>h}PqrJu|YIHI9Y z{SmhLhJ1Bny40e(l#ckZsl0?Sd|$x`mz8v&PD_6sqt78Sv|9N|`R8j`0ZPn+4erym zuo8Bt;Z+142LoSdk}Z;HOJ=ccy|m)FwKtSU2y}aRboT~{!UlNM;-HbE{M?)=pwsQw zwKp>=`6o9hSpOr#kGQY6jxj`&kbOr|R15j%u$9d~z;Bl3STCv$ic2_wup}4~`f8?H z2NDv;V`Y5s1RWLn8!Bu&zaNjjM+mXfGVGErwLZ0$|DDB$`G0=?vJUkl9iK*MLRVPt zkn8MC8g+7>SRgS{_}}eWRFBmQIe!a+hU^5IzDG=^ch@?N{nC?EZDR^1zn6M&k?jJZ zM4NGdsQ^_}@W*jYAR((GczJJPg=Y1LqJH+6ZkLy5l&{h;p8$Q}cpmIcG#OIafV>04 z^S+}GsOa0Le2JH6n8)#f7f)YKu)tHS$S<1l$;L>6Ue^2fziVV<2L^4MjmV1pIx?3| zj^BXqf)gGHywNkyHj}}Y;RWCu?9dj!QBQL~+Ffym961Js%vEW-4#cw*m3Sc!#So0n zW3l&KdY$XdlgtJJhX1BIGH=P)&PLG%W?*hM;bG1`UTpZ}{D#&m{YO=WZI3HTEn-X}OJ6>B#&vYZp-upS_`a14E%IB-Z|4YC?|m*feBqs{&CE}fbV!bzzRUIG+Bn}Y zy&G#gzg9H+NP)k_>^cX+Gq)W_HtY%F6*9ydfsLQ}fY&YwO#>+oM%-4x(@3xldrAic zU7wbDCVD~@nC~%WT90*g%X-k7@!v@23d|XVMY6>qR11s!F6!9f4rg(gf~}bsQET;3 zVdzgmB?mHtPw!aVb2`3HMp6Qe@b=1R8a{K+gVw)~Iv&bISL(!rEiE@5;yZHTH_Hd~ zbriikC7#0s?`YK`p{3kY{69;z2Q3jB={1iC>2F$HKg*;|9SjqMAL-rO`3r$h#*FkDzt{uE%8v*X zP&3SsS!~siy}HIvwuD7e%Crp7aTA+?FV5~*m7T6{xkaeZG7xvbGS5NduoB$16w_Lz zM{7s=z6C`&0Ll4>e9@K0?E7gduh(Wxo)AhUD`GsQExS96{+vcV6co{abypZHSM&AL zA4_Y7eJ!x*t&FiED|Cx%!5YE@`SCw#BXlT%X7b%_K&sqwapy>>S<2ezwF77>rYHq* z5CQws;!hYA2<>A?|5$m7Kn!UGttmv1?1C&__pFC_%)gCV&w#7|ZSQ~L*|ORST$lfc z6gCSSja*d#NIk5+d(x_ffNQByz_)nd)|W^}af~$kgktJEJl`ZWm}O{uAcAg@ZEmGx zmrd4T5ihvo?uriurqKv+>mqCc@xXtaGDTz0I4C-6+x2P<$t4#sys<>9)tvF(^qIDZ zflMZ5Bk3Buv~k7Vnz#Ao#7}n}z8qj4syQ1S6(^`x$VW*dh3Y`yhv+UZq4Ie=>A}C4RI! z6+7y}hyFS4m4q6tCzbnXy`a2OLv~)^okF^0R*^|rd_ft}8qL}Q9IX}#VFxzPM+H)s zCbobHFaT?CpyBKb%-W@>7n4KPXVbRcHw}e$2L+)$6SGYq*jeMEf4DJ%=*o zC)zT4MxP(Y8229wL5Nk1rkCTT3(aOWv%^BObya+BDk;pAh~D=8$yrE%6vm;GRXR)( zQ=xlqZw+QwhnrEaozuun_is4B&{0@~v(N&Hr~2uR3YK=SJ1Z7ir@7DlN>BC?2-eyH z8L^81^SkBRUiQ{7I#%6RM_N!1|CLHdrug(-^wsV97v+%^*(Lpe55#n)tVErlSerJD zGa~Ka6QCk`);^`gTLv%FGBB8g&kV}1J}Q54TwjZfL64qW zk}QS8aNK_LrP5Q-CtNsu(o7*M`_h+hnvC%2P41^lNbYjOWZ*<|20w4--?~6eRkn4J zNM_2Om(~s^D1StMrV3duZm}{&ny?yK>2=WO)&39CnpzRXA4W*_E(xc10%2UkZr2|C0P0$Ke9800+d%S>G(aA30kNP=AUyS?o(rx@XJ*l*T5f&lARq)dSfQ-m$jq%p5{uyt{tmiC`H zeSt0X^@uVoja=&Sd{~j=<)mm)4jKjGIP5K!^*_51_u^rl8bzZR*HQdu?i$3ok_xJU zorJcyXy;-sX=Ou4geg$?(!YGEpZ|`X>JLUP`|{U7TlX!wBPKIm^!OkQ^d7qCG?T(w zHzX+K%QZp*rU@VxRMD4dJ*wTZ8TK9N`~IDyn63mj=@;UtYn7hFP2}UxxrMMZa1iNr z!Lu@6_JpgPmE23ZwcKj3-lVoqkZuaNSqI7Se0t?`V#P@-HzcjS)Gx1SnuUjs=?Hsf z{bB~CN>N3nOnRFmd8!Pd(p=GEOFs3Wr(uc)u1F#6d;!YD7Bq}&$Wa^b+QG##)UNQsb z;Q*0_zrWeaVSZ=(i`_kA4{a~Josj^(@P;Rs9M8Gm2Y&Qm@63(Y4iK%qow0?$8vZI zVR5%~Sj03RDe$yzYWin(16lmmiy&YA@FJt>vRWUiQXF6W1X^T4z&A*xDi(hw(CQ7) zJ{t4v>NTL+*e1V&xwR9Zrl1Wj=-s0Hl*Z+7#QRn2>WRaaehU65h6AeI_t1y->mt)G zpd)o|x{JyBzt2~;s7dsuU;p=w@nB5BhG({Rn)@qy_AaD8FKCk)xwo|7d!;~?Zh5nDv~w9*wolDHjpwqF+FAw$-VcV@fy9;P!Xr0rFF zJbLq83+fiWCle0ZnJ2sjb8S)-TxRjxeF8a^TP85}^lgcSsf!5%`ZTXrjUolV7=hx7 zmN^6Itp!5|(ZU($zD(il#zhSs5FdTS*&AN+hsKVSB>dS*?YrDDY=MTrc=6W4#&10- zSv-T4>Fc~w9FXpr=o)ukl0e&#kN(-gj`h)P%0%S+9fT3XK?UUqQ6xU&I`x$MW+Bu?R0_< z7re71y&W@eHbDj_VhpH`#jTD>T-&qlrQfIZsK<<<6(*~H^=S6SZR-7`>S(l5?Ufz* z4he3-R{Ok`Wt6*r>rmwSxPN=#R>&@j%JDImz<%Zx>2M>w49_$~2OL0wP3ZjXDmU+7 z{v=*-c{Bqai_9|^s2;Iu-pQX_;`@wJCbB|KspwH4%o!*jRxma{aHNw-Yte5QGR=TV zv2dJK{PU&5XZEd?7T-dT)_;S(ouJdBD4EXNs6GT(AD_wu3`3D%y{R)G^gr#&>WbMb5A4txK(DJ5ZbfFfKg& zK9KcFOyS=qcj5J-&z(b5d^ty8(&Vtx<7PL;Z0RCT7n^mIHt=Pp;RD>*mi+&9NXnRqT>=T|Vp^6EUL830c$AqOR9 zYLA0oX43ICgGs8K{u;sU#2&^CG$9M*d+*DNBT#^;&sWz|?7z!+Lwp6VB19xPl|o&O zPA)5ol`B@1>{S5RN(hitwJi3Oh>5~`*jk64s8G=Vv8{7(cAQ*Ot$11=h$8td1-V_C z0f}ss_E*E5v-|LfSPM9VF=q2kXigUt^+z>m;QbI{K4m@3|8s6;)`$Of=D_TLehFTDK?fu7-5lsQ1L*j3U)S%y=DNkB1&$DEA z(q|Z}`>2C7een`&7NT%vc0WKb29=%#ctDN;8>q`)vCu3~3-E1XSt03)4$`mUuhllr z{cc5B>M^FCojM?521j;%OT141Ix;`6pfyXd8Zr7lxzggNFywu*SnWl;@NYE_8Kb5X zYm*C60HD+1XW35MGW0HDCaXHw$(;+wf-Gfe)$!|H_>pe5Co6Y94B1bNo&b$`>2omr zeC}Q28-L(7%f;VEpG8(oHNefJf6fv$or96()@W7)V(Q5p$ zQL9Av#{Ezy3CX{Xci&l%@@I^y4u(A*cv@tKM{{nxxi971_IJD%d7&z#xMZrEvgF_@ z9gmjit43Y3zqw>U8NPdzjn_!b_qZC+H z1=@u#)_Bb3XyD;xvy^`b`UwO8n>$#DIZqghsPBG0$?Qc|eggljPT#IYL>AhSQVXAA zf8I?;G>wxJ-E*-(1PtuL_goTlRWOf#xT>!wML{6h|69*vO{5)QSD*~|TlpmsgVy_J z07@2BsMJKvKy>2EP{5_4D*lsBi-kMQyTg8*i~4?C&sT&XoPlB(`xo z2C4g>kFZ-|ZFJv&DHCex0e%9o1S%BBB{Ev7Fu>|D-P4fnL>DLOKt)w)FT26W;4U)D% zPOpVE?GBd*gk?#6^`c*XB(7vOSu+#Xo?qLoCg(i$=M}qc7ln2V4Tv>1r{As&%%x1! z<%|2n8EKq6z}@FU5hw8r_0SMJ;y{io6jia)X&B%$c7xDbsvSF?C)LI0_E;6aV+=zO z^u;frz%wiJ!m)$gY6gjzVU7L_BQ~#u3p1*#kpB>2&C4c#tRO1>hF6TE5;W6GZjTeP}$CRz41?F#2KjoJw>TN$l?3i{s z6&Rl%h!FK|F&ZK;h@Vu1#up}LA8BB0qh*>cSQqtyIx%BHh*H&nL>mFNK*L21&PMR0 zdW1S_ngy1phr6;3QoZjZ@yZP_j=)MGnU%P}(IKlSk^!1u%+=&cb01_sO#2dWYnUfqCbeJx>LU#+a@3Bp z2$>x$wV9g`cd^GyP_C2=-|3TdAfFRK}MnCt!)-6AeJ7V}jhh*BPZ@;o(+ApIMp?^B)aA!GiV1F|iinPQLv0mjRJ( z*p2KQZ!Rq}bp$nxv?Ppe10qz-RQ1O_g1mLB7`Uq(jZ2QmDEU&)&l6e4?aFf6&+JcT z^@i$OWE>DbjC`0C_y6C*j;Ctcs8qCVvGg#x`g=BBIgSMup z?%A&-mz3WVEd__%<($0l{wF_hYiA*Ol9j^7b|j+$Xy~e>_;-lCm25K`y4c^+QJYIh z?1v)y%v=s4QwyU0xtvK?LXugw3n+1%^w4ZkM#8Q@6rDSK|Kq9|e*~pG45AaB_u)^! zn;c12dslr{6OuWQt}+Wo<%ajXK%Q&XFu}v#Kn>8`b}M}i;?zatLQo)K4<+$hZSi=F zbd*&zR3F+-AIhxm*vCn3=K%C%OVXI{Xhh-;M9;GGUBZH!p{SX(w; zSJojiLw<_s)($9f`GMwsScht-S#^!eUhnx~jpdb8xdGGwY1;(DdQ}^#ZCy*>w&wZH z1o7R3;$>x;R55axCx}k{4^Z08uekdZG53@p!XcC#g1Hob=uwyHvzAy6#W_vBaZHvQ z=bvfGE2fmY*`3*K@G1C9#NquA(dZrf0d;m7+uKj+h5gjC^p+|+3sDcmD``y>k-I+jh0e=pZ@hZ#HdhNV5Z?~j~7Er9lk zg(*U32#j6jRT-cKCyhr%bJ;Y&Z`qH2raHcT*H~R9Gon)zTI-i89*&l91ef!cy(Wf_ zMX8BM@Sr+|I5+htqIAoQ(93z+!&qstc$pPVDENE!*=(o;LVx5X{ZuUDlCo-QGK{u- zT`jj1E|zLp8cV|Po?6oi63I{kFLY$LYj!%EgqADmM4D8#~PL>$NapV_Jo#Y^P^#Q!Z3HE`F^=wY>mCK)k;sBH0w= z(t`9Ul$NC7<>jXFsn9g2(p!)2@+ryF*3n?&>_3c45;;6LgjXQwfM_Moe@Ul>9g$?jyfQIVf9ew>whmc=aF-C$=j9-whI}t2vkvo=UKAvOFW0_8 zV%WfW2$tr^aDJe8g9`Ad!^S#yGGOtWd0x*N8*Gk8UC1kiRV3lGQ{BrdyB=KP24iRc zZ`Wtiqe``q+1?j?W-^5sijot1!;SznSaz3Wve}J>(f!W?@(-v2O7begyfLQ0`b*3C|Y(wP1HZ%xYb#27by$ zO;Ut+7?ZX=2)4Cetaym=H-qS$e8Ow!+f&-U zAOFyXZv0PgA5rxKfWMiyhBt;cG|l-IGuf*7h(g|Wq>iYzv)RMhMz~xPoiF}tU z%Ai0S>*Ri%q%nODL8s71%ocXFZHt5x^GWuC*Ojh*U>_Kz3g1r2JIr{penDRT`~T{t z@6U0?10wfDFHO=kc&qNkj|2|PcyqeG=eh)SnvgtQ+$grR<#bTC3>2u4XaygKT#JA1uFo6j{(9i>1Fs4nJ`T7df9p{2i<`#gAm$2f5= zA-KKs!fgb^tnCGBU)wGf%j0*##c^EHLJ$5)S4cWhkd~SizjUm8*cyPUu+jK_xpU`; z!hwe%B4c6WWZxt;?7qD2hCw3eaS;I4(~uE;rqyW{_5EN>o#D^FQKMklV+hl2D!g?< zJPbAKf9^WJ>yt^OdCX{72EI+P;!Ia%pgVs{pL=oR!9e=#Iac-mN@Y%DSvAA=`tQyF z8dV_$`H7;FFqD42KjN0$ZuUbARUawOx?9612d2%)PJIpKZFPn1y@nUpC_%JGP(Y?z zPCchNVKfqjo=$qSa5ajA68EYQXNHj7=T#zFn(Y8H{xpK^p!<>{mND7^2ED3z_ zzNxrIcKj)W4O9U)3{f(r61!;{C<@q#OG)fK`2#3OIf!3 zw4%LFIKAh2vZ1g6$Zz1y45eX{1vCs|nWx*~IqqH327mrX&yC%(A^{>nF4 zjDPuXM>wJMAkjiU+je-_7ld*({T+9l#KEIP0A9;4@mKI;p`!0DaeZw`Hz{=O!fjPN!t_5HhOc(yS5bfwII|QmUP8Sp>JDhO1|kF zNoCnqVx=h3?@g@hx4=M3_D4P=BX%H#n-+;x&323@nnN-+e~WW?jl?k|LM6$cCSL&^ z0Ap@*N)*o*^Qu{JbUU0DL>nhLkj}=YmMr2NYS;9J~HZ4e0vosQ@zp46qa<@_g ziNL$-w@y0oYl%L6KH$Go8{P2!3^^|PzGV~$CY#P_%)hL@5$#Hv#?)Bs04Evo9M41Mr(ceN4*>c4G*BnMW|C1Y zswdc9k(bU$$VmV8i;sS?06SUPG$1So-4502Zd)N{$72w}0dU@va?{eZ%}zxXQW3GT z=#$&n0D`vF;9p-!n*O(4mIE6;C9XdQSZd72HlT+U8~V;0G-NwWnEvU$Y;OFkPx#dZ zOj?r-y>(SQre2w)o3zivzb`@rF4cH!c)7`7fvG1)(>&^&B?a)3y$lksCMXb^N2m4; zijPzv>JF?sg1QpU8^3o486Rg6GoPhiM3Oey|+nDOjqV7egNbp!804KmG-d@Xy zP`L>9S-%;Eq3|&9BjD=*_LeQ`Lj>p%JxU%PAXWiPs`rlRAAM@Q3I|mF$U}wYV2R0c zIb!5%Z_Z@WX)w8yfF*Se!OpPQ@{35E_TmNMvJj7k0F|N({ph8?z3WQWr|?q0GkwS5+MkQ zZq2$yB|o+F8w=g$^v8YPV$4gdD8|qW0o#FCSLSa~LkPxN?}Azj;Ka373J4uGeUxi~ zXN80G7L*ZERYVswk)@jSfoM?HR3lRgd;)Lm3l^ixhhug~fkaVK zBiB_m2b~}QOK&N0wACN)b`!5^M;v`wQ-(BNxgd*7u`csNx%1M8SF8Vgq&h+evj z=tv5!{#cdv3PSnC*+4Cw9DN-n^WI>qysd@$*YT1#mZkqSB}(5K`{xu;GPMJDY0QD+ z0skWPI=d4BPaLJ~eLa*5`PfG)Py@qd+-Tj=t5d|9`3NDE--UW%GOi`j+9N5OzW%s_&2L>+QXM1f+J zd95`+cM20=r0Bf*xJTm9agosG3vxpA#QA`do%Od$ z{52zM1ZVgzrkZd!JE{}M(QQ&H`6kGh2)bu*I6(muG2zm%OZJ z>}MwrK_Hs7G{w2dY`RAsmDWaaVG!J&R7e2+zL}Y3@TCCdNZUbfn5nt#y_+%fx7$8q z77z{K>yZOjJaN0SIR|J+N%*orSWuM^h}Ul;jrO6Kan1fCCo=or9bPr=9nBgB^O91n znJ~A9@+irk`7j;DH9)ssk|*htpc#{Qg(ZbtKHBkmMU0)7QLO}}#Ly|&4UpRDAykBj z$}9)m5u`=WK_1M%H(i+ClHqFS54AM-V>fVEebfS4FgXt-Lk9kxFU{w~Wjk6RNEIN_ zdfDUzQxI^g!FTo%v`EJ0{}w#tV>^J~PaNtw^^l~^eBxosjl_1lmi`QJbZsU95s-7* z&8n}92nO5l3?tRa!HKY5wc7e%b|E+E*OjjF#$LQeOcX74(OL9R1K?jO{JQ9xOsA{| zNsDO#SZ#D~qItzepm~ZwkC3IjI$rII$uT$DBzD})g(x3v&H)vDKD&dKJd&cHBb(xi zvNq$&hq3({^AP;1_DKF5W&??s2=TxKtQ%a%cHhiwWR}xwalDwJjG*>agMy^;f&35GB#}vO{JuP<%q(m6KqkMI9Lg3>VKez3K5`#V6#&B zO3b+vby|&*`?WmXd$4eu-m|l0{>}OQpi7s`z!M|b}k8hwx>vK1{(kva3 zr9gboy~y_IFNO7!i}hh~)W~wL?mz^Yi-iJ^MMT9Y$u%q>bLYugehgrS5v7hb3teDF z$9Y5xx}c-8F#0n4^kA>8Eacq6eMX1m`BuTHv0zP^z4cCY!3=r3ju6LtVcMpo#5R;x z7NvK3ksQl!er;){1hcHS1PN(kV-HLCaE9BlrQ;?-%7t(hJiZNnaEWp^oo=_?Ji7j8 zgrQ5>NL@A+(Pl07D~-LG^xjO%iJt4+;FIFC!~90k-4-5*YJd2E=4kLTo*iW$Wh43P zfsv8gfXpa!;Y$C!%A49dlN621)&-HRxj2VBx{}^KA8yd#@ybNWw6J1WPRJ>D{ULjq zebr^K$cSPv#Ay8$_MM_ebtGpPAE`TV zor!OrM}qM|nM~scDr#LAh%g69EkpO?&nH|7ckl_Yb&DJ~sXd;Ax!QQdb?;jVA07Fy1YQuFglxR^T z`eg>Fw)M|LFMMHaOTcC~*Qoh!V@+VZCO)>;WQ2qLjN-*DfDlX(q~T3}mXv?^vMQnC z*zxG^iI+P5O|0=2h5481J3^jeh?1~UD=)EYr}qJRCl%34PzL3&my(4-AiQLss&Rs` zP@T+Z2d}2ZCk$fo>e5+~)66oi7rIV^mb2eUbM$@z!hoVOMohMzi1r-EdYsN+IY9#s zyjm{xYbA|o&8Hi9xdfBMTCqUg)UUG->23C}gXq}|ad8iWtIFQ8V|^_zDNIwnbw?F6 zLoI$SwQRwsGy#vjBStR-K8Y>c>J6d1u?O!BzxZ$aB2a!N7zB35ebPG*j&zSE#NYX; zw`|&UY)WzaGoswvQpNlbxgthqkMhT8T57Qh(~(REzGpkc<6WV0e0V9!8F{Lp_8V;~ z7W8zqLaRJgEF2py387J#RmM z8$$2qLrQCVJbk6&WL}Wg$)ozXYY&_k8;?Ga&h{TP0vJBP6MMHctXlf;xgU;UI-P~v z{!#nY9~4(e62`J*b>dl{Lb=pmm{92Ip7G_fw#MExnb9ic1D~atZM!iox%D~JLiM3h z1B+0%jk-%Qm-Wp2MAm0b%qsN-EO(}fD&9u#U^z8~3v+}MBCNkv_PCm@yc+Maec{Gz z1fnn$@Bo5fK8&oJo4dafECB2!Nx$rWsqZvX7H{U+Va@9iedO6{gy3QL?~h!y2GidP z+0Vc>44kDmx3g{Jhy)Vk%eZWjJ802vggZ(X@zabeTBDOVu`aXAA~us;nsk$&zuC-a zZXR>ySWqQE1bt9jX8R(VOX1{{Bf@JN14`_0tG>30Dnuj~Fx)lM|Hp!U@_8kblB3>} z=c#}IyUOx-$4z|=U`55ju z`e2>-#zKCfm|%*TXE|(2->j3$TYob8lzB;k1A2_cyO~ z&{!7u=w}17UQ;F8fJNr)t;KYrjMvPD1cIMqLu~q0g1hL9hC~BI%r}>XGP)%acHgyw zKr8u;{16t8)m@8tU;X>>U_V&xM1FJ0HKW<ufPQg&rtv(5s1meYwI$=zXz}f5_$QDiD>iI z+)l>a=2%tFp$d$m(!<0`m@Qi%W1Ja;wuhLqg|z%DpAUN9kYeUQQa>lofq$hAScENv z?b~meDHKW8P!h9-Km*)Or9e4}I~W>-d2LesuWdEJs_ZAVm?mwZ(@_8?B85$=JG*I5 ziH=m1;6uC3MA-WQEdrejlT#Ib1d(sAODbfz$+%+6?yW;@?|-e%!8?!{DC<|QEn3*7 zFL$*WkM@%iZ5AynC}vTUKQ)wzTRh^bu9`y(t|c_tmLbtp6x?pKz%bk{JuBpqPB#HJ zxZRnK6TaXg#7$QsQ-2}9U1sU41=QvYMy9%6(&I_uKm`2`GyB?_h;9J%B zLNQad2ue6`tr;Dd=EJT@17w%wxS=EXT6Pt94LlHI>D)8pJe0`OipJRSJZA|ZE0;Qe zKUu&AboE90z0wJBFaLQ`_WI01S+|euI^sDN)qt-kVh5P__XXeI)l{r|cxe=W;kj?P zutcY;;;;yQ%t*tF1T{~#BAR!1eIy_)7Cfi>J`iaX5ds}Q8}-ACNT?DFr87eiI1cdO z${Jd?486U!7ca&}Sa3v^+WgPY?wZK}Bej7JwERG4^qR41AzKFvW@dnOePCcH{O}}s z;*&fX&3c_vRBDLS0y{zAB+(cVZw7$yk7qh_@CnWBLAtAfDVrkz6f;;fw-QPBv2>?8HbEOQ-3AP3oZ*DgkzWIUsvNfAdxj5U zS}D~=IlB9!4`pT!N`YK%BM4COi}W=Hg}Bij&Y8x|@o6tUDNWd0Km&%bOo6g!NSTV5 zIu*;M*0SV^eSJR=+6pzuJ(_+D$S@#_<2?)W7`dEUa|LeVh;dr+u5mE!Htis?Z09tb z|B*}c4Z*CXGa1RDl=v&Mc&SX%hIeu=%ETi0nIVixn9`NwAxASwYkt_>S`sbmKN&P` zeyb(P{L@bAfI}&E7?*H8?%huY+QBzf#WpHq_}J`tJmX_zpRN*P{JDYV->3*QkCwDs zI^NwjL0xP>(f-duO&;7mpCD*0*qUBc5Y{I9G&>94XcvJ2`_gl#eSKhQmo(zN2$F8H z^E;dYJ@9ur58-VRi!z$uLqmd=2QbL;fjC)3irU;*VfGYL!SW4V}AU?j=9dABta@tedGt~1Y?$#YVsjH#;KDlU_s(~8Etva zCNP@vmaMOhN=r zS64Qj&iM_Lv4Wh17lpOx_EpbdrFJdELXHmMdAZIL&$azluIX{qX@s|pR^wP9QoT+1 z0PhR~aTgUS&Q3$EWU6ZuZ@GuL?U|BHltNIXqJ<9M8JAP8JD>=`OktLCH)mZa z)D%+YxYa6Zx-$&TcoFXS1+(Q3Jwf;YBK*&UBtRLWI+hjT3NO77TYItSE(8TDi@3IKr+Wrcg=tlZ zT%#-PqxG7)xO{Vb0RHi1I+Q(MjT(_dnfsD^2<7t|<3GN;n00TU4I9!WFQp^Wl%_%s zVyy=Fyctj=JQx*wL_?j+W`_kA9K;fvY1e2+qCf$QO=J9Kl2`Wb&{p;u{>AX|m{s3~ z{yc;13vrT$OiGP2oe^pzb(*+w1&HEzcRIk{u{|8+FNYSPRHNMwS%i+u zC7#@}T0B`NJ>IL}ZcBkn`QALlcVNY$zf{I^eB^-YM=5(Y)3kH_A9^43+PzQKoxRW% z8X&Jw#e&+cI+LLZQfiW;Hlq=Nk679rOa=Yi22CJmH*kZw^>8x56Y`P)4tChF{{W=J zf}z*SLevcgn*-61vVR~ePRK{*vUH5^CUwZBnfix_5+<4=9di8`?n8N z7sUo_EfOLH1lw_utQmrvR1e(Wh{l=qrnx5ik!+I$irfTeq?s5Gl&%5AF;dL%Qn{nHj?Bk8q z<9fWvF-@d1KWhJffHEb*Pp}b&Y!L&IN!LENShR#@tw=ag-NAdurPf z@Z(CvfAx9Ev0G#Ppp^P{8S8>hl~z`yIIb-!feev%6af~qHu!a+ zO9VJYYVQEQM7J8dEyO&$4vbf+B<7SQWSv&1mq;0&n2$$W9kdp*S~{hX zEy0T@rxMDB`X9{(mj-1bzGHA1=ym?h@zlPq6F=K4%*Xp5*-{1Gf9RO@V%U@nfbzW@ z+n9Yl6cKn>eDIcfa_1E$rv7DK(oe#k0n$*KlcGT;&4s%bFi=MO6&Hza_wxgm?wfw` z*GkI2_#~oJt47}nYZ?;*h>DJ`N=uGCoO7my5v-@zBleiNS29Cse}O-o+>UfPB@Z&h zdJiO-OCJ9|^fAoa z6K)-!S=j>eW4eEUJ>GDxidD4|Rq%v=Ita;k^b|6q2i*uq8Zo~&x%^^6ueO9U2W#%e z@Xh1ExgfJDAB{Lccq!-$w|#+n%ge_8k-)O_(=E<`%W<;&_`4BZ+Cy{ zlNvgG;K2MGcS%^#sN5(xQQ(;fz$Br8q2@v5jAx*-H}sbwT^8O{+eS zc!4v*XK|tmDP!3Oza77Ls_J#x(&AV5m*TXW|J5udPxO~+SG%B_p9P;*%N=c%i)jBU z_Sp(c`C+&<*@`Y@F6}2JEvch8mdkcfGLFM-&qgnL1+TgGTF+kiqwCi!bug*XT``9C ztNfNgwZ#*E`=vdNQysk8{BFKp$JCQ)Erj0|cGjo5rN;H27@SO#kYF->km$Rv zG;S|fKMo!YQU&~tm=Wgj$U{Hk&%QYOmF|=Ay)NSPda+YZt4TV1t|ee>9}2grCCG?A zN8){BYmUMRYQ$4l4$8ft=O+R4-6pY>ZYuYX$&((XQr(3e5u~(Lu^(z88Qh>H_CluU zNNw4P`_Qgfm<4u>0Qhh7p?1u8@J|7_K)O94OA;ytqv}tXCFAd%)q*!V_5Lk&nVD?1 zICPHoKn?&CP+m}#3%N1Zi0QzQSQ~xtQdhQ{2K=X?{yE~e(B5B_5jG!c^I&9wIvrGK=_ zdqbr~zA-20Da*bEJ$K@zgcAbzj(>4FYY}DFI6ktgFkAbo6 zL}7d_G1>whBFR9Z9~KS==aJj2n$vJb{I3QB-m>(z1IZ}RvB!)=h;7N|tj4Jr$^s?a zRVV6i)Lv=fYPliY~I7SGSwoq|3zzU2$})3jU_6?9eK@1kp&ir_8Tb4hN5?Stk9-NZ3_^1R}fg z_6Jdklmm?XMc3{^f9QiV9>!SY)C7yy02yPhf!OT%xF~5l9agQqSBIl)I(0ieXC( zte3?06Km!H-%z#o#*3B=V=$!DYRTLjXx?Z%_MTLw6z4EQX~W<5U_$sTmc9@@D2iK& z`qp#@><$s~a$ECKTjB->@tYvzRm&lY2G|s|NV@5Oqn2~?lhMfR*m^Tl42SXG{iKfU ze|Q09>5q>oG}k>idmGmEp*u({jberD2tIn=~IB_KBsXhdAQ z+OiVQ86|ivt2H1!8$IQyfW<)5YUl=Bg}CIczVO3M;XAB?ogEoDQ0pu%)1HJbi!0~$ zF#BP*TUx>ouh2UQ@1ILIQgqpjrU1k*R;Vx+$KEaJ9tho2R6OhIRGy;^YS(O~R7n4Q zJ#TQN{$QU(vMm8NH;eY}(phdDoNg3+LC2a!BZ6bqbE`e%ti@pqNk%}IdxYjL|4`gV zw=|I_i7OI%(^4!r!u%5$_R5eXv1{Cwl)MV+9wLmW-8KM7p|>lN*gSLs6~qdmwQ__8 zY_RVBq6y-3yUU#_bCs|BOV_GJf0A!qyq$q;KJ%j!U^PhIRc{aE#Tm&;gIVJvK&3Xj ztXedqxw7;_0>oV(^K!>XBcNA<{!_RlPKOpCFO<^bD3qIZh+C)A6Cklb!RjG?dt3K= zE*uR#{(&Agh01TUaL|DK#d}Z=qej!KUE1$Cc^A6Yn)<8T;xawswm-R5L{R~oEut># zuG1Ybk>J_ieKb_P49HK1XiHnkT(4_a+tIs>9Epj6l3<~4o2;1#;xunT9Ec+#|Hf8h zULG9&6aAnfxcl1A!;2+h|%Dz2RZLv%iF zDVw;O@CGbXfZ_G&t1VSLBXIwpYwzSQr-U>i53)|g=<`r0Uf6EcS#PUa?9=d%Jx^@! zcvI}v3>;hIrnijG_+8N9>=Xc~`4Ihr=K}NbQL~P@T#hXW#J-a;G~knMfIxknmAh$6 zg>6^+v1ykAN$8qGBl)_pOs-y6B1BgS17gEW`?D>3o^;H+ z9&Gf`Gvwd)S!1@>CVW$|4?m9~5ir0~C-EkKe@jW{igk2hV*&040Iyyag7D_=Q9j>& zD+Nlo_hiVBbTNSeXx4l+|4ec>YF@>1M&c!=P@vr7RJrr;oF=V)1inuS!5A|I(2i@S zp2cF>a?K~@9B$xvA=U$$6n=uV)3VTvnU$_$j|;l%?h8vFN_S?Q(~3amRu9Oe;s7)$ zB{$V{j|VAdfZ(e%0_k#1}$tzOZO&Ua3FfVkLgpxf6(}84s=$+*-qs z+BYz$E=MyB1r7g89v(;kba6e*!id5Qw2Cu@xRG|WQQHVub*-YmhORM5!V`NK>kQPY zdH^VpVU>nZk4`MVc79=^eoW!K+rOnJz4+pbaM2kaDK=`bnd)=Mx{a^biubAf_S2!{ zLxXa$*Zbf+nkNu;#O^y-q2N+zmBZU6oS6S{B8c)esQrphNLwYr zOh<1zN{mV#JFZ|ft94@b~I^ZS+Y?en6)wRJ($`vQ#BV?OHG% z{Xl9krj^_Pc^w8{TNwF=6cY;ljb{XTo|b)l-eXgGm8Ug5^AIg;Pd2ONIKep_&SR2T zyDrx(U&Zr!UGM4J&TwSou40oE!p1(tf{;A^c_6%^L90{|^}1j%8A>2UGGp?)36}r; zQX>ey*tXd95>jWj;jx^{MK{e|lZkky444;=YjDDhFJ&48tl<9e=FTaZ=OMwP zCDdaeB%q_Wcp96;A03kr8Hq)KHclG~^Ut)S5onm0rew{#v?U*{3UTp{C4?Q#TOcb| z-M$llH~&wZUn@_q;erRj2QX+pe(qzYAx;P=nv*4y+>_;Ovx!f!k9GF;&-IP5as{^(q#~qrvf-h_nnB z;vjUe``8fbgC!&pMt{!H#76!6ZI2D?&AIkKCt3m6ISB|;# zTV3$8RRK$tISLxge5X^FoJ{w1?>_*yOMUK`?Y}(!*qJfz2VE?|tf`0RWskI?RI6rh z$zg*ql=A@6(rBKGu`1te3X2To5afH*xKoQsoG>(gIn5%qU4O<^t_joe-=@AAXnBy( zf;?%VMacdV1WgOw0!9cgrA`HP{GEmx9$PQ6HCFygyhyTnl-K-ed61;84Wm_6pDg{~ zjfhH{fvB-N24bP#e;~gb#!24M!e~v#Jv!||Mj`#ed^2LW* z8?@Az|F&CXGT9^q>(&P@M_{b6Sk@7tF*$PdcOmcK6C$DQ^+QjX;_wZjo7Uus$Xj7N8vYZSK%&#;pOk}JRN)P zqa@bdGjUb`m))%6P_8_W%WU?n=vg|q_kqeE=Y4WRK|MQ(PO((e9;X0mveBHpC-S@ z{{XkZq=4l#J@3f;a`Pc;6PL6x7?ns3#z0i+1vtECurzx2mOipG_V`ZV`ao_+H*xyH zo4{~jv`(mri)bEO-;45j9sZkRQH}C`S^sGE9#3bvz@Tv8u(={dfPlE7)irQJu$D7r zkuuO9)=(ZXQs}xCppfbeZ~dNZX{+f1%@>B51%%HeW-cu+tQ}vfNjW5x`jlNffa`53 z(}Oy14@SuoCd+f@fLVlj@0e>ZfT248Eq!>_ql}#7CAe zUcG7zjo#iGN-gmf!u>rT3sCBp@mO#+cT z66%;&wauoII{K!|A&#xK+Qv#GWTjdMjdHJg5zhb~^<-7IH1OK6XIf*bd<<|`F$LWc z5(5A+_&cu{Ww-3&?>!X937%Qs#O=-`=_6~I0Sj1Pew#y}az;~wi+1VRAk~<<_6{nb zTA^({s)=kTDo9$HW$G#axuV_xo&l&d!V=SYkK z=i2|%)FJ}E$atmr)K3q|Ntw43{-JdvjbF~PLFdb%Oe()A7|M6W@H}7l!<~&{EYPrX zPS-qc54OnI(6G${2FfuHrbkm8TJ3~){W$M$<~pP(Lb)j%yaeSc>y0Jt59o798HZU{ zeYKeWM2P^A4Q;Q-CjCGg`~56k8dvbTXo37p(AW`*-Pr^%+bITZaUZd0@h-;J^G*R< zDGQvO--0+t&V9Ctf_%m)oC(}~82YV5BK-~Dugqvi(TLvA-C9Tlhr}AZR?d7_K_N=8 zZhJL4)70x+ z_WxpflaBbT7K3TK3H&OxX82IjL}jdy1TzizwMIv`;ZL!~CT zuAfpKD^@Ary4T@mwiWo~ejIiG;85>ISA0t&_1e+iR*^3KhCx(d2~LZ%-f2+kVPnx* z$uYLPn1=@H_lYto*Ig2b+hLycrx+RREH_!GwKGc{g}mCH8XdY8@Et)AR!N{rm~3iP z_f-t?QHe9ie|{rVYK%X>4na0GxeTkz4kEI2sP@5-eYM>_58OE6V0;$sP9M?u2I#bu zAWm9j2Vj%{+#2A)WQMf0r{zrmA~R(WsX&{OAL-AL+(5!l6d|Ll%z(};f&8SQhN*S+ zP8r6OtQ!h^!0kP68_PSvH=sp++t7k>-A0g`8%be<1buvWPJvqfsgK4kNV)Q~ZF;RW z{{+mvhnB2m5QA4pB%{gB;{Uf2G)*)!XDPqnX)v`F9}t~~L%YA`O`|!wQ1dKbwfslj z3ZE>y{FfvWR6 zz2W4G`c`H@#@!+^-0u-FQdQ8<>}qvV&xF4MTztvwf}}NoX>h%{Kw)7E5-)kcJ`~fa z_<45g4to5qsS{UtaO_f}EQdLp2grwhT-`I@57x!fR<3hXr9g%`f_fqxbuL!5doF>> zj=Z3B{BNJa1-kMg$M>#siT9WKmZfl=TzZhMel?3IaMwpZ!qz7xZ2UxMKgkCpFKgnS z7Cj(^2WJ#-9>f97$_{u@_xT2&pgC#@clY1+^x{ziHR7%UYO`E?G0nLK-m`Dux9kyz zqCDR%b0uKvU9c8dz^Y`jBDKIM6X2_GAh^DdNs`!M>9>cVyYi+Z?yKyBD6kzNB}XR9 zS_RfRjsYuPq-6zZmp$ch>I*$pdC6|Me&uMMw6?~d$MXM|QCem+aw4}o3v2Offk!`P z?s|SuU?~5(+Dg0h9X_|7y^kqI?S9{`mhX_tuk$;{QXH#{ZP`u*yAU-2J7Cju2L{lf zC0CRDvV8bei$$^^J~aF#5I0 zz>$Zlt1s!>X#d4|Oy#jfZVxBgtJgCjwY}g9K_ZhV&dx!zt!?6xslej`uR^Mdjd4+p6!oXmtlfCkS58?z83 z1sVz@jUK&g`~P z$lIepk9LbycR(n)<6lKs&VQLF>f1X&t)yE5u2=CbArLP{H)uq z?G4(7M^#-DAzE2)egA*n)WH*)j~K)liW{{j2G!VdeHarZdSG}n6sJP6#4mY0z72NO zW@nD}snYFHpByRD#-PuJWNrD?7~zWXc7%f$lke$Sx4BKekm?FcFxiuj5gqCoo|Nsp z5lmuj91f}U#u+Zg6t@;;`6b?QU50^2fu3MVo@x04 zi(JBbbK@~*hpiAeO8!FufA$qH+JJuzC<{#RmNMv1x+!rI%&X?K9g5b;0uEF)Z7WIm zATxKmOf$LZS&O~$ehy=)FY=zBgB3E#uZ0Ozc_Ty|5*NHsz{`T|(pmTxssoy^s@72- zc>-ON437Rtr~F&P)&T#ZS#?3LwubM4?1BgAi4e3VhVw_SKP7p~`iJQ^*tvEAs~C?V zskz~A(fubFEXM%*Gi|Iv2$SaRR#Wh??8;DIn4r9QsN9h*CTVv|!h~1K-v@_8bvuoI zlLD-Ssy(RFsGM_=Bm=moo)hia`i-{^QtcTG?@*9{c(2j9-Zx8qW`H(_vuE7PQ9 zN)=c!fIZ&1bLHM|(CJfpqHHkuPbPRJA_V5-8Yy`yA0mB$Nq~ai9$scnf}&GIbkp4h z8>_Xczh?EG-*p0`*-kIs2QrhurX&I((e74IOY~sxYu@>kchJS9H(x`&6VBF>gS_W| zgPM|5FlWp5KlKeZf5Xec#S_?i88CzV1=D*MZ$uSg=7MFcXpcsM%gyKgLPG2Qi#=zt zU$}~EqU{1O4npraKmd=iwkrQzez#gF(}D#txlr7M19#d=Z$-*hKgu@Ab+UdnWqO zvXVl2`RL(k<=Q!c+D>n3r3;Tlj#G(rvLijDpM;LAUv`}P(YT=%B8?_zg9jboI#<%! zx6u^myfz)&SE}oi>AR#EjB8J^LE0WcV`QO6ao1O68N&CzakqlNSq5r(KLs$y%-q1f zVZt;w?5a5e_`7iJSTA(qGF7#I5SfCo8X&!*xh!SgwZh z$`JZ!t@PnDNDf>XyC) zXMCZy;&kb5Ugw7AjNUz56dG$%9e|zE?XnygSQ2ewf~S``-jh(I+wWlA11e#^cgAfT z(FcVjxf>Zum?wF?53R~RCog-&0wPbuOY?CGEzHyIe^NoaY7f1*q)M6@s*xIz{b2Kb zChXq}echTkJvRn=6RrtPZ(3`)!w6%e(owvtJ+5(Gb#LwUInRV>@2Mto??5OMG<~Fh zjKP^#3sw$>TOezLOt}h{5Z}0foO{j-6-jvae`Iky3m;jz|19{ON|f4cT4QF#VVJH* zQfP^C&+=Ti!@BTPu~?T`!^TaDFHJuv0B$t2;Q(C}a`9CSEY^H0NxlkBHW9zme%I4@ z0tN3`?{t(;C_FAw1WfresT<~(YC<(+1P$h0Fn&ue%e-+bN#e4CdC*02g33-MmB`?TMrxtaJdOC$A znrPw*j|-ba!21@H9g8v+NmD|aB=sOcVz6}0CCyE6qBrtP0NoP zf^Q@57)B9#jP#PlAF$8#Z{%3vsAs!8M8AuB0&gYEi~^nu?42==C3~Z5ma)Z~KfEU+ zlW_Eew%}}TSR6h+d5&T3_OA0H&AasDMPhKh`q_qW3GorsCnW9!TD>*gLj9J1Z5Du5qqT2 zwE?0ZtAmiIxSz}^CF%$Ki1jPZiq*-~-*Fo)1IA=iBT%$~vdvG}d96T{*oFL8$)Iq8!{j*2ZWctSuU`PPxjX)wG!ZG$i(L0H<5k z%K@+x#jocZ@hM8&J5*OBxDKJW_4KI(J>y@06!-u_=9lMyjXf>Wfk~A-(Oo)7yP-XR z$^v^4VM{bQVssm9R!NZD`tLotKQA)l3ebtCvC<|*pLU7gG!Bk*hJ!N1_kDFh-z&x6 ztWUN(3567Q2-sLDQQ1bBkU8xru7S&saf5%cS5E}+XgMjtpBppTv4nT@?*`mkM|Kiw zH)VQi7%(U#LHK@}?I01;m%C<#0U8=`+8xcTd&+-&mrY1-94mpf4fXlb9$uc!DUphX zwU7o|<&_QPB?~Z9jzc3HeDEO$@3_CvVrP2#FBmhvMj$=;f?Ews3Vsdq`H^odB=@@M zuWb`(FYmW^@BhbSBaBk&v%79b!)my=}J+zFw zDtqT^gMzTwZ@J4;KfDgD@_lO!_By@4eBbE#kb!kAzO1k9iW@%&V%1kN5Ma`&4eJxO zbh7W#on|C48H7eRou}noot$gxIY`~^EWy25o#SJ_V|qgEjG688IvPz+DruQrK6hn% z`=K`AdR1@-_CgU+G9&@xK7mq_>|-uXJV!g8Q1dAEZ%5&Mq1HDl#KqzXSebQDbfz28 zcDo#1#*?h&lVe%C6r)!Zc)e1uyP$rVmkvXX9oL6ien9r(YDNcdl2zd^rP!`aV+}CZ z07XE$zrg?lcEqVP*hReGcRrVs;c<;6*(PmM@Z_&cML@d0hU!VL6k1&7MQQ<$9HTx22=Y;1r)r|9x|oB- zo6N|GuK5@a1-wfWx&HN2neqJhY1iUbj}y@goIjzRD;#{N-&Y{8w+CUeN18EfgojZ0 z6%9c&jO-z#2dH+MEZE{P!?r#G0C34#E2^?=XYR%6SmG(btp)`GeSa8fPkKh1ZKi-r zwBTA*m>RJ2n9QX1aPXy91LS>9qv&OX8cX?(xDl0Z6vv`L1$8LhG45l zr-cfO2~Kp%{7N{&#qWHvJ_V96MyDRIm}|4j_y16!ljdD)f_MpCI+PuFMjEN-*47~& z3dqR%BO!mrg(ADA$x4MP#o|{b?bkuB{#Z+@pBz=y|hfxh&AmvXrCWAIJUB1(C{^kQQ87TNh8qAU7geZqz8In&4|84Y@|H&1 z8~*samVxq3B7(kV0HkCaB~m=)P*3T3%2Y2!T~%C?8u}myBgWf(T(*!yb6Ws!QIJo{ zVXe1iH7gvbC-o*(%}gDHcviNlbf&U8|*fdPMa2Hjli=Tra-r$$&p}G+L@W*`uy?T8fvl8mOanfyXqiE*L~0tm9M1e*BJDA zp>4O=w+T&jcuG6SuS<$I4e z(z(w27Xn{r)(c_yj74@}Y(JZQqE_h7DmnVfMqq;tO~`WAkfc#R>Pt3-XQ|{_mS|y= zH#rI%hq$+nOX!nImMLX3p54!$6q0|FIUq<7TWr}veSdsC{t zf~VN?N}7PwH8V(weBmG3bEfwKL>6k^s8?SZx8=|DVp!>*X9?%9_5c7hJYFPihZZYR z6ZiYbYm@y#qht_k#xT+ML1EV%lat;%f9)8yAX z6sb`={hRgGIH*Gme@;60AYNMBlB8>4stHOm;?Cbd2(FL?l>o{3usp9ix(gSu@zPXw zK_~{C{x(%wTeQsTVgQy6-t~<@rj@Y-CqL3?S8pr1qz>rM%Xi~c_V)xVHTeG^=%F~fNMO-{uyOK zrqO=ujF4ORv!QE-LH$u$tri$rOe{bjrS`)y(=8D`>1OSMty(z#A{n+U<(EfR9kt8m z8IWE6&2vK?M;T`i9TR|zP(o{4qH2mLBSzqFhB2$+5(LXO3$hAD$pds+j)wFFx>z)h z`PF%OQ$aT_|G4>-6lmHSf=R}0O_n!e{QaHbc*5i*BeF6+J-4oLt9f~epO=rA)>I0L z@hkTody{_{cH^{sSpQW*Juh{qnsf4s&yjG`iyF7@W3dLgDlqOaWhmO8gH)Sx*|+Gw zY!hT^Y4$6W;~=A+*i0a1B+T=L$|Q}Pr+44{M;JOJC6xakbfJ);gd@UXWNM)Oo#$2| zp7QG&;mZRm%QMv}hiN9lz>_R?^mhrdL04yYsZIjsTE$**iZnb#YM;AfTBtT7&TkL0 zrllZD2X9H?;7W6MkuB|MriK8Fx*_jeS`|Jiy}83Ul&!+`Rr#vS3(IH@9QHs6#5yV9D#=ou0<0$24pM51*Qy3TY zXnyn~trE)n*zK|8FPDT9Sk1k~j*)3=7voUE!Ey@dgv}AZRyZ3(H|v`18x#D};&?yu z_cE2k*uyl{(mV3BnwDz3Y6qEi>)ZTMJ4*k2mxNsavK_>!H*q4d|M+t zg3aaDgZRa(se5oHP(!$fgUY&8U?q6hT{4m-wRs@VYY8Lt(2uq}0rub1NR@7)-;42ZS9GXpjw$`)s)K`!d*=p9I31bWeS~U#HMqzSd za@W!@^^KeW*Sxi7Ly7USW9xh8PXZTW_lX}mhLdsJPI05#i!p}4zFddSi@+*o9f0ex zdWzip%;S)l%-oN9yrdBY`AT@Uozl0gX5BaKVRTYJlZlmLv01WDKd>FFi3dk2*z2(= zR_TY~lW&Kua6Y^aAGtBAfPDYT3tqD|B^1E!Zl%{kU%2A%)2YfDG>bB^22n~7{We@J zEzmO;`D?dVXg%ZKfRrWT^026|H@meg7s2b-1lm7GdO!MQ@6-YD@ZN>O_xtCY8~$5@ zodZno7Wc~RH+L)%iDh~cZaw%Xy}&@g%4mq#ErPCL7)E@awE5|X z{^65YgtHLM?CVsK_98vEw(|4$@T(4yiu=nZaVI z5?H`oEz8Ul;^Dj=9lZ-wh=*F~qfV@70Z1g6$HaRNYl7?9wW`#YJ-N_kw;W+3f-($u zf1@;+2PAP1t^t(A=~(8SRCg#pKMgk2VCxYC5Pu<(#*)KDwh(K(&ikuGh;=6+hD#_g ztKMSo$3xY<9@#hBAP7Wg`zi7n3$Gpt8LbYyoE0M27 zlUG5;8UdfgtOz(r;79aar(eOgQi5H6P(J+Ax$;$Au#fmj!w&oRrQBAfp~;-R_vP9K z+N_^GS>=>mhY5P10T-W*zf7;!$%nt_5ScQZuc>V#QB?Nb(};-n^;erYsG;wu`8zo@ zz3vOA3{uszdjQzWU-m+f^j!ge-01OKbPo~uVQ~pR;J1HJy^9`B+fYq7-h`Qxv z5cC82HTt4CgffB@#4zV9fI_yK=}Yx^@l&%zhU{no$mec>r6;r~uzMTCV)BNRK(yK+ zb<~~p_A;}jYyyiHQbu!YHPTW$k>%=j`h{9IpZt9Ra+TCh{qB z3tZqhsZ9q1#%rUYGVcX;y4#MxahMS^Lha?ecJ-^qqjl@jEb^xUeJqUjN{e7Px}eVP@wY>c zE(3XG!+d*6z8UgLy2tBN%SF=(gOx*yn% zRcMc>f*9T&BF;a}qq(p5()dH@{bhy#G#Vf4PwQa2FP#=GufuqbhDJ) zO<5S#(wPt7=RIbqLBBe5Z~tma4T(=!&sW4k@5n{wlABL~;!Hs?t)^65ezG~*l{~nf zWn3xK>z+`Ri?j2DY&%9@i0)_r8})2rP@8)5F_eHK;tK;@V5rS5BlC6y*{}zfJ=bMk zBRN&1q()HWj;W}Ic%dB8OUnPuRK70%joVbRfChawnD7let$tTubWC3H6)+YcOQNV{ z<20)8HNzJM0bGFw6Z}8k5_m`fT5c%m8yR2bl_JHztwWs03YnZ6wJ#%v#ls1eY+2o zTprXa@+??h0UCn)2^X3K7jA0SxY@awGQmEHiUH9sy}jq}wRsIDv-~QtS|}qFe;3;C zrCpSzR`$`b#+=@xXeomN2=Rf^T>+{vpWQJIuVYH{->jU*W>W2Bz9kI{*mGaDX8AOUw~r3*>N%SCVQxA?caCoKZ_S_>0*^(^+bVdmS*A-@q4JHs zk!9MPqv-V0|MiI~i**?F`hGp)#KQY4CL*bTiSG_iJ-xV7X2OVztVk6J3XYm`FV#$6 zcecv-5KavBLTQGm`DDUjZUpBwoRhR4PUsAEAd&z|{gDlh6cY`)BLkV*J6oKOFXKAd z_HtGy#_)JSac}NvJpvG?`04F9v%riV=6A^5xuuF$wDT-CB<+jQUeW3TA(l=9N;1{} z%UDC74hQ9OF=6?M`ZnasO3>tqz+>$yp&NEr%0@(=*f8ZkYQ5#}hZaukE?oRqV;}{j zwY86C46`wTSZmYi45&9YFiI`mz9b9~w~I}m9Hl)+Kq?-^D^5VnJNZVQaW@%Hj zyMDsv@5%ZNcrOgpHxPu@A@{f2K*E_v3=$bjR}nxJiKF8C`Fv7XG$_R8pE9`oMfYum zm3>8?c#pmT3=h1`BWDT&lo6TghW?ks&$_23kV4 znvTxU!PQ|+ous$if}`D>O{{AN-V$1Wm#bl1IVt~vP;Q<2k6hRN=`iQn&iQ>r0PXpo zOmfJKOmORwHPdbZ61m>oYW&%wc~DZGTPA?9uU{q7o!OLw6x;w#NJM=RdtR1-WJC~z z!G>L#HL1;^iMd}E&A$W=@%FAS9~=voSsp}|91R-90FazHznUYK&^OM z*rGa7W!M-jd=@MBZuYT4j&qk8`H8j8KX0g~7Cv1MmwiCv3&M`ugM9#dE71vJ9#N3x zSU#G&2Q-Nf#j_F$A?Na$|77K0M(0d=NZs|wX?zTAz+$X71G65phC2+Rn?{-2_Jc7g z;wu`%0N~jv7hphtx_+AKMz4)l{~~~<-kIG!i{?Ox{p#=ImT2f)Z)p8r`t_~gpB%{d zHFxO;I0^>#O;^Dx30c~7n{!; z;yso-99RTm;E%p`9;^s$kJ=(4$*^Iyd5iZN>|P2R;NfBoVv`BZhprw+=qfC94f7Ejl#`pVpTQdHMz!UZ|&vF1y_~UGwib9Ld zc%HImLX3VCzv!3pjdjJ2idLS;?-AhB5Kcl%@(U)0dP2$ho;(omv5%dv@=5>vi7U}auw{x@xlINp*|b(UY*J>bS< z$UPZI05`N@(ihRkz%xYGmw`8YJx0?8FU^iTN0XgRpe`Vxm4b1ehvlzdFLF zIP|~J35LC(e$!tin`?2M%sU_Jc`A)Mq>ojtH@^cmn~Bx0P>oC{eT=_jXW?JjTt#&L z8jDXLbUNl=VJKsRxzktbRxpAxdUTC__Yq&ra$T6YT-F*OCxK-o6G?svY!gxPVx8?S zilVIIz9;?qrx+~F9E2&0loc%XYFx@kF-jk6aG|tj7g`1^HDSe2BpDO#ixK1k_gg}0 z`rQ#1gUM0}c3acIV{5oI3JlJG>aev%80+*#^gYerSsL}c00_mHiQU-GJ(0qz1vLx|;?N4P%LjLbTAn5bS;^xqY zl+vn&9qbztB4}Yf>e^cu9gp!s9b)%{WA4S!`nAdhAfkS%G4W%#O@H zVJ6(Qr*<3rqj|7T!O{}+cDx+baXEBbLn(LKhMoioi+Mwz?lUt@mbyY_l@eM=!j1M< zEj8XW=hCR@Wb8AOb5X9stm@rTim{%7&ZxSQ>-e#(2gy+1HJ%ojo2CI#BcXz<7jqU! zYIZdDGY7pZ2HOf~ayY5+hgA$e45Y4tgoF1m=`o4nflbP~%Nhz9EqV4;u+onzch>OH z5L&;R+z5{@5I0@o{owRn%5>!^#X!N}Ta@o)sxp`d=kP(*^g1Vt#v8u=*lMBrjsq*v zQ{3IT%Q#kVjeQF;@lGdAbGJ9Su! zX;tMYR<6(4J(3j*!s^&MqI*h{WC@@nkKst?&wmwBrhNR*rkdxPSkys+5DzxV^1|(p zuBTkC@;*MVQsGr?_eqmko;e!{PC-rf6$gM8{40cg&@>Gkxgc%=54Z zS%fC1EldMg)D-}{XaHT_rt+S*G0%{ojXT?4c7}gIKl=2JPQ|IcufX_;#sY1fv>$+~ z`DS#(brWqoMYlvjklRbh3y7}6A-c(+$$m5|`>l^tt6@VmWPWYa=0lMby5glwFn z1@XM72vdZeieZqV=s3%>4VQa^AIbM=oSm@dVzry(wF>rh-H}YLnY*8l!KnmezRA|Y zF8(b2b$!?N-|vm_m`Pr#hH-ec>hy-Ye&XNCoKGkpBZMS=r(n#i;u0p&52n>JoUi(` zG#lwAyasWquWK*DF6OCqW6Kfx)?RHBot;i%t<)&o?CtlBOxOle2 zI24|-Ep_n05G#=GlTT*TLYyf!Yaj+Y6;%bknenWRT(fZ-$8;YGsm{i;K0xE>J9^)fSVcEF|x3@asU7lNmi8H)4}P? zY}To2iL{uYDv4p+OM*1$%$cFv7s;{+f`nriYurCt-K)_koKQ*P%dUD-8+pkfGq9f; z!bY#pp^c$W#jjf-#~uC-ReX`+#q1Oo2|O+2o+nWR&L+I*1Z>*)8Cir_Y2JHHVT<76 zEYlaDas#ei8Z> zdg4H3H>>z{JgI$(w9+r=bo$AQQZnBWs+3M|&j(OVWmomW9zN06MhXN+>28XR!OjyR z6|rSL`BJWF6S@6v5q^EnT}uoKTl?XR%NHOrb`Kq@II_)gjpW8D9x5Qr+0->rYR9jpm&|Dt-<2I$bQtapTWA$3Sdt zpPMCAr?P8-)M8;g8g@KYdwSG*zv+zR_f@PH13?@o<^>dfZt27eJRqR~oG{fr7hm60 zE>k8Vn}~G?gH13Q5^}$co_Xg0eoWR%xK_yM3z$XI(LQ(6!Ej->-C(a`n1?TAn|QPV zpEi`i@4QjwD_zg>Ot@^QypSSlbgA%d2M619u@P;kqM#1X)G{|dQWP#Y*$|MK*kkl+ z{$=+Mvm!NUK-+tlw*3sKDT0THm0ZDLqvk4tTm0bruCyboFJfN@xvY2XLoU5D9>|5T z0GBd6B8PD?8i3gB0X4`=4@zz~XNv`0hkzRaHERS=vY1~nNdaL}8g9$UV-O^st7BblIOGh!M_kM3X$_}?j4J5qKPY4jvFG3k&c@2OhQ$_ zgJA|IXk(-Ra!2N{WuPBR4p$+W5!beiuicQVvn&959P44>0#uUnTK|H!uhr_r;?m|| zTT6kE_L7L+%K35&Uma~WVHeVoeUn4{KvZzCsn(Xy4W~UDmZN*@)~U3AS@FAZ`?n*n zjLKvRW9(#qMhXLSbW-+;L@^2p5e|%Gp)1O&MML-L2okXsc3ne!dmhlHzkM*|B#ZLN z#R_2qfjA*SPuP9gRa?@|`ZVcrQ51kerqGO_hjW7-6*vQ88sb06p%8 zQf}o2lm!!S3R&KV@D6UC6v|FI)+t&vDTE?RK^f2gLP%+o82&RMKi#b;Z%cU+Y4e2Z zTaET`)BJgEOv#4h{qX(Vgnk!OkV;@4b@rXE#nBJ3)epz|91Ar8Zwg*A691&B8uH42 zL(G8MZ(bN*^T<;=nR3vu4|Tg~5GNpMD{g)kcxXzK7O@lY<7lC%nsWUbTj?i434Zex z8E=k6RcS-gedW8GaK?Zv;Nx+6oKh1b2PAIN6p!O3pNI8Z>ltlby(Nd37 z>2<HY)dePPT0_6B)iUroujn~=cz5CEU;zQ2r>woHIfC(&7h5%pbMfd%f2Lj zMf;1rMPv(W1;YVR-G>Ol7M9bl6vx=YJ3S69$Xjl2g7`-jgiBha6r4&0-7bZM;F44v+(hKk$ z4q)`HxF;^byeLdv)FnAMN{+4CsR9KYUROjLAI0{Npe*kPo0RG!CXm9g0b?lGJwtZZ&eN63T284zas}Sn;4_bo5LG558sGV0#wMUU{r} zyZltB5){$PM~=Q@O45mxt3seuTWk1fM*M-iDkUg)ppPOg_=Owu;9Bbn>vQ0s)IGWp zAYoRmOyZf(GQ>bJUM0RiIG*;GMA(G<-l?moOF#k6G;s}de1#-awaMoy}qUSYIi{^}Li`#0zFvDC7$bSt| zrX9mGkzC((mca}eQcm$b)SKIKm_(Iwb+=>1-lf9ELXXc2r zS_ZSbC_YO-)|(42RY}5Hl6QH2Y@b?Qro)3W{C!b}cGB%JhF))%jzQQ1Jc~!Y)}6SW zJ^beTw+c;Mhjw)X5?{k`>#f_lLhbysHNJ0gG`B2g-Ntg zGVC0navq2tDzbN_i-!C|OuZxjGiBPc?MDBOt>x13vy@k++7v?2Qrhm<3?ZKk-NDUA z=dldXQzftxQWLtkIWC=M6DIz>VWuDZRSg)+L!8Dxdn36qr-jJQHq+IZ~>$3dwvS1(bM>h#gE@PH}vG@~`K>9FSC)m$3zqsW;3 z+b2^U`}%)L3Bqp12JD-3>B=_;UB4AI;GD534{lZHiVm*d>RZW2DoHZ1Cq2fmhqS~_ zU^PlOA(O4loJ)RkNmob*?}sjb0nqdwD|1>ar!}0xYlzZxc>!L%cupbN@-NN6e@>8u z(?skuXhiG=m?s-gyeXV0tv%fAMj}zz<{OK}b^5z%M-9N40ZUyp!g}XYBNONp=p5bd z+VvbPl<6FD{1DoE3ZyrAn`jRGPW(G@cWsBuW_BIUBYJ&gkyZ2^4gT_pyND}AbWkZh zx;GPh{L?FkEdw#82F`0Wf>*=Bzp(7og`T$wE9&q7TDS5(0_~jYt*Y%P(s|+rdQ|Ae z$g|RcL3;VuiiNveOn|^O5B-r!o)K+V_3Z5K9`{MFny#E=NU>*2xg2RN_#{Xp~Jz>X?y=TkD z7!)9fT-Du;q?!ZN5b0yn2CmHKF@N1=Ig9mo95SG}g@8m&JuV)KBdcyV22&L#+(tn& zcQaTX0cikZ+M;1p(x7x6+ucQyv&`}dNnR&5$vec+i!BcSr+{m1?Rt4$O%X-` z2Yu%ChCXATR~Ywy*M+e!JCtSB=G(!tGlGDmwnRrB+DHtQzEtN2|9Mtb*V*MeZNt+r!Z>kN7c;8l)m+TsE@mO0WBnP(QS4y(dmTj z{Q&37WX8nIkj}C@R8w9dx$d7p#7T0tLoz+;NL6HytSROsEbN4M(140l9M8Tk&*-h@ z4jL#DqRmph5)|5&;Wn=TvYm-2AL0>Uwv}8b?fgH&vb^cX0Ep60#+qLtHke3Lrko;B zY6^($dw|CTX3XH!rJu8ZolOo4TkAWT%G<5?raC?jarz}torqe`d;lxx9L}cP=`b`X z!15Ax!T{SuIWYXk>iZZ8s&YSIkDr%|J;_&4F4DW)8m*Uo_W2&2sI>vamPTewz@>hf z|G&CphA6#Q0GTG(7??kZdP5G*Gvv|~qhn&e1XNx&Mv=c<%c_ZloJ$C+&HoeBPM+7L zGmF{zX|2@C;3I>@bh^=2bQ1%GT6L*9PZ!Tw>7BmiQv_D&FD`$ET-kIcY1B_1Rn-Qq zgziC6P3i6bP{41LTK4$BoeGQGyu9i(l*?_0Zw2PG>NU&ig-Bvk*iiht2V19Q!4w%VzOOGJ(kqX*F+7)Z*uGfFd5Y7`YN z)huS*!=Xw}b?VMg_YFtkG%45hLeCs+b4u)#&__S;RpTR&1SrNEG-U|l$@UaE*!VvU z81xvso@P?Hlrdgg>Jqc);A4Wwy0oB^agQyxJG=gXV|vq*$D~1!=o?|AhhK3`S1$iU zBWJ+{?`h6uZk?zU9vX^luMDZZdeerc>={8n7^PAZeHG9{2Ng`N^K(%=RHWNZ%Mzf} z9e+|INGQ28a8v`=d)6hLa# z>^N)cHo6m<7p?V4Fj?OPXi51-gyF5Ddy1_wdD+aPaW)AOr2yLXR^V(XIzYmF9^Mq2->Q)#8ZBeA!^v1sdyRC)Tx!IUN35@licvXsDcl7W3 zqOXOm>Ck@Pi(jorFEbKemq%sXFk}LWfE$_KDer2pynAz!^Bez)nHJY&vJ)0 ztMt1y_2%45bY&UX`bG63hXe8KOq|+*0gIcizA4)4t8M|7pL#{Omsr!Vd2T0A?y-XG zsmLVs!eRxh_Aei3Gh)+a*&9t2m5H=jquT470qse;euEEP7*Xa zv~kQT4I&O{sNGQ0_u((FnmI|&quuatx?pH0apU+|Qqyn(V|arF?{4^RuXVm$i3iwD zhNC&35ZOzzzekhMKzqM%8MI}k`!JUa62g}Am3E0WAq)^kog>z*5{7rX>sq3isGjL9sc`)8aDS50RXbx=?I zXI3v{>6>s1#igZMHBPHLLD%w&UoHHHZ*%-C?9zhqE{+wa#^*HWYN3WGEE@%(MvQ32 z>a>OLsyEL=vT`LR?WtR_9wyw9O*ib?5W0{>K5fB zCE^FZ@wX}k+RI=YSEmEoU^el(qq0?A{Y0q5-CuA4qeyZ`(R3e3^U*x0JR+@79rn2d z&d|S%NuqKwY7(J~3aSZ*FYacb?b?a8k*7w!J&v8O2YLLnw{HOZtqEkLqq;Q&OmqHP z=&biHZXnKiZ>G!1V{2*k@W~QJ1o#yMshHvFfl)P7IAtVcn6F7WlA&C z#FTSB(3hWIK2qSanKO?ue)g%7t8C~H0BieDTJNa7bM9oD2}X(pfH+%0STJ-B(gA$m zQZNWwG7S?8IhP>>uyF515Wc|WJU~Yev3hF@C9_@u#qgYW|8={R!xL0#W29$sf9sS^ zBbs8s=!Vy~`+=wKh|*iX<@eJ(8X1=BYXE(i>V6b1CSEf>i(=&dUM|pbRB;F%0DdS4 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 literal 0 HcmV?d00001 diff --git a/performance/fixtures/test-medium.png b/performance/fixtures/test-medium.png new file mode 100644 index 0000000000000000000000000000000000000000..0094599ffed6eb6086618b646321016a7b37e082 GIT binary patch literal 30173 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 literal 0 HcmV?d00001 diff --git a/performance/fixtures/test-small.png b/performance/fixtures/test-small.png new file mode 100644 index 0000000000000000000000000000000000000000..53913455966f686a81a9a7331749c6fe4bd5f699 GIT binary patch literal 97 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1SJ1Ryj={WG(24#Ln`LHJ-d;Yfr00c!N2-l owM=H-Gatto v !== undefined && v !== null) + .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`) + .join("&"); + + const fullUrl = qs ? `${url}?${qs}` : url; + + return http.get(fullUrl, { + headers: defaultHeaders, + tags, + }); + } else { + // POST requests: params go as JSON body + const headers = { + ...defaultHeaders, + "Content-Type": "application/json", + }; + + return http.post(url, JSON.stringify(params), { + headers, + tags, + }); + } + } + + /** + * Login with email and password. + * Returns the profile data on success. The session cookie is stored + * automatically by k6's cookie jar. + * + * @param {string} email + * @param {string} password + * @returns {object} Parsed response { status, body } + */ + function login(email, password) { + const res = rpc("POST", "login-with-password", { + email, + password, + }); + return { + status: res.status, + body: res.status === 200 ? res.json() : null, + raw: res, + }; + } + + /** + * Get the current user's profile (requires prior login). + * + * @returns {object} Parsed response { status, body } + */ + function getProfile() { + const res = rpc("GET", "get-profile"); + return { + status: res.status, + body: res.status === 200 ? res.json() : null, + raw: res, + }; + } + + /** + * Get all teams for the current user. + * + * @returns {object} Parsed response { status, body } + */ + function getTeams() { + const res = rpc("GET", "get-teams"); + return { + status: res.status, + body: res.status === 200 ? res.json() : null, + raw: res, + }; + } + + /** + * Create a new team. + * + * @param {string} name - Team name + * @returns {object} Parsed response { status, body } + */ + function createTeam(name) { + const res = rpc("POST", "create-team", { name }); + return { + status: res.status, + body: res.status === 200 ? res.json() : null, + raw: res, + }; + } + + /** + * Get projects for a team. + * + * @param {string} teamId - Team UUID + * @returns {object} Parsed response { status, body } + */ + function getProjects(teamId) { + const res = rpc("GET", "get-projects", { "team-id": teamId }); + return { + status: res.status, + body: res.status === 200 ? res.json() : null, + raw: res, + }; + } + + /** + * Create a new project. + * + * @param {string} teamId - Team UUID + * @param {string} name - Project name + * @returns {object} Parsed response { status, body } + */ + function createProject(teamId, name) { + const res = rpc("POST", "create-project", { + "team-id": teamId, + name, + }); + return { + status: res.status, + body: res.status === 200 ? res.json() : null, + raw: res, + }; + } + + /** + * Create a new file in a project. + * + * @param {string} projectId - Project UUID + * @param {string} name - File name + * @returns {object} Parsed response { status, body } + */ + function createFile(projectId, name) { + const res = rpc("POST", "create-file", { + "project-id": projectId, + name, + }); + return { + status: res.status, + body: res.status === 200 ? res.json() : null, + raw: res, + }; + } + + /** + * Get a file by ID. + * + * @param {string} fileId - File UUID + * @returns {object} Parsed response { status, body } + */ + function getFile(fileId) { + const res = rpc("GET", "get-file", { + id: fileId, + }); + return { + status: res.status, + body: res.status === 200 ? res.json() : null, + raw: res, + }; + } + + /** + * Update a file with changes. + * + * The backend uses optimistic concurrency control via `revn`. + * If a conflict occurs (status 400 with :revn-conflict), the caller + * should retry with the latest revn from getFile(). + * + * @param {string} fileId - File UUID + * @param {number} revn - Current file revision number + * @param {number} vern - Current file version number + * @param {string} sessionId - Client session ID (UUID) + * @param {Array} changes - Array of change objects + * @returns {object} Parsed response { status, body } + */ + function updateFile(fileId, revn, vern, changesSessionId, changes) { + const params = { + id: fileId, + revn: revn, + vern: vern, + "session-id": changesSessionId, + origin: "workspace", + "created-at": new Date().toISOString(), + "commit-id": uuidv4(), + changes: changes, + }; + + // update-file uses POST with id also as query param (per frontend convention) + const url = `${baseUrl}/api/main/methods/update-file?id=${encodeURIComponent(fileId)}`; + const headers = { + ...defaultHeaders, + "Content-Type": "application/json", + }; + + const res = http.post(url, JSON.stringify(params), { + headers, + tags: { rpc_command: "update-file" }, + }); + + let body = null; + try { + if (res.body && res.body.length > 0) { + body = res.json(); + } + } catch (e) { + // body may not be JSON + } + + return { + status: res.status, + body: body, + raw: res, + }; + } + + /** + * Upload a file media object using direct multipart upload. + * + * @param {string} fileId - File UUID + * @param {Uint8Array} fileBytes - The file content + * @param {string} fileName - The file name + * @param {string} mimeType - MIME type (e.g., "image/png") + * @returns {object} Parsed response { status, body } + */ + function uploadFileMediaObjectDirect(fileId, fileBytes, fileName, mimeType) { + const url = `${baseUrl}/api/main/methods/upload-file-media-object`; + + const headers = { + ...defaultHeaders, + // No Content-Type — k6 sets it automatically for multipart/form-data + }; + + const formData = { + "file-id": fileId, + "is-local": "true", + name: fileName, + content: http.file(fileBytes, fileName, mimeType), + }; + + const res = http.post(url, formData, { + headers, + tags: { rpc_command: "upload-file-media-object" }, + }); + + return { + status: res.status, + body: res.status === 200 ? res.json() : null, + raw: res, + }; + } + + // ----------------------------------------------------------------------- + // Chunked upload + // ----------------------------------------------------------------------- + + /** + * Create an upload session for chunked uploads. + * + * @param {number} totalChunks - Number of chunks + * @returns {object} { status, sessionId } + */ + function createUploadSession(totalChunks) { + const res = rpc("POST", "create-upload-session", { + "total-chunks": totalChunks, + }); + const body = res.status === 200 ? res.json() : null; + return { + status: res.status, + sessionId: body ? body.sessionId : null, + raw: res, + }; + } + + /** + * Upload a single chunk within an upload session. + * + * @param {string} sessionId - Upload session UUID + * @param {number} index - Chunk index (0-based) + * @param {Uint8Array} chunkBytes - The chunk content + * @param {string} fileName - Original file name + * @param {string} mimeType - MIME type + * @returns {object} Parsed response { status, body } + */ + function uploadChunk(sessionId, index, chunkBytes, fileName, mimeType) { + const url = `${baseUrl}/api/main/methods/upload-chunk`; + + const headers = { + ...defaultHeaders, + }; + + const formData = { + "session-id": sessionId, + index: String(index), + content: http.file(chunkBytes, fileName, mimeType), + }; + + const res = http.post(url, formData, { + headers, + tags: { rpc_command: "upload-chunk" }, + }); + + return { + status: res.status, + body: res.status === 200 ? res.json() : null, + raw: res, + }; + } + + /** + * Assemble all uploaded chunks into a final media object. + * + * @param {string} sessionId - Upload session UUID + * @param {string} fileId - File UUID + * @param {string} name - Media object name + * @param {boolean} isLocal - Whether the object is local to the file + * @param {string} mimeType - MIME type (e.g., "image/png") + * @returns {object} Parsed response { status, body } + */ + function assembleFileMediaObject(sessionId, fileId, name, isLocal, mimeType) { + const res = rpc("POST", "assemble-file-media-object", { + "session-id": sessionId, + "file-id": fileId, + name: name, + "is-local": isLocal, + mtype: mimeType, + }); + return { + status: res.status, + body: res.status === 200 ? res.json() : null, + raw: res, + }; + } + + // ----------------------------------------------------------------------- + // Smart upload — picks direct or chunked based on file size + // ----------------------------------------------------------------------- + + // Chunk size threshold: files larger than this use chunked upload. + // The actual chunk size is irrelevant to the backend; this controls + // which upload path is exercised. + const CHUNK_SIZE = 50 * 1024; // 50 KB + + /** + * Upload a file media object, automatically selecting direct or chunked + * upload based on file size. + * + * Files <= CHUNK_SIZE use direct multipart upload. + * Files > CHUNK_SIZE use chunked upload (create-upload-session → + * upload-chunk × N → assemble-file-media-object). + * + * @param {string} fileId - File UUID + * @param {Uint8Array} fileBytes - The file content + * @param {string} fileName - The file name + * @param {string} mimeType - MIME type (e.g., "image/png") + * @returns {object} Parsed response { status, body } + */ + function uploadFileMediaObject(fileId, fileBytes, fileName, mimeType) { + if (fileBytes.byteLength <= CHUNK_SIZE) { + return uploadFileMediaObjectDirect(fileId, fileBytes, fileName, mimeType); + } + + // Chunked upload path + const totalChunks = Math.ceil(fileBytes.byteLength / CHUNK_SIZE); + + const sessionRes = createUploadSession(totalChunks); + if (sessionRes.status !== 200) { + return { status: sessionRes.status, body: null, raw: sessionRes.raw }; + } + const uploadSessionId = sessionRes.sessionId; + + for (let i = 0; i < totalChunks; i++) { + const start = i * CHUNK_SIZE; + const end = Math.min(start + CHUNK_SIZE, fileBytes.byteLength); + const chunk = fileBytes.slice(start, end); + + const chunkRes = uploadChunk(uploadSessionId, i, chunk, fileName, mimeType); + if (chunkRes.status !== 200) { + return { status: chunkRes.status, body: null, raw: chunkRes.raw }; + } + } + + return assembleFileMediaObject(uploadSessionId, fileId, fileName, true, mimeType); + } + + /** + * Delete a file. + * + * @param {string} fileId - File UUID + * @returns {object} Parsed response { status } + */ + function deleteFile(fileId) { + const res = rpc("POST", "delete-file", { id: fileId }); + return { + status: res.status, + raw: res, + }; + } + + /** + * Delete a project. + * + * @param {string} projectId - Project UUID + * @returns {object} Parsed response { status } + */ + function deleteProject(projectId) { + const res = rpc("POST", "delete-project", { id: projectId }); + return { + status: res.status, + raw: res, + }; + } + + /** + * Delete a team. + * + * @param {string} teamId - Team UUID + * @returns {object} Parsed response { status } + */ + function deleteTeam(teamId) { + const res = rpc("POST", "delete-team", { id: teamId }); + return { + status: res.status, + raw: res, + }; + } + + /** + * Logout the current user. + * + * @param {string} profileId - Profile UUID + * @returns {object} Parsed response { status } + */ + function logout(profileId) { + const res = rpc("POST", "logout", { "profile-id": profileId }); + return { + status: res.status, + raw: res, + }; + } + + // Return the client interface + return { + sessionId, + externalSessionId, + rpc, + login, + getProfile, + getTeams, + createTeam, + getProjects, + createProject, + createFile, + getFile, + updateFile, + uploadFileMediaObject, + uploadFileMediaObjectDirect, + createUploadSession, + uploadChunk, + assembleFileMediaObject, + deleteFile, + deleteProject, + deleteTeam, + logout, + }; +} diff --git a/performance/run.sh b/performance/run.sh new file mode 100755 index 0000000000..48f890e202 --- /dev/null +++ b/performance/run.sh @@ -0,0 +1,160 @@ +#!/usr/bin/env bash +# +# Penpot Performance Tests +# +# k6-based load/performance test suite for the Penpot backend. +# +# Prerequisites: +# - k6 (https://k6.io/) installed and in PATH +# - A running Penpot backend (local devenv or remote) +# +# Usage: +# ./run.sh smoke # 1 VU, 1 iteration smoke test +# ./run.sh lifecycle # 1 VU, 1 iteration (defaults) +# ./run.sh lifecycle -v 10 -n 5 # 10 VUs, 5 iterations each +# ./run.sh lifecycle -u http://remote:6060 -m register -v 5 +# ./run.sh clean # Remove test results + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# --------------------------------------------------------------------------- +# Defaults +# --------------------------------------------------------------------------- + +BASE_URL="${PENPOT_BASE_URL:-http://localhost:6060}" +VUS=1 +ITER=1 +REGISTER_MODE="${PENPOT_REGISTER_MODE:-demo}" +K6="${K6:-k6}" + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +usage() { + cat < [options] + +Commands: + smoke 1 VU, 1 iteration smoke test (forces demo mode) + lifecycle Full user lifecycle test + clean Remove test results + help Show this help + +Options (lifecycle only): + -u URL Backend base URL (default: $BASE_URL) + -v NUM Number of virtual users (default: $VUS) + -n NUM Iterations per VU (default: $ITER) + -m MODE Register mode: 'demo' or 'register' (default: $REGISTER_MODE) + -k PATH Path to k6 binary (default: $K6) + +Environment variables: + PENPOT_BASE_URL Same as -u + PENPOT_REGISTER_MODE Same as -m + K6 Same as -k + +Examples: + $(basename "$0") smoke + $(basename "$0") lifecycle -v 10 -n 5 + $(basename "$0") lifecycle -m register -v 5 -n 1 + $(basename "$0") lifecycle -u https://penpot.example.com +EOF +} + +check_k6() { + if ! command -v "$K6" &>/dev/null; then + echo "Error: k6 not found at '$K6'" >&2 + echo "Install from https://k6.io/docs/get-started/installation/" >&2 + exit 1 + fi +} + +run_k6() { + local results_dir="$SCRIPT_DIR/results/$(date +%Y%m%d-%H%M%S)" + mkdir -p "$results_dir" + + echo "Penpot Performance Test" + echo " Base URL: $BASE_URL" + echo " Register mode: $REGISTER_MODE" + echo " VUs: $VUS" + echo " Iterations: $ITER" + echo " Results: $results_dir" + echo "" + + "$K6" run \ + --env "PENPOT_BASE_URL=$BASE_URL" \ + --env "PENPOT_REGISTER_MODE=$REGISTER_MODE" \ + --vus "$VUS" \ + --iterations "$ITER" \ + --out "json=$results_dir/k6-summary.json" \ + "$SCRIPT_DIR/scripts/lifecycle.js" +} + +# --------------------------------------------------------------------------- +# Commands +# --------------------------------------------------------------------------- + +cmd_smoke() { + check_k6 + REGISTER_MODE=demo + VUS=1 + ITER=1 + run_k6 +} + +cmd_lifecycle() { + # Parse options + while getopts "u:v:n:m:k:h" opt; do + case "$opt" in + u) BASE_URL="$OPTARG" ;; + v) VUS="$OPTARG" ;; + n) ITER="$OPTARG" ;; + m) REGISTER_MODE="$OPTARG" ;; + k) K6="$OPTARG" ;; + h) usage; exit 0 ;; + *) usage >&2; exit 1 ;; + esac + done + + check_k6 + run_k6 +} + +cmd_clean() { + local results_dir="$SCRIPT_DIR/results" + if [[ -d "$results_dir" ]]; then + rm -rf "$results_dir" + echo "Cleaned $results_dir" + else + echo "Nothing to clean" + fi +} + +# --------------------------------------------------------------------------- +# Main +# --------------------------------------------------------------------------- + +if [[ $# -lt 1 ]]; then + usage >&2 + exit 1 +fi + +command="$1" +shift + +case "$command" in + smoke) cmd_smoke "$@" ;; + lifecycle) cmd_lifecycle "$@" ;; + clean) cmd_clean "$@" ;; + help|-h|--help) usage ;; + *) + echo "Unknown command: $command" >&2 + usage >&2 + exit 1 + ;; +esac diff --git a/performance/scripts/lifecycle.js b/performance/scripts/lifecycle.js new file mode 100644 index 0000000000..8ba13f06e4 --- /dev/null +++ b/performance/scripts/lifecycle.js @@ -0,0 +1,417 @@ +// Lifecycle Performance Test +// +// Simulates a realistic user lifecycle from registration through CRUD operations. +// Each VU performs the full flow independently, creating its own artifacts. +// +// Flow: +// 1. Register (via demo profile or prepare+register) +// 2. Login +// 3. Get profile & teams +// 4. Create project +// 5. Create file +// 6. Get file +// 7. Update file (add a shape) +// 8. Upload image to file +// 9. Delete file +// 10. Delete project +// 11. Delete team +// +// Usage: +// k6 run scripts/lifecycle.js +// k6 run --env PENPOT_BASE_URL=http://localhost:6060 scripts/lifecycle.js +// k6 run --env PENPOT_REGISTER_MODE=register scripts/lifecycle.js + +import { check, sleep, fail } from "k6"; +import { uuidv4 } from "https://jslib.k6.io/k6-utils/1.4.0/index.js"; +import { createClient } from "../lib/penpot-client.js"; + +// --------------------------------------------------------------------------- +// Configuration +// --------------------------------------------------------------------------- + +const BASE_URL = __ENV.PENPOT_BASE_URL || "http://localhost:6060"; +// "demo" = use create-demo-profile (requires demo-users flag) +// "register" = use prepare-register-profile + register-profile +const REGISTER_MODE = __ENV.PENPOT_REGISTER_MODE || "demo"; + +// k6 options — smoke test defaults (1 VU, 1 iteration) +export const options = { + scenarios: { + lifecycle: { + executor: "per-vu-iterations", + vus: 1, + iterations: 1, + maxDuration: "2m", + }, + }, + thresholds: { + http_req_duration: ["p(95)<5000"], + http_req_failed: ["rate<0.01"], + "http_req_duration{rpc_command:login-with-password}": ["p(95)<1000"], + "http_req_duration{rpc_command:get-profile}": ["p(95)<500"], + "http_req_duration{rpc_command:create-project}": ["p(95)<1000"], + "http_req_duration{rpc_command:create-file}": ["p(95)<1000"], + "http_req_duration{rpc_command:get-file}": ["p(95)<500"], + "http_req_duration{rpc_command:update-file}": ["p(95)<2000"], + "http_req_duration{rpc_command:delete-file}": ["p(95)<1000"], + }, +}; + +// --------------------------------------------------------------------------- +// Test Data +// --------------------------------------------------------------------------- + +// Load test PNG fixtures — small uses direct upload, large uses chunked upload +const testImageSmall = open("../fixtures/test-small.png", "b"); +const testImageLarge = open("../fixtures/test-large.png", "b"); + +// A minimal "add-obj" change payload for update-file. +// This adds a simple rectangle shape to the first page. +// All object properties use camelCase — the backend's JSON parser +// (json/read-kebab-key) converts camelCase to kebab-case keywords automatically. +function makeAddRectChange(pageId) { + const shapeId = uuidv4(); + const x = 100; + const y = 100; + const w = 200; + const h = 150; + + return { + type: "add-obj", + pageId: pageId, + id: shapeId, + frameId: pageId, // required: the frame this shape belongs to + parentId: pageId, // root frame is the page itself + obj: { + id: shapeId, + type: "rect", + name: "Perf Test Rect", + x: x, + y: y, + width: w, + height: h, + fillColor: "#ff0000", + fillOpacity: 1, + rotation: 0, + hidden: false, + locked: false, + // Required base attrs + selrect: { + x: x, + y: y, + width: w, + height: h, + x1: x, + y1: y, + x2: x + w, + y2: y + h, + }, + points: [ + { x: x, y: y }, + { x: x + w, y: y }, + { x: x + w, y: y + h }, + { x: x, y: y + h }, + ], + transform: { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, + transformInverse: { a: 1, b: 0, c: 0, d: 1, e: 0, f: 0 }, + parentId: pageId, + frameId: pageId, + }, + }; +} + +// --------------------------------------------------------------------------- +// Helpers +// --------------------------------------------------------------------------- + +function assertOk(res, label) { + const ok = check(res, { + [`${label} — status is 2xx`]: (r) => r.status >= 200 && r.status < 300, + }); + if (!ok) { + // Try to get the response body for debugging + let bodyStr = ""; + try { + if (res.raw && res.raw.body) { + bodyStr = typeof res.raw.body === "string" + ? res.raw.body.substring(0, 500) + : JSON.stringify(res.raw.body).substring(0, 500); + } else if (res.body) { + bodyStr = JSON.stringify(res.body).substring(0, 500); + } + } catch (e) { + bodyStr = "(could not read body)"; + } + console.error( + `FAIL: ${label} — status=${res.status} body=${bodyStr}` + ); + } + return ok; +} + +// --------------------------------------------------------------------------- +// Setup — runs once before VUs start +// --------------------------------------------------------------------------- + +export function setup() { + console.log(`Penpot Lifecycle Test`); + console.log(` Base URL: ${BASE_URL}`); + console.log(` Register mode: ${REGISTER_MODE}`); + console.log(``); + + // Verify the backend is reachable + const client = createClient(BASE_URL); + const res = client.getProfile(); + // We expect 401/403 (not logged in) — anything else means the backend is down + if (res.status === 0) { + fail(`Backend unreachable at ${BASE_URL}`); + } + + return { baseUrl: BASE_URL, registerMode: REGISTER_MODE }; +} + +// --------------------------------------------------------------------------- +// Main VU Function +// --------------------------------------------------------------------------- + +export default function (data) { + const client = createClient(data.baseUrl); + + // ---- Step 0: Create a user account ---- + let email, password; + + if (data.registerMode === "demo") { + // Demo mode: create-demo-profile returns email + password + const demoRes = client.rpc("POST", "create-demo-profile", {}); + if (!assertOk(demoRes, "create-demo-profile")) { + fail("Failed to create demo profile"); + } + const demoBody = demoRes.json(); + email = demoBody.email; + password = demoBody.password; + console.log(`VU ${__VU}: Created demo profile: ${email}`); + } else { + // Register mode: prepare + register + email = `perf-${uuidv4()}@test.local`; + password = "PerfTest1234!"; + const fullname = `Perf User ${__VU}`; + + const prepareRes = client.rpc("POST", "prepare-register-profile", { + fullname, + email, + password, + }); + if (!assertOk(prepareRes, "prepare-register-profile")) { + fail("Failed to prepare registration"); + } + const prepareBody = prepareRes.json(); + const token = prepareBody.token; + + const registerRes = client.rpc("POST", "register-profile", { + token, + }); + if (!assertOk(registerRes, "register-profile")) { + fail("Failed to register profile"); + } + console.log(`VU ${__VU}: Registered profile: ${email}`); + } + + sleep(1); + + // ---- Step 1: Login ---- + const loginRes = client.login(email, password); + if (!assertOk(loginRes, "login-with-password")) { + fail("Login failed"); + } + const profile = loginRes.body; + const profileId = profile.id; + console.log(`VU ${__VU}: Logged in, profile-id=${profileId}`); + + sleep(1); + + // ---- Step 2: Get profile ---- + const profileRes = client.getProfile(); + if (!assertOk(profileRes, "get-profile")) { + fail("get-profile failed"); + } + + sleep(0.5); + + // ---- Step 3: Get teams ---- + const teamsRes = client.getTeams(); + if (!assertOk(teamsRes, "get-teams")) { + fail("get-teams failed"); + } + const teams = teamsRes.body; + // teams is an array; the user has a default team from registration + const defaultTeamId = Array.isArray(teams) && teams.length > 0 + ? teams[0].id + : null; + + if (!defaultTeamId) { + fail("No default team found after registration"); + } + + sleep(0.5); + + // ---- Step 4: Create a project ---- + const projectName = `Perf Project ${uuidv4().substring(0, 8)}`; + const projectRes = client.createProject(defaultTeamId, projectName); + if (!assertOk(projectRes, "create-project")) { + fail("create-project failed"); + } + const project = projectRes.body; + const projectId = project.id; + console.log(`VU ${__VU}: Created project: ${projectId}`); + + sleep(1); + + // ---- Step 5: Create a file ---- + const fileName = `Perf File ${uuidv4().substring(0, 8)}`; + const fileRes = client.createFile(projectId, fileName); + if (!assertOk(fileRes, "create-file")) { + fail("create-file failed"); + } + const file = fileRes.body; + const fileId = file.id; + console.log(`VU ${__VU}: Created file: ${fileId}`); + + sleep(1); + + // ---- Step 6: Get the file (to read revn, vern, page-id) ---- + const getFileRes = client.getFile(fileId); + if (!assertOk(getFileRes, "get-file")) { + fail("get-file failed"); + } + const fileData = getFileRes.body; + const revn = fileData.revn; + const vern = fileData.vern; + + // Extract the first page-id from the file data + // fileData.data.pages is an array of page UUIDs + // fileData.data.pages-index is a map of page-id -> page objects + let pageId = null; + if (fileData.data && fileData.data.pages && fileData.data.pages.length > 0) { + pageId = fileData.data.pages[0]; + } + + if (!pageId) { + console.warn(`VU ${__VU}: Could not find page-id in file data, skipping update-file`); + } + + sleep(1); + + // ---- Step 7: Update file (add a rectangle shape) ---- + let updateOk = false; + if (pageId) { + const changes = [makeAddRectChange(pageId)]; + const updateRes = client.updateFile(fileId, revn, vern, client.sessionId, changes); + + if (updateRes.status === 200) { + updateOk = true; + console.log(`VU ${__VU}: Updated file successfully`); + } else { + // Check for revn conflict — retry once + const body = updateRes.body; + console.error(`VU ${__VU}: update-file failed: status=${updateRes.status} body=${JSON.stringify(body).substring(0, 500)}`); + const isRevnConflict = + body && (body.code === "revn-conflict" || body.type === "revn-conflict"); + + if (isRevnConflict) { + console.log(`VU ${__VU}: Revn conflict, retrying...`); + // Fetch latest file state + const retryFileRes = client.getFile(fileId); + if (assertOk(retryFileRes, "get-file (retry)")) { + const retryData = retryFileRes.body; + const retryRes = client.updateFile( + fileId, + retryData.revn, + retryData.vern, + client.sessionId, + changes + ); + if (assertOk(retryRes, "update-file (retry)")) { + updateOk = true; + console.log(`VU ${__VU}: Updated file on retry`); + } + } + } else { + console.error( + `VU ${__VU}: update-file failed: status=${updateRes.status} body=${JSON.stringify(body).substring(0, 300)}` + ); + } + } + } + + sleep(1); + + // ---- Step 8: Upload images to the file ---- + // Small image (97 B) uses direct multipart upload. + // Large image (120 KB) uses chunked upload (create-session → upload-chunk × N → assemble). + if (testImageSmall && testImageSmall.byteLength > 0) { + const uploadRes = client.uploadFileMediaObject( + fileId, + testImageSmall, + "test-small.png", + "image/png" + ); + if (assertOk(uploadRes, "upload-file-media-object (direct)")) { + console.log(`VU ${__VU}: Uploaded small image (direct)`); + } + } + + sleep(0.5); + + if (testImageLarge && testImageLarge.byteLength > 0) { + const uploadRes = client.uploadFileMediaObject( + fileId, + testImageLarge, + "test-large.png", + "image/png" + ); + if (assertOk(uploadRes, "upload-file-media-object (chunked)")) { + console.log(`VU ${__VU}: Uploaded large image (chunked)`); + } + } + + sleep(1); + + // ---- Step 9: Delete the file ---- + const deleteFileRes = client.deleteFile(fileId); + if (assertOk(deleteFileRes, "delete-file")) { + console.log(`VU ${__VU}: Deleted file: ${fileId}`); + } + + sleep(0.5); + + // ---- Step 10: Delete the project ---- + const deleteProjectRes = client.deleteProject(projectId); + if (assertOk(deleteProjectRes, "delete-project")) { + console.log(`VU ${__VU}: Deleted project: ${projectId}`); + } + + sleep(0.5); + + // ---- Step 11: Delete the team ---- + // Note: We only delete the team if it's NOT the default team. + // The default team cannot be deleted (or may cause errors). + // For this test, we skip team deletion to avoid errors. + // In a real scenario, we'd create a separate team and delete that. + console.log(`VU ${__VU}: Skipping team deletion (using default team)`); + + sleep(0.5); + + // ---- Step 12: Logout ---- + const logoutRes = client.logout(profileId); + console.log(`VU ${__VU}: Logout status: ${logoutRes.status}`); + + console.log(`VU ${__VU}: Lifecycle complete`); +} + +// --------------------------------------------------------------------------- +// Teardown — runs once after all VUs finish +// --------------------------------------------------------------------------- + +export function teardown(data) { + console.log("Lifecycle test complete."); +} diff --git a/plans/2026-06-12-backend-performance-test.md b/plans/2026-06-12-backend-performance-test.md new file mode 100644 index 0000000000..e2e63f6baf --- /dev/null +++ b/plans/2026-06-12-backend-performance-test.md @@ -0,0 +1,494 @@ +# Backend Performance Test Plan + +**Context:** Build a k6-based load/performance test suite that simulates realistic browser-to-backend HTTP flows for distinct Penpot user operations. The goal is to measure backend impact (latency, throughput, error rates, resource saturation) under synthetic user load. **Browser rendering performance is explicitly out of scope.** WebSocket testing is deferred. + +**Date:** 2026-06-12 +**Validated Requirements:** +- Tool: **k6** (confirmed). +- Environment: flexible — local devenv first, then remote staging/perf. +- Target scale: **1000 concurrent VUs** (ramping from lower baselines). +- Flows: **realistic CRUD lifecycle** — create, edit, upload, delete. Must include **image upload** and **font upload**. +- `update-file` is important but difficult because it requires **2–3 concurrent users editing the same file**, and **file size matters**. +- WebSocket: **deferred**. + +--- + +## Current Progress + +### Completed (2026-06-12) + +Phase 1 is done. Phase 2 has the first flow (lifecycle) implemented and validated. + +**What was built:** + +``` +performance/ +├── run.sh # Bash runner script (smoke, lifecycle, clean, help) +├── README.md # Usage docs, configuration, architecture notes +├── lib/ +│ └── penpot-client.js # 388 lines — shared k6 HTTP client module +├── scripts/ +│ └── lifecycle.js # 398 lines — full user lifecycle test +├── fixtures/ +│ ├── test-small.png # 97 B +│ ├── test-medium.png # 30 KB +│ └── test-large.png # 120 KB +├── results/ # k6 JSON output (gitignored) +└── baselines/ # for regression baselines +``` + +**What the lifecycle test covers (all passing):** + +| Step | RPC Command | Method | Observed Latency | +|------|------------|--------|-----------------| +| 1. Register | `create-demo-profile` | POST | ~136ms | +| 2. Login | `login-with-password` | POST | ~136ms | +| 3. Get profile | `get-profile` | GET | ~7ms | +| 4. Get teams | `get-teams` | GET | ~6ms | +| 5. Create project | `create-project` | POST | ~9ms | +| 6. Create file | `create-file` | POST | ~22ms | +| 7. Get file | `get-file` | GET | ~12ms | +| 8. Update file | `update-file` | POST | ~25ms | +| 9. Upload image | `upload-file-media-object` | POST (multipart) | ~7ms | +| 10. Delete file | `delete-file` | POST | ~12ms | +| 11. Delete project | `delete-project` | POST | ~5ms | +| 12. Logout | `logout` | POST | ~4ms | + +Smoke test result: **10/10 checks pass, 0% failure rate, ~10s total iteration**. + +**Key discoveries during implementation:** + +1. **JSON transport works.** The backend accepts `Content-Type: application/json` for POST bodies (kebab-case keys auto-converted) and returns `application/json` responses (camelCase keys) via `Accept: application/json` or `_fmt=json` query param. No Transit encoder needed in k6. + +2. **`create-file` `features` param.** Sending `features: []` (empty array) causes a 400 validation error. Omit the field entirely — it's optional in the schema (`backend/src/app/rpc/commands/files_create.clj`). + +3. **`update-file` `changes` payload — shape schema is strict.** The `add-obj` change requires a fully valid shape object. Minimum required fields beyond the basics: + - `selrect`: `{x, y, width, height, x1, y1, x2, y2}` + - `points`: array of 4 `{x, y}` corner points + - `transform` / `transform-inverse`: identity matrix `{a:1, b:0, c:0, d:1, e:0, f:0}` + - `parent-id` and `frame-id` inside the `obj` + - `frame-id` at the top level of the change object itself (required, not optional) + - Schema defined in `common/src/app/common/files/changes.cljc` (line 189) and `common/src/app/common/types/shape.cljc` (line 165). + +4. **`update-file` URL convention.** The frontend sends `id` both as a query param and in the POST body. The URL is `POST /api/main/methods/update-file?id=`. + +5. **Two registration modes work:** + - `demo` mode: `create-demo-profile` — fast, requires `demo-users` feature flag. + - `register` mode: `prepare-register-profile` + `register-profile` — two-step flow, works without flags. + +6. **k6 installed at** `/home/penpot/.local/bin/k6` (v0.56.0). Not in default PATH; use `PATH="/home/penpot/.local/bin:$PATH"` or set `K6` env var. + +### Remaining Work + +| Phase | Status | Next Actions | +|-------|--------|-------------| +| Phase 1 – Discovery & Tooling | **Done** | — | +| Phase 2 – Core HTTP Flows | **Partial** — lifecycle.js done | Write `workspace-open.js`, `workspace-edit.js`, `media-upload.js`, `font-upload.js`, `viewer.js`, `export.js` | +| Phase 2 – Data seeding | **Not started** | Create `seed-data.js` for 100+ user pool | +| Phase 3 – Scenarios | **Not started** | Define multi-scenario k6 config mixing flows | +| Phase 4 – Advanced update-file | **Not started** | File size tiers, concurrent editing matrix | +| Phase 5 – CI & Reporting | **Not started** | Grafana dashboards, regression guard | + +### Immediate Next Steps + +1. Write `workspace-open.js` — read-heavy flow (get-file, get-file-libraries, thumbnails). +2. Write `workspace-edit.js` — write-heavy loop (get-file + update-file per VU, independent files). +3. Write `media-upload.js` — direct + chunked upload flows. +4. Write `font-upload.js` — chunked upload + create-font-variant. +5. Write `viewer.js` — get-view-only-bundle + comments. +6. Create `seed-data.js` — pre-seed 100+ users/teams/files for multi-VU runs. +7. Define scenario mix in `run.sh` (multi-scenario support with ramping VUs). +8. Add `--scenario` flag to `run.sh` to select individual flows or the full mix. + +--- + +## Affected Modules + +| Module | Why it is involved | +|--------|---------------------| +| `backend/` | Target system. All HTTP RPC (`/api/main/methods/*`), auth, storage, media processing, DB, and Prometheus metrics (`/metrics`). | +| `frontend/` | Source of truth for user request flows. We inspect `app.main.repo` (RPC client), `app.main.data.*` (user flows), and `app.main.data.persistence` (save semantics). | +| `common/` | Shared schemas, Transit helpers, and data structures. Used to understand valid `update-file` `changes` payloads. | + +--- + +## Approach + +### Phase 1 – Discovery & Tooling (Days 1–2) + +#### 1.1. Read the frontend RPC flows to build a request catalog + +Inspect these files to map every user action to its RPC command: + +- `frontend/src/app/main/repo.cljs` — HTTP client conventions (headers, retry, GET vs POST rules, query params, form-data, multipart). +- `frontend/src/app/main/data/dashboard.cljs` — Dashboard init (`get-projects`, `fetch-fonts`, `search-files`). +- `frontend/src/app/main/data/workspace.cljs` — Workspace init (`get-file`, `get-file-libraries`, `get-file-object-thumbnails`, `resolve-file` via `get-file-fragment`). +- `frontend/src/app/main/data/persistence.cljs` — File save flow (`update-file` with `changes`, `revn`, `session-id`, debounce/buffer logic). +- `frontend/src/app/main/data/viewer.cljs` — Viewer flow (`get-view-only-bundle`). +- `frontend/src/app/main/data/comments.cljs` — Comment thread fetch (`get-comment-threads`). +- `frontend/src/app/main/data/media.cljs` / `upload.cljs` — Media upload flows (`upload-file-media-object`, `create-upload-session`, `upload-chunk`, `assemble-file-media-object`). +- `frontend/src/app/main/data/fonts.cljs` — Font upload flow (`create-font-variant` with `:uploads` map). +- `frontend/src/app/main/data/team.cljs` — Team creation (`create-team`), invitation (`create-team-invitations`). +- `frontend/src/app/main/data/project.cljs` — Project creation (`create-project`). + +**Goal:** produce a **Request Catalog** mapping user actions to RPC command names, HTTP methods, payload shapes, and required preconditions (e.g., `team-id`, `file-id`). + +#### 1.2. Confirm JSON compatibility for the test harness + +The backend middleware (`app.http.middleware`) supports `application/json` request bodies and `application/json` responses (via `_fmt=json` or `Accept: application/json`). + +- **Action:** Send a manual `curl` to `POST /api/main/methods/login-with-password` with `Content-Type: application/json` and verify the response format. +- **Action:** Verify `GET /api/main/methods/get-profile` with `Accept: application/json` returns plain JSON. +- **Action:** Verify `POST /api/main/methods/update-file` with `Content-Type: application/json` and `_fmt=json` works. +- **Action:** Verify `POST /api/main/methods/upload-file-media-object` with `multipart/form-data` works (k6 supports this natively). + +#### 1.3. Set up the load testing directory and shared client + +Create a directory `performance/` at the repo root. + +Install **k6** (`k6` CLI or Docker image). + +Create a shared `penpot-client.js` module that wraps: +- `login(email, password)` → returns session cookie / token. +- `rpc(cmd, params, opts)` → builds the correct URL, headers, body, and query params. +- `uploadFileMediaObject(fileId, filePath, name)` → multipart upload. +- `createUploadSession(totalChunks)` → chunked upload setup. +- `uploadChunk(sessionId, index, chunkBytes)` → multipart chunk upload. +- `assembleFileMediaObject(sessionId, fileId, name, isLocal)` → finalize chunked upload. + +**Headers to replicate (critical for backend telemetry and session binding):** +- `x-session-id`: generated UUID per VU (must be consistent across requests for the same session). +- `x-external-session-id`: generated UUID per VU. +- `x-event-origin`: a string origin (e.g., `"perf-test"`) +- `accept`: `application/json` (for HTTP-only load path) +- `content-type`: `application/json` (or `multipart/form-data` for uploads) +- `credentials: "include"` (for cookie jar) + +#### 1.4. Data seeding strategy for 1000 VU scale + +Creating 1000 users/teams/files *inside* the load test is too slow and will distort the results. + +**Recommended approach:** +- **Setup Phase (k6 `setup()`):** Run a pre-test script that creates a shared pool of test artifacts. + - Use `login-with-password` with a fixture account (e.g., `profile1@example.com` / `123123` if fixtures exist). + - Create `N` teams, `N` projects, `N` files of varying sizes (see **File Size Tiers** below). + - Export the IDs into a JSON file that k6 `setup()` reads. +- **Alternative:** Use the backend REPL / fixtures (`app.cli.fixtures/run {:preset :small}`) to create fixture data, then export the IDs via a small Clojure script. +- **Data pool per VU:** Each VU picks a random user from the pool, or uses a dedicated user (e.g., VU #1 → `profile1@example.com`, VU #2 → `profile2@example.com`). For 1000 VUs, we need at least 1000 pre-seeded users. +- **Cleanup:** A post-test script can delete the seeded data, or we can use a dedicated perf DB that is reset between runs. + +**Action:** Document the seeding procedure in `performance/README.md` and create a `seed-data.js` script. + +--- + +### Phase 2 – Core HTTP Flow Scripts (Days 3–5) + +Create one k6 script per user flow. Each script: +- Uses `setup()` to read the shared data pool and log in. +- Uses `vu` iterations to simulate the flow. +- Tags every request with the RPC command name so k6 metrics are sliced by endpoint. +- Uses `check()` assertions for HTTP 200 and valid JSON structure. + +#### Flow 1: Realistic User Lifecycle (`lifecycle.js`) + +This is the primary realistic flow. Each VU performs a full lifecycle: + +1. **Auth** + - `POST /api/main/methods/login-with-password` → `{email, password}` + - `GET /api/main/methods/get-profile` + - `GET /api/main/methods/get-teams` + +2. **Create Team** + - `POST /api/main/methods/create-team` → `{name: "Perf Team "}` + - `GET /api/main/methods/get-team?team-id=` + +3. **Create Project** + - `POST /api/main/methods/create-project` → `{team-id, name}` + - `GET /api/main/methods/get-project?id=` + +4. **Create File** + - `POST /api/main/methods/create-file` → `{project-id, name, features}` + - `GET /api/main/methods/get-file?id=&features=<...>` + +5. **Edit File (Simple Update)** + - `POST /api/main/methods/update-file` with a minimal `changes` payload. + - **Changes payload:** Use a simple change like `{:type "add-obj", :id "", :page-id "", :parent-id "", :obj {:type "rect", ...}}`. Inspect `app.common.files.changes` for the exact schema. For a load test, we only need the shape to be structurally valid; the backend validates it. + - **Revn tracking:** Fetch the file first, read `revn`, then send `revn` in the update. If a conflict occurs (`409` or `:revn-conflict` error), retry once with the latest `revn`. + +6. **Upload Image (Direct)** + - `POST /api/main/methods/upload-file-media-object` (multipart) + - Payload: `file-id`, `is-local: true`, `name`, `content` (the file bytes). + - Use a small dummy PNG/SVG (e.g., 1 KB, 100 KB, 1 MB) stored in `performance/fixtures/`. + +7. **Upload Image (Chunked)** + - `POST /api/main/methods/create-upload-session` → `{total-chunks: N}` + - Loop `N` times: `POST /api/main/methods/upload-chunk` (multipart, `session-id`, `index`, `chunk`) + - `POST /api/main/methods/assemble-file-media-object` → `{session-id, file-id, name, is-local}` + - Use a larger dummy file (e.g., 5 MB) to stress the chunked pipeline. + +8. **Upload Font** + - `POST /api/main/methods/create-upload-session` (chunked, because fonts can be large) + - `POST /api/main/methods/upload-chunk` for each chunk + - `POST /api/main/methods/create-font-variant` → `{team-id, font-id, font-family, font-weight, font-style, uploads: {"font/ttf": ""}}` + - Use a small real TTF/OTF file from `performance/fixtures/`. + +9. **Delete File** + - `DELETE /api/main/methods/delete-file` (verify the exact method name; it may be `update-file` with a deletion flag or a dedicated command). Inspect `frontend/src/app/main/data/dashboard.cljs` for the delete action. + +10. **Delete Project** + - `DELETE /api/main/methods/delete-project?id=` + +11. **Delete Team** + - `POST /api/main/methods/delete-team?id=` + +12. **Logout** + - (Optional; session cookie expiry is usually sufficient) + +**Pacing:** Add `sleep()` between steps to simulate realistic think time (e.g., 1–3 seconds between dashboard navigation, 3–5 seconds between edits). + +#### Flow 2: Workspace Open (Read-heavy) (`workspace-open.js`) + +For 1000 VUs, most will be read-only viewers or editors opening files. + +1. Login (reuse token from `setup`). +2. `GET /api/main/methods/get-file?id=&features=<...>` +3. `GET /api/main/methods/get-file-libraries?file-id=` +4. For each library: `GET /api/main/methods/get-file?id=` +5. `GET /api/main/methods/get-file-object-thumbnails?file-id=` +6. `GET /api/main/methods/get-file-data-for-thumbnail?file-id=&page-id=&object-id=` + +**Data:** Use a pool of files of varying sizes (see **File Size Tiers**). + +#### Flow 3: Workspace Edit (Write-heavy) (`workspace-edit.js`) + +**Scenario A — Independent editors (default, easiest to scale):** +- Each VU creates its own file in `setup()`, or picks a dedicated file from the pool. +- Loop: + 1. `GET /api/main/methods/get-file?id=` (to refresh `revn`) + 2. `POST /api/main/methods/update-file` with minimal changes + 3. `sleep(3)` +- This measures the latency of the save path without concurrency conflicts. + +**Scenario B — Concurrent editors (advanced, measures conflict resolution):** +- 2–3 VUs share the **same file ID**. +- Each VU: + 1. `GET /api/main/methods/get-file?id=` (to get latest `revn`) + 2. `POST /api/main/methods/update-file` with changes + 3. If `revn-conflict` (HTTP 400 or 409 with `:code :revn-conflict`), retry with the latest `revn`. +- **Problem:** k6 VUs are independent; they cannot easily share a mutable `revn` counter. +- **Solutions:** + 1. **Optimistic concurrency:** Let conflicts happen naturally. Measure the conflict rate and retry latency. This is realistic for many-user editing. + 2. **Shared state service:** Run a tiny Redis or in-memory service that stores the latest `revn` per file. VUs read/write it before each update. This adds coordination overhead but reduces conflicts. + 3. **Sequential VU groups:** Use k6 `scenarios` with `executor: 'per-vu-iterations'` and a small shared file pool. Accept that some conflicts will occur and measure them as part of the benchmark. +- **Recommendation:** Start with **Solution 1** (optimistic). If the conflict rate is >10%, consider **Solution 2**. + +**File Size Tiers for `update-file`:** +The backend `update-file` performance depends heavily on file data size (serialization, validation, pointer-map resolution, snapshotting). + +| Tier | Size | How to create | +|------|------|---------------| +| Small | ~10 shapes | Create a file with a few rectangles. | +| Medium | ~100 shapes | Duplicate a page with many shapes. | +| Large | ~1000 shapes | Import a real-world design file or use a fixture. | + +**Action:** Create a `create-file-fixture.js` helper that generates files of each tier via the `create-file` + `update-file` API (or by importing a `.penpot` file via the binfile import API if available). + +#### Flow 4: Viewer (Read-heavy, anonymous or logged-in) (`viewer.js`) + +1. Login (or use share-link token for anonymous). +2. `GET /api/main/methods/get-view-only-bundle?file-id=&share-id=&features=<...>` +3. `GET /api/main/methods/get-comment-threads?file-id=&share-id=` + +#### Flow 5: Export (CPU/IO-heavy) (`export.js`) + +1. Login. +2. `POST /api/export` with export payload. + - Inspect `frontend/src/app/main/data/export.cljs` for the exact payload shape. + - Common exports: `type: "png"`, `type: "svg"`, `type: "pdf"`. + - This hits the **exporter** service (Node.js/Playwright), which is a separate process. If the goal is to stress the **backend**, limit export tests or target the backend export queue endpoints. + +#### Flow 6: Media Upload (Storage/IO-heavy) (`media-upload.js`) + +1. Login. +2. Direct upload: `POST /api/main/methods/upload-file-media-object` (multipart, small PNG). +3. Chunked upload: `POST /api/main/methods/create-upload-session` → `upload-chunk` x N → `assemble-file-media-object` (large PNG). +4. URL-based upload: `POST /api/main/methods/create-file-media-object-from-url` (if a stable external image URL is available). + +#### Flow 7: Font Upload (Storage/CPU-heavy) (`font-upload.js`) + +1. Login. +2. `POST /api/main/methods/create-upload-session` (for the font file) +3. `POST /api/main/methods/upload-chunk` for each chunk +4. `POST /api/main/methods/create-font-variant` → `{team-id, font-id, font-family, font-weight, font-style, uploads: {"font/ttf": ""}}` +5. `GET /api/main/methods/get-font-variants?team-id=` + +--- + +### Phase 3 – Scenarios & Orchestration (Day 6) + +Define k6 `options.scenarios` that mix the flows to simulate realistic traffic. + +**Example scenario mix for 1000 VUs:** + +| Scenario | Script | VUs | Arrival Rate | Duration | Notes | +|----------|--------|-----|--------------|----------|-------| +| `lifecycle` | `lifecycle.js` | 100 | 1/s (ramp 0→100 over 5m) | 10m | Full CRUD, most realistic. | +| `workspace_open` | `workspace-open.js` | 400 | 5/s (ramp 0→400 over 5m) | 10m | Read-heavy, simulates many editors opening files. | +| `workspace_edit` | `workspace-edit.js` | 200 | 2/s (ramp 0→200 over 5m) | 10m | Write-heavy, independent files. | +| `workspace_edit_concurrent` | `workspace-edit.js` | 30 (10 groups of 3) | 0.5/s | 10m | 3 VUs per file, measures conflicts. | +| `viewer` | `viewer.js` | 200 | 3/s (ramp 0→200 over 5m) | 10m | Read-heavy, simulates public/private viewers. | +| `media_upload` | `media-upload.js` | 50 | 0.5/s | 10m | Storage stress. | +| `font_upload` | `font-upload.js` | 20 | 0.2/s | 10m | Font processing stress. | + +**Thresholds:** +- `http_req_duration{p95} < 200ms` for `get-profile`, `get-teams`, `get-projects`. +- `http_req_duration{p95} < 500ms` for `get-file` (small), `search-files`. +- `http_req_duration{p95} < 2000ms` for `get-file` (large / 1000 shapes). +- `http_req_duration{p95} < 1000ms` for `update-file` (small). +- `http_req_duration{p95} < 3000ms` for `update-file` (large). +- `http_req_duration{p95} < 5000ms` for `upload-file-media-object` (1 MB). +- `http_req_duration{p95} < 10000ms` for `assemble-file-media-object` (5 MB chunked). +- `http_req_failed < 1%` globally. +- `http_req_failed{code:revn-conflict} < 5%` for `workspace_edit_concurrent`. + +**Correlation with backend metrics:** +- Scrape `/metrics` before, during, and after the test. +- Key Prometheus metrics to watch: + - `rpc_main_timing_seconds` (histogram/summary, labeled by command name) + - `rpc_management_timing_seconds` + - `http_server_dispatch_timing_seconds` + - `websocket_active_connections` (if any WS is active) + - `websocket_messages_total` + - JVM hotspot metrics (`process_cpu_seconds_total`, `jvm_memory_bytes_used`, `jvm_threads_current`) + - HikariCP metrics (if exposed; check `com.zaxxer.hikari:type=Pool` via JMX or custom Prometheus exporter) + - PostgreSQL: `pg_stat_activity` count by state. + - Redis: `INFO` `connected_clients`, `used_memory`. + +--- + +### Phase 4 – Advanced `update-file` Testing (Days 7–8) + +Because `update-file` is the core of the product and the user explicitly noted that **file size matters** and **concurrent editing is difficult**, we need a dedicated deep-dive. + +#### 4.1. File Size Tiers + +Create a `file-size-matrix.js` script that parameterizes the file size: +- `SMALL_FILE_ID`: 1 page, 10 shapes. +- `MEDIUM_FILE_ID`: 1 page, 100 shapes. +- `LARGE_FILE_ID`: 1 page, 500 shapes. +- `XLARGE_FILE_ID`: 1 page, 1000+ shapes, or a multi-page file. + +Run `workspace-edit.js` against each tier separately and plot: +- `update-file` latency vs file size. +- `get-file` latency vs file size. +- Backend CPU and DB time vs file size. + +#### 4.2. Concurrent Editing Strategy + +**Problem:** `update-file` uses optimistic concurrency control (`revn`). If two users submit the same `revn`, the second gets a conflict. + +**Backend behavior:** +- `update-file` acquires an advisory lock (`db/xact-lock! conn id`) on the file ID. +- It checks `revn` and `vern` conflicts. +- It processes changes, validates, updates the file, and sends notifications via `msgbus`. +- The transaction duration is what we want to measure. + +**Test design for concurrent editing:** +- **Shared file pool:** Pre-create 50 files of `MEDIUM` size. +- **VU grouping:** Use k6 `scenarios` with `per-vu-iterations` or `shared-iterations`. Assign groups of 3 VUs to the same file ID. +- **Conflict measurement:** Do *not* synchronize `revn` between VUs. Let them race. + - Track `http_req_failed{code:revn-conflict}`. + - Track retry latency (if a VU retries after fetching the latest `revn`). + - This gives us the **natural conflict rate** under load, which is a realistic product metric. +- **If the natural conflict rate is too high (>20%):** + - Add a small `sleep()` jitter (0–500 ms) between `get-file` and `update-file` to spread out the requests. + - Or, use a tiny shared counter (e.g., a small HTTP endpoint or Redis) that VUs read to get the "next" `revn`. This is less realistic but gives a cleaner latency measurement. + +**Action:** Create `workspace-edit-concurrent.js` with the grouping logic and conflict-rate thresholds. + +--- + +### Phase 5 – CI Integration & Reporting (Days 9–10) + +1. **Runner script (`run.sh`):** + - `./run.sh smoke` for a 1-VU, 1-iteration smoke test. ✅ Done + - `./run.sh lifecycle -v 100 -n 10` for the standard run. + - Add `--scenario` flag to run individual flows or the full mix. + +2. **Output:** + - k6 JSON/CSV output to `performance/results//`. + - Prometheus snapshot diff (before vs after). + - Grafana screenshot or dashboard export. + +3. **Grafana Dashboard:** + - Panel: `p95 latency by RPC command` (from `rpc_main_timing_seconds`). + - Panel: `HTTP requests/sec` (from k6). + - Panel: `Error rate by command` (from k6). + - Panel: `DB connection pool` (if available). + - Panel: `JVM heap used`. + - Panel: `update-file conflict rate` (custom metric from k6). + - Panel: `File size vs latency` (from the matrix test). + +4. **Regression guard:** + - Store baseline results in `performance/baselines/`. + - After any backend change, run the baseline scenario. If p95 increases by >20% for any critical command, fail the CI step. + +--- + +## Risks & Considerations + +| Risk | Mitigation | +|------|------------| +| **Scale: 1000 VUs creating data simultaneously will exhaust DB connection pool or storage quota.** | Pre-seed the data pool. Use a dedicated perf DB. Monitor `pg_stat_activity` and HikariCP metrics. | +| **Media upload (images/fonts) will saturate network I/O before the backend is stressed.** | Run the load test from the same datacenter/VPC as the backend. Use small dummy files for most tests; reserve large files for a dedicated storage-stress scenario. | +| **`update-file` conflicts under 1000 VUs may be so high that the test becomes a conflict test, not a latency test.** | Measure both. The conflict rate is itself a critical metric. If it is too high, we can add jitter or use independent files. | +| **Exporter service is a separate bottleneck.** | `export.js` should target the backend queue endpoint, not the full export pipeline, unless we want to test the exporter too. If exporter is in scope, run it as a separate scenario. | +| **Chunked upload creates many temporary DB rows (`upload_chunk` table).** | The backend has a `upload-session-gc` cron job. Ensure it runs after the test, or clean up manually. | +| **Font upload shells out to FontForge and WOFF tools.** | This is CPU-intensive and may be a bottleneck. Run font upload as a separate, low-VU scenario to measure the processing time without blocking other tests. | +| **Prometheus metrics may not expose DB pool wait time.** | Add a custom JMX exporter for HikariCP if needed, or query `pg_stat_activity` directly. | +| **Cleanup:** 1000 VUs creating teams/files will leave logical deletions or orphaned storage objects.** | Use a dedicated perf environment. Run a cleanup script after the test that deletes all seeded data via the RPC API. | + +--- + +## Testing Strategy + +### How to verify the test harness itself works + +1. **Smoke test:** Run each k6 script with `1 VU, 1 iteration` against a local devenv. Verify all requests return `200` and the response body is valid JSON. +2. **Baseline run:** Run `workspace-open.js` with `10 VUs, 60 s` against a clean devenv. Record baseline p95 and p99 latencies. +3. **Regression guard:** After any backend change, re-run the baseline. If p95 increases by >20%, flag it. +4. **Saturation test:** Ramp `workspace-edit.js` to 100 VUs editing independent files. Monitor backend CPU and DB connection pool. The test should reveal the breaking point where `update-file` latency spikes. +5. **Media upload stress test:** Run `media-upload.js` with 50 VUs uploading 1 MB files. Verify storage throughput and no `413` errors. +6. **Font upload stress test:** Run `font-upload.js` with 10 VUs. Verify FontForge CPU usage and no timeouts. + +### Manual validation checklist + +- [x] `POST /api/main/methods/login-with-password` with JSON body returns a session cookie. (Validated via k6 lifecycle) +- [x] `GET /api/main/methods/get-profile` with `Accept: application/json` returns JSON. (Validated via k6 lifecycle) +- [ ] `curl -H "Accept: application/json" http://localhost:6060/metrics` returns Prometheus text. +- [ ] Backend fixtures create at least 100 test users and 100 test files. +- [x] A `update-file` request with a minimal `changes` payload succeeds and returns `{"revn": N}`. (Validated — needs full shape with selrect, points, transform, frame-id) +- [x] A `upload-file-media-object` multipart request succeeds and returns a media object ID. (Validated via k6 lifecycle) +- [ ] A chunked upload (`create-upload-session` → `upload-chunk` → `assemble-file-media-object`) succeeds. +- [ ] A `create-font-variant` request with chunked uploads succeeds. + +--- + +## Immediate Next Steps (if approved) + +1. ~~Create `performance/` directory and `README.md`.~~ ✅ Done +2. ~~Write `penpot-client.js` (k6 shared module) with `login()`, `rpc()`, `uploadMultipart()`, and `uploadChunked()` helpers.~~ ✅ Done (388 lines, JSON transport, cookie auth, session headers, tagged metrics) +3. ~~Write a manual `curl` validation script~~ — Skipped; JSON compatibility confirmed via k6 smoke test. +4. Write a data seeding script (`performance/scripts/seed-data.js`) that creates 100+ users, teams, projects, and files of varying sizes. +5. ~~Write the first k6 script: `lifecycle.js`~~ ✅ Done (398 lines, 12-step CRUD flow, all checks passing) +6. ~~Run a 1-VU smoke test against local devenv and commit the baseline results.~~ ✅ Done (10/10 checks, 0% failure, ~10s iteration) +7. Write `workspace-open.js` and `workspace-edit.js`. +8. Write `media-upload.js` and `font-upload.js`. +9. Define the `1000-VU` scenario mix in `options.js` (shared scenario config). +10. Run the first 100-VU ramp test and capture Prometheus metrics. + +--- + +**Plan Author:** Senior Software Architect +**Status:** Phase 1 complete, Phase 2 partially complete (lifecycle.js done). See "Current Progress" section above. +