From 709d450f0d026af3d93477ed35e2d88a00f83dcd Mon Sep 17 00:00:00 2001 From: John Haverlack Date: Sun, 28 Jun 2026 09:08:41 -0800 Subject: [PATCH] Initial Commit Migrating NWA from SDL --- .gitignore | 1 + LICENSE.md | 21 + README.md | 72 + changelog.json | 81 + config.json | 22 + docs/CHANGELOG.md | 126 + docs/DESIGN.md | 314 +++ docs/INSTALL.md | 119 + docs/WORKFLOW.md | 61 + docs/lib/img/sdl-about.png | Bin 0 -> 112019 bytes docs/lib/img/sdl-dash.png | Bin 0 -> 143198 bytes docs/projects/new-project/project.json | 21 + docs/projects/nks/project.json | 31 + docs/projects/ssl/project.json | 34 + install.sh | 437 ++++ metadata.json | 41 + nwa/.gitignore | 5 + nwa/LICENSE.md | 9 + nwa/README.md | 72 + nwa/app/index.js | 22 + nwa/app/modules/nwa-lib/index.js | 614 +++++ nwa/app/modules/nwa-lib/package.json | 4 + nwa/app/modules/template/index.js | 5 + nwa/app/modules/template/package.json | 4 + nwa/app/modules/web/index.js | 197 ++ nwa/app/modules/web/package.json | 4 + nwa/app/package-lock.json | 3084 ++++++++++++++++++++++++ nwa/app/package.json | 47 + nwa/conf/modules/template.json | 3 + nwa/conf/modules/web.json | 21 + nwa/conf/nwa-config.json | 23 + nwa/conf/nwa-id.json | 5 + nwa/html/404.html | 13 + nwa/html/banner.html | 9 + nwa/html/conf/nav-sidepanel.json | 115 + nwa/html/conf/nav.json | 49 + nwa/html/css/custom-styles.css | 307 +++ nwa/html/css/nwa.css | 36 + nwa/html/favicon.ico | Bin 0 -> 236606 bytes nwa/html/img/circle-nodes-solid.svg | 1 + nwa/html/img/favicon.ico | Bin 0 -> 236606 bytes nwa/html/img/square-binary-solid.svg | 1 + nwa/html/index.html | 92 + nwa/html/js/about.js | 97 + nwa/html/js/api.js | 6 + nwa/html/js/docs.js | 0 nwa/html/js/index.js | 1 + nwa/html/js/proj.js | 0 nwa/html/js/settings.js | 50 + nwa/html/js/weblib.js | 913 +++++++ nwa/html/md/about.md | 22 + nwa/html/md/api.md | 6 + nwa/html/md/changelog.md | 126 + nwa/html/md/docs.md | 119 + nwa/html/md/index.md | 8 + nwa/html/md/proj.md | 0 nwa/html/md/settings.md | 26 + nwa/scripts/sdl-mgr.service | 29 + nwa/scripts/sdl-uninstall.sh | 0 nwa/scripts/start-sdl-mgr.sh | 65 + nwa/scripts/status-sdl-mgr.sh | 41 + nwa/scripts/stop-sdl-mgr.sh | 65 + tools/changelog-update.sh | 111 + tools/genmd-changelog.sh | 134 + tools/main-merge.sh | 44 + tools/metadata-update.sh | 91 + tools/new-version.sh | 90 + tools/udp-bcast-listen.py | 33 + 68 files changed, 8200 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 changelog.json create mode 100644 config.json create mode 100644 docs/CHANGELOG.md create mode 100644 docs/DESIGN.md create mode 100644 docs/INSTALL.md create mode 100644 docs/WORKFLOW.md create mode 100644 docs/lib/img/sdl-about.png create mode 100644 docs/lib/img/sdl-dash.png create mode 100644 docs/projects/new-project/project.json create mode 100644 docs/projects/nks/project.json create mode 100644 docs/projects/ssl/project.json create mode 100755 install.sh create mode 100644 metadata.json create mode 100644 nwa/.gitignore create mode 100644 nwa/LICENSE.md create mode 100644 nwa/README.md create mode 100644 nwa/app/index.js create mode 100644 nwa/app/modules/nwa-lib/index.js create mode 100644 nwa/app/modules/nwa-lib/package.json create mode 100644 nwa/app/modules/template/index.js create mode 100644 nwa/app/modules/template/package.json create mode 100644 nwa/app/modules/web/index.js create mode 100644 nwa/app/modules/web/package.json create mode 100644 nwa/app/package-lock.json create mode 100644 nwa/app/package.json create mode 100644 nwa/conf/modules/template.json create mode 100644 nwa/conf/modules/web.json create mode 100644 nwa/conf/nwa-config.json create mode 100644 nwa/conf/nwa-id.json create mode 100644 nwa/html/404.html create mode 100644 nwa/html/banner.html create mode 100644 nwa/html/conf/nav-sidepanel.json create mode 100644 nwa/html/conf/nav.json create mode 100644 nwa/html/css/custom-styles.css create mode 100644 nwa/html/css/nwa.css create mode 100644 nwa/html/favicon.ico create mode 100644 nwa/html/img/circle-nodes-solid.svg create mode 100644 nwa/html/img/favicon.ico create mode 100644 nwa/html/img/square-binary-solid.svg create mode 100644 nwa/html/index.html create mode 100644 nwa/html/js/about.js create mode 100644 nwa/html/js/api.js create mode 100644 nwa/html/js/docs.js create mode 100644 nwa/html/js/index.js create mode 100644 nwa/html/js/proj.js create mode 100644 nwa/html/js/settings.js create mode 100644 nwa/html/js/weblib.js create mode 100644 nwa/html/md/about.md create mode 100644 nwa/html/md/api.md create mode 100644 nwa/html/md/changelog.md create mode 100644 nwa/html/md/docs.md create mode 100644 nwa/html/md/index.md create mode 100644 nwa/html/md/proj.md create mode 100644 nwa/html/md/settings.md create mode 100644 nwa/scripts/sdl-mgr.service create mode 100755 nwa/scripts/sdl-uninstall.sh create mode 100755 nwa/scripts/start-sdl-mgr.sh create mode 100755 nwa/scripts/status-sdl-mgr.sh create mode 100755 nwa/scripts/stop-sdl-mgr.sh create mode 100755 tools/changelog-update.sh create mode 100755 tools/genmd-changelog.sh create mode 100755 tools/main-merge.sh create mode 100755 tools/metadata-update.sh create mode 100755 tools/new-version.sh create mode 100644 tools/udp-bcast-listen.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7a05565 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +docs/.obsidian \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..5cc4b24 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 John Haverlack + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..24b8a99 --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +# Node Web App (NWA) + +| Attribute | Value | +| --- | --- | +| **Author** | John Haverlack | +| **Copyright** | 2026 John Haverlack | +| **License** | MIT | +| **Version** | 0.1.0 | +| **Date** | 2026-06-08 | + +## Overview + +The Node Web App (NWA) project provides a Node.JS based Web Application Template for building Websites with serverside API and Web Front End. + +## Design + +![SDL Dashboard](docs/lib/img/sdl-dash.png) +![SDL About](docs/lib/img/sdl-about.png) + +For more information, see the [Design](docs/DESIGN.md) document. + +## Getting Started + +See also: [Installation](docs/INSTALL.md) + +``` +$ git clone https://github.com/jehaverlack/sdl.git +$ cd sdl +$ ./install-sdl.sh +``` + +Point your browser to http://IP-ADDR:8081 + +#### Firewall Rules + +> NOTE: Firewall rules are required to allow SDL Workers to connect to your SDL Manager. + +``` +sudo ufw allow in proto tcp from to any port 8081 +sudo ufw allow in proto tcp from to any port 1883 +sudo ufw allow in proto tcp from to any port 2883 +sudo ufw allow in proto udp from to any port 10101 +``` + +## SDL Worker Installation + +#### Discovery of the SDL Manager + +Listen on UDP port 10101 for beacon messages broadcast by your SDL Manager. ```nc`'` is only used for discovery, once and SDL worker is running these sets. + +``` +nc -u -l -k 10101 | jq +``` + +Or to get the install command from the beacon message: + +``` +nc -u -l -k 10101 | jq -r '.msg.sdl_wkr_install_cmd[]' +``` + +**NOTE**: If you use UFW or a local firewall, you must allow SDL Worker to listen on UDP port 10101. + +``` +sudo ufw allow in proto udp from to any port 10101 +``` + +> **NOTE**: CTRL-C to exit `nc`. You cannot run `nc` on port 10101 in parallel with the sdl-wkr. So run it once to get the install command. Then install sdl-wkr. + +#### Installing a SDL Working Node + +Run the **curl** or **wget** command provided by SDL Manager UDP beacon to install SDL Worker locally. + diff --git a/changelog.json b/changelog.json new file mode 100644 index 0000000..f725fb2 --- /dev/null +++ b/changelog.json @@ -0,0 +1,81 @@ +{ + "releases": [ + { + "version": "0.3.10", + "date": "2026-02-08", + "maturity": "ALPHA", + "summary": "", + "notes": "" + }, + { + "version": "0.3.9", + "date": "2026-02-08", + "maturity": "ALPHA", + "summary": "", + "notes": "" + }, + { + "version": "0.3.8", + "date": "2026-02-01", + "maturity": "ALPHA", + "summary": "", + "notes": "" + }, + { + "version": "0.3.7", + "date": "2026-02-01", + "maturity": "ALPHA", + "summary": "sdl-wkr Telemetry", + "notes": "" + }, + { + "version": "0.3.6", + "date": "2026-02-01", + "maturity": "ALPHA", + "summary": "Light/Dark Mode, Wkr Install, Cluster Stats", + "notes": "" + }, + { + "version": "0.3.5", + "date": "2026-02-01", + "maturity": "ALPHA", + "summary": "", + "notes": "" + }, + { + "version": "0.3.4", + "date": "2026-01-31", + "maturity": "ALPHA", + "summary": "", + "notes": "" + }, + { + "version": "0.3.3", + "date": "2026-01-31", + "maturity": "ALPHA", + "summary": "", + "notes": "" + }, + { + "version": "0.3.2", + "date": "2026-01-31", + "maturity": "ALPHA", + "summary": "", + "notes": "" + }, + { + "version": "0.3.1", + "date": "2026-01-31", + "maturity": "ALPHA", + "summary": "README Cleanup", + "notes": "" + }, + { + "version": "0.3.0", + "date": "2026-01-31", + "maturity": "ALPHA", + "summary": "Initial Codebase", + "notes": "Initial GitHub Release" + } + ] +} diff --git a/config.json b/config.json new file mode 100644 index 0000000..96223d4 --- /dev/null +++ b/config.json @@ -0,0 +1,22 @@ +{ + "DIRS": { + "HOME": "~/.nwa", + "CONF": "HOME/conf", + "DOCS": "HOME/docs", + "LOGS": "HOME/logs", + "DATA": "HOME/data", + "SCRIPTS": "HOME/scripts", + "NODEJS": "HOME/nodejs" + }, + "NODEJS": { + "VERSION": "24.18.0", + "TARGETS": [ + "linux:x64", + "linux:arm64", + "linux:armv7l", + "darwin:x64", + "darwin:arm64", + "win:x64" + ] + } +} \ No newline at end of file diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md new file mode 100644 index 0000000..8b21c6b --- /dev/null +++ b/docs/CHANGELOG.md @@ -0,0 +1,126 @@ +# Software Defined Laboratory (SDL) + +| Attribute | Value | +| --- | --- | +| **Author** | John Haverlack | +| **Copyright** | 2026 John Haverlack | +| **License** | MIT | +| **Version** | 0.3.10 | +| **Date** | 2026-02-08 | + +## v0.3.10 - 2026-02-08 (ALPHA) + +- Dash Staging Jobs +- Dash Full sdl_id +- Updating main Web UI Page +- Start v0.3.10 + +## v0.3.9 - 2026-02-08 (ALPHA) + +- v0.3.9 +- Refactor Cluster Status Dash +- Refactor Cluster Status Dash +- System Columnn Display +- Start v0.3.9 + +## v0.3.8 - 2026-02-01 (ALPHA) + +- v0.3.8 +- Refactoing Worker Dashboard +- Refactoing Worker Dashboard +- Refactored Cluster Status +- Refactor of telemetry format + hw detection +- Switched Panel Icon to Bootstrap Icon +- Updating Worker Telemetry +- CP +- Fixing Wget/Curl Command Installer Port +- Fixing Wget/Curl Command Installer Port +- Fixing Wkr Start / Stop +- Update stop-sdl-wkr.sh +- WIP Install / Start / Stop +- WIP Install / Start /Stop +- Systemd Detectiion in Start/Stop/Status +- WIP: Start / Stop Scripts +- Non Systemd start stop scripts +- WIP: start stop scripts +- Updating Task List +- Adding Screeshot to README +- Start v0.3.8 + +## v0.3.7 - 2026-02-01 (ALPHA) + +**Summary** +sdl-wkr Telemetry + +- v0.3.7 +- Fixing sdl-wkr RAM detection +- Start v0.3.7 + +## v0.3.6 - 2026-02-01 (ALPHA) + +**Summary** +Light/Dark Mode, Wkr Install, Cluster Stats + +- v0.3.6 +- Adding Cluster Telemetry +- WIP: Worker Display +- Tweaks +- Start v0.3.6 +- CP Updates +- Dash: Total Cluster Stats +- Publishing to cluster/workers +- Light/Dark Mode Toggle +- Added light/dark theme CSS + +## v0.3.5 - 2026-02-01 (ALPHA) + +- v0.3.5 +- Fixing sdl-wkr install script +- Start v0.3.5 + +## v0.3.4 - 2026-01-31 (ALPHA) + +- Working sdl-wkr self update +- Working sdl-wkr self update +- Start v0.3.4 + +## v0.3.3 - 2026-01-31 (ALPHA) + +- WIP: sdl-wkr updates +- WIP: sdl-wkr updates +- Start v0.3.3 + +## v0.3.2 - 2026-01-31 (ALPHA) + +- v0.3.2 +- v0.3.2 +- Squetching Logs +- WORKING: join/authz +- WIP: Join Worker +- WIP: Join Process +- sdl-wkr Cluster Join +- Fixing Service Module Render Bug +- Start v0.3.2 + +## v0.3.1 - 2026-01-31 (ALPHA) + +**Summary** +README Cleanup + +- v0.3.1 +- Start v0.3.1 +- Removing duplicate LICENSE file +- README Cleanup + +## v0.3.0 - 2026-01-31 (ALPHA) + +**Summary** +Initial Codebase + +**Notes** +Initial GitHub Release + +- Preparing GitHub Version +- Start v0.3.0 +- Initial commit + diff --git a/docs/DESIGN.md b/docs/DESIGN.md new file mode 100644 index 0000000..150fea7 --- /dev/null +++ b/docs/DESIGN.md @@ -0,0 +1,314 @@ +# Software Defined Laboratory (SDL) + +| Attribute | Value | +| --- | --- | +| **Author** | John Haverlack | +| **Copyright** | 2026 John Haverlack | +| **License** | MIT | +| **Version** | 0.3.10 | +| **Date** | 2026-02-08 | + +## Overview + +The Software Defined Laboratory (SDL) project provides a generalized distributed computing platform for managing parallel computational workflows across a cluster of heterogeneous distributed nodes. SDL is a minimalistic High Performance Compute (HPC) platform focused on extreme minimizaton of technical debt associated configuration, deployability, and maintenance of the cluster. + +# Roadmap + +## Prioritized Tasks + +MVP - Minimum Viable Product Task List + +**Core Infrastructure** βœ… (Complete) +- [x] sdl-mgr MQTT Broker +- [x] sdl-mgr UDP Beacon +- [x] sdl-mgr Web UI MQTT WebSocket Pub/Sub +- [x] sdl-wkr UDP Listener +- [x] SDL_ID Node ID +- [x] Version Updater Script +- [x] Deploy sdl-mgr to $SDL_HOME/sdl-mgr +- [x] Create SDL Worker Dist Build Process +- [x] Create SDL Worker Install to $SDL_HOME +- [x] SDL Install Script +- [x] Create SDL Worker Auto Update Process +- [x] Create Worker Join Process +- [x] Create Worker Telemetry Process + +**Worker Monitoring & Telemetry** πŸ”„ (In Progress) +- [x] Worker Hardware Inventory Detection + - [x] Physical vs Logical CPU cores (hyperthreading) + - [x] Physical vs VM detection (hypervisor flag) + - [x] GPU VRAM capacity aggregation + - [x] Memory capacity (bytes β†’ GB conversion) +- [x] Worker Real-time Load Status (pure Node.js) + - [x] CPU usage percentage (`os.cpus()` times) + - [x] RAM usage (total/free/used via `os.totalmem()/freemem()`) + - [x] GPU usage (exec-based: nvidia-smi, rocm-smi) + - [ ] Disk IO (optional: exec-based) +- [x] Worker Uptime Tracking + - [x] SDL process uptime (`process.uptime()`) + - [x] OS uptime (`os.uptime()`) +- [ ] Worker Display Improvements + - [x] Show OS distro + version in Platform column (e.g., "Debian 12 / x64") + - [x] Show system type (Physical πŸ–₯️ / VM πŸ’ ) + - [ ] Show GPU VRAM in GPU column (e.g., "2 GPUs (48 GB)") + - [x] Show worker uptime in dashboard + - [x] Display last seen as elapsed time in dashboard, not date + - [ ] Track Username for sdl process +- [ ] Web UI + - [ ] Progresive Web App + - [ ] Editable Dashboard + - [x] Panel Icon +- [ ] Capacity Detection + - [ ] GPU VRAM capacity + - [ ] CPU capacity + - [ ] RAM capacity + - [ ] Network Negotiontion speed + - [ ] S3 (MinIO) storage capacity + - [ ] SW: Python, Rust, C++, etc + - [ ] PyTorch, Tensorflow, etc + - [ ] Ollamma, Models, etc + + + +**Resource Management** +- [ ] CPU Reserve Capacity Allocation + - [ ] Config option: `reserve_cpus: N` (keep N cores for system) + - [ ] Track: `total`, `reserved`, `allocated`, `available`, `used` + - [ ] Display reserved capacity in dashboard +- [ ] Memory Reserve Capacity (optional, future) +- [ ] GPU Reserve Capacity (optional, future) + +**Service Management** +- [x] Start/Stop shell scripts (for systems without systemd user services) + - [x] `start-sdl-mgr.sh` / `stop-sdl-mgr.sh` + - [x] `start-sdl-wkr.sh` / `stop-sdl-wkr.sh` +- [ ] Combined systemd service + - [ ] `sdl.service` to manage both sdl-mgr and sdl-wkr together + - [ ] Support for system-level (sudo) and user-level services +- [ ] SDL-MGR auto-installs SDL-WKR + - [ ] Manager node becomes worker by default + - [ ] Config flag to disable: `install_worker_on_manager: false + - [x] Pass HTTP Port to SDL-WKR Curl installer + - [ ] Update Install Summary + - [ ] Add Quite Mode to sdl-wkr install + + + +**Data & Storage** +- [ ] Data Storage Organizational Structure + - [ ] Define directory hierarchy for tasks, results, logs + - [ ] Implement file naming conventions + - [ ] Add cleanup/retention policies +- [ ] MinIO S3 Storage Server Integration + - [ ] Install and configure MinIO + - [ ] S3 bucket structure for SDL data + - [ ] Worker access to S3 storage + - [ ] Web UI S3 browser integration + +**Projects** +- [ ] Project Management Org Structure + - [ ] Define project metadata schema + - [ ] Project lifecycle states (draft, active, archived) + - [ ] Permission/access control model +- [ ] Experiment Organization under Org Structure + - [ ] Experiment templates + - [ ] Parameter tracking + - [ ] Result linking to experiments +- [ ] HPC Experiment Binary Development + - [ ] sdl-wkr Capablity detection (phyton, etc) + - [ ] Binary packaging format + - [ ] Dependency management + - [ ] Version control for binaries + +**Dashboard Enhancements** +- [x] Dark/Light theme polish (CSS refinements) +- [ ] Real-time resource graphs (Chart.js) +- [ ] Worker status history timeline +- [x] Cluster health indicators +- [x] Alert/notification system for worker failures +- [ ] Add a Clock to the Dashboard (ISO 8601) +- [ ] WOrker Cards instead of table +- [ ] Search bar for workers, experiments, projects + + +**Testing & Documentation** +- [ ] Cross-platform testing (Debian, Ubuntu, Arch, Fedora, Win, OSx, BSD) +- [ ] Installation documentation +- [ ] Configuration examples +- [ ] Troubleshooting guide +- [ ] Validate IP change Stablity + +--- + +# Design + +## Design Goals +- Cross Platform (lnx, mac, win, bsd, rpi) Linux First, support for others later. +- Node.JS based Manager and Worker code bases +- Extreme Minimal OS Dependancies. sdl-mgr self hosts all the necessary code to deploy and coordinate a cluster. Including distrubution of Node binaries. +- Automate Update Workflows +- Zero Technical Debt (ideal) +- Sane handling of sdl-workers comming and going, being added or removed. +- Zero sdl-wkr Configuration (ideal) - Install > Run > Listen to UDP Bcast > Auto Join MQTT for cluster command and control. + +### Security +- Security considerations TLS, Auth, etc. are future scopes of work. At this stage we are focused on getting the manager and workers up and running as a Minimum Viable Product (MVP) for a cluster of SDL workers. But we are also thinking forward about security and cross platform support so those features will be easy to implement later without a major code refactor. +- The current assumption is that SDL runs in a segmented subnet with restricted access. And that we do not have malicious actors trying to compromise the network or poison the open MQTT broker. + + +# Architecture + +Software Defined Laboratry (SDL) + +## Directory Structure + +### SDL Source Code Structure +``` +../sdl +β”œβ”€β”€ docs +β”œβ”€β”€ sdl-mgr +β”‚Β Β  β”œβ”€β”€ app +β”‚Β Β  β”‚Β Β  └── modules +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ mqtt +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ nwa-lib +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ sdl-mgr +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ template +β”‚Β Β  β”‚Β Β  └── web +β”‚Β Β  β”œβ”€β”€ conf +β”‚Β Β  β”‚Β Β  └── modules +β”‚Β Β  β”œβ”€β”€ html +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ conf +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ css +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ img +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ js +β”‚Β Β  β”‚Β Β  └── md +β”‚Β Β  └── scripts +β”œβ”€β”€ sdl-wkr +β”‚Β Β  β”œβ”€β”€ app +β”‚Β Β  β”‚Β Β  └── modules +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ nwa-lib +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ sdl-wkr +β”‚Β Β  β”‚Β Β  └── template +β”‚Β Β  β”œβ”€β”€ conf +β”‚Β Β  β”‚Β Β  └── modules +β”‚Β Β  └── scripts +└── tools +``` + +### SDL Installation Structure +``` +~/.sdl/ +β”œβ”€β”€ conf +β”œβ”€β”€ data +β”‚Β Β  β”œβ”€β”€ jobs +β”‚Β Β  └── proj +β”œβ”€β”€ dist +β”œβ”€β”€ logs +β”œβ”€β”€ nodejs +β”‚Β Β  └── current -> /home/jehaverlack/.sdl/nodejs/node-v22.22.0-linux-x64 +β”œβ”€β”€ sdl-mgr +β”‚Β Β  β”œβ”€β”€ current -> /home/jehaverlack/.sdl/sdl-mgr/sdl-mgr_0.2.7 +β”‚Β Β  └── sdl-mgr_0.2.7 +β”‚Β Β  β”œβ”€β”€ app +β”‚Β Β  β”‚Β Β  └── modules +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ mqtt +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ nwa-lib +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ sdl-mgr +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ template +β”‚Β Β  β”‚Β Β  └── web +β”‚Β Β  β”œβ”€β”€ conf +β”‚Β Β  β”‚Β Β  └── modules +β”‚Β Β  β”œβ”€β”€ html +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ conf +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ css +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ img +β”‚Β Β  β”‚Β Β  β”œβ”€β”€ js +β”‚Β Β  β”‚Β Β  └── md +β”‚Β Β  └── scripts +└── sdl-wkr +``` + + +### SDL Manager Install Script + +``` +$ git clone https://github.com/jehaverlack/sdl.git +$ cd sdl +$ ./install-sdl.sh +``` + +This will install both sdl-mgr and sdl-wkr on the Manager host. + +### SDL Worker Install Script + +Once the sdl-mgr is running, it broadcasts the install command over UDP. On your worker host, run the following command to listen for the install command on your worker. + +``` +nc -u -l -k 10101 | jq -r '.msg.sdl_wkr_install_cmd[]' +``` + +> **NOTE**: If you have a firewall, you must allow SDL Worker to listen on UDP port 10101. + +The install command will look like the following, both curl and wget commands are supported. + +``` +curl -s http://10.0.0.144:8081/dist/install-sdl-wkr.sh | bash -s 10.0.0.144:8081 +``` + +### SDL Worker Auto Update Process + +Once a worker is joined to a manager's MQTT broker, it will automatically update to the latest version of SDL deployed by the manager. + +### Worker Join + +- UDP Bcast +- MQTT Join + +### Worker Telemetry + +Workers publish telemetry to the manager MQTT broker. The manager will collect this telemetry and publish to the web UI over MQTT. + +### SDL Storage + +- MinIO S3 Storage Server +- this is a non node.js codebase + +``` +Project +└── Experiment + β”œβ”€β”€ L0 Raw Results + └── L1 Analysis Results +``` + +``` +sdl/ +└── projects/ + └── / + └── experiments/ + └── / + └── runs/ + └── / + β”œβ”€β”€ l0/ + └── l1/ +``` + +# Projects +- Need to define structure to manage projects. +- Projects will be managed with JSON files +- We web UI management Edit interface is nice but will come later. Just need to get things working first. + +## Experiments +- Within each Project Scope experiments can be conducted (run) as a specific configuration or permuation of variable to test. +- Each experiment represents a set of parrallized jobs for the cluster to execute on. +- L0 Raw Results will be collected and stored in a run S3 Bucket. +- L1 Analysis Results will be collected and stored in a run S3 Bucket +- L1 Analysis represents an other set of parrallized jobs for the cluster to execute on. + +### Run Engine (RE) + - Run Engines (RE): May be a Python, Rust, C++, etc. executable/script that is forked by the sdl-wkr Node.JS process. + - RE get input configuration from the local sdl-wkr process. + - RE communicate only with the sdl-wkr process. + - But RE processes may read and write direclty to S3 + - the sdl-wkr process communicates with the SDL Manager (sdl-mgr) process to notify on status of jobs. + + diff --git a/docs/INSTALL.md b/docs/INSTALL.md new file mode 100644 index 0000000..02fd478 --- /dev/null +++ b/docs/INSTALL.md @@ -0,0 +1,119 @@ + +# Software Defined Laboratory (SDL) + +> NOTE: This is a work in progress. SDL has only been tested on Debian based +> Linux systems. But has been designed to be cross platform. So running on +> other platforms should not be a heavy lift. + +Other than basic OS tools, SDL downloads all dependencies idempotently and installs +everything into a single SDL home directory (~/.sdl). + +--- + +## SDL Manager (sdl-mgr) + +#### Installation + +``` +git clone +``` + +``` +cd sdl +``` + +``` +./install-sdl.sh +``` + +#### Upgrading + +``` +cd sdl +``` + +``` +git pull origin main +``` + +``` +./install-sdl.sh +``` + +--- + +## SDL Worker (sdl-wkr) + +#### Prerequisites + +(Debian, Ubuntu, Linux Mint, Zorin OS, Raspbian) + +``` +sudo apt -y install gawk coreutils curl jq grep sed tar unzip netcat-openbsd +``` + +#### Installation + +(Linux, OSX, Windows(WSL2)) + +Listen to UDP Broadcast on port 10101 +- To get the **sld-wkr** curl or wget install command + +``` +nc -u -l -k 10101 +``` + +OR + +``` +nc -u -l -k 10101 |jq +{ + "ts": "2026-01-31T05:20:31.815Z", + "sdl_id": "906a027c-52f0-4cbb-b3e0-5eb84856d50a", + "role": "sdl-mgr", + "host": "haverlab-sld-mgr", + "type": "udp-beacon", + "msg": { + "info": { + "desc": "Software Defined Laboratory: Manager (sdl-mgr)", + "version": "0.2.10", + "copyright": "2026 John Haverlack" + }, + "cluster": { + "id": "haverlab", + "name": "Haverlab", + "desc": "John Haverlack's Home Lab" + }, + "mqtt": { + "host": "10.0.0.178", + "port": 1883 + }, + "web": { + "proto": "http", + "host": "10.0.0.178", + "port": 8081, + "api_config": "/api/config", + "config_url": "http://10.0.0.178:8081/api/config" + }, + "sdl_wkr_install_cmd": { + "curl": "curl -s http://10.0.0.178:8081/dist/install-sdl-wkr.sh | bash -s 10.0.0.178", + "wget": "wget -O - http://10.0.0.178:8081/dist/install-sdl-wkr.sh | bash -s 10.0.0.178" + } + } +} + +``` + +``` +curl -s http://:/dist/install-sdl-wkr.sh | bash -s +``` + +#### Upgrading + +SDL Workers continuously monitor the SDL Manager’s advertised version via MQTT cluster status messages and will automatically re-install and restart when a newer version is detected. + +Alternatively you can upgrade manually by rerunning the install-sdl-wkr.sh script. + +``` +curl -s http://:/dist/install-sdl-wkr.sh | bash -s +``` diff --git a/docs/WORKFLOW.md b/docs/WORKFLOW.md new file mode 100644 index 0000000..35ad50a --- /dev/null +++ b/docs/WORKFLOW.md @@ -0,0 +1,61 @@ +# Developer Workflow + +## 1. Start from clean main branch + +```bash +git checkout main +git pull +``` + +Ensure no local changes + +```bash +git status -s +``` + +## 2. Generate new branch + +```bash +./tools/new-version.sh +``` + +## 3. Commit changes + +```bash +git add +git commit -m "feat: " +``` + +## 4. Update Changelog + +```bash +./tools/changelog-update.sh +``` + +**Optionally**: Update changelog.json Summary and Notes + +Generate CHANGELOG.md + +```bash +./tools/genmd-changelog.sh +``` + +## 5. Merge into main + +```bash +./tools/main-merge.sh +``` + +## 6. Push to GitHub + +```bash +git push origin main +``` + +## 7. Push Tags to GitHub + +```bash +git push origin --tags +``` + + diff --git a/docs/lib/img/sdl-about.png b/docs/lib/img/sdl-about.png new file mode 100644 index 0000000000000000000000000000000000000000..44469b1b0c9b2c0802abd19f72735c9fe2eac293 GIT binary patch literal 112019 zcmbSzbySt>xAg{8lvF@E3|ax{4udYGyEY-+or-{TcS%c^ba!{>2I=mu@7YJs?~d;q zAC$JGzvXM2{x3Sl@(t{Y7nVag-SwpS#^vtY3o7?OmHSj?o z&mf{7-pV;7Y|q)d1Wnv^94JOTdDl80s_^&)xlU_Pov=xc!j1|f*H6hE`Cq36LzCQg zJ9YLWk3T#N{YL&`=}dU@)(`CoPD6ciu#fQE8rKA8^44-{!GLRjRX}r+kMPSEe?DQ5 zrC<_|?|(in(-Az6pZxO){XpaR?C&+cGKT^m{(P`9mcRVHm|Uz2>Cd%KL?gnG|GghI z1?KHRCoFkz@Z&D*jN-+M-%y{wUv}8_=4KMX1tp3>wY^}jrl{Yz&(jC9(&>b@)Zor9 zKYynKL-NnpET@TEtTs)UHZ=3t(#m8=JXwntoMyS+zd5_pPax~fkkfi7&u!Z$CRb8^dYLwl$vbf0p3U z5nTLAE+kDjs8$yCcb$?wKb-Lt{rW}TVCsfTuO!eH$J+HdGPKzXl}gEdBaC8sAUT`cW=5cHhKq@WJ2q>>CAq;tv_y`>R_F7#hE=tm(BV9bFn1k9_`R_Q#h5hNHjg~dpE-Fs#1dk=LH}47qS=m zO!~TeNp))VE(FZX%#_lx&7Ggpe&=6FuJzB>T6zlK-kPH^QB!{}P_Ow|qd*6Ct$Xj4 z0)9|c%~@KWI6<}DPV-2XG;DHw>w)VTFYI)?sWfZg{I@^D`RR;C03qjB$#}M*YRPzx z6A(a3rB1ALD!-obTzhGsekjjVwzS`q9!P?f=pN`mj3x^D7M{PablV|pYHEU@k@9?N zdVhPt>d>j$@#@cu9GW{X&EV!JSY{Yvv-9xqOx8LwSD4R<6&1wC6Y<}kzm(0C$dnT` zARH|m0BiL--&j}o!!D2GzVrS<;M@61OC)x7_HwhC_Tvra7?ut9Qi3=Z!{(;WcFx=4u^1YaNeDOr~VKgYiYbAl`S_UqA>Z<8N+lC6{u=#Ke3}$Na`_8=px@ zPpkRc0px>_5F%sOeJFHENpEOqsCXKmN6Gi^sk(5fJupS>@27}mb!PK?fbqi;%I?R- z@v*s!alYE1KOyB?DYITrB$C$FIGxMgjs5oQ20@mYjSb7$?mUI{ssvbjXnTM}B2%oX zYQ@2NXv3nspT54nT){Sqhw)Rz@dEX$_Hqry3^92#M_8&r6CwvUxAoOH9hdz^^OA7z zl6oqm{VuUWzM5dWMC>gm?5U=vW`Ras_m3|~RThhg4Y#L6#^ZVV)4#G`Ym~%%^+M6s z);<_7_WhemQ08NJD&1}fDJdy=oUZXbgoCj(^xs&oGE7cQ4HYJHL&nE1-mv;M_`?+Q z7J3+t!a1#@*dK7(cH?wMy%L@%m^(jeM5w42%po$FVr^i@ zsloT1Y>mBK5QX3x(p5SfyZ{?>uDANS#9)9lH;`~F7yFbafMwMXLakgv9wa4&M#dJA zr(CM79UApYvA|+=o3pz*;yJWk0=#}GtvoG=k0&70|C>uSzjh&!k&(IGxq4~)Hs;D? zhzZsd&S*z6E+J!n{FBZ;@DDh)_xCS%y2t-t>G$vDdR$1xg z<>m3|s{KtjhrS|KhYP*2%-%2t&H4n1SSExJR$~P(>-1O7fJY z*&H=L*0_=i;g=dl*d0n<;iOP0VAn4E6c5UWZek4i}gYd-61~Z@l?c%T089 zqUf5I3a|E*ZbHU9(T~2orOy%^&Z-o+J!BiEmNS zo7H~Jn1zGui-zU>FTBLl4e{VdiK6-03)F|GsLpE+c6)QeA3wg)6N^sGG1hzc?!9t@ z_6T>y-wdBjN`c9TO~hup3exc-QujZpclBTiLdxS1qE+|fhZcYUr~L=D5;$^}`4V;2 zwh*PI&~rD$Ea~JFsT(02JBxHff3{xDO89DXoHRE#QeGO_i)6uv>w--ZMTiH@oTzizwb}rw6L;S9^Dvm zxVf>fU7Ky#6P`Iu6bXHCpDvz#$?@D;D40aLhfT9XW%n^5q0o(xq*AR7CufO4mTCO> za%s8#NL2o)>AH}BK%;7hYHNQG#?Cb2$Hws}4c;1P;e_I!FeHx!OaI8+eD7ZH%D^23 z>gsLxAq8qInlOAQ;~Lt83cXM99u zGwW#2_aggi9EMea z48Nyn$@str#(gwQvMx;H0sdpd{sOU-VC>WLPDvJJOYduC@7 zZf`CL`9S?Ry7_Sn4rXAVYWZ-Ay6TIUFO|zyF;Skr7N{vV)k%bvI5Ll47e_sPr8oks zV~E-qX&7PsbMQolDPnj;`jHA;I1Gnlu(7bP_KL@hd6eqfJuRU^L%&3q*!y`z5^3d2 zmMV9aTBfYmXjVT)5ObCgb<>SxWdXE&DN};3am=R{?3o* zw3cwxA$s>W3teR`HyNbbWQQg2S+mVU30~6~j*p^q*Sv#_X3K{gPZS)Hzj-r%2~M1t z0vbqHdFl-aP$<;NX6tKc=%)lO|7iN90Z*Sw`vLax-fvmbOYXAYV4ld!-LcrSVg?9y^1L@#q_6Mq+L^L`~S0T+YX&`nra|ivA@JNEF>&qP+3#clnNBP9|GGoh=X;xeYb<*73y<7$B z+h)P;=vU$o#Kj@?*Dv%ngZMQaPjp2@9*(%jKBb~(F2%Vc4t9U=?aywL_flVuQSn^- z1cexD?&t8#6|OB&HASW6@A#Pb_)@(VmBZD(qwzI9_gmsD89m>CfaUG3wBq^=clEM3 z+l!ta{|jq<*(w=9;smjS-)*QMp?#h$LKw@DUmRR~`C9dsJCJbPVE8;4eT~oQIL3~o zVR3lnRb>QCpnA$XwJN8`_ddZPRQ6_Wn&mOuQ>6$N;9WM^EMC&m`hn#LeWfU6=d|0* z=I#bgy5OU%-`8wgco2{(EM9NC`}~=jB7xX&D31nxbHa7Y^3RT6Fbwy44ty`O)yR|} zPToGsl1vK8Yei^0B}w}B?c2wEKjTziBuIr2Iz;JaVAT(@(o^+jJBy6d{uc4(60;R7 zz(|&qUnB>lGiJ!|gfy4naOv!Wu<+4U&KQs5;Wva`*U4(b#OIcfk3|9qZt<+1vsG4> z%j=8)Ikqb1s}|b~xyWRSJjqp;m6i3zxIh4XOMha6$3P-?7a{WBJpH1N=lol(;cAok zO!=N}ZkI*G$YF4vlnH`56auo9q3!eWyKJFp*dZTE-o&R&O~l@dU|@@&XC2r`{$Zrqp`?^)gUFF5V2dH z>O0kRgQ7$O?g^@ODWEy)%+|&F7)~A9!>7J}i;P;c)rUD}}tq&d%>3^bXes1^~oi zWMWE{&s9*mEqz%}u5R@oI7p^a5=}?UVc`LaD=`U)Xe<*2XqZ?08R{D+Cu5M1F~}W~ zDk|7dw#HenFAsdk9%5if$t1_Knd$ca{M3c6{1;Y*HL+AtU7nxsx8Z48obPK7XNV&= zW{5`@m6x-S=3#sjK6ipP$E}Q;8_obeHUs$(XD$z?4qrU79X4*6oUmWCF`NkVFCz7Z(=_B9#QE zDw{i)Zgj=64U9e)#B76X<}t zqZtNSzUvTgVTCxyY&V<-RvxJ&+!I=NFo>OlhDvEbj^Ps zpL(jx6XhxG!qU=vB_({}AmT;vaJ3ExUq`cLWmDdHeHaMz^GkJ`tA^k(Xe7jy85QfF z7`d3i+IvSM0HO^D3{(b#K#c6!Wu;hk zgu&*qCKk`_$bKjXN2Jr5n;!=Uqrv=Fdo!vkz6@uR4k_uDI_tIFIfpGY0xn`ARqjj7 z>s!sB-KD8=SE~G(a_Q3x_PfL8_VFN)=MmmqRdC|GI@mqiy~29@SU@!TkjLSC{}0eT zRAvOcmB{eUR84RJuakBt8yyJ#7tZBYaI`Z7?XW!W{Iq(*_NvR2*ZDL6w%XrOW<}wL zQ=ay*2~i@BD{yU~g@55`t$QF5+cU+21dy<-VFQ>9g$=Qc)@(i)WZEl-6ftp>H@KQR z#ZmC0E7^geq^$QWODjNtjVRaj@eN z-f&qDyQtX~056l1%W2YuN!}$~WybXtN-Nk<-FpI}YgpUIvm{iG7T2w!NxZ1f4f=`k zZ)DHzYP|fTKMq&j!w}rzx1%PLi#F$yDUf+8mCWf6J#aU$3k5;axhBe}COb;?I(i9us2EZiN>xtipgl;h zv^E5Ro25u*|L)y8os&|B4XYfvLdoY>zejUsLP(NobV$xl>)R?^FBj!aF0!W^clcYTnYPPoG{ZRmKq7ZW0w}ZCGY9s{N>C{eE*!&!h#&i8q>=9SVto0L*BasnsEs%?Z!|p zgPkA=0jnJ`s6`pF(YwXJgT8b7pk%htPHhmM7g_iAEuVH}iN|UG(!zp2q z3gkKg;cu76<$Si2WqKurV`-5pwBtm~XBF{P(63l;%kDi%12X3NU^Z%7qBwY+Dvq<| z1V_}LvJFp|4eq>ME!XaKEQ3DswQ~7aeaSgAJwUYSZ&yAV{iz=O?zAmiw&7C(fBY3D zaXPsBB%XbyU7b(@xBG1%^tPIQ&iN2z4wti~$6!Y~nzC_VHalM$?p|J^49Z_-XZ1doW^iE!k)I~_8!l)^!?keVFi}?2RloFH)2tXB= z$D6)sP|NpoQtH*k&@3vFnTn+~#+QuD%q{tb;x4pm|t9&ZGt@1<}M!8DKE^*u<3bP`Jh36D*T*wdblU##oS0FH{TNs+*N zG`;Q!otE~#Yc_k0ye)igGsK`a|B-xgD&p-OIAY4LHxjdbiK=w%O*CjX%P-eDM}=%DL2pPVJDfY zA7LIRMFja*zx5|$Wo3l^=JF$)@>d@!Nb4pm?jVnkJWl1Wf)e=ML-!t3j-=d7NW}An zdw5WR*o_Y3x^3u>71sT554$4}R6r|$cF6>ZrxV>0i986%$j*R$ zAlmx!RO|d_@$ve&SGBDXV4ipXiQgy+YkdHYhAItF!5@s*Sxv{gQ=Zmq41={Nir!a5 zS|@~GNu^o@lES@AHRxfsJ+$iBQqvAY_Vs_(`1;p1gj|h)zj0Geo>cN^_MPzYQ^3U z`5xTf1U@e-J@pt+CT8qg1IA9a*w;rhh(Jeo&ErHaul9=3WjTgD$k9ci@L-|I(Cw_GfxHRdJ!aISR$9Gq$nL-uxd_+GZGGF%i==@2X))v+{vM4 znjUI$C~jXI=xe)2HyYkp9?A`8l>W%cef6|EIzvBjGhHMU(L*x6!eaB7092OXd{Z|- zy63&kcS4T^yGR*$N09ZK(f7g3^@XLC&ArC%?l4gF(0d@Del1M;VzU@;wud(g6ll~) zWry`A@VQ1&1`=8BB{68Y-${a&{^28Zif7L}7T+T;$z-xXSaTGYZe97MlDJ5=xYW8M z^v?&Iya!;Iw~vV8U0!Rv!2@;B=o1MlDk>aRToulQaaw0qkmgFn>hwEapED9Lt1!C# zUjA@J4Z3hZCNAEse@f7MTu2Lk)!>EJAM8%nlTb+~E{<+63-t0~jkv`M=0^1qaBgSC zF#c4CT*zt)NapYod-|%#@(6c#?nv(}C|^l+?f#E%;V(~!SRJ>FUB+et8eBu0y;*ly zjpeAAUW93`^{3h9;sU0o1EdZnn|;h*hI7Lf=pcOr#J};4W6dF$-k8ls+G)6@?YhCE zReILX%Vu?~B$Fl9m-AE{WC)}mMv>iec`}~-JmUoQ_oX9gDg5zz+ge-id4Npae$A64 z37Csi<=%>}csBDCm#UI<>0}HK#x!p#!|edcfNYI?6mC1G;c7kWXZl_5EcRt-6>}QX zX-2|W`}0-He*k_45d1nPEO#oF4A4(k&ux1`ChU_x1ZR!r3>JI!}S9w#`X!hanx%FPFY2knPtqsh!(zVpqC~&% zumRtAqIH;wH?1Q`dU&#d79=(h`jG%Lhq9ZK))y5wbv}IH&i~K@G}Me>$v|L80;nNM znA}=W_p#|KIDS@_#{}JxwvJm4aJYYKJny*L1arUZE79wT;j*QzvD*q@n2%Q+8bbd^d7oHguZNz>$NnEV2qUckBgha1d<=0Vt5^&*& zs931+^0>_WRO3e21++UI(h2Eqb6ogDcYAhuvNe>8i;Ld%^HIK{5~`nuKJAqS>tF~% zUCWC^j3@bjUwirALq;Aca+^~E#7IupnDK-I7Jg^(5>-)u{6q)`gj!}Y=yfb}!wGBs zrfGYF&`C})pJIVrsPmc21>i*bH7f+3NY zs6c950vA9`=E^;D3L-@#Y31`|=U2Hj8x%onXi%w0OyYlZRnnF&7V8H>snn%S%#DyO z4DZPkZ@VC3PN5aQr#kBPV-u%ToG#1M8Dd;S;2Z6{P;|l>lC?7*d-(c3Iu%5r;v%ka z-3_5L9#_&6-0?4R9?)<)u{3D!o!XwRi$35F&a}$#gAnt(g`uo;xA`5c6rU6GTZS1j z>l3=waV=#4@SQGo({NA@R95%f>*D?j5o7I`jPQs9(e3F{{Xha{^F7r^?f9It70svS&@hSw zI6sPrh{$2H{t(=s*@Ut}cgiZWFGf&607Af|+gLB6J)9*yO!eLGUoQZjaUF-i=4dv0 z!`YEDd}G5iCHzm&_r&e@=EM&xYr6;b5Fk?}`p$)}>EZ@Ywn{@opM1JggVnj;zGg8U zqMWzho2?QDIUz5Psg*M-Vd`n2_~yCl?UCf!mMil8`<4S|ZW79w5(nc|JJ!g#Xp9F9 zm)0)vx0<#C6VbEQ%J!FKpiz4z z-`d+70GLio<556(+Jih8d@wG#Y1@8L)A2AhRcDjO{Wjqf)J=bk5EB$1z;I9r`r|$a zWU#j#D1l42c^X$$@FWV<2osYf`T-z8v0d}&`Qa2}85}&qz+mg0cE~n3D*!d=E1haZ zspA$K1Z8rX$@K7^iRlq&(m!~k74NOJVreskBQs1*ud?@A`0g1bOagI-i7#Zo3^2jr zF9cD(-iofnNg9#gfL8e)5D-vCc_A+(Bm|NPkb=@t6mg?c@2)KX>TJ|_y?t2lh-OQMa=aM$1K@<^(Ha9H zoY5(ORT5+c`e2gj7#YPaPvGIwDtVYFms)L~KMEq{mWm&zqmldh41kgZ4%;|DyuGpM z#1!bQux}9x$b4T@Udd|qA?53R$ohC1o*O19`D90r&oDjACjH$-rMVwql@!BtT<1Xl zLVY;!KC#Mi6LYv@OT+Do+jiK2spAL|33$BMBU$VwY8N>R3t4-kXEOnJTcw`=5KeT) z3cLD#qUk)xXTc|?r!79WkYctK3A4Mx?gX0flsiRUmv!klmV>hsnX;UMbWv!3RQ}9V z@kn^wSZ>>t1WR^rZ|~1dm00zf(-3B1j3i!Hp{EmKmYWo3T$&YTEkbUZW!^(CeLkhA z0fA!RL90pkh0J)K^3t}~!61x(d<~RnsA*y}c%6U|;mhNVaAHi43_T~ax)K6fkE$w8 zSErM7&}#zp{v|P~M!8m(rbReV8|Vs3`bEnJOC81ik5I(UwQE6C9Bas7k&E9n6u6hlVz0@{I`Pt5la08#~`C`o;hub!O z=#wWBNgSbWu9t%in*3(4`o1qn$dh%(cmQUv)O8CV9p;9Y(gy8?fu*~ zX^D;2gROz`@ZrMp2D8#yplA*by@}6RzAE6TT0T;No?>9m8OxO}wzya{!Cbm0nxSX6 z&bi8IhpV4LOiM@S-6JYtTn1&K#&D{LyEX0WN#7WnVUlTAQ->cY=;%wJnmwY7eYTp? z2V^D10*y3@4H<7u7V-TK=f8`NiHTWohA}Ac z-v*Nw10_W!{Y@Hx8i0w4Cn_YjH-nvuo}&3j&+A`fl5!Dm9RUKQGlJ$NrXhvJ$=c!V%@x?lB+xfw)#-zd2`<_ITMMVv zF@7KstH`h0c)3E2NKjdI=DY(d6$)_7U%aR~yV!y^J_QM!hV#FCpIgFvY>+L`ci_U} z7f)npS&cpHR%vJQDQzdv|7nagTP+$QZRnf!UMX5%MN&|Th7Qp}6KJjh^J6gkEv8SN z#BZ9pu?`dXdzKeld>|1Fs!?@UryZ9Ub=9-W@5Q>$$yW;-YCX;kUj36K)Vh)!0kz+j zCLCO8wSsaV1*dU(Ixe2w919=+yQk;-RAx+#B+Ul1-pl1?LB9}gdo##%nR)R0VjU2X zi9&%yvh8hNAa-QhSKNtI=o0wJsHmtU`g%E>mLI|$4rjNnl+uaaUv91#+1axk4t~R_ zOaOCt=WB$$P@Z;%Qw<1fiiN5HU9R$Mrw`N9M%<9eH_awk0VxRlUJ9r|u`nQcbgW@ucTm(~J593~ayXw7%VyoZn_Ql$9Gtl9Ca3zE=?@_QA&a(Lo&X&d3i?RL{eA5O!@fcy zwq@b^-FS}U83Lh)j3%MAZ-HReaNX)ShW;@z&lU|YJ5>Yk3g(;%xLj~toz7Z;t6bz$vT+!Sq0i_6?|0)QS z5}8aJQE(o*+WS<`b8LG93P9>y4lXk`Jdt;vb{q2pJZy=>#$m-Kdljg8z86~z3=)!( zmR1rRDYXAE7~$K?O+B&5AhLcRuJpheac5^|6g+wfEiJ7uebPKwC8Lz%xXBb+13MEF zQ(l%k&>-$f^)tqQ<8{4^Fc?TWc)_zuHVYUipkNbju6Bj{0Dxn;H_y8|5HAc2bkq^w zCMsP0R9K9=@z~h%U`4*rj$rE}28R0UL)6W&Y)PL}&G1(SQqL%d_5oCclQ@92vfjPE zZ*sT-ol3p%_DMefWilVZdv(fu6RjLUyCD%foI$NP?gcyx35mq$Ct}w}SBlXP_>u43 zkd5UvpeHq+2&aJx!B0ho-%2d!6+0l>uw3?EtLKm>&9E_FRa9NGxR4w6_GPPNF!9#d z2ilVy=Yp+yyk)Ij^9~#=Ngq4Sj%xe)jNpnPKy`9(;OGs7WgKtLB=b~R53S9R{<>Pr zzXz(wYn3-X=FVqJ9nV`jqv(iWHk%&6U14s*&BK$h(a`HBJD+JQE`&xBwtTQRl#fa! zm6WO`s92)c*WRhk|0&B(^1bt}tMk&bQUVBtH|DqRhBHN5CWl_?P(U@JN0Z%M&L($B zRAW!J%q3#kkdnFWe=4OAW4nFJNlSjk6`pDuuK>2Eew)Md%ED$?d;7kOA{}d%VF80pvjJ8#6TZ8uu#& zS1Mt6g|0c<&2TECRZ>=V-BfR7Hi57qNxkwIT5 zx}VYucuc-Jr#4a2N>-!R-l@_qumQlsp?^7=4ZxdB>wFRd67uk9U*HC_{=(K)PpTK{ zqeqWYTzxPqD=Xi89(?vfqjq7V)IcJc(X)({)WOMVxJn$)EuJ@?#c+84#fz_^;ZzML zDhuxZ(a|7y*??0ebf7(ZB+GC*6`i}>1hf+j{B|6BW4pZ00Pf4?RfNO!Vo=0N%_oA+ z*r2QOrEZBWB~M>*R+>)`u`69q&g;3{9v-Hu^i^-qmQTG&6N4iXtY~lw#WgfEi1}Rb z-QC@{cXnvhtB;A;xx|BRcXw+aVuDijXzwKcx3n*iZA7g%4xa*gTYqAg*F_uJ0-9`o zJCVJ)<~DIS3FCczPQq!u{EV6-#uA${6gUfjZhP)|On^lwHnv#?mr6%-ly5wb59(`s z&e1Nl-+^{OqR3Q1%dv~p zU@)>uhfhQ#`}5=5?y86s;R<@PV<-FVV_lG#@{}+3w$($_x=6^}?}Pyli#f0pU#1He zrk3e|`tS(c8~)xOKJW=9pQb9nWeM=PSpT2c)BYRV@d&UnUpoVZ+k##;$%HZm`5_KAfVII8QU7`An|ar-2xaEQS0+aYiq>%PwIfc07iCcPtdr6 zHVEQ<_g7`_IMFa51*WaanwpNwYfdNNdFjAd3>^BX3 zd~XZ+c3&%3zfgbygMrTFs3?Hvkyq9`$a|e!nUgx6z=wO$NLy8)75AF9pOeW|1 z_C`ylj*gDnQ0P(N=~S^bI}qCf%gCSCmtgJyv+4G9O)zL0J~a=8W=ay?8N2$!5h&@DM<*C$&%{xM8@n4qYzJB!?Cr{YHa-CJpkO#0i>K^DJ?$C`p2IiU=X&3 zkh!;Jm?#6Qq;Pnqq;HU4^TGJZ7a!Q2v!qwX@_4OWQsgv73-YZ8kn@EE zK<%%z+aUx3z3xbR;5Tn?EZ}R=Q&hwt;pu$o?#^V;^%Y##=7-k-Tnkc398G{NaXj57 zXcrRtWim%7F5Yv-sY^C@%~={QcbdY>!8_QL^I2zyw1pey9 z(OLqp^#c#A{(ct2=?H6s0Uapx{V6dg_2PCfZ~_$n`jsBwkGe6O;hg;MSv*vBFlWZ> z2WCHTupeVSJ!V7Kx>mPbim^lz0PR)%LtOXyI(+a0r<7({1C~&abYP?Cx0Cyr&O2%>2Tv4?T4`Tt> zM0+4fPie?wr?ZU~AaMU4WoBa1={VhSaCV038~J&8-H(M8-FyG{2;8Nem23b0-R6IA zb`~s~#A&TRT@HjcQ}=*ARDSQ^V(q`4)z0|ZvkE$q!~e{aB&VKLYp1$Y6SA1K1CA5o zaJ=x;l$q_5vj7uOeQUXn3HBslUudL!oLvxHexj=y~p3z_zk*nO?2N1Fm3|sHCK0dxd zc9jOT45%kdYR$et5Y>t0<3+kU)0^^(1n>d$bDa^Q*l4BzjtmywJ)>8ze+RHX+U+7Y zSj#rTU|SB3NiYGTPAN^?k;rYoF_p{Ub2n?uVLue6m#0z;yju^QPIja>WEhD)0y^pU zvRs0btr4@{G#aoP2>=WW`6*Y77)h(J&^ZJJa;IFO4bGoB7!6EE26-u}%!Jjf;xco< z(s(ooO9r0a($(OyUq(}3VXM6@qoWaEOa(5x0r%+|u)3P4?7amvKz{A}nVVf8+1h9o~f$Ap` z&qfD?C2uQWb^-XjFNf8=$o>xDP~6|5~-fB;p+R$wEQdi(@hC z1h4W49kmTm#^a`woza?;gT*%!%l(P|03L+__6wvdK?etpaLQ{5z;GVny#^x@-#{8_ zH9e1HSFd%M%gD~A0g3;hT^k$;&})Ecf|x8RQ))0Yu?8p)7W3(E#8ahk$s7o#?8Gql z1!-CZYT?aMOU*8tzZE?SxUt&*EejLoGnuS#lG|~F5&#zF!8!&!2KAIr;kdJv7m9h+ z*)kcrr-jxrH#2IaJTBj%2Rh!+v0V8StuKgjIWnG_YrPrn4d-WXIb2s?)33bhpuNhm z6^A#wiPbh)f=BuwRG4%pAA&O-!v@6#pnB~%w)B_T*jH3Mh7ujU-jhU5=O8R8Dbf1B zILFcbxZF$n`+WJ|Bb*G3W`E-K$^UrT3aik=!y}mF>z{1u@*;xg9P<$>YD;}3^PJPM z*suAYjQpi0ZDPPA5hN=beKy_zEN;hS=dQIKTz0!r%XSZL-o(VaI}-4*n+~leLgRtL zm^F~#3atZhfC;*~ZNK|xifY+f=du_n#C^aoGM5IlwV)e20*yz=%{JIR|5S>ydBGr_+@|n7vtA2i^}rv{ z0-6Ov5Nm7>c`0&JC?>F41mmsRgZ3u`1YO@hK^euh+re}Y0bm$v>&zKV+a+qv=X8EJ z^N*X)e&2KDx6&O50y+WA#^kPjE1pG<5cFjCcpTqlVe9~vn`x;d2w2GOoqd4~WldCE zFBV6^-DG^~T!12GzM9|!ja}O$2W9YTu^BLxRlr?!fWb}!@}BD)au}Ve0?4*sRF{PC z2?*dSK3ps8*h8+LNQN6#fK??+q|5Ukcm~NkQ@UyY7oRbv2^T?qsI1rMfP-$)auYNx zp+KF0t5`q?YwvsrI>KfUCzC~nsDQ-o=yDEhb-U9S=iZI@|5e*L75*6M^0@*=nU5E-ISMUi&vT3c$&nHvi~z+wlPQ zpqfAHafiI8H`Sae84qTDF0Vd|@r3|R$O^!2jKu09qdqc+JkpQeL;A0-=71 zmqET;4xW1e8wJ0I>tJW+{)`%@HRa?P(6}o1*5D5ZPAS~u5tGG;{LI$K8u)xgqx%s7 zz?6OQ@&y9OS*8-~?Uv>vK$ITIQj`Yn3sEkA8+g-ewPMub2vjv-%M4wjlD66()CMr? z{tw_;Ki(LFGuMEK7mIWM?O+H<2jIyHXEP;y@iOfVD{~tUfT@7Nl>}+8%4(&xbuw(YpYUTA z6j~XNeh2Lg)p^L-UEqIO=6`D1lTf8F)}v>@G0bW_Zn4%+0(?{)Z--I^0nU=JdqyRl zpw)DJ0l~4iS&r{#goQ9@REB_I6uAPmSTK#qXtMqhEDNxuX5KY^fxE({T>p9@JL4&g z2ao_kN~>5P0nl1k5-+!h_JC)@-EI8f;9!NB^-!}Hi_vTh(A*B!z96a9Iy~{n?2BWx znS2vPuM7tQz+C@Qd^|70(vtC*?$6e~Ad@O#m*8=|WB~x20-WB;s;Vboj3|muRjI*^ z7p@{7&jM^(I^N<57zZ5_6N&B!8Uz65n%5&G&mmvGeua~;G&1RpKn1Jc-Nkt=_Z|3b zi^f}CRxSR)@!v(TC|>=;B?Gu=IF|D?B_#zE6Hp~7fkF7YpWpAxlP#bcPBj47Dz|%eVR^R8>xR!0!N@PI)mil9pnS%C;q3hS$U8(a5j58X%m!2& zDz2`3Be}sfi$`(%<0V!napZ~dF~D|3m95dyKQ~{+@XxD3sL* z)1HSY7&<^khAZL?w}(2wGY05?&7dY2X6pad{eK=y<>YlfS=mo^j%9;V!o}Ta1_nSG zz}?AFeQ^lD>L9h}h6Jx3xVVO*2BAUbTxC=&uLJmYkvIXv9!_=L3vzpv)d?->*7(sv z6A~!MU8AB)=*15n+#dq$G$=D~2j*SDyauy6mp$A)5La>Ow<;O01wbiX9P7$LN(&fS zL5E$r;k^C~_$YQ;;|VdF*S9h7fQ$7mpO6*qhUdp5MO*-;86h;HcXi&9pTYQ; zG#Js*^S^`c)u*NXoaP8}H^6?!Me@!El7hEU=le{M>-%w#2p)pqlXRc?c$`qYw;?YC zb3DB0>yXhz!s7--XA;kXL$xH7(uPwT1~@sV5`m#Mwx(+?)?hc$R3bBgAaT6vuJLWX zas??Ejs?d0w;RJ0V5ElV@^rVYwM#bpV?FWJ>E7CW-4zEh1{_pwmccA`rdfeQ8ee^2 z@&LPd%H-R8wcu5GKBvR}V}T`|v*zNB>w)teGUy;kYmWHp{D zJ)?qjN71#EQ20**<1r=~AGvr`FBnaWWr%hW>{*pW5J{#dnPD?;oNFQR=!2Q9toFsf zzJ$3j>32S^Gi~u`LPGwSA*NTaQ5OXq;2{j=8ib>{;=qOnCC7jmjr|NVjFX6C3X~$5 zhIa-f20af14t2Y}m&`cUsmSlb8V07#$7L(*ObgViz7_^E>;VO*=*gGKol~mvwgkr` zg2OHWL?2AO(^*Fd&g+jJX6jtw9K}R<^lGgW6F%UspNa%p9L~=% zAU{;bCO&^Gq=(qmBmxL8Tk^pYtE#%hc!CtywJJh%sRN6{{R!ZpX;-Cf|;_-(NzV=h?frJ)2m$tNujwG6zWb;4e z!-03_Xc5_U?`v-nc>E6l7;qdMqFScM_ z@>vp%X%@D_|G)kSE_iC0Um~F#J$jvh8FY5n0ooB5+o2f$<@F$Nz8TQu+AoDedE@yL zxyrLtgY6HFj?&<>Ui|J~yv^BOy4Tm*Y|Q(!8;zx##z!6BeSYR`U=>RSXY~X@u0%yT;<4})>mp5BI_W`)+bHT&q zU#24{@&2gt8u_ih;Zzq!7bt||6YOz+&}C_sxvGP2?Q2;1AaHnV>NRj{%?)g?rQ8%&YBPm`g4;U)pf^4{Wr%u>U8zHt;%u#BIPfXL5$OEZ z6#~1NH<*{!e}GBcSbLFdO}mbU-5%sOpQJ6IL_S-IOjEabUbwJzz>!okr47X-lC6kM zKk)MQ3d9@yM;8QU(CZ=5I$l5E6!rDB$pcgDpM~2=-JVslwQ>pUZe8X{ z0yO{Am|A&>m#BY=hlYM!d5A~8TKO_VB&@=n{}#a!^7BTBPJ>47u}68n>TrpqbN$sW zG9seYV3n*em~(u0G;Uhue#eKpHsD`cpX`pYx+Lul#(N|IQRz_n={@*&KLpAM%MMOX zQr^~8OC)o?0;f+{OyBG1`NGmIp1s+e{S22LPP z$ePg&V|aV%ty8^`fTSm%JZs&bX`W@l!A{R;uqA(RS7(n1yTl)|=*g3;|EMPa&yOMZ z)+T1g_j2u%=Gb2BJhr-<8e{k;ABBa$Q+M&ce_QQXt^Dy3an+GEE%o|+Asqgb%#PaR zwZaPRK_la-8PvPWI=+e|CS}LLWixyLE{M)ov&G?&NL=4ssa^c1|@emu<~wJ?)a?uN@vBvagfo|MLKk0_$@Q_|0bQ zVazWqSRgCikt=DKmj_@A)1@N)dKzxUg0`oMxwiqq`gN)_v()*Fbk*&Y6DVZAOy|Pn zvSo(qWevI`=~{fTv({(U)QRo5zd+lSZ9!=lCOd}yUd}eTwL`r)TwUoP;Xc>i(*J)> zQNC)$#&jJXx7#EkNpj1#V8z(kT1;D%=DsV7<*=^q_Ya!bCC14-9l3w#DsYe?>RAgc z20T#5i^b`LaNhhdwDg=8m!xE8RB}i29%WRrY)p63@Qj7UZ=VzJ%r2PW+vMt6@ZF>T zrt%VVKKmgo>6erp!`xRhN>6L-lM#r=0iA zKc9E)`?12qT#csmD9$ool4ou~1OFb|8>MpoRX2R^vO?3fc(iQjLL&a~d9$!){H)tU zHl#~M_Vde-u2W_Z&@Q)0|HSR`6bQ_Fc_2rOnhtWzuh{QR`MV2{ULzf9Rm>nfB<2k| zHJvHa9eSrwsN9oO$B*zaNA8i+$$ESFUOkn?`N!G0xv8rWJ~){RNXvt>f$mK63drN9 z1Z`kM+;A$DMmDSOhi(_vv10|EJguVs#bG4S@!kLuHHuF8Lz~|U!b3jiD9)o**#Z*+ zFdlnud4&OB1GUiah0rq%W_*PXts&B%_*xd>cA+sqevWlh4Yc}}F+_>eMG0UbQ*u&E#J zwn+Ah9L_Iy%jpvPrt95lCvARP3R;)Pzy56erY`%f5m`*_n4g{yv!^diWyMe5JH-~Q zV3uv;Chl#WqLyM&m+z~`A*%;$l%~bq!XKLK-|oLblyPVgW<@i?`*zYHQIg35o_Oq{ zP2;od^g`~9#R0Ca7{*pmtlGo-H7#Nx#I;&uBMshfV@<~GK&(WnDG(9Y>smc0KXv1f z_{l!kggl0BmF_L?BQa0?*h9I((&pX6R^2C);d{jkfAgMb@6#=`RR z@4F8mKXBi-{-zKI8aR@jk0*5vNzI%TxWMM-Q|PCd|HIo`M`gip-J&202BFd-AWDdU zbVy1I64Ko*ozkF4mqBfG$g$>}l?S*-Q4z&k>m?t#-f!5F~&sFTDhDr|E$%LD7|D$rN~Hs zKY7oEuXFl7TdI59hr4#THfJiSoWB-5Pq=jEt+DCZv)m#%|Apm>jj))LA*)rGwe#i9 zL&Da7bb)p2A#;>0h4`ClWtsHF){qDMGb`3md-`IX9@LMI6Ezy9DvTdaR1*r!_~}4D z;A3*~00MRZ(MdnK7_`6eAw_D2yvxFW;^xgZ{!j}Ifl03Nb*$;S--j zMtlCn>}r%VCZTAO{PAsPR}&lR-?4_UhdGs58bR{C7m9iTZ*{hIcy`ikuh{D zU+Z)0c>9-$2cS4%etKNJ3TY5q247ry;8yjUzk;O|HN1ATzS{-`io$jXZ0 z^74|NGYAYs!cwa*q}bD`&M|$x61NU##}3xv0_g>>fw5APgQi^%g__kNo|~5;H>xB) zSSnMk5O)wpxVFdg^<#D|$Fk62+TUJVQ2Tpa&=FbVEk>tpTHg}cQ?~dQ)_3Z&Go<_nSCaZq`|I)l^)Ekp{MruCa*;6n5J&|u0tE~}eBf7L_4jqb>2d>ed4W7Lzoy)S@ z8jU+3V&fg$_@^1qm%hM%4IB1m9Tu;1pU$~nYO7Rop#1z`qEx?*lsogbQ8_wBFX*^N;b1YcXLF0juG}bjUQ+YV2=^v|Bo*RiKass&2459Y7So9gSqkkh|JA{hKW= zto9dsUw>+@$~WawM&;#;nJ(jX8bK?=tr!Eu^!eG%`e-fMM=~iwNb*i4_i;6IT9i5z zd&^}|413OkSWIO1PO3+N1NEk;Wu06y_9L`Zg@>8nc5<9w6ft>hrjMAhWy;|K8S|QG zC{(>sz{1?M+-NLke82Bh$5(Ua?m1~e1?9ZFOU`Xvnuv6>n0}8uoqrt?p@kSkM$p6} zDKT(8Ss~DHp(D=MozxZa&Fn%nOp+8`@7+Eyci`jWyS>oUb4bjEJW{kw^(&eVFrU|U zF;K(^@s*Bm{24vN22yU5q04!?EWA$m{d~)-%eoqRuuTXeB5tdl;!Tl;3y-{0Y=H~OOQyOea>EMIYMJ*3Nb8!ly(#0b`1HBs= z0Y$@J#G60(ogpjCSL=N60JZ@19p*?sQJtpi6VlXMt?n**RFeXPz+&G21yqvx7iVjjcfBuJAG(^a{P&{t!lKYVdW1$iR<<}WYP-`Ddn5nzZKMp@w1t-7xPqYy_X|`= zVv)!tqar?i`b3a7U17_9x|2$MD}l2ZL9Q-X`}KuUFy6XLEC17vAFswrOfX(T)sK{z zn26Mwz@7x^Wh=)kO>XXq)ZwC0Gdts9AJDnj9B<12_;hI``R@YiJ)99Mp2}K5(0vlT zcuV_o-|7k+>GC(Kw!j@E=()?{FI-Gw-cB0t+t=RYN)VP>oIMwb5tVy@1U0|V%a@o4 zm2<{tiWCoM*fuu^2Rd0@@y9b>6kC#N<*8y{aIfqWA~6}h!g@K%^u1tw1N|ZQ^{Pn{ zy~!HBjrs0`DxeylSeE_c0mrTJ!0FRZ3aR)nVU4~Z>|ZD7IIB5&Ub4ov(3M7KJX%c% zikta?7kjUgcs2I@JOSt08Qfh2v5HR=T`WS-1%)+1Y9c=h?>B{UOvGV^vOf%fF)(Rq z*vt=yzVgpg5ubLs^u6wJV(N2oGO*&{Uw%HhI+lumsZvxpx2^y@!Z({`eRq(NUmyy- zTGi4IdaWUHAb15&u@^@jPD44hy6$mBORG@~7ZfQHNS->3YWp{=zWmPTsM7QrgQdEwz zSOwGf3b$oUOIAza`LbB%y@3}9dNr`LWxuORvRRBtbg*rh3>9LOto?=#ep2ds02p+* z^h{N&mY@btzwMrVA{xVG$vbE|xOjnu)?2Nh1{i+#B?=m~My_no0cZ8(4Nr}#BL=-8 zGmGg8!RYRUmDBF=&X^-Y;PIrnR@m6B_s4*>2T&>rYcA>Q;?wOr@BmK*rvUf);y(@a zcmo-FE(nadxCqV?!t$h_m#wUJ!gB65lFlJ+Vy->a#Z=tM&$# zLV?4e%`>I!moqB`{|j{U6wZ&sc|NN%4!XvG&UN6$#|{PEPepj4Ca7TS}ygm8Sxr4!>p=g4f; zdl!VeM27_%!pp3rjsF!Y&f(8#l2+n@K7x=UMe;lD|SD3Uc5~$OTOGa zS>@>Lcs|ee46(p{087L`?jRa% z|4zDdpRnj=jeQ_=*JO?(1>$dWV!Rw2)-;$r#gKO`d7v|^(;3GT2o1rOu_@R>07fVU z^`XI4WivBFrdno(`O-_oa&`l{17WFciCsx4!T}79D^>_N3O3&9!D<)meS~E0b<}e` zFgH2=E{32PLvNo1bYcNTW_yN~eL1aF$0sK;mGbR%1J(beUqlG_Nf?bRYGy|J{Nh5( zj^LlflyN2T-=2-PQejW1Q&j+_ynp(C#SRLO|&%MfzXut3u9gF1bg~FHVkA zv5p;k@8)XNNr8TJWg>$ItFd8aw2B@GaJmyKVK~pvZ&&Q%>R!a=wST5yMj9~ed@LFJ zyL?2w{CqtR;rm6Sgj8R@{6is?6N%l@XS~Y*beB?{i|It($nzdqZ>E^C< zSe3Y_v_G~xtX>&@JDl16CuZOa*Ou+>QhQ@2u9i%;@jdX6V?-z5wn9?KlwSh06Pd$l zU)ydDX=Wp2W3<}o&N`bJf@rH=VRJ)^RBLJr0m)wj14JXonWwy$+g|D8NdK&m`oFL_ zAid?P*NB7F-?7cA60KUn=Qwsjq_$7IZgv&hepY5|L||YfnAunqmY0{0 zw~tHnZ?v0PTJmX}edKR>A3THos;anvs|aa+T$6=An8-c{Uq(H+C-|b>@ctF%!qNo? z@b2eFc0@H*{QLP+(HfDuo7TJSf6CP()%iCqfB z6rcryDzF^uRv7*!jY7J;#W()O+zz{InAfe`{rgA@6v+$q9!=*r?(AT- zc>xqvxY*eNq&E&+9HFrL26MDv6=_&`;E3-jz1*2x;sh1~<005y?>1W_|7Wep|osC6Oq%?Jk){@~pDJA?46675pfw|FNJHQps z^DYDn4P6&(dwSAm|9%3O^N{)er!lJG4()?uw1uw_%2-Ut$ek;?4CfQNiB|_}Xh3$3 z$g;uqi%e^(y4Z3&Hg-@e-SA)-a*2h747qHLlK5Y^G#@656rC3GPa|7Hgn?f=qatWs%eYF7p#N!I%WKy52+@v!JGHEo>hBKk ziANCbLq$jcaY}!eN7C0ps?p~G{YZ!>v-#s!)l7|k?q8S;ZbO!fyuH7FJ^SNF==|m8 z=8kNHIJT~AY?v!p7U>ib!rQ%OMYE5G1Sk^%D8a?W-QxTw6;G1+dtu*#rrSyDfn&Lg zwsyYVs15L%e}Wph?GqVTG+j0Un=Pe7!EHmqX0cPOSt*NAZz#R`$fBpNIDtx1&GGX@ zh3(!(R1QefVnCt-)aPc;_el)a=LG8N>LbNY9a>|x|5T+s|L;4y?Z5BV|Bsve|EFK` zy8cZjduCp$>rKc0U>ez(a@FP0_mAr^`P10a zZTe5#PBy%bb@YhKyQ>nTuOm;hN(636$M4Ew0a3KC!STdtdxn31-FCSvUA!bV=mA&a z?Z_xv8H-*K0(XyT}wff7G--{58UJahfQ)<;e zW-%T0feN1SR~Hmska~q*Md}v69e>{o&E4wDZ5;wqOah_iXM=5#d3V91>HDyY8+6ra zD>-Y2*? z^$^M^#HD^y&Vey)_6wS>^B0%@+?sX()4q-+cSn|BtP^@TAKZv z3(%7=e^I-&GgZYiQsKhhrB)JLFh#%IQ>51_l_v3^buF@k!Hi9{WaFjs7yAZx@RN@% zHI}@urPXph1lq43GCCy)lpfjG;7d+1x7sB5o z$!DED0Hbk(y)rfAb#h)7mI!H7ccE2TiqsAW`H@M78=(bx-na3RU54Kw84sti@@;Va z1d%?{NTEFufFio>DOWPZuXK>WnZI?N)f8$WMo`Y&OU*ql9zZ0B!^;{EhVszBp8Wekz@-vv_++XSCCy4(J)W_eRs(2%{p^sBH=`$vhY0fPz8*K%YX5+M z$F#KZ9nl_(=iYaq2f!;*jY}i%=l*APwjTcuZ>rFaF2ZobfJTwYb-@@xj&JnE(#ux* zq5Yl!@c;3++gHFO3ACuHQ zyH;W<5zD|PQuX)EoyVCh)Yl0C3IhG)ZwIeYen!$LL4RK$UdwxBD~aON8g2^RnoZC) zKwGckKL8Gs!+DePBCz%!UW^P6v$vS~=pbpwVgio@<>l@IW7$Eg3{ujSB^emQK7sXu z;1kBDrTLkBrR!_4=YO5(&7dVhZMRG|=Hil}7Af)rZS>W?53 z5Y2NrJ4_&$=C<0LppU+kX$^%&(rf^JcrY>$`DzK*C6V8hWA|6tm`Bc4RXLX z*g}A91QxhLHPsjTZmT5iCUkF~aax0DoB;NG!>mOS1MJq>xj6xt=t0Q*_BtW6{!B|S(N4(ge|_5IWCH*+F3o*^4X#!crzeA1 z!^(SIoCr7PT06$v+#$8G)Nw8ZxeF}Pd%@=!T#K6U1M3#5wmiOda3$#eK5>Y)uv}N?j?a<-W}XtFmT* zL|HDqXQAE;4K%earfPeJJ^Fof6to(p@n0K7Kl8eArb)+qjEhstF5EFm?^){9pZF+7 zt5*9N_%ENpR^JDcL?}{;rrG7{Z#s9MsquV%a%I6AY+qWYr>`o^EjO!A5V1X66zkGS z=Wlj&($mlL?_mT@)c=`F&_oTHoIm%L$(?^n-JP#t{|BWmf|xTWeN>8bl<(NF2c zLEYoW(xIoCVIRd{E#_pF38%Y(dJ@kpi9Nt_Rjj3OMe}%f|Hl@tSUI8x=x0d<)-2jVT${0iM$=7j8&vH+RYp8{j}PL>Md8U`vIpL~=j3o{wvz4V$L+S}B77gr@BiLK zLjU6<_}}>7OM~vokBP4(eg!+c(JSGTQ*D~2%ls>(} zImuGjvP0V42l7ao1@o%hptkE1lt#zu>QVLZ$b?{gB5iJs${U2?{NcK&k^wwP=2EWA zI>o_$)$!0WJfXPmGsxH$KWFX1WKq=acR+cOMRe}i@=aFCOUHhGc$#+P#ziJimS0i= zG~M4WEz@spJ&BV7ciOM2Q${#5w%hNHfG@USkOuLmsqxjZwzrr4r1>8nqkaqIj}yK^ z&wI;n;&h<{iK9AkSo@eW92gf^kCW}#ykrhYNhdfYXoy_4KiF@xr6dOHswB%Ndj%k) zQd%vt1!Y^##N2eIV|_uYdw!2mrr+EIOYVuNx$w0_wfy^@aYgrccw*3-uuV=7ns zlSjT63hAg-iOM+b7vw40xA{x?Vma5g7o{nEp9|EzMU~{W*JiJ8_T*K-c(%+$P}8S> zpo3k7G8WAW1cKy_$Lmh_v5prh_cL|hJYJu72q+e;C&N5+Gj4oxFKASM?rl--{hBDW z00Xv_qRxe1#Np&5sNr!0e|Sv`4{#)m%^2Q#wI|wcD^Pg}yGX(wDgCaOrx)9+?c)_O zg}P{?%ox~KH&B0P`@%%IzfZg69p$)3X;f0>+rrAg;L<%wAgmO`Njy$@B>LdhqB#W~ ziO9VSK^wgr(%SnqGqV$=V{0cQKC|zB(#q)m^RP6BLgv zNPQ9Wr$v3TPENh5GKq1+`*^UA z2!l+F-&j_{GMhP837)!R4!tYd5vP30*ietBRtfx89iPyZKAfgu+4nB`nH} z>YU3oZPa=H)*9=B$j2!rcdj19g~Z8sm|AU8b{>-~6*C;CB-CT2O9mJ*{)lbWo^~pC zB9(QlPfKL0-7wHQAPc8?M>5R3?t&dElwVsF+%rCAd{lul@X`0)kpD%SGirOyOx2_C zWYgXsehR}8lx!_#uH5IQzZAn0>bDKhP?YeGFN@n(P}vk~^`68;@41aSUVFCeJRwF} zu043BVoId1U@~9a8Cb`-=zIG8Q7T2F&;2Vrbkvz>5p6Qf*{o2pNq*>h`tmsY`GREpnezwG( zcfJl?(pW#*-6{nhKy(1ZnP_;`Y<_YMe=CA4MHWmi-+AjRmV~n+qp@sfvWm6WrdDjH zg^r$!77<(Api#rTyPm3?m+U2~+*IMcMZZZ)(J`_ND)m~7=|mQ>MHIB>%5ubOI004k zp~bD6floHL8Kby#7>+T#PRz5zx9UqU4B6Pf9Ow8qeq`GtrhRLhKD`7ayJwcYwL$#NNM1>Kjk{&$W-idJQ%&Mw-6JyNuqdP% z;VhWO)3-T&u&~Q~v+{P*2FvI~M@tI{+madk3+o$E8|g`xNJurACfN zDiQ&zV`KK2*9#$HEX1nD`;gcHub>K=ufb?Yq%Ab(dgNGGn8molWEo%xz- z4Qry9gAceed|CU+yzzrZZ<>aFnW#oPoF?Wo=hPvd`0zffi!)}cY+~(G9G=H+yWPjh z(~YHFM-}gfs9f$oLrE`Q{ducl&E?khO@a+&t4T4&W)#EywE6a2K zH_;@LZi8w`ZwF+qNAFa<$Vo!W?Ds#nx6CW{Q~W~qSFe8(X5YO2TOmtA%)@KaXjb5d zSgE{7{5q1p7&3{)j?N9hVE6WF$mxO(R!9ZQBgn0 zMG0q(pnOdKmm(Uj4S~=S?60`rbow<~CA&m+T7N)_NV{SAAooBI{YPw3~? z_O6a!?1`QXsMXD@9*Hq7AIiA~5 z1fcFd@228%bQ^RXl^uR^R$x?M*_JkbU_;Hqj8U&L#mK91BE5|j?9@zdsk;7l;4Dv# zcyT4za3A@J#|l@g854CJait>D2%dhw`Ods<)>J{Cb1f@Qsi2A%alNAAkp;oRgOy|{ zey^rnE3zhWi$8ppvJ77JQQ?#dwyhK0mwUi6`t|6_5!Ly*HA7ZKa2^&`iYMrdSQn`*mt{cI{)@ z`}eC<8_$`)=WCw1)#)&2>r_aum9673IWFJ9mS%V4$>R@h$NcQ9n%X$G*0DiZa60$k zBABXWx3zZvNcK#5pz&0mb=%6TW_iZqOM4pjkiyxtZE2mgd847WWLOo-d>!kH6aQeL z=WonYN~Ul0H^!J!;tR~o$FfnnuWL8+kMuUS^bF^e19eT|e2_O>qCF?$?w%TvBNf|^ z7tQr8*q`WYSPxQ+4NaRwqHyZ&G_=>Y4lZBq*e)#$ep%-p>nR`VitF}cHoRxODdwi9 zer045{xjHRNcb5$KF1}^!hWEKmP&duzxWn@McHF|G?>~*@$5Q5b=7Zu8*DGiB4XM} zBjQgFYmNJjVpydIhEc@zS%HhnKj=^S9QE6FDd&JFWGD<)C1R;6l>TOKcu326XUWUO zEspg|+A|vwTvI2#I?`rXJbwRsm}_`Uua=OzmcFRpWL=f-Uh^D}VlzIg@Ujy_cb&|U za_Q1JPIm6}qP&u4c!$OkYhZJc+wK{Q>dnl*ajjQ3x4m0C{8(p^M+;;63>Ad2-YAYWDk&+l7h0(Ms*9A(p}nhP z?}d5iAskUzX;_wQ>2D_1Ssw4~3v$;4C)}KDq;(IPO~RY#qxftJ>i12-`|q7 zIcugRnuY{7niO5#{PU9%R=Te(`s-fJI=q+F&k_e`bp3EesXa`cy+j|kMPp7qGjtno z^tx$!DwK2WO+V?>NyEaLMmP&E>fo+26`tTw?7o_X%{+-jz;dwdl~@w#aHVvBicq{u zKchJ;Es4LjF|pS#J!1XW26qp~pQaxlMuK^ZGVtQ&-<4AIsQ)F#F-FCtiGifm!oq|1 zvS#=`tj-SYgnJ;X_>g)U8Yk+Q!Qnufkq z{>IwV(Tvp7gqkBRu<_y6GP!FbV|W;mL<>v4j1bzy<6U%~V3ZNxFvvFT=ZErNt&d+* zH~-Yok!;EO#JL;f#mc~Q7jul@hFUHdF(K-lmD3aVp3~*;qX2U5%I|T1!BRht{~Am2 zzxicb-5X=$l|W3gsfNZzu(X>E#6*}|#J|~nc?m?>>vCyb+CnqN2$CzP@Lqt&PPPpe z2{%HI=uvSAn03HAl>rt|2I7He{^s)gLrvb^jduUk@Sa z$iJ6bZhyu$oHw29a3O+V-}$eDlKSw4$b)eN_rrNGmM<7vd*^c=c=Ju{-Rkr|F3$36 zdjgL=H&LMloWsXG|5A%2hTIBIvjQ{{UUdf0P(haQRcVV?NJO|V*PaG|EQ7Tm6|mm@ zjME+trWzXetE!kmP;z>3xbr1 zdCFpkW|y?5Hqar)B^QZ>eh5YF#EX9Z)R-v(0XC*&*&i2R6_HV6;|Fq3`6`;fu!_F~6< zLc-=>c}liG4|#}*hOs_uP~RZTV)iBpIBiTWOLjh(q+$S?rgR_xXw8~4a0sV#cGwE2 zsyf^A7!Y3gju4LMcYXGTUN!phHj4YnA~&>+n+69X;U%sPUp)!Hqi%RF==(#t_Tf6a znf^jP8HYr8SZWK1Kf2D`rsrW%l*@FmL0@z2r$reyV8>u?y8LI<7PN)vzz>p$<&K0A zkTdJ^f-UHT1PsOupvea`->1nRd#~&C0Faxw`BrJGXDci;itdsrLjKza2MDOuwfBf; z3dYI86s%oP*nprsG%qUeCyOK){|EpFDQTn(^Wip`+w_(+5QAXoDJQsq%!A%Ch?w;Q z(87d=`+%$9usP|MEy88C=`hwc#WoqV9Tvb9A>_YlBQBZ_GANcS%V(^v5$!4cH5x&)s9Q3shL zUTTOLOhp8{zrN4TDbtw$w!`5VE(!%{L16ckTcQ{;5`o+Zh)0*YTcMgVHGkdh0M=%&=iZ1_x|>J^zeSD?b8*Rdoaz zCW2Z6{1+_|Ju##u`eRaPV15)8$~TR+SE%E^s03mr6U<_y1k5giUMUKOO&016Ki`}@ z<8wJ8#%n%cwL0gz^IGM)%*$p}T}fSf<)?>J!4#BDn!0AmEg`JWQbhFrh@{HMu^ejZ zZq+p7@zWN_$iMvlXS3QPJ3IGj`&nSXr#+`c?)C1eilc7h?ui$+MwE$RdbNi7+iw~g z8v9<6{P8}Q^ zo(2fm*f5Et-UFpLlDkUnHLrREKk;Z&ba%V&2`~!5ITr)?t@Tv`Hv*aPpKHQgohT5^ zzM6Y^6EVlic&L~WK;cwX1&hb^O{F{!Q6NWEZ@cpG>Bu}7_|>qH6&rZ?5Xx0p%o5vAH=X*=U2XX?=JqD{qrX%DHMxW;aP#v7B8>G zRM0K0P@Rp_10?x_z0}v~n6AgOXgJ&ZV2>dVDo=kP2G6N|X96Bj^N$BFod0luu%U_1 zy%Tl~!-L>>4(kAL*R5kK-5e`HbK!D9J;ZAG@nChV;>ite9zfcE2owAJ`!}`nrN!1= znVzX+q_DfW9O;9dLj;|g&sfRZG)`W9dz(3!D1bQFdacshmU*x3gDR}|P%SKS|K#xhiDp7GSJgeaOyUL}K*l=W_8EA`K6t%UgaA8X6LO7E}XjoZ&QG|P`ue|*K4 zoBLei7KYC9CJzBR>M7dcSHUVIFd4n+S)WX$KcReC?Hq}@!AAw-fWhCuAKuFByj+Za zO(IZS3#=#K+Rh=p!VT0ULD4LC^e;#imCZFX0TPYQJ$!Ig@DB}5x0)5ep;0BTaR|O^ z=}nHsbT2{5H>FRT;Q>t6#NMmbdbO!u@kRzYzo<;K@9ByiCsKA!$x8SEOx5X4by0f2 z{?;=gAxAhNR9Da1#)h1c@t&HR8kqBUiITdtLDvrq8`$|S3LheYxhOiS9G6I{c-ixW zm8tK{!bvwI6KZ&dhK2|*L=-~6DLX#|U5C2SxLL1Elf141jGxgUG zi3#>mk#zOukp}2+RCDkiDX5s5t{a)JzQMWSlE8x$AOfG+>-}4QdV1=fr^7Zg>`r`L z^7e9Rqce8U|0i$F@i$3Ro2uok92)?YGWs3hB<#o|wYYq8r7><1HI2tCy+9 z3}xjXa2hZ-01bjDv9qf9B*c5}>afpt?R&N&&My`Q1Y)1tT~GAu5Gm z!sDSun3k-Byzg97AyKFqT2n(!{npepK#aOTOa=!&?$KI3-_>_SM6Pa``Ez9o$EWhM zs$i)a5|W(hdR*^&{csh=24YYv<(kD7DCikH-~`jK)IQ^0Zm!L0Wl;fv$5K*KCWix4 zmrg;7IC2%yp;(vhwVjDvp}aM<`a|(f$MP8wYXk#j@91}zJ32o4hii_8i0FBz$bi4E zfDDejf}}z#`#{b_CG0IFYL&w4?zOzX%||}MgfDC&B2nDB zwWaAvb&U$wliM(6a&CQx3_)2!bb#b4?!*4zc0K+C&al|`tP@&8=aG-ki%RWQCW6Tt zMZci-(D%J9F&iuN1}6MHe9}-`Hls@W)z1DT_gNq#)1K=v!qmy~ZMYWcHOi=x15She_M7OnXUula5B@#3`1I#P)GDIz7y1~9Qv zbnB;k=a&~mpm}o5k{8JvmaBDDxqtsYc%r;g5V=XFN%1<0_79hp=lHwL`$R{l$K!lI z=mVo^4Q4*+BtL~Y2?;+hQM&4sGUdZI>b8W^2kvniJVRtnzRi61w^h_^LD2kw!L`Y_ z(ql9b90pW>dz>8-UzS_F&(fLARxj~E1K)Tfqt?_B|=9WIFdEICy|Rd!aDNXR+V;FkZ&!g4y+oFe5+_Kx}zB) zOdpDT2X{Y3Y5EZa)_`<{ka`LJ=kk9P%bk|F*GqZA|g`xX52vE*m?;eY-q z6q`Lzh4T0Umqs)8^|JI;-LMM`p_!&vzy5Mn_WO^utMm3T#4Xk}b`V6&(_d`{e&-*( zwFL8q2Z}`B?XA~+ygOHCJho3bGKRQU%dUgLpV7UEs&}DaoF7i7#)brDcw*}6 z+{2a=`b+y6kY(KVyun91*B+h@T!r3J$#wQhVtAE(#TdptN*7*F)zbE_Tqc1;kUIF5 zdSy{~xSJT6fdKt?=I|$QS(@8g77X2-^z--sD%}gSc{PnS!9X3w47#x{E?n2n_NAiH zLhtNYwvDP$_Ej4YeelH~n>)3~e8?>XF4?zkB?P6Wu4)v)48KQ=jA4+f7MdQBz`lE` zQHi4`f{L=euqs!1w9*Vb;0(pVP?+o^mm+Zk;S4;O&rlo3<_!@t-)Y?;O_H%@ab`%% zqpwid7TTDSKxyl0_d+C4tNpm2KhtT2>6zzzHwp!N&O>a-EK>CS=e8WEZjm(+9j?gG zB6!&=U)|7&7z1n!ep$`^rp{GA!RFI~N!BYN?{Gc9zM#d^ou5|Y5F5;=>^3Ia)my)J zM6a@IGXz`%Uj(YMDy}CY!Qb`U=afydZ(;Y!zyS%#Sc? zo5Au>VsoOR12~3V{$5nliEqwRLw~GjyiZ0(p>TTiu?j#d)qIO2I8G8UGkr;P`v6IcG0THoAMY@BX%eVaBsNq_ zkbr+g2-yano0Pct+&beE5EIc+Nhd+hGF>&UBT$WOGcG{c2bOI>eD8$gWIR=T3&Q+M zu)4LjwVhunm`6fi!G8soG8`~K4DRDJhpy$Vb?0Iq(q)sTWg5uy$g0_f&v29h=z06s4$Tb6E*Va>mG3~ zA-77$7q1LdZ{hcTmlNH4mmno7ymec>+Aanw0N16%Md&8kRB)&oi&0}sm)S^h45$h6 zEgDf1c_ovD{OA5mmXUg%9;`7q{Jx2(kfDGw{+-WTT3WiqN$IgiH;^v*6*AKpW=muw zL^EA}n?dos^mDdKLMISj%8wnncb8FqzA#mUSb=0^Yj4m{&ggm~%uXr6(!7&gTO(J) zm;&RqVEVGpcfJdO*$+3Sr@HUW(SD?h@g51dHq^^5km&jxW>aQQTT5LNm8^=TBNa{2&ra^F*e;EZ|8%kz`I(ee32nsniZ>;&jdEzMzE zWmUo_JT6B?)YJ~wFMtx;3Mm-vn#`LwINlLu!|QgOIKilmG`e|NS$F#S7?HRG+&;Zy^F9&JRIb78w8m!EFj}-scmG-+BV!N&PZXlO)R;Z#^xD+m+PnB5%mP6& znYTuBR0QrLf4_2z%gyC=!MZ%u0?R98$VfUSPVP8vZv*ds7H5B#Df&(V6Y!%hzfF6- z2Q($16nR6;eKm@S>|@)HSXT27IXM_m$C!X4&(Y>IRk8-vrl?44$J2m@F?%A!>LbB- zyXxEq=|5p=R5J6D??<)|iiYZD%qdNt^MEJeM9sTUO*gN$BtE`?l)I3;JRj9iE-|DN z^a)LdS}S8sjPS|TPV0>iFqh?Qi)#^&DU6I7?DZtSRRQy?T9aTO`EMxCG?9j|ITiHn z8+oy6PP0Gnt8-idzcMl1yt{;eO{@Bi}o^9qEQ(#(AEw*3}Na?Pp8o$>BW-!iZy2%l6p)ZQ^qy^s(J9`IeA4Peg$D zY{2q%>;o+4>d1GZ){rCs2o3Tk=uy4i)7Sp#g6K*Y)!Ni_x0m;TX5`Y`BhXzw;B_=T ziWTTdV#~}LFWO$gBmHa;=0iNu;a4!c<2iTHpUL@WOi&0L0ht@Q14TD%( zTzL{<7_~?!Iw%w3acPw1!}v(a=3DzK)Ua+lI@_H5!iDr<78qOE8fEXnZ<5j8<|&m_ zeDPDhlQIk<2CIlO!Ahnk^QEFRSiu%tr--5$xNh!&ar&Vfa0*`#{XLJn;Ojl}d zZ@=eN|8tHsdUxY@I}*$}^G$NU#PtlTZ|rOhgN&)#p^jeVkhyy_UXD)dpIF}k%Z?0H zf%c@2Boy-zUcMnSv$Hyyc|b5kfS>d(XiV!>CP?Ju2xJ97n|!AZw*Qua2<>1Ik}~;!`9c7$gF+>lmxwnHScskR(TfMc4NE!W zF>Dk#48d-EPZi%RwuYi0VDa^gJ2(UES+1O4a+=-1PO94oP+g29l!+4 zrYa**57yFJ4R4b*xh&?@Sri#f^A6>yh#_Xwx}Bm!5tR&;93NG zcXfe`3!J?BaQ6>otD(Id%9fJPTkBb4UDvG0gN=y5&XEvuI+z*0G%X(Ww7&UuTGlCz zyIM2cXCpgjxhzL+z7EI>zhamNcmI_-&|gj;;{R->05ow*IB!@_FIBS05*%TjY!_Av z>gcFG9#2DU90^3^o%KSBg`VhWr)l~}mQ&SG09)^mSR=u+bpM#qf%}SLI9C|P8hFEg zt*+@Pa=38*uLLORFA7KfRzB-jDRCW)XmLV}&mxJV7OMOVc}f(NRFNWxB^Q&DN`sLj zfWl+tUAut<#qnQ2rP$(tF-Oh4bM}d-%64OlQj|wnW+pTuqOhr?08#W!)x(oN(6Ib^ zSD@PCJ~lS8U#N$KuIzobQnvk-sYozR)7I1Tl1fK;nJSZCQe@CDtk)JvgQj(eNq+Ku z*TJDQW6c8`7?D5`4zri!n2zfkeLJ55n9VueuAS}8%nIJdfA&f{Ns~67FB;4v zQQp*95Q|6BzW4G%Ss$`Yme%%wFT2s$A4I7Sx`%ILA=~*@P_2Zk!y z_EEt|@AVG(#a36Iskzb{43g7Oc9VlTXv<~*s?(ZCDrpPx$fx`@B9iUlTsf!SjoPI14IUX1o2D;;%X6B-|VyG%jgyydb%JEff}+~kiZ9#Fv92^cd5ZvVZP@&P?5vHt%R6?^xEAmSa_d7)8YQ_ za;@@pHU5`rKeX~N&BsuC*E{GnM?>)wxSe8c4B*~_Dcmq0dsk&#dWH0N zG{2zFy{lu!z2#1E1zt`{pL?mTx~YS^CgW@{5%%+x+u6O|Tx_?qef{;)4ZJeCZ`*r& z2H5ool)VNHcrP9bt(VL@K*xi_2+LNPHh)BZN>#kH^~6i2q4FT^_vVw&W7K2x$olL=C}}p z_HX3k`Iluo@y$(>!;JMGwWEHcJqw4*Ay)`+0mdf9V1dB57*U z@%vX(`II@W<66~{y zoR!}NJSq<#etBU;Vv(C$uHPf2uKuvp!9gPK{;%Y(jo|GpeeUW8NC^uzm77=KYX|TI zxJAhB149As5@QQOn*q9G*P-aKzG%U?j~%|!`y6rX^K}>BLSgqr|6G~UgPCs7)LyVG zqlw!`a}c-t;(OsPdHmUHgAUUC1%`-jwBZzOaT;&3ECsV{G7q-n^6Z8H>}Oi?3i%~$ z81m*hI*VdYXRa}ZlIa)y3fI4xnZaNYEwE9E7jYFJ&7elZW#eJ7z;g^CA|6B>&tr2N{8;=yCG~7!|O8q=4s*?t4E)J z-w_b;b&4&BJVAi>m24g{TO7>CzrW$9r}UPX8R!|8t5ts?mGDB4)q@XOXCiIo1q2F_ zpL*LlI1P2Sexru{WQ4Ufb2wSk@`hECY__AQ}Sn1ClYchP%#+x9RM zJty62^?7`qxj5~<+Sgr{uGtvJSh6_P1D*=Qs_Ib?T8L zv4`vH@dRRN{#0i7*R1X7$t$|7yiw8Fb-<8rv%by8G;)A@2dB_1$CaKTRz*r>Uvewe>+!&f%Nt1&>Mof6Q?+SR@||Rf?IRIvcK z-Z6$Uu*gWt2+9!v5`Hze%?iG-Teyk%ud@(h#Bj#{!ePR`d=UzHXhEw{{V6}6A@|%V z^76eK;v9f0kPQG*Qb{iw`srQrF zXB4|nUDXx83Xym_lTvf6OL)b{lK$N6NDJSJ4|#L+eox4EyfHF3rw z*fB0^O>!72!y1mxB)ziU+v7gmknkFfU=v?t&avgg3|NvMal7$+(z}gg#GU`UXI4X) zsWw9_YTJ=6*>nCyly^^K*zLIv*|zQFZlvD6cuS4biCjk7(17yH*~{dDxlCgGa@nnI z1ARc1hJ`k)^10{cL;zlo$Kk1aJN?>~O>E0KqYk6xp5XoKr`?=68^sku?( zm;xXwg^(p7jTKvnef^*b$yF!FSz`1=w!c`$Wh+I$M5;xp+q}Z791Q5pV>^~#O>4vH0r@D6eh11 zWQ;Y(_ElKYSM|Bdzcw_cr+u`*4sNXM9I@o7nqXWm>O@TMI{GKcht6X~^y9@!-QlF5*=<&J1Zt@p^_FiDT%Ix!u7y zu$31OMt$L|!98{EOHH<+M4*@?L#KNRKeLB|lUB->qg3-$p61%P=E~kl-A~p#EHpXV z-{1c~%)Mn;mhIN>i=v<)pn{}=s5F9fhaf0j(nxoRbcX_>q;z*Th;&JJiFCJgBh4DO z&z#46=3K{m_j>n!KkQF@0PZ-i>pIUd#{W0|qx{-{$eOQS1c~Bj7c>#ak>3=#>`6cB zN*lIM(0s019cC(-JR}{pc6KW25mld2eWj)O#%qj(8mB{xllLG$U+KP>)wg4cE_vPY$5DwuHE_Zo z7WcU7H=Ubq+`2kisp6kS?5|xZu#hW<-~TG9I8b0}J)Kb@dilH(H>@&0bc+zfFb^k} z84acV!Hpm-h({hk)B<7MGs5_04IE~lkD(F$YwH{Rl!P!T4njDcXZU^p7Ax{Y&PC`C z#h$02U1@`s4$VP$)YQT$K~Qe7szUM5J448RgKVu4xHb3P?rqvJ4vJu#{{V8bs@Td8CKZu;HCP{&YPd ztk{V(z3%>Fxs$z0``kh9g7Kj-#`JL&4}TVJkq^FhT9X8FPNhS=WvMv{R4Zqbl3`3` z-<1tS+3S=Pu1AM6KH$p6Whu!kW;nPfe*Q~QRg2%uYt8}3mvcDwqu?y{bDGz56Rh1| z#5feH?P-e!R7#_Mpm6gADD#*|nH-#L%`evKk^iN{OeOwDbFh|0t)oh;AXI!$-9pc>)M)PB7ptr(#5e*R>q7h3*Y-%tX zY&u(WKG3Np?gd?}x>Vw&>cG8|&|asazGb{HjR3)tQN^ zn)LT03ue@IIudgHm-%rxVKw4%jufUc7Iq(IO(X_%rKXuDoz7Y}+|(m(Kbl9l(1Q(p zvT^qqM7)`-GJ>pap|ydw65~+uF!= zd3h?;bU&f90Du1!AB_X%Q@geIR5zX0-me&6ZbiIyJWCC39iD?qv_sUiBrLo@8 zLJ|Y3I>dsr_xp^BI)WRiH)v?~&j(-SI8b+YRy%SLb2A)JJXL?2u1XmGzze1Mo^Rwu zR+&H}fiOy*tVJ(c#8wt9&sD7WO#FDVcsq63QTrZiKXx0zv_tRox)i+5Qretnwjo9& zEoA&_yJ&(6L@VVZ)?zOd`MlDL{q~PU$TCv&;^eCn4^h#CEF<+TaXNOYx9Ew*EZJ2` zmATpOW3rizccT{cGM7=AkCt`4kdc?ST;KYPC|Xum(}t(1DSw68%Ngo7Nnb1>GF^q7 zl1M6~E#8aGsp5zPw>Lc#%1r9VP9QzFoMJqLC59(Wzc*GAlda5cQ2%!twH5Nh6BW=C zYv_ycH0M&O$}1{=Wa*qLUbsS8td^J$@o0YK;9@cuvb)T#$!LFOAJ$Q3cHwX)@rsq_ z8!I)j=mG-|$;0BPa(N*G(Esbu z{?Bj!L%jUIF0^{w5|AI<-~4b}K$r9KNvSBJa?2)$sN6yp!H6Uz)3?&{CHWTB;7z3Q!UOXjnUdR96HSKJ*llyu660s6fGRN`Li*H02JYEK~PC z*9hpE4y{aH31Wp;G-L)2LhLr;onFY`KzaU;w9#L^ix%0W0hPdF#P==|*?ovCjnjNz z*Izj0nl!2BQG_f?MgdWEt&-$i0U~iiBt9d+BRsFhH~o`I8DXev&_4dxJBkN$xA9+y zB;Nb;c@f{tP71!+7M{o=6deD`kp8Q6YiGh@yzTG1)1M1aCiv$9oM}W|!B=KjX!s`!;NSAsl~OTkR{HZ{Y!M%3 z9+V}NhgiOMpys~3mEMB}OSt#v)_L`RU19$}-(k9U>xdNzDwn2tq7FNFMowDS)<&$= z$+%ZFSP&`NO&&Le(O_Y2m$;)BO7;&A9AUQMpBFPnWT~TvzSI$$wHLq1yh#sNs@4K; z3!DwB`F`kjOI7DB&J(B!47HmkMO=$d1_-@J)erkq<3NJ zd^xnH1xbyM9_K6LoS_K@#Xd7B6a7HEt0xyZA^TiD>DcsfSPc#_8NcMq$UZfcTA&1$ zyPo22$K0Xur$Ui5GV-yhBlcL~^5=F$oBsA=X5;2OTh4lI^6rzH**LFnm?ibC;e{Pt zE62?X?i88*#S*r{l_cfA%uu}COYJZA!35XqqK=^7_@Q@7!*DlFwc3DDqu1?6aV{mY zi-w+}3S3^8@`?HkUQ$a$44Il~-xY0e=qBEyN{Q>t>mr9fFj!c4hM|(Pe6W9}EM$&E z;Z*L7M_%%pGI)MV=P;6-P&mKLuRWgbnYDk%N=!N@1@o|UJ=3IZ&~5Wj1oCy=3xfx4 zjHDJe;T}KfUwb(n0sI*7=k_HXes8F6w$JFIo}wC;6Cg_%5O8oS3EH_`uesukPGzlO ztn0vraeY#Ao+Hdy2#4CKKCu!l!1mqVl{4a0O>^Ywb!p`TFO)cpYqYONt%REE3L6G= zVL|IfkvCkWIS8*z#jn&vKy{)!x>Qh4zzSoLM{|%WOlMf449~u@0Sm}i&a~Zmt*{So z(daM>d87pe7(pReR^N}f|N zuVS2Rcl!@hPW#{+n5FM7Sgkd(cV}+?@`rq<1E+VdcWm{|8?71YF#Tfx1setY!6OWF zKiH>l8(hflSTmd}n_QnRW$1S1!AE(3OHfpzfd5*NG>0d&J>%WOTdv&K(V96iYtLB^1i8i!NptRxe?Pi>&k=wWb3D z`7uR>5#r3Gcaj5L^l&QBQ%Aj={qaWb7ISvr@yjs5*_r6;r3ZtEJZi>rVAS=rV|!zZ zS_CER5r+iRtpJR{ccz&iq2zGVGtD|!G8^0BT@mJnQ><|>gwN7@u0rd@V??N*jl)b9 z=+$;hcj-B~{5R34z$`3KTnr)?xpaqoM&dN~|f9A1sgWHx~iS=c;jxzO^KvR<@gU1MX8TU30S+@}*2M0%j%MrzW06GGvryC_6X zM$)t9I)^#hyQrJ#JJt~5sbKu1517yV3macyBJ}(_R7zk%kH?B z+wH%F5BDPssXXoL$jHc&290IZqp7!fo2MoDvYplMWaEVqsKLHF>*7;Gg_RaoHg+S3 zm7^FN&W|Y_?6T~MKrhQWEnnhXZZ-79r;uNthGI6x2_*%&cGjsa-N4!kry`gt7!xvyjNhS3p=IrYAi=uY06i zkYNR;i`}bDT^xJBkTjDy+0M<)ffAxK7AlQ`@t`M+#%&JZfaQP(nbVn6sJ}NDhh5f3 zZVCrJ@GdT9asf>F#80`YiTitddl2zt6@#lbWqW)3(ecqXC2M%iDV8)$lGg8y@~(2C zQzSL5T=YAwBHv;<)aAWHF4SM%6d^ym3H{l30gVBG`c40@!yrvriQyu4q7?C`^-ptRXbvkpoplohdfJq=S%t00DeM1iS^B6Rb;4>K0 zg5z(is7`ZvQOd8r3L-!+5M=+A=kF1Nq5%Q5fGRw2BA_fkrd9p=34@5W=wGE?6_;HC zrVOdZfdOQ9m|ynQzFihl^5+@{22H<;>=$}pb`uUN7g^quqwxL;fbT+g3?!A*SNn3z zzc|oxIsbZ?-WwT4Ej(PH81d=T+Q`Vr7tmCqS&f>ikI~;*Tc=7VefXIr&-CDT=504A zePZ25e*=oH#uMK<$>zIPLsMuq{PzE`Xa_=)P7pg1rOer5VHB)+&_%6)0y6!kBJ7Xf z0t%DTS5pEKHuDz1hYF#J1$Ns0bp=>#=92=;iH(5pBi5i1i!Tv4YEx4)0=Passy*1Ws`2&hfbMx4^I1b0N zYeK0 zo?Jhyf9%HFP#`2-AxoD1I==~=W3N!YE_}#SX~)t!j_2j&9iwB}bDv{sG5`_Mwhaz? zQ6&LET!RW?(&cwvYeT&6Cm2KkKJI(^5p^bjdv4PCGR(ya@x`NOa9Cw_cBT%=9kzmI z4c2^BGs*i%v>HXhBBb*KESBs$JAwa#=U4UiYaGWQc`p!zI8a%{xms4=PPWuC{#Ks{=%d{g48#DXoty(qQ9D_dWiqza=)N@ zWt`U!DIf&}_(sxz2|A+XA#1gys6TnoSj0Y>|7@NeJZIdKDV^D@Siyc-Yj0>n*UA?B zemehw_m|;!wlwp(n)z+QY=q;zMZJb5k<84Wd2%rzg~4p)Ij#{Mfio^h=u)jQIrD{H zyjyP)fz|=_|ELH_nVR00Kg(7PMX=|4cxU|Kw8{v461hX5$2-y=`dT1AYM54TQVJa+-dM=E6RTgXCMPz=6yNMd+V^EPhG zFXkBlA0IRvXodkXg64=u@O=YawRhYN*L=B017QOvgX5K%Y3LhC3k08kT+M@>zyw5B zfnkZtey`+`Mmi;0$_MX(w9|;LC@vCU9{&N6(G659V~^RaHhP515#|_TVz`CHBEyk#Z9^PL*cdwqEaRdHKguuE4K(( zKY(XiS+%$4(eJ-u+Yu2F&5V%OOG>sZDGxCg69Suq-Rc%MH3`Qb>R(5@+?%4_TRV1u z`sS+De9Q_FysRyR#91)v>0yVG!WrJH-5H);4N1VQUpTg#A}c{f;l_WrzJWude&VLm ztNBd2#+GLc<(mQQfJ=+O>mm9#{f-^0n`4M^FA5`J|LM;$zYTEC*_tal=?h_2vy)rC zBgdCE0ArzG66+V4f?RKO&~AVLt3k)!vjjv3PvdY2S`03&oB(W8M*Id4mdgwqr^B1c zAda3~J7lAGI$?f46O=;0MjGnn>&i?Y7!Z)OhR1Vk+$RmZ`cc8@(-)4#l3SRc*iwMF zxrRj~&~&hNpWXfwl%=unauU33rM*SW?oQe$`~@NcKwhD@6bsnLP`Woh439mo*zfLB zO*-w*!RL6Lv)a0yFh!pQb2Gb_W zjxegm&vF^%f9a+$mXvEs{tPCs5+R>lFR~t$YHDigE3u-mdH}5r>C^7@lxWtgBk>cP zn|}B#;lX6SI%!3Zn04=C#4sBy?Hhqd6jFL*jpGI{X3YL0JUT(^Hb%M1dlj^{sfP6tB)k$KpN-dt_773(jw+u@E#B8SN>Yox1?Y z@VVUeK}vtyoY!8r4WhN9Vz0Kfz5Vl@M(^#@o=I>{jfek&jYq2Sb zm48A(pu^}=o+>~B6fh<-HM_3B;W!|U%V;Q-0X8G@%u9Y~;Hi#@d)%b@2uePmal2!_ z0^|xjwE&FMjr@Inb$6-T7iJH_urpLm{yKgz=&%15YV-`w;&T*eVQff0$bl$}1h6#` zC?3`~qJ%t+VM5F+haF(4>56+y4h?pT|7<6HC4<)7lf7LCPpnS52*NM+(Y?@dnduAU za>zr-12SIh1j6i%Caw`0SSDM&?$sAz)L|o)W~k8V)YzGW^e+TzZe`#_Jhwx`M;01} zUm-9LpaGglrluCVZMx#>5Z$JTzS$P`0Lplj0|+ z2aAh%ujOJ6`xc zLkgQxCYjyo_XcXBYYiu&PZeOcF95C+vg8Kl<>gfbv`;&C?i)_`^NDZQq3sO~ zXZuBpjO-wV=WPO+|8Gon_zoL2b@XXa5WJWsVu2Iu;3H4ZpF&COE~ z1)~3#Ky`Dh-{6WhB_Rfi&T5glsVS|6g@ux`GFa|ut5d*gPfS_`v6v{Vn(Wik{baD>iACDD4z1e;M>p;Jf=&-i!@X)Issa* zkbvU()YeAnoIRANRP+es&L{Fjs%2&rg_rz$b`k4zi02bJxd6>aJ>1{}zftew`9Sn* zfrO2R({?}L0NS$z%t#o+=XKeS$aIN5j9BZA$B0vf?Iqr-2n!qoZc zwQp;>J6(7}GD=l%k-@0>N+OI1JuWLax&9+b%&x`T6DBoeMC2^@rzm9~$+NlHSz9ka zYT+q)fwHx`2vp%?0DWOBZle_6rD*TeduHRG?!SKF*J>Og!vqw~p6X-Zk25%u;804> zr0;dWSs~}VEvg&xv|j)^BSz&2F26Oeseye$8vX>u|s)bS?vHPB8IYZ9XY$Y6Dvv~1tDL6zK0cwn>EM8Kv#F?ZhwEj^Y#V8M<0>(fywJb z-5=K+s|RBqd7)d8bH_5I2)|q1Hv%ip-n{X$D2}J0;QP<{Edl%@=&QH65w-??c1tZS zExW@SZWgdji<4uEn2+ro9xl~&h8G^~k%dyr2g6t#-I;jUS2Ijx+g^jovS5bQr=&M&+KPkR?7y9kw!Hhm_#TP1AS?r4w#5gthJW znIyHNc_|o`APFoiv`Ty|E5mC9oZH(!2GK?hCj@khe32#7(1s5PgOQ%YhU`$HkAZ^W z66oo94WMDq_S7S-0bG3vtm}S7TfSsId!3s+*W|GlRtHUBj4nli+iEBx8Syb2x9j^I zH$_lFoGDm+ zMn)>Z@l>TveXi@jg0hAX@;*f69f(ouKNBwore47Iv4m)rj?>aIJ#l?X zqnNj}4bP<4MzUGVuz#__V(08~gvUw}>FZpTxTy#yOPEDP$Y#nPH&(*#I+rSVT3&8n zt5HQg=m)|DQO>L=eD-8*tAfNl8hG zcX)Q|XTVo78b7gtHlo(#-nv)C=jY#spyUw`ccR6z23aPmS-akp)oO8-C0Bvy{b2TwN_nwth5TFY-rlGkx()hea;4$~D2H2O;^MT> zkK1TmB9p0kg%i~-Ri^PLMf0~I=w$pbWIA3OLO7U{qPI$4ocm(G82 zCS_6#HPWbXdJXlCP--C$^6q1*bpxm;ypTTNUuApS0-3s8iJh%2`4elxX<7{tXg!Bc z>|8;oG#vk!l7)$iJyw|~lZOw#YsC*m!*%V_w?aYe@n2ben?C^mviiP0Z2>e*ZjN9O z2YD?Q;Ys~RYs?R_=D+pAi23{t^Nhg3YLd%fd2t!W2s_!`*#ghYb0?#NWsyU<8>I2+ zskEJbQRK?Bo^4%>Ru^bNFMLpyn{r1J3N>fv{5FN7S>yYhot`aL#J1Acf92tHILXAk z)S@-nllymcyR#dFwy%4sRTdT~~8ewGnj3MvK%JdF+gI$6`H+qC!< zV+_b4jC6UqV!fdfV_GNdJobL}k*Ty(4{mZbkUjP~DjSE&BhS=oT>s=NIkiXomn{q# z7m^po7^FltyZdgd9@q8FPnVUC?S`IyrE_R~g}Vz*-?{3=0cpUOq^(l%3o(n+h+@r|)yY7vh>n0j1`KFv-qsQXlV)x{)3Ju! zm85nT)moB6VdJa5^(FX6JIw4{My}i#c;+T&H8X0=`&n5dRti=#-1e0Muf&>1N}@kM z#ToB4oXSdztdO^&z^r`QkJE-}_w-p>nD!WMx7FKiS>h2p++-d9M51B^ouYOP|G47JY-Z zrMJ1}lQQ$uVoP2DVg4B?8i8JAbw7_Bp2wiQo2L&RX36qO?!L+uhDNj5B*@^Fa=s8w z1=bU7+`REj5zwR0@YI?Sp7vXWjQ7(LoyzF!9UK`ArmmiypS8`d!E<~o|LxnZXyOj1 z$1kyoFNmHGB2#JP)5@jyJba0%advigBZ@tq*!+O}qy>XoKKDB~%M-BZ-(RLzdq`R@ zFx*oQ^Q#9)>oU3{o6+gHrA|xDf=_WPm96IK?aRW0ENHNTV{OEK^N(JAcgQJjo`PQ_ zN?|a)ws?;Q({L(3>zxgXFNRuN(y1G>FBgrdf0>o8-)C11qNo$fJ>C^f-SO6OmJB)% zl;{WxZejZ>f+!+vERbE6yp1Me>r-cDu2atB6q(=`a;Ft+O?7rv;o@J!wP<^A(tn6S zcec9`9rn>2>q8yh{bRXevoEn+cOKCko&UZuW_ts1%6NVh*KKHOH=RDlZuUWW#-a-_ zlTDJg7utPJOHW0UYekS23Xn^F&o5~lu{nsgK^U5pm$O6W*_*>+^6cHV^xZrKN9zrN z8b=%Zj4dQ*mtw2!KCPj=lW?j5{U^(IFzC~0B$cDi(@bh}e5yFwV$E;DROv_Cw7j?f z$}Hc#6a}Mf6nws(mIP3*!^(JH!u(j(QP@;VFk0j3|?nSW4iz2Y=wbF zqrcZ&fizx|W=Aqqx(iTmE)Uw^Rh$wre*b0vtm2!F3W|Q_=}M)FR%egk=J)+7{9VFS z>zagkwTuu8SN-rQOCu2_dD-hHW17dVDT$gz0wd+F;axr zeJbe*ufLC*r5dkZEnJFIoE-@8`B2-T^(IErNWCK^@`q^9mm3~y*$bT`+H?M9P%I>V zFkY4hhxaI)6MKBBFRDuOqPPrLLj=;VeqpnFJ4M113}t*qyB<@ZuRDt7TW_L|3w0Ad zM^ukIuc32TofMn4YeQ&y<8s3e1?$&Kc+zrJFM06j&g!8HN2yeoftPm_Np|t~NdMyp zXsAdvCX`dRUa!|gSQJDkwkt2sUlClRGOoM9)nn+r-Wh^%Qj-FMTVrH?kmhM$Q2 zHvEu7y=42^`8Z?as{XiI5 zXtamxQ@71soGwL1P98pB(C}b)+?16I2NOxXBL{!@^^Q}hOzP7xss69I3{T_HMHfCY z_}?p=^ZSq=LG+p)#ld-|=ETQzvqfIveKQVM14~7`+`_?oWwU$!VpQHcmeLID*7cNy z3RLUmfyDO}kz*^b(n$)FtHWrLB5Cf3xk*L;#h#5!`?SoUF!=95A8PN5sQ@t^T)KF% z{<83S{FC~fincR|ZoFWO)7Vj}WBXBb$f%||>&HT0jWZ~8%F&}uL&c<8!0F{@x@Z2w5!NN;+MvL`B zZ+ocap|L5pQ1@JmV{myD{+d+fOvkA1YmKn05l)w=_L2U46bDAH9}Btj`6;D6I9Zv! zD#K8C_^E$r5bplV3o7@Jt60SisSh8CW~eC2x>vEMt;eKpz)elZVY%>qpb8LCiIlq)pm9&Vp0*SaV}GRJ>Tp zA6pbRCrEGex8=sV&BilzULuT9p4lqF=;2PTk9sQet1n_0a6g7}ORAKv)m$}`)mO=F zQ98J|5N=H;MyGJOyfAfr=S;`QM@6hF(;dx9O1;YYo<$?&#b-Q9g>OpH8=;j}&?3*p_4{Uo~*lRDb*X1+8+W zADHRAmjzD{uPYwjJm1VO)C+gNw2bPC&tv;{idWGW4OC=X*sh)G%&gsXjellbqCbCA zVlo1Ug6?_LSzmf}^6v;+&Zk3>INx$IBMYAooYEjR+>&!$?;uN!je%R2k^>v}gid|> ze(7+<@eS{18F`Kq`7GSMCH+Oot)6xv5D?cbwZi1{&n)D&vQZY5uAN9e-9h zU4><)SZPi5Asbb;&Vng`m`wZ4X?EX{$Y_rKL|I{{_$fD&|NR*#QQpxdYnCv|M}^h= zu&)#=;LR0C5rdZU#(tz(JtK}MMvMp3z`769G4Y{b-UQtkza};$z72s0-$M%B-=Am0 z5>-|Xw|ZlQ?;Q5Vv?{k+!}hjCG@CyspPx2%0| zDQq=&H>;)dX$NJG8cRMH&bPF2q{)^M)d$lAcSvv92>r|f%@=BO~ zD0=&Dy`ai>UTH*MYP9FH`1sGh7doU8klYKU!By{n+8xH-h`z){!w~Zy7Ak*r^pLF9V0zEKBsIA9y7Ur?^q=a>DQPEQBAuzI*h^@;`8j-!9aPj* zsH{@nkl*G<&~Z5wa_*)56y%FQz_6`SUll)fiTI#Kxwq>!7?)@lDW#$TY!mo?h!FD9 zLE~m!$D<_aT_AXgN!ry(8QGToHr-z({5i?Mox?K|GFEa?s@T|AsG_&%GNw78i2wO( z`agdS+t*dmtc5Qn;~RGqA-BdN!;mClwXdm*`||;b5g+g^e84B0SgL@42EKIk7(N|; zg>QQuQO0{Q#tWk#rI&#-sU zpA{2BtQaKWz3h1`wpDMYED#?Gv3hUIGB`ekA^wI!@XxAV6m(r%w{;S>1Fw*;x%-R? zh=qy2g;*F8#KP|HQ4JEUoq^G)}X6PJi4;B1DS0pu3RC zvV^+=RuREbp}wibV9Ch1^l7uZ$QiX4Xc?HB?&L_}dKjPkoQ9cipDBnUX0C+5_E9p9 z&^c43;$OF%E}a!;rXNt^1fqy4LKs)yu@x8F#fPugWj}0eO6nKF$*J#sysi~paA*|zA=u#_(-QHz(vR+@>Nr9>ypfUKrnK?Cna5l& zTOySlqz8Vp2>CMlETZ~7H&@w1YzIeWH%=b}y!pBA+S{~Yf8&-!#o|LMxq?;!OsNsZ z?=Q}3#vM@y8PKk>#Cx-4Q#KS33Vm`gr%b;5puB;@!F}%@#b|}IY7m9$s?Fs)HMx>c z+NL_q^mhmiD(>=|C(6e86f||$7`ml>>l}brqVVS6j_TVxg~=nj47#{%Z@O0L)0${S zrf1g8Npg#un9X(8NE?L5F)2Rm4?pBaY>DhvLfA?_+lD84pRYJm%}=L_A6R?MM#!aJwD)Xn!-Bn;89#?Qkpm>C~WB#z& zgLbZY;@#ZWovoMjgmP6Mcd<9r0?rdR!Loo z!CbTztG`DTxAf<0*mD%jEz}ehyve}PAtjR=Chk}BeSci!`Xagt!FVraAU*ar$Gp+l+z*gg*Whe#^LLeZBVj&oi2BYn{eDCo#?V2JRmV zB0?`Ofx@GRP`#Qr14;@p*9q~knQ3qKkY^Gixzzqp^Y35SRru*9GOPO|YYF_;>V!&= zMhGy?a1D3$nb7jErYhyrK575JvR!pawUGVNh@Rg{n2^VwczmLmQoem&g+)T5QHwE{ zG4}cBS(?PknUAt6OH$0k`s}DFm6qYT>E}jI@y57mU~t#X8#mrXW|WYFs#`o7`@H6^ zz^B}9?V_2vxetyrp#$?h)175W=K@?@sGSd?UU^}3s9t>?3TPj4DCh1bseFo?Y?LUk zI=ftxbljfxC_A}utGs+UkcIY>nyy)8XX}aLKhIFcuyx`V!FolKq1eD7>+EdetasaX z5~HaVwy4xNp$+ekeh1TF#gqOis&czXt#u&ksZ>v(dBk~POa!qh{@EE*|FgFG&tKdK z`fr@q++TkS2cT$IKh1T6^q;E*>!?GB2r>L#DQwh ztq-X#^p%Eu`N9v?{;bzUS0M)^>o-n&IZUV9d3fJy>FIZfP43s$0*o=Uy3_*I$-hZO z#FLdj3Pr43Z;oT)Z;fs6y}JRm0;|~u63`w7l9lDM6|%bA!RzPTvGdlHC2%)N4?a{$ zr@qtb@$_cW|9PV)q3(Oe-qQT~dRM3j$?jBJKa7xjFZeQG+t~jhVw!qe)6J0*L>V2l zgtHW-kAOrN`z<#mr14V(MQ$bjjE7KiHdnY6*0D3?d-ZBDuyN`dGJ!ic`W8N zqjC8>Q^_zQQup`HY#`rd)7!EE2;<46qCSqAl55mfBa-|;eK&!4?uwQE8X920I%jG? zcz5dsK9B&K)j^hNYF}t-x;g)AZ2oDo`9z{@GukZzcK5LZBAL??MZ~y3DB2Y|^{UsZ zom^iVHrH(KkVkkqe-Xhp9Js*vr&2aj>VgUqQGL5^vVhkv0ALXUUTCsumSiC2B#ewG zO|4J)gV^v;wbb-%r!~QP}cEBTKg8?s(}I(nIsjiE2K_CopnWZ!>n(bhI)|r!^28x70V9 z?c4gr92M`7-pw_Mh=x-3{0eFWi>ms`Nq@y`dA*;J77O%W_&iEqL+tPr^qXX% zy>x^$rf4#xsd-rotX3gdharuXm`M~RU%{c3b{|$P(KQ(91^0?iXUBWuM32XBc&8YcD-&FCLBPW$o5GfMzzQYQ(^kd}SyN zTZ3NXJq#H>IaovV^`pC~eV?8V1Z)NS4#8*d48-*4M=*hspri`4n4;~y&iehn+oc;L zpS$&Sw%uJ}>AETprja#^m2XC|zn^x!jLUz+8APK^l4#tf#l z;dD4iI2`-(yNbHkEf=5TA}TM8QH7%$h~#}sp@6hdMLu7>2F*RDimM}adwUz{`{?L( zljM8FIZ;zJj=@}ZbQ2S555lv=$)N;;^ejpo9vrK+p{L|h_oWhQk-?u->Ou{+s66oW zVW7fhjfrbdlG{1lZJ~dO*++**OWW&KMglg*TV{`K@IRV7^*A^Jgf z`t_i7W5iSeJn@R2kYCj-9`(|}Y;R7^Cw~d=xCgFE};jRTU|r4Z4i8vzr0F zzDx=^VSnj|jX^1`pdUUNwCvVr5~bqaddf$oq3BxXlejv#v%US@mEL2+_HnO-q@-ch z0~i+gcCylcP~S>Pf0Qwe>>szc%)h0dk=~^=KvaS%kTqRS@G_8i8B$X9o7rJ!$71y1 zGXQ*4kGnm@qgRJjjB1xl|Ef8woh_e7>jnF+y?aCio>rBu#merz%_eScH&m?k-r%%O zd{%87%I5TLRs3i6O1sMvglyU^xuXce44y~WBU(JMS}YGIIOx=lJV4}yU*N)LotTvS zS`Nke{RhuJNdcKYJ)MAfZXfN^!tMe0tRb#j?KP@M^iy!h<_VYbBE&9_Y#HWBxx zUA;kfQ!vr~k7^3s9f=mj;jvtHycH6Yl_e{XW0Oln`JB4wyn%_mtfmuf^i_7)*hJ{x z7pMmC*>&;rR7+l!DFo( zL0Nq_iNmyo&U+9xIdl-DeLAb)KIEAAY0>3CP0(s2w zzx9rSUie>}X`z(LhvqKk^Xc6n?g_*k)?05K+wPvhNyMl#WXcx9hLC5HTxIFpao`e;EdLnZ)Q@u zH6miYkI@nSG~_IGv&zEaZ(^b!F-NTIB6WYZkdfn%G?E3PBoyp#w0Gn!d5 zH3Cr%))*`-Dc^9RQv6;757emrS=3k_rvTVhlD4>NuG|*@WkrQd_poLMQ@e_QX3kf`Msh#D5PFm0I79O~UyZ?m;6G%XnZFMq|O5V<1ez~;WGiw80VoY~r?v|2_lKVqyhXiX@&PP(~+3vYj_g&=qvg+#!3+LvAN zEODiI8U|Azgpj~gEZ9OvxM*%X45G(a!C9mqJp*&})%>;24$8IKWUymhLqrc*Q}{B4 z{8y%aja(FMp_9hrBGO&N*1IglhwX4k`@Y~c&>VM^@~GiIQjHfokf8qs)eg&!+>Jec zqQdG22%M&@s=JJvZt?-b^Twko$99x0Q$YntkTL zff;ychZ=cwIG7P*yRF{61VR;yAF$az6{fiwQpD(AsbxCE$%WY|Lm4Xjq^4%7u@PC1 zQA_Nx(*P5=CPR3erBKo_Kgv%U|D5q_+aoyIrE?9D08SF7YMyz2uKd-S00sjw3a9-p zXXgUa+uuCAAQd_}psndjU#<~$a&kVl(jJRwsQr7m6km)V#ITxceV@_7+>`Xa6bApu zD>qIZ@aop5JPHcv6P!5%35xaqgl)%*9id6TtIkIcI^TLTX(P7VLB|plnc;$gY9pPF zP&%Q1@h%V0uJ*INeRk+^)APLAwx&kR1G4J2+C?ckXG>s*o#UZyGk9ymv_DZDM8 zQv1Nq{~B40j~!q7CkyaDhc8+v^@2kubc|rKn7m$Iep7dwh^W)Rm)F3!P511~a@*;} z6TFL~#@V<^xFy0Q(4sov>rlSVe3;&63N3q%Z7+1U!TFyu4j=Obh@X^$N(rc^7xjZ|AZ%3--ri3BF`Noo~e-)+eo$mhIH zHxBFAU*U3>;)@T{I)!R@d})>47Nh32G~L+_MV<BJHv-83 zL=ongyvhouXQ0xh{_AkvtX3&T@%UH^V>hn8;j(Mcl|3soLN8ADzM2?1*L=76SnoG6 zd~yU<1W$X&FrQ7w6UkWa=F$zWU#F5=eqeLTErUoe4vz6INy~Lmd80WVz!sI>9V?`% z$pYMmWE?LB{{XqaVShCh5u4f4(N0LDI61Ju60-tcvLz&3j=I?mSNO+=@;-4pg_C!y zTAVeBKuRI<>eY8hz>pw$eKpH}W2e9Z2Flxh^eTFLAZlEr64K}$c>7=ZB1DgTb@#=w zFto-4;?!!L5vKbv14emu$kiWO7&*TM`uZ-PmgEGZql)}*G^3#}f@UoI2hGT)L6@fa z{_H8O|XS__a#V-GAb+ zF&J$+b@I41fFwgaN|vA~NfF7di|6Cx$iaK>whJ2KfZ}P?$T!>CMHIT6h~8JQ~cW*b(h3&YiTLk_*jLgdD>jT z*|)50SZMdKIzzSKsD!!;qj4Aeznvfn#-l1G zZ~#9yDK(oGfu_}d+46grj38(vbJ{t21oB1+Mr`u5$4`30VZ5hbz}ppbsl5CBz!jnt zJCcGHO$#$ z_#BEYn(L_8h!YAj{#4*L)L^{OOwVKm?!A9zZP{Dbugg>(8%IB z^GHt$LbDJI^96q}oREidf)A)@hlU11C#rw0pGt zZK*e}*{w*vKRf1d+PvTR@zx{G6O@FWm_WEU~9Pe8?1SXG{eWep3?>DgF$W0@sVyhVpc@kPp2L%%5#veXDn0 zBrIma>B8I^VvJvM+J8iQBx1#O2W3csb~4Ap>f>rJ&Cmbs12}TOIddD( zK|6?!$}e3g*AVq)Hoaw8HukpIT6Xi6Z*Hq2&SQ>i^n|=xLZ6ZvZ1T8IuNmfszIPo~?vePBDYDjm<}wWzBP$ls9W=KA;KV zUZa^hE=Yg*FqaQybRtPt-M;5RE!VZ~&hMYaTHNWYP_5BlR~Nm)o4PIz>2pNMQjwp6nmIBV|JHYGK>p#7G?oq=$p*!k%cbZxxVxTT} zrD_OMXBbQk5C+oVLSQ(O$XhU!TMzND$TyXh{u1(PoYy0nw1p+D}At4~5q|&97K`D)NH_|1Y3aB(lcbBvv z-HixHr_>@O7v1q*%l+)%v!C;xc+VMUjF*4*7;YGA&3n#y-&cID@3%FN&AM3oC6&7( z8E3}Fb2)#N^lTEXIi2mVzCNVvhs7_>hw(L$!`139td_rqs5|#OpKrWbJwn7rark=M zRTB?a#&U&Gu#H5(DRj$C??p>>B0Ci+t)%8VM5KRXV-i73-7PHxRqwW%NM)C5b(>|@ zH(E|6EgML7Lec?8+#4FqNABxSs)NU)_FN@!NiB9(0)&hV&E+2Ys~W~l9GBPVEE~kf zoyX@8BUaYM*!`+#Ye(|v=CG@%UI*e{I4hh*!{1~Jl)3ViXFajAEMQma=-D=mr__Ck z9%yWCAGA{mldleIJIDii@{%!W-Gg<3A-)lT+cbqneSCXYJy!!Crl|~m_Y#k(|Bh5B zf`}62#zqRw33%*2fa@yMQ6K2dUUoY^wXd{W5(IZcZrf${euM6N%!MRA3JjT&iRj?F zz~unb5-zoR*|hOJ(wNI_PBKnp-?7evLQ`+fAssQBj?I)=B5>QNhFUUw|ZTsl~7y^qAPH-|0XT zH)ZYRS>&zfdK~{hVT}O;Vc#3WAF0g+9mV?FPe2862Biy>={&1C&1dlv|J%V&PFP#4*4iQ zVekORR~PnUJYRgqW@oTef}HmWe>$ z0(TC6r2Grq9bUOd7s(i!lChsuXySCL+22N_Y2XTsyuJ1DBuI=sf>h}Tt>kK;N8Ds! z+f_dTV_pmV(BV5Drt+VxCz8#?Ko_+wu!4-B0H33@&Vdmcu~-63LceZPCgPa#q`oFw zSw~JGWde`s@^phlDXZ>dZ|?54Kn|YMbujOI?rg(BV$WqCCJXU2@5C>FfLe{eeg0=y z>HnHdrni?CA5132Z^VCp=lOB7Gvrc9t9h|*ky7D!z;uy{*=2>CS1SsSvQ;XqV_diG z*fOw0mxoRZcE1@RUS0U_F_gk1gY@QV+SA(T{WWzBw=IYbD>--BSOgyGSEQ2AT|Cil zz7+ZCajd_ujw}VQn!o!#v!@^4EyCxY&R+)JdAzZ+QEx2qU9;_)osGRnG(IXe<^JEH zB8gHNt9~V%Fz#S%A3vKkpI+5dOW)Nxqb7J|w!g)d_hZ7DbN+{7_&p-e=1N~mTY?AM zbPpAD$%fMOjoPwf{j4~%-?|Zv3-*@YJ1B2zCZxu1jZQtbIX5lYtSJe+B1x?)jb*u4 zS@iyRdtzzQt|F>F&EI`>jObHL{8{9A3KVGS|KG4=RyALCUYs*T<1dfeX*4hD%p0aw1;1m z`>CP8M^m_95=Ogu5nPzXisNN{|0904TP}%q*_aW0&r{mg+38oPURD0~5X)XihvOZ!U1mN9E>6n@<6n!;8fXYE$Y<4yCC@|u}4s{CK@ zmZd2=&GbCKBHqM>SqVK)EMR9VHJ-$JHO8q_EoO6sg}>#UMFjIE{k8Ylk47W`o^UnJ z5Hye7@}1NiTPjh~{fs9EJ%wz=9`c83J6F0?OSQG?>?y|b-!92QB|6OwVQ`1f=}tQS zaB6;kk`)_dzK%tLm^$Z`oxT=zO)V|nP-v9NQy~C-iDsbUAl-RAzkh&3v$?xPfTNyS zmJ{OlvOmW@X=*s8V81D+CH#efMy=Vzd9>V)M9khbS@!b*Nn`rBoX4u$WF(ZeUsKoJ1}GA z0TYKn0_&N}3ZqNqi{ef00ml5O(w6cTI9ysET?SUZQtmYjKS{=dNbY>!WMHiQd71d5 zWn|R(%a%}J7)}WF<-h29Io<6pXiZzCxJ@IpZYm5q)rcXVg1x0Vol72mY`MHwc^U&; zkEe^)pQR2H9W%jxX_;0slzm?6a}D=j4X5ffHZgiM67p}FdR(UX5N$iF*bpZ+%}%fC}lFVX+D zm8N$~Wd()D0bv>RIqwfh=zwa#J$|`)7fEX=aq zN`cFaqD{B7wr(69B@XQfTOels^V`*bej5f6Flp02zZfzv6Y~J5QXq_-+jV4m_ss6E zFNh)?E8q);mJ6WtQ+dgku4^DIs8FC~P!NEY(V_S_tRW(pD!|ADCH|o&T>(?%RZR;6 zF5|Do_#Z9bdE2BqhJyT=)g5;M`*HO?|L%8TX>&vY9&bJG#;5erJ^0jk|1z$iEWTVN zX&QRpFAiZ(Q(a~)TcoR-`yB;S@sr0o+s8+Xri=WGk1rfgEV@al!MDKtY)F2eaw|=l z(o0m0$@wp&^FBE&<7a9*^82_UGjup8&8X(&Z^9<0WfktaO$Ci zVtLc$CO)<$iv;5Je14^VpO|zN{-7m6u>DCozPMn@@x2Oc1>t->Z zwP=QKr`Vcv=WXt~SCqnc9B@6VGU$&kKioSFjO?tA#YsAvD_1_|zP7ODdN?6Yi-fgk zMxJ+c2VbS-7p|3*J=urri00uP@QOv8mzg?ojj;VrJio16NI~x1oG|<~OvjXJ_uT9g zEZehUmoxQ-oi75WyxUuA=Mt0HD3I!e?3kg&8R-7wG7( zQhqLN`KYyQM~d@edVGQUhT7nS>s58aH^%s0Q#YQEP881PW@yL=)$5j%Urm&k*OS6? zPn?Wp$0#t)F#o86q(?9Kd6{=e?^SwK-}4X-+ts}gM*#h0Z z-r_?l{g8+EI$LYDzBTEn-2&$|^>oY|gWO|HSx3~?Y2FzEW1`56tOLon?%R9Dobm58 zpO83m8|N*^WM!B#dfVf*oVNX3ry+eOmE>U8@oaMnFu40v+Js$w=Dv?=B*fjG#jPSGzc9-;SUrMMCi@-{JM}6bSXr0%8yr4&)+g7aDqR_{2O=1GeY4b_W+-nCX z+Kg|$jCY#fu6`&|-{y&m8u{Vnr`)J`HDqW06%MIW%x}#Xo1JbF1;)PO*;}2OheLHSZq`7wZRED}NMUg|O?m^6rglV>)g3eOEzj4pxki1$z zL$>f?Bk>AUpwoZ$n8qHfb2J2G0_pTz_Zym z?*1har;8H&8%mWv_*y%=80f3aAHzgSYfviVvi~u}WO3cNI+nI{pRG43Dx?REi@bg| z-}fCyfPX^ii{hT)D8tf^fdbb2ogTDxvhP7%~Ec^+CJtjFK5+2#g}HoF_vcQO|v<%Z99|S$`I5zdac`l zHiqNqlKb-&f2nzUe0Pm^H@3~V#~9FuQLpp8C5F(>s0=HXZWlf#T`b+0M%=IIEY>{@ zH^|%CyRi3@y3`QlV>jjr!Y6&Y+Zgsa0(?n#&zywtYY0aVcG1KgIiH@LHH|D@WX)vF zFb@nqRi>TqOa$7>%LOSIxouM~7d54Gp3E%~#UrR6QM<0_!RA8v5t zQ(LfEGc{r*<60i_mpnAeMS9v3yijN_u5NY@!pHfH8(y!(=uX`d%_Gf?9ao;MX&fzz zG>|#5npA4dm{}#eo^_kiLN-Y=I8&s6v`2QNZ@f97^QWOtK+}u)i$s)yeS7xGN36=R z0#tW=Lp~asix?Wsi7Ka-qGFR=j!(jE1ZuCa!~Og3CONZ^pvpy#zfn%K}_q zh;#E)pB%X#y|&@IS(`=EauHVg0SMKAP~d<~X5lRc_fTK=ZGPDF&%+>8co{VC$M zHa0fGNVHw7EYcKYGAIRg;B6I^R#GAYgGiqvzI*rHJ8rxC^smQahF-mP&Hu<1wB_yy zSbaXlxY26FFQ*t&{#P)c~1Mt5*DJVmM&MOHN7}T)z+CZO>UST5zWwMk% z94z2!C%Ef6*Dg~V&q`1{Dh}r0n^krJYdm&eUFMBf#i?IKxZrpYvn}4l#~198az=yy zHUn0PD7k+1>fAWp>%)~ZS5Wfk1TB`43KK4Qdc{VA;u-rj5)deyuFCFjk9&+4&trR6 zbtuacfg|DQF1Z_??TpG%o z@)$K;0Km68lnV!3(vx6Cib|JennYwqNx+Z2Es$)nqd(JyMTZFBOEe&QJTs*de^!jQ zh0$unO;2lqXbW^_?*hQ0H&ft;2DV`MP=V8$`^}=)@j3cuurx{>w^X1MF+B}Fb>BJ= zGnzbvJb7L?^erB?!u06KR z6K>eX%KO7y)v|Xmd-<77#ICw6F!}H!1&ix4zV-ZfOrjQ#1M9t>Q;d!M{l$(@8kAQa z<1c~aJ&bSCuQu85d-b|vFz@B`LX!|%#Y8q!QV2SVhqIk5b3a1xa$1fIaAIzre?}cs zD1#vxst=&eXAMSA9RQ!ud0G^O{mlY^@!7cj>gBs)8R9N1F0{ut?r62{WP=}f!0SvY z1fn<+1Z=J0`ay#v7rN2!E*>roXWn*m8%|lsQ>lr_4yEaS-D~qj76*j^SeHw4%jz1?pBAC?Pz~V2s+px3C4$}kaGoD z&}O-`rT?VFLaf!F_%TR^0J_`I*%^A(&)a6Pr`WCQdsgGKv;*r>>4u9A;rq=3cRgU> z5oPKQTDX2gF;LQH0c9EUsftdJsw^Be1yg!|a3|cf8fM&CKg4{M&1DtkCCU!C0V_0Q z-UH1%W{UFJCCE`g%qs^v@2_8&o?G>2rGqXiT49-e{VnZ=)i)X7@~!th43~zcjr+8+ zva&VWkTX{1)yz>Y6B?JI;`{gHIz=jYrlg~Z@$q~LIS(f9KGMjg9sO#c|Lf?wl9>Xa z3a9Sm1h6shLZJspXN8;qD+En&uJwdE@OLPx1zk9{+40o5jc4r94Y^)^PY46NW zhq?yCVL}%de<#?@4-y6!t(pj>Xn@GuM+r(M7rXEpdhQ z!gTSZD<(Gf;zwlW^z<|uQRIB>Cm8oWe|PB)zcoIb*vmtQ9#YPG55yvVq*>aeu8HKK zl(OM^45iO&-JQh&86X zT4b*35R#O17X*(#B~EbwLw6^q2lTovkA5hN+fnBq9kS>QM1qqV$mG!+T_^Gf*~x58 zX#(6$)o1ReZl#W!4f20x#w1&DAI?k$8hCkDp$GOy4 zchqBS8X7}*V~*)LRj*RcUd6#_YiOYCmY23LIjhASE|}8l?bE+}`SQ=KXRf@R;it#m z5IkJ?SKjU5cRw99GJF3vs|Vy@!>D1`G!gS!+`l6O!T)(ap7eBP_I&^AyLazOjHWQr zat6P*CGNoVbyd+AuYmi)YDKQnw?H#ncd{X6ts zM~nA+lsemfOaWEBak@h+eqx8kWj=W#urK^+Sfus~OEik*p!(n%IQ7kB_0)zUPo=cp z`9x4Q%~H2L>4FQt>3wZ&?Gn>oe=;6>+?zK=H)mT8w)}9>c&}q7I|ucY+blA|{;hZL zlMiqMVBoS$#nKv-yyuDDn1*Z_W4MyXJKLC<#7m_ zWH&)z?MtnupXad)5vMi&1F^8__99)B2%9fK^+Ab<{r9i}!Lbq}qMrp!*zTtR_4Q9d zH4cBU?GXF+Z!WE?ubyQFu62BV%tj>=ko=~%aU^<`2zhE7SAgp*6VV#y4(6ONmn7;S zxLUyA?B+IE?RZ%3BoErXuim_&m6Vh$ShfWFrj$)ETawF`?JIP1jd4A|&52}6K2M_b z^7igcR#%lwc+&DAAU0br`(ngZY`5Wj?c&h`$H~76i_<87{H~hqhDv%*HJz6 z3$QLYNX491m?EN+spf{)%TL7(pjq&7F4A6mWw*XHGut8t8w#t=$j!dAmt8)gnOXA1 z!f{9n$gUvy= z&%vS;IUxk=y)vBZzk7;c`ra3{ZKK!IEs`hT-@kBph*T{24jSGLH}SnBYCT?F4G{$E z>?qq6uCL}z!ZGZ|^>EN$#l%b(4QX868@=>&4ab<(U_cb*99x>JoUK^K31;vzuHq+q zJQnQY^bsWru9oUzV@_?hF&| zn_CY>im3zW6f>@&fqHlAXiKz}rt8ZLapHqCLyBAl6FCZ&I(m^*EB4r;va+@`aizS{ z;aVV3L-5&+e|i`RoQRHo)dw5w`oGTGfdWMg8X7jb*J<^GgW14%9Mz~`Sk#k+1@$qCZl9mkcv(iHa z>xFj7D$cy@*HwgQ(QJmoCS8l37$_{yEsDhtZez=`NV4=R+{3k9Mn*>3P|OuEJtRcT zS8v>C?d}eIyVNJN8^Wmm1Y{U=1BwxlR< zmDjl)x8CNyfBIDGE?h{1VGoG+1=emQIS+S(s(B*3t^H-ghMW1Qv*zGX-Fb$yT0G)t zb+j|_c&{zG&Z+AOwRp^P%MIP{&Jo8|zhGl869ze8%eU;IAm^r1`lhv}?kq*3O7g_^ z+)a)&9UlNH-7y@2i@`jK`B}s0dn+$Ma*%k1kAG4Xot)oA@X%LYv-}L7P8JpLw@RHv zu%^EP^Y7OSKaPD!kcx;l<>IBI&CG#p^%d_!9uWAFOyqC+#3YQ$>CR_GuWmZg=FO9| zlz9WT4ISR11*1RbS|ta-s)Dmve;rvv7=OXbiYRzF?LXk<|M?;GUpurrut0B_-~_Km#zz+6u`b3^UwZmRmG^H5?cwp)K5Y$vFqYoQab_kY zR>Sou^sxe~kopC>eGHdL zV_e1dA?)g~yBxmShwS_;d&rE3IZ2a3rEIPKu)j_7@Mz3_`4viU6m*TG68Y2p4TWy3 zZ)>;z#4Rx&{EVzk?1qS_V_#AOUc{|Y8j89tGnfe;Hk&%TgxVX;)t|80?5QpE#P7Zu zwg({;{jOzD(Kf9@1Y@of>Y|$P@*U#{a zno@%cp6l1BY_+qX_14Xs^}m^fE$4li#3Bps8wRdA1d)LNEnhnrXrjW>=E5G-pbI=9 zRKI6lz_4Er?2)?P5)an!x8|Kc;;6C*skAvD3)&NzC_MQoJ`BY`_|10YH(Wn{bg8sm5)>7stvT8=+zaYYjp&KxVxoX;5e{+8>(^U4Ixsyn-8IEyhw`%2vM(kl zCxe_95kwQdkOXsca|eT|I=}JYXP+ra?YWKQKlJC?*w(KBBfai8pYHf4mIoU$<`dO0 zP?-wvhzzRUki!l;^zj~&#<2U2_^~-F^z;_ttbiBH+pyxR{~eAGDMHw~hF=k#GE~1e zm6>24pwp-((sX0Fr*P~B_Vs1YbTg#lpXaMRz91UQd7VZw;j&hLL|z!J%$)?+LFJ0C z`m~Cr*AjPDn{cM29Ch2^QPwDfyt*&*{AvDF`O8q@>y8D2R@H4%_l2;yK3pTIZ7SmdAxc>f`|A;zN2eFU`1q^bubV$YL7JFQe zvy~eBocGV}3Hjp7tR!x9DRsy3we6AdEFBsJaoG}iIi$~(dh0D`9@hQLuw|%?<;7G~ zRHT+ncpn)_gG-3L*q!)Cg8W}8r{&R(GW2garV1oMz#LIR@z~`JWV-c@n(l@>Q3XXx z4{6hh2I`plGkTI_`Uye4ug!R6b=9i_|Jd1Osz3u3xNqL2y&5|tv(2Pd4HWqUG-$Db z;3pQEQXVG+nyJ->kL}jS$P5z;4vuDLm!C)XrFTS8UV`dRi}ANvn7M_f^ZDsB*q!RE zR));%4i5_L2CY$Z*qaENjm=T9`Bwx$yAy#Se412Z;7df!Xsyj{`}K+Wfu&+puM#LulilzD|bl?$8u3+GyWQW#E(&%G_lwP0tz!q zFJ=e}FKira;z7>O!V`mecEa)Hv+UpYHI zlY)P3jq_KNG8~u=LdA^#I@>Gk)|}#MzYRgA1md_;MSd`qXF30UkgG8AdG%vd-anYX zCgQ?n_GFF6_JKsqfIkUK9Mp?uV69To^I77#9X~^Aj@sN>{0IX2&tSv*`lh2#sS{K^ z;}p!sKz5Q6MM%upWA~Q6FhuRmaO=@d_KtN_z8OjRDVY5RxiFZA(gL04664YLz<;u6 z_u->far8PS*dJjpC&MN@Ydv@wO1#MT?g_brj_CsT7*C5# z@fIDQ`Y8bqM}bpRumCx3nq0@{oz?yzqkhBb_4PAjoN_3t!FHXj6mri+x`49Ebx*of z+~{AqbR4ha6G>Af|id&}`UH8125$X@r04}$Ja{c6vL9UO?Dg zNeS17W%?s+Pd@Yx`>V&yi|Fn<{=~7<@E=#)_wL;$|JdN69|K*okCa8@-hxk&Ek%(O zm9luZZoDo#KQ{CgHm|yQ?+{hIrIBhmrS>js_$g0v-p1mlnXN0KIm8ufn`~t?ywt4){6>rw82m-7p_A8qiTvQrNB#NzZwb$|*oI?X+&R#Qx z+8?`WcPIR|6Il1TC9)wl1Sl+zC#81HHBN8 zPfZQql>e!3zhMu=x2&xCKR!95+mj6B`2Bb5(gUTB9$&+7;m~lLDbPAV@Q#cdopIxo zksD9^s%Jvu&j$Ef-e!)Z{KN0YITM-LGYz{i z&^;eRL#a;DuRWvtJy67Fo}Vv%Zbi7h)E~-drt;p?)9S6Y&iAxURI~w>Su#cMZzIEL zySeacAE;NBz==eTET~JfDI>pp8C@1N4dhSK0SegVcBfCYwP)VG8UBEQL*D!y2|){` zX081}q0n}g8EKsm-oa)59OJpMtyIh92!noNr9ui{!;d*<%-I#Q+u3gB&J?J zIp35ZLcTZ zy62NgQAJ3WAO!TbJryaZiwrt&5m5(+FKWeGOANn_C{s9^cK?#kCC`u)Rilc~!Wr)|u}=4N6KkNVDY8)Q zAzq7#l$&tTs>ZnMD6B-=GB&(6FR<_D{#gu*W{_@M{;#<%M8jpIRf+j{g6sn^>(yq=FLGbn zfg#>FbGpWvryHQI2yYVj8NpZ;s^^de@+ALmG7aP&@+a=CzJPiS41#l;C^8~qeWrxx z?NRoF>IIbUN2{O4N)KAY8G=ar5nKdK3dY|ZiLSi=&_bYT@l#I>EUf7W$%b6iURtf+ z2kcp@dzPLF7b!&3n!Az+vMn%^r|`W`H?z;?0MO=#jiGzIN@l}3l+>j8P3kpIXF7r(k`-GKXF7GD?9zv!@t%bCBVddvBX{q`Y&A#O(F+wN7o=B zk?4RBFp9&F5iNif~fi5}qo;Z(Sew(P1n}_I9$;48_uz-_u zN#J;?G+|5Dk#FaIK*QURT;CJofW00Iy!wi&X)TOURa$caFA+-V;=6h;^JpWwq7sKV zoW`ljRIi4dl$;e_fhbIWJ0kB#9A9L_qZk(5Be_;5DYXA(D+ogKj^KuPZ-E}f_izS4 zB%{wfKE`+XiuU4_E_wI%sEfumIqh*+PV%o!1q$dQd7LVL-#qDi*_*Gn^W+{I zn{!P0=t=d$gMii3Q>~rqLw93?G8u7mZG7;l8G^;#z`(-5L*XpWSI9pIE*iVIw(cBa z_(IdWZgu$b$LDqFoV8~8N`;#J8F4zoCc1SA=X}>oLQ$nmT?qHFl zEIPBQh2P_T#8&yM8#`vl+?V7ErHS!~+cTSGYM1lm6!CmRWTL^rm*mEbfE2NWYlSN* zo0P4_Y#Efen-mt58j&SN{Ts)PiC~0D3Itk|3B$eIg5MOX)davE z`lm_65`xglLj^#}YPhq>4a=MxafZ#V6a+YVryeWVN?y8{?_ z1#J_x2c>k4#%exj(_6VXG#I+hI{5|mJeM4fvg%G!-uf}{jJr|XqN`KD2qL9vkQ?W66((`tOlo_w^zmaeJx zX9>H?XE$$o$v(dNgtro)#VY zQ#X>-c*eAT>)S;|Lv@zF(hhCS^UXipu{o>Hnmm%Q?$pO@=OhXWw z_|*E(1&;fz|FfkSV82Ro^X8vq?5!U7LqS3t^e~z=xp&00b%g8lAPG-heB2#`>9}8O zcu0SHZs8cPLoIFL_{aJ6F^Jyio8~OOX)}Zf3xTc65lzL7*%#L_nqu&T>jL+L7bS1B zuCQ!N>Z%1We@#fSKKuQUr6q7HYrf80@42>GHfd;0M?0&oYwz)#PGC*IJ;%@PlX_pC zojO6@9HcmJj87XJ1a^C^O0d(303#iKo|C+xn93xiHS~?i zuVAoQKGm#rJHbSqNv53rbru$G8im2n5^*l2Lwz2VfH=*RR#Y`?>9!IF&$qEB<1*`c zXY}`Qj)k*zo978!F;b_jVYsHMKOv%;8a(^Mj5gxYnHxdt;021V!4o@a5rP}DQ^-0C z=j{u3w%Eq_>m-^K8(*|u{hI|a@ipL}FN51!9z~g6T%?vAS9s)=vBujKe%w3G=yhQ6 zd-5Bb%|dskHez9CZFdxVoZyj)7c_9P!PqZ@n6H3h7lkhnW4Y07VjzRW59 z7|(g^e%m_3n+GSruvU=Jl`8YP@3R^s&JX!tgx~Z?oJxF;Ykj}<9s+4WU*+(@(_M}fnhO)rQ z^m(H$QZd|RLrg?y7z?vbAZkr}|3Or7+epGO^x%?X2@apmA(h}(a>)>8-LcPb>)%+}y`G2B zT?@A#h9AVRp9iy~)=mFZg2+8NLk3 zEviD*h6Anfp8v5u9#&zb(J|=71rKji@D|)ttKh+Khc>drcshe*(SOdK3Tpj`AU?avigR%@l%in|0lv$_n~-H#dr zo+32HbFO?`su1k|5F%ePu+GUAWob7Zx| zAI0Yal;ZQ!e<(gf?*A|8X6R5q`@aE&zw{b9lCXe@gZn&8p?@Y0LdsFqh!{9|FVm(M zDm}DtVuKGG{ZsGMIH>$XyYv5z%3pwq1CY0Na44742G>E{H+isxD7yjHnuEMQlFzqL zBLFXBPh%(1LYUL>;rF3DgDC?ItNX9$z&W@Ox?_5*EzW;QKHrM`^V`6Pq5)jr4y`y$ zX;6BNyoZs=L{i;q`bWOm2PKW12E64C{?J;SsVq(e}>(T~atr|71 z5!*$Gy!+<^G_L938v6BEcik9B`uJoru#PO#>gt{l~O*&9D96YT0Blvv( z&z&IuzwlUio|m_2Gg}mF3XV&;51T&0J688kp8FVnaj^!)D#tW~S;RYws*#W7|Ur`QvnT72WZ2 zq~5rVWMh7Ny2tM8(}gb<^d|6^hvOQ)`t3L07vBZW%MaMOetTwvq+~y65I(l{#onKW z>@4`|#!jh~i`PJb*UgvXnRTR|t@x6b?NYCcgB#CTCY&$h#n7*=CH>;&S|ru`IVVO@Ej3g3^C*P5C8b71_(AOVN? zcyjbos+B$47-45dp%H@K3&F)s$lz~cn({V(WM!#B)Mkn4cbyztR)o&9HPnV;YLYm&!uia*BkFdZ%~scD-~;JNQ`6zzE}tWyzfy-N`{7b-9o{`3#J|7=8l zfMH*|rH$Hdk1d&FwK8i-S>6gsxuc%KGTz2`2klxxYJ2d-(w&HNW2lAZ=-t>b&!vr! zz=e;(K|$2zSAf!f7c{phh2*)qCZQe*>O>ipuSqFfJf& zl0M?BY_mwYqnffbjmdvWqGaDo!;ga!m)W+;K$i(`MS!if2(8%8B#un$H4|RlTh`vg z+o}U5A+;vi4qVvc#`(YN#QTe?S@O+P3DKE`cfJtD&-mRi`tDp$;VR*8k~U)1@cZG9 z6y-6AqVzJKCF|H2(Z;#&o?tZjH;Rv*e8X;o`;Hpf%#PXbTIucSY^(DKjsy|nunv9k z{_^_%!jXdLHUBSPF6N`ZK@HZXE;D?QDlN(@kh%N2Ob%hwakcfui}W{ly+qIc#pv_p zZ5DfCCU`ZZJOA9_)79vD?)$7$f0`DUNJ?iFOKWB~$@z=ggyq(n{aNKKXSs@ky(S?6 zJU9f(vM)*pgjEZ8TZ#4t%p8a=YUZ<9KMtyz{cU@2pr{)>kzAc(9`d_!eMvRuks$$p zBq$zm^X|V~-Z9JC3EZ@qy!JCEMrTvI%lR=rHAIawOV|mqcLilZ99vKDfn}d@7iO1c zLt$F%y=lzq{czmR)IzyfD}G1i$5*M(lOGF=d3llz9Ca(T>v&Q!n7NFr^&%8WozbHY zN-HkT+Rgn&o_p zj+bxRtrgNAZFT*ofiU~=3okw--TFE50jIeD`+Cjkn|!%j@Tg85<)`a( zlS}M&aPn(rAEqS0z)!7lRu*T6@NuI5qWuLIHRa9wcVI}MBq6mU?A9vY!CUqU@7YDy zm<%?KOA$s`daPeAhJXpl>rM7M{Be22C2IYpadOuMw^B+Xw#hB9H8nMPd3klZ`c?2i z9%A(5eOpznj^b(Z_;XU#W6^lvX?UUY257Q})B3>AoOVq|B^VDR(lFe^qP+FdU!xiO zm+NIRx6Qil(o)&q+JTj}^$S1*Z4LCCy2_Gr9LYGqJHC#qPn}2jM(BXTH!*wUd z4^wMTo^-n)F+*p697E)G@zNzc+hZ?L(>)8POMn&j6q7i9OX_=HR~cSFA}E6#t+4E@v|$;)zeZ z;ZrZRVmLxY2Nvf|Z!TSCcihZK5rWt8D2z$?8Ui}{miz1KAig8Grt%F{Z7mr}lZ^MZ zGvmKF4PCm&Zx62isdsu?uZxLC7kZ7fzn^?RCI~vvnQ7fl+^2tAo#~jLok)d z9+&MKZwE?LS(r{GjX72Ln(orqd5}2@1yw6BD>VX5Sj?vp;?V~>2hw|F-1YuTuSYA* zNI`$4wat(T1g_?wZ3uF`D2Dswa8mns@H?*$-YB5;A_Z?fH29VC?*L3*WHW63s#Y=X z6+t!R!$~TG^w%h!ctQXC({k#+x$mEa%qU&&atrFds>MthGZelKa^qk=nLlh*4gq>E z)Q~<3UtcK(cQcfG>C`0O%lIc8U|M}43Cu5{u>UM@b<}7qjit(EkJ+SK?W=z02N0Xa zz#+>}=pvEv_7ok=QCR2?s)CxD)Ol?U%7i&*C|7^>z>3{s4BumalHpmvevSz25VLjn zT#w$0lOVhH;;TTucL&+;2XfklAXS5wtAIVJ88p*3--%Mx_9teuUY~IM56HssG$tJN zaiBv!d%8S0Vn2_H6~8_iLc|8$#7~6&w4~ zUC$~WZ!Cf|w_i^ls(i%<0;aA`Kn3M|TvW^!LZ>S%?9LJg{(_f&mP#mH%SY~Rzs6zD?zrhjhxvH41gND*h%O5Uok#7*r^LcNYS%yfJ^YM!|GIJ5%_=Y3)#p}{@v zlNgDwMwg$5O5XKI!I6KGEQWLWneW|;vUAVU#cm7U{Dd!5f6@27)+M7p9-`pM5Xq~3 zn3y+TjjeYdYd^Sk(b0v2^8T5-=7)Gbn}q}!^X>^}g9UKfMbq2bYaJXUi2ap7zbH1B zD7$&rb8BmFR$PgwYH#%c{qhjicsMv?FArXPbCamK!Skb!Z>12t_UC>4lJPh68yZ{G z>a08;)+#Gix?CxOQOs)$GP`sCKu zqSSDG3k+SG6g7LOL+vx_>@Nxm_K9YQM_&age!oaYJlmDt!!q}&$sb^j`tHStciM$c zT`EUA6bAyw7K8&{lIn2Y##vlgEKn^r=y|E+`{{(#(ZR_QDtrs)B{#XaxJ*kAK9;gw z^9*U4N;cW&I&e9<4Xq_yq5YP9iUuRfH=$r8j+s3_lV2lhC-NcXQ*sK?Rbb#>or%(QKKP^OvHYeWz zdlNpp_~JeXQHEmyCymh~c6NMs_jAD&@&5b#{QNVD=R07ou#B#knc*7Ec;cVFvO~pVBZ$>U}@) zx_z&?w-f}*37}pDKOuVA4tMYU_0u7{`4m_n)X!(Y?4_#(qwBt@I~;HT$=>su1;KqX z9^$nt5=3UWOq0m6+0+3lv;4hK0*&fuY!N$?slIoQxB(Q@4@{5z>%Ofmvel*yb{70B zbrs3~@R8BGJQ<3ATuk_bHQ?^ zQOK!w)Dx!cimV4oG>~J&9=oKRw@67rXHit}KEXsau@IEGM=Bn9fiE9Q$8&FWyv+xX zCR^i?2bc5POr1dzIO4*~j(7Ff%cf8YA21Drcf@UX!hJeP?yvoHarnt+7%UR@zax^Zv$uq zq9GUi)l%<7=frhH3f+;lR|)`&lop_a5;29}*6W$QBp1sk1&$ZsZBK8aq>ZR~yC3cl zf;lSCe(Dy@pN)^}DrTr$({&jkeKBRpr;`1gz3pu@U4~ntoEhVL);+RzFQ3M#kQ>s5 zUzIXj!JOh}DgRkFRJDhPX^LUsBBJm+Kd%CY87<%IaI%LcvGFS;@?U?2?-Wi9UeIp+ zB)b&uyxVjcHYK#Hl0nNdS7=(jV2Szq>K=6XBL^~4TwdQSbPACa0*2dQNXJV2KkG>mVyJB0N zHjkz0x<~|5R>7h~NUO4EC}7+mzO$hgU&OF^S}M={fH0vW68kPt8p-&b$+q0o?uIi* z5y4T!$5Sh2K8ez;kqoW4d+MdeeH&)sNS4x5~VLVcQCoZY?7- zoT&tJWjSOd%eIoCE?VW*o~^>d&b&q>HWeqrx-O=s%=bshMOz}&-YtDeeX@}zF4jHj zFDfc?v~9!q^eY0bDA_UHDYi=niy3=+m8Rt>cd(Lu_AuhJ#ajF@Ok%*{(=XPr5zgtb zva+HvlVEsXH$J`Nc1r7|`;NixHnivNPyGV-7cca!Syr8u!=31Z8?@vP!RrKa2;Hv9 z1cxtm{y$A^k3lB<{*f)0c`w>)A%{(!XkbB?$$D~H6i!#!txEP{pQrE9sivIB;j zWF$Dd-qWM=^T07EZY^X_K93`axM;7>v$Izk0sn1k>i!21XJ09?fas#9DcSok**+ z3M*|WLEpEh?&~H4&-=NMRQ^es8rsD(?tJ#^mPv2#iW$&dAEl6O49`@|Sf6Wk2OGro znO0RUlTp9&vQfu*;WO^hL?pI66E+~|n&2wz4v+sAl1Cq4g-;&_QuzM(@-!D~4Ua}5 zJ)8v*k6v9}4M^C2e1WFS=fN+B9Mwy!t4bh7EEdzxQe?fuxFYycZkw5`hD~L2i{rD) zAwQZY4)Mc%^Sc$oFBgBs`wZ%ME%+wN`$c>3%ni7tz3}CHD0`?b(^Rl^i zOG?$(LQHZ#=o@f8Itg~(s}U-XI%26Ab79?)^Jvu9i`;8A4V}Gr|Nh3_o_tYh1?_B~Jqc9Gk94K1iLj2(TN}Odgk>$IsLCo_>RIO-K|T-E zUhKVK*j*ix-ynQO-Et~myIP-NF?$9niypqe-2j5(NG=K!y`RjEg5IZJz6+ZKk(Mky zZD^P2P`Ud+ad|tUP~AJTqc}DO&;3p1TWBRCczg|``t^ZxFYNh}8v^tzKPH~de=Z*Q zYv=h;`E{^HklTqAqvjpR!@K*W0VYt%cTQCxuG_DTOvDOPH6LU8uQRE<9?wZ$*mT54 zM5H%}7|4W8&qF-dZ|Z0P-voLZS;~(1J5EGEm!B|*cP%DkEilJ?Jz~zVysT=v!IPA% z|1-yi!DPVfs*NE{{B5NG7=q^58=^r=Q_K;9!5iJ(*di5N9H8ZarfZoqOlpEV2x1|fJWACbQ+7CM$(OC7 zc#bH9q2}i}He3@bk&3r6NBh^YF1mOVF~}hc9Y)>sY8$)WGd?{->W`BwftT8#UIw@C zuP|?)w0m;6P0NUTBh||&sr`CxzY*?N0AAwn}I`14a|D^1_s{9wz~c>+&(C$ zipVD*;FEepd+%3OcUKH70tyQ0RJ<&`9I_0Jvhb#`dJG*htPT=4FYN-eRL6WYPzm(| z`=RPn24}YLu^`H4HTAOF&bg9maabXWDNNsR7nfAWI?FZHg-@Rog4CqU@pBGz^rt=r zgNUOC8kB0Yb%wI4`HmMsjR17D9#yR3T;AN0?slyeypL@BF+3=QzMcl@Mx6)>_io@K zI_{OugcU)%$%P3i80SFl7tBu>j2>gkoTqMSv>U5p!}TL|^MXkN0utMB5H85I8xy9) zJLj~!e4U4fM_1&PcABhWFU!6A9glt|K})-4u%n_?@=BBF>FH4q7{e3S4<;sql!VpY zbvS97>GVf?G*{^im_T&isAJRxoP%H#!r-1_Cl>kyc^rwG79qw=6`bm+Inu%ZlbKS&>W9;sr05Mvy0V%0Z#!d1hh0KXJ#;8 z*XfO~PP7;9NrnjSDU^2Q;&0&R?X6qNcUKq-_*aOuqp)cbWP*2oiiyP=Ks^ovMYpw! z{Mrl|K0bT~L?*czKVtOt&CTj;m25#uB;^F=yEPDUy)M$g_)0^T4mwQCOM=5)=(brj zJ%k9IXw#pCQCIrD>VCCkLUBN7d_<+?;u)Oka<8F%q%pcL;@?UtY>a^;Dn9#n2Ijxc;v}c>;Brt6btF; zw~;X&)Ej7lZxa;7JNmUKe_K~tE5FRFa%n1fp_rD|m_@t1^!;tsP%qi&4e3S_#-|x~ zcV3`#oyVOVud0zkx($S|Er_(MBdOO-^&c`xyI#7N$a-V5vvYlaz?ezoI~H~0;p~Yi z0WN|N2DK@#i+q;jmja`C5O*77F5IbI0}GEl+!EB-X=$&MLmhbUhiQtQ=VqSqNGOwX zR(JV*BPgA6|J1IL1|^Gq=>#yLkRG#o8tz6@qu8|UOK{~1G+o@1UVSzuq@jTUe%a)- z(`eZkw+R?)KFia8S4pfp0_I`B1ugV4ZI3h(8H3+DiFea{Xr&W}9qW4L{XbzbL@{ZI zu9~CEgWfRI``vELHT?FWFFw6%<6pJg?W|Wx zkHZX)uY6~}_JhhuM&dT6o$_hVs@F!7_fmXo=QUMw76me)!zSt!-(v|b21R!y{{ za;LG4IWF+3>XLr+g>9J+gBVBjC`-oL@GZuiZ1`TDr2wV*u&2*ZJI>ICi45&pWv2vI z0!oh~FR?18#d1Gsi?(3QIZQBT{jmu~mWY-i`>W)pE@_=JVQN=!wVo3ic^7w7kB-G{ zcx$?++6YG6St1(_vZ~+NB&F{l^++SLFYv9JJfR=DKVh4Zn?*eqHQkPDL@QINv;5HL z8eKYR>Eo7z@IYx^UG+4nv0|*TH4c88iTPs{9HKf>x!eAvQz0)I)PH0+i`em`4}6=N{?e17mK**|ngo%#4o#6pf8JAkL)?E~ z0hmTwFS=seuBk_E%*brcb;!5J@(+R}4I28>bT*S&QfHxOS3GeJ*q`5ZgJ-Q0#zs}j z3|TsaGy|H#=q+K-38E4aebA`%qCX9>mw;BMFT}cz8E&${-f=~~_qWh)Y>{)ZPH6~- z;rPvYO}xS4O`3o^BSZZt^XBx1r|3o8@AlQ7iRjVAFY?5(0cv|QZp>jKq3x(+KwXI$ zS2QIq!Ks&R{)$fa?t^aaCe9*3)}-{yZvq=o$F9m`&cE7q?z*_ERz&AznXan}!d>OB zSktQc=*XU?LH6sZhf75aww0=;t_}tU4gPum*B>gXJ}I^DCYkF}|R|=2qZaquc#GHurJt#f;l-x64qGEbkV|%TdhNUk7_{jXKBF z6bX;w@g~IyTHD#I4mH&9w~WU-abCJTYN;jWblTNk(^fRvcZLL~u!zj9){MS(%0ZhV z24k4nZPz%5&*&_g3ho@P|C0k(OM(BIo#>W338R zPKP_9ObkP(hOMw9&H|qF*@YQ}747h+m9?M-3OL4(vTk#0r!{~s%kxp@`4F?Ce-&ja z)qTe>#ctf6#7!lb*U&QL }s{3|yiElr*4qG>CDH4E!%$}&CE2j8C|O&hE?qAUCuKDCJ=uOkGQ_^lvT`$5=%F#1D6t}L#;=YpD-ZA%0(Ct9v z6|v)q&i4fm8=iXJEb^D>yG94eeid8&0aocUn+CS=>Fes*sMsxX!U|29VtIEF91dfu zmWnsJ(X$o$LE-|J{*yQYUX(xngbp@pGMAeuKc7$W47c`N*>nl{ZKgkNnNZOjogwB05Ck4uf||sqo!Nqn(FmuPK)WUm{v_W*g5Rdc;9fs&$fO!4&#LQON#n z8uT4E_wPtys1Ss>;ml}-Y)clf{8fJauhwc9f0~&DQsI8*{%U51G85x>WA||N2%IGd z!JW&RR+`Z!2zj3}%oHm8#r_>0nHOmHOE0m(IRX#aOP4J8SnEJpjBe)Ep5G^VM(R%( z;B^Dwl!iv^*KBZgsf1c{auQ^HRG5QLQy=|)>rHs8#W!QB*K{3hxS;O(?c2A9N$}oU zHeAr~v3&Ik#B_wVN!kAO%jJK5Ipm!I6`nU?nopz1OAE`xw5p|(mG^GqVo$uRvmP9N zo)i_Za{EpUM;kH>%C0=k74V&sMam#vvvHN`fXP=$OzcVgW)?KDQiqu~4IbZ;|0`@G zZQFk;*nh4{yu-Vod8u6Znx(8!`SfEZ+E8mG2Vj6}z zy_0S;oP|nl+3&Qpn*-!tA@8(l#E}z!hFl|UMrxr6nV#AS%Xz8-TG}s_-~(;RM=jv< z6iG3tkm5V+ROvGYp7-wzqbWPIB?tNDFZbb0P9DRVbk^v>iFBZ%1(@K- z>{R~44gQbW%BSly4FZK!M+3k5W2eWv+j;{f#pp6+M&_NFYJy8-y>9nYToHQ`^ZOeX z8dnoZIEqqVU4~50Lc-?)7Hx2tiQ2iVx2DV~x1)@#_uu-KPy{%2-FxPiw)w_ATAo6z zp=#!4WM5b1$VIQ)JcDlSwT9kSZ7hm6&sw-z=-J&Rk>{g8cUiik$Qau%Nl&|J5JRB1 zIU+|?^P14%qOj?+OAhC-nAwUbpM}=-=q$Eiea2>WS-mPr@2*#bBoN$Xaoop-3DE)O zhL?=oL!@Ps^kf)`InJBwj&I(LsyJ3T7jvjimbX5^U(&)$U(yl1KgU+++ECQ?A??$K zMk)HpEZNKWhy4zG*ixbqN?$Eo0DQcW8Rkl#t83Fur8szQsFUebJ{> z{|PN?DbG;Vp5{eYhoQIFt;5mI)UIqs7W|mH1oT7aR3m@&j~{9pm3Y@}Ig375Z66O@ zlQlefv8gIZj3-8SGXJ#UsvXJx=2N3pzx=3Hw-Fs#3!DVgW3c^WU_IXa;Ynk6g9`PF z3WX|V+O+r_b&kBY$?~}}Iy2V(Gw)Q6nzfMe#WRrZ3|(%*9$Pt!&s#`-F*!WSErZv` z&+3PRK!nZ05;psWu_eTth1S||l2OeU9A?Sx(RH|!(v=f9^d>=M9o z&dH~5bx<)=DI~-!&>v5J)jmvVVK+PBx+Qy0PGDf=N_62YN~c}es_v7u zE`~hQG>D@)#EywD#-Hxs9((i8KOK7DBP~XE$Fcn5o4VJgqwVBHd+!_PG^mRsUmiON z98WkXi&6jX=PC;GSyC?w& zkAjbkE}pE@K_$sy=iRg{hZQw^@k}beip}Nt7X3?#zdrhjc#OSGITwea(Bd+1${NA@ zT&PX-C1u8%Nma{YDW>jSmpRYlZ)}Cc`rYY4=t4sLB9XbMuz?3ZBihg#SOo{*1nk^+ z7Bm}lc6q`KpW((geF!y@5S!j`=q2mlY&b+sO9`Crw4AP&GST9vPKsh}7_zt%h{WW@ znt$K33oBS6$HsgEPehOw$3}LWM)YsRZ^Xr>Exs({TR0n8 z&xA|ehSAUtaz)m~)?A{2d-O~%vqD}fF0HJVrWFiUHPjTXw&0OC!dnGNzB|>dSE97V z3!dm2g%s|3sw=muWt0`W)pcIn1ct-2vH`dEaDoP(OKNWNg&?gNq5X+5`j+T)$&r1?#_J;LD+VSOso{i}(M8w9StBX2c&F z`Sah~=>Ln|5x(~L8V^r+NmrLKl~*H?WF5qZ5Vtqb?scBFbJ|;(^LE&x(dVM2mH$RF zdr}+4sf&B%P)^O8c2mqsb_tE{nM^!KAoQh)puMFtC82mzI|y>COBT6ZDGHL`4VqdR z5<0FL1yLGwga@h@m`|)uR831wk#lWRA%G2(&MzG@i2|d!rUu>xV!Oyk?cNFr833<9 zZv+wpjP<7Ws)J0=Cw=eT)_pyCKs_(BsGUuZp=bfq&AV@okLQZ!(vI$}a0x-Q_>u*k z)62INeZU=$4e_>$KZ%nUNeD_$XQBykNf5n1mgrn$y)0XZ12nBC#fHIlZM3CmYYvdG(!+=9r$>gYSGY|<`onww z`J_GcmfnE}dkz@VL+l&i#i9CD5qimJK8n-y*RgaG<;vn>^;>a&cD6LNe2yc0N&`^g z1No;ex@-b~Cq5d7-7PBq#RvxhytpnA^jYx>VWEl!8K)y%Tyd!! zbnl6qBc@i$UF;J@^|jIz{F#)$J%J(@4Cu#)_;Xc$m||{`GFL+f=50gJX8>B)o8C8F zW}H4(Ia+p9=z1c6G}563t6Wp#fGV?F7%ZW2b;U&VhblZwr5ZmJn2*T47xdphN?mCp zkh%fL+#9438&c!Vj!MP6e0#b9eCoa^=9~el)0y9n_ebrIzy`j`C_zSdrT7&X`9;2a zm)V=~IPQ+q9;o3H5#vD*t8Q_&zLB7&(0S&dD`3Pt)h$8mc_UzH?`uau@8!ML1ArUW&qnoXm8nOdtrwJb;Le?SR-f9^smM(K*Jtx6V+0O+^1yKj!sS(o7 zP^Enz_c=SVq3Me#U4l#vC|^9Yd2s%|SK!sK0$#9D-k6xah0qR$3M@o|?h_F>B!Lc_ z@4HO(BKrl7#|6}JZvWJvWMo(%QUo}|8|*Rd5|S#Qvebh$i%^lx<8lpt$K8d7Jb7VI zP!usj-#2WkhNFW;E}5ez&QC;AzaEnO^M^hIpxSKDHt7&dyOT`IwEz8pQ)ykVal)DP zMJBU_-kDckrJQf!26C>t`v$mLoCRtMpVRK%WdVpNw>oNi+=uK|IZ&ydutDkO-YK0o zCd0b!U2%V4)nff%n-$u=2z;WeYee)*=mRGMFl8L3%c)yG;JcDErQ!CJD#wK7kwDpq z!q|Hd^;ycy7 z96OX630dTUSWsgHCm`id@_*fg{KHte%=&oN!h!;r6GuUD=-ERn$9sYCfGO}ljsZ$4 zr~OaM^2mc%)x|zO4~kFrWU+9sG>2zaoUWYs`+QOkOO=_*UEn<3-TjNYLYS#uXpc=J zf8xPDRj*$#MAYAL>_`HjRlu5C>|Vhh18wNzJhQP7;Mkxt&1iVOdDiMrF7cXorpvpH zlCy`>%%RR!#+NyfpRPLj{y?Urkk9(k$juHPY`HN}hcrkb7`sCY&+o@yZ=`SjaO)VG=*#D>5FHrDt^IAx3ID0a}m@9&-!Ru_#=LNYe2EFhZLZ@>G{CRkh z0st~uLU{n6k?kK7XI7q!Y^=;Ff)I6pe`jpDs$H{0;UECeHtMP=hRwlkazthFEBE3h zYS%!#tXCW4kbGlzLzV#&j>vDD%DC zXM>xXY*T>ReCR!}zv}G(9GnmH#IwOy2JTI=Zvfc}nRh1$8Gt=>a?fUt&vyB&)rPM& zjl*AL9z)rfZ0o*gMPYqfRIJ}QaxwqeHbi-#ML8qMA@{1GGk+L_{V-0Wc8{vbp_9W$ z1#(n~R; z_+{b3y46lZWqdnHdOc-Xj*@L z$^;+)Nb})Uja9H+yQcm{HCe!EclEk}Ksb0Oe>dGYv;4!px+d!}%ST3Hrf~Ur{XqZk0HQyDyLhtz$23WIqNBq|K(A+MyNfwvbXI^(O7|o zLQ+x^8a(_-PwFlp^kyS}Qu!H9XaU$G!56YIgF(L!dG_o+2@x!C0A8Utq}Kr-K?4K! zj`@)5Kl7dNK_n_!N(Z##M~Dt1_a_<<=%t@Udvc4M6CI&IT=&M;Kb6O6486e#93q+L zs5C1UI%=Z1)+qOD?&mmcw;w2FsEAjc!L&~qX^{#_T*b+Ubc|UgFVav{_Q@%o?;H(Ua=nXH9VHEMkqpS@ zr=B=KJzw?MiE$gO27;qQnaP}+E$n*oK6>K*5SXfy>O{7l_><-gLZ!xh*L!ybF;_+d z-gy<5tQW3h%!QejjRhmlpSgD0Nv^oKxHaRWLrZ0)rEJV3%lJ)Yu+)X0&wS-p85v&R z*#XInj9?)zAiyCpAe=QH$%sxex2|sTK@UpQU}q=gIf2{afD3vJ+x%DoZ|+>{v+;Mj z?T;5|hBY*?L4lQKj^(3b6JcRtA=;(hJnV^yf%9pq-jj6+vr(xDa^Hi!M}EAE-3Sw= zg)g=Oq1n;Jk%+gLIE3U5ajX@G(IAn$eh|PqzdOcDL|E$cz)`V8>0))3X0mDzBDapz z1Ajqjz8B6AgoMCu$8`-D^f}cDocP6dOVtsaMqL>WOozLUONmGJ>pwit$)fb=K`(xM zw(X`cXq5q$|M)m6H7*wjVV}l^Fa~q&XaIhqJAGpeovwvG|F*Z5b_GugA6-K@fFD4m zb1ab)vSFfMWL{OF2@&ioJss#bHPRkc+hH8;Bq|K723({@bxtn27qAHNedES;hqHV5 zT@!FSL?m3Zq(XE>jXTVl=KC|I3iF}%*P{3No%eaYDvqefPCgoSH->evt$i4zYxDH- z0=>9|=W)i-kccqIxD!GvkNE!4F z=c80EiSI989|Nuxt(98fSl-e~YSkaq1~@iyr%eM5h;Gup-7|$wd$-FDw~Zj6BC*c; z!x{Lzesy{ibV#G&x~{7Y3?QWn6esbEM?>(G&oE*_ri(uJ)2X#^L*pVoDDhe0l5xHN zNRpZMxP%%$Grc*}@`XCAacVZYAN|21&=>fUM`@(R#Lljj3C8xM-jAx#A#laSxg=!E zrKzd*Wuf3%n|H_ZE&K%D;JTDxe1Ei}2 zY32FKuiPfL)PLaa5n4ha^F`wetp!LcrYbO`4)R|ja2Q4|YQibT{>iX|TItV#xt8#2 z|4>siH1fUvDWu;sZ&1PkUiw1=MMzg5@aSB^@nPLKU>CTgtyn32Co4KTqJfc*k_ ze_caEpm&l14v z&zttXD`xGTUAK@V%nuF|rScIi((npprcYy-e%;qTkmioT zuA2J!!$pv@JXY%T_#j7Z1hKI|OL-W6HT&^>fX^l9Em)Drmn*@|sKnd>kfU!_0hFV>?lnko!+EBvjHmh6zK>$E-vPaL5g%M^FKUsnQ!05BlY)$@)`hi z`5~Un^QOa>P}LwSxVu}g2S%e*dFDHiB9_;Jd}Y+ZPKNi7iUi~6_fDq{yUT-;K#Bpz zvGFn^?U~y8`ZJdQubCK9ZkIV_iR|p`*kpN3hh+j^^s#a8e{drw#^*@G%POyK=n+oT zpEEaTTWo2ZSnJF`qHq>I@%MPMbOP(Tv2cTk&qd%pi0j=j7Fj&WIcBqPO)|r+K^R{b zraMrDc+z6izTv>@eHg(kRYTu~5NbX!Dt!cA)0MMWpnyCJxrQUeaG^%O@rz2X0xq(=8C-?HA-d zDnjo{f6^&#m^7oU8-1y?Sw;m<(?B?Ht>Ur*rOe=ZM?#nzE6IUfx1hxDZXVqTKe$H2 zC=2o^Ou09(Ikxign4fwki62K@5(=`-viC1)Qki(s&O~Vdhg#HEvOzz7rd1tnb`~OK#E@y>LpTqiAVadH@LM-pwk<7o=G*(3{ z*i1vDp1IA{+2?X#=_^G}cU&l2B0J6=ny%o+4bpo1&{Ma8yfFmZRanRR*e@h)E&dg5 zw9Ddn9S0UaM^%)`I9sBdrhDDFR*#O6bc$F!Z;UyqJl2ZkcKe15wjx^!z~qN^P|I6N zxUnU84BsMU%*`g`GC1ks^89sAr0ifks(H`GDAy639O4CF2+l{v$dx#QporW^uuOB$KxLoIDUFxbebjFx`vi}i0nl<&-+nl9&sdyZgI5Fa29>E zHz%WhZh8F*o+ewW)F`;nI z?*qh$>2W)4)ky-rw&jmI4mUQ+(So$FcuDuu@~i4%E?Er?&Z+0);%UF`_(iVBGU=Q$ zTNXJzCdG;W*E(qmXQ2T8aAAqM)z1|)GBRk@%+hc!l?mMes+7>3J61NY>dXuDa|f~0 zo{$4)67sxf6O%XK(>gh1g(W4qF9$CGv;Q8jyJ3zG)4N6#fXZsu72E$`&&aoJ?#)xL z5PM_Pb>R|><|A^UIGouv9xFZ>wQw6AzuY%{GaWN!W;Dw~J#9tXI~-(PwK^#N#)+`~ z+|<6o&-bpmza1o6V;aR9b=zKeCOe#ZM5F9K>XuPK>VGU~Jo^Wv{A7Z8VLDZ`thm=X zk@{$t=A%RIN68;6|74@s%G0oCr>gq*zg+I5X0>2#_wdXy>Cj3_GVkKbW(U3JPr&(N zmkopPn)0s=NEiP5alYeEST}M^OPyM`VAO`CCr>CdB{r}b!AtY$}2}PZyjSW!Q znX0MlniW<7YAjZ+gTl6{ zm{U~M3nSf+Mk}Z>;Z&5KHT@aADcvVb82+m&`^pL%MW~TNXE+A zD^NhLGC6`o1L(U@d(wme=MvWL14l!@$qbYT;sULim(y8vYS0jIK;i+?g=!2FPZ9dh zIR(ETL@^-9InIg-Br&wXp=DG!iev&D|DgHtDzQ`oa*bAR$lu|U0HhpvpT^NB&MZcR zJn{SYqzhO+VYqQO`S~4a4^LE#uIAzGhTq?e_ZvHcGr5cJ4<~X5WwMw7RA`|;FaOVmw%<4S z-#GH?th=*&^y#kBgQ@OH)fN47el_%o1E*>gsDvMXaH&vO74Nt>jReKSQ=QfS zTs0|Ggck8BfRTnzWw7TSJ$@r^W7XGql4cT*hf|aufGN8bG!0Zl*A7nXEMl!Nk-y$b za^AK+Crr(@^9)D+pt41qFFR&juk!UF)+;{>85RBS+aG;L&^6P^OO^V@ZJA%jKC_zDum9Kkvj2Km8ge`8KNQO$=uICc8aK90NsZOqK8R;Bc>wB&^etY+$&O zD-lqBbu5*A%aNLf*MJbqcr|q7m6lryl9Vs%=+;<;t=UGCVZCoOS=VL4 z;El)=ei*{wk94hcq}=-?Wi}74OmBJ0GRSZ^kX`ODvz)xp`pfY9w=B;{`{!JJqWmIr zo~~V~I_5?=()iKaaYmcAE=F#-0;+|s=gjLW`+Kq%dBceBu&CfZ-~Hm=eQw z&EY_<<>J?^{y^a3ph#W_RhK)S0(%=b``|@b^ zkKir)mH^w%tZ(o)u}3>P*2(Ps*fv$J$ijYAvIVs^`}I6OA3CNI-hD?1n%Sq3QgSpu z?eKPw&AT&jGlWc^6lLcY9xfRZDYxwr5l9iNFp`}Ouw&a5ch8rMo2nDE60*kK2TI-% zH_o+GgnD7-&O99O=Jf_18p$&Ef@;HfT{qU&E}z>v@LK8EA#wKk*o~#Y6FI%b?Eq{_ zWp#9!x6PFnq_TQuY%ij=BKGbL;sSpUH9XFbQS;idgB(|D`lBH`PDQ+zTrOo{$rZR9 zmYZLt?h?4mthT#6Wqpx-l+hF-{U|xoMzbsRqcW9y!fteihXMcPXOh-|F$XmI@UD? zSyMbq8%zs3K4*gB4(=k?0o8Zef+)?a@+5N{VS<+%XODAZFjs3oH@aaF9FV?HYu164 zr7d4Gj)g@L6mtwC!&KDb8(5adf)Uf**RVzJd3QQxCEA_W^m}<0JqP{m15yz57e5>F z^`?*CwwB`6t7Ut4@8Po~(%tQVd8Qw$erc)rdl)RJNjNcOVSx_ubNaFDv(alz=DiLKX*E2Iiyb}t7sRI|x_f@ZgPZk>N-527Yv)HFy+L$Y8KM2cn&z75+aE4d97Q)q zU<;3J8&rH!0$GlhV$7T6gBJ#9xequ~?qG4K`99>f0V;I;jIL9s|9+#$d92Fw9{CH0_3EtE6t*F7J+jWeg<#q>jKSY z%?Bup&$E+6{(LwwdDaHy(a*`vo5WRvL7ZZ9w`a1`_}j)E%vTp~y&&a?XQjxwxAluT zF!y??y0q2lXM*R_2JYWJ5WA2X8`?c_&U@11__@C6>_z{#$=J=a9>Ntr_rG}iGUFSb z{RHb$d7e$uW+VKw@>9<&$?jqWt=tYfm#+GoPLmSrZ1=&5m|2U>Ff-6m`vv7?gZ0a#GNLTfh>c#;otgjS~n9XI$uTy;?4AF%Tc1) zmsE{zWiLZh$>Xzv_zho-Yp1kuqgZJ$dCNBQ?hbj4Otx}ICelWHVR$lYHiBvFiG&S`&8G&lr z;w{#9olY8~t_$Xy^g;6~<^YF;kc#Mj2XFDlT##n0unSfuH*}@MZ`DcCxIF|zM}F5y z1HGb^Uq5E|H;7C}zkdMV4fORMR)0R{{hQ{i=3FL(jT=)8eAe51$r8bdfKA&wIovQG zOP92BJ)BVn?sJjd8a2k~n)JZx9Ymew@#DwPhw_i%wcvO@{(8=C+~X2!sV!9rxDVZy z13ggZc^z;ys~i|>t?cRw>8N*Zh7*4bgH3pp0%CzLoICai z*mili&L<@KrdH`qJoRyFt! z*4QY@SMIR!YU&7^9R0I38N4Cki5==c${R7=))TOU}HDSy+NSsf`srkLMi zZgQ?2VLEd;-vp~G-Ky#Ye#`Nz`obrXqh*dd`6~OXM>IE;^3KDIST56E_YB(w(%bJN zfhXd?K}JJk0rmjWFeZFUqwtc$u?^znskPSKWR6r}fEtLNVN|9SqJ1kJ!K!f8P1p{?ZNQy$W85Ulu=p^ zGY%`?F5Nq3+CMbZ>E5?GE_c9zfu~cKv1F6EAZQ?80l%T@!meaKmswoRl9#j!pJihC ztU`UYW$3SdB-|ju0S6$19lsRhmn1G;$%dZr5AzX;qVFld>7fNd90L#)M^nsFBd(B% zy7{D#?Ybmx-O!MKW8=+|jjT(o*QKaS#eksph@MC(Ch$XgKN}c1ecwMw>_P)o(9U~Rc8~7Sr|z_2Un!Ju5R#~j0G#Jp8gcrqoIX_Jhpdo3WE*euzU|ME8pzC zVTBBz$j|n=K1^j&Qml)Vub=p3eU?2}SyhFu%&UC)0gqtW2V{`iO_Sbw|MP%;V2s7U5-1AuS@XL2*j|)? znLv4Yd8`0&YX<+=#m4h_Cmqf#_nb{?9JKPOu!1G z%gJmisBk%gd7z9L(1{ragxdHen4kaLSYN;T{K&TYo5l@}#jcG4w+i9bBVAzt@-S0W zz*P~<3uH84hOj-Z@d1!pyl;%&`|!Hi!&{PVZ^!{L)*58($^A(M8{suXS$A*Lh2*68 zEGSVihwm#6C_H3`;m^_e9TGS>PZ+g)BdZSk>_P1-N=nN0-Ob6q@HR9)BEsg$8!d&e zj(B)th}jH}zo}P*>>cjT0lLq}7r9&y9xVay`>~7oH%kBDcouu{{n3Cd)e6o)P>s(3 z{#tBo?2}fp(0wM4OY$8R{JZV(M=ZL!%4F=Ojn@aDqoMz6n|!#`Aq?Qq?nKQYqo4P# z#p6p+ANxN*!gPF@+ZGyRbXrP;PM-D~jrur$2WyW9VRZjyE<-Q%cLD(-MY2I1CFqj} zOe%SF48hdLLqn>WN{d|QYgrZOzdpVA(Jm_p*kz=gcE?i4E|J949e+4kKB z^QthLV6nIAnVfulfPL+>gkC-hJahTkZBMCeD&iVI*sTz@9}g>iAiGIv@aXqbau{x> zi06Kmi4k@O2pQJZLVv!^qyX5ZS2s2J3Z0&A&z~L=4bs4^cno^Rp7@7*$uObm=A;gm zjzep^GszS8f!Y}i`X}I}<=6H?_V{g`k53Hl6#>eu?FHlhJocw&7%{R<_NZr1vJoTW zQKvZ;>*>Z*u{~*@RM~H%T7+PT@oc}d3Pfh&SKL-VY7X%PP>2x7%j3hgQ8}J?-6t?G z#VkhYIL85pfPnXw>i4q=&RmqQkGi_H7fD6y zk@v`7;suVMJsQ09j>SX?fDOqJfmeh>)?iWB{DH+Cq_|QZ(qlf8lw=rP9r33jIc|h8 zms?sH^SLW@aL(1M!@i#k;|`Zc32HkDuHRDldKo;quk!74JxxEmZo8)SK1<^wL>R2n zvETz7*Xh}|nC-%ym`MmHSr7*rvbU8Y|F; zF)7QTLGs{nkAV2qz-_Dbp*6Ql6cjj>gEn(8fU>9i6}QMJe+_ zxIJRF)?k>+Zn2tC%Ms43-VQQ-QW3YQu6tdaEg0jq*>8OOTlb~+U8UE>=RXU{-LOtV z3fu5;NG5m2Q(MqMdrc~bR=hEqTYAzd#uXJeD;v}K(N*(shbA&oXn$s)4uI9SfUki) z!Y>q)19R;bU9x9{SPMxTGT{f?L^7;3Ft|X*E<@P$C4d@*Q~7%D=t-&)OAzH9Ihe2g zqF$mjxeF3Yke|78GevxNkiJw^m5OMt?01SnkgXd<1L3`KeyQbWSHgOK%pLNh zL66$Kbmh$IN+7eso?bN8Chc4yw-ODTCczcQa4=I7an}M5-&9a;f2sIQBLtLga-KV;CBX-)SE1Z_u$IHHHTphahLOT%7eMemr+0uCBdk7n zYOhV*2afHp0POhc1?NOA`(-9I58%q)DAz7gl7)4KaTi2j3LUmcI1Sq-KwPc1z7GA; ztG(O&G{Y;K4-?`eBO_Bs!KQTMQ`xWYn4mZDtS>r0&vf)Xcoq`CJUg%f2e0H^eC>5{ zk^RN=`Q7CaNw8owru=!y$Y`#XgWmP@h625Ol5D#2f#EDAnja}sGx&U{ZZGL|r^*{e z@S)BBD!T3(#erVW>|re$|er=P&C+RuggajHA=_d!^ ztzp)D?p64bXKfkUmPfv+5el(Z4hrfT+%lLSB4ivtN zVIS@|z#v}ceXVJ(#x}EL+udRJB!WE zfx}KKMcL>iC{@Z=C_s5io~D`%iC&FyKixaRI{(M{57BloVywZ@vc%(VZCgyhzWrrU z;3$-9bho-g7=j^pJ1&zhkclq~dqMSgNOn`sB&^(*(#{4EC}0EC1*qB-z8X5c9S-Ju zU_yej+k!yM3=FB4NxKN$K%sFVK!N_e&|R#s@She4bo3rRU+nGcw{VbJ}tKy5quaCJ^I#@33`BRfwLO<$7WIN0 zu%pRPVtR^kW__=t&B^5`j6at~%|!UbfX-oy?j*Vvd=<}Q-|+{vi{wyT*i-!N68mj8u9#!|q&#n}_d}Q@zVOar@yjJq z(D+(_?PPClEY&&XD-s!@pfw)*#-i=Y9XQ@&1mab!=pUFi+SM(DcX;3`<^F*36e*3M zmgj_=qdbOl>q~!u(VFn6^U)n!R1qM#0h@!U|ad;%H0I);QG#DXSR>n7wV0wdznHobsyX$c3<;H{r;It;?8#aBPaX#*1 zEA37-YWuZ=25g$}c#D?^{c-Pb$EcsThp@oz<;zL9jQhG(!9eH@1>5K|**X&)FHOgS z7W=+lA|k2*DIatgTSw9uNLrx@A_@J9DELtcT#9OF8gxNqE|2B-&>D3zKbpzpw6{7p znIa7_9rmXnfBzOvp)>bSe7Muenxp9#2@dj}cw*tI8W-Gd(V~ ze@rQZ=geWvS2pGl3~ASUX4SXy!dJ`-PLEv1u4fzLe3p%pL9AleH#TNFCu-0#hO$FI zN3z;IK_FcvQHYr7{amKq(dLeFSb{pudAL`$s8g52UDRtBB}Z~8cxIb`Dgt^WI-Qm1 z8JY!eY9xa|0Z&>d*$bjvBpjmU)~qb}7fXN^u06^fANatqwa7)v`2C%Hy?+9Vmaj!X z1qn(Sn9>MJO*PUxArhvjfpLxfli;?}BipsPzKi#So^JlEmR=lMD^JxwvUNVFqXS}T z)P9}8nKO(87CDw6dx!>6Ky1rURt0)3lEKtqV(>Ui&dhqIHA?Szr+JrD*+5V1QRi@|8Ug!}jITiMtpI~Q3SIL;8e9zG+0B7J?`GAM!X zphNKK`D@fun=@=b&SK_R59%Sp1)zd?{-6E_R9OF3-$Tke1L~tNg%etwZvxybtCF4M zWPOz~J!8GetD@i)i;YuK4aC02kUN6UPhxjfpfySb5;nmQFhyAR-vHI0-DZ~(*b)*@ zstuMk6cRiI2UQ6?n&u>JQk&gne)Hk;sZi23N3gbReqcg~QvY9bUl|ouxVDW&2nb5I zNQZQ%fPhGMcQ;7G07^;>NQlxMf^@?GQiDi|Al;4R(9QRZ=bUxkbKbSSf8Sc4e_ZT6 zv-h68pZz@decji6T{@LH4%kyN*S;RWsSv~NchO*&Vw>yAhAL3FJrVI#f4~0PiW1g^ ztX*>?H@NQz0erN^emVt+_aIY66p*g{UC3op$I9P;GXz|1TK8MI6>l+&S4s(k$a*M5 zE@86m76l~DW*ZAQ!+Imtr^2w)LH&p=cvqK6?G#}A(SnRkI{!66ksjS!_Cf{omG{}7 z73k--rZb~S{E_Q#k8)a`!6esAlYgo)5&Hu`0YwLSGcp1ZNFB(+2XF_9s;U>0ZjQiY zm6au6vgtT#feA}?{&a2;L^J@G1iF{cLvWFVSR~<5*1>?>>hI+FZHOJus+t6eW+>2y zR!4E0i(iLuQ(Jp)&9(wji=^2LV4%(PV|?n>%?)fr8*nVpabmp-E`i+mtrmEGp9ieU zjGAM*&FHe!q>&L*{U;(IgaPd8uq^@VFv+N!bN~7R)|XT&0V0xk<~tvI0hpOCTn92v zOr{@Wddu6}SbB$-RQfj8cJh*vgJR(=_)F(;$!P3ffK~MV>Zq;z=GZ-FXLj%y;mD;p zm8AL!S{aToAibOSC_km8O^m0DEY!J8Q0_s(oZEFI0ye}uLY}6l8KOYW<}C|0NHY)htkQf%J%5&11InYPx2V057+&K(| zz1G6d#km505k*t*zmviK!!YJUPn!rRN3K8wDs8}u&7k4?dP1Q|WfvYLW&4ct`r^XE z3jp*W^&7n|TZcw%M}R}$^2m-0bqGq8)qrG#S+~v41`_??I@N55V6hfPR)C56BPy{J zGk;lGSzr|QeW;DKz`HUBhnmXQc-=2Yj(O41C zDFZacKbtidYnHey!?DTJS17-p?WyYZ8T153{iB6*XATIt16`d-hX5s{UF;`)AiD}6 z(jtps`G__U9v;2=i_;^a`+uhKasQdO|MxUFK}o%#un)5MEQH} zJeHi0KZA%tbrU zWBgx88eZZBoc7kQy7sv5dJb%E-5R&_LUpn+T2aNXpl#Xrg{4eS_|D685EjVvzEf81 zCcdMs{e$I6=5Yvd^8SUkf1S~Ein+#aw_*0j?o|IzH+CcSe31(HvwNS|zH%0MJo97NS>{=1l$RS%Y!CRoMo z)Xym7xhZqvFe%`-OMVXxrVOHZ5YnQ8L<7RNrXE8nJ~-5N6=__cU;&|{Q6xj-a=Lm6 z-ezJR7Zjv-BshaY29H>!W7~G9VRp(}n2YGc0AY-nM=A449jn4A(UdN1cjt=BCVaH2 z_e2_mI~xciAG7e}rn1}!I;79y zJ2<{_5?T_c@)IYgiFQQ*_)2eba~^$N7OjSJh~!IMTU}RH9Z-jDtxa-~qX! z3M2PSgi^7&8H@BD6|_~rUG7j{9521?_gQ{$^%_KmGT3u%=bDx>sNrCy)F3;7$PwSpG!}7|cX$*Xxh?F37)|z^$Y^_IhVF}#> zlVyxjajI2E>+)ub^6p@a3mf^tB}w_-nA%s7uaj;{Zu`$hneIDUlX$@N)j2)f)O>aa zW&Co?vYZ{eH@8yGn_t}deMsggzw?=Gs!)*Z5g+#T7Nfx`W@fiJ1jN5UbI^S0tPiu&dzWN2*3o731ofO{~a?SO~{AnAxdFbVrLf@Tuw&}?t7D| z?^F4iwCk{PWnxS=_T!LpVG|PqO`f~oOZDrwmrlEe7!t$-#f`!Kl+m2@CNO`I;dTqH=TtE_4zPsfI6p3CA>gxd)dpb;Obm}y35#W zO#vPFYSU^1OIj-a6xH0GgPx>PpjMhMn>5si=NnL*wbVF$71glLZOhjN>>}~B zvm~7+JP3^tJZxuZW9sg=MkCol*TmK`AK=}Oi&xYk)pWMeS4xVPRc=-sQ$!H#M~2US zTSlF1b|E$y6bW|LCv1$=;uvT=5p4>Oq(f(h7QaQU%Yb<2C9@{$5Kw*9fmty9C+I{? zO|4T(2!~RFa+USQR6ZWCn+Ja72?|$cK&0{G`+6M#*8^Gr%JD}YV%W#K5LjzN|r1^H@MtG zFIzwsHO4Wo*fa?m9)PxMYGA$Wc{h0{V5)><^aW>K=(9Py(M2adHZd}MRZ!@^_id&X zmo%fQ{I!KTWKPdj?CvwFnOZ$xl|xVLv!<43;jv~roaP*cyi{LBt*~gt9f60D`F`>` z0eZ7z4XBqtwAO-*sXZpcx!z)aw)?Jx#Ey`IEhKQ(I!#2$I2=zq+sO1%sC6!fDGs~U zCU@Ueo4f&Cx{COEZMi@opj*I4#}RIy*3IapL(_c_Ec2{m8>RMP$JhuQg?A%*O^?<= z*qqj2i51s#0-uDqvxpQ68S-ME(8GgSO0HS2bv@Tg5iUbBvd&$i)!a4LBB1yb|2l zk(*k+8ry4TWVbT#Yp-z}iQF71;INvxj{*u|h~WzA?TOkbu&KgzgdX6OuT_3}7HgOM z0ICQ8n_m8?&BX=krIM18=wkk1|E}DnU=pzbvNrYFP-+ywUEbm&{JJWrktY-J4| z^BdaLxd=Tvc29rSkTYut1g0wxNC{%8rK4nA&|><%cvXJ?=*Z&y$43<8?SlDS^@3cb zyw0ILDa(-wUWS510A|y4A%d37jc%(eoj4cjAOsA!HRlF*Iq$^{1T4bu0D6}uLf}Bu z27Fcxmr$}?2mxIvlAAx?%pxitZvRdJt{AoWE^>NU=HPgd_`uX(+E<$<>HdnQ!=o|x zuSEisRxoWk=O3Jph|No+h<19u7nV%FHE8ky05Vq9(T!l;CNIsUQ>$b|cL(MA@Gc5GCAwi0aOtz;EqFQ) zHV=!bgMP~kH7f5W;y%jVL$^h_)uv>;skl*ggjSKJO6ARGB~>R$pq-;Va;`s+9mUbq zi18p=x5Ve{XlX66%J9qar`%ov8Kp-L(Gog}p}9ArT#>G&Z|5+1Q@3PM;=`4bv9 ziUZTKv!p|f*n4>?GNvnTQm=lzZJ|{6^%BY5yrH=8sOz_!*$U0xv7CnsMyy@a(Wi## z(H^1(@pT@aJiGAuSZ=GkqU6Ef-ru3O+z$7}#mUs_cTQBkumiqTlW{Ww;?_ zR|bgBoVC=q(cclEK}jB2q0vaVEat-lg?$8S`oq^ZEN)CH_~Q zuC7YMd_xw_bg^w?Xgk$YSq@5<#PLt5lW1Y6E1`%j-qP14_~zssaIM~+@4d?-os_k# z=Vv_B^z^Z}7&}jHDdI)E<#<7fmyEvyqsS;K=)qwEzw+FFl~X?oRHSntFvV`%c_^ma z!axC8V!9sBbCo$xRTaXKMI|~1i6UUpHswp*{5MoB!8GPP@oN6&9@S)BG-buvTllaA z4Bpk5MVI1q=-^&vGc&8_-{qb6ZJZ*EjXNW^RA0xH73;M{@K`=U3(>>|k2Zr5deGyd zb*Flq=rSt-GE))w2M!f6PwB&RdT3$PWB2gq<28)=#5JSvRLtlo)!S%z&B=r2Y&pbe zb+Nt@*^V!MHI0KPc6B{T@CKEqogKm3w<2(g>+r1&b@|Gx4+SFzir=x#3Bl+zKkwF0 zd8Qlv4&f(m%ZE12VEneL72*DF`jq$gVF?=1#m~C0W}~EvdsMutI8PYGcgL{wa(r5< z{4>#3ICn=qZnT!I_zF7n93ajSxY9ZT00f)lUEYgy1rt!&_8Yv-`nH$Pp= zDvj3MZOfQnK5X%I2i7HfHbA@Z8q`eVwg}jbq5;LKh-xCSGdwmY@!6G(0QTL~rOi#b z?4SpYfRO^2IY9{tcmNs6SId70nmZ%Z+uH?%g$v)$XF`O+G6Qsvz6;@*S#)gGzM?6( zsv)w&SLvTE6Z9c?bMft?FvqG_`sX7>Z%4hmq{_9N7Rk>MOOy-0o(p&kw6<0WLZfz> z+Q?#YQ5*!ZgOlw3;D;E||~?T}MYyk20IkIx?@TzU(1cPYb`)K}W1u z3}rwsJhoI?JPnbq4^dW%REc6$j{uo@VhC}3w>)CfcOar< z;S(j>EInrd&-3i>iAr&UysN|v5(k_IwTzcl3{E_hmg=^wU^$tpu=yphwRlElEob-X zY)?hcUe5nk{vVxZ&ZkBl>2%fSlcMgkRmb+x7iTn)_-u)P4iI?a)_PFM(ox1b>DlW*cUAm6?|+Ylycm5{-NF zrYe;QWk~GC32@v(#cw;J%L@ydMCj?m#P)EWa&bk1M5sVHwS$F(`v!;w42<`0oSm8t zT$rbbOD!%b68~0+`@MyjMjFc9CQV@OPlm`@93AIRZyBl?1~3bJR_=iS@k8=CBO}P+ zu{OBC4K)72R)@>+<-GwvKnVpwUSyRZRm+?)35~N*_PHy6|IRan@s()2$QjXjJ(9?< z)V4Cu^A^~k-9C(Hk%W1#R6h6@Nl`LSE*{Vv|M}l^Lg3!p(>Kq|NZT%_478DFa6-G6={h;k%whin%o-XTR1tvay73c6 zT8BPtB*{A34E(x~t^HIQV-kI~_u$AFghiOWQ7A9c;mc(Nu-6f7u zh|)9j_r!M3M~h>%m7ycZ6fGDTy&Ltf+&1D1-cSmVqBe5Bn4qvxm*MnIrZ3^=VWGwV ze!I9|)b;GI&wc}(^?{?69l*Ts4}w|Z78bEg>)Q)Vr(fW8SKkD#8byUDY-Tv<8(Sf? z-bdb{I$fWA0xEDzX9Pt8gE9$Akq>6o>Cw4xYT{1d%L@g@u z2nYv7jf^lEi_DFPv`W4wiiMcw086kP8)zH5ds!awh{14EbWT=gCF_!tS_YzLHf2uS zPN;u4TCcQnN-c@NIW#`k0n(y-xE92anJYqc=SIqL^zxU?Y%ky3wcKHmhD`}cN*bb7->XLg0=w4pKky7cpDO**|dFRD*A#P_O|+;}P0=|6IFzf!k^ zFr#fTMb&qFqhqqJ5&1eal;ZaI=RqpNVf~vkuKqdtMpx=5l_c7M1+thk~THf9w2qa;!zprpQi`UZ? zRC3G_0CbhaF^wlT8&J^z|!UVmWQTd?5SP_^mw(o?e)vo2@#WCv3%w z$++gucOz<_YBgo0c*-29tYs^b2K6Xj)lO?H^Qo;_`qTpVBY;oY7;3p5QXUoMys!Lr zD$KKK*glQztEqmJQts**B41A>I-#^b{MvCm;BEQM&#Q*%jc^HUeB+yfMhIHj>^li; z0(l(To#oA~lgl>2iL=`RI@7Z^(1c*lx7RtfIfq^;&yusVGR^vI2aZl6hwXWIF^RAF zzSMSXo7BiNy^jdrZ(i6ko$*mvPw@o`AB7mr*psmK_WNK;XBwMyF*vkFZ%Bcj^zS;% zkME6Zy5Ef17|(V@f^g)LyUy+pLD1pj{Xt1ZgoDaAoD@QBfMg`_(`<{Dzf1Iz!bAPu zT}?7l5@itvIv4^)yJK)KPmapUK37Pn^D$uIXegXh=q1IIkV~S>G}y!7x@z^?mGUSk z_z`k1B{YnkeD@K}WSAtPJD<|A$01FqlFr{46cfx6y9)&0=pK$3(G(E$Jp?OZ;&}9g z3&Rwn*DI;+1F4kwNrF{33GYN#e!l}}j^gB;bqJ7KUZfvov#DwCp?G9C$~t3SV+!Eo zsoW291gK-$HJ{6Lr}Pn90n1TTj-+$Od5s=8dY@{=wXyVyBO*Cl4^4mANA7t|^ehvE z^AI_cI%+G5&hmfw|)-d(M*t1VBWTjQM}qXx+Vgz}Pf6VsXqBV_F#b@ByoE(K$Chyk6t0 zFe-@HU_zbmOXwe%>xcIBW@s%GaeuK7kI6SUb4;>!y#q33tL`nwS_E4qcF6Gl8 z(R|OG<#u+8E$;_WfoJRMs<%Lzj`R)J?p^fAF<5PQvZZzxB2ge|^iT^G8PlvAPFuGhZ^ju9U5B zR(RC?#UB3R&(d>q6E6gzK+02NdgIW@;w={n$ zw8|^pnPYqUsavLaXV2hqt5f-e&zN`J*%^Vk%Xnd(*~MC(Kcv*)$8~ueRSID^0UGbB zTYHE$!2_=LKQt*D0>r;wA@l?r{O4btIEXrmbYA_omx~C#Pr(9S8)&n4D0kuP8mja{ zuy5q^_Alj!ePRP%h6J6YOb$Yp##FFZW|vQ%0E>~}Y$a;xeO_qpy))|&(sP0+92>o) zAdFJ{b0eQ4w=qk%j=}+a?LC`|am^aW2f|Y7b!b^H`Dk2Ga6h004SqM|rbc;aako17 zF^E{+ora3wgfzFXRE77C2w3lpXp^%50|M`p3;su;R=lk!d7-MV9BXE?n$ z+TXwEK6)fm#n+xc4PW*enPL+2({x2>{Xv}l{>9N)y5t~mRFb}J@HWSNz!tFDy7L~r!*SU0^(tXKWo0E*7^t0Z3)Gq=4M4ei{q*Oz%UL~xe%E{p$Wo5?FUDi&&+tVI@O&UcY(hnEf zwW<(vA1I*2k||yf=d|aMA|oX#qQ{$w4HcY*u4b^&&UqkQlt|mLoQgoFdZ0wg%-mrL zq@4KHxR(l&3i zrijlE;0KD##~vbS1h3zv3Vw-M@xOMtq$H$33*{#38SZ|3acwq}>?WU|8EgD*aGo?4 zjv^q&I5eqBcQd^)lDH$c!OpSEC6xb*koqR-4}sRoca)D;1UpvK0xs>|fh(^S7qt06 zbwHkOdDr{Rra`=zA5IVB@lP~<>KGjd87z|D3aY$R{nMuXD)OM)roa}Xd(A}gHyY7I z22OWlxF|Uh%wT8PK++#00ylxH&dXCFlE8%agCP~fx}($&e*V1Hdwa#iK!9PpE%@F` z_*(!E1X#&Ar?JeHeql_}%(v0Es{|l*_X+OVH7yU5)7Q&=!JbKsnSql_{&W6iRhWBO zmZOX~UCpIK1N|JTpg(>Ly?}c}*UkO2KfHJku1(G!E?36(qz{oNx?;ums2?}rW#KM~ z%Tns&O49y_H@cHz8P0QR2DM!?^@_HfY=NqV6k@LQqr5Y=U2;t6NG1RUl>^Hl`Vohf zA0`mxj!xqbHfkK!&73A_4a4x$a+a@C4I`zz2C=_b=bUFAzZUb}*40$?s)DA8MQT}Y zf4X*kt-dF3zOnu%Pv!luiw7cXd51&`E1H}Y0U20~+CP+!6tM;LXAD;LwU!Ah!ZIp#7 zmaf(E^dNoHt%>)l9}fSY6XQt5IC6C-lByi(N1d7Q^pW}v)( zde^Ws%Z67@6em%&xv|bP@hEUs-;iALo0qIj5Nr^d7}T6zN^`IBhDPDJU(R_)n;B_$ z=lGu>&emJL45>`{PI+70iN@UgY9tfrkx~*7&1v-R_zF$U;sx)0?u< zA#1lf`$$V(E3W$imNX^xoKhSTzS;sVs@0;jJ1fYZG^qeb2@sMmQIfYNa?$2_X(kRL z$Vq7_nFb#Y6inJ^$_$GxOd34Btx(4!b)x;~tEH)NbZbA|-6d6i4yQp?K2Xxq+d1dH z?-@cGD~L`VA2J^bVxj9TrYfKI11b1v`q9^vi?6|LX4Z^X0k6697Uyqax{e$HE%eM| zXH{^{0nNxg1MfKy%tOvot# zlB`#Cd#0^-TzgCmG)r#vnuaN0PG?5%*sWbN|`{lGK1=Rb-Mn z^m}Kz9yFv?`R)D8mI?_C1=z3TWR)f2=<1XIgEnv=1l2MJ;IGFGH9rP_IKMeqk>him zdO4gUeFtc6vTlu+D_@-&Beu%h)EZnCZ%b}~w{nG9_rpgNLLq=|-r5R}O#{+&9DXMb zfKTz_)hlGzlJbld(6wRJuZsq!uXdQ^0!)Rl+}xG$ZsTgt9X+j5z3y83aRIkAEMT8` z)e|Tldw~T+2!SOtGwsdg$<8#ZVN)XDbZXVDWMv4M5+Um(@05YNgs+8sF z?ku@@0C_@hNMRwvNP!{^XoCQ{qrfX%HHJdisy+tTvVn@l%1Up-{Ra?cr&n1kOHDc@ttA!uTR@h0VVAzjXZFeFED8j`l?fT7kEztt}VOrShb>9QyZvUb4L&i21N>CX^N z1swFpL`1UfK@XU8DxQL0o~K@@6mfieGVP2-E`SUA8~T&Ek-I>5Qyi#h;sgc;(kpy^ z5sZrcgnb`a%VS_+SpX-}`>3b@ra3UUP=a}v8Mgs4BD(*@QM&~faBxB@X90C7tJNPL z0e=Wd1Wce;B<8il2ijC?K(B|#VdBF_7VU#SCFP)Br~RvLHPCNF0~X2j${8YH^tl2j z!L`A3d2nk5{4aP6n?1Y1PqFBIf|MQG{FUDbwA_HT?lV@_Xja`Spy==p;D2a1lzHHj z;OlX~)HUF8hyTy$mwPXeHb`W*ZC?vJRcyh10`C(MgjcH7_bSR^qi$TW!a^$7t0`zgEUjKoH#;>3c@xRb=X=&5n zd4BeEus(UPX=t}qq}$bX9bn|~%Wtp+ct~dmw)qY17!#9`<>u#yrlgP(vK#dmD5lyt zI<9tvVyf54<*VTn5O8vFbpQdm#+|z9fGWkNrlvD+KZq$PEWtaGn3(9Y)=z@+B;x*b zog+AmrJC_gqYG!U!ojdCm8y=;_l*rkMMcG$nwtF?w;^C8r|awM`@Y-k==d19m%?jC z0z9~h47`)wS$W+1SU5Q9#>PY7OMrE037m-*=d)hXcA!x~zJbT{rMS51j&;OMrvK@z z7fMZC9Tyjuq)@sQI5h;(O_TudiivpyF2&N)^1j3iLqiJZ*7LQzN<`1H4fDsGwzkZv zsVSF0`8BC&LGL<3wn9^z`#;M14l_!j`wosFZLO)xRQfdi={|1LQWt@1+&Hwhq&On` zC=5zzPt%pS#l#+c>rG(jU43K0MWU1`hW$cQlb}SqoIdUqhufNhot<5WPz-hMXhlj^y<@;l+{?D9D0w|1tmRw$CRegJHJbk!6R0xBmozD5i z#>dC+H_!w1+=#ul+YAC0tt+j89PzAo@9r=&GouID8)G2Hnx!Qj+9UF;%F4L4H7g*d zOhQVUQ&W>DOXZ)~)Fd1*Ls0Z%`^4qYuNDFMHBd%i6*EDW{G zsD(5M7{~KH1jkiUWl*36z{+Mi#BFz?o>*)Wqbi1y?T+3neBeUzw;h z7dcx9UTg(-FFie7eTqAEKuuS-7ceT{%j7N^sv+l>#>QmJzNEOmRp91^0-iW3T9YYw z_7N|GkW;#4A7Om&N{#svFjtzaFuey@DRc?F3Z!Is5nYK~tE;OCLHFLGKQ1P0?-e^+ zZ*$u=xDTww~8M0HYev<&hSQ_$%3y0E{B+yl2CevyZ-TN-zV^_ z%z9!d`qPC!xvdW>GLMgq$@u$ULmknt7ZnW_Gk%Vb%j)ZsEiW%849)|TGNXFZAqfos zbg#MIg@vcUU(xh;u_j8wt}jN=Q;4XlHtv0VA|kx|5;{Z2z~-{iAm(znIaXQhJOs>@ z!$6~l?Hn#!V}S#=+giU1#?S4^YKG%BbI_?j ze&HY?ac9^DzkzQtv!U7AjH&bGZU6Wu=f+h+QZh1I2Zxn7S;{+E_4S{?yG8$%1w0si z{l3M{a1ziNmAP5rx-5fSZ!IlX6HgTK)oSYN?}aRy@sMKIH+mzUp@4DDZX9^co12?E z0$KW1PIO;FfHGf=DJ(4PASMEDasJqR8O*o^&kxN7-)x_`g#=Rcx!ycsWsdsLHygsw zv}xVwAQ4~i7%CIdvwU=Vs;;e#_HCzresJ2kO%aUT2!;%FhP`2%#Oupbb?|%T<1=dKvJ8uYS&##oyoG-ODQ+ZwafbVxp8L(Trl0ccP~07rpjjcnUVKmsNA%}Y}3ba<3Lf^y~WBNO3bY-aE`+vacl7Y zCnhDSX=--Mv$u3blW=$R-rh2!-$l+Pl$7+z3h}S=8QIwjK^TI?Xm4*nS!+)OhDml) zlkb!-@J#IhE_~o6%_}N;@3A$87}IyI^hTHwvg*8`eL_tcix=AiR6>={1m)T^tgRWE zJ$LS+4-NeKrS|aQLvJu`%N34@Tpx|7!@B@wS?>7C+abDVS^dWmu+-G8L&43&CSvb| zf8<|%E`JgFtT9bDgI4as#~Fq|9TErfDov%O(2+vr!%=l*#mh7(m@=}Z!ih;pvMVY+ z45aeU_ji7zmE-@g_>NXCK5u?@{$yps?P^?EQRs%U;z+rz{f=Bp*Zzxmu$Ojq^&TD_ z_+L)VC=BiI7AE%2d&mv(iJ$%+fL88D-@iU=>HYNYt^+VZAipi=Lqhoq`Pn=e7S7)d f|KA^GJP;*c*X(O8r_&?~_$Mc=^s-FSBmS9iM>*Bj^CYgQc-zB)N#c6^|N0lUFM|XTYA_$4x|1-UD~9%tP3+6Y*OF9h?-5p zKp~*cDYZ!Npj}?^xfuEbrFjufBA#FWe6{F3unVHC;jlAFtdW4t`&4llFFg+_`tkRZ zR?L}2^7-!%kLjeyDgJzDUQ3}q|MOu(kyiTea;dkWuf_g+WWR=D|GiirGd%6t->buZ zQF;BnI{G0s{ojjQss5K+zxqGl+SxP|-0!Q^T(y1@GvoOCNR+#xfyjvS`JMie421>b zo4-#x4gy!|uleCHX;z014i1VrU=tE{E679^nyqy|oYASY{?hafNwHkk^mx5XvA9LBP=HQ#QD z&O%fa6%}tTH6@h)YUJX1D&Gon!7=PAQw~e#@4f+^OQC4^0&oGxZ6N(CVpW zBAKum-v^u#alcIwLrGn+nyo$RMyRlwd)ht8`BV>-%)Zop@WcPSj znVHM+WoL++uq~D4Olh02CU^byzGn8jyzaf3iWP#TIqMCWWqTk|&q$}FabJ9w-&&7) zxjlW23G>$EUb%a#8T8HBKsXazkReKSw|2_OU^5Iq$isL z0}Z)Sr<3m@?!!$&VO`V%hQ zRwXUiC#fdi#IPG}&ec36;;~uEw_{AxqQ0XX! zzW6G;%U#d=6-CL%b2SbtL#c1P9X;E-o*J2XhcYLPDfS1%#KD zmckiT6OnXuO3f#N7M_wU-_^N#Uxm>sEN*X0R~@fyO_kuCA1o-SG=QgpNx{?A)!nac z3GV#!5}gg2DWvCW4IV;C?pO5XF%~E;_{{|kcc_gWqQc9EA(RDNAENTLFFAG<+Ji`X z4)t0(jiJF66+DTFWu~*0Z13N{_uZy-4I*wVdBh&Jy%|DTYQB)@8%9IW&G0@`vdruq zpX)k$1cNG4BJ+pKmK1T70v*K9*<;yDOZ)A|Mm?Le=`x`m_ZcS}11EhvC0j$O$VNSp zD|*&1s%$s7&6L5Kqhed@AE!z2uG^+L9SEbqTtD1*(x=4^(C@Jh0zf@R9>84Mm_IsjGO+V&c6$ol9Ce5 zXNP3e9l6%I)Y4ee#+f9DqHs!y%(@Ns6`9*1c^nVac%nanSm@JtGF+C{xw(YB8K<9Z z{VYwwy?uQM=p1aJ%ePO37s0>(Y&Idv#|t)9*=vfmC%vR0nZ>kQJ)ZcJLC0% zxDNQ-PUpo9;hT@=E=_PX;74#-j>jAK#Zs?P{z2f7`%E#ReT27lpDF%;xVNKw2sr)KQ1dQ6+O502JkqmdQ z5`VO*;@i4g)4Hp(%e$I`Xb~u>c8vpNpn9{XyDMxrSwCG`O-*faaq%<2p@v&noFWkv z3T0wq>ikKLn3$MI%x^#VRy9>Zj@_i|YfKD5AR*8CV)8c|(j(LHTmrc`E}mM|uYZG3 zD1{;iJ;d=~zAaC)qVt1+Xb6S4^XcYuK0CvW;Wb8;?3-PiFW=+&9sG{hx{Hl=-kXhP znyRQ4DNz%0er&W!TJiNW0YC@F^EO>NqBZd~u|a#l`tDRou4Y4`bOeKefk8_QhlyIb zeU@U4&k+^nJ~Gns{(j1TJVC{Zlh z4;Po)&^S+F9!8!3Id^9&qL9CQv6!iDdrOLsFS$4EdGIR~ENjb70D)y-C~FM+ZQZvY zd3nswmzKVJd4;eUNTe3&$V$__`WtnmLN;TtS^fO{nrTFl%*Kve!G98k-D5LXW@l%u z?d=U)50@SyKOz`=B;?%K^bOg@)>gVO2@Q|sg)3|VetvsdXFoLLFFXOK+R<`*JkKYv zuS8oM>=HzL5eeIKD+mB^_FB(p_&)}YR>J9YD7p+f;2>~QYKWqc#My4nldRlL*9Bl5MLsML2kGVCA&1YQZF_4V~|R^2Z`u8v?BsEzB)njo_A zC?pClRUjgNoKNvezP)q5&U~Lo2tty@OnKn;m^uD9;@@maN}g3tX|*eXR$g6{m9@oO zVL;=EG>R2kuli&%-CQ+3Awj*$miqeX?nd+FNo94_Q1(0XtJ6K1`C6C2>4WLAFzp&g z!g)L5Fc<@x7UuhwgO%ne0JSq4WG)eg3BcWf$eHyu1Wq$K@ zpKR89O=CGtAG};@_SzaOS~+oi<^1VWvEB;J@Nc=e#fyTL-rj)MUZ_huu09XsAJM(b z#(pwIQdn9_r8{3+`f=*(;^K$>g$9M`Qq!&&jt!Y>R1A!^jtyQgqZIPmAX-Lq+E9>_ zBvM0j^YS*wa%hA2NU~HLgpInx*-HCjIA~Mf1X9q@cuPkx4y8$r7z&yEnUau(Xs#eTS$zWo*T8fT^Vxm}q5kmV$Uyk^lw*A$ z>5XJ3B{(Z|s`bR_GEwBB-sn)l`Nl7Zn-djekI-=ZlzE{0TDBWqqAlJS3(mxUANal2 zp2jO80>|}y-!-o2@qn>1nCPYLb%_r71YWxDf_cK4A@zW$(qG(YHP{rTKqM`2X*Z{lC`|O%(LVmXn2F7*+G1VPPSD z2KWhL2rnNWwYl|w^}<3(8dV!u@PK)dPp3cNfB^O;9R~+TOG^tP4^IV;|J%PSafcNe zp7ujQMFrc#!$ZQTzP^67D-0Q|>MEhc-%I8d>Fa6QKqjX2>G9vibV2^OB>VTzY$(v1 z$~X0HmmjB!)0b}VZm;=lS3Bbz8UGIEo(&qGR_Ss=qnsfx7o_&z4iJ$IcKw^b{E(7V z3T^K0W*D*@0fP=F|DV^;NCmq(ySmzK^d~%=nVOn1y;hX6L2x=KzS`gdsVbp|$KA!% z<>$GsPvR{NZffb+_Z*bgA+EK1r$h$W_GNgZ=q*@Hb2vH_Eo&^nd;* zsk9EE)ja9#6;d=jX5-#U6kjvf>5az#Km|Q+qV?0eskft_x7@v{U`fys$WNOtaUz=& z*}UxL-WtgO)d{ZVOn_leYy^Lqkm*=4-_|{~z&iTPCf0b~0;F*rqd{r1jXHu7Bx2yB zoDqYqf9xh7Z+H_GhB-Ii-rm01;LCQ`BXw~R@4Ppuq@<>XkAQ%nRqy8Y&Bw!JG}=Zb zPccq+B46RZ+Dz$tD7OCa14OdHngdAcoB+VP7(RD~4iAyW-YsRUxrv-0|Dd4X2cMfB zA|umk?)pZ6(BB=!XnvX@yQQ?+AAx3PXO~*u)YOE2ccEpX_4o-M|-1tyCs zzaS8D^56^DHJK6*-M_CKVhGm0)sXqh#-?^nA@Tc2rFzfAK5J12vqtTAn5Ax=yQvx2 z+{qh02Pa+k-Ux-@Y@8mP5knOioROutG^uR|ga#x+5}m z3!az`w#OYwIrbPt}@1E)Z0}%40Q4<$32OVM!{4y7n!xj_=d1bNmY8g9m^*T09I%Wzwzk z%YMgD@?qbCiJ3WYS{w{uGgV=-(6mI+<-;N(*kIPP9X?|Xxq4#*=Fwa$jO-@er^C?) zb2S5lOO$*z$Ki^XpaS5tgE1{IYqdrzCXJLky0(P;wp^;!J&1qDf6 zH1xT-xge+70MCZpIBuTvO8MboVLySvIHBVYH(q;n#`9tEK?PjA|J z>^3+GJ#|xs>+w1IfBx!>=n0AJi&M<}@M<_ss>L@fL+$Kj;QM#M<%(}U_@_f1OgD~q zG6wro8DwBB4b0<0mH}CjR)_|6Y z)X!gZN|i5iR9UFqtL-H$s~z_{<6TZ-1ReMxniYF5q?H~%dQ{}r#vn@8kgMTcR#ui= z|LV57cA^fgPotE4abFv@FVL7bQ=uFC!TFdf9%iY@VYd1>)$-l#&CS+inE-qx+1W$6 z8uATZHV8{yO8%YT!)~fJ5IyJL6Cbl~tgeRVf*}&n(uY>>ZT3)&CKV)khF*IR5fc-m z7u?^BINJ<4urfX7KP?Gfp`yDxUb;SDbGmZ^bd>ELnQst$(~*#1yreupl0flmFm$s1 zCASlqyHJR-@%j;pXInnq{=QWJ0T+A!6ULw*zRT6rSEu_t&mc)yw7Szi|8CFPYn_-h z$$cV)kXCjco;zKRteK#C+1Iyq7UWXOA#ACEOAf0elBv}NGzzOAJZRP0Pkrne_5|0` z>dp(s3ppibC?sK$kXS~q{UjGf60Kp@bUJKuohvH*alN7sUfnnLaxg`GB+6qQWa}KY zPf$^%3t9g?#x71Vu!QIvleMmdo=tb93KI92?^o9w7r$vZPm7iusy%yc3&AOVeQ9gI zH_Ilk{|`5JC7zxwhGJl(Aw#F;)?M7LGC{A@;Q$quUlUMe#r;Ibc@VW1- z0a+fKlvGUD3yV<5aW4W{Gy}rA*3Q6z0o#U zaEz!f6AHVu5Qm?W)dn#zGwOL7+zR%gDHbuqDC4x;QbX16{Kd*-n0L4>UhLL}ucL1F%yNtL zB*^Kv_|@r_=}<1c2+PfQA!X-Uk7}rg{=d^t0SIxL-r?a;n!q}&G1y>o3smJk3VjAFq@+v(5Ua>a6H9q^z7a)&YuMAd5X^#aLIXXH*36ISMJeMf;_#J&K zSU?##Q*rRj`aECJMYQ0mb|TL_9;RMwn4GNY&TTdQ8l+%>aTO^JIk@3WI>evR@K}qj zZrh#1xWQ)V4537Zcxh{WoT??r@l+T6_>pd2PGhj%l1OlNt}~LEaUx%Pqe8Ty#C1Ri zGMpjvCaRxxVeU_9M#TotSyZ!Cq`W;~Y%2``p1)iDgGkPoCXZUFsdYF3G}FJ!HdJbM zHAqfQCKk5kRJ7zU>};eJMH2`n48L&>u^|7zO9xXmIH#Tnn*@Woa;96n0!%=0}8y!;XVhd^%(sj;<`o7KyU_I_lLH-)SyS@z;yagHiDh9@Z_d=vf`|b?zUyb2T8+oIA;N!fadDaM zx867&O!X`$McV9)TarE$TI>q5w%zC!A4>7c(#+t-*dF7`mH9(a9nZayMG|q&ZG9sy|kJq#@;fA0c!$>fhb*qn3|3p8lQzV|&7At+)+Z z8z$z6jO?LariJoXe!@zL!9IRpw09dgiy%pp4x3x4wXa*0$S>E|YGRF>QCIcRX z?ay|na!7X$T?T3XLnNe7Zc7b7S4oCRgz?<MO{78c$bTUp72=#lj{ z`9V-n5ItLNbY-~YUwy2zhFm;yb)n204_y7Cmi6r~2)GM)hRyAjgVk*1tGQ|i8z(0P z-=%+{4uG95-BcKM6szt!pq~u8!%>itkpThz8q|-@$Ezry2*svX`mR}FRb0I*)nWzrm-ZCa*u?#Gl`XKPr%V_Wuf0w$Q*a3+{$QuLN`pr^Nwd*<}V27 z=oYg+D{?x}fdKj=Q?(#bQ5*0wS-K6v^78UxZ{FA~Hh!TB94datXy)=MbiDa1<(r@v zaK!>h6;R@uKTGu^EzvMR3hV-A(c;C)A_J{j=b_9yFxN~vwb6jsegJ}`=Q9EV-}Lk~ z^T3gEZGjdxbI;p|gZkS@2lI*HB4yeKh=`Y_U6}m|LNbz)k6n(}*MMG>nwi76(VuA4 z=7$5SsF+;!lI5kPS3W@`UD0f*$p=`%!m>BVG9Zc0Qpv(}zk;>R&fdrg-GKjJFg-Y_3jCK^86wCZvds4T6r(`J)Sqw%Bco9Rk?*&7l(7v+W6(r%n52R4lG*M9 zc-!_iHsl~x3rX?}YG@Eq&X8N#;4&{PE~b!+HOYtp0~NksM*A(BAYHQFClyA6A;P5f zaWp9=W~$`AUQy*wV5B&%4t1b`N6sv*D)wvG9!L0Gy?b0{CLIug4o^-v!sB9M6aj5a z6<7iW52T(-k6V6|fp|Ef=gejCG1C*1R331bfVQXC)6)~{XnjUX+W!jy@dXdhMoXd) zy;3RNOoe6Yho-MmB6|PrBkr!)w9h8~#z;s=ws$u!kp0=J7O<4QetvK~@pc5X(rQl> ztCT^m#~rV)m)9de&it0Cu;S(Ce{gu`Lw0=D8-sqk%Yn)#u(G_|97x0?30{y=BFJ!i zwM!PCC#W}uL%Cdwui9}3=56wqB%$bPVgd;oH#Pr!ohuK}@<4gr*LQS!+S<}W`2iKR z?>{d9wKooy6Od@)SSs??mP*Xe>ofJoRR$~t#WJX3t{!(*){}K)&ZmX0pPRanMWAzOd zY8~Q0$qnbs+S;FmcxG^#4lnNPbWD|OERSkeT_J;FP}VC`OpL1R^6F}+v4(!(zVNZ8 zrsi~+sa&=FIMdD2o7nP?qK7R$^rYYHl3!yx+Q=W-oNCk2w^5WKK68ly^)HggDckt? zxP{s^+7t;fy2JSa@^GdpnztK&cv(ZPY#W{UYC}7q<3W-I2zbLC7)fPK4Z#?6y7mzs zo(YR?Jm5mZf*bYVQ0DyS3jkt}~7{IQiyyjgix0IR4QB^J-WyQu;@9QKYA` zLwBzhyh4jPL8&y}B-#K3*muGN?2~k_5I#VY0+(8ZTR_bPc#1%~+yUP(-l|I5{hLRr z$F>pK?C$Fm5Kup}=`9;>yKtpszL6s2wrVdWC_(SwF`ItyInr{PV>Rra;7YCZf>vrPekJ_Xo3~t-ypm%ydesr0R4*KWKly}gB=_Udh zN1(C?$YkfQ3-NGrMt^~4Y;#%k=1$q6pDnOziPd=rs-Nuen@yNYDh7vgT8)SHX_gb5 z*&PlZlzyBGQZiy(RxD;>)~GPJ4$_vetk=9i?9(W3s&h7-cG~XtmW#8owc(02b32{1 zA>@I2X&x!5k|9Z`cHbx{-j-jOnbFP%%Z; z|%?KJDpCopb_Ky z{4?4zyjDTjjP zyhB7T0*Zu{<$&`}T^->Iqv3~u-lEC6C-cy1{>`jux{%LYVj`a^y(SH(6+zv(w5;{x z$lwja3$s=0Pp}J!GfdR(bn-|gTjk4^u3URx8C~7gsmZ{+yM#OZ1(m2}hI2ZuvCj`B zmE0)wr?)3^dccbvb(A%*p5`x9Hi5u+WcGF;)6&_Q*HXZq2E+>mZ*=2cyrh*6&;hVh zcu%kOL~B6S!{K!IfJP3#r$D5jvAa7qSokskL|I6GLc`0beX_T;(a5^;_%aQ`b&oMH zj0S%yI0I;a$V3$w&VH8yX>~Mx7|mid2Q8T=|0rD|z>C3pWmWd6X?COKC^#t&GH{dO z@PdHpJgqYha&qzjd>Fx6ccj$#fP&bn_GqptR!X7-8{pV@!8kye_pUC_(;P}Gs#tPY z%4ZZPgZflexx_z&wfAX$94&kmua2gWB%D5TO;&7kf~!&fv42oOWC@(FQqu)rpg2IT zp5V6vhSPMRoGYxJSOLUgum@M}ZW|vCoIBK`Ap(XNpcLToUTmK%p>NLkz=!->loo_N zh+4TmwE?YcYs*|9Jyu5;v*!EM?;c9j_CW>J0IU>%deDXX`0+oQ>G;!RmH{0fJn^I8BZy6!kbOk?a9kJ{xTGI z;_fv1kxa$9*yIDixq(GL#P-z`lNDuq>I7=VBBLG0tJ7_S#_BVothe>VG3-+ho)HnD zHC~?J0YG`GOzSBX*&BoKalDkRO(tSx(uR!TJ>AonK&;bs6OIR#h6gW8c`ZZx;agflD0PT8#w*P3F{1Q1~e)_e{K2rz8< z-REsUBX@~PoAH3@$^!u;G|31KAW@{mqE$%OjOw49F+l{*CO`27*2dXcL241{=OIqs zapsEk_l~6Byxm@>{$?%&`(HZUZ!}ju0qJZz1kB}Up6dKu<(V*mF+}-#3W@xIpgag9 zaye0`aXeUR?NZLwD*w)&E(5n!yxZon=-uMj02vU;jMJ|E`e~N4D;MM!kU&OrwS&Q? zGFbhgS0K1Bamf-dY=^s*J5r>-r4Q0Qw3n6xu`1`e9otD0gVpcQ*8A>*K=n@m*u#>6 zG%J$VqM}npcIP`cT=;PE_v^+*_VojoYd8vPq6)XX2V_P?U9o4g@e@8R6g5N{J=V*P@2Al zC5CfiDudIrdXXg~AR zfvq#i+zhxn5GEw_wE6+nbldjITi>-(B z_vdTgsHshsY~|^k0muigk{>@Fw+G#a;^cVHFfjPdR@o}%MhiqnMtb`TLS$rQfLMfa za35PvgmseJN204MOvV>0bPY2ARcP|pM>Z@fhw3YnYOh6;gpNa~WpGoE?qdlMtk3s_ zQc_c6_$`r?Gx~nm$%9JQdxEtcWPJr0_E}2dEN4u7&z?PddEGyEVKMo*+;aXrXTF;9atxFX4?>1kIy^@)tZ>C?ym2nxo(7N1=iotm7aato!p zs~H?vSfDe#!vpNp?9thp;%3SB+JuB7r%Bxsg-`~zjf?8@N1?@VBIRA48O}QMMNmS` zd20H`3fK)m1~mi8KNA3xI=e|w$*(DZ zw*WeER@&WOm4PBIh65P_&W2~+(Qo;5@!o=!hZa?$#dR1w%?yj3`W9u!7uK&Ec{^wqW z1AjJnT|nK)CbDCQh7^a#q43d=!TVS zydaJt02!tZ2;ZS3#^Mp1al8k9YE9qJ3JC8sWgl4M!bk%yv$`Kx z6T(kp8>wGzV>s6sfsPqyk{1g%6PxuqgG4LUbco_!5hTdzGUrbLcKv=;$A45#zrNH| zU_Nou5-dZvyWCp`-d4m06HrqjN2Djk9*+CdEs=RMo#FIYCjE&9$D^j+9=*N2nKfA3m)`W`QSCI|rf6;dRbLEKB(T?$CO7lFe$q z7Wj)WfP|YS=8LJOq4E24@c4U zk4j1el5|`PeNrrwHfj&N{AzW`S+3}m|p!6QqpMbHPF1Km}{4v5$0acb* zP8iV3F=lI=g1vRlwx(7P{qR`ZpNg>jzW+rIT)%N&Zm$N*WHPYtnZ#N-BUn{&ftmx3 zfq{t$9yG3>WCjZpv-zn%t{u?4{jgbNgP+cCfEi=as*T<(Y7ZpsiVFM9Zhs+3>gBCe zucf};pV(#6B4NF>EKWFDATu+b2dazK;x)#pasy;bjWWZF!U24KH!6Z8T3yC))(%)7 zQ8Wh>At{(p)EkgBd+K2zG%_qPP#h!8&S_0mo07-ce2!lQGIWhPN(Qz=$4i z3?xk_N2i%jHW>B&@)SU*- z5>5iX7%+eyrf~oXg2+VreX(e*ot!d&55`v~dTkNfaJ2Rv#rr&l-Grv*8=_H9M5;p^ zx9|RjIPjggUEE!(6J7P(w43%-}>Z1D*o5Q^1r^Jj$ja%AZr%-`NrI@5nchS7pm znyq$^1Eqdi2l7F><3~6Pgdz>2YV&tpr)l3|!iS|$i2E&WueSr48t#+_%rp(0RO2x_ zopx7O*H~U(mq_H9O;~F0i-U8u;4!qbz{EuL2%YGP9uWy+2~?+&Nh~C7{&*`R!6GTj zK8{%=vGBM38Y(2Uw7mSaq=W;^)dt{?0Y%?#3YbzaA0N0ooGVxUuG+7^`L)Ie+5P&AV^`o{Wu+z9A9rbEN9OLqqmQGN zY~UyxwifC^a&^l5fc2F9Q!jqG#e6RqLZ!nAjmybeD$vuU5|6-ihW?A&C*4L_B;N3s z3Z=S!PXq|(T&=25VCYiJQlgIKfy(c1475#8PJ&{=VY=KBknj&dU&s>R|Db~Fmv$SF z&co&_Ts&4CDt3ym4DYhv7JhhTET7b0oGv3}U?mUi{d^9`R7vOr%`HApS5BQa2g!~5 zqrX`GmL1J~x1*k7u&V~i*QsjG452(aKS?z!GZTD_ZrGhB1!uhpxssS`l}5)R2)WFj z5fk^uL+hKSr(+SmAuVlgc6wtF9rAhH1P-YGQ3h7E!*)67S+uKLf#DvaR$)Z~m(^rr z*hjOJec;=Xn6J6s{Te;^J21*)tE#H@CPDop)yzFEy|@}~^qrvqk`wqnp-u*RdQF=} z21UUC35@STt}u9CM|~{JBB#8pzu)bAuVQzqE)Erkk?^d};lvms5lkv~g-%ddQex1x z+%{Y7;BPro4v3MS!lf4a_qDu$Hd|cwLW3tS1Jh+wC9XVHR#v0Or&Oyh52z0B+`p0v zHr6OoB0V8YvsC-5rYwi-O8NQWFe;@-$5%)60=|9?<~(n zfRTYj0qiLWZ*+pq!DK}EApko9TmV^IT@?czFL3q(*v3I?N+g9;r5-Ej^Z+7=t_Jj| zxfdM>Ly&x}dJDQ5hih)gREbHjU2Z}G37Do2r_pTD=~6AAsfG;^6_-hJe8cx=4FUo$ zFR%6XC_UK90idztUdsr$X_2Cuy<#rUCT440!n6gPY(dY(`#g=d!{?Mb*YSU6ao(sa zG40~^e1XsNPVms;l?)FLk4A-6AmH=)?ABW+CZd4X9upJuK_dVOI+e_4xVT-Uey?x^ z1kA>>+xE3~2;i~FPcO@JfW^dkZn_NH!NI6=u?cQ*F1OV5 z!)5ONa(iv#=qO)g_qWsNvO*fx`x36O+1sxI)mIRBJvmJJtzoJ=vQ}pI~7yaz%*IR79yabp?x~t77vQ&EAkIWb3R%Ij=Cp3rKYB{ zX(+eI=!-Lt(D2#Zc=>GC)8uB#OD)D2+6GkrZjFU@CRhOulX4@l$nZJt;@{ofc^89* z5Jk{G1}y4<5fSgCF>W=08XJtnd_Nd@U_4 zYa5%Ug@q)My9-1>Gztm|>bTMdhpz(MOYuVcV&ZS<2cQd%UfYtF8%&?$`RqbZF0Jr? zueYD=Xf=3{xVpO55^+iIS^pWK*j6s$ctbFE|KQ`*^d(Fx1S<6N`p8uUVDe|J$;?Jw!s!{1Ueqr zT*sr*ZJV;e#&CHzx=<#H9>%06hZO0K)K6>FXXM5anjFYN#Vl15s=V;i9S@o$x2IGduzT<0N!yis+y;P0~7%Z zOG}{drh>Hx|9F8`9m44VG7(bnH{MH~o07Iz6akii+ArIJ=`*;XhY--g^`$ z%uJV=gPtLyrmv5>#y4u{m>2?d1Qt>cn`^^wyS|xr(AJgiHQS*bYOF}rLrauR9Nuy3 zEzs9q>Eo!Rx%0w|k9)i%mytmf}I?l}Kh+cev zBzat!GDuJtV3H8YZ7^F-I+>g^&bWIz3hNRR!y;4I-KCW}8`=RBJXs-O-`jfE6M$^sDjQdLSG1^t$(wnX2$p!DtIb&bam5E}7sKR{&|8_i0 zHG82c(-TSs;6z0p*W5RWl_j`dncqFGifw*=D*&89_k^H`NB+?K9i=ypCm7)F3$sx= z(2&#yWW|8#C!~VWK$EmPTzmx@5t{+>#{jwCJQ&akGLY1Ov#YZmgpXs{IG}?y0H5sv z9v&W${XttN9hjU)iSDM8)6?wsn{NSDz5 zAo~n*0Pu*fps&&8WF6kQ>U~n{dd>pGXLMrTpZjw)pybv8^n>{G7Y%|RGHcxtpr1+qaIpzA(jgSwRiGC-AL-ZD z)^;Wc@aX91xSfruf?WIscrC{R4_F)?0P7%9A+zYRBEv3B2Gx97a54x4onHXVf)EQj z|IF8}QObNv4kqT~LcP$JFJB;~R&%i_;{NRJSN7nybUL-X3m(_ZN$xNL78aIpCiUS> zeJ3!(;mn$m;N*`~*_nXXKaUjdn&4QWqaUvr=wEJdBoGAy%H5%!WGO)7tb@%k>d=l1E)N?Dp zr<}#ZN!j+Z4G>|UesEZ26Tz`( zoX|WQ>~f!+S1|fZx=1^@yQ6o+{a18TOWycSG}Gr&wG+2fu3N*@El#gN)6gcsEU@FJmdZw5mzTtRpl#W>l-^wl@vL8necQbbxLw_qrlv$ipMRJ z9sT&%AJ7emR%yXX78AL`9viV5`g2>R)Lmb%$ou3J%Z0{l+yYNb;z|65OMlgQqU*uA z;J`T(XxKsmeX#(|?B-Z@9(sUIrU9!G&@;5Tym8({4hyr@9^)C*WdgJ-RK&i(Dpo(~gROk~Y zNHoOcvc9&yApcOX*hmVLW8Cps|uJ+XRhl*3@0e3RO zaEs2k(+R^)j>5qLs=N};OxcgkEfx-$-6~m`Ca*q>I!Nb_n`~Dd^$MIC$p7aBD7(gR zz&X>J36u-c+Y5ZhWO+qv#z0X}c<2Ieat1?$i@1baUv3yO@y&3T`f zqE%%Ju*w=(LBP-?E|1p`qB%`50nmp}AH!hA0B}??-+lp^oh<;D;gON8g$5BP&|f(> z2hR(D7bX~dO0|LGPPIU1Z6nDOF5j^kw1X_L9hg;sx%C4GauCqO`2%EJ28M=WAftwt z&$m}+Jq_P3eTUhVU1DxRIr8sr)ay|**iGAf( z!bXIJs>40j){$62SIAiRx*>XD(+O5*$MTSuXuacJ!`Y@ZjO>WsMppQG#DuH@*@irN zCsGvVH2B^-!0#$@88z`AH|nHM`*PCK&0}K`pf7$n`yC_T-={!hJtQ_Z7VIqKw{8`C zvsD}*L_Gq!R6J-;^o&cu65jNKfUW_Q?D0WGYbL3=NBzP8IjKvc9%YcV&+H%U}kIqV4u z|1%t%HozkQbI9^ii;w;0AcCBnoS=|UQF*yB*cwclX z1lYr+rJTTPH8eK1c5$>~0kBcC24JSMi;H50JPEuW1RQ-AFljm8-Bb-fg(t-l0r*Y8 z01j+Ep%Qv-2O803G~9gNjhQ>2DI|xcQi$lc=s#we(m#4;kGgfZjVKy!jz6|~)c6>e zQ$x0aa;}PiB;0(;p<;Y+bTlNpEqiUQ|M5KmkOd9FZbT>KeEj+P{J`_HE;l9RtDhh0 zK#~Y85Fro{k*rVFBv$PK1onH=I3PFPvH{;*0PJw2@O$#{#em@YpwM!Ld%Z8NqfoDf*A>Qe zzK>JOs(`gEpB^1a{w?Qe%gYk{CY$rOAz6j#flpJ-f<@nnN0|5y462M!@YACTVF<-W za~qi)azor+mydM!5;)*EaL>ciG}CoT*`Pkjk&0^l>&t6)3Td#y&LpXv>!wrCloS{;V&Qy*Rl$6 zBqWf}#AreJ*Q?h)z8wzwGUoA?VWdV{MA|IswfOI51qYhqjV6|1juDZaXzC2wRAtFG zrZMv5B$!IA_D^43Xb7*(?f_#MaKwH?e%jvB^39+lNHITt*)26W`4y-x5DLr6+Q5zm zp&jvqnOQgBY#RE4P{_igW9e=i+`Z(958R&2U}7Aa)+8PBETLcI@R-J!D#cukUOdx0s27_Bx)x$0?kfT^z^=ylWMpuTAg4|qoAW3_Qi6+n_ofo zzE1cmC|I?23IiRzK64IG6NMhPp8&8?YIp4pr=y{upit4!U_N{HIVk8js?KTSXIxyI z%ze@ey$3*a0ERd}@Wl-X@JbHaaQnOTw#3A*w*lZ;WaL<`I@V`eg+$4o;s-Av8X6jD zzl{ef4uzB=vX+&Sv1$$uenlIz?YGbV)~KR3!R2h%#VcblAR1DgAxMTqM*(Y@K{t$Y4dNOxbB0|Dq<32(f z+33b-HUkKqb~PtRRn#;!H>Tf@yEa%aHA99uOF-->{B&k^b9*~9GJ@C{Y0r_91gv!r zetJJPITN6y6h*bDCBjE&8+$1u)1$ygeEC{C{pawtu8f?2xP9B@x!WXxH;K~*tVdIY zC;OF#F++@PThXRg5u+y(vx!5%TeshGvXO>>rRN}-OaB;vt>^Khxhy}zmg zq_3Zsc|w8T5d@UJgMCz&wC|26^n-Q$A0I_vWH0a7Mk^~Q4BksBq(dIP%%b?ckP`Pu zz8;w#v^#+!N0^3&2A+F?HXdb=xJgIdIeS~YS^|MOGb<}OC+9k^CbYogYKs;mg@-WM zZxH7}o*Vk}XCtT@O0?fBFX)iMyyGn7~18wZ8`NYUih<|}{9o+VP?CX@gyg z)T#1?`$4>5B2V!0YNk~hEF(fz4Yg7h(&$f&h)%&mY1)C zU;3oW&;pdtI8wJ<~=L&8XI3Cq2s>JK(HHqhagoShx3$yLS9 zrg-yWz5+_dz`y`})Z}4Z0IsigtLwddj-E!_I}$U-xeV4?$bf-b&mB4*fP?p)>zWnvHS%VP0=p-CmRCk$a(kMBlhljWY?P z`}V*RbG0&@7a&i%;)f6@MIrOhXJeySyW*Z1!DnEpf8`|0*eiu&2R!!7ySc=@q-K*b>|4hfEmi}O3;^Xa&B<_!UyPPw-H&d3HP-0i!;Du(}z^tdUGna$s zbTHdh0h0(MmICgX`ZCqGr~7k6I5;?f1`@r$y9Ivk43G*zIHB`eS-S@f_x%9`9Gd{B zz*i)+bav8d{`28rzOFY(ME6RwnsYAQM(qFiZX0952m;C}`|uDP@GN3l^4Lj(sm_iyMzJxJ-i zfc5o-)4mpsLZU3NIj0#W-a9%3SpMu;yE$k#t5&l|ekms&2*cs$Setcj#!M>OxRaL` z6p|j0_T!5M3&eYW!}JS3(w&R_^V&qz&+x?7p$;Du-h|=95jZy*%k`hwV z-CasZN_VGpcb|2i^S;0LjBkA3_{P|O?9IdGx#PO7wbop7&IOG29JP|W9Hw`Tjg5Pm zr?Z2-!EEFTLD{Q|VTM;lSBZOidXiI9cTP9!u8H5^6%d2x1vmE_LQQfqB@xrx4#*>t zgYTnj4z7D<-BWp~D035H+Yjtr3O>bPb#cuU+|C7!NK5@&>vXmu)~tJ(;}~7)CKbd< zN77o(Htc`xhqrm3t$D6LNuf$iQMoI1-j=MF%CbAl7SbD+P;_n<-)QM)tJ&adpwyQ$ly@(A&Ni-a?$XQQp@@ycOZ0k z*tOx^wTn&5i~II1+G8=Nq?cpCj!xVxte?@_%#_u{W zj{YDf8E*2P@~v4=&NRjh_H9J9djm$EFEsOV;wBY3JhpZq%C@)7W0RZs4J4{8MhR9v>L-KmMx zanNL7=ip!j;3R#pM&Pfvmzs@H%l|}!ukU0#yUCy~q!NH)-vPTTz`z;+&%OIh8x7Uj z*$JBCeBIWNpnn4yV5$LK1H}FXs{NmE(NN~wVkCC;R6E+-Cgl=TQ@aOy5Hdd!CMKrk zo7OK>Qz8j9=_`BQnV4mBD7~AI!69wq!jCvoqV22DR((7r+}-purnsZlR#Bh*hwqqH z4fC6n_E`_~Y%4X{jK}0;>mxypq)%ntlupX!ol#L4U-&fv=eCTBV!-)7WQeoP17E*SJOWmqG{IP zX%dNoEp=AkvK)Q*srb{@S0f`M z1olu;QW`GQy9t}KKTiu93DB&8Q|Siq93bUKK_u~=pVuAC)4KdkYF1w+|F-OEd-Mg3 zjAG|k*e2>~n@W;xXIf7~7j0S?*xAFOM<<`9!T^=<*49>nc<^Ie=q(1Um`vq9sOyZE zO=%0aFA%B zr&Hb8`h88hJ?wo8j=(!;B0+Hz$>Yoc<~`HKf+&H@ott%?G0pIP)0HY%YMl3f!ZP&Y zcWHs&Gt{*R^7Fd^;SSJ(kD!R@o82VuN2DaXsWPByQH1`RPte_uxp8E&@x1T zA?FVh?Q@ZarUVG0v?|1eUO;1wKF(w&3$&IO@63I>o^(oq`euO_57#Pc$BD4JuNz}J z$Rq&sb9u*~h)E!@m#YJ-wL2PjfH^mccDyO5EV4m013{EkIP z+bBENJXd5x1*(p0je<|9w{O25lJ6kKu6B!s)Mxi}wh8IxjTGqzM@31vxbP}c$)bvX2_a|@12+srVUsnO-MTFj84^;T{oE$t9Pf zNyk&8B`piq=4j~K%^^?rHpYm9N=$kN`gD4Jmfbi7_`iE-Xt8X{-Jq@5$nzp=7im;hLbTa9vf&*bRHeQ-*!;3N&ENLCN?I9 z&^1%8;Blt0@$d*8zI9YoOz4jv(%RaOeQ_St_q0lkE+#hk^b|Qe)K67-C-+S`5DhSC0bB*;U@$AJ+MNWaw2v z#_vViBP`I|Zq$N?-So5|w97x1DaxW)di(tRzcl{(;msk6$WXUV5L%(fd1`sjoc5O`DvL(jj zet}#y^3Tj^h{banfIo!P)J8GddEGD00t&yme}b(e^NE8kr}_Ocdd2RfQ0 z&t15fv}(_mN>^4^ynN|4V%#h*-FQ{EYFhCGo{=;+3^v)kugO@npvU;oU1cOjA3 z-dT_W{;)tsHuP!PFFNmRXIQsM$QSBLxJ5k=m3jO2jMtSLs!9Z*y3-s~BA|vmyF;0GHeX)ahi9)ju6PQsK}5{ zx_)h9B8t<25!6+HrLwrq3g5!UPWdHb4xr*6;B1iaYoh_(LT9ie%GT2A#nVuESeZ26 zeg5a7U1WC_h?0SpoPFBp6$mgvc}0cm0vU}cBSZ=bBT3(_eLwYYSkI61#?|Cn+@~7W z7rbOCI`4dXIM2RK@@T$f^SmfCVf+)DmZ-wg>S`Z=(7?=+aB_0WU4R?&|N2$N%~s;* z71T=ZtBjb(!n{NEheFjVbq}o8&|gq5H9AV-=6!T$oWeIy$<>HztDr0+@ERxZ5|7*3 zM@K{Zyr_0Wd-dwoo$6QHYUdn{jzYLz)lc~;ys8gs+*Mq^9yWe&8B+g7L}~UT)@XN< zK@|NzE9J$J{m%LZ*?+$s30@|v>$)Xf?Wp4~sloyMb} z@w)s06%jm;2=+vmI6nQ=7VmC#}Pw}4bSoAl15<{c$ z+Vp|M4oVbgFVQiS<>w2ktDlChdB>g7dTn1;S!IQzoUe*GHLu=C86G+<5h^Jqx03U1W}YRPKS z*g&pk3{-nA3N`Q^>g&XyB!kbqd;^xROvT)CydLXNGS@h#Lr!0IGK%#y_KYUpNqNif zq4!q2HL$y&_DXJs4GB^|g$nD1yvYODiqHXO#Qo^w{u3Gja4Z%;6D)5~)JdP4&xJ?F zU^Fsj7koN);VLF%QPDHrL!`l z_l@3&xZa%n5-R0BgfT86s~At7Bc?ZY;i!F=kw<-2HpOn* z9EEtN_@6qhhpg+1+pFGhen51`3H0C6GB7if0TZ)2J5H>yg)35qg%$k!oc;fVhM%X{ z9qPQKF3EmfYQ{0Sl|0cIflAg-W?30#UdhdS^`Oi6hqL`Z&-K_3E?=I;tm@R{(!LBT z4~@vc9L%k*BpAak$$zUFE|ACAZZ&32C#A1mI+x3t6o}nkQe*egBzeEb;%EBY8-`|U za_-xk_dn$0++^wuC6nEa5vO3fPg=?r)~Pe}_^M%L#T7f=tY=4Z?kw{ps1H+OHR~uQmPUoR(*Jn|sS@@sQ+cZ%^}a=xKPLi72kuW_xPP zeg2b?5WVH_sT+RS7j1ke{GZ`P=j|-@P{8R|N zd&B;8JP7t|#{GzC?!VzY*qagbZ;*akcW>|2>6L*jK&rIgz5BH_(;y)$t2!zQOL%%Z zp?D3<%An7R3s@BVQW}A!V_kJ=DF^IBFf4sQK|uj7j_q-L&PxCzz!?f>xcE7r-AY(5 zbvW>g`l=i(XqD0h0TF<`fB`54y5P%~*Pvu_JKtATP*C8%lsG}7A}6PK`rPNne%iZi zvwG=MM?+%T$FcKSIXTp>Mw+f>=+}&Ir#fv%_j6=4v~cS@KUiWyy`|(&+Y%yyH;Ed) zUl{Wd<78SzryFC8+%}y&H@vDZM^aW05A)gaOJX^fD07P*KY2U%`qyJ#i6vrkNgo&( zy<+F=Mol;-#(#xf@1xP|59`fKo3FPHHQVtB2WB$SC90B}C`RWf=pnxD@)xQO3 zR<9G3X&_Zbyas`&cQI=ZCp4GC#yU=Sb>^Y79p5aiNp8-(*kvev|T(W;A<;E|pNox}G7J=L`H%koz?_ougk@_47OMlOT6L3*J1X zaN*C3iL1!%8|a=B4zd}JFn-9OPF(&}pi+4nIUe(h9e4HbahSG%^1Q-!86RZpf|{DK z(40YaXTM>Evo~V(V0AD@2ZZNP!}-8&0CJPUQ<-S5j~}lfCbm$a@H!vd0y`}bas%yk z5o|vZ2@c)I66Ju1!0?e{u#9AVb#o}#k?5RN2txoAu^o_KUg_@ZYv}0-hJ^R3 zKTZ!25TDtvy#}AiF1XCF)Dd9NZJiz4bb<|4LE3BpTJv~PwHvxFb!uJ9H~H3tLITaV zV@?#Ybav>|FV*&{3}D#+f5S$w{|=JI@u%NU*pfNYVFa{Qgd4obu`X=s{0+ z18<;vWA)QIiFVR9?!u(Gx2QFlnEccMC!>jma*T0D$D1)BrM&*m;y%2dl1-W7t62nD zZJy<)m-CjG=;NR9D9Rl+$pH9Gg1BQkUM`Py?}vap_`sgvl9S8A`-@_yJCx^-pEDUa z<$He5$tyS9IUyre;#>h+LfTbC!f7x)(xL8=Eor=BvsWX|hdMOQpbj#a<#3mV04^b1MDV@E2aMmH*II z^8p}CvE~D|x#$#%K(S|yb0BHxt!nQ=+s7sPvLy4~`l=fxL(1=0*JV6rtL=GCp4!Av z2R-}{9xW(FZjjBF6KQGPAr{n7g|9FhJ4*`z2dE6E4b)biKX$&%9+9h?~Rs#&xQ=KYAv`qi?!Rz4@ws&{>FWaz7E zMqXiw&GIq2liI$BNUBg!rfYIy|JbH!3Pg~8O|95t;zjW>X@2&=(bdfEcS-E9sZj(J z9NAwo`%-$xHm|XEy`oqfU>)5w{hQUV+EK^l3sF&VAcY zm?F5B<3+5m^RoQ=RGj~ilD~o%Xfkufs}W77wh&E*V`y=soriqQ;8k7}q&B1LrV^fC z2m7bw)Um2>qQ`rBUV|7xDv*0u9)w{=f_UTCxt#W>;b&lG`OIoo7@B3D;g`U$!)BI( z>c6=FXBVfNBc1EO7W>oM>IZ>E^y|GQK)ZGve7(AeNNr?K+*==~P-mH3_lQg77psg# z`~a>nF-qwbcH&*yK_cqHY27}AO8EKnOaT(c=dAi2?||U|Aa@M79e~&51E>8JbU{ca zz;oOncmMooE~p$>+8K&eg4-0y;B<}P;YeQ%>735a&ArKgcLhN{fi&6J+$?p>IB9oW zI6EIoD9mL&kID458eug73l1?^fT|(9^f}yS05Kg-xEVN)*Ik^@!I!4MZGh}y({lmr z5WriW=+0AVK}E$o;Do5(755ilV}yAMF1lC1vNjswCId?q1r1+uDXBTw_e;ymRx|bJ z2kXu;cfd;%OHO0t{2pYOJw4sZYN<(ybTWtOs)hWi+y`qrj?+?%813^*rd>9ogJGgj+m(J{MoFN&j6<-pAp^S1gumVyg5SSq1E zf~DW@*QaMx*I4ydofBQe_74nzH~b8AP9Y|5&9~lx6A$edblg$H#YT7NRI=XzEyA() z-w?zt{EHLxC;rtBZd*Y74fSds55k_w^Yy)}a4MFvv+6mh&{s|rj-90|^=h!8jCBL! zl%?Y1abG0-P-I!=mB^0c{#Do_rL4*mc>=%(0rR#UZJC1Qq|Q_x4;5Z z7ImlSEw5+$e{3=~?XFVfTAlUuXaqH!ijqBO`|;;yx6*(x<9DfMONXsjNDdF1ys4NV z{-x9pl`TlWXm(;~fl!g?sb`?u@_J>DDqJN--a6_ELwnh8VfE)?ZN6=5YvsgJo|2La zHqN=l10^K-d?5j@d3Z6Tp=P5ku-)KPNy`0s+r!WPMqG`PWaFx(e(Y(~x#q>JOFWb> zx!bBsk6b(*@JMZfc3pD|^V;!J<0PTvR_5`w#ZxrqET-Rrc4JE4@S zYcjtP|7bh*p~?5QiSpsYd)QK(vz9}5s>NMi;Y@Y*_P>y$93c??CUg`C);YCbbai#9 z8DyoTfcl|mG*Z>N&=w|t7J2?d_a(TMddxN~OfQX_Sv zILz)r)4>4ngJQEWBFImH4))Zwz|~z~Ct7}HRw3b$6jYm|DW*;n_4R#;qKl;`Eo18`#Enx<3#Z&cl&5-m$1b@HM zpMj*{@GZ*$dV|iCbb?X)ahGYeDm7Dg*a_K}ja`WE-4_T)VPBtU(b9LTF~vi`fUz>^zUK2LC@fXecB1cN z2DbamL4t1>y@EeF@!}5K%#Ay;flXz*k2()(J2IM^pPzQfX4J~kgg#M>i<+}R&re)e z8C5ZrC5bKkQV{j4>2Sx?Fw);DHT}jo zWqzIQy=+ZkE;co9ztPKv?9v!z z>Qnslr~Ybx22p1-#UZJBG0#dT(cCgQ@fzwyJM!bD1dkRpPNT+5^k2=P8)gx|{}Yw5 zR<|kD%kwr)=I2>^R9f-Da@MNMF$4vhfB+li8rvO87gK12w|{S`vMWTA9|>=d{? zRP^*;=AqsDt38|!qnd9qKsXv*3!+x?B~w*lr0b(v()A6o`55ua_2qPe1Qaua+S9tK z*AJ2WbE4&*ktLXGEjL?($_C&l*mN}EHTLV#dzYt&gfG_tCggIUj9I zo57awyaC@35h*JH4&jpmCb7<%$g0;wSz$TZa6)a=Rx|L`654t0oDkNfYsNc|6|k0w zQGF-B^QKdPM=JF1{&9~Jf#SW3ozf|Hcif3UH|Kyc9wrOLo?o*8CloH^z#V}Gp7Yk> zp#e;l08)bl1x?}PDneN8T}p9ZK@z%WgB*{}_Q#l(X8inrZ8zobD!ow*tiP!THOt2t ze6tHIK4B;WY>6`ZEOj0PdXptWeLw#BR+vxc5KC!ldFiu59naR5j%iqU)g)Al`{mNP z*yWzLNF&z{FYP-_vN7_C=Z*`VL8lNTYnA)ou40yQUQ$=Gc;LXvH&?pFX z5&LyLqfz+urQ=qm@-oeWJ0{6a*fWl~)^7&+h2Fl!XQ`*ID?lH?A4R@LUF*)Z9PMKI zAs^bNOv&Gw4)J9UX=S>8PIp>kiaE1uYW+Xg#goIm>#~Hyvfr<+9~-(Qm{*hgVPY@& zg%J<8>I$v7;!&)Xy$Yq&`FhzDH4yYcxFrSOK9X)HcIKG|AzaX^wQHJ@mMZlHv;bcaf*u0)gEIEenPcK;N)*%raj4&iER zXGa?-og<$JAocu0#&;5Wx?8a@We2+s%&3yH>JVWT^p_AzbZELDO^)r^rf00I*9gk2 zy~~o6cGMbrzfvotW5W6N9;SxwlgtW zF+BEo^4+~5$45KR&RCR|ij2ISBmOz(MHiDoozekQE?!>4^IfH9L3{RlGNYyrjeU+b zrMl+Ew8pj>{a*{3#kSxm<=JquVcTOq&%{2O)pA0fH-dQX*Xne(-HmH+GHb~v2<`0A5lb&uN;n{0XU(Q7=cdLZlQt;V; z#jlT(#f(!Ccy@op&&8!>lFvI;vdtGV*c1|#9Ss6~$gUFz*z1Jq%knOuwX{ez>DM^eu&5kgsyKNY)s0sm%5*ju;HjVtYR-$jZp2mH;q!mE> ze1_-mDzGX%Kiy-qlN`>^IXBPP5KaARLmzs_rpEd?zbb?VdyLKbv!Wv1HrC_a6&AH{ zlcfGYIP**;M+@fyIiq%@OD7NN6SFBwQ|?)^?O`5jZ|u&tzjEh=^g9-zKbRF%NkWMd|kl;dQ z2k5L$Ahy9+?B8)$N8gemLWc%5lKef-^NwZq(EUr;*H$bR5?k$63glj!FJb8!4d*|C zjx(F-@HZenAlw<4&H+X-H(=32%t6?y&>`ajOB_gjfUI*JP$eU!JV*LK!p*|`r#83`vObrZw#jaR72^RnP7iI2wJ+ot)Wys-Q5XL!E24j11@d3 zzoM+)5s9#9#bsp|-7incIXFh@zKx8IUIq245WtJbsRE$89%u)lRf34%xNVmb*z(Sx z-#L<6>e8|p;jXHoK?*YEq!-sj>}P-^(VZqs4umj|xHvNKR0Y)ofcH(9p40C{@GAh_ zbd`4|PfAH`x?mI|u^#@q;~pWT)sBh}Jf%*~JW3z~@YZ|)$FMI1 zqw8{kwp~Wm9z7g$uScvI8Uo7|oiz!2S^5z8pv^~lcRC#tT^{)PBjADugtjDL@kT~R zBY3NRmx{)X(t?8DV`DMU`tQk6=L7|%5HQy-t*W%LCO0KYX4#h%GVUM5tR$u-HeZgK zo~?18D6MuCc~K3Ur+b%Ha$K6si3AIYM4EF3%(!F!cG6V-8S;~>dx1L93TNcbT0`7B z^5xw7>kQm8x|5%vHjg&VWg4hTA5m~i=lh{~Xy2`C;|eUeyT&97!x-0OuED7zoc4{e z?)h0_7|x<=ihr2?d_khFzW(&Pe_kaLnjTF2w_lpzG`|mcu+Py?`E>cosbt=gbvrF`)M8z3k=U4WS@cf5OEu}7 z^Ad}9mG-vCf*^!wA3-XB0(%xj&j{trc_#=ZNCARt20ZX}x#c4uA}O~?BxYo^!s10l zIlv@BQpED|^7+$R%=omQwdf{ld1=txk?i|8m6ny*yzvSHDApY%9qxY8jP(UV{7y$( ziEQy3IsgIZmtGvE_xD;)n@Wq`ViO4wG-sdK9PpN{fO z9i!|W1E}Q-O@jvu2)MSn1LS=a+i+Md-1ihwz zVBqbvS8-^9%F5yJT;qJb-@nPgzh7OwG+5&tcc}P3OUY|(=Zq*BI&27p4Ga&MZs-q_ zmVmGZ)uJV+&J+|Cfi7bJJVfXS9TcsC_-GMeNXd2>(Nl#-NOD6Ej*TmFii*fIDo%^Z zZ;_GI;hY@Lt`NMq(Z`}EE-CpKm}|jFVo7OfzK4T3(lIRFWD}KE!sFb^|GA7K-}x5D zla(9a8<_BppUxs3_qN@}C}{CL;^&V8dVWG~?knIMBC#C=HIHHR7fe-4NKF+rHl~Ga zITc#JfS|Z5OjuX^_-Jkp4?RR6f`Q@@Kbgm4_r)*Fv|vj9?HjJ?$^g6zPlTvbZuvgN zo zIWJ(Z_%JGo`V$(GD?pJOuT%yd{mgJeAYw(bI&-{t4@yseTf>cFTpzPJJF-%t*LnqZ z&5wTn78VuOjVZL+`gH}gbC~7U-e68T%Ebf3rw%Y$kylm*7sf|QnHo_^?&l&9qIb8J z{&UrsmxS9i+qZZ}G3pGcigK#(9*7TJTem8@*QinD-STZguEAY4?iCCM#i8QoCj&`6 zcn~EU5nES~g24w7gBm3|bs;aXxzua@agMEyEP=@+IK96LT(9n?fpEs$TohysEHFU@ zI0qxEtL@M{vb@jivBBp=J+yQk+jG=eKM7#SQuQ9nFTWv-z{#HO2Zbs>_z_ zJCe0~kC<1u35UWj3@e8Kxak?2Py%yFbn{t!DEB+2SRTVy3owBWIZwXx- zxkF|lZ$yFbri78BlM^?ryiU6+cz@uFbSz z#T3APvZp{t40xW&%`(w!FJMIjq^X$qwubZ)#9GncCvY#W0fsLlEZo_u4`;5h@9hVO zG6q)XdvFWr<%Xe)p!aEoUidRk&NQpJKWkHU@c@iy8yic8K9x!KP=lNAOOuq2Iql-iP|bJpdC|I=Go*v;vYu$*!0w7+bjL! zl5?Zxb_ISc0s60%1VUS$?vMIB>*Hn$EKknRW)$kh6$i|(%%$4 zg>*`9yxgicwiu*)@ISu?UBCp&UZihliiq3fwVOcf1)QJ=t_g+vUo00M^E*L&>RaAOL`paTG^1X$8= zAui?cR!3G~TPRi1jRA7VW%KVAJS7kz#)A7dsNRp>oL4*T(;{-NK*d)Mu2mo`h0f47 zGMKFw0|i$vR0qp#)-bpGBlN zl@T^I%`TpV;sOdH*hsCAqVi{LbRECFnGc@(#zVP?CJ5qv8hqZHjtyV|TyGS&9W4wh zJYK8Ce7;*7bN(Y*MD^wO^YtKwq2##Yj@42FH`}aV?R^hLpr(OU{GDuddHKDW8MYwk z*umtXeY>l_pN*;N(OEmfOgcLH@buM>NV5rWRzYO?5dUtG^Z$U@n{PZoyNq!S(N-h; zlE%+?2-Opq(+KMdOy4WuX+R=y{T--L&R~JTfe*v&enE2^+Hfyw*V9lB4od%g2oLPl ziV(Yz_+|HgQbOUH1m7ma~1Fd2vm2xAgyX|9d1o*Ztuz(l{EcI9j*WGzq0w`eJ)Wcu8%kRPkVSOIT1b})M z;~%8wO{Z~(a`kQ#um=ovmUfO*KLBO2f_4cRdkt+*ss@i*Oxbz%2}rP@U}+X>Z)$Ev z2*D7cP?4|{ApJi&IqA*SruMaF`DNg67CDY!SHLOR;!%WHrHU z(W@0>f;=6>BG6J5*xKJm^y-k@rzFlH!zOI=rt<| z#<#4JL2TABc4aH(T!A)+sXfU{4wXqz^wWNu+v72n2+{YQ2L*ISvdceH#{()}LQ}9u zqNn#nDDz&2@ey4ni?T+WgSOZfhYR@;^B}h~v&@mhNVydS6txSmybO(uU@9vexX2v~ zI3GSBY})6rJgZ_uC&ZtHw!4Hs}K9P~I~ zcqB0jc0(gH(V`%6q8b1m%{^F3Z)%*_0UVl#pMz~V=+qlvOCTc@z(>SjZMXnsyD=UG zwg>6xMldcW15Ii4{PJfnED0EYfCF=tz`E-%D5ZhA_84TmV8{){E|=xBhxOx_(D|-d zyO=L_GFt^35}R!TR&&R8K4_O-p&|^t*((Oo0`&CKB!0*!EO0qTwAqlu=mTU2mITbE z!^{vvMS&~?XMy~|{^4QsUxwZA5hxwIhh%T&a{zq~P+Jz~15>?ISR5qJw7o*oAZP;l zGB`TA4dMmnjT^tayKy031j}qhme$(Z3KlwX&|C$ppBKP`Lv|~8X8mexL}SRwcAv~p zq6JkS;>!YkPb9I2tw23haBy${lkSxr-Ce#wOhx98gI~`vm|e8Q3xF>)=hG)oyaCnQ zT^XROv|rCWh;;=t9KMqaEVI~+`xD+2=^N`^z?%SgQIv#1sX$xA#nts=F&-ma5!jS~ zjmb4c6+BjE0cd=)*wnLEpYuedq#!?ii(Dxb8x{8J_kf9wlEk%b4S9kvF5pPJMe(sW zmfKEp{i>!@!kmu#koj=_74e(dRe%TrAp`JumVQ>VQ8p+@_8qyIK7<~LI*w=S-aHw* zo!B?Yakj$c-BK=IiEc+pvyc}&nuo8YC&p?+dPCu}MWAGgHkeE)WvP4xw;n$r?k6S& zb5O%k>j!Hg-+Dn3+(vVx`J4{@|5XK;t}HE4cd@gvf&EI^PQxNR3|LFF0;GLUC@H^z zzft3N)t=$&zC!G*6A^nHc$fE0hVg2{)3$s1sprL^Y@*39%Cl=%FpG4zx?N-DrrZAa zYloFoRmc`4qyPGh`fK|)uIEvEEhT=r5~=$lO(L_-=Csb6YocN=F-+#LBsISg;SDJn z(RRt~cYF>ExLAxOp85hOl_EsF1u>-e!8%X#IzM5j+^`4nyzqwFP`Bl9Bi*-7D&9ZX ztprg`hS90s7L2m~=2k}M0pjEWJrI6O)#WAE@Qgm&xwL3Dd;dOBr3+d)BoLF4^aS{x zZAzbEZz0ckL6?KdIrb>pib~9C53WNdIK!yRer>H@8a6Lnt=+zdh_06HrrH}|>&sDQ^Hb1``H%G8t| zBnfT1dw8bt|oX`y+2#jG6#t{k=qCxw7simu| zsL0xG%^J$2#SbaACp3t_TN4cp4Musi!;=g5_}udLe*ItppUX`f6Q+OqLm1iFV1&y< zC5s)v`RHsx#(rwj&qwl_*i8eJ^gaw^aif&4(GIKg%n6(`S3a!SKeu<{l`jZJb0Yhv zoyL597+ZPlVyEjnWf~=2SA3($`zYC3!;6~$fd~V#0SBPqsB)&;`3cPnl;HDcVBPTq zEXZA5UBLw%X2st2C=_k#OOtJtJi+POo2h_Sb_(oFQ5+PAlnCurlGO1wuwm=T1@Ze4}0L#BVBmJEG97UDI;Sb7(t}u9wMp| z*uabM^Zx7%GR)5JOyt1*Fg60hy@Gn^GMq4yavPhFasfJpdI%%o5Qgdt&+8eQ^eE&K zfLsGrH0Zttf-0SV!xlntIG9M43|kwJ0ot+x2W#tDc$tD~C~%MvJw6?78u32DhXGND z?s#jpBPvBaw7@zK+zkg-cU1P~ik6tg^P1MHP*Z;V9u3e@kdCsP~FR#CHHd9*n`q1+w1WpJ5@xT9}?&4#5+qHIkO zU{Qf3==a#2K3uZtaLT<4aj67QiJLnbdcj5Db+M2vq?>m%|G~Co?}*X;O^5sJ39gyvub)b;al{Cy@=e30PGfY}>Z zE|LHg4wB+TcohiQ9a6k!@Vjou3&hZ}sfS0Op;=YRGO)f`X}kOhRQC|>AR&wcW<3iq zkpVI|0)#CcEDj+b1h(*X0D<7`AVvjW#5US#V(V^29Mi+YV3VMc;Png8ZVG?We<8@c zv&jZ(YLsK}V-X}lg}t64pCsD$naJZRI+m`Jbx)avR;k5QCnT^Q#qa)1rD+!%_I!eG zy=p}99JC!=P<=vvsgg<`C*TfJ!(*XM?iuL{DOgC2qE+YCR-Y4J znLvzaX&|5=^aV%`Nua$0Vd`*Xl<6Y&wq8xoV0Y>~%S{bVZp%|;v?-GCh2JrCKBZ;! z13pw%^2M|hDiSuOwtCfbl$F;VFAA*@*7vPt7`q#?q{-6mJ86WoYVjWP2R}C_2{Kfcb)qs{i;NKEmG)%hg*fs;%%q{ z8{69aAss+w?76tO440bShr=JKL=m0&b3V|;dU|-EfX8Pzl=}euXAqN_YR6p`lR&7qx)Tnl6oh!ZBqjN1Sn`a;Vf7JgDe<3Z+zufR|Xu~FgI}< z5^MkrlCrZXD7@;j@vlFLTIbd>)78?yWlfS_`uk~#$k=7OQpRQsimuTBMgqQoVnTq{ z0w4m|&=>U#_|IRwejQmoI2(X@^QPjb@c4!_H^v;zstDkw6}Rd3Jh$iRAMu@OKN zzzepV#p(q|f7)Z;V@rTuy;e2@yjk$dS|blSS%s^EUD0LD?TdRURd~uXYr+k1G(1-% zX9htSZ5g@wNMXvj`5tTAO4Z8z+?_MaU%tK;EP<6-rpC=ehYPQliO;bZT z+*$W-U@9B~^E(3u{XsdRlA3<*s(Ei)Pl?lE+V(UcyR!60k$jpX{-Ia>vA0A z7gv$QgO8E#dn6qA@CDZY`B(VwdCdRwfBwIIRA`yn(o}xuN~f5& zf=zgh+US{0(Xo;HrYz&?OR_pg;jdF>0!Hy^Ry|5@)l?YP?Pg7Ox?0ZVPl@+fAM!tc z>*%(=OqfZZwMLf;6_LdbP zPw8~xx^KAs+E+}u-ZEda#M-+1gx|ydO;4Poh8fCI7vmVYvWt^? ze7LK{KU}ohRb*$pIBt4!*D`?#CBrd5adhz>71t!tq;Eum5+x=3vR-IyyuNa$Ka{bG zTh6c)FKCKap>5($?af2GSMU%bQe#$jS!U2tHbSl$`N;(P=M%4Yny;3WN@J18?*7GY zO^U@(rE^eFP`vvgfqY~2ho$D+uVpbRfeecRi2_^ft)KsBomWn)tae2rtvj67mZftB z28?Pe9wC44pb9&u6l_^$SLshrWP_+YE=x+_;v0;Vl`lSXA4;6#YWEyFmct!Yu zTCjE+-J{TG8abOmNDXc&B;bh-D-fv=0rEdtS%*e7Xo|LzcHTnO36|dr6(HC{9H$x7 z5BQw@9M*v?Hrhw$Wy{@s5aE1eq-C+t_&oj<|3UHdmY{=cZ$7v&f_s=n0cYMHzgLL_ z?DUPKwHHQ@^gAdlY97omya6r#IYq<)}5?GU2J=vV*};Yx4Ks>V4F( zi|e(&Qf0c%!$kFQQ`5p}HfyGYEiDucVZUBp(bOT^cMWu$6q1Zpl$Fs|+U-QW? z?gkwb)1u=UD^Xm0dqJSV@WekOfn}FkoJ?~qH>L7XEA0XFH?R;5LIE;x<@XR zQ>|GzguSu*SH$CDklLL6OPVjm!oEExd~ds1RI#BxSm!|(bNLD83EF{NU{s9qgTN25 z^8@ireu2*QG@Ak~NwK~S4mj0id7GgF+h`wR?>4{}-(BRDVEFK;(O1LC`s0i*#&$qo z-uT7?iM%$~rP{HW#xg(2xam`0qsk0^D+#Mj1{3I=A+hL#v$G9ktw3Ry1e7$A%jz*W z?#u7IQB#A2O;S=43u0mloNE9YZUcnMG$;#uezs2Z6Txf@(l0^47)WWrK$Ap8MFsrt zMUI0%fO9w?D&S|)4APoVs>{l@YSR%GXn%mx3_vGuu!of)@x~)2ZU*xv5AZdEyssH@ zWdNkFARS)7#Soifz@Pye!M}H}ZF|nhW^;4A!c#J(zP^b&5}x|I%7c)?<19*oQY z<+;jiKF$Tbd1(a&e4s`zLZK}=hByp^E*1G&xDtTr=*FO-hp2&&A`WuVFj{4QNM_td zuEjQ>jgLyqDg5o0GkW*aKvG2U{z6-o;(KD0@oTOf`iqQ0S9(N73)#)`nU&i(1XJk~cnS!rR&6I`}uh+@kjx zi_n+H>zgVMQ*c4>hfU+==@r-|{qk+p;dN|_kGpzxWDV-ICV!Sq1Vxb*8Jy3B)q2|> zn`Fv&uk#fj<+7v9RW>A@ulxu|ejufzc}F8>{0ens^N;T^vDrdviYjaFMt6w<4x7@w za#{RNeVT!s=L zwZ1@6($Uu!1#vjy@CH+2kWuyk)tGK5cdB+aM&FrSw4|98C0pl(o6}BKM}7KuSm6;3QU_*?%v zW7|Jo!$5)Ioj#Cs!DT zLBOJN>1>G7Inq*ZPvXS6A}%5GLj7e-S%wAce1Mccx}dt^eH0-rB(dS^-1oRQlikn4 z5^lLN#<$mLv^4_ zDP;+03Rz#}kZQMF95DM4P_B?>sHq?U=h20{3)4SFnhY8^$!Amz_>%%}DX!Lu@Jj_c za0k>lvj6is>9vnp6g$M8HuFCzkd}~<&*iTh)0YWPJ#pJyUOm;dY$xHu`v&LCTc){^GrC;pkCaw)^LgToYwR(-)y zzwY|oT|l&bOl0$>i!*7Tx!1N8j}zbMa*WyvnPC^5QE2>X6Ko(MBhys+j=w>E(Meqjy#i;XkPD!o&@*7d4U0q1^`#z6dU3p zQ!2pf4JrX_KE9e;ms+P#NkK7Z)bkS^g%Da{2idL-<9Pr~=ep1D5vYqWtTC~!U zwm+!fru6fy;w!XrrY`^>Zs_kVMt7GwD zU0gI27gk*nI#UPF7_%Vo zCXsMgqN zd&lMc_F?0fvR8IeWQ8UwrL>5)mXu0Dds8$tNl6+sB`qz{&@L^r(vXIxiiY;y`#DbB z_x<|)UeD|K=N{iLbzPs&dA`T-K91vkaTH9G7m>b}`-57%qN1Xv0_GBW72iML?|V*% z^02(X=%LAb79}2RQ0q{AW@7lxxo(QCk9%{fz={Hig+%4_hCLZBZ~HW?chR1=(K6b9 z#kKvas&7oRKu>xsYeGp3`<=$NoZ9l#WrlDtGhCMzChM7 zbkWA|`TU~Gp3j=M(r@*s#jSeHk#yDUlPSwHU7<#H(&7TUkBf#`oVcnLHc`q+)>T$2 z(BA|%;sfHaYP#X}0$A!547ifk>G58d=|YT#Q*5V9G43Th(a{R7dIuC8Zn>iX=OO-f zM=b!X=rdvl@|8Ijg)1jrDaSp3DrEmQCI4g2>AP4k4ia1u!uj0-*@G>I^BkP8Vs{x? zK7`q1YmVk$YpO1c7U1f%6}>b{5EMgFdp39$tQ3l_$X?XE_&#mR6X92y5h`&?GB<;R z_6;$3yOgx26n64`Tii+M%2_y}v}oJpv5r$2@BdN}m!ZMXZ}nDV=@^sh>e}}2@6M*D zJ><9$)XjOK)H0}x{j8f+<1C%(ifw9vCgOc5y;io8L!uG6I|RDN>eT2PcQsCb4=S3Q zUzqkk!BNq`5lA^!$)$5HGM#SHX6mVl{$icR76*%vM1imINkdY?i;jT}BjYKPUsIl& zDa`7fMcKE3o(DPgK0qFM%-{efrZC)PS`0F|792$1Ta<74-ms^qT2M$Ch#KJ`W1|E{ zk(8hP{p;VMxSx5yaQpUcVU{rR9)!HWk!^hr9$%q1qa4g+iM=2$X$06e9Tj0==oYk$ zjmz*(NS4Iik3`K0n8+22Se#*XUkz>wd4IsUDUg&E!WjyzHUd|nwCk>prGYhHJ5cVh znhaneWPJvq|H5+~)P1GI%c^veH-Y!a(B?95>u&;OlB_EL2Z!}z!Q7;O&hFJC36eDX z^Bop;L8=K5uo!t-NBfhWu8HE4RBt)1_VI0PK5g||^B?t9Bvz)4$0k%NqDDn2K)}PN z0ydl|TM3mI)}s%B%qU>(gUpmIuuY`2HdGVlrwsO;4S3NWdZSlib*84+k-p<=_O-}~ zVRk4xd-2!5{E&j)8;`NZFA_T}$p@pp%R$TvKF@MV(W?vHL60LxZJ#<1q{ zxiAGzM`p{kwc1L<#~cI~*ZHq&yotWbojzc?BW2}A1~-)TU>IG+NQ^?LZ5XP%ZruH< zLt67Bmj+l&M9z&YJwLU3%b9t(HE4{&XlV9vk0vPu|F&6oJZ-jOx_-Rmjef?awxh3V ztO7sKwyk^Q=0;qH8U1;=xwiqvIQrv7MLR|`+gO!7fSAF#(Wl?^D=*~N37<1NEebOK z1Aqu+9-YYMY#Y96SE22&PRr?8z}I-Qj1>o(slxJ1_bqBZqY3#B7l2k`wcJ0zK*$*t zc^a)z7*Gbrm#9oiEF(@mrB)@g&CirDa+F1eYV{OeU<6gX~Y8kf#W8A=5-**AA_zG>9#`w^d$!Y>;7TJEUUQ&!=E+Gl6J z%tybxs`jv9&$>70%_sC!xH>8l{ORer)H5*z>54pBPc5vWTQX0+7z6^hKzM zm^RZdH`vVh9ncjSyRoY4(ptE7+Kn-2f7x=fb`Gu8_+B-)d{Sv}V?e&xw~uu+-N~_B zJ5tvTH=kjh9UAiOIr2zJk$aP1@M>)aP;i~KLv%(mO=?y@W`xp3>sX}by;QL|T4 z%w6@)SAjo0Og7uYprZV1JkqzsfxR^C+StT~Tv@w?U(W18?$8gA=!b4u^XQkd@U=X_DcXy{}N2w#AdDMnU%Ll7-m3s zcXJN2q5OL1%dS$5u10;)?_1QyW?5&V3fEGF4xf%X+I_ ztRiPTl~JVn{Dl9{M_w@-EmsUQ?8umLE)uf?i{jsf`&2N_e_YJwa^uVOhx00`6Q9#Q z>P>nV?M8pQmX9@Z8|C^|Et}w3c%AibjRp`_1AkHGK%@N)en?^VVMCrtb7~eN`B%L}ecxUV} zK$hgF_fl0ubB`C3o|rBJW^}bKVRv^I=TCfD?jw~aMS}n>otMu^GiS_q5HXkHlxvS% zcFD$oPSDsPJSLipX)I7NI6q~kaAkk_>&mz@v}?O}RmT|H3b=pQ8u0g*+GFIqmLbQn ze(rrG zsA;RSy`1MncxZu9%;9oVWx64^O5T^W8kV=^LycZ?>yKy{JqwuZ_~fjG?^~QFif1$_ zX~I*kRX9-Ksck*~d-IC?cLNIRexK00RX*}*gY3mcS(~NPd0RGcl`{s9iy6oPTfWsg z;E9ml%AQ6g;`o+l)zIK!?$Pxh4Lv$^ljffH*gc8zJ2?MUrb50YODhsT^SF4P?ADQw z2V75iC}wOwaU>=~s4&t>ESHqpA%U6SOWfT(m2zB+FDw2KKDmEC>wm#~H^;coRtd~c z3Zm2+^P6n57nZ7bw?s!roGW0V=8Rgp+7y_#IcKJ`yW-%%!;hs{)Xj{qtJmvZPB7F{ z8yX1QY(1b~K6YPk{%YNjZ)5+3xWfP?Rm5z{HcT8CG1;FOv|N5M;p=w$OSc^M)P-JI zR<+}I{GzQ6(!t|>W;ib;&uCWM@@)8iCu}tHg+im7&u;dq>qfdo_be<3ZvBP%i(j#( z`xzVd;%I+X(IDW5PkVzDYu@a9lI%@qW$eev8D&4?#l|n}jiypk^8B^7hsh-l38fkB zc1lOYmmK-U^*iV?T7R4wt8#2^oA^COf2kvVUPfF>U&o3XIqTcEZ)6}L)seoVb*)X3 zE+gM0rYSOjOm!S5!DXFVmm0`<(AoNu)>T*(fJQw_c6VHJ8d zwTj)(PRq;dYFXuG9DiorOV9VuDjQEiH|v$}qlbqz9`mZEgl51z2sO8V*9K{xU!7q` z;|f|lyQlSgcf7912*|1YUbRqEw2u{#8ESONoB{T%8G-d$f?JhOjHm?HK1o#Gta2!1 zCruQ)%*^0R-*E+I4(0U9s&CiNBz#G$uiClL)2RQF`=xsBGSOGw#qz&bKd9txP@?(R zlYPYKa_B3UR~F-on@JMxvP#IB9ABGJS`ryo6(t=#_)!-8-%ZtV6HDk+AjqP+Ko9)=BUft#qlN5@4jSdqhQG^`8TnSxZwHOadBME zdKZ4$ISY1kXz!wcHT8o4jS2Qry$GxyY8(AvL;4#EqSF^ zKM^H(7(VO>5H=2^VCJJwSD|VGKF@7Xbsv&u z<~cxTt~l+i6^uf5Zu$8_Xj6*Q&V$aaQo<@9PT9C|BUvR3S9Yd_W?{?+fY!+oe+LCn*7lc!FlUjMw7X$do9{cqvRK6;Pyt~kGIrlztGqwraXE$natt0e~W zbwdMuAZ#Fsi^gWDjW{gW$0vo`j^|aLCWkwazmX76#W1X=V0LyLQVF1tVW44vECxW_ zs%CWK&SMme;$)wnd=CU2j;p$rFXRfg48iu<+&qO-tsNpE=%Lm)q1B8_FMa=hnG>Mt zYS{Zj022qv8-dMASiv1ctM&p2C;@EIkBcVc1D}t;F$DvQ20x1vB*ho_jsVa@Hy;pt z?B};!1V|jjHCkx8$=?DkeKVB!=#|N4Kw$Mmp#i$Coq0j3dU1PuuI!!ilMf_!c5;a1 za9-&*-r;tQ<72<+vrTls$cP3AINE7^il^ALg@Z2>DnRFi3)TMX*WJIX6ry2dR?;Ot zI|<k&0CtcHPJMK>NJVYd=#9!WyUb@Xz%<;OLX(y^zB&v7bCisC)o5I6FLHHiq7P ze5f@H=Q$N;LZ)vF4C?0Q5IhjQh8VK57b{1g6n*;FO>Hk+v0*+BE+6p*UAKdcQ0g#R zY9Ac*^7Ql^br=~NBaVxp636+ULd#AlT<|l)hPSXV z83_b{Hog#FjSd8C3V`wh_)R$czxw+wtQHe~_D9@YbO`1Z*xJ+X$r z7sr)c3Cv}PzZXR2C6a|8mVu%ejxa%VcAzBPn42^q8wT++3>wpzL2i=@!haA$B-pHg z`r#xMeI{BlS+8Obfnxv{<40_KY`OjD!m8K7!Soit%dWweLP`~kMrbC8D*|zh2RxKz zGj<5jM>M1c@SXL-8MVi8Y)YWLro^3bt$E^meFC$$uj54@MW17}CuWAfp0U#bs~9X{ zi0Rz1KN>irw5tFvHhjLwJkt|;EErTI@KA}^@%u};UTcO5B_gJXHQShuioAg^020+D!40f2#H%OrseHWy9}9x;cvlrqeLQiDhN|qdR|j) z$jk68UWJs!o&tY{OudSVpdj^tO9hxw5qOAX6)D1XJAN?UD7-yS5hbCx_$nkJ;ns!oF|qe<-n(pl+#Q`uMzvf{cwA+rVn~ z^$sP)I3;fVZzROA7$CS14jzVpB7pl;b1Y3_OtxdB*ZC(+5&InAa|YO;ho?cp2>e99 z<=I++pq=vL=)!{vAaugnUtyU}bpu|A*jT>a>Gf+?EQWvuI~L1a@+xet{d z86+ZMM%NZ1{*jGVxTg=GJ%%mg-u?Tjz{Nt?FN8a+aCU10I}3-8z15WY=*Z1UZefYi zDieMBZ_mNuZ)oP;$4N-ycuIJcSb6?j#(5hCVPwHEng4*v3N(`*rz9Zx*oY)PM-Zrw zV3vQ~v>xXsn)7x$E(2M^inMM2rVLyw3>-uEEdVFQ081WJG>qB(Dy9mIT5_{FH;`#6 z`I6Ms&QrB<2-}z~;WAtUUk#jJ?Po{9|3uils;}>cLyZ+f7`@6jm||xgey5vJCWZyl0}sNXV#M1roFHbq{!)bS3swKhD%a9{K-Ay9$#;s^V?tm9ycZ0|l6)p9 zKlwd#Gc(s<1&oJHJax#rpG`H>J31=AK03`IYNye4?Ui1kxBn`|hP1W80J{cb);u|_ zt$h%G@SLV5qr{x!lGTc}+X&y0f)u)3#AN4OPK#jB7DinyEm4rlrs0-VQ-%GjWbFdp z8gZz>c-kDn{QamBi~p7*4l1*fM@3K_q%fE~5o*@L?nb{O3UE-^Wv3)eR`|dgBIUUu z71nqaA<-qLq+p&xgP9CiijE~v_u#Tw<2!-SNeU})KsV9S#s&zdItbZL%GiWC6fe|G z4L8(1{64{ZH8l2dV}_sKHXS95y6-jeGxQi~M9EE&87eq!kd2-Y-OYGsLzsZg8K~?C zFZliY(=!FM^z?+>i={Z=3GV_UH$B`L6FZD!I&tIrLCgI3b~^}mCFXe>fum`L68aE< zy%7%p|KA6}4xy4LnJ9ESzS^nGeow@$T#CG7RP&NNPH``W2zX(Gi1Yk;RpPE_ntkUr zxvRlhMTqDuDo3FN9;~9Ql#@)-V~|L^apbRq-wFva{`r2*d^@mqSK$y~s=a#k>UUU_ zjbHbA;mG&eTID^#PoKUqZRSVtDML+-BV0EA%2Zb6Y%7b&uJtTT?EXq^zNveTA2@3X z8EY46ixU5NjE!`P$A*82G97v1;NMbKwq9aRMnaQ&^J2rxq@@?Q2Aw5SkYgbubOE|A z`EpzYt7q}MuY#spvdxu-I7LG~`$fJE>B2up^fm-%WZ&ndzMmQ(?h-03PB~4j&Chqs zz(sI@U>I48uH?L=q(Z(K5oo|W6UB8Ast{F=E%IW@PoF*|)*{dgLSP$-p(%FPV`b&5 zTpC7Uhj^Z~2lB#D$TAua0VzvcH>`cbf3x2SmauEKkbM!S1q#?R%C+s*ye~t)V!awf zFr?1zoUp5VaOERf32eAs4{hfQHlDY**Pvo}!803W9H}Mv9D0U=!35ueQO=DUZ_I!F z(n!)Q_?jk2N?d_IB`$I%@q0q5CUrCd4mkHKoE|)Qpw@zHc?_=8(8_!Uqxl#z5?J?! zHqhm~C+zJ4NXc~6%x2$iLzRN+p4#B_3S1G?a#&6y@9I9;SLcnek(-wXjbBPtJ|uLy z4M&l=U0hax0;`L~r!X2#wVN>q0T483L6ocq5m{i|0LH>sydJI%w~&A@AbX)#l0qc| z@f+-O7utWySdPAVB@RJ^#nSKregkXgue8o-SjbB1TqdnaqQ#g zN2If4)iLaXvB^Sbc`@FppOY;=JUfUiYzOyTPJPs+#(~LC6}N>px)|y{E5e3n6rict z-r16GFAWK8!P4R>yAawJ*|^hQ3)ivI&{ZW2B+Og518~FWnO){ zQD7tgel)V$IJw{(=}gPoWB6TwBk-?B2~$>Ht_pX2 z*NA7>C{K@4;xMXmd}6ZL8Z*e406kZ(SHK3whEit(|wgO?^|!QdtL)&>&+WCif?qe zTX{0@ZNZ2U3&NLme}7Gj{+;deO9vx4JYGyc&!E4Tl#+UZGl3%G7$koF#;U*3|JsR@#CC-6*8tBNSi56%8)-F(Ka zLXQcGA#LsAiipp%^cD`C!QhWyuSq&t*X4 zipBmy%Zn}m1zv!>fapea^e9fQ0xh81p}DP1JsFR^-}FodazD|s5$gHol+diSYAP(Ta|NNi-65;JEz`B6#$GGhnK|OGR z^|I{|4S^6H61{`kt-CHMOsx@jx3aEoNoB(R7089)q4jx}%)tl&B*H}nK@`#Az(-~* zEG>T{9Mg?{{`?su>Oi(qAVY1aK9*i?V2ng3YFGh(jL3!170OpUN`Tmk%+9=J6?x*V2lg)dU|55`>b1(;R@ z4YROK0bat*{`%wi;r?j|i4aoeA63+pB_0|G(P-i$(4j$J#v@APUbvE7Nv&CINN5&_ z$k_bgmj7LI|AVI9_7U{A}zC@9go1UU*OJoxATJF zjh_@CRG-1{1#R=l;pQGl#>swIm|cX`4Bdhe%8xu-6?U?U5l@&HyHen+BaRPG$PsvT zQu#=E`1z;NVZV9PH1r=Xfd9|;KqB71^^e z6!H0wISB2UYM;K`XQ2^xN_J{4zi=ZM9>iQ4;ELVEcNG}SID@=MncMDrQpVz=fc2fe4xTEDix2yp9#Om$Po%g%S^M@e>LRKD<|)EIsI=N$=%^ zJclQdwB2OJwJ22cP;!*XxY6$4IaZAB)=D3JgIN+Ab=&TDYmY_$`cuU*Hrr} zxUYwy0LR}4@*rkzZXS>i7AhUS9);{Sy4Af2Ia7J`6x>DGW{BbqkAYOw6tbTkABs2{ z^O&{B+`02su>wf~$x^2ulpQL0-8uw}Nafe51HRDH6o|`CZJHrcZla02iUQ%RxH$D2 z>yhi7w4{Lfd)dDvm?F?m(|%7-r$xH)@bG}|*8Ie8u+VVPzoi*mU@FHA!g2du9}08? zx#&9fM-WgOnH%jOwFs=qh*&(H0v(lS^giS=@|}@5naa^5fpCcfERB|*)Ygb6D2J)9 zYH5wNXhE_@Po4%0{f38cAg;^5{*G9IP|OHX9w@G*H-9vI`O$=Ae|AkqE;Bp~flpAQ zl|C25se|~;2t2Io0i%O_$qD27O%$LfaOabN1Z`w|yd$&Kp&b_;qB4=7a(U$AOra!5 zol#`FOGUo`VA;TN8hThB;279+;f+rKxWEk?YJCb2mSAQ00r#^3<>3}%$Q|iGO@k0^ zJqp{k+m48UC56DWEF94Tj*}yJ>j)NvY>Gy8*Ax+=LfQ>*N~mZ`3b#-5FcjTsh3X6+ZgQtZBZy@_t*L&}W4Cn%l)o{_g8<61V4K)^bMFT9HYK9&XUXsIbW z%wKYH?Ph#PpR{!=Y5pONc*xlTfcrQ++rT=;<_lz10)Y2o<8IISh;4wxI`iqx`h zyqS`Cn&L}|m55t9zt8;k*$O;tuhy6QY6-vFK0gANUp`7=GjKf*e}Dg~*0K~n!KAV| z-HfTOpBG{{G~WK~>LRfYi@BvUbcl-(E*>HIKi=F;I+K`~7?2( z5e%7-e6&!fW;fd-`Dc?9g-5ZLmO}<0QA4uUD#LWb;2Y~pD#%ZDK#M#LdR*%BMfm~; zd!5$%=n066g6CXAL&HsU!lpm6-hd>z?uezM7|#A5Y^4aC(<%h4OL13YqAIg1xwQE@xHDa8A;!`aU&owCnslA`VR~Y!5itTt77oVp-Lxr8M=;ZIy#>LQ2Cps zZ`ra1I*yXp_+}_(k6RC44HdAVdIw#)LYxwyC3%A>zzu*=5Q==;t%)f#q8l15!n?x{ zL;aHMJ((-SL=)OtWOsAT#1Y_;8O<&$OGv{L<_ju-U!YFry!wu!mSOx5F%$g6vW!qLgh7QQCTW--Ao!*lmlzlt zJ{uS_x3D0?8vx6$IF0B)o(%|MC?SG&7FaY;@UD}tm;zOXpQa||82%_Q?K~enY?H<{ zhFm(>M;U-EH15Gn5-T1sa{=s3@hT3C$-$i4I1Nc&N$^{k;{46@@rXP$+zccRSAF2r5G4VC*s{3!lN^ zQ^Y`*=rSO3gnt$j{vpc2RNz@+UJPIEtyl-lNxn824Vf>{;cpOeSVB)T!1X3Q9e;Ki zuwP&Z1pdKR6Hu}-9gUp2BNXP--_j>zl9{&mTQtv}y)Q6#ndf$|5`nSp4=6g|wzkz6H@Ra9i=@Vo>9eD*z{_f5IaGxSwHpfOB)C z6ILx!cwNlcXAigT3d|0(ycYqI}%eojZ$(+#JX(%BomgV>rEhOVyKuxya6w zH^R5n6N5M~9f(K*ufkG?ZtJ-qQrO(bzP?=uXzY$6M$~4hstHqFwUPaUmjOXONK+3#nz+OMj`4j zbcc_93VTvkSSO`N0q?D>Pv8AB*eripvCm>m|0uDKKve?jc%k|bvELvi0Lq?2hYpdR z8+F_$atNSqDyN8ueaO5YP^kmc$BCP6U0N(cwM1kwz${QRkpcveGZ+NF5wA-UhtULD zchy@+zvT}Z%;D!MXh_=GM_XI`$Z>ZU3Q*- zVqzloN2XGo;x-l~2~{4S9w67mn0tKu)?O{mM*wyZ;;2MWmq42G{gSI4Bk{E@48Yt)}Kz4*E46E8O-Plzs z-|}2n?d>fCom`lmizFyxnMGpResVWYS@!=#^hOGE#_c7L$L!=l@R(@DxdP2*37lMO z&2gowMiYZCR+H^-j;EiKj&^Yu)_wI-VN77chGSgmAI0W4di#!bNQ!WbyN@G87L20H zZ$QJY-Uq73O~fFk$HRv(PaxqLF&Kj9NSJJ5ro08r7tGyXD#iJ1*<=%pWIOheMwp=6 z=v0oKIH7~Ofr8zRoK5_xy5()(B1DNK3M>~=OaW**&dsfKZzcNf7(i~Iy0o{oy4peBsuE<+jMmcX0`;Jahn$>We9|k8*wLf z!CV+(n_HM_)uRtDPJ>qj0sBDJz}xu*U=(SP3yh4ZL2sxH0F4nB4HU1-=(zaEQWjjo zlmsm#H~hGr$FDC(yv5Ta@F1WlX+)yC9vcL2-?~L82Ji)|d3Es-s^vD6Q6|Ai)5sHC zU{VliA6`6Bmt)p<790Vz2KlqStQf#U<2@)J+GqI=rVopPQ_Ghc9v~l zPUhFvEl`{n=Gt%G+Rv}XUuK#s;>Gy)tF~{olJuI z6ME87Yzl+jssw--oc6TxLhLFE+Jp z-M)GAlO~Pn3Lm|rco%lFKyXH*5umUqU6q1z<%Gk{JS>zpy)C_ZgmG`{+s=(aH<|8F z*TX5oJZIe*H{;^XQU3wi?iX+UpV;7xo526a^x^;YuR3ngJc<31Sh}=(^a{c_i3{X& zq+xE{VM+U`05kG4Ek+~W)??OfxhyC*Fz)~mZ;`|L=T~RT2H29Ll-&Uo8>G2Yn#(Wz z`5}uN{sM{S4>8JutT(&a(~h0##ELr4ul@P?qLR{fLGPUMG=Kfn2%I%CvKan-*SK%c zqu#YoOwTYWcZy((9UYd6XEm6t!g zV{9If8zvg_^KGT-0TZ*Rqr%GN;fc`X@Xo5L>XTJt+b)FYRyoQYt*SBDrPJo4)zeq!{5g^Jo3%?w${1qHxW)Uyas%Sa4||4VV_<__U?vK7~i7arCu zlwCPs?^tt1td?$ri0^3mu_~_pY(h$r9*#A8p81){cD}gyt+Fb@vHW!Z!yh4?du$Kb z_o!P?T=`v}?qE9?EHzNSAoh5#Z=K7DOF?#Du|+whW4GtJL#QtYl?4?d6ss8Xtcx?3 zi3atayl`Om-tn)JyMp+tKD^i-*|*>nnz5&F)+Nrs@Z*fFUaP7mPFQi0cDu|IGcqs7 z@)~p+i{{O6&KoP|Z6yY1j^TP|Rxd++L3V;-91G%I3BVz=3hQD2IzQyNq=lFR)*y21 zz1UdztqWo2&6Z7@-l4CO1QpKQcJ<6xDc++_Y^k(^LXToE{~9g1O?QCH^pKd*a<0LH zt%YUN-sMu|o~2uSZ?TpN;-AEb`hRsy(=q;<@F`fe#9p2Ez{wGd;%%Pe;z##zmO1WS z^>ziuK8pIXjrBuwm(s}C1n~vV4NKjn)lJ3)87_hv z)Gca%zH9}#gDznFRHhrLAXiMhH-VX;S9o~1cReeLEm4pFC`Uk_q@X{9q1YZLeA8oC zITDRr(O=N|erc15KhH~Uy&W$XZza4=2-KN8ZJD}xJZ&PSW?}5MEGPAv)Lj)tQ6g(R zb9;&$KIF?#Gn9qY)Azi7lG#4X)2L*c^kl~_N1tW3`PW@b84Lr`t~DkzTKqj|dN^n^ zXYA~9#hkEc)E$G06jZMmcH9MZ2MIw3<7xn3)%fV-&Hxo6rQ9-95C;VXceAsrb{@Px zJ}u}9nyVt*=5w8I7j0Q3`}T8+_?1d)o5#?EXu!~9R4z8!r*N@ZQGlJI-m^KCLMv!QvOe{`nNUFq@!U3n&dN_W2ilcQD5Mwvi5)}&F^^EaD& zKMlUzZDw%MZ9UjeU|J9W2$K=w^@dd1fr5-e{283kvbXiIwz@j9m9wN>{SxFtVaT;{ zicJc=uJSffEa*^Y@aAn>i*cwCfm3?t&3~|@f2mR~bx#<1`^MF;X4{}(&Z8kujkJYo zwg=Cj(g$|V>`WisWy{bBj}~XImUAvl?{wzay8$hLFB}EP7S3^cUucKKJ)flqDj)BeJ{f=h z6>Cr69*v(xkJrQ-Wd}WDGHj<$_q}$PI$M$BncLwy^RnviRi)3X^$~{a1XI8~>+;}8>^1X9=Jn0OkCFT?)Px@i zZZs1YmwxbV+BeI@@PqRcxtVx*y5Bq+XX|$F{`S4~IT;n)h}qCDj*UgaW}svO&5&x=us4mT zsO$P`uus@!Lhv+Tq#%c+q%C1)M@)SvU&AU+7PT}qsD2?#_*S?lQVzUu_-)KUlSSGV9NCSCuJ09p)F* za;`07+Cy=fSeKd-6sX9-IkHl8C7UQKegul;=S~G?sgdc+y(QzV4=ix z$BK98u85ByBo05(&22j(FA^D#UWNkrT?fr6a8zQU4G1$e?K?CAFa`JL2PnDpZtG^s zN`BYzv|iDKE$MtaQd!~6NWQ^MOg?J>q=F?CYm!=ixqV4?NHI@{o z6t%W5>CWE*`u?=_pZtxetN;ANNJNsKpC7}JLYlq;hOu613A&g%Ue33pa{^+3E3;Dj z`6f{Ywi)SFEm;bRkiBa>mGHOAKVx?ilq|d0bL+}!B5*=3 zE*k6WzhsbJ3%ZyTUTSO3K!xzqb0oTD!?5M2ax8mMpKqU281F$`aWDeztbYfxmIAc% zii(Q*H7_eQcLiJXzHY01p3PX;W-(^-WcD6)=HyMmU%L|68uL#KRg`&@(P{ApEL(Ml z>#*@kl|7nmVjphnhAajRd3(IL{!6N%W$9~bQ+#gLbKjADSt>n~ax^M`JJO|X zaM4R=zrT)(_pS%sRx{9;OJagCVVHk(VEfQ@g{=L-Y9@Megy-IL z$^5OOcXpt%?8ej5!qwMFyLv8zKWC#~zE7aJRq*eOu?=-!w4tfN6~tb9?+p}XI>N#;WA@!#Jh80>o$ z%uLwrI1Ei6$BUi+*G^?jSiD^dJAZ2Oz@(AM8x5`UcX{^Kv%PU*i}^={cUkLb934tY zT(;(D*f5IS{<=naql*`3hkDK#-eIB84u-`J1nj7ph?iVQPapAa^!-@9m3)mof4|0l z&9^s}1Z4aA`7EZ=#AfS1YDq}TwM$f7kh%R#a!D{x;zDflOvy?fjVndSVhvO0_M+qI zDETO@+8E+!$nz!1XZ7GsAJ2h|kpkzB5)S_H9TlsCq^6z6_$FCn=L&j$mQx$&rfd|Z zMtSldF2EG^^Vwd3S^kytw{B5UlBzYi7P3~^+u1%&l&yOiIL*1tgx%!K&~xpHneF}E zxev7RKDf!cUH{nWaUfNE`T7_emmTEqUoqhwooGq29-d#+w9Ud-I?M6oPcw<0eMM~7 zv5dvDy$-rbPx6Hgj~?jrSQ#!kqr0<*Zu1Vt5Wd5H2-{u7nvl(6m@B=-OIM|U(3)Qrw+!&iJ2WS1B<@tW`P*NZu$)s+uD1dct`RF790%{_XDv+vWb zj9Wik?s~mfZ`whQ^jZWef%00OKG$n))mwM$I5d<#EskkVrzP!Kjw4#yg#%HSJeHO| z+F_pdTTF*pqZ|E95pyS+lp`ux_rx@)cT>CX+}647_0i+CTrmL#Ca(ftN{XZji?`q7 zd&_hwavsf1K*ptsD^p9UtTaa}ZEV%^Gs8rZdhJ{I5+kt>a3+2arEZTV6Av>1d@o!o zSRKOFOxG(c!aQg4PCilSZQ1)lab|_^j#K|WvFaaRP&YX#oR&-Gy89j{pYE(ykxORP z2n^V+-hTVq2Ie^CAGVgXt@Jtib4$`}EgfnROa-nwH3cCI;?oNjS$;+(W5*3UPy8_5 z*1k)oWJ|bp(?aVluH(;Y3wZTR`Zf%04|np&e-^zYV8{?+!+rc{i zZ0qmOL?Hm$Bgc}W#Ri|S?>`O@P%!yR0qdEL^C>?>JB+YQVdY*<&Mi(@ZP}q~^|x-p zaAX7dx6ZuXRf$v};=(KZ1FG$ri8@V`%j;pr0>+g??!TWLHhCGev;rY`->Yx_RU{TUSyl?HTE1bKl^x*}>m&v_6a z@1h1wh7}D~-9syThGXI%xDFl$6X9X}g4HMzF>&Mv5RBo>gZuZFdvapn)DF@e2(LSG$89GdjFe24BY2M-HQ-IY%kVuSj^70aUO3=t3hlXwx0cRD{ zZa_OM6SavNk6@(@NaxE)3s936Fe<|QZY`ibupmy1@}hi3p9Z0u>TECYue4M)^kTv6-OYVAcU@Otpx6HlC{m!$;vBWFs88GY@B6Zd_3b zJFimPB_;gtbuJ;zx!Gv}LnewT)GO`%{bk??E4==$dIjtiP{VZz4OCB04<_s>CT193 zU?A`WgOLj_&aPRzgRK};*chH7WPcIB1GKeV7%C{oUmAI@#dou9IjS7H zg^4)959~4 zlamFbhE&5$92~L-3ecrt{__TP7tNlo)=E=p>HJrfVa4yQrdu2Ys3+~twUyqtQu*g3 z+@VfR?J0D%?HJdRNh*fTL}tWP@s}_MuCq&Cp1qd|zU@7{DAEa_KRJHl#EPqU=}_)* zlq$sFc&^l(yTSQmhV+$!H$Vpb#IX^M=5OG|M6AW5VNOq0qky2~1hyLK zNFm!=_7bjEnCC4M(bNnBy^srx1+qdqKYwwR=Y;6NJgNO0d^@!4wLB&~j@KStFJgG7 z_mttd?Uh)->fl^F&p zrInt)+g=hBa%5T9N`*r5hdsl#*USK|zYTct4mmx&fW~)z$R?sON%1&?0i;r)5wUD zrle+USP;_YdT2?F@qV!+YFdmTmLx&_9PM}6#CQ?u+0^Rv>L(kJW=1_BSQ-X6drY@% z=amA7au5xO3>TAEN;o=Uf|EdXOrcxm`B(iM9G!rf{W5u60}9Yx?nygrNK6x(K^$bY z0TC4aS|yG$MTh1=zjmM%)Pr!R=jK^JEIAA=Ly3=ff506Pv14R6Iq~ztaFxrj<_NmL z2LZ~3nw@}#x1bQm#z`#6*$Zg63nvO>O+IPi=OH24b~BuSJ*Ma8h?2U-FkOreI^(@o z`FU(e+t5MyXGPG=e80`)yijn6hj}jg@ALzsd}lJsC&Nb=kV)W{Jx*@@0ML!_n*QuR zB#SPfT6O~KjvOp(ItYnQDEo;Wd~6gfoUyjH{`u=y#B#wFzie9Y^j5*Q?I2ipK#8cr zU%y_bUG})xFZ-dcrSw54GHA4yU*z;>zTirxMBpBS;s}8;BRDIVW$D-KhPuSSz<`Ji zfG7|@OzctepT`ZL41+)og8v85XzzM7Y@q~U7K*RNjqPAQNikjc%nfxMpsspl5g1TtPZt0FyS58jl1Tt^t&-`psqGfH1j z%o`scPssKFj;=vQim5Or~iiWrg{ z9?(yapr8f&hS@X2kb%B_F~aMzrx21NkrCM|5yitT*|S=fvy@#mQBsw z&>xu%`gsQs;E4AdK?rm?$wptSD!mFZBqp;XQ&WdQiUhw%x2Xj>WkUVMI1`GDQ9xG6 zavpHO$LchjA%_hRB$0|>|2g+J*uDnEqElX;AOv*flg>-n_aRGcID<9>A!D3Hq{C2H zp!M=bpAa|$egkcpERDna^k>?Q;-=DSPa{zf`c4f7H~3~plwQi4TAf?HFGUB6TWFLs zdO%iO5e_J*Az+56rpciddN~M31e7d=z=DzXb!@4=BglYz*+H&E1}^>{$X!TQ#@GZq zfB2HXXHG>*J89H{^3p z@xR6U!Vh_YPf&8R35B#>=~g26(S%@(++(+bpGf-HpuW_32Q}S1mQ0e12;3ST*RDA^ zwgDN0r~rt|msn20Qt2G$Zt2b^egnSgX5e{<$ znNxczK;Zz{ldJ*6{z(_IJFhNiSpcvzg_rXCP=A&pVfA40g9>c6`|bgJtT1?fkRsZ# z8d1CFTLd){E^N=Y?A-aX0}QqH-@jd<-vZ|zXJg7)=%+i4)$q^L2s{rUV1|B0u?qav z&%m?%`iNZt(iSxxJp&;^Vy0UOx;Q`FwoyTn7=W@ES@arAIq}@#O?0nt)EhB}rAWA- z2c->}gGM595%Y8~d2WISi~Hu!iIpF4a~Mw$7oMA*hN|=|$STwQsTE{*AXwPDQul&( z9_hgp%k&sO_cvBdeX6M;GCEjQN@Cke%f!jnCX~DA4)iemBgGg(b#;NGBV;8{2G)}f zgbHZ`?`WPKwVBpCr)8KBj(w)p+yn*EzoA%1$R&JlWDHlRIf={bWp^IklGayO=jJ5F@h;JwoDoBmj0BZuM^eVcnr-xqzhY-!|79fLg zp(QJpoPv4H$iNaL@R3?eaAM&vn_{j31|rt*f_Fr96=4aHpbCXsX2ym1OUH?{9CSQv zW%>X*JKx$lE#mNlO%o7Tzk%rhp)FzzLRLAYFa)yWTWbG@Xh4>a;R~VGDn_O9r$`5z z=B%{zIw-brQ$L~-Ap|JoIQZ)`1==pO{z-}WG^mV4u`?1-?40d)m;&%2+O7=875f1e zcI3ntl=9^FqwB)wmXMPp%ZrFq0+mu;`XJs$svn^-;8;LJ69~RiGN|vIazT!D24i^P zX!hU-S%OQt)bego8@1v%axLykHKH)m8u7G1$en*VMdC%zw=V-5SC8-@aAr5J7&-c6 zO07c$dj}Bi?jc7}Uo|oD1VoDvhW3Iy6JnKuncfN~oNY8K^^J|cbu)$6z!(EQ-ckn! za7{!CGpB<%y{H;A8liPF|Ud`99OTPT7$@-agoU807wrhL;{O+3Zd5> zy#@SMSZKsQnFM2lG)`!@Th@)`f!ss*V2~pDr$45=c1r zH#cjFA2oqB?g`m%)~L3Q~g%ZV9^RMjBkZn zLEk_}rX@W&`5M`Rgm@F#Ocb}V=l-&I96#bXAOf$z5JwkJ8*>V3Cy=Aev2_u|f}L1) zh5(N1P3r{9m=4`QVY=O?EIW|ZbTH`;@T!qiZZ0j_Q$Wh9&$H#FKo3F|CpO)|lhOl`8R}n|10u`l zN?q|C{Z#NGX0T?|-<}C)&o5{*Ug~eWd$KVbN-bhkjJYLX6Av+ZoN`KkOP=~nkZo!R z$}ujB-!dpK2^01yvWIwxB#d07P>6Lm=VWr)V&UL(q(H)n_O@gsk$cC#$SsPKehD7c zc%IlKya}ouu&T+6CMFcPXq9cUWzC;k;F_7Y+R7b<22ZaXo+#Pv?>bU>FaLz$Z4Iv@f>8FivMf^Nq z+R>49EgEqUtV`JRY$uF^?O0ua1z8x0l$4?Y$28|MUni+lrS?XFpg`~yRFtqtkc7cC zjIp@zMRu8hv9-zx?Ig?TVBV%6@Q0ySMC-y)^IC-l{UO=~pZ2VQ{(kORAPt%Y6MQ{= zeW^xu9Eh!N($YTF*YD)z=u_5PMc|bS#h-NAN;%64k=Q_FxQ_C zvj47~{cmuf*s_wHO zizAiWp=-O>3oX8%$v2-{j~|>eoBp{rb3A3ZB{PKEZtB|wmBm3G)z|4oZzYU!?8o6( zu=qi%cd4HRTa^mLs5X`>zyaT%6Z9=nC3 zJQ?UM*PvDz8mwF5%bkCY0X3hr2Fwc>g{33|GH+}>teDh6GsE?DPG=2N-P+YJI#hG) zS5qEKY~;8eN(ojM$*wc{cbUxY0nlP|?ZKMqKvAdD`-ZJQuj7on^1d>I_xy9FhR-yD zcAEl}PjXxiEh=oRaH@Tsu3npdYpfxg?NMOkMb)<9O_Y)FK&^4{nCHsAW5*=RM)}$q4=$Z>Vf|QGg2x!OEOh)L z3+(HqQt1=)8gqnYw{4?XFU_BX3*9;D(ofeL(jpCJf>jH*#?~sO+&!7lWO<|Pd(V~3 z*l^DLS);mL0#?HwSKXUV@mcgVe`^~D!nM0}Wq!&a&%VB^HPY*)<-`^C{(ikQqZX;~ zA@w$<4a_IRvLQR#hU~|1-eFKBB<0^*`$PV)xdve;nDRXzN-k2!gbjr^(R$b0f+aRG zjxCnZR0ayuU`pFDA;}A&r%}qIL_Al@r7g3i^uP|{}%2D&;OvAS9fDw(X$?|9Y$kc1f@)>dT zUm1{)5T9J|HIm*vLp|J_!P;FcsaxUyIMr+4Mf%Ect19N*r%Yb(PttdvTK;(Y{Dn)G zqCA2UURw5D5~AOJDA_ENJ29b2^J+M+k)Zj(F_X)_^V3rU$;}O;+`?x6E}PFX0@*E8 ztas(J<;B-C@b(U6Pu8ZS9_TLS0B&w-a{C9eJUf}fOZ%CBe-)}W~5WS zDm2;pXw4%-pOQo{=Y1h4H&R_1^q~e%#^W`kTfu6K5 zbsET=vmXZsc*8xXma){3!ski&zxr`~q`0l;{BykxzuNrPZ~xMjdWU*xjr~VonZZ~i zg^L^pdP**J!Rk;O%4bLa=k@9kCbHfp#P@((%$wJ}$&fj+e#fp=?cMD~<1c6_@O9j@ zU~A!X<71;)pQ+_tFtR8(Zo(v{26hWFhebHt(TS|3^6uGZ)_bIAMT|l{KNAThtJLy0 ztx_1V6eYOCz0|#ZTg>YdSD=uEvSdwteN@g=-xrIifqKHE=Pnp3xi5b4M~=hVmV#eW zZ6f(*yX~Y6cSlOgocxY53r;EPCUsXnjJcr zx>P5hcDVEN$#?bu{*3O3e}>tiQzZJLhk37TYLcSgIIYuuoRj0p>f%^ z14oZ>RNl`{O|3=K!`NsNLDRmY)g`Mrv$0Ur`J{U5vt>t4nO%ana(9Dq^x>!L)4B`g zfl8jfzEQ`hL8tfh4UUpSMa z4(sSkiPO;V-dV;J?0s*qreB#5S^4Su@$~yV$0cRST)P>rzK_+T!?7!^`ABL{DIht?Z zqm8U+EOmDe$T_GDI97RXlK*S#H}=zeSY7rsFzK`lZ4`9s5bg2&HKj_De5+qXkRS0ms~S zruMdvYSb}RRcRp_Z`g-y}i2XeOOshiS=bIdqn>XE$?${7m(GU=Z zD)6^;JY$F>K_(lL^(N`(MTDPpchgDajRjUre~Xq?h_)Oy7~tRgZ?2_t55Tp5!_acn zs|sCa>pgkZIcu5!E;Zz(bzE`aEl1}5Iio_fI!#hbWWip*xIox{*U7iz6%FMWJe}36 zm7AS?%yB4g_sTHeHT%@{&f=4OJj>~dKk{#JpABZLT&5O1ZdEC5D^#EGV=2v``BAo+ z?#tld_gUk#v)!pxv5J|SG%Ed-hIoTmhM#2mF*NUWdZ#4P(;_L^0r@4?N7eh9Fg7khs%IL5U_Opxp8B&x$Db;t-m6O`a$VelGIwdx5`4T>>_Vz;bZZ7DvVBE6P?|H$#oh2GhOC@Ms9`%=Q@CK379}WQuzto$XZ~aR{-dF z`}z5;U%y@+eQ|jrxU6S6Wg>PpNnOpTC1>jGf~>SO%E@$jA_P6m;98Za`bYttF{MQ;x-c?4Evg=)JpER*DF?3iy6Si1np7B2if1PUVJLtuD({YwD+2 zRV;c?k=~#PxXU}s1R>>Brg{q$k1u@)mFt(fx~aE1T(c9ClM3in@-9>?c$C@oPuX5g zP(BG^)Eo33uCTLUX7Uy10bs6gENt2?+UBdPQxZ6T^vYl9y zStvZWNFSVq!B4H$bKxlHjD66o&4DvQEnG5UHmvz5Q}H;LQ%B)j;NfOcFF z=P>8rtao05AbJ!c1L6{mt~k-=qe&lz1`M!3D$Lf_y#xSIWqhK=uz*3o-W*^~ zq@vgiou22J$)v-t!NLJ(ZLKJl)OB}vhkCCf!hLAuu##34!7I87%ZEZtAAr(+&q%$_ zxx@X_-kC;NqCjL+oP6^mwCRNL0GPGypo6mVE&}?*hr9t$OrOo|R&oY1WQx<`_*!h4 zm3PIN!Dv_#5E+m{DZVMGa9LS}_(=k}-wAvPCa>=ec^R^w;}H_k#bn{i(sKYd@?fI? zbg8J36|I2m)$qHoPnEnQ)PNoMAU2qx5}6kKA{cW-*|REu#}F^8 zTKa6~M?5u1W(oZDYsyyLVXE#-h&b!HM%L5oO-rAm83D+PC=&s`pbhXhY#IJ4!j%z- z7TXm=smMk)>;Alz0_C{~1?aJ0P*UhYMTzAjVk&Bmagy zxja+20?M-#mKfy~kWvDiVdB^6=$d0NP8fa#1sGh9DeQB$W~s!U5)upIi2#NGux<2s zczKDLOI}{y?Yg(y(awhMRe*N^%$&dB!Ws%AW?}+_E|n(&B33D^%ZUb9xd}ie8O{a& zYeI|=i~Y+)zc%K*S(WfwXBf#ryY>AGqLA=Nb|3Tb=;B%PR_m^f_!ifnx2&yCkwH}W z7&7qogi(_>)XztfSA(U(gE$P~`W}Q`cz!hxosF&`q!8ea>3d3KMVBIaBIBiGWDa0q zI4#dTwraI`j5kle-f{|*5blE6`v}%}QgXLh`1$!E{@VdfE}@7JdoO^sgkl3lQuV>R z6$;lxF3!0;emq$zI64MxY$~=T1zIu%?WWEV>h-M(KwDLForwM(T`LNL_qpZ*JPpH^ zoU6@NsDMpiHsPbFP6#db<0T9Y7RIKzKtCB|MdtPcFpNO(5t{y?g{Md5jHOls&jxU^ z^jdOGlDA9m7WsM5`ZWBMA>@C7^JZealm$G0USQCMy}w|Hcnma}QuL_Uf~`O3zWjk; zNZfy5BBKY};cJDEX8bZ20L({*ymA2B27+F*+~Bf>?-E7& zBnY7i5RLW_aKPs{Q>zg*yyflD-GChOK56&AU&~?wMF-yM-$m$O(}>|CZMxj~3_Z{& z`^*+%e?~%L4KfKfU;#KFj^D1{1DFsWwE>(F(pe{vZnGtR>q`7}PlPUiT=e)4t><#E zfeCh6w!t!w5$>@YPH&jS0d%`LZZW1A5?~zmI}Oc*CwSpsYl5jw^vjcHQ2YOwa%* zkU}iWY`yd3+wE?E(M@d<;c|x+EiE&qt*%|8#{t)5IcIm3&U;0WaQ}%5G;BTr-(!3a zG~BSPM>~X!YM|#*y?*=B283{|iW2kzCyj#5uZY0+wz#o~~-|K^mX-O2xNbocc5i0U0yU|--Bx` z-1)z^A%$Q8pd8_}vJ6WdS9F^0qRGu{`->I3tiEaltA=RvHVchWOonIZGiklb&Y=-D zi)~=j;lgmcCTe4w!weG;a5gA|Nl1f#IV3LYHfxr{a<^zn;D zptq#8cjEu8=li2$FTWu!A-8~V3(a+VQU39>Cw7lJFraYf&Yi?42FZlc3LX|4C+D-w zR$Dh@1{g8$YbEs4!>o@!)7G@6R@pnvQhpL%dQr!hkm}#1`v6{cev_f<~b_+}jVq zZH#155M|y>ea@h6eVR%i?TQlS%yywwa~jA7<)t`I;`wRzZAE8nQs#(VD}R|C$gCVV zt5(OLV-$xz*J^fr&Q)y6fHbG2DZdIww|NgthQEfj^Ae_Vb>D~E3N@(o(PZWAQOhv& z_l?3q_!e#H0jv;3X92U`XA=_>@;HX&(YHU8Y9^1(_ZDZ#TeOYX#g3@uToJ|_S9XAO z@>2d>Z@`z2AJ5`P(kF3}S1)6zv+^&yEUahVDO-$3De>=Od0Z-8ad0C|Qa3Gyx z?q+st+3~N(tJuYYkYjRK^e}f}3F2*%M@pGODs(u~djNg1RkheqX%4DY`Qmyxp8SN& zk%ZQ5INZ7FNV4Y8i+}UU&(B|v=S&_E{CTe&^pb2&Y`4f`M`H3VvCsLfQfkyZA*jep zSqwK_r8;!vhzv($?OGQ5P|6gzFmcaCm#;~CkW5S-OgAtR6o9+Vy34jlO>3d8E0WT z5;o}oUUxhasxh32gi>ZMpypXRIX(U9+qbRBmTZp>%MM*bH*sZoMieJ>r0`tIbF(g58J01P~im*n83~V&9Bo5Z07!Vn^1o!srQ2W8yS#kKMVO|L#Mi zw}fPZjfktV;m)Pn-Q@#YUguLd-h-ibE&X2S-*bQMHj7;V=Wl4S#f)X*rLG zjo3kq{avO#6&2_D+N@!e*pCJ>KEZw3At;*D(Raeuqnjxjfs?nbZ*KB#`hxRIybn=8 zX0-UIB3sceMBw9T>)C4;dX-0x+&Pa&4L`&ONRS$9Bas7RJ>3LoA+)kM={#kNljM-m zbs1A{dH5K8hiMVKOfI5_2J(&$`gnxMil=A9uK2!{2OP$ldMv*5>Kq}Mgg3Q;T()Dw zBgS|t)^mh_EGX3~GfhICMco1}i3;V-A%P$dFaXBUse9+RWGk}tTY(r`p|HYoJngyg zbw$Bp&Ug_AX$6|iHlz77#19BQyf<)(uohEO42I*esg+eUs#RsaALt>{ii#Er4%}Mh zGMls!{bpn?sg}cVYU%$#y;(>Mj~o?nI`4G;4J}8E4UbpPe+`~;HL5SLph#tm9_V3j zUT$muhTA;;(wLZPFlgLuYZeY?#@LNcQq-?8KRP=uL{p2u$f4>)r+gr1bfIpw5K z-U78>K;n2qctpsay1^+@|A2&%ijN}3zNndSTyp}7c1HUA>0o;@a;@d%1!I(Ua#C-+ z!-(L{x=yvkW(M24$i3Ky38&6wTGhQb6UCi6~nVXARGID z%ahtLzf6{AjNAjF69tnV)@d@R_etIg;vOp`n*ryMX&Ip`LK6J=Qgk>DtfszU{8lt# z5&}02!?lmcM`SIWV=Z>~qJNPUZa!G=Za13mS0%FW3=W^iK)5CTE&HAeoxy2C_9-b& zU^ZztlKmKU0I4Ui(kTW>PvORn>g%e}r`gW#mWO!#7$TJ*ESJ;DwgYPwj(%XW6prD= zy+BHNkvqQ_^}~oOBQ5J4A6(6whC!Ror)K~lc%@vY5uiF|wtxG+-LfUE)=B6@z) zW>hbP>h;f13VQu+97?YU@eeI<;#G-@I)}UD#~>~A^7DJM=_Yo00Wc$Y6!EAMahRuI zgFSXqtX_o*U-|a^Y6AyMwf4<2W?*12GF(Ad|0|&i55y(G2~rCOobsMC9FdXbqA5Jr z3)zU}_oYjlkljaPSO{PGV&)m#8L5$mc~N^?NS@a zNz;@bc&saO>!eCix*GM;FW?|e&w(}5em*`w@GB_wIiSd}qwGW$9gO1m9{fDprIH%& zH;gZWf_@40AxyowQC3zb8eoYL@dJqFPeED=Z5SjBQY9rNdlVC(;!uJ94_7<)q$FB~P!H(?4$J6r-D1cXP+z8EBu2a;w>MUSA z7W(D$=gUAl3g2aBDk3co*(GG1ys8%S3B2X&O~X+|V{Vwb`hkKFKI340OlaYKbaK?H z(`8FTRLrpin4MjQQ{iQD$bMqwMhU3m1bVi`%F@hXv3$I$0r(Xj!bqX)++38#%6eu^ zSYvNdS=7NEsRu$;X2;o^$Oj4K2T9}?OxbYXd0fH*X=H#`r+z~~fZDZ4Z{-wCm-QPq za9YMY0adF-zWom4!c%Mqdk9yye-kK`QpJnY^me*9CC#u41L8ch$$p zoyDZKsZTV4vdiyQz@c7$8MmenXQ}mQJ0fxZS^#r?&xxL0;&YoSZCQ=A@)hdkgm3TJg&1uAm!DV!clS zG9on}7?-fMdsQ#kf9?1BdF~dOhG#3UYZJ1VCv3K^Mw$w+y_jh~YA@_R>iUpw_)G#U zat5was(U|EP<%P1W3KPV=YX_#?QL^x@9D%1V0sU+H3=m^v_b*{caxY2&%R4|1yX`` z1id%;4~h?xrx47cDe#sMS6Rlg^tXfs_juMKFbpZ`-+*d_ab{4igoy*+ADFdNgz3A$wyc zo0X3xy_EUk$(h!Bnf+N&-K=E1i8lPSN)i>Nh>Z~d==^}7e3f38;RpVCD>4B(3!T$j zQr;e@>$@7!+_Y)PqeX6Ldd-7-Py&Q{AG+&cX~~NOr87Fj@tslu(I)_x0CC=WD5VrC z4cU~^&lMFtC^Vz7S3vtUtrDIY1BgTnF%L;RUx$S}Ob;9LPO@pb_6eGlI9+O>}v z=oR+v2)R&tvXY#`07kuf+LNxqVZovmG2d7a$B5Lf(scvwZ7;UPrun< zG`4uP=1x=EQzWSj*&|4XG;vx2It>5(>6TKBk&#{J@h_D{)BnLY`7AlAlC^h0v>{Iy&?mYY<+?4nI~zbnq$x@igyWwWF}f}F^P_Sg zK4f_jG!VN!D>qjmRaZvcZule_3<1Aq8Y5tPQb@1iI$VP0o$cG6N81%c#IrpGa+y%w z&HIk5Kp12VLBp?K)^%wZ^Lhi>1ewuDIyt05c=hV==}N2|Pb3jPzYMj~0UM5nFo;lh zF@j(W7?)Pc^`r-=lp%(scg7<4hJ6ltmQhAkTq;(4GF)_tYbc(|9O@D!*xN~QK&vSR z=Ox0eMe8iN7Y6lqH|~OJ+kOCHWU(EEkslU^s}LDkX+78DM~3tf z8aFa)<GzyTkWv8$a?Nll<8`SIIP8S`6kY#kU; zc(MXJ-(n*zpd+NpMTJ1V#c{5Oq19nVjZ7G@MyYbja%cz1+sSijNHiFfYJuB2l7$~B zADdQN1Nx*i(H5iSfroAlc8nxEX23C_($vn(s64TX0K zztBf^rMGDPx|+$IZa?7tY6QG^Y$$n*7Cke#VGT8`IZ5?!il2XbvwI<@Qls7xwq05FVvQt)-X4*#sMPqyZWq)ubs6uA%mkw@I>9% zZV+OT3M9hiNT?z8h=S}G67<~9l;0@d`yt`ne&`B&+8xLPg~p1gn-<{f)hM(v-+FxS zK8a^pQS-Bz|HiVGO*vMhn~HFhlp>8Iq<%~xtq#^V#-s+MLk9h@YOcbjCE9sH&W0Qk z@y85_I0OIUEo@_#`wvTuazSJx65pU8CX{=4YrDW?CbH94JUZ(EZp!;_W`}U?1uk*b z765R7qA$qEh;x)6`$2<%m5uE&@~2eTuAgb{t5ZVdNNOuY45+cbqt!+R{~!>%muPl) zo{xW@jw9?f!i%r<(93}gc8CiIlN)c3B49ZbBV0Uy?wlUq^AkkJ;Haj>esbqhK~d9$5v1PH-J%3~~+=1d*> zyTj>#ihwWA0yR3i%YCRNmjTcgB+D0ZG6Tkg3}O|q?_pg`?|M&~f5_Li=QI-8)f_5! z48{^cF?!!JaM5!b2OUbV4GHC~!_|Lp>Adtk_Rgu`ONq~#h5VxKS;TZ1INXnD=(k?+ ze7`7SOU1Z0uWDcVYo6$4H;Vnwn>!NX_&W0+2Y1A-F+!xIY$`mu)>({ORP$3}gjtIT zb3)Ctma|c&_T4Mi=~t6h;-yup?sS~{ZYuGpz`QPvP(*;bi6`2jt|RR&1VcVSx6EhH z&Az$!7Z06_S`>P^r|RvH{lqQ%KznQ3Nd@sJcUM<%s@^uKO3VRz>gep0@3zF7CmRf@ z22p;ZM~69HVxD?%rbi3BrjpW7p z;z$P1p!CCAd#{;tRQvN5%vCuKMEA>Lelgl#i4F%b+Z1{|c7)%WRdfPDn_xG9%1j)~ zbiGlC;w=yWq7hpu+Pb;~B_fW7xEzqTzkOf3X3bqRZniyFQ*EIKVXTR0YYDFDoX$Xo zy-`3o;05w{c--2i!3}8B8}E1gw~&8DV&W252B5g;7}?;9i58ZEy?X=ArUv!?>vkFh zmqI^DzZ?MZC;X2xxa9?qg*JM!K*5j7G9vd5(CZigK8*j6#O3Zl%TS5~XBZ``FW8P^ zVs0LR2U9XK2$@RLVc@^eSAyb$SiT8T#7@XU*uWHo)ZwM4r`#`zy-64E$h8dX*T=^$ zL9CX*#5PK^W)E7z=&!{2{Kvnr;XxRq2r!9I_;1 zKQLl|L>J)jK;ryf5m=-L$p%u}q|Yk{9=sRR*Vp$H_IGXKJ$%>=rUYbZgR4-A&MFzY z6JkHH4y=`QJ^R*81(>gl1&Fkwe$OZxAQuB!*19I9g7WbleQ%w@9Nb#l9f3`&i*h#KGjguckQ-#%03Gb+f&hOttKa>lD|r z9Zc(wjxfg6+MW9wUdu6xmwnvTXqexZwEe7a-!MIGPLVf4B zG^L2~I7YtUcoDzP*2QOSnx~nsUf_%9Q3?AP{ojwZ4N0|r4r$sN(clwNEZ)1JQt zm~>N>0+P84k>QSgzsP+IW84>)mN0IMFg%4sz~rr3z4z7Z!eY2RD- z1zA~xnm}c+<>-pgX3`q5-A`vV)H#rmHA#ILL>!|Fs2DU*uR|?WCLI zz}?QkrJC%2E+4TAU9NL>ER2j7#l*y(PAOCaHka>C;_I1e#qb$IFTx2zKx1{sIh_El z^>R%SY=~H-c_TF2{(#T~uqxyc{h(1J{T7Iga88U9MJT-b$>ghT#?$N3_Y21%NyebV z;uM@RGQS8HL7r_>wP#+yOLS$9ft(FW(E_=4*7 zlNffnhxy7f74%;@l?>!N@r>4+NtFH4cJB&t#4$VkmP3{pUa`R`w_n3)&tQ1NJMKyW zwxZt}A|+LI&y&~77=G`OIjV@VZ%{-6Y#1VTZ!B~Yr4XYFAV?rOp3x&#uSj8tmJkRL zxn=MHCYubBj-kyG;B3X5+V~Ym9?^HA3g9%&TXgDgt8EM_f@mF6bZ;Ep0sU*Hzp5QD zbYMs>IDmQJ@^eP*SEf<31lXUhKaJ&@GhpM3rG**;fBP1ar>5$Md6Oi7g6k%)OHD^f z%Aei^MiF{h1|eOT%YakWbvKW<#|Y)*h#vt`@1`>CmuGHr!hRv!H+EI^ncJ~HUy-vdLVv!|y78G_s6$7@j&5K0cD zn4}kM2A3R2!R^%+suo_BF$y-pXo3>JU6jYTF_I23Ad1jrAYyM=oYco^Ci-G9Et26d zkJGjc^&fP#If;4eSracM9iBg&sIo*qwAi;JAk0x0XJ7oOWm-1wz@D-f^xx>c_`B<(x^4m8=^R3QyVTmBTOH<5@6EslLeTzU*5O1187vDRXLQ13`93qKn#V+;9t znc3LIN#T~&eK1ytNf0W&ujr1%x12RZNe<4OKRj!&!2SkbY7TuWqYPmjf@pPdq*Zlh zIG{0y?l*}G#83y3@f)izCn*U=RV9<(815_Cnyax50STq&DvXX{x zp<9X`9MtJp4EAD(c{uP1EwO|dQ!rBx)F;5m2Mk)?Iq(=eaM(m4c>Er2Cb>Cg0IgH? zKb0EM<+ICA$Xtx2A{MJw{_0Y9B8BSjhc77dc9^cfw3v$hSKOkE3-yp zm)vZT(cX7$`c`LJKphSMm1D%ipOV4p=IVOL^oe`c!Ub94&R`Y6Jf-XljPOeRBpdz{ z-Qp%m=QB^__x>?&u!;b-`RiJ=joz*B3oQdDqK!XSuxN2&`dDw)_OtkBgM?$tS%N|j zK6r9#-FQ+ZAjk3Z@*C)NIMVZeS=bKDulXLXesSI9KVUH3*+25mk=qVunpu|l|NXZX|BB5D54^}5N_S=4g7cVKqTJR1&7vFfKl)jVO1E0w z5x}wWKml)6}O_`5r>TTANq?`2l@`5|8jtqW9LC>`Z4ow3bCr+rh;GT?&tsbFg%}` zn@`YQSgPQ6#4VNb#D;qTUcnR=gT~amM~c->Xr53>t{IF`t=y^@{UPO=SEH@tWTh6q zoy(&nr0r#;ma?yZ@rix9G^d&dV>$<)=0w12(SGh?*e^$`*ZrQn?;?(J@0wtx7hcyhw zsFkhr5+7Go-IVBI``ea@;k!)jdXrC=Hb-}wNtW{{d}}^!@YieZ=*?r<-`P*RqU+<{ zm6*$^>(;6I@?N`Z<&Wn6Czn1S=y~~cLQ(IhMqJp$_7MGZNspDODpkK6Gvh6D_KQp3 z8u;?&UXi*d;%bs125BZDB^QRJ>ILQwbFF=Dc$}FlV=BZ~=irfY>0znLUlszF*m^aS z8ajrr{kGxpPtZx~AAOh_9z1(xUkb;XRpo-)ybOz)D?HY#U)mqF^J(tw2;Gto_g?*= zqqg*`(vr`4`a_%g{eeI{?XEdGYM*lc&JO+a(l77fx%+(Kf9b39skx%H*Pg*!E3N%@ zQuL(*w*q<^l&$7;dX(8mZ_3`7`|y54Wcyc_pPHHfPCv0E{O0DPui|A(D|B7H#T)K= zBH^`Hq~b}4pD_QY!$a$m7r_C5xp-tZ=c7sfVX^Ts>r>Dn?~LDruwrV1Z@hc zeBD~6E}QZGew zMSpC3-z2!_WX-dHRHjYS^;)av1T^K=N5*d)GkmbxOG!_dbLh7K&%28Ks+;zA8!38s zPFRe;eA*7yV^d3`N9cq{Ou2>EOT94t)ohp8RQ3LjzNIo|@~z8y?z(#7bOgh<>dG(a z?Y$ADmy&7*P8}(K=ohEvsZ+*#-6XA{!#7GvotG?1FAJ#2uirh`} zetUvmx!n(1t8dPYVXnf3Hl_DJ^lcT3-ZvT_P8Xs3TC)5^c#YCz`>?L8p(|CmWXF(+ zPHw!aL6WzqW-7L0RXLTY?83G|E{>Ms5^1k%1-Q^tf&*nd*FT?BK4r?qXeY?zW4}8s zN5tuyb7p|>(}T^edz7;2mp#hgYo{z1UP$_5SM#@A{U5ZNOP!~qHzNAJA2r^|A+EJ7 ze1d_yrvkHIT)bg({+-#8Zt=|%In#m8MeRmLWj&tCoptit7Ag|8+G)jhv>3@_|)s@6Ca zwcGtHH;Ma)1v-9(_F*fy`Nf~5aMFv{&h5S{v)))y5G(aAN9nV&_?YeO&&1#NKU9_H z@*g)%?ZRJH_6baB7{olBwRX8-&sDAz)b`}jw7c8e@T|}YE@bpy509W z)_9$>-iSv2dR4yHgLHcVrXP*{M_*k@=|l6T?^sc(XU)27<l-?=oeIap~Zw_oQ~j6&JYpvw8i zC$cBy6YkRr#J}Hyr~Z=L|5SiB(_1r+X6ZyZx$HpUrx@Jmsv7QsP{6)guFq`DB;#X+ zQq*3!4&g-6@gw=!$CbNhxktj%-y5W`bFS~?-eqc)^07kFiT}b)A0LDFw=;NuPpUe; zs%oUM`M75Hg^D!Z*@0`8^}*B8w*sEZ@Ln#d+7)`CoX5-8DC$umt)(r~v9dMW+B{^Pb033k@&M$0woKyKirnr?tS%b zFzI(xS=VTfnAD@E|AWA3m9#Y`)|9tGw&p`A2w_aV4 z;*)D>lN`(m@z;9aT&HfgZFEpiR=?M0kF$mKd!I+0B^Pyh%s6*1uw7KKTr}jx=`(x0 zzUjJEa>LduHyrQfEjBjyo8VhMZV^%&A}6)(q}3~q?2I;L#s z(Kn*4s9kX8ZB}T*S$-=!#6Tg{q2wBskm5d;=@%gnf;{5wPqRLd?6?t~T1jP^&37*| zfa$7z5c{a9&h1N@ueXN#cb2chj)_re6kw6Kmgw@eyi`&uv$Fh6jnd#>Ox-&;7rkD& zjo)plOBlMJum1R31lQ)_lzM~KYS*wzW7EE4f(`e*9$X6ebv!hX+NoHVL5W3iVEId1 z+0KIjldm%0X62M0Q=S#%Vi1+J`x!L#qd&oBl|j;aC*!q8oWp9A8hSQTl}*}7XViZz zK4=}@^_orn-%~pc-T0rp?>5pe<_(QortT}f@&5KZUj4}xeF>avoDWbg68 zuWbgh`g#_+QU(Ja%uOEnuG3X5@Tzr2diVmjedWyr+sJ2d`4K4@9tu)QO3^ zQ40i}6zjCj?(o-23vGBa^@tMF*>_>P=y{_h1g9e*8{@L|`@hn=ypQ0rT4~TTlbW?{ zq78cC_AMegGqsYhqx1GK2RTde%)io$OTV+Y@bGaelPN=j@V230^9YSi?Rcg}9)5nA zVG}(ahNUj%v{WLSl!Hz+GOX0Ld@S)m3L@ckCh2h+CoKZ}xOw_VK8?-yad?`>Dh5w$ zzXfKp=jBr_! zSk&DmcK6XbhVz4gJ4%AuIxEB7!h==A_a1J*NgJFNf4EQhd47<^^0VKDsr1rcw2pG{ zymP-YWwy6Ln@LLkc+onhoV|lw4L6o{cTZJI7O1K_e^otRwo_n{Ywc(6`xKV7hk63C zCX2HT^);A1UcS~FKGxZ%r{=^_D!sj}(`gt6Z(smCc?Uo6&Ww)HVKMm}&j@o`-*WdF z)o=^>Phmfrm7KVEMjKcu`m-t_M-?Z{_Vbop!0w6+eH&siQTFi4zBOK*nW>7w(u!Q8 zPm&%BukjL&rZqdJL^pEDHi6Y&>+&hz>Q~%PgIb#c_8u&ouL(hrt2=zE<3>_=V6L#7 zDgQ4D1Do)&`ljyJoW%zj=LALiCh}-(?{cr@S!KVQoH2s=7W%Ten!OS1mzXPOJf{?<07^d_ zZD%j&I#B>&8@#81aHB)EK;L)aZ_{YFlI8rrX>Ru&I^+stIX_F@b36AwultwG0&2s5 zpBVE$DRBNTKlj?8ddh4#&-{A)`OW!B)Z*kOwVbrQkN?UZEIOyFHd-C%0 z>WhDyCSYv5eS_-pk*Ig{=0qr*6)-qSXfy{c4ktbqdPTRo><=~N9}x9e{SQP z|DJ!0{r-IZ|MW)_gUtAVk=p>|I|nr~C+#|Sa(fl)ENs(2{KZyd%Lu4yEHlfe5K1TtI)q*&sy)6B)N#N zubvQkCnjU~vAF;fFzo`pg$*<``}XV!t)4)eb|a(-!D#ntW?~S#CfZ^IhyaQJz{k91 z;=sdXWfN-!|8d>3Q%8Y^6PQXbLn$o?YsjJfFz(a;NnES z1-AA$5N|4sD0aORU89Z$IWhUx`ySz>_+zE0vy0~TyFGuk#ojTW>)m=u^_04yGQfWH zj-)sOdZ39REg5`+T#BZ6FgU#11_=x6^FF+lJD1VGuxY=y*3h@7yL+rwLDm%P(`}g! z0AyeB88Z{j4|)Y~D>Q?vm5RR_>xh8HN=|-S>g6yMpF--snen;zvWc(J&#?f3l`fai zlZga~@3=wcxhr2no-K~^7yLRJBKkP^O^OrkBjcUQhVz|Y&=b}qbQn+o8o@5boJK++ z6(4~j+Y?MXbguDMFcB>nNME=&p#|@7mpC$vxxrADqX`Wqv;Q%zHO-lREmgfag#}&i zL0UHXbLSi#cwNP+4w);}75sQ34+;XXn;k)yZl&6T97*JdL^wX&yi(N~RN7#YNVR(< z$ZKgof8&9agDJ-1a@)MG=bMgF{e7)p{r#={O)DW`S*YYCI1oH^MNl_jgGRLJL^LPiqf#3Je#h?m+p9G+aYywJ8Xfo5$@gd{CdK(8s=#3VrY>I9hv zN_c>eZzf8ITP?>MaLqUbEErwC7?7a_SPG+v?*q$!2u*s^qFteqO3nB0WbP;0COvN+ zGk1bR#{KS@7;4XFCw+wF7vtLsit zKBqj@d{c9i5AmSE)%!17Zn z-SebVi!EVUew>Hr6&Tn=R!Lw}tZ`tAeU}thG8(b^m|9k_eMpD7v2o7BXBdKzi!{7fgTG-P5P3z@|n+KLl@y2-b-h0?6x4 zzx&F8KYywc>vu@S4SDus&z9ne*&uZ7glRW{usxgct@qG+pxwHa>hh4w68ImW9}$%` zWx(q`Y&BH|0e>Fh(0eJJAaVA2(`K*Ne&QjvM#ecwF2bMW)JNAAMI5K)|qCJ7jT6<^b-SW0KGg8f8hL5RytyS9n zXdzdben}+6*p_}tSWP|`#NrdwAqL;ujp_9`47-WldG4qUI5VVa3rc3K4*Clj zHevkMV>e(qYQtWce`I0mL=;GLemT4CU`dz!epb_kX1&J;-p+fBzw)MujjBf zOH0o$d01Fl_99lnuJ$FK2}B-$uV1hEGs~2Yj*e14!~lBEiCr6&6EZ$?dN4vHL3;g= zJU);Je~&LE7X9Vi4Uf(yf)I`k>Ng*nmgR&B&U>ea2RS!!G53hE_sob6NFNkJZ$l4d z5`qR(5M?ssu>X?0aMnkTk;%i8AuRb;1j{u@^stFguIN`=klH*9AwhepHV(you+H%$TYY0d?{B?I`7h`a=>0KK9l|ZN4o-=MeB0E;WlhQEp}Zr zM8rkhdWzTGI&!FW=dO*LeT*wtg0ENguN2o5PK!NCWOTmoU=kT&2^*wwq&|@3JDGrA z3L1s=U_$0leR)+?A82{V{w}HUg3l80djJ77E+hf%y5Z)`R8W)&g#vU!*oxK}-JT7~ zoUYYhPSrc5F+IEKe8Q6IpbMWN5uA+l;YkyoP~OUN#bZ_<36OTX!6(xM)6RaPjO~Dk z9^wWo)b@Rl3ut5d0g1%OH-98#f-O0vn{yc=BCtYkAzvfHa|H3Gzz0Ya4kBZM3Z3ZV zaH+&D14PLEqM{)X?)l&Y#&Bk#SNi$OmrlIE1CT+1m>t8Hfp@2iuYt4ib%!QLKB1{v z1%3K&(3p^e9ePB4=QH8o+93o7n*%)Y^yY%(Dkww9(NFccvXTUQ;&*^1IACrf2tvZ< zd)*QqIv|WkAY(;$)raKL8X9ey&7_6Dyf{hN=TrutDi9R^_~9eHxY7g$fmXGcWr2SKG2 zRJs_hLCip2K{7;WNf28=R2#cl%5C8>amhNS^Mww*!4je+MTQF2FX_kOC_#&n0`X zZZBCpk_?Wjy1MOR?v4x+rPQldE!#%_&ARN%HkjVymwNuXohJ8FCA|&$+A@kP-+s&G zH0YnM;y#$lp)1;Pl|{Q&-ymmf);%P-Ylzi?r96yx+pms`(E~C&s`mtT2bwzyJ9*nY z&o5GZocZ0UXG5^T{0^DJP0!;BTE0&x>}2A9xxJ-O{9{GP!vwY5YcdH1t^YX6?1xcL zK5bpzK9Qe%k8P=@`ghm6PcUzPRnu`+Q(pG&o|_?{VWl(1f1Cn4MgmM2B!Qd2puYjX zUQ32ylo=R3^CO~c2Hx8n&tZ;Bk?s<<({~IAN6LC0Yu5&n+Bq-UpK>(jTbrqEdxef&s`LJNYgYu!SWKYt0F zN-`2cd|3s;CkUGxb$)}xoN7072R%tf(oBV@5gDC=DAY}l>sH#p(gcI=+wBveX~goC zVqLNA+xK8`(H?9w;<;U*u@f49Jm7ZNnJgGKI6ZPzWa{m}g&S;XQ)u<=s?E7U`=0; zlInzpY9GdiV4u@6GgrcB#x=eV1TNekCWjJL#5jU>lQu6>Wj81a!`au9^yMjAFndPD znV&Qh>*eKik#n}%Ps)hQwo{Y*1l{K%LKlZIOU#wBdAi@EsDQ^fMc-8$#QEAMR&N-& z1!zjk=uh(ghR~64T4B`l(%)ZihWf91&cEGCG$)I@a1{s@uRw=wiOTyBdah&4;{zLy zcq9vSeT5V&?xsh)MRJ2;AI=R%=m;BRb{C3rM_&`$&ZWKIY~YJ{?=Z)l-c0uIbCw~Z zQlkP1v5Y%D%s4Hdd1Y*4eu+z3&uMsgtIXlrcCp2$f=y8}DU26B7ksAPdeYE$u(j^R z!rS2)HF%NWWxmVxUtId3ZE%xvfv#>q@fO-pZhh5P9Kg5(gm2@u{I;ycnMp5D+jgcv<<#lh@^6HKNCd zyH>*0dN!3L!kI%LAl`Fu=Y}<8zyLb!V5h72FS{SM|Gu$)@`Gl|k08c|Dh*BEfMWp_ zFncl``N%@trNMpFMNte$FLT)j(T^>#B`Jn*|jf_VX4P?c<@%fu`Ul zB;rD7m6cB|5efJ9DRU&|k(_DUwlmkS??Cbridu>Ywt-B=H4{Y_Hql$Et6-w0DIb9& z4;!gEknd55CcY`%jk#$Chv+2gYz8H6aXE5FP-BrTP5e&yTCE=-g{N!QWgX!yi`b&` z2}wMet4wT&fFKa!BYdvO*F&;`q=Oj~NLZno5Wu0Qg#*WY&fx|G$ahyT-0uIemUCpX9V73vS4k};5d?W02FE3jB*V6yN+Ixp{-M|0;8dRiZ zMJNqKDcOWlNGcip4K1!o8ppW)SjGj zmT|jn#x_N9S$oEK;6a|KIh(TbM8nU?hPEz`n+{hbMrC$v`LUx)<9^Pz;D`tHhQ@|{ z+lH##?v<@;PUrtoqd)JMFtvX8x3$Wj8$VxYB=&2BI=YRnp@A;+8%SOIr>I=fcxWz1 zd~Jzf9tzNYyt2YxtNDEPl;%7A^3uJveS0WeU%PJ_J2!SzIrT-g%f9R#r2CSnmSK#G zPMhn{|9G>-EO-A|2?kGqNO7@C&Dy-sa(W z(+k3UftyR$(Me)3cZu}U0D#qL9f?^PZCK_7!H6H;gFPZd_kf$-=oAxrfOdhi_zqmu z`7{lBS>lvTq)XS19$VG?>O6YTkkm@)$}+ifC92`4wfL36-%=9j!&W*vxfVd0aF!^F z^|Z8Q=t+j8jH20q(UX}|Hjoy!Iwp>(em*`Mk<5_&a4)But%ZOF5LbhSOXY#>JZU<( z<5kKf+7Wy;O=jB}DBU4Wi$hC{aJzZ77QxHAxQIhOd*I@04+O2MIO2n5Pr6Z74J-Ta zc(cq)R(Z5A?ht<>*JKOjgl<}ss6?xj8DC=O zB{n4~w2r}w=`-}%M6%h$-!1|{=e1|aVNn<)fH3Fa`X)(z1h)hjqhf2zVmT3y2JI7-e^G2_|U+##Ub7&vWazD|14sIKGMV*-?HOqb7wnY!x74N|y>F&Hn z()rY@#nXjZElSA2n;@(YqVU`@_uy~cVTo6A_Sj_aPsKZ5^rZ2Pk&c7Ay6a^+cu^yW z-VTW^x7Y3yl6SddBFs8GrDnf&4V%*^Di!7&ZOE-GOua`4p(hjf(3wDOG2ilgFrCSi zUdPwQ&$9RWmxUYR@}3uBV$bFA9GyM3?tiMrR+{jEvbR_NQ{=z9agKb_KNX3@|51^k z{_kSN|N0}Fxh+Qb+M;baIs9Mg(D9wq;-Sjz9sU05ru7kR(QTn}zFsazJHo7;i{<=1 zyOnfbXpVYlYe|bqM)vu*4yjFY^0s`+t8CkT#+WrrD@lDqt@7NdihcVfcY4cml(Yw) z?(gtEuy{s>Gmp}kf@gks{fr%N&dB>IA7h$LTC^|TMGQKg)0p#@wJxf78|m?ME{1!e z{3ox-!Oh=V^R9l$pl&Swa9eW!c-kH*vHK3enN9sSU6{-@CUe?XP8+XcdTM`In_efJ zEkRG^5p*My^~GPg-tgdxz6&G{b=UZ5vA-?5tQQvG<65Iz6Q^+2J!(FO$Lsx1Z_Pd( zNvXikdA;%1@_w$^9F1q7{KcvmZB%IQH2j0%nVA3MhxJ$9HMQM(Jr$8FDwYswGQj*$ zD)M-P*2VKYjhXS6l(MR08sSpeMtFim_SH2InsSVwm|28yo%sBPQGaBaYK0*0f z@d6t{=5OSw@|Wlqnx!!fop8yy^>W6QMQyh1O3oGr2Tuwc4cvrfPVwWgI~QVJs0dkv zZi-YW`%t~%T1)gFvj~MxyX(q*FG-7h$)pW?pcrpfO~JRfKZ={$H)-)nxJh4Yp5SNa z{1^4~y5p^iA#XY~#GK`xw`?e>e<%Ftc=7Scz;`Ra`Ni;#=eXAO@#zs{ZN$iyY-AFM1)p# zsmbB@NgeV2G^(pd>^HJX1lQbYTBgVidoHhID9qSDo~{0OL_WxuCF#gd6(Xwj-E6*8 zVc}VWI;;@kY#AAllR3?2@Ginh#5Xa7vUH!OO^r?%Yr=(hzpv(%nEt3LJFtxhnZ#1=R^blDyj2FRKlukzc(QXr|be#6wRPc3I@ zsjufrTt344tcvmdXZ`%2Z<-mMMNg}myOH*j>veH!HS*>Q5vZ5H85Qm<>LG@xFTi>Z&Qzi1JoQ>apN z@bK2qOOjL=^c=_8@s&*NT;fdfhKDo-%zQX$*sc8<6Z(u=(#qIAjJWP`rZoN~%hfxQ zuy5}tx}8cU!+DP%dsbX|QDREw)Z<;7De&@6ovvI*i|KPWaPDMseH6Ftlnj;nYDv+^ zXKoESoN@j2QAueo9Z4FEG2E9_yvD9YwK}lSpVRxaOQiemojdo;`)3Dr*eWVSRBGDK z9{gaM@z~}t`9{p_uj&P2PJEqX^Z%?ZD$oBW-o{eTy}Z)b+xzo&g(ZhX?cK$u7Yx}v zH&(TF)G?oBJjZoKHQq~v-MZOIN$=CQTZi?dYfY5%!X%1Zm<$gYCARCFsd(2`e@k55 z70=3kH{Y5=*SHLO1WYeBZRV1vl{aj#eQTY28w=6I6>3KB_iHq4PSVN=v^mRdil};L zsFJYd+c`@2MGo$k%^DRo0#E&IO3OcZ`>?KIZlhvRpZj2_BI?AaTfLS$z;+)~xOdE1 zUF!SBjR^_z=TEZ8-m0(4E45@_Ki#k9dhhL$jOr!|OdW$9ZdU2wd!gQISZD`3!xOd* z-iqcfEj6*{-xo~1B90LoFxT+H!LhHS-?C=!6;+Ya^3*Cke7{Jg6DqHc-+v!Edww!x z?eE;JeZz8kw?d?AOXWdJgjAUv07u)zqIk_=fz>m&%8t1sjlZC!CoB9+h3rY zhskormFledZ~tdOkMsskmQh@z_A%ZRel8XxoKlXa;^)Fq<)(G(Qi3vrGae{baqRtc z2dm#iLw>8#HTtFtva7JV^6}s24OtMC5Zl5y@=$IK(>Cw1Yc`t)cGsG{rjo*FPt>gN zCVw4+zEF85{)57qOseuD4!#)b8|4I2Yc@4*o_=TgHeXZjp9O7+jVx$#ryj41?-6Lb zabN?J$w6_AbzY~=DI4{(@NYlJD(+^*zB=W?o%$;rX(e~Zo31XNUNyEieqhbx2&*Vn zF^img_e1H(Z3RR;swvxQ!ew!ISF4izX}O}eO6yYY-8ZLv9Hd*4?qDXE`u$N`^ra)b zN4FTW0ecaA>wf4Sy|EhleL~3|t#=QYt4+PHP*K$KT4l4@gf)xB@ma|%b>u|LhV<|? z(bf+{FrTAq<9mmC9_X3Cbard-@un%XxVAd!V4ZrUvq!`9=-UThpcy+ek6=Pk(1XjJB> zD-}qF**VyK!+7D$^jsao?69?$$xHkmMH8>yjvp$CVG_aTr!ktL$CJ>^ldIt}zI;Uc z^+kHJ%F?3cI&|fduy&>}^&MI)o4IdAqa5r?JDaxRLqf!$ z7_@Jk;#sF<^YPKgxWvW6r#(q8ck(>&mzzAo@yTR zOxE1U0;Aq$4%T9Q+6&l%m9uYiS@UgLyQtYhLp!yXhqHf3c*3^xm}8gEdjq~>Pu!k9 z3o2hIwM<&;^6mSNyWh=?kKs}c>0Y%RUeQaHWj`UpWRWA^Z$CEVn}5MVvWtH*q>t$t z_jp<+)q{+ULpcm75oglysASN|>d$(zIoXHn=m&e990&b` z<^wb?@#J(XMi+{Qm42o2bveVzD%eOft@3AO^!n7*;G7wGV7AGp%b zvVNxiJb%eA+MSpQdtdf_iF%$^-9g>OUK3U*zOl?wMRaSh!P(wg{?)BU$~qMXN-R|p zG%IK_WjT0TPQ8w-NiF!?f3)$>PB$qw{X&{sQr6E7QcqThUC-0DS$R|%m-sFUD@)yQ zSe?1hUY&|Um&_FPmN{F8-F*Bz@cY=>P0|LkeeXIhA%n^qN@v*mmKhF$>yl zu|HJa>C4-@NO>8ioH(b)iPtqbXTOiD`{b>9*V*m4wnNd`-QjH6VY!d}g7XE0c%4-; zpJvRnMb;7v}oxcwIEP=U}DBqj&+V zyF+j52L#sta@d22t1{6UTclUmx_^~uUm(tju1feeUV|mh&mPQ<)qH}X^|P0+_Yt6SPtPzapdWDrmEcI0~h*yO>|T@El>&E zc(gCEl3L)vQlU;$9=EGx_@wJ z182et1AN<}zl|is%IzC=Y2qCsA2D}cgZuEaG6iL?xo;#!ng7gUJ0=L>|M?@%S+W0v@5J-pe5e20 zAIYiE_v>lMbV%b|yMcx#Kh`lOI(p33>fbAv(?Xtb+>znr{+{b;yt_^+kVdGqBtZDjjRu8!JeXL3XSMGWq;XUe^b)&AT0 zCR_?51_uY-vmycC0ADfh`ect)QrqY>I^vM+CZ3Dq zj_P+`PxjYGDxUS|kxNz@`(7FI=1+H3{<`kC?@ZYVZwgpqayIQL+*R1>xS3V71m*3K zAQNNbBgn0wag}g}P+t0Fl{OH5#HD?`@seXLP;=fQRt6 zhroPdcbzy6DW$Bn_1M9g_oL_4+`9(HfBgsg1Vs_B+Q_BCZu@##^6pf^^d$`QF-M`h z++Xkl#vB<=U(Fw$Q=hT_wRfZ^J|eWWV}R)hEpx$6q5Rfa^n9N}MRPQ$7z`a`-X|e< zA`DT2-bKH?wS}(D8`jI4=$s0xHuLjZ1!>dMtfSjJvDi?qz#6E~lW^voltWSLGCHHZ zQ9^?{X+$a?_Qps+4WO2iV|4KVcpJg!$vYw-fF{vCfPZDj^i}A8&yCljlofK=U^P^j z$N}C%%P=8IhnD7heWAxXi7>F2jy?*3WE&v+$t<&gfv=9?{8Jtqn+jAz~ zUHI+}VF$_wYlwGC41l1sO&V4?7`7P6T|c(3w>yMIC4d=S=%n@r=Ku=OyorS=idWG{ z`9h9RWgSXfobkT4f%1YDPoIh_t1qF_vyn|a0Qe6TWOoU%0Kj8pf#uR#h+HgjktnAy zTaa5uN}V8MNWlYT2x3CNY{!C&#FY_y7*vX;+e{+cn&NZq-~G<|3AR=o3dxOW`jMoY zx2oz)zS|NSqA`Bzf&B+~c~r{1?}@zS&9E64Ng#NrUMUqIA1O&Pe>!|9gw zIo_|H!fP*~Msa5IPM7lGIStu(_BDd@c?-S0J>FEbjNW+|-k{dvfsf45vH8Zt?12&i z1)!*U)5D?O-AZP4!|{ANHF+n?dBTAhD4yaHmuK_8rW7^wW$I|r4;4ORrta}FN^3lC zQ@m8Lm!oupP6BrbwB?y_@j$2!{)%KXyKese$wkuVsQWl0B}^~_c>4b=Z0O=ufGeV`4 zcovkF>q|U(W7+l|JnUi#&CYg`_?$r%6y+*X*GKF3?J~n~E>leAtAll`JcxHa+N{6o zu3`Aq22o7gTSs0}f(ErB?5OiolwY^Ws%#g(%8l3a1Ee)TcM^}s_k1qW?Cu)G>mW`< z$ma$Caz!ww5K`!y^Om3>PyuO?*MVL@vM{$IP#;T&euflU@g;!x8lZO_ndC-A+IS3lKOiz+SKRy!-;e~@s-}-J_E`JlVB%k8EP$zM zf;)j33qh3$040!P(`}vE1=0#(e&M3_qD+jLZh&GCBsiilx&Wpn3i>00>LPS5%m!7+ zEN?DN%HspQqF>whpXSj>J%iIEr!)lsXa{e}*qcZT|(8ign{_gA;_gMFv)} z!+Ou$-qX+bo=>i?i~058r9ra z>AI_*_%`b($JSdf8D8?oE`-Xa?qFiNua_3@x-j)?@n=^!w6hp!gd9m$PJ$LmaV+dC zx-r5PbZvoZCzF0?|Kg!P_mEUp{1h`X@@D5XF19toGw)u}vt1?JKfKs1?J*GMP#z-> zQY&RNwgIK#dh=|;EC7SL7dlwDM|f`>A@2f%FsU2fZunJ@v(^5vi&%%9eJytIpfBb| zhpPM1`LbCPm6damfyAzv>Kix41EMdC`M=m?sS;<@7o5RL^R6=9f0l4g zvF~muApk185J(#!guyKl+|$Cs0x8qUZW=u}l8IRk$;Umtv~t*AVV7`#Knm(+K(Ssp zm{{0ap{56ql9@SMk8+SDWk5IB(WnR9MYF zJw3gLkIxS!q6QQS!Q>`1oNMD<_#_5dmFobPL;FBQfd|$H$!LLD@j6OMSaj1sVCD_c z8%l4ecTNK`ku*&9PAuAiza36>NPq51ySL?H!{D#+ZX(gV4i(!1B7*wiSyyZ;!oz2= zB!N+P82%9yg&6L8vJKWYcUw!ptgQ2rk+^V zpgZCWM5)kd#KV-xN%|$OmK_Y|^vc8Bi5(CI0d02{lNWmvE$Hyyp@t)%Gz#;q8^GrR z%tPXf+1=MraVL%#2wnuNz6xRigL;?laA@`3ef#PG;D;(i(a>-=Q6>IM-^N-);9IEN z%%jK-%){zk0HNHzy&5_IdBp6%MWa@)bdX@O!X$455wQTIDBp6}&U1{#m78ZD8;|at z{j^Uo@KGp z2bCxi1{sbm-5np)OQw9(Y*~}GN4jUQHMXER?vJj8fAXCjoih2E3lGCb2J@LT_lQ6lsS!|KSQl|(B@S%?U2sAz>_&U&y3A+n}Nor~0 z1XP1w;Y=^H1{>lZxtt?J55%qChli7?xFOzz><7Gx&3EtKZMD*GH9_GQVG)%FfVzaV zM0oqyS6~d2@%Cn!7XwC|>q8@^mN#XImr~BR?+|>sG~84IPZ2U_;TB6MZw(;_ z^>AE&%GA^}MD%{?GH46YXLvSpL;Jk5Q11~i-hk*k?a%QPnevpTr=+Cxn60QqT#K_; zFXSWZlp7`@T!!|Cg@r-?82Egwt=Qe21iCdf#PFdossm&qNc#H*29k|1x3XJZm^7>n zZX~JY3BPE^{A_G2m+?$2X^DRUFxoZ-23d?^6ig{zncYB%G7d2WiDV&YnvEVp2Zh=j ztJb>bMeLCDLYi|OE=+_LiK1={&N^iA1=0$FhsGn&T3LSOJ*hc!r7&m7{?~d7rICI7 zyuQbMUn$*|7jz34%hsx!_O7t>YE!!2w2nFZ_X&!RHcNNeOwxQ)^cl0C9NVlfu5R<* zgX;Un{~|^0y^oos|_%-tQCBjW1|}v!_=CD1Q%*VB_hY;(yhVSYj?T?)|#4jH#Q2SDVVnVH!wKsDf@%maV3=?vc!OD7|e|Z7cu?bgjV+bo`UuRdm7FY@t@ALVkCSY8gglH zB8oi7bLeFWWp#f>L|VZVwM!wpySsaY>y!fa8WaETix9nSWoCXD%}wg~?2;~j)kBod zl!@H_kgQV>KrG~=CsgP$QRVO_t7hIM)#8=PWwhSG(<==8ej8GU!t9 zZyZFFg{|!u~^LqaNW6C*{3-u-?l&BayzL*jf)eL%%kHxCi_(A)2H zN~(gI4V;-NnfLZ{ZjIiYseKL7O|sOu=lNh?Q&Cn%D6DnAnf{!Uvs+!B@*{Rhf?;*W zl-#g!BXG_BSaR1i^DlVb`Vozl+u-o_(@jge+~3rGem?Bp0$l{7ci9-{)bC!y^o!W@ z$m-3+WD?9k*i^f@JP%m6gL(rDk`MHicojs_kE1K;S|Gk4q{YcPG9Z9Bc37WCwvk56 zfL0Kknh61R=(X4T`+N4~rPso*;WLH-F)2WE&FWVz_gZSFZGngr+Vdhe!+W^B$8Ym7 zoG{w5E5#=#q1tJEW7B;wdlo7m95y?RO{XAGI*ag#xL*cCY+dt1YZN~max{djiKv#* zg;PewT_VmUt72orQ`Lo~ISPvrIrJkQgU-n2eB72ft+s-pEpvs~4<@k(&@nSB;1q=r z;WDPNY+OrM!i1s3zI~K;-jzVDNfC7}X+v`rkr+ece*(flieb+R_m%0LMB|OUUI9xu z)Z_#0HpPXp57SQbv|#Q*#6(nq?Cwi;#9s$C8=pY)ucR{VDi9+II!^zGcyVokgjF?go+BfV)3TB(NaSnb6G$ByBJBeh8Sg;Z4G@Jy{CPMY< zXS~c(=tj$l_0Do@K3piQe62n{_2u`Y$%dalHS_mzJ*}l1qB}GEc-{8)Xb}ndsxp;f zkK4B+&pnE)5l&eS-P3Ny^wr~V_oX87tiarvlRk_BbbEr8O~wQcNWJ#+a-Xr)y5D4%>n-6FTyJWcsrP-YgXiVKKVA@slg*cOFhSPuj9>-r za zUp6>dRAU&y&KWArq<>^{ShEhJ(+1$~%fT{#pfF7>6D|2gwa9)t;~i=ix#mG4XCa() z=gu9LV{no26km!?$hc*=m?P@!>^$^Z!{fn&2fGKZ>`yAdi%clz6%g=WwT6mBv7r={ z1WMIb;4A_=|Q^!i4wk; z3X(n&95j*~c#qC?I1q@QNM{Zj1;Ld=?B!Gk#VyuXfTpuI*g{jy08?3L=B+sI!K99d z2&=$9y9}@>8HmvAb*YBiTo}XbBARkBaF($R#UVK`R2t&RBdd3Na=5k-a2_NjO<HyZ!d9Yo0zoV;wZ(5)zTU^T&ry z=}Q^U#J0OXZE4>WrSf|8Y~S<2&S&&A61D8#U0rrph%pzWi``bKb|E3P+FbIdwybCq z+u}~+EZ6(WX-Qd&MU)JTi}qpz+ic#r)LIwf_`?*cHTvya2@d%WeSKpQ@C-e^gJ;_H z{cAV#8jH=}v*Y|jvd4?Fe&=k!5kA^`*+K|$plV;}IqNb)nn%ASS>4EaJxJxfZx6D< z$0Okp1H>LeOw#^>11T7Ntf4PfdD5Buo}aG+h#savDVyjH(5@&%F7F`b#emj*Nj%Sm zl_RYyfdI>qfDkZvzZQe`1lr;7CUV+@5hS23t93Lp*D4ZGAA(xM2C>lh&J4FaB3(9t zPF;MYtptDBy7>)_ zF9&i)Aavihcp1wZWZ|eyU2b?5%fb3u`K1&q8_VJ4K2im-m7W_| z?)W7!3KTqYb^TbB|FoQ;(0*Q4S{fi)D%at@*Iy=J1H`w=<%NuXm2@%>o;FUUAyKUy z{jU_x^ZtEB(9>?5By)<$8Ht(}`t6~;m(lzGl*>GLY~1VL-_5pa|D~sK{J~;SLFb8p z4Ip1Op=_}w+6NT{QF*%BjK2j028lv*sj7zA593IIlgi9#w!f8(`4$({5)yZA|NKu` z!d+<~q0T%D*Yk-kTN@Ygx6rZuuM}=F?bp?1+YQuzYdHa1k8pYZr^LhYzZO6Kmy!wU zBHZWsVwo#yL@uWv6FVgDdelwgvAQ+`I~`K_<5?Ti8c(E>1Zdg-n^rm}0r>y~Q_rR=R^He3Z)Hw_IqQ_-0CQzx9S zx_Ln3P3^(g-}<6B>e{z%)>wDMp?a^$^8}VYRl|jrIo%h$nw|;MA!0w;wT$IbLuOd%)Ys@H;1`TjL&97u&%T*-x|T(wOv=<68`UNp;x15%2(p~2 z5oE~UDVsi)eA9^SW`v)+nf4t84vuPDMXQ1^>yyczL+?@p<{Wzrhq6aiGtY#bJy4=5 z<9{fYxz04o*ja;TohY4q8E|m)#m6_$m$^)bKQ>;{ZXaO0I5BV^!tun`&STb@2T!_NMX&3DyRK77ugV`pFYIKW^@dsaMB$D_FG z$@6~Q`aILTFzeBVd0wicomq`Z-|hw1+){j#TkM@kb>_!#&8&`q<^p39Wu9_o0dzs0 z0o!WU{Ui7MOii@gau*_)^_9>_Y3tbBGxUl>qWgAm4Z(uh-+MxFNPfz<#rlV8=GnbQ z+ey2PxMrk|g`lleo5#B=ZqG-@&8k0r61bTA*s!`31=TGhm#euazG&K9rSYL=p*0n3 z8S;N-T^jA-ALD3%fs4n_LMv+eA}rWniwR<$-|AOAgf1DVluVj)o>Vd7irb+=FX|>1 z`TAI};hVxIw`V!UTFXNeTPS&^d>KKF+J2T=>)_f&(eK}fj%npQG=G;$fjYQyMJ+Ss zu&=YZ-$R~$9nsK>$|H6a0vEaCugS!$;#xPUvj54&Rwys(IMS^ENQ{JUjvIX4IPokp} z9z1Y4n4nR5Z#7SX#_=+$FW$Rk59@4+J-L3%mm5ksHRF%=DGImIMx9(;5IIn>r%lqK zCPZM^_(!-ymzlls1{t{`&ly*mcPrwqg@PBYc;Yjwvl7OA&&AjtC}a)4!0|~*@u7nE zxtQFw+&sKInRZ7S?jBF$XzjM+7)~7>bkSRiP&@4{uxhPR`-Q+D3(h(X?>Fy+9@vSu zpLtN++tYGIMWKp2Ue?WKBeE((j(#*ZD9bUEPT2%Y%#Y0v3iSnlT|d!en3)*x zd~|-h;9xf5;&3^GF8rBQ=@3_Pos6kR&1mbY?QYrlr!TPaC~rw;QP!(*-_>guey;!3 z@btXpzWwd#9s77LDmlFf9JDQ;+&I-_&z-lC!rg}U)=j^3A8KLS-5>6H^$Mu+|BCqC zKbGrBHCSiH;1?gtEz~wK`{hf*m#IDZ)c5@>U$?89EHj%JTaqu(RTq0=@IK06t?QR5 zw#e@dZ6<5Zb-B=XnMmmg=bzR~R}UgWag@e+AqyxqI$_uGJ9TRJ%K^=ax%!@gSTVYn zw%q&Q7}V{*{*&!m6z`N8t0Hf_-s4`Gj`FA%jwg1iHK`Q+4AEI*uuA;>>YA1Dty^Dm z^;JFfu3GJV`C~~4&7{}VLhQhs#=Rx-l|sI*6+BtCJe6n8NFD+@xU-Zh$+|u3wDsNS zBRgV^C93xn?tc@;Mx*X{z>`}jM4?9PM!L4KYI>V`!3=fE4wv)h84`y^EKXhfzWc~U zC40Y+JMpK(nVK0g&n^{Y*M!l$SS4=nZMFSsjxlWT`64rW{kHBeSI zkBO?{@6z&>!Qb?8^`4$@-Ym4`TgtvY-7=~8!sf;C2VIxPw)&UxOYAMxzhB?t zR(JZJM_T#t?<0*@d9C)@@ltx?U^Ytz4V&1OBU81h<8QvIz4uMsX`>{t`V6B4RbiYK zMd=ll^kkk3p?eJC^pY>l*zLW$G3ki+ag`+flR?@etY_`^+BFW-Ysrai6Q)`mt}|u5 zwvfO^tFZ(qgtp6g>?x^9YkQWX~G0>Ekw;MM;(Kq~1eyLnB zP4v!r6%H4_mjTXW0rz|38uo9$%3`vNIeLVKwVXS@?(|^DK1I2m2f6)<)(HAe*jr!D zXkA67ad|^6U-o|PY=iVfyu1BEZQ+$Jx0m)F`)A8Y-H1Ra*q36b{L0-e?rfN5_L-+W z9^KnjoVkG~lO_cdP;isLrOf1KDMKNb?)6mN`Zkafo6vZ#Uf#PRdP@FzMaZ|hCo`&w zHy`@WxXN!U^I1)UVOMX);Pu&Z-oub>tGhOr%{v1>g&ToS513{mpTm{_twmO zvA{G_z8=R%*^$$kvC_7jbbYmalT~ugsyfjQ{?Mon!-13cDi*G8*1vNPH=^>+g<8?d z#Q4m0It;Spy3=Z}BaURUg|Zb~jc^zeP8<-=+$GfF(=_%hQPnzK;$ye=35C4gZpP$2 z-&8M}f9PIZ`k0y|?5pGd*yU^RHhk9QFq%p;uQG)NV&=^KzRq4ls zu<68H^Ha@BWwqMgO>y=II!|8Beb?U_Q6*?F)3mK8EvBRAVjMl)_kR9oe=W{9O8ZX% zVV1Ggo-@x###{`c0(J7j)-X(`809q;ZGPnz=0eo`v#jTr&f8b-HGYOwV=J@eu{4LH zoJS;UWiGs+%v9ee-r-UFhB1`Q&*WK{0tau9*GbA@u^>Ia3%#d`6PlWj?m3or{=vhE z-TPX6~XRE3y zb>{A^8^Ti8bLup+o(pY#AiJ-5-Tu8sq001>g%>!M&fKo1VauQH4E|`Kw|C%K#lf6t z71mXONkZ!u%=on(!#)WbWGBzxFnh4xeuh`P`3p0 zJ4;zLVLSfOU+JXMtE*eocyX1=f?9D3$-CNQOSI;rp!TYteFH}-J@U&M)z>~YWu>X> zRxj%9Nc}Nc@O-W?r=r!120VddIyxP(WA_qHT#0!7|HuAAYyUgIKlk67sQ)KFQdUz} zKjg?&+C(T&I8a7tw85iSYs~kbE9g;020Fh{cZ=hLG1Y6w9z6fV6ttzswXD2cU#Oc| zqoL@6qtN&btK8@1)8<`sUpX)H^4;xtMz3CB{}&hbcdcyvA7HZZ|84{NKY#R40AaRH zy_AoBrAe{%59J^GN0OGG&Yp-dH9ddmd3*a@Z$vIf@T-4p{2c$W@zY6YZrvfr;J-(*Ay&;XtTR1cGX_gv#p$9FhAO*n_I;So z4u1R33v~3uiq2qx8aubw>JpT7ROmfn)G>FcNemsB^s*dcVhc(wgmO-rAyGeNhA$o{ zIP;2b+hlU*+R{P~IWn26g)WC?E-UHeYp4g}Bn<)|EspgtU=Fr4CHzkzSAH#*rU9g))fM z_V3>>7HN=c+Tm-W?(S4Y9*WZ5Qyit;rjQPxR=DZVp+jRui`F+u?Rzsj^gM4Hb?JJ% z9Jr?&W5;BC&6C}wv!L2ng(+RNqx_rqH+7G+U=q0$N zL8_&2>eRQl!>PRh&Dd!dCnb@30#Ys0u=DXp_8E$=-0$~x5Kltr;X*S<;AeF|DT$tu zk(fsNKYm;SQ2YW6 z0rIGbVxp;w@D0EfdRusR#Qo3WPEwslYMcl{P~8>lUWD8z1Rl22b4ZJC-LXSE#m|fX z2C7Tsf(9EC=}a;V+&xDb>xt&nMstY3O&IIP#{xE+^QZz{sF}nd=fO;b9(M+l|6zH zI=M3nJcb=!(ZpOZ*b0t5D)eRjP;l*sf=fpj;2{D@!t*0W)E#!^Y<%ulQ2izt3Bvt? z6zXD1^K{ZR6eXcW04s}v5a_(3MU|+!od?)ZLqe*auYI&NeGTY#z=O!j%962yydp8M zfEh3m4kTsILc<68(mVKVe1uGg8SEfvB>*Z}YBSg>bKN#|Z%FOtuwz5L`iFmM)iMn+ z$YuRJ9{J2-RjaU_l*8hqwAqzqW~1gKoGZ2>n?IhFHfci^(s`pQK>!g&Ub}UE^=O%i zuE5}Q*Gq=@BQJke^Q9Hf6~AF-mcY8;v#;X~`=J?UPg#+JWrk@tRaW_xRAX8s7e)jI z$|BcA?9NaOwRg5bqO1e1KP2k}z8k?ALEq8y>wP~uJ9|iW6e+2hkv4x14+^a;({~u! z)q7ydZEbCxZ3Tyt!OnaUq>oq!4zmAN_lB$FNjCUX;sYZgj7KpXi?QGq5pisJmq#6au z=8>!}vAx7d5CV^>C4t<8+<@N!V_9RH+ z@__UrPJe_>1I;E-J4*#~5M}6>Sqf*ui<_7YL5wo^%heIwI+ToKl6aDmjvxt1@t5_4 zjN`vJFyzy0+iUT7+Ve({Y@bI34wGdk1@M|Z`Qt?nA zNU}OZl}8Fy_(wmi;W1<=%#eVQO3eJkcOv3M^KFDiAu2l`Oa;Z`qgUo*F4Nv9V=G0W ztrmGbC;PRg{Vnj(+2FO(+a6xM34glosCbZ@6F@!t{D*cQ8YvsGACZq8gCPnM%^ zKj>EqsJ6gUEa6u~ntx!1&3l1GPSF{okLU9p&%npdS!C6$pT>v!e{tCGRtU3mIZLj$jAuk+UC)zNEC83 zXtue0b*gx}530efDHjxNZ4YC;E9>sw>4ENXbkJ8qN252e#tV|3ohZkxjzR?iMHcX~ zu;h|r42svvdU`b&SXIILH#Mymz4*P+J&nr;8*U3~Ye>%$w+t-mCDv~ormOwF!9{2G zP;;uT_T_dV-xLXXKUNM-&U@GuR@+t{d?RXO*m=Fz7Y)-{AAhYZkGp5WW(|$|D6L@U zlPJDSOSOheDPk{xGZ`62h)5ZzGlW_WHM!YZDZs(d%N~!Z&!xS0fdMy+5~=9TCDbu6 zG0(uuCTt-8+q0h%;9`jXVcw?ksB%2*UatAitKvY6kqZH+3Dam2GK)2S{zTbeToYD- zY4(ZeZfR(yFKS>Z36mxi0b=rq@4a|{5h_9Az(aT%uU`3JsTF{rWh2#{>CF}30ir(I zM<_{784I8jK=5o+2cF4T;hdg{MBtv$q@<}d_hq7}oCL&D^bV`cgW~eKR_pDBuFi&? zyTwH;Ws_gadF;79`Q_U~*5@l679WdCzcGeuiuQ0&=oQ*+b2Cis_q;V{e`E1x)u?pD zdWt$PG3 zX7IQ3p6HbjKNMG+4cNvJq9!9%5!6~ox(Y)fo*QwJxYp^&M4WPA)#@k}Mc^I4Ypf9s zx(H;9GVDQ-%)j9y9WbbIC@83qY@OcY$Yf@JP9&C zq4@-Rg$lmMy=ZHX%7+|&uV=|C}5v_CB4dtS<^tFvGK zwo2;v;2u<<;1YLqvV}@+z1_Q&wj2=*w7~I4BfDd`AK9_7H8gQK6@?h2cWz&qJpbx! z>xnG!pGfSKU0+=s9@!Pi zan`2iPim;lOMUvCT}%7r?KjyS&UAU-gBM>jn&Ig{aAQERbRdk!y80R>V8q=mcxf90 z)Jmrm6zo>IU^->8E82Wp1B#cV>W-�^t^^^%C*s{zHd~zezO*NlO?9hoa8Y0w{vGLL^yrF{_F$vL{BfGJ><+-&cXfrIlzM>qa8q_F1Xp^t$hF! z^%J5J7|HjRwbd6_5Z^nf2EsBoK^Np+Q~M;JUjj-yFnjiZ$@vlht-;yf<^rpdR`jy1 zlr9e|fDnXwyAi|!;Ilq`{(NZTM@OiMKYjYd(w_VpfJ36bfXqEyOxp!*9~j?=M1J@y zNxeXUE(<1Jpk5G3Fa%?He^wSX3$F8%;upRM!h;GcX)LWi(cIUao%yjUkN_{3xrkU% zVu&Urea0xsgGNDeEOSfn?Z3yt=~!-RU>OgP@EUS72J&y>E0>E8f9U14+Lnq}O})Qy zf33pLBsnkMoEr!}HmqNN32`SuFV_^%tKXrAGqNF$AO#3kAaQRJ8{KTHsjubCqw5Lo zMLKGj0GRA!zcG?d!@QcKRCdc4)2Z=in+657RCiw6dvHhBTZvjrt<#p*1>*^5eEh`9#s2I`x}y0}`RDf<%QeQ!Mv6=hiAso=R&@34$$qP3LFc|J zTh#NHxAxnDu|+SLpE0T3*C4xt)$>j$wO?pS5{pJcJgKa#?4QN+ghA;&@9s&hX3@jX zWg6NXN4=siowo|mQcMoi#WTQ`z2f<%K9}Fq(=*ko)w1TUTVSWla0;>KCGXaD|(kaB*> zGCLofp`By-9rE65?Lqlgd-m+f!`TH*A%x(Gl{OO?D^hYtfH-2$hZy5KKXY$vToTWp zIrl&E1_m6VA~sHZ$rnNL!x_%qxBUWJCTh91ysM=1G|dS+_y5 zos>?7-p_f8Ec2$%A#|fqGJ@DPT`=beJ-eXUm%D^c2ailbL?WzN(61cY2^!|h7gdv8 zZI=$C_klP&644h~l__A)&YGf~um5!pYtS?lpt+;LNlAo~#l%#OsZ@#}uLKfflxEY) z-57huIR(hF2i*>-tz$~rRtvS%L9`AbojlktM2?1r9wLZv#ak-?Qdfv-e^L@yRw%@C zNlJ#}xj?9T3^6YHfnMUwR};I3fomUrIslaBJM(P4I<|7~W2eEd-3BEc;z}Z|CpB&; zFPVh5%RG=Ct=Y7OJM&q{>nR4#SFWD-FBG3mNPF@r(q81sG0TMkB7FWtEAKF-s;w{hj6 ze(~U{S1g6^7N+ZFA8p(5^|^XX@;XK$g~lPfBULg&+%ajoW(85wc3^(dwDYGug2yg> zIr+n~tN#Rp^gapu)djz&6&t60qa4(jg6%PY(dm`z_Pity-jKH-P|dttzKKH5ec5rS zC5@nQlLFZ!T?8dvW}@&s2%&pq#L{V`_1%fhlBI~~hqq0nUdn81ExaKD`9Kc@ORww- zm%4wZ`LqQRF(2X|AlRT>u~kT2{bdFKN@OkNLU>6uOwuAzKo0F zw=SH$0A=6zjmi{|yGJ#dhl^E^W1G&+@2OTLy{#^)NR=QqBhlWsQ>l7Siu-gHsr^d2 zivKLzSPJhbD=Py6><%OsZ!MGm;;gA4xDN;jm_Sg*?>cXrn)ic+OqsCQY&36BerXQ|E|#REZ98^Uf_zNO$7>fS&3C%ZZ2dMLQ-GG10j2(Y zTbT!6g`&@H&IvzS^7XDHw$l$&ceKUA0!yDcxn0by~Nyj7sOeqlP?STEa;kpl&m%1 zWzGJ%|0h{XN_0Z1@}8TYoSo#dCTPvRk1nSuKq{+=emF$MC(>FnqYFBp{YML6TEANg zoaRRryAi7?TIY`nlXf>KpGad34z_3>?7D;vvc{g_p@5?}8|e>vZjt&OeHp*cyS<`y zH>yfq3pJr3Hgib$@4$stw3Y*!)-t@&FeOX=kTXqw?U3UQf+>|PE@^e6MfchZZfg9M z-H_|!z`7Fa-Jr;U1`xu-gUl?i&wwB_Ylj`woU4ee6sD+V?Kvr#5$?;w`cOZUqcL)8 zih$xx%*_n|@-*H03!-8RR&808Q|Hb({k#vUHVn_V0jk}Ba)*c=w7NI`W$0Bwe=c+&O+^`RsZQbJbainD3RB&HMQynY`udA0N$h z(F>in6LOc<`|k|1?uN?}FzbXrwL{rnkCe*PyS3iAxmP%mZGFJ28tUS>7&7iy=q|I~ zPH4x`>#x^x-_1RV+#pGpUd72G_$`f4((p30G|8CRQ)~xIWB;)p)OdNL=icEYt8v9n z+Bb69qt%jjC-@vi{!O?#uT3PM-+cSU<}dt7Vv);lLr#Rm^O^Zk`$oSwTWD%+)2+0c z{%`i2{co#sDZ?o8yW8mh=ALuUeEPpjBLA21MdOC4tDEiGM4vsAKmM?-KYMV!J4@4a z?-dvRJXuauP`1P{osC(YWEB~xf9}F@F{|i1VQGP(R!ifLSW_*Sy@K_;%ghJr5?#Gc zAH2KHx!0<08{2pNlvJOBBS-8jU$?5stln6vW_Bn)D$M_9*;R9fA5Qv5raqsl-2cH^ zWAfLRF9o-@-l-8XHckK9HYdMHSL=SHgQXh7%C!UcsSJuvu3gko&bz%$fa{AFYJ)es z#I=M4qq(VNV;RpG(O=H_I{C%offeiB3o)<%T;$_dSy}s=*?h!5-kvczyf;xcAT-58 z#`~V!QbFx_azY&cet|a82(1SdhVt?1rrUmgb!xh7m6^<{bnsamX1y=>vPw#v4skCCoFpI^LLSK^q$x2PSc!-`)nX!Db%7cg0C4nlj3Gq^%TuRQabw z`t~#_D%Gc&y`JvIJ#BkRld$uL;<*pfCo|Z(n?6PE!^&l`wF@`4vck2m!xAqrz1%59IIy&F_g4{` zceUE(i9Uwg&eJ@!8drOD&u+%9dy zrodc=7ZB~dW>5Kf?+5FDaqu+WUosSse>(NEgD0#(bDt@_r(ZRj6{~23ck!bL3{R7Z zO7rt=?gz@nax@qEONBRG-ElavuFy(yp!j@-;{F5t|Ek7hbhcuG@<}-5f39z!6tuZ? zGFfH4kjqx{nboY<8deE=!a@&l{xbj|impVZp={Z`lBP_}DXytio>lYRFn zP+tEjL?5U%KYN{zF*N-3F`MW;OBDx?(DjXbwuJnCzq%0rXDjO^TfL3)^79Tn9~`2h z&6X(!MsDqLU(-g(NpXg8NBMcL!`h?s*TY9`@NF0pts^{;9$dY<6Azc7kMa58kkPdZ zEv`?7QgtreWWIINWuOSxTAG>^5TLE15^i0twlha~(0M%F-6K4LsRK$Eqk`Lo zI|C~Z2{#m(eF|}L6MdrR)AZvRgYC>!DI?AuF5&kQl=X)woo}%EC-KzsDl6+4v#3cn zQ|#+!WVyHSg8Nb(spme?xWb7C-WaBOWcst%H2?c@MR~J4hC_FnojY)Q3(HrE9High z`TuBp3#hKv?cW;_6hR3Akq|^AB}76}q$QPZ5RmR}R0Kf~5R?*-4(aZc?gkN%?vhU7 zor`_Wz31Nh-aGDi$NxQJ>~Z!M`Ndk#n$LXZ{LJr{OrLPcsLoU&|JRx~nsdU4NsNUv zDfHPjvzGi^{l0(gcMQ{K8ehT;u`GJ=&iT5Ts*yCF0 zd%-GA%4_^Ms@EX(djItumg-xdoAAiEqqIfR`!bv7v3Rm)3hqF&J)Z|`fwQvx{H#@X zWHTzJo3Vau>L*36q~M(vGozLsOO^5x7dn}Hf4Rfu{U$bQwle0;ISHjwUcO7D0}uih z2K^}YZF}_@_4j>Qo+$7Xq-U(yNN1QT_8^a-Cr@NO1wDFqfSpqdNnMY|b4Nk$j1^Te zc}>G6y+U5Yg}I1<8nuV;ZDZfE=vdu>K#)T70EOF!hLN)+5|&K-5Z67D@7yVeuFJiv z>}W@%F>yXKkFenGdp+>5#FAo~BaAXu?7?|M6LNOwY+|A~B zcUty@lPpta9>T^^lc9UH{q0>6Xbv(A<_C_NE{eFaW|N?QE6jGS|GnfuQnjlN5pIq! zT8_aoS0zNpJhiM-vcbA`=NC@2*8DU1OX)%5BoIIzeGY@WBt)W=k!B(N5XcAEPF^iU zaKA$p55+WA7C?y|_K0I}W-uzugn(RRPVg*ESr?wQwkYcED7r8u&+md|`iu_@F$u!d z#!EDcHEqh#Mdik?C~gM;K?o2-yvZXYj ziEh!KfHhW$S?Sp`H`1u-UR`WVApU}is}3tLkt>+@jbK`W%e$x8aqUJeR>YCn`=+yB zVw8l$ETh<3l_+5&2}wp(=ft$XR+MZQaTCuSgsG#HlX~-AFo??yVx(D8;~PI#_t%9* zWf6Rd{7&Jzf9?a95@VOmnjtB2F6-u+k>T%)T?50W{L?)7Oo6<-B|7?|kA9OGTpkEG zSE}E?e9f}$FFj<7rKovz;nr6=Ko5_s!*{By#S3D&nHVzNx6=E#!b6N5f#2ZJk##xJyT+#dEiBvfq8P) zN>exTG_nsXu=%NF!Ux;Jz(jdapV)~Rn9z$j4~U+941dCvHqoIUku*q| zSN4?YOcIP_fdFi;-b8IRFvhPw%H2Bc6n~Nh#ER6VO=bGIU}Tjqm1d_ zHFQ%i{ELP}J6)GdhY$ZadRWVL$QXSc3JYk#e z72?5;c-=hfj$!X;x6>(BR{L?PW#8qN294jtr@W*+M-a~2p2+~we?9s*X~YD+C_0(c zLxvHzL&YbQVl1W44~#W?CN0WCZ6DOewKlxU*X=cFd+=y>F}tB9p11HkNDY0tJcYT# ztN=^;@MMXeLoNT8;;Fm6=H|i&(v5cXBT~rQsA(A9?2bQzKL$=nOyDwH6!?^MkXl*u z1qU$+jR#$Ok|czWyiBB5?k?OlvS)9CApU=U?__H(I2Y($mUm{uHYS3chfN3vXd?sbuTVD0R zAGsgOjX5y)ggv$nZ0^UJYxV>S)9%O-hJx1q z^>5dnqF;4}EWmJ*f;)aDrc2I-Y@ec|X(_X1L_-{s6qfnOf&UEzL>WgU7k0n< zpCb{Z4I=+XSmu8?=UmJX{Ig^IAAc)8JtISVMb54{O7;@cmywO<^L~!G`EbrMvieD*?1$EZ`j@EC9X8B zef77Ojzq74#MW%}+eeF$bD)BdH%ep%{f>)MW@li)(ZW(UoJ3L9GGc|*B*@y2zq})+ zb#|$%W&U3vptgD$2b!mHLyyIsw&CCZCkO~S)6lp@sX5u;{U}BSqc~?$4zgd_uffQh zq-48J2K4&9&2q}$LuchQ#nl`4n}>t*$ps{n^g8!+``(BrnbcTHS5%P=>QSnDumLow zJCMp)wYI8iE7%W$#B>6f%vBxE1Q1on6&tvz); zq#3L5u&^eiL3vNblWD#Y1#Awylh`^P$8rMl#>`X2hY@_? zIv~9O)r;)?V<_E%guDTCQL1*jc^P$TVxW_+Iao9N9#!GeK&hiFE#edd<$d{M{ST($*-mri5C0cij0&?RgTkpkYQZ z``3O(hV7U|X+8{KB3)W=G&=%S7bpN>0d=zgW!vroL>2{p5eyy+Ue=>RdmOs2Ra-{y z7req$B_qrK>efiBdUh9Z1dhV@EMG4f`w6r37WO-d;Mcf_xDlbiC|~;CK3v|Q3t|XL zW3#EzmWfa?MY`TAHb&dgSLmW%M z$}tiKPzf@JT($Qs$II}N`dZLd{#*~mG+lNQwNlI24o7Iw(ZFyhBJQuL z*$J-+bM8~$)4nCbz#8H;IrNLi`OjUr$+R3Cgw@*TwBk^4deR+sf9L2e{xgqLtY&TA zsThYqn%%N7;8b-0@k=f^|uD! zzGZu~D_3rUdrTtTPxEi9-ye`qa0aq6ZK7Gb)mQE98rGc6VufQLT^Fd18{^?aw?t|0 z=s<+up;)gAf_20hDe;8xHAE*ZA2k5z#h%F zt7i*vaD>Ij-@SgNfx81HB6|?Y3S_k^x90A)JW$ucB)@Iyy$ZZ3-8V5J)#5-D6<5zS)IufRzKYvCgG$9{Xzj{r%%5 zHZatQ6!l?Ec7O&4(&2+QKa;C_&Km0r!Y5qB|bj>4GdbxAvg(0Jw?U6K8so( z>#<^RfYK`l2F4V?C1_JHi-`IXptU!%I7Yz#z_oSw7D+=*Z3E55esI03IbL~i`uFdp zmZD+kblN~*=!1c2Qwv}_py=5ExMc70eXDHT2!ANrr0m_83nyfSvZY=4>&0-B!N>1W z|IEFocHduJuruE}ARS(#Sd;ww!BA>t;FS%UODdnClgF^dD41TsZUD?gu69qJE}}RE zw^F^tybZ=4S5Nzka)Ui2YUqP`H74itJ3HLsj0_jkj0t*w>cbZ*Wdqlg(oOKj2;ki) zdj}EPP4m}7fB+CN4-!uErTxD^ydlmh(A82|uLjAa-fB0e#Z6eDcIBjBBO+R0-{9N- ztEbMos{u>-+U%3=uELKGPW+)|GEr`~}Q2zC0fW&j@r#Rt`LTWU|w zpr;xzfe*Hx_Yi~0mMJwY8))S{gzgDSPUQv<1g+Md9&sflY;wM5Ab>{4!mnxL zBW{=}H-x^=BL4<7LXpXG=&FM%J^1oKFTC5O1}n&*@9~7aLhL!f6)p1zD#Dt@02|#3 z@PW67;rvn1QhA`9g+f+H37m(^Qi3m{ZUi=;R50jEzPd72n!95JDiqgmKYRmM4s;d3 zSCJM3J)uGR8ManL2n=MT-qgRe{uT+2fFW5~jK3kyo6<(-)*LhZOAid>?nXy}+2`1rRUBwv@B@xx-Yrf*i;@emXJO!U1@)Qz%(^ANX<9A zT2gBfS5^FEoPh1)Ua6)2>XMf8s(a)?2bN_AOd_}GPf;>5euVD34FnKGJ`LoR*7*c; z0{SK8T?|JUf3F=b7RddCeog~-Qr@>}o=$WeoMB8V;;F(8Gads}-IePl2;7J=ADBDWQ`|5=2o+LCnx-lZJ!8$?zR5(5Jedt$Cn!-Qd82W{y% zvoX+fKxkK3+=!YU;M3GxT(>JHx;+ji5K=NIIQV&Bx#Ef!H2b&(@L|m(;u4@2#C4g# zKM@fY0ICgACz%Wjh^?%wte}2}Wj%Lz3?vdFhiGDA!fpw}LZi@(2D4Em{jqP&!=Ir` zYrE7-iTG%88RmnR56VNp@<4r3N8b&g*v?qq2w->OGcr0@5v2G#^r-=%LJZ7~T*3J! zM#?))I_d{NXs^%S1?lfMfo`4<0ZXS)mlfv>8RdK6Hx= zz%Clm&;wrQ542?CGZ;V&2SwAaivtCWU4dKTA0~v((P>*(mjO`7hyVf56(B5!m{G&C zi=+(%goH}GA?cB!tCkg7(D{uymz9>bFQMY5^G@geU(p{Yum4x{M;zPhAM|Imc<=uX z{YhBdObjsDhv_XdJDfW|a#I{yIrt{#)xuBrYovnSW10$VxVVEy@8Y0g@4cLNYDJtj_n^H*b6}?;uf<#n#*IW-)lBeO6X4d z;efpZP);o1$p>heVD5;@$?Kw(O{KAY_L6VlNxls-m#iRH2s#|~=d7%Z5$ji!``lf-eLmEB_|&<%<2j(rKxb4 z&fRK#p7;S{2}lG{-j{B4vqgvBBqi@!_F_D1m6#5U<;XqakBX}gQn*Syno2t&!af}^ z>6vWu%)@;Td`)?~)pG3Kg|EpxkMD~WunHPO1HXR!5DKCYkcC|-qdEd& zkc7mCj%)debPkRco~{xkZv9S$v>uNYIOD1-e@^|yt+{>7R87Le?9NG(y_i&go}Hfq z*#sOSyi9YgK&b(?5yeVec1WUMO93@F>*()l0M`o(_Qi*ge$#?{BjDq%`}X&@AW@SF ztT(@4eC%S|ab`?9bJ)Yqa~}A6O<3kS_Ng9~{akkhL;=ab^&!_2K-{J*gls4*+$j~p<`N@> z(x21mT>m?KRA~K%N_T7V@oSlrKS3yDEL>vw<&m;@BW_t~_v$raKgz5g0qu}Qb6?~j z2_1YM`z0iR#2Nrp4)9I-){iS>X2X?$UdcS3XoSRC3_LU87`6Z>8l$b5Cgw|60KK8qUSzGqk-5*J zdZT205?nXKnHnrh0UK>axC<$KhM)$}=;?ypRK@5r0Sc(Za5+}Sf@p2s5jQgutt4;T)R^27JTurj?aHdJI9N9fyC}`zX<%Pl(>gIRl z>_)E%-*fVbJl%I=w6Slf{Iv4d5}^u(xW$3;*Wz`IH&8kOjo;5Zl70yOh>WKos&F9W zej9K&@aWG$nFY~>0o=R61v?*( z=?MD@LUhQ18#J*Xv6(a&gCPyjd{fMpeLe>I*Xv+n51aYFrkucY^2lnmCx9B*JvbIb zzywe$HBJY>FrM8hVL$^ae_u zi_G@dhW5^`Q8o@`0j7I19(U$u$Uha&{-2J2iThUGU$8et6>sy`na@b{;WvrIEg180 zg<>dNDiC4(2g4ng&qOQzZiAX)EY)b+Po)#;Re*(XNryjaXdHY2*Epm`d-bA(Y$HlK zmQ#y{eo?1o#xEGZsIp$va2@^!a{=1_G#B{4|4}Q|vnGQEQw51O4h2i)OBgtVcr%;F zcu!*W#gr0kA}_re*LyJPqpV1H-PK1d;RJ(JZrZ+#w)Un!`TLS6eSeBKuKcr}RPzFhg@cy+ z(cWt`c=Uq5dv*?6H@+wsJ}O*{wd+piT^j9HwyHG<)5cz?`~&02tdX5UM9jfK52pF$%xMxB0tF$O_ z^^@FGV!y=~?iwGTXVLU8zupMTbQ}A`)8W~Zm29DjODV{)HP8@ja#u#HXL^uJajkiM zC_j?mSBsjQ($CLY5_(^J@}`arM!#-69WZ_3B!NluGcnZ6NkP#th9#k%AzR;zE_1hF z^HX8>eg-E0uV2<&9PsvK)g&}Zl=Ky;HJ2s2Y~
||*~ui}yF(u{1R#t#aAQlow} zC}Lh_!#ANFyURyJF4L05P!x3Shp8Ouy7on(-_HY(lX0a`WuT40et@-J^4X{M1ii<^=*&98yp^q@f>9B9>Vb_x*OVt~ z@5sX3K5ahU+jHqPY?hQT68dY(ilVx&b3UKm#F5iYePi-CY+^b^G(D=34r}>{*^3FY zZA%%mgYOkl`O=r$(m4}>84UK822DxJYi&7=w~}v?&DBmvt1bPQ9a?o)*%;)C+-#f& zho6S;eazjG0lgK0`@&6@ibEJNUzA)WdYte~GD*#iP0LBN4)XMmjYcj%>#*x*VDP(@ zK+|sv`pt>7s=L!|`K^beO^WM}Su ztdnyLi56~R{?)|sx9T6^mKhN+PU13AJa~aZg_1CA>RZXriV9PquIR5s51T7ZI^10B#Qto3mV-pPm-2!m4lh1>(BJ02hw?*M zT2&g;ANAH}uOai~O_pbJGk<3Gm%OixXoc&{q$(-Ch}Fh_$sWWpAuRS5D|%>&8uyhl z9mCG2(A%0y%=i|1UcH&yN*Zb@5yDMkFWWMS`Q_j6%=-uAeS+IFJ@_!1pvtu?AIn}E zXVI$qX-mg?pI{4-^M`AXKWi!CC)3gGeNtKyYiYGg?(%Lh*}Xj)ruH^fP&3)545M)K zHr*R}t>cD*D4mu1*`W`anD!1inD$Gz9rQAI2$+NWq*_;pY}_1la9h^<`W%uT`wV2` zo3Mot;CnZG$1_#(Sk!X%yH?pC{Opt0Skj4WNs)Q-#y@>3V@s%A%JK|NOpY~PNuU#Z zxB2wj=V4YsMJ|pE4SyP??`+AGxsAdietird^P>FnqUab74rQo%Hk$R6-JjQWFjPBN z@xY?c$dK;4^fgG{O`u=2FyXAO$!R69A!Z#>Ern@UtJ z=PkudP5vR}`YqrOC#5q729C1H=nwg!%4X4Yr6xlq6J54^swC8hsB$62uCc~EWDK?g zH3k#23CnqGNvJYx+gD#GE>I3;^grsykY4M~xz8}@t-|&`DZ2~KUTl<|>NXye;kS2$qL zaeu&O!}4?b`6XDs{|Qx_O0>}Y z>%vlZd*%~?#do$>@st^;^w&5VNYzErx7-ZTD-6H1naXixJLKi|d8)7CVoy5QRuNV< zNI8a7X4I^tDX1qLk@VqlCI!_Sk3Y7T&O}#d>D`h)^-(bOP@0&fyw~VGnmlFg>M{A2 zl|?+cOT@&7s8ppzdzza2x4(o^E1h+S!rFe9j=bsuWx8^FrsIU8*&QL1ln98rS{lB% zI)o@~%#ToUzNRKEi@iw{w(fcn_|uxr(Kq7I84Ek2x%;D}&cxfGRAT;@JVix%g;(TW ztW3YG$?T(~zS!l3*ohz!pbfm~8*w*4D6c8fuHI0IK6h7MbEAH~uZ=!u`5Iin`z;z< z&JOZ+G2XtBU)iss-VWr8?|${k$ABE0uw26L(R=H%>Waa9+QDH(W;hqqDV zT8`;l+C0e)6-vz&>-9FWb?l^&I~2>cvYO;~Dw9&*YTtSDLTR>k=^-2JWPFX@66k3f z`mclVorc_rv_gfRb!6WkG-Y3D@KTaw(A?|iuF4wP2|(jX#iOKogcbO=2!rFj-l0aW zXnNb`RzYMKkDYB9pL%S3P{!b?hTO*<^b|1`-xW&sA+s+krbT&C9@T1kRGkj*cfTk* zugd6s)tRBBffgC{y+$-l#R_&zNq(*gl6<^doiy(F2I0!ieY;=n@{hTeDp(a$j;q>> zD{b8B&6P18W!CVC`b4!As|;s4{nIc1`T#()&+jLuMF$*yHLLg8-|-zV9xTop>g{IP znzA-23cMjS5ZHEIpQ^Lb`|)2zmfXP={=Yfarru-HBEC_XuHwdM8j27!Ov+_>8Z}m{ zLhilzRe!6vJK4Y4{RU48p*F;Df-n~htnUIx`qrN$1;6{k!XRY6k$a{7R@1?ikS`); zvD!C%pZ7++SY5vlFLN)W{idG;2T9FP0|_=^r3b&rk9RV2Vb0HB^Vrto$h0?8U~@@T z8L+8vkiW?oDT8@LO@4Y~6yhN4aNg=laEpwT-YgXjbDU&&NWXo9A2qDaQdl>C;H-2o zn4Yr<&*gM+Q1QfclOabC9B>y3;P<{LmzfY5H7)ni**Gsp8mIK9*ropz^)eKc`{?n@ zH1;xfnz9i&;lqXWbY|f_O`>|iX1;qiA%^PSU(@YPnl%Y-zYWboB}y26NznB32~VfA ziO^aP%>6G}KjRG~&XY*63cez&_!+D9p(z=6 z@`sS|x1=7ZgzRZQFhYn!JWOFS@X0UGF7gKZJ@hYq4vCw!%-@r9b1jQI%|w!UquivlNGjhk7!iX((9e#P1C(R8yEI{ zLzTn~F&Dz7R(->S4z6Z@x!U?$VK1@D_zd?KdA5vsbd`r54>n{jJ9EUwH?)5yv!YI_ z6O}8!S}2hqEcGBmZ`ZMn#pbilj9@0FCM@zkUBj7sjBC7eA}tszjozf69dAXrjfTm6 z=*qZ>ue*;v9IEHV6}kAsT)sE6{~x4f=2gN{MQzB85E<$B@87|e;jzgCG&@00Wl=wd zin39s2`06kN0~K$R~&ALZ8s3OB|Cr27uMmKB{AVIpa&EMRBsnWjD*>#`f*hM&6(hK z{pZ1?kMTbbCjVc5l(<^qnaD;dd*?qtC)w=yzur@}2qey9mKgmE8{HGx?urP{c~N^C zXTW!fc{K7-;d_=ZFMMjonD#vvhKRqapDPGod-FvS_ zZXJ1zvwlMh)gR+b4@N`WP-I4ARi7w|rf76Hj`!L~IcSrkLwZLstHkK1&P>uR8eJO1Dl zx10B?vM-I?P&ATF`wT88y`boen1w)R+f(yG3zJp=1e|OzHDJ^YN;GLu;X)dbP(m&M zQFQ!RCdwfHat-BgON{JGs&gz|HE|DaUDr3N58|18oj30>Os?6rO**_iD^n}4sn$8@ z@cHtF5wv+U?fXPMBkn-Y9OQ07VciWLodK+Xm`fl?0rbG-fWVM~`J_dxAwMV1u9qe& zrS5!s*ij32z4Q7xGO8< zIQ@nbgrub8Mb{K2386e=tB^VG$27h}{X9Fi?(-sFL-?4}08b*5io2^sU4s1E6>}&+ zWr0f?ba@KFJ_+7qFx^w5;h;MIDq3U=g69t+c=V6xMr1Azt3U{d+`$>u*pfx~0s4zc_v zYlSiJ`H>X}J-;KbS1>BO0ENE=sGKffa#~P$W<~e{k#UUmcR+BY+@VYgdi!ft`jDABE6*|uNFnzdJQavB8;VvjE zDhIgKJYX`YF9G)f86N18=om$VTLgjVcf0G}Tp<^S5NI}Px^Fch)w%~cWnR!qt}Cm9 z$?^$gJwXBe$_l3FqDj|i5Pi}5*6i{8k z5|t~1w@2h`v?}bsAH2KwqN2DfP9P2xAO_$`NPQmn`{fZdy%42nfZp_=A`Af=QHwya zto`c<%0TcZP*(c{-6>?sa>IwY}*3TWok1t1Q3tyxG};sxr1E9=rOIs$0t{-{ZM({cp7b3_T$o z+!T8WR(h|0i4|oQnrr-4CLqwWun|EQhZ$+@2~ukvU>UYalUqAF5M?Dq7MoeCVop7`T>qC+Dy#eu5M_H1THvz5ruObXW3@108 z{yuduEFD9%@R8*)0UA-jU_tX_L)GX!{Rmpb!OLfCY-})4e-BFjeGVfa@B-!)2=4~Q zCiKVWxz7a5+?$Bsi1Fi z2r@><)F%KId}oKVuhX+okUj!PS$GZrPA7m!$3W*$4|+!KP@&J#stgA(0zUZ@03q&x z?7?%y$6_tN<#a^5kv~WC{QY<17oOZcwXlrPR#>kvIw9O6@Vhp@c#zF4TTUAP(a)U z>vrD)NZ{Lo@Xi1<>7XLqfefC*g;JqKbcQ~G%K+GKM!E{np{8hzAr}!Sx-HjgzQd_^ zh&q#J$3Cb-A!=QKumE*42gIcA`Ps3+bs{G9M#NAMhRfR^c-28)#!$#K% z`an4qD-4sz!F<~ofYH%F)TRL~!+c4|v?7m*PueAN~Tk4?Je-wQiC z9(zPd2}RSUi4Zznw;y&#Aw*UEE(%znO1Qf60LzhW*9qVi^z-#Wa37I-foW$vXxGE? zY=LR|UWZr+Q4J#vb6!}?FQG3NIP3;JHVC)mfW&|^4}7@SB=Rwu58{>E&v>%oC!(7K>GfAb#*|5y${Gu{y)Tkl5H8bwBgujs>eTsJ?v`ce8XI8T(O9&}9Ix_{$266eR<-8I z{&jYn6Gk~*?XM&rOn9u|8#Md=+dCJ_RwnbZ0}B*&@#YX#-6vEP-@4|tx@UZO&%1_C zj2@kNtp;mEd3Du$tDg#(;N*K-i!Np!+N|ZC+IHL$>ZIg5PdYEhhXk=18jgRN3)QoK znsc6;czeTK_v1W>D~J}Q7ENdcSDj3il3#BHL58I z1%%5>BWjQ&KvS)5-O&!_)sJd|gMuvONg$MiM`Qm1L^h~u`K9yYv_fXro}rY9@RyKo zHNJYZ1=fz7^sz|>)8AN*PC{)?Ckc00GxrwNtt4YF@ohEYBMz-jlTSuLt!o?f-VjNv z9oR@7A0}5r%N+^m;Ck~3vQQ9IO6RFhGX>1;4@WBL7$+!e>Ae0M1_!Z$e0_053=&*M z``80vh<+Qy5_AhkSGutJ$H(C-*G!VZT0rzPpwWGZ3dj`bY;}U02&@v}YS>k>py#XU zJne(>B`l29Y?u+@)VnVIk@zwgnDF`J#_qTf%!*h{Z!RkV(33zQ_K>hMm^($yM zz&QaxDw2qf`Bil%NNEui6LHRf-xh>6H6qaSh9w^N!XryM@}+c^~SV*psk8hL;4Gxzz``6AVxukWa4pWGV~DNY`trWgQxk@q3ct$uaP@{+!fP`!7vXD zs$eYOJ6m}FviC0skT`(sAN&|x^6pmW|^0?Vd&QWwjYcfDu;tVC&pjL*Ko z!r;-W0zP2@ON8u+oCyezAgyG;+-3lbKn@Ouugqz4GIy@SUolSh z>~4f0g2$j6BHro#U2fv*Zt z?NSTKgF*HKaWDg8G-T=K!$k(tsTtPM>UAT2;wn&M>x|(Z7+HNeT3`_PP5f+y5fmce z(DVgCbYYhPaARc;jq7x0+T?1+*i%IL3*HCvKLTJXAOY8zDgg#;&r^6v_W6i|WyLZL zq|G1(=UnXpSoEi58-=0> zqE6?~0RDt%QxM7Zfk{Q2!w@+fKo&bnE#g^|WF(0Gh>jnuk6j%^Q%9(!faIPiww42r zJ=eGIwatHNTxh2} z`~B?>x9XI0HXJX)ua||{vmi^KE@OxkhexODenej;P>3k?W%4`jJYNHj8#{1PCSq zGQYBJ1(zNUqLWtYXC*QqWxL(MLJO{|h_M*|fhJ;m1^XVd%YtMplr<2#9{7@T_qV}# zS|jB^>%k<&9SHKfu{*G<>%+13hNYWx>|Y4pnqbn4#CASd>~FT*avZIC;azn|-u7cF zmfirg1`F`%#%b$^kdeZS$5+K+;KLAJOwB0PCy@@ZbVjsp9Tsj~_6rWq9iVzlMb&U% zodHNCvgY9$cXc0l(}0i|yuAAFrMh!zUg zwcaZfSScwfAP)w)URzI3?vB1CFxQb?hJcKn+kjLPE*{>c+p|}cln}W;E;4LnVhJ8> zf;_1m3K~r zErCSjW5~jB+&|w(e+0@yIu%GrW{${|$WTe80P_I=0~#nlf;FU*Bf3~RbDOK1{)RJ9BGauatM z2TL%EN3dn?WP4>T6UVZ$F?g;O~ZG+}vg z!g=W-3AIA1v@YCwxKK#2f-}&m?+$|3*}{Q|)wuUAqR6+svvUN1&IOSU1grb(xY>?t zHInBcN{odEud$L!$4m&Xd8-GpJEgeH#ewU$s z04dBL4j$09z7V^CL@~&u{QM)(h!`>hk!(gmn|vi>>p#s)#C)$0v{>QZ%;hiFa8*Tu;2n3NYgh@oe8M)qXT>06M z|3{v7&;fUb(~?KEkhb$QI^-co$iyCu`OLUg;I=}Z)&u@xqctA9Kw|PkA~|0&W@%P5 zE)>%#=ExgdY*L&5>cAD*`m3@tN24OT9r&+o{bmU`GF=R=dc3U*i=0gRWJ`hXYlIG) z=i-OAuV+6tRy^;l`F%u1uBE?z#zaQfO_@91{kh}!;>IPFyxDp+dw-Lzv&1zNZf;L) z@>8BWNj!a<@4kst=}~5~e;#3WP5SNH5y4{B|LN*$ZTBrvz_vudAu&&t5wc722yTq# zm?z=%MAG{GUt2!JkqgQmQHE>Q-VFG)`;7oJwyzzwE4_hAp%2I@MZ>sK3}Vjb{AY^F zQ)v<|csn8yizqCExHNKnfdmj4T}1Qf9oMs&-bK#4< zN9!d&wUyn^E!w}Iw2!`D$*QV~0Rk}+4tn~9qwdwFGcz+mE@#0J$T=R?FP!N3&bESI zAChDs<1g^m+tv9^%l5;(mJsL;ct04&TeJk-fz27I00Q7>3qi(qSk358ge^G}Vk-+a zz%ccf+s(%aLdrJpUb_vMj*LQ8p&Q--4w{u#0k=FZGs^BP8Xp9zm1{rGDouOOebtLkKhGzYK~2j zG{K^Dq8S+8BiKxkbP@_BTk8WlFAKlJ4>#PbX+`XkmL3WFa|MCkH)+gdLJ87TNqZir zACt5&4@cTpz_BBxP2lQ?eH#Rgjp-hbTVF=XB(SX*!GRbtP=$g-+|n9mUly5Xm+{|z z2l2l*NW?#9q`#7mbP^f+=0DP6nr1s1?4wZrUmSKAj3dUBvINT#lp<_)aMDKpInmmOJFo-)P%M&Nv$3rx?x99H9T)A7tU=e`eB}>EC%Mun}UVCxT z`a%20z2LP8^jJi^P%k>>wAZ3O|I<20{>uN?juZIH#VGb;hskL!2n{%>xA^oIh+4@hee?ja~*@*gL#rm^j`t^vG00|`t2~1kX#{QSTP70+m``@Sk zxf_b#KzdqJQZ*yFy%uI~Hj>zVP=UQ#vS@305PoTZUr|+1_rqt)b&bP*1tlNHE#1B0 zF8YkSX?dunDzrR&e%PMd@?+C`1}skb0+rWgMy}o994L1{12@PQGjlI3xwU0zxmrci zhVjs%lo(>P_YK0P?9HJ0yN>_E@A{352wDBrFr~6lmpa+noczEAR;t{XB4LA?c~(4S zS>7z|;ACc!S(e~hiXw%VPW_xuz6{$W?3I|bZ}Dp?bSwssCFR>ZzjRqPz|7Y5pR3AF zdjTY@d13l@#O$uze%G+9Yi5eqH?7=BgZ>Z|@5qt156^+LEsWVJR*a&qChMji)jN|r zL@Q`g6}?DxaGaOGWPiNF4K zCa=eM=wN~`F|KjeKP6B`Lkn^>P|;QFM1pAK(1tFf`Q5~-LJ zwmf7H_eg%hI*INRt_2ksv4wlQ&-et4aFTFs3p4do48D~Kea zi|XFIy3WPGA1RGVpM5V6J(`}`VS3;Aeko>J-9}cS==X-Ma1S5A2lGSfivnB%yiyc?D&IWh?%#2b=_clR;Aw8UOrgA!f%pio7znV_pBaSAq$Ww4m4UsSinJvl<^ zsV%15-Mo|dJ9hxRfDg>p9fvZH^+h0UtfNMH_5{~V2DlAmDbH*fLvP!L$;%|}MeRZ0 zzfq%33BUuGgWne`>kpc;PTRZnIs6Q>OjD2-QA)}a`;{QCcP|TAxZoBSj@Z4*?N(;% z-V)QL#`strBug+|(htO?aY=W(ZVKV>rn4%yySN|dtVo6y!Y60^u z-RG(NFR8eCT{ALzskk$=KT*CQ=a8XoP3&iA7Tf%CyOSmiZo*zamLid5aP#l1oAROe zxC#}nZ^_l4diG-1eY>9I)D;!0w=~WZI&oF%kMrIe7rtzZ`|xs^`t0*zWPVmqoqiBdI~)B~NitKF)X34FlIDk`n6}G1xCgax>+XyA_VQAb1eB+Ca*KwE z{AAo+yDbBc+8lw>RQm4S3m8wtj)otAFRB+2$CtSciZHP5jTZ=njPc5El~>g<8o2LnmtJ}OLcGJYCMQpjwwX=6nBK|y@yJ> zrzaP0spu7cy_R80N89|fwN&M}t2Tq-eiO+KaZwPK!QY%JvDHKa#k^cW5`5j1^iH2d zw2ihG8u7>5raahb5jhrA*B)iAbuds&{)H5;ZZ zcqQuGvd{@)YPck;q+SZy-&Z-pVOn-6N|P-nSy=BjsUy+g*pnApUwFpL#z~?NZViv7 z<&btr-m%;_97f|O3$wiU_2FQus46a{GX7P&jxA@77)zGl8TpiFLAg1?9~$OK^}gKJ zr^+|=`&7G)-mW829)OwF;>29?JwxYB36n6rLmBrch4(F)2__XR&;J!$RW4%d-9rBk ztpTd~<$XXms2@ze9a!Jn^Fq-_6)co!(%zWy&E~Gzd<_e0G_CahgGoWXk2gf$E?Gao z#3WP_#+0?9m?ym_H@$o4Y6YJj*2fY$E~y%u&YRjz94l9qK$`mL-Mst^Ig8@Rr7`MT z#iqB>Z{JXV%}*>T3mO4!UW+_{Y{Ob5< ztejBFfG{lWTZNKx-!ml)@GdUzzu?^>jKWY2ML~B(4D7V4;wh}OBI~70ujYB_GVM=( zqxGc(ZoL*y@x61^-^1#lHD|?tl3`d(&4*NGBwwvd1>3zN9q3R+dB_XQP z47>vY%fmUMv88VyM0?g+;oTDXmGEes{6>IUO5R|k!Vg0ZJMCcSN!R9>+a#hX%!xnV zwLZu(@4p`I{n2mC;}mb_&9i{n-) z62FIf&0d;cd&0^6hY(}(;ukzEj=(m9N6Gzl7uoAIOyvQIocOD4;e7M$2tkVDBdI`UUNH< zhA34#uRwsh&~wW?HE&SDzAVfu^y_b)c|uD_pWrV^yQH*i_EP){uOq$KN8u(I5L$As zGu0(C*qh#2j?UW-=Bf6;g3$lTD7R5h87?e31hG^Dy6`PNkG)A8ZnM=1CTp~!B6_FcxeG_MA~qpUYwLp+bNQvn!Nbf)G8sQ$ zOOnnI%Df%TV%%F|KnM8;_)5W6KIu*@gGXYXYM_WwyUygDA!aict_!5E34Zy*2$EskY+L4(Aw_wNonfo zz((?G_x+gB8=kbNNM3d7#dr^AhI*Xnhm9WC5kXL)~c18pA?p z{JuOy$kW3zyk&eA5jT44NtI}dR_9h%1PoK*L;qCh6g_L~s+r?zS@qiy6Jl)PN_zW` zyq-rl;dfE{?8}ZVV#6v)&gn_WugCpPv31z%{&;+DP6_DOU=`Hk*f`Ebn5s}_RdKZxvKtKU00i>xk0U`9zYk;}W`=43! zewxqoto3{lLUPX8d*5YeU%zYPpHvFN5m7&klH!A3TmMRuLd{ntr-!FvuU(3N-u}2a z{?hIF`^5Vvv7h3<+_(5sj;Ya;cFJ!EHN<2@yAGTx`Pyf})xMvi^5i6W=~v2}y82k~ z-gXyn_tuaBNtd|?$96@EQ$Tz|0v%4|Zkvl)C_E5+Zzt|SUU3$x#5?||6An$-LTfJsZ@Oms zTl6-Uk^0HI9-bBj1bv%-cSG(743_?le%iF{+{Z-8K9ajMcqKaJTJ}GD{>tu?Xgcbg zCw2KN{iC#cG66M5lD9rAJM(QT;8Ha9_^VeZ;&;D>&zm`SI(gzkDEcA}8|&qwy~Op0|p_85pb^3b~)%BWldM||V9ZfD3Yc4_hgxg;# zi^HUL8Mk&>s3I~=9*s5oE)8AAjS8eb5XB29W*B=jZblNam|u$wC5tAF<|aRp6HUFf z!E;>exxcZ)e0k?-6fxtvQXOGy$UC%YZI0IK%cBbSZ~{MtOKalI)W8Pr^w*Vw=K1}) zB{VCFuAkR}_+f}iDvjZZb{%(C15ann+^jl~rSKN5ZN%~E3Y z<7xvM6ZhRwk~4@2ef?XIbh(9)K$vL*5knF7*W{9mYQU*R+P5ja9wx;N1NttMt*OS| zLH@=?=N{j(-_oF;gVnfv)W?NA$V!`OecG`;%6QhNMuwB`Ox0Dk-~3URDGtw_xma-M zL|tyl>bb;}4@)D;l#!QY(L@XSS)Vf9=E>*kzO+V+Ip?0N7!MflXRfA1W4u>WwXO`g zyl>!;raMvlL47R1`5@;O(Jg39jMltBFgcfRi%j$i`qr|u%lb?A(vY6Bk!D3I zEJ$O{-24Z9o*vItrXH+M5Gk9!w7E~uw>4__JCx5PM&%b<+H~+2;AWP#7*KOHFPlxG@@PF?3Z~SX0J?#pAa5o(n(442;r_>06LB*pOD<UHOwMHynhgfZh8C?kQ-Co+?lx4fj*446;)jDc1_bb3ewGfLFEiJ&}&QRZ;^7C2{ zNg{1dLwXP)XjCi=#qa(uAYK=}`E3jDc-mm7qeV{k);rC6yRpGV9ARIkc}6HV&jb+Z znYJ;c6buO`Tu61GSGYQ5%^F2Hu&(7%n;V%TBx$znn_JYjPeqzrvkNI=nEc6#aqsBA zk@KwEmu}y)?7(aA-C2IVKHYLv;@YF8n#Be_#lTIRgp=Lj2`}Hy)c8++zC}O(iqWM0 zOn2UGF80V%d3;3)uW8yFM_F}N)yK+!yL6#T_;7NS(%85?C3;^w%Zx=7-5|fKcFM*7 zk=br3S3P~j0Zr1Ri0k8qzujq+XzjiI$}wLv`R-OjjPIWtiG69AeyD1aUzIv>K^Rl* zjB{c)>U^rgc-t`D!X^mqaPtUFAMsV&=>xq?!A*vw$069!{Q=AL&92COdz~snVj0Qh z%hP(T8oJx;y`JV3QV-uX2s_NN_$Zhz<>@7@O0@s^Xm}cHAg@XZVPa~Jvb32=6+LY9 zD~?KE0LJUSYS2{?mnQ$hQp-FQ*^=cybI}Q6$;=ved-3b(xUCq`@7hb<+}xkR@%n!v_Z`h1KloW ziOWSaTDJ7DJ9f%}&V3|Ik+Fr#F0Xq%ouClkOkwfF-RN6;bh0(+dudrJG3#H^JSH8; z6kFN2dm7gMf{=96u>z4->KPM^Yn({ncJv^S5*g}4< z{$yHj$n}6-?RN?_b?2t>K#1bsdQVJexZ1CSfL5CUl*CdGrAY^etn_a4GFPMbb)WJF zMB_GBBfO$%j|P|6+rO=7;Zdc%vQ2Mki3zYA+3XL}ck#L+nPAD-t7|&K`rP3`?qhuT zQk1dv3f|888elVV&p>{{DiVT2-z*HzVvVz_tjo774YI)gjW z{bF*?^TpdY`UbLD`jt)Vn>a)wzUSBtT$(s2xO^%1RL#qs@}kPKBz{YuJ<{e^qI({B z)Q%D!G|zCFpzFtmH-)ksTYc(^N#2iKkJ>G;B}F)B=?n>F||yyY%E5NREgSh&>gh*XH=na=HR-=mEvOpdusjphh!x4<>)|&d&_FXNDNe=6pi_+4Y zA?%w>jc?ehbxc<^xOii<&Tp`e6i)NO`KXo zD6++5Rw83@esg!l@p?C=zgBQR7CsUwx=>XFF+wENBc(mR)Ul*;}be&^cwk7{GN-$ao<5n zCmx>?UCv8CJyy=sP`VqYI?6H!9cPXokq1o#N(~J2#xAq%TT%c-7Gxkp3Y^syZ@hK*>j0R9)X!y0{!-G~e{`NbE z&(AoUxw@_XZlWnZTe7q^Noj0%+t0w4TuOlY!0VbM(ZgfLI;gUaQR04@yG zDDk<7ly$S%xu-MbG=@p_8q7Fnd_%@CZiFN4(S%MG14^RzbnEMhm~*BqF>PMG%4ROc z=@0x2JhXnleQhMGEAUU6EIFoy`!IjuNUhiBFZtu~b3RWv#EupWso#nBI!af}xB9e{Xj-k8{8 z%s%Q=r*M~|XvDiGPMogxsBRU*1C^^o!%hbSKknDGo_lyY`14O4-{K`v3ATZpg|tu7 z1`A`8{ixF?a%`R|4B)EomOu0QG@9c#wovBQam-Rp>#2h#Hn>F9Is~fDJigEt|2M35d(e34PL3so0@|bv8;vc0 zoK5+$p4|q`^E+$R!w|=&+*|3R$eG&|x%cgbkUIGR+s5&z*dQ`PHu!0ts zq>ho0lZv=zUCR;UGPU@EHOs|ksQ0F`+vEy=!(#Zh1ZQUMV;hxpUj5I0n_YI}dQ~cw z0=b>eQn`fqlUq(PPv1;v2B>r>_K)`T@%dNT>GqxLc6FtZFnNAfrl2yHKE9*$}%|GP0+;>I#ux#u2QNr|J>eB{5Y zi$f3OaJq23&rzp-Ut@dul5*AuL%#!`VuKV>Zt7`zT^4u-zH@qiMQnc-dst^$S+ifs z%vmD_aE&fAC9Aw2eAxdnfIf@H1)rGF7E<9|yNJRL^mQz@9PyK|SD zp~1{0exSD^*>q3zv2vxrY^H?japsC>U-#h}(ZxGNPLTC3lC`q`nQZG-SE0&3J*c%G z>Zm+!(xcvImw(Y_p#8P&`a$UN{cyL*o3pXsvT{)d2@yFqUa95g1}UwvnwOTNDO`r; zg;flKiGe~5X1x}VQl5BTve9HUOBwPcw3#hbc`wJ$`o%Zr1W;1SH0Ak5#~qd9%drg( z9&HJxb|*TDW0#84G41@6Ps@8{5J+=IuLzLb zRv7+R$evusr~XQ9k1aTT&`)vLF+s?+QMi&u$%pG;FT5$2bJYJuX3hx$D7fQC7u&oz zG{lnJO0{=y?}VyQq1Qbc-3qEsQZ$uLtIySoa);xF}#3I#|T#Hv7ZwCyoqBL)ibWO>=i*HRIj~lk-0V z8B89_;i#*uSRw*r7!tN!s#Qpts8>0d@=sca&ix&tW0g>CTOdsrE$=dK7Y)bh*(rA@ zqJE%B(<70Y$+G&Zqk8{TMRimMLJZZpPFww<&h1y?HjW;C=dN<+!f6WrX|he;to4am z{7?K;;OUoFX}9cC6x0r%_15pBGU*-_783Hqqy;?J56`Q*aNJHjQ?NXxs@6^YJH_%)N4QT5i%VcQ_E+iK+f3HkF zY*92VwDyH;3p14RcK@jJA9Jlb)8;!g;`X__gAT`KNwWh66Vczz4HnWgQmg1Su@C(_ zUqmR&ensEDDv{TWvKbg|ReAs0>j&C&gpaP}(~YBbwf2wKoRT8j+UHpuq zj^%Y1RdN}KV{rrt^z?jk_FkG>pz76M&T`L}Bqnrd#!cTYvPp@KFS1n4wo5thm0kxj^wK;d22!G3 zY};}tSNVdIjtcvUW||4)Wbxb0S?1qKoiM&CVYq4jo-1J5x+qz|XO%rE@o}rN1p7zq zzqj^l6S#^)ycV&%JIVB%3-tJE-Sy@@;=*PBqxxUT!c~B5w(AxDou*2Z={HyQ7%?aLUTv8XCkM9#Z?%u^9e)vx$MW1;d`jqt`*mB& zbUHTLMbpf$9@XYzcj{F!{I?1IVwdRhm?S*CZfhCp5U$OB-r&WL9C~h)t6qhXvLMEh zd!N|kFAT6L3yG7}T@xzb+s6rGC7L6%Xznvux)26)1pUes`8QodxCp2(Pt&ljT8#Gn z3x2I*w{nX%k`lSI|H_Zld3Sf|i%9;S6)zh_5G98VW~W7dKEW^zo6gg1XaJK{JQ+;X)Shd zB)PeUv3+m&Bis}JdXVXautDD_X;>LkdECvSID*n18B3zu@P|Ka=QNf3BD!45RFf0z z%zsu@pT5T6?#syd!TRcVLv6cHs<26;pxI`XT^K}i*Dy39F7}>g<@EmG>g8uOrB~JZ zEPLYYq`ib}?VwxZ%rQC^iasq;OjK3(@MPX_)%V6QQ5n_)sdw~m6)t__Amrd>>xF)F zmgl8p*>=T!s$Y5hsJACzq(jsAH{02p+O9(6ynAyPlqoT^ z_N2Dcvvm$7d~HK}*WitX^n=VDL;A^qD2z&apN|oNYhpaC7KO{pn^~B%!jBr#oxmwR zB-J?Vn0bXAEcPK4rZJF6}L2At7Yz$60XXv zO{2WgpO-&IJjGCylV#@{AiU#SuxjPt+T$p8w93bRs+DT`Z&t~UR#YGJt3pX@&6oDL z6$$_5nQ^(z>)FvoUVr-Ytj1&=eED6K(8=~qjq1XRDQi6y9v!p#NBSeayC?TB)@m+9 z#M^VxVH{incJGG&=~b70GbL8<;$EEflTj^o>-apyu4?15+n|(z#rP%9rnN3!k7iO|->58?qrnxq z&xtPU?CXLRpm7pEFKq zu@6<9(Me^NR+czPJuAABy*-XjN>TY*)pby?;HUHUr>41T`XKca7uH-Qq1Dw`iru~Q zKdsGL$IrNNIA zq~q)^Jr!OWt`1DQ|MrUfXL)FfK6l4%hY>7l%IaK<4HB$AY-(dpQ#1;$3t#5@TDyv2 zcSF-v8ZnHZOJ-9E`)Fm+{gSxRrU%pyu_W^iHC??Fdw%hjk*jJG1$^<5Y{p#y2ifWc zi`uh1Z4;ZF`+VXpt1anQT;IQn{YOSI9B>cr6@MfMU~iu?inX{#R;JNP)KY61K7S;a z7T~edg}2CTvI*zMC8Lxm;k$ckb$dri!y%S8J}5?9@5JhuUt2O3^mw=aFC!_mb0aL zX{K61*E{~3+3`@)N$`KziSVtVIu{hV{~HhT;#|gmBT4?Bzk0Wqr+p9kyK!||8X9RZ z+zw6hBX40r?7=`t77NSE~+bh&qUc~K2F6@jX7d#qCz@P$E!lLEPB_ZqH zmdAfUo3#zX^eu3zC@CweA$6iaY67r&u!|qr^ro|bFT3Xl%Oij-KqiwBCD!fRw}Ykz zOUx)h1#5u!h~(1B1ssb7Xiq6SHbFUfo{i0DdL0}nK#3Aa@ri1@s1*F;!78~Ow9}4Q zY#N{v5MAy&ZtcZ1pM5w)eAi|ATv6Tr4tp{`2wy-9{sR90*z#1nKu-q1ukep6AO=UM z93XSWf@uoKIsy|C^wXZg?IJc4pn|J!YYPK4J=16mfDI5)Ed1f(;-bsKpS~@7gK9S% z9rq*0@o%RR6B8{*@OOb=a2OE+MssUBfe^3tJ?{g!QAC>%pQ$^%0qWeJU+nb%lsh=M z)&dNBY+*qe6srXU?YD7)77xL{I2jx-!UinbV{X&_?s zx7@B4`|`~{1hf#5@$s#2T$xgNx3rd-x{X@Sb%!vpJ!poqk>>5Be$e0|6*H^ktzxz| zD%4c;Q$ZE1dCG0^Kjd z6>s_RBrg|%#gm7(w+85zDZa3d7cGOHq}Za9jSUX4BU&~1`5n)dE1>S5 zk=73ZI}14A|9#gh0+d6c-UMfOn0!X~7SOMO<*_*!jv(4hcvY1Fc}5ZUMSPJRgVeixxKm)GlKb+oss{6aH%l?lWf&WK zp5vmWkyhDixj)OZ-dQ4=?;#`T7jr0REB#|Na8E_A*bRO`Bu5{!vdkH213)$nGjkXYM+Kky z5fl_WI>pbxf~LF9-~SL`vl&I)ctBn96aXRgqwEqA5))SL5KKVKJqC~e*We%xE$ug0 zIEd2fQ1q)}Fi?Ukei0USx>J0Y2OKmIKc8oml!SQUwhyP9K^xx$R`uY9GXZYbc=Adc zC_fQJG^n5?Vg3SFU$a23PX|%v1A~QGbt%PT{1ilsX5hRa7Z14me}GGn>=CL0m^C;E z-Ep2e19RegfB$=sS3I_{2}?*|hR1%1iz^h=vn4hIe4vjCE-e+bx3>qeZq={9idYaf z;e6jkMM;B>+ygKdH}oF~b3}p02K3Ue&%6Ooiiyd|?|9EuM`vda3??2lob&VZ(?H!| zgC#M&Rq45U4a6ar;a9K%e0+T&o>E71X(6UaXP5+D0M-2vqPT=l_Sju0uXLHY01B5e zkUV>MdUj{2vRn7(pF&jgRuGjyu6W~J0d}A42_ig6TU#Emzu|y|0FWJ+QenSs&4C?) zL}R!Lpi{#~MSNfJ{C!)@d*dU_GJng2@9A?OxVtHE!+r$+1F+wBo&KE+4-co;RfJgp zwBqNW*@GW916T&@;_?I{K%%JU1<(=t?O}lPf`DN_(+~IG2%7a@)$aTt+UzaRkAW-r z3-Y8gUW6zB6zMo&n@4HueTOLYEvM39H0k&>eu+fxH^THl(+Vr=m~cR2M0iEP%P6u zri~|(!KmPJ&)E0$1<+PODnuO)6?JoS1B}qalE>dKL?AvP28M>7pe&n#RSrUCVFd++ zV-yt5;NvlILKGkx)_u85&Fb|eRY{OS5nH3Vm11u`hl%L~2`p>|sP*7KwE_~OzY`P4 z!!9l<8Dl@#qS)Bj099I%8txUOT*$58z^4phD8y%u!3*I*99mjkg_-#0aU9Hh0gok< ziA^xH0qIhD`nPwmjRMRNyp_l z*-9r)UXq5@)eL6#IR=$0Vtd0NI0gI+3xuC1FonTjpaggsW;@^@;5in=fOwqUOz0p( z0BQQ%7(y)wQp#(p$*%(gk4o&VX_Ta_Hd0F)Hz;reP zz7~KqAZeR-KeavB--Bls1$JTp`?l&zJ+qHteQvpIYc-T{^d^)tv{SV;5;y9vPXcik6R2Fo4L z`{JNQCeIlkd;q`_FtLF>!VvZ28z@s1;35EsP6gDWaTsjGrNVx=>Xz@03kwTNtb~7U z$q;h$KyQi@vbq510aM^>971HxffUpLL%5D$X#hC!yULaOS>KlzDJdyfHZH z3rJy~Uf0MxBW&G!)Bc#4nAq~h2A>l%66=)CJ%gdw5!xbYrpr{B1 zKmV@UYn?0A{#l2)b4lCPYu5w-8-{UTd|h6ShIj<`TmLv2E|_sxY~_iCg&-gy5|feu z>ca}Mb5I%wA{JOrTFxOTEBLTGU{_%1aP%wKgRw)!p#q-@%itqO?tu_A@GBUiFp5|J ztL*|iy8{n1Bco*^Kg4V(E)+{rXf9l60Bb&;eY*K7#Q6k3OfO+xL%AW{BLq_ILzdki zj)SdEgY9kSxqe>69t6l8P-lJp{TmXICjjm#pkj=Oh@jrxB%w#p;2b0;C&$9diW?A8 zo&cs$I^<8wcv&YW0VtxIh@eW30C*PI76wAij?~=S+lO;y5xczmd5;P#e}4QSwqUb7 z$_eWguo+#|?hX)hme<#xg0}6>ojb*l(eyrBN`h9^5rR3utP&;sM^2KVZtiQ*18~w8 z?Aem?a!x=Lz;bq6_#=$uR-h*xnzK8_$;mm}Utr_vn_`z*av%!({G-||T{FX)53he~ zygCdASH~||10e(Q`=uK9sswq6`?C^w)}^8tJdykh@eOggf>ihc zd<7v+j?d0ow>yKyAk1E#kv2FvYx(aGeGkl6Z*8l;XV0FcN6SHSxk@kkxuhfw7A+an zcPt`q?UpXXFusNW%I}ruU^p;b39ylQ6%o-qJY4f_qQAcWaMXgjOoJ@O38ovv}QMaW~kaH^Omv$CQ>r(NUbMeRa^%F8ynLpjh98Ps@1gL?re zCYzu`x#Pbt3i5GQ!1_&r9vvaZ$?zorLka}8QXuQadBOmmK7K_&K!B%8`YF&+5Hy*Y zStDqkLw`I$S}d?dx(Rb|Xm}Xw?w$ob3M}BTuV24jC_mb}O@#1DzuN*+WN63$MD~Yx zj;JD|e|L^x1-(HTDltbh=PfopCCkKd3d#bcUT$fThc~-nT#Lsp=D>|X24|7*O9V<2 zEcsV3SAa43t*MEg%T^~tkuGfEMk|y82pa||nu?5);FBg;;t~^WOH@|^jDetWSzMeA z3}qlbJb+Bgt|zIZjxmJCfP?~rftAAN5N3f#Hk5rSiU`q;mJ-=Gb8t$r8c>vzcRp|+YZVUrPl92hJhtPF6qgfN;X!&My> zMFZ|x^lcfLne>Af>+}f(f1WV_45U0*ojwpio}M zbDWArP+Y74eJd(9)>1v@B5(#lPk+m2YYFl;01r~VTN7T|H1TggK}z0Jc7dfnU2!ch&0{gsDebN(wwjFJ8U+efsg({QT>b6n0Si-kN$KFMqv61hG*;WZ__OktP{<;@7WV zO9)agc5!}Q$=W)1c^7&t+?fkt@Q73aF#i#l1eB9xjW5Qc@dgHP{z|p{X=o~wCY)C`@!^Pn(YWy$8 zDRQPW2aT5kebClvd3QLy@6iHTT^gDns7vV^cgt*lzXijjw~>*OK+l5pXc+mu;2xE8 zIhb3un+rm%T~Sd%+bVwY?AfV?pwm#?q115GP~V}@_6Fk^@Y8SxoWQ(TJfN+|Q|#C$BG8bPx!)XHn3pIKq4Bm02IZmxOTzQVSF$^&j#=gptM^ z1jryP1a4fx2qWziGc)N&kKSV&jnts>fjn#u;_2R8tuR2;B>~?GJjuSpiu0Vwh@s@A zVPI&6GUh-3pz_zvQem~VvqP-4faHj9@SqzTBatG0+aflj4V|3`yFmBY;f48m$Q7rM zNG&KB1?92Z!XK?C49B3pfd^3Ovn>GCra`4s5LEt0yK=R-5tl&-k5I_LyV6TY)P)%R zx(%LO)YR1T3k#KwV@IHq{@l|3pM*!vehnHZ$ec%J9k9#eGU@$LYM+6dczzKCq(V&OO6H1m|%fFawxM?a5*ZfM*+@o|cE=5qJAp*_ZqEHlKx) zm@N}4&OF$hSv&RqVvxty6+}S zuB1!DczN_GY`h@`$G{vY1#%>Wc`MinLAImv*~{UDVA&ZA4?#*=I>)g3Hl*#((^I__ zSTnczU)X`NL&!D?AOqUq^B~s{OPblL^AcL2O;9Vd)`Fdi79jf|`Q-Z=gRxH}1V`Y= zT>u;xymK1NHpDpca*zmY9eE?WKD4nuP}5KUD&c^TVG7V11aJhMJmS*>TVicM@Tzc{ zq=d>oN=CgEawg)a2~9h~z&iln=nB_4dMF8G)k;!f%AIFt?*y(zOKa-~*nW{Uj0bE( zvDdmaY|y-bT?JTK{t9mQp!`6L?2w>jWMtHvr``FX*c>YAoQD~dP!-6c&qK`wdoYbK z8n`rL*Z`4tbS&=cT-n&jgdP`4+v8BWaA_ARfvYQ270*ycPOv>dTuo=~v*4yj!-|rdUgtiJ902nQq48`c_fg*Oe9Hb|Q zRD`sb5IEs-5c}*FJuVd}Tc96%0#o$atT<4epp!EY/dev/null 2>&1; then + return 0 # Use systemd + fi + return 1 # Use manual scripts +} + +# ------------------------------------------------------------ +# Main +# ------------------------------------------------------------ + +# ------------------------------------------------------------ +# Require non root user +# ------------------------------------------------------------ +if [[ $EUID -eq 0 ]]; then + echo "ERROR: install.sh must be run as a non-root user" + exit 1 +fi + +# ------------------------------------------------------------ +# Requirements +# ------------------------------------------------------------ +for cmd in awk curl grep jq nc sed sha256sum tar unzip; do + command -v "$cmd" >/dev/null || { + echo "ERROR: $cmd is required" + exit 1 + } +done + +# ------------------------------------------------------------ +# Resolve ROOT +# ------------------------------------------------------------ +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT="$(cd "$SCRIPT_DIR" && pwd)" + +CONFIG_FILE="$ROOT/config.json" +META_FILE="$ROOT/metadata.json" + +[[ -f "$CONFIG_FILE" ]] || { echo "ERROR: config.json not found"; exit 1; } +[[ -f "$META_FILE" ]] || { echo "ERROR: metadata.json not found"; exit 1; } + +# ------------------------------------------------------------ +# Hostname detection +# ------------------------------------------------------------ +HOSTNAME="$(hostname)" + +# ------------------------------------------------------------ +# OS / platform detection +# ------------------------------------------------------------ +OS_ID="unknown" +OS_LIKE="" +OS_NAME="" +OS_VERSION="" +OS_FAMILY="unknown" +OS_ARCH="unknown" +OS_PLATFORM="unknown" + +if [[ -f /etc/os-release ]]; then + # shellcheck disable=SC1091 + . /etc/os-release + OS_ID="${ID:-unknown}" + OS_LIKE="${ID_LIKE:-}" + OS_NAME="${NAME:-unknown}" + OS_VERSION="${VERSION_ID:-}" +fi + +case "$OS_ID" in + debian|ubuntu|raspbian|zorin) + OS_FAMILY="debian" + OS_PLATFORM="linux" + ;; + rhel|rocky|almalinux|centos|fedora) + OS_FAMILY="rhel" + OS_PLATFORM="linux" + ;; + *) + if [[ "$OS_LIKE" == *debian* ]]; then + OS_FAMILY="debian" + elif [[ "$OS_LIKE" == *rhel* ]]; then + OS_FAMILY="rhel" + fi + ;; +esac + +# ------------------------------------------------------------ +# CPU architecture detection +# ------------------------------------------------------------ +ARCH_RAW="$(uname -m)" + +case "$ARCH_RAW" in + x86_64) OS_ARCH="x64" ;; + aarch64|arm64) OS_ARCH="arm64" ;; + armv7l) OS_ARCH="armv7l" ;; + *) + echo "ERROR: Unsupported architecture: $ARCH_RAW" + exit 1 + ;; +esac + +PLATFORM="${OS_FAMILY}:${OS_ARCH}" + + +# ------------------------------------------------------------ +# SELinux detection (warn-only) +# ------------------------------------------------------------ +SELINUX_MODE="disabled" + +if command -v getenforce >/dev/null 2>&1; then + SELINUX_MODE="$(getenforce | tr '[:upper:]' '[:lower:]')" +fi + + +# ------------------------------------------------------------ +# Setting variables +# ------------------------------------------------------------ + +VERSION="$(jq -r '.METADATA.version' "$META_FILE")" +VERSION_DATE="$(jq -r '.METADATA.version_date' "$META_FILE")" +COPYRIGHT="$(jq -r '.METADATA.copyright' "$META_FILE")" +AUTHOR="$(jq -r '.METADATA.author' "$META_FILE")" +LICENSE="$(jq -r '.METADATA.license' "$META_FILE")" +HOMEPAGE="$(jq -r '.METADATA.homepage' "$META_FILE")" +REPO="$(jq -r '.METADATA.git_repo' "$META_FILE")" + + +# ------------------------------------------------------------ +# NodeJS detection +# ------------------------------------------------------------ +NODE_VERSION="$(jq -r '.NODEJS.VERSION' "$CONFIG_FILE")" +NODE_PLATFORMS="$(jq -r '.NODEJS.TARGETS[]' "$CONFIG_FILE" | tr '\n' ' ')" +NODE_PLATFORMS="${NODE_PLATFORMS%% }" + + +echo "####################################" +echo "Node Web App (NWA)" +echo "####################################" +echo " Version: $VERSION" +echo " Version Date: $VERSION_DATE" +echo " Copyright: (C) $COPYRIGHT" +echo " Author: $AUTHOR" +echo " License: $LICENSE" +echo " Homepage: $HOMEPAGE" +echo " Repository: $REPO" +echo "" + +echo "NodeJS:" +echo " Version: $NODE_VERSION" +echo " Platforms: " +for np in $NODE_PLATFORMS; do + echo " $np" +done +echo "" + +echo "Target System:" +echo " Hostname: $HOSTNAME" +echo " OS: $OS_NAME $OS_VERSION" +echo " Family: $OS_FAMILY" +echo " Platform: $OS_PLATFORM" +echo " Arch: $OS_ARCH" +echo " systemd: user services available" +echo " SELinux: $SELINUX_MODE" +echo "" + +if [[ "$OS_PLATFORM" == "win32" ]]; then + echo "ERROR: win32 host install not supported by this installer" + exit 1 +fi + +echo "Directories:" +echo " ROOT: $ROOT" +resolve_dirs "$CONFIG_FILE" + + +# ------------------------------------------------------------ +# Build sdl-mgr tarball to DIST +# Always rebuild sdl-mgr_version.tgz from source +# ------------------------------------------------------------ +echo "" +echo "Generating: $DIST/sdl-mgr_$VERSION.tgz" +tar -czf "$DIST/sdl-mgr_$VERSION.tgz" -C "$ROOT" sdl-mgr +sha256sum "$DIST/sdl-mgr_$VERSION.tgz" | awk '{sub(".*/","",$2); print}' > "$DIST/sdl-mgr_$VERSION.tgz.sha256" + +# ------------------------------------------------------------ +# Build sdl-wkr tarball to DIST +# Always rebuild sdl-wkr_version.tgz from source +# ------------------------------------------------------------ +echo "" +echo "Generating: $DIST/sdl-wkr_$VERSION.tgz" +tar -czf "$DIST/sdl-wkr_$VERSION.tgz" -C "$ROOT" sdl-wkr +sha256sum "$DIST/sdl-wkr_$VERSION.tgz" | awk '{sub(".*/","",$2); print}' > "$DIST/sdl-wkr_$VERSION.tgz.sha256" + +# ------------------------------------------------------------ +# Copy install-sdl-wkr.sh to DIST +# ------------------------------------------------------------ +echo "" +echo "Copying: $DIST/install-sdl-wkr.sh" +cp "$ROOT/sdl-wkr/scripts/install-sdl-wkr.sh" "$DIST/install-sdl-wkr.sh" +sha256sum "$DIST/install-sdl-wkr.sh" | awk '{sub(".*/","",$2); print}' > "$DIST/install-sdl-wkr.sh.sha256" + +# ------------------------------------------------------------ +# Fetch NodeJS Tarballs / Zips to DIST +# ------------------------------------------------------------ +echo "" +echo "Fetching NodeJS Binaries to $DIST" +SHASUM_URL="https://nodejs.org/dist/v${NODE_VERSION}/SHASUMS256.txt" +curl -sSL "$SHASUM_URL" -o "$DIST/NODE_SHASUMS256.txt" + +# Download NodeJS tarball + +for np in $NODE_PLATFORMS; do + IFS=":" read -r OS ARCH <<< "$np" + + case "$OS" in + linux) EXT="tar.xz" ;; + darwin) EXT="tar.gz" ;; + win) EXT="zip" ;; + *) echo "ERROR: Unknown NodeJS target OS: $OS"; exit 1 ;; + esac + + PLATFORM="${OS}-${ARCH}" + NODE_DIR="node-v${NODE_VERSION}-${PLATFORM}" + NODE_TARBALL="${NODE_DIR}.$EXT" + NODE_URL="https://nodejs.org/dist/v${NODE_VERSION}/${NODE_TARBALL}" + + echo " $np: $NODE_TARBALL" + + if [ ! -e "$DIST/$NODE_TARBALL" ]; then + + curl -sSL "$NODE_URL" -o "$DIST/$NODE_TARBALL" + + EXPECTED_SHA="$(grep " $NODE_TARBALL\$" "$DIST/NODE_SHASUMS256.txt" | awk '{print $1}')" + + if [[ -z "$EXPECTED_SHA" ]]; then + echo "Error: SHA256 not found for $NODE_TARBALL" + exit 1 + fi + + ACTUAL_SHA="$(sha256sum "$DIST/$NODE_TARBALL" | awk '{print $1}')" + + if [[ "$ACTUAL_SHA" != "$EXPECTED_SHA" ]]; then + echo "SHA mismatch $NODE_TARBALL" + rm -f "$DIST/$NODE_TARBALL" + exit 1 + fi + + echo "$EXPECTED_SHA $NODE_TARBALL" > "$DIST/$NODE_TARBALL.sha256" + fi +done + +unset OS ARCH + + +# ------------------------------------------------------------ +# Extract sdl-mgr-VERSION tarball from DIST to MGR +# Symlink to MGR/current +# ------------------------------------------------------------ +echo "" +echo "Extracting: $DIST/sdl-mgr_$VERSION.tgz to $MGR/sdl-mgr_$VERSION" +rm -rf "$MGR/sdl-mgr_$VERSION" +tar -xzf "$DIST/sdl-mgr_$VERSION.tgz" -C "$MGR" +mv "$MGR/sdl-mgr" "$MGR/sdl-mgr_$VERSION" +# rm "$MGR/current" +ln -sfn "$MGR/sdl-mgr_$VERSION" "$MGR/current" + +# ------------------------------------------------------------ +# Copy MGR/current/conf/efault-sdl-mgr-config.json to CONF/sdl-mgr-config.json +# ------------------------------------------------------------ + +if [ ! -e "$CONF/sdl-mgr-config.json" ]; then + echo "" + echo "Copying: $MGR/current/conf/default-sdl-mgr-config.json to $CONF/sdl-mgr-config.json" + cp "$MGR/current/conf/default-sdl-mgr-config.json" "$CONF/sdl-mgr-config.json" +fi + +# ------------------------------------------------------------ +# Extract NodeJS tarball from DIST to NODEJS +# Symlink to NODEJS/current +# ------------------------------------------------------------ + +# Check if NodeJS is already installed +NODE_BIN="$NODEJS/current/bin/node" +NPM_BIN="$NODEJS/current/bin/npm" +# echo "NodeJS binary: $NODE_BIN" + +if [ -e "$NODE_BIN" ]; then + NODE_BIN_VERSION="$("$NODE_BIN" --version)" + # echo "NodeJS bin version: $NODE_BIN_VERSION" + # echo "NodeJS version: v$NODE_VERSION" + case "$OS_PLATFORM" in + linux) EXT="tar.xz"; TAR_OPTS="-xJf" ;; + darwin) EXT="tar.gz"; TAR_OPTS="-xzf" ;; + win) EXT="zip" ;; + *) echo "ERROR: Unsupported OS platform: $OS_PLATFORM"; exit 1 ;; + esac + + if [ "$NODE_BIN_VERSION" != "v$NODE_VERSION" ]; then + echo "" + echo "Installing NodeJS: node-v${NODE_VERSION}-${OS_PLATFORM}-${OS_ARCH}" + + echo "Extracting: $DIST/node-v${NODE_VERSION}-${OS_PLATFORM}-${OS_ARCH}.$EXT" + if [ $OS_PLATFORM == "win32" ]; then + echo "ERROR: win32 host install not supported by this installer" + exit 1 + # unzip "$DIST/node-v${NODE_VERSION}-${OS_PLATFORM}-${OS_ARCH}.$EXT" -d "$NODEJS" + else + tar $TAR_OPTS "$DIST/node-v${NODE_VERSION}-${OS_PLATFORM}-${OS_ARCH}.$EXT" -C "$NODEJS" + fi + # rm "$NODEJS/current" + ln -sfn "$NODEJS/node-v${NODE_VERSION}-${OS_PLATFORM}-${OS_ARCH}" "$NODEJS/current" + else + echo "" + echo "NodeJS $OS_PLATFORM-$OS_ARCH v$NODE_VERSION already installed" + fi +else + echo "" + echo "Installing NodeJS $NODE_VERSION" + + echo "Extracting: $DIST/node-v${NODE_VERSION}-${OS_PLATFORM}-${OS_ARCH}.tar.xz" + tar -xJf "$DIST/node-v${NODE_VERSION}-${OS_PLATFORM}-${OS_ARCH}.tar.xz" -C "$NODEJS" + ln -sfn "$NODEJS/node-v${NODE_VERSION}-${OS_PLATFORM}-${OS_ARCH}" "$NODEJS/current" +fi + + +# ------------------------------------------------------------ +# Install sdl-mgr Node Dependencies +# ------------------------------------------------------------ +echo "" +echo "Installing NodeJS Dependencies" +cd "$MGR/current/app" +export PATH="$NODEJS/current/bin:$PATH" +$NPM_BIN install + + +# ------------------------------------------------------------ +# Copy Start / Stop Scripts +# ------------------------------------------------------------ +mkdir -p "$HOME/scripts/" +cp "$MGR/current/scripts/start-sdl-mgr.sh" "$HOME/scripts/" +cp "$MGR/current/scripts/stop-sdl-mgr.sh" "$HOME/scripts/" +cp "$MGR/current/scripts/status-sdl-mgr.sh" "$HOME/scripts/" +chmod u+x "$HOME/scripts/"* + + +# ------------------------------------------------------------ +# Install Systemd Unit Files +# Copy MGR/current/scripts/sdl-mgr.service to ~/.config/systemd/user +# ------------------------------------------------------------ +if use_systemd; then + echo "" + echo "Installing SystemD Unit Files" + + mkdir -p ~/.config/systemd/user + cp "$MGR/current/scripts/sdl-mgr.service" ~/.config/systemd/user/ + + "${HOME}/scripts/stop-sdl-mgr.sh" + sleep 1 + systemctl --user daemon-reload --no-pager +fi + + +# ------------------------------------------------------------ +# Start SDL Manager +# ------------------------------------------------------------ +"${HOME}/scripts/start-sdl-mgr.sh" +sleep 2 +"${HOME}/scripts/status-sdl-mgr.sh" + +if [ $? -ne 0 ]; then + echo "" + echo "ERROR: SDL Manager failed to start" + exit 1 +fi + + +# + + +echo "" +echo "SDL Manager deployed:" +echo " to: $HOME" + diff --git a/metadata.json b/metadata.json new file mode 100644 index 0000000..dcb20a0 --- /dev/null +++ b/metadata.json @@ -0,0 +1,41 @@ +{ + "METADATA": { + "name": "nwa", + "description": "Node Web App", + "abbr": "NWA", + "version": "0.1.0", + "version_date": "2026-02-08", + "maturity": "PROD", + "author": "John Haverlack", + "copyright": "2026 John Haverlack", + "license": "MIT", + "license_url": "https://opensource.org/license/MIT", + "homepage": "https://github.com/jehaverlack/sdl", + "git_repo": "https://github.com/jehaverlack/sdl.git" + }, + "META_MAPS": { + "package.json": { + "version": "version", + "version_date": "version_date", + "author": "author", + "copyright": "copyright", + "license": "license", + "license_url": "license_url", + "homepage": "homepage" + }, + "*.md": { + "**Version**": "version", + "**Date**": "version_date", + "**Author**": "author", + "**Copyright**": "copyright", + "**License**": "license", + "**Homepage**": "homepage" + } + }, + "MANIFEST": [ + "README.md", + "docs/DESIGN.md", + "sdl-mgr/app/package.json", + "sdl-wkr/app/package.json" + ] +} diff --git a/nwa/.gitignore b/nwa/.gitignore new file mode 100644 index 0000000..ca7a4f3 --- /dev/null +++ b/nwa/.gitignore @@ -0,0 +1,5 @@ +conf/secrets/* +app/node_modules/* +data/* +logs/* +**/.~lock*# \ No newline at end of file diff --git a/nwa/LICENSE.md b/nwa/LICENSE.md new file mode 100644 index 0000000..89c7b7b --- /dev/null +++ b/nwa/LICENSE.md @@ -0,0 +1,9 @@ +# LICENSE: MIT + +Copyright 2025 John Haverlack + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the β€œSoftware”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED β€œAS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/nwa/README.md b/nwa/README.md new file mode 100644 index 0000000..f2cc46e --- /dev/null +++ b/nwa/README.md @@ -0,0 +1,72 @@ +# node-web-app +Node.js Web Application + +# Purpose + +This web application template provides a modular template for building a node.js web application with an API and UI. + +## Directory Structure + +``` +node-web-app +β”œβ”€β”€ app +β”‚Β Β  └── modules +β”‚Β Β  β”œβ”€β”€ template +β”‚Β Β  └── web +β”œβ”€β”€ conf +β”‚Β Β  β”œβ”€β”€ modules +β”‚Β Β  └── secrets +β”œβ”€β”€ data +β”‚Β Β  └── sqlite3 +β”œβ”€β”€ docs +β”œβ”€β”€ html +β”‚Β Β  β”œβ”€β”€ conf +β”‚Β Β  β”œβ”€β”€ css +β”‚Β Β  β”œβ”€β”€ img +β”‚Β Β  β”œβ”€β”€ js +β”‚Β Β  └── md +└── logs +``` + + + +# Getting Started + +## Pre-requisites +- [Node.js](https://nodejs.org/en) 22.x + +### Optional for Development +- [DB Browser for SQLite](https://sqlitebrowser.org/] +- To view data in **data/sqlite/nwa.db** + +## Clone Repo + +``` +git clone +``` + +## Install Dependencies + +``` +cd node-web-app/app +``` + +``` +npm install +``` + +## Starting the application + +Ensure you are in the `node-web-app/app` directory + +``` +npm start +``` + +``` +$ node-web-app/app$ npm start + +> node-web-app@1.0.0 start +> node index.js + +web: INFO: WebUI running on 127.0.0.1:8080 diff --git a/nwa/app/index.js b/nwa/app/index.js new file mode 100644 index 0000000..a81d329 --- /dev/null +++ b/nwa/app/index.js @@ -0,0 +1,22 @@ +// Node Web Application (NWA) +// John Haverlack +// https://github.com/ +// LICENSE: MIT +// Copyright (c) 2025 John Haverlack + +import { load_config, log } from './modules/nwa-lib/index.js'; + +const config = load_config(); + +log('======================================================================================='); +log(config.package.name + ': STARTING: ' + config.package.description + ' v' + config.package.version); +// log(JSON.stringify(config, null, 2)); +// log(JSON.stringify(config.dirs, null, 2), false); + +for (let m in config.modules) { + // log(`Loading module: ${m}`); + await import(config.modules[m].main); +} + + + diff --git a/nwa/app/modules/nwa-lib/index.js b/nwa/app/modules/nwa-lib/index.js new file mode 100644 index 0000000..f7c1dfe --- /dev/null +++ b/nwa/app/modules/nwa-lib/index.js @@ -0,0 +1,614 @@ +// Node Web App (NWA) Library Module +import fs from 'fs'; +import path from 'path'; +import os from 'os'; +import yargs from 'yargs'; +import { hideBin } from 'yargs/helpers'; +import { fileURLToPath } from 'url'; +import crypto from 'crypto'; +import { execFileSync } from 'child_process'; +import { get } from 'http'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// console.log("DEBUG: __dirname: " + __dirname); + +// Process start time (set once per Node.js process) +const PROCESS_START_TS = Date.now(); + +const config = load_config(); + +function load_config() { + let config = {}; + + const argv = yargs(hideBin(process.argv)) + .usage('Usage: npm start -- ') + .option('configfile', { + alias: 'c', + description: 'Path to config file', + type: 'string' + }) + .help() + .alias('help', 'h') + .argv; + + // console.log("DEBUG: dirname: " + __dirname); + let home = path.join(__dirname, '..', '..', '..'); // HOME Default + let mgr_cfg_file = path.join(home, 'conf', 'nwa-config.json'); + + // console.log("DEBUG: HOME: " + home); + // console.log("DEBUG: MGR_CFG_FILE: " + mgr_cfg_file); + try { + fs.accessSync(mgr_cfg_file, fs.constants.R_OK); + } catch (err) { + console.error(err); + process.exit(1); + } + + // // Load from ENV + // if (process.env.HOME) { + // home = process.env.HOME; + // mgr_cfg_file = path.join(home, 'conf', '-mgr-config.json'); + // // console.log("DEBUG3: HOME: " + home); + // // console.log("DEBUG3: MGR_CFG_FILE: " + mgr_cfg_file); + // try { + // fs.accessSync(mgr_cfg_file, fs.constants.R_OK); + // } catch (err) { + // console.error(err); + // process.exit(1); + // } + // } + + // // Load from ARGV + // if (argv.configfile) { + // home = path.dirname(argv.configfile); + // mgr_cfg_file = path.join(home, 'conf', '-mgr-config.json'); + // // console.log("DEBUG4: HOME: " + home); + // // console.log("DEBUG4: MGR_CFG_FILE: " + mgr_cfg_file); + // try { + // fs.accessSync(mgr_cfg_file, fs.constants.R_OK); + // } catch (err) { + // console.error(err); + // process.exit(1); + // } + // } + + // console.log("DEBUG: HOME: " + home); + // console.log("DEBUG: MGR_CFG_FILE: " + mgr_cfg_file); + + + + // Load Config + try { + config = JSON.parse(fs.readFileSync(mgr_cfg_file, 'utf8')); + config.dirs.base = config.dirs.base + '/..' + } catch (err) { + console.error(err); + } + + // console.log("DEBUG: home: " + home); + + // Expand Data Directory + let search_replace = {} + search_replace['DIRNAME'] = __dirname + // search_replace['HOME'] = home + config.dirs['home'] = home + for (let d in config.dirs) { + search_replace[d.toUpperCase()] = d + } + + for (let d in config.dirs) { + for (let s in search_replace) { + switch (s) { + case 'DIRNAME': + config.dirs[d] = path.join(config.dirs[d].replace(s, __dirname)) + break + default: + config.dirs[d] = path.join(config.dirs[d].replace(s, config.dirs[search_replace[s]])) + break + } + } + } + + // Replace DIRNAMES for metadata objects + for (let m in config.metadata) { + for (let s in search_replace) { + switch (s) { + case 'DIRNAME': + config.metadata[m] = path.join(config.metadata[m].replace(s, __dirname)) + break + default: + config.metadata[m] = path.join(config.metadata[m].replace(s, config.dirs[search_replace[s]])) + break + } + } + } + + // Ensure all directories exist + for (let d in config.dirs) { + if (!fs.existsSync(config.dirs[d])) { + fs.mkdirSync(config.dirs[d], { recursive: true }); + } + } + // for (let k in config.dependencies[d]) { + // // if k matches case insensitive "dependencies" the remove elemend k. + // if (k.match(/[Dd]ependencies/)) { + // delete config.dependencies[d][k] + // } + + // if (k.match(/scripts/)) { + // delete config.dependencies[d][k] + // } + // } + + // Load Package Info + config.package = JSON.parse(fs.readFileSync(path.join(config.dirs.app, 'package.json'), 'utf8')); + config.dependencies = {} + + // Version + config.cluster.version = config.package.version + + for (let d in config.package.dependencies) { + config.dependencies[d] = JSON.parse(fs.readFileSync(path.join(config.dirs.app, 'node_modules', d, 'package.json'), 'utf8')); + } + + // ------------------------------- + // Host Metadata (static) + // ------------------------------- + config.host = { + hostname: os.hostname(), + ips: [], + + os: { + platform: null, + id: null, + name: null, + version: null, + pretty_name: null +, }, + + cpu: { + arch: os.arch(), // x64, arm64, etc. + model: null, + cores_logical: 0 + }, + + memory: { + total_gb: Math.round(os.totalmem() / (1024 ** 3)) + }, + + gpu: { + }, + + network: {} + + }; + + config.nwa = { + version: config.package.version + } + + config.identity = { + id: null, + created_at: null, + hostname: null + } + + // console.log('config: host:', JSON.stringify(config.host, null, 2)); + + config.host.gpu = detectGPUs(); + + // Get Host IP Addresses + const networkInterfaces = os.networkInterfaces(); + // console.log(JSON.stringify(networkInterfaces, null, 2)); + config.host.ips = []; + for (const interfaceName in networkInterfaces) { + config.host.network[interfaceName] = networkInterfaces[interfaceName]; + + const addresses = networkInterfaces[interfaceName]; + for (const address of addresses) { + if (address.family === 'IPv4' && !address.internal) { + config.host.ips.push(address.address); + } + } + } + + config.host.ips.push('127.0.0.1'); + + // ------------------------------- + // CPU Info + // ------------------------------- + const cpus = os.cpus(); + // console.log('cpus: ', JSON.stringify(cpus, null, 2)); + if (cpus.length > 0) { + config.host.cpu.model = cpus[0].model; + config.host.cpu.cores_logical = cpus.length; + } + + config.host.os.platform = os.platform(); + + // ------------------------------- + // OS Info from /etc/os-release (Linux) + // ------------------------------- + const osReleasePath = '/etc/os-release'; + + if (fs.existsSync(osReleasePath)) { + try { + const content = fs.readFileSync(osReleasePath, 'utf8'); + const lines = content.split('\n'); + + for (const line of lines) { + const match = line.match(/^([A-Z_]+)=(.*)$/); + if (!match) continue; + + const key = match[1]; + const value = match[2].replace(/^"/, '').replace(/"$/, ''); + + switch (key) { + case 'ID': + config.host.os.id = value; + break; + case 'NAME': + config.host.os.name = value; + break; + case 'VERSION_ID': + config.host.os.version = value; + break; + case 'PRETTY_NAME': + config.host.os.pretty_name = value; + break; + } + } + } catch (err) { + // Non-fatal: OS info optional + } + } + + // ------------------------------- + // Identity + // ------------------------------- + + try { + // console.log(JSON.stringify(config.dirs, null, 2)); + const idinfo = load_or_create_id(config.dirs.home); + config.identity.id = idinfo.id; + config.identity.created_at = idinfo.created_at; + config.identity.hostname = config.host.hostname; + } catch (err) { + console.error('Idnetity Error:', err); + process.exit(1); + } + + // Node.js Version + // config.nodejs = { + // version: process.version, + // } + config.nodejs = { + version: process.version, // v22.16.0 + v8: process.versions.v8, // V8 engine version + abi: process.versions.modules, // Node ABI + arch: process.arch, // x64, arm64 + platform: process.platform, // linux, darwin, win32 + exec_path: process.execPath, // /usr/bin/node + openssl: process.versions.openssl || null, + uv: process.versions.uv || null, + icu: process.versions.icu || null + }; + + // console.log('config: ', JSON.stringify(config.dirs, null, 2)); + // console.log('modconf: ', config.dirs.modconf); + + + // Load Module Config + for (let m in config.modules) { + try { + config.modules[m] = JSON.parse(fs.readFileSync(path.join(config.dirs.modconf, `${m}.json`), 'utf8')); + config.modules[m].dir = path.join(config.dirs.modules, m); + config.modules[m].package = JSON.parse(fs.readFileSync(path.join(config.modules[m].dir, 'package.json'), 'utf8')); + config.modules[m].main = path.join(config.modules[m].dir, config.modules[m].package.main); + + } catch (err) { + console.error(err); + } + + // for each item, expand dirs with search and replace + for (let i in config.modules[m]) { + // console.log("DEBUG: " + i + " " + String(config.modules[m][i])) + // If item is a path + if (String(config.modules[m][i]).match(/\//)) { + // console.log("DEBUG: PATH: " + i + " " + String(config.modules[m][i])) + for (let s in search_replace) { + switch (s) { + case 'DIRNAME': + // config.modules[m][i] = String(path.join(config.modules[m][i])).replace(s, __dirname) + config.modules[m][i] = path.join(config.modules[m][i]).replace(s, __dirname) + break + default: + // config.modules[m][i] = String(path.join(config.modules[m][i])).replace(s, config.dirs[search_replace[s]]) + config.modules[m][i] = path.join(config.modules[m][i]).replace(s, config.dirs[search_replace[s]]) + break + } + } + } else { + // console.log("DEBUG: NOT PATH: " + i + " " + String(config.modules[m][i])) + } + } + } + + return config; +} + + +// Logger Function +function log(msg, cnsl=false) { + const now = new Date(); + const ts_datetime = now.toISOString(); // "YYYY-MM-DDTHH:MM:SS.sssZ" + const ts_date = ts_datetime.split('T')[0]; // "YYYY-MM-DD" + + const logFile = path.join(config.dirs.logs, `${config.package.name}-${ts_date}.log`); + + try { + fs.appendFileSync(logFile, `${ts_datetime} : ${msg}\n`, 'utf8'); + if (cnsl) { console.log(msg); } + } catch (err) { + console.error(`Logger error: Could not write to ${logFile}`, err); + } + } + + function ipToInt(ip) { + return ip.split('.').reduce((acc, oct) => (acc << 8) + Number(oct), 0) >>> 0; +} + +function intToIp(int) { + return [ + (int >>> 24) & 255, + (int >>> 16) & 255, + (int >>> 8) & 255, + int & 255 + ].join('.'); +} + +function computeBroadcast(address, netmask) { + try { + const ipInt = ipToInt(address); + const maskInt = ipToInt(netmask); + const networkInt = ipInt & maskInt; + const broadcastInt = networkInt | (~maskInt >>> 0); + return intToIp(broadcastInt); + } catch { + return null; + } +} + +function load_or_create_id(home) { + // console.log('DEBUG: load_or_create_id() home: ' + home); + const id_dir = path.join(home, 'conf'); + const id_file = path.join(id_dir, 'nwa-id.json'); + + try { + if (fs.existsSync(id_file)) { + const data = JSON.parse(fs.readFileSync(id_file, 'utf8')); + if (data.id) { + return data; + } + } + } catch (err) { + console.error('Failed to read nwa-id.json:', err); + process.exit(1); + } + + // Create new identity + const id = { + id: crypto.randomUUID(), + created_at: new Date().toISOString(), + hostname: os.hostname() + }; + + try { + fs.mkdirSync(id_dir, { recursive: true }); + fs.writeFileSync(id_file, JSON.stringify(id, null, 2), { mode: 0o600 }); + } catch (err) { + console.error('Failed to write nwa-id.json:', err); + process.exit(1); + } + + return id; +} + + + +/** + * Detect GPUs using /sys/class/drm (authoritative) + lspci (labels) + * Non-root, Linux-only. + */ +export function detectGPUs() { + const drmPath = '/sys/class/drm'; + const gpus = []; + + if (!fs.existsSync(drmPath)) { + return []; + } + + // --- Helper functions --- + const read = p => { + try { return fs.readFileSync(p, 'utf8').trim(); } + catch { return null; } + }; + + const readlink = p => { + try { return fs.realpathSync(p); } + catch { return null; } + }; + + const exec = (cmd, args) => { + try { + return execFileSync(cmd, args, { encoding: 'utf8' }).trim(); + } catch { + return null; + } + }; + + const vendorMap = { + '0x10de': 'nvidia', + '0x8086': 'intel', + '0x1002': 'amd' + }; + + // --- Collect render nodes indexed by PCI bus --- + const renderNodes = {}; + for (const entry of fs.readdirSync(drmPath)) { + if (!entry.startsWith('renderD')) continue; + + const devPath = readlink(path.join(drmPath, entry, 'device')); + if (!devPath) continue; + + const pciBusId = path.basename(devPath); + renderNodes[pciBusId] = entry; + } + + // --- Enumerate card devices --- + for (const entry of fs.readdirSync(drmPath)) { + if (!/^card[0-9]+$/.test(entry)) continue; + + const cardPath = path.join(drmPath, entry); + const devicePath = readlink(path.join(cardPath, 'device')); + if (!devicePath) continue; + + const pciBusId = path.basename(devicePath); + + const vendorHex = read(path.join(cardPath, 'device', 'vendor')); + const vendor = vendorMap[vendorHex] || 'unknown'; + + const driver = (() => { + const d = readlink(path.join(cardPath, 'device', 'driver')); + return d ? path.basename(d) : null; + })(); + + // Human-readable model (best-effort) + const lspciOut = exec('lspci', ['-s', pciBusId]); + // const model = lspciOut + // ? lspciOut.replace(/^.*?:\s*/, '') + // : null; + const model = (() => { + if (!lspciOut) return null; + + // Remove leading PCI address (e.g. "04:00.0 ") + let s = lspciOut.replace(/^[0-9a-fA-F:.]+\s+/, ''); + + // Remove device class prefix + s = s.replace(/^(VGA compatible controller|3D controller):\s*/i, ''); + + // Remove revision suffix + s = s.replace(/\s*\(rev.*\)$/i, ''); + + return s.trim(); + })(); + + const renderNode = renderNodes[pciBusId] || null; + const computeCapable = Boolean(renderNode); + + const gpu = { + id: entry, + pci_bus_id: pciBusId, + vendor, + driver, + model, + compute_capable: computeCapable, + render_node: renderNode, + memory: { + type: vendor === 'nvidia' ? 'dedicated' : 'shared', + total_mb: null + } + }; + + // --- NVIDIA enrichment (non-root, correct fields) --- + if (vendor === 'nvidia') { + const nvidiaSmi = findNvidiaSmi(); + + if (nvidiaSmi) { + const smi = exec(nvidiaSmi, [ + '--query-gpu=pci.bus_id,memory.total', + '--format=csv,noheader,nounits' + ]); + + if (smi) { + const target = pciShortId(pciBusId); + + for (const line of smi.split('\n')) { + const cols = line.split(',').map(s => s.trim()); + if (cols.length < 2) continue; + + const [bus, memMb] = cols; + + if (pciShortId(bus) === target) { + gpu.memory.total_mb = Number(memMb); + break; + } + } + } + } + } + + gpus.push(gpu); + } + + return gpus; +} + +// Normalize PCI bus IDs so "0000:04:00.0" === "00000000:04:00.0" +function normalizePciBusId(id) { + return id + .toLowerCase() + .replace(/^0+/, '') // strip leading domain zeros + .replace(/^:/, ''); // safety +} + +function findNvidiaSmi() { + const candidates = [ + '/usr/bin/nvidia-smi', + '/bin/nvidia-smi', + '/usr/local/bin/nvidia-smi' + ]; + + for (const p of candidates) { + try { + fs.accessSync(p, fs.constants.X_OK); + return p; + } catch {} + } + + return null; +} + +function pciShortId(id) { + if (!id) return null; + + // Extract the stable "bus:device.function" portion + const m = id.match(/([0-9a-fA-F]{2}:[0-9a-fA-F]{2}\.[0-9])/); + return m ? m[1].toLowerCase() : null; +} + +function getProcessStartTs() { + return PROCESS_START_TS; +} + +function getUptimeMs() { + return Date.now() - PROCESS_START_TS; +} + +function getUptimeSec() { + return Math.floor((Date.now() - PROCESS_START_TS) / 1000); +} + +function getUptimeDHMS() { + const uptimeSec = getUptimeSec(); + const d = Math.floor(uptimeSec / 86400); + const h = Math.floor((uptimeSec % 86400) / 3600); + const m = Math.floor((uptimeSec % 3600) / 60); + const s = Math.floor(uptimeSec % 60); + return `${d}d ${h}h ${m}m ${s}s`; +} + + +export { load_config, log, ipToInt, intToIp, computeBroadcast, getProcessStartTs, getUptimeMs, getUptimeSec, getUptimeDHMS }; diff --git a/nwa/app/modules/nwa-lib/package.json b/nwa/app/modules/nwa-lib/package.json new file mode 100644 index 0000000..ec75c8d --- /dev/null +++ b/nwa/app/modules/nwa-lib/package.json @@ -0,0 +1,4 @@ +{ + "main": "index.js", + "type": "module" +} \ No newline at end of file diff --git a/nwa/app/modules/template/index.js b/nwa/app/modules/template/index.js new file mode 100644 index 0000000..1abaae8 --- /dev/null +++ b/nwa/app/modules/template/index.js @@ -0,0 +1,5 @@ +const module = 'TEMPLATE'; // Module Name +import { load_config, log } from '../nwa-lib/index.js'; +const config = load_config(); + +log(`Loaded module: ${module}`); \ No newline at end of file diff --git a/nwa/app/modules/template/package.json b/nwa/app/modules/template/package.json new file mode 100644 index 0000000..ec75c8d --- /dev/null +++ b/nwa/app/modules/template/package.json @@ -0,0 +1,4 @@ +{ + "main": "index.js", + "type": "module" +} \ No newline at end of file diff --git a/nwa/app/modules/web/index.js b/nwa/app/modules/web/index.js new file mode 100644 index 0000000..9e6fd95 --- /dev/null +++ b/nwa/app/modules/web/index.js @@ -0,0 +1,197 @@ +import serveIndex from 'serve-index'; +import open from 'open'; +const module = 'web'; // Module Name +import { load_config, log } from '../nwa-lib/index.js'; +const config = load_config(); + +log(`${module}: Loaded module: ${module}`); + +import fs from 'fs'; +import path from 'path'; +import { parse } from 'csv-parse'; +import express from 'express'; +const app = express(); + + +// Serve config as JSON +app.get('/api/config', (req, res) => { + res.json(config); + + let webreqmeta = { + method: req.method, + url: req.originalUrl, + ip: req.ip, + userAgent: req.get('User-Agent') + }; + + log(module + ': WEBAPI: ' + JSON.stringify(webreqmeta)) +}); + +// Serve sidepanel nav as JSON +app.get('/api/nav/sidepanel', (req, res) => { + const sideMenuPath = path.join( + config.dirs.html, + 'conf', + 'nav-sidepanel.json' + ); + + try { + const sideMenuData = JSON.parse( + fs.readFileSync(sideMenuPath, 'utf8') + ); + res.json(sideMenuData); + + log(module + ': INFO: GET /api/nav/sidepanel'); + } catch (err) { + log(`ERROR: GET /api/nav/sidepanel failed: ${err}`); + res.status(500).json({ error: 'Failed to load nav-sidepanel.json' }); + } +}); + + +// Serve nav as JSON +app.get('/api/nav', (req, res) => { + const menuPath = (path.join(config.dirs.html, 'conf', 'nav.json')); + + try { + const menuData = JSON.parse(fs.readFileSync(menuPath, 'utf8')); + res.json(menuData); + + log(module + ': INFO: GET /api/nav'); + } catch (err) { + log(`ERROR: GET /api/nav failed: ${err}`); + res.status(500).json({ error: 'Failed to load nav.json' }); + } +}); + +// API +app.get ('/api', (req, res) => { + res.json(gen_api_usage(req)); + + let webreqmeta = { + method: req.method, + url: req.originalUrl, + ip: req.ip, + userAgent: req.get('User-Agent') + }; + + log(module + ': WEBAPI: ' + JSON.stringify(webreqmeta)) +}); + + + +// /img +app.use('/img', logStaticRequests('img'), express.static(path.join(config.dirs.html, 'img'))); + +// /css +app.use('/css', logStaticRequests('css'), express.static(path.join(config.dirs.html, 'css'))); + +// /js +app.use('/js', logStaticRequests('js'), express.static(path.join(config.dirs.html, 'js'))); + + +// md +app.use('/md', logStaticRequests('md'), express.static(path.join(config.dirs.html, 'md'))); + + +// Web Libs +for (let wl in config.modules.web.weblibs) { + log(`${module}: Serving WebLib: /${wl} at ${path.join(config.dirs.app, 'node_modules', config.modules.web.weblibs[wl])}`) + app.use('/' + wl, logStaticRequests(wl), express.static(path.join(config.dirs.app, 'node_modules', config.modules.web.weblibs[wl]))); +} + + +// Top Level Website +app.use('/', logStaticRequests('html'), express.static(path.join(config.dirs.html))); + +// Catch-all for 404 Not Found +app.use((req, res, next) => { + const webreqmeta = { + method: req.method, + url: req.originalUrl, + ip: req.ip, + userAgent: req.get('User-Agent') + }; + + log(`404 NOT FOUND: ${JSON.stringify(webreqmeta)}`); + + if (req.originalUrl.startsWith('/api/')) { + res.status(404).json({ error: 'Not Found', path: req.originalUrl }); + } else { + res.status(404).sendFile(path.join(config.dirs.html, '404.html')); + } + }); + + // Starting Web Server +const bindAddrs = + Array.isArray(config.modules[module].bind_ip_addrs) && + config.modules[module].bind_ip_addrs.length > 0 + ? config.modules[module].bind_ip_addrs + : ['0.0.0.0']; + +for (const ip_addr of bindAddrs) { + if (ip_addr === '0.0.0.0') { + const server = app.listen(config.modules[module].port, '0.0.0.0', () => { + for (const ip of config.host.ips) { + log(`INFO: WebUI running at http://${ip}:${config.modules[module].port}`, true); + + if (config.modules[module].open_browser && ip === '127.0.0.1') { + open(`http://127.0.0.1:${config.modules[module].port}`); + } + } + }); + + server.on('error', (err) => { + log(`ERROR: WebUI failed to start on 0.0.0.0:${config.modules[module].port}: ${err}`, true); + }); + } else { + const server = app.listen(config.modules[module].port, ip_addr, () => { + log(`INFO: WebUI running at http://${ip_addr}:${config.modules[module].port}`, true); + if (config.modules[module].open_browser) { + open(`http://${ip_addr}:${config.modules[module].port}`); + } + }); + + server.on('error', (err) => { + log(`ERROR: WebUI failed to start on ${ip_addr}:${config.modules[module].port}: ${err}`, true); + }); + } +} + + + + + +// ----------- Libraries ------------- + function logStaticRequests(subdir) { + const func = 'logStaticRequests'; + return (req, res, next) => { + const webreqmeta = { + method: req.method, + url: req.originalUrl, + ip: req.ip, + userAgent: req.get('User-Agent'), + type: subdir + }; + log(`${module}: ${func}: Web Req: ${JSON.stringify(webreqmeta)}`); + next(); + }; + } + + + // API Usage +function gen_api_usage(req) { + // let base_url = "http://localhost:" + config.web.port; + const base_url = `${req.protocol}://${req.headers.host}`; + let usage = {}; + + usage['API'] = {} + usage['API']['DESC'] = "API" + usage['API']['URL'] = base_url + '/api'; + + usage['API']['CONFIG'] = {} + usage['API']['CONFIG']['DESC'] = "API Config JSON" + usage['API']['CONFIG']['URL'] = base_url + '/api/config'; + + return usage; +} diff --git a/nwa/app/modules/web/package.json b/nwa/app/modules/web/package.json new file mode 100644 index 0000000..ec75c8d --- /dev/null +++ b/nwa/app/modules/web/package.json @@ -0,0 +1,4 @@ +{ + "main": "index.js", + "type": "module" +} \ No newline at end of file diff --git a/nwa/app/package-lock.json b/nwa/app/package-lock.json new file mode 100644 index 0000000..d9bd557 --- /dev/null +++ b/nwa/app/package-lock.json @@ -0,0 +1,3084 @@ +{ + "name": "sdl-mgr", + "version": "0.3.10", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "sdl-mgr", + "version": "0.3.10", + "license": "MIT", + "dependencies": { + "@alenaksu/json-viewer": "^2.0.1", + "@fortawesome/fontawesome-free": "^6.7.2", + "aedes": "^0.51.3", + "bootstrap": "^5.3.7", + "bootstrap-icons": "^1.13.1", + "chart.js": "^4.5.1", + "csv-parse": "^5.6.0", + "d3": "^7.9.0", + "express": "^5.1.0", + "graph.js": "^1.21.1", + "highlight.js": "^11.9.0", + "html-to-image": "^1.11.13", + "marked": "^15.0.11", + "mathjax-full": "^3.2.1", + "mqtt": "^5.14.1", + "node-fetch": "^3.3.2", + "open": "^11.0.0", + "serve-index": "^1.9.1", + "three": "^0.181.2", + "websocket-stream": "^5.5.2", + "ws": "^8.18.3", + "yargs": "^18.0.0" + } + }, + "node_modules/@alenaksu/json-viewer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@alenaksu/json-viewer/-/json-viewer-2.1.2.tgz", + "integrity": "sha512-hyR5EUc0GZMTjlSULJZlMzEYB896ICbCf1+5YLQRSAczGRWy42UWac5PcXM4spuyTkrEHHlOZBTVEq34f9+JVQ==", + "license": "MIT", + "dependencies": { + "lit": "^3.2.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@fortawesome/fontawesome-free": { + "version": "6.7.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-6.7.2.tgz", + "integrity": "sha512-JUOtgFW6k9u4Y+xeIaEiLr3+cjoUPiAuLXoyKOJSia6Duzb7pq+A76P9ZdPDoAoxHdHzq6gE9/jKBGXlZT8FbA==", + "license": "(CC-BY-4.0 AND OFL-1.1 AND MIT)", + "engines": { + "node": ">=6" + } + }, + "node_modules/@kurkle/color": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", + "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", + "license": "MIT" + }, + "node_modules/@lit-labs/ssr-dom-shim": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.6.0.tgz", + "integrity": "sha512-VHb0ALPMTlgKjM6yIxxoQNnpKyUKLD04VzeQdsiXkMqkvYlAHxq9glGLmgbb889/1GsohSOAjvQYoiBppXFqrQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@lit/reactive-element": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.1.2.tgz", + "integrity": "sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.5.0" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@types/node": { + "version": "26.0.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.1.tgz", + "integrity": "sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==", + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "node_modules/@types/readable-stream": { + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.23.tgz", + "integrity": "sha512-wwXrtQvbMHxCbBgjHaMGEmImFTQxxpfMOR/ZoQnXxB1woqkUbdLGFDgauo00Py9IudiaqSeiBiulSV9i6XIPig==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT" + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.9.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.10.tgz", + "integrity": "sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==", + "license": "MIT", + "engines": { + "node": ">=14.6" + } + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/aedes": { + "version": "0.51.3", + "resolved": "https://registry.npmjs.org/aedes/-/aedes-0.51.3.tgz", + "integrity": "sha512-aQfiI9w3RbqnowNCdcGMmCtxBFXN9bhJFcuZm24U5/NU06V3MCl42jWK2GUnu8rOypR2Ahi/aEcgq3w7CMcycg==", + "license": "MIT", + "dependencies": { + "aedes-packet": "^3.0.0", + "aedes-persistence": "^9.1.2", + "end-of-stream": "^1.4.4", + "fastfall": "^1.5.1", + "fastparallel": "^2.4.1", + "fastseries": "^2.0.0", + "hyperid": "^3.2.0", + "mqemitter": "^6.0.0", + "mqtt-packet": "^9.0.0", + "retimer": "^4.0.0", + "reusify": "^1.0.4", + "uuid": "^10.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/aedes" + } + }, + "node_modules/aedes-packet": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aedes-packet/-/aedes-packet-3.0.0.tgz", + "integrity": "sha512-swASey0BxGs4/npZGWoiVDmnEyPvVFIRY6l2LVKL4rbiW8IhcIGDLfnb20Qo8U20itXlitAKPQ3MVTEbOGG5ZA==", + "license": "MIT", + "dependencies": { + "mqtt-packet": "^7.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/aedes-packet/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/aedes-packet/node_modules/mqtt-packet": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-7.1.2.tgz", + "integrity": "sha512-FFZbcZ2omsf4c5TxEQfcX9hI+JzDpDKPT46OmeIBpVA7+t32ey25UNqlqNXTmeZOr5BLsSIERpQQLsFWJS94SQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.2", + "debug": "^4.1.1", + "process-nextick-args": "^2.0.1" + } + }, + "node_modules/aedes-packet/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/aedes-persistence": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/aedes-persistence/-/aedes-persistence-9.1.2.tgz", + "integrity": "sha512-2Wlr5pwIK0eQOkiTwb8ZF6C20s8UPUlnsJ4kXYePZ3JlQl0NbBA176mzM8wY294BJ5wybpNc9P5XEQxqadRNcQ==", + "license": "MIT", + "dependencies": { + "aedes-packet": "^3.0.0", + "qlobber": "^7.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "license": "MIT" + }, + "node_modules/bl": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/bl/-/bl-6.1.6.tgz", + "integrity": "sha512-jLsPgN/YSvPUg9UX0Kd73CXpm2Psg9FxMeCSXnk3WBO3CMT10JMwijubhGfHCnFu6TPn1ei3b975dxv7K2pWVg==", + "license": "MIT", + "dependencies": { + "@types/readable-stream": "^4.0.0", + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^4.2.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/body-parser": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.3.0.tgz", + "integrity": "sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^2.0.0", + "debug": "^4.4.3", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.2", + "on-finished": "^2.4.1", + "qs": "^6.15.2", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/bootstrap": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz", + "integrity": "sha512-HP1SZDqaLDPwsNiqRqi5NcP0SSXciX2s9E+RyqJIIqGo+vJeN5AJVM98CXmW/Wux0nQ5L7jeWUdplCEf0Ee+tg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "license": "MIT", + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, + "node_modules/bootstrap-icons": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.13.1.tgz", + "integrity": "sha512-ijombt4v6bv5CLeXvRWKy7CuM3TRTuPEuGaGKvTV5cz65rQSY8RQ2JcHt6b90cBBAC7s8fsf2EkQDldzCoXUjw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "license": "MIT" + }, + "node_modules/broker-factory": { + "version": "3.1.15", + "resolved": "https://registry.npmjs.org/broker-factory/-/broker-factory-3.1.15.tgz", + "integrity": "sha512-ko+aWvgNuP49meGrdjUu7rC+Y+Wai3cCPxP3xWwHsHfehFjOh5ZQM2yC4gEB2UddeZ/YXhm0K1eG/L6fxym2Og==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.7", + "fast-unique-numbers": "^9.0.27", + "tslib": "^2.8.1", + "worker-factory": "^7.0.50" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT" + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chart.js": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz", + "integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==", + "license": "MIT", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, + "node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/commist": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/commist/-/commist-3.2.0.tgz", + "integrity": "sha512-4PIMoPniho+LqXmpS5d3NuGYncG6XWlkBSVGiWycL22dd42OYdUGil2CWuzklaJoNxyxUSpO4MKIBU94viWNAw==", + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "license": "MIT" + }, + "node_modules/csv-parse": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-5.6.0.tgz", + "integrity": "sha512-l3nz3euub2QMg5ouu5U09Ew9Wf6/wQ8I++ch1loQ0ljmzhmfZYrH9fflS22i/PQEvsPvxCwxgz5q7UB8K1JO4Q==", + "license": "MIT" + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "license": "ISC", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "license": "ISC", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "license": "ISC", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "license": "ISC", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/default-browser": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", + "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delaunator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz", + "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==", + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/duplexify": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", + "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" + } + }, + "node_modules/duplexify/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/duplexify/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/duplexify/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/esm": { + "version": "3.2.25", + "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fast-unique-numbers": { + "version": "9.0.27", + "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-9.0.27.tgz", + "integrity": "sha512-nDA9ADeINN8SA2u2wCtU+siWFTTDqQR37XvgPIDDmboWQeExz7X0mImxuaN+kJddliIqy2FpVRmnvRZ+j8i1/A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18.2.0" + } + }, + "node_modules/fastfall": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/fastfall/-/fastfall-1.5.1.tgz", + "integrity": "sha512-KH6p+Z8AKPXnmA7+Iz2Lh8ARCMr+8WNPVludm1LGkZoD2MjY6LVnRMtTKhkdzI+jr0RzQWXKzKyBJm1zoHEL4Q==", + "license": "MIT", + "dependencies": { + "reusify": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fastparallel": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/fastparallel/-/fastparallel-2.4.1.tgz", + "integrity": "sha512-qUmhxPgNHmvRjZKBFUNI0oZuuH9OlSIOXmJ98lhKPxMZZ7zS/Fi0wRHOihDSz0R1YiIOjxzOY4bq65YTcdBi2Q==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4", + "xtend": "^4.0.2" + } + }, + "node_modules/fastseries": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fastseries/-/fastseries-2.0.0.tgz", + "integrity": "sha512-XBU9RXeoYc2/VnvMhplAxEmZLfIk7cvTBu+xwoBuTI8pL19E03cmca17QQycKIdxgwCeFA/a4u27gv1h3ya5LQ==", + "license": "ISC" + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graph.js": { + "version": "1.21.1", + "resolved": "https://registry.npmjs.org/graph.js/-/graph.js-1.21.1.tgz", + "integrity": "sha512-h37V3CoVDdsuzpKAzjyrcgnKBqjBAB6EnncJCx+d42o6XX9a+eOrvtBjW+LM7vxVr3dBtUcIIaSsKCTyAi1kew==", + "license": "MIT" + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", + "license": "MIT" + }, + "node_modules/highlight.js": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/html-to-image": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/html-to-image/-/html-to-image-1.11.13.tgz", + "integrity": "sha512-cuOPoI7WApyhBElTTb9oqsawRvZ0rHhaHwghRLlTuffoD1B2aDemlCruLeZrUIIdvG7gs9xeELEPm6PhuASqrg==", + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/hyperid": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/hyperid/-/hyperid-3.3.0.tgz", + "integrity": "sha512-7qhCVT4MJIoEsNcbhglhdmBKb09QtcmJNiIQGq7js/Khf5FtQQ9bzcAuloeqBeee7XD7JqDeve9KNlQya5tSGQ==", + "license": "MIT", + "dependencies": { + "buffer": "^5.2.1", + "uuid": "^8.3.2", + "uuid-parse": "^1.1.0" + } + }, + "node_modules/hyperid/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/ip-address": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", + "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-in-ssh": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz", + "integrity": "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "license": "MIT" + }, + "node_modules/js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/lit": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.3.3.tgz", + "integrity": "sha512-fycuvZg/hkpozL00lm1pEJH5nN/lr9ZXd6mJI2HSN4+Bzc+LDNdEApJ6HFbPkdFNHLvOplIIuJvxkS4XUxqirw==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit/reactive-element": "^2.1.0", + "lit-element": "^4.2.0", + "lit-html": "^3.3.0" + } + }, + "node_modules/lit-element": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.2.2.tgz", + "integrity": "sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==", + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.5.0", + "@lit/reactive-element": "^2.1.0", + "lit-html": "^3.3.0" + } + }, + "node_modules/lit-html": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.3.3.tgz", + "integrity": "sha512-el8M6jK2o3RXBnrSHX3ZKrsN8zEV63pSExTO1wYJz7QndGYZ8353e2a5PPX+qHe2aGayfnchQmkAojaWAREOIA==", + "license": "BSD-3-Clause", + "dependencies": { + "@types/trusted-types": "^2.0.2" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "license": "ISC" + }, + "node_modules/marked": { + "version": "15.0.12", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", + "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mathjax-full": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/mathjax-full/-/mathjax-full-3.2.1.tgz", + "integrity": "sha512-aUz9o16MGZdeiIBwZjAfUBTiJb7LRqzZEl1YOZ8zQMGYIyh1/nxRebxKxjDe9L+xcZCr2OHdzoFBMcd6VnLv9Q==", + "license": "Apache-2.0", + "dependencies": { + "esm": "^3.2.25", + "mhchemparser": "^4.1.0", + "mj-context-menu": "^0.6.1", + "speech-rule-engine": "^4.0.6" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mhchemparser": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/mhchemparser/-/mhchemparser-4.2.1.tgz", + "integrity": "sha512-kYmyrCirqJf3zZ9t/0wGgRZ4/ZJw//VwaRVGA75C4nhE60vtnIzhl9J9ndkX/h6hxSN7pjg/cE0VxbnNM+bnDQ==", + "license": "Apache-2.0" + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mj-context-menu": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/mj-context-menu/-/mj-context-menu-0.6.1.tgz", + "integrity": "sha512-7NO5s6n10TIV96d4g2uDpG7ZDpIhMh0QNfGdJw/W47JswFcosz457wqz/b5sAKvl12sxINGFCn80NZHKwxQEXA==", + "license": "Apache-2.0" + }, + "node_modules/mqemitter": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/mqemitter/-/mqemitter-6.0.2.tgz", + "integrity": "sha512-8RGlznQx/Nb1xC3xKUFXHWov7pn7JdH++YVwlr6SLT6k3ft1h+ImGqZdVudbdKruFckIq9wheq9s4hgCivJDow==", + "license": "ISC", + "dependencies": { + "fastparallel": "^2.4.1", + "qlobber": "^8.0.1" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/mqemitter/node_modules/qlobber": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/qlobber/-/qlobber-8.0.1.tgz", + "integrity": "sha512-O+Wd1chXj5YE1DwmD+ae0bXiSLehmnS3czlC1R9FL/Nt/3q8uMS1bIHmg2lJfCoiimCxClWM8AAuJrF0EvNiog==", + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/mqtt": { + "version": "5.15.1", + "resolved": "https://registry.npmjs.org/mqtt/-/mqtt-5.15.1.tgz", + "integrity": "sha512-V1WnkGuJh3ec9QXzy5Iylw8OOBK+Xu1WhxcQ9mMpLThG+/JZIMV1PgLNRgIiqXhZnvnVLsuyxHl5A/3bHHbcAA==", + "license": "MIT", + "dependencies": { + "@types/readable-stream": "^4.0.21", + "@types/ws": "^8.18.1", + "commist": "^3.2.0", + "concat-stream": "^2.0.0", + "debug": "^4.4.1", + "help-me": "^5.0.0", + "lru-cache": "^10.4.3", + "minimist": "^1.2.8", + "mqtt-packet": "^9.0.2", + "number-allocator": "^1.0.14", + "readable-stream": "^4.7.0", + "rfdc": "^1.4.1", + "socks": "^2.8.6", + "split2": "^4.2.0", + "worker-timers": "^8.0.23", + "ws": "^8.18.3" + }, + "bin": { + "mqtt": "build/bin/mqtt.js", + "mqtt_pub": "build/bin/pub.js", + "mqtt_sub": "build/bin/sub.js" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/mqtt-packet": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/mqtt-packet/-/mqtt-packet-9.0.2.tgz", + "integrity": "sha512-MvIY0B8/qjq7bKxdN1eD+nrljoeaai+qjLJgfRn3TiMuz0pamsIWY2bFODPZMSNmabsLANXsLl4EMoWvlaTZWA==", + "license": "MIT", + "dependencies": { + "bl": "^6.0.8", + "debug": "^4.3.4", + "process-nextick-args": "^2.0.1" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/number-allocator": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.14.tgz", + "integrity": "sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.1", + "js-sdsl": "4.3.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/open": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/open/-/open-11.0.0.tgz", + "integrity": "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==", + "license": "MIT", + "dependencies": { + "default-browser": "^5.4.0", + "define-lazy-prop": "^3.0.0", + "is-in-ssh": "^1.0.0", + "is-inside-container": "^1.0.0", + "powershell-utils": "^0.1.0", + "wsl-utils": "^0.3.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/powershell-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz", + "integrity": "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qlobber": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/qlobber/-/qlobber-7.0.1.tgz", + "integrity": "sha512-FsFg9lMuMEFNKmTO9nV7tlyPhx8BmskPPjH2akWycuYVTtWaVwhW5yCHLJQ6Q+3mvw5cFX2vMfW2l9z2SiYAbg==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/qs": { + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/readable-stream/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/retimer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/retimer/-/retimer-4.0.0.tgz", + "integrity": "sha512-fZIVtvbOsQsxNSDhpdPOX4lx5Ss2ni+S72AUBitARpFhtA3UzrAjQ6gDtypB2/+l7L+1VQgAgpvAKY66mElH0w==", + "license": "MIT", + "dependencies": { + "worker-timers": "^7.0.75" + } + }, + "node_modules/retimer/node_modules/fast-unique-numbers": { + "version": "8.0.13", + "resolved": "https://registry.npmjs.org/fast-unique-numbers/-/fast-unique-numbers-8.0.13.tgz", + "integrity": "sha512-7OnTFAVPefgw2eBJ1xj2PGGR9FwYzSUso9decayHgCDX4sJkHLdcsYTytTg+tYv+wKF3U8gJuSBz2jJpQV4u/g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.8", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.1.0" + } + }, + "node_modules/retimer/node_modules/worker-timers": { + "version": "7.1.8", + "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-7.1.8.tgz", + "integrity": "sha512-R54psRKYVLuzff7c1OTFcq/4Hue5Vlz4bFtNEIarpSiCYhpifHU3aIQI29S84o1j87ePCYqbmEJPqwBTf+3sfw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2", + "worker-timers-broker": "^6.1.8", + "worker-timers-worker": "^7.0.71" + } + }, + "node_modules/retimer/node_modules/worker-timers-broker": { + "version": "6.1.8", + "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-6.1.8.tgz", + "integrity": "sha512-FUCJu9jlK3A8WqLTKXM9E6kAmI/dR1vAJ8dHYLMisLNB/n3GuaFIjJ7pn16ZcD1zCOf7P6H62lWIEBi+yz/zQQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.5", + "fast-unique-numbers": "^8.0.13", + "tslib": "^2.6.2", + "worker-timers-worker": "^7.0.71" + } + }, + "node_modules/retimer/node_modules/worker-timers-worker": { + "version": "7.0.71", + "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-7.0.71.tgz", + "integrity": "sha512-ks/5YKwZsto1c2vmljroppOKCivB/ma97g9y77MAAz2TBBjPPgpoOiS1qYQKIgvGTr2QYPT3XhJWIB6Rj2MVPQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.5", + "tslib": "^2.6.2" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/robust-predicates": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz", + "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==", + "license": "Unlicense" + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-index": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.2.tgz", + "integrity": "sha512-KDj11HScOaLmrPxl70KYNW1PksP4Nb/CLL2yvC+Qd2kHMPEEpfc4Re2e4FOay+bC/+XQl/7zAcWON3JVo5v3KQ==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "batch": "0.6.1", + "debug": "2.6.9", + "escape-html": "~1.0.3", + "http-errors": "~1.8.0", + "mime-types": "~2.1.35", + "parseurl": "~1.3.3" + }, + "engines": { + "node": ">= 0.8.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-index/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/serve-index/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "license": "MIT", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/serve-index/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz", + "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.1.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/speech-rule-engine": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/speech-rule-engine/-/speech-rule-engine-4.1.4.tgz", + "integrity": "sha512-i/VCLG1fvRc95pMHRqG4aQNscv+9aIsqA2oI7ZQS51sTdUcDHYX6cpT8/tqZ+enjs1tKVwbRBWgxut9SWn+f9g==", + "license": "Apache-2.0", + "dependencies": { + "@xmldom/xmldom": "0.9.10", + "commander": "13.1.0", + "wicked-good-xpath": "1.3.0" + }, + "bin": { + "sre": "bin/sre" + } + }, + "node_modules/speech-rule-engine/node_modules/commander": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", + "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/stream-shift": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", + "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", + "license": "MIT" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/three": { + "version": "0.181.2", + "resolved": "https://registry.npmjs.org/three/-/three-0.181.2.tgz", + "integrity": "sha512-k/CjiZ80bYss6Qs7/ex1TBlPD11whT9oKfT8oTGiHa34W4JRd1NiH/Tr1DbHWQ2/vMUypxksLnF2CfmlmM5XFQ==", + "license": "MIT" + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "license": "MIT", + "dependencies": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/uuid-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/uuid-parse/-/uuid-parse-1.1.0.tgz", + "integrity": "sha512-OdmXxA8rDsQ7YpNVbKSJkNzTw2I+S5WsbMDnCtIWSQaosNAcWtFuI/YK1TjzUI6nbkgiqEyh8gWngfcv8Asd9A==", + "license": "MIT" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/websocket-stream": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/websocket-stream/-/websocket-stream-5.5.2.tgz", + "integrity": "sha512-8z49MKIHbGk3C4HtuHWDtYX8mYej1wWabjthC/RupM9ngeukU4IWoM46dgth1UOS/T4/IqgEdCDJuMe2039OQQ==", + "license": "BSD-2-Clause", + "dependencies": { + "duplexify": "^3.5.1", + "inherits": "^2.0.1", + "readable-stream": "^2.3.3", + "safe-buffer": "^5.1.2", + "ws": "^3.2.0", + "xtend": "^4.0.0" + } + }, + "node_modules/websocket-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/websocket-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/websocket-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/websocket-stream/node_modules/ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "license": "MIT", + "dependencies": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + } + }, + "node_modules/wicked-good-xpath": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/wicked-good-xpath/-/wicked-good-xpath-1.3.0.tgz", + "integrity": "sha512-Gd9+TUn5nXdwj/hFsPVx5cuHHiF5Bwuc30jZ4+ronF1qHK5O7HD0sgmXWSEgwKquT3ClLoKPVbO6qGwVwLzvAw==", + "license": "MIT" + }, + "node_modules/worker-factory": { + "version": "7.0.50", + "resolved": "https://registry.npmjs.org/worker-factory/-/worker-factory-7.0.50.tgz", + "integrity": "sha512-hhwc0G+sFwM4qBuhJIUBn2p1Jf8v/FwmLUANBf/Q+Lt2uI8mfIZQhXaZQACodQD4R7Zp6cn/6702bIvNn2puJQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.7", + "fast-unique-numbers": "^9.0.27", + "tslib": "^2.8.1" + } + }, + "node_modules/worker-timers": { + "version": "8.0.33", + "resolved": "https://registry.npmjs.org/worker-timers/-/worker-timers-8.0.33.tgz", + "integrity": "sha512-RQVlKkek80v8M6SHvdMKiywaXFmX3XTpYTXTw0r62PdXB06t74XNxcTuG8wsQwnjorAQymNQyyQ0rzv7PykEMQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.7", + "tslib": "^2.8.1", + "worker-timers-broker": "^8.0.18", + "worker-timers-worker": "^9.0.15" + } + }, + "node_modules/worker-timers-broker": { + "version": "8.0.18", + "resolved": "https://registry.npmjs.org/worker-timers-broker/-/worker-timers-broker-8.0.18.tgz", + "integrity": "sha512-FrjzDVX1wKfZN0gRbCFqv8VHuTncG4sbI/WGEg4tSSQeIsnwqg4YBYWMAHYLJtUDEYYmiK65UKFrVMVk2irDSg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.7", + "broker-factory": "^3.1.15", + "fast-unique-numbers": "^9.0.27", + "tslib": "^2.8.1", + "worker-timers-worker": "^9.0.15" + } + }, + "node_modules/worker-timers-worker": { + "version": "9.0.15", + "resolved": "https://registry.npmjs.org/worker-timers-worker/-/worker-timers-worker-9.0.15.tgz", + "integrity": "sha512-KKUe7lZ/Aignr51H6hOUik8LwTnIgojH/1lwhli8A8qIEIyewogZTpNpMW5B6BF7nmwBOkUoTYgf1H3QShcjSA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.7", + "tslib": "^2.8.1", + "worker-factory": "^7.0.50" + } + }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/wsl-utils": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.3.1.tgz", + "integrity": "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==", + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0", + "powershell-utils": "^0.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", + "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^7.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + } + } +} diff --git a/nwa/app/package.json b/nwa/app/package.json new file mode 100644 index 0000000..35887b5 --- /dev/null +++ b/nwa/app/package.json @@ -0,0 +1,47 @@ +{ + "name": "nwa", + "version": "0.1.0", + "version_date": "2026-02-08", + "description": "Node Web App (NWA)", + "main": "index.js", + "type": "module", + "scripts": { + "start": "node index.js" + }, + "repository": { + "type": "git", + "url": "https://github.com/jehaverlack/nwa.git" + }, + "author": "John Haverlack", + "copyright": "2026 John Haverlack", + "license": "MIT", + "license_url": "https://opensource.org/license/MIT", + "bugs": { + "url": "" + }, + "homepage": "https://github.com/jehaverlack/nwa", + "dependencies": { + "@alenaksu/json-viewer": "^2.0.1", + "@fortawesome/fontawesome-free": "^6.7.2", + "aedes": "^0.51.3", + "bootstrap": "^5.3.7", + "bootstrap-icons": "^1.13.1", + "chart.js": "^4.5.1", + "csv-parse": "^5.6.0", + "d3": "^7.9.0", + "express": "^5.1.0", + "graph.js": "^1.21.1", + "highlight.js": "^11.9.0", + "html-to-image": "^1.11.13", + "marked": "^15.0.11", + "mathjax-full": "^3.2.1", + "mqtt": "^5.14.1", + "node-fetch": "^3.3.2", + "open": "^11.0.0", + "serve-index": "^1.9.1", + "three": "^0.181.2", + "websocket-stream": "^5.5.2", + "ws": "^8.18.3", + "yargs": "^18.0.0" + } +} diff --git a/nwa/conf/modules/template.json b/nwa/conf/modules/template.json new file mode 100644 index 0000000..60e7e09 --- /dev/null +++ b/nwa/conf/modules/template.json @@ -0,0 +1,3 @@ +{ + "enabled": true +} diff --git a/nwa/conf/modules/web.json b/nwa/conf/modules/web.json new file mode 100644 index 0000000..997fb5d --- /dev/null +++ b/nwa/conf/modules/web.json @@ -0,0 +1,21 @@ +{ + "enabled": true, + "bind_ip_addrs": ["0.0.0.0"], + "port": 8080, + "open_browser": false, + "weblibs": { + "bootstrap":"bootstrap/dist", + "bootstrap-icons":"bootstrap-icons", + "chartjs":"chart.js/dist", + "d3":"d3/dist", + "fontawesome":"@fortawesome/fontawesome-free", + "graphjs":"graph.js/dist", + "html-to-image":"html-to-image/dist", + "json-viewer":"@alenaksu/json-viewer/dist", + "marked":"marked", + "mathjax":"mathjax-full/es5", + "mqttjs":"mqtt/dist", + "three/examples/jsm":"three/examples/jsm", + "three":"three" + } +} \ No newline at end of file diff --git a/nwa/conf/nwa-config.json b/nwa/conf/nwa-config.json new file mode 100644 index 0000000..4a56434 --- /dev/null +++ b/nwa/conf/nwa-config.json @@ -0,0 +1,23 @@ +{ + "cluster": { + "id": "haverlab", + "name": "Haverlab", + "desc": "John Haverlack's Home Lab" + }, + "dirs": { + "base": "DIRNAME/../..", + "app": "BASE/app", + "modules": "APP/modules", + "logs": "HOME/logs", + "data": "HOME/data", + "clstr": "DATA/cluster", + "proj": "DATA/proj", + "jobs": "DATA/jobs", + "conf": "HOME/conf", + "modconf": "BASE/conf/modules", + "html": "BASE/html" + }, + "modules": { + "web": {} + } +} diff --git a/nwa/conf/nwa-id.json b/nwa/conf/nwa-id.json new file mode 100644 index 0000000..17c6053 --- /dev/null +++ b/nwa/conf/nwa-id.json @@ -0,0 +1,5 @@ +{ + "id": "1a381c70-9c9d-41d6-b3f3-41b2b2dcb201", + "created_at": "2026-06-28T16:46:10.204Z", + "hostname": "jehaverlack-ws-0" +} \ No newline at end of file diff --git a/nwa/html/404.html b/nwa/html/404.html new file mode 100644 index 0000000..8e68416 --- /dev/null +++ b/nwa/html/404.html @@ -0,0 +1,13 @@ + + + + + 404 - Not Found + + + +

404 - Page Not Found

+

The requested resource could not be found on this server.

+
Return to Home + + diff --git a/nwa/html/banner.html b/nwa/html/banner.html new file mode 100644 index 0000000..9240691 --- /dev/null +++ b/nwa/html/banner.html @@ -0,0 +1,9 @@ + + + + diff --git a/nwa/html/conf/nav-sidepanel.json b/nwa/html/conf/nav-sidepanel.json new file mode 100644 index 0000000..2884c0d --- /dev/null +++ b/nwa/html/conf/nav-sidepanel.json @@ -0,0 +1,115 @@ +{ + "sdl":{ + "title": "SDL", + "desc": "Software Defined Laboratory", + "icon": "fa-solid fa-square-binary", + "page": "index", + "enabled": false, + "items": [] + }, + "dash": { + "title": "Dash", + "desc": "Dashboard", + "icon": "fa-solid fa-gauge", + "page": "dash", + "enabled": false, + "items": [] + }, + "wkrs": { + "title": "Workers", + "desc": "Workers", + "icon": "fa-solid fa-server", + "page": "wkrs", + "enabled": false, + "items": [] + }, + "proj": { + "title": "Projects", + "desc": "Projects", + "icon": "fa-solid fa-layer-group", + "page": "proj", + "enabled": true, + "items": [ + { + "title": "Experiments", + "desc": "Experiments", + "icon": "fa-solid fa-flask", + "page": "expr", + "enabled": false + } + ] + }, + "jobs": { + "title": "Jobs", + "desc": "Jobs", + "icon": "fa-solid fa-person-digging", + "page": "jobs", + "enabled": false, + "items": [] + }, + "app": { + "title": "App", + "icon": "fa-solid fa-dice-d20", + "enabled": false, + "items": [ + { + "title": "About", + "icon": "fa-solid fa-circle-info", + "page": "about", + "enabled": true + }, + { + "title": "API", + "icon": "fa-solid fa-code", + "page": "api", + "enabled": true + }, + { + "title": "Docs", + "icon": "fa-solid fa-book", + "page": "docs", + "enabled": false + } + ] + }, + + "experiments": { + "title": "Experiments", + "icon": "fa-solid fa-flask", + "enabled": false, + "items": [ + { + "title": "Browse", + "icon": "fa-solid fa-table-list", + "page": "experiments", + "enabled": true + }, + { + "title": "New Experiment", + "icon": "fa-solid fa-plus", + "page": "experiment-new", + "enabled": true + } + ] + }, + + "runs": { + "title": "Runs", + "icon": "fa-solid fa-play", + "enabled": false, + "items": [ + { + "title": "Active Runs", + "icon": "fa-solid fa-spinner", + "page": "runs-active", + "enabled": true + }, + { + "title": "History", + "icon": "fa-solid fa-clock-rotate-left", + "page": "runs-history", + "enabled": true + } + ] + } +} diff --git a/nwa/html/conf/nav.json b/nwa/html/conf/nav.json new file mode 100644 index 0000000..6d6efdf --- /dev/null +++ b/nwa/html/conf/nav.json @@ -0,0 +1,49 @@ +[ + { + "title": "Node Web App (NWA)", + "icon": "fa-solid fa-square-binary", + "page": "index", + "enabled": true, + "brand": true, + "children": [] + }, + { + "title": "App", + "icon": "fa-solid fa-dice-d20", + "page": "app", + "enabled": true, + "children": [ + { + "title": "About", + "icon": "fa-solid fa-circle-info", + "page": "about", + "enabled": true + }, + { + "title": "Change Log", + "icon": "fa-solid fa-circle-info", + "page": "changelog", + "enabled": true + }, + { + "title": "Docs", + "icon": "fa-solid fa-book", + "page": "docs", + "enabled": true + }, + { + "title": "Settings", + "icon": "fa-solid fa-gear", + "page": "settings", + "enabled": true + }, + { + "title": "API", + "icon": "fa-solid fa-code", + "page": "api", + "enabled": true + } + ] + } + ] + \ No newline at end of file diff --git a/nwa/html/css/custom-styles.css b/nwa/html/css/custom-styles.css new file mode 100644 index 0000000..48d590e --- /dev/null +++ b/nwa/html/css/custom-styles.css @@ -0,0 +1,307 @@ +/* ------------------------------ + Callouts (final tuned spacing) + ------------------------------ */ + +.callout-established, +.callout-proposed, +.callout-speculative, +.callout-caution, +.callout-warning, +.callout-danger { + border-radius: 6px; + padding: 5px; /* internal padding */ + margin-top: 5px; /* external spacing tightened */ + margin-bottom: 5px; + border-left: 6px solid; + background: #fefefe; +} + +/* Title line */ +.callout-established p:first-child strong, +.callout-proposed p:first-child strong, +.callout-speculative p:first-child strong, +.callout-caution p:first-child strong, +.callout-warning p:first-child strong, +.callout-danger p:first-child strong { + display: block; + font-family: "Libertinus Sans", sans-serif; + font-weight: 600; + font-size: 1.05rem; + margin-bottom: 5px; +} + +/* Paragraphs inside callouts */ +.callout-established p, +.callout-proposed p, +.callout-speculative p, +.callout-caution p, +.callout-warning p, +.callout-danger p { + margin-top: 5px; + margin-bottom: 5px; + line-height: 1.45; /* slightly condensed text spacing */ +} + +/* Remove top margin on first paragraph, bottom on last */ +.callout-established p:first-child, +.callout-proposed p:first-child, +.callout-speculative p:first-child, +.callout-caution p:first-child, +.callout-warning p:first-child, +.callout-danger p:first-child { + margin-top: 0; +} +.callout-established p:last-child, +.callout-proposed p:last-child, +.callout-speculative p:last-child, +.callout-caution p:last-child, +.callout-warning p:last-child, +.callout-danger p:last-child { + margin-bottom: 0; +} + +/* Math inside callouts */ +.callout-established .math.display, +.callout-proposed .math.display, +.callout-speculative .math.display, +.callout-caution .math.display, +.callout-warning .math.display, +.callout-danger .math.display { + margin: 0.4em 0; +} + +/* Colors */ +.callout-established { + background: #e8f7e8; + border-left-color: #3a8d3a; +} +.callout-proposed { + background: #e8f0ff; + border-left-color: #3b6dd8; +} +.callout-speculative { + background: #f7e8ff; + border-left-color: #8b2be2; +} + +.callout-caution { + background: #fff0e8; + border-left-color: #f5a22f; +} + +.callout-warning { + background: #fff0e8; + border-left-color: #f5a22f; +} + +.callout-danger { + background: #ffe8e8; + border-left-color: #d83b3a; +} + + +/* three.js */ + +#proto-time-viz { + width: 100%; + height: 600px; /* or auto-resize later */ + border: 1px solid #333; +} + +/* #cylindrical-time-viz { + width: 100%; + height: 600px; /* or auto-resize later */ + /* border: 1px solid #333; */ +/* } */ + + +json-viewer { + background: #ffffff; + color: #222; + font-family: monospace; + font-size: 0.85rem; + border: 1px solid #ddd; + padding: 0.5rem; +} + +/* Keys */ +json-viewer .property { + color: #005cc5; +} + +/* Strings */ +json-viewer .string { + color: #22863a; +} + +/* Numbers */ +json-viewer .number { + color: #b31d28; +} + +/* Booleans */ +json-viewer .boolean { + color: #6f42c1; +} + +/* Null */ +json-viewer .null { + color: #999; +} + +/* Expand/collapse arrows */ +json-viewer .toggle { + color: #666; +} + +/* Hover */ +json-viewer .property:hover { + background: #f5f5f5; +} + + +#ssl-graph { + border: 1px solid #ccc; + background-color: #ffffff; + border-radius: 4px; + /* padding: 6px; + box-sizing: border-box; */ +} + + +/* Side panel */ +#sidepanel { + display: flex; + flex-direction: column; + width: 200px; + transition: width 0.2s ease; + border-right: 1px solid #ddd; +} + +#sidepanel.collapsed { + width: 47px; +} + +#sidepanel-rail { + display: flex; + justify-content: flex-left; + padding: 0.5rem 0; + border-bottom: 1px solid #ddd; +} + +#sidepanel-inner { + flex-grow: 1; + overflow-y: auto; +} + +#sidepanel.collapsed #sidepanel-inner { + display: none; +} + +#sidepanel-toggle { + /* margin-right: 0.25rem; */ + border-style: none; + background-color: transparent; + color: hwb(0 55% 45%); +} + +#sidepanel .nav-link { + color: lab(32.07% 0.83 0.29); +} + +#sidepanel i { + color: hwb(0 55% 45%); +} + +/* .dash-val { + color: hwb(116 7% 35%); +} */ + +[data-bs-theme="light"] .dash-val { + color: hwb(116 0% 46%); +} + +[data-bs-theme="dark"] .dash-val { + color: hwb(116 40% 0%); /* 40% lighter for dark backgrounds */ +} + +/* Dark Theme */ + +/* Dark mode for callouts */ +[data-bs-theme="dark"] .callout-established, +[data-bs-theme="dark"] .callout-proposed, +[data-bs-theme="dark"] .callout-speculative, +[data-bs-theme="dark"] .callout-caution, +[data-bs-theme="dark"] .callout-warning, +[data-bs-theme="dark"] .callout-danger { + background: #1a1a1a; + color: #e9ecef; +} + +[data-bs-theme="dark"] .callout-established { + background: #1a3a1a; + border-left-color: #5cb85c; +} + +[data-bs-theme="dark"] .callout-proposed { + background: #1a2a3a; + border-left-color: #5b8fd8; +} + +[data-bs-theme="dark"] .callout-speculative { + background: #2a1a3a; + border-left-color: #ab5bea; +} + +[data-bs-theme="dark"] .callout-caution, +[data-bs-theme="dark"] .callout-warning { + background: #3a2a1a; + border-left-color: #f5a22f; +} + +[data-bs-theme="dark"] .callout-danger { + background: #3a1a1a; + border-left-color: #d83b3a; +} + +/* Dark mode for side panel */ +[data-bs-theme="dark"] #sidepanel { + border-right-color: #495057; +} + +[data-bs-theme="dark"] #sidepanel-rail { + border-bottom-color: #495057; +} + +[data-bs-theme="dark"] #sidepanel .nav-link { + color: #adb5bd; +} + +/* Dark mode for json-viewer */ +[data-bs-theme="dark"] json-viewer { + background: #212529; + color: #e9ecef; + border-color: #495057; +} + +[data-bs-theme="dark"] json-viewer .property { + color: #58a6ff; +} + +[data-bs-theme="dark"] json-viewer .string { + color: #7ee787; +} + +[data-bs-theme="dark"] json-viewer .number { + color: #ff7b72; +} + +/* Dark mode for tables */ +[data-bs-theme="dark"] th { + background: #343a40; +} + +[data-bs-theme="dark"] th, +[data-bs-theme="dark"] td { + border-color: #495057; +} \ No newline at end of file diff --git a/nwa/html/css/nwa.css b/nwa/html/css/nwa.css new file mode 100644 index 0000000..31dee7a --- /dev/null +++ b/nwa/html/css/nwa.css @@ -0,0 +1,36 @@ +body { + font-family: sans-serif; + /* margin: 2rem; */ +} + +header h2 { + margin: 0; +} + +table { + border-collapse: collapse; + width: 100%; + margin-top: 1rem; +} + +th, td { + border: 1px solid #ccc; + padding: 8px; +} + +th { + background: #f0f0f0; +} + +#page { + min-height: 500px; +} + +.custom-header { + background-color: #003764; + color: #fff; +} + +footer { + --bs-secondary-color: #666; +} diff --git a/nwa/html/favicon.ico b/nwa/html/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e546a47baff05558cc6691ca7ad1dcf4dbd33f5e GIT binary patch literal 236606 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%0A7{^@!&?Yhnn*X;}ApYQgE z`KRmBpT?ijI3NwBho9^1v+MSSao2bI!~E0rXxeq1AFkUM#y{Wf5A#pgr9X{7qj5kQ zN)JER+h^D93*)Zu_J{eW>(R99IzL>uFN}Y_+aKniu1kL!e@5egG?X5GuD8#w+ZV=N z-|Y|cPuHVq*L8llZeJMxe78T$KV6spH2#dn0cj{b{9JFJUAHfcyT02W=AW)d)2{3M zaNWKz{`qcyn18x1{b~FejRVq9didFhze~*AxaxH5*s+SgThzcL9ltHEdQ?6wYurZs z`ThI%2QjdTA7fp0Uzn1>uudg(oR^E0mq5^EdIe;CI~b-MV!%YuBzdjg5`*h^>WOq`&OxFHh%B&zd#MivAc! ztXsFvN}pFOL+LMj`WrN8kl|detfw<)&X|D%2U^izRaKSMey>nB(qH!U*Rf+qvvcRp zLKSEB9UnV%=n%X9c!qQA*s(-#lm4=&KkkDVgWkDwClL;%Q3$@-8hg&4Ki}NHe?Jko zq`z$HuV>Gm=J4UeiEt>5LJl1|WO&vR_nZr3hnkw2#9Wg8vZ+6KId|?{bNBAu#JH4H zLG*pxpT(^`^6S;BmpO6bL{hF8=`Xwb>)N$zu-+swG9^^dzJ2=)zS+j^afZM2gFcN#v6X%=DeX}w?3GGl3`!Z4l{61jUV8dwJwNwQKCDk;u7!J7 zY)+apJiEDd>z1`4HSG#zB<+{VOHX?R?3uq{UX*>cF0PTmCTwG#)8<@D0gsm6(fo!D z8?3B@VcbdHX|yXX`Ko_QN0W@{XoFcNa74|7$pGJGD zGTrLur%jt?mMmE!{Yih~Knfgq|NZyP)~#Eu?=Mrk>sGs~`bfV%cozKi*Ix%Q{xt`a zetk$2`KklvUXC6;YA#*6WF}9Ztno(*;}2;rUz#hf?QPaH^) z1Iv~zGlerDzyJPw)1^xn;h!M>#ciQJ=c^w?YX=S-c(Tv=^5x6s<(FTU{-i%~AOQ|c znl$OZ&LdyHe!XeeuAT5t0Dtv&`RebA>l=-+7rA-*?YG~|;K75XKj}{#u*iX-Lx-AE zr%pAOpLzK3p;@tFg_rx&irW``&Trr1dfl`4bJeO+!jDPMEp0ZVKQ@I>KLujeB ziSs{}FJEq+e)?&RMKu-`2im{^?)g6c_+x{X+L9Qr62no?<<;Jo+urMYe{%)Ry!F;w z=KA&PZIRxS-~9F0U%|QK(x5abZI#CX;+k=u0NRNokq`Z##-ionFP`QXC*lsj{r21D z;>C+`NbgA=j72~C=p)U^wBbHLdd!C&nQP&GFY1`{iRhYr;M|a!ni?}?$PnF+ONZh> z5eGQWgLzKd=lHN^kn=UBPoJ*Xy+!z|kIP3N*SwyI+7HFr)}14S^1Mb_SmpY;fFy?i|3zz-mnfJYxN<2vfakOS-4AF7UGk^Y2viF(BGA#d)fz{S^V_V zPr+HJj8SNi4ldk;qe?ypLN+2nYcoAOwVf5D)@FKnMr{ hAs_^VfDjM@LO=)z0U;m+gn$qb0zyCt2!V_s@c&~Ss$2j7 literal 0 HcmV?d00001 diff --git a/nwa/html/img/circle-nodes-solid.svg b/nwa/html/img/circle-nodes-solid.svg new file mode 100644 index 0000000..3caa410 --- /dev/null +++ b/nwa/html/img/circle-nodes-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/nwa/html/img/favicon.ico b/nwa/html/img/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e546a47baff05558cc6691ca7ad1dcf4dbd33f5e GIT binary patch literal 236606 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%0A7{^@!&?Yhnn*X;}ApYQgE z`KRmBpT?ijI3NwBho9^1v+MSSao2bI!~E0rXxeq1AFkUM#y{Wf5A#pgr9X{7qj5kQ zN)JER+h^D93*)Zu_J{eW>(R99IzL>uFN}Y_+aKniu1kL!e@5egG?X5GuD8#w+ZV=N z-|Y|cPuHVq*L8llZeJMxe78T$KV6spH2#dn0cj{b{9JFJUAHfcyT02W=AW)d)2{3M zaNWKz{`qcyn18x1{b~FejRVq9didFhze~*AxaxH5*s+SgThzcL9ltHEdQ?6wYurZs z`ThI%2QjdTA7fp0Uzn1>uudg(oR^E0mq5^EdIe;CI~b-MV!%YuBzdjg5`*h^>WOq`&OxFHh%B&zd#MivAc! ztXsFvN}pFOL+LMj`WrN8kl|detfw<)&X|D%2U^izRaKSMey>nB(qH!U*Rf+qvvcRp zLKSEB9UnV%=n%X9c!qQA*s(-#lm4=&KkkDVgWkDwClL;%Q3$@-8hg&4Ki}NHe?Jko zq`z$HuV>Gm=J4UeiEt>5LJl1|WO&vR_nZr3hnkw2#9Wg8vZ+6KId|?{bNBAu#JH4H zLG*pxpT(^`^6S;BmpO6bL{hF8=`Xwb>)N$zu-+swG9^^dzJ2=)zS+j^afZM2gFcN#v6X%=DeX}w?3GGl3`!Z4l{61jUV8dwJwNwQKCDk;u7!J7 zY)+apJiEDd>z1`4HSG#zB<+{VOHX?R?3uq{UX*>cF0PTmCTwG#)8<@D0gsm6(fo!D z8?3B@VcbdHX|yXX`Ko_QN0W@{XoFcNa74|7$pGJGD zGTrLur%jt?mMmE!{Yih~Knfgq|NZyP)~#Eu?=Mrk>sGs~`bfV%cozKi*Ix%Q{xt`a zetk$2`KklvUXC6;YA#*6WF}9Ztno(*;}2;rUz#hf?QPaH^) z1Iv~zGlerDzyJPw)1^xn;h!M>#ciQJ=c^w?YX=S-c(Tv=^5x6s<(FTU{-i%~AOQ|c znl$OZ&LdyHe!XeeuAT5t0Dtv&`RebA>l=-+7rA-*?YG~|;K75XKj}{#u*iX-Lx-AE zr%pAOpLzK3p;@tFg_rx&irW``&Trr1dfl`4bJeO+!jDPMEp0ZVKQ@I>KLujeB ziSs{}FJEq+e)?&RMKu-`2im{^?)g6c_+x{X+L9Qr62no?<<;Jo+urMYe{%)Ry!F;w z=KA&PZIRxS-~9F0U%|QK(x5abZI#CX;+k=u0NRNokq`Z##-ionFP`QXC*lsj{r21D z;>C+`NbgA=j72~C=p)U^wBbHLdd!C&nQP&GFY1`{iRhYr;M|a!ni?}?$PnF+ONZh> z5eGQWgLzKd=lHN^kn=UBPoJ*Xy+!z|kIP3N*SwyI+7HFr)}14S^1Mb_SmpY;fFy?i|3zz-mnfJYxN<2vfakOS-4AF7UGk^Y2viF(BGA#d)fz{S^V_V zPr+HJj8SNi4ldk;qe?ypLN+2nYcoAOwVf5D)@FKnMr{ hAs_^VfDjM@LO=)z0U;m+gn$qb0zyCt2!V_s@c&~Ss$2j7 literal 0 HcmV?d00001 diff --git a/nwa/html/img/square-binary-solid.svg b/nwa/html/img/square-binary-solid.svg new file mode 100644 index 0000000..dff9ed0 --- /dev/null +++ b/nwa/html/img/square-binary-solid.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/nwa/html/index.html b/nwa/html/index.html new file mode 100644 index 0000000..7c66dfe --- /dev/null +++ b/nwa/html/index.html @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+ +
+
+ + + + + diff --git a/nwa/html/js/about.js b/nwa/html/js/about.js new file mode 100644 index 0000000..bb08f10 --- /dev/null +++ b/nwa/html/js/about.js @@ -0,0 +1,97 @@ +fetch('/api/config') +.then(res => res.json()) +.then(config => { + + // Package Vars + for (let pkvar in config.package) { + let appvar = 'app_' + pkvar; + + // Select *all* elements with this class + const elements = document.querySelectorAll('.' + appvar); + // console.log(`Found ${elements.length} elements with .${appvar}`); + + elements.forEach(el => { + // console.log(`Setting .${appvar} = ${config.package[pkvar]}`); + + switch (pkvar) { + case 'license': { + const lic = config.package.license; + const licUrl = config.package.license_url; + + if (licUrl) { + el.innerHTML = `${lic}`; + } else { + el.textContent = lic; + } + break; + } + + case 'homepage': + let home_html = `${config.package[pkvar]}`; + el.innerHTML = home_html; + break; + default: + el.innerHTML = config.package[pkvar]; + break; + } + + }); + } + + // Node.js + let node_html = ''; + node_html += '
    ' + node_html += `
  • Version: ${config.nodejs.version}
  • ` + node_html += `
  • Arch: ${config.nodejs.arch}
  • ` + node_html += `
  • Platform: ${config.nodejs.platform}
  • ` + node_html += '
'; + + document.getElementById('nodejs').innerHTML = node_html; + + // Dependencies + let dep_keys = ['name', 'version', 'license', 'homepage']; + let dep_html = ''; + + dep_html += '
'; // g-3 adds spacing between cards + + for (let dep in config.dependencies) { + const depInfo = config.dependencies[dep]; + console.log('Dep: ' + dep); + + dep_html += ` +
+
+
+
${depInfo.name || dep}
+
    + `; + + for (let key of dep_keys) { + if (!depInfo[key]) continue; + if (key === 'homepage') { + dep_html += ` +
  • ${key}: + + ${depInfo[key]} + +
  • + `; + } else { + dep_html += `
  • ${key}: ${depInfo[key]}
  • `; + } + } + + dep_html += ` +
+
+
+
+ `; + } + + dep_html += '
'; + + document.getElementById('dependancies').innerHTML = dep_html; + + +}) diff --git a/nwa/html/js/api.js b/nwa/html/js/api.js new file mode 100644 index 0000000..e77a879 --- /dev/null +++ b/nwa/html/js/api.js @@ -0,0 +1,6 @@ +const host = window.location.host; // includes hostname and port +const link = document.getElementById('api-link'); +if (link) { + link.href = `http://${host}/api`; + link.textContent = `http://${host}/api`; +} \ No newline at end of file diff --git a/nwa/html/js/docs.js b/nwa/html/js/docs.js new file mode 100644 index 0000000..e69de29 diff --git a/nwa/html/js/index.js b/nwa/html/js/index.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/nwa/html/js/index.js @@ -0,0 +1 @@ + diff --git a/nwa/html/js/proj.js b/nwa/html/js/proj.js new file mode 100644 index 0000000..e69de29 diff --git a/nwa/html/js/settings.js b/nwa/html/js/settings.js new file mode 100644 index 0000000..70a10f7 --- /dev/null +++ b/nwa/html/js/settings.js @@ -0,0 +1,50 @@ +console.log('Settings.js loaded'); + +// Get current theme from localStorage +const currentTheme = localStorage.getItem('theme') || 'dark'; +console.log('Current theme:', currentTheme); + +// Set the toggle to match current theme +const themeSwitch = document.getElementById('themeSwitch'); +const themeLabel = document.querySelector('label[for="themeSwitch"]'); + +console.log('Theme switch element found:', themeSwitch); + +if (themeSwitch && themeLabel) { + themeSwitch.checked = (currentTheme === 'dark'); + + // Update label based on current theme + updateLabel(currentTheme); + + console.log('Switch checked state:', themeSwitch.checked); + + // Listen for changes + themeSwitch.addEventListener('change', (e) => { + console.log('Switch toggled!', e.target.checked); + const newTheme = e.target.checked ? 'dark' : 'light'; + console.log('Setting new theme:', newTheme); + setTheme(newTheme); + updateLabel(newTheme); + }); +} else { + console.error('themeSwitch or themeLabel element not found!'); +} + +function setTheme(theme) { + console.log('setTheme called with:', theme); + document.documentElement.setAttribute('data-bs-theme', theme); + localStorage.setItem('theme', theme); + console.log('Theme set to:', theme); + console.log('HTML data-bs-theme attribute:', document.documentElement.getAttribute('data-bs-theme')); +} + +function updateLabel(theme) { + const themeLabel = document.querySelector('label[for="themeSwitch"]'); + if (themeLabel) { + if (theme === 'dark') { + themeLabel.innerHTML = 'Dark Mode'; + } else { + themeLabel.innerHTML = 'Light Mode'; + } + } +} \ No newline at end of file diff --git a/nwa/html/js/weblib.js b/nwa/html/js/weblib.js new file mode 100644 index 0000000..fc19627 --- /dev/null +++ b/nwa/html/js/weblib.js @@ -0,0 +1,913 @@ +// Dynamically include fragments +async function includeFragment(id, url) { + const res = await fetch(url); + const html = await res.text(); + document.getElementById(id).innerHTML = html; + } + +// Load a markdown file into #page +async function loadMarkdown(page) { + const mdPath = `/md/${page}.md`; + + try { + const response = await fetch(mdPath); + const hasMarkdown = response.ok; + const mdText = hasMarkdown ? await response.text() : ""; + + // Load nav metadata (top + sidepanel) + const [navRes, sideNavRes] = await Promise.all([ + fetch('/api/nav'), + fetch('/api/nav/sidepanel') + ]); + + const navItems = await navRes.json(); + const sideNav = await sideNavRes.json(); + + // ---- Locate metadata (nav.json first, then nav-sidepanel.json) ---- + let meta = null; + + // 1) nav.json β€” brand + meta = navItems.find(item => item.brand === true && item.page === page); + + // 2) nav.json β€” top-level + if (!meta) { + meta = navItems.find(item => item.page === page); + } + + // 3) nav.json β€” children + if (!meta) { + for (const item of navItems) { + if (Array.isArray(item.children)) { + const child = item.children.find(c => c.page === page); + if (child) { + meta = child; + break; + } + } + } + } + + // 4) nav-sidepanel.json β€” section headers + if (!meta) { + for (const section of Object.values(sideNav)) { + if (section.page === page) { + meta = section; + break; + } + } + } + + // 5) nav-sidepanel.json β€” section items + if (!meta) { + for (const section of Object.values(sideNav)) { + if (Array.isArray(section.items)) { + const item = section.items.find(i => i.page === page); + if (item) { + meta = item; + break; + } + } + } + } + + // Fallback + if (!meta) { + meta = { + title: page, + icon: "" + }; + } + + + // Fallback title if still not found + if (!meta) { + meta = { + title: page, + icon: "" + }; + } + + // ---- Build header HTML (NOT passed to marked) ---- + const headerHtml = ` +

+ ${meta.icon ? `` : ""} + ${meta.title} +

+ `; + + // ---- Render header directly ---- + const pageEl = document.getElementById('page'); + pageEl.innerHTML = headerHtml; + + // ---- Render Markdown content below header ---- + if (hasMarkdown) { + pageEl.innerHTML += marked.parse(mdText); + } else { + pageEl.innerHTML += marked.parse(` +> No markdown file found for: \`${page}.md\` +`); + } + + // Load optional page-specific JS + await loadPageScript(page); + + // Re-render MathJax + if (window.MathJax?.typesetPromise) { + await MathJax.typesetPromise(); + } + + } catch (err) { + console.error(err); + document.getElementById('page').innerHTML = + `
Error loading Markdown: ${err.message}
`; + } +} + + +// Dynamically load a JS file if it exists +async function loadPageScript(page) { + const jsPath = `/js/${page}.js`; // βœ… declare properly + + try { + // HEAD request checks whether the file exists without downloading it + const res = await fetch(jsPath, { method: 'HEAD' }); + + if (!res.ok) { + console.warn(`Script not found: ${jsPath}`); + return; + } + + // Dynamically inject script into the DOM + const script = document.createElement('script'); + script.type = 'module'; + script.src = jsPath; + script.defer = true; + + document.body.appendChild(script); + console.log(`Loading script: ${jsPath}`); + } catch (err) { + console.warn(`Error loading script ${jsPath}: ${err.message}`); + } +} + + + // Load Nav +function loadNav() { + fetch('/api/nav') + .then(res => res.json()) + .then(navItems => { + const navContainer = document.getElementById('nav'); + if (!navContainer) return; + + const urlParams = new URLSearchParams(window.location.search); + const currentPage = urlParams.get('page') || 'index'; + + const enabledItems = navItems.filter(item => item.enabled !== false); + + // --------- FIND BRAND ITEM ---------- + const brandItem = enabledItems.find(x => x.brand === true); + + // --------- CREATE NAV STRUCTURE ---------- + const navEl = document.createElement('nav'); + navEl.className = 'navbar navbar-expand-lg bg-body-tertiary'; + + const container = document.createElement('div'); + container.className = 'container-fluid'; + + // BRAND + if (brandItem) { + const brand = document.createElement('a'); + brand.className = 'navbar-brand'; + brand.href = `index.html?page=${brandItem.page}`; + brand.innerHTML = `${brandItem.title}`; + container.appendChild(brand); + } + + // TOGGLER BUTTON + const toggler = document.createElement('button'); + toggler.className = 'navbar-toggler'; + toggler.type = 'button'; + toggler.setAttribute('data-bs-toggle', 'collapse'); + toggler.setAttribute('data-bs-target', '#navbarNavDropdown'); + toggler.setAttribute('aria-controls', 'navbarNavDropdown'); + toggler.setAttribute('aria-expanded', 'false'); + toggler.setAttribute('aria-label', 'Toggle navigation'); + toggler.innerHTML = ''; + container.appendChild(toggler); + + // COLLAPSE REGION + const collapse = document.createElement('div'); + collapse.className = 'collapse navbar-collapse'; + collapse.id = 'navbarNavDropdown'; + + const ul = document.createElement('ul'); + ul.className = 'navbar-nav me-auto mb-2 mb-lg-0'; + + enabledItems.forEach(item => { + // skip the brand from the menu + if (item.brand === true) return; + + const hasChildren = Array.isArray(item.children) && item.children.length > 0; + const li = document.createElement('li'); + li.className = hasChildren ? 'nav-item dropdown' : 'nav-item'; + + // --- NO CHILDREN: NORMAL NAV LINK --- + if (!hasChildren) { + const a = document.createElement('a'); + let cls = 'nav-link'; + if (item.page === currentPage) cls += ' active'; + + a.className = cls; + a.href = `index.html?page=${item.page}`; + a.innerHTML = `${item.title}`; + + li.appendChild(a); + ul.appendChild(li); + return; + } + + // --- HAS CHILDREN: DROPDOWN TOGGLE --- + const toggle = document.createElement('a'); + toggle.className = 'nav-link dropdown-toggle'; + toggle.href = '#'; + toggle.role = 'button'; + toggle.setAttribute('data-bs-toggle', 'dropdown'); + toggle.setAttribute('aria-expanded', 'false'); + toggle.innerHTML = `${item.title}`; + + const menu = document.createElement('ul'); + menu.className = 'dropdown-menu'; + + item.children + .filter(child => child.enabled !== false) + .forEach(child => { + const childLi = document.createElement('li'); + const childA = document.createElement('a'); + + let childClass = 'dropdown-item'; + if (child.page === currentPage) { + childClass += ' active'; + toggle.classList.add('active'); // highlight parent + } + + childA.className = childClass; + childA.href = `index.html?page=${child.page}`; + childA.innerHTML = + `${child.icon ? `` : ''}${child.title}`; + + childLi.appendChild(childA); + menu.appendChild(childLi); + }); + + li.appendChild(toggle); + li.appendChild(menu); + ul.appendChild(li); + }); + + collapse.appendChild(ul); + container.appendChild(collapse); + navEl.appendChild(container); + + navContainer.innerHTML = ''; + navContainer.appendChild(navEl); + + // --------- MANUAL DROPDOWN HANDLERS --------- + // const dropdownToggles = navContainer.querySelectorAll('.nav-item.dropdown > .dropdown-toggle'); + + // dropdownToggles.forEach(toggle => { + // toggle.addEventListener('click', evt => { + // evt.preventDefault(); + + // const parentLi = toggle.parentElement; + // const menu = parentLi.querySelector('.dropdown-menu'); + // const isShown = menu.classList.contains('show'); + + // // close all others + // navContainer.querySelectorAll('.dropdown-menu.show').forEach(openMenu => { + // openMenu.classList.remove('show'); + // }); + + // // toggle current + // if (!isShown) { + // menu.classList.add('show'); + // } + // }); + // }); + + // // close dropdown when clicking outside nav + // document.addEventListener('click', evt => { + // if (!navContainer.contains(evt.target)) { + // navContainer.querySelectorAll('.dropdown-menu.show').forEach(openMenu => { + // openMenu.classList.remove('show'); + // }); + // } + // }); + }) + .catch(err => { + console.error('Failed to load nav:', err); + const navContainer = document.getElementById('nav'); + if (navContainer) { + navContainer.innerHTML = + '

Failed to load navigation menu.

'; + } + }); +} + + + +function loadNavTabs() { + fetch('/api/nav') + .then(res => res.json()) + .then(navItems => { + const ul = document.createElement('ul'); + ul.className = 'nav nav-tabs'; + + const urlParams = new URLSearchParams(window.location.search); + const currentPage = urlParams.get('page') || 'index'; + + // Filter out disabled items + const enabledItems = navItems.filter(item => item.enabled !== false); + + enabledItems.forEach(item => { + const li = document.createElement('li'); + li.className = 'nav-item'; + + const a = document.createElement('a'); + a.className = 'nav-link'; + a.href = `index.html?page=${item.page}`; + a.innerHTML = `${item.title}`; + + // Apply 'active' class to the current tab + if (currentPage === item.page) { + a.classList.add('active'); + a.setAttribute('aria-current', 'page'); + } + + // Optional: Support for disabled nav items in nav.json + if (item.disabled) { + a.classList.add('disabled'); + a.setAttribute('aria-disabled', 'true'); + } + + li.appendChild(a); + ul.appendChild(li); + }); + + const navContainer = document.getElementById('nav'); + if (navContainer) { + navContainer.innerHTML = ''; // Clear previous content + navContainer.appendChild(ul); + } + }) + .catch(err => { + console.error("Failed to load nav:", err); + document.getElementById('nav').innerHTML = + '

Failed to load navigation menu.

'; + }); + } + + + + // =============================== +// Side Panel (layout-collapsing) +// =============================== +// async function loadSidePanel() { +// // console.log('DEBUG: loadSidePanel() called'); + +// const panel = document.getElementById('sidepanel'); +// const inner = document.getElementById('sidepanel-inner'); +// const toggleBtn = document.getElementById('sidepanel-toggle'); + +// if (!panel || !inner || !toggleBtn) return; + +// const currentPage = +// new URLSearchParams(window.location.search).get('page') || 'index'; + +// const isCollapsed = +// localStorage.getItem('sidepanel_collapsed') === 'true'; + +// if (isCollapsed) { +// panel.classList.add('collapsed'); +// // toggleBtn.innerHTML = ``; +// toggleBtn.innerHTML = ''; +// } else { +// // toggleBtn.innerHTML = ``; +// toggleBtn.innerHTML = ''; +// } + +// try { +// const res = await fetch('/api/nav/sidepanel'); +// if (!res.ok) throw new Error(`HTTP ${res.status}`); + +// const sideNav = await res.json(); + +// renderAllSidePanelSections(inner, sideNav, currentPage); +// } catch (err) { +// console.error('Failed to load side panel:', err); +// inner.innerHTML = ''; +// } + +// // Collapse / expand behavior +// toggleBtn.onclick = () => { +// const collapsed = panel.classList.toggle('collapsed'); +// localStorage.setItem('sidepanel_collapsed', collapsed); + +// // toggleBtn.innerHTML = ` +// // +// // `; + +// toggleBtn.innerHTML = ` +// +// `; +// }; +// } +async function loadSidePanel() { + const panel = document.getElementById('sidepanel'); + const inner = document.getElementById('sidepanel-inner'); + const toggleBtn = document.getElementById('sidepanel-toggle'); + + if (!panel || !inner || !toggleBtn) return; + + const currentPage = + new URLSearchParams(window.location.search).get('page') || 'index'; + const isCollapsed = + localStorage.getItem('sidepanel_collapsed') === 'true'; + + if (isCollapsed) { + panel.classList.add('collapsed'); + toggleBtn.innerHTML = ''; + } else { + toggleBtn.innerHTML = ''; + } + + try { + const res = await fetch('/api/nav/sidepanel'); + if (!res.ok) throw new Error(`HTTP ${res.status}`); // βœ… Fixed template literal + const sideNav = await res.json(); + renderAllSidePanelSections(inner, sideNav, currentPage); + } catch (err) { + console.error('Failed to load side panel:', err); + inner.innerHTML = ''; + } + + // Collapse / expand behavior + toggleBtn.onclick = () => { + const collapsed = panel.classList.toggle('collapsed'); + localStorage.setItem('sidepanel_collapsed', collapsed); + toggleBtn.innerHTML = ` + + `; + }; +} + +// ------------------------------- +// Find matching section by page +// ------------------------------- +function findSidePanelSection(sideNav, page) { + for (const key in sideNav) { + const section = sideNav[key]; + if (!section || section.enabled === false) continue; + + if (Array.isArray(section.items)) { + const hit = section.items.find( + item => item.enabled !== false && item.page === page + ); + if (hit) return section; + } + } + return null; +} + +// ------------------------------- +// Render side panel content +// ------------------------------- +function renderAllSidePanelSections(container, sideNav, currentPage) { + container.innerHTML = ''; + + Object.values(sideNav) + .filter(section => section.enabled !== false) + .forEach(section => { + + // ---- Section Header (linkable if page exists) ---- + const header = document.createElement( + section.page ? 'a' : 'div' + ); + + if (section.desc) { + header.title = section.desc; + } + + + header.className = + 'fw-bold px-3 pt-3 pb-2 small nav-link'; + // 'fw-bold px-3 pt-3 pb-2 text-uppercase small nav-link'; + + if (section.page) { + header.href = `index.html?page=${section.page}`; + if (section.page === currentPage) { + header.classList.add('active'); + } + } + + header.innerHTML = ` + ${section.icon ? `` : ''} + ${section.title} + `; + + container.appendChild(header); + + // ---- Section Items (if any) ---- + if (Array.isArray(section.items) && section.items.length > 0) { + const ul = document.createElement('ul'); + ul.className = 'nav flex-column px-2'; + + section.items + .filter(item => item.enabled !== false) + .forEach(item => { + const li = document.createElement('li'); + li.className = 'nav-item'; + + const a = document.createElement('a'); + a.className = 'nav-link py-2'; + if (item.page === currentPage) { + a.classList.add('active'); + } + + if (item.desc) { + a.title = item.desc; + } + + a.href = `index.html?page=${item.page}`; + a.innerHTML = ` + ${item.icon ? `` : ''} + ${item.title} + `; + + li.appendChild(a); + ul.appendChild(li); + }); + + container.appendChild(ul); + } + }); +} + + + + +// Load Footer +function loadFooter() { + // Load API Config JSON from /api/config + fetch('/api/config') + .then(res => res.json()) + .then(config => { + let html = ''; + html += '
\n'; + html += '
\n'; + + html += '
'; + + html += '\n'; + + // Add GitHub link from config.package.homepage + html += '
\n'; + if (config.package.repository.url) { + html += ` \n`; + html += ' ' + config.package.repository.url + '\n'; + html += '
\n'; + } + html += '
\n'; + + html += '
'; + + html += '
'; + // Add copyright + html += '
'; + if (config.package?.version) { + // html += '
\n'; + html += `Version: ${config.package.version}\n`; + } + html += '
'; + + html += '
'; + html += `Copyright © ${config.package.copyright || ''}`; + html += '
'; + + // Add license if present + html += '
'; + if (config.package?.license) { + html += `License: ${config.package.license}`; + } + html += '
'; + + html += '
'; + + html += '
\n'; + + html += '
\n'; + // document.getElementById('app-description').innerHTML = config.package.description; + document.getElementById('footer').innerHTML = html; + }) + .catch(err => { + console.error('Error loading config:', err); + document.getElementById('footer').innerHTML = '
Footer could not be loaded
'; + }); +} + + +export function genBootStrapTable(json_obj, tblsize='1') { + + // json_obj structure + // [ + // [ + // "value1", + // "value2", + // "value3" + // ], + // [ + // "value1", + // "value2", + // "value3" + // ] + // ] + + // console.log("json_obj: ", JSON.stringify(json_obj, null, 2)); + // console.log("tblsize: ", tblsize); + + // A basic table with rows of content and no headers. + let html = ''; + html += '\n'; + html += '\n'; + for (let row in json_obj) { + html += `\n`; + for (let col in json_obj[row]) { + html += `\n`; + } + html += `\n`; + } + html += '\n'; + html += '
${json_obj[row][col]}
\n'; + return html; +} + +// SQL JSON To Bootstrap Table +export function sqlJsonToBootstrapTable(json_obj, col_disp_map) { + // console.log("Running sqlJsonToBootstrapTable"); + // console.log("json_obj: ", JSON.stringify(json_obj, null, 2)); + + let table_html = ''; + + if (json_obj.length == 0) { + table_html += "

No results found

\n"; + return table_html; + } else { + + table_html += "\n"; + + table_html += "\n"; + table_html += "\n"; + + // Get column names + let cols = []; + + for (let col in json_obj[0]) { + cols.push(col); + } + + // console.log("cols: ", JSON.stringify(cols, null, 2)); + // console.log("col_disp_map: ", JSON.stringify(col_disp_map, null, 2)); + + // Add column names to table + for (let col in cols) { + // table_html += `\n`; + // console.log(`cols[col]: ${cols[col]}`); + table_html += `\n`; + } + table_html += "\n"; + table_html += "\n"; + + for (let row in json_obj) { + table_html += "\n"; + for (let col in cols) { + switch (cols[col]) { + case 'id': + table_html += ` + \n`; + break; + + case 'balance': { + const val = Number(json_obj[row][cols[col]]); + if (!isNaN(val)) { + const formatted = val.toLocaleString('en-US', { + style: 'currency', + currency: 'USD' + }); + table_html += `\n`; + } else { + table_html += `\n`; + } + break; + } + + + case 'trans_amount': { + const val = Number(json_obj[row][cols[col]]); + if (!isNaN(val)) { + const formatted = val.toLocaleString('en-US', { + style: 'currency', + currency: 'USD' + }); + table_html += `\n`; + } else { + table_html += `\n`; + } + break; + } + + case 'trans_id': + // Fixed width, monospace for alignment + table_html += `\n`; + // table_html += `\n`; + break; + + case 'trans_date': + // Fixed width, monospace for alignment + table_html += `\n`; + // table_html += `\n`; + break; + + case 'notes': + table_html += `\n`; + break; + + default: + table_html += `\n`; + break; + } + // table_html += `\n`; + } + table_html += "\n"; + } + + table_html += "\n"; + + table_html += "
${cols[col]}${col_disp_map[cols[col]]}
+ + ${json_obj[row][cols[col]]} + ${formatted}${json_obj[row][cols[col]]}${formatted}${json_obj[row][cols[col]]}${json_obj[row][cols[col]]}${json_obj[row][cols[col]]}${json_obj[row][cols[col]]}${json_obj[row][cols[col]]}${json_obj[row][cols[col]]}${json_obj[row][cols[col]]}${json_obj[row][cols[col]]}
\n"; + + // console.log("Table HTML: "); + // console.log(table_html); + + return table_html; + } + +} + +// Load config (always from API) +export async function loadConfig() { + const res = await fetch('/api/config', { cache: 'no-store' }); + return await res.json(); +} + +// Load config +// export async function loadConfig() { +// const cached = sessionStorage.getItem('ssl_config'); +// if (cached) { +// // console.log('Loaded config from cache'); +// return JSON.parse(cached); +// } + +// const res = await fetch('/api/config'); +// const config = await res.json(); +// sessionStorage.setItem('ssl_config', JSON.stringify(config)); +// // console.log('Fetched config from backend'); +// return config; +// } + +export async function getConfig() { + const res = await fetch('/api/config', { cache: 'no-store' }); + return await res.json(); +} + +// export async function getConfig() { +// const cached = sessionStorage.getItem('ssl_config'); +// if (!cached) { +// throw new Error('ssl_config not found in sessionStorage'); +// } +// return JSON.parse(cached); +// } + + +// // Load tags +// export async function loadTags(forceRefresh = false) { +// if (!forceRefresh) { +// const cached = localStorage.getItem('app_tags'); +// if (cached) { +// // console.log('Loaded tags from cache'); +// return JSON.parse(cached); +// } +// } + +// const res = await fetch('/api/sql/query/tags'); // βœ… wait for fetch to resolve +// const tags = await res.json(); // βœ… wait for JSON parsing +// localStorage.setItem('app_tags', JSON.stringify(tags)); +// // console.log('Fetched tags from backend'); +// return tags; +// } + + +// export async function refreshTags() { +// const tags = await loadTags(true); // force refresh +// // console.log('Tags reloaded:', tags); +// } + + +// export async function tag_badge(tag, active = true) { +// const tags = JSON.parse(localStorage.getItem('app_tags')) || []; +// const t = tags.find(x => x.name === tag); +// if (!t) return `${tag}`; + +// const safeName = t.name.replace(/\W+/g, '_'); + +// const styleActive = `background-color:${t.color};border-color:${t.color};color:#fff;`; +// const styleInactive = `color:${t.color};background-color:#e9ecef;border:1px solid ${t.color};`; +// const style = active ? styleActive : styleInactive; + +// return ` +// ${t.name} +// `; +// } + + + + +export async function getLocalTimestamp() { + const now = new Date(); + const pad = n => String(n).padStart(2, '0'); + const offset = -now.getTimezoneOffset(); + const sign = offset >= 0 ? '+' : '-'; + const offsetHours = pad(Math.floor(Math.abs(offset) / 60)); + const offsetMinutes = pad(Math.abs(offset) % 60); + return `${now.getFullYear()}-${pad(now.getMonth()+1)}-${pad(now.getDate())}T${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}${sign}${offsetHours}:${offsetMinutes}`; +} + +export async function getUTCTimestamp() { + const now = new Date(); + return `${now.getUTCFullYear()}-${pad(now.getUTCMonth()+1)}-${pad(now.getUTCDate())}T${pad(now.getUTCHours())}:${pad(now.getUTCMinutes())}:${pad(now.getUTCSeconds())}Z`; +} + + + + +// =============================== +// Load on page load +// =============================== +window.addEventListener('DOMContentLoaded', async () => { + // Apply saved theme (default to dark) + const savedTheme = localStorage.getItem('theme') || 'dark'; + document.documentElement.setAttribute('data-bs-theme', savedTheme); + + const config = await loadConfig(); + // console.log(`DEBUG: ${JSON.stringify(config)}`) + + // const tags = await loadTags(); + // console.log(`DEBUG: ${JSON.stringify(tags)}`) + + // Load fragments and layout + includeFragment('banner', '/banner.html'); + // includeFragment('footer', '/footer.html'); + + loadNav(); + + loadSidePanel(); + + loadFooter(); + + + // Load Markdown content + const urlParams = new URLSearchParams(window.location.search); + const page = urlParams.get('page') || 'index'; + loadMarkdown(`${page}`); + + // Update title and app description + if (config?.package?.description) { + document.title = config.package.description; + + const descEl = document.querySelector('.app-description'); + if (descEl) descEl.innerHTML = config.package.description; + } +}); \ No newline at end of file diff --git a/nwa/html/md/about.md b/nwa/html/md/about.md new file mode 100644 index 0000000..5b6bd94 --- /dev/null +++ b/nwa/html/md/about.md @@ -0,0 +1,22 @@ + +## + +- **App:** +- **Version:** +- **Copyright:** +- **License:** +- **Homepage:** + +## Node.JS + +
+ +## Dependancies + +
+ + + + + diff --git a/nwa/html/md/api.md b/nwa/html/md/api.md new file mode 100644 index 0000000..a60a13b --- /dev/null +++ b/nwa/html/md/api.md @@ -0,0 +1,6 @@ + +You can access the JSON API Usage / Nav at this link: + + \ No newline at end of file diff --git a/nwa/html/md/changelog.md b/nwa/html/md/changelog.md new file mode 100644 index 0000000..8b21c6b --- /dev/null +++ b/nwa/html/md/changelog.md @@ -0,0 +1,126 @@ +# Software Defined Laboratory (SDL) + +| Attribute | Value | +| --- | --- | +| **Author** | John Haverlack | +| **Copyright** | 2026 John Haverlack | +| **License** | MIT | +| **Version** | 0.3.10 | +| **Date** | 2026-02-08 | + +## v0.3.10 - 2026-02-08 (ALPHA) + +- Dash Staging Jobs +- Dash Full sdl_id +- Updating main Web UI Page +- Start v0.3.10 + +## v0.3.9 - 2026-02-08 (ALPHA) + +- v0.3.9 +- Refactor Cluster Status Dash +- Refactor Cluster Status Dash +- System Columnn Display +- Start v0.3.9 + +## v0.3.8 - 2026-02-01 (ALPHA) + +- v0.3.8 +- Refactoing Worker Dashboard +- Refactoing Worker Dashboard +- Refactored Cluster Status +- Refactor of telemetry format + hw detection +- Switched Panel Icon to Bootstrap Icon +- Updating Worker Telemetry +- CP +- Fixing Wget/Curl Command Installer Port +- Fixing Wget/Curl Command Installer Port +- Fixing Wkr Start / Stop +- Update stop-sdl-wkr.sh +- WIP Install / Start / Stop +- WIP Install / Start /Stop +- Systemd Detectiion in Start/Stop/Status +- WIP: Start / Stop Scripts +- Non Systemd start stop scripts +- WIP: start stop scripts +- Updating Task List +- Adding Screeshot to README +- Start v0.3.8 + +## v0.3.7 - 2026-02-01 (ALPHA) + +**Summary** +sdl-wkr Telemetry + +- v0.3.7 +- Fixing sdl-wkr RAM detection +- Start v0.3.7 + +## v0.3.6 - 2026-02-01 (ALPHA) + +**Summary** +Light/Dark Mode, Wkr Install, Cluster Stats + +- v0.3.6 +- Adding Cluster Telemetry +- WIP: Worker Display +- Tweaks +- Start v0.3.6 +- CP Updates +- Dash: Total Cluster Stats +- Publishing to cluster/workers +- Light/Dark Mode Toggle +- Added light/dark theme CSS + +## v0.3.5 - 2026-02-01 (ALPHA) + +- v0.3.5 +- Fixing sdl-wkr install script +- Start v0.3.5 + +## v0.3.4 - 2026-01-31 (ALPHA) + +- Working sdl-wkr self update +- Working sdl-wkr self update +- Start v0.3.4 + +## v0.3.3 - 2026-01-31 (ALPHA) + +- WIP: sdl-wkr updates +- WIP: sdl-wkr updates +- Start v0.3.3 + +## v0.3.2 - 2026-01-31 (ALPHA) + +- v0.3.2 +- v0.3.2 +- Squetching Logs +- WORKING: join/authz +- WIP: Join Worker +- WIP: Join Process +- sdl-wkr Cluster Join +- Fixing Service Module Render Bug +- Start v0.3.2 + +## v0.3.1 - 2026-01-31 (ALPHA) + +**Summary** +README Cleanup + +- v0.3.1 +- Start v0.3.1 +- Removing duplicate LICENSE file +- README Cleanup + +## v0.3.0 - 2026-01-31 (ALPHA) + +**Summary** +Initial Codebase + +**Notes** +Initial GitHub Release + +- Preparing GitHub Version +- Start v0.3.0 +- Initial commit + diff --git a/nwa/html/md/docs.md b/nwa/html/md/docs.md new file mode 100644 index 0000000..02fd478 --- /dev/null +++ b/nwa/html/md/docs.md @@ -0,0 +1,119 @@ + +# Software Defined Laboratory (SDL) + +> NOTE: This is a work in progress. SDL has only been tested on Debian based +> Linux systems. But has been designed to be cross platform. So running on +> other platforms should not be a heavy lift. + +Other than basic OS tools, SDL downloads all dependencies idempotently and installs +everything into a single SDL home directory (~/.sdl). + +--- + +## SDL Manager (sdl-mgr) + +#### Installation + +``` +git clone +``` + +``` +cd sdl +``` + +``` +./install-sdl.sh +``` + +#### Upgrading + +``` +cd sdl +``` + +``` +git pull origin main +``` + +``` +./install-sdl.sh +``` + +--- + +## SDL Worker (sdl-wkr) + +#### Prerequisites + +(Debian, Ubuntu, Linux Mint, Zorin OS, Raspbian) + +``` +sudo apt -y install gawk coreutils curl jq grep sed tar unzip netcat-openbsd +``` + +#### Installation + +(Linux, OSX, Windows(WSL2)) + +Listen to UDP Broadcast on port 10101 +- To get the **sld-wkr** curl or wget install command + +``` +nc -u -l -k 10101 +``` + +OR + +``` +nc -u -l -k 10101 |jq +{ + "ts": "2026-01-31T05:20:31.815Z", + "sdl_id": "906a027c-52f0-4cbb-b3e0-5eb84856d50a", + "role": "sdl-mgr", + "host": "haverlab-sld-mgr", + "type": "udp-beacon", + "msg": { + "info": { + "desc": "Software Defined Laboratory: Manager (sdl-mgr)", + "version": "0.2.10", + "copyright": "2026 John Haverlack" + }, + "cluster": { + "id": "haverlab", + "name": "Haverlab", + "desc": "John Haverlack's Home Lab" + }, + "mqtt": { + "host": "10.0.0.178", + "port": 1883 + }, + "web": { + "proto": "http", + "host": "10.0.0.178", + "port": 8081, + "api_config": "/api/config", + "config_url": "http://10.0.0.178:8081/api/config" + }, + "sdl_wkr_install_cmd": { + "curl": "curl -s http://10.0.0.178:8081/dist/install-sdl-wkr.sh | bash -s 10.0.0.178", + "wget": "wget -O - http://10.0.0.178:8081/dist/install-sdl-wkr.sh | bash -s 10.0.0.178" + } + } +} + +``` + +``` +curl -s http://:/dist/install-sdl-wkr.sh | bash -s +``` + +#### Upgrading + +SDL Workers continuously monitor the SDL Manager’s advertised version via MQTT cluster status messages and will automatically re-install and restart when a newer version is detected. + +Alternatively you can upgrade manually by rerunning the install-sdl-wkr.sh script. + +``` +curl -s http://:/dist/install-sdl-wkr.sh | bash -s +``` diff --git a/nwa/html/md/index.md b/nwa/html/md/index.md new file mode 100644 index 0000000..c2fe4a8 --- /dev/null +++ b/nwa/html/md/index.md @@ -0,0 +1,8 @@ + +# Node Web App (NWA) + +Node Web App (NWA) is a Node.js application template for building fully self-hosted web applications with no CDN or Internet dependencies. + +NWA provides a single-service application structure that includes a web API, JSON-based configuration, configurable navigation menus, sidebar navigation, responsive Bootstrap UI, and integrated NPM libraries. + +The project is designed to be modular and extensible, allowing additional components such as SQLite support, MQTT broker integration, and other service modules to be added cleanly. \ No newline at end of file diff --git a/nwa/html/md/proj.md b/nwa/html/md/proj.md new file mode 100644 index 0000000..e69de29 diff --git a/nwa/html/md/settings.md b/nwa/html/md/settings.md new file mode 100644 index 0000000..a88e16e --- /dev/null +++ b/nwa/html/md/settings.md @@ -0,0 +1,26 @@ +
+
+
+
+
+
Appearance
+
+
+
+
+ +

Toggle between light and dark theme

+
+
+
+ +
+
+
+
+
+
+
+
\ No newline at end of file diff --git a/nwa/scripts/sdl-mgr.service b/nwa/scripts/sdl-mgr.service new file mode 100644 index 0000000..8812223 --- /dev/null +++ b/nwa/scripts/sdl-mgr.service @@ -0,0 +1,29 @@ +[Unit] +Description=SDL Manager +After=network.target + +[Service] +Type=simple + +# Paths +Environment=SDL_HOME=%h/.sdl + +WorkingDirectory=%h/.sdl/sdl-mgr/current/app + +# Start command +ExecStart=%h/.sdl/nodejs/current/bin/node index.js + +# Restart behavior +Restart=on-failure +RestartSec=3 + +# Logging +StandardOutput=append:%h/.sdl/logs/sdl-mgr.out.log +StandardError=append:%h/.sdl/logs/sdl-mgr.err.log + +# Clean shutdown +KillSignal=SIGTERM +TimeoutStopSec=15 + +[Install] +WantedBy=default.target \ No newline at end of file diff --git a/nwa/scripts/sdl-uninstall.sh b/nwa/scripts/sdl-uninstall.sh new file mode 100755 index 0000000..e69de29 diff --git a/nwa/scripts/start-sdl-mgr.sh b/nwa/scripts/start-sdl-mgr.sh new file mode 100755 index 0000000..5903030 --- /dev/null +++ b/nwa/scripts/start-sdl-mgr.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# start-sdl-mgr.sh - Script to start SDL Manager +set -euo pipefail + +use_systemd() { + # Check if systemd user mode is available and service exists + if systemctl --user status >/dev/null 2>&1; then + # Check if our service file exists + if systemctl --user list-unit-files | grep -q "^sdl-mgr.service"; then + return 0 # Use systemd + fi + fi + return 1 # Use manual scripts +} + +# Get $SDL_HOME directory from SCRIPT PATH +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SDL_HOME="$(cd "$SCRIPT_DIR/.." && pwd)" + +if use_systemd; then + # Systemd mode - let systemd handle it + echo "Starting SDL Manager (systemd)..." + systemctl --user start sdl-mgr + exit 0 +fi + +# Manual mode - continue with checks +NODE_BIN_DIR="${SDL_HOME}/nodejs/current/bin" +NODE_BIN="${NODE_BIN_DIR}/node" +NPM_BIN="${NODE_BIN_DIR}/npm" + +SDL_MGR_APP_DIR="${SDL_HOME}/sdl-mgr/current/app" + +# Check that sdl-mgr is not already running +SDL_MGR_PID="" +for pid in $(pgrep -f 'node' 2>/dev/null || true); do + cwd=$(readlink /proc/$pid/cwd 2>/dev/null || true) + if [[ "$cwd" == *"sdl-mgr"* ]]; then + SDL_MGR_PID="$pid" + break + fi +done + +if [[ -n "$SDL_MGR_PID" ]]; then + echo "ERROR: SDL Manager is already running with PID ${SDL_MGR_PID}" + echo " Stop it first: ${SDL_HOME}/scripts/stop-sdl-mgr.sh" + exit 1 +fi + +# Verify paths exist +if [[ ! -f "$NODE_BIN" ]]; then + echo "ERROR: Node.js not found at: $NODE_BIN" + echo " Run install-sdl.sh first" + exit 1 +fi + +if [[ ! -d "$SDL_MGR_APP_DIR" ]]; then + echo "ERROR: SDL Manager app not found at: $SDL_MGR_APP_DIR" + echo " Run install-sdl.sh first" + exit 1 +fi + +echo "Starting SDL Manager..." +echo " App Dir: ${SDL_MGR_APP_DIR}" +echo " Node: \ No newline at end of file diff --git a/nwa/scripts/status-sdl-mgr.sh b/nwa/scripts/status-sdl-mgr.sh new file mode 100755 index 0000000..a69a4cf --- /dev/null +++ b/nwa/scripts/status-sdl-mgr.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# status-sdl-mgr.sh - Script to show status of SDL Manager +set -euo pipefail + +use_systemd() { + # Check if systemd user mode is available and service exists + if systemctl --user status >/dev/null 2>&1; then + # Check if our service file exists + if systemctl --user list-unit-files | grep -q "^sdl-mgr.service"; then + return 0 # Use systemd + fi + fi + return 1 # Use manual scripts +} + +if use_systemd; then + systemctl --user status sdl-mgr +else + # Get sdl-mgr PID + SDL_MGR_PID="" + for pid in $(pgrep -f 'node' 2>/dev/null || true); do + cwd=$(readlink /proc/$pid/cwd 2>/dev/null || true) + if [[ "$cwd" == *"sdl-mgr"* ]]; then + SDL_MGR_PID="$pid" + break + fi + done + + if [[ -z "$SDL_MGR_PID" ]]; then + echo "SDL Manager is not running" + exit 0 + fi + + # Get process uptime + PROCESS_START=$(ps -p "$SDL_MGR_PID" -o lstart= 2>/dev/null || echo "unknown") + + echo "SDL Manager is running" + echo " PID: ${SDL_MGR_PID}" + echo " Started: ${PROCESS_START}" + exit 0 +fi \ No newline at end of file diff --git a/nwa/scripts/stop-sdl-mgr.sh b/nwa/scripts/stop-sdl-mgr.sh new file mode 100755 index 0000000..454f4d8 --- /dev/null +++ b/nwa/scripts/stop-sdl-mgr.sh @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# stop-sdl-mgr.sh - Script to stop SDL Manager +set -euo pipefail + +use_systemd() { + # Check if systemd user mode is available and service exists + if systemctl --user status >/dev/null 2>&1; then + # Check if our service file exists + if systemctl --user list-unit-files | grep -q "^sdl-mgr.service"; then + return 0 # Use systemd + fi + fi + return 1 # Use manual scripts +} + +if use_systemd; then + # Systemd mode - let systemd handle it + echo "Stopping SDL Manager (systemd)..." + systemctl --user stop sdl-mgr --quiet 2>/dev/null || true + echo "SDL Manager stopped" + exit 0 +fi + +# Manual mode - find and kill process +SDL_MGR_PID="" +for pid in $(pgrep -f 'node' 2>/dev/null || true); do + cwd=$(readlink /proc/$pid/cwd 2>/dev/null || true) + if [[ "$cwd" == *"sdl-mgr"* ]]; then + SDL_MGR_PID="$pid" + break + fi +done + +if [[ -z "$SDL_MGR_PID" ]]; then + echo "SDL Manager is not running" + exit 0 +fi + +echo "Stopping SDL Manager (PID: ${SDL_MGR_PID})..." + +# Send SIGTERM for graceful shutdown +kill "$SDL_MGR_PID" + +# Wait up to 10 seconds for process to exit +for i in {1..10}; do + if ! kill -0 "$SDL_MGR_PID" 2>/dev/null; then + echo "SDL Manager stopped successfully" + exit 0 + fi + sleep 1 +done + +# If still running after 10 seconds, force kill +if kill -0 "$SDL_MGR_PID" 2>/dev/null; then + echo "WARNING: Graceful shutdown failed, forcing kill..." + kill -9 "$SDL_MGR_PID" + sleep 1 + + if kill -0 "$SDL_MGR_PID" 2>/dev/null; then + echo "ERROR: Failed to stop SDL Manager" + exit 1 + fi +fi + +echo "SDL Manager stopped" \ No newline at end of file diff --git a/tools/changelog-update.sh b/tools/changelog-update.sh new file mode 100755 index 0000000..fea9f01 --- /dev/null +++ b/tools/changelog-update.sh @@ -0,0 +1,111 @@ +#!/usr/bin/env bash +# changelog-update.sh +# +# Idempotently maintain the changelog entry for the active version. +# - Updates METADATA.version_date +# - Syncs maturity +# - Ensures exactly one entry per version +# - Updates or creates the active changelog entry +# - Regenerates docs/CHANGELOG.md + +set -euo pipefail + +VERSION=$(jq -r '.METADATA.version' metadata.json) +DATE=$(date +%Y-%m-%d) +MATURITY=$(jq -r '.METADATA.maturity' metadata.json) + +tmp_meta=$(mktemp) +tmp_changelog=$(mktemp) +trap 'rm -f "$tmp_meta" "$tmp_changelog"' EXIT + +# ------------------------------------------------- +# Update version_date in metadata.json +# ------------------------------------------------- +jq --arg d "$DATE" ' + .METADATA.version_date = $d +' metadata.json > "$tmp_meta" + +mv "$tmp_meta" metadata.json + +# Propagate metadata into files +./tools/metadata-update.sh + +# ------------------------------------------------- +# Ensure changelog.json exists and is valid +# ------------------------------------------------- +if [[ ! -f changelog.json ]]; then + echo '{"releases":[]}' > changelog.json +fi + +if ! jq -e '.releases | type == "array"' changelog.json >/dev/null 2>&1; then + echo "ERROR: changelog.json must contain a single 'releases' array" + exit 1 +fi + +# ------------------------------------------------- +# Normalize: enforce one entry per version (keep newest) +# ------------------------------------------------- +# jq ' +# .releases |= +# ( . | reverse +# | unique_by(.version) +# | reverse +# ) +# ' changelog.json > "$tmp_changelog" +jq ' + def semver: + (.version + | split(".") + | map(tonumber)); + + .releases |= + ( + sort_by(semver) # oldest β†’ newest + | reverse # newest β†’ oldest + | unique_by(.version) # keep newest entry per version + | sort_by(semver) + | reverse + ) +' changelog.json > "$tmp_changelog" + + +mv "$tmp_changelog" changelog.json + +# ------------------------------------------------- +# Update or add active version entry +# ------------------------------------------------- +if jq -e --arg v "$VERSION" '.releases[] | select(.version == $v)' changelog.json >/dev/null; then + echo "Updating changelog entry for v${VERSION}" + + jq --arg v "$VERSION" --arg d "$DATE" --arg m "$MATURITY" ' + .releases |= + map( + if .version == $v then + .date = $d + | .maturity = $m + else + . + end + ) + ' changelog.json > "$tmp_changelog" +else + echo "Adding changelog entry for v${VERSION}" + + jq --arg v "$VERSION" --arg d "$DATE" --arg m "$MATURITY" ' + .releases |= + ([{ + version: $v, + date: $d, + maturity: $m, + summary: "", + notes: "" + }] + .) + ' changelog.json > "$tmp_changelog" +fi + +mv "$tmp_changelog" changelog.json + +# ------------------------------------------------- +# Regenerate Markdown changelog +# ------------------------------------------------- +./tools/genmd-changelog.sh diff --git a/tools/genmd-changelog.sh b/tools/genmd-changelog.sh new file mode 100755 index 0000000..52b331a --- /dev/null +++ b/tools/genmd-changelog.sh @@ -0,0 +1,134 @@ +#!/usr/bin/env bash +# genmd-changelog.sh +# +# Generate docs/CHANGELOG.md from changelog.json, metadata.json, +# and Git merge history. + +set -euo pipefail + +OUT_FILE="docs/CHANGELOG.md" + +echo "Generating ${OUT_FILE}" + +# ------------------------------------------------- +# Load metadata for header +# ------------------------------------------------- +NAME=$(jq -r '.METADATA.description' metadata.json) +ABBR=$(jq -r '.METADATA.abbr' metadata.json) +VERSION=$(jq -r '.METADATA.version' metadata.json) +DATE=$(jq -r '.METADATA.version_date' metadata.json) +AUTHOR=$(jq -r '.METADATA.author' metadata.json) +COPYRIGHT=$(jq -r '.METADATA.copyright' metadata.json) +LICENSE=$(jq -r '.METADATA.license' metadata.json) + +# ------------------------------------------------- +# Collect release merge commits (newest β†’ oldest) +# ------------------------------------------------- +mapfile -t MERGES < <( + git log main --merges --pretty=format:'%H|%s' | + grep -E "(Merge branch 'v|Release v)" +) + +find_merge() { + local tag="v$1" + for entry in "${MERGES[@]}"; do + if [[ "$entry" == *"Merge branch '${tag}'"* ]] || [[ "$entry" == *"Release ${tag}"* ]]; then + echo "${entry%%|*}" + return + fi + done +} + +# ------------------------------------------------- +# Write header (README-style) +# ------------------------------------------------- +{ + echo "# ${NAME} (${ABBR})" + echo "" + echo "| Attribute | Value |" + echo "| --- | --- |" + echo "| **Author** | ${AUTHOR} |" + echo "| **Copyright** | ${COPYRIGHT} |" + echo "| **License** | ${LICENSE} |" + echo "| **Version** | ${VERSION} |" + echo "| **Date** | ${DATE} |" + echo "" +} > "$OUT_FILE" + +# ------------------------------------------------- +# Iterate releases (SEMVER newest β†’ oldest) +# ------------------------------------------------- +jq -r ' + def semver: + (.version | split(".") | map(tonumber)); + + .releases + | sort_by(semver) + | reverse + | .[] + | "\(.version)|\(.date)|\(.maturity)|\(.summary)|\(.notes)" +' changelog.json | +while IFS='|' read -r version rdate maturity summary notes; do + tag="v${version}" + merge_commit=$(find_merge "$version") + + echo "## ${tag} - ${rdate} (${maturity})" >> "$OUT_FILE" + echo "" >> "$OUT_FILE" + + if [[ -n "$summary" ]]; then + echo "**Summary**" >> "$OUT_FILE" + echo "$summary" >> "$OUT_FILE" + echo "" >> "$OUT_FILE" + fi + + if [[ -n "$notes" ]]; then + echo "**Notes**" >> "$OUT_FILE" + echo "$notes" >> "$OUT_FILE" + echo "" >> "$OUT_FILE" + fi + + # ------------------------------------------------- + # Commit list + # ------------------------------------------------- + if [[ -n "$merge_commit" ]]; then + # Released version: merge-boundary commits + lower_bound="" + for ((i=0; i<${#MERGES[@]}; i++)); do + if [[ "${MERGES[$i]}" == "${merge_commit}"* ]]; then + if (( i + 1 < ${#MERGES[@]} )); then + lower_bound="${MERGES[$((i+1))]%%|*}" + fi + break + fi + done + + if [[ -n "$lower_bound" ]]; then + git log "${lower_bound}..${merge_commit}" \ + --no-merges \ + --pretty=format:"- %s" >> "$OUT_FILE" + else + git log "${merge_commit}" \ + --no-merges \ + --pretty=format:"- %s" >> "$OUT_FILE" + fi + else + # Active (not yet merged) version: commits since last release + if [[ ${#MERGES[@]} -gt 0 ]]; then + last_merge="${MERGES[0]%%|*}" + git log "${last_merge}..HEAD" \ + --no-merges \ + --pretty=format:"- %s" >> "$OUT_FILE" + else + git log HEAD \ + --no-merges \ + --pretty=format:"- %s" >> "$OUT_FILE" + fi + fi + + echo "" >> "$OUT_FILE" + echo "" >> "$OUT_FILE" +done + +cp docs/CHANGELOG.md sdl-mgr/html/md/changelog.md + +echo "Changelog written to ${OUT_FILE}" \ No newline at end of file diff --git a/tools/main-merge.sh b/tools/main-merge.sh new file mode 100755 index 0000000..8091edd --- /dev/null +++ b/tools/main-merge.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# main-merge.sh - Merge release branch into main, tag, and delete branch +set -euo pipefail + +CUR_VER=$(jq -r '.METADATA.version' metadata.json) +CUR_BRANCH=$(git branch --show-current) +TAG="v${CUR_VER}" + +# Ensure correct branch +if [[ "$CUR_BRANCH" != "$TAG" ]]; then + echo "You must be on branch $TAG" + exit 1 +fi + +# Ensure clean working tree +if [[ -n "$(git status --porcelain)" ]]; then + echo "There are uncommitted changes" + exit 1 +fi + +# Ensure tag does not already exist +if git show-ref --verify --quiet "refs/tags/$TAG"; then + echo "Tag $TAG already exists" + exit 1 +fi + + +# Switch to main +git checkout main +if [[ "$(git branch --show-current)" != "main" ]]; then + echo "Failed to switch to main" + exit 1 +fi + +# Merge release branch +git merge --no-ff "$CUR_BRANCH" -m "Release $TAG" + +# Create annotated tag +git tag -a "$TAG" -m "Release $TAG" + +# Delete release branch +git branch -d "$CUR_BRANCH" + +echo "Release $TAG merged, tagged, and branch deleted" diff --git a/tools/metadata-update.sh b/tools/metadata-update.sh new file mode 100755 index 0000000..c991fac --- /dev/null +++ b/tools/metadata-update.sh @@ -0,0 +1,91 @@ +#!/usr/bin/env bash +# metadata-update.sh - Update SDL metadata in package.json and Markdown files +set -euo pipefail + +# ----------------------------- +# Resolve paths +# ----------------------------- +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SDL_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +META_FILE="$SDL_ROOT/metadata.json" + +command -v jq >/dev/null || { echo "jq required"; exit 1; } +command -v sed >/dev/null || { echo "sed required"; exit 1; } + +# ----------------------------- +# Helpers +# ----------------------------- +get_meta() { + jq -r ".METADATA.$1" "$META_FILE" +} + +escape_regex() { + printf '%s' "$1" | sed -e 's/[][\/.^$*+?{}|()]/\\&/g' +} + +# ----------------------------- +# Update package.json +# ----------------------------- +update_package_json() { + local file="$1" + local tmp + tmp="$(mktemp)" + + jq \ + --argfile meta "$META_FILE" \ + ' + . as $pkg + | ($meta.META_MAPS["package.json"] | to_entries) as $maps + | reduce $maps[] as $m ($pkg; + .[$m.key] = $meta.METADATA[$m.value] + ) + ' "$file" > "$tmp" + + mv "$tmp" "$file" + echo "Updated $file" +} + +# ----------------------------- +# Update Markdown metadata table +# ----------------------------- +update_markdown() { + local file="$1" + + jq -r '.META_MAPS["*.md"] | to_entries[] | "\(.key)|\(.value)"' "$META_FILE" | + while IFS='|' read -r label meta_key; do + value="$(get_meta "$meta_key")" + esc_label="$(escape_regex "$label")" + + sed -i -E \ + "s#^\\|[[:space:]]*${esc_label}[[:space:]]*\\|[[:space:]]*[^|]*\\|#| ${label} | $value |#" \ + "$file" + done + + echo "Updated $file" +} + +# ----------------------------- +# Main +# ----------------------------- +jq -r '.MANIFEST[]' "$META_FILE" | while read -r rel_path; do + file="$SDL_ROOT/$rel_path" + + if [[ ! -f "$file" ]]; then + echo "WARN: $rel_path not found, skipping" + continue + fi + + case "$(basename "$file")" in + package.json) + update_package_json "$file" + ;; + *.md) + update_markdown "$file" + ;; + *) + echo "WARN: No handler for $rel_path" + ;; + esac +done + +echo "Metadata update complete" \ No newline at end of file diff --git a/tools/new-version.sh b/tools/new-version.sh new file mode 100755 index 0000000..dbff02f --- /dev/null +++ b/tools/new-version.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env bash +# new-version.sh - Create new release branch and update metadata +set -euo pipefail + +# ------------------------------------------------- +# Preconditions +# ------------------------------------------------- +if [[ -n "$(git status --porcelain)" ]]; then + echo "Working tree is not clean" + exit 1 +fi + +# ------------------------------------------------- +# Helpers +# ------------------------------------------------- +suggest_next_version() { + local cur + cur=$(jq -r '.METADATA.version' metadata.json) + IFS='.' read -ra parts <<< "$cur" + echo "${parts[0]}.${parts[1]}.$((parts[2] + 1))" +} + +# ------------------------------------------------- +# Load metadata +# ------------------------------------------------- +DESC=$(jq -r '.METADATA.description' metadata.json) +ABBR=$(jq -r '.METADATA.abbr' metadata.json) +COPYRT=$(jq -r '.METADATA.copyright' metadata.json) +CUR_VER=$(jq -r '.METADATA.version' metadata.json) +CUR_VER_DATE=$(jq -r '.METADATA.version_date' metadata.json) +NXT_VER=$(suggest_next_version) + +echo "Description: $DESC ($ABBR)" +echo "Copyright: (C) $COPYRT" +echo "Current version: $CUR_VER" +echo "Current version date: $CUR_VER_DATE" +echo "" + +read -p "New version: [$NXT_VER]: " NEW_VER +if [[ -z "$NEW_VER" ]]; then + NEW_VER="$NXT_VER" +fi + +NEW_VER_DATE=$(date +%Y-%m-%d) + +echo "" +echo "New version: $NEW_VER" +echo "New version date: $NEW_VER_DATE" +echo "" + +read -p "Are you sure? (Y/n) " -n 1 -r +echo "" +if [[ $REPLY =~ ^[Nn]$ ]]; then + exit 1 +fi + +# ------------------------------------------------- +# Safety checks +# ------------------------------------------------- +if git show-ref --verify --quiet "refs/heads/v${NEW_VER}"; then + echo "Branch v${NEW_VER} already exists" + exit 1 +fi + +# ------------------------------------------------- +# Update metadata.json (structurally) +# ------------------------------------------------- +tmp_meta=$(mktemp) +trap 'rm -f "$tmp_meta"' EXIT + +jq --arg v "$NEW_VER" --arg d "$NEW_VER_DATE" ' + .METADATA.version = $v + | .METADATA.version_date = $d +' metadata.json > "$tmp_meta" + +mv "$tmp_meta" metadata.json + +# ------------------------------------------------- +# Create branch +# ------------------------------------------------- +git checkout -b "v${NEW_VER}" + +# ------------------------------------------------- +# Propagate metadata and commit +# ------------------------------------------------- +./tools/metadata-update.sh + +git commit -am "Start v${NEW_VER}" + +echo "Created new release branch v${NEW_VER}" diff --git a/tools/udp-bcast-listen.py b/tools/udp-bcast-listen.py new file mode 100644 index 0000000..7771f2a --- /dev/null +++ b/tools/udp-bcast-listen.py @@ -0,0 +1,33 @@ +import socket +import sys + +def main(): + if len(sys.argv) != 2: + print("Usage: python udp_listener.py ") + return + + port = int(sys.argv[1]) + + # Create a datagram socket + try: + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + except socket.error as msg: + print('Failed to create socket. ' + str(msg)) + return + + # Enable broadcasting mode + sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) + + try: + # Bind the socket to a specific port + sock.bind(('', port)) + except socket.error as msg: + print('Bind failed. ' + str(msg)) + return + + while True: + data, addr = sock.recvfrom(1024) + print(data.decode()) + +if __name__ == "__main__": + main()