From 734cad2f16edb4507415e244a332f59249a8dfdc Mon Sep 17 00:00:00 2001 From: chidebele dunamis Date: Sat, 12 Sep 2026 23:42:47 +0100 Subject: [PATCH] Drop the sillo.wire alias; sillo_wire is the import path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The package shipped under two names. `sillo_wire` is where the code lives; `sillo.wire` was an alias that read as part of the framework, built from a meta-path finder in `_sillo_wire_bootstrap.py`, a `sillo_wire.pth` to register it at interpreter startup, and PEP 561 stubs in `sillo-stubs/` because static analysis never runs import hooks. That is three mechanisms, a `.pth` executing on every interpreter start in every environment the package is installed in, and a second set of type declarations to keep in step with the real ones — all so an import could read `sillo.wire`. A plain top-level package needs none of it, and inline annotations become the single source of truth for type checkers. Removed `_sillo_wire_bootstrap.py`, `sillo_wire.pth`, `sillo-stubs/`, both `force-include` blocks that shipped them, and `tests/test_alias.py`. `sillo-stubs` also leaves `packages`. CI keeps checking a built wheel rather than the editable checkout — an editable install can import what the wheel does not ship — but now asserts `sillo_wire` imports, that `sillo.wire` no longer resolves, and that nothing lands in the framework's `sillo/` directory. Type-checking the wheel now exercises the package's own annotations instead of stubs. Also drops `sillo_wire/__pycache__/*.pyc`, which was tracked. Verified: 124 passed, ruff clean, and the built wheel installs into a clean venv beside sillo-framework 1.0.0a1 where `sillo_wire` imports, `sillo.wire` raises ImportError, and no .pth is installed. --- .github/workflows/ci.yml | 25 ++-- .github/workflows/release.yml | 7 +- CHANGELOG.md | 22 ++++ README.md | 42 ++----- _sillo_wire_bootstrap.py | 104 ---------------- pyproject.toml | 21 +--- sillo-stubs/py.typed | 1 - sillo-stubs/wire.pyi | 48 -------- sillo_wire.pth | 1 - .../__pycache__/__init__.cpython-313.pyc | Bin 1943 -> 0 bytes .../__pycache__/backlog.cpython-313.pyc | Bin 7272 -> 0 bytes .../__pycache__/consumer.cpython-313.pyc | Bin 8931 -> 0 bytes .../__pycache__/envelope.cpython-313.pyc | Bin 4212 -> 0 bytes sillo_wire/__pycache__/errors.cpython-313.pyc | Bin 1588 -> 0 bytes sillo_wire/__pycache__/hub.cpython-313.pyc | Bin 14953 -> 0 bytes sillo_wire/__pycache__/peer.cpython-313.pyc | Bin 10198 -> 0 bytes sillo_wire/__pycache__/policy.cpython-313.pyc | Bin 1141 -> 0 bytes .../__pycache__/testing.cpython-313.pyc | Bin 4788 -> 0 bytes tests/test_alias.py | 115 ------------------ 19 files changed, 52 insertions(+), 334 deletions(-) delete mode 100644 _sillo_wire_bootstrap.py delete mode 100644 sillo-stubs/py.typed delete mode 100644 sillo-stubs/wire.pyi delete mode 100644 sillo_wire.pth delete mode 100644 sillo_wire/__pycache__/__init__.cpython-313.pyc delete mode 100644 sillo_wire/__pycache__/backlog.cpython-313.pyc delete mode 100644 sillo_wire/__pycache__/consumer.cpython-313.pyc delete mode 100644 sillo_wire/__pycache__/envelope.cpython-313.pyc delete mode 100644 sillo_wire/__pycache__/errors.cpython-313.pyc delete mode 100644 sillo_wire/__pycache__/hub.cpython-313.pyc delete mode 100644 sillo_wire/__pycache__/peer.cpython-313.pyc delete mode 100644 sillo_wire/__pycache__/policy.cpython-313.pyc delete mode 100644 sillo_wire/__pycache__/testing.cpython-313.pyc delete mode 100644 tests/test_alias.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e1a4b6..4343870 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,7 @@ jobs: pip install pytest pytest-asyncio pytest-cov ruff mypy - name: Lint - run: ruff check sillo_wire tests _sillo_wire_bootstrap.py + run: ruff check sillo_wire tests - name: Types run: mypy sillo_wire @@ -35,19 +35,28 @@ jobs: - name: Install the package run: pip install -e . - # The `.pth` only fires for a real install, so the alias is checked - # against a built wheel rather than the editable checkout. - - name: Check the sillo.wire alias against a real install + # Checked against a built wheel rather than the editable checkout: an + # editable install can import a package the wheel does not actually ship. + - name: Check a built wheel installs and imports run: | pip install build && python -m build --wheel -o /tmp/w . python -m venv /tmp/v && /tmp/v/bin/pip install -q sillo-framework /tmp/w/*.whl mypy /tmp/v/bin/python -c " - import sillo, sillo.wire, sillo_wire, os - assert sillo.wire is sillo_wire + import os, sillo, sillo_wire + from sillo_wire import Hub + # The package is top-level and adds nothing to the framework's own + # directory; it used to ship a .pth that aliased sillo.wire onto it. assert 'wire' not in os.listdir(os.path.dirname(sillo.__file__)), 'wrote into sillo/' - print('alias ok, sillo/ untouched') + try: + import sillo.wire + except ImportError: + pass + else: + raise AssertionError('sillo.wire still resolves; the alias was removed') + print('sillo_wire imports, sillo/ untouched, no alias') " - printf 'from sillo.wire import Hub\nh: Hub = Hub()\n' > /tmp/tc.py + # Types come from the package's own inline annotations now, not stubs. + printf 'from sillo_wire import Hub\nh: Hub = Hub()\n' > /tmp/tc.py /tmp/v/bin/python -m mypy /tmp/tc.py - name: Test diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6738225..ed4aca2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -77,9 +77,10 @@ jobs: - name: Build the package run: uv build - # The whole point of this package's layout: the wheel adds a top-level - # `sillo_wire` plus the `.pth`/bootstrap at the site-packages root, and - # it must never write into the framework's own `sillo/` directory. + # The wheel adds a top-level `sillo_wire` and nothing else; it must never + # write into the framework's own `sillo/` directory. Two distributions + # sharing one package directory goes wrong in both directions, which is + # why this is asserted rather than assumed. - name: The wheel must stay out of sillo/ run: | python - <<'PY' diff --git a/CHANGELOG.md b/CHANGELOG.md index 936ce1d..923bbcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## Unreleased + +### Removed + +- **The `sillo.wire` import alias.** `sillo_wire` is now the only import path: + + ```python + from sillo_wire import Hub, Peer # was: from sillo.wire import ... + ``` + + The alias was a meta-path finder registered by a `.pth` at interpreter + startup, plus PEP 561 stubs under `sillo-stubs/` to serve type checkers, + which never run import hooks. It read as part of the framework, but it cost a + `.pth` executing on every interpreter start in every environment the package + was installed in, a second set of stubs to keep in step with the real + package, and a name that static analysis only resolved because a second set + of files said so. A plain top-level package needs none of that. + + `_sillo_wire_bootstrap.py`, `sillo_wire.pth` and `sillo-stubs/` are gone, and + so are the `force-include` blocks that shipped them. Inline types in + `sillo_wire` are now the single source of truth for type checkers. + ## 0.1.0.dev1 Development pre-release for testing. Install with diff --git a/README.md b/README.md index b005d26..517b3ab 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,11 @@ Rooms, presence and fan-out for [Sillo](https://sillo.build) WebSockets. pip install sillo-wire ``` -Installs as `sillo-wire`, imports as `sillo.wire`. +Installs as `sillo-wire`, imports as `sillo_wire`. ```python from sillo import SilloApp -from sillo.wire import Hub, Peer +from sillo_wire import Hub, Peer app = SilloApp() hub = Hub() @@ -66,7 +66,7 @@ await hub.replay(peer, "lobby", since=last_seq_the_client_saw) When a peer's queue fills, what happens is a choice, not a default: ```python -from sillo.wire import Overflow, Peer +from sillo_wire import Overflow, Peer Peer(socket, overflow=Overflow.DROP_OLDEST) # keep current — prices, cursors Peer(socket, overflow=Overflow.DROP_NEWEST) # keep order — reconcile later @@ -98,7 +98,7 @@ joins the rooms, pumps messages, and guarantees the peer is removed from every room when the connection ends — including when a hook raises. ```python -from sillo.wire import Hub, RoomConsumer +from sillo_wire import Hub, RoomConsumer hub = Hub() @@ -122,7 +122,7 @@ app.add_ws_route(path="/ws/{room}", handler=Chat.as_handler()) Retention is per room and capped by payload bytes, evicting oldest first: ```python -from sillo.wire import Hub, MemoryBacklog, NullBacklog +from sillo_wire import Hub, MemoryBacklog, NullBacklog Hub(backlog=MemoryBacklog(capacity_bytes=4 * 1024 * 1024)) Hub(backlog=NullBacklog()) # keep nothing — typing indicators, telemetry @@ -133,10 +133,10 @@ importing anything from here. ## Testing -`sillo.wire.testing` ships the piece unit tests are missing — a socket: +`sillo_wire.testing` ships the piece unit tests are missing — a socket: ```python -from sillo.wire import Hub, Peer +from sillo_wire import Hub, Peer from sillo_wire.testing import FakeSocket, drain async def test_a_broadcast_reaches_the_room(): @@ -165,32 +165,6 @@ to reproduce against a real server and the two most worth testing. | `Backlog` | `MemoryBacklog` `NullBacklog`, or your own | | `Overflow` | `DROP_OLDEST` `DROP_NEWEST` `CLOSE` | -## The two import paths - -`sillo.wire` and `sillo_wire` name the same objects. The code lives in the -top-level `sillo_wire` package; `sillo.wire` is an alias, so it reads as part -of the framework: - -```python -from sillo.wire import Hub # both of these -from sillo_wire import Hub # bind the same class -``` - -The alias is a meta-path finder registered by a `.pth` at interpreter startup — -the only hook that runs before an `import sillo.wire` could fail. Type checkers -never run import hooks, so they are served separately by the partial stubs in -`sillo-stubs/` (PEP 561), which are additive: mypy resolves `sillo.wire` and -still uses the framework's own inline types for the rest of `sillo`. - -Nothing is written into the framework's package directory. Shipping -`sillo/wire/` in there would be simpler, and it is what this did first — but -two distributions sharing one directory goes wrong in both directions. -Installing the framework from a checkout moves where `sillo` resolves and -orphans the copy in site-packages; removing or replacing the framework leaves -that directory standing with no `__init__.py`, which is an override rather than -an addition. Uninstalling either package here leaves the other exactly as it -was. - ## Working on it The alias works under an editable install too — the `.pth` is shipped by the @@ -199,7 +173,7 @@ editable build target as well as the wheel. ```bash pip install -e ".[dev]" pytest --cov # 100% required, bootstrap included -ruff check sillo_wire tests _sillo_wire_bootstrap.py +ruff check sillo_wire tests mypy sillo_wire ``` diff --git a/_sillo_wire_bootstrap.py b/_sillo_wire_bootstrap.py deleted file mode 100644 index 579d9a4..0000000 --- a/_sillo_wire_bootstrap.py +++ /dev/null @@ -1,104 +0,0 @@ -"""Make ``sillo.wire`` resolve to this package, without touching the framework. - -The code lives in the top-level ``sillo_wire`` package. This module registers a -meta-path finder that maps the name ``sillo.wire`` onto it, so both import -paths reach the same objects: - - from sillo.wire import Hub # reads as part of the framework - from sillo_wire import Hub # where the code actually is - -It is loaded by ``sillo_wire.pth`` at interpreter startup, which is the only -hook that runs *before* an ``import sillo.wire`` could fail. Nothing is -imported here — neither ``sillo`` nor ``sillo_wire`` — so the cost is one -object appended to ``sys.meta_path``. - -Why not simply ship ``sillo/wire/`` into the framework's own package directory: -two distributions writing into one directory goes wrong in both directions. -Installing the framework from a checkout moves where ``sillo`` resolves and -orphans whatever the other package left in ``site-packages``; and removing or -replacing the framework leaves that directory standing with no ``__init__.py`` -in it, which is an override rather than an addition. Nothing here writes into -``sillo/`` at all. - -Static analysis does not run import hooks, so type checkers are served -separately, by the partial stubs in ``sillo-stubs/`` (PEP 561). -""" - -from __future__ import annotations - -import sys -from importlib.abc import Loader, MetaPathFinder -from importlib.machinery import ModuleSpec -from importlib.util import find_spec - -ALIAS = "sillo.wire" -REAL = "sillo_wire" - - -def _resolve(fullname: str) -> str | None: - """The real module *fullname* stands for, or ``None`` if it is not ours.""" - if fullname == ALIAS: - return REAL - if fullname.startswith(ALIAS + "."): - return REAL + fullname[len(ALIAS) :] - return None - - -class _AliasLoader(Loader): - """Hands back the already-imported target, so both names are one object. - - Loading the source a second time under the other name would give two - ``Hub`` classes and two sets of rooms — a broadcast would reach half of - them, and ``isinstance`` would disagree with itself. - """ - - def __init__(self, target: str) -> None: - self.target = target - - def create_module(self, spec: ModuleSpec): - import importlib - - return importlib.import_module(self.target) - - def exec_module(self, module) -> None: - """Already executed under its own name; nothing to run again.""" - - -class _AliasFinder(MetaPathFinder): - """Answers for ``sillo.wire`` and anything beneath it.""" - - def find_spec(self, fullname: str, path=None, target=None): - real = _resolve(fullname) - if real is None: - return None - try: - if find_spec(real) is None: - return None - except (ImportError, ValueError): - # The package is half-installed, or its parent is missing. Decline - # rather than raise: another finder may do better, and the import - # error a caller gets should be the ordinary one. - return None - - import importlib - - module = importlib.import_module(real) - spec = ModuleSpec(fullname, _AliasLoader(real)) - spec.submodule_search_locations = getattr(module, "__path__", None) - return spec - - -def install() -> bool: - """Register the finder. Returns whether it was newly added. - - Idempotent, because a ``.pth`` is not the only thing that may import this - module — a test does too, and a second finder would be dead weight on every - import in the process. - """ - if any(isinstance(finder, _AliasFinder) for finder in sys.meta_path): - return False - sys.meta_path.insert(0, _AliasFinder()) - return True - - -install() diff --git a/pyproject.toml b/pyproject.toml index f98bdb5..0571fbf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,21 +39,7 @@ Source = "https://github.com/sillohq/wire" dev = ["pytest>=8.0", "pytest-asyncio>=0.24", "pytest-cov>=5.0", "ruff>=0.6", "mypy>=1.11"] [tool.hatch.build.targets.wheel] -# `sillo-stubs` serves type checkers, which never run the import hook below. -packages = ["sillo_wire", "sillo-stubs"] - -# The bootstrap and its .pth land at the site-packages root. The .pth is what -# runs at interpreter startup and registers the `sillo.wire` alias; see -# _sillo_wire_bootstrap.py for why the package is not shipped into `sillo/`. -[tool.hatch.build.targets.wheel.force-include] -"_sillo_wire_bootstrap.py" = "_sillo_wire_bootstrap.py" -"sillo_wire.pth" = "sillo_wire.pth" - -# The same two files for `pip install -e .`, so the `sillo.wire` alias works in -# a development checkout and not only from a built wheel. -[tool.hatch.build.targets.editable.force-include] -"_sillo_wire_bootstrap.py" = "_sillo_wire_bootstrap.py" -"sillo_wire.pth" = "sillo_wire.pth" +packages = ["sillo_wire"] [tool.pytest.ini_options] asyncio_mode = "auto" @@ -61,11 +47,6 @@ asyncio_default_fixture_loop_scope = "function" testpaths = ["tests"] [tool.coverage.run] -# `_sillo_wire_bootstrap` is deliberately not measured. Its `.pth` imports it -# at interpreter startup, before coverage exists, so its `def` and `class` -# statements never run under the tracer and it reads as 65% however thoroughly -# it is tested. tests/test_alias.py covers it; the number would be an artefact -# of import timing rather than of testing. source = ["sillo_wire"] branch = true diff --git a/sillo-stubs/py.typed b/sillo-stubs/py.typed deleted file mode 100644 index b648ac9..0000000 --- a/sillo-stubs/py.typed +++ /dev/null @@ -1 +0,0 @@ -partial diff --git a/sillo-stubs/wire.pyi b/sillo-stubs/wire.pyi deleted file mode 100644 index 20d9edd..0000000 --- a/sillo-stubs/wire.pyi +++ /dev/null @@ -1,48 +0,0 @@ -"""Type stubs for ``sillo.wire``. - -The runtime alias is installed by ``_sillo_wire_bootstrap`` via a ``.pth``, and -a type checker never runs import hooks — so without this file -``from sillo.wire import Hub`` type-checks as a missing module even though it -imports fine. - -``py.typed`` next to this file contains the word ``partial`` (PEP 561), which -is what keeps these stubs additive: a checker uses them for ``sillo.wire`` and -falls back to the framework's own inline types for the rest of ``sillo``. -Without it, this directory would claim to describe all of ``sillo`` and hide -the types the framework ships. - -Nothing is declared here. Everything is re-exported from ``sillo_wire``, whose -inline annotations are the single source of truth. -""" - -from sillo_wire import Backlog as Backlog -from sillo_wire import DeliveryReport as DeliveryReport -from sillo_wire import Encoding as Encoding -from sillo_wire import Envelope as Envelope -from sillo_wire import Hub as Hub -from sillo_wire import MemoryBacklog as MemoryBacklog -from sillo_wire import NullBacklog as NullBacklog -from sillo_wire import Overflow as Overflow -from sillo_wire import Peer as Peer -from sillo_wire import PeerGone as PeerGone -from sillo_wire import RoomConsumer as RoomConsumer -from sillo_wire import RoomNotFound as RoomNotFound -from sillo_wire import WireError as WireError - -__version__: str - -__all__ = [ - "Backlog", - "DeliveryReport", - "Encoding", - "Envelope", - "Hub", - "MemoryBacklog", - "NullBacklog", - "Overflow", - "Peer", - "PeerGone", - "RoomConsumer", - "RoomNotFound", - "WireError", -] diff --git a/sillo_wire.pth b/sillo_wire.pth deleted file mode 100644 index 7dcddfc..0000000 --- a/sillo_wire.pth +++ /dev/null @@ -1 +0,0 @@ -import _sillo_wire_bootstrap diff --git a/sillo_wire/__pycache__/__init__.cpython-313.pyc b/sillo_wire/__pycache__/__init__.cpython-313.pyc deleted file mode 100644 index 3d473439e561c92a1126e9752aa12312230edbe1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1943 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 diff --git a/sillo_wire/__pycache__/consumer.cpython-313.pyc b/sillo_wire/__pycache__/consumer.cpython-313.pyc deleted file mode 100644 index 21e0905c7eec699af1f74d0cd0e5c42b40daf528..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8931 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 diff --git a/sillo_wire/__pycache__/envelope.cpython-313.pyc b/sillo_wire/__pycache__/envelope.cpython-313.pyc deleted file mode 100644 index d4bd74b26b017ce764a7671068ee688528e6407e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4212 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 Hl*0Z83+)C} diff --git a/sillo_wire/__pycache__/errors.cpython-313.pyc b/sillo_wire/__pycache__/errors.cpython-313.pyc deleted file mode 100644 index c50f3333302c410762c7bb08fdfca2a4c13780c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1588 zcmbVM&u<(x6!z@zou(>OAeEl1a6l_1yN3#?T_L0(3KF0QvO-)UCf*tEu8G%Po;^u6 z2h>x=u@dwz;MD(vOaFxxr*g^-l zS!e9e@Nqqwm6Ke459c`>F=C@+RQoddqDD2&25eOSCZYO0w#`P3`)t%aYxSz>6FC?iG$tPsze3)zD-01c;L%Z1@$vCt|PAvt~%o}UWi9rrf% zB=Nb8TpXVEYA(4JOG$hpl$OOd7miyac`jTq29M8YRTg0iZmgdvGv!h{*-tM*@x5eq zOBe*=hOSp%b-tF8K1Z6>)`7M2ed|B7r72e3%~A949UNv zIL{>>wt6`D)=6>$QOuPYL=OAoQt6`dcLSf58>t<{pU%((d@KXQr=+}>XdsIo zJg7R<8odl{8u~|`g(g#;JFM1nH>BrIn!@$@9v_nx1)9in6UezDB8}v{dh%=Ru|kT1 zyK?Muz=<_D5*p4@p^ON0o=AgpqMgy4X4jT}2k`>SQj!Zx9+L4hvgLHP(ZWwR(&qRFy}A#I6)lHCdw`flJ4pb2%Q)UH5TYVd~umPQ_ee3@s&E3n|iN>2I)s=0pZPM zQlQ~-&jA5d(nJi*1&ILX@iN_z+Fc$y2`K&8*^=3qXOqI5~P z1^x%dP%Z@%O5VWOxP~!A_In+=gLe?+r1Z!LRn@};q$jk7^tzk7vXV+9C#DYSyJ2nB z`KXfh$8;ZoaId(BFn`sPB>B76NE)xVSn~d>^ud$%x|JluXWcc!Yu#xjou8L$hS&OT OGwD2g^qS!nGye0QA00@Ev2tLJ!v?3{qph!@%Wy>PgKRO-KR7?g@QsvR4r<^W3-4_nfXg6<`76y*)AuzN8Gb+8rqTz7%>X(BnYa&cZW0x6h#<%W4{=Rw-K6 zh<2WEh%#0??hO01&*63`m6|lW)Jc7ERA=TyBCTphTpiPl?o>7t@%j4I-dIvI&EAVA zv!fT))RbYWjNak(G84L**72g6FjU-3M%3prYJv*Y6pJSejZLem(Mx(P)1?}zj89W1 zQ}Jw4S5spuZx>(AOr+wbn!2JhRa3{4sZlMd#uN00nNl-TDRm+{iWZs9ZxTjaPwPmG zjBhH%l5w*auVr*IqmJtt)KkZpZceBvgSy7lm}cteote?KIDM7rVyp>cT+O7^v1AsN z%?UIg_n8?jqj&LolL~EOd>^N(=w{5vn#rZA9NLc%;<(@;EOTTy{zkLnt%jT zGfW#BOT>IyT4Nb?DuLl;Gw7C~P0}}GDdP(Ijz%%NC&WC{jNy4K%NUCI_l=a%J(^6# zF5{hudQ49y@KLMJ8eaibH$I)!v*-}|M_ros(Nd`BD4NoFD@@O18CFcouj1#-L^7Eg znMyD{qN69tR9cT%%})>M=@iRc>{AmNpFvHkQ(6Ke#0NAiM(naK7KP?EmP}Az`NT|U zn3YT_oz~-+Uk&SyacTw@T%AZI<7zhTBT2!!pl@R|PkaOs-v=a*VL4aMEAy!~2g8n> z=cpFDoJ@`9DhBk)6q~kgQBt|FuQ~5=YY=F~Pr^Ky%A81LjX3UIFJeA&P6%#H$h}CN z;zz!JPA2tzNn$co*b$YXvJR-L6RNs3x-F_SN|#)hWaf^lQ8(q82RRSVd86B-UY;w9 zwvuw8)ED(d%OmnusrbKWThvdbmmI8o!}IoN1%J!G;pvWOC4X8G4MbhhV8l^eD)P~t z(JHIO%IL0WM>NFmWEMcL!_jK%E*R~M*6_BfkgMgnP-$z?-O)N;QXTDz*7ICVv^(0s zbG6Y(bPLbbq2-=vBjqt4_0c`iCjPu3x;NU)b6cX1MO%2T5%bqH?g~H7$bf#JBANM* zekcuJ`pry-3H6S##3+OZ3dyYq><$|@dwnXdQ6UPw>L(|3*nqfD1yr`hSJ(!Kx|T5T zcr3*rY3ZakO?oq?Ws{KLOp3Qf?Lk`;MmLNA@XKZ%0ndO`)84PQ`#d9 zOG#cp$*>&5G@iFaR+65uN?wqMWz@^J(?f6NYwd!lC;CeQ5A;;>vj+;Bujvx#I}|n9+h5pt0Jf&qR zxz25W!kUNYq^NTO`7839&OybGIT&|&r9oxCb4(5^Gk+YlRY~n6Ea)VK>Li)#jHp8v z7EDb5abemM04LaOL!U}ct6Ds+#{o#u3_uI&V&VEOQ=MXo4AmZ^7l2b_)U_m8`DvA9 z4FjUjfm(ybPbPI{0S7?>K5nBe@)6%m6Lp1vP**3jzyfM(R9E%MbY}X*;F+*D=R2z< zv-)wyQjE)M&IiL07h0TiBA;`jt2qTo%`#9e!i_A@j!W~~E>?}H%eiRmITw8_=dqZL zN$LRYS6MBu!d25t@=3YJ@yxu8Ra;n-3`^v@1EPe8tPaM zb=<7^OY<+XE1@T5J!|EG-#W5Z-Eh|%c)k2;`L$!Wy<672{?~n1eZTErs|>wvTs0QX z+_-T4!p&nVmHSt{`Mrxe`HD1ag#-r1I=> zs-qzxQC;^-)%7Hs^F4Dti|s3+T_lyEt49{%D_i$kq8YdvSa99;wtO#8w-&6KFP|%4 zIJOkrUJ%evUH{b0Gb@!(t$LqY@;+q==mA?m!~KuTw|2|@dtJA>+{kYLFE={!1+vNi zip(!7ptd!vmd-lPI!$MfWIC#)ldynUSnPD0u()yL2mB;zFFFQ*Vdb!ThRbk3s9mVt zEJ=m~!nTPpp9|U68+Nlq*ao(WmWEyJ_^K+IPQz(9#w3VfkXuv7Oy3YV!WDgk=y9Gc z$ZOu9lHhP$w@qrohzy3E!hR{Ro|7gEV|uXqWS-b|N?IvtLy~hP^|6de-6!*8*{3qT z)+4Z}xr+SK)9ozwOcII|M!SoL?j=kI_Ws@-xoP(54z@V-Y14$0s5cRVyz zH2&Jw{&sn>yuaD~jvL9}HRJYHmE7Ozz7?uMeuE4+@dB5DE6C(!0EVXYD&XUz>jAu^ zc1kW=LLRL@E@P!y@W01et+9_>E$$7)k}2{9fp5Z>fq^)Kwd39lrd9=nrC@RMuLmBZ+jabnhkjAwb}#ny!?b~q5gpUj=bOXuKR%e z75RXC1`dx~kl;Lo&>E0n@zoi`omia$Pdc|fDj#qduHn3?*V?$L|G13FWkU>B$g$C! zLnp~Plw2Rpn+#=G$%76N-#mx1C87Q2FsE~8Y+dKJV|>_Sx1;UAh?F5>x+T}J$L_s~ z8g~7-0J}~4J}d+5-0fr|KFy`hnLUn4wLDVT32i!=(&C(T2}8jha)d@K#42`c$YPLB zlZZ`l>p^VE@@hCJ&Kha;$z+Z!~!HDL88e$RM{y+^gcg7hVQZ5~YhFw8JT)?SWa*AKO(c zcENl;i+2iVj(=eSkrf@TS{Dz{&>X>eg41vHgWJZzu$K|5V8oJhZvIAGxG=luF)dHd zH;Y%=C2|lJ$}$G)if_YyIj40*89EkDl)d+bKR?})ON;@O)&hB#;T)3$ zWMoPEf06Qnp|NSAc%2CfIVLjGog6lxj`&B=-wIy{I?Op_7XGpd2aECG8A{<f{YG-3qN*@kroPW`Z7qI|9QZ>Y#`_ptjGB zef07UP3oqpjRWWq7>UObbTdU{1>b@xMdMxR_e14Zx~Q;a?++I2rDPR zkSR<8*KZV|LSvi)y--$}kv@gpg5$Wakke-7y!I4v?PO1(3fEy<5&P)@S46D3;6XVL z965^HGIFkS^2jC%=pl17l1b&t2}iA)-#~Tqb4WOdHb~9tjlS!Bt4-a@P2IPfdLAfB zweJT_J=Z*U8n#_?fz!VJ%GFmEy~}})pL(Q!yQkpy+f6;|ic}W7C!sz$e)V}} zTz7*V&A%}B!s4@kdG4#@%iSkdYM#AXHtXhE_war_-gsz2o{23Tkrx~L54iuyhvb%b zKkhf^9$70V(c~_O9Ci%kL$cvKB&Uz}NQS!r#|44}ePYlj&#-5+;DrrU8VJF#5_NRL zNCGOpbeE+DvKC+Y(g`}bsr+rIx}=)lvF#JN~O7B5Dnm4Qxns? zL_|}lQ>q3>6T{`L^B|7cs=zKv68foX;|P4>m&$1yfX7Z7v_w*mhZVRZXfx;L>^9E!Bq+~lKq|zoWEl1NlqH@C^NO1wb|Cs9jE)qDk4N^yV$=fkohUD6_Yt36{l^@kK zEY$w~X?QWvQ#wS;+JApQO(GIx+^?nhN8<6>PXF6d&T+$0NYzK7Ta1Sf67Am(E@<&?$TerD}Hj#!#om{`& z10YF+!xp_g%n{NxvlfNvwH@FoVuz5c$%G|WHm%Kj!V^%2NzIs6rzlbnOHDh7XCRi? z*)kD6frBM>jmU^2M1sh^s*O>2kw_(vsF0dL3rLnPUNrSjLodaSk&n|>TmPvarZ&}O zDW0V_sGlVQD0T!Uq3oHIk%$R(B}fKR0-|9K2Kkz@XR$u;)QMxV7bzK_2NrMSGR(;% zFZu+Ea&9qr3YZ%iF8-SU7EwxUKjeIbS0ew#$ak=2w)|d|6heHFL({hYR{1TtUv>RS4|2C^{Ydkoi~bb+ zLmK2SExIA{A4`2d?jgB^hcpwR^`A_^K4Fb%$IxCv>?0R%*;p9F z-ppW;3S6Eln1EBVD}tkYETSSPf_1Y>)!LBIoZkhOUzoeFS{q)j4c}~EstqsI9$XC^ zT=E`V4jg=FBETfQ*0NK+A-B60m7PB)!f_V0e%U!HfO@HVBYIC!3=zF|%!E&*hzeO+ z1q`vaV|a*C_>%%$5M8on2SEs3V{cnQ2$IzeL5K=oJYr8y70Kgf~zW_T|d$2qsqUTJ`Q)@$RxFXImJ8w9EA;?(n0NgT;{Vli|mf%*XaA3KIjnBLnN@ znZ%Gj4)}pV=p-0|lgK)|#KuA#s;<*MH20|~XiwYPG$F7tmXFEJhW#RkaF_`k61a@2 zR69=4Eg5 z!nxbtooj*m*#O6mM$Q4oIFdgRS-Rr0Mn`|bm( z2ak>P2>TnAS1VV&Ez91Pg^AnVT}5Lh7__f^-04+3v2;cTP(3Ex#8#;Qdxmn}erwac zx>k@}J5sX&!f-RJJgQX#@bd}GxOSBZXK&`#NAHIX*n_ext@XHR~+wgs{L+VJd&JAt~@Kqec6I^jVGwGH({` zm_lty5K1n4*12+a9Ovv(xQB3F>7)sS3zl7Ywa{8HVYsG{vD0XXJBM$}e1c5sgk`K1 zYQfC$Ee)Q1h%&PZ$*al|W?48+mjhNY}V6`2jL-O6P)`g>v|IA7ZG50|W)5 zs}q5wofEuYdJW5@zR=bq&{hO^HVf*IWg7@V12-)lcF?yu(QHQ@$EHr_rf=FyqDlk( z7AYl1P|lu2gU5ghAmRKpnbCdm;vhut;ziX8JS4#Ugo=Zx+AC;L(7ft1a1ovL0!~u2 zg0zy@r-MTV`9%=Nz>y`3#fn`5m`)?wQ@75f3_*esf8hs>de!H~EMZYM?kz^t zVI!HijOdP<4;mtxW73%}9$h7CV08oC6=+6%z_BUdl{kULcPYt@BNGmKi}72*Be9DjF>rn0{a`2fZS78r-#)4gd8_c}_%=9*Jc^)y zQCwwH4I*CY_xjoQ*? z$ED6aW`Dob#Z;wpd;2gW9AE&s*ghtAvE&+d+VcQ8`;?Hgf#0-1MdohmPT{6ziF+Nt zf>Xx`Q6MfD=L|3rheZ{HfFC?5IAMk3R5;!XmBKkE92KBABVd8Y3Mqa^aSUiI>^1Ce z0{hBVs1N!Kvy9VIJl0PUzQR%CU8WCiO2UNtZPZwn7@t7zG;Bjav&A83-ZtSC#pdx5 z9E(cozG4-o;Gr=6G2k9B#F(~@D{>7U40}o$3lS@jU2}v+&krteXTidToW~k!f#;UM zgObL;bfF(S+d}%057k#n+qTbo?jW95bED~c)BA(~cN&^;PU`&i^EXDWkK8=-?qe%0 zPrl!wcNEI@&4U>aIch4Ou?g;r)ig zL-~qkq#NuR$@dq?p8XFpwmsX7ENvFkv*jH7W%5}^e!mPdnyTWvWw4g1R_ue}D`fo! z;FQrXcERMIN6I$SQTscpbUv?-o1MGDUjWEJF6`9VZ#*eIs#D_*YnRMn$owcwxfR6u z$7lG_2_gU70jCWs3LX~$l|{4!3}MT8EWzk})u@gWxwOM21dkILa-V4b7YF={xRG_5 zik}rXC$bsD)(x~mp~5@^pU94G042@FNYX-~s-SDW2`xF+Ei^ge+pw%otR{!4r;C;q zX03*nW(cBMdf5|Z!Bw4DsPSUBVtertyq?eEM=&NL2)3v93i1|G(JXF)R8NtHpkaYj zTMvItdDnGg=K9Qy&n|rSYl?+a&07nnc%MdH215)*yH#q-s@Z&cm# z%vCM7J~^u_2T#&bM;v=x^=?`AZn^Dkdf=79Xf;b>WdR_Is#+uy-r9hJ6OHOZzh)dls|$DIf^+`3_17dcNA!uG z;ctT2hUWNGx!5QQbHNv_c+&G$uu^>S4^f8Myo{fjI^Jw)y$ePC*tkq$DIB7q-=~H| zN5r!5h4hIgX%ZhPoC)&6!ouaN`~li3nh@&?e?(uf@_b}go`fLWOh@_Yv^Ip>H!>pr za*G6gWaQJ}6jn)KWQ1G#B+j`SDHMA~M&hX$GG!wpW-^sAp&?}4SUV+QN+Oidim*eJ z9HWHX1~yE|uTTQICb1YLW0deivT4eZWn)v6yh_QhQ9|~YT|<(q#W(R+T#49-mdW6c zO0w8J!1qn6K)!$ozTtF<#Q#1;MaJ9*UHx_Gp2Mwp*Mm~k)AFm&eduy2`yK>?O3j1z zN+ha+q|&W4t#8HN&Asw%O5NScs&xgwa7shX>mJJaq^jC=Dg*KGHTNCaUo=$RwCza59%Ds_6K;e=Ybb<@L{`GIW9k_sZ{D8sO8Gj z@=qIO<%G;9G+f8t!X%P_jAdbSmeA1 zg^V9&e}yWL3d#H=l69vn%l909+4YlVNpAb0RQVnHSMi_H6aOwf^eTxTNRw7VkbXR-c%|ZK~;XHB!n~CHc7F&soLV72yDD@T&aBL z_H>UVB)k`K`}FPmzUO@BapzGu9Arp;{(tYBeS0lqzoHjAc`QPD^ahCcSe$Vdm*TD? z(qR{O$wa%4xVc+AJ=`OnUhWl7ANPr;pZoFj90?q*;1!31JSf_Ek5nEG@sOZ>N5Y4z zcok^>R*6@~1H7i0b+ULx3rcpmtU2)7QFkP$Y{KhcPL-2dE~h39H6(C zF`Ioj)RIU2II1!=;!1-){s|`rLbMrR+P3?!GKr`Emr1u zij`S)$CaEis_Hm}6R07NDOf1lUKmqzn3$M>y<8myc`chsPEaSbW7r_7l~U76A#2E6 zwqS?VU|%w;=z8DqAL`g9EpZ{k)u`1jI*1vhaXQ1%-~(cwh-6lZRVhYB%jp9Vml-&a zOKP;sM4eZ&T3$u1z$;kTH?W69+r5|`Dt+(*I!aP+cJf(-^vETVY@B9Xf_%8*66C=J zS#WdjY2Q$lUA_E_drk*VR}3|jOKd9ciF>2ocDB;LczxX0!p?fmx_IU3P`n}Tr?*|D z<~)qDRq?>0vZ{C^$a6=qfw zmEx=8we*hh8{;kUI`ORuD{UV2L|S zUVw@m0e2a4RwZ@09qPb8u3XeJlj^}py&3#{C0kGraISIQgtjXlp6MwRz!qN0cXArl z^bkLr72_I|td`3pIn9%=qECKBpU5RMn(6%}nJk>v%xU8}?N7vSdUQ3Le)@>IA5{qi zk<8s&k3;tLtx9SO>)WS7`{0}C~LU-`4W_v+fF*YwTSJv$wG_I_jAo0W6T@|)qMSzGtr!0P|3Yka@w z?VfiwU-rD|zt_@!J^I7wY)cQ`s^{9|>x0(@FMFuk*4-a}`!B!!&nIWL4&Ynm+>YIN zgyx#sE_=QgdW7Lw(qU@65G{nF1;l$e>>)gtrgbm^CU=||1F#8;0w5v4g6ysl6%b^J z{bqRth7k-&R3Ze4MkKNMA4$9uUFtNJ%)5}Az7kMWiY>BU5>$sy3>)NQH4Rr^zw-KY zpp|bx(V{t%Ac{Hf2C>qdha3?_rBbp5O{{0#%c`!EU1fo1N|=FUyiLGzwY5#eB^b%{ zzDn%NyV23YW%!{AgdG$T>nOdYs1~udy^zdxsv=aVb)pt4xe4?wK6 zqU2si=i|T-s+JN>i&ClwU_*V0G~@t~UBciOpa%zJ%H{UX*x&~-cRr3v^e^I?{NoqZ z(M--VUCDNE6b~%ZLn(DUuR%e>m(a-tfX+h&pukx;3YMY}3RN%;w3kknu96i-zYbl7 zbHL^YVIFY9`DkemUAO@mBN`;EcVwE&n4(!}>5~MR*U&q{u72X2yrNq(C{9Vx8MM`R zBEuPlSg7Xe-Ya`&Dm!j^J`Q~xy4TuSY>nP&jovgqe(j^z{_5Mat@|$T|K8DgH;e3= zuIza70BNhgL%QWZz~o&_Uhxp^qf$_B6wctnjb8q6M^3JE>_d*W!RV!AAEeAqImbUl zrsa~<$xaE!m5S{{cVXVk&*2c9)iE&WdFeg(fH#DCa7yvdb}#82E5Vwq^tEFmJg4z-2o?NPj6+}s!&W6# z3)wjqsSt21BnSwszzEF39@BDU+-EZ*WU?~ANHjrWL5>d>pw>Jpe$xr_s)gt=9*ebE8VZGw&bpB4ib3u%F$w|V6*?j+^lZr{nn1l``fFwW(OwHB;9$TerR#T0b3Hj}}#%r^A~bR}9L z3awHKz9@+EB;g^!rAfihf|^0}NVtJk5Gfiwcr4!gbQfa>=mqY7}KE34x%m;Hy4>%+WLuf%@iokEdcM zvHg)NM~b1&nNa7A-@6;yJXhcJ+k4fGmbvhVNvKobRIHE8)JJZ1&(`E+hB{WJ{0o0KApB?380o3=vl@Nk^H*eNYNZzR>G}wvMVP zS3_H<9iiZyo=2s!(iw78K)DT+lA{2YbCqnqE-5O<5h?dDw5m+ol&vhFq}=qcthBJM z<@zAgZAmK2fhAKbs*|3Qe0U~z#qxOs3i}kp;C;h3_aI_I*3r0Sds-G!Dw7georZ~X z@Yp~kWI3?pjF^@ChX!9s3>-LdAb!mB_8%RLAFw!=Y+t^IGMVZH1x zcPe}41FTIZM8>zr!7?Z}71#F8tnK|QyqMMRhSm}C?z$fcU9G%QSq!w#1lm8icsI~J zS6%;wA79YoOPyF`s=M{YjqY0;gD>v#Kf&MMA^x66t>4MyS`D6sL|BOomgRW93Ow8E zwmD6e1oF~REJN`rpu+EmjJwBiu)TdUI6by3c<7$mn2T3@Nl1e zC@uOIpbhM|+`r8-rnO)J2#}*`G&t!M^(KR)CakIhc)0*qLxx(IfJ;Dp!U=<{D1omO zb$SLR5x0fb{?ay?ZuMePCoYg}$VOlwmoh78dLlyi*Z8QDe;}-uI^i5#g{$83!SNeA z|6@ z*F|DMOA})81Hk^%#9{$_I{=Ngr3Cn1C;X0OpnW+9a*;SJ@f8MBU#mbIU&J1tv8T?! ziD;GUKXnFOn89)@AtkybJ*}TpPF!p`#jz+V=d4J0Am0Tb9KioBA-t~1ov$GFEo9D$ zaf24TPeCP!4`?tu@%=_!wu7Y6fk>qlETi)>J%{6i1C}{n;e_l>&#?n1kC|2hne}E+ zd=ue%PBA|Y9I`@Yh;4KdeH7Uu#c%`#rWKj7B$Vj`+h#)B=EAjC&s;h4qp|Db*T(NGu+5j$yKmd<-7|IFH=AbbwiiR&r$gKC zA*wa-!-3g0M7%x=tcLL(-u{cwwqJy{|BrCo-`LweEtmxOcP)hf+zN_ikwNcT zS*LhH@9Z(^;6eQT~Ay~%&v(Ro1&Lrf>Chw^{cPX)vTI#`NECl4xW4K+>P#H=Z=}q z9kY$S#hTvfnqII-b^Xtqq7MVCcIR*J)wexjo^T^#iSoMdd)^KG9L+0G@RhzFL%z`w zdq(96_}){K5dvA55JjYz3I&@IFiN z6LLKdMm*wtbL+c5JoRqF`|WSH->qHyN%FF9uBNfLx@QJ|H9a>|#pv@h_^W zDmdz2L9E144!VH<;?iIhIbO?!imOrL01-Z_1v^Tzh+RqLl4 zHr!AUH4c9iE_OdV)BWu9#=Y}Q+SfeS+;%;DEj-iIeN(!*{$|rB5{$htoOgFcP@Hk3 zN+|CygT_Rjf+e_;FKc(}GII>m5jUEq8$Y%wdxe5;zYO758jz^hWxA9#eO$y^Ob@+b z{pZlu^o(d)w)Dcuq_ycR(HBkj(d3y@0a+A6Myg> zl=V^eB4s3;oB}ai=qOS|{0wDjWacUi4`#_EamkDyHH;L%H;KeYOq`Xtv=YD25ZPKZ z&`BEqggtasc?0uuxYl>+(EJA0zTx_rYiH&Wc6$2D~SrR`o{S%QC0M{ny4Dq)HP2n=j&Lo>NoX7?Pb2IM=qDQ z?U5VQJW=S+D(iB}x*iAo-sc|I)q6KQj)uKEAGgsPGmb5{E~tNfgWk^h?Q_;>l`dnSg7M!W zJDlkJh~zt#F=Sbkbq=FP9qjO+$A>Y-Su%+*`Hfv;S$B_R$@BP0ld}o_DQ?BBioJO} z>dD&?W6vt-xwpKqd{u$rk}m;}=h7JK`AdK$ub1OAy~mx+Ujn@i0m|`kJp*Ot96=Vm zK&g!5%1|y}0Y#z#{k!81Ou=V$Spx2JNxC&Tq)nDmY3<0#SExMjnWhSE%pV36r8Eb& z-j?$?@&mq2BkwggJ;W1?QxT94Dup_ij*Gi+KVy|Ts~Zw4m> zq9m00&#sd~jSeR|zd`aHd(JSTNp`Y`CfC6vi?Mr^riML|UF>H)9L)Qp0VeeP^Cc2* z-Zz8VPN%RyxRkGm$^x8|b0DsSIE;m(wTsXXn%dI}kggi{+}eWIMiD+IHIa6!FcNF@ zn$qNzRXMeSI^&75$}7G^y9D&>bRoT`Ail7aEc7RI!=o%qmD3bLV_cv=PB+7Xf`rut zdA5duRWQ1_whlh9Z!QW^Ds)~e1IE&IUx>oy zqzoTCIe9Ejjvh?2r(5HA`YagT`$s3)bh9f2ErnOPAmFHGwFf5zh9BabKDPN|@~0z0 z6Im>jnK)WQJD$}_7p;D;gxkVb@|xSJ&YVqZE*cW`!r{mM@d&;Cc> zxqLeP8vJ;f-jDitF<>C(Wlf7s%rOp*1WdgMENPvIWilnRtrvhdvA7!^4(_xqyNi2d whShQ-5@&G~MPE9-DEWSgMb|&E;m6_b+x^r2x87bfc7@#ul)}aEaYhDL!}CdzMw=7guEcWb7#DEehDE2 zBl+x^JNKS@?mgc<=eyqM?zR}7U;Xp_bNf0O`=;>UBg;aJuly)RkO)fG>Q`HE%D z3hqbVvPk-R%H}IeD_qv>DAZ=`1>tgxh8badK_p~qdqFJn^Iq7JHU_QG9NapnLU&0- z2W=@_;hh(?QJV)fo7+h{^&~T4K<3CnZnpw2N+zm>a4`rY8+Jv-nsR-v)YSa*d`URk z?0hmFGc9{c(eS2+d8TYwP(1Qf-Gc>jUdUB#y=~%qTT8Aqg^-n3Am(kC`##nS2jOlg zYw0pzZG|hl0sjMR&`g-Px`>sN1Kx5ol#!^}3#)oDP3|u720lef`1MgNmHcr-6_IV5 zH=Dla!n-iAu()K^ku6(+P1|0vzM$BajkuEs_^vyu8N8e1WNd9MKhD)8_X0P;LL#%t zkFn8&a+!UXNdr*JI0m4V0j#nz=a>YjO2+9WFlo@_ogGerblCx|sI`LAmw-wZos!e3 z+bn0Nv&-qyqYT;U^gG?Uy$g1`8$IO#nZvYS3_$Sj0D{xDim;uBy+EUo+pmg+By`x1 zAP_F?0yYg1eAtdF5k%PVY1vRymW`(-e7-tmJBUtz=m3$*7orLL*n+!@_KNf(QL$&j z;P3?@@zq;y`5fQ}u3X=PIX#)SMfMOq<%cU|ND2;ct}J>mlcLh)K=}${yVyQIgbf1h zA`S##kAZDlQZ!{)Yq=QWVFV|Oev0#o?p5cWKV{Ey4`#_@!e!zzK$3Wx9D)1yW|7cG zXN0ft)rwU%aiI|IE=!X&q{Q)P&##f+R zW@BuQ`T8S?*LL`uec5ZapC%fH1chEHx)^J)a%SfFvMIYTWNR8(#A`WAp7`~xW<}$$ z-SM%pIfaxo#%s%7Fs2V-WufKywM0KmIVe$eIvPvUR;9Tb7phecnW0*ZcW#}4%4Xv* zCQw5V7uk*dPyNVzuXv+>=soMku0d!6kN?R0N#_Q>GPk`RypUJ#&_3LRGQw=^#T=W3 ze*g1S+Q6ZlZCjX4Q~LJa!;i2qneOR}Nc@YK_q=gAJCp0eoR~4c-9n@?`-$I@ANj;9%UnL1vYd1Xtr)3ei!=dNTu6SaRRDxhmKs_!PRQu zdSRT?hfL!r-t}Dj+|CLmqHjtWN{xazkIW{5+SQEg!K`sdldt+AuPNFv+m^k$v6`Io zxU@ZpNgJ(MeH-EniehK)rKw9(pOzl`;P6ime`x;N`lYp2I<($(=vvny`grWpu}@3; zuN^qCRyujD>*S3>*QMgS#r4AA^}^tl-G40XyVb$ENAD_{_U*~(XN}(*(-Y>$UxW5= z5PSn2+aPEk0W_x1);1!D#=Svo7}(ZavaJ^eW_9tfxqZrcdB#q+p)zyTkc3b<2yxG* z6mf2A)pIHg;$7RerQJG)0UNs^Xk$L>DP8j4^{*UV>ls-ujI0$#?rzW_e7k?I@qzJ# zc_qL1|F^(MycKONP(A@a9<@B``rz`Y^$3l!N$i989|vc5J2(rgR2SOOd4%(pj)LwR z_un5J-F)=X?i2ELf}yx4*wSefZ^_fhXv}0U?0$*MC0$ukVw4=k${7^?B&nvS$|MGN z4|;OCYrJEdbGqfLm_iXUG-?KhCGe)JI8(7oJpEaqLb3(fPJ5(Ze&bjrkqcbO|Fhy5hXdsTnIxy z&Px%sWDuLE<^q-GF<7`@MXOEJ$jWrEDxX1vB=DpkQ;(qidw&zxP6iIKP1$biQVd_vKL77bRD=mm) zsb?X6z;0zaa)p~E);DqS#m~y4A5Y$4X6{S3yK=eV+dY}wmv0v~#owVley3pMp428& zu5k0@RU_^o@)@=w7bf*XVWoimkSb&y${R)sVRO!yFdPHSuW-%`GG}#n60-!P`yN!p z%rKiwTj!XQIhH{J&EA(lv+V?$<8NT$+9_|;!E5W1>rja_B($zKtT^2kbrknZ- z2UXN^m#_@10KPB6Euy;>s~uzt3#EkjDae0?av}}#Sqi^?D9~rv&UOelDM*{5AOZe6 zQsiVdM?FY@{)3qSub2MI*%AT-uyrJvcjkdah>Vgj;{@$lwdYV7&vEmVdl_WqPe=&w63c^}-%vWh1xptho2CIso(Uj89J*zcr@E&EGu^ z?UT}DBo7(WN6b$~^Q0d!ASKn08A-Jk*BV+@T%b*=;-4Gkto%9}S=EPlHI^WqpZd^6S z%#ZTp1}c@FfsRWu;nSo&Lqb;}OCE<#m7?B4q&XttYK!!T}TiiY{O0cMPR&U*gDy8q1V&pLNs*?rC0 fcd_G6{-E*XEe3J(b%Q;9