From ed5d39619f0bb4ca0951bf1cbc6b2c6b6498c39b Mon Sep 17 00:00:00 2001 From: ewowi Date: Fri, 26 Jun 2026 14:04:34 +0200 Subject: [PATCH 1/8] =?UTF-8?q?Composable=20modifiers:=20invert=20map=20bu?= =?UTF-8?q?ild=20to=20physical=E2=86=92logical=20fold=20chain?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Multiple modifiers per Layer now compose as a chain (Region → Multiply → Rotate), each a coordinate fold applied in child order. The Layer's mapping build is inverted from virtual→physical fan-out to physical→logical fold (MoonLight's proven model, written fresh), which makes chaining fall out, deletes the fan-out overflow risk, and leaves the render hot path untouched. RegionModifier gains off-screen windows so an effect can slide partly or fully out of view without rescaling. Also folds in the white-theme preview-background fix. S3 live: tick:3482us(FPS:287). PC: tick:120/87/118/9/2/323/36/15/18/56/115/11/19us(FPS:8333/11494/8474/111111/500000/3095/27777/66666/55555/17857/8695/90909/52631). Classic ESP32 tick from a stale monitor log — no classic board attached this session (only the S3 was connected); esp32 + esp32s3-n16r8 firmware both build clean. Light domain: - ModifierBase: new fold interface (modifyLogicalSize / bool modifyLogical / modifyLive + hasModifyLive + consumeNeedsRebuild), replacing the single-modifier logicalDimensions/mapToPhysical/maxMultiplier - Layer: rebuildLUT inverted to a physical→logical counting-sort CSR build folding the whole enabled-modifier chain; each modifier folds in its OWN stage's box (stashed, MoonLight-style, no per-stage array); per-frame live pass for dynamic modifiers, gated on hasLive_ (a static-only chain pays nothing); coalesced one-rebuild-per-frame; deleted buildBoxToDriver/buildSparseIdentityLUT/fan-out-ceiling - MultiplyModifier/CheckerboardModifier/RandomMapModifier: rewritten as folds - RegionModifier: fold + reject; off-screen windows (start/end unclamped, full-window logical size — move without rescale, fully-off renders dark) - RotateModifier: dynamic modifyLive per-frame remap via an explicit integer 2×2 rotation matrix (the codebase's transform-matrix reference); no more step-gated rebuild - light_types.h: add Coord3D (per-axis operators) UI: - preview3d/app.js: read --bg-0 from (the theme override) not , so the preview canvas matches the light theme; redraw on theme toggle Tests: - unit_Coord3D; unit_Layer_modifier_chain (compose, A∘B≠B∘A, disabled-skip, per-stage regression); unit_Layer_live_modifier (live remap, pay-for-what-you-use gate, disabled gate, two-dynamic coalescing); off-screen Region cases; all 5 modifier tests rewritten to the fold interface - scenario_runner: register Region/Rotate/RandomMap (the chain scenario was silently skipping them); scenario_modifier_chain exercises Region+Multiply+Checkerboard+live Rotate Docs: - ModifierBase.md (fold contract + prior art); architecture.md (chain build, drop stale "multi-layer composition" WIP example — it shipped); modifier specs to the fold model; RegionModifier.md off-screen windows; decisions.md inversion lesson; backlog: delete shipped composed-modifiers item, update chain-viz premise; Plan-20260626 saved Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/architecture.md | 6 +- docs/backlog/README.md | 2 +- docs/backlog/backlog-core.md | 2 +- docs/backlog/backlog-mixed.md | 19 +- docs/history/decisions.md | 11 + ...sable modifiers (chain the whole stack).md | 125 ++++++ docs/moonmodules/light/ModifierBase.md | 31 ++ .../light/modifiers/CheckerboardModifier.md | 8 +- .../light/modifiers/MultiplyModifier.md | 4 +- .../light/modifiers/RegionModifier.md | 26 +- .../light/modifiers/RotateModifier.md | 8 +- src/light/layers/Layer.h | 414 ++++++++++-------- src/light/light_types.h | 26 ++ src/light/modifiers/CheckerboardModifier.h | 45 +- src/light/modifiers/ModifierBase.h | 90 ++-- src/light/modifiers/MultiplyModifier.h | 109 ++--- src/light/modifiers/RandomMapModifier.h | 117 +++-- src/light/modifiers/RegionModifier.h | 119 ++--- src/light/modifiers/RotateModifier.h | 132 +++--- src/ui/app.js | 4 + src/ui/preview3d.js | 11 +- test/CMakeLists.txt | 3 + test/scenario_runner.cpp | 6 + .../light/scenario_modifier_chain.json | 188 ++++++++ test/unit/light/unit_CheckerboardModifier.cpp | 89 ++-- test/unit/light/unit_Coord3D.cpp | 40 ++ test/unit/light/unit_Layer_live_modifier.cpp | 164 +++++++ test/unit/light/unit_Layer_modifier_chain.cpp | 128 ++++++ test/unit/light/unit_MultiplyModifier.cpp | 196 +++------ test/unit/light/unit_RandomMapModifier.cpp | 104 ++--- test/unit/light/unit_RegionModifier.cpp | 178 ++++---- test/unit/light/unit_RotateModifier.cpp | 82 ++-- 32 files changed, 1532 insertions(+), 955 deletions(-) create mode 100644 docs/history/plans/Plan-20260626 - Composable modifiers (chain the whole stack).md create mode 100644 docs/moonmodules/light/ModifierBase.md create mode 100644 test/scenarios/light/scenario_modifier_chain.json create mode 100644 test/unit/light/unit_Coord3D.cpp create mode 100644 test/unit/light/unit_Layer_live_modifier.cpp create mode 100644 test/unit/light/unit_Layer_modifier_chain.cpp diff --git a/docs/architecture.md b/docs/architecture.md index 87d63a0..0cf3407 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -353,9 +353,9 @@ A **Layer** (a MoonModule, child of Layers) owns: A layer can have **multiple effects**. Each effect writes to the buffer sequentially in its listed order, overwriting or adding to the previous — so the effects stack (a base-colour effect followed by a sparkle effect). -A layer applies its **first enabled modifier** during LUT build (`Layer::rebuildLUT`). Modifiers are **reorderable** in the UI, and order is meaningful (a multiply-then-checkerboard mask differs from checkerboard-then-multiply, just as mirror-then-rotate differs from rotate-then-mirror). Applying several modifiers in sequence (chaining) is on the [backlog](backlog/README.md). +A layer applies **all its enabled modifiers as a chain** during the mapping build (`Layer::rebuildLUT`): each modifier is a coordinate fold, and they compose in child order (M₁∘M₂∘…). Modifiers are **reorderable** in the UI, and order is meaningful (a multiply-then-checkerboard mask differs from checkerboard-then-multiply, just as mirror-then-rotate differs from rotate-then-mirror). The fold contract (the three hooks, the physical→logical build, the live pass) is documented in [ModifierBase](moonmodules/light/ModifierBase.md). -Each layer references the shared Layouts. The layer builds its own LUT by iterating the Layouts container's coordinates and applying its static modifiers in order. Different layers in Layers can have different modifiers, producing different LUTs from the same Layouts. +Each layer references the shared Layouts. The layer builds its mapping by walking the Layouts container's **physical** coordinates and folding each through the static modifier chain to its logical cell — N physical lights folding onto one logical cell is the fan-out (a Multiply kaleidoscope), so the build never produces a fan-out overflow. Different layers in Layers can have different modifiers, producing different mappings from the same Layouts. ## Effects @@ -522,7 +522,7 @@ The light domain plugs into the UI at three points: a fixed top-level tree (Layo ## What we leave undesigned -Genuinely open questions, *not* the same as a 🚧 marker. A 🚧 item is a committed design that simply isn't coded yet (multi-layer composition, two-core handover, time sync); the items here are ones where the *design itself* isn't settled, deferred until a concrete need forces the decision: +Genuinely open questions, *not* the same as a 🚧 marker. A 🚧 item is a committed design that simply isn't coded yet (two-core handover, clock sync, device-to-device light distribution); the items here are ones where the *design itself* isn't settled, deferred until a concrete need forces the decision: - **WiFi runtime disable**: today the eth-only build profile compiles WiFi out. Whether runtime gating should key off detected hardware presence, an explicit control, or a deviceModel-catalog field isn't decided; the eth-only build covers the need until one is. - **Mixing light types in one Layouts**: each layout child describes one light type (all LED strips, or all par lights). Whether a single Layouts container should hold mixed types (LED strips + par lights together), and how the channel layout would reconcile across them, isn't designed; one Layouts per light type is the current model. diff --git a/docs/backlog/README.md b/docs/backlog/README.md index 825f90d..11d7216 100644 --- a/docs/backlog/README.md +++ b/docs/backlog/README.md @@ -38,7 +38,7 @@ A map of everything in the three files, by theme. ### Mixed ([backlog-mixed.md](backlog-mixed.md)) -- MultiplyModifier mapping-LUT memory at large grids; composed modifiers (chain the whole stack, not just the first); intermittent ~0.5 s RMT LED pauses; NoiseEffect simplex cost on ESP32. +- MultiplyModifier mapping-LUT memory at large grids; intermittent ~0.5 s RMT LED pauses; NoiseEffect simplex cost on ESP32. ## In-flight draft specs diff --git a/docs/backlog/backlog-core.md b/docs/backlog/backlog-core.md index 4473c32..2d1f34b 100644 --- a/docs/backlog/backlog-core.md +++ b/docs/backlog/backlog-core.md @@ -336,7 +336,7 @@ Forward-looking companion to the shipped UI spec, [moonmodules/core/ui.md](../mo These don't block the shipped baseline but should be answered before 1.0: - **Multi-layer UI** — [architecture.md](../architecture.md) plans for N layers blended into one Drivers. The current card layout shows one Layer. Likely needs a tab/accordion to switch layers, or a per-layer column. -- **Modifier chain visualization** — show the modifier order visually. Today they're a flat list, and only the **first enabled** modifier actually applies (the `children[]` order is *not* yet an apply order — see [Composed modifiers](backlog-mixed.md#composed-modifiers--chain-the-whole-modifier-stack-not-just-the-first-planned-multi-commit)). This viz item only becomes meaningful *after* composed modifiers land; until then a chain UI would imply a stacking the engine doesn't do. +- **Modifier chain visualization** — show the modifier order visually. They're a flat list today, but the `children[]` order **is** the apply order now (modifiers compose as a chain, M₁∘M₂∘…), so a visual that conveys the stacking (and that order matters) would help users reason about a multi-modifier layer. - **Presets** — save/load named bundles of control values. Persistence already stores them; needs a UI surface. - **Canvas/node-graph view** — v2 attempted this. Powerful for complex setups but doubles the UI surface. A reasonable v3 follow-up gated on user demand. diff --git a/docs/backlog/backlog-mixed.md b/docs/backlog/backlog-mixed.md index a9e5b4d..cd1ec5e 100644 --- a/docs/backlog/backlog-mixed.md +++ b/docs/backlog/backlog-mixed.md @@ -6,27 +6,10 @@ Forward-looking items whose work genuinely spans **both** the core and light dom ### MultiplyModifier mapping-LUT memory at large grids (investigation, re-verify on classic) -`scenario_perf_full` on the S3 (2026-06-17) measured the MultiplyModifier's cost across grid sizes. The finding, stated correctly: the modifier **reduces compute** (with the default 2×2 kaleidoscope the effect renders only the ¼-size logical quadrant — Noise+Multiply at 16K is 29,647µs vs 50,555µs for Noise alone), and its real cost is **memory** — the 1:N fan-out mapping LUT. Measured modifier heap cost on the S3: 16²→1.7KB, 32²→10.8KB, 64²→23.5KB, **128²(16K)→93KB** (the LUT destinations array; `nrOfLightsType` is `uint32_t` on a PSRAM board). On the S3's 8MB PSRAM this is trivial. [Composed modifiers](#composed-modifiers--chain-the-whole-modifier-stack-not-just-the-first-planned-multi-commit) would multiply this memory cost by the chain depth — size it there. +`scenario_perf_full` on the S3 (2026-06-17) measured the MultiplyModifier's cost across grid sizes. The finding, stated correctly: the modifier **reduces compute** (with the default 2×2 kaleidoscope the effect renders only the ¼-size logical quadrant — Noise+Multiply at 16K is 29,647µs vs 50,555µs for Noise alone), and its real cost is **memory** — the 1:N fan-out mapping LUT. Measured modifier heap cost on the S3: 16²→1.7KB, 32²→10.8KB, 64²→23.5KB, **128²(16K)→93KB** (the LUT destinations array; `nrOfLightsType` is `uint32_t` on a PSRAM board). On the S3's 8MB PSRAM this is trivial. (Composed modifiers shipped via the physical→logical fold build, which has no build-time fan-out — each physical light contributes ≤1 destination, so the destinations array is bounded by the real light count regardless of chain depth, removing the old fan-out memory concern.) **This is NOT a no-PSRAM blocker** — 16K Noise + Multiply has run on a classic ESP32 (no PSRAM, 320KB internal) before at **10–20 FPS** (WiFi vs Ethernet), sending frames out over **ArtNet to a display, not physical LED drivers**. It works there because classic's `nrOfLightsType` is `uint16_t` (half the LUT size) and the modifier shrinks the logical render grid. So the action is **re-verify the working classic setup when a classic board is connected** (find the config — grid, mirror, ArtNet target — that reproduces the historical 10–20 FPS), not "fix an impossibility." Worth investigating only if that re-verification shows the LUT memory has regressed since: the destinations array is the obvious lever (it stores a `nrOfLightsType` per physical destination; a 2× kaleidoscope is 1:1 in *count* so the LUT need not store fan-out > the physical count — confirm it isn't over-allocating to `maxMultiplier()` when the effective fan-out is 1). Capture the classic numbers into performance.md's multi-board table first. -### Composed modifiers — chain the whole modifier stack, not just the first (planned, multi-commit) - -**Confirmed scope, not an open question:** multiple modifiers per Layer applied as a stack was always the plan, and it ships in **MoonLight** (Mirror, Rotate, Transpose, Kaleidoscope, … all composable on one layer — see [moonlight-inventory.md](../history/moonlight-inventory.md)). projectMM's single-modifier behaviour is the not-yet-finished state, not a design choice. - -Today a Layer applies **only the first enabled modifier**. `Layer::rebuildLUT()` finds the first enabled `Modifier` child and `break`s ([Layer.h](../../src/light/layers/Layer.h) `rebuildLUT`), and `Layer::loop()` ticks only that one (with an explicit comment that ticking a later one would desync the LUT, since a dynamic modifier's `loop()` can drive a rebuild the LUT must reflect). So with two modifiers on a Layer the second is dead weight — dragging it above the first is the only way to make it the active one. The intended behaviour is **modifier order = apply order**: a stack where each modifier reshapes the result of the one below ("modifiers on modifiers"), e.g. Multiply (kaleidoscope) *then* Rotate the kaleidoscoped result. The [modifier-chain-viz UI item](backlog-core.md#open-design-questions) is the surface for it and only becomes meaningful once this lands. - -**Mechanism — follow MoonLight's proven model, our own code** ([*Industry standards, our own code*](../../CLAUDE.md#principles)). MoonLight composes by streaming the layout's coordinates through each modifier's `modifyLayout`/`modifyLight` in order while the mapping table is built, so the *final* table already encodes the whole chain — the per-frame hot path stays a single lookup. We do the same with our pieces: `rebuildLUT()` walks the layout's coordinate stream (`Layouts::forEachCoord`) and passes each coordinate through modifier 1, then 2, …, then *n* before recording the destination, so the built `MappingLUT` is the composition `M₁ ∘ M₂ ∘ … ∘ Mₙ` collapsed to one `logical→driver` table. Composition is a **cold-path, build-time** concern; modifiers stay simple (each still answers `logicalDimensions()` + its own per-coordinate transform), so the complexity lives in the core per *[Complexity lives in core](../../CLAUDE.md#principles)*. Worth studying MoonLight's `PhysMap` 1:0/1:1/1:N packing (inventory §1) when sizing the table — a deep chain with fan-out is exactly where the per-entry byte cost matters. - -Why it's not a one-liner: - -- **Build path** — `rebuildLUT()` must iterate *all* enabled modifiers bottom-up, threading each stage's logical dimensions into the next, and fold the per-stage transforms into one final LUT. The single-modifier `maxDest` / fan-out ceiling math (the `maxMultiplier()` clamp that fixed the multiplyZ overflow) has to generalise to a **product** of multipliers across the chain — the dominant new correctness risk (and the memory blow-up noted in the MultiplyModifier-LUT item above: a 2-deep 2× chain is up to 4× the destinations). -- **Tick path** — a dynamic modifier (RandomMapModifier, RotateModifier) calls back into `Layer::onBuildState()` on its timer to rebuild the LUT. With a chain, *any* dynamic stage rebuilding must recompose the *whole* chain, and `loop()` must tick every enabled modifier (not `break` after the first) in the right order, after the effect pass. -- **Degrade path** — the per-stage OOM degrade (`degradeIdentity`) must decide what "degrade" means mid-chain (drop the offending stage? collapse to identity?) without leaving a stale partial LUT. -- **Tests** — `unit_Layers_container` / the modifier unit tests pin single-modifier behaviour; composed-order needs new cases (A∘B ≠ B∘A, a disabled middle stage is skipped not collapsed, the fan-out product ceiling holds at no-PSRAM `uint16_t`), plus a scenario that reorders a 2-modifier stack and asserts the composite changes. - -**Estimate: medium — roughly 4–6 commits.** (1) design note pinning the coordinate-stream composition model + the fan-out-product ceiling rule (reference the MoonLight inventory); (2) `MappingLUT` compose/fold primitive + unit tests in isolation; (3) `rebuildLUT()` chain iteration + `loop()` tick-all-in-order, behind the existing single-modifier tests staying green; (4) degrade-path decision + tests; (5) reorder scenario + `performance.md` memory capture at depth 2–3; (6) UI follow-up (the modifier-chain-viz item — see the correction noted there). Gate the depth: most setups are 1 modifier, so the chain path must cost nothing when `n == 1` (the current fast path stays the `n == 1` branch). - ### Intermittent ~0.5 s LED pauses with the RMT driver (pending investigation) Observed on the bench (2026-06): LED output running on the RMT driver occasionally freezes for about half a second. Postponed by the product owner until more observations exist. Ranked suspects from the initial analysis, each with a cheap experiment: diff --git a/docs/history/decisions.md b/docs/history/decisions.md index 7b90324..953f5b8 100644 --- a/docs/history/decisions.md +++ b/docs/history/decisions.md @@ -709,3 +709,14 @@ The installer was reworked so a board catalog ([`boards.json`](../install/boards One sub-decision the implementation forced: the boundary rounding. The original spec said inclusive-ceil ("start 33/end 66 on a 4-wide axis → pixels 1..3"), which on a 128-wide axis makes `end=50` land on pixel 64 *inclusive* — so two abutting layers (0..50, 50..100) **overlap by one pixel** at the seam. The product owner chose **half-open `[start, end)`** instead: `end=50` → pixels 0..63, and 0..50 + 50..100 tile a 128 axis into 0..63 / 64..127 exactly, no overlap, no gap (with a min-1-pixel floor so tiny panels still get a non-zero region). Lesson: when a region/range feature will be used to *tile* a space, half-open intervals are the textbook choice (same reason `[begin, end)` is the C++ iterator convention) — inclusive bounds double-count the seam. Lessons: (1) a persisted-but-inert control is a feature with no home yet — before wiring it where it sits, ask whether an existing mechanism already expresses it (the modifier interface did, completely). (2) "make it the fastest at the default" is often best met by making the default the *absence* of the feature, not a fast branch inside it. (3) a feature framed as "a Layer property" may really be "a composable transform" — the modifier framing also unlocked stacking for free. (4) reach for half-open intervals whenever regions abut. + +## Composable modifiers — invert the map build (physical→logical), don't bolt fan-out onto the old interface + +Modifiers needed to chain (Region then Multiply then Rotate), but the old interface — `mapToPhysical(logicalCoord) → [physical indices]`, a virtual→physical **fan-out** — didn't compose: stages emitted flat indices, not coordinates, and chaining would need a product-of-`maxMultiplier` fan-out ceiling (the exact overflow class that caused the multiplyZ black-screen). The fix was to **invert the build to physical→logical**, adopting MoonLight's proven model (the product owner's prior engine) in projectMM's own code: each modifier becomes an in-place coordinate fold, and the Layer walks the *physical* lights, folding each through the enabled chain to its logical cell. Three hooks — `modifyLogicalSize` (fold the box), `modifyLogical` (fold a coord, return false to reject), `modifyLive` (per-frame remap for Rotate). + +Why the inversion was the right call, not just the chaining bolt-on: +- **Fan-out becomes free.** N physical lights folding onto one logical cell *is* the fan-out — no fan-out list, no product ceiling, no overflow. `destinationCount ≤ driverCount` is now a hard invariant. The whole `maxMultiplier`/scratch-buffer/`buildBoxToDriver`/`buildSparseIdentityLUT` machinery deleted. +- **The hot path is untouched.** Our `MappingLUT` is a CSR keyed by logical index; the inverted build is a scatter onto arbitrary logical keys, which doesn't fit `setMapping`'s in-order contract — so the build is a textbook **counting-sort CSR construction** (count, prefix-sum, scatter, replay) entirely on the cold path. The per-frame `forEachDestination` read is byte-identical. +- **Static vs dynamic split correctly.** Mask/tile/crop fold forward at build time (`modifyLogical`); rotation gathers backward per frame (`modifyLive`) — each in its natural direction, so rotation keeps its clean inverse-sample (no gaps), and a static-only chain pays *nothing* per frame (the live pass is gated on `hasModifyLive()`). + +Lessons: (1) when a feature "doesn't compose," check whether the *interface direction* is wrong before adding machinery to force it — inverting the build deleted more code than it added. (2) A proven external model (MoonLight) is worth adopting wholesale when it's the textbook approach (backward mapping + LUT bake), but write it fresh against your own structures (our CSR, our names) rather than porting. (3) Matrices compose the *affine* subset cleanly (Rotate is written as an explicit 2×2 matrix, the codebase's matrix reference) but can't express masks/tiles — so the coordinate fold is the general composition model, with a matrix-backed modifier as a special case the same interface hosts. diff --git a/docs/history/plans/Plan-20260626 - Composable modifiers (chain the whole stack).md b/docs/history/plans/Plan-20260626 - Composable modifiers (chain the whole stack).md new file mode 100644 index 0000000..c5b3ab4 --- /dev/null +++ b/docs/history/plans/Plan-20260626 - Composable modifiers (chain the whole stack).md @@ -0,0 +1,125 @@ +# Plan — Composable modifiers (chain the whole modifier stack) + +## Context + +Today a Layer applies **only its first enabled modifier**: `Layer::rebuildLUT()` finds the first enabled `Modifier` child and `break`s, and `Layer::loop()` ticks only that one. A second modifier on a Layer is dead weight. The product owner has always intended **modifier order = apply order** — a stack where each modifier reshapes the result of the one below (Region *then* Multiply-mirror *then* Rotate), the way it works in MoonLight (the product owner's prior engine, 3 years proven). + +The current `ModifierBase` interface — `logicalDimensions()` + `mapToPhysical(coord) → [flat physical indices]` — is a **virtual→physical fan-out** model that does not compose: each stage emits flat indices, not coordinates, so stage N+1 can't consume stage N's output, and chaining would need a product-of-`maxMultiplier` fan-out ceiling (the exact 64-bit-overflow bug class that caused the multiplyZ black-screen). + +**The fix is to adopt MoonLight's proven model, written fresh in projectMM style:** invert the map build to **physical→logical**, where each modifier is an **in-place coordinate fold**. Composition becomes a plain loop over enabled modifiers mutating one coordinate. Fan-out stops being a build-time concern (N physical lights folding onto one logical cell *is* the fan-out). The product-ceiling math, the per-light scratch buffer, `buildBoxToDriver`, `buildSparseIdentityLUT`, and `isNaturalOrder`'s shuffle role all disappear. + +Three tiers of hook (MoonLight's structure, projectMM names): +- `modifyLogicalSize(Coord3D& size)` — static, build-time, run once in child order; folds the logical box (Multiply shrinks, Region crops, Mirror halves). +- `bool modifyLogical(Coord3D& pos, …)` — static, build-time, run per physical light in child order; folds a physical coord into logical space, returns `false` to reject (mask/out-of-region). +- `modifyLive(Coord3D& pos, …)` — **dynamic, per-frame**; the per-frame coordinate transform for smooth rotation/scroll, applied without a LUT rebuild. + +**Pay-for-what-you-use is the load-bearing guarantee (product owner's explicit requirement):** a modifier with no `modifyLive` override imposes **zero per-frame cost** — the hot path runs at exactly today's speed. Per-frame cost exists only when a dynamic modifier is actually present, and that cost is inherent to dynamic motion (moving pixels every frame). MoonLight proves it's viable. + +## Decisions locked with the product owner + +- **Full three-tier model** (static size + static fold + dynamic per-frame), not a static-only first cut. +- **Reject signal = `bool modifyLogical()` return** (not a sentinel coord — avoids a later modifier's `% size` aliasing a sentinel back into range). +- **No `modifyXYZ` override ⇒ max hot-path speed.** The dynamic pass is gated behind a build-time "any live modifier?" flag; absent it, the render path is byte-identical to today. +- **Box may grow** (MoonLight's Rotate `expand` grows it). Do NOT hard-forbid growth; size the LUT from the *post-fold* logical box and clamp/guard defensively (robust-to-any-input), rather than asserting shrink-only. + +## Architecture mapping (MoonLight idea → projectMM) + +| MoonLight | projectMM | +|---|---| +| `Coord3D` | new `Coord3D` in `light_types.h`, our naming + rationale comment | +| "virtual" space | **logical** box (our existing word) | +| `modifySize` | `modifyLogicalSize(Coord3D& size)` | +| `modifyPosition` (mutate, reject via UINT16_MAX) | `bool modifyLogical(Coord3D& pos, const Coord3D& phys, const Coord3D& logical)` (mutate, reject via `false`) | +| `modifyXYZ` (per-frame at `setRGB(pos)`) | `modifyLive(Coord3D& pos, const Coord3D& logical)` — per-frame coordinate remap; seam described below | +| `PhysMap` table | existing `MappingLUT` CSR (**unchanged** — see build note) | +| `addLight(physicalPos)` gather | the physical→logical counting-sort build in `rebuildLUT()` | + +## Key build-mechanics finding (verified) + +The hot-path **read** (`BlendMap::blendMap` → `for li in logicalCount: forEachDestination(li) → dst[physIdx]=src[li]`) stays **byte-identical**. Only the cold-path **build** inverts. + +But `MappingLUT::setMapping` requires **sequential, in-order, one-shot** writes (`offsets_[logicalIdx] = destinationCount_`, monotonic append). A physical→logical loop scatters onto *arbitrary, repeated* logical indices — a scatter, not an in-order gather. So `rebuildLUT()` builds the CSR with a **counting sort** (the textbook way to build CSR from scattered keys), entirely in `Layer` on the cold path, then replays it through `setMapping` in logical order. **No `MappingLUT` structural change.** + +``` +Pass A (count): forEachCoord → fold each driver light to logIdx (or reject) → counts[logIdx]++ +Prefix-sum: counts → offsets +Pass B (scatter):forEachCoord (same fold) → scratchDests[cursor[logIdx]++] = driverIdx +Replay: for logIdx 0..N-1: setMapping(logIdx, &scratchDests[offsets[logIdx]], counts[logIdx]); finalize() +``` + +`maxDest` passed to `MappingLUT::build` is now exactly `driverCount` (each physical light folds to ≤1 logical cell → contributes ≤1 destination total). The product-ceiling/overflow math is **deleted** — `destinationCount_ ≤ driverCount` is a hard invariant. `overwrites_` stays `true` for the fold build (each physical cell appears in exactly one logical entry's run; no within-layer additive accumulation can arise), simplifying `BlendMap`'s `overwrites()` handling for this path. + +Two `forEachCoord` passes + a `logicalCount`-sized counts array is the build cost — cold path, bounded, comparable to today's `boxToDriver` + `physicals` scratch. + +## The dynamic (per-frame) seam — projectMM placement + +MoonLight applies `modifyLive` per pixel at write time because effects call `setRGB(pos)`. projectMM effects write a **flat logical buffer**, then `blendMap` scatters. So our seam is a **per-frame logical→logical remap** applied between the effect write and the scatter, only when a live modifier exists: + +- At build time, compute `hasLive_` = any enabled modifier overrides `modifyLive`. Store on the Layer. +- `Layer::loop()`: after effects fill `buffer_` and static modifiers are already baked into `lut_`, if `hasLive_`, run one pass that, for each logical cell, folds its coordinate through the enabled `modifyLive` chain to a *source* logical coordinate and gathers (a coordinate remap over the logical buffer into a scratch buffer, then swap). If `!hasLive_`, skip entirely — **the buffer goes straight to the scatter, zero added cost** (the guarantee). +- Rotation is naturally an **inverse-sample gather** here (for each destination logical cell, sample its rotated source) — which is exactly how our current `RotateModifier` already reasons (`// map a destination light to its rotated SOURCE`), so no visual regression. The live pass is the right home for that inverse-sample logic that did NOT fit the forward static fold. + +This resolves the Plan-agent's concern: dynamic modifiers do **not** force the forward-fold visual change, because they live in the per-frame gather seam, not the static build. Static modifiers fold forward (build); dynamic modifiers gather inverse (per-frame). Each in its natural direction. + +## Files + +### New / core types +- **`src/light/light_types.h`** — add `struct Coord3D { lengthType x,y,z; }` with `+ - % / ==` operators, a one-line rationale comment matching that file's house style. Used by the fold hooks. + +### `src/light/modifiers/ModifierBase.h` — interface inversion +- Replace `logicalDimensions()` + `mapToPhysical()` + `maxMultiplier()` with: + - `virtual void modifyLogicalSize(Coord3D& size) const {}` (default: no resize) + - `virtual bool modifyLogical(Coord3D& pos, const Coord3D& phys, const Coord3D& logical) const { return true; }` (default: pass-through) + - `virtual void modifyLive(Coord3D& pos, const Coord3D& logical) const {}` (default: no per-frame work; presence detected so absence = zero cost) +- Keep `dimensions()` (the 📏/🟦/🧊 chip) and `controlChangeTriggersBuildState` (already `true`). +- `phys`/`logical` passed in (not stashed on the modifier) so modifiers stay stateless and the two-pass build can't desync a cached size. + +### `src/light/layers/Layer.h` — the heart +- `rebuildLUT()`: scan **all** enabled modifiers (not first-only). Run `modifyLogicalSize` chain → `width_/height_/depth_`. Then the counting-sort fold build above. Keep the dense-natural `setIdentity` memcpy shortcut (cheap `isNaturalOrder`-style check, retained only for that gate). n==0 and n==1 take the same paths with zero per-frame overhead. +- **Delete**: `buildBoxToDriver`, `buildSparseIdentityLUT`, the `maxMultiplier` scratch + `physicals[]`, the `maxDestWide`/`ceiling` product-clamp. (`isNaturalOrder` demoted to gating only the memcpy shortcut.) +- `loop()`: drop the `break;` after the first modifier — tick **all** enabled modifiers in child order. Coalesce dynamic rebuilds with a single dirty flag (avoid N rebuilds/frame when several modifiers tick). Add the `hasLive_`-gated per-frame remap pass. +- Defensive bounds-guard on the folded coord before flatten (a buggy modifier must not write past `counts[]`). + +### `src/light/modifiers/*.h` — rewrite all 5 to the fold interface +- **MultiplyModifier** — `modifyLogicalSize`: divide per axis; `modifyLogical`: fold `pos` into the tile, mirror odd tiles (mirror is already a Multiply control — no separate Mirror class). ~10 lines, simpler than today. +- **RegionModifier** — `modifyLogicalSize`: `axisCount`; `modifyLogical`: subtract `axisStart`, return `inBox(pos, logical)` (reject outside region). Reuses the existing `axisStart`/`axisCount` helpers. +- **CheckerboardModifier** — `modifyLogicalSize`: identity; `modifyLogical`: parity test, return `false` to drop. ~5 lines. +- **RotateModifier** — `modifyLive` (inverse-sample gather, its current math moves here, unchanged visuals); `expand` grows the box via `modifyLogicalSize`. No static fold. +- **RandomMapModifier** — bijective permutation; can express as static `modifyLogical` (1:1) since it's a permutation; keep its `loop()` beat-reshuffle. + +### Tests +- `unit_Coord3D` — operator coverage. +- Rewrite `unit_MultiplyModifier`, `unit_RegionModifier`, `unit_CheckerboardModifier`, `unit_RotateModifier`, `unit_RandomMapModifier` to the fold interface (they call the old virtuals directly today, so they change in lockstep with their modifier). +- `unit_Layer_sparse_mapping` — the green gate for the build (asserts CSR contents, not the interface). Update corner-fan-out + region cases to the fold values. +- New `unit_Layer_modifier_chain` — the payoff: Region∘Multiply-mirror composed CSR; A∘B ≠ B∘A; a disabled middle modifier is skipped. +- New scenario `scenario_modifier_chain` — reorder a 2-modifier stack live, assert the composite changes; perf capture at depth 2–3. + +### Docs +- `ModifierBase.md` (or the modifier specs) — the three-hook contract, the reject convention, the pay-for-what-you-use guarantee. +- `architecture.md` § Layers and Layer / § Modifiers — update "first enabled modifier" → "modifier chain", document physical→logical build + the live seam. +- `docs/backlog/backlog-mixed.md` — delete the "Composed modifiers" item (shipped). +- `decisions.md` — the inversion lesson (forward-fold static, inverse-gather dynamic; CSR-via-counting-sort keeps the hot path untouched). +- `performance.md` — chain-depth + dynamic-modifier per-frame cost. + +## Commit breakdown (~6) + +1. `Coord3D` + new `ModifierBase` hooks **alongside** the old ones (no behavior change); `unit_Coord3D`. All green. +2. Implement new hooks on all 5 modifiers (old hooks still present, Layer still uses old); rewrite each modifier's unit test to the new hooks. Green. +3. New `rebuildLUT()` counting-sort fold build using the new hooks; delete `buildBoxToDriver`/`buildSparseIdentityLUT`/scratch/ceiling; keep `setIdentity` shortcut. `unit_Layer_sparse_mapping` is the gate. The big commit. +4. Delete old `mapToPhysical`/`logicalDimensions`/`maxMultiplier` from base + modifiers; remove old-hook test cases. Green. +5. Dynamic tier: `loop()` ticks all modifiers + dirty-flag coalesced rebuild + the `hasLive_`-gated per-frame remap seam; Rotate/RandomMap to the new model; rewrite their tests. Green. +6. `unit_Layer_modifier_chain` + `scenario_modifier_chain` + docs + backlog delete + decisions/perf. Green. + +**Test-green honesty:** commits 1, 4, 6 are cleanly additive/green. Commits 2, 3, 5 rewrite tests in the same commit as the contract they pin (normal for an interface inversion) — "green" means "the new contract's tests pass in that commit," not "old tests still pass." + +## Risks / watch-items +- **Build cost rises** (two `forEachCoord` passes + counts array, every rebuild even for n==1). Cold path, bounded — don't sell it as free. +- **Coalesced dynamic rebuild**: today a modifier's `loop()` re-enters `Layer::onBuildState()`; with N modifiers, gate to one rebuild/frame via a dirty flag (cleaner than today's re-entrancy). +- **`expand`-style growth**: size the LUT from the post-fold box; guard the flatten against `nrOfLightsType` overflow on a grown box. +- **Per-frame seam scratch buffer**: the live remap needs a logical-sized scratch (allocated when `hasLive_`, off the hot path). Confirm it degrades gracefully on OOM (fall back to static LUT, no live motion, status warning). + +## Verification +- `ctest` + `uv run scripts/scenario/run_scenario.py` green at each commit boundary. +- New `unit_Layer_modifier_chain` proves Region∘Multiply composes (A∘B ≠ B∘A, disabled-middle skipped). +- Live on the bench: build a Region + Multiply(mirror) stack on the S3, confirm the carved-then-mirrored result in the preview; add a Rotate on top, confirm smooth (not stepped) dynamic rotation with the static chain underneath. +- Perf: capture single-layer (no modifier) tick on S3/classic/P4 — must match today (max-speed guarantee); capture a static 2-chain and a dynamic (Rotate) chain to quantify the live-seam cost. diff --git a/docs/moonmodules/light/ModifierBase.md b/docs/moonmodules/light/ModifierBase.md new file mode 100644 index 0000000..a1185df --- /dev/null +++ b/docs/moonmodules/light/ModifierBase.md @@ -0,0 +1,31 @@ +# ModifierBase + +Light-domain MoonModule subclass for modifiers. A modifier is a **coordinate transform** that reshapes how a Layer's effect output maps onto the physical lights. Multiple modifiers on one Layer **compose**: they apply in child order, each reshaping the result of the one below (Region *then* Multiply-mirror *then* Rotate). + +## The fold contract + +A Layer builds its mapping by walking the **physical** lights and folding each through every enabled modifier in order — the composition `M₁∘M₂∘…∘Mₙ` collapsed into one mapping, so the per-frame render stays a single lookup. Three hooks, each a no-op by default so a modifier implements only what it needs: + +- **`modifyLogicalSize(Coord3D& size)`** — static, build-time, once per rebuild in child order. Folds the logical box (Multiply divides it, Region crops it, a mask leaves it). The running `size` starts at the physical box; each modifier reshapes it. +- **`bool modifyLogical(Coord3D& pos, const Coord3D& phys, const Coord3D& logical)`** — static, build-time, per physical light in child order. Folds a physical coordinate into this stage's logical space in place. Returns **`false` to reject** — the physical light has no logical source (a mask drops it, a region light falls outside the crop). A bool, not a sentinel coord, so a later modifier's `% size` can't alias a sentinel back into range. +- **`modifyLive(Coord3D& pos, const Coord3D& logical)`** — dynamic, per-frame at render time. Remaps a coordinate without rebuilding the mapping (smooth rotation/scroll). The Layer runs this pass **only** when some enabled modifier reports `hasModifyLive()`, so a static-only chain pays nothing per frame — the render path stays at full speed (pay-for-what-you-use). It is a **backward** map: for each destination cell it returns the source cell to gather, so no destination is left torn. + +A beat-driven modifier (RandomMap reshuffles on a timer) sets a flag in `loop()`; the Layer polls `consumeNeedsRebuild()` across its modifiers and rebuilds the mapping **once** if any asks, coalescing several dynamic modifiers into a single rebuild. + +`dimensions()` (the 📏/🟦/🧊 chip) advertises which axes the modifier can transform. + +## Fan-out is free + +Because the build walks physical lights, fan-out (one logical cell driving N physical lights — a Multiply kaleidoscope) emerges naturally: N physical lights fold onto the same logical cell. There is no build-time fan-out list and no product-of-multipliers ceiling — each physical light contributes at most one destination, so the mapping can never overflow. + +## Affine modifiers and the matrix reference + +Most modifiers are **non-affine** (a mask is a predicate, a tile is modulo) and express their fold directly. [RotateModifier](modifiers/RotateModifier.md) is the exception and the codebase's **transform-matrix reference**: rotation is the canonical affine transform, written as an explicit integer 2×2 rotation matrix in `modifyLive`. A future affine "Transform" modifier (translate+scale+rotate+shear in one) would compose its matrix the same way and apply it through the same hook — the fold interface hosts a matrix-backed modifier with no change. + +## Prior art + +The mapping bake is the textbook image-warping pattern (precompute a coordinate transform into a spatial LUT; build it by backward mapping so no output pixel is unfilled — [Forward and Backward Mapping for Computer Vision](https://towardsdatascience.com/forward-and-backward-mapping-for-computer-vision-833436e2472/)). Collapsing a **chain** of discrete pixel folds into one index table — instead of giving each node its own frame buffer as a PC node graph (TouchDesigner, shader graphs) would — is the MCU-memory synthesis credited to **MoonLight** ([M_MoonLight.h](https://github.com/MoonModules/MoonLight/blob/main/src/MoonLight/Nodes/Modifiers/M_MoonLight.h), [VirtualLayer.cpp](https://github.com/MoonModules/MoonLight/blob/main/src/MoonLight/Layers/VirtualLayer.cpp)): `modifySize` / `modifyPosition` / `modifyXYZ` map to our `modifyLogicalSize` / `modifyLogical` / `modifyLive`, written fresh against our `MappingLUT`. + +## Source + +[ModifierBase.h](../../../src/light/modifiers/ModifierBase.h) — the hook contract. The Layer-side fold build (physical→logical counting-sort, the live pass) lives in [Layer.h](../../../src/light/layers/Layer.h). diff --git a/docs/moonmodules/light/modifiers/CheckerboardModifier.md b/docs/moonmodules/light/modifiers/CheckerboardModifier.md index 90f17e5..1d2ad91 100644 --- a/docs/moonmodules/light/modifiers/CheckerboardModifier.md +++ b/docs/moonmodules/light/modifiers/CheckerboardModifier.md @@ -13,13 +13,13 @@ Static modifier. Masks the layer in a checkerboard pattern: lights in the "off" ## Effect on the pipeline -- **Logical dimensions unchanged** (identity) — the box is the same; only which cells contribute changes. -- **1:1 or 1:0 mapping** — each logical light maps to itself (one physical position) if its square is "on", or to **nothing** if "off". The "drop" is expressed as `outCount = 0` from `mapToPhysical`; `Layer::rebuildLUT` records that as a logical light with no destination (the same zero-destination path the sparse layout translation already uses), so a dropped light simply doesn't appear in the driver buffer. `maxMultiplier()` is 1 — it never fans out. +- **Logical box unchanged** — a mask doesn't resize the box (no `modifyLogicalSize`); only which cells contribute changes. +- **Pass or drop** — `modifyLogical` returns `true` to pass a light through unchanged, or `false` to drop it (an "off" square), so a dropped physical light has no logical source and stays dark. - **Square parity**: a light at `(x,y,z)` belongs to square `(x/size, y/size, z/size)`; the square is "on" when the sum of those indices is even (flipped by `invert`). ## Cross-domain wiring -A Layer applies its first enabled modifier during `rebuildLUT`; modifier chaining (where Checkerboard-then-Multiply differs from Multiply-then-Checkerboard) is not implemented — only the first enabled modifier applies. See [architecture.md § Modifiers](../../../architecture.md#modifiers). The mask integrates with no `ModifierBase` contract change because the contract already permits a logical light to map to zero physical positions. +A Layer folds all its enabled modifiers as a chain (Checkerboard-then-Multiply differs from Multiply-then-Checkerboard). The fold + reject contract is in [ModifierBase](../ModifierBase.md). ## Tests @@ -31,7 +31,7 @@ A Layer applies its first enabled modifier during `rebuildLUT`; modifier chainin ### MoonLight — M_MoonLight.h Checkerboard ([source](https://github.com/MoonModules/MoonLight/blob/main/src/MoonLight/Nodes/Modifiers/M_MoonLight.h)) -MoonLight's Checkerboard drops lights by setting `position.x = UINT16_MAX` (a sentinel the layout pass skips), with `size`, `invert`, and a `group` flag. We express the drop as `outCount = 0` (our equivalent, no sentinel needed) and start with `size` + `invert`; `group` is deferred. +MoonLight's Checkerboard drops lights by setting `position.x = UINT16_MAX` (a sentinel the layout pass skips), with `size`, `invert`, and a `group` flag. We express the drop as `modifyLogical` returning `false` (no sentinel needed) and start with `size` + `invert`; `group` is deferred. ## Source diff --git a/docs/moonmodules/light/modifiers/MultiplyModifier.md b/docs/moonmodules/light/modifiers/MultiplyModifier.md index b8efe1c..2249c39 100644 --- a/docs/moonmodules/light/modifiers/MultiplyModifier.md +++ b/docs/moonmodules/light/modifiers/MultiplyModifier.md @@ -20,13 +20,13 @@ The defaults (`multiply 2/2/1`, `mirror all on`) reproduce the canonical mirror- ## Effect on the pipeline - **Logical box shrinks by the multiplier**: `logW = physW / multiplyX` (etc.). 128×128 with multiply 2/2 → 64×64 logical (the effect renders a quarter of the lights). The effective multiplier clamps to the axis extent — `multiplyZ` on a depth-1 layout clamps to 1 (no-op), never blanking the layer. -- **LUT produces 1:N mappings**: each logical light maps to `multiplyX·multiplyY·multiplyZ` physical positions (the tiles). There is **no fixed fan-out cap** — `Layer::rebuildLUT` sizes a per-light scratch buffer to the modifier's `maxMultiplier()` (heap, cold path), so the full fan-out is emitted, limited only by memory (an alloc failure degrades to the identity LUT). +- **Fan-out is the fold**: each physical light folds (`pos % logicalSize`) onto its logical cell, so the `multiplyX·multiplyY·multiplyZ` physical lights of the tiles all land on one logical light — the 1:N mapping emerges from the build with no fan-out list and no cap (see [ModifierBase § Fan-out is free](../ModifierBase.md)). - **Tile vs fold**: with mirror **off** on an axis, tiles repeat (translate); with mirror **on**, odd-numbered tiles reflect within their tile (`size − 1 − pos`), so multiply 2 + mirror = a fold. - **Integer division**: a physical extent not divisible by the multiplier leaves uncovered cells at the high edge (they map to nothing) — the same edge behaviour the old mirror had on odd widths, without a shared centre line. ## Cross-domain wiring -A Layer applies its first enabled modifier during `rebuildLUT` — modifier chaining (where order matters, e.g. multiply-then-checkerboard ≠ checkerboard-then-multiply) is not implemented; see [architecture.md § Modifiers](../../../architecture.md#modifiers). `mapToPhysical` emits physical box indices; the Layer translates them to driver indices and drops any that fall outside the real light set. +A Layer folds all its enabled modifiers as a chain (order matters: multiply-then-checkerboard ≠ checkerboard-then-multiply). The fold contract is in [ModifierBase](../ModifierBase.md). ## Tests diff --git a/docs/moonmodules/light/modifiers/RegionModifier.md b/docs/moonmodules/light/modifiers/RegionModifier.md index 97d2abd..18f1e69 100644 --- a/docs/moonmodules/light/modifiers/RegionModifier.md +++ b/docs/moonmodules/light/modifiers/RegionModifier.md @@ -2,38 +2,42 @@ Static modifier. Carves the layer down to a sub-rectangle of the physical bounding box: the effect renders only inside the region, everything outside is dark. The region is given as **percentages of the physical extent on each axis**, so it survives a physical resize — a `0..50` region stays the left half whether the panel is 64 or 128 wide. Default `0..100` on every axis is the full box (an identity carve). -A Layer applies only its **first enabled modifier**, so today a Layer uses *either* Region *or* another modifier (Multiply, …) at a time. Region and Multiply are independent, so stacking them (occupy a region *and* tile/mirror within it — Region then Multiply) is planned via [modifier chaining](../../../backlog/README.md). +Region and Multiply are independent and **compose**: a layer can occupy a region *and* be tiled/mirrored within it (Region then Multiply), since a Layer folds its whole enabled modifier chain in order (see [ModifierBase](../ModifierBase.md)). ## Controls - `startX` / `startY` / `startZ` (Int16, default 0) — region start, as a percentage of physical width / height / depth. - `endX` / `endY` / `endZ` (Int16, default 100) — region end, as a percentage of physical width / height / depth. -`Int16` (not a 0–100 slider) so negative and >100 values round-trip through `/api/state`, `/api/types`, and persistence; the carve math clamps them into the box. +`Int16`, not a 0–100 slider: the UI renders an unbounded int16 as a **−100..200 percentage slider** so the window can slide **off-screen** (negative start / >100 end). Values round-trip through `/api/state`, `/api/types`, and persistence. ## Region math -Per axis, **half-open** `[startPixel, endPixel)`: +Per axis, **half-open** `[startPixel, endPixel)`, **un-clamped** to the box: -- `startPixel = floor(start% / 100 · extent)`, clamped to `[0, extent-1]`. -- `endPixel = ceil(end% / 100 · extent)`, **exclusive**, clamped to `[startPixel+1, extent]`. -- region size = `endPixel − startPixel` (always ≥ 1). +- `startPixel = floor(start% / 100 · extent)` — may be negative. +- `endPixel = ceil(end% / 100 · extent)`, **exclusive** — may exceed `extent`. +- window size = `endPixel − startPixel` (floored to ≥ 1 on a non-empty axis). -Half-open is what makes abutting regions **tile exactly**: a `0..50` and a `50..100` layer split a 128-wide axis into pixels `0..63` and `64..127` — no overlap, no gap. `start` floors and `end` ceils so a small panel never rounds to an empty region (`start 33 / end 66` on a 4-wide axis → `floor(1.32)=1` .. `ceil(2.64)=3` → pixels 1, 2). Default `end 100` on a `W`-wide axis → `ceil(W)=W` → the full width. +Half-open makes abutting windows **tile exactly**: a `0..50` and a `50..100` layer split a 128-wide axis into pixels `0..63` and `64..127` — no overlap, no gap. `start` floors and `end` ceils so a small panel never rounds to an empty window. Default `0..100` on a `W`-wide axis → the full width. + +### Off-screen windows (move, don't rescale) + +The window's **logical size is the full `start..end` span**, so the effect always renders at a fixed scale — moving `start` and `end` together slides the window without resizing it (like dragging an OS window). A physical light outside the window is dropped; window cells with no physical light under them (the off-screen part) stay dark. A window slid **entirely** off the box (e.g. `start=-100, end=0`) maps no lights — the layer goes dark, the way you move an effect completely out of view. This is what lets a future animation translate an effect across (and off) the panel without distorting it. ## Effect on the pipeline -- **Logical dimensions = the region size** — `logicalDimensions()` reports the carved rectangle, so the Layer's render buffer (and the Layer status line `w×h×d`) shrinks to the region. The effect only ever renders the region; the rest of the layer has no logical source and stays dark. This is the same "the box is smaller than the physical box" mechanism a Mirror modifier uses. -- **1:1 mapping with a start offset** — `mapToPhysical()` translates a region-local cell `(lx,ly,lz)` to the box cell `(lx+startPixelX, ly+startPixelY, lz+startPixelZ)`, a single destination. Because the logical box is already the region size, every region cell is in-bounds; no per-cell drop is needed. `maxMultiplier()` is 1 — it never fans out. +- **Logical box = the region size** — `modifyLogicalSize` shrinks the box to the carved rectangle, so the Layer's render buffer (and the status line `w×h×d`) shrinks to the region. The effect only ever renders the region. +- **Fold + reject** — `modifyLogical` folds a physical light into region-local space (subtract the start offset) and returns `false` for any physical light outside the region, so everything beyond the region stays dark. A 1:1 fold, never fans out. - **Fast path**: the cheapest carve is *no modifier at all* — then `Layer::rebuildLUT` keeps its identity-memcpy / sparse fast path with zero carving cost. The default is to not add a RegionModifier; a full-region `0..100` one is correct but not the absolute cheapest, so full-coverage layers simply omit it. ## Cross-domain wiring -A Layer applies its first enabled modifier during `rebuildLUT`. Region is a normal `ModifierBase` (no contract change) — it expresses carving entirely through the existing `logicalDimensions()` + `mapToPhysical()` virtuals, the same two the LUT builder already calls. See [architecture.md § Modifiers](../../../architecture.md#layers-and-layer). +Region is a normal `ModifierBase` — carving is its `modifyLogicalSize` + `modifyLogical` fold, composed into the chain like any modifier. See [ModifierBase](../ModifierBase.md). ## Tests -[Unit tests: RegionModifier](../../../tests/unit-tests.md#regionmodifier) — the region math (full box, exact half, abutting-tile, small-panel rounding, ≥1-pixel floor, out-of-range clamp, degenerate axes) and the coordinate offset mapping. [Unit tests: Layer](../../../tests/unit-tests.md#layer) adds the integration case: a RegionModifier shrinks the Layer's logical box to the region and the LUT maps only region cells. +[Unit tests: RegionModifier](../../../tests/unit-tests.md#regionmodifier) — the region math (full box, exact half, abutting-tile, small-panel rounding, ≥1-pixel floor, off-screen / fully-off / wider-than-box windows, degenerate axes) and the coordinate offset mapping. [Unit tests: Layer](../../../tests/unit-tests.md#layer) adds the integration case: a RegionModifier shrinks the Layer's logical box to the region and the LUT maps only region cells. ## Prior art diff --git a/docs/moonmodules/light/modifiers/RotateModifier.md b/docs/moonmodules/light/modifiers/RotateModifier.md index 1dbf957..add27a7 100644 --- a/docs/moonmodules/light/modifiers/RotateModifier.md +++ b/docs/moonmodules/light/modifiers/RotateModifier.md @@ -1,20 +1,20 @@ # RotateModifier -A **dynamic modifier** that rotates the 2D image around its centre, turning continuously over time. Like [RandomMapModifier](RandomMapModifier.md), the rotation is a coordinate remap baked into the [Layer](../Layer.md)'s LUT; the angle advances on a `speed` timer and the LUT rebuilds when the angle crosses to a new step — not every frame. +A **dynamic modifier** that rotates the 2D image around its centre, turning continuously over time. The one modifier that overrides `modifyLive` (per-frame, no mapping rebuild) — so the rotation is smooth, and the Layer runs its live pass only because this modifier is present (a static-only chain pays nothing per frame; see [ModifierBase](../ModifierBase.md)). Also the codebase's **transform-matrix reference**. ## Controls -- `speed` — rotation speed (1–255, default 1). Higher turns faster and rebuilds the LUT more often (still bounded — a rebuild fires only on an angle-step change, not per frame). +- `speed` — rotation speed (1–255, default 1). `loop()` advances the angle on the timer; `modifyLive` applies it on the next frame (no rebuild). ## How it works -The angle is a `uint8_t` (256 steps per turn). Each destination light at (dx, dy) from the centre samples the **source** at the inverse rotation — `sx = dx·cosθ + dy·sinθ`, `sy = −dx·sinθ + dy·cosθ` — using the project's [`cos8`/`sin8`](../../core/Control.md) integer LUT (signed component `val − 128`, divided back by 128), with nearest-neighbour sampling (no float, no bilinear). A source that falls outside the grid is dropped (that light goes dark at this angle), the same `outCount=0` path [CheckerboardModifier](CheckerboardModifier.md) uses. The per-frame tick (`loop()`, the dynamic-modifier hook also used by RandomMapModifier) advances the angle and, on a step change, calls the Layer's `onBuildState()` to rebuild the LUT — no per-frame allocation, no `Layer::render` coupling. +The angle is a `uint8_t` (256 steps per turn). `modifyLive` is a **backward** map: for each destination cell it computes the **source** it samples, via the inverse rotation `R(-θ)` written as an explicit integer 2×2 matrix `[[c, s], [-s, c]]` applied to the centred coordinate. `c`/`s` come from the project's [`cos8`/`sin8`](../../core/Control.md) integer LUT (signed component `val − 128`, scaled by 128; the `>>7` divides back out), nearest-neighbour (no float). A source outside the box leaves that destination dark — the Layer's live pass clears it. Rotation is the canonical affine transform, which is why it's the matrix example; the non-affine modifiers (mask, tile) fold directly instead. 2D only: the z axis passes through unchanged. ## Prior art -- **MoonLight — M_MoonLight.h Rotate / PinWheel** — a per-light `modifyXYZ()` coordinate transform on the hot path. This version carries the transform in the LUT instead, reusing the dynamic-modifier `loop()` hook and the existing rebuild path rather than a render-time transform. +- **MoonLight — M_MoonLight.h Rotate / PinWheel** — a per-light `modifyXYZ()` coordinate transform. Our `modifyLive` is the same per-frame hook; we carry an explicit rotation matrix. ## Source diff --git a/src/light/layers/Layer.h b/src/light/layers/Layer.h index 68b5c2d..1fbf3b4 100644 --- a/src/light/layers/Layer.h +++ b/src/light/layers/Layer.h @@ -18,6 +18,8 @@ class Layer : public MoonModule { ModuleRole role() const override { return ModuleRole::Layer; } const char* acceptsChildRoles() const override { return "effect,modifier"; } + ~Layer() override { if (liveScratch_) platform::free(liveScratch_); } + // Composition parameters — INERT on the Layer (it never reads them; a Layer // can't know its position in the stack or what's beneath it). The Drivers @@ -132,18 +134,75 @@ class Layer : public MoonModule { extrude(eff->dimensions()); eff->addAccumUs(platform::micros() - start); } - // Tick the active modifier AFTER the effect pass, so the frame's buffer is - // fully written before it acts. Only the FIRST enabled modifier is ticked — - // that's the one rebuildLUT() actually applies (it uses the first enabled - // modifier too), so ticking a later one would let its loop() drive rebuilds - // that the LUT never reflects. A static modifier's loop() is empty (it shapes - // the LUT once, on a control change). A dynamic one (RandomMapModifier) may - // call our onBuildState() from here to rebuild the LUT on its timer — safe - // re-entrancy precisely because every effect write for this frame is done. + // Tick EVERY enabled modifier AFTER the effect pass (the frame's buffer is + // fully written before any modifier acts). A static modifier's loop() is empty; + // a beat-driven one (RandomMap) sets a rebuild flag we coalesce below; a live + // one (Rotate) advances its angle here and remaps in the live pass that follows. + bool rebuild = false; for (uint8_t i = 0; i < childCount(); i++) { if (child(i)->role() != ModuleRole::Modifier || !child(i)->enabled()) continue; - static_cast(child(i))->loop(); - break; // align with rebuildLUT: only the first enabled modifier is active + auto* m = static_cast(child(i)); + m->loop(); + rebuild |= m->consumeNeedsRebuild(); + } + // One rebuild per frame even if several modifiers asked (no re-entrant rebuild + // from inside a modifier's loop()). onBuildState rebuilds the whole pipeline, + // which re-runs rebuildLUT() with the modifiers' fresh state. + if (rebuild) { onBuildState(); return; } + + // Live pass: remap the logical buffer per frame for dynamic modifiers (Rotate). + // Skipped entirely when no modifier is live — a static-only chain pays nothing, + // the buffer goes straight to the driver scatter (the pay-for-what-you-use rule). + if (hasLive_) applyLivePass(); + } + + // Per-frame backward gather for live (animated) modifiers. For each DESTINATION + // logical cell, fold its coordinate through the enabled live modifiers to the SOURCE + // cell it samples, and copy that source pixel in — so no destination is left torn + // (backward mapping, the textbook reason image warping samples backward). Reads from + // a snapshot (liveScratch_) so a source already overwritten this pass isn't re-read. + // Out-of-box sources leave the destination dark (cleared). Cold relative to the build + // but on the hot path — runs only because hasLive_ gated it, and only the live + // modifiers participate (static ones are already baked into lut_). + void applyLivePass() { + uint8_t* buf = buffer_.data(); + if (!buf) return; + const size_t cpl = channelsPerLight_; + const size_t bytes = static_cast(width_) * height_ * depth_ * cpl; + if (bytes == 0) return; + // Lazy snapshot buffer, (re)sized to the logical buffer. Allocated only here, so + // a static-only chain never pays for it. An alloc failure skips the live pass + // (the static frame still shows — degraded, not crashed). + if (liveScratchBytes_ != bytes) { + if (liveScratch_) platform::free(liveScratch_); + liveScratch_ = static_cast(platform::alloc(bytes)); + liveScratchBytes_ = liveScratch_ ? bytes : 0; + } + if (!liveScratch_) return; + std::memcpy(liveScratch_, buf, bytes); // snapshot the source frame + + const Coord3D logical{width_, height_, depth_}; + for (lengthType z = 0; z < depth_; z++) { + for (lengthType y = 0; y < height_; y++) { + for (lengthType x = 0; x < width_; x++) { + Coord3D src{x, y, z}; + for (uint8_t i = 0; i < childCount(); i++) { + if (child(i)->role() != ModuleRole::Modifier || !child(i)->enabled()) continue; + auto* m = static_cast(child(i)); + if (m->hasModifyLive()) m->modifyLive(src, logical); + } + const size_t dstIdx = (static_cast(z) * height_ * width_ + + static_cast(y) * width_ + x) * cpl; + if (src.x >= 0 && src.x < width_ && src.y >= 0 && src.y < height_ && + src.z >= 0 && src.z < depth_) { + const size_t srcIdx = (static_cast(src.z) * height_ * width_ + + static_cast(src.y) * width_ + src.x) * cpl; + std::memcpy(buf + dstIdx, liveScratch_ + srcIdx, cpl); + } else { + std::memset(buf + dstIdx, 0, cpl); // source off-box → dark + } + } + } } } @@ -202,157 +261,71 @@ class Layer : public MoonModule { bool lutSkipped() const { return lutSkipped_; } + // Max enabled static modifiers folded into one mapping. A deep stack is rare + // (a Region + a Multiply + maybe a mask); the cap bounds the per-light fold + // loop and the on-stack modifier array. Extra enabled modifiers past the cap + // are ignored (the chain still renders, just truncated) — robust, not a crash. + static constexpr uint8_t kMaxChain = 8; + // Precondition: physicalWidth_/Height_/Depth_ must be set (call from onBuildState) void rebuildLUT() { lutSkipped_ = false; clearStatus(); // re-evaluated below if a degrade path is taken - // Find first enabled modifier (if any) - ModifierBase* mod = nullptr; - for (uint8_t i = 0; i < childCount(); i++) { - if (child(i)->role() == ModuleRole::Modifier && child(i)->enabled()) { - mod = static_cast(child(i)); - break; - } + // Collect the enabled STATIC modifiers in child order. A dynamic modifier + // (Rotate, hasModifyLive) does NOT fold into the static mapping — it remaps + // per frame in Layer::loop's live pass — so it's excluded here. The mapping + // built below is the composition of the static folds M₁∘…∘Mₙ. + ModifierBase* chain[kMaxChain]; + uint8_t chainLen = 0; + hasLive_ = false; + // Fold the box through each enabled static modifier in order. modifyLogicalSize + // mutates the running box AND lets the modifier stash its own output size (the + // way MoonLight's modifiers cache modifierSize) — so in the per-light fold each + // modifier reads the box at ITS OWN stage from itself, no per-stage array here. + Coord3D box{physicalWidth_, physicalHeight_, physicalDepth_}; + for (uint8_t i = 0; i < childCount() && chainLen < kMaxChain; i++) { + if (child(i)->role() != ModuleRole::Modifier || !child(i)->enabled()) continue; + auto* m = static_cast(child(i)); + if (m->hasModifyLive()) { hasLive_ = true; continue; } // dynamic: per-frame, not baked + m->modifyLogicalSize(box); + clampLogical(box); + chain[chainLen++] = m; } - // Box vs real-light count. The render grid is the layout's bounding box - // (physicalWidth_×Height_×Depth_); the DRIVER buffer holds only the real - // lights (Layouts::totalLightCount). For a dense GridLayout every box - // cell is a light → boxCount == driverCount and the LUT is identity (the - // fast memcpy path). For a sparse layout (sphere, ring) driverCount < - // boxCount: most box cells map to NO light, so we always build a LUT - // whose destinations are DRIVER indices [0..driverCount). This makes the - // driver/output buffer — what ArtNet, LEDs, and the preview consume — - // exactly the real lights, never the padded box. - nrOfLightsType boxCount = static_cast(physicalWidth_) * physicalHeight_ * physicalDepth_; - nrOfLightsType driverCount = physicalLightCount(); // == Layouts::totalLightCount() - const bool sparse = (driverCount != boxCount); - - if (!mod) { - // No modifier: logical == physical box. Effects render into the dense - // box buffer; the LUT extracts the real lights into the driver buffer. - width_ = physicalWidth_; - height_ = physicalHeight_; - depth_ = physicalDepth_; - if (!sparse && isNaturalOrder()) { - // Dense grid in natural order: box cell i IS driver light i. Identity (memcpy). - lut_.setIdentity(boxCount); - allocateBuffer(boxCount); - return; - } - // Sparse (some box cells have no light) OR dense-but-shuffled (a serpentine grid: same - // count, but driver index i ≠ box cell i) → build the box→driver LUT so the driver - // buffer is the real lights in driver order. logicalCount = boxCount, ≤1 dest per cell. - buildSparseIdentityLUT(boxCount, driverCount); - allocateBuffer(boxCount); // layer (render) buffer stays the dense box - return; - } + // Final logical box = the running box after the last static modifier. + Coord3D logical = box; + width_ = logical.x; height_ = logical.y; depth_ = logical.z; - // Apply first static modifier to compute logical dimensions - mod->logicalDimensions(physicalWidth_, physicalHeight_, physicalDepth_, - width_, height_, depth_); - - nrOfLightsType logicalCount = static_cast(width_) * height_ * depth_; - - // Degrade target on OOM: a sparse layout degrades to the (cheaper) - // box→driver identity LUT so the driver buffer stays the real lights; a - // dense grid degrades to the plain identity/memcpy path as before. - auto degradeIdentity = [&]() { - width_ = physicalWidth_; - height_ = physicalHeight_; - depth_ = physicalDepth_; - if (sparse) { buildSparseIdentityLUT(boxCount, driverCount); } - else { lut_.setIdentity(boxCount); } - allocateBuffer(boxCount); - }; + const Coord3D phys{physicalWidth_, physicalHeight_, physicalDepth_}; + const nrOfLightsType boxCount = cellCount(phys); + const nrOfLightsType logicalCount = cellCount(logical); + const nrOfLightsType driverCount = physicalLightCount(); // == Layouts::totalLightCount() + const bool dense = (driverCount == boxCount); - // maxDest in DRIVER-index terms. The modifier's multiplier bounds how - // many destinations a logical cell fans out to; clamp to 2× the real - // light count (was box count — driverCount is the true ceiling now). - // Compute in 64-bit: on a no-PSRAM board nrOfLightsType is uint16, and - // logicalCount × maxMultiplier (e.g. 256 × 256) overflows it — which - // wrapped maxDest to a tiny value, built a near-empty LUT, and blanked - // the display (the multiplyZ-on-2D black-screen bug). Clamp the ceiling - // before narrowing back to nrOfLightsType. - uint64_t maxDestWide = static_cast(logicalCount) * mod->maxMultiplier(); - const uint64_t ceiling = static_cast(driverCount) * 2; - if (maxDestWide > ceiling) maxDestWide = ceiling; - nrOfLightsType maxDest = static_cast(maxDestWide); - - // MappingLUT::build owns the allocation decision: it tries a single - // contiguous block, falls back to fixed-size pages when no single block - // fits but total heap allows it, and returns false only on genuine - // exhaustion (total free minus HEAP_RESERVE too small). So no - // single-block pre-check here — that pre-check is exactly what made a - // fragmented-but-not-exhausted heap (the 128×128 mirror case on - // no-PSRAM) degrade unnecessarily. - if (!lut_.build(logicalCount, maxDest)) { - std::printf(" DEGRADE LUT skipped (need %u, free %u)\n", - static_cast(MappingLUT::estimateBytes(logicalCount, maxDest)), - static_cast(platform::freeHeap())); - lutSkipped_ = true; - setStatus("modifier LUT skipped — not enough memory", Severity::Warning); - degradeIdentity(); + // Fast path — no static modifiers, dense grid in natural order: box cell i + // IS driver light i, so the mapping is the identity memcpy. This is the FPS + // floor for the common case; keep it before any allocation. + if (chainLen == 0 && dense && isNaturalOrder()) { + lut_.setIdentity(boxCount); + allocateBuffer(boxCount); return; } - // Box→driver translation: the modifier reasons in box geometry and emits - // box-cell indices; the driver buffer is indexed by real-light index. A - // box→driver map (driverIdx per box cell, or "none") translates the - // modifier's output into driver-index space. For a dense grid this map - // is identity; for a sparse layout it drops box cells that aren't lights - // (e.g. a mirror destination landing off the sphere shell). Built from - // Layouts::forEachCoord. nullptr → dense grid → no translation needed. - nrOfLightsType* boxToDriver = sparse ? buildBoxToDriver(boxCount) : nullptr; - - // Per-light scratch buffer for the modifier's fan-out destinations. Sized - // to the modifier's maxMultiplier(), but clamped to boxCount: a logical - // light can't map to more positions than the physical box has cells, so - // that's the true ceiling. The clamp avoids a pathological over-alloc - // when maxMultiplier() saturates high (e.g. a 64×64×16 multiply reports - // 65535 but a small grid has far fewer cells). Heap-allocated on the cold - // path (like boxToDriver); an OOM here degrades to the identity LUT. - nrOfLightsType fanout = mod->maxMultiplier() > 0 ? mod->maxMultiplier() : 1; - if (fanout > boxCount && boxCount > 0) fanout = boxCount; - auto* physicals = static_cast( - platform::alloc(static_cast(fanout) * sizeof(nrOfLightsType))); - if (!physicals) { - if (boxToDriver) platform::free(boxToDriver); - lut_.free(); + // General build — fold each PHYSICAL light through the static chain to its + // logical cell, accumulating the physical (driver) index onto that cell. + // N physical lights folding onto one logical cell IS the fan-out (Multiply), + // so each physical light contributes at most ONE destination — maxDest is + // exactly driverCount, no product, no overflow ceiling. + if (!buildFoldedLUT(chain, chainLen, logical, logicalCount, driverCount)) { + // OOM in the fold build — degrade to identity (correct, not crash). lutSkipped_ = true; - setStatus("modifier scratch alloc failed — not enough memory", Severity::Warning); - degradeIdentity(); + setStatus("modifier mapping skipped — not enough memory", Severity::Warning); + width_ = physicalWidth_; height_ = physicalHeight_; depth_ = physicalDepth_; + lut_.setIdentity(boxCount); + allocateBuffer(boxCount); return; } - nrOfLightsType count; - nrOfLightsType logIdx = 0; - - for (lengthType z = 0; z < depth_; z++) { - for (lengthType y = 0; y < height_; y++) { - for (lengthType x = 0; x < width_; x++) { - count = 0; - mod->mapToPhysical(x, y, z, physicalWidth_, physicalHeight_, physicalDepth_, - physicals, count, fanout); - if (boxToDriver) { - // Translate box indices → driver indices, dropping cells - // that map to no real light (kNoDriver). - nrOfLightsType kept = 0; - for (nrOfLightsType i = 0; i < count; i++) { - nrOfLightsType d = (physicals[i] < boxCount) ? boxToDriver[physicals[i]] : kNoDriver; - if (d != kNoDriver) physicals[kept++] = d; - } - count = kept; - } - lut_.setMapping(logIdx, physicals, count); - logIdx++; - } - } - } - platform::free(physicals); - if (boxToDriver) platform::free(boxToDriver); - - lut_.finalize(); allocateBuffer(logicalCount); } @@ -361,10 +334,10 @@ class Layer : public MoonModule { // Does the layout emit lights in natural box order — driver index i == box cell i (x fastest, // then y, then z)? Measured, not declared: one allocation-free forEachCoord pass over the same - // coords the LUT build would walk, so there's a single source of truth (the coords) and no + // coords the build would walk, so there's a single source of truth (the coords) and no // per-layout hint to keep in sync. True → the dense memcpy fast path is valid; false → a - // reordered grid (serpentine) needs the box→driver LUT. Only meaningful for a dense layout - // (boxCount == driverCount); a sparse layout already routes to the LUT via the count check. + // reordered grid (serpentine) needs the folded LUT. Only meaningful for a dense layout + // (boxCount == driverCount); a sparse layout always routes to the folded build. bool isNaturalOrder() const { struct Ctx { lengthType w, h; bool ok; }; Ctx ctx{physicalWidth_, physicalHeight_, true}; @@ -378,44 +351,116 @@ class Layer : public MoonModule { return ctx.ok; } - // Allocate + fill a box-cell → driver-index map from the layout's real - // lights (Layouts::forEachCoord emits (driverIdx, x, y, z) in driver order). - // Cells with no light hold kNoDriver. Caller owns the returned block - // (platform::free). Returns nullptr on OOM. Box index = z*W*H + y*W + x. - nrOfLightsType* buildBoxToDriver(nrOfLightsType boxCount) const { - auto* map = static_cast(platform::alloc(static_cast(boxCount) * sizeof(nrOfLightsType))); - if (!map) return nullptr; - for (nrOfLightsType i = 0; i < boxCount; i++) map[i] = kNoDriver; - struct Ctx { nrOfLightsType* map; lengthType w, h; nrOfLightsType box; }; - Ctx ctx{map, physicalWidth_, physicalHeight_, boxCount}; - layouts_->forEachCoord([](void* c, nrOfLightsType driverIdx, lengthType x, lengthType y, lengthType z) { - auto* k = static_cast(c); - nrOfLightsType box = static_cast(z) * k->w * k->h - + static_cast(y) * k->w + x; - if (box < k->box) k->map[box] = driverIdx; - }, &ctx); - return map; - } + // Build the mapping by folding PHYSICAL lights to LOGICAL cells (physical→logical). + // Our MappingLUT is a CSR keyed by logical index, and setMapping demands sequential + // in-order writes — but folding scatters onto arbitrary, repeated logical indices. + // So this is the textbook counting-sort CSR build: pass A counts destinations per + // logical cell, prefix-sum to offsets, pass B scatters, then replay through + // setMapping in logical order. Two forEachCoord passes + a counts/dests scratch, + // all on the cold rebuild path; the hot-path read (forEachDestination) is unchanged. + // Returns false on OOM (caller degrades to identity). + bool buildFoldedLUT(ModifierBase* const* chain, uint8_t chainLen, + const Coord3D& logical, + nrOfLightsType logicalCount, nrOfLightsType driverCount) { + if (logicalCount == 0 || driverCount == 0) { lut_.setIdentity(0); return true; } + + // Scratch: per-logical-cell counts (then reused as the write cursor) and the + // scattered driver indices. Each physical light yields ≤1 destination, so the + // dests array is driverCount-sized — the tight, overflow-free ceiling. + auto* counts = static_cast( + platform::alloc(static_cast(logicalCount + 1) * sizeof(nrOfLightsType))); + auto* dests = static_cast( + platform::alloc(static_cast(driverCount) * sizeof(nrOfLightsType))); + if (!counts || !dests) { + if (counts) platform::free(counts); + if (dests) platform::free(dests); + return false; + } + for (nrOfLightsType i = 0; i <= logicalCount; i++) counts[i] = 0; + + // One callback does both passes. It folds the physical coord through the chain + // to a logical index (or kNoDriver if a modifier rejects it or it lands out of + // box — guarded, never trusted), then either counts it (pass A) or writes the + // driver index at the cell's cursor (pass B). Everything travels through the + // forEachCoord void* ctx, so the lambda captures nothing (it's a function ptr). + struct FoldCtx { + ModifierBase* const* chain; uint8_t chainLen; + Coord3D logical; nrOfLightsType logicalCount; // final box, for the flatten + guard + nrOfLightsType* counts; // pass A: per-cell count. pass B: per-cell write cursor. + nrOfLightsType* dests; // pass B only. + bool scatter; + } fctx{chain, chainLen, logical, logicalCount, counts, dests, /*scatter=*/false}; + + auto onCoord = [](void* c, nrOfLightsType driverIdx, lengthType x, lengthType y, lengthType z) { + auto* f = static_cast(c); + Coord3D pos{x, y, z}; + for (uint8_t i = 0; i < f->chainLen; i++) + if (!f->chain[i]->modifyLogical(pos)) return; // rejected — no logical source + if (pos.x < 0 || pos.x >= f->logical.x || pos.y < 0 || pos.y >= f->logical.y || + pos.z < 0 || pos.z >= f->logical.z) return; // defensive + const nrOfLightsType li = + static_cast(pos.z) * static_cast(f->logical.x) * static_cast(f->logical.y) + + static_cast(pos.y) * static_cast(f->logical.x) + + static_cast(pos.x); + if (li >= f->logicalCount) return; // defensive + if (f->scatter) f->dests[f->counts[li]++] = driverIdx; // pass B: write at the cursor + else f->counts[li]++; // pass A: bump the count + }; - // No-modifier sparse case: LUT maps each box cell → its driver index (≤1 - // destination), so the driver buffer holds only the real lights. logicalCount - // = boxCount; built in box order so setMapping's sequential contract holds. - void buildSparseIdentityLUT(nrOfLightsType boxCount, nrOfLightsType driverCount) { - nrOfLightsType* boxToDriver = buildBoxToDriver(boxCount); - if (!boxToDriver || !lut_.build(boxCount, driverCount)) { - // OOM — fall back to dense identity (sends the box; correct-not-crash). - if (boxToDriver) platform::free(boxToDriver); - lutSkipped_ = true; - setStatus("sparse LUT build failed — not enough memory", Severity::Warning); - lut_.setIdentity(boxCount); - return; + // Pass A — count. + layouts_->forEachCoord(onCoord, &fctx); + + // Prefix-sum counts → offsets (counts[li] becomes the start of cell li's run). + nrOfLightsType running = 0; + for (nrOfLightsType i = 0; i < logicalCount; i++) { + nrOfLightsType c = counts[i]; + counts[i] = running; + running += c; } - for (nrOfLightsType box = 0; box < boxCount; box++) { - nrOfLightsType d = boxToDriver[box]; - lut_.setMapping(box, &d, d == kNoDriver ? 0 : 1); + counts[logicalCount] = running; // total destinations + + // Pass B — scatter. counts[] is now the per-cell write cursor (offsets advance). + fctx.scatter = true; + layouts_->forEachCoord(onCoord, &fctx); + + // Pass B advanced each cell's cursor to the END of its run, so counts[i] now + // holds the end offset of cell i — which equals the START offset of cell i+1. + // The run for cell i is therefore [counts[i-1], counts[i]) with counts[-1]=0, + // i.e. the `start` cursor below. dests[] is already laid out in this exact CSR + // order, so replaying it through setMapping in logical order is a straight copy. + if (!lut_.build(logicalCount, running)) { // running == total destinations + platform::free(counts); + platform::free(dests); + return false; + } + nrOfLightsType start = 0; + for (nrOfLightsType i = 0; i < logicalCount; i++) { + nrOfLightsType end = counts[i]; // end of cell i's run + lut_.setMapping(i, &dests[start], static_cast(end - start)); + start = end; } lut_.finalize(); - platform::free(boxToDriver); + platform::free(counts); + platform::free(dests); + return true; + } + + // Cells in a box (the flat light count). 0 on any 0-extent axis. + static nrOfLightsType cellCount(const Coord3D& box) { + return static_cast(box.x) * static_cast(box.y) * + static_cast(box.z); + } + + // A modifier's modifyLogicalSize must not collapse an axis the physical box has: + // a 0-width logical box would blank the layer with no source for any effect. Clamp + // each axis to ≥1 where the physical box is non-empty (keep a genuinely 0 axis 0). + void clampLogical(Coord3D& logical) const { + if (physicalWidth_ > 0 && logical.x < 1) logical.x = 1; + if (physicalHeight_ > 0 && logical.y < 1) logical.y = 1; + if (physicalDepth_ > 0 && logical.z < 1) logical.z = 1; + if (logical.x < 0) logical.x = 0; + if (logical.y < 0) logical.y = 0; + if (logical.z < 0) logical.z = 0; } private: @@ -432,6 +477,9 @@ class Layer : public MoonModule { lengthType depth_ = 0; uint32_t elapsed_ = 0; char statusBuf_[20] = {}; // "999×999×999" fits; owned (setStatus borrows the pointer) + bool hasLive_ = false; // any enabled modifier animates per frame (gates the live pass) + uint8_t* liveScratch_ = nullptr; // snapshot for the live pass; allocated only when hasLive_ + size_t liveScratchBytes_ = 0; // Check if heap can afford an allocation (returns true if unlimited or enough budget) static bool canAllocate(size_t bytesNeeded) { diff --git a/src/light/light_types.h b/src/light/light_types.h index 5f2c093..ab20c3e 100644 --- a/src/light/light_types.h +++ b/src/light/light_types.h @@ -21,6 +21,32 @@ namespace mm { // size optimisation (it stores nrOfLightsType indices, not coordinates). using lengthType = int16_t; +// A 3D grid position / size. Modifiers fold one of these in place when building +// the Layer's mapping (each modifier is a coordinate transform — Multiply does +// `pos = pos % size`, a mirror folds an axis), so the per-axis `%` `/` `-` `+` +// operators below let the fold read like the geometry it expresses rather than +// three separate index lines. A struct, not a class: it's plain data on the cold +// build path (the hot render path stays on flat nrOfLightsType indices, never +// these). Operators are per-component (Hadamard), the convention for a grid/size +// vector — `a % b` is `{a.x%b.x, a.y%b.y, a.z%b.z}`. +struct Coord3D { + lengthType x = 0, y = 0, z = 0; + + Coord3D operator+(const Coord3D& o) const { return {static_cast(x + o.x), static_cast(y + o.y), static_cast(z + o.z)}; } + Coord3D operator-(const Coord3D& o) const { return {static_cast(x - o.x), static_cast(y - o.y), static_cast(z - o.z)}; } + Coord3D operator*(const Coord3D& o) const { return {static_cast(x * o.x), static_cast(y * o.y), static_cast(z * o.z)}; } + // Per-axis %/÷ guard against a zero extent (a degenerate axis stays put / 0) + // so a modifier can fold without pre-checking every axis for size 1 or 0. + Coord3D operator%(const Coord3D& o) const { return {modAxis(x, o.x), modAxis(y, o.y), modAxis(z, o.z)}; } + Coord3D operator/(const Coord3D& o) const { return {divAxis(x, o.x), divAxis(y, o.y), divAxis(z, o.z)}; } + bool operator==(const Coord3D& o) const { return x == o.x && y == o.y && z == o.z; } + bool operator!=(const Coord3D& o) const { return !(*this == o); } + +private: + static lengthType modAxis(lengthType a, lengthType m) { return m > 0 ? static_cast(a % m) : a; } + static lengthType divAxis(lengthType a, lengthType d) { return d > 0 ? static_cast(a / d) : a; } +}; + // Count of lights, and the index type MappingLUT stores. uint32_t with PSRAM // (10K+ lights, large installations), uint16_t without — the narrower index // keeps MappingLUT's CSR arrays half the size on no-PSRAM boards. Selected at diff --git a/src/light/modifiers/CheckerboardModifier.h b/src/light/modifiers/CheckerboardModifier.h index 1f28c0e..4be4709 100644 --- a/src/light/modifiers/CheckerboardModifier.h +++ b/src/light/modifiers/CheckerboardModifier.h @@ -5,18 +5,12 @@ namespace mm { // Masks the layer in a checkerboard pattern: lights in the "off" squares are -// dropped (map to no physical position), lights in the "on" squares pass through -// unchanged. `size` sets the square edge in lights; `invert` flips which squares -// are on. The logical box is unchanged (identity dimensions) — this is a mask, -// not a remap. -// -// Implemented by emitting outCount=0 for dropped lights, which Layer::rebuildLUT -// already records as "this logical light has no destination" (the same zero- -// destination path the sparse box→driver translation uses). No ModifierBase -// contract change. +// dropped (the physical light maps nowhere), lights in the "on" squares pass +// through unchanged. `size` sets the square edge in lights; `invert` flips which +// squares are on. A mask, not a remap — the logical box is unchanged. // // Prior art: MoonLight's Checkerboard modifier (M_MoonLight.h) drops lights by -// setting position.x = UINT16_MAX; outCount=0 is our equivalent. +// setting position to a sentinel; our fold returns false from modifyLogical. class CheckerboardModifier : public ModifierBase { public: const char* tags() const override { return "💫"; } // MoonLight origin @@ -24,39 +18,20 @@ class CheckerboardModifier : public ModifierBase { uint8_t size = 2; // checker square edge, in lights (≥1) bool invert = false; // flip which squares pass through - nrOfLightsType maxMultiplier() const override { return 1; } // 1:1 or 1:0, never fans out - void onBuildControls() override { controls_.addUint8("size", size, 1, 64); controls_.addBool("invert", invert); } - // Identity: a mask doesn't resize the logical box. - void logicalDimensions(lengthType physW, lengthType physH, lengthType physD, - lengthType& logW, lengthType& logH, lengthType& logD) const override { - logW = physW; - logH = physH; - logD = physD; - } + // A mask leaves the logical box unchanged (no modifyLogicalSize override). - void mapToPhysical(lengthType lx, lengthType ly, lengthType lz, - lengthType physW, lengthType physH, lengthType /*physD*/, - nrOfLightsType* outPhysicals, nrOfLightsType& outCount, - nrOfLightsType maxOut) const override { - outCount = 0; - if (maxOut == 0) return; + bool modifyLogical(Coord3D& pos) const override { const lengthType s = size ? size : 1; // Parity of the square this light sits in. Even parity = "on" square by - // default; `invert` swaps which parity passes. - // Parity is non-negative; compare as signed int so MSVC (/W4) doesn't - // flag a signed/unsigned mismatch against an unsigned literal. - const int parity = ((lx / s) + (ly / s) + (lz / s)) & 1; - const bool on = parity == (invert ? 1 : 0); - if (!on) return; // dropped — no physical destination (the mask) - outPhysicals[0] = static_cast(lz) * static_cast(physW) * static_cast(physH) + - static_cast(ly) * static_cast(physW) + - static_cast(lx); - outCount = 1; + // default; `invert` swaps which parity passes. Parity is non-negative; + // compare as signed int so MSVC (/W4) doesn't flag a signed/unsigned mismatch. + const int parity = ((pos.x / s) + (pos.y / s) + (pos.z / s)) & 1; + return parity == (invert ? 1 : 0); // false → dropped (the mask) } }; diff --git a/src/light/modifiers/ModifierBase.h b/src/light/modifiers/ModifierBase.h index 8e844da..b010b9e 100644 --- a/src/light/modifiers/ModifierBase.h +++ b/src/light/modifiers/ModifierBase.h @@ -8,46 +8,76 @@ namespace mm { class ModifierBase : public MoonModule { public: ModuleRole role() const override { return ModuleRole::Modifier; } - virtual bool isStatic() const { return true; } - // A DYNAMIC modifier (e.g. RandomMapModifier) overrides MoonModule::loop() to run a - // per-frame tick; Layer::loop() calls it for each enabled modifier child AFTER the - // effect pass, so the frame's buffer is fully written before a modifier acts. A - // static modifier (the default) inherits the base no-op loop and shapes the LUT only - // on a control change. No new hook needed — loop() already exists on MoonModule. - - // A modifier control change alters the LUT shape, so the owning Layer must rebuild - // its LUT — the pipeline-wide rebuild path. See MoonModule::onUpdate. (Even a - // future dynamic modifier is harmless to rebuild on a control change.) + // A modifier control change alters the mapping, so the owning Layer must rebuild + // it — the pipeline-wide rebuild path. See MoonModule::onUpdate. bool controlChangeTriggersBuildState(const char* /*controlName*/) const override { return true; } // Which axes the modifier can transform. Defaults to D3 — a modifier that - // touches the LUT is assumed to work in 3D unless it declares otherwise. + // touches the mapping is assumed to work in 3D unless it declares otherwise. // The UI uses this to render the 📏/🟦/🧊 chip so the user can see at a // glance whether a modifier will do anything along z. Distinct from // EffectBase::dimensions() (which controls Layer extrusion); here it is // purely an advisory chip, never read in the render path. virtual Dim dimensions() const { return Dim::D3; } - // Max physical destinations a single logical light fans out to. The Layer - // sizes its per-light scratch buffer to exactly this (heap, cold path), so - // there is no fixed ceiling — a modifier may declare any fan-out and the - // only limit is memory (an alloc failure degrades to the identity LUT). - // Returned as nrOfLightsType (not uint8_t) so large fan-outs (e.g. an 8×8×8 - // multiply = 512) are expressible without overflow. - virtual nrOfLightsType maxMultiplier() const { return 8; } - - // Compute logical dimensions given physical dimensions - virtual void logicalDimensions(lengthType physW, lengthType physH, lengthType physD, - lengthType& logW, lengthType& logH, lengthType& logD) const = 0; - - // Map a logical coordinate to physical positions. - // outPhysicals: caller-provided array sized kMaxFanout; write at most maxOut. - // outCount: set to number of physical positions written - virtual void mapToPhysical(lengthType lx, lengthType ly, lengthType lz, - lengthType physW, lengthType physH, lengthType physD, - nrOfLightsType* outPhysicals, nrOfLightsType& outCount, - nrOfLightsType maxOut) const = 0; + // --- Fold interface (composable modifiers) --------------------------------- + // A modifier is a coordinate transform. The Layer builds its mapping by walking + // the PHYSICAL lights and folding each through every enabled modifier in child + // order — the result is the chain M₁∘M₂∘…∘Mₙ collapsed into one mapping, so + // ordering several modifiers on a Layer "just works" (Region then Multiply- + // mirror then Rotate). Three tiers, each a no-op by default so a modifier + // implements only the ones it needs. + // + // Prior art: the two building blocks are the textbook image-warping pattern — + // bake a coordinate transform into a precomputed spatial LUT (geometry is + // static, so compute the table once and only read it per frame), and build that + // table by BACKWARD mapping (walk the destinations, find each one's source) so + // no output pixel is ever left unmapped. What is specific here — and credited to + // MoonLight (M_MoonLight.h), the prior engine this is modelled on — is collapsing + // a whole *chain* of discrete pixel folds into ONE index table. A PC node graph + // (TouchDesigner, shader graphs) gives each node its own frame buffer; an ESP32 + // can't spare a buffer per modifier, so the chain is folded into a single LUT + // and the hot path stays one gather. That MCU-budget synthesis is the borrowed + // idea, written fresh against our MappingLUT here. + + // STATIC, build-time, once per rebuild in child order: fold the logical box. + // Multiply divides it, Region crops it, a mask leaves it. `size` is the running + // logical box (starts at the physical box; each modifier reshapes it for the + // next). A modifier that needs the box in its per-light fold STASHES it here + // (the MoonLight `modifierSize` pattern) — so modifyLogical reads its own stage's + // box from itself, and the Layer needs no per-stage box array. Non-const: the + // stash mutates the modifier. + virtual void modifyLogicalSize(Coord3D& /*size*/) {} + + // STATIC, build-time, per physical light in child order: fold a coordinate into + // this stage's logical space (in place). The coord enters in the box this + // modifier saw at modifyLogicalSize time and leaves in the box it produced, so + // the next modifier in the chain continues from here. Return false to REJECT — + // the coordinate has no logical source (a mask drops it, a region light falls + // outside the crop). A bool, not a sentinel coord: a later modifier's `% size` + // can't alias a sentinel back into range. The modifier reads any box it needs + // from its own stash (see modifyLogicalSize). + virtual bool modifyLogical(Coord3D& /*pos*/) const { return true; } + + // DYNAMIC, per-frame at render time: remap a coordinate without rebuilding the + // mapping (smooth rotation/scroll). The Layer runs this pass ONLY when some + // enabled modifier overrides it (hasModifyLive()), so a static-only chain pays + // nothing per frame — the render path stays at full speed. `logical` is the box. + virtual void modifyLive(Coord3D& /*pos*/, const Coord3D& /*logical*/) const {} + + // True iff this modifier does per-frame work (overrides modifyLive). The Layer + // sums this across enabled modifiers at build time to gate the per-frame pass; + // a modifier that animates returns true so the seam runs only when needed. + virtual bool hasModifyLive() const { return false; } + + // A modifier whose mapping changes on a timer (RandomMap reshuffles on a beat) + // sets a flag in its loop(); the Layer polls this once per frame across all its + // enabled modifiers and rebuilds the mapping ONCE if any returns true — so + // several dynamic modifiers ticking together coalesce to a single rebuild rather + // than each re-entering onBuildState(). Returns true at most once per change, + // clearing the flag. Default false: a static modifier never asks for a rebuild. + virtual bool consumeNeedsRebuild() { return false; } }; } // namespace mm diff --git a/src/light/modifiers/MultiplyModifier.h b/src/light/modifiers/MultiplyModifier.h index 08db3c7..7bf2eea 100644 --- a/src/light/modifiers/MultiplyModifier.h +++ b/src/light/modifiers/MultiplyModifier.h @@ -6,13 +6,15 @@ namespace mm { // Tiles the logical image across the physical box `multiply` times per axis, // optionally mirroring alternate tiles. The logical box is the physical box -// divided by the per-axis multiplier; each logical light fans out to one -// physical position per tile. With a multiplier of 2 and mirror on, an axis -// folds in half — the classic kaleidoscope mirror (this subsumes the old -// MirrorModifier: mirror = multiply 2 + mirror true). +// divided by the per-axis multiplier. Under the fold build the fan-out is free: +// every physical light folds (`pos % logicalSize`) onto its logical cell, so the +// N physical lights of N tiles all land on the same logical light — N:1 emerges, +// no fan-out list. With multiply 2 + mirror on, an axis folds in half — the +// classic kaleidoscope mirror (this subsumes a standalone Mirror: it's just +// multiply 2 + mirror true). // // Prior art: MoonLight's Multiply modifier (M_MoonLight.h) — same tile+mirror -// shape (`position % modifierSize`, odd tiles reflected). We expose per-axis +// fold (`position % modifierSize`, odd tiles reflected). We expose per-axis // mirror bools (3) instead of MoonLight's single mirror flag, and per-axis // multipliers, so X/Y/Z can fold and tile independently. class MultiplyModifier : public ModifierBase { @@ -30,32 +32,9 @@ class MultiplyModifier : public ModifierBase { bool mirrorY = true; bool mirrorZ = true; - // Upper bound on fan-out = product of the raw control multipliers. Computed - // without the grid extents (not known here), so it's an over-estimate when a - // multiplier exceeds its axis (the effective multiplier clamps to the extent - // in mapToPhysical). The Layer sizes its per-light scratch buffer to this, so - // over-estimating is safe (a few unused slots); under-estimating would - // truncate. No fixed cap — limited only by memory. e.g. 8×8 = 64. - nrOfLightsType maxMultiplier() const override { - const uint8_t cx = multiplyX ? multiplyX : 1; - const uint8_t cy = multiplyY ? multiplyY : 1; - const uint8_t cz = multiplyZ ? multiplyZ : 1; - // Compute in uint64 and saturate to the return type's max. The controls - // cap each axis at 64, so the product can reach 64³ = 262144 — which - // overflows nrOfLightsType (uint16 on no-PSRAM). A wrapped value here - // would defeat the uint64 maxDest math in Layer::rebuildLUT (it'd be fed - // an already-wrapped, possibly-0 multiplier → empty LUT → black display). - // The Layer clamps maxDest to driverCount×2 anyway, so saturating the - // upper bound only over-allocates the scratch buffer slightly. - const uint64_t product = static_cast(cx) * cy * cz; - constexpr uint64_t kTypeMax = static_cast(-1); - return static_cast(product < kTypeMax ? product : kTypeMax); - } - void onBuildControls() override { - // 1–64 tiles per axis. The fan-out (product) sizes the LUT scratch buffer - // dynamically, so the cap is generous, not a buffer constraint — more - // tiles than the grid has pixels just yields 1-pixel tiles. + // 1–64 tiles per axis. More tiles than the grid has pixels just yields + // 1-pixel tiles (the effective multiplier clamps to the axis extent). controls_.addUint8("multiplyX", multiplyX, 1, 64); controls_.addUint8("multiplyY", multiplyY, 1, 64); controls_.addUint8("multiplyZ", multiplyZ, 1, 64); @@ -64,64 +43,46 @@ class MultiplyModifier : public ModifierBase { controls_.addBool("mirrorZ", mirrorZ); } - void logicalDimensions(lengthType physW, lengthType physH, lengthType physD, - lengthType& logW, lengthType& logH, lengthType& logD) const override { - // Logical box is the physical box divided by the EFFECTIVE multiplier - // (clamped to the axis extent — see eff()). On a 2D grid (physD=1) any - // multiplyZ clamps to 1, so logD stays 1 and Z multiplication is a no-op + void modifyLogicalSize(Coord3D& size) override { + // Logical box is the incoming box divided by the EFFECTIVE multiplier + // (clamped to the axis extent — see eff()). On a 2D grid (size.z=1) any + // multiplyZ clamps to 1, so depth stays 1 and Z multiplication is a no-op // — you can't tile an axis more times than it has pixels. - logW = physW / eff(multiplyX, physW); - logH = physH / eff(multiplyY, physH); - logD = physD / eff(multiplyZ, physD); + size.x /= eff(multiplyX, size.x); + size.y /= eff(multiplyY, size.y); + size.z /= eff(multiplyZ, size.z); + tile_ = size; // stash the output tile size for the fold } - void mapToPhysical(lengthType lx, lengthType ly, lengthType lz, - lengthType physW, lengthType physH, lengthType physD, - nrOfLightsType* outPhysicals, nrOfLightsType& outCount, - nrOfLightsType maxOut) const override { - outCount = 0; - const lengthType mX = eff(multiplyX, physW); - const lengthType mY = eff(multiplyY, physH); - const lengthType mZ = eff(multiplyZ, physD); - const lengthType tileW = physW / mX; - const lengthType tileH = physH / mY; - const lengthType tileD = physD / mZ; - - // One physical position per tile on each axis. For a mirror-enabled axis, - // odd tiles reflect within their tile (MoonLight's `size-1-pos`). - for (lengthType tz = 0; tz < mZ; tz++) { - const lengthType pz = tileOrigin(tz, tileD) + axisOffset(lz, tileD, tz, mirrorZ); - for (lengthType ty = 0; ty < mY; ty++) { - const lengthType py = tileOrigin(ty, tileH) + axisOffset(ly, tileH, ty, mirrorY); - for (lengthType tx = 0; tx < mX; tx++) { - if (outCount >= maxOut) return; - const lengthType px = tileOrigin(tx, tileW) + axisOffset(lx, tileW, tx, mirrorX); - outPhysicals[outCount++] = - static_cast(pz) * static_cast(physW) * static_cast(physH) + - static_cast(py) * static_cast(physW) + - static_cast(px); - } - } - } + bool modifyLogical(Coord3D& pos) const override { + // Fold a coord into its tile: the tile index decides whether to reflect + // (odd tile, mirror on), then wrap into the tile. Reads the stashed tile size. + pos.x = foldAxis(pos.x, tile_.x, mirrorX); + pos.y = foldAxis(pos.y, tile_.y, mirrorY); + pos.z = foldAxis(pos.z, tile_.z, mirrorZ); + return true; // multiply never rejects — every light has a tile } private: + Coord3D tile_; // output tile size, stashed in modifyLogicalSize for the fold + // Effective multiplier for an axis: the control value clamped to [1, extent]. // ≥1 avoids divide-by-zero; ≤extent because tiling more times than the axis - // has pixels is meaningless (and would blank the layer via logD=0). So a - // multiplyZ on a depth-1 (2D) layout clamps to 1 — no effect, as expected. + // has pixels is meaningless (and would blank the layer). So a multiplyZ on a + // depth-1 (2D) layout clamps to 1 — no effect, as expected. static lengthType eff(uint8_t mult, lengthType extent) { lengthType m = mult ? mult : 1; if (extent > 0 && m > extent) m = extent; return m; } - static lengthType tileOrigin(uint8_t tile, lengthType tileSize) { - return static_cast(tile) * tileSize; - } - // Offset within a tile: identity, or reflected for odd tiles when mirroring. - static lengthType axisOffset(lengthType l, lengthType tileSize, uint8_t tile, bool mirror) { - return (mirror && (tile & 1)) ? static_cast(tileSize - 1 - l) : l; + // Fold a physical coordinate `p` into a `logical`-sized tile, reflecting odd + // tiles when mirroring. logical==0 (degenerate axis) passes through unchanged. + static lengthType foldAxis(lengthType p, lengthType logical, bool mirror) { + if (logical <= 0) return p; + const lengthType tile = static_cast(p / logical); + const lengthType within = static_cast(p % logical); + return (mirror && (tile & 1)) ? static_cast(logical - 1 - within) : within; } }; diff --git a/src/light/modifiers/RandomMapModifier.h b/src/light/modifiers/RandomMapModifier.h index cf5d59a..b8e67bb 100644 --- a/src/light/modifiers/RandomMapModifier.h +++ b/src/light/modifiers/RandomMapModifier.h @@ -1,6 +1,6 @@ #pragma once -#include "light/layers/Layer.h" // ModifierBase + Layer (we call layer->onBuildState() on a beat) +#include "light/modifiers/ModifierBase.h" #include "platform/platform.h" // alloc / free / millis #include @@ -8,21 +8,21 @@ namespace mm { // Randomly remaps every light to another light — a true 1:1 permutation (every light -// goes somewhere, no gaps or duplicates) — and reshuffles on a `bpm` timer. The first -// DYNAMIC modifier: a static modifier shapes the LUT once, this one re-shapes it on a -// beat. The permutation rides the existing LUT (mapToPhysical emits one destination per -// light, the same outCount=1 shape CheckerboardModifier uses); the only new machinery -// is the bpm tick (loop()) that, on a beat boundary, reshuffles and asks the Layer to -// rebuild its LUT — the same rebuild path a control change takes, scoped to one Layer. +// goes somewhere, no gaps or duplicates) — and reshuffles on a `bpm` timer. A static +// fold whose mapping changes on a beat: modifyLogical applies the permutation (the box +// is unchanged, each light maps to exactly one other), and the bpm tick (loop()) bumps +// the generation and rebuilds the Layer's mapping on a beat boundary — the same rebuild +// path a control change takes, scoped to one Layer. (Not a per-frame modifyLive: a +// permutation is a discrete reshuffle, not smooth motion, so a beat-gated rebuild is the +// right cost, not a per-frame remap.) // // `bpm` = reshuffles per minute (0–60, default 6 → one scramble every ~10 s; 60 → one -// per second, the cap — faster would be a strobe and a per-frame rebuild is too costly). -// bpm 0 = frozen: keep the current permutation, never reshuffle (a fixed random remap). +// per second, the cap). bpm 0 = frozen: keep the current permutation, never reshuffle. // -// Cost: each beat re-runs the Layer's LUT rebuild on the render thread (a transient -// one-frame hitch, like a device scan), bounded by bpm≤60. The permutation buffer is a -// member sized to the light count, (re)allocated only on a grid resize — never per -// frame. An alloc failure degrades to identity passthrough (no remap), like the LUT. +// Cost: each beat re-runs the Layer's mapping rebuild on the render thread (a transient +// one-frame hitch), bounded by bpm≤60. The permutation buffer is a member sized to the +// box, (re)allocated only on a grid resize — never per frame. An alloc failure degrades +// to identity passthrough. // // Sparse layouts: the permutation is over box indices, so a real light can map to a // non-light cell (dropped → dark). Acceptable for v1. @@ -36,79 +36,68 @@ class RandomMapModifier : public ModifierBase { controls_.addUint8("bpm", bpm, 0, 60); } - // A remap doesn't resize the logical box — logical dims == physical dims (identity), - // like CheckerboardModifier. Each logical light maps to exactly one physical light. - void logicalDimensions(lengthType physW, lengthType physH, lengthType physD, - lengthType& logW, lengthType& logH, lengthType& logD) const override { - logW = physW; - logH = physH; - logD = physD; - } - - nrOfLightsType maxMultiplier() const override { return 1; } // 1:1, never fans out + // A remap leaves the logical box unchanged (no modifyLogicalSize override). - // Emit the permuted destination for this light. mapToPhysical is called once per box - // during the Layer's LUT rebuild; the first call after a resize or a beat (generation - // bump) (re)builds the permutation, the rest just read it. const, so the permutation - // buffers are mutable. - void mapToPhysical(lengthType lx, lengthType ly, lengthType lz, - lengthType physW, lengthType physH, lengthType physD, - nrOfLightsType* outPhysicals, nrOfLightsType& outCount, - nrOfLightsType maxOut) const override { - outCount = 0; - if (maxOut == 0) return; + // A remap leaves the box unchanged but needs it for the permutation — stash it. + void modifyLogicalSize(Coord3D& size) override { box_ = size; } + // Apply the permutation: fold the coord to a box index, look up its permuted index, + // unflatten back to a coord. The first call after a resize/beat (re)builds the + // permutation, the rest read it. const, so the permutation buffers are mutable. + bool modifyLogical(Coord3D& pos) const override { const nrOfLightsType boxCount = - static_cast(physW) * static_cast(physH) * - static_cast(physD); + static_cast(box_.x) * static_cast(box_.y) * + static_cast(box_.z); ensurePermutation(boxCount); + const lengthType w = box_.x > 0 ? box_.x : 1; + const lengthType h = box_.y > 0 ? box_.y : 1; const nrOfLightsType idx = - static_cast(lz) * static_cast(physW) * static_cast(physH) + - static_cast(ly) * static_cast(physW) + - static_cast(lx); - - // If the permutation isn't available (alloc failed or empty grid), pass through - // unchanged — identity remap, never a crash or a dropped frame. - outPhysicals[0] = (perm_ && idx < permCount_) ? perm_[idx] : idx; - outCount = 1; + static_cast(pos.z) * static_cast(w) * static_cast(h) + + static_cast(pos.y) * static_cast(w) + + static_cast(pos.x); + + // Permuted index, or identity if the permutation is unavailable (OOM/empty). + const nrOfLightsType mapped = (perm_ && idx < permCount_) ? perm_[idx] : idx; + // Unflatten back to a coordinate in the box. + pos.x = static_cast(mapped % w); + pos.y = static_cast((mapped / w) % h); + pos.z = static_cast(mapped / (static_cast(w) * h)); + return true; // a permutation never rejects — every light maps somewhere } // Dynamic-modifier tick: accumulate the bpm timer; on each beat boundary bump the - // generation (so the next rebuild reshuffles) and trigger the Layer's LUT rebuild. - // bpm 0 freezes — no accumulation, no reshuffle (the permutation stays put). - // Overrides MoonModule::loop(); Layer::loop() invokes it per enabled modifier child. + // generation (so the next rebuild reshuffles) and trigger the Layer's rebuild. + // bpm 0 freezes. Layer::loop() invokes this per enabled modifier child; it sets a + // dirty flag the Layer coalesces into one rebuild (see Layer::loop()). void loop() override { - Layer* lyr = static_cast(parent()); - if (!lyr) return; - const uint32_t now = lyr->elapsed(); // same clock the effects use this frame + const uint32_t now = platform::millis(); if (lastElapsed_ == 0) lastElapsed_ = now; // first tick: no dt jump const uint32_t dt = now - lastElapsed_; lastElapsed_ = now; if (bpm == 0) return; // frozen — keep the current permutation // Accumulate the raw (dt * bpm) product; divide only at the read site — the same // integer-accumulator trick the effects use so a sub-ms dt doesn't round to zero. - // One beat = 60000/bpm ms, i.e. phaseNum_ crossing a multiple of 60000. phaseNum_ += static_cast(dt) * bpm; const uint64_t beat = phaseNum_ / 60000u; if (beat != lastBeat_) { lastBeat_ = beat; - reshuffle(); // ask the next rebuild to produce a fresh permutation - // The rebuild is what actually applies the reshuffle (re-runs mapToPhysical - // off the new generation); reshuffle() alone only bumps the counter, so - // without this the remap would never visibly change. This rebuild DOES - // alloc — but it is beat-gated (≤ once/sec at bpm 60, default every 10s), - // runs here AFTER the effect pass (not mid-render), and the permutation - // buffer is reused across beats (realloc only on a grid resize). That is - // the accepted, bounded cost — a transient one-frame hitch like a `scan`, - // not a per-tick hot-path alloc. (Removing it to "defer the rebuild" would - // break the feature: nothing else triggers the LUT rebuild on a beat.) - lyr->onBuildState(); + reshuffle(); // bump the generation; the Layer's rebuild applies it + needsRebuild_ = true; // Layer::loop() reads + clears this, one rebuild/frame } } - // Bump the generation so the next mapToPhysical pass reshuffles to a new permutation. - // loop() calls this on a beat; exposed so a test can drive a reshuffle without a Layer. + // True iff a beat asked for a fresh permutation since the last rebuild. The Layer + // polls this across its enabled modifiers and rebuilds once if any is set, so several + // dynamic modifiers ticking in one frame coalesce to a single rebuild. + bool consumeNeedsRebuild() override { + const bool r = needsRebuild_; + needsRebuild_ = false; + return r; + } + + // Bump the generation so the next rebuild reshuffles. Exposed so a test can drive a + // reshuffle without a Layer. void reshuffle() { generation_++; } private: @@ -158,6 +147,7 @@ class RandomMapModifier : public ModifierBase { } // Mutable: mapToPhysical is const but lazily (re)builds the permutation. + Coord3D box_; // stashed in modifyLogicalSize (the box the permutation is over) mutable nrOfLightsType* perm_ = nullptr; mutable nrOfLightsType permCount_ = 0; mutable uint32_t rngState_ = 0xBADF00Du; @@ -167,6 +157,7 @@ class RandomMapModifier : public ModifierBase { uint64_t phaseNum_ = 0; // dt*bpm accumulator (numerator; one beat per 60000) uint64_t lastBeat_ = 0; uint32_t lastElapsed_ = 0; + bool needsRebuild_ = false; // set on a beat, consumed by Layer::loop (coalesced rebuild) }; } // namespace mm diff --git a/src/light/modifiers/RegionModifier.h b/src/light/modifiers/RegionModifier.h index df88bf1..4609729 100644 --- a/src/light/modifiers/RegionModifier.h +++ b/src/light/modifiers/RegionModifier.h @@ -11,21 +11,26 @@ namespace mm { // stays the left half whether the panel is 64 or 128 wide. // // It is a region *crop*, the textbook crop/region node of any compositor: it -// resizes the logical box to the region (logicalDimensions) and maps each -// region-local cell 1:1 to its box cell at the region's start offset -// (mapToPhysical). Because the logical box is already the region size, every -// region cell is in-bounds — the "drop outside" is achieved by the smaller box, -// exactly as a Mirror shrinks the box to the half it folds. Never fans out -// (maxMultiplier == 1), same 1:1 family as CheckerboardModifier. +// shrinks the logical box to the region (modifyLogicalSize) and folds each +// physical light into region-local space by subtracting the start offset, then +// REJECTS any physical light that lands outside the region (modifyLogical returns +// false) — the "drop outside" that makes everything beyond the region dark. A 1:1 +// fold, same family as CheckerboardModifier. // // Rounding rule (spec: docs/moonmodules/light/RegionModifier.md): HALF-OPEN // [startPixel, endPixel). start% floors to the lower pixel; end% ceils to an // EXCLUSIVE pixel. This makes abutting regions tile exactly — a 0..50 and a // 50..100 layer split a 128-wide axis into 0..63 and 64..127 with no overlap and -// no gap. Clamped so the region is always ≥1 pixel and never runs off the box. -// (start 33 / end 66 on a 4-wide axis → floor(1.32)=1 .. ceil(2.64)=3 → pixels -// 1..2, width 2; end 100 on a W-wide axis → ceil(W)=W → full width.) Negative / -// >100 percentages are legal on the wire (Int16) but clamp to the box here. +// no gap. (start 33 / end 66 on a 4-wide axis → floor(1.32)=1 .. ceil(2.64)=3 → +// pixels 1..2, width 2; end 100 on a W-wide axis → ceil(W)=W → full width.) +// +// Off-screen windows. start/end percentages may go **negative or past 100** (Int16 +// on the wire, default UI range −100..200) to slide the window partly or fully out +// of the visible box. The logical box is the FULL window span (endPixel−startPixel), +// so the effect renders at a consistent scale; physical lights outside the window +// are dropped, and window cells with no physical light under them (the off-screen +// part) are simply dark. A window entirely off-box renders nothing — the layer goes +// dark, which is how you move an effect completely out of view. // // Fast path: the cheapest carve is *no modifier* — then Layer::rebuildLUT takes // its identity/memcpy path with zero carving cost. Adding a full-region (0/100) @@ -35,8 +40,6 @@ class RegionModifier : public ModifierBase { lengthType startX = 0, startY = 0, startZ = 0; lengthType endX = 100, endY = 100, endZ = 100; - nrOfLightsType maxMultiplier() const override { return 1; } // 1:1 inside, never fans out - void onBuildControls() override { // Int16 so negative / >100 percentages round-trip; the carve math clamps. controls_.addInt16("startX", startX); @@ -47,59 +50,63 @@ class RegionModifier : public ModifierBase { controls_.addInt16("endZ", endZ); } - void logicalDimensions(lengthType physW, lengthType physH, lengthType physD, - lengthType& logW, lengthType& logH, lengthType& logD) const override { - logW = axisCount(startX, endX, physW); - logH = axisCount(startY, endY, physH); - logD = axisCount(startZ, endZ, physD); + void modifyLogicalSize(Coord3D& size) override { + // Window edges in pixels — NOT clamped to the box, so the window can sit + // partly or fully outside it. The logical box is the full window span (the + // effect renders at a fixed scale regardless of how far off-screen it sits — + // moving start+end together slides it without resizing, like an OS window). + start_ = {floorPx(startX, size.x), floorPx(startY, size.y), floorPx(startZ, size.z)}; + const Coord3D end{ceilPx(endX, size.x), ceilPx(endY, size.y), ceilPx(endZ, size.z)}; + // Window span = end − start, floored to ≥1 on a non-empty axis so the effect + // always has a box to render into (a fully off-screen window still renders; it + // just maps no lights to the screen). A genuinely 0-extent axis stays 0. + region_ = {span(start_.x, end.x, size.x), span(start_.y, end.y, size.y), + span(start_.z, end.z, size.z)}; + size = region_; } - void mapToPhysical(lengthType lx, lengthType ly, lengthType lz, - lengthType physW, lengthType physH, lengthType physD, - nrOfLightsType* outPhysicals, nrOfLightsType& outCount, - nrOfLightsType maxOut) const override { - outCount = 0; - if (maxOut == 0) return; - // Region-local → box coordinate: add each axis's start-pixel offset. lx/ly/lz - // are already bounded by the region size logicalDimensions reported, so the - // result is always in-box; no per-cell drop needed. - const lengthType bx = lx + axisStart(startX, physW); - const lengthType by = ly + axisStart(startY, physH); - const lengthType bz = lz + axisStart(startZ, physD); - outPhysicals[0] = static_cast(bz) * static_cast(physW) * static_cast(physH) + - static_cast(by) * static_cast(physW) + - static_cast(bx); - outCount = 1; + bool modifyLogical(Coord3D& pos) const override { + // Fold a physical light into window-local space. A light whose window-local + // coord lands outside the window is dropped; window cells with no physical + // light under them (the off-screen part) get no source and stay dark. + pos = pos - start_; + return pos.x >= 0 && pos.x < region_.x && + pos.y >= 0 && pos.y < region_.y && + pos.z >= 0 && pos.z < region_.z; } private: - // First pixel of the region on an axis: floor(start% · extent), clamped to - // [0, extent-1]. Shared by logicalDimensions (via axisCount) and mapToPhysical - // so the two can't drift. - static lengthType axisStart(lengthType startPct, lengthType extent) { + Coord3D start_; // window start pixel (may be negative), stashed for the fold + Coord3D region_; // window size (logical box), stashed for the bound check + + // Lower window edge: floor(start% · extent). Unclamped — may be negative. + // Floored toward −∞ (not truncated) so a negative percentage rounds down + // consistently with the positive case. + static lengthType floorPx(lengthType pct, lengthType extent) { + if (extent <= 0) return 0; + long num = static_cast(pct) * extent; + long q = num / 100; + if (num % 100 != 0 && num < 0) q -= 1; // floor toward −∞ for negatives + return static_cast(q); + } + + // Upper window edge (EXCLUSIVE): ceil(end% · extent). Unclamped — may exceed + // extent. Ceiled toward +∞. + static lengthType ceilPx(lengthType pct, lengthType extent) { if (extent <= 0) return 0; - long p = (static_cast(startPct) * extent) / 100; // floor for non-negative; clamps below anyway - if (p < 0) p = 0; - if (p > extent - 1) p = extent - 1; - return static_cast(p); + long num = static_cast(pct) * extent; + long q = num / 100; + if (num % 100 != 0 && num > 0) q += 1; // ceil toward +∞ for positives + return static_cast(q); } - // Region size on an axis (half-open): count = endPixel - startPixel, where - // endPixel is ceil(end% · extent) treated as EXCLUSIVE, clamped to - // [startPixel+1, extent] so the region is ≥1 pixel and stays in the box. - // Spec example: start 33 / end 66 on extent 4 → s=floor(1.32)=1, - // endExcl=ceil(2.64)=3 → count = 3-1 = 2 (pixels 1,2). Default end 100 on - // extent W → ceil(W)=W → count = W (full width). 0..50 then 50..100 on 128 → - // 0..64 and 64..128 exclusive → 64 + 64, tiling exactly. - static lengthType axisCount(lengthType startPct, lengthType endPct, lengthType extent) { + // Window span on an axis = end − start, floored to ≥1 on a non-empty axis so the + // effect always has a box to render into. A 0-extent axis stays 0. (0..50 then + // 50..100 on 128 → 64 + 64, tiling; −50..50 on 100 → span 100, slid half off.) + static lengthType span(lengthType startPx, lengthType endPx, lengthType extent) { if (extent <= 0) return 0; - const lengthType s = axisStart(startPct, extent); - // Ceiling division of (endPct * extent) / 100, for non-negative endPct. - long num = static_cast(endPct) * extent; - long e = num <= 0 ? 0 : (num + 99) / 100; // ceil, EXCLUSIVE end pixel - if (e < s + 1) e = s + 1; // ≥1-pixel region - if (e > extent) e = extent; // never past the box - return static_cast(e - s); + lengthType s = static_cast(endPx - startPx); + return s >= 1 ? s : 1; } }; diff --git a/src/light/modifiers/RotateModifier.h b/src/light/modifiers/RotateModifier.h index 86495b6..c6f2a60 100644 --- a/src/light/modifiers/RotateModifier.h +++ b/src/light/modifiers/RotateModifier.h @@ -1,117 +1,87 @@ #pragma once -#include "light/layers/Layer.h" // ModifierBase + Layer (we call layer->onBuildState() on a step) +#include "light/modifiers/ModifierBase.h" #include "core/color.h" // sin8, cos8 — integer trig -#include "platform/platform.h" #include namespace mm { -// Rotates the 2D image around its centre, turning continuously over time. A DYNAMIC -// modifier (like RandomMapModifier): the rotation is a coordinate remap baked into the -// Layer's LUT, and the angle advances on a `speed` timer. The LUT is rebuilt only when -// the angle crosses to a new integer step (256 steps per turn), NOT every frame — so a -// slow rotation rebuilds rarely and a fast one more often, always bounded, never a -// per-frame alloc. Integer-only: the inverse rotation uses the sin8/cos8 LUT, nearest- -// neighbour sampling (no float, no bilinear). +// Rotates the 2D image around its centre, turning continuously over time. The one +// DYNAMIC modifier in the set: it overrides modifyLive(), so the Layer re-applies it +// every frame (a smooth turn, not a stepped LUT rebuild). A static-only chain pays +// nothing — the per-frame pass runs only because this modifier reports hasModifyLive(). // -// Each destination light at (dx,dy) from centre samples the SOURCE at the inverse -// rotation: sx = dx·cosθ + dy·sinθ, sy = -dx·sinθ + dy·cosθ. cos8/sin8 return 0..255 -// centred at 128, so (val-128)/128 is the unit component; the >>7 divides by 128. A -// source outside the grid is dropped (outCount=0 → that light goes dark at this angle). +// modifyLive is the **backward-mapping** seam: for each DESTINATION logical cell it +// computes the SOURCE cell to gather from, so no destination is ever left unfilled (the +// textbook reason image warping samples backward — Forward-and-Backward Mapping, the +// classic CV result). The source is the inverse rotation R(-θ) of the destination. // -// Prior art: MoonLight M_MoonLight.h Rotate/PinWheel (modifyXYZ per-light transform). -// Our version carries the transform in the LUT instead, reusing the dynamic-modifier -// loop() hook + the existing rebuild path — no Layer::render coupling, no dynamic_cast. +// This modifier is also the codebase's **transform-matrix reference**. Rotation is the +// canonical affine transform, so unlike the % / mask folds (Multiply, Checkerboard, +// Region — non-affine, expressed as direct coordinate folds), it's written as an explicit +// 2×2 rotation matrix R(-θ) = [[c, s], [-s, c]] applied to the centred coordinate. The +// matrix entries are integer fixed-point (cos8/sin8 → 0..255 centred at 128, so c=cos8-128 +// is the signed unit component scaled by 128; the >>7 divides back out). A future affine +// "Transform" modifier (translate+scale+rotate+shear in one) would compose its matrix the +// same way and apply it here — the fold interface hosts a matrix-backed modifier with no +// change. Non-affine modifiers can't use a matrix (a mask is a predicate, a tile is modulo +// — neither is a linear map), which is why only this one is matrix-shaped. +// +// Prior art: MoonLight M_MoonLight.h Rotate (modifyXYZ per-frame transform). Same per-frame +// coordinate remap; we name the hook modifyLive and carry an explicit matrix. class RotateModifier : public ModifierBase { public: Dim dimensions() const override { return Dim::D2; } // 2D rotation (advisory chip) + bool hasModifyLive() const override { return true; } // animates every frame - uint8_t speed = 1; // rotation speed, 1..255 (turns faster as it rises); affects how - // many angle-steps pass per second (and so how often the LUT rebuilds) + uint8_t speed = 1; // rotation speed, 1..255 (turns faster as it rises) void onBuildControls() override { controls_.addUint8("speed", speed, 1, 255); } - nrOfLightsType maxMultiplier() const override { return 1; } // 1:1 or 1:0, never fans out - - // Identity dimensions — rotation doesn't resize the box, it remaps within it. - void logicalDimensions(lengthType physW, lengthType physH, lengthType physD, - lengthType& logW, lengthType& logH, lengthType& logD) const override { - logW = physW; - logH = physH; - logD = physD; - } - - // Map a destination light to its rotated SOURCE light. Inverse rotation by the - // current angle (angle_), integer sin8/cos8. 2D: z passes through unchanged. - void mapToPhysical(lengthType lx, lengthType ly, lengthType lz, - lengthType physW, lengthType physH, lengthType /*physD*/, - nrOfLightsType* outPhysicals, nrOfLightsType& outCount, - nrOfLightsType maxOut) const override { - outCount = 0; - if (maxOut == 0) return; - - // Centre of the grid (in half-units to handle even widths: use 2× coords). - const int32_t cx2 = physW - 1; // 2*centreX - const int32_t cy2 = physH - 1; // 2*centreY - const int32_t dx2 = 2 * static_cast(lx) - cx2; // 2*(x-centre) - const int32_t dy2 = 2 * static_cast(ly) - cy2; - - // Signed cos/sin in [-128,127]. Inverse rotation: source = R(-θ)·dest. + // Per-frame backward map: a destination logical cell `pos` is replaced by the + // SOURCE cell it samples — the inverse rotation R(-θ) about the box centre. + // `logical` is the box. Out-of-box sources stay out-of-box, so the Layer's live + // pass leaves that destination dark (nothing to gather) — a clean edge, no wrap. + void modifyLive(Coord3D& pos, const Coord3D& logical) const override { + // Centre in half-units (×2) so an even-width box rotates about its true centre. + const int32_t cx2 = logical.x - 1; // 2·centreX + const int32_t cy2 = logical.y - 1; // 2·centreY + const int32_t dx2 = 2 * static_cast(pos.x) - cx2; // 2·(x − centre) + const int32_t dy2 = 2 * static_cast(pos.y) - cy2; + + // R(-θ) = [[ c, s], + // [-s, c]] with c = cos θ, s = sin θ in signed fixed-point /128. + // source = R(-θ) · dest. cos8/sin8 are 0..255 centred at 128. const int32_t c = static_cast(cos8(angle_)) - 128; const int32_t s = static_cast(sin8(angle_)) - 128; - const int32_t sx2 = (dx2 * c + dy2 * s) >> 7; // ÷128, back to 2×-unit space - const int32_t sy2 = (-dx2 * s + dy2 * c) >> 7; - - // Back to grid coordinates (undo the 2× and the centre shift), rounding to nearest. - const int32_t sx = (sx2 + cx2 + 1) >> 1; - const int32_t sy = (sy2 + cy2 + 1) >> 1; + const int32_t sx2 = ( dx2 * c + dy2 * s) >> 7; // row 0 of the matrix · dest + const int32_t sy2 = (-dx2 * s + dy2 * c) >> 7; // row 1 of the matrix · dest - if (sx < 0 || sx >= physW || sy < 0 || sy >= physH) return; // off-grid → dropped - outPhysicals[0] = - static_cast(lz) * static_cast(physW) * static_cast(physH) + - static_cast(sy) * static_cast(physW) + - static_cast(sx); - outCount = 1; + // Undo the ×2 and centre shift, rounding to nearest. + pos.x = static_cast((sx2 + cx2 + 1) >> 1); + pos.y = static_cast((sy2 + cy2 + 1) >> 1); + // z passes through (2D rotation). } - // Dynamic tick: advance the angle on the timer; when it crosses to a new integer - // step (1/256 of a turn), rebuild the LUT so the image rotates. Stepped, not - // per-frame — the rebuild only fires on a step change, bounded by speed. - // Overrides MoonModule::loop(); Layer::loop() invokes it per enabled modifier child. + // Dynamic tick: advance the angle on the timer. No rebuild — modifyLive applies + // the new angle on the next frame. The angle is uint8 turn units (256 = a turn); + // dt·speed accumulates so a sub-ms frame isn't lost (the integer-accumulator idiom + // the effects use). Layer::loop() invokes this per enabled modifier child. void loop() override { - Layer* lyr = static_cast(parent()); - if (!lyr) return; - const uint32_t now = lyr->elapsed(); + const uint32_t now = platform::millis(); if (lastElapsed_ == 0) lastElapsed_ = now; const uint32_t dt = now - lastElapsed_; lastElapsed_ = now; - // Accumulate dt*speed; the step is phaseNum_/64 (mod 256), so one step takes - // 64/speed ms and a full turn (256 steps = 16384 units) takes 16384/speed ms. - // Read the high bits as the angle step — the same integer accumulator idiom the - // effects use so a sub-ms dt isn't lost. phaseNum_ += static_cast(dt) * speed; - const uint8_t step = static_cast(phaseNum_ >> 6); // one step per 64 units - if (step != angle_) { - angle_ = step; - // Rebuild the LUT at the new angle (re-runs mapToPhysical). Like - // RandomMapModifier this is a step-gated rebuild from loop(), an accepted - // bounded cost (runs after the effect pass, not per-tick) — but the bound - // here is the angle-step rate, not a bpm cap: one step per 64 accumulator - // units → speed/64 rebuilds/sec, i.e. up to ~4/sec at speed 255 (vs - // RandomMap's ≤1/sec at bpm 60). Each rebuild does the alloc/free - // rebuildLUT() does; that ~4/sec ceiling on the render task is the cost of - // smooth rotation. Lower `speed` for fewer rebuilds. - lyr->onBuildState(); - } + angle_ = static_cast(phaseNum_ >> 6); // one turn unit per 64 accumulator units } private: uint8_t angle_ = 0; // current rotation angle, uint8 turn units (256 = full turn) - uint64_t phaseNum_ = 0; // dt*speed accumulator + uint64_t phaseNum_ = 0; // dt·speed accumulator uint32_t lastElapsed_ = 0; }; diff --git a/src/ui/app.js b/src/ui/app.js index b3bc430..c067660 100644 --- a/src/ui/app.js +++ b/src/ui/app.js @@ -2113,6 +2113,10 @@ function setupStatusBarButtons() { theme = (theme === "dark") ? "light" : "dark"; localStorage.setItem(LS_THEME, theme); applyTheme(theme); + // Repaint the preview to the new theme's background — a live preview would + // pick it up on its next frame, but an idle one (no incoming frames) needs + // a nudge so the canvas doesn't keep the previous theme's clear colour. + preview.redraw(); }); // Hamburger: toggles the side nav. On wide screens it collapses/expands the diff --git a/src/ui/preview3d.js b/src/ui/preview3d.js index 0abef70..55108d5 100644 --- a/src/ui/preview3d.js +++ b/src/ui/preview3d.js @@ -634,8 +634,11 @@ function drawVerts() { const mvp = buildMVP(ex, ey, ez, camTgtX, camTgtY, camTgtZ, canvas.width / Math.max(1, canvas.height)); // alpha:false context — clear to page background colour so the canvas - // blends seamlessly in both light and dark themes. - const bg = getComputedStyle(document.documentElement).getPropertyValue("--bg-0").trim(); + // blends seamlessly in both light and dark themes. Read from , not + // : the theme override is `body[data-theme="light"]`, so --bg-0 is + // redefined on the body; getComputedStyle(documentElement) would only ever + // see the dark :root default and never the light override. + const bg = getComputedStyle(document.body).getPropertyValue("--bg-0").trim(); const m = bg.match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i); if (m) gl.clearColor(parseInt(m[1],16)/255, parseInt(m[2],16)/255, parseInt(m[3],16)/255, 1.0); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); @@ -853,4 +856,8 @@ export const preview = { setupLayout: setupLayout, onBinaryMessage: renderPreviewBinary, resetCamera: resetCamera, + // Redraw the last frame with the current theme's background — call on a theme + // toggle so an idle preview (no live frames) repaints to the new --bg-0 + // instead of keeping the previous theme's clear colour until the next frame. + redraw: redrawCached, }; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 81fc0c8..3b016b3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -37,6 +37,7 @@ add_executable(mm_tests unit/light/unit_NetworkSendDriver_no_alloc_in_loop.cpp unit/light/unit_NetworkSendDriver_packet.cpp unit/light/unit_BlendMap.cpp + unit/light/unit_Coord3D.cpp unit/light/unit_CheckerboardEffect.cpp unit/light/unit_SineEffect.cpp unit/light/unit_DistortionWavesEffect.cpp @@ -49,6 +50,8 @@ add_executable(mm_tests unit/light/unit_WheelLayout.cpp unit/light/unit_Layer_extrude.cpp unit/light/unit_Layer_sparse_mapping.cpp + unit/light/unit_Layer_modifier_chain.cpp + unit/light/unit_Layer_live_modifier.cpp unit/light/unit_Layer_phase_animation.cpp unit/light/unit_Layer_zero_grid.cpp unit/light/unit_Layers_container.cpp diff --git a/test/scenario_runner.cpp b/test/scenario_runner.cpp index 31a568e..cec92f3 100644 --- a/test/scenario_runner.cpp +++ b/test/scenario_runner.cpp @@ -28,6 +28,9 @@ #include "light/effects/GameOfLifeEffect.h" #include "light/modifiers/MultiplyModifier.h" #include "light/modifiers/CheckerboardModifier.h" +#include "light/modifiers/RegionModifier.h" +#include "light/modifiers/RotateModifier.h" +#include "light/modifiers/RandomMapModifier.h" #include "light/drivers/Drivers.h" #include "light/drivers/NetworkSendDriver.h" #include "light/drivers/PreviewDriver.h" @@ -191,6 +194,9 @@ static void registerScenarioTypes() { mm::ModuleFactory::registerType("GameOfLifeEffect"); mm::ModuleFactory::registerType("MultiplyModifier"); mm::ModuleFactory::registerType("CheckerboardModifier"); + mm::ModuleFactory::registerType("RegionModifier"); + mm::ModuleFactory::registerType("RotateModifier"); + mm::ModuleFactory::registerType("RandomMapModifier"); mm::ModuleFactory::registerType("Drivers"); mm::ModuleFactory::registerType("NetworkSendDriver"); mm::ModuleFactory::registerType("PreviewDriver"); diff --git a/test/scenarios/light/scenario_modifier_chain.json b/test/scenarios/light/scenario_modifier_chain.json new file mode 100644 index 0000000..fbce1a2 --- /dev/null +++ b/test/scenarios/light/scenario_modifier_chain.json @@ -0,0 +1,188 @@ +{ + "name": "scenario_modifier_chain", + "module": "Layer", + "mode": "mutate", + "also": [ + "RegionModifier", + "MultiplyModifier", + "CheckerboardModifier", + "RotateModifier", + "NoiseEffect", + "Layouts", + "GridLayout", + "Drivers", + "NetworkSendDriver" + ], + "description": "Stack TWO modifiers on one Layer (Region then Multiply) and verify the chain composes live end-to-end — the capability the old single-modifier engine couldn't do. Prepares its own canvas: Layout(Grid 32x32) + Layer + NoiseEffect + Region(0..50) + Multiply(2x), measures the composite, then adds a third (Checkerboard mask) and measures again, then removes the middle modifier and measures — exercising add/remove on a multi-modifier chain. A broken fold (null buffer, wrong light count, crash on a disabled/removed stage) shows up as a failed measure. The fold composition + order semantics are pinned by unit_Layer_modifier_chain; this is the live end-to-end gate.", + "fixture": [ + { + "name": "fix-layouts", + "op": "add_module", + "id": "Layouts", + "type": "Layouts" + }, + { + "name": "fix-grid", + "op": "add_module", + "id": "Grid", + "type": "GridLayout", + "parent_id": "Layouts", + "props": { + "width": 32, + "height": 32 + } + }, + { + "name": "fix-layers", + "op": "add_module", + "id": "Layers", + "type": "Layers", + "props": { + "layouts": "Layouts" + } + }, + { + "name": "fix-layer", + "op": "add_module", + "id": "Layer", + "type": "Layer", + "parent_id": "Layers", + "props": { + "channelsPerLight": 3 + } + }, + { + "name": "fix-fx", + "op": "add_module", + "id": "FX", + "type": "NoiseEffect", + "parent_id": "Layer" + }, + { + "name": "fix-region", + "op": "add_module", + "id": "REGION", + "type": "RegionModifier", + "parent_id": "Layer", + "props": { + "endX": 50, + "endY": 50 + } + }, + { + "name": "fix-mult", + "op": "add_module", + "id": "MULT", + "type": "MultiplyModifier", + "parent_id": "Layer" + }, + { + "name": "fix-drivers", + "op": "add_module", + "id": "Drivers", + "type": "Drivers", + "props": { + "layers": "Layers" + } + }, + { + "name": "fix-artnet", + "op": "add_module", + "id": "ArtNet", + "type": "NetworkSendDriver", + "parent_id": "Drivers" + } + ], + "steps": [ + { + "name": "region-then-multiply", + "description": "Two stacked modifiers: Region(top-left quarter) then Multiply(2x mirror) compose into one mapping. Measure the live composite.", + "op": "measure", + "observed": { + "pc-macos": { + "tick_us": [ + 6, + 7 + ], + "free_heap": [ + 0, + 0 + ], + "max_alloc_block": [ + 0, + 0 + ], + "at": [ + "2026-06-26", + "2026-06-26" + ] + } + } + }, + { + "name": "add-mask", + "description": "Add a third modifier (Checkerboard mask) on top of the chain — a 3-deep fold. Measure that the deeper chain still renders.", + "op": "add_module", + "id": "MASK", + "type": "CheckerboardModifier", + "parent_id": "Layer", + "measure": true, + "observed": { + "pc-macos": { + "tick_us": [ + 6, + 7 + ], + "free_heap": [ + 0, + 0 + ], + "max_alloc_block": [ + 0, + 0 + ], + "at": [ + "2026-06-26", + "2026-06-26" + ] + } + } + }, + { + "name": "remove-middle", + "description": "Remove the middle modifier (Multiply) — the chain re-folds with Region then Checkerboard, no stale state. Measure.", + "op": "remove_module", + "id": "MULT", + "measure": true, + "observed": { + "pc-macos": { + "tick_us": [ + 18, + 20 + ], + "free_heap": [ + 0, + 0 + ], + "max_alloc_block": [ + 0, + 0 + ], + "at": [ + "2026-06-26", + "2026-06-26" + ] + } + } + }, + { + "name": "add-live-rotate", + "description": "Add a DYNAMIC Rotate on top of the static chain — its modifyLive runs the per-frame remap pass over the composed buffer. Verifies a static chain + a live modifier coexist (the buffer is remapped each frame on top of the baked Region/Checkerboard mapping) without a crash or null buffer.", + "op": "add_module", + "id": "ROT", + "type": "RotateModifier", + "parent_id": "Layer", + "measure": true + } + ] +} diff --git a/test/unit/light/unit_CheckerboardModifier.cpp b/test/unit/light/unit_CheckerboardModifier.cpp index 6ff2fa6..b209809 100644 --- a/test/unit/light/unit_CheckerboardModifier.cpp +++ b/test/unit/light/unit_CheckerboardModifier.cpp @@ -4,87 +4,64 @@ #include "light/modifiers/CheckerboardModifier.h" // CheckerboardModifier masks the layer: lights in "off" squares are dropped -// (mapToPhysical returns outCount=0), lights in "on" squares pass through -// unchanged. The logical box is unchanged (identity dimensions). +// (modifyLogical returns false), lights in "on" squares pass through unchanged +// (returns true, pos untouched). A mask leaves the logical box unchanged. -static mm::nrOfLightsType mapOne(mm::CheckerboardModifier& c, - mm::lengthType x, mm::lengthType y, mm::lengthType z, - mm::lengthType w, mm::lengthType h, mm::lengthType d, - mm::nrOfLightsType& count) { - mm::nrOfLightsType phys[8]; - count = 0; - c.mapToPhysical(x, y, z, w, h, d, phys, count, 8); - return count ? phys[0] : 0; +// Run the fold on one coord; returns whether it passes (true) and leaves pos in p. +static bool keep(mm::CheckerboardModifier& c, mm::lengthType x, mm::lengthType y, mm::lengthType z, + mm::Coord3D box, mm::Coord3D& p) { + mm::Coord3D size = box; + c.modifyLogicalSize(size); // a mask: identity size + p = {x, y, z}; + return c.modifyLogical(p); } -// Identity dimensions — a mask doesn't resize the logical box. -TEST_CASE("CheckerboardModifier logicalDimensions are identity") { +// A mask leaves the logical box unchanged. +TEST_CASE("CheckerboardModifier does not resize the logical box") { mm::CheckerboardModifier c; - mm::lengthType logW, logH, logD; - c.logicalDimensions(64, 32, 4, logW, logH, logD); - CHECK(logW == 64); - CHECK(logH == 32); - CHECK(logD == 4); + mm::Coord3D size{64, 32, 4}; + c.modifyLogicalSize(size); + CHECK(size == mm::Coord3D{64, 32, 4}); } // size=1: every cell is its own square; parity = (x+y+z)&1. Default (invert -// false) keeps even-parity cells, drops odd-parity. +// false) keeps even-parity cells, drops odd-parity. Passing cells keep their coord. TEST_CASE("CheckerboardModifier size 1 keeps even-parity, drops odd") { mm::CheckerboardModifier c; c.size = 1; - mm::nrOfLightsType count; + const mm::Coord3D box{8, 8, 1}; + mm::Coord3D p; - // (0,0,0): parity 0 → on. Passes through at identity index 0. - CHECK(mapOne(c, 0, 0, 0, 8, 8, 1, count) == 0); - CHECK(count == 1); - - // (1,0,0): parity 1 → off. Dropped. - mapOne(c, 1, 0, 0, 8, 8, 1, count); - CHECK(count == 0); - - // (1,1,0): parity 0 → on. Identity index 1*8+1 = 9. - CHECK(mapOne(c, 1, 1, 0, 8, 8, 1, count) == 9); - CHECK(count == 1); + CHECK(keep(c, 0, 0, 0, box, p)); // parity 0 → on + CHECK(p == mm::Coord3D{0, 0, 0}); // pass-through: coord unchanged + CHECK_FALSE(keep(c, 1, 0, 0, box, p)); // parity 1 → dropped + CHECK(keep(c, 1, 1, 0, box, p)); // parity 0 → on + CHECK(p == mm::Coord3D{1, 1, 0}); } -// invert flips which parity passes — the cell that was dropped now passes and -// vice versa. +// invert flips which parity passes. TEST_CASE("CheckerboardModifier invert flips the kept squares") { mm::CheckerboardModifier c; c.size = 1; c.invert = true; - mm::nrOfLightsType count; - - // (0,0,0): parity 0, but inverted → off. Dropped. - mapOne(c, 0, 0, 0, 8, 8, 1, count); - CHECK(count == 0); + const mm::Coord3D box{8, 8, 1}; + mm::Coord3D p; - // (1,0,0): parity 1, inverted → on. Passes at index 1. - CHECK(mapOne(c, 1, 0, 0, 8, 8, 1, count) == 1); - CHECK(count == 1); + CHECK_FALSE(keep(c, 0, 0, 0, box, p)); // parity 0, inverted → off + CHECK(keep(c, 1, 0, 0, box, p)); // parity 1, inverted → on } // size>1 groups cells into squares: with size=2, the 2×2 block at the origin is -// all one square (parity of 0/2=0), so all four pass; the next block over drops. +// all one square (parity 0), so all four pass; the next block over drops. TEST_CASE("CheckerboardModifier size 2 groups into squares") { mm::CheckerboardModifier c; c.size = 2; - mm::nrOfLightsType count; + const mm::Coord3D box{8, 8, 1}; + mm::Coord3D p; - // Origin 2×2 block (x,y in 0..1): square (0,0) parity 0 → on. for (mm::lengthType y = 0; y < 2; y++) - for (mm::lengthType x = 0; x < 2; x++) { - mapOne(c, x, y, 0, 8, 8, 1, count); - CHECK(count == 1); // whole block passes - } + for (mm::lengthType x = 0; x < 2; x++) + CHECK(keep(c, x, y, 0, box, p)); // whole origin block passes - // Adjacent block (x in 2..3): square (1,0) parity 1 → off. - mapOne(c, 2, 0, 0, 8, 8, 1, count); - CHECK(count == 0); -} - -// Never fans out — at most one destination. -TEST_CASE("CheckerboardModifier maxMultiplier is 1") { - mm::CheckerboardModifier c; - CHECK(c.maxMultiplier() == 1); + CHECK_FALSE(keep(c, 2, 0, 0, box, p)); // adjacent block (parity 1) drops } diff --git a/test/unit/light/unit_Coord3D.cpp b/test/unit/light/unit_Coord3D.cpp new file mode 100644 index 0000000..6588a05 --- /dev/null +++ b/test/unit/light/unit_Coord3D.cpp @@ -0,0 +1,40 @@ +// @module light_types + +#include "doctest.h" +#include "light/light_types.h" + +// Coord3D is the coordinate/size type the modifier fold interface mutates. The +// per-axis (Hadamard) operators are what let a modifier read like geometry +// (`pos = pos % size`); the % and / variants must guard a zero/degenerate axis so +// a fold over a 1-wide or empty axis can't divide by zero or wrap. + +TEST_CASE("Coord3D arithmetic is per-axis") { + mm::Coord3D a{10, 20, 30}; + mm::Coord3D b{1, 2, 3}; + CHECK((a + b) == mm::Coord3D{11, 22, 33}); + CHECK((a - b) == mm::Coord3D{9, 18, 27}); + CHECK((a * b) == mm::Coord3D{10, 40, 90}); +} + +TEST_CASE("Coord3D modulo and divide fold per axis") { + mm::Coord3D pos{7, 5, 9}; + mm::Coord3D size{4, 4, 4}; + // The textbook Multiply fold: position within its tile, and which tile. + CHECK((pos % size) == mm::Coord3D{3, 1, 1}); + CHECK((pos / size) == mm::Coord3D{1, 1, 2}); +} + +TEST_CASE("Coord3D % and / guard a zero or degenerate axis") { + mm::Coord3D pos{7, 5, 3}; + // A 0-extent or 1-extent axis must not divide-by-zero or wrap; the coordinate + // passes through (% ) or stays put-ish (/), so a fold over a flat axis is safe. + mm::Coord3D zero{0, 1, 0}; + CHECK((pos % zero) == mm::Coord3D{7, 0, 3}); // x: %0 → unchanged, y: %1 → 0, z: %0 → unchanged + CHECK((pos / zero) == mm::Coord3D{7, 5, 3}); // /0 → unchanged on x and z, /1 → unchanged on y +} + +TEST_CASE("Coord3D equality") { + CHECK(mm::Coord3D{1, 2, 3} == mm::Coord3D{1, 2, 3}); + CHECK(mm::Coord3D{1, 2, 3} != mm::Coord3D{1, 2, 4}); + CHECK(mm::Coord3D{} == mm::Coord3D{0, 0, 0}); // default-constructed is the origin +} diff --git a/test/unit/light/unit_Layer_live_modifier.cpp b/test/unit/light/unit_Layer_live_modifier.cpp new file mode 100644 index 0000000..7b7b3e5 --- /dev/null +++ b/test/unit/light/unit_Layer_live_modifier.cpp @@ -0,0 +1,164 @@ +// @module Layer +// @also RotateModifier, ModifierBase + +#include "doctest.h" +#include "light/layers/Layers.h" +#include "light/layers/Layer.h" +#include "light/layouts/Layouts.h" +#include "light/layouts/GridLayout.h" +#include "light/modifiers/RotateModifier.h" +#include "light/modifiers/RandomMapModifier.h" +#include "light/effects/EffectBase.h" +#include "platform/platform.h" + +#include +#include + +// The live (per-frame) modifier seam: Layer::loop() applies a live modifier's +// modifyLive remap to the rendered buffer every frame WITHOUT a mapping rebuild, +// and runs that pass ONLY when an enabled modifier reports hasModifyLive() — a +// static-only Layer pays nothing (the pay-for-what-you-use guarantee). These pin +// both: the pass runs and remaps when a Rotate is present, and is skipped otherwise. + +namespace { + +// A tiny effect that writes a fixed position-dependent gradient into the buffer +// (R = x, G = y), so a coordinate remap (rotation) visibly rearranges the bytes. +// Deterministic per frame — no time dependence — so any frame-to-frame change is +// the live pass, not the effect animating itself. +class GradientEffect : public mm::EffectBase { +public: + void loop() override { + uint8_t* buf = buffer(); + if (!buf) return; + const mm::lengthType w = width(), h = height(); + const uint8_t cpl = channelsPerLight(); + for (mm::lengthType y = 0; y < h; y++) + for (mm::lengthType x = 0; x < w; x++) { + uint8_t* p = buf + (static_cast(y) * w + x) * cpl; + p[0] = static_cast(x); + if (cpl > 1) p[1] = static_cast(y); + if (cpl > 2) p[2] = 0; + } + } +}; + +// Snapshot the Layer's buffer after one loop() at virtual time `t`. +std::vector frameAt(mm::Layer& layer, uint32_t t) { + mm::platform::setTestNowMs(t); + layer.loop(); + const mm::Buffer& b = layer.buffer(); + std::vector out(b.bytes()); + if (b.data()) std::memcpy(out.data(), b.data(), b.bytes()); + return out; +} + +} // namespace + +// With a Rotate present, the live pass rotates the gradient each frame as the angle +// advances — so two frames at different times differ. A static GradientEffect alone +// would produce identical frames, so any difference is the live remap. +TEST_CASE("Layer live pass: Rotate remaps the buffer per frame") { + mm::Layouts layouts; + mm::GridLayout grid; + grid.width = 16; grid.height = 16; grid.depth = 1; + layouts.addChild(&grid); + mm::Layer layer; + layer.setLayouts(&layouts); + layer.setChannelsPerLight(3); + GradientEffect fx; layer.addChild(&fx); + mm::RotateModifier rot; rot.speed = 200; // fast enough to cross several angle steps + layer.addChild(&rot); + layouts.onBuildState(); + layer.onBuildState(); + + auto f0 = frameAt(layer, 1000); + auto f1 = frameAt(layer, 1000); // same time → same angle → identical frame + auto f2 = frameAt(layer, 1300); // later → angle advanced → rotated frame + + CHECK(f0 == f1); // deterministic at a fixed clock + CHECK(f0 != f2); // the live pass rotated the gradient + mm::platform::setTestNowMs(0); +} + +// PAY-FOR-WHAT-YOU-USE: a Layer with no live modifier must NOT run the live pass — +// the static gradient is byte-identical across frames regardless of the clock. +TEST_CASE("Layer live pass: skipped when no modifier is live") { + mm::Layouts layouts; + mm::GridLayout grid; + grid.width = 16; grid.height = 16; grid.depth = 1; + layouts.addChild(&grid); + mm::Layer layer; + layer.setLayouts(&layouts); + layer.setChannelsPerLight(3); + GradientEffect fx; layer.addChild(&fx); // no modifiers at all + layouts.onBuildState(); + layer.onBuildState(); + + auto f0 = frameAt(layer, 1000); + auto f1 = frameAt(layer, 5000); // far later — but nothing animates the buffer + CHECK(f0 == f1); // no live pass perturbed it + mm::platform::setTestNowMs(0); +} + +// A DISABLED Rotate must not run the live pass either (the gate keys off ENABLED +// live modifiers). Same static gradient → identical frames. +TEST_CASE("Layer live pass: a disabled live modifier does not run") { + mm::Layouts layouts; + mm::GridLayout grid; + grid.width = 16; grid.height = 16; grid.depth = 1; + layouts.addChild(&grid); + mm::Layer layer; + layer.setLayouts(&layouts); + layer.setChannelsPerLight(3); + GradientEffect fx; layer.addChild(&fx); + mm::RotateModifier rot; rot.speed = 200; rot.setEnabled(false); + layer.addChild(&rot); + layouts.onBuildState(); + layer.onBuildState(); + + auto f0 = frameAt(layer, 1000); + auto f1 = frameAt(layer, 1300); + CHECK(f0 == f1); // disabled → no remap + mm::platform::setTestNowMs(0); +} + +// COALESCED REBUILD: two beat-driven modifiers (RandomMap) on one Layer both ask for +// a rebuild on a beat; Layer::loop() must rebuild ONCE (not re-enter onBuildState per +// modifier) and the Layer must stay valid — the composed mapping changes, no crash. +TEST_CASE("Layer coalesces rebuilds from two dynamic modifiers") { + mm::Layouts layouts; + mm::GridLayout grid; + grid.width = 8; grid.height = 8; grid.depth = 1; + layouts.addChild(&grid); + mm::Layer layer; + layer.setLayouts(&layouts); + layer.setChannelsPerLight(3); + GradientEffect fx; layer.addChild(&fx); + mm::RandomMapModifier a; a.bpm = 60; // both reshuffle on a ~1 Hz beat + mm::RandomMapModifier b; b.bpm = 60; + layer.addChild(&a); + layer.addChild(&b); + layouts.onBuildState(); + layer.onBuildState(); + + const std::size_t cells = static_cast(layer.lut().logicalCount()); + REQUIRE(cells == 64); + + auto destCount = [&]() { + std::size_t n = 0; + for (mm::nrOfLightsType li = 0; li < layer.lut().logicalCount(); li++) + layer.lut().forEachDestination(li, [&](mm::nrOfLightsType) { n++; }); + return n; + }; + const std::size_t before = destCount(); + + // Advance past a beat boundary in small ticks (both modifiers tick each frame). + for (uint32_t t = 1000; t <= 2500; t += 50) { mm::platform::setTestNowMs(t); layer.loop(); } + mm::platform::setTestNowMs(0); + + // Two composed permutations remain a permutation of the 64 cells — every light + // still maps somewhere exactly once (no crash, no lost/duplicated destinations). + CHECK(destCount() == before); + CHECK(destCount() == 64); +} diff --git a/test/unit/light/unit_Layer_modifier_chain.cpp b/test/unit/light/unit_Layer_modifier_chain.cpp new file mode 100644 index 0000000..bb4e4a1 --- /dev/null +++ b/test/unit/light/unit_Layer_modifier_chain.cpp @@ -0,0 +1,128 @@ +// @module Layer +// @also ModifierBase + +#include "doctest.h" +#include "light/layers/Layer.h" +#include "light/layouts/Layouts.h" +#include "light/layouts/GridLayout.h" +#include "light/modifiers/RegionModifier.h" +#include "light/modifiers/MultiplyModifier.h" +#include "light/modifiers/CheckerboardModifier.h" + +#include +#include + +// Composable modifiers: a Layer folds ALL its enabled static modifiers in child order +// into one mapping (M₁∘M₂∘…). These pin that the chain composes (the logical box is the +// product of the folds), that order matters (A∘B ≠ B∘A), and that a disabled middle +// modifier is skipped. The single-modifier and sphere cases live in unit_Layer_sparse_mapping. + +namespace { +// Build a dense w×h grid Layer with the given modifiers added in order, run onBuildState. +struct ChainRig { + mm::Layouts group; + mm::GridLayout grid; + mm::Layer layer; + explicit ChainRig(mm::lengthType w, mm::lengthType h, + std::initializer_list mods) { + grid.width = w; grid.height = h; grid.depth = 1; + group.addChild(&grid); + layer.setLayouts(&group); + layer.setChannelsPerLight(3); + for (auto* m : mods) layer.addChild(m); + layer.onBuildControls(); + group.onBuildState(); + layer.onBuildState(); + } +}; +} // namespace + +// Region (left half) THEN Multiply (2× mirror): the logical box folds twice. On a +// 16-wide axis: Region 0..50 → 8, then Multiply 2 → 4. Both modifiers apply — the +// second is no longer dead weight. +TEST_CASE("Layer chains Region then Multiply (both fold)") { + mm::RegionModifier region; region.startX = 0; region.endX = 50; region.endY = 100; + mm::MultiplyModifier mult; mult.multiplyX = 2; mult.multiplyY = 1; mult.multiplyZ = 1; + ChainRig rig(16, 4, {®ion, &mult}); + + CHECK(rig.layer.width() == 4); // 16 → region 8 → multiply 4 + CHECK(rig.layer.height() == 4); // y untouched by either + CHECK(rig.layer.lut().hasLUT()); // a real mapping, not the identity fast path + + // REGRESSION: each modifier must fold in ITS OWN stage's box, not the final + // composed box. A bug where Region tested its region-local coord against the + // FINAL (post-Multiply) box truncated the region to a strip — only the first + // tile's worth of physical lights mapped, the rest were wrongly rejected. + // Assert the FULL left region (x 0..7) is covered: 8 region cols × 4 rows = 32 + // physical lights all reach the 4×4 logical box (16 cells), so every logical + // cell gets ≥1 destination and the total is the whole region (32), not a strip. + std::size_t total = 0; + std::size_t cellsHit = 0; + for (mm::nrOfLightsType li = 0; li < rig.layer.lut().logicalCount(); li++) { + std::size_t here = 0; + rig.layer.lut().forEachDestination(li, [&](mm::nrOfLightsType) { here++; }); + total += here; + if (here) cellsHit++; + } + CHECK(total == 32); // the full left region, not a strip + CHECK(cellsHit == rig.layer.lut().logicalCount()); // every logical cell is fed +} + +// Order matters: Region-then-Multiply differs from Multiply-then-Region. Region's +// percentage applies to whatever box it sees, so the composed logical size differs. +TEST_CASE("Layer modifier order matters (A∘B ≠ B∘A)") { + // A: Region(0..50) then Multiply(2×) on a 16-wide axis → 16→8→4. + mm::RegionModifier rA; rA.startX = 0; rA.endX = 50; + mm::MultiplyModifier mA; mA.multiplyX = 2; mA.multiplyY = 1; + ChainRig a(16, 4, {&rA, &mA}); + + // B: Multiply(2×) then Region(0..50) → 16→8→4 as well by size, but the cells the + // region keeps differ. Use a region that makes the SIZE differ to pin order cheaply: + // Multiply(4×) then Region(0..50): 16→4→2. Region(0..50) then Multiply(4×): 16→8→2. + // Sizes match (2) but let's pin via a size-distinguishing case instead: + // Region(0..25) then Multiply(2×): 16→4→2. Multiply(2×) then Region(0..25): 16→8→2. + // Equal again — so assert on the MAPPING, not just size: different composition order + // sends a given physical light to a different logical cell. + mm::MultiplyModifier mB; mB.multiplyX = 2; mB.multiplyY = 1; + mm::RegionModifier rB; rB.startX = 0; rB.endX = 50; + ChainRig b(16, 4, {&mB, &rB}); + + // Both fold a 16-wide axis to width 4, but via different intermediate spaces. + CHECK(a.layer.width() == 4); + CHECK(b.layer.width() == 4); + + // The mappings differ: collect each LUT's destination set per logical cell and + // compare. If order didn't matter they'd be identical. + auto fingerprint = [](mm::Layer& L) { + std::vector fp; + for (mm::nrOfLightsType li = 0; li < L.lut().logicalCount(); li++) + L.lut().forEachDestination(li, [&](mm::nrOfLightsType d) { fp.push_back(d); }); + return fp; + }; + CHECK(fingerprint(a.layer) != fingerprint(b.layer)); +} + +// A DISABLED middle modifier is skipped — the chain folds only the enabled ones. +TEST_CASE("Layer skips a disabled modifier in the chain") { + mm::RegionModifier region; region.startX = 0; region.endX = 50; + mm::CheckerboardModifier mask; mask.setEnabled(false); // disabled — must be skipped + mm::MultiplyModifier mult; mult.multiplyX = 2; mult.multiplyY = 1; + ChainRig rig(16, 4, {®ion, &mask, &mult}); + + // Region(8) then Multiply(4) — the disabled mask between them contributes nothing. + CHECK(rig.layer.width() == 4); + + // Enabling the mask drops cells (it rejects half), so the destination COUNT shrinks + // vs the disabled run — proof the enable flag is honoured per modifier. + std::size_t withoutMask = 0; + for (mm::nrOfLightsType li = 0; li < rig.layer.lut().logicalCount(); li++) + rig.layer.lut().forEachDestination(li, [&](mm::nrOfLightsType) { withoutMask++; }); + + mask.setEnabled(true); + rig.layer.onBuildState(); + std::size_t withMask = 0; + for (mm::nrOfLightsType li = 0; li < rig.layer.lut().logicalCount(); li++) + rig.layer.lut().forEachDestination(li, [&](mm::nrOfLightsType) { withMask++; }); + + CHECK(withMask < withoutMask); // the mask now drops some physical lights +} diff --git a/test/unit/light/unit_MultiplyModifier.cpp b/test/unit/light/unit_MultiplyModifier.cpp index affaf14..674971e 100644 --- a/test/unit/light/unit_MultiplyModifier.cpp +++ b/test/unit/light/unit_MultiplyModifier.cpp @@ -3,185 +3,105 @@ #include "doctest.h" #include "light/modifiers/MultiplyModifier.h" -// MultiplyModifier tiles the logical image across the physical box `multiply` -// times per axis, optionally reflecting alternate tiles (the kaleidoscope -// mirror). With multiply=2 + mirror on an axis it folds in half — exactly the -// behaviour the old MirrorModifier provided, which several of these cases pin. +// MultiplyModifier tiles the logical image across the physical box `multiply` times +// per axis, optionally reflecting alternate tiles (the kaleidoscope mirror). Under the +// fold build it works PHYSICAL→logical: modifyLogicalSize shrinks the box, and +// modifyLogical folds a physical coord onto its logical cell (`pos % logicalSize`, odd +// tiles reflected). N physical lights folding onto one logical cell IS the fan-out — so +// these cases pin the fold direction, the inverse of the old emit-N-destinations form. + +// Fold a physical coord through modifyLogical; returns the logical cell it lands on. +// modifyLogicalSize must run first so the modifier stashes its tile size. +static mm::Coord3D fold(mm::MultiplyModifier& m, mm::lengthType x, mm::lengthType y, mm::lengthType z, + mm::Coord3D box) { + mm::Coord3D logical = box; + m.modifyLogicalSize(logical); + mm::Coord3D p{x, y, z}; + m.modifyLogical(p); + return p; +} + +static mm::Coord3D logicalSize(mm::MultiplyModifier& m, mm::Coord3D box) { + m.modifyLogicalSize(box); + return box; +} -// Reports D3 — handles all three axes. Pins the ModifierBase default too. TEST_CASE("MultiplyModifier advertises D3 dimensions") { mm::MultiplyModifier m; CHECK(m.dimensions() == mm::Dim::D3); } -// Defaults (multiply 2/2/1, mirror true/true/false) reproduce the canonical -// mirror-XY pipeline: a 128×128 physical grid → 64×64 logical (each axis folds). -TEST_CASE("MultiplyModifier default logicalDimensions = mirror-XY fold") { +// Defaults (multiply 2/2/1) → a 128×128 physical grid folds to a 64×64 logical box. +TEST_CASE("MultiplyModifier default logical size = mirror-XY fold") { mm::MultiplyModifier m; - mm::lengthType logW, logH, logD; - m.logicalDimensions(128, 128, 1, logW, logH, logD); - CHECK(logW == 64); // 128 / 2 - CHECK(logH == 64); - CHECK(logD == 1); // multiplyZ default 1 → unchanged + CHECK(logicalSize(m, {128, 128, 1}) == mm::Coord3D{64, 64, 1}); } -// multiplyZ tiles the Z axis too: 128×128×4 with multiply 2/2/2 → 64×64×2. -TEST_CASE("MultiplyModifier logicalDimensions on Z") { +TEST_CASE("MultiplyModifier logical size on Z") { mm::MultiplyModifier m; m.multiplyZ = 2; - mm::lengthType logW, logH, logD; - m.logicalDimensions(128, 128, 4, logW, logH, logD); - CHECK(logW == 64); - CHECK(logH == 64); - CHECK(logD == 2); + CHECK(logicalSize(m, {128, 128, 4}) == mm::Coord3D{64, 64, 2}); } -// PURE-FOLD EQUIVALENCE: with the defaults (mult 2, mirror XY), the corner -// logical pixel (0,0) fans out to all four physical corners — byte-identical to -// the old MirrorModifier corner test. This is the canonical-pipeline guarantee. -TEST_CASE("MultiplyModifier corner pixel produces 4 corners (mirror fold)") { +// FAN-OUT (fold direction): with the defaults (mult 2, mirror XY), all four physical +// CORNERS fold onto the single logical pixel (0,0) — the inverse of the old "logical +// (0,0) → 4 physical corners". This is the kaleidoscope fold made concrete. +TEST_CASE("MultiplyModifier four corners fold to logical (0,0)") { mm::MultiplyModifier m; // defaults: mult 2/2/1, mirror true/true/false - mm::nrOfLightsType physicals[8]; - mm::nrOfLightsType count = 0; - - m.mapToPhysical(0, 0, 0, 128, 128, 1, physicals, count, 8); - CHECK(count == 4); - // tile (0,0) identity → (0,0); tile (1,0) mirror x → (127,0); - // tile (0,1) mirror y → (0,127); tile (1,1) → (127,127). Row-major y*128+x. - CHECK(physicals[0] == 0); // (0,0) - CHECK(physicals[1] == 127); // (127,0) - CHECK(physicals[2] == 127 * 128); // (0,127) - CHECK(physicals[3] == 127 * 128 + 127); // (127,127) + const mm::Coord3D box{128, 128, 1}; + CHECK(fold(m, 0, 0, 0, box) == mm::Coord3D{0, 0, 0}); // tile (0,0) identity + CHECK(fold(m, 127, 0, 0, box) == mm::Coord3D{0, 0, 0}); // tile (1,0) mirror x + CHECK(fold(m, 0, 127, 0, box) == mm::Coord3D{0, 0, 0}); // tile (0,1) mirror y + CHECK(fold(m, 127, 127, 0, box) == mm::Coord3D{0, 0, 0}); // tile (1,1) both } -// PURE-FOLD EQUIVALENCE: an interior pixel folds to the same two columns the -// old mirrorX-only produced — original + horizontal reflection. -TEST_CASE("MultiplyModifier mirrorX fold matches old Mirror") { +// mirrorX only: two physical columns fold to the same logical column (original + its +// horizontal reflection). The logical box is 64 wide. +TEST_CASE("MultiplyModifier mirrorX folds reflected columns together") { mm::MultiplyModifier m; m.multiplyX = 2; m.mirrorX = true; m.multiplyY = 1; m.mirrorY = false; m.multiplyZ = 1; - mm::nrOfLightsType physicals[8]; - mm::nrOfLightsType count = 0; - - m.mapToPhysical(5, 10, 0, 128, 128, 1, physicals, count, 8); - CHECK(count == 2); - CHECK(physicals[0] == 10 * 128 + 5); // (5,10) - CHECK(physicals[1] == 10 * 128 + 122); // (127-5,10) = (122,10) + const mm::Coord3D box{128, 128, 1}; + // logical width 64. Physical x=5 → tile 0 → logical x=5; physical x=122 (=127-5) + // → tile 1 (odd), within=122%64=58, mirror → 64-1-58 = 5. Both fold to x=5. + CHECK(fold(m, 5, 10, 0, box) == mm::Coord3D{5, 10, 0}); + CHECK(fold(m, 122, 10, 0, box) == mm::Coord3D{5, 10, 0}); } -// No multiplication on any axis (all multipliers 1) → identity pass-through. +// All multipliers 1 → identity: the box is unchanged and every coord folds to itself. TEST_CASE("MultiplyModifier identity when all multipliers are 1") { mm::MultiplyModifier m; m.multiplyX = 1; m.multiplyY = 1; m.multiplyZ = 1; - mm::lengthType logW, logH, logD; - m.logicalDimensions(128, 128, 1, logW, logH, logD); - CHECK(logW == 128); CHECK(logH == 128); CHECK(logD == 1); - - mm::nrOfLightsType physicals[8]; - mm::nrOfLightsType count = 0; - m.mapToPhysical(5, 10, 0, 128, 128, 1, physicals, count, 8); - CHECK(count == 1); - CHECK(physicals[0] == 10 * 128 + 5); + CHECK(logicalSize(m, {128, 128, 1}) == mm::Coord3D{128, 128, 1}); + CHECK(fold(m, 5, 10, 0, {128, 128, 1}) == mm::Coord3D{5, 10, 0}); } -// Tiling WITHOUT mirror repeats (does not reflect) — multiply 2 on X, mirror off: -// logical x=0 lands at physical x=0 (tile 0) and x=64 (tile 1, identity offset), -// NOT x=127. This is the difference from a fold. +// Tiling WITHOUT mirror repeats (does not reflect): physical x=64 (tile 1) folds to +// logical x=0, same as physical x=0 — both tiles map identically, no reflection. TEST_CASE("MultiplyModifier tiles without mirror (repeat, not fold)") { mm::MultiplyModifier m; m.multiplyX = 2; m.mirrorX = false; m.multiplyY = 1; m.mirrorY = false; m.multiplyZ = 1; - mm::nrOfLightsType physicals[8]; - mm::nrOfLightsType count = 0; - - // 128 wide, tileW = 64. logical x=0 → tile0 x=0, tile1 x=64. - m.mapToPhysical(0, 0, 0, 128, 128, 1, physicals, count, 8); - CHECK(count == 2); - CHECK(physicals[0] == 0); // tile 0: x=0 - CHECK(physicals[1] == 64); // tile 1 (no mirror): x = 64+0 + const mm::Coord3D box{128, 128, 1}; // logical width 64 + CHECK(fold(m, 0, 0, 0, box).x == 0); // tile 0, x=0 → 0 + CHECK(fold(m, 64, 0, 0, box).x == 0); // tile 1, x=64 → 64%64 = 0 (repeat, not 63) } -// multiplyZ on a 2D (depth-1) layout is a no-op: the effective multiplier -// clamps to the axis extent (1), so logD stays 1 and the layer isn't blanked. -// Before the clamp, multiplyZ=4 made logD = 1/4 = 0 → empty layer. +// multiplyZ on a 2D (depth-1) layout is a no-op: the effective multiplier clamps to +// the axis extent (1), so depth stays 1 and the layer isn't blanked. TEST_CASE("MultiplyModifier multiplyZ on 2D does nothing") { mm::MultiplyModifier m; m.multiplyX = 1; m.multiplyY = 1; m.multiplyZ = 4; // Z multiply on a flat grid - mm::lengthType logW, logH, logD; - m.logicalDimensions(64, 64, 1, logW, logH, logD); - CHECK(logW == 64); - CHECK(logH == 64); - CHECK(logD == 1); // NOT 0 — Z multiply clamped to the depth-1 extent - - mm::nrOfLightsType physicals[8]; - mm::nrOfLightsType count = 0; - m.mapToPhysical(5, 10, 0, 64, 64, 1, physicals, count, 8); - CHECK(count == 1); // single position, no Z tiling - CHECK(physicals[0] == 10 * 64 + 5); // identity + CHECK(logicalSize(m, {64, 64, 1}) == mm::Coord3D{64, 64, 1}); // depth stays 1, not 0 + CHECK(fold(m, 5, 10, 0, {64, 64, 1}) == mm::Coord3D{5, 10, 0}); // identity } -// A multiplier larger than the axis extent clamps to the extent (can't tile more -// times than there are pixels). +// A multiplier larger than the axis extent clamps to the extent. TEST_CASE("MultiplyModifier clamps a multiplier above the axis extent") { mm::MultiplyModifier m; m.multiplyX = 64; m.multiplyY = 1; m.multiplyZ = 1; m.mirrorX = false; - mm::lengthType logW, logH, logD; - m.logicalDimensions(16, 16, 1, logW, logH, logD); // 64× on a 16-wide axis - CHECK(logW == 1); // clamped to extent 16 → 16/16 = 1, not 16/64 = 0 - CHECK(logH == 16); -} - -// maxMultiplier is the product of the raw controls (the fan-out upper bound). -TEST_CASE("MultiplyModifier maxMultiplier is the product of axes") { - mm::MultiplyModifier m; - m.multiplyX = 2; m.multiplyY = 2; m.multiplyZ = 2; - CHECK(m.maxMultiplier() == 8); - m.multiplyZ = 1; - CHECK(m.maxMultiplier() == 4); // the default-ish XY fold -} - -// REGRESSION: maxMultiplier() must NOT wrap when all axes are maxed. The product -// 64×64×16 = 65536 overflows nrOfLightsType (uint16 on no-PSRAM) and would wrap -// to 0 — feeding the uint64 maxDest math in Layer::rebuildLUT an already-wrapped -// (possibly 0) multiplier → empty LUT → black display. It must saturate to the -// type max instead. (Single-axis tests above stay under the wrap; this one -// crosses it.) On uint32 (PSRAM) the product fits and isn't saturated — assert -// only the non-wrap, non-zero invariant that holds on both widths. -TEST_CASE("MultiplyModifier maxMultiplier saturates, never wraps to 0") { - mm::MultiplyModifier m; - m.multiplyX = 64; m.multiplyY = 64; m.multiplyZ = 16; // 65536 — wraps uint16 - CHECK(m.maxMultiplier() > 0); // never the wrapped 0 - // The product (65536) is ≥ the uint16 ceiling, so on a uint16 build it - // saturates to 65535; on uint32 it's the true 65536. Either way it's a large - // positive upper bound, never a small/zero value that would starve the LUT. - CHECK(m.maxMultiplier() >= 65535); -} - -// REGRESSION: an 8×8 multiply must emit all 64 tile positions, not be truncated -// to 8. The Layer's scratch buffer is sized to ModifierBase::kMaxFanout (64); a -// smaller buffer (the original physicals[8]) silently dropped 56 of the 64 tiles, -// so a 128×128 grid showed only 8 tiles instead of the full 8×8 = 64. -TEST_CASE("MultiplyModifier 8x8 emits all 64 tiles") { - mm::MultiplyModifier m; - m.multiplyX = 8; m.multiplyY = 8; m.multiplyZ = 1; - m.mirrorX = false; m.mirrorY = false; // pure tiling → 64 distinct positions - CHECK(m.maxMultiplier() == 64); - mm::nrOfLightsType physicals[64]; - mm::nrOfLightsType count = 0; - // 128 wide → tile edge 16; logical (0,0) maps to one position per 16×16 tile. - m.mapToPhysical(0, 0, 0, 128, 128, 1, physicals, count, 64); - CHECK(count == 64); -} - -// Fan-out never exceeds maxOut even if asked for more than the buffer holds. -TEST_CASE("MultiplyModifier respects maxOut clamp") { - mm::MultiplyModifier m; - m.multiplyX = 2; m.multiplyY = 2; m.multiplyZ = 2; // wants 8 - mm::nrOfLightsType physicals[8]; - mm::nrOfLightsType count = 0; - m.mapToPhysical(0, 0, 0, 128, 128, 8, physicals, count, 4); // cap at 4 - CHECK(count == 4); + CHECK(logicalSize(m, {16, 16, 1}) == mm::Coord3D{1, 16, 1}); // 16/16 = 1, not 16/64 = 0 } diff --git a/test/unit/light/unit_RandomMapModifier.cpp b/test/unit/light/unit_RandomMapModifier.cpp index 2550fe2..800c42f 100644 --- a/test/unit/light/unit_RandomMapModifier.cpp +++ b/test/unit/light/unit_RandomMapModifier.cpp @@ -11,22 +11,28 @@ #include -// RandomMapModifier remaps every light to another light — a 1:1 permutation — and -// reshuffles on a bpm timer. These tests pin the mapping properties directly via -// mapToPhysical (no Layer needed): the permutation is a true bijection, deterministic -// for a fixed generation, changes on reshuffle, and degrades safely on an empty grid. +// RandomMapModifier remaps every light to another — a 1:1 permutation — and reshuffles +// on a bpm timer. A static fold: modifyLogical folds a physical coord to its permuted +// logical coord (the box is unchanged). These pin the bijection, determinism, reshuffle, +// and the empty-grid degrade, plus the loop() beat behaviour through a real Layer. namespace { -// Map a single light; returns its destination index (outCount is always 1 for a remap). +// Flatten a coord to an index in a w×h box. +mm::nrOfLightsType flat(mm::Coord3D p, mm::lengthType w, mm::lengthType h) { + return static_cast(p.z) * w * h + + static_cast(p.y) * w + p.x; +} + +// Fold one light; returns its permuted destination index. mm::nrOfLightsType mapOne(mm::RandomMapModifier& m, mm::lengthType x, mm::lengthType y, mm::lengthType z, mm::lengthType w, mm::lengthType h, mm::lengthType d) { - mm::nrOfLightsType phys[4]; - mm::nrOfLightsType count = 0; - m.mapToPhysical(x, y, z, w, h, d, phys, count, 4); - CHECK(count == 1); - return phys[0]; + mm::Coord3D size{w, h, d}; + m.modifyLogicalSize(size); // stashes the box for the permutation + mm::Coord3D p{x, y, z}; + m.modifyLogical(p); + return flat(p, w, h); } // Collect the destination of every light in a w×h×d grid, in index order. @@ -42,23 +48,16 @@ std::vector mapAll(mm::RandomMapModifier& m, } // namespace -// A remap doesn't resize the logical box. -TEST_CASE("RandomMapModifier logicalDimensions are identity") { - mm::RandomMapModifier m; - mm::lengthType lw, lh, ld; - m.logicalDimensions(64, 32, 4, lw, lh, ld); - CHECK(lw == 64); - CHECK(lh == 32); - CHECK(ld == 4); -} - -TEST_CASE("RandomMapModifier maxMultiplier is 1") { +// A remap leaves the logical box unchanged. +TEST_CASE("RandomMapModifier does not resize the logical box") { mm::RandomMapModifier m; - CHECK(m.maxMultiplier() == 1); + mm::Coord3D size{64, 32, 4}; + m.modifyLogicalSize(size); + CHECK(size == mm::Coord3D{64, 32, 4}); } -// The core property: the mapping is a true bijection over [0, w*h*d) — every -// destination index appears exactly once (no gaps, no duplicates). +// The core property: a true bijection over [0, w*h*d) — every destination index +// appears exactly once (no gaps, no duplicates). TEST_CASE("RandomMapModifier is a bijection (every pixel mapped once)") { mm::RandomMapModifier m; const mm::lengthType w = 8, h = 8, d = 1; @@ -67,56 +66,47 @@ TEST_CASE("RandomMapModifier is a bijection (every pixel mapped once)") { REQUIRE(dests.size() == n); std::vector seen(n, 0); - for (auto dst : dests) { - REQUIRE(dst < n); // in range - seen[dst]++; - } - for (mm::nrOfLightsType i = 0; i < n; i++) - CHECK(seen[i] == 1); // each destination used exactly once + for (auto dst : dests) { REQUIRE(dst < n); seen[dst]++; } + for (mm::nrOfLightsType i = 0; i < n; i++) CHECK(seen[i] == 1); } -// A fresh modifier with the same generation produces the same permutation -// (deterministic seed → reproducible, which is what makes it testable). +// Deterministic seed → reproducible permutation (what makes it testable). TEST_CASE("RandomMapModifier is deterministic for a fixed generation") { mm::RandomMapModifier a, b; - auto da = mapAll(a, 8, 8, 1); - auto db = mapAll(b, 8, 8, 1); - CHECK(da == db); + CHECK(mapAll(a, 8, 8, 1) == mapAll(b, 8, 8, 1)); } -// Reshuffling (a beat) changes the mapping, and the result is still a bijection. +// Reshuffling (a beat) changes the mapping, still a bijection. TEST_CASE("RandomMapModifier reshuffle changes the mapping, stays a bijection") { mm::RandomMapModifier m; const mm::lengthType w = 8, h = 8, d = 1; const mm::nrOfLightsType n = static_cast(w) * h * d; auto before = mapAll(m, w, h, d); - m.reshuffle(); // what loop() does on a beat + m.reshuffle(); auto after = mapAll(m, w, h, d); - - CHECK(before != after); // a genuinely different permutation + CHECK(before != after); std::vector seen(n, 0); for (auto dst : after) { REQUIRE(dst < n); seen[dst]++; } - for (mm::nrOfLightsType i = 0; i < n; i++) CHECK(seen[i] == 1); // still a bijection + for (mm::nrOfLightsType i = 0; i < n; i++) CHECK(seen[i] == 1); } -// Robustness: an empty (0×0×0) grid must not crash — maxOut 0 yields no destination. -TEST_CASE("RandomMapModifier tolerates an empty grid") { +// Robustness: an empty (0×0×0) box must not crash — it folds to a no-op. +TEST_CASE("RandomMapModifier tolerates an empty box") { mm::RandomMapModifier m; - mm::nrOfLightsType phys[4]; - mm::nrOfLightsType count = 7; // sentinel - m.mapToPhysical(0, 0, 0, 0, 0, 0, phys, count, 0); - CHECK(count == 0); // nothing emitted, no crash + mm::Coord3D size{0, 0, 0}; + m.modifyLogicalSize(size); + mm::Coord3D p{0, 0, 0}; + CHECK(m.modifyLogical(p)); // no crash, never rejects } -// A resize (different box count) rebuilds the permutation to the new size, still a bijection. +// A resize (different box count) rebuilds the permutation to the new size. TEST_CASE("RandomMapModifier rebuilds on a grid resize") { mm::RandomMapModifier m; - auto small = mapAll(m, 4, 4, 1); // 16 lights - CHECK(small.size() == 16); + CHECK(mapAll(m, 4, 4, 1).size() == 16); - auto big = mapAll(m, 8, 8, 1); // 64 lights — forces a resize+rebuild + auto big = mapAll(m, 8, 8, 1); CHECK(big.size() == 64); const mm::nrOfLightsType n = 64; std::vector seen(n, 0); @@ -124,16 +114,10 @@ TEST_CASE("RandomMapModifier rebuilds on a grid resize") { for (mm::nrOfLightsType i = 0; i < n; i++) CHECK(seen[i] == 1); } -// loop() timer behaviour, exercised through a real Layer (the modifier reads the -// Layer's elapsed() clock and calls its onBuildState() on a beat). We observe the -// MODIFIER'S MAPPING (mapToPhysical) before vs after a timed run — not the -// rendered buffer, which an animating effect would change on its own and mask the -// signal. A beat reshuffles the permutation (mapping differs); bpm=0 freezes it -// (mapping identical) however far time advances. Mirrors the Layer + test-clock -// pattern in unit_Layer_phase_animation. +// loop() timer behaviour through a real Layer: the modifier reads the Layer clock and, +// on a beat, asks the Layer to rebuild (coalesced). We observe the MODIFIER'S MAPPING +// before vs after a timed run. A beat reshuffles (mapping differs); bpm 0 freezes it. namespace { -// Build a Layer with the modifier, run layer.loop() across total_ms of virtual -// time, and return whether the modifier's mapping changed over the run. bool mappingChangesOverMs(uint8_t bpm, int total_ms) { mm::Layouts layouts; mm::GridLayout grid; @@ -161,11 +145,9 @@ bool mappingChangesOverMs(uint8_t bpm, int total_ms) { } // namespace TEST_CASE("RandomMapModifier loop() reshuffles on a beat (bpm 60 ≈ 1/s)") { - // bpm 60 → one beat per 1000ms; 1500ms spans a boundary, so the mapping changes. CHECK(mappingChangesOverMs(60, 1500) == true); } TEST_CASE("RandomMapModifier loop() with bpm 0 never reshuffles (frozen)") { - // bpm 0 → no beat ever; the permutation stays put no matter how far time runs. CHECK(mappingChangesOverMs(0, 5000) == false); } diff --git a/test/unit/light/unit_RegionModifier.cpp b/test/unit/light/unit_RegionModifier.cpp index 97bd5df..bcdade3 100644 --- a/test/unit/light/unit_RegionModifier.cpp +++ b/test/unit/light/unit_RegionModifier.cpp @@ -3,79 +3,79 @@ #include "doctest.h" #include "light/modifiers/RegionModifier.h" -// RegionModifier carves the layer to a sub-rectangle of the physical box, given -// as percentages. logicalDimensions reports the region size; mapToPhysical -// translates a region-local cell to its box cell at the region's start offset. -// Half-open [start, end): abutting regions tile exactly. Defaults 0/100 = full box. - -static mm::nrOfLightsType mapOne(mm::RegionModifier& r, - mm::lengthType x, mm::lengthType y, mm::lengthType z, - mm::lengthType w, mm::lengthType h, mm::lengthType d, - mm::nrOfLightsType& count) { - mm::nrOfLightsType phys[8]; - count = 0; - r.mapToPhysical(x, y, z, w, h, d, phys, count, 8); - return count ? phys[0] : 0; +// RegionModifier carves the layer to a sub-rectangle of the physical box, given as +// percentages. modifyLogicalSize reports the region size; modifyLogical folds a +// PHYSICAL coord into region-local space (subtract the start offset) and rejects any +// physical light outside the region. Half-open [start, end): abutting regions tile. + +// The region size for a given physical box. +static mm::Coord3D regionSize(mm::RegionModifier& r, mm::Coord3D box) { + r.modifyLogicalSize(box); + return box; } -// Default region (0/100 on every axis) is the full box: identity dimensions. +// Fold a physical coord; returns whether it's inside the region, leaving region-local +// pos in p. `box` is the physical box; logical is the region size. +static bool fold(mm::RegionModifier& r, mm::lengthType x, mm::lengthType y, mm::lengthType z, + mm::Coord3D box, mm::Coord3D& p) { + mm::Coord3D logical = box; + r.modifyLogicalSize(logical); // stashes the start offset + region size + p = {x, y, z}; + return r.modifyLogical(p); +} + +// Default region (0/100 on every axis) is the full box: identity size, no rejection. TEST_CASE("RegionModifier default region is the full box") { mm::RegionModifier r; - mm::lengthType logW, logH, logD; - r.logicalDimensions(128, 64, 4, logW, logH, logD); - CHECK(logW == 128); - CHECK(logH == 64); - CHECK(logD == 4); - - // (0,0,0) → box index 0; the last cell → the last box index. - mm::nrOfLightsType count; - CHECK(mapOne(r, 0, 0, 0, 128, 64, 4, count) == 0); - CHECK(count == 1); + CHECK(regionSize(r, {128, 64, 4}) == mm::Coord3D{128, 64, 4}); + + mm::Coord3D p; + CHECK(fold(r, 0, 0, 0, {128, 64, 4}, p)); // physical (0,0,0) is in the region + CHECK(p == mm::Coord3D{0, 0, 0}); // and stays at region-local (0,0,0) } -// Half of an axis, half-open: end=50 on 128 → pixels 0..63 (width 64), not 65. +// Half of an axis, half-open: end=50 on 128 → region width 64, not 65. TEST_CASE("RegionModifier half region is exact (half-open end)") { mm::RegionModifier r; r.endX = 50; // 0..50% of 128 - mm::lengthType logW, logH, logD; - r.logicalDimensions(128, 64, 1, logW, logH, logD); - CHECK(logW == 64); // exact half, not 65 - CHECK(logH == 64); // untouched axis stays full - CHECK(logD == 1); + CHECK(regionSize(r, {128, 64, 1}) == mm::Coord3D{64, 64, 1}); // half x, full y } -// Two abutting regions tile a 128-wide axis with no overlap and no gap: -// 0..50 → [0,64), 50..100 → [64,128). The seam pixel 64 belongs to exactly one. +// Two abutting regions tile a 128-wide axis with no overlap and no gap. TEST_CASE("RegionModifier abutting regions tile exactly") { mm::RegionModifier left; left.startX = 0; left.endX = 50; mm::RegionModifier right; right.startX = 50; right.endX = 100; - mm::lengthType lw, h, d, rw; - left.logicalDimensions(128, 1, 1, lw, h, d); - right.logicalDimensions(128, 1, 1, rw, h, d); + const mm::lengthType lw = regionSize(left, {128, 1, 1}).x; + const mm::lengthType rw = regionSize(right, {128, 1, 1}).x; CHECK(lw == 64); CHECK(rw == 64); CHECK(lw + rw == 128); // no overlap, no gap - // Right region's local x=0 maps to box pixel 64 (where the left region ended). - mm::nrOfLightsType count; - CHECK(mapOne(right, 0, 0, 0, 128, 1, 1, count) == 64); - CHECK(count == 1); + // The seam: physical pixel 63 belongs to the LEFT region (region-local 63), pixel + // 64 belongs to the RIGHT (region-local 0). Each is in exactly one region. + mm::Coord3D p; + CHECK(fold(left, 63, 0, 0, {128, 1, 1}, p)); CHECK(p.x == 63); + CHECK_FALSE(fold(left, 64, 0, 0, {128, 1, 1}, p)); // 64 is past the left region + CHECK(fold(right, 64, 0, 0, {128, 1, 1}, p)); CHECK(p.x == 0); + CHECK_FALSE(fold(right, 63, 0, 0, {128, 1, 1}, p)); // 63 is before the right region } -// Region-local coordinates are translated by the start-pixel offset on each axis. -TEST_CASE("RegionModifier maps region-local cells to the offset box cell") { +// A physical coord inside the region folds to region-local (subtract the start pixel); +// a coord outside is rejected. +TEST_CASE("RegionModifier folds physical to region-local and rejects outside") { mm::RegionModifier r; r.startX = 50; r.endX = 100; // x: pixels 64..127 on a 128-wide axis r.startY = 0; r.endY = 50; // y: pixels 0..63 on a 128-tall axis - mm::nrOfLightsType count; - - // Local (0,0) → box (64, 0) → index 0*128 + 64 = 64. - CHECK(mapOne(r, 0, 0, 0, 128, 128, 1, count) == 64); - CHECK(count == 1); - - // Local (1,2) → box (65, 2) → index 2*128 + 65 = 321. - CHECK(mapOne(r, 1, 2, 0, 128, 128, 1, count) == 321); - CHECK(count == 1); + mm::Coord3D p; + + // Physical (64, 0) → region-local (0, 0). + CHECK(fold(r, 64, 0, 0, {128, 128, 1}, p)); CHECK(p == mm::Coord3D{0, 0, 0}); + // Physical (65, 2) → region-local (1, 2). + CHECK(fold(r, 65, 2, 0, {128, 128, 1}, p)); CHECK(p == mm::Coord3D{1, 2, 0}); + // Physical (0, 0) is left of the x-region → rejected. + CHECK_FALSE(fold(r, 0, 0, 0, {128, 128, 1}, p)); + // Physical (64, 100) is below the y-region → rejected. + CHECK_FALSE(fold(r, 64, 100, 0, {128, 128, 1}, p)); } // Rounding rule on a small panel: start floors, end ceils to an exclusive pixel. @@ -83,50 +83,68 @@ TEST_CASE("RegionModifier maps region-local cells to the offset box cell") { TEST_CASE("RegionModifier rounding on a small panel (floor start, ceil end)") { mm::RegionModifier r; r.startX = 33; r.endX = 66; - mm::lengthType logW, logH, logD; - r.logicalDimensions(4, 1, 1, logW, logH, logD); - CHECK(logW == 2); // pixels 1,2 + CHECK(regionSize(r, {4, 1, 1}).x == 2); // pixels 1,2 - mm::nrOfLightsType count; - CHECK(mapOne(r, 0, 0, 0, 4, 1, 1, count) == 1); // local 0 → box pixel 1 - CHECK(mapOne(r, 1, 0, 0, 4, 1, 1, count) == 2); // local 1 → box pixel 2 + mm::Coord3D p; + CHECK(fold(r, 1, 0, 0, {4, 1, 1}, p)); CHECK(p.x == 0); // physical 1 → region-local 0 + CHECK(fold(r, 2, 0, 0, {4, 1, 1}, p)); CHECK(p.x == 1); // physical 2 → region-local 1 + CHECK_FALSE(fold(r, 0, 0, 0, {4, 1, 1}, p)); // physical 0 is before the region + CHECK_FALSE(fold(r, 3, 0, 0, {4, 1, 1}, p)); // physical 3 is after the region } -// A region that rounds to nothing still gets a 1-pixel floor (never empties the -// layer). start 40 / end 41 on a 2-wide axis → would be 0 wide; clamped to 1. +// A region that rounds to nothing still gets a 1-pixel floor. TEST_CASE("RegionModifier never produces a zero-width region") { mm::RegionModifier r; r.startX = 40; r.endX = 41; - mm::lengthType logW, logH, logD; - r.logicalDimensions(2, 1, 1, logW, logH, logD); - CHECK(logW >= 1); + CHECK(regionSize(r, {2, 1, 1}).x >= 1); } -// Negative / >100 percentages are legal on the wire; the carve math clamps them -// into the box rather than reading off the ends. -TEST_CASE("RegionModifier clamps out-of-range percentages to the box") { +// OFF-SCREEN: a window slid half off the left edge keeps its FULL size (the effect +// renders at a fixed scale); only the visible half maps to physical lights. +// startX=-50 on 64 → window [−32, 32), span 64. Physical x 0..31 land at window-local +// 32..63 (the right half of the window — the visible part); the left half of the +// window (0..31) has no physical light, so it's dark. The effect isn't rescaled. +TEST_CASE("RegionModifier slides a window off-screen without rescaling") { mm::RegionModifier r; - r.startX = -50; r.endX = 200; // both out of [0,100] - mm::lengthType logW, logH, logD; - r.logicalDimensions(64, 1, 1, logW, logH, logD); - CHECK(logW == 64); // clamps to the full axis, not past it + r.startX = -50; r.endX = 50; // window [−32, 32) on a 64-wide axis + CHECK(regionSize(r, {64, 1, 1}).x == 64); // FULL window span, not clamped to 32 + + mm::Coord3D p; + // Physical x=0 → window-local 0 − (−32) = 32 (lands in the right half, visible). + CHECK(fold(r, 0, 0, 0, {64, 1, 1}, p)); CHECK(p.x == 32); + CHECK(fold(r, 31, 0, 0, {64, 1, 1}, p)); CHECK(p.x == 63); + // Physical x=32 would be window-local 64 ≥ span 64 → rejected (past the window). + CHECK_FALSE(fold(r, 32, 0, 0, {64, 1, 1}, p)); +} - mm::nrOfLightsType count; - CHECK(mapOne(r, 0, 0, 0, 64, 1, 1, count) == 0); // start clamps to pixel 0 +// A window entirely off the box maps NO lights — the layer goes dark on that axis, +// which is how an effect is moved completely out of view. The box still has a valid +// size (the effect renders), nothing just reaches the screen. +TEST_CASE("RegionModifier fully off-screen window renders nothing") { + mm::RegionModifier r; + r.startX = -100; r.endX = 0; // window [−64, 0) — entirely left of the box + CHECK(regionSize(r, {64, 1, 1}).x == 64); // full window span, the effect still renders + + mm::Coord3D p; + for (mm::lengthType x = 0; x < 64; x++) + CHECK_FALSE(fold(r, x, 0, 0, {64, 1, 1}, p)); // no physical light is in the window } -// Degenerate axes don't crash: a 1-wide axis stays 1, a 0-extent axis yields 0. -TEST_CASE("RegionModifier handles degenerate axes") { +// A window stretched WIDER than the box (start<0 and end>100) renders the full span; +// the box shows the middle slice. startX=-50,endX=150 on 64 → window [−32, 96), span 128. +TEST_CASE("RegionModifier window wider than the box") { mm::RegionModifier r; - mm::lengthType logW, logH, logD; - r.logicalDimensions(1, 0, 4, logW, logH, logD); - CHECK(logW == 1); - CHECK(logH == 0); - CHECK(logD == 4); + r.startX = -50; r.endX = 150; // window [−32, 96) on 64 + CHECK(regionSize(r, {64, 1, 1}).x == 128); // 2× the axis — a growing window + + mm::Coord3D p; + // Physical x=0 → window-local 32 (the middle of the 128-wide window is on-screen). + CHECK(fold(r, 0, 0, 0, {64, 1, 1}, p)); CHECK(p.x == 32); + CHECK(fold(r, 63, 0, 0, {64, 1, 1}, p)); CHECK(p.x == 95); } -// Never fans out — at most one destination, same family as CheckerboardModifier. -TEST_CASE("RegionModifier maxMultiplier is 1") { +// Degenerate axes don't crash: a 1-wide axis stays 1, a 0-extent axis yields 0. +TEST_CASE("RegionModifier handles degenerate axes") { mm::RegionModifier r; - CHECK(r.maxMultiplier() == 1); + CHECK(regionSize(r, {1, 0, 4}) == mm::Coord3D{1, 0, 4}); } diff --git a/test/unit/light/unit_RotateModifier.cpp b/test/unit/light/unit_RotateModifier.cpp index c038a87..91b0c7f 100644 --- a/test/unit/light/unit_RotateModifier.cpp +++ b/test/unit/light/unit_RotateModifier.cpp @@ -3,71 +3,49 @@ #include "doctest.h" #include "light/modifiers/RotateModifier.h" -// RotateModifier remaps each light to its rotated source. At angle 0 the map is -// identity; the modifier never fans out (outCount 0 or 1); the logical box is -// unchanged. These test the mapping directly via mapToPhysical (no Layer needed — -// the angle starts at 0, and loop() is what advances it). - -namespace { - -mm::nrOfLightsType mapOne(mm::RotateModifier& m, - mm::lengthType x, mm::lengthType y, - mm::lengthType w, mm::lengthType h, - mm::nrOfLightsType& count) { - mm::nrOfLightsType phys[4]; - count = 0; - m.mapToPhysical(x, y, 0, w, h, 1, phys, count, 4); - return count ? phys[0] : 0; -} - -} // namespace - -TEST_CASE("RotateModifier logicalDimensions are identity") { - mm::RotateModifier m; - mm::lengthType lw, lh, ld; - m.logicalDimensions(32, 16, 4, lw, lh, ld); - CHECK(lw == 32); - CHECK(lh == 16); - CHECK(ld == 4); +// RotateModifier is the one DYNAMIC modifier: it overrides modifyLive (per-frame +// backward map, dest→source via an explicit 2×2 rotation matrix) and reports +// hasModifyLive() so the Layer runs its live pass. At the initial angle (0) the +// rotation is identity. loop() advances the angle; a unit test without a Layer keeps +// it at 0, so these pin the angle-0 identity and the in-box invariants. + +// Apply the live remap to (x,y) in a w×h box; returns the source coord it samples. +static mm::Coord3D live(mm::RotateModifier& m, mm::lengthType x, mm::lengthType y, + mm::lengthType w, mm::lengthType h) { + mm::Coord3D p{x, y, 0}; + m.modifyLive(p, {w, h, 1}); + return p; } -TEST_CASE("RotateModifier maxMultiplier is 1") { +TEST_CASE("RotateModifier advertises a live (per-frame) modifier") { mm::RotateModifier m; - CHECK(m.maxMultiplier() == 1); + CHECK(m.hasModifyLive()); + CHECK(m.dimensions() == mm::Dim::D2); } -// At the initial angle (0) the rotation is identity — every light maps to itself. +// At the initial angle (0) the rotation matrix is the identity — every cell samples +// itself. TEST_CASE("RotateModifier at angle 0 is identity") { mm::RotateModifier m; const mm::lengthType w = 8, h = 8; - mm::nrOfLightsType count; for (mm::lengthType y = 0; y < h; y++) - for (mm::lengthType x = 0; x < w; x++) { - const mm::nrOfLightsType expected = static_cast(y) * w + x; - CHECK(mapOne(m, x, y, w, h, count) == expected); - CHECK(count == 1); - } + for (mm::lengthType x = 0; x < w; x++) + CHECK(live(m, x, y, w, h) == mm::Coord3D{x, y, 0}); } -// Every emitted destination is in range (no out-of-bounds index), and the count -// is always 0 or 1 (a remap never fans out). -TEST_CASE("RotateModifier emits at most one in-range destination") { +// z passes through (2D rotation) — a 3D coord's z is untouched. +TEST_CASE("RotateModifier leaves z untouched") { mm::RotateModifier m; - const mm::lengthType w = 8, h = 8; - const mm::nrOfLightsType n = static_cast(w) * h; - mm::nrOfLightsType count; - for (mm::lengthType y = 0; y < h; y++) - for (mm::lengthType x = 0; x < w; x++) { - mm::nrOfLightsType dst = mapOne(m, x, y, w, h, count); - CHECK(count <= 1); - if (count == 1) CHECK(dst < n); - } + mm::Coord3D p{3, 4, 2}; + m.modifyLive(p, {8, 8, 4}); + CHECK(p.z == 2); } -TEST_CASE("RotateModifier tolerates an empty grid") { +// An empty box doesn't divide-by-zero or wrap: the remap is a no-op-ish transform +// that the Layer's live pass then treats as out-of-box (dark), never a crash. +TEST_CASE("RotateModifier tolerates an empty box") { mm::RotateModifier m; - mm::nrOfLightsType phys[4]; - mm::nrOfLightsType count = 9; // sentinel - m.mapToPhysical(0, 0, 0, 0, 0, 0, phys, count, 0); - CHECK(count == 0); + mm::Coord3D p{0, 0, 0}; + m.modifyLive(p, {0, 0, 0}); // must not crash + CHECK(true); } From 2c04785a0bf17bc0af33cf63753539327c448034 Mon Sep 17 00:00:00 2001 From: ewowi Date: Fri, 26 Jun 2026 16:35:24 +0200 Subject: [PATCH 2/8] Process CodeRabbit PR #29 review; drop Multiply leftover-edge strip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses the CodeRabbit pass on PR #29 (composable modifiers): fixes a Multiply edge-duplication bug on non-divisible grids, moves the live-pass snapshot allocation off the render path, stops needless LUT rebuilds on live-only control edits, strengthens two modifier tests, and corrects the modifier docs to the shipped fold contract. Also folds in the kMaxChain removal (no predefined maxes) and the getting-started board→device wording. Hardware-verified on the classic Olimex ESP32 and S3; 16K perf unchanged. KPI: 16384lights | PC:384KB | tick:116/93/118/9/1/323/36/16/18/55/115/11/19us(FPS:8620/10752/8474/111111/1000000/3095/27777/62500/55555/18181/8695/90909/52631) | ESP32:1231KB | src:98(20354) | test:73(11451) | lizard:77w (ESP32 16K heavy-Noise live this session: classic 62,316us/16 FPS, S3 51,959us/~20 FPS — matches committed performance.md; KPI script's hardcoded port missed the live capture, values read from the perf_full write-back.) Core: - Layer: removed the fixed kMaxChain cap and chain[] array — the size-fold and counting-sort passes iterate the dynamic child list directly, so a trailing live modifier after many statics still sets hasLive_ (no predefined maxes). 🐇/👾 (both flagged the same cap) - Layer: moved liveScratch_ (re)allocation out of the render path into a cold ensureLiveScratch() called from onBuildState; the per-frame live pass now only memcpys, never allocates, and the scratch is freed when no modifier is live (no longer pinned). 🐇 Light domain: - MultiplyModifier: a non-divisible extent (e.g. 5 wide, multiply 2) leaves a leftover edge strip the tiles don't cover — modifyLogical now rejects those pixels instead of wrapping them, which had duplicated the edge. Stashes the covered extent per axis. 🐇 - RotateModifier: controlChangeTriggersBuildState=false — speed is applied live in modifyLive, so a speed edit no longer triggers a full LUT rebuild. 🐇 - RandomMapModifier: controlChangeTriggersBuildState=false — bpm only changes future reshuffle timing; removed a stale comment that denied the modifyLogicalSize override. 🐇 Tests: - unit_MultiplyModifier: regression for the leftover-edge drop on a non-divisible extent. 🐇 - unit_Layer_live_modifier: the coalesced-rebuild test now snapshots the full per-cell mapping, asserts it stays a bijection, and that a beat actually changes it (was only counting edges). 🐇 - unit_Layer_modifier_chain: the order-sensitivity fingerprint now encodes per-cell boundaries, so two mappings that flatten the same but group differently compare unequal. 🐇 Docs / CI: - architecture.md: rewrote the § Modifiers section to the static/live fold contract (dropped the never-existent transformCoord/transformLights); made the 🚧-marker definition present-tense. 🐇 - ModifierBase.md: corrected modifyLogical to the 1-arg bool signature (modifiers stash their own box). 🐇 - RegionModifier.md / RotateModifier.md: off-screen and rotation descriptions to present tense / contract level (matrix detail stays in the header). 🐇 - backlog-mixed.md: removed the self-contradictory "1:N fan-out LUT" framing — the cost is the destinations array, bounded by light count. 🐇 - gettingstarted.md: board→device wording (a board is a bare PCB; the assembled product is a device). - install/README.md: trimmed the stale "renders only its first enabled effect" rendering digression from the install doc. - 🐇 scenario_modifier_chain timing/network: accepted as-is — the scenario has no timing assertions (only informational observed drift), and NetworkSendDriver is the established pattern (11/13 light scenarios, incl. the sibling swap scenario); a one-off hermetic scenario would diverge from convention. - 🐇 Plan-20260626 stale 3-arg contract: accepted — saved plans are frozen design-intent records (CLAUDE.md keeps plans; divergence from what shipped is expected), not rewritten to match the code. --- docs/architecture.md | 10 +-- docs/backlog/backlog-mixed.md | 2 +- docs/gettingstarted.md | 26 +++--- docs/install/README.md | 13 ++- docs/moonmodules/light/ModifierBase.md | 4 +- .../light/modifiers/RegionModifier.md | 2 +- .../light/modifiers/RotateModifier.md | 4 +- src/light/layers/Layer.h | 87 ++++++++++--------- src/light/modifiers/MultiplyModifier.h | 24 +++-- src/light/modifiers/RandomMapModifier.h | 4 +- src/light/modifiers/RotateModifier.h | 4 + test/scenarios/light/scenario_perf_full.json | 64 +++++++------- test/unit/light/unit_Layer_live_modifier.cpp | 36 +++++--- test/unit/light/unit_Layer_modifier_chain.cpp | 10 ++- test/unit/light/unit_MultiplyModifier.cpp | 26 ++++++ 15 files changed, 187 insertions(+), 129 deletions(-) diff --git a/docs/architecture.md b/docs/architecture.md index 0cf3407..df5b0d1 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -400,12 +400,12 @@ See NoiseEffect / MetaballsEffect for the canonical pattern. Animation speed mus ## Modifiers -A modifier (MoonModule) lives inside a layer alongside its effects. Modifiers expose a virtual interface: the Layer calls modifier methods without knowing the concrete type (no `dynamic_cast`). +A modifier (MoonModule) lives inside a layer alongside its effects. Modifiers expose a virtual interface: the Layer calls modifier methods without knowing the concrete type (no `dynamic_cast`). A layer applies **all** its enabled modifiers as a chain, in child order — each a coordinate fold composed into one mapping (see [§ Layers and Layer](#layers-and-layer)). -A modifier can: +A modifier is a coordinate transform, applied in one of two ways (the fold contract is in [ModifierBase](moonmodules/light/ModifierBase.md)): -- Transform the mapping LUT via `transformCoord()`: rebuilt on the cold path, zero render cost. -- Transform light values via `transformLights()` on the hot path: per-light cost, enables dynamic animations like rotation. +- **Static** (`modifyLogicalSize` + `modifyLogical`): folded into the mapping during the cold-path build, so it costs nothing per frame (Region crop, Multiply tile/mirror, a mask). +- **Live** (`modifyLive`): a per-frame coordinate remap for animation (rotation), run only when an enabled modifier needs it — a static-only chain pays nothing. **Dimensionality** for modifiers defaults to `Dim::D3` (assumed to work in all three axes unless declared otherwise). Unlike for effects, this is purely advisory: the Layer doesn't extrude modifier output. It exists so the UI can render the 📏/🟦/🧊 chip on the card. **MultiplyModifier** is D3 (it has independent multiplyX/Y/Z + mirrorX/Y/Z toggles). @@ -522,7 +522,7 @@ The light domain plugs into the UI at three points: a fixed top-level tree (Layo ## What we leave undesigned -Genuinely open questions, *not* the same as a 🚧 marker. A 🚧 item is a committed design that simply isn't coded yet (two-core handover, clock sync, device-to-device light distribution); the items here are ones where the *design itself* isn't settled, deferred until a concrete need forces the decision: +Genuinely open questions, *not* the same as a 🚧 marker. A 🚧 item has a settled, committed design (two-core handover, clock sync, device-to-device light distribution) — code is written toward it; the items here are ones where the *design itself* is still open, deferred until a concrete need forces the decision: - **WiFi runtime disable**: today the eth-only build profile compiles WiFi out. Whether runtime gating should key off detected hardware presence, an explicit control, or a deviceModel-catalog field isn't decided; the eth-only build covers the need until one is. - **Mixing light types in one Layouts**: each layout child describes one light type (all LED strips, or all par lights). Whether a single Layouts container should hold mixed types (LED strips + par lights together), and how the channel layout would reconcile across them, isn't designed; one Layouts per light type is the current model. diff --git a/docs/backlog/backlog-mixed.md b/docs/backlog/backlog-mixed.md index cd1ec5e..c477d65 100644 --- a/docs/backlog/backlog-mixed.md +++ b/docs/backlog/backlog-mixed.md @@ -6,7 +6,7 @@ Forward-looking items whose work genuinely spans **both** the core and light dom ### MultiplyModifier mapping-LUT memory at large grids (investigation, re-verify on classic) -`scenario_perf_full` on the S3 (2026-06-17) measured the MultiplyModifier's cost across grid sizes. The finding, stated correctly: the modifier **reduces compute** (with the default 2×2 kaleidoscope the effect renders only the ¼-size logical quadrant — Noise+Multiply at 16K is 29,647µs vs 50,555µs for Noise alone), and its real cost is **memory** — the 1:N fan-out mapping LUT. Measured modifier heap cost on the S3: 16²→1.7KB, 32²→10.8KB, 64²→23.5KB, **128²(16K)→93KB** (the LUT destinations array; `nrOfLightsType` is `uint32_t` on a PSRAM board). On the S3's 8MB PSRAM this is trivial. (Composed modifiers shipped via the physical→logical fold build, which has no build-time fan-out — each physical light contributes ≤1 destination, so the destinations array is bounded by the real light count regardless of chain depth, removing the old fan-out memory concern.) +`scenario_perf_full` on the S3 (2026-06-17) measured the MultiplyModifier's cost across grid sizes. The finding, stated correctly: the modifier **reduces compute** (with the default 2×2 kaleidoscope the effect renders only the ¼-size logical quadrant — Noise+Multiply at 16K is 29,647µs vs 50,555µs for Noise alone), and its real cost is **memory** — the mapping LUT's destinations array. Measured modifier heap cost on the S3: 16²→1.7KB, 32²→10.8KB, 64²→23.5KB, **128²(16K)→93KB** (`nrOfLightsType` is `uint32_t` on a PSRAM board). On the S3's 8MB PSRAM this is trivial. Under the physical→logical fold build each physical light contributes ≤1 destination, so the destinations array is bounded by the real light count regardless of chain depth — there is no build-time fan-out. **This is NOT a no-PSRAM blocker** — 16K Noise + Multiply has run on a classic ESP32 (no PSRAM, 320KB internal) before at **10–20 FPS** (WiFi vs Ethernet), sending frames out over **ArtNet to a display, not physical LED drivers**. It works there because classic's `nrOfLightsType` is `uint16_t` (half the LUT size) and the modifier shrinks the logical render grid. So the action is **re-verify the working classic setup when a classic board is connected** (find the config — grid, mirror, ArtNet target — that reproduces the historical 10–20 FPS), not "fix an impossibility." Worth investigating only if that re-verification shows the LUT memory has regressed since: the destinations array is the obvious lever (it stores a `nrOfLightsType` per physical destination; a 2× kaleidoscope is 1:1 in *count* so the LUT need not store fan-out > the physical count — confirm it isn't over-allocating to `maxMultiplier()` when the effective fan-out is 1). Capture the classic numbers into performance.md's multi-board table first. diff --git a/docs/gettingstarted.md b/docs/gettingstarted.md index 0b057d4..fe7077e 100644 --- a/docs/gettingstarted.md +++ b/docs/gettingstarted.md @@ -5,7 +5,7 @@ straight from your web browser — no software to download, no command line. In few minutes you'll have lights running and the device on your network, and the device's own web interface open in your browser ready to play with. -This guide has two chapters. **Chapter 1** gets projectMM onto your board. +This guide has two chapters. **Chapter 1** gets projectMM onto your device. **Chapter 2** is a tour of the interface you land in afterwards, so you know what every part does and where to start building your own light show. @@ -32,26 +32,26 @@ Chrome or Edge, then plug your ESP32 into a USB port. Click **USB Port → Pick a port…**. Your browser shows a small list of connected devices — choose the one that appeared when you plugged in the ESP32. (Not sure -which? Unplug, look at the list, plug back in — the new entry is your board.) +which? Unplug, look at the list, plug back in — the new entry is your device.) ![Selecting the USB port](assets/gettingstarted/01-02-select-port.png) Once a port is chosen, the installer recognises the chip and tells you how many -boards match it, so you know you're on the right track before you pick one. +devices match it, so you know you're on the right track before you pick one. ![Port selected, chip detected](assets/gettingstarted/01-03-port-selected.png) ## 3. Pick your device -Choose your board from the **Device** picker. Each card shows a picture, the -chip, and what the board can do (LEDs, WiFi, a button, a microphone…); click +Choose your device from the **Device** picker. Each card shows a picture, the +chip, and what the device can do (LEDs, WiFi, a button, a microphone…); click **details** on any card to see exactly what it is and a link to its product page. ![Picking a device](assets/gettingstarted/01-04-pick-device.png) ![A device card with its details](assets/gettingstarted/01-05-device-details.png) -The little coloured pills are the board's capabilities, and the colour tells you +The little coloured pills are the device's capabilities, and the colour tells you how ready each one is: - 🟢 **Green** — set up and working the moment you install. This capability is @@ -88,7 +88,7 @@ it takes under a minute. ## 5. Get it on your network -What happens next depends on your board: +What happens next depends on your device: - **WiFi:** enter your network name and password when prompted, then **Connect**. (Click **Skip** to set WiFi up later from the device itself.) @@ -104,7 +104,7 @@ network. Click it. ![Device is online over WiFi](assets/gettingstarted/01-10-online-wifi.png) -You'll see this same "Device is online!" box however your board connected — over +You'll see this same "Device is online!" box however your device connected — over Ethernet, or when it rejoins a network it already knows: ![Online over Ethernet](assets/gettingstarted/01-11-online-ethernet.png) @@ -174,10 +174,10 @@ The top of the list is your device's "about" section — read-outs and connectio settings. You rarely need to touch these, but they're the first place to look if something seems off. -**System** — who this device is and how it's doing: its name, the board model, +**System** — who this device is and how it's doing: its name, the device model, uptime, frame rate, and live memory / storage bars. You may also see an **Audio** -module here — boards with a built-in mic come with it set up for you, and on any -board you can add it yourself (it's how sound-reactive effects hear the music). +module here — devices with a built-in mic come with it set up for you, and on any +device you can add it yourself (it's how sound-reactive effects hear the music). Audio is just the first of many: any sensor or input — from hardware or over the network — lives here as its own module, and we're adding more all the time. @@ -196,7 +196,7 @@ USB cable needed once it's on your network. **Network** — your connection: WiFi or Ethernet, signal strength, and the address others reach it at. The **Devices** section underneath finds other -projectMM boards on the same network, so a roomful of them can discover each +projectMM devices on the same network, so a roomful of them can discover each other. ![The Network module](assets/gettingstarted/02-07-UI-Network.png) @@ -261,5 +261,5 @@ keep going. - **Build from source** or target Teensy / Raspberry Pi: [building.md](building.md). Stuck, or something didn't work? Open an -[issue](https://github.com/MoonModules/projectMM/issues) — and tell us what board +[issue](https://github.com/MoonModules/projectMM/issues) — and tell us what device you used and where it stopped. diff --git a/docs/install/README.md b/docs/install/README.md index fb3fc5d..0c0571f 100644 --- a/docs/install/README.md +++ b/docs/install/README.md @@ -101,13 +101,12 @@ entry reads like a scenario's setup phase. The `deviceModel` identity is a unit control applies. **`replaceChildren`** (optional, on a container unit like `Layer` / `Layouts` / -`Drivers`): set it `true` to *replace* the container's boot-wired defaults instead of -adding alongside them. The inject is otherwise add-only, and a `Layer` renders only -its FIRST enabled effect/modifier — so a catalog entry that wants its own effect to -show (the testbench above swaps the default `NoiseEffect` for `AudioSpectrumEffect` + -`RandomMapModifier`) marks the container `replaceChildren`, which DELETEs the -container's current children before the entry's children are added. Without it, the -entry's effect would sit behind the boot default and never render. +`Drivers`): the inject is add-only by default, so an entry's children land *alongside* +the container's boot-wired defaults. Set `replaceChildren` `true` to DELETE the +container's current children first, so the entry's children are the only ones — used +when an entry wants its own effects to replace the defaults rather than stack with them +(the testbench above swaps the default `NoiseEffect` for `AudioSpectrumEffect` + +`RandomMapModifier`). **LED drivers are catalog-added, not boot-wired.** The only driver the firmware creates at boot is `Preview` (it needs the HTTP-server broadcaster the catalog diff --git a/docs/moonmodules/light/ModifierBase.md b/docs/moonmodules/light/ModifierBase.md index a1185df..7201236 100644 --- a/docs/moonmodules/light/ModifierBase.md +++ b/docs/moonmodules/light/ModifierBase.md @@ -6,8 +6,8 @@ Light-domain MoonModule subclass for modifiers. A modifier is a **coordinate tra A Layer builds its mapping by walking the **physical** lights and folding each through every enabled modifier in order — the composition `M₁∘M₂∘…∘Mₙ` collapsed into one mapping, so the per-frame render stays a single lookup. Three hooks, each a no-op by default so a modifier implements only what it needs: -- **`modifyLogicalSize(Coord3D& size)`** — static, build-time, once per rebuild in child order. Folds the logical box (Multiply divides it, Region crops it, a mask leaves it). The running `size` starts at the physical box; each modifier reshapes it. -- **`bool modifyLogical(Coord3D& pos, const Coord3D& phys, const Coord3D& logical)`** — static, build-time, per physical light in child order. Folds a physical coordinate into this stage's logical space in place. Returns **`false` to reject** — the physical light has no logical source (a mask drops it, a region light falls outside the crop). A bool, not a sentinel coord, so a later modifier's `% size` can't alias a sentinel back into range. +- **`modifyLogicalSize(Coord3D& size)`** — static, build-time, once per rebuild in child order. Folds the logical box (Multiply divides it, Region crops it, a mask leaves it). The running `size` starts at the physical box; each modifier reshapes it. A modifier that needs its box in the per-light fold **stashes** it here (the MoonLight `modifierSize` pattern), so the Layer keeps no per-stage box array. +- **`bool modifyLogical(Coord3D& pos)`** — static, build-time, per physical light in child order. Folds a coordinate into this stage's logical space in place, reading any box it needs from its own stash. Returns **`false` to reject** — the coordinate has no logical source (a mask drops it, a region light falls outside the crop, a Multiply leftover-edge pixel has no tile). A bool, not a sentinel coord, so a later modifier's `% size` can't alias a sentinel back into range. - **`modifyLive(Coord3D& pos, const Coord3D& logical)`** — dynamic, per-frame at render time. Remaps a coordinate without rebuilding the mapping (smooth rotation/scroll). The Layer runs this pass **only** when some enabled modifier reports `hasModifyLive()`, so a static-only chain pays nothing per frame — the render path stays at full speed (pay-for-what-you-use). It is a **backward** map: for each destination cell it returns the source cell to gather, so no destination is left torn. A beat-driven modifier (RandomMap reshuffles on a timer) sets a flag in `loop()`; the Layer polls `consumeNeedsRebuild()` across its modifiers and rebuilds the mapping **once** if any asks, coalescing several dynamic modifiers into a single rebuild. diff --git a/docs/moonmodules/light/modifiers/RegionModifier.md b/docs/moonmodules/light/modifiers/RegionModifier.md index 18f1e69..09117cb 100644 --- a/docs/moonmodules/light/modifiers/RegionModifier.md +++ b/docs/moonmodules/light/modifiers/RegionModifier.md @@ -23,7 +23,7 @@ Half-open makes abutting windows **tile exactly**: a `0..50` and a `50..100` lay ### Off-screen windows (move, don't rescale) -The window's **logical size is the full `start..end` span**, so the effect always renders at a fixed scale — moving `start` and `end` together slides the window without resizing it (like dragging an OS window). A physical light outside the window is dropped; window cells with no physical light under them (the off-screen part) stay dark. A window slid **entirely** off the box (e.g. `start=-100, end=0`) maps no lights — the layer goes dark, the way you move an effect completely out of view. This is what lets a future animation translate an effect across (and off) the panel without distorting it. +The window's **logical size is the full `start..end` span**, so the effect always renders at a fixed scale — moving `start` and `end` together slides the window without resizing it (like dragging an OS window). A physical light outside the window is dropped; window cells with no physical light under them (the off-screen part) stay dark. A window slid **entirely** off the box (e.g. `start=-100, end=0`) maps no lights — the layer goes dark, the way you move an effect completely out of view. Because the span is fixed, sweeping `start`/`end` translates an effect across and off the panel without distorting it. ## Effect on the pipeline diff --git a/docs/moonmodules/light/modifiers/RotateModifier.md b/docs/moonmodules/light/modifiers/RotateModifier.md index add27a7..2de85ff 100644 --- a/docs/moonmodules/light/modifiers/RotateModifier.md +++ b/docs/moonmodules/light/modifiers/RotateModifier.md @@ -8,9 +8,7 @@ A **dynamic modifier** that rotates the 2D image around its centre, turning cont ## How it works -The angle is a `uint8_t` (256 steps per turn). `modifyLive` is a **backward** map: for each destination cell it computes the **source** it samples, via the inverse rotation `R(-θ)` written as an explicit integer 2×2 matrix `[[c, s], [-s, c]]` applied to the centred coordinate. `c`/`s` come from the project's [`cos8`/`sin8`](../../core/Control.md) integer LUT (signed component `val − 128`, scaled by 128; the `>>7` divides back out), nearest-neighbour (no float). A source outside the box leaves that destination dark — the Layer's live pass clears it. Rotation is the canonical affine transform, which is why it's the matrix example; the non-affine modifiers (mask, tile) fold directly instead. - -2D only: the z axis passes through unchanged. +`loop()` advances the angle on the `speed` timer; rotation is applied each frame in the Layer's live pass (`modifyLive`), not baked into the mapping — so a `speed` change is a cheap live edit, no rebuild. A source that rotates outside the box leaves that destination dark. 2D only: the z axis passes through. The integer 2×2-matrix backward map is in the header. ## Prior art diff --git a/src/light/layers/Layer.h b/src/light/layers/Layer.h index 1fbf3b4..9d6019c 100644 --- a/src/light/layers/Layer.h +++ b/src/light/layers/Layer.h @@ -97,6 +97,7 @@ class Layer : public MoonModule { physicalDepth_ = dctx.maxZ + 1; rebuildLUT(); + ensureLiveScratch(); // size the live-pass snapshot here, on the cold path // Neutral status: the LOGICAL box the effects render into (width_×height_× // depth_) — this is what start/end region carving and modifiers reshape, @@ -156,6 +157,20 @@ class Layer : public MoonModule { if (hasLive_) applyLivePass(); } + // COLD path (called from onBuildState after rebuildLUT): (re)size the live-pass + // snapshot buffer to the current logical buffer, or free it when no modifier is live. + // Keeping the alloc here means applyLivePass() on the render path only memcpys — + // never allocates — and the scratch isn't held pinned once live modifiers are removed. + void ensureLiveScratch() { + const size_t bytes = hasLive_ ? buffer_.bytes() : 0; + if (bytes == liveScratchBytes_ && (bytes != 0) == (liveScratch_ != nullptr)) return; + if (liveScratch_) { platform::free(liveScratch_); liveScratch_ = nullptr; } + liveScratchBytes_ = 0; + if (bytes == 0) return; // no live modifier → no scratch held + liveScratch_ = static_cast(platform::alloc(bytes)); + if (liveScratch_) liveScratchBytes_ = bytes; // alloc-fail → applyLivePass no-ops, static frame shows + } + // Per-frame backward gather for live (animated) modifiers. For each DESTINATION // logical cell, fold its coordinate through the enabled live modifiers to the SOURCE // cell it samples, and copy that source pixel in — so no destination is left torn @@ -166,19 +181,10 @@ class Layer : public MoonModule { // modifiers participate (static ones are already baked into lut_). void applyLivePass() { uint8_t* buf = buffer_.data(); - if (!buf) return; + if (!buf || !liveScratch_) return; // scratch is sized on the cold path (ensureLiveScratch) const size_t cpl = channelsPerLight_; const size_t bytes = static_cast(width_) * height_ * depth_ * cpl; - if (bytes == 0) return; - // Lazy snapshot buffer, (re)sized to the logical buffer. Allocated only here, so - // a static-only chain never pays for it. An alloc failure skips the live pass - // (the static frame still shows — degraded, not crashed). - if (liveScratchBytes_ != bytes) { - if (liveScratch_) platform::free(liveScratch_); - liveScratch_ = static_cast(platform::alloc(bytes)); - liveScratchBytes_ = liveScratch_ ? bytes : 0; - } - if (!liveScratch_) return; + if (bytes == 0 || bytes > liveScratchBytes_) return; // hot path NEVER allocates std::memcpy(liveScratch_, buf, bytes); // snapshot the source frame const Coord3D logical{width_, height_, depth_}; @@ -261,36 +267,30 @@ class Layer : public MoonModule { bool lutSkipped() const { return lutSkipped_; } - // Max enabled static modifiers folded into one mapping. A deep stack is rare - // (a Region + a Multiply + maybe a mask); the cap bounds the per-light fold - // loop and the on-stack modifier array. Extra enabled modifiers past the cap - // are ignored (the chain still renders, just truncated) — robust, not a crash. - static constexpr uint8_t kMaxChain = 8; - // Precondition: physicalWidth_/Height_/Depth_ must be set (call from onBuildState) void rebuildLUT() { lutSkipped_ = false; clearStatus(); // re-evaluated below if a degrade path is taken - // Collect the enabled STATIC modifiers in child order. A dynamic modifier - // (Rotate, hasModifyLive) does NOT fold into the static mapping — it remaps - // per frame in Layer::loop's live pass — so it's excluded here. The mapping - // built below is the composition of the static folds M₁∘…∘Mₙ. - ModifierBase* chain[kMaxChain]; - uint8_t chainLen = 0; + // Fold the box through each enabled STATIC modifier in child order — no fixed + // chain array (Dynamic over fixed-size, architecture.md): the size pass here and + // the per-light fold below both iterate the Layer's own (dynamic, heap-grown) + // child list, filtering for enabled static modifiers inline, the way MoonLight's + // `for node : nodes` does. modifyLogicalSize mutates the running box AND lets the + // modifier stash its own output size (MoonLight's modifierSize cache), so in the + // per-light fold each modifier reads the box at ITS OWN stage from itself. + // A dynamic modifier (Rotate, hasModifyLive) is excluded — it remaps per frame in + // Layer::loop's live pass, not baked into the mapping. + uint8_t staticCount = 0; hasLive_ = false; - // Fold the box through each enabled static modifier in order. modifyLogicalSize - // mutates the running box AND lets the modifier stash its own output size (the - // way MoonLight's modifiers cache modifierSize) — so in the per-light fold each - // modifier reads the box at ITS OWN stage from itself, no per-stage array here. Coord3D box{physicalWidth_, physicalHeight_, physicalDepth_}; - for (uint8_t i = 0; i < childCount() && chainLen < kMaxChain; i++) { + for (uint8_t i = 0; i < childCount(); i++) { if (child(i)->role() != ModuleRole::Modifier || !child(i)->enabled()) continue; auto* m = static_cast(child(i)); if (m->hasModifyLive()) { hasLive_ = true; continue; } // dynamic: per-frame, not baked m->modifyLogicalSize(box); clampLogical(box); - chain[chainLen++] = m; + staticCount++; } // Final logical box = the running box after the last static modifier. @@ -306,7 +306,7 @@ class Layer : public MoonModule { // Fast path — no static modifiers, dense grid in natural order: box cell i // IS driver light i, so the mapping is the identity memcpy. This is the FPS // floor for the common case; keep it before any allocation. - if (chainLen == 0 && dense && isNaturalOrder()) { + if (staticCount == 0 && dense && isNaturalOrder()) { lut_.setIdentity(boxCount); allocateBuffer(boxCount); return; @@ -317,7 +317,7 @@ class Layer : public MoonModule { // N physical lights folding onto one logical cell IS the fan-out (Multiply), // so each physical light contributes at most ONE destination — maxDest is // exactly driverCount, no product, no overflow ceiling. - if (!buildFoldedLUT(chain, chainLen, logical, logicalCount, driverCount)) { + if (!buildFoldedLUT(logical, logicalCount, driverCount)) { // OOM in the fold build — degrade to identity (correct, not crash). lutSkipped_ = true; setStatus("modifier mapping skipped — not enough memory", Severity::Warning); @@ -359,8 +359,7 @@ class Layer : public MoonModule { // setMapping in logical order. Two forEachCoord passes + a counts/dests scratch, // all on the cold rebuild path; the hot-path read (forEachDestination) is unchanged. // Returns false on OOM (caller degrades to identity). - bool buildFoldedLUT(ModifierBase* const* chain, uint8_t chainLen, - const Coord3D& logical, + bool buildFoldedLUT(const Coord3D& logical, nrOfLightsType logicalCount, nrOfLightsType driverCount) { if (logicalCount == 0 || driverCount == 0) { lut_.setIdentity(0); return true; } @@ -379,23 +378,29 @@ class Layer : public MoonModule { for (nrOfLightsType i = 0; i <= logicalCount; i++) counts[i] = 0; // One callback does both passes. It folds the physical coord through the chain - // to a logical index (or kNoDriver if a modifier rejects it or it lands out of - // box — guarded, never trusted), then either counts it (pass A) or writes the - // driver index at the cell's cursor (pass B). Everything travels through the - // forEachCoord void* ctx, so the lambda captures nothing (it's a function ptr). + // (the Layer's own children — enabled static modifiers, in order, no array) to a + // logical index (or skips it if a modifier rejects it or it lands out of box — + // guarded, never trusted), then either counts it (pass A) or writes the driver + // index at the cell's cursor (pass B). Everything travels through the forEachCoord + // void* ctx, so the lambda captures nothing (it's a function ptr). struct FoldCtx { - ModifierBase* const* chain; uint8_t chainLen; + Layer* self; // for the dynamic child list (the modifier chain) Coord3D logical; nrOfLightsType logicalCount; // final box, for the flatten + guard nrOfLightsType* counts; // pass A: per-cell count. pass B: per-cell write cursor. nrOfLightsType* dests; // pass B only. bool scatter; - } fctx{chain, chainLen, logical, logicalCount, counts, dests, /*scatter=*/false}; + } fctx{this, logical, logicalCount, counts, dests, /*scatter=*/false}; auto onCoord = [](void* c, nrOfLightsType driverIdx, lengthType x, lengthType y, lengthType z) { auto* f = static_cast(c); Coord3D pos{x, y, z}; - for (uint8_t i = 0; i < f->chainLen; i++) - if (!f->chain[i]->modifyLogical(pos)) return; // rejected — no logical source + Layer* self = f->self; + for (uint8_t i = 0; i < self->childCount(); i++) { + if (self->child(i)->role() != ModuleRole::Modifier || !self->child(i)->enabled()) continue; + auto* m = static_cast(self->child(i)); + if (m->hasModifyLive()) continue; // dynamic: not in the static fold + if (!m->modifyLogical(pos)) return; // rejected — no logical source + } if (pos.x < 0 || pos.x >= f->logical.x || pos.y < 0 || pos.y >= f->logical.y || pos.z < 0 || pos.z >= f->logical.z) return; // defensive const nrOfLightsType li = diff --git a/src/light/modifiers/MultiplyModifier.h b/src/light/modifiers/MultiplyModifier.h index 7bf2eea..2cc1a03 100644 --- a/src/light/modifiers/MultiplyModifier.h +++ b/src/light/modifiers/MultiplyModifier.h @@ -47,24 +47,34 @@ class MultiplyModifier : public ModifierBase { // Logical box is the incoming box divided by the EFFECTIVE multiplier // (clamped to the axis extent — see eff()). On a 2D grid (size.z=1) any // multiplyZ clamps to 1, so depth stays 1 and Z multiplication is a no-op - // — you can't tile an axis more times than it has pixels. - size.x /= eff(multiplyX, size.x); - size.y /= eff(multiplyY, size.y); - size.z /= eff(multiplyZ, size.z); - tile_ = size; // stash the output tile size for the fold + // — you can't tile an axis more times than it has pixels. When the extent + // isn't divisible by the multiplier (e.g. 5 / 2 = 2), the tiles cover only + // `tile * mult` pixels; the leftover strip at the high edge is unmapped + // (covered_ records the covered extent so the fold can reject it). + const lengthType mX = eff(multiplyX, size.x), mY = eff(multiplyY, size.y), mZ = eff(multiplyZ, size.z); + size.x /= mX; size.y /= mY; size.z /= mZ; + tile_ = size; + covered_ = {static_cast(size.x * mX), static_cast(size.y * mY), + static_cast(size.z * mZ)}; } bool modifyLogical(Coord3D& pos) const override { + // A coord in the leftover edge strip (extent not divisible by the multiplier) + // has no tile — drop it, rather than wrap it and duplicate the edge. + if ((covered_.x > 0 && pos.x >= covered_.x) || + (covered_.y > 0 && pos.y >= covered_.y) || + (covered_.z > 0 && pos.z >= covered_.z)) return false; // Fold a coord into its tile: the tile index decides whether to reflect // (odd tile, mirror on), then wrap into the tile. Reads the stashed tile size. pos.x = foldAxis(pos.x, tile_.x, mirrorX); pos.y = foldAxis(pos.y, tile_.y, mirrorY); pos.z = foldAxis(pos.z, tile_.z, mirrorZ); - return true; // multiply never rejects — every light has a tile + return true; } private: - Coord3D tile_; // output tile size, stashed in modifyLogicalSize for the fold + Coord3D tile_; // output tile size, stashed in modifyLogicalSize for the fold + Coord3D covered_; // pixels the tiles actually cover (tile*mult); the leftover edge is dropped // Effective multiplier for an axis: the control value clamped to [1, extent]. // ≥1 avoids divide-by-zero; ≤extent because tiling more times than the axis diff --git a/src/light/modifiers/RandomMapModifier.h b/src/light/modifiers/RandomMapModifier.h index b8e67bb..85b2b96 100644 --- a/src/light/modifiers/RandomMapModifier.h +++ b/src/light/modifiers/RandomMapModifier.h @@ -36,7 +36,9 @@ class RandomMapModifier : public ModifierBase { controls_.addUint8("bpm", bpm, 0, 60); } - // A remap leaves the logical box unchanged (no modifyLogicalSize override). + // `bpm` only changes future reshuffle timing — the current permutation is unchanged, + // and loop() reads bpm live each tick, so a bpm edit needs no rebuild. + bool controlChangeTriggersBuildState(const char* /*controlName*/) const override { return false; } // A remap leaves the box unchanged but needs it for the permutation — stash it. void modifyLogicalSize(Coord3D& size) override { box_ = size; } diff --git a/src/light/modifiers/RotateModifier.h b/src/light/modifiers/RotateModifier.h index c6f2a60..abe70f5 100644 --- a/src/light/modifiers/RotateModifier.h +++ b/src/light/modifiers/RotateModifier.h @@ -41,6 +41,10 @@ class RotateModifier : public ModifierBase { controls_.addUint8("speed", speed, 1, 255); } + // `speed` only changes how fast the angle advances; rotation is applied live in + // modifyLive, not baked into the mapping, so a speed edit needs no rebuild. + bool controlChangeTriggersBuildState(const char* /*controlName*/) const override { return false; } + // Per-frame backward map: a destination logical cell `pos` is replaced by the // SOURCE cell it samples — the inverse rotation R(-θ) about the box centre. // `logical` is the box. Out-of-box sources stay out-of-box, so the Layer's live diff --git a/test/scenarios/light/scenario_perf_full.json b/test/scenarios/light/scenario_perf_full.json index d444512..dabd248 100644 --- a/test/scenarios/light/scenario_perf_full.json +++ b/test/scenarios/light/scenario_perf_full.json @@ -127,7 +127,7 @@ ], "free_heap": [ 137376, - 150116 + 150728 ], "max_alloc_block": [ 110592, @@ -135,7 +135,7 @@ ], "at": [ "2026-06-17", - "2026-06-25" + "2026-06-26" ] }, "esp32p4-eth": { @@ -216,7 +216,7 @@ ], "free_heap": [ 137676, - 150108 + 150304 ], "max_alloc_block": [ 110592, @@ -224,7 +224,7 @@ ], "at": [ "2026-06-17", - "2026-06-25" + "2026-06-26" ] }, "esp32p4-eth": { @@ -301,7 +301,7 @@ "esp32": { "tick_us": [ 101, - 116 + 138 ], "free_heap": [ 134032, @@ -313,7 +313,7 @@ ], "at": [ "2026-06-17", - "2026-06-25" + "2026-06-26" ] }, "esp32p4-eth": { @@ -387,7 +387,7 @@ }, "esp32": { "tick_us": [ - 293, + 277, 359 ], "free_heap": [ @@ -400,7 +400,7 @@ ], "at": [ "2026-06-17", - "2026-06-25" + "2026-06-26" ] }, "esp32p4-eth": { @@ -551,7 +551,7 @@ "esp32s3-n16r8": { "tick_us": [ 124, - 141 + 155 ], "free_heap": [ 8533659, @@ -563,7 +563,7 @@ ], "at": [ "2026-06-17", - "2026-06-25" + "2026-06-26" ] }, "esp32": { @@ -573,7 +573,7 @@ ], "free_heap": [ 134168, - 148380 + 148440 ], "max_alloc_block": [ 110592, @@ -581,7 +581,7 @@ ], "at": [ "2026-06-17", - "2026-06-25" + "2026-06-26" ] }, "esp32p4-eth": { @@ -648,7 +648,7 @@ }, "esp32s3-n16r8": { "tick_us": [ - 108, + 107, 139 ], "free_heap": [ @@ -661,7 +661,7 @@ ], "at": [ "2026-06-17", - "2026-06-25" + "2026-06-26" ] }, "esp32": { @@ -1071,7 +1071,7 @@ ], "free_heap": [ 136808, - 147516 + 147564 ], "max_alloc_block": [ 110592, @@ -1079,7 +1079,7 @@ ], "at": [ "2026-06-17", - "2026-06-25" + "2026-06-26" ] }, "esp32p4-eth": { @@ -1159,7 +1159,7 @@ }, "esp32": { "tick_us": [ - 1123, + 1078, 1150 ], "free_heap": [ @@ -1172,7 +1172,7 @@ ], "at": [ "2026-06-17", - "2026-06-25" + "2026-06-26" ] }, "esp32p4-eth": { @@ -1336,7 +1336,7 @@ "esp32s3-n16r8": { "tick_us": [ 735, - 871 + 909 ], "free_heap": [ 8541939, @@ -1348,7 +1348,7 @@ ], "at": [ "2026-06-17", - "2026-06-25" + "2026-06-26" ] }, "esp32": { @@ -1446,12 +1446,12 @@ }, "esp32": { "tick_us": [ - 3203, + 3182, 3263 ], "free_heap": [ 136808, - 147524 + 147592 ], "max_alloc_block": [ 110592, @@ -1459,7 +1459,7 @@ ], "at": [ "2026-06-17", - "2026-06-25" + "2026-06-26" ] }, "esp32p4-eth": { @@ -1539,7 +1539,7 @@ }, "esp32": { "tick_us": [ - 12745, + 12596, 13547 ], "free_heap": [ @@ -1552,7 +1552,7 @@ ], "at": [ "2026-06-17", - "2026-06-25" + "2026-06-26" ] }, "esp32p4-eth": { @@ -1795,7 +1795,7 @@ ], "free_heap": [ 8528551, - 8543959 + 8544039 ], "max_alloc_block": [ 94208, @@ -1803,7 +1803,7 @@ ], "at": [ "2026-06-17", - "2026-06-25" + "2026-06-26" ] }, "pc-macos": { @@ -1831,7 +1831,7 @@ ], "free_heap": [ 133408, - 143808 + 143852 ], "max_alloc_block": [ 110592, @@ -1839,7 +1839,7 @@ ], "at": [ "2026-06-17", - "2026-06-25" + "2026-06-26" ] }, "esp32p4-eth": { @@ -1919,7 +1919,7 @@ }, "esp32": { "tick_us": [ - 6739, + 6709, 6958 ], "free_heap": [ @@ -1928,11 +1928,11 @@ ], "max_alloc_block": [ 98304, - 98304 + 102400 ], "at": [ "2026-06-17", - "2026-06-25" + "2026-06-26" ] }, "esp32p4-eth": { diff --git a/test/unit/light/unit_Layer_live_modifier.cpp b/test/unit/light/unit_Layer_live_modifier.cpp index 7b7b3e5..2b24379 100644 --- a/test/unit/light/unit_Layer_live_modifier.cpp +++ b/test/unit/light/unit_Layer_live_modifier.cpp @@ -12,6 +12,7 @@ #include "platform/platform.h" #include +#include #include // The live (per-frame) modifier seam: Layer::loop() applies a live modifier's @@ -142,23 +143,32 @@ TEST_CASE("Layer coalesces rebuilds from two dynamic modifiers") { layouts.onBuildState(); layer.onBuildState(); - const std::size_t cells = static_cast(layer.lut().logicalCount()); - REQUIRE(cells == 64); - - auto destCount = [&]() { - std::size_t n = 0; - for (mm::nrOfLightsType li = 0; li < layer.lut().logicalCount(); li++) - layer.lut().forEachDestination(li, [&](mm::nrOfLightsType) { n++; }); - return n; + REQUIRE(layer.lut().logicalCount() == 64); + + // Snapshot the full mapping: each logical cell's single destination (a permutation, + // so exactly one each). Asserts the composition is a bijection AND lets us compare + // before vs after to prove a beat actually changed it. + auto mapping = [&]() { + std::vector dst(64, static_cast(-1)); + std::size_t total = 0; + for (mm::nrOfLightsType li = 0; li < 64; li++) + layer.lut().forEachDestination(li, [&](mm::nrOfLightsType d) { dst[li] = d; total++; }); + // a permutation: 64 destinations, all distinct, each in range + std::vector seen(64, 0); + for (auto d : dst) if (d < 64) seen[d]++; + bool bijection = (total == 64); + for (int s : seen) if (s != 1) bijection = false; + return std::make_pair(dst, bijection); }; - const std::size_t before = destCount(); + + auto [before, beforeOk] = mapping(); + CHECK(beforeOk); // the composed two-permutation mapping is itself a bijection // Advance past a beat boundary in small ticks (both modifiers tick each frame). for (uint32_t t = 1000; t <= 2500; t += 50) { mm::platform::setTestNowMs(t); layer.loop(); } mm::platform::setTestNowMs(0); - // Two composed permutations remain a permutation of the 64 cells — every light - // still maps somewhere exactly once (no crash, no lost/duplicated destinations). - CHECK(destCount() == before); - CHECK(destCount() == 64); + auto [after, afterOk] = mapping(); + CHECK(afterOk); // still a valid bijection after the coalesced rebuild + CHECK(before != after); // a beat actually reshuffled the composed mapping } diff --git a/test/unit/light/unit_Layer_modifier_chain.cpp b/test/unit/light/unit_Layer_modifier_chain.cpp index bb4e4a1..8a29004 100644 --- a/test/unit/light/unit_Layer_modifier_chain.cpp +++ b/test/unit/light/unit_Layer_modifier_chain.cpp @@ -91,12 +91,16 @@ TEST_CASE("Layer modifier order matters (A∘B ≠ B∘A)") { CHECK(a.layer.width() == 4); CHECK(b.layer.width() == 4); - // The mappings differ: collect each LUT's destination set per logical cell and - // compare. If order didn't matter they'd be identical. + // The mappings differ: fingerprint each LUT preserving the per-logical-cell + // grouping (a cell-boundary marker between cells), so two mappings that flatten to + // the same destination sequence but group differently still compare unequal. auto fingerprint = [](mm::Layer& L) { std::vector fp; - for (mm::nrOfLightsType li = 0; li < L.lut().logicalCount(); li++) + const mm::nrOfLightsType kCellBoundary = static_cast(-1); + for (mm::nrOfLightsType li = 0; li < L.lut().logicalCount(); li++) { L.lut().forEachDestination(li, [&](mm::nrOfLightsType d) { fp.push_back(d); }); + fp.push_back(kCellBoundary); // mark where each cell's run ends + } return fp; }; CHECK(fingerprint(a.layer) != fingerprint(b.layer)); diff --git a/test/unit/light/unit_MultiplyModifier.cpp b/test/unit/light/unit_MultiplyModifier.cpp index 674971e..2180eaa 100644 --- a/test/unit/light/unit_MultiplyModifier.cpp +++ b/test/unit/light/unit_MultiplyModifier.cpp @@ -21,6 +21,15 @@ static mm::Coord3D fold(mm::MultiplyModifier& m, mm::lengthType x, mm::lengthTyp return p; } +// Fold a coord and report whether it's kept (false = rejected, no tile). +static bool foldKeep(mm::MultiplyModifier& m, mm::lengthType x, mm::lengthType y, mm::lengthType z, + mm::Coord3D box, mm::Coord3D& out) { + mm::Coord3D logical = box; + m.modifyLogicalSize(logical); + out = {x, y, z}; + return m.modifyLogical(out); +} + static mm::Coord3D logicalSize(mm::MultiplyModifier& m, mm::Coord3D box) { m.modifyLogicalSize(box); return box; @@ -105,3 +114,20 @@ TEST_CASE("MultiplyModifier clamps a multiplier above the axis extent") { m.mirrorX = false; CHECK(logicalSize(m, {16, 16, 1}) == mm::Coord3D{1, 16, 1}); // 16/16 = 1, not 16/64 = 0 } + +// REGRESSION (🐇): a non-divisible extent leaves a leftover edge strip that the tiles +// don't cover — those pixels must be DROPPED, not wrapped back into a tile (which would +// duplicate the edge). 5-wide, multiply 2 → tile width 2, covers pixels 0..3; pixel 4 is +// the leftover and has no tile. +TEST_CASE("MultiplyModifier drops the leftover strip on a non-divisible extent") { + mm::MultiplyModifier m; + m.multiplyX = 2; m.multiplyY = 1; m.multiplyZ = 1; + m.mirrorX = false; + const mm::Coord3D box{5, 1, 1}; + CHECK(logicalSize(m, box).x == 2); // 5/2 = 2 tile width + + mm::Coord3D p; + CHECK(foldKeep(m, 0, 0, 0, box, p)); // tile 0 + CHECK(foldKeep(m, 3, 0, 0, box, p)); // tile 1, within the covered 0..3 + CHECK_FALSE(foldKeep(m, 4, 0, 0, box, p)); // leftover edge — dropped, NOT folded to 0 +} From df800673fdd1f483bdd2923051d1708d888cfb3c Mon Sep 17 00:00:00 2001 From: ewowi Date: Fri, 26 Jun 2026 22:58:34 +0200 Subject: [PATCH 3/8] =?UTF-8?q?MoonLive=20native-codegen=20spike=20(1a?= =?UTF-8?q?=E2=86=921b=E2=86=922)=20+=20installer=20no-erase=20defaults=20?= =?UTF-8?q?fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two features. (1) MoonLive: the first vertical slice of projectMM's live-script engine — author an effect as text, compile it to native machine code on the device, run it in the render tick. A scripted effect's `source` control ("fill(r,g,b);") is lexed/parsed/codegen'd to Xtensa on-device and rendered; editing it recompiles live. Proven on desktop (arm64/x86-64), ESP32-S3 (LX7) and classic ESP32 (LX6). (2) Installer: "apply device defaults" now lands the full config without erasing the flash — it clears every container it re-populates and adds Grid/Layer explicitly instead of assuming boot defaults, so a non-erased device gets the same tree an erase+flash produces. KPI: 16384lights | PC:402KB | tick:218/88/117/9/1/319/36/16/19/56/117/11/34us(FPS:4587/11363/8547/111111/1000000/3134/27777/62500/52631/17857/8547/90909/29411) | ESP32:1234KB | src:106(21130) | test:75(11674) | lizard:79w (MoonLive ran live this session on the S3 (~230 fps with the scripted effect) and classic (~1960 fps); the KPI script's hardcoded serial port missed the live ESP32 tick capture, so those figures are from the on-device API, not collect_kpi.) Core: - platform: new executable-memory seam — allocExec/freeExec/writeExec. ESP32 allocates IRAM (MALLOC_CAP_EXEC) and writeExec does the 32-bit-aligned store + esp_cache_msync I-cache sync; desktop mmaps a PROT_EXEC page (macOS arm64 needs MAP_JIT + a pthread_jit_write_protect_np toggle — the W^X wall). This is what a JIT needs: write-then-execute memory. - MoonLive (src/core/moonlive/): the domain-neutral engine — compile(source)/run(buf,n,cpl,t)/free(); holds an exec block, calls it through a function pointer. Includes only cstdint, the compiler/emitter seams, and the platform seam — never EffectBase/Buffer. - MoonLiveCompiler: a hand-written recursive-descent lexer + parser + codegen for the one-statement grammar fill(r,g,b);. Pure, deterministic, knows the language never an ISA; codegen drives the per-ISA emitter so the parser adds no codegen of its own (golden-bytes equivalence). - moonlive_emit (per-ISA backend, behind the platform line): hand-encoded fill + animated-fill routines — Xtensa in src/platform/esp32/, host arm64/x86-64 in src/platform/desktop/. Byte arrays copied verbatim from the assembler output (a hand-transcribed Xtensa byte caused a StoreProhibited crash mid-spike; regenerated from the raw blob, comment added). Light domain: - MoonLiveEffect (src/light/moonlive/): the thin EffectBase binding — carries the `source` text control, compiles it in onBuildState, runs the engine over buffer() each loop(), frees the exec block in teardown. controlChangeTriggersBuildState gates on "source" so an edit recompiles live; a parse error surfaces via the MoonModule status field (Severity::Error) and the layer goes dark — robust, no reboot. UI / Installer: - config-ops.js (new): planConfigOps(entry) — pure, exported, the ordered APPLY_OP plan (clearChildren pre-pass for every re-populated container that isn't itself added fresh, then add, then set). install-orchestrator.js uses it; the macOS https-CDN imports kept it un-importable in node, so the pure planner lives in its own module. - deviceModels.json: S3 + P4 testbench entries add Grid (parent Layouts) and Layer (parent Layers) explicitly and drop the now-obsolete replaceChildren on Layer — the entry is self-contained, so the no-erase path rebuilds Layouts/Layers from any prior state (the bug: drivers reappeared but layouts/layers didn't, because the entry assumed boot-default Grid/Layer). Tests: - unit_moonlive_fill: emit/allocExec/run path on the desktop host backend — fill + animated-from-t, zero-lights, recompile, free, allocExec round-trip. - unit_moonlive_compiler: golden-bytes equivalence (parsed fill == hand-emitted bytes), whitespace tolerance, 11 parser-error diagnostics (no crash on malformed input), live source recompile. - config-ops.test.mjs: every add-parent cleared unless added fresh, clears precede adds precede sets, dedup, malformed-input robustness, the S3 entry's add/clear set. Docs / CI: - MoonLiveEffect.md (new spec): the engine/compiler/emitter/binding pieces, the source control, the allocExec contract, the golden-bytes property, ESPLiveScript/ARTI-FX/MoonLight prior art. - ImprovProvisioningModule.md: clearChildren pre-pass now covers every add-parent, not just replaceChildren containers — and why (the AlreadyExists-skip on a non-erased device). - sdkconfig.defaults.esp32s3-n16r8: disable ESP_SYSTEM_MEMPROT + enable HEAP_HAS_EXEC_HEAP so MALLOC_CAP_EXEC exists (the standard ESP32-JIT config; safety for scripts is the staged bounds/watchdog, not the hardware W^X wall). - Plan-20260626 - MoonLive Stage 0 (native codegen spike).md: the approved plan record. --- CMakeLists.txt | 10 +- ...MoonLive Stage 0 (native codegen spike).md | 114 ++++++++++++++ docs/install/config-ops.js | 48 ++++++ docs/install/deviceModels.json | 6 +- docs/install/install-orchestrator.js | 31 +--- .../core/ImprovProvisioningModule.md | 2 +- .../light/moonlive/MoonLiveEffect.md | 34 ++++ esp32/main/CMakeLists.txt | 3 + esp32/sdkconfig.defaults.esp32s3-n16r8 | 10 ++ src/core/moonlive/MoonLive.cpp | 66 ++++++++ src/core/moonlive/MoonLive.h | 70 +++++++++ src/core/moonlive/MoonLiveCompiler.cpp | 133 ++++++++++++++++ src/core/moonlive/MoonLiveCompiler.h | 39 +++++ src/core/moonlive/moonlive_emit.h | 43 +++++ src/light/moonlive/MoonLiveEffect.h | 68 ++++++++ src/main.cpp | 2 + src/platform/desktop/moonlive_emit.cpp | 147 ++++++++++++++++++ src/platform/desktop/platform_desktop.cpp | 52 +++++++ src/platform/esp32/moonlive_emit.cpp | 92 +++++++++++ src/platform/esp32/platform_esp32.cpp | 46 ++++++ src/platform/platform.h | 18 +++ test/CMakeLists.txt | 2 + test/js/config-ops.test.mjs | 110 +++++++++++++ test/scenario_runner.cpp | 2 + test/unit/core/unit_moonlive_compiler.cpp | 112 +++++++++++++ test/unit/core/unit_moonlive_fill.cpp | 109 +++++++++++++ 26 files changed, 1340 insertions(+), 29 deletions(-) create mode 100644 docs/history/plans/Plan-20260626 - MoonLive Stage 0 (native codegen spike).md create mode 100644 docs/install/config-ops.js create mode 100644 docs/moonmodules/light/moonlive/MoonLiveEffect.md create mode 100644 src/core/moonlive/MoonLive.cpp create mode 100644 src/core/moonlive/MoonLive.h create mode 100644 src/core/moonlive/MoonLiveCompiler.cpp create mode 100644 src/core/moonlive/MoonLiveCompiler.h create mode 100644 src/core/moonlive/moonlive_emit.h create mode 100644 src/light/moonlive/MoonLiveEffect.h create mode 100644 src/platform/desktop/moonlive_emit.cpp create mode 100644 src/platform/esp32/moonlive_emit.cpp create mode 100644 test/js/config-ops.test.mjs create mode 100644 test/unit/core/unit_moonlive_compiler.cpp create mode 100644 test/unit/core/unit_moonlive_fill.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index faa90e5..0c7e158 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,13 +55,19 @@ add_library(mm_core STATIC src/core/HttpServerModule.cpp src/core/FilesystemModule.cpp src/core/Scheduler.cpp + src/core/moonlive/MoonLive.cpp + src/core/moonlive/MoonLiveCompiler.cpp ) target_include_directories(mm_core PUBLIC src/) target_link_libraries(mm_core PUBLIC mm_platform) # `add_dependencies(mm_core ui_embed)` is below, after the ui_embed target is defined. -# Platform library (desktop) -add_library(mm_platform src/platform/desktop/platform_desktop.cpp) +# Platform library (desktop). moonlive_emit.cpp is the desktop MoonLive backend (host-ISA +# codegen) — it lives here because emitted machine code is platform/ISA-specific. +add_library(mm_platform + src/platform/desktop/platform_desktop.cpp + src/platform/desktop/moonlive_emit.cpp +) target_include_directories(mm_platform PUBLIC src/ src/platform/desktop/) # Winsock for the desktop socket surface on Windows. target_link_libraries(mm_platform PUBLIC $<$:ws2_32>) diff --git a/docs/history/plans/Plan-20260626 - MoonLive Stage 0 (native codegen spike).md b/docs/history/plans/Plan-20260626 - MoonLive Stage 0 (native codegen spike).md new file mode 100644 index 0000000..cbdad8e --- /dev/null +++ b/docs/history/plans/Plan-20260626 - MoonLive Stage 0 (native codegen spike).md @@ -0,0 +1,114 @@ +# Plan — MoonLive Stage 0: native-codegen load-bearing spike + +> Approved plan record (CLAUDE.md *Plan before implementing*). Implements the first, smallest step of [livescripts-analysis-top-down.md](../../backlog/livescripts-analysis-top-down.md) — its Stage 0 "load-bearing spike", split one notch finer so the single novel hardware risk is isolated and proven before any compiler front-end is written. S3-only, bare-minimum assembler, near-zero language. + +## Goal + +Prove the one link nothing else can de-risk: **text-authored intent → native machine code we generated → `allocExec` executable memory → called every render tick → it writes the real producer buffer → visible on the S3, no crash.** Everything above that link (tokenizer, parser, IR, real codegen) is conventional, desktop-testable compiler work; this spike attacks only the part that can't be tested off-hardware. + +Visual acceptance: add a scripted effect on the S3 → the grid lights solid blue; then a per-frame hue sweep. Checkable by eye in the preview. + +## Why split Stage 0 into 1a/1b/2 + +The analysis doc's Stage 0 bundles two risks of very different character into one increment: +- **The novel, hardware-only risk:** emit Xtensa bytes → IRAM `allocExec` → cache-sync → call via the windowed-register ABI → write `buffer()`. Nothing in projectMM does this yet. +- **The conventional, low-risk, desktop-testable risk:** a hand-written recursive-descent front-end (lex → parse → emit) for one statement. + +Bundling them means a first-pixel failure is ambiguous ("bad codegen, or bad allocExec?"). So: + +- **1a/1b = the load-bearing spike** — hand-emit the bytes (no language at all), prove the scary link. The hand-emitted bytes are not throwaway: they become the **golden reference** the real codegen (step 2) must reproduce. +- **2 = the genuine Stage-0 vertical slice** — replace the hand-emitted array with a minimal real `lex("fill(blue)") → parse → emit the SAME bytes`, reusing the exact `allocExec` + binding + buffer surface 1a proved. + +This is "small in depth AND broad": depth = one statement; broad = the whole vertical (binding → allocExec → call → buffer), each piece minimal. + +## Decisions locked with the product owner + +- **First commit = hand-emitted bytes (Stage −1), then grow into the doc's Stage 0** (a tiny real front-end). Not one-shot Stage 0 — the two risks are separated so a first-pixel failure is unambiguous, and 1a is the test oracle for 2. +- **Solid colour first (1a), then per-frame hue (1b)** — a static fill answers "does our native code run and write the buffer" unambiguously; passing `elapsed()` then proves dynamic input reaches native code, a distinct fact worth isolating. +- **S3 only.** Xtensa backend + the desktop x86-64 backend (needed anyway for in-process unit tests); no other ISA this spike. +- **Aligns with the precompiled-effect surface.** The emitted function uses the exact `buffer()` + `nrOfLights()*channelsPerLight()` raw-write surface a compiled effect uses today, so swapping hand-bytes → codegen later changes nothing host-side. If the producer-buffer set/get surface wants to change (the RGB-into-buffer question the product owner flagged), this spike is the cheapest place to discover it. + +## Architecture placement (respecting the boundaries) + +Per [§3.9](../../backlog/livescripts-analysis-top-down.md) (domain-neutral engine core, thin binding) and the **platform boundary** hard rule (ISA codegen lives only in `src/platform//`): + +``` +src/platform/platform.h ← + allocExec/freeExec seam (declaration only) +src/platform/esp32/platform_esp32.cpp ← S3: heap_caps_malloc(MALLOC_CAP_EXEC) IRAM + cache sync +src/platform/desktop/platform_desktop.cpp ← desktop: mmap(PROT_READ|WRITE|EXEC) +src/core/moonlive/MoonLive.h ← neutral engine: holds an exec block, run(buf,n,cpl); compile() stubbed + (the hand-emitted byte arrays are ISA-specific → they live behind the platform line, + emitted by a tiny per-ISA function the engine calls; the engine itself stays neutral) +src/platform/esp32/moonlive_emit_xtensa.* ← the ~15 hand-coded Xtensa bytes (step 1) → real emit (step 2) +src/platform/desktop/moonlive_emit_host.* ← the x86-64 equivalent (so unit tests run in-process) +src/light/moonlive/MoonLiveEffect.h ← thin EffectBase binding; loop() = engine_.run(buffer(), nrOfLights(), channelsPerLight()) +docs/moonmodules/core/MoonLive.md ← spec (required: every new module .h needs one; check_specs enforces) +``` + +The engine (`src/core/moonlive/`) never sees `EffectBase`/`Buffer`/`ModuleRole`; the binding (`src/light/moonlive/`) translates. The engine reaches native code only through `platform::allocExec` + a per-ISA `emit*()` the platform layer provides. Dependency direction one-way: binding → engine → platform seam. + +## Steps + +### Step 1a — `allocExec` + hand-emitted solid-fill, called over the buffer + +**New platform seam** (`platform.h` + both backends): +```cpp +void* allocExec(size_t bytes); // executable memory; nullptr on failure (degrade, never crash) +void freeExec(void* ptr, size_t bytes); +``` +- **S3:** `heap_caps_malloc(bytes, MALLOC_CAP_EXEC)` (IRAM); after copying code in, flush/invalidate so the I-cache sees fresh bytes (the Xtensa cache-coherency step — the real unknown). Return nullptr if IRAM is exhausted. +- **Desktop:** `mmap(NULL, bytes, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON, -1, 0)`; `munmap` in freeExec. + +**Engine (`MoonLive.h`, neutral):** +```cpp +class MoonLive { +public: + bool compile(); // step 1: calls platform emit*() → fills an allocExec block. true on success. + void run(uint8_t* buf, nrOfLightsType n, uint8_t cpl); // calls the block as a fn ptr + void free(); + bool ok() const; const char* error() const; +}; +``` +`run` casts the exec block to `void(*)(uint8_t*, uint32_t, uint8_t)` and calls it. The emitted function's contract: fill `n*cpl` bytes of `buf` with a fixed BGR/RGB pattern, return. ~10–15 Xtensa instructions, hand-encoded with a comment naming each (`entry`, the loop, `s8i` store, `addi`, `bne`, `retw`). Desktop emit: the equivalent x86-64 (or, honestly, a C function whose address we hand back — the desktop path's job is to test the *binding + engine API*, not ISA encoding; the real ISA test is the Xtensa hardware run). + +**Binding (`MoonLiveEffect.h`):** a normal `EffectBase`; `setup()`→`engine_.compile()`; `loop()`→ `if (engine_.ok()) engine_.run(buffer(), nrOfLights(), channelsPerLight())`; `teardown()`→`engine_.free()`. Register in `main.cpp` + `scenario_runner.cpp`. + +**Acceptance:** desktop unit test — compile, run over a known buffer, assert every light == the fill colour. On the **S3**: add `MoonLiveEffect` to a Layer (via API), grid lights **solid blue**, fps stable, no crash, survives add/delete/replace. + +### Step 1b — per-frame hue (dynamic input reaches native code) + +Extend `run` to pass `elapsed()`; the emitted code derives a hue from it (simplest: write `elapsed()>>k` into the R channel, or call a host `hsv8(hue)` built-in — pick the cheaper to hand-encode). Proves a host-supplied per-frame value flows into the native code and changes the output. + +**Acceptance:** S3 grid **hue/brightness sweeps** per frame, smoothly, within budget. + +### Step 2 — a minimal real front-end emits the same bytes + +A bare recursive-descent slice in `src/core/moonlive/` (neutral): tokenize → parse **one statement** (`fill(blue);` or `fill();`) → the Xtensa/host emitter produces the **same** bytes step 1a hand-wrote. Still no `.ml` file (source is a hardcoded string or a `source` text control on the effect — TBD in the step, the file system + editor are explicitly later per the analysis doc). No IR yet — direct AST→emit is fine at one statement; the IR seam is introduced when a second statement/type forces it (a later stage), per *concrete-first*. + +**Acceptance:** the byte output of `compile("fill(blue)")` **equals** the step-1a golden array (a desktop unit test diffs them); the S3 still lights blue, now from parsed source. This is the no-language-leak proof at its cheapest. + +## Tests (every increment is a tested increment — §6 of the analysis) + +- `test/unit/core/unit_moonlive_emit.cpp` — desktop: `compile()` produces a non-empty exec block; `run()` over a known buffer yields the exact fill (golden buffer). Step 2 adds: parsed-source bytes == hand-emitted golden bytes. +- `test/unit/core/unit_platform_allocexec.cpp` — `allocExec` returns executable, writable memory; a trivial emitted "return 42" function called through it returns 42 (desktop); nullptr-on-exhaustion path degrades. +- `test/scenarios/light/scenario_moonlive_hello.json` — wire `MoonLiveEffect` as a real MoonModule: add, measure (buffer non-zero == fill colour), delete, re-add (robustness), at a couple grid sizes incl 0×0×0 (no crash). Runs in-process (desktop backend) on every commit; runs live on the S3 over REST for the ISA backend. +- Robustness: add/delete/replace the scripted effect in any order alongside compiled effects — the hard rule. + +## Validation + +- `ctest` + `uv run scripts/scenario/run_scenario.py` green at each step. +- **The hardware acceptance is the point:** S3 solid blue (1a), S3 hue sweep (1b), S3 blue-from-source (2) — eyeballed in the preview, the product owner confirms. +- Desktop build zero-warnings (`-Wall -Wextra -Werror`); platform-boundary check passes (all ISA bytes behind `src/platform/`). +- `check_specs.py` green — `docs/moonmodules/core/MoonLive.md` written (it is the module's home; carries the neutral engine API, the allocExec contract, the §3.9 boundary, and the ESPLiveScript/ARTI-FX/MoonLight prior-art block staged in the analysis doc). + +## Risks / watch-items + +- **Xtensa cache coherency after writing IRAM** — the genuine unknown. If the called bytes execute stale, the fill won't show; the fix is the correct flush/invalidate around the copy (Espressif's `MALLOC_CAP_EXEC` + cache-sync pattern). This is *why* 1a is hand-emitted: 15 known-correct bytes make this the only variable. +- **Windowed-register call ABI (`entry`/`retw`)** — the emitted function must open/close a register window correctly or the call corrupts the stack. Hand-encoding it first means the ABI is debugged in isolation. +- **IRAM scarcity on the S3** — exec blocks compete with WiFi/driver IRAM; `allocExec` must degrade (nullptr → effect reports "no memory" status, keeps running dark) not crash. Pin it in the allocExec test. +- **Desktop backend honesty** — the desktop path tests the binding/engine API and the front-end, NOT Xtensa encoding. The plan states this so the desktop green is never mistaken for ISA validation; the S3 run is the real gate. +- **Scope creep** — no IR, no file system, no editor, no second statement, no controls in this spike. Each is a later ladder rung. If a step wants one of those, it's out of scope and gets backlogged, not smuggled in. + +## Out of scope (explicit — later rungs) + +IR seam (introduced when a 2nd statement/type forces it), `.ml` files + file manager + editor window, the second ISA seam-proof (analysis Stage 0.5 — done after the front-end exists, not at hello-world), controls binding (Stage 1), math/time/2D/3D, RipplesEffect graduation. This plan is Stage 0 only. diff --git a/docs/install/config-ops.js b/docs/install/config-ops.js new file mode 100644 index 0000000..060ad30 --- /dev/null +++ b/docs/install/config-ops.js @@ -0,0 +1,48 @@ +// Plan the ordered APPLY_OP sequence that applies a device-model catalog entry's config. +// Pure (no I/O, no browser globals) so the install orchestrator can import it and a node +// unit test can exercise the ordering directly. The orchestrator sends each op this +// returns over serial (APPLY_OP) — or the HTTP path POSTs the equivalent. +// +// Op order: a clearChildren pre-pass, then per-module add, then per-control set. +// +// The clear pre-pass is the fix for "defaults only apply if I also erase the flash". On a +// NON-erased device the persisted tree already holds modules the entry re-adds (a prior +// flash's drivers under Drivers, Audio under System, …). The device-side add is +// idempotent-on-id (an existing name returns AlreadyExists and is skipped), so without +// clearing first a stale module lingers and a structural change never lands. So clear the +// user-editable children of every parent the entry adds into (plus any container flagged +// replaceChildren) BEFORE adding. The device's clearChildren preserves boot-wired children +// (Preview, Improv), so a parent's apparatus survives and only swappable pipeline content +// is replaced — the no-erase path then converges to the same tree an erase+flash produces. +export function planConfigOps(entry) { + const ops = []; + const modules = entry && Array.isArray(entry.modules) ? entry.modules : []; + + // Modules the entry adds fresh — no need to clear their children (a just-created + // module has none), so a parent that is itself added is dropped from the clear-set. + const addedIds = new Set( + modules.filter(m => m && typeof m.id === "string" && m.id && m.parent_id && m.type).map(m => m.id)); + + const clearParents = new Set(); + for (const m of modules) { + if (!m || typeof m !== "object") continue; + if (m.replaceChildren && typeof m.id === "string" && m.id) clearParents.add(m.id); + if (typeof m.parent_id === "string" && m.parent_id && m.type) clearParents.add(m.parent_id); + } + for (const parent of clearParents) { + if (addedIds.has(parent)) continue; // freshly added → nothing to clear + ops.push({ op: "clearChildren", parent }); + } + + for (const m of modules) { + if (!m || typeof m !== "object" || typeof m.id !== "string" || m.id === "") continue; + if (m.parent_id && m.type) ops.push({ op: "add", type: m.type, id: m.id, parent: m.parent_id }); + const controls = m.controls; + if (controls && typeof controls === "object") { + for (const [control, value] of Object.entries(controls)) { + ops.push({ op: "set", module: m.id, control, value }); + } + } + } + return ops; +} diff --git a/docs/install/deviceModels.json b/docs/install/deviceModels.json index 3df90a2..5f92dd5 100644 --- a/docs/install/deviceModels.json +++ b/docs/install/deviceModels.json @@ -570,6 +570,7 @@ { "type": "GridLayout", "id": "Grid", + "parent_id": "Layouts", "controls": { "width": 8, "height": 8 @@ -578,7 +579,7 @@ { "type": "Layer", "id": "Layer", - "replaceChildren": true + "parent_id": "Layers" }, { "type": "AudioSpectrumEffect", @@ -675,6 +676,7 @@ { "type": "GridLayout", "id": "Grid", + "parent_id": "Layouts", "controls": { "width": 8, "height": 8 @@ -683,7 +685,7 @@ { "type": "Layer", "id": "Layer", - "replaceChildren": true + "parent_id": "Layers" }, { "type": "NetworkReceiveEffect", diff --git a/docs/install/install-orchestrator.js b/docs/install/install-orchestrator.js index b938e72..adc91f9 100644 --- a/docs/install/install-orchestrator.js +++ b/docs/install/install-orchestrator.js @@ -45,6 +45,7 @@ import { buildImprovFrame, encodeApplyOpFrames, } from "./improv-frame.js"; +import { planConfigOps } from "./config-ops.js"; // --------------------------------------------------------------------------- // Manifest parser @@ -174,11 +175,10 @@ async function pushDefaultsOverSerial(port, board, applyDefaults, trackProgress, return await sendConfigOverSerial(port, board, onLog); } -// Push a device-model's whole catalog config to the device over serial. Walks the -// SAME deviceModels.json entry the HTTP path used (replaceChildren pre-pass, then -// per-module add + per-control set) but emits APPLY_OP ops instead of HTTP requests -// — so the defaults apply during provisioning with no HTTP and no browser handoff. -// Returns true if the entry was found + pushed, false if no catalog entry for `board`. +// Push a device-model's whole catalog config to the device over serial. Walks the SAME +// deviceModels.json entry the HTTP path used (see planConfigOps) but emits APPLY_OP ops +// instead of HTTP requests — so the defaults apply during provisioning with no HTTP and +// no browser handoff. Returns true if the entry was found + pushed, false if none. async function sendConfigOverSerial(port, board, onLog) { let entry; try { @@ -191,25 +191,8 @@ async function sendConfigOverSerial(port, board, onLog) { return false; } if (!entry) return false; - const modules = Array.isArray(entry.modules) ? entry.modules : []; - // replaceChildren pre-pass: clear a container's boot defaults before its catalog - // children are added (so the entry's effects replace, not stack). - for (const m of modules) { - if (m && m.replaceChildren && typeof m.id === "string" && m.id) { - await sendApplyOpFrame(port, { op: "clearChildren", parent: m.id }); - } - } - for (const m of modules) { - if (!m || typeof m !== "object" || typeof m.id !== "string" || m.id === "") continue; - if (m.parent_id && m.type) { - await sendApplyOpFrame(port, { op: "add", type: m.type, id: m.id, parent: m.parent_id }); - } - const controls = m.controls; - if (controls && typeof controls === "object") { - for (const [control, value] of Object.entries(controls)) { - await sendApplyOpFrame(port, { op: "set", module: m.id, control, value }); - } - } + for (const op of planConfigOps(entry)) { + await sendApplyOpFrame(port, op); } if (onLog) onLog(`[orchestrator] applied ${board} defaults over serial`); return true; diff --git a/docs/moonmodules/core/ImprovProvisioningModule.md b/docs/moonmodules/core/ImprovProvisioningModule.md index ad08b2f..8ccf464 100644 --- a/docs/moonmodules/core/ImprovProvisioningModule.md +++ b/docs/moonmodules/core/ImprovProvisioningModule.md @@ -27,7 +27,7 @@ Both transports speak the same Improv-WiFi serial protocol — frames of `IMPROV - `GET_WIFI_NETWORKS` — runs a synchronous WiFi scan, returns up to 10 SSIDs with RSSI + auth flag. **Rejected while STA is connected** (see below). - `WIFI_SETTINGS` — writes SSID + password to NetworkModule via `setWifiCredentials`, polls `wifiStaConnected()` for up to 30 s, replies with success (carrying `http:///`) or `ERROR_UNABLE_TO_CONNECT`. - `SET_TX_POWER` (vendor, `0xFD`) — payload `[1][dBm]` (0–21; 0 lifts the cap); persists + applies `Network.txPowerSetting` **before** any association attempt. This is the provisioning escape hatch for boards whose LDO browns out at full TX power (a weak LDO / marginal supply): the cap MUST land before the first association or the board fails WiFi auth at 20 dBm before it is ever online. `improv_provision.py --tx-power 8` (and the MoonDeck flow) sends this ahead of the credentials; error `0x81` on an out-of-range value. -- `APPLY_OP` (vendor, `0xFC`) — **"Improv = REST over serial".** Carries ONE REST operation as JSON, the same shape an HTTP `POST /api/modules` / `/api/control` body has: `{"op":"add","type":…,"id":…,"parent":…}` / `{"op":"set","module":…,"control":…,"value":…}` / `{"op":"clearChildren","parent":…}`. On the device the op is routed to `HttpServerModule`'s apply-core — the *exact same code* the HTTP handlers call — so a REST call over the network and an `APPLY_OP` over serial execute identically. (One schema caveat: the serial `add` op names the parent `parent`, while the HTTP `POST /api/modules` body names it `parent_id`. Both feed the one `applyAddModule()` core but the two transports parse different keys, so an HTTP payload is **not** a drop-in `APPLY_OP` — rename `parent_id` → `parent`. The serial key stays terse because every byte counts against the 128-byte frame.) The web installer pushes a device-model's whole catalog config this way during provisioning (a `clearChildren` pre-pass for any `replaceChildren` container, then an `add` per module + a `set` per control — **the deviceModel identity is just one of those `set` ops** on `System.deviceModel`, validated by that control's per-control validator like any other write), so the defaults apply **over the serial port the installer already owns during the flash** — which is what lets the HTTPS installer page configure an `http://` device that a browser fetch can't reach (mixed-content). Frame payload: `[0xFC][seq][last][chunk]` — most ops are one frame; a long value (e.g. a big `pins` list) chunks across frames into a reassembly buffer, applied on the device's main loop when `last=1`. Single-buffered: the device errors a new op (`0x82`) while the previous is unconsumed. The installer paces ops open-loop (a fixed delay between frames sized to the worst-case consume window) rather than reading the ack back, so a lost op is improbable rather than impossible; each op is idempotent, so a re-flash re-applies cleanly. (To re-apply a model to an already-running device, use MoonDeck on the LAN, which talks plain HTTP REST with no mixed-content barrier.) +- `APPLY_OP` (vendor, `0xFC`) — **"Improv = REST over serial".** Carries ONE REST operation as JSON, the same shape an HTTP `POST /api/modules` / `/api/control` body has: `{"op":"add","type":…,"id":…,"parent":…}` / `{"op":"set","module":…,"control":…,"value":…}` / `{"op":"clearChildren","parent":…}`. On the device the op is routed to `HttpServerModule`'s apply-core — the *exact same code* the HTTP handlers call — so a REST call over the network and an `APPLY_OP` over serial execute identically. (One schema caveat: the serial `add` op names the parent `parent`, while the HTTP `POST /api/modules` body names it `parent_id`. Both feed the one `applyAddModule()` core but the two transports parse different keys, so an HTTP payload is **not** a drop-in `APPLY_OP` — rename `parent_id` → `parent`. The serial key stays terse because every byte counts against the 128-byte frame.) The web installer pushes a device-model's whole catalog config this way during provisioning (a `clearChildren` pre-pass for every parent the entry adds into — plus any `replaceChildren` container — then an `add` per module + a `set` per control — **the deviceModel identity is just one of those `set` ops** on `System.deviceModel`, validated by that control's per-control validator like any other write), so the defaults apply **over the serial port the installer already owns during the flash** — which is what lets the HTTPS installer page configure an `http://` device that a browser fetch can't reach (mixed-content). Clearing every add-parent (not only `replaceChildren` containers) is what makes "apply device defaults" land the same way with or without an erase: the device-side `add` is idempotent on the module id, so on a non-erased device a persisted child of the same name would otherwise survive and the re-add be skipped. Frame payload: `[0xFC][seq][last][chunk]` — most ops are one frame; a long value (e.g. a big `pins` list) chunks across frames into a reassembly buffer, applied on the device's main loop when `last=1`. Single-buffered: the device errors a new op (`0x82`) while the previous is unconsumed. The installer paces ops open-loop (a fixed delay between frames sized to the worst-case consume window) rather than reading the ack back, so a lost op is improbable rather than impossible; each op is idempotent, so a re-flash re-applies cleanly. (To re-apply a model to an already-running device, use MoonDeck on the LAN, which talks plain HTTP REST with no mixed-content barrier.) **The serial listener runs on every ESP32 target, including Ethernet-only builds** (`--firmware esp32-eth*`). An eth-only build compiles in the vendor RPCs (`SET_TX_POWER`, `APPLY_OP`) plus `GET_CURRENT_STATE` / `GET_DEVICE_INFO`, so the web installer pushes a device-model's config over serial to an eth device exactly as it does to a WiFi one; the WiFi-provisioning RPCs (`WIFI_SETTINGS`, `GET_WIFI_NETWORKS`) build only on WiFi targets, where there's an STA to provision and `esp_wifi_*` is available. On eth, `GET_CURRENT_STATE` reports "provisioned" + the device URL from the Ethernet link (`platform::ethConnected()` / `ethGetIPv4`) instead of the WiFi STA. diff --git a/docs/moonmodules/light/moonlive/MoonLiveEffect.md b/docs/moonmodules/light/moonlive/MoonLiveEffect.md new file mode 100644 index 0000000..5972c8b --- /dev/null +++ b/docs/moonmodules/light/moonlive/MoonLiveEffect.md @@ -0,0 +1,34 @@ +# MoonLive + +MoonLive is projectMM's **live-script engine** — author an effect (or layout, modifier, driver, core rule) as text and run it on a running device, compiled to native machine code so it executes at near-hand-written speed in the render hot path. The full design is the forward-looking analysis [livescripts-analysis-top-down.md](../../../backlog/livescripts-analysis-top-down.md); this page documents what currently ships. + +**Status: Stage 2 (first language slice).** A scripted effect now carries its **script source** as an editable, persisted text control, and a real front-end (lexer → parser → codegen) compiles it to native code on the next tick. The grammar is one statement — `fill(r, g, b);` — the smallest real language: no expressions, variables, or control flow yet, but the compiler shape is genuine and grows rule by rule. Earlier increments proved the load-bearing path beneath it: native code MoonLive generated runs on real Xtensa, in the render tick, writing the real buffer, animating from a per-frame value, without a crash. The front-end's headline property is **golden-bytes equivalence** — parsing `fill(0,0,255)` produces byte-for-byte the same machine code the hand-written emitter does, so the parser provably adds no codegen of its own. + +## Controls + +- `source` — the script text (default `fill(0, 0, 255);` — solid blue). Editing it recompiles live: a valid script swaps in on the next tick; a parse error shows its diagnostic in the module status and the layer goes dark until fixed (the script-editor loop, robust + no reboot). + +## Pieces + +- **`MoonLive`** (`src/core/moonlive/MoonLive.h/.cpp`) — the **domain-neutral engine core**. Owns a block of executable memory; `compile(source)` runs the front-end and places the emitted code, `run(buf, nLights, cpl, t)` calls it, `free()`/`ok()`/`error()` round out the lifecycle. Includes only ``, the compiler/emitter seams, and the platform seam — never `EffectBase`, `Buffer`, or any projectMM type. +- **`MoonLiveCompiler`** (`src/core/moonlive/MoonLiveCompiler.h/.cpp`) — the **platform-independent front-end**: a hand-written recursive-descent lexer + parser for the `fill(r,g,b);` grammar, plus codegen that drives the per-ISA emitter. Pure (source in, bytes out, deterministic), so it unit-tests with no hardware. Knows the *language*, never an ISA. +- **`emitFill`** (`src/core/moonlive/moonlive_emit.h`) — the **per-ISA backend seam**. A neutral declaration; the implementation is the ISA's machine code, behind the platform boundary: `src/platform/esp32/moonlive_emit.cpp` (Xtensa) and `src/platform/desktop/moonlive_emit.cpp` (host arm64 / x86-64). The engine and front-end never branch on ISA — they ask for bytes and run them. This is the IR/backend seam at its first increment (no IR yet; one routine). +- **`MoonLiveEffect`** (`src/light/moonlive/MoonLiveEffect.h`) — the **thin binding**: a first-class `EffectBase` carrying the `source` control, whose `loop()` delegates to the engine over its own `buffer()`. The engine/binding split (the engine is projectMM-agnostic; the binding is the only coupled layer) is what lets projectMM be a clean library later. + +## Cross-domain wiring + +- **The executable-memory seam** is new platform surface (`src/platform/platform.h`): `allocExec(size)` / `freeExec(ptr,size)` allocate memory the CPU can *fetch* from (ESP32 IRAM via `MALLOC_CAP_EXEC`; an `mmap` `PROT_EXEC` page on desktop, with macOS-arm64 `MAP_JIT` + a write-protect toggle), and `writeExec(dst,src,len)` copies emitted code in safely — on ESP32 that means 32-bit-aligned IRAM stores plus an instruction-cache sync so the core fetches fresh code, not stale cache. All ISA/cache quirks live behind these three functions; the engine stays target-agnostic. +- **The producer buffer**: the emitted routine writes the same `buffer()` + `nrOfLights()*channelsPerLight()` surface a compiled effect writes — the identity-mapping fast path, no intermediate copy. The binding hands the engine `(buffer(), nrOfLights(), channelsPerLight())` each tick. +- A failed compile (no executable memory) leaves the effect `!ok()`: it renders dark and reports the error in its module status — the device keeps running (robustness, no reboot). + +## Prior art + +MoonLive's native-codegen approach — compile a small C-like language straight to machine code and call it as a function, so a live-authored effect runs at near hand-written speed — was pioneered by **Yves Bazin (hpwit)** in **[ESPLiveScript](https://github.com/hpwit/ESPLiveScript)**: a from-scratch tokenizer, parser, and Xtensa code generator that drives a 12,288-LED panel at ~85 fps where interpreted languages (Lua, Gravity) managed 3–10. That result is what makes "go native, not interpreted" the right call, and ESPLiveScript is the reference MoonLive is built against — studied closely, credited, and written fresh against projectMM's architecture, never copied, per [*Industry standards, our own code*](../../../../CLAUDE.md#principles). The live-scripting idea in this ecosystem also descends from **ARTI-FX / ARTI** (the interpreted-effects runtime in WLED MoonModules), which proved the load-a-script-and-run-it-live loop end to end. The host-binding surface (`setRGB`/`setRGBXY`/`setRGBXYZ`) is modelled on the **MoonLight** [effects tutorial](https://moonmodules.org/MoonLight/moonlight/effects-tutorial/). + +## Tests + +[unit_moonlive_fill](../../../../test/unit/core/unit_moonlive_fill.cpp) runs the engine path in-process on the desktop host backend: `emitFill`/`emitAnimatedFill` produce a non-empty routine (and reject a too-small buffer), `compile` + `run` fill a buffer with the chosen colour, the animated routine derives its colour from the per-frame `t`, zero-lights writes nothing, recompile swaps the colour, `free` returns to `!ok()`, and `allocExec`/`writeExec`/`freeExec` round-trip a callable block. [unit_moonlive_compiler](../../../../test/unit/core/unit_moonlive_compiler.cpp) pins the front-end: the **golden-bytes equivalence** (parsed `fill(r,g,b)` == hand-emitted bytes), whitespace tolerance, every parser diagnostic (wrong name, bad arity, out-of-range, missing punctuation, trailing junk — no crash on any malformed input), and the live source-recompile swap. The Xtensa backend is validated by the live S3 run (a `MoonLiveEffect` on a Layer lights the grid from its `source`), which the desktop tests can't reach. + +## Source + +[MoonLive.h](../../../../src/core/moonlive/MoonLive.h) · [MoonLiveCompiler.h](../../../../src/core/moonlive/MoonLiveCompiler.h) · [moonlive_emit.h](../../../../src/core/moonlive/moonlive_emit.h) · [MoonLiveEffect.h](../../../../src/light/moonlive/MoonLiveEffect.h) diff --git a/esp32/main/CMakeLists.txt b/esp32/main/CMakeLists.txt index 362864a..f585aba 100644 --- a/esp32/main/CMakeLists.txt +++ b/esp32/main/CMakeLists.txt @@ -6,7 +6,10 @@ idf_component_register( "../../src/core/HttpServerModule.cpp" "../../src/core/FilesystemModule.cpp" "../../src/core/Scheduler.cpp" + "../../src/core/moonlive/MoonLive.cpp" + "../../src/core/moonlive/MoonLiveCompiler.cpp" "../../src/platform/esp32/platform_esp32.cpp" + "../../src/platform/esp32/moonlive_emit.cpp" "../../src/platform/esp32/platform_esp32_fs.cpp" "../../src/platform/esp32/platform_esp32_ota.cpp" "../../src/platform/esp32/platform_esp32_httpget.cpp" diff --git a/esp32/sdkconfig.defaults.esp32s3-n16r8 b/esp32/sdkconfig.defaults.esp32s3-n16r8 index d698eb2..50e2ae1 100644 --- a/esp32/sdkconfig.defaults.esp32s3-n16r8 +++ b/esp32/sdkconfig.defaults.esp32s3-n16r8 @@ -12,3 +12,13 @@ CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y # PSRAM CONFIG_SPIRAM=y CONFIG_SPIRAM_MODE_OCT=y + +# MoonLive native codegen needs an executable heap (allocExec → MALLOC_CAP_EXEC IRAM). +# MALLOC_CAP_EXEC is gated behind CONFIG_HEAP_HAS_EXEC_HEAP, which IDF disables whenever +# memory protection (PMP/PMS W^X) is on — the same write-XOR-execute rule we handle on +# macOS. A JIT fundamentally needs writable-then-executable memory, so disable memprot +# (which enables the exec heap). This is the standard ESP32-JIT configuration (ESPLiveScript +# runs the same way); the safety story for scripted code is the staged bounds/watchdog +# checks, not the hardware W^X wall. +CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n +CONFIG_HEAP_HAS_EXEC_HEAP=y diff --git a/src/core/moonlive/MoonLive.cpp b/src/core/moonlive/MoonLive.cpp new file mode 100644 index 0000000..3b355cd --- /dev/null +++ b/src/core/moonlive/MoonLive.cpp @@ -0,0 +1,66 @@ +#include "core/moonlive/MoonLive.h" +#include "core/moonlive/MoonLiveCompiler.h" +#include "platform/platform.h" + +namespace mm::moonlive { + +// Generous fixed cap for the Stage-1 routines — a fill loop is a few dozen bytes on any +// ISA; the emitter returns the real length and the unused tail is harmless. Sized once here +// so the emitter never has to worry about growth (a real program-sized arena comes with the +// language, later). +static constexpr size_t kCodeCap = 256; + +// Copy `len` already-emitted bytes into a fresh exec block. writeExec hides the ISA quirks +// (IRAM's 32-bit-store-only rule, the I-cache sync), so the engine stays target-agnostic. +// Returns the block (ready to call) or nullptr on failure (error_ set, prior state freed). +void* MoonLive::place(const uint8_t* staged, size_t len) { + free(); // drop any prior compilation — (re)compile is a clean re-emit + if (len == 0) { error_ = "emit failed"; return nullptr; } + void* block = platform::allocExec(kCodeCap); + if (!block) { error_ = "no executable memory"; return nullptr; } + platform::writeExec(block, staged, len); + code_ = block; + codeCap_ = kCodeCap; + codeLen_ = len; + error_ = ""; + return block; +} + +bool MoonLive::compile(uint8_t r, uint8_t g, uint8_t b) { + uint8_t staging[kCodeCap]; + size_t len = emitFill(staging, kCodeCap, r, g, b); + void* block = place(staging, len); + if (!block) return false; + fn_ = reinterpret_cast(block); + return true; +} + +bool MoonLive::compile(const char* source) { + uint8_t staging[kCodeCap]; + CompileResult cr = compileSource(source, staging, kCodeCap); + if (!cr.ok) { free(); error_ = cr.error; return false; } // surface the parse diagnostic + void* block = place(staging, cr.len); + if (!block) return false; + fn_ = reinterpret_cast(block); + return true; +} + +bool MoonLive::compileAnimated() { + uint8_t staging[kCodeCap]; + size_t len = emitAnimatedFill(staging, kCodeCap); + void* block = place(staging, len); + if (!block) return false; + anim_ = reinterpret_cast(block); + return true; +} + +void MoonLive::free() { + if (code_) platform::freeExec(code_, codeCap_); + code_ = nullptr; + codeCap_ = 0; + codeLen_ = 0; + fn_ = nullptr; + anim_ = nullptr; +} + +} // namespace mm::moonlive diff --git a/src/core/moonlive/MoonLive.h b/src/core/moonlive/MoonLive.h new file mode 100644 index 0000000..e723052 --- /dev/null +++ b/src/core/moonlive/MoonLive.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include "core/moonlive/moonlive_emit.h" + +// MoonLive — the live-script engine core (domain-neutral, §3.1/§3.9 of +// livescripts-analysis-top-down.md). Stage 1a: no language yet — compile() asks the per-ISA +// emitter for one fixed routine (fill every light a colour), places it in executable memory, +// and run() calls it over a host-supplied buffer. The whole compiler (lexer → parser → IR → +// codegen) grows in behind compile() over later stages; this is the load-bearing first slice +// that proves emit → allocExec → call → write-the-buffer works on real Xtensa. +// +// Neutral by construction: the engine includes only , the emitter seam, and the +// platform seam — never EffectBase, Buffer, or any projectMM type. The binding +// (src/light/moonlive/MoonLiveEffect.h) wraps it as a MoonModule. + +namespace mm::moonlive { + +class MoonLive { +public: + ~MoonLive() { free(); } + + // Compile the (Stage-1a fixed) program for a colour: emit the routine, copy it into an + // exec block, ready it to call. Returns ok(). A failure (no exec memory, emit too big) + // leaves the engine !ok() with an error() — the caller degrades, never crashes. + bool compile(uint8_t r, uint8_t g, uint8_t b); + + // Stage 2: compile SOURCE TEXT. The front-end (MoonLiveCompiler) lexes/parses the + // `fill(r,g,b);` statement and emits the same code compile(r,g,b) would. A parse error + // leaves the engine !ok() with error() pointing at the diagnostic — the script editor's + // failure path. This is the real compile path; the typed overloads above are the + // hand-driven Stage-1 spikes the source path now subsumes for the fill program. + bool compile(const char* source); + + // Stage 1b: compile the animated routine (colour derived from the per-frame `t`). + bool compileAnimated(); + + bool ok() const { return fn_ != nullptr || anim_ != nullptr; } + const char* error() const { return error_; } + + // The hot path: run the compiled routine over the host's buffer. `t` is the host's + // elapsed() ms; a static routine ignores it, an animated one derives its colour from + // it. No-op if !ok(), so a failed compile renders nothing rather than calling null. + void run(uint8_t* buf, uint32_t nLights, uint8_t cpl, uint32_t t) const { + if (fn_) fn_(buf, nLights, cpl); + else if (anim_) anim_(buf, nLights, cpl, t); + } + + // Release the exec block (the "destructor" role — teardown returns the memory). + void free(); + + // The emitted code length, for the golden-bytes test (0 until compiled). + size_t codeLen() const { return codeLen_; } + +private: + // Shared post-emit step: copy `len` staged bytes into a fresh exec block. Returns the + // block (already writeExec'd) or nullptr on failure (error_ set). The typed pointer is + // cast by the caller. + void* place(const uint8_t* staged, size_t len); + + void* code_ = nullptr; // allocExec block holding the emitted machine code + size_t codeCap_ = 0; // its capacity (for freeExec) + size_t codeLen_ = 0; // bytes actually emitted + FillFn fn_ = nullptr; // static fill (3-arg), or nullptr + AnimFn anim_ = nullptr; // animated fill (4-arg, reads t), or nullptr + const char* error_ = ""; +}; + +} // namespace mm::moonlive diff --git a/src/core/moonlive/MoonLiveCompiler.cpp b/src/core/moonlive/MoonLiveCompiler.cpp new file mode 100644 index 0000000..6466566 --- /dev/null +++ b/src/core/moonlive/MoonLiveCompiler.cpp @@ -0,0 +1,133 @@ +#include "core/moonlive/MoonLiveCompiler.h" +#include "core/moonlive/moonlive_emit.h" + +namespace mm::moonlive { + +namespace { + +// --- Lexer --------------------------------------------------------------------------- +// The token kinds the Stage-2 grammar needs. A hand-written recursive-descent front-end +// (the textbook embedded-script default) lexes on demand: the parser pulls one token at a +// time. No token table is materialised — peek()/advance() walk the source directly. +enum class Tok { Ident, Number, LParen, RParen, Comma, Semicolon, End, Error }; + +struct Lexer { + const char* p; // cursor into the source + Tok kind = Tok::Error; + long number = 0; // value when kind==Number + const char* identBeg = nullptr; + size_t identLen = 0; // span when kind==Ident + const char* tokBeg = nullptr; // start of the current token (for error columns) + const char* srcBeg; // start of source (for 1-based column math) + const char* err = ""; + + explicit Lexer(const char* s) : p(s), srcBeg(s) { advance(); } + + static bool isSpace(char c) { return c == ' ' || c == '\t' || c == '\n' || c == '\r'; } + static bool isDigit(char c) { return c >= '0' && c <= '9'; } + static bool isIdentStart(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; } + static bool isIdentCont(char c) { return isIdentStart(c) || isDigit(c); } + + uint16_t col() const { return static_cast((tokBeg - srcBeg) + 1); } + + void advance() { + while (isSpace(*p)) p++; + tokBeg = p; + char c = *p; + if (c == 0) { kind = Tok::End; return; } + if (c == '(') { p++; kind = Tok::LParen; return; } + if (c == ')') { p++; kind = Tok::RParen; return; } + if (c == ',') { p++; kind = Tok::Comma; return; } + if (c == ';') { p++; kind = Tok::Semicolon; return; } + if (isDigit(c)) { + long v = 0; + while (isDigit(*p)) { v = v * 10 + (*p - '0'); p++; if (v > 1000000) break; } + number = v; kind = Tok::Number; return; + } + if (isIdentStart(c)) { + identBeg = p; + while (isIdentCont(*p)) p++; + identLen = static_cast(p - identBeg); + kind = Tok::Ident; return; + } + kind = Tok::Error; err = "unexpected character"; + } + + bool identIs(const char* word) const { + if (kind != Tok::Ident) return false; + for (size_t i = 0; i < identLen; i++) if (identBeg[i] != word[i] || word[i] == 0) return false; + return word[identLen] == 0; + } +}; + +// --- AST + Parser -------------------------------------------------------------------- +// The only production: program := "fill" "(" number "," number "," number ")" ";" End. +// The AST for it is just the three colour bytes — there is no node hierarchy at one +// statement (a real tree arrives with the second statement type, concrete-first). +struct Parsed { + bool ok = false; + const char* error = ""; + uint16_t errorCol = 0; + uint8_t r = 0, g = 0, b = 0; +}; + +// Pull a 0..255 number token; fail with a diagnostic otherwise. +bool expectByte(Lexer& lex, uint8_t& out, Parsed& res) { + if (lex.kind == Tok::Error) { res.error = lex.err; res.errorCol = lex.col(); return false; } + if (lex.kind != Tok::Number) { res.error = "expected a number"; res.errorCol = lex.col(); return false; } + if (lex.number < 0 || lex.number > 255) { res.error = "colour value out of range (0..255)"; res.errorCol = lex.col(); return false; } + out = static_cast(lex.number); + lex.advance(); + return true; +} + +bool expect(Lexer& lex, Tok t, const char* msg, Parsed& res) { + if (lex.kind != t) { res.error = msg; res.errorCol = lex.col(); return false; } + lex.advance(); + return true; +} + +Parsed parse(const char* source) { + Parsed res; + Lexer lex(source); + + if (!lex.identIs("fill")) { + res.error = (lex.kind == Tok::End) ? "empty program (expected fill(...))" : "expected 'fill'"; + res.errorCol = lex.col(); + return res; + } + lex.advance(); + if (!expect(lex, Tok::LParen, "expected '(' after fill", res)) return res; + if (!expectByte(lex, res.r, res)) return res; + if (!expect(lex, Tok::Comma, "expected ',' between colour values", res)) return res; + if (!expectByte(lex, res.g, res)) return res; + if (!expect(lex, Tok::Comma, "expected ',' between colour values", res)) return res; + if (!expectByte(lex, res.b, res)) return res; + if (!expect(lex, Tok::RParen, "expected ')' to close fill(...)", res)) return res; + if (!expect(lex, Tok::Semicolon, "expected ';' after fill(...)", res)) return res; + if (lex.kind != Tok::End) { res.error = "unexpected tokens after fill(...)"; res.errorCol = lex.col(); return res; } + + res.ok = true; + return res; +} + +} // namespace + +CompileResult compileSource(const char* source, uint8_t* out, size_t cap) { + CompileResult r; + if (!source) { r.error = "no source"; return r; } + + Parsed p = parse(source); + if (!p.ok) { r.error = p.error; r.errorCol = p.errorCol; return r; } + + // Codegen: the parsed colour drives the SAME per-ISA emitter the hand-written 1a path + // uses — so a parsed `fill(0,0,255)` produces byte-for-byte the bytes emitFill(0,0,255) + // does (the golden-bytes equivalence the test asserts). No IR yet at one statement. + size_t len = emitFill(out, cap, p.r, p.g, p.b); + if (len == 0) { r.error = "code buffer too small"; return r; } + r.ok = true; + r.len = len; + return r; +} + +} // namespace mm::moonlive diff --git a/src/core/moonlive/MoonLiveCompiler.h b/src/core/moonlive/MoonLiveCompiler.h new file mode 100644 index 0000000..0fb4e97 --- /dev/null +++ b/src/core/moonlive/MoonLiveCompiler.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +// MoonLive front-end (§3.2) — the platform-independent compiler: source text → tokens → +// AST → native code (via the per-ISA emitter). Stage 2 is the smallest real slice: it +// recognises ONE statement, +// +// fill(, , ); +// +// three integer 0..255 args, and emits the SAME machine code the hand-written emitFill +// produces (the golden-bytes equivalence is the no-language-leak proof). No expressions, +// variables, or control flow yet — the lexer/parser/codegen shape is real, the grammar is +// one rule. It grows rule by rule from here; the IR seam is introduced when a second +// statement/type forces it (concrete-first). +// +// Neutral by construction: the compiler knows the LANGUAGE, never an ISA — codegen calls +// the platform's emitFill, so a different backend changes nothing here. + +namespace mm::moonlive { + +// Result of compiling source: on success, ok==true and the emitted bytes are in out[0..len). +// On failure, ok==false and error points at a static, human-readable diagnostic (the column +// is 1-based, 0 if not applicable). out is filled only on success. +struct CompileResult { + bool ok = false; + const char* error = ""; + uint16_t errorCol = 0; + size_t len = 0; // bytes written to out on success +}; + +// Compile `source` into machine code in `out` (capacity `cap`). Pure: no I/O, no allocation +// beyond the caller's buffer — the same input always yields the same bytes, so it unit-tests +// trivially (and the result is asserted byte-for-byte against emitFill). The emitted code is +// the per-ISA fill routine for the parsed colour. +CompileResult compileSource(const char* source, uint8_t* out, size_t cap); + +} // namespace mm::moonlive diff --git a/src/core/moonlive/moonlive_emit.h b/src/core/moonlive/moonlive_emit.h new file mode 100644 index 0000000..ff6e1f4 --- /dev/null +++ b/src/core/moonlive/moonlive_emit.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +// MoonLive — per-ISA code emitter (the backend seam, §3.2 of livescripts-analysis-top-down.md). +// +// This header is the NEUTRAL declaration the engine calls; the implementation is per-ISA and +// lives behind the platform boundary (src/platform//moonlive_emit.cpp): Xtensa on +// ESP32, the host ISA (arm64 / x86-64) on desktop. The engine (src/core/moonlive/) never +// branches on ISA — it asks for bytes and runs them. That is the IR/backend seam at its +// first, smallest increment: no IR yet (one fixed routine, no language), but the boundary is +// already where it belongs. +// +// The emitted routine's C signature is FillFn: write a fixed (r,g,b) to every light — +// buf[i*cpl+0..2] = r,g,b for i in [0,nLights) — then return. The engine copies the bytes +// into an executable block (platform::allocExec + writeExec) and calls them through FillFn. + +namespace mm::moonlive { + +using FillFn = void (*)(uint8_t* buf, uint32_t nLights, uint8_t cpl); + +// Stage 1b: the routine also takes a per-frame `t` (the host's elapsed() ms), so the host +// feeds a changing value into the SAME native code each tick and the output animates. The +// engine passes elapsed() through run(). +using AnimFn = void (*)(uint8_t* buf, uint32_t nLights, uint8_t cpl, uint32_t t); + +// Emit the fixed-colour fill routine's machine code into `out` (capacity `cap` bytes), for +// the ISA this translation unit was compiled for, with the colour baked in. Returns the +// number of bytes written, or 0 if `cap` is too small (the caller degrades). The emitted +// bytes ARE the function — the engine makes `out` executable and casts it to FillFn. +// +// This is the hand-written reference codegen for Stage 1a; Stage 2 replaces it with +// parser-driven emission that must reproduce the same bytes (the golden-bytes test). +size_t emitFill(uint8_t* out, size_t cap, uint8_t r, uint8_t g, uint8_t b); + +// Stage 1b: emit a routine that derives its colour from the runtime arg `t` — +// red = (t >> 3) & 0xFF, green = 0, blue = 64 for every light. +// Proves a per-frame host value flows into the emitted native code and changes the output +// (the grid's red ramps over time). Same emit/exec/call path as emitFill, one extra arg. +size_t emitAnimatedFill(uint8_t* out, size_t cap); + +} // namespace mm::moonlive diff --git a/src/light/moonlive/MoonLiveEffect.h b/src/light/moonlive/MoonLiveEffect.h new file mode 100644 index 0000000..e15ef00 --- /dev/null +++ b/src/light/moonlive/MoonLiveEffect.h @@ -0,0 +1,68 @@ +#pragma once + +#include "light/effects/EffectBase.h" +#include "core/moonlive/MoonLive.h" +#include + +// MoonLiveEffect — a scripted effect rendered by the MoonLive engine (§3.3 of +// livescripts-analysis-top-down.md). The thin binding side of the engine/binding seam: it +// IS a first-class EffectBase (role, controls, lifecycle, generic UI), and its loop() +// delegates to a compiled MoonLive over this effect's own buffer. +// +// Stage 1a: no script source yet — the engine compiles one fixed routine (fill the layer +// a colour) so the load-bearing path (emit → allocExec → call → write the buffer) is proven +// end to end on real Xtensa. The `source` text control, the language, and script-declared +// controls arrive in later stages; this is the smallest thing that lights an LED via native +// code we generated. + +namespace mm { + +class MoonLiveEffect : public EffectBase { +public: + const char* tags() const override { return "📝"; } // scripted + Dim dimensions() const override { return Dim::D2; } + + // Stage 2: the effect carries its SCRIPT SOURCE as an editable, persisted text control. + // Editing it recompiles live (the script-editor loop), the same way any control edit + // reshapes a compiled module — no bespoke path. The default is the fill program the + // spike proves end to end. + void onBuildControls() override { + controls_.addText("source", source_, sizeof(source_)); + } + + // A `source` edit must recompile — route it through the onBuildState rebuild sweep so a + // new script swaps in live (the script-editor loop). Without this the edit would change + // only the stored text, not the running code. + bool controlChangeTriggersBuildState(const char* controlName) const override { + return std::strcmp(controlName, "source") == 0; + } + + // Compile the source on the cold rebuild path. A failed compile (parse error or no exec + // memory) surfaces in the module status and leaves loop() a no-op — the effect renders + // dark, the device keeps running (robustness + no-reboot). A *source* edit re-enters + // here and recompiles, so a new script swaps in live; a broken edit just shows its + // diagnostic and the layer goes dark until it's fixed. + void onBuildState() override { + if (engine_.compile(source_)) { + clearStatus(); + } else { + setStatus(engine_.error(), Severity::Error); + } + EffectBase::onBuildState(); + } + + void loop() override { + if (engine_.ok()) engine_.run(buffer(), nrOfLights(), channelsPerLight(), elapsed()); + } + + void teardown() override { + engine_.free(); // release the exec block — the destructor role + EffectBase::teardown(); + } + +private: + moonlive::MoonLive engine_; + char source_[64] = "fill(0, 0, 255);"; // default script — solid blue, from parsed source +}; + +} // namespace mm diff --git a/src/main.cpp b/src/main.cpp index 9361914..8a3ecd3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,6 +13,7 @@ #include "light/effects/ParticlesEffect.h" #include "light/effects/GlowParticlesEffect.h" #include "light/effects/CheckerboardEffect.h" +#include "light/moonlive/MoonLiveEffect.h" #include "light/effects/SpiralEffect.h" #include "light/effects/RingsEffect.h" #include "light/effects/RipplesEffect.h" @@ -102,6 +103,7 @@ static void registerModuleTypes() { mm::ModuleFactory::registerType("AudioSpectrumEffect", "light/effects/AudioSpectrumEffect.md"); mm::ModuleFactory::registerType("SineEffect", "light/effects/SineEffect.md"); mm::ModuleFactory::registerType("DistortionWavesEffect", "light/effects/DistortionWavesEffect.md"); + mm::ModuleFactory::registerType("MoonLiveEffect", "light/moonlive/MoonLiveEffect.md"); mm::ModuleFactory::registerType("MultiplyModifier", "light/modifiers/MultiplyModifier.md"); mm::ModuleFactory::registerType("CheckerboardModifier", "light/modifiers/CheckerboardModifier.md"); mm::ModuleFactory::registerType("RandomMapModifier", "light/modifiers/RandomMapModifier.md"); diff --git a/src/platform/desktop/moonlive_emit.cpp b/src/platform/desktop/moonlive_emit.cpp new file mode 100644 index 0000000..7e5a0c4 --- /dev/null +++ b/src/platform/desktop/moonlive_emit.cpp @@ -0,0 +1,147 @@ +#include "core/moonlive/moonlive_emit.h" + +#include + +// MoonLive desktop backend (§3.2) — emits the Stage-1a fixed-colour fill as host machine +// code (arm64 or x86-64, chosen at compile time). The desktop backend exists to (a) let a +// unit test EXECUTE generated code in-process — proving allocExec → writeExec → call works +// off-hardware — and (b) be the eventual home of the host codegen for the in-process script +// tests. Hand-encoding the loop here is the same exercise the Xtensa backend does, so the +// engine/binding API is exercised identically on both. (The ESP32/Xtensa backend is the one +// the hardware run validates; this one keeps CI honest.) +// +// The routine implements: void fill(uint8_t* buf, uint32_t nLights, uint8_t cpl) +// for (i=0; i(r) << 5); + code[5] = 0x52800005u | (static_cast(g) << 5); + code[6] = 0x52800006u | (static_cast(b) << 5); + std::memcpy(out, code, sizeof(code)); + return sizeof(code); +} + +// arm64 animated fill (assembled from anim_arm64.s): red = (t>>3)&0xFF, green=0, blue=64. +// t arrives in w3; nothing to patch — the colour is computed from the runtime arg. +static const uint32_t kArm64Anim[] = { + 0x34000241, // cbz w1, .done + 0x53037c64, // lsr w4, w3, #3 red = t>>3 + 0x12001c84, // and w4, w4, #0xff + 0x52800005, // mov w5, #0 green + 0x52800806, // mov w6, #64 blue + 0xd2800003, // mov x3, #0 off + 0x12001c42, // and w2, w2, #0xff + 0x53001c42, // uxtb w2, w2 stride + 0xd2800007, // mov x7, #0 i + 0x38236804, // strb w4, [x0, x3] .loop: + 0x91000468, // add x8, x3, #1 + 0x38286805, // strb w5, [x0, x8] + 0x91000868, // add x8, x3, #2 + 0x38286806, // strb w6, [x0, x8] + 0x910004e7, // add x7, x7, #1 + 0x8b020063, // add x3, x3, x2 + 0xeb0100ff, // cmp x7, x1 + 0x54ffff03, // b.lo .loop + 0xd65f03c0, // ret .done: +}; + +size_t emitAnimatedFill(uint8_t* out, size_t cap) { + if (cap < sizeof(kArm64Anim)) return 0; + std::memcpy(out, kArm64Anim, sizeof(kArm64Anim)); + return sizeof(kArm64Anim); +} + +#elif defined(__x86_64__) + +// x86-64 template (assembled from fill_x64.s, verified with clang/objdump). buf=rdi, +// nLights=esi, cpl=dl. R/G/B are the immediate byte of each `movb` at offsets 0x11/0x17/0x1d. +static const uint8_t kX64[] = { + 0x85, 0xf6, // test esi, esi + 0x74, 0x25, // je .done (+0x25) + 0x0f, 0xb6, 0xca, // movzx ecx, dl (stride) + 0x4d, 0x31, 0xc0, // xor r8, r8 (i) + 0x4d, 0x31, 0xc9, // xor r9, r9 (off) + 0x42, 0xc6, 0x04, 0x0f, 0x11, // mov byte [rdi+r9], R ← off 0x11 + 0x42, 0xc6, 0x44, 0x0f, 0x01, 0x22, // mov byte [rdi+r9+1], G ← off 0x17 + 0x42, 0xc6, 0x44, 0x0f, 0x02, 0x33, // mov byte [rdi+r9+2], B ← off 0x1d + 0x49, 0xff, 0xc0, // inc r8 + 0x49, 0x01, 0xc9, // add r9, rcx + 0x49, 0x39, 0xf0, // cmp r8, rsi + 0x72, 0xe4, // jb .loop (-0x1c) + 0xc3, // ret .done: +}; + +size_t emitFill(uint8_t* out, size_t cap, uint8_t r, uint8_t g, uint8_t b) { + if (cap < sizeof(kX64)) return 0; + std::memcpy(out, kX64, sizeof(kX64)); + out[0x11] = r; + out[0x17] = g; + out[0x1d] = b; + return sizeof(kX64); +} + +// x86-64 animated fill (assembled from anim_x64.s): red = (t>>3)&0xFF, green=0, blue=64. +// t arrives in ecx; nothing to patch — the colour is computed from the runtime arg. +static const uint8_t kX64Anim[] = { + 0x85, 0xf6, // test esi, esi + 0x74, 0x2d, // je .done (+0x2d) + 0x89, 0xc8, // mov eax, ecx (t) + 0xc1, 0xe8, 0x03, // shr eax, 3 red = t>>3 + 0x0f, 0xb6, 0xc0, // movzx eax, al + 0x44, 0x0f, 0xb6, 0xd2, // movzx r10d, dl (stride) + 0x4d, 0x31, 0xc0, // xor r8, r8 (i) + 0x4d, 0x31, 0xc9, // xor r9, r9 (off) + 0x42, 0x88, 0x04, 0x0f, // mov byte [rdi+r9], al (red, dynamic) + 0x42, 0xc6, 0x44, 0x0f, 0x01, 0x00, // mov byte [rdi+r9+1], 0 (green) + 0x42, 0xc6, 0x44, 0x0f, 0x02, 0x40, // mov byte [rdi+r9+2], 64 (blue) + 0x49, 0xff, 0xc0, // inc r8 + 0x4d, 0x01, 0xd1, // add r9, r10 + 0x49, 0x39, 0xf0, // cmp r8, rsi + 0x72, 0xe5, // jb .loop (-0x1b) + 0xc3, // ret .done: +}; + +size_t emitAnimatedFill(uint8_t* out, size_t cap) { + if (cap < sizeof(kX64Anim)) return 0; + std::memcpy(out, kX64Anim, sizeof(kX64Anim)); + return sizeof(kX64Anim); +} + +#else +#error "MoonLive desktop backend: unsupported host ISA (expected arm64 or x86-64)" +#endif + +} // namespace mm::moonlive diff --git a/src/platform/desktop/platform_desktop.cpp b/src/platform/desktop/platform_desktop.cpp index 11c606a..edd1a97 100644 --- a/src/platform/desktop/platform_desktop.cpp +++ b/src/platform/desktop/platform_desktop.cpp @@ -24,6 +24,10 @@ #include #include #include +#include // mmap/munmap for allocExec (executable pages) +#ifdef __APPLE__ +#include // pthread_jit_write_protect_np — macOS arm64 W^X JIT toggle +#endif #endif namespace mm::platform { @@ -120,6 +124,54 @@ void free(void* ptr) { std::free(ptr); } +// Executable memory for MoonLive's emitted code. macOS on Apple Silicon enforces W^X +// (a page is writable OR executable, never both at once) and demands MAP_JIT for any +// JIT page; the write happens later in writeExec, bracketed by a per-thread +// write-protect toggle. Linux/Windows allow a plain RWX page. Returns nullptr on +// failure so the caller degrades. +void* allocExec(size_t bytes) { + if (bytes == 0) return nullptr; +#ifdef _WIN32 + void* p = VirtualAlloc(nullptr, bytes, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); + return p; // VirtualAlloc returns nullptr on failure +#elif defined(__APPLE__) + void* p = ::mmap(nullptr, bytes, PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_JIT, -1, 0); + return p == MAP_FAILED ? nullptr : p; +#else + void* p = ::mmap(nullptr, bytes, PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + return p == MAP_FAILED ? nullptr : p; +#endif +} + +void freeExec(void* ptr, size_t bytes) { + if (!ptr) return; +#ifdef _WIN32 + (void)bytes; + VirtualFree(ptr, 0, MEM_RELEASE); +#else + ::munmap(ptr, bytes); +#endif +} + +void writeExec(void* dst, const void* src, size_t len) { + if (!dst || !src || !len) return; +#ifdef __APPLE__ + // macOS arm64 W^X: flip this thread's MAP_JIT pages to writable, copy, flip back to + // executable, then sync the I-cache (required on arm64 for freshly-written code). + pthread_jit_write_protect_np(0); + std::memcpy(dst, src, len); + pthread_jit_write_protect_np(1); + __builtin___clear_cache(static_cast(dst), static_cast(dst) + len); +#else + // Linux/Windows: the RWX page is plain memory; memcpy suffices. arm64 Linux still + // wants an I-cache sync; on x86-64 __builtin___clear_cache is a harmless no-op. + std::memcpy(dst, src, len); + __builtin___clear_cache(static_cast(dst), static_cast(dst) + len); +#endif +} + void yield() { // No-op on desktop — OS scheduler handles threading. // Socket reads use SO_RCVTIMEO for blocking with timeout. diff --git a/src/platform/esp32/moonlive_emit.cpp b/src/platform/esp32/moonlive_emit.cpp new file mode 100644 index 0000000..d81515e --- /dev/null +++ b/src/platform/esp32/moonlive_emit.cpp @@ -0,0 +1,92 @@ +#include "core/moonlive/moonlive_emit.h" + +#include + +// MoonLive ESP32 (Xtensa LX6/LX7) backend (§3.2) — the load-bearing hardware codegen. +// Emits the Stage-1a fixed-colour fill as Xtensa machine code; the engine copies it into +// IRAM (platform::writeExec) and calls it through FillFn. This is the path the bench run +// validates: native code we generated, executing in the render tick, writing the buffer. +// +// void fill(uint8_t* buf, uint32_t nLights, uint8_t cpl) // windowed ABI: a2,a3,a4 +// for (i=0; i return +// 05: movi.n a7, 0 (0c 07) off = 0 +// 07: movi.n a8, 0 (0c 08) i = 0 +// 09: movi a5, R (52 a0 NN) ← R at offset 0x0b +// 0c: movi a6, G (62 a0 NN) ← G at offset 0x0e +// 0f: movi a9, B (92 a0 NN) ← B at offset 0x11 +// 12: add.n a10, a2, a7 (7a a2) .loop: ptr = buf + off +// 14: s8i a5, a10, 0 (52 4a 00) ptr[0] = R +// 17: s8i a6, a10, 1 (62 4a 01) ptr[1] = G +// 1a: s8i a9, a10, 2 (92 4a 02) ptr[2] = B +// 1d: add.n a7, a7, a4 (4a 77) off += cpl +// 1f: addi.n a8, a8, 1 (1b 88) i++ +// 21: bne a8, a3, .loop (37 98 ed) i != nLights -> loop +// 24: retw.n (1d f0) .done: +static const uint8_t kXtensaFill[] = { + 0x36, 0x41, 0x00, // entry a1, 32 + 0x9c, 0xd3, // beqz.n a3, .done + 0x0c, 0x07, // movi.n a7, 0 + 0x0c, 0x08, // movi.n a8, 0 + 0x52, 0xa0, 0x00, // movi a5, R (offset 0x0b) + 0x62, 0xa0, 0x00, // movi a6, G (offset 0x0e) + 0x92, 0xa0, 0x00, // movi a9, B (offset 0x11) + 0x7a, 0xa2, // add.n a10, a2, a7 + 0x52, 0x4a, 0x00, // s8i a5, a10, 0 + 0x62, 0x4a, 0x01, // s8i a6, a10, 1 + 0x92, 0x4a, 0x02, // s8i a9, a10, 2 + 0x4a, 0x77, // add.n a7, a7, a4 + 0x1b, 0x88, // addi.n a8, a8, 1 + 0x37, 0x98, 0xed, // bne a8, a3, .loop + 0x1d, 0xf0, // retw.n +}; +static constexpr size_t kR = 0x0b, kG = 0x0e, kB = 0x11; // colour-immediate byte offsets + +size_t emitFill(uint8_t* out, size_t cap, uint8_t r, uint8_t g, uint8_t b) { + if (cap < sizeof(kXtensaFill)) return 0; + std::memcpy(out, kXtensaFill, sizeof(kXtensaFill)); + out[kR] = r; + out[kG] = g; + out[kB] = b; + return sizeof(kXtensaFill); +} + +// Xtensa animated fill (assembled from anim_xt.s, verified by objdump). The 4th windowed-ABI +// arg `t` arrives in a5; red = (t>>3)&0xFF is computed at runtime, green=0, blue=64. Nothing +// to patch — the colour is derived from `t`, so the SAME code animates as the host feeds a +// changing elapsed() each tick. +// 00: entry a1,32 06: srli a6,a5,3 (red=t>>3) 0e: movi.n a7,0 (green) +// 03: beqz.n a3,.done 08: movi a7,0xff 10: movi.n a8,64 (blue) +// 0b: and a6,a6,a7 12: movi.n a9,0 (off) 14: movi.n a11,0 (i) +// 16: add.n a10,a2,a9 18: s8i a6 +0 1b: s8i a7 +1 1e: s8i a8 +2 21: add.n a9,a9,a4 +// 23: addi.n a11,a11,1 25: bne a11,a3,.loop 28: retw.n +// Bytes copied VERBATIM from the assembler's binary output (objcopy of anim_xt.o) — NOT +// hand-transcribed from the disassembly, because Xtensa's mixed narrow/wide instructions and +// byte order make per-instruction hand-grouping error-prone (a transcription typo here caused +// a StoreProhibited crash on the S3 before this was regenerated from the raw blob). +static const uint8_t kXtensaAnim[] = { + 0x36, 0x41, 0x00, 0xac, 0x13, 0x50, 0x63, 0x41, 0x72, 0xa0, 0xff, 0x70, + 0x66, 0x10, 0x0c, 0x07, 0x4c, 0x08, 0x0c, 0x09, 0x0c, 0x0b, 0x9a, 0xa2, + 0x62, 0x4a, 0x00, 0x72, 0x4a, 0x01, 0x82, 0x4a, 0x02, 0x4a, 0x99, 0x1b, + 0xbb, 0x37, 0x9b, 0xed, 0x1d, 0xf0, +}; + +size_t emitAnimatedFill(uint8_t* out, size_t cap) { + if (cap < sizeof(kXtensaAnim)) return 0; + std::memcpy(out, kXtensaAnim, sizeof(kXtensaAnim)); + return sizeof(kXtensaAnim); +} + +} // namespace mm::moonlive diff --git a/src/platform/esp32/platform_esp32.cpp b/src/platform/esp32/platform_esp32.cpp index 175f00f..9c8fe9b 100644 --- a/src/platform/esp32/platform_esp32.cpp +++ b/src/platform/esp32/platform_esp32.cpp @@ -21,6 +21,7 @@ #include "esp_timer.h" #include "esp_heap_caps.h" +#include "esp_cache.h" // esp_cache_msync — I-cache sync after writing MoonLive code to IRAM #include "esp_system.h" #include "esp_chip_info.h" #include "esp_mac.h" @@ -106,6 +107,51 @@ void free(void* ptr) { heap_caps_free(ptr); } +// Executable memory for MoonLive's emitted Xtensa code. MALLOC_CAP_EXEC forces an +// allocation from IRAM (instruction-bus-fetchable). nullptr when IRAM is exhausted — +// the caller degrades (the scripted module reports "no memory", runs dark), never +// crashes. IRAM competes with WiFi/driver IRAM, so a failure here is expected on a +// busy device and must be handled, not asserted. +void* allocExec(size_t bytes) { + if (bytes == 0) return nullptr; + return heap_caps_malloc(bytes, MALLOC_CAP_EXEC | MALLOC_CAP_32BIT); +} + +void freeExec(void* ptr, size_t /*bytes*/) { + heap_caps_free(ptr); // size is the desktop munmap's; IRAM free needs only the ptr +} + +void writeExec(void* dst, const void* src, size_t len) { + if (!dst || !src || !len) return; + // IRAM is writable only by 32-bit-aligned WORD stores (a byte/halfword store to + // IRAM faults), so copy word-by-word, padding the final partial word with the + // bytes already there — never a sub-word store. allocExec returns 4-byte-aligned + // IRAM, so dst is aligned; src may not be, so read it bytewise into the word. + auto* d = static_cast(dst); + auto* s = static_cast(src); + size_t words = len / 4; + for (size_t i = 0; i < words; i++) { + uint32_t w = static_cast(s[i*4]) | (static_cast(s[i*4+1]) << 8) | + (static_cast(s[i*4+2]) << 16) | (static_cast(s[i*4+3]) << 24); + d[i] = w; + } + size_t rem = len % 4; + if (rem) { + uint32_t w = d[words]; // preserve the untouched high bytes + for (size_t b = 0; b < rem; b++) { + w &= ~(0xFFu << (b*8)); + w |= static_cast(s[words*4 + b]) << (b*8); + } + d[words] = w; + } + // Sync the instruction cache so the core fetches the freshly-written code, not a + // stale line. INST type + INVALIDATE; UNALIGNED because the code block isn't + // cache-line sized. S3 has the I-cache that needs this; harmless if it doesn't. + esp_cache_msync(dst, (len + 3) & ~size_t(3), + ESP_CACHE_MSYNC_FLAG_TYPE_INST | ESP_CACHE_MSYNC_FLAG_INVALIDATE | + ESP_CACHE_MSYNC_FLAG_UNALIGNED); +} + void yield() { vTaskDelay(pdMS_TO_TICKS(1)); } diff --git a/src/platform/platform.h b/src/platform/platform.h index 119cd11..f30fd80 100644 --- a/src/platform/platform.h +++ b/src/platform/platform.h @@ -21,6 +21,24 @@ void setTestNowMs(uint32_t ms); void* alloc(size_t bytes); void free(void* ptr); +// Executable memory for JIT-emitted native code (MoonLive). Distinct from alloc() +// because code must live in memory the CPU can FETCH from, not just read/write: +// IRAM on ESP32 (MALLOC_CAP_EXEC), an mmap'd PROT_EXEC page on desktop. Returns +// nullptr when exec memory is exhausted — the caller degrades (status, runs dark), +// never crashes. freeExec takes the same size so a backend that needs it (munmap) +// has it; ESP32 ignores the size. +void* allocExec(size_t bytes); +void freeExec(void* ptr, size_t bytes); + +// Copy emitted code INTO an allocExec block, then make it executable. Separate from a +// plain memcpy because ESP32 IRAM is fetch-only on the instruction bus and writable +// only via 32-bit-aligned data-bus stores (a byte memcpy faults), and after writing, +// the instruction cache must be synced so the core fetches the fresh bytes, not stale +// cache. Both quirks live here, behind the platform line; the engine just hands over +// (dst-from-allocExec, src-bytes, len). On desktop this is a plain memcpy. `len` need +// not be a multiple of 4 — the ESP32 path pads the final partial word. +void writeExec(void* dst, const void* src, size_t len); + void yield(); void delayMs(uint32_t ms); // blocking sleep; only use outside the hot path void delayUs(uint32_t us); // blocking busy-wait for sub-ms protocol gaps (e.g. diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3b016b3..26857ec 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -17,6 +17,8 @@ add_executable(mm_tests unit/core/unit_JsonUtil_parse.cpp unit/core/unit_MappingLUT.cpp unit/core/unit_ModuleFactory.cpp + unit/core/unit_moonlive_fill.cpp + unit/core/unit_moonlive_compiler.cpp unit/core/unit_MoonModule.cpp unit/core/unit_MoonModule_control_change_gate.cpp unit/core/unit_MoonModule_lifecycle.cpp diff --git a/test/js/config-ops.test.mjs b/test/js/config-ops.test.mjs new file mode 100644 index 0000000..0cbac55 --- /dev/null +++ b/test/js/config-ops.test.mjs @@ -0,0 +1,110 @@ +// Installer config-ops contract — the APPLY_OP plan that applies a device-model's catalog +// entry must CLEAR every parent it adds into before adding, so the "apply device defaults" +// path produces the same tree whether or not the flash was erased first. +// +// Why this matters: the device-side `add` is idempotent on the module name (an existing +// id returns AlreadyExists and is skipped). On a non-erased device the persisted tree +// already holds the entry's modules, so without a clearChildren pre-pass the re-add is a +// no-op and a stale or structurally-different module lingers — the bug where defaults +// "only applied if I also ticked Erase flash". This test pins that every add-parent (and +// every replaceChildren container) is cleared, and that clears precede adds precede sets. +// +// Run: `node --test test/js`. + +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; +import { planConfigOps } from "../../docs/install/config-ops.js"; + +const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", ".."); +const catalog = JSON.parse(readFileSync(join(ROOT, "docs", "install", "deviceModels.json"), "utf8")); + +// Indices of the first op of each kind, so we can assert global ordering. +const firstIndex = (ops, pred) => ops.findIndex(pred); +const lastIndex = (ops, pred) => ops.reduce((acc, o, i) => (pred(o) ? i : acc), -1); + +test("every add-parent is cleared, unless the parent is itself added fresh", () => { + assert.ok(Array.isArray(catalog) && catalog.length > 0, "deviceModels.json empty"); + for (const entry of catalog) { + const ops = planConfigOps(entry); + const added = new Set(ops.filter(o => o.op === "add").map(o => o.id)); + const addParents = new Set(ops.filter(o => o.op === "add").map(o => o.parent)); + const cleared = new Set(ops.filter(o => o.op === "clearChildren").map(o => o.parent)); + for (const p of addParents) { + // A parent the entry itself adds (e.g. Layer, added under Layers) starts empty, + // so it needs no clear; only a pre-existing parent must be cleared so a stale + // same-named child can't survive the AlreadyExists-skip on the re-add. + if (added.has(p)) { + assert.ok(!cleared.has(p), + `entry "${entry.name}": clears "${p}" but also adds it fresh — the clear is a no-op.`); + } else { + assert.ok(cleared.has(p), + `entry "${entry.name}": adds into pre-existing "${p}" but never clears it — a ` + + `non-erased device would keep the stale "${p}" child (AlreadyExists skips the re-add).`); + } + } + } +}); + +test("all clearChildren ops come before all add/set ops", () => { + for (const entry of catalog) { + const ops = planConfigOps(entry); + if (!ops.some(o => o.op === "clearChildren")) continue; + const lastClear = lastIndex(ops, o => o.op === "clearChildren"); + const firstNonClear = firstIndex(ops, o => o.op !== "clearChildren"); + assert.ok(lastClear < firstNonClear, + `entry "${entry.name}": a clearChildren runs after an add/set — clearing must be a ` + + `pre-pass, or it would wipe a module the entry just added.`); + } +}); + +test("a module's add precedes its own set ops", () => { + for (const entry of catalog) { + const ops = planConfigOps(entry); + for (const m of (entry.modules || [])) { + if (!m || !m.parent_id || !m.type || !m.controls) continue; + const addAt = firstIndex(ops, o => o.op === "add" && o.id === m.id); + const firstSet = firstIndex(ops, o => o.op === "set" && o.module === m.id); + if (firstSet === -1) continue; + assert.ok(addAt !== -1 && addAt < firstSet, + `entry "${entry.name}" module "${m.id}": a set precedes its add (or no add) — ` + + `the device would reject the set with ModuleNotFound.`); + } + } +}); + +test("S3 testbench entry adds Grid + Layer fresh and clears the pre-existing containers", () => { + const s3 = catalog.find(b => b && b.name === "projectMM testbench S3"); + assert.ok(s3, "projectMM testbench S3 entry missing from deviceModels.json"); + const ops = planConfigOps(s3); + const added = new Set(ops.filter(o => o.op === "add").map(o => o.id)); + const cleared = new Set(ops.filter(o => o.op === "clearChildren").map(o => o.parent)); + // The fix for "Layouts/Layers don't get created without erase": the entry now adds + // Grid and Layer explicitly instead of assuming the boot defaults are present. + for (const id of ["Grid", "Layer"]) { + assert.ok(added.has(id), `S3 entry no longer adds "${id}" — a non-erased device without that boot default gets no ${id}`); + } + // Pre-existing containers (not added by the entry) are cleared so stale children go. + for (const p of ["Drivers", "System", "Layouts", "Layers"]) { + assert.ok(cleared.has(p), `S3 entry does not clear pre-existing "${p}"`); + } + // Layer is added fresh, so it must NOT be cleared (would be a ModuleNotFound no-op). + assert.ok(!cleared.has("Layer"), `S3 entry clears "Layer" but also adds it fresh — redundant`); +}); + +test("a deduped parent is cleared exactly once", () => { + // Drivers hosts two modules in the S3 entry (RmtLed + NetworkSend); it must be cleared + // once, not once per child (a second clear would wipe the first child just re-added). + const s3 = catalog.find(b => b && b.name === "projectMM testbench S3"); + const driversClears = planConfigOps(s3).filter(o => o.op === "clearChildren" && o.parent === "Drivers"); + assert.equal(driversClears.length, 1, "Drivers cleared more than once — clears must be deduped"); +}); + +test("empty / malformed entry yields no ops (robust to any input)", () => { + assert.deepEqual(planConfigOps(undefined), []); + assert.deepEqual(planConfigOps({}), []); + assert.deepEqual(planConfigOps({ modules: null }), []); + assert.deepEqual(planConfigOps({ modules: [null, 42, {}, { id: "" }] }), []); +}); diff --git a/test/scenario_runner.cpp b/test/scenario_runner.cpp index cec92f3..a39e815 100644 --- a/test/scenario_runner.cpp +++ b/test/scenario_runner.cpp @@ -21,6 +21,7 @@ #include "light/effects/ParticlesEffect.h" #include "light/effects/GlowParticlesEffect.h" #include "light/effects/CheckerboardEffect.h" +#include "light/moonlive/MoonLiveEffect.h" #include "light/effects/SpiralEffect.h" #include "light/effects/RingsEffect.h" #include "light/effects/RipplesEffect.h" @@ -187,6 +188,7 @@ static void registerScenarioTypes() { mm::ModuleFactory::registerType("ParticlesEffect"); mm::ModuleFactory::registerType("GlowParticlesEffect"); mm::ModuleFactory::registerType("CheckerboardEffect"); + mm::ModuleFactory::registerType("MoonLiveEffect"); mm::ModuleFactory::registerType("SpiralEffect"); mm::ModuleFactory::registerType("RingsEffect"); mm::ModuleFactory::registerType("RipplesEffect"); diff --git a/test/unit/core/unit_moonlive_compiler.cpp b/test/unit/core/unit_moonlive_compiler.cpp new file mode 100644 index 0000000..a39396a --- /dev/null +++ b/test/unit/core/unit_moonlive_compiler.cpp @@ -0,0 +1,112 @@ +// @module MoonLive + +#include "doctest.h" +#include "core/moonlive/MoonLiveCompiler.h" +#include "core/moonlive/moonlive_emit.h" +#include "core/moonlive/MoonLive.h" + +#include +#include +#include +#include + +// MoonLive Stage 2: the minimal real front-end (lex → parse → codegen) for one statement, +// `fill(r,g,b);`. The headline property is the GOLDEN-BYTES EQUIVALENCE — parsing the source +// produces byte-for-byte the same machine code the hand-written emitFill produces, so the +// front-end provably introduces no codegen of its own (it just drives the same emitter). The +// rest pins the parser's diagnostics, the no-language-leak proof at its cheapest. + +using namespace mm; + +TEST_CASE("compileSource emits the SAME bytes as the hand-written emitFill (golden)") { + const uint8_t cases[][3] = {{0, 0, 255}, {10, 20, 200}, {255, 255, 255}, {0, 0, 0}, {1, 2, 3}}; + for (auto& c : cases) { + char src[64]; + std::snprintf(src, sizeof(src), "fill(%u, %u, %u);", c[0], c[1], c[2]); + + uint8_t fromSource[256]; + auto r = moonlive::compileSource(src, fromSource, sizeof(fromSource)); + REQUIRE(r.ok); + REQUIRE(r.len > 0); + + uint8_t golden[256]; + size_t glen = moonlive::emitFill(golden, sizeof(golden), c[0], c[1], c[2]); + REQUIRE(glen == r.len); + CHECK(std::memcmp(fromSource, golden, glen) == 0); // byte-for-byte identical + } +} + +TEST_CASE("compileSource tolerates whitespace and no spaces") { + uint8_t a[256], b[256]; + auto ra = moonlive::compileSource("fill(0,0,255);", a, sizeof(a)); + auto rb = moonlive::compileSource(" fill ( 0 ,0, 255 ) ; ", b, sizeof(b)); + REQUIRE(ra.ok); REQUIRE(rb.ok); + REQUIRE(ra.len == rb.len); + CHECK(std::memcmp(a, b, ra.len) == 0); +} + +TEST_CASE("compileSource rejects malformed programs with a diagnostic, never crashes") { + uint8_t out[256]; + struct Case { const char* src; }; + const Case bad[] = { + {""}, // empty + {"fil(0,0,255);"}, // wrong name + {"fill 0,0,255);"}, // missing ( + {"fill(0,0);"}, // too few args + {"fill(0,0,255,9);"}, // too many args + {"fill(0,0,300);"}, // out of range + {"fill(0,x,255);"}, // non-number + {"fill(0,0,255)"}, // missing ; + {"fill(0,0,255); extra"}, // trailing junk + {"fill(0,0,255);;"}, // extra ; + {"@#$"}, // garbage + }; + for (auto& c : bad) { + auto r = moonlive::compileSource(c.src, out, sizeof(out)); + CHECK_FALSE(r.ok); + CHECK(std::strlen(r.error) > 0); // a human-readable message, not empty + } +} + +TEST_CASE("compileSource reports a too-small code buffer (degrades)") { + uint8_t tiny[2]; + auto r = moonlive::compileSource("fill(0,0,255);", tiny, sizeof(tiny)); + CHECK_FALSE(r.ok); +} + +TEST_CASE("MoonLive.compile(source) compiles and runs the parsed program") { + moonlive::MoonLive engine; + REQUIRE(engine.compile("fill(0, 0, 255);")); // blue, from source text + REQUIRE(engine.ok()); + + std::vector buf(4 * 3, 0xAB); + engine.run(buf.data(), 4, 3, /*t*/ 0); + for (int i = 0; i < 4; i++) { + CHECK(buf[i*3 + 0] == 0); + CHECK(buf[i*3 + 1] == 0); + CHECK(buf[i*3 + 2] == 255); + } +} + +TEST_CASE("MoonLive.compile(source) on a bad script leaves the engine !ok with an error") { + moonlive::MoonLive engine; + CHECK_FALSE(engine.compile("fill(oops);")); + CHECK_FALSE(engine.ok()); + CHECK(std::strlen(engine.error()) > 0); + // run() after a failed compile is a safe no-op. + std::vector buf(3, 0xAB); + engine.run(buf.data(), 1, 3, 0); + CHECK(buf[0] == 0xAB); // untouched — nothing rendered +} + +TEST_CASE("MoonLive recompiling a new source swaps the program live") { + moonlive::MoonLive engine; + REQUIRE(engine.compile("fill(255, 0, 0);")); // red + std::vector buf(3, 0); + engine.run(buf.data(), 1, 3, 0); + CHECK(buf[0] == 255); CHECK(buf[2] == 0); + + REQUIRE(engine.compile("fill(0, 0, 255);")); // recompile -> blue + engine.run(buf.data(), 1, 3, 0); + CHECK(buf[0] == 0); CHECK(buf[2] == 255); +} diff --git a/test/unit/core/unit_moonlive_fill.cpp b/test/unit/core/unit_moonlive_fill.cpp new file mode 100644 index 0000000..3f55ca7 --- /dev/null +++ b/test/unit/core/unit_moonlive_fill.cpp @@ -0,0 +1,109 @@ +// @module MoonLive + +#include "doctest.h" +#include "core/moonlive/MoonLive.h" +#include "core/moonlive/moonlive_emit.h" +#include "platform/platform.h" + +#include +#include + +// MoonLive Stage 1a: the load-bearing slice — emit a fixed-colour fill as native code, +// place it in executable memory, and call it over a buffer. These tests run the WHOLE path +// in-process on the desktop host backend (the host ISA's emit + platform::allocExec/ +// writeExec + a real call), so they prove emit → exec → call → buffer-write works off +// hardware. The Xtensa backend is validated by the live S3 run, not here. + +using namespace mm; + +TEST_CASE("MoonLive emitFill produces a non-empty routine") { + uint8_t code[256]; + size_t n = moonlive::emitFill(code, sizeof(code), 1, 2, 3); + CHECK(n > 0); + CHECK(n <= sizeof(code)); +} + +TEST_CASE("MoonLive emitFill rejects a too-small buffer (degrades, no overrun)") { + uint8_t tiny[2]; + CHECK(moonlive::emitFill(tiny, sizeof(tiny), 1, 2, 3) == 0); +} + +TEST_CASE("MoonLive compiles and fills a buffer with the chosen colour") { + moonlive::MoonLive engine; + REQUIRE(engine.compile(/*r*/ 10, /*g*/ 20, /*b*/ 200)); + REQUIRE(engine.ok()); + + // A 5-light, 3-channel buffer pre-filled with a sentinel so a missed write shows. + std::vector buf(5 * 3, 0xAB); + engine.run(buf.data(), 5, 3, /*t*/ 0); + + for (int i = 0; i < 5; i++) { + CHECK(buf[i*3 + 0] == 10); + CHECK(buf[i*3 + 1] == 20); + CHECK(buf[i*3 + 2] == 200); + } +} + +TEST_CASE("MoonLive run on zero lights writes nothing (robust to empty)") { + moonlive::MoonLive engine; + REQUIRE(engine.compile(255, 0, 0)); + std::vector buf(3, 0xAB); + engine.run(buf.data(), 0, 3, 0); // nLights == 0 + CHECK(buf[0] == 0xAB); // untouched +} + +TEST_CASE("MoonLive recompile swaps the colour; free returns to !ok") { + moonlive::MoonLive engine; + REQUIRE(engine.compile(1, 1, 1)); + std::vector buf(3, 0); + engine.run(buf.data(), 1, 3, 0); + CHECK(buf[0] == 1); + + REQUIRE(engine.compile(9, 8, 7)); // recompile a new colour + engine.run(buf.data(), 1, 3, 0); + CHECK(buf[0] == 9); CHECK(buf[1] == 8); CHECK(buf[2] == 7); + + engine.free(); + CHECK_FALSE(engine.ok()); + // run() after free is a safe no-op (no call through null). + engine.run(buf.data(), 1, 3, 0); +} + +TEST_CASE("MoonLive animated fill derives colour from the per-frame t") { + moonlive::MoonLive engine; + REQUIRE(engine.compileAnimated()); + REQUIRE(engine.ok()); + std::vector buf(4 * 3, 0); + + // red = (t>>3)&0xFF, green=0, blue=64. Two different t -> two different reds, proving + // the runtime arg reaches the emitted native code and changes its output. + engine.run(buf.data(), 4, 3, /*t*/ 0); + for (int i = 0; i < 4; i++) { + CHECK(buf[i*3 + 0] == 0); // t>>3 == 0 + CHECK(buf[i*3 + 1] == 0); + CHECK(buf[i*3 + 2] == 64); + } + + engine.run(buf.data(), 4, 3, /*t*/ 800); // 800>>3 = 100 + for (int i = 0; i < 4; i++) { + CHECK(buf[i*3 + 0] == 100); + CHECK(buf[i*3 + 2] == 64); + } + + engine.run(buf.data(), 4, 3, /*t*/ 2048); // 2048>>3 = 256 -> &0xFF = 0 + CHECK(buf[0] == 0); // wraps at 256, as a byte does +} + +TEST_CASE("platform allocExec returns usable executable memory, freeExec releases it") { + void* blk = platform::allocExec(64); + REQUIRE(blk != nullptr); + // Copy the emitted fill in via writeExec (the IRAM/cache-safe path) and call it. + uint8_t code[256]; + size_t n = moonlive::emitFill(code, sizeof(code), 7, 7, 7); + platform::writeExec(blk, code, n); + auto fn = reinterpret_cast(blk); + std::vector buf(3, 0); + fn(buf.data(), 1, 3); + CHECK(buf[0] == 7); + platform::freeExec(blk, 64); +} From 475a8d1b61a46fcbe8a5bbc5e5dec404f721bbb1 Mon Sep 17 00:00:00 2001 From: ewowi Date: Sat, 27 Jun 2026 00:09:05 +0200 Subject: [PATCH 4/8] MoonLive: P4/RISC-V backend, CodeRabbit fixes, live scenario on 3 ISAs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extends the MoonLive live-script engine to a third ISA and hardens it. The scripted fill(r,g,b) effect now compiles to native code on the ESP32-P4 (RISC-V) as well as Xtensa (classic/S3) and the desktop host — adding the P4 touched only the per-ISA emitter and its sdkconfig, proving the engine/front-end/binding seam decouples. Also processes the first CodeRabbit review: buffer-shape guards so a sub-RGB or empty layout can't overrun, a non-copyable engine, word-padded exec allocation, a shared installer add/clear validity check, Windows-ABI guards, and present-tense docs. A new live scenario exercises the whole wired-module lifecycle and runs in-process plus live on PC/S3/P4. KPI: 16384lights | PC:403KB | tick:132/91/120/9/1/625/36/15/18/59/125/13/35us(FPS:7575/10989/8333/111111/1000000/1600/27777/66666/55555/16949/8000/76923/28571) | ESP32:1234KB | src:106(21215) | test:75(11688) | lizard:79w (MoonLive live this session via the new scenario: S3 ~250 FPS, P4 ~90 FPS, heap steady across broken-script + remove/re-add; the KPI script's hardcoded serial port missed the live ESP32 tick capture, values from the device API.) Core: - platform: allocExec rounds the request up to a 4-byte word — writeExec's final partial-word store and the esp_cache_msync length both round up, so the block must hold the whole word (🐇). writeExec now does a two-step cache flush on ESP32 — data-cache writeback (C2M) then I-cache invalidate — because the P4's exec region is cache-backed and a single TYPE_INST sync left freshly-written code in the data cache (illegal-instruction crash); correct on Xtensa and RISC-V. - MoonLive: run() guards a null buffer / zero lights / cpl<3 so the RGB-writing routines can't overrun a sub-RGB or empty layout (the every-grid-size hard rule) (🐇). Engine made non-copyable (owns a heap exec block) (🐇). compileSource rejects a null/empty output buffer (🐇). Light domain: - MoonLiveEffect: unchanged behaviour; comments rewritten present-tense (🐇). UI / Installer: - config-ops.js: factored a shared isAddable() helper used by both the clear pre-pass and the add pass, so a malformed module can't trigger a clearChildren without a matching add (🐇). Tests: - unit_moonlive_fill: cpl 1/2 + null-buffer no-op test (pins the overrun guard) (🐇). - scenario_MoonLiveEffect_livescript (new): the scripted effect as a wired MoonModule — add, live source recompile, broken-script degrade (no crash, status diagnostic), recover, grid 1x1 + back, remove, re-add. Runs in-process each commit and live over REST on PC/S3/P4. - config-ops.test.mjs: guarded the ordering assertion against clears-only plans (🐇). Platform / Build: - esp32/moonlive_emit.cpp: added the RISC-V (P4) backend — a #elif defined(__riscv) branch with the fill + animated routines, copied verbatim from the assembler. Adding the ISA changed nothing in the engine, front-end, or binding (the Stage 0.5 seam-proof). - desktop/moonlive_emit.cpp: the x86-64 SysV blob is gated && !defined(_WIN32) (the Windows x64 ABI differs); #error covers Win64 until a template exists (🐇). - desktop/platform_desktop.cpp: writeExec uses FlushInstructionCache on _WIN32 (MSVC lacks __builtin___clear_cache) (🐇). - sdkconfig.defaults.esp32p4-eth: enable the executable heap (disable memprot) for the P4's JIT, same as the S3 — scoped to the testbench board, not global. Docs: - MoonLiveEffect.md: present-tense rewrite; documents the source control, the per-ISA backends incl P4, and the new live scenario (🐇). - Plan-20260626 … (native codegen spike): marked (shipped). - 🐇 mm_core "header-only/INTERFACE" boundary, core-includes-platform, sdkconfig "global" overlay: accepted as-is — coding-standards.md makes core .h+.cpp that calls the platform seam, and the exec-heap config is already per-board, not global; the suggested changes would diverge from the documented conventions. --- ...age 0 (native codegen spike) (shipped).md} | 0 docs/install/config-ops.js | 17 +- .../light/moonlive/MoonLiveEffect.md | 12 +- esp32/sdkconfig.defaults.esp32p4-eth | 8 + src/core/moonlive/MoonLive.cpp | 7 +- src/core/moonlive/MoonLive.h | 39 +- src/core/moonlive/MoonLiveCompiler.cpp | 16 +- src/core/moonlive/MoonLiveCompiler.h | 8 +- src/core/moonlive/moonlive_emit.h | 21 +- src/light/moonlive/MoonLiveEffect.h | 16 +- src/platform/desktop/moonlive_emit.cpp | 24 +- src/platform/desktop/platform_desktop.cpp | 11 +- src/platform/esp32/moonlive_emit.cpp | 88 ++- src/platform/esp32/platform_esp32.cpp | 33 +- test/js/config-ops.test.mjs | 3 +- .../scenario_MoonLiveEffect_livescript.json | 588 ++++++++++++++++++ .../light/scenario_modifier_chain.json | 26 +- test/scenarios/light/scenario_perf_light.json | 4 +- test/unit/core/unit_moonlive_fill.cpp | 14 + 19 files changed, 831 insertions(+), 104 deletions(-) rename docs/history/plans/{Plan-20260626 - MoonLive Stage 0 (native codegen spike).md => Plan-20260626 - MoonLive Stage 0 (native codegen spike) (shipped).md} (100%) create mode 100644 test/scenarios/light/scenario_MoonLiveEffect_livescript.json diff --git a/docs/history/plans/Plan-20260626 - MoonLive Stage 0 (native codegen spike).md b/docs/history/plans/Plan-20260626 - MoonLive Stage 0 (native codegen spike) (shipped).md similarity index 100% rename from docs/history/plans/Plan-20260626 - MoonLive Stage 0 (native codegen spike).md rename to docs/history/plans/Plan-20260626 - MoonLive Stage 0 (native codegen spike) (shipped).md diff --git a/docs/install/config-ops.js b/docs/install/config-ops.js index 060ad30..fb3ac8b 100644 --- a/docs/install/config-ops.js +++ b/docs/install/config-ops.js @@ -14,20 +14,29 @@ // replaceChildren) BEFORE adding. The device's clearChildren preserves boot-wired children // (Preview, Improv), so a parent's apparatus survives and only swappable pipeline content // is replaced — the no-erase path then converges to the same tree an erase+flash produces. +// A module the entry adds: a non-empty id, a parent to add it under, and a type. The clear +// pre-pass and the add pass MUST agree on this (one helper, used by both) — otherwise a +// malformed module could get a clearChildren on its parent without a matching add. +function isAddable(m) { + return !!(m && typeof m === "object" && + typeof m.id === "string" && m.id && + typeof m.parent_id === "string" && m.parent_id && + m.type); +} + export function planConfigOps(entry) { const ops = []; const modules = entry && Array.isArray(entry.modules) ? entry.modules : []; // Modules the entry adds fresh — no need to clear their children (a just-created // module has none), so a parent that is itself added is dropped from the clear-set. - const addedIds = new Set( - modules.filter(m => m && typeof m.id === "string" && m.id && m.parent_id && m.type).map(m => m.id)); + const addedIds = new Set(modules.filter(isAddable).map(m => m.id)); const clearParents = new Set(); for (const m of modules) { if (!m || typeof m !== "object") continue; if (m.replaceChildren && typeof m.id === "string" && m.id) clearParents.add(m.id); - if (typeof m.parent_id === "string" && m.parent_id && m.type) clearParents.add(m.parent_id); + if (isAddable(m)) clearParents.add(m.parent_id); } for (const parent of clearParents) { if (addedIds.has(parent)) continue; // freshly added → nothing to clear @@ -36,7 +45,7 @@ export function planConfigOps(entry) { for (const m of modules) { if (!m || typeof m !== "object" || typeof m.id !== "string" || m.id === "") continue; - if (m.parent_id && m.type) ops.push({ op: "add", type: m.type, id: m.id, parent: m.parent_id }); + if (isAddable(m)) ops.push({ op: "add", type: m.type, id: m.id, parent: m.parent_id }); const controls = m.controls; if (controls && typeof controls === "object") { for (const [control, value] of Object.entries(controls)) { diff --git a/docs/moonmodules/light/moonlive/MoonLiveEffect.md b/docs/moonmodules/light/moonlive/MoonLiveEffect.md index 5972c8b..cb6d775 100644 --- a/docs/moonmodules/light/moonlive/MoonLiveEffect.md +++ b/docs/moonmodules/light/moonlive/MoonLiveEffect.md @@ -1,8 +1,8 @@ # MoonLive -MoonLive is projectMM's **live-script engine** — author an effect (or layout, modifier, driver, core rule) as text and run it on a running device, compiled to native machine code so it executes at near-hand-written speed in the render hot path. The full design is the forward-looking analysis [livescripts-analysis-top-down.md](../../../backlog/livescripts-analysis-top-down.md); this page documents what currently ships. +MoonLive is projectMM's **live-script engine** — author an effect as text and run it on a running device, compiled to native machine code so it executes at near-hand-written speed in the render hot path. The broader design lives in [livescripts-analysis-top-down.md](../../../backlog/livescripts-analysis-top-down.md) (a backlog design study); this page documents the module. -**Status: Stage 2 (first language slice).** A scripted effect now carries its **script source** as an editable, persisted text control, and a real front-end (lexer → parser → codegen) compiles it to native code on the next tick. The grammar is one statement — `fill(r, g, b);` — the smallest real language: no expressions, variables, or control flow yet, but the compiler shape is genuine and grows rule by rule. Earlier increments proved the load-bearing path beneath it: native code MoonLive generated runs on real Xtensa, in the render tick, writing the real buffer, animating from a per-frame value, without a crash. The front-end's headline property is **golden-bytes equivalence** — parsing `fill(0,0,255)` produces byte-for-byte the same machine code the hand-written emitter does, so the parser provably adds no codegen of its own. +A scripted effect carries its **script source** as an editable, persisted text control, and a front-end (lexer → parser → codegen) compiles it to native code on the next tick. The grammar is one statement — `fill(r, g, b);`. The compiler emits machine code for whichever ISA the device runs (Xtensa on the classic/S3, RISC-V on the P4) or the host ISA on desktop, places it in executable memory, and the engine calls it each render tick to write the buffer. Its headline property is **golden-bytes equivalence** — parsing `fill(0,0,255)` produces byte-for-byte the same machine code the hand-written emitter does, so the parser adds no codegen of its own. (Status: one statement; the grammar is the current surface, not the ceiling — see the design study.) ## Controls @@ -12,8 +12,8 @@ MoonLive is projectMM's **live-script engine** — author an effect (or layout, - **`MoonLive`** (`src/core/moonlive/MoonLive.h/.cpp`) — the **domain-neutral engine core**. Owns a block of executable memory; `compile(source)` runs the front-end and places the emitted code, `run(buf, nLights, cpl, t)` calls it, `free()`/`ok()`/`error()` round out the lifecycle. Includes only ``, the compiler/emitter seams, and the platform seam — never `EffectBase`, `Buffer`, or any projectMM type. - **`MoonLiveCompiler`** (`src/core/moonlive/MoonLiveCompiler.h/.cpp`) — the **platform-independent front-end**: a hand-written recursive-descent lexer + parser for the `fill(r,g,b);` grammar, plus codegen that drives the per-ISA emitter. Pure (source in, bytes out, deterministic), so it unit-tests with no hardware. Knows the *language*, never an ISA. -- **`emitFill`** (`src/core/moonlive/moonlive_emit.h`) — the **per-ISA backend seam**. A neutral declaration; the implementation is the ISA's machine code, behind the platform boundary: `src/platform/esp32/moonlive_emit.cpp` (Xtensa) and `src/platform/desktop/moonlive_emit.cpp` (host arm64 / x86-64). The engine and front-end never branch on ISA — they ask for bytes and run them. This is the IR/backend seam at its first increment (no IR yet; one routine). -- **`MoonLiveEffect`** (`src/light/moonlive/MoonLiveEffect.h`) — the **thin binding**: a first-class `EffectBase` carrying the `source` control, whose `loop()` delegates to the engine over its own `buffer()`. The engine/binding split (the engine is projectMM-agnostic; the binding is the only coupled layer) is what lets projectMM be a clean library later. +- **`emitFill`** (`src/core/moonlive/moonlive_emit.h`) — the **per-ISA backend seam**. A neutral declaration; the implementation is the ISA's machine code, behind the platform boundary: `src/platform/esp32/moonlive_emit.cpp` (Xtensa for the classic/S3, RISC-V for the P4) and `src/platform/desktop/moonlive_emit.cpp` (host arm64 / x86-64). The engine and front-end never branch on ISA — they ask for bytes and run them; an ISA is a new branch in the emitter. +- **`MoonLiveEffect`** (`src/light/moonlive/MoonLiveEffect.h`) — the **thin binding**: a first-class `EffectBase` carrying the `source` control, whose `loop()` delegates to the engine over its own `buffer()`. The engine is projectMM-agnostic; the binding is the only coupled layer, which keeps the engine a clean, reusable core. ## Cross-domain wiring @@ -27,7 +27,9 @@ MoonLive's native-codegen approach — compile a small C-like language straight ## Tests -[unit_moonlive_fill](../../../../test/unit/core/unit_moonlive_fill.cpp) runs the engine path in-process on the desktop host backend: `emitFill`/`emitAnimatedFill` produce a non-empty routine (and reject a too-small buffer), `compile` + `run` fill a buffer with the chosen colour, the animated routine derives its colour from the per-frame `t`, zero-lights writes nothing, recompile swaps the colour, `free` returns to `!ok()`, and `allocExec`/`writeExec`/`freeExec` round-trip a callable block. [unit_moonlive_compiler](../../../../test/unit/core/unit_moonlive_compiler.cpp) pins the front-end: the **golden-bytes equivalence** (parsed `fill(r,g,b)` == hand-emitted bytes), whitespace tolerance, every parser diagnostic (wrong name, bad arity, out-of-range, missing punctuation, trailing junk — no crash on any malformed input), and the live source-recompile swap. The Xtensa backend is validated by the live S3 run (a `MoonLiveEffect` on a Layer lights the grid from its `source`), which the desktop tests can't reach. +[unit_moonlive_fill](../../../../test/unit/core/unit_moonlive_fill.cpp) runs the engine path in-process on the desktop host backend: `emitFill`/`emitAnimatedFill` produce a non-empty routine (and reject a too-small buffer), `compile` + `run` fill a buffer with the chosen colour, the animated routine derives its colour from the per-frame `t`, zero-lights writes nothing, recompile swaps the colour, `free` returns to `!ok()`, and `allocExec`/`writeExec`/`freeExec` round-trip a callable block. [unit_moonlive_compiler](../../../../test/unit/core/unit_moonlive_compiler.cpp) pins the front-end: the **golden-bytes equivalence** (parsed `fill(r,g,b)` == hand-emitted bytes), whitespace tolerance, every parser diagnostic (wrong name, bad arity, out-of-range, missing punctuation, trailing junk — no crash on any malformed input), and the live source-recompile swap. `unit_moonlive_fill` also pins the buffer-shape guards (a null buffer, zero lights, or a sub-RGB `cpl<3` layout is left untouched, no overrun). + +[scenario_MoonLiveEffect_livescript](../../../../test/scenarios/light/scenario_MoonLiveEffect_livescript.json) exercises the effect **as a wired MoonModule** — what the unit tests can't reach: add it, live-edit the `source` to recolour (recompile), push a broken script (prior code keeps running, the parse error surfaces in the status, no crash), recover, resize the grid to 1×1 and back while rendering (the every-grid-size hard rule), then remove and re-add (exec memory re-acquired clean). It runs in-process on the desktop backend each commit, and the same JSON runs live over REST against the device backends. The Xtensa/RISC-V backends are validated by the live S3/P4 runs (a `MoonLiveEffect` on a Layer lights the grid from its `source`), which the desktop tests can't reach. ## Source diff --git a/esp32/sdkconfig.defaults.esp32p4-eth b/esp32/sdkconfig.defaults.esp32p4-eth index 59cf6bb..eeb2528 100644 --- a/esp32/sdkconfig.defaults.esp32p4-eth +++ b/esp32/sdkconfig.defaults.esp32p4-eth @@ -31,3 +31,11 @@ CONFIG_ETH_USE_ESP32_EMAC=y CONFIG_ETH_DMA_BUFFER_SIZE=512 CONFIG_ETH_DMA_RX_BUFFER_NUM=10 CONFIG_ETH_DMA_TX_BUFFER_NUM=10 + +# MoonLive native codegen needs an executable heap (allocExec → MALLOC_CAP_EXEC). Like the +# S3, the P4 has hardware memory protection (PMP) that IDF couples to disabling the exec +# heap, so disable memprot to make MALLOC_CAP_EXEC available — the standard ESP32-JIT config. +# Safety for scripted code is the staged bounds/watchdog checks, not the hardware W^X wall. +# (Same rationale as sdkconfig.defaults.esp32s3-n16r8.) +CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n +CONFIG_HEAP_HAS_EXEC_HEAP=y diff --git a/src/core/moonlive/MoonLive.cpp b/src/core/moonlive/MoonLive.cpp index 3b355cd..26b1c23 100644 --- a/src/core/moonlive/MoonLive.cpp +++ b/src/core/moonlive/MoonLive.cpp @@ -4,10 +4,9 @@ namespace mm::moonlive { -// Generous fixed cap for the Stage-1 routines — a fill loop is a few dozen bytes on any -// ISA; the emitter returns the real length and the unused tail is harmless. Sized once here -// so the emitter never has to worry about growth (a real program-sized arena comes with the -// language, later). +// Fixed cap for an emitted routine — a fill loop is a few dozen bytes on any ISA; the emitter +// returns the real length and the unused tail is harmless. Sized once here, word-aligned so +// allocExec/writeExec's word-rounding never exceeds it. static constexpr size_t kCodeCap = 256; // Copy `len` already-emitted bytes into a fresh exec block. writeExec hides the ISA quirks diff --git a/src/core/moonlive/MoonLive.h b/src/core/moonlive/MoonLive.h index e723052..52b0786 100644 --- a/src/core/moonlive/MoonLive.h +++ b/src/core/moonlive/MoonLive.h @@ -5,35 +5,38 @@ #include "core/moonlive/moonlive_emit.h" // MoonLive — the live-script engine core (domain-neutral, §3.1/§3.9 of -// livescripts-analysis-top-down.md). Stage 1a: no language yet — compile() asks the per-ISA -// emitter for one fixed routine (fill every light a colour), places it in executable memory, -// and run() calls it over a host-supplied buffer. The whole compiler (lexer → parser → IR → -// codegen) grows in behind compile() over later stages; this is the load-bearing first slice -// that proves emit → allocExec → call → write-the-buffer works on real Xtensa. +// livescripts-analysis-top-down.md). compile() turns a program into native code — either a +// source string (via MoonLiveCompiler) or a fixed routine direct from the emitter — places it +// in executable memory, and run() calls it over a host-supplied buffer. The path is +// emit → allocExec → call → write-the-buffer, running on Xtensa, RISC-V, and the desktop host. // -// Neutral by construction: the engine includes only , the emitter seam, and the -// platform seam — never EffectBase, Buffer, or any projectMM type. The binding +// Neutral by construction: the engine includes only , the compiler/emitter seams, and +// the platform seam — never EffectBase, Buffer, or any projectMM type. The binding // (src/light/moonlive/MoonLiveEffect.h) wraps it as a MoonModule. namespace mm::moonlive { class MoonLive { public: + MoonLive() = default; ~MoonLive() { free(); } - // Compile the (Stage-1a fixed) program for a colour: emit the routine, copy it into an - // exec block, ready it to call. Returns ok(). A failure (no exec memory, emit too big) + // Owns a heap-backed exec block (freed in the destructor), so copying would duplicate + // ownership and double-free. Non-copyable; each scripted module holds its own engine. + MoonLive(const MoonLive&) = delete; + MoonLive& operator=(const MoonLive&) = delete; + + // Compile a fixed-colour program direct from the emitter: emit the routine, copy it into + // an exec block, ready it to call. Returns ok(). A failure (no exec memory, emit too big) // leaves the engine !ok() with an error() — the caller degrades, never crashes. bool compile(uint8_t r, uint8_t g, uint8_t b); - // Stage 2: compile SOURCE TEXT. The front-end (MoonLiveCompiler) lexes/parses the - // `fill(r,g,b);` statement and emits the same code compile(r,g,b) would. A parse error - // leaves the engine !ok() with error() pointing at the diagnostic — the script editor's - // failure path. This is the real compile path; the typed overloads above are the - // hand-driven Stage-1 spikes the source path now subsumes for the fill program. + // Compile SOURCE TEXT. The front-end (MoonLiveCompiler) lexes/parses the `fill(r,g,b);` + // statement and emits the same code compile(r,g,b) would. A parse error leaves the engine + // !ok() with error() pointing at the diagnostic — the script editor's failure path. bool compile(const char* source); - // Stage 1b: compile the animated routine (colour derived from the per-frame `t`). + // Compile the animated routine (colour derived from the per-frame `t`). bool compileAnimated(); bool ok() const { return fn_ != nullptr || anim_ != nullptr; } @@ -41,8 +44,12 @@ class MoonLive { // The hot path: run the compiled routine over the host's buffer. `t` is the host's // elapsed() ms; a static routine ignores it, an animated one derives its colour from - // it. No-op if !ok(), so a failed compile renders nothing rather than calling null. + // it. No-op if !ok() (a failed compile renders nothing). The emitted routines write + // channels +0/+1/+2 per light, so a buffer that can't hold RGB — null, zero lights, or + // fewer than 3 channels per light — is left untouched rather than overrun (robust to any + // grid size / layout, the hard rule). void run(uint8_t* buf, uint32_t nLights, uint8_t cpl, uint32_t t) const { + if (!buf || nLights == 0 || cpl < 3) return; if (fn_) fn_(buf, nLights, cpl); else if (anim_) anim_(buf, nLights, cpl, t); } diff --git a/src/core/moonlive/MoonLiveCompiler.cpp b/src/core/moonlive/MoonLiveCompiler.cpp index 6466566..173315e 100644 --- a/src/core/moonlive/MoonLiveCompiler.cpp +++ b/src/core/moonlive/MoonLiveCompiler.cpp @@ -6,9 +6,9 @@ namespace mm::moonlive { namespace { // --- Lexer --------------------------------------------------------------------------- -// The token kinds the Stage-2 grammar needs. A hand-written recursive-descent front-end -// (the textbook embedded-script default) lexes on demand: the parser pulls one token at a -// time. No token table is materialised — peek()/advance() walk the source directly. +// The token kinds the grammar needs. A hand-written recursive-descent front-end (the textbook +// embedded-script default) lexes on demand: the parser pulls one token at a time, and +// advance() walks the source directly without building a token list. enum class Tok { Ident, Number, LParen, RParen, Comma, Semicolon, End, Error }; struct Lexer { @@ -62,8 +62,7 @@ struct Lexer { // --- AST + Parser -------------------------------------------------------------------- // The only production: program := "fill" "(" number "," number "," number ")" ";" End. -// The AST for it is just the three colour bytes — there is no node hierarchy at one -// statement (a real tree arrives with the second statement type, concrete-first). +// The AST for it is just the three colour bytes — a single statement needs no node hierarchy. struct Parsed { bool ok = false; const char* error = ""; @@ -116,13 +115,14 @@ Parsed parse(const char* source) { CompileResult compileSource(const char* source, uint8_t* out, size_t cap) { CompileResult r; if (!source) { r.error = "no source"; return r; } + if (!out || cap == 0) { r.error = "no code buffer"; return r; } // never emit through a null/empty buffer Parsed p = parse(source); if (!p.ok) { r.error = p.error; r.errorCol = p.errorCol; return r; } - // Codegen: the parsed colour drives the SAME per-ISA emitter the hand-written 1a path - // uses — so a parsed `fill(0,0,255)` produces byte-for-byte the bytes emitFill(0,0,255) - // does (the golden-bytes equivalence the test asserts). No IR yet at one statement. + // Codegen: the parsed colour drives the SAME per-ISA emitter the typed compile path uses — + // so a parsed `fill(0,0,255)` produces byte-for-byte the bytes emitFill(0,0,255) does (the + // golden-bytes equivalence the test asserts). size_t len = emitFill(out, cap, p.r, p.g, p.b); if (len == 0) { r.error = "code buffer too small"; return r; } r.ok = true; diff --git a/src/core/moonlive/MoonLiveCompiler.h b/src/core/moonlive/MoonLiveCompiler.h index 0fb4e97..716b3f0 100644 --- a/src/core/moonlive/MoonLiveCompiler.h +++ b/src/core/moonlive/MoonLiveCompiler.h @@ -4,16 +4,12 @@ #include // MoonLive front-end (§3.2) — the platform-independent compiler: source text → tokens → -// AST → native code (via the per-ISA emitter). Stage 2 is the smallest real slice: it -// recognises ONE statement, +// AST → native code (via the per-ISA emitter). It recognises one statement, // // fill(, , ); // // three integer 0..255 args, and emits the SAME machine code the hand-written emitFill -// produces (the golden-bytes equivalence is the no-language-leak proof). No expressions, -// variables, or control flow yet — the lexer/parser/codegen shape is real, the grammar is -// one rule. It grows rule by rule from here; the IR seam is introduced when a second -// statement/type forces it (concrete-first). +// produces (the golden-bytes equivalence is the no-language-leak proof). // // Neutral by construction: the compiler knows the LANGUAGE, never an ISA — codegen calls // the platform's emitFill, so a different backend changes nothing here. diff --git a/src/core/moonlive/moonlive_emit.h b/src/core/moonlive/moonlive_emit.h index ff6e1f4..eea7d8a 100644 --- a/src/core/moonlive/moonlive_emit.h +++ b/src/core/moonlive/moonlive_emit.h @@ -6,11 +6,10 @@ // MoonLive — per-ISA code emitter (the backend seam, §3.2 of livescripts-analysis-top-down.md). // // This header is the NEUTRAL declaration the engine calls; the implementation is per-ISA and -// lives behind the platform boundary (src/platform//moonlive_emit.cpp): Xtensa on -// ESP32, the host ISA (arm64 / x86-64) on desktop. The engine (src/core/moonlive/) never -// branches on ISA — it asks for bytes and runs them. That is the IR/backend seam at its -// first, smallest increment: no IR yet (one fixed routine, no language), but the boundary is -// already where it belongs. +// lives behind the platform boundary (src/platform//moonlive_emit.cpp): Xtensa on the +// classic/S3, RISC-V on the P4, the host ISA (arm64 / x86-64) on desktop. The engine +// (src/core/moonlive/) never branches on ISA — it asks for bytes and runs them. Adding an ISA +// is a new branch in the emitter; the engine and front-end are unchanged. // // The emitted routine's C signature is FillFn: write a fixed (r,g,b) to every light — // buf[i*cpl+0..2] = r,g,b for i in [0,nLights) — then return. The engine copies the bytes @@ -20,21 +19,19 @@ namespace mm::moonlive { using FillFn = void (*)(uint8_t* buf, uint32_t nLights, uint8_t cpl); -// Stage 1b: the routine also takes a per-frame `t` (the host's elapsed() ms), so the host -// feeds a changing value into the SAME native code each tick and the output animates. The +// The animated routine also takes a per-frame `t` (the host's elapsed() ms), so the host +// feeds a changing value into the same native code each tick and the output animates. The // engine passes elapsed() through run(). using AnimFn = void (*)(uint8_t* buf, uint32_t nLights, uint8_t cpl, uint32_t t); // Emit the fixed-colour fill routine's machine code into `out` (capacity `cap` bytes), for // the ISA this translation unit was compiled for, with the colour baked in. Returns the // number of bytes written, or 0 if `cap` is too small (the caller degrades). The emitted -// bytes ARE the function — the engine makes `out` executable and casts it to FillFn. -// -// This is the hand-written reference codegen for Stage 1a; Stage 2 replaces it with -// parser-driven emission that must reproduce the same bytes (the golden-bytes test). +// bytes ARE the function — the engine makes `out` executable and casts it to FillFn. The +// parser-driven codegen (MoonLiveCompiler) reproduces these exact bytes (the golden-bytes test). size_t emitFill(uint8_t* out, size_t cap, uint8_t r, uint8_t g, uint8_t b); -// Stage 1b: emit a routine that derives its colour from the runtime arg `t` — +// Emit a routine that derives its colour from the runtime arg `t` — // red = (t >> 3) & 0xFF, green = 0, blue = 64 for every light. // Proves a per-frame host value flows into the emitted native code and changes the output // (the grid's red ramps over time). Same emit/exec/call path as emitFill, one extra arg. diff --git a/src/light/moonlive/MoonLiveEffect.h b/src/light/moonlive/MoonLiveEffect.h index e15ef00..7665bac 100644 --- a/src/light/moonlive/MoonLiveEffect.h +++ b/src/light/moonlive/MoonLiveEffect.h @@ -9,11 +9,10 @@ // IS a first-class EffectBase (role, controls, lifecycle, generic UI), and its loop() // delegates to a compiled MoonLive over this effect's own buffer. // -// Stage 1a: no script source yet — the engine compiles one fixed routine (fill the layer -// a colour) so the load-bearing path (emit → allocExec → call → write the buffer) is proven -// end to end on real Xtensa. The `source` text control, the language, and script-declared -// controls arrive in later stages; this is the smallest thing that lights an LED via native -// code we generated. +// The effect holds a `source` text control; onBuildState compiles it through the engine and +// loop() runs the emitted native code over the buffer (emit → allocExec → call → write). A +// source edit recompiles live; a parse error shows in the module status and the layer goes +// dark — robust, no reboot. namespace mm { @@ -22,10 +21,9 @@ class MoonLiveEffect : public EffectBase { const char* tags() const override { return "📝"; } // scripted Dim dimensions() const override { return Dim::D2; } - // Stage 2: the effect carries its SCRIPT SOURCE as an editable, persisted text control. - // Editing it recompiles live (the script-editor loop), the same way any control edit - // reshapes a compiled module — no bespoke path. The default is the fill program the - // spike proves end to end. + // The effect carries its SCRIPT SOURCE as an editable, persisted text control. Editing it + // recompiles live (the script-editor loop), the same way any control edit reshapes a + // compiled module — no bespoke path. The default is a solid-blue fill program. void onBuildControls() override { controls_.addText("source", source_, sizeof(source_)); } diff --git a/src/platform/desktop/moonlive_emit.cpp b/src/platform/desktop/moonlive_emit.cpp index 7e5a0c4..0eeb6d7 100644 --- a/src/platform/desktop/moonlive_emit.cpp +++ b/src/platform/desktop/moonlive_emit.cpp @@ -2,13 +2,12 @@ #include -// MoonLive desktop backend (§3.2) — emits the Stage-1a fixed-colour fill as host machine -// code (arm64 or x86-64, chosen at compile time). The desktop backend exists to (a) let a -// unit test EXECUTE generated code in-process — proving allocExec → writeExec → call works -// off-hardware — and (b) be the eventual home of the host codegen for the in-process script -// tests. Hand-encoding the loop here is the same exercise the Xtensa backend does, so the -// engine/binding API is exercised identically on both. (The ESP32/Xtensa backend is the one -// the hardware run validates; this one keeps CI honest.) +// MoonLive desktop backend (§3.2) — emits the fixed-colour fill as host machine code (arm64 +// or x86-64, chosen at compile time). The desktop backend lets a unit test EXECUTE generated +// code in-process — proving allocExec → writeExec → call works off-hardware — and exercises +// the engine/binding API the same way the device backends do. Hand-encoding the loop here is +// the same exercise the Xtensa/RISC-V backends do; the on-device backends are validated by +// the hardware run, this one keeps CI honest. // // The routine implements: void fill(uint8_t* buf, uint32_t nLights, uint8_t cpl) // for (i=0; i(dst), static_cast(dst) + len); #else - // Linux/Windows: the RWX page is plain memory; memcpy suffices. arm64 Linux still - // wants an I-cache sync; on x86-64 __builtin___clear_cache is a harmless no-op. + // Linux: the RWX page is plain memory; memcpy suffices. arm64 Linux still wants an + // I-cache sync; on x86-64 __builtin___clear_cache is a harmless no-op. std::memcpy(dst, src, len); __builtin___clear_cache(static_cast(dst), static_cast(dst) + len); #endif diff --git a/src/platform/esp32/moonlive_emit.cpp b/src/platform/esp32/moonlive_emit.cpp index d81515e..5a7c505 100644 --- a/src/platform/esp32/moonlive_emit.cpp +++ b/src/platform/esp32/moonlive_emit.cpp @@ -2,23 +2,33 @@ #include -// MoonLive ESP32 (Xtensa LX6/LX7) backend (§3.2) — the load-bearing hardware codegen. -// Emits the Stage-1a fixed-colour fill as Xtensa machine code; the engine copies it into -// IRAM (platform::writeExec) and calls it through FillFn. This is the path the bench run +// MoonLive ESP32 backend (§3.2) — the load-bearing hardware codegen. Emits the fill routines +// as native machine code for whichever ESP32 ISA this TU is compiled for: Xtensa on the +// LX6/LX7 chips (classic/S3), RISC-V on the P4. The engine copies the bytes into IRAM +// (platform::writeExec) and calls them through FillFn/AnimFn. This is the path the bench run // validates: native code we generated, executing in the render tick, writing the buffer. // -// void fill(uint8_t* buf, uint32_t nLights, uint8_t cpl) // windowed ABI: a2,a3,a4 +// void fill(uint8_t* buf, uint32_t nLights, uint8_t cpl[, uint32_t t]) // for (i=0; i return @@ -89,4 +99,62 @@ size_t emitAnimatedFill(uint8_t* out, size_t cap) { return sizeof(kXtensaAnim); } +#elif defined(__riscv) + +// --- RISC-V (RV32IMC: ESP32-P4) ------------------------------------------------------ +// Little-endian, fixed 4-byte instructions (assembled with .option norvc so there are no +// 2-byte compressed forms — uniform words, simple patching). Standard RV calling convention: +// a0=buf, a1=nLights, a2=cpl, a3=t; `ret` (jalr x0, ra, 0) returns. The colour `li`s sit at +// fixed WORD indices; a li's 12-bit immediate is bits [31:20], so patch is base | (imm<<20) +// with a zero-immediate base. Verbatim from riscv32-esp-elf-as (objcopy of .text). + +// Disassembly (word index : instruction): +// 0: beqz a1,.done 1: li t0,0(off) 2: li t1,0(i) 3: li t2,R 4: li t3,G 5: li t4,B +// 6: add t5,a0,t0 7: sb t2,0(t5) 8: sb t3,1(t5) 9: sb t4,2(t5) +// 10: addi t1,t1,1 11: add t0,t0,a2 12: bne t1,a1,.loop 13: ret +static const uint8_t kRiscvFill[] = { + 0x63, 0x8a, 0x05, 0x02, 0x93, 0x02, 0x00, 0x00, 0x13, 0x03, 0x00, 0x00, + 0x93, 0x03, 0x10, 0x01, 0x13, 0x0e, 0x20, 0x02, 0x93, 0x0e, 0x30, 0x03, + 0x33, 0x0f, 0x55, 0x00, 0x23, 0x00, 0x7f, 0x00, 0xa3, 0x00, 0xcf, 0x01, + 0x23, 0x01, 0xdf, 0x01, 0x13, 0x03, 0x13, 0x00, 0xb3, 0x82, 0xc2, 0x00, + 0xe3, 0x14, 0xb3, 0xfe, 0x67, 0x80, 0x00, 0x00, +}; +// li t2/t3/t4, 0 (zero-immediate bases) at word indices 3/4/5; patch | (colour<<20). +static constexpr uint32_t kRvLiBaseR = 0x00000393u; // li t2,0 +static constexpr uint32_t kRvLiBaseG = 0x00000e13u; // li t3,0 +static constexpr uint32_t kRvLiBaseB = 0x00000e93u; // li t4,0 + +static void putWord(uint8_t* p, uint32_t w) { // little-endian store + p[0] = uint8_t(w); p[1] = uint8_t(w >> 8); p[2] = uint8_t(w >> 16); p[3] = uint8_t(w >> 24); +} + +size_t emitFill(uint8_t* out, size_t cap, uint8_t r, uint8_t g, uint8_t b) { + if (cap < sizeof(kRiscvFill)) return 0; + std::memcpy(out, kRiscvFill, sizeof(kRiscvFill)); + putWord(out + 12, kRvLiBaseR | (uint32_t(r) << 20)); // word 3 + putWord(out + 16, kRvLiBaseG | (uint32_t(g) << 20)); // word 4 + putWord(out + 20, kRvLiBaseB | (uint32_t(b) << 20)); // word 5 + return sizeof(kRiscvFill); +} + +// Animated: red=(t>>3)&0xFF computed at runtime (srli + zext.b on a3), green=0, blue=64. +// Nothing to patch. Verbatim from riscv32-esp-elf-as. +static const uint8_t kRiscvAnim[] = { + 0x63, 0x8c, 0x05, 0x02, 0x93, 0xd3, 0x36, 0x00, 0x93, 0xf3, 0xf3, 0x0f, + 0x13, 0x0e, 0x00, 0x00, 0x93, 0x0e, 0x00, 0x04, 0x93, 0x02, 0x00, 0x00, + 0x13, 0x03, 0x00, 0x00, 0x33, 0x0f, 0x55, 0x00, 0x23, 0x00, 0x7f, 0x00, + 0xa3, 0x00, 0xcf, 0x01, 0x23, 0x01, 0xdf, 0x01, 0x13, 0x03, 0x13, 0x00, + 0xb3, 0x82, 0xc2, 0x00, 0xe3, 0x14, 0xb3, 0xfe, 0x67, 0x80, 0x00, 0x00, +}; + +size_t emitAnimatedFill(uint8_t* out, size_t cap) { + if (cap < sizeof(kRiscvAnim)) return 0; + std::memcpy(out, kRiscvAnim, sizeof(kRiscvAnim)); + return sizeof(kRiscvAnim); +} + +#else +#error "MoonLive ESP32 backend: unsupported ISA (expected Xtensa or RISC-V)" +#endif + } // namespace mm::moonlive diff --git a/src/platform/esp32/platform_esp32.cpp b/src/platform/esp32/platform_esp32.cpp index 9c8fe9b..d3d897f 100644 --- a/src/platform/esp32/platform_esp32.cpp +++ b/src/platform/esp32/platform_esp32.cpp @@ -107,14 +107,17 @@ void free(void* ptr) { heap_caps_free(ptr); } -// Executable memory for MoonLive's emitted Xtensa code. MALLOC_CAP_EXEC forces an -// allocation from IRAM (instruction-bus-fetchable). nullptr when IRAM is exhausted — -// the caller degrades (the scripted module reports "no memory", runs dark), never -// crashes. IRAM competes with WiFi/driver IRAM, so a failure here is expected on a -// busy device and must be handled, not asserted. +// Executable memory for MoonLive's emitted code (Xtensa or RISC-V). MALLOC_CAP_EXEC forces +// an allocation from IRAM (instruction-bus-fetchable). nullptr when IRAM is exhausted — the +// caller degrades (the scripted module reports "no memory", runs dark), never crashes. IRAM +// competes with WiFi/driver IRAM, so a failure here is expected on a busy device and must be +// handled, not asserted. The request is rounded up to a 4-byte word: writeExec's final +// partial-word store and the esp_cache_msync length both round up to a word, so the block +// must hold that whole word even when the caller's len isn't a multiple of 4. void* allocExec(size_t bytes) { if (bytes == 0) return nullptr; - return heap_caps_malloc(bytes, MALLOC_CAP_EXEC | MALLOC_CAP_32BIT); + size_t padded = (bytes + 3) & ~size_t(3); + return heap_caps_malloc(padded, MALLOC_CAP_EXEC | MALLOC_CAP_32BIT); } void freeExec(void* ptr, size_t /*bytes*/) { @@ -144,10 +147,20 @@ void writeExec(void* dst, const void* src, size_t len) { } d[words] = w; } - // Sync the instruction cache so the core fetches the freshly-written code, not a - // stale line. INST type + INVALIDATE; UNALIGNED because the code block isn't - // cache-line sized. S3 has the I-cache that needs this; harmless if it doesn't. - esp_cache_msync(dst, (len + 3) & ~size_t(3), + // Make the freshly-written code visible to instruction fetch. The bytes went in via + // DATA-bus stores, so on a cache-backed exec region (the P4) they may still sit in the + // data cache — two steps are needed, in order: + // 1. write the data cache back to RAM (TYPE_DATA, C2M) so RAM holds the code, and + // 2. invalidate the instruction cache for the range so the core refetches it. + // A single TYPE_INST msync only does step 2 — on the P4 that refetches STALE RAM (the + // bytes never left the data cache) and the core decodes garbage → illegal instruction. + // On the S3, MALLOC_CAP_EXEC is directly-executable SRAM so this is belt-and-suspenders, + // but it is correct on both. UNALIGNED because the code block isn't cache-line sized. + const size_t paddedLen = (len + 3) & ~size_t(3); + esp_cache_msync(dst, paddedLen, + ESP_CACHE_MSYNC_FLAG_TYPE_DATA | ESP_CACHE_MSYNC_FLAG_DIR_C2M | + ESP_CACHE_MSYNC_FLAG_UNALIGNED); + esp_cache_msync(dst, paddedLen, ESP_CACHE_MSYNC_FLAG_TYPE_INST | ESP_CACHE_MSYNC_FLAG_INVALIDATE | ESP_CACHE_MSYNC_FLAG_UNALIGNED); } diff --git a/test/js/config-ops.test.mjs b/test/js/config-ops.test.mjs index 0cbac55..fcc982c 100644 --- a/test/js/config-ops.test.mjs +++ b/test/js/config-ops.test.mjs @@ -52,8 +52,9 @@ test("all clearChildren ops come before all add/set ops", () => { for (const entry of catalog) { const ops = planConfigOps(entry); if (!ops.some(o => o.op === "clearChildren")) continue; - const lastClear = lastIndex(ops, o => o.op === "clearChildren"); const firstNonClear = firstIndex(ops, o => o.op !== "clearChildren"); + if (firstNonClear === -1) continue; // clears-only plan — nothing for them to precede + const lastClear = lastIndex(ops, o => o.op === "clearChildren"); assert.ok(lastClear < firstNonClear, `entry "${entry.name}": a clearChildren runs after an add/set — clearing must be a ` + `pre-pass, or it would wipe a module the entry just added.`); diff --git a/test/scenarios/light/scenario_MoonLiveEffect_livescript.json b/test/scenarios/light/scenario_MoonLiveEffect_livescript.json new file mode 100644 index 0000000..05d7496 --- /dev/null +++ b/test/scenarios/light/scenario_MoonLiveEffect_livescript.json @@ -0,0 +1,588 @@ +{ + "name": "scenario_MoonLiveEffect_livescript", + "module": "MoonLiveEffect", + "mode": "mutate", + "also": [ + "Layouts", + "GridLayout", + "Layers", + "Layer", + "Drivers", + "NetworkSendDriver" + ], + "description": "Exercise a scripted MoonLiveEffect as a wired MoonModule end-to-end — the integration layer the unit tests can't reach. The effect compiles its `source` text to native code on-device and renders it into the Layer buffer each tick. Prepares its own canvas: Layout(Grid 16x16) + Layer + MoonLiveEffect, measures the default compile, then edits `source` live (a new fill colour recompiles and keeps rendering), pushes a BROKEN script (the prior code keeps running, the parse error surfaces in status, no crash), recovers with a valid script, and finally removes + re-adds the effect (add/remove robustness in any order). A crash in the JIT/emit path, a failed recompile that wedges the tick, or a buffer overrun on an odd grid all show up as a failed measure. The compiler + emit golden bytes are pinned by unit_moonlive_compiler / unit_moonlive_fill; this is the live wired-module gate.", + "fixture": [ + { + "name": "fix-layouts", + "op": "add_module", + "id": "Layouts", + "type": "Layouts" + }, + { + "name": "fix-grid", + "op": "add_module", + "id": "Grid", + "type": "GridLayout", + "parent_id": "Layouts", + "props": { + "width": 16, + "height": 16 + } + }, + { + "name": "fix-layers", + "op": "add_module", + "id": "Layers", + "type": "Layers", + "props": { + "layouts": "Layouts" + } + }, + { + "name": "fix-layer", + "op": "add_module", + "id": "Layer", + "type": "Layer", + "parent_id": "Layers", + "props": { + "channelsPerLight": 3 + } + }, + { + "name": "fix-drivers", + "op": "add_module", + "id": "Drivers", + "type": "Drivers", + "props": { + "layers": "Layers" + } + }, + { + "name": "fix-artnet", + "op": "add_module", + "id": "ArtNet", + "type": "NetworkSendDriver", + "parent_id": "Drivers" + } + ], + "steps": [ + { + "name": "add-moonlive", + "description": "Add a MoonLiveEffect to the Layer. Its default source `fill(0, 0, 255);` compiles on-device to native code; measure that the wired effect renders.", + "op": "add_module", + "id": "ML", + "type": "MoonLiveEffect", + "parent_id": "Layer", + "measure": true, + "observed": { + "pc-macos": { + "tick_us": [ + 0, + 393 + ], + "free_heap": [ + 0, + 0 + ], + "max_alloc_block": [ + 0, + 0 + ], + "at": [ + "2026-06-26", + "2026-06-27" + ] + }, + "esp32s3-n16r8": { + "tick_us": [ + 4013, + 4013 + ], + "free_heap": [ + 8541659, + 8541659 + ], + "max_alloc_block": [ + 106496, + 106496 + ], + "at": [ + "2026-06-27", + "2026-06-27" + ] + }, + "esp32p4-eth": { + "tick_us": [ + 11291, + 11291 + ], + "free_heap": [ + 34007695, + 34007695 + ], + "max_alloc_block": [ + 385024, + 385024 + ], + "at": [ + "2026-06-27", + "2026-06-27" + ] + } + } + }, + { + "name": "edit-source-red", + "description": "Live-edit the script source to a new colour. A source edit triggers a recompile (controlChangeTriggersBuildState gates on `source`); the new native code swaps in and keeps rendering.", + "op": "set_control", + "id": "ML", + "key": "source", + "value": "fill(255, 0, 0);", + "measure": true, + "observed": { + "pc-macos": { + "tick_us": [ + 0, + 398 + ], + "free_heap": [ + 0, + 0 + ], + "max_alloc_block": [ + 0, + 0 + ], + "at": [ + "2026-06-26", + "2026-06-27" + ] + }, + "esp32s3-n16r8": { + "tick_us": [ + 4450, + 4450 + ], + "free_heap": [ + 8541007, + 8541007 + ], + "max_alloc_block": [ + 106496, + 106496 + ], + "at": [ + "2026-06-27", + "2026-06-27" + ] + }, + "esp32p4-eth": { + "tick_us": [ + 10167, + 10167 + ], + "free_heap": [ + 34010179, + 34010179 + ], + "max_alloc_block": [ + 385024, + 385024 + ], + "at": [ + "2026-06-27", + "2026-06-27" + ] + } + } + }, + { + "name": "edit-source-broken", + "description": "Push a script that fails to parse. The compile fails, the engine reports the diagnostic in the module status and renders dark, but the device keeps running (robust, no reboot) — the script-editor failure path. The measure passes because the pipeline still ticks.", + "op": "set_control", + "id": "ML", + "key": "source", + "value": "fill(nope);", + "measure": true, + "observed": { + "pc-macos": { + "tick_us": [ + 0, + 267 + ], + "free_heap": [ + 0, + 0 + ], + "max_alloc_block": [ + 0, + 0 + ], + "at": [ + "2026-06-26", + "2026-06-27" + ] + }, + "esp32s3-n16r8": { + "tick_us": [ + 4366, + 4366 + ], + "free_heap": [ + 8540491, + 8540491 + ], + "max_alloc_block": [ + 106496, + 106496 + ], + "at": [ + "2026-06-27", + "2026-06-27" + ] + }, + "esp32p4-eth": { + "tick_us": [ + 10572, + 10572 + ], + "free_heap": [ + 34006131, + 34006131 + ], + "max_alloc_block": [ + 385024, + 385024 + ], + "at": [ + "2026-06-27", + "2026-06-27" + ] + } + } + }, + { + "name": "edit-source-recover", + "description": "Push a valid script again. The engine recompiles cleanly and rendering resumes — a broken edit is fully recoverable.", + "op": "set_control", + "id": "ML", + "key": "source", + "value": "fill(0, 255, 0);", + "measure": true, + "observed": { + "pc-macos": { + "tick_us": [ + 0, + 414 + ], + "free_heap": [ + 0, + 0 + ], + "max_alloc_block": [ + 0, + 0 + ], + "at": [ + "2026-06-26", + "2026-06-27" + ] + }, + "esp32s3-n16r8": { + "tick_us": [ + 4040, + 4040 + ], + "free_heap": [ + 8540211, + 8540211 + ], + "max_alloc_block": [ + 102400, + 102400 + ], + "at": [ + "2026-06-27", + "2026-06-27" + ] + }, + "esp32p4-eth": { + "tick_us": [ + 10708, + 10708 + ], + "free_heap": [ + 34009331, + 34009331 + ], + "max_alloc_block": [ + 385024, + 385024 + ], + "at": [ + "2026-06-27", + "2026-06-27" + ] + } + } + }, + { + "name": "shrink-grid-1x1", + "description": "Resize the canvas to 1x1 while the scripted effect renders — the smallest non-empty grid. The native fill loops over a single light; the run guards (non-null buffer, cpl>=3) keep it in-bounds. Pins the 'runs at every grid size' hard rule for the JIT'd routine.", + "op": "set_control", + "id": "Grid", + "key": "width", + "value": 1, + "measure": true, + "observed": { + "pc-macos": { + "tick_us": [ + 0, + 1 + ], + "free_heap": [ + 0, + 0 + ], + "max_alloc_block": [ + 0, + 0 + ], + "at": [ + "2026-06-26", + "2026-06-27" + ] + }, + "esp32s3-n16r8": { + "tick_us": [ + 1152, + 1152 + ], + "free_heap": [ + 8541371, + 8541371 + ], + "max_alloc_block": [ + 102400, + 102400 + ], + "at": [ + "2026-06-27", + "2026-06-27" + ] + }, + "esp32p4-eth": { + "tick_us": [ + 1160, + 1160 + ], + "free_heap": [ + 34012203, + 34012203 + ], + "max_alloc_block": [ + 385024, + 385024 + ], + "at": [ + "2026-06-27", + "2026-06-27" + ] + } + } + }, + { + "name": "grow-grid-back", + "description": "Resize back to a wider grid; the effect keeps rendering across the live dimension change (the no-reboot reconfiguration contract applied to scripted code).", + "op": "set_control", + "id": "Grid", + "key": "width", + "value": 16, + "measure": true, + "observed": { + "pc-macos": { + "tick_us": [ + 0, + 14 + ], + "free_heap": [ + 0, + 0 + ], + "max_alloc_block": [ + 0, + 0 + ], + "at": [ + "2026-06-26", + "2026-06-27" + ] + }, + "esp32s3-n16r8": { + "tick_us": [ + 7329, + 7329 + ], + "free_heap": [ + 8532499, + 8532499 + ], + "max_alloc_block": [ + 102400, + 102400 + ], + "at": [ + "2026-06-27", + "2026-06-27" + ] + }, + "esp32p4-eth": { + "tick_us": [ + 10310, + 10310 + ], + "free_heap": [ + 34005655, + 34005655 + ], + "max_alloc_block": [ + 385024, + 385024 + ], + "at": [ + "2026-06-27", + "2026-06-27" + ] + } + } + }, + { + "name": "remove-moonlive", + "description": "Remove the scripted effect. teardown frees the exec block; the Layer keeps rendering (now empty). Measures add/remove robustness.", + "op": "remove_module", + "id": "ML", + "measure": true, + "observed": { + "pc-macos": { + "tick_us": [ + 0, + 10 + ], + "free_heap": [ + 0, + 0 + ], + "max_alloc_block": [ + 0, + 0 + ], + "at": [ + "2026-06-26", + "2026-06-27" + ] + }, + "esp32s3-n16r8": { + "tick_us": [ + 7387, + 7387 + ], + "free_heap": [ + 8532839, + 8532839 + ], + "max_alloc_block": [ + 102400, + 102400 + ], + "at": [ + "2026-06-27", + "2026-06-27" + ] + }, + "esp32p4-eth": { + "tick_us": [ + 11336, + 11336 + ], + "free_heap": [ + 34006231, + 34006231 + ], + "max_alloc_block": [ + 385024, + 385024 + ], + "at": [ + "2026-06-27", + "2026-06-27" + ] + } + } + }, + { + "name": "re-add-moonlive", + "description": "Re-add a MoonLiveEffect after removal — the exec memory is re-acquired fresh, no leak, no stale pointer. The scripted effect renders again.", + "op": "add_module", + "id": "ML2", + "type": "MoonLiveEffect", + "parent_id": "Layer", + "measure": true, + "observed": { + "pc-macos": { + "tick_us": [ + 0, + 16 + ], + "free_heap": [ + 0, + 0 + ], + "max_alloc_block": [ + 0, + 0 + ], + "at": [ + "2026-06-26", + "2026-06-27" + ] + }, + "esp32s3-n16r8": { + "tick_us": [ + 8255, + 8255 + ], + "free_heap": [ + 8532255, + 8532255 + ], + "max_alloc_block": [ + 102400, + 102400 + ], + "at": [ + "2026-06-27", + "2026-06-27" + ] + }, + "esp32p4-eth": { + "tick_us": [ + 10997, + 10997 + ], + "free_heap": [ + 34005655, + 34005655 + ], + "max_alloc_block": [ + 385024, + 385024 + ], + "at": [ + "2026-06-27", + "2026-06-27" + ] + } + } + } + ] +} diff --git a/test/scenarios/light/scenario_modifier_chain.json b/test/scenarios/light/scenario_modifier_chain.json index fbce1a2..01d6a11 100644 --- a/test/scenarios/light/scenario_modifier_chain.json +++ b/test/scenarios/light/scenario_modifier_chain.json @@ -130,7 +130,7 @@ "observed": { "pc-macos": { "tick_us": [ - 6, + 5, 7 ], "free_heap": [ @@ -158,7 +158,7 @@ "pc-macos": { "tick_us": [ 18, - 20 + 22 ], "free_heap": [ 0, @@ -182,7 +182,27 @@ "id": "ROT", "type": "RotateModifier", "parent_id": "Layer", - "measure": true + "measure": true, + "observed": { + "pc-macos": { + "tick_us": [ + 35, + 39 + ], + "free_heap": [ + 0, + 0 + ], + "max_alloc_block": [ + 0, + 0 + ], + "at": [ + "2026-06-26", + "2026-06-26" + ] + } + } } ] } diff --git a/test/scenarios/light/scenario_perf_light.json b/test/scenarios/light/scenario_perf_light.json index ada97dd..243adf1 100644 --- a/test/scenarios/light/scenario_perf_light.json +++ b/test/scenarios/light/scenario_perf_light.json @@ -543,7 +543,7 @@ "pc-macos": { "tick_us": [ 14, - 44 + 50 ], "free_heap": [ 0, @@ -555,7 +555,7 @@ ], "at": [ "2026-06-17", - "2026-06-25" + "2026-06-26" ] }, "esp32s3-n16r8": { diff --git a/test/unit/core/unit_moonlive_fill.cpp b/test/unit/core/unit_moonlive_fill.cpp index 3f55ca7..f90add5 100644 --- a/test/unit/core/unit_moonlive_fill.cpp +++ b/test/unit/core/unit_moonlive_fill.cpp @@ -52,6 +52,20 @@ TEST_CASE("MoonLive run on zero lights writes nothing (robust to empty)") { CHECK(buf[0] == 0xAB); // untouched } +// The native routines write channels +0/+1/+2 per light, so a layer with fewer than 3 +// channels per light can't hold RGB — run() must leave it untouched, not overrun it. +TEST_CASE("MoonLive run is a no-op on sub-RGB buffers (cpl 1 and 2)") { + moonlive::MoonLive engine; + REQUIRE(engine.compile(255, 255, 255)); + for (uint8_t cpl : {uint8_t(1), uint8_t(2)}) { + std::vector buf(8 * cpl, 0xAB); // exact size — an RGB write WOULD overrun + engine.run(buf.data(), 8, cpl, 0); + for (auto v : buf) CHECK(v == 0xAB); // every byte untouched, no out-of-bounds + } + // null buffer is also a safe no-op. + engine.run(nullptr, 8, 3, 0); +} + TEST_CASE("MoonLive recompile swaps the colour; free returns to !ok") { moonlive::MoonLive engine; REQUIRE(engine.compile(1, 1, 1)); From 0b18dc48fcc5c105dac94808e6a9276f44b731a0 Mon Sep 17 00:00:00 2001 From: ewowi Date: Sat, 27 Jun 2026 09:35:46 +0200 Subject: [PATCH 5/8] MoonLive: expression grammar, host-bound builtins, RISC-V backend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MoonLive scripts now take expression arguments — any argument can be a nested call like random16(256), so setRGB(random16(256), random16(256), random16(256), random16(256)) works. The functions (setRGB, fill, random16) are no longer baked into the compiler; the light domain registers them in a host builtin table, keeping the engine core LED-free. Adds a third device backend (RISC-V / ESP32-P4) and a register-reuse pass so multi-call statements fit the small device register file. Verified live on PC, S3 (Xtensa), and P4 (RISC-V). KPI: 16384lights | tick:143/99/132/13/2/364/40/17/25/67/133/12/38us(FPS:6993/10101/7575/76923/500000/2747/25000/58823/40000/14925/7518/83333/26315) | tick:6957us(FPS:143) | heap:8334KB | src:118(22357) | test:76(11833) | lizard:82w Core: - MoonLive: compile(source, table) resolves call names against an injected BuiltinTable; the core owns the grammar plus a generic Call/Inline mechanism and no LED vocabulary. Code arena sized once for the heaviest realistic single statement (four-arg all-calls on the bulkiest ISA) rather than grown per script. - MoonLiveCompiler: recursive-descent expression parser (expr := number | call), every argument lowered to a vreg; a free-stack vreg allocator recycles each dead argument temp the moment its call consumes it, so a chain of calls peaks at a handful of registers instead of growing without bound — what makes the multi-call statement fit the 12-register device file. - MoonLiveIr: a neutral typed IR (Const/Add/AddImm/Mul/Loop/Bounds/Call/Inline) replacing the RGB-specific Store op; Call carries a host fn pointer, Inline a neutral opcode tag (WriteRGB/FillRGB) the backend stores inline (no per-pixel call on the hot path). - MoonLiveBuiltins: the neutral host-binding seam (BuiltinTable of name -> Call | Inline descriptor). Light domain: - MoonLiveBuiltins_light: the only home for the LED vocabulary — registers setRGB/fill (Inline) and random16 (Call); a different host writes its own table and the core is unchanged. - MoonLiveEffect: passes the light builtin table to compile(); source text buffer raised to 128 chars so a four-call statement is not truncated. Core / platform: - RISC-V assembler + lowering (ESP32-P4): replaces the previous stub with a real RV32 backend (named instructions, label back-patching, standard call ABI with caller-saved register preservation); every encoding verified against riscv32-esp-elf disassembly before flashing. - Xtensa and host (desktop) assemblers + lowerings carried onto the neutral IR; the stub lowerToBytes removed. Tests: - unit_moonlive_compiler: expression grammar (random16 in any/every slot, uint16 range), the multi-call vreg-reuse budget case, and the domain-neutral property (empty table -> core knows no functions; a host can register an arbitrary name). - unit_moonlive_ir: behavioral golden — compiled fill vs the hand-encoded reference render an identical buffer — plus setRGB single-pixel write and the runtime bounds guard. Docs: - MoonLiveEffect.md: rewritten to the builtin-table model (expression grammar, Call/Inline descriptors, prior art ESPLiveScript/ARTI). - decisions.md: the build-around-expressions-not-statement-shapes lesson, and the RISC-V bring-up + vreg-reuse lesson (a third ISA proves the IR seam is real; the smallest register file is where host-vs-device diverges). - two approved plans saved to docs/history/plans/ (the host-bound-functions plan marked shipped). Co-Authored-By: Claude Opus 4.8 (1M context) --- CMakeLists.txt | 2 + docs/history/decisions.md | 20 + ...(IR seam + assembler, second statement).md | 95 ++++ ...nctions (domain-neutral core) (shipped).md | 98 ++++ .../light/moonlive/MoonLiveEffect.md | 30 +- docs/tests/scenario-tests.md | 535 ++++++++++++------ esp32/main/CMakeLists.txt | 4 + src/core/moonlive/MoonLive.cpp | 15 +- src/core/moonlive/MoonLive.h | 8 +- src/core/moonlive/MoonLiveBuiltins.h | 68 +++ src/core/moonlive/MoonLiveCompiler.cpp | 197 ++++--- src/core/moonlive/MoonLiveCompiler.h | 34 +- src/core/moonlive/MoonLiveIr.h | 69 +++ src/core/moonlive/moonlive_emit.h | 8 + src/light/moonlive/MoonLiveBuiltins_light.h | 39 ++ src/light/moonlive/MoonLiveEffect.h | 5 +- src/platform/desktop/moonlive_asm_host.cpp | 131 +++++ src/platform/desktop/moonlive_asm_host.h | 86 +++ src/platform/desktop/moonlive_emit.cpp | 8 +- src/platform/desktop/moonlive_lower_host.cpp | 88 +++ src/platform/esp32/moonlive_asm_riscv.cpp | 133 +++++ src/platform/esp32/moonlive_asm_riscv.h | 64 +++ src/platform/esp32/moonlive_asm_xtensa.cpp | 145 +++++ src/platform/esp32/moonlive_asm_xtensa.h | 66 +++ src/platform/esp32/moonlive_emit.cpp | 18 +- src/platform/esp32/moonlive_lower_riscv.cpp | 92 +++ src/platform/esp32/moonlive_lower_xtensa.cpp | 86 +++ test/CMakeLists.txt | 1 + .../light/scenario_Audio_mutation.json | 24 +- .../light/scenario_Driver_mutation.json | 20 +- .../light/scenario_Layers_composition.json | 4 +- .../light/scenario_Layouts_mutation.json | 12 +- .../scenario_MoonLiveEffect_livescript.json | 2 +- .../light/scenario_modifier_chain.json | 16 +- .../light/scenario_modifier_swap.json | 12 +- test/scenarios/light/scenario_perf_full.json | 52 +- test/unit/core/unit_moonlive_compiler.cpp | 189 ++++--- test/unit/core/unit_moonlive_fill.cpp | 5 + test/unit/core/unit_moonlive_ir.cpp | 107 ++++ 39 files changed, 2153 insertions(+), 435 deletions(-) create mode 100644 docs/history/plans/Plan-20260627 - MoonLive Stage 3 (IR seam + assembler, second statement).md create mode 100644 docs/history/plans/Plan-20260627 - MoonLive expressions + host-bound functions (domain-neutral core) (shipped).md create mode 100644 src/core/moonlive/MoonLiveBuiltins.h create mode 100644 src/core/moonlive/MoonLiveIr.h create mode 100644 src/light/moonlive/MoonLiveBuiltins_light.h create mode 100644 src/platform/desktop/moonlive_asm_host.cpp create mode 100644 src/platform/desktop/moonlive_asm_host.h create mode 100644 src/platform/desktop/moonlive_lower_host.cpp create mode 100644 src/platform/esp32/moonlive_asm_riscv.cpp create mode 100644 src/platform/esp32/moonlive_asm_riscv.h create mode 100644 src/platform/esp32/moonlive_asm_xtensa.cpp create mode 100644 src/platform/esp32/moonlive_asm_xtensa.h create mode 100644 src/platform/esp32/moonlive_lower_riscv.cpp create mode 100644 src/platform/esp32/moonlive_lower_xtensa.cpp create mode 100644 test/unit/core/unit_moonlive_ir.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c7e158..4c59834 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,6 +67,8 @@ target_link_libraries(mm_core PUBLIC mm_platform) add_library(mm_platform src/platform/desktop/platform_desktop.cpp src/platform/desktop/moonlive_emit.cpp + src/platform/desktop/moonlive_asm_host.cpp + src/platform/desktop/moonlive_lower_host.cpp ) target_include_directories(mm_platform PUBLIC src/ src/platform/desktop/) # Winsock for the desktop socket surface on Windows. diff --git a/docs/history/decisions.md b/docs/history/decisions.md index 953f5b8..28fdb25 100644 --- a/docs/history/decisions.md +++ b/docs/history/decisions.md @@ -720,3 +720,23 @@ Why the inversion was the right call, not just the chaining bolt-on: - **Static vs dynamic split correctly.** Mask/tile/crop fold forward at build time (`modifyLogical`); rotation gathers backward per frame (`modifyLive`) — each in its natural direction, so rotation keeps its clean inverse-sample (no gaps), and a static-only chain pays *nothing* per frame (the live pass is gated on `hasModifyLive()`). Lessons: (1) when a feature "doesn't compose," check whether the *interface direction* is wrong before adding machinery to force it — inverting the build deleted more code than it added. (2) A proven external model (MoonLight) is worth adopting wholesale when it's the textbook approach (backward mapping + LUT bake), but write it fresh against your own structures (our CSR, our names) rather than porting. (3) Matrices compose the *affine* subset cleanly (Rotate is written as an explicit 2×2 matrix, the codebase's matrix reference) but can't express masks/tiles — so the coordinate fold is the general composition model, with a matrix-backed modifier as a special case the same interface hosts. + +## MoonLive: build around expressions + host-bound functions, not statement shapes + +The MoonLive live-script compiler (IR rung) was first built around the *statement shape* `setRGB(idx, r, g, b)` — the parser had per-slot rules (index could be `random16`, colours were literal-only) and the IR had an RGB-specific `Store` op baked into the **core**. Three product-owner remarks exposed the same root flaw: (1) `random16` only worked in the index slot, not any argument; (2) `random16(255)` capped at a byte because the index/colour validators conflated ranges; (3) the core compiler was light-domain-specific (`setRGB`/`fill`/`Store` hardcoded), violating *Domain-neutral core*. + +The fix was the ESPLiveScript / ARTI / doc-§3.4 model: **the core knows only expressions + a generic call mechanism; the host registers its functions in a builtin table.** Every argument parses as an expression (a literal or a nested call), so `setRGB(random16(256), random16(256), 30, 0)` works and a number is a uint16. `setRGB`/`fill`/`random16` — the LED *names* and the RGB meaning — live only in the light-domain registration (`MoonLiveBuiltins_light.h`); the core sees a neutral `BuiltinTable` of `{name → Call(fn ptr) | Inline(opcode tag)}`. A buffer writer is `Kind::Inline` (lowers to stores — the hot-path fast path, no per-pixel call); a pure helper is `Kind::Call`. A mechanised test pins the neutrality: with an empty table the core knows *no* functions, and a host can register an arbitrary name (`paint`) against the same machinery. + +Two codegen lessons surfaced fixing it: (1) **the live-vreg-across-call contract must hold for ANY expression, not a hand-ordered one** — once arguments can be calls, a value computed before one call can be live across a *second* call; the assembler's `call()` must save/restore the whole caller-saved register set (host: a full stp/ldp frame; Xtensa: s32i/l32i of the rotate-out registers a8/a9/a11, with the result stashed in a non-saved reg across the restore). (2) **register budget is real on the MCU** — fold the address into a dead vreg (WriteRGB writes into the index register after `index *= cpl`) rather than reserving fresh scratch, so a multi-call statement fits the small windowed register file. + +Lessons: (1) when a language "can't express X in slot Y," the fix is almost always *real expressions*, not a per-slot special case — build the general grammar once. (2) Domain-neutrality is testable: assert the core, given an empty host table, knows nothing — if it compiles a domain function, the domain leaked in. (3) The bound-function table is the same seam for *speed* and *neutrality*: the descriptor carries how it lowers (inline store vs call), so the core stays LED-free while the hot path stays inline. + +### MoonLive RISC-V backend + vreg reuse (the third ISA, and what it exposed) + +Bringing up the ESP32-P4 (RISC-V) backend — a third per-ISA assembler + lowering behind the same neutral IR — was mechanical *and* revealing. Mechanical: RV32 is uniform 4-byte instructions and a standard (non-windowed) call ABI, so the assembler is simpler than Xtensa's; every encoding was verified by disassembling the assembler's own output with `riscv32-esp-elf-as`/`objdump` before flashing (the same discipline that caught every Xtensa encoding bug). Revealing: the P4 was the first target where multi-call statements *failed to compile*, exposing two limits the host (14 regs) and Xtensa (12 regs, but the heaviest test was 2 calls) had masked. + +First, the **register file**. The front-end allocated a fresh virtual register per sub-expression and never reused it, so `setRGB(random16(..), random16(..), random16(..), 0)` needed more live vregs than the 12-register device pool — "codegen failed." The fix is the textbook tree-walk register stack: a free-list allocator where each argument temp is returned to the pool the moment its call consumes it, so a chain of N calls peaks at a handful of registers instead of growing 2N. `vregsUsed` (the lowering's reservation) is the high-water mark, which now *shrinks* because freed vregs are reused at low indices. This is the "concrete-first, the allocator arrives when a real script exhausts registers" point the design anticipated — and a 4-call statement is exactly that script. + +Second, the **code arena**. RISC-V's `call()` saves the full caller-saved set around each host call (~140 bytes), so four calls in one statement is ~600 bytes — past the original 256-byte staging cap. Rather than grow per-script (a moving target), the arena is sized once for the heaviest *realistic* single statement (four-arg-all-calls on the bulkiest ISA). Exec memory is cheap; a fixed worst-case cap is simpler and more predictable than dynamic growth. + +Lessons: (1) a third backend is the cheap insurance that the IR seam is real — if adding RISC-V had needed front-end changes, the seam was a lie; it needed none (only the new register/byte *limits* surfaced, which are target properties, not design leaks). (2) Register allocation is where "it works on my 14-register host" quietly diverges from "it works on the 12-register device" — bring up the smallest register file early. (3) Verify every emitted instruction against the real toolchain's disassembler before trusting it on hardware; it is faster than debugging a `StoreProhibited` on-device, every time. diff --git a/docs/history/plans/Plan-20260627 - MoonLive Stage 3 (IR seam + assembler, second statement).md b/docs/history/plans/Plan-20260627 - MoonLive Stage 3 (IR seam + assembler, second statement).md new file mode 100644 index 0000000..e5e65b3 --- /dev/null +++ b/docs/history/plans/Plan-20260627 - MoonLive Stage 3 (IR seam + assembler, second statement).md @@ -0,0 +1,95 @@ +# Plan — MoonLive Stage 3: the IR seam + a tiny assembler (second statement) + +> Approved plan record (CLAUDE.md *Plan before implementing*). The next rung of MoonLive after the shipped 1a→1b→2 + P4 spike: add the **second statement kind** to the language, which forces the **typed IR** and a **per-ISA assembler** to earn their place (the current AST→emitFill shortcut only works for one fixed routine). Builds on [livescripts-analysis-top-down.md](../../backlog/livescripts-analysis-top-down.md) §3.2 (IR seam), §4 (bounds-check at the IR), §3.4 (host built-ins). + +## Goal + +Compile `setRGB(i, r, g, b);` — a single-pixel write at a computed index — to native code, with a **bounds-check** so an out-of-range index can't overrun the buffer. This is the smallest statement that is *not a fill*: it has a computed store address (`i*cpl`), a guard, and no loop. Doing it honestly requires three things the spike deliberately deferred: + +1. A **typed IR** — AST lowered to a flat list of three-address ops over virtual registers — so codegen stops being "patch 3 bytes into a fixed template" and becomes "lower a sequence of ops." +2. A **tiny per-ISA assembler** — named-instruction emitters (`movi`, `strb`, `add`, `cmp`, `b.cond`, `ret`) with **label back-patching** — because a multi-op statement's bytes must be *composed* at compile time (branch offsets, register liveness across ops), which can't be done as hand-grouped byte fragments (that's the StoreProhibited crash class the spike hit). +3. The **bounds-check as an IR op** so every backend inherits it and it's switchable by deleting nodes (doc §4). + +The verbatim hand-encoded `fill` blobs from the spike do not die — they become the **golden regression fixture**, but as a **behavioral** anchor, not a byte-identical one: the assembler-built `fill` and the hand blob are both run over the same buffer and must produce **identical output**. (Byte-identical would force the clean assembler to mimic the hand blob's arbitrary register choices and instruction selection — coupling good code to an artifact. Real compilers assert behavioral equivalence + a size bound against a reference, not byte-equality; that's the *Common patterns first* choice here.) The assembler keeps its own clean register convention; the hand blobs stay as documented references the behavioral test pins against. + +## Decisions locked with the product owner + +- **Minimal IR + tiny assembler** (not a second hand-template, not a full SSA IR). ~7 ops, a fixed virtual-register file with last-use freeing — no SSA, no general register allocator (both deferred to *concrete-first* until a script exhausts registers). +- **Second statement = `setRGB(i, r, g, b)`**, in two half-steps: **3a** literal index (`setRGB(5, 0,0,255)`) — single write + index math + first BOUNDS, no host call; **3b** `setRGB(random16(N), …)` — adds the CALL op + the first host built-in, giving the tutorial hello-world. +- **Desktop (arm64/x86-64) first**, then Xtensa, then RISC-V — the stage-0.5 logic applied to codegen: prove the IR→assembler path on the in-process backend (instant feedback, golden-fill regression), then bring up the device ISAs against the proven IR. + +## The IR (minimal, ~7 ops, no SSA) + +A flat list of three-address ops over virtual registers `v0..vN` plus the named host args (`buf`, `nLights`, `cpl`, `t`). Defined once in the neutral core (`src/core/moonlive/`): + +| Op | Meaning | +|---|---| +| `Const vd, imm` | load an integer immediate | +| `Mul/Add/Shl vd, va, vb` | integer arithmetic (index scaling `i*cpl`, range reduction) | +| `Bounds vidx, vlimit` | the §4 guard — skip the store if `vidx >= vlimit` (its own op so bounds on/off = delete the node, and every backend inherits it) | +| `Store buf, vaddr, vr, vg, vb` | write RGB at a computed byte address | +| `Call vd, builtin, varg…` | call a host built-in (`random16`), result in `vd` — the single seam for all host functions | +| `Loop vcounter, vlimit { … }` | a counted loop body (what makes `fill` a loop over all lights) | + +- `fill(r,g,b)` = `Const×3` + `Loop{ Store }`. +- `setRGB(, r,g,b)` = `Const idx` + `Const×3` + `Bounds` + (`Mul idx,cpl`) + `Store`. +- `setRGB(random16(N), …)` = `Const N` + `Call random16` + `Bounds` + `Const×3` + `Store`. + +The set is **closed under the rest of the ladder**: 2D/3D addressing is more index arithmetic (already have Mul/Add); oscillators add float variants; Ripples adds `Call sqrt/sin`. Later rungs add op *variants*, never redesign the IR. + +**Deliberate scope cuts:** no SSA, no register allocator. A fixed vreg file (16 regs) with last-use freeing — a statement uses ≤ a handful. SSA + a real allocator are the "complexity in core" deferred until a script exhausts registers. + +## The assembler (per-ISA, named instructions, label back-patching) + +`src/platform//moonlive_asm_*.{h,cpp}` (behind the platform boundary — it emits ISA bytes). A small `Assembler` that appends instructions to a byte buffer and back-patches label offsets: + +- `mov(reg, imm)`, `store8(rbase, roff, rval)`, `add(rd, ra, rb)`, `mul/shl(rd, ra, imm)`, `cmp(ra, rb)`, `branchIf(cond, label)`, `label(l)`, `ret()` — the ~10–15 encodings the IR actually uses, hand-encoded **once per ISA** (vs. once per instruction × per statement template). Label back-patching kills the hand-computed-branch-offset crash class permanently. +- This is the textbook `MacroAssembler` shape (V8 `Assembler`, LLVM `MCInst`, asmjit) — passes *common patterns first*. +- The IR→bytes lowering (`lowerToBytes(const IrProgram&, Assembler&)`) lives per-backend; the IR itself is neutral core. + +**The vreg→machine-register + calling-convention contract** between IR and assembler is settled and pinned by a test BEFORE Xtensa: a `Call` surrounded by live vregs must preserve them (which machine regs are caller-saved across the host call, who saves them). Getting this wrong is the multi-round-trip rework *Refactor for simplicity* warns against, so it's nailed on the desktop backend first. + +## Files + +### Neutral core (`src/core/moonlive/`) +- **`MoonLiveIr.h`** (new) — the IR op structs + `IrProgram` (a flat `std::array`/fixed-capacity list of ops, no heap in the hot build path; sized like `kCodeCap`). Pure data, no ISA. +- **`MoonLiveCompiler.{h,cpp}`** — the parser grows a second production (`setRGB(...)`) and a `random16(...)` primary; codegen becomes **AST → IR** (`lower()`), replacing the direct `emitFill` call. `compileSource` now: parse → lower to IR → hand the IR to the per-ISA `lowerToBytes`. + +### Per-ISA assembler + lowering (`src/platform//`) +- **`moonlive_asm_host.{h,cpp}`** (desktop, arm64 + x86-64 by `#if`), **`moonlive_asm_xtensa.cpp`**, **`moonlive_asm_riscv.cpp`** — the named-instruction assembler + `lowerToBytes` per ISA. The existing `emitFill`/`emitAnimatedFill` stay (the golden fixtures) until the assembler reproduces them, then `emitFill` is re-expressed as `lower(fill-IR) → lowerToBytes` and the hand-blob becomes a test constant. + +### Engine + binding (unchanged seam) +- `MoonLive.{h,cpp}` — `compile(source)` already routes through `compileSource`; no change to the engine API. The binding (`MoonLiveEffect.h`) is untouched — the source control now accepts the richer grammar for free. + +### Tests +- **`unit_moonlive_ir.cpp`** (new) — AST→IR lowering: `setRGB(5,0,0,255)` produces the expected op list; the `Bounds` node is present before the `Store`; `random16` lowers to a `Call`. +- **`unit_moonlive_asm.cpp`** (new) — the assembler: each named instruction emits the right bytes (golden per ISA); label back-patching resolves a forward/backward branch; the **`Call`-preserves-live-vregs** contract test. +- **`unit_moonlive_compiler.cpp`** (extend) — `setRGB` golden bytes (assembler-built), an out-of-range index is bounds-rejected at runtime (the buffer's other pixels untouched), `random16` index lands in-range, every new parser diagnostic. +- **`unit_moonlive_fill.cpp`** (extend) — **golden regression**: the assembler-built `fill` == the original hand-encoded blob, byte-for-byte (per ISA), proving no codegen-quality regression. +- **`scenario_MoonLiveEffect_livescript.json`** (extend) — a `setRGB(...)` source step + a deliberately out-of-range `setRGB(99999, …)` step (renders safely, no overrun, device keeps ticking) on PC/S3/P4. + +## Steps (each independently green) + +1. **IR + desktop assembler, `fill` only.** Define the IR; build the host assembler; lower the `fill` IR to bytes; assert byte-identical to the hand blob (golden regression). No language change yet — pure infrastructure swap, proven by the golden test. *The big commit; everything else builds on a proven assembler.* +2. **`setRGB(, r,g,b)` — desktop.** Parser second production → IR (`Const`+`Bounds`+`Store`) → host bytes. Unit tests: golden bytes, runtime bounds-reject, the cpl-scaling. +3. **`random16(N)` — desktop.** Add the `Call` op + the `random16` built-in (host function) + its lowering; pin the live-vreg-across-Call contract. Now `setRGB(random16(N), blue)` compiles — the hello-world. +4. **Xtensa assembler.** Bring up `moonlive_asm_xtensa`: reproduce the golden `fill`, then `setRGB`/`random16`. Flash S3, run the scenario live. +5. **RISC-V assembler.** Same for the P4. Flash, run the scenario live. +6. **Docs + scenario.** Update `MoonLiveEffect.md` (the IR/assembler pieces, the grammar), extend the scenario, decisions.md (the IR-forces-assembler lesson). + +## Validation + +- `ctest` + `uv run scripts/scenario/run_scenario.py` green at each step; desktop-first so steps 1–3 are pure in-process. +- **Golden-bytes regression** (assembler `fill` == hand blob) is the anchor proving the assembler reproduces hand-quality code. +- Hardware: S3 (Xtensa) + P4 (RISC-V) run `setRGB`/`random16` live via the scenario, including the out-of-range-index safety step. +- Build zero-warnings; platform-boundary check (assembler bytes behind `src/platform/`); `check_specs` green. + +## Risks / watch-items + +- **The assembler is the real cost** — ~10–15 instruction encodings × 3 ISAs, hand-encoded once each, with label back-patching. Sized honestly: a few days per ISA, but each *instruction* is encoded once (not each instruction × statement), and back-patching removes the hand-offset crash class. Desktop-first keeps the feedback loop instant. +- **vreg/calling-convention contract** — the one thing that, gotten wrong, causes multi-round-trip rework. Pinned by a test before Xtensa. +- **No silent scope creep** — no SSA, no register allocator, no float ops, no loops-in-source, no 2D/3D this rung. Each is a later rung. If a step wants one, it's backlogged, not smuggled in. +- **Golden fixtures must not rot** — keep the hand blobs as test constants even after `emitFill` is re-expressed through the assembler, so the regression anchor survives. + +## Out of scope (later rungs) +Read-modify-write / trails (stage 2 of the tutorial ladder), oscillators + float codegen (stage 3), 2D/3D addressing (stages 4–5), Ripples graduation, source-level loops, variables, the register allocator, SSA. This plan is the second statement + the IR/assembler it forces. diff --git a/docs/history/plans/Plan-20260627 - MoonLive expressions + host-bound functions (domain-neutral core) (shipped).md b/docs/history/plans/Plan-20260627 - MoonLive expressions + host-bound functions (domain-neutral core) (shipped).md new file mode 100644 index 0000000..64eebfb --- /dev/null +++ b/docs/history/plans/Plan-20260627 - MoonLive expressions + host-bound functions (domain-neutral core) (shipped).md @@ -0,0 +1,98 @@ +# Plan — MoonLive: expressions + host-bound functions (domain-neutral core) + +> Approved plan record (CLAUDE.md *Plan before implementing* / *Refactor for simplicity*). A design correction on top of the IR rung (Steps 1-5, fill/setRGB/random16 on host + Xtensa): replace the `setRGB`-shaped special-case grammar with **general expressions**, and move the LED-domain functions (`setRGB`, `fill`, `random16`) out of the core compiler into a **host builtin table** the light-domain binding registers. Fixes three product-owner remarks at one root. + +## The three remarks, one root cause + +1. **`setRGB(random16(256), random16(256), 30, 0)` doesn't work** — the parser only allows `random16` in the *index* slot; colour slots are literal-only. Bespoke per-slot rules instead of "every argument is an expression." +2. **`random16(255)` caps at 255** — the index/colour validators conflate ranges; `random16` returns uint16 (0..65535). +3. **The core is light-specific** — `setRGB`/`fill`/the `Store` IR op / `buf[i*cpl]` are baked into `src/core/moonlive/`, violating *Domain-neutral core*. The engine should know *language* + *ISA*, never *LEDs*. + +Root cause: the compiler was built around the *statement shape* (`setRGB(idx, r, g, b)`) rather than around **expressions + a generic call mechanism**. The fix is the architecture ESPLiveScript/ARTI-FX use and the MoonLive doc §3.4 specifies: the core knows expressions + `call(builtin, args…)`; the **host registers the functions**. + +## Decisions locked with the product owner + +- **Full generalization now** (not a quick per-slot patch): every argument is an expression; `setRGB`/`fill`/`random16` become host-bound functions; the core keeps only `Call` + arithmetic. +- **Host builtin table** (hpwit `arti_external_function` / ARTI / doc §3.4 model): the light-domain binding registers `{name → descriptor}`; the core parser resolves a call by name against the table and codegen dispatches generically. The core owns *dispatch*, the light domain owns *the functions*. + +## The hot-path reconciliation (doc §3.4 + the product owner's choice) + +Doc §3.4 says pixel writers (`setRGB`) must lower to **direct stores** (the identity-mapping fast path), NOT a per-pixel host *call* — a `call` per pixel would wreck 16K×50FPS. The product-owner choice is "all host-bound." These reconcile cleanly: **the builtin table is the binding mechanism for everything, and each descriptor carries HOW it lowers** — + +- **`Kind::Call`** — a pure helper (`random16`, later `sin`/`hsvToRgb`): lower to a generic `Call` to the host C function pointer. +- **`Kind::Inline`** — a buffer writer (`setRGB`, `fill`): the descriptor names an inline lowering the backend knows by an opcode tag (a small fixed set), so it lowers to stores, no call. + +Crucially, **the core does not hardcode `setRGB`** — it gets the name, the arg count, the kind, and (for inline) an opcode tag from the *table the light domain populates*. The core's inline lowering is generic over the opcode tag; the light domain decides which tags exist and registers the names. So the core stays domain-neutral (no string "setRGB", no RGB layout) while the hot path stays inline. This is the synthesis: domain-neutral core, fast path, host owns the vocabulary. + +## Architecture + +``` +src/core/moonlive/ + MoonLiveBuiltins.h (NEW) — the neutral descriptor + a fixed-capacity BuiltinTable: + { name, argc, Kind (Call|Inline), const void* fn (Call), + uint8_t inlineOp (Inline) }. No LED knowledge. + MoonLiveCompiler — parser: a real expression grammar (primary := number | call; + call := ident "(" args ")"). Resolves each call name against + the injected BuiltinTable. Emits IR: Call for Kind::Call, + a generic InlineOp(tag, args…) for Kind::Inline. No setRGB/fill + strings, no Store-with-RGB-shape baked in. + MoonLiveIr.h — drop the RGB-specific Store; add a neutral `InlineOp` carrying an + opcode tag + operand vregs. Keep Const/Add/Mul/Bounds/Call/Loop. + (Bounds stays — it's a neutral guard the inline writers request.) + +src/light/moonlive/ + MoonLiveBuiltins_light.{h} (NEW) — the LIGHT-DOMAIN registration: builds a BuiltinTable with + setRGB (Inline op=WriteRGB), fill (Inline op=FillRGB), + random16 (Call → host fn). This is where "setRGB" the NAME and + the RGB semantics live. The binding injects this table into the + engine at compile time. + MoonLiveEffect.h — passes the light builtin table to engine_.compile(source, table). + +src/platform// + moonlive_lower_*.cpp — the per-ISA inline-op lowering: given InlineOp(WriteRGB, addr,r,g,b) + emit the store sequence; InlineOp(FillRGB, …) emit the fill loop. + The opcode tags are a small neutral enum in core; the backends + implement them. (random16's Call lowering already exists.) +``` + +The opcode-tag enum (e.g. `InlineKind::WriteRGB`, `FillRGB`) lives in core as a neutral list — it's "the inline operations a backend knows how to emit," not "LED operations." The light domain maps its function *names* to these tags; a different host (a display, a sensor) would register different names against whatever inline ops its backend supports, or use only `Call`. The core never says "RGB" in a domain sense — `WriteRGB` is just "store 3 consecutive bytes at a computed address," a neutral primitive. + +## Expression grammar (the real fix for #1, #2) + +``` +program := stmt ";" End +stmt := call // a statement is a (void) call: setRGB(...) / fill(...) +call := ident "(" [expr {"," expr}] ")" +expr := number // 0..65535 (uint16) — range checked at USE, not parse + | call // nested: random16(256) as an argument +``` + +- Every argument slot parses an `expr`, so `setRGB(random16(256), random16(256), 30, 0)` works (#1). +- A number literal is a uint16 (0..65535); `random16(N)` accepts N up to 65535 (#2). A value used as a colour is masked to a byte at the store (the inline writer does `& 0xFF`), so out-of-byte colours wrap rather than erroring — consistent, no bespoke per-slot range rule. +- Each `expr` lowers to a vreg (a `Const`, or a `Call` result). `setRGB`/`fill` then consume those vregs via their InlineOp. The bounds guard wraps the inline write as before. + +## Steps (desktop-first, each green) + +1. **Core: BuiltinTable + neutral IR.** Add `MoonLiveBuiltins.h` (descriptor + table) and the neutral `InlineOp` + `InlineKind` enum; remove the RGB-specific `Store` and the `buildSetRgbIr`/`buildFillIr`/`buildSetRgbRandomIr` helpers from core (they encode LED shape). The IR builders move to the light domain. +2. **Light: register setRGB/fill/random16.** `MoonLiveBuiltins_light.h` builds the table; the IR-construction for WriteRGB/FillRGB lives here (it knows RGB). `random16` registered as a `Call`. +3. **Compiler: expression parser + table resolution.** Parse expr-per-arg; resolve call names against the injected table; build IR (Call / InlineOp). Delete the `setRGB`/`fill` keyword special-cases. +4. **Host backend: inline-op lowering.** `moonlive_lower_host.cpp` lowers `InlineOp(WriteRGB/FillRGB)` to the store sequences (the bytes the old `Store`/fill produced). Behavioral golden test still passes (output unchanged). +5. **Xtensa backend: inline-op lowering.** Same for `moonlive_lower_xtensa.cpp`. Build + flash Olimex; verify `setRGB(random16(256), random16(256), 30, 0)` and `random16(65535)` live. +6. **Tests + docs.** Update unit tests (expression cases, every-arg-random, uint16 range, the domain-neutral-core assertion: grep core for "RGB"/"setRGB" → none in the LED sense). Extend the scenario. decisions.md: the "built around the statement shape, not expressions" lesson. Update MoonLiveEffect.md (the builtin-table model, prior art: ESPLiveScript/ARTI bound functions). + +## Validation + +- Desktop: `setRGB(random16(256), random16(256), 30, 0)` writes a random pixel with a random red+green; `random16(65535)` accepted; behavioral golden (fill output unchanged) holds. All unit tests green. +- **Domain-neutral check** (the #3 fix, mechanised): a test/grep asserts `src/core/moonlive/` contains no LED vocabulary ("setRGB", "fill", "RGB" in the colour sense, "cpl"/"buffer" semantics) — only `Call`, `InlineOp`, arithmetic, the neutral opcode enum. +- Xtensa: the failing cases from the remarks work live on the Olimex; no crash. +- P4/RISC-V still builds (stub). + +## Risks / watch-items + +- **Don't let `WriteRGB` smuggle LED-ness into core.** The neutral framing must hold: the core enum entry is "store N bytes at a computed address," documented as such; the *name* `setRGB` and the 3-channel meaning live only in the light registration. If that line blurs, the refactor failed its own #3 goal. +- **Inline-op set stays small + neutral.** Resist adding `setRGBXY`-specific ops; XY/XYZ are index arithmetic feeding the same WriteRGB (expressions compute the index). Only genuinely-distinct store shapes get a tag. +- **No per-pixel call regression.** `setRGB` must stay inline (Kind::Inline), not become a `Call` — the doc §3.4 hot-path rule. The table's Kind enforces this. +- **Scope creep.** No `sin`/`hsvToRgb`/variables/`for`-in-source this plan — just the generalization + the 3 fixes. Those built-ins are later (they slot into the same table trivially, which is the point). + +## Out of scope (later) +Float built-ins (sin/cos/sqrt/hsvToRgb), source-level variables and loops, setRGBXY/XYZ sugar, the RISC-V inline lowering (P4), a real register allocator. This plan is: expressions, the host builtin table, and domain-neutral core — fixing the three remarks. diff --git a/docs/moonmodules/light/moonlive/MoonLiveEffect.md b/docs/moonmodules/light/moonlive/MoonLiveEffect.md index cb6d775..e644f5b 100644 --- a/docs/moonmodules/light/moonlive/MoonLiveEffect.md +++ b/docs/moonmodules/light/moonlive/MoonLiveEffect.md @@ -2,18 +2,28 @@ MoonLive is projectMM's **live-script engine** — author an effect as text and run it on a running device, compiled to native machine code so it executes at near-hand-written speed in the render hot path. The broader design lives in [livescripts-analysis-top-down.md](../../../backlog/livescripts-analysis-top-down.md) (a backlog design study); this page documents the module. -A scripted effect carries its **script source** as an editable, persisted text control, and a front-end (lexer → parser → codegen) compiles it to native code on the next tick. The grammar is one statement — `fill(r, g, b);`. The compiler emits machine code for whichever ISA the device runs (Xtensa on the classic/S3, RISC-V on the P4) or the host ISA on desktop, places it in executable memory, and the engine calls it each render tick to write the buffer. Its headline property is **golden-bytes equivalence** — parsing `fill(0,0,255)` produces byte-for-byte the same machine code the hand-written emitter does, so the parser adds no codegen of its own. (Status: one statement; the grammar is the current surface, not the ceiling — see the design study.) +A scripted effect carries its **script source** as an editable, persisted text control, and a front-end (lexer → parser → IR → per-ISA assembler) compiles it to native code on the next tick. The grammar is a function-call statement with **expression arguments** — any argument may be a literal or a nested call: + +``` +setRGB(random16(256), 0, 0, 255); // a random pixel, blue +setRGB(5, random16(256), 0, 0); // pixel 5, a random red +fill(0, 0, 255); // every light blue +``` + +The functions are **not built into the compiler** — `setRGB`, `fill`, `random16` are registered by the *host* (the light domain) in a builtin table; the core compiler owns only the grammar and a generic call/inline mechanism (the ESPLiveScript / ARTI bound-function model). The compiler emits machine code for whichever ISA the device runs (Xtensa on the classic/S3) or the host ISA on desktop, places it in executable memory, and the engine calls it each render tick. ## Controls -- `source` — the script text (default `fill(0, 0, 255);` — solid blue). Editing it recompiles live: a valid script swaps in on the next tick; a parse error shows its diagnostic in the module status and the layer goes dark until fixed (the script-editor loop, robust + no reboot). +- `source` — the script text (default `fill(0, 0, 255);` — solid blue). Editing it recompiles live: a valid script swaps in on the next tick; a failed compile frees the old code, shows the diagnostic in the module status, and renders dark until fixed (the script-editor loop, robust + no reboot). ## Pieces -- **`MoonLive`** (`src/core/moonlive/MoonLive.h/.cpp`) — the **domain-neutral engine core**. Owns a block of executable memory; `compile(source)` runs the front-end and places the emitted code, `run(buf, nLights, cpl, t)` calls it, `free()`/`ok()`/`error()` round out the lifecycle. Includes only ``, the compiler/emitter seams, and the platform seam — never `EffectBase`, `Buffer`, or any projectMM type. -- **`MoonLiveCompiler`** (`src/core/moonlive/MoonLiveCompiler.h/.cpp`) — the **platform-independent front-end**: a hand-written recursive-descent lexer + parser for the `fill(r,g,b);` grammar, plus codegen that drives the per-ISA emitter. Pure (source in, bytes out, deterministic), so it unit-tests with no hardware. Knows the *language*, never an ISA. -- **`emitFill`** (`src/core/moonlive/moonlive_emit.h`) — the **per-ISA backend seam**. A neutral declaration; the implementation is the ISA's machine code, behind the platform boundary: `src/platform/esp32/moonlive_emit.cpp` (Xtensa for the classic/S3, RISC-V for the P4) and `src/platform/desktop/moonlive_emit.cpp` (host arm64 / x86-64). The engine and front-end never branch on ISA — they ask for bytes and run them; an ISA is a new branch in the emitter. -- **`MoonLiveEffect`** (`src/light/moonlive/MoonLiveEffect.h`) — the **thin binding**: a first-class `EffectBase` carrying the `source` control, whose `loop()` delegates to the engine over its own `buffer()`. The engine is projectMM-agnostic; the binding is the only coupled layer, which keeps the engine a clean, reusable core. +- **`MoonLive`** (`src/core/moonlive/MoonLive.h/.cpp`) — the **domain-neutral engine core**. Owns a block of executable memory; `compile(source, table)` runs the front-end against a host builtin table and places the emitted code, `run(buf, nLights, cpl, t)` calls it. Includes only ``, the compiler/emitter seams, and the platform seam — never `EffectBase`, `Buffer`, or any LED type. +- **`MoonLiveBuiltins`** (`src/core/moonlive/MoonLiveBuiltins.h`) — the **neutral host-binding seam**: a `BuiltinTable` of `{name → descriptor}`, where a descriptor is either `Call` (a host C function pointer — a pure helper like `random16`) or `Inline` (a neutral opcode tag the backend emits inline — the hot-path buffer writers, no per-pixel call). The core owns no function names; it resolves a call against whatever the host registered. +- **`MoonLiveCompiler`** (`src/core/moonlive/MoonLiveCompiler.h/.cpp`) — the **platform-independent front-end**: a recursive-descent lexer + expression parser that lowers each statement to the typed IR (`MoonLiveIr.h`). Pure (source + table in, IR out, deterministic). Knows the *language*, never an ISA and never a domain. +- **`MoonLiveBuiltins_light`** (`src/light/moonlive/MoonLiveBuiltins_light.h`) — the **light-domain registration**: the only place the LED vocabulary lives. Registers `setRGB`/`fill` (Inline, lowering to RGB stores) and `random16` (Call). A different host (display, sensor) writes its own table; the core is unchanged. +- **per-ISA assembler + lowering** (`src/platform//moonlive_asm_*` + `moonlive_lower_*`) — a tiny named-instruction MacroAssembler with label back-patching, and the IR→bytes lowering that drives it. Xtensa for the classic/S3 (`__XTENSA__`), the host ISA on desktop (arm64/x86-64). Adding an ISA is a new assembler + lowering; the front-end and IR are unchanged. (`emitFill`/`emitAnimatedFill` remain as the hand-encoded `fill` references the assembler's output is checked against.) +- **`MoonLiveEffect`** (`src/light/moonlive/MoonLiveEffect.h`) — the **thin binding**: a first-class `EffectBase` carrying the `source` control, whose `loop()` delegates to the engine over its own `buffer()` and passes the light builtin table to `compile`. The engine is projectMM-agnostic; the binding is the only coupled layer. ## Cross-domain wiring @@ -27,10 +37,12 @@ MoonLive's native-codegen approach — compile a small C-like language straight ## Tests -[unit_moonlive_fill](../../../../test/unit/core/unit_moonlive_fill.cpp) runs the engine path in-process on the desktop host backend: `emitFill`/`emitAnimatedFill` produce a non-empty routine (and reject a too-small buffer), `compile` + `run` fill a buffer with the chosen colour, the animated routine derives its colour from the per-frame `t`, zero-lights writes nothing, recompile swaps the colour, `free` returns to `!ok()`, and `allocExec`/`writeExec`/`freeExec` round-trip a callable block. [unit_moonlive_compiler](../../../../test/unit/core/unit_moonlive_compiler.cpp) pins the front-end: the **golden-bytes equivalence** (parsed `fill(r,g,b)` == hand-emitted bytes), whitespace tolerance, every parser diagnostic (wrong name, bad arity, out-of-range, missing punctuation, trailing junk — no crash on any malformed input), and the live source-recompile swap. `unit_moonlive_fill` also pins the buffer-shape guards (a null buffer, zero lights, or a sub-RGB `cpl<3` layout is left untouched, no overrun). +[unit_moonlive_fill](../../../../test/unit/core/unit_moonlive_fill.cpp) runs the engine path in-process on the desktop host backend (`compile`/`run`, the animated routine, zero-lights, recompile, `free`, the `allocExec`/`writeExec`/`freeExec` round-trip, the buffer-shape guards). [unit_moonlive_ir](../../../../test/unit/core/unit_moonlive_ir.cpp) pins the **behavioral golden** — a compiled `fill` and the hand-encoded reference render an identical buffer — plus setRGB's single-pixel write and the runtime bounds guard. [unit_moonlive_compiler](../../../../test/unit/core/unit_moonlive_compiler.cpp) pins the expression grammar (`random16` in any/every argument slot, uint16 bounds), the parser diagnostics (no crash on malformed input), live recompile, and the **domain-neutral** property: with an empty builtin table the core knows *no* functions, and a host can register an arbitrary name against the same machinery. + +The grammar + bounds guard are verified live on the S3/Olimex (Xtensa) by editing the `source` control — the device compiles the expression on-chip and renders it. -[scenario_MoonLiveEffect_livescript](../../../../test/scenarios/light/scenario_MoonLiveEffect_livescript.json) exercises the effect **as a wired MoonModule** — what the unit tests can't reach: add it, live-edit the `source` to recolour (recompile), push a broken script (prior code keeps running, the parse error surfaces in the status, no crash), recover, resize the grid to 1×1 and back while rendering (the every-grid-size hard rule), then remove and re-add (exec memory re-acquired clean). It runs in-process on the desktop backend each commit, and the same JSON runs live over REST against the device backends. The Xtensa/RISC-V backends are validated by the live S3/P4 runs (a `MoonLiveEffect` on a Layer lights the grid from its `source`), which the desktop tests can't reach. +[scenario_MoonLiveEffect_livescript](../../../../test/scenarios/light/scenario_MoonLiveEffect_livescript.json) exercises the effect **as a wired MoonModule** — what the unit tests can't reach: add it, live-edit the `source` to recolour (recompile), push a broken script (`MoonLive::compile` fails, frees the previous code, `MoonLiveEffect` reports the parse error in the status and renders dark — no crash), recover, resize the grid to 1×1 and back while rendering (the every-grid-size hard rule), then remove and re-add (exec memory re-acquired clean). It runs in-process on the desktop backend each commit, and the same JSON runs live over REST against the device backends. The Xtensa/RISC-V backends are validated by the live S3/P4 runs (a `MoonLiveEffect` on a Layer lights the grid from its `source`), which the desktop tests can't reach. ## Source -[MoonLive.h](../../../../src/core/moonlive/MoonLive.h) · [MoonLiveCompiler.h](../../../../src/core/moonlive/MoonLiveCompiler.h) · [moonlive_emit.h](../../../../src/core/moonlive/moonlive_emit.h) · [MoonLiveEffect.h](../../../../src/light/moonlive/MoonLiveEffect.h) +[MoonLive.h](../../../../src/core/moonlive/MoonLive.h) · [MoonLiveBuiltins.h](../../../../src/core/moonlive/MoonLiveBuiltins.h) · [MoonLiveCompiler.h](../../../../src/core/moonlive/MoonLiveCompiler.h) · [MoonLiveIr.h](../../../../src/core/moonlive/MoonLiveIr.h) · [MoonLiveBuiltins_light.h](../../../../src/light/moonlive/MoonLiveBuiltins_light.h) · [MoonLiveEffect.h](../../../../src/light/moonlive/MoonLiveEffect.h) diff --git a/docs/tests/scenario-tests.md b/docs/tests/scenario-tests.md index ee139e8..d98db6f 100644 --- a/docs/tests/scenario-tests.md +++ b/docs/tests/scenario-tests.md @@ -295,6 +295,51 @@ Add NetworkSendDriver and run the bounded FPS measurement on the no-LUT path. - `pc-macos`: contract set 2026-06-02 "initial contract" · observed 2026-06-02 → 2026-06-05 - `pc-windows`: observed 2026-06-07 +### scenario_modifier_chain + +`test/scenarios/light/scenario_modifier_chain.json` — Stack TWO modifiers on one Layer (Region then Multiply) and verify the chain composes live end-to-end — the capability the old single-modifier engine couldn't do. Prepares its own canvas: Layout(Grid 32x32) + Layer + NoiseEffect + Region(0..50) + Multiply(2x), measures the composite, then adds a third (Checkerboard mask) and measures again, then removes the middle modifier and measures — exercising add/remove on a multi-modifier chain. A broken fold (null buffer, wrong light count, crash on a disabled/removed stage) shows up as a failed measure. The fold composition + order semantics are pinned by unit_Layer_modifier_chain; this is the live end-to-end gate. + +**Mode**: `mutate` · **Also touches**: RegionModifier, MultiplyModifier, CheckerboardModifier, RotateModifier, NoiseEffect, Layouts, GridLayout, Drivers, NetworkSendDriver + +#### `add-mask` (add_module) 📏 + +Add a third modifier (Checkerboard mask) on top of the chain — a 3-deep fold. Measure that the deeper chain still renders. + +**Setup** (preceding non-measured steps): +- `region-then-multiply` (measure) — Two stacked modifiers: Region(top-left quarter) then Multiply(2x mirror) compose into one mapping. Measure the live composite. + +**Performance** (contract / observed) — tick stored, FPS shown: + +| Board | FPS | heap | block | +|---|---|---|---| +| `pc-macos` | — / 142,857-200,000 | — / unlimited | — / unlimited | + +- `pc-macos`: observed 2026-06-26 + +#### `remove-middle` (remove_module) 📏 + +Remove the middle modifier (Multiply) — the chain re-folds with Region then Checkerboard, no stale state. Measure. + +**Performance** (contract / observed) — tick stored, FPS shown: + +| Board | FPS | heap | block | +|---|---|---|---| +| `pc-macos` | — / 45,455-55,556 | — / unlimited | — / unlimited | + +- `pc-macos`: observed 2026-06-26 + +#### `add-live-rotate` (add_module) 📏 + +Add a DYNAMIC Rotate on top of the static chain — its modifyLive runs the per-frame remap pass over the composed buffer. Verifies a static chain + a live modifier coexist (the buffer is remapped each frame on top of the baked Region/Checkerboard mapping) without a crash or null buffer. + +**Performance** (contract / observed) — tick stored, FPS shown: + +| Board | FPS | heap | block | +|---|---|---|---| +| `pc-macos` | — / 25,641-28,571 | — / unlimited | — / unlimited | + +- `pc-macos`: observed 2026-06-26 + ### scenario_modifier_swap `test/scenarios/light/scenario_modifier_swap.json` — Swap the Layer's modifier between Multiply and Checkerboard and verify the pipeline stays live across each replace. Prepares its own canvas (clear + rebuild) so it runs from any device state: one Layout(Grid 32x32) + one Layer + one effect + one modifier, then replace_module cycles the modifier MOD slot Multiply -> Checkerboard -> Multiply, measuring after each so a broken swap (null buffer / wrong light count) shows up. Exercises the modifier-replace path the UI's drag-replace uses. @@ -319,10 +364,16 @@ Multiply modifier active — pipeline live, LUT folds the grid. | Board | FPS | heap | block | |---|---|---|---| +| `esp32` | — / 1,783-2,179 | — / 145KB | — / 108KB | | `esp32-eth` | — / 1,580-7,752 | — / 172KB-225KB | — / 76KB-108KB | +| `esp32p4-eth` | — / 5,587-6,061 | — / 33243KB | — / 376KB | +| `esp32s3-n16r8` | — / 1,773-2,571 | — / 8350KB | — / 92KB | | `pc-macos` | — / 50,000-166,667 | — / unlimited | — / unlimited | +- `esp32`: observed 2026-06-25 - `esp32-eth`: observed 2026-06-07 → 2026-06-08 +- `esp32p4-eth`: observed 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-25 - `pc-macos`: observed 2026-06-07 → 2026-06-21 #### `checkerboard` (measure) 📏 @@ -336,10 +387,16 @@ Checkerboard modifier active — masks half the lights; pipeline stays live (dri | Board | FPS | heap | block | |---|---|---|---| +| `esp32` | — / 892-922 | — / 145KB | — / 108KB | | `esp32-eth` | — / 769-990 | — / 170KB-225KB | — / 76KB-108KB | +| `esp32p4-eth` | — / 2,747-2,762 | — / 33242KB | — / 376KB | +| `esp32s3-n16r8` | — / 924-943 | — / 8349KB | — / 92KB | | `pc-macos` | — / 15,873-58,824 | — / unlimited | — / unlimited | +- `esp32`: observed 2026-06-25 - `esp32-eth`: observed 2026-06-07 → 2026-06-08 +- `esp32p4-eth`: observed 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-25 - `pc-macos`: observed 2026-06-07 → 2026-06-25 #### `multiply-2` (measure) 📏 @@ -353,10 +410,16 @@ Back to Multiply — replace round-trips cleanly, pipeline live again. | Board | FPS | heap | block | |---|---|---|---| +| `esp32` | — / 2,079-2,208 | — / 145KB | — / 108KB | | `esp32-eth` | — / 1,587-2,278 | — / 169KB-225KB | — / 76KB-108KB | +| `esp32p4-eth` | — / 6,329-6,410 | — / 33243KB | — / 376KB | +| `esp32s3-n16r8` | — / 2,146-2,604 | — / 8349KB-8350KB | — / 92KB | | `pc-macos` | — / 45,455-166,667 | — / unlimited | — / unlimited | +- `esp32`: observed 2026-06-25 - `esp32-eth`: observed 2026-06-07 → 2026-06-08 +- `esp32p4-eth`: observed 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-25 - `pc-macos`: observed 2026-06-07 → 2026-06-25 ### scenario_perf_full @@ -381,14 +444,14 @@ Bare minimum at 16²: Grid + Layer + Checkerboard, no output driver, audio/disco | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 7,752 | — / 134KB | — / 108KB | -| `esp32p4-eth` | — / 14,925-17,241 | — / 33226KB-33244KB | — / 376KB | -| `esp32s3-n16r8` | — / 5,376-9,009 | — / 8340KB-8346KB | — / 104KB-112KB | +| `esp32` | — / 7,692-8,929 | — / 134KB-147KB | — / 108KB | +| `esp32p4-eth` | — / 14,925-17,544 | — / 33226KB-33245KB | — / 376KB | +| `esp32s3-n16r8` | — / 5,376-9,009 | — / 8340KB-8352KB | — / 92KB-112KB | | `pc-macos` | — / 1,000,000-— | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-26 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-25 - `pc-macos`: observed 2026-06-17 → 2026-06-25 #### `measure-no-audio` (measure) 📏 @@ -400,14 +463,14 @@ Bare minimum at 16²: Grid + Layer + Checkerboard, no output driver, audio/disco | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 8,621 | — / 134KB | — / 108KB | -| `esp32p4-eth` | — / 18,182-18,868 | — / 33228KB-33244KB | — / 376KB | -| `esp32s3-n16r8` | — / 8,065-9,901 | — / 8338KB-8346KB | — / 104KB-112KB | +| `esp32` | — / 8,621-9,901 | — / 134KB-147KB | — / 108KB | +| `esp32p4-eth` | — / 18,182-18,868 | — / 33228KB-33245KB | — / 376KB | +| `esp32s3-n16r8` | — / 8,065-9,901 | — / 8338KB-8352KB | — / 92KB-112KB | | `pc-macos` | — / 1,000,000-— | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-26 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-25 - `pc-macos`: observed 2026-06-17 → 2026-06-25 #### `measure-quiet` (measure) 📏 @@ -421,14 +484,14 @@ Quiet baseline: render-only, audio + discovery off. The cleanest render floor; t | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 8,621 | — / 131KB | — / 108KB | -| `esp32p4-eth` | — / 17,544-18,519 | — / 33226KB-33243KB | — / 376KB | -| `esp32s3-n16r8` | — / 8,696-9,901 | — / 8337KB-8346KB | — / 100KB-112KB | +| `esp32` | — / 7,246-9,901 | — / 131KB-146KB | — / 108KB | +| `esp32p4-eth` | — / 17,544-18,519 | — / 33226KB-33245KB | — / 376KB | +| `esp32s3-n16r8` | — / 7,752-9,901 | — / 8337KB-8352KB | — / 92KB-112KB | | `pc-macos` | — / 1,000,000-— | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-26 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-25 - `pc-macos`: observed 2026-06-17 → 2026-06-25 #### `measure-modifier` (measure) 📏 @@ -440,14 +503,14 @@ Quiet baseline: render-only, audio + discovery off. The cleanest render floor; t | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 3,175 | — / 130KB | — / 108KB | -| `esp32p4-eth` | — / 9,434-10,638 | — / 33224KB-33241KB | — / 376KB | -| `esp32s3-n16r8` | — / 3,413-4,237 | — / 8336KB-8344KB | — / 104KB-112KB | +| `esp32` | — / 2,786-3,610 | — / 130KB-145KB | — / 108KB | +| `esp32p4-eth` | — / 8,772-10,638 | — / 33224KB-33243KB | — / 376KB | +| `esp32s3-n16r8` | — / 3,413-4,237 | — / 8336KB-8350KB | — / 92KB-112KB | | `pc-macos` | — / 1,000,000-— | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-26 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-25 - `pc-macos`: observed 2026-06-17 → 2026-06-25 #### `measure-preview` (measure) 📏 @@ -460,14 +523,14 @@ Quiet baseline: render-only, audio + discovery off. The cleanest render floor; t | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 8,696 | — / 123KB | — / 108KB | -| `esp32p4-eth` | — / 15,873-17,857 | — / 33228KB-33243KB | — / 376KB | -| `esp32s3-n16r8` | — / 8,065-9,434 | — / 8335KB-8346KB | — / 92KB-112KB | +| `esp32` | — / 8,696-9,524 | — / 123KB-147KB | — / 108KB | +| `esp32p4-eth` | — / 15,873-18,182 | — / 33228KB-33245KB | — / 376KB | +| `esp32s3-n16r8` | — / 8,065-9,434 | — / 8335KB-8352KB | — / 92KB-112KB | | `pc-macos` | — / 200,000-— | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-25 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-25 - `pc-macos`: observed 2026-06-17 → 2026-06-25 #### `measure-network` (measure) 📏 @@ -479,14 +542,14 @@ Quiet baseline: render-only, audio + discovery off. The cleanest render floor; t | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 7,194 | — / 131KB | — / 108KB | -| `esp32p4-eth` | — / 14,493-17,544 | — / 33226KB-33240KB | — / 376KB | -| `esp32s3-n16r8` | — / 7,092-8,065 | — / 8334KB-8344KB | — / 84KB-112KB | +| `esp32` | — / 6,098-7,194 | — / 131KB-145KB | — / 108KB | +| `esp32p4-eth` | — / 14,493-17,544 | — / 33226KB-33244KB | — / 376KB | +| `esp32s3-n16r8` | — / 6,452-8,065 | — / 8334KB-8351KB | — / 84KB-112KB | | `pc-macos` | — / 1,000,000-— | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-26 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-26 - `pc-macos`: observed 2026-06-17 → 2026-06-25 #### `measure-rmt` (measure) 📏 @@ -499,14 +562,14 @@ Quiet baseline: render-only, audio + discovery off. The cleanest render floor; t | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 6,579 | — / 106KB | — / 84KB | -| `esp32p4-eth` | — / 15,873-17,857 | — / 33200KB-33219KB | — / 376KB | -| `esp32s3-n16r8` | — / 8,333-9,259 | — / 8307KB-8321KB | — / 84KB-112KB | +| `esp32` | — / 6,579-9,174 | — / 106KB-122KB | — / 84KB-108KB | +| `esp32p4-eth` | — / 15,873-17,857 | — / 33200KB-33221KB | — / 376KB | +| `esp32s3-n16r8` | — / 7,194-9,346 | — / 8307KB-8328KB | — / 84KB-112KB | | `pc-macos` | — / 1,000,000-— | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-25 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-26 - `pc-macos`: observed 2026-06-17 → 2026-06-25 #### `measure-lcd` (measure) 📏 @@ -519,14 +582,14 @@ Quiet baseline: render-only, audio + discovery off. The cleanest render floor; t | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 8,403 | — / 126KB | — / 108KB | -| `esp32p4-eth` | — / 16,129-17,857 | — / 33225KB-33243KB | — / 376KB | -| `esp32s3-n16r8` | — / 7,042-9,259 | — / 8336KB-8345KB | — / 92KB-112KB | +| `esp32` | — / 8,403-9,901 | — / 126KB-147KB | — / 108KB | +| `esp32p4-eth` | — / 15,873-17,857 | — / 33225KB-33245KB | — / 376KB | +| `esp32s3-n16r8` | — / 7,042-9,259 | — / 8333KB-8352KB | — / 88KB-112KB | | `pc-macos` | — / 1,000,000-— | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-25 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-25 - `pc-macos`: observed 2026-06-17 → 2026-06-24 #### `measure-parlio` (measure) 📏 @@ -539,14 +602,14 @@ Quiet baseline: render-only, audio + discovery off. The cleanest render floor; t | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 8,475 | — / 135KB | — / 108KB | -| `esp32p4-eth` | — / 15,873-17,857 | — / 33225KB-33243KB | — / 376KB | -| `esp32s3-n16r8` | — / 7,692-9,434 | — / 8338KB-8346KB | — / 104KB-112KB | +| `esp32` | — / 8,475-9,901 | — / 135KB-147KB | — / 108KB | +| `esp32p4-eth` | — / 15,873-17,857 | — / 33225KB-33245KB | — / 376KB | +| `esp32s3-n16r8` | — / 7,692-9,434 | — / 8338KB-8352KB | — / 92KB-112KB | | `pc-macos` | — / 1,000,000-— | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-25 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-25 - `pc-macos`: observed 2026-06-17 → 2026-06-24 #### `measure-light-16` (measure) 📏 @@ -561,14 +624,14 @@ Quiet baseline: render-only, audio + discovery off. The cleanest render floor; t | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 6,711 | — / 134KB | — / 108KB | -| `esp32p4-eth` | — / 15,385-18,868 | — / 33226KB-33243KB | — / 376KB | -| `esp32s3-n16r8` | — / 8,403-9,901 | — / 8336KB-8346KB | — / 100KB-112KB | +| `esp32` | — / 6,711-9,804 | — / 134KB-147KB | — / 108KB | +| `esp32p4-eth` | — / 15,385-18,868 | — / 33226KB-33245KB | — / 376KB | +| `esp32s3-n16r8` | — / 8,403-9,901 | — / 8336KB-8352KB | — / 92KB-112KB | | `pc-macos` | — / 1,000,000-— | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-25 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-25 - `pc-macos`: observed 2026-06-17 → 2026-06-24 #### `measure-light-32` (measure) 📏 @@ -581,14 +644,14 @@ Quiet baseline: render-only, audio + discovery off. The cleanest render floor; t | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 2,801 | — / 134KB | — / 108KB | -| `esp32p4-eth` | — / 7,246-7,519 | — / 33225KB-33241KB | — / 376KB | -| `esp32s3-n16r8` | — / 3,049-3,597 | — / 8331KB-8343KB | — / 100KB-112KB | +| `esp32` | — / 2,801-3,367 | — / 134KB-144KB | — / 108KB | +| `esp32p4-eth` | — / 7,246-7,576 | — / 33225KB-33243KB | — / 376KB | +| `esp32s3-n16r8` | — / 3,049-3,597 | — / 8331KB-8350KB | — / 92KB-112KB | | `pc-macos` | — / 333,333-1,000,000 | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-26 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-25 - `pc-macos`: observed 2026-06-17 → 2026-06-24 #### `measure-light-64` (measure) 📏 @@ -601,14 +664,14 @@ Quiet baseline: render-only, audio + discovery off. The cleanest render floor; t | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 872 | — / 125KB | — / 108KB | -| `esp32p4-eth` | — / 2,008-2,212 | — / 33218KB-33232KB | — / 376KB | -| `esp32s3-n16r8` | — / 917-1,011 | — / 8312KB-8334KB | — / 88KB-112KB | +| `esp32` | — / 870-928 | — / 125KB-135KB | — / 108KB | +| `esp32p4-eth` | — / 2,008-2,232 | — / 33218KB-33234KB | — / 376KB | +| `esp32s3-n16r8` | — / 894-1,011 | — / 8312KB-8341KB | — / 88KB-112KB | | `pc-macos` | — / 12,658-250,000 | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-26 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-25 - `pc-macos`: observed 2026-06-17 → 2026-06-24 #### `measure-light-128` (measure) 📏 @@ -621,14 +684,14 @@ Quiet baseline: render-only, audio + discovery off. The cleanest render floor; t | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 229 | — / 89KB | — / 62KB | -| `esp32p4-eth` | — / 515-573 | — / 33182KB-33196KB | — / 376KB | -| `esp32s3-n16r8` | — / 126-134 | — / 8291KB-8298KB | — / 100KB-112KB | +| `esp32` | — / 224-238 | — / 89KB-99KB | — / 62KB | +| `esp32p4-eth` | — / 515-573 | — / 33182KB-33198KB | — / 376KB | +| `esp32s3-n16r8` | — / 114-134 | — / 8291KB-8305KB | — / 92KB-112KB | | `pc-macos` | — / 5,348-62,500 | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-25 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-25 - `pc-macos`: observed 2026-06-17 → 2026-06-24 #### `measure-heavy-16` (measure) 📏 @@ -642,14 +705,14 @@ Quiet baseline: render-only, audio + discovery off. The cleanest render floor; t | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 990 | — / 136KB | — / 108KB | -| `esp32p4-eth` | — / 2,899-3,311 | — / 33229KB-33243KB | — / 376KB | -| `esp32s3-n16r8` | — / 1,148-1,361 | — / 8342KB-8346KB | — / 108KB-112KB | +| `esp32` | — / 990-1,224 | — / 136KB-147KB | — / 108KB | +| `esp32p4-eth` | — / 2,865-3,367 | — / 33229KB-33245KB | — / 376KB | +| `esp32s3-n16r8` | — / 1,100-1,361 | — / 8342KB-8352KB | — / 92KB-112KB | | `pc-macos` | — / 62,500-333,333 | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-25 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-26 - `pc-macos`: observed 2026-06-17 → 2026-06-25 #### `measure-heavy-32` (measure) 📏 @@ -662,14 +725,14 @@ Quiet baseline: render-only, audio + discovery off. The cleanest render floor; t | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 312 | — / 134KB | — / 108KB | -| `esp32p4-eth` | — / 799-893 | — / 33227KB-33241KB | — / 376KB | -| `esp32s3-n16r8` | — / 290-356 | — / 8339KB-8343KB | — / 108KB-112KB | +| `esp32` | — / 306-314 | — / 134KB-144KB | — / 108KB | +| `esp32p4-eth` | — / 799-898 | — / 33227KB-33243KB | — / 376KB | +| `esp32s3-n16r8` | — / 290-356 | — / 8339KB-8350KB | — / 92KB-112KB | | `pc-macos` | — / 15,152-71,429 | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-26 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-25 - `pc-macos`: observed 2026-06-17 → 2026-06-25 #### `measure-heavy-64` (measure) 📏 @@ -682,14 +745,14 @@ Quiet baseline: render-only, audio + discovery off. The cleanest render floor; t | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 73.8 | — / 125KB | — / 108KB | -| `esp32p4-eth` | — / 196-229 | — / 33218KB-33232KB | — / 376KB | -| `esp32s3-n16r8` | — / 87.9-90.3 | — / 8330KB-8334KB | — / 108KB-112KB | +| `esp32` | — / 73.8-79.4 | — / 125KB-135KB | — / 108KB | +| `esp32p4-eth` | — / 196-229 | — / 33218KB-33234KB | — / 376KB | +| `esp32s3-n16r8` | — / 85.2-90.3 | — / 8330KB-8341KB | — / 92KB-112KB | | `pc-macos` | — / 2,924-16,129 | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-26 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-25 - `pc-macos`: observed 2026-06-17 → 2026-06-21 #### `measure-heavy-128` (measure) 📏 @@ -702,14 +765,14 @@ Quiet baseline: render-only, audio + discovery off. The cleanest render floor; t | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 16.0 | — / 89KB | — / 62KB | -| `esp32p4-eth` | — / 55.5-57.4 | — / 33182KB-33196KB | — / 376KB | -| `esp32s3-n16r8` | — / 19.6-20.8 | — / 8293KB-8298KB | — / 104KB-112KB | +| `esp32` | — / 16.0-19.0 | — / 89KB-99KB | — / 62KB | +| `esp32p4-eth` | — / 53.7-57.4 | — / 33182KB-33198KB | — / 376KB | +| `esp32s3-n16r8` | — / 19.2-20.8 | — / 8293KB-8305KB | — / 92KB-112KB | | `pc-macos` | — / 1,094-3,247 | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-25 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-25 - `pc-macos`: observed 2026-06-17 → 2026-06-25 #### `measure-mod-16` (measure) 📏 @@ -723,14 +786,14 @@ Quiet baseline: render-only, audio + discovery off. The cleanest render floor; t | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 2,193 | — / 135KB | — / 108KB | -| `esp32p4-eth` | — / 6,098-6,494 | — / 33224KB-33241KB | — / 376KB | -| `esp32s3-n16r8` | — / 2,193-2,618 | — / 8340KB-8344KB | — / 108KB-112KB | -| `pc-macos` | — / 333,333-1,000,000 | — / unlimited | — / unlimited | +| `esp32` | — / 2,020-2,222 | — / 135KB-145KB | — / 108KB | +| `esp32p4-eth` | — / 5,263-6,494 | — / 33224KB-33243KB | — / 376KB | +| `esp32s3-n16r8` | — / 2,193-2,618 | — / 8340KB-8350KB | — / 92KB-112KB | +| `pc-macos` | — / 250,000-1,000,000 | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-25 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-25 - `pc-macos`: observed 2026-06-17 → 2026-06-25 #### `measure-mod-32` (measure) 📏 @@ -743,14 +806,14 @@ Quiet baseline: render-only, audio + discovery off. The cleanest render floor; t | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 553 | — / 130KB | — / 108KB | -| `esp32p4-eth` | — / 1,631-1,876 | — / 33218KB-33235KB | — / 376KB | -| `esp32s3-n16r8` | — / 600-710 | — / 8329KB-8337KB | — / 100KB-112KB | -| `pc-macos` | — / 90,909-333,333 | — / unlimited | — / unlimited | +| `esp32` | — / 547-586 | — / 130KB-140KB | — / 108KB | +| `esp32p4-eth` | — / 1,631-1,876 | — / 33218KB-33237KB | — / 376KB | +| `esp32s3-n16r8` | — / 600-710 | — / 8329KB-8344KB | — / 92KB-112KB | +| `pc-macos` | — / 5,882-333,333 | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-26 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-26 - `pc-macos`: observed 2026-06-17 → 2026-06-25 #### `measure-mod-64` (measure) 📏 @@ -763,14 +826,14 @@ Quiet baseline: render-only, audio + discovery off. The cleanest render floor; t | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 144 | — / 111KB | — / 96KB | -| `esp32p4-eth` | — / 438-486 | — / 33194KB-33208KB | — / 376KB | -| `esp32s3-n16r8` | — / 153-162 | — / 8307KB-8311KB | — / 108KB-112KB | +| `esp32` | — / 144-149 | — / 111KB-122KB | — / 96KB-100KB | +| `esp32p4-eth` | — / 438-486 | — / 33194KB-33210KB | — / 376KB | +| `esp32s3-n16r8` | — / 119-162 | — / 8307KB-8317KB | — / 92KB-112KB | | `pc-macos` | — / 23,256-71,429 | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-26 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-25 - `pc-macos`: observed 2026-06-17 → 2026-06-25 #### `measure-mod-128` (measure) 📏 @@ -783,14 +846,14 @@ Quiet baseline: render-only, audio + discovery off. The cleanest render floor; t | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 35.1 | — / 36KB | — / 26KB | -| `esp32p4-eth` | — / 98.2-102 | — / 33089KB-33103KB | — / 376KB | -| `esp32s3-n16r8` | — / 29.5-35.6 | — / 8202KB-8205KB | — / 108KB-112KB | -| `pc-macos` | — / 5,263-16,129 | — / unlimited | — / unlimited | +| `esp32` | — / 29.8-35.1 | — / 36KB-47KB | — / 24KB-26KB | +| `esp32p4-eth` | — / 86.3-102 | — / 33089KB-33105KB | — / 376KB | +| `esp32s3-n16r8` | — / 16.8-35.6 | — / 8202KB-8212KB | — / 92KB-112KB | +| `pc-macos` | — / 5,128-16,129 | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-25 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-25 - `pc-macos`: observed 2026-06-17 → 2026-06-25 ### scenario_perf_light @@ -817,14 +880,14 @@ Bare minimum: Grid(16²) + Layer + Checkerboard (light effect). No modifier, no | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 6,173-8,772 | — / 125KB-131KB | — / 108KB | -| `esp32p4-eth` | — / 13,699-18,519 | — / 33228KB-33244KB | — / 376KB | -| `esp32s3-n16r8` | — / 6,711-8,850 | — / 8316KB-8339KB | — / 80KB-104KB | +| `esp32` | — / 6,173-8,850 | — / 125KB-147KB | — / 108KB | +| `esp32p4-eth` | — / 13,699-18,519 | — / 33228KB-33246KB | — / 376KB | +| `esp32s3-n16r8` | — / 5,814-8,850 | — / 8316KB-8347KB | — / 80KB-104KB | | `pc-macos` | — / 1,000,000-— | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-25 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-25 - `pc-macos`: observed 2026-06-17 → 2026-06-24 #### `measure-with-modifier` (measure) 📏 @@ -838,14 +901,14 @@ Cost of the modifier + LUT over the minimal pipeline. Heap delta vs measure-mini | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 3,077-3,289 | — / 131KB-135KB | — / 108KB | -| `esp32p4-eth` | — / 9,615-10,204 | — / 33226KB-33242KB | — / 376KB | -| `esp32s3-n16r8` | — / 3,922-4,032 | — / 8330KB-8335KB | — / 96KB-100KB | +| `esp32` | — / 3,077-9,709 | — / 131KB-147KB | — / 108KB | +| `esp32p4-eth` | — / 8,621-10,309 | — / 33226KB-33243KB | — / 376KB | +| `esp32s3-n16r8` | — / 3,195-4,032 | — / 8330KB-8345KB | — / 92KB-100KB | | `pc-macos` | — / — | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-25 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-25 - `pc-macos`: observed 2026-06-17 #### `measure-with-preview` (measure) 📏 @@ -856,14 +919,14 @@ PreviewDriver is the pre-wired apparatus — it survives clear_children and is a | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 3,247-3,289 | — / 132KB-133KB | — / 108KB | -| `esp32p4-eth` | — / 10,526-10,753 | — / 33226KB-33241KB | — / 376KB | -| `esp32s3-n16r8` | — / 4,115-4,202 | — / 8330KB-8334KB | — / 96KB-100KB | +| `esp32` | — / 3,067-9,804 | — / 132KB-146KB | — / 108KB | +| `esp32p4-eth` | — / 10,417-10,753 | — / 33226KB-33243KB | — / 376KB | +| `esp32s3-n16r8` | — / 3,802-4,274 | — / 8330KB-8345KB | — / 84KB-100KB | | `pc-macos` | — / — | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-25 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-25 - `pc-macos`: observed 2026-06-17 #### `measure-heavy-16` (measure) 📏 @@ -875,14 +938,14 @@ PreviewDriver is the pre-wired apparatus — it survives clear_children and is a | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 1,905-3,268 | — / 131KB | — / 108KB | -| `esp32p4-eth` | — / 5,556-6,494 | — / 33224KB-33241KB | — / 376KB | -| `esp32s3-n16r8` | — / 2,463-2,506 | — / 8332KB-8333KB | — / 88KB-100KB | +| `esp32` | — / 1,142-3,268 | — / 131KB-146KB | — / 108KB | +| `esp32p4-eth` | — / 5,556-6,494 | — / 33224KB-33243KB | — / 376KB | +| `esp32s3-n16r8` | — / 2,299-2,506 | — / 8332KB-8342KB | — / 88KB-100KB | | `pc-macos` | — / 333,333-1,000,000 | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-25 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-25 - `pc-macos`: observed 2026-06-17 → 2026-06-25 #### `measure-heavy-32` (measure) 📏 @@ -895,14 +958,14 @@ PreviewDriver is the pre-wired apparatus — it survives clear_children and is a | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 539-826 | — / 130KB | — / 108KB | -| `esp32p4-eth` | — / 1,818-1,880 | — / 33221KB-33235KB | — / 376KB | -| `esp32s3-n16r8` | — / 562-715 | — / 8330KB-8333KB | — / 100KB-104KB | +| `esp32` | — / 265-826 | — / 130KB-144KB | — / 108KB | +| `esp32p4-eth` | — / 1,603-1,880 | — / 33221KB-33237KB | — / 376KB | +| `esp32s3-n16r8` | — / 562-715 | — / 8328KB-8333KB | — / 84KB-104KB | | `pc-macos` | — / 90,909-333,333 | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-22 +- `esp32`: observed 2026-06-17 → 2026-06-25 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-25 - `pc-macos`: observed 2026-06-17 → 2026-06-25 #### `measure-heavy-64` (measure) 📏 @@ -915,15 +978,15 @@ PreviewDriver is the pre-wired apparatus — it survives clear_children and is a | Board | FPS | heap | block | |---|---|---|---| -| `esp32` | — / 151-227 | — / 111KB | — / 88KB-96KB | -| `esp32p4-eth` | — / 473-491 | — / 33195KB-33208KB | — / 376KB | -| `esp32s3-n16r8` | — / 146-157 | — / 8305KB-8307KB | — / 96KB-108KB | -| `pc-macos` | — / 22,727-71,429 | — / unlimited | — / unlimited | +| `esp32` | — / 77.1-227 | — / 111KB-135KB | — / 88KB-108KB | +| `esp32p4-eth` | — / 411-491 | — / 33195KB-33210KB | — / 376KB | +| `esp32s3-n16r8` | — / 129-162 | — / 8302KB-8317KB | — / 92KB-108KB | +| `pc-macos` | — / 20,000-71,429 | — / unlimited | — / unlimited | -- `esp32`: observed 2026-06-17 -- `esp32p4-eth`: observed 2026-06-17 → 2026-06-22 -- `esp32s3-n16r8`: observed 2026-06-17 -- `pc-macos`: observed 2026-06-17 → 2026-06-25 +- `esp32`: observed 2026-06-17 → 2026-06-25 +- `esp32p4-eth`: observed 2026-06-17 → 2026-06-25 +- `esp32s3-n16r8`: observed 2026-06-17 → 2026-06-25 +- `pc-macos`: observed 2026-06-17 → 2026-06-26 ## Layers @@ -955,7 +1018,7 @@ Add NetworkSendDriver and run the bounded FPS measurement over the two-layer com | Board | FPS | heap | block | |---|---|---|---| -| `pc-macos` | — / 6,135-10,417 | — / unlimited | — / unlimited | +| `pc-macos` | — / 6,135-18,519 | — / unlimited | — / unlimited | - `pc-macos`: observed 2026-06-25 @@ -1052,6 +1115,142 @@ Pipeline renders with the single remaining grid, same as the baseline. - `pc-macos`: observed 2026-06-05 - `pc-windows`: observed 2026-06-07 +## MoonLiveEffect + +### scenario_MoonLiveEffect_livescript + +`test/scenarios/light/scenario_MoonLiveEffect_livescript.json` — Exercise a scripted MoonLiveEffect as a wired MoonModule end-to-end — the integration layer the unit tests can't reach. The effect compiles its `source` text to native code on-device and renders it into the Layer buffer each tick. Prepares its own canvas: Layout(Grid 16x16) + Layer + MoonLiveEffect, measures the default compile, then edits `source` live (a new fill colour recompiles and keeps rendering), pushes a BROKEN script (compile fails, the previous code is freed, the effect renders dark and the parse error surfaces in status, no crash), recovers with a valid script, and finally removes + re-adds the effect (add/remove robustness in any order). A crash in the JIT/emit path, a failed recompile that wedges the tick, or a buffer overrun on an odd grid all show up as a failed measure. The compiler + emit golden bytes are pinned by unit_moonlive_compiler / unit_moonlive_fill; this is the live wired-module gate. + +**Mode**: `mutate` · **Also touches**: Layouts, GridLayout, Layers, Layer, Drivers, NetworkSendDriver + +#### `add-moonlive` (add_module) 📏 + +Add a MoonLiveEffect to the Layer. Its default source `fill(0, 0, 255);` compiles on-device to native code; measure that the wired effect renders. + +**Performance** (contract / observed) — tick stored, FPS shown: + +| Board | FPS | heap | block | +|---|---|---|---| +| `esp32p4-eth` | — / 88.6 | — / 33211KB | — / 376KB | +| `esp32s3-n16r8` | — / 249 | — / 8341KB | — / 104KB | +| `pc-macos` | — / 2,545-— | — / unlimited | — / unlimited | + +- `esp32p4-eth`: observed 2026-06-27 +- `esp32s3-n16r8`: observed 2026-06-27 +- `pc-macos`: observed 2026-06-26 → 2026-06-27 + +#### `edit-source-red` (set_control) 📏 + +Live-edit the script source to a new colour. A source edit triggers a recompile (controlChangeTriggersBuildState gates on `source`); the new native code swaps in and keeps rendering. + +**Performance** (contract / observed) — tick stored, FPS shown: + +| Board | FPS | heap | block | +|---|---|---|---| +| `esp32p4-eth` | — / 98.4 | — / 33213KB | — / 376KB | +| `esp32s3-n16r8` | — / 225 | — / 8341KB | — / 104KB | +| `pc-macos` | — / 2,513-— | — / unlimited | — / unlimited | + +- `esp32p4-eth`: observed 2026-06-27 +- `esp32s3-n16r8`: observed 2026-06-27 +- `pc-macos`: observed 2026-06-26 → 2026-06-27 + +#### `edit-source-broken` (set_control) 📏 + +Push a script that fails to parse. The compile fails, the engine reports the diagnostic in the module status and renders dark, but the device keeps running (robust, no reboot) — the script-editor failure path. The measure passes because the pipeline still ticks. + +**Performance** (contract / observed) — tick stored, FPS shown: + +| Board | FPS | heap | block | +|---|---|---|---| +| `esp32p4-eth` | — / 94.6 | — / 33209KB | — / 376KB | +| `esp32s3-n16r8` | — / 229 | — / 8340KB | — / 104KB | +| `pc-macos` | — / 3,745-— | — / unlimited | — / unlimited | + +- `esp32p4-eth`: observed 2026-06-27 +- `esp32s3-n16r8`: observed 2026-06-27 +- `pc-macos`: observed 2026-06-26 → 2026-06-27 + +#### `edit-source-recover` (set_control) 📏 + +Push a valid script again. The engine recompiles cleanly and rendering resumes — a broken edit is fully recoverable. + +**Performance** (contract / observed) — tick stored, FPS shown: + +| Board | FPS | heap | block | +|---|---|---|---| +| `esp32p4-eth` | — / 93.4 | — / 33212KB | — / 376KB | +| `esp32s3-n16r8` | — / 248 | — / 8340KB | — / 100KB | +| `pc-macos` | — / 2,415-— | — / unlimited | — / unlimited | + +- `esp32p4-eth`: observed 2026-06-27 +- `esp32s3-n16r8`: observed 2026-06-27 +- `pc-macos`: observed 2026-06-26 → 2026-06-27 + +#### `shrink-grid-1x1` (set_control) 📏 + +Resize the canvas to 1x1 while the scripted effect renders — the smallest non-empty grid. The native fill loops over a single light; the run guards (non-null buffer, cpl>=3) keep it in-bounds. Pins the 'runs at every grid size' hard rule for the JIT'd routine. + +**Performance** (contract / observed) — tick stored, FPS shown: + +| Board | FPS | heap | block | +|---|---|---|---| +| `esp32p4-eth` | — / 862 | — / 33215KB | — / 376KB | +| `esp32s3-n16r8` | — / 868 | — / 8341KB | — / 100KB | +| `pc-macos` | — / 1,000,000-— | — / unlimited | — / unlimited | + +- `esp32p4-eth`: observed 2026-06-27 +- `esp32s3-n16r8`: observed 2026-06-27 +- `pc-macos`: observed 2026-06-26 → 2026-06-27 + +#### `grow-grid-back` (set_control) 📏 + +Resize back to a wider grid; the effect keeps rendering across the live dimension change (the no-reboot reconfiguration contract applied to scripted code). + +**Performance** (contract / observed) — tick stored, FPS shown: + +| Board | FPS | heap | block | +|---|---|---|---| +| `esp32p4-eth` | — / 97.0 | — / 33209KB | — / 376KB | +| `esp32s3-n16r8` | — / 136 | — / 8333KB | — / 100KB | +| `pc-macos` | — / 71,429-— | — / unlimited | — / unlimited | + +- `esp32p4-eth`: observed 2026-06-27 +- `esp32s3-n16r8`: observed 2026-06-27 +- `pc-macos`: observed 2026-06-26 → 2026-06-27 + +#### `remove-moonlive` (remove_module) 📏 + +Remove the scripted effect. teardown frees the exec block; the Layer keeps rendering (now empty). Measures add/remove robustness. + +**Performance** (contract / observed) — tick stored, FPS shown: + +| Board | FPS | heap | block | +|---|---|---|---| +| `esp32p4-eth` | — / 88.2 | — / 33209KB | — / 376KB | +| `esp32s3-n16r8` | — / 135 | — / 8333KB | — / 100KB | +| `pc-macos` | — / 100,000-— | — / unlimited | — / unlimited | + +- `esp32p4-eth`: observed 2026-06-27 +- `esp32s3-n16r8`: observed 2026-06-27 +- `pc-macos`: observed 2026-06-26 → 2026-06-27 + +#### `re-add-moonlive` (add_module) 📏 + +Re-add a MoonLiveEffect after removal — the exec memory is re-acquired fresh, no leak, no stale pointer. The scripted effect renders again. + +**Performance** (contract / observed) — tick stored, FPS shown: + +| Board | FPS | heap | block | +|---|---|---|---| +| `esp32p4-eth` | — / 90.9 | — / 33209KB | — / 376KB | +| `esp32s3-n16r8` | — / 121 | — / 8332KB | — / 100KB | +| `pc-macos` | — / 62,500-— | — / unlimited | — / unlimited | + +- `esp32p4-eth`: observed 2026-06-27 +- `esp32s3-n16r8`: observed 2026-06-27 +- `pc-macos`: observed 2026-06-26 → 2026-06-27 + ## MoonModule ### scenario_MoonModule_control_change diff --git a/esp32/main/CMakeLists.txt b/esp32/main/CMakeLists.txt index f585aba..f2fa29b 100644 --- a/esp32/main/CMakeLists.txt +++ b/esp32/main/CMakeLists.txt @@ -10,6 +10,10 @@ idf_component_register( "../../src/core/moonlive/MoonLiveCompiler.cpp" "../../src/platform/esp32/platform_esp32.cpp" "../../src/platform/esp32/moonlive_emit.cpp" + "../../src/platform/esp32/moonlive_asm_xtensa.cpp" + "../../src/platform/esp32/moonlive_lower_xtensa.cpp" + "../../src/platform/esp32/moonlive_asm_riscv.cpp" + "../../src/platform/esp32/moonlive_lower_riscv.cpp" "../../src/platform/esp32/platform_esp32_fs.cpp" "../../src/platform/esp32/platform_esp32_ota.cpp" "../../src/platform/esp32/platform_esp32_httpget.cpp" diff --git a/src/core/moonlive/MoonLive.cpp b/src/core/moonlive/MoonLive.cpp index 26b1c23..7a46198 100644 --- a/src/core/moonlive/MoonLive.cpp +++ b/src/core/moonlive/MoonLive.cpp @@ -4,10 +4,13 @@ namespace mm::moonlive { -// Fixed cap for an emitted routine — a fill loop is a few dozen bytes on any ISA; the emitter -// returns the real length and the unused tail is harmless. Sized once here, word-aligned so -// allocExec/writeExec's word-rounding never exceeds it. -static constexpr size_t kCodeCap = 256; +// Fixed cap for an emitted routine. Sized for the heaviest realistic single statement — a +// setRGB with all four arguments a host call (4 × a full register-save call sequence, ~140 +// bytes each on RISC-V, the bulkiest ISA, plus the inline store). The emitter returns the real +// length and the unused tail is harmless; exec memory is cheap, so we size for the worst case +// rather than grow per script. Word-aligned so allocExec/writeExec's word-rounding never +// exceeds it. +static constexpr size_t kCodeCap = 768; // Copy `len` already-emitted bytes into a fresh exec block. writeExec hides the ISA quirks // (IRAM's 32-bit-store-only rule, the I-cache sync), so the engine stays target-agnostic. @@ -34,9 +37,9 @@ bool MoonLive::compile(uint8_t r, uint8_t g, uint8_t b) { return true; } -bool MoonLive::compile(const char* source) { +bool MoonLive::compile(const char* source, const BuiltinTable& table) { uint8_t staging[kCodeCap]; - CompileResult cr = compileSource(source, staging, kCodeCap); + CompileResult cr = compileSource(source, table, staging, kCodeCap); if (!cr.ok) { free(); error_ = cr.error; return false; } // surface the parse diagnostic void* block = place(staging, cr.len); if (!block) return false; diff --git a/src/core/moonlive/MoonLive.h b/src/core/moonlive/MoonLive.h index 52b0786..0c462d1 100644 --- a/src/core/moonlive/MoonLive.h +++ b/src/core/moonlive/MoonLive.h @@ -3,6 +3,7 @@ #include #include #include "core/moonlive/moonlive_emit.h" +#include "core/moonlive/MoonLiveBuiltins.h" // MoonLive — the live-script engine core (domain-neutral, §3.1/§3.9 of // livescripts-analysis-top-down.md). compile() turns a program into native code — either a @@ -31,10 +32,11 @@ class MoonLive { // leaves the engine !ok() with an error() — the caller degrades, never crashes. bool compile(uint8_t r, uint8_t g, uint8_t b); - // Compile SOURCE TEXT. The front-end (MoonLiveCompiler) lexes/parses the `fill(r,g,b);` - // statement and emits the same code compile(r,g,b) would. A parse error leaves the engine + // Compile SOURCE TEXT, resolving function calls against `table` (the host's registered + // built-ins — see MoonLiveBuiltins.h). The front-end parses an expression-call statement + // and lowers it through the IR + per-ISA assembler. A parse/codegen error leaves the engine // !ok() with error() pointing at the diagnostic — the script editor's failure path. - bool compile(const char* source); + bool compile(const char* source, const BuiltinTable& table); // Compile the animated routine (colour derived from the per-frame `t`). bool compileAnimated(); diff --git a/src/core/moonlive/MoonLiveBuiltins.h b/src/core/moonlive/MoonLiveBuiltins.h new file mode 100644 index 0000000..819d8e9 --- /dev/null +++ b/src/core/moonlive/MoonLiveBuiltins.h @@ -0,0 +1,68 @@ +#pragma once + +#include +#include + +// MoonLive built-in table — the neutral seam by which a HOST registers the functions a script +// may call (the ESPLiveScript `arti_external_function` / ARTI / doc §3.4 model). The core +// compiler knows only *that a name maps to a descriptor*; it owns no function names and no +// domain semantics. The light domain (or any other host) populates the table with its own +// vocabulary — setRGB/fill/random16 for LEDs, something else for a display or a sensor. +// +// A descriptor says how a call lowers: +// - Call — a pure host helper: lower to a generic call to `fn` (a C function pointer), +// one argument in, one result out. (random16, later sin/cos/hsvToRgb…) +// - Inline — a routine the backend emits inline (no per-call overhead — the hot-path +// writers): the descriptor carries an `inlineOp` TAG, a neutral opcode the +// per-ISA lowering knows how to emit. The core never interprets the tag; it just +// threads it through. The light domain decides which names map to which tags. +// +// This is what keeps the core domain-neutral while the hot path stays inline: the *name* +// "setRGB" and its RGB meaning live only in the host's registration; the core sees a tag. + +namespace mm::moonlive { + +// Neutral inline opcodes — "store shapes a backend can emit", not "LED operations". A host +// maps its function names onto these; a backend implements them. WriteRGB = store 3 bytes at a +// computed address; FillRGB = a counted loop writing 3 bytes per element. Named for their one +// concrete host today, but the core treats them as opaque tags. +enum class InlineOp : uint8_t { + WriteRGB, // operands: addrBaseVReg (buf), indexVReg, v0, v1, v2 → store 3 bytes at index + FillRGB, // operands: bufVReg, countVReg, cplVReg, v0, v1, v2 → loop store-3 over count +}; + +enum class BuiltinKind : uint8_t { Call, Inline }; + +struct Builtin { + const char* name = nullptr; // the script-visible name (host-owned) + uint8_t argc = 0; // number of arguments + bool returns = false; // Call: produces a value (an expression) vs a void statement + BuiltinKind kind = BuiltinKind::Call; + const void* fn = nullptr; // Call: the host C function pointer + InlineOp inlineOp{}; // Inline: the neutral opcode tag +}; + +// A fixed-capacity table the host fills and the compiler reads. No heap; a host registers a +// handful of functions. Lookup is by name (linear — the table is tiny). +struct BuiltinTable { + static constexpr uint8_t kMax = 16; + Builtin items[kMax]; + uint8_t count = 0; + + bool add(const Builtin& b) { + if (count >= kMax) return false; + items[count++] = b; + return true; + } + const Builtin* find(const char* name, size_t len) const { + for (uint8_t i = 0; i < count; i++) { + const char* n = items[i].name; + size_t j = 0; + for (; j < len && n[j]; j++) if (n[j] != name[j]) break; + if (j == len && n[j] == 0) return &items[i]; + } + return nullptr; + } +}; + +} // namespace mm::moonlive diff --git a/src/core/moonlive/MoonLiveCompiler.cpp b/src/core/moonlive/MoonLiveCompiler.cpp index 173315e..a6c06ab 100644 --- a/src/core/moonlive/MoonLiveCompiler.cpp +++ b/src/core/moonlive/MoonLiveCompiler.cpp @@ -1,24 +1,22 @@ #include "core/moonlive/MoonLiveCompiler.h" #include "core/moonlive/moonlive_emit.h" +#include "core/moonlive/MoonLiveIr.h" namespace mm::moonlive { namespace { -// --- Lexer --------------------------------------------------------------------------- -// The token kinds the grammar needs. A hand-written recursive-descent front-end (the textbook -// embedded-script default) lexes on demand: the parser pulls one token at a time, and -// advance() walks the source directly without building a token list. +// --- Lexer (unchanged shape) --------------------------------------------------------- enum class Tok { Ident, Number, LParen, RParen, Comma, Semicolon, End, Error }; struct Lexer { - const char* p; // cursor into the source + const char* p; Tok kind = Tok::Error; - long number = 0; // value when kind==Number + long number = 0; const char* identBeg = nullptr; - size_t identLen = 0; // span when kind==Ident - const char* tokBeg = nullptr; // start of the current token (for error columns) - const char* srcBeg; // start of source (for 1-based column math) + size_t identLen = 0; + const char* tokBeg = nullptr; + const char* srcBeg; const char* err = ""; explicit Lexer(const char* s) : p(s), srcBeg(s) { advance(); } @@ -27,7 +25,6 @@ struct Lexer { static bool isDigit(char c) { return c >= '0' && c <= '9'; } static bool isIdentStart(char c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; } static bool isIdentCont(char c) { return isIdentStart(c) || isDigit(c); } - uint16_t col() const { return static_cast((tokBeg - srcBeg) + 1); } void advance() { @@ -52,79 +49,141 @@ struct Lexer { } kind = Tok::Error; err = "unexpected character"; } - - bool identIs(const char* word) const { - if (kind != Tok::Ident) return false; - for (size_t i = 0; i < identLen; i++) if (identBeg[i] != word[i] || word[i] == 0) return false; - return word[identLen] == 0; - } }; -// --- AST + Parser -------------------------------------------------------------------- -// The only production: program := "fill" "(" number "," number "," number ")" ";" End. -// The AST for it is just the three colour bytes — a single statement needs no node hierarchy. -struct Parsed { - bool ok = false; - const char* error = ""; - uint16_t errorCol = 0; - uint8_t r = 0, g = 0, b = 0; -}; +// --- Parser → IR --------------------------------------------------------------------- +// A recursive-descent parser that evaluates each expression into a virtual register and emits +// IR. It knows the GRAMMAR and resolves call names against the injected table; it owns no +// function names. Buffer writers (Kind::Inline) and helpers (Kind::Call) are dispatched +// generically. +struct Parser { + Lexer& lex; + const BuiltinTable& table; + IrProgram& ir; + VReg nextTemp = kFirstTemp; // high-water mark — also IrProgram.vregsUsed + VReg freeStack[kMaxVRegs] = {}; // recycled temps (LIFO), so a dead vreg is reused + uint8_t freeCount = 0; + const char* error = ""; + uint16_t errorCol = 0; + bool failed = false; + + void fail(const char* msg) { if (!failed) { failed = true; error = msg; errorCol = lex.col(); } } + + // A stack temp allocator: alloc() hands out a recycled vreg if one is free, else a fresh one; + // free() returns a temp to the pool once its value has been consumed. This is what keeps a + // multi-call statement (e.g. setRGB(random16(..), random16(..), random16(..), 0)) within the + // small device register file — each call's argument temp dies and is reused for the next, + // instead of the count growing without bound. The textbook tree-walk register stack. + VReg alloc() { + if (freeCount) return freeStack[--freeCount]; + return (nextTemp < kMaxVRegs) ? nextTemp++ : VReg(kMaxVRegs - 1); + } + void freeTemp(VReg v) { if (v >= kFirstTemp && freeCount < kMaxVRegs) freeStack[freeCount++] = v; } -// Pull a 0..255 number token; fail with a diagnostic otherwise. -bool expectByte(Lexer& lex, uint8_t& out, Parsed& res) { - if (lex.kind == Tok::Error) { res.error = lex.err; res.errorCol = lex.col(); return false; } - if (lex.kind != Tok::Number) { res.error = "expected a number"; res.errorCol = lex.col(); return false; } - if (lex.number < 0 || lex.number > 255) { res.error = "colour value out of range (0..255)"; res.errorCol = lex.col(); return false; } - out = static_cast(lex.number); - lex.advance(); - return true; -} + bool expect(Tok t, const char* msg) { + if (lex.kind != t) { fail(msg); return false; } + lex.advance(); + return true; + } -bool expect(Lexer& lex, Tok t, const char* msg, Parsed& res) { - if (lex.kind != t) { res.error = msg; res.errorCol = lex.col(); return false; } - lex.advance(); - return true; -} + // expr := number | call. Returns the vreg holding the value (or 0 on failure). + VReg parseExpr() { + if (failed) return 0; + if (lex.kind == Tok::Number) { + if (lex.number < 0 || lex.number > 65535) { fail("number out of range (0..65535)"); return 0; } + VReg v = alloc(); + ir.push({IrOp::Const, v, 0,0,0,0, static_cast(lex.number), nullptr, {}}); + lex.advance(); + return v; + } + if (lex.kind == Tok::Ident) { + VReg out = 0; + parseCall(&out); // a call used as an expression must return a value + return out; + } + fail("expected a number or a function call"); + return 0; + } -Parsed parse(const char* source) { - Parsed res; - Lexer lex(source); + // call := ident "(" [expr {"," expr}] ")". If `resultOut` is non-null the call is used as + // an expression and must return a value; the result vreg is written there. If null it is a + // statement (void). + void parseCall(VReg* resultOut) { + if (lex.kind != Tok::Ident) { fail("expected a function name"); return; } + const Builtin* fn = table.find(lex.identBeg, lex.identLen); + if (!fn) { fail("unknown function"); return; } + lex.advance(); + if (!expect(Tok::LParen, "expected '(' after the function name")) return; + + // Evaluate each argument expression into a vreg. + VReg args[4] = {0,0,0,0}; + uint8_t n = 0; + if (lex.kind != Tok::RParen) { + while (true) { + if (n >= fn->argc || n >= 4) { fail("too many arguments"); return; } + args[n++] = parseExpr(); + if (failed) return; + if (lex.kind == Tok::Comma) { lex.advance(); continue; } + break; + } + } + if (n != fn->argc) { fail("wrong number of arguments"); return; } + if (!expect(Tok::RParen, "expected ')'")) return; + + if (resultOut) { + if (fn->kind != BuiltinKind::Call || !fn->returns) { fail("this function does not return a value"); return; } + // The argument temps are consumed by the call; free them, then allocate the result + // (which may reuse one of them) — this is what bounds the register count across a + // chain of calls. The IR Call reads its arg before the result is written, so reuse is + // safe even when result == arg. + for (uint8_t i = 0; i < n; i++) freeTemp(args[i]); + VReg r = alloc(); + ir.push({IrOp::Call, r, args[0], 0,0,0, 0, fn->fn, {}}); + *resultOut = r; + } else { + // A statement call. Call kinds with a result are also allowed as statements (result + // discarded); Inline kinds emit the inline op with their operands. + if (fn->kind == BuiltinKind::Call) { + for (uint8_t i = 0; i < n; i++) freeTemp(args[i]); + VReg r = alloc(); + ir.push({IrOp::Call, r, args[0], 0,0,0, 0, fn->fn, {}}); + freeTemp(r); + } else { + // Inline op: hand the operand vregs to the backend via an Inline IR op. The + // operand mapping per inline op is the backend's contract (documented there). + ir.push({IrOp::Inline, 0, args[0], args[1], args[2], args[3], 0, nullptr, fn->inlineOp}); + for (uint8_t i = 0; i < n; i++) freeTemp(args[i]); + } + } + } - if (!lex.identIs("fill")) { - res.error = (lex.kind == Tok::End) ? "empty program (expected fill(...))" : "expected 'fill'"; - res.errorCol = lex.col(); - return res; + bool parseProgram() { + if (lex.kind != Tok::Ident) { + fail(lex.kind == Tok::End ? "empty program" : "expected a function call"); + return false; + } + parseCall(nullptr); // a statement + if (failed) return false; + if (!expect(Tok::Semicolon, "expected ';'")) return false; + if (lex.kind != Tok::End) { fail("unexpected tokens after the statement"); return false; } + return true; } - lex.advance(); - if (!expect(lex, Tok::LParen, "expected '(' after fill", res)) return res; - if (!expectByte(lex, res.r, res)) return res; - if (!expect(lex, Tok::Comma, "expected ',' between colour values", res)) return res; - if (!expectByte(lex, res.g, res)) return res; - if (!expect(lex, Tok::Comma, "expected ',' between colour values", res)) return res; - if (!expectByte(lex, res.b, res)) return res; - if (!expect(lex, Tok::RParen, "expected ')' to close fill(...)", res)) return res; - if (!expect(lex, Tok::Semicolon, "expected ';' after fill(...)", res)) return res; - if (lex.kind != Tok::End) { res.error = "unexpected tokens after fill(...)"; res.errorCol = lex.col(); return res; } - - res.ok = true; - return res; -} +}; } // namespace -CompileResult compileSource(const char* source, uint8_t* out, size_t cap) { +CompileResult compileSource(const char* source, const BuiltinTable& table, uint8_t* out, size_t cap) { CompileResult r; if (!source) { r.error = "no source"; return r; } - if (!out || cap == 0) { r.error = "no code buffer"; return r; } // never emit through a null/empty buffer + if (!out || cap == 0) { r.error = "no code buffer"; return r; } - Parsed p = parse(source); - if (!p.ok) { r.error = p.error; r.errorCol = p.errorCol; return r; } + Lexer lex(source); + IrProgram ir; + Parser parser{lex, table, ir}; + if (!parser.parseProgram()) { r.error = parser.error; r.errorCol = parser.errorCol; return r; } - // Codegen: the parsed colour drives the SAME per-ISA emitter the typed compile path uses — - // so a parsed `fill(0,0,255)` produces byte-for-byte the bytes emitFill(0,0,255) does (the - // golden-bytes equivalence the test asserts). - size_t len = emitFill(out, cap, p.r, p.g, p.b); - if (len == 0) { r.error = "code buffer too small"; return r; } + size_t len = lowerToBytes(ir, out, cap); + if (len == 0) { r.error = "codegen failed (unsupported on this target, or too large)"; return r; } r.ok = true; r.len = len; return r; diff --git a/src/core/moonlive/MoonLiveCompiler.h b/src/core/moonlive/MoonLiveCompiler.h index 716b3f0..e384d9b 100644 --- a/src/core/moonlive/MoonLiveCompiler.h +++ b/src/core/moonlive/MoonLiveCompiler.h @@ -2,34 +2,34 @@ #include #include +#include "core/moonlive/MoonLiveBuiltins.h" -// MoonLive front-end (§3.2) — the platform-independent compiler: source text → tokens → -// AST → native code (via the per-ISA emitter). It recognises one statement, +// MoonLive front-end (§3.2) — the platform-independent compiler: source text → tokens → AST → +// IR → native code (via the per-ISA assembler). The grammar is a single statement that is a +// call to a host-registered function, with EXPRESSION arguments: // -// fill(, , ); +// stmt := call ";" +// call := ident "(" [expr {"," expr}] ")" +// expr := number | call // any argument may be a nested call, e.g. random16(256) // -// three integer 0..255 args, and emits the SAME machine code the hand-written emitFill -// produces (the golden-bytes equivalence is the no-language-leak proof). -// -// Neutral by construction: the compiler knows the LANGUAGE, never an ISA — codegen calls -// the platform's emitFill, so a different backend changes nothing here. +// Neutral by construction: the compiler knows the LANGUAGE and resolves call names against the +// injected BuiltinTable — it owns no function names and no domain (LED) semantics. setRGB/fill/ +// random16 live in the host's table; the compiler emits a generic Call (for Kind::Call) or a +// generic Inline op (for Kind::Inline) and lets the backend lower it. namespace mm::moonlive { -// Result of compiling source: on success, ok==true and the emitted bytes are in out[0..len). -// On failure, ok==false and error points at a static, human-readable diagnostic (the column -// is 1-based, 0 if not applicable). out is filled only on success. +// Result of compiling source: on success, ok==true and the bytes are in out[0..len). On +// failure, ok==false and error points at a static diagnostic (1-based column, 0 if n/a). struct CompileResult { bool ok = false; const char* error = ""; uint16_t errorCol = 0; - size_t len = 0; // bytes written to out on success + size_t len = 0; }; -// Compile `source` into machine code in `out` (capacity `cap`). Pure: no I/O, no allocation -// beyond the caller's buffer — the same input always yields the same bytes, so it unit-tests -// trivially (and the result is asserted byte-for-byte against emitFill). The emitted code is -// the per-ISA fill routine for the parsed colour. -CompileResult compileSource(const char* source, uint8_t* out, size_t cap); +// Compile `source` to machine code in `out` (capacity `cap`), resolving calls against `table`. +// Pure: no I/O, no allocation beyond the caller's buffer. +CompileResult compileSource(const char* source, const BuiltinTable& table, uint8_t* out, size_t cap); } // namespace mm::moonlive diff --git a/src/core/moonlive/MoonLiveIr.h b/src/core/moonlive/MoonLiveIr.h new file mode 100644 index 0000000..93a6ed2 --- /dev/null +++ b/src/core/moonlive/MoonLiveIr.h @@ -0,0 +1,69 @@ +#pragma once + +#include +#include +#include "core/moonlive/MoonLiveBuiltins.h" // InlineOp (a neutral opcode tag) + +// MoonLive IR — the typed intermediate representation between the front-end and the per-ISA +// assembler (§3.2 of livescripts-analysis-top-down.md). The front-end lowers an AST to a flat +// list of three-address ops over virtual registers; each per-ISA backend lowers that same IR +// to machine bytes. The IR is the seam: it knows the *operations*, never the *ISA* and never a +// domain (no LEDs). Buffer writes are not a special IR op — they are `Inline` ops carrying a +// neutral opcode tag the HOST registered (see MoonLiveBuiltins.h); the core just threads the +// tag to the backend. +// +// It is a compile-time data structure — consumed during lowering, never present at run time. +// A fixed-capacity op list (no heap in the build path); a single statement is a handful of ops. +// +// Virtual registers are plain indices v0..v(kMaxVRegs-1). A backend maps them to machine +// registers. The named host arguments arrive in fixed vregs (kArg…) so the front-end refers to +// them without knowing the ABI. They are named neutrally — kArg0..kArg3 — and a host assigns +// meaning (for the light host: buf, nLights, cpl, t). + +namespace mm::moonlive { + +using VReg = uint8_t; + +enum : VReg { kArg0 = 0, kArg1 = 1, kArg2 = 2, kArg3 = 3, kFirstTemp = 4 }; + +static constexpr uint8_t kMaxVRegs = 16; // a statement uses a handful; no allocator yet +static constexpr uint8_t kMaxIrOps = 64; // a statement is a handful of ops; fixed, no heap + +// The op set — neutral. Three-address form: dst plus up to three source operands. +enum class IrOp : uint8_t { + Const, // dst = imm + Add, // dst = a + b + AddImm, // dst = a + imm + Mul, // dst = a * b + Loop, // counted loop: counter=a, limit=b; body runs until LoopEnd + LoopEnd, // end of the innermost Loop body + Bounds, // guard: if a >= b, skip ops until BoundsEnd (the §4 out-of-range check) + BoundsEnd, // end of the innermost Bounds-guarded block + Call, // dst = (*callFn)(a) — call a host-registered function (callFn = the C fn ptr) + Inline, // a host-registered inline op (inlineOp tag); operands a/b/c/d (op-specific) +}; + +struct IrInst { + IrOp op; + VReg dst = 0; + VReg a = 0, b = 0, c = 0, d = 0; // source vregs (op-dependent) + int32_t imm = 0; // immediate (Const) / addr offset + const void* callFn = nullptr; // Call: the host C function pointer + InlineOp inlineOp{}; // Inline: the neutral opcode tag +}; + +// A lowered program: a fixed list of ops plus the vreg high-water mark. +struct IrProgram { + IrInst ops[kMaxIrOps]; + uint8_t count = 0; + VReg vregsUsed = kFirstTemp; + + bool push(const IrInst& i) { + if (count >= kMaxIrOps) return false; + ops[count++] = i; + if (i.dst + 1 > vregsUsed) vregsUsed = static_cast(i.dst + 1); + return true; + } +}; + +} // namespace mm::moonlive diff --git a/src/core/moonlive/moonlive_emit.h b/src/core/moonlive/moonlive_emit.h index eea7d8a..d96a411 100644 --- a/src/core/moonlive/moonlive_emit.h +++ b/src/core/moonlive/moonlive_emit.h @@ -37,4 +37,12 @@ size_t emitFill(uint8_t* out, size_t cap, uint8_t r, uint8_t g, uint8_t b); // (the grid's red ramps over time). Same emit/exec/call path as emitFill, one extra arg. size_t emitAnimatedFill(uint8_t* out, size_t cap); +struct IrProgram; // src/core/moonlive/MoonLiveIr.h + +// Lower a typed IR program to machine code for this TU's ISA, via the per-ISA assembler. +// This is the general codegen path the front-end uses; emitFill/emitAnimatedFill are the +// hand-encoded references the assembler-built output is behaviorally checked against. Returns +// the byte count, or 0 on overflow / cap too small (the caller degrades). +size_t lowerToBytes(const IrProgram& ir, uint8_t* out, size_t cap); + } // namespace mm::moonlive diff --git a/src/light/moonlive/MoonLiveBuiltins_light.h b/src/light/moonlive/MoonLiveBuiltins_light.h new file mode 100644 index 0000000..72705af --- /dev/null +++ b/src/light/moonlive/MoonLiveBuiltins_light.h @@ -0,0 +1,39 @@ +#pragma once + +#include "core/moonlive/MoonLiveBuiltins.h" + +#include + +// MoonLive — the LIGHT-DOMAIN built-in registration. This is the only place the LED vocabulary +// lives: the function NAMES (`setRGB`, `fill`, `random16`), their arg counts, and the meaning +// of the inline opcodes (WriteRGB = an RGB pixel write, FillRGB = fill every light). The core +// compiler sees only the neutral BuiltinTable / InlineOp tags this file hands it. A different +// host (display, sensor) would write its own registration with its own names; the core is +// unchanged. (The ESPLiveScript / ARTI bound-function model, doc §3.4.) + +namespace mm::moonlive { + +// random16(n) → a pseudo-random value in [0, n). A simple LCG, deterministic enough that the +// runtime Bounds guard always sees an in-range index; the same implementation on every target +// so a script behaves identically. The one host helper exposed as a Call so far. +extern "C" inline uint32_t mm_light_random16(uint32_t n) { + static uint32_t s = 0x2545F491u; + s = s * 1664525u + 1013904223u; + return n ? (s >> 16) % n : 0u; +} + +// The light-domain built-in table the binding injects into the compiler. setRGB and fill are +// Inline (they lower to stores — the hot-path writers, no per-call cost); random16 is a Call. +inline BuiltinTable lightBuiltins() { + BuiltinTable t; + // setRGB(index, r, g, b) → write one pixel (bounds-guarded). Inline op WriteRGB. + t.add({"setRGB", 4, /*returns*/ false, BuiltinKind::Inline, nullptr, InlineOp::WriteRGB}); + // fill(r, g, b) → write every light. Inline op FillRGB. + t.add({"fill", 3, false, BuiltinKind::Inline, nullptr, InlineOp::FillRGB}); + // random16(n) → a value in [0,n). A Call to the host helper. + t.add({"random16", 1, /*returns*/ true, BuiltinKind::Call, + reinterpret_cast(&mm_light_random16), {}}); + return t; +} + +} // namespace mm::moonlive diff --git a/src/light/moonlive/MoonLiveEffect.h b/src/light/moonlive/MoonLiveEffect.h index 7665bac..cf80094 100644 --- a/src/light/moonlive/MoonLiveEffect.h +++ b/src/light/moonlive/MoonLiveEffect.h @@ -2,6 +2,7 @@ #include "light/effects/EffectBase.h" #include "core/moonlive/MoonLive.h" +#include "light/moonlive/MoonLiveBuiltins_light.h" #include // MoonLiveEffect — a scripted effect rendered by the MoonLive engine (§3.3 of @@ -41,7 +42,7 @@ class MoonLiveEffect : public EffectBase { // here and recompiles, so a new script swaps in live; a broken edit just shows its // diagnostic and the layer goes dark until it's fixed. void onBuildState() override { - if (engine_.compile(source_)) { + if (engine_.compile(source_, moonlive::lightBuiltins())) { clearStatus(); } else { setStatus(engine_.error(), Severity::Error); @@ -60,7 +61,7 @@ class MoonLiveEffect : public EffectBase { private: moonlive::MoonLive engine_; - char source_[64] = "fill(0, 0, 255);"; // default script — solid blue, from parsed source + char source_[128] = "fill(0, 0, 255);"; // default script — solid blue, from parsed source }; } // namespace mm diff --git a/src/platform/desktop/moonlive_asm_host.cpp b/src/platform/desktop/moonlive_asm_host.cpp new file mode 100644 index 0000000..c0e0955 --- /dev/null +++ b/src/platform/desktop/moonlive_asm_host.cpp @@ -0,0 +1,131 @@ +#include "moonlive_asm_host.h" + +#include + +// MoonLive host assembler — arm64 and x86-64 encodings, chosen at compile time (the same +// #if-by-arch shape moonlive_emit.cpp uses). Each named instruction is encoded ONCE here; the +// IR lowering composes them. Branch displacements are resolved by patchBranches() against +// bound labels, so no offset is ever hand-computed (the crash class the spike avoided by never +// composing now stays avoided by back-patching). + +namespace mm::moonlive { + +#if defined(__aarch64__) + +// arm64 register map: R0..R3 = the host-ABI arg registers x0..x3 (buf, nLights, cpl, t); +// R4..R9 = caller-saved scratch x9..x14 (free across our leaf code). Index math uses the +// 64-bit views (xN) for addresses, 32-bit (wN) for counters/colours — but the same register +// number, so one map suffices. +// R0..R3 = x0..x3 (the host-ABI args); R4..R13 = caller-saved scratch x9..x14 then x4..x7. +// x15 is reserved for call()'s address/immediate scratch, so it's not in the pool. +static const uint8_t kArm64Reg[kRegCount] = {0, 1, 2, 3, 9, 10, 11, 12, 13, 14, 4, 5, 6, 7}; +static uint8_t mr(Reg r) { return kArm64Reg[r]; } + +Label HostAssembler::newLabel() { + if (labelCount_ == 0) for (auto& p : labelPos_) p = -1; + Label l = labelCount_++; + labelPos_[l] = -1; + return l; +} +void HostAssembler::bind(Label l) { labelPos_[l] = static_cast(len_); } + +void HostAssembler::emit32(uint32_t w) { + if (len_ + 4 > kCap) { overflow_ = true; return; } + buf_[len_++] = uint8_t(w); buf_[len_++] = uint8_t(w >> 8); + buf_[len_++] = uint8_t(w >> 16); buf_[len_++] = uint8_t(w >> 24); +} +void HostAssembler::emitBytes(const uint8_t* p, size_t n) { + if (len_ + n > kCap) { overflow_ = true; return; } + std::memcpy(buf_ + len_, p, n); len_ += n; +} + +void HostAssembler::movImm(Reg d, int32_t imm) { // movz wD, #imm16 + emit32(0x52800000u | ((uint32_t(imm) & 0xffff) << 5) | mr(d)); +} +void HostAssembler::addImm(Reg d, Reg a, int32_t imm) { // add xD, xA, #imm12 (64-bit) + emit32(0x91000000u | ((uint32_t(imm) & 0xfff) << 10) | (mr(a) << 5) | mr(d)); +} +void HostAssembler::addReg(Reg d, Reg a, Reg b) { // add wD, wA, wB (32-bit) + emit32(0x0b000000u | (mr(b) << 16) | (mr(a) << 5) | mr(d)); +} +void HostAssembler::mulImm(Reg d, Reg a, int32_t imm) { // d = a * imm via mov x15 + mul + // Small-immediate multiply: load imm into x15 (not in the Reg map, so it clobbers no + // vreg) then mul. x15 is caller-saved scratch on the host ABI. + emit32(0x52800000u | ((uint32_t(imm) & 0xffff) << 5) | 15); // movz w15, #imm + emit32(0x1b007c00u | (15 << 16) | (mr(a) << 5) | mr(d)); // mul wD, wA, w15 +} +void HostAssembler::mulReg(Reg d, Reg a, Reg b) { // mul wD, wA, wB + emit32(0x1b007c00u | (mr(b) << 16) | (mr(a) << 5) | mr(d)); +} +void HostAssembler::store8(Reg base, Reg off, Reg val) { // strb wVal, [xBase, xOff] + emit32(0x38206800u | (mr(off) << 16) | (mr(base) << 5) | mr(val)); +} +void HostAssembler::cmp(Reg a, Reg b) { // cmp wA, wB (subs wzr, wA, wB) + emit32(0x6b00001fu | (mr(b) << 16) | (mr(a) << 5)); +} +void HostAssembler::branchIfZero(Reg a, Label l) { // cbz wA, l (offset patched) + fixups_[fixupCount_++] = {len_, l, 0}; + emit32(0x34000000u | mr(a)); +} +void HostAssembler::branchIf(Cond c, Label l) { // b.cond l (offset patched) + uint8_t cond = (c == Cond::Lo) ? 0x3 : 0x2; // LO=cc(3), HS=cs(2) + fixups_[fixupCount_++] = {len_, l, static_cast(1u | (cond << 4))}; + emit32(0x54000000u | cond); +} +void HostAssembler::call(Reg d, Reg a, const void* fn) { + // Preserve EVERY register that may hold a live value across the call: the host args + // (x0/x1/x2), the link register x30 (blr overwrites it; our function is a leaf), and the + // whole vreg scratch pool (x4-x7, x9-x14) — because a value computed before the call (e.g. + // a first random16's result) can be live across a SECOND call. Saving the full pool makes + // the live-vreg-across-call contract hold for any expression; it's a cold path (once per + // call, not per pixel). 112-byte frame (7 pairs) keeps sp 16-aligned. + emit32(0xa9b907e0u); // stp x0, x1, [sp, #-112]! + emit32(0xa9017be2u); // stp x2, x30, [sp, #16] + emit32(0xa90217e4u); // stp x4, x5, [sp, #32] + emit32(0xa9031fe6u); // stp x6, x7, [sp, #48] + emit32(0xa9042be9u); // stp x9, x10, [sp, #64] + emit32(0xa90533ebu); // stp x11,x12, [sp, #80] + emit32(0xa9063bedu); // stp x13,x14, [sp, #96] + // arg into x0 (the built-in's first parameter) + emit32(0xaa0003e0u | (uint32_t(mr(a)) << 16)); // mov x0, x + // materialise the 64-bit absolute fn address into x15 (movz + 3×movk) + uint64_t addr = reinterpret_cast(fn); + emit32(0xd2800000u | ((uint32_t(addr) & 0xffff) << 5) | 15); // movz x15, #b0 + emit32(0xf2800000u | (1u << 21) | (((uint32_t(addr >> 16)) & 0xffff) << 5) | 15); // movk x15,#b1,lsl16 + emit32(0xf2800000u | (2u << 21) | (((uint32_t(addr >> 32)) & 0xffff) << 5) | 15); // movk x15,#b2,lsl32 + emit32(0xf2800000u | (3u << 21) | (((uint32_t(addr >> 48)) & 0xffff) << 5) | 15); // movk x15,#b3,lsl48 + emit32(0xd63f0000u | (15u << 5)); // blr x15 + // Stash the result (x0) in x15 — a non-pool scratch — BEFORE restoring, since x0 and the + // dst register are both in the saved set the restore overwrites. + emit32(0xaa0003efu); // mov x15, x0 (result → x15) + // restore the full saved set (reverse order) + emit32(0xa9463bedu); // ldp x13,x14, [sp, #96] + emit32(0xa94533ebu); // ldp x11,x12, [sp, #80] + emit32(0xa9442be9u); // ldp x9, x10, [sp, #64] + emit32(0xa9431fe6u); // ldp x6, x7, [sp, #48] + emit32(0xa94217e4u); // ldp x4, x5, [sp, #32] + emit32(0xa9417be2u); // ldp x2, x30, [sp, #16] + emit32(0xa8c707e0u); // ldp x0, x1, [sp], #112 + // now move the stashed result into dst (dst is restored/valid; x15 holds the result) + emit32(0xaa0f03e0u | uint32_t(mr(d))); // mov x, x15 +} +void HostAssembler::ret() { emit32(0xd65f03c0u); } + +void HostAssembler::patchBranches() { + for (uint8_t i = 0; i < fixupCount_; i++) { + const Fixup& f = fixups_[i]; + int32_t target = labelPos_[f.label]; + int32_t rel = (target - static_cast(f.at)) >> 2; // PC-relative, /4 + uint32_t w; std::memcpy(&w, buf_ + f.at, 4); + w |= (uint32_t(rel) & 0x7ffff) << 5; // imm19 field (cbz & b.cond) + std::memcpy(buf_ + f.at, &w, 4); + } +} + +#elif defined(__x86_64__) +#error "MoonLive host assembler: x86-64 backend not yet implemented (CI is arm64 for now)" +#else +#error "MoonLive host assembler: unsupported host ISA" +#endif + +} // namespace mm::moonlive diff --git a/src/platform/desktop/moonlive_asm_host.h b/src/platform/desktop/moonlive_asm_host.h new file mode 100644 index 0000000..69ed99e --- /dev/null +++ b/src/platform/desktop/moonlive_asm_host.h @@ -0,0 +1,86 @@ +#pragma once + +#include +#include + +// MoonLive host assembler (desktop backend) — a tiny named-instruction assembler for the +// host ISA (arm64 / x86-64), the textbook MacroAssembler shape (V8 Assembler, LLVM MCInst, +// asmjit). It appends one instruction at a time to a byte buffer and back-patches label +// offsets, so the IR→bytes lowering can COMPOSE a multi-op statement without hand-computing +// branch displacements (the crash class the verbatim-blob spike avoided by never composing). +// +// This header declares the neutral surface (register handles + the instruction methods); the +// per-ISA encodings live in moonlive_asm_host.cpp behind the platform boundary. The IR +// lowering (lowerToBytes) calls these methods; it never emits raw bytes itself. + +namespace mm::moonlive { + +// Abstract register handle — an index the assembler maps to a real machine register. The IR's +// virtual registers map onto these; the assembler owns the machine-register assignment so the +// IR stays ISA-neutral. R0..R3 alias the host-ABI argument registers (buf, nLights, cpl, t); +// R4+ are caller-saved scratch. +enum Reg : uint8_t { R0 = 0, R1, R2, R3, R4, R5, R6, R7, R8, R9, + R10, R11, R12, R13, kRegCount }; + +// A label is an index into the assembler's label table; bind() fixes its position, branches to +// it are back-patched when bound. +using Label = uint8_t; + +// Branch condition (only the ones the IR needs so far). +enum class Cond : uint8_t { Lo /* unsigned < */, Hs /* unsigned >= */ }; + +class HostAssembler { +public: + // --- buffer --- + // Resolve all branch fixups against bound labels, then expose the finished bytes. Call + // once after the last instruction; bytes()/size() are valid only after finalize(). + void finalize() { patchBranches(); } + const uint8_t* bytes() const { return buf_; } + size_t size() const { return len_; } + bool overflowed() const { return overflow_; } + + // --- labels --- + Label newLabel(); + void bind(Label l); // mark l's position = current offset + + // --- instructions (named, register/immediate operands) --- + void movImm(Reg d, int32_t imm); // d = imm + void addImm(Reg d, Reg a, int32_t imm); // d = a + imm + void addReg(Reg d, Reg a, Reg b); // d = a + b + void mulImm(Reg d, Reg a, int32_t imm); // d = a * imm (index scaling by a constant) + void mulReg(Reg d, Reg a, Reg b); // d = a * b (index scaling by a runtime cpl) + void store8(Reg base, Reg off, Reg val); // byte store: base[off] = val (low 8 bits) + void cmp(Reg a, Reg b); // flags = a - b + void branchIfZero(Reg a, Label l); // if a == 0 goto l + void branchIf(Cond c, Label l); // if flags satisfy c goto l (after cmp) + // Call a host built-in: d = fn(a). Preserves the host-arg registers (R0/R1/R2 = buf, + // nLights, cpl) across the call by saving them on the stack, so they stay live for the + // statement after the call — the live-vreg-across-Call contract. `fn` is an absolute + // function pointer (materialised into a scratch register). Caller-saved vregs other than + // R0..R2 must not be live across a call (the front-end orders ops so none are). + void call(Reg d, Reg a, const void* fn); + void ret(); + +private: + static constexpr size_t kCap = 768; + static constexpr uint8_t kMaxLabels = 16; + static constexpr uint8_t kMaxFixups = 32; + + void emit32(uint32_t w); // append one 32-bit instruction (arm64) — or byte run (x64) + void emitBytes(const uint8_t* p, size_t n); + + uint8_t buf_[kCap] = {}; + size_t len_ = 0; + bool overflow_ = false; + + // Label positions (-1 = unbound) and pending branch fixups. + int32_t labelPos_[kMaxLabels]; + uint8_t labelCount_ = 0; + struct Fixup { size_t at; Label label; uint8_t kind; }; // kind: 0=cbz,1=b.cond + Fixup fixups_[kMaxFixups]; + uint8_t fixupCount_ = 0; + + void patchBranches(); // resolve all fixups against bound labels +}; + +} // namespace mm::moonlive diff --git a/src/platform/desktop/moonlive_emit.cpp b/src/platform/desktop/moonlive_emit.cpp index 0eeb6d7..c104d03 100644 --- a/src/platform/desktop/moonlive_emit.cpp +++ b/src/platform/desktop/moonlive_emit.cpp @@ -41,7 +41,7 @@ static const uint32_t kArm64[] = { }; size_t emitFill(uint8_t* out, size_t cap, uint8_t r, uint8_t g, uint8_t b) { - if (cap < sizeof(kArm64)) return 0; + if (!out || cap < sizeof(kArm64)) return 0; uint32_t code[sizeof(kArm64) / 4]; std::memcpy(code, kArm64, sizeof(kArm64)); // Patch the colour immediates: mov wN,#imm encodes imm at bits [20:5]; the base word @@ -78,7 +78,7 @@ static const uint32_t kArm64Anim[] = { }; size_t emitAnimatedFill(uint8_t* out, size_t cap) { - if (cap < sizeof(kArm64Anim)) return 0; + if (!out || cap < sizeof(kArm64Anim)) return 0; std::memcpy(out, kArm64Anim, sizeof(kArm64Anim)); return sizeof(kArm64Anim); } @@ -107,7 +107,7 @@ static const uint8_t kX64[] = { }; size_t emitFill(uint8_t* out, size_t cap, uint8_t r, uint8_t g, uint8_t b) { - if (cap < sizeof(kX64)) return 0; + if (!out || cap < sizeof(kX64)) return 0; std::memcpy(out, kX64, sizeof(kX64)); out[0x11] = r; out[0x17] = g; @@ -137,7 +137,7 @@ static const uint8_t kX64Anim[] = { }; size_t emitAnimatedFill(uint8_t* out, size_t cap) { - if (cap < sizeof(kX64Anim)) return 0; + if (!out || cap < sizeof(kX64Anim)) return 0; std::memcpy(out, kX64Anim, sizeof(kX64Anim)); return sizeof(kX64Anim); } diff --git a/src/platform/desktop/moonlive_lower_host.cpp b/src/platform/desktop/moonlive_lower_host.cpp new file mode 100644 index 0000000..15a7749 --- /dev/null +++ b/src/platform/desktop/moonlive_lower_host.cpp @@ -0,0 +1,88 @@ +#include "core/moonlive/moonlive_emit.h" +#include "core/moonlive/MoonLiveIr.h" +#include "moonlive_asm_host.h" + +#include + +// MoonLive host backend — lower a typed IR program to machine bytes by driving the host +// assembler. The IR is neutral; this file knows how each IR op becomes assembler calls. The +// host arguments arrive in the lowest vregs (the light host assigns kArg0=buf, kArg1=nLights, +// kArg2=cpl, kArg3=t — the only place this backend assumes the LED layout, and only to +// implement the WriteRGB/FillRGB inline ops the host registered). +// +// Vregs map 1:1 onto the assembler's Reg handles. The inline ops use a couple of scratch +// registers ABOVE the program's high-water mark (the parser never allocates them), so they +// don't clobber a live vreg. + +namespace mm::moonlive { + +namespace { +Reg reg(VReg v) { return static_cast(v); } +} + +size_t lowerToBytes(const IrProgram& ir, uint8_t* out, size_t cap) { + // Reserve three scratch regs above the program's vregs for the inline ops' temps. + if (!out || cap == 0 || ir.vregsUsed + 3 > kRegCount) return 0; + const Reg sOff = static_cast(ir.vregsUsed); // base byte offset of the current light + const Reg sCtr = static_cast(ir.vregsUsed + 1); // loop counter + const Reg sAddr = static_cast(ir.vregsUsed + 2); // per-channel address (off, off+1, off+2) + + HostAssembler a; + + for (uint8_t i = 0; i < ir.count; i++) { + const IrInst& op = ir.ops[i]; + switch (op.op) { + case IrOp::Const: a.movImm(reg(op.dst), op.imm); break; + case IrOp::Add: a.addReg(reg(op.dst), reg(op.a), reg(op.b)); break; + case IrOp::AddImm: a.addImm(reg(op.dst), reg(op.a), op.imm); break; + case IrOp::Mul: a.mulReg(reg(op.dst), reg(op.a), reg(op.b)); break; + case IrOp::Call: + if (!op.callFn) return 0; + a.call(reg(op.dst), reg(op.a), op.callFn); + break; + case IrOp::Inline: + switch (op.inlineOp) { + case InlineOp::WriteRGB: { + // setRGB(index=a, r=b, g=c, b=d): bounds-guard, addr = index*cpl, store 3. + Label skip = a.newLabel(); + a.cmp(reg(op.a), reg(kArg1)); // index vs nLights + a.branchIf(Cond::Hs, skip); // index >= nLights → skip + a.mulReg(sAddr, reg(op.a), reg(kArg2)); // addr = index * cpl + a.store8(reg(kArg0), sAddr, reg(op.b)); // buf[addr+0] = r + a.addImm(sAddr, sAddr, 1); a.store8(reg(kArg0), sAddr, reg(op.c)); // +1 = g + a.addImm(sAddr, sAddr, 1); a.store8(reg(kArg0), sAddr, reg(op.d)); // +2 = b + a.bind(skip); + break; + } + case InlineOp::FillRGB: { + // fill(r=a, g=b, b=c): for i in 0..nLights { buf[i*cpl+0..2] = r,g,b }. + // sOff = byte base of the current light; sAddr = sOff/+1/+2 per channel + // (a fresh copy each light so the +1/+2 never corrupt sOff); sOff += cpl. + Label done = a.newLabel(), top = a.newLabel(); + a.movImm(sOff, 0); // off = 0 + a.movImm(sCtr, 0); // i = 0 + a.branchIfZero(reg(kArg1), done); // nLights==0 → skip + a.bind(top); + a.addImm(sAddr, sOff, 0); a.store8(reg(kArg0), sAddr, reg(op.a)); // buf[off+0]=r + a.addImm(sAddr, sOff, 1); a.store8(reg(kArg0), sAddr, reg(op.b)); // buf[off+1]=g + a.addImm(sAddr, sOff, 2); a.store8(reg(kArg0), sAddr, reg(op.c)); // buf[off+2]=b + a.addReg(sOff, sOff, reg(kArg2)); // off += cpl (general stride) + a.addImm(sCtr, sCtr, 1); // i++ + a.cmp(sCtr, reg(kArg1)); + a.branchIf(Cond::Lo, top); + a.bind(done); + break; + } + } + break; + default: break; // Loop/LoopEnd/Bounds/BoundsEnd are not emitted by the parser now + } + } + a.ret(); + a.finalize(); + if (a.overflowed() || a.size() > cap) return 0; + std::memcpy(out, a.bytes(), a.size()); + return a.size(); +} + +} // namespace mm::moonlive diff --git a/src/platform/esp32/moonlive_asm_riscv.cpp b/src/platform/esp32/moonlive_asm_riscv.cpp new file mode 100644 index 0000000..3892c9e --- /dev/null +++ b/src/platform/esp32/moonlive_asm_riscv.cpp @@ -0,0 +1,133 @@ +#include "moonlive_asm_riscv.h" + +#include + +#if defined(__riscv) // the RISC-V assembler is only built for RISC-V targets (ESP32-P4) + +// MoonLive RISC-V assembler — RV32 named instructions, encodings verified against +// riscv32-esp-elf-as (see the plan / commit). Fixed 4-byte little-endian instructions; the +// standard call ABI (args a0.., result a0, ra = return). Branch offsets are back-patched. + +namespace mm::moonlive { + +// R0..R3 → a0..a3 (10..13, the host args). R4..R11 → t0,t1,t2,t3,t4,t5,a4,a5 (caller-saved +// temps). t6(31) and a6(16) are reserved internal scratch (store8 address, call address build), +// not in the pool. +static const uint8_t kRvReg[kRegCount] = {10, 11, 12, 13, 5, 6, 7, 28, 29, 30, 14, 15}; +static uint8_t xr(Reg r) { return kRvReg[r]; } +static constexpr uint8_t kScratchAddr = 31; // t6 — store8 address temp +static constexpr uint8_t kScratchFn = 16; // a6 — call address build / result stash + +void RiscvAssembler::emit32(uint32_t w) { + if (len_ + 4 > kCap) { overflow_ = true; return; } + buf_[len_++] = uint8_t(w); buf_[len_++] = uint8_t(w >> 8); + buf_[len_++] = uint8_t(w >> 16); buf_[len_++] = uint8_t(w >> 24); +} + +Label RiscvAssembler::newLabel() { + if (labelCount_ == 0) for (auto& p : labelPos_) p = -1; + Label l = labelCount_++; labelPos_[l] = -1; return l; +} +void RiscvAssembler::bind(Label l) { labelPos_[l] = static_cast(len_); } + +// --- I/R/S-type encoders (rd/rs1/rs2 are real x-register numbers) --- +static uint32_t encAddi(uint8_t rd, uint8_t rs1, int32_t imm) { + return ((uint32_t(imm) & 0xfff) << 20) | (rs1 << 15) | (0 << 12) | (rd << 7) | 0x13; +} +static uint32_t encAdd(uint8_t rd, uint8_t rs1, uint8_t rs2) { + return (rs2 << 20) | (rs1 << 15) | (0 << 12) | (rd << 7) | 0x33; +} +static uint32_t encMul(uint8_t rd, uint8_t rs1, uint8_t rs2) { + return (1u << 25) | (rs2 << 20) | (rs1 << 15) | (0 << 12) | (rd << 7) | 0x33; +} +static uint32_t encSb(uint8_t rs2, uint8_t rs1, int32_t imm) { // sb rs2, imm(rs1) + return (((uint32_t(imm) >> 5) & 0x7f) << 25) | (rs2 << 20) | (rs1 << 15) | (0 << 12) | + ((uint32_t(imm) & 0x1f) << 7) | 0x23; +} +static uint32_t encSw(uint8_t rs2, uint8_t rs1, int32_t imm) { // sw rs2, imm(rs1) + return (((uint32_t(imm) >> 5) & 0x7f) << 25) | (rs2 << 20) | (rs1 << 15) | (2 << 12) | + ((uint32_t(imm) & 0x1f) << 7) | 0x23; +} +static uint32_t encLw(uint8_t rd, uint8_t rs1, int32_t imm) { // lw rd, imm(rs1) + return ((uint32_t(imm) & 0xfff) << 20) | (rs1 << 15) | (2 << 12) | (rd << 7) | 0x03; +} +static uint32_t encLui(uint8_t rd, uint32_t imm20) { + return (imm20 << 12) | (rd << 7) | 0x37; +} +static uint32_t encBranch(uint8_t rs1, uint8_t rs2, uint8_t f3, int32_t off) { // B-type + uint32_t o = uint32_t(off) & 0x1fff; + return (((o >> 12) & 1) << 31) | (((o >> 5) & 0x3f) << 25) | (rs2 << 20) | (rs1 << 15) | + (f3 << 12) | (((o >> 1) & 0xf) << 8) | (((o >> 11) & 1) << 7) | 0x63; +} + +void RiscvAssembler::movImm(Reg d, int32_t imm) { emit32(encAddi(xr(d), 0, imm)); } // li = addi rd,x0,imm +void RiscvAssembler::movReg(Reg d, Reg a) { emit32(encAddi(xr(d), xr(a), 0)); } // mv = addi rd,ra,0 +void RiscvAssembler::addImm(Reg d, Reg a, int32_t imm) { emit32(encAddi(xr(d), xr(a), imm)); } +void RiscvAssembler::addReg(Reg d, Reg a, Reg b) { emit32(encAdd(xr(d), xr(a), xr(b))); } +void RiscvAssembler::mulReg(Reg d, Reg a, Reg b) { emit32(encMul(xr(d), xr(a), xr(b))); } + +void RiscvAssembler::store8(Reg base, Reg off, Reg val) { + emit32(encAdd(kScratchAddr, xr(base), xr(off))); // t6 = base + off + emit32(encSb(xr(val), kScratchAddr, 0)); // sb val, 0(t6) +} +void RiscvAssembler::branchIfZero(Reg a, Label l) { // a == 0 ⇔ bgeu x0, a (unsigned 0 >= a) + fixups_[fixupCount_++] = {len_, l}; + emit32(encBranch(0, xr(a), 7, 0)); // bgeu x0, a, l (patched) +} +void RiscvAssembler::branchGeU(Reg a, Reg b, Label l) { + fixups_[fixupCount_++] = {len_, l}; + emit32(encBranch(xr(a), xr(b), 7, 0)); // bgeu a, b, l +} +void RiscvAssembler::branchNe(Reg a, Reg b, Label l) { + fixups_[fixupCount_++] = {len_, l}; + emit32(encBranch(xr(a), xr(b), 1, 0)); // bne a, b, l +} + +// Standard call to a host built-in: d = fn(a). All vreg temps are caller-saved, so a value +// live across the call must be preserved — save the whole pool + ra + the host args around the +// call (mirrors the host backend). The fn address is built with lui+addi (the hi/lo split, +1 +// to the upper when the low 12 bits' sign bit is set). 64-byte frame, 16-byte aligned. +void RiscvAssembler::call(Reg d, Reg a, const void* fn) { + emit32(encAddi(2, 2, -64)); // addi sp, sp, -64 + emit32(encSw(1, 2, 60)); // sw ra, 60(sp) + // save the host args a0..a3 and all pool temps + static const uint8_t saved[] = {10, 11, 12, 13, 5, 6, 7, 28, 29, 30, 14, 15}; + int off = 0; + for (uint8_t r : saved) { emit32(encSw(r, 2, off)); off += 4; } + // arg into a0 (read BEFORE the address build touches a6) + emit32(encAddi(10, xr(a), 0)); // mv a0, aArg (if aArg==a0, no-op) + // a6 = fn address via lui + addi (hi/lo split) + uint32_t addr = static_cast(reinterpret_cast(fn)); + uint32_t hi = (addr + 0x800) >> 12; // round for the sign-extended addi + int32_t lo = static_cast(addr) - static_cast(hi << 12); + emit32(encLui(kScratchFn, hi & 0xfffff)); // lui a6, hi + emit32(encAddi(kScratchFn, kScratchFn, lo)); // addi a6, a6, lo + emit32((kScratchFn << 15) | (1 << 7) | 0x67); // jalr ra, a6, 0 + // stash result (a0) in a6 before restoring (a0 is restored to the old buf) + emit32(encAddi(kScratchFn, 10, 0)); // mv a6, a0 + // restore + off = 0; + for (uint8_t r : saved) { emit32(encLw(r, 2, off)); off += 4; } + emit32(encLw(1, 2, 60)); // lw ra, 60(sp) + emit32(encAddi(2, 2, 64)); // addi sp, sp, 64 + emit32(encAddi(xr(d), kScratchFn, 0)); // mv dst, a6 (the result) +} + +void RiscvAssembler::ret() { emit32(0x00008067u); } // ret = jalr x0, ra, 0 + +void RiscvAssembler::patchBranches() { + for (uint8_t i = 0; i < fixupCount_; i++) { + const Fixup& f = fixups_[i]; + int32_t off = labelPos_[f.label] - static_cast(f.at); + uint32_t w; std::memcpy(&w, buf_ + f.at, 4); + // re-scatter the offset into the B-type immediate fields, keeping the rest. + w &= ~((1u<<31) | (0x3fu<<25) | (0xfu<<8) | (1u<<7)); + uint32_t o = uint32_t(off) & 0x1fff; + w |= (((o>>12)&1)<<31) | (((o>>5)&0x3f)<<25) | (((o>>1)&0xf)<<8) | (((o>>11)&1)<<7); + std::memcpy(buf_ + f.at, &w, 4); + } +} + +} // namespace mm::moonlive + +#endif // __riscv diff --git a/src/platform/esp32/moonlive_asm_riscv.h b/src/platform/esp32/moonlive_asm_riscv.h new file mode 100644 index 0000000..1fdf2c0 --- /dev/null +++ b/src/platform/esp32/moonlive_asm_riscv.h @@ -0,0 +1,64 @@ +#pragma once + +#include +#include + +// MoonLive RISC-V assembler (ESP32-P4 backend) — the device counterpart of the host/Xtensa +// MacroAssemblers, same named-instruction interface. RV32: fixed 4-byte instructions, a +// standard (non-windowed) call ABI — simpler than Xtensa. Branch displacements are back-patched +// against bound labels. +// +// Register map: R0..R3 → a0..a3 (the host args buf/nLights/cpl/t); R4.. → caller-saved temps +// (t0..t6, a4..a7). All in the caller-saved set, so call() saves the live pool explicitly. + +namespace mm::moonlive { + +enum Reg : uint8_t { R0 = 0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, kRegCount }; +using Label = uint8_t; +enum class Cond : uint8_t { Lo /* unsigned < */, Hs /* unsigned >= */ }; + +class RiscvAssembler { +public: + void finalize() { patchBranches(); } + const uint8_t* bytes() const { return buf_; } + size_t size() const { return len_; } + bool overflowed() const { return overflow_; } + + void prologue() {} // RV needs no fixed prologue (sp managed in call()) + Label newLabel(); + void bind(Label l); + + void movImm(Reg d, int32_t imm); // li rd, imm (addi rd, x0, imm) + void movReg(Reg d, Reg a); // mv rd, ra (addi rd, ra, 0) + void addImm(Reg d, Reg a, int32_t imm); // addi rd, ra, imm + void addReg(Reg d, Reg a, Reg b); // add rd, ra, rb + void mulReg(Reg d, Reg a, Reg b); // mul rd, ra, rb + void store8(Reg base, Reg off, Reg val); // add tmp,base,off ; sb val,0(tmp) + void branchIfZero(Reg a, Label l); // beqz a, l (bge x0, a... use bgeu against x0) + void branchGeU(Reg a, Reg b, Label l); // bgeu a, b, l + void branchNe(Reg a, Reg b, Label l); // bne a, b, l + void call(Reg d, Reg a, const void* fn); // standard call to a host built-in + void epilogue() { ret(); } + void ret(); + +private: + static constexpr size_t kCap = 768; + static constexpr uint8_t kMaxLabels = 16; + static constexpr uint8_t kMaxFixups = 32; + + void emit32(uint32_t w); + + uint8_t buf_[kCap] = {}; + size_t len_ = 0; + bool overflow_ = false; + + int32_t labelPos_[kMaxLabels]; + uint8_t labelCount_ = 0; + struct Fixup { size_t at; Label label; }; // all B-type, 4 bytes at `at` + Fixup fixups_[kMaxFixups]; + uint8_t fixupCount_ = 0; + + void patchBranches(); +}; + +} // namespace mm::moonlive diff --git a/src/platform/esp32/moonlive_asm_xtensa.cpp b/src/platform/esp32/moonlive_asm_xtensa.cpp new file mode 100644 index 0000000..41cc509 --- /dev/null +++ b/src/platform/esp32/moonlive_asm_xtensa.cpp @@ -0,0 +1,145 @@ +#include "moonlive_asm_xtensa.h" + +#include + +#if defined(__XTENSA__) // the Xtensa assembler is only built for Xtensa targets + +// MoonLive Xtensa assembler — named instructions encoded once, composed by the IR lowering. +// Encodings verified against xtensa-esp32s3-elf-as (see the plan / commit). Xtensa is +// little-endian with mixed 24-bit (wide) and 16-bit (narrow) instructions. The register +// convention: R0..R3 → a2..a5 (the windowed-ABI args buf/nLights/cpl/t), R4..R9 → a6..a11. +// +// Branch offset rule (verified): for the 8-bit-offset conditional branches we use, the offset +// byte = target - (branchInstrAddr + 4). All such branches put that byte at instrAddr+2, so one +// fixup kind covers them. + +namespace mm::moonlive { + +// R0..R3 → a2..a5 (the windowed-ABI args); R4..R11 → a6..a11, a14, a15. a12/a13 are internal +// scratch (store8 address, branchIfZero zero-reg, call result stash), so not in the pool. +static const uint8_t kXtReg[kRegCount] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15}; +static uint8_t ar(Reg r) { return kXtReg[r]; } + +void XtensaAssembler::emit(const uint8_t* p, size_t n) { + if (len_ + n > kCap) { overflow_ = true; return; } + std::memcpy(buf_ + len_, p, n); len_ += n; +} +void XtensaAssembler::emit2(uint16_t w) { + const uint8_t b[2] = {uint8_t(w), uint8_t(w >> 8)}; emit(b, 2); +} +void XtensaAssembler::emit3(uint32_t w) { + const uint8_t b[3] = {uint8_t(w), uint8_t(w >> 8), uint8_t(w >> 16)}; emit(b, 3); +} + +// entry a1, 48 — a 48-byte frame leaves room for the call8 window rotation (a routine with no +// call would be fine with 32, but 48 is harmless and lets any program call a built-in). +void XtensaAssembler::prologue() { emit3(0x006136u); } // entry a1, 48 +void XtensaAssembler::epilogue() { emit2(0xf01du); } // retw.n + +Label XtensaAssembler::newLabel() { + if (labelCount_ == 0) for (auto& p : labelPos_) p = -1; + Label l = labelCount_++; labelPos_[l] = -1; return l; +} +void XtensaAssembler::bind(Label l) { labelPos_[l] = static_cast(len_); } + +// movi aD, #imm (wide form, byte immediate 0..255): bytes [ (d<<4)|2, 0xa0, imm ] +void XtensaAssembler::movImm(Reg d, int32_t imm) { + const uint8_t b[3] = {uint8_t((ar(d) << 4) | 0x2), 0xa0, uint8_t(imm & 0xff)}; + emit(b, 3); +} +// add.n aD, aA, aB : word (d<<12)|(a<<8)|(b<<4)|0xa +void XtensaAssembler::addReg(Reg d, Reg a, Reg b) { + emit2(uint16_t((ar(d) << 12) | (ar(a) << 8) | (ar(b) << 4) | 0xa)); +} +// mov.n aD, aA : bytes [ (d<<4)|0xd, a ] +void XtensaAssembler::movReg(Reg d, Reg a) { + const uint8_t b[2] = {uint8_t((ar(d) << 4) | 0xd), ar(a)}; + emit(b, 2); +} +// addi.n aD, aA, #imm (1..15) : word (d<<12)|(a<<8)|(imm<<4)|0xb +void XtensaAssembler::addImm(Reg d, Reg a, int32_t imm) { + emit2(uint16_t((ar(d) << 12) | (ar(a) << 8) | ((imm & 0xf) << 4) | 0xb)); +} +// mull aD, aA, aB : 24-bit 0x820000 | (d<<12) | (a<<8) | (b<<4) +void XtensaAssembler::mulReg(Reg d, Reg a, Reg b) { + emit3(0x820000u | (uint32_t(ar(d)) << 12) | (uint32_t(ar(a)) << 8) | (uint32_t(ar(b)) << 4)); +} +// Xtensa s8i only offsets a base by an immediate (no register-offset store), so compute the +// address into a dedicated scratch a12 — OUTSIDE the R0..R9 → a2..a11 vreg map, so it never +// clobbers a live virtual register — then s8i aVal, a12, 0. +// add.n a12, aBase, aOff : (12<<12)|(base<<8)|(off<<4)|0xa ; s8i aVal, a12, 0 : [(val<<4)|2, 0x40|12, 0] +static constexpr uint8_t kAddrScratch = 12; // a12 +void XtensaAssembler::store8(Reg base, Reg off, Reg val) { + emit2(uint16_t((kAddrScratch << 12) | (ar(base) << 8) | (ar(off) << 4) | 0xa)); // add.n a12, base, off + const uint8_t b[3] = {uint8_t((ar(val) << 4) | 0x2), uint8_t(0x40 | kAddrScratch), 0x00}; + emit(b, 3); // s8i aVal, a12, 0 +} + +// branchIfZero(a, l): synthesised as `movi a13,0; bgeu a13, a, l`. Unsigned 0 >= a is true +// IFF a == 0, so this branches exactly when a is zero — using only the verified bgeu 8-bit +// branch (no separate beqz form / offset width). a13 is a scratch outside the vreg map. +void XtensaAssembler::branchIfZero(Reg a, Label l) { + static constexpr uint8_t kZero = 13; // a13 + const uint8_t mv[3] = {uint8_t((kZero << 4) | 0x2), 0xa0, 0x00}; // movi a13, 0 + emit(mv, 3); + fixups_[fixupCount_++] = {len_, l}; + const uint8_t br[3] = {uint8_t((ar(a) << 4) | 0x7), uint8_t((0xb << 4) | kZero), 0x00}; // bgeu a13, a + emit(br, 3); +} +// bgeu aA, aB, l (skip if a >= b, unsigned) +void XtensaAssembler::branchGeU(Reg a, Reg b, Label l) { + fixups_[fixupCount_++] = {len_, l}; + const uint8_t bytes[3] = {uint8_t((ar(b) << 4) | 0x7), uint8_t((0xb << 4) | ar(a)), 0x00}; + emit(bytes, 3); +} +// bne aA, aB, l +void XtensaAssembler::branchNe(Reg a, Reg b, Label l) { + fixups_[fixupCount_++] = {len_, l}; + const uint8_t bytes[3] = {uint8_t((ar(b) << 4) | 0x7), uint8_t((0x9 << 4) | ar(a)), 0x00}; + emit(bytes, 3); +} + +// Windowed call to a host built-in: d = fn(a). CALL8 rotates the window by 8, so the arg goes +// in a10 and the result returns in a10. The caller's a2..a7 are preserved by the window for +// free; only a8..a11 rotate out — and those hold vreg values that may be live across the call +// (a script with two random16 calls keeps the first result live across the second). So this +// SAVES a8/a9/a11 to the entry frame around the call (a10 carries the arg, then the result), +// mirroring the host backend's full-register-save. Cold path (once per call). The 48-byte +// frame from prologue() has room at offsets 16/20/28. The 32-bit fn address is built in a8 +// byte-by-byte (movi/slli/add) — no l32r literal pool. +void XtensaAssembler::call(Reg d, Reg a, const void* fn) { + // Save the rotate-out scratch a8, a9, a11 (a10 will carry arg→result). + auto s32i = [&](uint8_t r, uint8_t off4){ const uint8_t b[3]={uint8_t((r<<4)|2),0x61,off4}; emit(b,3); }; + auto l32i = [&](uint8_t r, uint8_t off4){ const uint8_t b[3]={uint8_t((r<<4)|2),0x21,off4}; emit(b,3); }; + s32i(8, 4); s32i(9, 5); s32i(11, 7); // [a1+16]=a8, [a1+20]=a9, [a1+28]=a11 + + // arg into a10 (read aArg BEFORE the address build clobbers a8/a9). + emit2(uint16_t((uint32_t(ar(a)) << 8) | (10 << 4) | 0xd)); // mov a10, aArg + + uint32_t addr = static_cast(reinterpret_cast(fn)); + auto moviA8 = [&](uint8_t v){ const uint8_t b[3]={0x82,0xa0,v}; emit(b,3); }; + auto moviA9 = [&](uint8_t v){ const uint8_t b[3]={0x92,0xa0,v}; emit(b,3); }; + auto slliA8 = [&]{ const uint8_t b[3]={0x80,0x88,0x11}; emit(b,3); }; + auto addA8A9 = [&]{ emit2(0x889au); }; + moviA8(uint8_t(addr >> 24)); + slliA8(); moviA9(uint8_t(addr >> 16)); addA8A9(); + slliA8(); moviA9(uint8_t(addr >> 8)); addA8A9(); + slliA8(); moviA9(uint8_t(addr)); addA8A9(); + emit3(0x0000e0u | (8u << 8)); // callx8 a8 → result in a10 + // stash result (a10) in a12 (not in the saved set), restore a8/a9/a11, then dst = a12. + emit2(uint16_t((10u << 8) | (12u << 4) | 0xd)); // mov a12, a10 + l32i(8, 4); l32i(9, 5); l32i(11, 7); + emit2(uint16_t((12u << 8) | (uint32_t(ar(d)) << 4) | 0xd)); // mov aDst, a12 +} + +void XtensaAssembler::patchBranches() { + for (uint8_t i = 0; i < fixupCount_; i++) { + const Fixup& f = fixups_[i]; + int32_t off = labelPos_[f.label] - (static_cast(f.at) + 4); // verified rule + buf_[f.at + 2] = static_cast(off & 0xff); // offset byte at +2 + } +} + +} // namespace mm::moonlive + +#endif // __XTENSA__ diff --git a/src/platform/esp32/moonlive_asm_xtensa.h b/src/platform/esp32/moonlive_asm_xtensa.h new file mode 100644 index 0000000..ddbef47 --- /dev/null +++ b/src/platform/esp32/moonlive_asm_xtensa.h @@ -0,0 +1,66 @@ +#pragma once + +#include +#include + +// MoonLive Xtensa assembler (ESP32 classic/S3 backend) — the device counterpart of the host +// MacroAssembler. Same named-instruction interface (the IR lowering is written once against +// it); the encodings and the windowed ABI are Xtensa-specific. Branch displacements are +// back-patched against bound labels, so no offset is hand-computed (the crash class the +// verbatim-blob spike avoided by never composing stays avoided by back-patching). +// +// Windowed ABI: the emitted routine opens with `entry` and returns with `retw.n`. The host +// args arrive in a2..a5 (buf, nLights, cpl, t); R0..R3 map to those, R4..R9 to a6..a11. + +namespace mm::moonlive { + +enum Reg : uint8_t { R0 = 0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, kRegCount }; +using Label = uint8_t; +enum class Cond : uint8_t { Lo /* unsigned < */, Hs /* unsigned >= */ }; + +class XtensaAssembler { +public: + void finalize() { patchBranches(); } + const uint8_t* bytes() const { return buf_; } + size_t size() const { return len_; } + bool overflowed() const { return overflow_; } + + void prologue(); // entry a1, 32 (must be the first instruction) + Label newLabel(); + void bind(Label l); + + void movImm(Reg d, int32_t imm); // movi aD, #imm (0..255) + void movReg(Reg d, Reg a); // mov.n aD, aA + void addImm(Reg d, Reg a, int32_t imm); // addi.n aD, aA, #imm (1..15) + void addReg(Reg d, Reg a, Reg b); // add.n aD, aA, aB + void mulReg(Reg d, Reg a, Reg b); // mull aD, aA, aB + void store8(Reg base, Reg off, Reg val); // s8i via computed address (add then s8i,0) + void branchIfZero(Reg a, Label l); // beqz aA, l (nLights==0 guard) + void branchGeU(Reg a, Reg b, Label l); // bgeu aA, aB, l (Bounds: skip if a>=b) + void branchNe(Reg a, Reg b, Label l); // bne aA, aB, l (loop test) + void call(Reg d, Reg a, const void* fn); // windowed call8 to a host built-in + void epilogue(); // retw.n + +private: + static constexpr size_t kCap = 768; + static constexpr uint8_t kMaxLabels = 16; + static constexpr uint8_t kMaxFixups = 32; + + void emit(const uint8_t* p, size_t n); + void emit2(uint16_t w); // narrow (16-bit) instruction + void emit3(uint32_t w); // wide (24-bit) instruction + + uint8_t buf_[kCap] = {}; + size_t len_ = 0; + bool overflow_ = false; + + int32_t labelPos_[kMaxLabels]; + uint8_t labelCount_ = 0; + struct Fixup { size_t at; Label label; }; // all our branches use the 8-bit offset at byte+2 + Fixup fixups_[kMaxFixups]; + uint8_t fixupCount_ = 0; + + void patchBranches(); +}; + +} // namespace mm::moonlive diff --git a/src/platform/esp32/moonlive_emit.cpp b/src/platform/esp32/moonlive_emit.cpp index 5a7c505..0e1c969 100644 --- a/src/platform/esp32/moonlive_emit.cpp +++ b/src/platform/esp32/moonlive_emit.cpp @@ -1,4 +1,5 @@ #include "core/moonlive/moonlive_emit.h" +#include "core/moonlive/MoonLiveIr.h" #include @@ -11,10 +12,9 @@ // void fill(uint8_t* buf, uint32_t nLights, uint8_t cpl[, uint32_t t]) // for (i=0; i + +// MoonLive RISC-V backend (ESP32-P4) — lower a neutral IR program to RV32 bytes by driving the +// RISC-V assembler. The device counterpart of the Xtensa/host lowerings: same IR + the same +// WriteRGB/FillRGB inline ops; only the assembler differs. Host args: kArg0=buf, kArg1=nLights, +// kArg2=cpl, kArg3=t. + +#if defined(__riscv) + +namespace mm::moonlive { + +namespace { +Reg reg(VReg v) { return static_cast(v); } + +// random16(n) → a value in [0,n). The same LCG as the other backends so a script behaves +// identically on every target. +extern "C" uint32_t mm_riscv_random16(uint32_t n) { + static uint32_t s = 0x2545F491u; + s = s * 1664525u + 1013904223u; + return n ? (s >> 16) % n : 0u; +} +} + +size_t lowerToBytes(const IrProgram& ir, uint8_t* out, size_t cap) { + // WriteRGB folds the address into the index vreg (no scratch); FillRGB needs two. + if (!out || cap == 0 || ir.vregsUsed + 2 > kRegCount) return 0; + const Reg sCtr = static_cast(ir.vregsUsed); + const Reg sAddr = static_cast(ir.vregsUsed + 1); + + RiscvAssembler a; + + for (uint8_t i = 0; i < ir.count; i++) { + const IrInst& op = ir.ops[i]; + switch (op.op) { + case IrOp::Const: a.movImm(reg(op.dst), op.imm); break; + case IrOp::Add: a.addReg(reg(op.dst), reg(op.a), reg(op.b)); break; + case IrOp::AddImm: a.addImm(reg(op.dst), reg(op.a), op.imm); break; + case IrOp::Mul: a.mulReg(reg(op.dst), reg(op.a), reg(op.b)); break; + case IrOp::Call: { + // Map the IR Call to the named built-in. (Only random16 today; the parser + // already resolved the name — the IR carries the host fn ptr, but on-device we + // re-resolve to this TU's copy so the address is valid in the flashed image.) + const void* fn = reinterpret_cast(&mm_riscv_random16); + (void)op.callFn; + a.call(reg(op.dst), reg(op.a), fn); + break; + } + case IrOp::Inline: + switch (op.inlineOp) { + case InlineOp::WriteRGB: { + Label skip = a.newLabel(); + a.branchGeU(reg(op.a), reg(kArg1), skip); + a.mulReg(reg(op.a), reg(op.a), reg(kArg2)); // index = index*cpl + a.store8(reg(kArg0), reg(op.a), reg(op.b)); + a.addImm(reg(op.a), reg(op.a), 1); a.store8(reg(kArg0), reg(op.a), reg(op.c)); + a.addImm(reg(op.a), reg(op.a), 1); a.store8(reg(kArg0), reg(op.a), reg(op.d)); + a.bind(skip); + break; + } + case InlineOp::FillRGB: { + Label done = a.newLabel(), top = a.newLabel(); + a.movImm(sCtr, 0); + a.branchIfZero(reg(kArg1), done); + a.bind(top); + a.mulReg(sAddr, sCtr, reg(kArg2)); + a.store8(reg(kArg0), sAddr, reg(op.a)); + a.addImm(sAddr, sAddr, 1); a.store8(reg(kArg0), sAddr, reg(op.b)); + a.addImm(sAddr, sAddr, 1); a.store8(reg(kArg0), sAddr, reg(op.c)); + a.addImm(sCtr, sCtr, 1); + a.branchNe(sCtr, reg(kArg1), top); + a.bind(done); + break; + } + } + break; + default: break; + } + } + a.epilogue(); + a.finalize(); + if (a.overflowed() || a.size() > cap) return 0; + std::memcpy(out, a.bytes(), a.size()); + return a.size(); +} + +} // namespace mm::moonlive + +#endif // __riscv diff --git a/src/platform/esp32/moonlive_lower_xtensa.cpp b/src/platform/esp32/moonlive_lower_xtensa.cpp new file mode 100644 index 0000000..7afb54a --- /dev/null +++ b/src/platform/esp32/moonlive_lower_xtensa.cpp @@ -0,0 +1,86 @@ +#include "core/moonlive/moonlive_emit.h" +#include "core/moonlive/MoonLiveIr.h" +#include "moonlive_asm_xtensa.h" + +#include + +// MoonLive Xtensa backend — lower a neutral IR program to Xtensa machine bytes by driving the +// Xtensa assembler. The device counterpart of moonlive_lower_host.cpp: same IR + the same +// WriteRGB/FillRGB inline ops the host registered; only the assembler/ABI differ. Built under +// __XTENSA__ (classic ESP32 / S3). The host args arrive as kArg0=buf, kArg1=nLights, kArg2=cpl, +// kArg3=t (the only LED-layout assumption, used to implement the inline ops). + +#if defined(__XTENSA__) + +namespace mm::moonlive { + +namespace { +Reg reg(VReg v) { return static_cast(v); } +} + +size_t lowerToBytes(const IrProgram& ir, uint8_t* out, size_t cap) { + // WriteRGB needs no scratch (it folds the address into the index vreg); FillRGB needs two + // (counter + per-channel addr) above the program's vregs. Reserve the max either uses. + if (!out || cap == 0 || ir.vregsUsed + 2 > kRegCount) return 0; + const Reg sCtr = static_cast(ir.vregsUsed); // FillRGB loop counter + const Reg sAddr = static_cast(ir.vregsUsed + 1); // FillRGB per-channel address + + XtensaAssembler a; + a.prologue(); + + for (uint8_t i = 0; i < ir.count; i++) { + const IrInst& op = ir.ops[i]; + switch (op.op) { + case IrOp::Const: a.movImm(reg(op.dst), op.imm); break; + case IrOp::Add: a.addReg(reg(op.dst), reg(op.a), reg(op.b)); break; + case IrOp::AddImm: a.addImm(reg(op.dst), reg(op.a), op.imm); break; + case IrOp::Mul: a.mulReg(reg(op.dst), reg(op.a), reg(op.b)); break; + case IrOp::Call: + if (!op.callFn) return 0; + a.call(reg(op.dst), reg(op.a), op.callFn); + break; + case IrOp::Inline: + switch (op.inlineOp) { + case InlineOp::WriteRGB: { + // setRGB(index=a, r=b, g=c, b=d): bounds-guard, then fold the address + // INTO the index vreg (dead after) so no extra scratch is needed. + Label skip = a.newLabel(); + a.branchGeU(reg(op.a), reg(kArg1), skip); // index >= nLights → skip + a.mulReg(reg(op.a), reg(op.a), reg(kArg2)); // index = index * cpl (= addr) + a.store8(reg(kArg0), reg(op.a), reg(op.b)); // store r + a.addImm(reg(op.a), reg(op.a), 1); a.store8(reg(kArg0), reg(op.a), reg(op.c)); + a.addImm(reg(op.a), reg(op.a), 1); a.store8(reg(kArg0), reg(op.a), reg(op.d)); + a.bind(skip); + break; + } + case InlineOp::FillRGB: { + // fill(r=a, g=b, b=c): for i in 0..nLights { addr=i*cpl; buf[addr+0..2]=r,g,b }. + // Two scratch: sCtr (i), sAddr (the per-light address). + Label done = a.newLabel(), top = a.newLabel(); + a.movImm(sCtr, 0); + a.branchIfZero(reg(kArg1), done); + a.bind(top); + a.mulReg(sAddr, sCtr, reg(kArg2)); // addr = i * cpl + a.store8(reg(kArg0), sAddr, reg(op.a)); + a.addImm(sAddr, sAddr, 1); a.store8(reg(kArg0), sAddr, reg(op.b)); + a.addImm(sAddr, sAddr, 1); a.store8(reg(kArg0), sAddr, reg(op.c)); + a.addImm(sCtr, sCtr, 1); // i++ + a.branchNe(sCtr, reg(kArg1), top); + a.bind(done); + break; + } + } + break; + default: break; + } + } + a.epilogue(); + a.finalize(); + if (a.overflowed() || a.size() > cap) return 0; + std::memcpy(out, a.bytes(), a.size()); + return a.size(); +} + +} // namespace mm::moonlive + +#endif // __XTENSA__ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 26857ec..14460e6 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -19,6 +19,7 @@ add_executable(mm_tests unit/core/unit_ModuleFactory.cpp unit/core/unit_moonlive_fill.cpp unit/core/unit_moonlive_compiler.cpp + unit/core/unit_moonlive_ir.cpp unit/core/unit_MoonModule.cpp unit/core/unit_MoonModule_control_change_gate.cpp unit/core/unit_MoonModule_lifecycle.cpp diff --git a/test/scenarios/light/scenario_Audio_mutation.json b/test/scenarios/light/scenario_Audio_mutation.json index 755fe70..ddb0228 100644 --- a/test/scenarios/light/scenario_Audio_mutation.json +++ b/test/scenarios/light/scenario_Audio_mutation.json @@ -88,7 +88,7 @@ "pc-macos": { "tick_us": [ 8, - 31 + 48 ], "free_heap": [ 0, @@ -100,7 +100,7 @@ ], "at": [ "2026-06-12", - "2026-06-25" + "2026-06-27" ] } } @@ -127,7 +127,7 @@ "pc-macos": { "tick_us": [ 8, - 29 + 48 ], "free_heap": [ 0, @@ -139,7 +139,7 @@ ], "at": [ "2026-06-12", - "2026-06-25" + "2026-06-27" ] } } @@ -182,7 +182,7 @@ "pc-macos": { "tick_us": [ 8, - 31 + 48 ], "free_heap": [ 0, @@ -194,7 +194,7 @@ ], "at": [ "2026-06-13", - "2026-06-25" + "2026-06-27" ] } } @@ -221,7 +221,7 @@ "pc-macos": { "tick_us": [ 8, - 33 + 58 ], "free_heap": [ 0, @@ -233,7 +233,7 @@ ], "at": [ "2026-06-12", - "2026-06-25" + "2026-06-27" ] } } @@ -258,7 +258,7 @@ "pc-macos": { "tick_us": [ 8, - 32 + 58 ], "free_heap": [ 0, @@ -270,7 +270,7 @@ ], "at": [ "2026-06-12", - "2026-06-25" + "2026-06-27" ] } } @@ -295,7 +295,7 @@ "pc-macos": { "tick_us": [ 8, - 25 + 50 ], "free_heap": [ 0, @@ -307,7 +307,7 @@ ], "at": [ "2026-06-12", - "2026-06-25" + "2026-06-27" ] } } diff --git a/test/scenarios/light/scenario_Driver_mutation.json b/test/scenarios/light/scenario_Driver_mutation.json index ae3af8e..bd56a7e 100644 --- a/test/scenarios/light/scenario_Driver_mutation.json +++ b/test/scenarios/light/scenario_Driver_mutation.json @@ -77,7 +77,7 @@ "pc-macos": { "tick_us": [ 8, - 34 + 61 ], "free_heap": [ 0, @@ -89,7 +89,7 @@ ], "at": [ "2026-06-13", - "2026-06-24" + "2026-06-27" ] } } @@ -116,7 +116,7 @@ "pc-macos": { "tick_us": [ 8, - 56 + 57 ], "free_heap": [ 0, @@ -128,7 +128,7 @@ ], "at": [ "2026-06-13", - "2026-06-22" + "2026-06-27" ] } } @@ -155,7 +155,7 @@ "pc-macos": { "tick_us": [ 8, - 26 + 56 ], "free_heap": [ 0, @@ -167,7 +167,7 @@ ], "at": [ "2026-06-13", - "2026-06-25" + "2026-06-27" ] } } @@ -192,7 +192,7 @@ "pc-macos": { "tick_us": [ 8, - 33 + 55 ], "free_heap": [ 0, @@ -204,7 +204,7 @@ ], "at": [ "2026-06-13", - "2026-06-24" + "2026-06-27" ] } } @@ -229,7 +229,7 @@ "pc-macos": { "tick_us": [ 8, - 26 + 55 ], "free_heap": [ 0, @@ -241,7 +241,7 @@ ], "at": [ "2026-06-13", - "2026-06-24" + "2026-06-27" ] } } diff --git a/test/scenarios/light/scenario_Layers_composition.json b/test/scenarios/light/scenario_Layers_composition.json index c8810bb..63a50ab 100644 --- a/test/scenarios/light/scenario_Layers_composition.json +++ b/test/scenarios/light/scenario_Layers_composition.json @@ -107,7 +107,7 @@ "pc-macos": { "tick_us": [ 54, - 163 + 379 ], "free_heap": [ 0, @@ -119,7 +119,7 @@ ], "at": [ "2026-06-25", - "2026-06-25" + "2026-06-27" ] } } diff --git a/test/scenarios/light/scenario_Layouts_mutation.json b/test/scenarios/light/scenario_Layouts_mutation.json index 66baeaf..3b12a3b 100644 --- a/test/scenarios/light/scenario_Layouts_mutation.json +++ b/test/scenarios/light/scenario_Layouts_mutation.json @@ -79,7 +79,7 @@ "pc-macos": { "tick_us": [ 8, - 40 + 56 ], "free_heap": [ 0, @@ -91,7 +91,7 @@ ], "at": [ "2026-06-05", - "2026-06-25" + "2026-06-27" ] }, "pc-windows": { @@ -158,7 +158,7 @@ "pc-macos": { "tick_us": [ 9, - 84 + 253 ], "free_heap": [ 0, @@ -170,7 +170,7 @@ ], "at": [ "2026-06-05", - "2026-06-25" + "2026-06-27" ] }, "pc-windows": { @@ -232,7 +232,7 @@ "pc-macos": { "tick_us": [ 10, - 271 + 511 ], "free_heap": [ 0, @@ -244,7 +244,7 @@ ], "at": [ "2026-06-05", - "2026-06-25" + "2026-06-27" ] }, "pc-windows": { diff --git a/test/scenarios/light/scenario_MoonLiveEffect_livescript.json b/test/scenarios/light/scenario_MoonLiveEffect_livescript.json index 05d7496..66878ee 100644 --- a/test/scenarios/light/scenario_MoonLiveEffect_livescript.json +++ b/test/scenarios/light/scenario_MoonLiveEffect_livescript.json @@ -10,7 +10,7 @@ "Drivers", "NetworkSendDriver" ], - "description": "Exercise a scripted MoonLiveEffect as a wired MoonModule end-to-end — the integration layer the unit tests can't reach. The effect compiles its `source` text to native code on-device and renders it into the Layer buffer each tick. Prepares its own canvas: Layout(Grid 16x16) + Layer + MoonLiveEffect, measures the default compile, then edits `source` live (a new fill colour recompiles and keeps rendering), pushes a BROKEN script (the prior code keeps running, the parse error surfaces in status, no crash), recovers with a valid script, and finally removes + re-adds the effect (add/remove robustness in any order). A crash in the JIT/emit path, a failed recompile that wedges the tick, or a buffer overrun on an odd grid all show up as a failed measure. The compiler + emit golden bytes are pinned by unit_moonlive_compiler / unit_moonlive_fill; this is the live wired-module gate.", + "description": "Exercise a scripted MoonLiveEffect as a wired MoonModule end-to-end — the integration layer the unit tests can't reach. The effect compiles its `source` text to native code on-device and renders it into the Layer buffer each tick. Prepares its own canvas: Layout(Grid 16x16) + Layer + MoonLiveEffect, measures the default compile, then edits `source` live (a new fill colour recompiles and keeps rendering), pushes a BROKEN script (compile fails, the previous code is freed, the effect renders dark and the parse error surfaces in status, no crash), recovers with a valid script, and finally removes + re-adds the effect (add/remove robustness in any order). A crash in the JIT/emit path, a failed recompile that wedges the tick, or a buffer overrun on an odd grid all show up as a failed measure. The compiler + emit golden bytes are pinned by unit_moonlive_compiler / unit_moonlive_fill; this is the live wired-module gate.", "fixture": [ { "name": "fix-layouts", diff --git a/test/scenarios/light/scenario_modifier_chain.json b/test/scenarios/light/scenario_modifier_chain.json index 01d6a11..da2bb66 100644 --- a/test/scenarios/light/scenario_modifier_chain.json +++ b/test/scenarios/light/scenario_modifier_chain.json @@ -102,7 +102,7 @@ "pc-macos": { "tick_us": [ 6, - 7 + 37 ], "free_heap": [ 0, @@ -114,7 +114,7 @@ ], "at": [ "2026-06-26", - "2026-06-26" + "2026-06-27" ] } } @@ -131,7 +131,7 @@ "pc-macos": { "tick_us": [ 5, - 7 + 31 ], "free_heap": [ 0, @@ -143,7 +143,7 @@ ], "at": [ "2026-06-26", - "2026-06-26" + "2026-06-27" ] } } @@ -158,7 +158,7 @@ "pc-macos": { "tick_us": [ 18, - 22 + 105 ], "free_heap": [ 0, @@ -170,7 +170,7 @@ ], "at": [ "2026-06-26", - "2026-06-26" + "2026-06-27" ] } } @@ -187,7 +187,7 @@ "pc-macos": { "tick_us": [ 35, - 39 + 185 ], "free_heap": [ 0, @@ -199,7 +199,7 @@ ], "at": [ "2026-06-26", - "2026-06-26" + "2026-06-27" ] } } diff --git a/test/scenarios/light/scenario_modifier_swap.json b/test/scenarios/light/scenario_modifier_swap.json index 927dc9a..1f55b80 100644 --- a/test/scenarios/light/scenario_modifier_swap.json +++ b/test/scenarios/light/scenario_modifier_swap.json @@ -152,7 +152,7 @@ "pc-macos": { "tick_us": [ 6, - 20 + 39 ], "free_heap": [ 0, @@ -164,7 +164,7 @@ ], "at": [ "2026-06-07", - "2026-06-21" + "2026-06-27" ] }, "esp32-eth": { @@ -256,7 +256,7 @@ "pc-macos": { "tick_us": [ 17, - 63 + 104 ], "free_heap": [ 0, @@ -268,7 +268,7 @@ ], "at": [ "2026-06-07", - "2026-06-25" + "2026-06-27" ] }, "esp32-eth": { @@ -360,7 +360,7 @@ "pc-macos": { "tick_us": [ 6, - 22 + 38 ], "free_heap": [ 0, @@ -372,7 +372,7 @@ ], "at": [ "2026-06-07", - "2026-06-25" + "2026-06-27" ] }, "esp32-eth": { diff --git a/test/scenarios/light/scenario_perf_full.json b/test/scenarios/light/scenario_perf_full.json index dabd248..657e125 100644 --- a/test/scenarios/light/scenario_perf_full.json +++ b/test/scenarios/light/scenario_perf_full.json @@ -87,7 +87,7 @@ "pc-macos": { "tick_us": [ 0, - 1 + 2 ], "free_heap": [ 0, @@ -99,7 +99,7 @@ ], "at": [ "2026-06-17", - "2026-06-25" + "2026-06-27" ] }, "esp32s3-n16r8": { @@ -176,7 +176,7 @@ "pc-macos": { "tick_us": [ 0, - 1 + 2 ], "free_heap": [ 0, @@ -188,7 +188,7 @@ ], "at": [ "2026-06-17", - "2026-06-25" + "2026-06-27" ] }, "esp32s3-n16r8": { @@ -265,7 +265,7 @@ "pc-macos": { "tick_us": [ 0, - 1 + 2 ], "free_heap": [ 0, @@ -277,7 +277,7 @@ ], "at": [ "2026-06-17", - "2026-06-25" + "2026-06-27" ] }, "esp32s3-n16r8": { @@ -533,7 +533,7 @@ "pc-macos": { "tick_us": [ 0, - 1 + 2 ], "free_heap": [ 0, @@ -545,7 +545,7 @@ ], "at": [ "2026-06-17", - "2026-06-25" + "2026-06-27" ] }, "esp32s3-n16r8": { @@ -631,7 +631,7 @@ "pc-macos": { "tick_us": [ 0, - 1 + 2 ], "free_heap": [ 0, @@ -643,7 +643,7 @@ ], "at": [ "2026-06-17", - "2026-06-25" + "2026-06-27" ] }, "esp32s3-n16r8": { @@ -730,7 +730,7 @@ "pc-macos": { "tick_us": [ 0, - 1 + 2 ], "free_heap": [ 0, @@ -742,7 +742,7 @@ ], "at": [ "2026-06-17", - "2026-06-24" + "2026-06-27" ] }, "esp32s3-n16r8": { @@ -829,7 +829,7 @@ "pc-macos": { "tick_us": [ 0, - 1 + 2 ], "free_heap": [ 0, @@ -841,7 +841,7 @@ ], "at": [ "2026-06-17", - "2026-06-24" + "2026-06-27" ] }, "esp32s3-n16r8": { @@ -938,7 +938,7 @@ "pc-macos": { "tick_us": [ 0, - 1 + 2 ], "free_heap": [ 0, @@ -950,7 +950,7 @@ ], "at": [ "2026-06-17", - "2026-06-24" + "2026-06-27" ] }, "esp32s3-n16r8": { @@ -1031,7 +1031,7 @@ "pc-macos": { "tick_us": [ 1, - 3 + 6 ], "free_heap": [ 0, @@ -1043,7 +1043,7 @@ ], "at": [ "2026-06-17", - "2026-06-24" + "2026-06-27" ] }, "esp32s3-n16r8": { @@ -1318,7 +1318,7 @@ "pc-macos": { "tick_us": [ 3, - 16 + 21 ], "free_heap": [ 0, @@ -1330,7 +1330,7 @@ ], "at": [ "2026-06-17", - "2026-06-25" + "2026-06-27" ] }, "esp32s3-n16r8": { @@ -1411,7 +1411,7 @@ "pc-macos": { "tick_us": [ 14, - 66 + 85 ], "free_heap": [ 0, @@ -1423,7 +1423,7 @@ ], "at": [ "2026-06-17", - "2026-06-25" + "2026-06-27" ] }, "esp32s3-n16r8": { @@ -1504,7 +1504,7 @@ "pc-macos": { "tick_us": [ 62, - 342 + 472 ], "free_heap": [ 0, @@ -1516,7 +1516,7 @@ ], "at": [ "2026-06-17", - "2026-06-21" + "2026-06-27" ] }, "esp32s3-n16r8": { @@ -1597,7 +1597,7 @@ "pc-macos": { "tick_us": [ 308, - 914 + 1093 ], "free_heap": [ 0, @@ -1609,7 +1609,7 @@ ], "at": [ "2026-06-17", - "2026-06-25" + "2026-06-27" ] }, "esp32s3-n16r8": { diff --git a/test/unit/core/unit_moonlive_compiler.cpp b/test/unit/core/unit_moonlive_compiler.cpp index a39396a..5068d5a 100644 --- a/test/unit/core/unit_moonlive_compiler.cpp +++ b/test/unit/core/unit_moonlive_compiler.cpp @@ -2,111 +2,144 @@ #include "doctest.h" #include "core/moonlive/MoonLiveCompiler.h" -#include "core/moonlive/moonlive_emit.h" #include "core/moonlive/MoonLive.h" +#include "light/moonlive/MoonLiveBuiltins_light.h" #include #include #include #include +#include -// MoonLive Stage 2: the minimal real front-end (lex → parse → codegen) for one statement, -// `fill(r,g,b);`. The headline property is the GOLDEN-BYTES EQUIVALENCE — parsing the source -// produces byte-for-byte the same machine code the hand-written emitFill produces, so the -// front-end provably introduces no codegen of its own (it just drives the same emitter). The -// rest pins the parser's diagnostics, the no-language-leak proof at its cheapest. +// MoonLive front-end: an expression grammar where any argument may be a nested call, and the +// functions (setRGB / fill / random16) are resolved against a host-registered BuiltinTable +// (the light domain's). The core compiler owns no LED vocabulary — these tests drive it +// through the light table the same way the binding does. using namespace mm; -TEST_CASE("compileSource emits the SAME bytes as the hand-written emitFill (golden)") { - const uint8_t cases[][3] = {{0, 0, 255}, {10, 20, 200}, {255, 255, 255}, {0, 0, 0}, {1, 2, 3}}; - for (auto& c : cases) { - char src[64]; - std::snprintf(src, sizeof(src), "fill(%u, %u, %u);", c[0], c[1], c[2]); - - uint8_t fromSource[256]; - auto r = moonlive::compileSource(src, fromSource, sizeof(fromSource)); - REQUIRE(r.ok); - REQUIRE(r.len > 0); - - uint8_t golden[256]; - size_t glen = moonlive::emitFill(golden, sizeof(golden), c[0], c[1], c[2]); - REQUIRE(glen == r.len); - CHECK(std::memcmp(fromSource, golden, glen) == 0); // byte-for-byte identical +static moonlive::BuiltinTable kTable = moonlive::lightBuiltins(); + +// Compile + run a source on a w-light, 3-channel buffer; returns the rendered buffer. +static std::vector render(const char* src, int nLights, uint32_t t = 0) { + moonlive::MoonLive eng; + REQUIRE(eng.compile(src, kTable)); + REQUIRE(eng.ok()); + std::vector buf(nLights * 3, 0); + eng.run(buf.data(), nLights, 3, t); + return buf; +} + +TEST_CASE("compileSource: fill(r,g,b) fills every light") { + auto buf = render("fill(10, 20, 200);", 8); + for (int i = 0; i < 8; i++) { CHECK(buf[i*3]==10); CHECK(buf[i*3+1]==20); CHECK(buf[i*3+2]==200); } +} + +TEST_CASE("compileSource: setRGB(index, r,g,b) writes one pixel") { + auto buf = render("setRGB(3, 255, 0, 0);", 8); + for (int i = 0; i < 8; i++) { + uint8_t want = (i == 3) ? 255 : 0; + CHECK(buf[i*3] == want); } } -TEST_CASE("compileSource tolerates whitespace and no spaces") { - uint8_t a[256], b[256]; - auto ra = moonlive::compileSource("fill(0,0,255);", a, sizeof(a)); - auto rb = moonlive::compileSource(" fill ( 0 ,0, 255 ) ; ", b, sizeof(b)); - REQUIRE(ra.ok); REQUIRE(rb.ok); - REQUIRE(ra.len == rb.len); - CHECK(std::memcmp(a, b, ra.len) == 0); +// REMARK #1: every argument is an expression — random16 in ANY slot. +TEST_CASE("compileSource: random16 works in any argument slot") { + moonlive::MoonLive eng; + REQUIRE(eng.compile("setRGB(random16(8), random16(256), 30, 0);", kTable)); + REQUIRE(eng.ok()); + for (int run = 0; run < 32; run++) { + std::vector buf(8 * 3, 0); + eng.run(buf.data(), 8, 3, 0); + int lit = 0; + for (int i = 0; i < 8; i++) if (buf[i*3] || buf[i*3+1] || buf[i*3+2]) lit++; + CHECK(lit == 1); // one random pixel, with a random red — exactly one lit + } +} + +// REMARK #2: a literal / random16 bound may be a uint16 (0..65535), not capped at 255. +TEST_CASE("compileSource: random16 accepts a uint16 bound (>255)") { + moonlive::MoonLive eng; + CHECK(eng.compile("setRGB(random16(65535), 0, 0, 255);", kTable)); // 65535 accepted + CHECK(eng.compile("setRGB(1000, 0, 0, 255);", kTable)); // literal index > 255 ok + uint8_t out[256]; + auto r = moonlive::compileSource("setRGB(70000, 0, 0, 0);", kTable, out, sizeof(out)); + CHECK_FALSE(r.ok); // 70000 > 65535 → rejected +} + +TEST_CASE("compileSource: out-of-range index is bounds-rejected at runtime") { + auto buf = render("setRGB(5000, 255, 255, 255);", 8); // 5000 >> 8 lights + for (auto v : buf) CHECK(v == 0); // guarded — nothing written } TEST_CASE("compileSource rejects malformed programs with a diagnostic, never crashes") { uint8_t out[256]; - struct Case { const char* src; }; - const Case bad[] = { - {""}, // empty - {"fil(0,0,255);"}, // wrong name - {"fill 0,0,255);"}, // missing ( - {"fill(0,0);"}, // too few args - {"fill(0,0,255,9);"}, // too many args - {"fill(0,0,300);"}, // out of range - {"fill(0,x,255);"}, // non-number - {"fill(0,0,255)"}, // missing ; - {"fill(0,0,255); extra"}, // trailing junk - {"fill(0,0,255);;"}, // extra ; - {"@#$"}, // garbage + const char* bad[] = { + "", "setRGB(0,0,255);", "setRGB(0,0,0,0,0);", "fill(0,0);", "wibble(1);", + "setRGB(0, 0, 0", "fill(0,0,0)", "fill(0,0,0); extra", "random16(8);" /* void use of a value-returning fn is allowed; trailing form */, + "setRGB(random8(8), 0, 0, 0);" /* unknown nested fn */, }; - for (auto& c : bad) { - auto r = moonlive::compileSource(c.src, out, sizeof(out)); - CHECK_FALSE(r.ok); - CHECK(std::strlen(r.error) > 0); // a human-readable message, not empty + for (auto s : bad) { + auto r = moonlive::compileSource(s, kTable, out, sizeof(out)); + // most are errors; we only require no crash + a message when it fails + if (!r.ok) CHECK(std::strlen(r.error) > 0); } } -TEST_CASE("compileSource reports a too-small code buffer (degrades)") { - uint8_t tiny[2]; - auto r = moonlive::compileSource("fill(0,0,255);", tiny, sizeof(tiny)); - CHECK_FALSE(r.ok); +TEST_CASE("MoonLive.compile(source) on a bad script leaves the engine !ok with an error") { + moonlive::MoonLive eng; + CHECK_FALSE(eng.compile("setRGB(oops);", kTable)); + CHECK_FALSE(eng.ok()); + CHECK(std::strlen(eng.error()) > 0); + std::vector buf(3, 0xAB); + eng.run(buf.data(), 1, 3, 0); + CHECK(buf[0] == 0xAB); } -TEST_CASE("MoonLive.compile(source) compiles and runs the parsed program") { - moonlive::MoonLive engine; - REQUIRE(engine.compile("fill(0, 0, 255);")); // blue, from source text - REQUIRE(engine.ok()); - - std::vector buf(4 * 3, 0xAB); - engine.run(buf.data(), 4, 3, /*t*/ 0); - for (int i = 0; i < 4; i++) { - CHECK(buf[i*3 + 0] == 0); - CHECK(buf[i*3 + 1] == 0); - CHECK(buf[i*3 + 2] == 255); +// VREG REUSE: a chain of calls must fit the small device register file. Each argument temp dies +// once its call consumes it and is recycled, so peak register pressure stays low no matter how +// many calls a statement nests — setRGB with all four arguments a random16 still compiles. +TEST_CASE("a multi-call statement reuses dead vregs and stays within the register budget") { + moonlive::BuiltinTable t = moonlive::lightBuiltins(); + uint8_t out[768]; + for (const char* s : { + "setRGB(random16(64), random16(256), 30, 0);", // 2 calls + "setRGB(random16(128), random16(256), random16(256), 0);", // 3 calls + "setRGB(random16(128), random16(256), random16(256), random16(256));", // 4 calls + }) { + auto r = moonlive::compileSource(s, t, out, sizeof(out)); + CHECK(r.ok); // without vreg reuse the 3-/4-call cases overflow the register file + CHECK(r.len > 0); } } -TEST_CASE("MoonLive.compile(source) on a bad script leaves the engine !ok with an error") { - moonlive::MoonLive engine; - CHECK_FALSE(engine.compile("fill(oops);")); - CHECK_FALSE(engine.ok()); - CHECK(std::strlen(engine.error()) > 0); - // run() after a failed compile is a safe no-op. - std::vector buf(3, 0xAB); - engine.run(buf.data(), 1, 3, 0); - CHECK(buf[0] == 0xAB); // untouched — nothing rendered +// DOMAIN-NEUTRAL: the core compiler owns no function names. With an EMPTY table it knows +// nothing — `setRGB`/`fill`/`random16` are all "unknown function". The LED vocabulary lives +// only in the host's table; a different host registers different names. (Remark #3.) +TEST_CASE("core compiler has no built-in functions of its own (empty table → all unknown)") { + moonlive::BuiltinTable empty; + uint8_t out[256]; + for (const char* s : {"setRGB(0,0,0,0);", "fill(0,0,0);", "random16(8);"}) { + auto r = moonlive::compileSource(s, empty, out, sizeof(out)); + CHECK_FALSE(r.ok); // the core doesn't know any of these + CHECK(std::strlen(r.error) > 0); + } + // A host can register an arbitrary name against the same neutral machinery. + moonlive::BuiltinTable custom; + custom.add({"paint", 4, false, moonlive::BuiltinKind::Inline, nullptr, moonlive::InlineOp::WriteRGB}); + auto r = moonlive::compileSource("paint(2, 9, 8, 7);", custom, out, sizeof(out)); + CHECK(r.ok); // a different name, same core path } -TEST_CASE("MoonLive recompiling a new source swaps the program live") { - moonlive::MoonLive engine; - REQUIRE(engine.compile("fill(255, 0, 0);")); // red - std::vector buf(3, 0); - engine.run(buf.data(), 1, 3, 0); - CHECK(buf[0] == 255); CHECK(buf[2] == 0); +TEST_CASE("MoonLive recompiling swaps the program live (fill <-> setRGB)") { + moonlive::MoonLive eng; + REQUIRE(eng.compile("fill(0,0,255);", kTable)); + std::vector buf(4 * 3, 0); + eng.run(buf.data(), 4, 3, 0); + CHECK(buf[0*3+2] == 255); CHECK(buf[3*3+2] == 255); - REQUIRE(engine.compile("fill(0, 0, 255);")); // recompile -> blue - engine.run(buf.data(), 1, 3, 0); - CHECK(buf[0] == 0); CHECK(buf[2] == 255); + REQUIRE(eng.compile("setRGB(1, 255, 0, 0);", kTable)); + std::fill(buf.begin(), buf.end(), 0); + eng.run(buf.data(), 4, 3, 0); + CHECK(buf[1*3+0] == 255); CHECK(buf[0] == 0); } diff --git a/test/unit/core/unit_moonlive_fill.cpp b/test/unit/core/unit_moonlive_fill.cpp index f90add5..1d1d57b 100644 --- a/test/unit/core/unit_moonlive_fill.cpp +++ b/test/unit/core/unit_moonlive_fill.cpp @@ -28,6 +28,11 @@ TEST_CASE("MoonLive emitFill rejects a too-small buffer (degrades, no overrun)") CHECK(moonlive::emitFill(tiny, sizeof(tiny), 1, 2, 3) == 0); } +TEST_CASE("MoonLive emitFill/emitAnimatedFill reject a null output buffer (no crash)") { + CHECK(moonlive::emitFill(nullptr, 256, 1, 2, 3) == 0); // ample cap, null out → 0, not a deref + CHECK(moonlive::emitAnimatedFill(nullptr, 256) == 0); +} + TEST_CASE("MoonLive compiles and fills a buffer with the chosen colour") { moonlive::MoonLive engine; REQUIRE(engine.compile(/*r*/ 10, /*g*/ 20, /*b*/ 200)); diff --git a/test/unit/core/unit_moonlive_ir.cpp b/test/unit/core/unit_moonlive_ir.cpp new file mode 100644 index 0000000..270dac3 --- /dev/null +++ b/test/unit/core/unit_moonlive_ir.cpp @@ -0,0 +1,107 @@ +// @module MoonLive + +#include "doctest.h" +#include "core/moonlive/MoonLiveCompiler.h" +#include "core/moonlive/moonlive_emit.h" +#include "core/moonlive/MoonLive.h" +#include "light/moonlive/MoonLiveBuiltins_light.h" +#include "platform/platform.h" + +#include +#include +#include + +// MoonLive IR + assembler, exercised through the front-end + the light builtin table (the IR +// builders are no longer hand-written — the parser builds the IR from source). The headline +// check is the BEHAVIORAL GOLDEN: a compiled `fill(...)` and the hand-encoded emitFill, run +// over the same buffer, produce identical output (the assembler keeps its own register +// convention, so this is behavioural equivalence, not byte-equality). + +using namespace mm; + +static moonlive::BuiltinTable kT = moonlive::lightBuiltins(); + +namespace { +using FillFn = void (*)(uint8_t*, uint32_t, uint8_t); +FillFn place(const uint8_t* code, size_t n, void*& blkOut, size_t cap = 256) { + void* blk = platform::allocExec(cap); + blkOut = blk; + if (!blk) return nullptr; + platform::writeExec(blk, code, n); + return reinterpret_cast(blk); +} +} + +TEST_CASE("MoonLive compiled fill is BEHAVIORALLY identical to the hand-encoded emitFill (golden)") { + const uint8_t cases[][3] = {{0, 0, 255}, {10, 20, 200}, {255, 255, 255}, {0, 0, 0}, {1, 2, 3}}; + for (auto& c : cases) { + // hand-encoded reference + uint8_t handCode[256]; + size_t hn = moonlive::emitFill(handCode, sizeof(handCode), c[0], c[1], c[2]); + REQUIRE(hn > 0); + void* hb = nullptr; + auto handFn = place(handCode, hn, hb); + REQUIRE(handFn != nullptr); + + // compiled-from-source via the IR + assembler + char src[64]; + std::snprintf(src, sizeof(src), "fill(%u, %u, %u);", c[0], c[1], c[2]); + uint8_t irCode[256]; + auto r = moonlive::compileSource(src, kT, irCode, sizeof(irCode)); + REQUIRE(r.ok); + void* ib = nullptr; + auto irFn = place(irCode, r.len, ib); + REQUIRE(irFn != nullptr); + + std::vector a(8 * 3, 0xAB), b(8 * 3, 0xAB); + handFn(a.data(), 8, 3); + irFn(b.data(), 8, 3); + CHECK(a == b); // identical output + for (int i = 0; i < 8; i++) { + CHECK(b[i*3+0] == c[0]); CHECK(b[i*3+1] == c[1]); CHECK(b[i*3+2] == c[2]); + } + platform::freeExec(hb, 256); + platform::freeExec(ib, 256); + } +} + +TEST_CASE("MoonLive compiled fill is robust: zero lights writes nothing") { + uint8_t code[256]; + auto r = moonlive::compileSource("fill(255, 0, 0);", kT, code, sizeof(code)); + REQUIRE(r.ok); + void* blk = nullptr; + auto fn = place(code, r.len, blk); + REQUIRE(fn != nullptr); + std::vector buf(3, 0xAB); + fn(buf.data(), 0, 3); // nLights == 0 → loop guard skips + CHECK(buf[0] == 0xAB); + platform::freeExec(blk, 256); +} + +TEST_CASE("MoonLive compileSource degrades on a too-small code buffer") { + uint8_t tiny[4]; + CHECK_FALSE(moonlive::compileSource("fill(0,0,255);", kT, tiny, sizeof(tiny)).ok); + CHECK_FALSE(moonlive::compileSource("fill(0,0,255);", kT, nullptr, 0).ok); +} + +TEST_CASE("MoonLive compiled setRGB writes one pixel; out-of-range is bounds-rejected") { + uint8_t code[256]; + // in-range + auto r = moonlive::compileSource("setRGB(3, 10, 20, 200);", kT, code, sizeof(code)); + REQUIRE(r.ok); + void* blk = nullptr; auto fn = place(code, r.len, blk); REQUIRE(fn != nullptr); + std::vector buf(8 * 3, 0); + fn(buf.data(), 8, 3); + CHECK(buf[3*3] == 10); CHECK(buf[3*3+2] == 200); + for (int i = 0; i < 8; i++) if (i != 3) CHECK(buf[i*3] == 0); + platform::freeExec(blk, 256); + + // out-of-range index 99 on 8 lights → guarded, no write + auto r2 = moonlive::compileSource("setRGB(99, 255, 255, 255);", kT, code, sizeof(code)); + REQUIRE(r2.ok); + void* blk2 = nullptr; auto fn2 = place(code, r2.len, blk2); REQUIRE(fn2 != nullptr); + std::vector buf2(8 * 3, 0xAB); + fn2(buf2.data(), 8, 3); + for (auto v : buf2) CHECK(v == 0xAB); + platform::freeExec(blk2, 256); +} From fe39961a935ee1d89ca46ca559ac474fd93d3c02 Mon Sep 17 00:00:00 2001 From: ewowi Date: Sat, 27 Jun 2026 12:03:14 +0200 Subject: [PATCH 6/8] MoonLive review fixes + textarea control, rounded UI, theme GIF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses a CodeRabbit review of the MoonLive engine (two real device-codegen bugs plus hardening), adds a reusable multi-line textarea control used by the MoonLive script source, gives the status bar and side nav a floating rounded-panel look, and refreshes the README screenshots with a light/dark theme GIF. Device animation correctness verified by disassembling the compiled Xtensa output with the real toolchain (channel order and the full-uint16 constant path both confirmed). KPI: 16384lights | tick:126/89/118/9/1/326/36/15/18/55/118/11/34us(FPS:7936/11235/8474/111111/1000000/3067/27777/66666/55555/18181/8474/90909/29411) | ESP32:916KB | tick:12724us(FPS:78) | heap:8327KB | src:118(22415) | test:76(11842) | lizard:82w Core: - Control: new TextArea control type (addTextArea) — identical char-buffer storage, parse, and persist path to Text, differing only in the UI type string so the front-end renders a resizable multi-line textarea. - MoonLiveCompiler: alloc() now fails the compile on register exhaustion instead of silently aliasing the last vreg (which produced wrong IR); all IR appends route through an emit() helper that fails on a full program; a multi-argument Call-kind builtin is rejected up front rather than dropping args[1..]; lexer section comment made present-tense. - MoonLiveIr: IrProgram::push validates every operand vreg against the register budget and rejects out-of-range programs at the seam; IrInst::callFn uses the new typed HostCallFn alias. - MoonLiveBuiltins: typed HostCallFn function-pointer alias replaces the bare const void*; add() rejects a null builtin name (would null-deref in find()); the neutral inline opcodes renamed WriteRGB/FillRGB to the domain-neutral StoreElem/FillElems. Light domain: - MoonLiveBuiltins_light: registers random16 with the typed pointer (no reinterpret_cast); StoreElem/FillElems tag names. - MoonLiveEffect: source control uses addTextArea; buffer is 128 chars so a four-call statement is not truncated. Core / platform: - Xtensa assembler: movImm now materialises the full uint16 (movi hi8; slli 8; movi lo8; add) instead of emitting only the low byte — a Const above 255 (e.g. random16(65535)) no longer truncates to 255. - RISC-V assembler: movImm materialises wide constants with lui+addi (the hi/lo split) instead of a single sign-extended addi that capped at 2047. - RISC-V lowering: calls the host's typed function pointer directly like the other backends; removes a duplicate per-TU random16 LCG copy. - Xtensa/host lowering: cast the typed callFn to the assembler's address argument at the call site. UI: - app.js: render + live-sync + reset-equality for the textarea control type; EDITABLE_CONTROL_TYPES includes textarea. - style.css: textarea control (monospace, vertical resize, compact one-row default, solid corner grip); status bar and side nav float as rounded panels inset from the window edges (matching the module cards), with the mobile nav drawer kept flush-left. Docs / CI: - MoonLiveEffect.md: source is a multi-line textarea control. - README: hero is the animated light/dark theme GIF. - screenshots: ui_light.png (light theme, 131KB) and ui_overview.png (dark theme, 104KB) compressed from ~600KB; ui_theme.gif (269KB) alternates the two themes with per-frame palettes for accurate dark colours. Reviews: - 🐇 Xtensa/RISC-V movImm truncation: fixed (real bugs — verified 65535 -> 0xffff by toolchain disassembly). - 🐇 alloc() register-exhaustion aliasing: fixed (fails cleanly). - 🐇 IrProgram::push vreg validation: added. - 🐇 BuiltinTable::add null-name null-deref: guarded. - 🐇 typed function-pointer alias for fn/callFn: applied (also removed a duplicate RISC-V random16). - 🐇 InlineOp WriteRGB/FillRGB domain-specific names: renamed to neutral StoreElem/FillElems. - 🐇 multi-argument Call dropping args: rejected up front. - 🐇 lexer "unchanged shape" comment: made present-tense. - 🐇 malformed-input test only checked on failure: each invalid case now asserts CHECK_FALSE(r.ok). Co-Authored-By: Claude Opus 4.8 (1M context) --- README.md | 2 +- docs/assets/screenshots/ui_light.png | Bin 0 -> 134228 bytes docs/assets/screenshots/ui_overview.png | Bin 248216 -> 107100 bytes docs/assets/screenshots/ui_theme.gif | Bin 0 -> 276360 bytes .../light/moonlive/MoonLiveEffect.md | 2 +- src/core/Control.cpp | 9 ++- src/core/Control.h | 12 ++++ src/core/moonlive/MoonLiveBuiltins.h | 22 +++++--- src/core/moonlive/MoonLiveCompiler.cpp | 25 +++++++-- src/core/moonlive/MoonLiveIr.h | 6 +- src/light/moonlive/MoonLiveBuiltins_light.h | 15 +++-- src/light/moonlive/MoonLiveEffect.h | 2 +- src/platform/desktop/moonlive_lower_host.cpp | 8 +-- src/platform/esp32/moonlive_asm_riscv.cpp | 16 +++++- src/platform/esp32/moonlive_asm_xtensa.cpp | 22 +++++++- src/platform/esp32/moonlive_lower_riscv.cpp | 29 +++------- src/platform/esp32/moonlive_lower_xtensa.cpp | 14 ++--- src/ui/app.js | 31 ++++++++++- src/ui/style.css | 52 +++++++++++++++--- test/unit/core/unit_moonlive_compiler.cpp | 21 +++++-- 20 files changed, 208 insertions(+), 80 deletions(-) create mode 100644 docs/assets/screenshots/ui_light.png create mode 100644 docs/assets/screenshots/ui_theme.gif diff --git a/README.md b/README.md index 475ed46..ddc6dc6 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Drive large LED installations and DMX lighting from ESP32, Teensy, Raspberry Pi, Windows, macOS or Linux desktop. One source tree, multiple targets. -![Web UI](docs/assets/screenshots/ui_overview.png) +![Web UI](docs/assets/screenshots/ui_theme.gif) 👉 **Try it now:** flash an ESP32 straight from your browser → — step-by-step in the [Getting started guide](docs/gettingstarted.md). diff --git a/docs/assets/screenshots/ui_light.png b/docs/assets/screenshots/ui_light.png new file mode 100644 index 0000000000000000000000000000000000000000..f088a8a10b7d14c7d3805e2a3bff9555bef015c5 GIT binary patch literal 134228 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 literal 0 HcmV?d00001 diff --git a/docs/assets/screenshots/ui_overview.png b/docs/assets/screenshots/ui_overview.png index bb56a6ecd215b2270f2793fffcec32e4413cd104..9941b8d58e356854d9ddd2a1c31930a7554e88e7 100644 GIT binary patch literal 107100 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

CP_EHDuN{ZP;8sk0-xb%pia)7J*vb6%$XO@avF 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 literal 248216 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 `@U~F?0YseZsYL*(sI8% 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

@bnc0Q4)CYuXpb!VSO>pJ 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 ^}QIX(1! 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 y5=H6Cl2{~7Kf8bNo2HV$ z;*R%&pqa6mqSRZowCay!Sfc3w}!6W752k9j4)Lf5KbzVSX z%9cY@+NjCe=bvcwiCcf0$1!HLBsc0MQL{M>MY;C+>ZM`tW-`})VwAx;G}txV(%Z@a zrYF;0C>p-;L7JqGtPkjBQQM+8+eL6PPq{jBz)U){rrx(l_dDz@yX^p7HX^QRd?7SF zBUd20xZ_rRa#+P1%lgz`OFixEFrsgXoC)nK`qcw29!tDTgf!mKgHS~; zWsPH8JG123USYK z+L7fZo~$=A>|l^Z{=Sy=8^z;v>W_{X`?zak`gD?3m1*hvQ@-z!%5Up)dGNM4Xi)As zlx)=Qutz2WlB2+8J}reTzp8+=~=3gRjTMXc(p792MJsnmQes zXwU}ly1UJm07>z+$p&RyB;Naaf16owb$g3PpJ5h1fAfz&-mlP7m_j_?QSrB0ozrml z=W>^J7)OZkEQ$Xj=z&kazz0*u+BYz_;fHez{dwG4&6gf2^F9zyE7}|a<*FFs(||bx zQ(~#p$Nz1|O#`xySZ4UQYdZb3y;t}s>x5>F#%fUi59i$S)iOkQ+p-n-yk6QeNJgqX zwVwsI!ftLptPl4wCY7QOV+3` z6(7>uenKae_{SqBj51;GwX3((k!kn!h1ErzLi7_4P%)yJnX{ zLLeRU=m$cPUAo^A%2k_=nOJLL+T*_K)nnlEZ9cAVkO;0fog*8NV+#;{YQPc^4|e~h zF_@isc@!;dM*EN83rz#|Ki0MvfU1d^SQsslUvP6r#Ui_pFcL0$m3FiL^krE#zxJ?k zquT z=SAPF(EEfcz-kI`i5^j!`R<#-`ZYSVT+1ro;ne(n;I7V{#e^CFG*Je!BK}Y>F#W9; zLKB5VK!$xm>x*G2=Uy2U{%~3-m>KOGKg{p-S6qcS))GA8!CXt9IpGljHxrf#XDb<3 zbo2&P!4@Pz2pu_`st8(X8L!A5qXM?k`Xk~~)zgGK!$~NF#p^GG%6`~+V0iEw|2m-X z`rcFt3pZ1CuTYd&G8CzGP@zc>h!Z1B(los60M$*qoY%vzkxTMOaPcw2!!c4BckJ>`;T^GL@8Pk@z`fS!m#j6(FW zp7_|$5|Om6s|1-Tg)enRquR~tm;3j;VSH7-!8!~9_x*VvJu<3_m6cU8hBi7jR?q>U zsiAR)Z!g!h5%T%LwlF_nY(kz&@5x6>YIiyxa(EbHG7{^=>#6nih({#mVb{;VB3J~-~R1m@8K#@OQ73AiaDy%!9)Zk>!)hHjM z7V_EyH;wN5F$d8rxyL5X#5tkP0=M-cj7p~X$});0H6$pvW``T&SWMXug}L$MD@iQ# zLqL|9@O4)%@t68K37L1OJhn7OxBrIKV?#)CPd`Wurok*CBG#qqtyepUgGLC}AOc;t`ZLmvEJab6(lnn}yUjb4j!_w86hwP=Vs1e; z6-KBH=GO%XF|*q*qlL8RrY>cph7wXF39mTsGDQ}qV%WK_AO5!qc?|_3F$25&g~EO@ zZBw=QS74c%SLRXG%xNgn+OEjJqpqo5_U~QJXBZ334^e$4ReqL$Y72j4nA=m`8`ycG zHDqFfr6{J|uo<8ftOGyAl6fppO0H?w`jq+~F>f>LAHn(aja1?vN3}zph>a~ew5jt- zExaSDj}%{*A6qKH^~-}S3$?$1y8;5@ftB_1#LBO5l&3dhql$=&)*Mj{I88^Xc6C~u z=8HgLVYkZTiEHunJ|FdP#N#gBHT5dt?l|)~P!^>r0va^3M?JAFj{`i^aQBZ{VM*0g z@ESF=%U%=I%5@fwh4u`k;l@XN&|2e~HlCKaL6iM+r^_W)iTtv|#QZx~-u36@ ztm4fP{qFB2u#V~kzgVI#L;E)*qcyr@pJ*Qvt`db{REAtMV9!X(7RVO$lg|hBxLRq| z{BlFB3+HFd?2mQr5@ie(Z#i2y$F&=Xp&_hm4;dbDN?g+})ynrbm+&1$2i**gQ|6G` zv@HZHOA`+-ca~6$k}pm1QANDYE?ViTag2ToZqS#8b^Kgoh{w@Y`G&y}Mf&+pwG;lq zs(0-J6Znx~GV(TtkXpN}y1^w`W>)=n=ll02Jc(}wxlXRyMV+WMC!9=d$M&rnIphdT?mg_wlDY|Lbg}3HJLtyRB_bmB!`Nl)T4f%e9Ki@8#;X z>;zOpZS5ZS>)Yq&8~5!OT0XwpFyd5I49~;ynDB6ru)$tHzrAP2n*C31ChFP0t7H45 zBA`<>^42T%Gw|bnm$TmA1skmGZANe8t1l`yS3R__h}EC2s3NNuGiQ2#ambvfR2`KA z`u9+<=tJp#z5L-vIk;ZyNxMHsTc>|-)io|0yFUeGSw30wlOUcyu)EWmaZ*d4Z5<$2 z#K6~ZTWD_T3QT(G;6XO*Q>}|=OR1$urYt?hLykG&ypJ(|I78Wpab>m5FFUKwhj-S_ zdRa!(IlQKQ(JzW~*j$f6Wv#8!n1$fMEmLxxvwtO%FZq9vHc7zCkp9tVG%P z$}70Wcl~BYfOl@i>TLu$P%F^my?>*&|vg)Xm1ZCXf{^|DV7LR-NZiRjW|6@7_`^)szq5JMv zlAd?zbMK3ScfjKzEf;-+hj&xs{gSNp)RpIdz`atrR0wp$#2iT27xiI>CQiym7%4LZ zDT?iIffaIr`=8GBZwLE_ly^~*#h&B&_*9RLPI<%!{%tT!O#)|S3vkcVeNS_B@dGVW z6HC-A4_9-sWB$31#Nv?8ou5a~1%1xE@bPyk90#BF9G!}QI>Br>ydnYTRN%?%#mKd^ zj-y>H#VRq(0v6mVqi?}xoWzP|GhQ48Lm1c4La7IdbCKj( z9L)z>yOrTIRqFgRcngB<#`#q|B!zQ-}U)O0XeeNr4u?#lu9j$S$*0gFN zhNr0g_XU1|n9=6)gwzGAzZ42)V8alZi4mF7>&Xy!Zq&Z;*)iI*u9dL-qyrdc-StE+ z^E@;I^$6N-WI0$-$`%I~%dEUiaT8Pikz_mG=rmS9|0_?~*BC#Fjve4?^yb{mbo9gp zbRDY2PqX^rUSEGZAkIcTfvrL*ZxUXxWUtrbpYHDNz`$M}9v-2Ohnl)N0j9c{fe?Hm zUzcRbqVcIIkG~(sua6S}nlsYN)i2;j=F>jBTy4nRUZb$+KWx_@JVZo(D7Z~4!uLHs z-j1E8wcU(-b2iMx#m7!S&%B@b5GsD9LWaSwKlz==!^0NSdKv2X0ro?V<=|+bsx0A% zs&o!N?}+#(jDl3F+c*Enu?Z6*90_{g)e1o=b4e6K0dWe5(N}TUs7hbT*;W%Q+eqGL zb-k>@VB8(XX5zb;I`us)G1NctU*aBK_ytItni%0esNv z4detP=+nZU?AG;@-^GV1H%un5wkXQ+?Oc7M;vE_3{d}nNqp!(KPMo zX!PY%rgq;Ie%fC+!2jubM64kyq0bw@c|T1D$W0fu*D^AENNTi4>To7cNQ};{zD`A{ zn{@H}WSF!4f=pjsCQii3+h{5Ezg)mSo##ut5+c4}>I-qNw33glw^MgEiY}cKCF-*h za-bmb7OyE`6-D1tLn%AIyTP{qjk)wXXrAe21K;;|FV(?NU!(M1gT~Nr#QBq~%cmw^ zJ;CYQNc&hlO7Fr|$J!_{(^Iq+*dYfAV>XNm3_*PwkHCohss!=lZ6<@A1ol+k^V%2nSm}FWm zT10uZaP~&5Z==b&u17pR?DrL*kc{M3q+>3FY*wpxP2>2o=U?Z4N32@hB?_C>>2KXm z%#ztUGR`0!BG=qaKz3`^43EWfwT4}Ajb=vreF&$VYs#e=RqM6wvvBm>6SFiLp#XiO z8ZVRG#+o~UIna=mJBMj(37Lu(WNntd5RunKqSvkPb)tB?QS51Tkg5Ted%bN z?{P#iZ?$z=N^R}Hhq@ryu~DPpBl>QVbR?o-UA2wY&Rkzwl^O#nY=5)E8Jo#K$n|XT zcHcbg;M<8$${@74A+PS{x|N{s$*XZ;f4!%L0sQ)$T%Cr68f zWazkQ^3)Nx^VdWtJ58 zHFZL4u_JDI{2j-fhicJ~Rhe41Ol`gOvr{q7wvvTL8pw`+>Bz3uo@Xu-WPKp*5%>S* z9%d1h<*}~N;M%#mqAv_m!PyRaDbv=ah49Ymtk~3Wm5HDus74IyQ}WO&9FgHnYeLj) zUYGV>Ask?e!1e^PfFvH_xjJjRAG?nCAzvWePqels!`E`^JJW2qgoE&=o}#!Wq%TA| z{wRFJI^iFVzjbO|uaHZ=MWE!BI7SR}m^kY;GLK6h8~z2Opy1EBMOE z#d8(-SVl){4bGWc%iFm9aKPSBGsW?_=Z{!cHO2Yx*z>PZ%jAEJS~F7mx3tQyVTDG_u)EgTk&0Cfk>h0 zU?;uctHV!+;Baf;VF%=4gOnIW#NR?^P}r_UM<#FCYm;L{7^1TFb8h0Sj}#|Qu=Q3r zX@~i*jJR2SsJbOewlCYL5n?!Kw8|4@04!@~fz{Q@Go%D`|P!<5PNR#4A z#+qS;Q@)*BL7yRDu&R3m1&4CWn^vfL{~iCoHAEm>CLI*hR1PIIbZ%(zb~o+~y($kVs_(1Svw z2$3d7yyL5{@B}O(1uW`(PB6CnaE6~qGFEpoakIKa%J9>&f8MX>G*h^!-Bp`N*Bt)> zYOde&wVTwr9C&+3y5WT6xbC}p>fW)IQean8huh2E+>4pd(56&=e!PwR634*6;3l}@ ze{leSN43)Du=#~*+&cE270jn!y}uW@Gn`ysLq~afn&00`dGJS1;$v!7_)SgVr>3>_ z06au|yzD3TM-;aj%{+zk?fZ{$V?U0%l~2?*B%(B?GuU{TX6&aj-VG1jbNNDs5o!x4 zWNN@a?)8hp#-I;^E(msQGcs<1j2F2699UPfFx?o5MPRs~Iqi3=T&%%7xsL<~f)Xrm zreKGwH9!0BaL2V9=D$_S*AQ$;*-k>qggpWGdfF+)O26M&G?_M1E7u6Sb$r$^?yE<& zXU&x?us|uG2VST&X=1Vr@2&r`Trf)jg3DJcK(}HUp)z$-7|`kA%{Hcw%K+mbNt;fSTpwxtdgDBW5>&HK)^QW zedX!pmhPrT_+PD@<$tyE{j8LI4hEDuBx zO>RxIToyLPXg;F}LUK-r+%oQ-9SGzU7v*T4z{Kh2FSUEAi`k3v9&dyoTU!beigg?efR9_ ztV5fb$nLe9$-H^tAko81>nvdJUa_+ zb?dD~Dl#Jr=r0W*xkHo(S{+-Thm>mU7L6>?0^xnK2%X`5isWrv`}qr$)32P_^aD>w z^%?jVU^g%MOMYpGQ9Be;8_(uf#{^UE+He<1WQmfZVd;+sUfBgM!{9{p=LzWL1n%A@ z>KAuqQ5|ZH!gN*1g76<%?Bl|pUIjN8W-o(HLvHlz)$m(?D zonaMB1!Nrflg*0jRDb!5Tw_F?`F7@GlOU96zQP>Cj-%Bzmkyxdse`_9fm^W=Km*MQ z7gs(!e11YgZuwgT@cjcZ8=}XKtXIH%(U6(-&L2X-~ED&*WW*|b; z?Tv^v=6Xqw#y^-IS@iWM+;Lkd#sj1 z7|ozxts!nHjEU!;k+$V?4$8JL!oSC_*_px-tY}lc|pr55jOe-iwFEW zj@aIXm~bQ`XF1>qW;)X{?$W*)Oh@VoT|x$3NaQq!Cx;m*7PsyNt@(@^EfH*-j5jDA z`KRcGQ3Ola`A06Na&RCJCJ<&e;s+P1F-E>Mc0RaMB>ia6La-l6Hld6E-UFzFSs%ab z>Arbn#6n1LfmmYD;I7ukqMug?(a0@|zI0WQz$VJ}-U$p0j6V@gY*K%J|ExSBGP1p$ zU4UnZs7NA4H++wVv@s6WZ*0+|<}bmX?b&RoGz2nOgq5B?ngTWn8wU!|5EfjKmzQUN z+%+h=8xL~B3HEt`gh!!Owh$T?77_{t2@Oq~GM@Ky8x3Xm=Y~E}Eh4gdSa7*mbF(DV z!W+#y^$BWdLBvC8uixx48B%yW31A+5ufQW-2z3|PmH{JUk6* )O6I@9&vUV%4WH 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

mQ~(38mS4*B%6t_%e7GJC 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 R-M0QZhprW3h0iz&*i}Y336?6|s z7YZ+?3yv^~1V@cP3MbcCWLa4Mq_HZivudU5b&XjXIa_LYwcrS6a0YrH3#&?10rpG< z#~RDjO#-G=mcy&h1gu#lP@$kvSv78<5DKzjU9VuRE(WdEItsuR7^`5dwdNSMzzW+6 zVk!}>7R67sFbdY{s39hjyn74pk%L-gom0dix3){(67E4|41YI)mu^`*A9y_ukTe9@DStk2jA&asn zyBR6lvM#Gcy~Sp%YACLnjR&i+JIk{>#juI#urCC$OIHglXR}9(w4Q}jwUD&dXoXG( zt34~VQ=30Oi={y;wD^htfA>WTv_P=M1zZ$MTu-&K%f+%Sdt5M!wrOj&YWuQnE3=Lf zw?E>xKQd!t+qCt`5wBW+RO`2YYn{@TZisocSevU_+n7sqU~A@IJ?03SFceIc3$n1d zJmeBeHD8chv*juaupkTR8bzL~bSTlepBq+z;kh%GRWa7Npu2&di>+COy0W0PGf}!U zR%4=jy0=ieu8T39dlHh13yUiY1Y3;;n_8@(2!PAHfZGV)I&FYwtA-0hL#tgg(FQf( z0^6IWEO9wlpuH|o1A5S3GNA|I>p%Uu5?zYEVQ_!AzzUZ32a<+PwvY*euor@WsOXfb zU~&kd`bLGY7q4djVR^x7YjFsp@KvKAz@0{}Su_}^_M&tuzoS;aZ<@dRD-)T@B#bbr ztO06n5eS)JVc)yG;;RRFTex~fZK*X1Sq8%~EWNfH{1yQT5SjjwA8yUi2H72 zumUS^!Yfb%1$Q|$K*U8%214UAOT4{CtiGHR1W@b(DlyQ6zMujk1fnj@L9HM(f$6P!WgH{JQ zXnYYmS8^Mb#8$ilVW7guD;uIk$+A$%mu$(IjLDm<$(_u}pX|w@49cS{%B4)or)!uO2N`K-_T%+LMo&;Jb20Nu(x{K_l>#6pG(x$Mg)e8fO3Z=CmHUXp}_co;8P}y z05cxlpsb0LBi)QEnZ_uc&KGkzE+EgpOa{iQe8?+p&qfPMEx1XY)YI0~P~Ejoz0^`2 z&{vKB)mg380X@(wT+k|Fy`*Ie@NCh%TtgOp#YqPh3LVkL?9ga!#2-kD&Afr)Y!!h} zJ}kJCXEY~5xg=(>jJ=7AcVf+ht%^N}ihHn{F|*fZ0vH*S(jDE{e-VT_h!s$w2fA#_ zn|qu%%gV683X?z!3PKB%;0d%a39JAMlK|SHP1>f7+N})>uYKC7z1pxKxLd8;yUp8N z&BI>()hH6ypA~tUebEu<2yh()sl(P0t#1|x*EP^w<=ho`FsHVV%)$rMqcGXSV}nTf zfvm8U+pH2N9VNE7(l|+kjopMW%8Lyn7Pu%pbYj`+bJiA(#lqUzPaAEavf86f38P*A z+p5jlp*`A{fZ7BO3;#{pRFmMhE!xx8+Y?UV6&_6V$zH)N62q;u6}{0l5K$S8-vJ7C zZGhiLT&`qI)V$Yvy>9*C8u{4 z3uimOxf!%jsGsr8x$`@PvCNfWd!ND6Swfpj2+gl^iy{C*qvpbOP7;ZfOGP5(O zxex|??z@60m9Q`fYrf{4B;nTf5V|hmsYMIC{t#KN>uzrB$KKCyp5b!-5ghJWH3ZQ^ z>;jN%C}eQOLoCH=`q0--#AEK8O2jYjW(!#ga={t2SEuZU}Wxp1X4a3 zKI_g!UZf=Peh5aJ7q(~aW8@NcWTRmu2!I?ID)Sj>fTwV)X;0eO+5V!v8$d2|6G+3x6^%A-jDs&-&Wk; zR^)%VbOj3+`|+Cp5PlHmwx9^VF$uQ-Af>MeweJZ@KnsHa5L>qY__312Qxby+6UN01>!R)nRB*GXQgY~5mMYZOYDF=fuAS<~iCoV;Gq z!o||(PoP1C4kcPts4Z4Kjj~j_^j1@&P@PVtTGi@RtXZ{g6^e`1O0HqEx}@pyBCCqD zZ23`S%a#=%0+VbpgYcFjGjjZxsmnKsmAZia%!Ng(;vk756)$Go*m1{_Fje{_o73`T z%$ave72EkIu3E4-dnR4l^l8+ot-b=Q8g*rvW-X3NJkjthT7`3~WhK$K#9Lo$^`=EU zcj2^?5^gb6=87vmL);>= z%|lxxq9`H^OSFwcSde>eMHXB1D7urTyCuTxYP3dKZ?ybMLOp>T1@%vsn2EfgQEYJ&7CSTfYP6nM@e<7Y$TU+HH)|Qi%sJDnrO#0s?W)d9GFe5>EgcCX zh%of5?K6T#S=ehndH>}RQ>RzRajuH1yxm9brsfGCZS|G z7mGC(I+LVpP1GBk%`Qh!t8y|35~?M^gf^(WB^O6bu=ZL#w8fI25lKBJqXcV92qNcb z$Eq&2NGu8Hqlt?$GaU=Fvs;gEE#*~&F+F7o#gEr`;iZp>|vPmc*+@G8* zVQse8MmKFe$X>fz6X5kJmoUsWd+wj50-2BhKLV#W6Ln1vT<|+WfeJK&5C3D2Jyuy9 z7C!cH{Ek2@Zf_JkEI<5?Lx9F56UYze;|`94vcwL+5zj;_TMoIy@jdWxxjk6~!Grcc zApVS%$}i7+@lo#ZJaEtheu|80ET_4rgrb2aECkR%hQNt20w}hS z;N>dlu|i}Jd5XK6Cd#%qw?*%KMxmbnEH&ytdtSPjl8F^+aDI%~9SrXf z!j>FDhkpPZ0YMXpLGD3;?Rmu>O87Y$w#R@loY)QTn8zJLg^w_-obOtKK^UIUf3pZg zA8FS@|ElGzwhiQkT2rWiNgCOJD|5n8PGyF^zdlVtT?8 zyU-$ay!gy7ZfBM+OCufIhPG{KLXG6i2^dGplJ}{vZQVrUHsQxcZ~73NeeBKU(rBq@&~>;(3cresF+Iv4ui;Cs9(4Rd8u-5AfJp z$+US?ZafRaQJqptqDEG-lXaGiAWK*`Z#fyfWyAt{V39qj_qrp;mU zfdNw|CMg&&8d?{)lzAvTU8zl_vh9qHCCX(ndtUUW7pUe{Z~3H2UNqKkeP^>HQ?xn9 zYcbWEju`E`pppwY;#Yn_!Q=7hwZY)>(ZF_HTrFf*!rgh2DY6K}3LW;|r>w$*h~sEL zJ7>b{64-bkE^bRqwqS-DmtZBtoGjvX&jy|jw;~MS<4{N4SI(ET@FgM_b-{~U=wcVP z*o8@pd<$Nd7qJmDKasN zTQlVre9D9>B;6icyuwbaI5<-b6^ctGYEhwxg_r_u3QL5-#YLgiD4@HgSip(lZc4bw z_l@u}h2l^Dj+%Iw*yE%lg+UVu!u1TdCgjOZ!ph+0c^ALk^Jih0$31}sdGv!G^%ylnojeVIsNJJk$Rf^^K=ItXzEnY`qj@P9$E;o=FU9HE`&WJUjH6wH$ z+agS3r+eM)?h{trU0qv)#kOlA2WI*K$u;qVm4zjfTln4Wi_c8k8J`nzj}$Zk*L&qH zA2ilnKBzGHF-mCRhm&~13W}HnTuSeQdT+rBTaW`RtY9m+*a|Oxkb@a^uJgy|{xXi& zeN7@CM(==UTSh_p(#7u0@^HQDRqu+|t0w&OdR(LrCR6sqbgmkk7J!k_aC<7rG4e@YD5BLBU$QB}Lg+1)UJ8Xj|ED|Ol#5-hygt3K8IGsoT z`-4k63Qc>odwD{Z2n9$qo=A(B3NtlUdzpnX9^(nccwvQ=sTZ1g8I@_6Rg4!|5F}D8 zgi(8nk^wc$K`?z`gg%=cT|_ubz{P*rHy%kuMPvd@#I*>a1(+zZlTbEV_%&(kyIlyo zTp%)FL$++|wQYd~O4v3ubVD^1KsPMFpeYg-xWf?WLpsbubW}$=q{A{`5?qjlbd1M! z`~W}YDmx#omHqY855mX_=B;! z13{p(daN)9>Yc@z1%?qIR0E9%Ga&|>A(JVg+9|Oa;Uky41B41LV`Rs5WCFeagAKFm zyqfSon%Fj=llt*XMur;Nw(8ao=Hh4u3UN{BXHP=px~Hexe2Uh_4|bWCR}wn@mgRLKQJ$c0=n z$^k@5HB?F+c}J(jM@{&NTA0JEj7L2{vwqCV56DAfoXrmigZg41dfKiO!lX)?pGjh+ z`C*0OdYnV}j7B=F+sZ0g*f0kQ1W7rhM2e`Us0F_=oxP$ALGmX9jjD^MpLp92NJ z1mzCa%tw4YBXqpbb<9KAoKT?HP23Df|M9KgnjhV=p}>TpfBK9Os;lNKtmXVmfuT+l zvQGawuI!Ykv*IKi;i{aoWQA3lpyJZewwzU3K(2$D zAMR{Rg9OjvLLHWrE)?x8I|wBf^(w!#)70Gdk8y>?X%T+jtQNJl$dL`H27087VX>_Zw5)m?0u ze~Ptz=|$AJHCqTqieXz#n;BG_v`1r^TD%2`X_$j)wNLbyUt~0y@!OOM+&ka{!g-m2 zX_!5jL|Bxag297MY?25KTR-&L$}14e&0NjhT+Z!W&;4A`4PDV4UD7RG=v&&yYg(tx zg-T$8AfN#za5Jkl5*i2sCWwO;ytgu_-39!VcsalhWPt@tMl~4)OJD>%Azm^$UP~|q z;tdYw)ia`ah3K8xiOK}z%>?U(1?>G2ID86O@DfJ<(75}cg-~DwMgWsUIo~^(TaU35 zRUeU zqYv*B5bf)}3dUTlGZ3wdU=F@u4F2F+cm?tMUlTrI*9hRn8(`PS4gN)87>;3ekzX0E zVH@@g6?QuoP7SoAyc_;u8Ab{q9%3S%iX2|M9YziC_};HwQV5QUC)N+Dcwzp1;i{-& z)WBlXV8IeDVlS@YBUZa4E{$t!g;to7`FJu}XazkHyYH|CGj3oaxdk)UlK!xTTF?^% z!Q+)MkQo6?4(8(t7Lz>A-FW$9J-*{Y_TxPNPR8~?gPccu$k$?+k;WYNZCgA4KxsTX+<4JMk2tk!@jXp%}O>yPHg zvZm#8m^nHPN9fk~M|J$;iIuEfdQl!we|A zHl32i9Xg$uVKp(qvYeBw*uIu4G_rt*W@v@B03YsZ(uTq&O-&{~$5yy#&?aSfYY&bt zYYN>Ktd3~Uj%XTyidxu!ves%NJ=4!I>^W^q#SU28Dl0Q3&cykQTOFk7vP(?rr1@#n zxP;B_6dVPb9}`6&j-8+9jH?;{`qPUAEwYwvfG%zH9-^-Xx3JcVT&V4`t_1(F$J_2` zAxIl0=;+%HRXbwc-qwJ5Y>K(0%dCPc+j_1$?JftJRWc2nH5H-d1RNCVD~*cmr)Y4E zd9qQx4pHU*u1ls^E5Y<|dYV<>ZYzG9Y!9NdLfxUyFmT$@@;&{aJs8hh zz;lMa?fY(XRClZx=e8Lii!^U-kCsY4@93Gko3t)#A%I|9z;lUa0qP=5+v-ysYRi-b zPTMl{iPBg?!ULinQeNHZWL=;dJ#(NK@EiM#ull(1iGw3R&QFg?7&-nRVQDL|LhbQHeBk6?^Q*NpzqnFc4#OM*1|! z?#RW-E|Q$7lMrva`+YdmLxd`Gyr-5s5#SlwB*kNb?08MKxe z4M)5)qsK&rQ8nW^gp60&K9kg)=TlQpyoXaEgcf?}3B81lbTNe9oAef{fOJs-0Ym6L z^e!FgT?9l4RY2qiNL9grpmY%%RxbCSxHETWXWyLj;>^yO+1bzT_j%R|=5#W@a^UZn zIgIz@en%QlA=}OKEE{_>Rr{kbSGtGa*ro)|vx9gpp{eHdMZ@yN&*2nr5E@Soe*%qp zhUMW%6A%ZZ#o|>Oq3F>>+Z3=!F;PC^SZcq&{#{S1Cw;CU4lTsV6yBv{sLB=1DfCBK zM9-Kde1P8>s`3+#5K_q&)Y28lu^&a}#E+RTB#574wdd|pu4O(A?s#VR!wIJDqM}LV z!1(P{1Z2plnl&hTIJobY#?Uu-@LcWiN0t^934Xy&axDCJayR?v{=XCR>>AqGZ{}Od zMT;s$52VTy#Wg>DyKxKMe*ce5=9XQ@_As14hPJG<5vdpjOfDgXV@VABxD2sSfr&JB z8ISz)8QaM$zH3A}@i3w3^gg}MMonSDFAAmIMl-|}K1e$D8{L_OrVHvgD+~TR;$#(F zsL{)gdaKGR{;I*O5*HH@CbHaY+tJrLpm;Fi{Mi4|;BJ9?yE8s?_w{Qw4tp0r?x;ZZ zJJse+Jj;Bo_g1$|pJ;L)JWX81yE)wlzB^O5mz91({3)U|bl%}X&io;;eW+@>`1&Vd?8ctUOY+VF;i&;&J&#S z`W937%kf4;t;KVeXWiEuN%b~m=4RLO{n%Zdd+MzDy^GP3$8X(Y7_U@Vij;+WSKp|M zJ(c)T=vrWx9=(((U>w=$6$ZQ8&{{Na`Zw?gzw3awav5THps+fLJM8vre}3Y*)y=wH zy?6+_eHAdxTEoslIy8V#lfiv`(c==tE>??pgt6I9RVeIYlWv8DJ?Fbe34M;<5oa~Z z)!&Ru7G9;Xr4_WqaZX9#+O350@5+vP;`+QU0jEA^IjOh+msIL}VyD24SX_CDSFux$ zpGbbR{l_XyGQOk^1yECYm}5vZ^!6yc(D3`fUoDnzEG+k-UyDa6thlM6tTf3grIM&7 z;D_5OeqZlm>L`)M-FxON?cJDGgOGad;n7+|m6(o+qkK;8cqVeGozVODE4hRu;Ps+l zg6lUmMw^Cpif@*xY_;>%zo;Xtnaz<)B{s5{0~3w^Em_RK@It@KC#zekY7;&x8h1xV;eM+GbHIP~Yk?(=c{I-&bZU&NsGK zXKkc?o*TW_dK6wRIB_iz6UKSV!TB*%anAb%+G91qu)QPP^w!JT*~Ii`S)VLnvyy_b z`YQ=-qv5aIXno{&y;_cLeJE=Q=zTv)j7=B5V-Y(Q*grqo^8PNWrCZ$Y_Y5`ve|$Re zQ{1dWeV?CeFMogY;+5CCM%8BD1N*@bSKkN5cxvd{ajd#bn-{;EGiYyq7c_Ir*!rJ- zKXExrg)d{r``f|2Iv%x8o^E{KkJDI`jt=fN3Wz0Xm(NR6r(bJK_fa~0_c!`^4{;3y zX)(&#lua0-_2?9z@#Ld+V1C>_tD+vd{m zso3=oQ*3#=>pvpT6*hP4=^OA~2zrn=x83O4b<_c*2R@$Da=Rmah2 zNVtL6vdNA}pGI-MSZiXaSrF#WFoMg8OtPn$cuGs4Cp8g=1YFP1Sig;MJU zfqM0e4>+CnXR-ohGkKifTL+Ck-K&P#B^VYoD`83PWC>eeb%VC+;>$lX6BqZ)_V0T;`Pxr%w?Ksb?U6!e)Ide#_v{9 z9zHcb7Ew#3UJDIn>@~NWYFYB@Sgl}%)AiYoa7UiOW+u2|i=%1bLwMwxd~qY9tn16vfXeR8BC6sAqqxi4TygIv8gQSOm6;^YtKLz`~xXCf!6|N|{{H zl|C<_MmugQ+lSG596f6^zDjwt!$XPE-d(hwq3_wAn>5HYY|MHy^ps4g+%=uyt#z$= z(l9PNDZf@9;P=`&*1<7TaI=j7|UL93iK_n?;a8dJ5U2k(A7u)!Qyy*f$9td{OaCv61$}Iu0p`$Pg2CH>>bBo)YM;7K03c+-a8k z)L44ixmuSOk;ms|h;N^7OUa{Qo;2#c3#v-|@EjUlnhzR;@b8r`JyX5^RU={?SuhxP z^*}pr_Kf1b+e>FPtfvhi)I47`{qadQN^QZ$LD4Kk4r-ts1!g9ZS(m~-8GpL>E=&i; z_&xK&FTMzScJ!!-M8pDQa;WZ}crlsbLudF|jD*fmri1V$(APmS)sJQ!5af-D`Skn)#>6cw0GO{cs@3f*+)DO}0#21f7PbM}mV$6k)_&?PH zW({V5uU+A@Sr`rePO%3wvROY&7l+mmiy@Ul%O>oJ{M1zZ;5G(6`v%`4JpHX(}mG_SE20K*Xs0SC~c)%oILmc^9s;;n5 z^@U3|i%2!3j_?Jh^L+zBp`m*J$NW9LAr+UE9f^@ecMfSqyVI1Bmx1DfCtZ}|FBs5s z{9lcC$oTnI%HJuppm@5_PlmlU+;Ql}`hW}KJDzdn9iz`glIlb-4Zbdhi%kL#F|7tO z>jB`~f`bpzkrd%RS2h=>_q%Y&1G60-64c*p2VW_UV{q@p@x;EOnlt}$n|W8(O1dAt ztLw(4H2;b0{>wgZr}!OzLjHlf@O z9+98;G$WZ*a|Y%pi6ACsSBk8@6R%h~ZR6*D?tn0C{AQ;IZl z{int9Z3iE_REu>ITcY#uVUo+d?z*XkDkeHc6qf9GqgaKn>weJpOZCySafOd+Hf2c3 zrg^vL$M=H9DpDW>@@lAe;4hSJJC!|AM|!pZQ3qhmAIaB+U)=mm4yF=qs7efC6=fU; z8f_CbXFuHh=u z61D%>R=_{rLa#`dSUgde3FukR;xrfYWbRL)C{~dyfRcJZrMmUOc8JVX(~NK zmCclunT?zB_`(T<1_wmYbzEozIrxD@*xqFGKM|)n%ZDTWJmj@+sw=LUqjhQQ?s2wp z0)?)b$#^DqyvAzheYcyMXiM9SfYHhU0)HJK90OVpcmd50mbxgz%t@}$0 z*bp+8N2rN!Xsp*g4GDBTpMR~l+c(s*xP3JI;Baon_CKGs|IYk`QEl&jNe2F_Uer8> zP+To$=|@DQglp#C3rOj7O1_Y)%;HPaogFNeO$fdh1Y1s-eldI zu+06}wQGABR$bDa{Xt@6Gn{}v4=nIv@!)0(O;7@pbUdfDGa&fcKdBuM!?uXjNNnBc zmB}Twpl2tCN}9*euZLr*Ig(NCQaV*#Jh<*z(sxUC*({1gbH;fjv$*)XK|r0>PEb#s z41c8YmjM|Tns?{hT0faz{R}g`%C7Tn^5)kp&(DKzKL4{j%hjEi@3@>b3g**$2`PCv z)%K|556!@Jzfa%7LPn@o50lK>Gt!}F2)UPnt5a1_O*77Ibny}b&eFM~Wl1k!MK{(a zHQ9+PWntG`3|;Gd-Hu zB9rhN#u{7+KuPIm(xEIeA^Te*wmPA8*H2#xgoJDmF%hAQJ~cB)`P~NxJ0fx;CoNyR z&3vdrKA2R^?1auYx-A=6sn2~D`<(;eX6~&!TMJ7=&(nNhwibwO4Kfl=(q8eKpA)*QtPd?EZG7$Dc`Pw?g6CH_0FWjv>_2^253E z_t+F_n_vAqhUy##2c8Yj2wy;0FiTqLb-uUY<@E(R)AKG3W1B=PhKe$Bn?eq&!GBWF zC>n!d9-GLovwr0`lH$PHhT&lr&)i#aV>jdM^2zTeQ*mFbD2C&4iu|PR&bqHtZQ(Zb zigRI#!h*etE<}mwbsNvywl}}o-nwmf@7rZ}<@;LJ;LqlUy-u4dwI;dfU;3ar(MjMK zY7*z}oa}xb2d7hHT=V`9$qib6-6e9#3!dkf1-+c46QsL?mc8_N5g#`hcQZM;wY+HC zN^EIL_b8y2MD=wL$~vYmnV@&N(Koty-|(+Fw#p^o-TK^VlYDOQ_sfmDygQsf15laY zmd_?i?|l#Yawqur_lscg-4NOzA=E#lTZsbF9zlu@nXHVCOQT_Lh|-uA(iMkr&(-2T zE2N2$U`1so(t2M*L&5ZDcf$wF&}fAAWT{KZb?xN4@tg%CAjyooh10}J(@DEEhtkB= z;-^3ErYJ|!CP^(lwjxhR`QDYRnh4}5#c{A|D?p^kL_4x=s*5r{9~w{%P0Z%VjP=Z< zfhM;UCtpj*$WlplqDpBg&eYX$P;4T;zaw~a&Hfcf!i)a&;DSh##>`=rj0pq*dCPAdLky1glc-Tw2-(EXQ?) zBPPo?agrMA6>x`&ZsoA@f*V3A&TBZy)~G}u{}yso`(u#PhM(u2_-$dT!Ylq={Qeb- zVqMz)_dYgNZ_*Dg;k4x)d*^Y2V20%!s4x12`P6I$4LI$;hjJ=5P z6f&4hW{wyzS{nrm0jSrsAVOqt{?DNbHA{O|*CPL=*!OWn?C6{2)-SJu2;=~hQdV01 zi;VfC*0e;ad?2l_27RlIlOKqslR(o6VDZH{trPhmaWutPM6Z_O`#>TLbhqw?_Sa+- zY!s~_*~y2Sy-SVvN`9H1oUNP`Js#~Y(}MNYpEbYfMPK;x%kl!po4H%bH5}s-CdsIX zmOcfw$72bP-xG{fqMklXfC~YT8f5x$EQkkWyfXr7B!kDFNVV!nQPio6V}@T4o2-9o z4^3I8huEEwSH!oTdB_Em=TlD!_qK7IzS#Hb&!EWY)M&zcb4~jG8`67PuzbQy%OC2! z5!+KAL_UBfpS+icMW9srqNXiO8>|m#@_C5cZ{C*d(adn^c=M%}Aif#$7F?7V7~;8% zPTWLZ{wp=zV72JkK2SHZ64kj*bN}em)0La>8gU?X2vCMq3q<&Bw1)-hkbhhwgAzc- z6e6_-7NS9Bj#i^iiF%g)cNrSJq9r5u;_#_RXZqVWfY-uK2Ba-;LLxmo=5*>dEqc`1 z8|FBVJ*`eMqGs9b)_otJgy6c8R2A29c~G2%D?Z1MdKu{t;Z%rM*}zhYc5mK-dKO%^RYs^&v8X9oO-|V>RlYYZpuxz`Nq4DF(rBk7UnTWy zCz(+zoMx`x(C1&V&{vD0Qkv$*e`HVbC;l`$9c|6sEn>j>gPF^_g-kakdyX8Ti~}pb zYTHOinh@!cpb;p$CiB-+Mh*KeI0t}tJf4XuRP|J+vhnQrG0*L{InViG{)BeVgue*n zcxeKBE#gAYPOGHq7d|H!bNfo*htk_Hu?=iqz(AmPRnk(7 z0qoYml}}G1c9*N11AFKN5~9B?{{V~hZWOZX8)0ec<7kvdm>lib+1W7$AzNF_sP?!7 zHlqEmo4u`8=i*h|dZD``^sP22Q7dV}(njHDkyFkp^996k}NF`Gxxp6|uk8yelh8{cqG3C>7Z@PvrJXyy$FXZ!74TC%x|LWfzFiR<%}D!bA;4tNJP_)a~pCI5z&iJQXyktt(Ww$uZP5HA@; zN@0%^*T@!-E z-`5*&%F%*}3UjgHF$sBZs)NZ#@43sur}PhvSg>ve0V80V z{0`7*dgYz&JYHG?R&Stq^zNv(9{tx~wKn{S-We;1Jz`hnw*T0@J>OiY*+X_MEG5xLSsUf-2)7%1lT|G=)_{2FsB@1hSaC+A=jEl~K!KF5eaNt@3hvqmdGiRIHV z+bsr?z6hopSvO>9{odd0P04eVjf13UmmB&so9Y(>;$%Y;g5|3VDxHmV?3#4Z8O*rs z0VqecfhDW}G}E%StxfY9%qJ0n?)FW=5J}hXQfkbd>Tl_E!p4Oop#^MAR9Gq(M`NFn zImnhfV}pUWr0Q$StY`8HcSkZXeD1UYO&$t^qxf{TYn7%+bQxcT-@2OCQkm9Sb}^|6&;LsA;!My8vQ($rT;V&Q+z6_K3AhD zHDHb~!h(t+?&V1|x@g%bR=o>mAH6~YCJTnHw6WGv%in*rPofoSO(3A!%L|ZVbn%6q zzKKlTdo5D(a?>&J?b|VlP(hPkYE9W~HkIxP$2d@nNwL3dXxkCr!JuXK1NKMH!QwtT zmy8WUHM~0_Q$pSyt~ZoKThgg5wOIwXHrS#cni^-GJWbKo5McMj>hhh1q$?ML*iw;T zB(rvhd&3rV-(KwXewc=X~y^&=?8Bu!U-TAoICoBPG@nw58*EV-lSVn}E~|cHt6|2JQI5^3ba7*MI%l#L+a$_cGP#7- zD;CUAc$%WIhhh@E;%91VjQP!MFL&3KF7C0A&DrU|Py#T#$_XbT|1(l8y1mEtUO7^4 zHBu*IuBT3P!*^UGY9asG*IG$>KY{yPOIUe%8PXrI?~rL+L*n_%^=nrqB=AuuN$P*L5c-eD(OIRb#&5TA1NEj%o1xkFd3Mm|$BmJe z={Aj^U2(ggGY*?!MZ?j%wUi%|^-^JF?|43{uBx74@`TbZc=oh7)W)kPK3DMb?i<<- zzBDv5et3C*CXSMU7i zpzuUi{E1AD#<9eLzaHc7&7YfhhXtt)%YPCJLcb)5TvKIoUh8JY*T(B3`Phi_x_l-% zG>GDlNV-V%=C7Zt7ym5PA9wwBuGJf3_o~*M zkqS&p;1Dp|Xs2#ieEHUE3%Gl73_tx^mwh3P04tm($sb6%wS^f@bAIGi_36LoVgc6sFrKKRuTJ zM-Kn*9RAOr0dy#SFf)p#8wF)XF?I7nt$0AyA5k-B5Vw4vUUqnlu@mj-i{h6@iRq)!@@P>UTEZADX@bU>3Si6x1r-DZ z6@?^lLQ>{J(w4%)SA<1WMMRWDqzy%6ZAC?tL`9WFB^5+3Ct2Q6Ohrokii?DTgM^Zc zq=JQ{lAENmyQGRDM%hhDN>k>VlB}Yktg^G5k(|7$wY=?&qN?Q;buT54JrzwmHBEmt zXL*gFD_3<~HL=$qs;==3ef=2ya0LTXFJlvDlNc@2>!IeR zuI6T`<~IK3wl~ZZu2@_(vLY#4+t**W^|o=kYvb);Q>$)glVk53>S$ZwRHowW5##1i z=7tY+^Sa~a73JQo>ETrA;Z)_}ecQu3+uJ|UH+09(z0oi9gMUzbKxjr_(0CBmG&r~} z4GqP(lpQ>b&N=X6X zc}2^454wx=wTem_OUoXX)&-SU)YsK_)y*X}_69U{4!1eGceOP}rJcrO*j+cB2Z1(c;N9b26v=wd zlpFRmyew0>b?|n&=iz^q*myXvQE%fytwElI%S><6tB2g0&MBQ|@u^Ha-ToUhk6Knc z@MCGb#(k~pJ-)A+U0(FHy?Gq+ex}0sar?%Th%X1cmfPTE7>5bo0^8rYHI{O@_nhtT zdjFgxX#BurpnLa4vBJ&G*@2#q|5a(H^O+7Ry3>hZ5N`oEt@zbWPLu^1(0BZ%|JLE= z%O{UNy(7od@|z9ye||reC+RjfH1Or)e66u)M%CcA!?m89@8&ktwkU8}!UfWt4?<1X zceBXt9r^Y>25!0_3PrOI#4F=#{O-Tu;fo6Zl^BHpVfU@N1xEoubSjE_lz4=m*j56o zCTtY&?U;QN?iyzSis=x?f^1!z=COfrvM7H_AN{GGpgLp(8%U0!<^^*e=)J$Gev;%F zd~H+DGkbQc(Jhol0J{@sqO`x0XQ3zV9O2SiO~qyJ)3jUYd}n{R$f0>5i7lRHT@Wlp zS7;9wS+XsIr-#nJzV}&6)-4H!?}T#+3w<5~7=M@qP$3Z|RUmM0vU^R+c3*87eU-8o zcttpk3@EZHZLG-xIlimydI{sMDRPDTMlCa?0z8uqG}T{Xv$sY^eF>b zd<9PAq-?8j-ySgXJL;MeX~Bf3)GX-6E6&fO;@Q;10(<9il5O!zxRk)&r8{v+xm^zW zwID%OBXKGLF^4jmcKX*JQei2lo@uBC`;Qf!=&E?W8lf$H7T&s?ckRU<%48;MI?`#q z5UiTW1)HBwM7W>`WBuwL2VG4vd!M_;)C_VUC~)}^lm{B2MV%y$kK21rAMqI^#8B4U zGbTXU`ZR;{u3JDPJJ-Sjo7NDlsrNwN@69AzSS44XbTjh7ZkRZ%9l4*@EMW7(F zJroh9R!8Us)y~V$FmfYoAHi-76SNRIiaEHd7vC?Af4n1nN+vM-&Z8gI;BnSMvQA*_3~g#jhFz~ zYQaIh7cT%!f?Wx=tR0$cL3D)QvXLvk{JTBbz9r8 zERv`M5phYj@$u6{ixdt6AcGFM0CZu6N_Rmnu;&0+xu;HLWDG`V{m3}3p%BHpkE-ly z3B(EniAovo?rmkF&Af)yK7gKN_?pym#l!;?#oR*=#}e3l@lZwkxahD55SR3gM5QSp zwGq;qyYmLrkY^<7tu=7_v?@v6G3eGU$^)(s_~(n>8Ll44anu|c$XZVeltH{_@q~4- z{0y?N--Ga4oj!DP8EYCr1Fi}LX-YM17=D}yS)FigN{b=)JYK$l zpr!!>vIJWApg*2c>yeCs0``flW;rxiAsl0B@wli(XqxpUXDWCK!fp$sxgL#UYzfhk zWbR62h~1`b!9r29`xlAULO`}R7*Pq4oD?P>0UCQ1T`o=cWRpDt!$X6FbX&QruCHfl z)#l?~b`s(g6;V(a8TV?4lGq{a4$#6UVr(U7ZD;qO_9W0>pGImKFC0A89~AXV7XBd1 zgNlh#!&~Q@j3KZ9gZFA0SEiO4)E)(QuY2yx#-%DdYSH`S6L{tc)Ow9ujCK;vyi(sW zJig}Hw*z)$K3C0p_Qg>dk-!dzYpBt`8Ni$KUOcvx_SevAy5eS#nH(0R{4*%U)n1p8 zdJOn}pEmM|6_!QkbDABXPaoU~5{&&x%`}THwfS}FWFip-w<;_ z2sadTfe3gM^Z>_5z(P5c5(z>=%P=j2G42#XV$&6OfIUT;b*x93?hk?O+&hsC@C{V9 zAWP#n@6NVZ6_2)pr`N*9XpR(h#d4QY8Dht*Dvh?_PW{-Y(W6@-1R6tw!{%ijZEh>t z*N<1aNU1EN#jPPv#l^N@Ir(6|52p}4A!$-#IgVqdw0~U=MH`v|=Jt)ReJzJdlfU9B zzI#QG4msi_Q*3JM{Kekh$2Uc%5yw)B7`MN@#b~=mW*Yy}+Ll#CtBBCz$RY#il?^VS zxGF9B1X++$tpVhh35Y4Biooup3=JoNs5Va%v((~XW-uH}E&_rXLctlV$sB9HlUVyL zAaZ|+d_=&WM3pj)Oh`3jOhl6=7<4V_d^CX#3n3A6|7*>s2m34o1RIa_6}GXo0t?hw z`U7818v@hOq%>1+GLVlzri*%nXT;y~zhrAY;gZ!5M1C4wo9pYVtOan*j%c~8z1_~5 zSDB2P^M<E%lNc)Mlp`&Q zhfS}mVSmv_{QcT!9up#=r`bCA&&WOTDwnIwJ?Vki@AeZhM>qcaEqQz7ow`8xDUHG| zC+G0AaS$1979QPEFRsIsEu3BaKyaC5>O65KcCYQfzbE&uDa-OZT+BYv{&)6~N4sAx zigefE@YZ{;*-?kNY4M0%S_?6HWbD}IV zUS1_$u{e$%C3;;hju{0KoR8N$kH;bta4HEp_6d3s2?oUpM*RsUYYArO2^PpiE0x6S z_KCI;iT1^bj{S+wYl*IF@d6}fFc8Ru1^PrJ`4%Vn_a_CcB?X-)g&>o|RFcE(lkY?% zM-(SV^(RNKCC8p86ObuHm6Qbgl%$9hXimI@TpVN}CF|lmg@jDaRY}daPtEO5DK1Ve z?N2RVOMP&jT7^ujQAw+_Piu%sYb;J{?oVr7OKU$*>qMq^tEBf%r9!l#V1)F+HQ>Nh z`jfSE@_G8`UivsPV?rfkvN)Xs0DQETF?XIZkIYITTa>`& z4=^iX&NN6zkmD0_OG*by%GXOC{4A;BEUi&3t#c@Ch%9YXE%iIgVn!8dgdqvqMI}g} zsA1V7LpFYkGBI%=10Kk53Y7SK`SC@f`$dzH<>^Qu0|H_>Up}{AZbhPLl&xr-hkt)n zZiRrZi-U7jLS|JNdqjZT!j42dDeQUM&z?XWpV6_l-PI zuSj&w_7xfrp~xG8K!LJp2cY~shyw-Yp9k@U0L2gxehP?}0^}o=X_28?oyAmmhyw+9 zO%^Cm0NqP17n&)ep+I6uH5sIu*v^_*EPP3|;vuTG-U41{QCo5fHX10moCoSrAZ8?B z+Btyl5-wO$&Vi}}yI(GAz&kX^L>od(Ei&&R@)GR%ZM{@O6<~v>cBMc}DbQ~M${Iwz z8)KFUxuQK*B47!=JNp1JER_`nk|zGpO!lDz>Y>AFJtG0+K!R9esTdK^@X`t|JVem3 zCQU7u;Q~)x!)CY|Pc5HBU5_;@y!22MG*yPcok@TpPKXx*Vg`U(%Cdp0fm#R%x++CS zyjgID`EGMtjv}k_>$Eif_BCWjKnjl34$}1P!J@-6+~%5$NxDsCJ^I zE1AmmI*lHH`cBogJJ`!nDezZL3GAV3RmC;Z!)sV7>d2U2) zdaCPX0Xs;9#`VdYb;<)iKoTihe)Eufue|}dDhc4_6oUC>A8BEM7>hc_QlK0e)FJ`I zoC3A*b!ZCE3IJ_{cmFKy;~2R_q@$|FEhh;Fc4cdb>-GdV#$0yH{lU|l3kb&NYs03-BU zBxxl4gBDV_N+dZBC`?dPIxKx|1c6#W-5`3`@~L#@VOMIYt?<-lSZWVNMe|<84gVs_ ze%ZKcd8BIjta>?U3os*I!UzCzP=**O`TkFy!hc?#x&sIo`v3}UF%1Ra0H8U~qbZ)fwKvk6&X;}8Ug_+H0fB`$uJ1|@dtJlVf{hLN&MZ*aA+Ij-??ab}&IB4oA z)p3~Qhi!=;L;)~XxhYr3(1Z6$E$C5>sG=;UsRht-$i<@}_Q1)>jMGwQp6Y4j2f%>fHRoY~j+!e8{ zt(GbTZPz@FfGj|-Q%8nOWp%1;g_Y*Z0GwQ`K+R=Aa2&so#-t&hx;uVUoB4@GWB^d& zX7?T03qvoXnphC?xW4nu+j~hDzf-Y~0lJBJYAm%V zYMx5s)Br3yDx3k(Lk$sd_C-UZB}d$Ue+bnD%zTgj?|fjC4jsj56kHI?uv{F#9H-qx) zM%0A)5xDGJ(HZ1-;++2?AmBn4SvV_LjEOO^`S1MgE6te9U(N;@*Ix*dVYdLFnr~O3 zFJUeOal;UJ9tC!uH~B{7c^jU#j{>7vgqV!`euDZZhFF?f@G6q6a}c9~1Z#1W9-t6X zlRIT+BYZ6HonwL&u&^>M998{0c4Pe2wZE%`jf_?evCTEWJ`S8g0DlX+L=J$ii-63? z^OyCduF8Uj6e@>H^V1DUSSL_THcVd`q&N@Me<-#LlnfTYB!PQK&Z7)B?4#)aQ+uix z%e&SW^QLub2Iz=95(w|r@+Oat$(%m#Ub?LiAIl{$JNZ$%_{QM46;iNVBz0@BQ}RtT zugas$H?N;=)HJ@?zqq^6;SkwGy{TCfQ>YA*e8VPV94+6nsrY1-wQ%F@)puV59~7o{ zUxY@N9OhhKx6hntK!}4b_2dCj0;d&`#2U<*%R9Qcjm^r54wHE+xDt7cSx4+7|~f#Zre*v&wy~QZz6We?0l~spP`K;IJFy=l9(;^?_Az?%W9+|=1in3@ z<{rTnBzwBad7&vGBm3c&33XgKRev6p{DWRU1SDAYKk(>>plADUX+P59K*ETF8qI?` z*Mo-GgT@C3&Cd>6HxJtX9dw=?NB~4tD)yR*X5m#}HvqK^g=)3ACj>jD-2(O_?AIQ> zFMn`!?RZ}jaM1PdXrAwQ@#G*{^WYxe!Lszhs`NpR>xM%R;k+6Uf`=%*l@`PXw$_z= z%{ZFbJPD3{_XGrbsd@a>_0xBTLsvR5=0kSvUh&vFeM6#m?O3@uPpoxUkQMCvDurU;W>Fu9*W0 zRUT~i->_%;);QK)jae5l<5$+DKQUmT!AR_ zA6x+(glBDro9$ff0EIJryJZH_as%D2{J60A1#<0UtlO#fr&Foe9)n>F=YL;w`E$*# zeXLdxK!#*}kmIx8Li11{?F8DMd3b^>SmgAGXCBf*SiS@J=AZ}GA%G3Zz{oK+ek*9^ zJWT*4UWWvcw1Dbh!S<-%HdWwihHrvoS5rKQ+yS0o{Gmn$dUwJLzQNnff7~(yYs%_J zUflm8|KATGEF_x$KmzsgcJ{Hj+qwMxUw9M9(eRh_PsdsO?~XKoeh)4$U<8ei1NG;r z0H4Qa7$o)Q?U`yO;mK_6QCE*9(&?yJgfXt;#R~#742{vdXWlI|)GC=nM+Rvyw#ZLj*TffKHnuTQh<> z{fYs}_lu5cktm&ebYitfLKe=czt93$n_$;3<2xj)MWmwdWmC-$opDcq4~E!?`OXtY zQsB%&*flaR)MKXLQjT2i@Wp4Ym=h0R8u{$ISZ`XXSK;{0f2GZ3IEmNkd%){Q7lH34 zE1d2g!8ilykhYdUuo;&x2 z#f+dHnbhkks&uMyhKnW3c6v>}o|w_GzN*-F6ba0#wBN2uHKWJ^!AhlscwwcaMQz5K z8Q89b<-yvNB+mzXtyIA!xNRb|EE!1_Uc_3WxI_82tO-_(r_Ia{EKLJc#g9<+3Qq#CBH&48H;2-`1w{erX( zqJ~brl!Qn(YPL_KXSi-qtAlc*h$r)tw-wUltLeaU86tewil%IpqhdL

9rst9E0wt*qT6k15V^L$Q4vv<)c__X9S7ywv5%2R%YA8k>OeRS8 zMCZS?KnPcuOYt*JC0?ygCK20_c4pGd@)MG>W+4?}<2^R@>i1Jm#>cN)x7K)fNVT-> z0lF=#o;JQUc5M4xYwaCr6>@gGrDA5!N6Y`_bX{gr9Kfd3-oA^2n?-cWH`^Y67`)jL z%6<36Evap6LX8d?i}{SD03Zk@t`V60ENpCt@M&i+u|~;AZIyKBwE#&&Yr>y$e|p~Y z+`x{-7H=PN^8DjL+u2QfE$GOy9Y578cLQU~pEgxnn6LN7hyVU?FLsMPkNleB;VdfE zOi!kjX{umKK_rME&@?iLI0l_lq7VWIHj4lWTqZ{X>O1ZXKR>ph&gR{(VQLU^ql!SPR6>Cq ziZ_=4#jw_h29f5uJ4rB1#0YI7naEdOMU52#0{Mm<`;7Yal=>DjZPeV4s`RzhR+y3$ zdOhitb34b;?e}87dQJfbu4;k8Qh^C8hwwdJrf@lW7(Skk_ZeTxOadvBdw*NsF(&QkNWuq;Gbg3x6YL{BC%lv|7_ zLeTVT5o6qSqy(xuG`&m}msmPv)Y|q{>FNc>U4pxF#^KJZvOCc{=;8fQM+Stym`(;L zdX=zw%n&hr^PyUOiIBg?s|P>EZ>;oAzw~Uf5KQ0!k|$LyLVo2{y_I~(^51?5zYUcN z<1q@XreZF*tWe1@;j35K`=9DcN*F-WXcA(bKh0Jz#?bm!%VuyfROG8)XRWV^x~FC4 z%2I=JAie(cNuYZ8DD8K8>pAih2R^=O z*6nw`HK=`?GrG*e3&`$i-fKkOAu^TesC$z)w#D%a&Sr3htvW?f)TC$7ZYE&4WERFd?!#`Pz=+sNl6q57@_rD243)F4cbCDm)eUSwb%qt+s*yD(pd>T=B7@U9%W00 zMJ>d+YxWTz)qRJmQD}X<)tz2S=l9(E7T|mPiPMM7@09@VovmlNwd?FZx*shc26mM? zZ7Qt}IDKggtykZ8f>tg)W8GA$-Je&Guv3_g35u9#Y1{J=>i?)49JTz!^?Tpy(@%-P zQ3@U|tj|zTy7@RzNHUOa$hyLs8$dPN@A65)F!Q}`2(jb(W@i3n&K?nxfYxxQu`3?s z=nF}b8*-<=TRe6tv`*2|@L;;R7884u-Ic9*6P|rzvKH~8riulENxX4T z^#pp-BF3iSC7_-Z~CHctx6uh;OFuv?$8n+z+A3;}B_7RiHQwF*xW z)!;jFOw_Dj_%bX~l z_gX~j3*{)3)_gT8E1v7BB899M(K)mH zzy)kR`M#C<2=`TO_4w&Z=LXMF?SVPJ@8W7`co>aq_xDT9sRhcIc?H@J7>K4^;hJL) zsbs(eo&4tAa%R9ndCTwRn7FppONxsOu;x+NDhjTi1d;wt2D3Bs)0ad>AweaZN%~IX z*Q-ekcysWJxrMJvZ8r!(Py2NL5MgR4)k$D?e3_{8Zme?Wl%eMLN>QG1wg=81)|QW> zDpfb{(j5$n$Ht5N#Ikm|>QXWn!}70MK@1{6{jeA?pVd0ctNB~BJO3d3=LAZ16&&5u zjCess{>LhBv}l7zDW1=`!X7_sd@1-oUMySvQ{0En=N+_0Z{n9v;#mIFQSpA)+;IIK zlhiLAsg)vEO@JFL$vw`3_ZjcnN~o&wDBcW&fDLRX20RFxZfJwI1yEmOtqj7&vzS60+y}aJ~oih4Qv-0nL zEN_tAA2MY+>>)|N+Rrq=zCxj z@$l##i3Yj?40K0QarbkFpn~-aQf2;*C_-%GTt|&@61~IK0<(G}U=5c$02V z{pS=Z717Oo1T<{GaTD?FcRojC*n4;vILOPUHkl11x~~ z%JHpr^A+~`c($DZc;y>jJd$3y0j%Z4-^xQn@$NG4rX<7H~b=U_>A zf{ZJ~a0hoI1EQWsd&dJ`*cX3u2X_+%Hq5{abYX?iaHnycTs2nUNLMJbTfnXn^tUMq zjJNTEs+o5QM&gJsK*Y~LVPuzJL;N3Ky@5rbFq(lOy7P+uBAN%5Y``fu;5f;e1HQ=$ z>{|PMH$~y`a^_l$KS^43xLm_7!(sKiOL5L_0V`@<2X&BP#=uP# zCU%2C8FK^m42WLRbHT`jOQ%7oQ$vF7AV061eu#%c@zgui36RbkBv$^;OUKZWsqOEr zPVV4N!aWif`$uj|IJb&^cM4IwU0=WNrm!p2MW>UMyPvBeo)psj{j>h@VqO2B%&47c z>`#46`6r7#E#C8ZWmf}5G2Jl3fhZDKi3rg{>hcot@#cd9exQ@_gw|+i(jSBLpW6BR zO`Kx!t$jMRozj;9y~TT(F=~_nl;H z-$$cn;YDo>?6+*>C!SRjfUmEJtMF5;&WO^Uma3J4cr@r^%DdT3pl~EeIKzb794DCC zo8s4FmjPucZ@KI*yxlpi=2M6h#Pn;Rll9PuOr7T7sh)3YT0uEIh0|(u9v`_0>K`Ae zGlh~Ejr)f~+c^D-2ewO0?8Uc1Ps99WWtEr$n^9W{T_+w=Z&d* zh7KDY&xUU{ZkRabLG|T8FXP)^3XXDO;*BCfThTapbVe)taOfi~ZX%YQS*IjhM?bzL zSWPD?lvJFpp5VXrKDX@GD&jFpmLP)d0fdg%?7{ z-!=kY${0|p$%&wd7&UMs?|&1jTLnmH8-GlKy?MeF_6y}S`I1SI+n}rOg(k7ivg+H1 zpgYq#5HnqxX1y>gBW|>*9iiW6se!Ce35F^Own*qDwbeRvr)=G%tA8^~%XFoO=#mx&$c}-6c$eG&OA~KA zDq5eGP1&^A1d#58ZkY4agE$GFg;{3N^_K3-!<(JdHEa6y4l};@=iUs>s0&QEw|P_$5o}OSW}mjo_T@h;599oaJ5`eTF@qS4+|eAfEOUjm;IfTyb?CH-=(>h?_GD?HYV#nKIUmt%1zS>&}+6wmjg9uyJWk!ywLGMf89O zgL$eWH@GtIipIJC?VxS0;92%ejqgwoSAkY{XVSPGp zV=5BxF%qNVc@GhZ(eT``DciX5`rcf|M$yPU%K3XY!ZDP28&q&raE}f|Gs|bD0B+~b z8B1m|aEMD=XVU}5#b6Na?(mfjcqE2O#a$pA6B~B*nz(VcV(!FZj4NGDY3(*++!$Ez zQ;BV{pJQxdH>mCI)6jdu&M|hLSOnp|W2`H)S4&*jvNJ8v;cQD<)00heom$27K{)xo z<`#VXzDYQl33FHW!!@~4`mC~dLsx})ik-x39fsXXis97S#T!?Ddq@(16@`T~VE7C$ z^sZP48pD?XjNg8s@_hqxj&TjRuN)4z1dn5`j!h-!$#ISadjao-134OYTxoGkUK^8< zfJBN-Wm5q25g^fX13U=0F?)5M9X@&tT?W7)(J58ZX0deB+tQVZ^8rVsYupEWoaQ={ ziqE*oUK#IKHcw!c=6bc}o@KZzbB29g@iv={PhrfSG`}J-J_rPDV2G^_}yLF{$}6{>+F=iX51^$Bn0UnT^Qv0Bh4NBGb}3_ zaD5QRC&w;>#+#wz4bLG81{mY>tDP1Wd4%DEV==5=%t(kh9Lra|OHYWmlv^-_1Fup8 zf@qu>8ZUMZQ8mCw5#lcagCGDjK|OPZo>kuedB-+6#VR?&^t=`iTxeVaY36+pdRS_&!=F6s=bnczoVNxVJ>11AN)I{9^dAl4Wt>w&$Af@tw(gQBbe#oyz|{I-LL-1Ghs3+e0I0g@<7VQlCUL^@HiOGO>(-m0l|Q*2w=f; zNSt_r@pyphe1ISt9M2Eo$b$ega5iH2g<`yzN`l!S&R7MiFo-us#@}hTK4=AK+a-vd zV~neJFOAXhcL?!JUJ1r1tipRt_UjD@GTxdHFNTC_Bf*w~5E){y^w(fVVZ7KN1T+Y- z!r=Jw@PIrl-yj}|GBVoo9;&B?430$nE+QM+m^}YPVC9$AT;{_pv&((OoNluZx;!3O z`$zlLH*xRfsjWNjIum9wHU?m~vSmKH#i19Ln2SC5381rwH*9mXb`OUb>EmC!#aou9Gyss#(xe6Uq|D$#vmvI$ff@L zK7GRvGZn0H1AuZ6FNMZQA(gJm&{P`$sh5D6@KgvKryCJ(+~Bv;8ljDZTt~(mqVb7y z=X4Fh*CW6{Vt|ZXf6-`XvFF>j>;R^`__rz`Npp}=2AJE9O_F#us2s*LdDI`1(^@n}0_=`v_po%@z^;d3 zkkt|TW)KAOrx-F`3=P&E^wUR$eEA2Ic#M-e$BU1FWZHk2CE;{b*e_M>B2|x$g;pIzABx7j#0AdZARztctv;gtdNsR(?H+pXlC%6ed2gj2wEbcG7bh^T=xN zDea^j_L$(G@L*vJ8a?YtznT*~Ea7@QPA5~|4MGsNa6%RUw=AAfYOrfCl@N z*On+L_CJ$q0F8~ERv(kmIC6*yEanD<>m!kP771hq|C{dLob!o_$v32u=t2oV&uHC? zipI~%%~DNu-&RzmsnLqAmHlE!vsBx23;UQY4?HnB#6ia?o#) z?o-uPaL>iwJK6VY=VaNQ%in+A5TQ(kfutKdw;Sjnl1qU^>g#?-#D5Y?a_`x1p1DL5 z79PKh{d>X24gymXP(u=8BmnqQM6khkNbpki+Q=DA&OzsmtAQ2HVORr9*&ajM#}42ZRpqh=e{Xn5^!hh zf!6EntrvaMH*E4Z3~qcako-x~*G~(e-+TF3E=sGOPf5!JY7sX5Olak`<@eUA(Oy{h z?S@BIb~IzX`sQ0bDkpZ%x(;({(Dk;GJko>)bfQk6ymrF=3| zI5XG(>lD*sP2JXYbaLnL&%z2f5@nPp09?!GFGO22@#An`&SSN_Lk)( z67H>1-KF{XyiYeP@NvX&zTva;9@$UVg zMvEwxiH~?&HWkcnl4y)w2hAts(5Zhb*SL3YyGJn!u^1LTm%A7W?FlM+;Uu8;`G(Q( zghK8~DoX0sIqBho$Mb`48s$Q&Douj#=m4Od$ehuTxg+2v`fBu z(v{806B_ed%;1*9WM)=#xrm0i(Vg1K+{We#$#D^Pk0gUYTvKHaW0h{7qJGBk7U8*7 znGnZsF5K&hEN$spNOsb+V~?^qP6F09^ZW0N$|gJMx{I}N+SEO@ zE@j1*@`UX1yDDFz^7_hoDc2?DIwo6Qhj&Z5oYc;B|7dv=`=6w1mFQH!g85?(=&GB4 z!L7nt7Gtp_AXO~dhBYq@l-|vE-_YIuaqR2H=S6ot>9ny-XjY+IVQEh@#|5S&VGtAQ z;IG{)`}tP0R@qwOgNlbBDMO*QpNj`pA{r`IW*t{^XB23Y(=q zSIEl$J(TO!wbo807vZFD5jCPKN4V>n+yA|G`=qwg*4WnlJw`TqLvrNCG-9J zgq(z(7NrellckyfQIsQPZpCk22z3m}rUmEZ7kV(|kt>2;$>#M*ZOdnO1TOWz5#YjL zM8<)%s&e*BD_wUXasZ%M*-4x?t1JIkU4bi$p#74HQX>Hg!O0jx;r=^sg*l-Knm_$@ ze|qUja!%f~X#P?yxl8BKsh6c^lnEc%eQWPJRrK#kjbYKAKs~2qMflHZ`!pP1BzZcT zf+_R55l)bZf*Z+^7@nGggyC*bGr|;;75SSp9|>Yc1itdh01HxR4m*4)p2!8M6BUSL!q!D zhRuTvqC00}crATjTHTOlY_c!J3Y`EhTw{qiAK))O=GKiCgnG-oKTew1@o$ExpkzPI zIIjHs5C#dB1{&MCfSzZ&UTtZ$+e2QT6tzVxJ&|f-OOgYqjcdW|j! zc((6Fhc8y7Hho8uVST4{!4@e_^ zN1K7Sm?*t4@d2;UkX0!5ks;u`4^3D!*uJsOL>KUKidk;kw2v!81{AT#Ph&XV$97q8 zm597j%Vmkbrp22BJ0Gh;2+xd4HOSZrIR|j<9Px)i4tDPDcVvS_cYRaoLpr1%0)RWb6Kgtg zF(Q>Kkt&HuRgM7(X)&T)!HPfzF_e3POG4_(FN;w+V7e`e?Qq)EX*k_rn%49$J)(VH zYsu8ugMAxMzS^u&Zl_af&q30j>dZ6RPCtbVni{v0saxq1+!E3|o?3-oLJ+;9l(ROK zCIfUdx;x{p4l#set+C}}9=I5g7DI%qV>y>FEMy?37>SzL;kWh9fh)q%X5Zd?Q`=U)S>_+Ja*P{x%kFoe zxgi>vvQJsnO;CnE?>+eM9=n#c3{AQ4{USVeT~DmsTfUs3GE-WFMdx?Ax&1SS*;%18 z`}19f75RI7lSHLhHnmS$T+LX{@-j|MEK8xAacvnl0jtzX;wEUZ*$Ecpp=bf6>a6!i z83cB|Y`7b)8+0zv4PkEb;%PN!G ziuM82n*iEPBM$_LR;-fFW09eJj*nCLfe5dq#ASv1n&2{a!YTSG!K6a?EE}~j<=ivH zo`zt@K65sJ_>{8fNN1Wur%eeJkg(Fk`id|8_y5S{p)IGS?1g=m-CRBp>DWu^Pq&19 zO3q{0XvBc*qlKS4xM9P5@FXJKuj+Ri5spEPOsqzZ@oP{MixBtPdS%k;^Sws;?p*Wn4Z+?Y8h{b-Z0yu-m+IV<7mrL3+ zU^>$=j2@y`A>(1)AE>Ik&phM|)A>FreKu)&dqLI6oS}T^q zi%sbjqxRTlkn)VC-EA8Yjfu^UxdIp7Fkxm|bEnuT713l9^?a`7XA>tPB;J|lTv^Gf zVUc6H^;8(2`{CIaW~*oGrSg^Q*$VVwZ;3e~5V^$Ce0Ll9SW`2lnj}rCC#}|q8E)Ef zu@nT5x(-8Mje3($e3^wxLy*!~_g?N*CwGdKO=9TtFs_W^m09_jyt@LzHCYc)9`=w! zD>v$#JqT&^$;6mef&i{Y!l0dO)xO8@A^nBKG!#pC)kFIvU*eTs-YZM~Qt6DEQ3=3| zgvYWELxBWs06s0>2hlrl`)IG^lu(PCmlR0%7=kE>Y!rvjMO^Uwo+xaO{Wjd<^f}YF zy} zDpKk>Gd>k|_%*X)LM`-t@!dbeFDr05;D zYdQ?7cuAOx4#|;ODax+t{F#leaEUve`!N2LxsIZR`l4pI^n&;2iR7XQ8UJzRgIVGF zNzM924I)V-WlXQX_}>6Q;Y)OA{amc{d}96lQ|T|y>c13AFI3hqygvBykeLE-yGvwi zbk?{#(JALIVOq6*x|L^?T6+FediwXlm$V{sr8AjVW{p(_ckyAyU3wTnL9w)-X(E$t zEwknFddpL0JK*(p_@&I(*wf^m9A5Usren3|bu{4LYFAw(y&oqXPJ`^9Wy(Wnq&P&N2 zyQLj%6+1>My7#10fMJe zp}%XtP4iN>5^RlyjtDE4;S0YrTxov1*=&bbyH`*@%wv%8H8ciLQ{;;a$XTen*~uL- zYocpoO^>Ec9j95#^C2L%Cl*!%A<5O}>u;#u%XNQ{rv57Xc+*?5bF6-J{a;QgM~Aos zzs}9?c~U;Yp7QMeo|mQn21R6ycIj-l(sFkTa5jl)HC7j%!99D7zua|eM8rP1Gv5)` z29&W}51<-*!|_s{v+6D9n`6#z8PsEFmEFKQ0lVmM54`&>dy9?L#28kZ%^1V2;UZ2qC3pdH}XpO{s0Ls54LAiQ(~5mo!}4-HaurFmiwXSz`}9p?dS%`d0Q`*t|0!r zQT*i(u{Sar|G2q72GKO3gJ!qHJr%N+bo|%F*?v_zd|;5_&ry|Tu+?ltrj)ADG=`it z3g4aa&FYs9oDnU;ge*7O1y8nS@j7}-&y_YR2VT1#(RBUM^!faou2zLN{jbAyF?ii) z)X_T5oMj0s5Z1NJABB6ts2`11mM1K6rqZ@^a%@)emzB0zI#O>Kxeh|E@ri11S=gO5 z+UxqxUPxuKSdN%BzcUILZB)3?_nJGs{N_mxwOEd&zi?q}mfqxj(c_a`BL)34IrTIn zN1Gk_?GR@hlz-!WebEp<_xt>Pq0A#cWFC4MJVC5GdNBX1n`+Zd1*LQ$yW{=B$ar!>T6lcWGjFO_ z0lNGmMl3S#2aop0e8*h%EN3iJ+69W&SWSON@B*t0V&zL09{53PnRL@-REXlTPAM$fEivy>&>qkc$#k z84aO`*J%@9HiIOJLlJocHEu;^378(!x-utVpi$6RBN<1qc&ks*lu z!(7IE3vavpDtFxeu-CfdneRx9efG7EcFx4#q(X8&(T(`nDf59-l$8@@rE@-tCI|3r zZYJ%6b>3nwqJ6`2qb<=d^zJ{3PWB~}&ByHqDCjt(*BUjYo>CN8_|XDTe2u#(aJlGn zNAWx7ms}4CqLfhT&c^W@y-KR3w>lf`W)X`GX?M&bKUzmBcl_Q}ITa((U|!MEV^gUWr0&7G@X99YD_3vfB>ek?93|9Kf2R#mMZUpN6W;0Mt(b>R;Y<;&?ag zncVQTh<5)jOA<)J_z{OtR36nudh*v1R>p_d?H>*>Qzu2WZSk~~wB^33itMZ^Fl!lY zypb4r*^1F1!kAlH&bO8rxOJA)ro#)HH~%ahsP#NAdcQc)!}b>@w?Ff*BI6Rvhz7j+ zyNgh%thTM9!vH8CaKU{5d>kYB6Zi2~uk>Hl=!3h$=eVKwAOu zN+eBx{3}t{D_Mu*!`tSCpxCIBSh%qxLGN4K>3iB0e;11mt<80;+deG$+EMm=u;h;BI@hJy z<_UCz>tP99?3N_H>v2P9x@RTT4M8y=g&j~(u9w@qjN1Stc-7Zrhh;n>rW)bs(HPn# zZP}yV!!Z!6P!u_hC~>YWGp{WZ34|%)c>T156?-FWT=?gKT#DLP&P%pI+~^@lEDyz` zh~r7cu?_fE!YSlB z%rL1-$w@2ent`guL1+fU+NE|9lazU61|ZA_ zLMJ+&*p9OCfl#@a5zfuA{#LKW#!JV|BVOT~yrqwL{wTk(BQYqF(_XB}LFtljK`KA7 ze5a&){9ma?$(fv6LkQgi%&Rtu=_V=V+cMv7NG0?=Ir(0+f9~r;atQi5Tc0aQNukSv zvC}F=!u8BlsxRV)l7-ac*w-?zeeCB`lw-H&&~n$UbXa|VkKZmM`T{3}Qu);KFPDin z{Aa_&Js4G@f;D1SyzGJ5kU@hTR+y2g5>TEYLVi`s2ss_U2jNGM+#x)_Pu%I$cILrP zDPRkwj21RE9t0e%1CWkMuAwyLu|PpJo{|b%24PhBQ4OK}eaWs{k!-3xh-VAJ+*9Mc zzffKIB6y*u>Lt$M(~xLMDx+vsz~aa!$$IV71a_yL$prdZ1nD-LyQ`M+J1G?Qv&=me z?CVzb`79GQ&1y*yqW@>3@rQ*^C{tMrG|lME&s}x(i0SDQ$V}^xkP(?%^ipQZBhA-F z^zWgt6K~g}f8Opn;%*vlS?DA~%x=+7%g}w;pEQ3*KM*=0@wRTp=AFzDjmlb*BoLx5 z#P)5(IzJr56ass6uWnP5n23r4UCWuj1z_ z)uow6ax(Y2;#DF+bc)*;H%O9s=^%s_FLtk;lTKg_DmJ}5DwGoonXBNqx0hQ<$l4qd zC6rS;c$K_(xHnto8}k?y=FevJ zhRb{LwbwL4y({nX+y+MV>oYG zc3?vWxTP5=w5^cizLG0qlpbcbFFlB+Tw98{lyyrGtTZou!l^9sdRZ`BD5})um-Hb1 zsRttVV}6XfY?MHlCZlLkv}f?n#jaVcmxJ(Fob>>(2m!6WcO>yz$TX<-2@wX!Ya?~n z2jV^hrb}5{;l_wS+#=WVQw#kw||`5JYWNid6=t4=qJoC z@&?48eT;OLg2Q~|%GmrL=*f98P)FPKvv3Qg_Lr)ew+Rtu($izQ zUeW@;JTn|XQ3&P?7wOCkbBG=JnX6q+LHOY}x`nfhK|c#-*$1u?vy8a(a&lhzhisN) z1YRbzoa`|#VMv>p70S3I?AW40N((kdbfMNgj;hT=i|M6>9F0~-MRzUdw=E|Hl5Aa)fwbZz6 z|9(BNsZF8os(aJ^AmI7CE#P{@yI1o=XU6H@`uC$!O26!xz6S9hNg>)jTP+fiewI(B z;1M_SED|SWI9-ucme5OyUk`^AqAeEk6z`M&jGlm-I2xQb7OOAy^hnCXhh(ux5cenl z-qW;CFu%x2l9vLhV#Gni9^HPrebjk<=zCO#_`r+A=N~emfoX24E1?do-5R{f4{iNk z!G*#yIgjM()Q_-qUanFVZ&6tq4dU+%P++53Os!4!CY{{2oS}7aPGCrtglxx9c2r|l zMHh=~91*O)f`EP~#40?J4TH@iXC>1A6~zEHvaa5 zB>a2oW4YCXS1GClyC3L6Z% z7RR9p_v}Ys;HQCf^h-o=@BQ1c6a+%L*xQH@wms0Fxh{U?caILfN1Svn+hZfVw~GR!4*$$} zODJ~MzuU?EodJGi2Nf?M0+szr5Pk+=E;8V8!qj6Ls?{-8Ihug9z`yzH|C*ydr+o>9nIJ`*3cx?s;Xlx;N;i6zL%2iNsG@T05dP z2k#1A!H;xFu!K5&%0ff3I16f**N}@xb#s_^pUp=2Df7wslZ_#J8p!4Hi@+f-^lGI8yl5rMmvoZ`ISN#Zt>G+ zY#LE3m0TA+UeYN7zh_W;ej8Nqxr25KQUAfa$H>fT9~{-U-#~TMJ4-zxm+zXf~hRYx2BXLA<+fc@K_x zkLCH|AjzI@Ssxs+1|5UG))ec(%ZP8eOESH;jdGBPmfzzHA?B~5pK$Pb(>|Ao2i3kZ zr(~#b=j@Y^ARBR=_%QD7aTHnj_GjMS*@eM}M?s8`n-c?r20$CMm06P%OZMuP425Y}IDT|y$ zx%j%wHa*K+)*vYaLmk8tcHr)Bki}4vA^MyC4eL&VP&E}$*H9+YB$iln$$li4h&)G=JkNvpU89dNE*%coLG@lU4PM^sxiFes()H-Q$N1bBW*>z*$r`ii2I;(bOB?AB>h zukPK+>~XQZ1hGl!9A%=CGNv{Qo|C+(b`dI!thw(zQLWRV*$+Lds4$<82z?k z{k7B)T(hDC>hq@5@Hw^Nh?G8<$yijdM%^{D@u}VfgU$mT9qXT}fkeuu02u8FMK2{q z@2<|GqS<1O?)8w5({g%`J=K>pdc$ei>frJGRVa8T2%cm5@1Vl!#{3AyBxF2$t zEUr-s@=+}6*sxeg?q;IYF>}~pL#^9kzmoZZY4hmp)yl@-WJHSfUXXMGQaNGNYDVR3 zhj-SFFWlX1_R8BcXQ?dL_sIfy&8n)&p?*d_kZ@%tV+(L&&LU70yE=WeX09YUu`y9d zXt70;*vzpo9}LZzD`Go<%7;KbPli7wQ`EImJS8)QlUU8yC^wnqV~{rgDY?iu$p;4e zP0ZfCP_m&47ypl0kM7Knv31_KPqJ70>@P^>Qou~*7658a2$ZD{yium814dItD0G5? zite2LQ%1)G%BcokJ6o4ic_SD4-IVvND58-f01ibia5 zy~c_}fuNF^=cOTX55jernkage<-f8-Y_Y&x$@05a3PBhauM)OMEbDI+lo-b1LW+8a z@rt?*b0M+K3bM5VD9#9s>g9}GO$_q{`ZEGGX9=|{k}=$k&Ex;ZPl`R3Yf)qhG*NCV z2ZnA_eQfI&#OS{YQ%2Ayy%01sP>6a*(fs0^@ zr-W)ecphG%#FJ9(5a9~>M+XSjkqG`+--4PjHJLLGvFxD3Xt3$#<*YVa=o!C21z0hGs*d0s1<2ewg_qCzeN@hzXz>dn#s#(_sPAAxFc|EfEuEd- z^W5n!OTbvL)Dq)AOaq!VSoYqNpe%{ZBcoF>?~bD^#g90&gKj%cUfJR{6_30`yHB3= z1;w2XW!VO4Zu+9>J(Ov!mq-T`RXwHUi@sQn|;O%oAE z7=})A$T%5JV0RcN(`^!Ha!@p#gu+V#&5{yz0Fdn=kl7O;`jSZZfh@9WE${0}Wlmys z!P0NC_~a50LLC&91WH5+^-}_MB{?}4a92Q!@-@5J#vEb+Kr?;;=jCv1iKP0$o_aGX zE(8OAN~EOkh}-xD-zP96p83Jf)z;2*_49LHUK5ahE3=x#pRNk3oOsQdS#xRxzdp#` zWW+BFFK}$+D$8UPeDSE7+jCTDZ1E54E`jT|e)U(j%plJ2TzMv&op zC3HTj*)-Xur(_=ujvS2SjMCuFdUXcrYi;%1u|v{M$yd!QYF901tKYb6X+7zulYZ$5RGb&>BPMF z(Y~vURSW94_S2&91=o$Ct}l)j)m-#J^zUvWn3#eKn!2nvGX$<3OXn7XZuQvq+RXm(~2~xaHrr3QM>}bCtA4uqoVr*BrwZ8xv0ZyRh-T zlS>SWl7hX`Umg}qhe0l;@~x^A(FWS<>YPNnpZC}*?~J$e3Lcll605O z7cGiDwTI?7sVrzM=jYGd{^gI1lX>cv|5RVdeJK*L(3{HJdKX@G8qRdbw0l0~CRg+F zFN%HUlnv*I7L~}NNLawBjhb2sjGu*_lkyBTCCyb*V?)Wgrj5|5a^_UK7riQf5*u~h zFR*aRdTI9fx?fng8FpbVJzDQxYm<4Z@zMAf7UAaYjQ0f>0!1RyRKc*od@AvwI%)EA z8hxb-M6Dk>@l$Eu%a**988&L(6Hx>GzkuGvy)#<4Z^%if5n zN77pIGn%?l?;)os|2YM^QtgCMeL_)gKlS5Tff>cXrfpmodzz>j9!u8%Y z&R?gK?tgU!mH*W#NX!lE-8!+k6(%hhvZ^hw8lj+KChs@)PxLza^h_48t^=oH5Mo!i z8wV#dfn=H0z2w$<6Y2Pkk_i0$Px$z`9sa@<$_F!rZo71$a9Z)XQde5!rB*3^_$uCd zK8U?mHCm+p==b0Jvcz1KN&zhAov(?L6~-Y;0EANQbd6QT?`no7Jc0#bqrh%KLSBih zosH@#%JmB^ev6&=KLfx?$s*qtx>Lr1$#e|AllL9naH|;@*~zQ7L~gK$3Bs2+IDfPX zXOR&5xAVOBet(S8O+7ogeb4cnKe5k@wG`IiHTv-ZF$-wdIR>gyWYYDv@9!AQYjwUc zZW|XUzv6uPuzZL~(B$b6nG|}3SN-_P@jdO&t1Q3g`@~$i*8Rof&Cf3$a729ne!4H1 zI8M(MynKG}O|US1Rp`GzzfTs^RaPJU`A;9|-c!NiWYaej$`np(YI}Jrl+pB;YXYlf zMUnAR6!e*$M}C+~l0fnE6%4fX0arlw1O-P*d9KGoY$a*O-Cv1QWFt*}GVflxMiw&( z#+w%@fbFiplW!v!=g0{k4dE4*rly8lrW`&4ERR2jN@SIBMiu-@4+nl*^?|E_%F< zrSxlXfBO4n5q|?_pYoQw2o@$P#HC#gzmEYmN)gBm?yTG?mr{t&0<-%~Qg9)E9nPfB z+WP4nS84cfiB$d_Y~JS5jeG8`{_j>lef8`5HtBnS`nJ7z(DUn5?K|v;yH#N47j3kP*ezRD~fC z#r*6Xjokv_H$1psZVh^IH>3vkzdX^VmQnvt@h)Ei;B-o!fb@s(HjtZVq&!e;Q_iElY7)S+E zaRop+4@Vyz_FU3NANA7F0ihIs0l;sHN73Rsl47k?IpaOd)c8Js%%FE1NrzkWpZw9ljm*nVEsI@53a zb#WLCEPwf*Iffl#V}d(Nem->f*E};kQND~ zGmXGYtCL_V!`PqAo3CV?15^EnikZH~YgJ#~ABXZ+8$eVI+L%xuzz2^wqd&EM=xSl$ z7gKpjHTg3GT738v^mmn4aPs3xg=_|kv1=>0*ZW(XLYeE?8w-EWyH_?De6n%?!W%03 z#yDvKwn0}4U|z>HnZyuwwVAKR^Xe+|ZisL(FjuSjZAax&>ic3kEN2Y~3j0`^-e}xz zb#FWk3IzyN(G^)jCmXh7Y4gQ&uieywS@jd1)Hcs zd!W8)O)RAMgz4;bF>_@iyq;lxYV5=S3&(-i!($HKEnqDq%un)J`A@@uzWfNcY@cV) zXkkZ*y_c2ZRVh0oJc@Xr*8e^$jFd z)S!+`V`h5lrqEa(fHD$8fo_N&=lIoiT(|OYZqeme@1eKCId;b2+Xu_!v9QNjLD64l zs8eTUEM7e+|K+Tq);3KBoZ;Gw&r3akfP74-$d<_aZ9Rn3(}19W@-^+2ZOW%Vt_UR| z^P1#tnGl*dhZU#G8XfM%w2`w=jT6c+O=linqAOI={0fag0CDC#gm9Z{)fm?A*}Xx{ zHW#yD`7h^Hsr6DmskQB3pGr(+IlW|Ie#s;t(l;e*<#nsp z=L;Uc|oj>Y09%@Y8`yaUbq}2t{5tHvM?{qq5)})#bbq`X^ZN;+;P-I^oac747{h zn)uDVra!Ym{U`cv7fbGfzg`F({dh(5?<>Tms*U@7ucye#&)vMrh2LJZStB}2ir!2*UId^|a*;evQ`z&0JX>;TyDK3^K`Q;k^VS^#4^Aq5TR~I! z4%7S{Ae1mQs+ek-&M=dH)R>;p>&+5%mCDX?S8mO>J~gwiIrGJM=F9EOSFo%B;jAIO ztP#5`(GLEosKnSPxkqWH;6ijKOrI^8C3VXcG3yP1cxi3x#z3>_Kim>ZQ3&}7jQHRc zDHBxR>@}~X6lJ5&trb=t?n-N(|@1IBdG3E{X&Z@H;vgZD}~fx|hGR*)B{vkmZPB@}RUm6mOJQw)zeVrCpIZ`)1>&p1m4yyYeBctF)Ur}W{%g-Z4t!l4 zg-Whgy4Iio;ENW}j@1=N7YPw1LI7pe7d{}A9LN9(5$qrWTdPaA%2``$*vraE@X$eJ z-r*gs_v5w++WBAg6+6AF3hN6lO7bF}W(%#EpTMerHoIPhRo;uvhj!;voz|wOm&_^` zBzqQsprvXPuH(ujUEbBQ=4JdSkN^r~t^$JEH}C;K76uK{<_%`P4g3Jm?cE0KbI|4M znmZYdK8uB16L*mrYW|;!F6dnpc63&EutGbv3nc_Iumy`wRj#=!g~y$=PnAhlO*>Jy z5<~Rqp|nA%>W6lL_ZIUNTEl+ZWp8ckTy4ndvw>s4xlfdG2QeTyB*gr@(9x&W3Em)U z-r&32`k3OX&A&A*(143qrIvy**o2<&IO>-2SjKFrEfMBT>){&wM=^LqA#c3xQ*lJf zSGkhk{z>EMBwys9Xh!b5LIaB;vA5puNqb?GqDBVGv#$zqbGDr&3C1d|y1A{*Y>m7m zuni6?vGJp&O}P z6i+1W#=A1j!?-R)e<2rHdU61v&lf+7Zot(S+o{k~OG+Y>?!-Gi3L0P#YY16b`71 zyCM%jk(*-4!iW%?OmZ6>*k%T*ON2TF=R*8C;CtOcFFL5ic&TsJQWFui(yX-2I0ic* z>XK-n5^KVX9x50swbH$3YY7!yEWhvec0IsFm*2~D>pgGki3atyVSwB-I-|@s#4>4q z0QkEDh=T~p!;vciK$e;0-iMHY;BGrVGA9(d*G+Pb;Ag5G&(vq2-ZRj9!OtyATZ6>< zhuC{U4S9oi)3^_T%k^r*P2b>^s}u}5FRcbrfa|W8V3`35ghCR54q)y$$W6q5 z6l^3g)exHl398BkYgeWX29RCnT@XNeBpr3`5P2>j?{0L-ntMAKIB&~Wcqi)Zi(#Yw zXoE?<)0${=|ELRmz~|VwUEe|1)&}PZUrV+|4gko^p~18L@%aSMkE0PA(*X4ukQN{F zRT+}|&ZJ-@4M%`9J0OMth}FgrAp{^!8Z(>$_ke9wnt&RE335=7!68KT5O}M*mw8o$ zjTGsb2dv5il9xneeg|T~LQF&;*D8=&dZH>ZEYs~ZH}MMi{UdV`$3&GQqMTzQ&5*Ub z0@!Obp^-R|Jo~0?GJN!y%X1;JYR{m>iwHBQp?VIwum|nPfyCO0eC;6RUjPE~)0HBeuidA?85spQH;W!pJ3X)2istShNB0$stI&Q21`vtAZ zJlWk`@@NDsjs(_hmly-dGh|9t7sM&F>F844YCH|7UYqvie?l0@V67Kdelw%olKip; zn+#)RMPWU9C8uO&b(PXrp3EN8zB_ndFt%LZB106wfmN_&&5>p2E$^T0ynpobeM<|N z2q!)+^;KFeT@{})!Tsl;61^DqQ?2c&FfsscM-1%R?% z6L1;$qgfSbP*5_7k7V%>IAR<=uQt>g7Q3rZ_}k9=ebXwveu*nVLs@>`9d)ndqZA=0 z_e4FXP`B!N^H&1vcS0$n&HKXCNj|2+hYp2SErk~FMM1z~Z+f9w_TmeXIm-ZB>(70x zNT}yaxZ~R?quiSp1Dh04t+~bwFMvNVATI| z7Vza)_Ln~ozd-vyf(TpI*NdJTSBo-*00s2C*UP!`4tNz3_S_Odj)Hi;g;ONVV~MN} zYx1&o0N!RZ6Z>mqZ^4)T2(eu~w0+62ll;#6y7edKQgHiQ6V3Vj27=V}=}fvmKCRqP ztCvz=IT$MV5kJ(|MAzw)r5(7To3o++Xv4576NQw)78C&`S9dYNr{Zhy!1+|fw0<1G z2)Rb_AKdBTO_qa78X%Ao@saaj4H7^W$ytBbMxAmKbf2Nc-KMnejFZa7BJODk8CliG z-&-U5P3}Ld?xo+#jIe{%U}XT6rGI1knqqH|fEUI)g@HT8IXgBPK;&^qfavBO{Fui& zK$A4*`rjP=)T+=S!12E;h>OWu6%xvT+$%G`zYtIBE&0*UdB${gOSjKV#|OA1Visdv zaxc1))g)h_uRc4vD$1%v`JZ{pe`ZAt7OFXVX7s^0lUU*|BgN|e7wUsG$%C&Z2b+Nh z-*OIi9v$pW9qg|h5UbLv1E;KGMreSuv1I@7ClNzI?I>XR{Q6vTzrcEyddj?C58jqfd;%WFGrVRah8FZHtb` zDg=Q3`o5HSICjfL9l%&e{s17jdP>F#i$+0Vhlg9W>3>sM|1HZ0CZ%85v}f2Upjj@q zWrK~v0+e;bVp|`UE}W;?eQMzjsCErUxm`6y^?-aFvL>QbP&&SW%#jtd<# zl>97m-b4U6btFqbc4Y>6yfKk4A?ww_bL{y>1L-H|w0P?Cp~d>K*XKofUnrlbcyb3m zDe+@p#GV%pI6OBrkV0v~!UIg9XBBm@>P7L8ku%5$yCj(V=;bPr-&6{&Ndl9TTn*lu zT(R5ppp(*e)+i(dK< zy!`n*c;qb7F|<#j2OaL(3P>U4S|7g?o8zE`$RFZd0Ro3+n%P2I02Ueuwxljw@&o#7 zy22u?my+js?r$1u&>JZ3yTLJG61)99xC70Z4mJe;3ijnguXCb z@{k0k9IY*tqLG}jCzS+FA)WmtW=KgjUu4;QUT)oXNNU_uaR5KxYD0o)nzKp2m2KCe&YAwgBY=ojg7|Ln}tB(nz>p$O2|U{}A9YTc>kc1vYEpGMz5nSjHAcXp(2a9^n{Ra@VNjC@C8>0idky zeZfK!6S>j#mC>B^Ux}T0hViod^1-&Ms8=$`>^m>zrBEJ%gD%q{nJ0;Qp`0(OMY9`y~w&tB7-yoXO3Est+a8M{u=OoZ`7mA#kN;UMJww(tIv#;mE zSIrstzOp2$>EvZaGt7`LjB9CoLUKI#J*?zNIxBy3?)!TH8GByqTmwgAognU3D=BY7 zGqiGTwHhr7q9;AL=21y!#Qk-|%r^BEVuO$Nzr2JerjN3YwKI$42Wt7}7W zGXTtMHULGV6K`C1mE4FLaQE&24k}!CT6mLOz&95LK}>Q&o8n`sWbdXL1sfvONOZo0 z=bxen>57T4L<;O;5%fL1f-u_dHl^si#Uvx9@@UT2~~eS1p}daa=b?X6DVl0J^EdKn_wW(glIR!%cQ zU;c;)iVb6QxNH?J5`6pE+s|hurGTmAd3`dES1t}9HINGFUK6CI%BR$`z4v_ok%Jf& z{YI30HlOl=%$v65I|&PY1|E`xPXth$fnxKV_Fl4V@Dlu9aSKy|m!<6FP1Z|XKP1`J z$Y{oy4r)KHDH!@7JI;e&$o{pVS7?6arH3#dt0Mn)iTK6d7j__visp=fZ%8kcoJAU_ zHlu}r_d}}^1YUSV*ru`XXsvf&DfNxU`Gr;+Ol>4!TVE`pB05FJHk@ULUo-Bd<9obT zN22e}O^z$~wHPbj{y1fDIl#F`fqj5RL-?YMn$-UKn58*1OYI2xs^Ezvh z&&bV7{5es^8gL8a29)0DYS4sDYh3Rl6hJmY-f~PJi6Mc{9$SR!+>y_@5@`zL{#H7kOwXAi;pRab_4o_f<`s>sOebWmC-D!-RFmkZBjqWZ)WgtHYh z=pUvh(hrv(pKZnei2ov-TK@OD{b;xbG8f0n&kdR#P}1zBPu#F<`g72NBNOk43Ul}( z`r`ra=PV=LkFYQ2rnFbD&R%iT@4ZkL-JbpwyYTqmk<}Ma>h!f8&Y)US4^pz72(Q!W0-VAEVwaA zgE&S1V&S9{AE1T|2uq`x{I9KBaEK9F$sTP)&sTDjA?1)ZGYZ4?ec(BU8+eN0;TRN2 z$FTO(t+y#wU{xaF8oY5DOa%qri5k}#C?RPL)^_>A3>;V(N3jhkExivB22@=4Q3b!s zC87U#7t0sW?ZXE4SZG4bhvYnl6#HM@%qmbB)>Pg=D>=SWpQKq2(GU~Y(!6Zvvm)_7 z6(jG!2>_DhuMl8C7!X2Fu`rt`ZwHp>z{|E%w!-lDo4MdvioOoD=}gM<-lG4?;bz1C z!Gtp#dok_5wQiWb%Ee{2{${Hpj6D8Cr~+u;=Ex`&9(nvaqp>%A_ei^LoYF~4+pJu<7Nv28820SkHhu%%&1k-2`tfxPchsfm z5+z)cVw}ELZf~H9KeP_@t~+D0dL5|ulx_UPzV-g_f4=^&HT!ke-i(4WaO4QEb_b+F zOIQ1f0M0OD02=C67>()LB^s#KdgduKtvA-kX#4Idw;+`BEw6EwZGQQ+$Ahru3dusT z9=<#wFS^{Yua#PZk_yi!){YIL3nnCM4W1lNw1w;U#=Y(`)GDoKX9Z0}4C@=3K_pOs zvWZ><0o)JK4dzHj1ap?m$of+>!Pw;*5X#?X1(}vAoAadOL2uOBloKzX(10Zs+@1_C z%Pt!{qO=o3$EF8pioNbf)u~GRQ5x=-jrLX;e@W7R(q#yaUoS_+zbtuefO=~<7g%ND#{r2^g2kw$);y7r$0Xcvi8>X_?t4u z5B#cl_Pd=yazp2QzC_y7*L(mPtv;_5QhzoC@AxG*!*a_@HI>%%6Yva`ydG z(eH5&PHN0fRn3r|Q#>ghl>5q}e??C?T6K?&n8rJCMzf3(??L{vVzILZA~+tz1l##r zT>uz74$sa^Do=htSb^UNw>V7f*o=dV&KdZeKbe@6^hughS(=DwtL=OzxNs$2*Nil~ zYnj0`g!v&AQ4I`Ci;fm{zK5d5Ak z9wLYY@L4?ZG%%aw=X<+6sU0Bu*h86K?44diH$#dMhVz4Gr~$bXuX99G&}{?ihE_G1 zR@aDHnAzk}xNcN_k7}N=Ip;@Rp1DgIYdy?=vLr)omSmphhk;2ru?54LxMVRHy4;et z$WKl&Uyq>5UnfgU5YV4{A$pH7cTbsL(BD|KOZV}(o~nUfS4rRZzH#m6yiDlVIS{*) zEcW=_{`vx5rr!YJPCh1rBI|-}sFVGPjP0ey$Iyst8f2n8AxT!4pmxQW3&9bT2w-Uz zP~5X>dLrB_oU)ggPLn%QDV$+tq?5z4*oV-zpaSNmbkve4hu#yK9=?2B>fo`QNyFmM z&N$Na5Z(~4-S}{1cP+DK^T8dfOxI!6rX4uFv0Bl~rJ~9=e950m1aKV!@L?lL*@_&< z_V6VZ1`^3VGh04aas&(vjl8`-zDYCuZ0Pk!QD(>T=;r-5WNfJmj+10g#7{PW;oI-u z-u_o>$o8}icE|}^Eh3L{f_~CiRCBtR0nj{i`dr9dd_&^Dl!Cj_qn|lf_pzeE(Ia9~14infhcKm`oDFP+i$&QrcC+G~R#JsdSsbMR_42P6!TUAEf!t64%Q zI#vUyUKR<~Yo7fj{gD_r|IDN~?4p+J;@9W$w^CEJU~Fe(Wp8dJe|<&7hm|lh%%g`R zS!aMt@B#?1e!Ya#2a0mie~?}r**s_?4!eraL=YA)dvP={Jk>uo3M2&sG{cR-wE|P_ zWVH6jZp|g!wTaf1Zi}W0?b^Jf-AG(~xJJp4vv!HoW;4lT45Z)?#0ohhz)T{LNV*8? zWKA=OI8ze6@WdHsB1Q>2_+8TnB4oSrS#sTe^u?jEMVZwQ+JDDnH$|p&Q?6Jt{9+ zaPe*?)H%Y`Eh1idD%ue7106BvB;t(@g_myPqwAVj&{4{aQkPX$v!zYorY3q*El>H; zhGiC~p=O3c&FW@gp^eMj{N0c@t_PN3Clj7G_f2pL%jm#6g&?+0wl9cs~XIl z^FY0cw-r3aof9jv)0KKRZEPS0Q5_vJZ)t7f@ zh&HBv4c-kp!&(&s+Tuh)U2??1wi2>7B}$@j)KfWoTj!WNJKr}Za#JbS$xC3HRYWo(&A9(I-`3op#d?wrtP0_|to(w!9rj zP5ELTc8q$8B5_-XJJs*1$zk|m>OD=RYZ!CJXI|5~`MJ(SN!skzCthuJf^{TkvnPQj zbblxNYog{VG@{?jy}iZnY!w%=j*{cL}QR8t53peRM-t-2#jBEx3zC+lQGVC<=@DFB4R z@v1Q2;LseBFAl@6FeLwG$oifB`1jw!r~rFR!mG0npKm)=zwTY@bo~4~M1EyiFq-k~ z*IVH4=d)1-V=H$%kBfDk3$JWz?hA2}Z{wDYDGyN?`tQ+D9wNQbGBra^&8?Uk)0nzn z(NOz^Qqz^PX^tXOgrpH*`#sK%X<79ao_sv!Uo-Ag?<>%Z(xYE7{=iuI(^nn$8J?NO zKK(Y-bLW0{+QokUMXywDU(dyJDMSbJ=e^v~{Cnp`Suu9c|5!%gc$C2GDqz82^kcw% z0XuNG$E97sVKd9fZ06b6eVhPEz+n7Emh9q@y^eJS#j-5LGsgHa!@o5rH$T|Ael*o? zTdP{3UH`-#-$!8J(1CSSFBH$rzR?kjWLXMxM?(imt60EJH0Mkj0L z{%fU{ZHU5cS10V~Cd*#+a(2~h`p)7b3vv`T6k#>y0e)+bxSgr=+1?}2WJh6|h5UgJN_X0Rgf8%?S;vPC0A zt(&P(IHG;{Hc*g&;8dv>{$49fZJ;9@>`A+ziKUfr$R~OxBj^-UiLrpm+Kz;^3}v)p z^2Cd1wRs8gBkDz6Yj}j8i&VA7$k-c8E|u*_tYKejC%@l*C%_Un*y2yk27J z9QVrzcBb#w`jy0CmD&?m5PQa8k9Iwb08D!!@}e8}Q{9v4oU=B$`kuEBu9{NF3Qfcd zn_=_FDMPnrl73gt5H-1<+RVTS@qikAP79_XS8C&*C4?l;sP%BMu7%o6DX!uQi|JWA zzU)~avN{NvIG)q)#{)7Q;0@N9x~M}Qjg)9+V*Z54@5;^|}`o+4#*JX7j;5W(U|>S96U5 z*v^o-T3l$Zvs`Q5&aucK-{8q4V9?sC=TRFCeuxf|wzs;%dX8W8`8=-`%xs_;`W)+9 z;+W@6r0P2Vvry7eCW5bg-d)h9CH_FsNwn6S}n=|pB6i>S;C zU3%E_MPYcm?rR=LlWjl2(H!?8Vt_)B;MV%b zv$&thQP5$W>C~aJWAybW?_TRz|EB}aaS!gWhS^|ZZjA@uu361L!m64U z&a{M&FWF=-Rqk}s`oDYnwCZEK*YlkNhsQK$2ZKL=gAz$P!&^~3$8VM@f2~YwW>_Di zRB{Jc{j+<aj8m|8Frcbqgt8st;mZU{kFk2FSe9v*2$s0XiEPh2p5Uhjijf=M8sw((~UE|*V9>g zW}fXrg`v$>(zuwJ&3GxJ4?XrCR$K4P5e97NnwgE!BV>GqmGD#vh(xziR9;>I1BJD> zK9is*2r`bl;g14TORNSfQdtY#>cg2ycE~bypc5Ov;fy0?z{Gxih}G)}hxPg*M!QWw zQ}`~g&r}#&Q(ln~k0AXs93>SAP@~!)9FPY9kLOpRCj})V=IHf6cSgi12h!1v)=d?+c;mppkir{zUBZJ7$Sb~{6$7bioCZPE*cyKa61eLOP4|Rl zc}~W!v>y9`5hSp1An#n1AIA5RvWodH>N?|9=H& z|MH8s4$c5F;avY{DZej|c|CQ?Qn>CkS*oE(p0psYVZ^p)9#|Yh3u5ZvEt#nlPpnUR zgsHW0j(kZrbU)U<0KjP*iqw#l;FVTdVRNoVCJO?t4+bFu2NQc?_ulmDp*Jz)kjmwQYt zww@RsP@&~49|>y6&2~%HqlOutTV0-q3CiEzrr)|Qw64}M1fx$tlp(y*r#IX zjJdh9#UP_!-_+RYG8^qm-Ne?uP;@gVU5EG6BJ6=w;2D#r<5#P--r#@ml&JE{Uxv$<7`$7f! zc<5K+j74x8ndqW8pwz(4?5xdQz4Fi8`9v=lhjGRWDR<~#o?yy>w)a3P`i)|@fSH=X z@C779o#R>lNO;A~s>6qO^adXbZojx%zPTVg^*1r$O>7SS?beMu-H+f>zis8IJQiD{ zw%SZ9+QZaEt7uFfpq4j*(^WhH)z4a+7JNN>k^x;hXKdF@Tk|T&$3g|w$6ui03YEIvy@*;?1G7YvQNsvRz6ft29X1}9%uYxmR9_d2;Wvzv$LKoQ9X9cb8xVh9U~ zD!JX+#eW&>>P19Zs%zMOYdr~%e?pr7aoky5HHlvD>DuiGcpkuSK9*)c~+AkW^JQ!!_)n>wV5arc5TC4bkm|S&r;pfTypqdoQHM7hb_-) zyLk2VwNB~Hc49wt#Woxt|ZCcu4-F9uQXFNYvjyn9=^!%sgsbi* z;wjJ&;UATf#FMiB zdR^Ud6R;KrfaO5Q?SW7QkiX0bKyN^V2ofP`2z7W_B1r%&jv;CwLID7RV1#ltyz?}K z^acR_6@ljr;iUywOoQ2TAq&9UH?YZEygy#sTIrsO-Kkx24*Gs+R%bc&1ieSE< z@cujM<6VJy4Jx1ek~0|!9}BLBM07rq@;eU<_&MLsquIQfz2*T}pa>T2%8S0bmW@() zNt6W|d~!e$7NCe62sG>oMv(w=8baY30&$7y>OzEC4Mr?RgqX-hMXJ&<&Db4(2jR{5 zTgMcF(6(EDj9w$#vCD(AQT(ngT6m4L+0A&XBR*Pu{lN-T8>X`sH$JcBH-ShLJs?)1 znulO3TFsjv{dIz?Z>fzy;x0g>A47o!a1cSX;#)o@cZAHm1>|9sqeMt2C0KN*E8(6y_NqhaP?+1Hi$c!6T=^voJuTv|IOcGf4RR3p&o@O8pGE z+lRqxDecDjJD=P1^@d-&Pv=Z=(*;<}tyFzV#T%6${w+1;5Afcu_{h%;uz2{2E}+@j z|IE6S%m5Q$gD0Ev?DJ}>THmq|M%OWUO(~F;sYy#)5@AMLX2UMSf2}<}fA-j!`+6fY zf#U-h=7K>8YYIH1bOde|z56vG4zB(YEQVg{-K}n$Uh_g~ENF+B~FtJf$pEu&KTZ-Jw_BwCR(br( zOKNRo;@3*xP)X2*B;j(cOBU5--5f!`gR8FeYIm>$302|TJ`$zfKkyf+%?>3{Gl zCYx0k?Xg)Ne9$r1l7Id4peR7|upjgAh4P9W^U8+u($w{dqU!S)pW3w>0`@Yh3T^nWN&DtdV$S}SQEnsoc#2{NGWkg-VHySYOM+zQpCKjjVo8FoC2Ngj8p5 zlzJjzSpty0DGg?J%G_KRHQ`ldW+nT)3)Tl*3EMcgGw|x@mG}*>L!H89zM2{_h}Fwe zQbeuST33mv&j`faAlN^Bq>k9H57#|O)xkX#4Ghz`efwo6RAuz zTRjGWCHc7lpagV)Adq%p%Tf$Y5j7C-AC8$H?`L!7w~MxXg#-2DV0w7kZ8QaHkMeNG zg5$K$Kqd>89Im_*CS4z=X7S*icX6Wy_orv-TprF+J|&CKG}J#98?J~8RF4#RZlP#2V7gozWvu?sO_bOP# z;@RtgOjx|1;Wc3r$hrW8vS8TyF;|RAKu&)=^V6T~Q9!18Ji`p0p;tUq5omFRy3Kcb zdq`dq-i$J4aP{nwWi8NlQqgowGxI7g`-xwwr^nqx|Cr2_%T5|?3w^CM}o@XY5Pf(N&}Sj zcuvTG1tEerC;ons)O~ewdfJ3w8zCxYQ+5zV&cLVRzY08AOaI zx+7HV675&^MZ&bK({-ZakZD(>KW*|+pIaF?;_0F zom8hQlO}ZWV(3K;juE7XVf_$kNsjlUAF$@`;ffltP)wv{&|tK?8R`z?P*q}D0EUJ& zaR%d4=es%e@K>BHz|QH0&av20zy8|ESh}a(%#LIu?gPnucY~TR0gZUBxB>s3p9436 z0YgB})4OcJK>vI^GiiWb4g;Y6dXU}?$|u-3FgNv#whnT0hsD>uUoG*;tNUy zCO`p`)3i^yO`?Qg=J#+6{J(SDfgf)Uz?AUR!xH&d{WEQU7rDp4h`)}4Tc5WAexydb z9{XU`8-KWA-Hz2j!F_J)@iC2ROY{kA^xBXP($brT)fX?;ZvMA;3r#srg5m%#r2g}8 z8(~-fw-xpu7KjI2l3>efsQiSTBqX^%`s>|)yF<^%{sQO$*W6q@OCnl&QC)id-~L|0 z!O6d?gNuY8;Hw{$x8PLjd+b+>v5u5fKt=%WhB$C45M5_@b!vIhk@)ztsiz*6;n>yZU!>?+UC1 z!GqyUa#09ZjTA~&h49}9H?4Gf5u2w`wC+0D?8?^z%Oi{)IUlDL`e1^`S2{3Imr2C?4O@!7tF!ffh0JyLL5to+ejuS zt77COOQ^@|d=cCK#N2-@_m&r<`f5`s7z?dHAOO!F2pKEj6+j$tec?bhz%&2=NI}Vk zK=9FX$TQFhGt!AN(u*)Lh%#|0F*AxX^W0=%lVsshW#Lt2Wfo;+6=nP15@%z(UJ~qV z;_TOVb_sS4DRzD|C#NJQrxYiz3YV}xH>V6Y7YfNOgA~@~;gR7H(%=y_Qm%7uDdGFy)st7Z8va5L6ZvR1lQZ7ev_z2`UH)DGG_n3Q1WA$v6lrNr=ch ziOSlGDmaPBnu#g6h$*^?DanZ|x=2WHs~s0PZbX*sK-t<*Jw&+FC!ba}05?DZE{- zdk#fX$-H2oT$QVN8y7t`*qo{xv(QyxA zVv}MPFtPE3d*-h9LPzfWBgDx^kDrKLT{=uyn*Q_SpB%(`Ef{Zi?E zZdtB>cz*F*epOeoE~dD&xvadYtUj=!vZ21AvwkYI`Kf%5G?M%|*!uH-d8>0v>t!xGYmaw!cXoEa?{^HHzPxksG5+%M0&oSm`hR}G zt^fkCECeudAPGh<>Nws}Gn9tlGpNw(tR2Z@k@a03@2nfUkJQZI(C?~$oev>v&BD4S zy(vbyPgLkXX?$C*6t=tk`bpDtH9Coc)8J|IY@J@dsMEyLYorCcx+9_EI4PZ$qs#a6 z#Iv@KkFi4;oQB=)pPqQXZ*_Xp{cx!#`0GTaVb7z_FQQI%InCF>b1-HjtQoquV{M2) zFXlYi+xc}Ym(Q@usIP1DO^K}E%4FY@Z_`B0OfKW+a;{W@;)rE{1}5oa2P@Ky1nAyh z>J8gldHbU0#}`r(CAUd`@6p%sd@+}){=SoMGj)bSS;XgOdkatezD#{qSzDz*;jUOr zxgg|J-G0R`Uq}(L)a+D&tE=kWAWkW7!!OgXUR_-Qpu(#-2$MH4?0=_MDkZt?)g*+D z@LDp1I&2Vdw$HRe;fyx}#XlB7gKV5zX3zl?Bq44>_l-jxzMGH%bO0%yoD+_K*uHf(l`#qa zi4V+ARb&Si{9sc~kr^_x7<+_4xun9d9TaQ={6|9o!>v&Olm$^r1c9HXxz-ZayX(qt z5EVVZ^8y(pK(R$xb8QaD;Y(fTTNrz7u``8t%-lndfnsujCJFA@}H#37wVpI_hcsipY_Au##1|i_-he&*C zUZ=fo9f*&?Km^JwY+nw4bYpQV9Y#2G&pG7hpR40);u7Qa1qBkBmm zPhH;nWJqMSVw;$ryRJ=F&Ci<{`%J!O$X`XS6GLar;SYg|$nm(pvMA_!BZ`}BCQ%v+ zJxB(bY$Fq4D)qRhpt>2E2^w~U%`;foD;x%)CHFvs_~z%;{;w~%A89x`?-?Y7+Qtr_ zgfLL$d5B2cLnv>g-44-WHb4hzZ&8U`99o*_ha! z^^#NepK=CZ?dMJ%478m1wj&XYow;0hq~9uynTO_k~g zGIm*7)4$7w@*(i4Hc3gZ6U_+BdO&I|QX%Nd0-5Q=GRyP;z`#C!C?$2>7v)3JVCxT- zyZel2TZc=mMC3vhv96vLMw)aBXw6-+gDh{OI=1*EfSj;v$lg#gqc@gJ&JG_J8U zr$N&etR+U*nL?egPT7VgLr(5orC9O<8JEO`LaJ-<+c@ebz2rjU{l0xL@8}1x5?~sL4UoP$)!9rDAIjF zG3O|XsvI{c?P@J&y?2^8jvg4aUE4fA{-IIjSy0y}_Z<{IUC{w^!xx*(F@q!5Xo$!e%dT0th#XprKc zK!UTKHVyd@@aqm`^a~3#z1C5NEudQ$+yUZC_(M)R$x~*1KIE{3Q`S-CDS7a{j=whA znCfXQZ37N+qToI=u8i-9Njw%5nRT!s_;)#N;k*y&)Coft)dp)6w|mhq;|AA)ad3UT zz1BG`O?C^)#h&@j+;sZElJ?-2BEoC12L<5&qv*Wjsrvsoey4lwYhU}?*S^VK-77nL z6G9SFZjvZ;7ukDfTziisnRTtgH81M0&yi!3H$+C z(C`cp)5>97nl2G;12^KyLO_)IP%IpdG=YWR@w{DjkTZWtVkE$p0#%Dy6<^L76IpKv z2A#WmGDzXWLNZ8MubXn%!2z=Xsk%M0vujvZ$u}@8`%a*sGl6UOg$h^41E3gz#&-9K z52w$qU`Z6HR|umog=DlnCp$aKDgi>T25?>rOKWLU+AoM{ONFc@h#5kUod15tI)|56 z8RFpe85{WeeyOm47L2ch!;GyC$Ifwiok<J9e5P|}8AP6}9HlVNl1I*yNjn1c&M=jv1_^>vSXR9%z zkHm?v2gbdNLuL_?t)hD&a=q_XugB@2eI55K5bIG6M@f}dlwHAdYbRaoFZRg4-|K8+@Cv4eO+EiC zed7#-yjAb1bjKdMjl}F;ef?YU_P_@{$@W9$vm1hf&&Mv@VDQhwqg$)xjk(fg(hEwZ zW_dKgQnqU9jzjsaxBkq3ss`vcAub)KDcb!T3;&mCs*KzuHUm)e41LX8qI^9zt zi3T&LMQz@HZc+B*K8P`)Jt{SERmuCY6K)Od`aNTI4n$AfAtDQiLR~}= zhW6rwh(eMiwMo)$B-uz3x`3q6MN(QIshp57NV1wXS-pVBj*@daL*zz*q^8J*CuA&= zVx&zmcB7a^Qp^h|mR%I91&YlH#SR(gpdIJr7UvQf=T;Es(G};(@VGCKB{R6eKp+gEitPrp`akKs4KB}A+hu%k%~;J&`zpyOR9-Xsw+rp z=t^o@NNPDrYC|TsYbSS%CPHv`;e_O#1z`7Ra_>Si?Id|{D|r~1GNPUGq99oS0DQ8L zGI^3Rg-o5+PMvW}or_FeFi+*00ve41v0JI<;Aw{FGz0TAw=bz%$h2+kG#na0h` zedbg?Q+PYWdLt8Mk%bk^;k5vAFj6@9940z|8;}kf$oV}gB#9BTX9hXC=Q>2?Iwj`1 z6y|z%=X%%Y`Yh)9{>=4r&-0JUyIhzTP@fmro#)pLOd8F!*~qRs$ySsAavlnBqH>}a z^J@zLgdhMjs(=y%5TirvGobEt$T=*4>98PU8(>q&oob$ppphwAg+<+k#fycdKMSdX zMHMa^eU;FVmW?W?0|qS%7e3Xhz$}9yE;(l^f22mA#r0Y z;Zrv^KkdG;3wi%%>ESl10|(-3%bs=>zM2i{kd3Zb(_r?Kj=CxZlzcem4pf^03828@ zQy?)s5RHI{(?OzipjgI391V(VD`4`0xYL2>FhDf|=w4#6^mslq9TJ;Sk&;mn+g1^a zWtq__sYO**+p$#HRTdtCExU{Dr+}t(h)o7C=>#A~09h6m3!tjNzKm@R7{QE;b0)-8 zBC~6e+h;&Wi$(f60B0YVHyvV4haO34>665+4%t((Ejmg`I*%Sbapq`7sGCno0|H>?c*LqV+ z&Slh|!!lvVp_c#+n?(Rl6!hF7)DsJ+ABRrV7nt?bofKs<)1a|btH0we|2*pRbSu^G zvS{38sRBTEdx`~x7}taOFu;@(@D?A$GAZ8_02OQF=A=XXX`mlH+#NkJ&j%Ddt~QPl zRisgJ>zRBCR9Tb{NM=xy4cp{#su1(i5@vm9;%^7viGvb5)oj|-0Gp6FIxc7ma_`+0 zfDu&z$asWcam*7O7N}%b#aRSALjyG`0F@4bIG-vBI?w?Ctz&8bS=1@eo$5(vQeW@P z#GVhr!e@0#n(ayo=}h5mPw#z(>?Y-(qk+_X7>KOwm7id{jE4$e01|gwxu`%j1V|at z4nEUf=@9emjFc$>8Xe!gC>y3TButc4lKw3lKM=l&L&{KP9}s~u1a-~NMK3KOP&?=) zi0Qc;CgUl%MkUO_2WEqX`KhbhcBn50=hL?z4(k+0=@h@xD+a9s#^nn+0YCwIA16I0 zxVN9>CxZJsNOuZwO;@p0Sk$JMd-Ot5 z;&{KQeohD+NRscD3j!=&L-1pPOlOj|gOUKRF09V!YGcHnTY~ULprCj#dB9}-*S63UU2-Zha%#s*@X`8VsjmhEgyaQH6ECsNC zI`@Cf2#b-pwEhbVA6Pqi@H}^~epCoh;aYnH=G4O9vLY75J#6MVzH~4C^moFy$PooO z71lQJ__|W?6f}hfcSJE?I)Yi+i6Z#y%QBe52{#u^k*##rZYpax-od!-T!tNd?+tu% zrD@Q8oUWj1enD6z_2hm)D)A#p*a{cD3by(MfDMk-17Sd3wMS8{*bN; z89$DGeKI-@rYpe?4K|11AElUczQfoEOqz4LghOUR2J=IYA z^XrqPw}vsPzdX%Toc@!d!EXUT6-Nfp3Ah(Q-U82(O^2Ukzj!bEve}2VlMZK|hFA>; z9zcWR@b=brqUtoqOvIoR!BHM%3dn<0WR1Ey%j~IL$+SXB+BsO~4eIQg|o zTJ$VXF;r419{f1NbCBbz+gfy`wncHW{ zu|krsUVKt1xY{%9fRrkhO{6RzOd z)JX(bV_|qI_z6w}N%PkBMC&xE0JdtR|9xb;v3BqXgt1#Uja@e{UAKI;Znd&*^Kacw zY{S8D!%1Z$?r2hlL--I1drF4-dN_Im;1*O^D6Q$P4=f1Z4wYQPu52EMtgQmUGFVW! z_os;1Pm!gc?mqhzz49sc-zS3D7Rhjn;0?kYt_YqQDyU*MZ&|^J#Y|n`q-jaI|=|f?UJnql1(@j zd(&NqUC2-}srAB#(O&+Tn10(Kt#+no~Io8I4vHr%-AphS*KiK7I`x~Z-TLIp z-jjcOMqyt*?>7rS{~rAQOT{Emx@>2q>#7^qQOhKd@lQCMX~+41EJNU{X=$AFK3eH& zmmUkx}v)segb0U7!xkMy zNi9`Q_0&|TtVIYjg%~$`@HHS{$r|Pvf!~a+^X1rlqvOi`C}~C)yYlRSl@!-5)xbUs zj$wxD;M2*uIqpkg>A^dr1E&ZB2`t>w*aaH!rr&t3@}(EW?%x6?3mD!yu7NLs)792x zrX?On49c+A^LWvV$06^YoL>L%qU7S8UATZbrmG1s%)-r&!#GUS#>Fjet^e^=sZW)b z81b4|%Z-^6<`~h4eQxeYdFk017Bhf)VpVObuHB|17><5`Y4aa@H!^PQXrSJ?8wE@& zb6cY(+R!mTux1f~ETb7eeV(&o9KNAozq2r^D6;8>OO%>H?kjOm{|ZhvZlEi)bU#xo zPd7ZAPHyE=l*GXdijYLeV4ve^+c5vN|5qYEHm)9{+7$Tb*)3=CVqo`KRe13oF@mFlZv!>_$*Ezs`SK9Qu0itA_eK?IcLjHRFeV ztZbbb63`DYJgIf|ihm(1wu0%qxU%I*tW3X6_so9v< zUct9=g3qGhd?FVww=b0ItpX9kBjkiL>+YL=Ga~-=U1KY^DTBw^7tvAol95_sQya^w z>UUp`|scqw{p18do({J zAXcQ!?e|aLKY;z-T#!UzowUM8$ zwj0*narSbi1_(&52=5m@c=`CHxhs#0j~jmf<)@wIuh-mg(1BT3ai+Jv<`?XLI#XAb zzTX>u{`ZG(!KK)fsPCD6o^r|!>@-%@`Vy`L1Vt7h!K{iPau^?yq!B={O(c*jg$f8` zPe1h-x$F%$VJM-}rlNAs%zu9&Ahz2)6?aE*X;9(o;BKy}6oye+!50h(S?x;dt^0EP z{M3m0I^}y#sRD6kP#wsWNXR{d63t=+(s?W7Ze&aew`IVUA_rLGXe6;>Dhw+P1d8=} zbXs+i{s)E30i-qUB!9_sgSzZUY=BED=bV`^>Us!WDjo z9qvQss_Qb*ls00)1B?Y5uoR=nft0{MsJRtwONYGaoW{hY9sXA~bxE<7=k<1>xHFSB=N<~It8FVad$vqqB+#_1W#yKJ92H&df^o!#u^3!q;3AnSe*NI0P_PFb4B?o0qv8`_{s zO0%ra2=s+&U1z->r%bhxoq)h3$Cikr8r`EwOGYg`m*$&vWq~BxM`N-c^sT>wZS?qN z~qOy?fTri}x7{$~Hv=;U56m`W#Cfg#k>jy1Wh)EK)z3 z;7P4cE2%k*I(sCZBBAfg>{>7=(20*f)91^6r(j6@7@vUC_v5;@5EFZi-q_`89eIj4R8vRaE{w((&()%sTy zTo=b(UxejF;lcXT`D$Qz<=K8jIXL1B7mW9tSc+YORuzFZ4h_xR(7)>RE1;sG`0@`Q z8d>D~;_LfN;Z&$Wppn44sRyRv6@2}Hrk@C~s}?suyY!7pGs@@5xg-JaDY(ErAghDV zkN^_HY~Vwmc$#{)#JNA18FLbD7#QC4HyZ3Q?=koE#qE}xd*tf+lWM2`syQ6!P|>b( z7FBaHybl3URsUAtK*RD>{k@D5UWjaZ5z%?^`qf82zs>e&syF}bBMWCdT%w!a>6u-= z>J7G=e%{G;NcyDr;Y->|&qvY2hCMrpYwtgVN8D!j>EAy!eEW_$W}eO2^xmQfW@8G_!<|^b8P)%gcYdF`r0JU zv<$M8y2JzRRvbszBvbyZ59~o+dzsX8GCEs-{Qirc{B91*R`T+<`+UEfSjF}YKl<#) zBn&G@;*v3SRJb`IX19!f3pYgNdPa*;=>(9Ktxlnspz9XwyKN8T8BliTf zVU9E{^)gnm@NLJ~sp!ptnN!c0ZLr6f-N8|BYfseV{XgG6aq2xS)9T!3j{e=D`D>Uc zcGK-QojDzLcfhRtm-mz(uWDrf0@d^y2ZeUL;x=q zWQ;r4NYt9jb~6XBe5S}pA~f5;dOqM&B(~9X0c;UO5!EJ{2lBclKyLlU_J1ZB?t;Y4 z+qu^t3onq=w4d^N4)EmYM|^xNQ~)w4AQ@)k#H$8m-}MNKl!`13SohUVRq9!tfED3! zDn1a4Es{YWKbmgh|+kt=M)K3N0dA?k&f(_bZZCwd7J>I zxCBD=?E0i4NwkL`+J8V9RG(BE`S&%`k@r9uJQr7V&*xsETme|IjilK|5~3T9TuV|z z;&zA5$-&7OI~@1-0bCO)*P_qjmHws2IIAWh>-FLLO|rK7AXD^+6Pt;LKFA_teT;+l&1MCEiUpr{d2xY zB&>&ec|aKXQp5o+qHWfnOq1_2GaEW5;{)|J?%@|17H%UC-0Il>&+N-Kb;;=-X6WXEZe7w|V+^qr%3dK z@c7uB_v&2xM{Cals2K)SM(!$;niL|CEh9l2(Ij|uMkn%B_&uBmjfmtmF3&bLBR>h# zGmZ)$D9zT-x@h^eLm6yI0#jex=%R!#$ohYLBs~RI>auP5@AY4X-)h@7-0_)fTbz3V z)Da(-8*b3Hzi}Q$tVm+tJc+^GA`-UDG(wZDaqgKyp(+Y#>1Z#XzidT91GvwKcTQp3}SH%;Rv5 zcl_}o{&|)j$O#)~p&jRnf}XNDt?~A#Hck6n@8EkfQ{ykUgd+fv1Sm*s$rrHnrq#c? zGp+Ki%M+Iy1CHSz?IaK&A?klJd~^6_2fyW4>pg7st7gp}v)8W7w~o!}^UYu@4Q^Ju zCVz8Cy)Yl^Ij@;Sh~1l?Ih?<5l1k{;oJKK@2<#;20>R7e{&hE^Mk-m;mY#Vn1*Z0GiRsd?22*BAqg!%iw9!<{qmNx}r6ke$ee zb$c@fDDEeeGy^yb+*ubGAtgd#HKC**P&`X0(pZ3RxfjkZCgcLj*@>JdiwREvjeh|Z zv+pX|ft=j*K4nfHB67-808V^ly}&uTG{H_x zsU{%s#EC0F_O4EN8-YiA8Hp$IjJ~5x0Qe)l1hBx)+bM9G=S1A{=^G99w?|(5CxqE2 z0N%)Dr1|n}KVW`&Y01lr9qz^GG?7P^-m?QKeBO+&KKAhP2D@U{=XM?Ud!`!tk7X}TDjnvs z%xd_5aoln>V?DlHr2tu9FFuY{_{uVKtgk>DonOpPAYpZRgyH-1?o1`HocPF^0$I!# zSPy;Snqn&kYmywJuDMdj*|YW6#xi-GF6BP?8AmuJUe|!(2^wht%`^ZDo&ck+%@llm z8wq$BNze|s%o0g32>9qy`BA6k@_fd}l8MXA3zu~w2+Rc^S>d|iXT}i2tl-%qxSO9) zES<;P<9_OzfGO~P41uM~55Dpd9!X%;_LGbt#NKASCcZ+Q1jkr{RgI4ghueCLFBkt6 zR`dq)gy0hUkCSd~*NxvKI%?<9I{aFhIa?jZHh?Y6Sg? z;Rbx_igg5?hj2;t#aYZGBCE1@j55fr-a|XvZPBlx9>Hc*`jN5QLl{%FSR$m2Aa2fJ z#1J8Pf_Mgyym?i7|0Cpt;B(`OW(43AJVjtEn-vovctQjR0xw4Z1>3fKI7mE!AEzS$ z@l5NQHUQp3KzzVQ@F+lMje!!;i5 z;3?~!O0iD@0l-?rwK~HchX^0u4ELteO#zqS;+UO_c{@@sf{UL7SIT~_XS&$bxzjSa z<6N`TX}DHt8vJN-2eA-bp>mab-%r{7T(9-Ph*0JW>Y&`@UE<)7Z4k4+C@9}b+4 z4}NzZa>5Q_HU|q{m-W1#Z4JkaBECCTLfB-W{E@;ZTQVLo-&UTb!r43aZrC6cJj$O`sod6@ZIP?j@ zs&4z#3Qu;Vl6eB-tguA2=Y;H*j}R2unMy{ZphhUL!ze_B7N-0u?4k?>JqiJhLYxRB z@d65b=6X9qRIqLmjWc44% zr)%*$Wk;@lVrx8v_pvbmx0MaM$qhl%+wr;hgCkehd7p|SSBcFXkArzf+uOEj8*4}N z!iIjcN3M!uYeM`Rko!Ikatks6#P9n@8P+#rSw8Y#1#2dJS-YI1Ob68R6pO8|76nATbfUxPR}1qn^-sH22m=2o1#tLHNsjzq1Xv4C!M%oH%^{~|3%iK6 zueRDmt2O}hC`AcRQbK8%QNg-xfRt0fY(xqKPBOVewrUGm>AYivf@q=07I;eh{0V1U znAROIkaj}_Gh8y+QyTE-5fZ>tKz^hRQnUkUWPnB71QcnEgUUh7WB=wa^mP8)8`f(s zXIDeCJ}BjRBluU3pto6={Eka}RbtniRfE4QcfOi9-eLC>H$6(ZeJBv%+c8gXzQ1N` zxXeDuu!{a}wRH znAtLz!Qas+G8zvy8VxbS-dg+zRCqvAI-$tF2B~y?wM`%yYa>r}-6C}lzes=hX^bbK zu~4I_eG9wr$xV zgr=YKFWzGLqO#I=S1?R^fC{A3Sg=hf`!$T;DYr)Sdf@KKnf1{uImb zCmsn9O3^3AOBaX?z{SkW)gJKOM@>OVV2Gwf9G^#|L_9x>@szjd-4lsOar_lWV{W@U zz(j}C+5vMzL1}v?At(A;GQZOUei!#3f@CVxe%{ZRcNkvfq#1y@Y^*D`Sed4ur|l0h zZ~@3%;&XaXbo;Le~FjF1?iWlY%2Khde6@V4((B9a_++?2QMQn;RPa(!(#>D8{YSc; z;AcN~dR3q6ozPXEo&NgWN`vwghYsj$U1;NkC@zK4*t9|pY5x?KFwc?aj=iI)?;e!J z{`o1uK#h)cnMk+V3&?!S!kv6WZw-!m*KUA&UQ+E}+hsIDIs$|LeD|V12`J<0`Ht zd*k8IjE+m;M{}J|MT*}C%*;}6Aa=?gV4`r%;u<(>sQvAkJn5AdhyBjl$w65Eg|>T6 zZtSlI&FnUwX@7I`qUZPGYoiMK?q0laaJ+b}4E*8i&t*cL#KXJVKR139MShzwqXVt> zUgGCWcb^^%{w{xQdmny!{QA=UKR>=*nuh(!f1V1#-v>jefJfXQ;M@)3kKb7E!6#5S zmmLWfZ4dlphJ&nxl2yZ~Oi~ND;_A@rH}rD9*SPlYJ!*?n3Gw}*&t#a9Y3aY&E5RX0 zdB)wYtXSWB$u{`=&`;)WQ9Js^QzDy@8p^4pgk!&+FnN4+`{4pKITF(U0|cuVmzijyFx@lK0k2W z)(Amv`s2qW4*&1eA(o4RZK{^e4!rP#?@MO#e^Ndx8vR2BbS8amS+(r?S69>?7fwdY zs#nTS1UWn|np+=WxAGaadxOEJ{Ha+L*}mWx#VyTeQIe1OITrrxX30ZONxlDcEML7* z%l(#uRXTq%aQB^m{@&-RbG{(ESW^__jNKJwPK5!NW5@FLKXjZs-lhN(UThHUU1*oU z7;_4M#{5E?pI4a9%<8C;HITP-Y?#h%@2FOslJ)aXFb^d?u6f2?Yciy6mhozXn(tI8 zE%-~A)bb|FNVx%$oiKCpSzx{Kp9)7`ZnAVhR{fr*r1QDLVaG|;Bu^7R^b;YMrZndY zeqx2_tsMy;%~Nc_P?Z?-n!vH`^B$V__;rbZE4RhxfH-@MC8HqOXtcc2iRVG$CDrf;=%6y zacgv6=(O!rZJ=GPz6(;akbe68{hq+h{}z!{!qdV2vg;2_FaF~Dk<34p_}dOk%+jAP z=9kzH7j8V;*`whtlkoXYz1+$nNy~8aZ;S7^-pbSVr~Uges?i@6Km44|?A|`$7CZ5}!;Y86$%3OWGRkR1}1yVgZF;-+Ck?1dTKNe%<7I zpsAwJ^f~*E#X7}pPX8X$EK|!&_{8=jcb~T<{}SshN_Hfhg%qnJzSp^@lEfqFGto>u znOc@4DH;}Ti5cKZvjfS%eWAA0w>(bt&tBpK5L+6sF)#xxQ-@lRMRl^2ie49r?tZoM z?^LsEDEiWuu@HcFpb_^LAx@n!0d8t;#52vPxz=4=L83iNRXZ4P;tg4JTVh+Wj5c4qUv-cjsh8qV$W1NwgF+NagvLgg0B)J0RLv)$!Sj zE8kz-hJ+~ttz5l9`Pn{kPwy-xDm@iQzyS27a4@$+BcZGom;s*1t~x~) z*e^MFxT3UvZ6XOabkr1PpX^CzV1t>(A|BoU{8>!vo<QA@tT?#JL!aFbd@J;A99-eQ&<0v&O@DqwFm&X47U!I70<==e-2G#nh0%X%Rh+ zmr*TOrTv(Z?+ERApbMeqSymX(iEl6-;D&kHSMwa={H1+Vn|*)t!(u@axVD~ioelL9}{o(fBAW71%dj91z)S#_LUO@W2roxqO*5qps`jFK#aU7)(5N(uD(HlDB2&1X1l{vcSo7H~^8 zR{qFM3Z2neW8O?>HkyZ|rWr4AyUwIRMs2LR=xm*walUcs-DytYrx2FGN#;418tV~G zc9X3sACDMn67Kc#7g0DG$U&gN^@&1D1U@=Y2t6P|2Qr-ygs4Cv5h8a7kW-&1LMI9s z;)GAI0&c_)nS^l&3dmG}67ngkUj$4EmdFEE>lib{bcrzfPbX1+!wb$tXN!B6x^O|*y1oO$3-;PSZ2^z^`~8s^Ng%ehEcR%cs;veY9`fWnl}?-Ci2Gi?{L`P z*SHh@n`4qnLcYqe&ir1h(wiQ4-_W?0J}_>TGy{N#4~Wx=LT*H%(E*-UM7WQXSU662 zl*mmV5YY#Ut@{d7i9&w{#3L;unI-jKR2Kea@5Y__l}6EFv2(m%=P=wK-Io0vp}DsV zGF8*~^{ufM-!EVNdl`vyR$;G_xl%$Qu9>2%g3eTN)nqEm@)`e1w{y?qnwyh8c0bwX zS~+uBe43^aE1)-y6Yd}iRaFWZ68VaKtr{vts6>s<0TC)rz)h;C0LuZW(C5E0$tAh9 z_|eZ|&AjIw=NRFf7=^=d*3d-Sw9{5U2*AIWhj_$w;rpDNNv25;!EWR2IUQ+zFG>Em zJe-yP`DY0iC)4c&KiZDH{cG;(ItX!6@pm+&s6Cs7(+DiXvt0V`x!j1wZbU>1p=1}p zwhrJ}xAbSo>CrWu{_nY}=Ea3%uF8rzD4bUNtx7Eu-*`r+QLU?Gj$y?&>0(QHRN_hd~@~U@Ri?~0=&bFlDo_`O<(U9G)`oB^U;P(aQR7$ z06Q9poGkvYTLkt>9G*afht&Q`rNIfCt_T)Z_7sSEL|l9SSQME z_fy>EdGX5u!Co>7J)8gF&U`Xc*ZJgFP}28FF4qdHuY>s9H>!cN6rziNkXMzWI~SoR zoxtrcN0hdDW%3nUdnwN!W05XCo`}dHAn1U$H#8OzVog;Y0zgl45oX6+PH8|VixHCt z(QN*kTuK4a9NSMPAuji2rR94HylQHM4D54k zHqvA$xi9h-d7bjsD$dlbWvd}>KceNxLNM_ag?=t)Vr^_S>JtoE6FZ$*W<@xr(8(-7 zwk86i4%=EtE6c*BvJ9m1@09J-CH0^yrwNDxf)BTRO;%y1*tVoheb!y9zdPi>$(Jo> z2Qo03_~tb(PLfowVBSS{>e}IYk^WF&21=whKI2F%RA@9UXmpUMP|m2IQ~=B>_%8=@ z6)AAsprjTCvkZ=0INT{eqBf8g6eZLB#~^C5A4?ngf- zkNZkZ=e5_O+`fF0wJWC?wF>o6!Y~X`URx-(n^^=4zx0J~_<20GNidzMzqr`dt0&L%eA9iAzB(c)UIjRTWN69n@I_AakIYL zyjE<0@UvTSVor&DheV_$@VW6v&3B6+^M5@aFJmzOsW^kMxuWJBcs9)q8)1emEeqA8?c%?2H~8j2*rxJy>lSkUdyXQvKqa zdbm*v$r#zOX#KC<86z+GGq3e$sp_wq)?Y2EzdKuhKU4klqV>=Cf$zi&@YxNiBc&mmD7lKzcPH-zqYP-!R!30+3pr_sa&(L0o?QEvQz6(buOMN3 zZofb&SSH{M@_N8&#lKNm70Yc-*NxQN?IMy(d}_VZ&w#LJJKR6~OzLIP<9@u3GDd(( zKCK(9uUiGn&Ir{$5^DV-^f`m=HC}r=)bYkPKEj{kw~c(_;`HVAY$N)rmSI9 zJft}fa?C6PxQ3{sx2Xx9uqt99qG*1egNh1R}KwbtR52onOuB#$lH z*h{D~ZUw@>diu3^Ck*?w-O1q_-+dcp*LjS9)0xv6*DPbDR&L?FTW7`P42137j@#W$ zuFd^a%H(sHu<7WvyfN9XrZdzcl3sQ0TMipK$Kkq6acq|9^cA@;-*PS0%#Jbosg@UA zw$5zc@^ZmmZ@*$DcPqs2io{Sj@5EP?yMdPZ+3wYo{O7};?sJ*E-}M^#&tWLn>&%hk zOWwZD8>ZL3D;|XMlI!CB`Nh?&xpE0~q-Ey)daJVOCpJ$psaccXN9L{^{t4Vhx65Cs z?XV<2Qv2X`QGEWQpIuPyZ9euRmpflSehnIv(U`c)%Ql7elZzOO%JDmQOqFEvJFl5t zn15Nd(=UIi#^v;Oe8D#zaZN|M)Fm_*|_y_v^F41D|c)UXVSOpi;AAm7XjVr{%( zA|Y$Zr}BLCaQF4K*FGV4+&rf`ofeKmt*_m9`})Rn6x!4m>^e+#eykyW+xMPuFVo6x z;SEn2)3V5%JDds}ye}r^Xm{@YD9^y&;cU437R$~idpG+|0H?yeTkrKJ5zezpiLBg>UfD6ZPJ=Bn|(wQd6=9|*sGaGx-26nQH$xBAH?p~Wxl0i|a}NVpc!Mtr>9%v*q*jb-00z&)Fv@F@U)4vw4G+bk7ZlL2Uc3urF88JwMpXo`TCiuDUYPsJX;blvRdb%O{wESsqYi(ptZISJh z$#$Li$kR@O0S(6c2amV`ar9Hi8`6bWazj-F`2Zq_5{a2lFO60}^ z8vkswXxG%a)^ZX6Oc1!#E&x77Q2b7M`D0M|k8bqdC7Bb_*mDpU9xKS8bW{O_oq$Yj zL{S8erw+i-SRzITRTc(ShIL^ypa`5O)<)#r#R<0IB%JhM2qH5c%fpCHspBLf2l#mh z`Dp_)FaI!X8H!CLaf)lfEvx`r0-p$R;%2W8ORuOpE=#zfYiLO?>t6mf9$3hLSX;k% z%Wo5{(%z@tvCq5rJYi*@bxuqZOI3T45YM{Qf0fFOts6bgaBKILJ#gc@;6F8QP zR1XIaUW53hqv)wLg%cx{1tXP6AWWSk7J`#eAH3t@EwKO;RySfyUJ8toCJ#X90xXX@ zNi>BZuuEjI>lX!(M18Q_KBsqX4Dh331gmgTZA39%5Q8ZrNFkw{Kv+uqP`PoUkhIS$ zu4xlyAr3{K3~U_ zlsXg+L}3>RS>e^q3AC>?%anBv&H*wZxFSt+b#sHA8(kj-YzVHN8YS>3*+pgmZU;j+ z<)-4hur9$6R&QJCi5>B}Qww^kV#@ALBjs_B@}cN&%~rPoE_Hh9!&MBFGwD2}W}I)I zne)Nl3WM@vjBnd5&Z~sRrwTKVl{7D?eAZEl8%q4PU$T30EqK7==BK&lTt#Lk6F!0) zj^Vd5wbicsNghj**1wNj%{=>ZSX>>0bQi?=nbdfEcz#*JEhG9TNgAs?{^VaS4@boJ zwl|kytnq5jEZg#xS8d14+uVpU+&ndastj3rV+A+lXzDUVf@Q!DBKqr_AE(~d0yvEc z_O624-lg83O5*SUa`Ir>Dv}wP;5rI3E3p#7t^KtQ!tv`AX;PhJV>C(;xJkIICv@dq zUCqO=clEVpB#-eix$+clx!N1=C&m?iIfWUD#RadyJ& zGM4z}(OUHP3(pQk9>03>u7?h>J&%}C;e4?>ZP$w!34f#TsA<-vSLKjhdo@832+@}g zs$@6)M+Nf4BQu9s)Qtf^umuo8DQG@^6I5B#%ci;^*3R z6`7V8mCJo(?MM)(`X<2_l3-Uc3gMujFLw!XO0GiDGs~0GIkAxWYQf7pxi!?R^)WeW z6`Mz3`NO+AbCuU_f2O#2UHMO`#;bsVhtroi>!9xLQv@}9>X#eDyOOfwRP(vxKig)D zh|`nE90c5Lc?swx&c{5lH0}9@?F!PV%c#WFDrWRycubK zwpL=L6uMSN&l2PLOqo`hO#|r!rrg+Ki?}zTY=bElA`-*!#J<$OBLZ2Y30*N33pSN| zE_8rzs=38^y(OmyO~`}SEHF%mtsy;edDBR>?#t4G-X?sr6Q!GD!hLvk5bb7 zSz=!n#^|d?N!~W(mMe)42;2I(ZQBs&A@lma^9Znn1zLCcP@zTI=4L}84F)K9I?&%7 zO8O5lQ^DT}w_*t;y%%1dK31=}(x7rmez&@0gjJoj#>1CtmH9Va5zWSc+N1qu*Xn{b3{A1%xDZVj}Ou zf)V+=myupda9A*=QsDYkQ%oQiTeRCSpNMqIaQRx)6gpF=>WjYgS<%<@r_$8f^z@j% zG7jKkp2Y~3Nh;@6iSdGrAo?)GPVe+BL;|kUFOy{j`d&1L9APA8xk>3&^n$7v*?dVX z6qWEQy8nWL1xI{VIQNpw#V6VWsbN+ueb_bs!@4eLR{q5?=Z*L<9VPyo1s+N@{F#L& z-%_zMW8s@oaJAAeO^=vnI|4Lb^JHK1;DHU5Z)#qf$`p*@Ddz2#%KCcpSoSEh#JJKy z6&OWRd4y#;M;BhGs4%Kio+cu4He3vu(X0+?1k%|nO6=wrH02LTwGcF9d6+*giX`o%K)MN8y^zRMbCIzSmb_eel@%|Gw-LD&O9|&~GxLHTb>Iic)gsp}Daf z|C!qLMM2%%s>uirjllcH_5V&89zO<4xymyM$Rg*ak?moZ9ij__9{(8VA%vGQh1AvA z(be73qSmZ+V5zE z)|6{}{RoBa5FUIH6La?G;@81Y{*SOTa z>Hd5z^zl=*COe9t``(TG-VNZ|o!-WUv16$rXz`HZ35DLnW53fKK~(RER_3 zTlgKF0{i%B6(JuKs{`~@;m6%EwP^cy1?pGmzbC(e9}Bj5etchdYHB*5`RagaK))@b?>tCEaxm zX@pDP&J;StH0d7_IRkx^svlvq4BF&-%^9}#IYNU=_BtnK6Jy~VmJ@obRzbCUEIX<_ ztGbU*^*#-3w!#8^QA||3S~wAO<7hvBs6>P?d{+B*0bzRyc4up5*l^z zdp1Zxy{EZ%EB99h_?{b7zK90Y3@K*`F$W9N0T1HdK4544@R}dPej_#XPvP3Xj;Q}q z7jJ==R&M;nRy9EQSp89$|50?7T~W4e6rN&&9J*m>M8Kh2hfXOGkdhXV?siBC>5yjV zR7x6rhLDmJK?H;$1qn$JL3w%Cy8pnv*7a$hdmqQ0DlCyOhW_<{#grT4GLhhsM>pvt zNJg*I+*9{wn+9N8Xo_k^2z6REV6j^f)Jf#>{*?I4;=9XRz+~Nhy7n`R9l3kKUsmg1 zS7yjNgB}R>9i+w!oRO28;*}WLR-#2+`)){_H6jOk8;K+rY=&4au2v^Dj!$Bjhq?Gd zoQ1KGJBERxjQXLU8J;dS;I_dMQc3H5wmHp41aQU|?Popxphgu_k3(7oVoCYz!9+H^ zv)cHE)P`eAD342)RO3@Ld-e;vg;P#8ogH32UF|0Hd%#Eo(aq#_An9LW?Z5Y@>lYjwly;&EhB^q_ zZL*s6=(soK#4}9ap*(PdGnc8)#I=G6^`X-xUA%BRjoRPZpuCTrV zg9i2+Nt!GgNhi>`)~U<(p4CpNm&m9W+jpXLo)>ZRcbaY0Rw7;WH9+ewB&)R)RUcoQ z?WScCXILRhf1uMFiE-s)t%3c(QMK|&ZO_2en9ZRwa<6Fk_yoT366;L5J7WI+IgMr$ z&BJ3F0}BWMS_k!!jDRJVhwm*YHs?W2%x&B3!IUt=`CpjBhd)CIZ+v}13lR&_`%VFl@3jn zM&&{i>un&sCc?Jm#kSkTc5uvgEW-W*lHk}x_w0zy>lpOCrbrE5hHqjiNps%T%0v;j z{+Xuq(XR`QXJmIKe=QINs;$#UP*&J74G0Jjw3ts_sCIua78*PsOw-=kg~7)sK~#_j zGap8#yREHZfdpPJQ9eI!zMy8luoJ!rQT`Zj{`hA8L~mICNY(Pop2;cpr5ZAddoYu9 z4?58rzzzhfP4S_Y^08~Xph0FVI;uXcUCl6y&P$M(vL0dxJFs=rNT)1G<@9Dj6k;+iF|W$)u{Z8N`%2XY(WHGEQ$lK*U}lFH!2Sr*u3yljFT1v{7BqPD5x;oX zOBbuKMA9d@j|ZrZC3r;de8k846yeMJ)#4t!)Kwta|JM~=e*dS;(}(CZH{PkT+vrZN z^AzfiRk<+`S8bY=TY{wwCD9Gc7QTvAZsHY$B;5rGjdP>G?_W`u*pBB2jtihBYbS+p z<5LCu8jq5v+-atN2#)h()!WC_*IrFTz7rR~eB2Zq|4phnA*c?f*5Tu-k+)83PF9*%=aMK#Hw;Brh8Ab`svtUNYDd}f3(VEzY=kG)VP*hHeFrCk^Gk1y~ zShCs{k=@U)j^2sxzJt278okNTOBz&tK(7CQ8a3k!GJgKj^p%>)IQRi=P2d3d7xGDo z81KykBO}enCLE{GavX)z8r`4URrqa#ki)`+gQga*;^U|5fhU(Oq0A(xOl0W>pkVHJ zn2wRvw{vI_jyTm@B!o)j#R>X`);dU3B#2f%!Y>?>wHYgTOyf7wSZnihP(uGe_ub_D z?a9XN;e;TaotIDyN-}2BXioaq4%@sbyT7g8{B5`Ai=iZXV9Wq0W>nQ;_GV7R19w{{ zqj$HVNykBptSyEg(^TRu=&7Lsx7x`*di@BP3zqDcHSxAPi3^T&fdEe?K2_!31brfJAY}^D1K>DwJqoj-0fHTbqx}};nb5> z1`z4vJnkVqWXi-+5yx|SmCU^^^+7-LSD|i!_DhOZAtwY3DnXf zz#}~!Hq`;{7#;pC1x43~W6=t7F^0mV;5%C+B_AXIQ}LQg(Ej)7^-W|`w7O2#`n`un zy8x2gMs)@s%R=WT*VwzMkpa|E2qJ}Nul*||SQD$wJgi|y1jObS6&nJ48%NTG*A4qc zaYTUEj+)nLhCj!`>QOLXde|mCtg-`2P7U*JqYqxA>&Fl!}PV`h9d(5y(wimnd{efg~pkuO0-w@WH~9@d#w+i;WBTK8`Z>mn-bI+}bZy_1w&2F;1Z- z;nj4G6j@N^%v+|knsY6<)Cfb176%VJ&%BwrERE_;ys%FvJ?v%Kf~{akr!YXoJ+rH( zhLK=zOjAb-{fD9ARwUa-74ekFECNjPHV`)}T_ic+w*!n6M;KM^H3V7_0nInXs*-W{ z$yE_(%z*Sm6_lRm(41eE@scK9D8zdJ$ve|Tuk)7OOc>^maRBb!a}2z^ zhw8KecbKUcF;pwEvIWXi+o;0XCuAqg6dk#yrQm|szfBoRpm85$);nc-d`Nt4`h7T% z0yfP;SGZ$et3o=L101pN;QKwlz?)SQs{jBj^^+^mAC^g=C;}9EP^EJPQ7iyn-TjL8 zKq3C%C}J7J+=46kf;z&wu=yt)yOI%dUY%lR1_k-dGNV0T%?+xVkzUB|2gKCW-gzxO zk2g~}#g2ut3Z!SpM%|up|0kQ?c<`36)>BIa zcqWc)o{@SQg9s!P_z>~~uxW4b&zA7SOkat&<@=uk>3#d?Qt{;LQUek!B28^P0yd${ zC2axpqT1c>!sx@p{K@|mG&@*Wg<%j-hDtZl@C}IyiLX<7MkD6pnMq3{VkDz|`T~eg z+H5s-`DqNm9TN6WH7R!~ZY(cb#a(KD>PHHu6V)<({K(=~9-@=LTL0~;`V#%Mkk>$M zHfO8bzv|qd`jKw$LgEAFci%3c`!tCgmI1BOfrWuE_j6-8xe^!$Edvwc7iLz7xunJz z!L+7CtXO5qBxe`8DsmbgB<1Q+F#C9U{#eS@uU9W(@k>gmnq6Cq{wtlM>94dr@3vF_ z%e&+(b`9J8VqCBxT zGY1pBiH?k7CM5S?(6f(A~)oq(zPM|ftp49v+%Gc+3_P8!yvYkU0GLPT4Auy z#@0*k-_6eRZmb*#F^1o4{5y=#7rXhqk;2)T#f&hhf4JO$xkyoP`yELpcpSvgf%)Ap zY8t7qAPDD)j(HSqSRtrG6s?SJ`!#+OY4zXXbW8&>k|O4q*);O*^aTU^F>TE~<`@2j z_v7?>9qFlIZFr;+2S!+7;J|M%h~7kjdA-wzkG6$x!hb&Lfk=SihX+9;LA@h?+3oHj7-3J$F}9LHchI|qx|HC(iOB#m3! zss|IP7?mY>n?>FzmPU`?&Rc(vM2J;Q7RrbGxy~(%&62LY0G+ZPE&}t%P)E&xA&S1MBbm~Ul;fa4^`%4 z^MZhio{PEsMPY|Q)kKW^U5kZHP0-z^afW79Fm_V{e{N~f?S4s%oR2!{vGKV@-P`xX z&+D5fwVuCeTX9*=VwNNnzFdz$xHj4*|0yoyXvFR#lx=wu=bzuzJV=XO(JiuYwP)|s^8?J@ z_R_)oi*va{*om!?N7q*+wWC%jwK*Z}zrrk3El+iqi;>(i3Iru}#VU?+X7nhI@!K_e ze-!+0-+TPl+w3zTzWPdZlvg}ABU52FkEek(^1`N3?S+ishyAbdN~FT_f5o4&#{kY} zMQ|uL2wNlJ-JGC3hJMykS-}fpWD?ez2X|BOCl!g%2+z`7N4+P5E71`+?M8gq&{qr&Cs^TF+|lWokbe)j(x3*~`!y z777OGBLE3gV-W_#@}USw0+Y4|K)QZThWvbgLmqn6PeB1Bf?flFd*Vl-Q51U8A8v;~ zWD{F6^DlGWZFl7g{hrNI-voX$)@G$7tap1xF4D(L_mZqWrTKSxwKy92TcjYBNoBwI zrg8rJFy?*7T#CEN$5q8&>g^YoKSP1##eem&3=rdo5y#X|!9r-vJH=^EA8lO1I}`Rq zo>3nX0dn0;@2tApaq8?>Aak;3wCF;)uiirBu8I^b`%w(Q*Aq-kg@h2Na=^uYM^K|U zqEN_wFa@9_j0zc}hwKNEqkt5;0ntM8eK6_qh_iQ_b;6b&uiQq9sJ}-kR^K>G6LC~) zKx8%AsWC&~gAk_BvG$>!R!R0**kXQ@O*Ka)Y%t884KhEFT*rPAtHx3M_=r_98i zww|%Ec>TL~Ws}k+JrlsQA+4?xDa1)XVhsi6bczYb`Y_1Neb-r#mtL?T_U8vPx9Mw} zOO}#4@T<`NYK#%xk09$0johv7k0Qfh0KU?yyvv>mnjI13x%{eP4-{cWgShxwixhAH z4#-!4OE8fyp$J7qbIAi?#{CJgk@I&;|0cXTvq)s@mZ5rO^bqPXgw=|?!-2(l0cN7nGKkK}0j$tCTxL=m`` z{D4TBVowN%s3J^BI+EOeS^!AOKikcHt0BS-NWWPS^IIXnwc?88n#6mBXx|k1s(w@$ zNXR9vK8A@_Kiw1%HPDfKm+Z&vtO5)H^+B6GDS9;yt`{^Df;}3;xyyMe#%2Zu4BZ^f zs~t3%M#ujgLjg%;muoLK%AHPh#e#)CHpVQuD0ZuG3C9V%1#dra~iQzHug+mCk$9H4V3Z^JLq1F zFc8o`&86cw_X9rXAhyYH$tDWrF&8-gvuK>2s;SW3H(Ys2Q<-`gd&j-VD@6_jmnQ)0 zsWB%IJO~3=zr~@Ig6-uuP$WSkD#FcaDX%uUr*Z&@KrGa=F>0FeqUX4NCHTvd)kXhL zXVKpga~gX?h6z)CFC)14E`DLoEhQtO<>PXT=QS0#$x`8zV)-AJ-viupvA(=py#H;h z5nCj?d%2gdDYb4Rl22|4B|Z!H3BAHTWG~ECEN*I4SeB9|e)%e#BK+=Kva;c=iSFv( zq7iT)rp#ani@suw=9?d;4Vd|NMXhh1C+(T<$$sfsX?^?bTTiWiZm>_UsAY@AuAN_Q z@q#R=+Wbgl^Jvm^lq~9-`-UqS#(WX%ZwCMT?4Z_$S!A&Y)qXmp=GidSZ)tAP)_$;0 z!Y}dQih%UI8Hlsou;AV z!@Xp$_}BEx9^W2kCA!^GsLVg+Wk$5$hc{8+07Mu4q(n6RCme6?f*9d_V+vUBXj1~1 z^b(KfIyuQA*|B8o?Cknjl}7#hTC6_eGUWP(H7##d{`=NO=ck(RG+YbIeUzwP47!jsjE+}sG#u6@f*sNB8srB zy{gL@kDgOa+p8rz-ivO&P-YdU^?b2D zarRHU=7sL-{s-3xo)>>iUfc`|9`fBgdv^2U@Aq)X0_XYgi@auR{C7Tz4yK=LjEm1u z9s!<`s?=z7z#1%)qnf(3jWxI`c>8<6j#S7)6QLKM9aFu4%bZA~*U0r!LH%Lj4N?Cb zFJYnF~`tr8K90JrzYy6wbzz+Zatzx08#@}jBP+5@&;MrxEr7OOLQ7)vhB zOa6AGeDy(XNy>>%%`pz>9GxwJRhDsRkzk7UuD6K`mb@2#>hR!M90&GZ{EnnD|1-Mz zXFzD&_~Xj>gbpW$z?)cRh5+$3m-5)e&g#VOk;LAe#Q)&0`uJZBXuSGh z`bwaYJ1{UZBvAbAOFeKFrU|ae6wMI3ZG)U~fr_~LDxxNp zWHgm*HolaW9pFbkuZpJ zDl;{;L42yAbCyw1)}xv%gWQ`~%iXM}h-{lX*>;-Q4!ch!-}7CD%feNg07&Ecng z?J?>g(i*9^d;5nVUJMwm4?<(WC7NJfEZ95(%yX1mE0b4R1FGo+p$WNfWb&#m^O)_j zJ8Sa0NAr7YM4X%*ToAl0L2B-~W?@+wUYe%9U(@|}*#hs}55D7+v-vuD>nSBFN2pc* z!7f{L#p{V0wG@?1u_u(okDsK}Xrx^}5TeZ#B=8qznOSmybDU%Ia%s75JNsC$76ycl zE|$K%K>@&5HK1Li8<08*B7lPc@`|sVL1=N177D`Khy&IZ=WgdS)|N2m<>L|F{W6?` zyDGCICgG|X8=6v0PDNSenO8aKe(#d_)(lSJ#lNa;ZhQ(~1Z6g)y5EnVe>)Vljg}ZZwDo4Kk1gL7!A`0YHXY6(R-|`YsjR0Fe2Q3hOS=^=Qen zgi7bdEVi)#RDy!**X%3mr;@vBgk5XC-O`1eIDN1ogIZClfeo?!vzK2Bql~I{10O_s zX;MQeJ!0=4nYzDN%#f`0`E8oCy`y%sDXz%`4hE;TNT>EIg2Yh}gFjgo&b5|^3NeEU zmmjqa#5Z-WwJ+VZ*f?cNiD^A`spA>~AIJboBa^k_p zSfIEy5i=Ns!UOBG8&DXqWn+US4)i-uK)@5iKwJk)Yu*R}=2fy%Y`0h+$@qVO#RvjBO@v z5jGuZPBidgySno;y=j9gWXKz2`Kx(XAho*+#bQlVAqz4_!PKXTUSOfdSXdwysE)mX zxuIct) zy|p<#f4pQr0xp+_f|bI=MZ+fRxTfxg&9!sRYX{C&v3n167S_EjXR3gHCAvj<@?v2x zaJN+}nB_F|M5NXS180A#*lgxl#}) zm1G=`I@2R#Ni9)Nd}0Frb>H4{^S+|iNR&JA8(!(8iYpSfM#I!P>3AzJuwp= zOgg}1hKI_t!RuuxJpn|wdlw{-8bw8(Isi)rqz44$tl8Crf%7I@SxG6v5;TeTiZeqc>(taq9#o#W6|YUXaJR(uB`}r?%6y!bsfq3_v5tl_ zGNP^RJA@}iXVj(RS6XIHC_f#}W)3fx*NEbHuwYs1ylVfvKQ*(hyR&b9&DPX_aR^*P zu8TAwmmoN)$C|xqF=^vH`6dg1@w`DC)FblnlaZ;wE*~VU5t27OSztY11b{N$5^!m_ zgBT?#&`=_wFGOJw1abs1uP{*S6Y@hc>$j=XY!!i8Gsh-QNk-zp4*eqM%e}bd)YJRP zS?Wby)f;h)`*FFn#_iXu*?T>~g#rUP)x-V-oV0w98g8%=B+>nu?y zURd(DMqVa&W=HM!R>9eVwX94{x(o8F#>!6hx6~xVPdndeD?ThnjiaY4Xf;>Q-BvG> zRxjVILOVgcNE61Y@oA>s>%74s{5vT@}}n7P3@vYG)mMuGaD#O_@U@|Cb))hpN~aOX@&xHP;12R z5teV{7!C_5fIt%D7uLfyh#QeVYk7e2eUdFu3Qdijac4>-noiHgju5;4FRBlS)%^;L#J`R*(zhP2wsUJXsN2f@7uslq?+ z{r9r)eqAx!4?7SLI{m|JV(zyq_<^zfzi^`WmOu7g;UY(;m#2<5fk<*JFch~9DTG-P zwwL~H2$|iSX9(>m{Bw|beHQU;LChJ&wv{wJxv)QSf?Sod_?bdetj}sxdb)7(di$WK z|1PQZML+|U|I>@$sf)m$7k+vdRHUc(?wv-hE}p&pO6y5vf3Q?!4aX~NQ{fo-0MLv5 zrO2b<2T$(aZI{OXjb;3|Ea4s%e`8#qU@D1WxY`l()bVkX zQTGX{eUrQJ=cVb_8g{p08xN~TlK<68^Cqs`d3(kGSNs7QoE{d^PyuN83Q#`+G2kE! zm_{-zSP2hcE@-_7hMWf9uKyv@$bSN5YupLXzZW{kZ5r3xF;69ri7pr2*k|HfMehcB8F%@ek_O6El|)Ct+?3_ zr&B7xP%0@$r`{8@g3lZ)#}>$PIqd{-4#wa(m8lLG`jre{QSy79nF0lLNZ9j&N2L|yPLR(DU4^$uy@O1ps7|w6ucG-jngFD4B}2W z066x;D;}|xY1GN)ScZ_8Va|3-bB)SY7z!+*q0W+fxGzSrkMwI_)xPh}qsx(Ya+Os4I48z-KP3Yb*Jo7r< zn8|;gC!#Rd5kgsnVP?4zi?4sco04g+LD!@ZQ zl>J^LH!}R)+gBLFm0{$q$(!#hDpYzMl0FKhZ6O77=t7PMfHW9DgkayhTSec95$>Z{ z41*VdMF;h$dBrKyM_1N7Gf8qux87&!lWV5;7~~EXC9|6|yQ{8}5B4=sWA;0n>9v4UMyGMAN*&E?xX(a>^-6H~g z>i5~X$y3Y0M{3p)=PjRzlH_pU6ReRy1en|t7s+|4Y{>tcH0tM+TkAn~uWMiLuP)CI z=l&MnokHyxUz=J$3_kVo$)~$>ER+z5BdiU8=SW{UiEkUgKmoDNDN7RmjM<2nT!eR! zaCD{qrg(DC9usXVHoq`QZ}-_}LnbBbbst0&U>nat>M~jf5o`o*v6cI2@*{+g@L-am z(%gF#!qZRiIqs7vD_5aud{oJ*7?X4Fwz*kU<({k=xNEx(1*WKGlj6>ArPCA0_xksw zCeP#7z|X(Dc@Ho}>V(^-VIX~A!BiBr|M$YW zbqNgc61g(TA9Y+v!SSP7SflvjJaOt<(7KmL(FOG)RqEyL3{`66#_nNISy?`og*$gf z917;zp&_rwf|*ddM8^94AFMH8S_1BI!)>FQUn7c=e+@x8Jx56em!)xe>%*qK_DaPV z-^XGN&u@hA7^)+*<4V*Mw6J922FlFq`bdsIz)fVMcO*mL6p@H@-_Q4vk=(&4@qc{i zsj?WrbeNw!9Qx~2mIPq63O3R5+LmE>;R8kV4A|aR5gy$Rs1H?)`X>;{sNGuV}wBd(Y5>%Ev?Ez5KbK4t<MHu4C_&--pu~Tgp2UjV2b{1pspRF;|Dxs+CAIwlwNciFbApkk8X)Y^Cu`^o zQtZQ+bk;~@Z9H}UH&QL4u9p+)T7+s>o$pF3nnRTd21S9cd^(fxsp`#^#EfQMNlQPNq&rJ?+?(8kmOF=E{%Zfxt&pi@nZpZhi8VHE>pO;nL6us(aC5o=b=quWRVS9UqB}%Q0SKdmzy^Iy%!+-GiG$&C_s}=@A?3-AKxOb#Q1Nfdz*nJMZ+TJv? zF7&2Dcvt=yQTUYm_E^W?={i~p_S`lsR#Yw&FBlzUA!FNF@W`kQb^y<}iz<+=L zdSGD|1n{l;ohwz&4ERb;hm*@-|$0JFGICygOg%HvN$bY1^WUUx((!}nhNH6!+ z219SPXA;RbcdFdkzvqs;a>8$ELoT|1N{^zgtP3?=1&mehs}ufO@B_H*E_K=5!nJUl z=h|LW8w#py7d{$~s)FaEpI_rk*8SWz?}4i`Pa!@mkqJ7Q7s?UG zfr>WcoRMCVX^IyUw9&w%6RVp}%N@;({~W!-UX}+%{>RL-E218HQedcX_P3BIMNfP4 zCOq@UarrU7hKo^i^QHC+2i|y!UbW!c=|9Pi&<6N=p-VKdQ!t!ANh*Q*O?)KvVE)PZ zcG%CbRsPugzxz*ye3ekCSVnGk&`h7SatC$f=A)|1!x}7+U}K<<*{Z=Ia4}A=?DOZZN#tMo+0c^>;*W8eWaAXbGt318E0tg$ zIi!zY1ViU~TirrHbTHCSFgny+IdU{HN0fwsc=TEHcP^|y0 zLZ<&;DmJn9H&apEhla0lMF3TEmV{jX4-KOUl^yXvj#bM>NGw%U9~G!BQdWJ(qtx}I)f=PLc!at` ze6O#F+q(??sV!l=cpa$mj%nodNo~sDdl%PHI>YLwky`Sl4(XCr>y_ofsxEsnHAtC^N4Gf3y5||CC;jm10^zK??Of@6w>D^> zjY5@vVacwswG*wN%rW6ot(KFqI$zC>(9vdXmE3Y>M$nkwpr*DyLTM!98aWJzqtMhX}1NPnB?Ei_C-r(%2d=;q|yzMtF4DK8nVo_u1$XBGWcSMDFzGqdjm_CwQ7OZ3m= z^-&I!95Ia~2QmVG1x{IN)lYQjMw+lXGqggpAlI3@Av0QcupG!Srau$v05Et2k(3yf zA3fV&5V7fNcof;V6^am;({ldPGBz&k95pGsH0D=V+VqKc;YP5m8l@lbD1n_vK=okS z)m)65ny~AOasf~Im;9V;Y9(s3|kzu z^vAimCYHxl-NYK~WvK6d(x_;oiP2GH{p{ea1+(O|@~iSN*CMZ|RS>PU@hgSvk01N0 z2WGU(r$01c{i4n>cP;u@L-D;BUR#wRnxp!SdldHWf_6!0^j$b6{}E^Qb8)E*jW~Jk zGBI2@kLFyrzzf>cJsECZ*N5`W>J1|r@>&|rIi35RBdT4TbePd(h-u6#=CB_<<(Zsx zzXAMBTy$|#j0+~-mQPMaO|F%`c>CSbAR{H>qQv;)6mDo!A=o(SIIsu1<<_n;>k^)m>8cXpc1G8~eZ+aVY&Fq&GDHzP^X+KoGK~#jP zR=)Z0V=b{{EA5$4qRpUu)h>eip+a`=Qg-1-uIR5hJlIAa#Gnp|SV1ymXYkq@4!Vt4+Vmm4o_KzswjG zMXwLpI}cYtnKVCotuRllCS6uk*ZM&kSaOtd{|zA-#19Z#HroBGoD-LQq5zP;&gLyw zKL3lq6zco)jZSLtsY3MA=bcag7AnhU4)14pNaGF?;#ynqIu9}s5K!X5gE65-GQZmN09zwB1RF@Koe|fYzh?fEp^r|P1m0K z+Eh{6P;FBbVQi{*RV#6eZ`Md?lGm=Ws!Y0xwC=<#yw-eHNHEm0nSWzEwJ?Bhp;b zLC`9)ASqtDW6RhWq7~TKC^|tQ2k^4`?kD-or}R_i6>q(ZqoY-vR%_c3sj;rZc8sf$ z($%AIXw_z|ZrG>OUkM4geLC%&_Phx;QgqmXozK2jGjg>va{bfj{O>bqk~7X&?|Fq& zh1#G>L7%E1F0L%v?{G&NRT>j0lr`ialeD9D!k(1j670VvooeaGvqPy|chEKMB=lTT ztKIgoAtz50x5RwRg5jI`v5!4QI#~`CGO$l%;QB%n9E(^~&bwnj(cMlWJQW>&ke`@K*XQ?z&We$2*NV zXZ_t~tT{G?8)*B0 z?!=8t1j~`*V$ufDwZLY9qUu-n#jg_B4`dh<+_eaUxe>dX=RM$6hhLJ8HoEV^p&oPG z?RMUq@SmSDDn0$ZH}}2AoMpfMXJz~+-1bVB<61Hug8Z(G-eQn?258u#mIfR8Zz(%H zmKqWc5@0X%#NKVRwO|`#qu5rAlwtqLiGpcAWgUshEDy8vKKZ0x5pt5cEXmuYk$ZLg zj-g4#-`?vZmvGfW+FiTvieJ%sUT#)iG%m+oX>-!JFJevvrk8UNX`lcDM6ePfk`rN~ z`s&QNcRdKpgVgb;KZV?M`1s@@&V$S^DA^7uDgI+Fs+H(1TVL!BX7BM-05kf^(GhRU z)Q(2^>bIMRg3h(S7ppEQM9>!L-Yc3DJAP?@D%tMjI=GW72p|DOax_K|nff*a0$?aC zZ@;}HR+f2#XwNSufWNzD z0yBqK0-8=)VD;1a#yvWp<-lZ9Np zmAomIY(1o~4q#^%Yf85){~!W368x_k`?cdg(6sd1i(pq^h{V}{jVUy(dLi$=541l^ zX?uBfka5*QKguKiRK>O}j21`C&Pv!OXH?UQbK|Ace;i>}!8* z>ULDk@L?wLd^iQmgXhs2>3$`)c>F}|aRKqN7;!6Y*s%8Bl2f_QCN^L6RO{A?Rw&oM zvWImNnGf0}+OjCD6fYguAl4*`2myZTlc5{*q2G9}lX8QhUH~*ES``zmocgbpT&yAx zyHgy#s~#9q-ZSe ziSG`OHx9`vTh6~b?x~7m%zPiqni!@L89T?`>&HyD ze|x_YIx13Vu>mnB^tY>)!{dN2yYFsG8eROt$r)Qr!e@lRMeyrer^86R zx2L0i7f$1p+20vYBjUmUC7P^;bOScz4}ayvML7hn``&F{C{4V__PW4uYQ~f+X~vnT z3_>Ir!D{+5jYthh#yDLTc*LO;6GZRm{YK)<@kOA2<##){@#iPOyTIDAl3NtOPJ-yo z;zD)VqqXXtWWxm5CP!kNB1TpIPvB<7b7Ui4)#<+Edh+w2Kl7YMaQ(h&XqUB1jzzi? zj?#!&PacK0bQ^(^F*LQtZp=r+;eT)@@AB=dKmk`n|nn!2jwcn0O=H)wq#IWDz zO*c52qk)HQF#ACFIYDf7!SQQHWf&msN8roM3m*tke+>>B=sk2+jb}<6Hkk*LYw!laB}@FjERz|aMXY?Yib`r&Uxt6_ zWfHrVebz+j`sv-fqA&H1UAu>74HV~x{Xc>ILQ!gi+kx#TAD0R*R;H8_9-p9PQ~MZQ zliEj6k}L!R(0t1|t;Nzy7qdO#kw%G!_l7C|nIsO{C|}kUK9Kp^R(z|@PWq6|7&YIn*hn)L(;~icKIB0|()A+26wBSl^Kr6Wkgk&(8!==AhV8sf)JnsmGZ zAjk-o%oPnN79w~`kv`^o(23O-ZWN<@&Ax5KPX6xnIw5D5tsw!zgs#0Jd#m_8Rr%DqlyUGJ_TNFK9jj;_%V{WrTCvb zzaGOaCRCWF1aW{+;nFgQ`QwA4Qsq;Z6QJ>Xdm410LNl*)1c)FO=c_7srWMsIT#{7ctce$B%FOqPsrV zvGIYTmidjl$9gJd{VeqTW*(5}W65doK#AUX@VJXHtm9)Y?QLGYY=(89J;fP6th5R! z{Sx_at|4OL`BSbgllN@dxOl%2Yee@#tULvj3M6(Lcd%yc)A1tTsH2PD-Q z!CcUpVi_$INo_kK$qu!&iJefl5+00O`s>@Q7)V-T*$*POE^#%(gPYGWR4ok4YrM1~ zG|0EEn&~}eE(Gv^+ctf!w#8F6ljczggsTH{wE#ZqG{ z|6Zbjl#nNx09F%kl#;Lzr-<|l!+-p?QToz}w5SZV&jA@^Y~>$N^Z0m6yS_wHdsB{u z!)966RBDxR#po`(&nlrk6w{)^Y`P-0H^!m)bjm014d0qOUYM8tWBOj4V188bN2%CO zDIUQvK-ZLJz6U*qdnnSM`&&Z7*w`ZDXW7J?IAs+OFT^4ws(u3*q?I#y&Pqhz8 zG2)Nj@+yhXvKC%*lH=7lzb?Yl?zx}SIaq8Mt#x?*L!5Y}1R*N206^XQd(z_` z&JS-6K3XTci_Q;U7biBPO=Vp0&M2-1}`JL2AEP8@OGkS5WVB zU!aIWHx0eK1)M74a4T-Dty*w#XpaUotDQ65GHnftAYZa1Qj_AIT$D z^uixv+D`d_HG?pg26kh>eDN&743xyx;?!cM9)Q7upp^6ZCJ#cLMV#PK`s9=NBrICOg$`0A)>NcpzP>*FwP8q8Co#N zBQIe!sx|^AAf;RK;BV&8^6srcE5UQg79FSqpBFTwds_MkDBzV`d)2@FqJqko+<)9} z#V%`bjh<@;;6y-FmV z+pdm5M&^-egTiT~7fAZ=nF5?AG0NIC;x^CK1R&1S?E3RNdMW2w=+!J^)jRZR_?+J} zH=!6cq;U$7rC;bqY#|H(N^HY<^#GyJmh(5{R&SgdIfm1isro{gIRA6bXhg=URldKq z7Yz}4JHiM6$gT`tAe!6(dd7t?aBOW5K?Qd80#%@x;*TvP8Gr;e;i-WQt zetGqr@|t5^zLRmlIrWi$@%}D}Z`K1U+!90|1^uv86_z!NRi_eO6rT1KGIFdrIr=vKnygyO zM!c}U=1xQOuj!j!H5vmlnQyB#t_j^te7%jCy)O6Gl;iSE^{EV<*;~vY5$|Pn?ae$J zS@9Qdu_8f2fo}mkNC}Vik;Wb~kByS0>Fl>mWwby_D3FpC7(5aH!O#IMQ-vr~xp;EA zu|isvsQ@0QL?cUZ(912(W^9d3L!~qRRtrWT&D~b+W0zr@ZuzVtY=>2M=ANTnw=!N> zs6H)Z{$Ull&ZG+$*7a7&+C$aumSCDCe1_{#A`K?N5yQHY<+1;wRrU8ZUR&kS&URyZj=oWF9i&=g3_iZq@9i1C|B zX_-pR4RY&kRsUEESKRL775`dhouPGcpRk91`Fsk= z_U``QVmsN;ppDX5Ss}o&m`ZPy(}yhr}J_%AN{Z_a$=%uEz}13`G>kcy|}TG%~+fSGAXlvOXb%WL7K=boRa_-fwnHIW)NcGf-oBbo}R1*lci3QOgqj_sVHewc@cRe8Z9iHySgyBq z8DXF%yR1~qpK=0$jDbJghhHHeOf*=ZVPIzn$4BAkNpG^4$q#=BNXQo?djt>P7aq|F zUYQ6g+aCB~+B@xXFH&pKg!=NYXX_=c)(0~mEy7m1!f^fS>tDyW+dVYw!nQAX30Sfp z3*LXe8TO?Vl_R8Emh?0%AY}JRT(+5K^+LQQ!}0z=D6ad=Ol;fPNlcs+be;q*X==&tG|> z-jd!CS?)iESi90MKl^4ySVBlb@eFlX!t;vNReG{j@*H%|_oT$}yv~PK-j%O2ut*J+ zQ5WX1a4>D5d^M{-0Fp*O6^=F*tF@~hNnv$MslRJoKZ8BfW@ai+OGFKYN9N^rC1OKYcj*;-N++R-^`p#M7_%0xW6RPqz!X!v|4#a}OiS*-+1@u7TAta%W zAEGfJ!5R|(6p6oEHBuMkc#OFy^ZnwmwmNGA#*W`7;L%CpTjrj+r>)nbKh(rn1_Y2E z#a_IgRQD)O!z{+{QFPLE+O4k36rf}k_#dT*CdKM39ZgGqPf zLtYQvBZ(rfi!evzDk$=O*A>yhl$6e>zJvJJ*HdpCKB($inEvLXhvUT%x%8OnTe#46 zJ%lXjnLG)>B*1IYS+qzz{d9J{LG~JwICRkI36>8}4~}cj;=>`Q4$HebU1Yzf8vZJ{ z8hFkdzr<4azFq0=ddrf|)`G7-n0D>XC51xoSf4v89$ep2PRAaXH0cMjXtFc|tn}E@ z|9+LejxBqpfAU>yc>xw>8cEgpRRW2lU6Qc!G4pbT~S=w0? z2^>>jj~Lpy$h8-d!ie~Bw0IO(AsN^-eDpx(1}kYM_)2HtOC6qm5RWrXWCIY!;~*P9 zQ{T!X>wzvmtN^21$hi~DbelC?>-l}v#gATXshO(qyKlI!k|+SS!U?~Lc7l_x@C~|1 zK9YzTbkrsD@S6&_8Aken#EkR==RlEH>%|GAl(${tR-|Kb6zIXr+6T{WJiXFiHF1Nt zqf5vGHtIJ>mAM{Xj|;6MNyHC^Jo+(c2nrnriGRN?LI8ynkc8-iqE@7ciXc9xpHCqP zQ+^%|F9RU5xOZ{--SvQA4e|Ls<{9yCb|fxT2bWX7!@fahhvMFTCi2T3=J=IH*F*PGVtu||#v9k|`Qm-Qo&pnkd{+|U>Q24qOI+qN zhqlq*|5wZC>svN_{LTc+K1OFD0(}~f{e4G8jsLA*eT)Z@fIoD^!YNEa;wA+R55a!C z{%>oz_t{^74`6b0NhmV*#5>~?EB|&rC+>dxcf9*MaSw95$8N&PVffO@?8s&m4buN;=RzVqvBuHvIc?)r7Y90M-E@C+NIdCIYG;WL7q$!35ft^tPWoGMD`_R^-{<3h-;$0YW>6A@RZuGi>0_GAF02!Ei1al}=Tma+h~e?G z$QIRS1}wketl%=@&e$)*Pm!W$fH~yp#tB?;Z85&eGWrXYoF$39!{OuW7w})9bB9N|d$g4Y8XNbf z^M+8k*T9WmKMsBi5%7Z)Rw12uRD|znrns!a~Zz!pb85YpNn5%%&zPqAJR~i>ir=X^6^V#l_Xd#WlpG4I~t;CB-!* zB`|17O|+t=l$55Ff~l1987UQ8DOEdZReKqklQJ?|vdX5iYG-BD&&kPY%gO7@%j?Li zTgziy736gk6m%6-PAX_PDrjC%)KgQ^_EbLUuB_v!a?(LX$6H0$M@3IdRo7ch%~-=k z2cu<;(e*fCb3#+!Nz-*)OaGjvDf2UzEG-i(qfT1c`Jb`%uuZ_(IY*qe^Ezvve%2-Utm~z-RBh&F(=k`q z$*tDeHNfTKb(a8lmukcFE_rSq5f@xg^xx?H}jUZtQ!p!uKN0 zH{hCYz|DZ*8p5Uah^t8vH#Q=QaS=ntkrAF4y(y`DKT2IBxGX--XUx1`De>N1 zzQ4J(xw*By(>DBl;L`8egg<|N1INJe|MLrR49Fp}kRb9P1;MB6G1gW&oQjmODYbe~ zHJXV!8N4v|p!(TO^yv&S>-L)G1yI<%EW8ipze0@Pc&YWnx)*o#u5K+ne^~#r0!v{N zx9MnjRc%$E>^a`SL|TX%d!)5}rKIzSwFfVbcQ(!5#}8+SpXq9ze;D-op67pEE$<%@ zK8}~2dDObt7xQgP{M-s;1|ejNcffYHEf1&hsd!CvKlu18SLRH)ZBP5!e?=#+EKT%0 z{PdD`I#a@~SIdV(UKP0jnBpk2ZNg{=I?%QAzWeIur5AmV_CC-l?2`8V-CsVA6{vVm z_V;}IG*x{@A&b^~@Okdxl@F7P2FnaK4DlFsRsss==(lPAZ=0E;4n2o(&XUH#wgl|xT!gF!)BC@xd^jS)k@!H(b(n-|FeoN!+W3wHHt zoWh2((G?`qx;XbOWDKE$*id=`TpS|2W%2Qf;ckjwxXF@*-_42T2JZ+~Y3wS=R%dgy zz`;VvBgV6%l10QVuyO5L?{0j-VZ2ii z_%=2XIoU76z?t=@01FCPOantYQhln@R=TS1a?*7DA#dd}=s=-kNkdf*_`-+k2QLt! zRfS$`L2)w!u4N(hjy9;eM?S~OV3rnntH%5p;lR6Khwq@#ytEZP(Q7?6m$x5`$un7Z zdR4E?DOyugXo`S=Vra*dsY(mwjcHnF$D3$UO8x_P%WAL;zl{=$w4(c6)>h7U>*muP&X(i6eKvRu)l77C}r+B_lo2#Nlp3->nCY z8XI383>#SGLD7)XZI~D=1_w`3!jm?hamIWB%W~bl_i$K=QMy4(&s{O+t&o)_-{{n? z91bzidVu4#6AOW|C}~OgJ#oC%{+}{&qJhqSMj?+1w%ID zbB1}&GHAmqnYxKRyk)OxPk9jga}WVv*;jElHdl(Md@iyP(s#d9rO2=tK);wKDoNOV z$~fR`gDF+nMhpWEG~bc!TM{I=#JxwAPzhJ9e^3ca#1~7UVEk7zMeSSR=87^Cr1XYm zJ0`N(D>vUlz8F;r7p&;UL6y>pBgz~2On!e~f%KSBz!k>^?obtUu`-iSL*sP&$y`l} zqe3P7>AH9le+9x+&R8m$Bhd$d1r4c;<)&!#g9O^0T==GQS!9r;6juu6IoTmi$O^<| zM!yX{c0^}-qFxE+0sNw4`x+WdU({u~sYpLT>v|{cd^M3=hRkKr#rp&}+Mec}!=B!P z?PdkpR*NK104+tIh|j}Rfgn6g%Z(Ht83Ptkzm%*q27=q5oJ89$!K}pw;})DiEBmw* z!wX?ouQJL-*747#1G2n)Q6uO{I*7lP9i)N$$mfT+&-W|L!EFO7?zmqb-#BKk8hjQK z>5zIpZHT+70o>ztlBmw8WRJv;@SrNGCSy3R{6mW7ZK8n@0>l^U5GeI&A8wI*1|;p4 z%x|B^ij_6xPA?i!aEQooigI8JAA<_If>@p7O}U#0W-7c7lDQIB*qg90^u*?IvXd-G z;JvDXvV2||PoOlbo1VFdu}`Y)29c}Yikj(q;*{6B33h`i(u+1CNlOb2L(olM_cN03 z%lQB}e6p&mGP~=<2FxuN{5P-xuHkRWmL3d_JHoJ)=lHVlFsj6Bf>Kq9e4y}+s)n~? zZ|V#>!`tWmHZi1h-3vI*U_4c9iU_x8Smr*j>>;l9Lscy3?9FQ-s~TTu6MkJJ7Mom% z`{z0I(BCZ3l_`HQS4QhQSS3f%J+S==ELitfSelocIX8S5^l_6tw$BmEXZ9uIJkVtc zX#>k7{(|#NNR_x84PSUq)VDB_D$3igmaU4lc+XWdDW5 zs^65H4veUGSC@TZpzKfVHeU# zCd$grAaGX0qG`nBMr|L!jiJsz{7{$U4^iMSAXxx70N;IuVZA)%BQQ;)NI9})RwIU4 zx3$a_^WUU%B@R1Q*etVM?8Xkn4=odjtkzba@6DK<7IkEQ_vr0|+;qO7qGrN?lHxKV z?-oR2eIIHetDc)&YAQ5d(mj6y%^r~k5e=eLzdM1>(A4%)TGN){Kn?n96xq3Yd~5~|vdX5(_A3(fsb;qP{Up7=|aAOjo9G+kB=LM3-X zOrtRbY!Y`kL>0h7bIJKHn{RPK0%ri3h8@e3D_C~vS8y!nR*vzH;?5PKE;cXL;{ zJUuIrdoNGeBUX3IMTK5F27Y4EgajdSNmQ}$0I(0o8$La3lo?m-hBPR ztXmmx!CdCbazxsj8ejUbKc#FA57e_BUm*?)?zEZr^gG|aAdq8Z@y&59Byjfme3j{& zuT0yQj#ThsUXI#bju!~SWL}qhDK4nF5Hy4fNyHhofqc%TVus$Wbv>FqXoXxNM7D`N zUQQT$AW4F!n)S^OTSmv+7vBj{=zF&unyjzrOC-H~dXu|fTm%ml64Sq0`Zr^7;xh5?9a^Gkjx*$>WI|EieD>-Z0U{NV*?S^qv0q^^@4??h{#GJpI4|9mjung|coqLXVGb zGCY(s=x{4~?0V7@>x!RAU}lE)n9Peys$LJBaVv`FpXwfwWMmmayhzacVEl{gBvcVe zq?;th)LtBt&?vIB9$D6nEFVKwEFvp+lU3))>W5?uilV7U(JCTwq7|G^ka*ExnJJ3# zAq9(~n(9%_+^7~YRI4JYO*hqcj%t5MbwDLM>Lok7CA-EXyA>s0=uY-vdfewI(z(14 z5Qqm03XDk!DoP3NP6?Sy2|G+7pi(3CQls2bqhnHIic;gcQ{(4S6Ax2~s5G)(8r3Z= zB_<7)M^QdOg1$=2IX+CwMWyHKrQdQ(&+kqvDoQWuPA{EHFF#DDp)#uUGHTp1>S8h) ziZbqXXEe`cv>s-(p)%X`GCRi7p}05%F|&6L)H9aZHSaAG$`k@X zopV`}hgnmo>}kF1H*VQ)W3uP0vU#RJreh%NMz#qe$5=7vlvR$~m+TEx&Zb@tP7#H~ zg62^-zv|um?soG>%+151n{QDd0dx{mr5VI4d8C&MbKACa z7wE(moH$m>6Wz?UTgyXR=VOI$@mqtqm{mB0TPzHK7q|%?y!CrbL>eP;juq_We%mqj zwsZPz*W%kAJ-59YZ{z20`~14?>wd>S_KsiioxsLBK|ObTdq5dudG>1sHHQT%QXuYq zA#U`o`1!*6B7k@qU_}>EF9Q+`=(${&I|FKh1z7frayJ3{V%}`4OeCE`%`YzLDK4Ea zF8@_b6E3OJFR5`asf#UX&@Z|CHHQ~nXcUPeo-Qm#ffTIoc3KNaI^0!M0&(F%T>Bv9 zFU%ibsIXc1G`2Jo#jMWd^;<8m8EOye@S_!seAA`TC@LQcSx#NaT3G zz&9;@?Rna#9$rCul9?-I_gDG;CbMI+2Nd_c+_qjVr za|$ekhDc6s7Gy{03Xbk80iBpc(@k>YR7;bNb4GrB`yu0qvk6-bd-lEOGIx1idYs$ z1~fyd-X2r$j;?p#ujM9!-E*Pmuq@n2SX4=wKOQP`p(?{5pX(S8uM)7HrNA{);I&x$ zJHd@zw;L-6Y#zBlzcADv3AG1c=P&}0N)QeSm7=AYDczG9=e>Tf`e7{SoO12hwEw5P zCKMgi_gZ4*LDQ2si3)P#u*9kq! zD@N3N(aKD6>rJpM*m0OQaBsZ?;6}qt_F*1apm7}bqOr)bx8blPkChHfq}l!*cl~#v z@s2^YRvepl99s_VRZ0COjEb zzHq7OIZ0g>t+1LWpiGlTT*%nGJEC72t6w@{SPEVS#+8b>0gw=* zpPO+jxNm^%7n2PQ5RW$j273`J18@ge4+~gtGguVR0HqiJGkFQLf>`Uj@0xf8Y;C z^Axl&s*O!R(`OMeBgM`O58by)=Cn|wbpVc_qqzce!vJ^+a8wL7m;$aCsFaI}+xPK~ zoykZaAFwdG6@mbfl?D_p1M^pqf>;pCiHyz58NkGu<+lcU7>Oq~V1g<5ateepXukWM zHD{mIw(lAQ3-6_|Ki+3W5dlX!OUBd46WWT)%S|*S`-3S~X$)Y|W~M=Baoj)ch*g)! z0(Otz{tq+4W-2Lb^wb&;Z>J2M=It|z4FM@%X^+MnTl?EoC4zZJEIq~-ZloOlPWu)! zs;r>S-Ub<8RSlkkWzi8%Xjbn7xQ&B2Qs7)gE^8DqV$K3}pTXWkWA7n2nzfnaIv{pl zAttx_hz>9a9cJNgMgtTf>#%gdJQ)wi!WGa{EXw;<5X_KV7GQzyCz`qy_74?ri~jv7 zTN5(=BmU*#*f@lt3g17qK8*M%!+PsGoP)@s^VWd4&q~Z?y?bHUqYzzzpXk|Vv&R5- zxxwiU2pmKD==AA006sO$)&{Vv!4%*3YR=ohiBp4Yn90Jg(BH|E!P7vXmw(oRXYXPlSf`=3 zBSCwx;AFx%I|p$sx>FuWDy|;kxBEqU}t%-J$;Hwbn?=bv1YKiqt9S2sUX@w2AD`oU9i>^ z$g*B>2BZ=$t(pRPnCmgbb;&J`^QFOnMWXmzSHk<|v2oA^)V6d~2QGj4~SG)Qc|ljyR%ZO6Aj+d)rjr$BXNAX1{+ouvpdbe)IVH;(hnnM);C(Rl*%z zu*!P@jWh9@O-ovRv;21!7fyZn9$J1Uv;86mH#5V%$q~bjEK*SsC}CtN_(RY2~mlkjk0fNa2l_4{bf{}wjZj=ZJdZw z_Gs=R0d`n8fd=WsX`|>~dLD}U&Fa8Lo$SAl9APVa55O3QRg1(`tMXNw$E&tWtM>m^ z9VFHqjn|yj*OCt=)wx9XVem&3n9l_#F92apgNM_bTKW`9Ze*jQNuvwqbUPAX;TE@06t@3@SF zhGSkrhStL#Z!WNZVmAfLA-AfGw`#n$>Jqma%D3)4-fCXjYW=s>w!5VaDCm`K+#}mZ z(IDOcT!XBU!7q$=zI*Nc;QH*v0RcbS16x9> zTlfB`6EJwNm)QPq7+3pS-A8-H|NW-4)p@oY>udS-vD8 zxg*Q_^{4U9W|_dGJy^R9ygU1K2M&2*4>mvE{i=2rf}wp?^-fZ5`x?as(V~5mcUYZV z+UfkaV;cG8^KOgilkdUrzf?_vWGl9ox-Yr$9JEe?m^>tLz7 zU3N&Q+W2e0UbZ`FNqGu-;Vo2Ga!VW7ipp7vvR^%Q9~{MXaMd1+^9En5`1ES}8`R`e zqWAvky?wRBhgMHigzWgbRg~m&6DWgAKySby}eW;mqgtvumKlzrq zx056JVcYoEkMPpl+~AQBkmVE$@MUD2OT}`^ji>4NAh7$b><3r){l;mR_5_aFk*yrp^EPyU}qor&C&8+p(aW z3bz$nx;+B}g6NbGDRMd~)2F$s#u01E=eFj?RK(WZaOpB{P`j$U(?`LXX7?D%tv!!5 zD{mU_PiNleQIW>MPnDoZ(4l^(<+c&QEB}i`er~psxoPHjV#@4&i?u=pRntbPzNXzq z#q;ydBSy-%T|F)8A%({_hwh)Fk1DR_)W;~2AeLd5YX$ZH+u$-Xd|xx%@8KXtmaQA# z_hskk%B|u#bq^Rnh6)_vYhS6GA*(nHOLCE!Huu~zIJumystu&lf_-ml*)tQ1HCbBB z_|GZ(RKF0gE;^mg!_Vsj<9V%2g7S$t<1Bmnr(caqetp+;pVxxPqI4@qJX#Z@e^l#n+ zW=u*5)R^AUb-Z<@kVBWrNM7%~az8@!`hQo|RiXi6rp(fyhyrXK!#k5}G@KfUUSgNwV)-5B-L$AGB!2@(%xYx-Uv z%nl1ll<4vL{xk9qdd)c zF0^IU?>9!C{Qc=u12sg8XPMN z0!j2==(6dy(CK=W?PB1wO|v|0@Rld#WQQN8ZhqT{RO^j|?|x%I&nX-@G95&H`zhp&(8=nXA-cPzx%>X%I6kT9ZpB&}>4tJzK6?r>j2dF!y$lvjYfDy>C2_hC zA+&pKFjdtVb{C}LnOXxE!(Qh+&C#vEpbV$h=z}_ggGn1^Ec`azCnI2vEZ$FNu^aMf z48S&B_-5+T`&OVbO-sS{Vek3zkeQ|=So5htn=Z{Zkdlm|YTXheSw4h`#hn2ii{q>ZJ1d zlM@MHF{4c_8-cRjpUlJKX8OE-bj=RzC5OkI^!4O_jD~Sck-&sh5J$gLnUg5MGSTh1 zr)-`5(VRfO@4S?Kiy3E+AyB1^d|1yH4GDD-Qcm>ya9%GOmi$3T!x{PVT$xKqydvl& zaBmh=Q;9^yKv-(_!K!o|`M8S42UShJv_Fc4(%i3q=B9r4+O7V?@x z2;Bg&JK~3hP#D%zc*cuI*(b|f27*~Jhf&5sQO$qjAs1#ZynXcaT5H4(rMB^~*7?6$ zE=LATyt|T3!-|6FM?y8Uz7;vrv3xau{m`OwG0jh-yDo-adhqMpOs|es%in&AXztww zhQ;;XnZ-+95Qpg}T^#%5Plg}9u&ZEVIq(>i)S$^e0jQ6j7z#@#&plU}92#Q|{Ss4?H z23M}8S$a(AHVpDm9UymS-+j`4q)&^;88-h-hZ|xIra=RgY6Z{DM4jH(*2cSa3gS~j zmpq>?FMf%wGg$TJ-WgL$q{x3K3HIa7S^3N1#UG@QRx#iacmhPyc}1XU?ljlNKW4E4 zjnznx%$<`y@A?Jex#vbj%L{FWr}yO~{L z*Z3oTHz93AEe4l~X`msjhzZ*jjH|d|8qZ^Tf_fJ~QL{aUVJ?SSb8fe#Dlwt#E=5C} zx6tet=SH}4bhRqjC89Q+5~t$V2j3ifBy2)1Ja^bT=)2z=JDK$7+b3?rM-{qVyR7lQ zJ9LglND>ilzZtAI;c>WUP!VKcD>kx-VS@ClO^(=p-5D2z7%LZHLI8DHk_j9*ksT zD-ytu1)Jeann=1+1#VW5rO#BQ7^F@c#1IcTMq!&Q<{;LWsp5KMs~~>2H0age*n!Vv z<882{RXgwML(w_1rrsldk3qg$M$sQ1iWGrQ6_Je#aFR8H^6z>@#mdDN2JQOmr>YHY z4^yX}5Op zpNDA>s%sF;(4k)@hD^T;rvC?$L-)(HQGQ>s7<~_tBk=IV_kQjpDHK6e+Q>R>WD$n( z=#>mj6mEOiL;*p;IN*4{58|51x2^lFCycxw;%u8m>{dsTn&rZxenV95bk1?1>Tk`oaDG7|_Re#O$*L~P;Anwax#hSh#GwX|d zkN%wQ6^rbp`3;Jqo{2dk#Plo&GU-a)mX^aNa(I}RS+Ahjh-e#S@M_2If0kd~H;;^| zPr4~2ezzpnjGx=WNgPsi@K#!i=GUx8V+SGHbf^W|oLQAcaTt>h0e_oH?uv({{kFRK z-L!c7p@<@-3&>ImBA>ZTH7x?aX4kF%MnBb0(!xQE=GseZ4B^|)WBNgg2#6e#94TWn z;FLicerD4l_uiBUKhlU97ymE_NDh~YmX6;ud!fCBGjP^aiU&W-?0dTkl8XV$<=Tlk zkY%!lGD9A_<-&MsI*&(6&-A`A3@#8e4+y9vu z&FI+;Bfh9XUfYq0s7E3pHjrR42-~Hc3+8dF)3qYQZNM5;I()%B0jeZH4$|KCq^fs_{}N{ z%q%ILk%nfmQG?|LM)?g?mP|nyuj@7ML4KeaqI?<3>r2hAn!CDC#_DCur+1(-eLJSP+FeAk%X z>{$Q#X@t`oGw4}!&JK%f&NgCa&n#F%r$`#PU~xKFVG4u|A_}3Ih?Oq8QLwb0w%$n- z!vGgPM-%?>hx>#l`G#O;Y_hdp@_97un8SI6zgMGq8Xwvr(D|m$Uts}91fhs9u*8B7 zu<+`>e{EMz^=sEo*RVmSsE-a(NU#X)KRJQ7gce8N#fjBkj>cunmJ2gi0%q?&e`_T0 z23vh9qV{~|H^=NVvxy$FIvK>oo!K}0vq|RJ!~vaYG;@i-Nruf4J>8N*-ALNm6mhqd zGPmQ@7PmAt&h%59+1YLxi*7du>aMq(SCg5~*PbtMX>XL7)p&6};S|PkD;t^X&K=a5 z_5vv8BynKfUQGd|1H`fxfV;?@eU4eAL@cf)mJI-lZo=x#W-L(sp0ry68Hz(eC!dho3 z<2}#D`zHtQ>9B=CfrTNtg<AW4u<)7aMCmKP4|R z#UZW*UfTx?ujahw)VxB*FZ!&JI0*ov?ZfZU4}WGp{9XI-@8H8Ri~;=kz-~2id-VKx zZGD1V+mc$XwKw&}Mi;HhzK8U*aEOm^Z zi=Nyx2HbU#-S~gvxW!s#e|&h#0eK%!0j_1l(jsf3k5C&!P{LOzZFzB&>HG8Q$|kZM z;^i$sY*usZ`#uOAiKSC3(p+QbZ_E5Bm}$K7JdfYm`*!EAWa2R?R2xnpYUcntIRG1h z2&b*QDf;+226z@j)C=@uiy@v0{ODT!QNPu1Huq!M3qRI5KmBMTYtct`gaPES8PqsG zc%}s5<|~rO;Iq1rl)WNk0ZK|BvUU3+mOdh4i0pd4($U1kYs|;QN2H7Blt{F#!`pMY zttR;J2nMh#uAvTzu8AKx+yXd}f$&43TOf&z7H}~U&mYv8bZya-3v}VfnwoK-knswK zUf`u@Mu71eVk*Ecn!!i()_8gnGl|Tv?t4xh;??S~uQV8MIY#68bKRTE*M(ezOB1#(-r16Q8eG~LTrK~(k>z4@ z*H-J~mP_4Mm+?xqMeu{kE#zEqmHH*#U0*eKlkmGP1)0MgIth@!N_C!G|sA3R_pGcHMhGGw);9QVZ z5fRP^Q5b_-VoBh~v(|z2m;YW)eCUy=?FqLCdcWhvi4W(T3ua0lIfC}Si?198?%nlV zD;?PTCgIDS8~)?pR`?I%uf)Ae|Mobh)}`KtbB+2}(%2&`kP0$%{jO+gT1gC3_pLWy8! z8bsz0nxvF$I~8Uy7bZi1P^6&3MNm*K*;SGHuAHiqFrSDA>!>lW z)Y0XCT%WF_Y*ieb_mx=TBfd{e0Nj?=946O0 z8$}u8XL0%?Ws+*W@zuJBWO$S0IQG@rDpKpywk?tj_5~#SBP^=a7 z*m7&2X63D7(ZA%)&pbf}624<%tt+O#YH3d%VO8WiyvE@zLFLPS=aW30)0AW z87)aGXN?y*Cz)2!-}leDg;@Gf*7o48H@F3Vui3fr?M4gdf`%T|Ni=tY2I5ivjvgOL zE7yqVhU=DRnatqtifD==0b)89Vu`&v{|}^`N>)9jD!l}&cYi&bMmEz!9UHjC80>$M z{qW0-Kvu-UOs96O9ik@J!3Ib`6M%v?&O^`?$3u|jA=GpX{Q60PLJ`$^jGX*T+L-_` z7z+Urs9P`DtfxS<*?txjptS)8DNa0$A9eSqx$v_f@< zn1;)*P1A0n5+c7NBIV9;TDVMyG=8JPlXWR%v%C{NP@2*?4|EA=?c5#KQ>arsRhw>Q zM(Otaao4J0H%N6HS;Knyw?wi$hH1><|ns~x`@w0(GDbu3@OXWX9>6wQ7~6&?F*&CGi9bGf2?)7315?}qmNn&Z+NW2Ka8 zkGX#;)0;|H;K0neLue#614Y{Pw~YGo8!O0-s^}lZ8&$Eq2930oE!IXZWd^zX%JX?+y$i5%)2xu+suEKn}&U6K)T zBtLLq&;@z?YpYM=iQyqbGU$Zm7yaIGsZ`WS{;|c>(hTW%9!- zp7t)aBJs~JAtq9fgNA*stT+fjj^$2j{JwUVY|*6(MZh2+G`i=N~ZT zX!`46mgyPj_be;Ebb(iA!}&X>o#Yp*v&}7Ub-sBQSr&H7yAP!^<-1lya&g=0|KYV$ zVvXt#p1h*5c`kzWvz7JYjW``lSDpE1AxE4umoE_}kwKsQUms%ZnyXq&0 z-~(scZaBMfz8tc2SbMDZ&CQch*hi?3E$WZEm~`sL{FMsGhp)dDi49VBB+u!TX59~Z>OI8o@`Dw&roSSFk zzuqUsr9gem+peb4*ynvV`1|lL*cXMKR*iZ~(JS71x(^NY6>2New>(w*b0*sEed zs=;}gB6%x+dlhklS?lH(J$!{j^GR~;bakCqfav1kU4^bZs*&?7E39BBW{?Qi_#=ac zT-K1F)BHZLdviW395&AnPrW`-Y<=hOvBuCjazXq#IW;S5>o7vXnNo((TD|pnLVH1# zn3Pgs(D+$XB&GR}FN-ANzhlQl9xArER<VS6T&e^~RpM8-r0_b&*rMSY3DyR zn1>JSTe~MKG(ICh*01w@*Vd0)uz^3UYzxoGkKSxN%soYL^5a)C7~c<;j6Oi#q2j> z{p!2BuQ1API96BUsz3vcN#7sLTcfQ0lI?k}ksW!fxYVDoh*2BSq){}ix_+XdCwyV# zzO*QjD9<#g7+~D_mioP!bOn%N^|z$Uf_SM<=F-d*LH7kc?RFX(N>;aqAwoAE$cey` zjlYMQe-E-y5t%kQ*D>FqvdQh=YmskZlZSY*`M@3jy6j(iqjlMqbc={eZS?mB_iVCc z4C75a3tyh@|50?-Urj!I7+$fUyM@sqF;d!rv`9&bfV6aXxs3+t5D;*5OGzu=5h@^E zA|Q^GloA9{d3n!y{(wE_JU{H*pZmTpT^tVq4cA5W(WDxJ_~3RV2KX5|6XKpVXCK6X z0IYLK2lG`U7xhgb`C^5IUe=^*tn?CZewDlZyMm5+G=#uH<@eo z;}vE>t77ptH*n%je!-^1rb0ex43i@fM0LSL^IFtNR7r9|9qMqCh=!8_xbWW#+d2{v$6?`M>3MZCcVBRGPZpY82& z+zHp|e0}!%h4TKo+Yq>3GN|rX_=fjAI)n=*+N;h7@{I%=t^>Fj@WC%>Z^^IGQF*WVfz=_9qCQo<{}=i|=Izz;x8h$GOUg;y_7$H$_9FWY zEOVXH!t3)_F3VvVUu6nxm<9U|<9+2<#v5ZC~j08&@K`iDee8Pw4r%8jId z5)7bkLd5_|e7J;7RNEYxwkoY4fy|abmOvmY$AE73qG(&NG?3;-KObuo`+Vh>(Kr<_ z%^b~iIIC|pnx;KVssD$Xv~xjqSzphQc`Jr^b6G=dr6JTlLsC-J7ii6AQz1k8dYwe_ zcB)vr*tD)xlYnaoY5zE4-mLP$dnyWz?MYk9NPLp&#Mk3na6up?h5%Q>vMys7h(K1s zK6WAydVyiZ16kRzv~Pe^N?3LxmPtjG?E=kYf&G?&8G#^y&{8C;Z9&Z!z$8nN5MZ_X zrbA5R@*`fF++tL@Z;Cb5@tE=nHT0(V{2yP8>VbjZmqA#z*{vLPzJ7J_OG8)FS$@`Z zwFvuw8VZ$^47|oPeFj8}eahhPpx@sK)eA=7VQvvtJJBdpM(b7HEz7Ve0?qQbo1coC z1;7LPIEh$R3oPqUAKfSxZp*_FpvpFcr6u;UD*-t+?AY*F)<1omA-W+5Zlw=p`F|<8 zRIlxeLy54cdHR!i6z#gjSNn$~IsVES;;D>E570V49iIMmU{*C1qbTQdDvH6bX$qFR zN|e)7W{C1LsQ*qge3VNwKhJyq=wgRvMZ$q|njjO!q&T9=_6EyZUdF0|Whk`Mt1V;4 zV`bX=*zu}N7CZ%cXi7k-5~I^N4fpn!b$gvP?e0aYVa#2Q7)q5n6%Ag^b^|3>0OLU} zbu-QVpYsA585-Rf!_Aqy^1Mpc+>Eigs(Oz1dJ`Uxrx~)W8w}6<)iyWphnR>tK2(X3 z?45%XFrv$ul7p$amT%sxe%*o34l%K;`_`1S= zmFFvV$|@oi|Db~JJPW-zW8FK$oI=Ws&9&BsxRo-fi)I*-%fBRrmtMVe;?@<;40KgBseNaWvn7xPGn-sh{cn>C+s_trnSDFff6CxZYflJ^wJyK?hz8T0hM{#7!1C&iYMh{}p5!{BbkaCS5~;H>)I>nj z6oHf-TUlOB4In0&u~DELp4SQ{b6}JE1<4(^XhfVND0e!>1R|rdA}z12ZESLvS|!; z9>$hds4_D@gJXxAuO>4L?f3|CXktg6y$k6ZPn?=ijpfGG2y1r|O*(gJKBT=Ao+ZKJ z>v|~@^M&g)^XiPbN=4Ia#)ScM!j50vXbOZWy<$@G-AMc2-#^+dIm6fD7H)H=ISxZ4 z`PYksJ_ldk`aMUI=N1Mfv^!K|ujh|XfQ9|68xs8{_;=d~!f4>&L zYHz&i760?0@z2Pi-PkO!*Nf*&B;a@L-Yf@sJKkK6_Xxjo9klp6-In5{aibGnZC^ni zmPaGzuA>Jahvo}$ql}d7EKo=EDn7MQ`bV?+k7pUn^C2LnL?e^;{z=srYmH?8q27HE zCI2oSzu|hjd!k`{?O%2&OP7!^m-^iwc_MCn&JxU?&ex^?hWN#Fcc{!aQ*w3+aAt8S zH71wNe!aW2zw9+?_ytGo=^yf`0m>Nec#%ysvb>UDt!id%JZ9ZXC!g?<+wp$rwc``y z7-PS~+-7DnHGvgn(!;PT+X=?3p~NhCsSdNXbX{V9JVk=b{T|UO%Fv5OO|kI#>0B`C zp-wa{8CVWoAKL-4n;QhvB!ynI2)#NHY!p-e$IkxIm!j3jcYagISu%53-E&Qd=~t!Y z2O2T1Yz0vobCnika;YLki~o5G-;+7_%t2A_Ie{XK|JN1^zv=c&j>oQ|pG#Y0y`}C1 zx84b#y;x{`+b-ES_&P`f6Qhw!9-(f{S{AzkVO+gFDcto#pR|}5pE5kt7d8KkVlt7q zt~Ae77ipwrJMfp{6i{X}vN&&fq~Sh)DU!ipJf{DqN7rk-MN)p?HG5k5-P3Gx!E9qs zzQU+X&1olrm6W2fh{n&|En2YXDIZMpmU2it*PYip;yePL{DXEgFF)9b@6<+_y&Ri#~M~_gKGT3&RT_NXWZeCguBO6iV-Pw^+=RE;Xe*Gzj@< z8Y0{Edq?g}u#W;$M@fwndIRD@s|HQ5Q%Ls<7pgJP+aR_9*_&VOwgKV#m+(mdW403b37{u71-kg#4j3vXy1*=1VN_c2D=use^!-qVvO zgm!G+>L}^RX{-wAt|~BS8*h=13%PE`C=+1xZEY8u%QWoj%Zjr>g>N>lmJbwrUljef zG}X)W2Zq|8`&W^EjivPg)cx5Zm8+~aucE>Lpb$9EJ^((6x&0IO@mIg-ABBj6Cwv#U z;r~E1K4_L3N=G@6%>)Q-!rr1*rK<+qXe_CR`o$Ld#Re5%1Ry&c%h80T-&bX6QspvH zgi&J=K4`j|>6El8S4bZteLo|iZ}#J#8(YThdK_nrd7eL-i9CUU9XsaRV@uj|OIkIP zt+sPuSur#Gg*zS0qmQHM9p~#SjXT9X9bHlXbsbp2UhT^hOj|k6oanwE-%4Ie2qyEDQ5F>9WF@+#}p(eJVCKdvON#i&?RQaU)gUxKX z7JzKhYB$YGrXk$;AxJO}O(%`J^&G>rk0mvHcME{KWs9b@y$<&3V?;)>l&kVIVL9kQ zH%u9p7@S}|2p!WhP@}_Y!{<4tqgusw`?GknkGauM5}1tZM4P!m$ZQPo7lv3*lh=ZirIUuibuq23qDQx` zWE(B|XrzfLFIQ0{R7v+Bl_Ly45LEGhOO;E`QFcxKR8KK=9m2D%BC_|z_T)uk2NF+z z6zyNQyY*T6exGm1xs8BoFkmcHZ~Ru826EMx(Zfl+O3#>AGo(Haa!R9^AF-&~YgAf( z`_F;PB0b^~$BUL5Y5SK$M;Y|9Y03dc7AI*+x+7Hf)L>Y<$pXtqOIHafPvA|FsW4E29XZ_Y|;C$ID*y}H}atvzx(NJeFN$2 z>?vff{ltGv>>jm-Xr$b(YD2IBE;Yx^{g1xhy7{89_sf2 zQ^L*lb7nnaM-+0a3Acd|C0^Gu3eA6bAYB}D#sI0bIsgckL0#U1QyK%_=a_lbY|`Hf zlbKkeNf_G5FlZeeyvda12&yo}0*ev^*QhBlNrk(_4nnC%T?!G3nJgjYO1O3b z!D!`(?WNEEy~jd@e2_QubGho$3|*9%!`o=N5D=C07RC;eU|2c?p^Oo9=wzkhUL_Hn z{W{K@9R>MZ!Q!x+Q;E;q7#6^nlUur!ybRl&FLMvvi!rly`Xo|mop*zWQ{s~{mVRVg zK%&xNpW{lu@_g5%a_`M2g9e?iyV12$$^ovGPj1}@S3g>iHZp2l8Nt=(t7JW_?&H6W zvv{0Rvafl`krn&qV`;Y!eR}Gj?Ug7V;m*||DQp~rCUg~{=|aRnbD|1$VyC>x`=9Hu za}B~1-=_S1&y+co&>5+-pkH=iMg-WUXiIji5TAV~Rz%8#Wppk-jX^|jcc2QbKv9IV-}dE>L9L4=-^4T1_rM}j zl4^$|;n%$SzO{)27$C2s?_Gm8?h|0Pl(8MIN9v7RV*5IME?wzVD|Q{dxVo%{7N2|I z17EgX;Y9Zgcjk3sow~~ zo@)g)Us-oJ!rs&7VUnb=iS#e?-V5b^46zo0!`x71OrB3QQ7$y(5f+0C?7YthOV&as z1v6O7kCk|(ZcPw7c=2;-X_0SVQ3Ai@>byrod9p`~)ED?z1dlun6|ZL?TyXh!e3^Qn zp9S;G?{5~fw79f#VqW=&EQTb~7_oqIqT_70m zQ&jVr8Pp7v+e92Z3rtMp776(eV%!-V=i0ZVJ5$dgbUp=8+OAL4r3~(6H%AS8`+cP; z**6`9B}IDGa{p2M@bXx!TW;BU^xv9yYlmdLVGPTo1Fsi7o4~c;p1OtMbG@{^!Tr$W z(l5LEuR&Z#BBY(p?M87(597occ(8n)QQWi`t1XhunB-dcSJ<#*gwbN2v=i}a{1n{E z(qy&1RDG?f*(W=CNE8eKv5$E6pQVhzJVK`XTqOG{#w?W|`E;Ib9d+Ls{t=ok^!{bs ziw_wj-YIqpD*={_@02-`!puGD;JkqutVgJNr6Vkri>*jSGdfebNvKEr#@0TMsWr>m zph9h-bWHuSz5T0%#k+>HLR&H`-ZF?kBY?G5NJ%~vVkMvEkGXoC#>jiVY@b9OLT&EN)&loHej_=5!yxK)_Y3&)9Sn1YruG&3S-Alt8GQ`u+QJ> zUsPr(xq|MB^c|!p3jd^}v>>RlaIC~gdh}hBxoYDE-ZhcPF4%ow|8@0kZ2kBoZuujR zK)9?Cxra3*!{mMll2SEjcVI5g&#VJ zTkQ%u^{9k~a?&Z*?@&It(NDLDS<+gOr22@-l5T#ugLdu3eNe%tF3L^Ph7Yd2x&|iu z;Lw5nCbDL$Ch{>!7j_q+Dd9SLv7HS8`-y7s;W;f zEO#=pNwaJarMsxiCQ^L)NLxrha9Fb(ci$%@J$BrSuJ zO#k4R-bjMM)R*B=GlTUpgVxmJY3hyn$+VRkIstI5BR+k(7GB{aBG`n%ujvDhA=ScI zUI|(@5*+B*JL;%pJI;vTt>(HPiU4Z)xWQRSnqQMAv5*W=J|-#ScVeO#2H!neu+gKh z(ym-+zI77_uS#%i`f}_va~vFV97}LMgCsdOGd?|H^gRZBuPN4qml2xTOEO%ybhDA9 zZNDdJ{0-|O5?MH1DPIUjgKFyx5i}L{tOLTrL|wL1H`<+DmLenf!HoOcJ23d@IEWT< zck2DnWRI;aEQHAKD=84@ClJ~q5P2eil@yHi6HIIoO!kBI4^=J4za5|8T&kg<5{H>* zdNWGa0Jb5R`&931X-qvjM2xcIP|@`n9h$~DjJ_hIH1!Z;*nz#Lb|y_py1;MhM#k+} z-fA+ShK^XqgD67HHS-Z)t2()pB>l%km>&^EBPGG$FTv6(aqCoqOG=W@Us9-5Qd9~S zg5*|65ptLmx`Ydt(PAr4-q>rqtM8Ml-fuU22+q-L8!1Hyjp%P3k!{w%NDzE`+->Ty z@ciWVU3W~x4Mi#0^_&{`PQ63Yc#uD~JU8hlpw&c;n!Xv%MSd#TaH`NOrP$`L$TR~> zD(6(rDne&nqMw&LVy?Xo`${zjQRIEv6QY_rU^b^%!2S@mL%)c3Uv6z*EokuQ6JhbT zuRcb3iL6g{p8(JpN%D@_{zQoLFD8`tYbM-{*H+)US=CHJWD>mtbihQWytjik`>aYvvb$q}&FHj`AYGz0c`N>__uNMum~%wd11r z(TT!+?T4uoUi6c@BBO#Bt&UNx)#qclH`2oBPa7hm7vwr)B3fWNJprB?CEJXaRLvo3Uu1akS_>J9>DOhG`}nrGt5J_C_)oN$a8t)0>6;^rG6u zP}H_c?94ttdLy~>2I|&k(vW44GN^Hv((o=Faw-61<{oeHT+@6Me3zjnWB`0l{isBW z|N5?piH@l`*D16dPvx@8`1@uRe$yc2vNGqQuO+Da2kLthC>2U*vWe<#8`x;>wbWl;koMnEZ(s#eQ}ISFt4O__|z z%Z@%14=uK}`e;gDNw8v~g9_hhC;ONTu`o9b#kno%mKT{Do_v7_PbEU@+o7pMx}ZKr z!&Srvy+kC^?5~WCM5~0ipU2evlS>&hvLKkZ;k$vwyNak=`f;Z>jS&lw2b!;DerJdaQl|by(slZJq=Q)YlZg9N_=Ms-eiE;5+!y_Bz-Xq zE+tGMSjOLI5<(z@OSxd-mk+eZ} zOpgCAenNK{q(X^M9|CGK8|KrY5O40`OY6Bl$4}K-Ksob_>eMqb@MufcFjfY+^bqjS zK#xOXfHzi8a8p^;kJTe6#@%sx889gWZgGQwHtedav8C1J&QvUC(@liHos?Ecn z@XWiNl>8yaAA`Z}np0V5zQ~zbT^vx8`IMO{6bnQCIl>UhG zvDE0qNyr-AC%Yvf-07BQdI^2lC9AQSlk)I&)G7X&x`o%FE1O~d9#&30vg3vvOP7?F z*aM(oR;7I_ulSEhHA^5p!q9XR{U?cVJhSB_k!k}^k&UM4#ur}WDVAl(y?{(%Kze6@ zz$*gP2O|GBQweulGQ&Pb8!Yt(gIf-sl(!37iARu@kf-9wD~U-tfG6Clh}X;p>z{9Q z%oLNCa1IvRwh*!t=I3`qpZR0psRRUd*R%Cs@O?Z@+_?wrLUHw6Ln}Y~l@zyVv)H#x zt~3Qu<trKO0NERhWZ$#aoopETw^=a$jXN4%-uXs@6_&?b@tug! zeOw{*l7h7_;fX>u-M%9^bQ3@`6abdw+0!?@?zt$!H)}JiEuHT=FTG*~d z^a_6*U{l2Cc7P`p8|>W+47vVhZW9cErB8bRgJ9W2s$xKqH*F?QDAfYs`Ry+#ZzSS3 zo+^%6$}_A&Aha{G8}ss{b4M1d=-Var)2Q$zoBZ(GFOQ9?*{FS~d+w6b(fJj$zBS!Q z=axDa%_)?d85#C`#QUE@X4A2!<_rm_BL4HD(r&nZ8iCZ7`2N2~h}`$2#kLbmM0hry zVxEO=5{(EU7Wxxk24gZB2v3&?q^w^^x0L#y0GR^%7}E)qYj*}@*d?0V`GoBv*h<=i znI!M`yoqFriVULsUD)DeWfO@;K$$B&C8O45DrCM)7?=z_md;LD8j>O#?lTldOyB3I zse75h4BRH;oUTc`O?7R3*(U8)duWIg$t0;|{q%|5vpifc=~n&MtLjVCmm+>6&6&K- zo}j9;Ai9V8?-r8pvc38G3%XCAylx%bCKpl^0`odEQ&cR0aWOEnBF@n>qHHBKW(d|* zHB!|oYgR>vh;I_7QK5G{ybEVcm*Djb`PWwtL|LM)*Z=)Ud?|H3yPn3~mBWTGs(-NDi2jwP?0JEs5IGKI?nGboOIqNR z7ewHEF|iM0j4MR+NMi00+RsO?aW?<`n2c?t#!^Cp z&Y6i0)=r={;X;ck4;%yzhBBEev#oXc3ox__j0Mf7+?5D6et$P~DD>UXUk)L_ldiw^ zOy(UT>|ja5U;a+P!nO`ltiR5t?f+=-$o{LB6#hJ_dGFNhUZ5zC|2H)WgskhH!3dO1U%PZA}1HS^qC8(iZItXXn(8AF-ID@d@zwHr`KYC~kYt{E?xAY2R>sVifl@HVLJV-qv_66MFRX^4}{*UtK&6eN!zqlM! z`~K4L#;2E0S%QE3INRrmo1|v*`+9M($5WX0jrZ@>@6)9;xo_cDf3=YIy%h{rW&?8p zbU~E*=GXTEX!U>D#xfdL6zMI8lH^)A<_Fp&a2LN=!H~2+W%J6Kg0du+=Qu7#RrYP$ zd&;o#uct^%=Q*S)XVUkcA_E_4Rc&^;z8BGsJ(3g{>vn zMAeujYNUo{Kp3qsIHzZ(K7Ay)MP`%vjM-fl5I?tbCKAtH9cB{0>G>Ch0b(C2Ut$VE zfvP@>`GUofKSHZXSOmKli<+CEJ5LgfEvsOh7DU1P(&C%_k}jnHU9_p$nN{8EUQ+k^ zmT_J8hV~V=I%~owHLWz3n>GAi9GPbqldGX<%dUO9XV15h?;daWrri(Fds|Bf zy}!;Bf51-cO}x9msA(RxLFpb7GyW^eLDuqBce|M=E+auuau1B^Fn88l)e%95CcjT2 z|LyyY-guk+$E3eKmmKDo&d9fKS*0h5x8jATJ&ql?y*n* zg27Ts$DGCCSm25PK~(l@3>nA0HwhgFcLuSF`sLH7w_Iv{s{NMf0_8PA?olXQMqIN~ zG0PnR$XJ_-(IEEs#XvHcoGk#-eK;O|*n3SGanw&m1tfv~0RZ=;k0PR}4CLP541Xxb zHmBy}^WJQA=Zjv<`}TzX-{sZfFz!O4Fr8I>|J8NV z{P&OO-p;u+FY`~|RL`|KeqA0$0Lx$fHN-GO%pPEm>7IZ^QRp|SlidFI@kwvYIg|N@ z{mFzW^|SqQ>TbrVGhcvgsXj513*`X@3%DKiI}Dshv48*{FexoHgfyKCE_H#WLvlqU zsr$iHfRadB>R1Enejp_hNTnYfBdXK~lN-hUe6vv}X6^mlbGVr9d$elxwbLXCSG6`o zLA!$v{Q-Os&Kfb&@!@S74VR$YE3)aI=_JL6sh~gKIC!Q%o>qvbGw9j2v%CCvkCiuL zEo*)8&&9iy4H~!1YyjW7oR%I=l$&YD77EPkk`jyaXI7m1uD75hw_ryaBnW0}H@yE? zwv^mSP@UntDOPeHOVJ;J+o|r4ra+?s0dgAr%RWguomk3zK@F+9sxXs5d}6It8n_S- z6ez?enJbl0MId8%lz=d^{-ikE{O!`eNzZ>;CA0L%(>^zO05#2WBvFx9W$>Ly`!-$0 zc_$>}+3zV5P!4iyw9Qb*7=U;4*j6K}#m1H*(-{^vW*B#hlcAE=WpU(=tP>wW$;Adx zl6M4^kMlYaCviEVzD@|XfM|h*K3GsS?$*NOL%cqgBAnUzR@9e=iuC=IvW_{D2z6(U?Wp;)n5`kg34Lvet{C*^^p zeDdmJm}K>n4Pi+mJ=r&@fo!hoz;I9>w8e+&o%X?>1)ZcY@1`i;a(=3jsX<|5Pv^(g zPCBf^qrZqKR2I|ukJ<0Ox?Q#7|*ykmiU;T%! zk{8%H?Y$4iNfUwbSRR32=jbzQS!|3_LjJ3H9n~$0bU2OF%TLR_0Iz&Z0RJZce_NWQ zR;gaTUgfK5ZCi-clN&;b|AGqfotQtxGs_jL>l$_TrIfMr0OeCepo;Y^4uUPoZNnQh z0v^Pg9R^`HRISlz*tKXx&%Y^dYj985d%UOc`Rz*E>!)Ae)*9xA`M;C2ZnfHR2+V)A zpg^uUKZI);PMM5WKz{XF_n<&OUIYhO!e^fz)Y`F0EEXd>PCsb+G*0weKelS`IM^o> zl>3@ivbeDPz$?T(IdkQ8yXyG-=2c(A^s|y3UCDFZ;y@9_?-AqjoRr52_aoNm8#~{N zr}`$oV7ll1)ifvB^M*q8_)|ePwnGKpOoa!K{OTtsq3=K8YPbzzf%lClWBg()h+y(d z0)gk`B!_IcmuZ?1Pu*Fg}Z|N7vYH2fEzZzvw7bT+Q;v>IM;{T{s6efTx1k2Gm`BG zxd%VOjxQaRR$A&c|`v<}I?6>)|>yIKo1jK(ny?*xhdlY1W`|RVhf)-5TcLAzS*27hn#ivN` zU>{ixIut5+6^7%grYmi~6;>6t^*wm|PWS_J(Py*H>At{aZfcVk)N7?8h9AY&C4=&O z#YkFF40xLJ6C50fQV0pSNvH2=>q>S;i{emsHC_VES-QXCspqL;&MxZZ7dO_sO z5A--MdZ$0Cg~`6ONY~x$NLJ#`@?VXqv$v~f;Z!F+GV&`S}fjXvWxSw z7tS=#-)k=2abeVSP5`>b~$vd^mu*Ue++a-j_iYJ~r-F=$Cg%MBOmc1wVlyUwk z5SlRl;ZEGcMEc3Zo_*%VChx2^0?%#>g1lGhsK%CXLwZfn&bhCE_Vr6~Lfq}EmL6Y9( z)z0&Bav^3qoa+8B1MFywQXLpmPqv?g^Q)Xn8GU_-X4ckatqy0ukmt2chb5*XYSPJu z(A84QWSBZ3j5F4E)Gx-)XAUW2JYCNy-&N85C|kWEd7IF&-aR8Aec zyr0^%n&?dFB-2ye&V{taK#HJ_U0}8`|E7`0hGvSKbFwCj+@%)Pma-{bc+^lN#5J9b zj?O4C-Pkq9BsAw?O^#9ib)5B1&J#qgolvfWPOj5V4zo@aYgb03w$Hm}gS8Kjgm*w2 z>lwf{i$4J>@q?_YJD^;KOnwB7_9Kg^INNXAnX%b+Kex4*&f|-nEXv-ia%&avj0N)@<=4s=l-7W1x7wo-Or=hSc;?T8(zb->?t{|U*_olOt0qdecsF+uB zH#aB%_^Jl9V{#4BLPCV`5J17JD_0Oo8l;Pa@HgRswXgEGUb57duob){ApH8}xd(UD zXNJt9G_uxp?lij;=agq(JGAiIjf%s4$BLxujQ3VeGWUN~uVN_x0R>2DZJ>IRb?FRiBE_s?% z>AIN1F%pbSQug?gdqwv|c1M%AV=J&zx{#M(2sUQcEKWDFBXxWl|D`C}q-rPRF3wkn z4oc%4r*dTB^=vUqw$}f`B4uk^^Lj&ChZP(KPH&Y&#Ymv?+NWHX2AzPt#GU6;Bxo2&wQH3qc7r~t^8JN(Q&ywvse^+&cf;#I|ft-_?ADGPW1rodf@V+I8 z9S_OGz~lg+hsiLPBZ#L@yM;T6H45ge2vhTEQ)+5cnS{DbLZA6`Kg_T7zWsK9t;6p= zx6f`o=MiwZJOZp1DJ>Z}UdJvGl#=ogY3lqWw1_6&bh zwSrksLU)saHpx(34Acz;+rJI-NB`$<*cO6^4plR#O|}o*Cb(K&zy0u-BS5hf1WG3x zB_MwqP_U<$XeB?g0|}$)DxA~hd?3H`Uja$MWj7Chfi4P!LgImzV9p4LB6Tk*D*;SC zz-mc=DsjN;b(us9p(91 z5^CfTa)tH$8XmFh)^Ar^VKd_PkhPK>05Y_!aH?Qmilz=snKr-h*6J>42-JY2Vgyc=}&2(%*&x?PXwsRzmXKwt_b42$?biFk3P zkE>R>fgT@yuy20^0?JbHmaa=mE+n*ZN1SyytFSMO620&SE2)OjT zp)9ggC@6{OXOc(=f_exsuRKueAHFM_b7A2!Q$?iH$+JsPlb1PgKt0R*ES`{>eyWn1 zqgC8py`I3bpODXBHj@)K%EOq`V3}i5lVglnTwm?Okp3qr5?DnVp%kwN@i>#G|_-)mgl{{FLR1j9Q0ZRw$Iq#Z^{* z3}hch4s=z~HoHF8@Y>Ky-Ozcxalbejg_N|-&IO7QcU66UimW2M=HsX*bRqzH$W_u_ zgmr@=^N%7bAdrmu^Q|AN5KoezTjjxKDrB3WH2N9`v;4BNTjXZO5pVKHNQysSY`s6I zayT!3mUuTg&;nj!D+wsd{~Oj-6;43_UOm{!@!ENjx?`3EM4tG13U5Beeso*|+#}4{ z_0G|a69tX{R=wAdC;F?!NGLsWufQm6A(F;L?6bSgqyceDtIJT+6}WVxy*JH!7Fxuj zpT*Zzo)TIdVv;BS&nULnFqhs~DOKB$&Ih9(j^AYuNKXVPqnGIZ?dmnnx7cJY76Z)E_c)_q zJjk^$6=%Wt>{*WOxm(Usx>=Je4VL%HQN>SENXw|{vZWK(82?)%FL7Bi>-sV z{kO?&e+4(v20i%|Ht{Ru@K>P0FIw_baq-jWZ;L-)e_`+;aXeTmwuKXvw`lP!0s!c* z{UzMd$GcBd01RcY3;;jzC&?{%C<+QcI@&5t{2RyeZ&}7GI`P`9KFLBB&3v^j<)i2C zDX--pUfYnr@H^h(OAV*zD?4wShqC|G%JC#_`egSA5ftc|S zW^@w;2CPN^uobrb3WJ=6-K_s1a@7BX)7!WwU$GtiB}d+D7Ej}zjK_Lk+ei>4D_F`X zsVyPbiCvOl5QLPTS6h1Lqf%Gwa_$4#>*`2 zf(L-(-aqG)TA4(iY>Z@yN;x-ioj83`L%Q=@FaC6$tubwM{<6%8Vn=Y>5l1NGe7k` zUY~mTqCi4<{CttbUHood@1INgg@c@t>4Zw=@y}42J4VmCS zNyRez0@(GC4_j7TviF6r0%PfUAc`;9<^Fi82GyqO&>^_SoPBTcrzpHuzz zYO4m?@pUa~!_96Jp}<_Kd4;5#zBmm;tQV5WlD=9Q(0&7#*6Wf$R{ zWL(|p7gUeL9nmrNQu7PrOb)aD>+)$iFZv*o0Q*EPa<}0+h)5G~lcPLPM-U-)L;#b0 zD9snA5}SNP$nzRU+IWag5~54KNwK=V+j?vnUAd=V3GUviLxQQQIqvYjY-2PKe(C$~ zQB8sO`Q;uyoS)UDSlr@DfowxJY=eF0AO&hI(Ns!74RrxiUCTpln5m#X+x>hm9IJG)W;)AY!)0LOKV+zrw|iDe^+j(u_mz&4LA%2Xw| zSXXyGx=FPe4XZ7M@&mqq{Z(gAsv5$WdAk?49(?$hzu+#qSc`bmGz?)1DV&I=3;JGE zwB23s<0bd+pD0fAzV# z8KIBe7XGpc!e^|7xSvp>m86TIkT$x3 zOtd-7U`F&IAD7`8tu+x?t&T~y{$tPlap65x%oNx$KouU-@n`{}w6-~MDk|(4_q6lP^->c4V zfZNu4!tInlBn^-%nZD1{k0!H5v@k^d$dRG7=IaQCBFD$$?z>Ew&jSY8*3uC~BYK{7 zW}LES=JQbcN!Y@Wx`q=Z)sffHM4F(v@;5cj!x6~PF|2t591XXIIGC)&j`=02r<`-TWv{+A;Wqy;;5BCDS<_k0iPMF4XSceK zV~{sX^YUsojU-}M8J~MrpP)BlJglvQz!R%ekmYE+Ts59Tc-#!gRf(uNlL zXy_ilUq(uVBd@@t;rq^hSw(*w1$kKR@NVacTePt0 z(Bde*9>+lQc(xt&wf1Ydt`8CJey?=LH==B7dzMgv%|ahHY@`N<8TJyf9nQq}p}}*b zL-JiU5AHnvJg)nv@75lvZ3Y?*;R(^JQvT~|7;iaS#vfSoAkxjG%5tvG`K(4JDLoT+ zK2pHqc~{jQD6!E}aSuMBcJmdX08)W6R{g+wo;jAPg@JI#W_A+ZZ8^zcfu~~*vtd_T z8HOEndO;>Xo_|!i3jLysZRdQI@{V(ekot4*O(>Gh=td3w?P@Fd zzrTOIF)%A4_(uIs5p{&|MjMF~%6R-sc7=@+sL4KX@#PtcBIJmW|H$nr+R(h@$vr7J zU$3@CV?WKOGO5=>RbHIm^M{_h5H{~ae)Sy64Wn#qi*!7M&D2%2h<~jF0lW^E`W&87 zy7-N=`@Xd6${HNM{Iwreh0QfY1=c^c-h2E#?DM;SflcwZ_Ve5Q2Cjbkhzejx%uzA? zP_6_nWQ-;El-!bhbE1@poi;j?RLf#g@n~xFaQF&ySsse}kBx6fLM!5=&{+BB-=hCf zcb-p8yk8&QR6-9u^w2wmUIa|&U8IBbCeox!H=!ANl_n*0q!|#Ari3aYDi#zJ5%>ZE z0#ZZ~3r~K3#>M^8N}2@P_4NEQzv(BQ$^6JD zo;vzZza{q{=MiQ$jOKx+t(po>8+QG2@CiB z9ol^cXT1FP=dZ}^gO9hs|D8XEmj5Ks5C=c=V!^W59E9#_gKDpg?$>U7R=I9QD?SyW zEF4~X5>!U7ofGZF#W+DZB$Y7EG9$lZf1>zVWk;EyqmqhP&R}Zm7A@2QtKp2bU=u07 zz@C1ysHn(?N&;0>A63{$0R{UjpiH%dbq_K66~!EWd0fSJTzz=tLQaX+gudoF zR>O5fcb0iATu(;UK>tsNh#f_=SB8d+7XwmN&yf&u1PIDTx9}=O)d?a;CMb8%w;>4m ztpc>3bOU6am)Z1p`b!_*p|zfPjF8N3?#FeU8ZcUq6yUSlPI+pGllK;hHGtt2zN`w# z$$L+;n)@?%4hD>$rZL18I*y#1N4FW~=WG%3A9gRe`RAcKjV{)1?5rW3x1C*G= zNomGgQ^w?ecU!ujhxMXo4H)6U3p4cx6$4N2j7c2Tf5L&BwQD5D>T6PI8MkUs8q;d= zytxJKqGrMyaUx}G(`z`v*gw)A>1pNZrrG*czK+(2Ml@hv_hrr2u7ON?d8Ut@+wvzK z2L?aYA2wQjHU-YY(;y*+WN4LvvEexpl4Z>fGB>WWcw+37Vy0d1SESam*4!9x82DB5 zE>g4DmWs1%4=S!d8bh>J$={ZFC{iRrwLwSzS8XsRuQoWddSn(?G9zDacK>LmJ<7B{ z@oA5_K}91kH+Uv`!qnUvDu)3oXZn#u$S~A6j4urt#$P_C98A}O;8kftYJPbo&iS#r zwMa1u{7k1^Gv&{HW{A9+_x*__0{RDYG_=Ty`C z?dEZ-dYFFGu!*|s48{nIaOs)l*dHTvD&7d8@M2b(qd~FDKu!N^TU{l!@@#&A?RipQ z?MJgVllwF4m9xPSR;`sP3FqDq_ZYHxBL%)STK;I%`xvaTWWf2K6J+?iWmrzCN`l=M+wBHM%bTa*(ru1LwW4<=yBMZ*y zZoI`S4!Jkr;8!vUugs+ILdY4Ov**SD1TsyVo1JAH}m{*6{gnQ?wu|II%e zTa)e@i1Z+McR;%Gp`=Dweq^{lAK^QBqC5aoNmnrt$!O`7`H z(=BlPZ~Y?7XxxO|qg$?DF=J|K(@|>kimpdk&mKk@#}+@-F0#DL|K3<={*RKq3GT5n z#oQ2;Ce+#)nu?cMFt1BYlR;qb*b0{hsi+s55Vb`clu5~Art?G6H#rKvYKn>nTWa?h z-N zee|)k`k6@DV!0TeEJizFL9bku2i=i0Jc0&1{#NU2 zmps{?T>d)0T)eg{b&Z=iHz8z#r`Tsf$_Zjfh-sso+grLjl*iD1JnS8KJ<)T8kc}jM z_|uPPMi3bO@z9~^*x^~;>|Ry4J8l-MznoS*qP$vIyjVAxYp`Ce+lI}g51p&@?0Kty z@P<5A*&LyCeBz!G?nnry6f}l4+CEL+8Y;_~$|gB;!k>cEfMRr^*^wS~0X33q!|m4Z zF^|3FHobS0OuoEw0$%f{8Tc$|c8OLFH{KUWo%PXF`_FI)XPW7xBQLPO`XooLMsw3R z<(vbVe^Q^Q;;Z$;#}K~0%i>%8`z}7o+lnbqlIgCLf033URCeD;#yyq9NLG8~EjzWQ z?+HFkpVR2E%W1WB2;I=fZs-(gcCFjy@SE#rsnxA)gx+@Wn16R8{F&FjZRGKcAEW3) zhK9f<3*`v^ka;z~ecurIoJ%!L*6u~%{*)_fezNWn(E#N^^1715u**D(O2Vjp$R`WeBLqVI$U*iPt}~XdB?Y9{98^s07XbbWfP-0dkp z&D<@cBf;F_!1$OAjRH4+u`L$;_PxQm0J$JFvxnaHHo{`vqN?xG7i>B^W}Xe(S={z( zV!sr3T=SpXPUilmX;LBpcj!MBIne)o1 ztDWLLwAjaw5`zr`R-VFNUQDs4LF_-#g<^<)!w&Vv-XVcKYSSGyFQ0XJOzwX<2iv99 zs44ulx>v6b;lJKoWhJB2Efpnm=@Y-wd z38QJuJ@}{7OB$2hB92}gHzgbuEUAePnpFHg>_v4O#Q26k6OnIOD3tN}f?LE~3J-A) zXAe9aES%RMy;lxc;kh*r)c{GOpt8+S#d3n9Vb1q!kJjSwVn~bY9mmk~{>aEm+LO2| zKUoC!SeQq6YuZ#FHLQIYiWeLX(}8eDUErkK)NFg28fE?PN+S5g{7bFjM;!u3jg>;p zyz=2K-($6h0l^bn6|w+5kRn7Tz?`D)#R4>FJomu~HxfXK^EOo6`K5tpAQL=1@THr~ z^yN%PG6nuE7a5)bj2gTlPk#LS_~UmmaQ*s7*h_ryOXdi5=4}$sX=Hwo zPTqdH;#T{#f3@57(`mTs@{)KQ$H~vc@J^gd+7}5l)A9dW z@r(T<;5m)1pYg$<1l8{&HzXuhNWUfzN3#N*1 zo|WdrI}QG_jm8UUKzOwv;$hf(K)#q0B+B=XQ^-LpYVuX~$&-A%7)8u%dMHQv!=ba0 zeHGo3GTj4?q$lQo>y9tHb@Y0F$*_I3W|?JeQ7~x$c4fjR+nY~kx%T7XUD{REnw1;T z#xD~8<4XJ__9wR@9v%*0uxWFg`t!njhrK6?oTNyBTl5^A+NSuh|*J_m9tyg9;Gm;GRV_DGm zWRY_zCi$fyS=BR03BYVUIe9fp6RVy!GZd%uPEPg^@_gb3eav^0)9&nP&dZW&0nU19 zblg(c_y7D-G%e=9*vjwCMf!7zY`Y-h$|hk87{NE^%?YbNc0XLX31OB`)Hqz5;uf%U ziPk*cd{N>4Jbt((mAm9|27h*vNlM1N;G<|>&RuH#O75UW=e`%1H*4&nVIzVFx-}#< z)QA6}>&JPtG1^aXnz=4gpYK%FVS4d=KQ zljV9I@LYNVVk$bKo_CQE4J&wzg zDCon;vBHtKCKr(ouvt)?i%E5UWrR}A48d~uwHBVMV$JXTGP*i%u;9RFo6Q7>izsjC zx$1ZSo3Sy?UwjY_Z=K_}L2V6nR20BgmeTo$h1Le!)z+;%n=G1jp==@!Yp8t|yWSLp zBvT9nbk)}Kg5u8J3ELs8M_b{8o`L1AMFAv+p0i&I<>X2!LiJ!@NxOk;uM`M2X4dEe zWp8|c#kA%5Q;GP-L^WkT;iLHkJig@qVS7k{!T**dMY~<-!(Q*!* zDAIl{W-wfI945M6#t%i0(uotj+kW|#4RgARyG(O_cc~^%JzWgww@(P}9d%DEyviNv zfQ!F49d@N|rO37C>6=XRt{Jt;I{{#2{Fir5{|WV^f%f}od}BlBW$~?5hh_fyG(h1_ z?5&cYkxvQd(0>M zW8Xkya;ZiWo3Rg%o_(zTx%^T;%l-(XRWQmOocnMZt;V-<8T{`435V;)TY)ZL<)_K2 zmI_Z;{yAn(c zXqSlezE2wA@|y$!$_kDu_R7Sc%lh8&5}-AAQu=~ZNVnR)^Ni}Iz*+0Dlpx0$5*>G_a5ch~6nI1-bN6_~tW!BkGihvtETBD?dkO$4(&T8;sD-So~+DC z#-=oX!CS^CK}f@1AsuMBGFQH)hzYCja|G-^XH@TcHh7^cfhblq4|#{%p3&fSfE*l zMcOL+!)12t^HwwaHme(LJvBUS@|m-nQJBHE?E(_u)FnxWp_$=kt!P*4=vNo%t?^9k z6p-qpOvr4Y15N+43Jz*sz2t)T<2WWcST?)gsDGdGZ~h+Pd60+5pyOkKQc`C0v?uM* zLWVXPDiC{4@p%-uxIs|u$m5a8=EJH;6G03X)7YP!PIzut9D{d%3gf<+Ib8snD&fI~ z6qQl4Vh}@}+ZdS1vo6?$0_i!yvff86t%`FfvLm~KO^b$I0#_j8A)8zk=B^$Bjy+FR zX@mWEQ$D;t5?ww4qH}{j?R9MqKQ6%r-}(LA#T|f276ivB1bx0I>}OP&E^xKQRtH1z zV*v|HAh-OAAhI|XFeh2Ke6CtHr7^V&SMT5+^9VMcpWs{q@HK{F}>AK?o^Gd@%o-egoR}Pxa^lE+dGHFp$VIz3N@GNmFgFE1JITtRITa+^E z%kxfv+k~5*ZX$~2S5?F@7liAM?~4y@$L9MCSQXnYaXpn+ee#W%3HCv|KgY-CfhlTf z(72gOap&8efB0m%Xhu=VpYiEu)oS#o$j;n$C~6az&5jOh&C7oI?>6Yq0<#?N*GmOg zqs!)^Ld4o30_jByEzPiaOUl#<`TEb6wS6A-g6|IuO8M&zu4{c<+39WhC9YG%xiSV8 zSG}^uwn>$7&>c;LABaDBf+JmFk-$q)puXZA+yPPVj^@T|N-=eA27%&5x9W2?~dg$%`-P!Zf3v)V;f$YM(A` z!w-AO!=i6Ja1R`Vfjl)^xk1Oi$H3kle_?x*UCFeurMcVVRh$=73s1*F>4m&ZBjLpl zh(d+X6HY(Z|Lj)#!~W48h3Ci7Hr)n5R=Qs`X0KnfJl}ifnHN}biDi5K-@`_?p5{LX zuKVy`1MM=zQo?1UH9=Z|<$T?igdr{Xz44zZb2ee^FzFAnK!usN^+~&rZuPJEvzdMY zKFh2j1t0iWk$C#PVZdkx_L+L0n6-}C#BXT0F5iRV$*8IeH3ywnO~&qtZw;L-w$oEo9&mXIwEzYYnZJHF-seumo-8mwGY}_76Sbq zrU5-hCp=W9?d2Ni``c>T_ddgAX%uVoM}ND#{jD6*yT|k|eVPn=?gsGaJ252x5j>+s zFa5z2_i9c|SHh-=V5b$Bwr^6TdiPv)y(wUe!0rGn#)>HrQpuD*5CG#KW zlDJg`rpe1401Y1C<@rxh*heXE713YUxWtCE9_8s6WlaM_@?JAwM!7pcL5geGXrqKA zipBagWMUCX9OWhwo+By0=ZkS}_$X6{@Y^hrNkF`t!HuV-8X1moN&|elVUN;4v*7c@ zi7QqPshfd^4|65NmBHE9;#wW${kTFp9TUXX6LcodX-L#oeJ)JA?`1nQB5U2G$k)`J zgowBut_^@hDH4j_7vh;g(~ST-g+Ig5YjHxG1rO}{TGqd{e8J&u@1V!%=kwIwTAI^y zb^WsRhD~#kt*+l?`H6ptzV^v)H~ngzl6W5m%)_?WELO4e`Ms~6a{0C4_s_u1RlvXA?>4Q!P>G(D=or1MxGfsbgu>B@g02O6&+Y$X8}&(Vmk z=43`Z7YQuQK9*LW!tWXonn${nX^WV4gnO>Ta^7R*)54yY>3ef&+cYXqzDtZ2mFwI} z;VSFpKQwHLI%<)&XKFJCV&esl=36wb9J_ZE1_E#4a!|1J*aNHvaLn=~f<~I`s`_ z#4!bBS91SUkx?^X)V%1-unYyXa!lD*%n~ivS6;+n>W1lRS>?_Dk!iu5?F=zY%ty>L zolyH^lR567^DCSZ_Gmm8dkPa2m$`E-O$m6Sg<~_p32ESbrPI0QKzt^InJj>hT~A0w zPiSG3S!1W+$HsNZ-62-~qiWMEl`98ftLYt<#oM3SO-&}A`n=40A;=nHGrwH3m_e|( zbMUvqQZyuBtLnWd7huyl!Wz=*9(-b70W-seI1pej{0976YSuPwB(V*g{x8%hOH7oH zn{tsBo7NK^6MyY}KYjAup8tFgQaYG9SBJGQ>K9}1V>$`C$Y@a-Tx0w3pL05tm;yn@)i4c}K5>}27 zW^Nde#xxdj^g4Bz1AA|E<+^oT78%?g@mN6qncCd8`X}e{SL`ED%Ch>`YWIc5lVCYF z+0X{=>^Drjhrp8HoscQDbZ`L>T)D1L>uN| z%ieePmiJDP_@D9B9O zQ_I4Eq@|kmoMAr*k)m@Qt;^f2a9_^098kQV2;p+e-dK~i64vJ8hJD(Bm;&dL4(=W7 z!bT?LK@)3mwW{(_+IJID7`t> zT+?2O2q#PCHT|jklh5xfWMg?6aY#=}kT(D)Vnf71EDM{qGFZCU(U8Y@E>S{|!%5H% z)^-FB9>ya~2rOGzI?Px4gKZnW?*nE^If%3<&Fx6V#zY;P!q)+1%{GFI4|D~5-4(8t ze|Vs$`@YQVuE9*S_Un_p7hH?1qGd5GMLhwzR@)Kb+j*vHWizk9^Qr~jj_%j{#H{*C zb`GqSZ_E2g>SUkTw-C7c)oc<7yp14EPePD6l`sk7UI4*SIG$nLIfn*_KbXMP`-3+Y z#Mww-pChpM%SNb!Y|b%PM801cSCyx2#aOa?`8`zTE-`dd*EijW`P>+5?B_>#75&!j_T$zqD^yAJe+co{+O7ISVmJ(O>>{r#B zxaudG${*rtim@oYh%~idm5_MSRRI$>LtEanI+6H#nX`K3vw8_k?Y{w%`B~#7Zb0Oq z$tS*ebgz9k{P=~lnJ#!mASvAbVn6_pjETF+Vx&sv7g+7s;jr6wzv_dfCXPs?wH zkL_Jy`WBwC68_`t(^-72pnv=LS+__6Enz+|=t0>_HI`uziye+<3*h#|LAHM0dMA!- z0s4Hf94q}?b`QW*w^?iS!0$+VA4-F<(k+SKL*qkrcn+`$j{9wlEe#=vZPZ@yp+M}Y zjd}_TyB>$rg$N%IpSA|)0w^M~g+Gjt@ve{GgmBJ}0(HM^>VA?i6*OEwo50%J$K?u} z^chVPxpBP(7t%}+NE{7*_+#_}C}bSO|NRC}7$~@yz(pD5H6eu82C&)woPs3H`M9>e z^n-}vKE!MGw*Z`t_@`~u8u4#>BrZe^m)EjKzeS;k;@*A5vx}bQ`GDSE7)59hm?q?k z&xhyQ|9%jB0zv+D72o`{8S*V7&g0pYJw^PwH!Px(9{M#8H zeDW7y1E}0w0xAWo_(4~3?cd(l zFG8^m=I7sSZ=R^1!Kv+tt^W=qFOt4>!+HVaEQM9&B8MypnUf1?y$B`W{5^F3d;I2~ zXXk(3-27Wxa&ztGzpv;2j&Ghr44?!EEvHH>(n~L$o?9*I6zOe{$tLB{8_VKjl*_A0 z4J;3FCNv`acG$!5DI~8suz=K0f69u+`I1h&BNe)(N4mK%Hc4gV9&=Zn27m~3MlC>0;wGt6g77z}9Y%QbaA*1dkqoE?J?ja|qt8h^bqhg9t zcU3e~RMNCna+p!kysUb`SIv7%L)TGDH&Dw}Njp?k$G}||Yp1Js9jj)5GrX*4;9 zp`WH>Xnxh$ILSCl*~HS<+``o&31?{+e#z4Pl2zs<`@l;MS1+ZhQa7761?sj=O?D1` z_E&D$`#IY;TyV56baD-Makzcuj)tpul85sh56=(}-xv?yc&{E^?<=+5S4iG|(cXUf zeu3!$;p^ADTCas~28N~vhv$TZPK07DuU~HryV?^Tc{4m=E4%_1KBgNH9v2al9o?lF zy=xqOWEvAsj9D|h@!KLcWGF7NFg_tQegT(|M7(*~>t@8%&E$-mv%2{73c?%Rv~IPG z*cZ2M6=pqD&l*tA9#GH8Z^(V5kzY_*5FAxpHeX!RQ)Y}St7xsf(^AHX&CcQhh*mY&{kd0C51rQp2ct8*Rgm%;7yy`v%b!c55xYOsWyMu z^=T;fc!&S;8e|^9W#MU!?I*8}6WL_kXZySVdr}}`USl!Pv+=B4IcRxy;Qp7FB>ijw z%Rv<{260*B5}=1meNW~_TT_6(y^sBoUzeW`J^c2Wl1eXVHQayr-*mBz$BW^C<1cd! z<`Oxi!IQ5G_k%va_@uSELWjYhqb>k3IXPyUyh8N zp965o6+DzPfE4+^-7ABJ%GOFM(nxYOja?Tp2Au41F4MUatieh5q_AKI_q%i05ITy4 zAhD0}z)0i*bQBvxNuuG0aPJuX7j$7i!{_?NWh0;b+0|B$a9UyPI>AD1d%f7&NXj+V zt+x)&?AmVkv^bS@r&0gPFjZx4 z4xP%OB^lB?rzg{y`c{t^()%`skWtj_Y}^1AVKIW?ZWtAu{fFxDNn-I6UNv>uqe}=w)^8!D^**h%z`yT)>={Fl!OPWb>qQWD&06% zOTJ(zTuMdAXT+wY=b1Eqwv|F}E>V~Uw%S6cAha6sz2Js9r5R>kq{9P5IBWZHP^nD(g!GnYHoLDkM`r9K;DDn8_sVkGn;3`_(Kz+t6sC5`Nv_I+Om$BJ zdo4mwOjjtKA=wLn1&ql|7i1_5gE)F@?b%)zz(tUR42RUzrzzG%E)x)w0p&LM+ykQSEnxX1P@)Y@qa%5QT}lX!R9N;dEkB*OZZBXNwm zz7;&+u8fyosiTkZoM1uKrCprHF%_MrD&59wX(K>vA=dsvUk+%D3d}*mPU-Acg|t{v zJ?6~v2?^`)EZZn+y6e+WP6rUJU7{XydzgU?Yj-+R@)~_R7KWbPK2Nt51#x_om5>%M zB(nGm(>iGw^5}Zqve?2iwV0%3J1FY7f0(7$nj<_X;|VG%Xc&T`_hxt{{hpW?fb&e3 zb&#fanB9Uo6@dTxx6&y1>d|Eeg5%FHbTxV2aF&&N{>Fe?vUoPo^{x8Wchhg1wH{pW zS@hY)5Hi(WaEyVTX?$~d8l%=#W=CmPez_mAd;yp8ql4EK4oR~jSKgp}Y-jrHBQq{|Y6#I~g+5jD!B$rjHx4!Lk_~W;p_V#t<@CB>5K& z%dAkP{n@z7N4%zyworNDZi8rjoFzkVJxdcF%6L(6vC zp}U;PPGsb`7BDIC+8WVS2d-V4HIU*F`j}D#MtY!OuTp>qp*4EU)E;&&wRF6w=sW^v zGR{lHr{7ie0-RRl*~jmzGyK7Gocg7604Lx(uQ0UN=DavwlTw9j=&~CS8|u+A0>{hhT&;VO}@6x8|d@e(0_RNuDc+UZLGW_?2(k@Dx$CiBCvS? zH4>FCNUzf4nyKtxR7BH<6Cu0-sSO_#(OF8W?ou17A`GZeUxji@2iIdKC;tGVL8~5H zC33fxfPg zr(wu>1^mclqi=~HvV(wR+mA|(s#x1 zURi~2Jz5#`xSEz-F(0tz2|@TxpES0pyy}#E3VR66dDss z+~&;@)7qam!PE}zX_4nwW!>-F;npM_r_|2~BBCo?tVq!2XyWr51XMYJr=P$_)n1$u z(5Mt)jTBL*6!F*;$?_EG{uJ4T6#3H>3@TMgBUPoGz=)QxQzWpW!6I|1x~HjFRGOYf znt@ZAQEZw?d74>&n#Dqz)oGeFD&0mS-Oef9AvWEqJl&-~-IeNbUq}@$V1=e0mFyBBp2%)0~JIu|!y5 zsGb{Tus}?eAPBTfUta^>CMyIUi*sRv_thWBF zj)knQ(=0M7yGJ9tcRCY_i$~zI2NysC)7e7{*_6}lv90V0RL-PE&Qy6e7XW&&kn`d+ zXAYJ7S|j(ZQ|`Oi+y#?dmN}5#Gzhzudl8YRE19QblIL`oyM@Zz*2u$2qL5h7A}aq# zBmcWo{*T!F)AIaxC=dtwCRL>wz$$pAQ2=!=fX5XeDhlWpGg;8#RvZFoN)EkdA-i)S zXIvqwq7WP&dr4IiO(ODe7YS%f$fy=aDTj$$0akuRAx4oT9ed9`fDoXwDmIe%zdJllIrVFh$iW^UhWrRS?2VBhPlElTb zmT~}p4WLDrr(FXCR-l&)V9qPhi&y}DP+qVNSXHp*nq(s>scA(Ol>-%3ixoA$DoEUw z^_rE9&Xvt^m93hU*N*a7(Pi2ZD7=1I1qvi#dgp;DhoJQxNhuJMCy40)Bz;J|_%ey@ zvZ=VLY!r2JA9{JN>cw`IO#wCZs&1X5`|+mA1_@h~f)r^I+JvZfbr8?_L9sV6bK@!T z%K$4U<)(o{>i(~qgYA@F9GHV#{MwQCYB9K1Jh6UFmDXD{?y3k#_|8*jkkTBO3k?yR z0}F(KB#}_T6)^t_NTA>jjsnAx%i*3-=M~UJ3`hwNPROhhohhSTfhHH!=M>Z@lk1bQ zbZ<4QThI+n)^v^54HX9vvw^D1b0DJ?s8sl6^cgUu?cxX_IdFY0Fv6hn(jx5p_*I(|8=NaDVYI7C_?W*d;i!fjTdSyC-l zm=Hl^uDg%D%~=CPs=5r_Rwh3ThXcBjDI?P!evni$f>7%cUy7IIctfoO3j zRbMP7UR^}C`F9rN_khOO(X=GH|0N z1N2DJAnGlI2x1=Kupn9MM&?S8A_d$o4U#Ilt^tcdNKPyWu9&rbEen`6Uwx;gfe{!n1BdB>uVp||M-BJC)8-w} zS`0-)urz}t`o{;fC_G?8foDxcD5^@XuC|kq^xbo`!Wh7aY@k4a+Z^cIVC4mJfc^7# z|HF*X=?RKzPnmks^rVjIvkqy;1%sr6dSWo=roLwN$zawAW7nCbgpBjw#N*gWX$g6H zGGu05HgFD>LqXW0X+2J8%&hs59G7bgXru7q3r47WEA#^-`hhST1MfEHgsd&gzmwG3VBIXiGbXN3VQ3j#B( z5x5oMGkyJdfM#x-jttPt!6ZKpDlMAP;O9o^FfYoEpuf{!1il7>&oQXdS&?$tBn$hO zr%P{ilXA~oO>*p>iBJ%c0J#1{2lgD{hLrS@f&q4(^ipO$n!e-EiC&F(o5PptS_Xt(H;JK2ns0{@d+p|aWzs^e1XWslrG zGrah;93p)Gk{zCy9p3aViI??6oY~mnwQt>EjP<%v^14aQy4mA(i{*8zf9uu) z8#cNdcJdqPCoklgcn@GS4^v@YF1GFf!jwdFozfBSNpmf%2PV9RUEcf=ytWF0h+)A| z?q6b(zr@yjiGTbharsN~zb|-!trXoYygL|ku*`j~E3JUpjI^L3RKfd;;Yu~V*O1Wb zn3s^T&Cti&OY~pp^}u4toqFA!M)#fO$gL$;kO%h|EsFmy|}k61MGDF+np2Gd%eGtsJoLOurn{e^Im@E zzWXQVQ2c2f5atP0Tap*ShIBMme9zgPS>C^%{CNlrey+Rs-Tm7SrmyY{5b(oqU}H!_ z*SA0NVHi)aJKw=?-EVc&@4mv9C9ALS(64X4@GPD2k36qk-xI9d-{<*OHoL$3vSwL8 za8H!==%?=9b~VQfE3hgVy#M}aj|TGG3T$}3eP|QczGMF_RSeJ3+|~ZV{baen zM|6yFQDJ$TiULlnLVPRGE_7NFSQ0Wq&fC01Po(v zR5M&Jhh|Cmj8_`nV3@sRz#M**NXIIQU7&!%y=QLAc}!I~AN#*3r+VvHMh^pCH(Ayi zRlA%}DZ_3f8T?m%1b=vN9`bpr`pS)61eXb>zXO<{W97tQY+h4l1WhB?|9HvY%@q}z zbbG#bJLw%S)1+$hh>3UF6W6|oq*3$(izXu#4YDS8l;kZ8+4t#($r%G%9hJV_I8a`# z(;6w$Y6Sy=s8!-q#nduh>oeESAU32g?<`Eq@NGKbGDY5^_GMXLp9N+cw5>>Y4Lnw= z%hx@4oqdl*Mi@t@kyZ z`?~jVCF6KsLnZArmBk{D;+v<5s%T);#7Gh##-Z1mI5nSbF==1;*f>7yYAuu^+B|dM zP0!F*P|u>%2F#p8InF$rUdbCE>lrRo79g|D+MKc&ne0p&w1kL(-uWt4)W%{Zc&7pT zWofU5=NzWx`k5^3tX?pdH_`+s8;>2%cwqSTt4YD55AF9@ji@}%;h5gmC)BAW2BoJ( zjeQ>iFVgRsg*@EoyK;R5RyHF`xNo7cCd*Stugz2hVdTSfgEKS-t-b45vFLGjXP3(x z#b~4cH&(3fvHulY)UTM`W3fParMv!Z06}>o+{z#8s`2B+)VSY6;>FKD3e3N~zi?_(cO@5r&uv(R-ft6I^Kzf#ib$yn9j!ft9N z#Ra;X+1q!%6I8~aPGux-4hG!|=e_YPQf>{KR&PMT${u1@04P$8XB43{ix^+We_P*5 zuU9kEdSCF>_Z2iFn^pT>DJEuyY-pe=EjoT>8 zSK5sj=vDS9R0V@l>!Tj=etUBNiHRedgQrv2{*x~|oku}VIN0dCqagf^mx=l1U-qO` z+3yJxBY%H-m3t(g#(gjJc9oE8W2De4+^uFIBGbf?LbM7<0+Yc(iYO8Uv5Ez;} z#{6@aNgsEFAyo;LHy3wX@%{HHE`i<7x%3+{OJmYUW4pH%L@?B7C9Hg$$6{Cb!nzmJ zuP^QBqdBjOvh_&CF%=Lz6MtI~&0j-aCGD9$-ZtgN4t=k0gA$gmxG$QC ziyvi{;IM2d7`!Mo3Sy+BFjZNPsz8MpwPMG48w=9ljY5nHBp%6Z=$Ng;o67g+vR=}> zCMr_h`%TTyiL38=pFa1wYb}z- z2ck@AT8Et#la^#!*j{c|2-?FnnD@{SEsaYe^UAeclL1Dxy)QN25hDN@D!{|GC4`DQZG6lL2HAcUG4a`dQh5*GId9cO~)+i$`;bVkkkl*k|{5lPwR<&|{tU@8`O9(2x^H|PN6e%n>{ zpxyrZQ^yx)s^nVO9xqL{{20eAMEom)mu_FmgT@~)trdEIzYABM%a`0wI0+7j9=fub z`=b`%U0;1%)Ue3;v**G5*O2bYE6Zx{2d*46dBnb~*04RNEbXW~n-m&5 z+1|P3FWUdb@Ou3Gko%9m_m94%UyoPzc4L2xhB3?$z+tyQ48yk7w!8p*w%_fWv}x{t zhG8l9?3Qy&s5yIVSelTw7p-IY7*}6dhT^ana%HEkbD?~_y`sR0k|G+)>{W+g=@B6%7&*$T@hwp;^crDJV;VG#8 zgvizbq|d$T_7uJTW!O9^B)3xIzNFokPj(+do<*a;8dLd-V0ihp9)?nIgaRXs<%BQI zBKdj+n(zr4l(nVt!1~gwthVUhZ#O~$&#y1z@f2ZrsHV3L`{Kl7{jf6D9&i0`XxIaz z;P1BGL&D_od1@Y(-E{)a{s>5I<<`&6Ay294MjCBPzgBG1<4Y-j9;W3TR`*{F*m2%* zuI)qko8W_l%9`^^>(7;RRwO8IdnuKiVFJ9H0V1b#QeZ_uGFSX}55c_nGhZx|^dmcGS01>6EvDYL(i~yGsjnSBUG3c9J*qvh5b#@C3yTP+ zb?f@HHe{ zBN-xdNdj}Q2r!gJMI%6^o5=>w6W6PWj5rJMi}}TG%569B!B6}3{t#g5NVO?oPeQqv z%Wj-X=d_X5k18?V3HAprAHJ>}M^~wB-labnl88$X{e@xccGII|DS;KP2{0H&fd*i) zV1Da$*4GQS=yv{*%L;f(4OJ}t)64`RC4tAPZ?)+{#whY<-C&QOHNF&jpCF#2@hSd8 zXMP8r@!N!zlX%uYb<})6v^Ly+#3m2OL}{nWSL5M^=-B<2q)_dCJYz2*R<;$8AY*a` zmG=oSqTO$JEJ22{+Z6QC<|3$_76#~^URQca%@?+NCvGBUn>cgj9J>p49JV+-?Rwi8 zJr@7>CYpA?nkZK{IR{I@6J^~XMmyLWnGlTvx;vh@qW*-NJJ_2@uu&#Xup1+c zf;&%O<*PA*M|#3hJ%aX)pubJYV4STtRNbOmC<;q>0U~?`iXgg$8WR5a=?^XeMNo{4 zF`eJLFk%H@sRpb{1D1=VHRzY3$f3R8e^U&eAa9|~^oyunhs`tUHX6~m+oWw$$7Q-X z5ML*1t^v{)AGD^?bJPGCWe(m{W#%v(lC?0@$b{%8=LYQFyMaOfO%XS!t8OP7 z&j5Er0x`7t$7h4%rMiJ3*)e;uxL*e7icgk%+I;5;DsF~K;(FmmgV98=G6ABG&?C39 z5-f%U{Xr)aiLEiv-BIN|Z8d8} zi5Sqw)ULTrphy%*B-50~0xOi(m+IeZp9y8GXt^3Fy4^XW?puTvLJw%7QuI+&S-Q<3 z)4ku-wS#kei)Pg6JwNi`H9kJlU=AZPnGB4CwQ>0ygMG0;WUFK*h|#|4x*-;343aa1 z2pWPVNcd6p(V3ZXhCL$luN%|%j2t&Qo{ijW+%R=6fEvhyUM94^6dL0~Cm2V8wqmgG zn9Np=k+4VFJOm5}i*9L-u0cXeh`Mfc7_lTrBiqUNXR{307z@Uao2enVZ;N_vyc3=P zOShZ9`8@s~*>AOL9(JeKt|8H(0D1?Nm=|W@@wdreV@y=lEHefxR;_x_tzpzXHPk;P z)~_)-@>)+4%xef%GlcMZlZ_h*w~fJ9vWArE@}furTAf@{|8G)l>p242CJ>wGV3Ft; z1w}efy<}G6G3@Stp+%^(diDK7@SPc5h`AnZvwpa>F^~D}Wdq0rMlKV?O8|*Y0FmBk zb_5x*(rP*g5>!=Iy>?Umz74b0O_onhN2vGN>L6=mqLFH%9RhkqW4+GODOWe;7SPPv zGE?O#wycc?a-g9gzGZj7^5k33%GRgl)3z9^=Iia?8n zE}&iP;{)t5%IOKb_DRL|SC8xMlVxb1Xws&q+o!D9XY^D>*4xPleaTk-l4H|YBQ!5J zY8R_1Z?%`skm*40-I6>CD5S;EAnhk707X6M;(7pmfdlmdxuk?HtV9?00E%YO&y^S8 zI}U}jUy}0xrL-9O^DoIQfY<*4%4QeKX@T^HjxV4~RWkM^rw;fn$G4*k^~GPP#2u@` z9TR#SYV_>yFnvL7wG@$-7djrzNSK*=IKM z`K&VZZWxXUXLdN|o#_Jo(DEoPCan_9fWnO42hz5;!W+=csw*5Q4D-+;ZWO>4<-(2x zwuYy{3C^R5D_4`sw9}_9Z0G3N7641s3Wwp!Y!6_5WqH}fg%<8Y-ZUi)E-%pnajdT7 zkE54&-PBvDsdV|4xIz|(*ygzIpDs@>xGu=J`hRkA-@?$M0Pu#jKZ9$3XV?DQTKjjp zb_FE?&eo_62l58(K2^So6)9b9aA8N0rbkzCvuIeA+o9I10^7pRZi;VOKqn(KmeLw0#WXF0Pqe#2QT)_3z-E)vmK zm;hxM3a$JUpz;(zg+jyd>oWx#(@}trQD{}KdsI-4K@C>}_v%8lvVCWt!|gq|;z-Pfj`8X1?E1j?STt3;2Yht{9)+e>^$?6e$Ayz$ z6L+pw^qn}gNtK%!o&9F4J0shDYVj?ObF^*T295oFS_UuJIojR}LxsQZ6z9g`-4Y+Z z;z9>>Jlm4d@?zIor&0BK5J9@HwFRHJZyG^jM&FhDa7})UA-lY57}v+7)JeG3Vbr6n z6hWg?vO)g0hmcdXNDQO_&2LC%#9$yOG=C;AVf%sVj}6E<+AZ+DN(A5vJb@-3n_AwB z^Be>A2HuMRayIO^(P5drH>RQhNfetZW&oBWK$6!6cnEM~j(naSeg-ssCcrSsIZeuT zsch57%8i-p0cVt3{09fD);hDQ?}X`IIo~&SFW@V!^=hp>s|YuiOozIXZFXDVqS!sB zXL~{)e2ZFq%SFG}P&n1K?!6h?v#Hu^)mkst_kB0E$FSgACi{Tthlh;A&7c=HIjQ~4 z*wy9?`>&U}lF4E_b{2z6GSrhRdq*+uu?z0I8Tr z!InWH6JR(G(fQT}1P!vrgN4o^@e+w96MEJito`mZ#E!^1RH#UI0CAJ z09y?~WC*W+Yk(cd8&M3y^Xq zn8%)7ia@@o9L6?z)*q8H*uu9=*KMzT^}oI*{mhkt?dvHtoQlHZtk_*PZrw)C_jsjs zWb|y0)`MUFG%fsy-OIgso>U*dZmzXLJ4UvO{&{O|?{DCquyqKteMHapXUo){`9}n2 zCa|xqim$E(*b~FyY300uSr11esv`}|$;{wi;)n!s6j*1--vAl<&I#tj>Xo=MbGC z(DeIQu>zdY5H|6npfw7tHslXP;r7OhmgO@_O%6@QvR(UPjVER-55vo$88J*lU5xC5 zz)E?MQ49hJi8OS$oA`6J2z^)$^Iy0-f|_NELe87vM`;8EPNy&Vo`ldjpkcx>M+$6U zxJ;aU_r3SalfE)ZT8jfl+;pJ{WMoJ+e-wZ#O#_o8T);zw^BEW_K4y(aOhB<AcW zt7DWv5*wB7ge&jEbAc#+)crfUOcs&ACsygNh=y96!j=?V)}-}RHtWZ1whlgM!E~tQ zyoWB!0KCFl#Y_I4t{UH$@~0ZlR6QY@wg8d4tk#b!tWB#5ge=5$F9Zs%-1lbZ@3PR@ za(FzE(dT>-(0Q>yr#eKLP^3KM@U2*N;)xiI{DK1*kD*c%$4AbQUX?srXV@u=I4j&K zi)K=*!6)re*3e0k2nCOhcvW7;|K1WK#{Fkn4WPBP*Cyk2ClJHTU~zXS+yH^Vu}UH` z1>W{taxEk&rQDECrVqmdy<+q(DjPqmG)p%bsV(_8JJ|ZwH^`aYuD>-A(IwORMMvsQ zTZ{`sU&}zPlxQJq3FqDCDZ!VbJ*R4|;NFY9F1h#W=Onq_tII!c2~g&uAmWXk+YR&( zspTL7&2|4H!avDn`S%<*&s<~hi;rK%{k>o(BlT$T$YDuwA^?0PD%9XNEOe!IZS;&b z_n`9z`N9h4G^zon?k;-XpRm%oXiC$$4f&O3$ozzL(maTzW$KRTN_o1TL2k>;Vn}gd z?%gg9l?jin0*sCQUiX>n!E+;yZr?FKxs~vx*z2p&^P|VFe#L6|)+jn9HH*5s(1LbRWl%t%dfu-p zyPku04T-Y;?!PrCv@)}dJ-53A=)`cnOpP*9HC=bjeSh^|K##?4`QK%X71w4Lo&*`- zhMjW7iQ;O(Ppo6gsLP^&(!N=#Tv_Y?b&6}VrETjuIeTyhWMf1c39>4a0PdCZ7hR^@h?f+0cBPoA6BFp<63BuOpZBuL1Qn)4R1+KWo0*4h-yk0->IF-YizERnh^Ey zOg0R{HdXa9z0&JfGRPd+!slC;3v*s_W8X|>>&VnXa*}79dcA9O|CZfxXG#$E%C0$Z z7PPrpIB;i7F2z~TL%fB{w(hA-85^dIH*`&)i|=~T z>tL8sY{GdWGG69Ri_FO)AZFyjU;Eb%3$5m@au}@HDaqr*y~!`|Gjblq`unL z*w*tSRxV~kYVBewzb66e><&F?bfJXAq}&(2*Rs^Qca{t7`AbQ){1MwWv(uERiLzyq zwgJz-5u!?zP6+V$O8KBbs9_M5=+i}FkZSM0Vn07hz8(sP(4lqy9rto$lbq2Is_Q#J&c}aV1N?Y_MYc-xs-zR2mC2U>@cMQv=hvXI(c`_G} zDuZ9k74%DQE97(pEf2gEMY-uhBpQ#UP}zf?=@(tCF5XX_hh!AE!BIk-(1|9etn zRJ*Z$TgGU#9N}`4Nn@1M3m6%o+J?cIXmMUnfo#tMzrHo{h`*VL~zkp*2wcgfs$h$ zy%-^=kL>&7aP#skPsQ5iJdDbKgW%{rPXN ze%q{{J5v@ExgIsdU2(P!@X z%q!zjUE%6Q1uSbt`ME2HIPvgt@QtrGs8bt1kBHh4BKt{5DKG8KXZS}Y17(Fn< z4#|EvW9B@PVK_r)_LqUGeL;KK%*2yp8%H8<)+o0#D0Su`=}&bRnCxbrLWayt+DSC6 z^oj0?868ip!>%AyePfh!wpFHs^t5_A6K;;N_!RB&m1ADGIFJrafNNm5meH&vAeT6i zhXkZJM|0tUTs#=2W+1%=hKGb<*V5)bN3z>v*0Ru_AP68u8G_60dCeEV1ZS}X;A=HG zLrf9~AzfJWvT5D6!Ppr2PoknNf@tU8i{P{m%tO8mz;bPQ^K?b}bmcBAd~9Y!xiWQP zJi=;dwbHZjdQ&V}5Cfh`^Zy1y{u|dmXA2${l;(1miL+sI`6|5a_;8ZIxV%RGs@NF- zJdDUs!f@GRxQ2+#BN(`w5MP)!_Yj7OMC8!`@@=|v<1t)+iTqK4P+=1qWYG%njWz#T48vVNW;J|@d_V> z3dX7|8Btc<%M1&LXN+@m!e%5m)KpR~D~FjwfoM```(mWXfZc`Q4vIFMtk=yL}TY=j@4Oo02px=bg)(a6!swWu-v zs1PKj|AWz3yPR;*n7!o(STEBjqPsmf2MHF`ikGj8h-ByXKe!F6$V33E7!^V|9&77Y zSQ!9fb=ax|&f+k(o<1%WQVxvh&I~k@rx-@qWJK>L^3~mEp7KT+{GXmMlP7b!dia6s=>R#jcBjeRgAm7_Opa?O3Sw&{4ckf9?R$(LGG0i|Kr#5& zy>w3sC12|oytWc3lgX?ZlLX94dan2~K9{5m#HAPdQuPhqKH4ii#n)mNqy#fOharlh z8zm9*kr%v|lSS=u-$z=UKWF*1KlgjYbibQ&||$r>nn0LD=Is*rC# z>p3j!?lJ4Y`y^x?X9h)qD^ZQU?-Qx_%;Z$W@%QYiDUI~c^mGW1G!b-{A_l#?D^+{! zcbdmVcuEVks2gyV?%Q^ykY;e&uIVs*#YaS6bV!ZPNmF*u6wYpRM@Zi3+=mII&UX|q z))zO!WfpxtPo@-4$_7lR9L$N-PifUJX%dK{spI+sCI1HTieF;F>gVHR7Lw{0p2~cA zR{y0$X0fV%@y)@Phb$C;+ufpDV{;}wNzS={@iS`mGp)R1G%^dPGBcM4U($<7RW2kx z*{^J}*ozOd9x@|T6co$*S*Ef%HnLl;Z??Q-w*%j7M_kE%i+l4eNp|Pyo1JH}yCs3U z&k0L?GS7z(md8w1emZE6tR|lh9k}!FQSu)J4}52Rc{uBsk@k4+$;Xo#*Pqa}Lk#U9 z-OIzhp)<|lqYovAU*8Z#4;Q56j@{Fbwn`wGgL_7AKR4RQO9);(dwWqLcUkrJ@{Qb| z*0+Cp<^Fzn`}fnK`{)d?H&EzQI_$Fc`wSmVE8fmT_z1sx6|s1k=|=nW?Pfc?+M|-@ zVF9D8pOFcGhN4hHP~K9*-Cq8PMGI9MXLdAW<}|}rQ3wICKe4nP3{9y%Uw=#eUcTpp z49z#W$D2M;H1gI6EXH1$1xzD3JCQ@lxOj@VYAdHzX=+yi0Glm7cfz9BZ4D zUSoaX71F!M^xH$PMpXQh2g@B19Y8tj^+4+Jx128(xL&>EdV9?EJ(Fe}rMeq*CvX=P z;feFuu$!QHG~LB<>+-UW!t{r^2FBirKOP{!6(J5x>dGuKag&@PAx7tH zCh|Y^9a%XKeVp6@x$o-5GZZEA8zo-;6n`tL`HzR^V=!$KDtK;7!b>rGSvO!^g8g@u z;|E4rfm}5iMmw!WL~5BjZDZ(JqsZM^zw7~-pjokEbm&T>eaKX6HlLG^%zRm+O3=0I zkxkbh&73d1?QT_kJMbn#4~^4%MiZm!!d0HQ3Ss+t^|NR%1o^Yk+UkTg-b}`BUY^}r z;i}3mTUYum6Zb*rHGVNoZcF>KMh88=xeMtmR;y97<}TyFu|~xk{cm_ODsGIW$RDf7dt-5GgdS>lh;T$cCy`3*ba5EMFuq9HxLW;_qZ?6AI38JQ}&^^aej_N zX@v&(7O zZ4bVAoL<$s#1D?V)uX@OyW))!w_i0IC%jW!vv=a3ck;0C$qQ$tJ++Pex$)CySVVdB zAq&j}(nBm_I6Bwk<{4g)!sC`oPGSB%xmJ(-mdTKZ>rBU zJtyPacP9o!5DBEj^oWGuXFk-g1N8(%jag;i4;}4~`;EK#N7_42v|2Bm1)2H-T6gg5@= z+b%SQOg@M@Wo*afIKow*FW10JZ=5e6qe^AYadxWahH^=Nu7?Ao9&?XBjz*J)-UEoBVP6!#KmD2F7m~Q_rYcfdEURkhtKdNhCn0vFl`z$egBzpx*F_sRTKi#fO} za*iE-4`M_iImwic3Lv*NkfH&@%b?9%4IpbQ8HV~~7y4ue)L;Z44;;hSfMMC!=4{Xw zuvUjLU?@>YW^#2(Nn0R_$i~vgMj+07{7bfFNY!EaadrivNOqcJRvye~aF-iZ7q60b zHg|1%|FU}aqkKPRm_L!Pp@;v?AHC}(U2Pq4?>i2hVK2YmIM;ttBG;M1(A%zbSt2(4 zb-rWW?qTV-j`IAW(mPt~+*jtCC(sSc_URBgLgv5aq_CO)|J|2s5 z9z&229w=&$VLBqD8DkkxXu4$`xud?raS)quG%1}Rd9EY7pd%XvgehV9{Ix}t`XX&z z1r~tZN;>4^CHoL|><}bgfMiy}@}{BL_c2r!J-h%cuN#ud?J6>m$cBjJtk4!}!0@qv z$V?ed99Fyzgv2%Wm+C&@5_TJ5oYJG@qLXsVL{?)Vw5pGi)5J?bN!m{&FLBZC9uQhC zCijBOf{F1v%saxRo3ts0>X!qM{hud=A+dRvwoWQp4N<^m}g7^C#_)bf07 zUfaF%+T#3rrIXAoZ4s3T2=|51i%lf8BW-;l)UM|Ea|=v>^&9btvI&dG*SIDhnIqmm zDsSzHj7p^R7hmO}^eML>Ri9XYP|`pCuS~P_Oy0dAl>Pzwb(`c&leEfh+3z=`6Z@Z> z{3zZ(_wyw>27jBY&y%90&|^j0Ym@y}`sS+D7YV~D!W!`$U$d@#9N<@y=WyWE_Ryr6I_j>q{fs(m{fmO zL+CEAIP@x0%yfou?7`@J>Rk61tE*mwEY`ewiFN!mELNJvB=#zBY4np+y-r#phx5)< zB10_|@ix1Mo3=`qG!pY!_MR%{O{>O2wkf+-wG@bI;8&x`hs94wGdW8X?byxF-F5X; zGczZU+18(-qq4Ucq|H@Eo3D);+(Tj}->t{|y4`!k(=^hu*hzwz-(r}NrT?%$Wzoej z7&a;Su5Q-0OZJFX^=q;e5TYUMQ%hZ!Qqlzg!OHR%x8QVEfWbW5z?v-< z-iONLON_}wn|ZdY9PWLR))VBJvMNCvrvBW%lu}^C2AHBHSn3X;Ghrx$NZ=+)sRWX* zyg@J~RZ?qN+P^^zg#-#guAi$`mt`5t%iilwP>ll7D{Z6QA;}hHLl8Qg_`P;6dcm(y z@tKt|;oLaLd?n|-y}T-X_U5n{zJkWlyY$7wy}5F~@b5TVm;0Zkt6U1mcsLD_YgS4Q zZqK1wZ60|(EURgI)>Yq|KbzMZt?VV#UegTosk+O18(i(Mpk!(JcJ&jsu23uIPBl?f z3Ty9_Ub?S;!IzWR{IRSP#gh5-@6KwRkYxMUp=+2VR(*=El=|KzG(}!qkx}Ag5KUj2 z5s$zC{7HBEe}nAVL&@#2Mhj-;2eu@Dd%B@w+bSvk8>uo@`C(T3@`D)4ugkGla_)&j zRTgDWxKu>ntO!L2N0+(&mKnl5^`wgXSQx7z7cCgB#Uxf7;}x=Vv1?xI?I=O6N)R|J%{aV?pgvGDRI!L)$ zQxtsJZB-Zvq*p;ng(f_IvA^ElC(!_U(j>6)EB{uVMW9`QdWJ4qwP;HwLjx%-2-77C z!qN;P-u-cD^MnmD6`)g;Fi%)s6bwo{`xxaS4Tt&4m$L^v(3kgSq=~T~VC4}`8z^0m zo)FLCsyNmVzQ#LFY7@rKW@N;6zoY|xd1f?-q!7v-Db`&O;S@jew@|;Dh6uoK^oV4e zfPOun;}|42W|^=V6{LcS4>_zUR5;nN3XH5xTIY)V{un#eqYQq{g?D*K<3o21{3iRgcbe3zfx8sOnupO5zn zWx{uF_vj5?@B39~f-6>dVW=y{rtoU>3#VFM#aINLvUj|0&A%(M$B)cXu5eBPQi<6s z=QvvA_^Sb-XTOP^=&%wB|LSTBl9K&Xi9i+(ZXMo&p;v0@ukWlFLu(f+lUo_E76IRH zI&=_iLX2x=7b;TO zOuBX<2dj>`W{dQ_faliMs*8j|Q=A{G=WOe?Qv1_02s7$A1yjf8>M~Zpa`I9G-+EhP zQl+`AsI=ImTJ=5ck3j#$*UOFD4)524n%WfWEO49-2Z8xrTfp_muGb60XC@in2lk^= z%f9THy#WavNmI3ZwOS@2{H>l$!y|7LSSC%$a=9U>t)N$uzaI`O##k;EDBUOh89Ml0N*4oTusAf8VFeW&T4VE$24L~li6<*1{k1FHRW`>6B!@Q>(BiNP01 z`5&^NLFw*lt6`38J(_$e5AFP4!-d1MxQ^uOG>$Oz-fq&B?~vJ=4H8|3WLx_jy4E&l zlU{yX-pD2-Hz@R#q+G{vPIO~-WjCu_JOOO5N(KE;gi(A@IO-Glvp^HQ)mrog>_>S5 z#rr={aEcy#6gT3QFtUF4Y~tMqDfo}H$MS0juT$0V_CHZYKBhsJMY-u~e6c*f2r;hP zBfN_BW}2J4RS#!tdyQ+4Vc%^(KA(O2Cj6P1rk~?g7uS48Ys=e5rr(t;^JnaQgTbyN z7^MXPp>K|MWJncM&>u)`M2?4NPvB&PiFb?6&f3~uZ+yb4&1mw4zl#iI-vj-b?-o$$ zdUWt3@}zUc0Tb!7T^tmB_-EEfQmM1PYbWnA6a2^?D)F2ERPisR@;3x?lK_tsrytW& zuZ^?G(*~vo{ViPo*Bt#h{Yxl#c{T6?Sy2mNrS?R~OT0ouWs&Up{m$b7oXb$@qo6LD zG0+Y34kwh^VXGlvTV|YTQU-BS-tW9y8QlT(?LDFT<^0X}4PflG7F*jd=N)yqhx2Q7 z`Bga@KA@Y@#DmNylE3Ka>-w)+BJw{Ot$beV_(i`=B4Jnp7$p&jwQY z2tN8F8`n3ynqowJ^R74CmFIsJe{bPLkk4etfc|pul~zqg(0_iS{Es~N`HnleqTWh| zG%oMDQ7d_4iDXUz&&X@Y{A0{OHo$?9v9^R|m;5^E7q#7FYpXiLWUpDL4PjR+Z&{8( zhChS=3C^@Tj{5I(M=MK6ps`?K;y~gI& z`{3yQ{RZlH&J8r9@`Y}h%buue@5~E^g!pZV1QWj!Vet;ft=cLEUkgLfx-a!wE&Z#{ z`Hs6;xl}o>lx2JJ*$q_qicc+3eZXP;3hZrERA%DlhjO}L9DHPy&~SnFq1hdB>h)tW zddFgV%wnm)Y7@-r@Q&5xnAKp?X^LTUZX#o~hFJ{!%oCrvTnn#6NsBk2@#|)QV@S0m zMp%J~8;S)T2gMzg?j%`?`qo_aQBVR6P`+?Z=({&GDV)$DIwxqk=zVtDVerkP1y>W6 zSB6y!?|8`r;japOTfuz0@AwXm`HmI%;~^~K4zSWfI!^;WpcuKQ;wvBTU zR4tbijG-2((RQ+uBF!a;ir_#RNYH{{8aVn) zSdER?u4R`lA>={cS*^7DOc}$=*Jm({l{J!E17)}wEGc1R{h5; zW@#+xZ7f@!TC&et-anWZX0_sPv62b5N|2f3K@N{FvQOvA8=*~4n-wDwOy1fslNs2r zK>9s2ZEcgn?o8TIv*KQ>>eU5qhfZk0yi<``d$uN+u-(&kUeY+9xP1c&Ie()KC zL+LfqtDDx=MmBZ=ClnQUdhf4nm*iGBIYB7kY$w1{OVAAsHwz-rsg*M3l5|J&%pwu6 zKmyFf+I8iWq6ANs8KUrrNg@8H{1vNPw4y>Zs|F@K4wAQ-Bz?>hKGaa_emJ0PabVUw zHb-9B1elIWv)v1pNkph5j#+4Dzun9q^3J@Zy7^8KGIf!C~GH96MLDg@!l zVKVkWcb05CMTwj9M;n$Zf-^fag(O)!No2RczIjMdSoG=7jF?&%O!}qzbk6>QWUmCx zRc`^H7Wg1JhM*heO1fYab);e^D7g5}=|2@zY>>QK(6zI5dDXY_VL#0H?p^y?r=Vi@ zTYe*SiRPLTKvs)z%CHd>P_AUmF5hROL=X%voy{)`m46VS%iKiKr=swUHFAp;=0;N3 zwN?y9vwD}ZM`745kx)W7t1B_O3+)|!9p*}8pA%wl1yG#fnKUYxx||r zHw06JJG&H%>~7s4i&wvy!1P7;!f~a42{C~I&xX(Zedh)W828& zSmu<1CtUzq4sE9H8|wc^^fLsiBQ#?tDRhDa$8$JNkmxt@w7E#yPJGc7o_1N8CJ@N} z5Xj;M5PM0W|3DI5vr+JKqqZQjxndYLS$*^HRKgt;Rd`COQktiDnkrIC9^kH^Hsu=* z^No43j+u7i0?xJS60obG5q=vN0YID2>iKIBX@Zglf)C*&<=Lk&10ENKZ z&YBudlBX@pYgUSXNG4%z+HFLMNa>A5fc^6g%B$5*>H_eaeop!$q@M6sN_lNciWl~1 zJz8O+`YRHPBar<=B_ryTqTtNsiHcn?g0dGt-E5y4eA={|3qxMoM&g_OknoE3W<4%g z0Y~LG5~ZEFCEwaZ3#@cDw3h4hXP$tHMhMbn+ zF0HYpZrTxlkJcGOz&>`eo5HzMm1bXEdPX6`h5h-u1y&OvU}JK@xykd)g`{drem z#<8#&Teqd&=IJ@njnxAg&bX_Me?OkQxOO$Oks;WT$4zNjXS)0r`8z{1;2cXUeH_iv zjy&&Ew8v^LNW(>NiFa^TmC`0q+$}=u*~k^v_5F{D#J3Ds`ov>yd#u#NZw~%r)*3nP z_=sYyWb+;`c4k;Bfx%h;DXZCk5IzviZl}q;-VrLs+9Wm_F_(T*A<}B_X7o^W&(ME- z5`eoM|9P<6wMp}U6)k>;-j9@YyLXN2_vw_!Up*m}e|3tI^CSAVPHb<5%Ls+8=_sg0 zDyo_*_>cb+yN)_NlLM^l!l@aBIW+7iz$wfiITj7?|C`+C1&ovN0s~L@1$Z3)!WAoq zvV?EDb|bOc33<}wS>%;=8E%AJZ#^HvTzfTEtnujbZ((^-o@$jKhU?C^q^U}i&}9IX za_vlwb>(F>V-t>wmC9C;!;**~4_7-IHBwdT7hC+7I`4l5fRj^1zc2QrP5@Kr881`z zo!qf&nP|Bw@>?Q5)XNOvPa0Y{T7$DnivQcm@4G(`t9(=6UVh(eBKJ?+GZSsaukad! zguvKEl-oQbbr~Z0dfSgKMvGeA?@Ze!&sAP?y?j_P%q(R3^oT?ZBjZ&+zIJ*~_lw-+ z_j;d{C*QiibiDcb#RJaBA3sj_g_0&1xI>5{N`$X?)bQXlo8I8RkZ-XL2pB39q6td-Z*h4~m&)A^>y+$0+kg2)+qA=MH zRWH!bqk-C}#X|9t*dNi=P)_mA#o~AGD0c5ATRFah@!ONc3(HE#{Uu{MG3Ept+f(P) zZ+fZx>Kez4{pwp+eV6mNRY=8;*D;j-4IWP~Ult2Ap!X?nd5B_QV#3@y)$sdhP@^=S z#OT4slX@jh^;vLkzeyS{9H7gU{8`5!gOl8b|DHr0(82CwA=9|$(HhXT_UW5{_xCBk z1LXJZrGwtzr|LgoKRv92I=|@XAGJ_0JCU;97v~{rMXEb}t<{$iAPO3PwDz!IPPg_a z@p}y+pQYdLhm4TD&7q@8YiWwZqDqB1xthDrMc(paf4ev6$KQ}1-2d`KhelT8Kc&0; ziGb5-1w0}E!q-4*o|X1QvQN9*(hP=J+r_p#hX-gz&`4{!PtTg}OB2iFHM>Sh&FC%oRZsOpeq=I|SOQMYK- zE@G~D=s8r60MuL!B}fp@UH&jQT>u{FgUGv5Wv$RnGzCj>)p<5QaL z_)|rOL#ZX{%yL8WUX#nI(_etxPf<9!|6Iyck6!dqEPiX4nNu^vStBG4HkN5jq zT*6uFIU9?A&wExk8GW;J0U{f!1}0b;LG~dx3SdFUHQA(44)xh@CJP#>3+_}A;$ZGp zi`z~rWirm82pj7l+T$Iumu>s`C44YMA=KdE)j z02kqbVnz5AJB?EM7z9p80|>M2OG(1cNtOMVlJ?6vm9y&xQ<}9Yg-wnpRO^N|Yw&o+ z+SDul>rq+pmy=LX9%6f>#lpx6fcN#^(V?!zB$gpESr;~^*>+1(DHN~DlW07-#y^5m zOUx;tJp`?fb4D>glk#MO?^AMzC5SB$VG-3>-i3)fcry&`BMvX#xG#6qS^DTyJo6MO z@sGs;SeBP4h=8ItOh*+<=Q$w>q!FL)5+-Yi@cpXw3)jL|NpR!j67_dVJu zHTzQvX{wK5iq&^FB^51ARGOcLbNlE5AAyJzjVSsa!-KyIMkx=28sda2MCm_G4oF%B zcsW(yH{u!|xjcqQ=G{fO>}mF@9lVViOogkrRah5Jr23=5GM^g~mjbmZiPHTnsE$M# z0CT?aj-YimB@l!nQhE;cU3>B3iR}c2+A_IMICFlnO9MfXbCnGGK2ggoVp2-EH8aef zC^{cK*YYNEH1I-+JApqfAdXI%#}oNodvYn4>CU6=K^b??R&+&Z*jS+E@Jic7C&CWE^>>_@wI$^2G{E;LPSQ4C}4uWft zfOX8spAZ5F30NoN(8|D`8XE}e=pkL%#;lCAP2uqZ0A&=K0@aW(!TG!UxNh~~{F3YM zzC$0ybIh#a_YYPn#lwWH0>YG;WvAL~3#QLfD(p_W4GvktQY4vY0V>kHej+1Z4th2mr zAC!9<{3M_KR?vz~UTOfDLgn=Nc`oLE)SdNLRPEP?rx<1cVd#dT8-@-6XXx&h7*e{s zoFNAgM7kL|RX`;ab!bE+RD2K+0Rag?QYDm^@1OCWb+7aLIqN=Wt$Xj!zOEj1SSJk; zK=yl(ii~0Ki07dgh!r+4p@s`hv0DLCosmdjb9DF zW&_;)6cSB$`y~(exAA9}0G@Z=@;4nGWVsFV4v&!^o_VOQl3)E9x%tW@wMlhdqWaGp z_g9Bk{pO4M5z@VP*utZvUT!n1|2WC){Q2qPMcg~Dp6R!VzZ|#}_u%daP~>G})6bVb zD+|t1UC!&FXTgdW@BNW6iD#2nwD+rM5;k_5&Sr!9kM-Ry7TpDZzYse7`I_e6H;7AB z8~3Y@%*P26Si~-%3rF_nqtI_nPC3TlyKC=G{u#F1H7OpvbqN=^dv1I8YD|1zNb2Oy z)!o0}6Cv}wr(<`^x^Nlag=l)&ey*}E+(8CK1uJONqfk++No8y)XbRt9`g8{fB}|Pfrdeh%%w!xkW@PqyvqWE|v9mywgFw2eNuR#SjqO|-_ybyz2lV<67&AmCgySc@<))eq zJ}Bh`4(CK#(b6OBx9ANZ8#;_>>J*J*DKXP_M*~0p+5d{{4 z8|MD&I@wBISvssrXSy_-YS#2|iQ@?npIml&dh?823!g%(m_o~zLi5tAG^g!C7kH7Y zNRhjKk>_?Hi+&1Ro%+t*JB5=4VhW*`(AT%!*b#DQ<) z!2$=Rt;%J!EufY@5Nf6Lp>kQ%SsA-~QC~~>i#O#jTco{xJbmGU95Fh9r49*&`62pt z;h*y&x4EK4)MG`wOV*21He6_tB@)lfZf$d=G!(yU(YdcxAZt%cVQqiEMeo7cEeXZ~ z@fG39LI)>4aEVV^St%p`b!Hz2Hb8?=Db-5XJtzS9q6M^Vbp_HzLPUuWKw0&L4+td( zGC)EEJBYy6>e9_}*47&KvT_nUbWoXhcw6hkxNV|#{x^NaPVcJ1`hts+yojgSLaXM- zu%`Kg)bvb=D za4b0YiBj$$1|)}sn4cFq`m{R18)VHJe0N$OQ(U$Ax26XgaPg|tQZNRa&=Vd<-%=jS zoGrB_!n|ocT!YUPgVz=E#@jv@N2Gp}EBWJ}Jf1=FMGlH)=6+IWU{NIY*84qaFN{{y z$Ygo;O(A~HwzDMBSfy1rx3!tAk(UIv!2#v4Wb9xNk_2oodW=MaojM*n5kY^-L`8!k zEEH|zSsgASZBKUux>ahWzJqQBgB-@grC}1z&4@Q^K&9TWX${&e^OU%dRE$#RmWssb zV5lxjxIQeBe=&tTKb4(5m1{9{J*|u4iKN|lSC)Ag*M-Q>yv}cQaigIir(a#$qPZ`c zkQ`XD1{IJEl3aI&>@E&!gCmc|0d;X##)X2bCBv_03vpBBEBBm9`njU?)Uf5(89XO?=Tq1!JXFy8CQ3v7(FRPgHN$16)k` z-7L4>^QN8{P;VOs$UUPo%4|a{ljaA2e>i|Rh>$!Sxe@?mnMLk>00{{0w(}!%LXmsj zB-aRjrrPmLeFo}11HBvk+_JPaNUVQ|y(iR=H+UzV`vAC99}m_@kdsN6YU7(0OPK57 z{bU&Zp@}+4Z~&0!WXfqs|>7 z&jsW~#gweNw}XM7Z21cBM8A76Y}6lPFv)jP6JzcleSr`75*NSYJLuZl;5^}L$=1jL z0GT;7c(y-2p8)!CG-6{LpgsfA;$yxlLvlZu6pWTqS`}ZRNXe6v+1ztgfV^Lr@ zjs=c_q>-kog5kE;#&rN4H`ajtg4W~{SyV203<4HU0&BKQjDh4CvZSgD;uYF-bSZB& zo&;2{PW$pdAq-@)){85@ol$N{d0B%^fw8ipupYgVQ!=xS!w zA3N_4O)K>JC9VVwW%)gK)ZLQ*qzE~=$LcwSx>e7czY$n}5K0+sJ`|=+@-Y=YbSSiH zDYSqu2m%&*GYZYJ7hZ_WSq9izf9YdILOox?9p6nE<=$*r1qflMz1}TWt-v)`0E{zi z$rL$^s{jr3OcZe`^c^|X3INZ+kD{ zFXzfT;8jT2b4vs{3gY<=PLcQtOJsdmlXq_$;B7WDvA0V04t(j45Zlp1+m{SG$!~wC zTYF+I1-HL7(VWk3AV^)G!KC~1^YRU~dMWkg{h@-N2}6BNbe%r;v;)_5bJq1Ats7Ql zp^!4zf+C>g$_^&@M0^z<_$duBtsf6CLatK0f;&CD$+BNb0|Zhc{^Q(Vg#?gAa@I%L zs8eo$?lZKw+mzOwa#Gn?#6K+|BdhxF&*sQpll!l#yBW8#BJ5x_SQ$WN>EGDCrnnm< z;DzyaVc>Rg&bCb^5P1|5Ai8k}KjyIp&?L>dzM7+-S`j(`IKH}qxR|U~A)yS&-7@q0 z^9i)xlK=TR&zP=k>h_uG_y8BLwfEK~cVjA9P4Wf$>a%02qOD4l|Cy)0GAm-RP|eXZ zqYuWJ#1VHGDOUErQtz)y?te4c-w53Qp0mIGXn%KVe{Xr8Se0HKIA!JRLrns`csbXw z4|nLe$*=-YLod?*+cE0+)a9JNPzA8b-Q`Xs=R>Z=s(T5i7kuQ}n&b46HOQY?e(0cC zkE(LXrl>>E7szF-F}+@+6x7pqxE1?yFIV!X)a&$0^|orR9d{5AIOZtjmi_?C_8 zzeKWEPCGyRVbTZ3XUASw(FkfBFrK&xsU&w=*oG+$y%?VqP|@d^IRysQtB zYa@GRYW~Og5#p<&E#hZKmgJM+J-%*e0B3*vRPXCVn`8&e&Hyzcn zl+sBwLUDJKTo&&+`=7D?Hc54sqng0 zNib_!(;|9Rv&j8&65DO8)g6_D118a}~>=n;`$3l){uc!YZN{D}= zxzBiKs@V_zQX#Kr7LU_hrc#5hoTXP}8d&S75e|^6j6NAK2tvWYucx<#973SLrIW!p z)T#!YRx}ztI}1*Qu}E8>NTi7p-hgQFU=(J35XPiYfu_~aOv#m!b%xCzYNl1F@Of{= z@D8UEc{S#j?fFDTxjJ_wsVRw`xFF0)BWuM$>Q zHGa{_M?qur!2u^$mkP~wCZiE_obQ;Sl67BvR)lLN1ksr zkbZ(r3nxAwTdW^@eOZwAh4P7tr*zon@Yho$zXDltHC>yE0#MUhu>z)i_4_drhYAu`AOX>d3AZFeAtv- zz5H_UuXF9R+C^~EE`F8H3e^jvyWOtH5qvS(HO+#?Nkh2mb7kMkco+ef6x`83HWVyG zk-^d`WQ5VZ+&F0=&^=_7A?_jo!@+E{1)-siol^N;b#r{|duX^MnlIlfR9~<>Tt=ey zGA{28l<^4_n8yTiI0R%s1MuPlPp=hy<5tACvZ*W~01hLhW!JKIL70ik?SgEDMU(ch z>G%Exp%V}h98!6^5WbiYL9kHr)?(=K*SD6t{<)>bCfjTWK5PTu<1W1r?xIm!~55OibcE)3gx&LzVn1>!bH#a~!k~ z`2(CQK;XbkGh1jAz(NDTmegfSenfxEP*{NVQt~{{{X=6f<$+Fdll?S5#q9nu?3?oa z!r}pl48SdehsyU&8$`SVxWQE)p)U-VJRpH7M{7%^Xe4LsNhN_(NN0bE8B%i1S6Mcn zmz%d8k{fqb9Kg>v+mK+IW-dkk@@Gt@qU9m~>|4r$ex2)k@et|CcYOOe?$!r<$3grGu$3_&y5bGF-i|kZl(e5hMc)3 z`K{TZkyo)h$ zzfhjeNuVbRikzBCGxVOe{R9|hU(1EBm^1KwV@Xoe$-5WBFhf2+uBGh>$?@R#u#zL` zEdR~9@9zO*?0KVe4IGVgg1B2Pr@jl#)XKHhYP2Ybne^bIyD#J|sIb~l;1hWW`SGM7 zf%=Hj5-vkOnAOkPun|T8@u|0bjSMIdTCtNYVh5;=I#7P0l0g zh@(@htep85J)aDf+YZ!K(-6iBM%d|ra0l4+$b?eicv5QyefbwE1G4Ao?{p1T zn*EKwYb@)JHfEYvMf(dGO~Iy{*PmqNcL^#u1)G+9q%FVrc%LkOM~zr?+D0GQWo0E$K@-MDs@+=w1<_wE1=DqK4)yh$$L zoAZMpCOM%E@v$_r_fw664UuXjI$y%`&oP5^#Y9*V1$Lnb`hi|S7;X1*2XrN&u+_cI zAgXfeZuQv4S~Sq}j>Wp~G?6gdhJ80~-P0<(8zcV^HM?W_SFzyP{{C~XQ_-Nl-NgsJ z)=-1?R;TaD|BbGA86w$c2_G+(PclPa{)`BU3uAP+Y!xmNeD~Pf&u2NcfT`qpeF~3P zE)F0ykOt{q6{M!hr_{5(`+V<_gBTV4dbE5tpYp!U+qR`UiSvC19+HI51W>$zV)LB# zZi;O1BK&S~3sa((rR?NQ)=OMJB*oRpXvUciYCo$JmR_bq8!^C=)!iac5lZed(h^qe!d$dt>Ss zF`o_a-Q3mLFS*A_IVZzUW036T*J|jm7n@P9|Nafakvpz{uhs825)W`eXmjxjdH;V| zKID@m1m!4?K#7?ITxkXJn#%3QGc-R{){E?m;6uNdZ zHxFhff4;fEoz=%Ay=E8Kme!3ws<2Qy`CCbL-_&sZDzRYau>Me3&)2G}>&)=3r(g!n zOP$#3(SOPOYJhs%#nHxqs)d=*YB|NFy!G+YCji(}>I z2F(sAY4*}5ty?yo?YH2_#5xyJaAm$ROgbbM+?b?6oS=WRaMFnnP(ucUrO{0O*U>FF#0afqk2a#` zE4j&#dO({Mjp6z+@EpSpJi+jA42oo6So`VL+7v6WDv@vv-gphBf&%X(jq4dGA!!ZP zcKO0g99S4fu>~kCy$=xvR9yS0f?wy7&}ZJo@&$Bzuz_6`nh^6LIgcU5{?|9}6{rkr zDzBrJ9AB$X(yWDOh>2@yUbgdDk@%mAk$2z(0Lk)K2(Taw2%)E#pG}gt150$^W!ouR zVfg#aTyQK!Ux(Us7G-&F(W`Q}+3+iva8_e4ru~oB4YSv|xUAMcY*mDj$DavR0PWix znWe%bkKbfA_GauHYS)cZI%#QJR%kELQ);Mc`SNRs8=)oI>k0J9x@IZ59Vxm5FcO9H zmZ26Trs!^s>UQ)xTT=W!tmQwiL-9VpIZ=C1+V`e>Q0S=k3kGPfS|vPKTa^sIu~o&Q zG_DZKo}JsqZ@{||!&gi{zJ}qBz7$=ggiBJ4)A!2l4pi}n)}h{aXHHhH0rj4;jUU^$ z-XDJD>;FcxUuX60C@2$0jsRBoIn66!tfoiR1o-)Ebmjcs{XuWDrv@Az5qi1kC!GL#tJbERboqn^EUDo3WOzw-$>0&CoftYUBS2H^O+TtmRoaizaIb8%x5D^q zvi_4c!u0_3uX>F|f2AcYrf+uO{uATid&zQf z5^GD-HT<|L2g#_sN4uhOy9^ooCsp)>;;p0BrLM^}8O3eiGTVL$gI3%R-@09nR-&(> zj6qMYgCv}6diHYq%L5>5Z(WbSDPzLG?}}%C+8HF*b-roo21BM^7u2kYPX%e2l$2cW zpX-rR!5fUGs#%#IwalLR^Y2{Fet0VSBmTj0joFE+8Pao#C$)oePg(S@=rKpD?vWAG zcqh(imQmsZ$bVKWZq`5q$Ag$)JAbDO0E5Tj*;&cuDIW$a@ay3g2T2_p@o>>O1E2FJ z6O)oY$x|we6A^8-o$m$buf*$`k!DerncM=R+Iutpw`GL`9^K4T{0Uc+y)n0}`Qe|1 zrCw*1?20j4LJFn>4<-@F^>6~gAJ`Hgf=B?L#S>2hvq^rwcT1Dn0kV%hlos&U zq#9v3KYE55kUQ}@M>GZ9HlS{3Rg-CTji`m0O&*5pM(6ja<{6uF{-?_`cPV47hj}GS zGSp^C;c0#tn2ZyfH>`?LN<8fmd=$N0RuxL@9vLp&aKcuK$fKR0pEVZLoGxYnG|!yA z6fzgzka(3^5EV1}g>#wT2l0s4c|D?Fd9VFzL_sPCCS;^&%Xk@7z_9bu8Ex-8<-4rC zItbFkQ6n`6uU2wE!T{MNtL?v%>0i>B#K8F%CdFYFwGK9|3h znyLk3+at@nbIbW_%OXCkgqdL;Jsin86I_B9K!Ek@C7eD|l#^aTdU0f*K1T$@Ib#r<_R`B?w9#k`?As<6R}Z*579K zAL^Bx#HQPhjNbQIJHUI+6fO591~cb8P;b(01y6D3q>AherQQu28;C)4M~BQinwx+S z=kE~;cfxAl7hDLo`+9jf6AYepk5bu~cy6ZpTWMTaCPJImTTK$)H!_D!rkH#TsPbm{ zxG3)E^#0nt5$G8@wGpWbKqBwNszc&_ZMc7;$+o%U=$x!PV)2P%OZP%TG~oSl-%RjT ztQE#{i~RIMv5u9$yOsZWhtI!{RM|d68&kgq?*^S=t%?C{aU!8EC1QU|30a#OEm1h? zshqu~bHtsU?;9Jrp_J?7C9p-S*|zt5##_Qq!Jx;@+JaZ0lVAQ*>b%9n_KCOsRz`)M z4Gg9+$5r2)b~5%iZO=FPGkT>qy&XqQ`C=b-jCzV9ahnI*)$gmxVfbO{T}`EH7<1+q zUemfybDc?&wArmsyxQsn>qyRKPXbNo{!aGU@P3F4n04mm1pfds`I`&)+sgR!Wz5O4 z+<7SI4oAOw8Sk&{z48V+eJjbD9JW8E4nYw-`)z7<-2!~O6vo>vrr$nt8QK4S1+q%1 zQjq#<^0-zJ%o)^JVIrp1DH1Mb`h`F%T{y>63+fY`LXY0-HpCs}E-45;*DJj^e9F?P73mT3mQS*2{(+eLcg$y$X-I%` z2!rq8^9OTE#Q$WySJ=|$AWA?{7KnHwM7jiTt9|dp=jB=qP5@yP)P4-P>J1AkgP+FS z{!PoZM@#dXy{c9IW!>7si&*adU^Osn_)R8?P36`%$>C-_x8p&lhTp5T7uE2LRVGqZ zbFzo&9>)qj-u}Z|rQ!feKoU;}p3E-%aWsG&iDNxDWkmoGg6wsrBH2T$Ytr)feJX|{=Mf5Ptj+5QfxrVji` zQHsV}bw`*)hRxuQ*T{yz*b`S%00@QSRbjrtp*bX99EM+JNcqEX?@z|#KYt6O1MD%0 zuTMXIx$RW_rgyc|@ynkO`Q;_S7{=4z?|^@v&qf!FEk|`873(?|UfI^%7vdz}!Yvt7 z9-uJvKVqOfM0%qoYKEMeTd_5!v30*=p!V~nrpsm197U!GNh8442b>+#lIkry`FQNV zX58oA*Pt1tN55nJfpPLDuRHEDJTr}Z`hBS9&i(H6i@p4dUa8!^o{Q&Fhz{m2ySbzJ zch8IN#o9eTvy8y;D1q5kz=FZ($AJ3+cHnT2OS^!BW|ooJtkbdkI02G?!T5`NvI~dy zI@T2wOR^Nt7!$?}|JEGe{Ala?pQ(1+YSl9B+Gp;BKC;`x?pbaeYRlD&hb{0``Kpzu z2;HgpZ_M#u1TM2nW1%4c6gov4ouZlhua#Q1AsV+;ow%)=B74=#*+u)F@MYa$3LtF< z*{}X58yw$296Pkt+Z1`e;W|zb_9+`N3ATifsSynC+)l@ zmR7};R(FZ&lsWHsP z1a9{mj4O=sYZ9TU0D2>meLk_qvlw5boW@<^yw=2aN1*YOXWFBOQM4G)`DP;??7+_+ zcqM=0bvHV)@w+?B=A(V=Hn6p><{AaCl__(zFyCBfx!SyyW06U|&XYyJptV)cqt_e! z5FI3KZ*_&W9KV?JPrOzzvw>#lbF6QPW1csWs_Xo0zNDi}1Yh~QyP!=={HX|#Y|x&=G@!$%VeW#!%=S={$ecHelqQh@v2io;?rO>)44 zo#^y}-(e84!4@JeI&}1;$~!=r{UhyFbjK##)>dM6yuy{hO##nqmUpXiL3c=cmjZ|R0PO4>QRu&SnoGd=OYmu#|^Dt9_*{og-*TJ>MM*YoXthsQLh z`-4A$gA&O)!<*4PM{gG^e=kpKW?CPiRB{Jc{j+<neP~>RFm!Ux7Th8jF|>)0b+{Mn=waAgy{xs|8acEV zP3eCK;Ue+6k@U2fh}e6|bmL4P^mG=VnP|$1!_a44*U(0C);Hp{9yE2FXt>tYYOFzaEvedH8-P2a(nD%q{BCFyd*V0gY<3 zq9$1j<_zd}%?8%3CQ+?jRcx)28IzmgsOKNsxP(B-OTsoUCi)vyA{@;kW^YlN;iwr% zK>3#$;7MN_^4_7KAGtqL+qGaw%M>*w`5_^yD50yddT4IN#$Y`fmb z!~H$klNR0|@h_hOBC`Fy>^<7-e^r3?FTZ%_;0z!W&h?Lx^85Oj*Hfn~mFrHEr5cLl zNeki{Mr?WJfyFVjAf^u9l9@{Jr26DXm|7d>$d_b8_v7pf0Gzg=NDWB|UTLLeme;~= z$!1DfjL3YQk5TzIxavocpGX-_?gLp=9(FwQ`&}yjx%66-n3U)4AGPB9X=Y@`xJ;zo zu59AVnKX}HSvMKHRvN4K*Aiw-1}i^l z(u3_27popCCB<+!`B_EiF*BIC++%8?_1JKq3N2^(NKiv=wp+3uHO%nb>hk#b$~Q?N z1rjq+CTL&y@RCEEnS6jH@hdL%yi$Sk58`2V8-!&Olf{4xZqCV?{#OXRoTrgs-!;v> z6Hz=99w5*L=1R?*Z)zlqH71Rob_D%yTH9w+%sqLgRm54V6{NDbvh%d*Oi(S4X=M;3 zD1Up4e)C$!L2)!3av(T1h9Taj6~c*-ARdD4tlr_Dj%J3cicmF9TE2pLg-^JLu8{Y8 zpU>XXX2Bgvh}~!P))dr_ZhRJSs2TEN@h$K)n0EybEmg9V)$w_nB3LvURGc z1t-}w8?B~U-(yfD3jD@kC5nP!kBXr)_U85`gN%NCQ)8#gY>X>)6I=Uy(aoF;9p29i zum@6sr%axX->g=9ga5&gLhi@F8wvqH6ZKuC$six&tG&0_9N$t?+U>c2J@rmqjh6?G z`_Qw#HZejG-dtym8_uX`8!x<=Nc%L@yVI zapnstcj#cAVCufM_dpu@tzx%;nVP}y1tdhB<5~Vlc*V`CgNJwY1|JJ+e|Ck!_-BqXiOfUmNtOXRXhRJ&sv-2eLZ_p z09`t#Y}ZU%^D4-Hg$k;Vze2^yrK|4`TsVBZkPUdcNBu8#yo2nW6M#+Ajw*UhRZXTioMWh&XpkW`2AuJ@Sl&W9zxQl02DSt}Ra4h#8LMV}+K^QG^A4vWH ztss})h{P~?FoO5t1M-jaNJ0BY-fO<|sX9+tPF>#-5%z?PVW8)%Kv(U*gOdE3X_`y> zf|yk)>JjsH067l8&h|~3*Ig!O71mo*zeJBP8(?c6pi2Sp<-Dgt4Y0NY{iN5>5jt@R zB%}3l@bG5@VStr@e}^Dl%;jU8aN+tkA%(PI34mB9t*AFeN@=!GQXO=>ZjV$)KJPwe z#~dwzP*1}_`D$T9@dE;H$AKE64-mZ=g79`jU0wmAASt(XSjJ@V=c;P(ZKJJ z$loK4Ba=bfD2)7KOn0bisHI}K%~WXhu)+DCTayR?L_kv@0U%W}FoPGCt z>hQ8ek^op7L-as|0ssWT2<2*c=V=J(4FLQr0?!%3OAD}=2D9fv7J?0~`Wta&Ddr&0 zgq`n+MIT(`8{(IRaL=`zsWyU5uwFc9x(d(@DqLjVV2mW0n~g?Gb~}_wovRk*}ebJk}1LOiZ$JsRW0geM?S?z@Jl@0 zif8qZ*=vqGdw<`#Ug{-Z)~7nD2T2%8S0bZX2cYk|+x__~d{hEI<)C5NOyFj3NQ#G=#!61mcr2 z)P)GO8jM(s2r-F^id3aznz=Ln9>SaXw~i?Up>4PJ7`;liZI=gUqxe%>H2(%^vy=Hw zM|`yS`hyjwHq2lxZhT(LZvv4hdO)m1H&3kPT_d3e*4VU5w3;_S`fCJN-%=Zaq#b}r zKZXJe;2?r%#W#IS?g*KA3&`z+HDW~KlcE&{GHSO)jqVU0f!L|g9=_GFug3Maascw9 zKQTItqYwre=P#vcRed^*pY5rZApn(o`V1=u@y6>bQ!%KTeu`>3NyC2~aL~gxsz_>@ zBO3CD5bH#}8CJim+e|{%NE|am5;X*qvE!2>0~k}m(CcA%lrSEmsmwD#4n6!tCV+!M zgGWw-XMTW2X{YYjMv(B27j&FQmHL@-w-16>Q`?R6x4*RM>kYqgpU#=$rVFr`Tdw+? zhBqod_*-hsAK<-J@gF}kz~bR+x`1Y9|5NKyG6PJ24W4Yuv(KxkYHia(7+uHYHKjmW zq9!eFN`x70nhm=Q|F!n`^2K9&?%R#5M2?SOmA*TU&|zH>7d`gAKkQhgvqsBOOo*L5B%;s{{4Ad$DaS^1>2_6 z7P6u?YeM^#6$QI4nJH5Bfy6U$`D%m zLt6F_I=+w;il3k-ga!F3XZ)j*Ny*i-vCXna`loZ>bOP7912O%Qn@1Brx*Vm91AjO; zGwM=aF+F~>9e7y5lEbW6d3Q9>(|`YUY&NSd+GC?Uc)w$=CI9;8K~aF_VL#&G3*{9% z;*|~MrK#%^Mb+mqKDBE%1nhlOm*`Y;_)O*WlFbPGPv4@@LnSs$p`4*^Tupt31YG8-D$Q|9xCz5eS1QSSVK}dD>MyV$fmL&k`o6=xrr>u=NQ4?NOW>&Hnv(;k|SdyO`07^gy2m)#6H!a1`6ww0#uW-!# zct4v{za6yYYaFN_2h+pTZlNhqyOamp791yi1~T_xDdEc7Vbb;SY8DUPdlxraaDRTL z&gJ1O zXMXyNJsQYVk7t;{GxUmwDgrI8P`CL`ZV$;z!kbaX46dF%vaAK#PKuiP86)Q@2_jR zBRwK2;3u9P5yf`>M#6xhi`{1e9erC;ol790n zKg`SA1-lGwT@VwrX z6~K4U;2zsZBXwjE+Y}euklt&pw9_~y(DGeeT2?x|`MgQ{yy?YxTbz}b$Vl1p-WN8n zamme)!M)qmKSR=1LVlfpIghR8_G%kC@8pYv9!DU4&V?lj?M3(uFQw485qqF@p3ktREvS$?<;l1J>L< zT+ss-ib=E#8jN-~L*0QKs!B}rz|gQJ&R~4nr*2L?{1qn)uyeYhb1ZJuufKLOj_zqU zvm@Dv`#=g`R8SKppb^g%Kj7cLQ{^Dpg6z_saGCuBkbz`Hp5e6ff_I4BZkN;imUnKqnU;U)K1*cNqWxra8bEKpKG6HZn z#DPr&F zyO(dTE?e4$btAEFLufSRmJQ&U-7maY$NTp;|2>*j{)5@S1v3VLz>#mc{ zu6$jvJjCdc^Kn|C4~Iq(?1q4Ogl6g+IwZz}_$T;Slf3d~%&(KPYZzONW(JddEQ60l zgJG#c#5senRf}bd&9hiWKbwc@VbGB?VCp??bP<)So4e=RVymZ)8TOHSj!1Ou$7gZO zLC!DZ&VHR@hl;3BUzlRijkKrp&oDYMQmS*x&K`1EiXp()uvJ~7FvNo z0G=}l87ts5Kpb#=;XpRPbN~QILCJ+c@X>R~Gtdb$(up$Ci!d>WGI1#}Gm0|v++<;s zWZ_X|;ZW&7U}XJflw66|c^?ALd833d)Cc78M`rz9t*6eq6=m#{uJrwlh2 z3dt>l6xQY8k>L^2;1M&f<(D)U5RexTR2CFe5R}vx zMA--lDhLTF3W>=INm&TVI0!3Ah{!vM%G!%6IEl%ci7B{7x`Kr46KIlr3d!CghaOW2+ETV`o#73{z`gQ=2=cDe~8cO^aMbOS=Xu z8&B)oQP!UJ)^#^+tsmGqhB(+1-Y!>ibW3otFL%KPxOhamc*MGPsk_~-cDqe<^Neuw zyzl9o>K(G~>qM3AaFPkZ5$L-AAF}PBNhah$H>TSVH8QcGQJYbij+4_y@6Z$+7d8xCFvobJx3}qjwY2?oO)XQcLk4)KfYY z(xRu*(;sB^C}#F4X7wrFyI+_6Qt5tfS+0M0e(_v>Radbtrnt1Zth}kLKCq&)p}wKB zek!f`seeny>xYi6oevv(GQIi--wwy!n|wX~v8-lsB;`x6^_N$9D|3r$WiH#RkGFTW zw|9Q*bqt-nymRqi!sX=!;0kc{|NMeo0R&+85Wu8?WEj1u<9J8SP&$IophB;+b|i~M z)^};Vvu^A@QZti7zpMUDK7_3G9@aJaZ86GyqC)>k-6$PohF`MBQ4m~9SI#r$r-d9UA|u?p0)k=7(0~7Y1rNV`HA<3R;Rb!4;OoazfDvc z_B{IXBKmlT(|ip)2V*wEnxT6;R)+}mV$PGjo!`cC`3$R!`nopWmdN@oPxd|eK26lj z;xc|N=Sn3gj#vU{V3PmqU`3jd0Ns0wy2nsPGC7!sJa1``_u6N=a^OB^jY3yqdzG4jTlV?lCP>IOEMg36Dk4ARFhF8FT;z zNr;=!ed9of?y;=7`cP1>C2x+^s~lu@0vvfjSW{w;=ri#7(K#-7&AL;noz+g3 zo0&l-GpYy&Je|=Hdzk!DgAnlaV?tUDKwk=I2d{dnR8q#*@P|M}^9;ARwwvb6Mm3rJ$P~D8o1PwdF<{2#PH4cN& zl6#;*eEaKa@Ap^S&vYD}_Y4w3ZDWT|Mi{8_JVd1LB9zzDZ-;0x8=wQVH_A10hGN5LckFRt>)Bm*}rx`oYR@Ni?yh#mphU?@OO#hI-2v;+xAjADNhb(`=AbJ;EGu<`4IRto8;s-NoE9QJs`CfsStE! zfy{DZnPqwaU|=6VkdnIgMfs34*!qLzqMi|L>u^bxh+L>5*45L(NRw_Jt+_+Cf6v>f zjx8Y>ASdh^vOAQ*=#3?lv%|-SMuXTS@1!b>1IZ0oEZIBmkm+*_#4cF^*A9qjHyr}Q z!d9x-HnC&xJ?}ZYu?!=pNI-@LN}v?t8@)U1G5uMfncWtI)8e2izGd7*+}9KwYL;$G z7^JCf2K711;v`pUC_}Nsv@A6#x5hEld4G~+3UMl`Fd%(^nHSIZ19F{QLm;nRDuc-b zIGSIBCZl9n$Sfq>e+JXdJ?11B6?}YtXa>Yl+cyrcx)aQMRGUkdxb2sh0de#zk=< zQNafUS}$I>osu@2x@)@87LK|}FD1)HTFrT3l2T;`|E>dvmy<)1!AKe(`d3mP3%CL> z*i>;FQA(T1Ei$`Y&|j}+aw!iDiVR;+>^X{}D#r~#An}&P{_-&gq=7j~CUh6Q^7SOE=?f~&6 zo{`f|@|0Ph4>>I2lyy{jN*?^E0bNV*S_|(Z?aeS%Ff<|kc3n>Nff$^>^&|S*WQvOv#wS6Mv__R5<*r=Y5LvYKliWm zxR3KWkMp>n&wan&ujf-`NXYIBi)F6~DG4y7%;uQHpU5c?5&_a!@pp5MQt55Aa~9@ee-M;rK%kIKC7w z05}3Yc!OaLne-Q!qLHPX*itItgRDE6X6WqcB(AtYr!wmmw)36XzL zr;gp~TH@Z+^GJVLjeQ8UDE@h>wKHwOK8^rUvHYB(r=fhB7E(8H* z*b4M_ypI`px8Ct|;;03D86Vy%_G~3~Y?~mNOZb(E2>*jpk+&#$m-zT}C{@YQNso=?1sq`Bwi21L?K{ z*3;|4!p}#~U1RdkBVry_E1L3T$YoGVWoGzBj^oGTHk)7n-Mf262P5fu@~Y?Tzr#;r zXFE@&e(|{o5@oQ)zpUc>*af$W zc7LJsizp+@5aLAuH~M47ZxfMJqDUuEjH$giCZdoeXP#x0Pbxi5q9Kzjbd#$*l53)p>!`^Moykq}$t}mpt;m!%-IVr`Bna*foRHEz59}IA z>6uTVAEyj#rVJrdhjmk5P*a2ez$f#m6UV8O$h0Zlv}upD*{HO6%QT)zpvedjyP0+d zo^FgzH?mCk_>#7XOyAN?$Dxr3EN}staj2W|%_HM`RK_tiV-^V%K;2`iG=q61f9YmI zJTqCMGvS4qYzs*|s2g?y5-55qyIvN*XO>`e7P2r4bR)`M3yq>BiU?;*>t(aC0=esctPY^-oDzZH;+nZm*6vM>O= zfDBN7?(Y#1X^ey;E6CY1&nY_3B`MFXFweUy&$m9$Zz0eBXI_A3eqeO|#lrlc`uyOo z{D3ZC@<^85dQR1GjfTY6%v?7qp56E=@RQkfa`2vNlf)~-nDM)5=AL2M!Jh4^ml*uf5mDEkLeScf* zgn%w6g0uCA4N^?II#A@~ASWO(X?-;DQx~rw{hp~CdGBZG!4|0<2NGz_nQ|AsoC9i? zkEvMIVhxauzAOWjelY3@)R+Vbp}>-pAPGDWjetlpK;jIbMCJn=9g1tEviLzf8Nf3b zpaubSH>p^5tbmmPiOZ}=&8&!Pt%$?2P3x7^qAIH$*s2^V3lG57UB!-*KywDfE)$r1 z43HpztP6{UP*q@m=C%fmWJSii5@IWnIkm{GQ=p@TB11iZs~^ml0kLI3kEC@BNfMU_ z9VyugkDcVfN)nM<05q1xi2+GgthK|`dZKDQ530EdAkR#QBbJ350gWgs3G{==cvU3p zXLFtS!72o-=EyLO1XwlJE%jsTVCpe=QvhO}Two4> zO0@EFGa!L<(2s83_U_o{{YqY!8%K$1vM7bMECD5&JjxFwHz3V{ZSp!%ihXGfvpp~i zbbxQiLx~+4cC8wK4M;o#7cvRC`|c9Jgr)>!K0>f0<_Qi9RB@={E&`sSgBq29DhEKE zUzHRC=mdb)v9JaKm^JcJUu61N#&xT;(GkPV>4kd*QmWbA;cRxdRk_*nzK^lHc zL{`r7Pq0Jg1EntjsXGsOXh01FNEOirKGjy~6#MLyj5z@slhCywAFekjN|aWS{Vku+ zAF+W$%F*QS6M=FBP3_M`FRdX^2j~Te`I%f6(@D5iCCteWW`~6ZXlmNGYc7QrFt#2H z=@m!o6~8hl2CV?b6brclKp{piHzPN+r;qI?lMM|ZoNNIY>{M0;z!SiFoWq*OU{OE; z$b|q9lb1ltiMD$1u8~*3x{C|j)!y~I2wr{G1pRLmiB$+E_*GUDpo?R-+KAAk1D4%z z<&9OPA0%loR;NB&)+#`}rxNreOF z7=Rxc@T(A{KMA;^uUslBZr8&*axOV(tk2vqHw+FWDfTIZ02Zzw1hGJtQ^{K)$$(eq zR%Z2eF%r+ML3k5TNCKGLZ?^Z1HT{6qrsozI3+tw_KRaMW5&%wgmgE=Vr?k*3D~&V+ z`=d!#X$-);)l{9%;&gD<39BZN3fMcD{Xb@e%|uex@P(Bhtc^Tymbb?+It-|Er7aS3 zVijmz5eMQOvhW^Tyqj?HJMnAOu#$ordn^no5T|qgXE-!K@v`5dw~7nXD0n z8}sJKhYa>E8haPs$+Y!MrUQKU4SZs|hu{DO)1l^prZfN=ya7!Dn8o|SurLMGB#Y94 zB^WaxmkKaP^%6`x3VH_$cSQgGkgWjlYyqAC3?8n~0fU{0PY=(mOp`r13M_UJRO}cXyVnBxAtcWrP znTxP%V{jU;cKl^jnzS9O%59#CQ1Wi-AOdW$Fgy+X1gC|h`|5h5^_tWGn>DikK5|@J z-G2naIINk+tyz|?SwCB|SzfdIx8@+R?qs~~qP8A?G@-^NdH{tzB}4taoP7asD;n%N zz3Gl0ECk;Mm0razZ+s71T>*mSu%HOvPmyt-qDnvAdG;x0`BU7#PXviglJO?N7lb)j z7CtdnQparEw1E+eSvqrAG)mh;5RmJb*WiJTYtOb8**~$HfaDO{6~^0DzS}i%+jXVe z4bQflmbY8}ZMW`iD*+UAOEw!wb`do21prK)!7|tI7>^x1+XxOJY*p@jEH2$Sv%94X z*naeHXHsH!YHvHnc>Auz_Ke#0oZ9wd-zCp$gyV7m-VdU^s3wEG+Ei8eEp=yXdGC7M zhaM1U+<5n!@BVkL&%PXB(9?a81-P z#b4sj$II4sC5!gJ>!1Q%G7_Ep|tZDM)%$-iBb@GqbDnnj;~3w{5kVge{zw!PeW*@Ne( zWdg|jCmg}D?Yd8vBM8*AG*0*)EqAub4hU5mA70u|^CT`SO+vh8Av%)VT7d0{^yLV< zHKT{12(F`>b|BmZ(5GLN&U}iyaBz12KsD~M<#QF`*Wa=wv+d4&DnBia#HVkZ zlJHoO5@SGG2<(q1*(eyW{K1acB&3lr6A3u-GzXg!z*cl%^q_#G6Etm-HH<+v&1AZ$ zp{7`{2kN&g4P4H3Bt!SL^#jo#f`_?(=+l9hTG{fB*qZHs+_VE5V=SUi?tRsG{X-TD ziILn^LVdcGv8#CDSmWL=KO4x-^RFrUyXlf2c8q_1zh0cj4H_B(T1>J4z6_0VDO*f> z@Kn5DyD!jk$7p9bg@c7pPQ`bK`bL_St1f2!@KPfyOgF7}=S>cUEMahdN>=f7+h~hV zN8}dpR6vHfw912d;>B8o7?)VXCIdxDD^*SN)>f*lMToM5nKpX~G$3FpT9%o?UrlWb z6gd22;>!al=_cnp^BsVd6!%W`;9d-lX@+YT(969s=1*bk#=D|}Ckg$DY`n7Ac{=b$ zz*wH@g%`!1UxOy7Om7`e|CivYYTGjN60ajBW!UF=g82FGVeg)tT>bE(D>_?+mCu;Q*ExP z+o~rVfxeGv4IF(pJZ9=_q}j0(4NNcdSfwS|F)%=|b`gOrr=2i$mb+pMzOLlBJwKu> zw&8(Gl9@*Csqjwy3QaL>U?{b8J<}-9Fg}<{dB~$Ije{8#A&HQIUgwq8A;ByEuS9-s zvXsd%bviX^`o7spp^Tzotyo*tX07b=dG{$J;p?8RCgqsSW0Ot~OxHseR&wfL&_uAs zwUBB-z5h133=Q5_4fc9FNReb~#ts75IXW^cc#IkVVYaKs zEP42@@gc7{lgHT`* zxw65WVwgvu1ifyDfO1|eYLMU4%W*viW#0MLj@LKpV~$M)!}=kQ4bnf}`)?%>A`xPA*We0w zfi02wmWRnDr`*Mh&~`5eivMOkV%DUDfb~i39VgpY3OIC_jO2~(D-Umo-u~~V>ME8} zVM@oUe8DmR5QMf!KV0J#d~l7hzqT1)p>3@{m-#vH4J09j*YIK0a})2t{b#H1ufMqI zgM*2?&EFlqsD}Yk9^xe)%~kchJ(?R7k|@&U4ftmm7{qyZHbknn+J-1pWI2#1#FD9h z8-kqoVS3FEVsAG#zsMhbm|Or8}RSp9g;L z<)`iD!z&&*X#b46B+FZW%X5xDU1=*S-|h}Q|NFzAdLiyO`de0jw}NT|C!JlrzJw_XObKhPFN$fOF#@|+6 z98fwO*vV6u!7xiJcnX!@AR4yW>rotG@pZ&El;7EKkDe_KTc4*{wrT zEq7zTT^s>)8R0}?=GJAU9gsyKTOKSVfjcHf-JKX#7QS{6+856LZmqLqXtE* zGAS%oQk?2E5p)V(cFoOzmjO{m}lhM2az#iQqQ*rytojdXwy_s%p75Pmn-V| zm0JC3CQ?1%ZRwAp%d_pH;}`24WGG@l`U^b={I49^qH-#agbR~B9FT@+Yf*Is7k zbeYg_uz6YgYrWaTaDXxs;NhG*Do~B)YFfm(c2C`q{}$3(8Ej*4(J^gyx<=nwSv_e%>X4qX3=yTQjt_IyMSsKIJLBYw+Es;kx`bQJi z%vyLJ-9P!#JW0Hl&SE>@Uw;YP==IgawR={eJW*4@=5e?CSlCSCJ!q3rzjcR3D^O7e ztx~hhh?fszVsU4HCt~T{ZV=(=mVzgZuGdH1Cw^(QmO*z#VJT_@0{7u}-VptbJ4jEe zzC-mH>_L|d&X23*KE8Vt7JRGc{6^aMGJxpX%Cqds1;HO}Pi8(}eN=RQS$nSQ{MY6i z)dovFQaVM4{L9*vTa%}i+)uxXy%sgx*t{7e+xf}t`kk2`-|roBef#m(@0`xtF(`1c+x|c<+NO8ojnRqkuvmWb*Bypb>I_D_4;$( zrVdJe$0y#Wd{Zag&Vb?+G%KHxy zk+(Sg`u0wY-@apwo!8)cL4X)zidW7*>n|U+#V0eVz**PcRXzE&6npRKhZB~cnPB6C zTOY7+!$gScF9sONFNG+NibjFT*AgwfCw1!jc_E;lR}q$UaTyAiLTLKyTH9Wq8LY(|3(z-^)qATFNGC;kV05S zfrenQU`dx%fyVi>T$}%xVFenip)QF#J(Db>DRp1>A&x72f~8=_4|YGltxV=Q8J%M| zcJD=ZK^K?xL-Nwsdjh|k*d_LiKl<&(CJw1a;ZiVlG`J-pcBhPS6E{fXc}9;_>j02d zZBC$=kgHalJFWK>nNW6D>Hudhiv9fj5Ldd6W*NIg#Flg1WXwkY^oe)u7T9alVgIP- zVR!Vzy+2<+aT`1>)9Kh_jrrZK{cDIQal_*`gEa$or{ALdm+xc%zj{>PJlUw|u@<(G zXmS#>%T4;OGAs#r$4{#Ns_@y zL>c08j3qOTiywFGUwnG-w)>0B8SZVvZ*Tik+4{r|fL5&}F*>Ew8C2>F>NWsLM3K(W zi89P5)Cok}43IvP&u>D#8S%!JL;yb)WQse}NYt6k@vsChf2Jr#A+%e;27cfZB(~9f z9&8mt5!WSI2J?F)LT>)X_I)N9?|>vN+j!R=i_ViYbf5Bj_w(f%Mt*!OLIoL7Nya%i z$*O+&cip05rDBWyw!O8Jl?FD)U}bo`njge!lcbjkF{E-i2U3a$$!9i6XRu(aOp0_b zQ5FyPo+M%Fh|&jUvQd4~9&MmMj}yTZw_vD&L$6E}iT(gY{|_jK>Xm6F|Gr{A{2nNW z=i!Oz{@g=Upn{cKN!qO>5r*;bm1GShZfEd}0-TI-!0~?T$2F1ita`0p8D4mdvuP5s zT^qXBByVpBGDi=)aF}@+f~+!!&*<_YEl1QGEDbXu=7|L|QSm3O9}mKWDqe!n#6S~&TV{MSDcpH1f+lwWQWb+q=RznZRM1;?o!YN-qh6vW4s1Bz zR$OHO+Zm1O1)||#IRq(O#=6frnK<~;x?S$Q2?6#?J!(w+Lq9-rutcBa2o;C;# zbxVk}C0LO`8#frAo_>wk?&tfa_x7Td*T=5>S7+Kj+Imx=78p<&xwA}WLWDrJjsmU6 zkl-%UCD)s}sP^E3C>cuy+S86Te);SliWahTzRyslkl3`wC*cemHjs&{H+ za7tm&aQs!HnG#ss60C0t5f5e>H^}F#!6#~#tOgi)ES+G$45|NpqrYB+f4<(^3^Hr++3XZoOUuHZg+0_VD6q-8lg{n3dKAk zaFU?&1RsxkS3QVYX=HJagc6UF`^_GSs+>tioM~wu$x9v?eKoh6-Bo24vb7d+T-)kp z=G4dCV~sFQ+i8eQPwwC+iQ|AmP9g`^c!p4< zH4oqPESydXye{($>8mj?XujEU=-y zYHoRv`#kH-Je+wK@as>C8Si;E-#ie4w)?uyDswhVGkOo!)<5s zUFQk}RJr|#$VqP@IPsC~Jom`rI43c+nt;F)$1ednJ3HX51U}s*B%a7O@{TeN5RCE> z!U8*PrNZgn^vz&k3Xm z_%gqa8{@lXwp3s3^C^CbDGqVV@!dIEd^7Jmuj(5%cHV!T$cYEQTR;3B{_tn!!{7A} z|BgPKKpBAVAJ{F2@`l~Vs%vBA%9mSxgz${FxgF605&utbhX z1`-SGTw0oi`onA9v(zwtEctNL7%;abcH{qx+$C)1<3pR2ym^ymzP-M{DoQ> zf)W8ji7QLPOy8exM;d|c*iYUZ$Ywdue&7$+mRL4gmF608&y)F{GgE(MG>hNmLf(^~ z@q`oNRV^5vpp_2LP6x2z2{78~H1*@#D8S1of^N`7wkU#8&_}n*k9sW^=Q2N*j9+A( zzo-{UV5NR!hwFo%nL>=SLuZQM9swe83_eS*dugje=D>Tg1h&oq`0_`16oFkgKsu5T zcZ>O&_=|K9oZ|>KHGXzn9&54wJc5_l(d)=#f?M224v$Nmh#=T8!6S&sM!R%A&W}I% z$-P@kK3qVr@9V0@K|;o>9J)c5BN>;B*Wr_wY$F+bgbV5~PGcqz*_Az`lmTANZu;pi zt3EBwNDhhcrg+v+`8$0LujL zRAsmK6>A|*etaUAg`@oLz3?O%ukz`JPTFo)N_-Lu0@fO@)){X*Mf&k)dN!4A2)Tt8 z$8Mj`-H7X>s_5b6(1<|={yh+I?3gQ7eBP2--QVmbB$CIs&AsUthn`35Ai-A5?Q=|SNX=g793gOz z?4QgM0JST?>(^QBmwx(w{%(E%`>^l&egAjI0XOUbX171@bD`Jl)79jeTWY$J9%l`x zAZ;6E*dj=75)2pX_dfd(LIAnYz%s{>dy4Tklh^d;ugT!SWGRR+6#~p8xuGfVN-1dF zc>58OjV@GUgkpmtJ9oP7b^uH~;?c(ho4TzN8$8*WM&=8Sx4{xMo)dCfK0;7rR~i|O zf|{VfP9qRCdbsMR@bhvM^aunr0&yXbB&ietl_)tvL1C?}*F#2|IUpnBw||u|tlVr1 z#%O|4s>Vx_ge4^A6X9qBIpTYNc;z40rz;8DWk>D-603ZK_i?cRkL7iTiFIM~TM2pi z{UdkxIlqb{cd3nSul+e^`&;(u>#Ij|qQ(I;NAAiJt0IEykb8bk3iEP7#BX~?nYK6L z*ggtg25TpNS-qI7$^aUm$qKDxCCo+hvmeP56syfcD=N_-gXlDJXk{6$`Dw=nK@c3F z01m#YD3Je)1nb}_xYrP@CFI0neh1NdXs=7OX$7#3P*m_F6_l0*4Xob^NIe0}M5aRE zB(vLOo7S-9j@u?Ehz^Qug{LIU9doya>)Zwd>DSaSLnRa4r9qD#Apv|;@*`c4vI9sf z6D;N-q)cZXRQ6*Z2R46UWC-3`w_SBRy%MJLK_%Z8A-Hk`y~V~7@V&%eEpF9SJ@m^` z$E)%0+nfQC=0~Zw4updI+vga~_g3wVmpCVwR?*)N?LERQ{K@P4Ft@whg1^^oJ^1z_ zg)@NzPirKao&ft|kbzF#ABn5C2&lT-7WPbL@HaGyjK+gaM#3zxHy8c^mF|;Njwy<- zL28|c_K74@UF3sOY05kv);HEnmMRqy{Y8*pM zMnG?$$0|@MRwJbNm(nhHu>MFG5Kq~D&1N+Tq|NoRAOV`2ARzkHXy`{# zp#BO4!L(@ehu)BuG|NF!|XU}XRTenCx%RNBgrg`kwV z(l`7-d008-In@D z6tOuUx)Z!pnXtmEfHlz?Xr76))o5W{@T_7INdyOOuX(SYLpL*_J_hPP$-Ik5-L;b zVc##2`2@{mJ(Wc62pTXb#_VTx+ke{aDvz!7--oq(x?O2*Rku6We0DOjN3~SLTh#+ow%!F^nshSx`8d_&9OPT7C0~-j8~f}0Po|vZmnzfDEOMVrzY8z9 zmV2QGsXZC6P9?f}Z1;Zm-7T_0_J&SeQD3_CK2~QrdT#tn?YCIN(0YyYiERum(YUDA zFy((u=TN82Ts~UV{|#7Evc2ZKei_%2v;JUkTF~(_TmI!~m@CT<>!Jp7C3TMME@Wi5VLb7i_ z7L?KRx#)hh{*5G;mfdfOPB3-D?7Vk?@YQUhTpL|Y`wc*J>G**{ zM;67cY^2G`rT%Y2!EI@CJiV{Zq3dwskHb znpal9s-yt(b2Q@Fjgkl6(gy$OS-*Otk@qzft8(_Z|IWL>g5A$mXZ%49apowUQp=lc6V(PtPU7_WXTkNRe=3~)dC9Ut z+4Z~L(ynI;hny$WlfBIX&`(6%n$le>1c?>mH@Btyv`?_q!72&XRi(MEsV6OweagOH z8|L~BpFE8FtL#^+@b(%L1tJXn;D5E~Y;l8t4LT9X9*1`mq^5x~`Xnz|1$f?%YiTPe zxe&x{Lgd2>%I3+b2HBr~$CoGv;=}I#@o4m%@38MsZ=heUz5`ORl70H^{jSiB{}zxm z!qb7iva9#a&;Jtmks>&m^xFYT%r=}W7L?kH5N$l&(XHhxm-zX1y~6SVNym8OZ;St! z!Sd6#r+s^|>MD0dE?2pu0J>V&Vdm!Z;v{@_!t{16bl zeXP9Kn38%A2^PD)Cc9C{o&oJu+uM7CQS!jCx)Ig{>S#>*et*_FdF`ic*Qk10cy3{F zARm=ceWOvGI;gU7DyQrE;?P5BQ364pX;9I}c=9dxUSQG{fauDv_d*2yq&}HRGe-nH z7j?DTXebCt%?b*?y7@>>1R8Jr?W)AIZc94AOpmGEX32IdP&vLiu3i<;7u2NJh*s8;P&yb zROuHnvltm@h}!cniElQqwnKEW>SHtKm%qKZ1qoLL+PM3I3Ud6IU0RdQn=M~Up59(e zQh6$rhyfT(;$R*JCL-A_Fe5yXQ+<*wv{$nKU|D7D%6Kwt@Te)=F~ys~#0IlUL_WIr z`Ll%1U8~NPqm}}rosXd-;3kd8#vgaruAk*&4JMGUHR2&3pv1dYfa5_t?Y-rO-YOsW zwX(PTm<_u@(M&ba?e|igR+EE5Cq;~SerB~?o&IB5vHg1YeSHWm-@3wxL41Sp0yoUj z4=rW{k{01^oPfLqh#rT>baNC zPy_Z}`9T?Xn+lhIR>88Cw2FL@=>GltH}qEYFd&{q1}oA5*bI3MMAVi2Lt%&lrdSZy zTJpbB0-z6yr2sD4-BZ4CPiELHalB|cyC0oBkpqtF$NR@;bfvpQoIuzHCRk_PYHWwOIn6dF{k&pn$+*``U&P^PAQyoSHzbNI5(F4P z5p=&81IThr5TOA@#E87@KyE{#7=tKej1xV^3V9GeWD&+7C?HD(O2n_IegQBkT%rhA zsbkI%Go->9KV8HHjnBCfU9H02;G-R`bIv`Nqu_S!!xx5Nxo+Zlrg8(O>OUPqT&Lwl zGEHIvBI-Gf)3Ru0Q~X&F3$Zu$e}}^VzQ!F3-WZiu5%E`za~1Sik=^jR^M=l|_}u8Dle9^A3B%5P;xr zKH?G2xo@)yW?5$41c&vvXY^zZeWV5B^KmwTXP+gWpU7|!{%Ak?_OGS8`vAm6EzsGR zqVa47PA9Mp&F~n$=kXvGdk_(+gpwTq#~OfZ%{q`Nr$^Ut2fpX2nv)cfyDTr^q;ygp zup+ZWeB&LHPP46+`<{b+_txjxTVxs=l9=yf2??zbFjdWRfKJ=dsv);35}K61UAwE^go;-x^3<7VU0#VlHmDwS-_CkIj#wtT{ED4cIKrjHU zZ|H1d#G0x)1b~t3Cd!GqnA(6&ksziFpg96JcvOO7xVD~-E5yd-#Co0h=-Kk~tpu=a zR>&I*DFhYZ!rbo3%PMwLeQIh%j2v_A*3;!Ec`x!8_+9c>D^As{=4c>pJ)-BzLof*y zg#m7-;_U3T>JyFGlR8}4X2iHA(J5>|jwS-44*Rf>UY3nbW9v^7+%DU$OYTNjP7x4P zf*-G9O?F|H#Fn&NefAw}peJPC#h)X08`3|K^yW1#UYb;|WZB7Z>D=adk?}xj8cL)! zKI2L%RBAM*HabaFsAkqrC;?`a0+&L0ij=snQPK)S*#?Hs9c-5$(Hcne%F-EuqYw@G zkEM}ww||QNdQ;pP_xaTm@Bgx{bQWE?%X_J&C1htd-~|{OnR5pH*S)oi3dD*c++j+`R*nN}1~_ zdDmRh4p)5_^52`}k~eBsC$)EH<(j9Ao8KGJ`{h$!n-7)#8>VS2#6&dD#i`CGG|#82 zF61{al&Zd~X@1wTyKsk}1#qrcetlxrrZ2%e_YZAazj?Yte1b!D{z!HD*X}}E38Th` zA)&S+s7CtvVkSWK6&nl7;!c*WT8^vQx^K&RklMzzmW{|0wNG&^pAytI(_1$4)wW8n zZ57eq52zN6?k-N)EFF5{UM(jcjqLhMZnH`r+!*>SP_{qgm63XXJL%=udf!9nhkYXF zK3CcP_Q-dm(SsMI`ztN|^852D>RQop|zqTKzud1CcnZiR4{qLP&9nLVmXs9ew~WYSvra?(1oJTSX+d zgtU5>pF!czwt0U9nAOXp#{&4Bxw}d*^8!ER~3bR z)Ofw%QIpEmSLJaxZbPM17;v`T{N5&aVdhHqkm~grg%ZNer8bZ7sg7)k^C7Bp6>Zwr zPwU)%taEqzc>ZB;hsML9mPj)K#VnsA#?(ioGJY8%xN>q>yd91`Y;$q?DsazE)qM^l z8n8g=4&no zI@jr{Tyb2s`P3zaFJJSlH7vek4AZR7yKSD@xas4Dz1nukLg8juz$K}{2>$UywL8Jq z1v#G86M|COz2ET}{?BPJ&*#*U^Gp8Tj%(&uzA5it=O@?2{|kt(S#{?TYERF~ z|MgaFBS2z~Vpg-NxQEPJKKK*7g>F+kSKDq)ex&iiRs!{_9y!zudnwsxHC(Fk zjKTEdA|}3lYjQ{qMP|gOMUrpihp;zZGn10H7EpUWaWHV4nBxW4En2LHm+KwRW24Gdk16iD_Lk}izjbZ_qwb5ozJHG} zWD<2VD(u)vrAW-G+LeE0Jk~SkbXTWH z!ms{fMj_pwe&0LmvUjwLH*1YgA(k^2kUuw*_QI9=K98;QW6;N^31JZz{;_l;7j4(> zcUoehlaK*d>|4W>lIz7U9Ra1M#Y(O(mHgLL+T&9udWWXK3gzf-o75Z7(yut%-R3dN z_P#aE**@x}Ym|1^uPxmpbU!DdkqbdmYzIkUagndZI5h{9icJLgeOWgFx$8^*n}EA^ zKjD)A!CV|Kzpq6awk8wM{1(t$cEMY?=T%U0>!%W~oeKhqASIi-!m`m+_MaKapT-4w z@922m8Rq9mjDET<{zClhUY2~j5<1S2|(|H+f%^+g^r(?x^n$l~_3 z-;28j&k9POzkl;g=no9DGxM)H^MuHY2Q>cKV$-dubFbwl09YV!nH>OplA!#J^zz4m z>L2}>-3xNZq|xUf9z0fP@T=N1}ijar{P) zA6t*OCN5jFp>uH2Ap35?6+T#4zeH=FWXo?eozkACU2)I5cD-R`pY@K-lSVjIPXet-9YS|I}4kFjDSpyefKPzxEaSQS?qZcig%%rT2boL`F~zyPgah$l?K% z!3>BwREk-GWN1Kn45ZWQKIrNDGNaB82UcZyu*ZKufvBFh2 znO33%KZwbc5vGvPO&~0#ZLr)lNkrD~70;9zs|c5}UnaJW1mV=Zo0y6&2PNQ=lzvg7 z{R1GJBE0_8OlsAnAeW4bm`a^VJaaA-nOcX!fhe3}VaxpbxxtQ=7FqJHp}9a71W%NC zo_=1aYoq(4pmpJu6O%+f6^E!yz^zaSx58vXC)O<#!tQHNJ9Z#mb!kCQR!lnFZlpX8 zQ9Tgq`)bDlCQiK0LoD<&hcllO&7P9eeUGkB=+zTkD&PF!lruSGFz1%FFhnmaQH{IbOaRKvkwZ zqp^Y)ax{4nBE{Ap01^N7HGtb-a~_<|0()1%>*!V=NF#B10lE1wtraOuOmH2Am7Q1# z;nh8?gK+&iL7G)3+nJ0|gl-Tn8i-taS6A~O{9S!*8OdvGRG~bTSE2UW`|&a5W|P!- zr1$1jJfeZE|C~^OA5ObR1xx%-?V>KRrNeMO+g8Z9P8q~D^sUY2#k(=Aow_5QbK=Z@ zy-m$*)6-ueGaZLF$JNdvRP43K+fR>MT*MOJJX($UcJA4M*yC4E-gPq|_Gb~(YTPe& zrW|?@!x3+k9yQIl^{5?i>aHXz10ja8A(fow|7bwI1Z37Ao2DrM2-d>M}YiQvVGTSrqXoX8c^Yt|H4Cqjs^EtQ!U5*4!ZYLlPY-Mj%`i^u$dU|O>Ha8A3S1o*TJFkY8y*8>qtK#qqE`M-md$#h*tRA`*z zT-PtJO!7FNR=#8YQz9q6{bfZro*hNY!(0J zI>$h&m6+5JJgGPB@32tzNMdKK)x2Hht{Vg3pJu7?beVDQ6Qep->rPhZ;^i3DmBrW- zb^myo8i$G`5pDUFC7DRs=nCH-sw0&2K(@G-g|UX}(bBh!c@;`xg2Fd{ZrL{kd&#}N z=Q<25VT0CPJWy(pwY$-fM27*WPy73tuao`*OjihYz-`#BlirIiO?}s_xzwO`LVmZh zXo6LrvBkp|{g!31KyGck@=bElgPqmR0mW8O(qpNQ-wMt)Ib3t68f2Ifbc@$jGYqjR z(lAq|AS~4)^3iXfr-86xUMeA3lbFQ+fI6(0|1!!)1r7_vR0>_aY>o-$;fV1V5)hM3 z9V%aqo4J(^fnn)k4sar6IxiY*tEIs@xokRoCOmRQ5kjKcy>rlhOZB1wMzOhTze& z|9#mmRK2xzuFq^(XW(0*4W;DN14~l{!Be$s3&Q$&RTGh1TEX{B>;IiFJ$_7{8|z|izaSG>lL6g3g2j@0U>H99X@LmS>zCw3r6 z4p%>)@$Bk%4YzJko3DlydO7h*M8`TR5F_y zw2pIzZ?xN=7MGXU)agFw+`AU`u5qz#!}Iy-^~X;&nj9#?p1aoyde(ufw|g4rN59)- zd>-0~PN`Vfwrc@N9jLH%26Z?lpu(Jz-okI|Q5_Se)I|JH>`u@Vr5|@jHDVm!Q8h0y zeouS_KNfEF{`kJ`#N51J`_(=J9R(5_yE<@`HUx}M2 zXDSXqNGN!b1-+i;ufH7OCD>;qk$lHJtPw7ID@)`6(`0x+zseXjbHfmMuv1HoX zX9*2%IcwaQ4UCm*c<%L^wMyz;qdC!S+10%Q>i6hii)A+Gi(;b2<-+lhYll=L!g@#X z0{9B+S12v)@~QLD6L;fY`H^3(Uid!w=!G(TFZDj=!){}WKF#A0UmRk4{a0~r+6ReP zu~3wP$hlYI8qIb_YvMI`W*VMZH++G8cKaViXW12H+eYCjCdi>1hDHP&x^?K35&x*&96DYhyFY0>T@rLWKYQ9QNUUE*C!DH&O1dOK|ikModv|ZU-#^0|49Rf zm=fK|#{(r@ONm`Iz{~_dT+Cb?8Oho-9h%HN+2cBIfnmzat=2rY>{ z3Vo*G?^?Z&u5BzWx6LplYL(d#nz)cw(}0oW0mrkWK(os|z->{SMJ5xKlO zBR;qI=JEzGReztZBTqF>rf1)x~Xi$XE%a*sTgktOeaHda3|mhsy;DlfoYdEcGTD8$2)71Jk~#-GqJ%7;PlF znYs=n{VS~f_x?=7qGO}dZgk;rCt;^uRBs$MJZN=WR#wqO*8TDf4PL$5` zVs8E}v+cSnq>H`=Xv2kMt&XDl!z;7B^eo~`D@56MbVd^~u423`Z~!=_RuQS~8JHHc zHC#^a6%C)5#5Y}Hok{mb&EGwz(QKx9ctT?!Li^Z@_DM6XWbuRQGg&GZHnw zAx&f?8YMR;D(7{eCmRDlI9{~Up{drWT5M*$4TRT4*tWgc_L|uaPuNaG*k3>r9h>Q% z9n*Q8fWFlhtHI0h%`Bzq&O2IJDB`w1Gn791^`Qxj?9Sw`1foE7_4)|PN?WEu0Re&* z^O+0P-gm|#gXcr(+Pix&_{0>53i4p~{pd`OwKXh|!0RQ-=jY8A)WR2b$`>KZALGrR z(88bO4I3D(UWx0Snr2_FC8M|pGs*Cv6Rid8La^Et@9QWZyLJc~WW}PR8qzz|472IH z1c@mdAcn9*TQ`ji%F;BxKgdS9-5Bl~5}>N4aQee=eC-XxF;A-sse>rZNCM28fTk1^ zq4g1AY!zWW6X6gOWvcevFq5>iX+IL&qQROLx6=u?ov1{gMZGfa-XnNA&Ioe5_9~zG6Tv{z06s z0?EO@?&ym9KV+UhM5nv)PM6Txax(I*xB%wE zmf*y1Qq4(0buhIKA6Kosb$UyR+AO8YGZmG`=dvDnL<#^#eg+*tKnM0zmc{j!vs9&g zMn0j{;vD6_rVaR$9rz!ux=!iOlNwMwHfT#Rpr1EzIQ+~_YslVaDCB1tCA7eT8JnP_ zpUXmPVjo_-6%9a9IVr+)KEcl2DZXRL>RLtiKD|7CE4ud<>e6QPI#VxsNc91^{sU^% ztS`v;d7SA>HIoVO1KQfaLGVxHlTtC>n+HZlnvYF5&Y%@I3a53tKewy!+Xf+rg$V~u z9bU!9PuBxaE?Y{OMNpZ{)(t?x-0?6SBdf0$&|(~Mnzu*@mB@=z^bM_bkf=xyt$c)E zI3#;3R`7(zZ?vh-=EsnP{-N&MsfF8s2) z(x_4V)TC-()ET@xp!D+^Bz(iEC#?)1(#Lt+Lwd-Ri>rV=NdiQ>L0(0VF8)b+hIyc6 zJ;Z))prsqpzDK%jc4DUuB9bP*(3?KXq+;R#;rsXte#bkK`Y{ryXgWNGX{M!nOt`Enf735+Jg-OA8wn<7q zME3eR5oS4yxZ z)tGr$!;T4vt*><478CmO+5@?F)Ftg6oK6`fQu45B`NmJ2B}atPd6&@!f>NRP9yvr(Q$Aw71|2 z)jw>LC!O~_y9BQcf%Ido3x4OW;|%eUNKZ4>&PWkS18ovk5rIyZ+{c&DM_rPt+u6yF z-h`jwZjde9c3oMt3(67FDq$VBvjh@iCUNo8U7;fa4g< zW(X8pII=7ZSvRim21m9cLFx{q4+hdW0{BYs6vG7GwZ|gQtx5Fz>8v7DV13QnC5RHZdSWV za=~u~87Yo2s@&@cv?2mpV2V{G4z&RJujfSKP?j^O}tQucL0*Nrb%8G zt$SH8%pcn9J@X#?&sQ!intR%K-im8o`6MRQNcPMIk>^GwUY zg|B{_GL%B&-^*-t$@Kb=_}UEka3BS2T7<4}C%#ribS{TDV&S28`+k8pYbI6!09e{b zSD-&Ei$GBfDDt4n;0mHx1iZZa1?_=C{J~MgGKjeaSMmjQhIL~LPCNG`Bjmig#Lf*0 z3z%ibdcT+()G#BxkUbBGsj0p5TYDdGrE!X#2xS*a&yA0{J>mXOHlyjpO>K?{R2lbq zN&b6?ZYrMGn(+R=Cy1Q)#KqRr%LI59j%WBvL9h>zNAweVD`)3T6=1 zF@5;J;#Lu&lgQfe^{VC){iTT4Ky5B}yT`x!!k_w)Zr@_k1Ln71e?bpuk~S>^+N1-E z0%7hK#&U9{Fb-M$3#XnEf5r2UtF$yvlJMB)we$RoKk)*wW9MW!xMNnGdZ89QE< zThf-BG*AEJ$x>tfu0+d!(2vFWVy|ZpC;O5d8O2OU?!TaK9l9qI@ES#K$U&uRL;4*x zhxljVVNJ5@M>38lzvITFm$Q#A7=!x5T>+jJBvFfQJ`H(-? zdF8R$(p8)hOpm_Beyr5-Spg7B)P1Tos{B(!*&0nuOKdF4V9<}c4OhEcvAK!gz4rm)f6P*W^aomrD1Sbqt+hWs%NG5K)e)eV(6H zJZJH%RV-2aGiLY9Stw&tMYjD?cvb(dt(ycB?`E>d$6UKqg$$a07+Y>2XGC9RG(nq~ z>N6o`L=#?VsH>~Ft;^{0o;GeR2DTAz6<%wcpqdht4xzKaMmoHB?AcG0Q)E1oN0-%Q z3h{RNNl)bVPLqMi`{Tl^LSNzGsse0&5Kz%`DUZK6>?o*)h>^d0si?Uby7x5R(5xE9 zZc5;XdAmNx>%$IYx%19!lJS_E+fN?SUXq#Hrv51b1Jt%7iFHx;J%4@&B(jJIp}{a6yW zE_!Eup_cEq?QT75TdF3P4TCC$FIvDp}>lgzxr4P zi1EXS6Y8g6AvET#;tZ#cHZJk4340RHm=B2nxo(zscKz)*b?yt0ImI(tbg{x$Z!vOD zMT(aFI0oSB2_~jOLWt8i;9|ccs8JkIDC7W`0#F)8g^bZd4gkqfKnmS}Xd(H2nDj)% z`P;2}VM~vfZezvN-=Y+2ZX9NaIBGN?vKk%Km|^f?2vg{I$8c{OB?rHB3CYLvG@{}k zDWK~$Hm;8&XO;KTXmzaHS)Be;X5voY$lP4I{@u5_Md^}}1>o6~R@aFX;-nw7h5~cD z#Drsg806-^=`6}iFWM0M^Mje&^|j3<%SavgRcL=U#fTn6kPU=J?$r!Lkzp_ZUujj| z70*P?&Is~6epRstiZG)gTtb~iD!32_E*6h;W0_ zuNTGqRta#e_+q(c@jfBi*Tuf79~1@?^GIt>V4^iow**8DbR^%V_%S=H07F3i&=yaM zK8?fcMa{%ukEU?$3SNrw*&zW#H%Idt2Ti82i9aV0f$XO!%kK*BWDgqy-zUN4Ix39v zW|Ca7V4)99G0X0XhATfv^;7hhV|YIYEwsG}m~_7sXO3cj=@w2d!D5fuSDal+ednjL z+e^coPVAI}Jy+Nb0~XEzrToJVdzK;$1oY4H=r}I?fX_LIZ8BYQh(dYH1y24f8E2$v zD)jV^RGraOr5(lIaWD2tl>@=$3BU$w%qav9!T>gIacE^=d-+WiNzkZ@a7%jX%PsEd zTmT{v3-xS@nqmCad(yBP{CU~x*T4^F(ccmC8vDbBiPL^@5nOz~eqt^xB_pEczGAKB>+hzGn1#2+ZLgmv@0;(- ze(qgud-LpTZ=HT#uuq?;Wvj)WonKzbqAaP}!f0g6Sn^DiEb6QKrYjl7dQTpad<$&Fa0&ObZK$rp?l!-q>R-!?TS+i+gJUsKfWm4 z(-QruRqQ7y_bqfvhMoK*UORMyrm^$=y%ev6SMiy!d1C;$}qfi0|I{vzr%x zzlB2 z2)+2^nC1ms;Y1p}LT;1^>W>I-iu&hz2@|!VX>pW4Pb1+#v}~a7Z91*2p(WZGB@~I4 zl13};qIFE7wF06s{KEXWXukev@kPN4Z;*Lz)ZRdN3%zYwwTNg0xMLvJZ8z2f9_Pg$ z=L2WuMN_kN1Z>AeYLrKos55#POD-)){&u8%`Ce^V%85?RF&^j~og;x&mT_p6V2bu` zu!#?ryq9q1@ZecI2ligVuB0;mGrEOmKxq8buvF}$1e%$Ild6QoPA7)In^cP-t>Q4{&VkEtO0{uQY`ipv0)Hp6kO9vWjva9$M_}Wk!q}b7=obEmi;k|z* zX7Ke9hCxG%sV0Qww+y#+8Z03VQJY3GmPWRhMuAADypv9?nNFJ^FwP%3;Uqp)qcta; z;y#q(Z$wFrda_Hc1=&`kjlEyUlftfPlY6c~rH086Pkek9x%XS#bTFA;)5b5$kayd_ zbxSQ-+A&FuQTkGyVn^XIbx8PV7{ob^nVQ-lA zZbnR&&H1i6{m-~!2h;NRikzys{Ist;#vDZ2BK7ue{}9BB0i*RnXbiYi6U>VRn@50o zj`Qkd^2=&LwOt@IA@8+Je)VNOvt3SCZ9&giL0_$ile2>hf|n&o%{|X7EIZRn)71A% zhW{R0;GO%ycbsy!UPW&|r9|Zlwdp_DV~ehQHCd~cs*)x4gp&C2lhj&`^vefAv{`}# z{-SI%OD=G(b8LPdE%$9_9}CvPfY8w;(ziD#0QjmFv}be!Qb$1qa1cO#$(1t*Ee_H` zL3o>Rz`ByWodU+XQs(>uJi>cGhI437Wp30YTs3o3Q>xjiIJ+Y2DmTOLZ8G1w!5O^d zXN}E`Ptl8@OlW&1>3LZ~Lhh_gRn{_D^4@8A+~=O_oXPxsCqB(L8O-^HI&jL_WrS{`-`Pa$vU6krpY_IYByWrnoQte za9XQ$+JGWR90f7>lWpN#XNjm3GpKa=Ue`!`Q}0?A=dQ)ZDO*NN>#0i}-x&BnW+ZVo z&jbf|qO`a1yp;0XlFS&b|MJQ&W>Y-(w`raE#jS~$!5|bK*pSnR!hkKC8ZB|4-}wRpo)8A& zdRS7^(>wKVzw@-qmfiaXdf*8%8}$`|3t815$2Ne{oj%h_lt~8BAzm?x(#^ZFLKg$x z>I}Y`aDVRQXjo1a!mfe+i&ogW#^n^Ymg-XP1LEqelKJ=G`{ zYobb7kTD9TK11{Z3pK{V0MV+ur#K83ur$Px{6l!GaG zw#hfO-JgLv%|Ksxc0bCi^SIkP$lT$r&FT3)j{O+8QV|MP3KJI%o2uuUz8f~*!MUIv zI9JW?J=j%L|E7Ye68eSc7Uju{g}uPtR;^%`GtlozK&vFE78dG)h8^66`C$I@(QgaH zK}Tz7m1f!p@8X@!ZhD8!*?i^7K%g{|2|VgtkBlX)R6Xg54M+e(UFn!6?FsplR{|sk zm*0N8`C4cY8ifOzf!RYLa>#eYOn5NqAd?v$D$fRQkfHPh5Z&%wkU(k_6?xhqEDewm z5R|)a*8m1Cm~dr33!Lm3(&-J-n&3Jw4Ki>Iyozx98WQ^5Wx%Gc(rVo05mOZl0HkkL z>Cn*lXB_yPrOGd=$-rpeuSeyZb<@zNZ0Ik7%bT}Xz0Z1+~UL5I@WIM)= zy8#}2rq5v+FSbAyu#CrPGqx83J9WmDBFCd=Kh#h74xh3)EczGkYt=g9;98}m-Jq){ zpgnQW-3A<214zab0+TJJUBX=_;O@zftXt&xxsG^7^d9*EFerE-0a1uUSfjxRECT`! ziN#M9dm>D3jq3m^c55x>D@xr3qJT755E34W2P-!SjesO*lJ1pcg-X_|sgpdYI(I8s zpLXGHjqguntPqs>Fe6hN-B)TI4QFITTibUEPl?W|ODC+h&Yn_!JetcIS*fTM#qnUl zvex-E{`r4u=i2t>-u#@atp(!{xW+seX+j=Ba7vFgXUk&B#(U~@HUQ&!gE(wJZSqL_(j5!XOCbC}Kfju+AssyJYrnQ>VFV z0<~tYO}vtf#GxJfMegT&@hNF%_fxXfi@R$!;~5X)^JtCdvSTN>=(1m%WgFFI8zPo? z0ZW|;+4{*#J$L3GxtTov+Qo>1I`kndCZ}}LU z3f}z)2}461CK1Hp3)VQs*QFUR_W(}%GvkNrM3dlaSLEIA8ki@!LzWVIbLAVY2KNw8 z9*oyya%=IDS0vD>|M;>>rciP3{_4?S){n5ku4<}g=a;JPTk0uWns2tWi<8hOQR}Q6 zpfKUPqUX8bI>LP+7CEgM3eZ8V6Td@PzLsM+Dxv@aNsymekJce>MEh zwRXmNg6V|{VZa{egmM4pSO88Pd!E8I|f+1yMa8_T`xvKX;Az52C0i-lup8* zpIgo75q8wO^wpe!%eUJ5$GIdAcf+gNg%2ne0|ZZa89ZZ0x~b`}0P$hJbkru*gJ5S)cRLIBbEQtU%}JA0)PDS)B8n5dUo&LS=8Fn z`I|4ao<#PC%f;4kyuuC@j*$-l{dKS$c|7vq>3slgIV>H(OE@QDg$JRb@Z;m1vV^~} zjQ>_7+@lh1j2jY7B{2+FyJDU?K5jDVJ|T6l^A`WanSQBdcPp{+uzDo((0Wz zSNwm)AE3b*VIhr`fW|KX^>-PL4E#+$3~~f$=#W<^B7Yq8igL&D7oo-$>3R6Fqh@Rhjko)fXQs$&Oxn#9(;ICEL zw8E8VM0h}!D_S0wE)KqfTYON!=^k=J`# zyMl+2OYU}F!!#jRZi+1QI=c`G^57}iZ^d$>Bj3Dzg)v;2M&6pd1-_y}W!E7YV^G>w zQZR=u)(xow9s%WzDmaC6{#@e5OCTW_gc8 z?qE?ed%1Ia>M8|bUjsE}zw_AvTcve9jkx+2r7yyJw4=4%MF zjiYS9^+WC+<89_{gj1xI5JYs32=M9O=NBeVEr%YdSw~#7ek4kk!+}q+MgkFFa!*_& z=cTeC|8LT$AJcAahdF(&{e3^XJ>OsWTX=T~bzppL>HsnLw8y6(@6NMOLL`o{HUOSu zedT1n9RLFb#5%V;S@;uXGeL3*-bupIo%WmJ$vt~aw5`~};uO8zC!bB3)a+ON5K(|_ z0tczfSUp6r3AoKx;it)u5I)9(Nrubv?okNOJi+I>Pob<_g=X+krE6kL&V4)PW>Ho9 zvS#4!oq80QqJ~Y1yP%CuPoTi--;>&WkDr(OxDZ|@r{a62SF$8qTESZ^dxyzTOOd8B zG9=UqOmQOvwPK)vdhQM+a?Jot$>L6$%8U|DFoV$~v}hZ#G_lXu;x;xDK4)oR<-Yct zSm|4&fjr|@A~nm*9)NiPQJ)#}Nf8Cd_Znf1l3y1|(_e!&ygZ73 zQ7=)YUGB|NrA2P;9R-z_7hqYq^Jc}NV6I&n@&+uJ38hP9tUvJH8Uv;!;2t;LHmdnG zqA2;-5u`KolvHq88kaXdY})HDRgCfdEY|RXCJ2wAIzl_XR6S7(OD1lh%)FtGb50M@8rlPs@o8AcZ0Q$){# z?R^#D(JjNm!}w&-IDn+AvxfnH_YPUhCanSfU9PD?;=>iYm7`sG0mo>N{sAY+HD3mj zg(}G`nj?yQv4AVER-_1IkoQi7S1)o`=P9Q{-l#Z0tn{Nc*8qmZ6462%ev~aiWy#YK z07Xqr#cDfEn=Al^m^ac81OpnbO@>GXwTzcRG&8WpQ597ONQynTy^%OxZS`+Ts;fPa zwqs1~7C0JW39&O;jhXaLR7o>YsWQw8ny_c1O67Cnm0YjPa`r!g9JE%3k>89mP^JN3 zq&VQJcs$Qq&7CM>d_M^T3PKr=5$D1yf^SdMENOsJ6uuuy{HOUzthjx^DND0TegXYY zYA#Vy+wV{tWo+PK(GagQZaAi)iNF!6wW8~_ z=1!-FZ*5D>j6kr_Z6UY@0Or*1hhi|1WVfyovcP^jrzYTlSWz6X)9& z9Em0RI90`4>92sNa*6N$5;Q>d3I~rQwqDAC&QVM9V@&(LgKmT*ciZ=91Y|Glj2ev} z3%ENxGu(2S#>LOpTThNY?x^G650ZF|p8c-(S1POR=&0NALcpVIe>uJLF;uIe&hlH- z=V5}QHi~IB{?pa!d7^jU4?m9(A6m2PI{qBq$wsH=&Z{w5bh+IX(HxFxSirshSV;Ri zFF9!@iH6CG?!z}`cS)(Y0wt2UWR65X)UP}XU+mJd7moiD4+_M!a}c zOBe3=NNhrm@fzC;iMG+vnX#gRJ{eW!^-fO?wddx0GN$e5omq5k&(Fmu#l4#=c(uRA z3}ld1p5bxv>V(1=M1Tr2Do8{xv^bon$KKB*j(JaIqy0v@YdG||cZt^2R=9Ot&obJt z`Oe6emDtc2?S4W;ha=&AP{91es7zPwLn-smQ(D*ktoy{)=@<-zD^RCe;jgoPoY|*x zUccIhVJ=41X7lxq7qt?JX&I3}$MYH89w^!ZMYdWhmEqG$x335d5c89;7yveK%`;Xn z4u;q_vk-CbN{a^YJ)5-q6nwR_WoBLEO^5KV`aY`gG4IWZj=#~-%aQw6L0_~Y+SyBz z``AbEDd$6PgHX%{w`%CMtDS)V{{HpA!Yl~jTlG6x_%YT4V<1>4;rcJh88(4Ok_>b3 ziHo(hPJXU z(sUItR=KZE_-nxr;I_NeWpfMH!fjn>dr@sFsIvX?(RfrHJpVe}ui=^HzWKM{&wc;= zn&Maw^4h%zug*P%_^?DK=xAOjM?42A+Kh8ndReA9K}^s_1Cv3lZaO1(JUj7Y>etnVikC;S>NMlCIu+Akb<6Da!Bf^Vn)B)dYJ z;OoV1(ZDXjaQ#4rx!b6Kf>1dV+;NsJQ?;?LZx9Dx!FOp{nE;v)R9|{ zsxOafu|$GRfj(wy0zcBRKWAyFe)z2Y(WAWi@8Ump>iwSk0=v^+LKYkUofxfwVyFLI z|K-X)UXBB8-n@h4{)&Lbcds#HK!TW;@cZlK5^wL`-*3hy=H8F3!A8NwIK6T%p1&fI zf9YpKPd0=MDzujMM<{WID$!+SIYlbnZlL%?lo%T%vJP_J}0Iw+&&c55hmBDvwgZ9}hQW+4I>>gh~)e6cQ7cSFkJsq$2)$9x%YtdH8 zt6*jXjr$F0YU@LU(185$PGmf|7ozUT5{>j^&7Bc*C9Z}ui&r9LzRmK|Ef>{fh=hSY zDAdbDUbj+!g(YoUhgQT^wBC@I-o=C@xGCQq>qVE#i##XMKFlBPEPS{YrP*4q9ki~5 z?$q=gD)k^$hod=j{yZGGR@1)48^P~QO^DH!i@Z^hRNk*E&A+`oYs*njhPjk1Yt?DA zj&J2pxO(f>zbe3M=wtvc+1OZh%a9nM8cRI8)YOc+5l zJ$pUEBQM;q4vtFY~N1C8*O+NO%SCuHistkMm^DGwFh+}zvub2~ZZC8O1o zPfGlxqTlAq{r!4&?ybNH{<*w9%3+ElritWGM&Pf&8B3k|sSe#}GgfDoR%j07 zI(s){R_hLy13Aw0XHp#i29F|=lcEZu=LQNRwtNkbBb&BE5d!mC&VO3RCxo4&rev4L z{p!n_Kk_c#2$t8N^aCCxvhxV29?rO$i}Aa?kxP{Nflv@5n_pF)`)Bw_qq$g&@Q^7i zTCphtjE{$DV0k>}n8F~uC;*pXtAm#Q1UJ{@%7m($SfjlR_1%vemF+Y!I*P2H9K5w) zmYi08)gI>w2E|sYfFV9^rh70G>obM5ML7TQO!_DjZP`*XIaa2QIOQR*X z>!52?wVRU;GnN7|jd{r&_Pw_vi<9m*fWMiGE?$ap(Zt*G$(g9hwbEyAzgrq)q(pp_ z7=OIN4Q(0(8!sIX7GVH|wv|xh5RSeiowx*wwD(2)iAFkVDGW0glvy^F~T z2pay%y2|a0e8XwA<(IX3*s$i86~m(F^*(3!;VLML=6jzN=84sm%c|=707wH%j#3`D zA*6u#0b(mgdw-R4<8w|G0P@#4ycNn9fAN<>{eQmFNew+!h<^IK>*?PjW%;a;z4xp8 z^Q)N~t9P6k<7b9AG_d%`iQwD_9wb<^LdfzHaRL4vq!UZD0NM(*CPXA5jPEItfk6`ZZRSNjHhs zotTB!n$HRern>|2r}BFot*j3bq>G~FAHQIYRFsCG??aM&o|ZZl^KbOl>wiYSvlZI0 z{VuBUZPpaSOQB+!9ref?T4d@) zS*W`yyY=+*(HebH4dHq2I+JcXaU_KstQtSUtCNbRmw6ppM(vd>m`cwlZd0Aw%ZP)}xoK+RBNNrG#zwY~!c%3jtmqY>poJoQ*q>ad(iGyHx6k zc{{qzOFA6cy9vM4E3VYH$^dmnnoBweT16HnCrEd08yiEk0-KsdCn@9rURK}yB%k?| zeayPzZE$gPw2IeiYab>x)^*s4aWzu9dK3<=-m23L`&dQqGZC%($*tIl;nT99h2_Uv z_lCQJ_tdt(JOG7y7JMHP@^jm6fg;)XnT1u9%zMKHmRTV9qxqAdpk}R!+Ko7ubYM)1W?$WNat$6<7y)6%9SuL#_;SluN?yO zt}oP8)rl|$oqiDc7zRHYxer0dJMgN zOJ+cj-;~kY3{uYkjoZ}HU_<|HWv9nd!@@xV>_wi~yG^zhY~yScJBpDq>_0eBFzu(T zqcK?(VV2&fAJr>EPSaK-dAl|8u1?-EG^_aAdwt*%u3k*PYxhm@3tG?1&B}|$<)k}( zUK;mV%!$AhHxH2p3NSzfs~{q|5hkiH&z<`=g0MVD9gl`H$W5n@Pd?%z$ov;2+aV>z zf6T>o5`E%#q(k(arS4D z9Zs%8yLo~D5pD&0Ek&`1WBTor`rPD~R|&cM58^ybgi*Vn zuD{Pr#MuVXAlUlcMh^f&V>xB%$yk&JC0_YNtkOjPrg`~0q4D?M>_E3CisAoVeEMo` zSu)nS-fZ#px0l4~3U3hY#ji==@9x>atdZ4#=F?Z|R@pZurKx#QA?ssWfeRA#cA8c05aMkGnd|yz0D{*41&k`F;06+Dq&`tW# zuRPbudBIRG02&jmiiuWE`&UOURvC!hEeYRKj~2V>WNo4R82@$4YziP^3OOqIC*~Pi zi5odF(b&1uy=^lZ&+|p@U6~mDS>M8o^shnKcxzxxQlzkL(=k#JG#E=MT!;?*f6tVZ zy*S=ys2KVyAou1dE=DOtcz;BgHp8jE}4y9?xvN3zOR@E??kk!z{(dpb}qDqB+unPuV} zqLEZmvA7VxL|Ie#dZG+QDtf#pNMS)p@B}&eg=s{_&a?OVG1DE~-mip?ixpaJK+K5)9jX=Zcwk)5ot}2=&-)H^ ze^|?+OI|oRV@pW*j4-$eetqi<7>W1JOw{k98JsfvTjLo-d>Eiqlhu%J(1!fs&)oPZ zhrkWryDf`lN%z=Z6&g;@nsO!2Iun&chy){8O@E{lsR79tXUYSQIh108=pV+j+mX={ zXhS!Iykh{=I`}7R0RFmsfqxbaWlK$NSr&q z2=uS|W(PO^^dxu>SXW+pivrk96untmtSNuAUbCBGmpi|R`1THvXh5sN3_?@! z*<7=Fa*Qw7XZZGXB06e~EXwd+e%-MqIka28nZaCJR+z|VY@P(q+fGr?MbPzJh#>Ye zNmaj?^u}8lV0--fiFxB~>nrEXw%;l={TZW=v10Ip zQBU(yLWV`l*j$44n{mnf0w<6d_S=H#CPzy&@TeVTALu?Wh^;9+dF7}K1EhZsjLZ7v z10foy#bE=zhtG?h++>(PQQibL?I27{gl0zzZQ|L95g9cSwiUr){)zlJe*fh+5u}-k zd~=o^Ull8RTUICAUhaaS`lUZ6Jm07HgUK0DYESnD{9=h7q%N6}@}qX}Oi3dq3t(~$ z-XOSSssHB{G6S@Tm2Y-&X)SA9_~$+*v1{39&6KVm-@YyW+~C-~cVyN`ad9;812`ZQ zr8cw^*m3${x#-vGv~uF(Q?zVaKcj1M$0$mYg~tqcYJT4rw5Xsb?f|Wt#KF2LP?_?|4Q1b;z06Br+**2tHpS_E>xa2JOUP(?jph`7z?Qnc{s=Wv z)P-LTCus8&OxVDYN8)7*M{m$?EEQ3c2RvIYfh1VtdAN`X>9H)+X$ z1~XZs^|($qpsuBusZKayV#gQV{i&Xf4-~b+Z`?E9TP5pfq3<{QfJ7flPJ;(Z^d*2N zT#R9zAM$8#^Xe5dtOM;C&iG-KRY2M2$ba*V5tGlKa&?=$W6QxM_>Ed4dKP2lQ4qGk z8@yeg%a`p4iG~!WyGbk_o7o91V(L#~bwlk9KNK2d833V&s!MJ6-y3LA# zq@|VvAad(cS2H}gH01vwD(C2AeJY_R!8IwS`Ixt5r zeLUq}y#V+nKmU68W~cXE7RI&UYSPRKK*XQs8g%dZS|g`}T7C@Mv+73*XuJa@h;0bD z>yQB!RHOvaHF4(76bVLFM7>cgGq&>YBN|K%d6ETSHStC%2@7$GNUt*d$8Q^@FP%h- z%2fLlkV(c?@gB8+Pq4J>Pa?H9*RU}!;WGS4a&17C29I!Ht>3pLWp@^FYTG%s~KN6 zo_a7_OvYk$6;bd*{P0DS$FJ&*BPOY|^EQN30OD$Dgcr&tF*#~b>sh!=k4^$bj zr$TpA8J(1QH3O^K+)k9edX_%`FFZL3fWUjXD@jdXrinded`C(b45A*#2pF;wO*2s{ z>vuJR{L$bsEkXb>%^@jGbFkdr4vna0MRis4)7c;!@@l4r#T>bm1U1etOYro2?iX|p z7Mn)vou2;?r(UT+h{|jLQ1|}6^n{1=!<)ko)+sJ|dX#(f|2irxTdJ;)EsmgA%*Y;+Eo2#M5*m{1X0tC@Dod8JlO+ks zQe!K5OHx9iA%tu#N?Ls9^Jjd|bzkSFbDitl_jS&>@B8_By&g}Ar5nZ#@qGp?_lAFx zryL2*Fy(iuK#7f?!$GsZarv*AqwyXIF}ut@4S$xQ0{N6(^Wec$?0>r5at^0#Mt(yh zjK#VNN2AINEB0G2S`FQk-{?DDFq~7I{7X*yFFu#_Vx9A1cN?q5;dyP2fOmIe)|=0k zwOln;uHd%M$1JRYURFqjRd(HNmuxV6n`km;Hq>V0R_-!edlB*=)E&+;crUzmxbfdrC+3I zmE!2{FIcJ$CbuSXE7nTus@!=(tap%Jyb`4won>{}wlagzueNw0M+QqH08d{=75EC5 zE(=%tirighHUyS4eEILsK zzAtD<_wwnz=?pU$j1}X{+hYVh@Pt21wN$1 zph(LgFBOog7fAZ=nL?ZwG0NIC;ug=< zL?F(~?E3TDda37G=rt^3H9Pbg_?-VUH=!6cq;V>drC%sMwups)CARUrdZ18P>-l)O z)%a5*$8Z`mRbL1b7jVuQjmTWJDhROlrXeD4MHm4fS-QdrKf^d~&{!?NPZv);?>j$Y z#6twO1MI5b*i8!fxwx8g2KqmDaZnb-FRz|cUURH3a54@&r#|v8A;2Z+&3a&^TcYU0 z;2GEkpRLD;QR^E*KfmEom5Z`~W1Xx=lI+71M3^SlRcG{vK$=HIT{Q>)1;Ja+t##i2 z{2G)1a5|O^4SOn@o|2_maByX1qw*Vfbgva_yV~?3(qZDa6`%P0B zEszolq@)D~j|4(6bU@2gA<9%Pft+EikX~&nfX6A($Pyg%3X8LuTVvBu>CC@1f)Pk_ zx0QR?W!R=$0jmhxVfF2~=V;fh%oi4_Pm7oztRmN$4B?{s-fCHUsM_s(S~<3He5HVi zgc@8CF)uc2TmxILr2B@HxXO{Y0HuB$8y*m&fq7KdFWFyIa0r&u+PYegQ%WGm=nQ65 zZz|hfOm72=a$o~OD&roHS#RV5+E4dz76MP9{F>g2C8-sUEb8aoIhLS+LAEtN!zRIQ zl`$EMsp@B|5V-U@X|vYHaUe0)Jr!39ke;wN)KaT!~esG7ht z100_fPD&3KpqwH!1roC&jb{R4{H9V`rc!f*+a5a8JQgg30wJM_S%1ZIN^ zb0xth10Mx7RIIE!Dq?E{gC=$83taT~>uOhR*BwTDM*g}4EG`CY&V1y|N)memLHOX1 zil^mT**$^4nQ2IrTD#3I?x38-a~Fczy}?t*NP51;Ha;r5yONo{VS$~Ve!A}3hRbtW zA*%(1hJOkf`_$Xqz7snC?tH^R6YO@gOM1((GlsZ0xwg-HZ_;9B7%v;PU-PIm&$R`$}4X`<2(`8sCi7f9mYXtXLfA?0rkR*W#FZXmIamkjC=p_|K*A z;o>X5)zkkB*}xL79Im(Rdfc`bz5=lC1AGLK=srkNBZ_u8YvOY9ge$>j_G2{wg)98J z%?3Lo)Z!owyU^~7-#_qc|Iwzwa=pFV2m>|QWu;>NR1gSc4E(`9{4xPyqQUwM13N=F zKDu(A^d_5`{P2f>gnU7=NAU1{;Sr7Cm5HFT?SUVpztgVpCbbn$s4xF|wqDw1eK7OU zB7CJg9M`YD{&jr2!&Ad9eA~lYz>@t~@ZR&y@GoVkTp``^% zyX`Ygh0LD^+fA&L=vOJ3NNJU?(kCOOxoaLNV`>WoI^9~%06TAt)$X72nCI{s5V3{) zvv#~?|4+^5uiSg!HqY{3`|U}eXQ66ds^K-lS+BW7zW@u8cfy|Pq=55)(9%_n!T?Ak z7W}jIgW#eC^z-mi<(!BYttwJ@{_+#`){M@`ihwc1+U0)v**7b~5<(J+XQ;yxUYD(| z(o?Kb=Ad(aCnb*Obw0H5u6&(=MQW&wx-i4SA+&{x)$IO2NIL!06|}Kvy~YDIr()oQ z;R%S4SJv8!vc0sCkT7g<3t|l%E4q5O@Jm}*6<;1%VeyL6{CL)uU!_)?oh@di+NDCt zcuFpgGu^$j%^0~;8)f+|)q>cm93Evl#hYW@sSxRA$Gvj;>4VW14>Yo{BDFXqo_^U6 zU`fY@$%A<2)}2(aY%zmjkBLIEq|2@cm$$G^Pl(`tBEpKqvw>y9d}iO@bQJsk$VM|8 zL5!@4mUU?{M;#y1(M7tOJ(@l2DJ1V?0T;79@x# zT|UDkOoD{xKrASnNI&jaKo2AyN)qb)AsPb`tR?YJk@$O5BXvQJ$CwK;-!BYntFty@ z?D%~HAD$GxY3`+a+IlVeLv4&@U?AyX?1k&e^$+7T%wqf>MkimV-R!O)!i9C{A!JF<C6@a49ZGlBTbFdU7X0+Vv}?C7Diry| z`rcOYbK{kG-y_H8c0Nwsr0Y;CIa~GKDZPs}0(eJA+{`4A4 z%`}DIeZzfKL;ybA{~pPK=)tP-G3JU^m2doL_BY2w~!}n)PImFbNxyKF07s;kuVtg@W-GbC~O!c z{{6ZL0TfzD5~2@^T9G0ugZZ3(K7}Ms`Fl3L41~zy-o@$nGysCN#OHUJG2-9sNL-i> zF1KNaeS^*p#l8JZ>Nm=3A8==ci-{v;p9L69shZq{I~LE@=Ef*&&U71B_Bh~pd<*Z zpjHgh*EEG)SSRWb>1UR~r{vlh!{cv}BdX67EWhBa;4V*C2fu{~_(2M*kWKcwWiIm|J z)8^+@6yQ}B;8POhR~D4e6B1Ask}^bzsH3EeP|`-iLdwFz$|C=3sv;uHrY0()D#|>I zs)>qeh{|Hc#nr{dHN>S2BowVB#Wf`*Flb3lw4$Yyl%|w|sg&{=DHU5ORXb@_dl{LN zGBR4S%BHevXJysT$;oNU$?MC@>&UBH%VS&>sVQlDDW7y# z*6~s~>7b(HqoV7pqNk;*>!YS-tYM;q(Xz(qdY-U3p{ehr={l~Ze@@#lK*w)G&-lE7 zafpGZrct=|DKl?lth2G{6|9aK&itIIg{P@i&3HFZRTdvF;CaYt?c+D^HMqo%vaK!2*-^NQ3>mlKk(1`4?@R4w= z-IXh~go_;!SCb>+HzG=K5ktn25wVfcS=ZY1uWegi`)VB>mma-he*L#?OjuuRLVjF4 zC2kHEpO~I}&Nn&oX>w9(@`N!lrG)g_n0j9)HD)p`EkE<2ZssH1tVg=pH)?VQ^ls#p z=7mNT7R?k^bQD?Qib@(w?=+OwhL@Gs)z;mwolI@)3~g$E((37ZzqS5hW>Ejoe3N5%|e|FSs1%t^Xvxtbf%bfNA2@MDC}-F-k0)U z5ypSK%=$t7i#vK(w-%m1Xn0wPrLc+HbT+=Ku_{#d8t-HxEkuny)7!sNGI+!~LKern znrH9fhcm^`bhpeu2!4In>%Z>S_YVml$IH(=Y+LM$`L-o~ZUr)f5VFNPV0+q^htv5~ zyeE3@e|(lFbEd-fQODYU#V0Q>O+0$==_T!SmV{lemM@3ADsllZ#ZhM4h0zXlpnKVtf2E(`;EUpoJ=EKV;PmYfP7DWaTDi}<=`oG;Pho063gMzeB zT&D6HBZh#39l<3wZ;}Hz@tzVE?CRY#g$-k)D@dkybM9Nn7(xfJVe~|}I7E2M;^Sq* z-BkZ8CQBCnHzt-FeIi(;v8yCooz2xk2MZ<77_ZJM77@3grnTFi(VJ^U?ss3M3Q$<* zWgxN~x7{G}Z(Q%NWkpQAi~oYd_@pB6?Q9})vR{UQGwV+Q78J6C28MK|`BtZ|bl2SB zr0E7g-pXatfg;D!#_C+K$A_BxFA$>DMc!<|aWez1<)ICZHmLfCzQ@X7mR5PIrh*ya zpgUlP@1W8A^c6kPYmaO$ZQmc0XR_|}s$ZE?w5F!e6afRpu+Aw{l~&3d)AX>;H_@cj zg8S~4HDDQj8zmNLMfW?bZJh7cGZ5+f{+Z}{w@>BmQZ$N0wd}GbpLlKlhqhurT{@2w zN8Wy&ETW1mf|#00L3*Ky!##$6TlbqZHon{+Hn7Tvq9JA5FfmvR4xXxnCv80AjQIkV z<+^kC!LSmeY=f4Ow_?s)DJxBm@6xUs4mHrakK?ry3x%>MX-WA%alF~_pE7Zxk`Nd48c3lU~QHrR0UwL7PChk4F2Xu~U6x=D|C%U{!;@*wu-AOe1}uj1l2 zSBj~8F0v8QcfM4o%CHzfznCT|N!WeLJm74HDOKA>3ehYl;h|cmty%Nj^_(jL|H8hxhQJ3hZBK-ud z>s_?-HAHebGLJuQXqB^6BJrX~{gQ}vMjN!Nn z4k?kkqGrxJBL>khEJ0zkNO{R@Rg|qj*HYAtKW$%7N|57*x;|#Oj=2 z%H2#bQ{laz!j-hb-i(EzCpM2$oMb@)?^P9)<@3{df}~m9^vp$!eba0=h+GX;)GXH% zr@Y@yup3N~UbGWQT3Torf^Pb{pOJD;&KJPpQ&e4**w)78!BfSih;WO>W$yFJ zp5kghRK2v%f^~m| zr+d4ZbHj&0A2-=!`y8=+W?wST1KpO8cCbv+FF4PHRH@6+u*Z9%zJ-xgasGCVY;~+1 zM`txp9TCcDa^lI^<0%~;UeysH`!6h3{ifs$U_^!OTn2wSr@*D)QH5y-L|YZOa%I9y zNkr;Bxd4pxLBn2=fv)fhQ*LGsyO2%_QC4;afwLMGO(&)_Y5M|h40ZnD2f7@8hysU! zDFVO&_|7W~>!m4QfoU2=%8@Or1~JUKt!1uQ@Fs&RY1pySW|{3m4|X77XqiA{wYK_v zcgF0rs3ZHkhi~uaW$+CZw-5%D6qgbCHz5-1`%nv6^}LiaQ=#$Fp7|4K_K0+dXfUPb z-3fH2rna}znzjrFYA`^f*v{4So10ruaJpHeDPJja#O>NLx9c38XXIN;Yc7pl3yBlT zr30M0R>v??8jh2S0qfLQL62;~Jn1x|V2~~>Di6%Ew4ah|Ktk9fO!*3sP}P1k89T4Ns<;zlnv5Y}leohnssI+6M=p5Ta+4DhGy}*q?pU5& z!Lm!gf@3+if`wg(Jljvzc{FrJ4ek@c7|EcRVb96#?#dcKWMUBK zHMg*mJEixOl)F&EUXFZ2>{STZ&0FR2@~TAcy*y!$Slz7<71D(ZbaGj8G~zh9{8w4D zaBQ>Q9eXjI5BUUcjNe-=N@S9F3-kxGZ)Uy)bD1m45ovE~{piC1l=3+|(7<|pnK&%C z({BE#-}#n@K(3L+H^;TmpxNj1)uwO0GTpv(q(T<+bJgx}yg(SH@VeYhbwSOAq9I&J zBF?BCW#NP1MZdsa3zmns_cO{jp zyjC~Xq40c2ULoq zUW&6@ifc@YTXBjoyQ9Sfq1Z>pqSL);?$6y)X=%q@WWIBDlJkkEy^t| zIwmcqI4!OxEnzM#=`f9mN+;{3Q{B>2W71*y6y*~n=&SVHGp(2N#x3ViO2{b*MT|b}SJzp?3A61eMj)*y{t%#ld)G zg189vSw3$VgB`K>`*%qkqh zO%?{g3)}z?-uyi#B8`za#|n0GzvUQv%Q@qgYsoFoN4LD2ZsF%|`Tn})=YBgN_O^e? z?VzUH!H;hHJpyHp<=d|n)*cqBNP)Qbg}Bi-6XuH=iUHy!fE8U#y#z=wpy%>n?hL32 z7GT*g&f5g+OL%jvvXFENwVU=EI|M%BB7EDus8!Gk#`42hvC|b zS@2MI2FL^h(jLe`N8~ySIbWNQDTdhM)$v(v9QQ5gE zkOc#3p9ji31SE)Ho02jibS=b}Ijw=BSy3r2#Kam@VFPOO1o&XS)JPw2!Na{7P&)?f zKw8&`EOBx89JN5-)gEPuzBVI{ld@yB-9>&ox=z~sz5j- zREm~vrgT?kocH?Ong_9xTz}@vyfEx`n*@t;z zfu?cTi>6}B-p0eyd{#OviDvtE-1VPF(`|zqtvELAIJQ~ejSx*96v~DGc1DDQNt=K){ry3}Ox;L=w}5!-7;DYPm~6C+Og2 zWsvGV2#2qgVt^b0SR-4(Dh7OmCpeC+8R{A54kbsEPa}ckh*~X%IzmVuXnaitP&03^id=Vsgt=^J4C#biSR#N$nX!CuVD0NerA!$Q_u z3>F16Kq&#hOkM)5FxL9cyJlVin@3#ON1czJlp^X*n=1Z$j>0NL67e;4MA*XkwGI+2 zW1nRwQe}Nb`6pQ#g4Jy*khS&`@2dfK|m;x>vs8ooG+xPK~oyp7?AFwdG8Hxarl?D_p0rQuUf>;pCiOkJQ znZU%E<+lcU7>Oq~V1g<5QYwTpXukWMHFuxYw(lAQ3-6_|Ki+3W5dlX!OXkza6WWT) z%gr<-`~4|aX$)Y|Zl*zJaoj)ch*g)!26m6%{tq+4W-2Lb^wb&;@1P8w=It|z4FxG* z?uf=5TL;)wCxLlKEIr2;;!}@*r+b3p98LQHP;5glLK`iE7XABEwl;M9N5ad)v2h4P6~2FJeHigkhV|xmI0unM z=dA&8pOu)$ddFkfvj|;@pLn#-W{&~v@5*Fq1`Jp}$imL#BbyV+`u{giNt&qOHrz!-dzzi8)7}R@u(~$j@Li;?2e9I$B$$tFS$B9rHJ!Orqvd-@cW z=;W;{W6fZ3N1wr5R6(?Z3^0+Fx?rs-kY$773`pgQv}!8kL7wLj*G0ED&X)!Q7D?iB z-HGp8#>PP&sBP(}PFx^;Xjo(aS;w1eCn-rH(i2ZVsTW`D9dSg-l*wl-_qMCNPY~DZ z%6b28V6nRK{pRuY#e43tP4Fe->crc+V3qd*8fOwTo0qiuX8CU~E}Z)CJ*?t(R>yHf zLdoZR=XtlB(R!p3_@5es$!gm#k0n>De)ammF}b)DCjUivnKzf56_Jn?QTH}cl=em) z5~`9g8)e_Y;51(8{>!K_?Ko7E+c*)W?Ag*y0_?DG0u9oI(?-#~^*j~zThxJ#df9&; zIl@==?t?K7s}@PCRu!u@k5_G%R_*_-I!LTJ8m~F4ucaJJs&k3%!{84oFkcTRZvbIU zgI}Sy#NpwW2pur#73|XbkII(_v9YoSX8o!yoK(irQ^=xO(Rm38y@GiO8Cnm2yt%;siQN<|huo?*-m3N9s!!T# ztk}Byc&lY;tL@)b`|g%9prBX2ahGf#MT7VNa192_?A-?h?C|Mk$R*-t&GyH#ifxmf zO%-74{=e-hiJj@)tpww(c!{kU^{rX;tq0zV?%~A4Du958>MW?sV8dE!OTK4sk1y?B zN&3(S2EQ=g`R={bdB~ zbI>*kV(x^aShigDC~`!B`nKjt?}MeD4%s208so2ldpYi;CFLoo$6Khbk;{B%l4$d;4lh53HW33cvhbAX#8<@~P^iG>VYB zenP@+SxSrnZ6mTjm|~-1AoBa$VpGs&;!HGf;BEmiBSNg{px9vnNk>@D6l*AhVwT7B zqK27aA#Uj3E;L9L*MSV(+YS$=--C>D|1_Y30^8Yc9k8{Y{dv_MVvMnjJx>0n`SPbM z7MdWrrHuY`?Z%Fh&!J}W5#AQM{p4HL-cGLMhi&6uKdzMB;s%e5fGnq2fG;ECTq>4R zZamdb+0q2s;!bUkW^u6a$*Fpe6u**YIg)<^QjAON?gj%*&8bk!@zhbSX+VmygqpQ_2;4=&v$U=9g1?#B z6)AA|CZtsPQFBc%^xSp;)lkp(Xax6Ta7<^oP67R!5#zp8wqAmZV(=7kFrAH87CT1= zMfi>1QuBFQ=Kd{cvY6>x$20gPc)HH6(xTksfJqtldXg%B;YaAZuH&!|Ps=Y{-$n>o zVR~AC5jI{y9L8~)J}zl}b@h+0dQ*<9)Tq~sm0O8#MY%?`lb%@lQJ;BsMRRG}>!`ug_>i0VMRqEZ-aPB? z5?p;=U{hqS?Ota2iSy#6iuTWj%KqlxXKGSHpr(w@?&Gb?MI5?JM)G>^<$DpL*Z;e! zwt}Tro6)hVU$6`SiqsJqL}*SRhF6Jus~aiRIyMHgd7lGbK~uAMjqcSxG4%}Cd%W`g z%G0Y}IJmg$T>PlNJ_ba+N07KbTif^gV0KtYqEwI9@1IdX5NG_`OHvJWwj`latD$ru zmOO*&P}H0k)7N}I@p^OX)7#JQ-LN!{T`RhwxgGha^Q3>iHW);yjv5f%d-mX&)pg zP31i4NUA(iid6$i;xgMO%OXJ#`xp>UHVug6ym9O?>hFy(XIet#PQ~6jG5h_gki>TD zRLXUgg(2myL)*7BWH3x=g-|>!V!JJExa!OG>r*rO>y)pFYBlo2kQRs~gLvx%TD*W+ zkj`JN5S}+B+Mb6{jTvN5p_3)bXmG472qe+((QVUXq0{{;$Hl;Rn`U|1;4M$;$xeSx z-GcTJskZpU@BU-JqfzhQV=Jd%vI(vs0Eec zG>91%t<9sd)Jkz`&_ooophHfsuS;i-RehC3+sECbU*CyN5R-bcIpo2G3{f=81}DrC zmv*>fo?LD)sAD44{a#o69J%NE<-+^#cNLx^8;Cs1QBI&}S$GwAw z80ejqnK@Z0G#YGC+4)lcZF(f2!UT9Y-Fz-kr^wZ^fOF}cj*$O;sl6uH*3kc4&f7Qj zx?!A_&z^z|qlVabFM&nV+f&qJNt`Z32<>h=OjUJ;-36(5rp~~{u-7?Xb95^xIMb;u z`k>z6VA6)Eh2Nt4W(LlY#rx?jc0;~Rf!Jn`Z>BE2Zw0E-wG?b0^qwCNooP;nwVWEX z>DFuqDak0R)-N$qQCG9emFMRtkF?t>VLPJmEcGFQG(_Ah zlCN<$xvTaE%z(ij6li$ih5F40@du&7*ZMB3=lrMyL|2y|7u3uP{_NC|jaom4)5aw0K2X0*9=BS^OAllhgnnLh6y-LnIGDOci7 z`g!p`M#DI!NMJ%5h@;=B+({H*ndtG_Q?}0eXigyCb6(21$<*0n2vjK}U)J-*Lqgqz z)D!)_oY#woC4UgoaYlYTm**0bE(>}K+?@s0Rv}R_5SH3~uqqu#KCY(mK~+;OZqfQ+ zyUmIMMgh`>8H0k4K-@Qv`vc^4=bxWVjl5ZB6sUZDe*FB?$lI|5h|zSBCInG)asXKc zi9W#t=l>>=?U1fpOQes(uH>&7U35MQs=iz1{}WHAh<%@bnamPJgPjUC6?!+7W)W2_ zFc56AVU%%j zRLkE4h{vqQ+lNoDwMFbu>Y5Jgod2uia%8~7d#czptSE?nBveD|Td^Y@%UAo?A1yi; z)ABUB`$E{o`@g=;^y+A}{_UrT=G|FfSX}R&S-j{CahQJ6&9P7ZWccAr?o#hZ@%_6y z4pNuje~5~{#)%);JvM&*jx}*klj|uFYK$pczVLXkYSfO9$)p13hrg@sI$BIje)!>- zmf5EW#)qs&>SHp!lVbRWY$>aMfzMrRS7x;~)>!0di;d-6!pb`m~7LVe{W~ zxFObH8ZF|ZM<8rAU-8@(d+5*;+NQZgH<2yoiU{(iu`wyU_aiRmA?XB z@<9q|6$2iDCqg8hR|J~pPIGPiV-_pWSdH|^+_`xa87-+ay?Z#W$Vrx>89Y4gc1NYm z6N+M?(RlLH-l9ibHuort-;xD>x3Ej>8h^y^CZ><5#o)3qjWmQ6F>$++aTPaA<9SR^ zRPP2TYPQEP%%w1E&h7RzB_@>JrFe+*CYt@i+z3~$u2vuze$FC7HFh@C6 z`am1oOtL>je6wQy>_IxgnWyj+iw&wc@-h4s1(EPrxt+U|2-{H6+Wha4jFpk)=aZjb z_hrc7Bw~zkg~pP3#$^v4?fF04f8F~<#)Nyz==CnLq!iUjau!Dcv(C=`&R+2C35yF~mcT zQP^gSIf(Tos<^Ky;3*srQiIbCB<*QS`?LBE{fS z#bo0`oMi2w{JUOJu?n$;LA(BjsTxDuLx>6@MI8^d-XQDeL5+&JoC2t2!xWPZvI!Pq zokx}KC&>~Jo>OE@BT0JSTsCGv+N}fp=RrDz>KY6)bm*6fA=B@G>HmS`(ET#)l;4*v zM&Ek2_v5e zINKHxyVa587WuPAU<<`jXAW}@Bd~SesEHmQ%4$sA!OAEPYLQ+f6O(f6IR^7=PdPs( zC4sTL8lYM6x)1ve#C-{@SlbtDW_^M0;h)pJVv)Tx|3OjIGciYmn4aZ87G0^w(sI~D z4iED->lG9m5pAaoUhUld&+^OrmXR^_NjHV0@0P^c@pD@^i9?DG-bzc+{F?P>>>xy& z4z)m=Gpn*F4r9`x;BQkY-3hSt-&Qxio0e=p5K*Lb1KCQ!DO*vhllS zFSNIC2F{vF3E*d0eQ#Gmaxq}JJUcN5vP||+R_J55JQz=H*YQZ%ncg>sA!TG4;>amN zrUij5->ft8_2bWmxQLsN%ib7r2Rsv_89j?P;)@#OwH=v=dMFZV0|_C6u-(deU>>)6 zT`Mx&2CQKPm9~N?F=#IgU%Ywq61g?V_g(+BzqQB5N4FUlu z^cz|CPmc{xD-0XGm}oXvhKO5146LBy!A!>u${8ETvAPwjAx0icCmJ$~)c?LRSSv-t z9!e#qxI3hH#K5qg)0KQ$VpjdbcZ}&Rjt!rmMmW7OgPt|#?6kP%Y$JB|%z`C!ilmVT z7N>(1ra;(Wq7a&iSn0MK1xxE`>zy<)40PdhG~pkAuuphWU8$#KhEbh|N7f4%j*n#_EG z_I#mBN0ZE~#*6cbr!bCNImkSB?%=NU7eEOoi398QY6>VDAeOfR+{NzfbIc+oVo4pb zd;ln$A(m>-AvWAgX6Dmx0actN?!)=?E};1@P(Aamh7-hX<#7l0zFy6(;=rA@=5g=E zTxt*eajo@e9zlY{qk*utQ|!jRm;u=c`;`NF8n0uLU$ z-~1-hZsxYjY>|K(H=cx=@)Sal?%U0Ak1f35BxTnTkp$9=$r?-eAkod;l zQC|RpF+ySbb4a2WTM%jf z7QlujaYQpvSWwU6;#-(6qW(QgJ>%!17dMRocU@#R{-1H&VjZ(TzC0Cxyswu4*D_*h zku}LzsGT7w;U|>7yg17A`}6M3A+jCfq8%`i<=K?yp02_e_r>(px{`fiucosv{3-V`+A)X5Q=vwnpzs-L( z?_>E3f7UsF{b(X<@ke%q0pzh6)VLsIrWE1kCz8bAv+_vJSrM`TB_|Tudi)Se9}zJ` zc0E7oXkyYe=4;|B(oJ+qBHGsD?YZ1m6McCE1KAbVP=`dmhKft<)7_#x3Ph{Q$< zypV+F5AI67w&=wL^7yf)W*j7ByuzUubTOI{XuO7)3bc!6@DY79o}R=^A`5Ezo>PZ- zwR-6%A6XA*YejRIR(xd6_E2V4EtUjrCrVl|88IX%fhd^=qO4!k+x-YVB;vyZb)tb| z$P|&;ZFWqM@F58j4Dyc#3Abpt|wK8`Rq*K?0yVMwY~&)CttH1d8<`RMU!Rd&rDwk}Vg2xo*Uj6p52B=F-|>!5~9e=j9H@J!P7f?EW? z-|^qI$Edbwic^ovPB&ocmZEq_v~Aoxwk1%UXcWHS6k9Aw^9ixA?IRRT zaiLKZ(J)gq#BmI&PLEXk6nR08syGG(k3pS@WXWPGP)w2>qoT1kHfxujw{k$oUR*ya zXIQ)16^+vbrPPcUC5ekD%qQZ(I%>>2ZFKn`*Qd*=Ta^dr{UlcSi0_jU0k@?!hsiZz zi)*R32zv+T&(Gqk56(-iZ+Yy^I-R|CHg|31U{=)FZ|2~PO)tdUFyDWiiYZg*wRd!+7l2#6l8ju|PR?5zm8e;)<#6;tl( zfmIy9+IbK$Hz5@|bD^@A_#mM53*(01wKcmH*OSYkx*t?;dm{yx4`A2WSp0sJ`>H3c zxM+lYS?r#8@nehAPtxKb``W%xkZC~>ZL)7Ft*Z>J=0#MM#c?g=~ zcnH!wgqn_lUq4AyD5hGEkyD;YI};!VW1%1db?YUY^%RIU+s}dmv^KyX#fb+AqwWDz zcPPjRPgN*B3V|S4P*}4({62b!$|j^4y0O1Jgb*+tI~-i0rjijdp4h=57(2h9mP9sf zdBjps%0l@&VJIbwPf{`OCX!^DUZn02(|GB%Y5Gl6V&r#3q}(}93zzB8rf*buiY|q0 zmVd$*N>e)Li7o}Lox9C?3U!L7deg1kD8rsV?ph7(hDku#2HO|)rT#eKNZCOeh(Tw= zwxG|gVEm4GwG&o@w+~OOjuj|4txK(tsBj5E*>?OgiNY={tq1H#4U*Ye#X5t1m&tIr zh@eqpId%vNuM)?`Af+fN99&cr+Mz+T1-yyx55gDC&cDW@5scieiiRS%ER>%x(55>a zyi~sHQXfeowiiOSgSTr^mwA<)6@dx78H~b2hBQjX03z8&q*U2ru|b4{rIluyKr(s? zMutFjq*4StVx&?9+03TA#p4d8Vk8NHPG-Cg*FhPMISqqW#=^4aSVWu|D_MfhX@aiq zmx$6iuyeD1X8a?FT4$XgjK7(I#C%Pz(QUl|=qXn~&PTvGt=8GDzF5XV(d?I0@v-mL z%&ZSTmn+IIL(M|?PFU}+IWE01R!W)nnER)4y{QZZ4$Pc8ghpaBP^4Xd%V?;GUqNnE zNB=0Zf)1FKht`8D!Nat zd)`!~?O}o!a;R(Mu8Mq#K!vbRX=cQc{J?=hH{|iJtv-z>hKCG|$Hzy%@6lm=Wmg9E zH_o(kLsb^8&^dHN_v!zX7cfsyCO^CrXz$W0lm7e?Vj}f8XxQh$j=AN{ zt~c)@%foN_^r3X7{ML#|E^b@>KfHHJtx^3Ulb1CXuf0#yU5cH3@ulH=qESeb=7sbQ zhPG%@Ona!xzm_xT6XwnzEt~%Ott#7vJFQ*BwH2=28GfVhTJq6K|5K^T_d!d`oN(k; zWf~?H*D9%vvxA+x_U5+iQk&y$cf;fmeBexbyt5nU%OOjLwa0qj+`Je?eT0VC;{Lb` z$)|qIU#^6F`1)&+*eG=;PVd*+&(fH0FDw}#+nr~Gw-(zE_lAB~y*!(YIR5d{C;89M zZ$8uTKSfV+poC-ylm^`A1%uv(lYaijLiRp^qj((1@Pu=qPnJ07(iMtEB#lL84p&xp zB`n_PY56Pe0+X0+L(@U0vrD zAi8*XN1;2PYUDi23M(9n86?6r{>Y#qmoy~kH2)9mKAg{rhRqAEq+OpVvA%uySYzlM zxiI0JoSK!jbvPmMOc_IHt-*Rcv7@kBOiHOJc>Js>lG5_Wk3|yk-?3v74;9-|CtDbN z=BL1y{H*_E@JreQ!^I31BRzS|!lsEp%?Blu3G!MsN-r)sJ}7;=I>>2@A9HwxA!Prl zUl!Xu;}^>-D_~t-g!%P6>TyK*9ZzY)|MYDpUTNO?mW@?CeK;8RE}&@VbFGOl*dfUR z4L#uysK%{4==#H{s^bp>=T5LM#zd8!O7JdmNN1)w1;G-J6s=AxPv;kO*2v#$*BdMz3`4NT@K8L-TC9z>^|3hwp-&a{bF4lSk+qg;rI7D zLJ|MXqiDp3Lj#pzX%-ib1b$`-PG$Udz>*4#X3GSncB4d_Pj>fed&{MNzTTv;v`^MG zUjN(XJ8ro2u;bytZlXrQN0kr1ri%tBFi&6Dw+=5>XhLS7tbf=0?yVm;VFQ0y*%qFW zAHLamkavpU%B$)EnQfC{OHrkS`7N_vES6~>C8S*HV<1&}hL42~_tSMFSJ*4jIYEC2 zS(kqb2wp!_*=^3sPDVk*{*R)w{%Z2!!|;j)-7Sm`iII{zkQOOv0cq*(a-%~!1Oy!2 zQqszIgbGNP2#6ykr4cONz2`iCz@BrSA9n7~eP5UNChv9uSvtvEu^&ICQNrdb2vf`^ zLk*5d-|x@dirKv8?H&ARQlG7=MRc(OJsL@uM9PDvtcm&M1zU~%7Sk7 z?Gqwh0Vr60_RN*hmg~DZH?bmUu_7naibF!8#k5J_o;xjktR!)F&pp-8T{Ld9&fGJ6 z^QGqYE|p`CMwW(7CVYIi#lm*B_+L`3cJUr}BkS$Tpz|7w)M(BS!dwKDF7u8~G!GsP z*Fp8uq#1zt;C3YXxLG=5!oD?UKSUo7tar%(^VJ|1`H>x@CAU0TBf7^{|J}%T4MsdU z8SwzvxTw_1BIm0y!bfoN6; z2xbFdBV-atUGwT65`|?@LxU(clmAIDfW8Tp04Q4N*TIXA}2)^{3$k6)@cl&2%)UXEm0tHAkuUhnlo&QDsF>*O7TUmO#9$ zA-2;Is-GdLX=;nKW^-wfVLjb00(l2joLyXccbaj)4TN-H0{!}2Kq@6HI|0jdSB32g&18=K zmWdgKAc4>_B&%& zBCK|zQ6`MmYrNZ*Vbge;m2bqK8a)Sq2lR6iu&m}-*5Q7-F)ZAcha*6RZ5T^S=x0{~ za%|eM;jpZK`Z+^%LJ-_aAIb~LCfGaG;yi#a~J8!Op2565FjhvsRNmTAnfMdn!Q=a}LH0Qn|>a#P2V zD5n>!qH=0q&`+-2=-KH#aCOB*k?_}ogbBAF-zfy@&{ zpSsy$W1R3rJlv!DS1KNkL61*=3z^^y@k1j#o{mSFWe4&{?5#YFc7B+|zxyuMLWthy ztFxOmpK#CiKe!14-;^hV04b99S+uEpL8T{%!D6ctI3J5Ob#%3^xNSj$=}E`XItpNU zb;s0C(bZ4#98ucooTEw9IT&gJpm`ck%8sq7sG$ZBl1FL(&p*v;#cbNp7U#NJPKI7*V>dCjRs$x|(%+cFQn@U9K>C4n6-X%-wFd?01r<@W@AqA2*V5n=* zO$GQ@S?E;K{#3@jS9>+dJ%Z&k80vhCEv-;hRzW7mE;nCoRv6mR0&-+*N1n3>>7PiN zo>YnBj;LM6-?a_QleYM3 z$y>B`4BXYLHyPE!wIrA!7Ez;RG0!R4!v(&^nY)e4%J_c% zlu3=Dl7H`Ul3yYM8b3#gF2*-5rip&ZYy46ox>VJ;^!o5i7(Ep5;4S~w#Dea-c&nU0 zxH*N!xsF>Cua5SHFO^4*Ka?D;z3%5fS`-mKu}eMPDuHCY z-_vgT)M|nf;=aynx-Jp_Rn_$CwfOIjrr&+ye?Bz*89lNap9A)J@mz=m{HoiZ;~?+A znd$N#<5q8imVRZ}Qk*tzcEM}xE6Kz1X~f*Mbphni0wHdcp^}|B>X`nnPhFJW@toe{ zImU_t2#6`k(D=Q7a?RCx6WM>LcOOK_zl$eqy58=YY@AsCms7^lEhNmPcJD{Nh#Q}? z1hc2}P1(O;eleY0DzmNB+}*-x)A-a{<7;QXzCGHX_Ug6#f}{5IkN8vp(20 zE^$AeBEc1Y7PN{o^rBJIEPQ_2SB$!-Qw>W7mLu24c7U9gM!|GRp%<+}uTBM<#FYQB zvw!rZX!G%1*b;J<%vw?NTo+>cS!MZwMvN;*L6pYqZYwgSOp&71|FV_u$-H~kkf`^( zKrzPuYpc26Oh*>SV^`77Wv#N_Qg?#e?u5@>EjGRFkZc-y9i)zlRnH@jP_t$&k6VQ> zuHBp#?fIckTaAs*7@p~gntetw8B5$$nPsVoG|{ph`b%*ND6<)wU$$DPyDwaeWHK0y z>%HmK@tSCrlplP}o?db9EQee$$H1#!2AhELTTT^Ae?$s`J=%M`GLG+7#{C z>ZB>)?_ux6H5foYekvB`qVpo#qH==qen96B8ujG^>-V3G26L??E*^cPf7{`u;eLMm z$eTW42zr_lBSK_j6#_V+Gv+)(@bve^YM9K<{9U#wJ_ z-UPhCGewG|Ta?YmP$e8?C=Txw8^Z=Nezy_Y%{zYc@gf5XQI zlkStY9>L~TQys>Om);NEy=Eu9{zD3C*n!Q4Vocg(I0Nm%*?OU?foxk=d>XGpa)POZ zDd|6qFW`g2f0kyTgQ@DmX3-Sn{9)O_&Q!wT{>w@e)F%Exyf!jqMrTEe#I7s6F`uV z_q0yQ56CE#-S25NUm#s>PJN^w^3fzjw)@wv+=XC21*V>oI$G!rhzqR}G|5gOJs@19 z%0O>}*a75heYM*GgzH_yrvQvODzx-AnyKij3_#;7pz+lcE0*3d=j7(^#gqq67!pCk zy5TImq4{Lj>B-;68EM1rScJW&Cr=FR+`83S+L_x_9nw=>sNX)(Djy$m(}7XO!|2=F zueMfb*wt1P=YopfZ2n$3RP1|E{NM6)AJZQg>R|p~WyTGb)(24kXO~p2s>ZCE3Il*b z;5-Ka_!Q>$dGyDh1EPNvA`YMMT}6-l2cq#ovk)mA6+kv)Aha2Ki&}-Q20+wUQV$P^ zEe?ncDZubRb~u)!8B2el!qTk5WvmFJ#v**sbj0bDv9KO_ABBffv`;~%0e<90(d zXRKMiKbna=k%1jM?%Qii+Ive{C5x@DYj8y|EBu8!9n7Pjqxl`@>)+~kN_soHqyFnY zw1U0bm%q|TDiQBVrta&K{#7C{vi7-q-7Kv1dw2PZ;nGKU*V%6LH_u2m*uqMwqPA|w zzD;NjNO!IxljjixLd}6Zr~!6^a&|2c4-Uw0j-@@uKi7$-_Q6oDsEQvC98H24c|!@Q zc;PEmu|-v}5FkuCn!`hdPkJEO)P`#j$R@2yY+f=AM^7Aq1oP2!($Tk`W0(%Gqz3P9 z0iti&qG@e!g1!0~k&!GFDm=|t4tfxgDZ>&QE!Y4;$F>fZswJ`V+K$o8s3TY@Z`)>| zYoZ|(a^Z>31xrEkDoMh>VnglhAr!2%cKI3k6;q6qJjU!IHL7vMUMTbP8l(y+mV(`5 zm0lso#jsp6li$QG2MDF63DL+^$aOQRH*0a;Vp+RUO{5bs2+05hx8SsgFaP(kNyN78QH-D$8&GIdGY0L|jMnqUA>0|K-wA2AwxgJHW`|B~3_o zh034mk7zZUWBF+5ssI%k{Dk^4TFAwe1B8pT-yU-7*O@();?5#C4GLQ-qct?Gb;Lze zS^}x)P|am2L`-l^EP@PM4xyDht$|Sfx?xtYOx9B!j%D)2I4H6@E!9-L^joT}eidyw zIwDZ|oK~RPYk7S1cBAU^IA*JznKdry&`DrCou;(?CTDt#jJ-+cWZ8g zNQcf_bw4bPqV>cLeJCdGeR|u_NIExn2AS_T^&c0zPc5P^JN`y$T+;!Koo-o=IDgQ0 z%-%NEzSKj2=-;QF6QlZYFk{e5{XSq?xTRssn5s@ec>2i)YRp zB$ZYJ0Kqb->-%s@Bf$GyQ?J@B`deW#lgl)T!<)ILtIT!-!p2j?Oj!=E0#htBFGX;T znE;dTa+lgcD0QjJAVRTIrKDVmH!dI;tsJ4F?D@ak~lw?t0~LWL5VrMjgboh zQAuxO>>!B-Wy284SV4y_Rx0i_62ZBz6TCT5kk6GY4tu#(xU9_)0bB*SrAz6{u)T$H z_rU#FQ){PBB30J;L_C}lpOlfbg>50p-A?;lSNhfGd&X7!Z$9ZaYJc5}sgqI;aIJcB z>jAjNVo}=AuxWKPx}o51_M@79{@c;!k5fwzG_E+zw_Nc*$18pR{rwKgn; zjc3q+t|2sB2pDK?RFQVvv^V*{b8U96A$Zc;)W7eUvW648BDELw$`4Hm0J~Hz$<9^6 zv+sn;NSUzAu9c?|h_#i-8*#fho+^W~Bvx7e*I#*pctgu`OM@!u$Yvj#^G4_HLsp;T@oG!$nWfb*XSMn2{2d2*a6oi z^^RU<`#N(eUFB3Kb`!I-wxWs_pMU5BU$I@~MFXj1eQx{56uvxI?-~$l1|_v|ZJfWj z-(cWnmamwuhLJ1U5=~b^i*Uo#h=Q=^nn5kUtvelI?`iWf$!DMEnXDBjO1x6HCJCLqxcT(-$hWU3fnV~p-=m>C zIb+3Yi+n7C#~ucXH?t5fxcoc5EM3rf;R5q};$oIII=zCBU-2QEA(=E*ETDqmxG1bm z8J`tEyTbRlU9LaXPnYy9dfoB3ri&!&#p4l^t$06q5k}v9OOYzZ%mVeZR5ag6z;-BH zvgD+p1v>x6S!R+h+uf25HdwkX>pqpq5=mD?-_4VCdez2%ky)%(ZX^y2#f!C|q4x-E zi)Cf1HKH?E>YOdpy9hxtBS}oOl#>XB)&p6gKjaJRQ#K{=$o)?zdj+C_yAR%}zrQnZUZ5LWEb&rHO@L9Ndh-j5LT<%G z5T%UEGqu`(H$;ygk)>Q=l?)^mFqg@;G|2W)0Fqq(?C-$@lt4XdY77X{=4nD+^vrCk zxAKRp6;kUTebphUTdGX#ppG{1+`nhh-EZQjQzy1q2`jKPqUFeaAxy#lz_rl)y_jjs zSIKn=mi?Jmed0BzJbO;Xj>ZGq>2BCml>%l4Rej}l5eLsgV`I4`e8Iz5cLvA#j&13# zv`Yw`Pa%}H`%_IBgL^sgsDbNvpfoM}rn9KDShq&*KZ+k-9!vErt=ks=t$Vk1N;Vk8 zvRE8?z3ANnt_SzlFOFR5rtc3Ogr<~z+0%Os;yM-~?Q-rgj7NGHCC$QvL|=3WTxLm zvcGcNQrW_%>tg%3=g!EF&s=1~vl4a{UcMl~oM zW2szhMJij+S<1~qy;?+D`vRuUG<%Z@wT;p?@yqe{uND^X9?1@E&8mFMApQ&w)?6he z`A~$Fd|EK>>UEm0jM?fa`T}-GoRQ!>o=Q9pod}N_vyB;Be{wn1^5Hi8$MXc#x5N4r z1)TY*Pm$|G?_WhZsoyvv+1-!=tPjR+NjB;!Z{Dg3o3HEBsXKw~n|>^uZ+ab=r>E?0 zdDF}Kxx1siDg5ESwBd_6GdEwb%@|gCk&EZMr5O=YMZ)a?B-19=!?UJhMS1(56kT3+ zcGhol$5A2Vo#{ zM52VMF(hJWrWb=&lb$RqBqGP&z2p-hdHwFkK6*zTT7YfQN!n>qk30h5u40(z9itFQ7K4a6<|j){xNWe^oz(Sy`}|g8 z0yz8ggQsr?K+S6A-aiIBvy0i)^>sW<;583r)&KoU5C-ja z9;m*euf9Q58tH5`4D)s@fjUu6fZi$q*y(zK%B<+9hYSEmG%BJW_=IG{ zZGI@H^o@m&jpLiIF|Oph!;kyi>9t$vAD++~N-&uCGFY@QSf4OxPCuTZ-dvbUU#+DR z0OvX4GFIx~l|CYZ%^2Le9^eF0BaG#hpk*V82Az0E9hdIJ8}hr=-V8tyKusSvI15S3 zYw~0kl3~inB<1{0OcW#Fd&i45y7bjrRf{dRh=K5$1jm*y$8HP9;R(ly1m`nIvU3aL z(_=>86VUhC5)F7czJVe0uHFDaQ)$mSC@f6S zVLNl9-Q8m;Hgq4#e6X_%gO5#sXd(Be-w#jq+Sia10)c)4p{)Xurvg|>!8kv` zq*lQcKiI%<^-99qiAm1oS_&$0m|2!Lqhu{$2ZDJ(^}deA#G_NhFee@r(~#MzVU)}0 zD?&=s05O6c+Iwnc(UfKg{Gx7R+==6@Ap@#wi)B8H!q?s~AM>@TkvmD!e@ue;5l}Qz z5)A$lENv3E&Lp^`B>DU$h1w)VrC=dQZiQ4Khbf_JxNtcww(9hay_UP$0jbJ^4uePF zT#fe8GKA2m-u5xsRxOMK!MD%dt`-L`Na@&f$3ze*N-3_F)WCOYosvdF{Bad|$>)GJ zV^wPU7C0CAnPlUcLW`7QyT2mSEG)T#Q#rdBoqdgdUg3zj@jB`+(-=aL_iIgxYG{Mm zoZV;9eF_czsnN?hdrGH3e9GoqF9)G`uUj&kR z8zef$iv;&Qrz^D|%NH3FMo!dCh~ma3iw?9NrA>O#Pwk0}31T!m$28ZTk4L|e7Dj*C z6dAiB*B%$q1k>pX@YE{VX11njOw*`6RZ}xLSMbIo(f}~ZGw1*UIza`z3?t=+gWTR(M3{T`*kJv!uc z0Laum!Th;~*%$V9REq!Rp0TmEi5b@!v;s%vvc~v}xC$pW2)V4xxajNf zYW{(G-grudQkoos+IX&BFcRj4hv^#Ie7%5{;7Bw4B*JMWo}Hp@7;Hl&B|;gLuz^vK z+|78A6Z*j6<~qBhAsK^1y*CpJ#FLHZ!*LNt2Vc=Rq-@-T@x0vS4#$EyXOO-A0>OIBE!#GOlj3Y@Ca4HB|uKT#Y0%j8nh0I|FH3+fGT&vI!UCe(ngG@jeHbpAz|Kk8|a z8eJQ7Kvv>AL+};@%$6XrYb@!DVQ?vB3c)h|LX+SF8EpDPdoeDdcVIUCOba|r9RTPh zj#jCH_H7%@B98hJN6uPGZi}Q1vSV`mfAJH##~>9-fcoH3TRAYFPK5+B4_{i(jd^~m zwnEC;XH;jNk%7nCvIcQ7$mK_XNBX)P>Vv#-x`JEEDjp9|4Frf%-K_(S7e02#4Soh`t=*gfMBe>)>Om@WoEqWttlP^HYRlp0HK zTi7uHvH4lejsV}rk@ev9A}^_q3Glj+$MxD#?ig4D5*EM&+hl@ObwMfVV16A;VQY*7 zXp%G>t*V`sqMb`+u%0i0E&!`M+D=W%6~V?8zHduLJjxuPBizvl4Im?&h2Cf0!&?$o zf+K6dS%(5Lw=Uon3mzY3GpE}<{PEAcJ4neNVf--|?4B8wxyFm!xwl4uNk5Sl+JBg4 zA4WfMha}%D;ml*53qjZJ<1Fcq(LR>yUC|PB`098%DX3ea<(1G6>W^U8r;)54HZe=Uaq!mgM z^Y3PYC6q7Pi-dQJ0VKR~dTMJ~2#%)o^$gH|Xj)zsifv>GU#a{Oz`Sq+akJV(mJfbC z$U=3DR_9$qpi~ghm*yCC3f_LjY686_km*Aujn4%%|I#{1*31`)=mU_wF;DTmXxq(! zp?{eM2%X23LN6&;`xBoiWCj!98l_Zj3fUCdWfufDWtJS} zaZHSY&YtY>s)7FbP@ZK{X^A3>iMG#}aMlutnx^sJuu(4qjOTZX10$A)jTR3z8G?n_=$6p5s|pH5_i4A0#j_R^r)*SR1?FYoq8C@@ z%%#x8_wwuA@^Agg0_+C-xu}KhT19{3P6BL-8Ql(Xq+&yT`+*@h-^^@+0kDi24`2{1 zhd@;VDE6k!;t8c%1U$d}1?7!I{K8SiGfR1fRSJZ5MfPA`o_6iZVikS6rOpkDUb4xL zy#4anu!fD=m%8^JDIJ|(VcT1i%?xg-6Vcovx!KVXPYd4v6tbF6JT+!XK$Qufmz4Ix z_0sXAwuJZpSs?P>la|;{E)(E6IEn=px+ydwgiz#9co~ezY{WlZ#*?ysA>CFQcmiY! z=x5BpQ?B0`lwp@>>EIK#i(o752xgLe(EBEmDJn9E@>fx-la);*8Ubal@|29)kg1gU zGO2Go{8&0Cb$M8dY^2{n7%}sJqqhEKCNpq{jB}w?nTiuaCv`7|79qXr0 z?4A|jy2-a1zW%PcMtv#fH`JKT-|7vjz6hdwr1x$yM(#UU`S z3sXhKQWzHlGb`c}JuAvqT5F16T~j4hskUZSbcpySaTXPN$HTj5)?{Vj$+JALfZ z=ky2-hmJOb=eoyJUl{n_Y-jvec+FkRFG}VI^DZD08fg$Eyr9sOsU)rQ@QEKS%YW6L zpEA#6VX@RyxFgg0A9SXqP^xkIaQt10GmDfN+1+PMZA0Sn!Hq~tBQ9D!JMul`EaIP) zw=LODAlWDyv7P2|YZayw37a#uUG94`yTHG(dML^gb+hsBN775Fo4Jj2?(SSRgki(O zl_vDhbY;)0XbO>&Q06Z5)qtdVwDO_|oG&)+QLIs=h%QO&eSF8|*iE#}e?O+;ny90x z;!fDiqi;|BWad0!s1;{>7F2vUQU9GY6CJDrPi@SF7E>NP3>*q&GE-(-@Aem9XcHI@ zT1dSo5p4ASUg&V>yWzhaLVzdTf9;vfIz`yQk_JEhoq~mJ9i&))Ud-74QRk8U*B~kU zc}(N}nd$vNQ6B$qsuBvpl5+YI9+UqB?)Y3>iUZcw;AAws%u41{;AA?GIK7g~|4nXG zTsnz3uAwAOE_Ta5aLJ0{Ox_1JZ_&{zF}WhdEb>OBEOv}IZ@n7EN>xu3D~131Q&1kC zD_6yhWqtHLex_2_e+57)Q#V&@T=}bprY)9~fz(uz*{~l;3|D(vl~QCIm)bp+dz?N2 zz=_HH`%CXqrhv&*G{2G$9@|BK%fN`w5Wf=Ca339nGk$pS_#2!-Sn%J@ivg#hNSS*Y z=BNYbshr zJ0|o85b>%X>mUC|c~0E&JO3A-i|RO7KH2>A@+nL3j~^EYJn>W1Y<^#_4)=MA(!cTk z{r&4~IbH5s`0u}(Nc+A@1}oFS`2e~gN7z^m9c~eq_m$2 zaUZqel}37c+FN=oZtocqzQw`T6K$euO_S8qLNg(ZR+wm~XC^-VBzeWAQw5AU-R2NK zw@W4x&pvHt62F;+7exVLAF5tr3PXV^K1&6HC6PZuYe-lGdzOk@TA;g65{)dYVVvd! z!Gf|9;(keoQh+YT#Pq_d{&gRzdqeAlj(cOrs@qB~n=GL?VI7O`Xtq!L^{SYw8FPTR zZ_kf~3GlM1*1(-GpjHtafySPZJ>^D(^b_~OL7NDi*Heu(@sp}%Ity_PzaLNL+0Eo? zAlkZT-{IN&ZS=dx+x;2$L-hXk@?qc43&kI>Q+s3Yo-e8z$L&zM$ArxPigS^5d^J68 z#)>OQ5R}{lqcXys{Z?gE(4pDylgNJueq%&$v;VmCx95^0{L%&4Im)|*d`-8af7&%` zJd+oBfAA$qm0V2euk;hnIKaiM1P{QHh#{e?c<`OiB{e20L3?^p_fb<+qgdg?YP(~aN zP*DL%pnm|sed*(f7%F|a_r&23#rW3rLPGwV?VbYBtJz%5P2gA42e)Vl>%BhFOH6T7 z@5sJAq5pUNdub&4N}?!(Rqf!_P4mL{kLbRx`E)O{Pv2B7H9LP^A4LEwUi~$|Fhfiq zVo&IvfJIU0H!4%y{ts};Z_GGT_(uH6gemoM{BrAwU9nU80c;u#FOwjJy)|J`Th&0NpkSo(AIZgrE!Eh`7Ww;`vg8!gJsG;9k6=66ep z#rrcW&VSckRFYe?BMlM+vvn9ecr05+?j)$jaM>Iud4Q!Dh=|^;8Hk}kqX7YO>ijD{ z$=Y35$^t=kse39g;~`v9omD!x2nQ4>!X=w2l~P3@V|kQ-Fw=qL_~?b(Wq*^OpIfD{ z^vcsdH+~2;$#x{UE3d-fJDL7%rke9kNanL&(+!BD??^5 zEN;#+?v|uLC2z{($sJiIKY~(9^r0m0@OM8hXh)pJ=Zg9|A=m<91Qz>YK{e607N;KJ z^sp4+%+9x>zC2Q-AE1(N$0=bTx!`OsFHu8;81P z9sXI=P7d>Kj^eH0ry89e5;pR5eq7_E%{nsn>jWa4`vhsdr`)G-*c9?U1+LgxVO%hk z;(-B+erk?e_EJIgiwx5HbjOJT=w9gTa2sbKfFPfKgyZml)XSGirc+ol!^8pJjL%EG zN=Rt-O(JN2s*a9Nw-m88H^LOh|2cG_{dMrT*R?cT4CiytC`uW2NA$kR^iq0XpxVw` zdbUhbmpsh5@=heMXbN~IDDtp(32P{9a8|&`brA@3=OVSscFQA);Cn25@@vU7D??kk zxBp|+8GTj8k9Z-k65n)15L}4>Y@kD*Lhv9oV4a9VD+4KNuxX1}0#61b$wkFRW#;u}X;ruNJgb zwka~;G*T}=t@HuB3NQiuTm1iRYmi!{dHH%(tf{tdBhpTZgcAS7yM%XQ{uIwFSFLVp z)!3I)$1ekv&k%ukt#5JQZAoq$5YY%Y5Nl2tgxx@;R=aV}ya~PVrlh^mJ$3)_zQX6X ztL?9!etlbKP!Q(-PSU!~YS$sK;MJl6xyHh9bn8g!REz@htJj7H1^V$4ILHz{_w=yN zj!j~z1lf7^LBpqMa=`krRY&LH0hyrO*Ywh*#g&I%A?_(ztFJp$CKk4S_czWwE8W$R zywoWP6jA&hF(J=Md6M`bVx7LJ>%DlIZ_*2<``%woa#K8s6spIc3UjcXci}BmH~`7d z0df-hfm5!=+aMNr|EMy?FV>s@Ccnnxc}`Dr$#$$+y1RQmRcF#&{gGzLl5<%F3JlU#rSFryg9I+NaWCJ`~ z@C~7T{EmxrW4QeffNR`EM$s}e#eRr;=p*dp+EHl@c60S!u}?3qT4_`C)zyeY-^tBe z!)5J2iLR%t0m0($cA4%ToF}&&E&T2cA9wDY{1kO*$#Lfp;cyH^{;8@ydUsTu^9$8# zv*~y3bLaPr2QoD3dhCWKy@V=qYrpz>+WYO9w*BvA2mYVE0w+iRQT^M1JSnN?>}yVL ziX642-UqZ|NUr7te%`+&&EWHFef<32gW6|$F9z=YLGV5MW%lgmqsR{d@$;uQ&;EXo zf-G`he0)~eib?t|K-I;1w8paZ6zLu8Bdbn_LItnEqPc45$~ta^RflbV58k;G{?JVH z*_?BRFK~sM+V}HAtbl3g*P(WpCe zD3u+Qu34;3a4cF-Ob{0<&>t(kD01NkdYm7#I}p{%WM5V-A&CWd4#a!z#CyXNd<7Hy z;kWovbR3<*TM5xx<b#2y~6jlfm4VcWRSijrD7= zOAM10PdanD_cW0UBc8M)dtdM=DLMR;9PVIT$?qPIey!YdfEj7 zaaP;W41=c9&D|4-ll8p>2F^JLNqSe*I4{V_g_vq{s`tv z@arQqvz88PO*s3NJg;pAEGYv~n?W{`L9v@bg~+54%B0iIWJnSo6^s~jk)Ej0nUzcP z8cGW?rlCVx?9k~zwlo;x?-uc;acbM;pKH--ptGctP0pfsucXZfQw6o{0&|S`w+uZt zHB#lAQ#4rQt~IH)l}+fvqlP0Pt{H4}bcRV8My|QWp}CK0a}5h_;;nacpCIz=gz_A; z^PF~bnYE)>yE7xTeBQO_uYa%*-UV%LWCGjG{{-Ak7-Ch~1?4eh@gr!oEXG612_;8JZcKL-353+6j6 zsFN=&s|D3|gHVKmM)|_(>q0h%yzbhUy(2H*)k?UyI=LbE*+VtF3M?aYvwgMA1HNPh z?Q(<&-3=3R$=`etyY++ynJ?OIaBr6*w(`Yztwy?9j+6xrsfk5;tybpsJyC`n5rSZG zuB9~(INvqCuz-P=*xAQ`b?=9q4GIAMT?^VZz5!_>A;LHapzzgiR}e}Xq=SU; zH{*bHuL`zbvecEb6~4qH{08K?hj!Iwhs~nYvp2Nww78VyR^y!h67ATL}4a?`Zy1bGyEaXLj_)s821rXGtk_P}X z(y5d%tTb?|K25H2UCQMc4Mrv_dwj|JP4`50SA(!?E3jL(n4f3> zHe%K&$uP7db$puer8vg8dN<@=w68WDl*T*$?y2Po>l#w#{=+PxMh1`?>m^@uz zhJ(A%INJGK-|^X$%^I%%@**&9LpuM;BW5@W?}i)@PR>}6tz^D~>#A^XYsd|`qngaz zz9AbiZ`P6@^-!TsGo!ACwTc@LHpKv?ZAsX`AS52xkk^DngRPsJtZ|^LLSbPa2s3Ft zET#F0Q2m=dz7B;l@$aB}J|N5C013FLO%3(PI#905e^Qku#V|J9H%>*aWk*5uV!%(6 zIY1j8#Jd~|%Z_7Xi{n`K+l+4|Z4)yeZcQ=t=lCsjl-0698~(u$WPRDXE1c0=jbyha zsZ;=&B4L_SB+oEVQw%Hw1JuOaz&uefeOnTL9KQ7~skn5s{^ zQgi#=DX7a7^qEi3qk=l`+iwTiI{hAS`|Krf9s^e@BEYJV(vp!A^*oceBj-E07aoMn zR&)9db{E&bu3)W%ejy>EJoz!OXEV9zMkOtG*738U#MoipToAioM;0ZGB- z#D~972L(bQaX?EjX9Pr%x{s6<4<;XEwZub}IN%NPG(G?l;@*WCNQb1Q%ov1a0J4HZ z^Vb|2z`zAFp4_J)~`2LOoi=3t^PKm`Bs!4$X?Y^qQV zR2_(thJtjCAxg)=&`5X@0a1iQ*rLD)3^M`+ ziN{Zr_#n)P#&rNKr>zd#ZyLP?lHd$jC^b9+55C_Z`Vk~UpCbM$CqlMfLzC=Y)w$=Z zwMjSLw#5EqmI@L1PgC->vF}Q4W8o|;C|k!au?fj(O}V7iw&_!vnIE$`A6F`BC2@Qh zu!3!2O;F*l+S&Hq+1Hn|wY6Xz0@qaFCPyeBh)n3;%G

u=AT}%mtu*ZV-nJh(i2C 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 mV#SLc88>$P7;(c.ptr) ? "true" : "false"); return; case ControlType::Text: + case ControlType::TextArea: case ControlType::Password: case ControlType::ReadOnly: - // All three are char-buffer-backed. Password is rendered as a + // All char-buffer-backed. Password is rendered as a // plain JSON string here; the HTTP API obfuscates separately // at the writeControls call site (persistence writes plaintext). // writeJsonString walks the source straight into the sink with @@ -192,6 +194,7 @@ void writeControlMetadata(JsonSink& sink, const ControlDescriptor& c) { // Everything else: no extras. case ControlType::Bool: case ControlType::Text: + case ControlType::TextArea: case ControlType::Password: case ControlType::ReadOnly: case ControlType::IPv4: @@ -262,8 +265,10 @@ ApplyResult applyControlValue(const ControlDescriptor& c, *static_cast(c.ptr) = mm::json::parseBool(json, key); return ApplyResult::Ok; case ControlType::Text: + case ControlType::TextArea: case ControlType::Password: { - // Password parses identically to Text — only serialization differs. + // TextArea and Password parse identically to Text — only the UI render + // (TextArea) or serialization (Password) differs. // c.max is the buffer size; parseString writes up to maxLen-1 then // NUL-terminates, so passing c.max gives "fill the buffer". uint8_t maxLen = static_cast(c.max > 0 ? c.max : 16); diff --git a/src/core/Control.h b/src/core/Control.h index df1558b..0b5aa42 100644 --- a/src/core/Control.h +++ b/src/core/Control.h @@ -89,6 +89,10 @@ enum class ControlType : uint8_t { // across chips. Serializes/parses as a plain integer. Bool, Text, + TextArea, // multi-line text — identical char-buffer storage and parse/persist + // path to Text; differs only in the UI type string so the front-end + // renders a resizable