From fdb47979fbb6fcb253bafeb66ceb77fe4556793c Mon Sep 17 00:00:00 2001 From: zzylol <50204836+zzylol@users.noreply.github.com> Date: Wed, 14 Oct 2026 23:13:35 +0000 Subject: [PATCH 1/4] feat(dag-viewer): show the stage pipeline as three stacked lanes The viewer drew one query at a time as a pre-ASAP and a post-ASAP DAG, read through stage-workload.js. It now shows the whole workload at each planning stage: Stage 0 (query-language frontends), the chosen Stage 1 logical ASAP candidate and its Stage 2 physical candidate, stacked top to bottom, with the Stage 3 candidate list beside them and node details below. - stages.js checks the document and builds each lane from the flat logical DAGs and the PhysicalASAPDAGs; a standalone scalar query is a Scalar node over the nodes it reads (scalar_roots in Stage 2). - The Stage 3 list keeps the candidate table: selected, costlier, rejected, pruned and composition-error rows, with a header that shows the Stage 1 error or selection.error. - The SQL and PromQL editor moves to editor.js and shows its result in the lanes. - server.py writes the examples.json documents into the git-ignored out/ on start (--regenerate rewrites them). Only Example 1 stays committed, for the stage_pipeline contract test; dag.example.json, generate-sample.sh and the scalar-queries fixture are removed, and the scalar test checks scalar_roots positions directly. - render.py, viewer.js, stage-workload.js and their tests go with the per-query view; test_viewer.py replaces test_render.py. --- .gitignore | 3 - crates/devtools/tests/stage_pipeline.rs | 14 +- tools/dag-viewer/.gitignore | 1 + tools/dag-viewer/app.js | 276 + tools/dag-viewer/cost-benefit-annotations.png | Bin 262020 -> 0 bytes .../cost-benefit-positive-savings.png | Bin 217800 -> 0 bytes tools/dag-viewer/dag.example.json | 27417 ---------------- tools/dag-viewer/editor.js | 167 + tools/dag-viewer/examples.json | 23 + tools/dag-viewer/examples/scalar-queries.json | 2802 -- tools/dag-viewer/generate-sample.sh | 24 - tools/dag-viewer/index.html | 662 +- tools/dag-viewer/node-category-outcome.png | Bin 202137 -> 0 bytes tools/dag-viewer/node-style.js | 147 +- tools/dag-viewer/planner-ui.js | 156 - tools/dag-viewer/render.py | 110 - tools/dag-viewer/server.py | 35 +- tools/dag-viewer/stage-workload.js | 207 - tools/dag-viewer/stages.js | 457 + tools/dag-viewer/test_render.py | 347 - tools/dag-viewer/test_server.py | 43 +- tools/dag-viewer/test_viewer.py | 426 + tools/dag-viewer/viewer.js | 795 - 23 files changed, 1630 insertions(+), 32482 deletions(-) create mode 100644 tools/dag-viewer/.gitignore create mode 100644 tools/dag-viewer/app.js delete mode 100644 tools/dag-viewer/cost-benefit-annotations.png delete mode 100644 tools/dag-viewer/cost-benefit-positive-savings.png delete mode 100644 tools/dag-viewer/dag.example.json create mode 100644 tools/dag-viewer/editor.js create mode 100644 tools/dag-viewer/examples.json delete mode 100644 tools/dag-viewer/examples/scalar-queries.json delete mode 100755 tools/dag-viewer/generate-sample.sh delete mode 100644 tools/dag-viewer/node-category-outcome.png delete mode 100644 tools/dag-viewer/planner-ui.js delete mode 100755 tools/dag-viewer/render.py delete mode 100644 tools/dag-viewer/stage-workload.js create mode 100644 tools/dag-viewer/stages.js delete mode 100644 tools/dag-viewer/test_render.py create mode 100644 tools/dag-viewer/test_viewer.py delete mode 100644 tools/dag-viewer/viewer.js diff --git a/.gitignore b/.gitignore index 3774bbbd1..fc28ab02d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,9 +3,6 @@ # Cargo.lock is intentionally tracked (workspace builds binaries). -# Regenerated by tools/dag-viewer/generate-sample.sh; dag.example.json is the committed sample -/tools/dag-viewer/dag.json - # Python bytecode cache (tools/clickhouse/extract_functions.py) __pycache__/ *.pyc diff --git a/crates/devtools/tests/stage_pipeline.rs b/crates/devtools/tests/stage_pipeline.rs index 0cb261ec1..fce25c9c2 100644 --- a/crates/devtools/tests/stage_pipeline.rs +++ b/crates/devtools/tests/stage_pipeline.rs @@ -446,9 +446,9 @@ fn pass1_failure_still_writes_stage0_and_the_error() { assert!(document["stage3_plan_selection"]["selected"].is_null()); } -/// The scalar-queries viewer fixture: standalone scalar queries first, in -/// the middle and last, which the physical DAG keeps in `scalar_roots`. -const SCALAR_QUERIES: &str = "../../tools/dag-viewer/examples/scalar-queries.json"; +/// Standalone scalar queries first, in the middle and last, which the +/// physical DAG keeps in `scalar_roots` by query position (the viewer's +/// test_viewer.py draws this workload). const SCALAR_QUERIES_ARGS: &[&str] = &[ "--promql", "scalar(sum(m))", @@ -463,14 +463,8 @@ const SCALAR_QUERIES_ARGS: &[&str] = &[ ]; #[test] -fn scalar_queries_fixture_is_current() { +fn scalar_queries_keep_their_positions_in_scalar_roots() { let document = document_of(SCALAR_QUERIES_ARGS); - let committed: Value = serde_json::from_str( - &std::fs::read_to_string(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(SCALAR_QUERIES)) - .unwrap(), - ) - .unwrap(); - assert_eq!(document, committed, "regenerate the committed fixture"); let selected = document["stage3_plan_selection"]["selected"] .as_str() .unwrap(); diff --git a/tools/dag-viewer/.gitignore b/tools/dag-viewer/.gitignore new file mode 100644 index 000000000..89f9ac04a --- /dev/null +++ b/tools/dag-viewer/.gitignore @@ -0,0 +1 @@ +out/ diff --git a/tools/dag-viewer/app.js b/tools/dag-viewer/app.js new file mode 100644 index 000000000..c1285c2c3 --- /dev/null +++ b/tools/dag-viewer/app.js @@ -0,0 +1,276 @@ +// ASAPPlanner Stage Viewer: an asap-stage-pipeline/v1 document as a Stage 3 +// candidate list beside three DAG lanes stacked top to bottom (Stage 0, +// then the Stage 1 candidate, then its Stage 2 candidate), details below. +// Each lane shows the whole workload. Document checks, the candidate list +// and node labels live in stages.js (tested); node-style.js groups operator +// kinds. +(function () { + if (window.cytoscapeDagre) cytoscape.use(window.cytoscapeDagre); + const $ = (id) => document.getElementById(id); + const esc = (s) => String(s).replace(/[&<>"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"' }[c])); + const tok = (name) => getComputedStyle(document.documentElement).getPropertyValue(name).trim(); + + let doc, rows, queryIds, logicalById, physicalById; + let current = { logical: null, physical: null }; + const cys = [null, null, null]; + const laneName = ['Stage 0 · query-language frontends', 'Stage 1 · logical ASAP optimization', 'Stage 2 · physical ASAP optimization']; + + function style() { + return [ + { selector: 'node', style: { + shape: 'round-rectangle', label: 'data(label)', 'text-wrap': 'wrap', 'text-valign': 'center', 'text-halign': 'center', + 'font-family': tok('--font-mono') || 'monospace', 'font-size': 10, color: tok('--fg'), + width: 'label', height: 'label', padding: '9px', 'border-width': 1.5, 'text-max-width': 240 } }, + { selector: 'node[group = "source"]', style: { 'background-color': tok('--cat-source'), 'border-color': tok('--cat-source-line') } }, + { selector: 'node[group = "rel"]', style: { 'background-color': tok('--cat-rel'), 'border-color': tok('--cat-rel-line') } }, + { selector: 'node[group = "summary"]', style: { 'background-color': tok('--cat-summary'), 'border-color': tok('--cat-summary-line') } }, + { selector: 'node[group = "scalar"]', style: { 'background-color': tok('--cat-scalar'), 'border-color': tok('--cat-scalar-line'), 'border-style': 'dashed' } }, + { selector: 'node.root', style: { 'border-width': 3.5, 'border-color': tok('--accent') } }, + { selector: 'node:selected', style: { 'overlay-color': tok('--accent'), 'overlay-opacity': 0.14, 'overlay-padding': 5 } }, + { selector: 'edge', style: { width: 1.6, 'line-color': tok('--edge'), 'target-arrow-color': tok('--edge'), 'target-arrow-shape': 'triangle', 'curve-style': 'bezier', 'arrow-scale': 0.9 } }, + { selector: 'edge:selected', style: { 'line-color': tok('--accent'), 'target-arrow-color': tok('--accent'), width: 2.6 } }, + ]; + } + + function clearLane(i, text) { + if (cys[i]) { cys[i].destroy(); cys[i] = null; } + $(`lane${i}-meta`).textContent = text; + } + + function render(i, dag, physical, costPerNode) { + if (cys[i]) cys[i].destroy(); + const elements = stageLaneElements(dag, { physical, costPerNode, queryIds }) + .map((el) => ({ data: el.data, classes: [el.classes, el.data.rootFor && el.data.rootFor.length ? 'root' : ''].join(' ').trim() })); + const cy = cytoscape({ + container: $('cy' + i), elements, style: style(), + layout: { name: window.cytoscapeDagre ? 'dagre' : 'breadthfirst', rankDir: 'BT', nodeSep: 18, rankSep: 34, padding: 14, directed: true }, + wheelSensitivity: 0.25, minZoom: 0.2, maxZoom: 2.5, boxSelectionEnabled: false, + }); + cy.on('tap', 'node', (e) => showNode(e.target.data(), i)); + cy.on('tap', 'edge', (e) => showEdge(e.target.data(), i)); + cys[i] = cy; + } + + const fmtCost = (x) => (Math.abs(x) >= 100 ? x.toFixed(1) : String(Number(x.toPrecision(3)))); + const kv = (items) => `
| field | type | nullable |
|---|
${esc(JSON.stringify(n, null, 2))}Select a node to see its operator, output schema, timing and Stage 3 cost; select an edge to see the schema and data state it carries.
'; + + // Show one stage document; `source` names it, `story` explains it. + function showDocument(data, source, story) { + const errors = validateStagePipeline(data); + if (errors.length) throw new Error(errors.slice(0, 3).join('; ')); + doc = data; + current = { logical: null, physical: null }; + $('details').innerHTML = HINT; + $('docName').textContent = source || ''; + $('story').textContent = story || ''; + $('story').hidden = !story; + queryIds = doc.workload.queries.map((q) => q.id); + const logical = doc.stage1_logical_asap_optimization; + const physical = doc.stage2_physical_asap_optimization; + const selection = doc.stage3_plan_selection; + logicalById = new Map(logical.candidates.map((c) => [c.id, c])); + physicalById = new Map(physical.candidates.map((c) => [c.id, c])); + rows = stageCandidateRows(doc); + + $('queries').innerHTML = doc.workload.queries.map((q) => { + const r = q.requirements || {}; + const acc = r.accuracy === 'exact' ? 'exact' : r.accuracy ? `ε=${r.accuracy.epsilon}${r.accuracy.delta !== undefined ? `, δ=${r.accuracy.delta}` : ''}` : ''; + const req = [acc && `accuracy ${acc}`, r.latency_ms && `latency ≤ ${r.latency_ms} ms`, r.repeat_interval_ms && `every ${r.repeat_interval_ms / 1000} s`].filter(Boolean).join(' · '); + return `${esc(q.text)}${req ? `${esc(req)}` : ''}>cjA97`816NiRLMBc|g|LIxbX999FG zKm_fQlCsqnXE5d ~#X(jVG zoTcBHd0a6laymvgKW@*`Z=^9hwNh`j)>ev3Gj*FmbNZjYo3QR2AqMfn%iejS#YCH~ zt}a*csfLDqy*)Xv>AU5};lWgT8Vr&di}fHUgfn=dnU{p*Fx3B%UX~ONhx?_!JnVDq zdfyxwS^lWD8yilq+ZlWEe9mYfIXberd)(Y*F_@~ T>!eMc8z-w9%iqO%Vo&y4|N!1Rg*4eRAtJER#Z~=vjMMX-4>VrDEB7O5@KAyL|*b zZhso9aq}TOFq1KvO>?#j %`~j8v^fE^<#x#0B{mq+_f- zeXD>%2_gFk_$XIR?^c1^Jc7q1-417#Ccme$vjY&IOY`Bfs-iyNV7Bsv(G@3$o^LM2 zJZWA_+fRCul&rkNu+wh(q8AqmsJE32SN3PrW_ct?9@+{^;>vcZ=5?|JlX=HYe~=*C zkKVJo0imVX6(p|@!5US9m++G3D)&Vb#SeoLgKaONS9Ei1JX|M>4XX%E%YN0{IJZiU z?W^|#Q?_9?zcZu4Z!kks_28JxrprO!#+3V0y&cT*A;(0d+sKM2oiZH0Si3dZ4cRTP z7`5um>F`jXnNpjYLcZP?#Z6~$eQDqY9FR?QJ;VwQ?p5dHcjUeGV(l69QzGTq`?tuz ze~qd^QmQgjrJ3ma1yWl(wUWLS3+}A{vHu?RdkZRR1E^*E{
b^|pbkQnMcovjtHi`)C+E`HaUUKjSpY*~-d~a$D3Cay>EEVr?ry+8g zzKFmFP-?hWJkH>8DeGUeV^Q!rHXhd5wr*xL?9dkp6zk?xz-JP #P)*V6v;>$Fb$>5>a?H8i z<^T`vsZfdriKVa@=2ccAeCP`hsT(YNc1H(V|KD6?8q9ajiz}X8%E!f4(*rLLcNWa4 z{9ZHCs6 4GEE(}@b@1*y^{l&3R!y|ZQWqobpJ6lH_-uIOq-@(5$Bkk`@evis?K z4B~pDEy;+7@BFSdFJ~LQF`3*jdyBunU;8r~)Z6YVMuM!cge QV5y{6Ku1<2n3Fq2{)>PnYd{Z||`_o=RzIy3dUgB_+mYvchKj3&kGXIA?wb zKH0CgwOO7gWN?8P^=myYlB}W#S@_%=;rhZ73kXGo*X^k9Sl3;n>Y0Hau=6`G__)uY zh6z%NFHu6vVAB}0zuC9)_-58$>@C74T2`@GAFkSfu!35HtbFo}_Fn~zk9MGaAmZQb zC5hb274;%9IH*R*4ZM$+`L}G8_hfu0oleJ!fEw{-(xv2S^12zykMXKJ`z~qlqZXFJ zz V0+pYu`52u-m;MXDYM)O0su?NgrMyb#^Ys zao6LCLP9NR*IxBJdUcL*XU9WiZpXroTW+3M{RNbOj4+1zQ-d~7VJ@x?LzZrA(Nl6V zW`mAn$HOmFVP35OcyNXG>i#UKYa7%!%^chr)WH%$0H87>X(Z46*r@WPvpH|2ig15g zzBJg)7dQqZe52mCA^gXoR`YzuKjjH|SbLVmX(PC2<_nvvrRE!t{@nFHBI;$Jg%-`c zi?#FmZkEeFr`>8t*#KNgtb#2WKxsj*sGuMjpt?vKV!{MVDt@`V ;`Sn+hM+6Fkzxn=Ivg+xT z?)7v^k;(Fo#Y)xkq7ER{F@pKkZ!9?|vfIz)wwJ#YXsMjGQ@6MAZ#P%HZ%F=Kje0+2 zblqR? &)_LJcqd)A>C&Ju<5B{VNvT|0*NW msVL$ %_shn20_kk2wLtzNBDR3E>sKP?uy_cY2~x|$@rFb<2oVW}pt;_GX$ zasLw8u`W-A+DJn=IYHIJN{4f%4p$b@;=k?fC}F-bQKV2RHoC_wf#+IWZC&aiuO*aW z9xKpuP`7oYfG4iLXHHPLl@lA7f-JGPMEiUTyqC1FMH8o4VL(Do?zaP%p&1-rRna2w z7k&|XLY4<}+xFbnj5g5-3QmuT8+InJod4 |g!Tx3WD~-ecGt>nC%n+=DK;!g^y<~^7j*#b zV~WbK+wI YC7Lqvh^e|EN!%%C}2Y|w#V)DiY>1YKrQ8hO1W zBqX%#eu9pQ+UWz+7lYtv`uURW6Q%1ATN59sduB*^NoshRU;FR8egxuW9F(%Jeb!D% z6dI)<-!= tGc`N?XvX!PFXOb?Pb`JZv~FQ|_=NT= zZmg7)QQX)w@`wVVsrj|kFNh-X+ivx3Giu@3g@tC^B1LZmzD`Ty^gS>w2mwcBl*$^b zmZ~h?_`UIobgHxrW+_!vAUq|kLfC0m8qG{1;t+N|#QY>^`U7iGt>?B6kBkXpk%h+8 zs${qyKdHV7a@$w+ckM8rV74Z+taVvPq6Nt*3BkPwnbFIr=`&GM(y#~{ri4eLlk>82 zQEmV37V~?4MpRN63w@!LS5P41GG`3$)vo^=XU|GCIx>=)npmcTNDp%!b5r8!KqY=# z3;DNwNxzie+(vxKWdtTABiu~Kes|CZDoK$*4lfVk#l%h}A_6~7RMPSC5=ff=EIgw3 zzd;d*sGz)P_c$;0KJ`OLCc*Eo!CcN6?E
ixf)JryBJRbwY3!&_g!N}>XA zizsR8#%rd}TlfH#fuh=9z!SF;c;sEFb&f$eZ |V-*iFbVQ+t65 zigtd+s*x!v)R(;wRRy^T)|vS`I0;If^F=~}TGzVjlQN#y((v%D=YIv*KDrKqgpd5R zR0Uo>TH=gB)`;rcoDOy_?qVHTX%AE !_6T@a| zqSmwX `Cg0xPrujzgpOW;exRh2U;W<0yAUyxO><2`H zX9_)tKL*$j50A$vNjKKA?l=hLA^x)Pf{zfm5Owz&I|Hbw;8q3c2|Agve;hra)D+SY zRZV(r0lZW0l7-Xo3um-#KeF2meWIm )HKKTKcdTt zr<7$xg>#p(3Zl(dzjuRjKR%n?-62J1{U6faGAOQX3)dzgL?DFV!IKc&-5~@EE{$sl z-ncsi0wH+NMuKbO?vkLvgS$)P?)uH_v-dgs{RUgkYKmU;>b2&aW4>d&&-ng2 zya#b~fb;$n89@@ydl@+nmZ9RqN jONCNBR`N@ABY|y zXPg5`0J=c?tFo|gqAvTTVoa35F~_P3ju3+F^$=UxFdOT|HCEXDfc%#Vldw?_Ch9}V zV|iP}(uM|N6xRe5Pj^ZGrURv$1hKzZ4=K-tUwL3tQ+SYJ_C&ydVOI-t!2_3#Yxk zz19^4m`tKESoPit>q~xo*sS1!u(c8tdJ{{ArG|qA!6AkeWYeF~R>WPDj#e!iuEkEo z+gOC+t`E$K>PArglBY2p8vll{6}pQuG&7e+1lS#+9Uh{7{oc^9;4tVNoc_AlZvb)N z(k;bLAx2v1wK4o*-U9A*lm6%X`Xuj{`n>5CPwj=jzkdQDwAS^aWg0HU{s%YUz{Xm> zj1>ugq@g*?RZ@HYEfly^wfd&fm?uG1zq2QW@Q?^d7|Z5!G4^n1!0R+@++Jo zbmJXKNREzVv=|*39+Z)j1^k0esQIQ;b#^8MUpPOE(lNp`>mc*7mKi^-=_fqkcY~ zC%!DQY+$O2T5z^#dQ7&E%7oN#F@vdWmMcybbeLbNiZV;d=dtEV1^jL9%#dH9vXy35 zMYcj0nwDekido=qyEIkJ*A?2dqf+Mg3o%Ci`WXWa4VCZj#i{`Cg}i)4b0uTduo4w{ z9oekwvACMd0t?~%zz9-`S*5~2jmg>;RZk8TqrkECiMDNG;e2>$Q(lmR`8y}M >6nQg&egL<&Du_6nN#c|O6B-&SDV^AZj-ep^}FdSEY6_wS2!-c9c!`0YK z VuW_@V_3=Ph_~h@BVt@pC2FgGhh7o#ao3xqxnC_W7NO?`p=6`!vFgH(|^DD z|3)kM`tHN~2mhj-aMngfVgYUZ@59TY=H!3>_1`D?e>wTTQ7CV5Xh)sJjh$4T rPtElVq)#KPrB^vLXyMqVEHeZ?q;=vej$#~ z(1IhChD^oHmSUn^p8oSDr0b{xVSPh~hP f|ap8X_zLm^ILk7c# zfM=~|8;$4tc|^Inp58@9yITp3MHT= z;Qt>dO)aU38(8$ ~HGE7-5&aOQ$k-3IDpf`lbb3|J58nKfx@of6jokpm&Dyxus0$T@k;%$^GH3(grWu z gU~*oT$XxD2Lv*>FR`(~iFBprD4y za$|P08Jm`KmVaFUf!=A*I}}%AH*9e=|5~m<$u9k46@ru(#@jR+kK|>gRTLFfl$0}# zA=udeyNOL9OzvX(m?>t}!LIlNrZjAvbuv)KVFRHktv6c>Q<_t7IK0%>c7&J&yY-(7 zMfw3dXtx%S>owN2GVzUi;q_$r?m2d{YM{PWg{k-*qjtsd+|46Cr(H}(Pz~*Ny<#RR z`i3LGuOY{nQ|7;q!Kb_nLTiESf|5y2<7=}uW1Flm2F87HYpTV&tNG(=H+$8$H z0uP%Q1C{rE|;-bHg^Knn``>S!B>;KLsKdSf89{Q)i z^7EyY)t~?G?&$p(cZq-U&l{0Emj5sF!oMe}|EJ05|CldU?)(0U5&!vkrCqBlYi$3# zMA|oKE$sa7tN&kIriXv# $Zb_o*sr @BhSc zptrlp=EB(H9aeY`12r`>3+MJ+Wl~TXmS=_WvehLa1w~eRdO8+Qw(L>IXg*JnGrp(w zylxUYx&r}H?$>o#KNC$?Pfxx|YnEj`rJbGKlGdm1Guk2|HiS=H_OstG7#XNsUXU z)*|Rs13H|X?5STudM$|P%8QbwfQK1kE-Km!oT>J9_T0q&AH7P0qde 0fkZ+KPZ+RCGQ$CTZDgL)^pTHLNoV7G>{HVm8^3c0WLb z%(dC$W@STqsy{k9+J*;JmS{X*V>X^-bJ1Y0+Do{CWpHRHm8IHjQe8u1 !bMu`o=6z>LmGlKYPY=)Z zvH_Z uPX00?lRLRa^+C<2eYyOe`MdmenL1g+;^pjT6a)Rg2 zd0pN*|2?rZX|baC1DW1y>Eay@j?yR#^&1Zbp)OSYodiZ5-YTp;B?SeSl}u3En4O*N zX3HW(!4Y#i5&rshhAV%vi`0AI`!zB!7|d~hly?lWuokn9ZBs+v4}H0;kq2~Y1dXn3 z49Gq6en5=8I-4@?5(VM~r)tmQDq}8y|AXdnYB9Ih UM_%-hnv*AV0+Nr=W|m>k&`*Z)Tme7AMMZ5^2vIuU&J59f4e%mt~QlM zpUQan^fPd5<0P3qPcxgY+^dNX9PEWx41NRtb3WHKDJv`8>V8Yz#!~=1Sp(nS Nus3T_ 7x7sbUdgQS9<1k#LblD$|mHDMU^w$e?04eQY*%*e?<>SP+8$PXfQbE~$P`wE>J zkx8i=g|iqOnqE=^mxa+I (tJ!jX~T^=F{Vqc;U zVes>cADW0YFC>iWd|blnCgW!ml+@CKe3WicQLu4z`VioG+1uGa79{&55NHa;yCnQ6 zjhRTnH176_O=pV{W@ZEebyXSB-!NBfHCutIJ6p!WA>?sfV7Wz^9vs})8 r(7#P63`au{MhK|w(v)we_v92|6Xh$d<; zfh|e%ibJ`^XJ@E1v~wo_z@7eH>947Nfe{s22OOr!y2TT!Ox9$ROs{P1DxN%l=zq^A ztx6ToX)N~E=VwR>>gKj7)u25rtgvXVrrxrhsqXhbTEKWjkD4{$GGx`UPbYnp3RE6^ zs}}&BRW_;w{02kektFD>adt=PLF5yZ&{s4x?Gx%L_dX5yMZ(W86+6DT4Ux?@-nF zW?uh9F+uY?BMHvn50hh1Fcj~5BH86Mrn-~@lAXACcmNc`Y-G*YjZc 4P( zHixKSz6p`+qB*U)o9O4}odCcY#m`Zn3nS8YH$&^glj`0Ns>@j1Cr>P^(vj?fOGyP~ z-!)`T{hZ@vWQ@S1ECkC86iVy!Gc9DFS9t0^jMwWxY~kj*vKC&NkB!r5Bk z1dF4LiO$5EGifTKhU@|4@{WodUb}+(nL>(NOyyLYo2K>Lle0A^-|~`tgwrx4j^!mK z>+SE%8hJU=4VK_oj4The%!9?6ZveIfmk4%#-M%&LOg0e5AKAE&>! _8em-VmsD<5u~m4qFQ8>TH9a zFmpst`>ld$IOEPlScQK4)s~&O%cM@>CZj+dYF#=0jiiD1!m0&hx9~C(4b5`KP*Ygr z$B0`3fd-*0FYt){+-t4g;~7emEvF3KWMQRtoNmVy#8OOXn3 CwnM3|nka%ORkh&QO5edaV~(ys@jpr$vU7F$k3Vxi>p@ici%ckKDb zJ&B`_ !e@t5+_1n5nZ(B_U^ct&5 ztG>nkN?%$E36Yul1!<7>mL(e1YVa_Sf@=B`sf)i@JKdVSSnopH(d5wo&$PY3M>Sm7 zS@7k=T;RZMkqk$(+Q~7Sm6*hWl;6$R#H9VPd5kgT7f>1R>=fO*ad>o8y(dVFNzhuQ z6w1!VCM|uAH42< }sa4N+`lY&4il-z)o!Oh`{%U37M1zO{3JfM7j% zyz$-w9ntUVY3UT_H2sLxV{9B^W4ai1Nt=8vazVk3u|_Hjb33Wcz+o5>M{ZwqeJyoL zX $}FNcpMxf zF9@P5cOqdh^Xa^e2yBGdfLEg{LeWCoV@H!QT|WlLwf~t@^`3xJKIDM%ED+K2 g}J!6s-z@XG4eu))HTCSt5na( zT>sus>?cK?1ie$rNEp75=F%qK!O5E2?n*a(NzX4e0yQ%0&^=n(Ha*)fbsi2^o1d80 z)T^;8*V|*n0)-MZVZ=jc=NrL1Jhf9(njjb*)g~d{eo#%77*7yRGIzI9OIJ v@aXc| z+~2=_1ekOh8?A AAS |yXTW$Wov2OvFiaE$KiOm!dXtXkA3aKLkN+d$akSC;@4*7vDPnDS#0$<^@KcXP}S z5EGr8Qjfi3sI7G^o=548XF1)wXRg-lwA^$|2eM)7rN+ZnYHAV(T>aDP@zthdHnXvf zpr~8B=svFFd~(tRU6aww4<)k#?ee7mY}Y>Reb<(=68zsLO{hw0#_fL_<2#jq?QD?# zwd46Wp*CB|TEKX91%`tIBG+r*kJ)idok%~bgUI#Lb6<9 BmY(B>PyDI_qU&hx(IQMff#0ZAT70Wh$h|7Py0A*HK5}V zQYQ5R>0EUqw`-EHXIQT}8&F}tf7cESOK3 px@s4lmxfMMmlg z8%q3w&U_04> sGw-)kbjSe|E7fS6KOAD5WILnHM0|f%CfV0k49JVS*IBh zt|)k;l`&g`?|%-;#CPT&N3k#PsrYj+=f6a7g@+1D>YwyMpB4-bwok-%wo{@>y{d9D z%FW9)y&; y^mA=Y)mcq=T__>4P-J(J&mJU0A~}I9mQc3Ou}{ZbgPR4(=zP5T zKi^|r5YrQxerg&TeaO4pF*qUkL;+Ru!yW`J&3D<~y!yKcQXB?!TB3$Q#h}WtEkUWT z4_VCp00XGSk5$^0wG~#qL5{uqoD?HUF<0|QnSMfy1mi!j1=S{^6|^)A6t8HWetLRa zxI+ZKgCEUBzNyU33j3#6m(D$_*M$%c0;F>*Mb(-)f##ApuMyvXP*#;l(qs1PL-DG~ zg7yz#>8Qv;oFd4x+uvt=TVAi_yWnaz+^OE1p2>)aWDWNr)4niIbv~Zn(WRPP^Wxyk zwTXFw6z{qEpMO`K+LEfUzqb*9Qtn3n4C#EgZ@19yGHpRkXLTIbDahR)?eUnvQ74gQ zzZJ4z1N3+BsKpg8rK>G=;(jw*_}bYkjAFhJ&&4GrSi~Y*0A)jpIf(hs*Ea7P @yMq`!+}Ux2hp2fjV*09W=8ENIJ`~75m_cYKCZ@L$l=vz4qhdLn zG~gb!Gr9-tIX#qfob@9T?S{G4{GTVf2?ZCJ@_pGCJ&EThiG^SNa2^;{di6$i-=?}{ ziT0sC4~tK_G42EZmzmeNbVGZ~cU~>o@w)Bdlm2AFJmw$X+hyiX-WPtQoXq-{wt4 t7K% zs=M<9)pd5GdUNP^6srY3@fVZn#^_=Vmk()o{`)yosBY;P8RsfZ=D#gw=s-uzCaaeB z(C{8Tc&O-n_vy!G<(3*$8IVLL%lDA}H)H7yB5KnnZkDpJspR)&8)sW)_=TE3bPcUG z-!| dMz_{oBT#UVi&msi<9cRm^S-7QZVLwK z-tLYO@nQu}@Hk`2z^d1#($_#);F2g~=Yp5W5114a){TBN#Kyt$+V$;hNa KyI2+)^jnm{9#!L3sL2mV$*Ew?AfZB1c`FG?cCxd#Ou$V@? z>fZKuZBKzD5c7gUkwK57dY4uOJ(%0CuD)_+w%zIcsGu?>Dd=U35m6z%jM(S#yv3n9 zF_Dcy5TWcAdhZ8`*-vE2=$ >z; zA5p#I)=XWQ9)n cCyGwo28iPYb>vDM#~NNyL0=SRIk|t_JUR~ z_Py%n+e`c8^9SX#C&vz7FgCkBJ$**XM|LUb%Q* IChtb%Jq7-K4%!7yPhJ$AA?CtUFb#sPnw6;@cl5!zv_Z#+~sId8Jj zy%|Lab|Y?=+qe=wkQS!W%F^|jCb4%)Qt)#6g!hB*DZ|O6j3_R D}Hk4b JV08OeIf8ILCtz*7?foL6yCOk>jlaY zevw9#&8hUfv(e4zq@Jy=Zc}Nga0)3&Tku~Rz(V4ot8dG%U)1;}CQ?t}8j2BTTX$;| zR5{EoKu<*{meN>EBkyczOo50-e*2c2n#yd}GJ)5ZIH+Lnm7J24RG6Q?wYl^?Ho3{7 z>)jyte1L!PwYjW=jEt9;*EnNRj{J>;cq`%vMz3RgHi!I7!mF+1mHQc4o&C{fvgacY z4_#~R<^;FYaMeN$=Zh8%!aKeEae%#4R99bLSw9~S5{ikA&hC#h`jF~ |f?%%zm^npDh&W({ z{`7cOta}B|hpWC2QrvdDn9`YCrplZ1kdk`Ou`kluA5O&ayT8L1b4(d-C+In3qw%G1 zJCn8LD;4#*fQl)gBY G+{Q`Pfx~tK2*Ju)yw`xW6g^*#* z11^bwv;hB)M%fh~4O0&_In95q896{rj>1HG@Oonrzd*r8Dqy8pEQw5qst`Fr;>YuK z3JXxWi!i30$O`qU#b>8RrEXq#Pf^#puUs?$!lMNYf+*rNU*eRB+nlT2a~)kKtxt}U zNM_1K @GCWuW|arh8?^ z8tf6Tad3jXSd~BJ^y{Ya+pc_@>vjGm#JmzyJU{Y8#LP2w{tCF$3K;rApPsx}G%Lr- zZex1+hL2B8MWr@Ei-8y84Js;)cG2$U+ebn&lhP~8#yKlL4fZM%Pj60-3{jrxw*NH+ z&!-Py<2Y^qZqgcPYQ8DW1>lmZsxeTMVQieeB=Gy<0??pS1Z~?oIue!|fK^*bp$6LL zW }fw+^ic%u3oXQC)Z#Oj8V^Xb=iHV zjE|&VK8`(fsiiF2ay74#1`h8GCys`}wuDS4`e(1G?K4XbXZgJ6@#S6l=ccRo wWM2kaADCqFk8P$1$4%Pe8NaJcu z_P*~NBt@A#t6OMvYiTJ86%`FI9f8vR@MK3y1@p9wBQFOqU`|5GsTd#p1J^+y@Iwfv zB-jZ`Z3AINbNxV29-zxr%c+A0F5U#_MtcoO0N!_37hHK@doZgox#%>k@sD_#i>Fqt z%28c6dPdBj6cJzPG&~0ix&+)8rF{rb Fa{jK6y z1|+NqgX|ddUIM9H5js{UMxO9yGcr0-Q(Yl9KSLI`2dpAVS7A(A+*<5O?PBf9*2=0% zhFUbIk?)&wha7j8LJ6Y8agKs1I+z6E%5+VxXQjzuv7eJ1f#zlU1LOmMVz1br`5(y6 z=}<_3e1lHXPG4ULT=+G0bTS8^3lUN|=0J%6Vjt`G;MjMby#b($0LfX7kS`9eZ%D|P zL~vJb*kSuPOWdl`Y(ImFsiTd;W@Xjg$ Gx`3>sI zN}T3slJ$(&n^B!2KtV-=S+lUZpDuAQe8iXFlP^}Y&{R`a7gceK316H7^F#NBMl2kw zt7kxM<-k-0-lC@FlaLqmxe=UG3;D`Pf5E=Ij9EoVDO1n+-N&+YK;4WDo!mWc-J42V z&7c~fp)NSQpEcOgMyYV;{w!&CI0M=Jr _f5fy6u8 zh95Q*NE#hUscINpiDHntI&Y06D7_P}c{sEdt?Hhg7mefk`O^8yad4`l;wb|I`kK3l zzl;Qq#he7q4q3AduBWK^`{jp@EiD-nF0j*vV@OtLLu84`6t~01Z>8AcRL9RwcmQt) zyrR;t=>Hm)`#Tr>XODPzC-!Elc-YwN`|8kT6RHVV+^zw3A@YWsS3&AMI%9u-7me>0 zI*|OPGThamCFSLo)7ddD131qfK6nsD%HbQ9XO>%}!z*y$K|9y_IUszh(o*)(v9j~# zc(oeO@S5$>*pFM!QPAi@MMdH%(cyU@)pVm%At3 Z^7`<={W1o^KVXM=$T+pC?iXI~j0`2(4`Ir6C73>CP8ZB+wXyjnXh1 zWeDke5-|K|OmI$UZZO4MN)G~$5HbnO#tI%Dk;F&gk4^yy^%E9L_c&!3iL$o#wyE{b z%7^J}qYUHFEycAHLH k^x|s+hxOoU|F)xbY33yKmsQ(JN?ps zs2wQ+mKD@+9@kxH0H-XXk66MLqGT7ez9#h!R})BlIdU%mzwb}f0V>OYogmH7{<3XR zNR~vlQltAk
8im3345&f?nf95`s=un9X~IxduSP7 z7W&@w{0L@^cg^YuxPyTAF(TCpq&CH*?}B}xFUxm;fJi(&-{^_$BdRhTd&&KVpNB%$ zLn_l`$5VX-WcdsvPE+`;z5-PEMKIR`Y)U3;W2g+xD?S{G+Gu2@10@IP!oWZp EE@vFf)O_Fh%8jvpe1Y7t^A% z)qD#g)Z5U37&%4j58g2_O(7XCs<6`kT%y7}C|KT5P~S)J&_lP{iKE-qv9i)*{$UA8 zy|bB!R#jnr*l)Z~vofXE$RgCinQhI&jR_S>F>LT9k{-foDyjEU**lTBF`U{Krs_th z#LspxO|knt0-&nSK=8T)ZV}KTpw{v1i6!d9ZbfHKyOfZ3_c=ASp&!cX=Eu9GRU&a0 zMW~zGm$t&CRRAguZ5bf`YgeU`f(UvyR3^rDnepW~Bt#{D9G#3~*MD4yaa2+9LScT* zX62&2edJqAOjgn9B8xy>T^-yEo}OG@Ru1%uZQ{G8WwVLjKbH)orRCLB6pa!qD=Xs4 zjEoJR5wpG<+5BLAW}AyKS^_W@y;t+r(H0=tH=3^{AunHDps47uwqklU#`HDM=9!bK zvI;XN>*Mf{Q?$oUwpFn8_)sDCk QEh`1>~e z%2hu|rXdBm)oi^F?*V8oi~JR^ EMWhHrrLF?;u`J{R%^rI+%s`$grHUI`P^LHqTg4;DaK3NklcQ1e;B$0BKprM1h zRAz(X*O@30QRH7ObT2Oz%Q^(@^5u4*7%47i>qD_5BJgs&7g*x1WNN}Cw|sn)v);$A z#+@9i47yVb3%eL00s=cVexg+w5orrw6hNhri2hvLD(!D>I6sXo*ig~cXQpQ?gVH~z zJ)y2DrS=%S@5kGKx$JhDE}`j0xk|z=xr8x$<; dt2FDpgyzYjG_ z _y`K{GW)U_G7)h+pGlE}59guRi>bYolFvJ@j^^N;9IsHi$|OL* zqM2E-W#5#RvNGLh+@p{&62CxCi;g;#0vU-wNt!0>YXK{v%p;a<*(IEuA=TN@$;DrR zXdu!7*xk^r@g|QOMv5N!l*aU=RMR(Q^buIE{z_&q0Bo;s%N8jY&8PCJs&r#n^iUyu zrW%(EDFvIy?l2>bk&zMV=Z1+ `!fD4eBSRy-T{q8vP*6~m zqbtv1k$9LOJYJNSmJT)v@NL$|TpYgzDV|1iME*Gc(C~1*Bh68hfvTgLgnThfvTMph zNu>6bztuhAy~(FrTd5Vkl*=d6pZL!Bne^(f0Bg8SXw}1GdvFnZm;B3dcAd7B?rrEa zM*EfG%7J#$PQcT3#!V%!B>%hufO3M>46U4{ 5mMEOKr(Y{xCI2F zpc?&QRmbCi$$rP6-lw~TkDsF4*S ZD2Ptz`e z@64)~OXPa?+3Sk? AnT>$)c5vniG#qd z=rNE=RK$}Pll2DV5$fK?yM2Nz+N@Ks&5_I)EmK8@jqlwD5zUBN@IXe0Xss~VmXPw} zWp>zoG;a#QemZn{y8Y%rMu1M54Tv9Y^?rWhFHloN)cWtol$g&y35r^GXrGBU*ceeX zHXe(N(V7Tf?~IE|%qhrx-|QT8J;8vK#HL DImLbE?9f34{Qy@zE|m{1rEM+j56cGQ>C)7=4?j z52Ab@-B%(p` 5)(?M@?uW6#3Ben=D$5Nfcj1^7FO zFj6Kw-By?N{XuD1v6Q@I_^u9 Eqbg 2?<_jKZlh% znG%%I?1vY=*=)8vW;Cun737z?49-QBjaV)7FXt-Oc 3tq9fA0taM4QN_s+i3!+SlV=FVk7Qp>>Q`W^$ z20#th_*fi1gioIdU}Ht~Y?yTIv2BDay`%kM!lZl0?ub(#JzjXtLBYYm$ryomll(bH zRP7_s&di{>F{5k{g6J8=Yy-noHF&YH;N*`c9gvwJ#}sHq035$$a~lL9xcwNUng{fn zCJQ1KWFoiG96&Ra3QZ+Y6Tc_9HSWh%#+amc4(pfR57*;Ei WIrr- ly0@2yo50l2y%N5LF119*BKujDS`?M1v_v-fsqpYkWiKQ$- zv+ox^+chawRi~tc7%vIDKcUBu@dMW)YVG~2>v=r-D*HUSjW3`v078yLK7~6kTU9v8 z_5Am%yQ`h>dP}kibuGu;5g)x4{5yxEFC0hSTJI&()6(v(eo5E*qA^?VB H4B9%V>bR#o=QKV0Hsyf+!=P85)U&Fo z5x2H>v%GPLw?+xb1nmD<*?^cGt)^RJSD%{t2{SQ-RA*{?%Gvje `jh{#_1hupY3XMeM5 z9Ycnj5rl4zr&|N!bmq(LjvWb*I*Y~2#*ve3?rrPuNCyRN?D)#2?BwjABhjo2Kcm)@ z40?;}bsxI1U7M2fSW=;zK+4Lx{N;4qMA=aE3U&@4ncV-}`X*>=PNbN(Qkz4Nkxvs3 z6fC)&uec&k873Hq%8d;y=hETRfC3KkhNg$2Otdj?CD%cFiZfFFS+G&hX6;Tmqos!r zwqEMidYu89t+M&fc)q=cv=l@nsk?KgSG~iWxfvz8#bIXxI={#!boZuaI9BNkP+x)s zv%Wdl+*zfMjaws+s;G!oSKH&_Byi^S=uU}f3|I$`Z=kiRGBAV!g*ax|-XB7d5VAhu zqB*b0X{`W;#8ui0O%L78QMjBWBL^|RTUeuqiedzn7j7gsxk%#_hYl(KDV`%HdV7MP z>w`I%rqOXvBD>RmL==APZDovCvIADSJ7R3RemjZSqFXc7C=EST?`_HYsjK+D?!rR^ z)2zj8`eKQ&0GGwAJQ45Z#V|hlI{EnlXq_=y>nW``04jXyy^e(87{76H^3t_8C*sP+ zE9x=$^%^d1mV7*g?BJfD{ADN(5E}D~ilpkTc_lb36kG?camg4!vmt?Y!}Juu7qTg- z-kr`^(wnS?O9!Y1$kz;9yTTVZSSdn)$_mO@W|-zJl$)#8HP+|3GGI$)+4rKgBT(=Vu5^ko~Q#OB3 zU0@CaEiSviLp_!K|CqV%;-wRA4a!wFw-s*eFE)2T6p^E-II+XbxzR%A>qU>AXD58H zljbdiRNAva{O~O`^P86I{23cfduYeJ-le~@%svT?IeLZmr0U4`k_ACz1y?=Gt3QF( zK11%pcTS0>%s8f;3`$sB|M>Vd{~1ki!E_1Dn`7X=RL#uFnXPr-3!lI3yq4abX-ok) zygDPawdo(LwqP4u>p1`s(W%U>aRIGVdSS3cSo7XK0WVJWFSz{`-dz?E*_}Z-r2rq$ zxX*xgHl1hdPow{iP)|aK`R;hw_|L5L=V()Yj&sOuSL@L+rhcm k!6dzxucHVfn 2bM3%XBp zM&cc7hIG#8*WvXTwO0G@+DfFi+9=N0M>r2)Bq!$GS6j-QApcz3EQdHYr>tR*yD>xd zKqGY*Yz8sX-wCF>DpcQhxMDm-@1Cd$Te&&6OC}EeNiK%Y#|h?Czm`Y_*~Ju2 ikh_lz%8jE(s>Bh z(D~l$pm)nbx4bcI;DQseh6dub4)$)3Tn?5-E8@#r$zQj%7AIC)S r*A7PL+Hein(P*8;3VZ3&5b;4Htpa(xEW!`;Lxi)M#a zsHmtY;Cgx|^MS3N<_HS~g;C#SC@JVp85B%dQ`>iGR` Q*m=oloy63R1$J{Oq| SnzZ@pVxomQhz7DkKq}qFrG~fMUs%EWr7D9RF^;;i?_ZW~|t%HIGOjB<#g!cAvW& z8X7v?p6T!)1QY?2v8R>{I|F=EWU{)7Kd 8W5R1;FXnNym_VKj1GwE9R3T3H#4CMLb5ToHUn`Lq(ca=J~T zS}Jm~7j!x@HK3^$g|wQ&A;la3gSjq66Y!H(d(Y1Sx^xQ184TD(rzh0QQ *n z+nA= s#hVO-q~p@@i-mJcD^dDaJ-dgQzlFKn3(50NYmQy}4qQ{D((fF8p)7 z8xWx@Q*Ua;jRWLo1A%hku=S?nvHtR6YbDUyAXBb3GX1`_^SqduYkX_?K)9TI$#8jU z=JxNS;0;%e5gm^ocRCv0-QyWCmN&_xQ^fABj+Ym!mwCLF+l1R5ZqS_@ l^#y%m|$vpiZ}ag&Vs)joD;5+{N>08WCo;t(#7eMcTas-Xreq(fZomC7 @tWEd49Q Nk|S7E-*RyusedPizdD74TmG%ST%On5Oc%4B^wX_}LdkB63q1~g6DyKR*i zi;bnFrCsg_)7+o-K$X?3+IfM6MTkvgu5WcaK+^!IY{}Joa|8s0C+l-Ymm^(=hld`5 zDU{0x%Q=HAEO=|ieF>Zz`&S-S-oWVpjF@)`vA4IhJyYsgKbY}_>DK-NWM0=Z7-!u9 zqwV9M@4P5^PFLd5(RB) ESVE;R|Ec( Z3>X;C6%5MX;5CpzHRxHd7UqQLrM~Zyd&eo_?UC zkn`49tvN#O>3dhqot)xq&kqYj9f<*~WNN@ob$6bhUq_pOFJY I)H(30GXFhMjo6?1Yz?@Erw$TG F+D}2>NWlS{ zl!eUqr}t&X77_Rx^Pb4JnQyBC9h2aQ*xeJ}EvTV{)~$FfN0i-=pV5|ek&&qD(vJiZ zBK2#MRIbV*Sv?6f2hTG-Q&6-Aj^}x8jjgD}9ST}=qH4NoI&xwWnzlTzesG0)WgbID z6%E!+q=n{+ w5oq$tcDGmw+kw@sMA#nLS;AuVRfiZ5*6>jY|K8j`3>v}5& zy0nQCRJ@koPq5ZZ9TEWLe%g&5$&G3{_X~9@W=!N#-kHOWb5KxFz$U%M_9#}Xu@0SR zOvq_zq>W}6?*8uMQUyUde~McrbhM?=Y8C`IGKoBVbacU>78W`K9D-ExFSzVGMmm$_ z;atxGL>5^+S3X#Cfv`p@h=8C(t^Kyr%@Pr|Q}4VZbFt 7H-SsJY%;{txeB%5-!<|euZhs`M;F|c6CmrYe^apuyde^+1Ha0tq zPtbNDQtwG N+xgF96YIL^)U| zu(7?Q^;4b(Ekn_tQlhWEVT^B+;bi`7p@8ldBb$qazy9KJ5PoN}=G!Q##SB)p2+A*@ zAw4pk_(Scm1+}*k#O+WJQKf1hBIwK^_H1oe;Hs_1IhB~FCgGE1a%?p7-yNI+y_eh% zZd;WMl |7} zdT=<^E54Xp4rTnAr@NF5bn*A~obBq3!@ zBh$ qPno(|s_27OZ2MFvENFYr6IJcEym+ jNJ5{>GA${pt%LNM2-oJcrd%^sis +Al;=j+jq4%-?<< zk(ZI7M;8k;1ZxCMJCUG6Nn;~PcqiYUS44W3&&`$UDS`iRwhVMKoxsSKr2aV7RPvfU z$VIc#!ghAqc(@~!#y69mL#X*j??{FI%_Ug!wkOh(0!uNGacLaUadKY1nN70W(ObKl zD-pi&@^r7)qhCd##~>B(BEysG`FJ%J3Vi+_43!-Kp|lHPvb?m6=({ij3-d_QYknyr z&=yZF&{f$XBsN=om@>4muu!wowCINl=&3=>Y54r)t&XTOdQYx7Os+KmJuT%^Cm8vl zi%&C8!kAXzU|GJ^=gSiSp&Y6+3LdF&kVdnAn3;Q^oXKarf>y;(t4&d*5<@lNl!Y(~ zbW4phJMNl?>$?rV!fI7IJ!5Zi9t^cSmMCnlBG8*|yO8x@=4cMa*eLZToYo?NAo2~W z%Ol2yrun$|xn+ndO3^Pvp6lXLN5-dkcI`D~M<#UE=-~KGZYJoRc<*xxa?^8Mnzflr zkurs0ec$}EduWLlt4~fYGFg+tO#Bi_=vX>jX}UMzM>mirYUlCUYipORSO3t aZfBREW-vmp*dJ~&Yc=zQZq0) z&ZRRoF{G>rTlroA75AkY5`5+^))(?peB^4QMGu|9{QGfjQ{|bpgzjU;i3S0E3{OLf z?|El5EI5O#f0l=re$^2`+a#wtsWYnT0UW6pgoGf?uX}pWpQMNQRj?g_6e-OxV7hfT zTzeeM%?0=mHmj<}9v9cuO&yRw8Y|YpCB~9UdxhR29)Ef1*3luZzlb>!q4bJ`Bng qb3R* zR~V$H7>~7UEhIGr{Jx+Tg_c!Z?<=&c(7^^TGxGC6+kpjE#?x+9@sh738+vzde^yEZ zmQyM&TqEBJ;I9D6F(h({O5kVfpm52dLbut64{y;g?zF2Zo)S*z?eG1L=+msq;%=(R zeXmPe@1+srHe6>)j-7xt>M{KlHt0pZdGbOzzg*NhYT#xA8~b;T8y{-U_A!0z+?4C! zo~~8eLT9qhx1*11@Wf?jEwjXiuc!6+n|;sTxoYUJ;k)u&e4fK+yqLu%)3UL9(Ch~m z=w4k$uB?Mp>5OWnrxKqb=8hQ{Ou~6q^rrjxx>4Mja^n*rZ6KDanq#?4t**y9iyGs_ z2TAsy>Mnyuy85o>B<{n iChM!sdZ`|@JPsKK`ukvm _! zO|Dd9iA~opS3w8pS36#AUnu9)b>>A|Q>{- !{cH`uAPba#a@DcY8=|xs zury9e``A6>0P@l0bY*aNwhlCUKd7-aU;jD?^a~S1RpmE;{6;kPtINigQ+5-yMC}YA z987J}di&w`o!5=Q#IMq)9t2>~&L1nVw<&3Mxj-%S!)#IOwwN!aW2ROg!Eh(}XsoIV zH+WtsL|n?h3z}_%zR{tPTMaXSF3VZDHNn8b5*Q(COF)MK5&y)PrqZ((aJrY~eR6UF zdVCEPL8`$@&-{|e689%%Fcw`ENB!FpMOK`qlQ^Z8-K+nPwYLn*a% YH76*84sUFAIvHYRo^LnoaAm0ROE5ls#AD!20k (X`0S^;St%T#buf%puIVjC5d1Yn?xQ`{1GXY3~2B0sCc)8|L{&N&5;fhtl1Bn zuqXH}&U?HzD+u{fLu9eb{ P_!rDBgG?yV*#p`3gZC;zv4dNKia z#_@&BBmYyg8=0$6he=z|hlSlqNii|4A4eMBn(SXZ$V$?&(1zlWle?arV%*vCldX6j z-M@Sx*NzI o!_4q|T6((F!}`{i#bRYj zd(WK%7kOr*)!xw+K9&TxQbf}>9bm#gZk*jHxKOH Igmpph2(RZ WE6*H$h$OdegkS!JO>9*Thh=@t+nG9O%4$fd+P^x`o^p5tY<9<)s-(R_4grv(; zoE1%iL-WvFNKHdd#pSX)bBM2M1b4LL(&}kSJ`91#zZP{IfivKte>R~q)XT%nnt9Zg zidz0_+CH%+_W^y>*MDM>>U8KXio9CFU6xNH;=4}!QjwZkJL@D<=>*HAv|}o1V;~KO zx@Aytb>L <4GD7by~7p{f^ckMujO-6fy37p z+~)e~u}!b&iH`($_w76?*)cE}s^kK=gIvdJgzq#}!#v&-JHnY054@AcubN_+l$(lk zF}kSC_A#>^G;G_zdYwYrm)*3VycvJv{otMbi{oF5nNZ>Oy)O(*`An4op|3KX=8eo5 za~N*=5ft)cI#YETpnXeJnnJZ>3zWEXfvVMk4p2IBOItFLsW=TlY~KLZu$;ztlM*jz zZ^1j5*VflhfE YCPYY4{|<)mRx~tKdDswQm)r>GAb?Y@PsO8hY0I$ z&Ym5fync-y zI#~p{TYB)1g1I~(zRrj)m>R=-;BsKGd-obCOSwR`fyGV *IJg{uL)wmwNs)>`3#n*_KW>{e zApmLKAk~Dv9ogxjdkCD|Kx-lS?(81qJf*xl%FoL? -1QEiNKD^`s4_5C zkSk^%s;H A7~Vuc1U(7qi+dVC^<;&R|s3i_MtAv;XITV4>ZRN-A92>KJD6V zF9<0DRh(VOm}P94OBHh#rAKU`tkLB~;p@Fxb`2C1$JM;-eDp~SN+;I^ROdaRE6Zsc zVeJ|r>^Cl{y9d=5k4<@Y2Y2ZsBFWcP31ts;)81zYMF%)Co^0hBBSNgzcC(DL8YAA# zqu&HtirfTJv7f+^2p%s*CtXhv%FkV6V@S>8l+U$ce;LB{oUmqzryewzK@0iM?VA3k zxN=;8Ut5#R5$FLTO@bE+eZJ75?|(hG?w1HRzo-1TVEiU5^O%%2Zb0@s6ss`(w{ZH( zAR?8K#%@CW&*pYn=&hUh`Fe!}+~i^7-i}fBuTBxphkNTb&R9;V9HR!3;8(bDPiqv# zOo@X%Nl|JS9;H2d`{S=P@iB(hN{SU3%QMA>Xa^_d6g`d~Zx#kjCNLDFZq;(OOQY2v z{I%$We81{3KkfQ;(9u>^+RZ=hbI~NURW 9&FMx`4LMo? zNeBMYDw4kab=r2!-`gY4FiRmK7*`aqZmWSM|1KdE5sMCTh9*tN&AoV_%7TrJjmPFF zC?w?6QLnJ-fDmhOkdGmm$i!oJYbvO_KGBE9VP`0fKVNCiY%yFI6*bWz5;k3+YA_j? z6?|OPTf<#kZ85z`nC+PRKfQp_HL@twYQv@d7=gpxNk1V#-6eBw(z<;WGQ(*7i-6 zT(`(jM7vAKfCxPy&2tr8aK~eFd_20u?P9uA8iPV>yL-6&y#}-d0algOR3=rCOF(52 z>_!gf)n&ekwteif@9*y?3&YP2Js<5n@owmKI9iNNybBxX0kk4vt0kbMi7EN^ut#U} zzO5h9*( 5@ft72)4q<}TLleXY0K;Vs}s#|iInXT?NyW`c9YCZt&uB4#EKrfJ9#GI0_5MuWm} z=p;ZBp7250H^ 3(qXQXR7NU=QAOlaZJRu z;k$P#8Y6>tU=Qr>ug@Li+>qY$ srm6u~v+pxi-HPyZzb9HTJcR}0Khv)J25yfofD08OMgwmuw zFM?#i-1TwqZm~`D4<*B~mv2hun+}Gp+)vW?8lDC(1$I7XH5L2W|F=aS8<{u)1Jk>C zDMKS`6FbiS##83I=#RRcX-6!Zp*)Y4Ne;`JLJG$Ft>`K^m10vWW2tYJsa_V{jRPi^ zE)7Sx#|BtAn}aQ03=10Y`sevm`XZ^Y8&@}z|FPUcpR39K!_sMDxP%@1!ACb#JTj_K zxvCF(swDrXbLf+X{P&31oDUkS+-j;VfhR5dPjqTKH*EKlK35}&%F||Ms;U1jhw1+^ zrsSVXkEboKt!)Kak739&y}w?;z&c}3DzTjtQxc$M&-`IFJV+y}6r;23uQZI4TarK2 z+#QR;^G!xTHU#|K#m$I2VK=Ny)rBN$McbM-|Gi{yO@9X??+r4OEs6)FLkwUHC41?X zDJ|nm+DD^^jitVBF~V#Vd(pyqwB&pwqVj!~`@5vh?QfC4#sqe0PjQ)wLQ>;lX#ts6 z&z)dlxek@;c@{^{j}{eCz4?**heqg3kZkEoQJ9Ize^6d0{KwvF@eH^wb6>8-bZqcw zs74(3`*;x&q>haA_9`kU=r;#ygg7mPO`|V)Kw4Yhg-x{%4kndPwLW9!v|%TdOPOzz ze(%|T-T!}m76!3uBG1|E=1<%^@xUPL>#{DAPX)Q;ROGH%ROZx3>tuGa+KJK&BLhP6 z*Bw+INR?A84|zMUs&AIh5ZQg5-gpi+pI(9>WpH=*7obJilQCdxSAa7i6Bm7|KSB-X zE)UFnqh+sw-D=mEa!`q>oMxm?%~C ef$^AYk%cW=2GQ=W?xSKE_NoTA%RF}M8p7t|#C&(N3G;B(hfG&cu% zi&_Bq7B($@PY!6xPHM2B-rfo24ALjiud1bRk8$~paF~E-t+$1V{^wRs)Cu{;hmHB! z=D#EJUiJUvou&L|$b)|m1M`1a?El}B82`f0J~Z@h*V1sl9ff-HP6K#2Jo7%UwO{>+ zW^KX^f$5xkxW3noe&eZI&(>KNE~V|z+ES5=G 2TFbb(5kJiYe-u ze*X^z=gN)%-m)pk3juruVI+BlVn~5X5f1GeS8vWMZuMk*@R)^=2C)=nWUc`ZVo_z1 zr;P9#9hg9XMFE#Fqopc~9ae5c<9dI|VRhOGYyhL bbp^Vl;`g`Fb{k#mpRWdB7*EHh5gP<|KJ25COr_3Y_ZSQ^;3$w)!Y4<7(>2r zeb{`s(vk%u+L=S;#hu+i|L| b2HX+}zxpWF_V_)7?mWfx->$153bmqNnJAjf)uwC*wG(HACDo=CWOhN;t4$U`Y=GtX<%_>OqO#YKJ@2Bi-}#?8 zTwbm3AfD_v?LJV_@>unxWWIgNV%m|b_23G<7W;a}*^&lK$(2=1`(}k4l_pYa>DsfU zcD%$rGs>!RMiQ}N#wxe*XVZr1+q=dR)OkQ?{^=7bA^QrC?w7DV) leNo{>`V z;?VFA4JGB_OmW>`3kWu465ozp_JndwoV*Gdfb4vgkg%9+L?I^Ix)}5y*&6>8f;Xqc zl|IndH(z(fCh3!&{s=%Db!xByCqHk4LnIoV*JxYf=td$e7BggBUFCou<%-q}!1Gv- zxGF6l-;j`Wx%o?vQ_p{U(1B}8{Y&BkJ{o37-gJrTc-Lr3c6x1Gi|YjoBBG8uO%ZLf ziUd}bVC?dr(uQ{_z&~W5t7~MOf3-Im-?MfL|AUQV?i%*TPyHAM!`W%%+Y3FtjX&=` z)6tELj#jG|ka|P+d6h7DTGl(`B_ q5jTd$yc zD1|=8JqLi7#zv=pYjPAxVbMa9*z8L?&s*HlmM`uxn8_{cCK5fqSSlm>PH~@@MJ3cH zIIdK5k#n1^M2v=Ur-l(MrHWq OG?XF&Lq8G%iZz}zXC*(mDAdxgozS`MP+Ah zEU02B8g=uZ`5H6Yvflt87abd$8k3^@?VF*IIX_x=R2g@~lH#9O@!+B)$%1p#D2;~B z8iI_zw1XR9U;+aLmj&pe{en8f+WXtTYFj~WM8p2EJen!M+O`aljnXkUA8R(N0Ia>H zb0>+a{xd9Y{jUTdknQZ;*=cP!v$=_*%uViMiI?2A`*#HTGZ`8tx)x8$5TL6pR$in4 zDtlBC1h@5#b&7Hz?}8!W1mWt}Yq2{~S~M!n>2?F%tiYdO>;&x9 49vd!v!}6t- Js~*P>$4KO1ou4c!`%$$ZZC(7zJ7hK zM}jI81w5#Wld_G7Zio*OU5WA0LUrkYP o^ZIp= z=~5w|yltVOKJaD`-{J66*=Wh4xFM2oBF35-&K9=am5K%^_!5t#V_N}bF4$L4O_>(# z({11T?ydSttAA2tgoO?5WSyJ+Cga4xIfsb}SllG95!gR1EDeiid BAF)%Gg`i^aT+mSp2N*@hK85ErXx7u4%Fsd31j6Ru2@$!- zYONIR==kur7pt{M0B4Fb?VRGzY-QJU2|5ZVrg9ePp YtM}=v0dOE$IQehPM*YhDALShjjlXC`3 zJo0ptlcU3ZLwYXbb;cK0Gyu;l$NXnwy~!p87OQ_p@Hc#P(|v$lOwGn9DYU z2kyDMvFWg;67Ys^j?2zSyWygQc6WE87$49HJIOxDB7YeIEOWpTLt5O$W3%QUqNvj- z+66NP^+;J#VPmtY$C#4x1{SuCknm97!a{x5vD1c0Ia?%yu%7}f34j}#sj)aW+5%h& zg+(+`Ka@n|v=xlwffIbXkhV8{@M}mEkA?^Z5jA!H(o~>2gMGE47K&`vlBl_9u5^$m z4)_e4vr|;mU;XiI*8M%5z2T1v3XtL9VStnYuuD)Pb$LNLZ)RhF^~=$}HNwqJ{lDWI z7M5NZ `qKa{pH+}RytQb0vuOe!Jvb9`37|rv_{y+Q*3JP|1jmXGy %)IJki7F!<-O~!1MZn`m=SPgTWi?;0 z2(r!1i-yJL_sUC$hDiJtziV|@M~jp@;&o x$nKcv2gHkp9Cg-5GyDvAT`rU(xa&y(7 z_=%+`>ro>vP&D}W1fg%|YIda9e~$R`$5T2m*Mq>o92p6jp%S@&&JPHyXm0431Q*9; zHSqyjptODs30aN&1>zFyekk++%q~W^wjyTs(ugLbH;z%?`>Q>$OI}R{IccYW&xV>U zD6IrCVUjZh^0Vc5+lW?Ho2)Qu+1c5HOUsj!X$(utm|V_i!{x|? z9tE0?PDd^|U#je=)qdyCKUf%uEu(;Q=ncc-{R-hN@C`;Cim0$Nl1tpUVz=PwXz8C0 z&ikWiG}vz3;%BRkF_Je8i;h^cM O@FjWSAyRRGXPxHH}CJK_hP4a8xrlG^~xxR0r#Tu6*IO5>u`! zBNO%5UfsL-1Ypym!i^oEY00Icb`^F+EY0+h^o9L4jPp^_S%IFOB{&r0jfRX2Hc1lm ztOwr)mb)@EH>0pKV(TTW@2raI%$$m|Qwu;5MHBo1xrFrWB%GYFGzgaz;aRj%*446l z7@2G;Q o|P!77HR z*Oiujp9pWx2s=Nw9ZqVwy`N0X$oQDA3(On9rUoqe%k9ys=61%rJI1Lz4hoV$e?nhl zIJbN+6jsFf&Ol#}*|LDfDo<(%hZd{ERGV DjDaCK8l$S}B`b#9XW4@6y~7t|K|z6)c1($^R#by@3l8g*DWUfIiuS;PBj^y; z-`7`VKI=GNObqyM>Kv6k)? 1uFqJbp!|d15SGL3D4u> z?IF&$AT32T4!6P~RN{O*L)k6hh|&N~kAg6Qhnq8{#jyhz+f`JAp(*(B*3k?;L%8CF zjAkm#F>!i3Vnc(2+9J968T?`=ntRm+xw$fx^v2uJJ+dxk^o(RK7YZ^?X?69ggaTqA ze$+KF>5=a)fbdjtmxb$a#JfRZeRqk@ TvSpZ)j6*% z1&a|2K><1m>`T*ti9ohEIi8|ct5$t4`(js~t|fN^>0%&(7zH8P+1^6a>RC*apQue` zO~f|^at!AO+V6KMnf>B{-NJC_Lj#Y9zi98-^1h1S;^t*$)rrZ7#&MY8M@0ii(`=@R z%Z^yq&dy>}$C5yfd=v(3$u)AbQ}%=}|B#+pW ;JfHdAoF}k z$axYt&-Z*h$qd(6sDu$Bo~=5%F88&mQ^WiD_mbCDb=}(f?4LRyT=I)dE~s4rWo?{K z-=Wj)FMRpd4gQiPK#w412 Prh-
VL;#H?-wsE%X?}R)T8Gx zP- !B{za64=M|Q_p4py}v7gkK(Vo%h?Y*kald8%8L=Q5Aeqn{LS*|;& z6@LsX$j|tlmiFNa K(SQ45_upn*!^c{K2ls;ulU7%?fp1?yK@Ws{gg$==6JQ5% zBhXK`8V4jVWA2mM2zX$T+|M9Bp`L05nlUgT?+#$>-4iL 3msr?HrF?Ib_`$BA;yTfp~2**1&Y9B z f)`&yUl?c9pDuP6wRVj@&k1I!EawQ-zbvd@}g$W&Bea) z>3FuQfOVD@fO~K2IYMxz^8WS!xDX%JQX>=RGbI|b0XD@%*LI=1= qA?G*3KJb& zqt=y?{1X>9v4Et) m*nf9z@W@E)S|%<4D|j& zSoE_SP=cJ!W0k(FuW#f sU_qgd)Pqf9w2@n-^s0TVp~5UQp&Il8T1-z+382qW!A zafpEoZ|ee89|X-t9WfB=byNnj$r;gz4RTbhti&vq-~@%chvVrLch=5>nz=uaPpnP( z)LwUL`8Y@O=D<8 Y7bYSEVrq1p1l! zz2gIylq@*k>}=uDQ7ce%%%Iirjt;4;?op^yC8_`pJZu^Z8pb7REjC+^k$@Iet}X~& z%5dsn# ;PVrFLUe~;_?;XLQQjG3F5k&&@y z;u|4+@e}5IEW9&KW&83G3uPW9cGue|c}2aPg|P*;kCelMGXRKrGgc*W`Me2H$y`{t z2e`AhcZ^k46Btu*eNE1lni7Z;QBnJ+H%F2?T}gpWd05IG@BspI96xf*CBW6!UlDMd zEtD2J0vsmtZH