Nightly motion imitation training and evaluation on Unitree G1 (1024 trials per run).
+Physics simulation throughput across tasks (4096 parallel envs, NVIDIA RTX 5090).
+No throughput data available. Run measure_throughput.py to generate data.
'; }} + + // Date-range windowing across both tracking and throughput charts. + // Setting min and clearing max also resets any zoom/pan. + function setRange(days) {{ + const min = days > 0 ? Date.now() - days * 86400000 : undefined; + [...trackingCharts, ...throughputCharts].forEach(c => {{ + c.options.scales.x.min = min; + c.options.scales.x.max = undefined; + c.update(); + }}); + document.querySelectorAll('.range-btn').forEach(b => + b.classList.toggle('active', parseInt(b.dataset.days) === days)); + }} + document.querySelectorAll('.range-btn').forEach(btn => {{ + btn.addEventListener('click', () => setRange(parseInt(btn.dataset.days))); + }}); + setRange(90); @@ -764,7 +822,11 @@ def main( if run_id in eval_results_by_id: print(f"Using cached result for {run_id}") else: - result = evaluate_run(run_path, num_envs) + try: + result = evaluate_run(run_path, num_envs) + except RuntimeError as e: + print(f"Skipping {run_path}: {e}") + continue eval_results_by_id[run_id] = result new_evals += 1 else: @@ -783,7 +845,11 @@ def main( print(f"Reached eval limit ({eval_limit}), skipping remaining new runs") break run_path = f"{entity}/{project}/{run.id}" - result = evaluate_run(run_path, num_envs) + try: + result = evaluate_run(run_path, num_envs) + except RuntimeError as e: + print(f"Skipping {run.name} ({run.id}): {e}") + continue eval_results_by_id[run.id] = result new_evals += 1 diff --git a/05_software/train/rc_mjlab/mjlab/scripts/tools/terrain_explorer.py b/05_software/train/rc_mjlab/mjlab/scripts/tools/terrain_explorer.py new file mode 100644 index 0000000..f3b8cfe --- /dev/null +++ b/05_software/train/rc_mjlab/mjlab/scripts/tools/terrain_explorer.py @@ -0,0 +1,115 @@ +"""Interactive single-patch terrain explorer (Viser + MuJoCo MjSpec). + +Run with: + uv run python scripts/tools/terrain_explorer.py + uv run python scripts/tools/terrain_explorer.py --port 8081 + +Then open the printed URL (default http://localhost:8080). +""" + +from __future__ import annotations + +import argparse +import time + +import mujoco +import numpy as np +import viser +from mjviser.conversions import merge_geoms + +from mjlab.terrains.config import ALL_TERRAIN_PRESETS +from mjlab.terrains.terrain_generator import TerrainGenerator, TerrainGeneratorCfg + +PATCH_SIZE = (8.0, 8.0) + + +# Per-preset overrides applied when building in the explorer (e.g. to surface +# difficulty-driven behavior that is off by default). +_PRESET_OVERRIDES: dict[str, dict] = { + "random_rough": {"scale_with_difficulty": True}, +} + + +def _build_terrain_mesh(preset_name: str, difficulty: float, seed: int): + """Generate a single terrain patch and return a merged trimesh (or raise).""" + preset_fn = ALL_TERRAIN_PRESETS[preset_name] + overrides = _PRESET_OVERRIDES.get(preset_name, {}) + generator_cfg = TerrainGeneratorCfg( + seed=seed, + size=PATCH_SIZE, + num_rows=1, + num_cols=1, + border_width=0.0, + curriculum=False, + # A degenerate range pins the single patch to exactly this difficulty. + difficulty_range=(difficulty, difficulty), + color_scheme="height", + sub_terrains={preset_name: preset_fn(proportion=1.0, **overrides)}, + ) + generator = TerrainGenerator(generator_cfg) + spec = mujoco.MjSpec() + generator.compile(spec) + model = spec.compile() + + terrain_body_id = mujoco.mj_name2id(model, mujoco.mjtObj.mjOBJ_BODY, "terrain") + geom_ids = [i for i in range(model.ngeom) if model.geom_bodyid[i] == terrain_body_id] + return merge_geoms(model, geom_ids) + + +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument( + "--port", type=int, default=8080, help="Port for the viser server." + ) + args = parser.parse_args() + + server = viser.ViserServer(port=args.port) + preset_names = sorted(ALL_TERRAIN_PRESETS) + + terrain_dropdown = server.gui.add_dropdown( + "Terrain", options=preset_names, initial_value=preset_names[0] + ) + difficulty_slider = server.gui.add_slider( + "Difficulty", min=0.0, max=1.0, step=0.01, initial_value=0.0 + ) + seed_input = server.gui.add_number("Seed", initial_value=42, step=1) + status = server.gui.add_markdown("**Status:** ready") + + handle: viser.SceneNodeHandle | None = None + + def update() -> None: + nonlocal handle + name = terrain_dropdown.value + difficulty = float(difficulty_slider.value) + seed = int(seed_input.value) + status.content = f"**Status:** building `{name}` at difficulty {difficulty:.2f}..." + try: + mesh = _build_terrain_mesh(name, difficulty, seed) + except Exception as e: # noqa: BLE001 - surface any generation failure in the UI. + status.content = f"**Error:** {type(e).__name__}: {e}" + print(f"Failed to build {name} at difficulty {difficulty}: {e}") + return + if handle is not None: + handle.remove() + handle = server.scene.add_mesh_trimesh("/terrain", mesh) + status.content = ( + f"**Loaded** `{name}` at difficulty {difficulty:.2f} ({len(mesh.faces):,} faces)" + ) + + terrain_dropdown.on_update(lambda _: update()) + difficulty_slider.on_update(lambda _: update()) + seed_input.on_update(lambda _: update()) + + # Top-down-ish initial camera. + @server.on_client_connect + def _(client: viser.ClientHandle) -> None: + client.camera.position = np.array([10.0, 10.0, 8.0]) + client.camera.look_at = np.array([0.0, 0.0, 0.0]) + + update() + while True: + time.sleep(1.0) + + +if __name__ == "__main__": + main() diff --git a/05_software/train/rc_mjlab/mjlab/src/mjlab/__init__.py b/05_software/train/rc_mjlab/mjlab/src/mjlab/__init__.py index eeb970f..462b3f0 100644 --- a/05_software/train/rc_mjlab/mjlab/src/mjlab/__init__.py +++ b/05_software/train/rc_mjlab/mjlab/src/mjlab/__init__.py @@ -1,5 +1,15 @@ import os import sys + +# Default to EGL for GPU-accelerated offscreen rendering on Linux. Must be set +# before any mujoco import: mujoco's gl_context module captures MUJOCO_GL once +# at load time. Override with e.g. MUJOCO_GL=osmesa on clusters without EGL. +# Linux-only because mujoco's gl_context rejects "egl" on macOS/Windows and +# raises at import. On those platforms we leave MUJOCO_GL alone so mujoco +# defaults to GLFW. +if sys.platform.startswith("linux"): + os.environ.setdefault("MUJOCO_GL", "egl") + import traceback from importlib.metadata import entry_points from pathlib import Path diff --git a/05_software/train/rc_mjlab/mjlab/src/mjlab/actuator/__init__.py b/05_software/train/rc_mjlab/mjlab/src/mjlab/actuator/__init__.py index c6db91a..153ee12 100644 --- a/05_software/train/rc_mjlab/mjlab/src/mjlab/actuator/__init__.py +++ b/05_software/train/rc_mjlab/mjlab/src/mjlab/actuator/__init__.py @@ -4,6 +4,12 @@ from mjlab.actuator.actuator import Actuator as Actuator from mjlab.actuator.actuator import ActuatorCfg as ActuatorCfg from mjlab.actuator.actuator import ActuatorCmd as ActuatorCmd from mjlab.actuator.actuator import CommandField as CommandField +from mjlab.actuator.builtin_actuator import ( + BuiltinDcMotorActuator as BuiltinDcMotorActuator, +) +from mjlab.actuator.builtin_actuator import ( + BuiltinDcMotorActuatorCfg as BuiltinDcMotorActuatorCfg, +) from mjlab.actuator.builtin_actuator import ( BuiltinMotorActuator as BuiltinMotorActuator, ) @@ -16,6 +22,12 @@ from mjlab.actuator.builtin_actuator import ( from mjlab.actuator.builtin_actuator import ( BuiltinMuscleActuatorCfg as BuiltinMuscleActuatorCfg, ) +from mjlab.actuator.builtin_actuator import ( + BuiltinPdActuator as BuiltinPdActuator, +) +from mjlab.actuator.builtin_actuator import ( + BuiltinPdActuatorCfg as BuiltinPdActuatorCfg, +) from mjlab.actuator.builtin_actuator import ( BuiltinPositionActuator as BuiltinPositionActuator, ) @@ -28,6 +40,15 @@ from mjlab.actuator.builtin_actuator import ( from mjlab.actuator.builtin_actuator import ( BuiltinVelocityActuatorCfg as BuiltinVelocityActuatorCfg, ) +from mjlab.actuator.builtin_actuator import ( + DcMotorDatasheetParams as DcMotorDatasheetParams, +) +from mjlab.actuator.builtin_actuator import ( + DcMotorInputMode as DcMotorInputMode, +) +from mjlab.actuator.builtin_actuator import ( + DcMotorPhysicalParams as DcMotorPhysicalParams, +) from mjlab.actuator.builtin_group import BuiltinActuatorGroup as BuiltinActuatorGroup from mjlab.actuator.dc_actuator import DcMotorActuator as DcMotorActuator from mjlab.actuator.dc_actuator import DcMotorActuatorCfg as DcMotorActuatorCfg diff --git a/05_software/train/rc_mjlab/mjlab/src/mjlab/actuator/actuator.py b/05_software/train/rc_mjlab/mjlab/src/mjlab/actuator/actuator.py index cb9df5c..34b130c 100644 --- a/05_software/train/rc_mjlab/mjlab/src/mjlab/actuator/actuator.py +++ b/05_software/train/rc_mjlab/mjlab/src/mjlab/actuator/actuator.py @@ -174,15 +174,6 @@ class Actuator(ABC, Generic[ActuatorCfgT]): """Whether this actuator has delay configured.""" return self.cfg.delay_max_lag > 0 - @property - def command_field(self) -> CommandField | None: - """The primary command field this actuator consumes. - - Returns None by default. Subclasses should override to return the - appropriate field. - """ - return None - @property def target_ids(self) -> torch.Tensor: """Local indices of targets controlled by this actuator.""" @@ -271,11 +262,6 @@ class Actuator(ABC, Generic[ActuatorCfgT]): """Create delay buffer. Called during initialize().""" if not self.has_delay: return - if self.command_field is None: - raise ValueError( - f"{self.__class__.__name__}: delay is configured (delay_max_lag=" - f"{self.cfg.delay_max_lag}) but command_field is not defined." - ) self._delay_buffer = DelayBuffer( min_lag=self.cfg.delay_min_lag, max_lag=self.cfg.delay_max_lag, @@ -287,19 +273,25 @@ class Actuator(ABC, Generic[ActuatorCfgT]): ) def apply_delay(self, cmd: ActuatorCmd) -> ActuatorCmd: - """Apply delay to the command_field target. No-op without delay.""" + """Delay all command targets with one shared lag. No-op without delay. + + Every target the policy issues (position, velocity, effort) travels the same + command channel and experiences the same latency, so they are stacked and + delayed together. Feedback fields (``pos``, ``vel``) are never delayed. + """ if self._delay_buffer is None: return cmd - cf = self.command_field - if cf == "position": - self._delay_buffer.append(cmd.position_target) - return dataclasses.replace(cmd, position_target=self._delay_buffer.compute()) - elif cf == "velocity": - self._delay_buffer.append(cmd.velocity_target) - return dataclasses.replace(cmd, velocity_target=self._delay_buffer.compute()) - else: - self._delay_buffer.append(cmd.effort_target) - return dataclasses.replace(cmd, effort_target=self._delay_buffer.compute()) + targets = torch.stack( + (cmd.position_target, cmd.velocity_target, cmd.effort_target), dim=-1 + ) + self._delay_buffer.append(targets) + delayed = self._delay_buffer.compute() + return dataclasses.replace( + cmd, + position_target=delayed[..., 0], + velocity_target=delayed[..., 1], + effort_target=delayed[..., 2], + ) def set_lags( self, diff --git a/05_software/train/rc_mjlab/mjlab/src/mjlab/actuator/builtin_actuator.py b/05_software/train/rc_mjlab/mjlab/src/mjlab/actuator/builtin_actuator.py index 151c429..2f7cfa4 100644 --- a/05_software/train/rc_mjlab/mjlab/src/mjlab/actuator/builtin_actuator.py +++ b/05_software/train/rc_mjlab/mjlab/src/mjlab/actuator/builtin_actuator.py @@ -7,19 +7,21 @@ created programmatically via the MjSpec API. from __future__ import annotations from dataclasses import dataclass +from enum import IntEnum from typing import TYPE_CHECKING import mujoco +import numpy as np import torch from mjlab.actuator.actuator import ( Actuator, ActuatorCfg, ActuatorCmd, - CommandField, TransmissionType, ) from mjlab.utils.spec import ( + apply_target_overrides, create_motor_actuator, create_muscle_actuator, create_position_actuator, @@ -63,10 +65,6 @@ class BuiltinPositionActuatorCfg(ActuatorCfg): class BuiltinPositionActuator(Actuator[BuiltinPositionActuatorCfg]): """MuJoCo built-in position actuator.""" - @property - def command_field(self) -> CommandField: - return "position" - def __init__( self, cfg: BuiltinPositionActuatorCfg, @@ -96,6 +94,102 @@ class BuiltinPositionActuator(Actuator[BuiltinPositionActuatorCfg]): return cmd.position_target +@dataclass(kw_only=True) +class BuiltinPdActuatorCfg(ActuatorCfg): + """Implicit-integration version of IdealPdActuator. + + Both consume a position target and a velocity target with kp/kd gains. The + difference is in how the PD is delivered to MuJoCo: IdealPdActuator computes + the PD force in Python and feeds it to a ``P?sl~_O0*|eoHAh8 z7E5=HGm-RC`glT$7D9__%i(I@r9^1@3EtdqLiVia1j{d<5>>aff%zpV+;Z<SSn{m>i>qbJUR3tcYL Kth-xM Kl#rWYg_x<750>*S zaZVA4fxXJzc79p{yF)9=dTiX|mg>71mb!bu%834;{(KOfX|S8#(@}wT=O%+uyH&L1 z&SX3@rcCtGH^*IhYsqo442Ipc!?U^e uIY|fA4TdQN zhe^J_io2Rx5_l(N!MXYlr1Q}WaPo!VHh%IJ$^|=N7Xx#unS2E&ZtseB+UsG5O{3`N zLmx@~(i@~*%T&ByUr)@}xM7dov*@5r@wlkmT|D~bC7vzYQ#NGR9 afQpVYkQB02m)o1SRB zp6t}$M_&4Fpqqm468=#W%wPAMPQJ31w#q4^BWLSib(#lGYNZ5I+n;uAZq*0Wm90Qm zc@&82?vu^->ag{f1q5}pg0tNmu`a*}YwV5D>=Z{Ir&UnkW`I|KCGl_WMSV(YY0+jC z^g88APTCmasT~P4V8lnVq77h=!;9#fWy)YTVI}dn@sb7=l#tisJ&A>80B#k1#BuxM z$o!f1M71*q&G&Rb-P;4mePK7X%2mRT+qaOcbD7jn<-WMht|!+-I}snN8{zymYf06O z2)JBH@Ymxs5*3g|o<@d~EvjGX%*tL+Rnv~F?GeM#pc`aT^I{A*`-O~c5r-!I7rPa? zP9(GT7?RpWEn(l-=~x2B*fi;-82+iZI8{9!H@1<0YOgHrN=zrL7(Sj 1UqxUw4H1xy{&0FH-njmh1e+C`5pn~rHQbxPPx1>|-?aR)` z>d>>6I-uJ2IqCP_nI7ubojT@Oaou8K(CD>0_K5d^cb8J|RLfsf^X7VbWo=uut=~Yj zH1l9t-$FcjJ%wAg_7^#G^E&SK>WfjEf*`Yn4<;yAkrIDb+Ub)Q8SeR$1X~P(CN0*0 z|0yf-xRVa14m6=vlNX`sV|N^z&>rG$oFvDN1flHRO?s*=|U)15jtv1+U1DiLM3vtA)_M*nT zU=kR6O}sWq2eKS8=vQ?wwtF)Ienc+D$E{zxEzB!|8PD?wBs>t0-Imc;Tp~&9wH0rg zMUw&jc90v-F3{(e!>D-bK26DAO!i$*biMn*1J3cYFwZ|44|Lc}24-9%lkZf5bZTqb zVr~XLW$!?X9 s!Ij#gDP5Y5>MhUIM*M zoZ<1~JG4*g50ZQ#lI&TXL*KtQf@#NAVaTaeu+LJ1O;=9RcK6wv4M`ohzQl_Kb(^3Kp)d^U4D~=pKpUs6;58yIu1x)>R zOB~>tPOhEcKt1jNZC2X~_BJzidj=|a?Z7cQ;G+k3u74zL( +md1=AaATi}+vOT 9udwqV3FQM0u+% zeNk@%F{Ty}pPWTs&g1C>x7#!$&<_@9To85aI?& En51Pw9VB<1HqD3x#;7Q6T|6)p)mxOx5RAQbl6k>fi72B3mM5<$;Pl=a7fFA zeG7k_>X&7T^PSd` IGDcVW^J6{55M6 s|pQ%P)#N+q(>db}mJ~>oZ`MPdKifXa`&E^dM|> zE0F2yvN4xMwjNN1f&p>3BE2tE-_(Y(DKp8;1MlgLa}xKpyrpRC(F?mSY6B S`*99D}bM`;$9w!VrQEj~cvxHTlfZ8goAum!mU zE0VXmH}p^Jip67ffF3m O!nQ1N=G&)YtjcC+H_{n(=2eKn>lTCN>pa?1Cl>ceH8Du32s8Np=(GJU zHJWXQyEH=};KfS((lHw|mG|QM*9qXBJrZxWP$hHfbTGrl7w;~)MR%N&AbD3r!yagW zO@Ebgt#)(Jd`&W5r#+x<@GeX}J%ruAg7Ed}jab+IFuL~E yMyim7PyCX~8f^~OSjr$o8b1N-;cNgsdQPQ9!;!2OjxeK*>J z%r!K^wP{lPnyOWvx!;Q1e7^zRuRi9Y&+fv+$cgwXXAbFjKM7M_YLSm`+__0DEO66d zWpJ9|K(Fs=ik MC%+_ZclE%!5x>aDT|szY~;0%@}oUDNJ7632=1){J3Tb zSGpw9a%&|rxylS|Rvx2OMiJ0*vy@A<-6+nTVp%SGW(mm&LxAtK8~qbJ!TQ#cvU&YJ zmUa2gzAJMzhvt1Up|zC`z5LRNeV_OQ7kgK5*L$kL+ub8!d+Q_Aa>iWvyfK%&+H`|h z_tYoS!3N+qayNJnV&5-7GB@y~5BVzghM9@&@vM42rPB&X)P+uvo8?L-)0w1>JzFJ3 zyMVW{Em8M;N0c-M!~Jy&VehxDw0r6!aw%~UIk>qGMipEn=8{BF+0T*=*shQB7nBk^ z!31sY6Rgd!0lUNx^p#p~oSLYQV|(h5X?JV?4?|}fPE{8LU_!}E$`nFL5#bZ=*-NRU zRFaTNlcb3TL(wQh=6MPc87s+<$~}8slu%R>5h)rqNu`vg@BHwe=W*}ZXRY^rH`!Wo zo(f0!(h!%A)GPfO*)FFI)%~AH)|X61ZOJl-=!rokFAq8=W*nw$=_k)rmf~cyLh?Iq z0fv4Tg;!70$jj@_kam6{9-c5%Q#)dc5#w|bM{X1C?T!4&?w+{dNC>(zVyN%+j?p-# z12r`|AT#R=)1LN}hI~{+H_pdcTkngeO0VeAxZSv?<19U3;>XmrX0cQ4W$4X2R#YT- z5%+iI(`0@p{d;pg`L9p{-dZk%kFu|*DbB}Ss~L2~+He}yBMx&+Lm<~%lz-lyrMEsw zQU}i#a?4zg@MB+FWF;j~DX-ahs WNE?zbPP4=IL%X<^~z9MhQ%M)iwA?F||e{#lO>JAXEmkeuHs SLP+nSz&^^xMxWr8$wIiyWC#i3m CE8k$x7Gd0dolj0UaK4nEJTX<& z!SrWCBy-Uj=6YW~J>H3A)&yxm)Yq5f_e+8y--1z7b}?$ty-l{_WpHj>iBAJ;n4pux zWahfdI2t%gE?GRqm1qKe=6z@;sYxYDF7P`??BMYx1=wt0L*Mu2Fh|2hFl~+onr|7P zx_@S3Lq`yNS`4b$7AoP!zqOxs8fFIu38Y8o^W^w>OHM1#zS5%#%iQOa7yFe5%`x z32(1Kh=LE5iRJ#U9ka dkbht`Hk3#6-YU70u&KAG zuV_4`SSo<)vN*bb(Nm(f@d=7AdyMmMZz56`f3ww#voL0;ld4`a#<2O~IHtJ|T7Qhk zk+^ke>~RP3wWRRcbC&+lpMd8w&QZVn%a9#)#^5#Wlz*%Yx36}fdw05V&+rdah5RGi zy4vufdmuljlz|t*dE|!pRg8Im1r0EWyjbRj2H*Fh=7IC@>t`kPRnNc+&;L;IUd~l} z9!IUecwl|oB6!n18=^`-(DXINROGcenoUR~9ny7leLA=EEiyr4$p-f4^G%@m^Caqo zYSY|1GBEMT940J01fO-c(eI;&=*ES^#E_eBRvp$s4Y?|!wQxQNcWcl)f8!x&StMgD zrV2ObspHU9FOcfAf~TTWP `>w1N7tzrV>Z(s nyJ1vfLA|}$XFIvRvxFiIO zCDO%O;p|V&0eJ4Jinn~CNbGu3Jnpp^3pXWDgTuq*pQ0~x7mXuBHqFE&&mP5g8bh$u z3>@=WPL3Ry4A1^81RIwie9xPP!xnpSTJaK)ot1>i^ZQZnQWBLqCQd zgWe2F%$n>=9~)Pb<#VUw@56+;e&xaWrgyA%U<>cW4QDtQyd1JT3TdA757H|Z&R!79 zr}v7UvnDx;^r=iH#)xL)&oz^QT=Ar4Mq$)GR2rLZBJK>ehE$ax*d1Dp{^d7`h0|aB zN)6FFz>x^`i&|aj^F{Oj{!&-HUHrR^dvM#I@hD;2M @fM9R% tnTF5lV zt%IY&d+7@2dK{Uxjb`VZh2178=y*ScB;Jt}D4#FIwOct}LvR7Ma?g_DLxB*q{5$LT zbpfuMIT^cMSmNNThIe+$A`{$)#girRuI)l19p1}1PKIow(kzHiZe^P!TTxDN6UGY) z;N4~yTygmTR;?bSqTzh<>82V^j}#&~_Q_YZ`{U?WgRP`!-*4XPzoF>*M~!^^`-YwR z&W;x|Ad9zr QyId%-b+52F^#e3vOR52A>bDaj738C|cUa-Et5$@YX(J=GZto}@0 z7`N>^ob_A+xqahd%1><=A?=ve_m6mAxWe>gM$!vX^0@fUMGTAEkM}N2p`Ch0Rwnnj zdE@5$tk9-=?7f&Rc-$i#2klR@?V~N^&jc?x_ _!kc$-9AxK8@s6h3z*vbP=$c4EcskNaVJo1s%LHwn z=#$-LF68PPVHj=ZVb0+m=FW&Z?sM-X9p>XP%$`A&j7rPBH7m)<{13!CtA)(zOeTc} z&&UIP8@QXLi%~KLK)ogDZ>PIti_8@=UF0}_*{P+>;%W)36iz3Gbw$kJtDo#Wk#cf* z^KAUOY7%Nx1`_W;9f;cJ&v7B5WVgOBV-ULrwq?3vmx3gfetniI_Kd@OpH5QQ-wAkW zr#RH6+R~G(G`*?kj*N{Am3|bCs%Ooi!{t0VAY4jLo^YXUj%g&MR17m4qKLu)jsq~X zMZKZr`0|AVx|Ho 61Fg@Hf0e!wNq0R4igPr+G z^gQ{7y4+U7>RG9D^h^`~zk8El;k!~44y s?8u6S_Ppi#IfaDA~O)Q z79D;oS&1u3K*gC5=$)Ow@Y *#|f8M9E4xCGW`5Rb0kqn%XZ&^pQ{(( zTBq6E&f_>;9eD}YtWN?xM?1Ri`UadfT?;1uDPh@yS~~N^d{U7!!|KG&81naNAk-EH zVkF01N+_m5#kccRZ*TyAu6P17$}M5v?hu@mRf6u9r&v82j$-l$l*t;mD|r86HS;RI zpVy@u42wASRd4 rI5}&x>(+d<>?#9EUe&4Iyq% zE8cx4Z8aKtn)$uAmb^GphLvj @)Z1 m#o`N5sc<$e+v|@us79>*iZj7A*MNy$#@fheOH6%lJxJ)yyix-S2vH{dEpGC zo0h=vhTqiP!WSOq*-& *1;7iOdOig)BGsjf%*pLKqeJhNGXF7N{ zzP};gl_s)BwVx5!ahHgm<|0t&-j2@FH%NW_B9ff?5~b6<;JUdsT<(m jS_mFzQb^#p5E$ k7kcnQ4D>tNHk_W Gne@Y_eCpD`?JG_t5T$V^8D`)LIqD}#;^j~Ahc?Zp<98wb z)HII%7ZHxtFBTKS3O78E5?~o`_> Y%1QMx9l3*l5k` ?4@`32kX}1 zmb6(I>g5Rg9Bk0%_-veEybWlHIDIzX9~}>fvX<9QVQ|7RmJ!;HmIbkVr}=y)?!h}W z^ed(NkBqS*xt{FI&_d2>nSdR4YpLhkD4ZlE0lA;HV(p9B7{ql+o}HBzEV(^{zU@!O za*nYI$S!9ceI1xmuAd>+uZQJd)M3|YmVQ-}hXm^bka3;j;anLQzq%4@dqe4pB2hZ^ z@et{I-~rA>fSKQa(kLf0@>Fgz^?Dcq1=5;=KU-vI=KEN5yEXy7wB}<@s4UfcDF^!| z^iua0Tavff2aC4PL*qaJU3JDpaP5j57CJ=WqUfuvoK!3(&fNroUQ-#l<`7!2W)E&? z-$pmCUx>jPL2&%$Qo+Cz9Y~QE6KvkSgr3)=`1IR;`qR;fJKug|a|Vp5T9P(c>&sJ{ z^@o61)zjyhKLEn4@Y@GBoYA<8Xs@|TUMzjV)J#wYt1YTHJ@qfnK{paM_awRhss@jL z6osD+n(*TdOA3n?13O@g)8D${@^NlBHhUFl+&zmqw;I@74 AVBMthL5kLH5oguzPGbyp-525VEttTs;#(`K{%G5tV6V``i$iSm=#slwEPr;#RD$ zIF4 -3tF8%@#w%sVnKg`KK (1)wc$IUdx09g z_ageHwIww*{EFi=)!ZI5O9LKN+pq2Q6*h +);YX`W)oe7^ko`UjQTzMZZAHrQe`dB5VYEkk^7Fw0p(e!mvprE-Fw8$ig zE?tg=U&5%3ya!FWtO3uHhAhW~ym>>vjbZ)Vo#=P&3(eEq1^czM$+yTJQmh<})0|Jx zkCP5VmEj)Nc$X7d`5~5WtPutD8Kuc*Hxe_WL|kXBgr^tn$5Q`v AN}qdj=Xc~VE V=yraKM6%3DbK{KPcPDEs{>H! zTOCoDCxyAC>(~R+m!U&`4JmcYV7_KY })7{Y78*v_j%xh zzq)j3&lcz?oQ4Mv3{YY9NXqG3=(8aa?Jqtf+c{75PxCH9PdL)4zZcNBzIM|7fk)oW zumZu~r!r5S|{62i~LcP+#$f`TKh%>DNqwZOL=#hh|OaD*XlWJEO4C$c?}2>^Q5r zPjfIxCJI`fiCKxCJImH{EgogRMWEA?$ByMkqe|xu;wCFatdwQo+@na?pi)jh{^%kD z@{!;(brU+6YC@mK1MnzShoF%MsC%Uxl+UD+?x 8#Ju zUD#6cl&l V~Nw8ZTrYkFDG_DW#O?HqW=OE+` EEYl*w_`hg${F0RRg?e3-tarG%%qSi z0Fgy3Y3RQOBW_PDjh}`>S5qPc%V=0Nz1s<^E=f`k>kcxG+qXaN_JA#a>p(BSlYDfZ z!MOV-L)I)2E8Vj#5OyoZ%BVSr7FQWzS8x`Lwxy8Fqn}B6lNg@&6o-vtd!XKBFOPot zNt-Z__*M$T+oEP-{9F I-S9=o9(*jViK$PngYJEII2^)#o*OsA z`{(PKZQRaag7gxS>(@Y@m|Z2+8&b)kK3(YIC($j>bI6))ZM6JH9C5upNNjuc(dv&i zX lmd$!XBt+DyML6d~olnHGqkKBQ-k%B1o=w*T zU!!NuRM0E7ll E!G5lO)Ua7G%ZcFs3n1P-v!OC1w49Rc)UP z6D}!QRSljaqK}V)!3A|IA59n1XSkKAIPnaAl`O@nlRh#LkOCFsE?DY{4v^EM(QsW= zyrwH_0!(k?L6`4h8an?W(=~M;RX%bBnCVhb_hB!XyO%)b8CNj |8 zXfR5Iiw`^KmY O;{4b*^lsKw67upX{oE=9 zL(>Krv7d@CWwtRCFx|9vTNljMdPinQ1cCL>8_eAtQCik)i@6f1q-SR${9IBFQ6oW+ z7`lez6f@A) MYulK6b!z&poyv7 z(+l(=nYxd2W z*GhKGx(&9D@fh8{mwwo<&e~S{fOibnCaTP3Bd$JSr$unQu-7(7f7QXis4r% WtA<0cSGv;*0FCqed}4e9!|7WPc3pw3T6$&citutiS?td*qjx7S6Ide#EI zW(UELsvfS9cA(?!Oo%==!>DNOpe=4GG@|P>$&FNnpJvCY7da1?L}pS^n|-9`+)QG_ z&8%AN6PccJ13Hqf2uXkC@VH7b>mM~nhZPm@U*#9Zb=^!*Q9X$fx9jO#vB$(_g$Noe z%!OV b>Je~f4?GO7v3cs$$$6riP>3T5h7x|q; zKGI-ALbSM +orf%OpTWMDH6jqp5&e4Hqcoi z3ChW?
HPh9 z;h=r`6uyd9Cl|ZZxLIp5F3BB$@4^=F&!&t~f8qdQH=XG@AzetSnP;`>?Ku)> GGe@QZ8m5eDuu4oE%A5Vey;cV ziWJ>gjR)d>qJW&m|H8hp%Q6>W?4m_@Q>>HcU9%96`SS2zPBE4`WwATna2=OJWwgyJ z1M4JfNqbg0gFarkKM^4NK{U3`*p1Joao*O@3GC6|h<}EQkQbUvD>$Dqe# ssT8Qaw9CxDhk#GsrYsqya{@^posFJn!+6zoUEv$tP2+bPdLj>?dk1GL7Bz zri 7t_gG50&*+1qPWvJ%HyTu$ckxJo6@9k+xgXW#qWHqdH9yT zt +w$7a3~QYx5?p9f*GWwDS}JSOxW>p8B^)Klw->t(!C=)aMb5E zJH5jP*VK2B5RPm0EY!r4jz!?iH5aG&eIr#`k*Mu132WT5$kLG}GGuoZ`5Qu^;j%yT zx}}urce=vMd-mwNIDr_ve@dQcT*ptx#GokJ7gdwz!0_SY=>9{=s^fMBVM>p{p{iA6 z*du_ZKM`S1ZV|=2?c5owuLSh_)?@mQbclGYghyAnLh#)-$YEwsolVm@zU?~m^spp` z*h$h0`;UW4sTKay;*rf4ZHQ1r4^i2{`IpW8Bxk(^e)bK4%*xN?#_b(!a{VP5T>qHV za$cUwwS3y8TgoIgPr%&yC&=#)9*|tYu@v8G$*bo=v`}a#(U}xN*3=}^y}wLQYGx2P ziXLI5B_DBoRxWi6T*sgbH*fkTjS2g<(SLa^xL_KxHMc(#fu#g2GiV{a!p(F;k0MbT zjKqKd3tst>dDO~x3Wy!gWcy}CfvVzDGAe5gKfc;h7YVK(w{Ifq2yH?Chl5P+D1$FI zsIr-cQd}n{98C54c#kAYNW{t}diRhBX{^a%+(vR}%&ic@lX}IPt&zj9!8TGPSp~bZGB5{67C4>G-&o`idxm^U+Uat;j{vu6Bw{Yl)@S ztCNUWhb&q=n2ZnRPKV7S4s_R^g+y`I65w<5-3>Dy;!KPr9eJ&!aq?PF;y KAqWJ5-lQ4Zah>ai5ac`*>FnU6lrsUlsS>A0V_eV1McOVsPR`VIpm|C8u z=0P|$Uk@}2qM bj=C~xeyH}y2|N?&N VuirTe;ue?6cX%OuZiv^_N@4B zJCJ&O3g)ekgYQuf;NgzB@LWh0s^YTf@IW0bG-k-yuRFNIw+=rm$5A{#7kC!S(CTgs z_+IygPSN|M+EN50dF42e=E=^r+fTgZyHVMDf?&(&Ia;tNl6iiAgb-0DdQd+aWbBT? z%%MRdunAyV(o>mfm4i$`SrfRmB~n~)hlrF~;N$bR$fBjEu|ZiB|J9C?gN!A7UF}9= zMFMHENDglNYl(Zfc1vDQF6PxmqH=W!{B6i2CyzGKP2X=bvXeY`Qg&(7>XIlOYP>}i zdTXi20gmI}7ee$8WdO^*r^!eBVA 3}~aRvx7*{?-q87&p9kHxD1jvmkYE#lmzcepOFQsK~&;HHtuXwL-_Ow zlq60;k+~wKU7HJ%>`!c#P(>MqEmZkxBdGN5zy{qidMxlQ9Tb-)YSZ?y50Y|OJ^nSO z^4VrOZ(IWj377y+IhI8JK_NLabrF%jm4>e6XL;s#XA=3D*-Xg-3sSdxV~yesu3a!$ z9(xB}@Va9mm3Pjh&2{5&Qp{x9Y~e|luI?j^QD@-&e 7>E6%fmcg#v>-a@E#kxe;r8MOvcJ(&l&6Gs$`MNPpTB>3rx*&u=wkU ziA74J)HDH^yBxprMjQ8aY-GLW4pGwpbJ}+z46pwXWp{n@$4RJ*x=+UAnqpgOXQM>4 z)v`fhSO_1czNVk=Y{RbiiQssNp;szW>A}G)ViqKf?;_gSu@iH_C;B(heY6rQy?@d( zyfnIFyd^BpeM;_+I>Ej}np9|=BR;L_Ceu4yP{FT~Ja5^Aogaso)bH`wY*~Qft^3Kz z9lJ<1*Sbi&$-w?@BUl>f0lM?dxX<$l(KWEcU}0;p*|G!+R4tH pY zVD#?HC+96WCMV`Rb&`wW_QogJ4eh?Lsb+-gn#B^=gIa*C3W8NCk)Zgc2W(Ab1%g>8 zAvn4o(gp{(`OQPv>}-RL!_7>mcRQ&lodLQlvtiCcFR0jIj uWN})msWR!^xoa{ug0ih@-x74$~8mK#xr?C-V<#Auk}4_+N5BvGHYC{9zKvM9oFI z_!w5LNdSe^161(S5T)bV>7k(=xYpr6yfbAPWOSd!5w00xsUHjm7N<}y>jF7sz_EFG z;+Xz@1bm9bQRJZtKE63d&W;<8e8)p@Ymo<)a(hqK_NbzU*$>F?3dB$IUeeER|G~$( zrC6I r6mNVFQSjH@M=5-#Dfu(>!m{2sn&hSTgoF~PSa zC-9mRg=@~Zk-;}n5Eq_JWotDBA7@8F<&OtAnm!e;P1S&1ekOQ-NFB|Va|1jl7He!} zp*$sruCWmVJtHx}hVR|5@2|MPp_%LE-Wh_*-O*G^w-;9xC&DemO3bb3hL6r#V0w5x zl$~hDLwbinJ2ekyavW^%Gkw^zHk3?q3dA3a%;4%5J=}G}lgKSdplN(JxToTefdvs{ zy~Tbq{I-(*7D&O725kuariu1)`uI3 lx-y#z;BU&RX#cH*pS69jP_Lw5h1D;|8Dgx!x!Vf*bM z=-B?67QLAVB?@k|er_>!@1Ki2$8wT?q?v0wThpZr9kI(N8#A@qSX+Sx_7=9|jK{@z zFHHv?ZMZ~bqf4MWXfB+aEH8*kX~(- IgQ(_jPY@@I9b^n3EKV3sP5*|{5s`l(C}58#6MVr*|`Oz z OofH(nZWERsg%28p^uP1?kDhNx&&HEdFbQo5>PT4w6PiDHB{3&XDD2FVNKe zMI^fa61}N)fb^KfGtqZV@E+Io5tpbS?_2rw;PoPMC?$;=-TTb0`+ktrFJDUikMl`$ z-2#w2@R`+jCtPoM2FOWC!E{sZ-T(Sd+LSqs+;&c9JJ0Q+r4# -EPVp}++Yb? z?5mh_;wB`le=_tQKS^tsRFX#{;nYOt9+M@VOI1WE#~ZFdi5Jae+X*{J8yCzUxuZtb z|KZVd$=OuxiUvOTTtrS)rL*rZ$It^m`bbw{0@YX`%dQ++3b%FSXwc^fIOEYugOAMv z7rKaScZ_1p)b!|<>&xjMT0;+AT!3F?XTi&2S@1FaK|Z*gq)%UWk$J0ph`z`)v|UwB z{R6p{#q$!@`++LG9U_KLO!eq+jV|qd&GnDSHzK;elQd_=fvxyNssY={mx4$LS$qU6 z#}uHfMG-{aNOIlKrA)lL2RxSgK~D5sLaXK%bjPqip7ZoV%N0TN8MZUV69N5;*VC^% z!oZ|s9!bqzPS!8cfVQ=Pv|PcOD!n{K28IwH>K|o(9+o7>tV{68djYHsI0{Dh1Mxta zw$+s!Pjc8g2|rrMSSg4q(wG7(47|75YR~-${CXu312qp=*}vgqF>?fXh6=Fd)D=wo z muj$oinqc!#8t#2Og}U4w0i DTW=BGCx#3L%;Ny+JbEv&m m-k0hrS~0 zN?ifrTaEEcoj-hNI0< z#R>4JcLS)>lQg4sGRRz8067n{LG?i#8}?loertq~CsFsAXUiwR{270E597@_{!JdP z-{sDPn^U k@ zTfo3l4b;};lY(EDX;kq^(j=#bP1A2eOUDk ?X!CBL+{0ZRKtb@W<+7&**pq zDbk}p3R6Zzts3OwaOv0Q%z=_H82l0g3of+s_EuU`s>S(|eWr}(;#S7t^ctAl(?FTO zp5VOU2AeeE00FY+VBhz4db8M(-e2&PAGJCJe*BR@a{m`OQ7i-1+$>q`;AfInd!0lX zaSe=AMP81uKDM6V*hy&{5~LJQp5N4mW?3KBH}o3`cxHf4kNqaX@5S*HI-;t#6Y1R1 zO*Z!uy;nbyQ;l;^$S^FC56rcwIs!&XL86HzDAj*-!1>+@k$D&D865JdQ8q_ kb3hT_apndwx+uYnP(T}V#!OTd_gGTe`6z~Iw+Qh5Ft=@^@e z7sV3Eu(K%+yU8)pu9ukp`F~06PZeAc(LlQ=jwfedO@s$W+h}I+DZ(t71+AJDY-aC# zdZ#Ic-1v0_1n+jx6_aPc_vbU|F54t(B5KT sk`j2X+6C;Z&kR3Yg^fF|hS97TcW1yVC~21h zdnH@0%T|mL6K2yRGDGA|BX?K9eG>wEwnO%4Az8G|hA!L9V%5D 4kZ8^cJzH#KWs| q7~;H!IW1 z7n`BqOP8$fw1!tMxg=dznur~^OQhXHVDaozcuR5|O0d_!thR*g)i}<|I@gnA*Tql* zMzpCn15A8hvhg9IWY0q -#DKUmVbMhjRyVh-M-d&uG8T%7Ka zLJxUKSvkZv HJ_{PQFJp^T=i!Q*x!~F3 zhQ^se5Sbz&xUDpTlN76{|M>A%$5N$8#Q_C&bz?qB;`aZ4q&zXiRmkd=9*gpq^2ko> zU- G3i2YH8Ei{?k-91L_vs%er9mLx3bEHDaR 0~mcx|}vf4U(U~H1LyR04f)9JMj0pTz~g0NVj_mb~U@AbP9n;(F>qj`i+@)A&t3c zFT^N(C}6T}^y!5>D}L(9iR98?IUSS?rA69jNY|%1)O7V;OS|)# Uc4Qq;q`^ z(lHU!j-AGyaskZu(8H*r+w4x=Z2HQpnI4yT$WGHOpo*cKV^Uto+KjJ)Q{Pv>a#aR2 z=NrM~3wu%8-~$`dC65 j1?g*p{ z;}dWic}XmyiQa3H(zRR>PYIy1YLU!MLqvE#EPE6D3=3!1#d6Oa!|cz4Ps Jm|yd?lfBTDH#=s7M#tq zqI)-(!*1@JdGve?zi472%5m+!hb1PMXgJ8bvF ;WhEsfXDAzO1#2<#q#rAEWG!2Ks0{zB6@i7KdBpBk8N0y0 zg__;eV73#^XB}{+N#A5qR$i3)^{Qe*{7MX;Bn>xdD#;I3C2 {8PU0f2CULZ&6!l%GxnGN*nxAPb{ z?i-rFa)eKPBcx&TCSv1lh=%W_agK8xX?(F71`7Mh*Ca)l@4u4%tM8^REfvt+`-+Ti zHDOBjp9C7O3p)(kagFI6a@nR3bYpVx&^lMD_uAMh-nRiA7K}r0-(6NOX}F+AP>I`O zzGA`qD@=f`Ekx>U#`Mofs`rf$BW`zc$mIhua#O;O24bjJ5X1UT@Sy{@t1#He7KPF~ z>6r64Zr^Yk>)Vgh$m7OPDDa^5liJAlzJC-se_xSE z7BNJ7^?4w%&Ym{Ckq1Fz5tTFY0!4FKM2?Z}shB}Wc3mKYRDhNldm%1Xn2hk}lCZwt z3~`zSQ>&{n@JuynhLgMx8w7aLxBx|a!XfQt32vz3oUw)&YPPVK-8`+6{df2yU-#Y` zd{uu8R(6hXd}2LaBl>~H$wxrx=OV7PbDo6meMy#at;goy?!;~GL8?15jL!H8aPWvK zv5zxFgEfP6AWj!n`naKAe;o{8wPBn)r{a{S<#b{-=P^uA!7Ex0WbM>xB+cpy+BLM| z6X_1%rzX%RwX#rgg<(t{KPHdGl|W?pH8CjJK)c&B;D)6(?F^iO!QpAx9cMOB&$# zxPLIQ%nHXV_R~7c1sFedGh}6yl0O?4gIi@a?)w-+0?`@^uTc8-N)Spn#ABh!1oGwf zI=aVYE$lAKqNV>NV0gky-0 J850< z2> 0)_GxA* z=T7C!J`cM-??S!&aNMcVz_&?c;E?EdGQhPRu8^-p*Xbj%Iwy^PkZ=sjBl>x{CF$_I zN%hk?x79t2tf*-te=@lae~y50<#@{F)y`=Cqk(l)uY+x)r-;NXWgI;12JdX @)s%iFWQ5N6u(LayhK26ZVLUo;TErGBiH2<-A$Ud z{UrP1ZjvJQGr8W{MLL3SkW)q>^!2{&xM)cj`-B?6GF3hB`p=a57z=^Ys4kq+{|{xj zcc1IBN@&B~@iC}kOeVE8kqbx4$jZ$s)bpw#FvUMenSf&yq>b?79tX1U<3d>STMFl0 zjiWn1*yFA@$KbSLA{qE>Mov_Sf!gh7 g6J zi{cD#O&U2gN|n|gWlryQA$A{Gvf9B2>)tJ)o }p92A9v$1eqGy5lido_vk zr2n{Hz{ AxdDX6 @gSRB6{R$TJp?&INHQxj+G)j7bKlGfTDNHkMK-kS_M;m#v6;aCAm9;qO4(f?ts?S1N%+y;%k zzU2C!$E29!ik{yZFG&1aON-WNgO-RG =C-?`?fhPuDI9@h^O=_Hopqor0-algB{q}>#wllmV zZzh6qxE|4tUXRsPf%sUeoyZM`VXJR1$=R4p&uKYgOOggi+!~+;GFPcxUpTEZNF*Eo z+d=m SvXw>W#Xud=XeaKXDZS|L$$?cjjS3&_SzUre9-G@g%`LZ6L zR?)}?d${iHL>pGzp?ePN ub1wCpcrYmcqRZeLMzDkWl^Mp z;|QDn>Tr(v8yr{gj;>n$2VCwalALw->7z?&=<|30w{BL3ibQoRd&A907T==hRmF+y z;7=muPzamPWU~KLbl&k;y>A>xR`%XAD-E*3bMEU&N=Z_RiUul4rJ+(%NhmX0Rzsyo z*^1}f7Ya#<(jxjQ?V+@n_?_Q>{`I_G=bZb#uFvQF77_H&4~tL1Qs+k6YNm%B*{eZ| z&4AF?KKN6-fwfj{gPsWM2FuW`C>eExzpH0vLwrg()z2-*CEPu3%Fbu((&w$X;>!m7 zp?jPho} ^uKc1uXK(+l_-n1WxHchme}2{b4_h~K&A{%K?;)FmxIm3?R7 zHY3t-#Wn-0dPA_j+Lq7sb>p`%AJ`m}jg}Wi==lzgS*-Zi@k3Yx`qeU|W%Xk`q!EI_ zUluWa&PM$5s5(5jFb5(ZEJX{?Cpdce2-);1ko>iZq3OT!IbUxwE|Z*(ojNsWFE7tG zj=zq 4Q3|)B3Bd*8=Cg@VyZO9rt6gE8!qd{8)tJM zL(2zqhw3m=YcbhbrV5VXzBpuWh^a@!8-900QD!mXbya)*Yq8I`CEcAVnsW|&Y$%Fu zI>ri7Q#$CjoAtj|PQo|oG-TZu;ha-eL>m0@bBYjzNi4^w>(tTRat|7WOvMl3<{aBI zpRThyPBJ8a688-)n6rm-4sQ8LN8*IhKu($bu9$^FqvlA;t8tpUB^D&yrzZ8s=+KxR z=tX5yLERISoFNP^Fq>3_mC#cv`pow?SqzEoBQ0AEk*Vn>&B@AGp+81{e~H4<;dEp^ zL=w{>uJf{O91fMmlUa@9Nrt#5C6;`w2;2;(Iyt6Zb|apVYb50>uX1 tu6wEIgjG zko(@K;@UEAqE `lJIhcoH_m8p8>DF{iu${bdyh>?wG_~H8 zPfOEGIF-{xkT R711l z7l=xs9d4U42MQ17)7>`Xr$5}^Ol(VYi0n}<^q3ey%6yh1yIvMWN03b!bD)LmP2uti z1H7j+A2!X|C3y8%gdE{IfaB|C;^>eqR&Mj5DY-XDz@Ib3@%% |sLnGay{wim6y- z%h+!5VR|^u{S11IKIdi@JcoGNjW)1BG#Ll77n2ipM`*I|6*gD#G%Is0lC8Vn$hz?S z!PoK(tt-|9qvkA7R#gI-j(D=^{w1<}`ZRD(`a?c@{)dm_zw#<4E5MKG64Yq#A@*yf zB#fnulCmIUc;cl@PmVIMF7Xgc)yp7b(l?@FvKFIbTF4*ny%4=Vm>l232XXCxMD(Z( zt~k>UOG_2tZpvm%9L~k)p9yq@kuXZX7HQa#>O_BB)@K$qK17S>9`Hc;8$16>0j}&f zfX6OoB>i9xev!M*h~_ZppCX1Edpns0Z7n2R{2ZFQYQoU&Y-FxAOkN*!pWTUdI1u zzvq5b{!)T}M1E7tRv*%cSLv>43Ao@+43$13i;=swa&9DftoL&!Q@MA>vcK^t_d^|I zSLEQ3V>v3e9Rlw>1#-@_l }4Xt+#>0sP$IzQtr*?FuQW%s&~=#$q$$eeQvwMvnSrMF4p<@1D%xk >pZy@@=LdPfUF^01v9&)cWAjkrA@rC&3q WKds1lZ`q^01??qm_D&c=#guJ?i)JX0$?S6KTm%@`HU&R_(&))ZB z@_`IAEE=jiIa`_(c2C4g`i^Su;_eQ=BhfiS6o2Za!Y?U~$F^M(E<1iA@l$r7{IepG z#^shGj-I7wtZE_QLOJYEN&}aUYH+E_ASt>B!KWz-O{?``vh_5+P_`nT9>1afxsW(O zRU9NLYl3R?C+Iq~lxng@U^%T5q!XU7o5p&u;p%Hx-IzpsQp>>->f!UHPFgI?@kHl} zqxw~@D>M5wnUb%FcS37P;|6npd*>l<