From 97256eb13239dbe44df53391860e74693295bb22 Mon Sep 17 00:00:00 2001 From: Lloyd Date: Fri, 24 Oct 2025 23:13:48 +0100 Subject: [PATCH] Initial commit: PyMC Repeater Daemon This commit sets up the initial project structure for the PyMC Repeater Daemon. It includes base configuration files, dependency definitions, and scaffolding for the main daemon service responsible for handling PyMC repeating operations. --- .github/FUNDING.yml | 1 + .gitignore | 50 + .pre-commit-config.yaml | 40 + README.md | 164 +++ config.yaml.example | 102 ++ deploy.sh | 110 ++ docs/dashboard.png | Bin 0 -> 146513 bytes docs/stats.png | Bin 0 -> 135837 bytes pymc-repeater.service | 39 + pyproject.toml | 57 + radio-settings.json | 44 + repeater/__init__.py | 1 + repeater/airtime.py | 78 ++ repeater/config.py | 137 ++ repeater/engine.py | 667 +++++++++ repeater/http_server.py | 459 ++++++ repeater/main.py | 271 ++++ repeater/templates/configuration.html | 216 +++ repeater/templates/dashboard.html | 712 ++++++++++ repeater/templates/help.html | 934 +++++++++++++ repeater/templates/logs.html | 238 ++++ repeater/templates/nav.html | 432 ++++++ repeater/templates/neighbors.html | 395 ++++++ repeater/templates/statistics.html | 335 +++++ repeater/templates/style.css | 1848 +++++++++++++++++++++++++ setup-radio-config.sh | 268 ++++ uninstall.sh | 99 ++ 27 files changed, 7697 insertions(+) create mode 100644 .github/FUNDING.yml create mode 100644 .gitignore create mode 100644 .pre-commit-config.yaml create mode 100644 README.md create mode 100644 config.yaml.example create mode 100644 deploy.sh create mode 100644 docs/dashboard.png create mode 100644 docs/stats.png create mode 100644 pymc-repeater.service create mode 100644 pyproject.toml create mode 100644 radio-settings.json create mode 100644 repeater/__init__.py create mode 100644 repeater/airtime.py create mode 100644 repeater/config.py create mode 100644 repeater/engine.py create mode 100644 repeater/http_server.py create mode 100644 repeater/main.py create mode 100644 repeater/templates/configuration.html create mode 100644 repeater/templates/dashboard.html create mode 100644 repeater/templates/help.html create mode 100644 repeater/templates/logs.html create mode 100644 repeater/templates/nav.html create mode 100644 repeater/templates/neighbors.html create mode 100644 repeater/templates/statistics.html create mode 100644 repeater/templates/style.css create mode 100644 setup-radio-config.sh create mode 100644 uninstall.sh diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..b3e56bc --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +buy_me_a_coffee: rightup diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a01bc69 --- /dev/null +++ b/.gitignore @@ -0,0 +1,50 @@ +# .gitignore + +# Python +__pycache__/ +*.py[cod] +*$py.class +*.so +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# Virtual environments +.venv/ +env/ +ENV/ + +# Testing +.pytest_cache/ +.coverage +htmlcov/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# Config +config.yaml +identity.json + +# Logs +*.log +.DS_Store diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..2f2968c --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,40 @@ +# Pre-commit hooks for mc_repeater +# Install: pip install pre-commit +# Setup: pre-commit install +# Run manually: pre-commit run --all-files + +repos: + # Generic file hygiene checks + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + + # Python formatting (Black) - apply to all Python files + - repo: https://github.com/psf/black + rev: 24.4.2 + hooks: + - id: black + language_version: python3 + args: ["--line-length=100"] + + # Python import sorting (isort) - apply to all Python files + - repo: https://github.com/pycqa/isort + rev: 5.13.2 + hooks: + - id: isort + args: ["--profile", "black", "--line-length=100"] + + # Python linting (flake8) - strict settings for code quality + - repo: https://github.com/pycqa/flake8 + rev: 6.0.0 + hooks: + - id: flake8 + # Strict but reasonable settings + args: [ + "--max-line-length=100", + "--extend-ignore=E203,W503" + ] diff --git a/README.md b/README.md new file mode 100644 index 0000000..2a5ceee --- /dev/null +++ b/README.md @@ -0,0 +1,164 @@ +# pyMC_repeater + +Repeater Daemon in Python using the `pymc_core` Lib. + +--- + +I started **pyMC_core** as a way to really get under the skin of **MeshCore** — to see how it ticked and why it behaved the way it did. +After a few late nights of tinkering, testing, and head-scratching, I shared what I’d learned with the community. +The response was honestly overwhelming — loads of encouragement, great feedback, and a few people asking if I could spin it into a lightweight **repeater daemon** that would run happily on low-power, Pi-class hardware. + +That challenge shaped much of what followed: +- I went with a lightweight HTTP server (**CherryPy**) instead of a full-fat framework. +- I stuck with simple polling over WebSockets — it’s more reliable, has fewer dependencies, and is far less resource hungry. +- I kept the architecture focused on being **clear, modular, and hackable** rather than chasing performance numbers. + +There’s still plenty of room for this project to grow and improve — but you’ve got to start somewhere! +My hope is that **pyMC_repeater** serves as a solid, approachable foundation that others can learn from, build on, and maybe even have a bit of fun with along the way. + +> **I’d love to see these repeaters out in the wild — actually running in real networks and production setups.** +> My own testing so far has been in a very synthetic environment with little to no other users in my area, +> so feedback from real-world deployments would be incredibly valuable! + +--- + +## Overview + +The repeater daemon runs continuously as a background process, forwarding LoRa packets using `pymc_core`'s Dispatcher and packet routing. + +... + +## Screenshots + +### Dashboard +![Dashboard](docs/dashboard.png) +*Real-time monitoring dashboard showing packet statistics, neighbor discovery, and system status* + +### Statistics +![Statistics](docs/stats.png) +*statistics and performance metrics* + +## Installation + +**Clone the Repository:** +```bash +git clone https://github.com/rightup/pyMC_Repeater.git +cd pyMC_Repeater +``` + +**Quick Install:** +```bash +sudo bash deploy.sh +``` + +This script will: +- Create a dedicated `repeater` service user with hardware access +- Install files to `/opt/pymc_repeater` +- Create configuration directory at `/etc/pymc_repeater` +- Setup log directory at `/var/log/pymc_repeater` +- **Launch interactive radio & hardware configuration wizard** +- Install and enable systemd service + +**After Installation:** +```bash +# View live logs +sudo journalctl -u pymc-repeater -f + +# Access web dashboard +http://:8000 +``` + +**Development Install:** +```bash +pip install -e . +``` + +## Configuration + +The configuration file is created and configured during installation at: +``` +/etc/pymc_repeater/config.yaml +``` + +To reconfigure radio and hardware settings after installation, run: +```bash +sudo bash setup-radio-config.sh /etc/pymc_repeater +sudo systemctl restart pymc-repeater +``` + + +## Uninstallation + +```bash +sudo bash uninstall.sh +``` + +This script will: +- Stop and disable the systemd service +- Remove the installation directory +- Optionally remove configuration, logs, and user data +- Optionally remove the service user account + +The script will prompt you for each optional removal step. + + +## Roadmap / Planned Features + +- [ ] **Public Map Integration** - Submit repeater location and details to public map for discovery +- [ ] **Remote Administration over LoRa** - Manage repeater configuration remotely via LoRa mesh +- [ ] **Trace Request Handling** - Respond to trace/diagnostic requests from mesh network + + +## Contributing + +I welcome contributions! To contribute to pyMC_repeater: + +1. **Fork the repository** and clone your fork +2. **Create a feature branch** from the `dev` branch: + ```bash + git checkout -b feature/your-feature-name dev + ``` +3. **Make your changes** and test with **real** hardware +4. **Commit with clear messages**: + ```bash + git commit -m "feat: description of changes" + ``` +5. **Push to your fork** and submit a **Pull Request to the `dev` branch** + - Include a clear description of the changes + - Reference any related issues + +### Development Setup + +```bash +# Install in development mode with dev tools (black, pytest, isort, mypy, etc) +pip install -e ".[dev]" + +# Setup pre-commit hooks for code quality +pip install pre-commit +pre-commit install + +# Manually run pre-commit checks on all files +pre-commit run --all-files +``` + +**Note:** Hardware support (LoRa radio drivers) is included in the base installation automatically via `pymc_core[hardware]`. + +Pre-commit hooks will automatically: +- Format code with Black +- Sort imports with isort +- Lint with flake8 +- Fix trailing whitespace and other file issues + + + +## Support + +- [Core Lib Documentation](https://rightup.github.io/pyMC_core/) +- [Meshcore Discord](https://discord.gg/fThwBrRc3Q) + + + + +## License + +This project is licensed under the MIT License - see the LICENSE file for details. diff --git a/config.yaml.example b/config.yaml.example new file mode 100644 index 0000000..f160724 --- /dev/null +++ b/config.yaml.example @@ -0,0 +1,102 @@ +# Default Repeater Configuration + +repeater: + # Node name for logging and identification + node_name: "mesh-repeater-01" + + # Geographic location (optional) + # Latitude in decimal degrees (-90 to 90) + latitude: 0.0 + # Longitude in decimal degrees (-180 to 180) + longitude: 0.0 + + # Path to identity file (public/private key) + # If not specified, a new identity will be generated + identity_file: null + + # Duplicate packet cache TTL in seconds + cache_ttl: 60 + + # Score-based transmission filtering + # Enable quality-based packet filtering and adaptive delays + use_score_for_tx: false + + # Score threshold for quality monitoring (future use) + # Currently reserved for potential future features like dashboard alerts, + # proactive statistics collection, or advanced filtering strategies. + # Changing this value has no effect on current packet processing. + score_threshold: 0.3 + + # Automatic advertisement interval in hours + # The repeater will send an advertisement packet at this interval + # Set to 0 to disable automatic adverts (manual only via web interface) + # Range: 0 (disabled) to 24+ hours + # Recommended: 10 hours for typical deployments + send_advert_interval_hours: 10 + +radio: + # Frequency in Hz (869.618 MHz for EU) + frequency: 869618000 + + # TX power in dBm + tx_power: 14 + + # Bandwidth in Hz (62500 = 62.5 kHz) + bandwidth: 62500 + + # LoRa spreading factor (7-12) + spreading_factor: 8 + + # Coding rate (5-8) + coding_rate: 8 + + # Preamble length in symbols + preamble_length: 17 + + # Sync word (LoRa network ID) + sync_word: 13380 + + # Enable CRC checking + crc_enabled: true + + # Use implicit header mode + implicit_header: false + +# SX1262 Hardware Configuration +sx1262: + # SPI bus and chip select + bus_id: 0 + cs_id: 0 + + # GPIO pins (BCM numbering) + cs_pin: 21 + reset_pin: 18 + busy_pin: 20 + irq_pin: 16 + + # TX/RX enable pins (-1 to disable) + txen_pin: -1 + rxen_pin: -1 + + # Waveshare hardware flag + is_waveshare: false + +delays: + # TX delay factor for flood mode (multiplier) + tx_delay_factor: 1.0 + + # TX delay factor for direct mode (faster) + direct_tx_delay_factor: 0.5 + +duty_cycle: + # Maximum airtime per minute in milliseconds + # US/AU FCC limit: 100% duty cycle (3600ms/min) + # EU ETSI limit: 1% duty cycle (36ms/min) + max_airtime_per_minute: 3600 + +logging: + # Log level: DEBUG, INFO, WARNING, ERROR + level: INFO + + # Log format + format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s" diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 0000000..2484912 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,110 @@ +#!/bin/bash +# Deployment script for pyMC Repeater + +set -e + +INSTALL_DIR="/opt/pymc_repeater" +CONFIG_DIR="/etc/pymc_repeater" +LOG_DIR="/var/log/pymc_repeater" +SERVICE_USER="repeater" + +echo "=== pyMC Repeater Deployment ===" + +# Check if running as root +if [ "$EUID" -ne 0 ]; then + echo "Error: This script must be run as root" + exit 1 +fi + +# Create service user +if ! id "$SERVICE_USER" &>/dev/null; then + echo "Creating service user: $SERVICE_USER" + useradd --system --home /var/lib/pymc_repeater --shell /sbin/nologin "$SERVICE_USER" +fi + +# Add service user to required groups for hardware access +echo "Adding $SERVICE_USER to hardware groups..." +usermod -a -G gpio,i2c,spi "$SERVICE_USER" 2>/dev/null || true +usermod -a -G dialout "$SERVICE_USER" 2>/dev/null || true + +# Create directories +echo "Creating directories..." +mkdir -p "$INSTALL_DIR" +mkdir -p "$CONFIG_DIR" +mkdir -p "$LOG_DIR" +mkdir -p /var/lib/pymc_repeater + +# Copy files +echo "Installing files..." +cp -r repeater "$INSTALL_DIR/" +cp pyproject.toml "$INSTALL_DIR/" +cp README.md "$INSTALL_DIR/" +cp setup-radio-config.sh "$INSTALL_DIR/" +cp radio-settings.json "$INSTALL_DIR/" + +# Copy config files +echo "Installing configuration..." +cp config.yaml.example "$CONFIG_DIR/config.yaml.example" + +# Create actual config if it doesn't exist (optional, will use defaults if missing) +if [ ! -f "$CONFIG_DIR/config.yaml" ]; then + echo "Creating config file from example..." + cp config.yaml.example "$CONFIG_DIR/config.yaml" + echo "NOTE: Default config created. Customize $CONFIG_DIR/config.yaml as needed." +else + echo "Existing config file found, keeping it." +fi + +# Setup radio configuration from API +echo "" +echo "=== Radio Configuration Setup ===" +read -p "Would you like to configure radio settings from community presets? (y/n) " -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]]; then + # Install jq if not already installed + if ! command -v jq &> /dev/null; then + echo "Installing jq..." + apt-get update -qq + apt-get install -y jq + fi + bash setup-radio-config.sh "$CONFIG_DIR" +else + echo "Skipping radio configuration setup." +fi + +# Install systemd service +echo "Installing systemd service..." +cp pymc-repeater.service /etc/systemd/system/ +systemctl daemon-reload + +# Set permissions +echo "Setting permissions..." +chown -R "$SERVICE_USER:$SERVICE_USER" "$INSTALL_DIR" "$CONFIG_DIR" "$LOG_DIR" /var/lib/pymc_repeater +chmod 750 "$CONFIG_DIR" "$LOG_DIR" +chmod 750 /var/lib/pymc_repeater + +# Install Python package +echo "Installing Python package..." +cd "$INSTALL_DIR" +# Use --break-system-packages for system-wide installation +# This is safe here since we're installing in a dedicated directory +pip install --break-system-packages -e . + +echo "" +echo "=== Installation Complete ===" +echo "" +echo "Enabling and starting service..." +systemctl enable pymc-repeater +systemctl start pymc-repeater + +echo "" +echo "Service status:" +systemctl is-active pymc-repeater && echo "✓ Service is running" || echo "✗ Service failed to start" +echo "" +echo "Next steps:" +echo "1. Check live logs:" +echo " journalctl -u pymc-repeater -f" +echo "" +echo "2. Access web dashboard:" +echo " http://$(hostname -I | awk '{print $1}'):8000" +echo "----------------------------------" diff --git a/docs/dashboard.png b/docs/dashboard.png new file mode 100644 index 0000000000000000000000000000000000000000..772b6bbd255848ed1dba9ab3f75956d911a7cd0e GIT binary patch literal 146513 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ⅇ^~c1?-}!$Z9aa{X^|*!EkP zUmmOv-CplCUy0-4lQlZE7su{DyE)t@J?)b68|9-%Ui z&fd~bu5|WZCi7Q2{TW8dcOxo)TJc;?ceY$EF5n8)DO?LUtd-`C+$b+9%E1AZqqY&7Ec zQ9EN*UZy`vL?s&v#m0d@^=5ZSOHU_#MG95sWT4xj^c6z%j$|9PE{Nk1U;bo#_TmMg z%>fsASQQ8p0;!~wfPQSvjdg&%`x2ikw)v<7w?ZlRF^}V;f&xq3zF&a-2R@V)g;rnC ztrRlS@+hmDE6R${o4wG`o&uG?dmyu0l{pKW2{=0rLU_RyZy$&Yx*JCwqMra==Cu zDG_2?)~`s~bQw>TbiSWb>}6o4Hk zaxS~ElFZE4f4mwV6R^*XesM;eth4GE5eoD_B_rDnlV-xfdPtz3-3WBKN}44Ta?ZJ4 zgbV5iiN4k99ZLjukN53)d;L#V!xmkzr+KaG+?k+6(EM=jaxb;mz!;b#;cv6vI%86F z3@L|6H>(e;p*OCKq-4JGJEj5&kn*5tds|2|@7lmjL^}e7$VPbZHGW9Y4&bU_>OZ>l zr9?SQsP1u2PJ19&S_XYL;i%9)n=uNJw<+>h_UbalSJM zyq(zIk4`q!E=1WJieh0!(u)K(*J$XK>2c^75HVE)i7AG68^p=LkYD(IdBl`*>u)ym`Q)F4GGj4lPmdMls1>Jf z>Eujr>=iJ7D>f7pQ$u2V0qg1Np3^8ksWCxVr}(dltv+jhka`th7%nONE<2O@(_fQ$ zH;oH*G)?Xb3(Lt?gX)0}c)VTvs>4g^?i-FiHG`{vixo~fEm&TQ!^wmu*u9cdA$k~Z%c69_!}HbNg?q{MNyF)hnG)~cyZN+kx6;v z_SUTN5@!`zO7Ev2Tsr0@me$T;OxT`oLaG&!C zheakMU(sb&et+Ak+6jP}1Dpt2I=X%9LxIxZ0((v%VJ6&wuW}(;OAAyv9F{DDDQv6gbJ{+$(SgVkKxgY}Ywt|Ml0xNa z$5#9Afx*qfH=i}**v(f$d7eFP?~J&S*j`&%0qk#ej8bk%`xnO*)f@a1JlDo>e^ z^9rjL;dT^3xZQe9xoyuD`CUyqz92QRwLRXn zG&HGlb5eGLOu3*e-l3GiXqEZu?!&{iVvp00K$`n*PdsO6V3c1&hmC{7w!xs;#Mky>fLtOAF#yNWJ(oe}3ZK z9;N3YThe&@d&&5g^n>5lMLb>RbrTVLy|9PzG!Q!6`qu9JxZ6oOtfbexO|}ztM!Rz! z8`g&_D=UdG)EKw-;pha;e$&$Sw%YYWmqYu(G}x!ZB#Q2iE_*~pNN9^eO_|oPLw}NA z?aKCqGi*K1gm5`=vLt!`qJj#W0v*wKYq?oDA#AU6HPd#P>5H1E;)LmNASNJ*FGu$M zUo(Q+Z4M7;6;m`g<;-};ZBD13auE1(UhUT1&=EwKm?DLB%L_L!5J^U*1FY)L$>$MW zv%u61_qClzDkBv%Hrjw!uhrnOq`EvXumkXS@C;V5KwKjF`fWWP3q)K;rPbAu`g+_b0G1%l^pL&Pab4$sP2TQ9V8M+7`y;;7gpW&wf`*h)cs zI7B)>_b2c=)M~#)*kR!XWe&f{+jzQYFTVp23qL+)kUo!ucsn9l`Nm1pf2;Pj!=0+&d$N-35@=I_2)EH$TQi zlsDd+Xc{4~Z|oEbFIkJ4RDIUIgSc{$w`&6=Xpl^oo=H{`b6}V<2d8v zbmLi{#sl95r&0+S!#bHW}lCvJ~5Z?tTX)Kl*+=z=-+DA3nP>AJo70 z;vUH6Rp6g`zivYpse7PL+yI5?UugON+T`>9132oR)%?DGXpYY=I^_;^b=)?#HD($J zg?aJ4AIaV(g?drnxJ?WH`2_b~OiiEB!%zw)nC9A^$n~T0sgOxe|9sw-9Fk&zM(pnP z-Zqk`9Yvu-+Ans4npdna-oDT}+;F!S$wBI2G$cQTup=|HFbUE{4RCFlh>()w=C4+R3c3 zpY?~jnx!U9Bt8rB;T+mrXHJtvH91@MAO4-dcw=rj15t{IP@8!-TYZ4LN|%pcDPF=y zd*B}D1~Cle<5CxX(v)OR$!<>Fu_8lKZM$h-s-Q5$fm7k_CIbE<+mCCjOt?Z=ctTqy z`bDb_$MHD@S9SaxBK5sFT?c8CFPxc3kHTpC*9NuNX*J=$ZfcXmUZuv(x;&oUp0r$~ z62l{Tb`r}b-a#PHK%Qo~Kj8n`lXXy9T)gOU{uaH=pLpL||C zQmlQ$KV_x5zAj9>R{h!GuR6;Zt<*wH-z%68-_!n?XL3xo>=n<{ezdXYM{ZapG^w!$ zy`hyYf;JKSEARJlS8dX-;F;3$Bx-f<4EIaLd**9N>A0fEpit?R>0f$dWr?Di>BO%* zc@)o2O0_-DJ*TcRbz(o{muwU^)skJl#a4@KlVqK}Bc%X8g`Gqm`;-65*?wu`*>U*D zT!O;v@!0rgz?f85$E;Z&R$6fmoFxJmS-rbL#y;6hyQ3PF@JfquLJ4KkmY<30Io)z~40;8;i?A6u!X z3}OLZAWxDI8{3IIbmfCDP7zXIqN5)viEf}da^-eS?aiFL*mpl5goWdXwiiW(%Jka1|fNKLTHjZPf zgQKNhSkxnsCkrd~`DYDnO#}24Hf^2&!#nTowY5qwG|$s8*)IXCxZ#ub^W?rf)z>76 zEGVl%Y`rQM=l<()xxMVE?Rd2t-VIK>MJ~TSD>*r0Agrjn-7vr19wx2I%Nvl}JACt6 z$>M9uBcSqVTxa%2WRJN9SMBE2%=BoG<&eS5O~0e~%H~onY7Q6_XN0KO{vEuZguy~f9>u^BmOwl=jK zxH*xq+5X$2VQ46#;?58+D}?%ls!Z69i&Ooj+Pn6gv8|1YgS|2XqG(CieFGMZO&3)N z1o|dt9T7hZ2Ik*Uj&8czPNF#-&+vk?HEg+wqa-P}Mq%wW+a3$Axj*;30Vo<6Up|(b zUCsg|z(dnu53fra@|e2aB)=(u5UYK~m2?kW@p)*{-bb*FKL<>mN#TqUKts&(i~61;mPdGfVh z?cu3)U*)EtW1BXfwVF8<^NV!-l_&dMv(wt0-E=HEXMXF7yR=HLVV;{QqZ$*}z)lM& zt*;f>u$on-DI@U_L@m1v%-|K*H8rsVUXjMlg$t>1b10CR3j|C+(5?0UB@7;j&z z?z*YKRsCsLpQDze?^Wqrc!TRTI;Qo+TToT4(E6?gw|Yty;dR~Z5o!DTW*>JlepKmj zi@uCFywQDNn6nk#{VS^bx zamd7AI+srAq;!V~B@oEIrDP8e1<$r5>17$D64cgmQ^qR)Uf0vp0e&kv^x5MF9$%sL zBMj z$*-3hhLkF~b?OH8aVVKfhBm=qAjSoXI~ z^c~*O<+X(ZiA(>+DQe4csEQFjt;w7cEachahk|y|=2hE|eGQ$hb%_1>Q(nBL40y6( z#dmlw{9O*hHH01JEBiZAh=PG`!}dF40ipRyWXP3`kK&|tJA9uvrpIOW*VX{#fmdR1 zdkjb|a`{zmE;2N9BlGp?Y!pA><{h1UrlJIba{wY7?CD_`fs&j&Cbeaxv{~G>Ha|6! zd>6lI;elBm}V;u`=RJ{TclGw>JBGrMjH(rqe0MM6hb1YRET%p22=4d3kbvZ_nw#o*(0` zlzsjEM$PX17%uskK?L{j`46`UoalSzE7W26Em7WQ8$HN`rs$GZ zqjRe6;ng()d6IJrpnb0QP!}dzL$~>%Sc8^GRDh51X(}EfRZ;^cZAct-Yir0|E^s6t8PtQ_H#C$2b`p4z8d7fLvC9hK}99>gTc>exY@=xFrumKKdbl8ZJE21!kO_i*o|NVS`J0u zkMnX=Bi@(v{!$p?6#Kkp!Cqk#)iv8!nN|L`9lqMON;jO(fTs9uU@=mdYFdM#9X!35 zhPoCQ<~v*0ky`?mAe$)#akKGz02)w)jAgd^`W;>H=p-&LC&c6~50c@4KFXMQNF_t( zM}fN?eD`d03JPxe`nBhwMztz_se0$TR9`m5jjfMN{VDR#GLukRSze}P7G(U@&gFH2 zZj-ksGZ3+m6LX!j$#mr?N!z`8hRLjSdz&8&yzGeiFCc(|^UqeUfW>>zPwv9P1MDu* zPeljSCkg$&tL#AqK~XeXWwO%!^}bqUx#|*(TtzdTT4C1?t=)RfOr(5-v}V6<@PASe z*ap`_l6pHFP}UUZsLAxJ`G}PtCk8$-DXfLJZ6kVL0VilHptw(wIQZ~*`_v{=3rL8^ z{`?i`i&x`w{$=F{%4g;&aEdfu@y_+(IVX!cY;M!r?)2i5Xpm^gJ5R3q1hL6#xKb#gRTxyDJNdx&DG)jRi~{>^|FUyAI?>kaR7Xsi^?Y<7Lq3iaJ0_ zoQqLB32Jjp^aq^uR6cZ|EW*m#w{9V`G?}MID#hg@1TD1hy-QU0A&Y^I`_qn*Q5Ecb zridb~=Z}mf5U=QuO6~!Bl_v_w2p)|0bI|NbS{i?Bv6Z=Tq$*jesx@i5G4P%SejQbf ztud!OdIXn~>o2dX+C@NzlOnY6^73oBxx>4jr>DIh9b^3YE5mZm?A67L|<0bbATPHEf5N*ht0P@|2-pFh(;0QFO5)v{nn z@r<6{TKq>sy35)?Q12^ADRF_DgZ5@(`gHSaoOiA z8nWtDxZgApU!9|0GP|N32F%d@E!MRs^OA z|AHlyIo+B}mU?s0v8I6_iM;{+WzJg--=Oy>N0tmbMGE7O(TVXEgiXgx0#JYy{YMS5p}_aQH3|iw(1~Dn@`k2KKb6 zVn4F^+&cHKQYB&ZF3gu_k7Q>~S5QRSp%x>wT0>8+Tn{^5EV!%oJ)1q7iAx_*Shq_V z>lYhcf+kIUYPpnbVR7_RmiGW2FD%b z?oxtkC z$mx;x_4Uxl);-ld0VntyFD?BPeWh{|mZ=my|Gscy3n=B1_SoLo^MO6i`6YL63CpJ* z2uK77Ej0_u-vA zF5Ib_$#F*;^$H2>ndAi1C|v9r-S76Dp7OXtN>g(49Mp-aJfh8ccgt8o)ueyL3&2rzw)6Hb9y&^zVewr!BJ{n_Fhj??i&my2#vB-627cAN+9}WRZ zS;zzqPE7YRuhOZ>T?|)m^vWSn@{q%&E-&s8 z#V{MmGaiRWGunCp-)4RBQ>)bo50DPQA2(s>>9^c@ZRh#Jnn`n@s-|uzY{1i5G)Y0O zH<7Ed<_)FEgbuGwVrVEANn{7|u&?ZA?CkW@D%DJNouBiB|6Bu{S z;&2v3JmXDCX)5(I#4>&S1T`n>}JhdgUn?UA$2Obdpfd=<8KPqzWz7-19?Zp2~=d z=6rl};l64@M^}(sC8Rifft|#Vj2pdkZQC(AA?KWAC*&`z!0LA3YL469K9r%(OmW*4 zi-CcZH-sv9NBN%}E2CXBN?;zFXHsg2UC)&{(wRyEM{H}_o)5C(`9FTS#t@Y5da-S; z{Pp|I7~OTDKr-O`^s z`}{C(J)Suwl4On45!_N+vVTe>E!Bn@vynZwu!%=kS z7~?rZJ56Fy``LDh=KlPiIZ{(E9_PO0W+~dJG?bd_%Ph{tl@V_uAvd1DIY{=NJ4<==&IgPhiZMe*S6-CjwgCl{0i&OpZsVA^wJmMG1c}9D|4j z!a^fsu%UeA7sv2cg`T1Q@OoF4LWLsPnH{Xgy30WXc%x>%?SvE{pWC!|dYNCf+>`U5 z$b4C4+4n2gpH+{je5;BqbKrJ${yr%;|H;KUg`JOCVfbc2*4pNDw#Euld(ZnlsPKWl z(f%55gYfw*_#&Via<)-*Y#vVMLJ|fitB-Ld=7ovi0Gc_7t#@nM?6^};#s=+}YH9M{ zoqAFUic8Ko3SRJn8Gvwc$R{6}b?{RatR!2UZ(-G%*pwEzemnh^Tbi{{+r0L>PFZaH zi_1eG9Ns3>K@1I@^<*1$E2R;?BJEy3ZqaS}3vgy;T!b!%i>%#@(}JPs<2+$+=K4t3 z3!BjYo>KZz{7p1MW&hbi&3b6>$+!~`p!5wvf0RzVINj3L00NZL+~)g?)^qlm*XU^T zFk|gRqTo>NSCDYcVv`@mKk+moRg*Ms^iOIc)4wUG5vZSP-esSDEX)l~^g@-i63zER z1RF?k7b?UUF?{CKEYYc4)+ef0iO<^S>+x|9->q3g>h?DO7N#Pz6>f?Diwl6&rL2?V zV2%*hlVc%++MQK1o{2QCezU zg@WI~2etkxN1nAPS{#2#0YuFokkV9+4zfq8qEl^bv;Z0@DMt38;B;qe!cFM3xI%qu z&UR^y+GgdgEuu-Axcwzg`$}eG85j>5p+Gw+F78XoNLg8=*7$W~D#_H;Sr%2+EETb^ z(BP8kgF}u(Mvh~jp9OS8sv#82%FoX?YIIi%rlv+o*>54%s&f^U zR1eclCqIT+vb8qgqG{(=*ZCI+77HAMdH^s zf#+v6k{SY(g^f#4X8)a>WDun42V%3|Rk*x!y?0*fd~ulm7V-~13Fbuc#>#*djmJ`1 zx3q903v+1d=XB#yMMECT4#2H-Gg#G+blsgTl`X=^N*vvCC9T`z0Q!t5-%A!{IZn`{ z7cg(h5S7v^Q;ttd#{X=DU&<7zViR|Bx>9vvq_|OeT$sa6+?M=-;Q}k3lM8;Q)ul0b-G5 z0l*l*dcr&A21lm?8^hFhZp>sg3?v;Cm8p3?7Dp?vIez0ZK>w3F8UJ`U{w2#DDI>1g z;+zZxd~zWAJkB!#3Qw3LHS;qYOT!h*e6anBbWf3907k6*;BGu&z^v5H+8ebV=F}c@ zOjz|7My|GOOrX-^fHk{HYg zQ6`Rewpii6NOt__2}pS#AcuhhbOUe>07VKcVr~1s92AYG*2GEs#il=st@3EO=9)w% zae#|f|4+09*$3b~{|&cc+8;6l0yOb|9x)h~Z@R~KHL)cl5fdiAB=by6{BeyUS?<7* zKYM?fN-3AePd7!t%$XBOGQ^shuL855RKp<=rut2BSMPXQe@{%rW)7JWNkYbwx#R)x zlP_Wb2)x|4}@; zmJ3A}FWv!J!qFtVmswP259Phn48De?!mXjVlE7xdsSStjUI%b?`e-r~^|OUISZ~4V zBJ!0Cx<%odDLif7rIWX{UbM8YAd^-lrA%0uSc3BgEcz{f5;(+t$zzO(C@F)N4!!if zBo%u_)X!}qYeoNe6i>A4KPZU392(_SRPOeZ`-ODH;=$Kxm9em%JZ z**;m?qIFxfn3x_`9Lgl29h3QA2at%YY<&X23^h*xd>EBf>Pmj*RovDpB{eZ3OXaWy z`Euvz-8pmnvp&@uyQTc+Fv7RzRHA!QKk46r0=%Ek(0`;MVlO|hh))8#&o#O_FcL&? zlE_nHx+QkZb-F2Q$-&}JoK#0!jwKWFH>iS##=W7rb0PNm{t;dMCmH?-$~h1vcQ~TzTA3x>q=!A_exS6l^qyqA&ozU*O~pOt~Qi zuqal*(_dRBTxh)g9!f44?dIXKR5>;}mQNfN%=bQNwhE$k+&{J3rCa~u32A)X(F2UAj-zKT~J zAbauKrRsBeqIaV#X}!!ppK$$Mn6p@hEPiT9w1P@!exZq)c5d(aafPT9DWLW+DVI5* z!9O`13Ne9dd;qw4cz0cCMHxh;ed}F+{uh)y7%Vsm+@FC@Rk;s)Yne*!1of}gkWRd= z{6f=&+3GZ0MxkqyR8uH7w{yWxAuv|SO|3Z@zxV&53wz?_K>yPnkH7(Cfkgl@Q9eKH zqztQz=;+tkT4gwMj`eOl$TQia5;M%^dnUcgt1j zhJX`@F=&}@ij=KJN{WWd1f>RWNI)OaL3c6iSw(rG3~7T5DNK&EUbe}gb1xdmwy0HM zwjqIargS`WQ*B_$U5UyC;TKYv7W~KcZn*Fr7YE31(Y|=L?CAKSsRJp=e|y|TbJ!=v z%>P!7`+p7*Pe54&FSX^&pTjfFem#dvu(<2PR7%IF^DGS;ASxDxuf{lKtR;e0HDS4=r%5})=^S0bmW>ZQMrsWkmEKwUy0j*6E}KisSD{ZaI=B% z&TiR!rb90OU}cZY{PeMgo$~_P%FVZ!4`?Y6>55mZ4eTpT&c9G&g63wob)j=n$e8_n z!?3)_J5W4)Y>-v@!=xoDeO@YHWG`R4!?`KDCfuO6iB&uYw!+I z9OwM!e|%iqHis9-xUuUPp%+6yRh#DVc?*VtK& z6~}7yOWtRs$Jwkps(R3VM750BW_k(>T6^&_AQUJG0D&xkynYt$$>ymugrMbd;FnPF z!eOIk$hu6Ma7l{SoU4_|Tz1%WJJ6*tP)JyyWzpF8PrIj^NeV3Y0if#&=>e%%zwe`$ zYTy%Xw!V_g|MLBV4CTd32Y*{54@k#AwHgm*Zd|={s(VWyS>Z+`%ahS?a&JBdaudt~oI#c*x7iucBW-!<<%65eN36Oc$D)4OVLUI}jY7wXXAh>lz7#OCh z)C&=Q?Olsh9n0}Kok#5Tzg!IxUxmjEHJ)o{Ml4|4U<_MKPAZB5xfzqYaK>|HuWSDw zmkOmW>ONa35#O!CW;txYpt85QnXjJzD?bl<1-{(z%A^zt;@j!iWSox(?IFVUAY|cp zt7g7DH|HdgC7d+AFQisC*Z+h$&8K?BkN`jB2un@#rfthr+)P27+sy_0z$;RW%w9Kn z%iSlhRx~;OFRW-H{+vln{8?s&>&q05lH&EPZZc^p-EH?7^ zd7%GP(t3ETV_;LbtP=AEl&Kb>Ol+>u>IpnM`5s zuo?Lu5St%+HvIdS=9u&*AaOsJ=b~4k{cB|W5Dkcvi~4Hvr$lNKCrBbQSlHFqheub`Kn9>0w4lcpVXP$ z_1$gctlOTCW5ubY@}yoVMDDKg{2hiCn^8L3Msk1ERSuEMuag@k$7A}f}D7m41F0( zw9}z1fSwQ~%g^SUzp}Ip`CBf1H&}3@2)9ygvZK;mut5(p1;EYM2k6S2xXuWnl(U6v zLImYU1s7l!FEosmyS2GbD}7*jen|ub?waC12yqigU&p$v$I3vca?<9ocN=Rfwdz-? z&CLy9mZO`4WUL?@oXp`WH~g9UFc_O^?6=_rLbJ^T>|>X)8ZT>ax4l+!e|&H5iNUG9 zq1fKg!IdDJ)mHnA!`?IpkyQ1Pj zF}S0OhymE~-|t#SZSa!cSMu7xG~1(BHuPZ$L8YlZlNxs047E-;PC<{;=~*?B(p0{u z<`2qE5xaFc+ek+bu!gcTAh&LgD_r@~l`qVm>D+}0O5~Zj6E;9ABYwZg2^_NUlo{mo8mmiEnzwvGr0jCjNO zU2bCL4|kVq8srOSf7e-vZk6y`r?s#;96fBdWe`Th0ghT&c7FM|f}oo=K{f&t^r274 z+4#_+rEA7CWlZg9ziIhT-2_$OR6eYoW95v_HzWZZtfIWUjle+m=Z7`YMzrDk^J#lO zi8$!(mb!aC9HhGz!)nUD`1R(m& ztYY=s31s8Y2mQMToRhK+{tzj29P*_IDY&+Ok_ltp^75I zux<5W@wVUcb3gX@b3f|$e(?h(X?#lU^`j@V9VvjUDnu+zdtYkbGvMeR59}~<6qAcD zHir~E==>P8`j}+2_9g_@V;UCJ7S{1+JmMgqa8kQS*;M=%C1F zTluMgOOo@AS&SOC)@}(>e25NLX1Cf_Rl-M|zr8lK;yv~NdV}ttNYDRxwGS5*bPBBC zqWjtF7GN>tzO*$+6SCyFfv2EA!u7n|=-`ffzCA90J};LS@kBCsFUJi-lQerVR7_7#v+-RA|C+ zTK8+pE~0qBiv7-VGxbl}@Kpc5y*=*yjAeV1d$h~Dv&(5WpN`)%y{+BcU5K?}y)JDi z9S#;`uAKG0J9NL0bTS~Yz)mJCA!*QkR0C;Hn(WKOoBTPlFfK{pU$EX|hm>k|=}vOH z^~FrzM|lhUlxUZiMc#}d%$j+5DJmI?gzA}as6OZ=(E3p{iO$O<%o8>@EL&EehfY5~ zx&=#13ZC!2J$|$@rb~sqB9Wpp4AQWy;J-nI1)7JzJBJWYE6g@AVQ8YI6wY`Kc;)2V zB)n3IrW4z9k#a~@!D3PEmJ(rjZ2nU`pzJYZ1ZW4jRG;^<(DIW7z+OMe#wZ7f@N&a& zx*+|~TCWcwlK{WulK*i@>`FGoJE^K%ksEOY#Lm`*iwiM66lilM!w7@O7uJ zmLk^*bPfE%;g4|dW4H}R))JCy;Rr59rZ?`_v=xbEn5>L9wmJ%DX z&-Led+(4w%jOBHpx}OPRP#_N@;LLNMc7QG`fRW>hPF%FRe!K>m!o4Hse+&E5e(Om+ zsZu3O{L$SwnR)v59Pfk{xzZ3wdsOT(_K?6Br8=#j33_eReQn|kqaF#c)iQ1AzW@2O zMWXmdE1}wxbM7l|f8Hy040^(C*Z(@$C;yGR(QweH5cilO9&ol_N)bgeMAUG72#CN8h`*PuY*u&V{Lqs_*y zIc(t&;EJ8Ak`lB#2o?DO3xPl55IxBHbT{f5*yFSp<+_cye@fK+>*v&;{rH)U66x?j za`_|*{!Da@V4ZirI4+3tMb7uLSe&4{c97x)K<`<0;Vuv1k`Je_p-{&44t!(4z`=#Q zTf1<*?M$!Xc&B=tnsQmcWAuJFb+~T8BnAY+pt0QMhco@45u3o_HIrDSJzpia${!n; zd64{$-}cMJiZ%EvughHRVcxmnhviS77|4}_qZ;Ug0@L@r`QA_q16MbZ06Vz$eu|WXIuW_q*Evj5O*B;+LMZjLfID62P?HrpDB-{bu z79*d75dCX49*JudKGS}M`O#wSoXq2x8hDXd@2&sC=XnoO9RIn6L)X)Iajvi%HO9$G z0<3zkz*ioAFCE54k&u}7Z1Ado$7PO_Ma$WfTCpupL&_mBIk0vQC;$s2r{^^)rMFjC zbuo8?O2bj9BHkcYn!)N8)6YH$=#{E7mZXkK&_il-PmWBGnKQwV&u#ih-`rnU}0 zRR~$aIvrCb!jqK@6!5$fF@2m=ukigAcKt>t^$XY&Ixz)a=KQYw9lH(8P%AOA#sC6E zQo4PKs|_Vr+@*V>SD&2|$gz@mK*a+#6`4jos8Wi}DoQ1ZtdPKXT5F3As$uF)sZ ztE*zxG>P4(NI+v!8Z`VpNyhi1vox5H-FdhNzu$JDwLN#iTH(2Q(NSyAe?S)=EzQkk zNcM-%prB`ZUTyL*>njk&3)m`d|FEIiENWWC@yOR{jH2o!|5l|=#%sZ>?;{b)3JKE! zQkl*lxtQIkuk}<3I+PR?;XD>WxfdDjN?TQs`d>d7m;&M=w1{x@9FxJcf7;GfnT)05 z-vlS$%F9TH0kjwSuC6C1y(4X9SSIEQtbj{B_>xTO%juYQQ5%|GP+|pRt+1=038xTH$tVhRkY(;?M%HcwI(#%1$LYpXw@^Kp|g#~hpdob;a{Od~p{ zXN-1RFR1DFm-2wNn0k}@S~F4A#d)}_(^1jXa_3C{p zi=SJh7@{Mcoo=~GToHP|2E`fA>ufwC2(`6LQ1`lUzzLz~{EGs9i6{7CXcmw^U17|T zF9!D1+Ir9Ch`tJlKK?_tfCyW}tXUuzwHvi5-4qWnax#jQJYYs-&ur!iN8b+`AiRVR zPF}x^0avDn<$Zq=fBG1dXV_hK_V&K%8zCST8p_>Lz*2OhWRnlH+AhV0@_^761g1g-6-QK{v$Wt zPwSoZ_P|Fm6BgbO1|#4uN`+YlW~`ni_5k>#&_BJ5l7=!K(7i0ELl!rupv%1ccQ{hH z#Tkt5l!avAkRh8yJ5D2wJ!(jSWcQWH2`9#dvD3Ptx82#NSZ*UU_-o*}LMm&Nae}Bs zd1wGenui4^&W7iUKx#WMyf~oRf#Kt5 zL*!$`%*4_mYMC`FS;gfuR=0;mStGR+xrYvIHwFNsO0^d8`PLAh{ZU4@{U*kyU{gKq z1{;YHpwndmc;io9{=JiIJg4-i;Ha{e;>o7ld8)F91grtMG{Y2r)*eqjjb6q?kSK!{ zjeaX#L)C;8o#TCz3rRUIxF=5%rM<_PtzuRh>?fA(&*Q1*WxK;vtTtXEcNS@6f{jF# z2Z%I)SA#f6#pZo)G$}bB`Qdd^*Kqg&)p8S#a6=VamdKOOC!fP5$zh9`?)$d5M*Rj! zVZNC9s86Yev?7YD0f7KViUPyE39xLeYb4Tt2gGXryVhsn1Nk5TfaxC$hy5{wlNLRZ z^plNz`-aosV^x6}5X1`ct_3ciK;V7-Q-|HyIT(_-)S+V`5G~sn`J!+Lz1Ly+zT+FF zO+D?p{%;p^p-X>QD9SVc;*bltH|dpX=q!R+AksR_&ZfK{TG8Bh_jk^_`rpDpWHqu2-!ZTXUXa#6@vqUVd$E;pBWC{hGw3NUwUt|XK@x-WL(c)-uZ`@(Q}#>*43lqEE} zzAu_OQ}eoj@Y!mWnmUH|=8PN9-!P}=K|ZqlH_@m)fC(`#^s zn^LVE26z_x<#&k;9<^n_CrO9Dp<`VmO|Y3)AT1j>UvK1X>iPe?6v7cwMnXrIe)nTF z^0gtPPa{xPAFaNYAeXKD=?=@(3?U2^I56W= z8Dch6hNXQ-h6EQ5;lva#TL?vWJ3EBgv>bp{L*prxIwUdq7_Lic3m9#unaQv#3=@PI zjo@HnH4O3nje`OWGz>NT@eK+54T9EbchKHEY+RRtJ%IK)WFRTL?*rDO{Xr^g=1x<8;$Du-adOV?piGVQPnlPtmLRaI{`TyTO; zZWOb*r+h1I8apn!cp4({@x~Yf0&)LecY{Ya90c z&%s1Zif&`0QhnN*?^s8vwG@;o>sQ<@WOFBmG=tu7nDbI(9mWJo|J#)^cT6JWT0Kh% z0}6Bi4*5H2T5NH;uXSZ1m~U_4s2z%jaDMneaV~0W1;uHx_b0Hk=89P; z0_1Y7XK_sMF?;>iXw#{r$rKSfkgMvwjb;CP_{$S$wF6j;$$etLzUtFpktd>R_IeRX#K6MG|&+#l!_X+RKb z%;Rqqh*L zQmJpG)YDCy_Eo>B3R45pWp^EnM*skjV-y%a>%k(xGxiVE*>nY@c@5f9#YsIr?U6dE z3;AFaH+J<+`C=epeM2j3`bjI7bwoN76B^3t&yqtd@M} z8_GY53eJEN(nd>zymQzSB*r$^?3|_8A3gVgcY*6?lTM+PejhXzl<8oYt)_GTL#mWv zr_xo%eu24gc@v?wNHBNMyM0zp-Z-{1m*El-Go)ADVT$A;z^p-;6&>YG&oOz1w$1Ac z_h0a8IMfbGV%@lv{4L$!bhs9s4jk*_GPZ z;%cUx<&+Suoplb@%;BeOH0@-UjHZF(xB_HWJ#qbSLL+K)_qVeQD21>mcqo+ZU-nOn z9J*v@&~efB`(Xd^@7Lm8cEntnol`$G2-1aoQOb++zdVS5tWt7B#$ek(!mWx z!*DsDq;9sb4`MblU;fQ{0R~a7Xx5HH{RMx8iRAZQA!WYl8X*D$1_XZhQp==Yk0B6! zM8GnZPmh*N_h_3N7g5qUddlV*tP{sL*`baWz3+4r7FSr5O(K&pueodS^c$o_z-(Ag z08pn`Ud|1w6&fuqXaW_wz-^O$nJJE<@%IKkZTR^D>zb4=@zBZrCJYNiX2)UYGzaxQ z7Ho2J*qzH)cDrwU_<3+p(XEiW)a0JFyd0Io2kXN}bvl>jZWBT>)2VFTdw=l0Khl1% z&bdx0PcC*}`D;CPon#wkd74~AJ5A2#e2G_BxgQ48O74nypQv2X!{aq zG0k<@%Ihq2Hu7lG{P+Y=i4$7rgGQ18Gm?6VJ=K6fd8>6zwW0P4`&*hUg~)v+z`3Xc zt`l%8nJR7_W9Lz@A>N$it3-L<0p}Ngb!pFC`(Kt3Z#aA6fYn%3R~U(oj#4YMu`bUy zd8oZZ$!%+-&H(aexhD{uZ7G{bK_f>Jor4~TPf@^jk5q1z-pCQy4a`H&w=%`8Nv}+U zNGn#FnmLZYk`Wsz6T5t&-!F|t(`&m(N-D_z;U^}=m_^&20d0cs+jE4Z@Xsw^%f)v1 zaFKZQzf4b8hLp+Thx1jp>+BIi_X`Zumc#&ge`jBs)4_n&%`9{{Spe$VAMSsA%8h?n z6rmpeWIU&AkrLKu?1Ru1=qq&{9+Jf7iwv7+a6wrojvY-CA*Z|(WemnfiK-jX)zmO% zc51M6VFvxt6qB4cW9_M1u!gYpDXK!^(Q6zfE*%E{#}%+dK_jzhp(s+(<^Gyk;baei z(pQf%bf!K4jmVF66zK?qqY?%%HEo{Hb8Vpdn(sNoLPF>m*rysf-Z3n>UT5Ozdt9AF zu0tTP`!>oSpGY;Y)8em2(5Ta$Fu6`!S@4{7qfT>+UFuQ(@S5H^N~f|b8jfh8r7-AK zjceQfg3f9$M}EpsQ0~a;n=HemIIDx9=DiArsi|whK>>sJ-wNX!(o&QwQ_RKJxr=vc zgs-EL2yQyL-(4S;jGJ?PJ?fit--v`aVMmF!{{VD${|}c|QnUhSpL2KP@x&76rEc=L z=12OG?KvT8tUQskq5LXrV2;~ruY(0zppE}0TSi8Th~MqE22~#etsc}&z5fue8v(A@ z{(YA`{!WeRCevM~-5)6uylEOqqj~zZIk8?95ehpk54iVMIDX7BK@D_H@E~sL9x-Xs zyC|jgF=GpJJ(7A4OdS^W8_&(9gSE~12h|eorI_>- z3PD{uRfIkkeio7fl;R2MUc-6XwK@7MdUA}}J^||OLma;ndUk&v7z9e-@B<_kZJ&Wp zkz|q2`+l*{4VRgL<#;TcvcJ4ln`quQEa9w8y9Uz8alhGgbdf9Qy}#LOpQfqJ{{h!2 zmDlO~;g(Y2etzI-xzpyO?YFxm&6=mzgn#t#{bv&#dse?=Mh-%xv&R z8@_*gF`s-JGOc=K^EvaAlDcZmhYv&&DDC;{*?>~W9K#w{BM-sEjI3JFb{I13v#+<5 zlm>u#&`PlXw_K~a94)J>!f6)0nCL4FNzWSRIwp z6$$5HT2fMoa~j$~k%bnjYA9Mp>s0rV)xvkkHQg((;F|)xo|xxJ$m&nlYm&B_I2tQh z_Yp`&{c}2P7fz#1G-HYRY9oML7TU(vU1IBrrtxUfWZJN4^&=tOMvsPNy~&oh)yJfA zV3c-@58y1hSo7%YDK>>uvj93MQYO*Mlv_ACIry2FndRi98r>?Ft`6s5e>w6`=1{$V z^Gio_HVhvBB<*Q$)Wn;)lmv(F2;fs>2n*B*(_`3syl5K)DE0Bv2fUS-pXHt@hm5P# z6-=>;7`Y{?RLg6W7<`hlCe6Re5+`}mnA{e;3a4k_`f*@+rAws0j*wFwrd{#$v)H|2j< zZKxO-<^LKaJY!}$zUGG<*A+@o5~?(zNs3~pbHc5^0d&ks66AfqJyv`iTDVYk1g^O! zd56Jb5E!{L?~KEJ%SV|o5IGZvbU zH8x-4zF+*z@x`WfEbZxbd&lIt@*?+oLL}n!pl@1_?>R~0;iqwf0#FiP+hkJo=b~i| z>QPN9O7UsDV(#GCV_&&BGmk}6TIZ~hu3*S)3JWg;I)707cX7^1B=fARN*z72n}C+b zjA7VuPHs|Q-^zF?o{P;-^y*`M5{rS~g=LO$k=J_`Bth_ul$0CsPUNNBINw$@x)72G?#gCISwzN!toY$piyE8nfMmSF?>*Gf;{;Z^PJE>7;?c_RcfxmwN{h3}tUmcblSR`)q zL}x!MX3`R}f%T^<=y$cUzrhAu!mK5AMq7rGXtE#+G+wKd$^ZmnQeQ^_Sn!gbhx%Du z_IhuDw=Fi?UuVkzxe{=1L@mvw&wO5a=E$X-jL(G~d;e`Y?pFQ-`WQ$uL%q40pRq#= z7)Tj7^EP`GY}vH=6>MepF)GwZZ}M+ER5iofwYIDiC0~IOgTNG5ZiR$0i`Ee$2s+5LnuDH2m#AbwBB&seoXGfy{3V}QL3X$$Shi;Bq<0^&u!CIz8we*d`4O0HB&JnTfinL zC(6yn4$B5%P_i}G{Q5Ms=~Nh_7+z4&;agkGe>L5xIxN+OnMgTsXXpBJmEA~_ecZwq zL;L#VMLKOK5oHDA8CQbcG!ybb0s+u6%R{n&@0+G8{2VL zeIDQ|ehRr~kg+53;)sO-C*pc#aZ>DSt(v!jut04DZ$g2;YX+ch)3;fOcM}q9F^h8=5u4ad^9L1tUZqs+ ztoZ_}`2v${kRGdZ{RMqZhbat{l~pBB9xnO$i;Ig7)Fq7YXvC&)u2*jsa&bJDI8wUj z3bH{UfzoP+jLWsp*TINjODzRQX_hK5&G*En?F#zf;YVvsWDrJ3P_jWJfO8GJOaL}z zY(AW5Db09xYw1IK*3fVu!St~1S9-13-P+GCgGz=8WL3E|#kpE!--zrV&oxGB|ht$>xTJ8Gso6HF0^Lt+xLHtwT$*ju(#ro!uRR_u0E1nip* zE575ugp1%TMZ|Zt`$?z}<3#UVpzCHI=H7e(oURuTq@ls;Lep+?s+io1=XRb-u*VZ1 zu`v~}`R9!vU*6dHHUA!&<>fx*78@@$YHLI(lQ&-Y3k5uV$0dju;`p7Zl6yf=kID|? zN3)ukTEg;%H)DCQxY`w5bmz1g^fGxig}68bW<2ezb_oNP)tHI~iuvU7X>s~QZXt02 zKq-q4fD=}d1zm@RKs|kj_j(>TB<9PItCpka#DI8IdgFw~3L{^qy&e(>eG3RAOlvP{ z0BL{NNUnq0gU^3~1Xk}mH##w3u(#+05&!Oj;guu<01?27kA7s>bt#h&*NPDTADFxO)N>B0O>^N% z%%sxZDvK=0l!?Nqe*DXC`?^JX`VZvR`{BzejC@(EnQu7g{-53&QusImnFr$6 zs2EpeM@l!obD5Cl#kEG2N7)!1I@!Z)sYAajC6JGH%?JDI(9klXZh*qA@9^s4cwRSm z(z0j4aj;E2;@$6&3yi6>ek{wC+BZyb|JAvwyg>q0sue8l=10gvI!r^gBe1=p)RjuW z+8-0+-XSN8y+7=cc?GhT`_D|7W;TBHXu%@DmV^RTCc-=gB%oe3NX?2#|7r!8N*PJn z-E6F2UM9s4{~F;%wzcp8p;#4*#Y|$eDJZv3yxh6XIIg~7M6DB_$cf{tb2m-JS6`JP zJW~Rh@)6+?v{SqS3Eu$l3%a=ZTc66sc-PMuO!A~y(F>|#;I5DAid?BIa6UBMtV~{? z0f5dl9S{azxOAn|vy-ofBP2r)V;MHw$W~iNM(dajU<^gqk_y)OZw%coji3>tcm#qM zhP<|yjPfTjw1X%%n_$UI4r{@Ug|b{v7CDh_Dhhh&XVvdr*4z}pYqr|OdVF6pUA%y| zoG@;t)0IWPxudu2I`#^JsNo+;Q)EODxf@hx_8n~U1NEwaY<>gaCm-%UX0u*21xRiO zpF*lSgh*zWhANnzZ=bfWnzvZ6IrB9TOi=_BkPpiXZ2aTDSvsDAQ7+T*H=3C}$9Tra zHr?28Z`*zY!R|Mgo1BoHY?`mOC<+z zA{S$x*PqIXB+kWwBy8^Q?J{mwIUed2u#QF~@To|ze5OEvfQh>P=S2^I9!1kC&z|{8 zKpl38EO`$stYLo;rc{N0@9VgbXz)Ns5QeIzh^KZHy*K#!plOx5scTm^TiP#A^H)QO zDfKeO2^IQ@#J>=c?{%INZP4PEpo7YP!z#L;M%<#a@8qlREEOGwo54gMWKBt0g*EH) zGcK36r!0IqpAn1oz^bvIm(}D-F)+off4TwFf7>LE%WERW6Ot!1{MQe-`Y+xoLK=)o z5r88Q_T|53%;ldSbwUE}B6d1EbA6SRr`A7SpK6h0zyftbwNEb_n+SlVKH{JMxts?b zk%&CL8FcfCPVP#D8!8Wt=?cP&`@n(_l(`$T0N(@a1@M&}Vj2{I{=)$2vy8RNZvB#; z4%{w0FCexD3by7Ql5K~FDnQ&T(4AdT$MHU8i&EW*Nde&4NjR$0r? z*kgca38eKpbQXEUbW91eJDh|HH|Pv0c-m&J)0PNBE{*Toc&q?(TtkkAPLbDMRwQG; zR`C^1AvfHwR+2^jtK;#o_1d=y4L}I!wy<@OU;=6~&3u@xBsH}J^vy^nr-(H+8LYJe z+{~&F$Nv(Nye5h4&0^09hxBG;8+SPpI-g18=={kmXX1Vf6=~hEMBprjeh4o-sY{ z){hzpmn~1=0>0n0Nk}u=qkDVXF7epVwobDj59RDh`eVJ5b$2;T?s%1%%#@s`VmK)~ z)wame=0ipfGRxS4pp}QANKv=40QXW!uS4LT`RI@j>6?l2ZS4ImR@sK;k+a8LGjIjg;o~ z@HB41QYj}x!rLeW#EBz6{P(Es>-$R{_ouPZb(3#jL#?7?W8CY0$KBXW%_#mtCFXV9 zTF71%y+b9LvA7?41hU5*rEMDSwg;<1E{`U8OaJfyK^i>)KD2{n`}M6w@I94+vhBL_4IV3thNt-0Z!q!q+>wm zog)ANFVbaXRx2Gh!}Tho(8{d0pUV2|^o}r7&L#~Pp*qWec26gtc^hZy{OdQ(R*5vG zb%p~PRW0+owf%4PSK&lcq_ewn1Z;UV()j>{9`NP2jBH@Tez?@J_k$xDdhO}^^xh)f znnymwX9cJw^5PXx`DhTBw?)knE2dw9@P;=C{4fxWM#OJBbyzYz&D_t{`lXf>`9+cZ zGKf#n3?M>%k1=Z*E==~>phV|%z3cvXD38v5_<$}TJaw+#)7!63i5?=EjhVD)z}kV0 z69)(Lqffd}g~A#G50wH5_E&=THSDkC;q6j8zF(~?fdAkM*NL{|?VcwLh&dqGcwS{| z{~mcRWufNvnoLv%J1}qCncIoT^vVx!>NLk|3xm~?L_}oEQlCo&Yt_>RSOlMU8je}=?b)zKt&9SW z2KYW&^_f1kLCtc4`+l}zQk2gUr$d_`1LR`}2d(C*{E&R*x3c@EigckV#+qT`##OUH zl4R(igzR6(V~ff%;m`4(XlLi-OH|e8F53hlum)%zz#!2KzWEqCqSh+ zc*`^#o)-z0nf3AT3IW>T@x8Q~P(p!P&j#$YVW`t1s zaGGz1hT6isvZ&%!dv2^V3gFV^uTiJR0av>2>oo~#5f+nt{5!q$*zHoF(?8x|vOSFZ zh<-QENY8xlacUR7-eEWj{QaCMWO!Tu@jMo(!g!$P2l4nk8%l-ieCj!G9MG|y@9P(v zkjX9}&gl2CnPIfg@o>!R#Aivb^xDpAbZ9XkNB*7T%7|2xd~{;eZ*7SqT9&KJBf-GcZu_0c8A!N_H4Z>M zqKuY8Mw0pYX=wd)N}dy{`y^vZE=``s>)H63HjP_(<^Sda{ME3^??o)pKX!3#@=8$A z+H^Sm{3GdW1+OJnfDUsCo54nq^0sJ`+s{@8?g~I>0|D^lz)%|dq{=MYOnAJw7%W@6 zIt~^6h;D{BncLv!Zz;U)k%4^KHHXVJ^Js-!03@_t?0MdNWitq5l`BVKd1=(88Yf5r zsETGomF|4ZR>WkVH6-_@utRzg{TY2eM2F1zetgtY0=75z~}3D~+nYw;zNAo2Lw5 z2HmV>|4eB>qoL|pa-W_+6TB@DTaYMx|Lc>`bNX!hX7ud#H8D?Wo#XIC5ap)btTV$@ zHqHBvohoG9=Reb8&ZcRxiBVq){yyA{Zvh#Lq512$EV`QR%In>zi}gnOUY~k>ycQ); z_Vv}iMM-vq#FYedcc8o*v7byF0CI)MIE#hSKliYPE+4dxoYNt1(eM6PX6?hvyHvM_W?jtyi{M?y(U4}HAph~4lapjkFcofy(~;nYLp^Zm z%{N-v9BsDEccC867ZwWB<~_onEuJ|_XB&2x`~nn&2fUZji&+<@R+#X?(ef%G8F0OI zwzY^dr{BrF988p*=_Z?F>sz=^r68V*STLhqRkD6P@mx0GV0+&Wz2-z)(Utq3SrW`O z67(4AytF&Hy*qvWS_xlofvi0F~`BUHdvBKe$<$(iYhniKWPO z?o_?LyV_Z<;m7ccdhT}>BkJZ&VNMqw(ZMknc<_P>lc5Ls3Z{9hPo)E!1oSVbfk?{YNVNM+MO)9T9iW+r2;{Ik2Rm&^_C(%QuC?EW=8pcytV?>Zi^pM=@s7nDbRqL;W@%gx+;U5ZHaF`LfuRGCiDkf4MtV z;VuD;oAo68=g;4bCbZ_8wS7{c;e*|csgsP9+`jAg-Gb=bLK|3L1iBWGdQFCUy>F4hy z4*<7?LK2&QV3v}UO3cF1cP{1HkGms+lR9RH@k=z);8UkI7iGUffi+C z6v}?ta$1-eRZ?;Vd4E+@G?b#TG-3e@)ErV?oAvR7pc*4CBJ5A!8;w+kkvT-aHu7I? zrCQN-UgON+UiQ`Zr!L>^^DAe7Lmf!V#5t;>sJV^i8~C&Ji%gxePqng7SNi{r0+71Z z8P6{jde1ITlXv(?2_(W|Nq zD(AdpzzW1LU{H5-%crs$uHrG2Uy-P*&H#4dc$K|-$fQ%g-$w^& zF!5Y&b~cs&oTz+SQ!T^(=MLY)VFvn zRdJO6rYvR*n2>w0h;T7m6hgPO3Q^jOPem=Sf;TJ+D7Z;jb=MZaKU{ zAzUzV?k8wXa&9O`4a1*%j)Xrxaq|Q$jR7W2y@=zL)oAwI2=9wk{?|W*cgpJ9*2-U2 zU@)W4bp+To!2jto*W|CcIj8(teem)g^6S4INL{Pw;VK#?ckOb0rV0#L1)!Lb5kdd?>sB9ez<0$4`AjKfPQv|sW#lSo``T_@&FPo!ep)#sv0x2${nm)Fu)HC>J znvpb3Zu;=izvnUuoL+wyH6tFYh%i1S{H4JdK1JP^If0q4LiQyH-)sF9$aJ+Gq~D^o-ufmBnBzkV()a_(0V-aW%jzth|ewhqT{!~X$o zv=GUc0b*dA&EVtv>qA1VtNTZ;hR3zeko_r}hpi-I>xh8mnY}NXSNZSLPHr31Jg#n! zAs9%0adtyfjVQ1%YDtF^y$7XnO!I$jl`DKT^@F5>oR>Z@yx(4%)Aw5C$9)l(6@oeW zS6-sD*yfp;_t*Q^HPGgVcISR7phgJevCHSxW19ETY&WTU(Gw{K0_@z*4hE$^)6K=Y z(9`N}PRJqdf@^n?N!qPw*M!u|;RT)-ko|jS0IB!c67;FUZE%>RG%nEXa?f>I*ZGWi z!bWyDUhBC{t?)3d6ngsdD<;EBBZ)#EAN5ZWPvx-YihJVSuqI!G*=OplfcvWwjp~E@ zwhR%9MCY}1=;Ng^#olzy@mOgyY7@Z7vn9l4(^YAfPuKe-yrSZC)g~sxWo%t?TPsQB z_WdZCV4MwTm4_8XCDtjH#BFnXvaUeSzy!v|!@Hk|RtP0#=i}lU`c^~C^L=Ic*xTjm zpl-y(`{pR=(`$I3MeNtnHJ0bIw)6IYw^$*5+xE;JC#~H{HP+M=`Lb7WLPFlh0?t^9 z4wW=Q*Dx4BaQH1L@6~m|`rmmW?Z2#*50B-g@Mt7d*hm2=B$alX5imc#T=Yb;o5&we z=a$NsR%jI0)zu;6d6yZqcg<<?>y!_h!nw01JBmH|C>gLD0eIQ4SIztNUy??>^B3l3RLt6-2g2$4mJv&TJlaY!Y_)3^^boOS=KK1sQx%GL z9N$;|{CKVJeO{~}sQX!WC8_l8dS6}nkDS(==k?O#koz-vM$ATxDg!hkEC{hFdwQ2f zA(7+TKs>KK=~9ii_n<^q7$Lh^o3C$&?@0erlN*y><;rMx+QMJk@t)Oo60h$^Ow&N? zROMgrXcQF2D%co7ol(w9uF8bO#BSW;r4gh1@E6C=Lk-Vv&+QQ+4DT1Xo-)yv@Iu7W z{e_Y;Mkx(0pT+yAH&-r!NG3FDK#SbyLWF1uMQSTNlAsQ2X;0K z)h3tNx@`^1Ojk|P91h~;!8T3jM~P`u{!8^<5F!1>jg5TeXSZu&in#{o(B)Jgrm# zcU%l!qHsJtx7qD;j8Q_HrpL)R88F3Y-fBDa@hM=o!o*)%@Z#hF>%GF%QaJe%tHaJ0 zin%iT>5NX9Kw2o-bCU70;BAKyAl7m+a!tu`>dH42aLCIp`&JZlRSXQ+3iDi6fj#C` z#Jp}C=m?mR%cML&8{fBuSlKFqS38vNssKqGP2qQ>qG(ub<}bV>m{7peA-KFAiKHCQ zktbmL`d0+QIFKD18&@`-RWDx!b2Qd!p2R(aPu+Wcux9U|ZS$(}_HI92ZPs|%^BIua zDQKUGO2KD?kVz%wl*BC^h!PVz;`PC71E9GQpL$=8(?~ENQ?=C!LiZ1c&dbPeeu1f? zd&iZOgCv>b()gTsRD28y`5)h*yh)HvO^%TTQ+HB30;Ps~bNK-51Lkn(S+Sp;TvUb= zQ5@{HFv-=fwnk>c#>J`EWzLtSiIlb}Ml!G3+r)cTIv?9UZ#r3HNUzqRFrTNfI?Iwe zoU2v^(`m$7EvI{$Hi0Q9GURuOMOnCumL;J!XB|Q8IVR|%&5!r`xBJW1#eG!?3=F{J z`M>sj0V-!Z`q)4zob5lKj2Cl8YP3Kag z7y}oIA~W{)ZJhC?VFvqZ={km_)s>&^4NiIJ0DWe_8HIw(d5`6d3qY&)^Lv{QABsB< zPgU^c?$JoJD!=yjM(2t!95-C2z6kCQ;S=|?G4oRO%y;P%F@bYc7P)Hq^aEQX{YeFO z#1R6w`lU~g$H4SiQN}AwB8CNTPiD&2(F7lQE-C-HA7W7f>NA{WZ)nEfyuU_ncQ>|1 zxeEO^r`?>|H8$@lWU@I`R@Sm*GP{?YF5w-utW48$jLfanLYOK~2Fq>qJ#I^pz;9Hjhs~ofhbnkWs|-C&Z;mfUU;H#>V+4m29rc%B1@RMbk_aeZ{8{FD=!zT+QfI&KX2_JH~GfXl=9wQZ4;7 zdU%*0BR?e>oB4XqdGYja`eiDP>$xw=EvA`kRMcOD!t?`Rc1}R_e?wQ!{lGE5<4@Ys z(vXWE?viYR0)ZaxcubA!;R!(MQWBf%S%|l*!T6oN2ppH?9JbcYS%}#4;{m&Zz->ps z@iV{ux*G*OGd*fmD-8`b^P}CV)q3~acRb&x1_++rwt*v5!ap=}`O>CIi8kZu*!$UO zc+t7(A`Z_w{U>#{ak?Lms7^AuJO!>0aN-@upkYbe0p;CgnG!5|wO^hn&!6tjLJ|n4 zhm(0ToywGFXIsCV)NMbnIjCWbGO#>mi{3k37L}h)3l6mVqk2A!-r8?}JXu+QM>+K$ zB%FR;&3}H!ZmyCmPwalNgG;ZhXwdAJ_>am%{+ZeYnmz$S2m`6S+K^d_ zHJ)^}%*{<>ClVt{m;bKjz-ZSPh~}L-9VNpD_N-qvUv(vMYx4)RmpRT~0n}}~m2+N?TT?Tk+N_Rd-E+&2AFtm?Qb)zJIW7|N}rXfFoEdAZG^ySuF$jb^N-iKA)M$48jMrA4dqWy zdxhx{F@#}&aA}-vj{R(dNgLjFYEVe&drE`qyE?CJm_6SO->j6( z_tWiK64F6?grHufOVH@oGdIs{>THYYl7=cI!TW`Cp&|`U1Fi;vRAfBn=8L}P1=ni< z>tP!b*GIsdBk_FbZMwgjJ-iYo`@)gnrBPRgE(4nzL?=Y zu|IDC0vEoI!G%_@Z`dnR7i-@O!+iSbegf0ESd~3e^LbB%PGxugs@NTw2}e2-b3qqt z0aJtw=0~li1NT1P`6C7ukH_Bx-vwyMzB!-M(7-{D!1jQ(qy|I!KEPjsy_9FG1cUic z`h_r)Up2tj|MH-di~(i6FAoCROQFom;~U`cV!r@Sn<(GE;<~Hk|H2%6PJROg$uEzv zizJW*@$!&;%=-VwaH$$QNFWtT%%BB+R+ZgVCM+2-MoI_Ae-;7T!pmE@Xg*=q7mR%1 zBm;qjI3RuZlgz>N6ORd@+H|65I8;y?4b&`#;pZWl&sC^fpKgl3>9d zl0kz8cSwSJaEIWogL^`dL4yuDB)GdXNPvl~SF+MgHC1mUXo99Sg{F2bBQo0P4Ho@t!zKYI#4pnefMxD z$k$9#&8hu0nD~p^%epijR)XgLY=;-EQ+{F5@a9}rw%}68%6!KqeZis zo*v#ea(5X?3ha+$sI%jW zfaiwoL>J>{C3V&YPBSV=C|v5}ZJEx68z(de4mE}3(CYM9{k;L*X!jy{SU)OC#v1{N z8U5xKl`6?mClDvyfiHD$Z?qsr$ZoL2UVnIaU)gwMRf{$~d)Az?e3WNayGNOlsPp+U zT%`cLN-S7hcH#V&fKzBV-B1C1Ni@(Ac&rp@8H~<0dpy2xIUUOV?-4_ra?$B+SJ;{9 zK136Auk65BM*7;0>-OB;4vdOg!^|MxFghhS$@t3vzdXV zH*S*pL=tp%Tx-#F6n9Ds4f`D^{qau8Ql}Q(We$NhC#Bo1X9||}$KQDZkXbt$60i}c zdoS0*K~E~!e83d$Y3zYMRfEM{e5!*8X^+)T*o&~~Oucu|wWIy(rLijxpGG{;Z|RB{ z-7G(A!?m`&7Lqn0zK?r*&5Mjv4aia&V#@h;21nRedXC!T&*7vE`JKjBj6QM;cSK=Y z{s_y&_&2J~U^#J)JDA3PbB*tv!Rc;ec>t?s>BKMWewisoYMuI(&3=O|>gIG3*d z1sl@rJUJ^diWzqOPO{okeTqg0AtBBYO1c~X#G$&nOX_2uuc>Cpu`HW~a=o$aZVR-G zwR?4OyXG#}%V&se*aAO#32=p-4BQ37MoPWwGZRS<0|L7oo5hBHaX?;JN-+3AUY!oV z7Q~CA+sm5?J~fUdj1%xWTHv_c*Y~wt7Ef=ZS(Mk(gguQ7hs6p9^**i;7TOlyPQ(u7 zxT`c<;HSAyv7bt|9-=ARt+oExKeEg|vhAmbr)^a3G-L7ksJ#7zE)yXoCto)VlOZiw zI$rPE1ZS9!?|D7amSn2G>JBa_Ef&5S>R#u`6213~n$Oy$FXdW*DWPT1EeBEM*3*Il?NkQnCA`)p z6HGOiNF{RAf-C(r)8Jx^f4de^X5?cxzyWlVSYA|jYe{u*iNp}ED~5m9jqJ4%OFu@v z{L_B!dw>ig-%5NYb#sGN$XB!CZ}8fXZ=3f`U;!0$Q3sW>0>gal?G9)e+PT~K&U9L@S(S+qW7jTp*EQ;y4*^D!Htn>l9vcT- z0poS`|19?|#t68|-Y0`E15)-HFKeT25+t(!=xcX-kO`D`?p@_r#`9#J7?8x$ipT1f zUEpXIo@NsIZOwZJIH~yy!u)}NPr_vjf%6$Pte(j#MSU@^G z_uAN(TOKlSuH8*sUM3ed*}mXrOcxc+Dd}C!>M3VED$Zg0v;9#fHg&3aAB@UlWbybb z<*rW)9DJFblPs=zvPi;mB0wS@VfGOblRAfbX1XmZFX*#OGlbCzpj^Po3wh_HxBx71 z6k$BxYBg>>SF)T|Y0KgVcw^dLG+G)SRd#H67!B7KZTvmZ_T95Qx zlZ1!wZ?~u4jZe8Q*|2&dg@lGV0z6lkF9Qz!PxI~1x$mRdxNHV%%pt7*9= z+%QJ=l$NuT=xy&M=w+m?F};tJQrh(@$>E?;3KGbS7k4^_*ag;G2ww>sl5|BwC^f}* zFZ0H%LNBqF>)+6N4}sHLSp6K$=MxwaBGVsQ?%E^O9N3N5U&e2#h%OJU|HWO;!G!KH zy#$`fF9*1kWOwgNaqy#3K`E5DM(bg6Qn!YAayjX_RL+0rRApI@r4I01)3xa23=CL$ zdpCX=AZW$Tu_o%&o`y7{Bu=|sT*w+8?1Y|!OKdY&RDVfm2HkY(%D;>}9bmRO z6WK;91J=9&*(Kin8H5RhLX9$&Im)elY*0YO{uS(oDhoHVG&3wy%J`E` zF5e8OA(@zX8=SUpF4$y0%V)D3E0!+y!R@GKvuKMYVa}bN{#l;1a3EWF^&_OwY}?Ka zmGMMZSugsTuRAR3P~}w7sPXDEaj#dM6M3@d#7fP&6Yp|7CgYPubRE%_^dr-``IU{@6?%@;X)a0AJ zuLQYpD4EZ8x%OurAEg1`>Js^8#^E9k$GR3rLHbs>Ww&4OWZEtURfLo8-c_`GeKVLU zVI{xkmM&Nv?YVmW4e(9<1Bll6=Dc^(sg8NE>FQ8GSdhyUhZx~Tb!UJ1-oaEpIg=ww z+@*(-(?f5WuR4Q(#7zY6nBPdtAeUBMqX zQ~lYsYtv!bu~7p%`z_wFluK`g{d=2XkOq~?nn!Dn-zmHtTo|7l1cqNm*DtU@J2rOm&1KEc3XHA@v5pY7+~1fp&ES^C~k!*X@@2i#1E*wLWj ztxrq&^wDq|w5j$7Q~SimilDlUYQUGRfubwo;r&K`aF?oCFP4q9cp$^P&N{?q+Gp~v z9{mTp2{wkAI_!LZ?5F;v<8_$lh9-3{(Sg3eUUw6%*h0LTX!+jcql(D;068^Tvn<;C zQX#*C#{kD`)wAl(Qz3oa3dFWSch6eHB^%}W?}a#;2g@fq)W7;2!IQ3&m0Z5Lv&S9H z!n}BUB*OV8=@BanVC~Q^Y_D(b_#_e-+I}@i?tUIiGZl=vM0I?d(H}@}>bOjD*#HD^yB-~MtztA4Qdy-xF1kq?9lf%BrR6T5i* zcSVh5YzC+AzIIgz|1uqCfG7r*8+Ejpn}U8j>Za2$wV?mS3`fO~dtH|}xZE3I3-Y^e zbJ~u%a%<0@OiWT(O4n&vGj$McT*-_Zw~F<1af#Z@PlJlOA{JC|$i3EFJ^Z-sw`gvP z#tQ=d?lqlg47Nfx8joNZ%k@N&l+Vagi2+6}hp+~j0I15irTkg-CF6vdgH3u?`pK_L z5M*8K+GePvOtko-`D=TPz5P_$a^Ogm*Jjz>ui=3*<*8s4 zYQ6PM)mF8jxMUDClU(nOP^Y>5z9V=SD6U!hSOV*K0lRlGQ@Ke}aw=Fn@OH?h6Psld z)t(Zk=rqJdK^6K>S8Uo-oMYlydz`BDv4i2>cb(N($2cuD*^uEsWfe-kz_Z%JX`+R# z=8nb&n)%-4hQs|CrHadQEHb|AyszOL;>dO5?XR@Rn7gVqYt3@@ipBd){hY0X$@)~p zVFWJYM2kE9Z1@-CT#FR6n$Bl)!Uv*?4EweKKUzU)@~~_XSFuWT5jZI#j<2T=Z-BHV<~ZWkh9}MYL`?aZP$Apf*Hz;RRBtl>Ekqm zx+$AJ_4IOMA}SIRC~wJS@#=PWhBgsCo<71Hq04k1DWA%|t!7azPn+Qr9?|8i(a&oqKMiGkJA=DG3Gbz_X1^<>6Ix$;m?` zR@E4bz`NHxf0tzfHvg`87mWXMVfAgTzSyT1w^xj%ymZxWdOryrj;li^-9t~h`oQ@W z&F1lZWTLNm95(Nzhf+jcd!`350X{s^@o61I7bXNDdUNra7#B}A)oVhE*1e1vJjuB< z3-$0)QK4-JoYMx_+JnVP60(%+4!Av?*?hxm!g4)WIfoHEDRom{r z+HemSs@Y+(Q3}-)2gu)zQ%b-0csuf`bw zsNER2o2S|C_{UoCc=5xuR>ZbI!T73q*W*khvO}>NBPz2+5#Uz*MxRxiPkq(e)(g^* zhQL?h)CYHN_>6%@#|y1jncrwHqqUg-jP;L%RP#O z0I{6C$%+O>i7q?WmjjuI+ponArW%rGG*b*Z;98UOW7gQM#q=d;1YI;GZ|-n+;Jq*+ zb-Gs(dAocYniDqBZV`Ai_tiAG@i^$`5Jd<-^;knlm{O+ZAJFfdp7ZBbd0p(Y?z(fi zhQ3+k=k7#7m&%Xs?z^y!Z@dhILEn7O%L>>7nu5c_N&SCMHQt{^HXjYHa%2CkFP3&Ug}0hc!(#FTV`xq^JlwuT966E>2FWg2O^x`6?~~-nwAFq9x(m&3 zve8I+8eH2;tU6KJBNi5Zo5qC8PPUDF4_b?^k^@&~N{0BZ4KQB7##fiymBsr`a)%Jn zmwXP3R_?q=SE`+C_MFW#~y z;NG3s#oKNR`?l zbep`g=NrrG-<_5=Sso+e{GtmA<^w8*7qD`|{9~3Z62rEbMA)& zOT~6Q#8<;2<>m|Gtb?(wd-hm8&el<-umW1MUQc}ltB=Xv4}xU}7NT1XKN$1TaAy0+ z%hcnk4bLekD<28N{jt$nQ>5_LFfj1)di0b;l(Ei+9ioHmiS<1ZY`$3OiK|*~Ve=$FxI*i4O%e^jbCm9B?J57cK2gsM7Seo*=^(mbC*xq~DDBaTE$h6n$@@@|`ZEpwo zmAcAcT_*rZL`>d}D1)-ZeXrwrtfcG`m`pc}?PTzsmM(_5fid_fyEr?bk^uG9Z?^-7 zv7P!e^eZpbAjyn9UR=lB_fardJ8;pV{rJbrDYZ$znZTab&614)4!Cji(f8f_o74z;S<8QDz}R@X4y< zk18;Dk<|aLsyonL74z;x2uA0(Z*}<1IB1fRtI_0)^pr(wACu(okEEkHQcI^Xn)c{C z+?#NLjPNyreF=>NWy4P)BUF;O?TZFYizR#6-rJ%s+vE#%j*k!LZWbLsE+lHGrfNrc zS5m2FI*t9m1RRk=di)c;d7k@|Id-TB$%iuG`2Qc!7Zg&C3i}i)z55ovgWUY6{q&)M zKk8AK^^ZTbksr1H!PGyRF`Xu6@&;u_P|`pT<$ny~t-|W|0KR|QI4Ht;dE$Kesj>Wd z>u{+SjAv7Nnh4doBhvRM$TVnfa9*9vXj#Rn(m`*MR%QqdiX7(O57>l7Au<+uc0q-rKZ`o|ln+>)PdR#)G zRQJ@)EAtnvFYc;I%hG>)INroL>7~a$ee@(Bz}5hyJ5A8kaJ=b!pdP%gn_FSdoM8c{qyIxZGN<9YVFV4rHbq$_;CPG~LSN_v>*&ONwVU%q4R* zN&LL&P7_nZQ#yo9fWqjWpQ=SV!Nf#&t~~2O5L(llOaHZ!&vtRabQ`kr3iov7wJFDU z&aSWz$|Vph$qbv3BFtrr6spq8u!MOm#`G`Y;o1d|bbOA022(D;i%yHJ4Wu74$FHxz z#{cLRI0?+iYKL32C1SzvWSn@OENeKEfu{=9a{isEcu;!ljh{VuQT zkUy|_ggS$xV`y~vTLlGbx5_`m0|=4PyBfZOtNs>At+&!@oS=)P`?$KLcOk9X6(<)iVM1QY-(AgUe;) z3YtW;hDOf5Yut(AOX&8?DZDr24=wd9`_hT;%gpOzK5yghx@GwW@)E0^31zyU$kwMC zyK`OQ&gpSQ7-5D^J6AZWAEGdApP?Pia3v*Lc^3khvxK`HgUJiyG2OJJiC4RG3Dc>j zrH|`3OnsjiH|-!MqQ+Q0ONJvAe z8DLMmaXoF6*Jh`OQ!{fLVHx(9<(VE*oT#kdhyADR#HU?GFS19Dplwk5S$J)Z2TD}K zbGhVnvHG=X24yTi&(nWAT^-oS>f%C+9Lur=C7&^IZ;bzNziU{G%O?qX(hjoPa6^zE|?+iS&|p8FTy!^6Y7u+w=EvFSd#$w<{YU^V{ckMcOP zQbC1NJmrwE2TroE6}7qz{P^)YHXQv?Ig3;Ux0Pk0^+5C`gzyKtEwSkL@OZ`p!e+py z;3F~YbVtlp(&SQ~gV5k(1y?7<^o)Sxxa=f<@{6~!OZth&zrR#^HoMQjdIUhzNre7Q zwClOZN0@}(ZDPeRprm%7>Xtml%3BdoUtC%$w^$eSW$NzOZ_I1384p)xN((c|w z@n|A^bCje?k}q1PF%9w1oCUR)t8%P3P%9a#JjX>j5~3Ll8V)xN3KTWUw?F^8_4!Oo z%jp-bESmF+v#n=n+7D}%^P9jV^|u5oQp80mmqfywrfoApG)_-~WK_sE7~F`^Rye=) z?xE5Ptg&xD1?5jOS!A?O8lyB=Q6KsVYPk$5X~ZN~Dp8?ATRHyA>hoDD3^h6=|HeqW zXw6fp#Npvi&=M4Fb9Mm)JEInrXn z>^yfZSso=Ump{#sb6C`~1)z#VqQR>*0t)SYsQC(+cd(H#`{v-6}u8TV6bI8+}ii>t|DI6Ic#zyqI? zI_^o>r4v=*=WcwhEGWIt`mAe~J}&mj4&ua@_G_&WOBJ9;&DrCoI8#o~O+K`@TdDUM z-ujYxUoD ziP|LL6um%;GLnAVBbWtY^1ySboq-$WpHB=SV~u2o#Ieh`2WM{9IG`_ZOl1T(9Gv7h^UdSdkT6wKUzk%gAh z)Yw-dM1T!k1#!DyHdZV6Yv&{57fJo>FeCyvB-sTK*aQBQ8|_%=aN@35Qm=|B&#W^G z7A%SGq5}!S>snJ*L5Yt-!Of&kKy}>Est)93LWC*AX*6dg^R--l2%V`>!ojf>)LGQS|_A^y1$UYEG9Svrduhp|xOH%vuUA_3bu)9zRS_$%b)o}Tf zt6YFxh3savd1!bz3DwP#@2GBQZcJO2jS)ZMwo-=cXO@zWo+WMZ^mOq&%&;dtb6exy zcU&oO({+e|L+B)WB)cXGs&E}UqIc1DaJ|YGk2~Q<_7ma!B^R+x28kAj2lB?8u%#`$ zV=q8M6KYuPk ztbNOES+39q4V2|yo_Z#!O*Mpqi`!;xeNnz-{6y=H4)gK+`x??ti6wN&Vu!lTwe{Pc zu4?73L--A~h~XvwvrHesWX2c1lbG8MoIknM*y0v8i&kpk!8|%A z+J@`Z?^mW>+VJibiD46pfC`x`XRFQfc#rJ+5k4T^T`fR0p(VKC==}2u()Y|v#X-5= zt8SM{GvJ*xA#Y<$dDki&W+T*kV-K0=oi2`MJXG$aAaN9qW5eA&k<8@~blbJU+4ONc zSe%%)VdsscYm$?~)}M|e@^rEh%wUh)8=cVx6?43ARWTxGv=yQ>&J%H^BU)bAp2_;90B$b)Ldzb?B z?b3NC+L(21OGJhl#ExR^CkBaU=T8Q?*@i5txp90O9QQ~0C866?B!x$7`{ebA-qD+0 zfo7R^H=g}0@r{Nha|Q-~g%ibl)6$5F1Ew_Sd@dGr`S+||iopW>rH$OUlm$J4ir~5< ztRKYblryDAycTANe?2|-yDOqzZIegNo(Fc@IC)yXl8_wWBd`k5m)kn%Hk>(`?$;Y@ z!L|+7kHL&TgDD7w6(*}gB_`iN-dtWiLkrMpr2@cz-eIoP6Pq)iUb;N^1Xq;8|m$ET7(MKDGR z79^aXf)foYUxARwt2MW!f+q09cc0r@zCxKS^ng;mH8ys+8u?Z~sW7~GQU5GvXC)bULi2Fg|ED2UXe z>$_D8j2}r<(SkG#Zhg0_W73^vfVg;O>}X~F<)BAY-2eSGZj0ldPuG!%ATNosbvUVa z=g%AoWe&r9k9Y1B(b^$(P89=!ZUP}7zihLL`fVWYre-kX?!ECjy6zuezY~=R_Eu}# z0|rrdGnIF3in-@23qy`!@n?&3ryz2L7Srxt3rPr_t%={Ic1=RcS2hS{Lrs@}aBF!H zi|bSH+xAq(n#G@uwvnrI?#y@DM0kvgjlJVu>r75fOhsd%qolgH&k9ThYgFA1=qZNj z#sqaS@)f*+eVB3Y^XN}?w3oNH168uJQyxQid1}Dd;8t^-pdB{cxIt?;^uJym? zo$2zNIUHU{*^SR3b8o$YE`lw3-+-ix)U6aX+yq&$Nt!IHv^P(af4nZNzjjr?({+@Hm_0a zT;wh6?ZmMZ4ggRU*jT(`|0TLlp~vPFOD&Mb&gHSpS`hlLmm#?&1?4M!a_8%3ls8qV z1HsX2)WEKgb&8tZ>Bf15+51pH>)yQ)1L{=xFgNFpHu?JClYvM*aXo{TJcaNGCr`a6L(KfT&mL5Gu{XW*QTkU zCyWiQ&Sna?-h{Gn5$v2zmCVL|{y%&H!g)p1EA@pef_SU{=+(*;ZG+ypLbL;1%tn?7 z#JxG#IsMPbWOyrUxJP=j3MXnVdsHnf3o7CZZwJt5=(Uad$aUfX9d8+m8{4Yo3&2vj ztNkrwrKJO_R(MGo%PJVCn9p8AN?wLPV5Omos|90mo|FuEbs@L6R$M)}xR;jfL|Wa5 zzOTM-DK6YG?G2t;eBd=_hFU{wr}bW)GLZ~ixdmKoin&YrU4u4HJ?Di*Y+2GWU zSi4@PBCU((j? zyKfbB06g>~l?tFC)G&>OoqBM4fmW)Sm2rKEiR*W~Z~U<%+@{^0QChPBRK1g^wRQ+& zRbE}B6^WwPi_w%Lg%3#M$yq3;hggZEOY+rjKma4s*(Y=qaXNHd<~?v_>DVQ5jEA2a zoCD!+xI?`uZyAN|92te>KG)!U$txI1@_okQ{wz-mqI2292Gc)@AD*W&VLDah&^fB4w_SkgVLUGQUr^@pgb7O?u-D!Wx*S0o`dbM<^C z?}eq*)Y8I9>GXWo0IOuSEOMGd7kbH59;+n~s@P!ecrkw3m8F^s%??Z>aWEb@X|OSE z{5lBK_ir;FaBhg5ei`6<+;_A_yL-E!m24QsvLwulh56k{?2ital)RNhAjdT^n*`Q$ zbrPt3U;mR}W&~xc5cv=Z@@4TNhU6O6nwcs|nxN^12^+cGq%g1A{r7ZSY)Im?l?mZ_ zdRUDV3H<)WUy1L}E>9f!yA9$sovpoz2r(wVGee_2Ti&L5`6NC{;t)pp;P)O1%%y_; zewC>Bwi94Pbgv$yaf7oXGtp&=FZw-D*@oU)R@5Iz)T~V}9Vp1H;*jFP)U(LG;`#)% zEBo9M&R2!GtjQMi3Q*|h3#2j)N5(1o4~cjb3p7;;X5cvq(Gd$xABRTbLYL_s9nJZ+ z7<R2B#Q%+kwQNJPlQmwBueaymC7 z&scJ-r`$icuYK*>&(JTA^P8K6Lxg#2Zj8hm+-gaiZB{=uNP#H)2$s<#K<5+dpZ+ z^;RMAV)=~9(>L?)Vt!xRSJeK%e%H*4$H^FbQ>Juu(~xknt}0ZL*+nNwC0o*2IX>6rlo8=HNROevx9cCISNvs>H8O&ElO)^F8IGJ+q8yz{s1hcd z$`@<3_m7vFLQF{nhs(~wcN-{X!|4DrMhho9r#?yRsTYo(oa?M2_^p6mIlum@x|<#)2(Ks4AgOu-yQpEEF;9o=leAdM z#ae6RTnYdZPAc{3G7j0vQC@KyjS=OGk|ing(^)%FaT3aKYAOmX)o$pg8qB`5b+5x=xOYCkcd*|~bWn}#c*Odf8$dT!Tf zIdHm3Rhms8?jw~&bm!hRe{k8doXHJmlM$O1Ppq2rR`zrqZ%Vt0CQLqKyU$&I_ia3D zxe0bVd9N;6UzwLRm*vN%PE56+At_lKD2|7x3smkgg1M?gomAxW=;`-o438^~fqsGY zEX=sE`l6e(aM_NbkXl94O~PstKEHYof29d?3xR&EAN(M8YtU#4Zp&6-Bw6fM$j=N`v*tKXvGh-v}?77v($r)Gm|SQ^}~wz3+W= zSi0VTo*KCkp;-zo$hiD(nLmYB&#d?=9S&b$ld2dWZyL@=%N96WIpU9MycO>^X}-*j z)vKLx07Y}oh6G%bI(|c8*(=OGEHIWcPsg3I(n=zNCzl+%Ky%v(B?2sI{79a9K}= zC*NO!$cM^qz{{Yk-#Uf&`KlD(e8k0R%#p(xCl6_PEmfh~h&g77>G==iBVpzXXT|9; z?9p@~F-{L2rA>R>k<-CMjzvl1wRae-TR>nhfvy7Y1MXcZX}9BprgN#aE~$8bmK#-A zDG@mo*ne&jO8{h)YS=3aq?t-j8=)wG2Ue;)ui-WQSU50-Kq#o}7VZL@p1uF&ZVaJ}L>wLu-tP<8d0ZUM;-H;!Rw-z^pr>K3#tZaYnT>Qbh(A zByZ5z9u+Phn}l(8uFC7OzAthJy|$FwXLw1rHNq{zVoBt_dGf;**mgek4B>ISR;sK7 zZDY2@Mf>z5T)56j$k2v+(M`{S&Ox8Y>t~z1>E9BTG$pdxBM4hB`4~jlu)K76ZkhDzx#CT;3PRSq#!OVQ*t^t{ut#A>*`t$*Ad#kZFbhwf z^7O>&l|GXOEEUfEOOMwf8onqQU?_^cyY)Bjkw;l8lXU0o_4l6v%G?F*pWXBtg@<$q zbD9QC{j)`|z(;nzXlT=dr5;E&j-=|BMKroNgrqOAhRvVQICsiQcX3Ut7YwCnSoZT! zob-<`E15czfHdxah--Y>>QhsvP_9ugz1Dv*H4DL=8_**pws@`yON|vfn^#}AWyL4) z4HU;_`()Ujg?@zi%K&X@byfTul$%FMg1LN1Y~Mtm3Ok@PNzP(mD!?_k#rOHFTGWL| z@OXdnou2^5yq+P936cXW|eo*!BVdZgvN|{pUmO z1ZKqRW(+*aq$YY~YQrh_hmD&vP#FlARQ}A&w87_}JX!RTnaWc*WSFJ;UG{7N_kRcb z6!K-HIq`QbzWdgYsjMiPsdFrzN_78rXv9aj-L-<4Au4=Z(auyYzPv6@ua+goTD!5a z?dbWcK1$;Bp}hC{$CIGuP+H80yjW_o}f{>oRV)LM|BHltxwbb)bQ~nPT zp)KzpvKXSu^Ut8@ROUQmcPUL{w}({`yFF0v;Prn2NOo!l7eVvzwI)n^6nu#SOiq40|H<h4bi&|(tMe1Uj@*L;d&+wzpqaUZAeKTQ(7J`f|*~0Of0jM55 zFpYKTb+3E$UZD_klsYjQ+P~?C`ujg8YO3ll5!x$=)BDQ5nhza7D}VVUE$(d-8zSpS&(2ZAjK zMp{7(TyiQ@7!+^Ynjpiz4+7Sl-<(ta75V`|tb`3>X0R8%Mj4?Q=0mLB3i?!SP2gSJ zYU2>8YNQpTQd!R1O8Lbo5)uwY=I8LErIMe`2AGEv)VeNQMuQgnf=9jGF*VKF-g&5< zz*g*`S3x&c0!@CsX{?h!8d^dJ6DD0jcUP;|6!M1i+@vUH3XlI7_Q2x80*q-A-$Je! zKeIKmkU#BGHza%El4*l>+J>x^n1;frs?xCjHR_H`nSHs9L4uGo;OHBlcO*OgT}@vi z#0i&@&&{A<-xx=q`b_GCf%dGqR;i0_4 z*-IEiMi=EZ5H$D10X1$94s{1KoCrRltM8zm40*6S~#Rv9ckeY zVlo>rLM=RKy#GQoCu}!288)PY!rtJxJ;c@>rTiNbv>=S&3pFp&rD%QlPBt^2pm*qM zSx*j0bzu9C@e{g}(iU@b(ZGbofp<|um9+4&LqRP@JkTPdp+ubR&tk%AxMyyz)&2KR z@!NE!qD$oSKBQ)o$E7JXJ$VKQA-Rh7(+)g9I)oPBN|>u6s54h?1s;*0qW6H1R__cn z&{>xEH3&Uh;;Vb`9tpB*8Wc>5N(jF$W}Se4#(JPT!I)@43O`y2spENB926Bn(WLw; z%OntMKI1qZWYm&!S1RVRzUSK*+KTU?ffp}T^!(pUz1dwX+qBEx*G-S-nDf$F;e)Z)^%~oa=mOa(0js-hCXZiIm$N4#4<(V!`%5@`B})rl3Sh~{~`-ZlM4sw`)DQZy_1nM z6<(*XHzZs~ssoA#@hB4ctMSfP+mcHrzGbApS!s`iH*uYf0XZyDRB|p-V6`p;n6(_O2xe8<|8DL{=`NX(vGmjIZ(L6ax`jErKUF zh&{`^Y{@0hZJY{;YvK3grefX(ISysG^?teRc>2s6VTZ~6EwIv{Wc7ag@41H3 z?NH@6sJSmtc+u)D6@yb#b1&+?o8S_YerVf$;RtlS7x1zo` z%xsDi6M0qy7kEy72~+9FVEl@;?Ha3zD1M5nr0*hi#CV+zJG!`1X911rY0o&K(<|Tcm)FiM#A|- zg?Nv^pe_LL;#lHl7%YhLH??T34-T!ZqS9#=tUKYKeK$}!rQ&A0Qt??A)TinkFapp0 zcvBbR-9W>gjY__CHx(sMrAm4!ZHRtZSg;~!rMuUbxqVVbcOJR3(5m^wsY>=EqE=nl zjLYq@VFHEZ1m??^0zMmOb#GM8_Pb6cMMf&4@=Hb7*g8$Cr1>1I8)i9-hl^9B*}?9+ zb7Fj3UYFEVpl~lhb~V=lDog!G*i&Acg$~OaZY;BRX=?G~;Y`fM*mxj%Q^3g~QkSa&~Rgqp9f|KhCTNT z1(LE;3A)iOcGMW55{SZgOn^s{T^9TLHLz?XWDrmC96kiIq`GqXb++We2~02e{fIWw zh|Fp<^GIONa_2Y#u{%*2@?r{4_&#b7{sFT->;#KDnG4cy=+o;vt?UkkvN#cm2oIcgk$zo61v^pC`41@d} z@0FWIim(gTp{!E-t=h*__=iaIBz1HTa@}1Li|`b!gE@Qaer{H?iR7|naAR9xz5Tr{ zYHR)x;nKAmUtc!D` ziN9P z00>n)m;kts+fc=+1AWFr*Xcz#BN&2v&+Nge)cW7NUh-1sSR>3yvT7X^hW7+6(Vz65 zS?e})t8%FKotWM&`7fz0im%6f9Djl|%=;Qjq5}H^*N08{M_q5ZMAd8(ST;#r!-OBn zKb#*R5 zPxQ8A7!#+yLh{lgx0?;1L(W#-!`vkmceqvt)dyGc5$#l4i;M{#Sx^gf<;%sns~f6D z#9vQ-oJf%9*ujSq9CnoV;ivAalc?GVovjHlSeOo<6YqHLEC-e_^eZ@xr&TDkRQ0e( zlx_ZAEV+b*jSc{a8|alWVI9@D#UfcJ9?dJz-itYPXW)ASFkPUP5hF@%cT~@iS^T@E z;Oiz5m%}93DFpZ;XLwKB$J&hU48k7o$^>-DA4Xx^W zEX;GYG$-Y&?d$Xm*m3dl^73juBLeD@lc&169*L?Ul73|;xXzT9`Oq)|BAS#aD9^{o zHVD23TGwnku}e#Kg=7N=(l=GuqJ+h1wW-+Ov8AsMGSyBpd6&))rLnr#JO&0TVB+Du zKwNnNa}Oj7Pfb&P;F~#WgL9OEO7J2T6*PUR&x%GirD-@Vb-*2$fYjQd>MAfpZ_ zZgv`+_VJUNoz55W)+rsahqPr6+I?hY%k)afe{B(AO(VPjL{O+B}?eoRwbf$zpJskh4-P}WFodh&??6J4+V_(@fVKNM0G!QoU+u+i3 zMwr9t7}8b1IT`)o++@{Nwo)^npK+DKZy`$N5idWG`wgp4b1Pjo*uP(Au(k$IUgie_`3u zYO&HmnIumX>8>6+xXGulC^<3nHxU9^bM; zy@wcN?ZappNN*<9WJG?qnZdK=F~K-aT7$J~485}T&~oFsTX@2II=x=jo1T@FAG{Q% zniyhYUtC(9l~#-jdKdV|KRN#%iQ0X_h3%6SS|pjo07Murr{v@P9wNf+ zE@75k1UL7C6{lv9N29KH^}?-84H0t2$u8i zfbr`MMcsD&DcVBR(b(e_1M|fj{rud{n|aSUJO4J}F8n>Tc6V7z8J)|nJEX*QWhj37 zq6Ei40fo`|eA_QB`pnN^EJ@=(nMth!8IT|c3k=hq6kx~6;u)T9LBaT2U&dW_%t8JP5x>PDj1J1*)FfBI~k zKW?*5V9%(WC=E~r4<;-z=54w8Pxq%dZ?zFeM-L@9Qu^*`c-!i^N=S@1y*N3=&r8=Vpey``nbhpX8%>Tzcx?$gi*uX)*6J9?ovyqhn8)pIEZx!Iesswa(C=RB zZ(dXO7!>=nNq+>qAnjkZ|=H{nYuug0|+FpEVh!T4n= zS75)=&r30ZYm1gAS77HigrG3sAyg zpf$>8?sakgFokXd!~NGc{EAt;CcADZc_ZP-_TgXQ8&2%ZF3aDOe%Q*9#n}^p7sR5% zCxlv?NBEr@{LX%HVlV=zPp(1&(0OS=37`4Mo&;nmN_PDb1cIaEFC6ELh%`1{6{=)u zNdGV5-ZCn#=iBopLI@BbxI-Yg26sq;ySpX0yL%u6hv4pRO>k`p?(Xg`jW^m%H@|!T zb7!rYweH-Rd7fJRqF0|jbxzf;u2a1~`@4(Xd_sd9L-b4Oq)V!twVJf&lPL|>wfkd# zKVosQWDpwheJCX#k#%5in6grARW+MO(GC62QdE@-QdBgX`Bk6gV%zUAKAk=|-+MOB zGUn~?P;A6xNCcOd)hXxs3Y5usoy(XtGA8E2qHIRX|9)+~KN3~tpHgxtG4DuX7qC3x zF0Lq<5_I(_iJz{$eBd*nopk1Ld`J6=S&&vI?4_)6c1d}Q`Ruk14O^6s!^KadA9*R| zwkPEdswWRcF~JhSscy4=J3H^GH(063H`;q*3zr+uaKt1E6GQ%TybO~lY0~Pk3C`Ou zrejL`p7%Mu>DFn8Oj%1#<%6u4oXGE~h^v7WqfAerk9U(IMJPH;?mXUk-D*(6prB=C z{lCWoRI(A5h*Wt|RXJ>Q1-lhppB`|@7oZUF+l-fQB60cHb%i)10Pvx{k;M2T{+sJk zgplBUN0U|lS+yqMX@{A`P@{dnit0)ev9Uf+r1Yo!?dy1YIi9JIgy2s64Urt<&%WB) z>1=A+w+s5$_3~7xX%~HK1GBU0k_I|*HvScP1LJm0CNsau?6={WSZKM#eeb>@Q{@?U}gG+uFsgrI!ElzCb?HtsNLGqBzvw&z=$<7}$M0%FfQdy>*H5 z=If8Y-SW0v^V$0Z92ifV0#2z7?w4f4fRow%K+5H9#M&S8f{_f0pu zP;7|@$DYL#Pjo617R2BgF4@*Y^s}|7vSmQBH>qrc2 z!Up87b+0MOO$G>(a4bg9><{e3KNTELGK-q=Z5-d;X$&|G7J2$dPDV7Z?%&R2aj_al z)rB|pI)*cue%@Z$B&`tiX^)jjXTR%NACG;q+K@4=>hp2^iK$GDI>63>P0xS&WB*<* z!&6yox`acNL{7xt_-`KNMTC2W6_X&B-sEQ~mVEkX_x z`Ib>Di#?Dr)`~P(|R};%f#bld|PSWbo!?SC(`u z0v$ciWe06YKeC?C-ELI(;1vfArDdejAmA)>rUeIYEj-#*wB>YqV#aQKT<>cP`)aHS z000DBZSyJ(#owc|SXes~^SZGU2(aNb75B3kNjyzVlthLRaQPVZ>c{QhOcX0`bFFX# z@*AgjFTgk2ces_Y_iS=5503h)wk|23z1O}C{9vlS)n;J!GHJ9^yk{jn2_aynZeJKi zN{i#LzN->ZWgth!0thu2>j#L0OVmUHuOh#*M$?F-N^@BOT3+^QH>L4>>D~VgoJD&Z zL^284KXll6&1?%cM=+Sl<1;mZZlh;TH(vw!sZ3?kpHcgXORu}}Z_e;GVSSwb2&v7( zLX!1V-a47*C4d;D@XtpwK=$E8-Ec(J`7Bf0YpeLzl$N&J4xg(5*$7*?0$xho;;zuW4``^i8%&AN^i*{Xs53lVaBnD1qbH$JLpiaC!+HpMP zQY=BLu%0+z5fU7LSSFFjy}js1vB&)hbIPa-FY90yEsreWDzjZxS20-D+E+;e-{+#r zs@-aO2K3YyV1}go>2QuxnKj2plksja>V#+9WxTp^h}{nI^EA>Lg;tqbGwhn=X+W8T zEr+&R<#}2fvw7Lei<7mh+6ShV{|jN@87c7zJ>aEA+mJJX}nvA zlE-bXHQWn7krCBOSEkoCMOa+Zb&0-X z)*xb<5+ZXEgF_#whFP$!=?pq|BiVT${5eME)M|iP!wE)C95+VR&~VR#SE+Oy0lh1~ zbK`SbL%n+sWCIH1B$NXmPKu=H1o!a;={onea{a{zG)}hK5$WPf%FPC|3xX#=1WfRC zQ1_ld-@0lK*fX2WL?Rry9=rZ&ExpK~ilM9U zK)3Lb6){B8L*3dh3}5oB%;%{x=JP_0_T5}0LX1`P%vVi`Kjw=TU(bY1jCfpa8Nhbr zEcP2qT}{?}(!%C!YDUvX7jC-kO;y647F|d9$|`})^`dWvh;0?n$Q$?VFDzy%>hL`_< zJDrROMc0)iVm;PAhC@K4SMqB|3xh7j56Fm|hO(NCiLr8}5uzP*+Wrg9tV2VH34Wc1 zBShE?tIIy3)xYPcyEWardL;{0J+-tA7p7kKD>B5=anS2pD#$0{*b%L&6DwR3cT#q~ zz(Khswr|)vJ7Cn98;Y57;wB;tb4HBflUZlB2TYC3(6CW0blHhewXKq>VSbJEQIdU@1lt1hZ2(rzk zwHx3AM&Q=&vg)Y1x=n}D1yv}25`@;1ScU#>oX_O@KJv3pZT`^gqR(Jeq@9qM+w+Ga zZn40h#oT>T>RL64uR8wi%DeH40|SP!g+qt9&|QIfHMD2hEw+|4GZGa3uG1ty7UKl6H?xWr735||rPYzF9MtJXCV7{N0buHSH zx3sqX35=Ax5DW|qS%7lpE>{{KNWIDx6J7E2)U;eg=U z`DD9Wm5u;uEUlt}AA7MXNn zD-Fv*=$QuBtDd2NcfVSB$qneLA~>C83y&vosNIpgTT8x0siDHzVXs4~WVPd%USdI) z@K9J|Uw4KR|HbTKX&h20#r(#iqjkW~I*sxG*)@GdbIlpY4W47LfieHP?qDM0jORd< z8T6zvRPYf@boVy?i%hclWS8X~gS1>TZFF$nZ!2QU0gdP~`Prs0FMZBjqHPY)yy1)D zMRif&H)899;vDFd3gCpz>TnIg8lUNWPMms88-@-Atsq>u^zhpYXprSVsP%N%#$2IS zf|Y@PrN!L-S6a;T=co+VaisP7I zS7d21Pu#`Zci733ZC2mc`i%dhfOWLe)qRpF9~s&@7?NmcJuA>{FVOvxKL|YoE*#cd zu=0Q009Yme7yILXS)Ga$E?m~9ipA1uN?P6LoBwRP>+I?2bPElX0naL8lUrVb7+l(mDUDnL_El0M2Cj*+SVGFQ?|%tsu@TQd_PnU@_&X<#tNDs0pz`SN zpguQ+_Ktt!ydgDSJ;Y%12l6h|Zhm2(SD`@`pT+qVTnvU60ON7N#YsT3?Zf6(jmh7v zoSLE=eU13>rFCt0v%<2mVkpY7M`g>W>(RopIzLKazg3^--&f+Vpwg(t@ALGLW4dY=z^Z@pD2|sxYfR0ms?uD!(&9wTgl}6cZ1dyA&u6L4e&*;270hW#*MC z^yYQ$pC=K!3@f4YOXa77mXVaJQp=f&)-rh5QLg8sv&2st2GiSD#rz!!bS4 zrtdIXyK(ipw3JA$zHK)JPC?ZDk`60w5&b(m!r)HTen)9d#75jFU7GeVbZNL?J{Kk8( zn+7Dw!88YEUi_o`(*y}<()uZ7)9C%V8;ckNP4U7i21fwH%4kG$+gA55*Aui(xq#Tyse% zGXj=lKfe0zHen1M9d;oNLti=cAFd|pDy#^6?lI{Lumqa(`#G;$ZCCe7Ys4dTsnF`; zk3D?#%ju;IHjczIlelP}j+O5S6PB!2o+c*UZ6<>~ecTT7Le;P9oEHG*jysH`cot@> z$dz&XuCifX*NLW~uNHi`1YDkuehFEZ9AcPOpvq*NzS^D8VKo)sNK4`6inkpzVT@N7 zu%Ja$-cZ_&Vtscw-6~2{{lcaL_-BUmi&yKUMq+inF!=o)lC^X<1=B^-l%Lh?XRo&mgKLhSJ zq~CBcmr1(=?v7aE->35T*OJSkic5$d5F?aj)_;;SlDQUe$rTFxNwWzNyI4ir*XAY_ zEI->VJ~((^th%IW=uW9WTXN*1wBw)+IoMsbLM)9n6@o0M24#92;-TnzStsa2+jjbE!_3C|P;f2=mlQn<}m*ee%5s+6CHE&s&I!Tv1Axvy~-wh6G#6cu83X4nMp8$kaH`UMA8E#-Pk6F=FQk5s?%g?`db2;f}2y^7~|8!^G{H z#t?&%0_tmSEpBs^g!|rI{#<}p4h0@1`TRx4&cX4n5H3gMT;0RQZYmFpy@E@Rax7b* zE=P0aMbi*;ZNQ|O2hs4k&;1Ah}@HrDG(O@xYp zNYR%^y!qJOdD(**(z_5GEE4UZowNHD@*}U>J`Rm^X}`8?JknG2a1>H};h>}qY|~q_ zg3t&?!IPw7GOQ#$l*OXQipyHx&eL4UBa3W&ZC~%&Ex~6opBPG@7N>lu`CPt5(6PKN zjuZl&cC4V$FcF^(?#8~9i_g|ebmb4UU~ZH*N@^eP?tb%^VsuLb%q23QuN_cdr*mt1 z?p+D&=FmnbjbC|=k1g?Wfrve2nYYidjfW6T>r*mxNpJhnn2vOec21Mfm+HqGEYe5v zP={|t_j(k^e}!O7;|sY>DkOXyPT5T7X1g9itnIjeTf9A2qJAyeNrIp5vV(6z#Nuss zyqlg={G0#Y6Wo%t}DF!J^EO$JQM%Qsy~7;@Pc*&t}d41mQ?}f*Y z&HQ4?%|Xj+->Z3Yrg*h!$VdCVbnn`GsZQYWr%gKOM0r}fUqv0IM0}qmM@8N?C|Xj6 z$}DyLFv^8^g@Mj;c;ng1W~yMIljqi%%13ByQmfTrBY*$B;mH1WbA$0{ufIHVIt|sq zAc2M61X+89e}jnsU?@i`73lrO@5#?x<*zb$!8_dK{ z4FmP^gz4xq+J+_6<^xVHW@v=+B2{~D#d??V=$8OU{S=s_wfrgXnJ>!>1jTbU|+BMzg!u&iwl0=FNK9N1UyXpf3zy^zHRoo?}&!OouX?( z>V0uIbVpH9TGknae);-{N*5;=bzg-j;rmPt$BaEw?ajs;d@C&C#2i`%SCVfbXcVP_ zB^=?L2rl&w<7|8-SU%-f`?O`?lwHQ31qh~jU9CAejI}_5qElb-gyUO zp3Oz(8dvByB%Dpu*@?BteaM>}godcB9iqqDwp`Q+-hv*0b@vhIU{&G)zKgMV@CfKa z!uX_Z4H9&6CG;dV4NvT^L&c-w>m-C8BB|WQGGDtMPcAf@;jIZ+y&K6emL|PzeSbcs zR>%9YJ!|6CZ>EYOk6jy|-QyH}9G!)GheH>><;lC`8Hx5vxkq}ECpYZqJiQCdi%?J_ z1f=+Vb(IlhYlVq~c^ z_CAS>?Bg&Uf=zB4qkG#nHG`p3xH`7%X9w%QoH0L`mtPCV=O98Ft?VqnZ z$caqsmov9D^mZq68urwa09_A|eOHeaTI~EX{=V}7(n>1dPFNBGOD=Dt_*)tvU{aSS zpr3DhB4iV*Sk0R!6hML-D*(n{PRo7w=>`MIO${|V?UmDQ@pLKM@q3g*3~$s z;}y~v@@Qh~{XtfC!hw&=OyWlqIEFeF6{6RzEw!6{E69l96p|OLnj#`W8cJTO^kt9Vei{^yGj+x4QDxP$lRX97T*; z#`zJ0cfH6{V*EH^>C0V?v@_;~{g2(GZI`{vk3BN!k9+dH<-Y5Wh9uqKH@K6xP0Hgq z7A%JdZ=H^g#B-|glz=}7IUk*(KE|b;DFLpBK2;?>6>(Y1)J=`<)5g&(>@cxX zF2&{8^J?jc*qbB9a-^K*Wg~~YR{TS-QB8kT*sLpOn$yi57v%DqX}gVjhsw;7Xnmyt zNp~5xXo;tDpU?$I*=jsSS#xAFuUfGvkKB-b?T?m9i0RQL;KzvOHNF|U*?BPGq021N zn9tQ7dO9n&+T3@n!&c|)&XT_(To$(NyL0EbZ>owVi-=JCmZ0dz zGFM6Dv`(TiW810< zE)IjPJi_0dhW_k{+*b)7ZfPfFkhM~|cKeBc&wO`nFbIe*Jx@1vnNxb~$2d&04?(D} zFRJQAHQX-M+Ctrr>*^vCR?;ffj$3H6oHSYJS;2KmvsrqK{7UK9Av2$AZ3uGQncRL? z)N=gy@X0&tktIxMoYT+?kYCl8D~BJGTj#qT;S2`)4gS#V06)wm*3qB%&RU=n;n*bY zv^gdlH8PUd?4uE@PlFVMP9N7o5mcxV;H#4lHkT6}3@$@s527+do6QXlvWFvLjMvs` zW)@6t$;FRkOvl7uSz{vzIh$3Hf-;8y{!Rv6O4l zqO2!=+Nd2ZspvqKY>o3t19JwPfj!|zI7nORAfuS=okElKdRBy^ohi$r=YRZK*`?IT z$qi=4az}5gav11-KmEC7#>OfN_GB8r_^VH3;MY&i#lFXRb)t(9+bd^ryGcWC5QIwM zqw)LZL}YeyP+*;=SOVx>%EcRuS{4(bP%ZPluI;4E^qvAi5HizB43s8UR6)~HUBO*P z?yFDJD#;;q4vAh(9zh26_pGQ%gidLw^YmC2q;a#5eL{pG;Lvj6?rB{>si1VjVI}Em zj@re{N6QtaFbvQgPyug}0!`cHaBRodry)0)&2gj!zH_rNZ(KTGyN={feI_IJWbbIo zygu>A@`B^JUhR?=+@t!}hjGoeX4eq&iFpm2LZc5*6B7=n?P#Io+geWD_tk0Q`Gic@5nfYNn+k%t-xbZgGLJkZ0 zn7;f$Te$3k7iUr1D3kA_p{U_49an|pjbXr5koH3Fx>RpUXTjE-A{Vag8S*=T`r*h3 z;f1#b_Puw)ZTW&tLiH~RV`+CEo0y^CoOa+gw*2(35ier+LA0H1K`9YaU&UVy7T0rR zh7=gdR<7JnHO;quwNut(+J7#~qNQe`+hD~B*o}qw1JxT8b6IFT*Rl?(Cg_)#bf~K^ zJ-YD&57cTWC@=N!>1VQLv4Ux=7l6*BD*z|#(CznD(_di=s< zBI9ExvOIvofq{-biRXm%PE0~*hwuWrZ3xfW0TJ;?;bh?I*pUw?IO?4+sAak4s=O0; z>C**wdUbV)&Ik3ZWvL%jk5#+9vlS|5jK^2=De6I#f;!6Dg{McR$Yf0X3U$u2iBe7@Ph7@ z4U-51RUX9#SF!G100*ft3<=^pdX1wsKtzpi;D9MqmJe05uar<^wT0|lZl+}qi{O=e zO}hi5te2gyNlF=PPQ$}fqE?ulimMZSnjdk~0^?Y!Pxg8@FjTNp1wlIn~Kw%3T;o%bOi{a^<-26BZ zMKvDiAGzVyl%NhnxGaR)n=Xa$WG?R{792y1ac?K(tK=VKOPVdcg08p3$$Ay)Z)?EmNVJB#OVj!o>?PmY zTa~}OJBIg@h6yLes$0nNMBw%w^9E!yRqBLF`aRnUHl-;^Ta>$D16-UbY>!;U z6KB#s9Qh)9mdU_16s-Fp!aSQ$_kYl{A33Z#fF?^Y*~NHVg=^$;k0Y2PWE@>;clXW=+}!N|D_eba$!o?|D`a+clMtH3># z%?=_?0rR=@{X-11B^82F$kQ;R`XU9G$S0^Q7f_o*`gFGrU2wk8e-bMo3h*hZ^U;;A ze{uCvvf9c)fjnm+Dn)WVlN{<^SAc`5EB}hxB`eR8c7M~q3>WCkK4Tar@}uAbrCJJ%j|oj5vdbT>ntP8+wB+jyVGHj zOnoN3SY*m$Ojx8msAWG4gQ1a%mVv9x_O=Q?)s>qW7{4`Ncil|h80ikA{1GMx1qyZ% zU!F6Xa=S*a>TU^v3$bD>W;vhukjtQ(9&Tn{%a%WlOh7LKcyR-SUI$kGBSL|B?qTI0 z?gxAKpTK5zk4WtCDOKX7P9{*4*IndfqI|vgS-JR`{=vFar}tr7`!YncX$k^$=!cHy zU(ZsT+z!^$zyRX!e{d2Orp?m&5cQ^Cqxs~CY7>rvSXS{=@{>!0eb9feXSE}O5Bge& zV}k7*`iv1^_xQhcx3Clb|6*;*w7PJ9F+CNx%;e{gUj2Bcf3iW>Uo-&)n!KQfx3IQR zX?p(9Z-ogu^ONWbia|A-u;TY$gtxGB{}V=peeM5Jx6tLow9E=Ij3Y@Y7A)GA0<%A7 zF>4kD{((EtU;LYAXnZw@V>&bTW7Z!oQlm=y2a7-_~VZjo_XP@UoI4qmRl*tPWu5;omKt6`D!UJ}HF_!0v;B4GobP@kBrZk*R#J9%0q{^Qw;P&+yrjjO_3X~z*9Sg&zyT9&L zkA0K!kEIv9P&f$CY`d$%f$3P6pV=w2ci{esBLL{@$&Z<^l}rmXbE%ZVrw;F3y@uKo z*0|@q-a2hZE>ZJ&Y{{1b3!(jaT&Yej&4uFHFBGJp6tJ-rhYm{8b&&0rVG~JPnvIaE zqD4u*Z`)ReW%nVXnmSLKjqfPOi>wc_C13V2;MNFWk9)XTj>5&LCFt}rMhF{hmkiZ% zoR-EBq5Az|A6a%#kXPO_twM?5xL_Q3OP3J#Lj=&m9v=cU{S^4M=kd1cxf_Udl7${C=%sBkSB2s29Jp71Q`Yr+Zt#j0?ky{~aCjU>WXxfp%1b z0m;~@N)KuE<=XMP)*?*8^q)Ehq@z!|#QOwqjF1JWcRL-obXVoweYg&9VtA=<(hCLH zehozP9{0A@0{Sug@POOu06vrMcY@Op^)hB)1uC5?SaaE$vzdn^>m=$kK<}Vi`IiqD z(@u0qa86`T??swk`S;sDm-+o##L+}3Crs9EKu#=jjM%#t&)kTwRTK(cyFSfX-#r;1 z;A+`SAA1k^@Zd}EC<%9N zULcW4JyO&`CcZpnKWQq*Mn>PO3){M%)BRAObu!#34|ERSUAjCC6?DF`*5#64($@b2 zvCUk3WSv9-UhHzo^PW#6%!J>%M%s9R8`b45F5#mD-5jT@NmbH-4!WOGYOzTl_d%Y( z-2_Ss*;Z?p%j#VpUplkYhrL{Bv2>s24|cUSZ&jnk1szb!74My@6s)~&(eCcvB6>fx zc{0~ni*`_%Zbd#_L{6R8e6L)Stfenmc+gK6^X2|kF6niQ_mO-$;$+u!Kf_IoQ2EaL zq50<2Jfdwcw*(m@+RjejtAA}L(3y5rHB+Df!yuNrQ7x~3{VnuQ>!#n;1x`;$cz5_r^=BW{`U_Z{*$1LJkPOsxC2kZ7825L&esiR=lo2(|zRFSa-ciy4!KGHRHhx zXFmo%^ODdVpdw=%C7x0d>srbuNq3UajejYFZSg!7Pf8 z?R2U5g~fjz{K1Bnp{_dG%&?LAEsp@sVw&s0?T(|ZV)MhVH20H~Osi{7={&Y>0l~^u zwEPJn_O4)>of@Ia-tmq=pMX07z6rHIqv~t&_}2LXs2DN@_9aPF^D#bj8qy4G?Rd^p z(7$BnJC~}n-R#IvvB-!EiIb*WYAVf+m4`u8ZeZ-pw?<#D5w?Q?YAJt3@)!YTE5M2m z#IlqMXE8zl!HbQW3Uy$_&BCGJPpJvmc;3Adkt_VRvJ&FSmzv`7$eCRTea7gpo-he1 zNr;Fjnuf*RS-#Hs)(HVaHYR1gb5`ezH9Bf+pS}$h3$-;Yjw_i`lVe=c?w=_+Mn@q_ z2E6GGT`3KDueiCs+1eXqTtrX~est3tB=pr<{l>okvbG&no6>}Ue@>~YwrezG;rxOC zH*!c3JFUS3)Y%iTpenC{Cc2IIwBO)bcO69-IF$PJ2S2CrkD6QJ;e#g$4Ms7LXRrDr zY27k3t}ix@sUgUY?CJBwJoi8l+R3HXj9D)*=gRbH%AKdUd_|wxj?)(_v-f4xFd~kv zjL%GeaDQ$=-Gf!!T_HTVefFB&hU0X261%y{U~Ddik5-o2IhDk+mw4A@c7RqCz&1Kq zI^LbSOWQHFZeet^{dK(7DYvfrqb7nMbN4$c?=h&`e)_1Y9aKwx{TC*DYp4+OxuwtyYIt z)hrxlqZV~i2bh=k<&|f6SAZ%d86G>xX%Oi+a+HkxZ(7W^zk{7e4u)1ZgU+%(dGg$A632C6iUnB)s*Mi_&Zh%-+{!6bW(WEG6%?DGRLtlx&<<$DN7%t1;JoT-oAJ&r!JNb0 za)B#w>F_$mp^QNXXgj&%CrnkKWRAdi8gY;)>) z4A&lM=mF!o({;mlxz`I?mDocZoA`E(zQpS}HAGjG1~^tkv`SMrAGY2SAV>+T{`zbsgzP~d!1Q`kj45iONt zOe`3lMg5IyzY5P;qHyESyXu1S%1FoJZd|UuZ@GI7 zShC+6FW{uTK4@MkmVd9|`O_SaS-GysIium6r>M`%;FB_PrCCTa;yBp&=UqQ8P3&eB zid9>t#3vz6(>>v|;9eIZM$gBN+VI4>K(-C%m%3khRb`2|F25V*4W4o(#EgT*b1_)A zKielY$aK|6n}lnu(?d61UQA-U0p;f73Y8ZTC*9u*X6vF)PPwV+>plh zpAnE;dZp;s&Y&NbFGl%76FvQ%?FxUy6=0vfKP$b>IH`vpFTlJ{T_7EZ&wSbjXR=uj z%q^0*yZyMomBoE0V2~EO#lW*iL7o!}g3MMlGQ^q@{8{5eV|0bMs3JRO{J60Eevum? zo5YaVN>$S1CZ@WtHf!-(d~rx;av+DPvowi)o7#F_kDMF_mn~yV_twDkCDgHMb-OG< zu32s4nAUb7&6)0&BTW5sl3C-jO7eGr&zX*2?6V@&8RFXyQ-o^b(`L%?d5g2_3aX8S zNbmx$wsFA?+><6lH#50lIq!8)GjS*`>e$Dvopk00ufidoNebBp(D?l>Cj|7Xhl%jP zHBzs3)|%!}`pGg}{}cXh*W5h*!f4V}im&^waM)a8IY~MdW=LMagtAWzRzmoC-T323lbxyi;fhPp1!2cA%Ur-QPq@*nKg zat;i!nc* z(8?fOoy|XS?9jzz&C-*9tz`rn)aHvk0{;Fh5wj(&6=EQcB%dB96UB65dRX&%usk;g zL#xzR6Dz~1GizO6u+40u={kS(73sVbcl5|I$Dy#72v>ZI+Q1}ie)r6YKec~x)LN=_z>wOz zzEH#6J+(LKJDCL;dBk+id4kTgII|3C>n4)ninXt>*?*@0GSJ!Y2@+lT1L&k zldm;ri+Hs|p!9}zN|1(OOyz)i6~ku9o(FeegIeJ7Chc8xGg|A##IKa=09_Ju9*-KA z`8}f_1o_BZH{pPTV^2`0wn`)(ifn?25@9SQqiLjAJTRnEq`#*xnx-3h$^zF@IMIMB z2xqyRWSRJf#JFR+NsJe#SbF~|AeWTimCpPWMcMWi5}u;!tEs*)$aDhoS}rz2u1&gn z^_th^`s29fyW*A4C{X!2PpVH-FS^u-8?CzM4)5N7mDcG@Bq|Qcj?Hj)B-SBycPQE> zQVg-DP;jU&>P`zv&f;h<&^EFv?u`kfpJkdMt0H)#U?mzU+z;iLN!{(Yyf!e1E42!T z{zV5!!zg$?uC`0LPCV!#G$NXp($!1KQZIWk#pIH`?iUZ@&XkvYO6Z+WDrBUI{nkgqqoG_sPIgP)4VX7XAf%UvFE{MD z;z@s6R~3LnBh;4qS_xo5lM~FW9a%ksPU_>jgPqoU)!+0I)FODy11<(OOMvkYrXPh< zuWR&O546+D?v2op*&d4}TW{5t3z?wOR~FX4^cVzC>Fe23wQJ^?ecbNCUy!Lu9j^Jb zacNUu-I6D0xyKK&ATZ(QXE|=#Em;Awt*yMY8qBXql73@QQ8(V7_8y?CW&(qz8fZd_ z-WtSr(L%aK&SmvyJNo$WX)=gW`DC7Wc!Xn=jjd91^RfLr569Uzlj&55oGU&fIFpal z{AtF8)^tcgt+7gXKS54XhJc^DHn&c-O8ruNp&I{8Q zy2E}0d|=?spto)md>A9smV-EtIfi)T!J<;LoXOpdBi9X(jB@#H634@~{{4h&*W#*^9CI09QeZ_#Jr1q|0_W$txQm?wpmL*;T`kz;Bz^ z0g{;K^@+z1V}#&zPlwIG^O=>FnEsKGDF0U^#2dG__7%cp0o-o2BV2;1W0DH>sCl>}95+$n4|2rX>I8fR}ZH>V63DCxhl-5Xv# z+J(h;Hg*lU)9>tP1r)CmH~9xk0JQFdd`bC19$`Z%T;6AU6QPEp-i5xmhXD~%DmWT{ zZ0=JDEsQo~p)pDhtH9O!VzsrAj%nu1w`{_YIMItm?NEZ30XE*>YziWi`W|YV^|h68 z$WG>(JuX7;Tfw<}uXmWW?+>!|JPjsx6FyO!Et;1fNG*~b7kIC1v&BCWssN<0W$@XG zAHg3(f{Eu6YnndoM}z?M8NfetDWXjdO%}7?+OtBuW6y_^uahan@WJ&OzP>6-!I;Wo z5v~sw+_RP%(yAS{k;NX8d0~%HX&PXkHUv^gKYK|3^)mgc)$b7W5T1*+$7*vVOiG8$ zo4sS}VGC6of=|qQMaZ>k#q7Qc`m^=61fM{3T$vdKlBt(tx*r)|A{q=S)|?CzqXuG( zuCA4an=AiX@#+0t2UfGS+?2@_bYE}KVVvFj+D%wTVk4Bip4W~{l4;{`hfPuhx$HE! z=Xt9C`Y?6h;|f?(iEZ0Tjm=X_WY~W#-d=Z=pJ53`8+qp-=R;AdhK_Zdu-XN_kN2F9 zC0WVw-jzIjL3+lunK^7Pcv|GCqp|d~dlN6N+WHL?$d$^hmRau+oE$bvc(i>zn(}E9 ztbeyx#m|DFNTYDOf4J+T36#Hid{t|uaYu@4WWlY-P*d1)mpQzfCXc`9UFN(p6waqj zr{3hA+eu1j#67n%&GAKW0f)1&E3%={Oishh2{`6vv6N)%EPGTxpP)|2^eJ6cB!=;7 z-@PB{W9S=Kbv1$c_q>g*$TOg!QQz=jxn(EXZeCO6Ear87*n`f{cai`1hQ*Q%A zi^WJvOUoW4B`=$7TDTDoV+Fn*6Tk%rJG|M6$2w{4VM}j3Ar*1j0~7+zyw<6rW9R4r ze{>eJ6`Oyq&~J{Y!jc+GW%~GT9yCt@zH-Q9R)15JQjj#hpRuvsq`kWjt~TXo zZ@Ak=iC{%XXN%CO`S7rRZ;Weh#~&+)>n+)QG2AK0irTo#bg`q3R(Ex0_Sz2ncr38h zT((gP({Nt++&6RcJ`=^6G7;>1SypH*)__nkH9qNiWCl|;!8H`0DIEXeKzYPr8b0y> zyiT(rC#OPkxEWezuHnTw%iv<2G?h=~qZU59=@5H$$bU!-+yk%w)Pig9diL8*64;xrBj3}d#ouRT zrd*tam6Q8w%0UV&|?t)@+7Mx*pZwB zCCIQ4ggx-sTEsZgMIeyVZGzDRKH9D{6PdslxyG-jgl{-&!T645zOO02Setnz)qf+k zb$7z{3kOtJ==tW%#HcwBv$L zB`*puzf)KgpK|1 z15d#dLPZG7vQCP#j-}@RxKFoH$S7Za22rcq-u!7gR2;4odfYDqued{AqmQ4+AB>Pj z1(noSl%GJP&YFa^3#hy{jL?RVyPh9ZC<#)csaeQbxTN~bglsh;V z^eClA%lhVv8}HbE33@WBz?P5eS<-*x!kQcdiHtg zQ#Gd|QoU%!ISkocdOX*pFLG{`iV+u5wEoD+3mW<2FZV5cs8OtwU3W6&+VN!xry^#R zI5#zkB2lxBNS^e#%ZV#y^nDVDfDu~9XXXHyH`~rUMzXJkIzISo#QL=^6S>8z$9SD` zKh3pf!Vo?#Mf>mK*}}$v;q{YOc7c<(vk#`TU|42JH~?(8!u`$fevsW=1k2mVXczSxC6B*CtEH%YMp&{9#;MhQGOu>> z<=ga+wLTNS@NBGJtF;^Cto@f}u`y7)4*GH~(S_|koAvLz?EN1%0M=v$QkeZ<<^NrE z)WuLHd557qG0g-u0kj3rG?LdlPr0F(xaqJs?T0&=Y4buL>=@joOFz$}htu7^(iQ)2 z*#uU@{})aApN$wvi|2t{(7K-S zEh~1ivf87=l!7a9v9T)Jho3(~pHesPo*T|pF~wdwOL%=7Ai#^8DYSR>OL?*k`W8BH z4xf?MQVWAg?%;NmXgo=}G($;)4us@6g|6MJ&(A4z*)$&hkV2gN3Ul07`4FPGZ+D5b z`_hFR*ZbniW{8D6fubkTFQL}3(Rd!zriki9-jK$WIrtUyAn)y!Al5X-@8#qAkO?7a zt%=^p+4?&V0%0AOW1ox9KctK28?GA9&sI}ihQnZ}5%Lde6kPo=E971{(sF~K>~NW1 z?h)a-3TOkaj#mo=z%KQV{5x@!e;q>E7&EO?eUJ8H?ASdyOxX325fT`KIyX&s)1j%~^6Q(F!W7nyM z_RiWxsQaT4YslT!mI|=!H^%vYW9_Yj+6wyqQK}S*6t@B;c#FH!LXhH8T#LIq1zIR> z#T{C-xVwbnPH|5t6o*hGc*yV2KF_@~ckUnWJM-Jg3<=qDcC+W4wU2ydjw0`Rx8R>1 zcYiKN2;fGUWYeT3*rnN#pWP??jf}Vi3uUVdXU0P(obIyF|9#xq5>MwFj!^O6C}HU|R}BOB zVI-7##n7h9g=no>D&Pzm=!rNOwI(&dLJu9R(Ro2|hEXq$($*!We z=hQmkfyrOEJhzFbI{F}E`VM^by}Qs(>aML12BOilB;XzxYq@wz^oofsHb& zX%>Cq+UGS7*#aXWN?!VzJxwE84&!n-;O?mEu)~DzB$UmQVK5yTNz|a9z!H8x;ac_j z3__ViDBTS7;|c|T-W(Mys{bYN&#(0)2>X!_`|X;&Nl)oByk6ZB>iG#yi_PD#(`dom zOwwQxcx;3X%noPxWdI*PfK>tbruoDxi>%zZi~3A!p1u$7e^7Mw0wAW$SAKT=Z%zii zDGh=kfTa`;ON~SEOAgY?w5fvVtx%PouW(-x!Yn&aYF*?sv*xL$MzV!&mQ0Z6Voa>D zPH#-x+!w1X8JThS;iu2y)&fjjf1D^y>Zk=YoBh#R!V#Tvo**$=(*DzS;)Zb_Pu%BX zfL|FbfU)Dpm6Ohydl*9tv5hg9qP7#D@X$v?^HGTxwBR0`69DY_rPufpGRukNu&lvV)J& z^jo+)wb=f_{Km9}?q~T9FB&;{*A;aciAd?f{3?0ur=l1DU*>C(_GyG_(I%dDUX0#p zM8qSs&S<*xdT#^8LNu+E{iEc8!cUvpvsOnH@hjg_kSzPo;i%9o}Hr!ZWKj$D9L(_`|l6CKllJ{b@CEN*aG{bVYm1)Wylt z-7&Au?{{i^>Qmbm1g&#zf$2!qvRhc$N@j{4>vllCHN2x4wTxvZJOiXf34&x2w8`<1?z%KuqpJ!B~v`J=IV*2Qz9aF4z z!j*D5Uv7>fKJV{x!1YNldMh2pa6T$akvPYWAI{a+W*DHOW6ng#08%Bi9Ldzy)qU@1 zudFCfl{gn0Rwrc7gTImKD*`<}PCr_gae#Fkp5|MOqFlDKjEh-EfA?(%ba74lx#4Y=NMN(&F{63c=GznZQ6>syjjiU`j zf}7rO+Kxp%TEEgu#Xr%2Lz`O{`@>~TDY)z>oAkLO2U)Mhp>CjQVblX3Pe(&juL*Wt zsx-=^`A0FBMa$5$oL3Ur$XQWo*eS}c9l~;*6a359e<#&=_R#Rvncmvfta^HcW6zR? z12X}^<544#lGu<}aSh!$a2Ahc7E1Ygod?k7T58TdNoaX*FRbLWKddF|>5y%#R@o5Q zZGy;_=a7WnIiYpm>Gj63-BaeM{-Ki64xRa(%eGoV1zeVNFW*Mtp=Z^2rqb==ncSZ) z4YCgH*?cMwEG}<+0S?_qTLqe4^%{6Pc_T|7u7N+ShlY1A5@|*u7NFD7SS@NH=IKS{ z!bkW)-|l>{XCzPSgSDLDMtZ#LP)}NSyRRXtO3;M7>J-l0oz72qt#mlQjnz_U&+hM2 zwp0-#!P)-}yOjc2!{9u$B43EGy=amPE^TfQ7K)4x+$X^|@!iC7I3|8jd^DbB-vdKBqP zAxh~10^yHhU_6F&ldT8x3P)-OW4uP90Y=l$_!bCEc z#j(GB`|8jx!ifp8uYRts)Af4$i&S#EB`(0GCOy6t zUVd13`}^yB;sW3L*a?)tWXCUTsebg0TG!%ZaRg8mOE3W$(Xbha5S#;OT83`XB{Y*j4)}o&ioYsMlQRJhckt7T(D4*>JO z<-?N+ojJbPhUCMQSDr2)6KC4ZWj?kQo4AZK2V}k!gGCLgWY%rPJ)#^&yJ~XgdEGkx zU3Y@Fx%s1+&W9<5w!uz%GaUyqHf<{{$i>Il$__#;s-sKBa`E$xj_I{iieTc%!LM_k zZcY0hGSbpM-rk5uG84Nz+!hKV$ah6^hE7#`RFO(U=E8gwEgn234kwH;g*!WP;j7e> z7RaJVJUr)L^PJon2ggaONWR`mktOoymO-(fn4$|3UyYVumg^ytJzl6K{u;`@qb>WM1Bgnp3^IX)KsHx?$ zXL7mz_-q!$FsoBzYUHd}$A5EZ=$M7Nl1by5hQi!`@1G#UG7 z%lPbjvEhf}nzr z#(F5o=uVFoHd%K`ORhzfIkgc&R>Qc=*X`vtdR7x@KJ_#XY2UBZ)Njd%8BFPr12q91 zKZ`SN*hL~NdljX2Y>F`(5@(xF2+E?CGgIk3p$D`ZD-;d04`&u1**G3DvAq;Vz9a0d zJ0;XfR*xqLmz@k_E`DpUl?5FTcqKAjZh5HV2srg-8t-3OgoiPb5_x8<5`_S%>5uNxCu@(?d2b$-ptnV6#K^c!4X&3ehMJfsIWAa!Y_?` zLUsD#S#~P)YKaMPNd%12J(AeyF#iG`ztykR@`t2)ZMRw70uAW=IKZ5vRL&V5a1e|H z_h2=DCx!h{rs>MZnv&uje^AG-SZC#$(He|uXa{e0{hBYydVt0f_d ztcbPZ;yA}aW$~Yj&p9_XEt_mSWtvD+GIF+sRo9;>X!ah)IGLp9w<*-Nmn9@T<2U|Q ze%V1u;W1N8tT>(mIAS9%aHvEU5j?`Jj&t>K)8%rC`iok_5Mh=kYsWe7q7JFS5aqL) zC)`QVCnMq9AJ!~-65n^<$SJzmp1ZvS7eyaEdZn&6jr2&XIPAG}jej-g@76N>aZ+>P zd(MLRrRpQ>{;C+-@UXftW9(mFE! z3$BS37J$)fE-sW!XiKW_Efue~cHOC{uvT4teQgM~V%f`WmD9Ae;*K%NiBi6kjErZ^ z$l#Fcgfejmce7K%g!oDT3e^zJd-XVCZR{Y#sky0W%)LLN;gBH=Nj&5t_2E-V3K+EZ zYqR8x^Ad?&&nVGS_NjRyq@K;cc*V7F#@3W3VE8CddE=rIM}C$_z*$wW={>*&K7EqR zVVqig6NDSFG`8^T*|ma!2#X@-sP79)D!jP0dC1vHcg2b42=7i%kpf2ZkWla&^(~gh zh>7nYrtEaT96|viQ2)0B8RC()LtFAb+UV9s;%33x{MdB9rsje^8XyE*V$KU^wkj*V zjVr|@{|Yh@TWSAzeC-w!Qan6nY+ob2Qq6NHHy78`t5-Hk;`VlSqoboOIu+4wga_X=>RN9drC+8RMzBuX zH=i@KC4^l&M~5^jmfmnm=Q84A0r<2;J5Li0q@nX*(h5Dc^(C@D;Ck z;%Wk>L*!ijp(O}^3ObpA&wErEWGy0^U4vp;RBV%8x`sl(?k&x2jbR(EY)(1!?7ux9 z8T#^&xH@OPs-dU&S@3RS06D+o#4(<(YE(Um-F(qxKqvFa7hZGdT2y*cX^gi39>(mw z1P23Sq0^hb8g7WmNIbtc^oc7ALpWtGg(q*OXYE_(KQ#r;t>Qv2a9giCXyjUzNIuMlBTJn#^!@ef2u=T6S&!iaxf<*rM0V7`KVbuywu&KdV{qZ> zt(f&3OW$=8q;eVpoQ8$_MKXKpCL(19#PsC$d^5)K)q%C3!TR!{{mV$*zKT?Goz`y` z%chx@lRFWD6S1?2kKXco?XX(QKw-1_9#0;AETt1HGKz%*(D$AEX4RrED_Bo_7E60p z8HUhz5UrVYpmMbw(vf7owP^MKT{V4nCPo&wxl*J|@V=#kz;bkeA&37(Pwgx`k0aWU z;xv1%5A!vP|46wycdow_?bWN;{g*p!zbEu9sG5u=yq1zTr>zs#Vh`9{{APXVt?B9U zyWhm^;r&In!uwLC)w~;7$kor|snMLe!y1U^Vp`p2%hk%Php(pQj7?|Tikr_6jzmH_ zlXd8Y?(IzZIC?I=F~;F@ya#?T0It}Y=vx9MEzRcq2|LOojwYL|L)1<&0KN=--||@G zs;>-Mcg1~G{9uCwJm{X&Ar*%0PNRQ0`h-T>Rc~J!?ZHThrvdQ8;hD)|;-g~oQq3eO z{C=2_Z=3CkKj3?AQN5jsO{LVcLdoVp4+8c2@2~Tkzo{5ZbsfIBX~2y^-J8oPhK%9~aJ2l9;Ca#y6GzG^Dj&V~1y z&|s`>A>EInq&}!GYJ6r;)o3O=PjR!&nqacv3yd=9ca7S3RU8=uBQ=?b{*U7#EV)Fw z;*U3w+4;AbBwwm`hIt@0V1QBljC$l>ME;CgsvkikbZ`JX>jSs9UzdF8&4ezq|3%=^7m9!9dAt-u23XJ4iL;+%FXsr2!^*kD>9Vmw_w zww{j+8wpRk)j))d0n+0)$dQj`PBWS z3Q`gbiz21E;#pPKUt)91=kl)p(dLqQ{L0eA1Cg4hH*cyEY zfrRQVS0uNh2fDF+XCdyh^1Y78yrzE4;JqAUrMfHO%*=-dSKF#-_60c)SqbVp*6}9I zv4*Au_+`?V2N^>4i15oE)VPhx+$6I^7EO)zmeILP51RzOE{gTG;$?ZRAuC75*SX1Z z$|)Z>t4{EnF2dxiGiuW`OF+oQ;GY(+?h|YXYk?M=|JiFQ)PD0*-?VpwtV~qceZ;pI zKR7$bXk9P{d4}{eou$n&W80oDg^Fnzor1A$U_E{}!wmp-*rw+>3U|gJJF&ou`>f)+ zu!%f-qO*8&NwBH3y|2<1qZA9WpvQo2cM3jLyprwi$fB}G2{h-)5wGD#XG>R3>Zp=s zaJlHvlRrV`KEr}S0V~%jqN&b;k^)JTRQhQCMPm6 zVch8g%!a1Kd16WIfa%~*+Ac%}RA8&76-to7mnDEykX7JnjS0~I`Qa*?gVDqB1qhQ$ zf9?(Vcg4!l+k+kF^YR0K#yr`bKE(n9TwHt9b_oft=)b^ykyTinI|;nt-y-{nnTO-v zT3iuV?TBQ@4*be5mCGVi$+1BT^cVW0}D-zte=;Gk#DfaQs4&Yms{u&-a)gN1?d zZ49(raawq%JqD1~T*JS}YVMf!UI-58nCd)0da0Ox+-hW!06(Zn>c3n7yo3S1p%4B- z8(=K{yhmsPxaV4S^Z6clU-5yxalj-nLGmBO>KRe{bC2Z|lRUW^RPU{Lefi*XMpuKg zrsX%Fs&A*(^u!Ose9R2u8TP{%lw!^`9(CC_2?9a~~*)uKQx9FT*oWtSniUWA40V`6>TZqtw5n_*&ga zs#D9k;C$S6$!GSm;Jr+p|3+{g@cTPQ(Hd9pq?17UA7H3=+5QK3-Ny{APQ&+OWb`|% zHYE2;;!cTO$G(4Tm$*Yx(e4zb4kq8yM?>=M{B#2V+;nDp>WU)1Mv^zkD)Wfi`QKl%?4U=D3KC_@9SN{OO zset6u3gE7yLVlGAkh2@5R%CFB2vyiRI*(_CB<+B#G;+%p#I_5JuVm>K!QZML6=HbPOC`j=cl$kM}4 zcNg2~L{7fe$B!n5{ksQ^XxCrE*gBp)l5B}X)Q5@>WcSd%p5Mit{Fsr_YvXih8w)`H zq9AgOOeIv3+t2|rXFQ4hu4Jmes4CD7|EL0Y4WdF_5pgG84Ee%$W*N(vL^2CU{sG&c zX7cy$KdL<(!1Q$^7HT^w@G~6!$QjETjoTAd7~wbWzboMRr?%pTQPsBe+B5fLZ7%y3RXyKbuI z9xl2km8%m}6M4I7fhxcxaA8CpSr$|H|M?MuqXfV~T*LwP4)9qmlw*zporN+Pzxlhf zFUg81q`&3ifejGSRSWGeYi8x>wbWpV=Xh)(Siz*c-_YNAOTIK+y+2=;E+#cawcTa! zDN4XNhsWjn=A^m*PYqH;ATi@p6r z6Xz>#$rpJvYBxvlIp{Lts!$#XgJnHmHf>4~xL&mI5JV&9x5iPOo4-EIHR$epR=K$7 zLN;{|0N#o2x`@t=U%wV}H5)mRv#s$@XJ@6uB;Nn9GoyPpMX|<~rKFP+PC$LL7`V~U zcVCzU>#FV>Exg6@N=plCzqEb&d!Y3Y;Z7%Guy4{Qb#*f4>@L>+SU#CbCAF#DXnFWp zE92JjgIy_%Le=3yzk?&|2R1glW)>W2*W;O9FeLY3PJ&6>bWfU~r-$x(2=36LG}aD# z{F=>TLqWq>sHPflUp~1Dz>B8OrkL{sm%WkF+xLVfR02$Ii}{!(!^h~g(s|3%Zg9_4 z6!lDHxtG?;BMMIMlUrn^1BQ+C7;<9mGYV-oUMc^e;u)g(;B2G26a`>Zn^?N3^DXeI z>zmgMr(|5BQr`_kLLv)4amUY<+qB02WKd{x?O6{R7ZPK&`Md-lWVx0+z`Mj`P{_Y{ zDh@>$a#J{++k4_ze>ho>MXes8H~B-)w-g1N91>EDK9Hr^oA?L(;6KL+QqAGD-2N1i zI}T^U2F9Bgf5)3)S~^zLY`&OA(zi$IkA+Lb=jU_ti}G!l^Enq9;leZo7#Qj#g?ysw zUU5e{FlP0R^a4`+o;>Q7;8VPF_UIEK?+^MX$)lKY0#37k5`#2IBC^&x)6#e=KU^Hf zlUl!|zL;43VDP%t_;;1qj?Tj1c_oLPe`D^^Nxp_*L+Ng;%vhZ{rgOtJ@lqH1^9`+K z6=v-|)*S1nVMocc(R)nq_j_8=rG&e#J}1}A-Q3hBtA^!p(wxq6>z!RR{j}UyPTtbC zxv%axc#x4WEzY+w9CAFPckt^KE5LH_xdXk?J`U#zXd-`a)EY92o{mp)k3ZC-wzdA@ z3w?Uthz-}j&Jk{fsQhV!c=$5V{jNT)?`NOiewpL7-cDeEn^#_mEJ+I6&Rcl|uJngW zy#RsW4hUj*-)vsqnda7G_9Z_pAEhGwi)C9&sgnb}Y$)_#$C4#)P5%T!p(JJ}cC!Z| zz$-Ue0N3pe$=wI;ag4D$uH<$M*-6MstBDi5V4PMoWm5I#@O-hucuux^{ zPdg4-sWnjKYwda}>x)v(7csjJ~3@OI|eI^w1 zZ&zT+R2~{|l)otT!C~3Rf4}_L)X^YFXRy3dwEl{GWb1}(4!)?>YMVn|`xwxQKm2dp z;jd!Mmd4qOyg*O-y?YO1(T*X9oirNy(UWb$>S67|D&isJ51|ig%XMQI&}6rVPCGP~ z8azP5Q+2T^S)>Pu8AbIo2?-fsuu>7gv#JoQT`=a{v$cJ03cqYw91Ek2&9p>1RQ%@NkW z(`=Z<%9;YM3CqVz6V8{r2SjU@^koKorI%G70}KGCJn3>PpTfuOJ<;Uuws+%6)APTF z_WcyTDmsnlLR%F|fw-t@g&G2)^FV{UWunW$IB)w4wr~Z2sk{34BcT8OIrWzZb9eZz zY+C>`XhPI@T4eNW2s$y-1{~p zua}c^rwEuQR>zJvNhl-$lj!C{iUfN?HM373v2)wtQ9V-(NS5jC_Ed`jCx}Z#z;bmY zIYK5D-5kW+d^mhLLG9>hOP8Fvbhz>98@0WSP~kHJD#tzTX$xD!-yITrN}; zhatB2sg&q?Q2WVc)p6Q$8jng}y$sOoO;Mmc{!YL9PS)(Viy(}p`tZyEW+Yh&EH<3d znz|$qIk)S1iXMPoqeE;#YlWOQi{GoN)}&w0mt0>JKVehktmmdJ za83?jC73Lhu}72WiCV(8B!9_S{#a6mOj6C}k9gmRCu0fU?y8d4?8ofK2cRtETP-^6 zVa<*fGt_*1MnxHK3KcG5P8vSc{06UOE=u!j_~#CHDC%6MBEYHf4zB*}-A$V7dW(J^ z=j_-I36QiqPfg-C;fS3fIAbm+VXQDR`EcKj^U(ws-TsVE2k@WmDYB8BeLz`z>B zs)r}Wg8jXbw;WxXP5oNi%9KRPtkgIR7^WQew%{F{X- z`^?nTl!T+-i3;y!kV-dR2IfGol=5oQt?uid>erQDQ`_!QO(#8}sF%lhUjaF|@Wz=4&p9Z}>+sH@`i%den7(X$bAvFL00ljGuu8Em5quF zcS>@U2o>rWO;1j~b$vud&L9JpR4i{cMvJr2`u6bzUJbn-*65l2U;{>BL&!%S5!II! zSVLfw4_zXINd1Ca-vv$E(qY%xWhLd%p{UvsGGqu~`p@ z>a>V%{DL-QIA`6dJ(o`f)urWRVAk)#1`b`~5?2lso9kECu_Y1|z%2gDDG*M6c4juU zv%yzOdW0a0kydl>-A`IZwu;TQul$)03Nq$q<{pjPJqrFjkx*lU3B`@G3ZJ@Ea-*c! za44FaWXx7w;?@62HTx$Su?-V}Q7Da(;LEY)m5(>dkYtf57@0eOymzrYj7cbDz!7(5 z&q^^wj!6=j%snz}xHFK%Wg^9JS$MBFxt#X=g2XGeODfnS=!^|B@%ybqMbd-~p3qZh zv1+2B_n#a5QcgxrUpRk&$lHlj`;f7f$~Nt2VA;-AII|rOs62z>0yW_R9qU23Pxp)B z3AwSd1ZUg#c4iIRzU@Mb&hPjYS$b=b#^5qfw@ehV{5yQGOJg`!+#H**O!5QfLtcY-dXo(tBW_XlI`3{tecwmA@G zv*bFXxo{qw9ShWaie{2W<8)5sjtsd3QaC6&0x5P|m3v z0E1baC!;b6C}2m%8>lt5s|8N~G2=`E(<15Z2>j;=qn2lQ-nvU&-Pq)qi3g&b$O=>wYBgK*8nZNO&c>UKI%PAKf=iVKWb?i3 z*8HNj18Xq(>U!ZVd61^H{l(jFfJ<{$2bcVfQ=Z6=UDR}OB6q+pntbkf>J|biDov+D zd>-cs#~zFuXc!Y~w6qd0j?#A$+A`9~;bili$@R)vS#GyHY zP#Ym8nfc}@w5K+gfn;c?o}%j*Nh@%8rJ{sD9GJv>Sr~X^(tcumU|!yrJQZ}}G&K<) zuyKwVP;i%JT2(MfwbXf4NR~h&KoFCmN2d!w(5F{6_$EA5y%2adaGgS=dt+hNdpI?Ufe-fw#?gVkb;l3!cE zxhcJ%@Z`)Q7N8JP^3i=O|61;fg7~}CWW9UdwpMJN1o8VN3x;`~TDh*=5-3Tc3XF^_ zR*sH`0uwC1$1lJ00r|t{^O1T>1(}?ZIvYc06jl@iiPf>!ph|`^aZ>rqBFo$4&xp|! zD(1Rzm2-`8TD${!c8(z+`1~g@NaveuWa09dy^!W<_}Pz8I{y89okZ|Fv9wa}elTCQ_D7iG9{GKdO+@NS^byh>ii9Dsxe_bg`ve zKF6HmD63|?^S8ZI~8>|m!VpLokQ1#QZnRDmO z)Ri86?f^p+%~Du(L!WZkUJOId3J$}rr&<^N(1VYrkmt*6dRpMmO5LA8#nu^{-yG2o zKghlueY(l96O@(WYFuZDL7cIXIU{)n%mNbqs=4MeIfE2t`h$-0`p?Q=X;bFqk7rl? z$j7)`8uswtj=mlfZ9bClt~8u6v)q#TaW59<=;o$r?%S7H((`%askj;jVR6@M`O0=g za9P_SmRmq03u(dpgvVuvzN?hA{Fkubonn@-er~JbPhlfG+_j3m!TmX9ka)eExuvIYyw5AMTFIb7=K7 z({<}K70-DC250+?iz8JL-W2ql_9K(Qxs_E6UjnZE67ch_qQDEPH)DF}?DOvOvO_to zb>p+SFHhQtTwTk5OdsMHyaCf`l_R?$xY_?7QF5EC<=S5S`!dhqiPNQ(`&)}M>|qce zkk=oXkQjOUb#Lv~lTQc6N3urEZ0m?A5vJ zYVTj`suQ=GgWcLBIF&}-)1wc{JWpBhx!E0gEx;yYH;yQ~^e%AtGNYJ}uU@ULQM`3$ z+t)m{Y2AX!n>T(2unypJ>GB7kbO!rLNzdrICv;()G7~NQo&ta&*<|5}61%}kNTcV? zIsQ~3Ry#6=#{RGzI@^~f=6BrX+^kzM5gfs9C%Wu^JSQVZ)?ufVOuCuwG}?#VPq}r& zgmI*PNC9hhQCGZ;cT|ekHK@rac>ZW*f5q|xMSqsqAJ{&d z(CffQcKE#vYJ@Y9Gi8P>gYD<>0=-Hu&STB`nZM(gS74suC*kUu`VsQamt0tuoRiE; zRof%+=_5=IDd}W$@%=n+*<}$XC9hu;Y+d&;)wYWNd{%{P;Ss{L4^uZl&7J+(L5X#> z#_FW7k$OAI9M~{A9>AZ*P7p0zg)$7#8{9KhyQoTt?7P_T>9;NKZ^ZT$vw8yg^4_O( znpE_u$W3FNqt=r-hc=Nkb2q58Ek)F z=yTF-g-EjSd&9ar{zwfSV+!ShUlOhjhc=h%3At@a<+1ZGp>K2)c?Ml*-%j)i*zZjY z_`+l=nV+fdw#n#;12zL1VC*RiXdP&O>e5UaDURt6Q#;8N#pS?mFAKhgq(E*4sE|%Bdv>+FY2I}LUK=bS@&@l z*2g>r~>^<%!vz_G~0{18|Xej4p5Cl`MJkUygnHAay@f?s`jMAe{PgrUwAP+J^ zGj?JVp_#E>#wGHClM_(AZy^VQ{*gY7WvOT z1LVSZQx>fHsY`9He$han81*z0cTllnh;b$N2$7MD)__^!l`^__4kICntuZ*4dCX}s zoE|qcFkYHbIsi_CM#}bh$#b`rb48q$HMbx$Hb6IhdS4p%B^s8@$WGCh2)D@@>b2o6 zuFE@0Ms9doEj-=M!=3em9e!Mqe|KSL+jT;uAxy`=)BnVN*flUv+IpdZjiaifu-zra zea8Sdzjejo#}dr*C7rUtu}TcfcY0WyD3REm2lNkeJ0Zk)m=ceuv)f&(7pC%qq0g>x z(2T7T_V~++4cCdnx9Ri*mMs0(5E|RV%u#?f zeGdda-@eRK9h6P9v#W0_#1jkbqtCTB6&I#fKQ<||%$ZW(OTtpyWyOf%r2k%5 zUhRCD+t0S?&%XjAqsujRY^tqdH1`hV(_y_P0@WN;U6-dy0}93pVQpZS6>U{s)@MRw zV*0{DSRLbt+eD-u`dNo_Os%oBi$WOsUFpkxgRPDZt%Q6`kI-h9n0<1%x-Rrk1QA!k z>fu-_4q^xDW-QPYCg{tal6oHS^!8u{k5lXD3-E8Pk3w%Y4gqrsIh7sg#tV>Q{Nfz` z3(ywlxL{uKS3GdJ@ko*EHL!F20_`pM&AZ9irm3#aZ=%s^SR&*h{LDxw9r^veXMlE4 z-!Em!(?+(f-0C>r3-w9(%x<|d6WJypdYBPFFsoQ;hcy@tVZD64>zb1XIG}bu>myRK zL?1aZatH17)-ADQI={hp+tIa;n3|&!sEvLj8^Dnkc2jvrE zz-=%*PFa2T5QgaWNIoy}K3BByws+RsSMFOmZCU_-Dx7;MhBQ~EE~Eks(!bIEq?|0Y zlBJVoG540}Ct11WgV3n&F(G=0Nq?iAPkSrdQ=biu9_oZMS-;O*58Kr5#9db* zv#gV%@9}w%;l+LU-kxtVecHOlM@ihznzHCW1=r@ens6+}ty~5~?^nB5)wTR^Hjywd zfcGF7g0JF(E5JpiK$Dw|WCQIAt}32W+%im^-nB^vV1;>%|@^U271&BWV9*JPNvFRaAYF zFbR9=JH<2frU-v6f37EHvTwe-pa1um@}f;ZJU>e3&4P;NLa(<$jyOxQ|M}0&mG=fQ z$-7SG|9AtP?wga0KY8gDnzgo;7iY9*<_y-0uge-8-FBzR{G`N-9p#=QtR|w3F5PQ% zdVTro_l#bGV9c!%I%U!)nk2N~pXcIc`r+k{@;|`9$!fa!^ zd%)4qNR_nVpS^>uEq$G>^W~d4hH)|T^w7ZR^n%U%1Mt!hDnUQu(=z6?)%}uUpQ1_j z0OQ}`{G~@->BnvPtaGb9^mCb3OyCw{{xx?Y#5-Dw^w#wUPIa!ty#Fn=fnEqtOaiiV_lFHkdC}eFv{H9*3nx2U)0nt(6JX;4 zl)h;C^7kp1_Iu4p+j-ofc@q_na=Vvw?AZ;8w&#ieA0kYJ7h=r(Tb5~?P`5bl< zmi(yu(bJc?qAx!Cgbx6P&iw9tX7u;mO%H>zh?qH;b@mZ&qjA9M0t8zl1o7mQCY9_Q z%z}$E|0+<$Gw((|(kP3-`9T2Y33gr8|MRfGM}`B2f1vg^hp{#=#9^EFl?>andW^cR|@&6jL|Nmbn?4v!U-s)ipO1q<^bQ>YQU;VNt-qGtgZJ}5W$v9pq zN=)}7llO3TAwhEkS{hQssSi97A+LWOnOl{}_qojoEl_eOgz|ZBI zKksb}`yr&~=_{}LK24F=#4XvvxU_0~W7}S^&exHWp4g1>45F0GaT4C&fpm<8@{SIG z&W8RLg0`9~RetymB^8}Z-crq=yVS5ucOJ zF`mUo79Q2~oNAkSLsSZNk=rDL;k|N$*$zxP?o!izP^yxo&Ze8gKFSjnyX|IVz#*Jju53D@ zEN;*kxxPzdAx=rk4WeJg{9c%`r%bf5;@<^uIvSNc_x1&2#oZDJ&$Kfg#I*^pNtkj6{pBn)U#9fm@F%71x z_htJj?fX^H#sif8uV!dcOj$J@U#9m>K0P&~C5c*qdG%R7fw?(@`tu13z z;iCU;-cDX-&|H=wA#C|{b^F3f>!0%i+63W(TM6m3OWE1`g64f@^#S33_%@LApMxchz!ky;w^mmJ4=V_w4>dS+1U+f{0u$83 zWVes9JjH7nn>Q&gIw@0bEBkriPZE9okFg1sBj2S5JfE#UD)lk`Th7P5x8TWQ6E)$S zR9W4(22jc&zR;Rc%f?^v&&V^r_EZ#1o2~YA+BsKfx9jl4z^R?!dmF*LlTmWEqh*b1z#~nN6!?aRCzFM8+I@= zrai{`W8GQQPxo$qNHMoHXs`@CnbGR=pDkS$7!;j*Ei+`o-}E~;L3AppC1p52Y67Zb zA*cz0C@*SRGM#apJeiwQD^MK$U?LqRq=4Ff=P-g$iv0B^wng-cK;|CW>)$=ST9mT zAYz0sER-BUx!c(l2NgDR)`)!F^X{l)L#()~q@tmAKJLe%Y}_q32OS)wLp$rAYNwD9~u9CX0~I$I%(|HabG3DfWv z1`MVlUz@ZgEi`kN)+8*^zAA-2rl~di)cM@i}8}N=cTSmu56&(1ScjQo@tVM-?$H1oCtSh?H3>A zgu+j+N}5&`7hfIo19Y=*4D|GU2S>glaqSNI0Vrl>&O5%xT@~Hrz>7%xd?&W*;fO+T z4{{gbPA<$S$n-M5sy3VYqOxoG*R7sJp5*VHV{q-r%GFE8Q6+3e%wd-A)_;zEyB#h+ zET~~Pi=;B21)paUw5|tkO&13VYK4W(o1b3;mLgxwBPOKk=IV@?^5+ly1Q)j27XeNX z8dL60VpyHjftE45K3*By&V_rCT6yF~D)J_L~N* zxsX`B!3;`Cs*I8Z_4H0MjC*PQJ&#@8GH)!YRkpWHmkG0zsHU&F6BIJ5RsqfzkY1Z% zLzn|T$NwMZ&N8a4uUq$&3dM>`(L!;GySF72cXxMpClq&zI}~?!3&o2U_uvkp6bTk` z)BpRPJI=l5+Q+*FA9fg9l0DX5d#yQt^LcdoS?TvIy){c*d`q8p2#-!7@`IH&7*5?Z zEgf%1OtRvyaBbbB(CXh*){Ku0$2*I#2OW2Wx=suaWa?f&WGAQY>!<%DIYmxQe1_EK z@S1V<>GEz5Yn_Wu)xD-P!{Z6sb5~j$rv~NIIBMKy^S-#tc#DjafSm^V1M-GnoDeil zwj(M;ko1(y9u(N}>RJc7pjY8uO3j@r%@0k(OZ8}%^|K_>x=}kV7;8AuG7}QRc?q!N zDcQL^thHIp(^8iG!4_3stO}3hK?r{#;Z;a|TUq=zyit*NLM2L+9+7A6b`AcL);PSc{v{-6h+`sR5$#?jc zkwrq%Q={WEu?b?VbE5rcZif-ae^2{20b>2)B!PLI@E2qj4+${o zEi0Iq0$Qm`>0ew;TuPFy3vGu<6z&Yga=)cahB`8dT8XnErujdL{}I9>#y`*RgNx1q zxwNhp6{8}`%;BpK5#Tr*ZN%vo(J*8Y<1#_IH>5cF6To#&|;LF#@2o3$*;Zf560 z?1Rx~^q`;~J8AS)LVdMA=s+jMOUM`cGLoFXQ`^l3h6QmrY*uz1A>w({z2AJU zKOKZPu!c1FOh{dGrEl3{u#7G&FJ$XO4C5Df{?)3QCW+2}O&N$&Uu7xq(B`;wv1VKE ze<46-$`0djPz&XnaOqM|oq2m^gP1u7t?orfbCszjbWchY<8E(bIHa)^AciX>V#uop z{|_sE9cS|S*=+nsvWH zg@n`}vA!&cnVg85l$3+tUf8A&8VYmhY-Do@M~Wy2nrDtEd(6N7dq={$qjiIA@LB$65ZQtj=|ML@iDFOhj z>1f|;)nDyASpb+yxLcZF`DJGjl(rg@8xpmiOe=o(eP8^IO~Vlye8a{8nIe|M-NIqI*V=}SBU*WAtg$L z*@3L>o1S$~VIZ0_eI|#r5OAd}O+!Yp*1GCj;hp5Oj(;jMh(b@)i;@sv-ri0_og4^J zOjYy8`PrsV;B=Q$sFS4U>J&6%QC@n&%0^HiP6GAR{+edjYJN9YlsA^?P0tDr1|ANU zrsl#Ui{;SH+gKD^#V^v!2Wt*{ii6p_!#Sm`4}oHg`cn)}y=Z+;XA&uu=h=T!5?H2q zsB*u&rQ<_Jig-s+xkooKkAR{FXcMxyg*AMyu`m%vFz!(l&waLJEG+wYx=b{8R+uolQF>O7&B%Ds~#sE^|@A{yB>_Mu?LR?cy4_)1-3Zjz6v_r z$?nYQuH;=fMa4m-4}YN8fK-v=BoV}+Dpc}Rj zJZ;X7Nbd$6L&$Fu@Jd06MK}UDFOSI{d7~BVAVQ*t8u~`c^P2bO1$c`&xhmWTD!{dV-l`EFVi0u;5y@p3ha#)kjEF&S^Ft7=`y87kF(2iTo!DD?@BaGZcsT*F zaSbk&8}+8fpf36(HENs$ZXY>FlFvy+$*+{-1AMix6xToJs7+n-aZV)uM6gcK|Fn>m zg-3B_CQhChNxUtQ&#K>UdFhQxB8}F1?tR`paV@GaASW=E>*XfBAql^L&ipQ)z|6Qj z*K_V?2xhRMF^X1w_1ykP&Z+plj= zl-+=A#BW;is7ujhDnq6kv_6>E(+V1r=eT2b=&@F5Q23@6)cM#rpO?H8eyn|=L^&~r zk)H)$eY&O{jk8PywqDMi5K5=)`QG>FqYp9lYD|>EZ3E!nKZgCP7IPJ@W2k~k`5N-T zH##e*ocMFO6s^5xrIQ!)1jtuCp>$6qa@;lXw2MsKpuk#$(0){n{-x4D|64NL2 zrNpvqXjxX1R=_c8nTgUAV6&?J=$r!vc^yq-w+7lFh~+sc*)lNJk*lUa6h$UcW@E=WOH(GluEz^vzW(f)Zu^p{TsE-$5e7pm-guy)A}PcmfRsj?JXY=e&PLgc!3*ZI^%Qrm6d^f>iH27c{Hb# z$?5pP3-3Ke@-alR;i;B)-+1wTkJCKZ+#_J{DR%Gi2c_xQrHJ93r>|!D-b6zqZ&$~Q zvFr!Ja8ckzD4O9~c4=;hzshXId$94nI*`MTK$1>9`|_ecI!+97UeW354wnZnuiV<% z?r2Y>#D*eFUGLIick?%mBrAD9-;>Kyv75)$)m*Vga=?Z}kt+O34Iaadh!d4hQ+Wj? zRHBCAsN2U|xrcL2JiWCBxY?u38R<$p!&z#pR|{ush@E}sGt=U4%!T>wk1sT44t)de z18>2#;995VR&1OECJR8t=EqBoDCTSym-VrRLk#PCx8n24EJ5k)bQ``Y-uVMiSJg@}BH zV&lQpt5VZug2$oaKl`2%p$q}_Q(C2HcnM5S2Y6RU{iqV6)L&nOa;vV1-3h{A%L9oB zCko}Fe2>v#0%P6!im0UT$_`m1*{T&_d!n0F?JvA@sUCS|ihsPAC;{f|2Ofq1DKHTz>!9Od&ZST*0qw?bCdBm1*=)Fzp? z352~G0%pDnkwF^g66>G}4DGMu6g#zxui!9WOOGNBRin`t5N(7VcjQ9ZAGW(l;7yUz zgItCVCfrZeFo~8;XFIo@{m~Va2YK1lnam1!{MD-DVrdsbBsNNP11ZC92B> zmn=V(C{I)tuQ(MW*b4~|-dg`-?6B=k*li-O>m*J2ZeBGS&b0s;jd}K;B0H8Rli*); zcsN{&z}+KS@5uVzQFlj&bESxM(Q`zJ5LQ?7Xr`MZ@KMEXFH6kPLC}1l23yR0p)71W z9(EwG-CVf0o}LJI^#>v^8%DE{4y+C}O5*8k%KC3Cefbl6x%^e%P7sIDq?L`^-lNT} za#vG|O7w^oc)7s>L*x&4^Hb#&^IwGIkh>XjZtMsQH`?%o2A`h74}7AtXIT8*{P4`B z0!Z@wNTW-%VoNJ}=mUk$98@ziaOgQqM_Pg0vdV9F+`2mv_B0d-dm8OE>&9WZDGtZ_ zYZXKm6(bn*X@cP+BRc5rXV>_P;LhE`#W$E+z1gL4N95yYl@*@RcMrM@`;gqmJQGxz zE~!aYWjYtns857)GD+fulhp+|c;|$^_#}Lv2^l($)Yvm(9Xc!F3ct3{hBGl^A_mQd zTOSe5-`-bA6!2y7=$6MPcz~n&Oe*X|wXyLg;5ZZH&^vK$V<=U*%=H?Ii;oeq5eyK!8%ex&z|E6LyD0E``xO5^ zm{eraU{1P`=C69zp{egbOG~YN{nV2Mfz|9l6;XW+;=DbXW$V~&QQjA%ht^n9 zz8)f-ul3_8u`3IsR1CD9(EG0Xge*5Qg|0&!k1jxPzRZKSjrC?&>;~0}TpzBb8Gx5#YYE%vE7|Zj)(my zeN#A#LVNjCu)7dNf<+HGYr46)Qd{~An44cNRG8r?emZ=%x=7)(;^Z_1DM+;NR2zL2 zk={GGO#1JzI2kTH(YS2uKLy6AWt+zR4kJldz(9N7)lIe`tj?3@QN5$LBhEV(gf#KK zo6B&&CrdG36Pxk2&VZ}+Q0vkXT=B_?jW!XFRnl`xkpy|Rqlo?&(NHl?!|UUYU*gbk z6u#0bC#zt7ZwaTXs6hANA>gAUzRhgSlkzs#)bYfI3s_jCZq-`_m!5jDB^@3xq*krk zqztM!{A}Vblb5FuovBw=rP1TB)?mwj4*wL?_(x`5H(bq;AX+~AKFDnDf=X!XKEu&; zdFywU$yH25#NbfI+Rdy&vDf$NqAE+Xh1YV-7>oFXVkaYcu;D5A3H^qyfXr@O!xY%- zh!1!_YbaKwX5yf!z!)kEOXr=N`_;D@_;~Bi#_z2zfKIGNM&L zHXK1adVzecHE0E&DdqRo4{J+*60&iP+4b-dz`1bZ_Y;;3S3IIH2NY>RyPx{zP12Kj8q%h86NT^@Vtfq?}nelicFsT#qx9?`fS64S8!BIh_h}qm)!+g z{dQW?$sMa$MV(>On{oVd9Of&Y!o98Ai$tMGPschfv;qDPX^y)arM5mZ*Vo_g&uckk z=>N^i8HHDLBn?|HO}ift_ssBhENQJ0ZKXLa)8sU;OE8E~|85L`9f&%Izi)G+0e0?< zSKr$Oxn>H3GE#2C^iD0p8o@&@y51??BTWM^U3-=NIp->mW_haF5&40E$H(2=FS_`O zI6UR(n1yM_NtXqh$bh8Q#*W$+_Fwg9GLU2_#20qwUn(}#RJgw`IuAO);*}ssBRp^Q zxW_kHiMeREIk^X0l@{5)j7Amy(~~<#Uf%;#>aLY)Gd9==_8jGaz6>>3|5~WB$~%uY zbqn$T%rtdr&UrMOqtb*z>#XQrYO3jLzw%M(4iA z1P2LG<`S@l_R);47sLHpe=3Kwf&YUAaIay*qN+CWnvvi^d~uFK8SX2g(9f6V=Mza_<^?Jv}*@j&eB(uIiYyQovw~!?2O2Whzyuw z_uhql~s7vy$ zd7rK~qAEn;{0|qkk6twcyOZmc8`cR3-;a#3+&hFid?ce!6KWRa+VqQo8+n{+ENyu zfB87gOPVX^HD;D44Xt}amuligmlg&yc-?RQ9BJFs%-qT%ur{-kDJPSaD+zR0HRG8w zUs+KWJAN)a)O}J)P`;Tcp*-5N41PD8{+wd_JRP2G4Lr@7JbLB~KcA?SEr63!o`WBH_a7*po?JcAt7=`& zbWubeWhW;ytow2dJFQ=!hqP@A{~0n(%18ed!_mR0Rf9Bzt;J47gUtxG9{nc$MZ0mK7M2-sP7!_b08EhTtxY>2^*Sl zByh*9r$l|WnP|ShFL4z^j&LSsr-{|t>aa&UhimKBO!r)TMLW^@^x`n-@|Ueo4*J7- z?$G=4ONuktpO(cB5XZIO(y}*ybnn?R-VNG%;^J^9e|z4$^EMf!Ta?=5cnKhHcoGRQ zD49s_G`o-*+(SKdKb;eMKWj56I(1#=Lyx5Q*EyG+rnx%)&Nexfq0L20YhUGxzRGCJ z%24|yQh!1+ziUU0+bm;RDEhwi*nz@S1ZJ`w7>D?E&Igb2po9s^9^GRlbMQqh2kb1d z^n=g4tU)>{cT&)yxL{}_YNYFVM#||W8VT6j3tr<>OHDJ?$(ZkFdS{gCNT9|R+V@!X*e-EsZ((RUENCo zGHq&Pv?mKsXDRW~cn2&Bl($E*8FCdAo&T8=13{;FYP+1JTAdVX1kv{Tl}dSrJDEXR zoqKtCn$oIt=H(H|lQ}0S1oG1C^68r2{1T|bj<^UXVUjFJ-4f&d%WGynbdouQ%pmk*|Rfmn4_cu=q09gqeF&}f6O4mE3(+|#jZu( z{Vb^55v$)`PK9WHdMDi7`VoZnWUxeA607TXIahx~$9Pyzv3CD7q%p>#D6FFdy@?jm zx%Z^~cjg&CA|kn_nytID6I~1s842eyOp4aUo?Fa1Lu|Zjam@vQgsm)>)$C;Ka5>4t zW1s+#GmlE>QV=6*E5K&_WEeQGLuuDFg%=ggQ>T$;6~*&(#^d7=4jq0shLU+_bZ_%XrC(<>JZ*>*IwF?$j`gjCh5mhJ}%j=P2 z&{2i(DH?G1x0|L38y^8o`ODlW);)hlp%Xj zEDhZDe!i}%AG55`JRa=VZR2BMqvP6znH575pf!;b&lJ0_$?wzDhg0VYO^xguj-`B($`0)%mVC| zZ`3iepFiyu^iW1>^8&M}$Sjzqy}iVDes~MCYC;-VyHCElwXU*wp#-!l=63AnlW8l- zg5LWw`SEiEan+@V5TAe4>|~IR`q0Q94F=ChqFzLrZ8TS|kc&OtNS|dbK+V{)2n!eX zCYiqXuP$iy186L+mTfCq$(p$Q(|FwcCQW*i5%Q#1XCLf%+1i-b#|39C54@n~OW?{} zud#8v>o|w-R**S7uH~uKY6-k^(ZX%NVmYNNdSb@q?>l&boc%oGlT=e#N97|Rkv3uV z*t?G=y3*{Vh0w3{%jmpRsUB^}AH2EZ7TGmD)VB2-)YL0kHin$a(Z71+d6U|+99O!ATF@OW(Txe7ovfmbJeIx*Ntfp~ur zN~NnsL}scDyFH*k^ZrQ}b2Z>$bvF0mpi&(GI9Z?&@fCv9Zh%^z<|Jjr;P%!<6(P1k z>BMYT++zShW|8tM$#z})Nc0t%w)P)O1|C1l>q26hmbBnOgCi}Y+a`|NCOOS*m2f5k zixy%seb&&3E=bblw5$a>54p`7c7Hp)A7|e_>~QCxoO0rr4hSr2Z9i{$Pe1jGhtRdH zQ$tz%OU^<}1`F&ZnIk=M(m04!h@OLWez)TCC%NQkBjJa(5jQK_c5+l6+?pZx(%v;M z_-)bH29_zZv&23(yJ!(yFCLYMjBQ)6%hb`+FSEks@@8_jrfb_R?yRlHq8~i8WTtus zKI1{uyuaC&M1+yzdSF{Qnug%n5YYfcLIx?&70(1$KG%VWURliNhF{;^U1*S2`lcpu zH1VD1iWxPo1lXYKAX*+Jie$avT}8T!FuM$@b62S*)h9etwY=Ls%?QPaqa%p2lw8cB z8`MhxPYa$uBRR6LU}P6`cq%Gz*YhpC?5O@Fe-3yY*bw+^9G|{}jFT&Na_VB$7hh$q zT4;g%nqW6J=BX%rv7~a^lVnu!o8%yqPl}WD^YQeu=t%iF9l8&cwl~~%*54wb0%Q%W zLXLVJo#JHb-&k)ea}IK9^qiqr3}~(biekLbR-P6{bj!)iwScl_Mb04xeRp0qdhkKr zL)#$ak1I8GadWW%zs2^pw0x*M>nBQP{$rgk0H9!nmD`(-z)A)G>589x*#t%DF$ zFlKb9V6>?EN_?_>a5d*c8=UMTi)<=~%4BbArw2O3dRjX#c@PQ);Gbf865bSjQ2a$# zY2Nc0&_eFfLtxAB><5pGeanFMmnto8iQTx4;FI`v1_!g4(#22prp=R2(K(&ABt@a< zj#E(Itg)74&*YXVohFyujYaLI>d8H9x{0jAYUL@ib=bvym?$-MNozW+1ua9%#3ZJB zEI&lA*uIfyqw5{bLj{z=#U=QCHm7f#??LXX&d)T&N!wjH-DshL3bsSr^b~AHNS@lT zor4YhS#vDU<s zjm6c;xVAO@c86x~!6<7eY)qJH(_y)#C4W#B@Oiw=H-J4ZqHh>Z5y zo~DgZ`u_?PKatGPln1RF*JSVLQmvIXb!~Vbzt`721t&#IAtUJ(K3?Z_JFgfyHKYL> zQX;GlGQToEBk(=zh__V~-f%JU9g7r_$bw4)06g@02e~sWdAK;r(B4?m$cVD*&6|ZJ z6en`+){CE!zL^8~U{zAOg3Y4|J9;$Y?``qb8&0DVp*Pi}3bkZ4+=zk$u+hr0fgTC8 zQY6hO@CSc1bU}|gF*B!R-aM@ih4{|kaHq46K&n9&A$^wb2!ty zf?rxI;$%S@{O!qz6X#C%d5iuV>IQoWgdx;NGsbTBEV0UH4+1BH1o_gaFBs+->ep|s z6DFNJ^w`A=bl2|ovJlBk4BD@dZ@d!X)Ep*^V|ap~!<*JS%nTjLa{87Z?@$o>vA#AB zb(#(VQ!)T)^c^xuJxfW|W6I?RRuCTIXoV;QE_g7Q#86h@qZX?`_)XjtHvwS?W3;4< zEOY)$awwP)FxR*t0T4l1vu>psz$e}doB`c*$(Fd=H)1_8NJsD7TS~Yjx2>y#+3c|p zx&2s#S+IJH4XuJK;OoT*4Rb}N0UdM2aqbVVvB=);s4r@X7_c*KL#5L}EjmQbyCZw} z9`UEJmI8Ab3)fF;>{n$Iq@_Ly%N<)L{fjKWqdwwJw!f(%TLUElcPUo>0yY^Ud+)GktuAp*NHNz?4~HCnM$?>621QkZlnp8xpO4KeDO zfxquN(Iec#L6>I;65(&FK|$=FIR9Q-#8Py$y<&wT-AEb3h8iuF+d8_sL!aefiQ?Zo za`nv%r*|}DaS)Q>5s{Hj5v!_ilOVq2-}bHp?Ib5xX8w@Ikb!&XGjcuXd&LB-IE&#Q z{OVOIyXM-xrVP*ICrUPlkQ3X&!(SY;8ho9L~q4-%9ie4tbbr5i>=A$8mPBw$m`o(&8{t zqqB32rXMcY#QxrjY)MO68DT@Vc zj+Jz06n>41gH?fUJ1>&G8(2oRp{Sn)Nzd^-dXD$SrF$TQ`oG{$^(!9op-CO?y=w_u z+#=_~%26E2jLs>`Ku-*u_X)}c3XyzFI;^L*3#mR@;dJRjf6#gmFL+{^dmHqf645UJ z#1iu_1TCJGSSUMQM^qc-L4|lC=4Tmk$}4>s{5TeaTqF*iDg+hQLlE^m^d{L*L-o-l z)oxe3LP3~j zK>%gwCKtz%_A3`IGG}}w=Kdl7ouY8R>+Iv1O{!(WOYL=txtrf@>b5dZSg?2M$2eWn zJz;^c*5(S|b(422{GuYi>`j*mwclOL7&0J+p6CAq*jZLfB0rj`-?=x=l4 z{i}HOw?+JSNyY!P1wtfZ)C;+^jJ9lwwE6jJ2oYp>v;ixkqaj*g#H&1o`IHu?7Hy7- z$odGv7yY=VQOe|#yp_!M)2x39H3W`Rj5vk7bSYUax@Bg&a%do0sc{UcI)WA_tDQ}D z`_hT<@j^5NI@F0MZ@xTg;k&ae!bzAr#1{TUs79?#jDe-c!hiscX30h)XYCzv?Lk(W z2xt(|`e%#~UCSEF13SU-Q&_e|;xYNLOU%{_v{;4LmRWmVIS#(6aG`(gyNRm@K#Z$r zy>HZ$-Xn-|eyZ~GmbKUOFZv_?;o{#eXcR8~i(+CO9xPU+#-mt*!!Hm>q&fDo2v3S4@7|w zqKWJOE$oBF|3ir(K4brPqr?C87J{9(*2{)S&Pv9%rc>)+V%QGbL5SY)rY-ybQ?7tG zMOa?%O%63e&xyp517>w!_8;)=KR2;}5#tq@{_m9$;;0CQD}I@}6T+lN`djp$Q8rMP zYsu}tuh-%QR$JZEiNfCrz~6=;i@0$4IK7gdpW0e< zhUwnons;qTP9lfrA6S4^{!Qp#jias?G}bx_`2geD={J|fNx7Ecxwfxy>%p0SQiOcO zgDgfKt>Hq;0hb1)-;%EvL+eTTDDRCW*~g2z9v{W3$1nW!RPm{)%@=cF8`Y`+{WaIt zSl<@gb48sxGllfzEQD@-l*>bJ&YsA@ zMtrjKj>=9wruk&qQ(~{&9eT+v6k>;QmDW3lYFeT|T#kye#v=KKuN@`I^d2Y4Vs{W5 zwYOy3|C*JCYSd;$iq>>%@7qy&t6z#taFY(tqDwRfQ`v^Ik!?>cvfq53Wm-jf)~>))#_s!?V|6krCmQ%(hRd zTss#rL&44Tg+-^X4y}JmP1H&anh%f8L^l;>7P-*$>VoQkzRG*YLZyD@W@E1M5v9r= z9E&+j-Ri!rXG`_oClHy@JRz!!Y#7qYxB!==-uLj2DV*LO25o+;`_-YkXnc^e!|tXC zUlK-d$RhCOrj+u0{bq|E=;fkN@bSJ>?3{)jrQ2{yX%1oV#%cGxRnx9{%W=XMb4C~&Kn;#c?>wkw$)*q{9c2OMrb*dDJSImvEd*n4pv%Bfqp%X-+7x-AItDl!qS5%Qv zxYCm#Qt0FmjO@e@Avj0n=zs$%B6@0EQ;`2d6(-gs`1j^Gd{mgZk^xk>E-Yl=+Z-;? ztIfLC6p3P&(hM#x#EoC!;Yu6Ov==lv_|URha^bHV7ec>JTN+j}CG-3B$o){Y#F_Do z#e}{U(}q`*CPIOYQD{&blwS%gdV63ts>>ftzNWIb^|>#Z_AZXx*DiHr_R=I>4!j>c z`23u`gp$L0$SSGebyuk!&^ta^#8KPiY~}eGCr-lYrEliq(>*M>#(Jvz>Q|EN0uQ6% z!$J&rG51dWyc>SE1MGA>=&z-~#KUp!*_f?AC_e9*6WwP9`t2tOiFqDEe)9j0CK8_# z^?$g%KGs&0XY;umY2Xrkn=bIaxVD_r>k*XB%>?Q^HrjYAo%D8l$A4W?9I$)rM$EZW zq`(szB*QRZB7bB$kyAWRY=Gj_J&hS#N$-Yh?8k;+=VJE%w``uYXD>@=yQ_smdaqee z=kFi&Vypb9&p%x`Pp(gb)k$q{LLx$v`u*b_Js094M$jY`Rp#_pGVrh-U_lxalRgEt zh52KZ$D(buDZ{ab+yNmah1#RU#STm@&|$ff1?zr)fBMex_R?}uel})M$DHwqoCkMu zn5rXjX$82U8$pE9m|tmTa2jWKSe#6@)KLMa$FozbQ&_@VJZ|I9!#dx99`-+(>}QPO zm6?ulSnlU6o6Lp_#m&3XsKcCk4>>OSrV*j0w$}SX&C1B5p{ckA=8fOX-y* zYomPg69&v>FIQVTDSXi#Wi6G*F|#<6&gCCQwWf{$c{-5S2a(vx^kBB~Q<1XU%an@< zydJ&Ko9eSkk|`JYjgA6f+`!Ns`sU$hq^hCxOhk4BviqBWED3tvJSpsB7mJx(89@q;1(lAw=lwK&d$m}GkYFIGCSR&t=y&2>#(XzLjoXc>vkdva*R>Q%% zZucw2MAs~4eN5|l^z$2GgP)`{e5DzQoV=*PALq)w&m&6n?71rJfq1C--^VvN!|!v- z-6b)`_UC|FLN+B(N_eBPCzRY({&}sW+_+pFpGS@gwkCe>hO&p#2KagGJ?f5XTu-_a z`-gG~gf+S}KH5l2rp0x7Wa`JIj}t6ma`v_t9Opkd2tRE9Q(*HEK%=E133CZ^zD$_B zNuq~zZE0OgmSj^}RK8_=U|P}s1WuA>N{Emm5ut-hce|^r9?D$1qA~T43fw_oJOO<@ zDU=4?p_q@J7W)vV^S!>85`d2qgwJ{jK1oauq+7}9H#j<)G#{;U?^~~wNUxM$NN1K< zC$@O6(95rKb4-+I%bBbbDB+u-ESqg?xoklm#azKn{NXJ&TZl&~tjdA;h zHI1ui{%1o)x8<22A9SFHy6M#TfLUl&so%$^Hkl#C<~@(NTL0bua?$f!|%i4;6&Hr-ep35D`L52Rpju!`Lyeu zoc{AKrHM*N*e?ytCN+i}$i7L0%VY}O{ZOiQ{r;t>W-`h|eBe}|;+|EMIuWW&siuj_=fhje` zxu)bf9ATx~j^*^LOJ32gC{OHCV|jV_O56UlVbw8{ynL5`NTVBSn3qgCmI*zuDKw2lr~-~yO6tOQM8*-(K!`1Cw6kf4|qDtAF}NzjQxcf zm(_By2zMm(@y1TJ%Ay^C{%E>dq|v1n^*SI-Xo(I6{T zPXC_CO-)+7|AAI{cR`L$`HY{daNG%H^MSGM$6~XG-OB80$d(&MTdLdWp+%9U{40&m zYa%I zCK!{TRFv>N8A;UWY%bRCaB&$nV1BvTS!vU-rD}qNM9TM97l3|Tx5@04;(Ctxpm^HB zXGID7!!8MJXVE+cjJez@Pr&4e=))Q#Ud#4{e4{w6)5C1-+M7**KiduVjxegTQeE(E z@VFjydQ6BZs|{0n8)z3DDuBQn9xpZu_2+`etEXK0T|Q;TcU;V}6;U8URzhq_o6mCTM(c zmSM-1xz_m^Qr)Ne>Bl2HUgg7Mb4!{j6MOq&e%z(lsHlN;5S-1hWoaNVgS53~ivPMZ z*#DC?)KR9D4|=bbhVO~VyX)`O)qnb&*KB#en%P9Qb@4Y6(rHMQyX&&SNoc8hLR4@u zW%rFjk0fr#X}FjcPvgxx8X_z9ENDk_kv9t><(yl;F!76c$6eL2v7D6)Tg zfvq*lQ)zI|(g_i<9Q0pFG9Yo!vraO&o;l%M<$uVf+pfUaFe7qCmO{0*N+2Ug(*EY8 ziCf#^BE(EAp9m$SHQhy+mPZL%=cf$OBcRovfmldV=CMU{s4hG`CLLw;NMy~P&i+1B znJ;rPeX0jo6K6=Iiw)V@%%+(k=@5u06qP7eawA}`lJ%L^SzkD?!^h+3#5;KBbSml> z0XtL5!|sORW7ko^hHA*QTc_JcDeD`XR+QjWzBQBo*g%z0=$Rp%_ae7<_4B2eMZg7W_nc54m6&B>2d1k+<<5rRJgxnS?ltams!1WIq=+k zv23WDtKz$SZ7w@id|7LqPGTQcZ3g~$gPU9>#O`~I_rMF%Bt6T z)}3unlqUY-tH$ z7vV$m3m)EFUUryBo=+OxT3O&`HL~p3B)iLrJf{X9%u0!4^qf!-QjN-Lfh-Fx#|Jz( zS2uSeUS%}L(s%?g|9&jPv62slM4E7KaESa7qO0u^=<+EF|H(R|O&v-^)J6TyxR+;F z7o0=MM%C?d0BvpfjzAsWc6$n%5F2kzG_7ooM5)@>QvLW{Y0xxqIwR5;zV}Rv@CB#v zqt;Kfu~e_Y${sS$zlE-4KX;R5jv3iGo!v7%jZ?Tc2Jka~CckOPQo*F9cns%*=S!4I!V{PYc2@TCAXT8x=>skS#|A!oEG0ebF zXY_L!1REgOw*VZ5ajEP`yW~f@?({8xjp}2DD!w}|o13$0F0F6t_bqmL9ZU76ER<1i zFirsniOk|MW4bf!b$2lwJnV*w^s&hmvYtpzT5z#&=1t!!`QqK^(kkS&&>iFk{OelW z({}JFQ@nF^b<#H~Yr2>4b_rb?=9dWEeA~Rc4Z*X?i{r z`E%%aF>9rkyg0*Zf%M07*JJ6y=gH|7McKMzA!cjvB${0HS1loJs1cVUPv3H?93}Iq<*};)T?K6H zStjhs^JU&~BQO2B>g1JU@;{%58w2Jk`;LulZ%Mdb__7f^C*r@}N1*^sU97CesxGn| zyaDM7OJ)4x8eG>mr=PT!S5tNL=bvpkaV*x5*`0NGFz~wQT=n(l^fud=V~Fe;JQMtW zB09qR^dqAnIW_EcW)Klz!B}+fVKrKe)-R}4+ zd?cd};}oA+NwkvRsLLEy+1IIlp9Fz>zkXc_phP0Re^r!^M$N04dkWlzuvBdC+({6e z@%E^Z=Hqbj?r)19zUnF7f*41|4AYIY`+m zEQc&;4;UX3Tym= ze*dl2BDr zH%JLS;n(c5{7`+WBgUp8Avg>uwWnSzoyZ~slr`?SeY&I-VZ-Tg0JLsyQCdjW&8ggi zNqtvB^FQpKu`!=$2{Ee?rt#M(oSWjF9mEI^grj|sQp*pD!8OHNT$PlHzy|yvramMl zKD>Ttjj?*$R6#w51Vef^Um>8|orH|E=lvuXw?Ab|L5uY6hVXHxxZ?MKfGotv&l$nK zEN$@YgWh0w1f(nAbm-_E26a-V&%&k+v06KOp87Ih0Hz{Mk7YBZmjEjp_5?^Y##}q@ zVJiJbTUm-&tw{~eqyZVrw=)}~U4Nv@%b(eYUegWE>GD-;9KbGvxn`gHj%@&2{Mv4J zd2}6HpI|-1kneFDa=fc4YWe;)S>u>LhL#NwmUrGN20t>-e=0rQF}U7jr9}n!z$|M^8}0u|W0h z<`fRA4GS;*RxI-8bncPppDA)XZj2yRKtng1WtH1mG2|(+D*(8N94qNow@JB*7M7av z<6Z2k-G%bE7p3EwbF&?*>INLSh`WuZ>gAZR0|Z)uRqD?YDxGE*{{JEzf+1TV3tk)drPTTf{h z61mS~%c~Op#Y5)+rMf!%;TH94o@!LPbt8kbm(TvPmJsnmZr-e31a)00@Ci|!Eo5o? zdPj2^pNXKmWyhmvuxp&%*8TKpXTzeSp?X;O{XRwh*%+UhWTn2bB8>wg%A5(hU*Cux znucP^ch75HW~?BJr2DJ~ol|JJsemYPDALO~u ziPVq)p&_X6EsDJlaK5Ri=#S;N1qn-K69IUtl(d~4zUC;%ly*Be%0>n$hf9B1V|5;w492B_0IowYOs)2p)gF|o2E(RvWNg>_{8FkZ$LP(`& z^w||@&7`!^%!;H#CO}w}NkbHn}PYow2qgc|y}VxuVP^rwHkk|1-3&WW;MFl`!1ZE zE1DW!Rm+T=#?cOFHOq#sJb1h8xTrgqu~K4WVM*9v`amWQ=4i3{PB0fP$|udhBYdim zFKAmYN9&(zUSbEVUB`WS9m{@{EMUu3BCm8qzQQ3)jEo&A*C*tMALu3A1)!ta%tr!n znMwm5EGacIXq6F8-iUi8r*%9dWEw%2Zo2|?L@7~qV{F&3tb{?qbZNqV%hin2oFzNjO+3kYRZW zy`fXy)FCyot<|?XEhf^3*@zjITY%r_=VxV8XFF!N$ihF(vKMzqWpZfPl{kOd6e4vk z^FM}^FKMPeC`Xd-sv;y(x2|gXOE0J?^&?OhTuLmkklG$rSh#}ci^Q`)Om)X&VW`Y$ zuP}Aq&gic)C~C1wFeUJh-VnAnY#l}K1buDg%@eH7=Ap+}nJH!JQnmDnJFg(~xk`=H z>#{2`x+$O20eMd)XjmHU7L1qM7)6xdY(_dDayj1um(-)@VT8C`V`~f3A~$;-YRPK6-%$-j3kUJfA%WM_|JpyT&AsFXHL!gd|?);{ZN5{FeUM8yi4m*oVH^XBCCrVPQ9L zig{#QptxJ~+18*&f}+vERBKx7&q~x-cWX_DB`blitY^+!+{n!1xNFv)oyig2tXSta z&Fmb;*2|uX1jLL7OkxH?8vEZqo1%~wn7#^owVD|ErDB|iw_Nn+s^O<*)MxASw1f#S z6j2wg)?`yduNPBN1;tYtePl7kJ$)lJYB@=(sFC>X557F^H2GLDTSKvn>3*7Wx!wb` zH>8u0KE77++21<)j;#(YH;I-~b19$N!HmIhojcG0D2)oJ_Cy}zngv(0qQ$UEE$eJ- zevc7VVt3!9_u@7dMEvLkiEL4)``?qypRMfhGNJy{!cB7qkH!yoZ1d+{l@v%nrd!}U zEzzxoko~pV%qc_F=7;$ToGONnEso=FAAR#ukSnuObIbVs3rK@Z1XPP93$O_PTX9$Z z4)xl`zge<{DO-v#8M`558B1f1r7U9|>r7NQPK9K9QHG9fhK!7{j>r;OvJ6J#P-K^6 z8DtG%kUixU@5p)2b-jPV`{Q$6-|P8)@8|wJpZmVA=kvXvJvuAk>Nti8*@w&X0^-~$ zpbfJrC#YsD&mgY3W60|tbM&HHn~^l?lG~ys@nY2E>t`IR@M4<0 z4hF1D7w-S?bm2)GAHkjlr>A(yRk^R^6t}-{85s2V`mME+GvSeR(wFPnfy<)ij3fl{ zi`nuFi-*{jq|4A5UDkd!8}0}l*Xewp>hZBO#G~2N=yXRbh-*Y94>yj?Ou}dorSi< z2T7S>KhG~$Q?(&{=Zh((Fpk&R;CXKjreZEdKd)PFjz3kA%EG32yh&6G`ob_dtV9t zTwjoJrPyr6?@Gr8dXl%)zhGz9U^zaKOZ*t-0&CyCtRP8gNUMQGe-X2Vh8k^1NH4VGF@9qMQ)Va47QUhCW3fmf!p>h8Gb>6Np!znW;QTK4%0&rcnz@l8H zwp$4oyw(;44KFF!y4VE4q%{s**7n_3>6yD<(zz`-DLF1FjUF6zO&eA8Su5%FsR}NVy#J<3SVHt&TXWr@C*j6=E=M41sKq;+ zU}0)f^LGKiLYdI>p4((cd|Qigh>lQ7vY}*Qk>bgiz(r#tvFwhgdgM>tY^~5tjk1NS zSpjo?`!bHNZVCfwiYddj3EhG+&=Pzsdp1c&RY5Jk&sAh%?AQU3ZGSE!3rnF;U5PYM zy7h`?aBe@}Qy8{5X(6IgLr5)YZlnVDmloF}SXW8I1V1p+!8%&M$2>1(qQ;@o>o20` zZHmswuc&u?g_f-x5=g1h7B=r05AHuh9atcCT@wO80X^hu+!?E4H}3g%C-;Tb;i7A4 znP*SwNYoBM=5?V+jf=XM$-b|2AEB9~#P>$!iRAW#!Leu+@Xp9xM)M#|?|LJuf@XdqXOAlibYU{}WVDKD8`NpF}GB)gh$%phA+2rC}D&f|(O;t@4wQ|!; zvMw1pPlsoZzGb#F_7NlU_e2DYo9$?8fkgm3UcN`L+nq~9Gno>&(+emE+*UgZ-ADNS z5K#BkU+&cKycjd*$F!c(PP*W%K_hj?K|k|1Mr%8#Q%GE8UCtK%G1)pjPon%IV!l6p zR22cU=p=f?4zVQ!ZZU3Xb7u7VEvk7XQz#ssRp2Qm|BZCR0ZXaiK5m ztRxjBrtv-2sQ{Eu^ynqnTwR)8!q@BF8Q7R@4esR835>a0g=E4!lCu?ShIQ^7c(jX~ z$O0@USsq{_n!{(}%dKUNUm$-`9s9G5`H%rwD`dQByk!I&6%n7#t}9^J^*y=ttg^bT zT>z}SZYh0SYD|%b6SzPjMaw&;BWBc{SpZO}U;{&HH(+5J+poezT9%v)45!>kwJAZ# zd5!HWi<52dUB!XnH#8g#fOV(R1RO)-=amqbpulDRo;(BX_GwgeW&+l#xOL^|UtjjD zzH~#`sH!F@usIu=h-8t5?5r--D9c0tMwfLbn=fS;%lUm4@JApyhRqM z4)BMxniJwOixays-n#{?9)(ieiq+Jh1n;3=ZUiA_S^TmxqaeK>w@E@mq^IPGxOhQcu->kHw*{g0XqF0059o(y}sWU6KxEOo2`4iOuOqqJqMt@cWJt5_`i*{ z4^;Fjl+=W(YU7z=f7U>BPdb|2b}AxBPj8&y%Se23R$(*Qy?!|!EYMKzMp03zF)u)6 zAvuBE|2@{o0dr@lU$KGOUn?a=G3OGrP-1#Pow-X$0yCNk7~#gKLA`3nR{6AeS#whS z8p`A;J&x6C_+3VV^kUK0qWeX{c6F&jYRfzrc{+U21B%q!9hc?Es!`To7B+j2J_(-? zb#Fh{BlN}C`{sQ+v#B>{F8PMVoAArDL=+TPTg*MHmsRt!5;V?T2IUQ7vwHUC!p_@KCl>e>8Z zc1=$FI4>ABG$$~`W4HRQA5$I(aS(G3DNOyORsKC(DU6Mlz3^Am1=g{5VH8dCF zJO6X;J!{>07i*aP?s)dI_p_h+?EnQi32aPaOf)n!Y_KFq5e@CZ0U8>5*Q0xPB`;2_ z2=2Zf+e&IUprPTk{QCPRngNFd4eccw81z=zHEDasMfus-6vqBqpJf(`SuB$d_!-?} zudc;3??$F1GcM(MZ(>T^tSo21dxPLQmy(RvpC1LL9nbP>8LwX7SNMvq$N7E{pc z6G=ZcwIR4y7(Vr@6b-FvmHXAeYyrgs&E}A&rvLmZn%8Z5LBS39AJxoXkmiV`)oBPn zg8PxY8u-&?gA@&*-3E=2p1poOY>{6ES*t=hJ1vMXS{#qv2-eKkOg=&j25 zP3TX-?*vC?{}LL^PyD-!X@2V%v)eimi(IS+nxbi9Y3RR-KN)uxVfbZtF6d&zWlzP^ zs2^xP(EMHB6nwMMC|IXDwP!B;vHc+d0GbS%}iF_%vE$xsly4)(4Y>#Q(0DcHs$%{MLXHK4!6z z`MA_#e@F{|d)SZ`eYh9?{L;uf!-p^c?5wue#N-0pZQZO<`*mZpr#98C<>~5Tl^#!m!d3 zQ8w#DVNn#6$4inz5J|tdc-)xo)ULP6nbEfHCf-`Z)Sw9~h>`>ry?*Ci)OuKMRGKR+ z!z?p;su;kWaxdr`7-TQeTQxy&-($?avK_zC6k@aaYdQ;Js~Tmg?i6c)#o)z+`AY>tR3+hh_> z5Hn^2!nz&KrD=k4W*ybD3X}MY=?C85bbE0v7(Igj-qdIzwjfZ!4aNOQKGE*S*Saj4 zZ@?zZk;}%SKhK6@Q-*YA^63c!%0`dZfdBw&x(9@;Uy>w4C|PZ8%BZ!~T+tc%AQuXa zla_@NZLq)=88R)}*SyNTvq5+iP9pE=%Bff1=m$gN(hP#3WiYqJ?(Z)$$}7HaZ;HJ@ znEDB89dAiPOTgrX zV9ShDc2i0D!0d)wh)1Uw5ya?16nc_?s-O0ovom^st6K)grZ|Fr%Ii$ig5H z=+ujYTtHd~_Ihbk`kSN`OB@@}r@zm*FpQVY!D~GuTAP4s!-%Nw;5#Tqsr!;IBFg$= zfzsJV$N<&FzX=2%CTYINEV5{Nrr0PxMcc+Fcu*zwftt14d6|c)|M9yuWkd&T{nLrG zgXNt2^v#zBqlgc}j>hGjlrYJYF^^Fj85hsPe=gT3N`<1NgNZ~hH^x@oM{qkk2H&Tvy*ds|<6Ay1)DbTd@_7)NC{SIWe97Aafd(%`vGn(U+=f^rE=n&qsw z$76_U2Ub~R6biKK%XgmC!Qa+7m6my;>iKlOiHYqGmp8uJ+5%|BJ3QmWaxr8n*b!OA zQ`OwAmTGx?GxH!+QvSkQKIW7fm?Z*A3aI}=**eTM+#R;3s2l)N8}T>j;zvan@NY2n zenry_00cO5qL_vxLAC5)yiS4RPyvE1jf=+w2N3~TuVd|a5y7O?_zvNecnQO;m*&^Q zAUyURRn{DQKzMlqv;&c$Bj-888L!;zaA(Shc`0Q}ybJ5}#A@MLRXzlG|&JTWXlMRmBo zv#a}gc+2qj$*6cc5XiZ4r1?v6CH_i8Qn=g4m3Q*0r`wEiaQ#3w8|FM7AfA_>$~lK? zYNT$%pk`M@mKyMnenx};{D@Xlt`gc?07{X3*{1P`kQ87yq9W6yLJ>-j+M;}bo9~Dj z71xp~m{w3AE{r&|YlCkh^ig{HD zQmS-#;O%KPf1?^6*uoRT$b|E~DD2YoJkUv=Q(g}nY|4N-!>i+EfTV7x8AuU&s1i1A z)txJ--@K!&24e3`bQ{$Cdur|L4mt|$FbP*k%#@j@@w4hYBGWbK4_BtFNf4#73-?ea zz~jX(*v$m8$~!Sucj`TG>v<91vE?T}*daE7z&{j6J;y@NYYtD6T;@O=AAmJ!rNLIz zj_aE2o`G%k=K+na{)}qm%hb`rtivT zWjk}M%ug{oD?1N;whDy2w<}42j$y~Amn?1)`j%o1yK2lWPyV}#90RUNr(6R5svO8Z zo$#X{BDKMBAh%0dN@*}KkFQWjTV1e64*s%>w?K-6qbI>$^F5okO><;iRVy!mcZ0tVKC50|1_~z|XqF&YWk(EK1bH+_)4U*jiMM zI>Oux*w4)4P1mS8 zCsnTYo(CQn8(rL|2Q`K-R+g=peES$Qhr`OYzaNz9jb&p>uN*GRY_8Is_x9gf;TRyY zrPtyLFJ%Xf>PMXv2gmLkPPArL5NtD^^&V|U1|`=0_*Y4BS8tSgLUTjtC@sobH?I#d zeU4X3u@Vb}Zd5f?rs~D*j?-kQfNu%yMD$GQbcb3QSfT&`0mYCs*v&l=M_~W zDe@cw@UZQ9Kvy&WGv9}M$U+uvM%BVAQSF!E*BwLc5|{<*Cr3!G1}kN1q6pu_z7d@~ zNyQ!PSltF(jf@YMDI2~YXfO-bOE3CcM?H{|z`RgS$eLMx;XoA51ETz5Qspg_Kj~xd>K0k_ zK>_g?s(qw6Yg92+YRPVD4=1;tmpokiBw{gK!L{oP_-jg+5mE>sV;CC~cD;76HHwFd z=Ze^30qv3=Bge(bo@L>u){o%ar(gcI62baCRNI~9)bsPm%tkkj8aTvC7G9+6e3IEY z*APde%so3X{|aFG>-lZ%rHRSty>ntXmdY&7O_lfHzi-8G=Mh+1|A^!w_-HBYqMO$0 z`InZK0na-V|G{Jp%irUWgWAB+py-#m`maym{F$9k=;c6#!lQeI2mF6cWRd)tZ4IGb zku0u3ANY)(CI8iwX**S1t=xBZH|Ey>CQCmqYhVF!F3n#(p!TQi;SH zSNz}c&1c>QpBY+nXIZ3b6nod(uZ2Ua&VtmK#oCj`%gkEagVsW~HjM3QyCPBErj4(K z`OCX%-|hLQk}kQEyklD9I}O0_aC)N#+qn`Kz7}C=-Pt1kHOhR2`A%_T;Ul=`KyX&~ zDzrh3-J+m`VA+o_6)nZLR%t7O62dhHj2?OU(D?_uTK9?+yM@W#z$>LyrgQu2^y}0o z?D=!i4xaOeTWzp;y9`r{B6$Rsp!);hpTLNaEA?)c|^wZU6 zcF0WdTW8KrjIk^4*@1rcUo`!k5phNePTF>tWY##oZgM~S`ZiXTw1a@yQE!#qwCQUR zR}!dtI+tlmK3f^wQ;Qro%lOjcs6ueAaZkbs1d>_dvRzzP&`Obvbu*vx%e^Nye;oUe zph(^OO`Ub7r2cAhn;aRwVNrfwNB0o7B@fMT3~hFI@OVX?`@m6MkvHPdG9J_eznng= zH>u|r33hmr%m(?K;Y+9M>D``sSscY)lO7hbLh0b?=UgT^eKqm8%naE~%A2jc8HrOc z<(mE#m3k_QI&`%^f|tM-kHV}jE)}Z%;^O<~!T6uaqJr~7wQqatF@b#*m&@(#eCk?> z=FEHAh!at}FVbH{JQy$#SofI1zqj8n;~RUZ3GN6xJ72PK)@F>5*_pZi-25<Sjg6CA&ie6fVGuy+i^XM$4=Vr->Uce6!N3dgKG zknbT)K3tgtgKACGMXqq?yd!d=p`F}U#Dyc^+O3W?+K2pYzC=&U_DlP(Pb;mmB8>Va zQYhvrN>&Akc&t@(f>RqblC!8&D19sg%AB8Zd7U^^i!boHHGUOhKHz6>y}&YkbkB+( z6AntM3k#E-savT=&Iw))wk1u5p|0Zhck&xtk;5`NS=2zb4IcL!t>W9_{3HP2fIQ5m z>daNU?1&;1u~)-z4-nq;%5*)hr1|3H(eci@xdkj{BR{aLxGhx(evT4$ZX+0*Tg|s6 ze}g)yOx-Lt27!7!4+Fn;mTP?tCSdogr+`JC3)i@<0U<8{V1mT86L;g{3TF)CQMsjB?4#BUY*6mTqAxB zL$`xYViEd1hex_$N_c<`ePJ>|BRBmo!ioo~HyI|q32Xw49aAHtGR)>(he*7>vD*25@oPi(hlfLnfX`5|pI*5q-7xo85^r_=^EMzkKZGS)_&&c5w{1DNlL*D}r2P z-v^Gwr=|{zRZpK8zJSaJ#uR|*Wvi3&sRKCTO9Cd%$O;ftKDlhW!w7*w5D3%bcH#b) zq-A$O8;iVZO@3xtL!Ced{D52_ZDn4*W~=LED7kNmM`<}8;4u(D86m}@+cWeuoQt+h zVFk7d1&OBBm4BJ0_KAT?Mimh>%ElutLM8~k|JKK~YJEbF}okuR)Le@8`e)fY2oJpR078-I`Mrfj5}riQ!fTElo@ zePm=Xf9um@6iS!P{`8=v!P>LX#DQCJ?t#hJ-o#$LXK7{Owz~(u1^4Y6?pqzN0!5sL zAG$gFZp)W*bEMk?A90l|V&z7elSLoOS2s){@25^FITLy2{aTp)xdF>;PVZ;~*nUeW z{>*Cd9^{7z2amsM1oSy2xM1#eV7Y-(@LE^D&9#+9wMfWFf4WhN`OzXaHg5`paKp-& zAGX^y`gx{E&V%$CK5r$>>&pUf9HxA-D_{f%aR><1f_PE`iJM5FfYf~uQ@th>FxPSU zSf`^aJg2T7Or;;p86TfNwpG;7Onfyp?*`w$SiRF{PWp|$M-!qHxGB)(w^;SJGBt)1 zd6NzK(;`Yy32}xS^9$zGCLpSfKsH6pya1@ABumkC0@%o3wLqGbzh7SNd&T#cvd_oyo@HignIvygJ-w<@Cc~HJ>o=MroD) zof=&j$v->rV+#NR^C7)fb$}zL^&J>xbwX;h&ILgqh9lJAPj%+4R>ix1ATMN!E=HX%D<948) z7L&!ra!QI2_1o@B;UeJ#ZzgRiF@B=33A(-8-31j~zBfR;GA_o1Gdbm@OH0lYVHR(u zIo_p?g)+Jw0B7ANP1h`bHoJv0?X1c+fQaMY8M2`qK$YvkkCLE@??76*{EBogv$@H4OPeGyVz+gNl6mUS z2(*tjpL?&xqU-PQt}7M?UQ?u+805u7e;BV??Fu;;krec@Oj5rm*P9{HRS-rq@eOlBmr!bwzT_r=Aob~DW6(J#B8P!~Iwh1UnG|CGh|<>#Ak z-bBJgO@z3pE^6L&szg-0UKmJy>>s=huYZ^S&k}~_F#2lgz6&AaR1yQ;d$7iP@g~Ie zR*{+lN`rOkEw11I?Lkp$^`*kib&AFbKY_EZ2y~?{J0t}bjAmsxb*QuNx=Z;` zXs&fq3?=Z@NzRLm#89L?VLhcuUy?wk+DBS$3rTL%F-sS@fY_XH6qdA+!6$c-s=(ZxkAV zauxL4Nx~5TfH^UAc28V)p4%F9=}4az$M3wqm3Rf#vemowQ^_B-4wWpfxA2?&+{*s;cShwW6RaQp5F#k{J8?5f z0#<=ZcqE_icrkz0FVC>_j=BDuUO-V#n+G;dMtI!U&jE^mt%Zlcv8 zKeW(sEJPY6tq4pFaa8mK|qxkE0LHLbzG>OIp@^Cx}>C}gcL2a?FX7) zGP{=V3kwKaDcp7j2~FXtDOcKaNAiYw+Q~r&4a)W1%*0}PEVKc`$l>*SaejLl_#ceXeYpEf4ygO)OQm24=( z*_zH|_PnuD_qnP5JV$Hb(8YAOa$QdUwt2B)+@$*h?Dg4nM5F4g!DIwCk=)Lbg@+Xx zLpmdWPCW@YFR!5Pl}6s{e2CI2yYcE%7xw6`rp`)*n|$Dj{@l_^{giX&HDXZ9V+Y8;f*ibQXs?7`MaCTsy-qje!=QY}8Hd+Woja>&%EWj?LPqjyoBANR5cs>B? z^89=?JKP2j5Wf2Jdy0HEw~hiLChuM9Woqi>KzBqDBRel|PHsZk?(_L2g+!LDuE>S( z@^D3$gAbOy!Ra)?P*`=3j-9Ks_I0;OaFx2Ht9|je($lHJvWC>np1bks4NuodjJd}P z_z{WFIM~?yL|9)!jd}qcSYED^X^?)K-|4hSi&Tn{2anvU%KmQ2(a;Qr=j6>i9YxYO z$7?6X3YmY7%iU$5YhnmNcnr)%+Q-Y>g9+7RDiYo|2OoSFu6pJ9%1hSMrm96-}kZtBj6kSb2kkk zi1Hcmc}>i1xH__;I7iuYeup-OtaGsZ`->&zQSyIgJWe*qreW3t>Us57V~b(9F>`(t zcNzXWso|^Lo|Iz23(#aMLzzoE+zDlGqSwskz2Kc?VKP&uLdHXfAuXp*Q&Ls#j@_`~ z;o-Yxf3N_{pD{xuYZYNpokmFtN-?p7M2Oqsy;<*rLe9ZfZ2cSsSLgl%OGHR=VYF{R z1X67^M>kNQ%4w-cNv%6`Gm4U5jxdibiEM{)q*g)Y8?l$|Nlg&p?K3w-kI~}sQ-)9u zqRN}voEyCoMYpTjcWUv`$sy%;9TDXPv9U<_+f2cC##ewoWmt*lALGa*K$&&$>l|&h z2N8Lt$kM?42Z+gGP@!@M&yZT|YIa-7^hvvkw9fg3`tHemLAL;cyL7l^bab*yhKbh7 zO~>4_rCv4>mE4<4Gi!2nRZ`Ce1hVce=QZ>_8}Z_}i(Bk$o$A8oqq0|xjcMq4@i|w} zVRr4@x5o?ZTZ>Z1iW;g?avC9-Z#Y#LqA_34ZstpuM(T%JYnN=dQa7Y>aO;Np5mnnUxfp4m#ZTy_?Fd#lZON;Rp(9^z%?n7+H6!IQZ+;gIDAjj!=P-7p0{ zvd{WPXh&Zn`4VWnIRQ}=>D<*`3z9V6BO=`F38)oKbYJa=j^L7c7X^j#IP`l9^5vn^ zAaha#qSbpQFiti%TOP}F61AraB&)pRZeubYN_UZ`aCjf#(8>y6jpwYbgQ!EZXl|lVzw4lLBisX*w;vJW!6&0Wv*-LT>4j5QBee5#{mOaAgMK`U394 zIjEJ@G}eD%6G|(%&4dQn;Sb#Wu%-CX}9)A!j3 z>voWeieK_&wyf`rXwc%)#vYPA#69?daIaU`nar1<#ShWxBLIofJkxT+;C+lZ&pJKH z>hkSDN|Z^1%8I_cBZ~ryso*KqxRi|%mlP3BGlF?2G#&ibc(7>Ev8$c$U*lr5QLaDhZC{(@1dqhd!6>-6R)Aff7Zdt?#SQ zWjPs7;KVuCUkOpaj$sUtMK;zLpPub}aL1E#Udm@T#%Yft07?D}WL|U*fZzXsMpQ zoFG2lD~0O5X^8C}4&7xWzC6*JXcvsRYRdIMHU}BLufbUMfZ~*KHEMv8hf_s=fV|zV zl{|=wu&sUAd0k&INprG7gaHt)^X-}ai*S&PwV9dd3pcuv3ZMJ_&Rk3aUOc+`*<@ZS zYN2Fx1!Wt&e0M%x7nGL;a1di%F`4vCFu?a<4HFtSInfH|DOb*;t5bes{!<}`*cZHk z5ah&jC|AMa)OJ5%S20EQNdT1Wq627|4b>)n<5v(ufK z5jKHF;7`^)QBZXB(aEM8R@k=-9IXXEUby0HYwqXyly^xMQs{*WukZk1U!u_MYD)?M zVAVTkQ1oM6rSFlFcUjn?8tp_nvd``p-OOU3=&{-6UM<(J$Ii^b(4Ij%LLY5LJRAy{ z0!7E|sfIF@UAIuR_j<=L2fjq>rD``r(q3bl-5C$Wh7lJgYfcfBji14tc&_~)$KinNlMg~%d6u$4Gkg@BX^S^STQ>H4R0?!P8#Y_F0f>i9%D0ASnG-e zsBt9F7xsv!+uK#VS^11nmB6Fxnj?bMhr@Ee%y#&RUFV~r$o2r%n z!_N7mSiEn-BxhFKq|%=f-1 ziX8^8lDW&m4w<8eF7@#HjuUKSHvp@Toljo^ zN1(X==KJ{?vcfsq;H^?hY;sD1E-Y5&-o>$_@poS(jA9&|!=a=mVuZ9pzk)m}lu+#b z8aw?noiCS&?i2GIoLwE}&A{!ZCbkfzk3Uj_f?^YmRIKF?zI&f`Z^)9jx2-I<`p0|sL(!9MP+>*XYsC);Nal`Vhf^IrnH{me4dnGGgN4$@cEr?uNPp-Jv-wyHsj*q%NmYf)*E< zRCRXTwMjq73$KZdEEH|arym+J@_8p(@StyUq`U4*vhue=2MV5_@6x&Lw&xdC@``$T zItKaBowmjsN08tuykzBq!6=o92dCKk+w0Z|_$0A9%$lwju9{k!cEG*up0%76qBR8rr)mniu!~w>O`R;Z@?# z?lJv(erxvW)`b2${N1nT_itO5niMk|DDHnR8e`{>3%c_fg8q`d^EOiE*DsIQhnkf2 zzwn!PVML0tMdJ5XpG2lau^voT4F7wo6*_CN?cDi5TuALHtTz?-{{jACO)yCe-YTTk zwHlQ^H6__Ywz>bCzYDRpOCt2C@Fjc%w}N5(RkD-2s%%X6tgx7#nocR={w4o;>-rrj z|JRspi|StPAFZH(M{p+iT_#al5M`bgea4s_eA4Bgcw2HI$seIjyBy>eC9 za<-ndckdGgjGYDr)fjDmxgSOVH z=Zc~?wDg58j^06pS9@=<{C_!FXBf{9NbvuqQl9@YJn)~r6ofr5FWoEN4gIEgFOqsq zj@fp2D}B7!{uw}r$>@RC@Y2Ye$f(SNJzwughZw1!ot2mQrN#g=`0Rc43`}XZaYl%9 z(=z?Fd3p8Jw992{O( zZycS!@fuV##}=uxwU}*7T*{HpTxiQ=p-t0;Lxt3v#4@18*!BaWA$H zb_TcfdvrE80CG2)i{V+7;!xZCy0K^%;)@{`|Gt!|X_sZRHf-=mzloiOw zw>w_#((bZ*G9FtKDxqC%tAE+9RBIuh7hg78u`&`b?}<2a9nPvkUP2P)rULI!eciiZ z%u}{!+bhz8CMfSWqoFSt>t7+}yiyA$?0}1o+X%`;p}2KwTHxyb&teH7&$C9Otx$

@y64lwVhqly(4e5u>C;10v%Ov*5oJL=qD2 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 z zsNi3{54Qjs*2Z&<5WepE9KYD~JLuX37*+j7R_OJzR1%f$e9&p6En=p&`|CoN7zi4g z>gsYEQwMu{md3`EXw^C0em!!5XCv;w;|#FN1^+BB1Idt(U=9&Prt|3a7nwFm=W!j5 zV7)&_6PfW4za~dUCTi=e33zI}P6EQVMHmnj)Rn6(CUeM>Hvl57u%NPXQVdUx&iissM!ozFcshNq zcVYVe?VK1TdR)VF6u^`@1*%u=k&jKsU8H#aj7xlNb@h|;E{vd|ftAyV;EvPsHV4PD zOPM8%FR$s)FO(z%O%xdadg9X2yjz6iaU%Sp%5(z^E0rK!Q@Q=W7r>J8hjBUNs zEs#A2WoLUmp#myxFE4|8TG$3?%HG%45|d!s*p$%zT=3iKbiMQEu1y>V8MHLQ>-WiZ zuR$TZ0%VYniAUi*Qn>05sD5p^YE*_KN2xz`682^;&7>}3-Y(65BxqfdTS>{}(4`{V zu_N2_C`9Aexg?f4m}AX_iQ$jV5J!18kh5EjQr7nMNUJ8>&Y#O=aN zctEc+&Y9hr-Fu+kV!DrK_nq@ymX6_J0D`?eg8Rwnmi*bZ3-8MvLDBl__3fk>`w61a;3d?q!;ujjm5=5$`2j4mCJ0&N(2lxj__byfYQ zMy7#3Lj*B$z112lt6)2yFDTX zIZL2^2AI{gl&{=xOj3Q}@t!w7-OZf+)kx5qjgoQjoOpN(|MB~`2<(TftYfBi<*hOBL<8Vcf(;laeH>*aYZ7Uc}Qbf-E*HoI*D!-c4a(!wdF)tyJ;%Z6f`!)?(&q<0IB$7QCY>(J0i}4}` zz1bk0-7Kr2vhnP8UkH{Qbjm!(@O=&ugGANmNRX{ro(K~D?U)Qe z0R0LQ6Xa7w!=HXER;5;dd;lEDkdYK>=m=sOPr94eke;61D%GnE$KxeL`akWGQURZ< z1&6{M2HCX~iHEMDs+pCj^)oFF1C>A%(0V)O$b;nBLq<>(4Lro8dA{;s2Oys_n3$0K zg0b+h=+)YH)&1@rwRkDAkQ1(+u#|%#VCMYFbqxj-XCkxiu$iK4q1tXQ#wUmSH>JD{ z_^z8+0~dhl%Fb^p+Evl+2Ah)`H&XSpOT%KB%@`@0lXC2iotE?BZ{LszTZSH&1gOhb zcy_G2Zr1%Z3Yv)|_>;BOtgiGjX*+rT?wk_KtqgBR(=i`nuc5q|MmiReqQ=6(`M#FY z>4oq=F4NiOK=~lV4I8X4gfv-viX9}8Aj)sa`QA4`AvI8!*?~FF^0l%V!&=UbqCohm zCW{QD>q`O~mrJo?z?R79>Wz%7G z=NUlKgOTWH)kCI?z#qIcYH1syWl7H1C5NM4*tR#T_Tx!*;Q;(_RUU0iGy8jd(V zOcfpu3)C>R_{@Ln;LaC&(erSN6;DW#D2?=+HT_XatOm=m4&XxCRzH@j!<+>jJ8t?r z8WESlbIj-ZA=n3m#KmYlIqoeXs7z`%g`=p~+3qEdz4)n55eJ&4F>E)97t`ZJ@%ed^ zOY79&cixf@3JeDjl21henTVm_VpPmfRp`Z(#)9RFWCnf5;5Gmb80{wR?kfE|V*Wb< z!$Z+ypV_lD&TBqdc6v1;>g!$scVTO5_e2Q61c{JSG;c2P{?FHvhH4&)DTk#$IDt4T zqN~+$!gtpnyTVYg;2UM(o8}O#Z7liAk_$bSr)z<>;b9TRumO*3zSxnVwg61-$=%^Yvsqd!2yuGbA0!SRRdDreZ@=83YrAz={x z!WEK0CR7hNbnQzhi7y4(k(fwluRh94NgZ8d)9FZd+F#BjhKHLYb%=r3=XPV*BpV>R z#8So|l1@X;Y@5>8GncSgua9cUcX4o6(YtL0R&Nh}LnOd3np1`Jn<_2dtN{%e{8B2bqs8tp|uO8$m zG8;({;*SN=B~&1b@zK?@AL%j7=Z{9AET!Raw(>EXy`2e$WHPHU_?p&$BB)_9y)r)# zFY5VQp3sFYCM3C#w~ZTSixI5(9kJ;837@Ye9&f zJ}c>u*@NWmoaASxnaOqth+=D4GQ>$iFrgpXNjt3db?9QVTNg)Pd39#Zd&<@Ggz_2> zB9+Uhb6M0`pi3uo=eCstrp@VBzB$<1AT}lX3lD;PpXp8RZj`79QyJa3`HOzHm<|RJ z)>6)}3@9ThIU@xjXy#w?EX zkGg}B!*f1lgt|b3>Gz1lYE@H&eiY?peLZU6AOw49gw~-L7#8SR4T~P(N(>=Gx*5hv zZ~?eprzcd(!>;YXgJoh1kLLTRIrHXB3+l)sx&?6~l{1HPnbe$&|1B7ASXrbz7|pFX zuwz7nF7%i_UKv5i#hdcV85^p!>i84lQloMw2(-}PI{l&~!;1#yjQ$!XknF5g+N`>u z-0vjMmSYwntr9KUEM2Hb!B0%m^M3pIOvnBH=lB@(1}C?vz5-lx9tx{4>|e zNyv|~`sOwd*& zeY}2X(nUg@kb1J%D)VI%^Y*E?Miv|>`<4HeOM1bEYr@PKMj8y#qWXy;(qcYD&Id+HUlcvO;mxDspVvy_#=X< zpU5KCCDZLjB&W!F^qCocCo=v`Nd0Z+&lrzrmQRhf%nwDA{T=ScoFj95!OUoT8&aY0 zs75@r0HQVLpu8v@4<821D}r^jV1Gqf*(}8RjmY+}?r!vPPyC=57&x$gTm21Cm-KHo zhk}!yPWRa|IiowPx#*hOX#wnR2r zEggDtoupD z$gzidUbPAfj71xS79>MeUQk zuJNjJo;{}af@K^M-$Jn3<*;!3u;HYdzYVZvPTqVR-}Qe}p1KPXDs42qEh^Zrgy&J> zcY%_j-hzWl`8kPx!2pC%cvnwenLZInULDe{u!j`c11!QQ*CEa6K9t{GllVk}sI+zd ziXrhgC3KV5E%1$yTEw45a&xJu3X@%MMLIS8p+ZP_u27wZUdn}Z>V9DS5L zI$=;~7AvB-t-E{a7Mq8abADgxfs7pKZB)EKjs_n$3S~y8a_1|@)FkO-R@;6TL-~A_ z`z|0^!|4ANm7kj+iP7e%@whP-Yc!2K851LM+p=5}l#!CmeLc?R4W^(ZE+jn`eTyF1 zL#tq_slB^9h8BpD=eTJ$QKr>qJ`ad_uXniTtB8l(Jo_Pq;ENs_7M;OrL>)6&>&0m; zsi|k@rtHjTE-o+VYAdv>wUCcDffWUR>#jfZb4yrC>G;Qb^}NAq)l?yts+95gEt*#B>k(dt)-;kZFmrSh4<4ZxW#%lWFkdHd0Z?sS7esc6Wi=1N;3#EFFWN` z9cOJ~clTs4=p#?Y4iC?|;)>?uh89*70nyGbndw~12;~;nFG<(aJ%gcs+R%RSbn99U zCL^+v1w#H~WVcoXb><3tzOKwLAe|?*im}W1Icn4}D4eH879Sh}^_QK2g0ilz%;$6s z^5h#g_f0(V8>8Dylf)yovzC@g*R7_>jxBJ%x@C`dS)PE!BdHx}uQK2Lm!}dr1(66F^kRdW%iuOhYTdaK~`CS5-(|an|LCATu z-Uo%kNUu|)EELwVQ(pD;)w^OAVl|Iqz_*u}!4_LI4kL`SVIfYm&)8uwNei*pa>?6t zr0*k@QF&R(afa*3>AF{jZ*Ey5!AEJm^|of8$I%t|X3zcRY-!DnK_^a2eRT~c8m#fE z95^$WhPL;6WS5i0VJGoT?Tg2XdXdsDT3tGTlO46m<*}U#+&LW~I__d;KLeSQczHEEW!7KfJc7@N}tA zGCxb>O`5njuQfq>63pK!Y!YJ!7U1$&U!vW=Wx_w;U=V4^B^*bq&oUbf*d@yA(SHtr ze3j1Vs19*ZG#twMFld(MeH7Mz8#Ue#&=`H;LE*s;`_%dFv5zcM>785AY`r!-8Gfu) zjaenFB8_rNIXC3;cI7N+$UG+CEL?HdD5+a&z!k2xP_hqkumDJq`7EtF#0RIP1s4rX z0sNYx(4YQ2j4OQT(&@G>E2%E7BK1NFgrtf1-)+1(zP(LX3#_kZRj|Z6k7ZL4YzIH` z)OVRPCwkvSRM$P1Sth5MuVB;K^VN}E&3DN1_%{!(xl(v==-mwEkTGd1YWksk20QOu z=mn6XT|P#rX|;ajsw5Ga%r78iK#aFzK^_dNhO5@jIx`cQY-qF~RAVLVt#ZI-lp@PG zX7*dVcx3i^0f0~uahAAg@5J(8QE}&PQ?0wE@P+B zR(}Di4uoSTL5QGktK!N{&0?f1>9~{iI*7tga8}Z^NVM@@rS%o1?Phg*0YO4+=8)Wj z?LU>p%A?u9oaRb^f9{oku6=YIWc1eI;oIp#whRuN@%dwU=U#81Mj(oOX=U7RYoLB= zZ7BCRI#i*e%EAA9Q223(RTRPJ4B}wpzlxS|W6#k$fjjrz8RNAW69g6Muv9{9cZ$KV zcTFhj^uOuM!aA~Qk8{>ftmVGGr9`4sdZ)ZT5Q|V!Ofa+3$G5^Og&0zVJa&EsxDoy4 z%&DfIX}N64Vo~)ryk6ZT$2MsS=QC+|dz?65LzOl7XOj-P4P0zA-v7+f>5Mw}4=?_D zKkj%!0&V)M&$akJHG~WzBV8)mW{&c}t1&FFnMLlYi#>KW3h*IVQe#+=!Whe(d;5PC z4L@q?;mpj*tx+b6r66yI*?gnT<}Lx~Cdxlwm}Icv?L5l%2bS91C@ZK$?Sf-_aYa-X>;8w~y;$ zDigAIIWxUSOaTdf_TFuVbT+SA+wLzwK>5Y>FF9~3EY+}2bF$d-XUn94zxfFSA^HW} zwalu0`8D+5I<2Adr6~sqnoWNk=tDa+<+)WbVy~+w-vV;v;Q1vWJ<4ma+$7a`LPg`U z^$oFWE;16YiHr*KEMo|>ucz`WsWWfc`I>GfD&bY zd<>np^R2*Sg|{eWdvbX#gsy_B>BVv-Z@hTF&WTTDZgI-7V+Kg<=+BX39CM|{^JLSa z-oLN?Gxo~Q!xFiLh@0_}Z zRoDy7Ha}Lbo z2X+*uervioWbxuF%XJt+bMq_!M(N@$R?7D9?9tl#9Z;(ijq4@t=5c+Np(~`$a5%V{ zm<^gzyI4QGO)M{80*F43&pI1Bz}NpnQYXH0GgFuixW>w=Dk=CpEYzZfu{-I-DN>qQ z$`f+h4@CRvwcuse$Zky*DZ4{`XPo&}|<{ zdu0T;HZpnNvEY?t$~{TB_MZSR*d}S1Cgy*?K*;acCYBFo$ZOP%@M1F06A0%F2lusfzx>w@?q9+Mr92M7Vz2 z4&<}Rpcak1(cobzFtup)+wuNNyxDzdO#)M&F=EMq^vc~OHTD~`;S9nVz>9DPmBfR( zO%dU5j!!NkiTd_0=@A8Bi4eChxzyw9Ok3WwZe;IlQEU%tC)aOoZXS*>+#GXiwq!i<7K;KB5%TOl%d>5@R$q2jn{U=jklt$ z)NzA{CunvqUE|66B=Y0ZmZ4K6^#|YrQTTo`re?DFmCdB_w#a4_&c`bs)e0=|db#$L z6f?YYB$;ebxQtpXn{IFz)Gy^bX+%4u!yVX&QdyDZOf%Sq{$F-_0&r%>-tafXfRd79 ziP4f~Y4n@nQ92$bkNLXxLyc>Tnx+Y(AKjPpNl9f*i_<TQRlYsb-HyXuXwGw>|9qpdj>%aEYXD~7Tg+(6IZV^e%&;v zaJG4#pcu&y!NvPL2Z*$szA$f_SDZpv4jelcNTpr?WvnJu$Cbr|bcF-1Z{4zd^`gP8 zLX5WLB)jL%$Av?X3h58}Z5&Vo26J8E?s`++hCV$ui)pk@<)^!!*YuGs%|+$q7B5B~ zA`%0Dk=Z$qZJXMf@pD~40TCr5GkGOYQ2qW@HRe1OI7`;{d^7r-{djkJ=|4@>At8d? zqFJ_Q2wlU>{nd5%gP?)MF!sv#>sPYP|5OS$9yit*PyY1GGihSW6<#@QYf$uRbyUcU z9%zWLHl2^*>qRSWP)XCA;t~=`qC3xLI@$qgShkRNg;2gcN;K|)vI&((#oq_b~F>^hJy#34`|M&l4TTpZo8 z3iz10v0qHpDV4Spd`7!^qMCHbM6ea2?}Z07w6jw=4<<5Z4$lCJ|D3Nd+1|6Kn*g$W zlBwcS+v}w*6)7L~G5dG&vinyZYO6NowWEWk%#Y%3JEfzjgjDaAr9u|EG_7a=7m6NDHbF9H=Vs0ih|HE6(l~-l^jlF_ zoRn9&G&T2P;=SLFMe2U`8`@{74t~^&NUR38*G49N;!tEgY|Odg|K;wHRz( zUiU6Ejqf9^b7|CBU8W>f69H+#+qLK&jKm-hgWg4BA%ZF_{?i{%vtKf&Nd^PClvq6w zRN77*)7D_C>>y*qAC`1Gn@;BBKbq=_Qw^9d193YZgL-(wG@8|Lzu>I7DO4 z?b>HbxDdcsqUOL==fL#}h`8`nOq-`^sMm~Ky?A-1r)#{tczObaomI%zECWWCG|5lG z1_5*ChHS^$D1xoR3d{~qfBGZ#&exAG;EX_yXkT&ipnSDv4In#tvnm~e=W-U(>SPE< zc(vC2QAR4Pc`_7u=xA^F#SFB)S_QV<$0cnESya;%=H20)H9>4SKqYzaw9acwK>!GE zr(&YxH=DdO1pZVd&G^ZF^i1enq!!IiwZTul{?b;4Qld#Vo&%Aihab>X@yD41VF4uI8?))`Fp0}lkn8l#sHV+ zthM-?p}{T_0RVklph#o9Z9Q+817)=gUXj7CaY@ccC(Sn<+{t<=1!PVN@tgqlc*Bnb zHb)E{-DEt2lg&-objVg_@~^v>k%vc4f3C~P{Zyl3{qojD)k8>9zV%v1$g!huDM}Xw z4Tw5E9BQLHO>SkL;r^jr3wL#I*2Q?H<*x6xQGP2qDr(g+XAzaiRbQ^}x)?SmywaKJ z<6$UDm)qg^?`svY5Ab|HG3FQwNaMxFI<1nW;s6&(edg3=iE-Zlv)#(~6PC!I^u5uU zi9d9(m%!)2?<9l2V)i4Chxu+VXTY7|8(#%FpM4mk+n=r9O+m0Zna_6>&)$H__?uV9 z-XlWtq)yYzLZ=TFI@7}viww0+i)b<8g1N-<3Iav=c9{s)oFIJR5!ob?-KWdJ^Q zmsDg^KRhPQF-~w!ak??TKA>*((3oY$xDE7E?^LPLoaY-S5PIoT6_@7=gkb+yY6u8+ zyYT2)@>|-O?Sd%QV1DDyl0}K()YExUjoWh5Py#F3nU3h^9}cTE;e8%tyVn_aR!~*F zIwD+OyVy=z&~dX?o?*^sl+XMA#}pn{y3;NohndoTCpV$l#i`U5gx>z zBHNXXg?>f?oUp*%#j0#BdTVuYX_^v)a-~R5R%bU*e}CIHUpGL`*Tr$( zS>&jgt^N>4DwG=?YZ4}C$-1r8$?19U3owGNum}d4*Y^}T6vyQ-5C~ga`tion97#@H zz77o?j{0L`g9W@M%(&}s*IY2j1pGX&fs0yv>xpxvm1Aj%H$La>csT4*Y9eZvZ0b~k4pJQqsNKEmci*I#%XPqJ%9j^A>LD#woLT(M>$nqX5HAc%Bq)2srj~dv`8kep z$j8TQdwp!=kijovi-az@)WTZ>#}oW-{rG9dh{mRlt3H|RUd7j!^%?#X!r~u5Qh+C# z%6ClYJO+v>z^)@4@K7f`ItWA1iUHDDmx=3t>1@?kr%T4bsb=B{Xfr~bXVI&JeyDFk zli+t_F*ABbZGxLCED4Idv2Y%XC6#kvOj+L%~njjc1@omdgVm4;^=a=0dTmDZer z<5vujZ{~p=6~Cwf8BOPSZk@bWOE$BY`Bn`0{BiG@<}xotf^^HyU<*ECbirozI~owz z1MpMqu+!}=*bG$)7hD`;Ue2qz1E^BI$(P=H0=OXC^0+< zasP#4Ihe@s!G;L|VublE`8tlXP-`Dyps3Z_HZ|Ab>MLSj54Z_@lx9YU^RNr?!K^lK z1}E5ctUw3vRMN+}#!lNz{DUR`$68cCR${V-a9QH;Iz-4vCN%BlG?-AO(tAz+uYGjb zoH)<*3*p;!=lR-Nucv1Ld=UJd13iv83V#Ga%BZ*Lra1t7`0@;1S+pmj+2sGe?jAmN zYUhp6!*P<(a8;d}=^qC22UrG1wZSahk9;GBXoKYIwS*IxEN0`#F@z`OP4`~{dC$6I z#)#8aEWCZkm%61S1LuI43EHI1r%svuGoz|`USuulQe{6Np~ou+u(=yM-)^%{M9!zl z-yg1S+13>(y|{S>s|fK2HN@mwErB4v2YIp?FlOB%&BT z+R7q^d|{sDt>*PBKOmr1;Yxkmx$)9RFG`lETCON1C7@_%Dy=Q2C@9LOmwM_6V8Rz? z0qW5QvRC7)e}Sw#o>j-RmxkuEle+EgYSYvrXhR|BwjKOv+%X)sh4MZifSnHSPFUH1 zK*z>cinX4%F$D(7qgKzN%`>^T24E)mO zws!w&C;wEC-6#?m!uxy!kJt;eY-@3sb&D=fvbp-EEIj~X-IakAn%*Rd#_1=m z*sq1uCnUg`_X!amURYx46FOs>*E=;P$~2V~Rz)_Z9j+W6i&I4aN$eLzNn1@)&{*pe zZ>u~l8c=lTJv*+Y_BfMV4n1^R$E`0wyL`L5hgaqPWMgW!%!bnN3cBN;jVw?93<9l| zLtPuIuqZ1Rk1isnvAd4=GmOe)g0J_%K0U^(tyhi6ykPf}y25?ipe^rK-BmzP-;|Lh zpO-#n{8twgpA-UUpCoCqUW>*5H< zXe_=1ZC^xCo=E=yB5s00;)L;(XeSYsNt>dw(%lJ@O%3inLEFuUM*laXjt+xD$q%ntwa0%pf8q}WANQZ z*zNkT=SfbFcbnw(=>cAwY0D_bJ1S&W@0wsL5u#C^83QQLJQ$otJ_gD<-x{$7&}P?# ze+}Eu@)*@+{&x-sp9Ye3o)Lr?&dx3^2l_9K1E&Jv43-e#H9!5S169$`6h6gtYOW@$ zJr0gz&)5Utz`iy{!_k7tM#3W-`PHqlegBSWP|>~86F-|#SMGPi(grg+7!Mj>?@>v0 zy<-AU7Y|-C&PIiWg{g^+*u+jwEq@yc@8e+d0z$Ydf;-sWzOwUjeggv@+2!7J-d~+s zIPTA62GVw2m!I{YI-C$S(B|i?R9?%ys;@b|8`c0Dc7?@QzH3_wAMIjwOb;^4VMBLj zJLw&NQbkmds_=&Q1INQ~vu7AA>p6s0tn#v?yrv06mlPm%tWvI8vp1Zdw-xNnbN08w z-SM#}F(zL+9a2E%Rdg$5`kND!rLZJHds9t;`Ke3#BBFbCgY^br%JVf@L=VTqhTB$? z$8(WBpMOGKaML{F*@_u7vAd$M;AA|CcP-*w=lLQsI_L$wXnfD3QQk{OUns9rK#~cf z$PeH)Y#7`R$738x9~627+MzAI3W|0CN&>wrv)7-#S-LbrzG4YYLuaG|B$`#etCL|5 zq%SK~W-ePPs?`s|&jAUKOh|-0&)bt8qBDR7xN>1?lg-{JLIP$QMRt>O?22O zFfZ=t2$Kuvc{(Z$I$M3-(`xUE%}NWAyeu?Y{7!P5Xk;j0-Pyw!Q2|y*O*-{KC(dWS zJcB7@z5f>zh0R%R5%vL}mmIkVqQry@@7BbU1V22v1CD$y)@VpgiR{kI{6%$s`F85f zf~nl*cFNXw(OMG=hZmbUdDwJXxaeT{LaIEoxS&w>3Sn5OFW|d_>RIZ*5452$P&A+n z+dF?OBEyE6^ec1p4?G3Ij)-ICk~=DcS8Ik_O0&Ut4;UMd=z>`zPdp}N&`c4j{@zj{ z@yyJe(62G7)WpNljnC}TL%TH_(Ggx=+$$_C2IO>->2U|gz2*pNwp`l-1IJM<%k$#S zJ>O!~sjkgnE3vaexA7qlGB`WgIDZ|Qw+gZb^uaQnZrf5ypR7zO>|&`8Vt!3mUvFKy zBGaaw9{x41S9dw*ny;9)%Z$zZpU|izN}#sQ*aVFrEMT+&4gJ7Uy7CVKTiZ~OOSqj6 z=;9mrHh{gbSBnnFCzJKla67y?THk#meflUUprD7%$@X49|MTAQ{%XAar&z(T29#M58DvXZJV-w_&eT*l_T7cSkyF~e zaW|`Z-UZ=@4?WR{&)iK)oUm2xe1gtKlHB^jCqP^~&iptH*nSekzZE0I|CSdsh<}=j zWkMQnQ1J-IYsH@i!p2M5^S%(=q$J**ntH?OFG}kweOcM#Lkm|egkui>w;}y}si}j- z)WaKLwnToSgN^ItKPuo6b;!Hnfa0Y*f0PBKPkhO3pj%h)=HWV z;D43HOokJ(NYchH6U6+72i@VlEgdV0gGz8xBzWH2UsJ#&bZeKh9uM=p5)BX!1XG8D zc_vcgv+1$swxFZtQQu6a3Vh z{{M1GD`JR^28WoS++BS=oh+dJN`Q$P=T@GVY4AVN;VHY~^~Bf?N|)g%oBh)5M|GxV z{TURxpgxx~_SnK2y>r}=lbdsZ^=N?edr(aEVhaPP!M|H9AP1k}Xk~K~5wUf;p0>^rn`J3zSSbi@v3l4>lT-$0z79R?-=qAwVehQVknWrG6rsjxFnhq=lKE zh3sm`#NAb)oFmG*N>esa8nt={0k$nNsVP6EZ~7$`UOd~AX{D1!^F4WWD%n!(_=&M_ zrHrdp|1`7Y{s%wb$k#7FHpY|EG`I9|cYNqh8!%Q75uZ~ZFV0;)6!0H3U!HN!zOmna z!$!&uxeZ}{LcXI5jjg))_}ch}dcmM_Txw^6;k1IqzCT+_;Hf(wsuw_y$dv-^Aq`0z z6cWT*U)z{+XYQ;Cm7*wkY7w@wGA{^D1BDyva3c|z3ng>NdLRm_LcPoS0Hk4>XO zkmbWy=Ju;urI>~A#~$3}4HcKkr!ZDA?~l94usN~S4DdnJ(-?T-#G`5cInJvqQh7Fospb$NL)=}23Ja?;bf7!bm5WsYosiLe%O~`hT z(^piU3}ft|I7O9aeDe=A-vrq=BN7R!+G6Q94|AjNOG$aH1y;UWSSynReuQK-S5Ipo z4#co;RvsPEG}#pY&rdq&_Ee`l<7vA&*T{gRa-rTKGx|eNnP|i3u}@smR()X)w%Jg5 zJ+&6g@OaQyjfn2X7LJHsIli^P%yU*Ychq2Sh2h>x!*z8khnMHcg=y&=K>hIUMe-+Y z9!32cq#2DuTz2xOE(<`P{1j(PG*C6R>+o61`3HcQ@}K(DDxEILvRRRTR+M@NJ;|^6 zx1n%~N_SC?zRi7!6AVSh4_Ghzug9b0WB^n6VTFqp7Y?V=frfLH-%02AlP|hx7LVJc zpMiS0-tEXYC?TVd({iouEv;5BZZfUV18GlC&6?pT#G4a_9|BtPmJ&mQ zRGF>#fZ~Q-SVSs954IcM5%lF`61mT(4t!jmFs+5`T}2mP>iNac&asPY*lsdXhV--g zoCxg-6050{PIlAV(Q4DaG2$~fP5KHVMEd`#i*5OFf`@<4G_ca}1CBq_8%@a$|JL;Fe^Ka66rU1u*7o~y3D0z4c@@xx_oIN!sgXe_&?ooMunxnfHcZ5tb99UyUhNR0Z9%e z0L|g+sJKI5lWu?!>c<4;FdEYC<};(zR)|*nAKivg__4yHHdy7WS55yQSNSOC@tqf3 z855!O$^xxpnI%xG?<;!txq4pG1ql>rMLV1ADETJtzCa>nwt8v4y{;R3aUMFGpi@p6S7Z(%kVvhA@u9m^r`bWSdxzc)0DOLtZFrKe$2%e^t&mBo z0j%g;M(5ro9?WA!@q7s3Opj>LMT6+b2`zp0{|k8$iAoI2wJA{na-MTnS668*PsSCq zj!w{ds6?b#HFC4&Qm1FuXOIxUrdW1)eQS*4AM}Ge3x&v#9!D({N;Aqr=>l{E^Xc;} zI(lAhiMnvG`-(9MGO3;g7IEN_H-~eG!<}Y%B=Wsf6I6cU034Cx2!`We|N$ijuG zqV*;>oBcuntFl4{vI@twWf>qrY3tD>WP}NujeRrT)?dYEY0xBpGlH znZ@`-JIBX7y5{O&9UGur85}Uw)sR8}icM7OF%QRbr#_G^QI81b-@GfAcq8S8lku_^ z^_)AGV}*P0igxI`M(d-G1sggk$A6fvk55rtLd1H4u2p%nXRVuq#v0>-z0nGpBFcTx z?>5$}k@S_;CZFMsH|{irVB0xOe#=uEGKp;hOjcG6-5dZ9Q%u%MIUMmzN)QwZBOqcAxwaMX?5Q|qYwd*b{tdh2Nr&S$hL z3mby}!WeUF=H+lVKAopt#(~b4$3?zhM;US8Iap-lW4^5?5RsSCI&yW^2;Enba8!~G zwr6~KzK3WOtF*|afc}i%nlOyMMMA<)5zc9U;wfRl1+>1gjdY#P>;~Cs5ULmT@CxhI zmsH`5G-NE80p{;XWnMv092N-6)17|w?g#M>n3rK$fcD6MX2FV1@W1H_xOhJO9zkJ& zd@*A+rk|ujW?H>~nbH3DH2hX~!-JNSBWoSbBptldb5}L zamUmkkDgxtwzgGZaOn7S7Zqm{frVxhzL);=^mDUlKR>mG6dscy3UGd?cwC4?hV?aa zCReVI!FrZF?Vu_;h#nMUiyeqlZji1Jf77IbLdchq3T>ByO(ZWpz_IAR$)tDu%-ey2 z)Qo9k`qi+5T+l*B|HkCnBN#1ONlNx4QChyMv*KUGF^D=V^o^EVrNfuhE;I8O*PNQm z{R*+%oBHLPcc`h>o_D=-O;5*mKw=w!PZv5N=^mc(whaG}QhMrR`!AbH-9gps7S3}! z+%n9Yc096#PFI7Nk^-!oTztU@emBNeY6gGHmJ~L;_XV(es&)5nhRvPuxme0&i!9B} z^@&hy`TX(nsUzngH+8M$rvV^5pSatjwW98`yIQNf#=^tsT|iM`JBo$*=Uid|hpX^h zVt#n|QOo^w+vPwPo3Y-Bvry0C|A(yi0Eg=d`-Y_vB|-EEf`o{kqL)OC9?_z%5!Lgp7;IEwHN2?o-=3Wp5M&eb5|kHpOp0( zInKCcMm0~IYZpTr7MoVc#6=H#*Yo?-T`a4)9B%4aOQ=^>HnoorHYB@d&s=;hgEtli z#}iQ9`X%i(&8zTW0na}jc^27D5@zsK%&sGim1|{`7cfIAf0aEt~qLS55K@~!e_x`^z3s&TB(!EBo9+w~)N z^O0&gqa+5=)_!C|?G@_Z*>1*^(BIl5Ed#Mtn*`Dt)x3$lp6>Q0a5;~MPkA48Moo(` zw9fT|N5dc5rWuGlE(w_%1yl&-teKPjL^F^Ih~5LhOJ>3f8LN{TMwTROMnqhG&#wKT z>n1bf7{Hw{s&%d^5&6T(Nk8}^*T{ojDiD7Nz$^}k&k~`kX)-52J52g@swyCPGm@Jf zeqJ~8LaaFNz@JMnF*R_&#vGME2COE&;Eo)Bagp_^S|=vP&LW#2C(=BV@*%cw3nIk7 zA6=;C#MRnMO6+Km7|vJt056#nbxLVNh`v3kqW6ONGD zk0n5S9&#xOd#qjXwtmN?p|8H185o%&Jd8alb-2gSJ!qktUNmm&R38`gmL>WQv!=nD zzuPy@YAI@IWXSs~cdKlIPkxrHv^hB_2(xZXTNYtBYvBX3l2un(bAgZ+E10@<6(P%ns$VPms-#?^ax zhbZo5M#UZ=qq(BfB2C-O7r0A?f2|7d1N_0ko0DGpyk3sylap?ylWM-!NTX8lR$Rnk~1eu+vMO)*DWB{ zLiD?iCay>dXRi+T1u;eehr^|o#x(Qofo0ON!U_mJ^HP>+DWL0p*qibTsAFP6N+8)C zqN=UH%26ob{<#%+twhe4(~3OU6Ic&_8;7Yk=*DqV+x#vPBQX%T#;gf1C`FhUiKh8G zo*a+2%QiSZfOSxW(^oNVb)Rnrn9aQvI>jJ#9Ep`}P>lx`#J3#Qe>MSLwXa5;TvS-d zlY1GyV1RH*K=h>lwf9?EqGAoSM}6xqqCjQj^T5gx+ii2=UXD0b zcNUT6Zr&0bTU-9Sn-hfWl#dCU-?vTwmLzfq?CiCs2;VP4Z@O^-e%(TmeeZPRY1>x! zeSgS*`(|Z` zK;ybkMx3eH%hu9Ku(q--e&pO}<(TP#=|`k1vbl)ZAj6u+vc*lOt_Pb_ymEhmHXB-1ATk9#Y+cKVFv&8+yZwfa#3R_@7F!BiTW1rpf z#jUu3rqP>#C>uaTQ+$Pmp>E}EX3CYE-y#X%2ZRTQNtEN>BE+R0uaXM`#vwkG-*Up# zdD+^qN1pu@aaY&@4tWDravkrQv~-sD;tC=)-NFE|?xF5(&%8)n zzOX^M)(5v%888D>iEh)Xv-_O&PdQFAGG2NJ&4;+if}e2S$@0PRc`S*`UpWN&lkqOY zHN4aGioIVzhIhVFK*rotQ1=a#iXPh0CcrNnN&dwXtJp@|s6RUJR`%Piq8o9qe0nT2 z#-7(m-?|qxUe3#{S&8>SB1Zm(*JlO@79DbfG~_>9fF}hF9Ts`c%>MS-J4_w&UW$Pp zLZ~y18_9h%9YNkW;fF?CzvuM1j0LOZItFNn@CJAd`#SI55sU4`@-{C9;5Zb8bMMo z&E1gem2__k9{g`x+$)KPES_x!Nzf4oNw@w|W9Vyt35`FfdUagN^YgB2Ms+ai?wwbvX={OCdUth+A4hxcdNyZuOeAlR7?71N;{208lHfU}ykc4| zX7cqi{dGPn{(R z`9dJ*)%v@<1)YnL(p5Jzq$fwynffCCXd_~he)9zQ+SZz|6lnwOB_3v?e9@9jDyzA@ zlFoIp0AMhw{Eo5HPjgjFpKgnP7c$w(V?s<1yfv#buzXsgyb?uL_ciUO#p~ZTB6VVB zVu8CRt<-oOdj2a$cf@o|gPRnEt@sMPh; zKr#M=C-b+!eX}?vU!U^WHT^`_iXVN-dbo;5^GI1U36ZY_V~a;j3&o%DHqIxJ?sST^`@FYn+m{qn>bOr`n8ZmJG*H?ALJR@0fj_p%WuMv)cT zzM>5O3fX|1Z^~Y1)pE^vnXaR`@FLl%hyV|5WgcoE{0R1BE!$sQfTLM{bhEP6w7fLR z65tWGmV&G`?f^;y^Cw|+>||2^^xu8~;Dy)L-$5-6`_Qi2TIPV4O7cNPuH;Tlc5LXZ z;WM^Qr0$ulC@ZHdJ)xB=(0f-2NEx&`HCbCaD(IkErL&%=jobG2Am0=I^!`l@p~3N9 z1nIS_=OY{4)ZMAop)k>c5&1`&+Y??NE)ajbgjY;?45NN@SZd%u zb(3h>275)J_dBg@8ux|WL{NG1CG`^Q^Qi<2jS?~X(|+Ur-%rGPx_hhKnHwvOcyG!M zd#4;y!+*&F-j_PldDy;3MD~_=md?Ki2Gu0JJ>E^!Y$9dee8-KnBNq!5%e zO37qWcLwdiCu4t=<3CVNaJkjx_1Tg^e1a1iYMqghXEL(ED@#V)3OBBjNVi-Q=Nu6UBqMD(osbhitxe`c#H8z1)kEqU08#4y7DGBy(+uN_ivf0t z0!$6>j&x=Gbu+9YAJxlYk>{mum07|=fOZsI0+HIGGxq^RFay}93nkO2OnQV zjp|SJOMAVEU==t;M{9VS;N20G^rT}z``B>er*ytHe<$oa;BrxXn^EL3<;_J_T@+;y znYZ`hrl0*4ySnyu4R*$feGNdo?w{7&EOm&Qs&Q3NROx*l5frdo1ay29`?>YL{e_)} z%jF(Y9{zgjplgqiw?9n$o5o_{w+0D^cNXSKpftcxzz9&t3(h%+d#&7&>U`f0doOta zY;!{V#komhBbQ`{X4y9>n@dnknXf!n)kyrIbYGfcq?=H$UAEz5gkbb}Yri z_kBHb`V8fVgd?Vr)9`7}*^hxRtl*;qxpwCQ#D~LE=E~96Sl4fi3d%_6~XNUV5 zc?PfF)=aL7-ifEp%&fTfu?gZa9=JTppPkQg$A+R!XZHmN?6f&=Dade&hNPZ#)88YH zzZLQeG^tIt{X;dv24*qiSesW-VzMY${1D6|!Bc?gjX$5W7xp<-1HyX4nLz%=@4e3* z;z23N&!9>Vz{$Kn;F#Fo3zRG@&~9G%1d8#|^LRwJMdhha!+>2>2R+?72~vP0tGa|) zkbOURlUiGI?7)RsH|5QJGEeum!mOtqQT=s4{Ehfy2gXKdV9)GmqQ}OvmX|kWeAFZk zS9c^l)o`r?E#e-?Js<|5O_H8=^-I8rihtt$Zgh0wP6MoclOZMHCds3uM=Xy6pf4oD zqM%e`)k1bt_4`k_A>L*6gOm*B!zGD1vZ4`;UWIC)}?!`z~!wWoWI1VijT66vE;>eozR(=*$V7CT9~ zva%xKO25*4R?x}upt!h1_af*U8PzSGu}#BM$0Y4yAC$bLr}5at4KH)>>1mY|p`DhU z3AlR~NWh}zt2G)tF+uFVyYF9Tys+jkhIy~9t`3sF^DQpU4T#ZU;E$<*K(dT>KEC*Y z@HaY=DKF2GdDrIBzpFt;P2D}9xbuv^b)fC_NgDZ@i-(a2n>ddR*r^wm8zn$uZFPio z{CR>ycze_=iUfsDbdEfHI=vTFwIvboa>UQ&N74KlrvS!zQufF|z+PEzYmxrcjpO8J z$3V{LBC!W0-^X&ZU^p^usNxUF74$Vj!5Kz1WmipoFxWzFqwWhs@pVnD+Wl(tD6zv& zCZWS7q}N5_?Ip_LZ+G0+ThDA(FfbVT5zIZ3O9zM@VRF9K(S(=>gI<_1oxO3Wn6P zd3h%Pa@+gZWj;!hs?s}}(lpXEhBSa0UoynC7 z4PwR4c&-)|1xkYW=h>Oke>yBnmQK3-sGlbw?!%$>)_LEH*Dq^!U!9k+WAy*}nHu>B zP&24v&2js@jTCjeh@F|bg&ZvmttRdwIX8B!dS`~FuH5D5V=BBQ<>~KaD(blUlS6QK zDg3lW++qCL5q51&K`B!0*QVW`w}%&^W9u+_&i79mc&A}Gx=|A0*RsNUbkHns)$C;k zglk3rhV6t$L=@+9Q7?Y)r^oW@KI4AEX3?74n|!pLPu}A!P7=Y@7eQ8xjMl2g&1cCvF^!{Y>bH6TUS zHfL|C&FOk3af8cAARVhl`pnmHWjd7g{_)iYygB=;v=4xuT3#ME^j%&u=Y4+;-eDAu z9T0dHm-ZwBwXi$lCFUkEH@LROFNxXp@IPJL(bm*1)^GH}1aof+yx-oCa~ILnLilXw z?636I`D_=ZnCP;+(bqT9a%)1}eZ=r^VL>jUGo8`z$;?ONn}e}^iBL?{{hNHQn7NR% zc-1bXk!GbtsgIPlly}O0x!bkVWWSEG*uX>zTgln^;-a0^1Z-P-eUH`je0%j%UsDCU z&7<@=pVgvrK|`@U4?#n~29`DXHtE!fAEJ|K#PC{!4c@ikIYxhu8D>28^0RJV-p12I zZb1+%%XmLy&llk~Q6dbTd+mJgW>E4tp%7BeKenzM5w6^T8KbvvMB^Kdk2_2_Kf=AT zv-1U_x~r%d{{3rbs}I(E_s&_-)W+Ev_^asrMp1DuKe`rZHIW-{VIgN>!Q|%ts3+nJ z0=?ar+DlDCFY4VrW;g9|Fe*jZ6edCxPC`CNmw)3lewxV>wIA6AK8zdN;Vf1Qt+=(+ z&@p*r#LcqoTgisvKq5}J{259nQW}eYY=Edi$4xW)7M~TtrY%<@GSd?1((hVMdq6oF z>k4QVpA~rK*_mof*`(ObmFEU5f3e%nm|YW+7;-mJePi*-tbZ^Vk#lSQHo*(t6HLgzVed}7+qI}O}^nx7PrIJPl~&{z{PKeU;MF zIX{;nMEK&C4AOH7uf9KG!C?y}gG9+lxRWwNO+slihgpf%%qlyw{lT+dDyZ4U5~Rm; z5C3|3w6+T3tjlfgoGW?M8z(LpXfJy2u)i8Li(q${{C09X1Ocf#1ROX}SypCwpTmS5 zJlp8$uW_5`6aF^1S7L+3?k8_2K6md5s`an24(^@WrJ8*nV}3LU_$)g*c(xFi5{Ep* zc@Aa|plsQ?OS0S=efA^%1XCJmIDhj4HopTXKxK+CM@#yn_WuN5|2?CnGmhPkiaOt; z!HHQmXMQ0VOIaKQRwp~@uG$AsJmY0>$Vtv+YQa-^=4bPqArUvuT&O3X_ zp8x#8PUc7ukoKY}YxA17l!~is$#5cef}($kArr}e8keglQW&XV^-;ndH8PWqsM8u> zcJS!Kc2D`HGZYZHg`Zj&rSob|C77Tu?fZ4r<{?1cW_j(WKC7ssm{0xMRc%0Kh489 znp;YZeC)#P{2NvnIvN4eyW`4TQI9GW-|lL2N(2l3}-yZ1HC}(Fr{8riv%AgU|vh<8|sXV)mPHA zY-VoyrT6m=4bc$nFol}}czCO2c#@3s7MdwH?i|#VC6OhOj?Jjz<0ZoIlejxQD9-QrxLm}0Fn*$22+3aajMpe1ORj=?)6H7>6`LnJ-++sK1F zROUgAd}-b9NDoyQy}SoFa@3Z(s-ZWP);wL%{;i*u*_+Se+yc&JD^S%!>dmrtp&}q5 zA5@%WjE;P>tWBi&cA>>Em$pik9+$%4;Sc*BhJ=$&c2GkNx>J(3?*3B==SQnh>+(L< zQI&oN;S}1!xKX&DAA02AExX3g4?pKU{C(cF4M!RH4)&JX33un1pNeEK+`Ds!%_a%R z?Q#lpB>0r^N`ou2Oo)4c_n=Sfjcbe0aNwBD7g|aCrEnUZ@X%pv4{$xp0{S$iFD=Hd zFV!ZZ^LWh1!Q2-)g+loKsVpnW-cF$3_Y=1~2T?1Z&re}2odVesV7(LuN^6m*m^2uB$~o(8;HH_->dgK}oV`!bN!oi8ptCg(DeDkrs9ywRQr99gDY6JJ3Q^ z_|)S4pT_q&wwG*``~qrhSL9k{#>8;jx?->>9|PT|=N~jQ;wp*PUz@i)WF6i~ep)?> zqLAe$FeH62#%NDXXHU%<^sS|ZkEUrX>zE13dt{Sz1O~Rz zn<3z&PntsXGJ-0*rx$jsY7o8KG0F|$1F@54PgWwklb&#KJY}Auxj9y+N6?2uVs;$e z7SDGl{oyX|mEFm4WZW!Ny}i;Wfv$x3D(8s)TKyX1v0tN1K#+T+$M|0Txl}1^G0%E^ zp*!7czWC7ns92Df4jA&926tzLoD^}po02^#!l>NDBbxDh)^397SPJ;cK=rd*p}bxM zY_jSxF?sk`KZamYNAr<4q+Dt0*w^CjUl@3xxaXZI~(?W3XUPbsbW4k}EDgHPl-pP8x=HSQXIvVNmY zHo^Bc%Wf-${!)pgi>HlaT&0btrI^}2yb*4z??FAPSyfY*WEiJX0mxJMuV$y&$7^&xX@6VD()(E)LRz%t%J{aAqO}?tu=0rPO8bHG#tC zJUn&PC1$?S#h|TP_pW*Xnt;_HCHnB)RFQrOS+tp2@iwyMuw?CKHyt{{#_0@qxa&u* z5I!uWfc;nztadw=yW#%o%6b+LeX`%=#6cbPk>w4o|t3M?*C&gz@js40svXVxw>`IaqH!6t^$W^r3UW={{53v!^eyx|7%TlYK zaT`Y+*6*F3`R+a~Xd$*u5?s~sGkS|$*({uCXEafWaLR6zvv??D!6swTCZE?X zQpd<$VmE2@MniL0PjmW>=J1en#exZ*Z|~~9ZZV*+cZc_)BpRr#q@-pl9`2JaF?eB$ zpBu(lt8r;d!Y7&5MAdMMyX(X3hTz>|d0V1A&V~X}X-g^nnK2ouGnK8w*6eIzVq#L< zsZQhku*8qIT+*DS!3ZhUqU@4lXa*v?ua#IBFCeUh-ad+*O$1Vm z5P!<*hHPI{qULRM{|OmQ9~lvFox`<~V=KR7J~kZ2q+?fFjz9?Oc3*#=BM`D7CIRu< z*(ZroRzWrGE(v}k1`%?cPx?$6#fh(?E~^=>Q5sEX{52w-pWoS4`+}$Xa?P79Lona} zEPyw?#%2#06RZUT}G@8Tk3|ixh zVgyC7|2{mKMtpR4Xj^pvC3oOFeLc;=Jgpc-af7;mCu+TA&DXf^( zdhLr}tcZ6|Njh$CRPEYeLOboTedJj`oST;ZoeYNF=hgT|Q3|(sX_c-v=D_`EBejt1 z^yi?NxupM^BkqT7?p~JnrYM++Opjhi`V!L>U;qMs5-uod$rFHjm=RLbh+K zMGEt>`uSKG$i3Hu8b(0%hZnON=GPlulvR&vOfT?oKnm^L#-TN~dInY8X}?yOk%5GK zS-&7tRJ_F-b#UL+hJi10SZq0;Y@kuuUMlI(&>Ji&BYno@L{hvGJHJgDbZ08*bYU#_ zsnMQg{zVs2#i64twd>C}rh&Pej5~I~-8G!0_(m`UYF=rl;MC?1);~(JNFBmqGRv5Z za%!r9&oV+Bw7$FswH~YveFP31zDqvf2IE~r6Bsmn=wSMTpAY`|)wuj^iP1B50CP59 zJXaBDs<`L#kvD}5qr`U&3=FnN8^Gz)bBDiH@}%e$tkgiJ3n%>itg}YwZC-zLo@2+= zP*-&56B2bTZ<#C7vD6fa4L&7>*$}_QZO0Dh_-(lH1VPYqD!Vj|ohf>Q*$>_H`S!xe zbk$A29kGG@e*VALsT|DBbGh~SHi5GsQI_8%=lwglJ4`g-vtmIgczPCzRqPOfq8~nh z=26lJ3h>y^1b?A##AW&4^5A+wpfcJzAS*)|5KB#)Sz)hlTrPXhcOli0z&T#7O&N=OZ4_l6<=%;2gSF{7KYn=4`SnVMg^bcWIvcKdp{qn0tytp_xruiDj4_X z_1lzazM`DslG^B~U5?h)&#zP)^2^G8$4@uXY`^_b`dTZ(6?2X6`m&g3ap*m8i5hi1 zN>KmT-EA)q7_;g42taxg#$c1RpD&L5gBiEHT@QN-h*x?Is!n6RWi6YK+!3M~M|vLp ztZ(60S0E%@4H1pM!p&oE;Ji1|B7b|%b2GpWNXvsyc$20#GYkj|mr1ZreuLySYltQ9 zcS#MI+xNI84klGYS{mMW`Gm9>X&IhjLsmy}g-rUE^3@ZUOZ}cSjqeCE09?-s;#jvZ z>fzo8qpU~aHo_+?A*)~L6Tj2qh6)RB1vHy|e=99L{Hf61XRln6+sHMj&9u1KoG?A+ zm2wKuqwcFEse*HdhA?=#o(^7~nii|fs7%!WD-mr?Ns9lvQfb4{v7}M5D|RCP)#^b6 z8-s|!!9loS@mT15t*)i-+fnLh-wqa#ugh{WJ(z!rZzFBM==)vUNB!8u2*nwE76VAVgGtv$!W9lncM`$UBRqKDm6S(7j#-}#ROxRaoS+0Zb$ z=xR}yWSK&4qrf@ z0Pkw7vS|!mV&drP>U+c^-0f9HC)o2P)QS3he%a`j`-@lZdg%?`d*#$e9%EYtHv7S% z_l(K(s=N(rq}N=CX?pZ(PZw8Ks?WCd(h@*x=u@fbjxTilmWR)@mh~%NE?bgfax==; zL+0S1Ry|OoIPC*33a!yMZ*pbjED*b6)4*!)e*rPu0!7QHM-g(W#3ZlbM@xn-==$kK zHx(y^#5pEMK)D&EBU5jbK5Q)sH=BAxF;tR@bYFWrd{}=`#eBCDKN!06ID1X2n>Q_< zk!ITw?|W3{Bh`?sj0|B~lK42CoE$bcan0AR@7t6Ln`!A!J9n^u#zDA4wZTE_#Kis7 zz5~Y)3X9wFirQmT(b2k5VyBdZ-1tI3Ovyo667qm4Gh@82U&_hiE;g^nbsElgKY2aH z9hLUq2>6*?0($1`Sv|d2HC=?_fdOoarwBfCs{5$*j<6LO3I4YQRS6m%^jWt|_DRkt z2o^BuYoM!vE?@QMNp*Z_pCq4jQgb?7y>V$O)icg3-e1hf|W6)~3C9Uarf zUT8Z9s(2SEo_^J|#I&!*u$AY~1`SQ!MN?0Z%mdncbrd7O&Y^#tpKo*5la)2qko3mY zD+v8kE)upV$=b}CrHOQZ+|K%XlS`M9h==1vWpz#2jmGgI}`Q2$f$N$*^44@L}!XiTQ`n*h(Jf(?B;q14-Rx4`* zC^|YqrI0*sA9MX=|Fv8f!-bGPQ*R~G`)#U%?Wa)!x#YJQn2M2n4)fR$+;O3BLT@;b z-nMj(>Sj$dgC{wBG!413-U{70qzxUP1) zqdCM3-_lxn_@`zw^X1WToD^ZeYE_{^v2%7r3DE!&1$g$^Y?F49b`qGiv#&RLvM z-=PBOIMV6x6nAqY?LFw{W9xONY0l(k21DAE1VAHEHREHH8+yLFa&D?~c8$WBw@dkZ zTwGag5%bJoP%qkSSY~NXfD!ViZu@S!i>~{^=bq!LhgI{D#Pivk!PARjVUaer4*fl^ z3}L(b4zsw{qy&fj*q?ZLXGwm?EllE$INv!O*2Av);2E8h$ndvsJfA~D zy)o!<>^`h9CdRkSga}Y4{Mr<0w;uhGFH+UfgjeMO!p{1h7`#DR~G0qii}v`TTMrGdJ9b>2-ibk zsU3dgBFEjnj^Ebyd8@gf+EuHrtyDO^GXm|dppeZTTaIBa>O;{dlf}_*{?R`WZn-0y z9eDyJyO6fe659a5&<$a3$m6q^fHG?_?}-}g&GR5Z>^aEK@hEjJJp7lv)x_L{&`p1E zdwZnftMB;`@*Ur@6&fHz&{W;2qx|(0I$(Wdq#R{|v;)ij@h#k}Gr90#D}4TTHmJC# z^mKb<-e2GK<(fTW`s{dnks`2!F15NeH-}S+gcqKBJHpLEcKT@+YKKZwutro*P#m_@ zOs7kjF^#L^FXfy{AdN4T)E-|ThI(H+1N=lI_b0Zq*u}A;@(`H^io6JMsw81fzSgzgS-Y$pS!}wmXQV`uAJppT*s%T4kJL=qv=A4<7HmIXKueJ z;o_A!4@(gUu^A`y?GRRBW|K^0nx=ulF>s&BVlFfC{neG60`g4!wmX!mPI|^=O1jUG z!eWT?oT`S?6~~UDQpQ;$&4CF&U3XLGSU0fOKBrelo~*g-G)~C>Qz+1{bDSq!>^hgA=s8%h_Hf;7FiYh0dqO9ttgOu78v&>3OnwJy zq>_x=p&u&qbh++8LxJ>XK>$X3SWL5?IMg<4sDois3Fp?{u~|it*(;z_omP5xBD+mdc)Y? zb?RwoR_2CMT>YI~f4rBxQx9LslRTNl9L-Ww#VtE)Jr|^@F<`Up2V`Ra6B7Bk=T7wK zPU%TM?zC~JBHWSSMhb<>7c!+62S3)NPer|r|8#r((TiwtKNjo{68(C`(m|zrHYuL5 zt&SqbtmJOoojQ!430krk$y6t9`F5dr)%-aaQ{2$BQgMow7j+)&cDb{IxX$$ObPBhC3suQp2LpI)-9pxUix)T7ADHEgRbQ3cn85_)rbB# z^iI0x#>v@sExG}0ucD^r0=L!IBbuI4Y+;xx5WZIc^h|1>E4deV6i$j;X1Wp7Hh-IF z?M9L;g&~FbBk!*O1_#2E1tjb@CX$|~!)QptMsM~ZH$xs*#|yrb6Mka0rG;!)vZH=3 zJ|)1$|F}JOJ=k>_T7DQBrZ8CC@FGIcc$~}d%z@kXc>If34tH9(K>whki)omNeiap#RB)7Et?~ zw)mgO>(+J!Cwz4H5mYB#@KPOf5PB7#ebj{Eu;~p*Lw~Z{wuK=gF}BJZ0M6bh=#+_S zJk6D8^Vc~f?nH~-Uh?(0_m7gQD35>7B;pA%C2zR zsi|L6Q;KflTbQM|*N^Gx5}8@hXt#cG+*Vdp@sDDDd?uAqfHg)(K!D8%o-gZJr0iHt z{wK+XB6th-j0*3=%ZFq)#db&b)x^-}ZNohE+f#)lhINmGy%e4IVh;~14i6Qh^2T-? zcKSU5%>;~H^j;UxP{`f+qqDg)A+?KBxy-Pn;W40Q(3ev0HNPX_t*vM-{Nh|cM zjRhjhMLo!xFOHATqNNpY^!z9+543-RIhyk|Gx8g}+%`bLYO@tN`ys0qR0#(uEQM|I z=5uxTy%%owTGJG^?HtCQ`g7n)i zZ$u?0PjAC*y^Q=a#+3+38Wysu=q2>v^LdhIQFMSdM#6VvraM+UU9Qd0hig<%tVgzBMqg7`Lz6-anagMLN)4F+AGzZsNOI$2j2D2(byzEU*cL z*gzm0ion05m62!iNN999%hVb#?xiYuSYKES;wt?)h&_{P5FW3Oi@W-J$YLp>1^dBc7S8m4St4z$M-N@t!e{4`HXk^?K&yU56GzC1-= zTYC;{M4B~NSs0@Pht^4eCUsoc%P?H41ipCUd=uf3Qg!G)UWZN|E1o-;#*vdNC@RzP zvskwNNtthPX^&*&w|N)&^vRPUI$PK@D+`n&o$=;%aYooQ%G%bU81h)m@lR*;r-kOs zZ@_0`BL>QSJ@_uSrbAJFrrrg>zW#5$gXc2(2nfG=_a?q#WIR~R2-aZ>)15Z7Pww5= zbsganMnQ&W2RC|6Vcrm%;|*mUl1lmI!bx}4m=nb zS{V-joh-gkrs6$fE-uAAiKY@GU5Y==#<9+ZiD1a2Cj8 zx%{iT)wpVaU z>LjbfT7|QO%TgLrj_qB+2NGzm=-V3{nLjA}-$KQm$MIM20+r-bNn|^kUc@JW zViQ0k|B0{X%KS0yNIS2GgA^ehY;xKt4r@t+sU!ZYw0a~h$G@{QiXIBQ)zTg<@wC>e z$-={P9Uzf@py{MM%Fh?-X?qv3dH12JZQkB3AW>OB!sFn3I0E1d?}Jx{5 ztt&SZGTbrhfcQ)O6<~M_92QyO%;79UXwr)dyqE!!0A*^v!orezE^+S3O>_ggufrZ@ zIdo!{r;=zz+YYe)fC+J(`(` zw4J~Zek+xNg@wk5Z+7``iHV0g8ak5QM!cF2sQ}VG@*b_Yp@z-Q285aSSB#ti;oip= zFJ#iZS8kQ|(@a^erF>P&HRPMGS9gcob&nxI+r5hvd%X*bW{oa$DrmGy2K?;yy~xl| z$J!ZD52m##_M!E+e7K>dY)|rnf@0iSO#h61RH(9$!^r0dcFyq3%~Jpb%S;^9(f-&$ zac*uq2qe7Ia@u`I)KQ3Jhp6d;Ai9~*#X`l_&KCA|B)C0(l&Q$0RxC??`7`~~1?15< zuc43EcA0Y2xNUiUv0VuuK?c%F!?%-eB+_vhIdtq)#qIb3j$nL#Go89mb038RZsv3P zK=RCjlalH9uvez01}-l8_sJ$sPZG{Z83{MR;z~)hrlGV@3H%;5LH1#Lm^fmgw=1T- zzvlQ8x4uVjjpv?5e*-xMg&XRdSHA?#-AXX|d1lAqI*p2mYmL=WT0LMHMcUu{n1_RS zhkl(~T(z_AOL`$>*&(WN#0-|l^Bx9Dn8oxlfrrr%X<(=2wP;})mBiy_&K+;D*;jds`hq-% zo(Mo~w)PU!Wobl14oA9=+jT>1H*9R^Pfr-G@CBjZhYnK}(lTsncN>p?2|E;>mz`xX z`5aik9ONfYd~#9`P&Am)5Q7c=X0~j2_AWcm0N`1ucuV$9edyLXx)Tgf)+l&`cwTzU z`5_@kW6uQyjyCl-v>+J~B@r3J7+Su>|5@3b+}*wP^Z?rmH$K|;@84HX7eOFEiDHiZ zU)b%L;#2*yf9L;`$lctZUK1Br4IRB}0@J7At$z<}hr1LAS)PojhL+Ip6i?P%2<8oC z)l+WEOG^Xs-MBbQ6_r$oQomBbNTUJpJ2~+K_{ZjM(tM#yW8VLK0cQsu>L=@7-^owr$wH zZ;|chD#n&^qo;k{QPa~#rgd>wP9#+|1c}-WX52fudK-?&1R^w98Zk+_%S546Ej!YT zO8eKLA)HvF@x{Q2_hZeOLu!H!{hhCB(JK%A0qbdLRA6Z6_$D!aK*qE{^60p({-ykj zV4n!E(sY9($IQ;igqd01sOutdzsY5R2y}iXW@buj6i-}LUnwc+D=8%pTQ_>ory$Nq z3n9PQmtu#8zA@IUwhy3ajJ#kSSx)7HZs;#a*vQ!P_Kx<}jyBghBmHq&CYs^J+TvnB zN>$)X_TDS3K*r3rFoMB~F*RGrlNM_oE)eytdooc6ZB>@}>m3 zTA>zenU7MtPw@CiqdIIW^<`V_VYNucglRLFt~BXn5xJrDblCL&G6~3YfxZ0EL1=Af zhx?tSCcE_Nr~3GN55PiG!=;cUhhiw8f3;Fibk=i?(ieFM3?7+mTOKRy{6f#?k*PX- zI#c#{2c0QE^jo{*gNRR=W)R39Lg5_7st)mI&ta-5e068>fq@f%Z6l!YWqa5TOs8(| zKSvciSZ(ai z$Xyy5ic|epGC<;UY+IzS9V%d{B5R}WdO#*Y8?7nl{#6T)q9SJ*`|8looxKu3Ze}g5 zPur=h z_l@;QQit>ruD=|=1$fX=FyufKE#z#>wNT@BOf2lJ7GFGnr_pm$M?fCa(QyC?d^1qe ztAR4|@l)!CdiiHjIDBuuBU$zVtGM}Y zb9b8e;TOhfm|ywo)WdzY&rG8uGjE=SCczKHn2mP6tJfYOtHfBO^nsbkm``X#wKMGh z5t6y5er22v=_Dk?bk2ZR(bN&%zl~pd$@ab$Wo~h)G&RlKZot$HAOWr30);a*Pb$Cp zUbYj<(9{%mO5fJV3xnY-{r=o`g;Gn))|P%&uWF3%GtkB&a&j25UPeo+g|GTO(hUlg zlF^Lto-^#GlGV~OpQZtgjf{kxA@OK`@YFdre^(cVKm=e=#L>^-(&^%yDsIk7GwPNF zS(p0R$Tvw}l07IX8Ib_V;5#)&{iF1F%`9Aiop3NuW+>y9LAT`}XyerjK7zzq zuj6|qv-j7Pj*CmYr-M?(86*K$^e!$wbme=$CXo)zz5K0mP~?uDG>M4>km?plYpeWB znnt~CAZQXulmQ$rg9ktIS%UjcFE3*TKlu#?q#kuz|K(m*GHzfpAF+f#A`<@ZX>muf zyoWg#8FKNyhf@EE$P9m+nwoiWiN{B%aufkkcr9ZROMP)|{8*3gQD%5%s1y+O2{=9! zSb4wD)RG}b{*b4<_@YS}`az9ET2o6Ch*EIz1<;UB68x^}TI$aMRSw0j#uYphr^p0Q z$t)fjwMor6MfPqQ^BKc@;01deoEm?all*iU-d&vh(Z{@{s3LQ0=zNC zu}VNRCHk_^kf%eVQ$9TMzc+5-VRmLpXD#J#;Xj}yL-KlmNTFUU$jE5kcGW8$b0oNm zCml3uQze>u>lW3jGucHg`n{JoeBCw0?`LF8{YdvCdZFf*URqVl%qAf*^AWGa6$sda$~Vi}Ej9dMi5ZAl71Vy%1(asI)pPqGa@NANs*2o?|{5;6gY+G zjcoi{c+up)O|W#)lpo_jQ+}F8QAJ0R>*-_(r52ckT@%WwX>FZ?43*Wp^3eiRnA++) z27JD57KX0X+4Egt!0Su2DW717{610h{rI{LSV_gad~OF-9N>b+{~$4xr?c}k=SIEh zTBeO@#v$#o_ebL&kc#|<5-w)`ry>#(T^6vnn&b~>oUVK*p!@k_8|`gS+^eM(QUniuIHM1N>5fea--33)1@`geF2I=5O_!WB={fW7izHtW?KTjB+!cn{E{XHe@5 zYpC37*SIR555rj`q3K3=O^NeB>jI*431pu?$PZ=y3}j;gjvg5b!@4KB?U#UN%cKi_ zWktml#HfDpBi>VW(RPblL{Wdy&X)B!JalNVEZpRB9EIg)hL)+)U)0jb&|78aWtv%e4iR<;VB6^@GkH>NZONf0hnOph;1&CLvF&WyNedvZ|@p0XT; zuo3wEYd?~UE54|{_Sf$!FHZ3))!Xi8T#l*D67 z_t5Q0u6>)!dS`|Mv1%^9=F72Pgnl0S+i+!B*0{^!Vjm9)OLa>D9>l<*p{|DbdoSL# zAsw*HAQ#cax5kW*y)FyR)?695d_Mf-a!gikj2(Svd3SLeFoGFyS~ARqmjx45{hMM$ z8sei&26!%uVgnhbUy^BmO&hFiaal7e=S!mMy6Ja^6#Z*kQs@Bv zz^Z&YFLz@=U$78OUN$uF8ULF5U%E;wj5Kud|6%xCBS;f0M6n!O-NN@BeV`a`H=0BKxwD6zYj`m-Q>? ztMa+12Ob|iU^{Yg(xVvHXQCIh`>)x2s+TtwE(&w2T#{=g^RZ!E$G@U9GIsbc9&0Lk z#`;XCo9vPV?=|^lT**zK|Jn#z2Pqr{f0+Cq@~?ZUDrG&oc;|;&SNgn%e{;9veg8km%>1uHk*m<`OFX*_%B+7^ zDCD~YsqtgL-2D*{ko}^Jo{)d^seB{*UGcJx535)+9u{Br)vbTaROLpBn$kZI(vRLx zzWh~VRfs#{Uk}Ss1qEKVR}nH4H}EF#AGB5eTUoQiR8g0-ihb5X#t%$;oj`O7;K@uG)0ht0~`L$5^to%GXdABf8DwAQ|J;NMVitNM%eyN z19&d#q7}OTT0NQ1@YEWPpj-%l$XGwM<3thw!W-^=7`45-v^sM9E_eomH&Z= z!1og37t}C+^6%E~YL~@w#_3+dDdx>T1hdKhM@3CN>A!SUR{W1sihazr@ONh5W7o=l zs>^&Y^*>vHf*f7Siv~1l{s(zZt^cpFuK;CoAqaLNs5F%3N3?N7+b~N!6&Hzhf>91#8~Rmv}mIdgzo3mhod#4?V^x+FzH#N_b^G1FuX} zg1`UENxaZ`r~!;G&4>)|Wv*(e#pBXOLjy@S|G=}A4GuSxvBoWD>E+b|1?|90PZc{~ z;dvq)8aD9YR?rEqL)YI|vS^le@%TBONZM!zyfUb;KEn6O{D+T=?zoN*vz!WL;Qu1oJ3#ro12ZxJ4u>GHzw zUTv~kxV7q)Em=yV?KP0Ov^@Rs0}+>sB&q)Qr3Fw`Ap$(nO2(5d%6jQEGOrY5f9T3o zlI$NTjN`4PyaW-ih{ivezsHw*#Po~&jIAfycm_E9BXgPD@t54eU{&m59_NQ9#dzS~ z31ZcJ5U)wTSJo;05}180O}3>{F)Pj#u*kO+3Xx!SAGSTly{D z;7T|!B{XdNIEr63uFH%<`TnIa1 ze)lc&DM0zsCO^a!djOk%Vqah5CwL%-IgEsPT2JjA4bvPgs>|Bb2TUCd9Qs% zN#Lc(8)pSPPQy#5EbE$Ccm0eJM}-&^u)pevbMxI~y&lmC|(X9fDR0A;Ih;G|=Q z5DElJViWq9XQs<>m@e|t#YkqY;hCZGONz1b@$F)I%2NvNnBq+5JtgG);PFbxwcoG* z_Was!pbzz`nT#r}5p&iymTXRz#cy+tEJ?QnjUu~^A*IT&628QxmH-Aw>97-WR8O1q zQHlQ->5|B6ckVs?uwwNyyVvuO;E}GNb=G}YO-s_fJ z;*O#fystu?k1j*|I_?U8Q$RBEC9dO15QfanTv0#*{^j zo6SovyNgCi$mmNkmv+{*su;~zpOK{4Z@M_TE_p)Xj<=!6z{^BM(GR?E)B!I!_=^D5 zdNeO?*e;1Zyxnjz`aJ}{Zjq~HMLz<9gH{YFafE&AZT|_B7sGSE@BfRT(cfF}imede zBp`~L&i(Bs-Ub$RwGdMxbV$LPxKgd|5%Sx41Bk9@B_qEaV^77^0sk&txZA-p?cZ(2PbJpbe90;ZjNqp~Sl5u^C zo$${9(ik)G^$0T4<)#6gFvx^|m8WJyF`2VWh|pr2d?s%jGAb{d99X5Zyvu8nietC* zt)5HS8Pe$}56x>t9864eD5lEs;s#mboVV^BQ|R_YAww@l>lSDC`9kwTdDZoW)m4|s z!(GstN}d-aXn0|r4sMTLdp$0XS@m`{I*oz{<}=qFcxeuK3c@*nz@q3N(Q=cCt-Ye4PX&=}A)={fcHQUAO?wl0u&oyalBj`=<>1X0C%KP4x7w0~K*JE?m z!Za@2*2NnqY8S~GV?b4U`m#^<^_LvappKb2;K@;cTwMxnM>fVn#<5N@*{m1r`tuJmwD|z)f9x%A4 z9=|Hyl9S^rdWd(WpsqTB>O4-Jnm+c?>K*5Xbi!D@{n;pS4Es&^BfzvGr*b{auSte%561;?qB)PIlCa<4aMg&p61MsUil2nVg}8IFI!; zye{4>BtnT|F~thqQ|u6?&5E|~6ho;ge-}GAt}|LaJ7(f)xh`S7zLHBZ6fxJMwID+u z(2dBI@*Z?tJ%#X!5Sp(a+iUK7h8=#&MaY&wx>2Q016 zxf$695+=z49&|V<-NwFQlxX5ET<+ck5gFCx;5ZjX>sRgZMs&tl3LTDve)gx?#(;BiNE30-X%BpWjfr*oE{u8a(!f;--mDP5V8|JkC-)}tk`!(Z!q@$bgb z@!NoT*Qs}&FESr;fn&nAzrPXcIZ)QzeUDa4HTW|Qi6gU%?@mv78t$CRQ4FMxOAEOhpDb;F-yL)cZui3Nic zghK}x;tRbDfG@wa5}8`bgSs@lF-siF!nYtD&IySzs_r4q^^eZ|&y(I<|3yKeZ`PyO zZ%O@kFRk$p*$ba`%WE6+UP#mz8?^{v)k2SPS+SRcz4!FJ#_t0d z?01HlpB3eH?ZlVtOoh&KZCF%4g@z!z^-mXW5JXqL{XXjxLnd{wX+>X=?PtdZ=nGdB zJKNo=Y>PZw#Zq>yirbmTs^BLjm$YLl(!Gkn(U3b%Pg~O?w@US1L9Q%4Ns9^y)bc|I zy#s%iRkh{iqzU<{dOp)bn>tQ)6hu!;PX-8hT7MFfdT_+5A$&p*=8f1n^Bx^y2BzO8 zIX?>vBQy{A^gGUfJs#ke`tASGs!T z>n;#2Ms-jW4=)chmuGukNOXT=j^}8?-FjJeHfwEPZcqWp`!$yQ=P#K!25wbF{rxP~ zw6J^X4n@70s+yUn5Yd2X@0fjwx&^aSDoyFV9+>HISi?5Y78y0)SwEMkW!pnHF3kDC zdidGEZeel-iAUPQX99!Mwd-(qVnQt-F+f>>TJ$iU2#HT8Repgn0fFff<$vGK!rX|I zO!RcSE3`mN#3hjuB091{sTr?3-$0+FIXg;J+(*p2la`?6s?j0GQI+IQC`5JKkqzV8 zYAAExy?Zx7@fl+!=I&+8A77105=no1^U>S;JxghcH^G2AFdLZy;m*1seDGWSL>07o zW-To^!~`@xY~d){fsGmI(J|p8PV0vP>t;iSgD~OjrYi8)wUtDNJ%y>V2<*t$DkGER z*UhPHD}osp`>-$GyE0&!dvkv0U<&U*;!W#4$cWg%Lr=Q~t z_0&K_q*>w9k(Hd-Ah<&^ZE%c~_*c|ek_)FDqbAdtPYaf}yi0oQ7Aboy48GoKxTSe2WXocNOOovmPAodN0i z*g3#amBmTo;_O^4S>9j+_0EY#mAzWLhq{BaTlTuQ=J4pkVFa8ws~G076597UQ|@I8kvXP>r?@Bf@=)l8syDuxyy#J z!93s0oY}(5l70Q{jBnH5NmzE%rIb`_Ym%0cL?rpgx&)`ag7G)ofeehSS2uW=TUH)8 z(BFUB=HMZ&{@UGa;bkNQ)I#M|JqUZeV!faLiQon`kE3SQp&@@mk(!+0!So&MuFWh* zm+zOS@YP8`XAb%Ulm@df*GU+4zkQMyPI|I1{@Fj+Dd+5rBEO5d1kigfstKa{bUe>i znjW8Q4FFheTA-4H1O20U((ajn2$%~#hT!pt0K;OzBrZ)CuR)rB$RR8?k9Ds9*x78&4YG@H-;j&=?HZp;=Y@#xl)o%&rL$==L|5Ov({+i`qZ#=;!Xw%GR zN_D2Eb8Y3F{)0;b6H<}7zhl0Af6VX(FgPUq2U~Bs2dA=w*AB6)wy0)-@W7DpXQT>t zfG+ww6NjUod0Z(QA<;(WsgYViRj*{xNhPISE|nvg_g5G}9)k_blqy4gzBubknd*l7 z$8BKHLOO7`p!4H~S?rr*9ZVNfi5O>nKP(Ok#1C$K9gd>e0EEv}_PZ!2jIB8*8^t_g z1jm!H^#+@`*P{iqy~MFC|5PT8H;4 zDa%BdsmxL7MpSp`X)1XfW5UpC^d`qxf~2c|Ga3Jx&@@x20)O|@F?;&(rRv+;ezX$i zyKficXHRZUdD5n2h_rKJvFjLOOH)6O;mglj zAmA@t4nz7+RS+m>WsI1-clTCRbnOGQE~VcPNNuk`ss;817-PU_3G$O*tS%fJn>8Wj z#ti-*^u##TIOp$m0-EL*Ws^!@S|`j3AEskK;u9Le5lY57oKfDDYB~`4e`B~{(9NHw z+P>?^_kmh>9+2#Y1ZowP-9Y*hVJ7+0hyOC&_bajR->hRgQ>S;y270`Pt* zuXX5ysue9$yKPifM%@(u5JjKl`3?P(qrgz_vl9|7c&j!Vnlqg`zO6aU-*_~T>Ym>5 z#6ExMc+Q#HF7;?7B9yY>0Bpf%u(2#wqd@GnORq?*KUpVGVcI+o8CF|9rg`Iy4xn%R zseZQSr%AkdUboz%l&#^tGQIIbbnna?F}cN(=f3Tk9}|-qA09B3;eNiwrir4v*vm18 zG@Nyv_dwHvFCill0Fdw9JJK-mvo{qV@(y~2>Q<(!;o+JO0w+VoQ~Hyo&s2Zo6r zKi7k^O?y=^Alns!2z_te@*c0=<5}lpU*p%W@{*b9vPYxm9xaBQ_1~bVw>e!YhQKGO zw1=M`J+$TRFGo|0ZTdr9T$1p3BH(ePS>$-(kRk&(a$b<`ve zves&BFdxDdEFUa&{SiQ}V0&TZhl>uViBjLwXmxq|a2x)x>^hMcKf-;(MK`)MH@j)* ztpB@#l>v~Of8Tt4b~1-mblc}J)`Ti2RR}=^$Ve|0?Dj@Zz#;A&;j{k;+#C+ zBP|rCJYVc}(x1DpkCBvXaU$ZN@_QWZI-1nl5wx{j-Ust6M*fv^;B2vOHCavH+Myh3 zp^}S`??|A@Za(_t?MP$V;;qBgpRKXeroAlEb$e|MK_iZaHuDof$s%7z^h0R}j2z=yn9mqq&_19d zFarSAWtSJFmr%FYI|<6{gD5qkjNdk^y~|XTmL7(z0K#l zj=HYI4gl<;OviT>oDUpETmJ`V!)xHiWQrR1?Mt93VJ~xJ1V?zm2k?o#trxyJ76T7` z7M(zpCW4T&&G~v+sI-C;cOF&*u8sCPx`UW9)T#43?)l9Mi!E5vtIxWW3-aB;2hPZL za)4(qNlt|aCtD{2a29=+(OQ6uIKO%k_L#hZt5??cflsc#mbG7Ws-R+m-Y<*e4wqUjuJf zEE_0y(!C!#Na^p7HVs>uV3U(dDt4|u1v?A^sv~ozzKdWfc%R8r{LE}$WcuvLSEpNC zHGbIj{D7GbB3V3*9O&pLO*SvTM`d%6hx%Yz`NqL1=X4>ED&oj9+J`89hfww9i``dp zYdZl@rGm1%*Hd*Lv=15^K-MIL`nq$)VYq~3!ag=tHUcLGzUyOnE49f7Z-T_?Zx{ODPGCo{VH zk;hDoy04S%p@w^FB$-R*=kI6A)yr_&HWNs7^wQ3HW=*PK$R zqhHt>HJTl-uOF1^P%k8aWlQR(5u=i z`-hHP#hjoRV@Ya`MFB^K$>5m%8OQkVZRP&`Zh0341b1&h>klB{U8m8~NK+rY>08YO2@s6)ucHAmo2bcFZ6Cg^ul) zGa+46{}+60kzcE{-&$5*QYyb;PinDqPu47z2a}WQJ;1zYx`bbT@wq-UH(qQtgkrJY z6D$M-NfdyHh|&F=&8V8UMjEAjq;m;qBQDE5~Csx#1UY6p8 z6c}d!Vv>eC=}*gswgfxbRi(?hnKe={Q@I^F%Aer^mgnhRhU33kd14yRBe(t534om_ z=2V@CxEF6saH+`WYf4-{aW(SK@9reJdqiXKI0h*jIlDJW;UcRUH(#B}o&Z(+`Ho7| zuxD5Rz7mRl{G$ZR?rVt&?d6OR?5FpW_qV)oCh3}ezhiui#$~w(L-E=YE*E_5@gerl zXJek%{{;jiq&yo_$F`3XlUD|Uqaw8F*2t{mt5I8F zOu4MsFRo1`f8W6+^R6Xw=Zy5($zGP@iVAa>@D)fT))C;^#zp>5b*ucKs-xl4TTpcm8hRxyK2&l*az}$tT4^!_1xTfqH;`YeIDE z=IHZ*#AQkL=?Ef6pLrXnPh*L=65i&km+120?PI=!&FPD^6klWLSSkC6ZAOr=QQ|P= z^mg*2o||mXG+KK$Jso>DN1PfH-FetORvgoc^s$; zYAJK+%;+r3VY6L`<+?92Axxh9Kqjcz94$U1$^?|`=}1nbtc$jp?bsl(c7HulxgVrD zw8x+>yA!xEQ90G#l0C7`o`J(b~u#l<(iZ#YrdPaDu2w*$aGIr2@?g(xJ6tQ2nl414|iQFgzWX zCZjD3k7yl}rExX7d0yG0HCrmU{AuKHW2|cH-Nb^!ye_Ei5(24%l>18gMLMswfxc-` z(!-(p7ko9{=YEan2)XI=Ns=zaU+J*cpd?+|5mc_Mf}^ZkYl0(nr8T&Wal)0?SV)03 z$Q;nuXWBLEaO*}4+`>gAid-${U@v>bMyo2jXy%qaE-IU;L}XzqUu?WgbieDRMh#7r zCNA34A=x1da{6j-E2?@yAlf@p@-`ig_K}X&>vPgCBz&sAvX?a$@@>lOev&%1u?l2$ z2KzE+yd64GSdXEQM$D4aCesk;CEoI=tFc9XBk_JMa-J%-SwPvNugb~GOF)Y zXtb!Rq$VGm32SW)EnU}gON2`HNf9au-k=^W(_u@ z|J5ykX9g9jL&&M~xX%R!*VJBP!WAFCB|jmZOrroOPo>x1)ii@Zx-CdtaTcvVbkHN~an~py9Fo zab32n$9`E{V1HeML%ZHn0z9L#9-Wrqy|brEBDUWf>DUl^K0*R{OB(j~OJ%u$C(**{ zYw2$%$0({r`%nBNvj^$duj^DC?xRN*kIf>>BMz@rfX?gO!akJLD;?sL*`? zZJ--ltol8}rF)rKE?s>wiry@ng$4PXsMgzPDM_FJ8=JU^qLc2(L2wvRaCL`fVbLQt zh?+(W+YOO(X_x*2t!T$Z)7^J!1$Vs5-?auZ88Kyj3Z13XeYLq}vr7Ho?(#@+-Xn+> zESI16Cr_pv-=-E#%^>g4MF@#bbwurKM&sTm617{@=d0eQ^Uxn_y{GLqh>_inB27g? za?6X+;xp7o0`#HBe*T4>brN#?r0IYx1)|f<73Q}`h z3`BhAvC7z47j?CC7-Z3}$SW;KALM&ix!Pf9d?ubh;5OlY8=kb7uMra)W32Er#hN-O zz(1$F!4icy{w~cDqpYW%WJFw1LF~7+&E{P!vwwWcP3Y3`34mO=iyX}}B2`Kp)q+9&ijCXh`koeg$8*EEo(Aje-k%||F3(6-uV1-6 z3_?f>$Cr+&KR(5u8F{pXKU0LfI>V8V#gMKb+$kl@|3Pu|1lwKWhu~Efxi2`>HX8CG z4*!At*V&N&I)jo00!2kY>2a?2D*?~g&DVM|GCN*#^Yl6m1(hDQW|D5>pMnazm$}YY zn7MRegE7c{Y~wERjz}iACH~ZvOf~k&goVJ@5XiG)^vCJ5?j&SERee!~*Fg>aDW=M% z%6>1g`^fd?O$BCZV;(Goc<Lx+@zACPA7{j;mjw^DlT?*FBdZve1*o zikcb$Gzm>YH-n02gU?jYSTJ_h*C|ghhj3=2z+JxZjq`c2qf-CVCGR7p<=(2(y6L0S z#UAU<(z~eLo@e@_b;Ywn(u}2= z2yynlKqbGju#9-Co*eek1X^GeHXIB)ZKGJ{;1aTD)g0z>fXs2T;+#^9vhq*dP zSTIhb%+9vj`>3!_bB;_TXUdcSR4xmtwYM^p-TKt#=}s*H(qW>mT+(fDt1qiA;_k0r z-u}$4bvENk;6}V(rxO|b;2=3QPE6aWEklT&9B{E>>JATCzC|x$YdqVOPSLr=_M>-W zogP*-gag`Dn^O(&P-H?#g@A00YC#X*bTv6AxTXQfbdSs+eg7vD=5Nt_d0cBOuCd&n zz4`eb0G>`nVw5`VXa~y2?88=~$Ht7y#R$7$o(aC z=`htl)_RZbyq7tPvbKAv!ap|~tVh2Gk5@PfkgA%WHkCw*yx^&Go9_TJ+`kJKQKSMy zSDo}3`P^{t2~l#+Yo4FS6fWfl0znlkefGCWgzi~^lb)TabLd=1yH4j9SKYn=1a>6H zZ^o^_#-lZ$MshICP{LOAw*F-5BxWba#q6;Cuoel!uikb>UbRo=oZ5hW$CRcLnTBt| z{b>Z;I>ZFsnhBihYY%TIi2{KX6hZAc#7o)=vyd~JZ|)|#4GPNl=sbwntNh*slScc9 z(?;3?02zg1Gc>|(D;*rvd7Os}t!Ft>6(&0|etGuAPu*hFAqsVRTcPn17WDZPfIfpl zse{Tu{=0lgmTm#(xwWsX?#Fq^A`$dNb5VccM1Con(~ygeTC8Nf{T>6}RMH9x zHBz|qQ7$BLs{;>(^`KEr3ALY~q$5x>U;S2hMOqncaAUpBEv^(%d!~1t5R->5A!Hem zU{KTf6Wyq}ckcCnU6|^C<_t2(Fnn>=GqQ5c28^UwnkBMNN^VhVq(2)XY4 z*lFu&hkoJnSRMSXNmA~*7Q^|tJgfC_k$y}5yzQa`E8vM|!ywc-xSTMR6riM$4TuZ& zWB!=3_Y`xic(9yY9Au(ctvhpo#pQzBJ4>~MWriaHhKdBHkSYtK-x8*g zp%2D*6W38R>GG;1ymae{N+kzl%Z(Q;Xs7cr&5JdtlGnn;+4flD8QR8s7t$Lft!uTf zd9h5tXlSCKe~`T8*BqXyfpzPz^^`ZeES3k_9%j8=9cI*gDsD8gRq$ZSGuK|!sL>V4 z*SNPgA11QL)_4-_M~6U-Px;DmSOFuJ;bsp3xav2D283z8?160@t z(~0hPq#08BOk`J_Z(Z!JC-N1ZH7%zIx^JEijYyptU@mt!qii^wr{1K+L5i|8L_DSj q8`M=&1OGOGPg7j|#H1@O=%w2~*BakOBw`kPO-4cyRP^%ohyMi_Nfmbh literal 0 HcmV?d00001 diff --git a/pymc-repeater.service b/pymc-repeater.service new file mode 100644 index 0000000..264f4f2 --- /dev/null +++ b/pymc-repeater.service @@ -0,0 +1,39 @@ +""" +Systemd service file template for Py MC - Meshcore Repeater Daemon. +Install as /etc/systemd/system/pymc-repeater.service +""" + +[Unit] +Description=pyMC Repeater Daemon +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=repeater +Group=repeater +WorkingDirectory=/opt/pymc_repeater +Environment="PYTHONPATH=/opt/pymc_repeater" + +# Start command - use python module directly with proper path +ExecStart=/usr/bin/python3 -m repeater.main --config /etc/pymc_repeater/config.yaml + +# Restart on failure +Restart=on-failure +RestartSec=5 + +# Resource limits +CPUQuota=50% +MemoryLimit=256M + +# Logging +StandardOutput=journal +StandardError=journal +SyslogIdentifier=pymc-repeater + +# Security (relaxed for proper operation) +NoNewPrivileges=true +ReadWritePaths=/var/log/pymc_repeater /var/lib/pymc_repeater /etc/pymc_repeater + +[Install] +WantedBy=multi-user.target diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..d2626ac --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,57 @@ +[build-system] +requires = ["setuptools>=61.0", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "pymc_repeater" +version = "1.0.0" +authors = [ + {name = "Lloyd", email = "lloyd@rightup.co.uk"}, +] +description = "PyMC Repeater Daemon" +readme = "README.md" +license = {text = "MIT"} +requires-python = ">=3.8" +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Communications", + "Topic :: System :: Networking", +] +keywords = ["mesh", "networking", "lora", "repeater", "daemon", "iot"] +dependencies = [ + "pymc_core[hardware]>=1.0.1", + "pyyaml>=6.0.0", + "cherrypy>=18.0.0", +] + +[project.optional-dependencies] +dev = [ + "pytest>=7.4.0", + "pytest-asyncio>=0.21.0", + "black>=23.0.0", + "isort>=5.12.0", + "mypy>=1.7.0", +] + +[project.scripts] +pymc-repeater = "repeater.main:main" + +[tool.setuptools] +packages = ["repeater"] + +[tool.black] +line-length = 100 +target-version = ['py38', 'py39', 'py310', 'py311', 'py312'] + +[tool.isort] +profile = "black" +line_length = 100 diff --git a/radio-settings.json b/radio-settings.json new file mode 100644 index 0000000..aee9b0d --- /dev/null +++ b/radio-settings.json @@ -0,0 +1,44 @@ +{ + "hardware": { + "waveshare": { + "name": "Waveshare LoRa HAT", + "bus_id": 0, + "cs_id": 0, + "cs_pin": 21, + "reset_pin": 18, + "busy_pin": 20, + "irq_pin": 16, + "txen_pin": 13, + "rxen_pin": 12, + "tx_power": 22, + "preamble_length": 17, + "is_waveshare": true + }, + "uconsole": { + "name": "uConsole LoRa Module", + "bus_id": 1, + "cs_id": 0, + "cs_pin": -1, + "reset_pin": 25, + "busy_pin": 24, + "irq_pin": 26, + "txen_pin": -1, + "rxen_pin": -1, + "tx_power": 22, + "preamble_length": 17 + }, + "meshadv-mini": { + "name": "MeshAdv Mini", + "bus_id": 0, + "cs_id": 0, + "cs_pin": 8, + "reset_pin": 24, + "busy_pin": 20, + "irq_pin": 16, + "txen_pin": -1, + "rxen_pin": 12, + "tx_power": 22, + "preamble_length": 17 + } + } +} diff --git a/repeater/__init__.py b/repeater/__init__.py new file mode 100644 index 0000000..5becc17 --- /dev/null +++ b/repeater/__init__.py @@ -0,0 +1 @@ +__version__ = "1.0.0" diff --git a/repeater/airtime.py b/repeater/airtime.py new file mode 100644 index 0000000..f456613 --- /dev/null +++ b/repeater/airtime.py @@ -0,0 +1,78 @@ +import logging +import time +from typing import Tuple + +logger = logging.getLogger("AirtimeManager") + + +class AirtimeManager: + def __init__(self, config: dict): + self.config = config + self.max_airtime_per_minute = config.get("duty_cycle", {}).get( + "max_airtime_per_minute", 3600 + ) + + # Track airtime in rolling window + self.tx_history = [] # [(timestamp, airtime_ms), ...] + self.window_size = 60 # seconds + self.total_airtime_ms = 0 + + def calculate_airtime( + self, + payload_len: int, + spreading_factor: int = 7, + bandwidth_hz: int = 125000, + ) -> float: + + bw_khz = bandwidth_hz / 1000 + symbol_time = (2**spreading_factor) / bw_khz + preamble_time = 8 * symbol_time + payload_symbols = (payload_len + 4.25) * 8 + payload_time = payload_symbols * symbol_time + + total_ms = preamble_time + payload_time + return total_ms + + def can_transmit(self, airtime_ms: float) -> Tuple[bool, float]: + enforcement_enabled = self.config.get("duty_cycle", {}).get("enforcement_enabled", True) + if not enforcement_enabled: + # Duty cycle enforcement disabled - always allow + return True, 0.0 + + now = time.time() + + # Remove old entries outside window + self.tx_history = [(ts, at) for ts, at in self.tx_history if now - ts < self.window_size] + + # Calculate current airtime in window + current_airtime = sum(at for _, at in self.tx_history) + + if current_airtime + airtime_ms <= self.max_airtime_per_minute: + return True, 0.0 + + # Calculate wait time until oldest entry expires + if self.tx_history: + oldest_ts, oldest_at = self.tx_history[0] + wait_time = (oldest_ts + self.window_size) - now + return False, max(0, wait_time) + + return False, 1.0 + + def record_tx(self, airtime_ms: float): + self.tx_history.append((time.time(), airtime_ms)) + self.total_airtime_ms += airtime_ms + logger.debug(f"TX recorded: {airtime_ms: .1f}ms (total: {self.total_airtime_ms: .0f}ms)") + + def get_stats(self) -> dict: + now = time.time() + self.tx_history = [(ts, at) for ts, at in self.tx_history if now - ts < self.window_size] + + current_airtime = sum(at for _, at in self.tx_history) + utilization = (current_airtime / self.max_airtime_per_minute) * 100 + + return { + "current_airtime_ms": current_airtime, + "max_airtime_ms": self.max_airtime_per_minute, + "utilization_percent": utilization, + "total_airtime_ms": self.total_airtime_ms, + } diff --git a/repeater/config.py b/repeater/config.py new file mode 100644 index 0000000..ffad780 --- /dev/null +++ b/repeater/config.py @@ -0,0 +1,137 @@ +import base64 +import logging +import os +from pathlib import Path +from typing import Any, Dict, Optional + +import yaml + +logger = logging.getLogger("Config") + + +def load_config(config_path: Optional[str] = None) -> Dict[str, Any]: + if config_path is None: + config_path = os.getenv("PYMC_REPEATER_CONFIG", "/etc/pymc_repeater/config.yaml") + + # Check if config file exists + if not Path(config_path).exists(): + raise FileNotFoundError( + f"Configuration file not found: {config_path}\n" + f"Please create a config file. Example: \n" + f" sudo cp {Path(config_path).parent}/config.yaml.example {config_path}\n" + f" sudo nano {config_path}" + ) + + # Load from file - no defaults, all settings must be in config file + try: + with open(config_path) as f: + config = yaml.safe_load(f) or {} + logger.info(f"Loaded config from {config_path}") + except Exception as e: + raise RuntimeError(f"Failed to load configuration from {config_path}: {e}") from e + + if "mesh" not in config: + config["mesh"] = {} + + # Only auto-generate identity_key if not provided + if "identity_key" not in config["mesh"]: + config["mesh"]["identity_key"] = _load_or_create_identity_key() + + if os.getenv("PYMC_REPEATER_LOG_LEVEL"): + if "logging" not in config: + config["logging"] = {} + config["logging"]["level"] = os.getenv("PYMC_REPEATER_LOG_LEVEL") + + return config + + +def _load_or_create_identity_key(path: Optional[str] = None) -> bytes: + + if path is None: + # Follow XDG spec + xdg_config_home = os.environ.get("XDG_CONFIG_HOME") + if xdg_config_home: + config_dir = Path(xdg_config_home) / "pymc_repeater" + else: + config_dir = Path.home() / ".config" / "pymc_repeater" + key_path = config_dir / "identity.key" + else: + key_path = Path(path) + + key_path.parent.mkdir(parents=True, exist_ok=True) + + if key_path.exists(): + try: + with open(key_path, "rb") as f: + encoded = f.read() + key = base64.b64decode(encoded) + if len(key) != 32: + raise ValueError(f"Invalid key length: {len(key)}, expected 32") + logger.info(f"Loaded existing identity key from {key_path}") + return key + except Exception as e: + logger.warning(f"Failed to load identity key: {e}") + + # Generate new random key + key = os.urandom(32) + + # Save it + try: + with open(key_path, "wb") as f: + f.write(base64.b64encode(key)) + os.chmod(key_path, 0o600) # Restrict permissions + logger.info(f"Generated and stored new identity key at {key_path}") + except Exception as e: + logger.warning(f"Failed to save identity key: {e}") + + return key + + +def get_radio_for_board(board_config: dict): + + radio_type = board_config.get("radio_type", "sx1262").lower() + + if radio_type == "sx1262": + from pymc_core.hardware.sx1262_wrapper import SX1262Radio + + # Get radio and SPI configuration - all settings must be in config file + spi_config = board_config.get("sx1262") + if not spi_config: + raise ValueError("Missing 'sx1262' section in configuration file") + + radio_config = board_config.get("radio") + if not radio_config: + raise ValueError("Missing 'radio' section in configuration file") + + # Build config with required fields - no defaults + combined_config = { + "bus_id": spi_config["bus_id"], + "cs_id": spi_config["cs_id"], + "cs_pin": spi_config["cs_pin"], + "reset_pin": spi_config["reset_pin"], + "busy_pin": spi_config["busy_pin"], + "irq_pin": spi_config["irq_pin"], + "txen_pin": spi_config["txen_pin"], + "rxen_pin": spi_config["rxen_pin"], + "is_waveshare": spi_config.get("is_waveshare", False), + "frequency": int(radio_config["frequency"]), + "tx_power": radio_config["tx_power"], + "spreading_factor": radio_config["spreading_factor"], + "bandwidth": int(radio_config["bandwidth"]), + "coding_rate": radio_config["coding_rate"], + "preamble_length": radio_config["preamble_length"], + "sync_word": radio_config["sync_word"], + } + + radio = SX1262Radio.get_instance(**combined_config) + + if hasattr(radio, "_initialized") and not radio._initialized: + try: + radio.begin() + except RuntimeError as e: + raise RuntimeError(f"Failed to initialize SX1262 radio: {e}") from e + + return radio + + else: + raise RuntimeError(f"Unknown radio type: {radio_type}. Supported: sx1262") diff --git a/repeater/engine.py b/repeater/engine.py new file mode 100644 index 0000000..dd7b4d6 --- /dev/null +++ b/repeater/engine.py @@ -0,0 +1,667 @@ +import asyncio +import logging +import time +from collections import OrderedDict +from typing import Any, Dict, Optional, Tuple + +from pymc_core.node.handlers.base import BaseHandler +from pymc_core.protocol import Packet +from pymc_core.protocol.constants import ( + MAX_PATH_SIZE, + PAYLOAD_TYPE_ADVERT, + PH_ROUTE_MASK, + ROUTE_TYPE_DIRECT, + ROUTE_TYPE_FLOOD, +) +from pymc_core.protocol.packet_utils import PacketHeaderUtils + +from repeater.airtime import AirtimeManager + +logger = logging.getLogger("RepeaterHandler") + + +class PacketTimingUtils: + + @staticmethod + def estimate_airtime_ms( + packet_length_bytes: int, radio_config: Optional[Dict[str, Any]] = None + ) -> float: + + if radio_config is None: + radio_config = { + "spreading_factor": 10, + "bandwidth": 250000, + "coding_rate": 5, + "preamble_length": 8, + } + + sf = radio_config.get("spreading_factor", 10) + bw = radio_config.get("bandwidth", 250000) # Hz + cr = radio_config.get("coding_rate", 5) + preamble = radio_config.get("preamble_length", 8) + + # LoRa symbol duration: Ts = 2^SF / BW + symbol_duration_ms = (2**sf) / (bw / 1000) + + # Number of payload symbols + payload_symbols = max( + 8, int((8 * packet_length_bytes - 4 * sf + 28 + 16) / (4 * (sf - 2))) * (cr + 4) + ) + + # Total time = preamble + payload + preamble_ms = (preamble + 4.25) * symbol_duration_ms + payload_ms = payload_symbols * symbol_duration_ms + + return preamble_ms + payload_ms + + +class RepeaterHandler(BaseHandler): + + @staticmethod + def payload_type() -> int: + + return 0xFF # Special marker (not a real payload type) + + def __init__(self, config: dict, dispatcher, local_hash: int, send_advert_func=None): + + self.config = config + self.dispatcher = dispatcher + self.local_hash = local_hash + self.send_advert_func = send_advert_func + self.airtime_mgr = AirtimeManager(config) + self.seen_packets = OrderedDict() + self.cache_ttl = config.get("repeater", {}).get("cache_ttl", 60) + self.max_cache_size = 1000 + self.tx_delay_factor = config.get("delays", {}).get("tx_delay_factor", 1.0) + self.direct_tx_delay_factor = config.get("delays", {}).get("direct_tx_delay_factor", 0.5) + self.use_score_for_tx = config.get("repeater", {}).get("use_score_for_tx", False) + self.score_threshold = config.get("repeater", {}).get("score_threshold", 0.3) + self.send_advert_interval_hours = config.get("repeater", {}).get( + "send_advert_interval_hours", 10 + ) + self.last_advert_time = time.time() + + radio = dispatcher.radio if dispatcher else None + if radio: + self.radio_config = { + "spreading_factor": getattr(radio, "spreading_factor", 8), + "bandwidth": getattr(radio, "bandwidth", 125000), + "coding_rate": getattr(radio, "coding_rate", 8), + "preamble_length": getattr(radio, "preamble_length", 17), + "frequency": getattr(radio, "frequency", 915000000), + "tx_power": getattr(radio, "tx_power", 14), + } + logger.info( + f"radio settings: SF={self.radio_config['spreading_factor']}, " + f"BW={self.radio_config['bandwidth']}Hz, CR={self.radio_config['coding_rate']}" + ) + else: + raise RuntimeError("Radio object not available - cannot initialize repeater") + + # Statistics tracking for dashboard + self.rx_count = 0 + self.forwarded_count = 0 + self.dropped_count = 0 + self.recent_packets = [] + self.max_recent_packets = 50 + self.start_time = time.time() # For uptime calculation + + # Neighbor tracking (repeaters discovered via adverts) + self.neighbors = {} + + async def __call__(self, packet: Packet, metadata: Optional[dict] = None) -> None: + + if metadata is None: + metadata = {} + + # Track incoming packet + self.rx_count += 1 + + # Check if it's time to send a periodic advertisement + await self._check_and_send_periodic_advert() + + # Check if we're in monitor mode (receive only, no forwarding) + mode = self.config.get("repeater", {}).get("mode", "forward") + monitor_mode = mode == "monitor" + + logger.debug( + f"RX packet: header=0x{packet.header: 02x}, payload_len={len(packet.payload or b'')}, " + f"path_len={len(packet.path) if packet.path else 0}, " + f"rssi={metadata.get('rssi', 'N/A')}, snr={metadata.get('snr', 'N/A')}, mode={mode}" + ) + + snr = metadata.get("snr", 0.0) + rssi = metadata.get("rssi", 0) + transmitted = False + tx_delay_ms = 0.0 + drop_reason = None + + original_path = list(packet.path) if packet.path else [] + + # Process for forwarding (skip if in monitor mode) + result = None if monitor_mode else self.process_packet(packet, snr) + forwarded_path = None + if result: + fwd_pkt, delay = result + tx_delay_ms = delay * 1000.0 + + # Capture the forwarded path (after modification) + forwarded_path = list(fwd_pkt.path) if fwd_pkt.path else [] + + # Check duty-cycle before scheduling TX + packet_bytes = ( + fwd_pkt.write_to() if hasattr(fwd_pkt, "write_to") else fwd_pkt.payload or b"" + ) + airtime_ms = PacketTimingUtils.estimate_airtime_ms(len(packet_bytes), self.radio_config) + + can_tx, wait_time = self.airtime_mgr.can_transmit(airtime_ms) + + if not can_tx: + logger.warning( + f"Duty-cycle limit exceeded. Airtime={airtime_ms: .1f}ms, " + f"wait={wait_time: .1f}s before retry" + ) + self.dropped_count += 1 + drop_reason = "Duty cycle limit" + else: + self.forwarded_count += 1 + transmitted = True + # Schedule retransmit with delay + await self.schedule_retransmit(fwd_pkt, delay, airtime_ms) + else: + self.dropped_count += 1 + # Determine drop reason from process_packet result + if monitor_mode: + drop_reason = "Monitor mode" + else: + drop_reason = self._get_drop_reason(packet) + logger.debug(f"Packet not forwarded: {drop_reason}") + + # Extract packet type and route from header + if not hasattr(packet, "header") or packet.header is None: + logger.error(f"Packet missing header attribute! Packet: {packet}") + payload_type = 0 + route_type = 0 + else: + header_info = PacketHeaderUtils.parse_header(packet.header) + payload_type = header_info["payload_type"] + route_type = header_info["route_type"] + logger.debug( + f"Packet header=0x{packet.header: 02x}, type={payload_type}, route={route_type}" + ) + + # Check if this is a duplicate + pkt_hash = self._packet_hash(packet) + is_dupe = pkt_hash in self.seen_packets and not transmitted + + # Set drop reason for duplicates + if is_dupe and drop_reason is None: + drop_reason = "Duplicate" + + # Process adverts for neighbor tracking + if payload_type == PAYLOAD_TYPE_ADVERT: + self._process_advert(packet, rssi, snr) + + path_hash = None + display_path = ( + original_path if original_path else (list(packet.path) if packet.path else []) + ) + if display_path and len(display_path) > 0: + # Format path as array of uppercase hex bytes + path_bytes = [f"{b: 02X}" for b in display_path[:8]] # First 8 bytes max + if len(display_path) > 8: + path_bytes.append("...") + path_hash = "[" + ", ".join(path_bytes) + "]" + + src_hash = None + dst_hash = None + + # Payload types with dest_hash and src_hash as first 2 bytes + if payload_type in [0x00, 0x01, 0x02, 0x08]: + if hasattr(packet, "payload") and packet.payload and len(packet.payload) >= 2: + dst_hash = f"{packet.payload[0]: 02X}" + src_hash = f"{packet.payload[1]: 02X}" + + # ADVERT packets have source identifier as first byte + elif payload_type == PAYLOAD_TYPE_ADVERT: + if hasattr(packet, "payload") and packet.payload and len(packet.payload) >= 1: + src_hash = f"{packet.payload[0]: 02X}" + + # Record packet for charts + packet_record = { + "timestamp": time.time(), + "type": payload_type, + "route": route_type, + "length": len(packet.payload or b""), + "rssi": rssi, + "snr": snr, + "score": self.calculate_packet_score( + snr, len(packet.payload or b""), self.radio_config["spreading_factor"] + ), + "tx_delay_ms": tx_delay_ms, + "transmitted": transmitted, + "is_duplicate": is_dupe, + "packet_hash": pkt_hash[:16], + "drop_reason": drop_reason, + "path_hash": path_hash, + "src_hash": src_hash, + "dst_hash": dst_hash, + "original_path": ([f"{b: 02X}" for b in original_path] if original_path else None), + "forwarded_path": ( + [f"{b: 02X}" for b in forwarded_path] if forwarded_path is not None else None + ), + } + + # If this is a duplicate, try to attach it to the original packet + if is_dupe and len(self.recent_packets) > 0: + # Find the original packet with same hash + for idx in range(len(self.recent_packets) - 1, -1, -1): + prev_pkt = self.recent_packets[idx] + if prev_pkt.get("packet_hash") == packet_record["packet_hash"]: + # Add duplicate to original packet's duplicate list + if "duplicates" not in prev_pkt: + prev_pkt["duplicates"] = [] + prev_pkt["duplicates"].append(packet_record) + # Don't add duplicate to main list, just track in original + break + else: + # Original not found, add as regular packet + self.recent_packets.append(packet_record) + else: + # Not a duplicate or first occurrence + self.recent_packets.append(packet_record) + + if len(self.recent_packets) > self.max_recent_packets: + self.recent_packets.pop(0) + + def cleanup_cache(self): + + now = time.time() + expired = [k for k, ts in self.seen_packets.items() if now - ts > self.cache_ttl] + for k in expired: + del self.seen_packets[k] + + def _packet_hash(self, packet: Packet) -> str: + + if len(packet.payload or b"") >= 8: + return packet.payload[:8].hex() + return (packet.payload or b"").hex() + + def _get_drop_reason(self, packet: Packet) -> str: + + if self.is_duplicate(packet): + return "Duplicate" + + if not packet or not packet.payload: + return "Empty payload" + + if len(packet.path or []) >= MAX_PATH_SIZE: + return "Path too long" + + route_type = packet.header & PH_ROUTE_MASK + + if route_type == ROUTE_TYPE_DIRECT: + if not packet.path or len(packet.path) == 0: + return "Direct: no path" + next_hop = packet.path[0] + if next_hop != self.local_hash: + return "Direct: not for us" + + # Default reason + return "Unknown" + + def _process_advert(self, packet: Packet, rssi: int, snr: float): + + try: + from pymc_core.protocol.constants import ADVERT_FLAG_IS_REPEATER + from pymc_core.protocol.utils import ( + decode_appdata, + get_contact_type_name, + parse_advert_payload, + ) + + # Parse advert payload + if not packet.payload or len(packet.payload) < 40: + return + + advert_data = parse_advert_payload(packet.payload) + pubkey = advert_data.get("pubkey", "") + + # Skip our own adverts + if self.dispatcher and hasattr(self.dispatcher, "local_identity"): + local_pubkey = self.dispatcher.local_identity.get_public_key().hex() + if pubkey == local_pubkey: + logger.debug("Ignoring own advert in neighbor tracking") + return + + appdata = advert_data.get("appdata", b"") + if not appdata: + return + + appdata_decoded = decode_appdata(appdata) + flags = appdata_decoded.get("flags", 0) + + is_repeater = bool(flags & ADVERT_FLAG_IS_REPEATER) + + if not is_repeater: + return # Not a repeater, skip + + from pymc_core.protocol.utils import determine_contact_type_from_flags + + contact_type_id = determine_contact_type_from_flags(flags) + contact_type = get_contact_type_name(contact_type_id) + + # Extract neighbor info + node_name = appdata_decoded.get("node_name", "Unknown") + latitude = appdata_decoded.get("latitude") + longitude = appdata_decoded.get("longitude") + + current_time = time.time() + + # Update or create neighbor entry + if pubkey not in self.neighbors: + self.neighbors[pubkey] = { + "node_name": node_name, + "contact_type": contact_type, + "latitude": latitude, + "longitude": longitude, + "first_seen": current_time, + "last_seen": current_time, + "rssi": rssi, + "snr": snr, + "advert_count": 1, + } + logger.info(f"Discovered new repeater: {node_name} ({pubkey[:16]}...)") + else: + # Update existing neighbor + neighbor = self.neighbors[pubkey] + neighbor["node_name"] = node_name # Update name in case it changed + neighbor["contact_type"] = contact_type + neighbor["latitude"] = latitude + neighbor["longitude"] = longitude + neighbor["last_seen"] = current_time + neighbor["rssi"] = rssi + neighbor["snr"] = snr + neighbor["advert_count"] = neighbor.get("advert_count", 0) + 1 + + except Exception as e: + logger.debug(f"Error processing advert for neighbor tracking: {e}") + + def is_duplicate(self, packet: Packet) -> bool: + + pkt_hash = self._packet_hash(packet) + if pkt_hash in self.seen_packets: + logger.debug(f"Duplicate suppressed: {pkt_hash[:16]}") + return True + return False + + def mark_seen(self, packet: Packet): + + pkt_hash = self._packet_hash(packet) + self.seen_packets[pkt_hash] = time.time() + + if len(self.seen_packets) > self.max_cache_size: + self.seen_packets.popitem(last=False) + + def validate_packet(self, packet: Packet) -> Tuple[bool, str]: + + if not packet or not packet.payload: + return False, "Empty payload" + + if len(packet.path or []) >= MAX_PATH_SIZE: + return False, "Path at max size" + + return True, "" + + def flood_forward(self, packet: Packet) -> Optional[Packet]: + + # Validate + valid, reason = self.validate_packet(packet) + if not valid: + logger.debug(f"Flood validation failed: {reason}") + return None + + # Suppress duplicates + if self.is_duplicate(packet): + return None + + if packet.path is None: + packet.path = bytearray() + elif not isinstance(packet.path, bytearray): + packet.path = bytearray(packet.path) + + packet.path.append(self.local_hash) + packet.path_len = len(packet.path) + + self.mark_seen(packet) + logger.debug(f"Flood: forwarding with path len {packet.path_len}") + + return packet + + def direct_forward(self, packet: Packet) -> Optional[Packet]: + + # Check if we're the next hop + if not packet.path or len(packet.path) == 0: + logger.debug("Direct: no path") + return None + + next_hop = packet.path[0] + if next_hop != self.local_hash: + logger.debug( + f"Direct: not our hop (next={next_hop: 02X}, local={self.local_hash: 02X})" + ) + return None + + original_path = list(packet.path) + packet.path = bytearray(packet.path[1:]) + packet.path_len = len(packet.path) + + old_path = [f"{b: 02X}" for b in original_path] + new_path = [f"{b: 02X}" for b in packet.path] + logger.debug(f"Direct: forwarding, path {old_path} -> {new_path}") + + return packet + + @staticmethod + def calculate_packet_score(snr: float, packet_len: int, spreading_factor: int = 8) -> float: + + # SNR thresholds per SF (from MeshCore RadioLibWrappers.cpp) + snr_thresholds = {7: -7.5, 8: -10.0, 9: -12.5, 10: -15.0, 11: -17.5, 12: -20.0} + + if spreading_factor < 7: + return 0.0 + + threshold = snr_thresholds.get(spreading_factor, -10.0) + + # Below threshold = no chance of success + if snr < threshold: + return 0.0 + + # Success rate based on SNR above threshold + success_rate_based_on_snr = (snr - threshold) / 10.0 + + # Collision penalty: longer packets more likely to collide (max 256 bytes) + collision_penalty = 1.0 - (packet_len / 256.0) + + # Combined score + score = success_rate_based_on_snr * collision_penalty + + return max(0.0, min(1.0, score)) + + def _calculate_tx_delay(self, packet: Packet, snr: float = 0.0) -> float: + + import random + + packet_len = len(packet.payload) if packet.payload else 0 + airtime_ms = PacketTimingUtils.estimate_airtime_ms(packet_len, self.radio_config) + + route_type = packet.header & PH_ROUTE_MASK + + # Base delay calculations + # this part took me along time to get right well i hope i got it right ;-) + + if route_type == ROUTE_TYPE_FLOOD: + # Flood packets: random(0-5) * (airtime * 52/50 / 2) * tx_delay_factor + # This creates collision avoidance with tunable delay + base_delay_ms = (airtime_ms * 52 / 50) / 2.0 # From C++ implementation + random_mult = random.uniform(0, 5) # Random multiplier for collision avoidance + delay_ms = base_delay_ms * random_mult * self.tx_delay_factor + delay_s = delay_ms / 1000.0 + else: # DIRECT + # Direct packets: use direct_tx_delay_factor (already in seconds) + # direct_tx_delay_factor is stored as seconds in config + delay_s = self.direct_tx_delay_factor + + # Apply score-based delay adjustment ONLY if delay >= 50ms threshold + # (matching C++ reactive behavior in Dispatcher::calcRxDelay) + if delay_s >= 0.05 and self.use_score_for_tx: + score = self.calculate_packet_score(snr, packet_len) + # Higher score = shorter delay: max(0.2, 1.0 - score) + # score 1.0 → multiplier 0.2 (20% of original) + # score 0.0 → multiplier 1.0 (100% of original) + score_multiplier = max(0.2, 1.0 - score) + delay_s = delay_s * score_multiplier + logger.debug( + f"Congestion detected (delay >= 50ms), score={score: .2f}, " + f"delay multiplier={score_multiplier: .2f}" + ) + + # Cap at 5 seconds maximum + delay_s = min(delay_s, 5.0) + + logger.debug( + f"Route={'FLOOD' if route_type == ROUTE_TYPE_FLOOD else 'DIRECT'}, " + f"len={packet_len}B, airtime={airtime_ms: .1f}ms, delay={delay_s: .3f}s" + ) + + return delay_s + + def process_packet(self, packet: Packet, snr: float = 0.0) -> Optional[Tuple[Packet, float]]: + + route_type = packet.header & PH_ROUTE_MASK + + if route_type == ROUTE_TYPE_FLOOD: + fwd_pkt = self.flood_forward(packet) + if fwd_pkt is None: + return None + delay = self._calculate_tx_delay(fwd_pkt, snr) + return fwd_pkt, delay + + elif route_type == ROUTE_TYPE_DIRECT: + fwd_pkt = self.direct_forward(packet) + if fwd_pkt is None: + return None + delay = self._calculate_tx_delay(fwd_pkt, snr) + return fwd_pkt, delay + + else: + logger.debug(f"Unknown route type: {route_type}") + return None + + async def schedule_retransmit(self, fwd_pkt: Packet, delay: float, airtime_ms: float = 0.0): + + async def delayed_send(): + await asyncio.sleep(delay) + try: + await self.dispatcher.send_packet(fwd_pkt, wait_for_ack=False) + # Record airtime after successful TX + if airtime_ms > 0: + self.airtime_mgr.record_tx(airtime_ms) + packet_size = len(fwd_pkt.payload) + logger.info( + f"Retransmitted packet ({packet_size} bytes, {airtime_ms: .1f}ms airtime)" + ) + except Exception as e: + logger.error(f"Retransmit failed: {e}") + + asyncio.create_task(delayed_send()) + + async def _check_and_send_periodic_advert(self): + + if self.send_advert_interval_hours <= 0 or not self.send_advert_func: + return + + current_time = time.time() + interval_seconds = self.send_advert_interval_hours * 3600 # Convert hours to seconds + time_since_last_advert = current_time - self.last_advert_time + + # Check if interval has elapsed + if time_since_last_advert >= interval_seconds: + logger.info( + f"Periodic advert interval elapsed ({time_since_last_advert: .0f}s >= " + f"{interval_seconds: .0f}s). Sending advert..." + ) + try: + # Call the send_advert function + success = await self.send_advert_func() + if success: + self.last_advert_time = current_time + logger.info("Periodic advert sent successfully") + else: + logger.warning("Failed to send periodic advert") + except Exception as e: + logger.error(f"Error sending periodic advert: {e}", exc_info=True) + + def get_stats(self) -> dict: + + uptime_seconds = time.time() - self.start_time + + # Get config sections + repeater_config = self.config.get("repeater", {}) + duty_cycle_config = self.config.get("duty_cycle", {}) + delays_config = self.config.get("delays", {}) + + max_airtime_ms = duty_cycle_config.get("max_airtime_per_minute", 3600) + max_duty_cycle_percent = (max_airtime_ms / 60000) * 100 # 60000ms = 1 minute + + # Calculate actual hourly rates (packets in last 3600 seconds) + now = time.time() + packets_last_hour = [p for p in self.recent_packets if now - p["timestamp"] < 3600] + rx_per_hour = len(packets_last_hour) + forwarded_per_hour = sum(1 for p in packets_last_hour if p.get("transmitted", False)) + + stats = { + "local_hash": f"0x{self.local_hash: 02x}", + "duplicate_cache_size": len(self.seen_packets), + "cache_ttl": self.cache_ttl, + "rx_count": self.rx_count, + "forwarded_count": self.forwarded_count, + "dropped_count": self.dropped_count, + "rx_per_hour": rx_per_hour, + "forwarded_per_hour": forwarded_per_hour, + "recent_packets": self.recent_packets, + "neighbors": self.neighbors, + "uptime_seconds": uptime_seconds, + # Add configuration data + "config": { + "node_name": repeater_config.get("node_name", "Unknown"), + "repeater": { + "mode": repeater_config.get("mode", "forward"), + "use_score_for_tx": self.use_score_for_tx, + "score_threshold": self.score_threshold, + "send_advert_interval_hours": self.send_advert_interval_hours, + "latitude": repeater_config.get("latitude", 0.0), + "longitude": repeater_config.get("longitude", 0.0), + }, + "radio": { + "frequency": self.radio_config.get("frequency", 0), + "tx_power": self.radio_config.get("tx_power", 0), + "bandwidth": self.radio_config.get("bandwidth", 0), + "spreading_factor": self.radio_config.get("spreading_factor", 0), + "coding_rate": self.radio_config.get("coding_rate", 0), + "preamble_length": self.radio_config.get("preamble_length", 0), + }, + "duty_cycle": { + "max_airtime_percent": max_duty_cycle_percent, + "enforcement_enabled": duty_cycle_config.get("enforcement_enabled", True), + }, + "delays": { + "tx_delay_factor": delays_config.get("tx_delay_factor", 1.0), + "direct_tx_delay_factor": delays_config.get("direct_tx_delay_factor", 0.5), + }, + }, + "public_key": None, + } + # Add airtime stats + stats.update(self.airtime_mgr.get_stats()) + return stats diff --git a/repeater/http_server.py b/repeater/http_server.py new file mode 100644 index 0000000..ee519c4 --- /dev/null +++ b/repeater/http_server.py @@ -0,0 +1,459 @@ +import logging +import os +import re +from collections import deque +from datetime import datetime +from typing import Callable, Optional + +import cherrypy +from pymc_core.protocol.utils import PAYLOAD_TYPES, ROUTE_TYPES + +from repeater import __version__ + +logger = logging.getLogger("HTTPServer") + + +# In-memory log buffer +class LogBuffer(logging.Handler): + + def __init__(self, max_lines=100): + super().__init__() + self.logs = deque(maxlen=max_lines) + self.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")) + + def emit(self, record): + + try: + msg = self.format(record) + self.logs.append( + { + "message": msg, + "timestamp": datetime.fromtimestamp(record.created).isoformat(), + "level": record.levelname, + } + ) + except Exception: + self.handleError(record) + + +# Global log buffer instance +_log_buffer = LogBuffer(max_lines=100) + + +class APIEndpoints: + + def __init__( + self, + stats_getter: Optional[Callable] = None, + send_advert_func: Optional[Callable] = None, + config: Optional[dict] = None, + event_loop=None, + ): + + self.stats_getter = stats_getter + self.send_advert_func = send_advert_func + self.config = config or {} + self.event_loop = event_loop # Store reference to main event loop + + @cherrypy.expose + @cherrypy.tools.json_out() + def stats(self): + + try: + stats = self.stats_getter() if self.stats_getter else {} + stats["version"] = __version__ + + return stats + except Exception as e: + logger.error(f"Error serving stats: {e}") + return {"error": str(e)} + + @cherrypy.expose + @cherrypy.tools.json_out() + def send_advert(self): + + if cherrypy.request.method != "POST": + return {"success": False, "error": "Method not allowed"} + + if not self.send_advert_func: + return {"success": False, "error": "Send advert function not configured"} + + try: + import asyncio + + if self.event_loop is None: + return {"success": False, "error": "Event loop not available"} + + future = asyncio.run_coroutine_threadsafe(self.send_advert_func(), self.event_loop) + result = future.result(timeout=10) # Wait up to 10 seconds + + if result: + return {"success": True, "message": "Advert sent successfully"} + else: + return {"success": False, "error": "Failed to send advert"} + except Exception as e: + logger.error(f"Error sending advert: {e}", exc_info=True) + return {"success": False, "error": str(e)} + + @cherrypy.expose + @cherrypy.tools.json_out() + @cherrypy.tools.json_in() + def set_mode(self): + + if cherrypy.request.method != "POST": + return {"success": False, "error": "Method not allowed"} + + try: + data = cherrypy.request.json + new_mode = data.get("mode", "forward") + + if new_mode not in ["forward", "monitor"]: + return {"success": False, "error": "Invalid mode. Must be 'forward' or 'monitor'"} + + # Update config + if "repeater" not in self.config: + self.config["repeater"] = {} + self.config["repeater"]["mode"] = new_mode + + logger.info(f"Mode changed to: {new_mode}") + return {"success": True, "mode": new_mode} + except Exception as e: + logger.error(f"Error setting mode: {e}", exc_info=True) + return {"success": False, "error": str(e)} + + @cherrypy.expose + @cherrypy.tools.json_out() + @cherrypy.tools.json_in() + def set_duty_cycle(self): + + if cherrypy.request.method != "POST": + return {"success": False, "error": "Method not allowed"} + + try: + data = cherrypy.request.json + enabled = data.get("enabled", True) + + # Update config + if "duty_cycle" not in self.config: + self.config["duty_cycle"] = {} + self.config["duty_cycle"]["enforcement_enabled"] = enabled + + logger.info(f"Duty cycle enforcement {'enabled' if enabled else 'disabled'}") + return {"success": True, "enabled": enabled} + except Exception as e: + logger.error(f"Error setting duty cycle: {e}", exc_info=True) + return {"success": False, "error": str(e)} + + @cherrypy.expose + @cherrypy.tools.json_out() + def logs(self): + + try: + logs = list(_log_buffer.logs) + return { + "logs": ( + logs + if logs + else [ + { + "message": "No logs available", + "timestamp": datetime.now().isoformat(), + "level": "INFO", + } + ] + ) + } + except Exception as e: + logger.error(f"Error fetching logs: {e}") + return {"error": str(e), "logs": []} + + +class StatsApp: + + def __init__( + self, + stats_getter: Optional[Callable] = None, + template_dir: Optional[str] = None, + node_name: str = "Repeater", + pub_key: str = "", + send_advert_func: Optional[Callable] = None, + config: Optional[dict] = None, + event_loop=None, + ): + + self.stats_getter = stats_getter + self.template_dir = template_dir + self.node_name = node_name + self.pub_key = pub_key + self.dashboard_template = None + self.config = config or {} + + # Create nested API object for routing + self.api = APIEndpoints(stats_getter, send_advert_func, self.config, event_loop) + + # Load template on init + if template_dir: + template_path = os.path.join(template_dir, "dashboard.html") + try: + with open(template_path, "r") as f: + self.dashboard_template = f.read() + logger.info(f"Loaded template from {template_path}") + except FileNotFoundError: + logger.error(f"Template not found: {template_path}") + + @cherrypy.expose + def index(self): + """Serve dashboard HTML.""" + return self._serve_template("dashboard.html") + + @cherrypy.expose + def neighbors(self): + """Serve neighbors page.""" + return self._serve_template("neighbors.html") + + @cherrypy.expose + def statistics(self): + """Serve statistics page.""" + return self._serve_template("statistics.html") + + @cherrypy.expose + def configuration(self): + """Serve configuration page.""" + return self._serve_template("configuration.html") + + @cherrypy.expose + def logs(self): + """Serve logs page.""" + return self._serve_template("logs.html") + + @cherrypy.expose + def help(self): + """Serve help documentation.""" + return self._serve_template("help.html") + + def _serve_template(self, template_name: str): + """Serve HTML template with stats.""" + if not self.template_dir: + return "

Error

Template directory not configured

" + + if not self.dashboard_template: + return "

Error

Template not loaded

" + + try: + + template_path = os.path.join(self.template_dir, template_name) + with open(template_path, "r") as f: + template_content = f.read() + + nav_path = os.path.join(self.template_dir, "nav.html") + nav_content = "" + try: + with open(nav_path, "r") as f: + nav_content = f.read() + except FileNotFoundError: + logger.warning(f"Navigation template not found: {nav_path}") + + stats = self.stats_getter() if self.stats_getter else {} + + if "uptime_seconds" not in stats or not isinstance( + stats.get("uptime_seconds"), (int, float) + ): + stats["uptime_seconds"] = 0 + + # Calculate uptime in hours + uptime_seconds = stats.get("uptime_seconds", 0) + uptime_hours = int(uptime_seconds // 3600) if uptime_seconds else 0 + + # Determine current page for nav highlighting + page_map = { + "dashboard.html": "dashboard", + "neighbors.html": "neighbors", + "statistics.html": "statistics", + "configuration.html": "configuration", + "logs.html": "logs", + "help.html": "help", + } + current_page = page_map.get(template_name, "") + + # Prepare basic substitutions + html = template_content + html = html.replace("{{ node_name }}", str(self.node_name)) + html = html.replace("{{ last_updated }}", datetime.now().strftime("%Y-%m-%d %H:%M:%S")) + html = html.replace("{{ page }}", current_page) + + # Replace navigation placeholder with actual nav content + if "" in html: + nav_substitutions = nav_content + nav_substitutions = nav_substitutions.replace( + "{{ node_name }}", str(self.node_name) + ) + nav_substitutions = nav_substitutions.replace("{{ pub_key }}", str(self.pub_key)) + nav_substitutions = nav_substitutions.replace( + "{{ last_updated }}", datetime.now().strftime("%Y-%m-%d %H:%M:%S") + ) + + # Handle active state for nav items + nav_substitutions = nav_substitutions.replace( + "{{ ' active' if page == 'dashboard' else '' }}", + " active" if current_page == "dashboard" else "", + ) + nav_substitutions = nav_substitutions.replace( + "{{ ' active' if page == 'neighbors' else '' }}", + " active" if current_page == "neighbors" else "", + ) + nav_substitutions = nav_substitutions.replace( + "{{ ' active' if page == 'statistics' else '' }}", + " active" if current_page == "statistics" else "", + ) + nav_substitutions = nav_substitutions.replace( + "{{ ' active' if page == 'configuration' else '' }}", + " active" if current_page == "configuration" else "", + ) + nav_substitutions = nav_substitutions.replace( + "{{ ' active' if page == 'logs' else '' }}", + " active" if current_page == "logs" else "", + ) + nav_substitutions = nav_substitutions.replace( + "{{ ' active' if page == 'help' else '' }}", + " active" if current_page == "help" else "", + ) + + html = html.replace("", nav_substitutions) + + # Build packets table HTML for dashboard + if template_name == "dashboard.html": + recent_packets = stats.get("recent_packets", []) + packets_table = "" + + if recent_packets: + for pkt in recent_packets[-20:]: # Last 20 packets + time_obj = datetime.fromtimestamp(pkt.get("timestamp", 0)) + time_str = time_obj.strftime("%H:%M:%S") + pkt_type = PAYLOAD_TYPES.get( + pkt.get("type", 0), f"0x{pkt.get('type', 0): 02x}" + ) + route_type = pkt.get("route", 0) + route = ROUTE_TYPES.get(route_type, f"UNKNOWN_{route_type}") + status = "OK TX" if pkt.get("transmitted") else "WAIT" + + # Get proper CSS class for route type + route_class = route.lower().replace("_", "-") + snr_val = pkt.get("snr", 0.0) + score_val = pkt.get("score", 0) + delay_val = pkt.get("tx_delay_ms", 0) + + packets_table += ( + "" + f"{time_str}" + f'{pkt_type}' + f'{route}' + f"{pkt.get('length', 0)}" + f"{pkt.get('rssi', 0)}" + f"{snr_val: .1f}" + f'{score_val: .2f}' + f"{delay_val: .0f}" + f"{status}" + "" + ) + else: + packets_table = """ + + + No packets received yet - waiting for traffic... + + + """ + + # Add dashboard-specific substitutions + html = html.replace("{{ rx_count }}", str(stats.get("rx_count", 0))) + html = html.replace("{{ forwarded_count }}", str(stats.get("forwarded_count", 0))) + html = html.replace("{{ dropped_count }}", str(stats.get("dropped_count", 0))) + html = html.replace("{{ uptime_hours }}", str(uptime_hours)) + + # Replace tbody with actual packets + tbody_pattern = r'.*?' + tbody_replacement = f'\n{packets_table}\n' + html = re.sub( + tbody_pattern, + tbody_replacement, + html, + flags=re.DOTALL, + ) + + return html + + except Exception as e: + logger.error(f"Error rendering template {template_name}: {e}", exc_info=True) + return f"

Error

{str(e)}

" + + +class HTTPStatsServer: + + def __init__( + self, + host: str = "0.0.0.0", + port: int = 8000, + stats_getter: Optional[Callable] = None, + template_dir: Optional[str] = None, + node_name: str = "Repeater", + pub_key: str = "", + send_advert_func: Optional[Callable] = None, + config: Optional[dict] = None, + event_loop=None, + ): + + self.host = host + self.port = port + self.app = StatsApp( + stats_getter, template_dir, node_name, pub_key, send_advert_func, config, event_loop + ) + + def start(self): + + try: + # Serve static files from templates directory + static_dir = ( + self.app.template_dir if self.app.template_dir else os.path.dirname(__file__) + ) + + config = { + "/": { + "tools.sessions.on": False, + }, + "/static": { + "tools.staticdir.on": True, + "tools.staticdir.dir": static_dir, + }, + } + + cherrypy.config.update( + { + "server.socket_host": self.host, + "server.socket_port": self.port, + "engine.autoreload.on": False, + "log.screen": False, + "log.access_file": "", # Disable access log file + "log.error_file": "", # Disable error log file + } + ) + + cherrypy.tree.mount(self.app, "/", config) + + # Completely disable access logging + cherrypy.log.access_log.propagate = False + cherrypy.log.error_log.setLevel(logging.ERROR) + + cherrypy.engine.start() + server_url = "http://{}:{}".format(self.host, self.port) + logger.info(f"HTTP stats server started on {server_url}") + + except Exception as e: + logger.error(f"Failed to start HTTP server: {e}") + raise + + def stop(self): + try: + cherrypy.engine.exit() + logger.info("HTTP stats server stopped") + except Exception as e: + logger.warning(f"Error stopping HTTP server: {e}") diff --git a/repeater/main.py b/repeater/main.py new file mode 100644 index 0000000..8e88715 --- /dev/null +++ b/repeater/main.py @@ -0,0 +1,271 @@ +import asyncio +import logging +import os +import sys + +from repeater.config import get_radio_for_board, load_config +from repeater.engine import RepeaterHandler +from repeater.http_server import HTTPStatsServer, _log_buffer + +logger = logging.getLogger("RepeaterDaemon") + + +class RepeaterDaemon: + + def __init__(self, config: dict, radio=None): + + self.config = config + self.radio = radio + self.dispatcher = None + self.repeater_handler = None + self.local_hash = None + self.local_identity = None + self.http_server = None + + # Setup logging + log_level = config.get("logging", {}).get("level", "INFO") + logging.basicConfig( + level=getattr(logging, log_level), + format=config.get("logging", {}).get("format"), + ) + + # Add log buffer handler to capture logs for web display + root_logger = logging.getLogger() + _log_buffer.setLevel(getattr(logging, log_level)) + root_logger.addHandler(_log_buffer) + + async def initialize(self): + + logger.info(f"Initializing repeater: {self.config['repeater']['node_name']}") + + if self.radio is None: + logger.info("Initializing radio hardware...") + try: + self.radio = get_radio_for_board(self.config) + logger.info("Radio hardware initialized") + except Exception as e: + logger.error(f"Failed to initialize radio hardware: {e}") + raise RuntimeError("Repeater requires real LoRa hardware") from e + + # Create dispatcher from pymc_core + try: + from pymc_core import LocalIdentity + from pymc_core.node.dispatcher import Dispatcher + + self.dispatcher = Dispatcher(self.radio) + logger.info("Dispatcher initialized") + + identity_key = self.config.get("mesh", {}).get("identity_key") + if not identity_key: + logger.error("No identity key found in configuration. Cannot init repeater.") + raise RuntimeError("Identity key is required for repeater operation") + + local_identity = LocalIdentity(seed=identity_key) + self.local_identity = local_identity + self.dispatcher.local_identity = local_identity + + # Get the actual hash from the identity (first byte of public key) + pubkey = local_identity.get_public_key() + self.local_hash = pubkey[0] + logger.info(f"Local identity set: {local_identity.get_address_bytes().hex()}") + local_hash_hex = f"0x{self.local_hash: 02x}" + logger.info(f"Local node hash (from identity): {local_hash_hex}") + + # Override _is_own_packet to always return False + self.dispatcher._is_own_packet = lambda pkt: False + + self.repeater_handler = RepeaterHandler( + self.config, self.dispatcher, self.local_hash, send_advert_func=self.send_advert + ) + + self.dispatcher.register_fallback_handler(self._repeater_callback) + logger.info("Repeater handler registered (forwarder mode)") + + except Exception as e: + logger.error(f"Failed to initialize dispatcher: {e}") + raise + + async def _repeater_callback(self, packet): + + if self.repeater_handler: + + metadata = { + "rssi": getattr(packet, "rssi", 0), + "snr": getattr(packet, "snr", 0.0), + "timestamp": getattr(packet, "timestamp", 0), + } + await self.repeater_handler(packet, metadata) + + def _get_keypair(self): + """Create a PyNaCl SigningKey for map API.""" + try: + from nacl.signing import SigningKey + + if not self.local_identity: + return None + + # Get the seed from config + identity_key = self.config.get("mesh", {}).get("identity_key") + if not identity_key: + return None + + # Convert to bytes if it's a hex string, otherwise use as-is + if isinstance(identity_key, str): + seed_bytes = bytes.fromhex(identity_key) + else: + seed_bytes = identity_key + + signing_key = SigningKey(seed_bytes) + return signing_key + except Exception as e: + logger.warning(f"Failed to create keypair for map API: {e}") + return None + + def get_stats(self) -> dict: + + if self.repeater_handler: + stats = self.repeater_handler.get_stats() + # Add public key if available + if self.local_identity: + try: + pubkey = self.local_identity.get_public_key() + stats["public_key"] = pubkey.hex() + except Exception: + stats["public_key"] = None + return stats + return {} + + async def send_advert(self) -> bool: + + if not self.dispatcher or not self.local_identity: + logger.error("Cannot send advert: dispatcher or identity not initialized") + return False + + try: + from pymc_core.protocol import PacketBuilder + from pymc_core.protocol.constants import ADVERT_FLAG_HAS_NAME, ADVERT_FLAG_IS_REPEATER + + # Get node name and location from config + repeater_config = self.config.get("repeater", {}) + node_name = repeater_config.get("node_name", "Repeater") + latitude = repeater_config.get("latitude", 0.0) + longitude = repeater_config.get("longitude", 0.0) + + flags = ADVERT_FLAG_IS_REPEATER | ADVERT_FLAG_HAS_NAME + + packet = PacketBuilder.create_advert( + local_identity=self.local_identity, + name=node_name, + lat=latitude, + lon=longitude, + feature1=0, + feature2=0, + flags=flags, + route_type="flood", + ) + + # Send via dispatcher + await self.dispatcher.send_packet(packet) + + # Mark our own advert as seen to prevent re-forwarding it + if self.repeater_handler: + self.repeater_handler.mark_seen(packet) + logger.debug("Marked own advert as seen in duplicate cache") + + logger.info(f"Sent flood advert '{node_name}' at ({latitude: .6f}, {longitude: .6f})") + return True + + except Exception as e: + logger.error(f"Failed to send advert: {e}", exc_info=True) + return False + + async def run(self): + + logger.info("Repeater daemon started") + + await self.initialize() + + # Start HTTP stats server + http_port = self.config.get("http", {}).get("port", 8000) + http_host = self.config.get("http", {}).get("host", "0.0.0.0") + + template_dir = os.path.join(os.path.dirname(__file__), "templates") + node_name = self.config.get("repeater", {}).get("node_name", "Repeater") + + # Format public key for display + pub_key_formatted = "" + if self.local_identity: + pub_key_hex = self.local_identity.get_public_key().hex() + # Format as + if len(pub_key_hex) >= 16: + pub_key_formatted = f"{pub_key_hex[:8]}...{pub_key_hex[-8:]}" + else: + pub_key_formatted = pub_key_hex + + # Get the current event loop (the main loop where the radio was initialized) + current_loop = asyncio.get_event_loop() + + self.http_server = HTTPStatsServer( + host=http_host, + port=http_port, + stats_getter=self.get_stats, + template_dir=template_dir, + node_name=node_name, + pub_key=pub_key_formatted, + send_advert_func=self.send_advert, + config=self.config, # Pass the config reference + event_loop=current_loop, # Pass the main event loop + ) + + try: + self.http_server.start() + except Exception as e: + logger.error(f"Failed to start HTTP server: {e}") + + # Run dispatcher (handles RX/TX via pymc_core) + try: + await self.dispatcher.run_forever() + except KeyboardInterrupt: + logger.info("Shutting down...") + if self.http_server: + self.http_server.stop() + + +def main(): + + import argparse + + parser = argparse.ArgumentParser(description="pyMC Repeater Daemon") + parser.add_argument( + "--config", + help="Path to config file (default: /etc/pymc_repeater/config.yaml)", + ) + parser.add_argument( + "--log-level", + choices=["DEBUG", "INFO", "WARNING", "ERROR"], + help="Log level (default: INFO)", + ) + + args = parser.parse_args() + + # Load configuration + config = load_config(args.config) + + if args.log_level: + config["logging"]["level"] = args.log_level + + # Don't initialize radio here - it will be done inside the async event loop + daemon = RepeaterDaemon(config, radio=None) + + # Run + try: + asyncio.run(daemon.run()) + except KeyboardInterrupt: + logger.info("Repeater stopped") + except Exception as e: + logger.error(f"Fatal error: {e}", exc_info=True) + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/repeater/templates/configuration.html b/repeater/templates/configuration.html new file mode 100644 index 0000000..79e053d --- /dev/null +++ b/repeater/templates/configuration.html @@ -0,0 +1,216 @@ + + + + pyMC Repeater - Configuration + + + + + +
+ + + + +
+
+

Configuration

+

System configuration and settings

+
+ +
+ Configuration is read-only. To modify settings, edit the config file and restart the daemon. +
+ + +

Radio Settings

+
+
+
Frequency
+
Loading...
+
+
+
Spreading Factor
+
Loading...
+
+
+
Bandwidth
+
Loading...
+
+
+
TX Power
+
Loading...
+
+
+
Coding Rate
+
Loading...
+
+
+
Preamble Length
+
Loading...
+
+
+ + +

Repeater Settings

+
+
+
Node Name
+
Loading...
+
+
+
Local Hash
+
Loading...
+
+
+
Public Key
+
Loading...
+
+
+
Latitude
+
Loading...
+
+
+
Longitude
+
Loading...
+
+
+
Mode
+
Loading...
+
+
+
Periodic Advertisement Interval
+
Loading...
+
How often the repeater sends an advertisement packet (0 = disabled)
+
+
+ + +

Duty Cycle

+
+
+
Max Airtime %
+
Loading...
+
+
+
Enforcement
+
Loading...
+
+
+ + +

Transmission Delays

+
+
+
Flood TX Delay Factor
+
Loading...
+
Multiplier for flood packet transmission delays (collision avoidance)
+
+
+
Direct TX Delay Factor
+
Loading...
+
Base delay for direct-routed packet transmission (seconds)
+
+
+
+
+ + + + diff --git a/repeater/templates/dashboard.html b/repeater/templates/dashboard.html new file mode 100644 index 0000000..4c65777 --- /dev/null +++ b/repeater/templates/dashboard.html @@ -0,0 +1,712 @@ + + + + pyMC Repeater Stats + + + + + + +
+ + + + +
+
+

Repeater Dashboard

+
+ System Status: Active + Updated: {{ last_updated }} +
+
+ + +
+
+
RX Packets
+
{{ rx_count }}total
+
+ +
+
Forwarded
+
{{ forwarded_count }}packets
+
+ +
+
Uptime
+
{{ uptime_hours }}h
+
+ +
+
Dropped
+
{{ dropped_count }}packets
+
+
+ + +

Performance Metrics

+
+
+

Packet Rate (RX/TX per hour)

+
+ +
+
+ +
+

Signal Quality Distribution

+
+ +
+
+
+ + +

Recent Packets

+
+ + + + + + + + + + + + + + + + + + + + +
TimeTypeRouteLenPath / HashesRSSISNRScoreTX DelayStatus
+ No packets received yet - waiting for traffic... +
+
+ +
+ Real-time updates enabled +
+
+
+ + + + + + diff --git a/repeater/templates/help.html b/repeater/templates/help.html new file mode 100644 index 0000000..7b91ef4 --- /dev/null +++ b/repeater/templates/help.html @@ -0,0 +1,934 @@ + + + + pyMC Repeater - Help + + + + + + +
+ + + + +
+
+

Help & Documentation

+

Learn how to interpret packet data, understand scoring, and optimize your configuration

+
+ +
+ + + + +
+ +
+

Packet Table Overview

+ +

The packet table displays real-time information about every packet your repeater receives and processes. Each row represents a single packet event, showing transmission details, signal quality metrics, and repeater processing information.

+ +
+

Purpose: The packet table helps you monitor network traffic, diagnose signal issues, and understand how your repeater is handling different types of packets.

+
+
+ + +
+

Column Details

+ +
+

Time

+
Format: HH:MM:SS
+
+ The exact time the packet was received by the radio module. Displayed in 24-hour format. Useful for correlating events with logs and identifying traffic patterns throughout the day. +
+
+ +
+

Type

+
Packet payload type identifier
+
+ ADVERT: Node advertisement/discovery packets (usually broadcasts)
+ ACK: Acknowledgment responses
+ TXT: Text messages
+ GRP: Group messages
+ PATH: Path information packets
+ RESP: Response packets
+ TRACE: Trace/debug packets
+ +
+
+ +
+

Route

+
Routing mode indicator
+
+ DIRECT: Packet explicitly routed to this repeater (contains its address in the path)
+ FLOOD: Broadcast packet intended for all nodes in range
+ DIRECT packets have higher priority since they're specifically addressed to your repeater. FLOOD packets are retransmitted if bandwidth allows. +
+
+ +
+

Length

+
Payload size in bytes
+
+ The actual payload data size (not including LoRa overhead). Affects airtime consumption and score calculation. Larger packets take longer to transmit, consuming more airtime budget. Typical range: 20-250 bytes. +
+
+ +
+

RSSI

+
Received Signal Strength Indicator (dBm)
+
+ Measures signal power. More negative = weaker signal
+ Excellent: -80 to -100 dBm (strong)
+ Good: -100 to -120 dBm (acceptable)
+ Poor: -120 to -140 dBm (weak, may be unreliable)
+ Affects score calculation - better RSSI yields higher scores. Distance and obstacles reduce RSSI. +
+
+ +
+

SNR

+
Signal-to-Noise Ratio (dB)
+
+ Measures signal clarity vs. background noise. Higher = cleaner signal
+ Excellent: SNR > 10 dB (very clean)
+ Good: SNR 5-10 dB (normal operation)
+ Poor: SNR < 5 dB (noisy environment)
+ Even with weak RSSI, high SNR indicates reliable reception. Critical for score calculation. +
+
+ +
+

Score

+
Composite quality metric (0.0 - 1.0)
+
+ A single number representing overall packet quality based on SNR and packet length. This matches the C++ MeshCore algorithm exactly. Higher scores (closer to 1.0) indicate better quality packets with good SNR relative to the spreading factor threshold. Used internally for optional reactive delay optimization (when use_score_for_tx is enabled). See Scoring System section for detailed calculation method. +
+
+ +
+

TX Delay

+
Time in milliseconds
+
+ How long the repeater waited before retransmitting. Delay factors include:
+ • Airtime budget checking
+ • Random collision avoidance (0-5ms factor)
+ • Current channel utilization
+ • Optional quality-based prioritization (when enabled)
+ Longer delays may indicate congestion or airtime throttling to comply with duty cycle limits. +
+
+ +
+

Status

+
Packet processing outcome
+
+ FORWARDED: Packet has been successfully retransmitted to other nodes. The repeater forwarded this packet over the air.
+ DROPPED: Packet was rejected and not forwarded.
+
+ Drop Reasons: +
    +
  • Duplicate: Packet hash already in cache. Prevents redundant retransmission.
  • +
  • Empty payload: Packet has no payload data. Cannot be processed.
  • +
  • Path at max size: Path field has reached maximum length. Cannot add repeater identifier.
  • +
  • Duty cycle limit: Airtime budget exhausted. Cannot transmit (EU 1% duty cycle or configured limit).
  • +
  • Direct: no path: Direct-mode packet lacks routing path.
  • +
  • Direct: not our hop: Direct-mode packet is not addressed to this repeater node.
  • +
+
+
+
+ + +
+

Scoring System

+ +

The packet score is calculated using the exact same algorithm as the C++ MeshCore implementation. It combines SNR (relative to spreading factor threshold) and packet length to produce a single quality indicator (0.0 to 1.0). This score can optionally be used for reactive delay optimization when use_score_for_tx is enabled.

+ +

The Scoring Formula

+ +
+
+
+ Score = SNR Factor × Length Factor +
+ + + + + + +
+
SNR Factor
+
+ (SNR - SFthreshold) / 10 +
+
+
Length Factor
+
+ (1 - length / 256) +
+
+
+ +

Spreading Factor Thresholds

+
+
+ SF7 → -7.5 dB +
+
+ SF8 → -10.0 dB +
+
+ SF9 → -12.5 dB +
+
+ SF10 → -15.0 dB +
+
+ SF11 → -17.5 dB +
+
+ SF12 → -20.0 dB +
+
+ +

Real-World Example

+
+

Packet Details:

+
    +
  • SNR: 12 dB
  • +
  • Spreading Factor: SF8
  • +
  • Payload Length: 100 bytes
  • +
+
+

Calculation:

+
+ SNR Factor = (12 - (-10)) / 10 = 22 / 10 = 2.2 (clamped to 1.0)
+ Length Factor = (1 - 100/256) = 0.609
+ Score = 1.0 × 0.609 = 0.61 (FAIR quality) +
+
+
+ +

This formula ensures that:

+
    +
  • Signal quality matters: Higher SNR produces higher scores, with SF-specific thresholds
  • +
  • Smaller packets score higher: They consume less airtime due to shorter transmission time
  • +
  • Poor SNR packets may score zero: If SNR falls below SF threshold, score = 0.0
  • +
+ +

Score Interpretation

+ +
+ +
+
Quality Scale
+
+
+ 0.0 + 0.25 + 0.5 + 0.75 + 1.0 +
+
+ + +
+
+
0.9 - 1.0 Excellent
+
Perfect conditions, high SNR, small payload
+
+ +
+
0.7 - 0.9 Good
+
Normal operation, acceptable signal
+
+ +
+
0.5 - 0.7 Fair
+
Degraded conditions, lower SNR
+
+ +
+
0.3 - 0.5 Poor
+
Marginal conditions, weak signal
+
+ +
+
< 0.3 Very Poor
+
Barely usable, may be dropped
+
+
+
+
+ + +
+

What Affects Your Score?

+ +

Primary Factors

+ +
+

Signal-to-Noise Ratio (SNR)

+
+ Impact: HIGHEST
+ Each 1 dB improvement in SNR can increase score by ~0.05. High interference environments significantly reduce scores. The repeater benefits from placement with clear LoS (line of sight) to minimize multipath and fading. +
+
+ +
+

Packet Payload Length

+
+ Impact: HIGH
+ Larger packets consume more airtime due to longer transmission times. A 100-byte packet scores lower than a 50-byte packet with identical SNR. +
+ +
+

RSSI (Signal Strength)

+
+ Impact: NOT USED IN SCORING
+ RSSI is displayed for monitoring purposes but does NOT affect the score calculation. The C++ MeshCore algorithm uses only SNR and packet length. However, RSSI correlates with SNR - better RSSI typically means better SNR, which indirectly results in higher scores. +
+
+ +

Environmental Factors

+ +
    +
  • Weather: Rain and fog reduce signal strength and increase noise
  • +
  • Time of Day: Atmospheric conditions change, especially during dawn/dusk
  • +
  • Frequency Congestion: More devices on 869 MHz = higher noise floor
  • +
  • Physical Obstructions: Buildings and trees block signals, increase fading
  • +
  • Antenna Orientation: Poor antenna alignment reduces SNR significantly
  • +
+ +
+

Environmental Issues: If you see consistently low scores across many packets, check your antenna placement, orientation, and surroundings. Poor environmental conditions are often the limiting factor, not the repeater itself.

+
+
+ + +
+

Reactive Score-Based Delay Optimization

+ +

The repeater includes an optional reactive scoring system that dynamically prioritizes packets based on signal quality during network congestion. This feature matches the C++ MeshCore behavior for intelligent packet prioritization.

+ +

How It Works

+ +
+

Key Principle: When the repeater detects congestion (calculated TX delay ≥ 50ms), it automatically applies a quality-based delay multiplier to high-quality packets, giving them priority while gracefully backing off low-quality packets.

+

Default Behavior: This feature is disabled by default (use_score_for_tx: false). When disabled, all packets follow standard C++ MeshCore delay calculation with pure randomization.

+
+ +

Delay Multiplier Formula

+ +
+
+
+ Applied Only When: delay ≥ 50ms AND use_score_for_tx = true +
+ +
+ Delay Multiplier = max(0.2, 1.0 - score) +
+ +
+

What this means:

+
    +
  • Perfect packet (score 1.0): Multiplier = max(0.2, 0.0) = 0.2 → Gets 20% of base delay (fast priority)
  • +
  • Good packet (score 0.7): Multiplier = max(0.2, 0.3) = 0.3 → Gets 30% of base delay
  • +
  • Fair packet (score 0.5): Multiplier = max(0.2, 0.5) = 0.5 → Gets 50% of base delay
  • +
  • Poor packet (score 0.2): Multiplier = max(0.2, 0.8) = 0.8 → Gets 80% of base delay (slower, backoff)
  • +
  • Minimum floor: No packet gets less than 20% multiplier (prevents starvation)
  • +
+
+
+
+ +

Example: Reactive Scoring in Action

+ +
+

Scenario: Two packets arrive during congestion (base delay 100ms), tx_delay_factor=1.0

+
    +
  • Packet X: Excellent signal, score = 0.9
  • +
  • Packet Y: Weak signal, score = 0.4
  • +
+

Without Reactive Scoring (disabled):

+
    +
  • Packet X: TX Delay = 0-500ms (pure random collision avoidance)
  • +
  • Packet Y: TX Delay = 0-500ms (pure random collision avoidance)
  • +
  • Result: Both may transmit at same time, causing collision
  • +
+

With Reactive Scoring (enabled, congestion detected):

+
    +
  • Packet X: Multiplier = 0.1 → TX Delay = 0-50ms (high priority, transmits first)
  • +
  • Packet Y: Multiplier = 0.6 → TX Delay = 0-300ms (lower priority, waits longer)
  • +
  • Result: High-quality packets forward with minimal delay; marginal packets gracefully back off
  • +
+
+ +

Configuration

+ +
+

use_score_for_tx

+
Enable/disable reactive score-based delay optimization
+
+ Default: false (disabled)
+ Options: true or false
+ When true: Activates quality-based delay multiplier when congestion detected (delay ≥ 50ms)
+ When false: Standard C++ MeshCore behavior, pure random delays, no score influence on timing
+ Location in config.yaml: +
+ repeater:
+   use_score_for_tx: false +
+
+
+ +
+

score_threshold

+
Reserved for future enhancement / statistics monitoring
+
+ Default: 0.3
+ Range: 0.0 - 1.0
+ Current Status: This value is read from config but not currently used in packet processing. It is reserved for future features.
+ Future Potential Uses: +
    +
  • Dashboard quality alerts when average packet score drops below threshold
  • +
  • Proactive packet filtering - dropping very poor quality packets upfront (below threshold)
  • +
  • Quality monitoring and trend statistics in web UI
  • +
  • Logging alerts for poor signal conditions
  • +
+ Recommendation: Leave at default (0.3). Changing it currently has no effect on packet processing. This setting will become active once future quality monitoring features are implemented. +
+
+ +

When to Enable Reactive Scoring

+ +
+
+
Enable (use_score_for_tx: true)
+

+ • High-traffic networks where collisions are frequent
+ • Noisy environments with poor average signal quality
+ • You want to prioritize high-quality packets during congestion
+ • Testing adaptive network behavior
+ • Duty-cycle constrained regions (EU) with limited bandwidth +

+
+ +
+
Disable (use_score_for_tx: false)
+

+ • Low-traffic networks where congestion is rare
+ • You want pure C++ MeshCore compatibility
+ • Consistent delay behavior is more important than efficiency
+ • New deployments - start simple and tune later
+ +

+
+
+ +
+

Important: Reactive scoring only affects TX delay timing, not packet forwarding decisions. All packets still get forwarded (unless dropped for other reasons like duplicates or duty cycle). The system gracefully prioritizes quality during congestion without dropping packets, matching MeshCore's intelligent backpressure strategy.

+
+
+

Configuration Impact on Scoring

+ +

Your repeater's configuration settings directly affect packet scoring and processing behavior.

+ +

Radio Configuration Parameters

+ +
+
+
Spreading Factor (SF)
+

Current setting: SF 8
+ Higher SF (9-12): Better range and SNR, but slower transmission, more airtime consumed
+ Lower SF (7): Faster transmission, less airtime, but worse sensitivity and range
+ Score impact: Higher SF generally improves SNR = higher scores, but increases payload duration penalty

+
+ +
+
Bandwidth (BW)
+

Current setting: 62.5 kHz
+ Wider BW (125 kHz): Faster data rate, less airtime per byte, but worse sensitivity
+ Narrower BW (31.25 kHz): Better sensitivity, but slower transmission
+ Score impact: BW affects SNR - narrower = potentially better SNR but longer TX times

+
+ +
+
TX Power
+

Current setting: 14 dBm
+ Higher power: Better outbound range, but may increase noise at nearby receivers
+ Lower power: Reduces interference, saves energy, but limits outbound range
+ Score impact: TX power only affects outgoing transmissions, not received score

+
+ +
+
Coding Rate (CR)
+

Current setting: 4/8
+ Higher CR (4/7): Less error correction, faster transmission, more airtime efficient
+ Lower CR (4/8): More error correction, better resilience to interference
+ Score impact: Higher CR can improve SNR in clean environments, reduce it in noisy ones

+
+
+ +

Duty Cycle Configuration

+ +
+

Current Duty Cycle Limit: 6% max airtime per hour

+

This means your repeater can spend at most 3.6 minutes (21.6 seconds per minute) transmitting per hour. How this affects packet handling:

+
    +
  • When below limit: All packets retransmitted if they pass validation
  • +
  • When approaching limit: Incoming packets may be dropped if airtime budget is exhausted
  • +
  • When limit reached: All new transmissions are dropped until the duty cycle budget resets (each minute)
  • +
+

Important: The repeater does NOT queue packets for later transmission. When duty cycle limit is reached, packets are immediately dropped. This is by design - a repeater must forward immediately or drop the packet. Note: Packet score does not affect duty cycle enforcement - all packets are treated equally when duty cycle limit is reached.

+
+ +

Airtime Consumption Example

+ +
+

Scenario: 100-byte packet at SF8, BW 62.5 kHz, CR 4/8
+ Airtime: ~512 ms
+ At 6% duty cycle: Can transmit ~420 packets/hour maximum
+ Effect on score: High volume of large packets will consume budget quickly, causing lower-scored packets to be dropped +

+
+ + + +
+

Configuration Settings Reference

+ +

The repeater is configured via config.yaml. This section explains key settings and how they affect packet performance.

+ +
+

Important: Packet Score (signal quality) and TX Delay (collision avoidance timing) are independent systems. Score is calculated from SNR and packet length. Delays are configured via tx_delay_factor and direct_tx_delay_factor and are based on airtime, not signal quality.

+
+ +

Delay Settings

+ +
+

tx_delay_factor

+
Flood mode transmission delay multiplier
+
+ Default: 1.0
+ Purpose: Scales the base collision-avoidance delay for flood packets.
+ Formula: delay = random(0-5) × (airtime × 52/50 ÷ 2) × tx_delay_factor
+ Effect: Higher values = longer delays between flood packet retransmissions, reducing collisions but increasing latency. Lower values speed up propagation in low-traffic areas.
+ Typical range: 0.5 - 2.0 (0.5 = faster, 2.0 = collision-resistant) +
+
+ +
+

direct_tx_delay_factor

+
Direct mode transmission delay (in seconds)
+
+ Default: 0.5 seconds
+ Purpose: Fixed delay for direct-routed packets (packets specifically addressed to this repeater).
+ Effect: Direct packets wait this many seconds before retransmission. Direct packets bypass the collision-avoidance algorithm and use a fixed delay instead.
+ Note: Typically lower than flood delays to prioritize DIRECT packets. 0 = immediate forwarding.
+ Typical range: 0 - 2.0 seconds +
+
+ +

How TX Delay is Calculated

+ +

The TX Delay shown in the packet table follows the MeshCore C++ implementation for collision avoidance:

+ +
+

For FLOOD packets (broadcast):
+ TX Delay = random(0 to 5) × (airtime_ms × 52/50 ÷ 2) × tx_delay_factor ÷ 1000

+ For DIRECT packets (addressed to this repeater):
+ TX Delay = direct_tx_delay_factor (fixed, in seconds)

+ Optional Reactive Scoring:
+ If use_score_for_tx is enabled AND delay ≥ 50ms:
+ TX Delay = base_delay × max(0.2, 1.0 - packet_score)
+ This applies a quality-based multiplier during congestion: high-score packets get shorter delays (priority), low-score packets get longer delays (backoff).

+ Example: FLOOD packet with 100ms airtime, tx_delay_factor=1.0, score=0.8:
+ • Base delay = (100 × 52/50 ÷ 2) = 52 ms
+ • With random(0-5) multiplier: 0-260 ms (before score adjustment)
+ • If ≥50ms AND score adjustment active: 0-260ms × max(0.2, 1.0-0.8) = 0-260ms × 0.2 = 0-52ms (prioritized)

+ Tuning: Increase tx_delay_factor in high-traffic areas to reduce collisions. Decrease in low-traffic areas for faster propagation. Enable use_score_for_tx for intelligent priority during congestion. Direct packets bypass randomization and use fixed delays. +

+
+ +

Duty Cycle Constraints

+ +
+

max_airtime_per_minute

+
Maximum transmission time per minute in milliseconds
+
+ Common values:
+ • 3600 ms/min = 100% duty cycle (US/AU FCC, no restriction)
+ • 36 ms/min = 1% duty cycle (EU ETSI standard)
+ • 360 ms/min = 10% duty cycle (compromise for EU testing)

+ Effect on packet handling: Duty cycle enforcement is independent of packet score. When duty cycle limit is reached, ALL packets are dropped equally - regardless of signal quality. The system does not prioritize high-score packets; it simply refuses to transmit until the budget resets.
+ TX Delay impact: TX Delay shown in the packet table is unaffected by duty cycle limits. However, packets may be completely blocked (dropped) when airtime budget is exhausted. There is no queuing or delay-until-later mechanism - dropped packets are lost immediately.
+ Packet distribution during high traffic: When approaching or exceeding duty cycle limits (>80%), incoming packets are dropped indiscriminately based on airtime availability. The mean packet score will fluctuate based on random traffic mix, not because the system prefers high-score packets. All packets have equal probability of being dropped when budget is exhausted. +
+
+ +

How These Work Together

+ +
+

Example Scenario - Packet Forwarding with Delay:

+

You receive 3 packets with different routes and sizes (tx_delay_factor=1.0, direct_tx_delay_factor=0.5s):

+
    +
  • Packet A: Route DIRECT, 50 bytes → TX Delay = 0.5 seconds (fixed)
  • +
  • Packet B: Route FLOOD, 100 bytes → TX Delay = random(0-5) × 52ms × 1.0 = 0-260 ms
  • +
  • Packet C: Route FLOOD, 150 bytes → TX Delay = random(0-5) × 78ms × 1.0 = 0-390 ms
  • +
+

Processing order (without duty cycle limits):

+
    +
  • Packet A: Waits 0.5s, then forwards (direct packets get fixed priority)
  • +
  • Packets B & C: Random delays prevent collision, lower packet transmitted first if random lucky
  • +
+

If duty cycle ~95% full: Still forwards all three, but with increased TX delays. If insufficient airtime remains for a packet, it is dropped immediately (not queued)

+
+ +

Optimization Tips

+ +
    +
  • For high-traffic/interference: Increase tx_delay_factor to 1.5-2.0 to reduce collisions with more randomization
  • +
  • For low-traffic areas: Decrease tx_delay_factor to 0.5 for faster propagation
  • +
  • For priority direct packets: Lower direct_tx_delay_factor below 0.5s for faster handling
  • +
  • For duty-cycle constrained regions (EU): Keep default settings; airtime budget enforces fairness
  • +
  • Monitor TX Delay column: Increasing delays indicate network congestion or approaching duty cycle limits
  • +
+
+ + ↑ Back to Top +
+
+
+
+ + + + diff --git a/repeater/templates/logs.html b/repeater/templates/logs.html new file mode 100644 index 0000000..22317e0 --- /dev/null +++ b/repeater/templates/logs.html @@ -0,0 +1,238 @@ + + + + pyMC Repeater - Logs + + + + + +
+ + + + +
+
+

System Logs

+

Real-time system events and diagnostics

+
+ + +
+
+ + + +
+
+ +
+
+ + +
+
+ [Loading...] + INFO + Fetching system logs... +
+
+
+
+ + + + diff --git a/repeater/templates/nav.html b/repeater/templates/nav.html new file mode 100644 index 0000000..95f9175 --- /dev/null +++ b/repeater/templates/nav.html @@ -0,0 +1,432 @@ + + + + + + diff --git a/repeater/templates/neighbors.html b/repeater/templates/neighbors.html new file mode 100644 index 0000000..117d6d9 --- /dev/null +++ b/repeater/templates/neighbors.html @@ -0,0 +1,395 @@ + + + + pyMC Repeater - Neighbors + + + + + +
+ + + + +
+
+

Neighbor Repeaters

+
+ Tracking: 0 repeaters + Updated: {{ last_updated }} +
+
+ + +
+

Discovered Repeaters

+
+ + + + + + + + + + + + + + + + + + + +
Node NamePublic KeyContact TypeLocationRSSISNRLast SeenFirst SeenAdvert Count
+ No repeaters discovered yet - waiting for adverts... +
+
+
+
+
+ + + + + + diff --git a/repeater/templates/statistics.html b/repeater/templates/statistics.html new file mode 100644 index 0000000..64e1009 --- /dev/null +++ b/repeater/templates/statistics.html @@ -0,0 +1,335 @@ + + + + pyMC Repeater - Statistics + + + + + + + +
+ + + + +
+
+

Statistics

+

Detailed performance analytics and metrics

+
+ + +

Summary

+
+
+
Total RX
+
0packets
+
+ +
+
Total TX
+
0packets
+
+ +
+
Success Rate
+
0%
+
+
+ + +

Performance Charts

+
+
+

RX vs TX Over Time

+
+ +
+
+ +
+

Packet Type Distribution

+
+ +
+
+ +
+

Signal Metrics Over Time

+
+ +
+
+ +
+

Route Type Distribution

+
+ +
+
+
+
+
+ + + + diff --git a/repeater/templates/style.css b/repeater/templates/style.css new file mode 100644 index 0000000..ed16ed9 --- /dev/null +++ b/repeater/templates/style.css @@ -0,0 +1,1848 @@ +/* ============================================================================ + LoRa Repeater Dashboard - Professional Design System + + Design Philosophy: + - Modern, minimal aesthetic inspired by contemporary design systems + - Consistent spacing, subtle depth, and refined typography + - Accessible colour palette with WCAG AA+ contrast ratios + - Responsive, mobile-first approach + ============================================================================ */ + +/* ============================================================================ + COLOUR PALETTE & DESIGN TOKENS + ============================================================================ */ + +:root { + /* Colour Palette */ + --color-bg-primary: #0f1419; + --color-bg-secondary: #1a1f2e; + --color-bg-tertiary: #252d3d; + --color-bg-hover: #2d3547; + + --color-text-primary: #f1f3f5; + --color-text-secondary: #a8b1c3; + --color-text-tertiary: #7d8599; + + --color-border: #3a4454; + --color-border-light: #2d3547; + + /* Brand Accent */ + --color-accent-primary: #00d4ff; + --color-accent-primary-hover: #00e5ff; + --color-accent-primary-dim: rgba(0, 212, 255, 0.15); + + /* Status Colours (accessible) */ + --color-success: #10b981; + --color-success-dim: rgba(16, 185, 129, 0.15); + --color-warning: #f59e0b; + --color-warning-dim: rgba(245, 158, 11, 0.15); + --color-error: #ef4444; + --color-error-dim: rgba(239, 68, 68, 0.15); + --color-info: #3b82f6; + --color-info-dim: rgba(59, 130, 246, 0.15); + + /* Spacing Scale (8px base) */ + --spacing-xs: 0.25rem; /* 4px */ + --spacing-sm: 0.5rem; /* 8px */ + --spacing-md: 1rem; /* 16px */ + --spacing-lg: 1.5rem; /* 24px */ + --spacing-xl: 2rem; /* 32px */ + --spacing-2xl: 3rem; /* 48px */ + + /* Sizing */ + --size-sidebar: 280px; + --size-header-height: 64px; + + /* Shadows */ + --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05); + --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1); + --shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1); + --shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.15); + + /* Border Radius */ + --radius-sm: 0.375rem; /* 6px */ + --radius-md: 0.5rem; /* 8px */ + --radius-lg: 0.75rem; /* 12px */ + + /* Transitions */ + --transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1); + --transition-base: 250ms cubic-bezier(0.4, 0, 0.2, 1); + --transition-slow: 350ms cubic-bezier(0.4, 0, 0.2, 1); + + /* Typography */ + --font-family-base: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", sans-serif; + --font-family-mono: "SFMono-Regular", "Consolas", "Liberation Mono", "Menlo", monospace; + + --font-size-xs: 0.75rem; /* 12px */ + --font-size-sm: 0.875rem; /* 14px */ + --font-size-base: 1rem; /* 16px */ + --font-size-lg: 1.125rem; /* 18px */ + --font-size-xl: 1.25rem; /* 20px */ + --font-size-2xl: 1.5rem; /* 24px */ + --font-size-3xl: 2rem; /* 32px */ + + --font-weight-regular: 400; + --font-weight-medium: 500; + --font-weight-semibold: 600; + --font-weight-bold: 700; +} + +/* ============================================================================ + RESET & GLOBAL STYLES + ============================================================================ */ + +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +body { + font-family: var(--font-family-base); + font-size: var(--font-size-base); + font-weight: var(--font-weight-regular); + line-height: 1.5; + color: var(--color-text-primary); + background: var(--color-bg-primary); + height: 100vh; +} + +/* Reset link styling globally */ +a { + color: inherit; + text-decoration: none; +} + +a:visited { + color: inherit; +} + +/* ============================================================================ + LAYOUT: SIDEBAR + CONTENT + ============================================================================ */ + +.layout { + display: flex; + height: 100vh; + overflow: hidden; +} + +/* Desktop layout - no extra padding */ +@media (min-width: 769px) { + .layout { + padding-top: 0; + } +} + +/* SIDEBAR */ + +.sidebar { + width: var(--size-sidebar); + background: var(--color-bg-secondary); + border-right: 1px solid var(--color-border); + display: flex; + flex-direction: column; + overflow-y: auto; + overflow-x: hidden; +} + +.sidebar::-webkit-scrollbar { + width: 6px; +} + +.sidebar::-webkit-scrollbar-track { + background: var(--color-bg-secondary); +} + +.sidebar::-webkit-scrollbar-thumb { + background: var(--color-border); + border-radius: var(--radius-sm); +} + +.sidebar::-webkit-scrollbar-thumb:hover { + background: var(--color-border-light); +} + +/* SIDEBAR HEADER */ + +.sidebar-header { + padding: var(--spacing-lg); + border-bottom: 1px solid var(--color-border); + background: linear-gradient( + 135deg, + rgba(0, 212, 255, 0.1) 0%, + rgba(59, 130, 246, 0.05) 100% + ); +} + +.sidebar-header h1 { + font-size: var(--font-size-xl); + font-weight: var(--font-weight-bold); + color: var(--color-accent-primary); + margin-bottom: var(--spacing-xs); + letter-spacing: -0.5px; +} + +.sidebar-header .node-name { + font-size: var(--font-size-sm); + color: var(--color-text-secondary); + font-weight: var(--font-weight-regular); +} + +.sidebar-header .node-pubkey { + font-size: 0.7rem; + color: var(--color-text-tertiary); + font-family: 'Courier New', monospace; + margin-top: 0.25rem; + word-break: break-all; +} + +/* Hide menu toggle on desktop */ +.menu-toggle { + display: none !important; +} + +/* ============================================================================ + MODERN SIDEBAR NAVIGATION + ============================================================================ */ + +.sidebar-nav { + flex-grow: 1; + display: flex; + flex-direction: column; + gap: 2rem; + padding: 1rem; +} + +.nav-section { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.nav-section-title { + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.08em; + font-weight: 600; + color: var(--color-text-tertiary); + margin-bottom: 0.5rem; + padding: 0; +} + +.nav-item { + display: flex; + align-items: center; + gap: 0.75rem; + color: var(--color-text-secondary); + text-decoration: none; + padding: 0.85rem 1rem; + border-radius: 12px; + transition: background 250ms ease, color 250ms ease; + font-size: 0.95rem; + font-weight: 500; + cursor: pointer; + user-select: none; +} + +.nav-item:hover { + background: rgba(255, 255, 255, 0.08); + color: #60a5fa; +} + +.nav-item.active { + background: #3b82f6; + color: #ffffff; + font-weight: 600; + box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.3); +} + +.nav-item .icon { + width: 1.5rem; + height: 1.5rem; + flex-shrink: 0; + stroke: currentColor; + stroke-width: 1.5; + opacity: 0.9; + transition: opacity 250ms ease; +} + +.nav-item:hover .icon, +.nav-item.active .icon { + opacity: 1; +} + +.nav-action { + background: rgba(16, 185, 129, 0.15) !important; + border: 1px solid rgba(16, 185, 129, 0.4); + cursor: pointer; + color: #ffffff; +} + +.nav-action:hover { + background: rgba(16, 185, 129, 0.25) !important; + color: #ffffff; + border-color: rgba(16, 185, 129, 0.6); +} + +.nav-action:active { + background: rgba(16, 185, 129, 0.35) !important; + color: #ffffff; +} + +.nav-action:disabled { + opacity: 0.6; + cursor: not-allowed; + background: rgba(16, 185, 129, 0.1) !important; + color: rgba(255, 255, 255, 0.6); +} + +.nav-button { + display: flex; + align-items: center; + gap: 0.75rem; + color: #ffffff; + background: linear-gradient(135deg, #10b981 0%, #059669 100%); + border: none; + padding: 0.85rem 1rem; + border-radius: 12px; + transition: all 250ms ease; + font-size: 0.95rem; + font-weight: 600; + cursor: pointer; + user-select: none; + width: 100%; + text-align: left; + box-shadow: 0 2px 8px rgba(16, 185, 129, 0.3); +} + +.nav-button:hover { + background: linear-gradient(135deg, #059669 0%, #047857 100%); + box-shadow: 0 4px 12px rgba(16, 185, 129, 0.4); + transform: translateY(-1px); +} + +.nav-button:active { + transform: translateY(0); + box-shadow: 0 2px 6px rgba(16, 185, 129, 0.3); +} + +.nav-button .icon { + width: 1.5rem; + height: 1.5rem; + flex-shrink: 0; + stroke: currentColor; + stroke-width: 1.5; +} + +svg.icon { + display: inline-block; + width: 1.5rem !important; + height: 1.5rem !important; + min-width: 1.5rem; + min-height: 1.5rem; + vertical-align: middle; + flex-shrink: 0; +} + +/* SIDEBAR CONTENT WRAPPER - Desktop: transparent wrapper, doesn't affect layout */ + +.sidebar-content-wrapper { + display: contents; /* Makes wrapper invisible on desktop, children act as if wrapper doesn't exist */ +} + +/* SIDEBAR FOOTER */ + +.sidebar-footer { + margin-top: auto; /* Push footer to bottom of sidebar */ + padding: var(--spacing-lg); + border-top: 1px solid var(--color-border); + background: var(--color-bg-tertiary); +} + +.status-badge { + display: inline-block; + background: var(--color-success-dim); + color: var(--color-success); + padding: var(--spacing-xs) var(--spacing-sm); + border-radius: var(--radius-sm); + font-size: var(--font-size-xs); + font-weight: var(--font-weight-semibold); +} + +.version-badge { + display: inline-block; + background: rgba(100, 116, 139, 0.15); + color: var(--color-text-secondary); + padding: var(--spacing-xs) var(--spacing-sm); + border-radius: var(--radius-sm); + font-size: var(--font-size-xs); + font-weight: var(--font-weight-semibold); + font-family: monospace; +} + +/* Control Buttons */ +.control-buttons { + display: flex; + flex-direction: column; + gap: var(--spacing-sm); + margin-bottom: var(--spacing-md); +} + +.control-btn { + display: flex; + align-items: center; + gap: var(--spacing-sm); + padding: var(--spacing-sm) var(--spacing-md); + background: var(--color-bg-secondary); + border: 1px solid var(--color-border); + border-radius: var(--radius-md); + color: var(--color-text-secondary); + cursor: pointer; + transition: all var(--transition-base); + font-family: var(--font-family-base); + font-size: var(--font-size-sm); + width: 100%; + text-align: left; +} + +.control-btn:hover:not(.control-btn-active):not(.control-btn-warning) { + background: var(--color-bg-hover); + border-color: var(--color-accent-primary); + transform: translateY(-1px); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +} + +.control-btn.control-btn-active:hover, +.control-btn.control-btn-warning:hover { + transform: translateY(-1px); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); + filter: brightness(1.1); +} + +.control-btn:active { + transform: translateY(0); +} + +.control-btn:disabled { + opacity: 0.5; + cursor: not-allowed; + transform: none; +} + +.control-btn .icon { + width: 1.25rem; + height: 1.25rem; + flex-shrink: 0; + stroke: currentColor; +} + +.control-label { + display: flex; + flex-direction: column; + gap: 2px; + flex: 1; +} + +.control-title { + font-size: var(--font-size-xs); + color: var(--color-text-tertiary); + text-transform: uppercase; + letter-spacing: 0.5px; + font-weight: var(--font-weight-semibold); +} + +.control-value { + font-size: var(--font-size-sm); + color: var(--color-text-primary); + font-weight: var(--font-weight-semibold); +} + +.control-btn.control-btn-active { + border-color: var(--color-success) !important; + background: var(--color-success-dim) !important; +} + +.control-btn.control-btn-active .icon { + stroke: var(--color-success) !important; +} + +.control-btn.control-btn-active .control-title { + color: var(--color-text-secondary) !important; +} + +.control-btn.control-btn-active .control-value { + color: var(--color-success) !important; +} + +.control-btn.control-btn-warning { + border-color: var(--color-warning) !important; + background: var(--color-warning-dim) !important; +} + +.control-btn.control-btn-warning .icon { + stroke: var(--color-warning) !important; +} + +.control-btn.control-btn-warning .control-title { + color: var(--color-text-secondary) !important; +} + +.control-btn.control-btn-warning .control-value { + color: var(--color-warning) !important; +} + +.duty-cycle-stats { + margin: var(--spacing-md) 0; +} + +.duty-cycle-bar-container { + width: 100%; + height: 6px; + background: var(--color-bg-secondary); + border-radius: 3px; + overflow: hidden; + margin-bottom: var(--spacing-xs); +} + +.duty-cycle-bar { + height: 100%; + background: var(--color-success); + transition: width 0.3s ease, background-color 0.3s ease; + border-radius: 3px; +} + +.duty-cycle-text { + display: block; + color: var(--color-text-secondary); + font-size: var(--font-size-xs); + line-height: 1.4; +} + +.duty-cycle-text strong { + color: var(--color-text-primary); + font-weight: var(--font-weight-semibold); +} + +.sidebar-footer small { + display: block; + color: var(--color-text-tertiary); + font-size: var(--font-size-xs); + line-height: 1.6; +} + +/* CONTENT AREA */ + +.content { + flex: 1; + overflow-y: auto; + overflow-x: hidden; + padding: var(--spacing-2xl); + background: var(--color-bg-primary); +} + +.content::-webkit-scrollbar { + width: 8px; +} + +.content::-webkit-scrollbar-track { + background: var(--color-bg-primary); +} + +.content::-webkit-scrollbar-thumb { + background: var(--color-border); + border-radius: var(--radius-sm); +} + +.content::-webkit-scrollbar-thumb:hover { + background: var(--color-text-tertiary); +} + + +/* ============================================================================ + HEADER & TYPOGRAPHY + ============================================================================ */ + +header { + margin-bottom: var(--spacing-2xl); + border-bottom: 1px solid var(--color-border); + padding-bottom: var(--spacing-lg); +} + +h1 { + font-size: var(--font-size-3xl); + font-weight: var(--font-weight-bold); + color: var(--color-text-primary); + margin-bottom: var(--spacing-md); + letter-spacing: -0.5px; +} + +h2 { + font-size: var(--font-size-2xl); + font-weight: var(--font-weight-bold); + color: var(--color-text-primary); + margin: var(--spacing-2xl) 0 var(--spacing-lg) 0; + letter-spacing: -0.25px; +} + +h3 { + font-size: var(--font-size-lg); + font-weight: var(--font-weight-semibold); + color: var(--color-text-primary); +} + +.header-info { + display: flex; + justify-content: space-between; + align-items: center; + font-size: var(--font-size-sm); + color: var(--color-text-secondary); +} + + +/* ============================================================================ + STAT CARDS + ============================================================================ */ + +.stats-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); + gap: var(--spacing-lg); + margin-bottom: var(--spacing-2xl); +} + +.stat-card { + background: var(--color-bg-secondary); + padding: var(--spacing-lg); + border-radius: var(--radius-lg); + border: 1px solid var(--color-border); + box-shadow: var(--shadow-sm); + transition: all var(--transition-base); + border-left: 3px solid var(--color-accent-primary); +} + +.stat-card:hover { + border-color: var(--color-border-light); + box-shadow: var(--shadow-md); + transform: translateY(-2px); +} + +.stat-card.success { + border-left-color: var(--color-success); +} + +.stat-card.warning { + border-left-color: var(--color-warning); +} + +.stat-card.error { + border-left-color: var(--color-error); +} + +.stat-label { + display: block; + font-size: var(--font-size-xs); + font-weight: var(--font-weight-semibold); + color: var(--color-text-secondary); + text-transform: uppercase; + letter-spacing: 0.5px; + margin-bottom: var(--spacing-md); +} + +.stat-value { + font-size: var(--font-size-3xl); + font-weight: var(--font-weight-bold); + color: var(--color-accent-primary); + line-height: 1; + margin-bottom: var(--spacing-sm); +} + +.stat-unit { + font-size: var(--font-size-sm); + color: var(--color-text-tertiary); + margin-left: var(--spacing-xs); +} + + +/* ============================================================================ + CHARTS + ============================================================================ */ + +.charts-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(480px, 1fr)); + gap: var(--spacing-lg); + margin-bottom: var(--spacing-2xl); +} + +.chart-card { + background: var(--color-bg-secondary); + padding: var(--spacing-lg); + border-radius: var(--radius-lg); + border: 1px solid var(--color-border); + box-shadow: var(--shadow-sm); + transition: all var(--transition-base); +} + +.chart-card:hover { + border-color: var(--color-border-light); + box-shadow: var(--shadow-md); +} + +.chart-card h3 { + font-size: var(--font-size-sm); + font-weight: var(--font-weight-semibold); + color: var(--color-text-primary); + text-transform: uppercase; + letter-spacing: 0.5px; + margin-bottom: var(--spacing-lg); +} + +.chart-container { + position: relative; + height: 240px; +} + +/* Chart.js customization */ +.chart-container canvas { + max-height: 240px; +} + + +/* ============================================================================ + TABLES + ============================================================================ */ + +.table-container { + overflow-x: auto; + border-radius: var(--radius-lg); + box-shadow: var(--shadow-sm); +} + +table { + width: 100%; + border-collapse: collapse; + background: var(--color-bg-secondary); + border: 1px solid var(--color-border); + border-radius: var(--radius-lg); + overflow: hidden; +} + +thead { + background: linear-gradient( + 90deg, + rgba(0, 212, 255, 0.15) 0%, + rgba(59, 130, 246, 0.1) 100% + ); + border-bottom: 1px solid var(--color-border); +} + +th { + padding: var(--spacing-md); + text-align: left; + font-weight: var(--font-weight-semibold); + font-size: var(--font-size-xs); + color: var(--color-text-primary); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +td { + padding: var(--spacing-md); + border-bottom: 1px solid var(--color-border-light); + font-size: var(--font-size-sm); + color: var(--color-text-secondary); +} + +tbody tr { + transition: background-color var(--transition-fast); +} + +tbody tr:hover { + background: var(--color-bg-tertiary); +} + +tbody tr:last-child td { + border-bottom: none; +} + +/* Table cell styles */ +.packet-type { + color: var(--color-accent-primary); + font-weight: var(--font-weight-medium); + font-size: var(--font-size-xs); +} + +.route-flood { + color: var(--color-warning); + background: var(--color-warning-dim); + padding: var(--spacing-xs) var(--spacing-sm); + border-radius: var(--radius-sm); + font-weight: var(--font-weight-medium); + font-size: var(--font-size-xs); + display: inline-block; +} + +.route-direct { + color: var(--color-success); + background: var(--color-success-dim); + padding: var(--spacing-xs) var(--spacing-sm); + border-radius: var(--radius-sm); + font-weight: var(--font-weight-medium); + font-size: var(--font-size-xs); + display: inline-block; +} + +.score { + color: var(--color-accent-primary); + font-weight: var(--font-weight-semibold); +} + +.status-tx { + color: var(--color-success); + font-weight: var(--font-weight-medium); +} + +.status-wait { + color: var(--color-warning); + font-weight: var(--font-weight-medium); +} + +.status-drop { + color: var(--color-error); + font-weight: var(--font-weight-medium); +} + +/* Path and hash styling */ +.path-hash, +.src-dst-hash { + font-family: 'Courier New', monospace; + font-size: 0.85rem; + letter-spacing: 0.3px; + word-break: break-all; + line-height: 1.5; + display: block; + margin: var(--spacing-sm) 0; +} + +.path-hash { + color: #e0e0e0; + background: #2a2a2a; + padding: var(--spacing-sm) var(--spacing-md); + border-radius: 4px; + overflow-wrap: break-word; + border: 1px solid #404040; +} + +.src-dst-hash { + color: #ffffff; + background: #1e3a8a; + padding: var(--spacing-sm) var(--spacing-md); + border-radius: 4px; + font-weight: var(--font-weight-bold); + border: 1px solid #3b82f6; +} + +.my-hash { + background: #dc2626; + color: #ffffff; + padding: 2px 6px; + border-radius: 3px; + font-weight: var(--font-weight-bold); + display: inline-block; + font-size: 0.8rem; + border: 1px solid #ef4444; +} + +.path-transform { + color: #999999; + font-size: 0.75rem; + display: inline; +} + +.dupe-badge { + background: #991b1b; + color: #fca5a5; + padding: 3px 8px; + border-radius: 3px; + font-size: 0.7rem; + font-weight: var(--font-weight-bold); + display: inline-block; + margin-left: var(--spacing-sm); + border: 1px solid #dc2626; +} + +.drop-reason { + color: #fca5a5; + font-size: 0.75rem; + display: block; + margin-top: 3px; +} + +.na { + color: var(--color-text-tertiary); + font-style: italic; +} + +.empty-message { + text-align: center; + color: var(--color-text-tertiary); + padding: var(--spacing-2xl) var(--spacing-lg); + font-style: italic; + font-size: var(--font-size-sm); +} + + +/* ============================================================================ + CONFIGURATION SECTION + ============================================================================ */ + +.config-section { + background: var(--color-bg-secondary); + padding: var(--spacing-lg); + border-radius: var(--radius-lg); + border: 1px solid var(--color-border); + margin-bottom: var(--spacing-lg); + box-shadow: var(--shadow-sm); +} + +.config-section h3 { + margin-top: 0; + margin-bottom: var(--spacing-lg); + padding-bottom: var(--spacing-md); + border-bottom: 1px solid var(--color-border); +} + +.config-item { + display: grid; + grid-template-columns: 180px 1fr; + gap: var(--spacing-lg); + margin-bottom: var(--spacing-lg); + align-items: flex-start; +} + +.config-item:last-child { + margin-bottom: 0; +} + +.config-label { + font-size: var(--font-size-sm); + font-weight: var(--font-weight-semibold); + color: var(--color-text-primary); +} + +.config-value { + background: var(--color-bg-tertiary); + padding: var(--spacing-md); + border-radius: var(--radius-md); + border: 1px solid var(--color-border); + color: var(--color-accent-primary); + font-family: var(--font-family-mono); + font-size: var(--font-size-sm); + word-break: break-all; + line-height: 1.6; +} + +.config-help { + grid-column: 2; + font-size: var(--font-size-xs); + color: var(--color-text-secondary); + margin-top: var(--spacing-xs); + font-style: italic; + line-height: 1.4; +} + +.info-box { + background: var(--color-accent-primary-dim); + border: 1px solid rgba(0, 212, 255, 0.3); + border-left: 3px solid var(--color-accent-primary); + padding: var(--spacing-lg); + border-radius: var(--radius-lg); + margin-bottom: var(--spacing-lg); + color: var(--color-text-secondary); + font-size: var(--font-size-sm); + line-height: 1.6; +} + +.score-formula { + margin-bottom: var(--spacing-xl); +} + +.score-formula code { + background: var(--color-bg-tertiary); + border: 1px solid var(--color-border); + padding: 2px 6px; + border-radius: var(--radius-sm); + font-size: 0.9em; + color: var(--color-accent-primary); + font-family: var(--font-family-mono); +} + +.score-formula table { + width: 100%; + border-collapse: collapse; +} + +.score-formula ul { + margin-left: var(--spacing-lg); + margin-top: var(--spacing-sm); + margin-bottom: var(--spacing-md); +} + +.score-formula li { + margin-bottom: var(--spacing-sm); + color: var(--color-text-secondary); +} + + +/* ============================================================================ + LOG VIEWER + ============================================================================ */ + +.log-container { + background: var(--color-bg-tertiary); + border: 1px solid var(--color-border); + border-radius: var(--radius-lg); + padding: var(--spacing-lg); + font-family: var(--font-family-mono); + font-size: var(--font-size-sm); + max-height: 500px; + overflow-y: auto; + box-shadow: var(--shadow-sm); + line-height: 1.7; +} + +.log-container::-webkit-scrollbar { + width: 6px; +} + +.log-container::-webkit-scrollbar-track { + background: var(--color-bg-tertiary); +} + +.log-container::-webkit-scrollbar-thumb { + background: var(--color-border); + border-radius: var(--radius-sm); +} + +.log-line { + display: flex; + gap: var(--spacing-md); + margin-bottom: var(--spacing-sm); + padding: var(--spacing-sm) 0; + border-bottom: 1px solid var(--color-border-light); +} + +.log-line:last-child { + border-bottom: none; + margin-bottom: 0; +} + +.log-time { + color: var(--color-text-tertiary); + min-width: 100px; + flex-shrink: 0; +} + +.log-level { + display: inline-block; + min-width: 70px; + padding: var(--spacing-xs) var(--spacing-sm); + border-radius: var(--radius-sm); + font-weight: var(--font-weight-semibold); + font-size: var(--font-size-xs); + text-transform: uppercase; + text-align: center; + flex-shrink: 0; +} + +.log-level.info { + background: var(--color-info-dim); + color: var(--color-info); +} + +.log-level.warning { + background: var(--color-warning-dim); + color: var(--color-warning); +} + +.log-level.error { + background: var(--color-error-dim); + color: var(--color-error); +} + +.log-level.debug { + background: var(--color-accent-primary-dim); + color: var(--color-accent-primary); +} + +.log-msg { + color: var(--color-text-secondary); + word-break: break-word; + flex: 1; +} + + +/* ============================================================================ + UTILITY CLASSES + ============================================================================ */ + +.refresh-info { + text-align: right; + margin-top: var(--spacing-lg); + font-size: var(--font-size-xs); + color: var(--color-text-tertiary); +} + +/* ============================================================================ + RESPONSIVE DESIGN + ============================================================================ */ + +/* Tablet: 768px and below */ +@media (max-width: 768px) { + :root { + --size-sidebar: 100%; + } + + .layout { + flex-direction: column; + padding-top: 60px; + } + + .sidebar { + width: 100%; + height: 60px; + flex-direction: row; + border-right: none; + border-bottom: 1px solid var(--color-border); + overflow: hidden; + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 1000; + transition: none; + } + + .sidebar.menu-open { + height: 100vh; + height: 100dvh; /* Use dynamic viewport height for better mobile support */ + max-height: none; + overflow: hidden; /* Changed from auto - we'll scroll the content instead */ + position: fixed; + flex-direction: column; + display: flex; + top: 0; + left: 0; + right: 0; + bottom: 0; + } + + .layout { + padding-top: 60px; + } + + .content { + margin-top: 0; + } + + .sidebar-header { + border-right: none; + border-bottom: 1px solid var(--color-border); + padding: 0 var(--spacing-md); + display: flex; + align-items: center; + min-width: auto; + width: 100%; + height: 60px; + background: var(--color-bg-secondary); + position: relative; + flex-shrink: 0; /* Prevent header from shrinking */ + } + + .sidebar.menu-open .sidebar-header { + border-right: none; + border-bottom: 1px solid var(--color-border); + background: var(--color-bg-secondary); + position: sticky; /* Make header sticky at top */ + top: 0; + z-index: 10; + } + + /* Wrapper for scrollable content on mobile */ + .sidebar-content-wrapper { + display: contents; /* On mobile closed state, act transparent like desktop */ + } + + .sidebar.menu-open .sidebar-content-wrapper { + display: flex; + flex-direction: column; + flex: 1; + overflow-y: auto; + overflow-x: hidden; + -webkit-overflow-scrolling: touch; + min-height: 0; + position: relative; + } + + .sidebar-header h1 { + font-size: var(--font-size-lg); + margin-bottom: 0; + color: var(--color-accent-primary); + padding-right: var(--spacing-2xl); + } + + .sidebar-header .node-name { + display: none; + } + + .sidebar-header .node-pubkey { + display: none; + } + + /* Show menu toggle on mobile */ + .menu-toggle { + display: flex !important; + align-items: center; + justify-content: center; + width: 44px; + height: 44px; + background: none; + border: none; + color: var(--color-text-secondary); + cursor: pointer; + padding: 0; + margin: 0; + flex-shrink: 0; + position: absolute; + right: 8px; + top: 50%; + transform: translateY(-50%); + } + + .menu-toggle svg { + width: 24px; + height: 24px; + } + + .sidebar-nav { + display: none; + padding: 0; + gap: 0; + width: 100%; + margin: 0; + flex-direction: column; + } + + .sidebar.menu-open .sidebar-nav { + display: flex; + flex-direction: column; + flex-shrink: 0; /* Don't let nav shrink, let wrapper handle scroll */ + } + + .nav-section { + border-bottom: 1px solid var(--color-border-light); + padding: var(--spacing-md) var(--spacing-lg) 0; + } + + .nav-section:first-child { + padding-top: var(--spacing-lg); + } + + .nav-section:last-child { + padding-bottom: var(--spacing-lg); + } + + .nav-section-title { + font-size: 0.7rem; + } + + .nav-item { + border-radius: 8px; + margin-bottom: var(--spacing-sm); + font-size: 0.95rem; + padding: 1rem var(--spacing-lg); + } + + /* Hide footer on mobile by default */ + .sidebar-footer { + display: none; + } + + /* Show footer only when menu is open */ + .sidebar.menu-open .sidebar-footer { + display: flex !important; /* Force display as flex when menu is open */ + flex-direction: column; + gap: var(--spacing-md); + margin-top: 0 !important; /* Remove auto margin on mobile */ + flex-shrink: 0; /* Don't shrink the footer */ + padding: var(--spacing-md) var(--spacing-lg); + border-top: 1px solid var(--color-border); + background: var(--color-bg-secondary); + } + + .content { + padding: var(--spacing-lg); + overflow-y: auto; + width: 100%; + } + + h1 { + font-size: var(--font-size-2xl); + } + + h2 { + font-size: var(--font-size-xl); + margin: var(--spacing-xl) 0 var(--spacing-lg) 0; + } + + .stats-grid { + grid-template-columns: 1fr; + gap: var(--spacing-md); + margin-bottom: var(--spacing-xl); + } + + /* Improve stat card readability on tablets */ + .stat-card { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--spacing-md); + padding: var(--spacing-md) var(--spacing-lg); + } + + .stat-label { + font-size: var(--font-size-sm); + margin-bottom: 0; + flex: 1; + text-align: left; + } + + .stat-value { + font-size: var(--font-size-2xl); + margin-bottom: 0; + flex-shrink: 0; + } + + .charts-grid { + grid-template-columns: 1fr; + gap: var(--spacing-md); + margin-bottom: var(--spacing-xl); + } + + .config-item { + grid-template-columns: 1fr; + gap: var(--spacing-sm); + align-items: stretch; + } + + /* Mobile table: card-based layout - structured design */ + .table-container { + overflow-x: visible; + } + + table { + display: block; + width: 100%; + } + + thead { + display: none; + } + + tbody { + display: block; + width: 100%; + } + + + tbody tr { + display: block; + background: var(--color-bg-secondary); + border: 1px solid var(--color-border); + border-radius: var(--radius-lg); + margin-bottom: var(--spacing-lg); + padding: 0; + overflow: hidden; + } + + tbody tr:hover { + background: var(--color-bg-tertiary); + border-color: var(--color-accent-primary); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + } + + /* Card header with time and type */ + td:nth-child(1), /* Time */ + td:nth-child(2) /* Type */ { + display: inline-block; + padding: var(--spacing-md); + margin: 0; + border: none; + background: transparent; + } + + td:nth-child(1) { + font-size: var(--font-size-lg); + font-weight: var(--font-weight-semibold); + color: var(--color-text-primary); + width: auto; + margin-right: var(--spacing-md); + } + + td:nth-child(1)::before { + content: attr(data-label); + font-size: 0.65rem; + color: var(--color-text-tertiary); + text-transform: uppercase; + letter-spacing: 0.5px; + display: block; + margin-bottom: 4px; + font-weight: var(--font-weight-semibold); + } + + td:nth-child(2) { + float: right; + padding: var(--spacing-md); + } + + td:nth-child(2)::before { + content: attr(data-label); + font-size: 0.65rem; + color: var(--color-text-tertiary); + text-transform: uppercase; + letter-spacing: 0.5px; + display: block; + margin-bottom: 4px; + text-align: right; + font-weight: var(--font-weight-semibold); + } + + /* Path/Hashes section - full width with label */ + td:nth-child(5) { + display: block; + width: 100%; + padding: var(--spacing-md); + margin: 0; + border-top: 1px solid var(--color-border); + background: rgba(0, 0, 0, 0.2); + clear: both; + } + + td:nth-child(5)::before { + content: attr(data-label); + font-size: 0.65rem; + color: var(--color-text-tertiary); + text-transform: uppercase; + letter-spacing: 0.5px; + display: block; + margin-bottom: 8px; + font-weight: var(--font-weight-semibold); + } + + /* Route badge on right side */ + td:nth-child(3) { + position: absolute; + top: var(--spacing-md); + right: var(--spacing-md); + margin: 0; + padding: 0; + } + + td:nth-child(3)::before { + display: none; + } + + /* Metrics row - RSSI, SNR, SCORE, TX DELAY */ + td:nth-child(6), /* RSSI */ + td:nth-child(7), /* SNR */ + td:nth-child(8), /* SCORE */ + td:nth-child(9) /* TX DELAY */ { + display: inline-block; + width: calc(50% - 8px); + padding: var(--spacing-sm) var(--spacing-md); + margin: 0; + margin-right: 8px; + border: none; + text-align: left; + } + + td:nth-child(6)::before, + td:nth-child(7)::before, + td:nth-child(8)::before, + td:nth-child(9)::before { + content: attr(data-label); + font-size: 0.6rem; + color: var(--color-text-tertiary); + text-transform: uppercase; + letter-spacing: 0.5px; + display: block; + margin-bottom: 2px; + font-weight: var(--font-weight-semibold); + } + + td:nth-child(6), + td:nth-child(8) { + margin-right: 8px; + } + + td:nth-child(7), + td:nth-child(9) { + margin-right: 0; + } + + /* Status section - full width */ + td:nth-child(10) { + display: block; + width: 100%; + padding: var(--spacing-md); + margin: 0; + border-top: 1px solid var(--color-border); + } + + td:nth-child(10)::before { + content: attr(data-label); + font-size: 0.65rem; + color: var(--color-text-tertiary); + text-transform: uppercase; + letter-spacing: 0.5px; + display: block; + margin-bottom: 6px; + font-weight: var(--font-weight-semibold); + } + + /* Hide LEN - not critical for mobile */ + td:nth-child(4) { + display: none; + } + + /* Better card positioning with relative parent */ + tbody tr { + position: relative; + } + + /* Path hash styling on mobile */ + .path-hash, + .src-dst-hash { + font-size: 0.85rem; + padding: 4px 8px; + line-height: 1.4; + display: inline-block; + margin: 2px; + } + + .path-hash { + font-family: 'Courier New', monospace; + letter-spacing: 0.2px; + } + + .my-hash { + padding: 4px 8px; + font-size: 0.85rem; + display: inline-block; + } + + .path-arrow { + font-size: 1em; + padding: 0 4px; + } + + .stat-value { + font-size: var(--font-size-2xl); + } + + header { + margin-bottom: var(--spacing-xl); + } +} + +/* Tablet: 480px - 768px range */ +@media (max-width: 600px) { + /* Compact stat cards for tablet - horizontal layout */ + .stat-card { + padding: var(--spacing-md) var(--spacing-lg); + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--spacing-md); + } + + .stat-label { + font-size: var(--font-size-sm); + margin-bottom: 0; + flex: 1; + text-align: left; + } + + .stat-value { + font-size: var(--font-size-2xl); + margin-bottom: 0; + flex-shrink: 0; + } + + .stat-unit { + font-size: var(--font-size-sm); + } + + /* Show all details compactly - inline with labels */ + tbody tr { + padding: var(--spacing-md); + } + + td { + display: inline-flex; + align-items: center; + gap: var(--spacing-xs); + margin-bottom: var(--spacing-xs); + margin-right: var(--spacing-md); + font-size: 0.7rem; + } + + td::before { + font-size: 0.55rem; + padding: 1px 4px; + } + + /* Path/Hashes wraps to next line */ + td:nth-child(5) { + display: block; + width: 100%; + margin-right: 0; + margin-bottom: var(--spacing-sm); + } + + /* Optimize path hash display for tablet */ + .path-hash, + .src-dst-hash { + font-size: 0.78rem; + word-break: break-all; + padding: var(--spacing-sm) var(--spacing-md); + } + + .my-hash { + padding: 2px 5px; + font-size: 0.72rem; + } + + /* Status on its own line */ + td:nth-child(10) { + display: block; + width: 100%; + margin-right: 0; + padding-top: var(--spacing-xs); + border-top: 1px solid var(--color-border-light); + } +} + +/* Mobile: 480px and below */ +@media (max-width: 480px) { + .content { + padding: var(--spacing-md); + } + + h1 { + font-size: var(--font-size-xl); + } + + /* Compact stat cards for mobile - horizontal layout */ + .stat-card { + padding: var(--spacing-md); + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--spacing-md); + } + + .stat-label { + font-size: var(--font-size-sm); + margin-bottom: 0; + flex: 1; + text-align: left; + } + + .stat-value { + font-size: var(--font-size-xl); + margin-bottom: 0; + flex-shrink: 0; + } + + .stat-unit { + font-size: var(--font-size-xs); + } + + .chart-card { + padding: var(--spacing-md); + } + + .chart-container { + height: 200px; + } + + .log-container { + max-height: 300px; + font-size: var(--font-size-xs); + } + + .log-line { + flex-direction: column; + gap: var(--spacing-sm); + } + + .log-time { + min-width: auto; + } + + .log-level { + min-width: auto; + width: fit-content; + } + + /* Very compact table with all details */ + tbody tr { + padding: var(--spacing-sm); + } + + td { + display: inline-flex; + align-items: center; + gap: var(--spacing-xs); + margin-bottom: var(--spacing-xs); + margin-right: var(--spacing-sm); + font-size: 0.65rem; + } + + td::before { + font-size: 0.5rem; + padding: 1px 3px; + } + + /* Full width items */ + td:nth-child(5), + td:nth-child(10) { + display: block; + width: 100%; + margin-right: 0; + margin-bottom: var(--spacing-xs); + } + + td:nth-child(10) { + padding-top: var(--spacing-xs); + border-top: 1px solid var(--color-border-light); + } + + td::before { + margin-bottom: 2px; + } + + /* Ultra-compact path hash for tiny screens */ + .path-hash, + .src-dst-hash { + font-size: 0.75rem; + padding: var(--spacing-sm) var(--spacing-md); + line-height: 1.4; + word-break: break-all; + white-space: normal; + } + + .path-hash { + font-family: 'Courier New', monospace; + letter-spacing: 0.1px; + } + + .my-hash { + padding: 2px 4px; + font-size: 0.7rem; + display: inline-block; + } + + .src-dst-hash { + font-size: 0.75rem; + } + + .path-transform { + font-size: 0.65rem; + display: block; + } + + .dupe-badge { + font-size: 0.65rem; + padding: 2px 6px; + } + + .drop-reason { + font-size: 0.7rem; + } + + /* Mobile filter buttons */ + #filterContainer { + flex-direction: column !important; + } + + .filter-btn { + width: 100%; + justify-content: center; + } + + #selectAllBtn, + #clearAllBtn { + width: 100%; + } +} + +/* ============================================================================ + ANIMATION & TRANSITIONS + ============================================================================ */ + +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} + +/* Smooth transitions for interactive elements */ +button, +a, +input, +select, +textarea { + transition: all var(--transition-fast); +} + +/* ============================================================================ + ACCESSIBILITY + ============================================================================ */ + +/* Focus states for keyboard navigation */ +a:focus-visible, +button:focus-visible, +input:focus-visible, +select:focus-visible, +textarea:focus-visible { + outline: 2px solid var(--color-accent-primary); + outline-offset: 2px; +} + +/* High contrast mode support */ +@media (prefers-contrast: more) { + --color-border: #4a5568; + --color-text-secondary: #cbd5e0; + --color-accent-primary: #00e5ff; +} + +/* Dark mode preference (already applied by default) */ +@media (prefers-color-scheme: dark) { + /* Already dark - no changes needed */ +} diff --git a/setup-radio-config.sh b/setup-radio-config.sh new file mode 100644 index 0000000..ba23757 --- /dev/null +++ b/setup-radio-config.sh @@ -0,0 +1,268 @@ +#!/bin/bash +# Radio configuration setup script for pyMC Repeater + +CONFIG_DIR="${1:-.}" +CONFIG_FILE="$CONFIG_DIR/config.yaml" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +HARDWARE_CONFIG="$SCRIPT_DIR/radio-settings.json" + +# Detect OS and set appropriate sed parameters +if [[ "$OSTYPE" == "darwin"* ]]; then + # macOS + SED_OPTS=(-i '') +else + # Linux + SED_OPTS=(-i) +fi + +echo "=== pyMC Repeater Radio Configuration ===" +echo "" + +# Step 0: Repeater Name +echo "=== Step 0: Set Repeater Name ===" +echo "" + +# Read existing repeater name from config if it exists +existing_name="" +if [ -f "$CONFIG_FILE" ]; then + existing_name=$(grep "^\s*node_name:" "$CONFIG_FILE" | sed 's/.*node_name:\s*"\?\([^"]*\)"\?$/\1/' | head -1) +fi + +# Generate random name with format pyRptXXXX (where X is random digit) +if [ -n "$existing_name" ]; then + default_name="$existing_name" + prompt_text="Enter repeater name [$default_name] (press Enter to keep)" +else + random_num=$((RANDOM % 10000)) + default_name=$(printf "pyRpt%04d" $random_num) + prompt_text="Enter repeater name [$default_name]" +fi + +read -p "$prompt_text: " repeater_name +repeater_name=${repeater_name:-$default_name} + +echo "Repeater name: $repeater_name" +echo "" +echo "=== Step 1: Select Hardware ===" +echo "" + +if [ ! -f "$HARDWARE_CONFIG" ]; then + echo "Error: Hardware configuration file not found at $HARDWARE_CONFIG" + exit 1 +fi + +# Parse hardware options from radio-settings.json +hw_index=0 +declare -a hw_keys +declare -a hw_names + +# Extract hardware keys and names using grep and sed +hw_data=$(grep -o '"[^"]*":\s*{' "$HARDWARE_CONFIG" | grep -v hardware | sed 's/"\([^"]*\)".*/\1/' | while read hw_key; do + hw_name=$(grep -A 1 "\"$hw_key\"" "$HARDWARE_CONFIG" | grep "\"name\"" | sed 's/.*"name":\s*"\([^"]*\)".*/\1/') + if [ -n "$hw_name" ]; then + echo "$hw_key|$hw_name" + fi +done) + +while IFS='|' read -r hw_key hw_name; do + if [ -n "$hw_key" ] && [ -n "$hw_name" ]; then + echo " $((hw_index + 1))) $hw_name ($hw_key)" + hw_keys[$hw_index]="$hw_key" + hw_names[$hw_index]="$hw_name" + ((hw_index++)) + fi +done <<< "$hw_data" + +if [ "$hw_index" -eq 0 ]; then + echo "Error: No hardware configurations found" + exit 1 +fi + +echo "" +read -p "Select hardware (1-$hw_index): " hw_selection + +if ! [ "$hw_selection" -ge 1 ] 2>/dev/null || [ "$hw_selection" -gt "$hw_index" ]; then + echo "Error: Invalid selection" + exit 1 +fi + +selected_hw=$((hw_selection - 1)) +hw_key="${hw_keys[$selected_hw]}" +hw_name="${hw_names[$selected_hw]}" + +echo "Selected: $hw_name" +echo "" + +# Step 2: Radio Settings Selection +echo "=== Step 2: Select Radio Settings ===" +echo "" + +# Fetch config from API +echo "Fetching radio settings from API..." +API_RESPONSE=$(curl -s https://api.meshcore.nz/api/v1/config) + +if [ -z "$API_RESPONSE" ]; then + echo "Error: Failed to fetch configuration from API" + exit 1 +fi + +# Parse JSON entries - one per line, extracting each field +SETTINGS=$(echo "$API_RESPONSE" | grep -o '{[^{}]*"title"[^{}]*"coding_rate"[^{}]*}' | sed 's/.*"title":"\([^"]*\)".*/\1/' | while read title; do + entry=$(echo "$API_RESPONSE" | grep -o "{[^{}]*\"title\":\"$title\"[^{}]*\"coding_rate\"[^{}]*}") + desc=$(echo "$entry" | sed 's/.*"description":"\([^"]*\)".*/\1/') + freq=$(echo "$entry" | sed 's/.*"frequency":"\([^"]*\)".*/\1/') + sf=$(echo "$entry" | sed 's/.*"spreading_factor":"\([^"]*\)".*/\1/') + bw=$(echo "$entry" | sed 's/.*"bandwidth":"\([^"]*\)".*/\1/') + cr=$(echo "$entry" | sed 's/.*"coding_rate":"\([^"]*\)".*/\1/') + echo "$title|$desc|$freq|$sf|$bw|$cr" +done) + +if [ -z "$SETTINGS" ]; then + echo "Error: Could not parse radio settings from API response" + exit 1 +fi + +# Display menu +echo "Available Radio Settings:" +echo "" + +index=0 +while IFS='|' read -r title desc freq sf bw cr; do + printf " %2d) %-35s ----> %7.3fMHz / SF%s / BW%s / CR%s\n" $((index + 1)) "$title" "$freq" "$sf" "$bw" "$cr" + + # Store values in files to avoid subshell issues + echo "$title" > /tmp/radio_title_$index + echo "$freq" > /tmp/radio_freq_$index + echo "$sf" > /tmp/radio_sf_$index + echo "$bw" > /tmp/radio_bw_$index + echo "$cr" > /tmp/radio_cr_$index + + ((index++)) +done <<< "$SETTINGS" + +echo "" +read -p "Select a radio setting (1-$index): " selection + +# Validate selection +if ! [ "$selection" -ge 1 ] 2>/dev/null || [ "$selection" -gt "$index" ]; then + echo "Error: Invalid selection" + exit 1 +fi + +selected=$((selection - 1)) +freq=$(cat /tmp/radio_freq_$selected 2>/dev/null) +sf=$(cat /tmp/radio_sf_$selected 2>/dev/null) +bw=$(cat /tmp/radio_bw_$selected 2>/dev/null) +cr=$(cat /tmp/radio_cr_$selected 2>/dev/null) +title=$(cat /tmp/radio_title_$selected 2>/dev/null) + + +# Convert frequency from MHz to Hz (handle decimal values) +freq_hz=$(echo "$freq * 1000000" | bc -l | cut -d. -f1) +bw_hz=$(echo "$bw * 1000" | bc -l | cut -d. -f1) + + +echo "" +echo "Selected: $title" +echo "Frequency: ${freq}MHz, SF: $sf, BW: $bw, CR: $cr" +echo "" + +# Update config.yaml +if [ ! -f "$CONFIG_FILE" ]; then + echo "Error: Config file not found at $CONFIG_FILE" + exit 1 +fi + +echo "Updating configuration..." + +# Repeater name +sed "${SED_OPTS[@]}" "s/^ node_name:.*/ node_name: \"$repeater_name\"/" "$CONFIG_FILE" + +# Radio settings - using converted Hz values +sed "${SED_OPTS[@]}" "s/^ frequency:.*/ frequency: $freq_hz/" "$CONFIG_FILE" +sed "${SED_OPTS[@]}" "s/^ spreading_factor:.*/ spreading_factor: $sf/" "$CONFIG_FILE" +sed "${SED_OPTS[@]}" "s/^ bandwidth:.*/ bandwidth: $bw_hz/" "$CONFIG_FILE" +sed "${SED_OPTS[@]}" "s/^ coding_rate:.*/ coding_rate: $cr/" "$CONFIG_FILE" + +# Extract hardware-specific settings from radio-settings.json +echo "Extracting hardware configuration from $HARDWARE_CONFIG..." + +# Use jq to extract all fields from the selected hardware +hw_config=$(jq ".hardware.\"$hw_key\"" "$HARDWARE_CONFIG" 2>/dev/null) + +if [ -z "$hw_config" ] || [ "$hw_config" == "null" ]; then + echo "Warning: Could not extract hardware config from JSON, using defaults" +else + # Extract each field and update config.yaml + bus_id=$(echo "$hw_config" | jq -r '.bus_id // empty') + cs_id=$(echo "$hw_config" | jq -r '.cs_id // empty') + cs_pin=$(echo "$hw_config" | jq -r '.cs_pin // empty') + reset_pin=$(echo "$hw_config" | jq -r '.reset_pin // empty') + busy_pin=$(echo "$hw_config" | jq -r '.busy_pin // empty') + irq_pin=$(echo "$hw_config" | jq -r '.irq_pin // empty') + txen_pin=$(echo "$hw_config" | jq -r '.txen_pin // empty') + rxen_pin=$(echo "$hw_config" | jq -r '.rxen_pin // empty') + tx_power=$(echo "$hw_config" | jq -r '.tx_power // empty') + preamble_length=$(echo "$hw_config" | jq -r '.preamble_length // empty') + is_waveshare=$(echo "$hw_config" | jq -r '.is_waveshare // empty') + + # Update sx1262 section in config.yaml (2-space indentation) + [ -n "$bus_id" ] && sed "${SED_OPTS[@]}" "s/^ bus_id:.*/ bus_id: $bus_id/" "$CONFIG_FILE" + [ -n "$cs_id" ] && sed "${SED_OPTS[@]}" "s/^ cs_id:.*/ cs_id: $cs_id/" "$CONFIG_FILE" + [ -n "$cs_pin" ] && sed "${SED_OPTS[@]}" "s/^ cs_pin:.*/ cs_pin: $cs_pin/" "$CONFIG_FILE" + [ -n "$reset_pin" ] && sed "${SED_OPTS[@]}" "s/^ reset_pin:.*/ reset_pin: $reset_pin/" "$CONFIG_FILE" + [ -n "$busy_pin" ] && sed "${SED_OPTS[@]}" "s/^ busy_pin:.*/ busy_pin: $busy_pin/" "$CONFIG_FILE" + [ -n "$irq_pin" ] && sed "${SED_OPTS[@]}" "s/^ irq_pin:.*/ irq_pin: $irq_pin/" "$CONFIG_FILE" + [ -n "$txen_pin" ] && sed "${SED_OPTS[@]}" "s/^ txen_pin:.*/ txen_pin: $txen_pin/" "$CONFIG_FILE" + [ -n "$rxen_pin" ] && sed "${SED_OPTS[@]}" "s/^ rxen_pin:.*/ rxen_pin: $rxen_pin/" "$CONFIG_FILE" + [ -n "$tx_power" ] && sed "${SED_OPTS[@]}" "s/^ tx_power:.*/ tx_power: $tx_power/" "$CONFIG_FILE" + [ -n "$preamble_length" ] && sed "${SED_OPTS[@]}" "s/^ preamble_length:.*/ preamble_length: $preamble_length/" "$CONFIG_FILE" + + # Update is_waveshare flag + if [ "$is_waveshare" == "true" ]; then + sed "${SED_OPTS[@]}" "s/^ is_waveshare:.*/ is_waveshare: true/" "$CONFIG_FILE" + else + sed "${SED_OPTS[@]}" "s/^ is_waveshare:.*/ is_waveshare: false/" "$CONFIG_FILE" + fi +fi + +# Cleanup +rm -f /tmp/radio_*_* "$CONFIG_FILE.bak" + +echo "Configuration updated successfully!" +echo "" +echo "Applied Configuration:" +echo " Repeater Name: $repeater_name" +echo " Hardware: $hw_name ($hw_key)" +echo " Frequency: ${freq}MHz (${freq_hz}Hz)" +echo " Spreading Factor: $sf" +echo " Bandwidth: ${bw}kHz (${bw_hz}Hz)" +echo " Coding Rate: $cr" +echo "" +echo "Hardware GPIO Configuration:" +if [ -n "$bus_id" ]; then + echo " Bus ID: $bus_id" + echo " Chip Select: $cs_id (pin $cs_pin)" + echo " Reset Pin: $reset_pin" + echo " Busy Pin: $busy_pin" + echo " IRQ Pin: $irq_pin" + [ "$txen_pin" != "-1" ] && echo " TX Enable Pin: $txen_pin" + [ "$rxen_pin" != "-1" ] && echo " RX Enable Pin: $rxen_pin" + echo " TX Power: $tx_power dBm" + echo " Preamble Length: $preamble_length" + [ -n "$is_waveshare" ] && echo " Waveshare: $is_waveshare" +fi + +# Enable and start the service +SERVICE_NAME="pymc-repeater" +if systemctl list-unit-files | grep -q "^$SERVICE_NAME\.service"; then + echo "" + echo "Enabling and starting the $SERVICE_NAME service..." + sudo systemctl enable "$SERVICE_NAME" + sudo systemctl start "$SERVICE_NAME" +else + echo "" + echo "Service $SERVICE_NAME not found, skipping service management" +fi + +echo "Setup complete. Please check the service status with 'systemctl status $SERVICE_NAME'." diff --git a/uninstall.sh b/uninstall.sh new file mode 100644 index 0000000..b7c33bd --- /dev/null +++ b/uninstall.sh @@ -0,0 +1,99 @@ +#!/bin/bash +# Uninstall script for pyMC Repeater + +set -e + +INSTALL_DIR="/opt/pymc_repeater" +CONFIG_DIR="/etc/pymc_repeater" +LOG_DIR="/var/log/pymc_repeater" +SERVICE_USER="repeater" +SERVICE_FILE="/etc/systemd/system/pymc-repeater.service" + +echo "=== pyMC Repeater Uninstall ===" + +# Check if running as root +if [ "$EUID" -ne 0 ]; then + echo "Error: This script must be run as root" + exit 1 +fi + +# Stop and disable service +if systemctl is-active --quiet pymc-repeater; then + echo "Stopping service..." + systemctl stop pymc-repeater +fi + +if systemctl is-enabled --quiet pymc-repeater 2>/dev/null; then + echo "Disabling service..." + systemctl disable pymc-repeater +fi + +# Remove systemd service file +if [ -f "$SERVICE_FILE" ]; then + echo "Removing systemd service..." + rm -f "$SERVICE_FILE" + systemctl daemon-reload +fi + +# Uninstall Python package +if [ -d "$INSTALL_DIR" ]; then + echo "Uninstalling Python package..." + cd "$INSTALL_DIR" + pip uninstall -y pymc_repeater 2>/dev/null || true +fi + +# Remove installation directory +if [ -d "$INSTALL_DIR" ]; then + echo "Removing installation directory..." + rm -rf "$INSTALL_DIR" +fi + +# Ask before removing config and logs +echo "" +read -p "Remove configuration files in $CONFIG_DIR? [y/N] " -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "Removing configuration directory..." + rm -rf "$CONFIG_DIR" +else + echo "Keeping configuration files in $CONFIG_DIR" +fi + +echo "" +read -p "Remove log files in $LOG_DIR? [y/N] " -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "Removing log directory..." + rm -rf "$LOG_DIR" +else + echo "Keeping log files in $LOG_DIR" +fi + +echo "" +read -p "Remove user data in /var/lib/pymc_repeater? [y/N] " -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]]; then + echo "Removing user data directory..." + rm -rf /var/lib/pymc_repeater +else + echo "Keeping user data in /var/lib/pymc_repeater" +fi + +# Ask before removing service user +echo "" +read -p "Remove service user '$SERVICE_USER'? [y/N] " -n 1 -r +echo +if [[ $REPLY =~ ^[Yy]$ ]]; then + if id "$SERVICE_USER" &>/dev/null; then + echo "Removing service user..." + userdel "$SERVICE_USER" 2>/dev/null || true + fi +else + echo "Keeping service user '$SERVICE_USER'" +fi + +echo "" +echo "=== Uninstall Complete ===" +echo "" +echo "The pyMC Repeater has been removed from your system." +echo "----------------------------------"