diff --git a/.github/ISSUE_TEMPLATE/guide_stuck.yml b/.github/ISSUE_TEMPLATE/guide_stuck.yml index 9249797d..ff4ae7cc 100644 --- a/.github/ISSUE_TEMPLATE/guide_stuck.yml +++ b/.github/ISSUE_TEMPLATE/guide_stuck.yml @@ -13,7 +13,7 @@ body: id: where attributes: label: Where in the guide - description: The guide (Build, Play or Make), the chapter and the step. Filled in for you if you came from the guide. + description: The guide (Build, Play, Make or Audio), the chapter and the step. Filled in for you if you came from the guide. placeholder: "Play · 01 Flash, step 5 — Hold BOOT, tap RST, let go." validations: required: true diff --git a/.github/workflows/console-sync.yml b/.github/workflows/console-sync.yml index 68da1dcc..c71f4878 100644 --- a/.github/workflows/console-sync.yml +++ b/.github/workflows/console-sync.yml @@ -49,8 +49,11 @@ jobs: python-version: "3.x" - name: Headers match console/*.html run: python firmware/toolchain/console_pages.py check - # audio-in.html is itself generated, from the browser extension's mapping - # editor. The step above proves the header matches the page; this proves - # the page matches the editor it was assembled from. - - name: audio-in.html matches the extension's editor + # audio-in.html is itself generated: from the browser extension's mapping + # editor and the console/_audio_in* files. The step above proves the + # header matches the page; this proves the page is what those sources + # assemble to, and that it is inside its size budget (BUDGET in the + # script: the stamped page, gzip -9, as the panel sends it). The step + # above reports both too; this one keeps them a line of their own. + - name: audio-in.html matches its sources and is inside its size budget run: python firmware/toolchain/build_audio_in_page.py --check diff --git a/.github/workflows/firmware-build.yml b/.github/workflows/firmware-build.yml index f0441056..5c8d8858 100644 --- a/.github/workflows/firmware-build.yml +++ b/.github/workflows/firmware-build.yml @@ -79,3 +79,15 @@ jobs: # of the split is that the blast radius differs. - name: Internal RAM this composition has already spent run: python firmware/toolchain/check_footprint.py --only ${{ matrix.composition }} + + # The one check of the pattern SDK that needs the Xtensa compiler, so it + # runs here, where the build above has just installed one, and once + # rather than five times. abi/pf_libm.h puts inline code behind floorf, + # fmodf and five more in every .pfm; firmware-checks.yml proves the + # arithmetic on the runner's own CPU, and cannot compile the half of the + # header that exists only in a module build. This builds a small module + # with build_module.py and reads the object: each spelling inline, the + # library call left only as its fallback, no fmaf import. A few seconds. + - name: Module SDK's libm is inline in a module the real compiler built + if: matrix.composition == 'default' + run: python firmware/toolchain/check_module_libm.py diff --git a/.github/workflows/firmware-checks.yml b/.github/workflows/firmware-checks.yml index 6f1a8be8..09a15e53 100644 --- a/.github/workflows/firmware-checks.yml +++ b/.github/workflows/firmware-checks.yml @@ -21,6 +21,17 @@ name: Core knows no features # what every pattern built afterwards renders — and reaches only newly built # modules, since an installed .pfm carries its own compiled copy. # +# check_libm.py — abi/pf_libm.h, which puts inline FPU code behind floorf, +# ceilf, truncf, roundf, fminf, fmaxf and fmodf in every .pfm so that a +# pattern nobody edits stops calling into the firmware's libm per pixel. The +# claim is that each of the seven returns exactly what libm returns, so this +# is equality of bits, not a band: all 2^32 floats through the one-argument +# functions and about three billion comparisons through the rest, against +# this runner's libm. One mismatch fails. It is the slow step here - a +# minute or two. (The half of that header only a module build compiles is +# read out of a built module by check_module_libm.py, in firmware-build.yml, +# where there is an Xtensa compiler.) +# # check_parser.py — the vendored HTTP request parser, the real Parsing.cpp # against a scripted socket and a fake clock. It runs on pf-net, where a loop # that waits for the peer without sleeping starves IDLE0 and the Core-0 @@ -93,11 +104,13 @@ jobs: run: python firmware/toolchain/check_oe.py --sanitize - name: Pattern SDK maths, colour and noise still measure the same run: python firmware/toolchain/check_math.py --sanitize + - name: Module SDK's inline floorf/fmodf return what libm returns, bit for bit + run: python firmware/toolchain/check_libm.py --sanitize - name: Thumbnail mailbox ownership and invalidation run: python firmware/toolchain/check_thumbs.py --sanitize - name: Wi-Fi and name-service recovery run: python firmware/toolchain/check_network.py --sanitize - - name: Runtime admission and frame-boundary lifecycle + - name: Runtime admission, resident modules and frame-boundary lifecycle run: python firmware/toolchain/check_runtime.py --sanitize - name: Crash record tells this reset's dump from an older one run: python firmware/toolchain/check_crash.py --sanitize diff --git a/.gitignore b/.gitignore index e6ccd81c..1302a653 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ out/ # Local staging for a release upload (bins duplicated from the flasher folder). firmware/release/ firmware/patternflow/patternflow_secrets.h +firmware/patternflow/patternflow_secrets.h.shelf-aside # KiCad 자동 생성/백업 *.kicad_prl diff --git a/AGENTS.md b/AGENTS.md index 4181b5df..2641084d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -11,7 +11,7 @@ It is standalone (not Eurorack), powered from a 5 V power bank through a screw t - `docs/` — contracts and long-form docs: `EDITIONS.md` (the firmware seam — read it before touching firmware), `rest-api.md` / `osc-spec.md` / `midi-spec.md` / `audio-ws-spec.md` (wire contracts integrations are built against), `pfst-v2-spec.md` (show files), `panel-compatibility.md`, `RELEASING.md`, `SERVICES.md` (production server ops), the assembly map (`docs/assembly/`), the manifesto, investigations, and media (build-guide images in `docs/build-guide/`). Root-level guides: `BUILD_GUIDE.md` (the **v3.0 board**), `BUILD_GUIDE_v2.md` (legacy v2.x board), `PATTERN_GUIDE.md` (community → deck → Pattern Lab → device), `FEATURE_GUIDE.md` (writing a firmware feature or edition), `AUDIO_GUIDE.md` (sound into the panel). The live roadmap is patternflow.work/journal and the `/roadmap` page. - `firmware/` — PlatformIO/Arduino code for the ESP32-S3. Main sketch folder: `firmware/patternflow/` with `patternflow.ino`, `config.h` (pin mappings, brightness, limits, LED calibration), `net_config.h` (Wi-Fi / Improv / OTA / self-update defaults and the lane scale — core only; a feature's own tunables live in `features//_config.h`, and settings TUNE a feature, they never add one; per-device secrets in gitignored `patternflow_secrets.h`), and `pattern_registry.h` (function-pointer pattern table; compiles in Origin alone — the rest of the curated set ships as the Basics pack). Shared engine code lives in `firmware/patternflow/src/` (`core_display.h` HUB75 driver, `core_encoders.h`, canvas/color/math/noise helpers, Wi-Fi/OTA/web-console modules, the hotspot in `core_hotspot.h`) — core only: OSC, audio, MQTT, weather, shows, MIDI and the clock all live in `features/`. `firmware/patternflow/abi/` is the frozen host ⇄ module contract. Curated patterns live in `firmware/patternflow/presets/` (`preset_origin.h`, `preset_wave_saw.h`, dated presets), each using its own pattern namespace; user patterns are `.pfm` modules uploaded to the device over Wi-Fi, built from `firmware/modules/` via `firmware/toolchain/build_module.py` (the old `custom1.h`–`custom3.h` root slots are gone). Features attach through `firmware/patternflow/features/` (named `addons/` until 2026-08-30; legacy spellings are shimmed for out-of-tree bundles — see docs/EDITIONS.md) — a directory and a descriptor of function pointers, with the core naming none of them (see its README). `firmware/bundles/` names firmwares built from the same tree (the shelf editions `audio` and `performance`, plus `clock` and `midi`, compositions the tree keeps buildable but does not publish; the default build has no bundle): two files each, saying which features compile in and what the build calls itself; `firmware/bundles/build.sh` builds the default or a named one, `shelf.sh` stages a publishable image. Device console pages are plain HTML in `firmware/patternflow/console/`, spliced into `*_index.h` by `firmware/toolchain/console_pages.py`. Tooling lives in `firmware/toolchain/` (repo-level scripts) and `firmware/patternflow/toolchain/` (PlatformIO `extra_scripts`, which must stay sketch-relative). Also: `firmware/encoder_test/` (standalone encoder diagnostic) and `firmware/CUSTOM_PATTERNS.md` (pattern authoring guide). - `hardware/` — Hardware designs. `case/` (Blender source in `source/`, STLs by printer bed size, `legacy_v2/` for the v2.x board), `pcb/` (KiCad 10.0 source, Gerbers per revision, schematic PDF), `bom/` (`bom_v3.9.csv` — **the BOM source of truth**; the guide's table is derived from it. `bom_v3.0.csv` is kept for the previous board). -- `web/` — Next.js site at patternflow.work: landing page (`/`, `/pattern`, `/build`, `/inside` are tabs of one view), the interactive guide (`/guide` is a hub over `/guide/build`, `/guide/play` and `/guide/make`, each with a `/ko` twin; one 3D stage in `components/guide/`, the firmware simulator in `lib/guide/`), Pattern Lab (`/pattern-lab`), community (`/community/**`, SQLite + Drizzle + Better Auth, only on the Pi deployment), browser flasher (`/flash`), the edition shelf (`/editions`; `/variants` redirects there), the feature catalogue (`/features`, reels per feature), device update handoff (`/update`), journal, roadmap. `lib/pattern/` is the pattern runtime and annotations, `lib/lab/` the Pattern Lab, `lib/community/` the community, `lib/ai/` the Gemini client. Architecture doc: `web/ARCHITECTURE.md`. The JS presets in `web/src/lib/presets/` are the source of truth for firmware preset headers. +- `web/` — Next.js site at patternflow.work: landing page (`/`, `/pattern`, `/build`, `/inside` are tabs of one view), the interactive guide (`/guide` is a hub over `/guide/build`, `/guide/play`, `/guide/make` and `/guide/audio`, each with a `/ko` twin; one 3D stage in `components/guide/`, the firmware simulator in `lib/guide/`), Pattern Lab (`/pattern-lab`), community (`/community/**`, SQLite + Drizzle + Better Auth, only on the Pi deployment), browser flasher (`/flash`), the edition shelf (`/editions`; `/variants` redirects there), the feature catalogue (`/features`, reels per feature), device update handoff (`/update`), journal, roadmap. `lib/pattern/` is the pattern runtime and annotations, `lib/lab/` the Pattern Lab, `lib/community/` the community, `lib/ai/` the Gemini client. Architecture doc: `web/ARCHITECTURE.md`. The JS presets in `web/src/lib/presets/` are the source of truth for firmware preset headers. - `tools/` — desktop-side helpers: `patternflow-audio-extension` (the browser audio-react extension — also the authoring source the device's `/audio-in` console page is assembled from), `patternflow-audio-android` (phone capture app), `rtpmidi-probe`. - `integrations/` — host-software integrations, each with its own README. `integrations/ableton/` is the Max for Live bridge (knobs → Live parameters over OSC). The Home Assistant integration left this repository on 2026-09-03; its author maintains it separately. Integrations are built against the contracts in `docs/` (`rest-api.md`, `osc-spec.md`, `midi-spec.md`), not against the firmware source. `docs/rest-api.md` also has the table for choosing between HTTP, OSC and MQTT, and the rules that make the device's single-connection web server easy to knock over. @@ -33,8 +33,8 @@ It is standalone (not Eurorack), powered from a 5 V power bank through a screw t - KiCad exports: Export Gerbers from `hardware/pcb/kicad/patternflow.kicad_pcb`. Export STLs from `hardware/case/source/patternflow_case.blend`. ## Versioning -- Project: v3.10.5 (current, released 2026-10-02), using unified semantic versioning across firmware, hardware, web, and docs — the firmware reports it as `PF_IMPROV_FW_VERSION` in `net_config.h`, and `CHANGELOG.md` gets a section per release. Current hardware is the v3.9 board — the v3.0 board with the USB-C footprint removed, same enclosure, pin map and guide; every v3.x release has been firmware/web on it. -- Editions carry their own version line, independent of the project version (`PF_VARIANT_VERSION` in `firmware/bundles//overrides.h`): Audio v0.6.5, Performance v0.4.0 at the time of writing. The shelf (`web/src/app/editions/editions-data.ts`) and the flasher manifest (`web/public/flash/manifest.json`) name the images that are live; only those images are kept in `web/public/flash/bin/` — older ones live on their release tags. The one exception is a try-out image: a frozen copy of a composition that is not on the shelf, named by the features page (`tryOut` in `web/src/app/features/features-data.ts`) so people can install it to try, never bumped with the core, and retired only by a newer try-out of the same name. `clock` and `midi` are bundles the tree keeps building (CI compiles every composition) but does not put on the shelf: the clock is a feature, and the MIDI bundle is the composition that proves the USB-OTG build; each has a try-out image (`clock-v0.1.5`, `midi-v0.1.0`). Neither is in the `EDITIONS` tuples of `release.py`/`check_versions.py`; an edition joins those, the cards and the line above only when it goes on the shelf, and its maintainer cuts it with `release.py edition vA.B.C` (docs/RELEASING.md). +- Project: v3.11.0 (current, released 2026-10-04), using unified semantic versioning across firmware, hardware, web, and docs — the firmware reports it as `PF_IMPROV_FW_VERSION` in `net_config.h`, and `CHANGELOG.md` gets a section per release. Current hardware is the v3.9 board — the v3.0 board with the USB-C footprint removed, same enclosure, pin map and guide; every v3.x release has been firmware/web on it. +- Editions carry their own version line, independent of the project version (`PF_VARIANT_VERSION` in `firmware/bundles//overrides.h`): Audio v0.7.0, Performance v0.4.0 at the time of writing. The shelf (`web/src/app/editions/editions-data.ts`) and the flasher manifest (`web/public/flash/manifest.json`) name the images that are live; only those images are kept in `web/public/flash/bin/` — older ones live on their release tags. The one exception is a try-out image: a frozen copy of a composition that is not on the shelf, named by the features page (`tryOut` in `web/src/app/features/features-data.ts`) so people can install it to try, never bumped with the core, and retired only by a newer try-out of the same name. `clock` and `midi` are bundles the tree keeps building (CI compiles every composition) but does not put on the shelf: the clock is a feature, and the MIDI bundle is the composition that proves the USB-OTG build; each has a try-out image (`clock-v0.1.5`, `midi-v0.1.0`). Neither is in the `EDITIONS` tuples of `release.py`/`check_versions.py`; an edition joins those, the cards and the line above only when it goes on the shelf, and its maintainer cuts it with `release.py edition vA.B.C` (docs/RELEASING.md). - Firmware source lives in `firmware/patternflow/`; use release tags for versioning instead of encoding the release in the folder name. Tags are `vX.Y.Z`. - Conventions: filenames lowercase with underscores; commit messages start with the area (`firmware:`, `web:`, `docs:`, `hardware:`) then a short present-tense summary. diff --git a/AUDIO_GUIDE.md b/AUDIO_GUIDE.md index f0448d77..6460a30e 100644 --- a/AUDIO_GUIDE.md +++ b/AUDIO_GUIDE.md @@ -6,12 +6,15 @@ know where knob values come from. There are two main ways in, and this guide covers both: the **Chrome extension** (any browser tab becomes the source) and the **on-board -microphone** (a $5 part and four solder joints, and the panel hears the +microphone** (a $5 part and five thin leads, and the panel hears the room with no computer involved). A phone app exists too — it's covered briefly at the end — and if you work in a DAW, the panel is also a **MIDI device** ([its own section](#midi--the-panel-as-a-midi-port)) and speaks OSC ([here](#osc--ableton-today-anything-tomorrow)). +The same ground step by step, on a 3D Patternflow in the browser: +[patternflow.work/guide/audio](https://patternflow.work/guide/audio). + Like the [Feature Guide](FEATURE_GUIDE.md), this page ends with a section written for AI coding agents; everything above it is for people. @@ -41,24 +44,42 @@ locally, and streams four knob lanes to the panel over your LAN. **Install:** it's not on the Web Store — load it straight from this repo. `chrome://extensions` → enable *Developer mode* → *Load unpacked* → pick [`tools/patternflow-audio-extension/`](tools/patternflow-audio-extension/). -(Details and screenshots in its [README](tools/patternflow-audio-extension/README.md).) - -**Use:** open a tab with sound → click the extension → **Start** (it captures -the active tab) → enter your panel's address (`patternflow.local` or its IP) -→ the knobs move with the music. (Make sure **Audio-React (AUD)** is turned on -at the top of the console's **Audio** page or via the panel's NETWORK screen.) +After pulling a newer copy, press *Reload* on its card there. Reload keeps the +mapping you already had, so the starting point described below is a new +install's. (Every control is described in its +[README](tools/patternflow-audio-extension/README.md), and so is how to get +that starting point on an older mapping.) + +**Use:** open a tab with sound → click the extension → enter your panel's +address (`patternflow.local` or its IP) → **Start** (it captures the active +tab). The corner of the popup says **Live** once the panel is being driven, +and the knobs move with the music. If it says **Aud off**, the panel's +Audio-React switch is off and the panel is ignoring what arrives: press +**Turn on** in the popup. (The same switch is the `AUD` row of the panel's +NETWORK screen and the *Browser extension and phone app* row on the console's +**Audio** page.) If it says **Error**, the line under the address says what +is wrong: no panel at that address, or a panel that does not run the Audio +edition. + +While the tab is silent or paused the extension holds the knobs, each where +its band rests, until you press **Stop**. A knob you turn by hand during a +pause goes back there about five seconds after you let go. **Then open the editor** (*Mapping editor ↗* in the popup) — this is where it gets -good. Each of the four knobs is a **box drawn on the live spectrum**: the -box's width is the frequencies it listens to, its height the loudness window -it maps. Drag a box over the bass and knob 1 becomes a bass knob. Inside -each box a **response curve** (presets or a hand-dragged bezier) shapes how -it moves, an **output range** decides how far, and **attack/damping** set -the ballistics — fast rise, slow fall. Boxes start in **auto**, breathing -with the music; grab an edge to take manual control. A **preview** popup -shows how a test signal would ride your settings before you commit. - -Boxes map to knobs 1:1 and that's fixed on purpose — box 2 *is* knob 2. +good. Each of the four knobs, **K1** to **K4**, is a **box drawn on the live +spectrum**: the box's width is the frequencies it listens to, its height the +loudness window it maps. Drag K1's box over the bass and knob 1 becomes a +bass knob. Pick a knob with the tabs under the plot, and its card has a +**response curve** (presets, or a bezier you drag by its two handles) that +shapes how it moves and an **output range** that decides how far; **Attack** +and **Damping**, above the plot, set the ballistics for all four — fast rise, +slow fall. A new install starts in **Auto range** with the Smooth curve: the +boxes follow the loudness of the music. Drag a box's top or bottom edge to +take the windows over by hand; switching Auto range off goes back to the +windows you set. **Preview** puts a scope where the plot is and shows how a +test signal would ride the selected knob's settings while you change them. + +Boxes map to knobs 1:1 and that's fixed on purpose — K2 *is* knob 2. ## The on-board microphone @@ -74,7 +95,7 @@ until you solder one on and switch it on. | Part | What to look for | Notes | | --- | --- | --- | | PDM MEMS microphone breakout | **Adafruit PDM MEMS Microphone Breakout #3492** (MP34DT01-M), or any clone whose pin row reads **3V · GND · SEL · CLK · DAT** | ~$5. This is the one in the photo. | -| Hookup wire | 4 thin leads, ~10 cm | Shorter is better — see routing note below. | +| Hookup wire | 5 thin leads, ~10 cm | Shorter is better — see routing note below. | **Don't buy these instead:** an **INMP441** or other standard I²S mic (it needs three signal pins — this board has exactly two free), or an analog @@ -82,7 +103,7 @@ electret module (no ADC pin is free at all). It must be **PDM**. ### Wiring -Four leads, five pads: +Five leads, four places on the DevKit (GND takes two): ``` mic breakout ESP32-S3 DevKit @@ -93,11 +114,11 @@ Four leads, five pads: │ CLK ───────┼──┼───────────► TX (GPIO 43) │ DAT ───────┼──┼───────────► RX (GPIO 44) └──────────────┘ │ - └──────────► GND ← SEL goes to ground + └──────────► G (GND) ← SEL goes to ground: the same GND pin ``` -- **Don't forget SEL → GND** — jumper it to the breakout's own GND pad so it - shares the same lead. It selects the LEFT channel, the slot a mono read +- **Don't forget SEL → GND** — it is a lead of its own, soldered to the same + GND pin as the GND lead. It selects the LEFT channel, the slot a mono read uses; tied high, the mic reads silence while looking perfectly healthy. - The DevKit silkscreen says **TX / RX**, not 43/44 — those are the ones. They're free because the console talks over native USB, and they are the @@ -109,8 +130,8 @@ the main board: 1. **Pull the DevKit out** of its sockets (straight up). 2. Solder the thin wires on the **top side**, where the header pins poke - through the module — four spots: TX, RX, 3V3, GND (that's what the photo - shows). + through the module — five wires on four spots: TX, RX, 3V3, and GND + twice (that's what the photo shows). 3. Route the wires out the top and plug the DevKit back in. Fully reversible — to undo it, just remove the wires. Two cautions: while @@ -125,22 +146,37 @@ Stick the mic wherever sound reaches it. Done. ### Turn it on -Console → **Audio** page (`/audio-in`) → flip **Microphone** on. (**Audio-React (AUD)** beside it is the other input, the extension and the phone app.) That's the -whole switch: on means listening and driving the knobs, off releases the -hardware completely. The **gain** slider (1–16, default 8) is there if your -room runs quiet — PDM mics on this chip are famously low-amplitude, and gain -is the official answer. - -The same box editor from the extension lives on this page, dark-themed — -same boxes, same curves. Mic settings are stored **on the panel** and -survive reboots and firmware updates; *Reset all* puts everything back. +Console → **Audio** page (`/audio-in`) → under **Sources**, flip +**Microphone** on. That's the whole switch: on means listening and driving +the knobs, off releases the hardware completely. The chip beside it reads +`listening` once samples are arriving, and the line at the top of the page +says who has the knobs. The **Input gain** slider (1–16, default 8) is there +if your room runs quiet — PDM mics on this chip are famously low-amplitude, +and gain is the official answer. (The row under it, *Browser extension and +phone app*, is the other input: the same switch as `AUD` on the panel's +NETWORK screen.) + +The same box editor from the extension lives on this page, under +**Mapping**, in the console's own look (light, or dark from the +toggle in the header) — same boxes K1 to K4, same curves, same Preview. Its +level axis is the microphone's, about −45 to +2 dB, and its frequencies stop +at 8 kHz. Changes save to the panel as you make them; mic settings are stored +**on the panel** and survive reboots and firmware updates. *Reset K1* puts the +selected knob back to the firmware's default. *Reset mapping* asks to be +pressed twice and then resets all four knobs, their curves, attack, damping +and input gain; it leaves the Microphone switch and Auto range as they are. ### If something's off - **Silence, but everything looks healthy** → SEL isn't grounded. This is - the classic one. -- **Mic not detected** → the firmware notices an absent/miswired mic and - falls back to a synthetic test source, and says so on `/api/status`. + the classic one, and nothing can detect it: a mic with SEL tied high reads + as a quiet room. +- **The chip says `not detected`** → the firmware sees nothing on the data + pin: no microphone, or no DAT lead (DAT goes to RX). Nothing is driven. +- **The chip says `not answering`** → the microphone was there and has + stopped returning samples, or its driver did not start. The knobs are left + alone in this state too. `source` in `GET /api/audio-in` and in + `/api/status` has the firmware's own word for each of these. - **The top band barely moves at normal volume** → physics, not a fault: at room levels the highest octaves carry almost no energy. The auto range is tuned around this. @@ -203,9 +239,22 @@ microphone on any lane it's driving. The mic takes whatever's left. So you can leave the mic on and still grab a knob whenever you want — it comes back to the music a few seconds after you let go. +A value set from the console's knob sliders, by MIDI CC 20–23 or by +`POST /api/params` outranks all of them: that knob stays where it was put, and +sound does not move it, until that encoder is turned. The Audio page's line +does not show this. + +The extension keeps driving its knobs through a silent or paused tab, so with +both on, the microphone does not take over during a pause. Press **Stop** in +the extension to give the knobs to the room, or **Mute** a knob in the +extension's editor to give the microphone just that one. The phone app does +not hold yet: half a second into a pause its knobs go back to the encoders, +or to the microphone if that is on. + One thing to know: the **extension keeps its mapping in the browser**, while the **mic and the phone app share the config stored on the panel**. Same -editor everywhere, two homes for the settings. +editor everywhere, two homes for the settings. The top of the console's Audio +page says which source has the knobs right now. --- @@ -223,8 +272,8 @@ You were pointed here to work on Patternflow's audio path. The map: | `feature_audio_in.h` | Descriptor, the sampling/analysis task, where levels enter the knob pipeline. | | `core_audio_pdm.h` | PDM mic driver (GPIO 43/44). Its header comment is the authoritative wiring + driver-choice record. | | `core_audio_fft.h` | FFT and spectrum buckets. | -| `core_audio_in_map.h` | Bands, curve LUTs (33-point, interpolate-only), attack/damping glide, auto-range, NVS persistence. | -| `core_audio_in_http.h` | `/audio-in` (editor page), `GET/POST /api/audio-in` (whole config), `POST /api/audio-in/reset`, the `?levels=1` live poll, and the frame endpoint the phone posts spectra to. | +| `core_audio_in_map.h` | Bands, curve LUTs (33-point, interpolate-only), attack/damping glide, auto-range, the defaults a reset restores, NVS persistence. Manual mode maps linearly in amplitude between a band's two edges; the page's dB axis is a conversion at the page's edge. | +| `core_audio_in_http.h` | `/audio-in` (editor page), `GET /api/audio-in` (the configuration, read once), `POST /api/audio-in` (any subset: a request changes only the fields it carries), `POST /api/audio-in/reset` (everything, or one band with `band=N`), the `?levels=1` live poll, and the frame endpoint the phone posts spectra to. The contract is [`docs/rest-api.md`](docs/rest-api.md#microphone-audio-edition); the phone app is a second client of it, so fields are added, never renamed. | | `audio_in_index.h` | **Generated** — never edit (chain below). | **Firmware — MIDI** ([`firmware/patternflow/features/midi/`](firmware/patternflow/features/midi/)): @@ -237,37 +286,83 @@ AppleMIDI library. Contract: [`docs/midi-spec.md`](docs/midi-spec.md). ([`firmware/patternflow/features/audio/`](firmware/patternflow/features/audio/)): `core_audio_ws.h` is a WebSocket server on port 81. The wire contract is [`docs/audio-ws-spec.md`](docs/audio-ws-spec.md) — clients build against the -spec, not the source. Probe `GET /api/status` for `"audio"` in `caps`. +spec, not the source. Probe `GET /api/status` for `"audio"` in `caps`. A lane +is released 500 ms after the last message that set it, so a sender that means +to hold a value resends it; the spec says which senders in this tree do. +`feature_audio.h` has the Audio-React switch and its two routes +(`GET`/`POST /api/audio`). -**The editor is authored once and baked down.** Edit at the top, regenerate -downward; CI (`console-sync.yml`) fails on drift: +**The editor is authored once and assembled for the panel.** The panel's +`/audio-in` page is the extension's editor inside the panel's own frame, and +one command builds it; CI (`console-sync.yml`) fails when the generated files +are not what the sources assemble to, or the page is over its size budget: ``` -tools/patternflow-audio-extension/editor.{js,html,css} ← edit here - → python firmware/toolchain/build_audio_in_page.py (dark theme + device adapter) - → firmware/patternflow/console/audio-in.html - → python firmware/toolchain/console_pages.py build - → firmware/patternflow/features/audio_in/audio_in_index.h +tools/patternflow-audio-extension/ + editor.html the editor's body (between its EDITOR BODY lines) + editor.css, mapping.js down to their EXTENSION ONLY line + editor.js all of it +firmware/patternflow/console/ + _audio_in_bar.html the page around the editor: heading, Sources, footer + _audio_in.css the console's look + _audio_in_adapter.js window.PFAdapter over /api/audio-in + ↓ python firmware/toolchain/console_pages.py build +firmware/patternflow/console/audio-in.html generated +firmware/patternflow/features/audio_in/audio_in_index.h generated ``` +Preview without building: `python firmware/toolchain/console_serve.py` +assembles `/audio-in` from those sources on every request and answers its API +the way the firmware does; `/mock?audio=music`, `silence`, `off`, `fresh`, +`nomic`, `stalled`, `phone`, `ext` and `noread` pick what the panel hears +([`console/README.md`](firmware/patternflow/console/README.md#the-page-that-is-assembled-audio-in)). +The page has a budget, `BUDGET` in `build_audio_in_page.py`: 17,500 bytes +gzipped as the panel sends it. It is the heaviest page in the console and +every byte crosses the panel's Wi-Fi, so whole-line comments and indentation +are dropped as it is assembled and what only the extension needs stays below +the `EXTENSION ONLY` lines. + +The seam between the editor and whatever feeds it is `window.PFAdapter`. The +contract is the comment at the top of `editor.js`: the source owns its +limits, its level axis, its defaults and where a level sits in a window, and +the editor draws what it is told. The page's adapter follows the console's +rules for a one-connection server (`console/README.md`, "Writing a page"): +every request rides `PF.poll`/`PF.status`, nothing is saved until the +panel's mapping has been read, and a save sends only the fields that changed. + **Chrome extension** ([`tools/patternflow-audio-extension/`](tools/patternflow-audio-extension/)): -`editor.js` is the single shared editor module (adapter-isolated — -`editor-adapter.js` binds chrome storage/frames, and includes a synthetic -demo mode when opened outside the extension). `offscreen.js` does FFT → -band mapping → glide → WS send. `popup.*` is the capture console; -`background.js` owns capture lifecycle. Extension config lives in -`chrome.storage`, not on the panel. +`mapping.js` is the mapping model, one object (`PFMap`): defaults, the +curves, level → knob. `editor.js` is the single shared editor module, and +`editor-adapter.js` is its extension adapter (chrome storage, the capture's +frames), the editor's source bar, and a synthetic demo source when the page +is opened outside the extension. `offscreen.js` holds the capture: FFT → +`PFMap` → glide → WS send, the resend that holds a steady value, and the +state every page prints (`phase`). `popup.*` is the capture console and owns +the panel's address; `background.js` relays between the pages and the +offscreen document. The mapping lives in `chrome.storage` +(`patternflowAudioConfig`; the address under `patternflowAudioHost`), not on +the panel. The extension keeps its cream skin; only the panel's page wears +the console's. **Android app** ([`tools/patternflow-audio-android/`](tools/patternflow-audio-android/)): -`Analyzer.kt` mirrors `offscreen.js`'s math; `DeviceLink.kt` speaks the WS +`Analyzer.kt` mirrors the extension's analysis and mapping (`offscreen.js` +and `mapping.js`); `DeviceLink.kt` speaks the WS contract + syncs panel config over `/api/audio-in` + posts monitor frames; `CaptureService.kt` is the playback-capture foreground service; `EditorActivity.kt` is a WebView onto the panel's `/audio-in`. Build recipe -in its README. +in its README. Three things it carries by hand and that a change elsewhere +can leave behind: it sends lanes only when they change, so it does not hold a +steady value (the spec has the fix); it has its own copy of the three bezier +presets, by the names the page stores (`p:smooth`, `p:sharp`, `p:fall`); and +its WebView is set up without the part that shows a `window.confirm` dialog, +which is why the page's Reset mapping is a button pressed twice. Open the +app's editor once before releasing a change to `/audio-in`. Rules of the road: firmware changes follow [FEATURE_GUIDE.md](FEATURE_GUIDE.md) (hooks, checkers, `build.sh all`); protocol changes update the spec in the -same PR; editor changes regenerate the chain above and keep the extension -and console page identical — they are the same file, which is the point. +same PR; API changes extend [`docs/rest-api.md`](docs/rest-api.md) and never +rename what is there; editor changes run `console_pages.py build` so the +panel's page follows — the editor's body is one file for both surfaces, which +is the point. diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a051000..9f17ab15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,77 @@ All notable changes to Patternflow will be documented in this file, newest first ## [Unreleased] +## [3.11.0] - 2026-10-04 + +### Firmware + +- **Going back to a pattern no longer loads it again.** An installed pattern (a `.pfm` module) was unloaded the moment another pattern took the panel, so every return paid for a whole load: reading the file, relocating it, its constructors and its own `setup()`. For most of the catalogue that is 25-100 ms; for the heavy ones it was the pause you could see - Branched Flow 3.3 s, Two Stream 4.1 s, Burgers and Wave Cascade 0.6 s each. A module that has run now stays loaded in PSRAM ("parked") when another pattern takes over, and picking it again resumes it: nothing is read or relocated, `setup()` does not run again, and the pattern carries on from where it was, as the built-in presets always have. Measured on two boards, a return takes 115-197 µs. Picked while another pattern is still loading, it comes back once that load is done; picked during an install, it is loaded afresh afterwards, because an install empties what is parked. Up to 16 modules stay parked (`PF_MODULE_RESIDENT_MAX`; `0` turns this off). +- **SELECT shows patterns that are already in memory live.** A preset or a parked module comes on as the knob reaches it and runs behind the overlay instead of showing its thumbnail, as long as the pattern being left can be kept too. A pattern that has to be loaded still waits for the knob to rest. +- **A module's data now goes to PSRAM first, as its code already did.** Only a module that holds no internal RAM at all is kept, so the console and Wi-Fi give up nothing for parked patterns. Measured A-B-B-A on two boards: 13 of 16 module runs within noise (-1.1..+0.8%), Burgers +2.6% (38.9 -> 39.9 ms a frame), Wave Cascade +2.2%, Breakout Arcade +0.85%; in exchange 4-6 KB more internal heap with a module running, up to 9.9 KB. +- **Parked patterns give their memory back.** Least recently used first, whenever a new load would otherwise start with less than 4.5 MB of PSRAM free (the most one pattern may allocate, plus 512 KB), and whenever a running pattern's own allocation would leave less than 512 KB. A pattern installed, replaced or deleted - from the console, or by a feature that installs patterns - is never resumed from the old file. Checked on two boards with 12-13 modules parked: internal heap stayed flat, an install emptied the table and PSRAM came back by exactly the parked bytes (683 KB), and with five 1.5 MB test modules PSRAM never fell under about 3.1 MB free, with no refused load. `/api/status` gains `load.resumed`, `load.resumeUs` and `moduleMemory.resident`. +- **What this takes away:** a pattern that wants a fresh start every time it is picked cannot have one. `setup()` runs once per load, as `firmware/CUSTOM_PATTERNS.md` now says. A restart of the panel, or any install, delete or reorder on the Patterns page, is what starts one again. +- **The console is light, and its six core pages look like one thing.** It opens light; dark is still the toggle in the header, and a choice made there is remembered. Console, Patterns, Status, Wi-Fi, Knobs and Update now share one set of parts - buttons, chips, list rows, fields, notices, section titles - from a base block every page opens with (`console/README.md`, "The look"), with one accent, the LED orange, kept for what is live: the current tab, the playing pattern, the panel being on. The home page leads with the panel itself: what is playing with its arrows, on/sleep, brightness and the four knobs, with the device's facts under them in the same card; the arrow keys step through patterns as the arrow buttons do. Which edition the panel runs has a card of its own instead of being one more row among the facts: its name, its versions, the feature pages it carries as links, the update notice when there is one, and a button to the other editions (or a line saying the panel's own hotspot has no internet). The numbered index of every page with a paragraph each is now a list of one-line links. The header no longer scrolls sideways on a phone: Console and Patterns, the build's own feature pages, then Wi-Fi, Knobs, Update and Status, wrapped onto rows. Long explanations are a line, with the rest behind a disclosure where it is needed. Windows showed every small label in Courier New because no font in the monospace list was a Windows one; the list now includes Cascadia Mono and Consolas. The Knobs page has an *All* row that sets direction and edges per click for the four at once (it reads *Mixed* while they differ). The Status page shows the crash record (`crash` in `/api/status`) when there is one, with a button to clear it. Feature pages are not edited and take the light palette from the header. Home is 8.9 KB gzipped (was 10.9), Knobs grew by 0.15 KB for its All row, the others are within 40 bytes of what they were, and the header script is 9.6 KB. On a panel on the home network a page still opens in about 0.1 s the first time and in under 0.01 s after. Every request, control and message the pages had is still there; checked on a panel by stepping through patterns, sleeping and waking it, dimming it, dragging a knob and playing a pattern from the list. The guide's live console demo is regenerated to match. +- **The Audio page is a console page now: it says who has the knobs, and it saves only what you changed** (Audio edition). `/audio-in` was the one page the light console left behind: its own dark palette, 12 px type, square corners, a second brand line. It opens light like the rest now, with the console's buttons, chips and list rows, and it does not scroll sideways on a phone (checked at 390 and 320 px). Above the editor is a **Sources** list. *Microphone* has a chip that reads `off`, `starting`, `listening`, `not detected` (nothing on the data pin; the page says to check the DAT lead) or `not answering`, with the input gain beside it; a board with no microphone fitted used to look the same as a healthy one in a silent room. *Browser extension and phone app* is the Audio-React switch, the same one as `AUD` on the NETWORK screen, with `off`, `waiting`, `connected` or `phone app`, shown only on a build that has that path. The line at the top of the page says which of them has the knobs. It does not know about a knob pinned from the home page's sliders, MIDI CC 20-23 or `/api/params`; that knob stays pinned until its encoder is turned. Under it the mapping editor is the extension's, with what that gained in this release (see Tools): the knobs are K1 to K4 with their tabs under the plot, the level axis is in dB, and Preview takes the plot's place. On this page the line in each box, the cursor on the curve and the readout now show where the firmware has the level (`outputs` and `levelsN` from the poll); the page used to work that out a second time on its own dB scale, which is not the scale the firmware maps a hand-set window on. A panel that has heard nothing draws its stored windows dashed, where it drew four boxes flat on the axis. *Reset K1* asks the firmware for that knob's default: the page's own table of defaults was the extension's, and put a 5-16 kHz band on an axis that ends at 8. *Reset mapping* is pressed twice and says what it resets, in place of a `confirm()` dialog, which the phone app's WebView is not set up to show. Saving: nothing is written until the panel's mapping has been read. A read that failed used to leave the editor on the extension's defaults with nothing on screen to say so, and the next touch saved four default bands over the panel's own. An edit now sends the fields that changed, one request at a time - an Attack nudge is `attack=0.611`, a box's top edge is that band's `inMin` and `inMax` - where every edit posted the whole mapping, 41 arguments, and two open tabs wrote their stale copies over each other's edits. A save the panel did not take is said so, tried once more, and kept for the next change; an edit made just before leaving the page is still sent. Polling: the levels poll rides the console's shared lane (`PF.poll`) as every other page's requests do, so it waits behind an upload in another tab and slows itself on a slow link. Measured on the mock, about 9 polls a second with the microphone on, one every 1.5 s with it off (it had been 9.2 a second there too, for numbers that cannot change), one a second while the panel is not answering, and 0.6 a second on a link as slow as the hotspot. Weight: 17.2 KB gzipped as the panel sends it, where it was 19.9, because comments and indentation are dropped as the page is assembled; CI fails the page above 17.5 KB. `console_pages.py build` assembles the page as its first step, so there is one build command where there were two, and `console_serve.py` serves it assembled from its sources on every request against a port of the firmware's audio handlers, with `/mock?audio=` states for music, a quiet room, no microphone, a stalled one, the phone app and a read that never answers. Checked on that mock in Chromium, light and dark, with no console errors in any state, and on a panel with the microphone: the mapping stored on it survived the update, the page read before it wrote, one slider step posted one field (`attack=0.645`) and held across a reload, and frame time and microphone drops were unchanged with the page open. Not yet on a phone or in the phone app's WebView. +- **The Audio edition transmits at the core's Wi-Fi power** (19.5 dBm). It had been left pinned at 17 dBm when the core's 13 dBm cap was retired in 3.10.5, so it was the one build with less than full power. +- **A microphone that is not delivering no longer moves the knobs, and the microphone API takes one field at a time** (Audio edition). When the PDM driver did not start, or the microphone stopped returning samples, the analysis carried on over its built-in three-tone test signal and those tones reached the knobs, while `source` said `synth (mic stalled)`. The microphone now drives the knobs only while it is returning samples. `POST /api/audio-in` applies a band when any one of its suffixed fields is present; before, a request carrying only `inMax2` was answered `{"ok":true}` and changed nothing, because only `hzMin`, `lut`, `knob`, `muted` or `outMin` counted as naming a band. A `band` outside 0..3 is refused with `400` before anything else in the request is applied. `POST /api/audio-in/reset` takes `band=N` and resets that band and its curve alone, naming it in the answer; a full reset now includes `attack`, and still leaves the microphone switch and auto range alone. `hzRange` is printed to two decimals (`31.25`, the bound a band is really held to). The poll reply is built in one allocation where it grew through about forty, ten times a second. Nothing is removed or renamed: the phone app reads the same configuration it did, and `d=` stays in the WebSocket protocol for earlier extensions. Static RAM is what it was (161,216 bytes of DRAM, the pinned figure). `docs/rest-api.md` describes the microphone endpoints field by field, including what had been there unwritten (both spellings of a band field, `smoothing`, `attack`, `meta`, `lut`, `env`, the phone's `frame` post, every `source` string, and that a body sent without the form `Content-Type` is not read, which turns a `band=2` reset into a reset of everything). `docs/audio-ws-spec.md` 1.1 states the 500 ms lane release that version 1 said did not exist, and which senders hold a value through it: the extension does, the Android capture app does not yet. Checked on a host by running the old and the new handlers on the same requests (78 assertions), by building every composition, and by the one-field post above on a panel. + +### Web + +- **The guide has a fourth part: Audio** (`/guide/audio`, Korean at `/guide/audio/ko`). Getting the Audio edition onto the board first, then three sections in no order: sound from a browser tab, the on-board microphone, and MIDI with a DAW (network MIDI, Ableton Live as the worked example). The browser section runs the extension's own mapping editor on its demo source, and the boxes drawn there turn the knobs of the 3D board; a hand on a knob takes it for five seconds, as on the real one. The rest is captures of the real pages and the two photos of the microphone. OSC, the phone app and the Director's `.mid` export are links at the end. The hub (`/guide`) has four cards. + +### Docs + +- **The microphone is five leads on four places, and the docs say so.** `AUDIO_GUIDE.md`, the Audio card on `/editions` and `firmware/bundles/README.md` said four wires; GND and SEL both go to GND, which makes five. `AUDIO_GUIDE.md` also names the Audio page's editor by its heading, **Mapping**. +- **The MIDI console page points at Live's current settings page** (Settings → Link, Tempo & MIDI, not Preferences → MIDI) and no longer mentions a MIDI row on the board's NETWORK screen: that screen shows the first two switchable features, and on the Audio edition those are OSC and AUD. `docs/midi-spec.md` says the same. + +### Tools + +- **The audio extension holds the knobs through silence, and Live means the panel is being driven** (`tools/patternflow-audio-extension`, 0.2.0). The panel hands a knob back to its encoder 500 ms after the last message that set it, and the extension did not send a frame equal to the last one. So half a second into a silent tab, a paused track or a Gate curve resting on one step, the knobs were let go while the popup said Live: measured, not one message in ten seconds of silence. It now sends an unchanged frame again about every 210 ms (five a second, no gap longer than 223 ms), and the knobs rest where each band rests until Stop. A knob turned by hand during a pause goes back there when its five-second hold ends, and a microphone that is also switched on does not get the knobs during a pause. Owner's decision. Muting a knob hands it back at once (`off=N`), where it waited for the panel's timeout. *Live* used to mean the socket was open, which it also is on a panel whose Audio-React switch is off and which ignores every frame; the extension now asks the panel once when the socket opens (`GET /api/audio`), and off reads *Aud off* with a **Turn on** button. A socket that will not open is explained, where every failure read "WebSocket error": no panel at that address, a panel that does not run the Audio edition, or something that answers and refuses the socket. A silent tab reads "No sound from this tab yet." The popup has one button, Start until a tab is captured and Stop while one is; *Release knobs* is gone (Stop does that), and *Test without audio* holds each knob where its slider is put. The address is taken on Enter, on leaving the field and on Start, where it reconnected on every keystroke (typing `192.168.0.42` tried `192.168.0.4` on the way), and it has a storage key of its own, so an editor tab left open no longer writes an old address back. Closing the captured tab ends the capture and says so, and a restarted service worker no longer shows Idle over a running capture. The frequency labels under the popup's spectrum sit where their frequencies are ("1k" had been over 317 Hz). In the mapping editor the bands are K1 to K4 everywhere, the knob tabs sit directly under the plot, the level axis and the curve's readout are in dB, and the hints are plain sentences ("This band still runs its old boost curve", on a band nobody had touched, is gone). A new install starts with Auto range on and the Smooth curve, as the panel does and as `AUDIO_GUIDE.md` already said; a mapping that is already stored keeps what it has. Switching Auto range off goes back to the windows set by hand, where it replaced all four with wherever the boxes were; dragging a top or bottom edge is what takes them over. Preview puts its scope where the plot is, so the sliders, the curve and the output range it demonstrates stay in reach, where it was a dialog over them. The editor holds together at half a screen's width, and on a touch screen a swipe that starts on an empty part of the plot scrolls the page while one on a box drags it. One model (`mapping.js`) holds the defaults and the level-to-knob arithmetic for the capture, the popup, the editor and the panel's page, where the first three each had a copy. The extension keeps its cream skin. After pulling, press Reload on `chrome://extensions`; the card should read 0.2.0. Reload keeps the stored mapping, so a copy you already had opens as it was, which unless you had changed them is Auto range off and no curve: for the new defaults, press Reset on each of the four knobs in the editor and switch Auto range on, or remove the extension and load it again (which also forgets the panel's address). Checked with the unpacked extension in headless Chromium against a loopback socket and a stand-in for the panel's HTTP side (44 popup checks, and the wire in each state), and against a panel: a tone in the captured tab swung K1 between 0.30 and 0.85, four seconds of silence left the lanes held at their rest value where they used to be released after half a second, switching Audio-React off on the panel turned the popup to *Aud off*, and Stop handed the knobs back to the microphone. Touch was tried only in emulation, and the panel requests only in the Chromium Playwright ships, not in a current Chrome with its local-network permission prompt. + +### Patterns + +- **Installed patterns run faster, and nobody has to edit one.** A pattern on a panel is a small compiled program (a `.pfm`). Two things about how it was compiled were costing frames, and both change where a module is built: + - It was built for size (`-Os`), on a note in the SDK that `-O2` bought about 2 %. Modules are now built at `-O2` (`build_module.py`). + - Every `floorf`, `ceilf`, `truncf`, `roundf`, `fminf`, `fmaxf` and `fmodf` in a pixel loop was a call out of the pattern into the firmware's maths library: 185 to 840 ns each as timed (`truncf` was not), 8,192 pixels a frame. The module SDK now gives those seven inline code (`abi/pf_libm.h`), so a header that calls them per pixel - most community patterns do - stops paying for the call. + + Frame time on one board, in one boot. The Basics presets as they are today, GCC 8.4: + + | | 0510 | 0512 | 0515-4 | 0520 | 0531 | 0601 | + |---|---|---|---|---|---|---| + | `-Os` | 11.9 ms | 23.2 | 15.0 | 23.4 | 13.7 | 15.0 | + | `-O2` | 10.3 ms | 22.6 | 11.2 | 19.7 | 12.4 | 13.6 | + + That is 3 to 34 % more frames a second; GCC 14.2 at `-O2` was another 1 to 8 % faster. Four presets as they stood before they were tuned by hand, with `floorf` and `fmodf` per pixel: + + | | 0510 | 0515-4 | 0520 | 0601 | + |---|---|---|---|---| + | built as before | 14.8 ms | 25.3 | 26.9 | 26.0 | + | `-O2` | 13.3 ms | 23.7 | 23.1 | 24.5 | + | `-O2`, maths inline | 10.3 ms | 17.5 | 20.3 | 20.2 | + + The last row was measured with a first version of the header. With the header as committed, all 147 distinct public community patterns build (the old recipe's compiler crashed on two of them), and each was loaded and run on a board: no reset, no refused load. +- **What is exact, and what is not.** The seven functions are exact: for every input, each returns the bits the library function returns. Each computes inline only the inputs that have one right answer, and hands the rest - a NaN, an infinity, a magnitude past the range its proof covers - to the call the pattern made before. `check_libm.py` holds them to that in CI: all 4,294,967,296 floats through `floorf`, `ceilf`, `truncf` and `roundf`, 419 million pairs through `fminf` and `fmaxf`, 481 million through `fmodf` by each of its two paths - twenty billion comparisons, no mismatch, under GCC with sanitizers and under MSVC. `check_module_libm.py` then reads a module the real compiler built, to see that this code is what a pattern's calls became, and `tests/modules/_math_probe` is the module that checks on a board what no PC can: that the panel's multiply-subtract is fused. It reported `math ok 9/9` on a board, over 1.17 million comparisons against the firmware's own library. One footnote: for `fminf`/`fmaxf` of a zero against a zero, or of a NaN, "the call the pattern made before" means what that call got from this compiler, which is not always newlib's answer for the arguments as written - GCC answers a call with constant arguments itself, and GCC 12 and later may swap the fallback's operands. + + What is not exact is the pattern's own arithmetic. `-O2` and inline code both move where GCC fuses `a*b+c` into one instruction (`-ffp-contract=fast`), so a value in a rebuilt pattern can differ in its last bit from the build before. Two cases, worked through: + - The cell hash written as one line, `sinf(floorf(x)*12.9898f + floorf(y)*78.233f)*43758.5453f`: 20 of 128 cells hash differently. + - A helper that ends in a multiply and that `-O2` inlines - a smoothstep: `mul.s` then `sub.s` becomes one `msub.s`. + + It shows only where a pattern amplifies the last bit: a float hash, state carried between frames, rarely the edge of a `floorf` band. Of 168 public community headers, one uses the 43758 float hash; 21 use the integer `PFNoise::cellHash`, which is unaffected. +- **The Basics pack is rebuilt**, for the first time since 15 August: the same 33 patterns in the same order, from today's preset sources, at `-O2`, with the firmware's own compiler. Against the August binaries on the same board, every one of the 33 is faster: 1.11 to 2.36 times the frames a second, 1.52 at the median (47 fps to 77 across the pack). + + | | 0510 | 0512 | 0515-4 | 0520 | 0531 | 0601 | + |---|---|---|---|---|---|---| + | August pack | 18.1 ms | 25.1 | 26.1 | 34.4 | 15.5 | 28.5 | + | rebuilt | 10.3 ms | 22.6 | 11.1 | 19.7 | 11.1 | 12.7 | + + It is also the first rebuild to carry the two SDK fixes of 3.10.1, and those change what is drawn. `cellHash`'s seed decorrelates, so Firefly Hollow and Midsummer Sea get a different random stream. `hsvToRgb` rounds, so the 16 patterns in the pack that colour through it are up to one level brighter on a channel. The pack is 166 KB where it was 122, and holds 104 KB of code where it held 63. +- **What the new builds need from the firmware.** + - **3.5.1 or later, for Basics.** Its modules are now stamped for the parameter bus (descriptor 2, where August's were 1), so a show can drive them - and firmware 3.2.0 to 3.4.0 refuses all 33. Update the firmware *before* reinstalling the pack: reinstalling replaces files that load with files that firmware will not. + - **3.10.5 or later, for the larger ones.** Firmware before 3.10.5 places a module's code in internal RAM, and the new builds are larger: 1.36 times the code at the median, over 145 community patterns built both ways with GCC 8.4. Thirteen of those 145 fitted an Audio build's budget of about 7.8 KB before and no longer do; nor does Breakout Arcade in Basics, at 14.2 KB where it was 5.4. From 3.10.5 code runs from PSRAM, and its size does not decide whether a module loads. + - Nothing else: a module built this way imports only names every loader since 3.2.0 resolves. +- **What you have to do.** A pattern already on a panel keeps the build it was installed with; nothing is rebuilt on the device. Update the firmware if the list above says to. Then install the Basics pack again from the site, and send a community pattern or a deck again: the community server keeps one build of each pattern, keyed by everything that goes into a build, so once it is redeployed each is compiled again the first time someone asks for it. +- **A module is 2.7 % slower than the same pattern compiled in, not 20 %.** The SDK said 20 % (a 2026-07 measurement: Origin, 53 fps against 43) and blamed the relocatable code model. Measured again on one board, A-B-B-A in one boot: Origin compiled in draws a frame in 9.56 ms, Origin as a module in 9.81. + ## [3.10.5] - 2026-10-02 ### Firmware diff --git a/README.md b/README.md index 15f71806..06268f78 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ **Patternflow is an open-source LED synthesizer.** You play it with four knobs. 8,192 pixels across a 128 × 64 matrix respond the instant you turn one; nothing is pre-rendered, every frame is computed live on the device. A pattern is just a small file, so every Patternflow plays every pattern anyone makes. -**Start with [the guide](https://patternflow.work/guide).** It builds one with you, shows you how to play it, and walks you through making patterns, each step on a 3D Patternflow you can turn in the browser: **[Build](https://patternflow.work/guide/build)** · **[Play](https://patternflow.work/guide/play)** · **[Make](https://patternflow.work/guide/make)**. +**Start with [the guide](https://patternflow.work/guide).** It builds one with you, shows you how to play it, walks you through making patterns, and connects sound and MIDI, each step on a 3D Patternflow you can turn in the browser: **[Build](https://patternflow.work/guide/build)** · **[Play](https://patternflow.work/guide/play)** · **[Make](https://patternflow.work/guide/make)** · **[Audio](https://patternflow.work/guide/audio)**. ## Where it began @@ -177,7 +177,7 @@ Patternflow is built around a standalone ESP32-S3 driving a HUB75 RGB LED matrix ## Repository & documentation -> **Moving fast.** [v3.10.5 is out](https://github.com/engmung/Patternflow/releases/tag/v3.10.5): every pattern runs 2.45 ms a frame faster, and a pattern's code no longer takes the memory the network runs on - it executes from PSRAM, so patterns that were refused for size now load and the console has more room while one is running. The panel raises its own Wi-Fi hotspot when it knows no network in reach, with the whole console on it. A panel that crashed says which pattern it was in and where, a pattern that hangs no longer takes the console down with it, and five requests that could reboot the board cannot any more. Audio is re-cut on this core as v0.6.5; Performance stays at v0.4.0 until its maintainer re-cuts it. [v3.10.4](https://github.com/engmung/Patternflow/releases/tag/v3.10.4) made the panel film properly on a phone. It refreshes at exactly 300 Hz — the one rate the shutter speeds a phone actually uses (1/50, 1/60, 1/100, 1/30) all divide — so the rolling bands are gone at those; 1/120 and slow motion still band, and [the firmware README](firmware/README.md#refresh-rate-anti-flicker-for-video) says why and what to set. [v3.10.3](https://github.com/engmung/Patternflow/releases/tag/v3.10.3) made the panel's colours come out right: bright edges had been growing purple and sky-blue fringes and greys read pink; the cause was in the display driver — which bit plane each brightness window actually lights — and part of it was 3.10.1's own dark-end fix. The planes are exactly binary now, and because the fix is in the driver every installed pattern gets it without a rebuild. It also brings a [Knobs page](docs/rest-api.md#get-apiknobs-and-post-apiknobs) on the device (which way each encoder counts, set from the browser) and the [features catalogue](https://patternflow.work/features). Both editions were re-cut on that core. [v3.10.2](https://github.com/engmung/Patternflow/releases/tag/v3.10.2) made a Format that did not take stop reporting success, and a storage that will not mount say why. [v3.10.1](https://github.com/engmung/Patternflow/releases/tag/v3.10.1) fixed an upload that could reboot the board, a failed settings write that could make the next boot forget a good pattern, a mic-less Audio panel holding the knobs, and MIDI notes that would not let go. [v3.10.0](https://github.com/engmung/Patternflow/releases/tag/v3.10.0) made a heavy pattern come on every time. [v3.9.5](https://github.com/engmung/Patternflow/releases/tag/v3.9.5) taught the web console to survive a slow Wi-Fi link and a heavy pattern, and put thumbnails under the knob. [v3.8.0](https://github.com/engmung/Patternflow/releases/tag/v3.8.0) split the firmware into [editions](docs/EDITIONS.md) — core, Audio (on-board microphone, OSC, network MIDI) and Performance (sequences, MQTT, weather) — installable in one click and switchable without losing your patterns. [v3.2.0](https://github.com/engmung/Patternflow/releases/tag/v3.2.0) made patterns install over Wi-Fi as `.pfm` modules, no reflash. On v2.x hardware? Everything you need stays bundled at [v2.1.0](https://github.com/engmung/Patternflow/releases/tag/v2.1.0). Follow the [changelog](CHANGELOG.md) and the [journal](https://patternflow.work/journal) for what's current. +> **Moving fast.** [v3.11.0 is out](https://github.com/engmung/Patternflow/releases/tag/v3.11.0): installed patterns run faster - the Basics pack is rebuilt and every one of its 33 patterns gains, 1.5 times the frames a second at the median, once the firmware is updated and the pack installed again - and a pattern you go back to resumes at once instead of loading again. The console is light and reads as one thing, and the Audio edition's page and browser extension are rebuilt: the page says who has the knobs and saves only what you changed, and the extension holds the knobs through silence. There is an [Audio guide](https://patternflow.work/guide/audio) beside Build, Play and Make. Audio is re-cut on this core as v0.7.0; Performance stays at v0.4.0, on core 3.10.4, until its maintainer re-cuts it. [v3.10.5](https://github.com/engmung/Patternflow/releases/tag/v3.10.5) made every pattern 2.45 ms a frame faster and moved a pattern's code to PSRAM, so patterns that were refused for size load and the console has more room while one is running; the panel raises its own Wi-Fi hotspot when it knows no network in reach, a panel that crashed says which pattern it was in and where, a pattern that hangs no longer takes the console down with it, and five requests that could reboot the board cannot any more. [v3.10.4](https://github.com/engmung/Patternflow/releases/tag/v3.10.4) made the panel film properly on a phone. It refreshes at exactly 300 Hz — the one rate the shutter speeds a phone actually uses (1/50, 1/60, 1/100, 1/30) all divide — so the rolling bands are gone at those; 1/120 and slow motion still band, and [the firmware README](firmware/README.md#refresh-rate-anti-flicker-for-video) says why and what to set. [v3.10.3](https://github.com/engmung/Patternflow/releases/tag/v3.10.3) made the panel's colours come out right: bright edges had been growing purple and sky-blue fringes and greys read pink; the cause was in the display driver — which bit plane each brightness window actually lights — and part of it was 3.10.1's own dark-end fix. The planes are exactly binary now, and because the fix is in the driver every installed pattern gets it without a rebuild. It also brings a [Knobs page](docs/rest-api.md#get-apiknobs-and-post-apiknobs) on the device (which way each encoder counts, set from the browser) and the [features catalogue](https://patternflow.work/features). Both editions were re-cut on that core. [v3.10.2](https://github.com/engmung/Patternflow/releases/tag/v3.10.2) made a Format that did not take stop reporting success, and a storage that will not mount say why. [v3.10.1](https://github.com/engmung/Patternflow/releases/tag/v3.10.1) fixed an upload that could reboot the board, a failed settings write that could make the next boot forget a good pattern, a mic-less Audio panel holding the knobs, and MIDI notes that would not let go. [v3.10.0](https://github.com/engmung/Patternflow/releases/tag/v3.10.0) made a heavy pattern come on every time. [v3.9.5](https://github.com/engmung/Patternflow/releases/tag/v3.9.5) taught the web console to survive a slow Wi-Fi link and a heavy pattern, and put thumbnails under the knob. [v3.8.0](https://github.com/engmung/Patternflow/releases/tag/v3.8.0) split the firmware into [editions](docs/EDITIONS.md) — core, Audio (on-board microphone, OSC, network MIDI) and Performance (sequences, MQTT, weather) — installable in one click and switchable without losing your patterns. [v3.2.0](https://github.com/engmung/Patternflow/releases/tag/v3.2.0) made patterns install over Wi-Fi as `.pfm` modules, no reflash. On v2.x hardware? Everything you need stays bundled at [v2.1.0](https://github.com/engmung/Patternflow/releases/tag/v2.1.0). Follow the [changelog](CHANGELOG.md) and the [journal](https://patternflow.work/journal) for what's current. | Folder | Contents | | :--- | :--- | @@ -189,7 +189,7 @@ Patternflow is built around a standalone ESP32-S3 driving a HUB75 RGB LED matrix | `integrations/` | Host-software bridges: Ableton Live / Max for Live (OSC knob mapping) | | `.github/` | Issue and PR templates, and the CI that runs on every pull request (web, every firmware edition, doc links, console pages) | -**Guide (interactive):** [Start here](https://patternflow.work/guide) · [Build it](https://patternflow.work/guide/build) · [Play it](https://patternflow.work/guide/play) · [Make patterns](https://patternflow.work/guide/make): each step shown on a 3D Patternflow you can turn, in English and Korean +**Guide (interactive):** [Start here](https://patternflow.work/guide) · [Build it](https://patternflow.work/guide/build) · [Play it](https://patternflow.work/guide/play) · [Make patterns](https://patternflow.work/guide/make) · [Sound and MIDI](https://patternflow.work/guide/audio): each step shown on a 3D Patternflow you can turn, in English and Korean **Build:** [Full Build Guide](BUILD_GUIDE.md) · [Assembly Map](docs/assembly/README.md) · [Panel Compatibility](docs/panel-compatibility.md) · [Hardware files](hardware/README.md) **Play:** [Pattern Guide](PATTERN_GUIDE.md) · [Custom Patterns](firmware/CUSTOM_PATTERNS.md) · [Audio Guide](AUDIO_GUIDE.md) · [MIDI in Ableton](docs/midi-ableton.md) **Extend:** [Feature Guide](FEATURE_GUIDE.md) · [Editions](docs/EDITIONS.md) · [Firmware build](firmware/README.md) · [Web architecture](web/ARCHITECTURE.md) · [HTTP API](docs/rest-api.md) · [OSC Spec](docs/osc-spec.md) · [MIDI Spec](docs/midi-spec.md) · [Director → MIDI](docs/director-midi.md) diff --git a/SUPPORT.md b/SUPPORT.md index 889478be..b5f6e112 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -4,7 +4,7 @@ One table for "where do I ask". Issues are for things that change this repositor | I want to… | Go to | | :--- | :--- | -| Learn to build, play or make patterns for one | [The guide](https://patternflow.work/guide) — each step on a 3D Patternflow in the browser. A step that didn't get you through has a **Stuck here?** link, which opens an issue with that step filled in | +| Learn to build, play, make patterns for one or give it sound and MIDI | [The guide](https://patternflow.work/guide) — each step on a 3D Patternflow in the browser. A step that didn't get you through has a **Stuck here?** link, which opens an issue with that step filled in | | Get unstuck building, soldering or flashing | [Discord → hardware-help](https://discord.com/channels/1497757947827327067/1499907910707187833) — photos and quick back-and-forth | | Show a pattern I made, or get feedback on one | Publish it from the [Pattern Lab](https://patternflow.work/pattern-lab) to the [Community](https://community.patternflow.work/community), then [Discord → patterns](https://discord.com/channels/1497757947827327067/1499908302962819236) | | Put my finished build on the [build map](https://patternflow.work/inside) | [Share your build](https://github.com/engmung/Patternflow/issues/new?template=share_build.yml) — or post it in Discord | diff --git a/docs/EDITIONS.md b/docs/EDITIONS.md index a4469d33..fab3a183 100644 --- a/docs/EDITIONS.md +++ b/docs/EDITIONS.md @@ -261,7 +261,14 @@ Four rules that are not negotiable: panel is not being drawn while it runs. - **Buffers of 1 KB or more go through `PFMem`** (PSRAM). Internal heap is the scarcest thing on the board and it is what caps how big a loadable pattern - can be. + can be. PSRAM is plentiful but shared: patterns that have run stay parked in + it (`src/core_module_resident.h`), and they give way to patterns, not to + you. While any is parked, the core keeps `PF_MODULE_RESIDENT_HEADROOM` + (512 KB) free whenever one is parked and after every allocation a pattern + makes, and evicts parked ones to do it, but your own allocation evicts nothing: it gets what is free, + which can then be as little as those 512 KB less whatever other features + and the console took from them. Your `setup()` runs at boot, before anything + is parked, so take big buffers there, not on first use hours later. - **Never edit a core file.** Your entire diff is additions. That is what makes taking a core update a file copy rather than a merge, and it is the whole reason the seam exists. diff --git a/docs/RELEASING.md b/docs/RELEASING.md index 39511e1a..fb438b7e 100644 --- a/docs/RELEASING.md +++ b/docs/RELEASING.md @@ -73,7 +73,7 @@ What the two commands do, step by step - and the way to do it by hand. ``` then point `web/public/flash/manifest.json` at the new folder. An edition that also moved gets its own `shelf.sh vA.B.C` and a card update in `web/src/app/editions/editions-data.ts`. The shelf retires the previous folder of the same name — older images stay on their tags. Try-out images (`clock-v0.1.5`, `midi-v0.1.0`, the ones the features page installs) are not part of a release and stay as they are; a fresh one is its own `shelf.sh vA.B.C` and a row update in `web/src/app/features/features-data.ts`, done when somebody wants a newer one, not because the core moved. -7. Run the web checks from `web/`: `npm run lint && npm run typecheck && npm run check:ci && npm run build`. +7. Regenerate the guide's live console demo, which carries the version, the core image's build id and the manifest version: `python firmware/toolchain/console_demo.py` (neither `release.py` nor CI does this; without it the demo on `/guide/play` keeps presenting the previous release), and confirm `python firmware/toolchain/editor_demo.py --check`. Then run the web checks from `web/`: `npm run lint && npm run typecheck && npm run check:ci && npm run build`. 8. Commit and tag: ```bash @@ -84,6 +84,7 @@ What the two commands do, step by step - and the way to do it by hand. 9. Create the GitHub Release from the tag. Publishing it triggers **Firmware release assets**, which attaches the four flash images from the tag plus a generated `FLASHING.md` with offsets and hashes. 10. Confirm it went green before announcing; re-run it with `workflow_dispatch` if it did not fire. +11. Redeploy the community host (the Pi: pull `main`, build, restart the web and worker units) once `main` has deployed. The Basics pack card and the pattern builds are served from there, so until it is redeployed it hands out the previous pack and the previous build settings. Do it before announcing anything that says "install the pack again". ## Branches diff --git a/docs/REPOSITORY.md b/docs/REPOSITORY.md index 39449c36..a889d122 100644 --- a/docs/REPOSITORY.md +++ b/docs/REPOSITORY.md @@ -8,7 +8,7 @@ A map for people. (AI coding agents get the same map with more rules in [`../AGE | :--- | :--- | :--- | | `firmware/` | The ESP32-S3 firmware. `patternflow/` is the sketch: `patternflow.ino`, `config.h` (pins, brightness, limits, calibration), `net_config.h` (Wi-Fi, OTA, update defaults), `src/` (the engine: HUB75 driver, encoders, canvas, colour, noise, Wi-Fi, web console — the **core**, compiled unchanged into every published firmware), `features/` (everything optional: OSC, audio, MQTT, MIDI, shows, weather, clock — attached through hooks the core exposes), `presets/` (the curated patterns), `abi/` (the frozen contract a `.pfm` module is compiled against), `console/` (the device's web pages as plain HTML). `bundles/` names the editions (which features compile in) and holds `build.sh`. `toolchain/` is the repo-level scripts: build a module, make the Basics pack, and the `check_*.py` that CI runs. `modules/` is the module toolchain's working folder, not a place patterns are contributed to | [`firmware/README.md`](../firmware/README.md) for building; [`EDITIONS.md`](EDITIONS.md) before touching anything | | `hardware/` | `pcb/` (KiCad source, Gerbers per revision, schematic PDF), `case/` (STLs by printer bed size, knob plates, the Blender source, legacy v2 cases, and `remixes/` for community variants), `bom/` (the CSV that is the BOM source of truth) | [`hardware/README.md`](../hardware/README.md) | -| `web/` | The Next.js site at patternflow.work: landing page, the interactive guide (`/guide`, with `/guide/build`, `/guide/play` and `/guide/make`), Live Editor, Pattern Lab, the community (only on the community host), browser flasher, edition shelf, device update handoff, journal, roadmap. The JS presets in `src/lib/presets/` are the source of truth for the firmware's preset headers. The breadboard build guide is a React page here, `src/app/build/breadboard/` | [`web/README.md`](../web/README.md), then [`web/ARCHITECTURE.md`](../web/ARCHITECTURE.md) | +| `web/` | The Next.js site at patternflow.work: landing page, the interactive guide (`/guide`, with `/guide/build`, `/guide/play`, `/guide/make` and `/guide/audio`), Live Editor, Pattern Lab, the community (only on the community host), browser flasher, edition shelf, device update handoff, journal, roadmap. The JS presets in `src/lib/presets/` are the source of truth for the firmware's preset headers. The breadboard build guide is a React page here, `src/app/build/breadboard/` | [`web/README.md`](../web/README.md), then [`web/ARCHITECTURE.md`](../web/ARCHITECTURE.md) | | `docs/` | Contracts other software is built against, how the firmware is put together, walk-throughs, the assembly map, project records and media | [`README.md`](README.md) | | `tools/` | Clients Patternflow ships that run on their own: the audio-react browser extension (also the authoring source of the device's `/audio-in` page), the Android capture app, an RTP-MIDI probe | [`tools/README.md`](../tools/README.md) | | `integrations/` | Bridges that run inside or beside someone else's software (Ableton / Max for Live today) and talk to the panel only through the contracts in `docs/` | [`integrations/README.md`](../integrations/README.md) | @@ -26,7 +26,7 @@ The rule for where a document lives: **a guide somebody follows start to finish | Make a pattern | [`PATTERN_GUIDE.md`](../PATTERN_GUIDE.md) — the Pattern Lab, the community, and the device. No local toolchain involved. The same loop as a tutorial: [patternflow.work/guide/make](https://patternflow.work/guide/make). [`firmware/CUSTOM_PATTERNS.md`](../firmware/CUSTOM_PATTERNS.md) is the older, more hands-on route | | Write a firmware feature or cut an edition | [`FEATURE_GUIDE.md`](../FEATURE_GUIDE.md) → [`EDITIONS.md`](EDITIONS.md) → [`features/README.md`](../firmware/patternflow/features/README.md) → [`bundles/README.md`](../firmware/bundles/README.md) | | Control the panel from other software | [`rest-api.md`](rest-api.md) has the table for choosing between HTTP, OSC, MIDI and MQTT; then the spec for the one you chose | -| Put sound into it | [`AUDIO_GUIDE.md`](../AUDIO_GUIDE.md) | +| Put sound into it | [`AUDIO_GUIDE.md`](../AUDIO_GUIDE.md). The browser tab, the microphone and MIDI with a DAW, step by step: [patternflow.work/guide/audio](https://patternflow.work/guide/audio) | | Work on the site | [`web/README.md`](../web/README.md) → [`web/ARCHITECTURE.md`](../web/ARCHITECTURE.md) | | Contribute anything | [`CONTRIBUTING.md`](../CONTRIBUTING.md) — one table of where each kind of contribution goes | | Cut a release, run the servers | [`RELEASING.md`](RELEASING.md), [`SERVICES.md`](SERVICES.md) | diff --git a/docs/audio-ws-spec.md b/docs/audio-ws-spec.md index f7f332e0..e0cc5b80 100644 --- a/docs/audio-ws-spec.md +++ b/docs/audio-ws-spec.md @@ -60,11 +60,90 @@ was learned: after the first send the buffer is never empty, lanes 1..3 dropped in index order, and knob 4 never moved. One `a=` per frame carries everything, in order, at a quarter of the traffic. -Send at your analysis rate (the extension sends per animation frame). There -is no keep-alive requirement. +Send at your analysis rate. The extension analyses on a 33 ms timer, so thirty +messages a second is its ceiling and not its rate: a frame equal to the last +one is not sent again at once, and one is dropped while another is in flight. +Measured against a loopback socket, it sends 23–26 a second on music and 5 a +second while nothing changes (the resend described next). + +## Holding a value — the 500 ms release + +**A lane is handed back to its encoder 500 ms after the last message that +set it.** That is what frees the knobs when a tab is closed or a phone walks +out of range without saying `off`, and it has been in the firmware since the +first version of this protocol. Version 1 of this document said there was no +keep-alive requirement, and that was wrong: a client that skips a frame +because its values did not change loses its lanes half a second into any +steady passage — silence, a paused track, a gate curve resting at one level — +and the next thing it sends starts the pattern's motion from wherever the +encoder had left the knob. + +So a client that means to keep driving **resends its last message at least +every 250 ms**, changed or not. The same goes for `k=`. To let go, stop +sending, or say `off`. + +### What the senders in this tree do + +**The extension holds.** A frame that has not changed goes out again once +180 ms have passed since the last one, which on its 33 ms timer comes to about +210 ms (measured: five messages a second, no gap longer than 223 ms). While +its tab is silent or paused the knobs therefore rest where each band rests, +and stay there until Stop; the sliders of the popup's *Test without audio* are +held the same way. Two things follow from holding. A knob turned by hand +during a pause goes back to its resting value when the five-second hold ends. +And on a panel whose microphone is also switched on, the microphone does not +get those lanes during the pause: it only drives a lane nobody else has. + +A band muted in the extension's editor is a `-` in its slot, and the lane it +had been driving is handed back at once with `off=N` instead of being left to +the timeout; with all four muted it sends four `off=N` and then nothing. Stop +sends `off` and closes the socket. A socket that reopens is told everything +again in its first frame. Everything the extension sets, it sets with `a=`, +the test sliders included (`a=0.800,-,-,-`, a `-` for each knob nobody has +moved). Nothing in this tree sends `k=` or `d=` any more; both stay in the +table above and the firmware takes both, for earlier builds of the extension +and for clients outside this tree. + +**The Android capture app does not hold yet.** +`tools/patternflow-audio-android` still has the change-only skip the +extension had (`sendLanes()` in `DeviceLink.kt`: `if (body == lastBody) +return`). Its lanes are released 500 ms into a pause or a steady passage, and +the next frame it sends starts from wherever the encoders, or the microphone, +left the knobs. For whoever maintains the app: keep the time of the last send +beside `lastBody` and send the same body again when 250 ms have passed. It +already forgets `lastBody` when the socket opens. This was outside the change +that corrected this document, and it is the thing to fix before filming a +track with a quiet passage in it. + +## The switch on the panel + +The device accepts the connection, and every message, whether or not +Audio-React is switched on (the `AUD` row of the panel's NETWORK screen; +`audioRuntime` in `GET /api/audio` and `/api/status`). Switched off, the +messages are parsed and nothing is driven — an open socket is not evidence +that the panel is listening. A client that wants to know reads +`audioRuntime` over HTTP, on port 80 of the same host whatever port its +socket uses ([`rest-api.md`](rest-api.md#audio-react-audio-edition)). + +The extension asks once each time its socket opens (`GET /api/audio`, four +seconds to answer), and once more each time its popup is opened while the +socket is open. `false` is shown as *Aud off* with a **Turn on** button, which is +`POST /api/audio` with `on=1`; no answer, or a `404` from firmware without the +route, is "not known" and nothing is claimed either way. It does not poll: +the panel serves one HTTP connection at a time. ## Version history +- **1.1** (unreleased) — no change on the wire, and nothing for the firmware + to do: old and new firmware behave the same. Written down: a lane is + released 500 ms after its last message, so a steady value has to be resent + (version 1 said no keep-alive was needed); the extension's timer is 33 ms, + not the animation frame, and thirty a second is its ceiling; the socket + accepts messages while Audio-React is switched off and ignores them. The + extension (0.2.0) now keeps to it: it resends an unchanged frame, hands a + muted band's lane back with `off=N`, sends its test sliders as `a=`, and + asks the panel over HTTP whether it is listening. The Android app does not + resend yet. - **1** — first written contract: `a=` / `k=` / `d=` / `off=N` / `off`, lane semantics, the unknown-prefix rule, the one-in-flight pacing rule. Matches firmware 3.8.0 (Audio edition v0.3.1 onward) and the extension as shipped in diff --git a/docs/midi-spec.md b/docs/midi-spec.md index cc2ee64f..68e0124d 100644 --- a/docs/midi-spec.md +++ b/docs/midi-spec.md @@ -72,7 +72,9 @@ carries "midi": {"runtime": true, "channel": 1, "outDiv": [1,1,4,1], "outMul": [1,2,1,1], "outMode": "abs", "host": "192.168.0.23", "rtpPeers": 1, "rtpPeer": "MacBook", "rx": 812, "tx": 40} ``` -`runtime` is the device's own switch (the `MIDI` row on the NETWORK screen); +`runtime` is the device's own switch (**MIDI on this panel** on the console's +MIDI page; the NETWORK screen shows the first two switchable features, so +on the Audio edition, where `OSC` and `AUD` come first, MIDI has no row there); off means the port stays open and everything is dropped, the same convention as `OSC` and `AUD`. diff --git a/docs/rest-api.md b/docs/rest-api.md index 965176fb..9aafd145 100644 --- a/docs/rest-api.md +++ b/docs/rest-api.md @@ -94,7 +94,7 @@ The numbers that explain a device when something is off. Requires `PF_STATUS_HTT | `busy` | Since 1.5. What is keeping the one connection occupied, as a word to slow polling for: `"update"` while a firmware image is arriving or the reboot after it is due, `"storage"` while the pattern volume is being written (a format, a delete, or an install batch between files), `""` otherwise. The console polls no faster than every 5 s while it is non-empty. | | `frameUs` / `presentUs` | Smoothed rendered-frame time and the part spent pushing pixels. `1e6 / frameUs` estimates rendering fps; it excludes housekeeping and is not an input-latency or worst-loop-gap measurement. | | `runtime` | Since 1.5. Whole-loop `loops`, `lastUs`, `maxUs`, `over50ms`; `housekeepingMaxUs` includes work before the rendered-frame timer, and `syncMaxUs` measures execution of a frame-boundary request, excluding time its caller waits. Timing includes loading-state pacing, but excludes time between exiting one loop and entering the next. `GET /api/status?resetTiming=1` returns the current runtime snapshot and starts a new timing window; a loop spanning the reset is discarded. This resets only `runtime`, not lifetime network/load counters. | -| `moduleMemory` | Since 1.5. `reserve` is the module allocator's internal byte-addressable RAM floor, not a guarantee about allocations by other subsystems. `serviceFree` is current internal byte-addressable free RAM; `execLargest` is the largest contiguous executable block. `runtimeBytes` and lifetime `runtimePeakBytes` track retained module dynamic allocations; `runtimeLimit` defaults to 4 MiB. Sections and temporary ELF storage are admitted separately. `refusals` counts allocation refusals, including recovered attempts; `loaderRetries` counts the asynchronous loader's bounded retries after such failures. `loaderStackBytes` reports the reusable worker's reserved stack, and `loaderStackMin` is its minimum free space after jobs (0 until measured). | +| `moduleMemory` | Since 1.5. `reserve` is the module allocator's internal byte-addressable RAM floor, not a guarantee about allocations by other subsystems. `serviceFree` is current internal byte-addressable free RAM; `execLargest` is the largest contiguous executable block. `runtimeBytes` and lifetime `runtimePeakBytes` track retained module dynamic allocations; `runtimeLimit` defaults to 4 MiB. Sections and temporary ELF storage are admitted separately. `refusals` counts allocation refusals, including recovered attempts; `loaderRetries` counts the asynchronous loader's bounded retries after such failures. `loaderStackBytes` reports the reusable worker's reserved stack, and `loaderStackMin` is its minimum free space after jobs (0 until measured). `resident` (since 1.5) describes the modules kept loaded after another pattern took over, so picking one again resumes it instead of loading it: `count` parked now, `bytes` of PSRAM they hold (sections plus their own allocations), `slots` the most that may be parked (`PF_MODULE_RESIDENT_MAX`, 16; `0` when residency is compiled out), and boot counters `resumes` and `evictions`. A parked module holds no internal RAM - one that does is unloaded instead. Parked modules are evicted least recently used first whenever a fresh load would otherwise start with less than `runtimeLimit` plus 512 KiB of PSRAM free, and whenever a running module's own allocation would leave less than 512 KiB free or finds PSRAM full; they do not make way for allocations by features or the core. All of them go at once before a refused load is retried and when an upload, delete or format begins. A module loaded before any module file was last written - by those, or by a feature installing a pattern - is never resumed: parked, it is evicted at the next chance; running, it is unloaded when left, and at the list rebuild that follows the write. | | `netMaintenance` | Since 1.5. Lifetime `calls` and `maxGapMs` for the Wi-Fi/name maintenance hook on the network worker. It runs approximately every 25 ms when serviced, including page-body sends and parser/loop-request waits. Other blocking work, including filesystem calls, can still produce longer gaps. | | `loopCore` / `httpCore` | Which core renders (1) and which answered this request — `0` on 3.9.1 and later, `1` when the network task could not be created and `loop()` is serving. | | `loopStackMin` | Since 1.5: the same for the loop task, which runs every pattern's `draw()` and the panel blit inside it. | @@ -103,9 +103,9 @@ The numbers that explain a device when something is off. Requires `PF_STATUS_HTT | `loopAgeMs` / `loopStalled` / `loopSyncGaveUp` | Since 1.5. Milliseconds since `loop()` was last at its frame boundary, read without the loop's help — a frame or two on a running panel. `loopStalled` turns `true` at 20 s (`PF_LOOP_STALL_MS`): the render loop has not come round, which in practice is a pattern that never returns from `draw()` — `active` names it. It can also be a healthy loop held up: with the internet down one feature lookup holds the loop 14 s, two features' lookups in the same iteration (Performance: weather and an MQTT broker named by host) 28-30 s, and a TLS handshake longer still, so `true` there is not yet a hang. What tells a hang from a loop that is only held up is the next poll: a hung loop's age has grown by exactly the time between the two, a busy one's is back to a few milliseconds with `loopStalled` `false` (`runtime.maxUs` is the longest it has been held since boot). The panel holds its last frame; this endpoint and `POST /api/wifi/reboot` keep answering, as does `/update` while it is armed without the UPDATE screen (the default), and every route that waits for the loop answers `503` (see *Conventions*). Two limits: the console's routes are registered by the loop on the first network link, so a loop that hangs before the panel has ever joined a network or raised its hotspot has no console to outlive it; and routes that only queue work for the loop - `POST /api/params`, `/api/sleep`, a brightness change - still answer `ok` on such a panel and do nothing until it returns. `loopSyncGaveUp` counts the requests refused that way. Nothing reboots the panel on its own, and a reboot goes back to the remembered pattern unless that pattern hung within 15 s of the boot that restored it — pick another one before it hangs again. | | `colorBits` / `refreshHz` | What the HUB75 driver actually settled on — it trades colour depth against the requested refresh rate, so these are read back rather than configured. | | `loadError` | Why the last module load failed, empty when it did not. Without it a refusal is invisible from the network. | -| `loading` | `true` while a module is being loaded on the network core (since 1.4 a switch no longer holds the frame; a heavy pattern's `setup()` takes seconds). `active` reads `"-"` meanwhile. | +| `loading` | `true` while a module is being loaded on the network core (since 1.4 a switch no longer holds the frame; a heavy pattern's `setup()` takes seconds). `active` reads `"-"` meanwhile. Since 1.5 a module that is still parked (`moduleMemory.resident`) comes back without `loading`, in the frame it is picked - when no load is in flight and no upload, delete or format is holding pattern switches. Picked during either, it is applied once that is over: resumed if it is still parked then, loaded if not (an upload, delete or format evicts every parked module). | | `thumbs` | Since 1.5: boot counters `captures` (cache refreshed), `reads` (disk image adopted), `writes` (save completed). `captureMaxUs` is the maximum loop-side capture duration; `ioMaxUs` is the maximum background file-operation wall time, including scheduling/filesystem contention. Both are µs. The cache and its temporary I/O snapshot use PSRAM only. | -| `load` | The last module load, in µs: `total`, `read` (flash), `relocate`, `setup` (the pattern's own). Since 1.4 also where its sections landed, in bytes: `internal` and `psram`. A big `internal` is the number to look at when the console dies the moment a pattern is chosen — the loader now sends data sections over 16 KB, or any that would leave under 24 KB of internal heap, to PSRAM (`PF_MODULE_DATA_INTERNAL_MAX`, `PF_MODULE_INTERNAL_RESERVE`). Since 1.5 `code` says where the module's code runs from, `"psram"` or `"internal"`: in PSRAM it is counted in `psram` and takes nothing from the internal heap, and `"internal"` on a module means PSRAM could not hold it or `moduleMemory.codePolicy` is `0` - which it becomes, until reboot, if code placed in PSRAM ever fails to read back on this unit. | +| `load` | The last module load, in µs: `total`, `read` (flash), `relocate`, `setup` (the pattern's own). Since 1.4 also where its sections landed, in bytes: `internal` and `psram`. A big `internal` is the number to look at when the console dies the moment a pattern is chosen — the loader now sends data sections over 16 KB, or any that would leave under 24 KB of internal heap, to PSRAM (`PF_MODULE_DATA_INTERNAL_MAX`, `PF_MODULE_INTERNAL_RESERVE`). Since 1.5 `code` says where the module's code runs from, `"psram"` or `"internal"`: in PSRAM it is counted in `psram` and takes nothing from the internal heap, and `"internal"` on a module means PSRAM could not hold it or `moduleMemory.codePolicy` is `0` - which it becomes, until reboot, if code placed in PSRAM ever fails to read back on this unit. Since 1.5 data sections go to PSRAM first as well, whatever their size, so `internal` is `0` on a module that loaded normally. Since 1.5 `load` describes the module on the panel: `resumed` is `true` when it came back from residency rather than from a load - the timings are then its own, from when it first loaded, and nothing was read or set up this time - and `resumeUs` is what coming back took; `false` and `0` after a fresh load. With no module on the panel (a preset, or a load that failed) `internal` and `psram` are `0`, `code` reads `"internal"`, `resumed` is `false` and `resumeUs` `0`; the timings are left as the last module had them - except after a failed load, where `read`, and `relocate` if the load got that far, describe the attempt that failed. | | `variant` | Which firmware this is: `"core"`, or a variant's own name. What the site's variant list matches, and what stops the update banner offering a core build on top of someone's chosen firmware. | | `caps` | What this build can do. **Probe this rather than assuming a feature exists.** The default build reports `["patterns","params","sleep"]` and nothing else; each [edition](EDITIONS.md) adds its own — Audio adds `osc` and `audio`, Performance adds `weather`, `mqtt` and `shows`. `patterns` and `params` are on every build. | | `mqttRole` | `"off"`, `"publisher"` or `"subscriber"`. Decides whether the device obeys knob and pattern topics — see [Knobs](#knobs-and-parameters). | @@ -282,7 +282,7 @@ An explicit pick here supersedes a pending console restore — without that, a p "abi": 2, "knobs": ["Waves", "Speed", "Sun", "Glitter"], "slug": "layer_stack", "absoluteReady": true, "panel_w": 128, "panel_h": 64, "module": "layer_stack.pfm", - "size": 6144, "opt": "-Os" + "size": 6144, "opt": "-O2" } ``` @@ -385,14 +385,91 @@ Scale constants live in the same file and are the contract for anything converti Gated on `"audio-in"` in `caps`; the page is `/audio-in`. Settings persist in NVS. The live shaping UI polls the same endpoints documented here — there is -nothing it can do that a script cannot. +nothing it can do that a script cannot. The phone capture app is a second +client of them: it reads the configuration, and posts what it hears so the +page has something to draw on a panel with no microphone. + +Every level here — `inMin`, `inMax`, `levels`, `env`, `spectrum` — is **linear +amplitude**, not decibels, and not clamped: a loud pure tone reads well above +1. The page draws a dB axis and converts at its own edge. | Endpoint | Meaning | |---|---| -| `GET /api/audio-in` | Full state: `micOn`, `micGain` (input gain, 1..16, applied to mic samples before analysis - raw fields stay unscaled), `autoRange` (each band self-normalizes against its tracked floor/peak envelope; on by default), `source` (`"off"`, `"pdm"`, `"pdm (no mic - data pin idle)"`, `"synth"`), raw `rawPeak`/`rawDc`, per-band config (`hzMin`/`hzMax`/`inMin`/`inMax`/`gain`/`outMin`/`outMax`/`knob`/`muted`), `hzRange`, and a 40-bucket log `spec`. | -| `GET /api/audio-in?levels=1` | The polling shape: `levels`, `levelsN` (the level normalized inside the band's tracked range - what the mapping consumes in auto mode), `outputs`, `spectrum`, `dropped` and nothing configurable — the page owns the config after reading it once. | -| `POST /api/audio-in` | `mic=0/1` switches the whole feature (installs/releases the I2S driver). `micGain=1..16` sets the input gain; `auto=0/1` switches per-band auto-ranging. `band=N` plus any subset of the band fields updates one band; partial updates are the point, so a dragged handle cannot clobber the other three. | -| `POST /api/audio-in/reset` | Back to the measured defaults. | +| `GET /api/audio-in` | The configuration, read once: `source` (below), `micOn`, `micGain` (input gain, 1..16, applied to mic samples before analysis - raw fields stay unscaled), `autoRange` (each band self-normalizes against its tracked floor/peak envelope; on by default), `smoothing` and `attack` (the two glide speeds, 0.05..0.9: falls and rises), `bands` (four, below), `hzRange` (`[31.25,8000.00]`, the bounds a band is held to; two decimals since 1.5). Also carried, for scripts and for diagnosing a microphone: `rawPeak`/`rawDc` (the last window before gain), `windows` (read since boot), `level` and `out` (four raw band levels and what they map to) and `spec`, a 64-bucket log spectrum. | +| `GET /api/audio-in?levels=1` | The polling shape, no configuration in it: `source`, `ext`, `rawPeak`, `rawDc`, `dropped`, `levels` (four damped band levels), `levelsN` (the level normalized inside the band's tracked range - what the mapping consumes in auto mode; 0 while that band's gate is closed), `outputs` (what each band maps to, 0..1), `env` (four `{lo,hi}`, the tracked range — present only in auto mode or with `ext`), and `spectrum` (64 log-spaced buckets across `hzRange`). A poll counts as someone watching for 2.5 s, which the phone app reads to decide how often to post; add `idle=1` to a poll that should not count. | +| `POST /api/audio-in` | Any subset of the settings; a request changes only the fields it carries, and answers `{"ok":true}`. `mic=0/1` switches the whole feature (installs/releases the I2S driver). `micGain=1..16`, `auto=0/1`, `smoothing` and `attack` (0.05..0.9). Band fields in either spelling below. `frame=…` is the phone's monitor frame (76 numbers: 4 levels; 4 × lo,hi; 64 buckets): nothing else in that request is read, nothing is saved, and the answer adds `watch` — `{"ok":true,"watch":true}` while a page is watching, `false` when none is. | +| `POST /api/audio-in/reset` | The firmware's measured defaults. With no argument: all four bands and their curves, `smoothing`, `attack` (since 1.5) and `micGain` — not the microphone switch, not `autoRange`. With `band=N` (0..3, since 1.5): that band and its curve only, and the answer names it, `{"ok":true,"band":N}`. Any other `band` is `400` and resets nothing. Firmware before 1.5 ignores `band` and resets everything; the missing `band` in its answer is how to tell. | + +**Band fields.** Each of `bands[0..3]` has `hzMin`/`hzMax` (what it listens +to), `inMin`/`inMax` (the level window mapped onto the knob in manual mode), +`gain` (the response exponent, 0.2..4, used while the band has no curve +table), `outMin`/`outMax` (where the knob rests and peaks, 0..1 each; +`outMax` below `outMin` is a knob that falls as it gets louder), `knob` +(0..3; two bands may name one knob and the lower band wins it), `muted`, +`meta` (up to 31 bytes the editor stores to describe its curve; echoed +verbatim, never parsed) and `lutSet` (read-only: the band runs a stored curve +table instead of `gain`). + +To write them there are two spellings, and both take any subset: + +- `band=N` plus the bare names (`band=2&inMax=0.5`) — one band per request. + A `band` outside 0..3 is `400 {"ok":false,"error":"band must be 0-3"}` and + nothing in the request is applied. +- the names with the band appended (`inMax2=0.5&hzMin0=62&hzMax0=375`) — any + number of bands per request. Since 1.5 a band is applied when **any** of + its fields is present; before, only a request carrying that band's `hzMin`, + `lut`, `knob`, `muted` or `outMin` was, and anything else was answered + `{"ok":true}` and ignored. + +`lut` (write-only) is the curve table: exactly 33 integers 0..255, commas +only. An empty `lut` clears the table and the band goes back to `gain`; a +value that is not exactly 33 integers is ignored and the answer is still +`{"ok":true}`. The firmware does not tie `meta` to `lut` — a client that +names a curve sends its table in the same request. + +Two pairs are settled together after a request's fields are in, whichever +half arrived: `hzMin`/`hzMax` are held to `hzRange`, put in order and kept at +least one analysis bin (31.25 Hz) apart; `inMin`/`inMax` are held to 0..1 with +`inMax` at least 0.01 above `inMin`. One half sent alone can therefore move +the other — send both halves of a pair when either changes. The bin's width is +kept only where the range has room for it: a low edge within 31.25 Hz of the +top is stored as sent, and from about 7984 Hz up the band covers no analysis +bin, so it reads 0 and its knob rests at `outMin`. + +A value that is not a finite number — `nan`, `inf`, one too large for a +32-bit float — is ignored: the field keeps what it had, and the answer is +still `{"ok":true}`. A `frame` with one in it is dropped whole, like a frame +of the wrong length. Before 1.5 such a value was stored and printed back as +`nan`, which is not JSON: the configuration could not be parsed again until +the field was overwritten or reset. One that is already stored is replaced at +boot — a band holding one goes back to its defaults (its curve is kept), and +so do `micGain`, `smoothing` and `attack`. Text that does not start with a +number is still read as 0. + +**Send the form `Content-Type`.** Like every route on this server, these read +a request body as arguments only when it is +`application/x-www-form-urlencoded` (or multipart). `curl -d` sends that by +itself; a browser's `fetch` with a string body does not (it says +`text/plain`, and the body arrives as one argument named `plain`). The +settings POST then changes nothing and answers `{"ok":true}`. The reset is +the one that bites: `band=2` in a body the server did not read is a reset +with no `band`, which resets all four. Put the header on, or put `band` in +the query string, and check that the answer names the band. + +**`source`** says what the input is, and the strings are the contract: + +| `source` | | +|---|---| +| `off` | The switch is off. The analysis is parked and the poll's numbers are whatever the last window left — not zeroed. | +| `pdm` | The microphone is delivering samples. The only state in which it drives the knobs. | +| `pdm (no mic - data pin idle)` | The driver is running but the data pin sits at a rail: no microphone, or no data wire. Nothing is driven. | +| `synth` | The switch is on and the driver is not running — it failed to install, or has not yet (the first quarter second after `mic=1`). | +| `synth (mic stalled)` | The driver is running and has stopped returning samples. | +| `phone` | `?levels=1` only, with `ext:true`: the switch is off and the phone app posted a frame in the last three seconds. `levels`, `env` and `spectrum` are then the phone's, already normalized 0..1 on its own scale; `levelsN` and `outputs` are still the microphone's and describe nothing on screen. | + +In the two `synth` states the analysis runs on a built-in three-tone test +signal, which is what the poll then reports. Since 1.5 that signal no longer +reaches the knobs. `micDropped` in `/api/status`'s `audioIn` block counts capture hops discarded because analysis fell behind; flat is healthy, climbing means overload. @@ -400,8 +477,11 @@ because analysis fell behind; flat is healthy, climbing means overload. ### Audio-React (Audio edition) Gated on `"audio"` in `caps`. The switch that lets the browser extension and -the phone capture drive the knobs — the same one the panel's NETWORK screen -toggles — from the console's `/audio-in` bar. +the phone capture drive the knobs. It is one switch with three handles: the +`AUD` row of the panel's NETWORK screen, the *Browser extension and phone +app* row under Sources on the console's `/audio-in` page, and these two +routes. The browser extension reads it once each time its socket opens and +offers to turn it on ([`audio-ws-spec.md`](audio-ws-spec.md#the-switch-on-the-panel)). | Endpoint | Meaning | |---|---| @@ -535,7 +615,7 @@ In short: HTTP is the management and state transport, OSC and MIDI are the low-l ## Version history -- **1.5** (unreleased) — the hotspot (`hotspot` in status, `GET`/`POST /api/hotspot`); status gains `network` and `thumbs` diagnostics, `fsError` (why storage is not mounted) and `flashId`; a failed `POST /api/patterns/format` returns the reason as its `error`; `POST /api/wifi/reconnect` reconnects without a reboot; core name registration retries partial failures and preserves feature-owned services; the MIDI edition adds a `midiUsb` block to status ([`midi-spec.md`](midi-spec.md)); `GET`/`POST /api/knobs` and the `/knobs` page (encoder direction and edges per click, per knob, persisted); `PUT /update` takes a raw image, and an upload survives a stall of up to two minutes; `GET /api/wifi` gains `join` (what became of the last network asked for) and `GET /api/hotspot` gains `seen` (the names in range); a network added on the hotspot with no station link is tried at once; console pages are cached by build (`?v=` immutable, bare URLs `no-cache` with an `ETag` and `304`, another build's `v` redirected with `302`), the chrome by its CRC (`?h=`), only for a `Host` that can only be the panel; `GET /favicon.ico` answers `204`; status gains `build`, `viaHotspot` and `busy`; `load` gains `code` (where the module's code runs from); pattern names in `GET /api/patterns` and `/api/patterns/select` are JSON-escaped, so a quote or backslash in a title no longer makes the reply unparseable; a multipart request that stops mid-form is dropped - the connection is closed with no reply - instead of spinning the server into its watchdog, and leaves none of its fields behind for later requests; `moduleMemory` gains `codePolicy`; status gains `loopStackMin`; the hotspot password is JSON-escaped in `/api/hotspot`; a `POST` that is not multipart to `/api/patterns` or `/update` is answered instead of crashing the panel, a multipart boundary longer than 70 characters is refused, and a raw `PUT` sees its own query string; status gains `crash` (the core dump's summary, and the pattern and phase the reset interrupted) and `DELETE /api/crash` clears it; status gains `loopAgeMs`, `loopStalled` and `loopSyncGaveUp`, and a route that needs the render loop answers `503` (`render loop is not answering`) when a pattern never returns from `draw()`, where it used to take the whole server down with it. +- **1.5** (unreleased) — the hotspot (`hotspot` in status, `GET`/`POST /api/hotspot`); status gains `network` and `thumbs` diagnostics, `fsError` (why storage is not mounted) and `flashId`; a failed `POST /api/patterns/format` returns the reason as its `error`; `POST /api/wifi/reconnect` reconnects without a reboot; core name registration retries partial failures and preserves feature-owned services; the MIDI edition adds a `midiUsb` block to status ([`midi-spec.md`](midi-spec.md)); `GET`/`POST /api/knobs` and the `/knobs` page (encoder direction and edges per click, per knob, persisted); `PUT /update` takes a raw image, and an upload survives a stall of up to two minutes; `GET /api/wifi` gains `join` (what became of the last network asked for) and `GET /api/hotspot` gains `seen` (the names in range); a network added on the hotspot with no station link is tried at once; console pages are cached by build (`?v=` immutable, bare URLs `no-cache` with an `ETag` and `304`, another build's `v` redirected with `302`), the chrome by its CRC (`?h=`), only for a `Host` that can only be the panel; `GET /favicon.ico` answers `204`; status gains `build`, `viaHotspot` and `busy`; `load` gains `code` (where the module's code runs from); pattern names in `GET /api/patterns` and `/api/patterns/select` are JSON-escaped, so a quote or backslash in a title no longer makes the reply unparseable; a multipart request that stops mid-form is dropped - the connection is closed with no reply - instead of spinning the server into its watchdog, and leaves none of its fields behind for later requests; `moduleMemory` gains `codePolicy`; status gains `loopStackMin`; the hotspot password is JSON-escaped in `/api/hotspot`; a `POST` that is not multipart to `/api/patterns` or `/update` is answered instead of crashing the panel, a multipart boundary longer than 70 characters is refused, and a raw `PUT` sees its own query string; status gains `crash` (the core dump's summary, and the pattern and phase the reset interrupted) and `DELETE /api/crash` clears it; status gains `loopAgeMs`, `loopStalled` and `loopSyncGaveUp`, and a route that needs the render loop answers `503` (`render loop is not answering`) when a pattern never returns from `draw()`, where it used to take the whole server down with it; a module that has run stays loaded when another pattern takes over and resumes when picked again - `load` gains `resumed` and `resumeUs`, `moduleMemory` gains `resident`, and a module's data sections go to PSRAM first; `POST /api/audio-in` applies a band when any of its suffixed fields is present (it took one of five before, and answered `ok` to the rest without changing anything), and refuses a bad `band` before applying anything, with `ok:false` in the body; `POST /api/audio-in/reset` takes `band=N` and names the band in its answer, and a full reset now includes `attack`; `hzRange` is printed to two decimals; a microphone that failed to start or stopped answering (`source` beginning `synth`) no longer drives the knobs from the test signal; `/api/audio-in` ignores a value that is not a finite number (`nan`, `inf`) where it used to store it and print `nan` back, which no JSON parser reads, and a band whose low edge is in the top analysis bin reads 0 instead of putting `nan` into the poll and into `audioIn` in status; the microphone section documents what was already there - `smoothing`, `attack`, `meta`, `lutSet`, `lut`, the suffixed field names, `env`, `ext`, the `frame` post, `synth (mic stalled)`, that `spec` has 64 buckets, not 40, and that a body sent without the form `Content-Type` is not read as arguments. - **1.4** (2026-09-06) — `GET /api/patterns/file` gains `ext=thumb`; `GET /api/display` takes `brightness` and status reports it; status gains `resetReason` and `load.internal`/`load.psram`; console pages are served gzip-compressed (`Content-Encoding: gzip`); the page sender no longer truncates on a slow link; the server no longer trips the Core-0 watchdog on a request that stalls mid-header. - **1.3** (2026-09-04) — `GET`/`POST /api/clock` (Utility edition) and the `clock` block in status; `caps` gains `"clock"`. - **1.2** (2026-09-03) — the server is serviced on Core 0 (the one-connection rule stands; the render-pays rule is history); status gains `httpCore`, `netStackMin`, `loopSyncServed`/`loopSyncMaxUs`; `POST /api/params` documents `d1`..`d4` and how a held value reaches a legacy pattern; `GET /api/patterns/select` gains `step`; `GET`/`POST /api/audio` (Audio-React) are documented; `featureNav`'s microphone label is *Audio*. diff --git a/firmware/CUSTOM_PATTERNS.md b/firmware/CUSTOM_PATTERNS.md index 84a03744..02c0ae85 100644 --- a/firmware/CUSTOM_PATTERNS.md +++ b/firmware/CUSTOM_PATTERNS.md @@ -103,6 +103,12 @@ One class of pattern deserves a note because it used to look impossible: anythin Note that the score measures the *JavaScript* as written — the C++ conversion prompt ships a decision table of firmware fast paths (precomputed per-pixel radius/angle tables for fixed-center patterns, `PFMath::fastAtan2`, cell-hash and pow LUTs), so a HIGH-scoring radial pattern often converts to cheap C++ anyway. Treat the score as pressure, not a wall. +### When `setup()` runs + +Once per load, before the first frame — which is why it is the place for the tables above. It does not run when the pattern is picked again. An installed pattern that has run stays loaded when the panel switches to something else, and coming back to it resumes it where it was: its variables, its buffers and its animation carry on as they were left, the way the compiled-in presets have always behaved. The first `update()` after a return gets an ordinary frame's `dt`, not the time it was away. + +So write `setup()` to prepare, not to reset. A pattern that wants a fresh start every time it is picked cannot have one today: nothing tells it that it was just selected, and `setup()` runs again only when the pattern is loaded again — after a reboot, after patterns were installed or deleted, or when the board needed the memory back. If a reset matters, give it a knob press (`input.btnPressed[i]` in `update()`). And keep anything the pattern needs from one frame to the next in its own variables or buffers: the canvas holds whatever was drawn last, which after a return is another pattern's frame. + --- ## Installing your pattern diff --git a/firmware/README.md b/firmware/README.md index a2bdfa81..3b89cced 100644 --- a/firmware/README.md +++ b/firmware/README.md @@ -135,7 +135,8 @@ firmware/ │ ├── abi/ # Host ⇄ module contract for loadable patterns │ │ ├── pf_abi.h # Frozen C ABI: PFHostAPI, PFPatternModule, PF_ABI_VERSION │ │ ├── pf_params.h # The absolute parameter bus -│ │ └── pf_module.h # Module SDK — the ONE header a .pfm pattern includes +│ │ ├── pf_module.h # Module SDK — the ONE header a .pfm pattern includes +│ │ └── pf_libm.h # ...and what makes floorf/fmodf & co. exact inline code in it │ ├── patternflow_secrets.example.h # Template (copy to patternflow_secrets.h) │ ├── src/ # Foundation — the core, feature-free (see docs/EDITIONS.md) │ │ ├── core_display.h # HUB75 driver init + refresh-rate config @@ -248,8 +249,13 @@ service reserve to module allocations, including PSRAM-to-internal fallbacks. Admission checks the relevant largest block and rechecks service RAM after an allocation. Executable IRAM is not presumed interchangeable with byte RAM. This protects against module allocations consuming the reserve; unrelated -system/feature allocations can still reduce free memory. Large data prefers -PSRAM; small data can remain internal. Module dynamic allocations keep their +system/feature allocations can still reduce free memory. Data goes to PSRAM +first since 2026-10, small sections included (they used to stay internal while +the budget allowed): that is what lets a module be parked holding no internal +RAM. Measured A-B-B-A on two boards, most modules' frame time did not move +(-1.1..+0.8 %); small per-pixel state arrays now read through the PSRAM cache +cost Burgers +2.6 % and Wave Cascade +2.2 %. `PF_MODULE_DATA_PSRAM_FIRST 0` +puts the old placement back. Module dynamic allocations keep their 16-slot, unload-as-a-group lifetime and add a configurable 4 MiB limit (`PF_MODULE_RUNTIME_MAX_BYTES`); `moduleMemory` reports usage and refusals. @@ -299,6 +305,8 @@ The sin LUT is 4 KB (1024 entries, ~0.35° resolution) and shared. Do not build **`floorf`, `fminf` and `fmaxf` are function calls on this target**, not instructions - GCC emits a call into libm at every use, at every optimization level. That is what `ifloor`, `floorF`, `fract`, `clamp` and `clamp01` exist for: they truncate and correct, or compare, which the FPU does in one instruction each. Removing a single `floorf` from a per-pixel path measured *smaller* object code as well as faster, because the call sequence costs more than the inline does. +That is still how it is for code compiled into the firmware image (Origin, the features). **In a module the SDK now does it for a pattern that does not**: since 2026-10, `abi/pf_libm.h` puts inline code behind `floorf`, `ceilf`, `truncf`, `roundf`, `fminf`, `fmaxf` and `fmodf` - and behind `floor(x)`, `std::floor(x)`, `fmod(a, b)` and the like on floats - that returns, bit for bit, what the firmware's libm returns (`toolchain/check_libm.py` runs every float there is through it). A header nobody edits stops paying 185-840 ns for each of them (as timed; `truncf` was not). The PFMath forms are still the least code where they fit, with one exception worth knowing: `jsMod(x, m)` divides, which is a call unless `m` is a power of two, and in a module `fmodf(x, m)` with `m` a constant no longer does. + `approxLength` is an octagonal sqrt approximation — 6.8% error (measured by `toolchain/check_math.py`, which pins it; the 0.375 coefficient is the shift-friendly one, not the minimax one), no `sqrtf` in the pixel loop. Use it only when distance is a **secondary** signal. If distance IS the visual structure of the pattern (radial ripples, concentric rings, vortex centers, anything that uses `1/dist` for amplification), use real `sqrtf` instead — the octagonal contour shows up as visible polygonal artifacts in those cases. ### `core_color.h` — PFColor @@ -395,11 +403,25 @@ libm hooks). A module is compiled freestanding against `abi/pf_module.h` only — never against Arduino or the HUB75 driver — and partially linked with `toolchain/module.ld` so all code lands in one contiguous `.text` with literals inside it. At load time `core_module_loader.h` reads the ELF from -FATFS, allocates executable internal RAM, applies `R_XTENSA_32` relocations, +FATFS, places it (PSRAM first since 2026-10, its code run through the +instruction-bus alias), applies `R_XTENSA_32` relocations, resolves libm/libgcc against the host's own symbols, runs `.init_array` (so C++ global constructors work), syncs the instruction cache, and calls the -module's entry point. Switching away unloads it; one module is resident at a -time. +module's entry point. One module runs at a time. Switching away parks it: a +module that has run stays loaded in PSRAM, and picking it again resumes it in +the frame it is picked — no read, no relocation, no second `setup()` — the +way a compiled-in preset resumes (115–197 µs, measured on two boards). That +is when no other load is in flight and no upload, delete or format is holding +pattern switches; picked during either, it comes in once that is over — +resumed if it is still parked, loaded if an install has emptied the table. +Up to 16 are kept (`PF_MODULE_RESIDENT_MAX` in `src/core_module_memory.h`; +`src/core_module_resident.h` has the rules), only modules that hold no +internal RAM, and they are evicted least recently used first when a new load +or a running pattern needs the PSRAM — until a pattern's allocation leaves +at least 512 KB free, `PF_MODULE_RESIDENT_HEADROOM`; a feature's allocation +evicts nothing — and all at once when patterns are installed or deleted. A +module loaded before its file was replaced — by the console or by a feature — +is never resumed from the old file. **Converting an existing pattern header:** @@ -428,8 +450,9 @@ surface — the math headers are literally the same files, included with |---|---| | Switch latency (7.5 KB module) | 6.0 ms = read 4.4 + relocate 0.6 + setup 1.0 | | Switch latency (22 KB module) | 10.9 ms — still under one 60 fps frame | -| Runtime speed vs the same source compiled in | **~20 % slower** (Origin: 53.4 → 43.5 fps) — the cost of relocatable code (`-mlongcalls`); not fixable by `-O2` (~2 %) or memory placement | -| Comfortable module size | ~32 KB of statics on a core 2.x build — measured: 0–64 KB of static tables all render at the same 62 fps, they just spend heap. (The old "≲ 8 KB, then `.rodata` spills to PSRAM and ~14 fps" figure was the core 3.x build's 7.7 KB largest block talking; see [Required board package](#required-board-package).) | +| Runtime speed vs the same source compiled in | **2.7 % slower** — Origin draws a frame in 9.56 ms compiled in (`-Os`) and 9.81 ms as a module (`-O2`); one board, A-B-B-A in one boot, 2026-10-04. The 2026-07 figure of ~20 % (53.4 → 43.5 fps), which this row blamed on relocatable code (`-mlongcalls`), is not in today's build. | +| Build | `-O2` since 2026-10-04 (`build_module.py`; `--opt s` builds at `-Os` again). On the Basics presets, same board, same boot, `-Os` → `-O2`: 0510 11.9 → 10.3 ms a frame, 0512 23.2 → 22.6, 0515-4 15.0 → 11.2, 0520 23.4 → 19.7, 0531 13.7 → 12.4, 0601 15.0 → 13.6 — 3 to 34 % more frames a second, for a third more code in those builds (67 → 90 KB over the pack). The same change makes `floorf`/`fmodf` and their kin inline and exact in a module (`abi/pf_libm.h`): presets as they were written before PFMath, with those calls per pixel, went 0510 13.3 → 10.3 ms, 0515-4 23.7 → 17.5, 0520 23.1 → 20.3, 0601 24.5 → 20.2 at `-O2` with a first version of that header (a first version of the header; with the header as committed, all 33 Basics modules are faster than their August builds on the same board, 1.11 to 2.36 times the frames, 1.52 at the median). With both, the pack is 104 KB of code, and 145 community patterns built both ways are 1.36 times their old code at the median. Two costs. **Room:** from 3.10.5 that code is in PSRAM and its size does not decide whether a module loads; before 3.10.5 it has to fit in internal RAM (about 7.8 KB of budget on an Audio build), so a larger pattern that loaded as the old build can be refused as the new one - that firmware needs updating. **The last bit:** the seven functions are exact, but `-O2` and inline code move where GCC fuses a pattern's own `a*b+c`, so its arithmetic can differ in the last bit from the build before, which shows only where the pattern amplifies it (a float hash, state carried between frames, rarely a `floorf` band edge; `abi/pf_libm.h` has the cases). A module built this way imports nothing an older loader lacks. | +| Comfortable module size | ~32 KB of statics on a core 2.x build — measured: 0–64 KB of static tables all render at the same 62 fps. They spend PSRAM since 2026-10, not internal heap: statics go to PSRAM first, which costs most modules nothing measurable and a few with small per-pixel state arrays 2–3 % (Burgers +2.6 %, Wave Cascade +2.2 %; `PF_MODULE_DATA_PSRAM_FIRST 0` puts small statics back in internal RAM). (The old "≲ 8 KB, then `.rodata` spills to PSRAM and ~14 fps" figure was the core 3.x build's 7.7 KB largest block talking; see [Required board package](#required-board-package).) | | Panel size | Baked in at build (`-DPF_PANEL_W/H`); the loader rejects a mismatch | | ABI | `PF_ABI_VERSION` must match; bump it on ANY layout change in `pf_abi.h` and rebuild every module | @@ -467,19 +490,22 @@ Installing costs nothing at runtime. The registry allocates its arrays at full capacity on boot, in PSRAM, whether or not the modules exist — so five installed modules and 128 installed modules use exactly the same RAM. Only the **resident** module costs internal RAM, and unloading returns all of it -(measured: 4,548 B free with a module resident → 11,692 B on a preset). +(measured: 4,548 B free with a module resident → 11,692 B on a preset). Since +2026-10 a module's code and data both go to PSRAM first, so a module that +loaded normally costs no internal RAM at all, and a module kept loaded after +being left (parked, see above) only ever holds PSRAM. | Limit | Value | Binding? | |---|---|---| | Installed modules | **128** (`MAX_MODULE_PATTERNS`) | The only real cap, and it is a UX choice — 136 B of PSRAM per slot, 17 KB total | | Storage | ~1,500 modules (10.2 MB partition, 5.9 KB median) | No — 12× the count cap | -| Per-module RAM | ~32 KB of statics comfortable on a core 2.x build (0–64 KB measured at identical fps; the old ≲ 8 KB figure was the core 3.x heap talking) | Heap only — frame rate does not move | -| Concurrent modules | 1 | By design — one pattern is selected at a time | +| Per-module RAM | ~32 KB of statics comfortable on a core 2.x build (0–64 KB measured at identical fps; the old ≲ 8 KB figure was the core 3.x heap talking) | PSRAM since 2026-10. Frame rate mostly does not move; a few modules whose small per-pixel arrays used to sit in internal RAM run 2–3 % slower (Burgers +2.6 %, Wave Cascade +2.2 %) — `PF_MODULE_DATA_PSRAM_FIRST 0` trades that back for internal heap | +| Concurrent modules | 1 running, up to 16 parked (`PF_MODULE_RESIDENT_MAX`) | By design — one pattern is selected at a time; parked ones hold PSRAM only and give it back to a new load or a running pattern that needs it | -A large module only costs while it is the selected pattern; it has no effect -on the presets or on any other module. On the core 3.x build a resident -module dropped `heapLargest` to ~3 KB — which is what made opening a console -page pause the pattern; on the core 2.x release build the same module leaves +A large module only costs internal RAM while it is the selected pattern, if +at all; it has no effect on the presets or on any other module. On the core +3.x build a resident module dropped `heapLargest` to ~3 KB — which is what +made opening a console page pause the pattern; on the core 2.x release build the same module leaves ~65 KB and the console stays comfortable (the pause-on-open mechanism remains, see the constraints section below). @@ -767,7 +793,7 @@ For the original defaults: Exits on a second K2 longpress, a **K2 click**, or after 8 seconds of idle. - **Encoder 3 longpress (≥1s)** — enter/exit the KNOB MAP screen: it shows which physical knob is which number (front view: K1 top-right, K2 top-left, K3 bottom-right, K4 bottom-left), and turning any knob lights its digit green so each one can be verified without leaving the screen. Knob input is swallowed while it's up, so the pattern underneath never sees it. Exits on a K3 click, a second K3 longpress, or after 8 seconds of idle. -- **Encoder 4 longpress (≥1s)** — enter/exit pattern SELECT mode. In SELECT mode, K4 rotation moves the highlight through the list — three detents per pattern, so a hand does not overshoot — and the panel shows each pattern's **thumbnail** (the frame it last drew, kept from the last time it ran); the highlighted pattern loads once the knob has rested for a third of a second — on the other core, so the knob keeps answering while a heavy pattern's setup runs — then runs live behind the overlay. A pattern that has never run shows nothing but its name until it has. Longpress again to confirm — a choice the knob was still resting on loads right then. +- **Encoder 4 longpress (≥1s)** — enter/exit pattern SELECT mode. In SELECT mode, K4 rotation moves the highlight through the list — three detents per pattern, so a hand does not overshoot — and the panel shows each pattern's **thumbnail** (the frame it last drew, kept from the last time it ran); the highlighted pattern loads once the knob has rested for a third of a second — on the other core, so the knob keeps answering while a heavy pattern's setup runs — then runs live behind the overlay. A pattern already in memory — a preset, or a module still parked from its last run — comes on as the knob reaches it and runs live straight away (unless the pattern being left cannot be kept, when it waits for the knob to rest like any other). A pattern that has never run shows nothing but its name until it has. Longpress again to confirm — a choice the knob was still resting on loads right then. Thumbnail ownership follows the frame boundary too: the loop owns the PSRAM cache, and the existing network task reads/writes one immutable job at a time. diff --git a/firmware/bundles/README.md b/firmware/bundles/README.md index 36493f08..73546715 100644 --- a/firmware/bundles/README.md +++ b/firmware/bundles/README.md @@ -48,7 +48,7 @@ A bundle earns its place when it carries something the default **cannot**: | | why not in the default | |---|---| -| **audio** | an on-board microphone that needs four wires soldered to the DevKit, a Wi-Fi transmit power that is not the conformance-tested one, and MIDI over the network | +| **audio** | an on-board microphone that needs five leads soldered to the DevKit, and MIDI over the network | | **performance** | the show player, MQTT in every role, weather — Simone Majocchi's performance-director firmware, pinned so a show behaves the same at the next gig | | **clock** | the time cut out of the running pattern, in a choice of faces — a panel on a shelf that tells the time, with nothing else attached. **Not on the shelf since 2026-09-15:** the clock is a feature; this bundle is the composition CI keeps compiling | | **midi** | the DevKit's USB port as a MIDI device, beside RTP-MIDI — a wire into a DAW with no driver and no session. It needs the port on the S3's USB-OTG stack, a build flag the other images cannot carry (that flag is what makes the port their serial console), so this one builds in its own PlatformIO env. Data only; the panel is still powered from `J4`. **Not on the shelf:** the composition that proves the OTG build | diff --git a/firmware/bundles/audio/features_local.h b/firmware/bundles/audio/features_local.h index 39bcc843..8f36d9dd 100644 --- a/firmware/bundles/audio/features_local.h +++ b/firmware/bundles/audio/features_local.h @@ -12,13 +12,16 @@ // directions over UDP. Was core until it was pointed out that // "needs no infrastructure" is not a test anyone can apply // evenly — OSC is sound integration and belongs here. -// audio The browser path: /audio in the device console, plus the -// websocket the Chrome extension speaks. Four FFT bands off a -// tab or a mic, driving the four knobs. +// audio The browser path: the websocket on port 81 that the Chrome +// extension and the phone app speak, and the switch that lets +// them drive (AUD on the NETWORK screen, /api/audio). The client +// analyses its own sound into four bands and sends the four knob +// values; this feature serves no page. // audio_in On-board sound. A PDM microphone on the panel itself, so the // thing works with no computer in the room at all. Six of seven // people asked for this and it was the single most-wanted item -// in the survey — more than every other option combined. +// in the survey — more than every other option combined. Its +// page is /audio-in, where both inputs are switched. // // Deliberately absent: the show player, weather, MQTT. This firmware is for // people who point sound at a panel. If you want sequences or a broker, that diff --git a/firmware/bundles/audio/overrides.h b/firmware/bundles/audio/overrides.h index d4eca5c6..f7e4d1bb 100644 --- a/firmware/bundles/audio/overrides.h +++ b/firmware/bundles/audio/overrides.h @@ -12,28 +12,10 @@ // ── Wi-Fi transmit power ──────────────────────────────────────────────── // -// Core ships 13 dBm and that is NOT a conservative default — it is a -// conformance fix. The comment where it is set says why: the ESP32's own -// default of 19.5 dBm, plus the WROOM-1 antenna's gain, can sit at or over -// the EU's 20 dBm EIRP limit. -// -// This firmware is for performing: a venue, a booth, a room full of -// competing access points, twenty metres of it between the panel and -// whatever laptop is running Ableton. 13 dBm is chosen for a desk. -// -// 17 dBm is +4 dB, so roughly 2.5x the uplink power, and it is the step the -// core's own comment names as the first one to try. Note that only the -// device→router direction changes: the RSSI on /status is the downlink and -// will not move, so do not use it to judge whether this helped. -// -// **19.5 dBm is deliberately not used here.** It is the maximum the radio -// offers and it is the value an outside report once proposed; the -// investigation that looked at it declined, on the grounds above. Raising -// this to WIFI_POWER_19_5dBm is one word away and whoever does it owns the -// EIRP question that comes with it — including that a firmware published by -// the same person who made the hardware does not read as third-party to -// anybody checking. -#define PF_WIFI_TX_POWER WIFI_POWER_17dBm +// Nothing is set here: this edition transmits at the core's power +// (net_config.h, 19.5 dBm since 3.10.5). It used to pin 17 dBm, which was a +// raise while the core shipped 13 and became a cut when the core went to full +// power; from v0.7.0 it follows the core. // ── What this firmware calls itself ───────────────────────────────────── // @@ -46,7 +28,7 @@ // v0.4.0 shipped still believing it was v0.3.1 because nothing tied the two // together; shelf.sh now refuses an image that does not contain its version. #define PF_VARIANT "audio" -#define PF_VARIANT_VERSION "v0.6.5" +#define PF_VARIANT_VERSION "v0.7.0" // ── The on-board microphone drives the knobs ──────────────────────────── // diff --git a/firmware/bundles/shelf.sh b/firmware/bundles/shelf.sh index fe03581b..b57d163a 100755 --- a/firmware/bundles/shelf.sh +++ b/firmware/bundles/shelf.sh @@ -45,6 +45,31 @@ OUT="web/public/flash/bin/$NAME-$VERSION" CORE_PKG="$HOME/.platformio/packages/framework-arduinoespressif32" BOOT_APP0="$CORE_PKG/tools/partitions/boot_app0.bin" +# A run that was killed outright (a Ctrl-C is covered by the trap further down) +# leaves the credentials at .shelf-aside. Put them back before anything else: +# otherwise this run would skip its control build, and the file would stay +# under a name that is easy to commit by accident. +if [ -f "$SECRETS.shelf-aside" ]; then + if [ -f "$SECRETS" ]; then + echo "both $SECRETS and $SECRETS.shelf-aside exist. Keep the right one and run again." >&2 + exit 1 + fi + mv "$SECRETS.shelf-aside" "$SECRETS" + echo "restored $SECRETS from an interrupted run" +fi + +# A killed build.sh leaves its composition files in features/, and a +# default build would then compile them in: a "core" image carrying an +# edition's features, with every check green. A shelf image is built from the +# bundle alone, so refuse rather than guess whose files these are. +for leftover in features_local.h addons_local.h overrides.h; do + if [ -f "$SKETCH/features/$leftover" ]; then + echo "$SKETCH/features/$leftover exists (a local composition, or left by an interrupted build)." >&2 + echo "Remove it and run again: a shelf image must be built from the bundle alone." >&2 + exit 1 + fi +done + # A version-stamped path is never overwritten: the CDN and every browser that # has seen it will keep serving the old bytes under the same URL, so a # corrected image published over an existing one reaches nobody. diff --git a/firmware/patternflow/abi/abi.sums b/firmware/patternflow/abi/abi.sums index 60568674..d578cd74 100644 --- a/firmware/patternflow/abi/abi.sums +++ b/firmware/patternflow/abi/abi.sums @@ -11,6 +11,13 @@ # uselessly. firmware/toolchain/check_math.py is the guard for that half: # it pins what those headers compute rather than what they contain, which # is the right test for a file whose comments are allowed to change. +# +# pf_libm.h is pinned here although it lays out nothing: it is compiled +# into every module, behind floorf, fmodf and five more, on the promise +# that it computes what libm computes. The hash makes an edit to it a +# deliberate one; firmware/toolchain/check_libm.py is what holds the +# promise, and has to pass before the hash is refreshed. 2fdca28c596418529252f340133343c708a9a8aa11078435f5c733cbc922ec1b pf_abi.h -b6788eb470841b19738ce3bf2f81281903dd9839b9a87078133b0ec42460c481 pf_module.h +0b225800b1211e52128c4de426be595d883384f4a39263a2562ce85773506d9b pf_libm.h +bfba1dcbd63f0ac8b311a10c5142d775a695ce0bdb12827d3593576902f7d000 pf_module.h 5456ff3504023a7f126f57d0712085e7d6facb85f5f576e2e8fc447dec8b0b4a pf_params.h diff --git a/firmware/patternflow/abi/pf_libm.h b/firmware/patternflow/abi/pf_libm.h new file mode 100644 index 00000000..2bf161e5 --- /dev/null +++ b/firmware/patternflow/abi/pf_libm.h @@ -0,0 +1,387 @@ +// ═══ PINNED WITH THE ABI ═══ Not a layout - but every .pfm built after an +// edit here computes with the edited code, so it changes the way the contract +// does: on purpose, with firmware/toolchain/check_libm.py and +// check_module_libm.py passing, and with abi.sums refreshed in the same commit +// (check_abi_freeze.py --update; it keeps the notes in that file). +// Patternflow module SDK — floorf, ceilf, truncf, roundf, fminf, fmaxf and +// fmodf as inline FPU code that returns what the C library returns, bit for bit. +// +// Why. A module links no libm: those names are imports the loader resolves +// against the firmware's copy, so each use is a windowed call into a generic C +// routine. Per call through a module, measured on a board 2026-10-04: floorf +// 251 ns, ceilf 273, roundf 185, fminf and fmaxf 611 for the pair, fmodf +// 700-840 (truncf was not timed) - and a frame is 8,192 pixels. Four presets +// as they stood before they were rewritten around PFMath (floorf and fmodf per +// pixel, the way most community headers are written), same board, -O2, with a +// first version of this file: 0510 13.3 -> 10.3 ms a frame, 0515-4 23.7 -> +// 17.5, 0520 23.1 -> 20.3, 0601 24.5 -> 20.2. With this file as it stands, +// the 33 Basics modules against their August builds, same board: every one +// faster, 1.11x to 2.36x the frames, 1.52x at the median (that is this file, +// -O2 and a month of preset edits together; the split is in +// toolchain/build_module.py). PFMath::ifloor, floorF, fract and clamp +// (src/core_math.h) are still the least code where a pattern is written with +// them; this is for the .h files nobody is going to edit. +// +// What is exact. These seven functions: for every input each returns the bits +// the library function returns. Nothing here is an approximation. Each handles +// inline only the inputs for which IEEE 754 leaves exactly one answer and the +// code below provably reaches it, and hands every other input - a NaN, an +// infinity, a magnitude past the proved range, a tie whose winner is the +// library's own choice - to the real function. The reasoning is beside each +// function. firmware/toolchain/check_libm.py runs it: all 2^32 float bit +// patterns through the four one-argument functions and about three billion +// comparisons through the others, as bits, against the libm of whatever +// machine runs the check. check_module_libm.py then reads a module the real +// compiler built, to see that this code is what a pattern's calls became. +// +// What a PC cannot check is the panel's own arithmetic: that MSUB.S is fused +// (one rounding - fmod's remainder depends on it) and that its conversions +// and compares do what IEEE says. toolchain/tests/modules/_math_probe is a +// module that checks those on a board, against the firmware's own libm +// (2026-10-04, one board: "math ok 9/9", 1.17 million comparisons). +// +// What is not exact: the pattern's own arithmetic, which this file changes +// without touching. Modules are compiled with GCC's default +// -ffp-contract=fast: a*b+c becomes one fused instruction - one rounding where +// there were two - when the compiler sees the multiply and the add in one +// basic block. The code here has branches, so a product computed before one of +// these calls and added after it no longer shares a block with its add; and +// -O2, the build default that arrived with this file, inlines helpers -Os left +// alone, which fuses products that were not fused before. Either way a value +// in the pattern can come out different in its last bit. Two cases, both +// worked through on the two compilers: +// +// sinf(floorf(x) * 12.9898f + floorf(y) * 78.233f) * 43758.5453f +// The shader cell hash written as one expression. The first product +// used to be the fused one; now the second is. On a 16x8 grid of cells +// the sum differs for 20 of 128, and the hash turns each of those into +// an unrelated value. (The same hash behind a helper, hash(floorf(x), +// floorf(y)), is unchanged; PFNoise::cellHash is integer and cannot be.) +// +// 1.0f - smooth(...) where smooth() ends in a multiply +// At -Os the helper stays a function: mul.s, return, sub.s. At -O2 it +// is inlined and the two become one msub.s. +// +// A last bit shows only where a pattern amplifies it: a float hash like the +// one above, state carried from one frame to the next, and - rarely - a value +// sitting on the edge of a floorf band. Everywhere else it is lost in the +// rounding to eight bits. +// +// Layout. PFLibm:: holds the algorithms as ordinary functions any C++17 +// compiler builds - that is what the host test runs, under GCC and MSVC. The +// second half exists only when a module is being built for the panel: it puts +// those functions behind the C names, so `floorf(x)` in an unedited pattern is +// the inline code. A firmware image never includes this file. +// +// Include it BEFORE (pf_module.h does): the redirection of +// libstdc++'s float overloads happens while that header is read. +// License: MIT +#pragma once + +#include + +// ── What the algorithms are written in ────────────────────────────── +// `real::` is the C library's own function - the answer outside what is +// proved below. +// +// On the panel's compiler these are GCC's builtins, for two reasons. A call to +// __builtin_floorf is the call an unedited `floorf(x)` always was - the same +// import, and folded by the compiler in exactly the cases it folded before - +// and it still means that after the C name has been given a body further +// down. And builtins can sit in a constexpr function, which these have to be: +// libstdc++'s std::floor(float) is constexpr and is about to be routed here. +// +// Anywhere else (the host test: GCC, Clang, MSVC) they are the C names from +// , and check_libm.py turns the compiler's builtin handling of those +// names off, so that the reference really is that machine's libm and not the +// compiler's inline idea of it. +#if defined(__XTENSA__) && defined(__GNUC__) && !defined(__clang__) +#define PF_LIBM_FN constexpr inline __attribute__((always_inline)) +namespace PFLibm { +namespace real { +PF_LIBM_FN float floor(float x) { return __builtin_floorf(x); } +PF_LIBM_FN float ceil(float x) { return __builtin_ceilf(x); } +PF_LIBM_FN float trunc(float x) { return __builtin_truncf(x); } +PF_LIBM_FN float round(float x) { return __builtin_roundf(x); } +PF_LIBM_FN float fmin(float x, float y) { return __builtin_fminf(x, y); } +PF_LIBM_FN float fmax(float x, float y) { return __builtin_fmaxf(x, y); } +PF_LIBM_FN float fmod(float x, float y) { return __builtin_fmodf(x, y); } +} // namespace real +PF_LIBM_FN float magnitude(float x) { return __builtin_fabsf(x); } +// The sign BIT: true for -0.0f, which `x < 0` is not. +PF_LIBM_FN bool negative(float x) { return __builtin_signbit(x) != 0; } +// x - q*y with ONE rounding. On the S3 this is MSUB.S; no call. +PF_LIBM_FN float fnma(float q, float y, float x) { return __builtin_fmaf(-q, y, x); } +} // namespace PFLibm +#else +#include +#define PF_LIBM_FN inline +namespace PFLibm { +namespace real { +inline float floor(float x) { return ::floorf(x); } +inline float ceil(float x) { return ::ceilf(x); } +inline float trunc(float x) { return ::truncf(x); } +inline float round(float x) { return ::roundf(x); } +inline float fmin(float x, float y) { return ::fminf(x, y); } +inline float fmax(float x, float y) { return ::fmaxf(x, y); } +inline float fmod(float x, float y) { return ::fmodf(x, y); } +} // namespace real +inline float magnitude(float x) { return std::fabs(x); } +inline bool negative(float x) { return std::signbit(x); } +inline float fnma(float q, float y, float x) { return std::fma(-q, y, x); } +} // namespace PFLibm +#endif + +namespace PFLibm { + +// 2^23. Below it a float converts to int and back without loss; from it up +// every float is already a whole number, so there is nothing to round - and +// that side, with the infinities and NaN (which fail the `<` too), is left to +// the library. +constexpr float WHOLE_FROM = 8388608.0f; + +// ── floor, ceil, trunc, round ─────────────────────────────────────── +// For |x| < 2^23 only. (int)x truncates toward zero and is exact, as is the +// conversion back; a negative x with a fraction lands one above its floor, and +// the subtraction is between whole numbers under 2^23, so exact too. That is +// floor(x) as a value. As bits it can be wrong in one case, x = -0.0f, where +// truncation gives +0.0f: a whole x is its own floor, so it is returned +// itself. +PF_LIBM_FN float floorSmall(float x) { + const float t = (float)(int)x; + const float f = t > x ? t - 1.0f : t; + return f == x ? x : f; +} + +PF_LIBM_FN float floor(float x) { + return magnitude(x) < WHOLE_FROM ? floorSmall(x) : real::floor(x); +} + +// ceil(x) = -floor(-x), signs of zero included: ceil of -0.3f is -0.0f, and +// floorSmall(0.3f) is +0.0f. Negation is exact. +PF_LIBM_FN float ceil(float x) { + return magnitude(x) < WHOLE_FROM ? -floorSmall(-x) : real::ceil(x); +} + +// A zero result carries x's sign - trunc and round of -0.3f are -0.0f - and +// the conversion from int only ever makes +0.0f. x * 0.0f is that zero: the +// product of a finite x and zero is exact and takes the sign of x. (A +// multiplication rather than a look at the sign bit, because reading a +// float's bits makes GCC 8.4 keep the value in memory for its whole life.) +PF_LIBM_FN float signedZero(float x) { + return x * 0.0f; +} + +PF_LIBM_FN float trunc(float x) { + if (!(magnitude(x) < WHOLE_FROM)) return real::trunc(x); + const float t = (float)(int)x; + return t == 0.0f ? signedZero(x) : t; +} + +// Half away from zero. x - t is the fraction of x, exact because both are +// multiples of x's last bit and the difference is under 1; the comparison +// with 0.5 is then of the true fraction, not of a sum that has already +// rounded (floor(x + 0.5f) gets 0.49999997f wrong, and every odd number from +// 2^23 up, where x + 0.5f is a tie that rounds to even). t +- 1 is between +// whole numbers no larger than 2^23. +PF_LIBM_FN float round(float x) { + if (!(magnitude(x) < WHOLE_FROM)) return real::round(x); + const float t = (float)(int)x; + const float d = x - t; + const float r = d >= 0.5f ? t + 1.0f : (d <= -0.5f ? t - 1.0f : t); + return r == 0.0f ? signedZero(x) : r; +} + +// ── fmin, fmax ────────────────────────────────────────────────────── +// When one argument is strictly less, the answer is that argument's bits. When +// they compare equal they are the same bits unless they are zeros of opposite +// sign - and which zero comes back from fmin(+0, -0) is the library's choice +// (newlib returns its second argument, other libraries the negative one), as +// is everything about a NaN. Those two cases go to the call the pattern always +// made, and get what it always got from this compiler - which is not always +// newlib's answer for the arguments in the order they were written: GCC +// answers the call itself when both arguments are constants, and from GCC 12 +// it may hand the library the two operands the other way round. +PF_LIBM_FN float fmin(float x, float y) { + if (x < y) return x; + if (y < x) return y; + if (x == y && negative(x) == negative(y)) return x; + return real::fmin(x, y); +} + +PF_LIBM_FN float fmax(float x, float y) { + if (x > y) return x; + if (y > x) return y; + if (x == y && negative(x) == negative(y)) return x; + return real::fmax(x, y); +} + +// ── fmod ──────────────────────────────────────────────────────────── +// fmod(x, y) is x - n*y for the whole number n = trunc(x / y) of the TRUE +// quotient, carries x's sign, and is always exactly representable. Write +// ax = |x|, ay = |y|, and R = ax - n*ay, 0 <= R < ay, for the answer's +// magnitude. +// +// |x| < |y| returns x itself (n = 0; this is also x = +-0 and y = +-inf). +// Otherwise the quotient is estimated - `p`, from a division or from a +// multiplication by 1/ay - and only its whole part q is kept. The remainder +// is then taken with the fused multiply-subtract, which rounds the exact +// ax - q*ay once: +// +// q = n the exact value is R, representable, so r = R. +// q = n + 1 the exact value is R - ay, in [-ay, 0): a multiple of ay's last +// bit no larger than ay, so representable, so r is negative. +// q = n - 1 the exact value is R + ay, in [ay, 2*ay). It may round, but +// rounding is monotonic and ay is a float, so r >= ay. +// +// So a single look at r says which way q is off, and after stepping q by one +// the remainder is computed AGAIN from ax - never by adding or subtracting ay +// from r, which in the third case has already lost its low bit. +// +// What that needs is |p - ax/ay| < 1, so that q is within one of n. p is a +// rounded division (relative error 2^-24 when it is correctly rounded, which +// _math_probe checks of the panel's; twice that would do), or ax times 1/ay +// (2^-24 for each of the two roundings - and where 1/ay is subnormal, an +// absolute 2^-150 instead, which ax < 2^128 turns into at most 2^-22). Either +// way |p - ax/ay| is under 2^-23 * (ax/ay) + 2^-21, which for a quotient below +// 2^22 + 1 is under 0.51. p < 2^22 is therefore the fast path's whole +// condition: it puts the true quotient below 2^22 + 1; it fails for a NaN +// anywhere, for x = +-inf, for y = 0 (p is inf or NaN) and when 1/ay +// overflowed; and everything it fails for goes to the library. q is at most +// 2^22, so the conversions are exact and so is q +- 1. +// +// The sign: here ax >= ay > 0, so x is not a zero and `x < 0` is its sign +// bit; an exact multiple leaves r = +0.0f, which negation turns into the +// -0.0f fmod returns for a negative x. +constexpr float FMOD_QUOTIENT_BELOW = 4194304.0f; // 2^22 + +PF_LIBM_FN float fmodFrom(float x, float y, float ax, float ay, float p) { + if (!(p < FMOD_QUOTIENT_BELOW)) return real::fmod(x, y); + float q = (float)(int)p; + float r = fnma(q, ay, ax); + if (r < 0.0f) { + q -= 1.0f; + r = fnma(q, ay, ax); + } else if (r >= ay) { + q += 1.0f; + r = fnma(q, ay, ax); + } + return x < 0.0f ? -r : r; +} + +// Any divisor: the quotient costs a division (__divsf3, 327 ns on the S3). +PF_LIBM_FN float fmod(float x, float y) { + const float ax = magnitude(x); + const float ay = magnitude(y); + if (ax < ay) return x; + return fmodFrom(x, y, ax, ay, ax / ay); +} + +// A divisor known when the pattern is compiled: `reciprocal` is 1.0f / |y| +// rounded to nearest, which the compiler folds, and the division is gone. +PF_LIBM_FN float fmodRecip(float x, float y, float reciprocal) { + const float ax = magnitude(x); + const float ay = magnitude(y); + if (ax < ay) return x; + return fmodFrom(x, y, ax, ay, ax * reciprocal); +} + +// y is 1.0f or -1.0f - the hue wrap. The remainder is the fraction of |x|, +// exact as in round() above, with nothing to correct; |x| >= 1 here, so +// `x < 0` is again the sign. +PF_LIBM_FN float fmodOne(float x, float y) { + const float ax = magnitude(x); + if (ax < 1.0f) return x; + if (!(ax < WHOLE_FROM)) return real::fmod(x, y); + const float r = ax - (float)(int)ax; + return x < 0.0f ? -r : r; +} + +} // namespace PFLibm + +// ═════════════════════════════════════════════════════════════════════ +// Module builds only: the C names become the functions above. +// ═════════════════════════════════════════════════════════════════════ +// __XTENSA__ as well as PF_MODULE_BUILD: a host test defines the latter to get +// at the shared headers, and on a PC these names belong to its own libc. +#if defined(PF_MODULE_BUILD) && defined(__XTENSA__) && defined(__GNUC__) && !defined(__clang__) + +namespace PFLibm { +// The divisor is a constant far more often than not (`fmodf(h, 1.0f)`, +// `fmodf(a, TWO_PI)`), and after inlining the compiler knows which calls +// those are. +PF_LIBM_FN float fmodAny(float x, float y) { + if (__builtin_constant_p(y)) { + if (y == 1.0f || y == -1.0f) return fmodOne(x, y); + return fmodRecip(x, y, 1.0f / magnitude(y)); + } + return fmod(x, y); +} +} // namespace PFLibm + +// 1. The spellings that go through libstdc++. In C++, `floor(x)`, +// `std::floor(x)`, `fmod(a, b)` and the rest on float arguments are +// 's float overloads, each one line: `return __builtin_floorf(x);`. +// A builtin cannot be given a body, so for the length of that header the +// builtin's name is a macro for the function above. PFLibm's own `real::` +// fallbacks were defined before this point and still mean the builtin. +// Checked with libstdc++ 8.4 and 14.2 - the versions between are let in on +// the strength of those two - and check_module_libm.py reads the result out +// of a built module. Outside that range the redirection is off, and those +// spellings simply stay calls. +#if __GNUC__ >= 8 && __GNUC__ <= 14 +#define __builtin_floorf(x) PFLibm::floor(x) +#define __builtin_ceilf(x) PFLibm::ceil(x) +#define __builtin_truncf(x) PFLibm::trunc(x) +#define __builtin_roundf(x) PFLibm::round(x) +#define __builtin_fminf(x, y) PFLibm::fmin(x, y) +#define __builtin_fmaxf(x, y) PFLibm::fmax(x, y) +#define __builtin_fmodf(x, y) PFLibm::fmodAny(x, y) +#include +#undef __builtin_floorf +#undef __builtin_ceilf +#undef __builtin_truncf +#undef __builtin_roundf +#undef __builtin_fminf +#undef __builtin_fmaxf +#undef __builtin_fmodf +#else +#include +#endif + +// 2. The C names themselves. `extern inline` with gnu_inline is a definition +// used for inlining and for nothing else: no copy of it is emitted, and +// anything that is not a call - `float (*f)(float) = floorf;` - still +// refers to the firmware's symbol. always_inline makes every call the +// inline one at any optimisation level (and is what makes GCC use this +// body in place of its own idea of the builtin). +// +// The imports stay within what every loader resolves: each fallback is +// the symbol the call would have imported anyway, and fmodf with a divisor +// that is not a constant adds __divsf3. All eight have been in the host +// symbol table (src/core_module_loader.h) since the first loader, v3.2.0. +extern "C" { +extern inline __attribute__((gnu_inline, always_inline)) float floorf(float x) { + return PFLibm::floor(x); +} +extern inline __attribute__((gnu_inline, always_inline)) float ceilf(float x) { + return PFLibm::ceil(x); +} +extern inline __attribute__((gnu_inline, always_inline)) float truncf(float x) { + return PFLibm::trunc(x); +} +extern inline __attribute__((gnu_inline, always_inline)) float roundf(float x) { + return PFLibm::round(x); +} +extern inline __attribute__((gnu_inline, always_inline)) float fminf(float x, float y) { + return PFLibm::fmin(x, y); +} +extern inline __attribute__((gnu_inline, always_inline)) float fmaxf(float x, float y) { + return PFLibm::fmax(x, y); +} +extern inline __attribute__((gnu_inline, always_inline)) float fmodf(float x, float y) { + return PFLibm::fmodAny(x, y); +} +} // extern "C" + +#endif // module build diff --git a/firmware/patternflow/abi/pf_module.h b/firmware/patternflow/abi/pf_module.h index 2847c1a5..126a8d84 100644 --- a/firmware/patternflow/abi/pf_module.h +++ b/firmware/patternflow/abi/pf_module.h @@ -14,18 +14,44 @@ // present() crosses per frame. Calling setPixel through a function pointer // instead would be far worse. // -// It is still NOT free. Measured on a 128x64 panel, the Origin pattern runs at -// 53 fps compiled in and 43 fps as a module — about 20% slower for identical -// source. That gap is not memory placement (it holds with every section in -// internal RAM), not optimisation level (-O2 buys ~2%), and not the framebuffer -// indirection (caching it in a module global made things worse). It is the cost -// of the relocatable code model itself, chiefly -mlongcalls. Budget for it. +// What being a module costs, measured 2026-10-04 on one board, A-B-B-A in +// one boot: Origin compiled into the firmware (-Os) draws a frame in 9.56 ms, +// Origin built as a module (-O2, this SDK) in 9.81 - 2.7% slower as a module. +// This comment used to say 20% (2026-07: 53 fps against 43) and blame the +// relocatable code model, chiefly -mlongcalls; whatever that was, it is not +// in today's build. Two things the old comment said still hold: where a +// module's sections live is not what a frame's time goes on +// (src/core_module_memory.h has the numbers), and caching the framebuffer +// pointer in a module global makes things worse (setPixel, below). +// +// What does cost a module is what costs a preset: the calls its pixel loop +// makes, and how hard the compiler worked on it. Both are dealt with where a +// module is BUILT, so that no pattern has to be edited for them: +// - toolchain/build_module.py compiles at -O2. This comment said -O2 bought +// ~2%; on the Basics presets it is 3..34% more frames a second than -Os +// (measured 2026-10-04, the numbers are beside DEFAULT_OPT). +// - pf_libm.h, included first below, makes floorf, ceilf, truncf, roundf, +// fminf, fmaxf and fmodf inline code that returns exactly what the +// firmware's libm returns - 185-840 ns a call, for the ones that were +// timed, that a pixel no longer pays. +// Neither leaves a rebuilt pattern's own arithmetic as it was: both move +// where GCC fuses a*b+c into one instruction, so a value in the pattern can +// differ in its last bit from the build before. pf_libm.h says where that +// shows. +// +// The rest of libm - sinf, sqrtf, powf, atan2f - is still a call into the +// host, about a microsecond for sinf. PFMath's tables and fast* functions are +// for those. // // Panel size is baked in at build time (-DPF_PANEL_W / -DPF_PANEL_H) because // patterns use PANEL_RES_W/H as array bounds. The loader rejects a module // whose panel size differs from the running firmware's. #pragma once +// First, and before : it is what makes floorf/fmodf and their kin +// inline in a module, and part of that happens while is read. +#include "pf_libm.h" + #include #include #include diff --git a/firmware/patternflow/console/README.md b/firmware/patternflow/console/README.md index 4b9a2ea3..2b60139d 100644 --- a/firmware/patternflow/console/README.md +++ b/firmware/patternflow/console/README.md @@ -19,8 +19,9 @@ whenever you save a page, so a tab left open reloads itself onto your edit the way a real one does after a firmware update. To see the console the way a phone on the panel's hotspot does, `--slow` (gzip, 0.4 s round trips, 5 KB/s, one request at a time); `/mock?hotspot=1` and `/mock?busy=update` -set what `/api/status` says; `--chrome404` runs every page on its fallback. -The script's docstring has the rest. +set what `/api/status` says; `/mock?audio=…` sets what the panel hears +(below); `--chrome404` runs every page on its fallback. The script's +docstring has the rest. When you are done: @@ -49,11 +50,9 @@ CI checks the two stay in sync. | `clock.html` | `/clock` | `features/clock/core_clock_http.h` | `features/clock/clock_index.h` | The list `console_pages.py` splices is `PAGES` at the top of that script — add a -row there when you add a page. `audio-in.html` is itself generated, from the -browser extension's mapping editor, by `firmware/toolchain/build_audio_in_page.py`; -edit the extension, run that, then `console_pages.py build`. CI runs -`build_audio_in_page.py --check`, so a page that was not rebaked after an -editor change fails there rather than shipping stale. +row there when you add a page. A file here whose name starts with an +underscore is a source, not a page. `audio-in.html` is the one page you do +not edit: it is assembled from other files (below). Pages under `src/` belong to the core and may not name a feature — not in a nav row, not in a sentence (see `docs/EDITIONS.md`). Pages under `features/` @@ -68,6 +67,85 @@ is editable the same way. The chrome draws itself inside a shadow root page, so do not style or query its insides. What a page may use is `window.PF`, below. +## The page that is assembled: `/audio-in` + +`audio-in.html` is generated. Edit what it is made from, and +`console_pages.py build` assembles it before it bakes the headers (the step +is `firmware/toolchain/build_audio_in_page.py`; its docstring has the detail). +`console_serve.py` does not read the generated file: it assembles the page +from these sources on every request, with the same code, so here too it is +save and refresh. A source the build would refuse shows as the build's own +message instead of a page. + +| source | what it is | +| ------ | ---------- | +| `tools/patternflow-audio-extension/editor.html`, `editor.css`, `editor.js`, `mapping.js` | the mapping editor and its model, shared with the browser extension: one copy, so the two cannot drift. From `editor.html` only what is between its two `EDITOR BODY` lines comes here (the extension's own source bar stays behind); from `editor.css` and `mapping.js`, what is above their `EXTENSION ONLY` line | +| `_audio_in_bar.html` | the panel's frame around the editor: the page heading, the Sources list (the microphone with its gain, the browser path's switch) and the footer, with a line saying `` where the editor's body goes | +| `_audio_in.css` | the panel's styles, applied after the editor's: the console's base block (the editor brings layout and no colours), then the editor's parts dressed as the console's | +| `_audio_in_adapter.js` | `window.PFAdapter`, the editor's one way out, spoken over `/api/audio-in`. The contract it keeps is written at the top of `editor.js` | + +Comment those files as much as they need. Whole-line comments, indentation +and blank lines are dropped as the page is assembled, which is a quarter of +its weight on the wire. Nothing parses the JavaScript to do it, so the build +refuses the few things that would make it unsafe and names the line; the one +you might meet is a template literal that runs across lines. Line numbers in +the browser's console are the generated page's. + +It is the heaviest page in the console, so its size is pinned: `BUDGET` in +`build_audio_in_page.py`, in bytes of the stamped page gzipped as the panel +sends it. `console_pages.py build` prints how much is spare. CI fails when the +page is over it, and when `audio-in.html` is not what its sources assemble to +(`build_audio_in_page.py --check`; `console_pages.py check` says so too). + +The page is an editor, so on the mock it has a panel that behaves. Its +endpoints (`/api/audio-in`, `/api/audio-in/reset`, `/api/audio`) are the one +part of `console_serve.py` that is a port of the firmware's handlers and not +a fixture: a request changes only the fields it carries, the two pairs settle +as they do on a panel, a curve table that is not 33 numbers is ignored with +`{"ok":true}`, a POST without the form `Content-Type` has no arguments at +all, and replies have the handler's field order and decimals. The sound is +made up; what is done with it is not. What the panel hears is a state you +pick, and each address lands on `/audio-in`: + +| `/mock?audio=` | the panel | +| -------------- | --------- | +| `music` | microphone on, a room with music in it (the default) | +| `silence` | on, a quiet room: every gate shut, every knob at rest | +| `off` | switched off after music: the poll's numbers are the last window's, not zeros | +| `fresh` | nobody has configured it: off, nothing heard, the factory mapping | +| `nomic` | on, nothing on the data pin: `source` is `pdm (no mic - data pin idle)` | +| `stalled` | on, the microphone stopped answering: `synth (mic stalled)`, and the numbers are the test tones | +| `phone` | off, the phone app posting monitor frames: `phone`, `ext:true`, its own units | +| `ext` | off, the browser extension connected: `audioClients` is 1 | +| `noread` | the configuration read gets no reply; the poll and every POST still answer | + +The switches on the page work from any of them. A build can also have the +microphone without the browser path: `/mock?caps=patterns,params,sleep,audio-in`, +where `/api/audio` is a 404. When a handler in `features/audio_in/` or +`features/audio/` changes, the port in `console_serve.py` changes with it. + +## The look + +The console is light unless someone picks dark. The choice is the toggle in +the header, kept in `localStorage` as `pf-theme`, and the chrome sets +`data-theme` on `` before the first paint. Every core page opens its +` -
- -
-
- - Patternflow Audio - MAPPING -
-
- - idle - patternflow.local:81 - -
-
- -
- - -
- Input gain - - 8.0 -
- - - - Extension ↗ - -
- - -
- - - -
- Attack - - quick -
-
- Damping - - balanced -
- -
- -
- -
B1·K1
-
B2·K2
-
B3·K3
-
B4·K4
-
- -
-
- RESPONSE - -
- -
- CURVE -
- - - - - - -
- - This band still runs its old boost curve — pick a preset to upgrade it. - Drag the two handles for a custom shape. Any curve saves as a 33-point table. -
- -
- -
-
- Band 1 - 62 – 375 Hz -
- -
-
- Output range - 0.30 – 0.85 -
-
-
-
-
-
-
-
- - - - - - -
-
- -
- Mute band - -
- -
-
- -
-
-
- - - - -
- - - - -
- +
+

Audio

+

Sources

+
    +
  • + + + +
    + + +
    - +

    +
  • +
  • + + +The extension brings its own mapping; the phone app uses the one below. Same switch as AUD on the panel’s NETWORK screen. +Get the extension +
  • +
+

Mapping

+

For the microphone and the phone app. Changes save to the panel as you make them.

+
+
+ +Each box follows the loudness of what it hears. Drag a top or bottom edge to take all four over by hand. +
+
+ + + +
+
+ + + +
+
+
+
+ +
K1
+
K2
+
K3
+
K4
+ +
+
+
+ + + + +
+ +
+
+
+Knob 1 + +
+
+Response curve +
+ +
+
+ + + + + + +
+ + +
+
+
+
+
+
+
+Output range + +
+
+
+
+
+
+
+
+ + + + + + +
+
+ +
+
+
+ + +
+
+ +
+ diff --git a/firmware/patternflow/console/home.html b/firmware/patternflow/console/home.html index 461dcf27..5b697440 100644 --- a/firmware/patternflow/console/home.html +++ b/firmware/patternflow/console/home.html @@ -5,320 +5,159 @@ Patternflow - + - - -
-

Console

device
- - +
+
-
-
-
- Now playing +
+
+
+

Now playing

-
- - -
+ +
—
-
- - -
- Panel -
- - +
+ +
+
+ Panel +
+ + +
-
-
-
- Brightness - - — +
+
—
+
-
-
- Live Knobs - -
-
- K1 - -
-
- K2 - -
-
- K3 - -
-
- K4 - -
+
+
Knobs
+
+
+
+
-
-
Patterns
—
-
Storage
—
-
-
Wi-Fi
—
-
Address
—
+
+
Patterns—
+
Storage—
+
Wi-Fi—
+
Address—
- -
Memory
—
-
Uptime
—
-
Firmware
—
- - - Edition -
—
-
-
+ from the one above, so its own line. --> + +
Memory—
+
Uptime—
- - - - update - how to update →
- @@ -330,85 +169,70 @@ function $(i){return document.getElementById(i)} $('host').textContent=location.hostname; +// A range's filled part (--p) follows its value. +function fill(el){el.style.setProperty('--p',(el.value-el.min)/(el.max-el.min)*100+'%')} +document.addEventListener('input',function(e){if(e.target.type==='range')fill(e.target)}); + // ── What this build actually has ─────────────────────────────── -// Feature rows are not written into this page. Whatever features the firmware -// carries report their pages in /api/status featureNav — path, label, one -// line — and the Features group is built from that. A build with none gets -// no group, and a page the core has never heard of still gets a row. This -// used to work the other way round: rows for every feature in the markup, -// deleted when the capability was absent, which meant a core page naming -// features and a new feature's page getting no row at all. -// -// Rebuilt from scratch whenever featureNav changes (not on every status: the -// card polls, and a rebuild under the pointer drops hover and focus), then -// everything renumbers so the list still reads 01, 02, 03. +// Feature rows, and the edition card's chips, come from /api/status +// featureNav (path, label, one line): a build with none gets none, and a +// page the core never heard of still gets a row. Rebuilt only when it +// changes: a rebuild under the pointer drops hover and focus. var navKey; function gate(s){ - var i,nav=(s&&s.featureNav)||[],k=JSON.stringify(nav); + var i,nav=(s&&s.featureNav)||[],k=JSON.stringify(nav),f=$('featRows'),c=$('s-feat'); if(k===navKey)return; navKey=k; - var f=$('featRows'),fk=$('featKick'); - if(f){ - f.innerHTML=''; - for(i=0;i
'; - a.querySelector('.pf-row-t').textContent=e[1]; - a.querySelector('.pf-row-d').textContent=e[2]||''; - f.appendChild(a); - } - f.hidden=fk.hidden=!f.querySelector('.pf-row'); + f.innerHTML=c.innerHTML=''; + for(i=0;i'; + a.firstChild.textContent=e[1]; + a.lastChild.textContent=e[2]||''; + f.appendChild(a); + a=document.createElement('a'); + a.href=e[0];a.className='btn';a.textContent=e[1]; + c.appendChild(a); } - var ghosts=document.querySelectorAll('.rows .pf-ghost'); - for(i=0;i 100) v = 100; - var sl = $('sl-' + i); - if (sl) { - sl.value = v; - lastReported[i] = v; - } - } -} - -function pumpParams() { - if (inFlight) return; // Drop redundant updates while in-flight; keep newest in queue - var keys = Object.keys(queuedParams); - if (!keys.length) return; - - var bodyParts = []; - for (var i = 0; i < keys.length; i++) { - var k = keys[i]; - bodyParts.push(k + '=' + encodeURIComponent(queuedParams[k])); +// ── Knob sliders (K1..K4 · 0..100) ──────────────────────────── +var activeSlider=-1,inFlight=false,queuedParams={}; +function updateSliders(params){ + if(!params||params.length<4)return; + for(var i=0;i<4;i++){ + if(activeSlider===i)continue; + var v=Math.round(params[i]/10); + if(v<0)v=0;if(v>100)v=100; + var sl=$('sl-'+i);sl.value=v;fill(sl); } - queuedParams = {}; - inFlight = true; - - fetch('/api/params', { - method: 'POST', - cache: 'no-store', - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - body: bodyParts.join('&') - }).then(function(r) { - if (!r.ok) throw Error('refused'); - $('ctrlmsg').textContent = ''; - }).catch(function(x) { - // Next to the sliders, not swallowed: a drag that never arrived looks - // exactly like a pattern ignoring its knobs. - PF.say('not sent — ' + (x && x.message === 'refused' ? 'refused' : 'no answer'), 'err', $('ctrlmsg')); - }).then(function() { - inFlight = false; - if (Object.keys(queuedParams).length) { - pumpParams(); // Flush pending latest updates - } - }); } - -function onSliderInput(idx, val) { - var v = parseInt(val, 10); - if (isNaN(v)) return; - lastReported[idx] = v; - - // pX on the 0..1000 bus. The device derives the clicks a legacy pattern - // reads from the change in the held value, so no dX alongside — sending - // both counted every move twice. - queuedParams['p' + (idx + 1)] = v * 10; - - pumpParams(); -} - -function initSliders() { - for (var i = 0; i < 4; i++) { - (function(idx) { - var sl = $('sl-' + idx); - if (!sl) return; - held(sl, function() { activeSlider = idx; }, function() { - if (activeSlider === idx) activeSlider = -1; - pumpParams(); - }); - sl.addEventListener('input', function() { - var v = sl.value; - onSliderInput(idx, v); - }); - })(i); - } +function pumpParams(){ + if(inFlight)return; // one in flight; the newest values wait in the queue + var keys=Object.keys(queuedParams),body=[],i; + if(!keys.length)return; + for(i=0;i 0 ? 'Next…' : 'Prev…'; - PF.get('/api/patterns/select?step=' + dir).then(function(d) { - $('now').textContent = d.name || prev; - $('nowsub').textContent = 'switched'; - $('stepmsg').textContent = ''; - }, function(x) { - $('now').textContent = prev; - if (!x || !x.nav) - PF.say(x && x.status === 404 ? 'nothing to switch to' : 'not switched', 'err', $('stepmsg')); +// ── Pattern stepping (← / →) ───────────────────────────────── +// The reply names the new pattern; the next status confirms it. One step +// in flight (?step is ±1); presses meanwhile add up in pend. +var stepping=0,pend=0; +function stepPattern(dir){ + if(stepping){pend+=dir;return} + var prev=$('now').textContent; + $('now').textContent=dir>0?'Next…':'Prev…'; + stepping=1; + PF.get('/api/patterns/select?step='+dir).then(function(d){ + stepping=0; + $('now').textContent=d.name||prev; + $('nowsub').textContent='switched'; + $('stepmsg').textContent=''; + if(pend){dir=pend>0?1:-1;pend-=dir;stepPattern(dir)} + },function(x){ + stepping=pend=0; + $('now').textContent=prev; + if(!x||!x.nav) + PF.say(x&&x.status===404?'nothing to switch to':'not switched','err',$('stepmsg')); }); } -$('pat-prev').addEventListener('click', function() { stepPattern(-1); }); -$('pat-next').addEventListener('click', function() { stepPattern(1); }); +$('pat-prev').addEventListener('click',function(){stepPattern(-1)}); +$('pat-next').addEventListener('click',function(){stepPattern(1)}); +// The arrow keys too, unless a field or slider has them or a modifier is +// held (Alt+Left is Back). +document.addEventListener('keydown',function(e){ + var d=e.key=='ArrowLeft'?-1:e.key=='ArrowRight'?1:0; + if(!d||e.repeat||e.altKey||e.ctrlKey||e.metaKey||e.shiftKey||/^(INPUT|SELECT|TEXTAREA)$/.test(e.target.tagName))return; + e.preventDefault();stepPattern(d); +}); +[].forEach.call(document.querySelectorAll('input[type=range]'),fill); PF.watchStatus(3000); PF.status(paint); diff --git a/firmware/patternflow/console/knobs.html b/firmware/patternflow/console/knobs.html index 09f78bbe..944d5520 100644 --- a/firmware/patternflow/console/knobs.html +++ b/firmware/patternflow/console/knobs.html @@ -5,97 +5,82 @@ Patternflow - Knobs
-

Knobs

-
-
-

Direction and click size

- - - -
PositionDirectionEdges per click
-
- - -
-
-

Turn a knob clockwise. Its position should go up by one per click. - Goes down: set that knob to Reversed. Moves two per click, or needs two clicks to - move one: change Edges per click until one click is one step (most encoders are 4; - the edge counter under the position shows how many one click really makes). - These are properties of the encoder soldered in, stored on the device — nothing to - rebuild, and a saved change does not move the knob.

-
- +tr.all td{border-color:var(--rule)} +.pf-offline .pos,.pf-offline #st{opacity:.5} + +
+

Knobs

-
+

Turn a knob one click clockwise: its position should go up by one.

+ + + + + +
PositionDirectionEdges per click
All
+
+ + +
+

Goes down: Reversed. Moves two, or needs two clicks to move one: change edges per click +(most encoders are 4; the count under the position is what one click makes). Saved on the panel; saving does not move a knob.

+ +
diff --git a/firmware/patternflow/console/midi.html b/firmware/patternflow/console/midi.html index 9a0cb18d..08f51d1b 100644 --- a/firmware/patternflow/console/midi.html +++ b/firmware/patternflow/console/midi.html @@ -149,14 +149,14 @@

Knobs → MIDI

K3CC 26
-
K4CC 27
-
-

Live positions as the DAW sees them. They start at 64 on boot and stop at the ends; the DAW's takeover mode (Live: Preferences → MIDI → Takeover Mode) smooths the pickup.

+

Live positions as the DAW sees them. They start at 64 on boot and stop at the ends; the DAW's takeover mode (Live: Settings → Link, Tempo & MIDI → Takeover Mode) smooths the pickup.

Switch

-

Same switch as the MIDI row on the panel's NETWORK screen. Off keeps the port open and drops everything.

+

Off keeps the port open and drops everything.

@@ -168,7 +168,7 @@

The map

Note 60–63Button 1–4 (on = press, held while held)Button 1–4 pressed / released Prog ChangeSelect pattern by indexPattern changed -

Ableton: in Preferences → MIDI, switch Track and Remote on for the port's Input and Track on for its Output. Then Ctrl/Cmd-M, click a parameter, turn a knob.

+

Ableton: in Settings → Link, Tempo & MIDI, switch Track and Remote on for the port's Input and Track on for its Output. Then Ctrl/Cmd-M, click a parameter, turn a knob.

@@ -216,7 +216,7 @@

The map

$('st').textContent=!d.runtime?'off':(con?('connected · '+(d.rtpPeer||'')):'listening'); $('st').className='sub '+(con&&d.runtime?'ok':''); var lead=$('lead'); - if(!d.runtime){lead.className='lead off';$('lead-t').textContent='MIDI is switched off';$('lead-p').textContent='Turn it on below, or the MIDI row on the panel.';} + if(!d.runtime){lead.className='lead off';$('lead-t').textContent='MIDI is switched off';$('lead-p').textContent='Turn it on below.';} else if(con){lead.className='lead on';$('lead-t').textContent='Connected to '+(d.rtpPeer||'a computer'); $('lead-p').textContent='The panel is a MIDI port there now.'+(d.host?' It will reconnect on its own.':'');} else{lead.className='lead';$('lead-t').textContent=d.host?('Calling '+d.host+'…'):'Listening'; diff --git a/firmware/patternflow/console/patterns.html b/firmware/patternflow/console/patterns.html index f3f4fc16..73f90221 100644 --- a/firmware/patternflow/console/patterns.html +++ b/firmware/patternflow/console/patterns.html @@ -5,190 +5,145 @@ Patternflow - Patterns
-

Patterns

-
- - - -
-

Storage needs formatting

-

This board's pattern partition isn't readable — either it has never been - formatted, or its filesystem is damaged. Format it once, and patterns install - normally from then on.

-
- - -
+/* Touch: no drag and drop on a phone, so arrows instead, in the room the + module chip leaves (the checkbox says it is a module). Narrow buttons keep + the name wide; their 40px hit areas meet in the 6px gaps. */ +@media(pointer:coarse){.btn.mv{display:inline-flex;width:34px;padding:0;font-size:16px}.chip.mod,.fine{display:none} +.list>li{gap:6px;padding:4px 6px}.list .btn.sm::after{inset:-4px -3px}.list .dan{padding:0 8px}#bulk{row-gap:10px}} +@media(min-width:960px){.wrap{max-width:1080px;padding-top:32px} +.grid{display:grid;grid-template-columns:380px minmax(0,1fr);gap:48px;align-items:start} +.grid section{margin-top:0}.lc{position:sticky;top:8px}.q{max-height:55vh;overflow:auto}} + + +
+

Patterns

—
+ + + -
-
+
+

Upload

-
    +
    -
    -
    - -
    +
    -
    +
    -

    Installed

    -
    - - - - - +
    +

    Installed

    +
    -
    -
      +
        + +

        Tap or click a name to play it. Drag modules to reorder. Presets are built into the firmware and can’t be removed here.

        -
        Presets are built into the firmware and cannot be removed here. - Home
        -
        + +
        \ No newline at end of file + diff --git a/firmware/patternflow/console/status.html b/firmware/patternflow/console/status.html index 0d9e7325..cc126625 100644 --- a/firmware/patternflow/console/status.html +++ b/firmware/patternflow/console/status.html @@ -5,109 +5,105 @@ Patternflow - Status
        -

        Status

        -
        - - -
        - - -

        Device

        -
        Firmware
        -
        -
        Build
        -
        -
        Last restart
        -
        -
        Busy
        -
        -
        Panel size
        -
        +/* Console base: the same block opens every core page. Light in :root (the chrome's + injected light palette holds these same values: keep the two in step), + dark under html[data-theme=dark]. No web fonts: the hotspot has no internet. */ +:root{color-scheme:light;--cream:#F4EFE6;--panel:#FFFCFA;--ink:#1A1814;--muted:#625C51;--faint:#7D7668;--rule:#DDD5C6;--rule-soft:#E8E1D4;--ghost:#E3DCCE;--led:#FF5C2E;--bad:#B83C1C;--ok:#2B7A4B;--warn:#85600E; +--sans:system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;--mono:ui-monospace,'Cascadia Mono','SF Mono',Menlo,Consolas,'Roboto Mono',monospace} +html[data-theme=dark]{color-scheme:dark;--cream:#0C0B09;--panel:#131110;--ink:#EDE7DB;--muted:#A39B8A;--faint:#7A7364;--rule:#2B2721;--rule-soft:#1F1C18;--ghost:#2A2620;--bad:#FF7A55;--ok:#57B87F;--warn:#D9A03F} +*{box-sizing:border-box}[hidden]{display:none!important} +body{margin:0;background:var(--cream);color:var(--ink);font:15px/1.5 var(--sans);-webkit-font-smoothing:antialiased} +a{color:inherit}:focus-visible{outline:2px solid var(--led);outline-offset:2px} +.wrap{max-width:720px;margin:0 auto;padding:20px 16px 48px} +h2{margin:0 0 6px;font-size:13px;font-weight:600;color:var(--muted)} +.card{background:var(--panel);border:1px solid var(--rule);border-radius:10px;padding:16px} +.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:40px;padding:0 16px;border:1px solid var(--rule);border-radius:8px;background:var(--panel);color:var(--ink);font:500 14px var(--sans);text-decoration:none;cursor:pointer;transition:border-color .15s,background .15s,color .15s} +@media(hover:hover){.btn:hover{border-color:var(--ink)}footer a:hover{color:var(--ink)}} +.btn:disabled{opacity:.45;cursor:default;border-color:var(--rule)} +.msg{font:12px var(--mono);color:var(--faint)}.pf-ok{color:var(--ok)}.pf-err{color:var(--bad)} +footer{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-top:40px;border-top:1px solid var(--rule);font:12px var(--mono);color:var(--faint)} +footer a{display:inline-flex;align-items:center;min-height:40px;color:var(--muted);text-decoration:none} +header{display:flex;align-items:baseline;flex-wrap:wrap;gap:4px 12px;margin-bottom:20px} +h1{flex:1;margin:0;font-size:22px;font-weight:600;letter-spacing:-.01em} +.sub{font:12px var(--mono);color:var(--faint)} +section{margin-top:28px} +.note{margin:8px 0 0;font-size:13px;color:var(--muted)} +.btn.dan{color:var(--bad)}@media(hover:hover){.btn.dan:hover{border-color:var(--bad)}} +.acts{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-top:12px} +.kv{margin:0;border-top:1px solid var(--rule)} +.kv>div{display:flex;align-items:baseline;gap:12px;padding:8px 0;border-bottom:1px solid var(--rule-soft);font-size:13px} +.kv dt{flex:1 0 auto;color:var(--muted)}.kv dd{margin:0;min-width:0;font:12.5px var(--mono);text-align:right;overflow-wrap:anywhere} +/* ── Status ── */ +.kv .big{font-size:14px;font-weight:600} +.ok{color:var(--ok)}.warn{color:var(--warn)}.bad{color:var(--bad)} +.bar{display:block;height:3px;margin:5px 0 2px auto;max-width:160px;background:var(--ghost)}.bar i{display:block;height:100%;background:var(--led)} +#cptext{display:block;width:100%;height:180px;margin-top:12px;padding:8px 10px;resize:vertical;color:var(--ink);font:12px/1.45 var(--mono)} +.pf-offline .kv dd{opacity:.5} + +
        +

        Status

        -
        + +
        + + +

        Device

        +
        Firmware
        -
        +
        Build
        -
        +
        Last restart
        -
        +
        Busy
        -
        +
        Panel size
        -
        -

        Render

        -
        Panel
        -
        -
        Frame rate
        -
        -
        Frame time
        -
        -
        Active pattern
        -
        + + +

        Render

        +
        Panel
        -
        +
        Frame rate
        -
        +
        Frame time
        -
        +
        Active pattern
        -

        -

        Patterns

        -
        Total
        -
        -
        Built into firmware
        -
        -
        Loaded from storage
        -
        -
        Last load error
        -
        +

        Patterns

        +
        Total
        -
        +
        Built into firmware
        -
        +
        Loaded from storage
        -
        +
        Last load error
        -

        -

        Memory

        -
        Internal heap free
        -
        -
        Largest free block
        -
        -
        PSRAM free
        -
        +

        Memory

        +
        Internal heap free
        -
        +
        Largest free block
        -
        +
        PSRAM free
        -
        -

        Internal heap is the scarce one - the LED panel's DMA buffers -live there. A pattern module loads only if it leaves 24 KB -of it free; run lower and the network is the first thing to go, while the -panel keeps drawing.

        +

        Internal heap is the scarce one: the panel’s DMA buffers live there. A module loads only if it +leaves 24 KB free; below that the network goes first, while the panel keeps drawing.

        -

        Storage

        -
        Pattern storage
        -
        -
        Storage error
        -
        -
        +

        Storage

        +
        Pattern storage
        -
        +
        Storage error
        -
        +
        - -

        Network

        -
        Network
        -
        -
        Signal
        -
        -
        Address
        -
        -
        Names
        -
        -
        Hotspot
        -
        -
        Hotspot mode
        -
        -
        Hotspot clients
        -
        -
        Reached via
        -
        +

        Network

        +
        Network
        -
        +
        Signal
        -
        +
        Address
        -
        +
        Names
        -
        +
        Hotspot
        -
        +
        Hotspot mode
        -
        +
        Hotspot clients
        -
        +
        Reached via
        -
        - -
        + +
        + -Patternflow - Mic mapping - +Patternflow - Audio + + + +
        +

        Audio

        +

        Sources

        +
          +
        • + + + +
          + + + +
          +

          +
        • +
        • + + +The extension brings its own mapping; the phone app uses the one below. Same switch as AUD on the panel’s NETWORK screen. +Get the extension +
        • +
        +

        Mapping

        +

        For the microphone and the phone app. Changes save to the panel as you make them.

        +
        +
        + +Each box follows the loudness of what it hears. Drag a top or bottom edge to take all four over by hand. +
        +
        + + + +
        +
        + + + +
        +
        +
        +
        + +
        K1
        +
        K2
        +
        K3
        +
        K4
        + +
        +
        +
        + + + + +
        + +
        +
        +
        +Knob 1 + +
        +
        +Response curve +
        + +
        +
        + + + + + + +
        + + +
        +
        +
        +
        +
        +
        +
        +Output range + +
        +
        +
        +
        +
        +
        +
        +
        + + + + + + +
        +
        + +
        +
        +
        + + +
        +
        + +
        + + )HTML"; // GENERATED by firmware/toolchain/console_pages.py build — do not edit. -// gzip -9 of AUDIO_IN_INDEX_HTML (69547 bytes → 19920 bytes), served with Content-Encoding: gzip. +// gzip -9 of AUDIO_IN_INDEX_HTML (57490 bytes → 17217 bytes), served with Content-Encoding: gzip. static const uint8_t AUDIO_IN_INDEX_HTML_GZ[] PROGMEM = { - 0x1f,0x8b,0x08,0x00,0x00,0x00,0x00,0x00,0x02,0xff,0xd5,0x7d,0xdb,0x76,0x1b,0x47, - 0x92,0xe0,0xbb,0xbe,0x22,0x45,0xf7,0x18,0x80,0x05,0x14,0x01,0xf0,0x22,0x09,0x14, - 0xa5,0xa5,0x28,0xca,0xd2,0x5a,0x17,0x1e,0x91,0x6e,0xcd,0xb4,0x96,0xeb,0x29,0x00, - 0x05,0xa0,0xcc,0x42,0x15,0x5c,0x55,0x20,0x09,0xab,0xb9,0xa7,0x9f,0xe6,0x03,0x66, - 0xe7,0x9c,0x3d,0xfb,0x25,0xf3,0x3e,0x9f,0xd2,0x5f,0xb2,0x71,0xc9,0x6b,0x5d,0x40, - 0x48,0xf6,0xcc,0x76,0x77,0xdb,0x26,0xaa,0x2a,0x33,0x32,0x33,0x32,0x32,0x6e,0x19, - 0x19,0xf9,0xe4,0xfe,0x38,0x19,0xe5,0xab,0x45,0x20,0x66,0xf9,0x3c,0x7a,0x7a,0xef, - 0x09,0xfe,0x11,0x91,0x1f,0x4f,0x0f,0xb7,0x82,0x78,0x0b,0x5f,0x04,0xfe,0x18,0xfe, - 0xcc,0x83,0xdc,0x17,0xa3,0x99,0x9f,0x66,0x41,0x7e,0xb8,0xb5,0xcc,0x27,0x9d,0x47, - 0xf8,0x35,0x1b,0xa5,0xe1,0x22,0x17,0x59,0x3a,0x3a,0xdc,0xda,0x5e,0x4c,0x3a,0xa3, - 0x24,0xce,0x92,0x28,0xf0,0x7e,0xce,0x9e,0xcd,0x0e,0xc7,0xdd,0xfe,0x70,0x67,0xa7, - 0xdf,0xdf,0x7a,0xfa,0x64,0x9b,0x0b,0x3e,0x95,0x15,0x9e,0x86,0x93,0xe6,0xfd,0xeb, - 0x30,0x1e,0x27,0xd7,0xde,0xe9,0xcb,0x56,0x73,0xb2,0x8c,0x47,0x79,0x98,0xc4,0xcd, - 0xd6,0xe7,0x2b,0x3f,0x15,0x1f,0x0f,0xf9,0x5b,0xfb,0xc7,0xc3,0xc6,0xb6,0xbf,0x08, - 0xb7,0xb3,0xdc,0xcf,0x97,0x59,0xa3,0xfd,0xe6,0xf0,0xd3,0x45,0xfb,0xed,0x61,0x3f, - 0x78,0xdc,0x3e,0x6b,0xff,0xa9,0x7d,0x7a,0xf8,0x11,0xaa,0x1f,0x7e,0xc6,0xcf,0xc1, - 0xa0,0x11,0x85,0x57,0x41,0xa3,0x3d,0x0e,0xd3,0x7c,0x35,0xb8,0xdf,0x6b,0x5f,0x0d, - 0xe2,0x65,0x14,0xb5,0xa7,0x41,0x3e,0xf8,0xbe,0x1d,0xc6,0x93,0x28,0x9c,0xce,0xf2, - 0xc1,0xbb,0xe5,0x7c,0x18,0xa4,0xed,0x59,0x12,0x8d,0x07,0xef,0x87,0x3f,0x07,0xa3, - 0xbc,0x9d,0xf9,0xab,0x81,0xee,0x41,0xde,0xbe,0x6c,0x07,0xad,0xcf,0xc1,0xb7,0xdf, - 0x36,0x03,0x2f,0x0f,0x6e,0xf2,0xe3,0x24,0xce,0x83,0x38,0x3f,0xcc,0x5b,0xb7,0xed, - 0xe1,0x32,0xb3,0x8a,0x0e,0xdb,0x8b,0xd6,0x67,0xf5,0x24,0x26,0xd0,0xf9,0xa1,0x37, - 0x0e,0x33,0x7f,0x18,0x05,0xe3,0xc3,0xfb,0xbd,0x5b,0xfb,0xa9,0x7b,0xb0,0xf0,0xf2, - 0x59,0x10,0x37,0x27,0xed,0x49,0xeb,0x20,0x0d,0xf2,0x65,0x1a,0x8b,0xc5,0x6d,0x7b, - 0x91,0x44,0x91,0x81,0x98,0xb5,0xe7,0xed,0x51,0x3b,0x61,0x2c,0xe4,0xed,0x49,0x7b, - 0x7a,0xd8,0x3b,0xd0,0x2d,0x7c,0x80,0x16,0xa6,0xdf,0x7e,0x7b,0x7f,0x02,0x7d,0x9b, - 0x1c,0xf6,0xda,0xa3,0x28,0xf0,0xd3,0xf3,0x70,0x1e,0x24,0xcb,0xbc,0x99,0xb7,0xda, - 0xcd,0xcc,0x1b,0xf9,0x51,0xf4,0x2c,0x6b,0xb6,0x06,0xdf,0x37,0xb3,0x56,0x4b,0xb6, - 0xa8,0xa0,0x8f,0x5b,0x9f,0x47,0xcd,0x71,0x1b,0xb1,0x02,0x63,0xd1,0xaf,0x03,0x7c, - 0x4d,0xa8,0x0a,0x5a,0xb7,0xb2,0xce,0x0f,0xed,0x1f,0x5a,0xad,0x5b,0xdd,0xf2,0x0f, - 0xd0,0xf2,0xe4,0xb0,0x7b,0x50,0x68,0xf1,0x00,0x7a,0xd3,0xcc,0x0f,0x81,0x24,0xd4, - 0xbb,0x0f,0xed,0x39,0xd4,0x4b,0xbe,0xfd,0x36,0xf1,0x26,0x61,0x9a,0xe5,0x87,0x87, - 0x80,0x88,0x67,0x50,0x7d,0x00,0x9d,0x97,0xc3,0x86,0xe9,0x4a,0x16,0x03,0x6b,0xca, - 0xa7,0x65,0xc8,0xb7,0x6d,0x00,0x6a,0xca,0xc4,0xad,0xcf,0xf3,0xc3,0xf8,0x60,0xf2, - 0xe7,0x3f,0x03,0xa8,0xdb,0x76,0x9c,0x5c,0x0f,0x3e,0xdc,0x42,0x19,0x22,0x0b,0x53, - 0x6c,0x04,0x23,0xf9,0xf6,0xdb,0x37,0xde,0x62,0x99,0xcd,0xe0,0xe1,0xe0,0xec,0x19, - 0x3c,0x8e,0x9a,0x67,0xad,0xc1,0x9f,0x9e,0xfd,0xc9,0x83,0x5a,0xd0,0x8f,0x3f,0x1d, - 0x9e,0x7a,0x88,0xf4,0xe6,0x8f,0xed,0xb7,0xed,0x17,0x00,0xec,0xda,0xcf,0x47,0xb3, - 0xb3,0x02,0xa4,0x79,0xeb,0xf3,0xa9,0xc7,0xe0,0xa1,0xdf,0x7f,0xf2,0xa0,0x37,0xcd, - 0xb7,0x87,0x6f,0xfd,0x7c,0xe6,0xcd,0x43,0xf8,0xdc,0x7e,0xdb,0xa2,0xaa,0x61,0xfe, - 0x32,0x49,0x5f,0x04,0x57,0xe1,0x28,0x30,0x95,0x61,0xfe,0x92,0xc3,0xe4,0xcf,0x7f, - 0xfe,0x7c,0xab,0x26,0x3a,0x0e,0xae,0xc5,0x69,0x9a,0xcc,0xc3,0x2c,0x30,0xd3,0xb1, - 0x6a,0xc7,0x3c,0xd1,0x33,0xd3,0xa5,0x7e,0xb0,0x63,0x26,0x26,0x6b,0x7d,0xce,0x60, - 0xba,0x9b,0x89,0x37,0x5c,0x86,0xd1,0xf8,0xdb,0x6f,0x33,0xfe,0x71,0x78,0x28,0xdf, - 0xb4,0x00,0xfd,0x33,0x0f,0xd1,0xd9,0x6c,0xb5,0x57,0x38,0xe3,0xb7,0xad,0x03,0x6b, - 0x36,0x2c,0x1c,0xab,0x62,0x07,0x71,0xf3,0x24,0x4d,0x93,0xb4,0xd9,0xc8,0xb9,0x50, - 0x03,0x07,0x92,0x78,0xf2,0xe9,0xcf,0x7f,0x7e,0x1c,0xec,0x02,0x94,0xdb,0x5b,0x43, - 0x76,0xdf,0x37,0x97,0x48,0x92,0x72,0x28,0x93,0x00,0xd0,0x45,0x6f,0x0a,0xd4,0x95, - 0xb6,0x3e,0xe3,0xaa,0x4e,0xbd,0xe4,0xb2,0x95,0xcf,0xd2,0xe4,0x5a,0x70,0x43,0xa9, - 0xc4,0xa3,0xa6,0x7a,0xa2,0x0e,0x5c,0x59,0xcf,0x52,0xfa,0x03,0x93,0x92,0x02,0xc3, - 0xc0,0x6e,0xde,0x5a,0x34,0xf7,0x02,0xc7,0x0f,0x10,0xe1,0xbf,0x67,0xb0,0xce,0x17, - 0x13,0x9e,0xa3,0xc3,0xec,0xe0,0x8d,0x37,0x49,0xd2,0x13,0x1f,0xba,0xa1,0x1b,0x9f, - 0x00,0x7d,0x42,0x49,0xec,0xf8,0x6d,0xab,0xd9,0xd2,0xfc,0x46,0x32,0xae,0xd8,0x9f, - 0x07,0x87,0x5b,0x57,0x61,0x70,0xbd,0x48,0xd2,0x7c,0x4b,0x8c,0xe4,0x9a,0xde,0xba, - 0x0e,0xc7,0x39,0x30,0x2a,0x9a,0xc0,0x0e,0x3d,0x00,0xa7,0x08,0xf3,0xd0,0x8f,0x3a, - 0x19,0xac,0xa5,0xe0,0xb0,0x87,0x5c,0x2e,0x0f,0xf3,0x28,0x78,0x7a,0xea,0xe7,0x79, - 0x90,0x02,0x1f,0x81,0xa1,0x75,0xc4,0xdb,0x70,0x24,0xe6,0xfe,0x62,0x11,0xc6,0xd3, - 0x27,0xdb,0x5c,0xe0,0xde,0x93,0xfb,0x9d,0x8e,0xf8,0xfe,0xe4,0xdd,0xc9,0x87,0xa3, - 0xf3,0x93,0x17,0x62,0xb8,0x12,0xb0,0x08,0xe6,0xd7,0x7e,0x1a,0x6c,0xe7,0x49,0x12, - 0x01,0xfb,0x0c,0xe3,0x6d,0x9a,0xb9,0x9f,0xfc,0xe5,0x38,0x4c,0x7e,0x0a,0xe3,0x9f, - 0x16,0xfe,0x34,0xf0,0x16,0x50,0x12,0xc8,0xe3,0x9e,0xc0,0xff,0x61,0xd1,0x6c,0x7b, - 0x61,0x5a,0xeb,0x50,0xe1,0x0e,0xa0,0x2a,0x88,0x33,0x44,0x4d,0x33,0x18,0x87,0x79, - 0x92,0x8a,0xfe,0xe3,0x6e,0xb0,0xb7,0xd7,0xdf,0xdd,0xdb,0x6f,0x79,0x5c,0xf7,0x04, - 0x3e,0x08,0x98,0x17,0x21,0x4b,0xc0,0xcf,0x34,0x68,0x8b,0x34,0x48,0x97,0x31,0xbd, - 0xa7,0xd6,0x81,0xff,0xe1,0x43,0x2c,0x24,0xbb,0xa6,0x4e,0x64,0xd8,0x0b,0xfa,0xec, - 0x89,0x4e,0x07,0x79,0x7b,0xbe,0xc2,0x41,0x6d,0x7f,0x27,0xec,0x91,0x1f,0x61,0x5f, - 0xc4,0x5f,0xff,0xf2,0x6f,0x6a,0xf4,0xb2,0x29,0x4f,0x9c,0x01,0x92,0xc5,0x28,0x0d, - 0xfc,0xb9,0x08,0xe3,0x2c,0x4f,0x97,0x73,0x40,0xb1,0x58,0x00,0x16,0xa1,0xb6,0xf0, - 0x33,0x6c,0x12,0x7b,0xb9,0x48,0x16,0xcb,0x85,0x68,0xd2,0x1f,0x6f,0x94,0x65,0x22, - 0xa4,0x4f,0x22,0x4b,0x96,0xe9,0x28,0x10,0xc9,0x04,0x9f,0xb2,0x40,0x5c,0xf9,0xd1, - 0x32,0xc8,0x5a,0x9e,0xf8,0x6e,0xfb,0xde,0x20,0x4d,0x92,0x5c,0x7c,0x86,0xea,0x9d, - 0x0e,0x35,0x31,0x10,0xdf,0x4c,0x76,0x83,0x49,0xb0,0x7f,0x60,0xde,0x75,0xfa,0xf0, - 0x36,0x18,0x07,0x0f,0xc7,0x43,0x7e,0x1b,0xc6,0x97,0xf0,0xa6,0xb7,0x8b,0xff,0xe7, - 0x37,0xf3,0x65,0x1e,0x8c,0xe1,0xdd,0xfe,0x70,0x7f,0x6f,0xcf,0xe7,0x77,0x13,0x98, - 0x95,0x1c,0xde,0xf9,0xfb,0x8f,0x27,0x8f,0xbb,0xfc,0x2e,0x5d,0x46,0x01,0xbc,0x1a, - 0x3f,0x1e,0xf7,0x46,0xf2,0x55,0x44,0x15,0x83,0x47,0x80,0xf0,0x80,0xdf,0x24,0x08, - 0xbd,0xbf,0xff,0xd0,0xdf,0xdd,0xe1,0x17,0x43,0x1f,0x8b,0x0c,0x77,0x77,0x86,0xfd, - 0xb1,0x84,0x1d,0x06,0x11,0xbe,0x9b,0x0c,0x27,0x0f,0x83,0x89,0xec,0x43,0x12,0x27, - 0x03,0xb1,0x0c,0xe9,0x47,0xb6,0xf0,0x47,0x30,0x41,0x67,0x2f,0xdf,0xc2,0x43,0xe7, - 0x43,0x30,0x5d,0x46,0x3e,0xcc,0xce,0xdb,0x20,0x8e,0x92,0xb6,0x38,0xa6,0xf9,0xf1, - 0xb3,0xb6,0xd0,0x65,0x0f,0xee,0xdd,0xde,0xbb,0xf7,0x9d,0xf8,0x2c,0x86,0xc9,0x4d, - 0x27,0x0b,0x7f,0x85,0x19,0x18,0xc0,0xef,0x14,0x26,0xb5,0x03,0xaf,0x0e,0x04,0x7c, - 0x1e,0x26,0xe3,0x15,0x21,0x6b,0xee,0xa7,0xd3,0x30,0x1e,0x08,0x1a,0xc2,0xd0,0x1f, - 0x5d,0x4e,0xd3,0x64,0x19,0x43,0x87,0x80,0xed,0x34,0x25,0xd6,0x5a,0xf8,0x6d,0x94, - 0x44,0x49,0xaa,0x5e,0x03,0xda,0xe8,0xe5,0x24,0x41,0xbc,0xf4,0xfa,0x8b,0x9b,0xed, - 0x9e,0xb7,0xbb,0x27,0x5e,0xc3,0xa2,0x81,0xbe,0x41,0xcf,0x33,0x3f,0xce,0x3a,0x59, - 0x90,0x86,0x93,0xb6,0xc8,0x56,0x59,0x1e,0xcc,0x3b,0xcb,0x10,0x7e,0xea,0xd7,0xd4, - 0x4b,0x0f,0xc9,0x4a,0xf6,0xe3,0x86,0x57,0x17,0x82,0x7b,0xd4,0x5d,0xdc,0x1c,0xd8, - 0x9d,0x13,0xfe,0x32,0x4f,0xf0,0xcd,0xc2,0x1f,0x8f,0x69,0x3c,0xfd,0x5d,0x2e,0x02, - 0xf2,0x73,0x11,0x81,0x74,0x16,0x93,0x28,0xa0,0x17,0xf8,0xb7,0x03,0x12,0x3e,0xa0, - 0x65,0x3f,0xc0,0x7e,0x2f,0xe7,0x31,0x7e,0x99,0xfa,0x0b,0x00,0x4e,0xf5,0xb0,0x69, - 0xc4,0x17,0x20,0x09,0x87,0x00,0x13,0x3c,0x0f,0xa3,0x95,0x1a,0x1d,0x7e,0x69,0x21, - 0x9a,0x40,0x3a,0xcf,0xcf,0x61,0x51,0x41,0x31,0x67,0xf8,0x44,0x23,0x5c,0x62,0x06, - 0x94,0xa1,0xa0,0x00,0xae,0x81,0x24,0x7a,0x3d,0x68,0xc1,0x2d,0x4f,0xf4,0xc3,0xe5, - 0x69,0x86,0x52,0xac,0x81,0xfd,0x04,0x64,0x5f,0x43,0x0d,0x9a,0x12,0x58,0x45,0x7f, - 0xfd,0xb7,0xbf,0xc0,0x3f,0x02,0x15,0x2a,0x28,0x23,0x9f,0x80,0xb6,0xe5,0x8b,0xcf, - 0x85,0xd1,0x0a,0x1f,0x14,0x96,0xb8,0x13,0x02,0x72,0x33,0x18,0x68,0x80,0xc8,0x3f, - 0x10,0x3f,0x2f,0xb3,0x3c,0x9c,0xac,0x3a,0x92,0x83,0x0d,0x04,0x35,0xd9,0x19,0x06, - 0xf9,0x75,0x10,0xc4,0x07,0x12,0x0d,0xfd,0x05,0x11,0x82,0x37,0x4c,0xfd,0x78,0xbc, - 0x21,0x64,0xaa,0xf9,0x58,0x56,0x04,0x62,0x87,0x6a,0x72,0xca,0x1e,0xe1,0xcb,0x59, - 0x40,0xea,0x13,0x3f,0x94,0x49,0x29,0x22,0x94,0x49,0x3a,0x4c,0xfd,0x71,0x08,0xd2, - 0x54,0xf4,0x24,0x34,0x62,0x8f,0x05,0x3c,0xe2,0x4c,0xf1,0x8b,0x6b,0x09,0x79,0xbf, - 0xdb,0xa5,0xd2,0x97,0xe1,0xe8,0x92,0x91,0x58,0x3b,0x79,0x36,0x20,0xa4,0x26,0x41, - 0x3c,0x26,0xed,0x20,0x32,0x88,0x80,0xba,0x5e,0xf7,0x71,0x30,0x3f,0xa8,0x9d,0xd8, - 0x51,0x04,0x78,0xdc,0x18,0xe9,0x84,0x9a,0x47,0x3c,0x18,0xa8,0x3b,0x0b,0x17,0x44, - 0xd5,0x3c,0x5a,0x1a,0x26,0x30,0xaf,0x28,0x1c,0xcb,0x76,0x90,0x79,0x94,0x91,0xd1, - 0xaf,0x46,0x1c,0x71,0x88,0x96,0x43,0xfd,0x7b,0x00,0x8f,0x87,0xb5,0x19,0x06,0xca, - 0x24,0xc9,0x2b,0xf8,0x96,0xfb,0x8a,0x74,0xbe,0x86,0xc6,0x93,0xcb,0x63,0x1a,0x90, - 0xea,0xaf,0x53,0x0e,0x64,0x79,0x01,0x34,0xbd,0xa1,0xc5,0x9d,0x86,0xb0,0x82,0x57, - 0xcf,0xf3,0x98,0x90,0xc1,0xcc,0x22,0x8c,0x41,0xe0,0x84,0xb9,0xdb,0x3f,0x1a,0xf9, - 0x68,0x99,0x66,0x08,0x65,0x91,0x84,0x84,0xd4,0x35,0xf8,0xa3,0xde,0x57,0xa1,0xaf, - 0x92,0x89,0x71,0x69,0xa7,0x8f,0x92,0xaf,0x19,0x8c,0xee,0x23,0x46,0x25,0x6f,0xb0, - 0x3a,0x3e,0x50,0xfa,0x39,0x8c,0x3e,0x41,0xd2,0x01,0xc3,0x01,0x48,0x67,0x67,0xcf, - 0x74,0x77,0x1c,0x4c,0xfc,0x65,0x94,0x13,0xa6,0xa6,0xb3,0x24,0xcb,0x37,0x18,0x6f, - 0xef,0x0b,0xc7,0x5b,0x4f,0x2f,0x95,0x03,0x96,0x14,0x53,0x3d,0xa1,0xee,0x90,0xfb, - 0x72,0xc8,0xaa,0xe7,0x83,0x59,0x72,0x45,0x54,0x5f,0x35,0xd7,0xbc,0x84,0x2b,0x5e, - 0x39,0xec,0x0b,0xb5,0x93,0x21,0x28,0xad,0xdb,0xf0,0x6b,0x3a,0x8d,0x82,0xcc,0xe2, - 0x64,0x9e,0xfa,0xf8,0x05,0xcb,0x8a,0x09,0x1d,0xb9,0xb1,0x3f,0x27,0x75,0xe2,0xcb, - 0x97,0xa4,0xae,0x1a,0xc6,0x8b,0x65,0x6e,0xf8,0x56,0x6f,0x87,0x60,0xfb,0x23,0xac, - 0xd4,0xa9,0x1c,0x98,0xae,0xea,0x08,0x0c,0x7b,0x1e,0x41,0xdd,0x57,0xa2,0x6b,0x6f, - 0x1f,0x5f,0xa0,0xca,0xda,0xa1,0x3e,0x0d,0x44,0x8a,0x9c,0x8b,0x59,0x1c,0x21,0xe3, - 0x63,0xea,0x2f,0xaa,0xa8,0xa3,0x48,0x0c,0x9b,0x0f,0xf1,0xa1,0xa4,0x82,0x2f,0x5d, - 0x2a,0x6b,0xe8,0xc6,0xe5,0x33,0x3d,0x49,0x22,0x3c,0x80,0x37,0xfe,0x30,0x88,0xaa, - 0xd0,0xe0,0x70,0xea,0x3d,0xc9,0xa9,0xb9,0x0e,0x0d,0x58,0xa2,0xa8,0xdf,0xb7,0x65, - 0x45,0xaf,0xa6,0x27,0xd5,0x04,0x2f,0x87,0xba,0x48,0xb2,0x90,0x85,0x7b,0x1a,0x44, - 0x7e,0x0e,0x36,0xbd,0x85,0xae,0x30,0x8e,0xc2,0x18,0xc4,0x5d,0x94,0x8c,0x2e,0x0f, - 0x34,0x56,0xba,0x15,0x18,0xd6,0xa3,0xec,0x2a,0xbd,0x61,0x20,0xe2,0x24,0x0e,0xac, - 0xc1,0x0a,0x6f,0x9c,0xe4,0x3f,0xb4,0x85,0x3d,0x79,0xf4,0x8a,0x46,0x64,0xfa,0xe1, - 0x0f,0x01,0xe5,0xb0,0xbe,0x50,0xcc,0x4c,0x72,0x29,0xd6,0xd0,0x86,0xe5,0x5f,0x8a, - 0xda,0xba,0xce,0xd0,0xbb,0x77,0x2d,0xdf,0xc2,0xe8,0xf7,0xba,0xff,0x00,0x40,0x41, - 0x5e,0xab,0x56,0xb1,0x2d,0x60,0x46,0xbd,0x7e,0x66,0x75,0x99,0xfb,0xa8,0xf0,0xae, - 0x46,0xdb,0x09,0xae,0x80,0x66,0x32,0x39,0x40,0x97,0x1c,0x3d,0xb0,0x17,0x4c,0x85, - 0x72,0x77,0x24,0x3f,0x2f,0xd6,0x60,0x34,0xa8,0x01,0x6b,0x41,0x4e,0x85,0xbc,0x6c, - 0xee,0x47,0x11,0x16,0xbb,0x1b,0xa0,0x29,0x5b,0x0d,0xd2,0xe2,0x2c,0x73,0xd0,0xa3, - 0xc4,0x22,0x02,0x3d,0xdf,0xe2,0x28,0xf8,0xac,0x57,0x55,0x15,0x69,0xac,0x93,0xa8, - 0x5f,0xc1,0x70,0x6f,0xef,0x7d,0x43,0x5d,0xb0,0xb8,0x90,0x24,0xb6,0x92,0x44,0xc8, - 0x93,0xe5,0x68,0xd6,0xf1,0xa5,0x26,0x6a,0x50,0xef,0x4f,0x6b,0xe9,0x67,0xe3,0x65, - 0xbf,0x5f,0x47,0x3d,0xd2,0xd0,0x69,0x1d,0x7c,0x9d,0x34,0x31,0x1a,0x36,0xd4,0xc2, - 0x05,0x57,0x4d,0x42,0xf7,0x78,0x1c,0x5e,0x46,0xcc,0xa0,0x5e,0x58,0xc8,0x62,0x24, - 0x7c,0xce,0x71,0xdc,0xb6,0x18,0xdd,0xdb,0x53,0x05,0xa4,0x96,0xfd,0x05,0xfa,0x9c, - 0x43,0x18,0x80,0xf9,0xab,0x00,0xba,0x1e,0x43,0x6f,0x6c,0xd2,0xa0,0x17,0x9f,0x4b, - 0x66,0x02,0x63,0xb0,0xdf,0xbd,0x4b,0xdf,0xfa,0x0a,0xf5,0x4d,0xa3,0xaf,0x47,0xe2, - 0xf5,0x91,0xe4,0x9d,0x80,0x99,0xb2,0xd8,0xaa,0x31,0x55,0x5c,0xc9,0x05,0x2f,0x3d, - 0x34,0x13,0x4a,0x26,0x83,0xab,0xc7,0x43,0xb1,0x1f,0xfe,0x0b,0xb4,0xe2,0xab,0xd4, - 0x12,0x9e,0x6b,0x59,0x37,0x2c,0x13,0x9e,0x95,0xf2,0x3a,0xa9,0x5e,0x16,0xa8,0x76, - 0x81,0x51,0x9f,0x67,0xc7,0x84,0x2b,0x25,0x2e,0xf6,0x15,0x22,0xf8,0xeb,0xf7,0x69, - 0xe8,0x98,0x2b,0x53,0x78,0x3e,0xa0,0xff,0x76,0x60,0x91,0xc0,0xbb,0x3c,0xe8,0x30, - 0x1e,0x33,0xe4,0x01,0x8b,0xc0,0xcf,0x9b,0x3b,0x6d,0x94,0xd1,0x60,0x61,0x36,0xbb, - 0x6d,0xd1,0x9b,0xa4,0xad,0x56,0x01,0xc7,0x0c,0x7a,0x13,0xb1,0x5c,0x26,0xa5,0xba, - 0x49,0xac,0x5f,0xb9,0x7b,0xeb,0x05,0xf6,0x17,0xd9,0x06,0xc6,0x3e,0xd7,0x84,0x07, - 0x06,0x9a,0xd8,0x95,0x6b,0xf7,0x9e,0x19,0x1c,0x4c,0x71,0x5c,0x10,0xdb,0xdd,0x6a, - 0xb1,0x5d,0xb6,0x13,0x0c,0x8e,0x24,0x4b,0x5f,0xb7,0xdc,0x4d,0x41,0xbb,0xc9,0xa2, - 0x09,0x07,0xb6,0xd5,0x22,0xfb,0x40,0x64,0xfd,0x45,0x9a,0xce,0x1d,0x66,0x8d,0x45, - 0xaa,0xaa,0x85,0x4f,0xb3,0x70,0x3c,0x0e,0xe2,0x0b,0xbb,0x25,0xcd,0x8a,0x75,0x37, - 0x86,0xcb,0x3c,0x4f,0xe2,0x8d,0x34,0x33,0x45,0xfe,0x8e,0xd1,0xcb,0x4f,0x96,0x4a, - 0x21,0x48,0x0d,0xd1,0x9f,0x7f,0xef,0x19,0xaf,0xb6,0xe6,0x2c,0x96,0x08,0x73,0x90, - 0x43,0x57,0x32,0x49,0x90,0x36,0x5b,0x1c,0x82,0xc9,0xff,0x2a,0xf0,0xef,0x32,0xfb, - 0x87,0x3e,0x30,0xf7,0x10,0x11,0xe5,0x2c,0x16,0xac,0xfd,0x0e,0x1d,0x79,0x2e,0x31, - 0xed,0x54,0x5b,0xeb,0xd7,0x33,0x80,0x46,0xfc,0x25,0x40,0xb4,0x5f,0x83,0xa0,0x26, - 0x28,0xc4,0x64,0xbf,0x90,0x25,0xee,0xcb,0x1e,0x50,0xdd,0xcd,0x69,0x67,0x23,0x87, - 0xc8,0x23,0x1b,0x76,0xad,0x8e,0x5b,0x43,0x6b,0x40,0x47,0x41,0x85,0x59,0x82,0x9e, - 0x8a,0x38,0x19,0xae,0xb3,0x07,0x37,0xb4,0xdc,0xbf,0xdc,0x52,0xdc,0x8c,0x6e,0xe4, - 0x10,0x6c,0xf6,0xa1,0x0d,0x61,0x45,0x8f,0xa8,0x8d,0x29,0xfb,0xa6,0x4b,0x1c,0x45, - 0x0e,0x6a,0x40,0xfb,0x37,0x9d,0xd1,0x2c,0xa4,0x99,0xac,0x28,0xde,0xb3,0xc0,0x58, - 0x64,0x2d,0xba,0xf0,0x7f,0x25,0xb8,0x14,0xb0,0xc8,0x2f,0xc3,0x52,0x75,0xa8,0x34, - 0xd7,0xb4,0xeb,0xd4,0x33,0x23,0x69,0xf4,0x7c,0x89,0x27,0x00,0x67,0x31,0x59,0xe6, - 0xe7,0x29,0x54,0x22,0xbd,0xb9,0xac,0x48,0xba,0x6b,0xbd,0xc4,0x17,0x6a,0xb4,0x3d, - 0x00,0xfa,0xc1,0x0f,0x23,0x07,0x66,0xd1,0x5e,0x20,0x7c,0xa7,0x0c,0x7d,0x57,0xdb, - 0x0e,0x0e,0x83,0xe9,0xdf,0x21,0x6d,0xb1,0x9d,0x97,0x61,0x54,0xd7,0x0e,0x01,0x7c, - 0xe8,0xed,0xd9,0x20,0x77,0xd6,0x39,0xea,0x18,0xe2,0x2b,0x58,0xec,0xd2,0x7e,0xab, - 0x85,0xba,0x6b,0x99,0x38,0x8f,0xbf,0xc6,0xc2,0xa9,0x31,0x5a,0x5d,0x7e,0x36,0x49, - 0x12,0x74,0xc7,0xa1,0xbb,0xca,0x71,0x28,0xf0,0x8b,0xaf,0x70,0x09,0x60,0xc5,0x63, - 0x3f,0x1d,0x7f,0x9d,0xd8,0x5f,0x0f,0xff,0xab,0x06,0xbd,0x99,0x5a,0xfe,0xd0,0xf6, - 0xda,0xa8,0x31,0x6c,0xa2,0x86,0xeb,0xb2,0xb4,0xe8,0xa5,0x47,0xaf,0xac,0x8c,0x63, - 0xb1,0x17,0x89,0xe5,0x23,0xd9,0xb7,0xa7,0x74,0xff,0x2b,0x7c,0xbb,0x1a,0xd1,0xb5, - 0x1e,0x14,0x28,0xe4,0xa7,0x52,0xc7,0x2d,0xea,0x27,0x35,0x8e,0x74,0x8b,0x32,0x80, - 0x4c,0xd1,0xa9,0x23,0xb5,0x47,0xf1,0x00,0x7f,0xe1,0x46,0x9e,0x4d,0x25,0x50,0xe6, - 0x54,0x7e,0xff,0x5c,0x69,0x0c,0x28,0xd9,0xa2,0x0b,0xde,0xe5,0xc2,0xeb,0xfe,0xe7, - 0xba,0xf0,0xd6,0xb1,0x6b,0x43,0x0c,0xb0,0x82,0x85,0x32,0x31,0x74,0xcf,0xbf,0xda, - 0x85,0xe7,0x2d,0xae,0xde,0x43,0x4d,0x40,0x4d,0x61,0xc1,0x4f,0xc2,0x9b,0x00,0xb4, - 0xec,0x30,0x46,0xe8,0xc8,0x83,0x7f,0x85,0x15,0x3a,0x46,0x7f,0xc9,0x5e,0x69,0xdf, - 0x28,0x9d,0x0e,0xfd,0x66,0x1f,0x14,0x6d,0xf5,0x6f,0xd7,0xdb,0xdd,0x6b,0x6d,0xba, - 0x82,0x4a,0xb2,0x5a,0x7e,0x20,0x45,0x56,0xf5,0x6e,0xad,0x46,0xb7,0xb8,0xd2,0x8b, - 0xba,0x1e,0xab,0x5f,0xe4,0x2c,0xdb,0x29,0xac,0x40,0x66,0xff,0x5f,0xa2,0xb8,0xf4, - 0xe4,0x34,0xab,0x15,0xb5,0x4b,0xc4,0x63,0x6d,0x79,0x3d,0xde,0xbd,0xba,0x96,0x63, - 0xac,0x56,0xcc,0xee,0xd2,0x61,0x2a,0x91,0xc9,0xd8,0xa8,0xe6,0x8f,0xae,0xfd,0x73, - 0x75,0x16,0x4e,0xff,0x16,0x7d,0xd6,0x3c,0x1f,0xd5,0xee,0x48,0xea,0xf4,0x5d,0xf6, - 0x48,0x95,0xed,0xf1,0x0d,0xd4,0x1c,0x25,0x8b,0x2a,0xcb,0xf4,0xab,0x99,0x36,0xf5, - 0xe7,0x4d,0x30,0x0d,0xaa,0x36,0xd3,0x8c,0xf1,0x52,0x3f,0x4d,0x3f,0x04,0xab,0xd7, - 0x71,0x5b,0xf0,0xaf,0xf7,0x8e,0x8f,0x7a,0xbf,0x24,0xb3,0x6b,0x9c,0x9d,0x06,0x50, - 0xa5,0xdb,0xcd,0xda,0x85,0xb4,0x5a,0xa9,0x97,0xff,0x8e,0x83,0x85,0xc3,0x00,0x44, - 0x76,0x19,0xc6,0x82,0xd8,0x8b,0x09,0x21,0x68,0x64,0x72,0x4f,0x3f,0x4f,0x2e,0x83, - 0xd8,0x16,0xce,0x55,0x7b,0xf2,0xdd,0xe3,0xee,0xf3,0xee,0xe3,0xe2,0x9e,0x7c,0xef, - 0x79,0xef,0xb1,0xda,0x81,0xe7,0x3d,0xf9,0x93,0x17,0x27,0x0f,0x5f,0x3c,0x77,0xf7, - 0xe4,0x1f,0x1d,0x3d,0xea,0x3f,0xec,0xbb,0x7b,0xf2,0x7b,0x47,0x7b,0x7b,0xbb,0xfb, - 0xce,0x9e,0x7c,0x7f,0xb7,0xdf,0xeb,0x3d,0xb2,0xf7,0xe4,0x5f,0xbe,0xdc,0x3b,0xee, - 0x9f,0x58,0x7b,0xf2,0x7b,0x0f,0x9f,0x3f,0x7a,0xf8,0xd2,0xde,0x93,0x7f,0xf9,0x72, - 0xff,0xf9,0xde,0x91,0xbb,0x27,0xdf,0xdb,0xe9,0xf5,0x7a,0xa4,0xf3,0x02,0x26,0xce, - 0x61,0xc0,0x12,0x0d,0x30,0xe2,0x37,0x38,0x1d,0x72,0x83,0x43,0x64,0xb9,0x3f,0x87, - 0x45,0x86,0x91,0x71,0x9f,0xc6,0x7e,0xee,0x77,0x00,0x39,0xf3,0xe0,0x90,0xc2,0xc9, - 0x2e,0x04,0x6e,0xb0,0x22,0xca,0xd2,0x70,0x1c,0xe8,0x10,0x87,0xe3,0xf7,0xef,0xce, - 0xde,0xbf,0x39,0xe9,0x60,0xdc,0x09,0xd1,0x52,0x88,0xdb,0x4c,0x99,0x68,0x52,0xd5, - 0x9f,0x88,0xbd,0x7a,0xb3,0x96,0x47,0xcd,0x6a,0x3c,0xe7,0xd7,0x09,0x45,0xaa,0x64, - 0x18,0x03,0x11,0x82,0x30,0x4b,0xae,0x63,0x84,0x16,0x07,0x00,0x84,0x9a,0x93,0x31, - 0x11,0x02,0xa3,0x3a,0x04,0x87,0x77,0xb0,0xc7,0x94,0x0d,0x2e,0xec,0x4b,0xee,0x83, - 0x55,0x08,0x5d,0x5e,0x89,0xb1,0x9f,0x5e,0x02,0x0d,0x09,0x9f,0xeb,0x52,0xe8,0x05, - 0x6e,0xc8,0x77,0xd0,0x6c,0x1b,0xcd,0x40,0xf5,0x05,0x5e,0xe4,0x67,0x62,0x0b,0xa1, - 0xc8,0xb1,0x8e,0x13,0x80,0x1e,0x27,0xf9,0x0c,0x56,0xe4,0x96,0x27,0x8e,0xfd,0xf8, - 0xca,0xcf,0x78,0xd5,0x66,0xb0,0xdc,0x22,0x8c,0x00,0x99,0xa4,0x41,0x30,0x40,0x68, - 0x32,0xf0,0xe3,0xe7,0x8c,0x40,0x65,0x3a,0x6c,0x43,0x8d,0x16,0x0a,0x66,0x33,0x01, - 0x6b,0x38,0x00,0xfc,0xac,0xa0,0x71,0x98,0x53,0x8a,0xe6,0xa8,0x41,0xe5,0xe7,0x02, - 0xd9,0x9c,0x3c,0x3a,0xe9,0xbf,0xd8,0x77,0x27,0xed,0xe5,0xcb,0x97,0xc7,0x2f,0x8f, - 0xe4,0xa4,0x71,0xdc,0x8e,0xc0,0xcd,0x2d,0x52,0x1d,0x31,0x92,0x64,0x1e,0xf8,0x31, - 0x74,0x1e,0xda,0xcf,0x60,0x9c,0x01,0x47,0xbd,0xd0,0xb8,0x5f,0x9f,0xd1,0x6f,0xae, - 0x74,0x60,0x85,0x9c,0xe0,0x58,0xa8,0x3a,0xe2,0xef,0x0c,0xb4,0x5e,0xe9,0x5a,0x82, - 0xfa,0x7e,0x6e,0xd5,0x81,0xd5,0x84,0xd3,0xf4,0x36,0x1c,0xa5,0xc9,0x62,0x06,0x22, - 0x49,0x64,0xd7,0x61,0x0e,0x98,0xc4,0x7a,0x80,0x34,0x78,0x06,0xb4,0xc3,0xf8,0xbe, - 0xc1,0x1e,0x21,0x6b,0x6e,0x8b,0x6f,0xb8,0x09,0xf5,0x00,0xc0,0xc9,0x60,0xac,0x10, - 0x6d,0xdc,0xc6,0x73,0xdc,0x3d,0xfb,0x12,0x85,0xb4,0xb7,0xfb,0xdb,0x34,0x52,0xcd, - 0xe3,0xeb,0x3c,0x9d,0x5d,0x7b,0xef,0xf4,0xcb,0xf7,0x15,0x00,0x67,0x6f,0xb3,0xa9, - 0xd4,0xfd,0x58,0xd0,0x14,0x03,0x31,0xbe,0xe1,0xa1,0xbf,0x03,0x0c,0x0e,0x82,0xf9, - 0x22,0x5f,0x01,0xa7,0xe4,0x6a,0xfc,0x58,0xad,0x09,0x4c,0xa0,0x89,0xe2,0xb6,0xb6, - 0x6a,0x14,0x3e,0xc2,0x8a,0x2c,0x7e,0x05,0x56,0x40,0x9f,0xd5,0x72,0x9f,0xa5,0xc9, - 0x9c,0xe6,0xcc,0xa1,0x14,0x74,0x2a,0x03,0x1d,0x2d,0x63,0xa0,0xc3,0xd1,0x0c,0x29, - 0xf9,0xc0,0xd0,0x50,0x12,0x47,0xb0,0xb0,0xc2,0x79,0xc6,0xb4,0x11,0xd2,0xaa,0x1a, - 0xf9,0x31,0xf4,0x4b,0x44,0x49,0x3c,0x05,0xd6,0x79,0x85,0x86,0x22,0x8c,0x36,0x6d, - 0x13,0x5d,0x8c,0x81,0x56,0x38,0xbe,0x69,0x8b,0xa2,0xdf,0xf0,0xd7,0xdc,0x50,0x50, - 0x12,0x6f,0x09,0x8a,0x32,0xf1,0x69,0xa9,0x2f,0x63,0x3f,0xce,0xae,0x61,0x75,0x8f, - 0x05,0x46,0x04,0x8a,0xeb,0x64,0x09,0xeb,0x3a,0xc1,0x30,0xae,0xeb,0x10,0x16,0x57, - 0x36,0x4b,0xae,0xf5,0x12,0x22,0x1c,0x4c,0x26,0x28,0x24,0x84,0xde,0x28,0x69,0x8b, - 0xd2,0x27,0x8b,0xb2,0x1c,0x93,0x00,0x71,0x51,0x2c,0xfc,0xcd,0xc8,0x5f,0x40,0x3f, - 0x83,0x57,0x1c,0xf9,0x72,0x15,0x66,0xe1,0x30,0x8c,0xa8,0x06,0xeb,0x65,0x12,0x81, - 0x1f,0x92,0x6b,0xc0,0x01,0x6e,0xcc,0x0c,0x03,0x18,0x6b,0x80,0xe3,0x5a,0x11,0x23, - 0xa4,0x28,0xb1,0xa6,0x15,0x83,0x76,0x0d,0x1c,0x24,0xf2,0x81,0xec,0x40,0x87,0x45, - 0xbc,0x00,0x43,0x02,0xe1,0x07,0x35,0xfc,0x21,0x07,0x77,0x99,0x0e,0xd2,0x5e,0x1c, - 0x6d,0x21,0x93,0xb0,0x24,0xed,0x06,0x7e,0x19,0xfd,0xbe,0x2d,0x74,0xb4,0x0c,0xe9, - 0x64,0xd8,0x83,0x81,0x50,0x7e,0x27,0x4b,0xf6,0xdb,0x11,0x47,0x5d,0xdc,0x5a,0x53, - 0x22,0x16,0x63,0x8d,0xc4,0xfd,0x70,0x8e,0x81,0x80,0x7e,0x9c,0x5b,0xf4,0x30,0x83, - 0x02,0x01,0x70,0x12,0x74,0x81,0x21,0x37,0xf6,0xa7,0x62,0xe6,0xa3,0x8f,0x6d,0xe1, - 0xc3,0x5b,0x0c,0x22,0x24,0x2b,0x06,0x11,0x0d,0x9f,0xc9,0x9e,0x87,0x21,0x4e,0x41, - 0x26,0x8d,0x22,0xe4,0x3d,0x44,0x0b,0x14,0x70,0x27,0x52,0x1f,0xff,0xc2,0x03,0x50, - 0x06,0x8e,0xd5,0x70,0x21,0xaf,0xb0,0xab,0xa5,0x51,0x66,0xe3,0xf7,0xbf,0x81,0xd4, - 0x08,0x7d,0xd1,0x74,0x07,0x01,0x0b,0xb1,0x45,0xbc,0x41,0xed,0x7c,0x54,0xa1,0x00, - 0xbe,0x5e,0xa5,0x55,0x0b,0x06,0x3e,0x14,0x37,0x1b,0x86,0x7e,0x16,0x66,0x0a,0x3f, - 0xb7,0xcc,0x52,0x4f,0x91,0x2a,0xe1,0x25,0xd2,0x26,0x6b,0xb9,0x2c,0xa4,0x98,0x47, - 0x00,0x59,0x0e,0x93,0x1b,0x60,0xed,0x18,0x4e,0x88,0x63,0x07,0xb9,0x41,0x7f,0xdb, - 0x52,0xa6,0x84,0xbc,0x30,0x10,0x13,0x11,0x9a,0x63,0x38,0x6c,0x98,0xb9,0x05,0x48, - 0xd6,0x40,0xee,0xe3,0x4c,0x42,0x44,0xe6,0xce,0x7e,0x57,0x20,0x3f,0x02,0x6e,0x1f, - 0x80,0x42,0x0d,0xef,0xa0,0xaa,0xcf,0xb6,0x88,0xd8,0xd9,0xdb,0xc7,0x8f,0x23,0x92, - 0x3e,0x08,0xeb,0x1a,0x18,0x6a,0xa0,0xd4,0x52,0x98,0xdb,0x7c,0x06,0x85,0xb3,0x6c, - 0x39,0x47,0xba,0x83,0x75,0x88,0x4a,0x6c,0x1b,0x98,0x1b,0xc2,0x99,0x03,0x0f,0x06, - 0x66,0x0c,0x74,0x29,0x28,0x84,0x13,0x96,0x20,0xc8,0x51,0xc2,0x7b,0x05,0x5e,0x1f, - 0x3a,0x68,0x9d,0xd2,0xa6,0xaa,0x66,0x7d,0x7d,0x69,0x8d,0x73,0x3c,0x9a,0xbb,0x27, - 0x23,0x84,0x43,0xb6,0x66,0xf9,0x59,0xfb,0x52,0x76,0x8c,0x5b,0x47,0x6e,0x86,0x1b, - 0x3f,0x1a,0xd9,0x5a,0x8a,0xf7,0x32,0x71,0x76,0x4b,0x1a,0x35,0x59,0x60,0x6e,0x6b, - 0xc5,0x2e,0x5a,0x81,0x14,0x76,0xef,0x4c,0x2c,0x46,0xe5,0x67,0x03,0x4e,0xc5,0x40, - 0x98,0xc5,0x67,0xbd,0xb2,0x78,0xb3,0xe6,0xca,0xf0,0x16,0x57,0x91,0x64,0x11,0x55, - 0xa4,0x24,0x44,0x29,0x26,0x83,0x77,0xe0,0x7b,0xda,0x97,0x44,0x31,0x7f,0x76,0x74, - 0x45,0xb7,0xd0,0x6f,0x19,0x4c,0x67,0x35,0x56,0x2f,0x0d,0x8a,0x6b,0xa2,0xd6,0x54, - 0x73,0xfd,0xf8,0x28,0xd6,0x6c,0x9c,0x54,0x6d,0x51,0x71,0x3f,0xf1,0xb3,0xde,0xf1, - 0x2a,0x4d,0x2a,0x4b,0xe7,0x2c,0x88,0x26,0xb6,0xde,0x8f,0x2b,0xae,0x68,0x9e,0xad, - 0xdd,0xd0,0xea,0x4d,0x52,0xfc,0x57,0xaf,0xc5,0x73,0x94,0x24,0xc8,0x17,0x60,0xc1, - 0x80,0x30,0xca,0x04,0x2d,0xde,0x30,0x26,0xa6,0x1c,0xad,0x58,0x2c,0xa1,0xcd,0x98, - 0x26,0x51,0x26,0x2e,0x83,0x60,0x81,0x6f,0xc2,0x94,0x96,0x83,0x43,0xf0,0x72,0xe5, - 0x20,0x36,0xf0,0x04,0x87,0xa4,0x77,0x23,0xcc,0x61,0x6a,0x55,0x88,0x10,0x31,0x5e, - 0xb0,0xbd,0xda,0x42,0xbb,0x76,0x6c,0x0e,0x0c,0xbf,0x4b,0xbb,0x2a,0x95,0xee,0xd4, - 0x5b,0xb7,0x85,0xc1,0xc0,0x9f,0x50,0xf4,0xa8,0x89,0x45,0xd2,0x6f,0xa8,0x3d,0xf3, - 0xa8,0x9a,0x35,0x6f,0x8c,0xf3,0x43,0xbe,0x42,0xd8,0x85,0x6e,0x38,0xa5,0xc9,0xcf, - 0x6b,0xde,0xd8,0xa1,0x08,0xf2,0xad,0x5c,0x9f,0xda,0xe2,0xde,0xda,0x3a,0xa8,0xf4, - 0x81,0x4a,0xaf,0x48,0x07,0x08,0x47,0x74,0xa4,0x79,0x6b,0x0d,0xad,0x00,0x53,0x17, - 0xa7,0xfd,0xe1,0x8e,0xf2,0x7f,0x81,0xb5,0xf5,0x64,0x5b,0x86,0x53,0x3f,0xd9,0x96, - 0x27,0x6a,0x30,0x48,0x17,0xfe,0x8c,0xc3,0x2b,0x90,0x1f,0xc0,0xcc,0x0e,0xb7,0x90, - 0xff,0x6c,0x3d,0xbd,0x07,0xd0,0x9f,0xb0,0x94,0x7b,0x4a,0x7d,0xb4,0x8b,0x50,0x40, - 0xe7,0x16,0xbf,0x87,0x2f,0xb4,0x07,0x27,0x3f,0x01,0xa7,0xa3,0x83,0x36,0xf0,0xaa, - 0xf2,0x3b,0x05,0x61,0x6e,0x3d,0x2d,0xc5,0x72,0xaf,0xa9,0xc2,0x91,0x98,0x5b,0x4f, - 0xdf,0x1e,0x9d,0x9e,0xbe,0x7e,0xf7,0xbd,0x5d,0xf2,0xc9,0x36,0xf4,0xaa,0xdc,0x3f, - 0x19,0x57,0x59,0xe8,0x61,0x38,0x86,0x2f,0x46,0xaf,0xd8,0x52,0xa5,0x51,0xf1,0xa9, - 0xee,0x33,0xd6,0x30,0x9a,0xb3,0xae,0x80,0xa4,0xb1,0xf5,0x34,0x1c,0x47,0x41,0x5d, - 0x25,0xa5,0x79,0x3b,0x55,0x50,0x63,0xdb,0x7a,0x6a,0x45,0xd4,0x7b,0x60,0x52,0xfb, - 0xd1,0xe0,0x51,0xaf,0x00,0x46,0x52,0x34,0xb5,0xce,0xaa,0xba,0x86,0x63,0xa2,0x06, - 0xb7,0x84,0x0a,0x1b,0x7c,0x8a,0xc6,0xc2,0x93,0x6d,0xae,0x55,0xc0,0x0b,0xcf,0x33, - 0xce,0xe1,0x3d,0x17,0x45,0x9a,0xf1,0x4a,0x24,0xd9,0x8d,0xfa,0xcb,0xf1,0x39,0xd1, - 0x95,0x6e,0xd6,0xac,0xa0,0x2d,0x81,0xe7,0xb4,0x80,0x04,0xa8,0x38,0x3c,0xe1,0x7c, - 0x1e,0x6e,0x9d,0xc4,0xd8,0x15,0x31,0x04,0xd6,0x90,0xa1,0xca,0xe1,0x0f,0x49,0xed, - 0x34,0x87,0x05,0xe8,0xf0,0x00,0x5a,0x68,0x7c,0xac,0xa2,0x79,0xf4,0xe3,0x8b,0x56, - 0x35,0x05,0x71,0x5b,0x30,0x21,0xf6,0x4b,0x8c,0xda,0xd1,0x73,0xb4,0x8e,0xbc,0x4c, - 0xfc,0xd8,0xd6,0x53,0x22,0xac,0xce,0x07,0x6c,0x93,0x1b,0x74,0x49,0xc7,0x41,0x98, - 0x35,0x7c,0xd0,0x8b,0xbf,0x72,0xf8,0x49,0xdc,0x19,0x26,0xe8,0x19,0x34,0xaa,0xf5, - 0x7f,0xf6,0x18,0x8d,0x19,0xb8,0x66,0x70,0xf6,0xbc,0xb3,0x40,0xac,0xee,0x96,0xd9, - 0x98,0xdc,0x7a,0xfa,0x9a,0x24,0xe6,0x14,0xac,0xe5,0x42,0x57,0x58,0x94,0x4a,0x44, - 0x7d,0x0f,0xdf,0x15,0x4e,0x80,0x29,0x00,0xe7,0x40,0x61,0x7a,0xb8,0xd5,0xdb,0x42, - 0xdd,0x17,0xfe,0xee,0x6f,0x09,0x64,0x91,0x87,0x5b,0xa0,0xe9,0x57,0xac,0x47,0x09, - 0xe2,0x8f,0x7e,0xa4,0x91,0x4d,0x0e,0x7c,0x19,0x14,0xbf,0xf5,0xf4,0x91,0xd7,0xad, - 0x5b,0xf0,0x1a,0x88,0x51,0x0e,0xd6,0xac,0x69,0x53,0x9c,0xd5,0x07,0x5d,0x54,0x3d, - 0x82,0xf8,0x0a,0x70,0xc1,0xe1,0xf1,0x9e,0xaa,0xaa,0xb2,0x38,0xeb,0x03,0x85,0x02, - 0x3e,0x01,0x86,0x0e,0x7f,0xbf,0x04,0x3d,0xfb,0x4d,0x18,0x5f,0x6a,0xf0,0x4a,0xd0, - 0x80,0x81,0x95,0x06,0x13,0xe8,0x59,0x9e,0x2f,0xb2,0xc1,0xf6,0xf6,0x34,0xcc,0x67, - 0xcb,0x21,0x68,0xc3,0xf3,0xed,0x20,0x9e,0xce,0x97,0xf1,0x74,0xdb,0x62,0x8a,0xdb, - 0x39,0x68,0xa4,0xdb,0x18,0x8b,0xb6,0x7d,0xe7,0x41,0x9c,0x6f,0xd0,0xef,0x31,0x87, - 0xb1,0xe7,0xa0,0x11,0xe0,0xb9,0xc8,0x9f,0x86,0x91,0x8f,0x3d,0x00,0x31,0x78,0xb8, - 0x15,0x27,0x60,0x8a,0xc4,0xd0,0x63,0x41,0xac,0x1f,0xc8,0x07,0x23,0x47,0xc7,0xc1, - 0x28,0x01,0xf3,0x00,0xc5,0x0c,0x69,0x2f,0x4a,0x35,0x90,0xce,0x3e,0x32,0xf7,0x6d, - 0x6b,0x5f,0xea,0x13,0xa8,0xbe,0x80,0xb6,0xa2,0xe9,0xfe,0x75,0x0c,0xf8,0x02,0x03, - 0xf1,0x98,0x8d,0x58,0x3e,0x95,0x73,0xa2,0x3a,0xb6,0xf5,0x54,0xff,0x14,0x7f,0xfd, - 0x97,0xff,0xf3,0x64,0xdb,0x2f,0x2f,0x36,0x12,0xa8,0x47,0x51,0x54,0x81,0x2f,0x67, - 0xa5,0x3d,0xfd,0x40,0x1b,0x26,0xfa,0xac,0x93,0xa1,0x6f,0x49,0x12,0x45,0x06,0x27, - 0x15,0xb8,0x4a,0xf6,0x96,0x27,0x9b,0x2e,0xf0,0xff,0x74,0x0e,0x05,0x96,0x0b,0xad, - 0x9b,0x35,0xab,0x57,0x13,0xae,0xd2,0x40,0xef,0xa4,0xf2,0x75,0xa4,0xfa,0x35,0xbc, - 0xe0,0x28,0xcf,0x41,0x93,0xa9,0xe5,0x03,0x3e,0x7d,0xae,0x62,0x03,0x5d,0xc5,0x06, - 0x0c,0x17,0xe8,0xf6,0x2a,0xd8,0x00,0x43,0xf8,0x08,0x46,0x47,0x0d,0x1b,0xf8,0x65, - 0x19,0xba,0x1d,0xa8,0x91,0xfc,0x1b,0x0f,0xe9,0x05,0x17,0xac,0x1d,0x93,0x02,0xf4, - 0x1b,0x06,0x25,0x41,0xac,0x19,0xd5,0xd0,0x87,0x85,0x3a,0x0a,0xc6,0x75,0x03,0xb3, - 0x88,0x16,0x17,0xf1,0x29,0xef,0x39,0xde,0xb5,0x54,0x64,0x47,0xf8,0x7f,0x72,0xa1, - 0x9e,0xad,0x62,0x50,0xca,0xf3,0x70,0x24,0x72,0x74,0x2e,0x64,0xb0,0xb2,0xfd,0x08, - 0x0d,0xe3,0x34,0x59,0x4e,0x67,0xec,0x85,0x0c,0x22,0xb0,0x56,0xd0,0xb6,0x46,0xc9, - 0xdd,0x81,0xe7,0x40,0xf0,0xbc,0xb4,0x85,0x32,0xa3,0xc8,0xb5,0x3b,0x53,0x36,0xf4, - 0x3c,0x81,0xff,0xf8,0x02,0x23,0x25,0x40,0xa3,0xe3,0xce,0x55,0xad,0x4c,0x39,0x45, - 0x38,0x0c,0x65,0xa0,0x1a,0x85,0x46,0xbd,0x90,0x43,0x66,0x6b,0x5b,0x97,0x45,0xe2, - 0xe5,0x57,0xe5,0xb9,0xce,0x7d,0x98,0x1e,0x2c,0x08,0x3f,0xba,0x85,0xd5,0x28,0xc3, - 0x3d,0xb7,0x9e,0x3e,0xef,0xfd,0xc7,0xbf,0xff,0xa0,0x14,0x2c,0x09,0xdd,0xc0,0xac, - 0xa1,0x22,0x1b,0x72,0xaf,0x16,0x72,0x1f,0x20,0xf7,0x7f,0x03,0xe4,0x7e,0x2d,0xe4, - 0x1d,0x80,0xbc,0xf3,0x1b,0x20,0xef,0xd4,0x42,0xde,0x05,0xc8,0xbb,0x77,0x42,0x2e, - 0x4c,0x9c,0x36,0x0c,0x62,0x5c,0x39,0x65,0x6d,0x3b,0x89,0xaa,0xd7,0x9b,0x8e,0x17, - 0x05,0xee,0x7d,0x72,0x76,0xfa,0xfe,0xdd,0xd9,0x49,0x61,0xbd,0x59,0xb3,0x4d,0x24, - 0x55,0x9a,0x6e,0xdd,0x91,0x52,0x93,0xc2,0x98,0xc9,0x77,0xb6,0x7e,0xfc,0xe3,0x87, - 0x3f,0x16,0x9b,0xb6,0x07,0xa6,0xa3,0x3e,0x35,0x24,0xb3,0xf8,0x9c,0x32,0xa0,0x74, - 0xe3,0x7e,0x01,0xac,0xf9,0x6c,0x9e,0x24,0xf9,0xac,0x28,0x2d,0xca,0x28,0xa5,0x36, - 0xcf,0xa8,0xb0,0x96,0x0d,0x36,0x77,0xdf,0xa4,0xa9,0x99,0x9f,0x2e,0x36,0x6d,0x09, - 0xcb,0x7e,0x6d,0x43,0x13,0x1f,0xe5,0xf0,0x46,0xed,0xbc,0x84,0xa2,0x5f,0xdb,0xcc, - 0xd4,0x47,0x45,0x6d,0xa3,0x66,0xbe,0x87,0xa2,0x5f,0x8d,0x36,0xb4,0xcc,0x37,0x45, - 0x1b,0x96,0xfd,0xda,0x86,0xfc,0x74,0xb4,0x29,0x21,0x1c,0x41,0xd1,0x9a,0x66,0xac, - 0x65,0x6d,0xf1,0x4b,0xe5,0x5e,0xd0,0xfc,0xd2,0xbc,0x60,0x47,0xad,0xd5,0x4b,0x02, - 0x9b,0x59,0x23,0x29,0x0d,0x40,0x43,0x7c,0x1b,0xc6,0xcb,0x12,0x6e,0xbe,0xfd,0xe6, - 0xd1,0xc3,0x7e,0xff,0xa0,0x62,0xfc,0xc6,0x18,0xc6,0xca,0xef,0x1c,0x69,0xb6,0xf5, - 0x74,0xe7,0xce,0xe6,0x4e,0xa3,0x72,0x6b,0x0f,0xd6,0x23,0x40,0x37,0x19,0x05,0x53, - 0x7f,0xb4,0xaa,0x50,0x7b,0xce,0x67,0x61,0xc6,0xb2,0x2a,0xcb,0x31,0x26,0x2d,0x5d, - 0xc6,0x19,0xef,0x57,0x46,0xe8,0x1f,0xa6,0xdd,0x38,0x12,0x53,0x78,0x4a,0x7c,0x01, - 0x9c,0x00,0x44,0x95,0x8c,0x53,0x06,0xa5,0x6b,0xb9,0x98,0xa6,0x60,0x18,0x43,0x05, - 0x6f,0x8d,0xb2,0xc6,0x2d,0xbd,0x48,0xfd,0x29,0x89,0x3d,0xdc,0x1a,0x9d,0x51,0xb4, - 0x5a,0x26,0xb7,0x10,0x46,0x4b,0xb0,0xd0,0xe7,0x02,0x96,0xe8,0x22,0xf0,0xc4,0x51, - 0xbc,0x92,0x6d,0x66,0xfe,0x55,0x80,0xce,0x61,0xf4,0x31,0xef,0x74,0xc8,0xf1,0x85, - 0x46,0x71,0x14,0x78,0x15,0xb2,0xbe,0xc4,0xde,0xae,0x48,0x69,0xab,0x67,0x7d,0xe8, - 0x81,0xdb,0xaa,0xe2,0x64,0x2a,0xea,0x76,0xab,0x6a,0xf2,0x54,0x50,0xed,0x96,0x5d, - 0x9a,0x5e,0x3c,0x7d,0x8e,0x68,0xec,0x95,0xe6,0xd1,0xa9,0xfa,0x36,0xc8,0xfd,0x1a, - 0x3d,0x66,0xbf,0x0f,0x48,0xfe,0xdf,0x62,0xe7,0xe1,0x9e,0x78,0xf5,0x6b,0x01,0x9d, - 0xd6,0x30,0xdc,0xbe,0x92,0x36,0x66,0x77,0xb4,0xf8,0xed,0x83,0x35,0xc8,0xb5,0xba, - 0xdc,0x7b,0x0e,0xce,0x2a,0xe9,0xd1,0xc5,0x51,0x24,0xcb,0x9c,0x6b,0xb8,0xf4,0xdb, - 0xf5,0x76,0xba,0xd4,0xff,0xae,0xf7,0x68,0xaf,0x84,0x03,0x9b,0x2a,0xad,0x85,0xa9, - 0xdc,0x7b,0x1a,0x96,0x7e,0xe1,0xb4,0x6d,0x8d,0x49,0xc6,0x68,0x6e,0x3d,0x2d,0x80, - 0x74,0x81,0x62,0x78,0xa5,0x0d,0x93,0x9e,0xd7,0x57,0x79,0x93,0xd8,0x15,0x38,0x9a, - 0xf2,0x8e,0x2a,0xaf,0xc2,0x0d,0xaa,0x54,0x0d,0xdd,0x54,0x92,0x5b,0x61,0xee,0x70, - 0x5d,0x46,0xa9,0x4b,0x15,0xbd,0x26,0xc4,0x39,0xa3,0x84,0x54,0x69,0xfa,0x3d,0x0b, - 0x51,0x9d,0x7e,0xda,0x05,0x16,0xd4,0xef,0xf5,0x0e,0x7a,0x65,0x1e,0xf4,0x1b,0x81, - 0x93,0xf7,0x41,0x81,0xf7,0xf6,0x7e,0x1f,0xf8,0x00,0xd3,0xe9,0xbe,0xb7,0xf7,0xfb, - 0xf6,0xdf,0xeb,0xef,0x39,0x43,0x78,0x08,0x63,0x80,0x77,0x6a,0x14,0x0f,0x7f,0xaf, - 0x61,0xf4,0xdc,0x66,0xf6,0xa1,0x95,0x9e,0x6e,0x65,0xff,0x77,0x6a,0x64,0xc7,0x69, - 0xe3,0x11,0x0e,0x65,0x47,0xb5,0xf1,0xa8,0x62,0x24,0xae,0x38,0x58,0xcf,0x45,0x5c, - 0x4e,0x51,0xcb,0x27,0xde,0x2e,0xf3,0x80,0x24,0xc6,0x3a,0x81,0x85,0x71,0x40,0x95, - 0xde,0x01,0x41,0x4e,0xf6,0x92,0xa0,0x5f,0xe3,0x03,0xa8,0x94,0x6f,0x15,0x63,0x30, - 0x76,0x7a,0x51,0x05,0x58,0x33,0xc8,0xa2,0x0f,0x05,0x59,0xf8,0x86,0x4e,0x14,0x46, - 0x41,0xad,0xf4,0xad,0x32,0x03,0x30,0xa5,0x8c,0xb4,0xf0,0x06,0x22,0xbb,0xdb,0x98, - 0x3c,0x3b,0x79,0x73,0x72,0x4c,0xd9,0x67,0x78,0x1b,0x5b,0x66,0xd2,0x52,0xdd,0x67, - 0x21,0x89,0x42,0x67,0x1a,0xe1,0x26,0x7c,0xc7,0x04,0xb9,0xa0,0x35,0x09,0xc5,0x41, - 0xa0,0x8f,0x93,0xb6,0xda,0xd6,0xf7,0x41,0xb0,0xce,0x97,0x19,0xb4,0x88,0x5e,0xaa, - 0x8c,0x53,0xc3,0xd8,0x66,0xa5,0x0a,0xdb,0x34,0x76,0xa5,0x79,0x63,0x2b,0x4a,0x8e, - 0xda,0x4f,0x51,0x9c,0x95,0x72,0x94,0x43,0x24,0x2b,0xa5,0xe8,0xe2,0xea,0x9c,0xb6, - 0x30,0xca,0x42,0xf4,0x54,0x85,0x04,0x83,0xbe,0x51,0x23,0x50,0xad,0x49,0x83,0xd6, - 0xa3,0x24,0x0b,0xee,0x9c,0x32,0x2a,0x75,0xb7,0xb2,0x68,0xc6,0x84,0x9b,0x7d,0x6b, - 0xec,0x18,0x0a,0xc3,0x4c,0xe2,0xca,0x95,0x8a,0xdf,0x97,0x11,0x74,0xea,0x29,0x5a, - 0x4e,0x82,0x7e,0x67,0x77,0xeb,0xc3,0x08,0xb2,0x0e,0x5e,0x9c,0x84,0xd9,0x6a,0xeb, - 0xe9,0x3b,0xfc,0x23,0xd2,0x24,0x99,0xff,0x36,0x70,0xd9,0x75,0x80,0xf2,0xf0,0x0c, - 0xf7,0x8d,0xe8,0xf7,0x7a,0xcc,0xd8,0xde,0x04,0x0e,0xca,0x28,0x5a,0x98,0x45,0xf4, - 0x71,0x7c,0x65,0x1d,0x37,0x91,0xa1,0x8f,0x66,0x8d,0x97,0x95,0x45,0x76,0x21,0x45, - 0xc1,0x55,0x10,0x55,0xeb,0x53,0x36,0x28,0xd0,0x5c,0xd6,0xc1,0xa2,0xb5,0xc0,0xa1, - 0xe7,0x95,0x5a,0x55,0xdd,0x6a,0xb5,0xd5,0x45,0x8b,0x1c,0x0a,0x88,0x56,0x3b,0x9c, - 0xec,0x36,0xc0,0xa7,0xee,0x5a,0xee,0x26,0xa3,0xf6,0xab,0x3b,0x8c,0x1f,0xe9,0xc4, - 0x16,0xeb,0x54,0xcf,0x7b,0x75,0x85,0x5e,0xfd,0x2a,0x8a,0xca,0x23,0xe8,0x5e,0x3b, - 0x0f,0xf7,0xea,0xbd,0x12,0x55,0x1b,0x35,0xb5,0x63,0xe8,0xfd,0x7e,0x63,0xe8,0x6f, - 0x3c,0x86,0xfe,0x5e,0x17,0x06,0xd1,0xbf,0xfc,0x9d,0xc6,0xd0,0xff,0xfd,0xc6,0xb0, - 0xb3,0xf9,0x18,0x2e,0x61,0x08,0x7b,0xbf,0xd7,0x10,0x76,0x7e,0xbf,0x21,0xec,0x6e, - 0x3c,0x84,0x3d,0x1c,0x42,0x6f,0x7f,0xa3,0x31,0xa8,0xe5,0xa2,0xff,0xaa,0x5c,0x6d, - 0xf7,0xb6,0xb7,0x65,0xc6,0x2b,0x90,0x3b,0x63,0x7f,0x41,0x11,0x0b,0xe4,0x20,0xc5, - 0x3e,0x65,0xcb,0x74,0x02,0xd2,0x5a,0x46,0x95,0x75,0x64,0x01,0x8c,0x3e,0x9d,0x86, - 0x57,0x1c,0x7a,0x6b,0x76,0x3d,0xdb,0x08,0x2b,0x5b,0x60,0xc8,0x32,0x07,0x34,0x73, - 0xda,0x3a,0xce,0x26,0xc9,0xbb,0x38,0x61,0xdc,0xc0,0x00,0x5c,0x30,0x2c,0xa1,0x56, - 0xea,0x8b,0x9f,0x93,0x61,0x26,0x30,0x85,0x24,0x45,0xd7,0x0e,0x00,0x00,0xc2,0x10, - 0xe8,0x8f,0xc5,0x30,0x22,0x0c,0x15,0x00,0x40,0x08,0x9c,0x23,0xa2,0x54,0x6a,0x37, - 0x8c,0x38,0x85,0xbe,0x05,0x59,0x5b,0xa0,0x7f,0x25,0x23,0xe1,0x3a,0xf7,0x17,0x19, - 0xc6,0xde,0xbe,0x79,0xfd,0xee,0xe4,0xe8,0x03,0x03,0x12,0xc2,0x9f,0x2f,0xa2,0x30, - 0x5f,0x82,0xd8,0x6d,0x72,0x48,0x2c,0x46,0x1c,0x63,0x14,0x1a,0x16,0x1d,0x81,0xb8, - 0x35,0x69,0xe1,0x00,0x80,0x37,0xa3,0xd0,0x39,0x09,0x7e,0x8c,0x81,0xb4,0x18,0xec, - 0xa4,0x80,0x71,0xaf,0xd8,0x0a,0x07,0xc9,0xdc,0x3a,0x70,0x83,0xb6,0x01,0x3e,0xe8, - 0x09,0x7e,0x24,0xfc,0x9b,0x90,0x52,0xac,0x8d,0x9f,0x77,0xe2,0x24,0x05,0x4d,0x2a, - 0xfc,0x15,0x80,0x65,0x09,0x47,0x72,0x29,0x68,0x63,0xb4,0xb4,0xa3,0xf0,0x12,0x47, - 0xef,0xa7,0xe8,0x71,0xbe,0x4e,0xd2,0xcb,0x0c,0x87,0x0a,0x75,0x27,0xa1,0x46,0x00, - 0x98,0xf9,0xc3,0x24,0xc7,0xae,0xad,0x60,0xa8,0xac,0x39,0x0c,0x31,0xf0,0xd3,0x4f, - 0x57,0x9e,0x82,0xf6,0xe2,0xf9,0x4f,0x2f,0xdf,0xbc,0x7f,0xff,0x61,0x1b,0x7e,0x9c, - 0x9d,0x1e,0xbd,0xc3,0xf8,0x15,0x76,0x5f,0x43,0xf3,0x43,0xb9,0xfd,0x25,0x7e,0x59, - 0x86,0xa0,0x13,0xa1,0x68,0xc2,0xb8,0x46,0x0c,0x74,0xcc,0xc2,0x5c,0xfc,0x2f,0x54, - 0x8b,0xc5,0x72,0xa1,0x60,0x61,0x35,0x1c,0x43,0x1b,0xba,0x97,0xc1,0xf4,0x42,0xdf, - 0x3a,0x57,0x18,0x32,0x13,0x88,0x45,0xe0,0x5f,0x66,0x58,0xe1,0x31,0x4e,0x24,0x3c, - 0x08,0x4e,0x77,0x17,0xe3,0x7e,0xf7,0x04,0xc8,0x75,0xe6,0x59,0xd3,0xe8,0x06,0xf2, - 0x7a,0x76,0x1c,0xef,0x22,0xb9,0x06,0x1a,0x41,0x5c,0x86,0x7a,0xf3,0x15,0xc8,0x02, - 0xda,0xe3,0x88,0x4f,0x42,0x7d,0xa8,0x91,0x95,0x81,0xe6,0x74,0xc0,0x11,0x8f,0xb9, - 0x1f,0x5d,0xa2,0x57,0x43,0x93,0xe9,0xd1,0xe9,0x6b,0xc1,0xe1,0x47,0x11,0xe0,0x43, - 0x67,0x28,0x14,0x4d,0x0e,0xba,0xc1,0x7c,0x8f,0x0a,0x3b,0xe2,0x50,0x74,0x76,0xf7, - 0xda,0x42,0x21,0xe9,0x50,0xec,0x3e,0xa4,0x64,0x25,0xaa,0x0e,0xac,0xb8,0xf9,0xa2, - 0xdb,0x6b,0x5e,0xb5,0x30,0x14,0x13,0xbe,0x73,0xf2,0x52,0x7c,0xfe,0xf3,0x9f,0x31, - 0x60,0x4a,0x66,0x54,0xbc,0x12,0x4f,0x44,0x57,0x3c,0x83,0x7f,0x07,0xf0,0xfb,0xa9, - 0xe8,0xc1,0xef,0x1e,0xfe,0xe6,0x30,0x1c,0x0d,0x6f,0x3c,0x8c,0x9b,0x18,0xec,0xa6, - 0xea,0x29,0xf8,0xcd,0x7e,0x57,0x7c,0x27,0x28,0xbb,0x65,0x94,0x4c,0x7b,0xdd,0x26, - 0x27,0xba,0xf4,0x6f,0x9a,0xb2,0xc1,0x1b,0x6e,0xb0,0x2d,0x7a,0x41,0x67,0xb7,0xd5, - 0x02,0x64,0xaa,0x31,0xb4,0xc4,0xb6,0xea,0x7f,0xab,0xd0,0x5a,0x14,0xc6,0xdc,0x73, - 0xd9,0x1a,0x01,0x05,0x4c,0x37,0x7b,0x00,0xa8,0x69,0x8d,0xed,0x3b,0x8d,0x81,0x07, - 0x0e,0xdc,0x7e,0x97,0x4f,0x2a,0x30,0xd6,0x4e,0x3f,0x9c,0x9c,0x9d,0x9c,0xff,0x44, - 0x1e,0xe5,0x33,0xc0,0x05,0x07,0xf0,0xb0,0x37,0x78,0x00,0xad,0x20,0xd3,0x1b,0x88, - 0xc6,0x30,0xf8,0x35,0x0c,0xd2,0x46,0x1b,0xf8,0x21,0x3c,0xf1,0x67,0x78,0x5a,0x75, - 0x07,0xd8,0xff,0x55,0x6f,0x20,0x7a,0x6d,0xb1,0xe8,0xdd,0x0c,0xe8,0xe8,0x12,0xfe, - 0xa4,0x60,0xd8,0x2e,0xfe,0xec,0xdf,0xf0,0x51,0x39,0xfc,0x49,0x6f,0x1f,0xef,0x89, - 0x5b,0x56,0x9d,0xc9,0x15,0x3c,0x10,0xb5,0xed,0xe0,0x67,0x78,0xa8,0x6e,0x07,0x07, - 0x2c,0xdb,0xd9,0x37,0xed,0xec,0xa8,0x76,0x7a,0x5e,0xb7,0xab,0xda,0x41,0x4f,0x30, - 0x34,0x53,0xd7,0x0e,0x7e,0xc6,0x66,0xa8,0x9d,0x1e,0xb7,0xd3,0xad,0x1a,0xcf,0xe3, - 0xca,0xf1,0x74,0xf7,0x68,0x8a,0x6e,0x0f,0xee,0xd9,0x13,0x15,0xc4,0xa3,0x64,0x1c, - 0xa0,0x97,0xa9,0x49,0xe6,0x41,0x4b,0xe2,0x36,0x9c,0x88,0xe6,0x7d,0xf9,0x46,0xce, - 0x61,0xa3,0x71,0xa0,0x3f,0xd1,0x17,0x8f,0x92,0x07,0x1f,0x1e,0x1e,0x02,0x0e,0xd0, - 0xe1,0xd8,0x30,0x45,0xb3,0x41,0x03,0x26,0x94,0x4b,0xc5,0xf5,0xd5,0xd0,0x89,0x6b, - 0xd5,0xf2,0xd7,0xb4,0x20,0x71,0xa1,0xfa,0x67,0x17,0x0a,0xc7,0xe2,0xdb,0x6f,0x5d, - 0x1a,0xf9,0xa4,0xbe,0x5c,0x18,0xe8,0x0b,0xab,0x4f,0xe1,0xf8,0x40,0x82,0x51,0x5f, - 0x87,0xf4,0x55,0xd6,0x5b,0x01,0x5e,0xe5,0xaf,0x9e,0xfa,0x05,0x78,0x36,0x3f,0x57, - 0xfa,0x67,0xdf,0xbc,0xed,0xaf,0x2e,0xb4,0xd6,0x09,0x2b,0x68,0x61,0x31,0x80,0xd2, - 0x5a,0xa0,0x78,0x79,0x77,0x21,0xf4,0xba,0x44,0xf3,0x2d,0xef,0xe7,0x04,0x56,0x4f, - 0xa3,0xdd,0x68,0x71,0x1f,0x6f,0xef,0xd9,0xfd,0x6c,0x70,0x14,0x9a,0xb3,0xb4,0x03, - 0x3d,0x87,0x73,0x67,0xfe,0xe6,0x7a,0xf0,0x98,0xe2,0xd7,0xe0,0x76,0x2e,0xb1,0x6f, - 0x50,0xaf,0x49,0x8e,0x66,0x44,0x12,0x1c,0xfd,0x46,0x82,0xd1,0xf5,0xbc,0x2c,0x02, - 0x16,0x8a,0xc9,0x36,0xfa,0x2d,0x39,0xf1,0x03,0x6b,0x4a,0x70,0xa9,0xc6,0xb0,0x3c, - 0x35,0x0b,0xe9,0xb7,0x85,0xce,0x9b,0xfb,0x08,0x28,0x11,0x03,0x10,0x5f,0xc7,0xb9, - 0x06,0xd4,0x6f,0x01,0x5b,0xe9,0x12,0x83,0xe9,0xb7,0x5a,0x85,0x29,0xd1,0x7d,0x62, - 0xe2,0xe2,0x4e,0xc5,0xd4,0x6c,0x1f,0x98,0x5c,0x03,0x25,0x6e,0x43,0x58,0xdf,0xf1, - 0x6c,0xb6,0xea,0xee,0xed,0xba,0x4e,0x2f,0x8a,0x9d,0x5e,0x40,0xa7,0x5d,0xfa,0x31, - 0x3d,0xbc,0x28,0x74,0x6b,0x01,0x6d,0xff,0xf7,0xb3,0xf7,0xef,0x3c,0x1a,0x4d,0x93, - 0x7e,0x66,0x39,0x8a,0xcd,0x70,0xb2,0x6a,0x2e,0x80,0x43,0x0e,0x2c,0x74,0xaf,0xed, - 0xc7,0xb0,0xd8,0x8f,0x5f,0xa0,0x1f,0xa6,0x65,0x2f,0x43,0x95,0x81,0x28,0x61,0x0d, - 0x39,0x35,0x35,0x56,0xaf,0x34,0x32,0xbb,0xc8,0x42,0x7b,0x74,0x28,0x4e,0x63,0x15, - 0xfb,0xf0,0x8b,0x17,0x05,0xf1,0x14,0x24,0xf8,0x7d,0x68,0x7e,0xbf,0x82,0x3a,0xca, - 0xc8,0x77,0x79,0x10,0x7b,0xde,0x25,0x4f,0xfd,0xe5,0x53,0xf7,0x82,0xd9,0xd0,0x2f, - 0x9f,0x7a,0x17,0x92,0x13,0xfd,0xf2,0xa9,0x7f,0x21,0x39,0xd1,0x2f,0x9f,0x76,0x2e, - 0x24,0x27,0xfa,0xe5,0xd3,0xee,0x85,0xe4,0x44,0xbf,0x7c,0xda,0xbb,0x70,0x67,0xa9, - 0xd0,0x0b,0xcd,0xf5,0x27,0x29,0x08,0xd5,0x97,0x48,0x50,0xea,0x13,0xbe,0x9d,0x87, - 0xa3,0xf7,0xf8,0x0e,0xd8,0x61,0x16,0xa8,0x97,0x24,0xca,0xdf,0xa0,0x02,0x57,0xf8, - 0x00,0x4a,0x95,0x5d,0x1a,0xde,0x82,0x1c,0x47,0x15,0x8e,0x6c,0x45,0x50,0xfa,0x92, - 0x64,0x41,0xe1,0xb3,0x99,0x3a,0x75,0x25,0x28,0x55,0x2f,0xa8,0x49,0x80,0x3f,0x31, - 0x07,0xdd,0x32,0x9c,0x83,0x7e,0x80,0x59,0x6d,0xf1,0xcc,0x54,0x32,0x11,0xa7,0x2f, - 0x29,0x63,0xf3,0x80,0x61,0x81,0x56,0xc2,0x1d,0xc5,0x5d,0x87,0x0c,0x56,0x22,0x2a, - 0x12,0x19,0xa9,0x0c,0x14,0xf4,0x8f,0x6e,0x98,0x19,0x6a,0xb5,0xa0,0x63,0xb6,0xcd, - 0x56,0x3e,0x85,0xee,0x34,0xf0,0x70,0x44,0x1c,0x30,0x20,0x3e,0xee,0xf1,0xcb,0x32, - 0x58,0xe2,0xfe,0x88,0x18,0x06,0x60,0x8d,0x8e,0xe5,0xf1,0x29,0x8e,0x8f,0xa2,0x63, - 0x21,0x9e,0x78,0x9d,0xe3,0x33,0x2a,0x63,0x49,0x3c,0x0a,0x54,0x20,0xf0,0x24,0x9c, - 0xd2,0x51,0x2c,0x06,0x36,0x03,0xe5,0x11,0xf3,0x84,0x60,0x08,0x7c,0xd3,0xe7,0x0e, - 0x9a,0xd3,0x1a,0x7e,0xce,0x07,0xb2,0x10,0x02,0x74,0x49,0x2c,0xe3,0x8c,0x14,0x43, - 0x3a,0x4e,0x86,0x5d,0x4c,0x41,0xd9,0x56,0x07,0xda,0x00,0x1a,0x9d,0x16,0x9b,0xa8, - 0x33,0x67,0xb8,0x21,0xdd,0xc8,0xe4,0x29,0x81,0x38,0xe6,0x60,0x6b,0x58,0xc8,0x59, - 0x44,0x68,0xbc,0x9e,0xa1,0xc2,0x48,0x1b,0x37,0xfe,0x10,0x51,0xc1,0x9e,0x20,0x84, - 0xcb,0xd0,0x30,0xba,0x92,0x5a,0x46,0xf4,0xc0,0xc7,0xc0,0x93,0x73,0x85,0xcd,0xac, - 0xd4,0x5c,0x81,0x46,0x8d,0x2d,0xe8,0x27,0x4c,0xbf,0x6e,0x9e,0x78,0x52,0x0e,0x31, - 0x44,0xde,0xe6,0x84,0xa0,0xf5,0x5e,0x36,0xd5,0x92,0x72,0xd3,0x8a,0x63,0x05,0xb9, - 0x16,0xac,0xca,0x9a,0x55,0x72,0xdb,0xb0,0x74,0xee,0x2b,0xaa,0x83,0xdf,0xd4,0x03, - 0xf8,0x4b,0x6d,0xc3,0xdf,0x71,0x32,0xa2,0xc4,0xc6,0x1e,0x0f,0x49,0xad,0x1f,0x06, - 0x23,0x3b,0x98,0xa7,0xcb,0x80,0x5f,0xe0,0x90,0x30,0x4b,0x38,0xbc,0xc4,0xa5,0xc8, - 0x22,0xbe,0xc2,0x14,0x79,0xc6,0xa4,0x78,0xd8,0x6b,0x14,0xd2,0x6a,0x8b,0x66,0x6a, - 0xad,0x73,0x95,0x27,0x9b,0x44,0x44,0xa1,0xdc,0xcf,0x86,0x8d,0x00,0x82,0x31,0xe3, - 0x96,0x24,0xc9,0x11,0x06,0x88,0x61,0x7a,0x69,0x9e,0x3a,0x52,0x78,0xfd,0xc5,0x02, - 0x08,0x31,0xe2,0x11,0x27,0xb1,0x6b,0x3a,0xc8,0x63,0x87,0x0c,0xc8,0xb6,0x19,0xc8, - 0xe2,0xe8,0x28,0x43,0x00,0x8d,0x04,0x3c,0xbf,0x27,0x7c,0x52,0x97,0x99,0x76,0xaf, - 0x41,0xf3,0xbd,0xa4,0xd7,0x64,0x25,0x19,0x38,0xac,0x79,0x03,0x74,0x58,0xbb,0xea, - 0x18,0x23,0x5a,0x05,0x18,0xf5,0xe6,0xb3,0xee,0x3d,0x0d,0xe4,0x69,0xbb,0xe7,0x80, - 0xc1,0xc0,0xcf,0x09,0xcd,0x16,0x67,0xc4,0x31,0x1d,0x8a,0x9f,0x3d,0xfa,0x7b,0x68, - 0xa3,0x59,0x68,0x8e,0x70,0x1f,0x3f,0x82,0x26,0xf0,0xb3,0x27,0xd3,0x49,0x23,0x97, - 0x6b,0x24,0x93,0x49,0x43,0x95,0xb4,0xd9,0x04,0x14,0x56,0xaf,0xb3,0x55,0x3c,0x7a, - 0xee,0xa7,0x4d,0xcd,0x2b,0xf5,0xf5,0x04,0x47,0xd2,0xd2,0x8c,0xd0,0x97,0x9d,0x79, - 0x91,0xae,0x8a,0x02,0x88,0x9e,0xfe,0xe3,0xdf,0x19,0x2a,0x49,0x22,0xf5,0xc6,0x44, - 0xa2,0xea,0xa6,0x71,0x10,0x88,0x3c,0x5d,0xbd,0x9a,0x9b,0xa3,0xda,0x0e,0x90,0x40, - 0x5b,0xb7,0xf9,0xb6,0xa4,0xc9,0x96,0x62,0x89,0xcd,0xcf,0x5a,0xd1,0x48,0x97,0x71, - 0x4c,0x07,0x1a,0x10,0x2a,0xd0,0x28,0x21,0x43,0xbb,0x7c,0xd5,0x0a,0xc5,0xb3,0xb2, - 0xd5,0x05,0x98,0xf8,0x06,0x40,0x43,0x9e,0x64,0x89,0x50,0xe6,0xd3,0x05,0x4b,0x1c, - 0xec,0x70,0xcb,0x94,0x65,0xa7,0x18,0x14,0xfe,0xd9,0x93,0x3f,0xb9,0xb0,0x29,0x11, - 0xc4,0x57,0x04,0x0a,0xfe,0xda,0x70,0xcc,0x58,0x03,0x6b,0xac,0x9f,0x81,0xfb,0x0e, - 0x08,0x29,0xcd,0x00,0x8c,0x0f,0x60,0x21,0xb3,0x50,0x3f,0xcf,0xc2,0x16,0x48,0x09, - 0xa0,0x75,0x03,0x3c,0x5b,0xc0,0x48,0xd2,0xe5,0x9c,0x5a,0x50,0x0f,0x6b,0xba,0x8b, - 0x91,0x7b,0x1f,0x70,0x7b,0x72,0x40,0xe4,0x22,0xdf,0x2b,0x89,0x08,0xab,0x68,0x84, - 0xe9,0xff,0x0b,0x9d,0x33,0x54,0x7b,0x34,0x4c,0xd2,0x9c,0xf2,0xd8,0xab,0x2c,0xe5, - 0xf2,0xd4,0x20,0x1e,0x82,0x95,0x0b,0xc0,0xe2,0x80,0x72,0x6f,0x1a,0x68,0xfa,0x92, - 0x0e,0xf5,0x1b,0x40,0xa3,0x08,0x23,0x62,0x80,0x11,0x43,0x01,0xc5,0xf3,0x60,0x19, - 0xfb,0x78,0xe4,0x7a,0x2a,0x57,0x10,0xfc,0x23,0xe5,0x0d,0xaf,0xa5,0xdc,0xbf,0x64, - 0x31,0xe0,0x8f,0x2e,0x0d,0x24,0xbd,0x92,0x63,0x9c,0x4c,0x3e,0x53,0x76,0x46,0xfb, - 0xf0,0x74,0xf4,0x0c,0x80,0xec,0x88,0x8c,0xfd,0x26,0xb1,0x7f,0x15,0x4e,0xc9,0xd8, - 0x26,0x4f,0xc2,0x08,0x43,0xde,0x22,0x10,0x07,0x9e,0x45,0x56,0x01,0xae,0x16,0x50, - 0xd0,0x51,0x38,0x90,0x42,0x62,0x86,0x8c,0x8a,0x89,0xe2,0x5e,0x3b,0xdd,0xae,0xb6, - 0x37,0x0f,0xa4,0xcc,0xa6,0xee,0xfc,0x68,0xce,0x49,0x0e,0x70,0x38,0x2c,0x90,0x94, - 0x18,0x6c,0xd3,0x51,0x2c,0x94,0x8f,0xc8,0x70,0xf9,0x00,0xb4,0x3e,0x78,0x39,0xc3, - 0x33,0x84,0x61,0xae,0x72,0xd7,0x5b,0x8c,0xb2,0xbb,0x9e,0xf0,0x0d,0xc1,0x4b,0x51, - 0x60,0x51,0xb8,0x7c,0xa3,0x48,0x1a,0x08,0x93,0x09,0x12,0x7f,0x18,0xe2,0xf9,0x74, - 0x61,0xd3,0x40,0x81,0x93,0x3a,0x14,0x70,0xcc,0x9a,0x00,0x0c,0x04,0xc5,0x30,0x0a, - 0x54,0x4a,0x83,0x0e,0x1c,0x6c,0x60,0x7b,0x14,0x32,0x78,0x15,0x14,0x05,0x22,0xfa, - 0x47,0x7c,0x03,0x0a,0x51,0xc0,0xe2,0xdf,0x97,0xf2,0x87,0xe7,0x39,0xc3,0x50,0x3f, - 0xc0,0x2d,0xc8,0xfd,0x0c,0xdd,0x2b,0x2c,0xf5,0x01,0x85,0xe8,0x6b,0x07,0x88,0x0a, - 0x3f,0xb6,0xf8,0xb3,0x11,0x74,0x9f,0x64,0x14,0x4c,0xe3,0x7d,0x96,0x70,0x5a,0xb8, - 0x59,0x57,0x41,0xa0,0x54,0x6c,0x13,0x86,0xf5,0xb0,0xd5,0x51,0x16,0x3d,0x70,0xd2, - 0x28,0x70,0xf4,0x96,0x38,0xe4,0xa5,0xca,0x32,0x99,0xf8,0xad,0x94,0xaf,0xc8,0xa2, - 0xb0,0xb2,0x16,0x87,0xfe,0x78,0x7c,0x82,0x29,0x24,0xdf,0x90,0x93,0x26,0x48,0x9b, - 0x0d,0x73,0x76,0x74,0x84,0xe7,0x28,0x03,0xd0,0x18,0x1d,0x1c,0x73,0x2b,0xba,0xcb, - 0x12,0x6a,0x8b,0x53,0x69,0x10,0xef,0x2d,0xc3,0x54,0x4b,0xa7,0x04,0x4b,0x2a,0x0a, - 0xdc,0xc5,0x2a,0xa9,0x6f,0x49,0xfc,0x4d,0x1a,0x41,0xe2,0xac,0xec,0x30,0x36,0x64, - 0x35,0xc8,0x93,0x61,0x23,0xa5,0xa5,0x34,0xcc,0x13,0x52,0xdb,0x50,0xaa,0x12,0x17, - 0xa2,0xc0,0x94,0x03,0xd4,0x1c,0xe9,0x86,0x1a,0x20,0x13,0x74,0x27,0x12,0x0d,0x4d, - 0xfd,0x05,0xe8,0x5f,0xb0,0x86,0xad,0x23,0xc8,0xa8,0xbd,0x85,0x79,0x5b,0xaa,0x4b, - 0x89,0xab,0x31,0x4a,0x47,0x24,0xc6,0x99,0x27,0xa0,0x96,0x31,0x69,0xf2,0x49,0x63, - 0xa4,0x17,0x71,0x04,0xfd,0x0a,0xa3,0x25,0xb2,0x03,0xd0,0x01,0xf1,0xec,0x2d,0x32, - 0x0b,0xa9,0xac,0x26,0x05,0x4f,0x98,0x73,0x58,0x17,0x14,0xe0,0x20,0x9a,0x50,0xeb, - 0x71,0x82,0x14,0xc8,0x47,0x76,0xe5,0x2a,0x45,0x01,0x06,0xe3,0x40,0x96,0x75,0x88, - 0x7e,0x07,0x64,0x5b,0xe7,0x12,0xaf,0x4a,0x3f,0x37,0xe4,0xe4,0xaf,0x9a,0x18,0xf2, - 0xde,0x16,0xc0,0xe3,0xc6,0x88,0x33,0x18,0xbb,0xfb,0xb2,0x6d,0xe8,0x07,0x84,0xff, - 0x49,0x14,0xe0,0xcf,0xe7,0xab,0xd7,0xe3,0x66,0x83,0x0f,0x07,0x34,0x5a,0x45,0xaf, - 0xd2,0x22,0xc9,0xf2,0x26,0x9e,0x9d,0x52,0xab,0x94,0xfb,0xf3,0xe0,0x01,0x93,0xb5, - 0x46,0xaf,0xad,0x1e,0x38,0x57,0x97,0x14,0x7c,0xc0,0x6d,0xbd,0xd8,0xe7,0x41,0x3e, - 0x4b,0xd0,0xc0,0x39,0x7d,0x7f,0x76,0xde,0x50,0x22,0x84,0x4f,0xf1,0x64,0xe8,0x61, - 0x6a,0xc8,0xab,0x80,0x3a,0xe7,0x60,0x16,0x35,0xd0,0x38,0x5e,0x80,0x71,0x36,0x22, - 0x06,0xbb,0x7d,0xd3,0xb9,0xbe,0xbe,0xee,0x00,0xae,0xe6,0x9d,0x65,0x1a,0xb1,0x4b, - 0x65,0xdc,0x50,0x6e,0x1d,0x3c,0x64,0x3f,0xc6,0x4c,0x24,0xf0,0xdf,0x1a,0xae,0x93, - 0xba,0x6e,0x0d,0xbe,0x48,0x45,0xd8,0x37,0xa9,0x34,0x5e,0x9d,0x9f,0x9f,0x0a,0x74, - 0x51,0x98,0x3b,0x55,0xac,0x1a,0x77,0xa2,0xd2,0xbe,0xce,0xa8,0x45,0xd3,0xd3,0x40, - 0xa2,0x1c,0x03,0x0e,0x1a,0xc9,0x65,0xa3,0x5e,0x26,0x9a,0x9e,0x51,0x25,0x64,0xe9, - 0x54,0x71,0x60,0xd1,0xeb,0x38,0x1c,0x33,0xaf,0x67,0xa1,0xe5,0x21,0xe3,0x84,0x45, - 0x8f,0xf2,0x8b,0xa5,0x1b,0xc6,0xdb,0x07,0x79,0xba,0xf2,0xb0,0xb9,0x20,0x4d,0x1b, - 0x1b,0xf0,0x5f,0x42,0x44,0xa7,0x23,0x49,0x0e,0x39,0x9c,0xa6,0xb8,0x96,0x3d,0xd5, - 0x16,0x4b,0x54,0x7c,0xed,0x5e,0x59,0x83,0xd3,0x4e,0xc3,0x91,0xbf,0x80,0x09,0x75, - 0xd7,0xb6,0xd6,0x47,0x66,0xbf,0xbe,0xc5,0xc3,0x9d,0x3b,0x3d,0xaf,0xbf,0xd7,0xc6, - 0x27,0x1f,0x6c,0xd7,0x47,0x5d,0x74,0xd1,0x1d,0xa8,0xe9,0x64,0x55,0x10,0x69,0x02, - 0xb5,0xbd,0x6a,0xa5,0x4f,0x95,0xb5,0x0e,0xbe,0x41,0xc1,0xf3,0xca,0x1c,0x00,0x88, - 0x1c,0xf4,0xbb,0xf3,0x79,0xe9,0x24,0x99,0x7b,0x92,0xfe,0x70,0x79,0x1f,0x93,0x61, - 0x57,0xe8,0xaf,0x91,0x2a,0xef,0x68,0xf1,0xa7,0x01,0x5e,0x65,0x35,0xc0,0x23,0xd4, - 0xb0,0xf2,0x41,0xd1,0x20,0x7b,0x0d,0x43,0xe6,0x32,0xe1,0x2e,0x6f,0x95,0xc0,0x38, - 0x53,0x86,0xa8,0x01,0x45,0x07,0xa0,0x39,0xa6,0x0f,0xb7,0x25,0xa0,0x2a,0x7b,0xcd, - 0xcb,0x76,0x32,0xcc,0x23,0x1d,0x0f,0x56,0xb6,0x2c,0xe9,0x22,0x48,0x13,0x06,0xd8, - 0x75,0x1a,0xe6,0x72,0x5b,0x06,0x56,0x62,0x3e,0x23,0x35,0xc6,0x13,0xef,0xe3,0x40, - 0x89,0x3a,0x12,0x8f,0xc4,0x97,0x07,0x96,0x05,0x2b,0xaf,0xa6,0x71,0x3a,0x76,0xf4, - 0xe3,0x0b,0x99,0x6b,0xc3,0x7c,0xa5,0xee,0x78,0xae,0xc7,0xa2,0x66,0x97,0xe7,0xb7, - 0x5b,0x54,0x96,0x81,0x71,0xff,0x67,0x8f,0x7e,0x1f,0xe8,0x4f,0xc8,0x15,0x91,0x2e, - 0x4f,0x22,0xf8,0x5e,0xbb,0x0a,0xe5,0x81,0xab,0x46,0xcb,0x54,0xe4,0x4a,0x1e,0x59, - 0x43,0x50,0xf5,0x8c,0x3c,0x48,0x4d,0x82,0x8f,0x45,0x51,0xad,0x7d,0x64,0x15,0xbf, - 0x0b,0xf4,0x1f,0xfd,0xc8,0x5d,0xe4,0x66,0xc7,0xa0,0x00,0xd3,0xcb,0x93,0x97,0x78, - 0xd2,0xbe,0xd9,0xb3,0xc0,0x97,0x90,0xf7,0xf5,0x98,0xf3,0x6d,0xcc,0x09,0xed,0x80, - 0xb9,0x7f,0xdf,0xf7,0x08,0xf2,0x07,0x94,0x76,0xf3,0xe0,0xc0,0x2a,0x53,0xb2,0xbf, - 0xea,0x38,0x91,0x6a,0x8d,0xb4,0x15,0xab,0x74,0x05,0x00,0xb5,0xa6,0xad,0x66,0x30, - 0xd6,0x03,0x96,0x21,0xb9,0x66,0xa4,0x49,0xda,0xb6,0x7b,0x41,0xfb,0x05,0xa4,0x5a, - 0x82,0x79,0xa1,0x1e,0xc8,0x9d,0x86,0xfe,0x7a,0x7b,0x4c,0x74,0x40,0x03,0x8b,0xf1, - 0x2f,0x2e,0xb3,0xef,0x96,0x31,0xc6,0x07,0x92,0x8d,0x7e,0xb4,0xcb,0x60,0x14,0x0b, - 0x9b,0x5e,0xf4,0xab,0xda,0x62,0x1a,0xba,0xf8,0xac,0x1c,0x19,0x8d,0x8e,0x79,0xd7, - 0xd0,0xa3,0x1f,0x9a,0x7b,0xd1,0xb3,0x7f,0xd3,0x2e,0x94,0x0e,0x63,0x2a,0x8d,0xbb, - 0x42,0x43,0x8f,0x1e,0xc0,0xfc,0x82,0xbf,0x58,0x45,0xbf,0xf4,0x6f,0x5a,0xc5,0x7a, - 0x48,0xb6,0x08,0x14,0xff,0xb6,0xd1,0x20,0x94,0x8d,0xf2,0x2f,0x7e,0xc3,0xcd,0xf2, - 0xaf,0x62,0x7d,0x0c,0xae,0xc0,0xaf,0xf8,0xb7,0x2d,0x64,0xfa,0xa7,0x21,0x67,0x2e, - 0x2c,0x96,0x25,0xb7,0xfa,0xc0,0x76,0x6f,0x43,0x41,0xf8,0x03,0x3d,0x8d,0x96,0x39, - 0x7b,0x59,0x9d,0x2a,0xb7,0x36,0x51,0xdd,0xb6,0xee,0x95,0xde,0x57,0x53,0x95,0x52, - 0x7e,0x0f,0x1c,0x4f,0xa4,0x31,0x12,0xda,0x4a,0xd7,0x08,0xca,0xec,0x78,0x34,0x99, - 0x16,0x5c,0xc9,0xe4,0x99,0x3b,0x14,0x9f,0x1a,0x38,0xe5,0x87,0x28,0xb1,0xb1,0x90, - 0x21,0x00,0xb9,0x3f,0xd7,0xb5,0x50,0xdb,0xd0,0xd4,0x46,0xe5,0xb1,0xb8,0x7e,0xa3, - 0x97,0xeb,0x8e,0x5d,0x81,0xe9,0x4e,0x97,0xe6,0x47,0xab,0xa8,0x76,0x5a,0xe3,0x57, - 0x22,0xae,0xd2,0xbd,0x69,0x40,0x59,0x30,0xe3,0x36,0x71,0x51,0xd7,0xf9,0x02,0xbd, - 0x06,0x11,0x11,0x82,0x0f,0xe1,0xdf,0x06,0x35,0x24,0x29,0xcb,0xe2,0x1f,0xd6,0x72, - 0x2b,0xd4,0xf5,0x6f,0x2a,0xea,0xfa,0x37,0x77,0xd7,0x25,0x52,0x74,0xeb,0xe2,0x5e, - 0xa2,0xa2,0x51,0x0d,0x60,0x6f,0x0d,0x80,0x62,0xe3,0x1a,0x00,0xd0,0xf3,0xdd,0x00, - 0x90,0xb2,0x8b,0x9d,0xc7,0x77,0x16,0x76,0x6b,0x6a,0xf2,0x1a,0x28,0xd6,0xe5,0xb7, - 0x9b,0xd5,0x2e,0xa3,0x8d,0xdf,0xde,0x5d,0x1b,0xd7,0x53,0xb1,0x2e,0xbe,0xab,0x29, - 0x4e,0xeb,0xcd,0x2d,0xdf,0x94,0xab,0x50,0xd3,0x67,0x5d,0x55,0x58,0x81,0x6e,0x4d, - 0x56,0x78,0x7f,0xfc,0xf0,0xfa,0x38,0x99,0x2f,0x40,0xa9,0x89,0xf3,0xa6,0xb5,0xad, - 0x38,0xf4,0x78,0x1b,0xd1,0x86,0x87,0xba,0xdd,0x51,0x9a,0xfa,0x2b,0x2f,0xcc,0xe8, - 0x2f,0x94,0x82,0x25,0xdd,0x42,0x35,0x8f,0x7e,0xc9,0x7d,0x0a,0x97,0xf5,0xd9,0xbd, - 0x80,0x32,0xc5,0xe1,0x62,0xb5,0xf2,0x36,0x49,0x15,0xeb,0xac,0xdb,0x81,0xeb,0xef, - 0xed,0xb5,0x5c,0x16,0x62,0xf6,0xe2,0x6c,0xd9,0x54,0xf0,0x24,0x99,0xad,0x21,0xb4, - 0x4f,0xb8,0x97,0x5c,0xef,0x5b,0x5d,0x4f,0x32,0x91,0x24,0x7e,0x89,0x8e,0x0c,0x9b, - 0x83,0x4c,0x62,0xd3,0x4b,0xb3,0xe3,0x31,0xd1,0x3a,0x86,0x34,0x2f,0x6d,0x28,0x52, - 0x87,0x92,0x17,0x57,0xba,0xca,0xa1,0xe2,0x56,0x94,0xc0,0xd7,0x65,0x73,0xd4,0x3d, - 0x54,0x18,0x0e,0xbb,0x0d,0x69,0x5a,0xdd,0x2a,0x83,0x55,0xdd,0xf0,0xe3,0x5a,0x87, - 0xfc,0xf6,0x6f,0xf3,0x1f,0xc7,0xdc,0x54,0xf2,0x5f,0x62,0x52,0xee,0xfc,0x9c,0xaf, - 0xd3,0xc9,0x74,0xbe,0x05,0xa5,0x95,0x21,0x51,0xe2,0xcb,0x16,0x55,0xf5,0x28,0xe4, - 0x09,0x1d,0x03,0x32,0xdd,0x07,0xac,0x50,0xb4,0x18,0x49,0x9f,0x69,0x19,0x0f,0x7f, - 0x7e,0x87,0xde,0x57,0x6e,0x03,0xc8,0x3c,0xaf,0x83,0x4e,0xda,0xa5,0x05,0x9d,0x32, - 0xcb,0xad,0x69,0xc0,0x1c,0xc2,0xb7,0x5b,0xc0,0x5a,0x86,0xa8,0xf0,0xa9,0xa0,0x19, - 0xb2,0x42,0xfb,0x4c,0x34,0x1a,0x9a,0xa8,0x07,0x96,0x73,0xfc,0x19,0x06,0x35,0x24, - 0xd7,0xca,0xc7,0xa9,0xb7,0x0d,0xfe,0xfa,0x97,0xff,0x9b,0x71,0x4e,0x09,0xbb,0x5e, - 0xc3,0x32,0x67,0x42,0xcc,0x5f,0x38,0xa1,0x60,0x5c,0x27,0xa3,0x1a,0x5a,0x87,0x3a, - 0x84,0xa7,0x61,0x36,0xff,0xc8,0x56,0x5b,0xe3,0x47,0x7a,0xf1,0xfe,0xad,0xec,0xf5, - 0x1b,0xb0,0x86,0xc8,0x5c,0x2d,0x9b,0x42,0x72,0xb6,0x31,0xb9,0xde,0x57,0xcc,0x37, - 0x54,0x33,0xb8,0xe2,0xe7,0x8a,0x8e,0x90,0x0f,0xb7,0xb2,0x75,0xb3,0xd5,0x43,0xa8, - 0xd5,0xfa,0x2e,0xfd,0x58,0xab,0xa2,0x96,0x95,0xee,0xb6,0xc3,0xaf,0xaa,0x9d,0x12, - 0xbf,0xa7,0x63,0xc2,0x38,0x27,0x80,0xfc,0x58,0x0a,0xd0,0x30,0x94,0x08,0xb0,0xd5, - 0xf1,0x2a,0x73,0xe0,0x0b,0x9c,0x15,0xeb,0x7d,0x0b,0xca,0x9e,0x95,0x3a,0x77,0x1a, - 0x74,0x54,0x5a,0x49,0x74,0x62,0x49,0xeb,0x2f,0x4b,0xd0,0xbf,0x8e,0x47,0xcc,0x94, - 0x11,0xc9,0x3e,0xd4,0x28,0x00,0xd5,0x0c,0x9d,0x0d,0x11,0x1a,0xa6,0x9e,0x05,0x52, - 0x4e,0xf1,0x7b,0x8e,0x1a,0xc0,0xa1,0x61,0xaf,0xf5,0x14,0x5d,0x53,0x12,0x38,0x33, - 0x33,0x9a,0xb3,0x6b,0x77,0x07,0x1a,0x9f,0xca,0xbb,0xc1,0xdd,0x58,0xe7,0xf7,0x28, - 0x78,0x37,0x5c,0x19,0x71,0xeb,0x84,0x73,0x6c,0xc2,0x32,0xbe,0x90,0x0c,0xb5,0x95, - 0xea,0x98,0xa8,0x25,0xba,0x33,0x52,0x80,0xe6,0x5b,0xb1,0x02,0x47,0xe6,0xab,0xbe, - 0xde,0x6d,0xd0,0x56,0xf4,0x91,0xc2,0xe8,0x6a,0xfa,0x88,0x0b,0xc5,0x0a,0x66,0xdb, - 0x00,0x3e,0x19,0xc8,0xba,0xf3,0x5f,0x63,0x06,0x5f,0x55,0x58,0xbc,0x95,0xae,0xc2, - 0xc2,0x96,0xb1,0x71,0x37,0xa9,0xcf,0xb6,0xcb,0xb3,0xe2,0xaa,0x65,0xe9,0x8d,0x2e, - 0xfa,0x45,0x0d,0xc6,0xb1,0x87,0x84,0xf5,0x2b,0xa4,0xb5,0xb6,0xe8,0xed,0x75,0x37, - 0x45,0xb8,0xca,0xd8,0xf1,0xc5,0x54,0xc1,0x81,0x5c,0x68,0xb7,0xa4,0xf3,0x66,0x83, - 0x8f,0xa2,0x70,0x50,0x01,0x9a,0x05,0x6d,0xeb,0x50,0x08,0xd2,0xb5,0x15,0x02,0x99, - 0x27,0xda,0x5b,0xf4,0x0c,0xd4,0x18,0x67,0xaf,0x9b,0x10,0x88,0x5b,0x12,0x4d,0x5c, - 0xa0,0xed,0x4a,0xdf,0xcb,0x36,0x75,0x19,0x59,0x5a,0x81,0x91,0xad,0xf7,0x7f,0x7e, - 0x8d,0x07,0x14,0x7d,0x65,0xcc,0xf1,0x3c,0xf6,0x8a,0x37,0xad,0x55,0x57,0x63,0xf2, - 0xe1,0xea,0x66,0x6c,0xb0,0xd3,0x6c,0xb3,0x75,0x6d,0xed,0x1c,0xf1,0x5f,0x67,0x79, - 0xe1,0x4b,0xbc,0x82,0xfa,0x80,0x72,0x7e,0xa9,0x5b,0xa8,0xd5,0x0f,0xe0,0x6f,0xd5, - 0x97,0x29,0x93,0x4b,0xd0,0xbd,0x50,0x59,0x86,0xae,0xa2,0xc3,0x6c,0x9e,0x8c,0x97, - 0x11,0x07,0xb6,0xf2,0x1e,0x80,0xf7,0x9d,0x4c,0x3a,0xcb,0x68,0x1f,0xf0,0x74,0x32, - 0xe3,0x04,0x72,0xcc,0x28,0xbf,0xa6,0x8e,0xdc,0xf5,0xd3,0x14,0x43,0xa4,0x11,0x9a, - 0x3a,0x2a,0x54,0xf2,0x8d,0x36,0x4b,0x71,0xd5,0x32,0x3d,0xb1,0x4a,0x0e,0x8d,0x09, - 0x33,0xe1,0x61,0x11,0x06,0x99,0x0e,0x86,0x25,0xe8,0xd0,0xf2,0x10,0x10,0x3f,0xe7, - 0x58,0xe4,0x6b,0x5f,0xa6,0x3b,0x55,0x80,0xe4,0x30,0x10,0x14,0x0c,0x23,0x88,0xd0, - 0xc5,0x40,0xfb,0x64,0x19,0x66,0xb6,0x89,0xf1,0xe6,0x18,0x9f,0x4f,0x13,0x53,0x06, - 0xc8,0xe7,0xef,0xff,0x11,0x83,0x91,0xaf,0x63,0x15,0x96,0x40,0xae,0x55,0xb5,0x73, - 0x87,0x90,0x9a,0x94,0x99,0x0f,0x57,0xed,0x8c,0xf6,0x61,0x41,0x0c,0xc4,0x23,0xec, - 0x55,0xa8,0xf4,0x0b,0xe8,0x40,0xd2,0x96,0x19,0x3f,0x65,0x39,0x72,0x61,0xca,0x51, - 0x73,0x96,0xc8,0x45,0xd6,0xa2,0x28,0x71,0xde,0x8f,0x53,0x69,0x12,0x04,0xc5,0x7e, - 0x71,0x7c,0x0c,0xd2,0x7a,0xa6,0xb3,0xc2,0x75,0x28,0x55,0x21,0xd7,0x8f,0x61,0x61, - 0xd8,0xef,0x79,0x27,0xbc,0x83,0xe0,0xe8,0x2c,0xac,0x27,0x8e,0x97,0xb4,0x1f,0x38, - 0x44,0xea,0xc9,0x93,0xd2,0x61,0x64,0x41,0xfb,0x23,0x57,0x32,0xb5,0xac,0x1f,0xfb, - 0xd1,0x2a,0x43,0x29,0x87,0xf3,0x86,0xe4,0x80,0x81,0x32,0x00,0x0c,0xf3,0x03,0xb2, - 0x53,0x58,0x47,0x9b,0xe3,0x57,0x8a,0x49,0xa6,0x8c,0x7e,0x8b,0x04,0x8b,0x64,0xec, - 0x88,0xe5,0x6d,0x20,0x64,0x5f,0xb1,0xcc,0x4f,0x0b,0x88,0xa5,0xd0,0x2d,0x40,0xad, - 0x77,0xef,0x5e,0x63,0x89,0x19,0x64,0xf3,0x34,0x1c,0xe5,0xa0,0x7e,0xdd,0xa3,0xe0, - 0x73,0x71,0x04,0x08,0x2a,0x12,0xc3,0x01,0x85,0xe2,0x9b,0xe4,0xe0,0x18,0x57,0xf4, - 0xb7,0x6c,0x06,0xdc,0x61,0x24,0xc8,0x91,0xbe,0xfa,0xd3,0x4f,0x2f,0x4e,0x5e,0x1e, - 0xfd,0xf8,0xe6,0x1c,0xc3,0x87,0x3f,0x7d,0xda,0xc7,0x28,0xbc,0x3d,0x0c,0x5f,0xfb, - 0x04,0x7f,0xf0,0x3a,0x83,0x2e,0x3f,0xc0,0xdf,0x36,0x5e,0x96,0x45,0x4f,0x7b,0xf4, - 0xd4,0xdb,0xc7,0xc7,0x8b,0x03,0x09,0xea,0xcd,0x8f,0xe7,0x3f,0x9d,0xbe,0x7f,0xfd, - 0x8e,0x20,0xed,0xec,0x68,0x64,0x72,0xfc,0xa0,0x8a,0x4e,0x36,0xb1,0xc9,0xb4,0x5f, - 0x00,0xbc,0xef,0x4c,0x85,0x23,0x4b,0xb7,0xd6,0x7f,0x5e,0xd4,0x32,0xeb,0x77,0x26, - 0x6a,0x59,0xf7,0x40,0x05,0x2a,0xdf,0xd1,0x83,0xdf,0x1e,0xcf,0xcc,0x3d,0x30,0xf1, - 0xcc,0xaa,0x07,0x2f,0x55,0x08,0xf3,0xfa,0x1e,0xfc,0x0e,0x91,0xce,0xdc,0x03,0x0c, - 0x11,0x75,0x7b,0x80,0xb9,0x2d,0x2a,0x7b,0xa0,0x82,0x48,0xb9,0x07,0x53,0x59,0x2c, - 0x1e,0x88,0xbe,0x46,0x28,0x96,0x70,0x11,0xaa,0xea,0xac,0x07,0x66,0xc5,0xa7,0xee, - 0x28,0x60,0x18,0x5b,0xeb,0xf6,0xec,0x88,0x23,0x6f,0xcb,0xc0,0x7c,0xf5,0xc1,0x0e, - 0xca,0xc5,0xcc,0x92,0x9a,0xf4,0xa4,0x13,0x03,0x48,0x0f,0xfd,0x18,0x87,0x4f,0x4d, - 0x00,0x6e,0xd7,0x0a,0xc0,0x05,0x44,0xba,0x67,0x08,0x50,0xdb,0xb3,0x00,0xbc,0xfa, - 0xb5,0x02,0xc0,0xf1,0xd1,0xe9,0x99,0xf2,0x28,0x6b,0x48,0xea,0xa5,0x7f,0x53,0x00, - 0x69,0x4a,0x23,0x6c,0x09,0x1c,0x5f,0x02,0xe4,0x23,0x0f,0xb7,0xde,0x48,0x38,0x5a, - 0x91,0xcb,0xa4,0x60,0xe0,0xb9,0xcd,0x26,0x25,0xc6,0x67,0x3d,0x80,0xeb,0x7d,0xb2, - 0x1d,0xd9,0x17,0x00,0xc1,0x5a,0xc4,0x9f,0xa8,0xb4,0xf8,0x07,0xfb,0x9d,0xf4,0x19, - 0x91,0xeb,0xd3,0xf1,0x91,0xdb,0x70,0xd8,0xf2,0x61,0x2f,0xb4,0x1e,0x0f,0x01,0x6b, - 0x8b,0x9d,0x96,0xf6,0x48,0x73,0x24,0xc9,0x3d,0xcb,0x53,0xde,0xd5,0xee,0xf1,0x5e, - 0x5b,0x7a,0xc1,0x7b,0xd2,0xa7,0x23,0x9d,0xe0,0x98,0xd1,0xc0,0x38,0xc0,0xf1,0xb4, - 0xb5,0xdc,0x04,0xe4,0xf9,0x44,0x5d,0xd0,0x75,0x5c,0xdf,0xd2,0x45,0x48,0x1a,0x19, - 0x3a,0xda,0x8e,0xd0,0x01,0x62,0x10,0x5b,0x2c,0xe0,0x84,0x64,0x25,0x70,0x18,0xe1, - 0x79,0x5e,0x19,0x79,0x6d,0x7c,0x8d,0x35,0x71,0x2e,0x3e,0xdf,0xb6,0xd8,0xf1,0x8d, - 0x75,0x78,0x52,0xa0,0xa6,0x9c,0xe9,0xa6,0x79,0x49,0xd7,0x69,0xf1,0x83,0x7f,0x53, - 0x51,0xc2,0xbf,0x21,0xdd,0x06,0xb5,0x32,0x0b,0xd2,0x53,0xab,0x52,0x4b,0x7c,0x32, - 0x5f,0xda,0xd6,0x07,0x9c,0xb4,0x4f,0xe6,0xb1,0x6d,0x75,0xe5,0xa2,0x08,0x13,0xda, - 0xee,0xd8,0x5d,0x7d,0x42,0xb1,0xcc,0x4e,0xcf,0x2a,0xe9,0xcf,0xaa,0xf2,0x00,0xab, - 0xe8,0x11,0xd3,0xbc,0xa9,0xf1,0x74,0x7b,0x4d,0xf3,0x52,0x8d,0x98,0xe6,0xb3,0xa2, - 0x44,0x71,0xc4,0x5c,0xae,0x63,0x83,0x7d,0x82,0x6c,0xa6,0xdf,0x72,0xe1,0xd8,0x2b, - 0xcd,0x2a,0xfb,0x80,0xcb,0xea,0x8e,0x31,0xbd,0x14,0xdb,0xe5,0xb7,0xaa,0x6b,0x4c, - 0x45,0x55,0x65,0x8a,0x9d,0x93,0x25,0x3b,0x0e,0x6c,0xea,0xde,0x5e,0xab,0x00,0xab, - 0xd4,0x3f,0x59,0xfa,0x01,0x97,0x3e,0x60,0x4f,0xe0,0xcb,0x64,0x99,0xf2,0xce,0x14, - 0xe8,0xf5,0xf8,0x1b,0x17,0x0b,0xfc,0x46,0xb5,0x8d,0xd2,0xc4,0xcb,0xab,0x69,0x28, - 0xd0,0x73,0x20,0xb5,0x37,0x50,0xdb,0xe8,0x20,0x1e,0x9d,0x9d,0x0d,0x3d,0x86,0x74, - 0x44,0x91,0x72,0x98,0xac,0x3b,0x9c,0xc6,0x68,0xca,0xf0,0xb1,0xaa,0x80,0x2f,0xab, - 0x40,0x55,0x25,0x18,0xab,0x0b,0x27,0x30,0x93,0x2f,0x66,0x76,0xe1,0x8c,0xc6,0x94, - 0x5a,0xfc,0x66,0x11,0xa1,0xdb,0x5d,0xa1,0x8d,0x40,0x1f,0xf2,0x92,0xd0,0xb8,0x64, - 0x8f,0xf5,0xa1,0xf3,0x60,0x45,0x87,0xd2,0x6b,0xb2,0x66,0xac,0x43,0x09,0x5d,0xcf, - 0x3e,0x96,0xb0,0xab,0x59,0x98,0x2e,0x4c,0xac,0x4c,0xee,0x50,0x68,0x3c,0xb3,0x9d, - 0xf4,0xed,0xb7,0xc4,0x95,0x93,0x89,0xb0,0x5e,0x72,0x98,0xe9,0x10,0x37,0xe1,0x1b, - 0x2d,0xfb,0x83,0x09,0x8c,0xd1,0x60,0x60,0xfd,0x23,0x90,0xe6,0xfd,0x82,0xfb,0x5b, - 0x7e,0xa3,0xa6,0xd5,0x83,0x0a,0xd6,0x7f,0x82,0x87,0x23,0xf4,0x5b,0x0b,0xaa,0xe4, - 0x72,0xf8,0xa5,0x86,0x95,0xf0,0x86,0x15,0xb2,0x04,0x9b,0x8b,0x64,0xa0,0xec,0x13, - 0xda,0xdc,0x4e,0x20,0xe3,0x80,0xbe,0xc1,0x1f,0xde,0x32,0x6a,0x89,0x67,0xe6,0x41, - 0x60,0xe0,0xdc,0x81,0xc3,0x88,0x68,0x9b,0x8b,0xde,0x61,0x98,0x50,0x33,0x02,0x5b, - 0x2a,0xe4,0x20,0xab,0x10,0xfa,0xbc,0x0b,0x7f,0x1e,0x3c,0xe0,0x7e,0x4b,0xaf,0xbd, - 0xcb,0xe1,0xb8,0x17,0x9f,0xc2,0x0b,0xdc,0x86,0x6a,0x95,0xb9,0x36,0xed,0xd6,0xda, - 0x9d,0xc2,0x37,0x84,0xa0,0x46,0x29,0x9d,0xaf,0xf4,0x8e,0x59,0x5b,0xb8,0x76,0x45, - 0xfd,0xba,0xe5,0xee,0xe6,0x82,0x15,0x32,0x56,0x11,0x7d,0x6f,0xb3,0x42,0x1d,0xe7, - 0x1b,0x55,0xdc,0xd9,0xe1,0x6a,0xce,0xd6,0xae,0x5d,0x47,0x7f,0x90,0x5b,0x75,0x6a, - 0x9f,0xd8,0x22,0x3d,0xd4,0xda,0x34,0xed,0x81,0x02,0xa3,0xa9,0xcf,0x06,0x43,0xd5, - 0x5a,0x6a,0x57,0xb9,0x25,0xa1,0xf1,0x9a,0xa4,0x9f,0x8a,0xcd,0xcb,0x0a,0x73,0x3f, - 0x5e,0xfa,0xd1,0x09,0x1d,0x65,0x7d,0x46,0x49,0xca,0xf5,0xf3,0xa0,0x54,0x00,0xc3, - 0x8b,0x3f,0xd3,0xe6,0x28,0xcb,0x1f,0x4b,0xdb,0x27,0xa2,0x05,0x9a,0xc3,0x55,0xfe, - 0xf7,0xa3,0xe1,0x9b,0xb3,0x6c,0x30,0x55,0x64,0x7b,0xf1,0xb9,0xb0,0xb6,0x58,0x32, - 0xd1,0x2f,0x2d,0x3e,0xba,0xd4,0xab,0xba,0x70,0xba,0x6d,0xa9,0xde,0xd7,0x1d,0x6d, - 0xd3,0x49,0xc3,0x81,0xfa,0xd7,0x1d,0x72,0xb2,0x37,0x97,0xf8,0xfc,0x9b,0x7b,0xbe, - 0x89,0x21,0x5c,0xda,0xfc,0x38,0x06,0xf6,0xdd,0x93,0x35,0x81,0xa6,0x93,0xb4,0xb9, - 0x14,0xdf,0x89,0x58,0xd5,0x90,0x3d,0xbc,0x14,0x60,0xff,0x52,0x51,0x1d,0xbe,0x79, - 0xf7,0x99,0x3a,0x82,0x99,0x41,0x1b,0xf4,0xe3,0xf4,0x35,0xc0,0x35,0x28,0xb8,0xe3, - 0x8c,0x1d,0x46,0xc0,0x2e,0x87,0xe1,0x88,0xc3,0x8e,0x51,0x97,0x5c,0x81,0x48,0x06, - 0xd6,0xdc,0x24,0x9d,0x1c,0x64,0xd5,0x0d,0x6e,0x66,0xe0,0xc5,0x3b,0x09,0xb0,0x71, - 0x14,0x10,0x7c,0xf8,0x83,0x0e,0x09,0x2b,0x56,0x7e,0x93,0x29,0x60,0x74,0x33,0x11, - 0xf0,0xe2,0x4f,0xdd,0x36,0x1e,0x17,0x22,0x77,0x6e,0x36,0x43,0xd7,0xc2,0x30,0xcc, - 0x64,0xd4,0x6c,0x1a,0xa8,0xd8,0x48,0x8e,0xa7,0x8e,0x57,0x62,0xe9,0x59,0x78,0xfb, - 0x0c,0x7d,0xe0,0xf8,0xc3,0x15,0xea,0xbb,0x6c,0x97,0x90,0x3d,0x40,0x96,0x00,0xd9, - 0x00,0x40,0xe2,0x87,0x4c,0xcc,0x36,0xc6,0x87,0x28,0x02,0xb1,0xbf,0xa0,0xde,0xee, - 0x00,0x1a,0x9a,0x3d,0x8c,0x70,0x6a,0x39,0xbf,0x72,0xf8,0x17,0xe0,0x81,0x44,0xdc, - 0x29,0xbd,0xa7,0x6f,0x7d,0xfc,0xa6,0x9e,0x72,0x07,0xfc,0xca,0x80,0xaf,0x02,0x6d, - 0x7e,0xc1,0x00,0x1e,0xdc,0xd1,0x83,0xd5,0x9a,0x1e,0xac,0xec,0x1e,0x20,0xb8,0x1e, - 0x77,0x03,0xd9,0x6f,0x94,0x30,0x6e,0x66,0xc8,0x87,0xe5,0xfb,0x2a,0xde,0xdc,0x57, - 0xcc,0x59,0x79,0xdd,0x78,0x0c,0xf3,0x10,0x85,0x42,0x13,0xa0,0x3c,0x10,0x18,0x7f, - 0xbf,0x2d,0xfa,0x76,0x90,0xe1,0xf0,0xa6,0x09,0x25,0x5a,0x50,0x1f,0x16,0x16,0x35, - 0x35,0xc7,0x1b,0x0d,0x02,0x50,0x95,0xb9,0xc5,0xb9,0x3a,0x56,0xe9,0x9c,0xeb,0xd2, - 0x2a,0xcc,0xaa,0xe9,0x80,0x6e,0x69,0x32,0x36,0x4b,0xd0,0x16,0x62,0x9c,0x83,0x8e, - 0x17,0x34,0xfb,0x2a,0xe5,0x7a,0x2e,0x9e,0x29,0x36,0x6b,0x1b,0x8c,0x75,0x80,0x6c, - 0x04,0xb9,0x0b,0x10,0xdf,0x13,0xb8,0x3f,0xa2,0x4f,0xb9,0x12,0xa6,0x25,0xbc,0x9f, - 0x59,0xfc,0xc4,0xbc,0xa6,0x0a,0x83,0xca,0xbe,0x15,0x1b,0xbb,0x0c,0xde,0x2c,0x73, - 0xfb,0x54,0x2d,0x23,0x99,0x45,0x78,0xbd,0xdc,0x34,0xfe,0x05,0x67,0x8e,0x50,0x1f, - 0x20,0x21,0x2a,0xe5,0x45,0x89,0xd9,0x85,0xc8,0x1e,0x2c,0xe7,0x04,0xf2,0x09,0x13, - 0xee,0xb0,0xdb,0x2a,0xa1,0x1b,0x20,0x16,0x04,0x00,0xba,0x55,0x83,0xbf,0x63,0x6f, - 0x0f,0x89,0x03,0x44,0xe7,0x68,0x82,0x41,0xca,0x45,0x2d,0x08,0xd5,0x26,0x40,0x02, - 0x16,0xc0,0x5b,0x55,0xe9,0x00,0x18,0x3e,0xf0,0xe9,0x38,0xb4,0xaa,0xee,0x3c,0x39, - 0xd0,0xbe,0xf3,0xf8,0x01,0x4a,0x55,0x5a,0x8a,0x7e,0x96,0x9f,0xd0,0x71,0x1f,0xde, - 0x04,0x20,0x0e,0x38,0xe7,0xcd,0x2b,0xcc,0xeb,0x8d,0xf7,0x97,0x74,0xf8,0x52,0x13, - 0x68,0xa9,0xad,0x8f,0xd7,0xa1,0x0e,0xf1,0xd7,0x7f,0xf9,0x57,0x96,0xd8,0x14,0xc4, - 0x99,0x4c,0x26,0xdc,0x67,0xff,0x2a,0x70,0x03,0xae,0x0d,0xb5,0x2d,0x30,0x28,0x3c, - 0xcb,0xa5,0xdf,0xdf,0xd9,0xc6,0xd0,0xd5,0x68,0xfe,0x6d,0x20,0xd6,0x16,0x46,0x93, - 0xf8,0xd6,0x91,0x67,0x82,0x9c,0x28,0xb2,0x49,0x6d,0x51,0xd8,0x84,0x4d,0x97,0x21, - 0xdb,0x94,0x6f,0x51,0xb8,0xa3,0xfb,0x6a,0x0e,0x6c,0x69,0xae,0xee,0xaa,0x28,0x90, - 0x1f,0x26,0x5e,0xe7,0x33,0x88,0xd3,0x20,0x99,0x63,0xbc,0xb0,0xf8,0xdb,0xf6,0x2a, - 0xfe,0x01,0xb9,0x25,0x72,0x44,0xc0,0x5d,0xdd,0xce,0x4d,0x38,0xd6,0x5e,0x16,0x1c, - 0x9a,0xbc,0x55,0xef,0x50,0xfc,0xa1,0xd9,0xa0,0xdb,0xa4,0xcc,0xd7,0x51,0x8e,0x12, - 0xca,0x14,0x42,0x48,0xb4,0x8b,0x75,0x93,0x37,0x1b,0xfd,0xb1,0x29,0xf9,0x96,0x69, - 0x75,0x20,0x76,0x41,0xdd,0x48,0x07,0x62,0xbf,0x2d,0xf0,0x1a,0x69,0xd0,0x43,0x86, - 0xf0,0xae,0xab,0x28,0xf0,0xf4,0x23,0x0b,0x83,0xd3,0x57,0xcc,0x5c,0x38,0xcc,0x97, - 0x8e,0x78,0xa6,0x81,0x0f,0xf3,0x96,0x65,0x62,0x71,0x63,0xcd,0x2c,0x1e,0x02,0x3d, - 0x85,0xef,0x4d,0x9b,0x5d,0xd1,0x3d,0x5f,0xba,0xbb,0x98,0x34,0x9a,0xb7,0x37,0xa5, - 0xe3,0x28,0xcb,0xb0,0x15,0x2c,0xe4,0x8d,0xa2,0x10,0x06,0xfd,0x11,0x1d,0xf4,0x4e, - 0x01,0x6c,0x7e,0x97,0x8f,0xf5,0xf0,0xbb,0xf1,0x22,0x35,0x8e,0x67,0x8e,0x60,0x38, - 0x05,0x26,0x15,0x7d,0xc0,0x2d,0x1c,0xb2,0xbd,0xe8,0x56,0x56,0x83,0x08,0xe5,0xf4, - 0xb7,0x55,0x2b,0x6c,0xf8,0x3b,0x04,0xd5,0x2a,0x14,0xd6,0xae,0x7f,0xb7,0xf4,0xab, - 0xea,0xd2,0x94,0x9e,0x5f,0x37,0x40,0x50,0x1f,0x80,0x7d,0x71,0xd3,0xa8,0x2c,0xa7, - 0x61,0x13,0x40,0xab,0x20,0xcc,0x9d,0x87,0xcb,0x29,0xf5,0xe3,0x0c,0xb7,0xdb,0x9b, - 0xd0,0x54,0x1b,0x91,0x0f,0xff,0xe8,0x9f,0xd4,0x36,0xcd,0x0a,0xb5,0xd3,0x11,0x6f, - 0xbd,0x88,0xfe,0x4b,0xb7,0xad,0xd2,0x34,0x11,0x5c,0x7c,0x95,0xd3,0x7f,0x87,0xb4, - 0x42,0x18,0x6b,0x2f,0x4e,0x8e,0x8f,0x5e,0x50,0xf6,0x0b,0x2b,0x59,0x87,0xf1,0x84, - 0x00,0xf7,0xb7,0xbc,0x6f,0x8a,0x54,0x6e,0xde,0x4f,0x90,0x46,0x67,0xbf,0xb2,0x57, - 0x0f,0xda,0x7b,0x80,0x3d,0x00,0xcd,0xc2,0x02,0xa2,0x3c,0x3e,0x58,0xca,0x81,0x82, - 0xf9,0x3d,0xb8,0x55,0x0d,0x70,0xf6,0x2b,0x43,0xbc,0x21,0x80,0xaa,0xa6,0xa9,0xa3, - 0x72,0x89,0xa8,0x5c,0x1f,0xaa,0xd7,0xd0,0x62,0xf3,0x86,0x87,0x8c,0x60,0x4f,0x3f, - 0xb6,0x8c,0xf7,0x71,0xc5,0x20,0xa5,0xe7,0x11,0x86,0xfe,0x80,0x15,0x1f,0x13,0x9b, - 0x85,0x1a,0xd0,0xe9,0x2b,0x55,0xfe,0x8a,0xcb,0xaf,0x4c,0x17,0xa0,0x10,0x56,0x68, - 0xae,0x18,0x77,0xd4,0xc2,0xab,0x16,0x6f,0x65,0xbc,0x06,0xfe,0x9c,0xd2,0xb5,0x4b, - 0xe8,0xbd,0xc8,0x78,0x03,0x45,0x6d,0xbb,0x81,0x91,0x47,0x3b,0x33,0x7c,0x1e,0x95, - 0xf7,0x9e,0x48,0xdb,0xc5,0x11,0xb5,0xd5,0xd9,0x1b,0x4a,0xee,0x82,0x79,0x68,0x10, - 0x1e,0xc6,0x50,0xa8,0x23,0x5f,0x4e,0xde,0xa1,0x46,0x26,0xfa,0x94,0x90,0xaa,0x7b, - 0xa9,0xf7,0x53,0xad,0x33,0x01,0x99,0xd8,0xe9,0xc1,0xd7,0x47,0x97,0x9e,0x1c,0xc6, - 0xf9,0xeb,0xe3,0x1f,0x78,0x37,0x02,0x77,0x20,0x1a,0x7b,0xdd,0x06,0xee,0x39,0xf4, - 0x70,0xcb,0xa1,0x01,0xff,0x6d,0xc8,0xfd,0x08,0x78,0xea,0xcb,0xa7,0xbd,0x2e,0x17, - 0xc4,0x27,0xa0,0x18,0x2c,0x4b,0x85,0x2f,0x1b,0x66,0xef,0xa2,0xd1,0xbf,0x6c,0x98, - 0xbd,0x8b,0xc6,0xde,0xa5,0x82,0x2a,0xe1,0xc2,0x33,0x66,0xa9,0xf0,0x26,0x61,0x04, - 0xc6,0x6d,0xb3,0xf9,0x69,0xf6,0xeb,0x05,0x21,0x72,0xf6,0xab,0x78,0x2a,0x9c,0x89, - 0xec,0x61,0x32,0x1e,0x30,0x31,0xe1,0xcb,0x13,0x61,0x51,0xda,0x77,0xe8,0xdc,0x7c, - 0xa8,0xa7,0xef,0xc5,0xf3,0x9f,0x30,0xd5,0x11,0x0f,0x05,0x14,0xf2,0x06,0x77,0x16, - 0x33,0x67,0xc2,0x43,0xa7,0xf7,0x48,0x3e,0xd2,0xd3,0xce,0x9e,0x7c,0xea,0xd3,0xe3, - 0xde,0x0e,0x3f,0xe2,0xef,0x87,0x50,0xef,0x82,0xe7,0xcc,0xbd,0x7b,0xb4,0x70,0x88, - 0x99,0x0f,0xd5,0x1f,0x9f,0x9d,0x99,0xeb,0x46,0xdb,0xf2,0x88,0x3a,0x5d,0x3a,0x8a, - 0x47,0x12,0xf9,0x7c,0x3b,0xcc,0x1f,0xed,0xf2,0x91,0xab,0x4a,0xdf,0x73,0x9a,0x06, - 0xf2,0xf0,0xfb,0x28,0x49,0x65,0xae,0x1e,0xbe,0x8b,0x55,0x5f,0x9b,0x41,0x67,0x21, - 0xd4,0x99,0x4f,0xbc,0x92,0x14,0xa1,0x60,0x06,0x80,0x94,0x58,0xfc,0x81,0x1d,0x26, - 0xa7,0xb6,0x48,0x91,0x05,0x66,0x86,0x5a,0x78,0xd7,0x16,0xad,0x1d,0x3a,0x76,0x81, - 0xd7,0xb1,0xf2,0x25,0xba,0x9e,0x25,0x49,0xb1,0x57,0x0e,0xb3,0x45,0xc1,0x40,0x5c, - 0x7f,0xbe,0x40,0x5f,0xd6,0x19,0x32,0x1d,0x13,0x24,0x41,0x27,0x9f,0x0c,0x1b,0x45, - 0xdd,0xa2,0x89,0x67,0x37,0xdb,0xb4,0xd3,0x32,0x24,0x9f,0x01,0x5a,0x1f,0x24,0x39, - 0x4e,0x81,0xee,0x82,0x34,0x5f,0xb1,0xae,0x8b,0xc5,0xc8,0x0c,0xd5,0x25,0xbd,0x3c, - 0x0d,0xe7,0x4d,0x0b,0x5c,0x44,0x1e,0xa1,0xab,0x66,0x83,0xae,0xd6,0xc5,0x2d,0xef, - 0x6f,0x82,0x47,0x7b,0x7b,0xfd,0xc0,0xe6,0xf7,0x73,0x28,0xb2,0xfd,0x3f,0xbf,0x79, - 0xd6,0xfc,0xd4,0xed,0x3c,0xf6,0x3b,0x93,0x8b,0xcf,0xfd,0xdb,0x56,0xfd,0xc3,0x1f, - 0xb6,0x43,0x2f,0xb8,0x09,0x46,0x4d,0x3c,0x78,0x0a,0x78,0x8f,0xfc,0x51,0xd0,0x6c, - 0x7c,0x83,0xc0,0x65,0x64,0xa4,0x6e,0xfb,0xc3,0x14,0xbd,0x7b,0x73,0x50,0xbb,0xff, - 0xf9,0x0f,0x9f,0x4d,0xa2,0x10,0x4a,0x2c,0xd1,0xdb,0x6f,0xdd,0xb6,0x85,0xf3,0xba, - 0x5f,0xfd,0x7a,0x47,0xbe,0xfe,0x67,0x8c,0x4b,0xeb,0xef,0x80,0x48,0x7c,0x04,0x74, - 0xb6,0xbb,0xdf,0x28,0xb9,0x97,0xe4,0xa5,0xb2,0x34,0x5e,0xfa,0x4d,0x23,0x9e,0x0c, - 0x27,0x0f,0x83,0x49,0x43,0x7a,0x5c,0xe4,0xcd,0xc6,0x54,0x86,0x7e,0x73,0x99,0xdd, - 0x60,0x12,0xec,0xab,0x32,0x74,0xa7,0x31,0x95,0x80,0x5f,0xf4,0xbd,0xb7,0x8b,0xff, - 0x57,0xdf,0xe5,0x86,0x02,0x95,0xe0,0xa8,0x5b,0x2c,0xb3,0x3f,0xdc,0xdf,0xdb,0xf3, - 0x55,0x19,0x79,0xe3,0x31,0xf7,0x05,0x7f,0x53,0x19,0x7f,0xff,0xf1,0xe4,0x71,0x57, - 0x95,0xe1,0x1b,0x90,0xa9,0x08,0xfe,0xa4,0x12,0xe3,0xc7,0xe3,0xde,0x48,0x97,0x88, - 0x54,0x04,0x3d,0xfc,0x38,0x1a,0xd0,0x41,0x0c,0xa0,0x86,0x7f,0xa6,0x6b,0xdb,0xff, - 0xf0,0x99,0x51,0x4c,0xf8,0xf2,0x6f,0x5b,0xff,0x6c,0x80,0x96,0x8b,0x9a,0x84,0x22, - 0x95,0xad,0x95,0x26,0xd2,0x4e,0x6c,0x52,0x31,0x2b,0x5f,0x06,0x05,0xa6,0x6c,0xf7, - 0xb7,0x43,0xd9,0x6d,0x8b,0x7d,0x0b,0x8a,0x19,0x31,0x4c,0xd2,0x5d,0x03,0x2e,0xce, - 0xe3,0x57,0x8d,0x77,0x53,0x20,0x6b,0x87,0xbb,0x29,0x90,0x8a,0xd1,0xda,0xee,0x3d, - 0x64,0x6e,0x32,0x7a,0x41,0x46,0x5b,0x50,0x8a,0x3a,0xbe,0xc5,0x34,0xc6,0xeb,0x47, - 0x55,0xea,0x13,0x99,0x66,0xbd,0xad,0xee,0x97,0x1e,0x62,0x2a,0x06,0x74,0x9c,0x22, - 0x18,0x30,0x53,0x82,0x08,0x6f,0x58,0x65,0x27,0x8f,0xaf,0x6f,0xd1,0xa1,0x24,0xf0, - 0x9e,0x6b,0x67,0x7f,0xa4,0xf6,0x9a,0x75,0x1b,0x58,0xfa,0x58,0x02,0x6f,0xe4,0xe9, - 0xdd,0x20,0xe7,0xa4,0x84,0xeb,0x73,0x0b,0xc8,0x98,0xe2,0x13,0xe1,0x94,0x5f,0x00, - 0x24,0x93,0x7e,0x90,0x60,0x88,0xc5,0x35,0x95,0xe9,0x05,0x05,0xe4,0x4f,0xf5,0xd9, - 0x04,0x7e,0x42,0x9d,0x56,0x21,0x0f,0x81,0x54,0x24,0xe0,0x8b,0x95,0x8c,0xc0,0x7a, - 0xc9,0x19,0x09,0xee,0x39,0x49,0x72,0xb0,0x62,0xd7,0xeb,0x71,0x61,0x90,0x78,0x5c, - 0xc0,0x32,0xb2,0xb9,0x88,0xd9,0x0d,0xe2,0x92,0xd6,0xb6,0x51,0x61,0x13,0x10,0x95, - 0x47,0x3f,0x97,0xca,0x98,0xe5,0x98,0x40,0xe9,0xcc,0xc7,0xe3,0x81,0x43,0xc2,0x47, - 0xce,0xef,0xd3,0x35,0x06,0x3e,0x17,0x78,0xfc,0x78,0x4f,0x25,0x83,0xeb,0x19,0x72, - 0xd9,0xfe,0x1f,0x5e,0xf7,0x0f,0xdb,0x44,0x31,0xa8,0xb4,0x5e,0x62,0xae,0x0a,0x79, - 0xe0,0xcc,0x52,0x92,0xa1,0xc5,0x7a,0x8b,0x0c,0x55,0x23,0xdc,0xa6,0xf9,0x9b,0x8f, - 0xf4,0x78,0xfb,0xfe,0xdd,0x7b,0xa0,0x93,0x06,0xdd,0x79,0xba,0x0c,0xe9,0xa6,0x69, - 0x4a,0xf5,0xdc,0x16,0x67,0x2f,0xdf,0xc2,0x43,0xe7,0x43,0x30,0x5d,0x46,0x78,0x71, - 0xeb,0xdb,0x20,0x8e,0x92,0xb6,0x4c,0x3b,0xe9,0x03,0xd5,0xeb,0xb2,0x0d,0x67,0x93, - 0x1a,0x86,0x5e,0x32,0x8b,0xce,0x39,0xb6,0x08,0xe5,0xb7,0x11,0x63,0xa8,0xda,0x6b, - 0x1d,0xfb,0x01,0x2a,0xf6,0x6d,0xf1,0x8a,0xde,0xa1,0x4e,0x7b,0xfa,0x8a,0xde,0x0d, - 0xb5,0xc1,0x40,0x66,0xfa,0x07,0xd0,0x40,0x9a,0x6c,0x28,0x7c,0x84,0xd2,0x2d,0xfd, - 0x15,0x14,0xb4,0x88,0x14,0x00,0x00,0x70,0xee,0x91,0x88,0x72,0xbe,0x95,0x2b,0xf2, - 0xae,0x1b,0x6a,0xba,0xba,0x5c,0x42,0x11,0x90,0x88,0x13,0x63,0xa6,0xe4,0x29,0x28, - 0x22,0x06,0x32,0xc9,0x80,0x26,0x06,0x50,0xd4,0xb6,0x8d,0x22,0x49,0x7f,0x43,0x2d, - 0xfa,0xa3,0xb4,0x97,0x7a,0xda,0x89,0x25,0xb7,0xec,0xaf,0xda,0x1c,0xcf,0x70,0x81, - 0x89,0x92,0x94,0x62,0xe8,0xae,0xe3,0x95,0x6b,0x9a,0x81,0xf2,0x4f,0x0a,0xfe,0x03, - 0xba,0x95,0x5a,0xaf,0x4f,0x4c,0x65,0xd8,0xc5,0x15,0x8c,0xf9,0x0d,0x7b,0xc6,0x51, - 0x49,0x5d,0x18,0x06,0xd3,0x30,0x3e,0x05,0x18,0x18,0xb3,0x4b,0x6f,0xf0,0x52,0xa4, - 0xf3,0xa4,0x09,0xc8,0x6f,0x8b,0x95,0x7a,0x89,0x3d,0xe5,0x97,0x34,0x23,0xd6,0x17, - 0xc6,0x41,0xd3,0x89,0xc8,0xa5,0x0f,0x68,0x6c,0x1f,0xe1,0x0d,0x6c,0x48,0x42,0xc4, - 0x20,0x65,0xb0,0xba,0x46,0x0b,0xe6,0x3c,0x6d,0xd2,0x18,0xdb,0xd2,0x7e,0x03,0x23, - 0x9c,0xfc,0xb5,0xda,0xbf,0x56,0x86,0xc4,0x17,0xb9,0x35,0x8a,0xd8,0x9a,0xfd,0x6a, - 0xa3,0x8b,0xec,0x01,0x17,0x57,0x37,0x2e,0xae,0xc0,0x9a,0xa3,0x55,0x6a,0x23,0x6b, - 0x3d,0x42,0x6e,0xda,0x64,0x0f,0xb9,0x08,0xe1,0x97,0x44,0x91,0xd5,0x08,0xa9,0x1c, - 0xac,0x55,0x0b,0x77,0xda,0xf7,0xd7,0x0c,0x17,0x6f,0x38,0x6e,0x38,0xc4,0x44,0x80, - 0x34,0x6f,0xb3,0x4c,0x4c,0x42,0x62,0x15,0xe4,0x75,0xd3,0x71,0x07,0x58,0x3c,0xb6, - 0x28,0xcc,0xac,0xd7,0xc0,0xae,0x5a,0x07,0xfa,0xe3,0x7a,0x0a,0xb3,0x20,0xc2,0x3c, - 0xd4,0x92,0x5b,0x75,0x31,0x83,0x6a,0x99,0xdf,0x41,0x25,0xb2,0xc9,0x72,0x3f,0x4c, - 0x47,0x7e,0x16,0x18,0x73,0x00,0x3e,0xe1,0x59,0x20,0x92,0x72,0x6e,0xc2,0x1b,0x25, - 0x30,0xf1,0xad,0xda,0x27,0x7e,0x6a,0x16,0x4a,0xc5,0x2a,0x46,0xcd,0x07,0x96,0x79, - 0x6f,0xb7,0x55,0x43,0x38,0xe6,0xad,0xb3,0x9a,0x80,0xe6,0xb0,0x91,0x4f,0xdd,0x0b, - 0x77,0x07,0x6b,0x78,0x8d,0x49,0xf3,0x3e,0x0a,0x1e,0x81,0xec,0x43,0xfd,0x96,0x83, - 0x5d,0xa8,0x6a,0xef,0x01,0xd9,0x82,0x6e,0x2b,0xbc,0x30,0xe1,0xea,0x25,0xd4,0x86, - 0x60,0x80,0x0e,0xaf,0x69,0x35,0xd7,0x96,0x69,0xe2,0xc1,0xb0,0x5e,0xab,0x50,0xd2, - 0x5a,0xe8,0xb5,0x93,0x65,0xe3,0xc1,0x14,0xaa,0xfe,0x3e,0xc2,0x2c,0xf1,0x25,0xec, - 0x21,0xda,0x9b,0x26,0x13,0x01,0x4c,0x31,0x67,0xdb,0xad,0x3a,0x8c,0x29,0x5d,0xa8, - 0x21,0xa9,0xa3,0xf6,0xca,0xbf,0xa6,0x98,0x05,0x5b,0x93,0x72,0x90,0x7f,0x83,0x9b, - 0x60,0xc8,0x0e,0xac,0x38,0x1e,0x58,0xa2,0xbd,0xc2,0x5b,0x19,0x2c,0xa2,0x71,0x7c, - 0x9e,0x2c,0x24,0x9a,0x01,0x3e,0x2a,0x34,0x80,0x9b,0xe7,0x49,0x6e,0xbd,0x03,0xcd, - 0xc7,0xae,0x11,0x66,0x67,0xe4,0x29,0x0f,0x69,0x83,0x30,0x0b,0x22,0xfb,0x23,0x53, - 0x32,0x7e,0xa5,0x52,0xcf,0x80,0xc8,0xd0,0x88,0x1c,0xc8,0x50,0x07,0x75,0x9a,0x50, - 0x0a,0x10,0x78,0x4f,0x44,0xe8,0xc0,0xf7,0xa3,0xc5,0xcc,0x77,0xe3,0x37,0x9e,0x61, - 0x34,0xca,0x2e,0x42,0x51,0x60,0x81,0x68,0xfb,0x82,0x02,0xfb,0x1e,0xf2,0xaa,0xa9, - 0xa4,0x6f,0xb4,0x7a,0x9a,0x04,0xaf,0x38,0x19,0x24,0x29,0x6f,0x70,0xcb,0x10,0xc6, - 0x4f,0x58,0xea,0x08,0x7a,0xc4,0xa1,0x77,0xe8,0xad,0x03,0xd7,0xe5,0x0c,0xfc,0x54, - 0xa0,0x0a,0x25,0x00,0x55,0x17,0x7b,0xde,0x1e,0xaa,0x5c,0x1b,0xb1,0x64,0xd9,0x91, - 0x22,0x53,0x96,0x1d,0x5a,0xc3,0x91,0xeb,0x41,0xf6,0xaa,0x41,0xf6,0xd6,0x81,0xac, - 0x50,0xb7,0x95,0x1b,0xf3,0x0d,0xd4,0x7f,0xe1,0x67,0xb3,0xe6,0x27,0x34,0x9e,0x2f, - 0x5a,0xbf,0x65,0x58,0x85,0xae,0x7d,0xd1,0xb0,0x0a,0x28,0xd9,0x68,0x58,0xe5,0x31, - 0x5c,0xa8,0xc9,0xc5,0xa0,0x6a,0x0c,0x28,0xe7,0x78,0x70,0xba,0x26,0x6a,0x96,0xa4, - 0xe1,0xaf,0xa0,0x24,0xf9,0x11,0xc7,0x36,0xf9,0xa3,0x34,0xc9,0x32,0x99,0xd8,0xfa, - 0x46,0xbb,0x09,0xe5,0x7d,0x1a,0xfe,0x55,0x90,0x62,0xce,0x2d,0x05,0x8d,0xf2,0x48, - 0x62,0xb5,0xe7,0x27,0x6f,0xde,0xbf,0xfb,0xfe,0x4c,0xe5,0xd7,0x81,0xaa,0xa0,0xe3, - 0x07,0x18,0xe5,0x9f,0x1b,0xdb,0x2b,0xc4,0x4b,0x3b,0xc0,0xd0,0x5a,0x84,0x71,0xac, - 0x52,0x7a,0x07,0x0a,0xd4,0x30,0xc9,0xf1,0x4a,0xab,0x60,0x0c,0xe6,0x54,0x13,0xef, - 0x16,0xa1,0x14,0x7b,0x20,0xf4,0x55,0x0c,0x38,0x86,0x05,0x6c,0xc1,0x7a,0xf5,0x87, - 0x09,0x1f,0x76,0x92,0x21,0xf2,0x79,0xb2,0xf0,0xc4,0x0b,0x72,0x5c,0xfa,0xb9,0x82, - 0x46,0xa9,0x44,0xfc,0x6b,0x1d,0xa7,0x8e,0xe5,0xaf,0x7d,0x04,0x86,0x0d,0x2f,0x73, - 0x54,0x37,0x94,0xcd,0x07,0x9d,0x6d,0xa3,0x49,0x37,0x9a,0xb1,0x13,0x0d,0xf0,0x32, - 0x44,0x84,0xc6,0xf6,0xb6,0x3b,0xa3,0x4c,0x9b,0x5f,0x32,0x29,0x88,0xb6,0xc0,0xf8, - 0x19,0xd9,0x36,0x27,0xa7,0xb6,0x6b,0xae,0xec,0x58,0x09,0xe2,0x40,0x0f,0x70,0xb9, - 0x58,0x41,0x13,0x72,0x1d,0xd2,0x4b,0xe4,0x43,0x04,0x4d,0x9f,0x17,0xae,0x14,0xd6, - 0xb0,0xd8,0x6b,0x56,0x64,0x7f,0x53,0xe9,0x76,0x83,0xdb,0xf0,0x40,0x4a,0xd1,0xaa, - 0x82,0xe5,0x13,0x8f,0x28,0x7d,0x74,0x44,0x37,0xaf,0x20,0x62,0x00,0x05,0xf5,0xb4, - 0x46,0x73,0xaf,0x1d,0x0c,0x30,0xc5,0x92,0x24,0x73,0x34,0x6c,0x2d,0x53,0xa5,0xde, - 0x38,0xc2,0x9c,0xc3,0x2b,0x52,0x1a,0x3f,0x7d,0x52,0x0b,0x0f,0xfd,0xa8,0x6a,0xb5, - 0xd1,0x6f,0xb9,0x7a,0xf4,0x7b,0xfc,0x7d,0xe1,0x9c,0xf7,0x77,0x18,0xe4,0x08,0xdd, - 0xf3,0x3b,0x38,0x07,0xa3,0x95,0xfa,0xf5,0x10,0xfe,0xb1,0x8f,0x81,0x9b,0xde,0x6f, - 0x56,0xe5,0xd6,0x1c,0xf4,0x64,0xa4,0x91,0x3d,0x7a,0xee,0x4f,0x29,0x0e,0xd8,0xb6, - 0x7b,0xad,0x0f,0x6b,0xac,0xac,0xcd,0xe5,0x67,0xee,0x4f,0x79,0xd7,0x0a,0x7e,0xd0, - 0x41,0x71,0x47,0xec,0xd4,0x4a,0x57,0x28,0x2d,0xb7,0x7a,0x50,0x87,0x2d,0x6b,0xdf, - 0x96,0xb8,0x6d,0x59,0xfb,0x3f,0x76,0xc5,0x9c,0x64,0x6c,0xc1,0xc2,0x91,0xe2,0x16, - 0x89,0xaa,0x57,0xae,0x58,0x3e,0x0c,0x0c,0x92,0x16,0x83,0xe1,0x95,0xd4,0x6d,0xad, - 0x2b,0x4b,0x62,0x13,0x50,0xd7,0x68,0x5b,0x62,0xd4,0xaa,0xf1,0xcb,0x32,0x48,0x57, - 0x67,0x74,0xcf,0x2c,0x1e,0xa3,0xf2,0xe4,0xbd,0xa4,0xa5,0x73,0x72,0x8d,0xe7,0x0d, - 0x4b,0x73,0x7a,0x50,0x90,0xe2,0x8d,0xff,0xf8,0xf7,0xb7,0x94,0xe3,0x52,0x9d,0x6e, - 0x44,0xb3,0xf8,0xfb,0x68,0xb5,0x98,0x35,0x2b,0x1a,0xe1,0xab,0x1f,0x1a,0xad,0xb6, - 0x3c,0x65,0xd6,0x03,0x23,0xa9,0xd7,0x6f,0x5b,0x1a,0xc0,0xad,0xf6,0x35,0x00,0xa7, - 0x53,0x17,0xf8,0x4d,0x11,0x5e,0x5b,0x2c,0x33,0xbc,0x10,0x17,0x13,0xec,0x4f,0x39, - 0x53,0xd1,0x24,0x49,0xf0,0x30,0x0c,0x5d,0xab,0xe2,0x1a,0xe6,0xdc,0x03,0x6e,0x4e, - 0x35,0x06,0x1a,0xdf,0xac,0xcd,0x9b,0x07,0x36,0x31,0x6d,0xb6,0xed,0x48,0x62,0xd1, - 0xde,0x76,0xc4,0xe8,0xce,0x6b,0xb9,0x79,0xa8,0x73,0x48,0x39,0xdb,0x92,0xf2,0xeb, - 0x81,0x7a,0xaf,0x77,0x09,0x67,0xf2,0x83,0x5d,0xc9,0xdd,0x70,0xbc,0x56,0xb4,0xe0, - 0x7e,0xb6,0x20,0x18,0x5a,0xb9,0xd5,0x23,0x41,0xd2,0x1e,0xd5,0xed,0x11,0x0b,0x31, - 0xdd,0x70,0x2b,0x72,0x5a,0xf2,0x40,0x20,0xe6,0x14,0x08,0x87,0x49,0x11,0x32,0x11, - 0x47,0x72,0x35,0x2a,0x9e,0x35,0x75,0x99,0x95,0xb7,0x6b,0x5e,0x1e,0xd3,0xbe,0x71, - 0x83,0xd6,0x40,0x83,0x5f,0x17,0xf8,0x71,0x85,0x99,0x00,0x30,0xf6,0x1d,0xf3,0x80, - 0x07,0xbc,0x24,0x15,0x74,0x1b,0x3f,0x5a,0x6f,0xaf,0xe4,0x1a,0xae,0x88,0xe9,0x39, - 0x28,0x18,0xd1,0x7d,0xc0,0x23,0xc6,0xd4,0x35,0xaf,0x61,0x09,0xee,0xba,0x4a,0x31, - 0xa1,0xb9,0x03,0x45,0x3a,0x00,0x11,0x8a,0xcc,0xac,0x22,0xc4,0xe2,0x69,0x15,0x76, - 0x5b,0x30,0x00,0x63,0x55,0xa3,0x2b,0x81,0x02,0x9f,0xa6,0x96,0x55,0xbd,0xd2,0x36, - 0xf1,0xd4,0x92,0x15,0x35,0x1e,0x35,0x3a,0xc3,0xe5,0x33,0x25,0xff,0x4d,0x7a,0xd2, - 0x4e,0x5e,0x7c,0x7f,0x02,0xb8,0x79,0xc8,0xd1,0x04,0x74,0x49,0x48,0x39,0xf0,0x64, - 0x16,0xe6,0xe7,0xa0,0xac,0xf0,0xe8,0x69,0xce,0x60,0xac,0x67,0xf6,0xa5,0xd6,0xa0, - 0x37,0xa1,0x56,0x43,0xb7,0x91,0xe1,0x51,0x36,0x80,0x8c,0x6a,0x15,0x65,0x0e,0x45, - 0x1d,0x87,0xd3,0xa6,0xb2,0xe7,0x59,0xdd,0x05,0x12,0x27,0xb9,0xca,0x3c,0xcd,0x17, - 0x64,0xcb,0x44,0x63,0xb0,0x2a,0x86,0x18,0x92,0x4e,0x7a,0x51,0x8c,0xbe,0x69,0x3c, - 0x9e,0xa9,0xd7,0x45,0x92,0x8e,0x29,0xb0,0xe5,0x53,0x86,0x0e,0x0b,0xcf,0xf3,0x8c, - 0xc0,0xc0,0xc4,0x20,0xcd,0x9f,0x94,0xa4,0x08,0x5b,0x7a,0xff,0x53,0xbe,0xa0,0x65, - 0x8e,0x9c,0xf6,0xa2,0xe0,0xa4,0x09,0x51,0xca,0x12,0x60,0x97,0x24,0xcb,0x8e,0xec, - 0x8b,0x83,0xbf,0x41,0xfb,0x2d,0xc6,0x63,0x85,0xff,0x88,0x60,0xc5,0x53,0x6c,0xb5, - 0xc3,0xd3,0x0a,0x9a,0xdb,0x0d,0x18,0xe5,0xd0,0xe0,0x03,0x7a,0x51,0xae,0xf3,0x4f, - 0x08,0x14,0xea,0x50,0x9b,0xa6,0xd6,0x0a,0x6a,0x51,0x93,0x76,0x3d,0x0a,0xa7,0x55, - 0x4d,0x61,0x0e,0x6d,0x09,0xa2,0x45,0x41,0xa0,0x61,0xbc,0x74,0x02,0x31,0x31,0xe7, - 0xf3,0x1b,0x25,0x22,0xfd,0x61,0x46,0x71,0x01,0x37,0xb0,0xbe,0x60,0xf5,0x23,0xcc, - 0x36,0x95,0xf8,0x50,0x2a,0xd1,0xd3,0x25,0x8a,0xd0,0xce,0xed,0xb2,0x2b,0x65,0xd2, - 0xb9,0xf0,0x9e,0x97,0xca,0xa0,0x09,0xe1,0x42,0xa4,0x3b,0x88,0x80,0xae,0xd2,0xcc, - 0x22,0x54,0x5f,0x4c,0x53,0x7f,0x88,0x04,0x98,0xe2,0xde,0x31,0x06,0xc9,0xc8,0x6b, - 0x70,0xfc,0x9b,0x80,0xae,0xc1,0xc1,0xac,0xb7,0x9e,0x49,0xbe,0x41,0xc3,0x03,0x54, - 0x51,0xcf,0xac,0x4d,0x06,0x9c,0xd4,0x81,0x08,0xdb,0x74,0x14,0x16,0xf3,0x90,0x45, - 0xce,0xe5,0x0a,0x3c,0xe8,0x4d,0xea,0xa5,0xa5,0x7a,0xba,0xbd,0xe7,0x6b,0xea,0x0d, - 0xd7,0xb4,0xb7,0xb6,0x5e,0x45,0x7b,0x6b,0x8a,0x57,0xb4,0xb2,0xa6,0x74,0x19,0xf6, - 0xda,0xa1,0x97,0x4a,0xaf,0xed,0xb8,0x53,0x5a,0x2e,0x00,0x43,0xf9,0x44,0xcd,0x92, - 0xc0,0x2d,0xca,0x5e,0x03,0x10,0xd9,0x7f,0xa3,0xb4,0xc5,0xc3,0x2c,0x91,0xb9,0xfc, - 0xf7,0x40,0x2a,0x43,0xca,0xea,0x6c,0xf6,0xce,0xd8,0xa8,0x93,0xe1,0x04,0xc0,0x08, - 0x31,0x23,0x24,0x1f,0x32,0xce,0xf0,0x1e,0xa5,0xad,0xd7,0x8d,0x28,0xd2,0xf9,0x9e, - 0x31,0xcc,0x01,0x01,0x21,0xb5,0x6d,0x0d,0xec,0x4d,0x36,0x4e,0x3f,0x11,0x50,0x82, - 0x15,0x3e,0xf3,0x6b,0x8e,0xf1,0xeb,0xbb,0x1e,0x29,0xe7,0x6b,0x10,0x8c,0x83,0xb1, - 0x0e,0x98,0x70,0xb6,0xee,0xc8,0x0e,0xcc,0x28,0x3f,0xb5,0xd5,0x41,0x3f,0xa7,0x50, - 0x17,0x75,0x14,0xe7,0xe7,0xe5,0x7c,0x61,0xef,0xe7,0xe1,0x29,0x9e,0x5f,0x83,0x23, - 0xe8,0x0a,0xf3,0x32,0x57,0x47,0x5f,0xbb,0x2f,0xa7,0x5c,0x93,0xcf,0xcc,0x2b,0x8c, - 0x9e,0xe5,0x5d,0xb9,0x96,0x71,0x65,0x7e,0x81,0x66,0x4f,0x09,0xd8,0x71,0x4b,0xef, - 0xc2,0x62,0x3e,0x41,0x21,0x17,0x7f,0xd5,0x01,0xb0,0xc0,0x30,0xc7,0x8a,0x63,0x5b, - 0x60,0x9c,0x96,0xce,0x6c,0xb5,0xad,0xba,0x33,0x79,0x46,0xe5,0x56,0x5b,0x22,0x26, - 0xa3,0x9b,0x95,0x84,0x14,0xf3,0x01,0x20,0xa6,0x38,0x87,0x47,0xd9,0xca,0x49,0xf2, - 0xd3,0x84,0x24,0x3f,0xde,0x02,0x46,0xe9,0x48,0x34,0x22,0x31,0x16,0xa5,0x14,0x00, - 0xf8,0x1c,0xd5,0x27,0x98,0x94,0x63,0x0a,0xae,0x23,0x55,0xcd,0x39,0x2a,0x23,0x6e, - 0xe8,0xfc,0x7f,0x9c,0xcb,0xf0,0xbb,0x7f,0x04,0xce,0x86,0x80,0xc8,0x7e,0x01,0xe9, - 0xe0,0x7e,0xfd,0x27,0xf5,0x15,0x8d,0x14,0xde,0x79,0xb4,0x9a,0xab,0xc8,0x86,0xcc, - 0x7d,0x85,0x39,0xc7,0xe4,0x44,0xaa,0xcb,0x72,0x42,0x54,0x40,0x3e,0x8a,0x7e,0x0a, - 0xb9,0x2f,0x8f,0xce,0x6c,0x8d,0xcd,0x28,0xbd,0xb7,0xa3,0x2d,0xe8,0x83,0x18,0xf0, - 0xd6,0x9e,0x3e,0x7c,0x07,0xaf,0x88,0x1e,0xb4,0x58,0x16,0xac,0x07,0xd0,0xc1,0x21, - 0xf5,0xb1,0x55,0xb9,0x6b,0x4c,0x11,0x4b,0x50,0x02,0xd7,0x2c,0x1f,0xcd,0xe2,0x65, - 0x5b,0x7a,0x1d,0x55,0xbc,0x33,0x87,0x1f,0x2a,0x88,0xfe,0x80,0x24,0x04,0x1e,0x49, - 0x90,0x12,0x01,0xf3,0x04,0x60,0x34,0x2c,0xdd,0xfd,0x51,0x58,0xef,0xb8,0xa0,0x33, - 0xba,0x7f,0xce,0x51,0xdd,0x4b,0x6a,0x83,0x1a,0x8c,0x75,0xb6,0xaa,0xa4,0x3b,0x38, - 0x03,0x96,0x8a,0xd8,0x67,0x75,0x10,0x68,0xc6,0x99,0xa2,0x05,0x67,0x31,0xfc,0xc7, - 0x01,0xce,0x07,0xfd,0xfc,0xa7,0x81,0x58,0xb5,0xef,0xd9,0xa9,0x21,0xad,0x43,0x9a, - 0x2a,0x3b,0xa4,0x39,0x9c,0x29,0x73,0xce,0x0e,0x04,0xab,0x11,0xbc,0x51,0xcd,0x6a, - 0xc6,0x3d,0x99,0x42,0xd1,0x8e,0x87,0x0c,0xd4,0x54,0x1f,0x73,0x8e,0x5b,0x9e,0x71, - 0x4f,0x52,0xcc,0x6b,0xee,0xab,0x7c,0x97,0xd2,0xdf,0x17,0x7c,0x5e,0x95,0xd6,0x04, - 0x7a,0x03,0x36,0xa1,0x3c,0x9a,0xba,0xaf,0xa5,0x3c,0x22,0x2e,0x44,0x97,0xab,0xb9, - 0xd5,0x90,0x62,0x45,0xb8,0x27,0x18,0xa5,0x59,0x82,0x0a,0x25,0x92,0x28,0xda,0xc0, - 0xf2,0xc8,0xad,0xca,0x7e,0x35,0x30,0xd4,0x73,0x68,0x08,0xed,0x99,0xfc,0xa1,0x0b, - 0x35,0xdd,0x52,0x28,0xf7,0x81,0xf1,0xb9,0x2f,0x87,0x48,0x79,0x50,0x33,0xbe,0xce, - 0x30,0xf7,0x11,0x6a,0x19,0xf5,0x00,0xd2,0x2a,0x00,0x91,0x04,0x10,0x64,0xd7,0x77, - 0x01,0xa8,0xea,0x80,0x6c,0x3f,0xd0,0x95,0xd1,0xd6,0x8f,0x33,0xf5,0x64,0xc7,0x3d, - 0x1c,0xac,0xa7,0x68,0xc4,0x78,0x91,0xa4,0xdf,0xbe,0x7e,0xf7,0x81,0x0c,0xc4,0x5e, - 0x5f,0x06,0x22,0xc7,0x7e,0x9a,0x26,0xd7,0x98,0xc4,0x89,0x1c,0x90,0xe4,0x8d,0x55, - 0x09,0x3d,0x56,0x82,0xae,0x09,0xbc,0x27,0xe7,0x90,0x00,0x16,0xb0,0x6c,0x9d,0x51, - 0x22,0x4b,0x17,0x53,0x61,0x60,0xb4,0x5d,0x94,0x4c,0x3b,0x1a,0xcc,0x40,0x3b,0x72, - 0xcd,0x0d,0x3f,0x1f,0x8e,0xce,0x5f,0xbf,0x17,0x64,0x7f,0x2b,0xb7,0x67,0xa8,0x4f, - 0x28,0xc9,0xc4,0x19,0x00,0x84,0xaf,0x4e,0x9c,0x07,0x00,0x1a,0x3d,0x11,0x5b,0x7c, - 0x0d,0x25,0x60,0x02,0x04,0xe5,0x3c,0xb8,0x26,0x65,0x10,0xed,0xbf,0x2d,0xdb,0x4b, - 0x9a,0xcd,0x42,0x72,0x1a,0x91,0xbe,0x4a,0xbd,0xe6,0x35,0xc9,0x11,0xaf,0x78,0xd7, - 0x1d,0x47,0xc3,0x9a,0x03,0x3b,0x34,0x4e,0x25,0x83,0x54,0xcc,0x2c,0x81,0x51,0x67, - 0xbf,0xec,0x02,0xee,0x81,0x7c,0x00,0x4a,0x6d,0xd4,0x9f,0xce,0xb7,0x4a,0xe0,0x51, - 0x69,0x82,0xd5,0xb2,0x45,0xa0,0x3a,0x15,0x6e,0xe0,0x40,0x27,0xa9,0x98,0x5b,0x88, - 0xe4,0xa4,0x01,0xe5,0x16,0xe2,0xc3,0x74,0x55,0x81,0x3b,0xda,0xf3,0x82,0x1a,0xc2, - 0x15,0x58,0x28,0x2b,0xf4,0x80,0xe1,0x0f,0x83,0x9c,0x7f,0xd2,0xce,0x42,0xb5,0xe7, - 0xe9,0x9b,0x1e,0x85,0x0a,0x8f,0x51,0xe2,0x96,0x8a,0x1c,0x9c,0x38,0x59,0x0e,0xf0, - 0xd2,0x4e,0x80,0xd1,0x56,0x15,0x41,0x96,0x8f,0xaf,0x4c,0xfa,0x46,0x47,0x35,0x30, - 0x60,0x1d,0x8d,0x80,0x2a,0x21,0x10,0x2b,0x51,0x1d,0x5b,0xfb,0x9a,0xe8,0x4e,0xc8, - 0xa4,0x45,0xd2,0x1f,0x49,0x8b,0x01,0xd3,0x66,0x04,0x26,0x00,0x94,0x8e,0x4e,0x23, - 0xa9,0x66,0x07,0x74,0xae,0x19,0x0b,0xb1,0xde,0x35,0x4e,0x30,0x72,0xf8,0x3a,0x31, - 0x26,0x83,0x26,0x70,0xe8,0xc1,0x28,0x5a,0x8e,0x83,0xac,0x09,0x6b,0xb4,0xd5,0x72, - 0x27,0x49,0x0f,0x11,0x63,0xb0,0x9b,0x37,0x2d,0xfb,0xa4,0x3d,0xcc,0x34,0x2e,0x31, - 0xcb,0x6b,0x51,0x05,0x33,0xb5,0x61,0xba,0xba,0x4f,0x09,0x26,0x87,0x72,0xdf,0x0d, - 0x33,0xd7,0x30,0x4b,0xfa,0x14,0xcf,0x78,0xdd,0x59,0xf8,0x7a,0x90,0x43,0x1b,0xa4, - 0x3b,0xf4,0x22,0x48,0x3a,0xf9,0xae,0x41,0xde,0xb2,0x78,0x94,0xd1,0x3a,0xac,0x26, - 0xa1,0xa7,0x80,0x6e,0xe7,0xb6,0x5f,0x9c,0x05,0x39,0xee,0xaf,0x64,0x5a,0x16,0x29, - 0x0d,0x2c,0x0a,0xfc,0x8c,0xce,0x40,0xe0,0x2a,0xd6,0xf2,0xc6,0x12,0x24,0x86,0x03, - 0x3a,0x0e,0x11,0x61,0x4e,0xe0,0x1c,0x60,0x62,0x8e,0x4d,0x64,0xdb,0x12,0x93,0xac, - 0x58,0x4d,0xb6,0x36,0xab,0xc6,0x37,0xb9,0x94,0xaa,0x96,0xce,0xf1,0x72,0x2a,0xab, - 0xbf,0xc3,0x5c,0x3d,0xd4,0x7d,0xfb,0x9c,0x0c,0xbd,0x30,0xc7,0x5f,0x46,0x7c,0x50, - 0xc6,0x2a,0x56,0x7b,0x52,0xe6,0x58,0x1f,0x95,0xc1,0xd4,0x1c,0xe9,0x40,0x20,0x7b, - 0xcd,0x07,0xe4,0x8a,0x1e,0x0e,0x44,0x7f,0x5f,0x9d,0x95,0x39,0x96,0x67,0x65,0x8e, - 0x5f,0x99,0x63,0x61,0xd4,0xc0,0x8b,0x6a,0xb7,0x17,0x4a,0x02,0x3e,0x03,0xf5,0xe5, - 0x1e,0x66,0xe7,0xe4,0xcc,0xce,0xde,0x7e,0x5b,0x1d,0x92,0xe9,0xf7,0x1e,0xd1,0x77, - 0x6b,0x60,0xce,0xc1,0x14,0xe5,0x65,0xb6,0xbe,0xbb,0x07,0x52,0xb4,0xb7,0xd9,0x2e, - 0x52,0x7f,0xc2,0xa5,0xa2,0x58,0xdd,0x01,0x97,0xd1,0xe6,0x07,0x5c,0x8e,0xad,0x03, - 0x2e,0xc7,0x1c,0x21,0x75,0x8c,0x47,0x5c,0x18,0xb7,0xf2,0x80,0xcb,0x31,0x9f,0x70, - 0x39,0x76,0x8f,0xb8,0x8c,0xe4,0x71,0x95,0x25,0xad,0xbc,0x63,0x0e,0xce,0x30,0xa7, - 0x3b,0x61,0x7c,0xc7,0x1f,0x35,0x15,0x38,0xa7,0x46,0x8e,0x6b,0x8f,0x8d,0x1c,0xbf, - 0x2a,0xc6,0xf2,0x95,0xe7,0xad,0x32,0x98,0xaf,0xa4,0xd7,0x80,0x05,0x72,0xe1,0xc6, - 0xfa,0xc9,0x1e,0x1e,0x23,0x46,0x8f,0x75,0xb4,0x9f,0xec,0xcb,0xf1,0x2b,0x7e,0x3b, - 0xd4,0xf8,0xab,0x89,0xf7,0x53,0x9f,0xdd,0x6d,0x43,0x8a,0x37,0x3f,0xb0,0xbf,0x51, - 0xcd,0x63,0x0a,0x46,0xc1,0x26,0xe0,0xbf,0x1f,0x91,0x60,0x5b,0x66,0x7e,0x6a,0x22, - 0x9a,0x46,0x95,0x9b,0x8a,0xa3,0xc2,0x66,0x9e,0x1c,0x0c,0x9d,0xd3,0x90,0x43,0xe0, - 0xdf,0x1f,0x79,0x2b,0xf4,0xf8,0x95,0x3e,0x7a,0x3e,0x5a,0x1b,0x47,0x68,0x8a,0x38, - 0x3b,0xfb,0x23,0x6b,0xef,0x15,0x27,0x9a,0x8a,0x72,0x53,0xea,0xab,0xf4,0xa4,0x17, - 0xbe,0x12,0x2e,0x55,0x11,0x7b,0xb3,0x7f,0x7d,0x1b,0x8c,0x29,0x24,0x13,0x82,0x55, - 0x68,0x43,0x4f,0x5d,0x45,0x91,0x62,0x84,0x16,0x2d,0x93,0xba,0x61,0xcb,0x8d,0x90, - 0x51,0xe5,0x3e,0xb4,0x7e,0x5b,0xda,0x0c,0x19,0x55,0xec,0x4f,0xab,0xa8,0x1a,0x54, - 0x55,0x0f,0x85,0x9b,0x75,0xc4,0x3c,0x95,0x52,0x13,0x98,0xaa,0xef,0x28,0x5c,0x05, - 0xab,0x3f,0xc3,0x8c,0x66,0xa0,0xc1,0xef,0xef,0xd6,0x6e,0xb4,0xbc,0x5b,0xb3,0xcf, - 0xf2,0xee,0x2b,0xb7,0x59,0x68,0xe6,0xf0,0x18,0x36,0x6e,0xaa,0x8c,0x38,0xb4,0xb3, - 0x62,0x23,0x65,0xe4,0x46,0x28,0xea,0xbd,0x94,0x91,0x1b,0xa4,0x68,0xb6,0x53,0x2a, - 0xa7,0x45,0xa6,0xb7,0x93,0x31,0xeb,0x95,0xb9,0x5a,0x2a,0xb1,0x56,0xc8,0x72,0x20, - 0xd9,0x89,0x83,0x71,0x39,0xae,0xaa,0xf9,0xd6,0xc1,0xb0,0x86,0x29,0x9a,0x50,0x13, - 0x10,0x2b,0x3b,0x2a,0x5c,0x66,0x93,0x15,0xd0,0x92,0xd4,0x37,0xf2,0x56,0xdd,0x56, - 0xd5,0x2a,0x18,0xe1,0xb5,0xc3,0xa6,0xd4,0xa2,0xb7,0x6a,0x55,0xad,0x84,0x8d,0x5a, - 0xeb,0x59,0xad,0xf5,0xea,0x5a,0xeb,0xdb,0xad,0xf5,0xd7,0xb6,0x56,0x0a,0xb2,0xd1, - 0x5f,0x4a,0x81,0x59,0x6b,0xf0,0xa9,0x83,0x1f,0x46,0x35,0xa1,0x0f,0x4e,0xc0,0x2c, - 0x50,0xc5,0x4c,0x05,0x3e,0x8c,0xe4,0x95,0xcc,0x88,0x15,0x0c,0x72,0x18,0xc9,0xcb, - 0x98,0xe9,0x22,0x66,0x2b,0xd0,0x61,0x54,0x08,0x73,0xc0,0x20,0xda,0x9b,0x96,0x89, - 0x5c,0xc0,0xe7,0x55,0xab,0x32,0x7e,0x61,0x54,0x0a,0x78,0xd8,0xb8,0xf2,0xad,0x15, - 0xff,0x47,0x51,0x47,0xec,0x4c,0x18,0x08,0xb6,0x1d,0xe9,0x70,0xe0,0x32,0x0b,0x47, - 0x94,0x32,0x9b,0xaf,0x98,0x97,0x8a,0x9b,0x3e,0x97,0x51,0xef,0x10,0x52,0x21,0x01, - 0x5f,0x12,0x9c,0x83,0xe2,0xcb,0x84,0xe7,0x98,0xf5,0xae,0x2f,0x82,0x67,0x30,0x1d, - 0xe9,0xff,0x41,0x9b,0xd5,0xce,0x5b,0xd3,0x05,0x11,0xc0,0xde,0x20,0x53,0xa4,0x70, - 0x18,0x6d,0x0d,0x93,0x18,0xad,0x09,0xe2,0x29,0x13,0xd3,0x8e,0x5e,0x46,0x6b,0x44, - 0xd7,0x7a,0x62,0x5f,0x56,0x09,0x0f,0x9b,0xd6,0x97,0x8a,0xce,0xaf,0x5a,0xb5,0xb2, - 0xa5,0x82,0xc2,0xeb,0xe9,0xbb,0xa6,0x53,0x7e,0x3a,0x2a,0x35,0xd8,0x16,0xbb,0x48, - 0x2d,0xca,0x6c,0xa5,0xac,0x2c,0x7d,0x55,0x41,0x85,0x8f,0x4a,0xd2,0xa1,0xdb,0xa6, - 0x74,0xc3,0x6e,0x3c,0xff,0x68,0x4d,0xac,0xfe,0xa8,0x3e,0x2a,0x7b,0xe4,0x84,0x4f, - 0x17,0x2f,0x27,0x69,0x00,0xb1,0xe1,0x33,0x39,0x80,0xe0,0x37,0x06,0x85,0xf0,0x7c, - 0xeb,0xb3,0x26,0x7d,0x46,0x6e,0xd4,0x76,0x74,0x1d,0x19,0x5c,0x3d,0x5a,0x13,0xb8, - 0x7d,0x47,0xcb,0xd8,0x24,0xd3,0x58,0xa9,0x29,0x29,0xab,0xeb,0xda,0x73,0x60,0x1a, - 0x75,0xad,0x78,0x7d,0x86,0x86,0xd6,0xc1,0x43,0xf3,0x12,0xd8,0xa3,0xcd,0xc0,0x58, - 0x77,0x78,0x54,0x82,0xb1,0x35,0xb1,0x4d,0xf0,0xbe,0xb4,0xc0,0xa1,0xa6,0x2d,0xfe, - 0xfa,0x2f,0xff,0x2a,0xf4,0x75,0x31,0xa5,0xc6,0x31,0x6b,0x34,0x46,0x27,0x54,0x0e, - 0x10,0xb5,0xde,0xaa,0x4a,0x56,0x02,0x10,0x0b,0x91,0xa6,0xcf,0xbd,0x3e,0x6f,0x50, - 0xd8,0x86,0xc0,0x57,0xed,0x02,0xd4,0x2a,0xcb,0x64,0x3a,0x5b,0x42,0x59,0xa5,0x37, - 0xb3,0x84,0xf2,0x7d,0x47,0x28,0x1b,0x03,0xdb,0xd9,0x15,0x29,0x58,0x7b,0x75,0xdb, - 0x22,0x46,0x15,0x59,0xb7,0x29,0x52,0xf8,0x6c,0xef,0x8a,0x58,0x06,0x5a,0x49,0x23, - 0x30,0x1b,0xce,0x74,0xa6,0x9e,0x64,0x91,0xc9,0x96,0x39,0x5b,0x2d,0x92,0x9c,0x3c, - 0x7f,0x4a,0x4a,0x60,0x4b,0x1d,0x2d,0x23,0x30,0x6f,0x4e,0xaf,0xaf,0x70,0x82,0x50, - 0x9a,0xb6,0x04,0x83,0xef,0xb6,0xc1,0xd9,0x58,0xf4,0x88,0x66,0x48,0x2f,0xb2,0x2b, - 0x68,0x11,0x57,0xaa,0xd0,0x37,0x15,0x2c,0x24,0xda,0x26,0xde,0x6f,0xf7,0xd9,0x6f, - 0x44,0x28,0xd5,0x4e,0x7b,0x93,0x73,0xeb,0x45,0xc1,0x93,0xf2,0xf5,0x13,0xed,0xc8, - 0xaf,0xda,0x19,0x97,0x86,0x28,0x25,0x37,0xf8,0x58,0x90,0x59,0x4e,0xfa,0x80,0xda, - 0xad,0x32,0x36,0x58,0x09,0xc0,0xab,0xcd,0x4d,0xc4,0x32,0x05,0xe9,0xa4,0x5b,0x3c, - 0x69,0x87,0x3c,0xcf,0x98,0x8f,0x9c,0x48,0x01,0xca,0x2f,0x0f,0x98,0x1c,0xd0,0x61, - 0xca,0xf7,0x11,0xb0,0xdf,0x91,0xe6,0xdc,0x14,0xe8,0x5b,0x05,0x46,0x1e,0xa5,0x6d, - 0x6a,0x8c,0x96,0x59,0x9e,0xcc,0x1b,0x75,0x19,0x57,0xd4,0xe6,0x91,0xb2,0x7f,0x8d, - 0x37,0xec,0x94,0x32,0xb5,0x5b,0x4e,0x32,0x37,0x4a,0xb3,0xe4,0x1f,0x3b,0x96,0x09, - 0x5e,0x9a,0x9b,0xcc,0x6d,0xc9,0x89,0xe2,0xb8,0xca,0x4a,0x1e,0xba,0xdb,0x83,0xcd, - 0x68,0xcc,0x76,0x9e,0x1d,0xcb,0x64,0x32,0x1b,0x55,0x2c,0xba,0xcf,0x54,0x65,0xcb, - 0x7f,0xb6,0x20,0x7c,0x64,0x6d,0xb6,0xa5,0xda,0x98,0x27,0x87,0xbc,0x85,0x18,0x05, - 0xb7,0x9c,0xff,0x97,0x47,0x6b,0x59,0xee,0x8b,0x8a,0xf9,0xda,0x84,0x09,0xcb,0xb8, - 0x9e,0xb1,0x6b,0x55,0x3e,0xb3,0xad,0x25,0xa4,0x21,0xcc,0xac,0x28,0x89,0x08,0x73, - 0x5c,0xa9,0xe9,0xd2,0xf9,0x07,0x9c,0x18,0xcf,0x23,0xd0,0x52,0x1a,0x1e,0xa3,0xaa, - 0xd1,0x32,0xdb,0xf6,0xb8,0x57,0xab,0x77,0xec,0x83,0xa8,0xee,0xa2,0x1b,0xf8,0x32, - 0xf6,0x73,0xc0,0x3f,0xb0,0x1a,0x5a,0x08,0xe1,0xd8,0xda,0x5e,0xff,0x43,0xb3,0xc1, - 0x19,0xb6,0xf0,0xe6,0x4c,0x80,0x3e,0x0b,0xc7,0xe3,0x80,0x6f,0x11,0x74,0x17,0x14, - 0x14,0xa4,0x49,0xfa,0x90,0x5c,0x3b,0xc5,0x9a,0x1b,0x5b,0xcf,0x35,0x59,0x3e,0xd7, - 0xd5,0xd0,0xad,0xbe,0x2b,0xc5,0xd0,0xca,0x53,0xb5,0x56,0x6d,0xbe,0xcd,0xe7,0x6e, - 0x1c,0x0a,0x15,0x32,0x6b,0x50,0xc9,0x6f,0x4a,0x01,0x10,0xb2,0xfc,0xa1,0xca,0x38, - 0xfe,0x49,0x86,0x82,0x2e,0x7c,0x20,0x0f,0x95,0x7b,0xc8,0x60,0xf7,0xa2,0x18,0xae, - 0xab,0x82,0x65,0x3f,0xab,0xcc,0xc4,0x0c,0x50,0xe5,0x37,0x93,0xb9,0x8d,0xf1,0x7e, - 0x8a,0x5d,0x3c,0xe2,0xbe,0xdb,0xb6,0x83,0x3e,0xed,0xd8,0xdd,0xaf,0x26,0x0c,0x98, - 0xfc,0xfa,0x4b,0x2c,0x9a,0x77,0x0f,0xd8,0x26,0x1e,0x3b,0x0a,0x84,0x0b,0xba,0xa1, - 0x20,0x56,0xba,0x2a,0x77,0x61,0xe0,0xf2,0x4e,0x97,0x23,0x94,0x83,0xe3,0xe3,0x28, - 0x89,0x83,0xa6,0x8d,0x87,0x96,0x15,0xe5,0x6c,0x31,0xcc,0x5a,0x96,0x59,0x60,0x9a, - 0xa6,0xa4,0x5b,0xc6,0xe6,0x7c,0xb7,0x8a,0xb9,0x2a,0x72,0x02,0x85,0x6d,0x99,0xad, - 0xbd,0xe0,0x83,0x71,0x43,0x85,0x8f,0xc6,0xb8,0x99,0xd4,0xec,0x50,0x9e,0x5a,0x05, - 0xe1,0x34,0xfa,0x62,0x00,0x54,0xdf,0xba,0x06,0xdc,0x7c,0x19,0x07,0x51,0xee,0xb7, - 0x7e,0x4f,0x25,0x4f,0xad,0x1f,0x33,0x3f,0xf6,0x42,0x59,0x93,0x53,0xd3,0x29,0xf6, - 0x40,0x70,0xcf,0x5a,0x05,0x00,0x05,0xfe,0xe6,0xf1,0xa5,0x3f,0x7d,0x34,0x2c,0x28, - 0x9d,0xbb,0x18,0xd8,0x1e,0xb3,0x0a,0x11,0x69,0xea,0x7e,0xad,0xac,0xac,0x98,0x74, - 0x7b,0x5f,0xa8,0xc4,0xc9,0xcd,0x3e,0xd4,0x26,0x68,0xfe,0x03,0x5e,0xe7,0x1c,0x8f, - 0xdf,0x81,0x51,0x5f,0x11,0xbc,0x8f,0xb5,0xc8,0x7c,0xc0,0xdc,0x76,0x0f,0xa4,0xa7, - 0x56,0x56,0xc1,0x8b,0xf8,0x4a,0x55,0xf4,0xb9,0x59,0x2b,0x06,0x95,0x0d,0x91,0xbf, - 0xfc,0x6f,0x82,0x54,0x2a,0x80,0xa9,0xbe,0xb1,0xc0,0xab,0x5f,0x39,0xb4,0xa0,0xe2, - 0x6a,0xcb,0x06,0xde,0x84,0x6c,0x5b,0x90,0xf2,0x99,0xae,0xe8,0xd3,0x5b,0x7a,0x05, - 0xd3,0x07,0x1a,0xb4,0xbf,0x3b,0x06,0x9b,0x1a,0x06,0x18,0x34,0x6f,0xd0,0x16,0x2e, - 0x0d,0xc3,0xca,0x64,0x5d,0xb2,0xa8,0xe4,0x40,0xac,0x34,0xd8,0x56,0x11,0x23,0x19, - 0xf3,0x14,0x2f,0x71,0x3d,0x94,0xcd,0x9c,0xe3,0x93,0x9d,0xf4,0xe6,0x9a,0x6e,0x09, - 0xc2,0x0b,0x36,0xad,0x14,0x67,0xe8,0x0c,0xef,0x5a,0x7d,0xc3,0x1b,0x73,0x9d,0x93, - 0x23,0xcd,0x3d,0xbb,0x65,0xda,0x4e,0xc5,0xe0,0xf3,0x3d,0xfb,0xf8,0x07,0xd7,0x7d, - 0x15,0xae,0xaf,0x4b,0x1b,0xee,0x35,0x75,0x5f,0x86,0x74,0x27,0xd0,0x9d,0x2d,0xdf, - 0x51,0xd3,0xc9,0xb0,0x26,0xb7,0xd4,0xeb,0xf3,0x8b,0xb7,0x2a,0x20,0xe2,0x39,0x11, - 0x7d,0x71,0x55,0x8d,0xd0,0x77,0x8e,0xa7,0xdc,0x21,0x40,0xa0,0xa5,0xd3,0xdf,0x51, - 0x86,0xd4,0x2e,0xaa,0x62,0x52,0x76,0x95,0x5e,0xd3,0x88,0x18,0x37,0x6a,0x50,0x67, - 0x53,0x2f,0x17,0x9c,0x85,0x0e,0xe7,0xb7,0x36,0x99,0x1d,0x5e,0x52,0xe0,0x11,0x35, - 0x42,0x01,0xdd,0xf5,0xd0,0x96,0xa3,0x3a,0x3b,0xf4,0xf9,0x3b,0x1b,0xea,0x8e,0x11, - 0xe6,0x36,0x74,0xa7,0x19,0x76,0xb5,0xa9,0x19,0xb6,0x87,0x26,0x54,0x93,0xde,0x5c, - 0xeb,0x45,0xc4,0x4b,0xdc,0x8c,0x55,0x47,0x69,0x5f,0x15,0xa9,0xee,0x49,0xcd,0x37, - 0xe4,0x4b,0xc0,0x7a,0x22,0xe6,0x38,0xb3,0xd0,0xa2,0x72,0x35,0x86,0xdf,0x6e,0xfb, - 0x6e,0x88,0xfc,0x35,0xc6,0xaf,0x1c,0x62,0xd9,0xf4,0xfd,0x9b,0x9d,0x14,0xec,0xb6, - 0x9e,0x18,0x8a,0x1c,0x4b,0x1a,0xad,0xc2,0x82,0x31,0xb1,0x19,0x6d,0xe1,0x72,0x0c, - 0x7d,0xab,0x00,0x99,0xb0,0x55,0x77,0x11,0x60,0x90,0x48,0xed,0x5d,0x04,0xeb,0x57, - 0x11,0xcd,0x89,0x63,0x96,0xbe,0x5f,0x5a,0x51,0x1b,0x76,0xd7,0xd1,0xbe,0x2e,0x2c, - 0x25,0x6b,0xd1,0xe1,0x5d,0x2a,0x1b,0xcf,0xae,0x6d,0x76,0x42,0x7b,0x9b,0x93,0x45, - 0xd1,0xec,0xe4,0xca,0xf7,0x8a,0x8c,0x73,0x13,0x96,0x76,0x07,0xcd,0x38,0xf7,0x22, - 0xdc,0x37,0x4f,0xd5,0x28,0x2d,0xf0,0x22,0x2b,0x7a,0xc6,0xd1,0x5c,0x64,0x5f,0x89, - 0x21,0xa3,0xa6,0xb1,0x69,0x57,0x9d,0xde,0x61,0x58,0x97,0x75,0x67,0x89,0xda,0xc4, - 0xf8,0x02,0x0d,0x5d,0x1a,0x00,0x32,0xa1,0xbf,0xad,0xa9,0x5b,0xe1,0xc1,0x0a,0x6e, - 0xb1,0xff,0xc6,0xc0,0xb7,0x8f,0x13,0xfe,0x3d,0x26,0xba,0x77,0x26,0xcd,0x45,0xf4, - 0xdd,0xf1,0xf3,0x38,0x6a,0x19,0x57,0x03,0xbf,0x9c,0xb3,0xb1,0xf0,0xbc,0xf1,0x59, - 0xd4,0x9a,0xc2,0x44,0x6a,0xd8,0xb3,0xaa,0xd3,0xa8,0x54,0xa7,0x78,0x1c,0x15,0x5f, - 0xd6,0xa8,0xb4,0xd6,0x79,0xd4,0x3b,0x00,0x80,0x46,0xba,0xa9,0x7a,0x2b,0x95,0xcd, - 0x2a,0xe5,0xb6,0x64,0x29,0x57,0x34,0x57,0x77,0xb2,0x55,0xd9,0xc8,0x66,0xbc,0xd2, - 0x4a,0x5e,0x77,0x37,0xc6,0x67,0x96,0x53,0x66,0x22,0xee,0x36,0xdd,0x0c,0x9d,0x87, - 0xad,0x82,0x4d,0x61,0x7f,0x33,0x79,0xd7,0x38,0xd5,0x75,0x65,0xac,0xe4,0x8e,0xba, - 0x61,0x48,0xdb,0x3b,0x26,0x72,0x6e,0x33,0xeb,0xa7,0x1c,0x4e,0x57,0xf6,0xe8,0x15, - 0x5c,0x6b,0x57,0x61,0x70,0xfd,0xf7,0x9a,0x63,0x5c,0xde,0x67,0x78,0x34,0x84,0xf9, - 0x4c,0xe2,0x0e,0xe9,0xce,0x03,0x3c,0xf3,0x01,0xb3,0x9f,0x87,0x23,0x91,0x63,0x3c, - 0x33,0xde,0x76,0xe3,0x47,0x99,0xbe,0x6e,0x11,0xb7,0x7a,0xcf,0x4e,0xde,0x9c,0x1c, - 0x9f,0x9f,0xbc,0xd0,0xa7,0x75,0x66,0x49,0x44,0x27,0x73,0x46,0x33,0x3c,0x10,0xda, - 0x91,0x41,0x63,0x6d,0x31,0x8d,0xf0,0x1a,0xc0,0xa1,0x1f,0xe1,0x6d,0x86,0xe1,0x28, - 0x93,0xe9,0xb5,0xe9,0x5e,0x29,0xbc,0x9a,0x93,0xcf,0x02,0x75,0x64,0xaa,0x5a,0x8c, - 0x9a,0x46,0x28,0xd9,0x28,0x4d,0xa0,0x46,0x3c,0x85,0x5f,0xc9,0x22,0xf0,0xc4,0x47, - 0x3a,0x9b,0x00,0x0d,0xd3,0xad,0x39,0xd7,0xa0,0xea,0x8b,0x71,0x42,0x97,0x57,0x87, - 0x78,0x03,0x21,0xef,0x3c,0xe3,0x15,0x97,0x18,0x2a,0x1d,0x02,0x7f,0xf5,0x94,0x8f, - 0x77,0x71,0xf5,0xfe,0x2a,0x48,0x23,0x7f,0x25,0x93,0x3e,0xab,0x47,0x2b,0x51,0xf5, - 0xd5,0x19,0x36,0xa2,0xbe,0xd3,0x83,0xfd,0xf5,0x98,0xf3,0x58,0xf3,0x87,0x8a,0xd0, - 0x3c,0x5c,0x0b,0xf0,0x35,0xe4,0x5d,0x8b,0x25,0xe8,0x06,0x0d,0xf5,0xf2,0x83,0x3f, - 0x31,0xf1,0x77,0x8b,0x2b,0x95,0xa8,0xdc,0xbc,0xb0,0x1f,0xde,0xf8,0x59,0xee,0x3c, - 0xcb,0x6d,0xf0,0xae,0xea,0xca,0xe9,0x1f,0x7f,0x3a,0x3b,0x39,0x7e,0xff,0xee,0x05, - 0xa6,0xa6,0xdd,0xb5,0xdf,0x9e,0x9f,0x9c,0xe2,0x5e,0x32,0xe8,0x3d,0x3b,0xba,0xfa, - 0xeb,0x58,0xa6,0xe5,0xe7,0xc7,0xf7,0x3a,0x4d,0xbf,0x75,0x7c,0x07,0x7b,0x0d,0x73, - 0xdb,0x94,0xe7,0x76,0x50,0xc7,0x90,0x23,0x39,0xd4,0x63,0xb1,0x22,0xd3,0x8f,0xc4, - 0x25,0xac,0x5d,0x79,0x4e,0xab,0xeb,0x3d,0x12,0xd9,0x80,0x6f,0xbf,0xc4,0x1b,0xf6, - 0xc4,0x70,0x99,0x66,0x39,0x31,0x8e,0x98,0x36,0x95,0x3a,0x59,0x88,0x57,0x34,0x07, - 0x9c,0x8d,0x36,0xf7,0xc3,0xc8,0x4e,0x10,0x22,0xe7,0x1f,0xef,0xe5,0xe6,0x8c,0xb8, - 0x99,0x49,0x89,0x4b,0x44,0x63,0xa2,0xd9,0x39,0x4d,0x2e,0x5d,0x38,0x99,0x4c,0x64, - 0xae,0xda,0x79,0x62,0x07,0xaa,0x2f,0x66,0xa0,0x82,0xa0,0x55,0x2b,0xfe,0x01,0xfb, - 0xe5,0x24,0xf5,0x59,0x84,0x94,0xd3,0x87,0x8b,0xd0,0x45,0x52,0xfb,0x94,0x83,0xe7, - 0x11,0x66,0xb8,0xe9,0x3a,0xf9,0x0f,0xed,0x0b,0xa5,0x6c,0x06,0xc3,0x30,0x48,0x95, - 0x7b,0x88,0x2c,0x9c,0x93,0x21,0x60,0x2a,0xfb,0x79,0xb3,0x45,0x8a,0xe1,0x5e,0x8b, - 0xb2,0x08,0x77,0x77,0x5a,0xce,0x5d,0x26,0x16,0x32,0xe3,0x24,0xcc,0x56,0x85,0x20, - 0x1d,0xd5,0xb7,0x26,0xf6,0xbb,0xe7,0x3d,0x6c,0xd9,0xfd,0xdb,0xb8,0x7b,0x32,0xb6, - 0x03,0xaf,0x6b,0xa3,0x3e,0xf6,0xba,0x2a,0x69,0x35,0xde,0x93,0x92,0x53,0xe2,0xe3, - 0x9d,0xd6,0xda,0x7e,0xf7,0xfb,0x14,0x20,0x0e,0xbd,0x31,0xfd,0xc7,0x65,0x78,0x1d, - 0xd0,0xc5,0x6e,0x1b,0xb4,0x8f,0xd7,0x18,0x52,0xeb,0x3b,0x7b,0xc5,0xd6,0xad,0x98, - 0x00,0x6a,0x6c,0x67,0x6f,0x7d,0x67,0xba,0xbb,0x45,0x41,0x00,0x6b,0x10,0xd9,0x4a, - 0x13,0x38,0x91,0x3e,0x93,0xfd,0x36,0xc4,0x5b,0x74,0xcd,0xad,0xaa,0x8b,0x70,0x11, - 0x60,0x3c,0xc4,0x00,0x98,0xc8,0x6a,0x8e,0xd9,0xf6,0x81,0x23,0x30,0xf7,0x91,0x99, - 0x65,0xde,0x9c,0xfc,0xf1,0xe4,0x8d,0xa4,0x4f,0xc9,0x9e,0xac,0x78,0x38,0x97,0x23, - 0x99,0x33,0xd8,0x3e,0xad,0x7d,0x5e,0x8b,0x4f,0x90,0x17,0xd2,0x70,0x9f,0xad,0xbf, - 0x03,0x89,0xbc,0x48,0x7c,0xeb,0x11,0xdf,0x39,0x3e,0xb8,0xbb,0xbc,0xb9,0xd4,0x89, - 0xb5,0x3d,0xd9,0xe6,0x03,0x20,0x0f,0x68,0x15,0x10,0x24,0xdf,0x20,0x92,0xfc,0x0d, - 0x6c,0x2d,0x5c,0xf6,0xcb,0xb5,0x79,0x48,0x9d,0xdd,0x46,0xd5,0x5e,0x87,0x28,0x08, - 0x4d,0x28,0x5c,0x22,0x4c,0x0d,0xf6,0x59,0x01,0x75,0xa6,0xba,0xfe,0x32,0x3a,0xb7, - 0x98,0x6b,0x1d,0x59,0x51,0xf3,0xbd,0x76,0xd5,0x45,0x75,0xb2,0x7a,0x4d,0xd7,0xb8, - 0x05,0x34,0xef,0x54,0xac,0x3c,0xbf,0x29,0x5e,0x05,0xe2,0x5a,0x60,0x75,0xf7,0xa4, - 0x7c,0xb7,0xce,0x25,0x54,0xa4,0x40,0x4c,0x49,0x74,0x77,0x90,0x2d,0xba,0xd6,0xf6, - 0x31,0x33,0x39,0x85,0x4c,0xee,0x74,0x79,0x26,0x8f,0x2b,0x82,0x65,0x75,0x6a,0x0a, - 0xfe,0xbc,0x3e,0xfa,0x94,0xcb,0xac,0xcb,0x54,0x79,0x45,0x31,0x64,0x9c,0x86,0x9c, - 0xc2,0x5c,0x31,0x5d,0xf9,0xc5,0xda,0x4c,0x95,0x33,0x99,0x20,0x62,0xe6,0xe6,0x5f, - 0xa4,0xa6,0xdc,0x1c,0x56,0xf4,0x4a,0x46,0x23,0x75,0x65,0xda,0x49,0xdd,0x23,0x78, - 0x77,0x6d,0xbf,0xb3,0xa3,0x8e,0x6e,0x1d,0x97,0x24,0xb1,0x3a,0x3f,0x4d,0x65,0x2e, - 0x93,0x36,0x9f,0x5a,0xb2,0x14,0xfa,0x2a,0x54,0x50,0x51,0xbb,0x6b,0x36,0x16,0xae, - 0xd5,0xe5,0x0c,0x55,0xfd,0xae,0x4d,0xdd,0x07,0x5d,0x58,0x97,0xb9,0x8f,0xee,0x55, - 0xa2,0xdb,0x67,0x2c,0x81,0xbb,0xad,0xe4,0x2c,0x05,0x61,0x5f,0x1f,0x94,0x72,0xfd, - 0xcd,0xac,0x48,0x6d,0x68,0x80,0x72,0x47,0x01,0x6e,0xed,0xfb,0x86,0x74,0xbc,0xa8, - 0x83,0x50,0x3b,0x60,0xd4,0xc1,0xea,0x4d,0x29,0xd1,0x5f,0x19,0xbf,0x64,0x6f,0x22, - 0x6a,0x9b,0x28,0xf0,0xdb,0x2a,0x60,0x0a,0x54,0x78,0x8f,0x1d,0xc2,0xea,0x23,0x88, - 0xff,0x36,0xc7,0x76,0x61,0x96,0xe7,0x22,0x71,0x9f,0x83,0x58,0x6f,0xc6,0xc9,0xb5, - 0xad,0x06,0x48,0x3d,0x49,0x6e,0x42,0x16,0x0f,0xb4,0xde,0x67,0x7d,0xa5,0x65,0xf4, - 0x16,0xa8,0x2e,0x6f,0x5c,0xfc,0x08,0xda,0x5e,0x67,0x14,0x25,0xa0,0x2a,0x64,0xa3, - 0x59,0x30,0x5e,0xa2,0x26,0xd7,0x26,0xf5,0x0c,0x6c,0xd8,0x0e,0x5f,0x4f,0x13,0xc6, - 0xa3,0x94,0x76,0xf6,0xb2,0x01,0x25,0xb4,0x4f,0x93,0x3c,0xc7,0x6c,0x7b,0xb9,0x3f, - 0x04,0x33,0x39,0x24,0xb9,0x83,0x22,0x28,0x9c,0x2f,0xe9,0x5a,0x68,0x62,0xe1,0x1f, - 0x5e,0x7f,0xff,0xea,0x5c,0xf8,0x73,0xa0,0x5f,0xd6,0x05,0x40,0xa1,0xc2,0x00,0xc4, - 0x98,0x43,0x03,0x33,0x50,0x0a,0xe5,0x05,0xe1,0x98,0xf4,0x66,0xb1,0x00,0x78,0x0b, - 0x3a,0xb0,0x8a,0xf7,0x7b,0x53,0xb3,0x74,0x49,0x17,0x26,0x8c,0x9f,0xd2,0x22,0xc0, - 0xa6,0x62,0xda,0xab,0xc4,0xde,0x81,0x5a,0xc1,0x17,0x1d,0x24,0xcb,0x8c,0xee,0x03, - 0x84,0x26,0x38,0xfe,0x58,0x66,0x45,0x68,0x19,0xb9,0x90,0xfb,0xe9,0x94,0x36,0xf1, - 0x50,0x8b,0x03,0x71,0x06,0xc3,0x67,0x1e,0x4d,0x58,0xe1,0x6c,0xc6,0x92,0x8f,0x3b, - 0xf8,0x41,0x5a,0x1c,0x2e,0xc7,0x5c,0x77,0x9f,0x8a,0x70,0xfe,0xe9,0x26,0x02,0x7a, - 0xa2,0xe0,0xe2,0x96,0x2d,0x95,0xea,0x74,0x30,0x73,0xac,0xce,0x83,0x89,0x8d,0x1d, - 0x2a,0x5a,0x74,0xf4,0x1c,0xd2,0x3f,0xb5,0x46,0x07,0x05,0xd5,0x1e,0x1d,0x10,0x06, - 0xdf,0xb4,0x84,0xe2,0x53,0xbd,0x7b,0xaf,0xae,0x5f,0xb2,0x65,0xab,0xc3,0xbb,0x47, - 0x14,0xe2,0x5d,0xb5,0x04,0xcc,0x0e,0x25,0xc1,0xd6,0xd9,0x3a,0xa1,0x06,0xba,0xa6, - 0xe8,0x25,0x9d,0xf9,0x63,0xe6,0x81,0x4d,0xe9,0xc7,0x5b,0x4b,0x3b,0x92,0x68,0x78, - 0xc2,0x4b,0x82,0xf8,0x29,0x0d,0x9e,0xd1,0xc6,0x0c,0x97,0x7f,0x9f,0x31,0x19,0x59, - 0xe9,0x67,0xd2,0xa3,0x97,0x3c,0xf1,0x74,0x40,0x6d,0x99,0x4e,0x90,0xbd,0x80,0xaa, - 0x88,0xba,0x40,0x1b,0x09,0x8a,0x54,0x6d,0x9c,0xe8,0x0c,0x03,0x5e,0xa8,0x68,0x98, - 0xb3,0x32,0xa9,0x48,0x2d,0x49,0x29,0x33,0x01,0x6f,0xb2,0x77,0x58,0xcb,0x94,0xf6, - 0x1c,0x9f,0xaf,0x84,0x15,0x4a,0xe7,0x9f,0x43,0x4c,0x96,0x21,0xae,0x61,0x31,0x38, - 0xba,0xb3,0xee,0x14,0x4d,0x8e,0xd2,0xf6,0xe9,0xb0,0x66,0x96,0x1f,0xc5,0xe1,0x1c, - 0x8f,0xc9,0xc4,0x2f,0x91,0xee,0x9a,0xbc,0xc6,0x58,0x52,0xd8,0xb7,0x19,0x49,0xa3, - 0x40,0x8e,0xb3,0xf6,0x26,0x23,0x9d,0xa7,0x69,0x14,0x44,0x25,0xc8,0xd0,0xae,0x9e, - 0x57,0x5a,0xc9,0x40,0xf3,0xe4,0x0e,0x40,0xe5,0x1b,0xc8,0xae,0x29,0xe5,0x63,0x1b, - 0xac,0x83,0x6e,0x69,0xed,0xbf,0x5f,0x04,0xf1,0x57,0x9c,0xfa,0x51,0xf6,0x90,0xda, - 0x69,0x01,0xa9,0xa7,0x0e,0xf4,0xa8,0x4f,0xfa,0xf0,0x0d,0x08,0x42,0x73,0x96,0x47, - 0x7d,0x75,0x37,0x6a,0x1a,0x50,0x9f,0xce,0xf0,0xb8,0x9f,0x35,0x88,0x06,0xc0,0x50, - 0x97,0xd3,0x30,0x1b,0xdc,0xe8,0xf0,0x0e,0x59,0x74,0xe7,0x61,0x1e,0x55,0xb8,0x63, - 0x4e,0x95,0xed,0xfe,0x97,0x7f,0x13,0xd5,0xbb,0x8d,0xc6,0x88,0xb2,0x0d,0x28,0x6d, - 0xb7,0xd9,0x36,0x9b,0x6b,0xaf,0x09,0x51,0xe4,0x9f,0x76,0x4e,0x85,0x12,0x3d,0xdb, - 0x8a,0x2e,0xa6,0x60,0xd5,0x14,0x55,0x82,0xa1,0x2e,0x82,0xbd,0x83,0x12,0x6a,0x48, - 0xec,0x96,0x9c,0x9e,0x80,0xdd,0x58,0x8e,0x7d,0xad,0xdb,0x93,0x49,0x83,0x5d,0xc2, - 0xb2,0x63,0x77,0x94,0xa7,0x32,0x78,0xec,0x4f,0x77,0x7c,0x8d,0xfb,0x27,0xb0,0x1c, - 0xdb,0x81,0xa7,0xd8,0xe9,0xe1,0xa1,0x19,0x76,0xcb,0xe0,0xe3,0x80,0xbc,0x4f,0x6b, - 0x03,0x34,0x90,0xf3,0xfd,0xd6,0xbd,0x35,0x65,0xc6,0x5b,0x9b,0x60,0x99,0x74,0xa2, - 0x7d,0x49,0xd3,0x7c,0x99,0xd0,0x4d,0xdd,0xb6,0xe1,0x0d,0x0d,0x13,0x53,0x42,0x3a, - 0x3b,0x64,0xc6,0xab,0x44,0x59,0x52,0xb8,0x0a,0xe5,0x3e,0xc8,0x97,0x19,0x2a,0x4b, - 0xe9,0xff,0xe7,0x84,0xf5,0xb6,0x6b,0xae,0x90,0x16,0x44,0x79,0xfe,0x7c,0xfd,0xae, - 0x7e,0xd7,0xd4,0xb5,0x45,0x0e,0x4c,0x45,0x19,0x1c,0xe5,0x2e,0x53,0xa7,0xb4,0xb4, - 0xbd,0x1a,0x94,0x80,0x58,0x66,0xa8,0xc0,0x7c,0xa4,0x39,0x7b,0xa5,0xd2,0x24,0x99, - 0xd3,0x72,0xa6,0xb3,0xac,0x3e,0xe6,0x12,0xc5,0x9b,0x1f,0xec,0x5c,0xa4,0x88,0x58, - 0x4c,0x4d,0x23,0xef,0xa3,0x93,0x97,0x7a,0x36,0xa4,0x91,0xd6,0x90,0xaf,0x99,0xf9, - 0x65,0x26,0x5f,0x6a,0x72,0x23,0x93,0x7e,0xe1,0x99,0xe8,0xe0,0xc6,0xc7,0x5b,0x76, - 0xf8,0x74,0x3d,0x5e,0x3d,0xd1,0x50,0x4b,0xcb,0x19,0xfe,0x26,0x24,0x57,0x61,0x9b, - 0x55,0x25,0xea,0x50,0x7b,0x4e,0xc5,0x34,0x2d,0x8a,0x1d,0x94,0xb3,0xb4,0x38,0xee, - 0xcf,0xaa,0xed,0x5a,0xd7,0xc7,0x59,0xb1,0x37,0x82,0x72,0xf3,0x79,0x1e,0x6f,0x30, - 0x90,0x23,0x0f,0xcb,0x92,0x94,0x41,0x22,0x7e,0x01,0x4a,0x30,0x52,0xea,0x21,0x1b, - 0xe7,0xc4,0x15,0xb5,0x71,0xcb,0x77,0x02,0x82,0x86,0xb2,0x82,0xf6,0x84,0x1f,0x61, - 0x8a,0xd6,0x15,0x5d,0xb8,0x01,0xa6,0x41,0x2c,0x4e,0xde,0x1e,0xa1,0xb9,0x8e,0xb9, - 0xd4,0x10,0x14,0x9b,0x6a,0xc3,0x00,0x56,0x56,0x60,0xa7,0x9e,0xa5,0x73,0xea,0xb4, - 0x59,0xe2,0x89,0x0f,0x24,0x21,0x38,0xb3,0xc1,0xc8,0x8f,0x80,0xd3,0x71,0x9a,0x84, - 0x0c,0x6d,0xff,0xb4,0x91,0x81,0x69,0xe3,0xf5,0x10,0x18,0x5d,0x12,0x02,0xf3,0xcf, - 0x39,0x9a,0xc1,0xee,0xf6,0x3c,0xb6,0xc5,0x49,0x27,0x9c,0xc2,0x00,0xf1,0xda,0xa3, - 0x24,0x05,0xa5,0x10,0xe0,0xd0,0xbd,0x4a,0x18,0x0b,0x83,0x89,0x1c,0x62,0xda,0x1b, - 0x07,0x15,0x0e,0xaf,0x2c,0x42,0x58,0xd7,0x94,0xd6,0xed,0x32,0x96,0x19,0x71,0xc7, - 0x61,0x2a,0xef,0x7b,0x4d,0x26,0x9e,0x78,0x9d,0xcb,0xf1,0xb2,0xd2,0xfa,0xfa,0xdd, - 0xe9,0x8f,0xe7,0x8c,0x07,0x56,0x82,0xf1,0xa5,0x74,0x36,0x00,0x81,0x21,0x38,0x8c, - 0xe7,0xa1,0x41,0xb1,0xb6,0x49,0x43,0xcb,0xf8,0x7e,0xd9,0x51,0x1a,0x66,0x0b,0xf2, - 0x5a,0x84,0x29,0xcc,0x77,0x38,0x9d,0xaa,0x73,0xf7,0x88,0xf4,0x4c,0x4c,0x70,0x2a, - 0x02,0x64,0x0d,0xea,0x1e,0xae,0x17,0x47,0x6f,0x4f,0x7f,0xfa,0xf8,0xfe,0xc3,0x0b, - 0xbe,0xc1,0x4a,0x5e,0x4d,0x95,0xd3,0xa1,0x05,0x73,0x73,0xd5,0xd0,0x8f,0x50,0x86, - 0x8c,0xed,0xbb,0xad,0xb8,0xd7,0x7c,0xc1,0x16,0x19,0xe8,0x8d,0x29,0x2e,0xe1,0x15, - 0xdd,0x63,0xc5,0xc0,0x8f,0xce,0x7f,0x28,0xc3,0xce,0x62,0x7f,0x61,0x81,0xfe,0x65, - 0x89,0xd4,0x61,0xc3,0x4d,0x26,0xb9,0x0d,0x35,0xf2,0x7f,0x65,0x98,0xd6,0x41,0x56, - 0x20,0x9a,0x8f,0x80,0x7b,0x3c,0xff,0xfa,0xb9,0x70,0xad,0x40,0x8e,0x59,0xae,0x71, - 0x62,0xf8,0x16,0x06,0x3d,0xbc,0x96,0xbc,0x59,0xe1,0x09,0x2e,0x74,0x9d,0xe6,0x0a, - 0x0b,0x5a,0x19,0x85,0xd4,0x10,0x0a,0x5b,0x0b,0xb0,0x60,0x24,0x9d,0x3a,0xba,0x8f, - 0xb3,0xc9,0x0c,0x24,0x22,0x0f,0x22,0x58,0x17,0x6e,0x5b,0x7e,0x11,0x58,0x24,0x63, - 0x86,0x01,0x8b,0xe4,0x0a,0x7d,0x0b,0x26,0x62,0xf2,0xaa,0x50,0x02,0xc7,0x56,0xe2, - 0x6d,0xd6,0xa0,0x8d,0x0b,0xe1,0xb2,0xba,0x0b,0xea,0x12,0x6d,0xb7,0x7d,0x7e,0x5b, - 0x6e,0xfe,0xd2,0x2d,0x50,0xd9,0xba,0x9e,0x4a,0x6f,0x02,0xab,0xaa,0xd9,0x44,0x3c, - 0xf3,0xd5,0x69,0x97,0x12,0xa5,0x9f,0x7a,0x17,0x9a,0xb3,0xa9,0x86,0xca,0xcc,0x20, - 0x8c,0x81,0x92,0x2b,0x76,0x74,0x6d,0x5c,0x96,0xba,0x6a,0xb2,0x4e,0xd1,0x6b,0xf7, - 0x3e,0xfb,0xb2,0x83,0x8c,0xf1,0x70,0xc5,0x57,0xb5,0xed,0xb5,0x7e,0xd3,0xe8,0xae, - 0xec,0xd1,0x55,0xb2,0x3e,0x33,0xab,0x5f,0x3d,0xda,0x02,0x61,0xe8,0xe1,0x1a,0x46, - 0xf8,0x75,0x23,0xfe,0x02,0x72,0x2a,0x0e,0xac,0x18,0xab,0x47,0x7a,0x85,0x9b,0xfa, - 0x2c,0xe2,0x4d,0x90,0x2c,0x59,0xa6,0xa3,0x80,0x76,0x3d,0x75,0xd8,0x04,0x9f,0xbd, - 0x43,0xc7,0x3b,0xda,0x78,0xa0,0x20,0x15,0xb4,0x69,0xfc,0x22,0xb8,0x62,0xe3,0x40, - 0x07,0x4a,0xbf,0xe3,0x9b,0x68,0x79,0x47,0xd0,0x8a,0xff,0x37,0x10,0xe5,0xfd,0xb3, - 0xe6,0x78,0x9f,0xbe,0x80,0xb6,0xb2,0x9d,0xe6,0x91,0xc7,0x47,0xc8,0xb0,0x82,0xfa, - 0xed,0xa1,0xd5,0x47,0x67,0x01,0x1b,0xe4,0x0b,0xfe,0x8f,0x7f,0x27,0x37,0x02,0xee, - 0x0e,0x25,0x35,0x9d,0x11,0xc9,0xe5,0xf1,0x5d,0x7d,0xaa,0x1e,0xe8,0x88,0xe2,0x6d, - 0xb0,0xcb,0xd0,0x8c,0xec,0x2c,0xce,0xf4,0xdd,0x83,0xae,0x82,0x16,0x8e,0xa3,0x3b, - 0xf0,0x05,0xf3,0x31,0x4b,0xb2,0x9c,0x67,0xa3,0x42,0x39,0xc2,0x8f,0xb8,0x0f,0x3c, - 0x78,0xd4,0x53,0x71,0x42,0xdc,0xc5,0xa0,0x52,0x9f,0x72,0xb1,0x8e,0x37,0xe8,0xe9, - 0x89,0x55,0x47,0xd6,0x8e,0x3c,0x0b,0x80,0x8e,0x4a,0x57,0xda,0xc0,0x38,0xcc,0xf0, - 0x8e,0x42,0x8a,0xc6,0x70,0x80,0x11,0xab,0x38,0xf2,0x94,0x35,0xd2,0xa4,0xdb,0x97, - 0xf5,0x2a,0x51,0x97,0x12,0xf3,0x9d,0xcc,0xd0,0xee,0xe7,0x5b,0x97,0xb4,0x6a,0x52, - 0xed,0xb5,0xac,0x9b,0x87,0xf5,0xb7,0x0d,0xb7,0x77,0x15,0x89,0xcb,0x05,0x20,0x4d, - 0xda,0xf2,0x9a,0x96,0x39,0xa1,0xec,0x45,0x6d,0xee,0x8e,0x3d,0xa8,0x49,0xc2,0x52, - 0xca,0xb9,0xd2,0xf4,0x51,0xaa,0x08,0xbd,0xcc,0xc2,0x38,0x74,0xef,0x58,0x02,0x1c, - 0xa6,0x84,0x37,0xff,0xda,0x0f,0x73,0xa4,0xde,0xc4,0x1f,0xcb,0xdb,0x89,0x15,0x83, - 0xa8,0xb8,0xe6,0x99,0xab,0xe9,0x85,0x78,0xe4,0xfd,0x04,0x76,0xc9,0x0b,0x98,0x2f, - 0xfe,0xde,0x12,0xc5,0x37,0x74,0xd5,0xf1,0x41,0x69,0x10,0x56,0x8e,0x8f,0x3a,0x9d, - 0xd1,0x11,0x8b,0x85,0x30,0x94,0x6e,0x25,0x62,0x05,0xb4,0x2e,0x3d,0x1e,0x36,0xb6, - 0xe9,0xc0,0xe7,0x93,0xed,0x0c,0xb4,0x97,0x45,0xfe,0x14,0x7e,0xa1,0xe2,0x84,0x7f, - 0x67,0xf9,0x3c,0x7a,0x7a,0xef,0xff,0x01,0x22,0xed,0x25,0xf1,0xab,0x0f,0x01,0x00, + 0x1f,0x8b,0x08,0x00,0x00,0x00,0x00,0x00,0x02,0xff,0xcd,0x7d,0xdb,0x72,0xdb,0x48, + 0x96,0xe0,0xbb,0xbe,0x02,0x96,0x7b,0x4d,0xa0,0x0c,0x52,0x24,0x25,0xca,0x32,0x69, + 0xca,0x2b,0x4b,0x76,0xd9,0x51,0xbe,0x28,0x2c,0x55,0xbb,0xbb,0xb5,0xea,0x1a,0x90, + 0x04,0x45,0x94,0x41,0x80,0x05,0x80,0xba,0x58,0xa5,0x88,0x79,0xda,0xd8,0xe7,0xd9, + 0xa7,0xfd,0x81,0xfd,0x85,0x7d,0xdf,0x4f,0xe9,0x2f,0xd9,0x73,0xc9,0x2b,0x00,0xd2, + 0xb2,0xbb,0x76,0x66,0x22,0xaa,0x2c,0x22,0x2f,0x27,0x33,0x4f,0x9e,0x3c,0xb7,0x3c, + 0x99,0xf9,0xec,0xc1,0x24,0x1d,0x17,0x37,0x8b,0xd0,0x99,0x15,0xf3,0x78,0x7f,0xe3, + 0x19,0xfe,0x71,0xe2,0x20,0xb9,0x18,0x6e,0x86,0xc9,0x26,0x26,0x84,0xc1,0x04,0xfe, + 0xcc,0xc3,0x22,0x70,0xc6,0xb3,0x20,0xcb,0xc3,0x62,0xb8,0xb9,0x2c,0xa6,0xcd,0x3d, + 0xcc,0xcd,0xc7,0x59,0xb4,0x28,0x9c,0x3c,0x1b,0x0f,0x37,0xb7,0x16,0xd3,0xe6,0x38, + 0x4d,0xf2,0x34,0x0e,0x5b,0xbf,0xe6,0xcf,0x67,0xc3,0x70,0x3a,0x9d,0x4e,0x7a,0xed, + 0xde,0xe6,0xfe,0xb3,0x2d,0x2e,0xb8,0x2f,0x2a,0xec,0x47,0x53,0xf7,0xc1,0x55,0x94, + 0x4c,0xd2,0xab,0xd6,0xf1,0x2b,0xcf,0x9d,0x2e,0x93,0x71,0x11,0xa5,0x89,0xeb,0xdd, + 0x5e,0x06,0x99,0xf3,0x69,0xc8,0x79,0xfe,0xcf,0xc3,0xc6,0x56,0xb0,0x88,0xb6,0xf2, + 0x22,0x28,0x96,0x79,0xc3,0x7f,0x3b,0x3c,0x3b,0xf7,0xdf,0x0d,0xbb,0xe1,0x53,0xff, + 0xc4,0xff,0x9b,0x7f,0x3c,0xfc,0x04,0xd5,0x87,0xb7,0x98,0x1d,0xf6,0x1b,0x71,0x74, + 0x19,0x36,0xfc,0x49,0x94,0x15,0x37,0xfd,0x07,0x1d,0xff,0xb2,0x9f,0x2c,0xe3,0xd8, + 0xbf,0x08,0x8b,0xfe,0x8f,0x7e,0x94,0x4c,0xe3,0xe8,0x62,0x56,0xf4,0xdf,0x2f,0xe7, + 0xa3,0x30,0xf3,0x67,0x69,0x3c,0xe9,0x7f,0x18,0xfd,0x1a,0x8e,0x0b,0x3f,0x0f,0x6e, + 0xfa,0xaa,0x07,0x85,0xff,0xd9,0x0f,0xbd,0xdb,0xf0,0xd1,0x23,0x37,0x6c,0x15,0xe1, + 0x75,0x71,0x98,0x26,0x45,0x98,0x14,0xc3,0xc2,0xbb,0xf3,0x47,0xcb,0xdc,0x28,0x3a, + 0xf2,0x17,0xde,0xad,0xfc,0x72,0xa6,0xd0,0xf9,0x51,0x6b,0x12,0xe5,0xc1,0x28,0x0e, + 0x27,0xc3,0x07,0x9d,0x3b,0xf3,0xab,0x3d,0x58,0xb4,0x8a,0x59,0x98,0xb8,0x53,0x7f, + 0xea,0x0d,0xb2,0xb0,0x58,0x66,0x89,0xb3,0xb8,0xf3,0x17,0x69,0x1c,0x6b,0x88,0xb9, + 0x3f,0xf7,0xc7,0x7e,0xca,0x58,0x28,0xfc,0xa9,0x7f,0x31,0xec,0x0c,0x54,0x0b,0x1f, + 0xa1,0x85,0x8b,0x47,0x8f,0x1e,0x4c,0xa1,0x6f,0xd3,0x61,0xc7,0x1f,0xc7,0x61,0x90, + 0x9d,0x46,0xf3,0x30,0x5d,0x16,0x6e,0xe1,0xf9,0x6e,0xde,0x1a,0x07,0x71,0xfc,0x3c, + 0x77,0xbd,0xfe,0x8f,0x6e,0xee,0x79,0xa2,0x45,0x09,0x7d,0xe2,0xdd,0x8e,0xdd,0x89, + 0x8f,0x58,0x81,0xb1,0xa8,0xe4,0x10,0x93,0x09,0x55,0xa1,0x77,0x27,0xea,0xfc,0xe4, + 0xff,0xe4,0x79,0x77,0xaa,0xe5,0x9f,0xa0,0xe5,0xe9,0xb0,0x3d,0x28,0xb5,0x38,0x80, + 0xde,0xb8,0xc5,0x10,0x48,0x42,0xa6,0x7d,0xf4,0xe7,0x50,0x2f,0x7d,0xf4,0x28,0x6d, + 0x4d,0xa3,0x2c,0x2f,0x86,0x43,0x40,0xc4,0x73,0xa8,0xde,0x87,0xce,0x8b,0x61,0xc3, + 0x74,0xa5,0x8b,0xbe,0x31,0xe5,0x17,0x55,0xc8,0x77,0x3e,0x00,0xd5,0x65,0x12,0xef, + 0x76,0x3e,0x4c,0x06,0xd3,0xdf,0x7f,0x07,0x50,0x77,0x7e,0x92,0x5e,0xf5,0x3f,0xde, + 0x41,0x19,0x22,0x0b,0x5d,0x6c,0x0c,0x23,0x79,0xf4,0xe8,0x6d,0x6b,0xb1,0xcc,0x67, + 0xf0,0x31,0x38,0x79,0x0e,0x9f,0x63,0xf7,0xc4,0xeb,0xff,0xed,0xf9,0xdf,0x5a,0x50, + 0x0b,0xfa,0xf1,0xb7,0xe1,0x71,0x0b,0x91,0xee,0xfe,0xec,0xbf,0xf3,0x8f,0x00,0xd8, + 0x55,0x50,0x8c,0x67,0x27,0x25,0x48,0x73,0xef,0xf6,0xb8,0xc5,0xe0,0xa1,0xdf,0x7f, + 0x6b,0x41,0x6f,0xdc,0x77,0xc3,0x77,0x41,0x31,0x6b,0xcd,0x23,0xc8,0xf6,0xdf,0x79, + 0x54,0x35,0x2a,0x5e,0xa5,0xd9,0x51,0x78,0x19,0x8d,0x43,0x5d,0x19,0xe6,0x2f,0x1d, + 0xa6,0xbf,0xff,0x7e,0x7b,0x27,0x27,0x3a,0x09,0xaf,0x9c,0xe3,0x2c,0x9d,0x47,0x79, + 0xa8,0xa7,0xe3,0xc6,0x4f,0x78,0xa2,0x67,0xba,0x4b,0xdd,0x70,0x5b,0x4f,0x4c,0xee, + 0xdd,0xe6,0x30,0xdd,0x6e,0xda,0x1a,0x2d,0xa3,0x78,0xf2,0xe8,0x51,0xce,0x3f,0x86, + 0x43,0x91,0xe2,0x01,0xfa,0x67,0x2d,0x44,0xa7,0xeb,0xf9,0x37,0x38,0xe3,0x77,0xde, + 0xc0,0x98,0x0d,0x03,0xc7,0xb2,0xd8,0x20,0x71,0x5f,0x66,0x59,0x9a,0xb9,0x8d,0x82, + 0x0b,0x35,0x70,0x20,0x69,0x4b,0x7c,0xfd,0xfe,0xfb,0xd3,0x70,0x07,0xa0,0xdc,0xdd, + 0x69,0xb2,0xfb,0xd1,0x5d,0x22,0x49,0x8a,0xa1,0x4c,0x43,0x40,0x17,0xa5,0x94,0xa8, + 0x2b,0xf3,0x6e,0x71,0x55,0x67,0xad,0xf4,0xb3,0x57,0xcc,0xb2,0xf4,0xca,0xe1,0x86, + 0x32,0x81,0x47,0x45,0xf5,0x44,0x1d,0xb8,0xb2,0x9e,0x67,0xf4,0x07,0x26,0x25,0x03, + 0x86,0x81,0xdd,0xbc,0x33,0x68,0xee,0x08,0xc7,0x0f,0x10,0xe1,0xdf,0x13,0x58,0xe7, + 0x8b,0x29,0xcf,0xd1,0x30,0x1f,0xbc,0x6d,0x4d,0xd3,0xec,0x65,0x00,0xdd,0x50,0x8d, + 0x4f,0x81,0x3e,0xa1,0x24,0x76,0xfc,0xce,0x73,0x3d,0xc5,0x6f,0x04,0xe3,0x4a,0x82, + 0x79,0x38,0xdc,0xbc,0x8c,0xc2,0xab,0x45,0x9a,0x15,0x9b,0xce,0x58,0xac,0xe9,0xcd, + 0xab,0x68,0x52,0xcc,0x86,0x13,0x9a,0xc0,0x26,0x7d,0x00,0xa7,0x88,0x8a,0x28,0x88, + 0x9b,0x39,0xac,0xa5,0x70,0xd8,0x41,0x2e,0x57,0x44,0x45,0x1c,0xee,0x1f,0x07,0x45, + 0x11,0x66,0xc0,0x47,0x60,0x68,0x4d,0xe7,0x60,0x39,0x89,0xd2,0x67,0x5b,0x9c,0xb5, + 0xf1,0xec,0x41,0xb3,0xe9,0xfc,0xf8,0xf2,0xfd,0xcb,0x8f,0x07,0xa7,0x2f,0x8f,0x9c, + 0xd1,0x8d,0x43,0x13,0xf4,0x4b,0x80,0xa5,0x7e,0x89,0x92,0x5f,0x16,0xc1,0x45,0xd8, + 0x5a,0xdc,0xf4,0x9d,0x70,0x12,0x15,0x4e,0x54,0xe4,0x4e,0x9e,0x2e,0xb3,0x71,0x98, + 0x3b,0xcd,0x26,0xf2,0xd1,0xe2,0x06,0xc1,0xfc,0x70,0x3b,0x4a,0xaf,0x9b,0x79,0xf4, + 0x25,0x4a,0x2e,0xfa,0xa3,0x34,0x9b,0x84,0x59,0x13,0x52,0xee,0x36,0x5a,0x58,0xff, + 0x76,0x1e,0x5c,0x73,0x27,0xfb,0x9d,0xee,0x5e,0x7b,0x71,0x3d,0x98,0x07,0xd9,0x45, + 0x94,0xf4,0xdb,0x4e,0xb0,0x2c,0xd2,0xc1,0x22,0x98,0x4c,0xb0,0x62,0x77,0x07,0xb2, + 0x80,0xf5,0x2c,0x62,0x64,0x6c,0x71,0x78,0x3d,0xc0,0x7f,0x9a,0xc0,0x18,0x43,0xc2, + 0x56,0x7f,0x9c,0xc6,0xcb,0x79,0x32,0xb8,0x08,0x16,0xfd,0x0e,0x94,0x05,0xf0,0xf3, + 0x34,0x49,0x6f,0xa7,0x80,0x95,0xe6,0x34,0x98,0x47,0xf1,0x4d,0x1f,0xe8,0xd2,0x6d, + 0x36,0x31,0xd9,0x83,0xec,0x49,0x34,0x3f,0x85,0x99,0xba,0x85,0x8a,0x69,0x26,0xf3, + 0x96,0x45,0x38,0xc1,0xcc,0x59,0x94,0x14,0x5c,0x17,0x3a,0x1e,0x42,0xd7,0xa0,0xf5, + 0x95,0x05,0xfb,0xe1,0x7c,0x51,0xdc,0xdc,0xca,0xde,0x25,0x69,0x12,0x42,0x4e,0xbe, + 0x08,0xc6,0x61,0x76,0x4b,0xdd,0xbc,0x00,0xd2,0xe9,0x77,0x20,0xf1,0x62,0x96,0xe6, + 0xc5,0x8b,0x22,0x21,0xd8,0xfd,0x28,0x99,0x85,0x59,0x54,0x0c,0x8c,0x86,0x3a,0xd8, + 0xd0,0x32,0xcb,0xa1,0xa5,0x45,0x0a,0xb0,0xc3,0x6c,0xc0,0x38,0xeb,0x43,0x0e,0xe0, + 0x37,0x8e,0x26,0x0e,0x77,0x21,0x5b,0xc6,0xa1,0x27,0x32,0x9b,0x59,0x30,0x89,0x60, + 0xad,0x63,0x37,0x47,0xc1,0xf8,0x33,0xb4,0xb7,0x4c,0x26,0xa2,0xaf,0xd3,0x28,0x84, + 0x65,0x55,0xd3,0x7d,0x85,0xdc,0x5d,0x00,0x8d,0x43,0x34,0x3a,0x78,0x16,0x64,0x51, + 0xd0,0x5c,0x64,0x61,0x9e,0x03,0xab,0x2f,0xb2,0x65,0x78,0x7e,0x2b,0xda,0x32,0x01, + 0xc5,0x61,0x09,0x72,0x94,0x7c,0xf6,0x0c,0x30,0xad,0x49,0x90,0x58,0x18,0x1e,0x05, + 0x84,0xb6,0x22,0x4d,0xe3,0x51,0x90,0xdd,0x56,0x67,0xf4,0x2a,0x83,0x19,0xc4,0x7f, + 0x06,0x01,0x08,0xb7,0xa4,0x19,0x15,0xe1,0x3c,0xef,0x8f,0x43,0x42,0x05,0xcd,0x6e, + 0x1b,0x7b,0x4b,0x53,0xfc,0x10,0x49,0xe4,0x35,0x4d,0x15,0x54,0xed,0x77,0x9c,0x8e, + 0xd3,0xed,0x86,0x73,0x6c,0x1f,0xf0,0x14,0xae,0x85,0xae,0x41,0xed,0xd1,0xc0,0x27, + 0xc1,0x7c,0x01,0xb8,0xb0,0xeb,0xac,0xe8,0x82,0x55,0xc3,0x89,0x92,0xc5,0xb2,0xb8, + 0x15,0x34,0xbc,0x8d,0x24,0x1c,0x8c,0xb1,0x70,0x05,0x51,0x46,0x1d,0x83,0x38,0xf5, + 0xbc,0x03,0x17,0x16,0x4b,0xa1,0xb7,0x4b,0x2d,0x14,0xe9,0xc5,0x45,0x1c,0x7e,0x82, + 0xee,0xda,0xf4,0xc2,0x80,0xd5,0x97,0x4d,0x2e,0xf7,0x19,0xc0,0x13,0xa4,0x93,0x7a, + 0xb2,0xc2,0x09,0xfc,0x06,0xaa,0x92,0x24,0xd4,0x43,0x4c,0x76,0x8c,0x5e,0xbf,0x0d, + 0x46,0x61,0x5c,0x1e,0x21,0x7d,0x5e,0x85,0xa4,0xb4,0xf4,0xda,0x6d,0x55,0x58,0x60, + 0xaf,0x8b,0x2d,0xcd,0x38,0xbb,0x53,0xdb,0x6a,0x1d,0xd1,0xe3,0x60,0x16,0x69,0x1e, + 0x11,0x1b,0xc8,0xc2,0x38,0x28,0x40,0x67,0xa2,0xe9,0xa6,0xa5,0x38,0x10,0x88,0x69, + 0x86,0x97,0x80,0x80,0x5c,0x2e,0xcf,0x49,0x5a,0xfc,0x74,0xab,0xaa,0x05,0x23,0xc0, + 0x01,0x2c,0x8a,0x41,0x1c,0x4e,0x0b,0xc4,0xc9,0x00,0xe5,0x39,0xfe,0x15,0xf3,0xda, + 0x36,0x7a,0xd6,0x5e,0x83,0x0f,0xbb,0x6b,0xbd,0xf6,0x7f,0x19,0x14,0x59,0x90,0x88, + 0x56,0x10,0xb8,0xd3,0x6e,0x75,0xba,0xb9,0x35,0xb9,0x2d,0x90,0x0c,0x12,0x11,0x15, + 0xb8,0xe9,0x67,0xaf,0x5a,0x98,0x3a,0xcf,0x5d,0x25,0x9c,0x3f,0xc4,0x05,0x7d,0xca, + 0x10,0x78,0xca,0xf3,0x30,0x9e,0x12,0x09,0x34,0x41,0x52,0x65,0x05,0x72,0xdb,0x38, + 0x2d,0x88,0x94,0xaa,0xb8,0x8a,0x60,0xf0,0x01,0x25,0xf1,0xaf,0xf0,0x2b,0xc3,0xbb, + 0x37,0x2f,0x4a,0x2f,0xc3,0x0c,0x65,0x4b,0x7f,0x16,0x4d,0x26,0x61,0x02,0x1d,0xc5, + 0x5e,0xa8,0x35,0x36,0x8a,0xd3,0xf1,0x67,0x49,0xbf,0x93,0x70,0x1a,0x2c,0xe3,0x42, + 0x21,0x1c,0x50,0x27,0x10,0x3e,0x8e,0x61,0xd5,0xb8,0xdd,0x1d,0xc0,0xbb,0xbf,0xd3, + 0xbb,0x9c,0xf9,0x3b,0x6d,0xf8,0x49,0x68,0x09,0x2e,0x9c,0x71,0x90,0x5c,0x06,0xb9, + 0xdf,0x1a,0xcf,0xa2,0xc5,0x61,0x90,0x4d,0x44,0x82,0x5c,0x90,0x7b,0x36,0x49,0x71, + 0xa5,0x9a,0x89,0xbf,0xcf,0xb2,0xd9,0xad,0x9b,0xf8,0x71,0x16,0x06,0xf3,0x66,0xf7, + 0x9b,0x71,0xa3,0x24,0x1b,0x94,0x67,0x12,0xae,0x90,0xe9,0xe0,0x6a,0x06,0x7d,0x68, + 0x92,0x24,0x81,0x04,0xe4,0x5b,0xdc,0x7f,0xd0,0xdd,0xe2,0x95,0xec,0xf8,0x0b,0xac, + 0xe1,0x09,0xb2,0x43,0x51,0x96,0x98,0xfd,0x29,0x0c,0x3a,0x05,0x38,0x11,0xd8,0x11, + 0xed,0x56,0xaf,0x87,0xf2,0x51,0xa4,0x4b,0x84,0x95,0xe4,0x17,0x54,0x25,0xf9,0xb8, + 0x42,0x82,0x9a,0x42,0xab,0x4d,0x78,0x5d,0x5c,0xd6,0xa0,0x35,0x4a,0x50,0xe9,0x6d, + 0xab,0x4e,0x75,0xef,0x23,0xc4,0x57,0x50,0x1f,0xb5,0xf1,0x1a,0x6c,0xb7,0x6f,0x15, + 0x1b,0xbf,0x2e,0xf3,0x22,0x9a,0xde,0x34,0x85,0x8a,0xd4,0x27,0x84,0x36,0x47,0x61, + 0x71,0x15,0x86,0x89,0xe4,0xe8,0x24,0x53,0xd4,0xac,0xb0,0x68,0xe8,0x8a,0x81,0x1d, + 0x02,0x6d,0xe5,0x5f,0x15,0x27,0x7b,0xa2,0xf4,0x49,0x74,0xf1,0x1f,0x2c,0xdf,0x89, + 0x89,0xd7,0xb0,0x66,0xea,0x1b,0x70,0x90,0x95,0xb4,0x63,0x32,0xe8,0x5d,0x64,0xd0, + 0x0f,0xa1,0xca,0x38,0x5d,0x84,0xa5,0x35,0xab,0xe4,0x6d,0x9b,0xa4,0x96,0x58,0x60, + 0x6d,0x63,0xf1,0x52,0x6b,0x6f,0xc3,0x8b,0x30,0x29,0x4d,0x97,0x94,0x3f,0xd5,0x79, + 0xa2,0x2a,0x3f,0x85,0x37,0x6f,0x12,0x9f,0x7f,0x7c,0xd0,0x72,0x75,0x57,0x2f,0xe3, + 0x6d,0x43,0x13,0x8c,0x92,0x38,0x4a,0x60,0x2a,0xb1,0x53,0xba,0x7a,0x95,0x8b,0x4e, + 0x03,0xc0,0xb5,0x27,0x4b,0x20,0xdc,0x4a,0x11,0x21,0xa1,0x3f,0x27,0xe9,0xe8,0x63, + 0x7a,0xf5,0x3d,0x9a,0x09,0xcf,0xff,0xb8,0x4a,0x2b,0x22,0x53,0x63,0x0d,0xf5,0x16, + 0x51,0x14,0x59,0xd6,0x1f,0x2c,0xdd,0xf7,0xbe,0x2a,0x9f,0xbe,0x99,0x49,0x3d,0xd1, + 0xab,0x41,0x76,0x7a,0x1d,0x0f,0x2a,0x15,0x73,0xe8,0xeb,0x3d,0x18,0x2c,0xb7,0x15, + 0x02,0xd3,0x05,0x89,0x1f,0xe1,0x4a,0x2b,0x33,0x2a,0x2c,0x71,0x94,0x4a,0x52,0x30, + 0x28,0xa1,0x96,0x1f,0x13,0x1d,0xdb,0x23,0xe9,0x58,0x1d,0xaf,0x55,0xbe,0xc8,0x1c, + 0x49,0x60,0x44,0x5f,0xd5,0x19,0x81,0x4b,0x38,0xdd,0xf6,0x1f,0x8f,0xe2,0xce,0xae, + 0x56,0x46,0x47,0x41,0x32,0x21,0x46,0x47,0x14,0xd3,0x76,0xda,0x0e,0xc9,0xc3,0xfb, + 0xd2,0xe4,0x28,0x00,0xa4,0xc3,0xba,0xa0,0x0e,0xa3,0x7c,0xd1,0x50,0xf5,0x24,0xf0, + 0xd0,0xb7,0x4b,0x5a,0x19,0xcc,0x49,0xbd,0xd4,0x81,0x29,0xbe,0xbd,0xa7,0xfd,0xb5, + 0x67,0xe9,0xb2,0x6d,0xae,0xdc,0x42,0x1b,0xe8,0xd6,0xe0,0x1b,0x84,0x4c,0x41,0x52, + 0xcb,0xec,0x32,0x3c,0x84,0x06,0x94,0xd2,0xc6,0x75,0x7e,0x8a,0xc6,0x9f,0xd1,0x84, + 0xba,0x97,0x04,0x02,0xd5,0x0d,0x6d,0x59,0xea,0x37,0xe2,0xb3,0xdd,0x6a,0x3f,0x0d, + 0xe7,0xf5,0x56,0xdb,0x65,0x26,0xf9,0xca,0x4a,0x25,0x53,0x76,0xeb,0x6b,0xbc,0x40, + 0x0e,0x83,0x68,0x02,0xf8,0x25,0x55,0xaa,0xe3,0x96,0x2c,0xd0,0xa9,0xd5,0x6d,0xd4, + 0xef,0x07,0x86,0xe5,0x6b,0x28,0x3b,0x5d,0x41,0x02,0x68,0x7a,0x85,0x45,0x8e,0x58, + 0x11,0xca,0xf1,0xae,0x62,0x21,0x04,0xea,0xfe,0x93,0xa1,0xa0,0xfd,0x98,0x45,0x9a, + 0x19,0x5f,0xc0,0xc7,0x00,0xff,0x69,0x02,0xcd,0x2c,0x50,0xeb,0x6b,0x72,0xb5,0x1c, + 0x14,0xc3,0x45,0x18,0x14,0xee,0xb6,0x0f,0xb3,0x08,0xbd,0x74,0xdb,0x7e,0x67,0x9a, + 0x79,0x5e,0x05,0x5e,0x89,0x6f,0xad,0xe1,0x54,0xf5,0xfd,0x5b,0xc1,0xbf,0x7a,0xab, + 0xad,0x93,0x7b,0x0b,0x45,0xd2,0xca,0xb4,0x0c,0x7c,0x0a,0x70,0x76,0x58,0xd1,0x52, + 0xbd,0xb7,0x15,0xc5,0x1d,0x43,0xc3,0xef,0xee,0x98,0xc5,0x80,0xa0,0x92,0xdb,0x12, + 0xad,0x95,0x0c,0x99,0x3a,0x13,0x98,0x6b,0xaf,0x11,0xb6,0x66,0x19,0xa3,0x11,0x8b, + 0x39,0xe6,0x45,0xb8,0xc8,0x2b,0x24,0xb8,0xc6,0xaa,0x2b,0xab,0x1a,0x75,0xd4,0x2f, + 0x81,0x3a,0xa3,0x65,0x51,0xa4,0xc9,0xda,0x59,0x14,0x04,0x6b,0x18,0x66,0xdb,0x26, + 0xd3,0x6a,0x0f,0x48,0xfe,0x4a,0x05,0xfb,0x8f,0x9a,0xb6,0x1a,0x74,0x12,0x73,0xbd, + 0x2f,0x07,0x62,0xf3,0x99,0xaa,0xdc,0x07,0x79,0xf7,0xd2,0x0f,0x25,0xc0,0x5a,0xc3, + 0xb6,0x16,0xcf,0xe9,0xb2,0x38,0xcd,0x60,0x84,0x35,0x16,0x97,0x44,0xd8,0x5e,0x55, + 0x1d,0x2c,0xd2,0xe5,0x78,0xd6,0x0c,0x78,0x4c,0x40,0x15,0xcd,0x1b,0x06,0xf5,0x31, + 0x88,0xe2,0x55,0x06,0x2b,0xae,0x98,0x8c,0x89,0x51,0x98,0xae,0x3d,0x83,0xa3,0x28, + 0xa7,0x5a,0x13,0x73,0x9a,0x6b,0x79,0x1e,0x34,0xf4,0x2a,0x8a,0xeb,0x1a,0x2a,0x41, + 0xdd,0x2e,0x43,0x6d,0xf5,0x56,0x49,0x63,0x06,0xfb,0x1a,0xa4,0x0f,0x98,0xa7,0xab, + 0x01,0x33,0xa9,0x3d,0x2d,0x19,0xda,0xc2,0x1d,0xd8,0x44,0x2d,0x16,0x05,0x61,0x73, + 0xa7,0xbe,0xa1,0xf5,0xc2,0x57,0x10,0x11,0x74,0xe3,0x98,0xf9,0xea,0x57,0x79,0x3a, + 0x93,0x90,0xaa,0xb0,0x52,0xa1,0x6f,0xff,0xf1,0x0a,0x7d,0xcd,0x22,0x28,0x39,0xec, + 0xb6,0x95,0x4c,0x07,0x3a,0xc9,0xbf,0x5b,0x57,0x7d,0x18,0xea,0x15,0x25,0x68,0x3f, + 0xa7,0xd4,0x33,0x58,0xd4,0x59,0x71,0xee,0xec,0x3b,0x3f,0x98,0xca,0x98,0x91,0x37, + 0x14,0x25,0xfa,0x51,0xee,0x0a,0xef,0x84,0x4f,0x8e,0x30,0x5f,0xcf,0xb5,0x4f,0xde, + 0x51,0xef,0xf6,0x32,0xca,0xa3,0x51,0x14,0x23,0x10,0xe9,0x07,0xf8,0xaf,0xf3,0x70, + 0x12,0x05,0x8e,0x6b,0xc8,0xc0,0x0e,0x19,0xf5,0xb7,0x28,0x9b,0x4b,0x96,0xa8,0x54, + 0x0f,0xfc,0x1a,0x3d,0x42,0x9a,0x1a,0x5a,0x5a,0x6a,0x57,0x20,0x02,0x14,0x54,0x45, + 0x0a,0x77,0x5d,0xb3,0x4f,0x44,0xab,0xe4,0x8b,0x36,0x55,0x31,0x43,0x3f,0x61,0xa7, + 0x05,0xd6,0x91,0x2b,0x6a,0xb7,0x6d,0x68,0x8c,0xaa,0xd6,0x8e,0xe1,0x46,0x45,0xff, + 0xa3,0xaf,0x9c,0x8a,0xe5,0xee,0x2a,0x37,0xa0,0xc1,0x51,0x2a,0xde,0x3f,0xdb,0xbf, + 0xc8,0x20,0x8c,0xe1,0x94,0x55,0x2c,0x32,0x3b,0x68,0xf2,0x41,0xfd,0x8b,0x72,0x56, + 0x2a,0x34,0x25,0x6b,0x5b,0xc3,0x36,0xdd,0x04,0x84,0x35,0xcc,0xb4,0x6d,0xf9,0x89, + 0xb5,0x11,0x00,0x2a,0x79,0xd5,0xeb,0x62,0x4f,0xdd,0x43,0xb0,0x1c,0x13,0x58,0x40, + 0xb8,0x4b,0x71,0x2b,0xb8,0x05,0x31,0xac,0xd2,0x7c,0x88,0x95,0x03,0xad,0xe2,0x66, + 0x2c,0x4c,0x87,0x62,0x9d,0x92,0xdb,0xa8,0xe5,0x28,0xd8,0x48,0xd5,0x2c,0xec,0x76, + 0x0d,0x6e,0x81,0x4c,0x99,0xd9,0x45,0x9d,0xaf,0xd6,0x30,0x59,0xb7,0x69,0x3c,0x06, + 0x15,0xa3,0x8b,0xcc,0x57,0xfe,0x6b,0x9f,0x4d,0x66,0x3d,0x4e,0x5f,0xb3,0x04,0xbf, + 0x2c,0x48,0xbd,0x2a,0xa3,0xff,0xc3,0x40,0xf7,0xfb,0xc1,0x14,0x30,0x74,0x2b,0x45, + 0x94,0xb3,0xb9,0x39,0x58,0xe5,0x6d,0xc1,0x31,0x3b,0x4d,0x1c,0x18,0x34,0x9f,0xa5, + 0xa9,0xd8,0xe3,0x68,0xe6,0xe3,0x59,0x38,0x0f,0xfb,0xb4,0x67,0x3c,0x10,0x6c,0xa6, + 0xff,0xf0,0xd5,0xce,0xcb,0x57,0x2f,0x77,0xe1,0x9b,0xa8,0x19,0xbe,0x5f,0xbd,0x3a, + 0x7c,0x75,0x30,0x20,0x9e,0xd9,0x7f,0xd8,0x39,0xe8,0xec,0x75,0x76,0x06,0x82,0x03, + 0xf5,0x1f,0xee,0x76,0x7b,0x87,0xbd,0xce,0x40,0x58,0xd4,0xfd,0x87,0x4f,0x8e,0x9e, + 0xec,0xee,0xee,0x0d,0x98,0xbb,0xf5,0x1f,0x1e,0x1d,0x1d,0xf5,0x0e,0x77,0xc5,0x67, + 0x33,0x4f,0x61,0xaa,0x1f,0xbe,0xdc,0x7b,0xd9,0x39,0x42,0x10,0x34,0x72,0xf8,0xde, + 0x3e,0x3a,0x3c,0x7c,0x39,0x20,0xd9,0x80,0xcd,0xf5,0x0e,0xbb,0xf8,0x35,0x0a,0xe0, + 0xeb,0xc5,0xde,0xf6,0x61,0xe7,0x70,0x80,0x6e,0xcf,0xfe,0xc3,0xee,0x8b,0x27,0x07, + 0x3b,0x2f,0xe0,0xe3,0x2a,0xc8,0x92,0xfe,0xc3,0xbd,0x1e,0xe8,0x44,0x58,0x32,0x0f, + 0x40,0x47,0xcd,0x6f,0x00,0x45,0xf3,0xe6,0x32,0xf2,0x9b,0xc1,0x62,0x81,0x8d,0x51, + 0x82,0xdf,0x38,0x09,0x2f,0xd2,0xd0,0xf9,0xf9,0x4d,0xc3,0xff,0x98,0x8e,0xd2,0x22, + 0xf5,0xb1,0x78,0x33,0x07,0xbe,0x3d,0x1d,0xb0,0xe5,0xd0,0x5f,0x46,0xf4,0x97,0xc4, + 0xbc,0xdf,0x38,0x0c,0xf2,0x71,0x80,0x64,0xf8,0x0e,0xd2,0x1a,0x00,0xe0,0x95,0xf8, + 0xf5,0x2e,0x4c,0xe2,0xd4,0x3f,0xa4,0xed,0xff,0x20,0xf7,0x1b,0x0c,0x4f,0x64,0x2a, + 0x00,0x03,0x21,0x7b,0x04,0xa7,0x26,0x2c,0x7a,0x03,0xe5,0x17,0x14,0xc9,0x34,0x76, + 0x10,0x41,0x18,0x8b,0x70,0x36,0x09,0x8a,0xa0,0x59,0xe0,0x64,0x0c,0x27,0x41,0xf6, + 0xf9,0xdc,0x9e,0x1f,0x4c,0xd2,0xd3,0xd3,0x3e,0x6c,0xbf,0x68,0x3f,0xd5,0xd3,0xd3, + 0xd9,0xee,0x00,0xab,0x94,0xd3,0xf3,0xf2,0xe8,0xe5,0x93,0xa3,0x17,0x7a,0x7a,0x0e, + 0xb6,0x9f,0xbe,0xd8,0x3b,0x30,0xa6,0xe7,0xe0,0xc9,0xf6,0xee,0x8e,0x9a,0x9e,0xee, + 0x8b,0xee,0x93,0x6e,0xc7,0x9e,0x9e,0xce,0xab,0xce,0x61,0x67,0x4f,0x4f,0x4f,0xf7, + 0xa0,0xbb,0xdb,0x6d,0xcb,0x09,0x79,0xf5,0xea,0xc9,0x41,0xaf,0x27,0x26,0xa4,0xf7, + 0xe4,0xc5,0xde,0x93,0x57,0x6a,0x42,0x8e,0x9e,0x1e,0xb4,0xb7,0x5f,0xdd,0x6d,0x9c, + 0x31,0x57,0x3f,0xb7,0x16,0xff,0x83,0x68,0x8e,0xfb,0x92,0x41,0x52,0xdc,0x6d,0x8c, + 0xd2,0xc9,0xcd,0xad,0xdc,0xd7,0xbb,0xb7,0xd2,0x47,0x52,0xb6,0xdf,0x01,0x69,0xbf, + 0x05,0xca,0x85,0x10,0xa2,0x38,0x99,0x80,0xdd,0xab,0x70,0xf4,0x39,0x02,0xa3,0x90, + 0xe4,0xf0,0x1c,0x48,0x7c,0x86,0xfc,0x09,0x1a,0x8b,0x40,0xde,0x81,0xf5,0x3b,0xb9, + 0xdb,0x08,0x6e,0x2d,0x47,0xca,0x5d,0x7f,0x9a,0x8e,0x97,0x79,0x93,0x84,0x11,0x30, + 0x10,0x58,0x6b,0xa8,0xba,0x92,0x79,0x6c,0x4a,0x69,0x72,0x1f,0x88,0xcc,0x66,0x3a, + 0x9d,0xe2,0x82,0x62,0x9e,0x87,0xe2,0xd4,0xdc,0xae,0x6c,0xaf,0xdb,0xae,0x24,0xf7, + 0x21,0xf2,0xcb,0x1d,0x62,0x40,0xb3,0xae,0x1c,0x3f,0x49,0x09,0xe0,0x4c,0x83,0xaf, + 0x18,0xe1,0xb5,0x1a,0xe5,0x3c,0x07,0x7e,0x91,0x2f,0x47,0xac,0x8d,0x90,0xd9,0x69, + 0x1a,0xc4,0x66,0x1d,0xe1,0xf1,0x6a,0x2d,0xa6,0x30,0x75,0xd6,0x16,0x1c,0xee,0x28, + 0x60,0x72,0x98,0x65,0x75,0x5b,0x73,0xc8,0x8e,0x6e,0xeb,0xfc,0x6b,0x5a,0xe9,0x77, + 0xf6,0xbe,0xcd,0x42,0x7b,0xfa,0x54,0x0c,0x90,0x94,0xe5,0x2d,0x42,0xce,0xaa,0x7e, + 0x0b,0x4d,0xa7,0xde,0x07,0x01,0x5d,0x6b,0xc1,0x68,0x6a,0x0c,0x2a,0x18,0xd4,0xa0, + 0x32,0x48,0x2a,0x2f,0x46,0x5a,0x53,0x07,0x07,0x3c,0xa8,0x60,0x80,0x6a,0xd5,0x6e, + 0xe9,0x4e,0x81,0xce,0xc2,0xec,0x0f,0xb0,0x26,0x3a,0xbb,0xb6,0xf2,0xdc,0xdd,0x55, + 0xf8,0x6c,0x8a,0x7d,0xa7,0x1a,0x9c,0xde,0x7b,0xce,0x45,0x47,0x9d,0xa0,0x3c,0x8d, + 0xab,0x7a,0x6c,0xc8,0x43,0x32,0x83,0x6b,0xe6,0x03,0x83,0x1a,0x9a,0x93,0x70,0x9c, + 0x66,0xbc,0x51,0xc4,0x18,0xc1,0x40,0xaa,0x75,0x18,0x51,0x8e,0xa8,0x1a,0x05,0x5b, + 0x6b,0x4b,0xb3,0x8e,0xd4,0x6e,0x14,0x8f,0xd0,0x8b,0x83,0xc4,0x7a,0x79,0x71,0x94, + 0x5c,0x3d,0xcd,0x56,0xbb,0x83,0xdb,0xbe,0x0b,0xc5,0x64,0x80,0x58,0xe2,0x28,0x2f, + 0x34,0xd3,0x31,0x0d,0xd6,0x1c,0x40,0x63,0x6c,0x02,0xfb,0x51,0xbe,0x86,0x77,0x01, + 0x6a,0x3f,0x8e,0xbe,0x47,0xcf,0x26,0xa7,0x5e,0x5b,0x38,0xc4,0x24,0x8e,0xf7,0x0c, + 0x0b,0x1a,0x0b,0xb4,0x07,0x2a,0x2e,0x02,0x84,0xfd,0xbc,0xb6,0x1f,0xc4,0xad,0xa1, + 0x33,0xa4,0xc1,0x9c,0x61,0x78,0xdb,0x30,0x0b,0x92,0x8b,0xf0,0xfc,0xb6,0x66,0xb7, + 0xb9,0xb4,0x0b,0xaf,0x54,0x0d,0x63,0x43,0x59,0x2f,0xe6,0xd2,0xce,0x7f,0x8d,0x5e, + 0x62,0x54,0x2b,0xa9,0x4e,0x83,0x9a,0x65,0x55,0xc7,0x01,0x6a,0x5d,0xd2,0x42,0x54, + 0xd6,0xf3,0xfd,0x5e,0x1b,0x7a,0xb6,0xad,0x88,0x9d,0xf9,0xbe,0xb1,0x97,0x6a,0x36, + 0xec,0xb4,0x3a,0xbd,0xdc,0x18,0x42,0x7f,0x86,0x1b,0x8f,0x86,0x3e,0xc5,0x09,0x75, + 0x3c,0x80,0x1a,0xfc,0x4a,0x00,0x83,0x6f,0x7d,0x09,0x58,0x55,0xbe,0x51,0x53,0x68, + 0x05,0xd3,0x01,0xb5,0x4c,0x46,0xd1,0x29,0xf3,0xaa,0xb5,0xd3,0x2b,0xed,0x84,0xda, + 0x01,0x00,0xd2,0xb1,0x2a,0xe6,0x8d,0xd5,0xe8,0xbd,0xf2,0x86,0x3b,0x9b,0x5c,0xfe, + 0x0a,0x4f,0x05,0xa0,0xf3,0xae,0x92,0xb5,0x66,0x3b,0xde,0xdc,0x3b,0x35,0x58,0xd4, + 0xba,0x39,0x55,0x1b,0x2d,0x7a,0x8b,0xba,0x2b,0x5c,0x12,0x5d,0xbd,0x9b,0xde,0xad, + 0x6e,0xca,0xde,0x6f,0x37,0x9c,0xdc,0x09,0x6b,0x76,0xc3,0x85,0xe3,0x4e,0x6c,0x75, + 0xfb,0xc2,0x42,0x2b,0xed,0x06,0xb4,0xe5,0x36,0xb0,0x2f,0xec,0xc6,0x75,0x9a,0xf7, + 0x6d,0x65,0xd0,0x22,0xb4,0xc8,0x97,0x3b,0xa4,0x7e,0x69,0x67,0xc1,0xb7,0xa3,0x3c, + 0x7c,0xd3,0x8f,0x58,0xdb,0x52,0x29,0xd0,0x68,0x85,0xa7,0xbe,0xc7,0x86,0x97,0xe5, + 0x15,0x47,0x66,0x58,0x5d,0x25,0x65,0x47,0xb8,0xe1,0xd6,0xa9,0x20,0x54,0x90,0xbb, + 0xdc,0x62,0xb1,0x05,0x9e,0xb0,0xe9,0x6c,0x47,0x35,0x2a,0x33,0x0e,0x6a,0x22,0x96, + 0x41,0xb7,0x57,0xe2,0xd2,0x3b,0x6d,0xc9,0x83,0x1d,0x11,0x4c,0xa5,0x4c,0xe0,0x5d, + 0x8a,0xd0,0x41,0xbd,0x64,0x3a,0x45,0xe1,0xe0,0x98,0xf3,0x65,0xa4,0x62,0x65,0x3b, + 0x05,0x35,0x1f,0xb5,0x5e,0x7a,0x77,0x1b,0xcf,0xb6,0x44,0x8c,0xd9,0xb3,0x2d,0x11, + 0xd2,0x8b,0x9a,0x26,0x06,0xc8,0x01,0x15,0x3a,0x63,0x50,0xda,0xf3,0xe1,0x26,0x72, + 0x67,0x07,0xcd,0x7b,0x19,0xf9,0x1b,0x66,0xfb,0xcf,0x66,0x9d,0x7d,0x11,0xec,0x06, + 0xbf,0x9e,0xe1,0xcc,0xc8,0xe2,0xd0,0xc6,0xa6,0x13,0x4d,0xe0,0x47,0x41,0x31,0xbe, + 0x90,0xb5,0xcf,0xe0,0xa1,0x1e,0x00,0xe8,0xee,0x9f,0x70,0x90,0x1b,0x24,0x76,0x21, + 0x61,0x19,0xcb,0x9a,0xd8,0x5f,0x6c,0x23,0x8e,0xb0,0x23,0x44,0x3c,0x04,0x68,0x1e, + 0x8d,0x39,0x34,0x63,0x53,0x96,0xd4,0xf4,0xbb,0xe9,0x48,0x46,0x80,0x01,0x73,0x46, + 0x37,0xb8,0xc8,0xa6,0xdd,0x37,0x24,0x74,0xa3,0x53,0xf4,0xa7,0xae,0x1a,0xad,0xee, + 0xcd,0xfd,0x77,0xd1,0x38,0x4b,0x17,0x33,0x98,0x4a,0x55,0x76,0x8b,0xfb,0x25,0x6b, + 0x89,0xee,0xe1,0xa6,0x9b,0xea,0x1c,0x92,0xc2,0x66,0x3d,0x74,0x0e,0x69,0x33,0x32, + 0x27,0xd1,0xa5,0xea,0x1b,0xd3,0x3c,0x21,0x00,0x9b,0x97,0xe9,0x9a,0xdd,0x6c,0x3a, + 0xd3,0x34,0xa3,0xf6,0x7e,0x84,0xe9,0xd9,0xdc,0x7f,0x83,0x92,0xcc,0xb9,0x80,0xdf, + 0xcf,0xb6,0xa8,0x0a,0x54,0x25,0xe9,0x26,0xbb,0x45,0xc5,0x1c,0x12,0x75,0x9b,0x24, + 0xeb,0x36,0x1d,0x10,0x3f,0xc3,0xcd,0x0e,0xfc,0x0d,0xae,0xe1,0xef,0xee,0xa6,0x83, + 0x6b,0x68,0xb8,0xd9,0x6e,0xf5,0xaa,0x98,0x34,0xa0,0xfc,0x39,0x88,0xd5,0xf8,0x70, + 0x4d,0x3a,0x22,0xf8,0xcf,0x18,0xca,0x16,0x8c,0x05,0xfe,0x2c,0xa8,0x1a,0x07,0x4f, + 0xbe,0x07,0x2d,0x4a,0x55,0x43,0x22,0xc6,0xe6,0x80,0xde,0x60,0x50,0xb6,0x9b,0x05, + 0xc1,0x2c,0x10,0x06,0x4d,0x7d,0x1c,0x11,0x0c,0x00,0x0f,0x6b,0x7b,0xd3,0xa6,0x85, + 0x60,0x39,0xf9,0x8f,0xa1,0x85,0x17,0x59,0x7a,0x05,0x36,0xb0,0x03,0xbd,0x0a,0x41, + 0x84,0x42,0x77,0x80,0xc5,0x38,0x44,0x1c,0x0e,0xd8,0xcf,0xeb,0x08,0x04,0xe3,0xc5, + 0xef,0x49,0x20,0x84,0xa4,0xfd,0xd3,0x59,0x68,0xb4,0x33,0xca,0x80,0x2c,0x72,0x0a, + 0x0f,0x4d,0xaf,0x12,0x98,0xb9,0x05,0xd2,0xc9,0xc0,0x01,0xfb,0x57,0xb7,0xef,0x2c, + 0xf3,0x30,0xa7,0x24,0x4c,0x80,0x1e,0xa7,0x57,0x2d,0xe7,0x04,0x38,0xa0,0x93,0x5f, + 0x45,0xc5,0x78,0xe6,0x04,0xb9,0x73,0xf0,0xf3,0x11,0xe4,0x72,0x3d,0x64,0xeb,0xff, + 0xf8,0xd7,0xff,0x95,0x3b,0xef,0x5f,0x9e,0x7e,0xfa,0xf0,0xf1,0x27,0x27,0x07,0x23, + 0x32,0x4c,0x5a,0x1b,0xcf,0x02,0x67,0x96,0x85,0x53,0xe8,0x4a,0x51,0x2c,0xf2,0xfe, + 0xd6,0xd6,0x45,0x54,0xcc,0x96,0x23,0xe0,0x9b,0xf3,0xad,0x30,0xb9,0x98,0x2f,0x93, + 0x8b,0x2d,0x23,0xe8,0x75,0xab,0x80,0x6a,0x5b,0xc8,0x2e,0xb6,0x30,0x24,0x31,0xdf, + 0x5a,0xe8,0xbc,0x26,0x05,0xba,0x36,0xd5,0x48,0x1e,0x82,0x99,0x3a,0x99,0xc3,0xcc, + 0x15,0xc0,0xf7,0xf0,0xc4,0xc0,0x2f,0xa3,0x38,0x48,0x3e,0x6f,0xee,0xff,0x08,0xcc, + 0xbd,0x30,0xc7,0xfc,0x6c,0x2b,0x30,0x08,0x8b,0x88,0x62,0x6b,0x19,0x33,0xf7,0x78, + 0xc7,0xe3,0xd7,0xe8,0x1d,0x05,0xd9,0xbb,0xfc,0x42,0x93,0x27,0xfe,0xce,0x52,0xa4, + 0x32,0x8e,0x31,0x36,0xb9,0x50,0x97,0x28,0xd4,0x42,0xf6,0x2b,0x50,0x7f,0xb0,0xf1, + 0xb9,0x5a,0xec,0x34,0xb3,0x16,0x76,0x5b,0xce,0xe1,0x0c,0xd7,0x4f,0xee,0xe4,0xc1, + 0x65,0xe8,0x14,0xa9,0x46,0x22,0x22,0xf6,0x26,0x5d,0xc2,0xb4,0x7c,0x0e,0x31,0x75, + 0xde,0x62,0x52,0xc6,0x95,0x4d,0x93,0x3f,0x01,0x6e,0x48,0x0e,0xdf,0x4d,0xfa,0xb3, + 0x69,0xaf,0x7a,0x11,0xc7,0x59,0xa1,0xf2,0x22,0x5d,0x4d,0xe6,0xff,0x1f,0xa8,0xfb, + 0x00,0x1a,0x74,0x88,0x45,0xac,0x23,0x64,0x19,0x2d,0x6a,0xaf,0xe9,0x7d,0x8c,0xba, + 0x76,0x46,0xe9,0x35,0xb0,0xa7,0x18,0xa6,0x9d,0xc9,0x30,0x4e,0x97,0x93,0x24,0xcc, + 0x81,0x66,0xa7,0xce,0xd5,0x2c,0xc0,0xf0,0x66,0x07,0x84,0x40,0x96,0xb7,0x9c,0xa3, + 0x2c,0xb8,0x70,0x02,0xc0,0xe2,0xc2,0x01,0xdc,0xb3,0xca,0xee,0x84,0x93,0x0b,0x46, + 0x2c,0xe2,0x31,0x88,0x63,0x00,0xb6,0xcc,0x1c,0x54,0x02,0x31,0x70,0x1a,0xb0,0x3f, + 0x69,0xd5,0x71,0x4d,0xf2,0x13,0x6f,0x7e,0x27,0x27,0x05,0x52,0x05,0x71,0x0e,0xa3, + 0xa7,0xbf,0x75,0x4c,0x54,0x94,0xa8,0xe1,0xa1,0x6d,0xc9,0x43,0x35,0x0b,0x6d,0x77, + 0x36,0x2d,0x6c,0x51,0xdd,0x4f,0xa0,0xfd,0xdc,0x93,0x75,0x7e,0xcf,0x10,0x54,0xc1, + 0x23,0xfe,0x51,0x37,0x08,0x59,0xe6,0xbb,0x46,0x21,0x2a,0x7f,0xc3,0x30,0xec,0x3f, + 0x72,0x1d,0x48,0x15,0x45,0x01,0x51,0x09,0x50,0x88,0xf5,0x24,0x55,0x0e,0x61,0x72, + 0x52,0x69,0xb1,0x04,0x17,0xac,0x5a,0xc0,0x8f,0x76,0x89,0xe0,0x85,0x2e,0xb9,0xb9, + 0xff,0x53,0x47,0x52,0xbd,0x00,0xa1,0x60,0xd5,0xe0,0xd9,0x84,0xd8,0x59,0x09,0xb1, + 0xfb,0x9d,0x10,0xbb,0x2b,0x21,0x6e,0x7f,0x27,0xc4,0xed,0x95,0x10,0x77,0xee,0x03, + 0x91,0x10,0x7c,0xa9,0xa7,0x00,0x7e,0xb2,0xeb,0xd1,0x6e,0x91,0x83,0x04,0x37,0xcb, + 0x89,0x14,0xc4,0x67,0x70,0x2a,0x95,0x01,0xd6,0x2e,0x08,0x16,0x10,0xbf,0xe8,0x96, + 0xc5,0x94,0x65,0x9c,0xc3,0xa2,0x44,0x3d,0xdb,0xa1,0xdf,0xb9,0xc1,0x4d,0x6a,0x2a, + 0xeb,0x9a,0x49,0x1a,0xe5,0x37,0x9b,0xfb,0xef,0xf1,0x0f,0x30,0xf1,0x74,0x7e,0xdf, + 0x8a,0xf9,0x55,0x18,0x03,0x1f,0x3b,0xc1,0x73,0x18,0xf4,0xdb,0xa8,0x58,0x45,0xa9, + 0x0c,0xac,0x2b,0xb3,0x52,0x11,0xfe,0xa6,0x19,0x67,0x55,0x36,0x17,0x21,0x68,0xe4, + 0x79,0x74,0x91,0x04,0x71,0x2d,0x5b,0x95,0xf1,0x71,0x25,0x18,0x8c,0x79,0x4c,0xb7, + 0xc1,0xfd,0xd4,0x71,0xc0,0xb4,0x80,0x95,0xba,0x62,0x15,0x99,0x2b,0x83,0x03,0x09, + 0xad,0xc5,0x51,0x5d,0x6b,0x02,0xba,0x88,0xc2,0x2b,0xcd,0xe1,0xb8,0x7e,0x06,0xa5, + 0xf9,0xc5,0x64,0x86,0x5f,0xe5,0xd5,0x25,0xcc,0x9b,0x7a,0xc4,0xc8,0xd8,0x34,0x07, + 0xf9,0x82,0xb9,0x00,0xcb,0x85,0x5e,0x7f,0x71,0x6c,0xd6,0xf1,0x28,0x19,0xe5,0x8b, + 0xc1,0x6a,0xca,0x5d,0x31,0xf7,0xd5,0xfe,0x76,0xfe,0x99,0xfe,0x76,0xff,0xfd,0xfb, + 0xdb,0xfd,0x67,0xfa,0xbb,0xfd,0xef,0xdf,0xdf,0xed,0x7f,0xa6,0xbf,0x3b,0x7f,0x7c, + 0x7f,0x05,0xc1,0x1b,0x2a,0x93,0xb1,0x51,0xaa,0x96,0x98,0x74,0x28,0x6d,0x3a,0xd6, + 0x61,0x9c,0xcd,0x69,0x80,0xfc,0x69,0x83,0x8e,0x69,0x0d,0x37,0x3f,0x2e,0x93,0x1c, + 0xd5,0x11,0xbd,0xb0,0x1d,0x3c,0xaa,0xb6,0xbc,0x98,0xc1,0xdf,0x28,0x77,0x70,0x29, + 0xf5,0x49,0xfd,0x66,0x89,0xee,0x3b,0x42,0x26,0xfa,0x0e,0x6d,0xac,0x93,0xba,0xc8, + 0x8b,0x98,0x55,0xa8,0xcd,0x7d,0xd1,0x91,0xf5,0x3c,0x08,0xb5,0xc7,0xd2,0xfa,0x94, + 0x61,0x8b,0x96,0x04,0x96,0xbe,0x8c,0x4d,0xb3,0x14,0x25,0xec,0xff,0x04,0x5d,0x73, + 0x3a,0x36,0x1f,0x92,0x55,0xde,0x85,0x45,0xf0,0xed,0x8a,0xc7,0x38,0x8d,0x1d,0x19, + 0x2e,0x50,0xe6,0x8e,0xca,0x75,0xb2,0xb9,0xff,0x31,0xcc,0x17,0x69,0x92,0x87,0x5c, + 0xb6,0x4e,0x31,0x93,0xb1,0x7f,0x25,0xf1,0x4e,0xc9,0xab,0xe4,0xbb,0x0e,0x42,0xd8, + 0xac,0xcb,0xc0,0xe8,0xbb,0x1a,0xf1,0x43,0x79,0x42,0x12,0x80,0x06,0xc3,0xbb,0x59, + 0x9b,0x35,0xe4,0x43,0x9d,0x3c,0xa1,0x6c,0xa5,0x18,0xaf,0x92,0x2e,0x65,0xa0,0xb3, + 0x20,0x5b,0xac,0x86,0x89,0xb9,0xdf,0x0a,0x12,0x88,0x30,0x5e,0x09,0xf1,0x55,0x10, + 0xc7,0xdf,0x0a,0xf0,0x22,0x28,0xc2,0x95,0x00,0x7f,0x84,0xcc,0x6f,0x1e,0x34,0xba, + 0xd7,0x56,0x0f,0x1a,0x73,0xbf,0x15,0x64,0x90,0x8d,0x57,0x4f,0xcd,0x01,0x64,0x56, + 0x01,0x96,0xd4,0x17,0xe9,0xf3,0x53,0xa4,0xad,0x13,0x94,0x2a,0x53,0xe9,0x9f,0xc5, + 0x29,0xa8,0xc2,0xbb,0x28,0x01,0xcb,0x90,0xf9,0x02,0x69,0xcc,0xc3,0xcd,0x57,0xe1, + 0x15,0x18,0x1b,0x62,0xd0,0x8f,0x1e,0xee,0x3d,0xe9,0x76,0x07,0x75,0x56,0x10,0x95, + 0x78,0x6f,0x2d,0xad,0xcd,0xfd,0xed,0x95,0x2d,0x1d,0xc7,0xe5,0x86,0xde,0xa5,0x59, + 0x28,0xdb,0x79,0x5c,0x1d,0xa9,0x6a,0x88,0xd6,0x4a,0x8d,0xbd,0xf5,0x75,0x85,0x5b, + 0x14,0xbf,0x24,0x57,0x53,0xed,0x1a,0xc7,0x20,0xa0,0xd2,0x22,0x23,0xdb,0xa2,0x2e, + 0x4d,0xac,0xe2,0x15,0xac,0xe0,0x83,0xc1,0xf9,0xaa,0x9c,0x08,0xf8,0xa2,0x30,0x57, + 0x2c,0x74,0xd5,0x71,0x20,0x51,0x9c,0xc2,0x56,0x54,0x71,0x95,0x60,0xf7,0x4b,0x84, + 0xf3,0x6d,0xee,0xd7,0x00,0x40,0x3f,0xad,0x59,0x9f,0xbe,0xeb,0x0a,0xbe,0x4d,0xcd, + 0x62,0x1c,0x17,0x53,0x5b,0xf0,0x75,0xb4,0xae,0x60,0x15,0xc1,0x3a,0x4a,0xae,0xca, + 0xae,0x54,0x9e,0x58,0x14,0x71,0x4a,0x56,0x18,0xfd,0x9e,0x45,0x68,0x89,0xed,0xb7, + 0x81,0xf8,0xba,0x9d,0xce,0xa0,0xb3,0x72,0x59,0x7d,0x0d,0x08,0xba,0xf3,0x14,0x98, + 0x56,0xef,0xdb,0xe0,0x90,0x2b,0xd0,0xe8,0x4e,0xab,0xf7,0x7d,0xfd,0x69,0x75,0x7b, + 0x56,0x97,0x9e,0x40,0x9f,0x20,0x4d,0xf6,0xea,0xc9,0xb7,0x76,0xab,0x63,0x83,0xdb, + 0x05,0x68,0x1d,0x05,0x6d,0xf7,0x1b,0x81,0x6d,0x5b,0xb0,0xf6,0xb0,0x6b,0xdb,0x12, + 0xd6,0x5e,0xaf,0xba,0x28,0xab,0x0a,0x87,0x3e,0x31,0xf8,0xef,0xe4,0xa3,0x79,0xb7, + 0x2c,0xea,0xbc,0x33,0x2b,0xa9,0x10,0x23,0x1b,0x4b,0x7e,0x25,0xc2,0xc3,0x0b,0xa0, + 0xdf,0xaa,0x8a,0x84,0x22,0x3d,0x2c,0x9c,0x9f,0x6a,0x26,0x59,0xd5,0x7c,0x67,0x98, + 0xef,0xb2,0x22,0xa0,0x31,0x51,0xac,0x97,0x61,0x08,0x0f,0xe5,0xca,0x3e,0xf2,0xee, + 0xfa,0xbe,0xf2,0x34,0x6e,0x6d,0xee,0x73,0x8c,0x50,0x48,0x7e,0x3f,0x95,0x2c,0xdd, + 0x77,0x7c,0xc6,0x9f,0x7d,0x82,0xa2,0x2e,0xc0,0x42,0x9f,0xa3,0xba,0x6c,0x64,0x7f, + 0x03,0xef,0x4c,0x38,0x7e,0x05,0x3d,0x74,0x86,0x8e,0xba,0x02,0xc0,0x71,0x3d,0xe7, + 0x76,0xa3,0xb1,0xcc,0x91,0xdf,0x66,0xd1,0xb8,0x68,0x0c,0x36,0xf0,0x36,0x92,0xc2, + 0xb9,0x8a,0x30,0xe2,0x05,0xcb,0x5e,0xfa,0x4e,0x9c,0xfa,0x30,0x02,0xcf,0x19,0xee, + 0x3b,0x7c,0x9b,0x43,0x70,0xed,0x62,0x9a,0xba,0xda,0x61,0x16,0xf9,0x0e,0xdf,0x11, + 0xe2,0x5e,0x7a,0xce,0xef,0xbf,0x3b,0x6d,0xcf,0x93,0x90,0xe8,0x70,0x65,0xbb,0x43, + 0xa0,0x08,0x04,0x83,0x46,0xb8,0x6d,0xdf,0xe9,0xa8,0x72,0xc7,0x1f,0x5f,0x9e,0xbc, + 0x3c,0x3d,0x81,0x72,0xb7,0x1b,0xac,0xa3,0xf4,0x9d,0x5b,0x72,0xc9,0xf4,0x9d,0xc6, + 0x28,0xfc,0x12,0x85,0x59,0xc3,0x07,0x5c,0xc3,0x17,0x67,0xc3,0xd7,0x4d,0xbb,0x8f, + 0x40,0x6e,0x3a,0x7d,0xa7,0xe3,0x3b,0x8b,0xce,0x35,0x7c,0xb6,0x76,0x7a,0xf8,0xf3, + 0x06,0x7f,0xb6,0xf1,0x67,0x97,0x52,0x7b,0xf4,0x93,0x52,0x9f,0xf6,0x9c,0x3b,0x7f, + 0x83,0x54,0x96,0xbe,0xb3,0xb2,0x0d,0xcc,0x86,0x8f,0xfa,0x36,0x3a,0x6d,0xd5,0xc6, + 0xae,0x6e,0x63,0x5b,0xb6,0xd1,0x69,0xb5,0xdb,0xd8,0x06,0xea,0x30,0xd0,0xc4,0xaa, + 0x36,0x30,0x1b,0x9b,0xa0,0x36,0x3a,0xdc,0x46,0xbb,0x6e,0x1c,0x4f,0x6b,0xc7,0xd1, + 0xa6,0x71,0xa0,0x5a,0x63,0xb5,0x41,0x92,0x13,0xc1,0x52,0x1b,0x98,0x8d,0x1f,0x49, + 0xdf,0xe9,0xd2,0xb0,0x31,0xb7,0xbf,0xb2,0xb8,0xfc,0x82,0xe2,0xdb,0x58,0x1c,0x15, + 0x12,0x0b,0x3a,0x26,0x50,0x9f,0xa9,0x38,0x7d,0x39,0x77,0x1b,0x77,0x83,0x0d,0x45, + 0x53,0xe1,0x65,0x10,0x1f,0xa2,0x68,0x76,0x49,0x40,0xfb,0xce,0x12,0x89,0x6c,0x09, + 0x13,0x2b,0x48,0xc1,0x5d,0xc2,0xa4,0x47,0x53,0xc7,0x7d,0x40,0x05,0x3c,0x47,0xdc, + 0x71,0xb1,0xe4,0x54,0x4a,0x6c,0xd1,0xfd,0x3a,0xc3,0xe1,0x50,0x76,0x09,0x61,0x30, + 0xa1,0x20,0x55,0x0a,0x1a,0x22,0x02,0xa4,0xdd,0x46,0x51,0x2b,0x21,0xea,0xeb,0xfa, + 0xf8,0xdf,0x1e,0xb4,0x22,0x20,0x2b,0x42,0x4d,0x9c,0x26,0x22,0x9a,0xbe,0xa7,0x71, + 0x9a,0x66,0xee,0xd2,0xf9,0xc1,0x49,0x3c,0xcf,0xd9,0x72,0x38,0x13,0x2a,0xdd,0xd5, + 0x76,0x83,0x86,0xaa,0xfa,0x4a,0x10,0x72,0xd9,0x85,0xe3,0x37,0x00,0x45,0x8e,0xaa, + 0x5c,0x51,0xcc,0xb9,0x1e,0xc0,0x2d,0xcc,0x37,0x8c,0x81,0x88,0x0a,0xfe,0x32,0x55, + 0xd1,0x5c,0xd3,0x2c,0xd3,0xfc,0x3a,0x77,0x88,0x2f,0x84,0x24,0xd7,0xc7,0xe8,0x1a, + 0x97,0x50,0x41,0x4b,0x68,0x1b,0x9a,0x73,0x3b,0xd0,0x5d,0xf8,0x34,0x7f,0x15,0xf0, + 0x3f,0xc0,0x72,0x1e,0x97,0x4a,0x14,0x32,0xaf,0x8b,0x79,0xf2,0xab,0x50,0xa0,0x6f, + 0x34,0xe8,0x3a,0xb0,0xfa,0x17,0x74,0xfc,0xf1,0x57,0x5a,0xbf,0x59,0xd3,0xfa,0x8d, + 0xd9,0x3a,0x82,0xeb,0x0c,0x36,0x62,0xe0,0x88,0x71,0xca,0xf8,0x98,0x45,0x88,0x0f, + 0xa0,0xa5,0x34,0x73,0x5c,0xcc,0xc0,0xef,0xf6,0x00,0xfe,0x3c,0x73,0xba,0x3b,0xf0, + 0xf7,0xf1,0x63,0x8d,0xc6,0x79,0x34,0xc1,0x6e,0x43,0xdd,0xc7,0xc4,0x9d,0xb6,0x9c, + 0x2e,0x4f,0xc0,0xe8,0xda,0x85,0x3c,0x0f,0xea,0x00,0xe5,0x11,0x68,0xf8,0x1c,0x38, + 0x21,0x18,0xb5,0xdc,0x02,0x7e,0xc2,0x2c,0x8b,0xa9,0x94,0x54,0x39,0xba,0x71,0x2d, + 0x60,0x9e,0x67,0x14,0x5a,0xe2,0x6f,0x45,0xe2,0xa3,0xe0,0x73,0xf8,0x76,0x59,0xb8, + 0x82,0x7e,0x65,0x8f,0x62,0xd0,0xf3,0x86,0xce,0xd9,0x79,0xed,0x00,0x86,0xce,0x76, + 0x57,0x8c,0x00,0xca,0xf1,0x75,0x3b,0x82,0x67,0x56,0x56,0x4c,0x04,0xed,0x6f,0x77, + 0xbd,0x56,0x91,0xbe,0x8a,0xae,0xc3,0x89,0xbb,0xe3,0x79,0x9a,0x98,0xa1,0x36,0x76, + 0x86,0x9b,0x7c,0xf7,0xe6,0xfd,0x2f,0x9f,0xde,0xbc,0x3f,0xfa,0xf0,0x09,0x1b,0x6a, + 0xb5,0xbb,0x03,0x23,0xe3,0xc3,0xcf,0xa7,0x9c,0xda,0x93,0xa9,0xc1,0x22,0x22,0x06, + 0x2b,0xc6,0xec,0x4b,0xae,0xeb,0xeb,0x55,0xeb,0xcb,0xd1,0xf9,0x06,0x70,0x5f,0xc2, + 0xf3,0x37,0xa8,0x8b,0x7f,0x0e,0xe2,0x65,0xe8,0xa2,0x19,0x2d,0x96,0xb7,0xe8,0x1b, + 0xa6,0x70,0x38,0xbd,0xf3,0xdc,0x60,0x04,0x3a,0x19,0x8b,0x6f,0xf4,0x79,0xf9,0x2c, + 0xd2,0x2b,0x57,0xb3,0x04,0x90,0x05,0x30,0x6a,0xb1,0xb0,0x05,0x62,0xa8,0x1e,0x6e, + 0xe0,0xd2,0xb2,0x86,0xee,0x82,0xd2,0xe4,0x3b,0x3b,0x34,0x33,0xfe,0x86,0x0c,0x4f, + 0x06,0x44,0x5f,0x86,0xb1,0xef,0x70,0x77,0xc2,0xe4,0x12,0x3b,0x84,0x84,0x20,0x7e, + 0x96,0xe6,0x59,0xb5,0xc9,0xf5,0x80,0x4a,0xa1,0x5c,0x2b,0x4e,0x71,0xd2,0x95,0x78, + 0x03,0xa4,0x21,0x7a,0x30,0x07,0x48,0x46,0x15,0xf1,0xe0,0x27,0x72,0x7f,0xe2,0x17, + 0x24,0x49,0xc4,0xb7,0xa7,0xa7,0x84,0x28,0x4e,0x11,0x15,0x8e,0x20,0x4a,0xc0,0x9c, + 0xd2,0x13,0x58,0x69,0xbf,0xd4,0xb6,0x89,0xf6,0x32,0x9c,0xe0,0xda,0xe3,0x0a,0x8c, + 0x02,0x76,0xab,0x98,0x13,0xb1,0xaa,0x0f,0x50,0xd2,0xea,0x04,0x51,0x3a,0x90,0x43, + 0xab,0x6e,0x3e,0x61,0xf5,0x56,0x2a,0xab,0x96,0xa9,0x61,0x11,0x42,0x84,0xba,0x92, + 0x4b,0x07,0xef,0xa1,0xab,0xc1,0x22,0x37,0xd0,0x9d,0xa4,0xd9,0x3c,0x88,0xa3,0x2f, + 0x21,0x95,0xa1,0xab,0xb2,0x1c,0xb3,0x24,0x81,0x51,0x85,0x40,0xc1,0x99,0x46,0x17, + 0x6e,0x16,0x5c,0x69,0x40,0x3c,0x94,0xbc,0x00,0x73,0x10,0x57,0x3a,0xe4,0x39,0x8f, + 0x1e,0x39,0x07,0x59,0x16,0xdc,0xb4,0xa2,0x9c,0xfe,0x62,0x05,0x0a,0x66,0x81,0x0a, + 0xcf,0xa9,0x04,0x2a,0x0d,0xb8,0x0f,0xf7,0x11,0xed,0xad,0xbe,0x83,0x17,0xcb,0x30, + 0x65,0x80,0xd0,0x3b,0x3b,0x77,0xee,0x14,0x02,0x6e,0x37,0x8c,0x62,0xdc,0x48,0x4b, + 0xa5,0x10,0xe7,0xa6,0xba,0x1b,0x3a,0xfa,0xb7,0x44,0x9c,0xa2,0x8e,0xca,0x67,0xc5, + 0x87,0x94,0x00,0xd6,0x3c,0x80,0x3e,0x3c,0x90,0xa0,0xe4,0x14,0x5b,0x51,0x99,0x33, + 0x45,0xcd,0xdd,0x52,0x4d,0xd9,0x6b,0x54,0x93,0x48,0xa2,0x6d,0x9f,0x03,0x7d,0x2c, + 0x5c,0x97,0x55,0x31,0x1b,0xc1,0x02,0x20,0xd5,0x39,0x8b,0xce,0x01,0xd7,0x02,0x91, + 0x1e,0x0a,0x67,0x92,0xd0,0x8c,0xcf,0xd7,0x7f,0x43,0x1e,0x75,0xb6,0x0b,0x50,0xbb, + 0xbd,0x36,0x14,0x3c,0x83,0x3f,0x3e,0x1e,0x66,0xe1,0x0f,0xf8,0xeb,0x3b,0x3d,0xf1, + 0xd5,0xa3,0xaf,0xce,0x2e,0x7e,0x9e,0x1b,0x32,0x7e,0x11,0x44,0x99,0xcb,0x6a,0xa1, + 0xef,0x90,0x20,0x85,0xd6,0xc2,0x28,0x26,0xaf,0xdf,0x45,0xb0,0xc0,0xe9,0x23,0x1a, + 0x14,0xa3,0xc6,0xa2,0x76,0x31,0x98,0x7f,0xe2,0xc5,0xa2,0x40,0x15,0x8e,0x90,0xa6, + 0x00,0x65,0x9f,0x38,0xf2,0x19,0x37,0x77,0x8e,0xbd,0xc7,0xd2,0x71,0x7a,0xce,0x25, + 0x68,0x7d,0x42,0xb1,0x67,0xb2,0x61,0x82,0xab,0x44,0xbe,0xea,0x16,0x51,0x3c,0x16, + 0x19,0x70,0xd7,0x54,0x09,0x06,0x0c,0x40,0x38,0x53,0xf1,0x7c,0xd9,0xa2,0xc5,0xfa, + 0x6d,0xac,0x13,0xbd,0x96,0xe8,0x5f,0xc8,0x54,0x4d,0xb1,0xa8,0x0a,0xa4,0x53,0xfa, + 0x22,0x85,0x20,0xa5,0x1b,0xf4,0x1a,0x4c,0xb0,0x1b,0x48,0xb0,0xbc,0x38,0x49,0x85, + 0x04,0x74,0xf3,0x6a,0xc3,0xaf,0xbd,0x9e,0xf0,0xa1,0x62,0xa1,0x56,0xab,0x25,0xb8, + 0xb5,0xa0,0x39,0xd0,0xcf,0x50,0x18,0xf4,0x95,0x38,0xb2,0xb3,0x3d,0x47,0xcd,0x39, + 0x86,0xc9,0x42,0x7f,0x46,0x2d,0xfa,0xf1,0xfc,0x39,0x35,0x24,0x33,0xcf,0x66,0x5f, + 0xa0,0x71,0x18,0xe9,0x17,0x68,0x15,0xd1,0x4b,0x93,0x3b,0x6a,0x51,0x32,0x16,0x7e, + 0xfd,0xb7,0x33,0x1a,0xe2,0xf9,0x19,0xd2,0x04,0x65,0x04,0xd7,0x76,0x46,0xe7,0x9c, + 0xc7,0xdf,0x12,0xb0,0xe4,0xef,0x00,0xf0,0x02,0xdc,0x51,0x35,0x45,0x9c,0x10,0x31, + 0x66,0x37,0x45,0xc9,0xd4,0x2f,0x84,0x4f,0xb9,0xf8,0xd5,0x61,0x13,0xc1,0x14,0x44, + 0x1a,0x14,0xe3,0x4c,0x62,0xcb,0x00,0xc6,0x19,0x58,0x1f,0x47,0x8b,0x00,0xb9,0x88, + 0x4c,0x01,0x2a,0x80,0x52,0xe4,0x8f,0x61,0x54,0xf4,0x7a,0x9e,0xd9,0x10,0x48,0x39, + 0xcf,0xe0,0x12,0x26,0x76,0xfc,0x0d,0xe1,0xec,0xa6,0x19,0xdf,0xe0,0x33,0x11,0xd0, + 0x00,0xfd,0x30,0x78,0x86,0x39,0x4c,0xd4,0xd0,0x71,0x6e,0x4b,0x92,0xad,0x4e,0xac, + 0x11,0x3f,0x37,0x06,0x25,0x84,0x2d,0xb6,0xc0,0x32,0x55,0x13,0x93,0x4c,0x29,0x11, + 0x94,0x4c,0xee,0x3b,0xc4,0x70,0x37,0x88,0x3c,0x6c,0x8e,0x39,0x6a,0x41,0xa2,0x87, + 0xa0,0xe8,0x57,0x2b,0x0e,0x93,0x0b,0xa0,0xa5,0x7d,0x10,0xbf,0xcf,0x39,0x49,0xd4, + 0x66,0xbe,0x21,0xf0,0x00,0x92,0x02,0xbe,0x3c,0x17,0x30,0x63,0xdc,0x13,0x27,0x7f, + 0x94,0x0c,0x4a,0xb4,0x35,0x8f,0x5e,0xfc,0xf2,0xea,0xed,0x87,0x0f,0x1f,0x61,0x5e, + 0x9a,0x68,0xc9,0xc0,0xf7,0xc9,0xf1,0xc1,0x7b,0xf8,0xdc,0x79,0x32,0xa0,0x12,0x7f, + 0x42,0xed,0x0d,0xf5,0x34,0xe0,0x65,0x93,0x74,0xbc,0x9c,0x87,0x49,0xd1,0xba,0x08, + 0x8b,0x97,0x71,0x88,0x3f,0x5f,0xdc,0xbc,0x99,0x60,0x36,0x17,0xd6,0x56,0x24,0x19, + 0xb1,0x2d,0xf1,0xcd,0x99,0x93,0x11,0x59,0xaa,0xd7,0x04,0x4a,0x49,0xd7,0x6e,0x1b, + 0xe4,0x18,0x2d,0xf2,0x38,0xbd,0xe8,0xb4,0x5d,0x25,0x5f,0xc5,0x14,0x5c,0x33,0xdb, + 0x85,0x79,0x0f,0x9b,0x3b,0x24,0xd9,0x65,0x9f,0x51,0x1a,0x8b,0xfe,0x8a,0xe6,0x63, + 0x61,0x0b,0x6b,0x1b,0x18,0x95,0x17,0xb4,0x02,0x95,0xa0,0xbc,0x44,0xb1,0x29,0x47, + 0xf9,0xd8,0x82,0xd5,0x6d,0x0b,0x30,0x79,0xc0,0x9a,0x36,0x06,0x75,0x3a,0x9f,0x81, + 0x8a,0x08,0xde,0xf1,0xab,0x16,0x64,0x18,0xa9,0xd8,0xaf,0x46,0x94,0x4c,0x53,0x30, + 0xb4,0xfe,0xe4,0x36,0x38,0x80,0xa7,0x81,0x22,0x5f,0x1b,0x59,0xc9,0x38,0x9d,0x84, + 0xb8,0x87,0xa1,0x95,0x50,0xc2,0x93,0x34,0x1b,0x68,0x6c,0x5a,0xe5,0xa8,0xd8,0x53, + 0x30,0xd9,0x8d,0xbc,0xdf,0x80,0x9e,0x8e,0x5b,0x09,0x30,0xa1,0x71,0xd9,0xd4,0xc1, + 0x02,0x41,0x43,0xe7,0x3c,0x30,0x6c,0x19,0xcc,0xc3,0x2c,0x97,0x27,0x43,0xf0,0x9d, + 0xb3,0x71,0x2b,0x9a,0x9c,0xb3,0xf9,0x5f,0xb5,0x7f,0xb0,0xce,0x42,0x34,0x18,0x4d, + 0x36,0xd0,0x18,0xa6,0x2f,0xa8,0x75,0x03,0x78,0x84,0x7f,0x3b,0xf8,0x2f,0x99,0x42, + 0xf8,0xe7,0x86,0xfe,0x74,0xf9,0xab,0x7b,0x23,0x44,0x9f,0x31,0x03,0xc2,0xee,0x33, + 0xd1,0xdf,0x69,0x0b,0x4d,0x4c,0xe1,0x09,0x4f,0x14,0x08,0x3c,0xcd,0x25,0x8e,0x7e, + 0x43,0x13,0xa0,0x95,0xc7,0xd1,0x38,0x74,0x41,0xc9,0xce,0x17,0x71,0x54,0xb8,0x0d, + 0xbf,0x01,0x5c,0x20,0x21,0x36,0x92,0xe5,0xe1,0x9b,0xa4,0x70,0x7f,0x23,0x7e,0x87, + 0xca,0x1e,0xdb,0x94,0x0b,0x45,0x7d,0x72,0xc0,0x58,0xe2,0x5c,0x60,0x1a,0xa1,0xfe, + 0x66,0x76,0xd2,0x55,0x80,0x2e,0x15,0x14,0x52,0x1c,0xb1,0x97,0x6a,0x66,0xe6,0x02, + 0xe5,0x88,0x9f,0xb2,0x91,0x6d,0x99,0xd8,0x7d,0x67,0xeb,0xef,0x79,0x7f,0xab,0x85, + 0xfb,0x70,0x38,0x96,0xe7,0x55,0x13,0x1e,0xcb,0x27,0x04,0xaf,0x8b,0xd8,0x26,0xd3, + 0xdf,0xb1,0x8d,0x7a,0xb5,0x08,0xba,0x86,0xfb,0x66,0xcf,0x27,0x13,0x98,0xdb,0x58, + 0x18,0x6d,0x00,0x93,0x58,0xd4,0xce,0x24,0x5f,0x02,0xdb,0x0a,0x72,0xdc,0x10,0x74, + 0x6f,0x41,0x10,0x2d,0x3c,0xaa,0x3d,0xb2,0x6b,0xff,0x26,0xd9,0x0b,0xd6,0xdf,0x35, + 0xfb,0x6c,0x7b,0x42,0xc6,0x4b,0xd0,0x5f,0xe6,0xc2,0xa3,0xc3,0x78,0x47,0x67,0xc8, + 0x6f,0x24,0x58,0xc8,0x1f,0xf2,0xdb,0x59,0xf7,0x5c,0xf8,0x43,0x7e,0x3b,0xdb,0x3e, + 0x17,0xfe,0x90,0xdf,0xce,0x76,0xce,0x85,0x3f,0xe4,0xb7,0xb3,0xde,0x39,0x0d,0x01, + 0x79,0x17,0xd2,0x00,0x4e,0x35,0x28,0x57,0x30,0x2d,0xac,0x7c,0xc2,0xb2,0x40,0xdb, + 0x87,0x85,0x66,0x2e,0x0c,0x35,0xba,0x4a,0xf4,0x0b,0x7e,0x6c,0x77,0x5a,0x5d,0xe0, + 0x53,0x7b,0xa8,0xea,0x70,0xfa,0x34,0x0b,0xe6,0xe1,0x2b,0x94,0x66,0xd3,0x0b,0xfc, + 0x43,0xea,0x7a,0xce,0x79,0x45,0x10,0xc5,0x48,0x0e,0x7c,0x3f,0x69,0x2b,0x0b,0xf3, + 0x34,0x06,0x43,0x47,0xac,0xf3,0x22,0x8b,0xc2,0x9c,0x4d,0xda,0x22,0x8d,0x51,0x79, + 0x6d,0x34,0xb0,0x7e,0x70,0x89,0x51,0xdb,0x43,0x87,0x36,0x58,0x05,0x4b,0xa0,0x60, + 0x5f,0x51,0x02,0x38,0x80,0xcc,0xf5,0x31,0xe0,0xee,0xa0,0x20,0xf3,0x91,0x7b,0x19, + 0xe4,0x07,0x4b,0x50,0xcf,0x83,0xe5,0x44,0x97,0x19,0xc7,0x11,0x9e,0xa9,0xe6,0xa6, + 0xf0,0xa2,0x5d,0x5d,0xfe,0xd5,0x9b,0x97,0x6f,0x8f,0x4e,0x48,0xd7,0x6b,0x90,0x0c, + 0x03,0xf8,0x88,0x4d,0xfa,0x0a,0xae,0xd5,0x17,0xc9,0x2a,0xf8,0xea,0xc9,0x2f,0xca, + 0xe3,0x2f,0x16,0x47,0x0d,0xd4,0x3c,0xe5,0x27,0xe5,0xf2,0x27,0x4a,0x30,0xfa,0x30, + 0x35,0xc3,0x1c,0x38,0x2b,0xe0,0x0b,0x07,0x8e,0xeb,0xad,0x48,0x49,0xa9,0x46,0xae, + 0x34,0xbd,0x30,0xf4,0x6b,0x20,0xcf,0x0e,0xd1,0x66,0xbb,0x31,0xc0,0x42,0x4a,0x8b, + 0x16,0x25,0xd5,0xb7,0xb2,0x81,0xb7,0x3d,0x2a,0xc8,0x1a,0xb3,0x84,0x47,0x1f,0x56, + 0x11,0x4c,0x26,0x25,0x58,0xdd,0x77,0xea,0x8e,0x80,0xc8,0x68,0x45,0xde,0x6e,0x30, + 0x52,0x74,0xd6,0x94,0xd3,0xa1,0xb3,0x67,0x53,0xa0,0x3a,0x60,0x47,0xa4,0x60,0xba, + 0x53,0x20,0x3c,0x10,0x89,0x3b,0xd0,0x4f,0x60,0xfd,0xee,0x88,0x72,0xcf,0x3d,0xe8, + 0xb0,0xfc,0xa9,0x1a,0xc5,0xa2,0xde,0xc0,0xb9,0xa3,0xee,0x9d,0x35,0x48,0x15,0x68, + 0x48,0x40,0x52,0x35,0x28,0x0d,0x17,0x8a,0x01,0x47,0x52,0xa5,0x0c,0x66,0x2e,0x04, + 0x38,0xea,0xa8,0x40,0xa4,0x67,0xdc,0x9b,0x07,0x23,0x65,0x53,0x83,0x09,0xd3,0x17, + 0x52,0xba,0x2c,0xc1,0x71,0x89,0x6d,0x6f,0x1b,0x42,0x9c,0x79,0x95,0x54,0x0e,0x25, + 0x64,0x6f,0xe3,0x3e,0x9c,0xb4,0x0b,0x4a,0x11,0x72,0xd2,0x12,0x37,0x8d,0xa6,0x53, + 0x37,0x07,0x8a,0x93,0xac,0x34,0x55,0x1e,0x0f,0xfc,0x5a,0xd0,0x97,0xfb,0x59,0x20, + 0x35,0x95,0x6e,0x8e,0xcf,0x30,0xd0,0xc6,0x10,0x87,0xcb,0x23,0xfd,0xf9,0xe3,0x9b, + 0xc3,0x74,0xbe,0x48,0x13,0x80,0x84,0xe2,0xeb,0xec,0xf3,0x39,0x34,0xe6,0x20,0x60, + 0xf8,0xc9,0x12,0x0c,0x7e,0x0c,0x50,0x81,0xa5,0x56,0x92,0x90,0xe0,0xc2,0x9a,0x00, + 0x96,0x26,0xa0,0x0b,0x57,0x1b,0x56,0x40,0xd1,0x04,0x74,0x80,0x3f,0x81,0xc7,0x62, + 0x22,0x96,0xd3,0xe9,0xf4,0xe5,0x61,0xf7,0xa0,0x6f,0x04,0x15,0x26,0x20,0x1a,0x03, + 0xed,0x36,0x90,0x22,0xf1,0xaf,0x88,0xe3,0xc6,0x9f,0x8a,0xf4,0x28,0x9f,0x28,0xac, + 0x71,0xae,0x49,0x86,0x47,0x07,0x5d,0x82,0x5f,0x5e,0xbd,0xab,0x4a,0x7b,0xaa,0x2a, + 0xf4,0x86,0x9c,0x56,0xd6,0x97,0x14,0x87,0x62,0xe7,0x19,0x70,0x47,0x98,0x50,0x2e, + 0x7f,0x96,0x38,0x7f,0x87,0xd5,0x29,0xb2,0xb1,0x15,0x2c,0x6e,0x90,0x96,0xf6,0x34, + 0x5a,0x94,0x24,0xc7,0x6b,0xa4,0x41,0x27,0x68,0xdc,0x3a,0x09,0x10,0xa0,0x26,0xa6, + 0x01,0x84,0x42,0x89,0x6a,0x82,0x04,0xd5,0x21,0x96,0xb4,0xf2,0x87,0xe5,0x7f,0x4d, + 0x61,0x1d,0x34,0x1e,0x35,0x3c,0xc9,0x5a,0x17,0x69,0x4e,0xb3,0xbd,0xcc,0xd0,0xf5, + 0x92,0x4e,0x6e,0x68,0x5c,0xe2,0xa6,0x63,0x4c,0xbb,0xdd,0x80,0x36,0x67,0x29,0xb2, + 0xf7,0xe3,0x0f,0x27,0xa7,0x0d,0x5f,0x9c,0xe6,0xcb,0xd1,0x98,0x69,0x88,0x1b,0xc2, + 0x9b,0xa7,0x20,0x0e,0x1a,0x28,0xe6,0x16,0x20,0x86,0xc7,0x74,0xf6,0x6f,0xeb,0xba, + 0x79,0x75,0x75,0xd5,0x04,0xa4,0xcd,0x9b,0x00,0x89,0x69,0x06,0x06,0x7e,0xe7,0xd3, + 0x41,0xdf,0x3e,0xb5,0x06,0x9a,0x52,0x18,0x2e,0x02,0xbc,0xbe,0xbc,0x2f,0x39,0x2b, + 0x18,0xeb,0x14,0x9f,0x02,0x2a,0xef,0x28,0xcd,0x8a,0x13,0xfa,0x90,0xb7,0x34,0xc3, + 0xc2,0xa8,0x49,0x75,0x3b,0x68,0xf0,0xe2,0xc2,0x86,0x25,0x10,0x4e,0xa3,0x24,0x9c, + 0x6c,0xc8,0xfb,0xbc,0xdd,0x4c,0x70,0x0d,0xf2,0x55,0xd3,0x1d,0xcd,0x4e,0xfd,0x25, + 0xcd,0x12,0x51,0xf2,0x4a,0x66,0x5e,0x38,0x9a,0x27,0x06,0x28,0x1a,0xb4,0x6b,0x44, + 0x08,0x0f,0xfc,0x23,0x5a,0x12,0x0d,0x91,0x40,0x00,0xac,0x08,0x39,0x85,0xe3,0x74, + 0x68,0x4a,0x91,0x36,0xf4,0xe2,0x1b,0x6c,0x80,0xca,0x48,0x57,0xb6,0x43,0xee,0x83, + 0x07,0x58,0x4c,0x38,0xd4,0x79,0x1e,0x64,0x33,0x28,0x7a,0xa0,0x22,0x29,0x97,0x52, + 0x0c,0xe9,0xce,0xe2,0x04,0xba,0x7c,0x55,0x3c,0x07,0xa2,0x13,0xf9,0x13,0x04,0xbb, + 0x57,0xb6,0xb0,0xa7,0x08,0x1f,0xd1,0x0d,0x6c,0x13,0x7f,0x82,0x16,0x31,0x46,0x4a, + 0x93,0x22,0xeb,0x08,0x16,0x1c,0x5f,0x1d,0x3e,0x10,0x52,0x93,0x2e,0x03,0x27,0xc5, + 0x47,0x26,0x88,0x6c,0x76,0xb6,0x63,0xf7,0x3c,0xea,0x68,0x03,0x91,0x35,0xc1,0xa5, + 0x97,0x7e,0x6e,0x10,0x4f,0x15,0x1d,0x87,0x9f,0x52,0xaa,0x96,0xc6,0x4f,0x88,0xb9, + 0x45,0x8c,0x4f,0xc0,0xa0,0x26,0xec,0xfb,0x8e,0xec,0xbc,0x31,0xf8,0xc7,0x8f,0x19, + 0xc2,0x33,0x56,0x8f,0x92,0xb4,0xa0,0x99,0x99,0x38,0x37,0x21,0x58,0x49,0x3a,0x62, + 0x7d,0x12,0x4d,0x1c,0xcc,0x0c,0x92,0xfc,0x2a,0xcc,0x5a,0xce,0x69,0x76,0x83,0x82, + 0x29,0x40,0x79,0xd7,0x42,0xb5,0x57,0x57,0xad,0xab,0x46,0x11,0xda,0x51,0x21,0x63, + 0xe2,0x31,0xb0,0x9b,0xaa,0x62,0xf4,0x36,0xa0,0x3e,0xbb,0x69,0xe1,0xe8,0xc2,0x2c, + 0x6b,0x08,0x0c,0x72,0xaf,0xc0,0x00,0xf3,0x94,0x29,0x00,0x1d,0xd6,0x65,0xee,0xaa, + 0x8c,0x78,0x4c,0xa0,0x91,0xa6,0xb4,0xae,0x31,0x70,0x0c,0xac,0xa0,0x1d,0x3a,0x90, + 0x84,0xc0,0xf4,0x87,0xb8,0xd6,0x1e,0xec,0x9b,0x50,0x99,0x6a,0xf7,0xa2,0xb8,0x0a, + 0x79,0x0d,0x36,0xd6,0x11,0xd0,0x18,0x2f,0x88,0x97,0x14,0x84,0x9d,0x5f,0x4d,0x44, + 0x77,0x1b,0xc1,0x64,0xf2,0x12,0x2f,0x25,0x7d,0x1b,0x81,0xa6,0x9a,0x80,0x02,0xd1, + 0xc0,0xe3,0x54,0xb3,0x68,0x82,0x3b,0x57,0x02,0x86,0xa1,0x3d,0xf1,0xd8,0x68,0x04, + 0x2c,0x77,0xeb,0xeb,0xe7,0xb3,0xf4,0xaa,0xb6,0x3e,0x6b,0x5f,0x8e,0xb5,0x44,0x71, + 0xd2,0xdc,0x5f,0xc9,0x7b,0x84,0xda,0xe0,0xaf,0xad,0xd9,0x17,0x56,0x54,0x40,0xa0, + 0xcc,0xbe,0x08,0x1b,0x74,0x7a,0xa1,0x2c,0x80,0xb2,0xe3,0xd2,0xf2,0x26,0xfe,0xaa, + 0x15,0x1d,0x18,0xa5,0xf6,0x1f,0xfe,0xaa,0xd5,0x1a,0xdf,0x91,0x7e,0xc1,0x5f,0x85, + 0x16,0xa3,0xfc,0x7d,0xbf,0x0a,0xfd,0x85,0xa4,0xf4,0x88,0x7a,0x5f,0x55,0xb7,0x47, + 0xc8,0x5f,0x49,0x75,0xeb,0xa3,0xf9,0x2b,0x7d,0x28,0x9e,0x70,0x3a,0x18,0x89,0xc1, + 0xb5,0x67,0xf8,0x11,0x80,0xbf,0x9f,0x84,0xc8,0x0b,0x0d,0xc3,0x08,0x94,0x14,0xf8, + 0xe3,0x29,0xb3,0x1f,0x5d,0x86,0x00,0xde,0x21,0xad,0xb1,0x0f,0x7f,0x48,0x1f,0x27, + 0xad,0x91,0xbe,0x40,0x35,0x22,0xac,0x93,0x1a,0x76,0x0b,0xd2,0x54,0xe9,0x7c,0xe8, + 0xcb,0xa4,0x0c,0x64,0x08,0x84,0x46,0xf8,0xfb,0x21,0xb1,0xd5,0x1f,0x91,0x8d,0xe2, + 0x56,0x16,0xc1,0xdf,0x4a,0xa1,0xa2,0x2d,0x61,0xd2,0xd5,0xab,0xc3,0xe6,0x06,0x94, + 0xce,0x0c,0x08,0xa5,0x9f,0x16,0x43,0x10,0xca,0x35,0x18,0xce,0x52,0xa8,0x7b,0xad, + 0x4b,0xf4,0x6f,0x33,0x61,0xcb,0xf6,0x64,0x09,0x8e,0x51,0x80,0x32,0xf2,0x70,0x14, + 0x14,0xb3,0xea,0x1a,0xe9,0x02,0xb4,0x64,0x33,0xd2,0xb8,0x9e,0x5e,0x58,0x8b,0x13, + 0x8f,0xf1,0xd0,0xe2,0xd2,0xd6,0xc7,0x80,0xf8,0x50,0xe3,0x23,0xe4,0x20,0x0d,0xda, + 0x47,0x8c,0x44,0x3c,0xc0,0x3f,0xfe,0xf5,0x7f,0x1b,0xdc,0xd9,0x7c,0xfd,0xc0,0x9d, + 0x80,0xfc,0x37,0x44,0x04,0x3e,0xa1,0x00,0x80,0xd9,0x55,0x9b,0x4d,0x88,0x28,0xf9, + 0x31,0x84,0xca,0x6a,0xc4,0xa2,0xb0,0x06,0x7e,0x35,0x24,0xd9,0xaf,0xb8,0xb4,0x5d, + 0x41,0x65,0xc2,0x5a,0x37,0x54,0xc8,0x1d,0x12,0x54,0x13,0xf9,0xf4,0x01,0x77,0x1c, + 0x3b,0x86,0x9d,0x70,0xc5,0x3a,0xc1,0x65,0xcb,0x1b,0x6a,0xcc,0x61,0x41,0x9c,0x2f, + 0x63,0x66,0x7f,0x38,0xfa,0xfa,0x01,0x96,0x39,0xa9,0xe6,0x70,0x74,0x8b,0x67,0xe1, + 0x8a,0x71,0x29,0xbb,0x94,0xbe,0x7f,0x40,0xd3,0x9b,0x3c,0xd0,0x6c,0xda,0xd7,0x70, + 0x43,0x0a,0xbe,0x70,0x99,0x39,0x7d,0x5d,0xc8,0xc2,0xe4,0x82,0x80,0xf1,0x5a,0x74, + 0xdc,0x48,0xb2,0x93,0x35,0x42,0x71,0x8b,0xc0,0xdb,0xa2,0x11,0x07,0x69,0xcb,0x98, + 0x06,0xc7,0x76,0x4c,0xa1,0xb9,0xf5,0x62,0xa1,0x61,0x32,0x76,0xd1,0x33,0xa0,0xa0, + 0x15,0x9d,0x13,0x14,0x67,0x51,0x5a,0x15,0x01,0x69,0xf2,0x96,0x84,0x2a,0x73,0x30, + 0xb2,0x5d,0x05,0x43,0x1f,0xa7,0xc9,0xa5,0x60,0xde,0x94,0x67,0x4b,0x63,0xf4,0x43, + 0x08,0x2b,0x17,0x4c,0x21,0xd9,0x0c,0x82,0x66,0x4b,0xf4,0xd7,0x16,0xfc,0xad,0x5d, + 0x6f,0x08,0x51,0x48,0x09,0x58,0xab,0xbc,0xdc,0x87,0x6a,0x7d,0x61,0xb2,0xd6,0x01, + 0x9c,0xa6,0x50,0x0c,0x40,0xc4,0xf5,0x50,0xd9,0x52,0x35,0x1c,0xcd,0x2a,0xb0,0x41, + 0xa0,0x45,0xd9,0x14,0xb9,0x35,0xa7,0x53,0xf2,0x1a,0xb5,0x89,0x75,0x74,0x1a,0x03, + 0xb3,0xf7,0x58,0xfe,0x39,0xee,0x60,0x40,0x9e,0x59,0x69,0x31,0x99,0x37,0x10,0x50, + 0x4d,0xf3,0xcf,0xa8,0x79,0xa8,0x05,0x23,0x87,0x5a,0xd4,0x17,0x1e,0x49,0x4d,0xe3, + 0x08,0x47,0x78,0xf3,0x2f,0x45,0xf7,0x9e,0x2b,0x2f,0x24,0xb1,0x57,0x40,0x5d,0x8b, + 0x3b,0x44,0x98,0x11,0x7d,0x43,0xae,0x8d,0x75,0x10,0xb3,0xad,0x7c,0x01,0xfc,0x2b, + 0x5b,0xce,0x19,0x75,0xe2,0xc3,0x2e,0x12,0x12,0x78,0x58,0x8e,0xf8,0x03,0xfa,0x70, + 0x06,0xa6,0x25,0x71,0x7e,0x5e,0xf0,0xa2,0x67,0x61,0x0b,0xf7,0x41,0x40,0xc8,0x23, + 0x66,0x43,0xdc,0x92,0xdc,0xe7,0x34,0xf4,0xa8,0xc4,0x69,0x9f,0xe6,0xd9,0xc5,0x14, + 0x0f,0xb7,0x2c,0xd4,0x37,0x6e,0x9c,0xdc,0x09,0xc6,0xae,0xc7,0x8a,0xe3,0x9a,0xb6, + 0x26,0xb8,0x45,0x71,0xd6,0xdc,0x03,0x1e,0xd2,0xec,0xa0,0xcf,0x43,0xac,0x68,0x31, + 0xb5,0x5c,0x88,0x37,0x18,0x79,0x84,0xe2,0x37,0xf6,0x1a,0x96,0x49,0x79,0xd0,0xe8, + 0xe5,0x8a,0x0c,0x26,0x45,0x7b,0x2c,0x8f,0xc9,0x1c,0x61,0x17,0x03,0xd9,0x23,0x95, + 0x4d,0x50,0xa4,0x00,0xf2,0x10,0xec,0x3b,0x38,0x35,0xae,0x84,0xf9,0x1e,0x6d,0x5f, + 0xbd,0xd9,0x8a,0x7b,0x21,0x30,0x0e,0xf7,0x52,0x6d,0x97,0xba,0x46,0x28,0x12,0x80, + 0xc6,0x83,0x59,0xbe,0x23,0x9b,0x0b,0xae,0xa5,0xf9,0xa3,0x36,0x4b,0x99,0x79,0x28, + 0xde,0x6d,0x92,0xbd,0x56,0xc2,0x70,0xea,0x3b,0x9e,0x54,0x98,0x84,0xa5,0xf3,0xfe, + 0xc3,0xe9,0x4b,0x8e,0x4b,0x22,0x92,0x04,0x5a,0x3c,0xb5,0xcf,0x43,0x46,0x78,0x8a, + 0x6f,0x0a,0xec,0x0d,0x93,0x99,0xd7,0xe1,0x75,0x65,0x4e,0x5e,0x44,0x71,0xec,0x8c, + 0x42,0x7a,0xbf,0x24,0x9c,0xc0,0xd2,0xdf,0x20,0xd5,0x71,0x12,0x16,0x40,0x0a,0xb0, + 0xce,0x01,0xd4,0xfb,0xd4,0x84,0x34,0xa5,0x93,0xf3,0xc4,0x3d,0x30,0xfc,0xce,0x01, + 0x50,0x8e,0xfb,0xf1,0x2f,0x1e,0x36,0x11,0x4d,0xe2,0x10,0x95,0xca,0x10,0x96,0x1d, + 0x16,0x38,0x3a,0x38,0x45,0xa5,0x46,0x83,0x65,0x7d,0x15,0x0d,0xd7,0xda,0x2e,0x5a, + 0x45,0x40,0x8f,0x35,0x63,0x76,0xa6,0x71,0xb4,0x70,0x23,0x60,0x69,0x69,0xe2,0x11, + 0x17,0x8a,0x26,0x5e,0x8b,0x22,0xd9,0x50,0xa7,0x12,0xf7,0x21,0xb8,0x8d,0x14,0x45, + 0x4a,0x8a,0xbb,0xc8,0x5c,0x02,0x16,0xe2,0x41,0x01,0x98,0x1b,0x81,0x65,0xea,0x36, + 0xcc,0x43,0x04,0xb2,0x9c,0xa5,0xb3,0x8a,0x36,0xd8,0xd1,0x0d,0xda,0x99,0x6e,0xca, + 0x78,0x33,0x0a,0x39,0x32,0x71,0x1d,0xa3,0x13,0x74,0x5a,0x02,0x16,0x25,0x82,0xc0, + 0x89,0x85,0xba,0xb6,0x5f,0x82,0x26,0x55,0x5a,0x6a,0x44,0xbc,0x92,0x95,0xb1,0xff, + 0x80,0x7d,0x0a,0xc4,0x6a,0xf6,0xa5,0x83,0x0c,0x57,0xe5,0x83,0x94,0x94,0x13,0x9c, + 0x56,0xd0,0xeb,0x57,0xf3,0x0c,0x58,0x82,0x5b,0x7f,0x77,0xa1,0xd8,0xef,0x84,0x4e, + 0xef,0x4f,0xc2,0xcb,0xc9,0x1c,0xc3,0x23,0x67,0x3a,0x3e,0x83,0x80,0xc8,0xdf,0xa8, + 0x61,0x48,0x90,0x1f,0x93,0x72,0x5a,0x5f,0xc0,0x71,0x13,0x22,0x03,0x68,0x56,0x4d, + 0x3b,0x4e,0xb7,0xd7,0x90,0x86,0x8a,0x22,0x19,0x69,0x7e,0xe8,0xc9,0xe6,0xd1,0x8c, + 0x02,0xd4,0x03,0xb6,0xfe,0x0e,0x79,0xa6,0x8f,0xb8,0xe1,0xf0,0xf5,0x44,0xc4,0x42, + 0xd1,0x05,0x9c,0x16,0x88,0xc9,0x07,0xc8,0x57,0x80,0x95,0x10,0x69,0x9f,0xcd,0xc9, + 0x6f,0x82,0x26,0x16,0xd1,0x81,0xa1,0x15,0x89,0x59,0xa4,0xa9,0x6b,0x88,0x83,0xfc, + 0x90,0x38,0xf7,0xa9,0x3d,0xa8,0xe4,0x0a,0xff,0xb5,0x1e,0x1d,0x35,0x0a,0xb6,0x1b, + 0x35,0xe8,0x79,0xa6,0x22,0xf6,0xe7,0x20,0x6e,0x94,0xe7,0x5a,0x4c,0x8b,0xd2,0xc8, + 0xf4,0xc9,0xf8,0x4a,0x51,0xec,0x7b,0xb5,0x8c,0x45,0x20,0x18,0x00,0x8e,0x04,0x02, + 0xbd,0xa3,0x92,0xf2,0x10,0x25,0x94,0xa3,0x93,0xf5,0x2d,0x71,0xbb,0x03,0xba,0xe6, + 0x02,0x74,0xcb,0xb5,0x5b,0x3d,0xea,0x28,0x95,0xe6,0x13,0xf5,0x50,0x96,0xc3,0x35, + 0xa1,0x10,0x3b,0x5d,0x69,0x88,0x5a,0xad,0x54,0x67,0xec,0x6d,0xe5,0xf0,0x01,0x17, + 0x96,0x48,0xd4,0xa5,0xc8,0x67,0xab,0xb0,0x28,0x4e,0xbb,0x43,0xaa,0xa8,0x40,0x9b, + 0x29,0xd0,0x89,0x07,0x01,0xff,0x26,0xb1,0xd7,0x17,0x12,0xa7,0xa1,0xce,0x58,0x63, + 0x9a,0x74,0xf7,0x42,0x3a,0x30,0xf8,0x84,0x29,0x02,0xfb,0x8f,0x9a,0x12,0x7b,0xac, + 0x10,0x08,0xca,0x08,0xae,0xad,0x27,0x82,0x7a,0x9e,0x17,0x15,0xa4,0x8a,0x3e,0x40, + 0x15,0x51,0x53,0xb6,0x01,0x3f,0x1f,0x08,0x28,0x81,0x33,0x12,0xa7,0xfb,0xd3,0xcc, + 0x51,0xfc,0x44,0xf7,0x00,0x77,0x82,0x6a,0xe9,0xc0,0x3e,0x37,0x6e,0x8c,0xca,0x3a, + 0x3d,0x4e,0x3d,0x4c,0x84,0xcf,0xcd,0x43,0x57,0x14,0xf6,0x8a,0x78,0x1c,0x6e,0xb7, + 0xe6,0x8d,0x92,0xae,0xc3,0xf1,0xae,0x2e,0x6d,0xb6,0xcb,0x09,0x72,0xe8,0x1d,0xb7, + 0x45,0x2e,0x45,0x68,0x94,0x8c,0xe3,0xe5,0x24,0xcc,0x69,0x1e,0xa2,0xb4,0xa1,0x2c, + 0xd8,0x65,0x0e,0x4a,0x22,0xfb,0xd0,0xf3,0x16,0xe5,0x7d,0x5c,0x26,0xe8,0x0c,0x82, + 0xf9,0x51,0xce,0x74,0x91,0x73,0xc8,0x09,0x86,0x5f,0xa1,0x6c,0x33,0xa4,0xd0,0x4c, + 0x44,0x5a,0xbe,0xd4,0x05,0xb5,0x6e,0x63,0x30,0x1c,0x5b,0x8b,0xa9,0xf1,0x92,0xa8, + 0x16,0xa4,0x61,0x3f,0x40,0xf6,0x6c,0x99,0x21,0x69,0xc2,0x27,0x96,0x2b,0x4d,0x09, + 0x43,0xca,0x7d,0x5c,0xb5,0x78,0x3c,0xcb,0xa8,0x2a,0xbb,0x07,0xf4,0xb0,0x2a,0x4d, + 0x71,0x37,0x70,0x10,0xf4,0xa3,0x42,0xf6,0xd5,0x71,0xb3,0x5d,0x42,0x24,0xc5,0xd8, + 0x45,0x72,0x1e,0x6c,0x20,0xbe,0x1f,0x3f,0xd6,0x4d,0xad,0xd0,0xca,0xcb,0x6a,0x37, + 0xf9,0x7f,0x12,0xf2,0x48,0xba,0x04,0x15,0x94,0x36,0x40,0x5f,0xdb,0x2b,0xbb,0xe6, + 0xb8,0xad,0xac,0x34,0x93,0x86,0xff,0x47,0x74,0x06,0x81,0x08,0x5b,0x86,0x9c,0x37, + 0x74,0xe9,0x43,0xbd,0xa2,0x2e,0xdc,0x3e,0x75,0x7a,0xba,0x80,0x89,0x83,0x6a,0x36, + 0x0d,0xfc,0x79,0x8c,0x44,0xb4,0xd9,0xde,0x2d,0x0b,0x72,0x62,0x7e,0x18,0xc1,0x7a, + 0xb9,0x04,0xc3,0xd8,0x35,0xfc,0xd5,0x42,0xe9,0x00,0x05,0x23,0x8c,0x61,0xe9,0x70, + 0xe0,0x0e,0x5e,0x51,0x05,0x06,0xf5,0x46,0x2b,0xe5,0x2a,0xae,0xda,0x0c,0x97,0x3f, + 0xc4,0x8e,0x38,0xda,0xe4,0x81,0x14,0xb7,0xaf,0xa2,0x18,0x2f,0x34,0x75,0xce,0x1a, + 0xfa,0xb2,0xc7,0x06,0x1b,0xe6,0xea,0x8d,0xc8,0x83,0x49,0xb0,0xc0,0x7b,0xda,0x86, + 0x14,0x8d,0x13,0x4c,0xd8,0x57,0xd1,0x27,0x13,0xcd,0xdf,0xc0,0xe5,0xd2,0x17,0x78, + 0x72,0x45,0x9c,0x43,0xad,0xb5,0x8f,0x5f,0x3f,0x06,0x8b,0xbe,0xc3,0x5b,0x64,0x1b, + 0x93,0x11,0x34,0x2b,0xf7,0xb7,0x7d,0xbd,0xd3,0xff,0x58,0x6e,0x7f,0x9f,0xfb,0xac, + 0xa9,0x2b,0x47,0x44,0xc7,0xf3,0x37,0xf0,0x86,0x26,0x0c,0xe7,0x29,0x87,0x91,0x43, + 0x35,0x76,0xa9,0x98,0x61,0x6f,0x58,0x29,0xa6,0xd0,0x1c,0x4f,0xe8,0x76,0x1e,0x06, + 0x44,0xf1,0xed,0x65,0x18,0xf2,0x63,0x40,0xa1,0xe8,0x21,0x90,0x9c,0xab,0xa1,0x60, + 0xf1,0xa6,0x80,0x02,0x93,0xe5,0x6f,0xa0,0x7e,0x27,0x91,0x61,0x18,0x5f,0x55,0xed, + 0x41,0x79,0x36,0x60,0xad,0x19,0xb1,0x73,0xc2,0x95,0xf4,0x5c,0xb8,0xd1,0xfa,0x8e, + 0xb1,0x6c,0xfd,0x8d,0x34,0x51,0xb0,0xa7,0x89,0x98,0x7d,0xda,0x42,0x44,0x7b,0x2e, + 0x19,0x38,0x54,0xe4,0x15,0x92,0x82,0x51,0x42,0x2a,0xa4,0x5c,0x66,0x43,0x19,0x16, + 0xf5,0x96,0xfd,0x73,0xce,0x1f,0x76,0x70,0x1b,0x0f,0xbd,0x00,0xd2,0x02,0xe4,0x65, + 0x2e,0x1e,0x61,0x94,0xac,0x92,0x13,0x8d,0x37,0x1b,0xdd,0x1e,0xdb,0x7e,0xd0,0x13, + 0x75,0xe4,0x01,0xfa,0xc2,0x58,0x65,0x6b,0xba,0x81,0xee,0x81,0x21,0xef,0x18,0x88, + 0x52,0x07,0x18,0xcc,0xee,0x8a,0xfb,0x62,0x35,0xc6,0x8e,0x5f,0xd1,0x70,0xb3,0xb9, + 0xc8,0x82,0x75,0x83,0x07,0x4a,0x72,0x36,0xf3,0xfb,0x0c,0x2f,0xd7,0x77,0x56,0x10, + 0x4f,0xf7,0x71,0xe1,0x45,0x19,0xc7,0x2a,0xe4,0x7e,0xf9,0xe8,0x28,0x9d,0x19,0x8d, + 0xd4,0xfd,0x3d,0xa0,0x47,0x40,0x63,0x30,0x06,0x5c,0x79,0xaa,0x19,0xd1,0xd1,0x46, + 0xc5,0x8a,0xe6,0xbd,0x21,0xfc,0x22,0xb4,0xd3,0x4f,0x5c,0x62,0xc2,0x04,0xb8,0x5b, + 0x13,0xc9,0x52,0x77,0x16,0xe2,0x80,0xc2,0xd4,0xec,0x35,0x35,0x50,0xb1,0xdc,0xf7, + 0x09,0xa5,0xa5,0x68,0x6e,0xa2,0x4f,0x8e,0x77,0x3e,0x3c,0x38,0x3e,0x51,0x9e,0x21, + 0x4c,0x38,0x7a,0x79,0x78,0x70,0x44,0x46,0x86,0x88,0x88,0x3e,0x7d,0x73,0xf8,0xd3, + 0x89,0x0c,0x26,0x36,0x1d,0x4b,0x87,0xb0,0x6a,0x5d,0x1d,0x66,0x86,0x82,0xe6,0x80, + 0x24,0x1f,0x0e,0x48,0xc0,0x55,0x8b,0x79,0xdc,0x32,0xa3,0x97,0xc4,0x37,0xc6,0x15, + 0xe1,0x12,0x1e,0xa3,0x05,0x08,0xe2,0x52,0x44,0x0a,0xa9,0x05,0x3b,0x6e,0xc9,0xdf, + 0x1c,0x9d,0xb4,0x21,0x56,0x3f,0x56,0x86,0x1f,0x1c,0x34,0xc3,0xcb,0x79,0xdc,0xc2, + 0x4b,0x4a,0x50,0x0d,0x94,0x0b,0xbb,0x76,0x3d,0x1b,0x4b,0x77,0xdc,0x12,0xf7,0x99, + 0x50,0x25,0xb1,0x8e,0x6b,0x97,0x2f,0xc5,0x2d,0x6a,0xbc,0x18,0x81,0x3c,0x38,0x4a, + 0x11,0x84,0xb6,0xe5,0xc8,0x0f,0x8a,0x6d,0x55,0x58,0x3b,0xc3,0x90,0xc6,0x46,0xaf, + 0xdd,0xc0,0x8d,0x6a,0x5a,0x20,0x0d,0xf8,0xb7,0x21,0x02,0x1c,0xe1,0xab,0x2b,0xbe, + 0x7a,0x6d,0x2e,0x88,0x5f,0x1b,0x58,0x92,0x8a,0x7e,0x6e,0xe8,0x50,0xc8,0x46,0xf7, + 0x73,0x43,0x87,0x42,0x36,0x7a,0x9f,0x25,0x4c,0x01,0x15,0xbe,0xcf,0xf1,0xe6,0x7e, + 0x64,0xc7,0xae,0x7b,0x36,0xfb,0x72,0xce,0x63,0xfa,0x02,0x82,0x5f,0x77,0x0e,0x83, + 0x53,0x5a,0x9d,0x1e,0x6a,0x55,0x90,0xf3,0xcc,0x31,0xc6,0xf0,0x03,0x1a,0xb7,0x4f, + 0x8c,0x00,0x62,0xa2,0xa8,0xd7,0x5f,0x4a,0x01,0x47,0xc8,0x2d,0x35,0x3c,0x23,0x7a, + 0x43,0x83,0x2a,0x1d,0xc2,0x31,0x50,0x63,0xba,0xba,0x71,0x61,0x1f,0xfe,0xd3,0x41, + 0xdc,0x6a,0x7f,0xb9,0x26,0x7e,0x18,0x87,0x82,0x44,0xcc,0x0e,0x73,0x4d,0xe5,0xf8, + 0x52,0x0f,0x6d,0xdf,0xe0,0x07,0xf1,0x3b,0xe1,0x40,0xa6,0x4c,0xe0,0xc9,0xf8,0x06, + 0x6b,0xc6,0x45,0x74,0xa8,0x69,0x98,0xe5,0xa0,0x54,0x32,0xcd,0x9b,0x0f,0xde,0xaa, + 0x0a,0xe4,0xa4,0xd4,0x95,0xcd,0xb7,0x5c,0xe3,0x65,0x3e,0xf3,0xd1,0x84,0xb3,0x4d, + 0x51,0x4a,0xd7,0x06,0xa3,0x86,0x24,0x77,0x4c,0xb8,0xcf,0xf7,0x6a,0x4e,0x07,0x59, + 0x1d,0xb4,0xb4,0x5c,0x61,0x9e,0x03,0x6d,0x0a,0xc6,0xb1,0x76,0x8b,0x83,0xba,0x63, + 0x4e,0x11,0x6e,0x86,0xde,0xe8,0x90,0x68,0x1a,0xfa,0x9a,0xcd,0x07,0x8a,0x42,0xc5, + 0xd9,0xc6,0xae,0xdd,0x24,0xe3,0x03,0x75,0x3d,0x92,0x2b,0x52,0xc4,0xed,0x37,0xf4, + 0x59,0x52,0x3d,0x4c,0xbc,0xd0,0xd3,0x10,0x7a,0xb6,0x45,0xf0,0x29,0x36,0x6f,0xd0, + 0x84,0x3a,0x97,0x42,0x69,0x7c,0xe6,0xc1,0x3e,0x10,0xa1,0x49,0xf9,0xde,0x31,0x7e, + 0x5c,0x1c,0xcd,0xb5,0x43,0x3e,0x0c,0x4f,0x4e,0x78,0xfc,0x6e,0xe8,0xdc,0x71,0x81, + 0x67,0x60,0x74,0x21,0x84,0x44,0x96,0xcc,0x35,0x88,0x81,0xee,0x44,0x97,0x7c,0x87, + 0x74,0xe5,0x80,0xb4,0xda,0xd9,0x03,0x31,0xd1,0x77,0x76,0x41,0xdc,0xf4,0x9d,0x2e, + 0xfc,0x01,0xbe,0xb7,0xd3,0x76,0x04,0xc9,0x1d,0x7f,0xe2,0xc0,0x99,0xe3,0xd7,0x3c, + 0x8f,0x5c,0xf9,0xf0,0xc3,0xc1,0xc7,0x93,0x97,0x18,0x23,0x86,0x02,0xf3,0x1d,0x5e, + 0x20,0xef,0x36,0xd4,0x0d,0xf2,0x8e,0xb8,0x42,0xbe,0x81,0x6e,0x3a,0xd4,0x21,0x73, + 0x59,0xef,0xe5,0xd1,0x8f,0x58,0x4b,0x54,0x07,0x85,0x75,0x0f,0xd6,0xc8,0x13,0x99, + 0xfb,0xe3,0xc7,0x37,0xc7,0x56,0x6e,0xc7,0xcc,0xfd,0xf8,0xf2,0xe0,0xf0,0xb5,0x99, + 0xdd,0xed,0xa2,0x87,0x52,0x9d,0xe6,0x78,0xfd,0xe6,0xfd,0xe9,0x2f,0x07,0x3f,0x9f, + 0x7e,0x40,0x23,0xf7,0x5b,0xef,0x93,0x6a,0x6c,0x80,0x16,0xad,0x20,0x37,0x4e,0xf8, + 0xc2,0x33,0x7d,0xa5,0x15,0x3a,0xaf,0x70,0x73,0x12,0xfd,0xd7,0x81,0xc3,0xd7,0x66, + 0xaa,0x4b,0xa5,0xc8,0x6a,0xf9,0xde,0x1b,0xa9,0xf4,0x8c,0xd0,0x00,0x5e,0x1f,0xbc, + 0x3f,0xc2,0x01,0x7c,0xa2,0xbb,0x1e,0xa9,0xdb,0xd3,0x2c,0xfc,0x6d,0x19,0x26,0x63, + 0x74,0xbe,0x05,0xa4,0x1a,0x38,0x6c,0x42,0xc2,0xa8,0xd2,0x96,0xf3,0x9a,0x2f,0x7b, + 0xa4,0xa2,0xbf,0x2d,0xa3,0xb0,0x68,0x16,0x69,0x13,0x87,0x2a,0x7a,0x8e,0xde,0x36, + 0x30,0xbe,0x81,0x06,0x52,0xf2,0xad,0x9b,0x9b,0xee,0xb0,0x3e,0x8e,0x81,0x54,0x68, + 0x99,0x1b,0x34,0xc3,0x7e,0x00,0x31,0x4a,0xde,0x58,0x16,0x6c,0x37,0xcf,0x3f,0xd9, + 0xe4,0xc5,0x06,0x21,0xf5,0xd6,0xc7,0xec,0xd7,0x75,0xd9,0xdc,0x45,0x09,0x64,0xb2, + 0xc8,0xb4,0xb6,0xc0,0x7e,0x8a,0x63,0x30,0xc0,0xe2,0x8f,0x68,0x14,0x90,0x3c,0x1d, + 0x98,0x9d,0xa1,0x5b,0x2f,0xa5,0x74,0x13,0x61,0x38,0xd8,0x8d,0x1f,0x10,0x90,0x67, + 0x15,0x55,0x3d,0xb6,0xcb,0xbe,0xae,0x2b,0x5b,0x1a,0x23,0x15,0x03,0xb3,0x7a,0x71, + 0x0d,0x83,0xc5,0x55,0x84,0x8e,0xbb,0x53,0xbc,0xea,0x16,0x63,0x2c,0x5c,0xa8,0x4e, + 0xf1,0xcf,0xf0,0x9f,0xfa,0x89,0x2a,0xe3,0x27,0xae,0xfa,0x09,0x84,0xf3,0xbb,0x56, + 0x4c,0xff,0x82,0xde,0x43,0x8b,0x85,0x20,0x62,0x42,0x41,0xff,0x8e,0xf4,0x8a,0xbf, + 0xfe,0x80,0x5b,0x0f,0xee,0xec,0x0b,0x4b,0x2e,0xa8,0xf7,0x18,0xd7,0xd9,0x0f,0xc2, + 0x5f,0xcb,0x12,0x5c,0x48,0x38,0x2a,0x65,0x89,0x70,0x0c,0xbc,0x65,0x91,0xaf,0x28, + 0x67,0xf6,0x85,0x21,0x1a,0xd1,0xbd,0x50,0xd3,0x92,0xac,0x56,0x40,0xae,0x54,0x19, + 0xa0,0x45,0xf7,0x9a,0xbb,0x8e,0x60,0x8f,0x3f,0x79,0xfa,0x4c,0xea,0x0d,0x83,0x14, + 0xd2,0x15,0x06,0xf1,0x98,0x8f,0xab,0xe9,0x10,0x28,0x8c,0x81,0x3a,0x7e,0x2d,0xcb, + 0x5f,0x72,0xf9,0x1b,0x2b,0xc0,0x18,0x2b,0xb8,0x37,0x8c,0x05,0x6a,0xe1,0xb5,0x82, + 0x1f,0x5c,0x47,0xf9,0xd1,0x48,0x59,0xc7,0x24,0xea,0x84,0xb6,0x45,0x1d,0x9f,0x8c, + 0x06,0xea,0x10,0x5b,0xb2,0x44,0x66,0xe7,0xb2,0x46,0x7d,0x52,0xa0,0x4b,0xcf,0x3c, + 0xd7,0x08,0xb2,0xbb,0x95,0x85,0x8b,0x38,0x18,0x87,0x6e,0xa3,0x89,0x46,0xe9,0x3f, + 0xfe,0xc7,0xbf,0x35,0x4c,0x61,0x31,0x19,0xe1,0xad,0x25,0xee,0xa5,0x56,0x0a,0xc9, + 0xb3,0xcf,0x7d,0x30,0xb6,0x29,0x21,0xf5,0x39,0xb7,0xe7,0x4e,0x46,0x1c,0x5b,0x74, + 0x89,0x58,0x82,0x8f,0x0e,0xba,0xda,0x29,0xd1,0xf3,0x84,0x3b,0xcc,0xf4,0xae,0x8e, + 0xde,0x46,0xc0,0x5a,0xdc,0x95,0x0d,0x88,0x33,0x47,0x58,0x48,0xa8,0xac,0x24,0x56, + 0x27,0x23,0x25,0x4f,0x29,0xcf,0x08,0x96,0x9a,0x48,0x3a,0xc6,0xd3,0x19,0xa2,0x3b, + 0x5b,0x14,0x24,0x8b,0x41,0xbc,0x03,0xc8,0x7f,0x36,0x74,0xa8,0x63,0xf8,0xfb,0xf1, + 0x90,0xb2,0xc0,0x88,0x45,0x30,0x1c,0xba,0x74,0xe6,0x4e,0x54,0x9f,0x71,0x37,0xc0, + 0x1e,0x85,0x2f,0x07,0xea,0x9d,0x9b,0x27,0x3a,0x44,0x37,0x4c,0x89,0x87,0x76,0xb3, + 0x31,0x32,0x1c,0x00,0x49,0x95,0xf9,0x02,0xa3,0xad,0x4e,0x70,0x21,0x69,0x83,0x9c, + 0x76,0x00,0x15,0x51,0xd0,0xb4,0xc1,0xcc,0xd2,0xcc,0x91,0x30,0x3a,0xce,0x52,0x50, + 0x57,0x8a,0x1b,0x56,0x87,0x28,0xaf,0x05,0x33,0x3a,0xd7,0x48,0x0a,0xe2,0xc5,0x2c, + 0xb0,0x2a,0x9a,0xc7,0x5a,0x75,0xc4,0xb0,0xa8,0xcc,0x41,0xca,0x1d,0x3c,0x1c,0xb7, + 0xab,0x67,0xd2,0x0d,0xa8,0xe6,0xbf,0x64,0x17,0xa3,0xc0,0xfd,0xd3,0x6d,0xe2,0xec, + 0xef,0x43,0xfe,0x9d,0xef,0x88,0xdf,0x7b,0xce,0x23,0x0c,0xe0,0x13,0x09,0xfa,0x77, + 0x70,0xe7,0xfd,0x0b,0x39,0x2a,0xd4,0xe1,0x06,0x7e,0x58,0xc2,0xb9,0x04,0xd2,0xe2, + 0x47,0x26,0x1a,0x18,0x2c,0x40,0x0f,0x43,0x70,0x22,0xfd,0xc6,0x44,0x7c,0x0d,0x82, + 0x93,0xe0,0x17,0x26,0x88,0xc3,0x0f,0x94,0xc4,0x91,0x69,0x1e,0x1e,0x78,0xc6,0x57, + 0x21,0x04,0x3c,0xfc,0x8d,0x89,0xf4,0x34,0x04,0xa7,0xe1,0x4f,0x4c,0x8a,0x55,0xd5, + 0x98,0x2b,0xc2,0x9f,0x83,0x3e,0x63,0xc7,0x48,0xc5,0xe2,0x66,0xb2,0xac,0x0e,0x5d, + 0x30,0x93,0xa9,0x47,0x7c,0x5c,0x41,0x4d,0x2c,0x06,0x86,0xbd,0x4c,0x2e,0xf9,0x70, + 0x9a,0x9e,0x5f,0xde,0x6e,0xb3,0x63,0x51,0x41,0x27,0xe7,0x15,0x8a,0x99,0xe6,0x87, + 0x38,0xdd,0xa2,0xf0,0x25,0xf2,0xc5,0xa1,0xc0,0x7d,0x71,0x28,0x50,0xef,0xc1,0x09, + 0xc6,0x20,0xce,0x0a,0x8a,0x8d,0x38,0x23,0xd1,0xdc,0x8d,0x2b,0x9d,0x27,0x4d,0x26, + 0x9f,0x48,0x66,0x94,0xfb,0x8b,0x39,0xa2,0xc3,0xe2,0x68,0x97,0xdd,0xa5,0xd2,0x30, + 0x81,0xc1,0x70,0x5f,0xf4,0xb9,0x43,0xee,0x86,0x3e,0x3f,0xe8,0xdc,0x69,0x66,0x8d, + 0x0a,0x02,0xc7,0xc4,0x30,0xa3,0x7a,0xf0,0x80,0x5d,0x54,0x72,0x5f,0x53,0x21,0x43, + 0xec,0xf3,0x71,0x98,0x40,0xd9,0x98,0x38,0x4e,0x73,0xdd,0x6d,0xe5,0xe6,0xa2,0x3d, + 0x42,0x05,0x00,0x3e,0x64,0x6d,0xa7,0xb4,0x03,0xa8,0x0a,0x88,0xb1,0x69,0x3a,0x67, + 0x6d,0x57,0x1d,0xf3,0x34,0xbb,0x22,0xca,0x8a,0x13,0x1c,0x65,0xf4,0xf8,0x25,0xbc, + 0x94,0xb4,0xff,0xa8,0x10,0xc8,0xc6,0x3a,0x4a,0xbb,0xe5,0x43,0x48,0x43,0xe3,0x0c, + 0xa8,0x61,0x64,0x99,0xf8,0x24,0x16,0xce,0x4a,0x10,0xcb,0x21,0x69,0x2f,0x93,0x84, + 0x31,0x30,0x3d,0x2c,0x19,0x69,0xb2,0x9c,0xaf,0xdb,0xd0,0xa5,0x05,0x5c,0x8c,0xba, + 0x30,0x0e,0x8d,0x96,0xbb,0x8e,0x9a,0x7f,0x21,0x24,0xa7,0x32,0xd8,0xd0,0xd6,0x44, + 0x16,0x49,0x9b,0xdb,0x90,0xc5,0x67,0x0c,0xda,0xda,0xfb,0xcb,0x05,0x9e,0x3e,0xed, + 0x61,0x01,0xda,0xfb,0xd6,0x42,0x65,0xeb,0xbf,0xb5,0xda,0x7f,0xda,0xf2,0x71,0x8f, + 0x00,0x75,0x04,0xda,0x2f,0xa8,0x8a,0x21,0x68,0xcf,0x50,0xeb,0xdf,0x7d,0x78,0x4f, + 0x9a,0x28,0xbd,0x82,0x64,0x3d,0x76,0xe3,0x9c,0xbc,0xc2,0xe7,0x6b,0x9a,0x1f,0xc3, + 0x8b,0x65,0x1c,0x80,0x32,0xc1,0x6f,0xdc,0x38,0xea,0x91,0x1b,0x47,0x95,0x35,0x95, + 0x35,0xf4,0xa4,0x2a,0x65,0x8d,0xdb,0xc0,0xa3,0xcb,0x82,0x3b,0x4b,0xe6,0x89,0x0a, + 0x89,0xd2,0x28,0x1e,0xa3,0x3a,0xe2,0x3b,0xaf,0x29,0x0d,0x25,0xf8,0xf1,0x6b,0x4a, + 0x1b,0x09,0x15,0x87,0x8c,0xb7,0x8f,0x60,0xea,0xb8,0xac,0xda,0x7c,0x82,0xb2,0x9e, + 0xc8,0x03,0x6b,0x3d,0x26,0xd6,0x0e,0x95,0x4f,0xf9,0xc2,0x7a,0x23,0x67,0x55,0xa5, + 0x94,0xbc,0xdb,0x38,0x76,0xa9,0x45,0x15,0x59,0xfa,0x39,0xd4,0x80,0x88,0x5d,0xb9, + 0x74,0xa0,0xac,0xb6,0x21,0x62,0x93,0x22,0x07,0x45,0xd1,0x27,0xa1,0xfd,0xc9,0x13, + 0xef,0xe2,0x64,0x1b,0xde,0xb8,0x81,0x57,0x9a,0x9c,0xa3,0x2e,0xaf,0x84,0xaf,0xc6, + 0xcc,0x8d,0xad,0x04,0x82,0x32,0x43,0x0a,0x0b,0xfa,0x49,0x7b,0x2c,0x7a,0x2f,0x69, + 0xd3,0x01,0x16,0xdf,0x25,0x6e,0x60,0x92,0x02,0x8c,0x6d,0x8e,0xc2,0x8b,0x28,0x39, + 0x86,0x9a,0x18,0x38,0x47,0x29,0x73,0x50,0xd7,0x4f,0x53,0x17,0x90,0xea,0x3b,0x37, + 0x32,0x11,0xbb,0xc6,0x89,0x84,0x69,0x23,0x87,0x07,0xcc,0x3b,0x21,0x94,0x80,0xa6, + 0xd7,0x01,0x3e,0x4c,0x81,0xd4,0x40,0x0f,0x04,0x36,0x8c,0x91,0x93,0x5e,0x42,0x43, + 0xf1,0x85,0x06,0x09,0xe6,0x18,0xdd,0x0f,0xf0,0x15,0x08,0x14,0x82,0x20,0xd4,0x0b, + 0xcf,0xb1,0xe1,0x35,0x26,0x2f,0x1a,0x06,0x38,0x56,0x41,0x9f,0x7a,0xe5,0x66,0x1b, + 0xaf,0xbf,0x94,0x8b,0x09,0x12,0x21,0x49,0x5a,0x6d,0x9b,0xdf,0xd4,0x68,0xd8,0x53, + 0x31,0xfb,0x62,0xce,0x05,0xb9,0x97,0xf4,0x3c,0x5c,0xdb,0xf3,0x00,0x9a,0x2f,0x2d, + 0x13,0x39,0x11,0xeb,0x51,0x7e,0x4d,0x7d,0x2a,0xa1,0xfc,0x5a,0x77,0xb4,0x8a,0xf2, + 0x5a,0xb4,0x5e,0x57,0x87,0x56,0x87,0x58,0xbc,0x42,0xbf,0x32,0x33,0x8a,0x99,0x18, + 0x0a,0x38,0xa1,0xec,0x3e,0xe8,0xaa,0x9f,0xec,0x0a,0x48,0x8c,0x3e,0x74,0x34,0x25, + 0xd5,0xc2,0xad,0x5b,0x46,0x83,0xfb,0x50,0xac,0x01,0x0d,0x30,0xbe,0x92,0x7c,0xeb, + 0x8b,0x69,0xc4,0x0a,0xcd,0x6f,0x11,0xa2,0x33,0x95,0x85,0x8c,0x0a,0xdc,0xa1,0xed, + 0x44,0x11,0xe8,0x0c,0x69,0xc6,0x69,0x49,0xb5,0xaa,0xec,0x35,0x8e,0xaa,0x09,0xb0, + 0x80,0xce,0x8e,0x57,0x33,0x84,0x8d,0xea,0xa2,0x03,0xa2,0x41,0xc0,0xa4,0x7a,0xab, + 0xe3,0xba,0x57,0xe8,0xea,0xf9,0x04,0x6c,0xdc,0x68,0xb3,0xfe,0xa8,0x81,0x59,0xc0, + 0xbe,0x1e,0x03,0xb9,0x84,0x82,0x8e,0x11,0xfd,0x1b,0x55,0xec,0x44,0xa0,0x6e,0x8f, + 0xae,0x68,0x81,0xd7,0xe4,0xba,0x78,0x2c,0xa0,0xe3,0x19,0x65,0xee,0x36,0xd6,0x63, + 0xb8,0x0a,0xa6,0x9a,0x37,0x8e,0xd3,0x3c,0xb4,0xf0,0x81,0x08,0x64,0x9e,0x52,0x77, + 0x78,0x87,0xfc,0x50,0x91,0xa5,0x2a,0x5f,0x91,0x9c,0x36,0x95,0x26,0x85,0xba,0x6b, + 0xbc,0x59,0x05,0x57,0x22,0xc9,0x50,0x49,0xd3,0xd7,0x9d,0x52,0x2a,0x90,0xa5,0xb2, + 0x05,0x4f,0xd3,0x85,0x40,0x15,0xc0,0x45,0x1d,0x0d,0xc6,0xfa,0x22,0x2d,0x8c,0x34, + 0xba,0xf4,0x80,0x4b,0x47,0xf9,0x09,0x39,0x31,0x23,0xda,0xee,0xce,0xc3,0x58,0x91, + 0x0f,0x91,0x13,0xe6,0x50,0x89,0xe7,0x40,0x0a,0x18,0x16,0x80,0x1b,0x26,0xd8,0xa6, + 0x3c,0x06,0x04,0xd2,0x06,0xd5,0x62,0x48,0x27,0x52,0xa9,0xd8,0x05,0x56,0xe1,0x76, + 0xab,0xbd,0x83,0x10,0x24,0x48,0x20,0x2b,0x74,0x0b,0x41,0xf2,0x93,0x7a,0x01,0x83, + 0x5a,0xb4,0x4b,0xa0,0xbc,0xb2,0x3c,0xbb,0xc6,0xeb,0x66,0x60,0xa8,0x84,0x8c,0xa6, + 0x43,0x9f,0x38,0xca,0x26,0xa5,0xd6,0x2f,0x45,0xfe,0xaa,0x11,0x58,0xb2,0x43,0x1d, + 0x8a,0x9e,0xe8,0x7c,0x95,0xd7,0x89,0xb6,0xcb,0xdc,0x4e,0xf4,0x61,0x05,0xab,0x5b, + 0x0d,0xae,0x53,0x0f,0xae,0xb3,0x0a,0x9c,0xd8,0x0e,0xd2,0x2a,0xbf,0x90,0x29,0x79, + 0x58,0xa0,0x70,0x3d,0x0a,0xd0,0x9e,0xec,0xf9,0xce,0xce,0xb9,0xf7,0xbd,0x43,0x29, + 0x75,0xe9,0xde,0x43,0x29,0xa1,0xe0,0xab,0x43,0xa9,0xf6,0xfb,0x5c,0x0c,0x50,0xe8, + 0xf2,0xa6,0xa2,0x10,0xdf,0x98,0xea,0x2c,0x11,0xfa,0x63,0x9c,0x33,0x43,0xaf,0x15, + 0x34,0x40,0x89,0xe2,0xb7,0x52,0xe9,0xe9,0x1e,0x10,0x93,0x48,0xbc,0x55,0x1c,0x3b, + 0xae,0x55,0x6b,0xba,0x5f,0xe3,0x80,0xd7,0x78,0x9b,0x10,0x0c,0x35,0x2e,0xf3,0x1f, + 0x22,0x51,0x2b,0xc3,0xd4,0x3c,0x70,0xb4,0x44,0x81,0x2b,0x58,0xb0,0xa9,0xcf,0x95, + 0x7b,0x0a,0x6b,0xee,0xeb,0x0a,0xd8,0x18,0x0f,0x1d,0xdf,0x90,0xc8,0x3f,0x3b,0x93, + 0x33,0x8e,0xbb,0x3e,0x72,0x9a,0xe9,0xb7,0x98,0x3a,0x95,0x8e,0xbf,0xcf,0xad,0x2e, + 0xd1,0xc2,0x1b,0xa3,0x13,0x8a,0xbc,0xbf,0x5b,0x18,0xc9,0x3c,0xbe,0xb1,0x3e,0xf1, + 0x17,0xff,0x6b,0x8f,0xf4,0x5b,0xab,0xde,0x89,0xc8,0x68,0xd2,0xe9,0x4f,0x83,0x8b, + 0xdc,0xb5,0x0d,0x07,0x23,0x7d,0x85,0x86,0x7d,0x3f,0xd6,0x5b,0x04,0x17,0xec,0x9a, + 0x87,0x1f,0xf2,0x28,0xda,0x1a,0xa6,0x0c,0xa5,0x6a,0xc2,0x08,0x81,0x6d,0xe2,0xc1, + 0x5f,0xc9,0x42,0x57,0x96,0x23,0x3e,0x08,0x9d,0x6e,0xf8,0x06,0x5f,0x14,0xa5,0x7f, + 0x5b,0x86,0xd9,0xcd,0x09,0xed,0x65,0xa4,0x99,0xdb,0x90,0x6f,0x25,0x55,0xe2,0x9b, + 0x1a,0x3f,0x35,0x0c,0x51,0xf6,0xb8,0xc4,0x8e,0x1b,0x8e,0x38,0x54,0x27,0x62,0xa4, + 0xd0,0x0e,0xf9,0x31,0xbe,0x59,0xcc,0xdc,0x9a,0x36,0xf8,0x32,0x56,0x3c,0x26,0xce, + 0x98,0xe9,0x80,0x5e,0xd9,0x81,0x89,0x90,0x9c,0x1c,0xab,0xb0,0xe7,0x15,0xd5,0x2d, + 0x5b,0x39,0x54,0x0b,0xae,0x22,0x9b,0x6c,0x83,0x06,0xef,0xcc,0x02,0x30,0xfc,0x2e, + 0x24,0x93,0x27,0x5e,0x4a,0xe6,0x29,0xf7,0xad,0x6e,0xa4,0x20,0xb9,0x55,0x32,0x04, + 0x84,0x08,0x83,0x4a,0xdd,0x8e,0xae,0x54,0x3e,0xc6,0xa9,0x46,0xc9,0x43,0x92,0x03, + 0x02,0x29,0x8f,0x8e,0x6e,0x7c,0x4c,0xcc,0xf0,0xf5,0xdd,0xc3,0xa3,0x4d,0x0c,0xd6, + 0xf4,0x68,0xe3,0x49,0xc4,0x2b,0xe1,0x99,0x46,0x48,0xb6,0xb7,0x5b,0xe4,0x0c,0xc4, + 0x9b,0xbe,0xda,0x51,0x3d,0x13,0x19,0xca,0xd0,0xa4,0xe3,0xc0,0xab,0xf6,0x7a,0x2e, + 0xee,0xe5,0xc6,0xbe,0xa8,0x58,0x82,0x38,0x4c,0xae,0x6e,0x31,0x07,0x1a,0x37,0x26, + 0x5b,0x9c,0xa1,0xb5,0x23,0x93,0x0e,0xe9,0xee,0xc4,0x06,0xe1,0xba,0x81,0x89,0x16, + 0x5f,0xab,0xbd,0xf1,0xab,0xb3,0x5b,0x52,0xca,0x96,0xa4,0x3a,0x6c,0x61,0x86,0xe1, + 0x45,0x5c,0xb1,0x23,0x3b,0x30,0xec,0x8c,0x2e,0xcc,0x25,0x5e,0x55,0xe7,0x5e,0xc1, + 0xd4,0xee,0x68,0xe5,0x85,0x90,0x06,0x93,0x0d,0xff,0x93,0x33,0x77,0x26,0xb2,0x89, + 0x47,0xd2,0x02,0x6b,0x7b,0xce,0x85,0x61,0x74,0xa0,0x2d,0x47,0x61,0xde,0x17,0x86, + 0xd1,0xc1,0xba,0xdd,0x85,0xc5,0x65,0xc9,0x4d,0x9b,0x05,0x7a,0x07,0x57,0x91,0xcf, + 0x2c,0x2a,0x4e,0x31,0xc0,0x94,0x2a,0xaa,0xa1,0xd1,0x2b,0x66,0xe8,0xfe,0xcd,0xd1, + 0x2e,0x69,0xb5,0x5a,0x9a,0xa3,0x50,0x78,0xf8,0x2f,0x92,0x95,0x44,0x9e,0xda,0x2d, + 0x17,0x09,0x44,0x2e,0xc8,0x08,0xce,0x2d,0x3e,0x1c,0x21,0x03,0x26,0xb0,0xeb,0xfc, + 0x61,0xe7,0x83,0xff,0x04,0x5a,0x61,0x92,0x47,0x93,0xf0,0x2f,0x08,0x0e,0x4c,0x03, + 0x68,0xad,0xc9,0xbb,0x81,0x60,0x81,0x5f,0x83,0x92,0x0e,0x0d,0x3d,0xa6,0x04,0xbb, + 0xfc,0x5f,0x11,0x18,0x94,0xa7,0xb6,0x74,0x8d,0x1b,0xa8,0x41,0x4d,0xc9,0x3a,0xe4, + 0x4c,0x97,0x4d,0xc0,0x82,0x13,0xbf,0xff,0xea,0x61,0x08,0x66,0x11,0x25,0xcb,0x50, + 0xc3,0xfd,0x8b,0xe9,0x74,0xc2,0xea,0xbe,0xe3,0x0a,0x85,0x0f,0xdd,0xe4,0xdb,0x74, + 0x40,0xec,0xaf,0x35,0x85,0x4c,0x39,0x4f,0x05,0x25,0xcc,0x04,0x16,0xcf,0x5b,0x1a, + 0x19,0x10,0x34,0x49,0x6c,0x68,0xc5,0xa7,0xe4,0x8f,0x3c,0xe0,0x21,0xab,0x94,0x90, + 0x3c,0x50,0x52,0x22,0x46,0x25,0xf9,0x81,0xdc,0x75,0xc4,0xb0,0x52,0x4b,0xff,0x32, + 0x81,0x93,0x18,0xc2,0x0a,0x3c,0xf6,0xa1,0x23,0x14,0x15,0xe8,0x27,0x37,0xf3,0xc2, + 0x2a,0x00,0xcd,0x89,0xbe,0x42,0x01,0x46,0x0e,0x77,0x11,0x72,0x09,0x9c,0x72,0x30, + 0xde,0x12,0x31,0xf4,0xf1,0x80,0xf5,0x3c,0x9d,0xe0,0xa5,0x13,0x45,0xdc,0x40,0xf7, + 0xa7,0xac,0xf3,0xf1,0x5e,0x75,0x32,0xab,0x8e,0x6a,0xe7,0xc5,0x9a,0x3a,0xa3,0x15, + 0xed,0xac,0xad,0x53,0x6a,0x67,0x4d,0xd1,0x12,0xf4,0x35,0x25,0x6d,0x98,0x6b,0x87, + 0x69,0x95,0x5c,0xdb,0x51,0x55,0x52,0x10,0xbb,0xa6,0x72,0x9e,0x20,0x9e,0x40,0x83, + 0x92,0xd7,0x00,0x43,0xc6,0xd4,0x70,0xcc,0x9b,0x86,0x2a,0xce,0x71,0xdc,0x5f,0xfe, + 0x80,0x91,0x94,0x22,0x0c,0xe2,0x3e,0x6a,0x0a,0xbb,0xf9,0x95,0x13,0x58,0x46,0xff, + 0xd2,0x85,0x80,0x32,0xb6,0xcf,0x72,0xfa,0xb2,0xcf,0xbe,0xe4,0xbd,0x65,0x9f,0x3d, + 0xf0,0x24,0xdb,0x61,0xcc,0xf2,0xd4,0xde,0x43,0x50,0xf1,0xe1,0xd5,0xf8,0x0b,0x4b, + 0x0b,0x4b,0x8b,0x63,0x0e,0x24,0x70,0xc3,0x4b,0x71,0x8f,0x02,0xf7,0x38,0x03,0xf9, + 0x54,0x09,0x6e,0x78,0x81,0xb2,0x06,0x94,0x7a,0x0e,0x44,0x26,0x11,0x66,0x5c,0x4d, + 0xe5,0x5c,0xf7,0x1d,0x82,0x22,0x76,0xa2,0xff,0x02,0x6b,0x02,0xc1,0x90,0xfa,0x01, + 0xec,0xca,0xce,0xfd,0xab,0xcc,0x45,0xbd,0xe1,0xae,0xac,0x0f,0x5c,0x7c,0x48,0x5e, + 0x47,0x85,0xd2,0x07,0x80,0xc5,0xe7,0x86,0xe0,0xae,0x46,0xaf,0x50,0xa4,0x08,0x1d, + 0x79,0xc0,0x23,0xb6,0x62,0x2c,0xc6,0x04,0x88,0xe1,0x8c,0xbf,0x32,0x14,0xc1,0x2e, + 0x10,0x18,0xe2,0x9b,0xbb,0x4b,0xb1,0x94,0x93,0x53,0x4c,0x0c,0xf3,0xb3,0xb6,0xba, + 0xe6,0xad,0xc8,0x5d,0x2a,0x59,0x3f,0x5c,0x33,0xcb,0x1c,0x2b,0xe8,0x4e,0x0c,0x77, + 0x91,0xd1,0xdf,0x23,0xbe,0xb9,0x50,0x1c,0x13,0xbf,0x75,0x16,0x78,0xbc,0x14,0x0f, + 0xc7,0xd2,0x04,0x3a,0xac,0x2a,0x69,0x8c,0xe8,0x09,0xf1,0xa5,0xe0,0xb3,0xf7,0xda, + 0x6b,0x22,0x7b,0x78,0x86,0x81,0x5e,0x92,0x7a,0xe4,0xdc,0xa2,0xdb,0x8e,0xaf,0x78, + 0xad,0x52,0x84,0xda,0xf7,0xa6,0xb3,0x90,0x96,0xac,0x15,0x54,0x0c,0x69,0x9a,0x8a, + 0x05,0x6a,0x68,0x51,0x28,0x39,0x6a,0xc6,0x17,0xcb,0x4c,0xd6,0x6e,0x2f,0x28,0x32, + 0x06,0xa4,0xf3,0x0c,0xcf,0x85,0xd2,0x04,0xfe,0xa5,0x8f,0xdd,0xa1,0x9f,0x7f,0xed, + 0x3b,0x37,0xbc,0x77,0xa2,0xd0,0x61,0xc7,0x15,0x84,0xb2,0xb3,0xa0,0x0c,0x41,0xfb, + 0xa1,0x2b,0x50,0xcb,0x89,0x6f,0xb0,0x91,0x95,0xc8,0x56,0x23,0x3b,0x79,0xfb,0x01, + 0x43,0x61,0x76,0xee,0x87,0x46,0xe2,0x10,0xdf,0x83,0x46,0xde,0x83,0x86,0x21,0xeb, + 0x45,0x56,0x8b,0xd3,0x4a,0xdc,0x04,0xbf,0x92,0x4b,0x27,0x3a,0x22,0x3a,0xad,0x20, + 0x2e,0xbb,0xc4,0x73,0x0e,0x88,0x4d,0x64,0x5d,0x7c,0xda,0x81,0xb9,0xd7,0x73,0xf1, + 0x63,0xa3,0xcf,0x53,0xa1,0xf3,0x51,0xde,0xe0,0x01,0x72,0x2b,0x71,0x84,0x77,0x01, + 0xe3,0xb1,0x9e,0xab,0x3c,0x6c,0x66,0x21,0xc6,0xa9,0xd4,0x55,0xcd,0xea,0xaa,0xc6, + 0xa2,0x6a,0x98,0x5f,0xad,0xae,0x5a,0xd7,0xa8,0x68,0x33,0x54,0xd5,0xe8,0xcc,0x45, + 0x2e,0xbf,0xcc,0xa3,0x9f,0x2b,0x14,0x2d,0xc4,0x24,0xfd,0x3c,0x37,0x50,0xdb,0x42, + 0x5a,0x91,0xdb,0x7e,0xa4,0x52,0xcc,0x6e,0x16,0x69,0x41,0x81,0x17,0x94,0xcf,0x14, + 0x86,0xd3,0x64,0x26,0xfc,0x15,0x2f,0xe7,0x45,0x32,0xb0,0xe9,0xb8,0xb2,0x2d,0xbb, + 0x75,0x56,0x8c,0xce,0xc5,0x71,0x26,0xaa,0x8d,0x03,0x82,0x05,0xad,0x45,0xc2,0x4a, + 0xe5,0x4f,0x75,0xb7,0x72,0xf7,0x00,0xe6,0xe0,0xc2,0x67,0x32,0xe7,0x0b,0x42,0x45, + 0xbc,0xac,0x56,0x0f,0x55,0xc8,0xac,0xd6,0x0d,0x7d,0xda,0x58,0x65,0xcd,0x8f,0x37, + 0x55,0x59,0x33,0x14,0x2c,0x43,0x5d,0xbb,0xfb,0x91,0xcc,0x86,0x8e,0xb8,0x8c,0x58, + 0x75,0xdb,0xa0,0x18,0x23,0xdc,0x60,0x16,0x91,0xa5,0x58,0x46,0x18,0x47,0xac,0xe0, + 0x85,0x72,0x1c,0xcd,0xc2,0xf1,0x6a,0xf4,0x34,0xba,0xd4,0xdd,0x64,0xbc,0x0b,0x81, + 0x40,0xa9,0x60,0x66,0xda,0xc1,0xa2,0x00,0x8c,0x60,0xaf,0x8e,0x1c,0x35,0x4a,0xe0, + 0x40,0x09,0x96,0xda,0xcc,0x64,0x18,0x43,0x03,0x06,0x74,0x8c,0x8a,0xe8,0x02,0xc6, + 0x6e,0x27,0x34,0xad,0x61,0xc9,0x92,0xbe,0x81,0x5b,0xd0,0xed,0x44,0xcb,0x3f,0xd2, + 0x85,0x9b,0x7c,0x15,0xb8,0xed,0x9f,0x53,0xd6,0x27,0xca,0xf1,0x4b,0xd0,0xb8,0x6f, + 0xd0,0xa6,0xc5,0x1f,0x26,0x15,0x69,0xdf,0x7d,0xa0,0x3b,0x18,0x49,0x54,0x9a,0xf2, + 0x7c,0xf5,0xce,0xae,0xb5,0x33,0x0b,0x55,0xe9,0x15,0x65,0x09,0x00,0xba,0x3a,0xb9, + 0xac,0xec,0xea,0x1a,0x40,0x1f,0x53,0xf9,0x3a,0x15,0x41,0x1f,0x90,0xb7,0xc8,0xc0, + 0x38,0x99,0x14,0x63,0x58,0xbb,0x85,0xe1,0xf5,0x81,0xbe,0x18,0x09,0xe5,0x5e,0x7b, + 0xbe,0x41,0x93,0x78,0x6d,0x30,0x50,0x9c,0x95,0xd4,0x34,0xb1,0xeb,0x0d,0x56,0x36, + 0x9f,0x99,0xcd,0xd7,0xec,0x56,0x0b,0x5a,0x50,0x7d,0xaa,0x34,0xcf,0xb1,0x57,0x56, + 0xf3,0xe5,0xc9,0x5d,0xd3,0x7c,0xa1,0x9a,0xbf,0xf7,0x66,0x39,0xd3,0xc1,0xea,0x8d, + 0xf2,0x55,0x4d,0x8d,0xcc,0xa6,0x56,0x53,0x82,0x05,0x5e,0xec,0xef,0x1b,0xf7,0x2e, + 0x7b,0x52,0x33,0x10,0x3b,0xd5,0xec,0x2d,0xe2,0x68,0x59,0xd2,0xca,0x12,0xb0,0x54, + 0xe9,0x0d,0x40,0xfd,0x79,0x12,0x16,0x78,0x2e,0x2f,0xb7,0x05,0x60,0x06,0xd2,0x39, + 0xc8,0x29,0x3a,0xd1,0x38,0x41,0x65,0xc8,0x2b,0xc9,0x14,0x45,0x8c,0x58,0x7a,0x49, + 0x07,0x0c,0x15,0xc3,0x55,0x92,0x9c,0x75,0x65,0xac,0x48,0x65,0x3c,0x1d,0x2c,0x4d, + 0x61,0x3e,0xf7,0x11,0xaf,0x4b,0x3c,0x89,0x68,0x74,0xe8,0x9e,0xca,0x0d,0x28,0x76, + 0x63,0xf2,0xe2,0xd9,0x55,0x45,0x24,0x25,0x3d,0x90,0x65,0xc4,0xf1,0x52,0x82,0x0e, + 0x06,0x1d,0x73,0x20,0xaf,0x51,0x6c,0x65,0x24,0xef,0xa1,0x0a,0xe5,0xdd,0xde,0xa1, + 0x50,0x5e,0x64,0x79,0x45,0x9f,0xfc,0x6e,0x23,0x0c,0xe9,0x95,0xb1,0xbc,0x87,0x22, + 0x96,0xf7,0xf0,0xb5,0x0e,0x31,0xa7,0x06,0x8e,0x6a,0x7d,0x18,0x28,0xf5,0xc4,0xd4, + 0x7d,0x93,0xa7,0xcb,0x0a,0x15,0x35,0x07,0x50,0x17,0x2b,0xda,0xed,0xec,0x0d,0x36, + 0xcc,0x42,0xd2,0x01,0xa6,0x63,0x3c,0x07,0x16,0x10,0x3b,0x62,0x53,0xf8,0xc2,0xc6, + 0xf7,0x0d,0xd8,0x3c,0x34,0x02,0x36,0x0f,0x79,0xe7,0xfb,0x10,0x43,0x36,0x19,0x27, + 0x22,0x64,0xf3,0x90,0x37,0xcc,0x0f,0xad,0xa0,0xcd,0xb1,0x88,0xda,0x5c,0x12,0x29, + 0x1e,0xb2,0x4b,0x52,0x5f,0xcd,0x0e,0x3d,0x39,0xfc,0xa4,0xc6,0x6e,0x05,0x4f,0x1e, + 0xae,0x8c,0x9e,0x3c,0x7c,0x5d,0x8a,0xef,0x28,0xa3,0xbb,0x26,0xc0,0xa3,0xa2,0x74, + 0x80,0x1e,0x7b,0x6e,0x46,0x7f,0x88,0xae,0x1d,0xa2,0xb7,0xf4,0x50,0xc5,0x7f,0x88, + 0x4e,0x1c,0xbe,0xe6,0xd4,0x91,0xc0,0xd9,0x8a,0x08,0x90,0x71,0x75,0xc7,0x80,0xa2, + 0xdc,0x8c,0x1c,0xaa,0x74,0x48,0xdb,0x97,0x08,0x1b,0xfe,0xfd,0x84,0x94,0x25,0x2b, + 0xd7,0x6f,0x58,0x8f,0x6b,0x76,0x15,0xc6,0x25,0x8f,0xbe,0xe8,0x7f,0x1b,0xb7,0x5b, + 0x44,0xaf,0xf9,0xf7,0x27,0xde,0xf0,0x38,0x7c,0x2d,0xde,0xa0,0x18,0xaf,0x8d,0x2f, + 0x91,0x05,0xac,0x7d,0xa5,0xb1,0xb1,0xb3,0x82,0x13,0x4a,0x05,0xb9,0x19,0x99,0x2b, + 0x3c,0x7e,0xa5,0x5c,0x42,0x9d,0x2c,0x62,0x6c,0x8b,0xaf,0x6d,0x81,0xb1,0x83,0xc4, + 0x40,0x90,0x4a,0x2d,0xa8,0x79,0xaa,0x29,0x52,0x6a,0xa1,0x76,0x87,0x66,0x5c,0xb3, + 0x97,0xa4,0xd2,0x4a,0x8e,0xd8,0x4a,0x3f,0xd5,0x8e,0x6c,0xb8,0xc8,0xa5,0xb4,0x56, + 0xf7,0x27,0xeb,0xaf,0xca,0x95,0xb8,0xb2,0xe2,0x7b,0xda,0xfa,0xc4,0xca,0xcf,0xf1, + 0x8e,0x1d,0xd0,0x94,0x77,0x77,0x56,0x38,0x78,0xdf,0xaf,0xf0,0xef,0xbe,0xff,0x46, + 0xf7,0x2e,0xcd,0x08,0xbe,0x81,0x80,0x0e,0xdd,0x31,0x47,0xf4,0x94,0x9c,0xb8,0x63, + 0x3b,0x78,0x44,0xf9,0x71,0xc7,0x76,0xfc,0x08,0xbb,0x72,0xc7,0xd5,0x0d,0xd0,0x7b, + 0xa0,0xa1,0xf2,0x52,0xc9,0xd8,0x42,0xdf,0x8a,0x09,0xa3,0x0d,0x6d,0xcd,0xa6,0xf4, + 0xce,0x24,0xdd,0x58,0x7f,0x4f,0x52,0xf5,0x04,0xa1,0xe0,0x65,0xc0,0x5e,0x1d,0xb9, + 0xd2,0xcd,0xc0,0xba,0xd4,0xa2,0x73,0xe3,0x7d,0x2b,0xc9,0x12,0x9c,0x8e,0xd1,0x52, + 0x67,0x55,0x4b,0x5d,0xb3,0xa5,0xee,0xca,0x96,0x2a,0xfb,0xb0,0xe3,0xda,0x2d,0xf9, + 0x6f,0x22,0xf4,0xd2,0x56,0xe4,0x0c,0xa6,0x74,0x26,0xb7,0x22,0xcd,0xdb,0x91,0x71, + 0xdb,0xd1,0xba,0x1f,0x99,0xb6,0x1e,0xc7,0xa5,0xad,0x47,0x0c,0x49,0xa2,0x77,0x1b, + 0xcc,0x5d,0x44,0x4c,0xbc,0xf1,0x56,0xef,0x25,0x8e,0x2b,0xdb,0x90,0xdf,0x05,0xe6, + 0x4e,0x89,0x97,0x8a,0x71,0xc6,0x9b,0x7e,0x2b,0x76,0xad,0x97,0xa2,0x2c,0xee,0x41, + 0x73,0xc1,0xfb,0x2c,0xa5,0x95,0xdb,0xd2,0xd5,0x99,0xda,0x36,0xe9,0xb2,0x8e,0x61, + 0xaf,0xa7,0xa0,0x65,0x1d,0xdb,0x34,0x09,0x68,0x29,0x89,0xe7,0xf2,0x8f,0x21,0x9c, + 0x6a,0x77,0x82,0x6c,0x5c,0x69,0xca,0x77,0x76,0x50,0x92,0x48,0x85,0x96,0x9e,0x24, + 0xea,0xca,0x0a,0x32,0xc4,0x46,0x6c,0xd5,0xd0,0x99,0x2d,0x8e,0x74,0x92,0x17,0xfc, + 0xa8,0x48,0x5d,0x91,0x20,0x23,0x9d,0x9e,0xdb,0xc9,0x24,0x91,0xd5,0xd9,0xbf,0xd2, + 0xf3,0x23,0x03,0x23,0xac,0x98,0x14,0x56,0x3e,0x99,0x60,0xc5,0xed,0x52,0x7d,0xba, + 0x74,0x4c,0x8e,0xda,0x0c,0xab,0x1c,0xaf,0x0c,0x9a,0x1c,0xaf,0x8a,0x69,0x1b,0x5b, + 0x01,0x68,0x2e,0xb7,0x8d,0xc7,0x2c,0xe9,0x07,0x5e,0x26,0x31,0x79,0xe1,0x70,0x6c, + 0xeb,0x3f,0xfe,0xfb,0xbf,0x39,0xb8,0x1b,0x2c,0x4e,0x49,0x42,0xef,0x75,0x88,0x6c, + 0x97,0x67,0x15,0xa5,0xd6,0xae,0x9a,0xdf,0x4e,0xb7,0xfc,0xae,0x8b,0x18,0x92,0xde, + 0xff,0xf9,0xe3,0x87,0x50,0xb9,0x28,0x39,0x50,0x27,0xb5,0xd0,0x6b,0x43,0xd1,0xb4, + 0x4a,0x35,0x91,0x6a,0x0f,0x45,0xd3,0x8d,0x57,0x46,0xe9,0xad,0x6b,0x01,0x77,0x94, + 0x28,0x8a,0x9d,0x6e,0x21,0x31,0x06,0x88,0xfb,0xc8,0x8c,0xbf,0x86,0xef,0x18,0x12, + 0xbd,0xbe,0x5d,0x05,0xdd,0x78,0x31,0xa6,0x8a,0xdd,0xa6,0x81,0xdd,0xbd,0xd5,0x55, + 0x8d,0xeb,0x00,0x6b,0xab,0x92,0x2e,0x66,0xde,0x2d,0xc4,0x4f,0x81,0x96,0xf7,0x24, + 0xc7,0x15,0x65,0x52,0x4a,0x32,0xf6,0x39,0xd0,0x75,0xc1,0xd5,0xeb,0xed,0xcb,0xc7, + 0x40,0xd5,0x56,0x15,0x9d,0x6c,0x22,0x8e,0xac,0xcf,0xe5,0x6a,0x87,0x97,0xe4,0x93, + 0xec,0xed,0x92,0x0c,0x12,0x5d,0x5d,0x74,0xbc,0xcf,0xb8,0xda,0x2f,0xc8,0x5c,0x93, + 0x97,0x93,0x6b,0x6e,0x41,0x37,0x24,0xca,0x3c,0xc5,0xd7,0x39,0xaf,0xdb,0x30,0x8e, + 0x0d,0x18,0xee,0x7a,0x6d,0x3b,0xf8,0x0a,0x07,0x9e,0x6d,0x73,0x7c,0x87,0x7f,0x5a, + 0x3a,0xef,0x6d,0xfb,0x6c,0x85,0x07,0xdf,0xb0,0xb1,0xd4,0x2c,0xac,0xd9,0x95,0x58, + 0xb5,0x25,0x61,0x3d,0x09,0x77,0x64,0x5b,0xc1,0x46,0x3f,0xfe,0x69,0x0f,0xf4,0x7d, + 0x30,0x53,0xe7,0x72,0x5e,0xd9,0xb9,0x6f,0xc7,0x98,0x92,0x72,0xea,0xa9,0x8a,0x95, + 0xf8,0x12,0x96,0x1c,0x9d,0x92,0xfb,0x64,0x49,0x42,0xeb,0x14,0xda,0xca,0x7d,0x1e, + 0xb6,0xf7,0xa8,0xfa,0xeb,0xfb,0x59,0x59,0x65,0x95,0x4f,0xbd,0x68,0xc7,0x73,0x4c, + 0x17,0x56,0x75,0x50,0x45,0xe4,0xb7,0x1a,0xa0,0xf4,0x72,0xc0,0x54,0x8c,0x8e,0x3a, + 0xbc,0x63,0x96,0xbd,0x5e,0xfc,0x78,0x83,0xca,0xee,0xaa,0x6c,0x7c,0xfd,0x81,0xe2, + 0xad,0xf9,0xca,0xff,0xfa,0x63,0xc4,0xf5,0xce,0x15,0x7e,0xa7,0x54,0xb9,0x58,0xec, + 0x58,0xa7,0xb2,0x77,0xe5,0x50,0x1c,0x56,0x76,0xbf,0x36,0x81,0x15,0x27,0x81,0xe1, + 0x44,0x29,0x39,0x75,0xee,0xee,0x49,0x40,0xa6,0x53,0xe5,0x50,0x1c,0x89,0xbe,0x57, + 0xc5,0xb2,0x5b,0x45,0x56,0x36,0xae,0x80,0xa8,0x20,0x62,0x75,0x6c,0x84,0x39,0xaf, + 0x84,0x74,0xeb,0x64,0xbf,0x61,0x11,0xe0,0x94,0xe0,0x0d,0x60,0x62,0x4e,0x3c,0xc5, + 0x6c,0xd4,0xc9,0x37,0x2b,0x14,0xea,0x00,0xef,0x23,0x69,0xf1,0xdb,0xd5,0x0d,0x4f, + 0xef,0xc4,0xe2,0xce,0x13,0xe1,0x3a,0x8c,0x57,0x5d,0x1e,0x07,0x39,0x78,0x5f,0x0d, + 0x54,0x6c,0x2d,0x88,0x9a,0xe8,0xf4,0x37,0xa4,0xae,0xbd,0x4a,0xae,0xbe,0xd6,0x1d, + 0x5f,0x6a,0xa5,0x9e,0x81,0xae,0xc4,0x7e,0x3d,0xb0,0xc6,0xdb,0x38,0x95,0xaf,0xd5, + 0x3b,0xcb,0x3c,0xe4,0x53,0xd3,0x62,0x67,0x07,0xaf,0x12,0xa1,0xf7,0xda,0xe9,0x7a, + 0xe7,0x75,0xb6,0x11,0xc2,0x21,0x62,0xc1,0xda,0xc5,0x55,0x4a,0xe7,0x9c,0x63,0x80, + 0x86,0x4a,0x7b,0xe0,0x30,0x02,0x9d,0x7c,0x16,0x2c,0x42,0x3e,0x3a,0xdd,0x10,0x17, + 0x6f,0xf1,0x23,0xdc,0xe6,0xdd,0x62,0x0f,0xdc,0x7b,0x5b,0xa6,0xf7,0x37,0xe1,0xe4, + 0x63,0x99,0xb2,0xcd,0xf7,0x15,0xa4,0x88,0xb3,0x3d,0x46,0xed,0xc4,0xbb,0xcf,0x44, + 0x3b,0x32,0xfc,0x4d,0xcf,0x37,0xa7,0x58,0xa2,0x43,0x94,0x1d,0x3a,0xea,0x4d,0x18, + 0xa6,0xf8,0x45,0x90,0xe9,0x8b,0x8c,0xf4,0x64,0x9e,0x9b,0x21,0x77,0x72,0xf3,0xf9, + 0x56,0xbe,0x79,0xc5,0xc0,0x7c,0x87,0xef,0x91,0xe9,0xe0,0x7b,0x57,0x3b,0xf8,0x54, + 0xd9,0x8e,0x2f,0x1d,0x47,0x22,0xf2,0x4e,0x6c,0xda,0x7e,0x37,0xb1,0x56,0xd7,0x23, + 0x5d,0x6f,0xa5,0x6f,0x8f,0x5e,0x35,0x3a,0x93,0x30,0xe5,0xbe,0x18,0x17,0xd2,0x0c, + 0xc6,0xb8,0x54,0xc1,0x5e,0x9a,0xb8,0xe9,0x9a,0x2d,0xc7,0x28,0xc5,0x26,0x87,0x31, + 0xde,0xae,0x2b,0xaa,0x7e,0x3b,0xf7,0x2b,0xf1,0x29,0xd3,0x0d,0xac,0x17,0x8a,0x7e, + 0xd8,0x1d,0xf0,0xf0,0xb5,0x21,0x53,0xe1,0x83,0xc9,0xaf,0x40,0xd0,0x6e,0xb3,0xe3, + 0x19,0x10,0xf0,0xc1,0xf6,0x6f,0x04,0xd0,0xb1,0x2e,0x20,0x31,0x73,0x26,0x61,0x8c, + 0x57,0x4f,0x7f,0x85,0x85,0xf1,0xd5,0x6d,0x9a,0xfa,0x81,0x57,0x95,0xa9,0xff,0x81, + 0x49,0xfd,0x56,0x88,0x87,0x20,0x72,0xd3,0xeb,0x5f,0x7a,0xfc,0xc6,0x2a,0xf6,0xd8, + 0xe1,0x3e,0x79,0x56,0xf5,0x12,0xfb,0x6c,0xad,0x7e,0x66,0xa7,0x56,0xa0,0xe9,0x9a, + 0x7f,0xe8,0xdc,0x96,0xe5,0x82,0xde,0x6e,0xb8,0x9f,0x50,0x30,0x4d,0x1a,0xeb,0xa5, + 0xca,0x3f,0xf1,0xfd,0x4f,0x78,0xad,0x62,0x4d,0x44,0x2d,0xb2,0x50,0x8a,0xaa,0xc5, + 0x0b,0x5d,0x1e,0x93,0xbf,0x52,0x54,0xc0,0xfb,0xc4,0x2b,0x15,0xd4,0xa1,0x20,0x23, + 0xfa,0x8e,0x0c,0x8b,0x7f,0xfc,0xeb,0xff,0x24,0x38,0x95,0x02,0xf8,0xd6,0x24,0x16, + 0x78,0xfd,0x85,0xee,0xae,0xa8,0x58,0x44,0xce,0xff,0xfd,0x3f,0x4e,0xd9,0x2a,0x42, + 0x96,0x48,0x19,0xf4,0x8c,0x45,0x4a,0xf5,0xa1,0x04,0x7e,0x95,0x46,0x28,0xa1,0x83, + 0x5d,0xc3,0x3d,0xc7,0xb7,0xee,0xf1,0x38,0x55,0xa5,0xe7,0xf5,0xf6,0x88,0xd5,0xf7, + 0x7a,0x6b,0x47,0x6d,0x4b,0xf3,0xfe,0x07,0xe4,0x9f,0x66,0xf8,0x8e,0x88,0x67,0xee, + 0x13,0xa0,0xab,0xb7,0xad,0x3a,0x90,0xaa,0x3b,0x2a,0x45,0xf4,0xb0,0xdb,0x33,0xc1, + 0xd3,0x66,0xdb,0x95,0x0e,0xe9,0xe5,0x5a,0xaf,0xa3,0xf5,0xb5,0x68,0x03,0xb6,0x52, + 0xeb,0x15,0xd8,0x5b,0xdf,0xd3,0x9a,0x55,0xcf,0xba,0xba,0x42,0x6c,0xa5,0x99,0xb6, + 0x9f,0x38,0x49,0x20,0x1f,0x1e,0xad,0xc0,0xd3,0xef,0xc3,0x37,0x56,0x5e,0x35,0x6b, + 0x45,0x7d,0x97,0xeb,0xac,0xd5,0x18,0xca,0x35,0xd5,0x35,0x67,0x55,0x72,0x16,0x0f, + 0xbb,0x57,0x09,0x7a,0x71,0xf9,0x61,0xb9,0x32,0xa0,0x5c,0x96,0x24,0x62,0x60,0x3f, + 0x48,0xe3,0x6b,0x42,0x08,0x8a,0x1d,0xff,0x21,0x72,0x68,0xc5,0xa2,0x36,0xa7,0x6f, + 0x28,0xbd,0x1c,0x86,0x88,0x22,0x8f,0x85,0x39,0x45,0x75,0x85,0x66,0x51,0xdd,0xee, + 0xa5,0xb9,0x83,0x33,0x70,0x6c,0x96,0xe4,0xd4,0x88,0x1b,0x74,0x94,0x43,0x23,0x96, + 0x5e,0x6d,0x2f,0x84,0xef,0xb0,0x4b,0xd7,0xf2,0x32,0x61,0x82,0xd9,0x8d,0x7c,0xc5, + 0x08,0xbb,0xbc,0xaf,0x11,0xd6,0xa3,0x3b,0x26,0x28,0xe5,0x4a,0x2d,0x5d,0x7a,0x98, + 0x47,0x8d,0x91,0xcf,0xdd,0xd3,0x6a,0x08,0x46,0x39,0x5d,0x54,0x6d,0xd1,0xff,0xb3, + 0x15,0x79,0x74,0x42,0xf2,0x2a,0xc0,0x87,0x5e,0x8d,0xf2,0xbe,0x59,0x80,0x9e,0xad, + 0xb5,0x07,0xf6,0xcf,0xda,0xc1,0xf7,0x9c,0x8b,0x95,0x96,0xb0,0x18,0x77,0xd9,0x0e, + 0xfe,0x4f,0x38,0x43,0xd8,0x5d,0xd1,0x5b,0x7a,0xe4,0xd9,0x5e,0x24,0x7a,0xf3,0xdf, + 0xc2,0xb8,0x75,0x1f,0x1d,0x3f,0x08,0x4a,0x66,0xad,0xbd,0x78,0x74,0x60,0x82,0x6f, + 0x81,0x7d,0x5c,0xa9,0xbc,0x7e,0x3d,0x7d,0xcb,0x8c,0x90,0x69,0x69,0xda,0xb4,0x6a, + 0x26,0x6e,0xcb,0x0b,0xce,0x58,0x97,0xce,0xdd,0xb7,0xb4,0xa1,0xac,0x50,0x77,0xed, + 0x94,0x9f,0x95,0x5c,0x6b,0x92,0x72,0x6b,0x92,0xf9,0x81,0x56,0x39,0x0d,0x40,0xed, + 0xe6,0xca,0x61,0xee,0x70,0x3f,0x14,0x59,0x12,0xe0,0x8f,0x60,0x97,0xe2,0xe9,0x56, + 0x61,0x2b,0x0a,0x4f,0x6d,0xb5,0x2f,0x25,0x8e,0x67,0x44,0x81,0x94,0xb9,0x5f,0x49, + 0xd8,0xac,0xee,0x63,0x80,0xa1,0xc2,0xfa,0x9e,0x5a,0x7a,0xec,0x42,0x5c,0x4c,0xa7, + 0x7d,0x8e,0xf4,0x0c,0x03,0xe7,0x1d,0xb4,0x14,0x5c,0x97,0xce,0x58,0xf0,0x15,0x73, + 0x1c,0xb2,0x8d,0x97,0xfa,0x9b,0xb7,0xc6,0x51,0x0a,0x77,0x08,0x0b,0x88,0xba,0x20, + 0x86,0xf8,0xce,0x75,0xee,0xe2,0x3b,0xba,0x18,0x5b,0x19,0xa4,0xfa,0x86,0x6b,0x23, + 0x77,0x75,0xff,0xad,0x37,0x5f,0x1d,0xdd,0x82,0x5b,0x80,0x9d,0x2d,0x5e,0xe6,0xa9, + 0x68,0xa8,0x86,0xcf,0xe2,0x7e,0x91,0xdc,0x78,0x69,0xb6,0x08,0x22,0x11,0x97,0xbf, + 0x47,0xe2,0x2a,0xed,0x7b,0x1d,0x2c,0xa3,0x82,0x6b,0x35,0x85,0x4a,0xe9,0x15,0xe7, + 0xd0,0xc4,0xa5,0xdd,0x96,0x62,0x41,0xe5,0xcb,0x27,0xd1,0x30,0x11,0x14,0xd7,0xfb, + 0x6a,0xc1,0xa0,0x48,0xae,0xd2,0x81,0x2d,0xe3,0xb8,0xa6,0xa9,0x55,0x07,0xd2,0xa4, + 0x71,0x2c,0xbb,0x29,0x66,0x62,0xed,0x4b,0x75,0x16,0x82,0xbf,0x6e,0xdc,0xe9,0xf8, + 0xe2,0xa8,0x74,0x45,0x89,0x99,0x25,0x2f,0x85,0xe1,0x8b,0x1f,0x6b,0x83,0xae,0xb6, + 0xc5,0xfb,0xd9,0xd2,0xe8,0x5d,0x13,0x5b,0x55,0x32,0x92,0xaa,0x11,0x56,0x65,0x6f, + 0x9d,0xb4,0xda,0x2f,0xc5,0x6d,0x82,0x97,0xc6,0x5d,0x82,0x97,0x27,0xe3,0x14,0x9d, + 0x26,0x9c,0x41,0x1f,0x66,0xee,0x21,0x5f,0x35,0xc8,0x19,0x35,0xd1,0x49,0x88,0xc7, + 0xc5,0xa5,0x88,0x39,0x5a,0x5c,0x1a,0x41,0x47,0x50,0x27,0x42,0xae,0xd6,0x58,0x2c, + 0x61,0x39,0x35,0x64,0xe2,0xc7,0x60,0x6a,0x96,0x31,0x6e,0x8f,0x14,0x09,0xe6,0xc7, + 0xdb,0x20,0x2f,0xac,0x6f,0xdc,0x3b,0x33,0xef,0x28,0x3c,0xfe,0xf3,0x2f,0x27,0x2f, + 0x0f,0x3f,0xbc,0xa7,0x07,0x3e,0x77,0xcc,0xd4,0xd3,0x97,0x18,0x5b,0xdd,0xc1,0x23, + 0x3c,0xaa,0xfa,0x9b,0x44,0x5c,0xcb,0xc5,0x9f,0x1f,0xd4,0xa3,0x8d,0xfa,0x60,0xc2, + 0x25,0x3f,0x8b,0xe7,0x16,0x32,0x98,0x57,0x0c,0x63,0xa8,0x06,0xa2,0x4d,0xca,0xc5, + 0x8c,0x9f,0x13,0x2f,0x9c,0xff,0x82,0x57,0x97,0xea,0x20,0xcc,0x88,0x0e,0xc0,0x73, + 0xf6,0x33,0x3c,0xa6,0xbe,0x4b,0x87,0xd6,0xe9,0xe5,0x8e,0x76,0xe5,0xe5,0x0f,0x2e, + 0x4f,0xb7,0x3b,0x3f,0x41,0x8d,0x9a,0x4f,0x32,0x02,0xdd,0xa4,0x73,0x97,0xef,0x6b, + 0xee,0x79,0x74,0x39,0x6a,0x7b,0x5b,0x1d,0xfb,0x35,0x7a,0x95,0xa4,0x51,0x7e,0x63, + 0x86,0xd2,0x8a,0xe6,0x5d,0xec,0x56,0xa7,0xf5,0xc4,0x33,0xbb,0x50,0xdf,0x03,0x7c, + 0x05,0x9d,0x3a,0xd0,0x51,0x4f,0x45,0xe7,0x40,0x92,0x05,0x5d,0xd6,0xba,0xed,0xad, + 0xed,0x54,0xb7,0x4b,0xb1,0x9f,0xd0,0xa6,0x79,0xc1,0x98,0x86,0xbc,0xd3,0x23,0xc8, + 0xdb,0xbd,0x32,0x64,0x63,0x77,0x96,0x00,0x6d,0xf7,0xd6,0x37,0xd4,0xde,0x29,0x9d, + 0x21,0xb9,0x3c,0x9c,0x05,0x00,0x2b,0x8f,0x8c,0xc0,0xf6,0x80,0x88,0x95,0xc9,0xe4, + 0x99,0x03,0x59,0xf8,0x58,0x8d,0x7a,0x20,0x15,0x2f,0xa8,0x36,0xdf,0x54,0x1d,0x6c, + 0xc8,0xb2,0x8f,0xf1,0xe2,0x7c,0x28,0xdd,0x94,0xb5,0xb1,0xcd,0xe0,0x2b,0xda,0x9c, + 0x75,0x3b,0x93,0xb5,0x05,0x5c,0xba,0xb0,0x49,0xc0,0x94,0xec,0xa9,0x6c,0xc6,0xb3, + 0xc6,0xdc,0xe6,0x58,0xea,0x8e,0x7e,0xa7,0xa6,0x3c,0xde,0x23,0xf9,0xba,0xc7,0xca, + 0xb8,0xb1,0x2b,0x1a,0xfe,0x27,0x80,0x44,0x3f,0x5e,0xe3,0x00,0x0f,0x6b,0x22,0xc0, + 0xc4,0xc9,0x4f,0xce,0x5c,0x17,0x5d,0xc5,0x25,0x56,0xc7,0x65,0x5c,0x52,0x40,0x46, + 0x9b,0x5e,0x0c,0xa6,0x00,0xae,0x76,0xeb,0x49,0xef,0x7c,0xe5,0x15,0x3d,0x33,0x71, + 0x3c,0x73,0xa6,0x2f,0x87,0xa1,0x06,0xec,0x7b,0x01,0x28,0x49,0x44,0x1a,0xb4,0xc5, + 0xad,0x3b,0xaa,0x1f,0x90,0x76,0x65,0xa6,0x99,0x47,0x35,0xc5,0x29,0x1a,0x50,0xeb, + 0x68,0x01,0x04,0x59,0x26,0x4e,0xf1,0xc2,0x88,0xb1,0xfb,0x42,0xa0,0xd6,0x0d,0x5b, + 0x1c,0x7a,0xad,0x8e,0x97,0x2a,0xd6,0xf5,0xb3,0x56,0x8e,0x40,0x93,0xf5,0xb7,0x90, + 0x5c,0xd3,0x05,0xb0,0xa8,0x9a,0x1b,0x2c,0x6b,0x4b,0x72,0x2a,0x0a,0x14,0xbc,0x2a, + 0x1f,0x66,0x55,0x4f,0x03,0x65,0x19,0x5e,0x5c,0x82,0x78,0x2b,0xc5,0x42,0x59,0xa8, + 0x32,0x83,0xa1,0x2c,0x7c,0xa9,0x68,0xa8,0x0a,0xc6,0xf0,0x2d,0x49,0xbc,0xfb,0x0a, + 0x59,0xa3,0x2f,0x43,0x98,0x40,0x76,0xb6,0xd0,0x73,0x23,0xb3,0x80,0x4d,0xfa,0x1c, + 0x87,0xe1,0x3b,0xdd,0x32,0x4d,0x9e,0x82,0x34,0x74,0x13,0x7e,0xc2,0x86,0x19,0x93, + 0xd0,0xa3,0xec,0x13,0x0d,0x0f,0x98,0x97,0x7b,0x9a,0xa7,0x27,0xe9,0x95,0x3e,0x25, + 0x99,0x5d,0x90,0x37,0x19,0xb9,0xff,0x63,0x7c,0x03,0xe6,0x8a,0x17,0x22,0xd5,0xe0, + 0x3b,0xbc,0x68,0xa9,0x1a,0x35,0x11,0xef,0xa3,0xe5,0x84,0xeb,0xed,0x42,0xf6,0xd5, + 0x2c,0x82,0x89,0x74,0x11,0xc4,0x33,0x09,0x11,0x37,0x07,0xa8,0x4c,0xb3,0x89,0x17, + 0x41,0xd1,0x9d,0x04,0xd8,0xc4,0x50,0xe2,0x5d,0xb1,0x6c,0x92,0x54,0x8a,0xf9,0x43, + 0x21,0xa2,0xfd,0x37,0x09,0xdf,0xb1,0x88,0x2c,0x06,0xbf,0x3f,0xc8,0xf7,0x62,0x4d, + 0xde,0xa3,0xa3,0x79,0x29,0x1c,0xaf,0x6e,0x7a,0x07,0x02,0x35,0x00,0x4f,0xdd,0x9b, + 0x03,0xa5,0xe9,0x4d,0x5a,0x4c,0xa4,0xa3,0x0b,0x4c,0xfa,0xd8,0x84,0xfa,0xbc,0x13, + 0xdc,0x5e,0x0c,0xf4,0x19,0x4f,0x39,0x9f,0x13,0xc5,0x01,0x62,0x9f,0x8e,0xc4,0xdb, + 0x0b,0x28,0xa2,0x67,0xe1,0x64,0x59,0x3d,0x6b,0x67,0x64,0x10,0x02,0x58,0x02,0xd3, + 0x7d,0xb3,0x79,0x71,0x90,0x44,0x73,0x7a,0xa7,0x81,0xee,0xe1,0x77,0x79,0x42,0xbd, + 0xd2,0x95,0xcf,0x42,0x4c,0x53,0x2b,0x35,0xb7,0x4b,0x2b,0x23,0x83,0xcc,0xa5,0x0a, + 0x44,0x68,0x4f,0xd4,0x04,0x52,0x01,0x23,0x81,0x34,0x3c,0x28,0xc9,0x4f,0x7e,0xf0, + 0xf1,0xb3,0xed,0x76,0xbb,0xd2,0xeb,0xe8,0xcb,0xb7,0x06,0x25,0xb3,0x22,0x22,0x95, + 0x15,0xc3,0xc3,0x38,0x10,0xaa,0x89,0x9d,0x25,0x6f,0xac,0x95,0xa9,0xd2,0x93,0x87, + 0x60,0x44,0x50,0xb2,0xcc,0x52,0x01,0xc9,0x08,0x47,0xc4,0x23,0x8b,0xd5,0x74,0x8f, + 0x80,0x64,0x7b,0x5c,0x61,0xe1,0xf2,0xeb,0x52,0x6a,0xad,0xf0,0x03,0x4c,0x6c,0x96, + 0x2e,0xc2,0x04,0xf4,0xbb,0xcb,0x28,0xbc,0x6a,0xdc,0xe7,0x5d,0xa9,0xb5,0x38,0x5f, + 0x31,0x89,0x6c,0xc5,0x1a,0x2b,0x54,0x22,0x9b,0x69,0x9e,0x75,0x22,0x53,0x3f,0x52, + 0x6a,0x99,0xa9,0x92,0xd9,0xea,0x58,0x99,0xfc,0x2a,0x43,0xf9,0x9a,0x26,0xcd,0x88, + 0xd1,0xec,0xc3,0xdc,0x8b,0xab,0xd6,0xfd,0x1c,0xde,0xd4,0xb9,0xc7,0xe8,0x5d,0x37, + 0x72,0x8e,0x40,0x01,0x56,0x8f,0x5e,0xe6,0xb0,0xd2,0xc2,0x06,0x3d,0xe3,0x63,0x30, + 0x27,0x6e,0x8e,0x2c,0x3d,0x61,0x18,0xac,0xdd,0x35,0x43,0xce,0xf0,0xfd,0xce,0x4a, + 0xa9,0x0a,0x1b,0xde,0xc5,0xfc,0x1b,0x1b,0xe4,0x5b,0x7f,0xaf,0x57,0x39,0x88,0xaf, + 0x69,0xb0,0x61,0xec,0x19,0xae,0x47,0x6d,0x89,0x94,0x8e,0xe2,0xb2,0x95,0x13,0xa8, + 0x94,0xd5,0x7e,0xe7,0xf2,0x61,0xf5,0x72,0xb5,0xb5,0xf4,0xb9,0xa2,0x72,0xed,0x0e, + 0x75,0x59,0x25,0xd2,0xf7,0x7c,0xf7,0xf5,0x95,0xd9,0x82,0xae,0xac,0x1e,0xdc,0xc3, + 0xf5,0x51,0x3a,0x9f,0x6c,0x9f,0xb0,0xaa,0x3b,0xa8,0x6c,0x3a,0x35,0x6a,0xdc,0xbd, + 0xa6,0xa5,0x55,0xf2,0x79,0x30,0xab,0x3a,0x3a,0x78,0x77,0xfc,0xcb,0xa7,0x0f,0x1f, + 0x8f,0xf8,0x09,0x06,0xd6,0x8e,0x1a,0x05,0x85,0xa0,0x61,0xb8,0x2a,0x29,0x4a,0x8d, + 0x51,0x10,0xe3,0xda,0x9d,0x88,0xa4,0x27,0x3d,0xf5,0xe4,0x3a,0x3f,0xac,0x40,0xef, + 0xf8,0x35,0x2e,0x70,0x62,0x6e,0x1a,0xe7,0xca,0x79,0x78,0x70,0xfa,0x53,0x15,0x76, + 0x9e,0x04,0x0b,0x03,0xf4,0x6f,0x4b,0x44,0x81,0x09,0x37,0x9d,0x16,0x26,0xd4,0x38, + 0xf8,0x62,0xc2,0xc4,0xc7,0x46,0x3e,0xa5,0x14,0xaa,0x28,0x8e,0x33,0xe8,0x21,0xb4, + 0xa6,0xc0,0x72,0x5d,0xf7,0xac,0x48,0x17,0xfc,0xa0,0x03,0x5e,0xa5,0x88,0x71,0x4a, + 0x78,0x27,0xb1,0x4d,0x63,0xea,0x72,0x7d,0xc5,0xb4,0x2d,0xaf,0x65,0xbb,0xf5,0x14, + 0x95,0x1a,0x53,0x01,0x97,0x2f,0x1a,0x32,0x75,0x88,0x37,0x4f,0x8c,0x67,0x6a,0x2f, + 0xcd,0x74,0xec,0x61,0x85,0x70,0x64,0xd7,0x29,0x4e,0x9c,0x1b,0xfd,0x5c,0xdf,0x28, + 0x9b,0x00,0x76,0x8b,0xe2,0x55,0x7b,0xdd,0xe0,0x67,0x23,0xb9,0xb6,0x3d,0x85,0xfe, + 0x0a,0x5e,0x3e,0x9b,0x78,0xb9,0xb3,0xa0,0x57,0x89,0x94,0x9e,0x74,0xa9,0xf8,0xe7, + 0x4c,0x74,0x55,0x7a,0x27,0x8f,0xda,0xb3,0x25,0x03,0x4c,0x97,0x46,0x76,0xc9,0xcf, + 0x66,0xf4,0xbe,0xbf,0xdf,0xd6,0x7c,0x56,0x7d,0x78,0x7a,0x52,0xbe,0x73,0x14,0xa5, + 0x59,0x15,0xc3,0x50,0x24,0x50,0x3f,0x92,0x7b,0x4f,0x79,0xa9,0xc3,0x2b,0x5f,0x99, + 0x10,0xe7,0x7b,0x4b,0xae,0x5c,0x7a,0x1b,0x47,0xbd,0x71,0xa4,0x2e,0xca,0xe7,0xdf, + 0xca,0xeb,0x22,0x14,0x58,0x53,0x76,0x08,0x71,0xf9,0x15,0x47,0x4d,0xf9,0xd4,0x9b, + 0x7a,0x3c,0x47,0x3c,0x14,0x84,0xde,0x30,0xe5,0x3c,0x94,0xaf,0xf9,0x04,0xf9,0x67, + 0xa9,0x4c,0x73,0x52,0xeb,0x97,0x20,0x9b,0x87,0x13,0x99,0x68,0x3d,0xce,0xa8,0x4a, + 0xa0,0x76,0x48,0x6f,0xca,0x0a,0xa3,0x54,0xf8,0x30,0x2d,0x0d,0xc0,0x06,0x87,0x6f, + 0x92,0x19,0xdf,0x2c,0xc6,0x45,0x52,0x69,0xf7,0x57,0x94,0xa3,0xab,0x36,0x95,0xd9, + 0xcb,0x4f,0xa7,0xdd,0x6d,0x58,0xb9,0xba,0xb4,0x01,0x62,0x05,0x58,0x18,0xa8,0xd1, + 0x87,0xf2,0x43,0x70,0xe5,0xce,0xd5,0xe8,0x9b,0xdf,0xd9,0x7f,0x8a,0x9e,0x21,0x7d, + 0xd3,0xc6,0x15,0x58,0x43,0x2e,0xfb,0xa2,0xd5,0xa4,0x44,0x49,0x64,0x5e,0xc5,0x9b, + 0x83,0x7c,0xa6,0x96,0xa4,0x23,0x5a,0x3f,0x26,0xc6,0x93,0x2e,0x9f,0x25,0x1a,0xac, + 0x7d,0xa6,0x84,0xc1,0xe8,0x97,0x4a,0x56,0xd1,0xdd,0xb7,0x3c,0x61,0xd2,0xe6,0xd5, + 0x4a,0x6f,0x26,0x67,0x21,0x5a,0x84,0x86,0x60,0xa6,0x47,0x94,0x1b,0xca,0x05,0x2e, + 0xdf,0xe5,0xf2,0x1c,0xfd,0xdb,0x35,0x9c,0xe6,0x50,0x10,0x33,0x58,0x97,0x74,0xf1, + 0x0d,0x2d,0xfb,0x86,0xf6,0x2b,0x3a,0xce,0x28,0xe2,0xa7,0xd4,0xf5,0xb4,0xe2,0x29, + 0x59,0x3a,0x33,0x04,0x35,0xe8,0x0a,0x6e,0x71,0x3f,0x4a,0xb9,0x28,0x5f,0x8b,0x85, + 0x11,0x53,0xd5,0x75,0xb2,0x72,0x49,0xb1,0x5a,0x53,0x7a,0xae,0x6a,0x0b,0x6f,0xa2, + 0xc7,0xbf,0xb3,0x62,0x1e,0xef,0x6f,0xfc,0x3f,0xa2,0x10,0x16,0xf7,0x92,0xe0,0x00, + 0x00, }; constexpr size_t AUDIO_IN_INDEX_HTML_GZ_LEN = sizeof(AUDIO_IN_INDEX_HTML_GZ); // diff --git a/firmware/patternflow/features/audio_in/core_audio_fft.h b/firmware/patternflow/features/audio_in/core_audio_fft.h index 55d3f516..6b060469 100644 --- a/firmware/patternflow/features/audio_in/core_audio_fft.h +++ b/firmware/patternflow/features/audio_in/core_audio_fft.h @@ -7,16 +7,22 @@ // and 5 of 7 either did not know it existed or had not tried it. What people // asked for is a different thing: the panel hearing the room on its own. // -// This is the analysis half, and it is written to run BEFORE any microphone -// exists, because the question that decides whether the feature is possible -// is not "can we read a mic" — it is "is there CPU left". The panel spends -// ~10 ms of every 16.6 ms frame pushing pixels. If a spectrum does not fit -// in what remains, no microphone helps. +// Owns: the transform and its buffers, the four raw band levels, the +// spectrum the page draws, and the verdict on what the input is (sourceLabel). +// Where the bands' edges are, and what a level becomes, is core_audio_in_map.h. // -// So `feed()` takes samples from wherever. A PDM mic on I2S would be the -// real source (GPIO43/44 are the only free header pins on this board — see -// the note in analyze()); until one is wired, synth() stands in and the cost -// measures the same, because an FFT does not know where its input came from. +// This is the analysis half, and it was written BEFORE any microphone +// existed, because the question that decided whether the feature was possible +// was not "can we read a mic" — it was "is there CPU left". The panel spends +// ~10 ms of every 16.6 ms frame pushing pixels. If a spectrum had not fitted +// in what remains, no microphone would have helped. +// +// So fill() takes a window from wherever. The real source is the PDM mic in +// core_audio_pdm.h; when that has nothing to give - the driver did not +// install, or the mic stopped answering - a synthetic three-tone source +// stands in, and the cost measures the same, because an FFT does not know +// where its input came from. What the synthetic source must not do is move a +// panel's knobs; the feature's fillInput sees to that. // // Deliberately naive: a plain radix-2 complex transform with the signal in // the real lane. A real-input FFT halves this and esp-dsp's assembly version @@ -180,12 +186,21 @@ inline void fill(uint32_t tick) { // edge that is halfway between the old value and the new one - 16 ms of a // slightly wrong band while somebody drags a handle. A mutex on the audio // path to avoid that would cost more than it saves. +// +// What an edge read here must never do is leave a band with no bins. A low +// edge in the last bin does (hzMin 7990: lo is N/2, and hi cannot pass it), +// whether it is stored that way or caught raw in the instant before the +// handler clamps it. The mean was then 0/0, and because each level is +// smoothed from the one before, that single NaN never left: `nan` in every +// poll and in /api/status until a reboot, and on the knob the band drives. +// A band over nothing hears nothing. inline void fold() { for (int b = 0; b < 4; b++) { const int lo = PFAudioInMap::binOf(PFAudioInMap::bands[b].hzMin); int hi = PFAudioInMap::binOf(PFAudioInMap::bands[b].hzMax); if (hi <= lo) hi = lo + 1; if (hi > N / 2) hi = N / 2; + if (hi <= lo) { bands[b] = 0.0f; continue; } float sum = 0.0f; for (int k = lo; k < hi; k++) sum += sqrtf(re[k] * re[k] + im[k] * im[k]); @@ -228,6 +243,11 @@ inline bool inputIsDeadRail() { // What the input actually is, for /api/status and the console page. Lives // here rather than beside the driver because the driver cannot see the // spectrum, and the spectrum is the only thing that tells these apart. +// +// The five strings this can return ("off", "pdm", the dead-rail one below, +// and PFAudioPdm::sourceName()'s "synth" and "synth (mic stalled)") are +// matched exactly by the page and listed in docs/rest-api.md. Add a sixth +// rather than rewording one. inline const char* sourceLabel() { if (!PFAudioInMap::micOn) return "off"; if (!PFAudioPdm::available()) return PFAudioPdm::sourceName(); diff --git a/firmware/patternflow/features/audio_in/core_audio_in_http.h b/firmware/patternflow/features/audio_in/core_audio_in_http.h index 83e954b5..e66f1002 100644 --- a/firmware/patternflow/features/audio_in/core_audio_in_http.h +++ b/firmware/patternflow/features/audio_in/core_audio_in_http.h @@ -1,11 +1,19 @@ // ═══════════════════════════════════════════════════════════ // PatternFlow - /audio-in: watching and shaping the microphone // -// Three routes: +// Owns: the page and its four routes. The settings they read and write live +// in core_audio_in_map.h; nothing is stored here but the phone's last frame. // -// GET /audio-in the page -// GET /api/audio-in live levels + the current shaping. Polled. -// POST /api/audio-in set one band, or the microphone switch +// GET /audio-in the page +// GET /api/audio-in the whole configuration, read once on load +// GET /api/audio-in?levels=1 live levels, mapped values, spectrum. Polled. +// POST /api/audio-in settings: the switch, gain, auto, the two +// speeds, and any band's fields +// POST /api/audio-in/reset the firmware's defaults - one band, or all +// +// The contract, field by field, is docs/rest-api.md ("Microphone"). The phone +// capture app reads the configuration and posts monitor frames through the +// same routes, so a field here is not ours alone to rename. // // ── Why polling and not the websocket ─────────────────────────────────── // @@ -16,11 +24,11 @@ // the browser path composed out - which is exactly the build someone with a // microphone would want. // -// So: a small GET, polled at ~10 Hz by the page. The response is deliberately -// tiny (four levels, four mapped values, a peak) because this device's web -// server is single-connection and every byte here is a byte the panel is not -// rendering. The shaping is sent once on load and only re-sent when the page -// changes it. +// So: a GET the page polls. The poll reply carries no configuration at all +// (levels, what they map to, and a 64-bucket spectrum to draw) because this +// device's web server is single-connection and every byte here is time the +// one connection is not free for anything else. The configuration is sent +// once on load and the page sends back only what it changed. // // License: MIT // ═══════════════════════════════════════════════════════════ @@ -74,14 +82,20 @@ inline bool pageWatching() { } // "l0,l1,l2,l3;lo0,hi0,..lo3,hi3;s0,..,s63" - 4+8+64 floats, three groups. -// A frame that does not parse to exactly that shape is dropped whole. +// A frame that does not parse to exactly that shape is dropped whole, and so +// is one with a `nan` or an `inf` in it: these are printed back into the poll +// as they came, and neither is JSON. inline void parseFrame(const String& s) { float vals[76]; int n = 0, start = 0; for (unsigned i = 0; i <= s.length() && n < 76; i++) { const char c = i < s.length() ? s[i] : ','; if (c == ',' || c == ';') { - if (i > (unsigned)start) vals[n++] = s.substring(start, i).toFloat(); + if (i > (unsigned)start) { + const float v = s.substring(start, i).toFloat(); + if (!isfinite(v)) return; + vals[n++] = v; + } start = i + 1; } } @@ -111,18 +125,26 @@ inline void handleIndex() { // dragged handle spring back to whatever the device last managed to save. // // Field names are the extension's — levels, outputs, spectrum — because the -// page's paint loop is lifted from popup.js and renaming them here would mean -// editing code whose whole value is that it is not edited. +// page IS the extension's editor (tools/patternflow-audio-extension/editor.*, +// assembled for the device by toolchain/build_audio_in_page.py) and it paints +// both sources through one frame shape. inline void handleGet() { const bool levelsOnly = server().hasArg("levels"); if (levelsOnly) { - // idle=1 marks the page's own low-power mode (its Monitor toggle off): - // it still wants a status trickle, but must not count as an audience - - // the phone reads that demand and throttles its frames accordingly. + // idle=1 is a poll that must not count as an audience: the phone reads + // that demand in every frame reply and throttles itself by it, so a + // client that only wants a trickle of status says so. Nothing in this + // tree sends it today (the page's Monitor toggle, which did, is gone); + // it stays because the demand signal is the phone's contract. if (!server().hasArg("idle")) pagePollMs = millis(); const bool ext = extFresh(); - String j = "{\"source\":\""; + // One allocation instead of one per `+=`: the reply is about 700 bytes + // and a String grows 16 at a time, so unreserved this was some forty + // reallocs of internal heap, ten times a second. + String j; + j.reserve(768); + j += "{\"source\":\""; j += ext ? "phone" : PFAudioFFT::sourceLabel(); j += "\",\"ext\":"; j += ext ? "true" : "false"; @@ -140,8 +162,11 @@ inline void handleGet() { if (i) j += ','; j += String(ext ? extLevels[i] : PFAudioInMap::smoothLevel[i], 4); } - // The level as the mapping consumes it in auto mode - the page paints - // its dot and meters from this so what you see is what the knob gets. + // The level as the mapping consumes it in auto mode - sent so a page can + // paint its cursor from what the knob gets rather than from its own + // re-derivation. This and `outputs` are always the MICROPHONE's: while + // `ext` frames are being served the phone is doing its own mapping, and + // these two do not describe it. j += "],\"levelsN\":["; for (int i = 0; i < 4; i++) { if (i) j += ','; @@ -197,7 +222,10 @@ inline void handleGet() { return; } - String j = "{\"source\":\""; + // About 1.3 KB with four empty metas; see the note on the poll reply. + String j; + j.reserve(1536); + j += "{\"source\":\""; j += PFAudioFFT::sourceLabel(); j += "\",\"micOn\":"; j += PFAudioInMap::micOn ? "true" : "false"; @@ -260,14 +288,17 @@ inline void handleGet() { j += PFAudioInMap::lutSet[i] ? "true" : "false"; j += '}'; } + // Two decimals: the low edge is one FFT bin, 31.25 Hz, and at one decimal + // a client taking its axis from here was handed a bound that is not the + // one clampRange() holds a band to. j += "],\"hzRange\":["; - j += String(PFAudioInMap::MIN_HZ, 1); + j += String(PFAudioInMap::MIN_HZ, 2); j += ','; - j += String(PFAudioInMap::MAX_HZ, 1); + j += String(PFAudioInMap::MAX_HZ, 2); - // Three decimals, not five: this is 40 numbers ten times a second and the - // page draws them as bars a few pixels wide. The precision that matters is - // in `level`, which is what the shaping actually consumes. + // Three decimals: these are drawn as bars a few pixels wide. (`spec`, + // `level`, `out`, `rawPeak`, `rawDc` and `windows` ride in this reply for + // scripts and for diagnosing a microphone; the page draws from the poll.) j += "],\"spec\":["; { float s[PFAudioFFT::SPEC_BUCKETS]; @@ -283,16 +314,41 @@ inline void handleGet() { server().send(200, "application/json", j); } +// A band's number, or the value it already has when the field is absent, +// empty, or not a finite number. toFloat() is atof(), which reads `nan` and +// `inf` as exactly that, and a NaN walks through every constrain() below; see +// PFAudioInMap::finiteOr for what one stored NaN cost. inline float argFloat(const char* name, float fallback) { if (!server().hasArg(name)) return fallback; const String v = server().arg(name); if (!v.length()) return fallback; - return v.toFloat(); + return PFAudioInMap::finiteOr(v.toFloat(), fallback); +} + +// Does this request say anything about band N in the suffixed form? Every +// field a band has is on the list. It used to be five of them - the ones a +// whole-config save always carries - which meant a request with only +// `inMax2`, the top edge of one box, was answered {"ok":true} and changed +// nothing. A page that sends only what moved needs that to work. +inline bool hasBandArgs(const String& suffix) { + static const char* const FIELDS[] = {"hzMin", "hzMax", "inMin", "inMax", + "gain", "outMin", "outMax", "knob", + "muted", "lut", "meta"}; + for (const char* f : FIELDS) + if (server().hasArg(String(f) + suffix)) return true; + return false; } -// Apply whatever band fields arrived under the given argument names. Shared -// by the single-band form (bare names + band=N) and the editor's whole-config -// form (suffixed names, hzMin0..hzMin3), so the two cannot drift. +// Apply whatever band fields arrived under the given argument names; a field +// that did not arrive keeps its value. Shared by the single-band form (bare +// names + band=N) and the suffixed form (hzMin0..hzMin3), so the two cannot +// drift. +// +// Two pairs are settled TOGETHER after the fields are in, whichever half +// arrived: hzMin/hzMax (ordered, inside the analysable range, a bin apart) +// and inMin/inMax (inMax at least 0.01 above inMin). So one half of a pair +// sent alone can move the other half; a client that wants to know what was +// stored sends both. inline void applyBandArgs(int b, const String& suffix) { PFAudioInMap::Band& x = PFAudioInMap::bands[b]; const String sHzMin = "hzMin" + suffix, sHzMax = "hzMax" + suffix; @@ -348,9 +404,11 @@ inline void applyBandArgs(int b, const String& suffix) { } } -// One band per POST, addressed by index, or `mic` on its own. Partial -// updates are allowed: the page sends only the handle that moved, so dragging -// one edge cannot clobber the other three by round-tripping a stale copy. +// Every field is optional and a request changes only what it names: `mic` +// on its own, one slider, one edge of one box, or everything at once. That +// is the point - a page that sends back its whole copy on every edit lets +// two open tabs revert each other, and a drag clobber three bands it never +// touched. inline void handleSet() { // Monitor frames are state, not settings: no NVS save, no other fields. // The reply carries the demand signal - see pageWatching(). @@ -362,6 +420,18 @@ inline void handleSet() { : "{\"ok\":true,\"watch\":false}"); return; } + // Refused before anything is applied: a 400 that had already flipped the + // microphone switch in memory would not be a refusal. + int single = -1; + if (server().hasArg("band")) { + single = server().arg("band").toInt(); + if (single < 0 || single > 3) { + server().sendHeader("Cache-Control", "no-store"); + server().send(400, "application/json", + "{\"ok\":false,\"error\":\"band must be 0-3\"}"); + return; + } + } if (server().hasArg("mic")) { const String v = server().arg("mic"); PFAudioInMap::micOn = (v == "1" || v == "true"); @@ -370,41 +440,39 @@ inline void handleSet() { const String v = server().arg("auto"); PFAudioInMap::autoRange = (v == "1" || v == "true"); } + // These three keep their value on `nan` or `inf`, like a band's fields. An + // EMPTY value is still read as 0 and lands on the low clamp, as documented; + // only what is not a finite number is refused. A NaN here was worse than in + // a band: as the gain it made every sample NaN, as a glide speed it made the + // damped levels NaN, and neither came back with a reset. if (server().hasArg("micGain")) { - PFAudioInMap::micGain = constrain(server().arg("micGain").toFloat(), - PFAudioInMap::MIC_GAIN_MIN, - PFAudioInMap::MIC_GAIN_MAX); + const float v = PFAudioInMap::finiteOr(server().arg("micGain").toFloat(), + PFAudioInMap::micGain); + PFAudioInMap::micGain = + constrain(v, PFAudioInMap::MIC_GAIN_MIN, PFAudioInMap::MIC_GAIN_MAX); } if (server().hasArg("smoothing")) { - PFAudioInMap::smoothing = - constrain(server().arg("smoothing").toFloat(), 0.05f, 0.9f); + const float v = PFAudioInMap::finiteOr(server().arg("smoothing").toFloat(), + PFAudioInMap::smoothing); + PFAudioInMap::smoothing = constrain(v, 0.05f, 0.9f); } if (server().hasArg("attack")) { - PFAudioInMap::attack = - constrain(server().arg("attack").toFloat(), 0.05f, 0.9f); + const float v = PFAudioInMap::finiteOr(server().arg("attack").toFloat(), + PFAudioInMap::attack); + PFAudioInMap::attack = constrain(v, 0.05f, 0.9f); } // Single-band form: band=N plus bare field names. The strip-era contract, // still honoured. - if (server().hasArg("band")) { - const int b = server().arg("band").toInt(); - if (b < 0 || b > 3) { - server().send(400, "application/json", "{\"error\":\"band must be 0-3\"}"); - return; - } - applyBandArgs(b, ""); - } + if (single >= 0) applyBandArgs(single, ""); - // Whole-config form: suffixed field names (hzMin0..hzMin3, lut2, meta1...). - // The editor saves everything it owns in one request - four sequential - // POSTs on a single-connection server was a drag stuttering the panel. + // Suffixed form: field names carrying their band (hzMin0..hzMin3, lut2, + // meta1...). Any number of bands in one request, any subset of each one's + // fields - four sequential POSTs on a single-connection server was a drag + // stuttering the panel. for (int b = 0; b < 4; b++) { const String suffix(b); - if (server().hasArg("hzMin" + suffix) || server().hasArg("lut" + suffix) || - server().hasArg("knob" + suffix) || server().hasArg("muted" + suffix) || - server().hasArg("outMin" + suffix)) { - applyBandArgs(b, suffix); - } + if (hasBandArgs(suffix)) applyBandArgs(b, suffix); } PFAudioInMap::save(); @@ -412,11 +480,39 @@ inline void handleSet() { server().send(200, "application/json", "{\"ok\":true}"); } +// The defaults live in the firmware (core_audio_in_map.h resetBand) and the +// page asks for them here rather than keeping a table of its own - the one it +// had was the extension's, and "Reset band" put a 5-16 kHz band on an axis +// that ends at 8. +// +// band=N that band only: its range, window, gain, output range, knob, +// mute and curve. Nothing else moves. The reply names the band, so +// a client can tell this from an older firmware that ignored the +// argument and reset everything. +// (none) all four bands and curves, damping, attack and the input gain. +// Not the microphone switch and not auto range. inline void handleReset() { + server().sendHeader("Cache-Control", "no-store"); + if (server().hasArg("band")) { + // Exactly one digit. toInt() reads "" and "x" as 0, and a reset is not + // the request to guess on. + const String v = server().arg("band"); + if (v.length() != 1 || v[0] < '0' || v[0] > '3') { + server().send(400, "application/json", + "{\"ok\":false,\"error\":\"band must be 0-3\"}"); + return; + } + PFAudioInMap::resetBand(v[0] - '0'); + PFAudioInMap::save(); + String j = "{\"ok\":true,\"band\":"; + j += v[0]; + j += '}'; + server().send(200, "application/json", j); + return; + } PFAudioInMap::resetBands(); - PFAudioInMap::micGain = 8.0f; + PFAudioInMap::micGain = PFAudioInMap::MIC_GAIN_DEFAULT; PFAudioInMap::save(); - server().sendHeader("Cache-Control", "no-store"); server().send(200, "application/json", "{\"ok\":true}"); } diff --git a/firmware/patternflow/features/audio_in/core_audio_in_map.h b/firmware/patternflow/features/audio_in/core_audio_in_map.h index c8abf314..c2529b1e 100644 --- a/firmware/patternflow/features/audio_in/core_audio_in_map.h +++ b/firmware/patternflow/features/audio_in/core_audio_in_map.h @@ -5,20 +5,32 @@ // a knob: the highs carry far less energy than the bass, so band 4 sits near // zero while band 1 saturates, and a room's noise floor is never at zero. // -// Four numbers per band fix that, and they are deliberately the SAME four the -// Chrome extension uses, computed the same way: +// Owns: the four bands' settings, where they are kept (NVS), and the +// arithmetic from a band's level to its knob. No I2S and no HTTP in here. +// +// Five numbers per band fix that, and they are deliberately the SAME five the +// Chrome extension's mapper uses, applied in the same order - window, then +// curve, then output range: // // inMin ignore below this the room's noise floor // inMax full by this what counts as loud, for this band -// gain curve between them >1 lifts a quiet band +// gain curve between them >1 lifts a quiet band (the curve, until +// the editor gives the band a table - see +// "Response curves" below) // outMin where the knob rests what the pattern does in silence // outMax where the knob peaks // -// mapBandOutput() in tools/patternflow-audio-extension/popup.js is the -// reference implementation. If one of them changes, the other has to: a +// The extension's half is mapBandOutput() in tools/patternflow-audio-extension. +// If one of them changes its order or its edge cases, the other has to: a // person who has learned the response graph on one path must not find the -// same handles doing something else on the other. That is the entire reason -// this file repeats the extension's arithmetic instead of inventing its own. +// same handles doing something else on the other. +// +// What the two do NOT share is the unit a level is measured in. The extension +// reads decibels off the browser's analyser and normalizes them; this side +// measures LINEAR amplitude, and every constant below was measured on that +// scale. inMin/inMax are therefore linear here, and in manual mode the level +// is placed between them linearly. The /audio-in page draws a dB axis and +// converts at its own boundary; the numbers the API carries are these. // // ── On the S3's quiet PDM input ───────────────────────────────────────── // @@ -83,9 +95,10 @@ struct Band { // same digital multiply. So: a runtime gain, WLED-style, user-tunable on // /audio-in. Applied only to the microphone - the synthetic source is // already full-scale and would clip. -inline float micGain = 8.0f; +constexpr float MIC_GAIN_DEFAULT = 8.0f; constexpr float MIC_GAIN_MIN = 1.0f; constexpr float MIC_GAIN_MAX = 16.0f; +inline float micGain = MIC_GAIN_DEFAULT; inline Band bands[4]; // filled by resetBands() or load(); see below @@ -102,8 +115,8 @@ inline Band bands[4]; // filled by resetBands() or load(); see below // tuned set (bands8 -> bands9 both did exactly that). The tables live under // their own keys, so an upgrade keeps the windows people measured. // -// A band with no table (lutSet 0) keeps the legacy gain exponent - the -// upgrade is invisible until the editor touches a band. +// A band with no table (lutSet 0) runs its gain exponent - which is what the +// measured defaults are, not a leftover: a factory panel has four of them. constexpr int LUT_POINTS = 33; inline uint8_t luts[4][LUT_POINTS]; inline uint8_t lutSet[4] = {0, 0, 0, 0}; @@ -128,8 +141,10 @@ inline float lutValue(int b, float u) { // one number means one feel on both paths; this side converts to its own // frame rate at use. Higher alpha = snappier; the editor shows it reversed // as "damping", tight to glassy. -inline float smoothing = 0.35f; -inline float attack = 0.65f; +constexpr float SMOOTHING_DEFAULT = 0.35f; +constexpr float ATTACK_DEFAULT = 0.65f; +inline float smoothing = SMOOTHING_DEFAULT; +inline float attack = ATTACK_DEFAULT; inline float smoothLevel[4] = {0, 0, 0, 0}; inline void smoothLevels(const float* level) { @@ -168,22 +183,32 @@ inline void smoothLevels(const float* level) { // inMin also clears the measured quiet-room floor per band (0.135/0.088/ // 0.024/0.010) so silence does not wobble the knobs. // -// One function rather than two literal tables: the HTTP reset handler and -// the fresh-boot path both call this, and the two copies they used to keep -// in sync are exactly the kind of pair that drifts. -inline void resetBands() { - const Band d[4] = { +// One table rather than a copy per caller: the fresh-boot path, the HTTP +// reset and the one-band reset all read this, and the copies they would +// otherwise keep in sync are exactly the kind of pair that drifts. The page +// asks for these (POST /api/audio-in/reset) instead of carrying its own. +inline void resetBand(int i) { + static const Band d[4] = { { 62.0f, 375.0f, 0.200f, 0.900f, 1.0f, 0.30f, 0.85f, 0, false}, { 375.0f, 1500.0f, 0.120f, 0.650f, 1.2f, 0.30f, 0.85f, 1, false}, { 1500.0f, 5000.0f, 0.045f, 0.160f, 1.6f, 0.30f, 0.85f, 2, false}, { 5000.0f, 8000.0f, 0.012f, 0.030f, 1.8f, 0.30f, 0.85f, 3, false}, }; - for (int i = 0; i < 4; i++) bands[i] = d[i]; - for (int i = 0; i < 4; i++) { - lutSet[i] = 0; - metas[i][0] = 0; - } - smoothing = 0.35f; + if (i < 0 || i > 3) return; + bands[i] = d[i]; + // No table: the band is back on its gain exponent, the measured default. + lutSet[i] = 0; + metas[i][0] = 0; +} + +// Every band, and the two speeds that belong to the mapping as a whole. Not +// the microphone switch and not auto range: those are how the panel is being +// used, not how it is tuned, and a reset that flipped either would be a +// surprise. +inline void resetBands() { + for (int i = 0; i < 4; i++) resetBand(i); + smoothing = SMOOTHING_DEFAULT; + attack = ATTACK_DEFAULT; } // The analysis is 512 points at 16 kHz, so a bin is 31.25 Hz and the last @@ -202,8 +227,27 @@ inline int binOf(float hz) { return b; } -// Ordered, inside the analysable range, and at least one bin wide - a band -// whose edges met would divide by zero in fold(). +// A setting that is a number, or `fallback`. NaN and the infinities are not +// settings. constrain() passes a NaN straight through (both of its tests are +// false), String() prints it as `nan`, and `nan` is not JSON: one stored NaN +// made GET /api/audio-in unreadable for good, and the page that could not +// read it had no way to offer the reset that would have cleared it. So nothing +// that is not finite is taken - from a request (core_audio_in_http.h) or from +// NVS (load, below). +inline float finiteOr(float v, float fallback) { return isfinite(v) ? v : fallback; } + +inline bool allFinite(const Band& b) { + return isfinite(b.hzMin) && isfinite(b.hzMax) && isfinite(b.inMin) && + isfinite(b.inMax) && isfinite(b.gain) && isfinite(b.outMin) && + isfinite(b.outMax); +} + +// Ordered, inside the analysable range, and a bin apart where there is room +// for one. Against the top there is not: a low edge within a bin of MAX_HZ +// keeps a band narrower than that (hzMin 7990 is stored as 7990..8000), and +// one that rounds to the last bin covers no bin at all. fold() reads such a +// band as silent rather than dividing by its width; this does not move the +// edge, because what a client stored is what it reads back. inline void clampRange(Band& b) { b.hzMin = constrain(b.hzMin, MIN_HZ, MAX_HZ); b.hzMax = constrain(b.hzMax, MIN_HZ, MAX_HZ); @@ -219,7 +263,7 @@ inline void clampRange(Band& b) { // analysis. Off means the feature costs a panel nothing but the code size. // // Default off, and this is the switch people will actually use. The mic is -// four wires to a breakout rather than a part on the board, so a panel that +// five leads to a breakout rather than a part on the board, so a panel that // installs the Audio edition for OSC and the Chrome extension should not be // running an analysis over a floating pin. Turning it on is one tick on // /audio-in and it persists. @@ -327,8 +371,9 @@ constexpr float NORM_LO_K = 1.5f; // normalization floor = ref * this constexpr float NORM_MIN_SPAN = 0.02f; constexpr float ENV_ATTACK = 0.3f; constexpr float ENV_RELEASE = 0.002f; -// The relative response window the normalized level runs through (the -// page shows the same 0.10..0.95 as the fixed in-handles in auto mode). +// The relative response window the normalized level runs through. The poll +// reply carries that normalized level (`levelsN`), so a page can place it in +// this same 0.10..0.95 and show where the knob really is. constexpr float AUTO_LO = 0.10f; constexpr float AUTO_HI = 0.95f; @@ -390,7 +435,8 @@ inline float normalized(int b, float level) { // The extension's mapBandOutput, in C++. Same clamps, same order, same edge // cases - including inMax being forced at least 0.01 above inMin, which is -// what stops a dragged handle pair from dividing by zero. +// what stops a dragged handle pair from dividing by zero. The level is placed +// in the window LINEARLY (see the header: this side's unit is amplitude). inline float mappedWindow(int b, float level, float inMinRaw, float inMaxRaw) { const Band& x = bands[b]; const float inMin = clamp01(inMinRaw); @@ -414,19 +460,6 @@ inline float mapped(int b, float level) { return mappedWindow(b, level, x.inMin, x.inMax); } -// How much of its knob a band actually uses between silence and its own -// recent peak. The extension calls this "travel" and shows it as a bar, -// because "this band is not doing anything" is the question people actually -// have, and it is not answerable from four raw numbers. -inline float travel(int b, float peakLevel) { - const Band& x = bands[b]; - const float span = fabsf(x.outMax - x.outMin); - if (span < 0.001f) return 0.0f; - return clamp01(fabsf(mapped(b, peakLevel) - mapped(b, 0.0f)) / span); -} -// (mapped() already routes through the auto window when autoRange is on, so -// travel and the page's readouts follow whichever mode is live.) - // ── Persistence ───────────────────────────────────────────────────────── // // One blob rather than 21 keys: NVS entries are the scarce thing here, and @@ -479,14 +512,19 @@ inline void load() { p.getBytes(NVS_KEY, bands, sizeof(bands)); // Saved settings are trusted to be the right shape, not to be sane: a // blob written by a build with a different sample rate would put edges - // past Nyquist, and fold() would read off the end of the spectrum. + // past Nyquist, and fold() would read off the end of the spectrum. Nor + // to be numbers: earlier builds stored a NaN when a request sent one, + // and no clamp removes it. Such a band goes back to its default (its + // curve table, read below, is kept). for (int i = 0; i < 4; i++) { + if (!allFinite(bands[i])) resetBand(i); clampRange(bands[i]); bands[i].knob = constrain(bands[i].knob, 0, 3); } } micOn = p.getBool(NVS_MIC, false); - micGain = constrain(p.getFloat(NVS_GAIN, micGain), MIC_GAIN_MIN, MIC_GAIN_MAX); + micGain = constrain(finiteOr(p.getFloat(NVS_GAIN, micGain), MIC_GAIN_DEFAULT), + MIC_GAIN_MIN, MIC_GAIN_MAX); autoRange = p.getBool(NVS_AUTO, autoRange); if (p.getBytesLength(NVS_LUTS) == sizeof(luts) && p.getBytesLength(NVS_LSET) == sizeof(lutSet)) { @@ -497,8 +535,10 @@ inline void load() { p.getBytes(NVS_META, metas, sizeof(metas)); for (int i = 0; i < 4; i++) metas[i][sizeof(metas[i]) - 1] = 0; } - smoothing = constrain(p.getFloat(NVS_SMOOTH, smoothing), 0.05f, 0.9f); - attack = constrain(p.getFloat(NVS_ATTACK, attack), 0.05f, 0.9f); + smoothing = constrain(finiteOr(p.getFloat(NVS_SMOOTH, smoothing), SMOOTHING_DEFAULT), + 0.05f, 0.9f); + attack = constrain(finiteOr(p.getFloat(NVS_ATTACK, attack), ATTACK_DEFAULT), + 0.05f, 0.9f); p.end(); } diff --git a/firmware/patternflow/features/audio_in/core_audio_pdm.h b/firmware/patternflow/features/audio_in/core_audio_pdm.h index 52b07b86..07805aa7 100644 --- a/firmware/patternflow/features/audio_in/core_audio_pdm.h +++ b/firmware/patternflow/features/audio_in/core_audio_pdm.h @@ -224,6 +224,8 @@ inline void begin() { for (int i = 0; i < WINDOW; i++) ring[i] = 0.0f; live = true; + // "PDM up: CLK" is the literal firmware/bundles/build.sh looks for in an + // image to prove this feature is in it. Reword around it, not through it. Serial.printf("[AUDIO-IN] PDM up: CLK=%d DAT=%d %luHz mono left dsr=%d\n", PF_AUDIO_IN_PDM_CLK, PF_AUDIO_IN_PDM_DAT, (unsigned long)RATE, PF_AUDIO_IN_PDM_DSR16 ? 16 : 8); diff --git a/firmware/patternflow/features/audio_in/feature_audio_in.h b/firmware/patternflow/features/audio_in/feature_audio_in.h index 59af7d11..10f47007 100644 --- a/firmware/patternflow/features/audio_in/feature_audio_in.h +++ b/firmware/patternflow/features/audio_in/feature_audio_in.h @@ -1,31 +1,37 @@ // ═══════════════════════════════════════════════════════════ // PatternFlow - on-board audio input, as a feature // -// The fifth port onto the feature seam, and the first one that was not already -// living in the core — which makes it the more interesting test. The other -// four proved the hooks could carry features that already existed. This one -// asks whether they can carry a feature nobody has written yet. +// Owns: the descriptor, the analysis task, and the one place sound becomes +// knob lanes (fillInput). The parts it wires together each own one thing: // -// Answer so far: four hooks, no core edits, no sketch edits. +// core_audio_pdm.h the microphone - a PDM MEMS part on GPIO43/44 +// core_audio_fft.h a window of samples -> spectrum -> four band levels +// core_audio_in_map.h a band's level -> its knob; the settings and NVS +// core_audio_in_http.h /audio-in and /api/audio-in // -// setup - build the twiddle tables, and start the analysis task -// loop - run a window inline, when measuring the inline cost +// It was the first feature written on the seam rather than moved onto it, and +// it needed no core edit. The hooks it uses: +// +// setup - load the settings, build the FFT tables, start the task +// onNetwork - register the page and its API // fillInput - four bands drive the four knob lanes, exactly the way // the weather feature drives them from a temperature. A // pattern animates from sound without knowing what sound is. -// appendStatus - report what it costs, because that is the open question +// appendStatus - the `audioIn` block: what the analysis costs, and what the +// microphone is returning // -// PF_AUDIO_IN_CORE picks who pays: -// 1 the render loop (Core 1), inline. The naive placement. +// PF_AUDIO_IN_CORE picks who pays for the analysis: // 0 a task pinned to Core 0, where Wi-Fi lives and the panel does not. -// This is the arrangement that would ship, if it ships. +// What ships. +// 1 the render loop (Core 1), inline through the loop hook. Kept for +// measuring that placement against the other; no edition builds it. // -// There is no microphone yet, and that is deliberate. The panel spends -// ~10 ms of every 16.6 ms frame pushing pixels; if a spectrum does not fit -// in what is left, no microphone helps. So the cost gets measured first and -// the hardware question — a PDM mic on GPIO43/44, the only free header pins -// on this board, since N16R8's octal PSRAM claims 35-37 and everything else -// is HUB75 or an encoder — gets decided after, with a number in hand. +// The analysis was written and costed before there was a microphone - the +// panel spends ~10 ms of every 16.6 ms frame pushing pixels, and if a +// spectrum had not fitted in what is left no microphone would have helped. +// It fitted. The synthetic source that stood in for the microphone then is +// still in core_audio_fft.h, and is what the analysis runs on when the +// microphone is not delivering; see fillInput for why it stops there. // // License: MIT // ═══════════════════════════════════════════════════════════ @@ -41,8 +47,9 @@ #define PF_AUDIO_IN_CORE 0 #endif -// Off by default: while this is a measurement rather than a feature, it -// should cost a normal build exactly nothing. +// Off unless the composition says so: carrying this feature to measure it, or +// to watch the page, must not move anybody's knobs. The Audio edition turns +// it on in its overrides.h. #ifndef PF_AUDIO_IN_DRIVES_KNOBS #define PF_AUDIO_IN_DRIVES_KNOBS 0 #endif @@ -131,6 +138,16 @@ inline void fillInput(InputFrame& input) { // for. A hand on the encoder outranks both - the core drops any lane whose // knob moved this frame. if (!PFAudioInMap::micOn) return; + // Only a microphone that is delivering samples. When I2S would not install, + // or the mic stopped answering, the analysis carries on over the synthetic + // three-tone source (core_audio_fft.h fill()) - and without this line those + // tones reached the lanes as if the room were playing them: four knobs + // moving to nothing, while `source` said "synth (mic stalled)". It also + // covers the quarter second between the switch going on and the task + // installing the driver, when the levels are whatever the last session + // left. The synthetic source is for measuring the analysis, never for + // moving a panel. + if (!PFAudioPdm::available()) return; // Belt and braces over the default-off switch: somebody who ticks the box // on a panel with no microphone would otherwise pin all four knobs at // their resting position and find that turning one does not stick. diff --git a/firmware/patternflow/features/clock/clock_index.h b/firmware/patternflow/features/clock/clock_index.h index ff8bc255..e04b9dfb 100644 --- a/firmware/patternflow/features/clock/clock_index.h +++ b/firmware/patternflow/features/clock/clock_index.h @@ -11,7 +11,7 @@ static const char CLOCK_INDEX_HTML[] PROGMEM = R"HTML( - + Patternflow - Clock - - -
        -

        Console

        device
        - - +
        +
        -
        -
        -
        - Now playing +
        +
        +
        +

        Now playing

        -
        - - -
        + +
        —
        -
        - +
        -
        - Panel -
        - - +
        +
        + Panel +
        + + +
        -
        -
        -
        - Brightness - - — +
        +
        —
        +
        -
        -
        - Live Knobs - -
        -
        - K1 - -
        -
        - K2 - -
        -
        - K3 - -
        -
        - K4 - -
        +
        +
        Knobs
        +
        +
        +
        +
        -
        -
        Patterns
        —
        -
        Storage
        —
        -
        -
        Wi-Fi
        —
        -
        Address
        —
        +
        +
        Patterns—
        +
        Storage—
        +
        Wi-Fi—
        +
        Address—
        - -
        Memory
        —
        -
        Uptime
        —
        -
        Firmware
        —
        - - - Edition -
        —
        -
        -
        + from the one above, so its own line. --> + +
        Memory—
        +
        Uptime—
        - - - - update - how to update →
        - @@ -361,85 +200,70 @@ const char HOME_INDEX_HTML[] PROGMEM = R"HTML( function $(i){return document.getElementById(i)} $('host').textContent=location.hostname; +// A range's filled part (--p) follows its value. +function fill(el){el.style.setProperty('--p',(el.value-el.min)/(el.max-el.min)*100+'%')} +document.addEventListener('input',function(e){if(e.target.type==='range')fill(e.target)}); + // ── What this build actually has ─────────────────────────────── -// Feature rows are not written into this page. Whatever features the firmware -// carries report their pages in /api/status featureNav — path, label, one -// line — and the Features group is built from that. A build with none gets -// no group, and a page the core has never heard of still gets a row. This -// used to work the other way round: rows for every feature in the markup, -// deleted when the capability was absent, which meant a core page naming -// features and a new feature's page getting no row at all. -// -// Rebuilt from scratch whenever featureNav changes (not on every status: the -// card polls, and a rebuild under the pointer drops hover and focus), then -// everything renumbers so the list still reads 01, 02, 03. +// Feature rows, and the edition card's chips, come from /api/status +// featureNav (path, label, one line): a build with none gets none, and a +// page the core never heard of still gets a row. Rebuilt only when it +// changes: a rebuild under the pointer drops hover and focus. var navKey; function gate(s){ - var i,nav=(s&&s.featureNav)||[],k=JSON.stringify(nav); + var i,nav=(s&&s.featureNav)||[],k=JSON.stringify(nav),f=$('featRows'),c=$('s-feat'); if(k===navKey)return; navKey=k; - var f=$('featRows'),fk=$('featKick'); - if(f){ - f.innerHTML=''; - for(i=0;i
        '; - a.querySelector('.pf-row-t').textContent=e[1]; - a.querySelector('.pf-row-d').textContent=e[2]||''; - f.appendChild(a); - } - f.hidden=fk.hidden=!f.querySelector('.pf-row'); + f.innerHTML=c.innerHTML=''; + for(i=0;i'; + a.firstChild.textContent=e[1]; + a.lastChild.textContent=e[2]||''; + f.appendChild(a); + a=document.createElement('a'); + a.href=e[0];a.className='btn';a.textContent=e[1]; + c.appendChild(a); } - var ghosts=document.querySelectorAll('.rows .pf-ghost'); - for(i=0;i 100) v = 100; - var sl = $('sl-' + i); - if (sl) { - sl.value = v; - lastReported[i] = v; - } - } -} - -function pumpParams() { - if (inFlight) return; // Drop redundant updates while in-flight; keep newest in queue - var keys = Object.keys(queuedParams); - if (!keys.length) return; - - var bodyParts = []; - for (var i = 0; i < keys.length; i++) { - var k = keys[i]; - bodyParts.push(k + '=' + encodeURIComponent(queuedParams[k])); +// ── Knob sliders (K1..K4 · 0..100) ──────────────────────────── +var activeSlider=-1,inFlight=false,queuedParams={}; +function updateSliders(params){ + if(!params||params.length<4)return; + for(var i=0;i<4;i++){ + if(activeSlider===i)continue; + var v=Math.round(params[i]/10); + if(v<0)v=0;if(v>100)v=100; + var sl=$('sl-'+i);sl.value=v;fill(sl); } - queuedParams = {}; - inFlight = true; - - fetch('/api/params', { - method: 'POST', - cache: 'no-store', - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - body: bodyParts.join('&') - }).then(function(r) { - if (!r.ok) throw Error('refused'); - $('ctrlmsg').textContent = ''; - }).catch(function(x) { - // Next to the sliders, not swallowed: a drag that never arrived looks - // exactly like a pattern ignoring its knobs. - PF.say('not sent — ' + (x && x.message === 'refused' ? 'refused' : 'no answer'), 'err', $('ctrlmsg')); - }).then(function() { - inFlight = false; - if (Object.keys(queuedParams).length) { - pumpParams(); // Flush pending latest updates - } - }); -} - -function onSliderInput(idx, val) { - var v = parseInt(val, 10); - if (isNaN(v)) return; - lastReported[idx] = v; - - // pX on the 0..1000 bus. The device derives the clicks a legacy pattern - // reads from the change in the held value, so no dX alongside — sending - // both counted every move twice. - queuedParams['p' + (idx + 1)] = v * 10; - - pumpParams(); } - -function initSliders() { - for (var i = 0; i < 4; i++) { - (function(idx) { - var sl = $('sl-' + idx); - if (!sl) return; - held(sl, function() { activeSlider = idx; }, function() { - if (activeSlider === idx) activeSlider = -1; - pumpParams(); - }); - sl.addEventListener('input', function() { - var v = sl.value; - onSliderInput(idx, v); - }); - })(i); - } +function pumpParams(){ + if(inFlight)return; // one in flight; the newest values wait in the queue + var keys=Object.keys(queuedParams),body=[],i; + if(!keys.length)return; + for(i=0;i 0 ? 'Next…' : 'Prev…'; - PF.get('/api/patterns/select?step=' + dir).then(function(d) { - $('now').textContent = d.name || prev; - $('nowsub').textContent = 'switched'; - $('stepmsg').textContent = ''; - }, function(x) { - $('now').textContent = prev; - if (!x || !x.nav) - PF.say(x && x.status === 404 ? 'nothing to switch to' : 'not switched', 'err', $('stepmsg')); +// ── Pattern stepping (← / →) ───────────────────────────────── +// The reply names the new pattern; the next status confirms it. One step +// in flight (?step is ±1); presses meanwhile add up in pend. +var stepping=0,pend=0; +function stepPattern(dir){ + if(stepping){pend+=dir;return} + var prev=$('now').textContent; + $('now').textContent=dir>0?'Next…':'Prev…'; + stepping=1; + PF.get('/api/patterns/select?step='+dir).then(function(d){ + stepping=0; + $('now').textContent=d.name||prev; + $('nowsub').textContent='switched'; + $('stepmsg').textContent=''; + if(pend){dir=pend>0?1:-1;pend-=dir;stepPattern(dir)} + },function(x){ + stepping=pend=0; + $('now').textContent=prev; + if(!x||!x.nav) + PF.say(x&&x.status===404?'nothing to switch to':'not switched','err',$('stepmsg')); }); } -$('pat-prev').addEventListener('click', function() { stepPattern(-1); }); -$('pat-next').addEventListener('click', function() { stepPattern(1); }); +$('pat-prev').addEventListener('click',function(){stepPattern(-1)}); +$('pat-next').addEventListener('click',function(){stepPattern(1)}); +// The arrow keys too, unless a field or slider has them or a modifier is +// held (Alt+Left is Back). +document.addEventListener('keydown',function(e){ + var d=e.key=='ArrowLeft'?-1:e.key=='ArrowRight'?1:0; + if(!d||e.repeat||e.altKey||e.ctrlKey||e.metaKey||e.shiftKey||/^(INPUT|SELECT|TEXTAREA)$/.test(e.target.tagName))return; + e.preventDefault();stepPattern(d); +}); +[].forEach.call(document.querySelectorAll('input[type=range]'),fill); PF.watchStatus(3000); PF.status(paint); )HTML"; // GENERATED by firmware/toolchain/console_pages.py build — do not edit. -// gzip -9 of HOME_INDEX_HTML (34460 bytes → 11493 bytes), served with Content-Encoding: gzip. +// gzip -9 of HOME_INDEX_HTML (26389 bytes → 9476 bytes), served with Content-Encoding: gzip. const uint8_t HOME_INDEX_HTML_GZ[] PROGMEM = { - 0x1f,0x8b,0x08,0x00,0x00,0x00,0x00,0x00,0x02,0xff,0xcd,0x7d,0xdb,0x76,0xdb,0x48, - 0x92,0xe0,0xbb,0xbe,0x22,0xcd,0x9a,0x12,0x88,0x12,0x08,0x92,0xba,0xb8,0x6c,0x52, - 0x94,0xc7,0x2e,0xdb,0x3d,0x1e,0x97,0x5d,0x6e,0xcb,0x35,0x35,0x53,0x3e,0xde,0x1e, - 0x90,0x48,0x92,0x68,0x81,0x00,0x1a,0x09,0x8a,0x62,0x4b,0x3c,0xa7,0x9f,0xe6,0x79, - 0xcf,0x9e,0xf9,0x86,0xfd,0x85,0x7d,0xdf,0x4f,0xa9,0x2f,0xd9,0xb8,0x64,0x02,0x09, - 0x5e,0x24,0xd9,0xee,0xde,0xee,0xea,0xb6,0x8d,0x4b,0x22,0x33,0x32,0x32,0xee,0x11, - 0x99,0x3c,0x7d,0x10,0xa6,0xa3,0x62,0x99,0x49,0x31,0x2d,0x66,0xf1,0xd9,0xde,0x29, - 0xfe,0x23,0xe2,0x20,0x99,0x0c,0x1a,0x32,0x69,0xe0,0x03,0x19,0x84,0xf0,0xcf,0x4c, - 0x16,0x81,0x18,0x4d,0x83,0x5c,0xc9,0x62,0xd0,0x98,0x17,0xe3,0xd6,0x23,0x7c,0xab, - 0x46,0x79,0x94,0x15,0x42,0xe5,0xa3,0x41,0xa3,0x9d,0x8d,0x5b,0xa3,0x34,0x51,0x69, - 0x2c,0xfd,0x3f,0xaa,0x27,0xd3,0x41,0xd8,0x39,0x1c,0x1e,0x1d,0x1d,0x1e,0x36,0xce, - 0x4e,0xdb,0xdc,0xf0,0x4c,0x7f,0x70,0x16,0x8d,0x9b,0x0f,0x16,0x51,0x12,0xa6,0x0b, - 0xff,0xdd,0x4b,0xb7,0x39,0x9e,0x27,0xa3,0x22,0x4a,0x93,0xa6,0x7b,0x7d,0x19,0xe4, - 0xe2,0x97,0x01,0xbf,0xf3,0x7e,0x1e,0x38,0xed,0x20,0x8b,0xda,0xaa,0x08,0x8a,0xb9, - 0x72,0xbc,0x1f,0x07,0x1f,0x3f,0x79,0x6f,0x06,0x87,0xf2,0xb1,0x77,0xee,0xfd,0xea, - 0xbd,0x1b,0xfc,0x02,0x9f,0x0f,0xae,0xf1,0xb5,0xec,0x39,0x71,0x74,0x29,0x1d,0x2f, - 0x8c,0xf2,0x62,0xd9,0x7b,0xd0,0xf5,0x2e,0x7b,0xc9,0x3c,0x8e,0xbd,0x89,0x2c,0x7a, - 0xbf,0xf3,0xa2,0x64,0x1c,0x47,0x93,0x69,0xd1,0x7b,0x3b,0x9f,0x0d,0x65,0xee,0x4d, - 0xd3,0x38,0xec,0xfd,0x34,0xfc,0xa3,0x1c,0x15,0x9e,0x0a,0x96,0xbd,0x12,0x82,0xc2, - 0xbb,0xf0,0xa4,0x7b,0x2d,0xf7,0xf7,0x9b,0xd2,0x2f,0xe4,0x55,0xf1,0x43,0x9a,0x14, - 0x32,0x29,0x06,0x85,0xbb,0xf2,0x86,0x73,0x65,0x35,0x1d,0x7a,0x99,0x7b,0x6d,0xee, - 0xc4,0x18,0x80,0x1f,0xfa,0x61,0xa4,0x82,0x61,0x2c,0xc3,0xc1,0x83,0xee,0xca,0xbe, - 0xeb,0xf4,0x33,0xbf,0x98,0xca,0xa4,0x39,0xf6,0xc6,0x6e,0x3f,0x97,0xc5,0x3c,0x4f, - 0x44,0xb6,0xf2,0xb2,0x34,0x8e,0xab,0x1e,0x95,0x37,0xf3,0x46,0x5e,0xca,0x58,0x28, - 0xbc,0xb1,0x37,0x19,0x74,0xfb,0xe5,0x08,0xef,0x61,0x84,0xc9,0xfe,0xfe,0x83,0x31, - 0xc0,0x36,0x1e,0x74,0xbd,0x51,0x2c,0x83,0xfc,0x43,0x34,0x93,0xe9,0xbc,0x68,0x16, - 0xae,0xd7,0x54,0xfe,0x28,0x88,0xe3,0x27,0xaa,0xe9,0xf6,0x7e,0xd7,0x54,0xae,0xab, - 0x47,0x34,0xbd,0x87,0xee,0xf5,0xa8,0x19,0x7a,0x88,0x15,0x98,0x4b,0xf9,0x58,0xe2, - 0x63,0x42,0x95,0x74,0x57,0xfa,0x9b,0xd7,0xde,0x6b,0xd7,0x5d,0x95,0x23,0xbf,0x86, - 0x91,0xc7,0x83,0x4e,0x7f,0x6d,0xc4,0x3e,0x40,0xd3,0x2c,0x06,0x40,0x12,0xe6,0xd9, - 0x7b,0x6f,0x06,0xdf,0xa5,0xfb,0xfb,0xa9,0x3f,0x8e,0x72,0x55,0x0c,0x06,0x80,0x88, - 0x27,0xf0,0x79,0x0f,0x80,0xd7,0xd3,0x86,0xe5,0x4a,0xb3,0x9e,0xb5,0xe4,0x93,0xcd, - 0x9e,0x57,0x1e,0x74,0x5a,0xb5,0x49,0xdc,0xeb,0xd9,0x20,0xe9,0x8f,0x6f,0x6e,0xa0, - 0xab,0x95,0x97,0xa4,0x8b,0xde,0xfb,0x15,0xb4,0x21,0xb2,0xa8,0x9a,0x8d,0x60,0x26, - 0xfb,0xfb,0x3f,0xfa,0xd9,0x5c,0x4d,0xe1,0xa6,0x7f,0xfe,0x04,0x6e,0x47,0xcd,0x73, - 0xb7,0xf7,0xeb,0x93,0x5f,0x7d,0xf8,0x0a,0xe0,0xf8,0x75,0xf0,0xce,0x47,0xa4,0x37, - 0x7f,0xf6,0xde,0x78,0xcf,0xa1,0xb3,0x45,0x50,0x8c,0xa6,0xe7,0x6b,0x3d,0xcd,0xdc, - 0xeb,0x77,0x3e,0x77,0x0f,0x70,0xff,0xea,0x03,0x34,0xcd,0x37,0x83,0x37,0x41,0x31, - 0xf5,0x67,0x11,0xbc,0xf6,0xde,0xb8,0xf4,0x69,0x54,0xbc,0x4c,0xf3,0xe7,0xf2,0x32, - 0x1a,0xc9,0xea,0x63,0x58,0xbf,0x74,0x90,0xde,0xdc,0x5c,0xaf,0xcc,0x42,0x27,0x72, - 0x21,0xde,0xe5,0xe9,0x2c,0x52,0xb2,0x5a,0x8e,0xa5,0x97,0xf0,0x42,0x4f,0x2b,0x90, - 0x0e,0xe5,0x51,0xb5,0x30,0xca,0xbd,0x56,0xb0,0xdc,0xcd,0xd4,0x1f,0xce,0xa3,0x38, - 0xdc,0xdf,0x57,0x7c,0x31,0x18,0xe8,0x27,0x2e,0xa0,0x7f,0xea,0x23,0x3a,0x9b,0xae, - 0xb7,0xc4,0x15,0x5f,0xb9,0x7d,0x6b,0x35,0x2c,0x1c,0x9b,0x66,0xfd,0xa4,0xf9,0x22, - 0xcf,0xd3,0xbc,0xe9,0x14,0xdc,0xc8,0xc1,0x89,0xa4,0xbe,0xbe,0xbb,0xb9,0x79,0x2c, - 0x8f,0xa1,0x97,0xd5,0xaa,0x22,0xbb,0xdf,0x35,0xe7,0x48,0x92,0x7a,0x2a,0x63,0x09, - 0xe8,0xa2,0x27,0x6b,0xd4,0x95,0xbb,0xd7,0xc8,0xd5,0xb9,0x9f,0x5e,0xb8,0xc5,0x34, - 0x4f,0x17,0x82,0x07,0xca,0x35,0x1e,0x4b,0xaa,0x27,0xea,0x40,0xce,0x7a,0x92,0xd3, - 0x3f,0xb0,0x28,0x39,0x08,0x0c,0x04,0x73,0x65,0xd1,0xdc,0x73,0x9c,0x3f,0xf4,0x08, - 0x7f,0x9f,0x03,0x9f,0x67,0x63,0x5e,0xa3,0x81,0xea,0xff,0xe8,0x8f,0xd3,0xfc,0x45, - 0x00,0x60,0x94,0x83,0x8f,0x81,0x3e,0xa1,0x25,0x02,0xbe,0x72,0x9b,0x6e,0x29,0x6f, - 0xb4,0xe0,0x4a,0x82,0x99,0x1c,0x34,0x2e,0x23,0xb9,0xc8,0xd2,0xbc,0x68,0x88,0x91, - 0xe6,0xe9,0xc6,0x22,0x0a,0x0b,0x10,0x54,0xb4,0x80,0x2d,0xba,0x01,0x49,0x11,0x15, - 0x51,0x10,0xb7,0x14,0xf0,0x92,0x1c,0x74,0x51,0xca,0x15,0x51,0x11,0xcb,0xb3,0x77, - 0x41,0x51,0xc8,0x1c,0xe4,0x48,0xba,0x38,0x6d,0xf3,0xa3,0xbd,0xd3,0x38,0x4a,0x2e, - 0x44,0x2e,0xe3,0x41,0x23,0x82,0x4e,0x1b,0x62,0x9a,0xcb,0xf1,0xa0,0x11,0x06,0x45, - 0xd0,0x8b,0x66,0xc1,0x44,0xb6,0xd5,0xe5,0xe4,0xe0,0x6a,0x16,0x7b,0xdf,0x1e,0xfd, - 0x00,0x97,0x02,0x2e,0x13,0x35,0x70,0xa6,0x45,0x91,0xf5,0xda,0xed,0xc5,0x62,0xe1, - 0x2f,0x8e,0xfc,0x34,0x9f,0xb4,0x0f,0x3b,0x9d,0x0e,0x36,0x76,0x04,0x82,0xf9,0x2c, - 0xbd,0x1a,0x38,0x1d,0xd1,0x11,0xdd,0x87,0xf0,0x7f,0xe7,0xdb,0xa3,0x17,0xf0,0x7d, - 0x0e,0xc2,0x4a,0x30,0xc4,0x0e,0x3c,0x14,0x53,0x89,0x12,0x8d,0xaf,0xc7,0x51,0x1c, - 0x0f,0x9c,0x6f,0x0f,0x8f,0x3a,0x3f,0x74,0x9e,0x75,0x1e,0x3b,0x6d,0xeb,0x13,0xe8, - 0xea,0xc4,0x11,0x4b,0xfa,0x5b,0x7f,0x6e,0x7d,0x6d,0x7f,0xfc,0xf2,0xe5,0xc9,0x0f, - 0x87,0x2f,0xcc,0xc7,0x08,0x0e,0x5c,0x91,0x9c,0x2f,0x96,0x38,0x5d,0x21,0xda,0xdf, - 0x89,0xb7,0xa9,0x58,0xc8,0xa1,0x18,0x03,0x0e,0x55,0x4f,0x00,0x11,0x08,0x58,0xe0, - 0xd1,0x85,0x12,0x63,0x10,0x3e,0x62,0x08,0x97,0xa2,0x80,0x26,0xd3,0xa0,0xa0,0x97, - 0x8c,0x5c,0x11,0x29,0xa1,0xa6,0x29,0xc8,0xf5,0x89,0x48,0x13,0x4f,0x04,0x49,0x08, - 0xbd,0xc1,0x7f,0xd8,0x24,0x0b,0x12,0x19,0x3b,0x4a,0xa4,0x8b,0x44,0x4c,0xd3,0x42, - 0x65,0x69,0x21,0xa6,0x81,0x12,0x49,0x2a,0xa2,0x04,0x71,0x2e,0x0b,0xec,0x92,0x48, - 0x0f,0x3f,0x98,0x89,0x31,0x30,0x13,0xf4,0xb1,0x5c,0x04,0x4b,0x5f,0x7c,0xd7,0x86, - 0xae,0x7a,0x79,0x9a,0x16,0xd7,0xad,0xd6,0x70,0xd2,0xfb,0x86,0x71,0xd0,0x6f,0xb5, - 0xa8,0xe3,0xde,0x37,0xdd,0xa3,0x6e,0xb7,0xdb,0x81,0x7b,0x58,0xac,0xde,0x37,0x2f, - 0x9e,0xbf,0xf8,0xfe,0xf9,0x33,0xb8,0x9b,0xcd,0x0b,0x19,0xf6,0xbe,0x79,0xf4,0xf4, - 0xd1,0xe1,0xf7,0x87,0x70,0x3f,0x0e,0x60,0xb4,0xde,0x37,0x27,0x4f,0x4f,0x4e,0x8e, - 0x1f,0xf6,0x19,0x3c,0xf8,0xaf,0xd5,0x9a,0x4c,0x53,0x05,0x2f,0x0e,0x0f,0xbb,0x2f, - 0xbb,0x8f,0xa0,0x61,0x3e,0x8f,0x25,0xdc,0x1e,0x1f,0x76,0xe9,0x36,0xc6,0x5e,0x18, - 0x75,0x70,0x97,0xc2,0x10,0x27,0xdf,0x3f,0x7b,0xf4,0xfd,0x4b,0xb8,0x59,0x04,0x79, - 0xd2,0xfb,0xe6,0xf9,0xe3,0xa7,0x9d,0xa3,0x97,0x76,0x8f,0x2a,0x48,0x54,0xcf,0x79, - 0x85,0x93,0x73,0xbc,0x79,0x44,0xf7,0x2d,0x25,0xf3,0x68,0xec,0xa9,0xa5,0x2a,0xe4, - 0xac,0x35,0x8f,0xbc,0xea,0xa1,0xfd,0xe9,0x2c,0x4d,0xd2,0x9e,0xf3,0xaf,0xb2,0x78, - 0x96,0x03,0xbc,0x4a,0xbc,0x81,0x7b,0xea,0x03,0x5f,0xa8,0x2c,0x18,0x49,0xef,0xfc, - 0x25,0x3e,0x6c,0xbd,0x97,0x93,0x79,0x1c,0xe4,0xde,0x1b,0x99,0xc4,0xa9,0x57,0xbe, - 0x5e,0x41,0x67,0xdf,0x5d,0x0f,0xd3,0xab,0x96,0x8a,0xfe,0x0c,0xeb,0xd1,0x1b,0xa6, - 0x79,0x28,0xf3,0x16,0x3c,0xe9,0xcf,0x82,0x7c,0x12,0x25,0x3d,0xd0,0x56,0x41,0x18, - 0xe2,0xbb,0x0e,0xb6,0x1e,0xa6,0xe1,0xf2,0x1a,0xe4,0x5e,0x8b,0x69,0xa6,0xd7,0xed, - 0x74,0x2e,0xa7,0x7d,0x5c,0xe9,0x49,0x9e,0xce,0x93,0xb0,0x07,0x52,0xac,0x89,0xa8, - 0x77,0xfb,0xa3,0x34,0x4e,0x73,0x7d,0x0f,0xd8,0x76,0x4b,0xc8,0x91,0x5c,0x7a,0xdd, - 0x93,0xec,0xaa,0xdd,0xf5,0x4f,0x4e,0x04,0xb7,0xc0,0x19,0xba,0xfd,0x16,0x90,0xd3, - 0x45,0x54,0xb4,0xb0,0x49,0x4b,0xcd,0x60,0x1d,0xa7,0x38,0x74,0x90,0x20,0x1f,0x46, - 0x81,0x92,0xe1,0x8a,0x29,0xef,0x03,0xd1,0x0a,0x01,0x86,0xf4,0x84,0xa4,0xb3,0xc8, - 0x83,0x2c,0x03,0x24,0x2a,0x0f,0xa8,0xa5,0x20,0x48,0xf1,0x5a,0xa5,0xf4,0x76,0x04, - 0x72,0x68,0x26,0x81,0xb2,0xd0,0x8a,0x91,0xb9,0x68,0xf2,0x7b,0x86,0x89,0x14,0x14, - 0x34,0x41,0x41,0x2a,0xf2,0x39,0xa0,0x52,0x86,0x13,0x89,0x84,0x46,0xff,0xc6,0xd1, - 0x85,0x04,0x52,0x15,0xf2,0x52,0xe6,0x4b,0x01,0x30,0xc1,0xf7,0x19,0x70,0xb4,0x2f, - 0x66,0xc1,0x15,0xcb,0x0a,0x01,0xd3,0x5f,0xe8,0xde,0x86,0x4b,0x66,0x85,0x28,0xac, - 0x60,0x84,0xae,0x2e,0xa4,0xcc,0xe8,0xc5,0xc9,0xc3,0x4e,0x76,0x05,0x82,0x27,0x9e, - 0xcf,0x12,0x4d,0xb2,0x3e,0x91,0xe7,0x35,0x75,0x85,0x38,0xfd,0xb6,0x5f,0xf6,0xdc, - 0x7b,0xd8,0x79,0x94,0x95,0xcb,0xd1,0x7d,0x08,0x9f,0x06,0x73,0xe8,0xae,0x5a,0x98, - 0xc3,0x63,0x78,0x46,0x7f,0x3d,0x84,0xbf,0x10,0x41,0x3c,0xc7,0x6b,0xb0,0x39,0xb2, - 0x18,0x2d,0x9a,0x58,0x5e,0xf5,0x01,0x7f,0x93,0xa4,0x15,0x01,0x41,0xa9,0xde,0x48, - 0x22,0xb9,0xf5,0x27,0x41,0xd6,0xc3,0xbe,0x75,0x47,0xb0,0xec,0x45,0x91,0xce,0x7a, - 0xdd,0x43,0x78,0x56,0x52,0x02,0x3f,0x82,0xce,0xc1,0x9a,0x8b,0x42,0xbd,0x5a,0x48, - 0xf6,0xae,0x86,0xc9,0xb4,0x39,0x7c,0xc4,0x83,0xfb,0x21,0x70,0x1f,0x83,0xfe,0x3d, - 0x74,0xa4,0x09,0x05,0x2f,0x37,0xc8,0x04,0xd8,0xc5,0x35,0x23,0xe5,0x41,0x18,0x81, - 0xde,0xed,0xea,0x19,0x74,0xaf,0x99,0x06,0xa2,0x3f,0x4b,0xa2,0x95,0x3e,0xdd,0x2e, - 0xb8,0xb3,0x87,0x9d,0x4e,0x3f,0x96,0x28,0x83,0x5b,0x48,0xc7,0x88,0x04,0xbf,0xd3, - 0x95,0xb3,0x8a,0x68,0x71,0xca,0xbd,0x2e,0x81,0xa3,0xe6,0x43,0xee,0x6b,0x1c,0xcc, - 0xa2,0x78,0xa9,0x87,0x46,0x1e,0x70,0xfb,0xd6,0x18,0x30,0x70,0x8d,0x64,0x49,0x04, - 0xb8,0xab,0x3d,0xa6,0xb7,0xdf,0xfe,0xfb,0x2f,0xf0,0x7f,0xc1,0xfa,0x5d,0x8c,0x82, - 0x3c,0x14,0xcd,0x34,0x81,0x45,0x9e,0xa1,0xa4,0x63,0xb5,0x26,0x50,0x73,0x8b,0xdf, - 0xfe,0xf2,0xdf,0xb6,0xb0,0x4b,0x90,0x68,0xa0,0x41,0x2e,0x83,0x19,0xb4,0x88,0xae, - 0x64,0xac,0x5c,0xd3,0x1d,0xaf,0x3d,0x28,0x28,0x29,0x93,0xeb,0x0d,0xec,0x10,0x4d, - 0x18,0xfc,0xec,0x58,0x82,0x92,0x06,0x90,0xa4,0xe8,0xaf,0x87,0x7a,0x15,0xc0,0xc6, - 0x69,0x4d,0xc3,0xfb,0x11,0x41,0xb7,0x63,0x7d,0x74,0x71,0x1f,0x6c,0xc1,0x07,0x1b, - 0x2b,0xd0,0x3d,0x86,0x15,0x40,0x35,0xde,0x2a,0x72,0xe0,0x66,0xd0,0xcc,0xb3,0xde, - 0x1c,0x59,0x72,0x04,0x8c,0xbb,0x05,0xb7,0x7a,0x91,0xfa,0x8b,0x29,0x00,0x44,0xdd, - 0xc8,0x1e,0x00,0x00,0x6c,0x5c,0xc2,0x92,0x04,0x97,0xf5,0x09,0x20,0xb4,0x27,0x16, - 0xb0,0xc3,0x22,0xb9,0x26,0xae,0xcb,0x52,0x15,0xa1,0xfe,0xef,0x81,0x06,0x0e,0x0a, - 0x30,0xff,0x6d,0x6a,0x23,0x78,0xb2,0x20,0x87,0x19,0xdf,0x8e,0x4e,0x1b,0x48,0xd2, - 0x09,0x5a,0x6a,0xdd,0x03,0x23,0xc8,0x34,0x60,0x02,0xc8,0x52,0x32,0xf6,0x99,0x07, - 0x90,0x2d,0x0c,0x13,0x20,0x93,0x72,0x87,0x75,0xa2,0xc7,0x6f,0x47,0xf3,0x5c,0xc1, - 0xd8,0x59,0x4a,0x7a,0xae,0x6f,0x66,0x1d,0x25,0xd4,0xe9,0xae,0xd5,0xa3,0xce,0xfe, - 0x38,0x57,0x45,0x34,0x5e,0xb6,0xb4,0x2d,0x63,0xde,0xd1,0xac,0x19,0x29,0x48,0xa4, - 0x7e,0xf7,0x04,0xe4,0x1a,0x2c,0x05,0x7c,0x63,0xe3,0xaf,0x37,0x4d,0x81,0x44,0xaf, - 0x37,0xc4,0xb5,0x06,0xd1,0x7e,0x8e,0xfc,0x5a,0xfb,0x34,0x18,0x21,0xaa,0x37,0x49, - 0x97,0xd4,0xa4,0xdb,0xaf,0xe8,0x80,0x4c,0xa8,0x66,0xc7,0x7f,0x7c,0xe8,0x6a,0x09, - 0xfe,0x5e,0xaa,0x79,0x5c,0x08,0x9c,0x1d,0x48,0x69,0xb4,0x3b,0x64,0x88,0xa2,0xf3, - 0xdd,0x4b,0x1f,0xbc,0x30,0xe0,0x9b,0x2b,0xd2,0xf4,0x24,0xbb,0x61,0x5e,0x79,0x1a, - 0xc3,0x35,0x18,0x12,0xa3,0x60,0x0e,0x6a,0x80,0x74,0xbf,0x11,0x9e,0x33,0x35,0xb9, - 0x17,0xcd,0xfa,0x27,0x3b,0x78,0x9c,0xfb,0x00,0xc3,0xb2,0x25,0xf3,0x3a,0x26,0x68, - 0xc6,0xe6,0x65,0x7a,0x51,0x7b,0x07,0xd6,0xad,0xcd,0x69,0x0c,0x07,0xea,0x46,0x5e, - 0xf8,0x4e,0x1f,0xf1,0x8a,0x36,0x62,0x6f,0x1a,0x85,0xa1,0x4c,0x98,0x37,0xca,0x87, - 0x32,0x8e,0xa3,0x4c,0x45,0x6a,0x17,0xfd,0xe3,0x80,0xe3,0x31,0xe2,0x87,0x86,0xf0, - 0x6a,0x0f,0x50,0xe2,0x08,0xff,0xf2,0x3a,0x45,0xe6,0x03,0x27,0xd7,0x3f,0x31,0xa0, - 0x58,0x72,0xf3,0xf0,0x1e,0x72,0xb3,0xe5,0x77,0x0e,0x81,0x6d,0x6b,0x94,0x0b,0x94, - 0x62,0x24,0x29,0x88,0x6d,0x30,0x3d,0x8f,0x50,0x01,0x55,0x4a,0xff,0xf0,0x31,0xdc, - 0x9b,0x79,0xb4,0x10,0xe0,0x1e,0x1a,0x60,0xa0,0x16,0x65,0x89,0x90,0x2f,0x94,0xba, - 0x9a,0xe9,0x6c,0x0b,0x43,0xb3,0x7b,0x25,0x84,0xdf,0xa1,0x5c,0x14,0x6a,0x11,0xa1, - 0x0d,0xc8,0xcf,0xfe,0xee,0xff,0x67,0xcd,0xff,0x54,0x28,0x39,0x99,0x21,0xf7,0x85, - 0xa0,0xf9,0xa3,0x9c,0x2d,0x91,0x40,0x28,0x90,0x33,0x6c,0x05,0x4c,0x26,0x60,0x2f, - 0x8a,0xf6,0xec,0x4f,0x05,0x3c,0x8f,0x41,0x29,0x84,0x4b,0x50,0x0a,0x68,0x32,0x0f, - 0x65,0xb1,0x00,0x35,0xc0,0x1d,0x51,0x18,0x03,0xcd,0x1a,0xb0,0x6d,0xc0,0xb0,0x15, - 0x4d,0x1f,0xa6,0xda,0x16,0x3e,0x70,0x01,0x18,0x3a,0x11,0xd8,0x1b,0x41,0x22,0x7c, - 0x30,0x49,0x46,0x71,0xa0,0x40,0x9f,0x80,0x0d,0xcd,0x8d,0xb5,0x2d,0xa4,0xc0,0xcb, - 0xe1,0x9e,0x26,0x52,0x81,0xab,0x25,0x2b,0x3b,0x88,0xc3,0x32,0x64,0x8d,0x80,0x99, - 0x0d,0xb4,0x74,0x99,0x8e,0x82,0x21,0x5a,0x87,0x60,0x3f,0xbf,0x42,0xa8,0xa0,0x25, - 0xda,0xe7,0x30,0xf2,0x34,0x82,0x61,0x09,0x16,0xee,0x6c,0x99,0xce,0x05,0x48,0x51, - 0x30,0xc5,0x45,0x1e,0x90,0x19,0x04,0x2c,0x99,0x88,0x40,0x5d,0xe0,0xec,0xf0,0x2d, - 0x70,0x2c,0x4e,0x0a,0xe6,0x1c,0x07,0x43,0x58,0x25,0x04,0x2c,0x4a,0xc6,0xd0,0x32, - 0x2a,0x4a,0x5b,0x67,0x71,0x4f,0xad,0x74,0x58,0xda,0x3d,0x2d,0x8c,0x13,0xa0,0xed, - 0x53,0x1a,0x91,0xc6,0x66,0xa1,0x17,0x47,0x95,0xc1,0x42,0xf7,0xdb,0x64,0xfb,0x4a, - 0x0f,0xfd,0x15,0xfa,0x0d,0xf8,0xc4,0x0c,0xff,0xf9,0x6a,0xae,0x1c,0x1f,0xe4,0xa6, - 0xda,0x54,0x6a,0x46,0x6d,0x53,0x8b,0xeb,0x4d,0x6d,0x76,0x4f,0x5e,0xda,0xb0,0x8a, - 0x1e,0xdd,0x07,0x68,0x63,0x45,0xa0,0x71,0xd9,0x3d,0xde,0x54,0x46,0xa6,0x83,0x1d, - 0x2a,0x75,0x0b,0x03,0xdf,0xaa,0x65,0x4b,0x78,0x2a,0x05,0x45,0x5d,0x54,0x2a,0xca, - 0xb3,0x95,0x4f,0xf5,0xb8,0xc2,0xd0,0x67,0x28,0x2d,0x86,0xa8,0xfa,0x14,0xd8,0xe6, - 0xde,0xca,0xae,0x4f,0xbe,0xd1,0x34,0x08,0x41,0x5e,0x83,0x87,0x05,0xce,0x67,0x87, - 0xfe,0x87,0xf3,0x5a,0x53,0x89,0xd8,0x37,0x70,0xbb,0xac,0x79,0x47,0xc7,0x75,0x0a, - 0xfe,0x7e,0x5d,0x94,0x3d,0xcb,0xb1,0x1d,0x68,0x40,0x65,0x9e,0x00,0x6e,0x89,0xad, - 0xa4,0x16,0x70,0xec,0x56,0xa7,0x9a,0xd9,0x90,0xcd,0x02,0x70,0x88,0x81,0xab,0xb4, - 0x4f,0x4d,0x46,0x22,0x23,0x34,0x4c,0x41,0x64,0x34,0x5f,0x77,0xc1,0xc9,0x89,0x53, - 0x60,0x8d,0x2c,0xc7,0x7e,0x15,0xfc,0x05,0x18,0x76,0x37,0x24,0x00,0xba,0xd5,0x11, - 0xea,0xd8,0xd4,0x30,0xe6,0x30,0x8f,0xbe,0x8c,0x31,0x8d,0xf9,0x08,0x1d,0x94,0x3c, - 0x86,0x44,0x9f,0x80,0x7c,0x31,0x2f,0x5a,0x97,0x5f,0xa1,0x0f,0x58,0xa3,0x1e,0x21, - 0x3a,0x89,0x88,0x09,0xb0,0x1e,0x21,0xaf,0x6f,0x8d,0x54,0xc3,0xed,0x8f,0xc0,0x36, - 0x80,0x9e,0x1c,0x44,0x61,0x81,0x26,0x38,0x50,0xa1,0xcc,0x09,0x41,0xbe,0xff,0xfa, - 0xd8,0xfd,0x2b,0x6b,0x0e,0x8d,0xc2,0x51,0x91,0xc7,0xea,0x7a,0x5d,0x68,0xd5,0x84, - 0xd5,0xf1,0x7d,0x85,0x15,0xf5,0xb5,0x69,0xc3,0xaf,0x5b,0x7b,0x64,0x38,0xb4,0xb4, - 0xfa,0xa8,0x2d,0xd9,0x10,0x58,0x06,0x15,0xfb,0x9a,0xa3,0x86,0xcb,0x67,0x0d,0xf0, - 0xe5,0xf2,0xf0,0xf0,0xf3,0xec,0x7d,0xf6,0xc6,0x68,0x19,0x5a,0xe0,0x2d,0x7f,0x36, - 0xa9,0xad,0x83,0x5f,0x75,0xd5,0x03,0x2d,0x58,0xb4,0xc8,0x7f,0xbf,0xae,0x37,0xee, - 0x70,0xcb,0x16,0x69,0xa3,0xfb,0x12,0xe0,0xba,0xc9,0xb4,0x93,0x20,0xd1,0xe1,0xea, - 0xd7,0x29,0xbd,0x02,0x0a,0xf4,0x5e,0x36,0x2f,0x3e,0x62,0x4a,0x65,0x00,0xf8,0x99, - 0xc8,0x4f,0x1e,0x31,0xc8,0xc6,0x63,0x76,0x60,0xb4,0x33,0xa4,0x25,0x07,0x52,0x0d, - 0x86,0x33,0x80,0x7c,0x13,0x32,0x0b,0x61,0x15,0x4d,0x70,0x64,0xfd,0xf9,0x2e,0xeb, - 0x7b,0x8b,0xa3,0x81,0x8a,0x32,0x9d,0x17,0x48,0x14,0xfc,0xed,0x9a,0xa0,0x0f,0x46, - 0x88,0xfb,0x4d,0x31,0x58,0xa4,0xf3,0xd1,0xb4,0x15,0x50,0x84,0xb5,0x07,0x22,0xa7, - 0xb5,0x34,0x4e,0xc4,0x6d,0xf3,0xed,0xf5,0x0c,0xc8,0xba,0x55,0x31,0x9d,0xcf,0x86, - 0x3b,0xb0,0xb0,0xbd,0x31,0xe3,0xe6,0xbe,0x98,0xd0,0xf1,0x13,0xcb,0xe3,0xa2,0xeb, - 0x3a,0x0e,0x4e,0x3a,0xdf,0x6a,0xd4,0xac,0xe3,0xcd,0xd2,0x04,0xe8,0x92,0xd5,0xd8, - 0x92,0xd0,0xb0,0x86,0x2d,0x4b,0x7f,0x95,0x1c,0x00,0xca,0xaa,0xee,0x65,0xdd,0x8a, - 0x20,0xf6,0x9f,0x3e,0x0f,0x4f,0xb7,0x7d,0xc3,0xe8,0x5a,0x77,0xbb,0xba,0xfe,0xa1, - 0xbb,0x3d,0xf8,0x72,0xcf,0x55,0x9c,0xa5,0x7f,0x6e,0xd1,0xcd,0x5d,0x2b,0xb8,0xd6, - 0x90,0xc1,0xb9,0xf7,0xaa,0x7c,0xed,0x82,0xe8,0xd9,0x80,0x0a,0x78,0x09,0x02,0x0a, - 0x84,0x7d,0x0f,0x94,0x25,0x98,0x09,0x45,0x04,0x68,0x40,0x7d,0x9a,0x49,0x11,0x8c, - 0xf2,0x14,0xb4,0x62,0xa0,0xf5,0x81,0x50,0x70,0x1f,0xc7,0x4a,0xeb,0xd2,0x89,0x14, - 0x4d,0x22,0x6f,0xd7,0x8a,0x4b,0x73,0xd8,0xcf,0x38,0xa1,0x13,0x59,0xe0,0xe7,0xc3, - 0x68,0x02,0x23,0x08,0x70,0xdd,0xd0,0x22,0x0e,0x44,0x98,0x07,0x8b,0x44,0xc8,0x2b, - 0x58,0x9d,0x78,0x09,0x16,0x31,0xd8,0xf3,0xb0,0x02,0xd2,0x47,0x9f,0x80,0x46,0x32, - 0xb1,0xc6,0x21,0x99,0xc4,0x4a,0x60,0x5c,0x80,0x4d,0x79,0x1c,0x1a,0x2d,0x2f,0x58, - 0xb6,0xd1,0x05,0x7e,0x1a,0x08,0xe4,0xc2,0x1c,0xfd,0xe1,0x12,0x23,0x1e,0xe8,0x6a, - 0x84,0x12,0xb0,0x4a,0x9f,0x72,0x67,0xe9,0x58,0x1b,0xe9,0x64,0x24,0xcc,0x51,0xcf, - 0x81,0xbb,0x44,0x5e,0x40,0x65,0x9f,0x69,0x19,0x20,0x9a,0xd8,0xe8,0xc7,0x17,0xcf, - 0x45,0x8e,0x66,0xc4,0x0c,0x0c,0x28,0x85,0xf6,0x7a,0xa0,0x7d,0x0e,0xb6,0x6f,0xd8, - 0xd8,0x71,0xfb,0xe0,0x80,0x4f,0xc8,0xf2,0x14,0x2c,0x50,0x55,0x15,0xbf,0xcc,0xd9, - 0x37,0x00,0x6f,0x0f,0x6f,0x97,0x62,0x21,0x71,0x9e,0x1f,0x28,0xfa,0x2f,0x33,0x3d, - 0xcb,0x39,0x88,0x5f,0xf8,0x08,0x50,0x03,0xd0,0x1d,0x63,0x70,0x12,0xc0,0x5b,0x04, - 0x39,0x4c,0x24,0x40,0x8f,0x86,0xc2,0xf6,0xf4,0x92,0x6c,0x1b,0x63,0x9d,0x2c,0x75, - 0xc0,0x0c,0xad,0xbb,0x38,0xc8,0xb4,0x65,0xf2,0xcf,0x33,0x19,0x46,0x41,0x53,0x2f, - 0x33,0x18,0x8b,0x98,0x7c,0x76,0x99,0xba,0xbe,0x48,0xe2,0x1a,0x47,0xd6,0x8a,0xac, - 0xb6,0x10,0x71,0x9d,0x35,0xaa,0xec,0x18,0x5b,0xd5,0x22,0x4d,0x14,0x9e,0x41,0xde, - 0x9a,0x60,0x13,0xc0,0x6e,0xd3,0x96,0xb9,0x9e,0x7d,0xe3,0x0a,0x56,0x64,0x6d,0x0c, - 0xe8,0xd2,0x0a,0x27,0x69,0x2b,0x97,0x20,0xb2,0x8a,0xd5,0xdd,0xa0,0xff,0xd5,0x84, - 0x27,0xea,0x80,0x6d,0x01,0xfe,0x2d,0xe1,0xa9,0x8a,0x2d,0x31,0xc8,0xa6,0xd9,0xcd, - 0xb6,0xf4,0x37,0xb0,0xd1,0x1a,0xc5,0x51,0xd6,0xab,0x02,0xc8,0x57,0xbb,0x2c,0xe6, - 0xc3,0x0d,0x8b,0xf9,0x6f,0x2d,0x76,0xfe,0x81,0xe7,0x6d,0xc2,0x67,0xbd,0x60,0x0c, - 0xf4,0xe1,0x69,0x97,0x86,0xef,0xae,0x8d,0x5d,0xd7,0x68,0xf4,0x4b,0x37,0x30,0x18, - 0x02,0x4c,0xc0,0xde,0x7d,0xb4,0x18,0x5b,0x3a,0x40,0x4f,0x06,0x0e,0xdf,0xc4,0x72, - 0x5c,0xf4,0x5a,0xa8,0xdd,0xc9,0x26,0xa6,0x4b,0x1e,0xeb,0x9b,0x2c,0x28,0xd0,0x13, - 0xb8,0x34,0xdd,0x73,0x53,0x8a,0xaa,0xd0,0x3b,0x0c,0xb4,0x99,0x77,0xfa,0x63,0x13, - 0x72,0x29,0x9d,0x2d,0xfd,0x9a,0x06,0x7f,0x64,0x8d,0xad,0x23,0xfe,0x64,0x78,0x53, - 0x48,0xaa,0x72,0x6c,0x27,0x79,0x14,0xf6,0xf1,0xaf,0x16,0x98,0x74,0xf0,0xa4,0x90, - 0x2d,0xce,0x78,0xa8,0x5e,0x77,0x9c,0x0b,0xf8,0x43,0xf6,0x5d,0x87,0x82,0xd6,0xeb, - 0x5e,0xfe,0xca,0xf4,0x77,0x6d,0x3c,0x53,0x34,0x40,0x01,0x9b,0x8f,0xef,0x6b,0x3b, - 0x73,0x7c,0xec,0xab,0x5c,0xfd,0xcf,0xb6,0x6c,0x75,0x48,0xee,0x3e,0xb1,0xe2,0x8d, - 0x48,0x24,0x69,0x3a,0x0b,0x0b,0xc7,0x56,0xa8,0x63,0x33,0x3e,0x78,0xcf,0xc8,0xa2, - 0x05,0xd6,0x30,0xc8,0x8d,0xd0,0x3b,0xda,0x96,0x89,0xd1,0x26,0xe3,0x56,0x5f,0x15, - 0x25,0x3b,0xc8,0x5f,0x4a,0xc5,0x0f,0xe3,0x14,0x74,0x54,0x84,0x3a,0x0a,0xf3,0xdd, - 0x24,0xb6,0x41,0x05,0xce,0xc0,0x27,0xa1,0x58,0x57,0x94,0x80,0x30,0x8f,0x28,0xac, - 0x7b,0x21,0x30,0x55,0x4c,0x1e,0xab,0xd1,0xa4,0x65,0xb3,0x38,0x4d,0x2f,0x04,0xb0, - 0xd0,0xe8,0x02,0x8b,0x62,0x30,0x9e,0x84,0x8a,0x65,0x06,0x93,0x81,0x3b,0x50,0x6a, - 0xd8,0x3b,0x34,0x85,0x2b,0x8c,0x26,0x81,0xe6,0x50,0xa4,0x60,0x74,0x70,0x6c,0x3e, - 0x6c,0x51,0x14,0x34,0xb2,0xd4,0x10,0x85,0xb2,0xd8,0x41,0x5e,0xa4,0x39,0x68,0x55, - 0x15,0x2c,0xf1,0x66,0x02,0x4e,0xb1,0xd6,0x22,0x01,0x93,0x94,0xa1,0x50,0x9a,0x0a, - 0x63,0x2e,0x94,0xa3,0x34,0x0f,0x88,0xd3,0xd8,0x2a,0xa6,0x95,0xd1,0x73,0x59,0x95, - 0x9f,0x72,0xd0,0x01,0xd7,0x78,0x5b,0x50,0xbc,0xde,0x06,0xe3,0x9f,0xeb,0x0b,0x6c, - 0xad,0x47,0x19,0x1e,0xb5,0x42,0x37,0xb5,0x10,0xec,0xf1,0xc9,0xb6,0x50,0x92,0xb5, - 0x3e,0x27,0x3b,0x29,0x24,0x9f,0x05,0x71,0x7d,0xe9,0x45,0xb4,0x36,0xeb,0x2a,0x7b, - 0xfb,0x6d,0xdf,0x44,0xab,0xb7,0x67,0xe7,0x2c,0x1b,0x97,0x13,0x9d,0xfe,0x91,0x5a, - 0x73,0xb4,0x7f,0xce,0x42,0x60,0x6f,0x34,0x6c,0x12,0xb4,0x32,0x98,0x24,0xc5,0x3c, - 0x29,0xa2,0x98,0x56,0x66,0x88,0x06,0x03,0xa6,0x4c,0x73,0x32,0x39,0xa2,0x62,0xc3, - 0xf7,0xe6,0xf5,0xf9,0x66,0x9e,0x85,0x25,0x9c,0xb4,0x0e,0xb6,0x58,0xa8,0xdc,0xe6, - 0x0d,0xb6,0x27,0x40,0x4b,0x49,0xd1,0x35,0x41,0x2c,0x3b,0xdf,0xac,0xf1,0x7c,0xb4, - 0x8d,0xed,0xb6,0x92,0x80,0xf9,0xb8,0x06,0xcf,0x56,0xe7,0xda,0x4e,0xa0,0xe1,0x14, - 0x84,0x5f,0x04,0x93,0x2f,0x15,0x3c,0x14,0xaf,0xdb,0x70,0xbf,0xee,0x0c,0xe0,0x6d, - 0x4f,0x21,0x20,0x34,0x3a,0x50,0x66,0x2d,0x6e,0x3e,0x19,0x06,0xcd,0xc3,0x93,0x13, - 0xef,0xf1,0xa1,0x77,0xfc,0xd0,0xf3,0x3b,0x0f,0xdd,0x0a,0xf4,0x49,0x6a,0x1c,0x68, - 0xd2,0x13,0x98,0x69,0xee,0x7f,0x45,0xd8,0x66,0x2b,0x58,0x40,0x39,0x3f,0x25,0xb5, - 0x6a,0x0f,0x53,0xe9,0x41,0xe6,0x70,0x92,0xc2,0xfd,0x0c,0x33,0xa7,0x05,0x70,0xf2, - 0x05,0x07,0xc0,0x28,0x1d,0x84,0xe6,0xa5,0x27,0xe6,0x60,0x85,0xa6,0xc6,0x32,0xa4, - 0x32,0x37,0x90,0x0f,0x6b,0xf4,0x6d,0x6c,0x3a,0xad,0x36,0x0e,0xff,0xff,0x12,0xcf, - 0xca,0x02,0xec,0xde,0xf8,0x27,0x6e,0xf5,0x81,0xc7,0xe4,0x35,0xa9,0x4d,0xd6,0x96, - 0xbd,0x6e,0xbb,0xd5,0x5d,0x55,0xef,0x50,0xf8,0x6c,0xe7,0x76,0xc0,0xea,0xd3,0x79, - 0x31,0x4d,0x73,0x43,0xb2,0x02,0xb5,0xa1,0x12,0x4d,0xcc,0x1b,0x5d,0x80,0xa4,0x45, - 0x23,0xdb,0x40,0xe5,0x82,0x90,0x9c,0xc7,0x21,0xdb,0xde,0x8b,0x48,0x01,0x93,0x4a, - 0x1d,0x50,0xfc,0xf9,0xa9,0x63,0x04,0xe6,0x47,0x66,0xe5,0x4f,0x35,0xa6,0x7c,0x10, - 0xcd,0xb0,0x72,0x2a,0x48,0x8a,0x35,0x31,0xf0,0x12,0x7a,0x98,0xe7,0xda,0x3d,0xf8, - 0xbb,0x27,0x64,0x34,0x79,0x20,0x30,0x76,0x38,0xee,0xa8,0x8c,0xb3,0x1b,0xa4,0xac, - 0x51,0xce,0x17,0xc6,0xd9,0x6f,0xcf,0x7d,0x1b,0x62,0xda,0x2d,0xd1,0xb5,0x45,0xb5, - 0x01,0x9d,0x38,0x10,0x98,0x03,0xdf,0x5e,0x69,0x81,0x6f,0xee,0x63,0x0d,0x41,0x3b, - 0x8e,0x90,0xa5,0xe3,0x16,0x1a,0xce,0x5b,0x63,0x64,0x30,0xa4,0x1d,0x94,0x63,0x6c, - 0x6c,0xe6,0x21,0x4a,0x4e,0x41,0x7f,0xe6,0x31,0x5a,0xb7,0x74,0xd5,0xb9,0x57,0xc1, - 0xc8,0x36,0x3c,0x68,0x51,0xb1,0x55,0x08,0x6f,0x98,0x39,0x3b,0x13,0x06,0xac,0x80, - 0x79,0x0e,0x3b,0x72,0x03,0x9b,0x4d,0x68,0xd2,0x64,0xf9,0x5c,0xef,0x4a,0x18,0x98, - 0xf7,0x9b,0x96,0xb8,0xb6,0x95,0xd1,0xfa,0x26,0x75,0x0c,0x9a,0xb4,0x5a,0x7a,0xba, - 0x42,0xa3,0xf7,0x3f,0x9a,0x2d,0x78,0x53,0x2f,0x2c,0xb0,0xc3,0x73,0x35,0x09,0x73, - 0x72,0xb8,0x16,0x7c,0x3c,0xda,0x9a,0xaf,0x3d,0x5e,0xcf,0xd7,0xf6,0xb5,0x63,0xdc, - 0x02,0xd7,0x19,0x2b,0xf1,0x08,0x77,0xb7,0xe3,0x4a,0xe3,0xa1,0x55,0xd8,0x56,0xc8, - 0xf7,0x6b,0xc3,0x9f,0x6c,0x1d,0xbe,0xbb,0x91,0x2e,0x3e,0xea,0x6f,0xb5,0x74,0x78, - 0x08,0x5f,0xa5,0x60,0x37,0x56,0xe3,0xed,0xc6,0x34,0xbe,0xc7,0xf8,0xad,0x29,0x7c, - 0x3a,0x7a,0x38,0x9a,0xda,0x26,0xc0,0xd1,0x76,0x25,0xb3,0x26,0xa1,0xd7,0xcd,0xa8, - 0x75,0x50,0xbc,0xad,0x70,0x85,0xd7,0x3b,0xac,0x7a,0xd4,0xe5,0x6b,0xb5,0x18,0xf7, - 0x16,0x14,0x3b,0x15,0xfc,0x9d,0x82,0x62,0x87,0xae,0xd2,0x32,0x63,0x0b,0x12,0x0c, - 0x5b,0x02,0x06,0x04,0x06,0x96,0xac,0xb0,0x39,0x29,0x72,0x82,0xc5,0x84,0xc2,0x74, - 0xfa,0x44,0xc7,0xd1,0xc7,0x69,0x8a,0xce,0x9d,0x8d,0x68,0x54,0x80,0x9f,0x91,0x73, - 0x20,0x0b,0xe8,0xe1,0xba,0xd2,0xfc,0xc2,0x64,0xe5,0x67,0xba,0x5d,0x0c,0xbd,0x08, - 0xae,0xef,0x29,0x53,0xec,0x6f,0x6e,0x11,0x14,0x3a,0xe2,0x54,0x11,0x23,0x15,0xed, - 0xb9,0xd7,0xba,0x50,0xaf,0x94,0x82,0x87,0xda,0x5e,0x10,0x27,0x87,0xc6,0x67,0x16, - 0xb6,0xdc,0xa8,0x66,0x7c,0x8c,0x0d,0x8c,0x94,0x35,0x81,0x03,0x16,0x22,0xdf,0x77, - 0xac,0x6f,0xb5,0x2e,0x7f,0x2e,0xd5,0x05,0xae,0x05,0x97,0x36,0x4e,0x31,0x61,0x67, - 0x12,0x77,0x40,0xb6,0x54,0x7a,0x46,0x65,0x63,0x54,0x6f,0x46,0x40,0x89,0x2c,0x02, - 0x03,0x3c,0x14,0xb8,0xde,0x9e,0xa8,0xca,0x11,0x69,0x0c,0x6a,0x16,0x25,0x60,0x41, - 0x05,0x21,0x3b,0x58,0xd9,0x14,0x3b,0x61,0x2b,0x43,0x80,0x90,0x05,0x14,0x61,0x2d, - 0x65,0x22,0xc0,0x5d,0x2f,0x96,0x82,0x56,0xb8,0x1e,0x7e,0xab,0x4a,0x5b,0x1e,0x33, - 0x32,0xaa,0xe9,0x12,0x52,0x2a,0x64,0x75,0xbb,0x0f,0x1f,0xd9,0xe8,0x40,0x83,0xe6, - 0x3e,0x91,0x01,0x10,0x38,0x80,0x4a,0x18,0x07,0xba,0x6a,0x76,0xbc,0xee,0x38,0x77, - 0x39,0x39,0x8e,0xd4,0x6b,0x1b,0xdf,0x60,0x0f,0xe5,0x45,0xd5,0x3f,0x4e,0xb9,0x12, - 0xd1,0x40,0xaa,0xa3,0x8b,0x25,0x49,0xe5,0xe3,0x63,0x1b,0x8c,0x75,0x63,0xa0,0xb3, - 0x5a,0xed,0x9d,0xb6,0xb9,0xa0,0xf9,0xb4,0x4d,0xdb,0x5b,0x4e,0xb1,0x2c,0xf4,0x6c, - 0x0f,0xfe,0x3b,0x9d,0x01,0x8d,0x71,0x7d,0xc5,0xa0,0x41,0x13,0x6c,0x60,0xd5,0xf3, - 0x29,0xd7,0x56,0x9e,0x9d,0x02,0x82,0xca,0xd7,0x61,0x5a,0xd0,0xce,0x16,0x78,0x74, - 0x76,0x3a,0xed,0x9e,0xfd,0xc0,0x2b,0x05,0x7d,0x76,0xeb,0x0d,0xc1,0xed,0x6b,0x9c, - 0x71,0x65,0xa0,0x69,0xde,0xd6,0x1d,0xa2,0x31,0x75,0x1a,0x98,0x86,0xda,0x4e,0x6b, - 0x88,0x28,0x1c,0x34,0xa6,0xea,0x07,0xb8,0x35,0x15,0xe5,0xed,0x45,0x34,0x8e,0xe0, - 0x86,0x54,0xe2,0xd9,0x7f,0xa4,0xf3,0xfd,0x5c,0xfd,0x69,0x9e,0xf6,0x73,0xa2,0x8c, - 0xd2,0xac,0xd6,0x4f,0xb9,0x94,0x5a,0xdb,0xd2,0x62,0x7f,0x16,0x06,0x6a,0xda,0x17, - 0x40,0x7e,0x58,0xae,0x91,0x8b,0x5f,0xa2,0xd6,0xcb,0x48,0xec,0xe7,0x41,0x9e,0xf7, - 0x4f,0xdb,0x01,0x43,0x11,0x46,0x97,0x06,0x0e,0x5c,0x27,0x9e,0xb7,0xf5,0x10,0xd1, - 0xbd,0xf1,0x90,0xc9,0x91,0x1e,0xd7,0x5f,0xe8,0xf2,0x28,0xfd,0x06,0xde,0xd9,0x08, - 0xa1,0x82,0xc3,0xc6,0xd9,0xdb,0x74,0x21,0x90,0x38,0x80,0x04,0x35,0x5e,0xb6,0x35, - 0x9e,0xa9,0x09,0x23,0x04,0x1d,0x01,0xbc,0x39,0x5b,0x6b,0xbc,0x36,0x2a,0xd8,0x3d, - 0xe5,0xa8,0xf0,0x96,0x63,0xd2,0x76,0x83,0x61,0x91,0x70,0x87,0x26,0x48,0xd6,0x10, - 0x54,0xcd,0x3f,0x68,0xbc,0x83,0x9b,0x28,0xc5,0x9a,0x4e,0x2e,0xf5,0x87,0x17,0x18, - 0x71,0x6c,0x70,0x1f,0x8d,0xb3,0xfd,0x98,0x31,0xc6,0xf7,0xf7,0x1f,0x04,0xfd,0x98, - 0x72,0x90,0xb7,0xe8,0xd4,0xec,0x1a,0x20,0xdf,0x36,0xc0,0x69,0x1b,0xa6,0xa8,0x51, - 0x6c,0x5d,0xd6,0xe7,0xcd,0xa3,0xe1,0xc5,0x99,0x5e,0xef,0xdd,0x6d,0xb1,0x4e,0xab, - 0x6c,0x4f,0xd4,0x79,0xcf,0xb6,0x20,0x95,0xc7,0x25,0x11,0xea,0x6f,0x36,0x3e,0xca, - 0x16,0x3b,0x96,0x9d,0x6a,0x04,0x1a,0x67,0x54,0xc9,0xb5,0x7b,0x0d,0x4d,0xb5,0xcc, - 0xee,0x45,0xa4,0x16,0x1a,0xbb,0xad,0x74,0x03,0x89,0x3f,0x25,0x77,0xae,0x50,0xad, - 0x07,0x15,0x4b,0x99,0xad,0x77,0x72,0x8e,0x0f,0x3f,0x7f,0x21,0x4c,0x3d,0x88,0x28, - 0xa9,0x16,0x9e,0xe0,0x83,0x9d,0x28,0x1e,0xe6,0xd1,0xad,0xd8,0xaa,0x8a,0x45,0xd6, - 0x50,0x46,0x21,0x71,0x0d,0x35,0x85,0xc1,0x1b,0xe5,0x97,0x63,0x9d,0x10,0x60,0x08, - 0x70,0x04,0x14,0xaf,0x83,0xc6,0x61,0x03,0x6b,0xd7,0x07,0x8d,0x6e,0xa7,0xd3,0x20, - 0xbf,0x1a,0x2e,0x1b,0x60,0x65,0xc4,0x73,0xe8,0xe3,0x11,0x3c,0x0b,0xf2,0x28,0xe0, - 0xac,0x39,0x30,0x03,0xa9,0x97,0x61,0x39,0xfc,0x76,0x28,0xa9,0xc6,0xa3,0x1c,0x07, - 0x2e,0x2b,0xea,0xab,0xa0,0xdd,0x45,0x29,0x54,0x8a,0xc0,0x1f,0xf3,0xe5,0x36,0x7a, - 0x28,0x0b,0x22,0x6c,0x82,0xb0,0x41,0xd0,0x05,0x0d,0x8d,0x33,0xaa,0xfd,0x78,0x9d, - 0xa4,0xc3,0x35,0x54,0xed,0x12,0x28,0xf8,0xe1,0x36,0x81,0x52,0x2d,0xd4,0x9a,0xb0, - 0x2b,0x13,0x17,0xbb,0x40,0xd1,0x25,0x07,0x8d,0xb3,0xd7,0xdd,0x0d,0x10,0xee,0xbd, - 0x5e,0x2a,0x6e,0x75,0xf4,0x82,0x75,0x6e,0x5b,0xb0,0x93,0x4e,0xe3,0xaf,0x0a,0xf2, - 0xe1,0x57,0x81,0xdc,0xfd,0x7b,0x80,0x7c,0xf4,0x55,0x20,0x1f,0xfe,0x3d,0x40,0x3e, - 0xfe,0x2a,0x90,0x8f,0xbe,0x1c,0xe4,0x5d,0x4c,0x48,0x19,0x9c,0xad,0x9c,0x87,0x6f, - 0x1a,0x75,0x53,0x86,0xc4,0x37,0xa9,0x2e,0xc3,0x63,0xf6,0x07,0x5a,0x10,0xa8,0x56, - 0x86,0x1f,0xd6,0xd4,0xd0,0x6e,0xdc,0x6d,0x1d,0xe4,0xbc,0x00,0xf3,0x7d,0x22,0x6f, - 0x1b,0x63,0xac,0xb6,0x69,0xba,0x0d,0xf1,0x1a,0x80,0xdc,0x38,0x8d,0xca,0x6f,0xf8, - 0xbe,0x1d,0x9d,0x7d,0x11,0x58,0x64,0x38,0xdd,0x06,0x14,0x59,0x69,0x5f,0x35,0xf3, - 0xa7,0x61,0x98,0x57,0xc2,0x7e,0xeb,0x20,0x51,0x76,0xeb,0x10,0x0f,0x5a,0x2d,0xbd, - 0x53,0xaa,0xda,0x55,0x87,0x25,0x3e,0x0a,0xf7,0xaa,0x80,0x07,0xe5,0x61,0xe6,0x00, - 0xf3,0x2f,0x05,0xe6,0x52,0xe6,0x19,0xd6,0x4b,0x28,0x89,0x35,0x73,0x45,0x19,0x7f, - 0x2d,0x71,0x89,0x7e,0x1d,0x26,0xed,0x4d,0xb2,0x25,0x18,0x82,0xf7,0x64,0x12,0x40, - 0x54,0x15,0x81,0x15,0x0a,0x38,0x04,0x25,0x67,0x6a,0x05,0xc2,0x62,0x26,0x73,0xdc, - 0x2f,0xd5,0x6a,0xed,0x9a,0x3e,0x56,0x89,0x48,0x33,0xad,0xa9,0x42,0x9d,0x6c,0x8c, - 0x8b,0x35,0xb4,0xfc,0x0b,0x07,0x8b,0x6f,0x43,0xcb,0x54,0x7d,0x1d,0xe6,0xdf,0xc8, - 0x59,0x9a,0x2f,0x6f,0x1d,0x41,0x06,0xd9,0xd7,0x8d,0xf1,0x73,0x86,0x1b,0x6d,0x6f, - 0x1b,0x63,0xfe,0x95,0x23,0xbc,0x8c,0xf2,0xd9,0x22,0xc8,0x6f,0x67,0x9d,0xc5,0x9d, - 0x04,0xf4,0x0b,0x15,0x98,0x98,0x04,0x20,0x55,0x99,0xb3,0xbf,0x09,0x17,0xf9,0x3c, - 0x49,0xc0,0x66,0xa7,0xc2,0xf1,0x45,0xa0,0xb3,0x82,0x94,0xc1,0xe3,0x8d,0x51,0x59, - 0x0e,0x54,0x06,0x4e,0xa5,0x4d,0x44,0x48,0x3f,0xec,0x81,0x7b,0xba,0x78,0xa5,0xcc, - 0xe3,0x61,0x05,0x39,0x4a,0x40,0x95,0xce,0x24,0xe7,0xf2,0x40,0x85,0x83,0x43,0x06, - 0xb4,0x28,0x43,0x45,0x3e,0xac,0xa9,0x74,0xb7,0x7b,0xd4,0x45,0xef,0xbc,0x8b,0x8f, - 0xbf,0xc3,0xfd,0x76,0x60,0xa5,0x62,0xbf,0x48,0x7e,0x63,0x1d,0x9b,0xc6,0x01,0xe4, - 0x55,0x26,0x47,0x58,0xa1,0x1f,0x29,0xca,0x45,0xe2,0xc8,0xbe,0xdd,0xdd,0x79,0x30, - 0x93,0x3a,0xd1,0x09,0x53,0xa2,0xea,0x1f,0xee,0x54,0xc6,0x4a,0x52,0x5d,0x90,0xce, - 0x7e,0x0a,0x19,0x11,0x8d,0x2f,0xc0,0xab,0x45,0xdf,0x0b,0xb0,0xaf,0xd1,0x54,0x83, - 0xae,0xa0,0x24,0x24,0x6f,0x66,0x01,0x42,0xd7,0x4d,0x80,0x1d,0x47,0x54,0x0e,0xa3, - 0x9d,0xb6,0x50,0x8e,0x03,0xdc,0x10,0x53,0x6b,0x0e,0x5e,0xfe,0x54,0xc6,0x63,0xcf, - 0xee,0x6f,0x28,0x69,0x0b,0x8c,0x68,0x30,0xce,0x02,0x93,0xff,0x24,0x50,0x80,0x31, - 0xd1,0x8c,0x62,0x84,0xfc,0x69,0x2e,0x95,0x5e,0xb4,0xb2,0x7f,0xbb,0xa7,0x3c,0x88, - 0x14,0x66,0x47,0x2d,0xae,0x0c,0x6a,0x24,0xa5,0x89,0x44,0x86,0x1c,0xf3,0x32,0x1e, - 0x28,0x6e,0x59,0x56,0xbd,0x76,0x3b,0xab,0xb6,0x41,0xfb,0x28,0x2a,0xda,0x7a,0x6a, - 0x6a,0x97,0xee,0x03,0xa2,0x7c,0xc1,0x4d,0x36,0x54,0xdf,0x36,0xe2,0x94,0xe1,0x7d, - 0xe4,0x7a,0xe9,0x95,0x10,0xa0,0xeb,0x3e,0x0c,0x6a,0xbb,0x60,0xcd,0x50,0xaf,0xf4, - 0x1f,0x11,0xf8,0x3b,0x14,0x85,0xca,0x14,0x54,0x9f,0xbf,0xfa,0xf0,0x82,0xf7,0x6d, - 0x10,0xa9,0xb3,0xb3,0xae,0x65,0x67,0x7b,0x4e,0xa9,0xce,0x1e,0x2f,0x24,0x95,0x8b, - 0x61,0x7e,0x9a,0xb0,0x6d,0x60,0xcb,0x65,0x8c,0x41,0x56,0x50,0xca,0x49,0x34,0x06, - 0xfc,0x13,0xbd,0x60,0xc5,0xb6,0xd2,0x89,0xac,0x85,0xa0,0xed,0xe0,0xbc,0xc0,0x30, - 0xc2,0x30,0xc5,0xdd,0x84,0x14,0x8f,0x26,0xa5,0x82,0x22,0x75,0xaf,0xe2,0x95,0x5c, - 0x32,0xa1,0x0a,0x7f,0x88,0x69,0xf2,0x14,0xc8,0x1c,0x09,0x10,0xfe,0x24,0x69,0x49, - 0xeb,0x21,0x40,0x17,0xa7,0x01,0x6d,0xa0,0xc2,0xb1,0xcc,0x9a,0xc2,0x7a,0x22,0x66, - 0x00,0xec,0x3b,0xd7,0x8e,0xa7,0x06,0xce,0x4f,0x00,0x92,0xba,0x18,0x34,0xfe,0x30, - 0x8c,0x83,0x04,0x96,0x9c,0x76,0xb4,0x27,0x69,0x9a,0xc9,0x44,0x1a,0xc3,0xbb,0xb6, - 0xa8,0x45,0x00,0x76,0x33,0x7f,0x5d,0xb3,0xf4,0xa9,0x8d,0x1e,0x1d,0x23,0x71,0x55, - 0x84,0xc4,0xfe,0x7a,0x92,0x36,0xce,0xa6,0xc0,0xa8,0x30,0x09,0xee,0xa3,0x0c,0x46, - 0xe8,0xf0,0x48,0xb0,0xb6,0x62,0xd6,0xca,0x63,0x3c,0x87,0xe3,0x10,0x35,0x6f,0xc9, - 0xe4,0x4e,0x36,0x0c,0x14,0x6c,0x99,0x04,0xc6,0x08,0x0a,0xac,0x0f,0xd0,0x5e,0x33, - 0xe1,0x15,0x8d,0x99,0x1d,0x5e,0x8e,0x89,0xec,0x35,0xce,0x3a,0xdd,0x5b,0x3c,0x57, - 0x1d,0xed,0x2e,0x41,0x00,0x79,0x31,0x84,0x79,0x2d,0x77,0x8a,0x70,0x13,0x87,0x6e, - 0x9c,0x3d,0xcf,0xd3,0x0c,0xe3,0x70,0x01,0xb8,0xaa,0x67,0x7e,0x36,0x9e,0x81,0xef, - 0x79,0x26,0x66,0x69,0x38,0xc7,0xd8,0x5f,0x0e,0xcf,0x39,0x1c,0x88,0x6f,0xff,0x1c, - 0x65,0xf4,0x36,0xc3,0xca,0x41,0x13,0xdc,0xc1,0x40,0x0a,0x88,0xa9,0x9c,0x2c,0x48, - 0x0f,0x28,0x38,0x96,0x85,0xf4,0x6b,0xf6,0x9f,0x46,0x2a,0x21,0x63,0xaf,0x94,0xf3, - 0x80,0x7d,0xa4,0x42,0x2d,0x2c,0xf5,0xf6,0xa3,0xb1,0xd6,0x22,0x28,0xad,0xf2,0x08, - 0xf7,0xea,0xa1,0xf2,0xc7,0x92,0x2d,0xac,0xde,0xe3,0xba,0x3f,0x4a,0xf9,0x41,0x1b, - 0x43,0xb6,0xc8,0x3d,0x8b,0x3c,0x82,0x99,0x27,0xb8,0x6d,0x3f,0x35,0x5a,0x63,0x42, - 0xac,0x23,0x97,0x08,0x1c,0x7a,0x68,0x64,0x4f,0x58,0xc7,0xba,0x98,0xa1,0xdf,0x06, - 0x97,0x5e,0x95,0x2b,0x4a,0x8a,0x3c,0x1a,0x62,0x44,0xd7,0xec,0xac,0x46,0x4a,0x87, - 0x3b,0x03,0x27,0xd9,0x20,0x01,0xb3,0x23,0x6f,0x70,0xc8,0x79,0x77,0x03,0x97,0x12, - 0x82,0xaa,0xce,0x43,0xd3,0x1b,0x6a,0xa6,0x22,0x8a,0xcb,0x32,0x4e,0x9c,0x07,0x0b, - 0x74,0x3a,0x46,0xc3,0x64,0xa0,0x61,0x86,0xd4,0x00,0xd8,0x0e,0xb3,0xb6,0x19,0x4e, - 0x53,0xcf,0xc0,0xf4,0xc4,0xbb,0x10,0x53,0x62,0xb7,0x16,0x4e,0x56,0xa3,0x61,0x0c, - 0x4b,0xd4,0xc6,0xfd,0x53,0x9e,0xd9,0xe6,0x05,0xdd,0xb7,0x17,0x92,0xcd,0xa1,0x16, - 0xa0,0x31,0x0b,0x86,0x51,0x1c,0x15,0xcb,0x96,0xe9,0x6a,0x02,0x88,0x0f,0xab,0x0a, - 0xc8,0x50,0x62,0x6d,0x21,0x3e,0x81,0x59,0xd3,0x81,0x15,0x21,0xd3,0x03,0x4d,0x8c, - 0xa6,0x89,0xba,0xd5,0xec,0xf3,0xb2,0x2a,0x55,0x85,0xa5,0x07,0x8d,0xca,0x2b,0xd1, - 0x42,0x15,0x35,0x61,0x44,0x72,0x83,0x95,0x23,0x58,0x70,0x30,0x00,0x95,0x65,0x84, - 0x1c,0x38,0x44,0x49,0x08,0x58,0x36,0xbd,0x4d,0x83,0x4b,0x56,0xc2,0x8c,0x27,0xdc, - 0x1c,0x16,0x97,0xa2,0x65,0x3b,0xcf,0x11,0xc7,0xe3,0xd8,0xaf,0x23,0x52,0x18,0x6c, - 0xc3,0xe9,0xdc,0xf0,0x1a,0x23,0x96,0x6d,0xdf,0x23,0x23,0x57,0xc1,0xa4,0x92,0x2c, - 0x77,0xb0,0xf5,0xb9,0x2c,0xe6,0xd9,0x67,0xf1,0x34,0x1b,0xe3,0x77,0xf0,0xf3,0xd1, - 0x7d,0xf8,0x59,0xdb,0xfd,0x77,0x72,0xf1,0x7b,0x39,0x93,0x78,0xbc,0x10,0xd8,0xd3, - 0x40,0x84,0x41,0x6c,0xcc,0x69,0x55,0xb2,0xa9,0xb5,0x49,0xfc,0x8f,0x29,0x16,0xd8, - 0x92,0x31,0xc4,0xd5,0xaf,0x09,0xd9,0xe4,0x28,0xe9,0xd5,0x16,0xc6,0xbd,0x65,0xa2, - 0x17,0x18,0xf3,0xb8,0x73,0xa6,0xc7,0xf7,0x99,0xa9,0x0e,0x9f,0xdc,0x39,0x53,0xb6, - 0x0f,0xb1,0xa6,0x97,0xca,0x79,0x65,0x32,0x4a,0xb1,0xda,0x78,0x94,0xce,0x41,0xab, - 0xb2,0xb9,0x84,0x02,0x1e,0xf4,0xe1,0x92,0x8e,0x53,0x50,0x70,0x79,0x21,0x91,0x9c, - 0xb1,0xfe,0xab,0x44,0x07,0xf2,0x0d,0x07,0xaf,0x73,0x2a,0x74,0x0e,0x90,0x58,0xc7, - 0x63,0x99,0xfb,0xe2,0x6d,0xa5,0xe8,0x72,0x49,0x7c,0x7a,0xbb,0x34,0xdb,0x41,0x36, - 0xcf,0xed,0xb8,0xfb,0xfd,0xe8,0x46,0x2b,0xc5,0xbb,0xf0,0x79,0x72,0x1f,0x7c,0x1a, - 0x73,0x5c,0x18,0x5d,0x79,0x3f,0x4d,0x10,0xb0,0xe2,0xdf,0x42,0x35,0x63,0x68,0x3f, - 0xa5,0x82,0x2a,0x05,0x36,0x22,0x5b,0x10,0x54,0xe2,0xfd,0xf4,0x2d,0x61,0x1d,0x70, - 0x05,0x66,0x36,0xd8,0x77,0x46,0x0d,0xd2,0x53,0x8e,0xf8,0x13,0x1c,0x09,0x6d,0xeb, - 0x90,0xe1,0xe7,0x91,0x18,0x8b,0xea,0x3b,0x71,0xf2,0xf0,0x3e,0x38,0xe1,0x83,0x7c, - 0xee,0x81,0x8a,0x97,0xb8,0x8d,0x0b,0x7d,0x4a,0x10,0x77,0x33,0xf2,0xce,0x40,0x5e, - 0x72,0x64,0x80,0x6d,0xa1,0xb5,0x04,0xc7,0x96,0x33,0x67,0x82,0x51,0x31,0x07,0xf9, - 0x05,0xf2,0x35,0x45,0x97,0xe5,0x56,0x0a,0xda,0x62,0x29,0xb2,0xcb,0x72,0x56,0x99, - 0x35,0x3c,0xcf,0x9a,0xc9,0x13,0xdc,0x61,0x64,0xdd,0x69,0x5d,0xad,0x7f,0x80,0x90, - 0x9d,0xb6,0xf5,0xd0,0x7b,0xa7,0x6d,0xcc,0x49,0x01,0x38,0xe6,0xec,0xb4,0xbd,0xf2, - 0x88,0xa4,0x7f,0x6a,0x46,0xe5,0xb9,0x4c,0x61,0x3a,0x9a,0xe3,0x26,0x64,0x1f,0x46, - 0x7a,0x11,0x4b,0xbc,0x7c,0xb6,0x7c,0x15,0x42,0x8b,0xd5,0xde,0x3f,0x35,0x1d,0x84, - 0xdb,0x71,0x6b,0x07,0x9a,0x81,0x71,0x4f,0x19,0x52,0x1f,0xdf,0xa1,0xc2,0xe9,0xef, - 0xed,0xb5,0xdb,0xa6,0xb0,0xe7,0x17,0xc6,0x25,0x9a,0xaa,0xa4,0x23,0x4b,0x44,0xa2, - 0x8e,0xfd,0xdb,0xee,0xaa,0x06,0x20,0x6a,0x65,0x45,0x48,0xb6,0xbb,0x8d,0x0b,0x7f, - 0xab,0x0d,0x23,0x4b,0x13,0x06,0xbb,0xd3,0x56,0x0c,0x20,0x1e,0xab,0x98,0xf0,0x75, - 0xc4,0x87,0xb9,0x28,0xd4,0xb1,0xdb,0x6d,0x11,0x9d,0x63,0x2d,0xa6,0x1e,0xef,0x6a, - 0x26,0x13,0x08,0x3b,0x23,0x67,0xb7,0xf2,0x4a,0xa5,0x01,0x56,0xb1,0xd5,0x20,0x34, - 0xca,0x0a,0x13,0x38,0x09,0x0a,0xdc,0x0f,0xb2,0xcd,0xd2,0xc0,0xde,0x8c,0xb1,0x61, - 0x5c,0xcc,0x5b,0x0d,0x9a,0x6d,0x96,0x0c,0x5b,0x29,0xd8,0x95,0x31,0x4e,0x88,0x27, - 0x2a,0xef,0x10,0x85,0x34,0x17,0xa1,0x55,0xb6,0x0a,0xef,0x6e,0x31,0xd6,0x82,0xa9, - 0xc4,0x0d,0xf2,0x0b,0x00,0x04,0xbb,0x62,0xf3,0x31,0x64,0x4f,0x9a,0xa0,0x29,0x0d, - 0x18,0x76,0xf9,0x87,0x4a,0xea,0x48,0x12,0x48,0xff,0x99,0x0c,0x92,0xa2,0x66,0xaa, - 0x00,0x39,0x01,0xbb,0x61,0x4f,0xe5,0x9a,0xf0,0xf4,0xd0,0x13,0xd2,0x8f,0x1c,0x5e, - 0x3f,0x9c,0x4a,0xb1,0x69,0x70,0xc0,0xb7,0xf8,0xf9,0x7b,0x69,0xe1,0x12,0x18,0x00, - 0x0f,0x6b,0x23,0xa8,0xec,0x05,0xc7,0xd5,0x1a,0x4d,0xd1,0xf0,0x55,0xa2,0x89,0x94, - 0x52,0x1e,0xdb,0xa3,0x4f,0x88,0x23,0x57,0x8d,0xe9,0x20,0xa4,0x03,0x5b,0x94,0x41, - 0xb7,0x56,0x2d,0xd6,0x0e,0x5c,0x5d,0x56,0x23,0x42,0x90,0xc3,0x58,0xa4,0x88,0xe3, - 0x60,0xdb,0x31,0x30,0x98,0x72,0x3d,0x6c,0x93,0x60,0x5f,0x56,0x88,0x20,0x97,0x09, - 0x9d,0x2a,0xa8,0xcc,0x6e,0x5b,0xb2,0xba,0x78,0xa1,0xd8,0x55,0xec,0x74,0x3d,0xd1, - 0x39,0x84,0x3f,0x47,0xfe,0x1e,0x1e,0xf9,0x06,0x32,0xe7,0xb5,0x5c,0xf6,0x2b,0x56, - 0x46,0x6b,0x10,0x8f,0x3a,0x03,0x91,0x82,0xef,0x23,0x0f,0x5a,0x0c,0x9a,0x0a,0xcf, - 0x7c,0xab,0x26,0xe9,0xde,0xdc,0x7c,0xfc,0xe4,0x5d,0x0c,0xfe,0xf5,0xfc,0xa7,0xb7, - 0xbe,0x2a,0x70,0x47,0x4f,0x34,0x5e,0x36,0xa1,0x29,0x15,0x16,0x45,0xe3,0xe6,0xc5, - 0x60,0x30,0xe0,0xbe,0x5d,0x96,0x0b,0x7d,0xae,0x04,0x83,0x07,0x83,0x8b,0xbe,0xee, - 0x7c,0x3c,0x00,0x81,0x60,0x2c,0x30,0xc7,0xf5,0xc6,0x17,0xe6,0x01,0x9a,0x6f,0x8e, - 0xe9,0x6b,0xac,0x73,0xfd,0x63,0x1f,0x4b,0x0c,0xf2,0x7f,0xf9,0xf0,0xe6,0xc7,0x81, - 0xe3,0x98,0xa3,0x51,0xf2,0x66,0x34,0xe8,0xf4,0x23,0x54,0xa7,0x7e,0x2c,0x93,0x49, - 0x31,0xed,0x47,0x07,0x07,0x65,0x79,0x00,0x8e,0x23,0x11,0x94,0x8f,0xd1,0xa7,0x3e, - 0x9e,0x0b,0x27,0x6f,0x6e,0xa4,0x6e,0x78,0x7a,0xe8,0xa2,0x7d,0x1f,0x25,0xf3,0xb2, - 0xf6,0x05,0x9b,0x07,0x83,0x52,0x86,0x8d,0x00,0x6b,0x85,0xd4,0x62,0xac,0xe9,0x04, - 0x4e,0x59,0x38,0x15,0xf8,0xa4,0x25,0xde,0xe2,0x81,0x6e,0x0e,0x6b,0x0a,0xa7,0x1f, - 0xf8,0x24,0x81,0xe5,0xc7,0xce,0xa7,0xaa,0x9d,0x05,0xf4,0x2e,0x6d,0xd5,0xd1,0x72, - 0xdc,0x39,0x28,0x63,0x0c,0xce,0x76,0x95,0xa5,0xc3,0x64,0x5b,0xb5,0x14,0xbf,0x73, - 0xaa,0x91,0xff,0x34,0x07,0xca,0x38,0x07,0x0e,0x1a,0x15,0x78,0xd0,0x5e,0x59,0x05, - 0xb5,0x26,0x7f,0xe5,0xc7,0xee,0xa7,0x3b,0x3f,0x0a,0x37,0x3e,0x3a,0xfc,0x74,0x73, - 0xe3,0x94,0xa3,0x8d,0x7d,0xdc,0x0e,0x99,0x84,0x3f,0xe0,0x36,0xd8,0x66,0xa0,0xf1, - 0xb4,0xd2,0x0b,0xc7,0xc6,0xf5,0x60,0x7c,0x61,0xae,0x1e,0x8c,0x77,0x0c,0xc4,0x18, - 0x5e,0x69,0x0a,0x21,0x04,0xa9,0x6a,0x3d,0x6a,0xdf,0x3c,0x8d,0x63,0xf8,0x8c,0x44, - 0x49,0x59,0xf2,0xc2,0x9f,0x57,0x54,0xc1,0x1d,0xd8,0x84,0x41,0x10,0xf1,0x63,0x20, - 0x89,0xda,0x9c,0x9a,0xd1,0x41,0xf7,0xb4,0xdb,0x79,0xe2,0x74,0x9c,0x9e,0xe3,0xb8, - 0x07,0x78,0x0f,0xfd,0xad,0x6c,0x75,0x64,0x9f,0xc1,0xf4,0x0f,0x72,0xfa,0x47,0xa5, - 0xad,0x5e,0x54,0xa2,0x20,0x35,0xe2,0x12,0xe0,0x1c,0xa5,0x33,0x10,0x48,0xe8,0x6d, - 0x81,0xd8,0xb6,0x94,0x4c,0xaf,0x7e,0x36,0xd9,0x28,0xcd,0x96,0xd8,0x4b,0x13,0x0f, - 0xc1,0xe1,0xa3,0x12,0x3d,0x71,0x21,0x33,0x14,0x7a,0x52,0x4d,0xb5,0x24,0x3b,0x12, - 0x8a,0x5a,0x58,0xc7,0x55,0xba,0x38,0x1a,0x85,0xdf,0x41,0x9f,0x80,0x65,0x01,0x56, - 0x52,0x46,0xe7,0xe4,0x60,0x6f,0x1c,0xe3,0xc7,0x81,0x8a,0x60,0x88,0x3a,0x89,0x29, - 0x80,0x5d,0x77,0x6d,0x1f,0x99,0x03,0xaa,0x40,0xf2,0xf2,0x19,0x55,0x98,0x0a,0x18, - 0xa3,0xd1,0x45,0x75,0x11,0x91,0x5c,0x90,0xa0,0x4b,0x42,0x76,0x11,0xe5,0x15,0x88, - 0x35,0x74,0xc1,0x53,0x76,0x56,0x42,0x50,0x2c,0x28,0x19,0x41,0x37,0xa0,0x80,0x62, - 0x65,0x08,0xb2,0x10,0x25,0x69,0x0c,0xd6,0x0d,0x29,0x49,0x25,0x19,0x0a,0x4c,0x82, - 0x63,0x6f,0x58,0x5e,0x03,0x6a,0xe2,0x0f,0x26,0xe0,0xf2,0x07,0x34,0x9b,0xfc,0xa9, - 0x5f,0x89,0xc2,0x70,0x9e,0x5b,0x92,0x30,0xe4,0x53,0x37,0xc1,0x38,0x02,0xe2,0x52, - 0xed,0x47,0x0f,0x8f,0x3b,0x1d,0xd7,0x9b,0xd6,0x9e,0x7e,0x4b,0x4f,0xdb,0x47,0x0f, - 0xf1,0xd5,0xac,0xfe,0x0a,0x1f,0xb6,0x1f,0x76,0x8c,0x50,0x0b,0x5d,0x63,0x2f,0x1d, - 0x38,0xa1,0x70,0x0e,0xa6,0x07,0xce,0xd4,0x41,0x01,0x35,0x35,0x2f,0xf0,0x09,0xbc, - 0x98,0x1d,0x38,0x33,0x47,0x7f,0x34,0x33,0xef,0xf8,0xa1,0xbe,0x51,0x07,0x8e,0x72, - 0x90,0x4c,0x61,0x56,0x3f,0x65,0x08,0x7a,0x50,0x1e,0x0a,0x36,0x9a,0x46,0xa0,0x3c, - 0x48,0x69,0x19,0x25,0xcb,0x7c,0x7a,0xae,0xcd,0x8c,0x48,0xc6,0xe0,0xe5,0x89,0x1f, - 0xd1,0xb2,0x50,0xf8,0xd5,0xd2,0x52,0x96,0xad,0x60,0x92,0xa4,0x58,0xd0,0xc4,0xfa, - 0x24,0x52,0xb6,0x66,0x25,0xc5,0x87,0xe6,0x9a,0xb2,0x42,0xdc,0xcd,0xf7,0x27,0xae, - 0x85,0xc2,0x0c,0x2b,0xe5,0xde,0xc9,0x7c,0x6c,0x21,0x12,0x6c,0x4e,0x90,0xf2,0xba, - 0x88,0x03,0xf8,0x95,0x84,0x72,0x6c,0xa9,0x09,0x3a,0x73,0x76,0xf0,0xf1,0x93,0x9e, - 0xb4,0xf2,0x31,0x90,0xf1,0x3e,0x8d,0x25,0xe8,0x14,0x27,0x9b,0x0f,0x41,0xa9,0x81, - 0x39,0xe1,0xb8,0x05,0x9f,0xaf,0xea,0xbc,0x9b,0x0f,0x99,0xed,0x31,0x42,0xbe,0xf9, - 0x85,0x9a,0x0f,0xd1,0x60,0x1d,0xda,0x9f,0x9c,0x9b,0x4f,0xa0,0x35,0x26,0x3c,0xd1, - 0x9c,0xf1,0x95,0x04,0xf1,0x92,0x8c,0xe4,0x1b,0xf0,0x20,0xf1,0x43,0xf0,0x5f,0x62, - 0x30,0x28,0x1c,0x16,0x19,0xfa,0xd3,0x7a,0xb3,0x27,0xce,0xbb,0x38,0x58,0xa2,0x96, - 0x05,0x89,0xf1,0x8c,0x76,0x9a,0x54,0xfd,0x6a,0xa9,0xe3,0x5e,0xcb,0xb8,0x14,0x80, - 0x01,0x80,0xd8,0x87,0xfb,0xda,0x81,0xbe,0x3e,0x7a,0xe0,0x4d,0x47,0xfc,0xdf,0xff, - 0x23,0x1c,0xaa,0xff,0xc3,0x89,0x58,0x5f,0x15,0xf9,0x7c,0xe3,0x23,0xc7,0x59,0xc1, - 0x8a,0x23,0xaa,0xb0,0xb6,0x7a,0x80,0xc7,0xe6,0xf6,0xd7,0xf0,0xae,0x71,0x4e,0xef, - 0x15,0x02,0xa5,0x75,0x3b,0x5e,0xc2,0x1a,0x7f,0x00,0x5d,0x25,0xf5,0x59,0x3f,0x9e, - 0x31,0xbd,0x78,0xaf,0xae,0xe5,0xba,0x00,0xe7,0xa7,0xf1,0x25,0x9b,0xb2,0x33,0xb0, - 0x1f,0xa9,0x48,0x05,0x65,0x49,0x0e,0x1e,0x84,0xe2,0x9e,0x98,0xd9,0x29,0x7d,0x87, - 0x6f,0x51,0xfc,0x58,0x8e,0x0f,0x7f,0xb1,0x30,0xb6,0x31,0x2f,0x12,0xd8,0xa8,0xa9, - 0x75,0x04,0xa7,0x0e,0x61,0x23,0xe5,0x95,0xae,0x96,0x00,0x8a,0xc4,0x1c,0x0a,0x6d, - 0x15,0x18,0xa2,0xcb,0xce,0xc6,0x0d,0x53,0x33,0x6f,0x70,0xc6,0x38,0xc5,0x02,0x37, - 0x19,0x87,0x60,0x32,0x5a,0x09,0x11,0xdf,0x90,0x0e,0x0d,0xae,0x8d,0x01,0xa6,0xba, - 0x35,0x3d,0xe6,0xf0,0x8c,0xb4,0x1e,0xe3,0x26,0x0a,0xa9,0xa3,0xd6,0x4a,0xf9,0xbc, - 0xef,0xfd,0x49,0xd3,0x5c,0x1d,0x38,0x88,0x9a,0x39,0x09,0xd6,0x04,0x86,0xbc,0x80, - 0x85,0xef,0x39,0x9c,0x9a,0x4a,0xc7,0x63,0xea,0x6f,0x55,0x11,0xa4,0xae,0x9e,0x7c, - 0x47,0x98,0xd2,0xf0,0x10,0xbb,0xca,0x84,0x62,0x0b,0xd6,0xc9,0x28,0x20,0x04,0xf1, - 0x00,0x15,0x3a,0xe5,0x11,0x5a,0xa3,0xc1,0xca,0x32,0xbd,0x5f,0x1e,0xb2,0x04,0x46, - 0xc4,0x3c,0xc3,0x38,0xa4,0xe9,0x67,0x48,0xe6,0x28,0x85,0x67,0xb1,0x6e,0x15,0x4c, - 0x57,0x3c,0x57,0xda,0xf4,0xac,0x45,0x1c,0x88,0x78,0x76,0x5e,0xab,0xbc,0x2b,0x45, - 0x0e,0xab,0x04,0x02,0xf4,0x74,0x89,0xc5,0x90,0xec,0x56,0xe0,0x41,0x68,0x98,0x5f, - 0xad,0xea,0x36,0xcd,0xc1,0xa9,0x81,0xbd,0x4c,0x65,0x5a,0x96,0xc9,0x80,0x24,0xae, - 0x7f,0x0b,0xc2,0x19,0x07,0x77,0x20,0xdc,0xc9,0xca,0xa8,0x41,0x8e,0x94,0x40,0x3b, - 0xeb,0xb4,0xdb,0x85,0xb0,0x19,0xdc,0x6b,0xbf,0x20,0xa0,0x14,0x72,0x08,0xca,0x60, - 0x1d,0xf1,0x88,0x25,0xf8,0x18,0xed,0x57,0x5e,0x38,0xe4,0xec,0x96,0x53,0x2d,0xc1, - 0x53,0x13,0xdc,0xc6,0x96,0xca,0xe8,0x4d,0xf6,0x58,0x48,0xda,0x8d,0x09,0xad,0x4a, - 0xc2,0xfa,0x84,0x88,0x62,0x0c,0xf7,0xc2,0xd5,0x7f,0x72,0x77,0xff,0x69,0xfa,0x61, - 0xfb,0xba,0xd1,0x6a,0x90,0x17,0x42,0x18,0xee,0x55,0xbb,0xd6,0x51,0x4e,0x62,0x2f, - 0x05,0xdc,0x63,0x39,0x33,0x73,0xa8,0x0c,0xfd,0xdb,0xb1,0xa0,0xa1,0xff,0xed,0x2f, - 0xff,0xbb,0x9a,0xd7,0x2d,0xc4,0x6c,0xe6,0x08,0x86,0x19,0x20,0xe9,0x0e,0x14,0x03, - 0x72,0x68,0x15,0x7f,0x56,0x4f,0x48,0x51,0x91,0x6b,0xd6,0xec,0xca,0x87,0xed,0xf2, - 0x85,0x0b,0x54,0x3e,0x06,0xfd,0x81,0x82,0x89,0x2c,0x22,0x6d,0xec,0x95,0x5c,0xf0, - 0x4a,0xbd,0x21,0xe4,0x3d,0x71,0x18,0x89,0xd0,0x8a,0xdd,0xa4,0x28,0xa9,0x0c,0x39, - 0x5b,0x09,0xf4,0xcb,0xfb,0x74,0x21,0xf3,0x92,0x41,0xcb,0xc7,0x55,0x95,0x19,0xbc, - 0xab,0x6a,0xbe,0xa8,0x01,0x4c,0x84,0x2a,0x3a,0x36,0x26,0x6d,0x68,0x05,0xa0,0x15, - 0x4d,0xe7,0x00,0x1e,0x00,0x79,0x80,0x5f,0x0a,0xf7,0x06,0x1a,0x71,0x20,0xf0,0x05, - 0x43,0x89,0x2f,0xf4,0x95,0xeb,0x94,0x5a,0x66,0x0c,0x73,0x99,0xe3,0x9a,0x54,0xe2, - 0x02,0x0b,0x35,0xd6,0x46,0xb3,0x50,0x45,0xdf,0x7c,0x48,0x0b,0x3c,0xa3,0x19,0xae, - 0x7e,0x46,0xce,0x6e,0x77,0x3b,0xc7,0x8f,0x4e,0xbe,0x7f,0xf8,0x5d,0xb7,0x83,0xd7, - 0x30,0xd0,0x9b,0x67,0x68,0x3e,0xc9,0x6a,0x31,0x74,0xf5,0x07,0xf4,0x4b,0xb5,0xbf, - 0x3e,0x9f,0x84,0x5c,0x75,0xf6,0xc4,0x74,0xd6,0x2e,0x1f,0x41,0x6f,0x9d,0x5e,0x07, - 0x56,0xe3,0x5b,0x8b,0x0c,0xb6,0xc3,0xe7,0xa0,0xdb,0x39,0xe3,0x89,0x38,0x2b,0x23, - 0xe9,0x59,0xce,0xa3,0x66,0x68,0x72,0x1a,0xb0,0x28,0x7d,0x73,0x16,0x59,0xa8,0x81, - 0x00,0xfc,0x32,0x94,0xb0,0xe5,0x24,0x64,0xee,0x8b,0x12,0xbe,0x8b,0x94,0x32,0xd0, - 0xca,0x9c,0x89,0x98,0xa0,0x7c,0xa6,0x9d,0x71,0xe0,0x43,0x03,0xe3,0xf4,0x39,0x18, - 0xbb,0x51,0x96,0xe1,0x6b,0xed,0x3e,0x55,0xb0,0x6a,0xba,0x5b,0x3c,0x9e,0xdc,0x4b, - 0x06,0x53,0x90,0x91,0x31,0x9e,0x68,0xa0,0x6e,0x3a,0xe5,0x62,0x63,0xe0,0x7c,0x63, - 0xb5,0xf1,0x21,0xca,0x61,0xa5,0x22,0xb0,0x9d,0x88,0x36,0x61,0x65,0x73,0xb8,0x85, - 0xbb,0xf0,0xd9,0x0c,0xe4,0x30,0x74,0x36,0xcf,0x9e,0x10,0x2a,0x80,0x6b,0x13,0x4a, - 0xb8,0x03,0x69,0xea,0xf3,0x0b,0x9d,0xb2,0xff,0x28,0xdb,0xde,0xbb,0xf2,0xa3,0xac, - 0x57,0xb2,0x10,0x35,0xc5,0xa2,0x10,0x68,0x6c,0xdc,0x17,0x1a,0x41,0x93,0x0e,0x5d, - 0xbb,0xba,0xd5,0x5a,0x87,0xd3,0x3a,0x9c,0xd8,0x36,0xca,0x80,0x3d,0xbb,0x8f,0x0f, - 0xfd,0xee,0xc3,0x47,0xfe,0xb1,0xdf,0x75,0x5c,0xf3,0x36,0x39,0x68,0x26,0x20,0x9e, - 0xba,0x4f,0x1c,0xc1,0xb8,0x00,0x98,0xf5,0x95,0x72,0x8c,0xda,0x7e,0x8f,0xa8,0x95, - 0xa1,0x09,0xb8,0x92,0xa8,0xdf,0xbd,0x7f,0x51,0x2f,0x6c,0xaf,0xac,0x9b,0x01,0xd9, - 0x3b,0x9e,0xc7,0xdc,0x55,0xb9,0xad,0x91,0xf4,0x71,0x74,0x59,0xe9,0x0b,0xd2,0x62, - 0xbc,0x5e,0x18,0xb3,0xa3,0x0a,0x70,0x6b,0xf6,0x80,0xfd,0xcb,0x28,0xd0,0xa5,0x30, - 0xfb,0xfb,0x0f,0x18,0x6d,0x15,0x93,0x62,0x7d,0xca,0x6e,0xbe,0x81,0xa5,0x87,0xf7, - 0x74,0xe6,0x34,0x58,0xaa,0xc0,0x25,0x87,0xc7,0x80,0x82,0xd7,0xc4,0xa2,0xc4,0x24, - 0x1b,0x6d,0xdf,0x29,0x90,0x47,0x86,0xb5,0xa0,0xed,0x1b,0x91,0xe1,0x93,0x6a,0x75, - 0xe6,0xeb,0xc3,0x91,0xed,0x0e,0xcb,0x82,0x55,0x2e,0x15,0x5c,0xe3,0x0d,0x65,0x74, - 0x89,0xb4,0x03,0x98,0xc4,0x43,0xca,0xfa,0x7b,0x8c,0x16,0x5d,0x24,0x60,0x3c,0x14, - 0x2e,0x8c,0xe0,0xe8,0x0c,0xa8,0x44,0xc5,0xd1,0x5d,0x2e,0x5a,0x42,0x76,0xb1,0x36, - 0x81,0x23,0xed,0x93,0xc6,0xf2,0xb8,0xa7,0xb2,0x58,0x82,0xe2,0x80,0xda,0x44,0x7e, - 0x05,0xd6,0x42,0xa2,0x2b,0x40,0x66,0x11,0x50,0x47,0x32,0xd9,0xac,0x99,0x88,0x38, - 0x33,0x96,0x5e,0x48,0xa5,0xd7,0xbd,0x2a,0xef,0x30,0x50,0x60,0xc4,0x8b,0x4a,0xa1, - 0x00,0x08,0xcd,0x8e,0x69,0xc9,0x6e,0xe8,0xf8,0x19,0x76,0x93,0x21,0x10,0xf6,0x25, - 0xd6,0xd4,0x26,0x05,0x2a,0x42,0x7d,0xf9,0x00,0x34,0x21,0x6a,0x38,0xe7,0x49,0xf9, - 0xa8,0xe7,0x18,0x79,0x58,0x17,0x83,0x72,0xc3,0xfd,0x0f,0x0f,0x9a,0xe5,0x57,0xff, - 0xc6,0xf8,0x03,0xb2,0x25,0x6c,0xd6,0x1e,0xa2,0xda,0xe8,0xdb,0x1d,0xb5,0xb6,0xa8, - 0xb9,0x0f,0x55,0x59,0x0f,0x9d,0x7f,0x8d,0xa8,0x70,0x0e,0x24,0xb2,0x8d,0x34,0x8b, - 0x51,0xc6,0x46,0x9c,0x0f,0x41,0x46,0x4a,0xd9,0xac,0x43,0x95,0x1a,0xce,0x11,0x03, - 0x93,0xb4,0x3c,0xfb,0x5d,0x9f,0x09,0x9f,0x84,0xe8,0x05,0x9b,0x38,0xac,0xef,0xd4, - 0xe1,0xa1,0xe2,0x12,0x24,0x6e,0x0c,0xdd,0x38,0x77,0x56,0x97,0x7c,0x03,0x80,0x51, - 0x92,0xe9,0xe7,0xf7,0xaf,0x7e,0x48,0x67,0x19,0xac,0x00,0x98,0xda,0x7a,0x9f,0x74, - 0x5d,0x41,0x6f,0xc1,0x9b,0x63,0x1d,0xde,0xef,0xdc,0x03,0x2f,0xdb,0xe0,0x17,0x3f, - 0x91,0x71,0x62,0x00,0xa2,0xf9,0x2f,0x2d,0xfc,0x2c,0xca,0x88,0x39,0x39,0xc7,0x58, - 0xce,0x83,0x92,0x90,0x8e,0x86,0x0e,0x50,0x48,0x1b,0x5f,0x18,0xec,0xf9,0x57,0x60, - 0xdc,0x01,0x6f,0xb3,0x29,0xce,0xf6,0xba,0xce,0x8c,0x55,0xfd,0x21,0x7d,0xf1,0x81, - 0x9d,0xb4,0x69,0x23,0xb3,0xf3,0x39,0x8a,0x63,0xa6,0xea,0x2b,0x91,0xea,0x18,0x5b, - 0x81,0x34,0x31,0x1d,0x9b,0xa7,0x5c,0xce,0x54,0x9d,0xf3,0xc9,0x79,0xd5,0x63,0x6c, - 0x0a,0x12,0x70,0x74,0xc1,0x3b,0xff,0x9b,0x25,0xff,0x7a,0x25,0xed,0xad,0x07,0x6d, - 0xfe,0x11,0xcf,0x6c,0xd5,0x21,0x9b,0x0f,0xe5,0x59,0x8b,0x8c,0xfc,0x7f,0x7b,0xf5, - 0xe2,0x17,0x34,0xb5,0x2b,0x17,0xcc,0x51,0xec,0xa4,0x71,0x59,0x90,0xe1,0x6f,0xf0, - 0x09,0x8b,0x69,0x4f,0xbc,0xee,0x6a,0x8b,0x95,0x83,0xe5,0xe4,0x74,0x51,0x40,0x39, - 0x11,0x6f,0x7e,0xff,0xe1,0x03,0xd8,0xa2,0x4a,0xa1,0xb7,0xce,0x51,0xe9,0x32,0x6b, - 0xcf,0x75,0x6a,0xa5,0xeb,0x45,0x41,0x1d,0x1d,0x31,0xe0,0x10,0x3e,0xd1,0x10,0x2f, - 0x3a,0x51,0x12,0x6e,0xd7,0x02,0xab,0xd5,0x84,0xe1,0x31,0x8d,0x65,0x22,0xe3,0x1f, - 0xb4,0x66,0x91,0x57,0x23,0x99,0xb1,0xf0,0x62,0x0e,0x9e,0xa5,0x18,0xb3,0x13,0x74, - 0x7a,0x8b,0x21,0x2b,0x5f,0xbc,0xfb,0xe9,0xfc,0x83,0x0e,0x42,0xb1,0x2b,0x99,0xc4, - 0x4b,0x94,0x7b,0x20,0xf9,0xb0,0x37,0x0a,0x14,0x95,0x9b,0x47,0x89,0x68,0xd1,0xa1, - 0x68,0xba,0x02,0xc3,0x05,0xe0,0xdb,0xd0,0xb1,0x91,0x3a,0xde,0x94,0xf0,0x16,0x16, - 0xc5,0xc5,0x79,0x54,0x87,0xa1,0x67,0x11,0xc3,0xcc,0x74,0xe0,0x3f,0x4a,0xcc,0xe9, - 0xb6,0x65,0x24,0x1c,0x93,0x2f,0x0c,0x23,0x78,0x49,0x5c,0xad,0xcc,0xa2,0x9c,0x7c, - 0x4d,0x95,0x04,0x99,0x75,0x06,0x26,0x4b,0x13,0x8a,0xcb,0x1b,0x00,0xf9,0xe0,0x2f, - 0x9f,0xa8,0x3f,0x96,0x3f,0xd3,0x59,0x13,0x6a,0x9e,0xd1,0x91,0x97,0x52,0x5b,0x45, - 0xec,0x36,0xd0,0xc6,0x76,0x80,0xb0,0xd0,0x31,0xa9,0x2c,0x5e,0x56,0x75,0x87,0x94, - 0xc4,0x49,0xb1,0xb4,0x2f,0x49,0xe7,0x93,0x69,0x99,0x84,0xd6,0xeb,0x48,0xe5,0x1d, - 0x97,0xb2,0xca,0x78,0xe1,0xa1,0x59,0x61,0xdf,0x3e,0x91,0x80,0xe6,0x4a,0x08,0xa6, - 0x99,0x92,0xb9,0x83,0x72,0x42,0x71,0x74,0xdf,0x82,0x6f,0xd0,0x59,0x0f,0x0f,0xb0, - 0x05,0xce,0xee,0xb9,0x07,0x43,0x8f,0xf8,0x4c,0x2b,0x0c,0xc7,0xd0,0xdd,0xfe,0xfe, - 0x73,0xa0,0x3a,0xfe,0xb5,0x9a,0x53,0xab,0x27,0x2b,0x52,0x03,0xdc,0x8e,0x1b,0x5f, - 0x80,0xcf,0xed,0x60,0x38,0xee,0x69,0x01,0x53,0x87,0x7b,0x7e,0xe2,0xa0,0x11,0x93, - 0x6a,0x77,0x80,0x3e,0x60,0x87,0xfb,0xd6,0x6f,0xf0,0x03,0xd6,0x1c,0xab,0x0a,0x6a, - 0x8c,0xdc,0xe1,0xcf,0x22,0xb9,0xd7,0x7c,0x4a,0x37,0x5e,0x83,0x29,0x15,0x25,0xe3, - 0xd4,0xf1,0xb0,0x67,0xde,0xe1,0x82,0x3f,0x24,0x53,0x7d,0x04,0x80,0xdb,0x13,0xa5, - 0x29,0xda,0x68,0xa9,0xe6,0x78,0xd0,0xc5,0x1d,0xc7,0x75,0xff,0x44,0x63,0x07,0x03, - 0x30,0x04,0x3f,0x81,0x60,0x80,0x34,0xc1,0x0e,0xf4,0xce,0x7a,0xce,0x22,0xb8,0xd0, - 0xd7,0x1c,0x82,0xa6,0x9f,0xa7,0x71,0x2a,0x42,0x77,0xbc,0x6b,0x2c,0x2b,0x4d,0xc3, - 0x9e,0x83,0x2c,0xe0,0x78,0x23,0xb4,0xe3,0x7a,0x60,0xa4,0xb6,0x30,0x95,0x2c,0x1d, - 0x53,0x73,0xc4,0x3b,0xf2,0x54,0xef,0xda,0xd1,0x7a,0xa0,0xf5,0x61,0x99,0xa1,0x67, - 0x15,0x64,0x19,0xb0,0x10,0x99,0xf1,0xed,0xab,0xd6,0x62,0xb1,0x68,0x21,0x3f,0xb4, - 0xe6,0x79,0xcc,0x0a,0x09,0xec,0x7d,0xaf,0x3c,0x7b,0x2a,0x5c,0x82,0xbd,0x9b,0x0c, - 0x2c,0x9c,0x76,0xa1,0x8b,0x8e,0xe3,0xae,0x38,0x9c,0x75,0xe7,0x8f,0xe6,0x74,0x4c, - 0x78,0xb1,0xfa,0x4d,0x9c,0x6d,0x1f,0x96,0x89,0x16,0x1b,0xa9,0xfc,0xfb,0x44,0xc1, - 0x55,0xd3,0x7a,0xe8,0x59,0x98,0x7e,0xd4,0xe9,0x94,0x89,0x94,0x1a,0x4a,0xcb,0xf8, - 0x89,0xce,0x6b,0x2e,0xb1,0x54,0x68,0x88,0x8a,0x5d,0xef,0x9b,0xc2,0x40,0x0b,0x4a, - 0x00,0xa7,0xb2,0x2b,0x0c,0x5c,0x23,0x8c,0x81,0x6c,0x01,0x0c,0x18,0xe3,0xd9,0xfa, - 0x6f,0xc1,0xf0,0x38,0x18,0x19,0x03,0x71,0x18,0x85,0x2c,0x5a,0xe9,0x2d,0x26,0x15, - 0x49,0x52,0x19,0xdf,0x5c,0xac,0x73,0x91,0xae,0xab,0x1d,0x37,0xf1,0x73,0xd7,0x22, - 0x16,0xbc,0xb7,0xfc,0xd9,0x72,0x72,0xe4,0x87,0xb0,0x2c,0x01,0xab,0xcf,0xfc,0x42, - 0x03,0x83,0x60,0x2a,0xa2,0x40,0xe0,0x2d,0xf0,0x27,0x58,0x1c,0x99,0xe7,0xd5,0xc4, - 0x90,0xfc,0x2b,0x1e,0x0b,0xc2,0xf0,0x05,0x6e,0xb1,0xff,0x11,0xa3,0xdf,0x09,0x0c, - 0xe8,0x10,0xa0,0x8e,0x67,0xcd,0xb9,0x24,0x77,0x8a,0x34,0xe2,0xcf,0x30,0xed,0xd5, - 0x78,0xee,0xb3,0xfa,0x20,0xb2,0xc7,0x2e,0x2c,0x55,0xba,0x71,0x66,0xf0,0x3f,0x96, - 0x3a,0x7d,0x4a,0xbf,0x5a,0xb4,0xa1,0x40,0x59,0xc4,0xd3,0x2f,0xa9,0x58,0x82,0x9d, - 0xf6,0x10,0xa0,0x0e,0xad,0x9d,0x59,0x8c,0xdd,0xe8,0x63,0x8b,0xcd,0x31,0x85,0x46, - 0x6b,0x72,0x00,0x72,0xab,0xda,0x34,0xba,0x52,0x9a,0xa3,0x1d,0x59,0x63,0x52,0x3a, - 0x3c,0x89,0x25,0x1d,0xfa,0x38,0xd5,0x87,0x2f,0x92,0xf6,0xf2,0xc5,0xef,0x5e,0x68, - 0x45,0xa8,0xf7,0x26,0x3f,0xa9,0xe2,0x1d,0x83,0xea,0x67,0x65,0xb0,0xb4,0xaf,0x65, - 0x52,0xfe,0xb9,0x24,0x0f,0x01,0x75,0x1a,0xff,0x8c,0x9c,0x67,0xca,0x7a,0xf1,0x29, - 0xcf,0x12,0x7f,0x0c,0x0c,0xed,0xb9,0xa1,0x9c,0x62,0xad,0x6e,0x54,0xe8,0x0a,0xc5, - 0x30,0x0f,0x26,0xe8,0xab,0x03,0xb9,0x61,0x57,0xa4,0x72,0xd1,0x69,0x37,0x11,0xaf, - 0x04,0x77,0x42,0x93,0xb3,0xcc,0x72,0x33,0x07,0xb3,0x8a,0xd5,0x08,0xc0,0xf5,0x94, - 0x63,0x67,0x44,0x55,0x1e,0xdc,0xbf,0x4a,0x5e,0xd2,0xf8,0xd5,0x93,0xdf,0x63,0x7f, - 0xe1,0xa0,0xd5,0xc5,0x9b,0xf3,0x1d,0x8a,0x07,0x5e,0xbd,0x1b,0x15,0xcd,0x61,0x59, - 0x2d,0x52,0x4a,0x8a,0x43,0xaf,0xfc,0x51,0xb3,0x6e,0xa7,0xe3,0x59,0x2e,0xe0,0x10, - 0xa3,0x1f,0xed,0xc3,0x93,0x13,0xb7,0x26,0xd8,0xd7,0xc3,0x45,0x43,0xa3,0xbd,0x4a, - 0x68,0x6f,0x6e,0x2c,0x05,0x56,0x07,0xea,0xe6,0x46,0x47,0xfd,0x87,0xe8,0x04,0xf1, - 0xb2,0x3a,0x6b,0x29,0x88,0x6c,0x50,0x42,0xdb,0x07,0x1e,0x82,0x1b,0xe0,0x1f,0xde, - 0xd9,0x94,0xe9,0x07,0xad,0xcb,0x35,0x8b,0x3d,0xe3,0x10,0x8d,0x0d,0xe6,0x7c,0x96, - 0x01,0x94,0x4d,0x0b,0x3a,0x83,0xbb,0x9b,0x9b,0x12,0x6d,0xa7,0x9d,0x6d,0x83,0xf3, - 0xbb,0xbe,0x8d,0xdc,0xbe,0x8d,0x7b,0xca,0x0b,0xe8,0x0f,0x2e,0x2b,0xa1,0x7b,0x52, - 0xa1,0x12,0x0f,0xfa,0xb1,0x50,0x99,0x7d,0x07,0x0f,0xf0,0x40,0x48,0x40,0x25,0x29, - 0xa9,0x32,0xd5,0x05,0xab,0x8f,0x3f,0x1b,0xb8,0xbe,0xb0,0x7d,0x86,0x98,0x87,0x3f, - 0x1b,0x74,0xdc,0x72,0x3a,0x68,0xac,0x83,0x1a,0x9e,0xc8,0x42,0x2b,0xb9,0x2d,0x44, - 0xec,0x1c,0x5c,0xea,0x1f,0x67,0xc3,0xfe,0x2b,0x11,0x63,0x36,0xef,0xa3,0xc6,0xb9, - 0xba,0xb9,0x79,0x70,0xe5,0x63,0xe5,0x00,0xcb,0xca,0xea,0x73,0x92,0x8b,0x78,0x94, - 0x21,0x4a,0x4c,0xd0,0x63,0x57,0xfb,0xfb,0x57,0x3a,0x2f,0xf9,0xc4,0x01,0xb7,0x82, - 0xa2,0xc8,0x3d,0x67,0xa7,0x2c,0x75,0x6b,0xc2,0x94,0x67,0x48,0x6e,0x86,0x11,0xab, - 0xbc,0xa2,0x9b,0x12,0x91,0xf6,0xc1,0xd5,0x24,0x62,0xb9,0x28,0x19,0x1e,0xfd,0xf9, - 0x0a,0xfc,0x3d,0x34,0x8a,0x99,0x18,0xbc,0x6e,0x87,0x72,0x58,0x11,0x98,0x32,0x6f, - 0x9b,0x99,0x5b,0x37,0x8f,0x76,0x13,0x49,0xb5,0xaa,0x59,0x7f,0x8d,0x63,0xd6,0x6d, - 0x92,0x12,0xe9,0x00,0x38,0xfc,0x01,0xe6,0x7d,0xa7,0x6b,0x43,0xf8,0xc8,0x15,0x16, - 0x48,0xba,0x5e,0x64,0x84,0x07,0x12,0xc7,0xe5,0xd9,0xae,0xb3,0x1e,0x5f,0xd2,0xb9, - 0xb2,0x1b,0x07,0xd2,0xb2,0x41,0x1b,0x90,0x29,0x99,0x53,0x02,0xb7,0x3a,0x9b,0x76, - 0x28,0x39,0x6f,0x1c,0xe8,0x53,0x6a,0x8d,0xdd,0x2a,0x71,0x33,0x01,0x08,0xa0,0xfa, - 0x78,0xcd,0x94,0xad,0x63,0xac,0xe9,0xe3,0x47,0xae,0xa7,0x73,0x84,0x94,0xad,0x68, - 0x4b,0x3c,0x45,0x16,0xe1,0x6c,0xac,0x09,0xc2,0x86,0x31,0xbb,0xf1,0xe0,0x04,0x2b, - 0x77,0x38,0x95,0x71,0xd8,0x54,0xb1,0x07,0x8e,0x1d,0x18,0x04,0x55,0x1e,0x16,0xbc, - 0x1f,0x4d,0x9c,0x36,0x05,0xcf,0xc1,0x45,0x40,0x13,0x06,0x5f,0xbb,0xd7,0x56,0x23, - 0xf8,0x16,0xa8,0x15,0xc9,0x55,0xc5,0x5b,0x16,0x5b,0xcf,0x02,0x3f,0xa8,0x2d,0x39, - 0xf5,0x40,0x0c,0xc6,0xb6,0x0f,0x0e,0xa6,0x7f,0x28,0x74,0x67,0x27,0x73,0x30,0xf2, - 0xe6,0xd9,0xbd,0x9a,0x32,0x8a,0x74,0xf3,0xd5,0x1e,0xcd,0xd5,0x90,0xa4,0x0d,0x46, - 0x25,0x7a,0x11,0x96,0xd5,0xf6,0x57,0x3c,0xd1,0xdb,0x89,0x68,0x4d,0x8d,0xeb,0x53, - 0xf3,0xc8,0x8f,0x16,0x4d,0x0a,0xba,0xe8,0x0c,0xa0,0x3e,0x2f,0xcf,0x2c,0x5e,0xb9, - 0x7f,0x07,0x35,0xa9,0x75,0x74,0x1e,0x29,0x06,0xf0,0xd0,0x7f,0xc0,0x1e,0x80,0x88, - 0xf5,0x8a,0x68,0x7f,0xb0,0xda,0x7e,0x52,0x1e,0x31,0x50,0x3a,0xfd,0xd0,0x17,0xe7, - 0x63,0x95,0xf8,0xe1,0xa7,0xf7,0x2f,0xcc,0x96,0x15,0xe5,0x8b,0x9f,0xb0,0xe0,0x56, - 0x87,0x22,0xb0,0x27,0xd2,0xe2,0xa9,0x22,0x4f,0x28,0xa8,0xe2,0x43,0x94,0x31,0xc3, - 0x4d,0x5a,0xb4,0x39,0x0b,0x23,0x2e,0x8e,0xb5,0x51,0x80,0x89,0xa9,0x08,0xe2,0x0b, - 0x11,0x60,0x27,0xe0,0x36,0xaa,0x54,0xd7,0xea,0x51,0xcd,0x2a,0x25,0xb0,0xb0,0x16, - 0x8d,0x5c,0xcd,0xa9,0x0e,0x7a,0x62,0xe1,0x7b,0x8c,0x49,0x62,0x40,0x0b,0xb8,0x5a, - 0x23,0x1c,0xd7,0xd3,0x3f,0x76,0x82,0xdd,0xe8,0x18,0x03,0xd6,0x52,0x80,0x10,0x88, - 0x54,0xaa,0xd3,0x4e,0x98,0xec,0x51,0xe5,0x66,0x18,0x7d,0xbe,0x33,0x06,0xc8,0xcd, - 0x4e,0x1c,0xfa,0x89,0x21,0x5f,0x9c,0xa7,0x30,0x2b,0xec,0x28,0x10,0x3a,0x48,0xc1, - 0x8e,0x35,0x66,0xdc,0x15,0x28,0xe3,0x48,0x16,0x7a,0x63,0x0f,0x56,0x59,0x16,0xf0, - 0x87,0x38,0x07,0x1c,0x43,0x20,0x14,0xa5,0x2b,0x05,0x81,0x26,0xc9,0xa0,0xa0,0xc5, - 0x53,0x95,0x1d,0x62,0x8e,0x94,0xd1,0x3f,0xe9,0x52,0x06,0x03,0x29,0xc6,0x53,0x71, - 0x94,0x1d,0x35,0xe1,0x05,0xd5,0x41,0x3a,0xcf,0xc4,0x4d,0xb4,0x92,0xaa,0xd6,0x15, - 0xa4,0x73,0xad,0xa5,0x25,0xdd,0xa0,0x5d,0x19,0x53,0x5c,0x8f,0x28,0x5a,0xcd,0x2c, - 0x1a,0x31,0xfa,0x4a,0xfb,0x46,0x3b,0xa3,0x42,0xb4,0x4c,0x6d,0x43,0x43,0xe4,0x7e, - 0x80,0xf7,0xb4,0xee,0x2f,0xfd,0x95,0x1d,0x99,0x59,0xad,0x64,0x0c,0x0f,0x86,0x51, - 0xc5,0xe0,0x9c,0x6a,0xdb,0x9a,0x33,0x13,0x62,0xc2,0x8a,0x27,0xd7,0x47,0x37,0x3e, - 0x18,0xc9,0x66,0xfb,0x7f,0x5c,0xb6,0x3d,0xa7,0xaa,0x09,0x23,0x9f,0x00,0xbf,0xdb, - 0xdf,0xe7,0x7f,0x01,0x1f,0x75,0xec,0x95,0x87,0xd6,0xa0,0x6e,0xd0,0x3b,0x93,0xb6, - 0x84,0xa4,0xf9,0xeb,0x03,0x87,0x64,0xe4,0x5c,0x87,0xee,0xaa,0xfd,0x60,0x1c,0x24, - 0x85,0x76,0xb5,0xce,0x0f,0x1c,0xdf,0xe9,0xaf,0xf5,0x5f,0x26,0x99,0xb4,0x76,0x1e, - 0x38,0x78,0xa0,0x52,0xd9,0x6c,0xa5,0xfd,0x8c,0x4d,0xdf,0xa9,0xfd,0x9d,0x30,0xbf, - 0xf6,0x84,0x05,0xdd,0xe0,0x80,0x25,0x29,0xb2,0xeb,0x79,0x99,0x19,0x36,0x24,0xff, - 0x5d,0x7b,0xb5,0x1e,0x6e,0xbb,0xfd,0xb7,0x2f,0x30,0xd7,0xd1,0xf1,0x7d,0xb4,0x42, - 0xbe,0xd0,0x85,0x20,0xd9,0xc3,0x09,0x48,0x8e,0x0e,0x8a,0x81,0x00,0xd3,0x88,0x1e, - 0x47,0xda,0x7e,0x81,0x47,0x5a,0x24,0xe1,0x53,0x32,0x76,0xc3,0x77,0x14,0x40,0x84, - 0x37,0xd7,0xab,0x7e,0x59,0x3a,0xf1,0x9e,0xa2,0x41,0xe0,0x98,0x0d,0xc4,0xc7,0x93, - 0x8e,0x27,0xaa,0x3f,0x9f,0x40,0x62,0x5a,0xfa,0xc5,0x0e,0x46,0xea,0x50,0xa4,0x60, - 0x7e,0x11,0xcd,0x07,0xfc,0x40,0xdc,0xdc,0xf0,0xbc,0x4d,0xc5,0x99,0x38,0x15,0xc7, - 0xae,0xa8,0x98,0x01,0x23,0x3c,0x4d,0x02,0x13,0x86,0xeb,0xf4,0xe1,0x1f,0x68,0x00, - 0xff,0x1c,0x1c,0x70,0x5f,0xdc,0x5b,0x7d,0x6a,0x03,0x70,0x06,0x5c,0x51,0x2f,0x54, - 0x24,0x8b,0x0f,0xba,0xb0,0x6d,0x3b,0x1a,0xf7,0x63,0xf4,0x49,0xb4,0x45,0xd7,0xb8, - 0xd7,0xd8,0xdb,0x25,0x8c,0x01,0xa8,0xbe,0xd4,0x23,0xd2,0x93,0x33,0x41,0xe8,0xc7, - 0x67,0xdd,0x4e,0xa7,0xea,0x53,0xc5,0xf0,0x04,0x63,0xb8,0x71,0xcb,0x11,0x07,0x22, - 0xb2,0xba,0x51,0xb1,0x01,0x91,0x34,0x29,0xfb,0x1a,0x03,0x71,0x69,0x68,0xc9,0xc6, - 0x25,0x42,0x51,0xbe,0xe2,0x33,0x9a,0x57,0x7b,0x75,0x9b,0x98,0xd7,0xa2,0x59,0xa1, - 0xd0,0x2c,0x5c,0x89,0x2e,0x74,0xdc,0x69,0x0f,0x42,0x2e,0x43,0x98,0x20,0x4a,0x4c, - 0x2d,0xf8,0x4c,0xfd,0x42,0xd2,0x62,0x27,0xa8,0xcf,0x79,0x76,0xed,0x05,0x81,0x1e, - 0xa3,0xe5,0xd6,0xf6,0xc2,0x85,0x5c,0xe2,0x92,0xf3,0x4f,0x6c,0xfb,0x78,0xd7,0xb4, - 0xa9,0x41,0x57,0xeb,0xc0,0x12,0xe2,0x2b,0x53,0xb2,0x53,0x2e,0x99,0xee,0x04,0x35, - 0xcd,0x3b,0xb2,0x90,0x80,0x4a,0x3e,0xed,0x5a,0x49,0xab,0x87,0xda,0x9a,0x12,0x18, - 0xd0,0x0c,0xdf,0x63,0x19,0xea,0x9e,0x09,0xd1,0x50,0x97,0x5c,0x57,0x74,0x81,0xe9, - 0xaf,0x01,0x22,0x7d,0x4b,0x76,0xc1,0x06,0xf8,0xe3,0xc5,0x27,0xb7,0x4c,0xad,0x6f, - 0xa1,0x6b,0x61,0xb3,0x00,0x4b,0xdc,0xb5,0x70,0x14,0x53,0x8a,0xe3,0x69,0xe8,0x74, - 0x54,0x4a,0xe8,0xb0,0x14,0x3d,0x63,0x59,0x2b,0xd6,0x83,0x53,0x26,0x34,0x25,0xae, - 0xc5,0x5a,0x70,0x4a,0xdc,0x23,0x3a,0x25,0x74,0x78,0x8a,0x82,0x53,0xd6,0xfc,0xb9, - 0xfa,0x69,0x9f,0xea,0xac,0x56,0x9b,0xbf,0xe6,0x6c,0x31,0x06,0x8b,0x75,0x61,0xff, - 0xaa,0x73,0xe9,0x09,0x54,0x59,0x26,0x7d,0x9c,0x4a,0x5d,0xae,0x02,0x32,0x38,0xb7, - 0xb5,0x29,0xed,0xae,0xcc,0x10,0x40,0x70,0x6f,0xad,0x1f,0xfe,0xd3,0x72,0x8b,0x83, - 0x43,0x6a,0x81,0x67,0x62,0x2d,0xb8,0xcc,0x89,0x5c,0x69,0x52,0xd6,0xda,0xc2,0xa5, - 0x0d,0x85,0x54,0xfa,0x72,0xa1,0x4c,0x57,0xe6,0xd7,0x18,0x28,0xea,0x10,0x18,0x33, - 0x48,0x44,0x93,0x24,0x25,0x23,0x07,0x03,0xd8,0xb4,0x61,0x8a,0xe3,0x4c,0x3a,0xa6, - 0xc9,0xc1,0x22,0x04,0x98,0xdc,0x1e,0xa0,0x87,0xe6,0x95,0xd8,0xdf,0x17,0x57,0xbe, - 0x09,0xe5,0xa3,0x40,0x28,0x67,0x2d,0x9e,0x58,0xd7,0xb4,0x60,0x95,0xfb,0x23,0xc8, - 0xff,0xf1,0x6a,0x28,0xd1,0x1e,0xd0,0x7a,0x10,0xcf,0xe0,0x78,0x43,0x78,0x1a,0xcc, - 0xef,0x64,0x9f,0x92,0x63,0x8c,0x6c,0xb0,0x99,0x9b,0x78,0xf8,0x65,0x0c,0xf4,0x2d, - 0xb0,0xe6,0x10,0x67,0xcd,0xaa,0xcd,0x70,0x72,0x25,0x20,0x58,0x87,0x94,0x22,0x22, - 0x4d,0x58,0xfc,0xbd,0x42,0x5f,0xac,0x19,0x85,0x57,0x1e,0x46,0x37,0x78,0x14,0x23, - 0xfc,0x4a,0x67,0x0c,0xde,0x78,0x46,0xe6,0x91,0x28,0x21,0x57,0xec,0xd2,0xb5,0x25, - 0x6f,0x5d,0x3c,0x85,0x57,0x5a,0x40,0xe9,0x02,0xb6,0x7f,0x37,0x76,0x2e,0x2b,0xa6, - 0x8e,0x18,0xce,0x55,0xad,0x42,0x15,0x40,0x89,0x4c,0x28,0x88,0x22,0x66,0x74,0xda, - 0x80,0x9c,0x04,0xa3,0xa5,0x59,0xd9,0x3d,0xab,0x0c,0xa8,0xac,0x89,0xd2,0x79,0x17, - 0x6d,0x5b,0xa3,0x99,0xcf,0x51,0x1a,0x8a,0xc9,0xc0,0x62,0x85,0xff,0x0e,0x56,0x5b, - 0x9a,0x4c,0xe8,0xe7,0x7a,0xb9,0x4c,0x95,0x13,0x17,0x9c,0x3f,0x06,0x1d,0xcb,0x5b, - 0xd9,0x80,0xb8,0xb8,0xfc,0x16,0x23,0x52,0x60,0x5f,0x02,0x4c,0xfe,0x1a,0xff,0x7f, - 0x74,0x32,0x22,0x17,0x98,0x1c,0xfc,0xd3,0x75,0x69,0x86,0xe2,0x3b,0x40,0x0c,0x4d, - 0xb3,0xb6,0x2e,0x35,0x5c,0xe3,0x0f,0x8d,0x1b,0xc5,0xc6,0x18,0xbe,0x5b,0x4d,0x55, - 0xb4,0x03,0xc3,0x55,0x8b,0xbf,0x4d,0x87,0xc0,0xfb,0xca,0x40,0x02,0x1e,0x46,0x45, - 0x52,0xad,0x0b,0x8b,0x15,0xf6,0xf4,0x84,0x4d,0x90,0xeb,0x0a,0x1e,0xfa,0xe9,0x83, - 0x04,0x11,0x1b,0x44,0xbb,0x5b,0x6b,0x22,0x64,0x5b,0xcc,0x04,0xf3,0x51,0x1d,0x23, - 0xda,0x22,0x2a,0xaf,0xb6,0x7a,0x8b,0x3a,0x34,0xb0,0x1d,0x08,0x43,0x97,0x46,0x3b, - 0x56,0x23,0x6d,0xa3,0xe6,0x8d,0x21,0x57,0x6e,0x33,0xd2,0x82,0x7d,0xb5,0x57,0x5b, - 0x93,0x9a,0xe7,0xf6,0xfb,0x39,0x66,0x67,0x8d,0x34,0xc1,0xea,0x0d,0x2a,0xb8,0x6c, - 0xfe,0xf6,0x5f,0xff,0x13,0x34,0xff,0x6f,0xff,0xf5,0xbf,0x5c,0xf1,0x37,0x4b,0x54, - 0x52,0xfe,0x4a,0xd7,0xfc,0x9a,0xe3,0x04,0x34,0x24,0x9b,0x69,0x29,0x93,0x8a,0xc2, - 0x58,0xa7,0x95,0x8f,0x01,0x80,0x75,0x0e,0xbc,0x19,0x46,0x79,0xc5,0xd1,0x58,0xe8, - 0xcd,0x84,0xb3,0x51,0xd7,0xa6,0x63,0x29,0x1b,0xcf,0xa1,0x39,0x74,0x01,0xc6,0x4c, - 0x07,0xc5,0x20,0x8a,0x6e,0x4c,0xbf,0xa0,0x18,0xc4,0x03,0xf4,0x4c,0xd1,0x5c,0x2d, - 0x4a,0x65,0x92,0xd7,0x6d,0x45,0x9b,0x08,0x9e,0xd0,0x91,0x45,0x48,0xa6,0x08,0xcb, - 0x9a,0x54,0x0c,0xcd,0xe2,0xee,0x1a,0xdb,0xa7,0xaa,0x3e,0xb4,0xf9,0x60,0xb8,0x5b, - 0x8b,0xed,0x50,0xfb,0x98,0x0c,0x80,0x95,0x29,0xe7,0x83,0x04,0x77,0x29,0x2a,0x8b, - 0xcc,0xae,0xee,0x00,0xa5,0x02,0x80,0x58,0xec,0x0a,0x81,0xd2,0x61,0x35,0x4d,0x65, - 0x5a,0xbd,0x68,0x5d,0xa2,0x97,0x08,0xb9,0xe4,0xb8,0x73,0x8c,0xd8,0xb3,0x8e,0x73, - 0xd0,0xd1,0xf1,0x22,0xd5,0x1a,0xa5,0x4a,0x5e,0x38,0xb6,0x4e,0x29,0xa1,0xaf,0x47, - 0xd5,0xcc,0x61,0x86,0xb7,0x25,0x1b,0xea,0x8c,0x6e,0x93,0x44,0xab,0x0b,0x4a,0xc3, - 0x64,0x2d,0xf4,0x89,0x85,0x5f,0xd6,0x93,0xe9,0x68,0xaf,0xbe,0x57,0xa1,0x79,0xd4, - 0xa1,0xe4,0x53,0xb9,0xc7,0xa1,0x99,0xf1,0xb1,0xb6,0x78,0x16,0xa7,0xde,0xd7,0x78, - 0xda,0xa6,0x73,0x38,0x4f,0xdb,0xd3,0x62,0x16,0x9f,0xed,0xfd,0x3f,0x52,0x93,0x44, - 0x7c,0x9c,0x86,0x00,0x00, + 0x1f,0x8b,0x08,0x00,0x00,0x00,0x00,0x00,0x02,0xff,0xcd,0x7d,0x5b,0x77,0xdb,0x48, + 0x92,0xe6,0xbb,0x7e,0x45,0x9a,0xee,0x12,0x80,0x12,0x08,0x5e,0x74,0xb1,0x4c,0x0a, + 0xd6,0xc8,0xb2,0xd4,0xed,0xb1,0x5d,0xa5,0xb5,0xec,0xa9,0xde,0xf6,0x7a,0x7a,0x40, + 0x22,0x49,0xa2,0x05,0x02,0x68,0x24,0x48,0x8a,0x4d,0xf1,0x9c,0x7e,0x9a,0xe7,0x39, + 0x7b,0xe6,0x37,0xec,0xfb,0x3e,0xed,0xfb,0xfc,0x94,0xfa,0x25,0x1b,0x11,0x99,0x89, + 0x1b,0x49,0x59,0xd5,0x3d,0x35,0x67,0xce,0xa9,0xa2,0x80,0x44,0x5e,0x22,0x23,0xe3, + 0xf2,0x45,0x64,0x02,0x3e,0x7b,0xe6,0xc7,0xc3,0x6c,0x99,0x70,0x36,0xc9,0xa6,0xe1, + 0xab,0xbd,0x33,0xfc,0xc3,0x42,0x2f,0x1a,0xbb,0x0d,0x1e,0x35,0xb0,0x80,0x7b,0x3e, + 0xfc,0x99,0xf2,0xcc,0x63,0xc3,0x89,0x97,0x0a,0x9e,0xb9,0x8d,0x59,0x36,0x6a,0x9e, + 0xe2,0x53,0x31,0x4c,0x83,0x24,0x63,0x22,0x1d,0xba,0x8d,0x56,0x32,0x6a,0x0e,0xe3, + 0x48,0xc4,0x21,0x77,0xfe,0x24,0xce,0x27,0x2e,0x1f,0x8d,0x46,0xfe,0x71,0xfb,0xb8, + 0xf1,0xea,0xac,0x25,0x2b,0xbe,0x52,0x0d,0x5e,0x05,0x23,0xf3,0xd9,0x22,0x88,0xfc, + 0x78,0xe1,0xdc,0x5c,0x5b,0xe6,0x68,0x16,0x0d,0xb3,0x20,0x8e,0x4c,0x6b,0x35,0xf7, + 0x52,0xf6,0x93,0x2b,0x9f,0xd9,0x9f,0x5d,0xa3,0xe5,0x25,0x41,0x4b,0x64,0x5e,0x36, + 0x13,0x86,0xfd,0xde,0xfd,0xf2,0xd5,0xfe,0xe0,0x76,0xf9,0x4b,0xfb,0xd6,0xfe,0x83, + 0x7d,0xe3,0xfe,0x04,0xcd,0xdd,0x15,0x3e,0xe6,0x3d,0x23,0x0c,0xe6,0xdc,0xb0,0xfd, + 0x20,0xcd,0x96,0xbd,0x67,0x1d,0x7b,0xde,0x8b,0x66,0x61,0x68,0x8f,0x79,0xd6,0xfb, + 0xad,0x1d,0x44,0xa3,0x30,0x18,0x4f,0xb2,0xde,0x0f,0xb3,0xe9,0x80,0xa7,0xf6,0x24, + 0x0e,0xfd,0xde,0x8f,0x83,0x3f,0xf1,0x61,0x66,0x0b,0x6f,0xd9,0xcb,0x29,0xc8,0xec, + 0x3b,0x9b,0x5b,0x2b,0xbe,0xbf,0x6f,0x72,0x27,0xe3,0xf7,0xd9,0x65,0x1c,0x65,0x3c, + 0xca,0xdc,0xcc,0x5a,0xdb,0x83,0x99,0x28,0x55,0x1d,0xd8,0x89,0xb5,0xd2,0x77,0x6c, + 0x04,0xc4,0x0f,0x1c,0x3f,0x10,0xde,0x20,0xe4,0xbe,0xfb,0xac,0xb3,0x2e,0xdf,0xb5, + 0xfb,0x89,0x93,0x4d,0x78,0x64,0x8e,0xec,0x91,0xd5,0x4f,0x79,0x36,0x4b,0x23,0x96, + 0xac,0xed,0x24,0x0e,0xc3,0xa2,0x47,0x61,0x4f,0xed,0xa1,0x1d,0x4b,0x2e,0x64,0xf6, + 0xc8,0x1e,0xbb,0x9d,0x7e,0x3e,0xc2,0x47,0x18,0x61,0xbc,0xbf,0xff,0x6c,0x04,0xb4, + 0x8d,0xdc,0x8e,0x3d,0x0c,0xb9,0x97,0x7e,0x0a,0xa6,0x3c,0x9e,0x65,0x66,0x66,0xd9, + 0xa6,0x70,0x86,0x5e,0x18,0x9e,0x0b,0xd3,0xea,0xfd,0xd6,0x14,0x96,0xa5,0x46,0xd4, + 0xbd,0xfb,0xd6,0x6a,0x68,0xfa,0x36,0x72,0x05,0xe6,0x92,0x17,0x73,0x2c,0x26,0x56, + 0x71,0x6b,0xad,0xda,0xbc,0xb3,0xdf,0x59,0xd6,0x3a,0x1f,0xf9,0x1d,0x8c,0x3c,0x72, + 0xdb,0xfd,0xda,0x88,0x7d,0xa0,0xc6,0xcc,0x5c,0x10,0x09,0x5d,0xf6,0xd1,0x9e,0x42, + 0xbb,0x78,0x7f,0x3f,0x76,0x46,0x41,0x2a,0x32,0xd7,0x05,0x46,0x9c,0x43,0xf3,0x1e, + 0x10,0xaf,0xa6,0x0d,0xcb,0x15,0x27,0xbd,0xd2,0x92,0x8f,0x37,0x7b,0x5e,0xdb,0xd0, + 0x69,0x51,0x27,0xb2,0x56,0x53,0x37,0xea,0x8f,0x1e,0x1e,0xa0,0xab,0xb5,0x1d,0xc5, + 0x8b,0xde,0xc7,0x35,0xd4,0x21,0xb1,0x28,0xaa,0x0d,0x61,0x26,0xfb,0xfb,0xef,0x9d, + 0x64,0x26,0x26,0x70,0xd3,0xbf,0x3d,0x87,0xdb,0xa1,0x79,0x6b,0xf5,0xfe,0x70,0xfe, + 0x07,0x07,0x5a,0x01,0x1d,0x7f,0x70,0x6f,0x1c,0x64,0xba,0xf9,0xd9,0xfe,0x60,0xbf, + 0x81,0xce,0x16,0x5e,0x36,0x9c,0xdc,0xd6,0x7a,0x9a,0x5a,0xab,0x1b,0x47,0x76,0x0f, + 0x74,0xff,0xc1,0x01,0x6a,0xcc,0x0f,0xee,0x07,0x2f,0x9b,0x38,0xd3,0x00,0x1e,0xdb, + 0x1f,0x2c,0x6a,0x1a,0x64,0xd7,0x71,0xfa,0x86,0xcf,0x83,0x21,0x2f,0x1a,0xc3,0xfa, + 0xc5,0x6e,0xfc,0xf0,0xb0,0x5a,0xeb,0x85,0x8e,0xf8,0x82,0xdd,0xa4,0xf1,0x34,0x10, + 0xbc,0x58,0x8e,0xa5,0x1d,0xc9,0x85,0x9e,0x14,0x24,0x75,0xf9,0x61,0xb1,0x30,0xc2, + 0x5a,0x09,0x58,0x6e,0x33,0x76,0x06,0xb3,0x20,0xf4,0xf7,0xf7,0x85,0xbc,0x70,0x5d, + 0x55,0x62,0x01,0xfb,0x27,0x0e,0xb2,0xd3,0xb4,0xec,0x25,0xae,0xf8,0xda,0xea,0x97, + 0x56,0xa3,0xc4,0x63,0x5d,0xad,0x1f,0x99,0x57,0x69,0x1a,0xa7,0xa6,0x91,0xc9,0x4a, + 0x06,0x4e,0x24,0x76,0xd4,0xdd,0xc3,0xc3,0x4b,0x7e,0x04,0xbd,0xac,0xd7,0x85,0xd8, + 0xfd,0xd6,0x9c,0xa1,0x48,0xaa,0xa9,0x8c,0x38,0xb0,0x8b,0x4a,0x6a,0xd2,0x95,0x5a, + 0x2b,0xd4,0xea,0xd4,0x89,0xef,0xac,0x6c,0x92,0xc6,0x0b,0x26,0x07,0x4a,0x15,0x1f, + 0x73,0xa9,0x27,0xe9,0x40,0xcd,0x3a,0x4f,0xe9,0x0f,0x2c,0x4a,0x0a,0x06,0x03,0xc9, + 0x5c,0x97,0x64,0xee,0x0d,0xce,0x1f,0x7a,0x84,0xdf,0x5b,0xd0,0xf3,0x64,0x24,0xd7, + 0xc8,0x15,0xfd,0xf7,0xce,0x28,0x4e,0xaf,0x3c,0x20,0x23,0x1f,0x7c,0x04,0xf2,0x09, + 0x35,0x91,0xf0,0xb5,0x65,0x5a,0xb9,0xbd,0x51,0x86,0x2b,0xf2,0xa6,0xdc,0x6d,0xcc, + 0x03,0xbe,0x48,0xe2,0x34,0x6b,0xb0,0xa1,0xd2,0xe9,0xc6,0x22,0xf0,0xb3,0x89,0xeb, + 0xd3,0x02,0x36,0xe9,0x06,0x2c,0x45,0x90,0x05,0x5e,0xd8,0x14,0xa0,0x4b,0xdc,0xed, + 0xa0,0x95,0xcb,0x82,0x2c,0xe4,0xaf,0x6e,0xbc,0x2c,0xe3,0x29,0xd8,0x91,0x78,0x71, + 0xd6,0x92,0x45,0x7b,0x67,0x61,0x10,0xdd,0xb1,0x94,0x87,0x6e,0x23,0x80,0x4e,0x1b, + 0x6c,0x92,0xf2,0x91,0xdb,0xf0,0xbd,0xcc,0xeb,0x05,0x53,0x6f,0xcc,0x5b,0x62,0x3e, + 0x3e,0xb8,0x9f,0x86,0xf6,0x77,0x87,0x97,0x70,0xc9,0xe0,0x32,0x12,0xae,0x31,0xc9, + 0xb2,0xa4,0xd7,0x6a,0x2d,0x16,0x0b,0x67,0x71,0xe8,0xc4,0xe9,0xb8,0xd5,0x6d,0xb7, + 0xdb,0x58,0xd9,0x60,0x48,0xe6,0xeb,0xf8,0xde,0x35,0xda,0xac,0xcd,0x3a,0x27,0xf0, + 0x9f,0xf1,0xdd,0xe1,0x15,0xb4,0x4f,0xc1,0x58,0x31,0x49,0xb1,0x01,0x85,0x6c,0xc2, + 0xd1,0xa2,0xc9,0xeb,0x14,0xea,0x1f,0x1a,0x6c,0x14,0x84,0xa1,0x6b,0x7c,0xd7,0x3d, + 0xec,0x9c,0x74,0x8e,0x3b,0x5d,0xa3,0x55,0x6a,0x09,0x35,0x8e,0x0d,0xb6,0xa4,0x5f, + 0xd5,0x4b,0xa9,0x93,0x93,0x52,0xe3,0xeb,0xeb,0xe3,0xcb,0xee,0x95,0x6e,0x8c,0x54, + 0xc1,0x15,0x99,0xfb,0x6c,0x89,0xb3,0x6e,0x7d,0xcf,0x2e,0xa5,0xa1,0x67,0x03,0x4f, + 0xf0,0x1e,0x03,0x49,0x60,0x02,0x78,0xcc,0x06,0x61,0x3c,0xbc,0x63,0x71,0xc2,0x23, + 0xc1,0xf8,0x9c,0xa7,0x4b,0x60,0x75,0xca,0x59,0x02,0x9c,0x70,0xd8,0x7b,0x1c,0x89, + 0x05,0x11,0xeb,0xa5,0x71,0x9c,0x31,0x13,0x5b,0x0d,0x41,0x52,0xa6,0xdc,0x10,0x7b, + 0x8c,0xc1,0x13,0x34,0xc7,0xdc,0x67,0x64,0xa9,0xa1,0x51,0xc8,0x81,0xe3,0x0c,0x4d, + 0xb5,0xc0,0x21,0x84,0x1a,0x64,0xee,0x85,0x33,0x2e,0x7a,0xec,0x8e,0xf3,0x84,0x86, + 0xce,0x16,0x31,0x76,0x2b,0x32,0x9e,0x58,0x36,0xf6,0xe4,0x7b,0xe9,0x1d,0x9b,0x45, + 0x3e,0x4f,0xc9,0xaf,0x7d,0xc1,0xf5,0x68,0x42,0x4d,0x90,0x02,0x7c,0xf4,0xd5,0x61, + 0x3f,0xc4,0x6c,0xc1,0x07,0x6c,0x04,0x82,0x20,0x24,0xfd,0x93,0x38,0x13,0x09,0x90, + 0x35,0xf1,0x04,0x8b,0xb0,0x3f,0x5c,0x6c,0x9e,0x39,0xec,0xfb,0xd6,0x1e,0x11,0xbc, + 0x1a,0xc6,0x61,0x9c,0x82,0x5c,0x60,0x3f,0x3d,0xa2,0xb1,0xdf,0x6c,0x0e,0x53,0xee, + 0x4d,0x7b,0xcf,0xaf,0x8f,0xae,0xae,0xaf,0x4e,0xe0,0x3e,0xf1,0x22,0x1e,0xc2,0xfd, + 0xf5,0xf5,0xe5,0xf5,0x05,0xdc,0x83,0x7c,0xf4,0x9e,0x77,0x2e,0x3a,0xa7,0x9d,0x23, + 0xb8,0x9b,0xce,0x60,0x7e,0xbd,0xe7,0x27,0xdd,0xe3,0xcb,0xe3,0x0e,0xdc,0x8f,0x3c, + 0x18,0xa7,0xf7,0xfc,0xc5,0x9b,0x17,0x27,0x27,0xa7,0x70,0x9f,0xce,0x42,0xde,0x7b, + 0xfe,0xe6,0xcd,0x9b,0xe3,0xcb,0x13,0x75,0xdb,0x14,0xf1,0x08,0xaa,0x5c,0x9d,0x5e, + 0x75,0xde,0x60,0x17,0xe3,0x49,0x2c,0xf0,0xfe,0xf0,0xcd,0xe5,0xe5,0x15,0xdc,0x87, + 0xd8,0xa1,0x5c,0x31,0xb8,0x1b,0x78,0x70,0xf7,0xfa,0xf4,0xf0,0xb2,0x73,0x09,0x77, + 0x31,0x8c,0xdd,0x7d,0xfd,0xe2,0xe2,0xe8,0x35,0xdc,0x2c,0xbc,0x34,0xea,0x3d,0x3f, + 0x3d,0x3e,0x69,0xb7,0xaf,0xfa,0x7b,0xcd,0xa6,0xf0,0x22,0xd1,0x13,0x4b,0xe0,0xda, + 0xb4,0x39,0x0b,0xec,0xa6,0x97,0x24,0x38,0x1a,0x15,0xd8,0xc6,0x2d,0x1f,0xc7,0x9c, + 0x7d,0x7e,0x6b,0xd8,0x1f,0xe3,0x41,0x9c,0xc5,0x36,0x56,0x6f,0x0a,0x9e,0x06,0x23, + 0x9c,0x47,0x1c,0xc5,0xbd,0x59,0x40,0x7f,0x45,0xe2,0x0d,0xb9,0x6d,0x5c,0x7a,0xa0, + 0x31,0x7e,0xe0,0xb1,0x0f,0x50,0x66,0x40,0x07,0xd7,0xea,0xea,0x03,0x8f,0xc2,0xd8, + 0x96,0x02,0xe3,0x09,0xdb,0x90,0xfd,0xa9,0x87,0x79,0x07,0xeb,0xbd,0xad,0x2b,0x55, + 0x65,0x3a,0x16,0x15,0x3c,0x6f,0x5f,0xb6,0x5f,0xb7,0x5f,0x16,0x3c,0xef,0x1c,0x76, + 0x3a,0x9d,0xb6,0xe6,0xf9,0xd5,0x9b,0xab,0x17,0x6f,0x5e,0x17,0x3c,0xbf,0x38,0x7c, + 0xf9,0xfa,0xf4,0xa2,0xc4,0xf3,0x8b,0x17,0x87,0x27,0x47,0x39,0xcf,0xbb,0xaf,0xbb, + 0x2f,0xba,0x9d,0x2a,0xcf,0x3b,0xd7,0x9d,0xcb,0xce,0x69,0xc1,0xf3,0xee,0x45,0xf7, + 0xa4,0xdb,0xd6,0x5c,0xbe,0xbe,0x7e,0x71,0x71,0x7c,0xac,0xb8,0x7c,0xfc,0xe2,0xf5, + 0xe9,0x8b,0xeb,0x9c,0xcb,0x6f,0x5e,0x5e,0xb4,0x0f,0xaf,0xd7,0x7b,0xdf,0xaf,0x06, + 0xf1,0x7d,0x53,0x04,0x7f,0x09,0xa2,0x71,0x6f,0x10,0xa7,0x20,0x91,0x4d,0x28,0x59, + 0x7f,0x99,0x04,0xbe,0xcf,0xa3,0xaf,0x2b,0x40,0x08,0x49,0x08,0xf8,0x23,0x8a,0x23, + 0xfe,0x2c,0x98,0xa2,0x79,0xf2,0xa2,0x6c,0xbd,0x37,0x88,0xfd,0xe5,0x6a,0xea,0xa5, + 0xe3,0x20,0xea,0xb5,0xfb,0x03,0x6f,0x78,0x37,0x4e,0x63,0x90,0xe8,0x1e,0x38,0x0c, + 0x53,0x71,0xc0,0xea,0x13,0x73,0x54,0x11,0x4c,0xda,0xea,0xa3,0x3c,0xf7,0x3a,0xc7, + 0xc9,0x7d,0xab,0xe3,0x1c,0x33,0xf9,0x00,0x17,0xce,0xea,0x37,0x41,0xdc,0xef,0x82, + 0xac,0x89,0x35,0x9a,0x62,0x0a,0xf2,0x3c,0x41,0x9a,0x60,0x30,0x30,0x76,0x01,0x68, + 0xb1,0xbf,0xde,0xf3,0x24,0xb7,0x7b,0x41,0x34,0x81,0x75,0xce,0xd6,0xbd,0x51,0x3c, + 0x9c,0x89,0xe6,0x3c,0x10,0x01,0xa0,0x98,0x15,0xf8,0x07,0x30,0x77,0xbc,0xd7,0x4d, + 0xee,0x19,0xac,0x65,0xe0,0xab,0x01,0x40,0x02,0xad,0xbe,0x7a,0xd8,0x8c,0x47,0x23, + 0x74,0xdd,0x50,0x67,0xbd,0xe7,0x2c,0x52,0x2f,0x81,0x59,0xdc,0x4b,0xd3,0xda,0x7b, + 0xd1,0x6d,0x27,0xf7,0x7d,0x3d,0x2b,0xe6,0xcd,0xb2,0xb8,0x9f,0x78,0xbe,0x8f,0x84, + 0xe0,0x23,0x30,0x75,0xf0,0x73,0x74,0x8a,0x6d,0x27,0xdd,0x7c,0xfa,0x60,0x06,0xa1, + 0xbc,0x2f,0x29,0x0f,0xfe,0xc2,0x7b,0x9d,0x43,0x7d,0xbb,0x20,0xeb,0xd5,0x03,0x91, + 0xae,0xf0,0x82,0x96,0xdc,0x02,0x02,0x86,0x5e,0xea,0xaf,0x36,0xb8,0x47,0xf2,0x62, + 0xf5,0xe5,0x7a,0xf4,0x3a,0xb5,0xe9,0xa0,0x00,0xe8,0x87,0xcd,0x14,0x44,0x1a,0x80, + 0x41,0x07,0x09,0xd7,0xa4,0x22,0x95,0xd0,0xf7,0x20,0x8b,0xf2,0xe5,0x0b,0x22,0x9a, + 0xfc,0x28,0xe4,0xf7,0x7d,0x60,0xe7,0x38,0x6a,0x06,0xa0,0x45,0xa2,0x37,0xe4,0x68, + 0x46,0xfa,0x7f,0x9a,0x89,0x2c,0x18,0x2d,0x9b,0xca,0xef,0xe8,0xe2,0xb1,0x97,0xf4, + 0x70,0x66,0x00,0x29,0x9a,0xd2,0x0e,0xf7,0x8e,0xca,0x03,0xb5,0x89,0x21,0xbf,0x84, + 0xce,0x53,0xac,0xbe,0x63,0xba,0xdb,0x85,0xe5,0xb8,0x0d,0xa3,0x1c,0x41,0xd7,0x65, + 0x61,0x41,0xb7,0xdc,0xf4,0x39,0xd8,0x6e,0x0f,0xdd,0x2b,0x49,0x67,0x7f,0x38,0x4b, + 0x05,0x74,0x90,0xc4,0x64,0x1a,0xfb,0x59,0x0a,0x55,0x03,0x7a,0xac,0x68,0xa0,0x01, + 0x98,0xd3,0x39,0x16,0x76,0x41,0x83,0xbc,0x2f,0x1e,0xad,0xf7,0xfe,0x61,0xca,0xc1, + 0x4c,0x98,0x93,0x18,0xfc,0x43,0x8f,0x7e,0xad,0x15,0x32,0x53,0x5e,0xaf,0xca,0x9d, + 0x95,0xa8,0x5d,0x63,0x15,0x27,0x49,0x03,0x5d,0xad,0x3e,0x4b,0x92,0xc2,0x2d,0x8d, + 0xa9,0x5c,0x16,0x3c,0x87,0x80,0x64,0x3d,0x02,0xd1,0x07,0xbf,0xe0,0xa9,0x7e,0x36, + 0xc6,0x91,0x2b,0xdb,0xd3,0xd0,0x7d,0x15,0x83,0x75,0x0a,0x20,0xa2,0x70,0x8e,0x8e, + 0x35,0x0b,0x7c,0x3e,0xf2,0x66,0x61,0xb6,0x6d,0x34,0x5a,0x14,0xd9,0x05,0x12,0xbb, + 0x49,0x26,0x71,0x7e,0xc7,0x1c,0x2b,0x4b,0x24,0x55,0x5c,0x75,0x15,0x0c,0x57,0x52, + 0x85,0xaa,0xf2,0x01,0x4f,0xa7,0x62,0xbc,0x92,0x5a,0xdf,0xcd,0x17,0x11,0x8d,0x6a, + 0xb5,0x33,0xb2,0x79,0xc0,0x43,0x08,0xd1,0xe2,0xbb,0xca,0x9c,0xe3,0x3b,0x59,0xcc, + 0xd3,0x2a,0x2f,0xc0,0xc4,0x91,0x02,0x4d,0x82,0xa4,0x2e,0xe5,0xe4,0xe4,0x4b,0x42, + 0x7a,0xfa,0xcb,0x64,0xf4,0xe5,0x4b,0xa5,0xbd,0xbd,0x0e,0x54,0x6f,0x91,0xe2,0xef, + 0xa2,0x5b,0x2a,0x72,0x7f,0x31,0x01,0x75,0x6a,0x92,0x9f,0x00,0x59,0x44,0xab,0x02, + 0xa4,0x45,0x71,0x06,0xa8,0x2d,0x27,0xae,0x4a,0x15,0x71,0x03,0xe5,0xfa,0x49,0x94, + 0x85,0x1c,0xcc,0xfd,0xe1,0x36,0xb3,0xf6,0x74,0xf5,0x2a,0x99,0x27,0x1c,0x76,0x9b, + 0x0a,0xad,0x15,0xcd,0x6c,0xb0,0x2a,0x5b,0x2f,0x50,0xc1,0xad,0x33,0x94,0x92,0x9a, + 0x4f,0xf0,0xa9,0xc6,0x85,0x3a,0x69,0x0e,0x78,0xb6,0xe0,0x3c,0x22,0x1b,0x43,0x56, + 0x44,0x5a,0xd3,0x26,0xc6,0x5e,0x24,0x44,0x6a,0x66,0x78,0xbf,0x9d,0x37,0x4f,0x96, + 0xaa,0x3d,0xad,0x52,0x4f,0x35,0x87,0x75,0x7b,0xb7,0x65,0xc5,0xb7,0xb2,0x0f,0x81, + 0xe7,0x47,0x2f,0x1a,0x03,0xe2,0x04,0xb6,0xb3,0x80,0x50,0x21,0xc1,0x56,0x00,0x8d, + 0x89,0x97,0x66,0x36,0x03,0xdf,0xc3,0x06,0x4b,0x2a,0x33,0x2d,0x04,0x85,0x04,0x4d, + 0x29,0x18,0x20,0x24,0x17,0x44,0xc9,0x2c,0xfb,0x82,0x19,0x0f,0x37,0xc5,0x9e,0xbe, + 0xae,0xb4,0x63,0x04,0xf8,0x03,0x21,0xa8,0x17,0xd1,0x02,0x80,0xb9,0xab,0xdf,0x57, + 0xe5,0x4c,0x6a,0x63,0xa7,0xdd,0xfe,0xae,0xaf,0x66,0xd2,0x3d,0x2d,0xf9,0xb6,0xb2, + 0x94,0x20,0x2b,0xbc,0xb4,0x39,0x46,0x19,0x82,0xf9,0x9b,0x2f,0xdb,0x3e,0x1f,0xdb, + 0x85,0xd6,0x2b,0xf6,0x26,0xf6,0x71,0xfb,0x3b,0x4b,0x95,0x13,0xe0,0xb0,0x58,0xdb, + 0x62,0x92,0x65,0x2d,0x1c,0x89,0xa1,0x99,0x8e,0xe2,0x66,0xca,0x81,0xb2,0x6c,0xc3, + 0x1e,0xc7,0xb3,0xe1,0xa4,0xe9,0x51,0x3c,0xd4,0x03,0x99,0x6c,0x2e,0xd7,0x9b,0xb3, + 0xed,0xf5,0xf4,0x74,0x05,0x2c,0x37,0x2e,0xff,0x64,0x36,0x1d,0xec,0xe4,0x81,0x9c, + 0x25,0x79,0x6d,0x3d,0xcb,0x92,0xe8,0x28,0xa5,0x00,0xb2,0xfb,0xdf,0x70,0xb1,0x75, + 0xc4,0x40,0xc6,0xee,0x31,0x07,0xb2,0x9d,0xf4,0x69,0xfc,0x97,0x26,0xdd,0x28,0xb2, + 0xb7,0x62,0xab,0x5f,0x95,0xe8,0x6d,0x74,0x21,0xcb,0xe7,0x7c,0x07,0x67,0x77,0x38, + 0xa4,0xf5,0x23,0xdd,0x6c,0xce,0x72,0x7b,0x17,0xda,0x91,0x2a,0x01,0xe8,0x0d,0x63, + 0xcc,0xce,0x41,0xcc,0xbc,0x21,0xe2,0x25,0x5d,0x5b,0x3f,0x55,0x24,0x14,0x17,0x4f, + 0x4a,0x5c,0x3c,0xd9,0xd1,0xbc,0x4e,0xf0,0x8e,0xa6,0xa4,0xbe,0x3f,0xff,0xfb,0x5f, + 0xe1,0x3f,0xf6,0x3b,0x08,0xfa,0xf4,0x35,0x68,0xa5,0x13,0x0e,0xc2,0xd5,0x23,0xf8, + 0xee,0x78,0x2b,0xbe,0xcb,0xb5,0x0d,0x9c,0xc1,0xc4,0xff,0xa6,0x9d,0x44,0x4b,0x88, + 0xd0,0x12,0xea,0x32,0x39,0x20,0x54,0xec,0x75,0xa8,0x31,0x23,0x3f,0x8a,0x76,0x49, + 0x12,0xdf,0xee,0x23,0x38,0xc0,0x50,0xbf,0x27,0x91,0xba,0x34,0x48,0x79,0x21,0x0f, + 0xc3,0x20,0x11,0x81,0xd8,0xe5,0x99,0x16,0xa5,0xd9,0x90,0x61,0xa8,0xa3,0x55,0x0a, + 0x6d,0x53,0x6a,0x88,0xe2,0xdb,0x74,0xda,0x5d,0x3e,0xed,0x93,0xd5,0x54,0x3c,0xeb, + 0x80,0x1e,0xe8,0x19,0xa2,0x28,0xb6,0x51,0xfd,0xcb,0xa6,0xf3,0x10,0x4a,0x73,0x32, + 0x9b,0x38,0x34,0xa0,0xf9,0xe5,0x02,0xa0,0x3b,0x47,0x1a,0xc4,0x6c,0x50,0x65,0x09, + 0xfe,0xc8,0x6a,0xf8,0xb3,0x8b,0x43,0x88,0xc0,0x09,0xed,0x96,0x46,0x22,0xfd,0xf9, + 0xb6,0x3b,0xc8,0x51,0xf7,0x20,0xbc,0x5b,0x95,0xdc,0x0d,0xb9,0x1f,0xe5,0x98,0x8b, + 0x82,0x6f,0xf8,0x1f,0x0a,0xc1,0xb0,0xb3,0x14,0x98,0xf9,0x94,0xa5,0x45,0xd2,0xd6, + 0x58,0xbb,0xb6,0xb8,0x82,0x8f,0xb7,0xfa,0x25,0x6c,0xd3,0x2d,0x81,0xa9,0xee,0x2f, + 0x44,0x31,0xdb,0xa0,0x40,0x8e,0xd9,0x60,0x50,0x36,0x98,0x65,0x59,0x1c,0xad,0xea, + 0xce,0xae,0x10,0xb2,0x17,0x27,0x3b,0xb1,0x7e,0xbb,0x36,0xda,0x8b,0xea,0x68,0x12, + 0x8f,0x6f,0x2a,0xc4,0x6e,0x44,0xff,0x08,0x78,0x7f,0x04,0xaa,0x97,0xe6,0xb1,0x0b, + 0x2e,0x97,0xeb,0x38,0x30,0xdd,0x27,0x47,0x1f,0x64,0xbc,0x27,0x9e,0x0f,0xda,0x84, + 0x11,0x1e,0xd0,0x9c,0x93,0x4c,0xfc,0xb6,0x83,0x08,0x9d,0x79,0x9b,0x35,0x0b,0x99, + 0x93,0x76,0xef,0x79,0xb2,0x48,0x01,0x48,0xf1,0x32,0x6b,0x3b,0x25,0xef,0x7b,0xa2, + 0xb5,0x05,0x88,0x9b,0x7b,0xe1,0xea,0x17,0x48,0xee,0x5d,0x11,0xd2,0x8d,0xd3,0xc0, + 0xef,0xe3,0x4f,0x13,0x24,0x0d,0x4a,0x40,0xcd,0xa1,0xc9,0x6c,0x1a,0x09,0xd2,0x68, + 0xd6,0x19,0xa5,0xdb,0x44,0x51,0xd6,0x69,0x92,0x44,0xb6,0x6b,0xb0,0x8b,0x28,0xba, + 0x8b,0x58,0xe8,0x0d,0xb8,0x22,0xeb,0x04,0x17,0xeb,0x29,0xa4,0x3d,0x9f,0x25,0xfe, + 0x86,0x52,0x3d,0xa6,0xdf,0x48,0x01,0x4a,0x01,0xf6,0x5e,0x21,0x14,0x53,0x76,0xa8, + 0x06,0x6b,0xec,0x92,0x81,0x05,0x8a,0x72,0x65,0xc1,0x12,0xb9,0xd0,0x00,0x51,0xeb, + 0x10,0x8c,0xf2,0x69,0xd4,0x70,0x8f,0xe2,0x88,0xd1,0x08,0x6f,0x18,0x5a,0x3b,0xbb, + 0x52,0x30,0x62,0xce,0xbc,0x08,0x9b,0x8e,0x51,0x44,0xc0,0xb1,0xd8,0xf4,0xcb,0xf2, + 0xb8,0x9e,0x66,0xd1,0x25,0x83,0xac,0x1e,0x1c,0x7c,0xbf,0xfa,0x16,0x28,0x25,0x1f, + 0x72,0xe5,0x93,0xe4,0x92,0xe3,0xe0,0x7e,0x54,0x36,0xb5,0xdd,0x6d,0xa6,0xb6,0x62, + 0x54,0x0f,0x1f,0x31,0x99,0x24,0x53,0x52,0x86,0x4e,0x49,0x86,0xda,0xf5,0xa4,0xc3, + 0xd6,0x95,0x11,0xcd,0x11,0xe0,0xb1,0xd5,0xb7,0x16,0xe3,0xb4,0x2a,0x0d,0x64,0xaf, + 0x74,0x63,0x46,0xd9,0x84,0x24,0x56,0x3a,0x99,0x72,0x90,0x37,0x40,0x03,0x1b,0xe6, + 0xbe,0x64,0x2d,0xba,0x1b,0x98,0x26,0x0f,0xac,0x0a,0x82,0x77,0x82,0x84,0xf2,0xb8, + 0xbd,0x9e,0x37,0xca,0x48,0xb7,0x65,0x00,0xd1,0x68,0xf4,0x73,0x4a,0xbc,0x01,0xac, + 0x01,0x4c,0xb4,0x4f,0xda,0xd8,0x6b,0xa2,0x44,0xb5,0xd7,0x44,0x37,0xf7,0x65,0x24, + 0xb8,0x2a,0x61,0xe1,0x9a,0x80,0x02,0x53,0x47,0x80,0x6b,0xc4,0x53,0x94,0x0a,0xf4, + 0x89,0x74,0x4a,0xdb,0x72,0x86,0x5e,0x07,0x3b,0xa8,0xb8,0xe5,0xd2,0xf4,0xba,0x3b, + 0xd6,0x43,0xca,0x5f,0x15,0xae,0x97,0x45,0x2e,0xf7,0x65,0x87,0x8f,0xa8,0x1d,0x99, + 0xa8,0x9d,0x92,0x32,0xf0,0xd2,0xda,0x00,0x7a,0x91,0xaa,0x6b,0x7c,0x4c,0xb7,0x3a, + 0xfd,0xd5,0x39,0x69,0x6f,0x73,0x1a,0x12,0xeb,0xcb,0x5e,0x59,0xb0,0xbd,0x5f,0x62, + 0xae,0xe6,0xc1,0xf6,0xd4,0x47,0xc9,0xa4,0x53,0x45,0xe6,0x1c,0x0a,0x52,0x97,0x0b, + 0xe6,0x73,0x19,0x01,0xa1,0xd2,0x64,0x13,0x58,0x75,0x3f,0xe6,0x98,0xe4,0xce,0x20, + 0x4e,0x82,0x70,0x89,0x93,0xfe,0x05,0x19,0x84,0x53,0xb8,0xab,0xc1,0x70,0xb6,0x42, + 0xa5,0xcf,0x03,0x88,0xa9,0x16,0x13,0xdc,0xd2,0x95,0x38,0x0d,0xf8,0x22,0x20,0xd0, + 0xdb,0x94,0xd4,0x9d,0x2e,0x5a,0x9b,0x9b,0x4d,0xcb,0x58,0x72,0x8c,0x47,0xe5,0xf4, + 0x1a,0x26,0x04,0xba,0x47,0x0a,0x87,0xb0,0xdc,0x15,0x0e,0x62,0xf0,0x31,0xd3,0x47, + 0xc0,0xc2,0x8e,0xa0,0x51,0x11,0xfd,0xa4,0x98,0x5b,0x4f,0xf0,0x49,0x46,0x50,0xd6, + 0x15,0x53,0x2f,0xd4,0x70,0xa3,0xff,0x9f,0x82,0x20,0x9f,0x62,0x74,0x72,0x42,0xeb, + 0xba,0xfb,0xbf,0xba,0x9d,0x97,0xdd,0x6d,0x0a,0x9c,0xd2,0xbc,0xdb,0x7d,0xd2,0xcd, + 0x7a,0xd4,0xad,0x23,0x79,0xb4,0xe3,0x41,0x34,0x8a,0x57,0x5b,0x0d,0x1d,0x5a,0x8c, + 0x8f,0xf1,0xa2,0x50,0x67,0x35,0x6a,0x91,0xd4,0x2b,0xe6,0xff,0x12,0x85,0xdd,0x5a, + 0xd5,0x53,0xc0,0x9d,0xf6,0x69,0xbb,0x86,0x06,0x0f,0xc9,0x10,0x32,0x07,0xcd,0xc2, + 0x53,0x0c,0x05,0x8c,0x01,0xfd,0x99,0x6d,0x1b,0xac,0x85,0xc5,0x0e,0xa9,0x3f,0x72, + 0x76,0xa7,0x35,0x47,0x27,0x32,0x2f,0xcd,0xb0,0x67,0x94,0xef,0xb2,0xd7,0x69,0xaf, + 0x65,0x62,0x38,0x87,0x7e,0xe8,0x99,0x6b,0xa8,0xfd,0xf0,0x88,0x42,0x96,0xb3,0x96, + 0xdc,0xf3,0x3a,0x6b,0xd1,0x41,0x88,0x33,0xcc,0xcb,0xbf,0xda,0xdb,0x3b,0x9b,0x02, + 0xc3,0xd8,0x30,0xf4,0x84,0x70,0x1b,0x38,0xc5,0xc6,0xab,0x3d,0xc6,0xce,0x3c,0x5d, + 0x24,0xf3,0x3c,0x0d,0x16,0xf8,0x6e,0x63,0x22,0x2e,0x41,0x42,0xf4,0xd6,0x60,0x6b, + 0x11,0x8c,0x02,0xb8,0x21,0x99,0x60,0xd4,0xb9,0xdb,0x50,0xb4,0x69,0x09,0x07,0xcb, + 0xd9,0x78,0xf5,0x3f,0xe3,0xd9,0x7e,0x2a,0xfe,0x3c,0x8b,0xfb,0x29,0x67,0xb1,0x4c, + 0x64,0x10,0x96,0x52,0xa5,0x82,0xc5,0x0b,0xdc,0x59,0xce,0x16,0x71,0x7a,0xe7,0xb0, + 0xb3,0xc1,0xab,0x0b,0xdf,0x67,0xcb,0x78,0x96,0xb2,0x9f,0x82,0xe6,0x75,0xc0,0xf6, + 0x53,0x2f,0x4d,0xfb,0x67,0xad,0x01,0x10,0xef,0x01,0xcd,0x40,0x9f,0x1f,0xcc,0x35, + 0x85,0xc8,0x5b,0x49,0x74,0xa9,0x70,0x98,0x85,0x9b,0x65,0xc0,0x29,0x2a,0xac,0x16, + 0x4f,0x74,0x21,0x14,0x4f,0x3a,0xba,0x14,0x90,0x77,0xe3,0xd5,0x0f,0x80,0xc1,0x71, + 0x11,0x81,0xb5,0xc0,0xb6,0x4e,0x5e,0x0d,0xa1,0x86,0xae,0x08,0xe1,0x96,0xe4,0x0e, + 0x6e,0xda,0xe1,0x0d,0x9e,0x11,0x81,0xe7,0x79,0x65,0x89,0x28,0x75,0x75,0xf0,0x52, + 0x2c,0x18,0xca,0x16,0x89,0x97,0x35,0x93,0x94,0xcf,0x1b,0xd2,0x5c,0xb9,0x8d,0x1b, + 0xb8,0x09,0xe2,0x99,0x00,0xf6,0xd0,0x0e,0x2d,0x33,0xf7,0x43,0x9c,0xba,0xd5,0x60, + 0x5e,0x1a,0x78,0x4d,0x82,0x5b,0x9b,0xd5,0xa0,0x3d,0x86,0xb1,0x0d,0x39,0x52,0xe3, + 0x95,0x6c,0x04,0xfc,0xa2,0xfb,0x27,0x12,0x12,0x81,0x52,0xe7,0x84,0xfc,0x00,0x37, + 0x25,0x22,0xd2,0x2d,0x44,0x94,0xab,0x6c,0x10,0x90,0x6e,0x12,0x70,0xd6,0x02,0x9e, + 0x6f,0x72,0x1f,0x64,0x55,0xd2,0x80,0x17,0xaf,0xf6,0xa7,0xbe,0x27,0x26,0xfd,0x5d, + 0x75,0x21,0x12,0x04,0xf6,0x12,0xf7,0x55,0x13,0x59,0x22,0x19,0x5e,0x59,0x16,0x4c, + 0xf7,0xe6,0x1d,0x27,0x60,0xaa,0xb4,0xa4,0xe6,0xb5,0xe5,0x18,0x1b,0x83,0x40,0xc8, + 0x57,0xc8,0x43,0xa9,0x3c,0x45,0xfa,0x54,0x79,0x4d,0x04,0x48,0x56,0x6e,0x50,0xa2, + 0xab,0x4b,0x5f,0xed,0x00,0xa2,0x8b,0x06,0x4b,0x63,0x64,0x2f,0xfa,0xbc,0xa4,0xb6, + 0xa6,0xd8,0xbc,0x34,0x40,0xb1,0x5e,0xb4,0x40,0xcd,0x78,0x83,0xc9,0x3f,0x46,0xf5, + 0x15,0xde,0x6c,0x25,0x42,0xce,0x93,0x7a,0xc3,0x5b,0x2c,0xdc,0x6c,0x5b,0x62,0x7a, + 0xfd,0xa6,0x34,0x8b,0x5c,0xdc,0x55,0xe8,0xd2,0x78,0xb5,0xb3,0x2a,0x71,0xec,0x8c, + 0xe6,0x57,0xe6,0x14,0x1b,0xc5,0x29,0x10,0x93,0x06,0x8d,0x57,0xaf,0xc9,0x94,0x47, + 0x5c,0x88,0xb3,0x16,0xd5,0xab,0xae,0x21,0x84,0x3c,0x72,0x2c,0xa8,0xdc,0x9c,0x97, + 0xa4,0xa3,0xbc,0x80,0x6a,0x5c,0x4a,0xe7,0xa8,0x89,0x52,0x0a,0x27,0x6f,0xd9,0x60, + 0x60,0x67,0xdd,0x46,0x17,0xfe,0x7a,0xf7,0x6e,0x03,0x00,0x48,0x83,0x36,0xd8,0xe1, + 0xb2,0x21,0x77,0xe0,0xdd,0xc6,0x69,0xbb,0x51,0x91,0xd2,0xad,0x52,0x41,0x1d,0x0e, + 0xb3,0x34,0x14,0x5b,0x05,0x04,0xed,0xc8,0xa6,0x58,0xbc,0x8b,0xe2,0x81,0xd8,0x26, + 0xa0,0x39,0x23,0xb1,0xc7,0xb2,0xdd,0xd8,0xc9,0xcf,0xbb,0x28,0x67,0x27,0xb1,0x50, + 0x84,0x4d,0x20,0xfb,0x5d,0x27,0xe7,0xdd,0x0e,0x1e,0x50,0x3d,0xc9,0x84,0xf6,0x63, + 0x4c,0x38,0x6e,0x37,0x7e,0xd1,0xe0,0x1d,0x18,0xbc,0xfb,0x84,0xc1,0x3b,0xbf,0xc6, + 0xe0,0x5d,0x18,0xfc,0xf0,0x09,0x83,0x77,0x7f,0x8d,0xc1,0x0f,0x61,0xf0,0xa3,0x27, + 0x0c,0x7e,0xf8,0x37,0x0c,0xfe,0x88,0x04,0x32,0x0a,0x41,0xb6,0x0a,0xdf,0xa8,0x26, + 0x7b,0x77,0x0d,0x7d,0xca,0x67,0xab,0xf0,0xcd,0x15,0x89,0x4d,0x30,0xe0,0x8f,0xeb, + 0xd5,0xe3,0x63,0xdc,0x66,0x00,0x27,0xc7,0x7c,0xfb,0x10,0x25,0x4b,0x0d,0x31,0x9a, + 0xd8,0x18,0xa7,0x5c,0x1b,0x22,0x06,0xa8,0x1f,0xe4,0x95,0xe5,0x7d,0x2b,0x28,0x94, + 0xe2,0x17,0xd3,0x46,0xd8,0xe1,0xd1,0xc9,0x13,0x80,0xf9,0x3b,0x66,0x0f,0x30,0x25, + 0x25,0xdb,0xf5,0xc8,0x18,0xe0,0x85,0x1e,0x1d,0xe1,0x59,0xb3,0xc9,0x3e,0x69,0x40, + 0x64,0x48,0x28,0xe4,0x0d,0x87,0xd0,0x2d,0xa3,0x48,0x17,0x83,0x96,0x20,0xc4,0xa0, + 0x06,0x77,0x89,0x66,0x49,0x8f,0x79,0x4c,0xf0,0xc4,0x03,0x14,0xcf,0x35,0x66,0x2a, + 0x99,0x7f,0x36,0x4a,0xe3,0x29,0x21,0x2c,0x88,0x18,0x98,0x37,0x00,0x94,0x6e,0x43, + 0x90,0x41,0x31,0x11,0x76,0x8d,0x80,0xdf,0x61,0xcd,0xe6,0xf6,0x19,0x2a,0x9a,0x27, + 0xa2,0x39,0x28,0xdc,0x65,0x6d,0xd2,0xbf,0x93,0xa7,0x8b,0x1e,0x9d,0xf4,0x44,0xfc, + 0x3d,0x6c,0xfd,0xc0,0xa7,0x71,0xba,0x7c,0x7c,0x00,0xee,0x25,0x7f,0xcf,0x10,0x9f, + 0x13,0x3c,0x26,0xf8,0xe8,0x10,0xb3,0xc7,0x06,0xc8,0x2f,0xeb,0x17,0x35,0xc8,0x89, + 0x78,0x5d,0x7b,0x16,0x5c,0xe9,0x0b,0x86,0x20,0x94,0x2d,0x02,0x88,0x6a,0x3d,0x9d, + 0x8f,0x84,0xf8,0x15,0xc1,0x38,0xb4,0x09,0x86,0x18,0x09,0x91,0x9a,0x03,0x52,0x58, + 0x14,0x0b,0xb5,0x03,0xc6,0xc2,0x83,0xa4,0xe2,0x6d,0x54,0x42,0xe9,0xac,0x95,0x6c, + 0x63,0x04,0xf7,0x23,0x3d,0x3b,0xee,0x6f,0x03,0x5b,0xd5,0xea,0xb9,0xfb,0x05,0x85, + 0x5c,0xd4,0x0d,0x64,0x52,0x0a,0x10,0x78,0xd1,0x6b,0x13,0x01,0x99,0x16,0x9d,0xdb, + 0xcc,0x8b,0x7c,0x9c,0xef,0x28,0x48,0xa7,0x0b,0x2f,0x05,0x51,0x8c,0x62,0x06,0xb8, + 0x31,0x85,0x39,0x42,0x04,0x36,0x03,0xed,0x71,0xea,0xb4,0xaa,0x01,0xe1,0x71,0x09, + 0xb1,0xd5,0x35,0xe6,0x06,0x55,0x03,0x58,0x95,0x91,0xa8,0xdf,0xbe,0xfd,0x74,0x65, + 0x53,0x16,0x20,0x9b,0x80,0x92,0xc8,0x43,0x90,0x4a,0x97,0x5a,0xb3,0xc4,0xc7,0x33, + 0xd5,0x32,0x5d,0x80,0xc7,0xf7,0x58,0x0a,0xf1,0x0f,0x6d,0xb8,0x96,0xf5,0x06,0xc2, + 0x7f,0x0e,0xf1,0x3d,0xd8,0xe9,0x28,0x18,0x71,0x91,0x31,0x20,0x9d,0x4d,0xe0,0x47, + 0x6e,0xcd,0xe2,0xb9,0x57,0x3a,0x06,0xc9,0xb2,0x58,0x8e,0x32,0x88,0x71,0x66,0x14, + 0x5c,0x93,0xa5,0xa9,0x68,0xd5,0xd6,0xf0,0x09,0x28,0xd1,0xb1,0x13,0x9e,0x9a,0x14, + 0xbd,0x56,0x2b,0x29,0x4e,0x62,0x3a,0xa8,0xc9,0x8a,0x5a,0x40,0x6c,0x10,0x47,0xe1, + 0xf9,0xf4,0x3f,0x0e,0x42,0x2f,0x02,0xe8,0x41,0x07,0x33,0xa3,0x18,0xcf,0x23,0xf2, + 0x34,0x67,0x4c,0x0d,0x8e,0xaa,0x41,0x30,0x3c,0x2f,0xd0,0xc4,0x00,0x34,0x76,0x81, + 0x54,0xcb,0xae,0xeb,0xc1,0x54,0x9d,0x60,0x08,0x0b,0x4a,0xab,0x49,0xa9,0x9b,0x6f, + 0x12,0xcd,0xa5,0xd4,0x89,0x6f,0x91,0xad,0xc5,0x53,0xe4,0x44,0x78,0xdf,0x94,0xa7, + 0x78,0x54,0x20,0xf7,0xbc,0x39,0x88,0x12,0x58,0x32,0x81,0x27,0x2c,0xbd,0x3c,0x08, + 0xd9,0xa4,0xa7,0xff,0x78,0xa8,0xb9,0x71,0x40,0xb2,0x2c,0x8a,0x55,0x7a,0x5e,0xbd, + 0x8d,0x20,0x06,0x0f,0xc1,0x7a,0x8e,0x41,0x30,0x62,0xe8,0x36,0xa5,0x43,0x9b,0x42, + 0x86,0xa9,0x8a,0x02,0x41,0x42,0x23,0x78,0x96,0x41,0xbd,0x92,0x64,0x6b,0xdb,0x20, + 0x6f,0x26,0x5d,0x70,0xcd,0x63,0x0e,0x6e,0x03,0xae,0x0a,0xd3,0x70,0x2d,0x15,0x02, + 0xf5,0x5e,0x4e,0x10,0xe5,0x79,0x91,0x06,0xd0,0x73,0xc4,0x30,0x5b,0x47,0x87,0x3b, + 0x97,0xf0,0x28,0x0d,0xe6,0x5c,0xda,0xf9,0xd2,0x2b,0x05,0x85,0x2c,0x2b,0xcd,0xfa, + 0xc1,0x9b,0xdb,0x74,0x3c,0x38,0x0d,0x06,0x98,0xf6,0xc0,0xc3,0x04,0xc8,0x8e,0x30, + 0xf6,0x7c,0xb8,0xd3,0xfa,0xc7,0x4c,0xc1,0x39,0x1b,0x83,0x64,0x98,0x96,0x55,0x32, + 0x38,0x91,0x97,0xdb,0x01,0x4a,0xd0,0x34,0x4a,0xa2,0xa2,0xe2,0x7f,0x3d,0xeb,0x06, + 0x0a,0x59,0x81,0x36,0x40,0xb0,0x28,0x93,0x04,0x46,0x16,0xc7,0x62,0x4e,0x32,0x9a, + 0x32,0xdc,0x88,0xf9,0x4b,0x90,0xd8,0x14,0x4e,0xdb,0x38,0x09,0x04,0x4a,0x36,0xe8, + 0x6a,0xc8,0x33,0x34,0xc4,0xd4,0xa2,0x2c,0x10,0xda,0x16,0xe8,0x54,0xcd,0x0e,0x6b, + 0xe0,0x55,0xb2,0x11,0x48,0x89,0xf2,0xfb,0x39,0x19,0xb7,0xde,0x1c,0xe6,0xab,0xd6, + 0x1c,0xe6,0xfb,0x27,0xb0,0x1d,0xb8,0x52,0x4b,0x3c,0x54,0x41,0x64,0x58,0xb6,0x3e, + 0x33,0xbb,0x8d,0x90,0x7c,0x84,0x3b,0x84,0xf2,0x34,0x84,0x02,0xf5,0xf9,0x10,0x6f, + 0x02,0x3c,0x97,0x8c,0x49,0x49,0x14,0x00,0xb2,0xe3,0x0c,0x73,0x2f,0x8f,0x76,0xa7, + 0x54,0x1d,0xfb,0xfb,0x4c,0x97,0xa5,0x0e,0x95,0xb8,0x91,0xd9,0xd1,0xc6,0xb3,0x5f, + 0x15,0x33,0x99,0x1a,0x81,0x6a,0xcb,0x47,0x47,0x91,0xd2,0x41,0xa3,0xc8,0x63,0xe7, + 0xa5,0x51,0xae,0x53,0x3c,0x84,0x8c,0x48,0xc2,0x66,0x53,0x72,0xba,0x80,0x15,0x24, + 0xa2,0xb3,0x35,0xc3,0x36,0x3a,0x3f,0x6b,0x81,0x68,0xec,0x72,0x7d,0xf2,0xfc,0x4c, + 0x09,0xf8,0x61,0x46,0xb8,0x9a,0x11,0xc9,0x49,0xdb,0x65,0x4e,0xbe,0x69,0x45,0xea, + 0x0d,0x90,0xb4,0xb3,0x96,0x1a,0x7a,0xef,0xac,0x85,0x19,0x2d,0xcc,0x6c,0xe9,0x93, + 0xf3,0xf9,0x41,0xfc,0xdf,0x98,0x41,0x7e,0xfa,0xdf,0x8f,0x87,0xb3,0x29,0x8f,0x32, + 0x07,0x46,0xba,0x0a,0x39,0x5e,0xbe,0x5e,0xbe,0xf5,0xa1,0xc6,0x7a,0xef,0x37,0xa6, + 0x81,0x74,0x1b,0x56,0xe5,0xb5,0x19,0x30,0x85,0x94,0x39,0x75,0xf0,0x19,0x9e,0xc3, + 0xef,0xef,0xed,0xb5,0x5a,0xe0,0xd2,0x49,0x86,0xc0,0xdd,0x94,0x8e,0xef,0x30,0xdc, + 0x2e,0xb4,0x20,0x5e,0x08,0x43,0x54,0x67,0x44,0x5f,0x84,0xf6,0x9d,0x82,0x18,0x3a, + 0xd8,0xc3,0x43,0x6b,0xc5,0x43,0x87,0x92,0x67,0xf8,0x6a,0xc6,0x4d,0x0a,0x93,0x4c, + 0xb3,0xa5,0x69,0x40,0x7b,0xc3,0x86,0xe7,0x0e,0xb5,0x6b,0xc2,0x05,0x44,0x12,0x56, + 0x0b,0x4b,0x30,0x07,0xa9,0xee,0xbf,0x87,0x88,0xe2,0xc0,0xf8,0xce,0x00,0xa2,0xf3, + 0x09,0x79,0xbe,0x7f,0x35,0x87,0x8b,0xf7,0x68,0x20,0x81,0x61,0xa6,0x41,0x01,0x8a, + 0x51,0x79,0x4f,0x26,0x18,0xe1,0x4b,0x41,0xc4,0x67,0x87,0x22,0x17,0xd7,0x35,0xe4, + 0x3c,0x2c,0x49,0x98,0x7a,0x88,0xef,0x61,0xd0,0x34,0xd5,0x11,0x87,0x9f,0x26,0x9e, + 0xf2,0xb2,0xf4,0xde,0x06,0x40,0xd5,0x6c,0x06,0xe2,0xb1,0x24,0x5b,0x2a,0xeb,0xfc, + 0x4a,0xff,0x21,0x11,0x65,0x1b,0x69,0x93,0x16,0xa0,0x45,0x53,0xe6,0x9e,0xa0,0x15, + 0x2c,0x03,0x66,0x7e,0x04,0x9a,0xbe,0xe9,0x16,0x2b,0x09,0x9d,0x14,0xf6,0x91,0x99, + 0x20,0x4a,0x13,0x5b,0xee,0x62,0xda,0x84,0x98,0x11,0x1f,0x5b,0x3d,0xc2,0x66,0x38, + 0x3d,0x02,0x6a,0x98,0x7c,0x67,0xc0,0x0b,0x41,0x57,0x72,0x5c,0x0f,0x7b,0x22,0x34, + 0x41,0x67,0xff,0xf1,0xd5,0x80,0x08,0x5f,0x13,0x00,0x4b,0x4d,0xb0,0x60,0x04,0x8a, + 0x04,0x7c,0x94,0xcd,0x3c,0x09,0xe6,0x3e,0x72,0xec,0x34,0x83,0x71,0x80,0x5f,0x8b, + 0x09,0x98,0xf5,0x20,0xc3,0x6e,0x86,0x13,0x64,0xbc,0xc0,0x51,0x53,0x2e,0xc7,0x95, + 0x5b,0x16,0xe4,0xbd,0xe4,0x76,0x17,0xf3,0x41,0x32,0x04,0x93,0xc9,0x7c,0x24,0x80, + 0x0e,0x34,0x3b,0x7b,0xf8,0xd6,0x0d,0xe8,0xe3,0x3b,0xbe,0xec,0x17,0xa2,0x45,0xd6, + 0x5c,0x58,0x2b,0xd0,0x37,0x7c,0x1e,0xd8,0x50,0xc3,0x35,0x05,0xbe,0x76,0x53,0x4c, + 0xde,0x7a,0x78,0xf8,0xf2,0xd5,0xbe,0x73,0xff,0xf1,0xf6,0xc7,0x1f,0x40,0x00,0x53, + 0x70,0x59,0xc1,0x68,0x69,0x42,0x55,0xcb,0x1e,0xb9,0xa0,0x02,0xda,0xf0,0x1a,0x96, + 0x3d,0xc4,0x7b,0x09,0xca,0x0c,0x90,0x07,0xc6,0x40,0x7c,0xee,0x40,0x64,0xe4,0xc8, + 0x96,0x54,0x29,0x2c,0x97,0x05,0xee,0x1d,0x5e,0x8f,0x9c,0x20,0x02,0xf1,0xfb,0xdd, + 0xa7,0x0f,0xef,0xdd,0x61,0xe9,0xda,0x30,0xe8,0x69,0x9c,0x9a,0x81,0xdb,0xee,0x07, + 0xe8,0x69,0x9c,0x90,0x47,0xe3,0x6c,0xd2,0x0f,0x0e,0x0e,0x88,0x6a,0x49,0x37,0xc7, + 0xfe,0xbf,0x04,0x5f,0xfb,0xf8,0x36,0x0e,0x7f,0x78,0xe0,0xaa,0xda,0x59,0xd7,0x42, + 0xbf,0x16,0x44,0x33,0xde,0xcf,0x2b,0x7b,0x6e,0xae,0x00,0x78,0x76,0x21,0xe3,0x4a, + 0xa9,0x4d,0xc3,0x93,0x24,0x33,0xe6,0x39,0x64,0x78,0xf8,0x97,0xf6,0xd7,0xbe,0x57, + 0x26,0xe8,0x0c,0xb1,0x50,0x6e,0x15,0xb5,0xc5,0x33,0x74,0x2b,0x7a,0xff,0xeb,0x12, + 0xa2,0x30,0xbf,0x62,0x0f,0xf8,0x97,0xce,0x57,0x5d,0x05,0x1c,0xe5,0xd6,0x1a,0xdd, + 0xaf,0x0f,0x0f,0x86,0xea,0x68,0xe4,0xe0,0xa1,0xb4,0xc8,0xa7,0x7a,0xa6,0xa7,0x89, + 0xfa,0xa5,0x74,0x93,0x53,0xfe,0x01,0x5f,0x03,0x32,0x00,0xab,0x19,0x50,0xb2,0x9d, + 0xa8,0xe1,0x96,0xe1,0xd6,0xb4,0x2c,0xd2,0x8f,0xc2,0x9a,0xa8,0x8b,0x67,0xa3,0xd2, + 0x14,0xfb,0x7b,0xeb,0xb2,0xbe,0xcb,0x37,0xc2,0x64,0xd4,0xf2,0xab,0xaa,0xf7,0xdf, + 0x62,0x0e,0xae,0xf0,0xbd,0x1c,0x3a,0xfd,0x4f,0x00,0x09,0x03,0xe4,0x92,0xae,0xcb, + 0x97,0x61,0xf4,0x6b,0x39,0xa0,0xa3,0xc9,0x92,0x99,0x37,0xd7,0xfa,0x25,0x2e,0x1b, + 0x8c,0x03,0x17,0x13,0xec,0x47,0xbe,0xdf,0x73,0xc8,0x04,0x3d,0x2f,0xbd,0x46,0x07, + 0x80,0x08,0x63,0x73,0x19,0x50,0x80,0x46,0xe3,0xa1,0x18,0x82,0x8c,0x49,0x70,0xcf, + 0x43,0x89,0x9d,0x48,0x83,0x41,0xff,0xff,0xa8,0xbd,0xf3,0x1f,0xd1,0xb1,0x39,0x13, + 0xab,0x64,0xeb,0xfd,0x59,0x5a,0xd2,0x47,0x5f,0xbe,0x7e,0x07,0xfe,0x0b,0x74,0x40, + 0xb4,0x4e,0x4f,0x8e,0xda,0x6d,0xcb,0x9e,0x54,0x4a,0xbf,0xa3,0xd2,0xd6,0xe1,0x09, + 0x3e,0x9a,0x56,0x1f,0x61,0x61,0xeb,0xa4,0xad,0x15,0xd1,0xb7,0xb4,0x4b,0x3b,0x30, + 0x7c,0x66,0x1c,0x4c,0x0e,0x8c,0x89,0x81,0x3a,0x33,0xd1,0x0f,0xb0,0x04,0x1e,0x4c, + 0x0f,0x8c,0xa9,0xa1,0x1a,0x4d,0xf5,0x33,0x59,0xa8,0x6e,0xc4,0x81,0x21,0x0c,0x94, + 0x00,0x98,0xd3,0x8f,0xb4,0xcb,0xea,0x85,0x4c,0xf2,0x4b,0x9a,0x55,0x69,0x50,0x95, + 0x11,0x61,0x52,0xc0,0x24,0xab,0xc0,0x9f,0xf1,0xd0,0x17,0x0e,0x7b,0x8f,0xc6,0x54, + 0x10,0x30,0x29,0x59,0xdb,0xa6,0x37,0x8e,0xc0,0x6d,0x06,0x43,0x4c,0x46,0x90,0xff, + 0xc8,0x5f,0xa7,0x52,0x86,0x13,0x3d,0x2a,0x05,0xbf,0xb2,0x41,0x89,0x7b,0x09,0x6e, + 0xe9,0xdd,0xf0,0x74,0x54,0xe2,0x21,0x20,0x02,0x30,0x49,0x2a,0xb5,0x0f,0x16,0x2a, + 0x73,0xbf,0x7c,0x55,0x53,0x13,0xce,0xf4,0xcf,0x19,0x58,0xae,0x90,0x9c,0x5a,0x32, + 0x1b,0x40,0xa4,0x00,0xe2,0x61,0x58,0x99,0x7c,0x9d,0xd2,0xb8,0x99,0x0d,0xa4,0x6a, + 0x01,0x9d,0x7c,0xb3,0x05,0x44,0xb0,0x88,0x1c,0x06,0xe5,0x26,0xb7,0xba,0x09,0xd4, + 0x46,0x77,0x89,0xf6,0x1d,0xfc,0xf5,0x9f,0x67,0x3c,0x1a,0xf2,0x0f,0xb1,0x4f,0x0d, + 0x07,0x31,0xf4,0xe0,0x45,0x86,0x45,0xfa,0xa7,0x9a,0x56,0xab,0x9d,0x1b,0x37,0x80, + 0x7f,0x31,0x74,0x31,0x7a,0xc6,0x6b,0x74,0x63,0x5c,0x93,0x92,0x6b,0x63,0xa6,0xad, + 0x21,0x94,0x55,0x5e,0xdb,0x75,0x10,0xb9,0x9a,0x06,0xfb,0x8f,0xff,0xc7,0xb0,0xd1, + 0x9a,0x8c,0x3f,0x5a,0x1e,0x17,0xdf,0x79,0xed,0xd7,0x18,0xa6,0x98,0x45,0xcf,0x05, + 0x0e,0xa1,0xbc,0x02,0x5e,0x22,0x6c,0xa1,0xad,0x01,0x16,0xcf,0x70,0xb7,0xfd,0x0e, + 0xb7,0x92,0x66,0x82,0xfb,0x94,0x5d,0xc2,0x07,0xa8,0x53,0x4a,0xec,0x61,0xad,0x3c, + 0x59,0x79,0x01,0xce,0x9f,0xd6,0x4a,0x32,0x4e,0xc8,0x9e,0xc6,0x31,0x56,0x8e,0x23, + 0xe9,0x18,0x11,0xea,0x8c,0x72,0x5c,0xc9,0x16,0xf1,0x0c,0xdc,0xd9,0x40,0x79,0xe2, + 0x01,0x1f,0xe1,0xaa,0x07,0x10,0xd8,0xc0,0x8c,0xe8,0xe5,0x36,0x47,0x2f,0x1a,0x0d, + 0xa1,0xac,0xbf,0x5c,0xda,0x1a,0xfe,0x32,0x24,0xc9,0xca,0x9a,0xca,0x2a,0x02,0xd7, + 0xa5,0x52,0x4b,0x38,0xf2,0xf8,0xe9,0xb9,0xa9,0xaf,0x0e,0x0c,0x70,0xab,0x62,0x86, + 0xd2,0x05,0xbc,0x59,0x78,0x77,0xc0,0xf2,0x9e,0x41,0x51,0x21,0xf8,0xe9,0x11,0xf5, + 0xb7,0x2e,0x44,0x41,0xbd,0xf8,0x7d,0x43,0xfc,0x50,0xf4,0x20,0xbf,0x22,0x08,0xa0, + 0x29,0x9e,0x19,0xa0,0x7d,0xc0,0x80,0xc6,0x43,0x27,0x36,0x05,0x3c,0x84,0xef,0xf5, + 0x21,0x0f,0x64,0xb4,0x49,0x56,0x00,0x0c,0x8e,0x44,0xd4,0xce,0x23,0x13,0x92,0x63, + 0x7c,0x63,0x42,0x06,0xc6,0x80,0xd8,0x7b,0x8e,0xfe,0x7f,0xfe,0xeb,0xbf,0xe7,0x33, + 0x22,0x18,0xe1,0x03,0x2c,0xa9,0x4f,0x03,0x49,0x85,0x56,0xe8,0xf3,0x25,0x1b,0x50, + 0x42,0x9b,0x46,0x69,0x42,0x6c,0x1a,0xfb,0xb3,0x50,0x06,0x84,0x42,0xef,0xcb,0xca, + 0xd0,0x96,0x74,0x13,0x57,0xf3,0x5f,0x64,0xdb,0x7f,0x51,0x39,0x93,0x46,0xb3,0x01, + 0xd3,0xf2,0x22,0x4a,0x49,0xf6,0x74,0x4f,0xf9,0xdb,0x8b,0xa8,0xc2,0x28,0x40,0x19, + 0xdc,0xe3,0x59,0x7e,0x29,0x88,0xdc,0x77,0x1e,0x9f,0xa0,0x22,0xf5,0xe7,0xbf,0xfe, + 0x9f,0x62,0x12,0x8f,0xc8,0x81,0x9e,0x10,0x78,0x56,0xe0,0xc4,0x37,0xb8,0x07,0x9c, + 0x20,0x69,0xfc,0x2c,0xce,0xc9,0x86,0xd2,0x61,0x13,0xb3,0xc3,0x4f,0x5a,0xf9,0x03, + 0x0b,0x04,0x64,0x04,0xa6,0x0d,0xd5,0xaa,0x67,0x18,0xd6,0x81,0x8a,0xa1,0x72,0x01, + 0x7a,0x2b,0x3e,0x10,0xa7,0xce,0x0d,0xc9,0x31,0xa8,0x25,0x81,0x5c,0x10,0x19,0xb9, + 0x5b,0x2d,0x1b,0xa9,0x7e,0x7e,0x1f,0x2f,0x00,0x7f,0x6b,0xd9,0xce,0x8b,0x8b,0x6d, + 0x2f,0x78,0x36,0xc8,0x6f,0xa8,0x02,0x01,0xad,0xc4,0xcb,0x36,0x26,0xad,0x97,0x1f, + 0xa8,0x35,0x8d,0x03,0xb8,0x07,0x11,0x00,0x70,0x09,0xb7,0x9a,0x18,0x9b,0x61,0xb9, + 0xa4,0x11,0xcb,0xd5,0x95,0x65,0xe4,0x86,0x71,0x04,0x33,0x99,0xe1,0x8a,0x14,0x7a, + 0x86,0xc9,0xf6,0xda,0x58,0x25,0x46,0x51,0x9b,0x4f,0x71,0x86,0x6f,0x11,0xc3,0xd5, + 0x67,0x54,0x89,0x56,0xa7,0x7d,0x74,0x7a,0xfc,0xe2,0x04,0x62,0x0f,0xbc,0x86,0x81, + 0x3e,0xbc,0x46,0x5f,0xca,0x8b,0xa5,0x50,0x19,0x7c,0xe8,0x57,0x46,0x36,0xf2,0x25, + 0xdd,0xa2,0xb3,0x73,0xdd,0x59,0x2b,0x2f,0xc2,0x48,0xa6,0xd7,0xb6,0x30,0x98,0x29, + 0x84,0x60,0x3b,0x7d,0x06,0x66,0x46,0xa6,0x72,0x22,0xc6,0x5a,0x1a,0x21,0x74,0xd3, + 0x68,0x1e,0x8b,0x48,0x3f,0x0f,0x0f,0xca,0x99,0x75,0xfd,0x6a,0xec,0x10,0xc2,0x53, + 0x30,0x4a,0x33,0x90,0xdb,0x18,0x20,0x3a,0xc8,0xbc,0xa3,0x1c,0xcb,0x44,0x00,0xaf, + 0x55,0x35,0x7c,0xf1,0xdd,0x8e,0xdc,0x09,0x18,0x85,0x10,0x5f,0x78,0x10,0x0f,0xed, + 0x7c,0x89,0x30,0xdd,0xb0,0xb1,0x46,0x58,0x88,0x86,0x47,0x88,0x00,0x9c,0x31,0x49, + 0x14,0xac,0x48,0x0a,0xb7,0x70,0xe7,0xbf,0x9e,0x82,0xe1,0x81,0xce,0x66,0xc9,0x39, + 0x4d,0x01,0x2c,0x4d,0x44,0xaf,0x09,0x83,0x40,0xa9,0x23,0x87,0x46,0xde,0x7f,0x90, + 0x6c,0xef,0x5d,0x38,0x41,0xd2,0xcb,0x05,0x9f,0xaa,0x62,0xba,0x1e,0x2a,0x6b,0xff, + 0x41,0x23,0xa8,0x25,0xa7,0x6b,0x4b,0xd5,0xaa,0x75,0x38,0xa9,0xd2,0x89,0x75,0x83, + 0x04,0x94,0xaa,0xf3,0xb2,0xeb,0x74,0x4e,0x4e,0x9d,0x23,0xa7,0x63,0x58,0xfa,0x69, + 0x74,0x60,0x46,0x60,0x41,0x3a,0xe7,0x06,0x93,0xbc,0x00,0x9a,0xd5,0x95,0x30,0xb4, + 0x4f,0xf9,0xc8,0xbd,0xe1,0x84,0xab,0xa4,0x28,0xe5,0x5b,0x35,0xbf,0x55,0x1c,0x05, + 0xf7,0xd3,0x7c,0xd7,0x42,0xad,0x57,0x2f,0xdf,0xad,0x00,0x63,0x38,0x9a,0x85,0xb2, + 0x2b,0x3c,0x8d,0x20,0x0d,0x09,0xb8,0x9f,0x71,0x30,0x2f,0xcc,0x2b,0x99,0xed,0x88, + 0x3b,0x72,0xf6,0xf2,0x48,0x4a,0x69,0xf6,0xc0,0xfd,0x79,0xe0,0xa9,0x4d,0x8a,0xfd, + 0xfd,0x67,0x92,0x6d,0x85,0x6a,0xe1,0xce,0xc1,0x6e,0x79,0x87,0xa5,0x87,0xe7,0x6f, + 0x29,0x21,0xe8,0x85,0x20,0xdd,0xdd,0x23,0x60,0xc1,0x3b,0x76,0x00,0x2c,0x20,0xe1, + 0xde,0xa8,0x7b,0x23,0xc0,0x8a,0x68,0x95,0x80,0xba,0x1f,0xd8,0xcd,0xed,0xc7,0x8b, + 0x0f,0xc5,0xea,0xcc,0xea,0xc3,0x11,0x18,0x84,0x65,0xc1,0x1d,0x08,0x0c,0xae,0x69, + 0xbe,0x2a,0xb5,0x09,0xfe,0x57,0xe4,0x7b,0x35,0x04,0x8e,0x44,0xc2,0xc3,0x50,0xd0, + 0xe9,0x35,0x32,0xee,0x1e,0x66,0xd0,0xf0,0x15,0xf1,0x70,0x36,0x06,0x31,0x4e,0x02, + 0xd0,0x9d,0x40,0x60,0x22,0x94,0xa2,0x52,0xea,0x6c,0xb2,0x4c,0xc0,0x2d,0x78,0x98, + 0xf4,0xa3,0x5d,0x20,0x68,0x2e,0xe8,0x45,0x71,0x7a,0xc7,0x9f,0x16,0x06,0x14,0x00, + 0x45,0x10,0x53,0x2a,0xbd,0x0d,0x2d,0x29,0xb6,0x95,0xb0,0xb3,0x6a,0x92,0x94,0x99, + 0xf0,0xa3,0xde,0x93,0x0f,0x32,0xf4,0x12,0x02,0x6c,0x1c,0x8d,0xbf,0x5c,0x78,0x4b, + 0x4b,0xeb,0x11,0xf7,0x41,0x62,0xe7,0x78,0xc2,0x21,0xca,0xd0,0x09,0xa9,0xcb,0x67, + 0xe0,0x85,0xd0,0xbb,0x18,0xe7,0x79,0x11,0xd8,0x5c,0x7b,0x20,0x03,0x4c,0x9d,0x71, + 0x06,0x40,0x17,0x4d,0xdd,0x56,0xb3,0x05,0x8c,0x13,0x99,0x09,0x46,0xe7,0x1c,0x04, + 0x05,0x1c,0x09,0x7e,0xb7,0xe5,0x22,0x33,0xdb,0xc0,0xd1,0xf8,0x33,0xa0,0xcf,0xf4, + 0xd2,0x13,0xdc,0xb4,0x0e,0xe0,0x91,0x08,0x01,0xac,0x98,0x1d,0x5a,0x69,0xb3,0x1c, + 0xe8,0x62,0x9c,0x6b,0x6d,0x7e,0x68,0x41,0x25,0x41,0xf6,0xf7,0x4d,0xc3,0x38,0xc0, + 0xb0,0x09,0xfb,0x7c,0x8f,0x06,0x5b,0xf6,0xe9,0xba,0x30,0x2c,0x50,0x81,0x8f,0xd6, + 0x85,0xfc,0x80,0xb6,0x56,0x97,0x33,0x9a,0x82,0xce,0x94,0xbe,0xa8,0x50,0xac,0xfc, + 0xa8,0xee,0xb9,0x60,0x22,0x68,0x1d,0xd4,0xbc,0xff,0x89,0xa7,0x02,0x08,0x39,0xaf, + 0x17,0x28,0x95,0x23,0x57,0x44,0xbc,0xd2,0xd7,0x73,0x34,0x26,0x73,0x59,0xa9,0x44, + 0x4f,0x53,0x7a,0x3d,0xa5,0x00,0xdc,0x7f,0x78,0x78,0x56,0x8a,0xd6,0x55,0x39,0xd6, + 0x1f,0x6c,0x10,0x63,0xfc,0x48,0xee,0x5e,0xe7,0xcb,0x61,0x1c,0x9d,0x62,0x27,0xcd, + 0xff,0xf9,0x5f,0xff,0xb7,0x21,0x1b,0x52,0x00,0x6a,0x7c,0x73,0x03,0x00,0xac,0x08, + 0xf6,0xfa,0x1c,0x18,0x1a,0x0d,0x01,0xee,0x7e,0xfe,0xf8,0xf6,0x32,0x9e,0x26,0x20, + 0x84,0x11,0xad,0x22,0xce,0x43,0x75,0x28,0xa9,0xad,0x29,0x2b,0x88,0x56,0x5f,0xcf, + 0x0a,0xa1,0x59,0xa1,0xd6,0x83,0xd2,0x3c,0xc8,0x95,0xe1,0x0e,0xe8,0x54,0x58,0x32, + 0x89,0x7a,0x4b,0xef,0xfb,0x88,0xa2,0x18,0xab,0x81,0x29,0x1a,0xde,0xc9,0xcc,0xaa, + 0x99,0xf3,0xcd,0xce,0xb9,0x6d,0xd5,0x22,0x5d,0x3a,0x80,0xc3,0x04,0x58,0x2a,0x40, + 0x77,0xff,0xbd,0xe2,0xdc,0x0b,0xf6,0x4f,0x6f,0xaf,0x7e,0xc2,0xa4,0x52,0x96,0x47, + 0xa3,0x06,0x85,0x58,0xb8,0xb1,0xf5,0xae,0x43,0xba,0x2f,0x91,0x1b,0xa5,0xfa,0x84, + 0x4a,0x29,0x15,0x5f,0xb3,0xa0,0x20,0xd9,0xc6,0xbe,0xb4,0x57,0xc4,0x68,0x57,0x45, + 0x76,0x72,0x23,0x23,0x40,0x6b,0x0d,0x30,0x24,0x73,0xd8,0xcd,0x8f,0xb7,0x9f,0x54, + 0x24,0x2d,0x83,0x04,0x4c,0x58,0x41,0x10,0x33,0xe3,0x72,0xdb,0x2c,0xcf,0x59,0x31, + 0x13,0x42,0xd2,0xc4,0xb4,0x18,0x46,0x61,0x80,0x85,0xa9,0x97,0x48,0x1e,0x0c,0x13, + 0x18,0x62,0x43,0xb0,0xe7,0xe9,0x61,0x42,0x18,0x1a,0xcd,0x78,0x80,0xae,0x97,0x5e, + 0xe9,0xc4,0x8e,0x64,0x9a,0x4c,0x78,0x4b,0xb9,0xcf,0x06,0x48,0x5a,0x66,0x4a,0x7b, + 0xb4,0x8f,0x12,0xf2,0xcf,0x11,0x54,0x28,0xbe,0x9c,0x21,0x93,0xeb,0x7a,0xf2,0x98, + 0xa7,0xe3,0xb4,0x03,0x24,0x28,0x93,0x86,0x15,0x52,0x9e,0x84,0xcb,0x22,0x39,0xa8, + 0x1c,0x09,0x51,0x40,0x07,0x4c,0x91,0x44,0xf0,0xbc,0x98,0x55,0x57,0xf9,0xb3,0xd2, + 0x40,0x6e,0xbb,0x1e,0x46,0x49,0x08,0x27,0x03,0x20,0x1b,0xe6,0x38,0xe4,0x84,0x9d, + 0x30,0x25,0x45,0x77,0xfb,0xfb,0x6f,0x80,0x10,0xf9,0x49,0x9e,0xb3,0x52,0x4f,0xa5, + 0xa4,0x18,0x48,0x34,0x9e,0xdf,0x02,0x69,0x2e,0x72,0x37,0xb2,0xc3,0x73,0x03,0x5d, + 0x7f,0x64,0xe4,0xb5,0x64,0x84,0xb3,0xad,0x22,0xd4,0xea,0x19,0x46,0xb5,0x3b,0x18, + 0xee,0x22,0x53,0xfb,0x3b,0xa6,0x41,0xa7,0xc9,0x10,0x15,0x62,0x4c,0x61,0x3f,0xf3, + 0x0a,0xd0,0x59,0xe9,0xfa,0xd1,0x46,0x45,0xab,0x75,0xc1,0x07,0xdc,0x01,0xc3,0xaf, + 0x49,0x59,0x2b,0xcc,0x9e,0x78,0x4b,0xbc,0x06,0x9b,0x87,0xa7,0x6b,0x0d,0x1b,0xfb, + 0x96,0x27,0xc1,0xf0,0xfb,0x3b,0x45,0x23,0x4c,0x66,0x97,0x58,0x47,0x4c,0x2b,0x33, + 0xba,0xe0,0xda,0x41,0x07,0x8f,0x30,0x57,0x21,0xb3,0xe2,0x77,0x96,0xce,0x38,0xcd, + 0x80,0x48,0xd0,0xac,0xd0,0x01,0x2a,0x06,0x0c,0x3d,0x03,0x02,0x3a,0x75,0x4d,0x35, + 0xe5,0x57,0x7d,0x8c,0x42,0x7a,0x0d,0x7b,0x35,0x05,0x84,0x17,0xfb,0x3d,0x03,0xe5, + 0xda,0xb0,0x87,0x08,0x52,0x7a,0x80,0xc0,0x9a,0xb8,0xd7,0xc1,0x0d,0x5b,0xe1,0x7d, + 0x3c,0x16,0x0b,0x46,0xa2,0xb7,0x32,0x94,0x89,0x6c,0x7e,0x82,0x48,0x1f,0x46,0xc0, + 0xcf,0x89,0x04,0x32,0xf7,0xdf,0xba,0x6f,0x2e,0x16,0x8b,0x26,0xca,0x7a,0x73,0x96, + 0x86,0xd2,0xca,0x01,0x08,0xd5,0x7d,0xe0,0x3e,0x3e,0xae,0xa8,0x0b,0x96,0x50,0x93, + 0xdb,0x81,0x2e,0xda,0x86,0xb5,0x96,0x69,0x81,0x6f,0x7e,0x6b,0xa8,0xad,0x93,0x31, + 0xc5,0xa7,0x84,0xb6,0x35,0x54,0x08,0xbe,0xca,0x54,0xf9,0x59,0x27,0xef,0xde,0x2c, + 0x15,0xda,0x25,0x4e,0x9f,0xb6,0xdb,0x2a,0xa7,0x58,0x63,0x69,0x1e,0x0d,0x53,0x70, + 0x89,0x7b,0x63,0xb8,0xd9,0x85,0x41,0xae,0x3a,0x4e,0x88,0x61,0x33,0x6a,0xb7,0xa1, + 0x4d,0x38,0x40,0x74,0x45,0xd7,0x10,0xe3,0xe1,0x2d,0x84,0x81,0x3e,0xbe,0xf6,0x86, + 0x77,0xb8,0xf5,0xbc,0x98,0xa8,0x9d,0x7a,0x39,0x0e,0xe6,0x24,0x40,0xe5,0x03,0x5f, + 0x6e,0xda,0xd3,0xd3,0x05,0xc0,0x0c,0xd2,0x6d,0x1d,0x2e,0xb2,0xba,0x5e,0xca,0x42, + 0xe0,0x16,0x36,0xb7,0x4a,0xc2,0x82,0xf7,0xa5,0x10,0x2b,0x9f,0x1c,0x81,0x6c,0x69, + 0xd0,0x01,0x9f,0xe0,0xc4,0x0a,0x12,0xfc,0xc0,0xa7,0xb1,0x25,0x7c,0x31,0x6c,0x83, + 0xa7,0x69,0x31,0x31,0x14,0xff,0x42,0xcd,0x36,0x37,0x54,0xa4,0x11,0xb2,0x4b,0x73, + 0xce,0xc5,0x7d,0xe4,0x01,0x4a,0xa3,0x5d,0x93,0x8a,0xd6,0xfd,0xa2,0x3e,0x48,0xec, + 0x6b,0x1b,0x2f,0x45,0xcc,0xf8,0xdf,0x2b,0x0f,0x9b,0xbb,0x28,0xfc,0xd8,0x13,0x00, + 0xcc,0x3b,0xe5,0x75,0x88,0xed,0xe8,0x9c,0xc0,0x56,0x87,0x71,0x34,0x96,0x46,0x06, + 0xd6,0x54,0xa0,0x1b,0x86,0xf0,0x6d,0x2e,0x9d,0x89,0xf4,0x4f,0x11,0x7d,0xcb,0xce, + 0xc1,0x9e,0x7e,0x7b,0xa5,0x7c,0x8f,0x3a,0x40,0x7f,0x5e,0x04,0xc8,0xae,0x7e,0x51, + 0x1e,0x53,0x22,0x9c,0x35,0x09,0xdd,0x82,0x3b,0x91,0x1f,0xc4,0xb3,0xf5,0x41,0x0d, + 0x2e,0x9d,0x01,0xb9,0x11,0x86,0x5f,0x36,0x43,0xaf,0x33,0xe0,0xe0,0x02,0x7d,0xc2, + 0xd1,0xe4,0x94,0xfc,0xd4,0x1b,0x6b,0x14,0x4c,0xae,0x0d,0x83,0x41,0x95,0x04,0x11, + 0x3c,0x9d,0x23,0x35,0xe8,0x1c,0x60,0xf4,0x0b,0x99,0x3f,0xa1,0x95,0xb5,0xe1,0xfe, + 0x6d,0x74,0x4d,0x03,0x16,0x25,0xff,0x03,0x3b,0xf0,0xdd,0x66,0x07,0x6f,0x6e,0x77, + 0xb8,0x13,0x78,0x74,0x33,0xcc,0xcc,0x41,0xbe,0x05,0x99,0x6b,0x6b,0xd7,0xce,0xbf, + 0xc7,0x06,0x51,0xb0,0x5d,0x8a,0x31,0x06,0x18,0x16,0xb7,0xba,0xc7,0xc7,0x56,0xc5, + 0xb8,0xd6,0xb3,0x08,0x03,0xed,0x93,0x72,0x6a,0x1f,0x1e,0x4a,0x6e,0xa9,0x4a,0xd4, + 0xc3,0x83,0xca,0x60,0x0e,0x10,0x8c,0x4b,0xce,0x1b,0x25,0x77,0x85,0xb3,0x4e,0xdc, + 0x9c,0x5a,0x04,0x64,0x70,0x03,0x32,0x2c,0xcf,0x2b,0x26,0x7d,0xda,0x20,0xd4,0xa5, + 0xfa,0x79,0x73,0x5e,0x03,0xbb,0x89,0x0c,0xe5,0xcb,0x54,0xcf,0xa6,0x09,0x10,0x6d, + 0x96,0x88,0xd5,0xac,0x7c,0x78,0xc8,0xb9,0x78,0xd6,0xde,0x46,0x8b,0x7c,0xd6,0x2f, + 0xf3,0xba,0x5f,0x5e,0x0a,0x54,0x19,0xdd,0x60,0x5e,0xd8,0xc1,0xe3,0x82,0xb3,0xc0, + 0xc4,0x32,0x67,0x93,0xef,0xa1,0x00,0x3f,0x46,0x00,0x9c,0x25,0xbf,0x91,0xe7,0xea, + 0x41,0xa8,0xf0,0x03,0x88,0xf5,0x75,0xee,0x4b,0x8a,0xe5,0xf0,0xaf,0xdc,0xb6,0x95, + 0x4f,0x07,0x53,0x10,0xe0,0x19,0xc7,0x3c,0x53,0x7e,0x67,0x8b,0xe4,0x1a,0x07,0x73, + 0xf5,0x99,0x39,0xec,0xbf,0xd0,0xfa,0x7b,0x65,0x2e,0xd1,0x09,0xdc,0x03,0x72,0xbf, + 0x77,0x70,0x03,0x4e,0x9a,0xaf,0xa2,0x39,0x99,0x2a,0x7c,0x87,0x14,0x8d,0x18,0xb8, + 0x96,0xfb,0xfd,0xfd,0x7b,0xb5,0x8f,0x71,0x6e,0x00,0x34,0xa7,0x34,0x62,0xcf,0xd8, + 0x69,0xde,0xac,0x8a,0x7d,0x93,0x33,0xa4,0x2c,0x8b,0xb6,0x74,0x72,0x81,0xbf,0xbd, + 0x73,0x9c,0xe7,0xdf,0x13,0x37,0xc1,0xd7,0xef,0x20,0x52,0x36,0x31,0x9b,0x2f,0x65, + 0xc3,0xee,0x80,0x7b,0x81,0x99,0x04,0x00,0x5d,0x7e,0x30,0x13,0xab,0x8a,0x81,0x76, + 0x0b,0x49,0xb1,0xaa,0x49,0xbf,0xa6,0x40,0x75,0x98,0x90,0x33,0x1d,0x08,0x87,0xff, + 0x41,0xcd,0x6f,0xd4,0x06,0x29,0x47,0xca,0xd5,0xae,0xb0,0xda,0x34,0x1d,0xe2,0xf7, + 0x1f,0x42,0x99,0x7c,0xc0,0x2f,0x71,0xf5,0xe4,0x25,0x7d,0x52,0x02,0x4c,0x00,0xe8, + 0x78,0x06,0x7e,0x9d,0x40,0x7f,0xc2,0x25,0x7e,0xf4,0x08,0x2c,0xa6,0x19,0x25,0x44, + 0x31,0x17,0x8e,0x81,0x05,0x18,0x0f,0xdc,0x4d,0xc6,0xdd,0x09,0x31,0x4c,0xe3,0x30, + 0xa4,0x43,0x99,0x10,0x26,0x70,0x3c,0x1e,0x06,0x06,0xa8,0x32,0x1e,0xc1,0x6c,0xb9, + 0xa7,0xe1,0x61,0xd2,0x58,0x92,0xd4,0xf0,0xe8,0x34,0x19,0x5a,0x30,0x3c,0x9d,0x9f, + 0x35,0x54,0x6a,0x1c,0xf7,0x44,0xee,0x4a,0x9b,0x1d,0x13,0x1e,0xfa,0xa6,0x08,0x6d, + 0x08,0x55,0xc0,0x1d,0x17,0x7b,0x46,0x10,0x97,0x2b,0x39,0x2c,0x0b,0xeb,0x0c,0xc0, + 0x37,0x02,0x08,0x7c,0x6c,0xad,0x4a,0x95,0xa0,0xad,0x89,0x5f,0x00,0x02,0x2f,0x1a, + 0x6e,0x59,0x57,0x45,0x30,0x36,0xa8,0xac,0x2e,0xf5,0x40,0xba,0x24,0x91,0x07,0x0e, + 0xa6,0xbe,0x6e,0xba,0xb3,0x93,0x19,0x40,0xac,0x59,0xf2,0xa4,0xaa,0x92,0x41,0xaa, + 0xfa,0x7a,0x8f,0xe6,0xaa,0xa5,0xaf,0x4c,0x46,0x61,0x74,0x91,0x96,0xf5,0xf6,0x47, + 0x72,0xa2,0x8f,0xcb,0x4b,0xcd,0x89,0xca,0x68,0x50,0x46,0x86,0xcc,0x4c,0x67,0x91, + 0xd0,0x1f,0x56,0x19,0xd0,0x5e,0x4c,0x6a,0xab,0x85,0x53,0x8e,0x40,0x86,0x5a,0x16, + 0xab,0xb8,0x3a,0x4c,0x35,0xe6,0xa7,0x04,0xe5,0x0b,0x89,0x03,0x90,0x8d,0xcb,0x1f, + 0x3f,0x5e,0xe9,0x53,0x84,0xa2,0x27,0xc5,0x47,0x85,0x9a,0x72,0xc3,0x0b,0x77,0xc4, + 0x04,0xf8,0x9a,0x80,0x67,0x24,0x22,0x22,0x00,0x66,0x20,0xe6,0xe2,0xa9,0xdc,0x40, + 0xd1,0xbb,0x25,0x99,0x17,0xde,0x81,0x93,0x9a,0x72,0xe9,0xde,0x00,0x3f,0x8d,0x00, + 0xe2,0xd0,0x36,0xa7,0xb7,0xf0,0x96,0xe5,0x33,0x0f,0x2a,0xe8,0x59,0xc2,0x94,0x62, + 0x70,0x45,0x78,0xc6,0x8b,0xe4,0x9a,0x7c,0x2c,0xc8,0x2c,0x28,0x69,0x20,0x90,0x14, + 0x10,0x5b,0x30,0x23,0xf8,0x79,0x41,0x7d,0xd0,0x11,0xd3,0x8d,0x2a,0x00,0x82,0x20, + 0xfa,0x12,0x59,0x02,0x0a,0x28,0x5d,0x1a,0xc4,0xe1,0x15,0xd7,0x55,0x0e,0xa5,0x25, + 0x4f,0x54,0xae,0xc2,0xd6,0xc1,0xb4,0x32,0xe9,0x45,0x4f,0x60,0xcb,0x2a,0x35,0x4b, + 0xb6,0x00,0xea,0xe5,0x89,0xa1,0x7a,0x5a,0xa8,0x54,0xad,0x44,0x95,0xb6,0xee,0x0a, + 0xdc,0xef,0xcc,0x43,0x10,0x9f,0x5a,0x7a,0x71,0x08,0x3f,0x03,0xfc,0xaf,0x03,0xfe, + 0xff,0x64,0x24,0x3e,0xcd,0x11,0x2f,0xb2,0xad,0xd3,0xaf,0x65,0xb2,0x74,0x0a,0xa3, + 0xbd,0x3d,0xb3,0xd1,0xd1,0x90,0x55,0x6e,0xe6,0x61,0xbe,0xcb,0xbd,0xa5,0xd3,0x18, + 0xe6,0x54,0x27,0x2d,0xf0,0x0c,0x81,0xe5,0x60,0x60,0xeb,0x0d,0xb9,0xd9,0xfa,0xe7, + 0x79,0xcb,0xce,0x51,0xb8,0x42,0xc4,0xd8,0x6e,0x7f,0x5f,0xfe,0x05,0x66,0x56,0x59, + 0xbf,0xca,0x0f,0x07,0x02,0x09,0xea,0x80,0x68,0x3d,0xa7,0x3e,0x37,0x0e,0x64,0xeb, + 0x03,0x83,0x6c,0xd4,0x2c,0x53,0x88,0x39,0x10,0xca,0xa7,0x90,0xba,0x40,0xb5,0x4a, + 0xdf,0x07,0x86,0x63,0xf4,0x6b,0xdd,0x17,0x93,0xcb,0x0d,0x16,0xc1,0x6a,0x05,0xae, + 0x37,0x03,0x86,0xd6,0xf7,0x4c,0xbf,0x72,0xff,0xfe,0xe2,0x07,0x8c,0x3a,0xa2,0x18, + 0xf5,0xe9,0x96,0x48,0x80,0x88,0x13,0x7d,0x19,0xc9,0xff,0xf7,0xad,0x75,0x3d,0x6f, + 0x83,0x67,0xf1,0x94,0x9d,0x16,0xcc,0x7c,0xd7,0x71,0x9c,0x77,0x47,0x98,0x40,0x6b, + 0x3b,0x0e,0x7a,0xf7,0x5f,0x05,0x2d,0x93,0xd6,0xc8,0xcd,0x21,0x99,0x7a,0x42,0xe8, + 0x17,0x54,0x61,0x21,0x81,0x4a,0xff,0x86,0xf2,0x51,0xee,0x6a,0x5d,0xd2,0xa6,0x6a, + 0xce,0x4a,0x65,0xac,0x74,0x52,0x41,0xde,0x3e,0x3c,0xc8,0xbf,0xfa,0xc4,0xcb,0x51, + 0x49,0x2f,0xf0,0xe4,0x0c,0x1d,0xeb,0xa1,0xd3,0x33,0x47,0xa5,0x43,0x33,0xd0,0xbe, + 0x42,0x94,0xeb,0x06,0x5b,0x4e,0xca,0xcc,0xcb,0x09,0x6f,0x39,0xcc,0x97,0xe0,0x6b, + 0xab,0xa3,0x43,0x43,0x54,0x4f,0x00,0x61,0x73,0xec,0x1e,0x2e,0x5f,0x21,0x17,0xe7, + 0x6e,0xa7,0xdd,0x2e,0xba,0x10,0xb4,0xfb,0x2e,0xc2,0xa6,0x71,0x10,0x58,0x7d,0xa1, + 0x0e,0xa9,0xb9,0x73,0x09,0x0d,0x45,0xa8,0x76,0xc5,0x6a,0xd8,0x4f,0xf2,0x22,0x87, + 0x7f,0x9a,0x5f,0x7a,0x6a,0x18,0x30,0x56,0x00,0x7d,0xbf,0x04,0xe8,0xd5,0xa7,0x52, + 0x09,0xce,0x6b,0x9b,0x4d,0x1c,0x56,0xfe,0xf1,0x8e,0x2f,0x85,0x2b,0xbf,0x82,0xed, + 0xe0,0xb5,0x59,0x66,0xbf,0x65,0x63,0x5c,0x8e,0x5f,0xdc,0x0e,0x94,0xfd,0x79,0x86, + 0x75,0x14,0x73,0x6b,0xac,0x95,0x6c,0x2d,0x3d,0x27,0x06,0x63,0x07,0x72,0x43,0x1f, + 0x9f,0x00,0xbf,0x0e,0x0c,0x77,0x6b,0x96,0xb3,0x3c,0xee,0x17,0x55,0xf7,0xab,0x84, + 0x98,0x1b,0x12,0x81,0x1f,0x8a,0xad,0xe1,0xd7,0x72,0xfa,0x42,0xae,0xcd,0x7f,0x4d, + 0xfe,0x82,0x12,0x17,0x34,0x49,0x79,0xc8,0x60,0xff,0x91,0x84,0x45,0x61,0x77,0x36, + 0xbf,0x91,0x9c,0xa3,0xd1,0xdc,0x3c,0x81,0xa0,0xa8,0x37,0xdc,0xea,0x26,0xc7,0xe8, + 0xef,0xb0,0x08,0xf7,0xe5,0x1c,0xc2,0xad,0x87,0x58,0x16,0xb3,0x78,0x74,0xf2,0x9e, + 0x6b,0x5d,0xef,0xe9,0x20,0x8e,0x40,0x9b,0xc2,0x5b,0x74,0x36,0xda,0x87,0x80,0x33, + 0xbe,0x13,0x8c,0xdf,0x83,0x36,0x84,0xcb,0xa2,0x2b,0x8a,0x4e,0xbd,0xfc,0xbd,0xd6, + 0x60,0x1c,0xc5,0xe4,0x7a,0x71,0xc3,0x85,0x0e,0xf7,0xea,0xcc,0x83,0xca,0x73,0xc9, + 0x04,0x02,0x9e,0x54,0x28,0xe3,0x6e,0x00,0x82,0xc2,0x1b,0xcb,0x13,0x93,0x6a,0xb2, + 0x65,0x10,0x0e,0xa6,0xab,0x8a,0xba,0xed,0x32,0x07,0xf2,0x14,0xc3,0xae,0x74,0x4e, + 0xd5,0x8a,0x94,0x6c,0xfc,0x4e,0xe1,0xd6,0x62,0x5c,0x56,0xb1,0x72,0x22,0x03,0x0f, + 0x2a,0xfe,0x5e,0x23,0x1a,0x69,0x15,0xdb,0x6c,0x30,0x13,0x95,0xb3,0x4d,0xc0,0xd1, + 0x40,0x47,0xe4,0xf9,0x7b,0x28,0x21,0x1f,0x7b,0xc3,0xa5,0xe6,0x17,0x76,0x24,0x8f, + 0x01,0xe4,0xef,0x18,0xa9,0x2c,0xb0,0xd2,0x47,0x84,0x73,0x52,0x4f,0x29,0xc6,0x06, + 0x46,0xf8,0xbf,0x67,0x1e,0x06,0xff,0xf4,0x59,0x86,0x26,0xb2,0x12,0x33,0xc0,0xd8, + 0xd1,0x00,0x0c,0x3a,0x00,0x14,0xda,0xc3,0x55,0xe7,0xaf,0x30,0x25,0x00,0x08,0x05, + 0xa8,0x71,0xf6,0xbe,0xb4,0xed,0x8e,0xdd,0xb5,0x0f,0xbf,0x6e,0xee,0xdb,0x04,0xfe, + 0x7d,0x8e,0x8a,0xcb,0x76,0x08,0x8a,0x71,0xd6,0x1a,0x3f,0x97,0x38,0x5b,0xb1,0x89, + 0x50,0x6f,0x6d,0xd7,0xd9,0xbe,0xc5,0x70,0x42,0x77,0x35,0x03,0x2f,0x79,0x5a,0xe7, + 0xb2,0xc4,0xc8,0x5b,0x41,0xf6,0xd6,0xe0,0x49,0xdb,0xe0,0x3c,0x7c,0xd2,0xd6,0xb3, + 0x1a,0x3c,0xcd,0xcb,0xc1,0x53,0xd5,0x74,0x7c,0x31,0x12,0x10,0x45,0x20,0xf0,0xa0, + 0x63,0x7d,0x75,0xe7,0xdf,0x77,0xda,0xbb,0x29,0xab,0xe1,0x5f,0xb5,0x4d,0x45,0x1b, + 0xab,0x74,0xc4,0xc7,0xfc,0xf9,0x5f,0xff,0x8d,0xb5,0x70,0xa3,0xc7,0x62,0xff,0x45, + 0x99,0xa2,0x4f,0x3a,0x47,0xaf,0x4e,0x7c,0xe9,0xb7,0x6b,0x94,0x94,0xf5,0x37,0xb2, + 0xf6,0x3a,0x59,0x0f,0x6a,0xea,0xb0,0x1f,0x23,0x4e,0xd4,0x63,0x4f,0xb9,0xa3,0x60, + 0xe6,0xb9,0xde,0x2a,0xfe,0x8f,0xff,0xdb,0xb1,0xfa,0x4c,0x26,0xb4,0x45,0x71,0x56, + 0x85,0xc1,0xea,0x60,0x82,0x07,0x63,0x35,0x10,0x42,0x95,0xf7,0x57,0x5c,0x70,0xdb, + 0x36,0x16,0x56,0x10,0x2f,0x3e,0x53,0xdc,0x32,0xfd,0x20,0xd5,0x3e,0x4b,0x37,0xb1, + 0x56,0xd8,0xe2,0xc0,0x85,0x47,0x0a,0x24,0xae,0x75,0x60,0x9c,0xf2,0xb9,0xbb,0xed, + 0xdc,0x8a,0x8a,0x82,0x37,0xcf,0xb3,0x40,0x1f,0xaf,0xda,0xe7,0x06,0xbe,0x4f,0x2f, + 0x33,0xda,0xf8,0x7a,0xbf,0x3e,0x0c,0x93,0xd3,0x48,0xe2,0x57,0xc9,0x32,0xe8,0x03, + 0x21,0x2d,0x01,0x88,0x7e,0x98,0x11,0x0b,0xc0,0x27,0x21,0xb5,0x9b,0xff,0x5c,0x02, + 0x49,0x48,0x31,0xe1,0xfe,0xee,0xe3,0x35,0xbe,0x83,0xeb,0x02,0x00,0x04,0xa8,0xf8, + 0xc6,0xc9,0x24,0x9d,0x5b,0x2d,0x1d,0xfc,0x90,0xdf,0x40,0xd8,0x61,0xe9,0x81,0x81, + 0xc8,0x36,0x08,0x33,0x83,0xd4,0xc5,0x2b,0x98,0x77,0xa7,0x07,0x9a,0x85,0xd7,0x4d, + 0x62,0x66,0x9d,0xef,0xc8,0xd6,0xf5,0x66,0xba,0x24,0x9f,0x89,0x5e,0xb8,0x9d,0xd3, + 0x29,0xa6,0x51,0x4d,0xb1,0x54,0x2d,0x7d,0x29,0xa1,0x02,0xca,0x7f,0xd4,0x3e,0xa2, + 0x13,0x1a,0x04,0x39,0xc1,0xeb,0xa8,0x4d,0xc1,0x2c,0x26,0xf3,0x5e,0xe4,0x94,0x8d, + 0xc2,0xc0,0xe7,0x13,0xaf,0xe6,0x55,0xf4,0x17,0x1e,0x9e,0x9a,0x01,0x2e,0xcd,0xbe, + 0xd9,0xc9,0xb3,0xc8,0xea,0xeb,0x0c,0x7f,0x43,0x27,0xb2,0x0f,0xa5,0x75,0xe0,0x1d, + 0xc1,0x4f,0xa3,0x03,0x81,0xa9,0xc4,0x36,0x9b,0x45,0x21,0xe6,0x96,0x3c,0x79,0x74, + 0x13,0x61,0xb7,0x4a,0x77,0xe0,0x96,0x3f,0xe6,0x4b,0xe4,0x21,0xb7,0x69,0xec,0x07, + 0x50,0x23,0xc5,0x03,0x7f,0x78,0x1e,0x00,0xeb,0x9a,0x17,0x61,0x76,0xf0,0x9e,0x8f, + 0xe8,0xa5,0x54,0xcc,0xf2,0x5b,0xce,0x63,0x2f,0x20,0xc0,0x98,0xb5,0x54,0x03,0x2f, + 0x9d,0x86,0xe5,0xe8,0xd4,0xc0,0x9b,0x5e,0x20,0x7d,0xd8,0xa9,0x71,0xde,0xec,0xf4, + 0x2a,0xa5,0x1f,0x51,0xcd,0x0d,0x10,0x96,0xb6,0x06,0x70,0x3e,0x9e,0x07,0x97,0xdf, + 0x0f,0xc5,0x2b,0x2f,0xcc,0xde,0xf1,0x25,0x5e,0xa1,0xab,0x55,0x97,0xf8,0x2f,0x26, + 0xa8,0x4b,0x08,0x9f,0x47,0xb2,0x46,0xeb,0x9f,0xcd,0xb7,0x3f,0xdc,0x7c,0xfe,0xf4, + 0x70,0x7b,0xf5,0xfe,0xea,0xf2,0xd3,0xc3,0xa7,0xab,0xdf,0x7f,0xba,0xf8,0x78,0x75, + 0x61,0xfd,0x46,0x9f,0x36,0xc8,0xdf,0x8b,0xf0,0xc6,0xb8,0x05,0x57,0xb6,0xc4,0x1c, + 0x8f,0x5d,0xe1,0xdc,0xde,0xc8,0x0f,0x27,0x83,0xa5,0xad,0x48,0xac,0x36,0xb9,0x5f, + 0x72,0xc7,0x45,0xff,0x90,0x89,0x99,0xf3,0x06,0x0c,0x79,0xba,0xbc,0x25,0x85,0x8d, + 0xd3,0x0b,0x78,0x62,0x6c,0x7c,0x87,0x12,0x53,0x21,0x00,0xa3,0xa1,0x9b,0xea,0x61, + 0x64,0xf3,0xb0,0x4d,0xdb,0x37,0xf9,0x11,0x66,0x93,0xd2,0xc0,0x50,0x52,0xfa,0x47, + 0x22,0x5a,0xf4,0x59,0x97,0xb3,0x96,0xfc,0x47,0x70,0xfe,0x3f,0x64,0x43,0x59,0x9a, + 0x15,0x67,0x00,0x00, }; constexpr size_t HOME_INDEX_HTML_GZ_LEN = sizeof(HOME_INDEX_HTML_GZ); // diff --git a/firmware/patternflow/src/knobs_index.h b/firmware/patternflow/src/knobs_index.h index a87cf8dc..27518ca7 100644 --- a/firmware/patternflow/src/knobs_index.h +++ b/firmware/patternflow/src/knobs_index.h @@ -12,101 +12,86 @@ static const char KNOBS_INDEX_HTML[] PROGMEM = R"HTML( - + Patternflow - Knobs
        -

        Knobs

        -
        -
        -

        Direction and click size

        - - - -
        PositionDirectionEdges per click
        -
        - - -
        -
        -

        Turn a knob clockwise. Its position should go up by one per click. - Goes down: set that knob to Reversed. Moves two per click, or needs two clicks to - move one: change Edges per click until one click is one step (most encoders are 4; - the edge counter under the position shows how many one click really makes). - These are properties of the encoder soldered in, stored on the device — nothing to - rebuild, and a saved change does not move the knob.

        -
        - +tr.all td{border-color:var(--rule)} +.pf-offline .pos,.pf-offline #st{opacity:.5} + +
        +

        Knobs

        -
        +

        Turn a knob one click clockwise: its position should go up by one.

        + + + + + +
        PositionDirectionEdges per click
        All
        +
        + + +
        +

        Goes down: Reversed. Moves two, or needs two clicks to move one: change edges per click +(most encoders are 4; the count under the position is what one click makes). Saved on the panel; saving does not move a knob.

        + +
        )HTML"; // GENERATED by firmware/toolchain/console_pages.py build — do not edit. -// gzip -9 of KNOBS_INDEX_HTML (8285 bytes → 3566 bytes), served with Content-Encoding: gzip. +// gzip -9 of KNOBS_INDEX_HTML (8774 bytes → 3712 bytes), served with Content-Encoding: gzip. static const uint8_t KNOBS_INDEX_HTML_GZ[] PROGMEM = { - 0x1f,0x8b,0x08,0x00,0x00,0x00,0x00,0x00,0x02,0xff,0xa5,0x5a,0x7d,0x73,0xdb,0xb8, - 0xf1,0xfe,0x5f,0x9f,0x02,0x51,0xfa,0x33,0xc5,0x33,0x45,0xbd,0xc4,0x49,0x1c,0x52, - 0x54,0xda,0xbc,0x5d,0xaf,0xb9,0x5c,0x33,0x71,0x3a,0x9d,0xc9,0xcd,0xcd,0x6f,0x20, - 0x11,0x94,0x78,0xa6,0x08,0x0e,0x01,0x5a,0x71,0x65,0xcd,0xf4,0x43,0xf4,0x13,0xf6, - 0x93,0xf4,0x59,0x80,0xa4,0x68,0xd9,0x79,0xe9,0x34,0x33,0xb6,0x49,0x60,0xb1,0x58, - 0x3c,0xfb,0xec,0x62,0x01,0x66,0xf6,0x20,0x96,0x4b,0x7d,0x5d,0x08,0xb6,0xd6,0x9b, - 0x6c,0xde,0x9b,0xd1,0x1f,0x96,0xf1,0x7c,0x15,0xf5,0x45,0xde,0xa7,0x06,0xc1,0xe3, - 0xf9,0x6c,0x23,0x34,0x67,0xcb,0x35,0x2f,0x95,0xd0,0x51,0xbf,0xd2,0xc9,0xf0,0x9c, - 0x3a,0xd5,0xb2,0x4c,0x0b,0xcd,0x54,0xb9,0x8c,0xfa,0xa3,0x22,0x19,0x2e,0x65,0xae, - 0x64,0x26,0xfc,0xdf,0xd5,0xf3,0x75,0x14,0x8f,0xa7,0x8b,0x47,0x8f,0xa6,0xd3,0xfe, - 0x7c,0x36,0xb2,0x82,0xf3,0x7a,0xc0,0x3c,0x4d,0x06,0x0f,0xb6,0x69,0x1e,0xcb,0xad, - 0xff,0xfe,0x8d,0x3b,0x48,0xaa,0x7c,0xa9,0x53,0x99,0x0f,0xdc,0xdd,0x15,0x2f,0xd9, - 0xdf,0x23,0xdb,0xe7,0xfd,0x2d,0x72,0x46,0xbc,0x48,0x47,0x4a,0x73,0x5d,0x29,0xc7, - 0xfb,0x39,0xfa,0xf5,0x37,0xef,0x5d,0x34,0x15,0xcf,0xbc,0x0b,0xef,0x93,0xf7,0x3e, - 0xfa,0x3b,0x86,0x47,0x3b,0xea,0x16,0x81,0x93,0xa5,0x57,0xc2,0xf1,0xe2,0xb4,0xd4, - 0xd7,0xc1,0x83,0x89,0x77,0x15,0xe4,0x55,0x96,0x79,0x2b,0xa1,0x83,0x1f,0xbd,0x34, - 0x4f,0xb2,0x74,0xb5,0xd6,0xc1,0x2f,0xd5,0x66,0x21,0x4a,0x6f,0x2d,0xb3,0x38,0xf8, - 0xeb,0xe2,0x77,0xb1,0xd4,0x9e,0xe2,0xd7,0x41,0x6b,0x81,0xf6,0x2e,0x3d,0xe1,0xee, - 0xc4,0xc9,0xc9,0x40,0xf8,0x5a,0x7c,0xd6,0x2f,0x65,0xae,0x45,0xae,0x23,0xed,0xee, - 0xbd,0x45,0xa5,0x3a,0xa2,0x0b,0xaf,0x70,0x77,0xcd,0x1b,0x4b,0x60,0xfc,0xc2,0x8f, - 0x53,0xc5,0x17,0x99,0x88,0xa3,0x07,0x93,0x7d,0xf7,0x6d,0x1c,0x16,0xbe,0x5e,0x8b, - 0x7c,0x90,0x78,0x89,0x1b,0x96,0x42,0x57,0x65,0xce,0x8a,0xbd,0x57,0xc8,0x2c,0x3b, - 0x68,0x54,0xde,0xc6,0x5b,0x7a,0xd2,0xa2,0xa0,0xbd,0xc4,0x5b,0x45,0x93,0xb0,0x9d, - 0xe1,0x03,0x66,0x58,0x9d,0x9c,0x3c,0x48,0x60,0x5b,0x12,0x4d,0xbc,0x65,0x26,0x78, - 0xf9,0x31,0xdd,0x08,0x59,0xe9,0x81,0x76,0xbd,0x81,0xf2,0x97,0x3c,0xcb,0x9e,0xab, - 0x81,0x1b,0xfc,0x38,0x50,0xae,0x5b,0xcf,0xd8,0x68,0x8f,0xdd,0xdd,0x72,0x10,0x7b, - 0x84,0x0a,0xd6,0xd2,0x36,0x0b,0x6a,0x36,0x50,0x09,0x77,0x5f,0x8f,0x79,0xeb,0xbd, - 0x75,0xdd,0x7d,0x3b,0xf3,0x5b,0xcc,0x9c,0x44,0xe3,0xf0,0x68,0xc6,0x10,0xd6,0x0c, - 0x74,0x04,0x4a,0x34,0x6d,0x1f,0xbc,0x0d,0xc6,0xc9,0x93,0x13,0xe9,0x27,0x69,0xa9, - 0x74,0x14,0x01,0x88,0xe7,0x18,0x1e,0xc0,0xf8,0x7a,0xd9,0x70,0x97,0x2c,0x82,0x8e, - 0xcb,0x57,0x77,0x35,0xef,0x3d,0x28,0x3d,0xc8,0xe4,0xee,0x6e,0x13,0xe5,0x61,0x72, - 0x73,0x03,0x55,0x7b,0x2f,0x97,0xdb,0xe0,0xc3,0x1e,0x32,0x86,0x16,0x07,0xb1,0x25, - 0x56,0x72,0x72,0xf2,0xb3,0x5f,0x54,0x6a,0x8d,0x97,0xf0,0xe2,0x39,0x5e,0x97,0x83, - 0x0b,0x37,0xf8,0xf4,0xfc,0x93,0x8f,0x51,0xb0,0xe3,0x53,0xf4,0xde,0x27,0xd0,0x07, - 0x7f,0xf3,0xde,0x79,0xaf,0xa0,0x6c,0xcb,0xf5,0x72,0x7d,0x71,0xa4,0x69,0xe3,0xee, - 0xde,0xfb,0x56,0x3d,0xec,0xfe,0xe4,0xc3,0x9a,0xc1,0xbb,0xe8,0x1d,0xd7,0x6b,0x7f, - 0x93,0xa2,0xdb,0x7b,0xe7,0x9a,0xa1,0xa9,0x7e,0x23,0xcb,0x57,0xe2,0x2a,0x5d,0x8a, - 0xc3,0x60,0xf8,0x4f,0x46,0xf2,0xe6,0x66,0xb7,0x6f,0x1c,0x9d,0x8b,0x2d,0x7b,0x5f, - 0xca,0x4d,0xaa,0xc4,0xc1,0x1d,0xd7,0x5e,0x6e,0x1d,0xbd,0x3e,0x98,0x34,0x15,0x8f, - 0x0e,0x8e,0x51,0xee,0x4e,0xc1,0xdd,0x03,0xe9,0x2f,0xaa,0x34,0x8b,0x4f,0x4e,0x94, - 0x7d,0x88,0xa2,0xba,0xc5,0x05,0xfc,0x6b,0x9f,0xe0,0x1c,0xb8,0xde,0x35,0x79,0x7c, - 0xef,0x86,0x1d,0x6f,0x74,0x30,0x6e,0xc4,0xc2,0x7c,0xf0,0xba,0x2c,0x65,0x39,0x70, - 0xb4,0x15,0x72,0x68,0x21,0xd2,0xaf,0xdf,0x6e,0x6e,0x9e,0x89,0x33,0x68,0xd9,0xef, - 0x0f,0xb4,0xfb,0x71,0x50,0x11,0x25,0xeb,0xa5,0x24,0x02,0x70,0x99,0x96,0x23,0x76, - 0x95,0xee,0x8e,0xa2,0xba,0xf4,0xe5,0xa5,0xab,0xd7,0xa5,0xdc,0x32,0x3b,0x51,0x59, - 0xe3,0xd8,0xb2,0xde,0xb0,0x83,0x22,0xeb,0x79,0x69,0xfe,0xc0,0x29,0x25,0x12,0x06, - 0x99,0xb9,0xef,0x70,0xee,0x15,0xad,0x1f,0x1a,0xf1,0xfb,0x02,0x71,0x5e,0x24,0xd6, - 0x47,0x91,0x0a,0x7f,0xf6,0x13,0x59,0xbe,0xe6,0x30,0xa3,0x9d,0x3c,0x01,0x3f,0x21, - 0x49,0x86,0xef,0xdd,0x81,0xdb,0xe6,0x9b,0x9e,0x4d,0x5c,0x39,0xdf,0x88,0xa8,0x7f, - 0x95,0x8a,0x6d,0x21,0x4b,0xdd,0x67,0xcb,0x3a,0xa6,0xfb,0xdb,0x34,0xd6,0x48,0x54, - 0xc6,0x81,0x43,0xf3,0x82,0x4c,0x91,0xea,0x94,0x67,0x43,0x85,0x58,0x12,0xd1,0x84, - 0xb2,0x9c,0x4e,0x75,0x26,0xe6,0xef,0xb9,0xd6,0xa2,0x44,0x1e,0xc1,0xd2,0x86,0xec, - 0x6d,0x2e,0x17,0x6a,0x36,0xb2,0x5d,0x48,0x84,0xfa,0x9a,0xfe,0x8e,0x7e,0x60,0xaf, - 0x78,0x79,0xc9,0xd2,0x5c,0xe9,0xb2,0xda,0x60,0x0e,0xa6,0xe5,0xa5,0xc8,0x15,0xfb, - 0xf7,0x3f,0xff,0xc5,0x94,0x10,0xac,0xb0,0x5a,0xd4,0xff,0x23,0xc3,0x89,0xcf,0xfe, - 0x9a,0x61,0x2d,0x0c,0x48,0xb2,0x5c,0x6a,0xe1,0xb3,0x1f,0x46,0xbd,0xa0,0x94,0x52, - 0xef,0x86,0xc3,0x65,0x29,0xf8,0x26,0x78,0x38,0x7e,0x39,0x7e,0x31,0x7e,0x16,0x0e, - 0x87,0x69,0x7e,0x19,0x3c,0x7c,0xfd,0xea,0xf5,0xd3,0x57,0x2f,0xf0,0xb6,0xa9,0xb4, - 0x88,0x83,0x87,0xe7,0x7f,0x3a,0x9f,0x3e,0x9d,0xe2,0x3d,0xe1,0x69,0xae,0x83,0x87, - 0x8f,0xff,0xf4,0xf8,0xf1,0xd9,0x93,0xb0,0x37,0x1c,0x96,0x55,0x26,0x82,0x87,0xd3, - 0xb3,0xe9,0x64,0x72,0x1e,0xda,0xd7,0xa1,0x92,0x09,0x64,0x26,0x2f,0x26,0xcf,0x26, - 0x67,0x68,0xcb,0x48,0xc3,0x9b,0x37,0x8f,0x5f,0x4e,0x5f,0xe3,0x4d,0x42,0xfd,0xe3, - 0xa7,0x2f,0xce,0x9f,0xbe,0xc1,0x4b,0xc1,0x73,0x91,0x41,0xf4,0xd1,0x64,0x32,0x19, - 0x93,0x3a,0xc5,0x73,0x15,0x38,0x3f,0x01,0xb7,0xd2,0xf1,0xaa,0xd4,0xbc,0x0f,0x95, - 0x28,0xd3,0xc4,0x53,0xd7,0x4a,0x8b,0xcd,0xb0,0x4a,0xbd,0x43,0x23,0x0d,0xd9,0xc8, - 0x5c,0x06,0xce,0x5f,0x84,0x7e,0x51,0xc2,0x38,0xc5,0xde,0xe1,0xdd,0x8c,0xa5,0x0e, - 0x55,0xf0,0xa5,0xf0,0x2e,0xde,0x50,0xe3,0xf0,0x83,0x58,0x55,0x19,0x2f,0xbd,0x77, - 0x22,0xcf,0xa4,0xd7,0x76,0xef,0x7b,0x3f,0xec,0x16,0xf2,0xf3,0x50,0xa5,0xff,0x48, - 0xf3,0x55,0xb0,0x90,0x65,0x2c,0xca,0x21,0x5a,0xf6,0xbd,0x85,0x8c,0xaf,0x77,0x1b, - 0x5e,0xae,0xd2,0x3c,0x18,0x87,0x0b,0xbe,0xbc,0x5c,0x95,0xb2,0xca,0xe3,0x00,0x01, - 0x35,0xa8,0xb1,0x73,0xc3,0xa5,0xcc,0x64,0x59,0x37,0x01,0x3e,0x37,0x4c,0xe0,0x78, - 0x40,0xb5,0x49,0xb3,0xeb,0xba,0x99,0x4c,0x76,0xc3,0x5e,0x96,0xe6,0x62,0xb8,0x16, - 0x66,0x8b,0x98,0xf8,0x8f,0xc3,0xe1,0x56,0x2c,0x2e,0x53,0xc8,0xd2,0x00,0xb5,0x81, - 0x47,0xd6,0x64,0x02,0xcf,0x89,0x1b,0x29,0x57,0x22,0xde,0xf7,0xfc,0x6d,0xc9,0x0b, - 0x18,0xf1,0xd9,0x32,0x27,0x78,0x72,0x3e,0x2e,0x3e,0x87,0x8d,0x51,0x8c,0x57,0x5a, - 0x86,0x05,0x8f,0x63,0x1a,0xf8,0x68,0x5a,0x7c,0x66,0x53,0xf4,0xb3,0x27,0x67,0x05, - 0xec,0xa7,0x5d,0x55,0x94,0x3b,0x6c,0x10,0x45,0x46,0xdb,0x4f,0x26,0x3e,0x87,0x50, - 0xbc,0xca,0x87,0x29,0xb0,0x54,0xc1,0x52,0x10,0xd2,0xe1,0x8a,0x17,0xc1,0x39,0x94, - 0xd6,0x6a,0xb0,0x76,0xad,0xe5,0x26,0x98,0x40,0x5b,0xd8,0x6b,0xf1,0xb0,0x6d,0xd0, - 0x8d,0xbd,0x37,0x8d,0x99,0x5d,0x18,0x39,0xdc,0x85,0x91,0x31,0xc8,0x64,0xed,0x7b, - 0x8a,0x41,0xf5,0x12,0xe9,0xf1,0x0e,0x68,0xa0,0x83,0x1b,0xd6,0x4a,0x4b,0x1e,0xa7, - 0x48,0x88,0x13,0x63,0xeb,0x64,0x67,0x61,0x48,0xff,0x21,0x82,0xc9,0x63,0x0c,0x35, - 0xaf,0x5b,0xab,0xea,0xc9,0x78,0xdc,0xae,0x39,0xa4,0x75,0x04,0x13,0xcc,0xaa,0xaa, - 0xc5,0xee,0x2e,0xd8,0xe4,0xd9,0xda,0x09,0x56,0x19,0xf4,0xdf,0x72,0x92,0x61,0x31, - 0xac,0x56,0xc2,0xc4,0x75,0xed,0xe1,0x21,0x6d,0x18,0xd3,0x27,0xc6,0x96,0xe9,0xee, - 0x68,0xf8,0xb1,0x2d,0x99,0xa0,0x00,0x1b,0x12,0x81,0x08,0x78,0x7f,0xfc,0x4c,0x6c, - 0x42,0xca,0x33,0x43,0x5d,0xc2,0xd7,0x08,0xb7,0x4d,0x50,0x15,0x85,0x28,0x97,0xf0, - 0x62,0xd8,0xeb,0xce,0x6e,0x62,0xca,0x3d,0x78,0x70,0xcc,0xce,0x69,0x4e,0x4d,0x7b, - 0x78,0x8d,0xe1,0x64,0x3c,0xfe,0xbf,0x06,0x23,0x0c,0xcd,0x78,0xa1,0x44,0xd0,0x3c, - 0x34,0x1d,0x64,0xee,0x7d,0xee,0x30,0xf1,0x87,0xd5,0xe9,0xf5,0x7f,0xbd,0x88,0x27, - 0xdf,0xbd,0x08,0x0b,0xa1,0x15,0x36,0x8c,0x0a,0x32,0x91,0xe8,0x96,0x88,0x4f,0x88, - 0x87,0x66,0x55,0xf1,0xae,0x69,0x7b,0x66,0xdb,0xc2,0x6f,0x13,0xca,0xae,0xa0,0xeb, - 0xc1,0x47,0x18,0x77,0x25,0x4a,0x9d,0x22,0x5d,0xd6,0xf3,0x6d,0xd2,0x38,0xce,0x04, - 0xcd,0xe0,0x5f,0x7e,0x0f,0x09,0xa6,0x47,0x24,0xa8,0xdd,0x60,0x01,0x7f,0x74,0x66, - 0x8d,0xc5,0x0e,0xa9,0xbe,0x47,0x19,0xd1,0xd3,0x8e,0x7c,0x36,0x3e,0x8c,0x64,0x6a, - 0x83,0xca,0xa8,0x0d,0xb7,0x45,0x26,0x97,0x97,0xdd,0x51,0xe3,0x2f,0xf2,0x30,0x03, - 0x13,0xcd,0xbc,0x41,0x9a,0xaf,0x91,0xd9,0x74,0xf8,0x9d,0x2b,0x6a,0xc0,0x85,0x41, - 0x44,0x23,0x84,0xeb,0x71,0xbc,0x99,0x1c,0x7b,0x4f,0x92,0xb2,0x6e,0xb8,0x3f,0xa0, - 0x8f,0xe2,0x73,0xda,0xae,0x96,0x88,0xd9,0xd8,0x1b,0x24,0x72,0x59,0xa9,0x1d,0xf6, - 0x73,0x4a,0x6b,0x41,0x2e,0x73,0xd1,0xe1,0x6c,0x3b,0x19,0xc5,0x7b,0x33,0xc4,0x37, - 0x65,0xf3,0xee,0x8b,0x52,0x3e,0x37,0x01,0xa9,0x6e,0x67,0xac,0x26,0x3d,0xdd,0x93, - 0xb9,0x3a,0x91,0x3b,0x31,0x2e,0x5c,0x54,0x60,0x55,0x7e,0x1b,0xca,0x65,0x55,0x2a, - 0x4c,0x54,0xc8,0x94,0xc6,0x34,0x32,0x7e,0x51,0xa6,0x18,0x0e,0x6b,0x8e,0x11,0x33, - 0xf0,0x74,0x8d,0xab,0xf3,0xfc,0x17,0x10,0x33,0xe2,0xbd,0xbb,0x88,0x35,0xae,0x41, - 0x16,0x64,0x93,0x27,0x4d,0xf8,0xb5,0x7c,0x3e,0xb6,0x23,0x58,0x4b,0x30,0x7c,0xf7, - 0xd5,0x7c,0x79,0x17,0xb1,0x23,0x1d,0xcd,0x51,0x60,0x27,0x29,0xa6,0x71,0x42,0xf1, - 0xcf,0x9a,0xe5,0xc7,0x22,0xe1,0x55,0xa6,0xdb,0x21,0x48,0x7e,0x32,0x8f,0x09,0x80, - 0xa3,0x38,0xbb,0xcb,0x94,0x83,0x49,0xc6,0xc9,0xbd,0xff,0x92,0x3a,0xc7,0x40,0xdc, - 0x35,0xa1,0x59,0xfb,0xfd,0xab,0x0c,0xef,0x2e,0xfb,0xe1,0x46,0xad,0xba,0x69,0xdb, - 0xc4,0xc2,0x77,0xee,0x04,0x28,0xad,0xdb,0x3d,0xd8,0x58,0x43,0xca,0x50,0xf9,0x0d, - 0x45,0x59,0xee,0xee,0x4c,0xd5,0x74,0xca,0xcb,0x5b,0x7d,0x28,0x3f,0xf7,0x54,0x87, - 0xc9,0x24,0x31,0xec,0x37,0xc5,0xd5,0x12,0x05,0xe9,0x46,0x38,0x0a,0x0f,0x69,0xc1, - 0x70,0xe0,0x43,0x46,0x90,0xa1,0xe9,0x02,0x83,0x62,0x44,0x0a,0x8b,0xd3,0x8d,0xa2, - 0xf2,0x8b,0x0e,0xc0,0x46,0xad,0x1d,0xcf,0x6c,0x02,0xf1,0x8e,0x9b,0x1f,0x2a,0xdd, - 0x71,0xe5,0xbe,0xf7,0xc7,0x8d,0x88,0x53,0x3e,0xa8,0xa9,0x8c,0x2d,0x81,0x8e,0xca, - 0xee,0xae,0x89,0x1b,0x56,0xd3,0x1f,0xaa,0x52,0x6a,0x08,0x4a,0x91,0x71,0x8d,0x03, - 0xeb,0x21,0xb4,0x6a,0x91,0x20,0xe0,0x09,0x34,0xec,0xea,0x1a,0x35,0xe8,0xf7,0xc3, - 0x76,0x10,0x5f,0xc0,0xab,0xc8,0x8e,0x21,0xca,0x29,0x9c,0x8f,0x86,0x94,0xcc,0xc7, - 0x7b,0xa8,0xa0,0xe2,0x71,0xf7,0x95,0x94,0x5a,0x6f,0x0a,0xf5,0xce,0x76,0x4e,0xc3, - 0x18,0x76,0x99,0x5b,0x45,0xcf,0xd9,0xe3,0x5a,0x11,0x5b,0xec,0xee,0x49,0xc7,0x47, - 0x5b,0xd4,0xbe,0x97,0xa0,0x28,0x82,0x9d,0x1d,0x5f,0x3f,0xea,0x70,0xea,0xe0,0xfc, - 0x6f,0x6c,0x8a,0x88,0xd0,0xff,0xa1,0x50,0xe0,0xf7,0x98,0xba,0xef,0xa1,0xfe,0x37, - 0xf5,0xf8,0x6c,0x64,0x6f,0x2f,0xa8,0x5a,0x9c,0xcf,0xe2,0xf4,0x8a,0x2d,0x33,0xae, - 0x14,0xea,0x7e,0x54,0x6e,0xcd,0xe5,0x86,0x28,0xe7,0x33,0x6c,0xb4,0x79,0xd3,0x87, - 0x82,0xc9,0xdc,0x58,0xa0,0x69,0x3e,0x5b,0x4f,0xe6,0x75,0xa5,0x8f,0xa7,0x5b,0x62, - 0xa8,0x70,0xfa,0x2c,0x8d,0xf1,0x00,0xf1,0x61,0x23,0x3f,0xaa,0x35,0xe2,0x44,0x60, - 0x4b,0x98,0x79,0x8f,0xb1,0xd9,0x7a,0x3a,0x7f,0x95,0x96,0xb6,0x81,0xf1,0x3c,0x86, - 0x8e,0x74,0x79,0xc9,0x68,0x69,0x18,0x31,0x35,0x32,0xa6,0xca,0xa0,0x27,0x7a,0xb6, - 0x66,0x6b,0x58,0xa6,0xd7,0x50,0x4a,0xbf,0xf0,0xf3,0xbe,0xe6,0x41,0xdb,0xd0,0x2a, - 0x6d,0x5b,0x5e,0xc7,0x2b,0xa1,0x18,0x0a,0x03,0x3b,0x85,0x6d,0x1f,0x91,0xa2,0x91, - 0x55,0x5a,0x4f,0x40,0x88,0x18,0xeb,0x71,0x50,0x53,0xb4,0x5c,0xd3,0x62,0x0c,0x19, - 0xb5,0x96,0x74,0x11,0xab,0x49,0xda,0xaf,0x15,0x58,0xae,0x36,0x9d,0x75,0x92,0xeb, - 0x33,0xba,0x49,0x8a,0xfa,0xb6,0xb3,0x46,0x87,0x5f,0x89,0x3e,0x6b,0xb2,0xdf,0xfc, - 0x02,0xaf,0xb3,0x91,0x15,0xb8,0x57,0x55,0x9b,0x79,0xee,0x53,0x56,0x0a,0x2a,0x35, - 0x3a,0xea,0x3e,0x98,0x86,0xae,0xc2,0xd9,0x08,0x46,0xb7,0xc6,0xd3,0x20,0xa4,0x09, - 0x5a,0x60,0xd3,0x5c,0x34,0x33,0x11,0xd5,0xd1,0xb1,0x98,0x7f,0xa4,0x53,0x29,0x67, - 0x97,0xf0,0x33,0xfa,0x50,0x19,0x6c,0x71,0x4e,0xf7,0xa1,0x74,0xce,0x7e,0xd2,0x00, - 0xb3,0x46,0x9d,0xa9,0xb5,0xac,0xb2,0x98,0xad,0x24,0xab,0x0a,0xb6,0xb8,0x66,0xc8, - 0xb8,0x07,0xa4,0x7d,0xe8,0xfe,0x51,0x02,0xfb,0x58,0x6e,0xf3,0x00,0x47,0x3d,0x9c, - 0xfc,0xd6,0x5c,0x5b,0xad,0x5a,0xb2,0x99,0xd8,0x58,0x6b,0x71,0x74,0x98,0x8d,0xf0, - 0xe2,0xe3,0x3c,0x74,0x05,0x79,0xbd,0x95,0x07,0x2d,0x1e,0xc3,0x69,0x30,0x17,0x22, - 0xb6,0xed,0xa6,0x0d,0x8f,0x12,0xca,0x37,0x90,0xa6,0x29,0x03,0xba,0x86,0xcb,0x57, - 0xc2,0x68,0xbc,0xe3,0x6e,0xb4,0xb1,0x0a,0x87,0x94,0xcc,0x58,0x67,0x69,0x96,0x2a, - 0xf3,0x82,0x43,0x5a,0xc1,0x06,0x1b,0xa9,0x34,0x13,0xf9,0x52,0x82,0xa5,0x8a,0xf1, - 0x52,0xb0,0xb3,0x10,0xda,0x29,0x13,0x0a,0x28,0xc3,0xd1,0xb8,0xa2,0x04,0x06,0x25, - 0x10,0x30,0xcd,0xdd,0xf5,0x6f,0x15,0xc3,0x2f,0xb6,0xe1,0xf9,0x75,0x67,0x02,0xa4, - 0xd0,0x2c,0xbb,0x46,0xeb,0xa5,0x50,0x2e,0x01,0xf1,0x71,0x2d,0x94,0x30,0xca,0x8b, - 0x52,0x16,0x54,0x1d,0xc2,0x4a,0x99,0xd8,0x59,0xec,0xdc,0x94,0x07,0xf0,0x47,0xc4, - 0x38,0x2b,0x7b,0xb0,0x4d,0xd2,0x23,0x26,0x21,0x11,0x7b,0x1e,0x67,0x27,0x9b,0x98, - 0xab,0x75,0x48,0x27,0x63,0x3a,0x7c,0x59,0x18,0x4a,0x61,0xae,0x3e,0x3c,0x13,0x46, - 0x9c,0x11,0xbd,0xe2,0x06,0x91,0x98,0xf0,0x87,0xb4,0xc5,0x8a,0x34,0x11,0xfa,0xf0, - 0x64,0x31,0xa7,0x94,0xd0,0x04,0xe4,0xcc,0x66,0xae,0xf9,0x8c,0xb3,0x75,0x29,0x92, - 0xa8,0x3f,0xea,0xcf,0xff,0x8c,0xed,0x61,0x36,0xe2,0x73,0x4c,0x8a,0xea,0x55,0xea, - 0x90,0x1d,0x7a,0xed,0xfd,0x45,0x7f,0x6e,0x2f,0x20,0x48,0x6a,0x36,0xaa,0x55,0xf4, - 0x6a,0x5e,0x35,0xf7,0x9a,0xbd,0xf6,0xfa,0xe2,0x0f,0x83,0xb4,0xbd,0x33,0x89,0x51, - 0x8e,0xd1,0x5d,0x80,0xbf,0x12,0xfa,0x75,0x26,0xe8,0xf1,0xc5,0xf5,0x4f,0x31,0x24, - 0xf6,0x87,0x01,0xd8,0x94,0xe8,0xf2,0xd1,0xdd,0xbd,0x7f,0xe3,0xd7,0xcf,0x37,0x37, - 0x4e,0x9a,0x27,0x38,0x33,0xff,0x61,0xe0,0x80,0xc5,0x74,0x55,0xd3,0xa3,0x0b,0x23, - 0xd4,0x0d,0x11,0x5d,0xd8,0x85,0xcc,0xfc,0x1b,0x8d,0xd8,0x96,0xa8,0xd6,0x81,0x6e, - 0xcd,0x95,0x91,0x34,0x55,0x5d,0x94,0xf0,0x0c,0xa7,0x04,0x2b,0x89,0x04,0x56,0xe5, - 0x0d,0x6a,0x92,0x64,0x6b,0xd0,0x0b,0xbe,0x12,0x66,0x0c,0x3a,0x81,0x76,0x3b,0x88, - 0xc6,0xb0,0xf7,0x7f,0xbd,0xf8,0x08,0x47,0xb1,0xfa,0x12,0x95,0x51,0x18,0xc3,0x69, - 0xb4,0x8f,0x62,0x43,0xec,0x2c,0x42,0x64,0xc8,0x49,0x9d,0xa5,0xc3,0xf2,0x34,0xbf, - 0x72,0x4e,0x6f,0x2f,0x55,0x64,0x17,0xd5,0xe2,0xb6,0x14,0xd2,0xe9,0x91,0x94,0xf1, - 0xf3,0xc0,0xdd,0xc1,0xe9,0xe6,0x3a,0x74,0x11,0x41,0x8c,0xd2,0x95,0x83,0xd3,0xcd, - 0xc2,0x4f,0xf3,0x5c,0x94,0x7f,0xfe,0xf8,0xee,0xe7,0xc8,0x71,0x88,0xc1,0x38,0x13, - 0x0d,0x48,0x2e,0x8d,0xc6,0x61,0x3a,0x3b,0x0b,0xd3,0xd3,0x53,0x33,0xb6,0x1e,0x5d, - 0x46,0xad,0x17,0xa8,0x68,0xd4,0xa2,0x76,0xc4,0xc0,0xd1,0x25,0x14,0xf6,0x6a,0x20, - 0xb1,0x9a,0x3a,0x7e,0x08,0x13,0x73,0x87,0x69,0x2a,0x04,0xe2,0xdf,0x2a,0xa5,0x78, - 0xe5,0x0d,0x6e,0x0a,0x94,0x69,0x88,0x09,0xa0,0x93,0x04,0xa4,0x4e,0x50,0x66,0x18, - 0x55,0xba,0xec,0x1a,0x38,0xd3,0x71,0x93,0x75,0x2e,0xfb,0xf3,0xb7,0xce,0xe9,0x20, - 0x3d,0x9d,0xb8,0xa7,0x0e,0x72,0x6d,0x3c,0x77,0x4e,0x7b,0xd6,0x8f,0x5d,0x31,0x04, - 0x5d,0xbf,0xde,0x6f,0x28,0x87,0xe1,0x15,0xe8,0x9c,0x3a,0x9d,0x9d,0xc6,0x9c,0x68, - 0x6c,0x56,0xe4,0xdb,0xba,0x13,0x7d,0xd4,0x3a,0xbf,0xab,0x17,0xf2,0xa6,0xce,0x37, - 0x03,0xac,0x47,0x30,0xe0,0x90,0x48,0x67,0xb2,0x30,0xa0,0x5f,0xf1,0xac,0x42,0xd2, - 0x1d,0xf7,0xe7,0xbf,0xe0,0x8c,0xc9,0xb3,0xd9,0xc8,0x76,0x1c,0x0b,0x4c,0xfa,0x9d, - 0x6c,0xd6,0x88,0x8c,0xec,0x1c,0xdf,0x9a,0xde,0xba,0xfa,0x6b,0xd3,0x9f,0xf5,0xe7, - 0x67,0x47,0xa9,0xca,0xfd,0x92,0x25,0xd3,0xfe,0x7c,0xfa,0x15,0x2b,0x27,0x5f,0x32, - 0xcf,0xba,0x1c,0x44,0xe2,0x38,0x47,0xe7,0xf1,0xcb,0x35,0xb1,0x4d,0x97,0x35,0x15, - 0x5a,0x22,0xfb,0x32,0xb7,0xd9,0x25,0x6a,0x59,0x7b,0x68,0xc2,0xa6,0x77,0xa9,0x68, - 0xc0,0xbe,0x87,0x9a,0x73,0xc4,0x10,0xba,0x26,0xe2,0xd8,0x20,0x97,0x8c,0xd0,0x21, - 0x5b,0x50,0xe9,0x49,0x1e,0xbb,0x88,0xd1,0x34,0x13,0x07,0xfa,0x58,0xc6,0x28,0x43, - 0x99,0x4e,0xe8,0x1e,0xf8,0x6f,0x94,0x1f,0xf8,0x8f,0x94,0x5b,0xc7,0xe4,0x37,0xc9, - 0xce,0xa3,0xc1,0xc1,0x7e,0x0b,0x45,0x14,0x39,0x13,0xc7,0x7d,0x10,0x45,0xc8,0x1d, - 0x60,0xe6,0xd5,0xaf,0xe9,0x6f,0xde,0x22,0x2a,0xa8,0x48,0xfd,0x09,0x41,0x70,0x58, - 0x9b,0x11,0xf7,0x26,0xe3,0x46,0x16,0xce,0x82,0xec,0x31,0x28,0x86,0xa5,0xbf,0xd0, - 0xad,0x2a,0x7f,0xee,0x98,0x25,0x3b,0x01,0xc2,0xf0,0xa0,0xe6,0x20,0xb0,0xe8,0x0a, - 0x18,0x35,0xb4,0x12,0xfc,0xdc,0xdc,0xf0,0x9b,0x9b,0x85,0x45,0x8f,0x74,0xeb,0x57, - 0x24,0x36,0x40,0x0f,0x7c,0x70,0x2b,0x5b,0xd4,0x3d,0x32,0x77,0x77,0x36,0xa1,0x35, - 0x38,0x47,0x32,0x0f,0x29,0x77,0x20,0x1b,0x39,0x6e,0xe7,0x6b,0x8b,0xcc,0x6f,0x6e, - 0x6c,0x1a,0xa3,0x6e,0x5b,0x35,0x1c,0x09,0x74,0x27,0xc0,0x96,0x46,0x1f,0x47,0x60, - 0x05,0x65,0xd6,0xf8,0xeb,0x00,0x43,0xa1,0x8d,0x46,0xf7,0xd6,0xe7,0xa1,0xd8,0xb7, - 0x9b,0x75,0x0b,0x16,0x4d,0x6c,0x02,0xf3,0x58,0x0e,0xad,0x10,0x3a,0x75,0xcc,0x7e, - 0xab,0x9c,0x63,0x68,0x5b,0x23,0x0f,0x60,0xb6,0x4d,0xad,0xff,0x19,0xa3,0xbb,0x76, - 0x83,0x41,0x6d,0x56,0x57,0x85,0x75,0x79,0x5c,0xfb,0xf9,0x39,0x3c,0x1f,0x38,0xe3, - 0x7a,0x22,0xc6,0x8e,0x7c,0x1d,0x5d,0xe8,0x12,0x40,0x0d,0xe2,0xda,0xd5,0x75,0x00, - 0xec,0x6b,0xbf,0x80,0xd5,0xa5,0x18,0xfe,0x5e,0xc1,0xd6,0x98,0x09,0xbe,0x5c,0x37, - 0xe9,0x30,0x80,0x1f,0xb1,0x31,0x53,0x91,0xc0,0x17,0x20,0xeb,0x35,0x36,0x1d,0xda, - 0x14,0x68,0x6b,0xe9,0x19,0xfb,0xac,0x79,0x35,0x8d,0x43,0x01,0xd3,0x0d,0xac,0x5d, - 0x48,0xef,0x63,0xd4,0x7d,0x24,0xb2,0xd4,0x21,0x57,0x93,0x1f,0xbb,0x78,0xda,0xaf, - 0x7e,0x44,0x98,0xae,0xa7,0x11,0xa1,0xe4,0x8d,0xa8,0xf3,0x05,0xa4,0xa5,0x91,0x01, - 0xd1,0x0d,0x61,0x20,0xbc,0xed,0x1a,0xe7,0xd3,0x43,0x48,0x9b,0xae,0xe3,0xb8,0xfb, - 0xb0,0x77,0xa0,0xd4,0x3d,0x7a,0xea,0x70,0xa4,0x72,0x39,0xfa,0xf5,0xb7,0xaf,0x73, - 0x85,0x84,0xec,0xd7,0x28,0xa7,0xc9,0xb9,0x91,0x73,0x7a,0xe4,0xa8,0x1a,0xf0,0x8e, - 0x6c,0x93,0x20,0xad,0x6c,0xd7,0x59,0x6e,0x1b,0x2e,0x76,0x93,0xbe,0x87,0xfc,0xba, - 0xac,0x6c,0x8a,0x30,0x1f,0x6a,0xec,0xf7,0x53,0x2a,0x83,0x94,0xe3,0xed,0x68,0xc3, - 0x92,0x71,0xe0,0xd0,0x76,0xee,0x78,0x66,0x5a,0x7b,0x5e,0x51,0xc1,0xce,0xa9,0x11, - 0x1d,0x7e,0x44,0xd9,0x0d,0xc2,0x20,0x2d,0x62,0xe9,0x9c,0x56,0x3d,0xfa,0x3c,0xdc, - 0x6e,0xb7,0x43,0xba,0x75,0x1c,0x56,0x65,0x66,0x53,0x72,0xec,0xec,0x3d,0xb2,0x39, - 0x30,0x86,0xff,0x8e,0x73,0xef,0xc0,0x39,0x01,0x7c,0xae,0x51,0x7b,0xf7,0xe3,0x50, - 0xbd,0xd5,0x1f,0x3e,0xf5,0xdc,0xfd,0x3a,0xd9,0x10,0xb4,0x5b,0x80,0xd4,0x6d,0x86, - 0xee,0xf4,0x69,0x69,0x47,0x6e,0x8a,0x7d,0x41,0xdf,0x96,0x50,0x1f,0xe1,0x1c,0x88, - 0x45,0x3b,0x9e,0x83,0x06,0x6c,0xe3,0xf7,0xe0,0x61,0xd5,0xd8,0xd9,0xf7,0x6d,0x08, - 0xdc,0xe6,0x42,0x9d,0x01,0x2c,0x05,0x0c,0x83,0xa1,0x51,0x5e,0x36,0x75,0x01,0x6c, - 0x5d,0xd2,0x57,0xc2,0xee,0x07,0xb5,0x5b,0x46,0x9a,0x71,0xd6,0x14,0x36,0xac,0xab, - 0xa9,0x5c,0x64,0xc8,0xf1,0xb1,0xa9,0x47,0x79,0xae,0xb6,0xf4,0xdd,0xe4,0x1b,0x56, - 0xee,0x29,0xf1,0x85,0xbd,0xba,0xf0,0x09,0x69,0x47,0x79,0xb9,0xc6,0x41,0xf7,0x50, - 0x19,0xb7,0xf7,0x18,0x19,0xa7,0x93,0x00,0xea,0x5f,0xfa,0x1a,0x85,0x73,0x3c,0x0e, - 0x93,0xba,0x24,0x8f,0xa3,0x42,0x49,0x35,0xaa,0x63,0xb6,0x46,0x1d,0x2b,0x72,0x0a, - 0x4e,0xd2,0x53,0xf0,0x4a,0x35,0x5d,0x28,0x9c,0xad,0xae,0xb4,0x40,0x30,0xd3,0x17, - 0x31,0xc5,0x0c,0x9e,0xca,0xef,0x21,0xb3,0x9a,0x4f,0x91,0xb7,0x78,0x73,0x36,0x1e, - 0x7b,0x5d,0x3f,0x51,0x6c,0xbb,0x35,0x66,0x64,0xf3,0xe1,0xf3,0xfe,0xc8,0x9e,0xb4, - 0x47,0xf6,0x3f,0x13,0xfc,0x07,0xc4,0xf4,0xe0,0x5a,0x5d,0x20,0x00,0x00, + 0x1f,0x8b,0x08,0x00,0x00,0x00,0x00,0x00,0x02,0xff,0xb5,0x5a,0xeb,0x72,0xdb,0x38, + 0x96,0xfe,0xaf,0xa7,0x40,0x94,0x59,0x93,0x6c,0x93,0x94,0xe8,0xf8,0x16,0x52,0x54, + 0xd6,0xd7,0xde,0xae,0xc4,0x3d,0xae,0x38,0x5d,0x53,0x95,0x54,0x7e,0x50,0x22,0x28, + 0x21,0xa6,0x08,0x16,0x01,0x49,0xf6,0xc8,0xaa,0x9a,0x87,0x98,0x27,0xdc,0x27,0xd9, + 0x73,0x00,0x90,0xa2,0x64,0x3b,0xd3,0x7b,0xab,0x9a,0x71,0x84,0xdb,0xb9,0x7e,0xe7, + 0xc3,0x85,0x3d,0x78,0x93,0xf2,0xb1,0x7c,0x2c,0x29,0x99,0xca,0x59,0x3e,0xec,0x0c, + 0xf0,0x1f,0x92,0x27,0xc5,0x24,0xee,0xd2,0xa2,0x8b,0x1d,0x34,0x49,0x87,0x83,0x19, + 0x95,0x09,0x19,0x4f,0x93,0x4a,0x50,0x19,0x77,0xe7,0x32,0xf3,0x4e,0x71,0x50,0x8c, + 0x2b,0x56,0x4a,0x22,0xaa,0x71,0xdc,0xed,0x95,0x99,0x37,0xe6,0x85,0xe0,0x39,0xf5, + 0x7f,0x88,0x0f,0xd3,0x98,0x66,0x59,0x96,0x1e,0xf5,0x8f,0xba,0xc3,0x41,0x4f,0x4f, + 0x1c,0x9a,0x05,0x43,0x96,0xd9,0x6f,0x96,0xac,0x48,0xf9,0xd2,0xbf,0xbd,0x76,0xec, + 0x6c,0x5e,0x8c,0x25,0xe3,0x85,0xed,0xac,0x16,0x49,0x45,0xfe,0x16,0xeb,0x31,0xf7, + 0x8f,0xd8,0xea,0x25,0x25,0xeb,0x09,0x99,0xc8,0xb9,0xb0,0xdc,0x4f,0xf1,0xb7,0xef, + 0xee,0x4d,0x7c,0x40,0xdf,0xbb,0x77,0xee,0x57,0xf7,0x36,0xfe,0x1b,0x2c,0x8f,0x57, + 0x38,0x4c,0x43,0x2b,0x67,0x0b,0x6a,0xb9,0x29,0xab,0xe4,0x63,0xf8,0x26,0x70,0x17, + 0x61,0x31,0xcf,0x73,0x77,0x42,0x65,0xf8,0xab,0xcb,0x8a,0x2c,0x67,0x93,0xa9,0x0c, + 0x7f,0x9f,0xcf,0x46,0xb4,0x72,0xa7,0x3c,0x4f,0xc3,0xbf,0x8e,0x7e,0xd0,0xb1,0x74, + 0x45,0xf2,0x18,0x36,0x16,0x48,0xf7,0xde,0xa5,0xce,0x8a,0xee,0xed,0xd9,0xd4,0x97, + 0xf4,0x41,0x5e,0xf0,0x42,0xd2,0x42,0xc6,0xd2,0x59,0xbb,0xa3,0xb9,0x68,0x4d,0x1d, + 0xb9,0xa5,0xb3,0xaa,0x5b,0x24,0x03,0xe3,0x47,0x7e,0xca,0x44,0x32,0xca,0x69,0x1a, + 0xbf,0x09,0xd6,0xed,0x56,0x3f,0x2a,0x7d,0x39,0xa5,0x85,0x9d,0xb9,0x99,0x13,0x55, + 0x54,0xce,0xab,0x82,0x94,0x6b,0xb7,0xe4,0x79,0xbe,0x91,0x28,0xdc,0x99,0x3b,0x76, + 0xb9,0x8e,0x82,0x74,0x33,0x77,0x12,0x07,0x51,0xa3,0xe1,0x33,0x68,0x98,0xec,0xed, + 0xbd,0xc9,0xc0,0xb6,0x2c,0x0e,0xdc,0x71,0x4e,0x93,0xea,0x0b,0x9b,0x51,0x3e,0x97, + 0xb6,0x74,0x5c,0x5b,0xf8,0xe3,0x24,0xcf,0x3f,0x08,0xdb,0x09,0x7f,0xb5,0x85,0xe3, + 0x18,0x8d,0xb5,0xf4,0xd4,0x59,0x8d,0xed,0xd4,0xc5,0xa8,0x80,0x2f,0x4d,0x37,0xc5, + 0x6e,0x15,0x2a,0xea,0xac,0xcd,0x9a,0x8f,0xee,0x47,0xc7,0x59,0x37,0x9a,0x3f,0x82, + 0xe6,0x2c,0xee,0x47,0x3b,0x1a,0x23,0xb0,0xc6,0x96,0x31,0x40,0xa2,0xee,0xfb,0xec, + 0xce,0x60,0x1d,0xdf,0xdb,0xe3,0x7e,0xc6,0x2a,0x21,0xe3,0x18,0x02,0xf1,0x01,0x96, + 0x87,0x60,0xbc,0x71,0x1b,0xd2,0xc5,0xcb,0xb0,0x95,0xf2,0xc9,0x73,0xc9,0x6b,0x17, + 0x84,0x6e,0xe6,0x14,0xce,0x6a,0x16,0x17,0x51,0xf6,0xf4,0x04,0xa2,0xd6,0x6e,0xc1, + 0x97,0xe1,0xe7,0x35,0xcc,0x51,0xb0,0xd8,0x4c,0x1b,0x83,0x27,0x7b,0x7b,0x9f,0xfc, + 0x72,0x2e,0xa6,0xd0,0x88,0xee,0x3e,0x40,0x73,0x6c,0xdf,0x39,0xe1,0xd7,0x0f,0x5f, + 0x7d,0x58,0x05,0x76,0x7c,0x8d,0x6f,0x7d,0x0c,0xba,0xfd,0x87,0x7b,0xe3,0x5e,0x82, + 0xb0,0x65,0x22,0xc7,0xd3,0xbb,0x1d,0x49,0x33,0x67,0x75,0xeb,0x6b,0xf1,0x60,0xf7, + 0x57,0x1f,0xac,0xb1,0x6f,0xe2,0x9b,0x44,0x4e,0xfd,0x19,0x83,0x61,0xf7,0xc6,0x51, + 0x4b,0x99,0xbc,0xe6,0xd5,0x25,0x5d,0xb0,0x31,0xdd,0x2c,0x86,0xfc,0xf1,0x98,0x3f, + 0x3d,0xad,0xd6,0x75,0xa2,0x0b,0xba,0x24,0xb7,0x15,0x9f,0x31,0x41,0x37,0xe9,0x78, + 0x74,0x0b,0x9d,0xe8,0xe9,0xc6,0xa4,0x03,0xfa,0x6e,0x93,0x18,0xe1,0xac,0x04,0xa4, + 0xdb,0xe6,0xfe,0x68,0xce,0xf2,0x74,0x6f,0x4f,0xe8,0x1f,0x71,0x6c,0x7a,0x1c,0x08, + 0xff,0xd4,0xc7,0x70,0xda,0x8e,0xfb,0x88,0x19,0x5f,0x3b,0x51,0x2b,0x1b,0xad,0x18, + 0xd7,0xd3,0xa2,0xc2,0xbe,0xaa,0x2a,0x5e,0xd9,0x96,0xd4,0x93,0x2c,0x74,0x84,0xfb, + 0xa6,0xf5,0xf4,0xf4,0x9e,0x1e,0x82,0x94,0xf5,0x7a,0x03,0xbb,0x5f,0xed,0x39,0x42, + 0xd2,0xb8,0x92,0x51,0x08,0x97,0xea,0xd9,0x41,0x57,0xe5,0xac,0xb0,0xaa,0x2b,0x9f, + 0xdf,0x3b,0x72,0x5a,0xf1,0x25,0xd1,0x8a,0x2a,0x13,0xc7,0x06,0xf5,0x0a,0x1d,0x58, + 0x59,0x1f,0x2a,0xf5,0x0f,0x24,0xa5,0x02,0xc2,0x40,0x33,0xd7,0x2d,0xcc,0x5d,0xa2, + 0xff,0x20,0x11,0xfe,0xde,0x41,0x9d,0x97,0x99,0xce,0x51,0x2c,0xa2,0x4f,0x7e,0xc6, + 0xab,0xab,0x04,0xcc,0x68,0x94,0x67,0x80,0x4f,0x98,0x89,0x86,0xaf,0x1d,0xdb,0x69, + 0xf8,0xa6,0xa3,0x89,0xab,0x48,0x66,0x34,0xee,0x2e,0x18,0x5d,0x96,0xbc,0x92,0x5d, + 0x32,0x36,0x35,0xdd,0x5d,0xb2,0x54,0x4e,0xe3,0x54,0x25,0xd0,0x53,0x0d,0x60,0x0a, + 0x26,0x59,0x92,0x7b,0x02,0x6a,0x89,0xc6,0x01,0xb2,0x9c,0x64,0x32,0xa7,0xc3,0xdb, + 0x44,0x4a,0x5a,0x01,0x8f,0x80,0x6b,0x1e,0xf9,0x58,0xf0,0x91,0x18,0xf4,0xf4,0x10, + 0x10,0xa1,0x7c,0xc4,0x7f,0x7b,0xbf,0x90,0x0b,0x4d,0x81,0x64,0x94,0x08,0x1a,0x12, + 0x88,0x11,0x11,0xa0,0x9d,0x8c,0x72,0x3e,0xbe,0x27,0xbc,0xa4,0x85,0x20,0x74,0x41, + 0xab,0x47,0x30,0xa2,0xa2,0xa4,0x4c,0x26,0xd4,0x27,0x9f,0x90,0x9a,0x08,0x2b,0x48, + 0x58,0x71,0x2e,0x89,0x8d,0xab,0xc6,0x10,0xc3,0x19,0xb5,0x44,0x87,0x10,0x18,0x41, + 0xa2,0xa2,0x29,0x51,0x1c,0x06,0x8b,0x72,0x0a,0xb6,0x10,0x24,0x31,0x81,0x2a,0x84, + 0x51,0xb2,0x48,0xf2,0x39,0x15,0x21,0xb9,0xa7,0xb4,0x54,0xaa,0xe5,0x92,0xa3,0x58, + 0x21,0x69,0xe9,0xb8,0x28,0x29,0x4d,0xaa,0x7b,0x32,0x2f,0x52,0x5a,0x29,0xc2,0xff, + 0x96,0x26,0x32,0xf1,0x60,0x26,0xc4,0x07,0x87,0xbe,0xfb,0xe4,0x77,0x4e,0x96,0x74, + 0x44,0x32,0x08,0x91,0xd0,0xf6,0x4f,0xb9,0x14,0x25,0x98,0x35,0x4d,0x04,0x29,0x50, + 0x1e,0x86,0x81,0x4a,0x9f,0xfc,0xd2,0xeb,0x28,0x83,0x57,0x63,0x9e,0xf3,0x0a,0x22, + 0x86,0x72,0x42,0x65,0x63,0xe4,0x79,0xe3,0x8a,0x26,0xb3,0xf0,0xed,0xf5,0xe1,0xd5, + 0xf5,0xd5,0x31,0xb4,0xcb,0xa4,0xa0,0x39,0xb4,0xaf,0xaf,0x2f,0xae,0xcf,0xa0,0xcd, + 0x8a,0xfb,0xf0,0x6d,0x70,0x16,0x9c,0x06,0x87,0xd0,0x9a,0xcd,0xc1,0xbf,0xf0,0xed, + 0xf1,0xc1,0xd1,0xc5,0x51,0x00,0xed,0x2c,0x01,0x3d,0xe1,0xdb,0x93,0xcb,0x93,0xe3, + 0xe3,0x53,0x68,0x57,0xf3,0x9c,0x86,0x6f,0x2f,0x2f,0x2f,0x8f,0x2e,0x8e,0x4d,0xd3, + 0x13,0x3c,0x83,0x29,0x57,0xa7,0x57,0xc1,0x25,0x8a,0x98,0x4c,0xb9,0xc0,0xf6,0xbb, + 0xcb,0x8b,0x8b,0x2b,0x68,0xe7,0x28,0xf0,0xfa,0xfa,0xe8,0xe2,0x00,0x5b,0xa3,0x04, + 0x5a,0xe7,0xa7,0xef,0x2e,0x82,0x0b,0x68,0x71,0xd0,0x7d,0x70,0x7e,0x72,0x76,0x78, + 0x0e,0x8d,0x65,0x52,0x15,0xe1,0xdb,0xd3,0xa3,0xe3,0x7e,0xff,0x2a,0xea,0x78,0x9e, + 0x48,0x0a,0x11,0x8a,0x47,0x88,0xda,0xcc,0x9b,0x33,0xd7,0x4b,0xca,0x12,0xb5,0xa9, + 0x0e,0xd7,0xba,0xa3,0x13,0x4e,0xc9,0x1f,0xbf,0x59,0xee,0x67,0x3e,0xe2,0x92,0xbb, + 0x38,0xdd,0x13,0xb4,0x62,0x19,0xfa,0xc1,0x0b,0x1e,0xce,0x99,0xfa,0x57,0x94,0xc9, + 0x98,0xba,0xd6,0x45,0x02,0x58,0x4a,0x59,0x42,0x6e,0xa0,0xcf,0x02,0x01,0xd7,0xe6, + 0xd7,0x0d,0x2d,0x72,0xee,0x6a,0xc0,0x24,0xc2,0xb5,0xb4,0x3c,0x33,0xd8,0x08,0x58, + 0x77,0x5e,0xcc,0xd4,0x76,0xd0,0xb1,0x6b,0x13,0xf3,0xfe,0x45,0xff,0xbc,0xff,0x7e, + 0x13,0xf3,0xe0,0x5d,0x10,0x04,0xfd,0x3a,0xe6,0x57,0x97,0x57,0x27,0x97,0xe7,0x9b, + 0x98,0x9f,0xbd,0x7b,0x7f,0x7e,0x7a,0xd6,0x8a,0xf9,0xd9,0xc9,0xbb,0xe3,0xc3,0x26, + 0xe6,0x07,0xe7,0x07,0x27,0x07,0xc1,0x76,0xcc,0x83,0xeb,0xe0,0x22,0x38,0xdd,0xc4, + 0xfc,0xe0,0xec,0xe0,0xf8,0xa0,0x5f,0x47,0xf9,0xfa,0xfa,0xe4,0xec,0xe8,0xc8,0x44, + 0xf9,0xe8,0xe4,0xfc,0xf4,0xe4,0xba,0x89,0xf2,0xe5,0xfb,0xb3,0xfe,0xbb,0xeb,0x75, + 0xe7,0x97,0xd5,0x88,0x3f,0x78,0x82,0xfd,0x9d,0x15,0x93,0x70,0xc4,0x2b,0x40,0xa4, + 0x07,0x3d,0xeb,0x6f,0x53,0x96,0xa6,0xb4,0xf8,0xbe,0x82,0xbd,0xb3,0xcc,0x61,0x67, + 0x2e,0x78,0x41,0xdf,0xb0,0x19,0x16,0x6e,0x52,0xc8,0x75,0x67,0xc4,0xd3,0xc7,0xd5, + 0x2c,0xa9,0x26,0xac,0x08,0xfb,0xd1,0x28,0x19,0xdf,0x4f,0x2a,0x0e,0x88,0x0e,0x81, + 0x4a,0x6d,0x13,0x01,0x27,0x52,0xc1,0x31,0x5d,0xe0,0xb4,0x13,0x21,0x9e,0xc3,0xe0, + 0xa8,0x7c,0xe8,0x05,0xfe,0x11,0xd1,0x03,0x98,0x38,0x27,0xf2,0x00,0xee,0xf7,0x4c, + 0x7a,0x38,0xc3,0x13,0x33,0xc0,0xf3,0x14,0x6d,0x02,0x65,0x40,0x03,0x0c,0xaa,0x38, + 0x5d,0x77,0x12,0x1d,0xed,0x90,0x15,0x53,0xc8,0xb3,0x5c,0x87,0x19,0x1f,0xcf,0x85, + 0xb7,0x60,0x82,0xc1,0xfe,0xbe,0x02,0xe6,0xcc,0x59,0x41,0xc3,0x83,0xf2,0x81,0x40, + 0x2e,0x59,0x6a,0x14,0x00,0x02,0x9d,0xc8,0x0c,0x7a,0x3c,0xcb,0x70,0x53,0x83,0x39, + 0xeb,0x8e,0xbf,0xac,0x92,0x12,0xbc,0x78,0xd0,0xa4,0x13,0x9e,0x1c,0xf4,0xcb,0x87, + 0xa8,0xf6,0x8a,0x24,0x73,0xc9,0xa3,0x32,0x49,0x53,0x34,0x04,0x87,0x48,0x70,0x0c, + 0x7f,0x0e,0x4f,0xd5,0xda,0x91,0x2c,0x9a,0xe8,0xb0,0x42,0xc9,0xce,0x72,0xfa,0x10, + 0x81,0xb5,0x93,0xc2,0x63,0x00,0x52,0x11,0x8e,0x29,0x56,0x69,0xf4,0x63,0x2e,0x24, + 0xcb,0x1e,0x3d,0x43,0x78,0x75,0xf7,0x24,0x29,0xc3,0x63,0x54,0xc8,0x0a,0x6f,0x4a, + 0xd5,0xb9,0xe8,0x10,0x0d,0xa8,0x55,0xf6,0x95,0xbe,0x48,0xa7,0x25,0x0c,0x76,0xbc, + 0x42,0x1c,0x38,0x66,0xd0,0xab,0x00,0xd9,0xb0,0x73,0x9e,0xe2,0xf4,0xdd,0x5c,0x28, + 0xf4,0xbd,0x96,0x8b,0xa3,0x3e,0x68,0x39,0x04,0xd1,0xed,0x5c,0xe0,0x7e,0xe0,0xa5, + 0x14,0xa8,0x31,0x41,0x5e,0x57,0xc9,0x8f,0xc6,0xf3,0x4a,0x80,0x80,0x92,0x2b,0xe6, + 0x89,0x64,0x05,0x53,0x99,0x1a,0x36,0x36,0x28,0x05,0xc4,0x0f,0x8e,0x84,0xbb,0xb1, + 0x41,0xb7,0x37,0x43,0xeb,0xce,0xbf,0xcf,0x28,0x54,0xa1,0x3d,0xe5,0x40,0xbf,0xa1, + 0xfa,0xeb,0xac,0x30,0x98,0xfa,0xf7,0xaa,0x2d,0xac,0x65,0xed,0x1a,0xa7,0xf8,0x65, + 0xc5,0xea,0x69,0xbb,0x5e,0xaa,0x24,0xbf,0xb0,0x58,0xf5,0xeb,0x8e,0xb7,0x70,0x12, + 0x5e,0x67,0x80,0x2c,0xa0,0xdd,0xc4,0xc8,0x79,0xa6,0x47,0x67,0x36,0xac,0xcf,0x8c, + 0x2b,0x0e,0xc5,0xcf,0xe0,0x28,0xeb,0x1f,0x1e,0xd5,0x21,0x48,0x69,0x96,0xcc,0x73, + 0xf9,0x92,0x36,0x95,0x14,0x2d,0x02,0x8d,0x7d,0x6e,0xa6,0x8a,0xfc,0x2b,0x3e,0x6e, + 0xa5,0x48,0x57,0x10,0x88,0x9a,0x89,0xc9,0x4a,0x97,0xcd,0x41,0x93,0x26,0x64,0xa5, + 0xed,0xe9,0x8a,0x34,0x20,0x4a,0x70,0xfa,0xe7,0xf7,0x5b,0x5e,0xf1,0x7b,0xdd,0x4d, + 0xab,0x6d,0x6f,0x81,0x23,0x40,0xbc,0x0e,0x47,0x03,0xe4,0x3f,0x8b,0x60,0x45,0x89, + 0xde,0x88,0xca,0x25,0xa5,0x85,0x02,0xb2,0x82,0xaa,0x2e,0x1d,0x0f,0x4f,0x96,0x0a, + 0xc9,0xc6,0x51,0x6c,0xbf,0x0c,0xdf,0x3f,0xed,0x58,0xa7,0xce,0xdb,0x9f,0xad,0xb9, + 0xdd,0xa2,0x6a,0xcb,0x54,0x8c,0xfb,0x32,0xcc,0x81,0xe6,0xe1,0xa6,0xf5,0xb3,0x88, + 0xe0,0x89,0x02,0x35,0x47,0x38,0xe2,0x21,0x83,0x84,0xf8,0x47,0x05,0x01,0x0b,0x09, + 0xbd,0xa9,0x03,0x01,0x1b,0x89,0xe4,0x33,0xc5,0x1d,0x20,0x38,0x58,0xe1,0x92,0x30, + 0x68,0x18,0x26,0xd2,0x64,0xc7,0xfe,0x0e,0x9c,0x85,0xab,0x54,0x73,0xa9,0x8d,0x86, + 0x5d,0x30,0x52,0xe7,0x0b,0xd8,0x5c,0x10,0x82,0xc0,0x07,0x9e,0xdf,0x0f,0xe8,0x0c, + 0x20,0x21,0xe6,0xa3,0x3f,0x0f,0x89,0x0e,0x1c,0xb9,0x25,0xad,0xb9,0x1a,0x18,0x82, + 0xf4,0x49,0x5b,0x73,0xf0,0xee,0xc5,0xe8,0xc0,0xba,0x64,0x2c,0xc5,0x76,0x1c,0x76, + 0x5c,0x7e,0x21,0xec,0x18,0x85,0xd3,0x6d,0x24,0x04,0x8a,0x6b,0x21,0x68,0x70,0x78, + 0x5a,0x69,0xa2,0x0d,0xfa,0xfd,0x7f,0x7b,0x95,0xf6,0xd0,0xc4,0xa0,0xff,0xff,0x4d, + 0x7c,0xbb,0xa4,0xb7,0xc6,0x53,0xe3,0x7f,0xfe,0xf3,0x1f,0xf0,0x3f,0x7d,0xaa,0xac, + 0x1b,0x70,0xbc,0x92,0x48,0x05,0x5b,0xa6,0xb7,0xbc,0xdb,0x50,0x34,0x16,0x74,0x9e, + 0x94,0x70,0xe0,0xac,0x7f,0xac,0x3b,0x70,0x94,0x95,0xe9,0xaa,0x76,0xed,0x58,0x45, + 0x1f,0xff,0x9e,0x6e,0x56,0x19,0x90,0xbc,0xe4,0xa6,0xda,0xe7,0x0d,0x50,0x55,0xb0, + 0xc3,0x9c,0x66,0x12,0xa5,0xae,0x5e,0x23,0x9f,0xdd,0xcc,0xb6,0x31,0x05,0x64,0xff, + 0x72,0xa6,0xef,0x8d,0x73,0x07,0xa7,0x9b,0x3c,0x78,0xa8,0xca,0x60,0x14,0xd1,0x48, + 0x7e,0x0a,0xb6,0x46,0x54,0xc9,0x85,0x11,0x76,0x72,0x6c,0xd4,0xab,0x0d,0xbf,0xbd, + 0xb2,0x46,0x83,0xaf,0x5e,0x09,0x56,0xaf,0xb0,0x36,0xb8,0x59,0xf9,0x70,0x93,0x26, + 0x10,0xc0,0x9f,0x50,0x2d,0x32,0x5e,0x96,0x61,0x45,0x12,0x54,0xee,0xb6,0x3b,0xde, + 0x0a,0xb9,0x21,0xf0,0xa3,0x75,0x07,0x6e,0x24,0xea,0x86,0x30,0xe8,0xe9,0xf7,0x14, + 0x3c,0xc5,0xe0,0xed,0x04,0x2a,0x85,0x8c,0xe1,0xf0,0x27,0xe0,0x2e,0x02,0xc0,0xae, + 0x1f,0x5c,0x68,0x35,0x1c,0x4c,0x83,0xa1,0xb9,0x64,0xc0,0xaf,0x01,0x94,0x62,0x33, + 0x13,0xaa,0xb0,0x4b,0x58,0x0a,0x3f,0x64,0x77,0xe8,0x81,0x68,0x18,0x33,0x92,0x61, + 0x61,0x67,0x50,0xd6,0x13,0xb1,0xfe,0xba,0xc3,0x2f,0x78,0xeb,0x4a,0xc8,0x3d,0x08, + 0x23,0x40,0x34,0x30,0xc8,0xe0,0x2a,0x32,0xc6,0x0b,0xc9,0x92,0xe1,0x25,0x85,0x49, + 0x41,0xc0,0x01,0xb5,0xa7,0x12,0x31,0xe5,0xf3,0x3c,0x25,0x13,0x4e,0xe6,0x25,0x19, + 0x3d,0xe2,0x0a,0x7f,0xd0,0x2b,0xf1,0x1a,0x84,0x60,0x1c,0xc2,0x2d,0x62,0x20,0xb5, + 0x13,0x12,0xac,0x94,0xd3,0x5a,0xd9,0x3d,0xbe,0xf3,0xc8,0x29,0x76,0x0d,0x6f,0x8d, + 0xb4,0xa6,0xe3,0x92,0x55,0x74,0xbc,0xd5,0x73,0x95,0x4e,0x28,0xa8,0x05,0x66,0x55, + 0xf6,0xe8,0xfe,0x1e,0x8a,0xec,0x69,0xf1,0x4a,0x11,0xc6,0x49,0xb9,0x0a,0x17,0x4a, + 0xd1,0x45,0x95,0xb5,0x3a,0xc8,0x0f,0xb6,0xd3,0x96,0xfa,0xb3,0x3c,0x87,0xc5,0x69, + 0xbb,0x17,0xdc,0x52,0x66,0x29,0x71,0x28,0x10,0x46,0x35,0x04,0x94,0x54,0x56,0x2c, + 0xce,0xba,0xa4,0xde,0x72,0x87,0x03,0x5e,0xaa,0x18,0xa8,0x7b,0x54,0xdc,0xed,0x0e, + 0x6f,0xd8,0x03,0x4d,0x07,0x3d,0xdd,0xbd,0x3b,0xdc,0xef,0x0e,0x7f,0xe7,0xd5,0x2c, + 0xc9,0x5f,0x9b,0x00,0x97,0xc7,0xcf,0x78,0xd7,0x13,0x6d,0x19,0x3d,0xad,0xfe,0x27, + 0x46,0x41,0x7a,0xff,0xfb,0x46,0x0d,0x0f,0x9f,0xf5,0x1c,0x3c,0xeb,0x09,0x5e,0x31, + 0xa3,0x8e,0xbb,0x41,0x65,0xcf,0x64,0x7a,0x90,0xb2,0x45,0x13,0x6e,0xa0,0xe4,0xae, + 0xca,0xc9,0x68,0x0e,0xac,0xd1,0x60,0x11,0xce,0x1b,0x04,0xce,0x1b,0x5d,0x82,0x8f, + 0x88,0xd0,0x54,0x83,0x06,0x9d,0xc9,0x82,0xb6,0xfc,0xb8,0x83,0xe6,0xa0,0xa7,0x27, + 0xbc,0x2c,0xe8,0x25,0x21,0x15,0x06,0x50,0xb6,0xc4,0xa8,0x88,0xca,0x2d,0x41,0xed, + 0xda,0x80,0x43,0x8b,0x5e,0x88,0x3f,0x86,0xa6,0x36,0xc0,0x25,0x70,0xe5,0x79,0x65, + 0xfc,0xca,0x01,0x82,0x29,0x5f,0x16,0x21,0xa9,0x13,0xe5,0xc3,0xf5,0x6b,0x01,0xbd, + 0x70,0x73,0x76,0x09,0x1c,0x1d,0x0b,0x4a,0x53,0xd5,0xd2,0x30,0x85,0x9f,0x9c,0xcc, + 0x60,0x06,0xd6,0x45,0x88,0x2f,0xa2,0xc5,0x84,0x12,0xba,0x8d,0xe5,0x8e,0x3d,0x83, + 0x5b,0x11,0xa1,0xc5,0x98,0x43,0x41,0x0a,0x92,0xc0,0x45,0xff,0x30,0x52,0x77,0xe9, + 0x31,0xec,0x12,0xd2,0x5c,0xc0,0xb1,0xdd,0x94,0x1d,0x13,0x64,0x39,0x4d,0x64,0xab, + 0x40,0x67,0xc9,0x3d,0x15,0x8e,0x4f,0x30,0x6e,0x29,0xf4,0xeb,0xf9,0xb8,0xb3,0x44, + 0x70,0xdb,0x5f,0x00,0x51,0x82,0xe9,0x14,0xaf,0xe4,0x52,0x5b,0xa4,0x4b,0xdc,0x14, + 0xab,0x3e,0xb5,0x0c,0x07,0x09,0x99,0x56,0x34,0x8b,0xbb,0xbd,0xee,0xd0,0xbc,0x4c, + 0x0c,0x7a,0x49,0xab,0x5b,0x3f,0xce,0x74,0x87,0xfa,0x75,0x45,0x8d,0xf5,0xcc,0x5a, + 0x88,0x1a,0xf2,0x53,0xf3,0xcc,0x3b,0xec,0x34,0x8f,0x33,0x7f,0xb1,0x59,0xf3,0x22, + 0x94,0xc2,0x55,0x68,0x06,0x7b,0xb0,0x3f,0xa1,0xf2,0x2a,0xa7,0xf8,0xf3,0xfc,0xf1, + 0xb7,0x14,0x66,0xac,0x37,0x0b,0x44,0xf2,0x88,0x4f,0xab,0xce,0xea,0xf6,0xda,0x37, + 0xbf,0x9f,0x9e,0x2c,0x56,0x64,0x70,0xd3,0xfd,0x8b,0x6d,0x41,0xb2,0xf0,0x21,0xaa, + 0x83,0xcf,0x61,0x70,0xd6,0x8d,0xd5,0x73,0xa4,0xe2,0xe8,0x38,0x4b,0x72,0x41,0x5d, + 0xed,0xb0,0x6e,0x44,0xa4,0xd7,0x53,0xc1,0xd0,0xef,0x36,0x96,0x88,0x08,0xe4,0x7f, + 0x5e,0x08,0x15,0xa8,0xf1,0x94,0x43,0x27,0x74,0x91,0xdb,0xbf,0xde,0x7d,0xc1,0xd7, + 0x0f,0xfd,0xf2,0xdb,0xb2,0x85,0xe6,0x40,0x47,0x2d,0x0f,0xc0,0x00,0x20,0x02,0x6b, + 0x7f,0xdb,0x62,0x9a,0xdf,0xcd,0x47,0xdb,0xb3,0xa0,0x32,0x77,0x66,0xa9,0x77,0x38, + 0xdb,0x59,0x01,0x0c,0xd5,0x9b,0xed,0x28,0x86,0x69,0xc8,0x55,0x96,0x13,0x41,0x5f, + 0xc6,0x2b,0x1b,0xfb,0x59,0xdc,0x8f,0xd8,0xe0,0x30,0x62,0xfb,0xfb,0x6a,0xae,0x99, + 0x5d,0xc5,0x4d,0xf0,0xf0,0xd4,0x2d,0xa9,0x89,0x9f,0x6d,0xc9,0x4a,0x0b,0x20,0xe8, + 0x6b,0x8d,0x7d,0xf0,0x51,0xb2,0x1c,0x3c,0x53,0x6f,0xab,0x04,0x56,0xe0,0x76,0x49, + 0x26,0x0c,0x01,0x9b,0x18,0xcf,0x61,0x2b,0x9f,0x51,0x75,0x85,0x45,0xa8,0xa6,0x2c, + 0xcb,0x00,0x6a,0x59,0xc5,0x67,0x4a,0x1a,0x6c,0x6c,0xac,0x28,0x68,0xf5,0x1f,0x5f, + 0x6e,0x3e,0xc5,0xd6,0x36,0x7d,0x7e,0xb4,0xf6,0x6d,0xb6,0x1f,0x38,0xfb,0x96,0x22, + 0x05,0x6b,0x5f,0xad,0x20,0xc4,0x7a,0xc6,0xa7,0xaa,0xe0,0xb0,0xc8,0xa0,0x09,0x01, + 0xd9,0xb7,0x5a,0xdb,0x50,0x8b,0x33,0x9a,0x5a,0xac,0x92,0xa5,0x99,0xa6,0x0b,0xf1, + 0xb9,0xfc,0x5d,0x56,0xd6,0xd3,0x5f,0x25,0xc1,0xff,0x03,0xea,0x7d,0x5d,0xbd,0xce, + 0xf2,0x4b,0xea,0xff,0x57,0x24,0x6b,0xe9,0x7c,0xca,0x91,0x9f,0x94,0x25,0x2d,0xd2, + 0x8b,0x29,0x42,0x47,0x56,0x26,0xcf,0x0d,0x2a,0x7d,0x5e,0x68,0x5a,0x89,0x1b,0x08, + 0x6e,0xba,0xe0,0xfc,0x77,0x2f,0x70,0xc1,0xba,0x03,0xe7,0xc6,0x1e,0x81,0x2d,0x0f, + 0x56,0x4a,0xf5,0x0a,0x08,0x68,0x9b,0x57,0x11,0xec,0xe6,0x0a,0x19,0x82,0xa8,0x7d, + 0x02,0x58,0x85,0xe5,0x14,0x87,0x1f,0x0d,0x18,0x36,0xe0,0x85,0x2d,0xd4,0x06,0x1d, + 0x0a,0x91,0xf5,0xf3,0xee,0xf6,0x97,0x98,0x45,0x0c,0x48,0x12,0xbe,0x0a,0x6a,0xc4, + 0x32,0x7b,0xe1,0xbc,0x84,0x68,0x90,0x81,0x46,0xea,0xd0,0x2f,0x22,0x65,0xa3,0xed, + 0xac,0x23,0x30,0x51,0x97,0xd6,0x99,0xd5,0x72,0xc1,0x68,0x05,0x67,0x9d,0x48,0xd7, + 0xd4,0x4b,0xc3,0xe0,0x38,0x04,0xa6,0xc5,0x1f,0x33,0xaa,0x6d,0xd5,0x66,0xa1,0xca, + 0xbe,0x51,0x19,0x6d,0x6c,0x0a,0x1a,0x9b,0xf0,0xe5,0xb8,0x65,0xd6,0x9b,0x38,0x5e, + 0x38,0xc6,0x47,0xcb,0xaa,0x9f,0xa2,0x17,0x2a,0x86,0x40,0x49,0x8a,0x6a,0x88,0x5d, + 0x70,0x82,0x98,0x41,0x85,0x15,0xcd,0x39,0xdc,0x53,0x4d,0xf8,0x9a,0xe2,0xd2,0x21, + 0x14,0xaa,0xa0,0x5a,0x1c,0xb4,0x31,0xd4,0x38,0x5f,0x13,0x42,0x52,0x18,0x06,0xfb, + 0xd7,0x6c,0x90,0xc4,0xf6,0x06,0x03,0x3a,0x96,0x71,0x6c,0x05,0x96,0x03,0xb6,0x03, + 0x27,0x42,0xdd,0x2e,0xbe,0xb1,0xef,0xee,0x28,0x2e,0xf1,0x2b,0xdc,0x6f,0xc0,0x12, + 0x1b,0x7c,0xa8,0xe9,0x6e,0xd0,0xaf,0xe7,0x42,0x50,0x61,0xee,0x2e,0xb0,0x54,0x59, + 0xfe,0x8e,0x6f,0xe1,0xc9,0x07,0x4b,0xb9,0x6c,0x85,0x10,0x8c,0x8d,0x98,0xcd,0x84, + 0x51,0x7b,0x82,0x12,0x83,0x9e,0xc0,0xff,0x9f,0x9e,0x92,0xa7,0xa7,0x91,0x46,0x20, + 0x21,0x9b,0xf4,0x6a,0x83,0xeb,0x34,0xed,0x24,0x77,0x7b,0x50,0xa7,0x16,0x0d,0x93, + 0x97,0xa8,0xc3,0x06,0xb1,0x0e,0x82,0xa5,0xc5,0xbd,0x66,0x84,0x17,0xce,0x4a,0x6f, + 0x03,0x75,0x92,0x62,0x5e,0x28,0xc1,0xc0,0xf5,0x20,0x78,0xf3,0x81,0x8d,0x17,0x4f, + 0x4f,0x7a,0x93,0xc0,0x61,0x7d,0x6a,0xd8,0x99,0xd0,0x56,0x30,0xe5,0x4b,0xfc,0x1e, + 0x06,0x56,0xe0,0x76,0x93,0xfe,0x3c,0x3b,0x20,0x50,0x13,0x9d,0xb3,0xf5,0x45,0x30, + 0xf5,0xf5,0x91,0xa0,0x89,0x34,0x2a,0x56,0x4c,0xb7,0x3b,0x0f,0x7a,0x61,0xd2,0xbe, + 0xa5,0x0f,0x0a,0xd6,0x6e,0x5e,0x1a,0x23,0x37,0x99,0x68,0xba,0x1a,0xf0,0x10,0x82, + 0x9f,0x57,0x54,0x0c,0x8c,0x59,0x6d,0x11,0x3a,0xc2,0xa9,0x01,0xc9,0x07,0x80,0x4d, + 0x68,0xf5,0x8d,0x22,0x42,0x76,0x80,0x12,0xdf,0xc9,0x0a,0x02,0x65,0xa7,0x06,0x27, + 0x86,0x81,0xd6,0x26,0xa9,0x50,0x12,0x15,0xf5,0x7e,0xcc,0xc1,0xd6,0x94,0xd0,0x64, + 0x3c,0xad,0x77,0x9a,0x10,0x40,0xc0,0x01,0xf4,0xb0,0x73,0x25,0x23,0x40,0xfa,0x23, + 0x99,0x42,0x1a,0x88,0xda,0x77,0xb7,0xc0,0xd0,0x58,0xbf,0x81,0xc0,0x73,0x87,0xc0, + 0x1d,0xed,0x8d,0x29,0x99,0x88,0xc2,0xc0,0x0a,0xb3,0xb0,0xc3,0x2d,0xcf,0xd0,0xfb, + 0x12,0x60,0x01,0xa6,0xff,0x63,0x30,0x1a,0x24,0x0b,0x04,0x4c,0x3b,0x71,0xfa,0x8b, + 0xb2,0xa1,0xb1,0x06,0x52,0xc0,0x54,0x98,0xf6,0xb8,0x45,0x95,0x0d,0x5e,0x95,0x73, + 0x0e,0x32,0x25,0xc0,0xca,0x51,0x28,0xc3,0x1f,0x11,0x1e,0x79,0x2c,0x0b,0x29,0x71, + 0x83,0xdd,0x17,0xe4,0x18,0xd2,0xc0,0x63,0x78,0xfc,0xed,0xfb,0xcf,0x41,0x89,0x93, + 0xf4,0x97,0x4e,0xab,0xde,0x2f,0x63,0x6b,0x7f,0x07,0x11,0x26,0xb3,0xad,0xb9,0xf5, + 0xe6,0xa6,0xe7,0xb6,0x51,0xe1,0xd4,0x45,0x6d,0xce,0x5a,0x2f,0x54,0x99,0xac,0xe6, + 0x9a,0xc8,0xd4,0x47,0x40,0xfd,0x6d,0x1e,0xcf,0x9c,0xc2,0x72,0x57,0x78,0xe8,0xe0, + 0x69,0x68,0xe1,0xa9,0xcb,0x52,0xdf,0x9a,0x88,0xbe,0x90,0x8a,0x70,0x65,0x99,0x88, + 0x7a,0x5f,0xe0,0x7c,0x0f,0xc8,0xc4,0x0f,0x2a,0x6c,0xac,0xde,0xbe,0x7a,0x0f,0xde, + 0x72,0xb9,0xf4,0xc0,0xcf,0x99,0x37,0xaf,0x72,0x7d,0x66,0x4e,0xad,0xb5,0x8b,0x36, + 0x87,0xca,0xf0,0x1f,0x9c,0x15,0xb6,0xb5,0x07,0xe1,0x73,0x94,0xd8,0xe7,0x1f,0x1e, + 0x0d,0x9d,0x6f,0x3e,0x23,0x3e,0xff,0xf2,0x5d,0x57,0x42,0xfb,0x1c,0x69,0xfa,0x54, + 0x5d,0xe1,0x67,0xcb,0x15,0xa6,0x29,0xf5,0x29,0x7e,0xb7,0x84,0xd3,0x69,0x96,0x00, + 0xf5,0xa7,0x96,0x6b,0x41,0x87,0xe5,0xbc,0xc4,0x3a,0x5a,0x8c,0xd6,0xbe,0x6e,0x6a, + 0x6d,0x1b,0x0b,0x86,0x6a,0x34,0x04,0x54,0xa9,0x80,0x44,0x7e,0x5f,0x1f,0xef,0xc0, + 0xd6,0x31,0x7e,0x81,0x6e,0x7f,0xac,0xdd,0x32,0x52,0xad,0xd3,0xa6,0x10,0x6f,0x73, + 0x07,0x80,0x9d,0x28,0x55,0x47,0xff,0xa4,0x10,0x4b,0x5a,0xfd,0x4b,0x2b,0xd7,0xc8, + 0xb0,0x51,0xc7,0x9c,0x57,0xa3,0x0e,0xf0,0xa9,0xfa,0xe6,0xbc,0x95,0xc4,0xc3,0x7e, + 0xdf,0x6d,0x07,0x0d,0x4b,0xd4,0x31,0x0e,0xa0,0x80,0xcd,0x7f,0xc7,0xd1,0x53,0x57, + 0xc5,0x41,0x4f,0xff,0x57,0x23,0xff,0x05,0xcc,0x0c,0xb8,0x04,0x46,0x22,0x00,0x00, }; constexpr size_t KNOBS_INDEX_HTML_GZ_LEN = sizeof(KNOBS_INDEX_HTML_GZ); // diff --git a/firmware/patternflow/src/patterns_index.h b/firmware/patternflow/src/patterns_index.h index a48a988a..2dfe0ac7 100644 --- a/firmware/patternflow/src/patterns_index.h +++ b/firmware/patternflow/src/patterns_index.h @@ -28,194 +28,149 @@ static const char PATTERNS_INDEX_HTML[] PROGMEM = R"HTML( - + Patternflow - Patterns
        -

        Patterns

        -
        +/* Touch: no drag and drop on a phone, so arrows instead, in the room the + module chip leaves (the checkbox says it is a module). Narrow buttons keep + the name wide; their 40px hit areas meet in the 6px gaps. */ +@media(pointer:coarse){.btn.mv{display:inline-flex;width:34px;padding:0;font-size:16px}.chip.mod,.fine{display:none} +.list>li{gap:6px;padding:4px 6px}.list .btn.sm::after{inset:-4px -3px}.list .dan{padding:0 8px}#bulk{row-gap:10px}} +@media(min-width:960px){.wrap{max-width:1080px;padding-top:32px} +.grid{display:grid;grid-template-columns:380px minmax(0,1fr);gap:48px;align-items:start} +.grid section{margin-top:0}.lc{position:sticky;top:8px}.q{max-height:55vh;overflow:auto}} + +
        +

        Patterns

        —
        - -
        -

        Storage needs formatting

        -

        This board's pattern partition isn't readable — either it has never been - formatted, or its filesystem is damaged. Format it once, and patterns install - normally from then on.

        -
        - - -
        + + -
        -
        +
        +

        Upload

        -
          +
          -
          -
          - -
          +
          -
          +
          -

          Installed

          -
          - - - - - +
          +

          Installed

          +
          -
          -
            +
              + +

              Tap or click a name to play it. Drag modules to reorder. Presets are built into the firmware and can’t be removed here.

              -
              Presets are built into the firmware and cannot be removed here. - Home
              -
              + +
              )HTML"; + +)HTML"; // GENERATED by firmware/toolchain/console_pages.py build — do not edit. -// gzip -9 of PATTERNS_INDEX_HTML (43203 bytes → 15416 bytes), served with Content-Encoding: gzip. +// gzip -9 of PATTERNS_INDEX_HTML (42129 bytes → 15116 bytes), served with Content-Encoding: gzip. static const uint8_t PATTERNS_INDEX_HTML_GZ[] PROGMEM = { - 0x1f,0x8b,0x08,0x00,0x00,0x00,0x00,0x00,0x02,0xff,0xcd,0x7d,0xdd,0x76,0x1b,0x47, - 0x92,0xe6,0x3d,0x9f,0xa2,0x04,0x4f,0xb3,0x50,0x66,0xa1,0x08,0x50,0x3f,0xb6,0x01, - 0x81,0x1c,0xc9,0x92,0xc6,0x9e,0x96,0x6c,0x8d,0x28,0x6f,0xf7,0x58,0xd6,0xf4,0x29, - 0x00,0x09,0xa2,0xcc,0x42,0x15,0x5c,0x55,0x20,0xc5,0x26,0x31,0x67,0xae,0xf6,0x01, - 0x76,0xe7,0xcc,0xde,0xec,0xf5,0x3e,0xc0,0x5e,0xef,0xd3,0xf8,0x49,0x36,0xbe,0x88, - 0xcc,0xac,0x2c,0xa0,0x28,0xd1,0xdd,0xbd,0x67,0xb6,0x8f,0x9b,0x42,0xfd,0x65,0x46, - 0x66,0x46,0xc6,0x7f,0x44,0x3e,0xbe,0x37,0xcb,0xa7,0xd5,0xd5,0x4a,0x79,0x8b,0x6a, - 0x99,0x1e,0xef,0x3d,0xc6,0x3f,0x5e,0x1a,0x67,0x67,0xe3,0x8e,0xca,0x3a,0xb8,0xa1, - 0xe2,0xd9,0xf1,0xe3,0xa5,0xaa,0x62,0x6f,0xba,0x88,0x8b,0x52,0x55,0xe3,0xce,0xba, - 0x9a,0xf7,0xbe,0xc4,0xc3,0x72,0x5a,0x24,0xab,0xca,0x2b,0x8b,0xe9,0xb8,0x73,0xb8, - 0x9a,0xf7,0xa6,0x79,0x56,0xe6,0xa9,0x8a,0x7e,0x2e,0x4f,0x16,0xe3,0x59,0xff,0x68, - 0x72,0xff,0xfe,0xd1,0x51,0xe7,0xf8,0xf1,0xa1,0xbc,0x78,0xac,0x3f,0x38,0x4e,0xe6, - 0xdd,0x7b,0x97,0x49,0x36,0xcb,0x2f,0xa3,0xd7,0x2f,0x82,0xee,0x7c,0x9d,0x4d,0xab, - 0x24,0xcf,0xba,0xc1,0xf5,0x45,0x5c,0x78,0x7f,0x18,0xcb,0xb3,0xf0,0x87,0xb1,0x7f, - 0x18,0xaf,0x92,0xc3,0xb2,0x8a,0xab,0x75,0xe9,0x87,0x2f,0xc7,0xef,0xde,0x87,0xaf, - 0xc6,0x47,0xea,0xab,0xf0,0x34,0xfc,0x31,0x7c,0x3d,0xfe,0x03,0x7d,0x3e,0xbe,0xc6, - 0x63,0x35,0xf4,0xd3,0xe4,0x42,0xf9,0xe1,0x2c,0x29,0xaa,0xab,0xe1,0xbd,0x41,0x78, - 0x31,0xcc,0xd6,0x69,0x1a,0x9e,0xa9,0x6a,0xf8,0x0f,0x61,0x92,0xcd,0xd3,0xe4,0x6c, - 0x51,0x0d,0xbf,0x5b,0x2f,0x27,0xaa,0x08,0x17,0x79,0x3a,0x1b,0x7e,0x3f,0xf9,0x59, - 0x4d,0xab,0xb0,0x8c,0xaf,0x86,0x16,0x82,0x2a,0x3c,0x0f,0x55,0x70,0xad,0xf6,0xf7, - 0xbb,0x2a,0xaa,0xd4,0x87,0xea,0xeb,0x3c,0xab,0x54,0x56,0x8d,0xab,0x60,0x13,0x4e, - 0xd6,0xa5,0xf3,0xea,0x24,0x5c,0x05,0xd7,0xe6,0xca,0x9b,0x13,0xf0,0x93,0x68,0x96, - 0x94,0xf1,0x24,0x55,0xb3,0xf1,0xbd,0xc1,0xc6,0xbd,0xea,0x8f,0x56,0x51,0xb5,0x50, - 0x59,0x77,0x1e,0xce,0x83,0x51,0xa1,0xaa,0x75,0x91,0x79,0xab,0x4d,0xb8,0xca,0xd3, - 0xb4,0x6e,0xb1,0x0c,0x97,0xe1,0x34,0xcc,0x65,0x16,0xaa,0x70,0x1e,0x9e,0x8d,0x07, - 0x23,0xdb,0xc3,0x1b,0xea,0xe1,0x6c,0x7f,0xff,0xde,0x9c,0x60,0x9b,0x8f,0x07,0xe1, - 0x34,0x55,0x71,0xf1,0x36,0x59,0xaa,0x7c,0x5d,0x75,0xab,0x20,0xec,0x96,0xd1,0x34, - 0x4e,0xd3,0x93,0xb2,0x1b,0x0c,0xff,0xa1,0x5b,0x06,0x81,0xee,0xd1,0xb4,0x3e,0x0b, - 0xae,0xa7,0xdd,0x59,0x88,0x59,0xa1,0xb1,0xd8,0xdb,0x0a,0xb7,0x79,0xaa,0x54,0xb0, - 0xd1,0xdf,0xfc,0x3e,0xfc,0x7d,0x10,0x6c,0x6c,0xcf,0xbf,0xa7,0x9e,0xe7,0xe3,0xfe, - 0x68,0xab,0xc7,0x11,0x41,0xd3,0xad,0xc6,0x84,0x12,0xe6,0xde,0x9b,0x70,0x49,0xdf, - 0xe5,0xfb,0xfb,0x79,0x34,0x4f,0x8a,0xb2,0x1a,0x8f,0x69,0x22,0x4e,0xe8,0xf3,0x21, - 0x01,0xaf,0x87,0x4d,0xcb,0x95,0xaf,0x86,0xce,0x92,0x9f,0xed,0xb6,0xbc,0x09,0xa9, - 0xd1,0xfa,0x9d,0x2c,0xb8,0x5e,0x8e,0xb3,0xd1,0xfc,0xe6,0x86,0x9a,0xda,0x84,0x59, - 0x7e,0x39,0x7c,0xb3,0xa1,0x77,0x18,0x2d,0xea,0xd7,0xa6,0x34,0x92,0xfd,0xfd,0x97, - 0xd1,0x6a,0x5d,0x2e,0xe8,0x62,0x74,0x7a,0x42,0x97,0xd3,0xee,0x69,0x30,0xfc,0xf1, - 0xe4,0xc7,0x88,0xbe,0x22,0x38,0x7e,0x1c,0xbf,0x8e,0x30,0xe9,0xdd,0x1f,0xc2,0x57, - 0xe1,0x33,0x6a,0xec,0x32,0xae,0xa6,0x8b,0xd3,0xad,0x96,0x96,0xc1,0xf5,0xeb,0x48, - 0x9a,0x27,0xb8,0x7f,0x8c,0x08,0x9a,0xee,0xab,0xf1,0xab,0xb8,0x5a,0x44,0xcb,0x84, - 0x1e,0x87,0xaf,0x02,0xfe,0x34,0xa9,0x5e,0xe4,0xc5,0x33,0x75,0x91,0x4c,0x55,0xfd, - 0x31,0xad,0x5f,0x3e,0xce,0x6f,0x6e,0xae,0x37,0x66,0xa1,0x33,0x75,0xe9,0xbd,0x2e, - 0xf2,0x65,0x52,0xaa,0x7a,0x39,0xae,0xc2,0x4c,0x16,0x7a,0x51,0x83,0x74,0xa4,0xee, - 0xd7,0x0b,0x53,0x06,0xd7,0x25,0x2d,0x77,0x37,0x8f,0x26,0xeb,0x24,0x9d,0xed,0xef, - 0x97,0xf2,0x63,0x3c,0xd6,0x77,0x02,0x9a,0xfe,0x45,0x84,0xe9,0xec,0x06,0xe1,0x15, - 0x56,0x7c,0x13,0x8c,0x9c,0xd5,0x70,0xe6,0xd8,0xbc,0x36,0xca,0xba,0xcf,0x8b,0x22, - 0x2f,0xba,0x7e,0x25,0x2f,0xf9,0x18,0x48,0x1e,0xe9,0xab,0x9b,0x9b,0xaf,0xd4,0x03, - 0x6a,0x65,0xb3,0xa9,0xd1,0xee,0x1f,0xba,0x6b,0xa0,0xa4,0x1e,0xca,0x5c,0xd1,0x74, - 0xf1,0x9d,0x2d,0xec,0x2a,0x82,0x6b,0xec,0xea,0x22,0xca,0xcf,0x83,0x6a,0x51,0xe4, - 0x97,0x9e,0x74,0x54,0xe8,0x79,0xb4,0x58,0xcf,0xd8,0x81,0x9d,0x75,0x52,0xf0,0x3f, - 0xb4,0x28,0x05,0x11,0x0c,0x80,0xb9,0x71,0x70,0xee,0x19,0xc6,0x4f,0x2d,0xd2,0xdf, - 0x53,0xda,0xe7,0xab,0xb9,0xac,0xd1,0xb8,0x1c,0xbd,0x8c,0xe6,0x79,0xf1,0x3c,0x26, - 0x30,0x6c,0xe7,0x73,0xc2,0x4f,0x7a,0x13,0x80,0x6f,0x82,0x6e,0x60,0xe9,0xcd,0x9e, - 0x10,0xae,0x2c,0x5e,0xaa,0x71,0xe7,0x22,0x51,0x97,0xab,0xbc,0xa8,0x3a,0xde,0x54, - 0xef,0xe9,0xce,0x65,0x32,0xab,0x88,0x50,0xf1,0x02,0xf6,0xf8,0x82,0x28,0x45,0x52, - 0x25,0x71,0xda,0x2b,0x69,0x2f,0xa9,0xf1,0x00,0x54,0xae,0x4a,0xaa,0x54,0x1d,0xbf, - 0x8e,0xab,0x4a,0x15,0x44,0x47,0x68,0x68,0x3d,0x4f,0x5f,0x95,0x8f,0x0f,0xe5,0x29, - 0xd1,0xc2,0xea,0x0a,0xff,0x1e,0x7e,0xee,0x3d,0x8b,0x8b,0x73,0x2f,0xc9,0xca,0xaa, - 0x58,0x2f,0xa9,0x1b,0xaf,0xca,0xcf,0x55,0x56,0x7a,0xbf,0xfe,0xdb,0xbf,0x7b,0x25, - 0x41,0xc2,0xe0,0x94,0x9e,0xba,0x50,0xc5,0x95,0xa7,0x29,0xa6,0xb7,0x8a,0xcf,0x94, - 0xb7,0x2e,0xe9,0x7e,0xb7,0xd7,0x9b,0x16,0x2a,0x5e,0x7a,0x49,0xb9,0xe7,0x79,0x5e, - 0x87,0xa6,0x59,0x9e,0x4e,0xe2,0xe9,0xf9,0x59,0x91,0xaf,0xb3,0x59,0xc7,0x9b,0x5c, - 0x79,0x8b,0x84,0x96,0xb4,0xb8,0x0a,0x42,0x2f,0x03,0x65,0xf3,0x2e,0xe2,0x74,0xad, - 0xca,0xc8,0x7b,0x4b,0xef,0x2f,0x09,0x02,0x55,0x25,0xd9,0x99,0x57,0x26,0x95,0xf2, - 0x68,0x05,0xae,0xb8,0xb1,0x55,0xbc,0x52,0xc5,0xc8,0x43,0x93,0x32,0x68,0xea,0x84, - 0xaf,0xaa,0x05,0x5e,0x3e,0xa3,0xb1,0xe1,0xdf,0x24,0xf3,0x62,0x6f,0x86,0x51,0x14, - 0x79,0xbe,0x8c,0xbc,0xcf,0x0f,0xf7,0x86,0xf4,0xab,0xba,0xd6,0xa0,0x0d,0x3f,0xeb, - 0x7f,0xdd,0x7f,0xda,0xff,0x6a,0xd4,0xeb,0x25,0xd9,0xf9,0xf0,0xb3,0xe7,0xcf,0x9e, - 0x7f,0xf1,0xec,0x29,0x5d,0x2d,0xd7,0x95,0x9a,0x0d,0x3f,0xfb,0xf2,0xc9,0x97,0x47, - 0x5f,0x1c,0xd1,0xf5,0x3c,0x4e,0xb2,0x6a,0xf8,0xd9,0xc3,0x27,0x0f,0x1f,0x3e,0x78, - 0x34,0xda,0xeb,0xf5,0x8a,0x75,0xaa,0x86,0x9f,0x1d,0x3d,0x38,0x1a,0x0c,0xbe,0x1c, - 0xc9,0x65,0xaf,0xcc,0xe7,0xf4,0xce,0xe0,0xe9,0xe0,0xab,0xc1,0x03,0xba,0x97,0xa2, - 0x85,0x17,0x2f,0x1e,0x7e,0x7d,0xf4,0x9c,0xae,0x72,0x6a,0xfe,0xe1,0x17,0x4f,0xbf, - 0xfc,0xe2,0x05,0x5d,0x5c,0xc6,0x45,0x36,0xfc,0xec,0xd9,0x57,0x4f,0xfa,0xf7,0x5f, - 0xa0,0xb5,0x55,0x9c,0xa9,0x94,0x3e,0xbd,0x3f,0x18,0x0c,0xfa,0xb8,0x51,0xc6,0x59, - 0x39,0xf4,0xbf,0xa5,0xd5,0x2d,0xfc,0x70,0x9d,0xf0,0x75,0xaf,0x54,0x45,0x32,0x0f, - 0xcb,0xab,0xb2,0x52,0xcb,0xde,0x3a,0x09,0xeb,0x9b,0xf8,0x64,0x99,0x67,0xf9,0xd0, - 0xff,0x47,0x55,0x3d,0x2d,0x08,0xd8,0xd2,0x7b,0x45,0xd7,0xfc,0x2d,0x1e,0x94,0xab, - 0x78,0xaa,0xc2,0xd3,0x17,0xb8,0xd9,0x7b,0xa3,0xce,0xd6,0x69,0x5c,0x84,0xaf,0x54, - 0x96,0xe6,0xa1,0x7d,0xbc,0xd9,0xfb,0xfc,0x7a,0x92,0x7f,0xe8,0x95,0xc9,0x9f,0x69, - 0xee,0x86,0x93,0xbc,0x98,0xa9,0xa2,0x47,0x77,0x36,0x7b,0x93,0x7c,0x76,0x75,0x4d, - 0x6b,0x71,0x96,0x64,0xc3,0xfe,0xa8,0x5e,0xbf,0x21,0x6d,0x7b,0xb3,0xcc,0xc1,0x68, - 0x9a,0xa7,0x79,0xa1,0x6f,0xd1,0x74,0x06,0xa3,0x39,0xa1,0x27,0x4d,0xdd,0x32,0x49, - 0xaf,0xf4,0x6d,0x80,0x1c,0x8c,0xf6,0xd2,0x24,0x53,0xbd,0x85,0x62,0x46,0x36,0x88, - 0x1e,0x8e,0x7a,0x97,0x6a,0x72,0x9e,0xd0,0xbb,0xf8,0xa0,0x5c,0xd2,0x0a,0x61,0x19, - 0x87,0x71,0x06,0x0c,0x4e,0xe2,0x52,0xcd,0x36,0x7b,0xd1,0x65,0x11,0xaf,0x08,0x88, - 0x0f,0x82,0xdf,0xc3,0x47,0x5f,0xf6,0x57,0x1f,0x46,0x06,0x28,0x2f,0x5e,0x57,0xf9, - 0x68,0x15,0xcf,0x66,0xf8,0xf0,0xfe,0xd1,0xea,0x83,0x77,0x44,0xcf,0xbd,0x47,0x0f, - 0x56,0x04,0x3f,0x78,0xbf,0x2a,0xae,0x89,0x8d,0xad,0x52,0x30,0xc9,0x54,0x7d,0x18, - 0x51,0xc3,0x67,0x59,0x8f,0xb0,0x6a,0x59,0x0e,0xa7,0x0a,0x33,0x3d,0x3a,0x8b,0x57, - 0xc3,0x2f,0xa9,0x51,0xdd,0x0c,0x8d,0xbd,0xaa,0xf2,0xe5,0x70,0x40,0xad,0x8d,0xf6, - 0xec,0x7c,0xc8,0x3d,0x6a,0x9b,0xf0,0x3d,0x99,0x79,0x32,0x30,0x20,0x40,0x40,0x40, - 0xce,0x08,0xb9,0x04,0xbe,0x2f,0xe8,0x23,0x3d,0x44,0xfc,0xdc,0x99,0x34,0x42,0x8f, - 0x60,0xa4,0x1b,0x2d,0xe2,0x59,0x42,0x64,0x7b,0xc0,0xb0,0x0e,0xae,0x65,0x1a,0x92, - 0x3f,0xab,0xe1,0xe0,0x21,0x7d,0xca,0x97,0x97,0xd2,0xd4,0xa3,0x7e,0x7f,0x94,0x2a, - 0x6c,0xd9,0x1e,0x16,0x0d,0x83,0x8d,0xfa,0x03,0xb5,0xb4,0x13,0x31,0xc2,0xe0,0x86, - 0x03,0x02,0xa5,0x5c,0x4f,0xae,0x77,0x57,0x00,0xcb,0xad,0x57,0x46,0x7a,0xa0,0x4e, - 0x1b,0x2b,0xc7,0xa8,0x4e,0x43,0x29,0x15,0x93,0x24,0xbd,0xec,0x3d,0xf0,0xba,0xa3, - 0x2f,0x19,0xc0,0xa3,0xeb,0xad,0xcf,0x3f,0x09,0xe0,0x57,0x04,0x20,0x48,0x64,0xaf, - 0x2a,0x08,0x01,0x88,0xea,0x2d,0x87,0xeb,0x15,0xed,0xe0,0x29,0x2d,0xed,0x68,0xcf, - 0xed,0x9d,0x37,0x5e,0x50,0x2f,0x6b,0xdf,0x1b,0xf4,0xd1,0x69,0x34,0x2b,0xf2,0x95, - 0x5d,0xc0,0x49,0x9a,0x4f,0xcf,0xf5,0xe4,0xf1,0x52,0xcc,0xe2,0x72,0xa1,0x1a,0x6b, - 0xb1,0x35,0xb5,0xf7,0xeb,0x65,0x1d,0x1e,0x3d,0xa2,0x2f,0x06,0x58,0x68,0x86,0x89, - 0x11,0xc1,0xa0,0xc0,0x9e,0xb3,0x4e,0xc5,0xd9,0x24,0xee,0x1e,0x3d,0x7c,0x18,0x9a, - 0xff,0x47,0xfd,0xa3,0x60,0xc4,0x43,0x48,0x30,0x35,0x66,0x8b,0x30,0xfc,0x5e,0x34, - 0x78,0x58,0x86,0xf5,0xd7,0x7c,0x3d,0x9a,0xae,0x8b,0x92,0xc6,0xb6,0xca,0x13,0xb4, - 0xae,0x87,0x11,0xe5,0x44,0x31,0xaf,0xdd,0x8f,0x1b,0x28,0xd1,0x02,0xc0,0x57,0x47, - 0xe1,0x83,0x47,0xd4,0xfd,0xa3,0x40,0x37,0xe1,0xad,0xea,0xed,0xe8,0x2c,0xc6,0xfd, - 0xad,0xb5,0x94,0xd9,0x34,0xdf,0x4c,0xae,0xdb,0x77,0x68,0xbd,0x72,0xf4,0x26,0x6d, - 0xbc,0xea,0x2f,0x47,0x9b,0x91,0x83,0x2d,0x8f,0xb0,0x6e,0x49,0xb6,0x5a,0x57,0xef, - 0x20,0x98,0x8f,0xe7,0x49,0xaa,0xde,0xdb,0x25,0xcc,0xf2,0x8c,0x28,0xce,0x3a,0xbd, - 0x4e,0x89,0xfc,0xf7,0x98,0xe7,0xf0,0xbd,0x1a,0x91,0xcd,0x72,0xf5,0xcd,0x52,0xa2, - 0xd1,0xb6,0x7d,0xc7,0x84,0x97,0x46,0x99,0x26,0x77,0xda,0xe1,0x40,0x28,0xdb,0xf8, - 0x57,0x20,0x14,0x77,0xdb,0xde,0xd2,0xcd,0xd6,0x74,0xa3,0x57,0x2f,0xca,0xfe,0x9a, - 0x19,0xa3,0xe9,0x12,0x8a,0x71,0xf4,0xc0,0xb6,0xb7,0xbc,0x96,0x6d,0x3c,0x02,0xaa, - 0x80,0x53,0x0f,0x17,0xc9,0x6c,0xa6,0x32,0xc1,0x58,0x7b,0x53,0xa5,0x69,0xb2,0x2a, - 0x93,0x72,0x74,0xb9,0xa0,0x61,0xf2,0x86,0xc3,0x2c,0x82,0x52,0xd2,0x52,0x56,0xf1, - 0xd9,0x5d,0xe0,0xc2,0x74,0xec,0xec,0xd8,0x47,0x1f,0xdd,0xb1,0x66,0xf6,0x30,0x4d, - 0x8f,0x40,0xdc,0xea,0x9d,0xb8,0x43,0x14,0xb7,0x36,0xe2,0xd1,0x6d,0x28,0x4a,0xd0, - 0x46,0xcb,0x7c,0x76,0xeb,0xbe,0xd8,0xbe,0x81,0x99,0x8a,0x88,0x3e,0x7d,0x6c,0xbf, - 0x7c,0x45,0x6f,0x91,0x1c,0x03,0x49,0x02,0xb2,0x8a,0x11,0x12,0x80,0x22,0x2c,0x3b, - 0x15,0x79,0xea,0xd1,0x74,0x8d,0x48,0xce,0x38,0x87,0xec,0x10,0x57,0x1e,0x61,0xd0, - 0x14,0xf3,0x0b,0x7d,0x87,0x05,0x06,0xbd,0x1e,0xdb,0x3b,0x59,0x6e,0x0f,0x17,0xbc, - 0x97,0x5b,0x61,0x9b,0x15,0x34,0xff,0x39,0x66,0x94,0xf4,0xb8,0xe8,0xfe,0x43,0x01, - 0xf8,0x02,0x53,0x91,0x5f,0x5e,0x3b,0x38,0x7d,0xb4,0x35,0x6d,0xd2,0xc2,0x64,0x4d, - 0x98,0x98,0x45,0x33,0x95,0xf2,0x22,0x0e,0x93,0x6c,0x41,0xac,0xbe,0xfa,0x18,0x46, - 0x69,0xe2,0xe9,0xcc,0x08,0xef,0xa8,0xbd,0xdf,0xb8,0x3c,0x96,0x4e,0xd2,0xfb,0xa0, - 0x92,0xcd,0xa1,0xb7,0xe2,0x5a,0x0d,0xac,0x9e,0x91,0xbb,0xaf,0xa2,0xf3,0xa9,0x51, - 0x33,0xeb,0x59,0x7b,0x60,0x3a,0x9f,0xa9,0x79,0xbc,0x4e,0xab,0x51,0x4b,0xbb,0x32, - 0x8c,0x36,0x9c,0xc2,0xda,0x7f,0x4b,0xe2,0x2b,0x29,0x93,0xde,0x2f,0x6b,0xb5,0x56, - 0x43,0x8f,0xe6,0xc3,0x83,0x4c,0x4f,0xa8,0xec,0x81,0x20,0x85,0x1e,0x2b,0xdd,0xa5, - 0x87,0xef,0xd7,0x68,0x78,0xa6,0x66,0x22,0x7b,0x4e,0x48,0xa5,0xa1,0x39,0xa4,0x17, - 0xe3,0x0c,0x32,0xe7,0x7a,0x95,0xe6,0x31,0x26,0x86,0x1b,0x20,0x54,0x4a,0xaa,0xd2, - 0xcb,0x2f,0x49,0xfd,0x2d,0xf2,0xb3,0x42,0x95,0x25,0x09,0xb0,0x79,0x45,0x02,0x27, - 0x49,0x9f,0x93,0x38,0x45,0x57,0x8c,0x40,0x11,0x77,0xed,0x2e,0xb8,0x43,0xe7,0x84, - 0xa8,0x61,0xd6,0x9b,0x84,0x51,0x3e,0xf2,0x1c,0x92,0x26,0x3c,0xcf,0xac,0xcd,0x23, - 0x4d,0xb7,0x76,0xc9,0xd6,0x0e,0x2b,0xd4,0x74,0x6b,0xef,0x2e,0x24,0x2a,0x7a,0xc8, - 0xcc,0xf6,0x17,0x60,0xe8,0x5d,0x69,0x29,0xde,0xcf,0xfe,0x6a,0x7a,0xd5,0x4e,0x14, - 0x7e,0x29,0xaf,0x5b,0x49,0xdb,0x2f,0x65,0x74,0xa6,0xaa,0x10,0xff,0x42,0x79,0xbd, - 0x6e,0x13,0x60,0xf0,0x70,0xbd,0xda,0xe1,0x79,0xf2,0x80,0x74,0xb8,0xe2,0xaa,0xf1, - 0x0c,0x32,0xb9,0x7e,0x98,0x9f,0x37,0x9e,0xe4,0xe6,0x23,0x6a,0x39,0x6d,0xd9,0xeb, - 0xd1,0x2f,0x84,0x2a,0xd7,0x5a,0xd6,0x3b,0x6a,0x93,0xf5,0x9c,0x65,0x70,0x96,0x5d, - 0xcf,0x35,0xf0,0x6c,0x7b,0x99,0x8d,0x6c,0xdc,0xef,0xff,0x6e,0x24,0xcc,0xa1,0xdf, - 0x2e,0x41,0xa2,0x01,0x55,0x14,0xae,0x94,0xb6,0x2d,0x04,0xf0,0xbe,0x73,0xe9,0x3d, - 0x16,0x79,0x6b,0x05,0x8a,0x65,0x9c,0xd2,0x7e,0x89,0x96,0xe5,0xd9,0x5d,0x59,0x19, - 0xf8,0x96,0x01,0x93,0x59,0x3d,0x3e,0x26,0xf5,0xb6,0x07,0x70,0x5a,0x26,0x49,0x3f, - 0x6d,0x9b,0xdb,0xcf,0xd0,0xed,0xd6,0x7e,0xa0,0xbb,0xa9,0x73,0x5b,0x23,0x38,0x0b, - 0xa0,0xb4,0xab,0xbf,0x86,0x99,0x05,0x5b,0x31,0xf6,0x26,0xc9,0x19,0x6d,0x93,0x49, - 0x11,0x93,0x12,0x4a,0x9b,0xf2,0xfb,0xef,0x9e,0x7b,0x85,0xa2,0xdd,0x53,0x56,0xae, - 0x7a,0x48,0x4c,0xec,0x52,0x15,0xb4,0x5f,0xb3,0xf4,0x8a,0xfe,0x40,0x61,0xac,0xbc, - 0x45,0xcc,0xca,0xe4,0x9c,0x54,0x65,0xec,0x19,0xa2,0x05,0x39,0xbe,0x29,0x98,0x51, - 0x64,0xb9,0xdd,0xd8,0xa4,0xf8,0x52,0x9b,0x50,0xb9,0xbd,0x79,0x91,0x2f,0xa1,0x12, - 0x27,0x33,0x6e,0x01,0xaa,0x70,0x0c,0x62,0x82,0x3d,0x41,0xba,0x2d,0x9a,0xe3,0x06, - 0x2e,0xf3,0x75,0x3a,0xf3,0x26,0xf4,0x52,0x76,0x81,0xed,0xc2,0x34,0x05,0x34,0xa3, - 0x24,0x88,0x08,0x5c,0x62,0x30,0x95,0x22,0x50,0x12,0x22,0x30,0xb4,0x99,0x68,0x32, - 0x61,0xfa,0x43,0x93,0x25,0xa9,0xb8,0x5e,0xa7,0xac,0x92,0x34,0x45,0x6b,0x97,0x79, - 0x71,0x4e,0x03,0xed,0x84,0x1e,0xad,0xd8,0x74,0x61,0x58,0x18,0x8f,0x83,0xf4,0x72, - 0xa3,0xf1,0xc6,0x17,0x84,0x9b,0x20,0x9e,0x21,0x0d,0x75,0x86,0x76,0x96,0xb4,0xeb, - 0xca,0xfa,0x2b,0x6e,0x0b,0x0c,0x0e,0xf6,0x93,0xd2,0x2b,0xa1,0xb5,0x13,0x91,0x5a, - 0xe6,0x90,0x64,0x53,0x0c,0x92,0x04,0xfe,0x3c,0x9b,0x95,0x32,0x44,0xd2,0xe8,0x98, - 0xd4,0xc5,0x25,0x8d,0x6f,0x11,0x67,0x67,0x42,0xca,0x68,0x12,0xd5,0xea,0x2f,0x40, - 0x75,0xb0,0x91,0x6d,0xd2,0xb0,0x45,0xf0,0xb8,0x69,0x30,0xf4,0xc6,0x4e,0x30,0x0f, - 0xee,0xb0,0x43,0xee,0x3f,0xf8,0xdd,0x2d,0x5a,0xd6,0x5e,0x9c,0x25,0xcb,0x98,0x45, - 0x76,0x69,0x6c,0x10,0x0d,0x4a,0x4f,0x91,0x28,0x43,0x14,0xa1,0x97,0xaf,0x2b,0x5a, - 0x06,0x20,0x41,0x45,0x70,0xfc,0xfd,0xb9,0xba,0x9a,0x17,0x6c,0xd8,0x90,0xc1,0x62, - 0x3a,0xae,0x6b,0x29,0x88,0x7f,0xa5,0xb4,0x58,0x7f,0xec,0xf6,0x48,0x59,0xff,0x5d, - 0xb0,0xa9,0xf2,0xf6,0xc7,0x0f,0xfa,0x78,0xca,0xe8,0xfa,0x26,0xbf,0x04,0xea,0x61, - 0xe5,0x92,0xc2,0xbb,0x24,0xe9,0x83,0x3a,0x0d,0x49,0xd6,0x58,0x2e,0x89,0x54,0x63, - 0x3d,0x61,0x65,0x21,0x75,0xd7,0xbb,0xca,0xd7,0x90,0x4c,0xe6,0x49,0x41,0x6b,0x10, - 0x03,0x19,0x21,0x96,0x64,0xb4,0x74,0x8a,0x3f,0xc7,0x32,0x12,0x2a,0xd2,0xe2,0xa7, - 0x31,0x16,0x0b,0x08,0x88,0xef,0xa7,0x69,0x32,0x3d,0xf7,0x32,0xa5,0xe8,0x1e,0xbd, - 0x2e,0xe8,0x0e,0xd9,0x46,0x98,0x52,0x92,0x11,0x96,0x5f,0x28,0xb3,0xc6,0x5a,0xb0, - 0x89,0xce,0x88,0xa5,0xbb,0x32,0xca,0xc6,0xde,0xf4,0x1c,0xf1,0xe3,0x22,0x29,0x93, - 0x49,0x92,0xe2,0x15,0x59,0x3a,0x5a,0x94,0x98,0x95,0xc3,0xb2,0xc9,0x24,0xf0,0xa7, - 0x07,0x0a,0x3d,0x64,0x8a,0x6e,0xd4,0xe9,0x2d,0x3c,0xd8,0x65,0x25,0x5a,0x4d,0x88, - 0x4a,0xea,0x4c,0xd6,0x72,0xf0,0xa0,0xd6,0x9e,0xf9,0x77,0x3c,0xc5,0xab,0xbb,0xf2, - 0x84,0xd1,0x16,0x18,0x4f,0x1a,0xd0,0x80,0xe4,0x5c,0x6c,0xa9,0x1b,0x9f,0x4d,0xd6, - 0xe9,0xf9,0xd6,0xbd,0x08,0xf7,0xde,0xe6,0xb5,0x42,0x55,0xeb,0x9c,0x78,0xf2,0x5d, - 0x5e,0xa9,0xbf,0x46,0x8d,0xa6,0xd5,0x7f,0x01,0x0b,0xb2,0x57,0xac,0xb3,0xc8,0x7b, - 0xe2,0xad,0xe2,0xa2,0x62,0xed,0x51,0xc4,0x4d,0x22,0x41,0xb4,0x6a,0xb4,0x33,0x88, - 0x56,0xd0,0x0a,0x03,0x87,0x60,0x90,0x9b,0x79,0xd3,0x38,0xc3,0xc2,0xc1,0xbe,0x4f, - 0xab,0x79,0xc5,0xbb,0x3c,0xc4,0xea,0x63,0x7b,0x5b,0x0a,0x40,0xd2,0x11,0x13,0xab, - 0xd8,0xa1,0x2a,0x84,0x57,0x0a,0x26,0x3b,0x5a,0x21,0x5a,0x71,0x59,0x47,0xb1,0xd9, - 0xe5,0xd2,0xa7,0xbe,0xb5,0x2e,0xd5,0x0c,0x0d,0x12,0x71,0x23,0x7d,0xd6,0x8b,0x85, - 0x5e,0xce,0x73,0xea,0x35,0x9f,0xf3,0x6f,0x91,0x78,0x20,0x1a,0xad,0x97,0x59,0x08, - 0x24,0xc2,0x5d,0x36,0xfd,0x11,0x5d,0xbc,0x02,0x71,0xe8,0xbc,0x01,0x23,0xf5,0xc0, - 0x19,0xd5,0xac,0xc3,0x00,0x0a,0x9e,0xb3,0xa4,0x05,0xcc,0xa9,0x80,0xbb,0x05,0x53, - 0x4b,0x97,0x76,0x19,0xf1,0x9b,0x21,0xe2,0x31,0x17,0x84,0x96,0xdf,0xeb,0x69,0x61, - 0xa1,0x8c,0x3e,0x41,0x7b,0xd3,0x1c,0x9b,0xb1,0xca,0xc5,0xbe,0x47,0x7a,0x2c,0x66, - 0x80,0xa9,0x24,0xd3,0x33,0x6e,0x57,0x9b,0x02,0xa1,0xe5,0xfe,0x19,0x1d,0x9f,0xe5, - 0x22,0xd2,0x81,0x04,0xd3,0xcc,0x81,0x20,0x32,0x6c,0x13,0x35,0x8d,0x69,0xdc,0x98, - 0x2f,0xbc,0x2b,0xc4,0x9e,0xa6,0x5a,0x60,0x22,0x7a,0x3f,0x5b,0x83,0x59,0x88,0xb4, - 0x88,0x69,0x98,0xaa,0xa2,0x8a,0xb1,0x7b,0x68,0x80,0x34,0xd7,0xb2,0x73,0xc4,0x27, - 0x40,0x0b,0xda,0x40,0x25,0x17,0xcf,0x8f,0x5c,0x1d,0x13,0x08,0xec,0x81,0x47,0xde, - 0x2a,0x89,0xb3,0xec,0xd1,0x62,0xb1,0xd8,0xd1,0x6d,0x06,0x5f,0x84,0x83,0x47,0xfd, - 0xf0,0xd1,0x7d,0x63,0x0c,0x30,0x90,0xb4,0x11,0x4e,0xf3,0xcc,0x5b,0x1c,0xed,0x4a, - 0x3a,0xae,0x89,0x45,0xd8,0xb7,0x7d,0xfd,0x37,0xda,0x16,0xec,0x77,0x96,0x28,0xb8, - 0xac,0x9c,0x6d,0x5a,0x35,0x31,0x89,0x56,0x45,0xb2,0x6c,0xd3,0x0e,0x04,0xa8,0x16, - 0x81,0x6c,0xeb,0x5b,0xa3,0x5f,0x7c,0x64,0x66,0x06,0x47,0x81,0xd5,0xe7,0xef,0xd4, - 0x85,0xb6,0xca,0xcc,0xe7,0x2d,0x8a,0x07,0x6d,0xc2,0x1e,0xe9,0x0f,0xf9,0xa5,0x9a, - 0x99,0x05,0x12,0x63,0x05,0xaf,0xdf,0x66,0x0f,0xfb,0x45,0x35,0xe5,0xaf,0x47,0x8e, - 0x09,0xd1,0x8a,0xf7,0x9f,0xb0,0x61,0xdc,0x51,0x3e,0xbf,0x85,0xc8,0xc4,0xd7,0x6d, - 0x2b,0x03,0x17,0x28,0x8b,0x5c,0xf3,0x39,0x0c,0xae,0xde,0x67,0x30,0xb5,0x84,0x3b, - 0x77,0xe7,0x65,0x3d,0xec,0x87,0xa2,0x2f,0xe7,0xeb,0xe9,0x62,0xe8,0x3d,0x00,0xde, - 0x56,0x31,0x6d,0x38,0x92,0x06,0xb4,0x76,0x43,0xd2,0xd6,0x19,0x11,0x2b,0xbd,0x7b, - 0x69,0x3b,0x7c,0x97,0x7b,0x50,0x74,0x79,0x53,0xf2,0x9e,0xca,0x61,0x73,0x5f,0x2d, - 0x68,0x43,0xf0,0x8e,0x23,0xa2,0xf3,0xeb,0x7f,0xfc,0xef,0x5f,0xff,0xe3,0xff,0x30, - 0x8b,0x23,0xa9,0xc2,0xd2,0x11,0x98,0xe4,0xf9,0x07,0x69,0xfd,0x34,0x03,0xd4,0x13, - 0x09,0x72,0x2a,0xbe,0x80,0xeb,0x00,0x3e,0x5a,0x35,0x3d,0x9f,0xe4,0x1f,0x64,0xa7, - 0x27,0x55,0xc0,0x3b,0xef,0xef,0x89,0x5f,0x26,0x71,0x57,0xab,0xa2,0xc3,0x69,0x0e, - 0x47,0x6e,0x70,0x0d,0xd3,0x8f,0xdd,0x6d,0x47,0xa2,0x1a,0x59,0x4b,0x4a,0xe3,0x41, - 0xdf,0xe0,0x7d,0x4f,0x5f,0xba,0xa6,0xe8,0x23,0x21,0xfb,0xc4,0x48,0xec,0x37,0x0f, - 0x6b,0x1b,0x73,0x6f,0xf0,0xb0,0x89,0xcb,0xd7,0xab,0x5c,0x1b,0x02,0x0b,0x45,0x2c, - 0x9f,0xa8,0x6d,0x43,0x9b,0x1d,0xc6,0xf3,0x8a,0x6d,0x03,0xec,0xa3,0x19,0x76,0x3a, - 0x23,0xfb,0x7e,0x3c,0xa1,0xf5,0xa7,0x55,0x1a,0x01,0x1d,0x7a,0xbc,0xac,0x5a,0xc0, - 0x95,0x8b,0x54,0xcd,0x2b,0xda,0x7d,0x05,0x43,0xd5,0x6f,0x72,0x32,0x11,0x7f,0xb4, - 0xc5,0xe8,0xcb,0x2d,0x75,0xbd,0x6f,0x21,0x58,0x5e,0x18,0x00,0xb8,0xb1,0x1e,0xde, - 0x94,0xf6,0x7a,0x5f,0x8a,0xb4,0x7e,0x71,0x80,0x66,0x35,0xe2,0xf2,0x4b,0x42,0x07, - 0x8c,0x21,0xa6,0x8d,0x75,0x92,0x5e,0xd8,0x63,0xbd,0x0f,0xa6,0x2a,0xc6,0x95,0x67, - 0xaa,0x3c,0xc7,0x28,0x0c,0xb3,0x58,0x25,0x59,0x46,0xec,0x0b,0xed,0x85,0xbc,0xba, - 0x46,0x36,0xe7,0xce,0xad,0xbc,0x72,0xb9,0xb0,0xce,0xa2,0xcb,0xa4,0x5a,0x90,0x38, - 0xc4,0xc8,0x32,0x25,0xa4,0x4a,0xc1,0xe9,0x0c,0xb6,0x80,0x06,0x6b,0x74,0xd2,0x7c, - 0x88,0xdf,0xf7,0xe2,0x29,0x11,0x63,0x22,0xe7,0x5a,0x10,0x77,0x91,0xa3,0x36,0xa8, - 0x7d,0xf5,0x88,0x16,0x94,0xb0,0x63,0xdb,0x75,0x30,0x18,0x3c,0x90,0x95,0xa6,0x16, - 0x6b,0x11,0xe6,0xac,0x48,0x66,0x23,0xfc,0xe9,0x91,0x68,0xb2,0x82,0x0c,0xd7,0x93, - 0x1e,0xcb,0xe1,0x83,0xfb,0xf0,0x25,0x50,0xc3,0xd4,0x46,0xb7,0x1f,0x0e,0xe6,0x45, - 0xc0,0xd2,0xcd,0x83,0x6d,0x79,0x86,0xf8,0x56,0x51,0x49,0xc3,0x2f,0x6b,0xf4,0x20, - 0x61,0x7e,0x7a,0x7e,0xc5,0x8b,0x3d,0xf8,0xd2,0x74,0xfc,0xc6,0x6b,0xb1,0xb0,0xb3, - 0x26,0xb5,0xd9,0x7b,0x7c,0x28,0x4e,0xb8,0xc7,0x87,0x12,0xb5,0x00,0xff,0xcb,0xf1, - 0xe3,0x59,0x72,0x41,0x72,0x5e,0x5c,0x96,0xe3,0x0e,0x06,0x64,0x82,0x1a,0x54,0x71, - 0xfc,0x98,0xf4,0xb8,0xcc,0x3c,0x9b,0xe5,0x15,0x47,0x2a,0xd0,0xad,0xe3,0xc7,0x8b, - 0xc1,0x71,0xed,0xe1,0xa3,0x8b,0xc6,0x9b,0xe5,0x7a,0xd2,0xf1,0x92,0xd9,0xb8,0x33, - 0x2f,0x3b,0xc7,0x3d,0xf3,0xc9,0xa1,0x6e,0x74,0x8f,0xfe,0xe7,0xf6,0x69,0xe8,0xbc, - 0xfe,0x04,0x57,0x6f,0xd6,0x08,0xad,0xf0,0xbc,0xc7,0x8b,0xa3,0xe3,0xd3,0x2a,0x2f, - 0xb0,0x9c,0x22,0x83,0x6a,0x41,0x86,0x96,0x92,0xda,0x3b,0xe2,0x77,0x56,0xc7,0xac, - 0x01,0x4d,0x68,0xcf,0xce,0xfc,0x92,0xd6,0x9e,0xc1,0x72,0x04,0xa2,0xa4,0xcc,0xfc, - 0x8a,0x35,0x0f,0x28,0x32,0x8c,0x2a,0x2a,0x61,0x11,0x38,0xd9,0x16,0x94,0xa8,0x3d, - 0x2b,0x2a,0x85,0x5e,0x5e,0xb0,0x2d,0x06,0xf6,0x1c,0xf1,0x8e,0xb1,0x9a,0x15,0x2f, - 0x09,0x1c,0xd2,0xbb,0x5e,0xf0,0x8b,0x68,0x03,0x7a,0x9f,0xe8,0x47,0xba,0xf3,0x52, - 0xe4,0x6e,0xd6,0xb4,0x44,0x09,0x26,0x29,0x80,0xf5,0x1f,0xf8,0x84,0xe9,0xfd,0xe8, - 0xf1,0xe1,0x8a,0x81,0x77,0xe6,0x41,0x73,0x39,0x1e,0x38,0x3d,0xd1,0xd2,0x94,0x99, - 0x7c,0x95,0x7a,0x60,0x53,0x7a,0x92,0x96,0xb4,0x14,0xba,0xff,0x52,0xe6,0xe7,0xf1, - 0xa1,0x7c,0xa0,0xbf,0x76,0x97,0xc3,0x88,0x9b,0xf6,0x5b,0xbe,0x30,0x4b,0x09,0x28, - 0x0e,0x09,0x0c,0x5a,0x75,0xf9,0xa7,0xb1,0x38,0xc0,0x64,0x20,0x44,0xf3,0xd6,0x4b, - 0x8e,0x6d,0x11,0x34,0x33,0xeb,0xf4,0x03,0xef,0x53,0xbb,0x2a,0xa4,0x34,0x12,0xc8, - 0x06,0x7a,0x22,0xe0,0xd2,0x3b,0xff,0xd2,0x30,0xb2,0x70,0xee,0xb1,0x0d,0xbf,0x83, - 0x39,0x36,0x08,0x80,0x5f,0x90,0xcb,0x57,0xd5,0xb8,0xf3,0xf9,0xe1,0xe7,0x1d,0x6f, - 0xb9,0x4e,0xab,0x64,0x05,0xe7,0x31,0x7f,0xb7,0x3a,0x7e,0x06,0x86,0xf0,0x78,0x72, - 0x4c,0xfc,0x66,0x49,0x03,0x3f,0x96,0x25,0xc2,0x7a,0xc5,0x7c,0xfb,0xcf,0xc9,0x8a, - 0x6f,0x13,0xfb,0x39,0x67,0xbd,0x99,0xd7,0x52,0x74,0x19,0x12,0xf8,0xa6,0x8b,0x3c, - 0x2f,0x95,0x5e,0x02,0x34,0x68,0xe0,0x84,0x93,0xa2,0x73,0x2c,0x7c,0xa3,0xf4,0x0e, - 0xb4,0x5e,0xc5,0xce,0x77,0x0f,0x14,0x61,0x4a,0x6c,0x81,0x16,0x3a,0x4d,0x21,0xd5, - 0xb2,0xb6,0xbf,0xbf,0x24,0xcd,0x25,0xaf,0x46,0x1e,0x75,0xe9,0xad,0x33,0x74,0x58, - 0x1a,0x46,0x34,0x21,0xaa,0x56,0xaa,0xc2,0x2c,0xf5,0x21,0x4f,0x09,0xff,0x5c,0xdb, - 0x99,0x61,0x81,0x50,0x06,0xfe,0x0b,0x96,0x64,0x9d,0x6e,0xa3,0xc5,0xb2,0x3c,0x93, - 0xe7,0xf8,0x71,0xac,0xd7,0xa8,0xf9,0x0a,0x2b,0x94,0xf2,0x92,0xfc,0x3c,0x7e,0x9c, - 0xd0,0x9b,0x49,0xfb,0xdb,0x9f,0xc2,0x33,0x69,0x88,0x0d,0x59,0x1d,0x8f,0xe9,0x05, - 0xdd,0x76,0x68,0x76,0xe7,0xd8,0x95,0xcd,0x5d,0xbc,0xb3,0x78,0x64,0x51,0xe3,0x16, - 0x94,0x7a,0xd3,0x82,0x3f,0xda,0xa0,0xaa,0x6a,0x14,0xda,0x85,0xd9,0xd3,0x3a,0x95, - 0x80,0x88,0x8b,0x4f,0x8c,0x82,0x34,0xbf,0x27,0x69,0xda,0x39,0x3e,0x55,0x29,0xf5, - 0x86,0x95,0xdb,0xda,0x27,0xb7,0x7c,0x87,0xa6,0x7f,0x4c,0x68,0x22,0x9f,0xe5,0x97, - 0x19,0xf3,0x9f,0x1f,0xbf,0x7d,0x7d,0xf7,0x4f,0x9f,0xd1,0xc5,0x31,0xfd,0x51,0x88, - 0x2d,0xe0,0xae,0x9b,0x13,0xf5,0x11,0x80,0x49,0x4e,0xf9,0xbe,0x98,0xdd,0x32,0xf1, - 0xa7,0xf4,0xd4,0x63,0x81,0xef,0xce,0xdb,0x9d,0xaf,0x5a,0x36,0xfb,0x2d,0x48,0x96, - 0x6e,0x61,0x19,0x61,0x2a,0xdf,0x26,0x09,0xcf,0xa0,0xe7,0xee,0xf2,0xda,0x55,0x16, - 0xa1,0xf5,0xf8,0x35,0xf1,0x50,0x45,0x84,0x93,0xe4,0x3b,0x0f,0x81,0x38,0xb0,0x13, - 0x68,0x3d,0x0b,0x06,0x88,0x4b,0xdc,0x07,0xb5,0xd4,0xba,0xe8,0x84,0x64,0x36,0x05, - 0xb3,0xd2,0x8c,0xf7,0x6a,0xb4,0xb7,0x9f,0x4d,0xca,0xd5,0xe8,0x71,0xec,0x2d,0x0a, - 0x35,0x1f,0x77,0x0e,0x3b,0xc7,0xdf,0x90,0xae,0xf6,0xf8,0x30,0x26,0x08,0x74,0x17, - 0xb6,0x6b,0x13,0xc8,0x62,0xe3,0x62,0xfe,0xae,0x9b,0xd8,0x60,0x9c,0x59,0x3e,0xe5, - 0x08,0x13,0x18,0x75,0x9f,0xa7,0x0a,0x3f,0x9f,0x5e,0x7d,0x3b,0xa3,0x37,0x36,0x7b, - 0x88,0x2c,0xa2,0xc1,0x8e,0xff,0xae,0xeb,0xd3,0x3f,0x7e,0x10,0xa6,0xfa,0x2a,0xd5, - 0x97,0x34,0xe4,0xe7,0x29,0xdf,0xa0,0x5f,0x74,0x03,0xa4,0x0b,0x97,0xf8,0x97,0x2e, - 0x41,0x72,0x70,0x89,0x7f,0x71,0x59,0xf2,0x45,0x49,0x3f,0x79,0x49,0x7e,0x91,0x6f, - 0x7f,0xa1,0x6b,0xde,0x4a,0x4f,0xab,0x0c,0xd7,0xfc,0x9b,0xee,0x61,0x59,0x70,0x8d, - 0x7f,0xf5,0x25,0xe1,0x8b,0xb9,0x43,0x3f,0xf5,0xcd,0xef,0xcc,0xad,0xef,0x4c,0xc3, - 0x82,0xd2,0xb8,0x2d,0xbf,0xf4,0x8b,0x84,0xad,0xe6,0x55,0xfa,0xc9,0xf0,0x09,0x23, - 0x15,0x18,0xe5,0x37,0x6e,0x0b,0xf9,0xe7,0xbb,0xf2,0xd3,0x36,0x2c,0xa8,0xc7,0x2d, - 0xcb,0x4f,0x9f,0xb4,0x87,0xbd,0xc3,0x43,0x0f,0xc1,0x6f,0x44,0xdc,0xaa,0x24,0x35, - 0x6b,0x58,0x56,0x1e,0x07,0x1e,0x5a,0x7e,0xe7,0xd8,0x8e,0x20,0xbb,0x8b,0xf1,0x70, - 0x19,0x5f,0x79,0x44,0x34,0x21,0x4a,0x91,0xae,0x0e,0x8e,0x8b,0xd6,0x08,0x53,0x35, - 0x77,0x64,0x0d,0x99,0x94,0xe3,0x29,0xb1,0x76,0xc2,0x02,0xe2,0xbc,0x4a,0x18,0x24, - 0x58,0x34,0x89,0xec,0x25,0x5b,0x0c,0xf0,0x22,0x9b,0xa2,0xf2,0xe5,0x72,0x9d,0x91, - 0x3a,0x41,0x6d,0xaa,0x55,0x8f,0xc4,0xb9,0x73,0x34,0x47,0x34,0x15,0xd6,0x28,0xb1, - 0x47,0x12,0x68,0x8a,0x2d,0x62,0x2c,0x01,0xf2,0xc6,0x65,0xb3,0x65,0xe9,0xa9,0x0f, - 0x44,0x43,0x08,0x44,0xd6,0xed,0x63,0x19,0x43,0xcf,0xc0,0xc1,0xf6,0x84,0x5c,0xa0, - 0x2b,0x8a,0xe4,0x82,0xdb,0x88,0x04,0x45,0x48,0x17,0xa4,0xdd,0x3b,0xe6,0x00,0xc0, - 0x95,0xca,0x20,0x12,0x9f,0x16,0x53,0xbe,0x96,0xd9,0xf9,0x1e,0x2a,0xbe,0xe1,0x19, - 0x84,0x68,0x9e,0xa3,0x64,0x60,0x20,0x2b,0xbd,0x1b,0x10,0x7e,0x29,0x06,0x33,0xd9, - 0x02,0xd1,0x04,0x61,0x44,0x04,0x1d,0xb6,0x03,0xed,0x03,0xb4,0x35,0x03,0xcd,0xb0, - 0x52,0x8a,0x98,0x8d,0x61,0x56,0xc0,0x86,0x49,0xe5,0x92,0xe6,0x81,0x20,0xc2,0xc4, - 0x5c,0x8a,0xd1,0x25,0x3d,0xf7,0x0c,0x79,0xc4,0x7c,0x56,0x50,0xb2,0xa2,0x7a,0x3f, - 0x10,0x96,0x3c,0xcd,0x3f,0xa8,0xb2,0x6b,0x77,0x85,0x60,0x36,0x3c,0x41,0xc5,0x95, - 0x10,0xc6,0xbc,0x20,0x44,0xea,0xfa,0xd6,0x5a,0xe6,0xc3,0xc9,0xe5,0xb6,0xc0,0x14, - 0xec,0x34,0x5d,0x9f,0xa1,0x19,0xc2,0x16,0xcc,0x0c,0xc9,0xd6,0xe3,0x77,0xef,0x47, - 0x74,0xf5,0xe9,0x06,0x87,0x3c,0x23,0x8a,0x30,0x6a,0x37,0x36,0x6d,0x1a,0x5c,0x53, - 0x53,0x3a,0x28,0x31,0x9a,0xc5,0x55,0x4c,0xd3,0x15,0x95,0xd4,0x19,0xc2,0xf5,0xf6, - 0x60,0x97,0x94,0xb8,0xb8,0x75,0x35,0xda,0xdb,0xf0,0x94,0x1b,0x37,0x1a,0xad,0x2b, - 0x1b,0x11,0x53,0x46,0x30,0x11,0xe3,0xac,0xa1,0xdd,0x58,0xb7,0x34,0xfc,0x62,0xc4, - 0x06,0xbd,0xf9,0x79,0x5d,0x0a,0xf7,0x46,0x53,0x25,0x51,0x16,0x79,0x2d,0x29,0xed, - 0x48,0x87,0x5e,0xa7,0xe6,0x18,0x1d,0x96,0x11,0x89,0x7e,0x31,0x9d,0x22,0xd0,0x79, - 0x7d,0x58,0x9a,0xfb,0xb3,0x2a,0x72,0x8d,0x7a,0x68,0xcb,0x41,0x33,0x36,0x68,0xb1, - 0xb1,0x09,0x96,0x57,0xac,0x5f,0xa2,0x8d,0xad,0x31,0xa1,0x65,0x76,0xc6,0x73,0xa6, - 0x65,0xda,0xa9,0x71,0x3a,0x60,0x1a,0xea,0x69,0x5f,0xaf,0x68,0x2e,0xd4,0x53,0x5a, - 0xdf,0x7a,0xce,0xab,0x9c,0xf0,0x75,0x5c,0x2f,0x69,0x94,0xaa,0xec,0xac,0x5a,0x8c, - 0xf4,0xe3,0x6c,0xbc,0xb5,0x56,0xce,0x73,0x20,0x4a,0xc4,0x5c,0x25,0xd2,0x4c,0x65, - 0xcc,0xad,0x9d,0xf8,0xb0,0x6a,0xfa,0x43,0x1f,0x2c,0xc6,0x37,0x6f,0x12,0x05,0x6a, - 0x44,0xf7,0xfa,0x5b,0xdc,0xcc,0xeb,0xfa,0x07,0xd9,0x81,0x1f,0x34,0x3e,0xb0,0xe1, - 0xbc,0xd9,0x78,0x3c,0xee,0x9b,0x27,0x44,0x8e,0xb6,0x9a,0x72,0x78,0xea,0x4e,0x3b, - 0x78,0x7b,0xb7,0x1d,0x21,0x75,0x8d,0x66,0xba,0x78,0xc8,0x23,0xd8,0xdf,0xe7,0x7f, - 0x8e,0xfb,0xc1,0x89,0xcf,0x0e,0x1c,0x0d,0x25,0x4d,0x22,0x0d,0xab,0x5e,0x48,0xdb, - 0xc7,0x77,0xcd,0xc0,0x65,0x9e,0x85,0x6e,0x76,0xe2,0x9b,0x9d,0x5a,0xf3,0x25,0xde, - 0x8f,0x84,0xb6,0x43,0x7e,0xe9,0xc0,0xd7,0xfb,0xdc,0x3f,0xe8,0xca,0x4a,0x8c,0xc7, - 0x83,0x13,0x9f,0x3a,0x21,0xb2,0x1f,0x0c,0x7d,0x9f,0xf1,0x53,0xc3,0x4a,0x08,0x06, - 0x79,0x73,0xec,0x04,0x99,0xea,0x55,0x9a,0x60,0xed,0x9c,0x45,0x34,0xab,0x77,0x19, - 0x67,0xd5,0x2d,0x0b,0x78,0x6f,0x3c,0xe6,0xaf,0xdc,0xf5,0xe4,0xeb,0xb6,0xdd,0x34, - 0x8d,0xf4,0x6e,0x1b,0xa3,0x45,0xd9,0x43,0x2e,0x32,0x11,0x94,0x42,0xb9,0x9e,0x11, - 0xb5,0xc4,0x5e,0xf5,0x5e,0xfd,0xd3,0xdb,0xb7,0x7a,0xd2,0x38,0x34,0xb2,0xb4,0xf4, - 0x8c,0x19,0xb2,0xf7,0x46,0x5d,0x20,0xb8,0x3d,0x3b,0x53,0xa5,0x41,0x62,0x84,0xec, - 0x32,0x0e,0x6b,0x82,0x29,0xd4,0x52,0x13,0x48,0x7a,0x7f,0xdc,0x1b,0x8c,0x6a,0x5c, - 0x8e,0x57,0xc4,0x1e,0x5e,0x6b,0xea,0xc9,0x23,0xc3,0xfe,0x2e,0x89,0x35,0x5e,0x84, - 0x98,0xec,0x35,0x34,0x28,0x1a,0xcb,0x54,0xf1,0x2c,0x21,0x50,0x96,0xaf,0xbc,0xfd, - 0x7d,0xda,0x73,0x17,0x34,0xcf,0x75,0xcb,0x81,0xd0,0x03,0x33,0x6b,0xf4,0xf8,0x6b, - 0x06,0x6d,0x36,0xa6,0x9f,0xf7,0xdc,0x37,0xf1,0x8a,0x03,0x51,0x21,0x77,0x2e,0x18, - 0x41,0xaa,0xf1,0xf5,0x06,0x57,0x02,0xc8,0xcd,0xcd,0xbb,0xf7,0x2d,0xa4,0x09,0x11, - 0xc6,0xaa,0x7a,0x77,0x5a,0x61,0x7b,0xd2,0x55,0x54,0xe5,0x2f,0xf3,0x4b,0x55,0x7c, - 0x4d,0x24,0xaa,0x1b,0xbc,0x1f,0x0f,0x36,0x41,0xbd,0xf5,0x0c,0xa2,0x9a,0xad,0xd9, - 0xb6,0x34,0xcc,0x68,0x99,0x78,0x66,0xe3,0x7b,0xf7,0x9c,0xb6,0x9b,0x84,0xef,0xe6, - 0xc6,0xf7,0xb7,0x3b,0x1b,0xf1,0xb7,0xf5,0xda,0xe6,0x99,0xdc,0xa1,0xb9,0xca,0xb3, - 0x20,0x3b,0x38,0xc0,0x65,0xdb,0x62,0xf3,0x2b,0x66,0x92,0x69,0x7e,0xe6,0xc4,0x49, - 0xcc,0xc4,0xea,0x99,0xc3,0x95,0x3b,0x11,0x82,0x65,0x0e,0xb8,0xcb,0xa4,0x2c,0xa9, - 0xcf,0xdd,0x77,0x7a,0x1a,0x0a,0x26,0x4e,0x63,0xff,0x2d,0x1b,0x66,0xb2,0x73,0x09, - 0xb0,0x9d,0x79,0xbc,0xb9,0xeb,0x4d,0x93,0xb9,0x1b,0x86,0x1e,0xd0,0x14,0x91,0x8c, - 0x26,0xd4,0xc0,0xb7,0xc3,0x91,0xce,0x68,0x3b,0x57,0x07,0x63,0x3f,0xa4,0x36,0xe4, - 0x06,0xbd,0x8f,0x5d,0xc9,0x6e,0x80,0xa4,0x64,0xe4,0xd3,0xdf,0x94,0xf1,0x55,0xb7, - 0x0a,0x69,0x03,0xe7,0xe7,0xd4,0xb4,0x2a,0x0a,0x9f,0x85,0x39,0x1e,0xfb,0x46,0xb3, - 0x8c,0x37,0xaa,0x24,0x2d,0xb2,0x64,0x97,0xc4,0xe4,0x4a,0x6b,0x76,0x6c,0xb4,0x1f, - 0x7a,0xf0,0x13,0x33,0x2c,0x5a,0x1e,0x20,0x1d,0x8f,0x9d,0xc4,0x7c,0x4f,0x6c,0x4d, - 0xa5,0x4b,0x99,0xa5,0xbf,0xf3,0x50,0xa5,0xc1,0xf5,0xeb,0x17,0x91,0xbe,0xa4,0x45, - 0x4b,0xb2,0x79,0xee,0xd3,0xed,0x9b,0x1b,0x74,0x2f,0x1d,0x3f,0xf1,0x10,0x49,0xa0, - 0xad,0x4b,0xf0,0x9e,0xb2,0x17,0x2c,0x2e,0x84,0xf5,0x5b,0xef,0x28,0x73,0xf5,0xc8, - 0xfb,0x81,0x06,0x6a,0xbb,0xd5,0xfe,0x66,0xf6,0x6e,0x88,0x74,0xb0,0xeb,0x73,0xe6, - 0xed,0x28,0x3e,0x90,0x19,0x8c,0x59,0xc6,0x10,0xc6,0x02,0x90,0x26,0x64,0xe5,0x22, - 0xbf,0x04,0xeb,0x4a,0xad,0xe7,0x19,0xad,0x49,0x2f,0xa4,0x08,0x87,0xf0,0xdc,0xa0, - 0x0d,0x88,0x0f,0xfc,0x2e,0xfb,0x4a,0x10,0xf2,0x8e,0x66,0x27,0x0a,0x10,0x4f,0x45, - 0x0e,0x6a,0x38,0x0f,0xd3,0x1c,0xd3,0xa2,0xf9,0xb1,0x88,0x25,0x6c,0xa8,0x2a,0xa1, - 0x1e,0x1f,0x95,0x90,0x5f,0x48,0xd2,0x82,0x18,0x27,0xe2,0xa1,0x30,0xe8,0x79,0x8a, - 0x48,0x12,0xf8,0x96,0x32,0xce,0x35,0x11,0xca,0xc1,0x8a,0xab,0xc8,0xcc,0xfc,0x93, - 0xc5,0xda,0xf2,0x0a,0xa1,0xfa,0x85,0x16,0xb6,0xec,0xfc,0xe3,0x41,0x17,0x24,0x9c, - 0x57,0xc0,0xb0,0xc6,0xfe,0xf8,0x19,0x61,0xbd,0xa4,0x31,0x60,0xe5,0x49,0x9c,0xd6, - 0x2b,0x21,0x57,0xd1,0x8a,0xd8,0x70,0x46,0x22,0xef,0x4c,0x45,0xb4,0xd0,0xaa,0xa8, - 0x9e,0xb2,0xc8,0xd9,0xd5,0x5d,0x53,0x63,0x51,0x46,0x8d,0x9e,0x92,0x3c,0x41,0x23, - 0xe6,0x36,0xf4,0xa3,0x88,0xd5,0xa4,0x97,0xc0,0x82,0x78,0x36,0xeb,0xfa,0xc4,0x58, - 0xec,0xc6,0xaf,0x76,0x89,0xbc,0xa6,0x31,0x92,0x0f,0xc1,0xce,0x84,0x6e,0xb7,0x86, - 0xad,0x57,0xf5,0x83,0xc3,0x41,0xbf,0xdf,0x0f,0x6a,0xec,0x2d,0x8f,0xc7,0x47,0x27, - 0x18,0x11,0x21,0x39,0x56,0xd0,0x3f,0x28,0x0f,0x68,0x8f,0x0c,0xe5,0xd6,0xaf,0xff, - 0xf6,0xbf,0xfc,0xd0,0x22,0x97,0xa0,0x35,0xfe,0xa0,0x6b,0xbb,0xc3,0xed,0x6c,0x05, - 0x2c,0x4f,0x70,0x8c,0xf7,0x45,0x9c,0x3a,0xf7,0x85,0xfd,0x99,0x39,0x25,0x5a,0x63, - 0xdf,0x41,0x43,0xe1,0x43,0x86,0x68,0xd3,0x9c,0xe6,0x67,0x84,0x53,0x5d,0x43,0x93, - 0xeb,0xa6,0xae,0x6f,0xed,0xa3,0xb9,0x68,0x9b,0xd6,0x39,0x14,0x6d,0xcf,0x4c,0xe3, - 0x46,0x4b,0xd1,0x1c,0x3f,0x37,0x55,0x5a,0x63,0xd0,0x5e,0x38,0xc2,0xb2,0x75,0xb6, - 0x66,0x71,0xc0,0xbb,0x80,0xd5,0x14,0x52,0x7d,0x7e,0x8e,0xed,0x7f,0x4e,0x3b,0x97, - 0xcd,0x2c,0x84,0xe4,0x08,0xa6,0xaa,0x8c,0x23,0x2f,0x64,0x39,0x9e,0xda,0x89,0x67, - 0xb5,0xeb,0x2e,0xd4,0xfb,0xad,0xd6,0x22,0x98,0x4e,0x2d,0x54,0xda,0xc4,0xe9,0x72, - 0x85,0x8c,0x04,0x16,0xbf,0x40,0x95,0x78,0xa7,0xf0,0x76,0xd0,0x7b,0x09,0x2a,0x8e, - 0xb1,0xfd,0x49,0x02,0x01,0x72,0x07,0x1c,0x37,0xa9,0x78,0x2c,0x1c,0x4f,0xa9,0xd9, - 0x91,0x46,0xee,0x67,0x8d,0x8a,0x77,0xc2,0x0c,0xde,0x44,0xd0,0x23,0xf1,0x25,0x92, - 0xa4,0x58,0x70,0x12,0x83,0xd7,0xd5,0x7a,0xd5,0x9f,0x0a,0x75,0x46,0x13,0x56,0x5c, - 0x45,0x0b,0xe3,0x99,0xbc,0x0a,0x6a,0xd5,0x20,0x11,0x1a,0x80,0x06,0xb9,0x3d,0xda, - 0xdd,0xcb,0x55,0x55,0x36,0x84,0xff,0xea,0x95,0x6c,0x5c,0xa4,0xec,0xd0,0x62,0x18, - 0x75,0xe6,0xde,0xbd,0x25,0xd0,0xc1,0x28,0x86,0xb2,0x38,0xdf,0x21,0x0d,0x44,0xbf, - 0x71,0xe2,0x1b,0xbb,0x2c,0x91,0x54,0xeb,0x8a,0xa3,0x15,0xc3,0x67,0xec,0xd7,0x6a, - 0xf9,0x84,0x55,0xe3,0xa1,0x2f,0xfe,0x9a,0xf9,0xdc,0x97,0x2e,0x44,0xe2,0xd4,0x89, - 0x5e,0xfa,0x5d,0x7e,0x22,0xfa,0x67,0x53,0xaa,0x33,0x2f,0xec,0xef,0xd7,0xca,0x56, - 0x70,0xc2,0x1b,0xc5,0x8f,0xc5,0xa0,0x47,0xc3,0x06,0x49,0xc5,0xe4,0x63,0x2a,0x40, - 0xfc,0x34,0xdd,0x86,0xe7,0xb8,0xcc,0x21,0x6f,0x94,0x32,0x3d,0x9a,0x2a,0xfa,0x2c, - 0x9b,0x09,0x73,0x69,0x69,0xfe,0x5a,0xb6,0x6b,0x7d,0x67,0xb4,0xad,0xe8,0x71,0xd2, - 0x1c,0x32,0xb4,0xd6,0x19,0xdd,0x43,0x26,0xcd,0x9e,0xa1,0xee,0x32,0x58,0x58,0x1e, - 0x19,0xa1,0x48,0x19,0x48,0x59,0x8b,0x35,0xe8,0x4a,0x20,0x58,0x0c,0x96,0xe8,0x1d, - 0x95,0x89,0x1b,0x38,0xa6,0xc5,0xae,0x48,0x69,0x05,0x7a,0x31,0x41,0x8e,0x93,0xb4, - 0x64,0xff,0x36,0xe6,0xcc,0x63,0x3d,0x95,0x89,0x30,0x30,0x0a,0xe6,0x6b,0x1e,0x5f, - 0xa7,0x8e,0x17,0x04,0x71,0x2e,0x90,0x3b,0xd3,0x81,0x88,0x36,0x87,0x03,0xda,0x58, - 0xa3,0xd1,0xde,0x7a,0x05,0x2d,0x25,0xab,0xb4,0x69,0x1a,0xbe,0x2d,0xb3,0x4b,0xc4, - 0xed,0xe1,0x78,0xca,0xe7,0xc9,0x07,0xc6,0x45,0x17,0x77,0xc4,0xb0,0xfc,0x86,0x3a, - 0xbe,0xb2,0x24,0x40,0x4f,0x9f,0x96,0xc1,0x38,0x52,0xa8,0x39,0xb1,0xc4,0xe1,0x39, - 0x81,0xee,0x1a,0x64,0xcd,0xe7,0x70,0x23,0x51,0x87,0x01,0xb0,0x13,0x38,0xc5,0xbb, - 0xa2,0xb8,0x92,0xde,0xe3,0x33,0xf8,0xc2,0x39,0xab,0x66,0x99,0xc3,0x4a,0x43,0xe4, - 0x0e,0x6c,0xdc,0xe6,0x44,0xb1,0xcc,0xc2,0xc4,0x04,0xad,0xce,0x1b,0x96,0x6f,0x6d, - 0x8f,0x30,0xdb,0x4c,0x1b,0xd6,0x92,0xd2,0xa1,0x08,0x12,0x78,0x90,0x94,0xcc,0x15, - 0x6d,0xdb,0x2e,0xfe,0x8b,0x87,0x88,0x08,0x5a,0xfe,0x5f,0x12,0x75,0xd9,0xbd,0x66, - 0x6f,0x18,0xe9,0x44,0x44,0xe5,0x68,0x2b,0xfb,0x0d,0x45,0x94,0x81,0x19,0x59,0xb9, - 0x02,0x36,0x06,0x38,0xc4,0x3c,0xe8,0x86,0x5a,0x4c,0x0e,0xf5,0x0e,0x47,0xda,0x1a, - 0x21,0x3b,0xb3,0x41,0x58,0x4b,0x79,0x19,0xd1,0x4e,0x91,0x28,0x13,0xb9,0xe3,0x81, - 0xe9,0x78,0x92,0x04,0x26,0xbc,0x10,0xe2,0xc6,0x0b,0x7e,0x5f,0x84,0x35,0xb6,0x45, - 0xbd,0x51,0xbf,0x6c,0xf3,0x41,0xe0,0x81,0xac,0x8c,0x79,0x81,0xa4,0x91,0x33,0x55, - 0x75,0xfd,0x86,0x71,0xc6,0x0f,0xaf,0x75,0xfa,0xda,0x70,0x40,0x44,0xbe,0xbf,0x69, - 0x49,0x7c,0x34,0xb2,0x57,0xdd,0x75,0xc0,0x53,0x6d,0xf9,0x8e,0x15,0xa8,0xbc,0x1d, - 0xf0,0xe4,0xee,0xbc,0x6c,0x6c,0xe4,0x59,0x64,0xa8,0x42,0xd7,0x61,0x83,0xb3,0x68, - 0x5e,0x28,0x45,0xdc,0xef,0xe8,0x01,0xe4,0xbf,0xdf,0x3f,0xf5,0x70,0xed,0x93,0xee, - 0x64,0x16,0x13,0x34,0x56,0x7f,0x69,0xa4,0xbb,0x9a,0x86,0xd9,0x46,0x1d,0x48,0x88, - 0xb3,0xc0,0x35,0x58,0x7c,0xf3,0xf6,0xd5,0xcb,0xb1,0x5f,0x7f,0xf3,0x3d,0xcc,0xa4, - 0xcf,0x78,0xdf,0x32,0x94,0xc1,0xc8,0xf3,0x98,0xf2,0x5e,0x22,0xce,0xb6,0x1c,0xc2, - 0x3e,0x40,0x3b,0x13,0xa6,0x2e,0x98,0x60,0x0a,0x2c,0x1b,0x67,0xa3,0x11,0x9a,0x9c, - 0xd1,0xfe,0xe0,0x76,0x66,0x91,0x99,0x44,0x92,0xcb,0x09,0x6d,0x49,0xac,0x2f,0xb0, - 0xb0,0x90,0xc8,0x77,0x05,0xfc,0x95,0x9e,0x47,0xe1,0xff,0x69,0x32,0xb6,0xf6,0x47, - 0x64,0x32,0x55,0x4a,0x9b,0x20,0x61,0x51,0xf4,0xf5,0x08,0x78,0xd2,0x57,0x11,0xc2, - 0xf5,0x3e,0xd0,0xd6,0x99,0x71,0xac,0xc3,0x85,0x0a,0xd2,0xc4,0xa1,0xaf,0xbe,0x26, - 0xbb,0xa6,0xe5,0xec,0xd6,0x86,0x61,0xe4,0xa5,0xa6,0x5d,0x7a,0xee,0xd3,0xb7,0x59, - 0x63,0x69,0x74,0x77,0x07,0x83,0x46,0x9b,0xcb,0x4f,0x36,0xba,0x6c,0xb4,0xba,0xf4, - 0x71,0xa7,0xd9,0x2e,0x82,0xe1,0x4d,0xa3,0x78,0x88,0xcc,0xbf,0xb1,0x0f,0x5b,0x83, - 0xef,0xdc,0x6e,0xd1,0x8c,0xf1,0x8a,0x99,0x86,0x50,0xda,0x09,0x36,0x2e,0x78,0xd5, - 0xd9,0xa7,0xc0,0xd3,0x2f,0x57,0x67,0x2e,0x94,0x55,0x7c,0x46,0x6a,0xc7,0x2a,0x12, - 0x0d,0xe4,0x84,0x55,0x11,0x30,0x03,0xf7,0xf5,0xe6,0x10,0xcc,0x9b,0x5a,0x67,0x19, - 0x6a,0xa3,0x80,0xdf,0x58,0x2e,0x79,0x68,0x57,0x5b,0xcb,0x7b,0x97,0xb7,0xaf,0x37, - 0x3c,0x3f,0xd4,0x69,0x79,0xe9,0x02,0x57,0x5e,0xda,0x56,0xa5,0x85,0xe9,0xad,0x0d, - 0xb0,0x21,0x8d,0x1a,0x98,0x46,0xec,0x2e,0xf3,0x8d,0x81,0xd1,0xa7,0x3b,0x6e,0x8b, - 0x2a,0x75,0x9a,0x6c,0xea,0x94,0x76,0x6c,0xf4,0x09,0xad,0x00,0x93,0xa9,0x71,0xad, - 0x29,0xd6,0x9f,0x11,0x90,0xa4,0xae,0x13,0x03,0xfc,0x9a,0x58,0xd9,0x0c,0x79,0xc2, - 0x84,0x8c,0xee,0x9d,0xf2,0x32,0xa8,0xdf,0xd6,0x09,0x09,0x67,0xe0,0x6f,0x63,0xc3, - 0x0e,0xea,0x47,0xad,0x00,0xd8,0x37,0x2e,0x93,0x42,0x3d,0xa3,0xaf,0x89,0xec,0xd8, - 0x26,0x37,0x8a,0x36,0xeb,0xd6,0xcc,0xae,0x3e,0xb5,0xf8,0xe5,0x4a,0x9b,0xb6,0x24, - 0x61,0xd5,0x47,0x1c,0x81,0xbf,0x03,0xf7,0xaa,0xee,0x64,0xcf,0x82,0xe8,0xbe,0x91, - 0xed,0x8c,0x35,0x5b,0xee,0xdc,0xaa,0xce,0x82,0x8f,0x23,0xc3,0xd6,0xfb,0x90,0x75, - 0x0b,0x1a,0x61,0x88,0xd9,0x09,0x76,0x9a,0xb3,0x8f,0x85,0x4a,0x05,0x4d,0x94,0x98, - 0xdc,0x3a,0x72,0xe1,0x73,0x34,0xf6,0x89,0x8b,0x01,0x33,0x60,0xc0,0xa4,0x69,0x6f, - 0x13,0x23,0x96,0x83,0x18,0x93,0xb6,0x2d,0x48,0x6f,0xd9,0xc1,0xe8,0x2d,0x18,0x4e, - 0xea,0x4d,0xb8,0x33,0xac,0x49,0xcb,0x64,0x32,0x35,0x76,0x5f,0xb2,0xeb,0xba,0xd1, - 0xff,0xd2,0x13,0x6d,0xf1,0xf2,0x5a,0x8c,0x42,0x44,0x6e,0xe5,0x12,0x26,0x86,0xd0, - 0xb3,0x97,0x6f,0xd4,0xc5,0xcd,0x4d,0x3f,0x14,0xce,0x1b,0xb2,0xd4,0xa1,0xdb,0xd8, - 0x36,0x75,0x10,0x6f,0x9b,0x42,0xa8,0xea,0x6e,0x6b,0x64,0x0e,0xd3,0xaa,0xb1,0x94, - 0x59,0xdc,0x94,0x85,0x32,0xf0,0x1d,0x66,0xce,0x46,0xdf,0x67,0x81,0x82,0xdd,0x35, - 0x04,0x80,0x96,0x19,0x1c,0xb3,0xc2,0x36,0x0f,0x0d,0xae,0x1b,0x4c,0x9a,0xf5,0xe0, - 0x6e,0xd3,0x80,0xad,0x5f,0x30,0x92,0xc3,0xeb,0x1c,0x12,0x2b,0xbf,0xa8,0x05,0x88, - 0xda,0xb9,0xc2,0x00,0x00,0x9c,0x61,0x0d,0x4f,0x52,0x3a,0x32,0x4c,0xa9,0x5f,0x8c, - 0xcb,0x73,0xad,0x3f,0xa3,0x9d,0x9a,0x23,0x3b,0x52,0x1c,0x03,0xa2,0xc5,0x37,0xfd, - 0x96,0x63,0x42,0x33,0xdf,0x99,0x49,0x81,0x3d,0x43,0xf2,0xfa,0xf3,0xec,0x54,0x27, - 0xa6,0xcb,0x6d,0xa7,0x10,0x40,0xf7,0xbe,0x51,0x5f,0xe9,0x3e,0xa7,0xe6,0x37,0x25, - 0x8e,0x43,0xbd,0x6e,0x7e,0x78,0x44,0x02,0x47,0xd8,0x26,0x68,0xcc,0x82,0xb6,0xe5, - 0xb7,0xf6,0x25,0x2c,0x7e,0xa1,0x57,0xdd,0x2e,0xf7,0x26,0xbc,0xe6,0x51,0x0f,0x45, - 0x1c,0x0c,0x5c,0x77,0x00,0xa9,0xd6,0x3d,0x70,0x11,0xf1,0x3b,0x15,0xe7,0x30,0x34, - 0xe4,0x88,0xaf,0xd3,0xa9,0x59,0xc8,0x84,0x46,0xfc,0xd7,0x79,0x96,0x4f,0x3c,0x0c, - 0x1e,0x52,0x71,0x2e,0x7a,0x5f,0x3c,0x09,0xd8,0x9e,0x2f,0xba,0x9e,0xf2,0xca,0x05, - 0x49,0x7d,0x33,0x2d,0x92,0x85,0x26,0x90,0x88,0xd6,0xa3,0x67,0xc2,0xd7,0x6b,0xa3, - 0x10,0x5b,0x3d,0x13,0xb6,0xc8,0x89,0x8b,0x88,0x7f,0x3f,0xa9,0x60,0x0e,0xb4,0x8b, - 0x60,0xa6,0x12,0x06,0x45,0x1a,0xc7,0x22,0x2e,0x4f,0x17,0x04,0xd9,0x98,0x2b,0x52, - 0xac,0xb4,0x41,0x8d,0x99,0xdf,0xf7,0x73,0xa2,0x69,0x78,0xe6,0x07,0xc7,0x62,0x51, - 0x6c,0x08,0x65,0xfb,0xfb,0xf7,0x34,0x16,0x05,0x22,0xfc,0x19,0x2b,0x44,0x3c,0x2e, - 0xb5,0xe8,0xa0,0xbf,0x29,0x23,0xad,0x25,0xdc,0xdc,0xdc,0x8b,0x6f,0x6e,0x62,0x12, - 0x2e,0xfc,0x9e,0xef,0x2c,0x3c,0x2b,0x2f,0x3a,0x24,0x62,0xc1,0x5a,0xf5,0x4a,0x82, - 0x61,0x90,0x6b,0xc4,0x26,0x7e,0x02,0xfc,0x4a,0x55,0x43,0x68,0x50,0xbe,0xc4,0xfd, - 0x4b,0x58,0x9d,0xcc,0x2e,0x14,0xd5,0x48,0xfa,0x92,0x31,0xef,0xef,0xc7,0xb0,0xbe, - 0xea,0xdf,0x8e,0xbd,0xc3,0x4c,0xc9,0x63,0x08,0x9f,0x0e,0x04,0xce,0xb4,0x99,0x61, - 0x2c,0x92,0xaa,0x96,0x29,0x6f,0x75,0x2b,0x41,0x7c,0xda,0x95,0xbe,0x88,0xd6,0xd4, - 0xc6,0x17,0x12,0x67,0x88,0x5e,0x35,0x3e,0xed,0xfa,0x11,0x09,0x2d,0xc1,0xa8,0x61, - 0x83,0xa5,0x0e,0x69,0x4e,0xef,0x65,0xcb,0xfd,0xfd,0x2d,0x69,0x66,0x3c,0x8e,0x79, - 0xb6,0x9b,0x3c,0x6c,0x3c,0xbe,0x67,0x26,0xfa,0xdb,0xf2,0x95,0xc3,0xce,0xc4,0x0c, - 0x0b,0xf8,0x6b,0xf2,0x62,0x84,0x38,0x36,0x7a,0x54,0xf9,0xd9,0x59,0x2a,0x46,0x8f, - 0x90,0xde,0x34,0xd6,0x5a,0xc1,0xe0,0x5f,0xff,0xfd,0xdf,0xe8,0x3f,0xef,0x35,0x12, - 0x08,0xbb,0xb2,0x28,0x52,0x95,0x21,0xd0,0x8f,0xfe,0xb3,0xfe,0x03,0x70,0xa7,0x08, - 0xcc,0xa6,0x7d,0x2a,0x1a,0x64,0xc3,0x15,0xeb,0xb3,0xbe,0xda,0x13,0x88,0x8d,0x37, - 0x15,0x55,0x1a,0x24,0xd5,0x4d,0xeb,0x7a,0xac,0xee,0x96,0x76,0x77,0xd1,0x7e,0x22, - 0x45,0xc9,0xc4,0x9b,0x33,0xe2,0xad,0x54,0x01,0xa5,0x0e,0x1a,0x68,0x68,0x6d,0x1b, - 0x82,0x29,0xa5,0xe3,0x19,0xd6,0x96,0x64,0xb4,0x04,0x54,0x25,0x49,0x3c,0x93,0xf8, - 0x76,0xa6,0xdc,0x46,0x03,0x5c,0x24,0x67,0x0b,0x2e,0x66,0x23,0xb9,0x2a,0x5e,0xbe, - 0x22,0xf5,0x27,0x41,0xb0,0x1c,0xc7,0x61,0xa1,0x63,0xd9,0x3c,0x68,0xe7,0x02,0x75, - 0x0e,0xa0,0x8c,0xd1,0xae,0x2b,0xe0,0xb9,0x63,0x52,0xec,0xa8,0xc1,0x2c,0x9a,0x8a, - 0x60,0xca,0x0b,0x72,0x6d,0xb4,0x50,0x19,0x07,0x53,0x83,0x1c,0x86,0x6e,0x7a,0xd8, - 0x34,0xd5,0x59,0x66,0x21,0x35,0x41,0xb6,0x48,0xa4,0xb8,0x5b,0x4e,0x44,0xf2,0xf7, - 0x0f,0xa4,0x87,0xeb,0x29,0xa1,0xb4,0x82,0x2f,0xae,0x07,0x85,0x48,0x91,0xe2,0xc9, - 0x98,0xb4,0x5b,0x4b,0x44,0xf3,0x94,0xba,0x32,0x48,0xdb,0x7b,0x33,0x2b,0x95,0xc0, - 0xa3,0x32,0x43,0xe9,0x11,0xd6,0xcb,0x67,0x91,0x42,0xed,0x91,0x9b,0x1b,0x3d,0x08, - 0xad,0x9d,0x36,0xb9,0x9c,0xf4,0x60,0x98,0xbb,0xde,0xac,0x33,0x16,0x0c,0x6e,0x6e, - 0x58,0xd2,0xb7,0xb4,0xae,0x69,0x78,0xb5,0x9c,0x35,0x43,0x42,0xa4,0xd0,0xe4,0x21, - 0xcd,0x90,0xbc,0x1e,0xc2,0x4a,0xef,0xaa,0x93,0xbf,0x79,0xa3,0xc3,0xe8,0xab,0x6e, - 0xb3,0x27,0x6e,0x02,0x57,0x6d,0x80,0xa6,0x37,0xfe,0x48,0xf3,0x5e,0xe4,0xa8,0x0e, - 0x78,0xb9,0xbd,0x37,0x2b,0x03,0x81,0xd8,0x21,0x34,0xf8,0x5b,0x92,0xc0,0x9a,0x1e, - 0xd2,0x70,0xd0,0x0f,0x88,0x3a,0x68,0xcd,0x2a,0xb0,0xe6,0x66,0x2d,0xaf,0xdd,0x66, - 0x41,0x76,0x65,0xa3,0x36,0xd9,0x45,0xcf,0xa3,0x36,0xa0,0xb3,0xd3,0x59,0x36,0x55, - 0x63,0xad,0x76,0x08,0xc9,0x13,0xd1,0x67,0xbd,0x2e,0xc7,0x50,0x63,0x60,0xa1,0x44, - 0x56,0xd4,0x11,0x44,0x7f,0x53,0xca,0xc2,0xde,0x44,0xa8,0x01,0xda,0x2c,0xf5,0xec, - 0xdb,0xd3,0xd7,0x2f,0x9f,0xfc,0xb3,0xf4,0xe4,0xd6,0x53,0x01,0xc1,0xc0,0x3d,0x93, - 0xb3,0xa1,0xb5,0x69,0xaf,0x6b,0x38,0x2a,0xfb,0xe4,0x17,0x86,0x6d,0xa7,0x70,0x1e, - 0x98,0x88,0x4e,0xe6,0xfd,0xa1,0xcd,0xb5,0xe8,0xc0,0x88,0x05,0x2e,0xd4,0x13,0xab, - 0x0c,0xe6,0xe6,0xfb,0x22,0x21,0x10,0x3a,0xba,0x75,0xb4,0x35,0x4d,0xf3,0x12,0x39, - 0x7b,0x65,0x9a,0xa3,0xd0,0x0c,0x72,0xce,0x66,0x41,0x64,0x82,0x5d,0x48,0xf2,0x0e, - 0xbc,0x75,0xd6,0xd3,0x60,0x58,0x5a,0x73,0x59,0xb0,0x7d,0x11,0xb4,0x88,0x1b,0x21, - 0x16,0x90,0xe6,0xa4,0x2a,0x7e,0xa8,0xb4,0x21,0xce,0x1a,0x95,0xb4,0xb1,0x6e,0x6c, - 0xa1,0x3c,0x0a,0x1b,0xb1,0x26,0xf8,0xbc,0xc5,0x9a,0xab,0x3e,0xac,0x08,0x0f,0x35, - 0x99,0xe4,0x16,0xaa,0x82,0x40,0x4a,0x01,0x77,0x91,0xaf,0xcf,0x16,0x62,0xa6,0x2b, - 0x88,0x83,0x23,0xc8,0x59,0x6c,0x7e,0xba,0xad,0x85,0x38,0x93,0xfe,0xd8,0x7b,0xfd, - 0xa2,0x07,0xa9,0x7f,0xd8,0x80,0x0f,0x54,0x90,0x10,0x66,0xa6,0x66,0x44,0xef,0xc0, - 0xc6,0x0d,0xe5,0x16,0x59,0x65,0xa6,0x57,0x49,0xa4,0x95,0xdc,0x1a,0x49,0x0c,0xdb, - 0x45,0x1f,0x3f,0x68,0xab,0x88,0xac,0x14,0xdb,0x36,0x8d,0xc1,0x15,0x36,0x4e,0x91, - 0x49,0xa7,0x04,0x27,0xf1,0x85,0x25,0x82,0x29,0x44,0x78,0x66,0x9f,0xbe,0xf1,0x3d, - 0x39,0x14,0x54,0x1b,0x4f,0xe1,0x2a,0xe3,0x9f,0xe3,0xba,0x57,0x12,0x4e,0xee,0xd5, - 0xb6,0xd6,0x4d,0xc3,0x70,0xed,0x18,0x70,0x88,0x5f,0x62,0x0f,0x3a,0xd0,0xe6,0x99, - 0xb5,0xc9,0xee,0xd9,0xd0,0xa5,0xad,0xf0,0x86,0x3c,0x63,0x57,0xa3,0x8e,0x6b,0x70, - 0x1a,0xe7,0x76,0xb4,0xeb,0x53,0x44,0xe3,0x66,0x77,0x22,0x70,0xd6,0xaa,0x0a,0x6f, - 0x42,0x99,0x8c,0xa9,0x76,0x98,0xca,0x2c,0x57,0x9e,0x4c,0x14,0x89,0x49,0xdb,0x54, - 0x5f,0x6f,0x47,0x49,0x51,0xe8,0x72,0x50,0x0e,0x1b,0xeb,0x43,0x78,0x1f,0xbf,0x3e, - 0x3d,0x0d,0x86,0x9c,0x2c,0x14,0x13,0x8e,0x99,0xdc,0x71,0x6b,0xf0,0x8d,0xbc,0x57, - 0xda,0xfd,0x8e,0xc0,0x11,0xd8,0x69,0x93,0xb3,0x75,0xbe,0x76,0x0d,0xfb,0x56,0x6b, - 0x5c,0xaf,0xea,0xb8,0x82,0x4f,0xeb,0x8a,0xf0,0x00,0x6d,0xa9,0x8b,0xde,0xf2,0x62, - 0x5b,0x63,0x5c,0xaf,0x4e,0x7c,0x82,0x9c,0xe6,0x8e,0x80,0x97,0xa8,0x09,0x54,0xf8, - 0x7a,0x52,0x55,0x45,0x42,0x4d,0x11,0xa5,0x8d,0x8b,0x24,0xee,0x89,0x55,0x23,0xc4, - 0xdb,0x2c,0x1c,0xae,0x61,0xfb,0xe7,0x5f,0xf0,0xe9,0x9a,0xde,0xda,0xe3,0x20,0xb4, - 0x62,0x9f,0x4c,0xd0,0x19,0xc9,0x49,0x2b,0xda,0x81,0x09,0x0d,0x51,0x83,0xac,0x7d, - 0x70,0x43,0x7a,0x02,0xeb,0x68,0xf3,0xae,0x15,0xb8,0xee,0xd1,0xf7,0x84,0x41,0xf4, - 0xb7,0x19,0x36,0x54,0x8b,0x98,0x8e,0x89,0xd0,0x71,0xf8,0xf1,0xb4,0x9d,0xd0,0x67, - 0x43,0x7c,0xba,0xed,0xf3,0x03,0xd0,0x73,0x9a,0x48,0xab,0xa7,0x36,0xd1,0xc5,0x7a, - 0xdf,0x34,0x07,0x9e,0x34,0x50,0x4b,0x90,0xc6,0xac,0x48,0xf1,0x31,0xde,0xf3,0x0e, - 0x30,0xf7,0x00,0xf0,0x7b,0x6d,0x8c,0x26,0x19,0x15,0x5f,0x25,0x24,0xf8,0x27,0x8f, - 0x0b,0x13,0xeb,0x91,0x1c,0x1c,0xe8,0x29,0x2b,0xde,0x25,0xef,0x77,0x64,0x5a,0x5a, - 0xbe,0xa0,0xf6,0xaf,0x24,0x26,0x62,0xa6,0xed,0x6d,0xee,0x98,0x3f,0x78,0x37,0x78, - 0xdf,0xfc,0xc6,0xf4,0xd6,0x1b,0x58,0xa7,0xb9,0x1d,0x93,0x6b,0xa5,0x11,0x63,0x33, - 0x78,0xd8,0x73,0xe4,0x1f,0x83,0xa1,0xa9,0x8c,0x30,0xd1,0x07,0x2a,0xb3,0x2e,0xe9, - 0x87,0xae,0x51,0xc1,0x50,0x9b,0x34,0x19,0x35,0xa4,0x61,0x66,0x82,0x78,0x6a,0x38, - 0xf6,0xad,0x8d,0xd2,0x8c,0x36,0x9a,0xdc,0x95,0xac,0x0d,0xfb,0x97,0xe6,0x1a,0xcb, - 0xfe,0xd7,0x8b,0x14,0xba,0xae,0x86,0x01,0xf3,0x13,0xc0,0xe2,0x75,0x07,0x5a,0x75, - 0x51,0x2b,0xbc,0xf7,0xcc,0x5c,0xdc,0xdc,0xd8,0x59,0x19,0xd3,0xbc,0x34,0xb0,0x55, - 0x5d,0xf0,0x4e,0xa0,0x76,0x9f,0x49,0x61,0x8a,0x6e,0xd0,0xa2,0x4a,0x88,0x04,0x61, - 0x21,0xbb,0x03,0x5c,0x9c,0xff,0xd4,0x98,0xc6,0xd6,0x19,0xdc,0x1e,0xed,0x2d,0xed, - 0xe5,0xab,0xb6,0x31,0xde,0x0d,0xf6,0xdd,0xae,0x46,0xbf,0x69,0x86,0xda,0xf6,0xb3, - 0x79,0x3b,0xb4,0xe6,0xa6,0x96,0x2d,0x6b,0x69,0xb2,0x49,0x28,0x01,0x8f,0x47,0x4e, - 0x18,0x74,0x8d,0x50,0x47,0x7a,0xd1,0x0f,0x71,0x2e,0x85,0xc6,0xff,0x1b,0x30,0x0d, - 0x26,0xd6,0x85,0x8c,0xd9,0x6e,0x50,0x0e,0xd9,0xa7,0x23,0x1e,0x48,0x09,0x3f,0x65, - 0xdf,0x2d,0xb1,0xbd,0xa5,0xf8,0xd9,0x30,0xd5,0x6c,0x84,0x48,0x66,0x3d,0xee,0x02, - 0x36,0x1a,0x6d,0xae,0x89,0xf6,0x0c,0xa7,0xba,0x3d,0x3a,0x8c,0x4d,0x20,0x9f,0x0a, - 0xac,0xdc,0xa2,0x1e,0xed,0x08,0xcd,0x2d,0x49,0x5c,0x25,0x82,0xf3,0x76,0x03,0x2b, - 0xe5,0x05,0xeb,0xd0,0x10,0x07,0x89,0xe6,0x9e,0x9a,0xf9,0xff,0xfa,0x5f,0xff,0x9b, - 0xd1,0xe2,0xe4,0x46,0xb7,0x54,0xec,0xa4,0xa1,0x61,0x4d,0x68,0x0d,0x03,0x13,0x05, - 0x41,0x44,0x74,0xec,0x7f,0xe6,0xb9,0x45,0x00,0x8b,0x75,0x96,0x71,0x58,0xa3,0xb4, - 0x44,0x1c,0xd3,0x18,0xb7,0x32,0x76,0x8b,0x5a,0x89,0xe9,0xa7,0xcc,0x3f,0x10,0x58, - 0x7e,0x26,0x41,0xa5,0xeb,0xd3,0x75,0x70,0x80,0xbf,0x96,0xf9,0x96,0x32,0xa7,0xdc, - 0x52,0xab,0x8a,0x45,0x0b,0xc4,0x51,0x21,0x7a,0x7e,0x43,0xb3,0x60,0xad,0x95,0x25, - 0xe1,0x12,0x76,0xb8,0xd0,0x87,0x45,0x31,0xc6,0x7b,0x7f,0x7c,0xf5,0xf2,0x9b,0xaa, - 0x5a,0xbd,0x91,0xa8,0x1b,0x83,0xbd,0xf4,0x34,0xca,0x49,0x46,0xe9,0xfa,0xaf,0x7f, - 0x78,0x4b,0x1d,0x37,0x5d,0x6b,0xce,0x4b,0x34,0xb9,0xfa,0xd3,0x6f,0x38,0xd1,0xa9, - 0xeb,0x5b,0x09,0x8d,0x3e,0x73,0x44,0xb4,0x3b,0x7c,0xf4,0x92,0xe4,0x5e,0xfa,0x68, - 0xe0,0xbe,0x9a,0x73,0x98,0xd4,0x0e,0x1f,0x95,0x31,0x68,0x33,0x0a,0x8a,0x8f,0xcc, - 0xc6,0xff,0x78,0xfa,0xfd,0x77,0x11,0xeb,0x29,0x5d,0x7c,0x58,0xa8,0x72,0x95,0xd3, - 0x7e,0x79,0x4b,0x6b,0x14,0x6c,0x44,0xc5,0xa0,0xf1,0x6f,0x6a,0xd5,0x70,0xb6,0xbf, - 0xaf,0x95,0xc3,0x36,0xbf,0x59,0xed,0x19,0xa8,0xe5,0x20,0x96,0x7b,0x7c,0x57,0x97, - 0x73,0xea,0x6a,0x02,0xce,0xf0,0xab,0x7e,0x3f,0x30,0x5d,0xc0,0xa4,0xcf,0x5f,0x4b, - 0x4f,0xac,0x7c,0x06,0xd0,0x3e,0xa1,0x8d,0xb4,0xe9,0x9e,0xfa,0xbb,0x19,0x47,0x9f, - 0x68,0xed,0xc8,0x9d,0x09,0x6e,0x61,0xfc,0x9b,0xf5,0xa4,0x91,0x34,0xb8,0x71,0xe7, - 0x3f,0x13,0x24,0x79,0x9a,0xe6,0x93,0xee,0x3b,0xa0,0xf1,0xfb,0x40,0x13,0x8c,0xf0, - 0xfa,0x6c,0x1d,0x17,0xb3,0x21,0xc4,0xc1,0x4d,0x50,0x87,0x4d,0x6a,0x1d,0x0b,0x46, - 0x67,0x4d,0x36,0xfe,0x93,0x8d,0x34,0x1f,0xd3,0xca,0xdc,0xba,0x25,0x26,0xae,0x4c, - 0x92,0x24,0x6d,0x90,0xac,0x2e,0x21,0x82,0xbc,0x7d,0x68,0x48,0x97,0x71,0x2a,0x26, - 0xc1,0x15,0x72,0x92,0x69,0x5b,0xb2,0x72,0xf7,0xfc,0xe5,0xf3,0xb7,0xcf,0x4d,0x14, - 0xdb,0x04,0xe2,0xe5,0x82,0x73,0xe6,0xaa,0x4b,0xd8,0x5f,0xe0,0xfa,0x16,0xf2,0x27, - 0x55,0x3b,0x21,0xd2,0xb2,0x7a,0x44,0xca,0x97,0x37,0xe5,0xd0,0x8c,0x25,0xa1,0xb7, - 0x31,0x01,0x69,0x8a,0x42,0x88,0x89,0x90,0xb3,0x17,0x4f,0xde,0xbe,0x38,0xd5,0x41, - 0x0e,0x56,0x87,0x30,0xc4,0x43,0x27,0xf1,0xea,0x98,0x17,0x1b,0x2a,0x1d,0xa3,0x29, - 0x93,0x02,0x4a,0x3d,0xcd,0x93,0x79,0x45,0x0a,0x17,0x97,0x15,0x59,0x26,0x19,0x09, - 0xa9,0x1c,0x21,0x1f,0x1b,0x0b,0x8d,0x31,0xa2,0x17,0x8a,0x98,0xc5,0xa5,0x6b,0x9d, - 0xe2,0x70,0x1c,0xc4,0xbc,0xc5,0xe7,0xaa,0xb4,0x2f,0x86,0x3a,0x25,0xa5,0xb4,0x2a, - 0x1e,0xa7,0x75,0x61,0x5c,0xab,0x18,0x25,0x99,0x04,0x5c,0xc0,0x5f,0x72,0xc4,0x05, - 0xe2,0xe0,0x98,0xbb,0x88,0x36,0xe3,0x75,0x3e,0xef,0xb8,0xb9,0xa6,0x4d,0x2b,0xbe, - 0x59,0x02,0x3d,0x80,0xa1,0x3b,0x29,0x3c,0x44,0x36,0x96,0x41,0x7d,0xe4,0x28,0x02, - 0xa4,0xb9,0x69,0x18,0x38,0x13,0x60,0x5a,0xad,0xd9,0x92,0xc5,0xba,0x55,0x79,0x6e, - 0x92,0x1f,0x38,0x43,0x80,0xb7,0xb7,0xaa,0x03,0x78,0xb5,0x1a,0x8c,0x79,0x55,0x08, - 0x96,0xfd,0x59,0xc2,0xb7,0x91,0x39,0x80,0x8a,0x0c,0x62,0x85,0x93,0x48,0x89,0x42, - 0x74,0x5f,0x89,0x5c,0x80,0x17,0x07,0xb2,0xa8,0x13,0x6d,0xe9,0xaa,0x74,0x8c,0xf6, - 0x6e,0x24,0xaf,0xc7,0xda,0x00,0xd3,0x24,0x6d,0x91,0x87,0xd5,0xc1,0x3b,0xcb,0x51, - 0x8d,0xc4,0x44,0x3a,0x30,0xcf,0x0b,0xeb,0x30,0x4e,0x25,0xfb,0x15,0xd1,0x2b,0x3a, - 0x30,0x52,0xd6,0x51,0x03,0x2f,0x6d,0xd5,0x0b,0x63,0xe2,0x1f,0xbd,0x39,0x6d,0x58, - 0x13,0x38,0xa9,0xcb,0xd2,0x98,0x50,0x47,0xe2,0x5c,0x88,0x60,0x9c,0xc3,0x26,0xce, - 0x81,0xf6,0x1c,0x1b,0xc9,0x8a,0xbc,0xb4,0x66,0x62,0x23,0x4d,0x14,0xa5,0xdc,0x28, - 0x38,0x00,0x92,0x03,0xb2,0xce,0x81,0x0d,0x5c,0x41,0x25,0xe1,0x5a,0x3a,0x43,0x27, - 0x09,0x43,0xc7,0xce,0x68,0xc8,0xa4,0xa2,0x49,0x7c,0x19,0x5f,0x45,0xc6,0x0c,0x9f, - 0xa6,0x63,0xe1,0x64,0x22,0x4e,0x23,0xde,0x75,0x7f,0x9f,0xef,0xbc,0xeb,0xbf,0x87, - 0x0d,0xfe,0x73,0xff,0x6e,0x19,0x13,0xfe,0x47,0x02,0x88,0x11,0xcf,0x9b,0xa6,0x37, - 0x37,0xd2,0x93,0xf1,0x1b,0x6c,0xe5,0x4e,0x1c,0x8f,0xfb,0x0d,0x0e,0x41,0xcb,0x31, - 0x86,0xb3,0x98,0xcd,0x21,0x3b,0x21,0x08,0xf4,0x34,0xa0,0xff,0x6f,0x0b,0x99,0x3c, - 0x42,0xf3,0xe2,0x46,0x8b,0x51,0x3a,0x8c,0xb0,0x2b,0x8e,0x45,0x4c,0x81,0xef,0x1d, - 0xc8,0xfa,0x13,0x1a,0x18,0xfa,0xbd,0x9d,0x14,0xe0,0x8d,0x3d,0xc7,0xc7,0xc4,0xcc, - 0xb9,0xd5,0x2e,0xaa,0xbd,0x95,0xe1,0x35,0xf2,0x32,0xf2,0xd9,0xd0,0x7f,0xfd,0xfd, - 0x29,0xf1,0x5d,0xa4,0x2a,0x0f,0xb7,0x85,0x84,0xbf,0x95,0x75,0xb4,0x8e,0x74,0x1c, - 0xd9,0x3b,0x5b,0x29,0x0d,0x4e,0xa4,0x8b,0x6b,0x4d,0xb5,0xbc,0x91,0xd0,0xe1,0x5b, - 0x8e,0xe2,0xb9,0x42,0xfa,0xc8,0x9a,0x88,0xc0,0x92,0x93,0x4e,0xed,0xc6,0x97,0xb0, - 0x45,0xb5,0x5c,0x55,0x89,0x89,0x35,0x6c,0x94,0x19,0x8e,0x1c,0x47,0xeb,0xad,0x52, - 0x5f,0xa4,0x17,0x64,0x17,0x37,0x30,0xe8,0x16,0x31,0x5b,0xbf,0xbf,0xd9,0xe2,0xe2, - 0xb5,0x01,0x58,0x33,0xb0,0xdb,0x0d,0xc0,0xdb,0x5e,0x5e,0x89,0xf4,0x9e,0xe9,0x1e, - 0x66,0x6e,0x78,0xb7,0xbd,0xb9,0x15,0xe6,0xad,0xef,0xba,0x31,0x14,0xb3,0x08,0x41, - 0xdd,0x50,0xb9,0x4d,0x90,0xb7,0xbd,0xa3,0xe3,0xbc,0xe7,0x08,0x18,0xf2,0x5d,0x7b, - 0x72,0x15,0xda,0x77,0x4e,0x18,0xce,0xe1,0xae,0x29,0xd9,0xd0,0x1e,0xe1,0x2c,0x5c, - 0x15,0x6b,0x4e,0xaf,0x8a,0x37,0xcb,0x99,0x6f,0x16,0x63,0x35,0x5f,0x7c,0xc3,0x5c, - 0x27,0x30,0x65,0xba,0xea,0x96,0x0c,0xc1,0xe6,0x40,0x6b,0xa9,0x33,0x60,0x02,0xd4, - 0x1c,0x8f,0xad,0x90,0x30,0x03,0x67,0x8b,0x14,0x64,0xac,0xbb,0x82,0x83,0xed,0xee, - 0xe9,0x36,0x1c,0xfc,0x7f,0x8a,0x07,0xb7,0x20,0xf8,0x1d,0xe5,0x28,0x3d,0xa2,0xa6, - 0x84,0xc4,0x0a,0x96,0x69,0xf7,0x53,0x0a,0xce,0x56,0x9e,0x8b,0xf6,0x5a,0xde,0x73, - 0x89,0xe7,0x56,0xa2,0xc7,0x47,0x72,0x9f,0x68,0xad,0x6c,0xe5,0x36,0x16,0x40,0x6c, - 0x46,0x97,0xf1,0x56,0x21,0xd5,0x96,0x68,0xa3,0x51,0xd7,0xa4,0xbc,0x9b,0x95,0x80, - 0x42,0xe3,0x8d,0x92,0xb6,0x0c,0x27,0x8e,0x91,0x41,0xea,0xe9,0xe0,0x7d,0x78,0x80, - 0x25,0x14,0xbe,0x42,0xfd,0x1f,0x61,0x54,0x5c,0xa9,0x5c,0x7b,0x41,0xb7,0xe8,0x7e, - 0x23,0x35,0xe9,0xc8,0xb1,0x03,0xe8,0x32,0x5c,0x5d,0x93,0x52,0x05,0x97,0x99,0x7f, - 0xd0,0x4c,0x30,0x2a,0x4f,0xb4,0x29,0x5b,0xc2,0x8b,0x23,0x3f,0x68,0x28,0xc1,0x2e, - 0xfb,0x7d,0x47,0x2c,0xe5,0x3d,0x2d,0x0e,0xcb,0x5d,0x7a,0x2b,0xea,0x05,0xaa,0x3f, - 0xd9,0xec,0xb5,0xf7,0x6d,0xf4,0x2e,0x81,0xda,0xdd,0xcb,0xdb,0x5c,0xcc,0x6e,0x67, - 0x4b,0x47,0xe4,0x7f,0xbe,0xd7,0xeb,0x79,0x4d,0xf5,0x8d,0x76,0x33,0xed,0xfa,0x93, - 0x06,0xcc,0xbb,0x02,0xc3,0x6f,0xed,0x58,0xcb,0xdf,0xf5,0x79,0x12,0x60,0x1e,0x5a, - 0xdd,0x7c,0x41,0xc4,0x8b,0x3f,0x0c,0xa1,0xe2,0x5c,0x3b,0xa1,0x9a,0x6d,0x1c,0x06, - 0x92,0xd4,0x09,0x7b,0x73,0xfd,0x03,0x95,0xa1,0x32,0xe7,0x0f,0x6f,0xbe,0xfd,0x3a, - 0x5f,0x92,0x92,0x04,0x6b,0x29,0x73,0xcf,0x03,0x7f,0x9f,0x35,0xda,0x03,0x64,0x19, - 0xdc,0xdd,0x2b,0xe7,0x30,0x54,0x1d,0x46,0x41,0x43,0x78,0xd0,0x7f,0x60,0xe2,0x65, - 0x8d,0xaf,0xdb,0xe0,0x82,0x73,0x1a,0x00,0x94,0x10,0x7d,0xf4,0xc0,0x56,0x44,0x8a, - 0xcc,0xee,0x81,0x1f,0x31,0x34,0xb5,0xbb,0xca,0xf0,0x38,0x04,0x2f,0x5e,0x3d,0x5d, - 0xcf,0xe7,0xf0,0x66,0x6c,0xc1,0x34,0xb1,0xbc,0x10,0xed,0xff,0x90,0x64,0xd5,0x97, - 0xf0,0x0d,0x5d,0xd1,0x83,0x8d,0x13,0x9a,0xa3,0x2d,0x23,0xa0,0x87,0x5c,0x1b,0x9d, - 0x53,0x68,0xd7,0xc5,0x2a,0x2f,0x39,0xc8,0x9e,0x36,0xd2,0x37,0x6f,0xdf,0xbe,0xa6, - 0x0d,0x53,0x5c,0x88,0xef,0x86,0xbd,0xbe,0x79,0x96,0x19,0x0d,0xe2,0x75,0x5c,0x20, - 0x79,0x3f,0xe5,0x38,0x66,0x25,0x22,0xe8,0x0a,0x6e,0x8d,0xf5,0xaa,0x2e,0xee,0x85, - 0x02,0x75,0xb1,0xe4,0xc1,0xb2,0xbd,0x97,0x20,0xe2,0x22,0x0a,0x88,0xb5,0x55,0xb3, - 0x3a,0x4e,0x23,0xf2,0xbe,0x51,0x29,0x7b,0x3b,0xba,0xeb,0x8c,0x29,0x0c,0x78,0xaa, - 0x31,0xac,0xb0,0x04,0xa3,0x0b,0x15,0x2e,0x94,0xcd,0x1f,0x0a,0x40,0xbe,0x6b,0xdb, - 0x0c,0xed,0xd2,0x3c,0xda,0x33,0x19,0x85,0x7f,0x5b,0x6a,0x24,0xd2,0x0f,0x62,0xd1, - 0x01,0xcf,0x9f,0x93,0x95,0x43,0x16,0x77,0x72,0x18,0xb7,0x85,0x1e,0xc0,0xfa,0x62, - 0x8e,0x32,0x30,0xdd,0xdd,0x18,0x22,0x6b,0x93,0x60,0x69,0x5f,0xa7,0xa8,0xe9,0xe0, - 0xc0,0x45,0x9c,0x64,0x63,0x6d,0xcb,0x80,0x4e,0x9f,0xa7,0x17,0x96,0x59,0x08,0xa0, - 0xbb,0xf9,0x6b,0xd8,0x12,0x89,0x45,0x4b,0x69,0x82,0xff,0xb6,0xf7,0x6c,0x95,0x7c, - 0x67,0x6c,0x3a,0x1f,0xa5,0x9b,0x1c,0x0c,0x68,0x57,0x1c,0x36,0x29,0x46,0x4b,0x74, - 0xb0,0x83,0x9a,0xed,0x5b,0xd4,0x27,0x6d,0xc6,0xdf,0x1e,0x3a,0xdd,0x73,0x60,0x90, - 0xd9,0xc7,0xbd,0xed,0xbd,0xa1,0x77,0x02,0x14,0x22,0xad,0x45,0x68,0xf6,0xe4,0xf4, - 0x2e,0x39,0x07,0xe5,0xbb,0xfa,0x5d,0xff,0xfd,0x98,0xfe,0xba,0x6f,0x7c,0x1c,0x42, - 0x48,0x8d,0x6e,0x8b,0x3b,0xd1,0x5e,0x3f,0x97,0xdb,0xd0,0xd2,0x9d,0x46,0xaf,0xdc, - 0xc4,0xfb,0xf1,0xcf,0xa5,0xdb,0xca,0xae,0xc7,0xd7,0x25,0xd4,0xde,0x47,0x57,0xc6, - 0x39,0x12,0x68,0xce,0xe8,0x73,0x73,0x83,0x58,0x50,0xe8,0xbe,0x7c,0x89,0xa2,0x27, - 0xa7,0x57,0xd9,0xf4,0x1e,0x29,0x19,0xe6,0x6b,0x3f,0xb0,0x9d,0xef,0x10,0x19,0x2c, - 0xad,0x51,0xa0,0xb5,0x54,0xe5,0xe9,0x26,0xfc,0x86,0x0f,0x9d,0x6e,0xae,0x20,0x1f, - 0x34,0x7a,0xe9,0xf2,0x60,0xc3,0xeb,0x94,0xf8,0x4e,0x3a,0x7c,0xd4,0xf4,0xba,0x4f, - 0xd2,0x7c,0x32,0xae,0x8d,0x2a,0xd2,0xc0,0xfb,0xf0,0x1a,0xf0,0x0e,0x7d,0xc4,0x7d, - 0xc1,0x35,0x49,0xf0,0x1d,0x62,0xef,0x34,0xbf,0x8d,0x6f,0x75,0x62,0xc5,0x35,0x58, - 0x71,0xc4,0x95,0x21,0x7e,0x78,0xf3,0x52,0xbf,0x24,0x27,0x15,0xd1,0x75,0x17,0x7d, - 0x3b,0xef,0x19,0xea,0x30,0xf6,0x57,0xb5,0x65,0xb2,0x07,0xf4,0xc6,0x48,0xfc,0xda, - 0xd0,0xa4,0xfb,0x84,0x9a,0xd1,0x88,0x60,0x8c,0x9d,0xc6,0x98,0x86,0x74,0x9d,0x1b, - 0x5a,0xba,0xaa,0xa3,0x1e,0x5a,0xcf,0x96,0x01,0x98,0x44,0xee,0xf2,0x73,0x07,0x4c, - 0x19,0x41,0xb0,0x09,0x8f,0xea,0x44,0xb0,0xdb,0xf4,0x90,0x06,0x39,0x69,0xe8,0x21, - 0xdb,0xd1,0x8f,0x76,0xff,0x9a,0x61,0xab,0xd9,0x5f,0xc0,0xe2,0x77,0x22,0x34,0x36, - 0xb7,0x84,0x56,0x1a,0x23,0xea,0x36,0xd8,0x1f,0x03,0x7a,0x17,0x64,0xb6,0x0b,0xaa, - 0xfd,0x7d,0x15,0x91,0x2c,0x55,0x92,0x6a,0x0e,0xcb,0x20,0xb0,0xf3,0x16,0xc3,0xe0, - 0x66,0x47,0x14,0x40,0xe8,0x2a,0x6f,0x5c,0x09,0x5a,0xad,0x32,0x9b,0x44,0xbc,0x2d, - 0xef,0x74,0x4c,0x9c,0x4e,0xa7,0x29,0x9e,0x18,0xf3,0x31,0x7d,0x1b,0x7e,0x4c,0x3b, - 0xfd,0x94,0xd8,0x50,0x6b,0xad,0x62,0x30,0xfb,0x0d,0xf1,0x3b,0xb7,0x2a,0xa7,0xa2, - 0xaf,0xe5,0xe7,0x27,0x5a,0xe1,0xb6,0x6b,0x1a,0x0c,0x6f,0xd5,0xe3,0x82,0x90,0xbf, - 0x68,0x4b,0x88,0x75,0xb4,0x24,0x6b,0x86,0xe3,0xd3,0x92,0xa4,0xf0,0x38,0xeb,0x3c, - 0xf9,0x5c,0x2c,0x56,0xcc,0xf4,0x39,0xe2,0xe0,0x5f,0x07,0x0f,0xfb,0x4b,0x7a,0x1a, - 0x73,0x70,0x54,0xbb,0xae,0xf3,0x85,0xa3,0xeb,0xdc,0x12,0xc9,0x72,0xbb,0xb6,0xd9, - 0x66,0x6e,0x75,0xad,0xad,0x8d,0x7a,0xeb,0xde,0xff,0x8f,0x96,0x56,0xa9,0xd4,0xe6, - 0x5d,0xcf,0x87,0xe0,0x24,0x37,0x1c,0x5f,0xc1,0x0a,0x02,0x6a,0xc1,0x0f,0xfd,0x33, - 0x55,0xf9,0x37,0x3e,0x44,0x13,0xfa,0x67,0xbd,0xa2,0x3f,0x52,0x0b,0xe6,0x06,0x8b, - 0x74,0xe3,0x63,0x4a,0x48,0x72,0x5e,0x4d,0xab,0xd0,0xa3,0x59,0x91,0xfa,0x34,0xdc, - 0x24,0xfb,0x82,0xd0,0xc1,0x1f,0xa4,0x86,0x8a,0xc8,0x63,0xb6,0x90,0x0e,0x17,0xa7, - 0xa0,0xb5,0x12,0xe1,0x6d,0x8a,0x48,0x36,0xc8,0xa8,0xa1,0x64,0xdf,0x12,0xf7,0x2b, - 0x25,0xb1,0x8a,0x53,0xe7,0xa0,0xd2,0x4c,0xd8,0x20,0x58,0x70,0xbe,0xd8,0xcc,0x58, - 0x74,0x51,0x89,0x31,0xcf,0x9a,0xd5,0xf1,0xd2,0x44,0x45,0xde,0x1f,0xb4,0xea,0x23, - 0x71,0xb1,0xb0,0xac,0x11,0xbd,0x5d,0x8b,0x29,0x8c,0xeb,0x6c,0x88,0xfd,0x54,0xca, - 0xc1,0x70,0xe0,0x8e,0x58,0x33,0x5f,0xff,0xf0,0x16,0xc5,0x50,0x04,0x36,0x92,0x80, - 0xd9,0xe8,0x16,0x7a,0xb5,0x6b,0x42,0xd2,0xdd,0xac,0xe6,0x25,0x0b,0x2b,0x90,0xa1, - 0x22,0x76,0xe9,0x75,0xda,0xd4,0xd0,0x8e,0x51,0xbc,0x2f,0x73,0xc9,0x68,0xe3,0x12, - 0xd3,0x3a,0xa1,0x2a,0xf2,0x9e,0x94,0x25,0x72,0xee,0xae,0x24,0xda,0x50,0x4a,0xd6, - 0xc4,0x3a,0x90,0xd7,0xab,0x23,0x9a,0x25,0x99,0x92,0xa3,0x7f,0x2f,0x21,0x4d,0xf1, - 0x64,0xdb,0xa8,0x5c,0x91,0xd8,0x4c,0x52,0x2a,0x57,0xca,0x45,0x9a,0x41,0x69,0x12, - 0xf6,0x42,0xed,0xc7,0x92,0xb0,0x9d,0x9c,0x83,0x94,0x22,0x7e,0x57,0xf2,0xf9,0x6c, - 0x1c,0xc7,0xd0,0xe6,0x77,0xb3,0x39,0x97,0xa5,0x7a,0x2e,0x30,0xc9,0xee,0x58,0x99, - 0x7a,0x1b,0xf3,0x2d,0x40,0x10,0xdf,0x79,0x8a,0x46,0xb6,0x53,0xbd,0x00,0x05,0x3f, - 0x30,0xb1,0x30,0x1c,0x6f,0xba,0xbf,0x7f,0xcf,0x7c,0x10,0xdc,0xc1,0xe3,0x65,0x1b, - 0xc7,0xe5,0xae,0xf6,0xae,0xfd,0x34,0xa0,0x9b,0x68,0xda,0xb6,0x6c,0xbf,0xeb,0x72, - 0x50,0x4c,0x0d,0x9f,0x49,0x82,0x94,0xd2,0x6f,0xe6,0xc4,0x03,0x96,0xcc,0x21,0x34, - 0x5a,0xf3,0xa0,0xe8,0x0b,0xd6,0x0d,0x01,0xfb,0xaa,0x98,0xcf,0x65,0xa1,0xd9,0x68, - 0x0f,0xf9,0x43,0x22,0x3b,0xe1,0x1e,0x64,0xec,0x4d,0xf0,0x2a,0x57,0x4b,0x81,0xa7, - 0x21,0x2f,0x0a,0x89,0x0a,0xa3,0xd6,0x18,0x57,0x64,0xc2,0x4e,0xdf,0x3e,0x79,0xfb, - 0xfc,0x4f,0x2f,0x9f,0x3c,0x7d,0xfe,0x72,0x7c,0x8d,0xb3,0x0c,0x7d,0xa6,0xdb,0xd4, - 0x1a,0xfb,0x09,0xb1,0xdf,0x86,0xbe,0x8e,0x5e,0x42,0xac,0xca,0xd0,0xb7,0xf9,0x93, - 0x7e,0xb8,0xa5,0xcb,0x42,0x04,0x51,0xc5,0xd0,0xe7,0x98,0xd0,0x2b,0xd3,0x04,0x6f, - 0xd3,0xa1,0x6f,0x52,0x01,0xf8,0x5e,0x7e,0x3e,0xf4,0x7f,0xfd,0x9f,0xff,0x5d,0x92, - 0x4b,0x43,0x60,0x32,0xae,0xff,0x87,0x21,0x6c,0x0d,0xee,0x54,0x28,0x1c,0xf6,0xf0, - 0x4f,0x22,0xc9,0xfd,0xf2,0x3c,0xdd,0x8a,0x52,0xe2,0x3d,0xae,0xb9,0xf0,0x89,0xcf, - 0x79,0x87,0x6e,0x31,0x96,0x5f,0x5a,0xf2,0xdb,0xe4,0x93,0x1d,0x69,0x3f,0xa9,0x1c, - 0x05,0xe2,0x4e,0xd9,0x67,0xc6,0x2e,0x7c,0xdb,0x9b,0xb3,0xe4,0x02,0x39,0x98,0x79, - 0x23,0x6d,0x09,0x87,0x38,0xf8,0xa3,0xbd,0xbf,0x34,0x71,0xec,0x97,0x6c,0x27,0x71, - 0x2c,0xa9,0x9c,0xcc,0x31,0x49,0x66,0xb8,0x53,0xb6,0x57,0x59,0x35,0x1a,0x2e,0x59, - 0x4d,0xa9,0x22,0x3e,0x56,0x11,0x8b,0xe8,0x9f,0x80,0xd0,0x0e,0xf9,0x56,0xfd,0xc9, - 0x56,0xcf,0xf2,0x36,0xbd,0x47,0xfc,0xb5,0x4e,0xad,0x95,0x96,0x88,0x1a,0xdf,0xdc, - 0xf4,0x49,0xed,0xf9,0x9d,0x1f,0x0c,0x1d,0x4c,0x7b,0xc7,0xdf,0xe9,0x92,0x1c,0x98, - 0x9e,0xed,0x94,0xa1,0xed,0x7b,0xe8,0x7f,0x2b,0x7f,0x06,0x16,0x77,0x1b,0xfa,0xe0, - 0xc2,0xd1,0xb0,0xda,0x4f,0x3e,0xb1,0x36,0x8d,0x68,0x2e,0x1c,0xf9,0xd0,0x48,0x14, - 0x9c,0xdf,0x9e,0x50,0x46,0xdf,0xce,0x1b,0x39,0x53,0x5b,0xe3,0xb5,0x02,0x68,0x03, - 0xe8,0xf9,0xce,0x30,0x26,0x8e,0x73,0xa0,0x39,0x14,0xe6,0x68,0xfb,0xfb,0x74,0x4d, - 0xfc,0xac,0x31,0x28,0xf5,0x89,0x41,0xa9,0xc6,0xa0,0x20,0x23,0x8c,0xd4,0xf6,0xb2, - 0xd1,0xdd,0x51,0x7b,0x12,0x97,0x6a,0x40,0xf4,0x4b,0x7b,0x46,0x92,0x36,0x7a,0xd4, - 0xd6,0x24,0x3e,0xab,0x41,0x53,0x39,0xce,0xd1,0xb0,0x04,0xb7,0xf6,0x82,0x73,0xd2, - 0xc0,0xf9,0xb8,0xcf,0x1b,0x5e,0x67,0x67,0xdc,0xbe,0x15,0xdd,0xb9,0x20,0x16,0x1f, - 0xe4,0xe7,0x07,0x07,0x23,0x43,0x61,0x9b,0xb3,0x14,0xe0,0xef,0xc1,0xc1,0xc6,0x18, - 0x1e,0xb8,0xfd,0x31,0x1c,0x38,0x90,0x9a,0xe8,0x43,0x9f,0x99,0x0d,0x21,0x25,0x75, - 0xdf,0xb4,0xad,0x27,0xa6,0x6c,0xa2,0x38,0xe3,0xd9,0xe5,0xce,0x19,0x74,0xf6,0x4b, - 0xfb,0x06,0xac,0xec,0xdc,0x91,0xdf,0x14,0xc0,0xf8,0x1b,0x53,0xa4,0xae,0x95,0x3a, - 0xa1,0x48,0x56,0x73,0x70,0x5a,0x7c,0x6d,0x59,0xec,0xf9,0x26,0x70,0xc3,0x2e,0x6f, - 0x13,0x13,0x77,0x2c,0x4e,0xb7,0xb9,0x96,0xc5,0xef,0x29,0xb1,0xfd,0x44,0xfa,0xff, - 0xa0,0x26,0xa7,0x62,0x85,0x22,0x7d,0x14,0x8e,0x41,0x3e,0x4a,0x87,0xe5,0x06,0x92, - 0x40,0x66,0x3a,0x20,0x38,0xef,0x71,0x1d,0x05,0x29,0x1c,0x0a,0xe7,0x1e,0x9c,0x48, - 0x24,0x01,0xfc,0xeb,0x00,0xc2,0xd0,0xe0,0xc8,0x9b,0x25,0x4a,0xe7,0x0f,0x70,0x01, - 0x95,0x85,0x8a,0xd3,0x6a,0x71,0x15,0x70,0x35,0x93,0x17,0xba,0x26,0xbb,0x4e,0x7c, - 0xc1,0xc1,0x16,0xba,0xd0,0x44,0x85,0xf2,0xd5,0x52,0x63,0xfa,0x52,0x6a,0xc1,0x17, - 0x46,0x84,0x12,0x4f,0x00,0x0b,0x51,0xc6,0x96,0xcc,0xb2,0x4c,0x09,0xd7,0x64,0xc1, - 0x2e,0x71,0x94,0xe9,0x9f,0x49,0xfe,0x37,0xa4,0x9a,0xae,0x16,0xbf,0xd7,0xa5,0x78, - 0x73,0x59,0x6c,0x9b,0xd5,0x21,0xc5,0x71,0x76,0x75,0x19,0x5f,0x05,0x2c,0x45,0xb1, - 0xe3,0xfc,0x32,0x37,0x82,0xce,0x48,0x24,0xb6,0x94,0xc5,0x29,0xed,0xd4,0x05,0x16, - 0x8b,0xeb,0xd6,0x48,0x43,0x0e,0x0b,0x5a,0x67,0xff,0xd4,0x15,0x3b,0x10,0xd7,0x27, - 0xe8,0x26,0x8f,0x5d,0xb6,0x83,0x75,0xa3,0x2b,0xc4,0x0b,0x96,0x15,0xec,0x07,0x2c, - 0xa5,0xee,0xdc,0xd5,0xb5,0x0b,0x13,0x29,0x35,0x04,0x24,0x3e,0x6e,0x70,0xaf,0xe0, - 0xba,0xb1,0x8b,0x9c,0x04,0x02,0x91,0x65,0xc7,0xa6,0x3d,0xb3,0x97,0x92,0x12,0x71, - 0x2e,0xd6,0x42,0x66,0xe2,0x1f,0x7f,0x1e,0x27,0x07,0x83,0xd1,0xcf,0x0d,0x10,0x47, - 0x3f,0x1f,0x1c,0x04,0x86,0xc4,0x74,0xa5,0xa1,0x9f,0xdf,0x6b,0xe4,0x13,0xa1,0xfa, - 0x66,0xeb,0xae,0x06,0x97,0x64,0xa4,0xc3,0x9f,0x20,0x06,0xff,0xdd,0x61,0x42,0x44, - 0xa4,0xac,0xea,0xaf,0x39,0x9f,0x23,0xd0,0x50,0x58,0x1d,0x55,0x70,0x1a,0x94,0x78, - 0x24,0x24,0x91,0x76,0xba,0xe5,0xe1,0xec,0xfd,0xfc,0x54,0xd0,0x10,0xca,0x02,0xc4, - 0x97,0x10,0x81,0xc5,0xd7,0x6d,0xd1,0xb0,0xe1,0xe8,0xb7,0x98,0xec,0x97,0x0e,0xa2, - 0xb2,0x6c,0x5c,0xe8,0x63,0xa0,0xa4,0x2d,0x20,0x7a,0xa2,0x24,0x10,0x7e,0xe1,0x75, - 0xf9,0xdd,0xde,0x74,0xb1,0xce,0xce,0x49,0x2a,0x62,0x44,0xa6,0x3f,0x1a,0x0d,0xad, - 0xcc,0x9c,0x93,0x98,0x8c,0x70,0x54,0x89,0x25,0xcf,0xf9,0x24,0x32,0x69,0xce,0x16, - 0xcb,0x98,0x2b,0x95,0xea,0x02,0x12,0x01,0x70,0xb3,0x20,0x98,0x61,0xff,0xe0,0x52, - 0xb7,0xa4,0x07,0xac,0x50,0x54,0x99,0xb6,0x17,0x2c,0x44,0x00,0x8b,0xbd,0x2b,0xb4, - 0x59,0x0b,0x25,0xfe,0x2e,0x6c,0x15,0x42,0x26,0x3e,0x01,0xcd,0x48,0xf1,0x22,0x01, - 0x13,0xc4,0x67,0x3a,0xa0,0x1d,0xd2,0x9d,0x14,0x7e,0x26,0xac,0x85,0xa4,0x3c,0x1e, - 0xd0,0x13,0xae,0xf6,0x5e,0x6e,0x7b,0x5c,0xfc,0x52,0x8b,0x81,0x10,0x83,0x7b,0xf9, - 0xbc,0x27,0xad,0x89,0x33,0x4f,0x7a,0x34,0x89,0x6b,0xac,0xcd,0xa0,0xbe,0xa4,0x6c, - 0x2e,0x56,0x73,0xa4,0x74,0x11,0x2c,0x0e,0x4e,0x05,0xef,0x3a,0x12,0x42,0x48,0x08, - 0x02,0x52,0xce,0x72,0x7d,0xec,0x83,0xb4,0xb6,0xa5,0x39,0xd5,0x05,0xe5,0xcd,0xa0, - 0xb4,0x37,0x91,0x47,0xa5,0x0f,0x99,0x20,0xd5,0x42,0x54,0x1b,0x46,0x26,0x99,0x27, - 0xbd,0x5a,0x79,0xb1,0x55,0x1b,0xb3,0xab,0xd5,0x30,0x1b,0x2a,0x5f,0x5e,0x72,0xe5, - 0xff,0x7a,0xc2,0x7c,0x23,0x20,0xa3,0x9f,0x20,0xb2,0xdb,0xe3,0xf5,0xbc,0x1c,0xef, - 0x20,0xaf,0xa0,0xad,0xd9,0x82,0x78,0x87,0x50,0xdc,0x28,0x2f,0xc6,0xcf,0x24,0x38, - 0xcc,0x54,0xb9,0xc6,0x62,0xf3,0x88,0x88,0xfe,0xd8,0x6f,0x6a,0x91,0x3a,0x7c,0x82, - 0x23,0xff,0x18,0xd8,0x9a,0xec,0xd2,0xc8,0xd5,0x2c,0xd1,0x79,0x8c,0x5a,0x0f,0x54, - 0xe9,0x1c,0x9f,0xf8,0xc6,0x7c,0x69,0x76,0x47,0xbb,0x01,0x4c,0xc8,0xcf,0xc1,0x20, - 0xd8,0x84,0xf7,0x1f,0xf6,0x5d,0xc7,0x32,0x88,0xc3,0x56,0x80,0x1d,0x8f,0xe8,0xc4, - 0xaf,0xf5,0x49,0xe2,0x25,0x2d,0x31,0x77,0x9f,0x8c,0xb8,0xdb,0x9a,0xa8,0x7b,0xdc, - 0x6e,0xf0,0xc9,0x98,0x3b,0xa1,0x06,0x27,0xfe,0x80,0xba,0xed,0xfb,0x66,0x3b,0xbf, - 0xe5,0x83,0x78,0x10,0xf7,0xc6,0xa9,0x29,0xfd,0xde,0x57,0xfd,0xdf,0x45,0x5e,0x07, - 0xa7,0x04,0x71,0x46,0x89,0x44,0x4e,0x49,0x31,0x8f,0x0a,0x47,0x2b,0x75,0xe4,0xa8, - 0xa7,0xca,0xeb,0x58,0xf6,0xdb,0xc1,0x6c,0x4a,0x73,0x31,0x17,0x00,0x5b,0x49,0x8d, - 0x34,0x9a,0x54,0x6a,0xc7,0x29,0x1c,0xa3,0xe9,0x03,0x82,0x99,0xa4,0x7e,0x8a,0xce, - 0x46,0x21,0x05,0xb6,0x60,0xce,0x0d,0xb7,0x39,0xbb,0x86,0x74,0x6b,0xf4,0xde,0x7a, - 0x4a,0xf8,0xab,0xd3,0x49,0x38,0x69,0x66,0x40,0x90,0x25,0xe5,0x56,0xe6,0xcd,0x65, - 0x66,0x0e,0x01,0x62,0xf3,0x6b,0xa4,0xe7,0x51,0xc4,0xde,0x28,0xcf,0x4c,0x79,0xb0, - 0xf1,0x8e,0x89,0x0f,0x33,0xa8,0x34,0x2d,0x86,0xcd,0x6b,0xcd,0xfb,0xac,0xe1,0x94, - 0xd4,0x68,0xe6,0x54,0xfb,0x50,0x91,0xd8,0x20,0x0f,0x49,0x7a,0x83,0x7b,0xf3,0xf3, - 0xaf,0xfa,0xb5,0xc8,0x6b,0x1e,0x12,0xa9,0xd6,0x8f,0x03,0x8d,0xb5,0x20,0x89,0xdb, - 0x88,0x65,0x82,0xee,0x59,0xe3,0x80,0x67,0xce,0xb5,0x2c,0xdd,0x05,0xe9,0xec,0xd7, - 0xe0,0xc0,0x6f,0x40,0x22,0x77,0x22,0x29,0xa9,0x7b,0x66,0x9c,0x2c,0x0a,0xf3,0x2f, - 0x16,0x86,0x07,0x8d,0xe1,0xf5,0x5d,0x99,0x9d,0x5f,0x7a,0x3c,0x3e,0x22,0x61,0x4f, - 0x40,0x17,0x9e,0x33,0x6a,0xc0,0x7d,0xbb,0x61,0x58,0xb3,0xe5,0x8d,0x88,0x44,0x6e, - 0xbe,0xdd,0xf6,0xfe,0xe5,0x1d,0xdb,0xea,0x96,0x77,0xfa,0xca,0xf8,0xb0,0x70,0x3b, - 0x55,0x1f,0x89,0x19,0xfd,0xab,0x22,0x46,0xdd,0x78,0x51,0x0d,0x27,0x09,0x9e,0x86, - 0xca,0x10,0x2a,0xef,0x80,0x24,0xdf,0x19,0xd1,0xd7,0x09,0x01,0xbd,0x6e,0x1b,0xa7, - 0x7e,0xf8,0xb1,0x56,0xec,0x1a,0xee,0x0c,0x57,0x02,0x43,0xed,0xf3,0x9a,0x52,0x10, - 0x3a,0x42,0x38,0xdd,0xc9,0xb8,0xb5,0xa7,0x05,0xa0,0x70,0x7b,0x17,0x4e,0x81,0xff, - 0xd4,0x94,0xdb,0xbd,0x9a,0xbb,0x71,0x09,0xdb,0x3a,0x05,0x0c,0x16,0xf1,0x7c,0x2e, - 0xb1,0x80,0x07,0xba,0x26,0x3d,0xc8,0xf3,0xa5,0x8e,0xde,0x73,0x2a,0x9b,0x3d,0x53, - 0x53,0x2e,0x8d,0xac,0x3e,0xf0,0xd9,0x72,0x0b,0x3e,0xd1,0xeb,0x2a,0x5f,0x37,0x92, - 0x72,0x45,0x56,0x2c,0x95,0x8e,0xad,0x8b,0x8b,0xe9,0x22,0xb9,0x90,0xa3,0xe2,0xb8, - 0x8e,0xed,0x8a,0xc9,0x1a,0xa7,0xda,0xaa,0xa2,0x66,0xf0,0x4b,0x85,0x9a,0x7d,0xa5, - 0x07,0x57,0x7f,0xed,0xd9,0x35,0x05,0xad,0x88,0x7f,0xd0,0x6d,0x91,0x50,0x74,0x3d, - 0x51,0x64,0x03,0x87,0x52,0x69,0xd7,0x3d,0xfc,0x89,0x65,0x19,0xae,0xdd,0xac,0x2b, - 0xa8,0x11,0x47,0x45,0x92,0x14,0x4e,0x56,0x10,0x57,0x93,0x3e,0xa0,0xc9,0x1c,0x9b, - 0xe0,0x14,0x40,0xec,0xca,0x79,0x98,0x5f,0x3f,0xfb,0x2e,0xb0,0xc9,0xc2,0x20,0xba, - 0xa5,0x1c,0x12,0xf3,0xf2,0xc9,0x77,0x56,0x18,0x62,0x73,0x1b,0xd1,0x64,0x7b,0x7c, - 0x9d,0x18,0x2c,0x99,0x8b,0x8b,0x9f,0x7a,0x66,0x2a,0x9a,0x62,0x82,0x6d,0x68,0xa5, - 0xd0,0xf9,0xf5,0x4a,0x8c,0x4c,0x3f,0x7e,0xfb,0xfa,0x4f,0xaf,0x9e,0xfc,0xf1,0x4f, - 0x4f,0xff,0xf9,0xed,0xf3,0xd3,0xf1,0x97,0x9f,0xa3,0x7e,0x11,0xff,0x71,0xed,0x3c, - 0xae,0x63,0x57,0x5b,0xe8,0x1a,0xfe,0x3a,0x13,0x03,0xd0,0xea,0xc2,0x75,0x9c,0xf4, - 0x3b,0xc6,0x3b,0x7a,0x31,0x24,0x19,0xbf,0x51,0x5a,0xf0,0x56,0xf3,0x08,0x97,0x43, - 0xb7,0x06,0x92,0xa8,0x2c,0xa6,0x63,0xdf,0x09,0x7d,0x10,0x27,0xde,0xcf,0xa5,0xa9, - 0x93,0xd4,0x42,0x21,0x0a,0x04,0xd7,0x6c,0xea,0xe7,0x3b,0xb1,0xd6,0x04,0x4b,0xb7, - 0x35,0x5a,0xc1,0x06,0xef,0xae,0x33,0xc7,0xbb,0xe8,0x07,0xa6,0x35,0x0b,0x34,0x04, - 0xc2,0xa6,0x7d,0x64,0x2b,0x47,0x43,0xcf,0x01,0x0b,0x53,0x31,0x04,0xaf,0x8a,0x45, - 0xcd,0x77,0xef,0x87,0xbc,0x4c,0x06,0x09,0x0d,0x96,0x86,0x88,0xae,0x2b,0x4d,0x9c, - 0x2e,0x02,0x74,0x05,0x73,0x75,0x2e,0x25,0xea,0xf5,0xa5,0x90,0x40,0x71,0xd4,0x4b, - 0x1a,0xaf,0x4a,0x36,0xdb,0x4e,0xe2,0x92,0x65,0x57,0x09,0x21,0x9d,0xe5,0x95,0x7c, - 0x4d,0xc2,0x21,0x22,0x3a,0x9d,0x13,0x0e,0x75,0x28,0x2e,0x57,0x7a,0x93,0xcd,0xf2, - 0x27,0xc2,0x86,0xaf,0xbf,0x3f,0xfd,0xe3,0x61,0xf4,0xa7,0xcf,0xbd,0x9f,0x21,0x88, - 0xc7,0xde,0xab,0x78,0xda,0x43,0x8c,0x33,0x03,0x08,0xa9,0x94,0x55,0x30,0x1d,0x14, - 0xbc,0x4a,0xb9,0x30,0x9b,0xf7,0xf4,0xfb,0xb7,0xdf,0xd0,0x9e,0x23,0x4a,0x1b,0x93, - 0xa8,0xa9,0xf9,0x35,0x3e,0x28,0x57,0x6a,0x2a,0x56,0x69,0x9a,0x9f,0x42,0x07,0x16, - 0x79,0x9c,0xe6,0x7a,0xd8,0xe1,0x02,0x9d,0x68,0x27,0xfa,0xee,0xf9,0x5b,0xe2,0xe3, - 0x5f,0xf3,0x9a,0xbf,0xa0,0x39,0x32,0x15,0x7c,0xaf,0x6a,0x52,0xf0,0x1a,0x35,0x63, - 0x4f,0x49,0x42,0x4b,0xf1,0x26,0xb1,0x6a,0x30,0xf4,0xde,0x13,0xd9,0xde,0x5c,0x32, - 0x0e,0x15,0xf9,0x65,0x13,0xc8,0x57,0x92,0x07,0xd3,0xf9,0xa9,0x83,0x3b,0x7f,0x60, - 0x94,0x2d,0x4d,0xb9,0x44,0x2d,0x67,0xcf,0xf4,0x29,0xc6,0x1c,0xc1,0xa1,0x2b,0xa4, - 0xf3,0x16,0x4b,0x2a,0x3d,0x3f,0x72,0xec,0x0e,0xdd,0x29,0xab,0xb5,0x9c,0xb2,0x61, - 0x14,0xd9,0x8c,0x53,0x54,0x71,0xc5,0x33,0x45,0xb7,0x60,0xc0,0x97,0xe3,0x22,0x97, - 0x3a,0x40,0x3b,0xb4,0x3b,0x7c,0x2d,0xce,0x61,0x25,0x45,0xe3,0x76,0x5e,0x6b,0x78, - 0x0b,0xf4,0x2e,0xed,0xc4,0x3f,0x4d,0x7e,0xba,0x24,0x65,0x03,0xb4,0xb1,0x13,0x89, - 0xab,0x84,0x80,0x65,0x05,0x3d,0x37,0x27,0x0f,0x08,0xed,0xe0,0xf4,0x4b,0x56,0xaf, - 0x59,0x6d,0xc1,0xf8,0x59,0xf2,0x46,0x8c,0x39,0x90,0x4d,0xc2,0xa8,0x30,0xcc,0x24, - 0x1b,0x4a,0x94,0x39,0x96,0x3d,0x14,0xfa,0x10,0xea,0x2a,0x7f,0xd1,0x4e,0x52,0x30, - 0x57,0xac,0xe4,0x80,0xe3,0xa1,0x73,0x22,0x1d,0x66,0x8f,0x56,0xa1,0x99,0x08,0x43, - 0x7a,0x3f,0x0d,0x4c,0x72,0xb0,0xb9,0x21,0xf1,0xd1,0xc0,0x08,0x6e,0x0b,0x32,0xd7, - 0xaa,0x8c,0x94,0x38,0x34,0x32,0x24,0xe6,0x95,0xa5,0x62,0xe9,0x8b,0x75,0x1c,0xd3, - 0x11,0xb7,0xa5,0x0b,0x29,0xc4,0x88,0x48,0x67,0x16,0xc1,0x90,0xc4,0x52,0x03,0x96, - 0x40,0x81,0x24,0xcf,0xcd,0x96,0xf9,0x1a,0xf5,0x95,0xbb,0xb6,0xcb,0x43,0x94,0xc6, - 0x55,0x41,0x68,0x4a,0xd1,0xc7,0x26,0xf4,0x5e,0x1f,0x7c,0xd2,0xd0,0x5d,0xa5,0x50, - 0x08,0xe3,0x27,0xd7,0xcd,0xad,0x8c,0xc1,0x5e,0x68,0xb9,0x44,0xd9,0x39,0x06,0x08, - 0x3d,0x00,0xf4,0xdc,0xb5,0xfe,0x8b,0xc3,0x7f,0x71,0xa0,0xfd,0x89,0xc1,0xb5,0x6a, - 0x4e,0x16,0xdc,0xdc,0x40,0xf3,0xa1,0x17,0x76,0x9e,0x04,0xcd,0x3a,0x7c,0x96,0x78, - 0xd2,0xfb,0x88,0x23,0xb9,0xc1,0xeb,0x37,0x34,0xa7,0x41,0xb3,0xb5,0x7f,0xd1,0x0b, - 0xf6,0x13,0x56,0xcc,0x79,0xd4,0xb0,0x03,0x0a,0x45,0x01,0xad,0x29,0x39,0xc4,0x01, - 0x41,0x94,0x36,0x56,0x08,0xbe,0xd1,0x31,0x87,0x4f,0x45,0x24,0x29,0x93,0xcc,0x7a, - 0xb5,0x52,0x52,0x17,0x2e,0x42,0x8e,0x83,0xfd,0x80,0xeb,0xb9,0x18,0xbd,0x83,0x23, - 0xe7,0x9b,0x06,0xb4,0xb9,0xb5,0x9f,0x11,0xc8,0x84,0x56,0x16,0x98,0xb9,0xae,0x7c, - 0x16,0x04,0x6e,0x58,0xc8,0x36,0xc3,0xe0,0x16,0x65,0x1b,0xb5,0xb5,0xe9,0xce,0x75, - 0xdd,0x20,0x1b,0x2e,0xd8,0x07,0x2b,0xa7,0xd4,0xe8,0x70,0x0f,0x78,0x33,0x1a,0x35, - 0x8a,0xee,0x14,0x9d,0x24,0xa5,0xff,0x43,0x12,0x1e,0xb2,0xf1,0xf5,0x26,0x94,0xc8, - 0xcc,0x7e,0x6d,0xbf,0x47,0xce,0xd9,0xd8,0x05,0x8d,0x83,0xa9,0xe8,0xed,0x77,0x02, - 0xd0,0x7b,0x08,0xed,0xf5,0xd5,0x78,0x30,0xb2,0x27,0x00,0xcc,0x83,0xcd,0x5d,0x83, - 0x9e,0x64,0x1e,0xb6,0x4d,0xaf,0xf3,0x46,0xc4,0x4c,0xfb,0x04,0x8b,0x71,0x76,0x77, - 0x9e,0x02,0x0d,0xc2,0x56,0xed,0x8b,0x8f,0xc5,0x4d,0x19,0x5c,0x6c,0x46,0xe1,0xed, - 0xb8,0xf3,0x27,0xeb,0xf9,0x56,0xb9,0x38,0xe6,0x91,0x4e,0x60,0x0d,0x5f,0x73,0x68, - 0xcd,0x76,0xa4,0x1e,0x7d,0xea,0xc4,0x22,0x49,0x0c,0x49,0x74,0xae,0xae,0xca,0xae, - 0x69,0xa3,0x25,0x0e,0xd8,0xd6,0x2a,0x69,0x54,0x13,0x23,0x96,0x37,0xc6,0x93,0xa8, - 0x04,0x47,0xea,0x1e,0xbe,0xfb,0xe9,0xa7,0xc3,0xf7,0x87,0x41,0xb4,0xca,0x57,0xdd, - 0x46,0x04,0x15,0xe6,0x0e,0x6f,0xdf,0xdc,0xe0,0x6f,0x44,0x63,0x2f,0x9e,0x54,0x5d, - 0x2e,0x71,0xe1,0x47,0xfe,0xcd,0xcd,0x3d,0x77,0xf6,0xf0,0x4a,0x33,0x40,0x55,0xc7, - 0x1c,0x71,0x4c,0xeb,0xd8,0x40,0xf9,0x8e,0x17,0xdb,0x09,0xdb,0x75,0x3a,0x93,0x30, - 0xd9,0x86,0x9c,0x15,0x34,0xbc,0x73,0xad,0xf1,0x4b,0x30,0xd2,0xc3,0xc0,0xc3,0xee, - 0x68,0x12,0x96,0xfd,0x83,0x6e,0xa3,0x89,0xe3,0xe3,0x23,0x78,0x33,0xbc,0x57,0x4f, - 0x21,0x3a,0x6f,0x27,0xed,0xe9,0xc2,0x26,0x58,0x72,0xb4,0xcb,0xf1,0x5f,0xef,0x9a, - 0xc0,0xbe,0x0f,0x65,0x6c,0x7f,0x75,0x10,0x57,0x7d,0x86,0xc5,0x66,0x4b,0xd4,0x79, - 0xce,0x1c,0x47,0xb8,0xbe,0xae,0xb0,0x93,0x3b,0x2c,0x8e,0x99,0x98,0xa9,0x09,0xc1, - 0x92,0x38,0xfb,0xf5,0x31,0x7a,0xae,0x2c,0x8b,0xbd,0x2c,0xc2,0x8e,0xa9,0x3a,0x21, - 0xa7,0x91,0xf1,0xe1,0xa8,0x17,0x72,0xc6,0xd9,0x14,0xac,0x0e,0x72,0xb4,0x5b,0xd6, - 0x26,0x99,0x9e,0xb7,0x90,0x39,0xde,0xa8,0x8d,0xc2,0xaf,0xce,0xba,0x42,0x6b,0xb7, - 0x4e,0x61,0xa6,0x25,0xce,0x59,0x2e,0x49,0xcd,0xe7,0xd8,0x6a,0x34,0x9b,0xe9,0xbc, - 0x19,0x53,0xa1,0x1c,0x3c,0xc3,0x1c,0x89,0xbd,0x55,0xdf,0x95,0x9d,0x16,0x6d,0x94, - 0x77,0x6b,0x1e,0x59,0x42,0x73,0x2c,0x12,0x7c,0x6d,0xad,0xcf,0x3a,0x76,0xbd,0x32, - 0x35,0x79,0x0c,0x64,0xba,0x22,0x06,0xa4,0x83,0x50,0x2b,0x4e,0x79,0x61,0x23,0x23, - 0x2a,0x23,0xda,0xb5,0xc0,0xa4,0xcb,0xa3,0x69,0x3f,0x10,0x77,0x7e,0x6b,0x24,0x54, - 0x6b,0x18,0xd3,0x4e,0x6d,0x39,0xea,0x8b,0xe3,0x45,0xb5,0xe3,0x65,0xcb,0x0b,0xb5, - 0xd5,0x9b,0x5d,0x95,0xda,0x19,0x65,0x6b,0xa1,0xd5,0xa1,0x21,0xdb,0x85,0x03,0xcc, - 0x97,0x6e,0xfd,0x00,0x6d,0x54,0x80,0x3d,0x9d,0xf1,0xfd,0x7a,0x3e,0x34,0xaf,0xbd, - 0x4b,0xde,0x73,0xdc,0x94,0x7b,0x43,0xaa,0xff,0x95,0xda,0x73,0xee,0x87,0xa4,0xd8, - 0x0f,0xfb,0x21,0x5b,0x3b,0x86,0x7d,0x5b,0xd3,0xae,0xcd,0x5f,0xe4,0xb8,0x7d,0xb8, - 0x7c,0x6c,0xe0,0xa8,0xf1,0x6c,0xee,0xe8,0x3b,0x55,0xdb,0x58,0x18,0xe5,0x6a,0xbd, - 0xac,0x8f,0x39,0x79,0x67,0xa2,0xc9,0xed,0xd4,0xfe,0x36,0x29,0x1a,0x1e,0x0e,0x58, - 0x95,0xc8,0x02,0x96,0x3e,0x49,0x82,0xad,0xe4,0x94,0x63,0x9e,0xe2,0x66,0x3e,0x9d, - 0xae,0xdb,0x8d,0xb8,0x26,0x39,0xd8,0xd7,0x14,0xde,0x2d,0x58,0x0d,0xa8,0x48,0x52, - 0xe6,0x43,0x5a,0x4c,0xb5,0x16,0x73,0x60,0x4b,0x3c,0xe1,0xb8,0x97,0x9c,0x03,0x8e, - 0x45,0xf2,0x64,0xf5,0x72,0x4e,0x6c,0x68,0x21,0xc7,0x0d,0x4a,0xb9,0x29,0x24,0x84, - 0x55,0xe6,0x68,0xec,0x84,0x03,0x57,0x18,0x07,0x49,0x14,0x95,0x53,0x96,0xfc,0xe0, - 0x96,0x00,0xe4,0x46,0x88,0x9a,0x3e,0xcf,0xcf,0x64,0x45,0xeb,0x5d,0x78,0xf2,0x53, - 0xf6,0x53,0xc6,0x53,0xa5,0xe5,0x74,0x03,0x80,0x73,0x2a,0xa1,0x26,0x18,0x3e,0xaf, - 0xf2,0x81,0xbf,0x73,0x02,0x62,0xe4,0x3d,0xb1,0x67,0xe1,0xb0,0x41,0x31,0xcf,0xcc, - 0x21,0xf3,0x09,0xc1,0xb5,0x9e,0xd5,0x89,0x96,0x8d,0x44,0x3b,0xd3,0xa0,0x9e,0xc2, - 0xe9,0xba,0x40,0x45,0x23,0xe4,0xc2,0x98,0x72,0x5b,0x9c,0x60,0x53,0x56,0x45,0x7e, - 0x05,0xbd,0xfd,0x7b,0x73,0x30,0x88,0x9d,0x1c,0x73,0xe4,0xbd,0x74,0x6e,0xda,0xb3, - 0x73,0x8f,0x99,0x6d,0xe6,0x36,0x98,0x72,0x63,0xe5,0x22,0x5e,0x29,0x4f,0x87,0x36, - 0x4d,0xea,0x4c,0xdb,0xa1,0x94,0xae,0x8e,0x39,0xb6,0x2a,0xb7,0xe7,0x19,0x88,0x95, - 0x80,0x36,0xbc,0x18,0xfd,0x79,0xb5,0xa4,0x35,0xce,0x01,0x34,0x6e,0x10,0x39,0xfb, - 0x49,0xe6,0x40,0x9f,0x10,0x85,0xa4,0x2c,0xf1,0x81,0x44,0x36,0xb2,0xbb,0x3e,0x64, - 0xd2,0x6f,0x24,0x97,0x73,0xa5,0x80,0x8f,0x26,0xaf,0xc9,0x97,0xdb,0xc9,0x6b,0x7f, - 0x83,0x20,0xc0,0xdd,0xc8,0x50,0x1b,0x16,0x68,0x4b,0x27,0x63,0x55,0xae,0xbc,0x9e, - 0x63,0x61,0x11,0xff,0x43,0x46,0x92,0xbf,0x1b,0x2a,0xa8,0xcb,0x67,0xdb,0x50,0x41, - 0x87,0x19,0x6e,0x45,0x0d,0xb6,0xd9,0x2f,0x77,0xd2,0x9a,0x76,0x43,0xfd,0x1c,0x60, - 0xdd,0x7a,0xdd,0x5b,0x4e,0xe7,0xf6,0xfc,0x6a,0x4b,0x57,0x6e,0x0f,0xd9,0x2f,0xd4, - 0x2c,0xd7,0x94,0xef,0x8e,0xee,0x77,0xc7,0x3b,0x1d,0xe0,0x6b,0x21,0x82,0x7c,0x69, - 0x7d,0xee,0x7c,0x5f,0xb3,0x12,0x87,0x0e,0xe3,0x76,0x9d,0xfa,0xed,0x1e,0x4b,0x39, - 0x64,0xcf,0xc8,0x07,0x22,0x3c,0x25,0x9b,0x7f,0x40,0x60,0x96,0xec,0x92,0x7e,0x92, - 0x11,0x3f,0x49,0xf8,0x30,0x52,0x94,0x3c,0x2b,0x3c,0xce,0x25,0xed,0x81,0xc8,0x11, - 0x07,0x46,0x3d,0x7c,0x5b,0x80,0xd7,0x19,0x1d,0xfc,0xfe,0x78,0xd6,0x60,0x6a,0x4d, - 0x36,0x2d,0xcf,0x60,0x4e,0xd1,0x11,0x9c,0xac,0xc9,0xca,0x39,0x9a,0xd8,0x8a,0x26, - 0xd5,0x52,0x7c,0x09,0xeb,0x4c,0x57,0xcb,0x0e,0x25,0x75,0x3a,0xa9,0x8b,0xb0,0xcb, - 0xe9,0x9c,0xf0,0xba,0xe8,0xf8,0x3d,0xd9,0xa0,0x62,0x44,0x48,0xb8,0x54,0x11,0xfb, - 0xce,0xe0,0x31,0x80,0x2b,0x32,0x9b,0xa1,0x7e,0x5e,0x76,0xb6,0x26,0xee,0x5d,0x1f, - 0x7f,0x15,0xeb,0x9d,0x23,0x05,0x9f,0x68,0xe3,0xa2,0xe4,0x15,0x0d,0x91,0x8f,0x07, - 0xd8,0x2d,0x1c,0xc2,0x10,0x34,0x8a,0x90,0x80,0xfe,0x99,0x8a,0xde,0x4d,0xa9,0x03, - 0xcb,0xf2,0x4e,0xd7,0x7f,0xa9,0x50,0x53,0xa5,0x2e,0xaf,0xf2,0xbe,0xa5,0xda,0x05, - 0xe3,0x46,0x7b,0xb7,0xaa,0x59,0xaa,0xe4,0xd6,0x2a,0x25,0x4e,0x71,0xfb,0xfa,0x78, - 0x83,0x66,0xb9,0x15,0x20,0x6d,0x0d,0x98,0xae,0xa9,0x22,0xf5,0x50,0xfe,0xf6,0x40, - 0x6d,0x01,0xe1,0xd6,0x4d,0x31,0x70,0xdc,0x32,0xcf,0xed,0x05,0x5a,0x20,0xbc,0x5d, - 0x70,0x8a,0xb0,0x71,0x66,0xdd,0x86,0x69,0xad,0xaf,0x71,0x87,0x3a,0x26,0x72,0xab, - 0xde,0xa3,0xb1,0xd0,0x3a,0x16,0x68,0x98,0x12,0x86,0x9e,0x13,0x36,0x5d,0x4c,0xc7, - 0x8f,0x35,0x87,0xe9,0xad,0x8b,0xf4,0x38,0xb2,0x12,0x00,0xd3,0x61,0x12,0x0d,0xec, - 0xb1,0xa9,0xfa,0x30,0x3e,0xbf,0x34,0x07,0xa8,0x1a,0x53,0x1c,0x7c,0xb0,0x7c,0x00, - 0x1b,0xf1,0x17,0x1d,0x71,0xd1,0x5d,0x54,0xd5,0xaa,0xe4,0x42,0x8e,0x75,0xe7,0x62, - 0x23,0xa2,0xa6,0xf9,0x69,0xe0,0x9e,0x38,0x40,0xfb,0xe0,0xdb,0xb7,0x92,0xe6,0xcd, - 0xe6,0x29,0xa2,0x44,0x52,0x9d,0x90,0xa9,0xb9,0x35,0x59,0xed,0xe9,0xe4,0x4f,0x5d, - 0x34,0x8f,0xfb,0xc4,0x7d,0x7d,0x6d,0xcd,0xde,0x9a,0xd2,0x8a,0xa4,0xce,0x8c,0xd3, - 0x1c,0x37,0x52,0x17,0xc1,0x74,0xec,0xd6,0xfa,0x80,0xba,0x38,0x3d,0x2f,0x0d,0xcf, - 0x36,0xf6,0x69,0x6b,0xd8,0xd2,0xa3,0xb7,0x69,0x4d,0x96,0x7b,0xee,0x5a,0x53,0x60, - 0x00,0xfc,0xa1,0x48,0xbb,0x65,0x31,0xfd,0xb4,0xec,0x2e,0x84,0x58,0x87,0x46,0x1a, - 0xd3,0x0e,0xd3,0xac,0xdd,0xe5,0xb3,0x46,0x01,0x39,0x2a,0x6b,0x35,0xa6,0x1e,0xea, - 0xf2,0xb4,0x27,0x5c,0x9a,0xf6,0xc4,0xdf,0x27,0x26,0x71,0xe2,0xd7,0xf5,0x2d,0xe9, - 0xa5,0x03,0x7a,0xf9,0x80,0xcf,0xcb,0x1c,0x0f,0x76,0x52,0x7c,0x0a,0x47,0x70,0x77, - 0xf2,0xde,0xfa,0xa3,0x2d,0x2e,0xd8,0x5a,0x5e,0xd9,0x70,0x42,0x4d,0xf7,0x78,0xf6, - 0xd9,0x68,0xc7,0xc1,0xa3,0x98,0x76,0xd2,0xd9,0x20,0x5a,0x47,0x48,0xc3,0x36,0xa5, - 0x2c,0x26,0x8a,0xa5,0xfe,0x43,0x2d,0xf4,0x73,0xf9,0x35,0x39,0xad,0xcf,0x64,0xf8, - 0xaa,0x79,0xc5,0x47,0xc5,0xb0,0x9d,0xce,0xb5,0xe8,0x89,0x09,0x6b,0xa8,0x1f,0xd2, - 0x2c,0x64,0x6d,0x33,0xe5,0x97,0xa6,0xa5,0x59,0x8e,0x9a,0xad,0x6c,0x1a,0x9d,0x71, - 0xc0,0x75,0xba,0x22,0x4a,0xa9,0xb8,0xe8,0xa8,0x29,0x79,0xd8,0x2c,0x07,0xa1,0x6f, - 0x56,0xec,0xe8,0x35,0xc7,0xa4,0x46,0x75,0x5c,0x25,0x2c,0xc0,0x63,0x9c,0x98,0x80, - 0xed,0xa7,0x0f,0x28,0x13,0xa3,0x8f,0xa3,0x7b,0x3b,0xd5,0x93,0x24,0x17,0xb5,0x69, - 0x69,0xca,0x5c,0x4b,0x13,0xcd,0x84,0x63,0xf6,0x22,0x8e,0x6b,0x16,0x80,0x9b,0x68, - 0xd1,0x31,0x8c,0x96,0x21,0x2d,0x2f,0xe3,0x55,0x5b,0xc3,0xa4,0x4a,0x70,0xa8,0x3b, - 0xeb,0x10,0x59,0x68,0x42,0xdd,0xb7,0x74,0x06,0x47,0x49,0xfe,0xb8,0xda,0xd0,0xa6, - 0x38,0xdc,0xcd,0x1c,0xf4,0xa9,0xa8,0xd8,0x3b,0xe6,0xbf,0x35,0x32,0x47,0x2d,0x4e, - 0xf3,0x69,0xaf,0xad,0x79,0x1f,0x26,0xce,0xc0,0x35,0x57,0xec,0x62,0xfe,0xed,0x48, - 0x8f,0x84,0x25,0x5b,0xa1,0xa0,0xf5,0xf3,0x09,0xfb,0x49,0xe7,0xe3,0xda,0x48,0x31, - 0x79,0x5f,0x87,0x37,0x68,0x17,0x2a,0x2b,0x6a,0xf5,0x9c,0x35,0xdb,0xdb,0x91,0xd0, - 0x5a,0xfd,0xcb,0xb5,0xb2,0x2a,0xd4,0x90,0xd1,0xbd,0x46,0x75,0xb7,0xf1,0xbf,0xcc, - 0x06,0x22,0xfa,0xdf,0xe6,0xe3,0x85,0xda,0xdb,0x02,0x2f,0x77,0x2a,0xb5,0x0b,0x80, - 0x46,0x65,0xb1,0xfb,0x92,0x4f,0xe0,0xd1,0x7a,0x89,0x28,0x0e,0x09,0x82,0xf7,0x59, - 0xde,0x8f,0x2f,0x68,0xb0,0xd8,0x32,0x27,0xee,0x01,0x30,0xa2,0x81,0xb7,0xb2,0x35, - 0xce,0x81,0x78,0xfd,0xe4,0xeb,0xdf,0xef,0x30,0x32,0x62,0x60,0xf2,0x14,0xb6,0xc5, - 0x63,0xe3,0xe4,0xb1,0x74,0xc9,0x70,0x15,0xa6,0x4d,0x28,0x92,0x15,0xd6,0x87,0xfa, - 0x6c,0x25,0x58,0x94,0xf6,0x14,0x23,0x43,0x84,0x98,0x5d,0x98,0x03,0x32,0x89,0xb7, - 0xb3,0x76,0x63,0x98,0x03,0xd8,0xaa,0x8e,0xe3,0x58,0x35,0x1c,0xd7,0x7c,0xd2,0x32, - 0x98,0xa2,0x38,0x60,0x09,0xac,0xd2,0xd4,0x2e,0x45,0x31,0x73,0x06,0x06,0x42,0x3c, - 0x8a,0xa7,0xb2,0x57,0x55,0x9f,0x56,0xe4,0x9e,0x7c,0xd5,0xb1,0xdd,0x4a,0x45,0x34, - 0x66,0x75,0x5c,0x3f,0x56,0x2a,0x9d,0x41,0x69,0xeb,0x88,0xc7,0x4a,0x3b,0x8b,0x9b, - 0xc6,0x29,0x2b,0x40,0x18,0x5f,0x80,0x63,0x9f,0x82,0xd7,0x45,0xdb,0xa7,0x4c,0x0d, - 0x20,0x6c,0xa4,0x26,0xe9,0x66,0x9e,0x39,0xb2,0xc2,0x28,0x2b,0x89,0x28,0xc6,0x62, - 0x8e,0x02,0x13,0xdf,0x77,0x62,0xbd,0x6a,0x4f,0x84,0x34,0x67,0x39,0x07,0xad,0xc1, - 0xbc,0xe4,0xa4,0xbd,0x5d,0xa9,0xca,0x39,0xb7,0x95,0x06,0xbd,0xe2,0xf3,0x77,0x74, - 0x0d,0x59,0x5b,0xf6,0x86,0x40,0xaf,0xe4,0x1c,0x58,0xf8,0x2d,0x08,0x6e,0xf6,0x6a, - 0x1d,0xf5,0x8f,0x1a,0x53,0xc3,0x51,0xf9,0xe0,0xc1,0x5c,0xb4,0x5f,0xa7,0x34,0x4c, - 0x61,0xaa,0x20,0x0e,0x91,0x60,0x29,0x05,0xd3,0xb4,0x39,0x42,0xcd,0xce,0x58,0xe8, - 0x96,0x6a,0xf4,0x25,0x20,0x88,0x2b,0x4e,0x72,0x58,0x71,0x28,0xbf,0x74,0x6d,0x82, - 0x35,0x99,0x88,0x95,0xbb,0xec,0xfd,0xc7,0x64,0xe5,0x70,0x78,0xa1,0xa4,0xbc,0x47, - 0x24,0x48,0xc5,0x04,0xa8,0x18,0xe6,0xce,0xd7,0x27,0x3e,0x03,0x62,0xc4,0x7c,0x4c, - 0x3e,0x98,0x79,0x9d,0x14,0xd1,0xb8,0x1d,0x34,0x98,0x77,0x5b,0x9a,0xfb,0xed,0x2c, - 0xdc,0xc9,0x6f,0xa7,0xd9,0x72,0x6d,0xee,0x0c,0xc8,0xf1,0xf8,0x41,0x7f,0x37,0xb1, - 0xdd,0x74,0xce,0x67,0xed,0xf2,0xae,0xb4,0xe0,0x9a,0xf3,0xa0,0xdc,0xa3,0xa0,0xb8, - 0x8e,0x93,0xbf,0x9d,0xf0,0x7e,0x9b,0x8f,0xbc,0x11,0x08,0x04,0x9f,0x39,0xa7,0x56, - 0x3a,0x84,0xf0,0x36,0xa3,0xec,0x47,0xe6,0x1b,0xe1,0x43,0xdb,0x01,0xee,0x9f,0x4c, - 0xda,0x37,0xb2,0x24,0x1b,0x73,0x70,0x9a,0x9b,0x7f,0x60,0x66,0x0b,0x27,0xdd,0x36, - 0x49,0xa5,0x61,0x00,0xbb,0x34,0xdf,0xcd,0xe7,0xac,0x27,0xa1,0x16,0xd3,0xdf,0x35, - 0xf8,0x81,0x6f,0x73,0x5a,0x83,0xf7,0x77,0x4c,0xdb,0xfc,0x84,0xc5,0x72,0x6b,0x18, - 0xbb,0x54,0xb3,0x11,0x5f,0xcc,0xc7,0xa2,0x69,0x41,0x94,0xf6,0xc0,0x2b,0x8e,0xdc, - 0xd4,0xf1,0x82,0xec,0x17,0x86,0xf4,0x35,0x74,0x88,0xde,0x0f,0x6f,0x5e,0x4a,0xf0, - 0x26,0x57,0xcb,0x3a,0x21,0x7c,0x1c,0x7f,0x00,0xfc,0x9d,0x3a,0x4e,0x72,0xa2,0x03, - 0x34,0x51,0xbc,0x3d,0x3e,0xd7,0x06,0x3e,0xf1,0x49,0x47,0xde,0x53,0x90,0x3b,0x36, - 0x13,0xe9,0xc3,0x33,0x35,0x19,0xb5,0xce,0xdd,0x99,0xd7,0x99,0xc4,0x65,0x32,0x2d, - 0xa5,0x55,0x6b,0x11,0xb2,0xe2,0x1e,0xef,0x7d,0xa3,0x5c,0xa0,0x70,0x2b,0xd0,0xae, - 0x73,0xc8,0x6f,0x9b,0xf8,0x91,0x5a,0xbf,0x8f,0xf9,0x0c,0x61,0x13,0x9b,0x49,0x43, - 0x7d,0x4d,0xa3,0x62,0xc9,0x58,0x7c,0x29,0x90,0x8b,0xdf,0xf5,0xdf,0x9b,0xab,0xcf, - 0xf8,0x4a,0xdb,0x16,0x0e,0xdf,0x45,0x87,0xef,0x5d,0x3f,0x94,0xfe,0x3c,0x08,0x5a, - 0x71,0xcf,0xc9,0xc5,0xda,0x95,0xf4,0x79,0xe6,0x2d,0x08,0x45,0xbc,0x64,0xb9,0x80, - 0x66,0xf3,0x54,0x81,0x2c,0xf3,0xad,0xb2,0x4b,0x04,0x4b,0x42,0xfc,0x4a,0xbe,0x1b, - 0xc8,0xb1,0x60,0xf4,0x09,0x16,0x10,0xc5,0x4a,0xf4,0xc7,0xfb,0xfb,0x87,0xff,0xc2, - 0x3a,0xd4,0xc9,0xf0,0xa7,0xc3,0x9f,0x0e,0x0f,0x1d,0xf0,0xe8,0x69,0x60,0x96,0xf3, - 0x19,0xf4,0x1a,0xba,0x2b,0xce,0x07,0x5d,0xa6,0x0d,0x6a,0x14,0x02,0xe4,0x0b,0xa5, - 0xc1,0x1c,0x79,0xe7,0x4a,0xad,0xbc,0x0b,0xa9,0xe6,0xad,0x0b,0x44,0x4b,0xbd,0x6c, - 0x92,0xc2,0xcc,0xd4,0x81,0xae,0x1b,0x98,0x6b,0x38,0x39,0x1d,0x1a,0xe3,0xe6,0x13, - 0x87,0x4b,0x67,0x2c,0x91,0x58,0xfe,0x2c,0xf4,0xf4,0x4a,0x02,0xfa,0x74,0x15,0x21, - 0x1a,0x2e,0x9e,0x2a,0x24,0xf5,0xa9,0xae,0xb9,0xc9,0x59,0x68,0xa1,0xef,0x87,0xdc, - 0x10,0x50,0x8f,0x33,0x7e,0x9d,0x66,0xe5,0x37,0x31,0x87,0x85,0x09,0xef,0x44,0xb9, - 0x09,0x09,0xd5,0x26,0x4c,0x1e,0xba,0x27,0x8e,0x05,0x7c,0x20,0x24,0x0f,0x40,0x6a, - 0x55,0xd4,0x67,0xb4,0x48,0x2c,0x1e,0xf3,0x8d,0xfa,0xb8,0x3f,0x69,0x8f,0xcf,0x61, - 0x27,0x8c,0x75,0xa2,0x68,0x09,0x73,0x75,0x05,0xa1,0xfa,0xa8,0x17,0x6d,0x16,0x13, - 0x9f,0x08,0xb1,0x4d,0xe2,0x24,0x75,0x25,0x33,0x62,0x60,0x51,0x7d,0x7e,0x31,0x4e, - 0x22,0x34,0xab,0xc2,0xf5,0x54,0x4c,0xf1,0x6b,0x42,0x06,0x73,0x12,0xc8,0xe3,0x43, - 0x89,0xfe,0x39,0x7e,0x7c,0x08,0x8e,0x48,0xff,0x2c,0xaa,0x65,0x7a,0xfc,0x7f,0x01, - 0xe1,0xd7,0x15,0x74,0xc3,0xa8,0x00,0x00, + 0x1f,0x8b,0x08,0x00,0x00,0x00,0x00,0x00,0x02,0xff,0xcd,0x7d,0xdd,0x77,0xdb,0xc6, + 0x92,0xe7,0xbb,0xfe,0x0a,0x58,0x9e,0x11,0x88,0x08,0x84,0x48,0xd9,0x4e,0x1c,0xd2, + 0x90,0xd6,0x9f,0x93,0xec,0xb5,0x1d,0x8f,0xe5,0x6c,0xee,0xc4,0xd1,0xe4,0x80,0x04, + 0x28,0x22,0x02,0x01,0x1a,0x00,0x25,0x2b,0x12,0xe7,0xcc,0xd3,0xbc,0xef,0xce,0x9c, + 0xb3,0x2f,0xfb,0xbc,0x7f,0xd8,0xfd,0x4b,0xb6,0x7e,0x55,0xdd,0x8d,0x06,0x09,0xd9, + 0xce,0xde,0xbb,0x67,0xf6,0x9e,0x5c,0x99,0x00,0x1a,0xfd,0x51,0x5d,0xdf,0x55,0x5d, + 0x78,0x74,0x27,0x2e,0xa6,0xf5,0xd5,0x32,0x71,0xe6,0xf5,0x22,0x3b,0xda,0x79,0x84, + 0x7f,0x9c,0x2c,0xca,0xcf,0xc2,0xdd,0x24,0xdf,0xc5,0x8d,0x24,0x8a,0x8f,0x1e,0x2d, + 0x92,0x3a,0x72,0xa6,0xf3,0xa8,0xac,0x92,0x3a,0xdc,0x5d,0xd5,0xb3,0xfe,0x43,0x3c, + 0xac,0xa6,0x65,0xba,0xac,0x9d,0xaa,0x9c,0x86,0xbb,0x07,0xcb,0x59,0x7f,0x5a,0xe4, + 0x55,0x91,0x25,0xc1,0x6f,0xd5,0xf1,0x3c,0x4c,0x66,0xb3,0x59,0xfc,0x60,0xf0,0x60, + 0xf7,0xe8,0xd1,0x81,0x34,0x3c,0x52,0x2f,0x1c,0xa5,0xb3,0xde,0x9d,0xcb,0x34,0x8f, + 0x8b,0xcb,0xe0,0xcd,0x0b,0xaf,0x37,0x5b,0xe5,0xd3,0x3a,0x2d,0xf2,0x9e,0x77,0x7d, + 0x11,0x95,0xce,0x4f,0xa1,0x3c,0xf3,0x7f,0x0c,0xdd,0x83,0x68,0x99,0x1e,0x54,0x75, + 0x54,0xaf,0x2a,0xd7,0x7f,0x19,0xbe,0x3f,0xf5,0x5f,0x85,0x87,0xc9,0xb7,0xfe,0x89, + 0xff,0xb3,0xff,0x26,0xfc,0x89,0x5e,0x0f,0xaf,0xf1,0x38,0x19,0xb9,0x59,0x7a,0x91, + 0xb8,0x7e,0x9c,0x96,0xf5,0xd5,0xe8,0xce,0xd0,0xbf,0x18,0xe5,0xab,0x2c,0xf3,0xcf, + 0x92,0x7a,0xf4,0x0f,0x7e,0x9a,0xcf,0xb2,0xf4,0x6c,0x5e,0x8f,0x5e,0xaf,0x16,0x93, + 0xa4,0xf4,0xe7,0x45,0x16,0x8f,0x7e,0x98,0xfc,0x96,0x4c,0x6b,0xbf,0x8a,0xae,0x46, + 0x66,0x06,0xb5,0x7f,0xee,0x27,0xde,0x75,0xb2,0xb7,0xd7,0x4b,0x82,0x3a,0xf9,0x58, + 0x3f,0x2d,0xf2,0x3a,0xc9,0xeb,0xb0,0xf6,0xd6,0xfe,0x64,0x55,0x59,0x4d,0x27,0xfe, + 0xd2,0xbb,0xd6,0x57,0xce,0x8c,0x26,0x3f,0x09,0xe2,0xb4,0x8a,0x26,0x59,0x12,0x87, + 0x77,0x86,0x6b,0xfb,0x6a,0x30,0x5e,0x06,0xf5,0x3c,0xc9,0x7b,0x33,0x7f,0xe6,0x8d, + 0xcb,0xa4,0x5e,0x95,0xb9,0xb3,0x5c,0xfb,0xcb,0x22,0xcb,0x9a,0x1e,0x2b,0x7f,0xe1, + 0x4f,0xfd,0x42,0xa0,0x50,0xfb,0x33,0xff,0x2c,0x1c,0x8e,0xcd,0x08,0x6f,0x69,0x84, + 0xb3,0xbd,0xbd,0x3b,0x33,0x9a,0xdb,0x2c,0x1c,0xfa,0xd3,0x2c,0x89,0xca,0x77,0xe9, + 0x22,0x29,0x56,0x75,0xaf,0xf6,0xfc,0x5e,0x15,0x4c,0xa3,0x2c,0x3b,0xae,0x7a,0xde, + 0xe8,0x1f,0x7a,0x95,0xe7,0xa9,0x11,0x75,0xef,0xb1,0x77,0x3d,0xed,0xc5,0x3e,0xa0, + 0x42,0x6b,0x31,0xb7,0x13,0xdc,0x66,0x50,0x25,0xde,0x5a,0xbd,0xf3,0x27,0xff,0x4f, + 0x9e,0xb7,0x36,0x23,0xff,0x89,0x46,0x9e,0x85,0x83,0xf1,0xc6,0x88,0x63,0x9a,0x4d, + 0xaf,0x0e,0x09,0x25,0xf4,0xbd,0xb7,0xfe,0x82,0xde,0x2b,0xf6,0xf6,0x8a,0x60,0x96, + 0x96,0x55,0x1d,0x86,0x04,0x88,0x63,0x7a,0x7d,0x44,0x93,0x57,0xcb,0xa6,0xed,0x2a, + 0x96,0x23,0x6b,0xcb,0xcf,0xb6,0x7b,0x5e,0xfb,0xd4,0x69,0xd3,0x26,0xf7,0xae,0x17, + 0x61,0x3e,0x9e,0xdd,0xdc,0x50,0x57,0x6b,0x3f,0x2f,0x2e,0x47,0x6f,0xd7,0xd4,0x86, + 0xd1,0xa2,0x69,0x36,0xa5,0x95,0xec,0xed,0xbd,0x0c,0x96,0xab,0x6a,0x4e,0x17,0xe3, + 0x93,0x63,0xba,0x9c,0xf6,0x4e,0xbc,0xd1,0xcf,0xc7,0x3f,0x07,0xf4,0x16,0xcd,0xe3, + 0xe7,0xf0,0x4d,0x00,0xa0,0xf7,0x7e,0xf4,0x5f,0xf9,0xcf,0xa8,0xb3,0xcb,0xa8,0x9e, + 0xce,0x4f,0x36,0x7a,0x5a,0x78,0xd7,0x6f,0x02,0xe9,0x9e,0xe6,0xfd,0x73,0x40,0xb3, + 0xe9,0xbd,0x0a,0x5f,0x45,0xf5,0x3c,0x58,0xa4,0xf4,0xd8,0x7f,0xe5,0xf1,0xab,0x69, + 0xfd,0xa2,0x28,0x9f,0x25,0x17,0xe9,0x34,0x69,0x5e,0xa6,0xfd,0x2b,0xc2,0xe2,0xe6, + 0xe6,0x7a,0xad,0x37,0x3a,0x4f,0x2e,0x9d,0x37,0x65,0xb1,0x48,0xab,0xa4,0xd9,0x8e, + 0x2b,0x3f,0x97,0x8d,0x9e,0x37,0x53,0x3a,0x4c,0xee,0x35,0x1b,0x53,0x79,0xd7,0x15, + 0x6d,0x77,0xaf,0x08,0x26,0xab,0x34,0x8b,0xf7,0xf6,0x2a,0xf9,0x11,0x86,0xea,0x8e, + 0x47,0xe0,0x9f,0x07,0x00,0x67,0xcf,0xf3,0xaf,0xb0,0xe3,0x6b,0x6f,0x6c,0xed,0x86, + 0x05,0x63,0xdd,0x6c,0x9c,0xf7,0x9e,0x97,0x65,0x51,0xf6,0xdc,0x5a,0x1a,0xb9,0x58, + 0x48,0x11,0xa8,0xab,0x9b,0x9b,0x6f,0x93,0xfb,0xd4,0xcb,0x7a,0xdd,0xa0,0xdd,0x3f, + 0xf4,0x56,0x40,0x49,0xb5,0x94,0x59,0x42,0xe0,0xe2,0x3b,0x1b,0xd8,0x55,0x7a,0xd7, + 0xa0,0xea,0x32,0x28,0xce,0xbd,0x7a,0x5e,0x16,0x97,0x8e,0x0c,0x54,0x2a,0x38,0x1a, + 0xac,0x67,0xec,0x00,0x65,0x1d,0x97,0xfc,0x0f,0x6d,0x4a,0x49,0x0c,0x03,0xd3,0x5c, + 0x5b,0x38,0xf7,0x0c,0xeb,0xa7,0x1e,0xe9,0xef,0x09,0xd1,0xf9,0x72,0x26,0x7b,0x14, + 0x56,0xe3,0x97,0xc1,0xac,0x28,0x9f,0x47,0x34,0x0d,0x33,0xf8,0x8c,0xf0,0x93,0x5a, + 0x62,0xe2,0x6b,0xaf,0xe7,0x19,0x7e,0xb3,0x23,0x8c,0x2b,0x8f,0x16,0x49,0xb8,0x7b, + 0x91,0x26,0x97,0xcb,0xa2,0xac,0x77,0x9d,0xa9,0xa2,0xe9,0xdd,0xcb,0x34,0xae,0xe7, + 0x61,0xcc,0x1b,0xd8,0xe7,0x0b,0xe2,0x14,0x69,0x9d,0x46,0x59,0xbf,0x22,0x5a,0x4a, + 0xc2,0x21,0xb8,0x5c,0x9d,0xd6,0x59,0x72,0xf4,0x26,0xaa,0xeb,0xa4,0x24,0x3e,0x42, + 0x4b,0xeb,0x3b,0xea,0xaa,0x7a,0x74,0x20,0x4f,0x89,0x17,0xd6,0x57,0xf8,0xf7,0xe0, + 0x2b,0xe7,0xa9,0x70,0x41,0x67,0x12,0x55,0xc9,0xc8,0x21,0x30,0x39,0x15,0x4d,0xc0, + 0x99,0x64,0xc5,0xf4,0xdc,0x29,0x96,0x49,0x5e,0x39,0xc9,0x45,0x52,0x5e,0xd1,0x3c, + 0xca,0xc4,0x59,0x46,0x67,0x49,0xe0,0xbc,0x04,0x77,0x72,0xd2,0xdc,0x19,0x95,0x45, + 0x51,0x3b,0x3d,0xbc,0x35,0x25,0x30,0x2e,0x12,0xb7,0xda,0x71,0x1c,0x7a,0x02,0x5e, + 0x95,0xc4,0x0e,0xb3,0x31,0x7a,0x29,0x4b,0x68,0x02,0x0e,0xf8,0x58,0x85,0x21,0x2a, + 0x35,0xc8,0x45,0x94,0xad,0x92,0x6a,0xe4,0x9c,0x27,0xc9,0x92,0x87,0xae,0x2f,0x0b, + 0x74,0x5b,0xd5,0xc9,0xd2,0xf3,0xd1,0x53,0x1c,0x95,0xe7,0xce,0x2a,0x8f,0x93,0x92, + 0x79,0xfe,0xfb,0x38,0xaa,0xa3,0x3e,0xb5,0x24,0x10,0xe1,0xd1,0x69,0xe0,0xbc,0x2e, + 0x9c,0xcb,0x64,0xe2,0xcc,0x08,0x4a,0x95,0xcc,0x7f,0x5e,0xd4,0xd5,0x92,0xa6,0x35, + 0x8f,0x2a,0x27,0x47,0x7f,0x58,0x7b,0x52,0x07,0xce,0x57,0x07,0x3b,0x3c,0xe1,0xeb, + 0x69,0x91,0x15,0x25,0x01,0x0d,0xfd,0x8c,0x78,0x8e,0xe3,0x7e,0x7f,0x5a,0x26,0xd1, + 0x62,0x74,0xf7,0xc5,0xfd,0xe7,0x2f,0x9e,0x7f,0x4d,0xd7,0xcb,0x28,0x4f,0x32,0xba, + 0x7e,0xf1,0xe2,0xe9,0x8b,0xc7,0x74,0x9d,0xe6,0xe7,0xa3,0xbb,0xc3,0xc7,0xc3,0x87, + 0xc3,0xfb,0x74,0xb5,0x58,0xd1,0xfa,0x46,0x77,0xbf,0x3e,0x7c,0xf0,0xf4,0xc1,0x90, + 0xae,0x67,0x11,0x8d,0x33,0xba,0xfb,0xcd,0xb3,0x6f,0xbe,0xfe,0xfa,0x21,0x5d,0x97, + 0xab,0x2c,0x19,0xdd,0x7d,0xf6,0xec,0xd9,0x83,0xa7,0x5f,0xab,0xcb,0x7e,0x55,0xcc, + 0xa8,0xc9,0xf3,0x87,0xcf,0x87,0xcf,0xd0,0xc5,0xd9,0xbc,0xa8,0x70,0x7d,0xef,0xd9, + 0xd3,0xa7,0xcf,0xe9,0x3a,0x43,0x87,0x2f,0x5e,0x3c,0x78,0x7a,0x88,0xab,0x49,0x44, + 0x57,0x4f,0x1e,0xde,0x7b,0x3a,0x7c,0x4a,0x57,0x05,0x8d,0x7d,0xf8,0xe4,0x9b,0xc7, + 0xf7,0x9f,0xd0,0xc5,0x65,0x54,0xe6,0xa3,0xbb,0x0f,0x1f,0x7c,0x3d,0x18,0x3c,0x1f, + 0xef,0xf4,0xfb,0x55,0x94,0x57,0xa3,0xea,0x8a,0xa0,0xb6,0xe8,0xaf,0x52,0xbf,0x1f, + 0x2d,0x97,0x18,0x8d,0x6f,0xf8,0xee,0x49,0x72,0x56,0x24,0xce,0x8f,0xdf,0xbb,0xfe, + 0xdb,0x62,0x52,0xd4,0x85,0x8f,0xe6,0xfd,0x2a,0x29,0xd3,0x19,0xd6,0x51,0xe4,0xc5, + 0x68,0x95,0xf2,0xbf,0xd5,0x32,0x9a,0x26,0xbe,0xfb,0x34,0x22,0x74,0x8a,0xd3,0xc8, + 0x79,0x45,0xf7,0x5c,0xea,0xe0,0x85,0xfa,0xf5,0x2a,0xc9,0xb3,0xc2,0x17,0x84,0x89, + 0x2a,0xdf,0x95,0xfe,0xd4,0x43,0xd3,0xc1,0x7a,0xa7,0x73,0xa7,0xda,0x40,0xc7,0xad, + 0x06,0xe6,0x83,0xa7,0x83,0x27,0x83,0x6f,0x1b,0x98,0x0f,0xef,0x0d,0x87,0xc3,0x81, + 0x86,0xf9,0xf3,0x67,0xcf,0xbf,0x79,0xf6,0xa4,0x81,0xf9,0xe3,0x7b,0xdf,0x3e,0x79, + 0xf8,0xd8,0x82,0xf9,0xe3,0x6f,0xee,0x7d,0x7d,0xdf,0xc0,0xfc,0xf0,0xc9,0xe1,0x37, + 0x87,0xc3,0x36,0xcc,0x87,0x2f,0x86,0x4f,0x87,0x0f,0x1b,0x98,0x1f,0x3e,0x3e,0xfc, + 0xfa,0x70,0xa0,0xa1,0xfc,0xe2,0xc5,0x37,0x8f,0x1f,0x3c,0x50,0x50,0x7e,0xf0,0xcd, + 0x93,0x87,0xdf,0xbc,0x30,0x50,0x7e,0xf6,0xed,0xe3,0xc1,0xbd,0x17,0xeb,0x9d,0xaf, + 0xae,0x27,0xc5,0xc7,0x7e,0x95,0xfe,0x9e,0xe6,0x67,0xa3,0x49,0x51,0x12,0x46,0xf6, + 0xe9,0xce,0xfa,0xfd,0x3c,0x8d,0xe3,0x24,0x3f,0xbd,0x26,0xf1,0xb9,0xcc,0x48,0x38, + 0xe7,0x45,0x9e,0xdc,0x49,0x17,0xa0,0xdd,0x28,0xaf,0xd7,0x3b,0x93,0x22,0xbe,0xba, + 0x5e,0x44,0xe5,0x59,0x9a,0x8f,0x06,0xe3,0x49,0x34,0x3d,0x3f,0x2b,0x0b,0xc2,0xe8, + 0x11,0x71,0xd3,0x9e,0x82,0x80,0x37,0x66,0xe0,0xa8,0x5b,0xb4,0x68,0x6f,0x0c,0x7c, + 0x1e,0x0d,0x1f,0x2c,0x3f,0x1e,0x0c,0x83,0x07,0x8e,0x3c,0xc0,0xc6,0x79,0xe3,0x3e, + 0xa1,0xfb,0x79,0x5a,0xf7,0xd1,0xa2,0x5f,0x2d,0x08,0x9f,0xe7,0x98,0x13,0x0d,0x46, + 0x9c,0x20,0x25,0x2a,0x8e,0xd7,0x3b,0x91,0x40,0x7b,0x94,0xe6,0x73,0xda,0xe7,0x7a, + 0x3d,0x9a,0x15,0xd3,0x55,0xd5,0xbf,0x48,0xab,0x94,0x44,0xfc,0x35,0x31,0xcf,0x2c, + 0xcd,0x93,0xd1,0xe1,0xf2,0xa3,0x43,0x7b,0x99,0xc6,0x6a,0x00,0xc2,0x40,0x6f,0xac, + 0x1e,0xf6,0x8b,0xd9,0x0c,0x72,0x8d,0xda,0xac,0x77,0x82,0xcb,0x32,0x5a,0xd2,0x2a, + 0x3e,0x0a,0xdf,0x19,0x7d,0x73,0x38,0x58,0x7e,0x1c,0xeb,0x55,0x39,0xd1,0xaa,0x2e, + 0xc6,0xcb,0x28,0x8e,0x31,0x11,0x3c,0x72,0x86,0x5f,0xd3,0x9f,0xfb,0x0f,0xf1,0xee, + 0xfc,0xd0,0x2c,0xdf,0x19,0x38,0x74,0x7f,0x2c,0x33,0x4f,0x7f,0x4f,0x46,0xc3,0x7b, + 0xfa,0xf2,0x32,0x61,0x8d,0x87,0x50,0xba,0x05,0x0b,0xde,0x72,0x8f,0x26,0x30,0x8d, + 0xca,0xf8,0x7a,0x0b,0x7a,0x8c,0x2f,0xde,0x58,0xf6,0x63,0x34,0xdc,0x58,0x0e,0x10, + 0x40,0x3f,0xec,0x97,0x84,0xd2,0x24,0x35,0x87,0x98,0xb8,0x9e,0x2a,0x66,0x49,0x7d, + 0x4f,0xea,0xdc,0x6c,0x5f,0x9a,0xf3,0xe2,0x67,0x59,0xf2,0x71,0x4c,0xe0,0x3c,0xcb, + 0xfb,0x29,0x51,0x51,0x35,0x9a,0x26,0x60,0x23,0xe3,0xdf,0x56,0x55,0x9d,0xce,0xae, + 0xfa,0x8a,0x29,0xeb,0xdb,0x67,0xd1,0x72,0x84,0x95,0x91,0xbc,0xed,0xcf,0x65,0x25, + 0xf7,0xed,0x81,0x06,0x0c,0x90,0x3f,0x32,0xcf,0x87,0x68,0x7e,0xcb,0x72,0xbb,0x91, + 0xe5,0xc1,0x80,0x46,0xb9,0x4f,0x5d,0xdb,0xc8,0x02,0x99,0xd5,0x8f,0x13,0xe2,0xdd, + 0x11,0x64,0x0f,0x63,0xe7,0x78,0xba,0x2a,0x2b,0xea,0x60,0x59,0x30,0x6b,0x1c,0xd7, + 0x25,0x35,0x4d,0xf9,0xb1,0x9a,0x03,0x0f,0xe0,0x04,0xc3,0x07,0x95,0xdf,0xcc,0x41, + 0xae,0x9b,0x47,0xeb,0x9d,0xff,0xb2,0x48,0x88,0x4d,0xf4,0xe6,0x05,0xc9,0x87,0x11, + 0xff,0xf5,0xae,0x01,0x4c,0xf9,0x7d,0x6d,0x77,0x66,0xcd,0x76,0x8d,0x26,0xc1,0xb2, + 0x4c,0x75,0xb3,0xcd,0x55,0x32,0x16,0x76,0xbc,0xcc,0xf7,0xe5,0xc6,0x5d,0xd2,0xd6, + 0xd7,0x33,0x42,0x7d,0x92,0x0b,0x91,0xea,0x67,0x6b,0x1c,0xd9,0xd9,0x91,0xd6,0x6b, + 0xaf,0x0b,0xe2,0x4e,0x29,0xa9,0xdb,0xc1,0xfd,0x07,0x1a,0x04,0x71,0x32,0x8b,0x56, + 0x59,0xdd,0x35,0x1a,0x6f,0x8a,0x74,0x81,0xc9,0x6e,0x4f,0x93,0x21,0x7f,0xcb,0x1a, + 0x5b,0x5b,0x24,0x24,0xae,0xba,0x4a,0xa7,0xd7,0x42,0x42,0x6d,0xfc,0xa0,0xa7,0x8b, + 0xea,0xec,0x5a,0xa8,0xfe,0xd0,0x6c,0x22,0x98,0x6a,0xbb,0x33,0xe6,0x79,0x04,0x43, + 0xb2,0x5f,0x8a,0xf3,0xd6,0x9a,0x8b,0x73,0xb9,0x9d,0x94,0x6d,0x58,0x10,0x8b,0x63, + 0x02,0x9a,0xa7,0xcb,0x4d,0x2c,0x67,0x21,0x6f,0x21,0xe9,0xc3,0x3f,0x86,0xa3,0xdf, + 0x7e,0xab,0xa8,0x77,0x34,0xa4,0xe6,0x07,0x4c,0xf8,0xb7,0xcd,0x5b,0x08,0x79,0x7c, + 0x39,0x27,0x72,0xea,0xb3,0x9c,0x20,0x5c,0x04,0x57,0xa1,0xa9,0xe5,0x45,0x4d,0x2a, + 0x8d,0x99,0x5c,0x7b,0x56,0x0c,0x0d,0xe0,0xf5,0x17,0xcd,0x2c,0x4b,0x88,0xdd,0xdf, + 0xeb,0x62,0x6b,0x5f,0x4e,0x5e,0x16,0x7b,0xc2,0xb0,0x5d,0x24,0xb4,0x56,0x73,0x76, + 0x26,0xd7,0x36,0xf7,0x22,0x12,0xec,0x5c,0xa1,0x60,0xaa,0x59,0xe0,0x97,0x32,0x17, + 0xee,0xa4,0x3f,0x49,0xea,0xcb,0x24,0xc9,0x99,0xc7,0x30,0x17,0x11,0x6e,0xda,0x87, + 0x61,0xc2,0x48,0xa4,0x56,0x86,0xeb,0x6e,0xd8,0x7c,0x31,0x56,0xed,0x68,0x92,0xfa, + 0x52,0x76,0xb8,0xc9,0xef,0x3a,0x76,0xbc,0x13,0x7c,0x3b,0x30,0xd4,0x3f,0x05,0x11, + 0x68,0xa3,0x18,0x79,0x8c,0x27,0x7d,0x40,0x71,0x84,0x3f,0x0c,0x04,0xf0,0x38,0xac, + 0x46,0x03,0x82,0x94,0x90,0xba,0x58,0xb0,0xdc,0xa1,0x8e,0x87,0xd7,0x78,0x65,0x34, + 0x34,0xd2,0xc9,0xda,0xcf,0xc3,0xc3,0x0e,0x71,0xc3,0xba,0x69,0xc9,0x3b,0x06,0x7c, + 0xeb,0x07,0x83,0x61,0xb2,0x20,0xac,0xac,0x56,0x93,0x2f,0xa7,0xc7,0x9d,0x2a,0x61, + 0xe5,0xfe,0xda,0xda,0x9c,0x43,0x16,0x7f,0xc0,0x94,0x44,0x4b,0x40,0xba,0x43,0x32, + 0x70,0xb0,0x29,0x01,0x3b,0x25,0x1e,0xb8,0x45,0xb5,0xb8,0xb6,0x40,0x7c,0xef,0xb0, + 0x2d,0x52,0x0e,0xb7,0x64,0xa9,0x7a,0x2d,0x8e,0xf2,0x6d,0x16,0x70,0x1b,0xb7,0x46, + 0xeb,0xdb,0x39,0x36,0xbf,0x4a,0xdd,0x46,0xd3,0xba,0x6a,0x6f,0xd7,0xc6,0xce,0x74, + 0x60,0x07,0x36,0xeb,0x61,0x1b,0x61,0x87,0xac,0x4e,0xa8,0xa9,0x28,0x01,0x34,0x9a, + 0x16,0x70,0xd5,0xa8,0xd9,0xd0,0x92,0x97,0x85,0x12,0x47,0x65,0x92,0x11,0xd2,0x5c, + 0x24,0x6b,0xf5,0x64,0x34,0x8a,0x66,0x35,0xb3,0x7a,0x21,0x90,0xdd,0xdd,0xb1,0x69, + 0x1b,0x4d,0x08,0xf1,0x09,0x76,0xe3,0x34,0x87,0xda,0xd2,0x07,0x96,0x0c,0x30,0xf3, + 0x2c,0xad,0xea,0x46,0x01,0x33,0xe0,0x1b,0xe3,0x7e,0x9f,0xed,0x20,0x91,0x8a,0x9f, + 0x23,0x23,0xd5,0xd5,0x51,0x96,0x7e,0x96,0x90,0x99,0x54,0x07,0x1b,0xfa,0xc0,0x43, + 0x6b,0xf3,0xa0,0x1d,0x0d,0xc6,0x46,0x8d,0x64,0xfc,0xed,0x1a,0x94,0xd5,0x57,0x3d, + 0xb2,0x13,0xe4,0x0b,0x83,0xdc,0xd4,0xb1,0xc8,0x91,0xc1,0x18,0x5b,0x07,0x43,0x6f, + 0x24,0xaa,0xa8,0x50,0x9c,0xb9,0x99,0x64,0x59,0xba,0xac,0xd2,0xaa,0x9b,0xf5,0xaa, + 0x7e,0xaf,0x9b,0xfe,0x58,0xb3,0xfb,0x72,0xbc,0x0f,0xe2,0xb2,0x58,0x76,0x20,0x46, + 0x9c,0x96,0x42,0x11,0xb4,0xbb,0xd9,0x6a,0x91,0xff,0x41,0x65,0xea,0x7e,0x1b,0x78, + 0xc3,0x96,0xb4,0x3c,0x1c,0xb4,0xa5,0x41,0x1c,0x55,0xf3,0xe4,0xb3,0x4a,0xdf,0x6d, + 0xec,0x9e,0xc1,0xc5,0xb3,0xd3,0x13,0xd8,0xd0,0x8f,0x3a,0x98,0x5a,0x5b,0x44,0x28, + 0x28,0x04,0x9f,0x54,0x7b,0x6e,0x53,0xfd,0xd7,0x30,0xc2,0xff,0xf2,0x1f,0xff,0x4a, + 0xff,0x19,0x3b,0x5d,0x5f,0x93,0xc1,0xca,0x3d,0x77,0x88,0x99,0x2d,0x6d,0x47,0xcd, + 0x81,0xb4,0xf6,0x2e,0x45,0x87,0x98,0x10,0xad,0x91,0xf0,0x21,0x89,0x35,0x6c,0x04, + 0xf5,0x19,0xe3,0xd6,0x3b,0x77,0x67,0x69,0x96,0xb4,0x6c,0x18,0xa5,0x91,0xd8,0x9b, + 0x00,0x7e,0x76,0x97,0x6f,0x36,0x04,0xad,0xef,0x8d,0x92,0xc5,0xb2,0xbe,0xda,0xe8, + 0x81,0x56,0xf6,0x8e,0x0c,0x72,0xdc,0xa1,0x7d,0x73,0x4a,0x42,0x47,0xb6,0xd0,0xa3, + 0x29,0x20,0xed,0x24,0xf1,0x59,0xe2,0xf3,0x0d,0xf8,0x3c,0x60,0xf8,0x4f,0x8a,0x2c, + 0x66,0x3b,0xdd,0x90,0x9a,0xe6,0x21,0x7a,0xef,0x41,0xd6,0xcc,0x57,0xb3,0x34,0x20, + 0x76,0x7b,0xbb,0x45,0x40,0xc6,0xda,0x3c,0x8a,0x69,0x44,0xe6,0x07,0xce,0x3d,0xe6, + 0xbc,0x8d,0x3e,0xa0,0x3a,0x10,0x9a,0x6a,0x4b,0x84,0x6e,0xb2,0x30,0xaa,0x80,0xa0, + 0x4a,0x89,0xd6,0x66,0x85,0x32,0x7f,0xf6,0x6d,0xf0,0x6a,0xd9,0x69,0x53,0x16,0x99, + 0x2c,0xf7,0x72,0x0e,0x07,0x0b,0xbc,0x4c,0x02,0x49,0xb4,0x4c,0x6b,0x6a,0x4d,0x60, + 0x54,0x7e,0x89,0xc9,0x8a,0x78,0x40,0x8e,0xc9,0x08,0x9d,0x90,0xf4,0x9b,0x8d,0xaa, + 0xba,0x84,0x03,0x4b,0x4b,0x32,0x61,0x69,0x2d,0x48,0x68,0x1e,0xd2,0xb6,0x2c,0x45, + 0xc9,0xb7,0x0d,0x40,0x21,0x68,0x7d,0x61,0x2d,0x03,0xfa,0xd2,0x06,0xbe,0xaf,0x9b, + 0xc9,0x28,0xb1,0xb0,0x29,0xc2,0xd9,0x1f,0x03,0xe9,0x0c,0x29,0x79,0xd9,0x26,0x7e, + 0xa3,0xb6,0x0d,0x1b,0x03,0xb1,0x8f,0x0b,0xb4,0x4d,0x32,0xa5,0xfc,0x02,0x95,0xc6, + 0x16,0x5a,0x35,0xc2,0x5a,0x50,0xa3,0xdf,0x81,0xde,0xd5,0x52,0x98,0x1f,0xaf,0xae, + 0xe9,0x06,0x1b,0xf9,0x3e,0x2e,0xa3,0xb3,0x33,0x68,0xf9,0x61,0x5d,0xae,0x92,0xd3, + 0x6b,0xb5,0xa2,0xb3,0x32,0x9a,0xf0,0x46,0xe3,0x79,0x43,0x16,0xf7,0x1e,0xc8,0xee, + 0x0b,0xb5,0x6e,0x20,0xca,0xc0,0x69,0xd8,0x9e,0x20,0x0a,0xed,0xf1,0xdb,0xe2,0xb2, + 0x72,0x08,0x5b,0x68,0x37,0xd3,0xd2,0xb9,0xa4,0x0d,0x26,0x2b,0xd9,0x89,0xd3,0x05, + 0xef,0x2f,0xbd,0x08,0x5b,0xdf,0xb9,0x2a,0x56,0xd8,0xf8,0x59,0x5a,0x2e,0x7c,0x87, + 0x28,0x8e,0x36,0x3e,0xe1,0x77,0x9c,0x32,0x59,0x66,0x57,0x88,0x54,0xc4,0x15,0x6f, + 0x37,0x8d,0x7e,0x46,0xd0,0xb6,0xe7,0xb4,0xd6,0xf7,0x1c,0x16,0xdf,0x6c,0xaa,0xa7, + 0x19,0x9e,0x09,0x5b,0xe7,0x79,0xbc,0x28,0xca,0xfa,0xca,0x79,0x96,0x90,0xce,0x42, + 0xba,0x27,0x6f,0x53,0xe5,0x54,0xf3,0x62,0x95,0xc5,0x3c,0x20,0x7e,0xd6,0x23,0xe7, + 0xc3,0x2a,0xa5,0x95,0xac,0xf2,0x3a,0xcd,0x64,0x78,0x42,0x3d,0xc2,0x39,0xd9,0xe0, + 0xd8,0x89,0xea,0x86,0xc6,0x64,0xb4,0x4d,0xe4,0x69,0xf1,0x30,0xb6,0x0f,0x97,0x51, + 0x49,0xfb,0xd2,0xa5,0xa9,0x48,0x37,0x99,0x32,0xe7,0x9c,0x6e,0xe5,0x83,0x98,0xc9, + 0x45,0x37,0x73,0xc0,0xdb,0x2e,0xd1,0x02,0x1c,0x93,0x3e,0x93,0x45,0x99,0x54,0x64, + 0x93,0x39,0x40,0x30,0x87,0x20,0x26,0x3e,0x49,0x5a,0xb2,0xb8,0x5a,0x09,0xd4,0x57, + 0x60,0x15,0xc4,0x3c,0xa7,0x73,0x87,0xf6,0x2d,0x77,0x22,0x78,0x0a,0xb3,0x82,0x40, + 0x87,0xce,0x02,0xe7,0xb1,0xee,0x02,0x60,0x27,0x71,0x27,0x74,0xb9,0x28,0x62,0x12, + 0x0d,0xb4,0x41,0x04,0x17,0xda,0x19,0xe2,0x77,0x34,0x1a,0x20,0x76,0x29,0x7b,0x12, + 0xd4,0x45,0x91,0x55,0x8d,0xf2,0x41,0xb2,0x69,0x7a,0x7e,0x35,0x06,0x93,0x1b,0x8c, + 0x7f,0x27,0x2c,0x8c,0x2d,0xb5,0x92,0x79,0x5f,0xdf,0x66,0x4c,0x0f,0x85,0x1c,0x3f, + 0xc1,0xe4,0x1f,0xcb,0x1c,0xd5,0xdc,0xc0,0xad,0x69,0x62,0x05,0x2f,0x19,0xab,0xc0, + 0x7a,0x65,0x22,0xf3,0xf8,0x8b,0x35,0x2d,0xa3,0x27,0x83,0x4f,0x1e,0x2a,0xc5,0x78, + 0x4d,0x3d,0x38,0xf3,0xc3,0x4d,0x45,0x78,0x8d,0x9e,0x9d,0xc0,0x30,0x6b,0xff,0x6e, + 0x86,0xdf,0x84,0x37,0x3d,0xb9,0xe1,0xed,0xdf,0x9d,0xac,0xb2,0xf3,0xd7,0xed,0x8d, + 0xe2,0x56,0x16,0x27,0xa4,0x7e,0xb8,0xd9,0xb5,0x71,0x7a,0x6c,0x48,0x03,0x3c,0x74, + 0xd8,0xa9,0x62,0xe9,0xaa,0x03,0xfd,0x84,0x90,0xb7,0x31,0xc9,0x6f,0xb7,0xbb,0x09, + 0x5e,0xdf,0x83,0xac,0xb2,0x8c,0xb0,0x39,0x59,0x25,0x23,0x22,0x3f,0x41,0xe4,0x25, + 0x21,0x19,0xa4,0x55,0xc0,0xe8,0x33,0x89,0x4a,0x61,0xbc,0x51,0x8d,0xa7,0xae,0xc0, + 0x72,0x59,0x16,0x67,0x04,0x67,0xb5,0xb1,0x1f,0xb4,0xfa,0xc7,0x1a,0xcd,0xc0,0xf9, + 0x94,0x16,0xd8,0xad,0xf9,0x7c,0x81,0x6e,0xf8,0x81,0x36,0xf0,0x7a,0x03,0x19,0xbe, + 0x5c,0xbf,0xfb,0x40,0x73,0x6f,0x6f,0xba,0xd6,0x21,0xf1,0x30,0xff,0x9b,0xaa,0x7d, + 0xdd,0xe6,0xc6,0x87,0xea,0xfa,0x33,0x4d,0x8d,0xca,0xf7,0xa1,0x0a,0x56,0xcb,0x6d, + 0x7f,0x0b,0xee,0x93,0xc0,0x2a,0xaf,0x5a,0x8f,0xe0,0x46,0x95,0x67,0x5d,0xfe,0x0a, + 0xba,0x4d,0xdd,0x66,0x7e,0xf0,0xe1,0x16,0xb7,0xc5,0x07,0xda,0xe0,0x6b,0x25,0x29, + 0x0e,0xdb,0xc8,0xf6,0x75,0x97,0x52,0xc7,0x0e,0x5e,0xfd,0xa2,0x93,0x6e,0x38,0x15, + 0xb4,0xc8,0x19,0x0c,0xfe,0x7e,0xdc,0xe9,0x77,0xc2,0x8b,0x98,0x89,0x6d,0xc6,0xd1, + 0x30,0x1a,0xac,0x42,0x85,0x51,0x7e,0x45,0x9c,0xbd,0x54,0x9e,0x6e,0x76,0xc5,0xcc, + 0x66,0xcc,0xae,0xee,0x02,0x9d,0xfc,0xad,0xbb,0xb3,0xaa,0x61,0xf4,0x0f,0x84,0xf3, + 0x15,0xab,0xe9,0x7c,0x84,0xf0,0x04,0x44,0x13,0xb3,0x39,0x56,0xf4,0x88,0xc1,0x45, + 0xce,0x72,0x4e,0x98,0xe8,0x13,0xb2,0x38,0x51,0x59,0x42,0xf2,0x40,0xc0,0x90,0xc1, + 0xec,0x83,0xef,0x09,0x3f,0x2f,0x58,0xf2,0x80,0xef,0x69,0xb6,0x36,0x4f,0x97,0x4e, + 0x96,0x44,0x17,0xc4,0xed,0x54,0x3c,0x26,0x99,0x9e,0x93,0x74,0x73,0xaa,0xe8,0x0a, + 0xea,0x06,0xa8,0x24,0x52,0xad,0xbd,0xc0,0x79,0xcd,0x5d,0x1b,0x19,0x82,0x08,0x0c, + 0x7a,0x33,0xca,0x18,0x61,0x59,0x32,0x56,0xf2,0x0e,0xca,0xb7,0x43,0xb8,0x41,0xd3, + 0x49,0xa2,0xca,0x59,0x24,0x49,0xad,0x67,0x02,0x43,0x86,0x30,0x55,0x08,0xed,0x53, + 0x66,0x9d,0xc5,0xfe,0x6d,0x9f,0x82,0x20,0xf3,0xbd,0xfb,0xb6,0x61,0x6b,0x6b,0xd7, + 0x70,0xd0,0xb2,0xeb,0x2a,0xa0,0x99,0xfb,0xc1,0x8c,0x5e,0xdc,0xd4,0x52,0x6d,0x4d, + 0xf1,0xeb,0x0d,0x4d,0x91,0x5f,0x17,0x31,0xd7,0xb6,0x20,0x2d,0x23,0xb1,0x7f,0xaf, + 0x69,0x04,0xd9,0xd5,0x72,0x87,0x09,0xcf,0xba,0x26,0x50,0xf5,0x0d,0x41,0x1a,0xfb, + 0xb5,0xa1,0xc6,0x6f,0xbf,0xa6,0x07,0xb4,0xd2,0x0d,0x37,0xf9,0x70,0xf0,0xd0,0xb2, + 0x5b,0x18,0x9b,0xee,0x89,0x3f,0xfd,0xac,0x4c,0x1b,0x26,0x8f,0x8b,0x31,0xfe,0xf4, + 0x89,0xa7,0xd3,0x1d,0x22,0x42,0x31,0x9a,0xaa,0xd1,0x3d,0x74,0xe0,0xd0,0x40,0xd4, + 0x69,0x6f,0xe0,0x0f,0x67,0xa5,0x27,0xf6,0x11,0x44,0x8f,0x2d,0x08,0x88,0x55,0x96, + 0xb5,0xea,0xd8,0xe9,0x70,0x46,0x0c,0x68,0x89,0xd3,0x4e,0xe1,0x86,0x55,0x32,0x8b, + 0xfc,0xa8,0x15,0xfd,0x07,0x0f,0x2e,0xe6,0x0d,0x7b,0x81,0x73,0x9f,0xd6,0xfc,0xe8, + 0x40,0x62,0x86,0x8f,0x0e,0x24,0xc9,0x02,0x71,0x8d,0xa3,0x9d,0x9d,0x47,0x0b,0xe2, + 0x0a,0xce,0x34,0x8b,0xaa,0x2a,0xdc,0xc5,0xf2,0x75,0x1a,0x46,0x52,0x1e,0x3d,0x9a, + 0x0f,0x8f,0x9a,0xd0,0x23,0x5d,0x3c,0x22,0xde,0x62,0x1a,0x57,0xab,0xc9,0xae,0x93, + 0xc6,0xe1,0xee,0xac,0xda,0x3d,0xda,0x5b,0xc0,0x88,0x1b,0xd3,0x20,0xd4,0x42,0x8d, + 0x41,0x3d,0x50,0xff,0x77,0xfa,0x7d,0xe7,0x05,0xc2,0xf3,0x4e,0xb9,0xca,0x49,0xb6, + 0xe7,0xa4,0xd7,0xcc,0x8a,0x72,0x81,0x7e,0x63,0xa7,0x28,0xc9,0xfa,0x5b,0x44,0x67, + 0x30,0xff,0x00,0x32,0xc2,0xfb,0x28,0x87,0x26,0x84,0x58,0x23,0x91,0xd5,0x15,0x07, + 0x47,0x7c,0xad,0x47,0x00,0xc5,0x1d,0x22,0x32,0x52,0xc6,0x48,0x45,0x4c,0x84,0x26, + 0x62,0x92,0xa0,0x93,0x84,0x74,0x5b,0xa2,0xb9,0x38,0x21,0xa5,0x7b,0x35,0x85,0xd3, + 0x41,0xd1,0xc6,0x48,0xf4,0x3e,0x12,0x2f,0x9c,0xb9,0x01,0x4a,0x9a,0x16,0x0b,0xe8, + 0x14,0x85,0xf4,0xc6,0x71,0x4b,0x22,0x5d,0xad,0xaa,0x30,0x1d,0xff,0x0e,0x69,0x75, + 0x56,0x50,0xb3,0x29,0x26,0x32,0x49,0x68,0x1a,0x31,0xbd,0x1b,0x38,0xfd,0x3e,0x01, + 0x28,0x4e,0x2f,0x34,0x18,0xc4,0x91,0xa8,0x20,0x81,0x75,0xbe,0x5d,0xe5,0xbb,0x8e, + 0xf0,0x74,0x87,0x41,0x1e,0xee,0x6e,0x3b,0xba,0x08,0xca,0x8e,0xf3,0x68,0x72,0x74, + 0x52,0x93,0x52,0x7e,0x46,0x34,0x9b,0x24,0x71,0xe5,0x28,0xb8,0xd0,0x8a,0x1f,0x1d, + 0x4c,0xb8,0xc5,0xd2,0x1a,0x26,0xd9,0x3d,0x62,0x6b,0x4c,0x76,0x84,0xfe,0x2d,0x6b, + 0x46,0x06,0x80,0x6c,0xaf,0xac,0x3e,0xac,0x8a,0x71,0x4d,0x33,0x85,0x66,0x15,0x13, + 0x6b,0x42,0xd4,0xd7,0x31,0x90,0xf6,0x2d,0x50,0x07,0xd0,0x43,0xe9,0x36,0x60,0x51, + 0xe4,0x53,0xd1,0xd2,0x96,0xda,0x76,0x4d,0x95,0xd8,0xce,0xd1,0x26,0xcb,0xae,0x82, + 0x47,0x07,0x4b,0x9e,0x8b,0xb5,0x68,0x78,0x93,0x76,0x09,0x87,0x18,0xc2,0xfa,0x26, + 0x51,0x28,0x8d,0x90,0x2b,0x50,0x2c,0xea,0x5d,0x07,0xb9,0x3e,0x74,0x9f,0x5b,0xed, + 0x1e,0xa9,0x41,0x2b,0x59,0x32,0xad,0x90,0xef,0xb7,0x91,0x8a,0x54,0x15,0xf3,0xfe, + 0x6b,0x5e,0xb2,0x41,0x29,0x1a,0x9e,0x20,0x2f,0xff,0xb4,0x76,0x00,0x24,0xb3,0xdb, + 0xde,0x94,0x6c,0xca,0x09,0x43,0x42,0x46,0x3c,0xf9,0xf9,0xe1,0xd1,0x8f,0xcb,0xac, + 0x88,0x62,0xc2,0xcd,0x43,0xbe,0x93,0x45,0x93,0x24,0xd3,0x2f,0x60,0xd3,0x65,0x60, + 0xfe,0x75,0xc4,0xa8,0xf1,0x28,0xcd,0x97,0x64,0x26,0xc8,0x2a,0xa0,0xb2,0xe8,0x5d, + 0xc6,0x2f,0x18,0x3a,0xcb,0x3a,0xdc,0xfd,0xea,0xe0,0xab,0x5d,0x67,0x41,0x1a,0x61, + 0xba,0x44,0x44,0x9e,0xdf,0x9b,0x1c,0x3d,0x9d,0x17,0x45,0x95,0x38,0x24,0x45,0x16, + 0xac,0xec,0x54,0x00,0x7f,0xe4,0x04,0xbf,0x13,0x97,0x27,0x49,0x72,0xae,0xf6,0x97, + 0xda,0xf2,0xea,0xb0,0x37,0x40,0x3b,0xc4,0x5b,0x1d,0x88,0x26,0x9f,0x18,0x78,0x3d, + 0x77,0x58,0x47,0x16,0x9e,0xaf,0x76,0xb8,0x72,0x38,0x7d,0x41,0x41,0x05,0xeb,0x38, + 0xe0,0x85,0xf0,0xcf,0x95,0x59,0xcf,0x07,0x99,0xea,0x07,0x8d,0x89,0x04,0xbf,0x55, + 0xb6,0xb9,0x8b,0x06,0xd8,0xf8,0xa1,0x21,0x8c,0xe9,0x6f,0x6e,0xab,0xb4,0x62,0xdd, + 0x60,0x63,0x53,0x75,0xf7,0x6f,0xf1,0xcc,0x81,0x2a,0x90,0xc4,0x66,0x67,0xc1,0x73, + 0xf4,0x16,0xd8,0x1b,0xb7,0xb1,0x35,0xd6,0x8c,0x58,0x85,0xd7,0xd0,0xb7,0xee,0xcf, + 0x69,0x83,0xb1,0x85,0x4a,0xa5,0x4c,0x64,0x17,0xbb,0x31,0x27,0x53,0xab,0x11,0xb4, + 0xe9,0x6c,0xc2,0x5a,0xf2,0x26,0x6a,0x6d,0x8e,0xc9,0x38,0x6e,0x9a,0x9b,0x95,0x0a, + 0xd3,0xe8,0x00,0x92,0x53,0x2d,0xa4,0x39,0x69,0xf4,0x8f,0xb3,0x6c,0x13,0xfb,0x4f, + 0xd8,0x02,0x72,0x68,0xf6,0x0d,0x7c,0x3e,0xdb,0x15,0x46,0xfe,0x39,0x5d,0x6e,0xf6, + 0xf5,0x8c,0x74,0x64,0xe0,0xb2,0xf3,0xf3,0xf7,0x6f,0xbe,0xa8,0xb7,0x86,0x2e,0x95, + 0x0a,0xbf,0xd5,0xa3,0x98,0xa4,0x62,0xa6,0xd9,0x5b,0xf8,0xa9,0x4e,0x97,0x65,0xaa, + 0x56,0x4c,0x6a,0xcb,0x0f,0x65,0x7c,0x0b,0x6e,0x9c,0xd0,0x53,0x87,0x55,0xe9,0x76, + 0xb7,0x0d,0xc2,0x99,0x1f,0x0d,0x02,0x43,0x9e,0xab,0xfd,0xc4,0xaf,0x06,0x7f,0xdb, + 0x1c,0x91,0x5b,0x40,0x91,0x30,0xa3,0xbd,0x2e,0x14,0xc1,0x54,0xce,0x15,0xa9,0x08, + 0xce,0x8a,0xa9,0x9e,0x94,0xb1,0x45,0xc2,0x3c,0xb0,0x4a,0x88,0xd9,0x45,0x4c,0x84, + 0xce,0xac,0x14,0x3d,0x0c,0x12,0x61,0xb1,0xca,0x49,0xbf,0x33,0xcc,0x6e,0x93,0xf1, + 0x46,0x4b,0xbc,0x3c,0xcd,0x48,0xec,0x3a,0x92,0xe3,0x03,0xdb,0x8f,0x7d,0x45,0x24, + 0x15,0x5a,0x68,0x06,0x25,0x67,0xf7,0xc8,0x79,0x06,0xc5,0x50,0x4f,0x85,0xda,0x96, + 0x09,0xc3,0x20,0x50,0x58,0xe7,0xbc,0x21,0x03,0x27,0x21,0xdb,0x91,0x94,0x32,0x07, + 0x19,0x56,0xd0,0xc9,0xa8,0x19,0xa6,0x03,0x0f,0xc4,0x25,0xee,0x83,0x31,0x6f,0x71, + 0xf7,0x05,0x89,0xf6,0x98,0xb9,0x84,0xcc,0x76,0x9b,0xca,0x0c,0xb1,0x49,0x3c,0xe8, + 0xe8,0x51,0xe4,0xcc,0xcb,0x64,0x16,0xee,0x1e,0xec,0x1e,0xa9,0x7c,0xa1,0x47,0x07, + 0x51,0x73,0x7b,0x5e,0xd7,0xcb,0x6a,0x74,0x70,0xb0,0x6c,0xb2,0x8e,0x82,0xcb,0xa2, + 0x24,0xa4,0x17,0x17,0x57,0xb8,0xfb,0xeb,0x24,0x8b,0x72,0xba,0x2e,0x93,0x0c,0x20, + 0x41,0x5e,0x51,0x52,0xee,0x1e,0x6d,0xbe,0xc0,0xbd,0x1e,0xa8,0x51,0x69,0x1a,0x50, + 0x2e,0x4c,0xe6,0xe6,0xd1,0x8e,0xc9,0xb7,0xfa,0xbb,0x5e,0x6a,0x92,0xbc,0xe2,0x62, + 0x4a,0x72,0x3f,0xaf,0x03,0x1a,0xe8,0x79,0x96,0xe0,0xe7,0x93,0xab,0xef,0x63,0x6a, + 0xb1,0x6e,0x5e,0x58,0x9c,0xf7,0x6a,0x7f,0xea,0x7f,0x94,0x24,0xb6,0x24,0x34,0x2f, + 0xc1,0x3e,0xaf,0x13,0xf5,0x1e,0xd2,0x04,0x93,0x80,0xb7,0xe1,0x35,0xb2,0xb0,0xa6, + 0xe3,0x74,0xd6,0xfb,0x78,0x27,0xe4,0x2c,0xc4,0x76,0x86,0xe5,0x47,0x9d,0x21,0x46, + 0x2a,0x28,0xba,0x24,0xde,0x10,0xfe,0x5d,0xcf,0xa5,0x7f,0x5c,0xcf,0xcf,0xd4,0x55, + 0xa6,0x2e,0x09,0x03,0x9f,0x67,0x7c,0x03,0xce,0x0e,0xcf,0x07,0xaf,0xc6,0x25,0xfe, + 0xa5,0x4b,0x30,0x78,0x5c,0xe2,0x5f,0x5c,0x56,0x7c,0x51,0xb9,0x92,0x02,0xe5,0x7c, + 0x90,0x77,0x3f,0xd0,0x35,0xb3,0xd0,0x27,0x75,0x8e,0x6b,0xfe,0x4d,0xf7,0x40,0x91, + 0xb8,0xc6,0xbf,0xea,0x92,0xa8,0x51,0xdf,0xa1,0x9f,0xea,0xe6,0x6b,0x7d,0xeb,0xb5, + 0xee,0x58,0x18,0x0d,0x6e,0xcb,0x2f,0xd5,0x90,0x18,0x86,0x6e,0x4a,0x3f,0x79,0x7e, + 0xa2,0x96,0xc8,0x1c,0xe5,0x37,0x6e,0x8b,0x88,0xe5,0xbb,0xf2,0xd3,0x74,0x2c,0xf4, + 0xcc,0x3d,0xcb,0x4f,0xd7,0x1b,0xef,0xec,0x1c,0x1c,0x38,0x80,0xa4,0xe5,0x98,0xe2, + 0xde,0x1c,0x4e,0x9b,0x35,0xfa,0x43,0xcb,0x57,0xf6,0x5a,0xb2,0x5b,0x9c,0x05,0xd1, + 0x49,0x9c,0x4c,0xc9,0x28,0x81,0x53,0x8d,0x5e,0x2d,0xd1,0x1b,0xd1,0x8c,0xd6,0x36, + 0x08,0xcb,0x61,0xf5,0x4f,0x49,0x0f,0x22,0x2c,0x9f,0x21,0x57,0x0d,0x29,0x7f,0xce, + 0x5f,0xfe,0xf5,0x3f,0x60,0x16,0xd1,0x28,0xc5,0x0c,0xcc,0xb3,0x4d,0xae,0xd4,0x67, + 0xb2,0xec,0x93,0x39,0x72,0x8e,0xee,0x48,0x0c,0x16,0x97,0x3e,0x7c,0x43,0x24,0x35, + 0x69,0x6a,0x09,0xfb,0xfc,0x90,0xf2,0xe6,0x30,0x0f,0x00,0x35,0x91,0xc6,0x98,0x7c, + 0x24,0xd6,0x4e,0x53,0xbc,0x84,0x52,0x18,0xc9,0x1a,0xfa,0x7a,0x1e,0xf0,0xdb,0x91, + 0x82,0xc2,0xb3,0x2b,0xcb,0xf4,0x82,0xfb,0x08,0x04,0x45,0xe0,0x79,0x4a,0x62,0xc6, + 0x26,0x9f,0x08,0x00,0x36,0xc1,0x49,0x39,0xe5,0x6b,0x81,0xce,0x0f,0x50,0x4c,0x35, + 0xc9,0x13,0x3a,0x3b,0x51,0x63,0xc6,0x61,0x21,0x4b,0x45,0xf2,0x48,0x1e,0x86,0x05, + 0xa6,0xe9,0x3c,0x98,0xd0,0x05,0x66,0x07,0x9a,0x27,0x86,0x83,0xbe,0x62,0x70,0x64, + 0x78,0x59,0xf8,0xcd,0xaa,0x50,0x79,0x77,0xcc,0x15,0x32,0xb9,0x54,0xbe,0x30,0x02, + 0x0c,0x2f,0x85,0xfd,0x36,0x11,0xd3,0x4b,0xc5,0xf0,0xac,0x61,0xa4,0x06,0x0d,0x11, + 0x11,0x96,0x3c,0x29,0x3e,0x26,0x55,0xcf,0xd0,0x9e,0x60,0x76,0xf0,0x61,0x95,0x94, + 0x57,0x22,0xa0,0x8a,0x92,0x10,0xa9,0xe7,0xb2,0xe6,0x03,0x87,0xaf,0x4b,0x54,0xd8, + 0xea,0x81,0xe5,0xc3,0x49,0xb6,0x3a,0x43,0x37,0x84,0x2d,0x80,0x4c,0xb1,0xaa,0xc3, + 0xf7,0xa7,0x63,0xba,0xfa,0x7c,0x87,0x23,0x86,0x48,0x42,0x18,0xb5,0x9d,0x59,0x39, + 0xf5,0x90,0xda,0xa4,0x52,0x6a,0x03,0x64,0xa0,0x11,0xb8,0x82,0x8a,0x06,0x43,0xb2, + 0x29,0x75,0xaf,0xb3,0x3a,0x57,0xf5,0x78,0x67,0xcd,0x20,0x6f,0xf9,0x90,0x88,0x25, + 0xb2,0xcf,0x96,0x55,0x5f,0x6d,0x3c,0xe0,0x91,0xcc,0x1b,0xd0,0x14,0xc7,0x2e,0x22, + 0x57,0xa2,0xfa,0x42,0x2e,0x70,0x33,0x74,0x66,0x5a,0x26,0xa4,0x40,0xef,0x36,0x12, + 0x7b,0x97,0x73,0x11,0x89,0x39,0x8b,0x76,0x3d,0x9d,0xf3,0xbe,0xb0,0xf4,0xf8,0x3d, + 0x29,0x0b,0x0b,0xc4,0xab,0x25,0xcd,0x3a,0x79,0x42,0x3b,0xd1,0x40,0xa7,0x2e,0x08, + 0xb3,0xc2,0x06,0xf8,0x41,0x96,0xe4,0x67,0xf5,0x7c,0xac,0x1e,0xe7,0xe1,0x06,0x54, + 0xad,0xe7,0xd8,0xd2,0x40,0x04,0x5b,0x78,0x87,0xfb,0xc1,0x5d,0x22,0x4b,0x88,0x3c, + 0x82,0xa0,0x7e,0x24,0xcf,0x6e,0x6e,0xee,0x28,0x1c,0xd5,0xef,0x12,0xf7,0x68,0x71, + 0x3d,0x77,0x43,0xce,0x3b,0x3d,0x77,0x3f,0xdf,0x77,0x3d,0xd7,0x7e,0xc1,0x24,0x92, + 0xe7,0x61,0x18,0x0e,0xf4,0x13,0x62,0x25,0x1b,0x5d,0x59,0x4a,0xc8,0x56,0x3f,0x68, + 0xbd,0xdd,0x8f,0xb0,0xa9,0x56,0x37,0x3d,0x3c,0xe4,0xd9,0xef,0xed,0xf1,0x3f,0x47, + 0x03,0xef,0xd8,0x7d,0x8a,0x0c,0xed,0xc6,0x69,0xec,0x8e,0xdc,0x66,0x33,0xcc,0x18, + 0xaf,0xdb,0x29,0xf3,0x78,0xf9,0xb8,0x97,0x1f,0xbb,0x9a,0xca,0x94,0x25,0x49,0x7b, + 0xc6,0xb4,0x44,0x28,0x37,0xe2,0x46,0xfb,0xae,0xa2,0x51,0x77,0xbf,0x27,0x7b,0x13, + 0x86,0xc3,0x63,0x97,0x06,0x21,0x96,0xed,0x8d,0x5c,0x97,0x71,0x4b,0xcd,0x95,0x90, + 0x04,0x32,0x3f,0xb4,0xd2,0x9b,0xd5,0xbe,0x4d,0xb0,0x9b,0xd6,0xb6,0xea,0xfd,0xbc, + 0x8c,0xf2,0xfa,0x96,0x2d,0xbd,0x13,0x86,0xfc,0x96,0xbd,0xc3,0x7c,0xdd,0x45,0x09, + 0xd3,0x40,0x51,0x4a,0x88,0x1e,0x05,0xff,0x6d,0xf4,0xa2,0x59,0x0a,0xd7,0x79,0xc6, + 0xa1,0x5a,0xd2,0x4e,0x5e,0xfd,0xe3,0xbb,0x77,0x0a,0x68,0xc4,0x70,0xcb,0xf3,0xca, + 0xf0,0x22,0x56,0x16,0x9c,0xb7,0xc9,0x05,0x8e,0x55,0xe4,0x67,0x74,0x87,0x43,0x1b, + 0x11,0x27,0x8b,0xb3,0xeb,0x44,0x31,0x3b,0xe1,0x74,0x8a,0xb9,0x51,0xfb,0xb0,0x3f, + 0x1c,0x37,0xd8,0x8d,0xc4,0xd4,0xab,0x37,0x8a,0xf3,0xf1,0xca,0x40,0x9b,0x15,0x89, + 0xb5,0x0b,0x1f,0xc0,0xa6,0x76,0x89,0x4f,0x6b,0x99,0x26,0x0c,0x25,0xa4,0x68,0xf3, + 0x95,0xb3,0xb7,0x47,0x74,0x73,0x41,0x70,0x6e,0x7a,0xf6,0x84,0x96,0x35,0xd4,0xe8, + 0xf1,0x53,0x9e,0x5a,0x1c,0xd2,0xcf,0x3b,0x76,0x4b,0x34,0xb1,0x66,0x54,0xca,0x9d, + 0x0b,0x46,0x90,0x3a,0xbc,0x5e,0xe3,0x4a,0x26,0x72,0x73,0xf3,0xfe,0xb4,0x83,0xad, + 0x20,0xb7,0x3d,0xa9,0xdf,0x9f,0xd4,0x25,0x75,0x41,0x57,0x41,0x5d,0xbc,0x2c,0x2e, + 0x93,0xf2,0x29,0xb1,0x97,0x9e,0x77,0x1a,0x0e,0xd7,0x5e,0x43,0x8c,0x1a,0x51,0x35, + 0xb1,0x76,0x6d,0x0d,0x0b,0x49,0x66,0x7c,0xa0,0x3d,0xab,0xef,0x36,0xd3,0xba,0xb9, + 0x71,0xdd,0xcd,0xc1,0xc6,0xfc,0x6e,0xb3,0xb7,0x45,0x2e,0x77,0x08,0x56,0x45,0xee, + 0xe5,0xfb,0xfb,0xb8,0xec,0xda,0x6c,0x6e,0xa2,0x81,0x4c,0xf0,0x99,0x91,0x14,0xd0, + 0x80,0x55,0x90,0xc3,0x95,0x0d,0x08,0xc1,0x32,0x6b,0xba,0x8b,0xb4,0xaa,0x68,0xcc, + 0xed,0x36,0x7d,0x35,0x0b,0x66,0x57,0xa1,0x2b,0x71,0x9d,0xfc,0x9c,0xb1,0x88,0xfa, + 0x65,0xe2,0x6e,0x88,0x26,0xb7,0x09,0x86,0x1e,0xcc,0x60,0xbe,0x2a,0x6e,0xe0,0x9a, + 0xe5,0xc8,0x60,0x44,0xce,0xf5,0x7e,0xe8,0xfa,0xd4,0x87,0xdc,0xa0,0xf6,0xa0,0x4a, + 0x76,0xca,0xa4,0x95,0x84,0x8f,0xc6,0x4a,0xeb,0xb8,0x22,0x25,0x8f,0x08,0xb8,0x38, + 0xa7,0xae,0x93,0xb2,0x74,0x59,0x11,0xe3,0xb5,0xaf,0x15,0xbb,0x7f,0xcb,0xc1,0x17, + 0x62,0xd3,0x29,0x11,0xf6,0x95,0xf2,0x6f,0x4e,0xa3,0x15,0x72,0xdf,0x11,0xc6,0xe6, + 0xb9,0x28,0x59,0x5e,0xf9,0x0e,0xc7,0x3e,0xf8,0x5e,0xad,0x42,0x55,0xb6,0x38,0xe4, + 0xf1,0xce,0xfd,0x24,0xf3,0xae,0xdf,0xbc,0x08,0xd4,0x25,0x6d,0x5a,0x9a,0xcf,0x0a, + 0x97,0x6e,0xdf,0xdc,0x60,0x78,0x19,0xf8,0x85,0xea,0xa3,0x4c,0x48,0xb6,0x55,0xb5, + 0x84,0xa4,0x24,0x97,0x9f,0x24,0x4d,0x45,0x1b,0x5c,0x89,0x5f,0x8a,0x09,0x4b,0xdc, + 0xb5,0x31,0x9c,0x48,0xbd,0x48,0x04,0xb3,0xf0,0x20,0x9f,0x15,0x0b,0xe5,0x96,0xf1, + 0xd8,0x81,0xac,0x63,0x1d,0xa4,0x97,0xa4,0xdc,0x71,0xc1,0x41,0x2d,0x5f,0xc9,0x7c, + 0x89,0xa3,0xb1,0xa4,0x87,0x74,0x9a,0x16,0xa4,0x57,0x19,0x87,0x32,0x7a,0x13,0x75, + 0x87,0xbd,0xc4,0xbb,0x15,0x29,0x66,0x99,0x03,0x4d,0x9c,0xb0,0x71,0x97,0x35,0x07, + 0x74,0xc1,0x13,0x43,0x64,0xd5,0x11,0x89,0x18,0x5d,0x90,0xa1,0x0e,0xde,0x1c,0x38, + 0x27,0xf0,0x3b,0x92,0x11,0x52,0x3b,0xc3,0x0a,0xbd,0xc1,0x69,0x4d,0x76,0x7c,0x85, + 0x34,0x32,0xd1,0xe4,0x94,0x23,0x29,0x43,0x92,0x06,0xdc,0x6d,0x39,0x1f,0x6a,0x42, + 0x94,0xee,0xc7,0xef,0x1d,0x1c,0xe5,0x28,0x45,0xa6,0x46,0x1a,0x1c,0x0a,0x44,0xc2, + 0x4a,0x70,0x8a,0x09,0x47,0x44,0x4a,0xa5,0x26,0x19,0xe8,0xe3,0x41,0x0f,0x0c,0x9c, + 0xe1,0xaf,0x45,0xe5,0x20,0x7c,0x46,0x38,0x2f,0xc7,0x67,0xb0,0xef,0xa4,0x08,0xab, + 0x7d,0xd0,0x14,0x5a,0x6f,0x73,0x63,0xc5,0x0c,0xe4,0xc8,0x0c,0xd9,0x22,0x45,0xd9, + 0xeb,0x35,0xdd,0xf4,0xeb,0x81,0x77,0x30,0x1c,0x0c,0x06,0x5e,0x83,0x66,0xd5,0x31, + 0x46,0x26,0x54,0x04,0x84,0xdc,0xfd,0x6a,0x9f,0x30,0x79,0x24,0xb7,0xfe,0xf2,0xaf, + 0xff,0xdb,0xf5,0x0d,0x0a,0x08,0xf2,0xe1,0x0f,0xc6,0x95,0x39,0xf1,0xf1,0xa1,0xef, + 0xe1,0x22,0xbf,0x88,0xb2,0x9e,0x59,0xa0,0x27,0x72,0x49,0x2f,0x97,0x98,0x80,0x69, + 0x83,0x77,0xfd,0xc3,0x07,0x98,0xc1,0xba,0x0d,0x81,0x67,0x84,0x22,0xb4,0x88,0x5b, + 0xbb,0x6c,0x83,0x6f,0xad,0xb4,0x4c,0x4e,0x52,0x98,0x26,0x4a,0xa3,0xae,0x94,0x26, + 0x0b,0xcf,0xea,0x8a,0x45,0xae,0xf6,0xa5,0x12,0x28,0xce,0x41,0x62,0xe7,0xea,0x64, + 0x88,0x4a,0x7a,0xa4,0xc6,0xca,0xe9,0xa9,0xd0,0x31,0x26,0x5c,0x6a,0xfc,0x9e,0xe2, + 0x6d,0x8d,0x2c,0x2d,0x9b,0x79,0xc1,0x3c,0xc9,0x62,0xa7,0x8c,0xa0,0x65,0xc1,0x9f, + 0x4a,0xf4,0xb3,0x44,0x12,0x08,0x2b,0x3d,0xa0,0x7c,0x46,0x1f,0x76,0xb4,0x2a,0xc1, + 0x0b,0x13,0x40,0xbb,0x1a,0xe1,0xd3,0xe4,0xd0,0x6f,0x65,0xb9,0x6b,0x25,0x6c,0x62, + 0x79,0x6c,0x35,0xbe,0x6a,0xbd,0x98,0x2d,0x0e,0x46,0xbf,0x18,0xae,0x58,0xd0,0xbc, + 0x38,0x62,0xaf,0x8a,0x15,0xa1,0x16,0x4c,0xf8,0x9e,0xb2,0x3b,0x7e,0x2d,0x93,0x33, + 0x22,0x9f,0xf2,0x2a,0x50,0xb4,0x70,0x39,0xbf,0xf2,0x1a,0xd5,0x39,0x15,0xea,0x44, + 0x87,0xdc,0x1f,0x11,0xdd,0x62,0x59,0x57,0x2d,0xe5,0xb8,0x7e,0x25,0xca,0x13,0x0e, + 0x64,0xed,0x38,0x46,0xdd,0xbf,0x73,0x67,0x81,0x9d,0xd5,0x86,0x93,0xd6,0xb9,0x2c, + 0x4d,0x8b,0x13,0x77,0xd8,0xea,0x7c,0x09,0x0e,0x53,0x17,0x67,0x67,0x59,0xd2,0x73, + 0x8b,0xd9,0xcc,0xf5,0xb5,0x46,0xe6,0x49,0x1f,0x44,0x72,0xcd,0x39,0x3d,0xab,0x0b, + 0x65,0x80,0xb5,0x55,0x23,0xdd,0x60,0x6f,0xaf,0xb1,0x36,0xbc,0x63,0x46,0x62,0x57, + 0x79,0x30,0x68,0x5d,0x38,0x18,0x06,0xe8,0x62,0xad,0xe0,0x3b,0x8a,0xf9,0x39,0xa4, + 0xb1,0x56,0x05,0x84,0x76,0x25,0xeb,0x57,0x0c,0xc9,0x65,0x05,0x47,0x38,0x74,0x47, + 0xf7,0xd7,0x42,0x4a,0xcd,0x9d,0xf1,0xa6,0xa5,0xc3,0x67,0x1e,0x71,0xc0,0x6e,0x95, + 0xd3,0x3d,0x1c,0x84,0x52,0xbc,0xf9,0xb1,0x0a,0x81,0xc1,0xd1,0xc9,0x18,0x23,0x41, + 0x7a,0x6c,0xa8,0xc2,0x47,0x9a,0x82,0x41,0xd1,0x4b,0x4e,0x7a,0x48,0x72,0x8e,0x0e, + 0xd3,0x1b,0x13,0x1c,0x8d,0x63,0xfc,0x41,0x5f,0xf0,0x24,0x56,0x1c,0x32,0x06,0xcc, + 0x1c,0x36,0xd4,0xc0,0x6b,0x18,0x65,0xe0,0xe0,0xe6,0xf5,0xed,0x0a,0xe6,0x29,0x25, + 0x7f,0x52,0x16,0xe7,0x49,0xbe,0x0b,0x3d,0x67,0x46,0x02,0x41,0x7b,0xb7,0x19,0xc7, + 0x57,0x4b,0xa8,0xeb,0x79,0x2d,0xb8,0xcd,0x11,0x2e,0x4d,0x06,0x05,0xb3,0x57,0x4e, + 0xf7,0x11,0xf4,0x9d,0xa5,0x1f,0x19,0xd9,0x6c,0xe4,0x10,0x9f,0xf5,0x5b,0x1a,0xf8, + 0xaa,0xa7,0x75,0x1b,0xbd,0xb3,0xca,0x28,0x01,0x7f,0x6d,0x03,0x96,0xc4,0x24,0x7b, + 0x1e,0xae,0xc1,0x72,0x5c,0xe1,0xcd,0x2c,0xf7,0x31,0x61,0x4b,0x7e,0x30,0xda,0x97, + 0x57,0x32,0x7a,0x74,0x86,0x80,0x0c,0xec,0x41,0xc2,0x40,0x38,0x35,0x88,0x1b,0x41, + 0x16,0x9a,0x23,0x6d,0x2c,0xf8,0xd7,0x3b,0xc2,0xc8,0xdc,0x59,0xcb,0xa9,0xae,0x0c, + 0x72,0x4d,0x47,0xca,0x6b,0x97,0x56,0x16,0xc9,0xc3,0x5c,0x64,0x7c,0x80,0x5d,0x6c, + 0xfa,0xb6,0x11,0xbc,0x9a,0x12,0x28,0x32,0xe2,0x46,0xc5,0x7f,0x4b,0x93,0xcb,0xde, + 0x35,0x87,0x5e,0x47,0x6e,0x4e,0x2c,0x8a,0x68,0xd5,0x6d,0x59,0x62,0x3c,0x99,0xb1, + 0x11,0xce,0x30,0xb2,0x11,0x9f,0x13,0xc7,0xb5,0xe8,0x9a,0xbe,0x22,0x61,0x9c,0x3a, + 0x24,0x64,0x67,0xf1,0x02,0x37,0x31,0x6f,0x23,0xfa,0x29,0xd3,0x44,0x27,0xe7,0x50, + 0xd3,0x8f,0x12,0xa8,0x59,0x29,0x55,0x14,0x32,0xfb,0x05,0xb7,0x17,0x8d,0x87,0x9d, + 0x31,0x6f,0x93,0x0f,0x9b,0xe2,0x04,0x78,0x20,0x3b,0xa3,0x1b,0x90,0x48,0x27,0x13, + 0xbc,0xe7,0xb6,0xbc,0x13,0xae,0x7f,0xad,0x4e,0x1f,0x8e,0x86,0x0f,0x48,0x26,0xac, + 0x3b,0xce,0xad,0x6a,0x05,0xa6,0x19,0xda,0x63,0x50,0x1b,0xb1,0x60,0xb4,0x12,0x67, + 0x6b,0x7a,0x72,0x97,0x60,0xf1,0xa7,0x27,0xea,0xd8,0xdb,0xd0,0x79,0xf5,0x64,0x64, + 0x22,0xb7,0x12,0xb1,0x9a,0x91,0xd6,0xfd,0xa7,0x27,0x81,0x11,0x5d,0xe7,0x61,0x1c, + 0xcc,0xca,0x24,0x21,0x31,0x75,0x78,0x5f,0xba,0x98,0x55,0x2d,0x5e,0x10,0x07,0x0a, + 0xa7,0x8e,0x7b,0xe7,0x8f,0xa8,0xd5,0xbd,0xe0,0xc1,0x31,0x8b,0x3b,0x0e,0x80,0xf7, + 0xce,0xa1,0x86,0xd1,0x90,0xe8,0xc4,0x1d,0xd9,0x0f,0xd0,0x65,0x70,0x1f,0x02,0x90, + 0x5a,0xbc,0x52,0x2d,0xc8,0xc8,0xd1,0x08,0x03,0x46,0xad,0xba,0xd6,0x6a,0x58,0xc3, + 0x08,0xcd,0xa8,0xd6,0x6a,0xc9,0xba,0x4f,0xf3,0x3c,0x29,0xbf,0x7b,0xf7,0xea,0x65, + 0xe8,0x36,0xef,0xfc,0x00,0xe7,0xe6,0x33,0xe6,0x0d,0x0c,0x09,0x6f,0xcc,0x70,0x80, + 0x81,0x81,0x18,0xf8,0x08,0xf6,0x38,0x81,0x04,0xfe,0x24,0xf8,0x39,0x4a,0xa0,0x06, + 0xd0,0x1b,0x10,0x39,0x23,0x1a,0xe4,0x7e,0xe2,0x40,0x6f,0x14,0x29,0xd0,0x44,0x1a, + 0xa4,0x7f,0x97,0x40,0x1e,0xa8,0xce,0xdb,0x9a,0xf8,0x52,0xed,0x95,0x00,0x31,0x4b, + 0xc3,0xc5,0x39,0x5c,0x74,0xae,0xbf,0x0c,0x38,0xdb,0x87,0xe8,0x2f,0x46,0xe2,0x33, + 0x89,0x16,0x52,0x2a,0x61,0x45,0xba,0x9e,0xbf,0x08,0x39,0x16,0x4d,0x7b,0x31,0x56, + 0x2f,0x83,0x60,0x4d,0x4f,0x4a,0x97,0xb8,0x94,0xbe,0x10,0x59,0xa1,0x6d,0xaf,0x2e, + 0xe9,0xc5,0x29,0xdf,0x62,0x3f,0x06,0x6e,0xc1,0x35,0x32,0x36,0x2f,0x4d,0x03,0x76, + 0x7d,0xbb,0xda,0xdd,0xe3,0x8e,0xdb,0xa6,0x40,0xb8,0xa0,0x1b,0xc4,0x8d,0x98,0x22, + 0xc2,0x46,0xb3,0xb7,0x7b,0xa0,0xb6,0x8f,0x6b,0xa2,0x06,0xa2,0x5a,0x92,0x1e,0x51, + 0x99,0x46,0x7d,0x33,0x01,0x31,0xea,0xdc,0xfd,0x65,0x00,0xd7,0xb3,0x35,0x70,0x75, + 0x19,0x90,0x55,0x46,0x2c,0xfa,0x29,0x31,0xdb,0x18,0x07,0x91,0xb3,0xb4,0x75,0xa7, + 0xba,0xb4,0x5a,0xab,0x54,0xba,0x26,0xd5,0xae,0xfd,0xa8,0x3d,0x61,0xf3,0xe8,0x92, + 0x0c,0x4c,0x78,0xb3,0x89,0x22,0x4c,0x5f,0xeb,0x04,0xe6,0xc7,0xc6,0x58,0x00,0x10, + 0x9c,0xdb,0x98,0xf1,0x92,0x6c,0x69,0xdd,0xf8,0xf6,0x66,0xb9,0xd9,0xac,0xfd,0x61, + 0xd3,0x9e,0x2d,0xb1,0x05,0xc3,0x5b,0x58,0x18,0x5a,0x2e,0xd0,0xb4,0xb5,0xfa,0x7c, + 0xa1,0xa0,0xae,0x1a,0x8d,0x71,0x03,0x79,0x68,0xa1,0x0b,0xc7,0xbc,0x6b,0xb5,0xeb, + 0x30,0xe3,0xd1,0xa4,0xa7,0x06,0xd7,0x3d,0xaf,0x6f,0x99,0x71,0xbe,0xf8,0xfc,0x5a, + 0x16,0xc7,0x2e,0x27,0x71,0x10,0x72,0x11,0xa6,0xe1,0x27,0xdf,0x53,0x56,0xd3,0x48, + 0xb9,0x25,0x2c,0xa8,0x6c,0xe0,0xdd,0x66,0xc7,0x08,0xe0,0x13,0xc4,0x7d,0x6c,0x93, + 0xb7,0xb5,0xad,0xe6,0xb1,0x10,0x9b,0xb5,0xc7,0xac,0x75,0xb7,0x61,0xd7,0x04,0x81, + 0xe8,0x42,0x5c,0x40,0x84,0xbc,0x93,0x0d,0xe8,0x99,0x1e,0x26,0x5d,0xe0,0x22,0xb5, + 0xad,0xb7,0x50,0x70,0xf2,0x27,0x0d,0xa4,0xb6,0x26,0x3e,0x69,0x70,0xc4,0x80,0x8c, + 0xd9,0x86,0xdd,0xc8,0x60,0xd2,0x5a,0xfd,0x4b,0x4f,0x94,0x0f,0xc5,0xe9,0x70,0x33, + 0x10,0x5f,0x90,0x4b,0x18,0xad,0xbe,0x63,0x2e,0xdf,0x26,0x17,0x37,0x37,0x03,0x5f, + 0xc4,0x90,0xcf,0x22,0x58,0xf5,0xb1,0x69,0x3c,0x13,0xa3,0x9f,0x42,0xc3,0xe8,0x6d, + 0x9a,0x0e,0x16,0x07,0x6f,0x08,0x82,0xf9,0xfd,0xd4,0xa4,0x65,0xb2,0xa4,0xd2,0x16, + 0x24,0x4b,0x57,0x76,0xde,0xd3,0x04,0x94,0x00,0xb5,0x0c,0xd5,0x4d,0x81,0xe2,0x5d, + 0xb7,0x24,0x16,0xe7,0x79,0xf4,0xda,0xee,0x4c,0xd5,0x40,0x8b,0xd1,0x37,0x05,0xd4, + 0x37,0x6e,0xa8,0xa4,0x69,0xe3,0x6a,0xe7,0x09,0x48,0x80,0xdf,0xcc,0x27,0xad,0x2c, + 0x81,0x5e,0xa9,0x86,0x51,0x75,0x2e,0x02,0x94,0xfb,0x69,0xc4,0x93,0xa5,0xd2,0xf0, + 0x44,0x94,0x2e,0xa3,0x5a,0x59,0x4e,0x19,0xfd,0x9e,0x06,0x0a,0x2c,0x64,0xa9,0x51, + 0x50,0xe4,0x27,0xea,0x90,0xbd,0xdc,0xb6,0x8a,0x1a,0xf4,0xee,0x69,0x3b,0x8b,0xee, + 0x73,0x99,0x81,0xb6,0xf8,0x3d,0x50,0xfb,0xe6,0xc2,0x1a,0x1a,0xf8,0x5d,0x52,0x37, + 0xf6,0xba,0xb6,0xdf,0x78,0x2c,0xb0,0xf9,0xa5,0xda,0x75,0xb3,0xdd,0x6b,0xff,0x9a, + 0x57,0x3d,0x12,0xdd,0xc8,0xb3,0x9d,0xc3,0x64,0x03,0xf6,0x75,0x02,0x3b,0xbb,0x33, + 0x90,0x14,0x81,0x4c,0x7a,0x95,0xed,0x8d,0x7c,0x73,0x18,0xe9,0xe7,0x79,0x31,0x71, + 0xb0,0x78,0xa8,0x88,0x85,0x58,0x39,0xd1,0xc4,0x63,0x2f,0xaf,0x58,0x36,0x09,0xd9, + 0xe5,0xa4,0x02,0xc5,0x4a,0x3f,0x91,0x10,0x3d,0xf2,0x88,0xcb,0xa4,0x8f,0x2d,0xd1, + 0x5a,0x9d,0xb8,0x19,0xd8,0x8f,0x96,0xb2,0x8f,0x47,0x02,0x06,0xfc,0xfb,0x71,0x0d, + 0x07,0x93,0xd9,0x04,0x0d,0x4a,0xb8,0xa8,0x68,0x1d,0xf3,0xa8,0x3a,0x41,0x3e,0x6b, + 0xc8,0xd5,0x35,0x96,0xca,0x45,0xc3,0x1c,0xea,0x87,0x19,0xf1,0x19,0x3c,0x73,0xbd, + 0x23,0xf1,0x51,0xb5,0x34,0x94,0xbd,0xbd,0x3b,0x0a,0x8b,0x3c,0xd1,0x84,0xb4,0xb9, + 0x1c,0x85,0x95,0x12,0x81,0xea,0x9d,0x2a,0x50,0x2a,0xf3,0xcd,0xcd,0x9d,0xe8,0xe6, + 0x26,0x22,0x21,0xe9,0xf6,0x5d,0x6b,0xe3,0x59,0x93,0x97,0x40,0x27,0xaf,0xa6,0x20, + 0x9d,0x51,0x8e,0xe2,0xd7,0x88,0x2e,0x20,0x69,0x27,0x97,0xd0,0x2a,0x99,0x13,0x2e, + 0xb4,0x87,0x0b,0x51,0xf2,0x15,0x74,0x61,0x96,0x05,0x32,0x96,0xac,0x79,0x6f,0x2f, + 0x82,0x3f,0x4f,0xfd,0xb6,0x0c,0x73,0x0d,0x92,0x47,0xd0,0xc4,0xac,0x19,0x58,0x60, + 0xd3,0xcb,0x98,0xa7,0x75,0xa3,0x60,0xdd,0x1a,0x64,0x20,0xd9,0xdf,0xa1,0x26,0x10, + 0xaf,0x69,0xbc,0x04,0x24,0x56,0x88,0x5f,0xb5,0x5e,0xed,0xb9,0x01,0x09,0x17,0x6f, + 0xdc,0xf2,0xea,0xd1,0x80,0x04,0xd3,0x3b,0xf9,0x62,0x6f,0x0f,0x52,0xc5,0x52,0xc5, + 0xc2,0x30,0x62,0x68,0xb7,0xc5,0x65,0x18,0xde,0xd1,0x80,0xfe,0xbe,0x7a,0x65,0xe9, + 0x17,0xe2,0xd8,0xc3,0xfc,0x1b,0xf6,0x42,0x2f,0x77,0x98,0x8b,0xc4,0x9c,0xa9,0xa5, + 0xf6,0xff,0x09,0x06,0xeb,0x83,0x25,0x88,0x33,0xf7,0xec,0xe8,0xb3,0xa7,0x1e,0xfd, + 0x67,0xfd,0x87,0xc9,0x9d,0x20,0x0a,0x4e,0x74,0x2a,0xe6,0x54,0x2b,0x30,0xe7,0xb2, + 0xf1,0xd6,0x97,0x19,0xeb,0xd8,0x1a,0x19,0x65,0x95,0x64,0xfd,0x2a,0xc3,0x87,0x6d, + 0xbf,0xca,0x50,0x17,0xd1,0x13,0x59,0x0d,0x3a,0x0f,0x8b,0x11,0x6f,0x99,0x94,0xb0, + 0x70,0x60,0x8e,0xf9,0xc6,0x92,0x17,0x4c,0xa9,0xac,0x38,0xa1,0xd2,0xae,0xd1,0x13, + 0x50,0x95,0x54,0xc6,0x9c,0x9e,0x26,0xb9,0x70,0x6e,0x6d,0x0e,0xcd,0xd3,0xb3,0xb9, + 0x54,0xb4,0x00,0xce,0xd2,0x0c,0x97,0x64,0x0b,0xa4,0x48,0xa5,0x43,0x9e,0x13,0x0f, + 0x2c,0xc4,0x83,0x7e,0x2e,0x50,0x60,0x01,0x96,0x09,0x51,0x5d,0x89,0x70,0x21,0xb3, + 0x62,0xcb,0x26,0x64,0xfd,0x41,0xb4,0x07,0xde,0x90,0x6b,0x6d,0x92,0xc9,0x3a,0x98, + 0x1b,0x14,0x70,0x9d,0xd2,0xc3,0xb6,0x5b,0xc9,0x08,0x0b,0xa9,0x6f,0xb2,0xc1,0x22, + 0x45,0xd7,0x3b,0x16,0x0d,0xd6,0xdd,0x97,0x11,0xae,0xa7,0x84,0xd2,0x09,0x19,0x61, + 0x45,0x1f,0x9a,0x7b,0x42,0x56,0x18,0x63,0xd2,0x76,0x5d,0x14,0x25,0x53,0x9a,0x2a, + 0x27,0x5d,0xed,0x62,0xa3,0x77,0xc0,0x47,0x1f,0xa3,0x8c,0x0a,0x1b,0xa9,0x71,0x90, + 0xa0,0x8e,0xca,0xcd,0x8d,0x5a,0x84,0x32,0xd5,0xda,0x52,0x4e,0x46,0xd0,0xc2,0x5d, + 0x11,0x6b,0xcc,0x8a,0xc1,0xcd,0x0d,0xfe,0x8e,0x0d,0xaf,0x6b,0x3b,0xf3,0x8c,0x64, + 0xcd,0x91,0x06,0x2e,0x3c,0x79,0x04,0xa5,0x96,0x9b,0xfb,0xf0,0xfb,0xda,0xb6,0xd5, + 0x1f,0x26,0x74,0x38,0x12,0x93,0xcc,0x22,0x2d,0xc9,0x95,0x60,0xd2,0xf2,0xd6,0x2d, + 0xed,0x12,0x26,0x49,0xf8,0x89,0xee,0x9d,0x20,0x6f,0xd4,0x7b,0x34,0xee,0x1e,0xcd, + 0xe8,0x40,0x60,0x76,0xc8,0x96,0x25,0xab,0xb9,0xd7,0x8e,0xb9,0xf9,0xc3,0x81,0x47, + 0xdc,0x41,0xeb,0xb8,0xf4,0x50,0x8e,0x62,0xa8,0x94,0x05,0x6b,0xb6,0x51,0x1c,0xcb, + 0x54,0x8d,0xfe,0x64,0x74,0xa4,0x6d,0xdd,0x45,0xc1,0x51,0xf9,0x68,0x39,0x14,0x29, + 0x44,0xd5,0xda,0xab,0x2d,0x46,0xf2,0x58,0x0c,0x2f,0xa7,0xc7,0x69,0xcb,0x58,0x98, + 0x2f,0x71,0xf6,0x26,0x49,0xe7,0x6f,0xca,0x59,0x38,0x3e,0x05,0x8b,0x43,0xf9,0x68, + 0x9e,0x7d,0x7f,0xf2,0xe6,0xe5,0xe3,0x7f,0x92,0x91,0xc6,0x96,0x13,0x04,0x0c,0x03, + 0xf7,0xf4,0x29,0x2c,0x65,0xf6,0x49,0x2e,0x34,0x36,0x8a,0x9d,0xd3,0x73,0x2d,0xb6, + 0x33,0x64,0x04,0xe8,0x4c,0x48,0x96,0xfd,0x3e,0x11,0x3c,0x07,0x02,0x9c,0x5d,0x78, + 0x74,0x20,0x85,0xfa,0xe2,0xa2,0x00,0x6c,0x7e,0x28,0x53,0x9a,0xc2,0xae,0xea,0x1d, + 0x7d,0x4d,0xb3,0x82,0xd8,0x51,0xed,0x54,0x19,0xc9,0x35,0x22,0x54,0xb2,0xcc,0x62, + 0x2f,0xd0,0xa9,0x0f,0xa4,0x5b,0x7b,0x64,0xad,0xf6,0xd5,0x34,0x0c,0xaf,0xb9,0x2c, + 0xd9,0xd9,0x06,0x5e,0xc4,0x9d,0x90,0x08,0xc8,0x8a,0xb3,0xa0,0xfe,0x58,0x2b,0xaf, + 0x94,0xf1,0xb0,0x28,0xcf,0x55,0x68,0x66,0x79,0xe8,0xb7,0x32,0x0f,0xf0,0x7a,0x87, + 0xef,0x32,0xf9,0xb8,0x24,0x3c,0x54,0x6c,0x92,0x7b,0xa8,0x4b,0x9a,0x52,0x86,0x79, + 0x93,0x49,0x7f,0x36,0x17,0x9f,0x55,0x49,0x12,0x3c,0x2a,0xaf,0x94,0xeb,0x55,0xf5, + 0x35,0x97,0xa4,0xc1,0x3f,0xf7,0xdf,0xbc,0xe8,0x23,0xff,0x65,0xd4,0x9a,0x1f,0xb8, + 0x20,0xf2,0xd5,0x93,0x98,0xf8,0x1d,0xc4,0xb8,0xe6,0xdc,0xa2,0xab,0xc4,0x6a,0x97, + 0x44,0x5b,0x29,0x8c,0x35,0xaf,0xc5,0x2e,0xc6,0xf8,0x51,0x99,0xef,0xb2,0x53,0xec, + 0xe8,0xd3,0xde,0x47,0x38,0xfc,0x44,0x27,0x95,0x42,0x42,0xce,0x02,0x21,0x76,0x51, + 0x9e,0x39,0x4a,0x2c,0xca,0xd6,0x59,0x62,0x71,0x50,0xe5,0x49,0x44,0xf0,0x85,0x7f, + 0x86,0xcd,0xa8,0xa4,0x9c,0xdc,0x69,0x1c,0x8f,0xeb,0x96,0x9b,0xd6,0xf2,0x34,0x90, + 0xbc,0x04,0x0d,0x5a,0xb3,0x2d,0x72,0xe3,0xa0,0xdc,0x31,0x89,0x2c,0x26,0x4e,0x8e, + 0x68,0x9b,0xd5,0x19,0xbf,0xa7,0x82,0x67,0xa2,0x0a,0xb7,0xbb,0x17,0x05,0xb3,0x31, + 0x4d,0x98,0xe8,0x64,0xf1,0x53,0x15,0x72,0x13,0xa8,0xd6,0x8e,0x00,0x86,0xd4,0xa2, + 0x4d,0x2e,0xaf,0xc8,0xef,0xdf,0xfe,0xfb,0x5f,0xfe,0xed,0xdf,0x09,0x9b,0x91,0x92, + 0xc1,0xae,0x68,0x1f,0xf1,0xab,0xa7,0x27,0x27,0x1e,0x9c,0x43,0x00,0xbe,0x6f,0x4e, + 0xc8,0x18,0x6f,0x67,0xe0,0xbc,0x52,0x01,0x5c,0xa4,0x7d,0xc0,0x49,0x99,0x9e,0xad, + 0x8a,0x95,0xed,0xb6,0x36,0x76,0xe0,0x6a,0xd9,0x44,0xa6,0x3b,0xad,0xbf,0xc5,0x85, + 0x4b,0x8d,0x8e,0x5d,0x9a,0x0a,0x59,0xa3,0x34,0x1b,0x61,0x34,0x1d,0x56,0xe0,0xe4, + 0x13,0xae,0x08,0xf4,0xc0,0x1a,0xdf,0x6a,0x49,0xbd,0xf0,0x2f,0x84,0xfe,0x74,0x5f, + 0xdd,0xe1,0x72,0xe5,0x54,0x49,0x27,0x21,0xbd,0x4e,0xca,0x0f,0x99,0xc2,0x17,0x29, + 0xad,0x43,0xf1,0xc0,0x93,0x74,0x92,0x41,0x14,0xd0,0x13,0xf8,0xff,0xda,0x77,0x8d, + 0x16,0x75,0x87,0xde,0x27,0xb4,0xa0,0xbf,0xed,0xcc,0x90,0x46,0x6f,0xb4,0x1c,0x54, + 0x55,0x52,0xd6,0x4f,0x98,0x64,0x05,0x36,0xc7,0xf4,0xda,0x08,0xaf,0x62,0x00,0xd5, + 0xb3,0xe2,0xae,0x93,0x80,0x8b,0xeb,0x68,0x11,0xd5,0xc6,0x09,0x13,0xfe,0x51,0x62, + 0x75,0xd2,0xc2,0x1f,0xc1,0x0c,0x0d,0xf6,0xf2,0x53,0x02,0x45,0xea,0x29,0x61,0xc2, + 0xa7,0xca,0xdd,0x4a,0x8a,0x27,0xde,0x4a,0x49,0x9b,0x4f,0x1f,0x95,0x3a,0x25,0x20, + 0xdd,0xdf,0x57,0x20,0x2b,0xdf,0xa7,0xa7,0x5b,0x8a,0x2a,0x6d,0xa2,0xd7,0x44,0x10, + 0x52,0x9d,0x58,0xd1,0xd5,0x9a,0x07,0xe6,0x17,0xde,0x0f,0x4f,0xdb,0xef,0xe8,0xd1, + 0x10,0xdc,0x6f,0x62,0xab,0x2f,0x38,0x4f,0x99,0xf9,0x00,0xa1,0x32,0x3c,0xd0,0x92, + 0x8d,0x44,0xc8,0x08,0xac,0xc4,0x03,0xa6,0xf0,0x2b,0xe7,0xac,0x70,0x54,0xa6,0x62, + 0x6a,0x9f,0xf1,0xb2,0xd0,0x92,0x5e,0x60,0x8f,0x51,0x72,0xd1,0xe4,0xf9,0x29,0x06, + 0xb3,0xb7,0xf7,0xfe,0x54,0xdb,0x34,0x5c,0x43,0x90,0x1a,0xf1,0x8e,0xbe,0xc3,0x39, + 0xc5,0x19,0xe9,0x58,0x40,0xc8,0xca,0x77,0x79,0x3e,0xae,0xf7,0x68,0x60,0x41,0xdc, + 0x76,0x45,0x89,0xb3,0x17,0x62,0xf3,0x39,0xe9,0x7b,0x35,0x64,0x28,0xb2,0x12,0x91, + 0x96,0x17,0x9d,0xb1,0xf9,0xea,0xfa,0xb6,0x03,0x43,0x33,0xb8,0x2c,0x1d,0xb7,0x14, + 0x70,0x96,0xbb,0x78,0xaa,0x95,0x84,0x5b,0x3b,0xa5,0xfd,0x6e,0x75,0x29,0x3e,0x4b, + 0x9b,0x71,0x76,0xe8,0xf7,0x5a,0x09,0x91,0x11,0x5a,0x78,0xfa,0xd7,0x2b,0x36,0x44, + 0xfc,0x7a,0xd6,0x9f,0x99,0x3b,0x37,0xb5,0x0a,0x31,0x5e,0x34,0x26,0xf7,0x1d,0x6b, + 0xb7,0x6e,0x6e,0xcc,0x7a,0x42,0x82,0x54,0x8b,0xba,0x68,0x9f,0x40,0xb9,0xd4,0xf5, + 0x33,0xa9,0xbb,0xd3,0xf3,0x3a,0xd6,0x2b,0x6a,0x0c,0x4f,0xec,0x0b,0xa6,0xc5,0x47, + 0x9e,0x5a,0x40,0xed,0x04,0x9e,0xbd,0xd0,0x5b,0xfa,0x2a,0x96,0x5d,0xcb,0xfb,0xb2, + 0x39,0xb7,0x87,0x19,0xff,0x51,0xc0,0x74,0xb1,0x1d,0xdd,0xda,0x37,0xae,0xae,0x0e, + 0xce,0xc2,0xf2,0x41,0xbb,0xc7,0xbe,0x74,0xdb,0x36,0x26,0xe6,0x75,0x2c,0x11,0x1d, + 0x7f,0xa2,0xdb,0x2e,0x50,0x51,0xaf,0x76,0xa7,0xcd,0xf2,0xba,0x20,0xb8,0x85,0xf3, + 0xf4,0xf6,0x26,0x15,0xf3,0xb1,0x07,0x9d,0x83,0x02,0x15,0x9f,0xe9,0xb9,0xbb,0x19, + 0xe0,0xa0,0x32,0x4c,0xf5,0x89,0x18,0x68,0x5a,0x72,0x16,0x2e,0xad,0x7c,0x95,0x3d, + 0x41,0x3f,0x24,0xde,0xe5,0xeb,0x98,0xb3,0xc7,0x4c,0x8a,0x14,0x08,0x9c,0x17,0xea, + 0x79,0xd5,0x88,0xc3,0x4c,0x12,0x14,0x95,0x94,0x50,0x8e,0x17,0xe3,0x5c,0x85,0x84, + 0xfe,0x80,0x6f,0xec,0x0a,0x4a,0xe3,0x3e,0x0f,0x01,0x4f,0x99,0x72,0x9a,0x05,0x3b, + 0x5a,0x5f,0xb8,0x3d,0xeb,0x8b,0x1d,0x51,0x9f,0x4b,0x76,0xdc,0x60,0xf7,0xdd,0x04, + 0xcd,0x3d,0x49,0xae,0x23,0x92,0xee,0xb6,0x93,0x1d,0xa5,0x81,0x89,0x7f,0x48,0x3c, + 0x45,0x9d,0x39,0x53,0x2a,0xd8,0x5f,0xfe,0xed,0x7f,0x68,0x5b,0x5a,0x6e,0xf4,0xaa, + 0x84,0x79,0x36,0x2d,0x6b,0x42,0xa8,0xe3,0xe9,0xa4,0x09,0x92,0x7a,0xa1,0x7b,0xd7, + 0xb1,0xcb,0x4a,0x96,0xab,0x3c,0x07,0x24,0x54,0x4f,0xa4,0xc7,0x68,0x17,0x63,0xce, + 0x61,0x2a,0xa3,0xb7,0xfe,0x92,0xbb,0xfb,0x32,0x97,0xdf,0x48,0x5d,0xec,0xb9,0x74, + 0xed,0xed,0xe3,0xaf,0x51,0x89,0x2a,0x81,0x29,0xf7,0xd4,0x69,0xe8,0xd2,0x06,0x71, + 0xbe,0x87,0x82,0xaf,0xaf,0x37,0xac,0xb3,0x56,0x29,0xa2,0xd4,0x96,0xda,0xf0,0x71, + 0x5e,0x86,0x68,0xf7,0xe7,0x57,0x2f,0xbf,0xab,0xeb,0xe5,0x5b,0xc9,0x2e,0xd1,0x64, + 0x4c,0x4f,0x03,0x64,0xa3,0xf7,0xdc,0x37,0x3f,0xbe,0xa3,0x81,0xdb,0xd1,0x3e,0xab, + 0x11,0x01,0x57,0xbd,0xfa,0x1d,0x9f,0x4d,0xeb,0xb9,0x46,0x4f,0xa6,0xd7,0x2c,0x45, + 0xf9,0x0b,0x5e,0x7a,0x49,0xd6,0x07,0xbd,0x34,0xb4,0x9b,0x16,0x9c,0xfe,0xb4,0xa5, + 0xf8,0xc8,0x1a,0x94,0x33,0x0b,0x67,0x6b,0xe3,0xf0,0xbf,0x9e,0xfc,0xf0,0x3a,0x60, + 0x6b,0xb1,0x87,0x17,0xcb,0xa4,0x5a,0x16,0xc4,0x39,0xde,0xd1,0x1e,0x79,0x6b,0x31, + 0xf4,0x68,0xfd,0xeb,0xc6,0x40,0x8f,0xf7,0xf6,0x94,0x89,0xde,0x15,0x66,0x6b,0x42, + 0x41,0x8d,0x76,0xca,0xda,0xa8,0x6b,0x5b,0xd4,0x56,0xa5,0x56,0xcc,0xd3,0xff,0x76, + 0x30,0xf0,0xf4,0x10,0x1c,0xca,0xc1,0xdb,0x32,0x12,0xbb,0x00,0x3c,0xf8,0x00,0x60, + 0x13,0x76,0x79,0x00,0xd4,0x7b,0x31,0x27,0xaf,0x28,0x1b,0xd5,0x86,0x04,0xf7,0x10, + 0xfe,0x61,0x6b,0x75,0x2c,0x1d,0xae,0x6d,0xf8,0xe7,0x82,0x24,0x4f,0xb2,0x62,0xd2, + 0x7b,0x0f,0x34,0x3e,0xf5,0x14,0xeb,0xf4,0xaf,0xcf,0x56,0x51,0x19,0x8f,0xa0,0xa4, + 0xaf,0xbd,0x26,0x1d,0x52,0x59,0xba,0x4f,0x9a,0xa4,0xab,0xff,0x64,0x57,0xd9,0xa7, + 0x6c,0xe3,0x1f,0x5e,0x3f,0xd7,0xf9,0x52,0x26,0x4d,0x4d,0xca,0x8d,0x98,0xe4,0xd7, + 0x91,0x9d,0x2a,0x20,0x82,0xaa,0x32,0xf6,0x25,0x52,0xc1,0x60,0x37,0x88,0x05,0x58, + 0x55,0x2a,0x3f,0xa2,0x9a,0x12,0x77,0x95,0x04,0xe7,0x5e,0xe4,0x3c,0x7b,0xfe,0xf2, + 0xf9,0xbb,0xe7,0x7c,0xf0,0x1e,0x74,0xec,0x2c,0x08,0x95,0x91,0x1d,0x21,0xed,0x9c, + 0x17,0x8f,0xdf,0xbd,0x38,0x51,0x2f,0x6a,0x6b,0x92,0x4d,0x70,0xc5,0x20,0x54,0xe4, + 0x5a,0xaa,0xc8,0x72,0x12,0x06,0x98,0x45,0x84,0x53,0xad,0x64,0x1e,0x40,0x13,0xe4, + 0xf4,0x25,0x9a,0xcf,0xac,0xbe,0xf2,0x02,0x7a,0x9d,0xf9,0xb8,0x58,0x6f,0xce,0xee, + 0x57,0xbb,0x3a,0x5d,0x4d,0x1b,0xc6,0x4d,0xd4,0x42,0x2f,0x36,0x4b,0x27,0x25,0x99, + 0xb3,0xad,0xa5,0x72,0xa7,0x95,0x64,0xb8,0x91,0xbd,0xca,0xb5,0x29,0x9a,0xe3,0x74, + 0x9c,0x07,0x3f,0xad,0x57,0xec,0xb9,0x63,0x5b,0xb2,0x3a,0xd7,0xa9,0xff,0x9c,0x1f, + 0xcf,0x84,0x94,0x34,0x29,0xb0,0xca,0xec,0xc7,0x02,0x39,0xb5,0x4d,0x55,0xb8,0x45, + 0xde,0x3c,0x6a,0xe0,0x8a,0xd7,0x51,0xd2,0x24,0x4a,0xb1,0xf5,0x55,0x1a,0xf9,0x72, + 0x89,0xc5,0xa5,0x56,0xbe,0xa2,0x6d,0xc2,0x32,0x82,0xd9,0xb9,0xb0,0x0e,0x1b,0x4a, + 0x46,0x5e,0xd0,0xfc,0x42,0x61,0x9e,0x22,0x09,0x91,0x3a,0xb9,0xb7,0xc7,0x77,0xde, + 0x0f,0x4e,0xe1,0x7c,0xff,0xca,0xfd,0xb2,0xc4,0x79,0xf7,0x13,0xb9,0xa8,0x48,0x0d, + 0xcd,0xb2,0x9b,0x1b,0x19,0x49,0x07,0x0c,0x36,0x52,0xe8,0x8f,0xc2,0x41,0x8b,0x29, + 0x91,0xb8,0x0f,0xa7,0x81,0xf2,0x83,0x88,0xea,0x69,0x85,0x26,0xe9,0xa9,0x47,0xff, + 0xdf,0x54,0xec,0xb8,0xc4,0x89,0x6e,0xb8,0x56,0x3a,0x8c,0x4a,0x7c,0x23,0xd5,0x02, + 0xe0,0xc0,0xae,0xbb,0xce,0xbe,0x00,0x82,0xe0,0xa1,0x59,0xc6,0x66,0x7e,0xb9,0x13, + 0x3a,0x56,0x70,0x89,0xe5,0x41,0xa7,0x43,0x54,0x60,0xec,0xfa,0xd7,0xc8,0xd4,0x2f, + 0xe2,0x91,0xfb,0xe6,0x87,0x13,0x62,0xf5,0x38,0xc0,0x3c,0xda,0x94,0x4b,0x7f,0x2b, + 0xb7,0x68,0x93,0x9b,0x37,0x36,0x77,0x36,0xb2,0xe3,0xad,0x7c,0x0f,0xdb,0x8d,0x6a, + 0xd8,0x31,0x21,0xdb,0xf7,0x9c,0xcb,0x72,0x85,0xd3,0x04,0x2b,0xa2,0x86,0x05,0x1f, + 0x40,0x33,0x14,0x20,0xd9,0x79,0xa8,0xdc,0x91,0xea,0x94,0x3a,0x0b,0xfd,0xd3,0x2a, + 0xb0,0x22,0xac,0xb7,0x2a,0x1a,0x81,0xda,0x90,0x6d,0xdc,0xc0,0xa2,0x3b,0x54,0x5c, + 0xd5,0x7e,0xbd,0x21,0x38,0x1a,0xcf,0xaf,0xe2,0x99,0xb7,0x7b,0x7e,0x37,0xc3,0xbb, + 0x92,0x34,0x1c,0xab,0x11,0x62,0x3b,0x53,0xd8,0xdc,0xdc,0xc8,0x18,0x56,0x77,0x5d, + 0x0b,0x7c,0x71,0x80,0xfc,0x60,0x98,0xe5,0x3a,0x5f,0xd8,0xdc,0x51,0x29,0xc3,0x33, + 0x64,0xb4,0xb8,0xb6,0x23,0xb9,0xf6,0x4d,0x9b,0x63,0x9e,0xe7,0x68,0xdb,0x87,0xac, + 0xc2,0x80,0x8a,0xd7,0x21,0x09,0x2e,0x21,0xb5,0xb3,0x94,0x30,0x96,0x05,0x6f,0xd6, + 0x9c,0x14,0x2b,0x7e,0xcb,0x7c,0xd0,0xd3,0x29,0xb8,0x4d,0x4f,0x9a,0x73,0x71,0x42, + 0x2d,0x97,0x90,0x32,0x69,0x5a,0x56,0xa8,0x96,0xa5,0x9c,0xde,0xbd,0x2e,0xc1,0xab, + 0xdd,0xba,0x82,0x83,0xdd,0x71,0xe9,0x2e,0x1c,0xfc,0x7f,0x8a,0x07,0xb7,0x20,0xf8, + 0x17,0x8a,0x6e,0xb5,0xa2,0xb6,0x50,0x66,0xef,0x97,0xee,0xf7,0x73,0x3a,0xf5,0xc6, + 0x91,0x09,0x65,0x55,0xdc,0xb1,0x99,0xe7,0xc6,0x99,0x81,0x4f,0x1c,0xac,0xa1,0xbd, + 0xe2,0x33,0x24,0xe0,0x45,0x2c,0xb8,0xcc,0x51,0x20,0x1d,0xa6,0xe2,0x73,0xf3,0xa4, + 0xf7,0x28,0x0b,0x21,0x72,0x26,0xe9,0x59,0x23,0x74,0xed,0x8a,0x03,0x7c,0x18,0x4b, + 0x44,0x52,0x84,0xe3,0x8a,0x10,0x9d,0x3a,0xf4,0x3b,0x49,0x24,0xce,0x93,0xc5,0x22, + 0xfe,0x38,0x36,0x57,0xa9,0xf0,0xe7,0x06,0xdf,0x6f,0x9d,0x72,0x39,0xb4,0xcc,0x6f, + 0x55,0x73,0xaa,0xa7,0x4f,0xe7,0xc0,0xa1,0xe2,0xee,0xb7,0xcf,0xaa,0x54,0xc7,0xca, + 0x87,0x2d,0x59,0xb4,0x81,0xeb,0xb5,0x2c,0x50,0x5b,0x0e,0xbd,0x27,0x91,0x72,0x4a, + 0x9b,0xc3,0xf2,0x5a,0x91,0xa2,0xda,0xa0,0xe6,0x95,0xf5,0x4e,0xf7,0xd8,0x5a,0xd5, + 0x97,0x59,0xdb,0xb4,0xbc,0x29,0xc5,0x0c,0x39,0x1b,0x3e,0x22,0xff,0x73,0x9d,0x7e, + 0xdf,0x69,0x5b,0x0c,0x44,0xcd,0x44,0xf5,0xc7,0xad,0x39,0x6f,0x4b,0xce,0x3f,0x3a, + 0xb0,0x52,0xf9,0x9a,0x8f,0x62,0x40,0x78,0x28,0x0b,0x07,0xb6,0x26,0xbf,0xe8,0x43, + 0xab,0xbe,0xb6,0x12,0x16,0xbb,0x24,0x0c,0x54,0x8a,0x63,0x0e,0xe3,0xba,0xfb,0x49, + 0x3e,0x2d,0xe2,0xe4,0xc7,0xb7,0xdf,0x3f,0x2d,0x16,0xa4,0x97,0xe3,0x54,0x29,0x4b, + 0xcf,0x7d,0x77,0x8f,0x8d,0xa8,0x7d,0xa4,0xac,0x7f,0x79,0x38,0xce,0x12,0xa8,0x2a, + 0x7f,0x82,0x96,0x70,0x7f,0x70,0x5f,0x67,0x8d,0x36,0x4e,0x23,0xe5,0x71,0x68,0x3e, + 0x69,0x00,0xbd,0x57,0x7d,0x3f,0x61,0x23,0x15,0x45,0xa0,0xbb,0xef,0x06,0x3c,0x9b, + 0x26,0x4e,0xa5,0x65,0x1c,0xd2,0xeb,0xae,0x9e,0xac,0x66,0x33,0x84,0x31,0x36,0xe6, + 0x34,0x31,0xb2,0x10,0xfd,0xff,0x98,0xe6,0xf5,0x43,0x04,0x85,0xae,0xe8,0xc1,0xda, + 0xca,0xc9,0x11,0x97,0xe0,0x09,0xf8,0x21,0x17,0x26,0xe7,0x93,0x94,0xab,0x72,0x59, + 0xc8,0x57,0x06,0x88,0x90,0xbe,0x7b,0xf7,0xee,0x0d,0x11,0x4c,0x79,0x21,0x41,0x1b, + 0x0e,0xf7,0x16,0x79,0x2e,0xf4,0x13,0x90,0xa9,0x59,0xe2,0xa4,0x7e,0xc6,0xd9,0xbc, + 0x89,0xe8,0x62,0x4b,0xc4,0x33,0x56,0xcb,0xe6,0x2b,0x05,0xa8,0xc4,0x16,0xc9,0x71, + 0x48,0xf6,0x09,0xd3,0x8c,0x58,0x69,0x45,0xc6,0x69,0x12,0x37,0x09,0x1a,0x81,0xf3, + 0x5d,0x92,0xb1,0x62,0xda,0x5b,0xe5,0xcc,0x61,0x20,0x53,0xb5,0x2d,0xcf,0x1a,0x8c, + 0x3a,0xe4,0x30,0x4f,0xcc,0x51,0x14,0x0f,0xec,0xbb,0x71,0x07,0x10,0x95,0x16,0xc1, + 0x8e,0x3e,0x9c,0xf6,0xb7,0xe5,0x46,0xa2,0xfd,0x20,0x23,0x1b,0xf3,0xf9,0x1d,0xa9, + 0x5f,0x2d,0xb5,0xa7,0x75,0x1c,0x6e,0x53,0xe9,0xc1,0x5c,0x5f,0xcc,0x50,0x36,0xa6, + 0xb7,0x9d,0x3c,0x64,0xcc,0x60,0x56,0x7b,0xd5,0x69,0x27,0xb9,0x33,0x9d,0x47,0x69, + 0x1e,0x2a,0xf3,0x19,0x66,0x64,0x91,0x5d,0x18,0x61,0x21,0x13,0xdd,0x3e,0x0a,0x05, + 0x92,0x48,0x0d,0x5a,0x4a,0x17,0xfc,0xb7,0x7b,0x64,0x63,0x57,0x5a,0x6b,0x53,0x87, + 0x26,0x7a,0xe9,0xfe,0x90,0xa8,0xe2,0xa0,0xcd,0x31,0x3a,0x72,0x64,0x2d,0xd4,0xec, + 0x26,0x51,0x97,0xd4,0x7a,0x77,0x73,0xe9,0x74,0xcf,0x9a,0x83,0x40,0x1f,0xf7,0x36, + 0x69,0x43,0x51,0x02,0x2c,0x03,0x75,0xae,0x48,0x89,0x27,0x6b,0x74,0xc9,0xbc,0xaf, + 0xde,0x37,0x6d,0xdd,0xd3,0x90,0xfe,0xda,0x2d,0x3e,0x3d,0x43,0x68,0x8d,0x76,0x8f, + 0x5b,0x69,0x5e,0xbf,0x55,0x9b,0xb3,0xa5,0x3b,0xad,0x51,0xb9,0x8b,0xd3,0xf0,0xb7, + 0xca,0xee,0x65,0x3b,0xd4,0x6b,0x33,0x6a,0xe7,0x93,0x3b,0x63,0x7d,0xd7,0x68,0xc6, + 0xe8,0x73,0x73,0x03,0xcf,0x39,0x19,0x59,0x72,0x89,0x62,0x23,0x27,0x57,0xf9,0xf4, + 0x0e,0x19,0x19,0xfa,0x6d,0xd7,0x33,0x83,0x6f,0x31,0x19,0x6c,0xad,0x32,0xc4,0x1c, + 0xa5,0x55,0x39,0xaa,0x0b,0xb7,0x15,0x3c,0xa7,0x9b,0x4b,0xe8,0x07,0xad,0x51,0x7a, + 0xbc,0x58,0xff,0x3a,0x23,0xb9,0x93,0x8d,0xbe,0x6e,0x87,0xdb,0x27,0x64,0xbe,0x87, + 0x8d,0x1d,0x2f,0x1d,0x9c,0xfa,0xd7,0x98,0xef,0xc8,0x45,0xc2,0x17,0x62,0x92,0x34, + 0xbf,0x03,0xd0,0x4e,0xfb,0xdd,0xe8,0xb6,0xc3,0xfe,0x6e,0xd4,0x4c,0x2b,0x0a,0xb8, + 0x92,0xc1,0x8f,0x6f,0x5f,0xaa,0x46,0xf2,0xb9,0x25,0xba,0xee,0x61,0x6c,0xab,0x9d, + 0xe6,0x0e,0xa1,0x6b,0x15,0x2f,0xe8,0x03,0xbd,0xb1,0x12,0xb7,0xf1,0x6d,0xa8,0x31, + 0x61,0x66,0xb4,0x52,0x17,0x23,0xab,0x33,0xe6,0x21,0x3d,0xeb,0x86,0xd2,0xae,0x9a, + 0x74,0x87,0xce,0x0f,0xe4,0x60,0x9a,0xc4,0xee,0x8a,0x73,0x6b,0x9a,0xb2,0x02,0x6f, + 0xed,0x1f,0x36,0x47,0x95,0x6e,0xb3,0x43,0x5a,0xec,0xa4,0x65,0x87,0x6c,0xa6,0x3d, + 0x1a,0xfa,0xd5,0xcb,0x4e,0xe2,0xff,0x0b,0x11,0xbf,0x95,0x9a,0xb1,0xbe,0x25,0xa7, + 0x52,0xfb,0xed,0x36,0xa7,0xfd,0xa9,0x49,0x6f,0x4f,0x99,0x5d,0x51,0xc9,0xde,0x5e, + 0x12,0x90,0x2e,0x55,0x45,0x67,0x09,0x9c,0x51,0xc0,0xce,0x5b,0x7c,0x51,0xeb,0x2d, + 0x55,0x00,0xc9,0xaa,0x4c,0xb8,0x92,0xad,0x5a,0xe7,0xe6,0x3c,0xea,0xa6,0xbe,0xb3, + 0xab,0x13,0x74,0x76,0xdb,0xea,0x89,0xf6,0x58,0xd2,0xbb,0xfe,0xa7,0xac,0xd3,0xcf, + 0xa9,0x0d,0x8d,0xd5,0x2a,0x5e,0x98,0x3f,0x90,0xb8,0x73,0xab,0x71,0x2a,0xf6,0x5a, + 0x71,0x7e,0xac,0x0c,0x6e,0xb3,0xa7,0xde,0xe8,0x56,0x3b,0xce,0xf3,0xf9,0x8d,0xae, + 0xb3,0x95,0x96,0x95,0x64,0xbc,0x4c,0xfc,0xc9,0x27,0xa9,0x72,0xcb,0x36,0x4f,0x31, + 0x13,0xd7,0x0d,0x0b,0x7d,0x4e,0x35,0xf8,0x97,0xe1,0x83,0xc1,0x82,0x9e,0x46,0x9c, + 0x15,0xd5,0x6d,0xeb,0x7c,0x63,0xd9,0x3a,0xb7,0xa4,0xb0,0xdc,0x6e,0x6d,0x76,0x79, + 0xf8,0x6c,0x07,0x5f,0xab,0xbc,0xa4,0xf3,0xff,0xa3,0x73,0x4f,0x2a,0xbb,0x39,0xd7, + 0xb3,0x11,0x24,0xc9,0x0d,0x27,0x56,0xb0,0x81,0x40,0x86,0x65,0x3d,0x72,0xcf,0x92, + 0xda,0xbd,0x71,0xa1,0x9a,0xd0,0x3f,0xab,0x25,0xfd,0x91,0x92,0x20,0x37,0xd8,0xa4, + 0x1b,0x17,0x20,0x21,0xcd,0x79,0x39,0xad,0x7d,0x87,0xa0,0x22,0x65,0x4a,0xb8,0x4b, + 0x0e,0x3f,0x60,0x80,0x9f,0xa4,0x94,0x86,0xe8,0x63,0xa6,0x68,0x0c,0xd7,0x2a,0xa0, + 0xbd,0x12,0xe5,0x0d,0xe5,0x53,0x59,0x47,0x95,0x82,0x84,0xf0,0xa1,0x55,0x72,0xbc, + 0x88,0x0f,0x90,0xc1,0xa4,0x99,0xb0,0x67,0xac,0xe4,0x53,0x53,0xb1,0xe4,0x90,0x44, + 0x50,0x34,0xab,0x22,0xb7,0x5d,0x77,0x50,0xd4,0x92,0xc0,0xf9,0x49,0x99,0x3e,0x92, + 0x10,0x4b,0x22,0x64,0x46,0xfc,0x76,0x25,0x79,0x78,0x5c,0x92,0x8d,0x4f,0xed,0x15, + 0x52,0x15,0x84,0x33,0x76,0xc4,0xad,0xf7,0xe6,0xc7,0x77,0xa8,0x89,0x21,0x73,0x23, + 0x0d,0x98,0xb3,0x41,0x7c,0xa7,0xf1,0x86,0xcb,0xa1,0x2f,0x63,0x79,0xc9,0xc6,0xca, + 0xcc,0xf0,0x35,0x9e,0xca,0xd9,0xed,0x32,0x43,0x77,0xb5,0xe1,0x7d,0x59,0xc8,0xb9, + 0xae,0x0c,0x67,0x69,0xd5,0xb1,0xa2,0xc0,0x79,0x5c,0x55,0x38,0x79,0x76,0x25,0x69, + 0x86,0x52,0xb9,0x24,0x52,0x19,0xbc,0x4e,0x93,0xca,0x2c,0x67,0x06,0x39,0xed,0xf7, + 0x12,0xda,0x14,0x03,0xdb,0xa4,0xe3,0x8a,0xc6,0xa6,0xcf,0x5e,0x72,0x69,0x3a,0xe4, + 0xfc,0x57,0xfa,0xd8,0x9a,0xaf,0x42,0x27,0x92,0xaf,0x53,0x70,0x76,0x52,0xc0,0x6d, + 0xe5,0x54,0x9b,0x49,0xe8,0x40,0xae,0x87,0x9c,0xe1,0x65,0xbf,0x26,0x6b,0xf5,0x5c, + 0x6c,0x91,0xc3,0xa0,0x02,0x7a,0x93,0xec,0x2d,0x93,0x20,0xb9,0xf3,0x04,0x9d,0x6c, + 0x1e,0x78,0xc2,0x2c,0xf8,0x81,0x4e,0x82,0xe1,0x44,0xd3,0xbd,0xbd,0x3b,0xfa,0x05, + 0xef,0x0b,0x82,0x2c,0xa6,0x73,0x5c,0x6e,0x5b,0xef,0x2a,0x34,0x00,0xbe,0x89,0xae, + 0x4d,0xcf,0xe6,0xbd,0x1e,0x67,0xc7,0x34,0xf3,0xe3,0x54,0x02,0xcc,0xfa,0xe4,0xdd, + 0xe3,0x77,0xcf,0x7f,0x7d,0xf9,0xf8,0xc9,0xf3,0x97,0xe1,0x35,0xbe,0x8a,0xe8,0x32, + 0xf3,0x24,0x95,0x82,0xe3,0x43,0x40,0xfa,0x91,0xab,0x72,0x87,0x90,0x54,0x32,0x72, + 0xcd,0x51,0x3e,0xd7,0xdf,0x30,0x28,0xa1,0x07,0x24,0xe5,0xc8,0xe5,0x8c,0xcc,0x2b, + 0xdd,0x05,0xd3,0xca,0xc8,0xd5,0x89,0xf8,0x7c,0xaf,0x38,0x1f,0xb9,0x7f,0xf9,0x5f, + 0xff,0x2e,0xe7,0x1c,0x7d,0xa0,0x13,0xae,0xff,0xa7,0xe6,0x2e,0x2d,0x11,0x51,0x26, + 0x38,0x9e,0xf5,0x8f,0xa2,0x4e,0x7d,0x78,0x9e,0x99,0x1c,0x21,0x26,0x31,0xcb,0x95, + 0xf0,0xa1,0xe3,0xc4,0x93,0xb4,0xd9,0xd2,0xae,0xd3,0xda,0x52,0xd8,0xad,0xf3,0x48, + 0x38,0x77,0x04,0x6f,0x2b,0xae,0xe3,0xf4,0x82,0x6e,0xa0,0x5e,0xac,0x31,0xca,0x8b, + 0xcb,0xdb,0x8e,0xc7,0x7c,0xa0,0x3f,0x69,0x2d,0x67,0x53,0xbe,0xa0,0x75,0xc5,0x6a, + 0x79,0x1d,0xf0,0xb7,0x10,0x01,0x2f,0xb2,0xb9,0x91,0xa7,0xc3,0xb7,0x3c,0x0d,0x57, + 0xd3,0x80,0x1e,0x91,0x08,0x69,0xce,0x50,0xca,0xcb,0xc4,0x70,0x6e,0x6e,0x06,0xa4, + 0xd9,0xff,0xbd,0xeb,0x8d,0xac,0x7d,0x7c,0xcf,0xef,0x9d,0x7a,0x4d,0xfc,0xdc,0x9e, + 0x06,0xdc,0xc5,0x26,0x6e,0x6e,0x8f,0xd0,0x72,0x39,0x4f,0x6c,0x10,0x4c,0xa2,0x12, + 0x55,0x88,0xe4,0x58,0x15,0x03,0x69,0x3c,0x0b,0xb8,0x62,0x62,0x20,0x9f,0xca,0xdb, + 0x98,0x8d,0xd1,0x80,0x5a,0x03,0xcf,0xb6,0x8e,0xc6,0x4c,0x2c,0xef,0x74,0x7b,0x3a, + 0xcc,0x52,0xf7,0xf6,0xe8,0x9a,0x18,0xaa,0xd7,0x71,0x92,0x47,0xcd,0x8c,0x85,0x90, + 0x6a,0xa5,0x3a,0xfb,0xd0,0x7d,0x80,0x45,0xa8,0x84,0x53,0xe6,0xe5,0xd8,0x24,0xe0, + 0x88,0x72,0x3f,0xbd,0x38,0xa9,0xce,0xeb,0x62,0xe9,0x71,0x69,0xd6,0x26,0x60,0xc3, + 0x65,0x7f,0x32,0xfd,0xfd,0x3d,0x58,0xbc,0x81,0xc9,0xd4,0xff,0xb0,0xe9,0xdb,0xeb, + 0xb9,0x52,0xab,0xd7,0x35,0xf5,0x1f,0xe4,0x7c,0x6e,0x19,0x46,0x2a,0x8b,0xf4,0x35, + 0x29,0x1d,0xd6,0x4f,0xbf,0x08,0xcb,0x40,0x3e,0xfc,0xf5,0xae,0x58,0xf6,0xd1,0xa1, + 0xb9,0x6a,0xd2,0xd1,0x1f,0xe1,0xbe,0x4c,0x97,0xee,0xdf,0xdc,0x14,0xfb,0xfa,0xa5, + 0xef,0xb8,0x80,0xe8,0x51,0xeb,0xf9,0x3e,0xae,0x48,0xd1,0xa5,0x21,0xe4,0xb1,0x36, + 0x21,0x5a,0xad,0xc2,0xa2,0xbf,0xd9,0xee,0xe0,0x90,0xd9,0x41,0xe3,0x9f,0x49,0xf3, + 0xb4,0x9a,0x2b,0xbe,0xc1,0xc7,0x1d,0x0c,0x0b,0x6b,0x42,0x99,0x9c,0x7f,0x7f,0x1e, + 0x0e,0x98,0x7a,0xd5,0x41,0x87,0xdb,0x89,0xcd,0xde,0x5c,0x12,0x9a,0x5e,0x71,0xbe, + 0xbf,0x3f,0xd6,0x3c,0xab,0xbd,0xed,0x1e,0xfe,0xee,0xef,0xaf,0x35,0x28,0xb9,0xff, + 0x10,0x21,0x11,0xe8,0x21,0xf4,0xa2,0xcb,0xfb,0x43,0x34,0x40,0xc3,0xb7,0xbd,0xd5, + 0xa9,0xae,0x3a,0x28,0x11,0x55,0x8e,0x9b,0x62,0x90,0xe6,0x4d,0xd3,0x02,0x7e,0x6b, + 0x1e,0xc8,0x6d,0xab,0x34,0xfc,0x8e,0xae,0xfe,0xb5,0xc1,0x6a,0x50,0x82,0xa8,0xbd, + 0x2c,0xa5,0x0a,0x76,0xe0,0xed,0x4c,0xa5,0x06,0x74,0x2a,0x5a,0x5b,0x3e,0x1b,0x25, + 0xca,0x27,0xb0,0x88,0xa0,0x27,0xa8,0x4f,0x11,0xa9,0x10,0x9a,0xa4,0xc5,0x93,0xe6, + 0xf0,0x53,0x32,0x39,0x11,0x3f,0x0e,0x59,0x74,0xe7,0x49,0x25,0x27,0x57,0x59,0xf2, + 0x92,0x0c,0x8f,0x55,0x2e,0x6d,0xd1,0xe7,0x03,0xf7,0xa6,0x00,0x46,0xef,0x5f,0x86, + 0x40,0xe1,0xe1,0xa1,0x13,0x23,0x4d,0x9e,0x73,0xee,0xb9,0xf8,0xc5,0x3c,0x89,0xb2, + 0x7a,0x7e,0xe5,0x05,0x0e,0xce,0xa6,0x90,0x66,0xc0,0xa2,0x57,0x0e,0x8b,0xe0,0xc3, + 0xa0,0x72,0x34,0x19,0xfb,0x3e,0x32,0xd5,0xed,0xb9,0xd4,0x6a,0xa9,0xb5,0x0f,0x71, + 0xa2,0xb3,0xfe,0xa1,0x95,0x01,0x1c,0x3d,0x26,0x65,0xa5,0x2c,0xf9,0x0c,0x3f,0x1c, + 0x4c,0x8f,0xa5,0x6c,0x81,0x28,0x04,0x3d,0xa5,0xb9,0xae,0x2a,0x89,0x08,0xb2,0xc6, + 0x13,0x37,0x54,0x87,0x92,0xcf,0xd1,0x95,0xa7,0x14,0x90,0xcb,0x42,0xd4,0x0a,0xd6, + 0x11,0xc6,0xa2,0xec,0x64,0xac,0x89,0xa8,0xc0,0x20,0xd0,0x55,0xc2,0x7f,0x5a,0x91, + 0xb0,0x04,0xc7,0x2a,0xff,0xc7,0x9e,0xb8,0x50,0xf8,0x80,0x7b,0x2f,0x7d,0x64,0x8b, + 0x0c,0x6c,0x13,0x5d,0x21,0x1d,0xaf,0xaa,0x61,0x7a,0xb3,0x82,0xb7,0x75,0x57,0x55, + 0x7f,0x4b,0xa5,0xe0,0x0b,0xb0,0xf5,0x28,0xb4,0xbb,0xf1,0xae,0x5b,0xe4,0x62,0x25, + 0xdd,0x8b,0x1a,0x18,0xea,0xfe,0x34,0xd1,0xa4,0x15,0xb2,0x12,0x8c,0x73,0x49,0xa7, + 0x17,0xfe,0x16,0xa6,0xfb,0xc3,0xf1,0x6f,0xad,0x29,0x8e,0x7f,0xdb,0xdf,0xf7,0x34, + 0x37,0xe8,0x49,0x47,0xbf,0x9d,0x2a,0x5c,0x13,0x7d,0xf4,0x66,0xe3,0xae,0x9a,0x2e, + 0xa9,0x17,0x07,0xbf,0x40,0x83,0xfc,0xbb,0x83,0x34,0xa8,0x11,0x36,0x34,0xed,0x44, + 0x46,0xa9,0x59,0x18,0xf3,0x4e,0x50,0x18,0x72,0x60,0x2c,0xcc,0x9c,0x48,0xda,0x48, + 0x5e,0x0e,0x1c,0x7e,0x2e,0xc5,0x03,0xe7,0xca,0xa3,0x4b,0x68,0x8f,0x52,0x66,0x44, + 0x4a,0xad,0x46,0x65,0x3d,0x6a,0x67,0x8f,0x9b,0xfb,0x0e,0x2b,0x93,0xa5,0xfe,0x48, + 0x07,0x50,0x9a,0x36,0xb0,0x96,0xae,0x60,0xba,0x04,0x9c,0xe9,0xc8,0x25,0x1c,0x35, + 0x76,0x89,0x9a,0x46,0x4d,0xcf,0x54,0xba,0x35,0xaa,0x0b,0x48,0x0d,0x63,0xc2,0x0f, + 0xa8,0x73,0xe1,0x90,0x9e,0xa4,0x67,0x67,0xa8,0xf3,0xb2,0x11,0x16,0x70,0x51,0x8e, + 0x26,0x3f,0xcb,0xf8,0x2c,0x4c,0xbf,0x98,0xf5,0xa5,0x37,0x89,0x38,0x05,0xdc,0x52, + 0xcb,0x08,0x56,0xb9,0x51,0x0b,0x8f,0x31,0x59,0x74,0x71,0xd6,0xa1,0xb9,0x36,0x19, + 0xb2,0xcc,0xab,0x94,0x63,0xf3,0x4d,0xdc,0x5a,0xa8,0x14,0xc5,0x64,0x4d,0xd6,0xa5, + 0xf4,0xb6,0xa1,0xde,0x37,0x45,0xc6,0x2d,0x92,0x81,0x76,0xcf,0xab,0x92,0x92,0xc9, + 0x52,0x44,0x86,0xf3,0x36,0xb1,0x6d,0x24,0xa8,0x88,0xd2,0xa5,0x37,0x24,0x1f,0xb4, + 0xeb,0xf8,0xf5,0x94,0xad,0x60,0x12,0xb9,0xab,0x4b,0xfe,0x02,0x4d,0x03,0x30,0x39, + 0xe4,0xa3,0xc6,0xf1,0x02,0x83,0x88,0x6f,0x66,0x55,0xb8,0x85,0x26,0xcd,0xd1,0x5d, + 0x20,0x3b,0xda,0x10,0x32,0x69,0x0d,0x5b,0x07,0x43,0x04,0x5b,0x98,0xdd,0x35,0xf8, + 0xa2,0x1f,0x11,0x1f,0x0d,0xdd,0xb6,0xa9,0xa3,0x2a,0x8d,0x70,0x46,0x14,0x4f,0xb6, + 0xe1,0x6c,0xb4,0xf2,0x24,0x4e,0xd5,0x29,0x3b,0x65,0xac,0x24,0xd9,0x0c,0xaf,0xb8, + 0xda,0xc7,0xa6,0xf1,0xb0,0xdb,0x4b,0x23,0x84,0xbe,0x3f,0xf4,0xd6,0xfe,0x3d,0xd4, + 0x6e,0x69,0x07,0x4f,0x36,0x12,0x8f,0x78,0x45,0xc7,0x6e,0x63,0xf4,0x90,0xf4,0xe8, + 0xc8,0x45,0xfa,0x6c,0x26,0xd2,0x06,0xa0,0xee,0x70,0xbf,0xde,0x67,0x73,0x91,0x84, + 0xee,0x8e,0xdd,0x21,0x0d,0x3b,0x70,0x35,0xe1,0xe8,0x24,0x3b,0x75,0x70,0x62,0xd0, + 0xff,0x76,0xf0,0xf7,0x81,0xb3,0x8b,0xf2,0xf9,0x7c,0xde,0x01,0x1c,0xb2,0x50,0x75, + 0x17,0x6a,0x29,0xf8,0x99,0xca,0x41,0xbf,0x5d,0x23,0xd1,0x76,0x47,0xd2,0x97,0x39, + 0x75,0x31,0xa4,0x97,0xd3,0x6a,0xe3,0xe8,0xc6,0x65,0xae,0xbf,0x9c,0xc2,0x6e,0xbc, + 0x40,0x2d,0x55,0x74,0xcb,0xa0,0xc8,0x75,0x91,0xa3,0x70,0xcb,0x55,0x84,0x45,0x26, + 0x8a,0x31,0xc1,0x77,0xb2,0x62,0x52,0x68,0x05,0xb7,0x14,0x26,0x58,0x65,0x0d,0x92, + 0x40,0x7c,0x59,0x07,0x49,0xc0,0x61,0xb2,0xaf,0xbe,0x1d,0x34,0xda,0xa7,0x7e,0x48, + 0x7c,0x4b,0x3d,0xf6,0x14,0x62,0x41,0x23,0xde,0xdc,0x7b,0x9d,0xe0,0xcd,0x87,0x03, + 0x11,0xe1,0xb1,0x3d,0x14,0x5f,0x82,0x17,0xe6,0x6d,0x88,0xa3,0xb7,0x90,0x46,0x5b, + 0x49,0x60,0x34,0x3c,0x4b,0x11,0xd6,0x68,0xf9,0x17,0xeb,0xb4,0xc3,0xd6,0xf2,0x06, + 0xb6,0xfa,0xcc,0x8d,0x1e,0x85,0x87,0xa4,0xe2,0xc8,0xd4,0x85,0x01,0x8f,0x5b,0xf3, + 0xbe,0xdd,0xc1,0xa8,0x64,0xd4,0x5a,0x14,0x03,0xfb,0xc0,0xd6,0x26,0x89,0x31,0x51, + 0x75,0x86,0x77,0xad,0xb1,0x72,0xfe,0x72,0xb6,0x01,0xd5,0x27,0xd2,0xdd,0xfe,0xaa, + 0x64,0x37,0x3b,0xd5,0x4d,0xcd,0x93,0xd4,0x2d,0xcd,0x08,0x08,0x6b,0xb7,0xa6,0x24, + 0xef,0x69,0x85,0xcf,0xca,0x5e,0xbb,0xee,0x5a,0xa7,0x7a,0xf8,0xa9,0x5e,0xcc,0x1e, + 0x6e,0x2d,0x57,0x72,0xda,0xcc,0xf3,0x86,0x98,0x09,0x1d,0xa1,0x98,0x6d,0x1d,0xd9, + 0x34,0x65,0xda,0xa7,0xe7,0xc4,0x4c,0xe1,0x5c,0xfe,0x4f,0x3d,0xb3,0xb9,0xd3,0x08, + 0x20,0xd4,0x20,0xc9,0x9b,0x33,0x44,0xf0,0xac,0x16,0x33,0x49,0xae,0xda,0x97,0xf2, + 0xe2,0xcc,0x41,0x2f,0x55,0x3a,0x94,0x55,0x08,0xea,0x59,0x32,0xe5,0x4a,0xab,0xc9, + 0x47,0x78,0x64,0x98,0x87,0x54,0x28,0xc6,0xd4,0x3a,0xd5,0x29,0x8a,0x53,0x95,0xa8, + 0x34,0xb5,0xa8,0x24,0xbb,0xff,0x22,0x19,0xa9,0xa2,0x68,0xf4,0x2a,0x73,0x1e,0x3e, + 0xab,0x99,0x94,0x8d,0x0c,0x5e,0x24,0xa8,0x2b,0x56,0x39,0x08,0x19,0x37,0x11,0x42, + 0x5d,0x1e,0x88,0x58,0x3c,0xdd,0x66,0x21,0xdf,0x57,0xd5,0x30,0xf8,0x38,0x29,0x87, + 0x17,0x39,0x10,0xa1,0xca,0xfa,0x9b,0x9a,0xce,0x3a,0xaf,0x43,0x4e,0x30,0x3c,0x7d, + 0xf6,0xda,0x33,0x07,0x48,0xc1,0xea,0x2a,0xf9,0x56,0xc7,0xcb,0xc7,0xaf,0x45,0xe7, + 0xcc,0x59,0x27,0xd4,0x1f,0x1b,0x17,0x29,0x9a,0x36,0xc9,0x1e,0x12,0xc2,0x8c,0x75, + 0xdd,0x44,0xc0,0xcc,0xa4,0x9f,0x09,0x77,0x5d,0x2d,0xc5,0x61,0xf3,0xf3,0xf7,0x6f, + 0x7e,0x7d,0xf5,0xf8,0xcf,0xbf,0x3e,0xf9,0xa7,0x77,0xcf,0x4f,0xc2,0x87,0x5f,0xa1, + 0xa6,0xcb,0x57,0x52,0xd8,0xa5,0x5d,0xae,0x46,0xc7,0xfc,0x94,0xf3,0xa6,0x15,0xca, + 0xd1,0xe1,0xe1,0xce,0xe8,0x9e,0x15,0xbf,0xdd,0xf2,0xeb,0x50,0x43,0x9f,0x74,0xd8, + 0x56,0x5d,0xb4,0xdb,0xe2,0x27,0x52,0x96,0x59,0xbb,0x21,0xc8,0x10,0x29,0xa7,0xa1, + 0x6b,0x45,0xc5,0x25,0xbe,0xf3,0x5b,0xa5,0x8b,0xbc,0x74,0x10,0x7d,0x89,0xbc,0x8b, + 0x75,0xf3,0x7c,0x2b,0xf3,0x93,0xe6,0xd2,0xeb,0x0c,0x64,0x9b,0x43,0x61,0xab,0xdc, + 0x0a,0x3c,0xb9,0x9e,0xee,0xcd,0x4c,0x1a,0x6a,0x58,0xbb,0xa2,0xc9,0xc6,0x19,0xe9, + 0x9f,0x80,0xa7,0x6c,0x40,0x14,0xba,0x80,0xb6,0xec,0x3d,0x1f,0xb1,0x62,0xf5,0x9f, + 0x95,0x3d,0xe0,0x34,0xeb,0x2b,0xc8,0x66,0x84,0xbe,0x23,0x19,0x12,0x97,0xfc,0x3d, + 0xaa,0x91,0x13,0xb1,0x95,0x52,0x16,0x4b,0x5f,0xf6,0xd7,0x57,0x65,0xac,0x82,0xd6, + 0x29,0x3a,0xfd,0x91,0x38,0xa2,0x23,0x92,0x7b,0xed,0x1c,0x6a,0x32,0x42,0x88,0xba, + 0xe4,0x10,0x25,0x23,0xa6,0xf8,0x5a,0xe1,0x26,0x34,0x35,0x3a,0x1b,0x6d,0x2f,0xd0, + 0x87,0x9b,0x23,0x52,0xe2,0x84,0xea,0xf4,0x87,0x90,0xd0,0x88,0x7a,0x87,0xfe,0x82, + 0x96,0x52,0x97,0x6f,0x55,0x4e,0x13,0x5d,0x69,0x4f,0xa5,0x64,0x42,0x59,0x24,0x4d, + 0xb2,0x95,0x36,0x29,0x47,0xf5,0x41,0x1c,0x52,0x0b,0xd1,0xce,0x56,0x54,0x42,0x1d, + 0x7d,0xf6,0x8c,0xd3,0xf0,0xe0,0x9f,0xad,0x79,0xfc,0xc2,0x13,0x31,0x6a,0x5b,0xee, + 0xdd,0xdc,0x40,0x93,0xa3,0x06,0x5b,0x4f,0xbc,0x76,0x09,0x28,0x83,0x96,0xd4,0x1e, + 0xc1,0xdb,0x1b,0x34,0xbf,0x21,0x00,0x78,0xed,0xde,0xfe,0x59,0x41,0xf3,0x17,0x80, + 0xd3,0x7a,0xa4,0x8b,0x5f,0x08,0x42,0xf3,0xdc,0x23,0xe8,0xae,0x35,0x6b,0xeb,0xef, + 0x4f,0x47,0x4c,0x73,0x9a,0x49,0x68,0x2e,0xe2,0x23,0x8b,0xae,0xd2,0xb9,0xb0,0xc8, + 0x80,0x4d,0x54,0xfe,0x2a,0x1f,0x96,0x44,0xf9,0xb9,0x0c,0x4a,0x3c,0x3e,0xe3,0x91, + 0x45,0xcb,0x8a,0xdd,0xb3,0xf8,0xe8,0x16,0xe7,0xe9,0x48,0x01,0xba,0xa2,0x96,0xb7, + 0x49,0xbf,0x8e,0x80,0x0c,0x3d,0x95,0x2a,0xfa,0x2b,0x91,0xf1,0xd3,0x1f,0x4e,0xfe, + 0x7c,0x10,0xfc,0xfa,0x95,0xf3,0xdb,0x2a,0xc7,0xd1,0xfb,0x57,0xd1,0xb4,0xcf,0xe9, + 0xb3,0x98,0x0c,0x94,0x78,0x12,0xd3,0x64,0xe8,0x9e,0x2c,0x33,0xae,0x36,0xe6,0x3c, + 0xf9,0xe1,0xdd,0x77,0xc4,0xfa,0x48,0xe0,0x45,0xb4,0xdb,0xd5,0x48,0xea,0x79,0x5c, + 0x26,0xe5,0x09,0x29,0x9f,0x19,0xed,0x29,0x54,0x1c,0x28,0x42,0xfd,0xc7,0xc2,0x16, + 0xf5,0x39,0x87,0xdd,0x5f,0x90,0x26,0x4b,0x16,0x04,0x0a,0x81,0xc9,0xe4,0xa8,0xbb, + 0x9f,0x98,0x27,0xd8,0xd6,0xa7,0x2c,0x5c,0x8e,0x52,0x60,0xda,0xc8,0xe9,0x32,0xa9, + 0x0b,0x08,0xd5,0x84,0x9c,0xcd,0x11,0x90,0xc2,0x45,0xaa,0xcf,0xd5,0x32,0x91,0x42, + 0x4a,0x72,0xd0,0x4a,0xbf,0xc0,0x75,0x25,0xb4,0x86,0xc9,0x19,0xad,0x6d,0x1f,0xc4, + 0xcc,0xb8,0x20,0x68,0x33,0x69,0xa5,0x66,0x9b,0x66,0xaa,0x4c,0x8e,0xe7,0xd9,0x51, + 0xea,0x4d,0x26,0xc5,0x3d,0xd2,0x58,0xc4,0x46,0xba,0xfa,0xb4,0xb1,0xb0,0xe9,0x90, + 0x8d,0x41,0x0e,0x09,0xad,0x72,0x2b,0xd9,0x00,0x7e,0xdd,0x56,0xad,0x94,0x2f,0x4a, + 0x96,0x90,0x82,0xd4,0x3e,0xc9,0xa0,0x3c,0xbc,0x5e,0xfb,0x92,0x28,0x36,0x68,0x52, + 0x27,0x70,0xea,0x22,0xb4,0xa7,0xc6,0xb9,0x1d,0xd4,0xfa,0xbd,0x4c,0xe8,0x14,0xba, + 0x5f,0x73,0x15,0x0e,0xc7,0xa6,0x2e,0xf5,0xcc,0x5b,0x7f,0x69,0x0e,0x86,0xc0,0x61, + 0xd3,0x6f,0x35,0x6b,0x05,0xf0,0xbb,0x01,0x2c,0x9e,0xad,0x6d,0x38,0x79,0x6a,0x0a, + 0x1b,0x67,0xf0,0x3f,0x95,0xc6,0xa1,0xa9,0xb4,0x9d,0x14,0xb4,0x15,0x5d,0x9c,0xac, + 0x66,0x1b,0x35,0xb0,0x98,0x2f,0x5b,0x71,0x7e,0xbe,0xe6,0x48,0xff,0x66,0xe2,0x10, + 0xbd,0x6a,0xa5,0x46,0x48,0x48,0x3b,0x38,0x4f,0xae,0xaa,0x9e,0xee,0xa3,0x23,0x2d, + 0xd1,0xd4,0x4c,0x68,0xd5,0x2d,0x22,0xca,0x0c,0xf1,0x24,0xa8,0x40,0x51,0xbd,0x83, + 0xf7,0xbf,0xfc,0x72,0x70,0x7a,0xe0,0x05,0xcb,0x62,0xd9,0x6b,0x25,0x74,0x00,0x76, + 0x68,0x7d,0x73,0x83,0xbf,0x01,0xad,0xbd,0x7c,0x5c,0xf7,0xf8,0xa8,0xbd,0x1b,0xb8, + 0x37,0x37,0x77,0x6c,0xe8,0xa1,0x49,0x3b,0x5f,0x4e,0xa5,0x40,0x70,0x8a,0x5d,0xa8, + 0x67,0xf9,0x9e,0x37,0xdb,0x72,0xfd,0x5b,0x83,0x49,0xd6,0x5e,0x4b,0xb6,0x7b,0xad, + 0x38,0x45,0x67,0x3a,0x05,0x3c,0x9c,0x30,0xe5,0x39,0x3a,0x46,0x3a,0x97,0xbb,0xdf, + 0x6b,0x75,0x71,0x74,0x74,0x08,0xdf,0x36,0x0a,0xb3,0x91,0x06,0xb6,0x79,0x6c,0x45, + 0x15,0x58,0xc0,0x96,0xa3,0x5f,0x4e,0x47,0x79,0xdf,0x9e,0xec,0xa9,0x2f,0x6b,0xfb, + 0xab,0x73,0x4a,0x9a,0xca,0xea,0xeb,0x0d,0xf1,0xfa,0x9c,0xa5,0xa4,0x30,0x26,0x55, + 0xe9,0xa3,0x68,0xe2,0x73,0x22,0x78,0xf5,0xd9,0x74,0x56,0xe8,0x38,0xcc,0x88,0xd5, + 0x73,0xb9,0x47,0xd0,0xb2,0xf0,0x64,0x7d,0xfa,0x9d,0x8b,0x32,0x3a,0xfc,0x05,0x36, + 0xce,0x1d,0x83,0x5f,0x6f,0x21,0xe5,0xd4,0xed,0xf2,0x1a,0xe6,0xc4,0x58,0x8b,0xcd, + 0x31,0xa1,0xb6,0xaa,0x31,0x5a,0xfb,0x0a,0xe3,0xcf,0xc4,0xa8,0x98,0x97,0x58,0x5f, + 0x18,0x48,0x1b,0x71,0xcd,0xfe,0x81,0x38,0x56,0x25,0xf5,0x75,0xc5,0x5e,0x48,0x4e, + 0x71,0xba,0x35,0x6e,0x5b,0x8b,0xd0,0x3a,0x39,0xef,0x06,0x1c,0xe5,0x30,0x5b,0x63, + 0xd8,0xb6,0xce,0xc0,0xe9,0x53,0x30,0xb5,0xae,0x0d,0xa2,0x67,0xa6,0x4e,0xe6,0x43, + 0x01,0xf7,0x95,0xfe,0x5d,0x94,0x26,0x50,0xcb,0x1a,0x81,0x9c,0xd9,0xdb,0x9a,0x93, + 0x2a,0xd3,0xa4,0x9c,0xe8,0xea,0x24,0xdd,0x2d,0x89,0x19,0x9d,0x59,0x15,0x5b,0x35, + 0xae,0x68,0x2c,0x4e,0x5f,0x53,0x5e,0x6b,0xc1,0x82,0x76,0xd1,0xa8,0x66,0x34,0xb3, + 0x2b,0x8d,0x27,0xdf,0xd4,0x64,0x6a,0x22,0xd5,0x9b,0x67,0x9d,0xf5,0x9b,0xf6,0x91, + 0x67,0x65,0x9b,0xc2,0x47,0xc9,0xf8,0x7e,0x3d,0x1b,0xe9,0x66,0xef,0xd3,0x53,0x4e, + 0xe3,0xb0,0x6f,0x48,0x15,0xb2,0x4a,0xc5,0x10,0x5d,0x9f,0xec,0xc3,0xd1,0xc0,0x67, + 0xa3,0x79,0x34,0x30,0xb5,0xb5,0x5a,0xce,0x76,0xed,0x18,0x95,0x52,0x8e,0x9e,0x65, + 0x04,0xb2,0xb1,0x3c,0xb0,0x8a,0x46,0xa1,0x96,0xa8,0x54,0xce,0x64,0xd5,0xdf,0x3a, + 0x06,0x22,0x46,0xc3,0x56,0xa1,0x5d,0x53,0xab,0x19,0x1f,0x4b,0x94,0x3c,0x63,0x8e, + 0xef,0x92,0x68,0xaf,0xe1,0x6e,0xe7,0xac,0xff,0xa8,0x6e,0x1f,0x6f,0x51,0x45,0x72, + 0x91,0x5d,0xc1,0x96,0x56,0xaa,0x8b,0x60,0x96,0xac,0xa4,0xd4,0xa4,0x2f,0xf0,0x17, + 0x69,0x75,0xb1,0x08,0xfc,0xe6,0x53,0x2f,0x13,0x8e,0xbe,0x17,0x9c,0xf6,0x28,0x76, + 0x0a,0x5b,0x32,0x33,0x92,0x3e,0x73,0x67,0x52,0x44,0xa5,0xe8,0xd5,0xf8,0x08,0x21, + 0x0a,0x81,0x71,0x71,0x6a,0xf4,0xbe,0xab,0x50,0x6f,0x37,0xd8,0x91,0x4f,0x7e,0xb8, + 0xde,0x2d,0x69,0x90,0xad,0x44,0x19,0xf5,0xd9,0x2e,0x7d,0x1c,0x50,0x11,0xdf,0xf1, + 0x2f,0xf9,0x2f,0x39,0x83,0x4a,0x29,0x30,0x7a,0x02,0x8c,0x85,0x57,0x15,0x6d,0xa5, + 0xe6,0x13,0x2e,0x6f,0xee,0xbe,0xbb,0xf5,0xe1,0x32,0x7c,0x0d,0x4e,0x25,0x66,0x4b, + 0x71,0xd4,0x82,0xe9,0x90,0x0b,0xd9,0xd2,0xbc,0x56,0x71,0x73,0xfa,0xa8,0x55,0x6f, + 0x5b,0x77,0xa8,0x40,0x38,0x5d,0x95,0x88,0x7f,0x21,0x23,0x5f,0x57,0xfb,0xe1,0x34, + 0xff,0xaa,0x2e,0x8b,0x2b,0x7c,0xed,0xec,0x07,0x5d,0xe9,0xde,0x00,0x47,0x3e,0x1d, + 0xa7,0x49,0x51,0xf7,0x67,0x60,0x0f,0xc8,0xb6,0x33,0xac,0x75,0xb5,0xa3,0x6a,0x1e, + 0x2d,0x13,0x47,0x25,0x58,0x58,0x75,0xbd,0xe5,0xf3,0xa6,0x50,0xb2,0xfc,0x56,0x4d, + 0x6f,0xb6,0x46,0x89,0xce,0xc5,0xab,0x1b,0x98,0x54,0xd1,0xe6,0x4b,0x6f,0x6e,0xeb, + 0x80,0x24,0x9f,0x76,0xfd,0xe4,0x69,0x18,0x79,0x73,0xf3,0x34,0xcc,0xdf,0x20,0xab, + 0x68,0x3b,0xd5,0xcc,0xe4,0x19,0x99,0x6a,0xa1,0x00,0xf0,0x95,0xd3,0xd7,0x1f,0x55, + 0x32,0xbe,0xe2,0x9c,0xec,0x15,0x3b,0xf7,0x48,0x55,0xa5,0x35,0xb9,0x47,0x96,0x38, + 0xdb,0x48,0x43,0xea,0x72,0x64,0x6d,0x9d,0x93,0xd8,0xce,0x1d,0xb2,0x26,0x6b,0x97, + 0xc1,0xdd,0x88,0xb9,0x75,0x9f,0x11,0x34,0x9c,0xe1,0xf6,0x1c,0xe0,0x32,0x89,0x0b, + 0xc5,0xbb,0xbe,0x30,0xfa,0x68,0x85,0xe8,0x3c,0xbc,0x2d,0x6c,0x8c,0x2f,0x4d,0xc8, + 0x91,0xef,0x2b,0x61,0x60,0x71,0x52,0xdc,0x6e,0x8e,0x2f,0xda,0xdf,0x95,0x1b,0xb1, + 0x17,0xfb,0x23,0xf1,0x90,0x8a,0x9d,0x06,0xe0,0x15,0x0b,0x8e,0xd0,0x3d,0xce,0x49, + 0x22,0xa4,0xb1,0x5b,0x49,0xfd,0xa5,0xd2,0xe1,0x2f,0x5a,0xf6,0xc1,0xaf,0x48,0x86, + 0xa2,0xcc,0xb4,0x29,0x36,0x6a,0xad,0x0e,0x61,0x4f,0x3c,0xbb,0xe5,0x68,0x76,0xf3, + 0x0c,0x46,0xb8,0x4a,0x09,0x53,0x41,0x6b,0x7c,0x08,0x0f,0x54,0xa5,0xcf,0x6e,0x49, + 0x6d,0xe7,0x55,0xae,0x0a,0xc4,0x32,0x07,0x64,0x68,0x9a,0x2a,0x78,0xfc,0x79,0x3d, + 0x78,0xc8,0x55,0x42,0x90,0xd0,0x9a,0x38,0x96,0x52,0x2e,0x82,0x62,0x3e,0x9d,0x8c, + 0x50,0x4d,0x1e,0xa3,0x12,0x57,0x7e,0xb6,0x22,0xf9,0xdb,0x7c,0x5e,0x25,0x52,0x55, + 0x9d,0xa5,0x74,0x0c,0xd1,0x20,0x8a,0xe7,0xd0,0x12,0xb9,0xe4,0xf6,0xf6,0xd1,0x76, + 0x9e,0x41,0xab,0x9a,0x00,0x58,0x99,0x2e,0x62,0xdb,0xd6,0x1b,0xb0,0x2d,0xef,0x55, + 0x59,0x87,0x1a,0x87,0xec,0x9b,0xf3,0xf6,0xa7,0x1d,0x27,0xb6,0x19,0x37,0xba,0x87, + 0x4d,0xda,0x87,0xe9,0x6f,0x2d,0x39,0x60,0xd5,0x8c,0xde,0x28,0x19,0xce,0x47,0xeb, + 0xe6,0xba,0xc8,0x41,0x33,0x33,0x55,0x1d,0x41,0x8e,0xec,0xff,0xed,0x67,0xb5,0x31, + 0x0b,0x7d,0x34,0xa8,0x35,0x91,0x5b,0x20,0x7d,0x6b,0x11,0x81,0xbf,0xaa,0x0c,0x80, + 0x2e,0x02,0xf0,0xc3,0x56,0xed,0xb8,0xed,0x4f,0xb5,0xe1,0x1b,0x10,0xc4,0x77,0xad, + 0x4c,0xcc,0x72,0x1a,0x3e,0x52,0xe2,0xa2,0xbf,0x2a,0xb3,0xa3,0xc0,0x88,0x73,0xfe, + 0x06,0x14,0xc9,0xf9,0x4a,0x7f,0xf7,0x56,0x7d,0xe6,0xc9,0xe5,0xaa,0xe1,0x97,0x15, + 0xbe,0x61,0x20,0x58,0x89,0x88,0x99,0x7c,0x9a,0x7c,0x52,0xa8,0x48,0x74,0x8f,0x3f, + 0x8f,0xc6,0x45,0xe1,0x9a,0xc1,0xc5,0x6b,0x49,0x5d,0xf3,0x53,0xcf,0x2e,0xe5,0x4d, + 0x94,0xf0,0xfd,0x3b,0xa9,0x58,0xcf,0xf1,0x67,0xe2,0x45,0x52,0xe9,0x8c,0xf9,0xb9, + 0x89,0x49,0xef,0xa8,0xf3,0x64,0xaa,0x00,0x17,0x8f,0x89,0xfb,0xea,0xda,0x78,0x40, + 0x15,0xaf,0x15,0x6d,0xbb,0xfd,0x81,0x52,0x53,0x50,0x6f,0xa7,0x39,0xa3,0x24,0xde, + 0x57,0x82,0xd9,0x79,0xd5,0x14,0x69,0x11,0xa7,0xa6,0x71,0xb5,0xaa,0xd5,0x9b,0x93, + 0x12,0x46,0x14,0x6e,0xfb,0x8a,0x5e,0x10,0xdc,0x7f,0x2c,0xb3,0x5e,0x55,0x4e,0x3f, + 0xaf,0x7f,0x0b,0x2b,0x56,0x89,0x5e,0xda,0x49,0xc5,0x5c,0x6b,0x7b,0xfb,0x8c,0x61, + 0x2f,0x1f,0x72,0x59,0x86,0x34,0x42,0x53,0xea,0xf2,0x98,0xcb,0x5c,0x1e,0xbb,0x7b, + 0x24,0x26,0x8e,0xdd,0xa6,0x56,0x1e,0x35,0xda,0xa7,0xc6,0xfb,0xfc,0x25,0xb6,0x70, + 0xb8,0x75,0x6a,0xa0,0xb4,0xab,0x9a,0x34,0x47,0x69,0x06,0xe3,0x0d,0x39,0xd8,0x59, + 0xaa,0x55,0xcb,0x42,0xc5,0xf9,0x18,0xfa,0xec,0x2c,0x8c,0xf8,0x33,0xb3,0x04,0x76, + 0xb2,0xbb,0xc4,0x35,0xc8,0x36,0x0e,0xdc,0x7d,0x6e,0xd5,0x72,0x0f,0x8a,0x0f,0x3a, + 0xc2,0x87,0xe2,0x75,0x57,0xca,0x91,0xac,0xbf,0x26,0xa6,0x10,0x50,0x15,0x3b,0x23, + 0xdd,0x2f,0xaa,0xcc,0xe7,0xff,0x9a,0x8a,0xe0,0xec,0x01,0x0a,0x49,0xa2,0x32,0x5d, + 0xa8,0xef,0xda,0x88,0x47,0xc5,0x32,0x6c,0xad,0x4a,0x25,0x72,0xee,0xac,0xed,0xc6, + 0xc9,0x6d,0x37,0x0e,0x19,0x17,0x96,0xb7,0x8d,0x84,0xa1,0x86,0x0c,0x77,0xd1,0xa1, + 0xc0,0x6b,0x15,0x5e,0x7a,0x5e,0x44,0xcb,0xae,0x8e,0x49,0x4f,0xe7,0xb4,0x56,0x56, + 0xd0,0x73,0x5f,0xa7,0xb5,0x6e,0x28,0xe4,0x96,0x05,0xda,0xa9,0x93,0x77,0x69,0xe5, + 0x5f,0xe6,0x62,0xf9,0x5c,0x22,0xde,0x17,0x1e,0x71,0x69,0x1d,0x0e,0x33,0x38,0xc6, + 0xdf,0xf5,0xeb,0x4c,0xed,0x36,0x39,0x79,0x96,0x62,0xb3,0x8d,0x89,0xb7,0x23,0x21, + 0xce,0x24,0x98,0x43,0xc8,0x9d,0xaf,0x4f,0x38,0x84,0x35,0x0b,0x1b,0xc3,0x7f,0x72, + 0xda,0x04,0x87,0x55,0x74,0x8b,0x8d,0x9f,0x06,0x66,0xed,0xfe,0xb6,0x74,0xa6,0xce, + 0xd0,0x5f,0x63,0x00,0x0a,0x77,0x62,0x42,0x6d,0xca,0x78,0xda,0x9d,0xff,0xdf,0xf9, + 0x15,0xc4,0xb8,0x5a,0x7f,0xba,0x08,0x73,0x57,0x26,0xd8,0x56,0x15,0x66,0x99,0xa0, + 0xb6,0x07,0x0c,0x47,0xe1,0x4f,0x4d,0x28,0xa5,0x5f,0xb4,0xf2,0x14,0xf9,0xb9,0x6c, + 0x50,0x98,0x0f,0xdd,0x1c,0xdb,0x5f,0x3a,0x10,0xab,0xb6,0x53,0xcc,0x70,0x9a,0xf3, + 0x9b,0xc7,0x4f,0xff,0xb4,0x25,0x58,0x48,0xa0,0xc8,0x53,0xf8,0xeb,0x8e,0x02,0xc3, + 0x21,0xe4,0x7c,0x9b,0xb0,0x78,0xbf,0xf9,0x62,0xc5,0x46,0xde,0x34,0xab,0x36,0x63, + 0x3b,0x1e,0xd6,0x72,0x9c,0x70,0xf9,0x06,0x2d,0x1b,0x99,0xbb,0x48,0xe8,0x49,0xbb, + 0x4f,0x10,0xc8,0xd0,0xee,0x13,0xe1,0xa2,0x71,0xd2,0x66,0x49,0x26,0x1a,0xa6,0xbe, + 0xc4,0x81,0x48,0x44,0x5e,0xe0,0xf0,0x0a,0xfb,0x35,0xac,0xe3,0x1f,0x57,0x49,0x6d, + 0x7d,0xce,0x8e,0xb6,0x7a,0xc9,0x5f,0x63,0x50,0x45,0x14,0x4d,0x1d,0x04,0x9a,0x57, + 0x2d,0x91,0x07,0x64,0x46,0xd3,0x8c,0x38,0x4a,0x76,0x38,0x38,0x6c,0x7d,0x03,0x86, + 0xbf,0x0d,0x01,0xc1,0xc1,0x55,0xab,0xd5,0x11,0xdb,0x29,0x8c,0xe5,0x78,0x44,0x7c, + 0x09,0xfa,0x1b,0x6f,0x87,0x32,0x88,0x93,0xf8,0x8c,0x75,0x45,0x29,0xc7,0x5c,0x61, + 0x06,0x11,0xbe,0x3e,0xe4,0x54,0x4b,0x4e,0xb1,0x95,0xa1,0x75,0xe6,0x15,0x53,0x7a, + 0xb5,0x2d,0x93,0x7e,0x4e,0x97,0x96,0x58,0x12,0x2e,0xc3,0x88,0x24,0x41,0x76,0x1d, + 0x60,0xd7,0x12,0x89,0xaf,0x8f,0x5d,0x9e,0x88,0xd6,0x4e,0x01,0x56,0x48,0xa0,0x26, + 0x2f,0xb9,0x75,0xdb,0x6b,0x49,0x9c,0xae,0xe3,0x9e,0xb7,0xcb,0x1d,0xeb,0x9c,0x27, + 0x41,0xcb,0x76,0xf6,0xf2,0x44,0x8e,0xc2,0xfb,0x83,0xed,0x03,0x9e,0x7a,0x70,0xfe, + 0x04,0x21,0xa3,0xae,0x99,0xae,0xfe,0x3a,0x88,0xfd,0x61,0x10,0xae,0xbc,0xe1,0x6e, + 0x1e,0xfc,0xbc,0x2d,0x20,0xd8,0x4a,0x64,0x40,0x80,0x90,0x8f,0x18,0x59,0xdc,0xe2, + 0x36,0x6f,0xe0,0x27,0xe0,0x8d,0xf4,0x87,0xcd,0x3c,0xdb,0xcf,0x1e,0x5e,0xd5,0x0a, + 0x10,0xbb,0x13,0xf0,0xf1,0x1e,0x77,0x5f,0x43,0x0b,0x1f,0x0f,0x6c,0xf3,0x13,0xcd, + 0x25,0xb7,0x19,0xa3,0x7d,0xae,0xa9,0x01,0x42,0xa3,0x5b,0xbe,0x6f,0x31,0x4d,0xd7, + 0x9c,0xed,0xf2,0x4e,0xbf,0xf0,0xf8,0xd2,0x67,0x5c,0x65,0x1b,0xcb,0xd8,0x66,0x2d, + 0xad,0x64,0x41,0xfe,0x48,0x8e,0xd2,0x9e,0x88,0x06,0x5e,0x71,0x72,0x98,0x4a,0x49, + 0xe2,0x93,0x34,0x30,0xda,0x46,0x0d,0x97,0x70,0x7e,0x7c,0xfb,0x52,0xf2,0xc3,0xb8, + 0x7c,0xca,0x31,0xe1,0x63,0xf8,0x11,0xf3,0xdf,0x6d,0x52,0xb1,0x26,0x2a,0x07,0x0c, + 0xd5,0x8b,0xa3,0x73,0xe5,0x62,0x92,0x13,0x1d,0x81,0xf3,0x04,0x8a,0x2b,0x3b,0x2a, + 0x2a,0xf5,0x85,0x50,0xa9,0xd7,0x62,0x22,0xa1,0xb1,0xb3,0x3b,0x89,0xaa,0x74,0x5a, + 0x49,0xaf,0x91,0xce,0x01,0x33,0x1a,0x0d,0xd3,0xbe,0xd6,0x88,0x13,0xf9,0xe0,0x98, + 0xb3,0x7b,0xc0,0xad,0x55,0xb0,0xdc,0x32,0x4b,0x23,0xfe,0x2c,0xa3,0x4e,0xff,0xa2, + 0xa5,0xbe,0xa1,0x55,0xb1,0x3a,0x27,0x4e,0x7c,0x28,0x73,0xef,0x07,0xa7,0xfa,0xea, + 0x2e,0x5f,0x29,0x93,0xf8,0xe0,0x7d,0x70,0x70,0x6a,0x07,0x40,0xd4,0xeb,0x9e,0xd7, + 0x89,0x7b,0xd6,0x99,0x84,0x6d,0xf5,0x74,0xac,0x0f,0x1f,0x70,0x1f,0x65,0xb4,0x60, + 0xe1,0x49,0xd0,0x3c,0x49,0xc0,0x73,0xf9,0x56,0xd5,0x23,0x86,0x25,0x29,0x4a,0x15, + 0xdf,0xf5,0xe4,0x23,0x31,0xf4,0x0a,0x36,0x10,0x87,0xf6,0xd5,0xcb,0x7b,0x7b,0x07, + 0xff,0xcc,0x8a,0xff,0xf1,0xe8,0x97,0x83,0x5f,0x0e,0x0e,0xac,0xe9,0xd1,0x53,0x4f, + 0x6f,0xe7,0x33,0x28,0xe3,0x74,0x57,0x34,0x42,0x55,0xeb,0x14,0xba,0x3f,0xb2,0x5d, + 0xcb,0x44,0x4d,0x73,0xcc,0x59,0xe0,0xce,0x85,0x94,0xb3,0x55,0x15,0x52,0xa5,0x60, + 0x2c,0xa9,0x2a,0x1a,0x74,0x70,0x3f,0xe9,0x39,0x37,0xf3,0xe4,0x63,0x81,0x58,0x37, + 0x7f,0xc4,0xb1,0xb2,0xd6,0x12,0x88,0xef,0xc9,0xcc,0x9e,0x9a,0xa4,0xe0,0x4f,0x57, + 0x01,0xb2,0x79,0xa2,0x69,0x82,0xc3,0x2d,0x49,0x4f,0xdf,0x04,0xa1,0x25,0xbe,0xeb, + 0xfa,0xdc,0x11,0x50,0x8f,0x4f,0xbe,0x59,0xdd,0xca,0x6f,0x12,0x0e,0x73,0x9d,0x41, + 0x86,0x63,0xd7,0x12,0x74,0x26,0x4c,0x1e,0xd9,0xdf,0x86,0xf1,0xf8,0xfb,0x5f,0xbc, + 0x00,0x39,0xb3,0xdd,0x7c,0xa4,0x40,0x72,0x89,0x58,0x6e,0x34,0x1f,0x7f,0x92,0xfe, + 0xf8,0xb3,0xb4,0x84,0xb1,0x56,0xa2,0x1e,0x61,0xae,0xaa,0xa4,0xd1,0x7c,0xeb,0x40, + 0x79,0x73,0xc4,0x19,0x4f,0x02,0x91,0x24,0x49,0x6a,0xbe,0x2b,0x46,0x02,0x2c,0x68, + 0x3e,0x09,0x89,0xef,0x52,0xe9,0x5d,0xe1,0xba,0x02,0xba,0xfa,0x2b,0x21,0x83,0x2e, + 0x85,0xff,0xe8,0x40,0x7d,0x81,0xfa,0xd1,0x01,0x24,0x22,0xfd,0x33,0xaf,0x17,0xd9, + 0xd1,0xce,0xff,0x01,0xc3,0x72,0x03,0x9b,0x91,0xa4,0x00,0x00, }; constexpr size_t PATTERNS_INDEX_HTML_GZ_LEN = sizeof(PATTERNS_INDEX_HTML_GZ); // diff --git a/firmware/patternflow/src/status_index.h b/firmware/patternflow/src/status_index.h index 4ac1891c..17a89af3 100644 --- a/firmware/patternflow/src/status_index.h +++ b/firmware/patternflow/src/status_index.h @@ -12,113 +12,109 @@ static const char STATUS_INDEX_HTML[] PROGMEM = R"HTML( - + Patternflow - Status
              -

              Status

              -
              +/* Console base: the same block opens every core page. Light in :root (the chrome's + injected light palette holds these same values: keep the two in step), + dark under html[data-theme=dark]. No web fonts: the hotspot has no internet. */ +:root{color-scheme:light;--cream:#F4EFE6;--panel:#FFFCFA;--ink:#1A1814;--muted:#625C51;--faint:#7D7668;--rule:#DDD5C6;--rule-soft:#E8E1D4;--ghost:#E3DCCE;--led:#FF5C2E;--bad:#B83C1C;--ok:#2B7A4B;--warn:#85600E; +--sans:system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;--mono:ui-monospace,'Cascadia Mono','SF Mono',Menlo,Consolas,'Roboto Mono',monospace} +html[data-theme=dark]{color-scheme:dark;--cream:#0C0B09;--panel:#131110;--ink:#EDE7DB;--muted:#A39B8A;--faint:#7A7364;--rule:#2B2721;--rule-soft:#1F1C18;--ghost:#2A2620;--bad:#FF7A55;--ok:#57B87F;--warn:#D9A03F} +*{box-sizing:border-box}[hidden]{display:none!important} +body{margin:0;background:var(--cream);color:var(--ink);font:15px/1.5 var(--sans);-webkit-font-smoothing:antialiased} +a{color:inherit}:focus-visible{outline:2px solid var(--led);outline-offset:2px} +.wrap{max-width:720px;margin:0 auto;padding:20px 16px 48px} +h2{margin:0 0 6px;font-size:13px;font-weight:600;color:var(--muted)} +.card{background:var(--panel);border:1px solid var(--rule);border-radius:10px;padding:16px} +.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:40px;padding:0 16px;border:1px solid var(--rule);border-radius:8px;background:var(--panel);color:var(--ink);font:500 14px var(--sans);text-decoration:none;cursor:pointer;transition:border-color .15s,background .15s,color .15s} +@media(hover:hover){.btn:hover{border-color:var(--ink)}footer a:hover{color:var(--ink)}} +.btn:disabled{opacity:.45;cursor:default;border-color:var(--rule)} +.msg{font:12px var(--mono);color:var(--faint)}.pf-ok{color:var(--ok)}.pf-err{color:var(--bad)} +footer{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-top:40px;border-top:1px solid var(--rule);font:12px var(--mono);color:var(--faint)} +footer a{display:inline-flex;align-items:center;min-height:40px;color:var(--muted);text-decoration:none} +header{display:flex;align-items:baseline;flex-wrap:wrap;gap:4px 12px;margin-bottom:20px} +h1{flex:1;margin:0;font-size:22px;font-weight:600;letter-spacing:-.01em} +.sub{font:12px var(--mono);color:var(--faint)} +section{margin-top:28px} +.note{margin:8px 0 0;font-size:13px;color:var(--muted)} +.btn.dan{color:var(--bad)}@media(hover:hover){.btn.dan:hover{border-color:var(--bad)}} +.acts{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-top:12px} +.kv{margin:0;border-top:1px solid var(--rule)} +.kv>div{display:flex;align-items:baseline;gap:12px;padding:8px 0;border-bottom:1px solid var(--rule-soft);font-size:13px} +.kv dt{flex:1 0 auto;color:var(--muted)}.kv dd{margin:0;min-width:0;font:12.5px var(--mono);text-align:right;overflow-wrap:anywhere} +/* ── Status ── */ +.kv .big{font-size:14px;font-weight:600} +.ok{color:var(--ok)}.warn{color:var(--warn)}.bad{color:var(--bad)} +.bar{display:block;height:3px;margin:5px 0 2px auto;max-width:160px;background:var(--ghost)}.bar i{display:block;height:100%;background:var(--led)} +#cptext{display:block;width:100%;height:180px;margin-top:12px;padding:8px 10px;resize:vertical;color:var(--ink);font:12px/1.45 var(--mono)} +.pf-offline .kv dd{opacity:.5} + +
              +

              Status

              -
              - -
              - +
              + -

              Device

              -
              Firmware
              -
              -
              Build
              -
              -
              Last restart
              -
              -
              Busy
              -
              -
              Panel size
              -
              +

              Device

              +
              Firmware
              -
              +
              Build
              -
              +
              Last restart
              -
              +
              Busy
              -
              +
              Panel size
              -
              -

              Render

              -
              Panel
              -
              -
              Frame rate
              -
              -
              Frame time
              -
              -
              Active pattern
              -
              + + +

              Render

              +
              Panel
              -
              +
              Frame rate
              -
              +
              Frame time
              -
              +
              Active pattern
              -

              -

              Patterns

              -
              Total
              -
              -
              Built into firmware
              -
              -
              Loaded from storage
              -
              -
              Last load error
              -
              +

              Patterns

              +
              Total
              -
              +
              Built into firmware
              -
              +
              Loaded from storage
              -
              +
              Last load error
              -

              -

              Memory

              -
              Internal heap free
              -
              -
              Largest free block
              -
              -
              PSRAM free
              -
              +

              Memory

              +
              Internal heap free
              -
              +
              Largest free block
              -
              +
              PSRAM free
              -
              -

              Internal heap is the scarce one - the LED panel's DMA buffers -live there. A pattern module loads only if it leaves 24 KB -of it free; run lower and the network is the first thing to go, while the -panel keeps drawing.

              +

              Internal heap is the scarce one: the panel’s DMA buffers live there. A module loads only if it +leaves 24 KB free; below that the network goes first, while the panel keeps drawing.

              -

              Storage

              -
              Pattern storage
              -
              -
              Storage error
              -
              -
              +

              Storage

              +
              Pattern storage
              -
              +
              Storage error
              -
              +
              - -

              Network

              -
              Network
              -
              -
              Signal
              -
              -
              Address
              -
              -
              Names
              -
              -
              Hotspot
              -
              -
              Hotspot mode
              -
              -
              Hotspot clients
              -
              -
              Reached via
              -
              +

              Network

              +
              Network
              -
              +
              Signal
              -
              +
              Address
              -
              +
              Names
              -
              +
              Hotspot
              -
              +
              Hotspot mode
              -
              +
              Hotspot clients
              -
              +
              Reached via
              -
              - -
              + +
              )HTML"; // GENERATED by firmware/toolchain/console_pages.py build — do not edit. -// gzip -9 of STATUS_INDEX_HTML (15075 bytes → 6144 bytes), served with Content-Encoding: gzip. +// gzip -9 of STATUS_INDEX_HTML (14577 bytes → 6101 bytes), served with Content-Encoding: gzip. static const uint8_t STATUS_INDEX_HTML_GZ[] PROGMEM = { - 0x1f,0x8b,0x08,0x00,0x00,0x00,0x00,0x00,0x02,0xff,0xc5,0x5b,0x6b,0x72,0xdb,0x48, - 0x92,0xfe,0xaf,0x53,0x94,0xe5,0x6e,0x01,0x18,0x81,0x10,0x25,0xbf,0x41,0x53,0x1a, - 0xc9,0x8f,0x99,0x9e,0xb6,0x7a,0xbc,0x96,0x7b,0x27,0xb6,0xbd,0x1d,0x13,0x45,0xa0, - 0x40,0xa2,0x05,0x02,0x08,0x14,0x28,0x8a,0x43,0x31,0xa2,0x0f,0x31,0x7f,0xf6,0x02, - 0x7b,0x85,0xfd,0xbf,0x47,0xe9,0x93,0xec,0x97,0x59,0x05,0x10,0xa4,0x64,0x5b,0x1d, - 0xf3,0x63,0x27,0x7a,0x2c,0xd6,0x2b,0x2b,0x2b,0xdf,0x99,0x55,0x78,0xf9,0x20,0x2e, - 0xa2,0x7a,0x51,0x2a,0x31,0xa9,0xa7,0xd9,0xf1,0xce,0x4b,0xfa,0x23,0x32,0x99,0x8f, - 0x87,0xbb,0x2a,0xdf,0xa5,0x0e,0x25,0xe3,0xe3,0x97,0x53,0x55,0x4b,0x11,0x4d,0x64, - 0xa5,0x55,0x3d,0xdc,0x9d,0xd5,0x49,0xef,0x39,0x0d,0xea,0xa8,0x4a,0xcb,0x5a,0xe8, - 0x2a,0x1a,0xee,0x1e,0x94,0x49,0x2f,0x2a,0x72,0x5d,0x64,0x2a,0xf8,0x45,0x9f,0x4c, - 0x86,0x71,0xff,0x68,0xf4,0xe8,0xd1,0xd1,0xd1,0xee,0xf1,0xcb,0x03,0x33,0xf1,0xd8, - 0x2e,0x38,0x4e,0x13,0xf7,0xc1,0x3c,0xcd,0xe3,0x62,0x1e,0xbc,0x7f,0xeb,0xb9,0xc9, - 0x2c,0x8f,0xea,0xb4,0xc8,0x5d,0x6f,0x79,0x25,0x2b,0xf1,0xb7,0xa1,0x19,0xf3,0x7f, - 0x1c,0x3a,0x07,0xb2,0x4c,0x0f,0x74,0x2d,0xeb,0x99,0x76,0xfc,0x77,0xc3,0x4f,0x3f, - 0xfb,0xe7,0xc3,0x23,0xf5,0xc2,0xbf,0xf0,0x7f,0xf2,0xdf,0x0f,0xff,0x86,0xe5,0xc3, - 0x25,0x0d,0xab,0xd0,0xc9,0xd2,0x2b,0xe5,0xf8,0x71,0x5a,0xd5,0x8b,0xf0,0xc1,0xa1, - 0x7f,0x15,0xe6,0xb3,0x2c,0xf3,0xc7,0xaa,0x0e,0xff,0xe4,0xa7,0x79,0x92,0xa5,0xe3, - 0x49,0x1d,0xfe,0x30,0x9b,0x8e,0x54,0xe5,0x4f,0x8a,0x2c,0x0e,0xff,0x3a,0xfa,0x45, - 0x45,0xb5,0xaf,0xe5,0x22,0x6c,0x31,0xa8,0xfd,0x4b,0x5f,0x79,0x4b,0xb5,0xb7,0xe7, - 0xaa,0xa0,0x56,0xd7,0xf5,0xab,0x22,0xaf,0x55,0x5e,0x0f,0x6b,0x6f,0xe5,0x8f,0x66, - 0xba,0x33,0x75,0xe4,0x97,0xde,0xb2,0x69,0x89,0x04,0xc8,0x8f,0x82,0x38,0xd5,0x72, - 0x94,0xa9,0x78,0xf8,0xe0,0x70,0xd5,0x6d,0xf5,0x07,0x65,0x50,0x4f,0x54,0xee,0x26, - 0x7e,0xe2,0x0d,0x2a,0x55,0xcf,0xaa,0x5c,0x94,0x2b,0xbf,0x2c,0xb2,0x6c,0x0d,0x51, - 0xfb,0x53,0x3f,0xf2,0x0b,0x43,0x85,0xda,0x4f,0xfc,0xf1,0xf0,0x70,0xd0,0xee,0xf0, - 0x01,0x3b,0x8c,0xf7,0xf6,0x1e,0x24,0xc0,0x2d,0x19,0x1e,0xfa,0x51,0xa6,0x64,0xf5, - 0x31,0x9d,0xaa,0x62,0x56,0xbb,0xb5,0xe7,0xbb,0x3a,0x88,0x64,0x96,0x9d,0x68,0xd7, - 0x0b,0xff,0xe4,0x6a,0xcf,0xb3,0x3b,0x36,0xd0,0x63,0x6f,0x19,0xb9,0xb1,0x4f,0x54, - 0xc1,0x59,0xda,0x6e,0x45,0xdd,0x4c,0x2a,0xe5,0xad,0xec,0x9a,0xef,0xfd,0xef,0x3d, - 0x6f,0xd5,0xee,0xfc,0x3d,0x76,0x4e,0x86,0xfd,0xc1,0xd6,0x8e,0x03,0x60,0xe3,0xd6, - 0x43,0x88,0x44,0xd3,0xf7,0xc1,0x9f,0x62,0x5d,0xb1,0xb7,0x57,0x04,0x49,0x5a,0xe9, - 0x7a,0x38,0x04,0x21,0x4e,0xb0,0x3c,0x04,0xf2,0xf6,0xd8,0x60,0x57,0x51,0x86,0x1d, - 0x96,0x8f,0x6f,0x43,0x5e,0xf9,0x00,0xba,0x9e,0x93,0x7b,0xcb,0xe9,0x30,0x1f,0x24, - 0x37,0x37,0x00,0xb5,0xf2,0xf3,0x62,0x1e,0x7e,0x58,0x61,0x0e,0x8b,0xc5,0x7a,0x5a, - 0x84,0x93,0xec,0xed,0xbd,0x0b,0xca,0x99,0x9e,0xa0,0x31,0xb8,0x38,0x41,0x33,0x72, - 0x2f,0xbc,0xf0,0xa7,0x93,0x9f,0x02,0xac,0x02,0x1e,0x3f,0x0d,0xdf,0x07,0x44,0x74, - 0xf7,0x47,0xff,0xdc,0x7f,0x0d,0x60,0x73,0x59,0x47,0x93,0x8b,0x2d,0x48,0x53,0x6f, - 0xf9,0x3e,0x30,0xe0,0x81,0xf7,0x4f,0x01,0xb0,0x71,0xcf,0x87,0xe7,0xb2,0x9e,0x04, - 0xd3,0x14,0xc3,0xfe,0xb9,0xc7,0x4b,0xd3,0xfa,0x6d,0x51,0xbd,0x56,0x57,0x69,0xa4, - 0xd6,0x8b,0xc1,0xbf,0x62,0x58,0xdc,0xdc,0x2c,0x57,0x0d,0xa3,0x73,0x35,0x17,0xef, - 0xab,0x62,0x9a,0x6a,0xb5,0x66,0xc7,0xc2,0xcf,0x0d,0xa3,0x27,0x6b,0x94,0x8e,0xd4, - 0xa3,0x35,0x63,0xb4,0xb7,0xd4,0x60,0xb7,0x5b,0x04,0xa3,0x59,0x9a,0xc5,0x7b,0x7b, - 0xda,0xfc,0x18,0x0e,0x6d,0x8f,0x07,0xf2,0x4f,0x02,0x22,0xa7,0xeb,0xf9,0x0b,0xe2, - 0xf8,0xca,0x1b,0x74,0xb8,0xd1,0xa1,0x71,0x33,0x6d,0x90,0xbb,0x6f,0xaa,0xaa,0xa8, - 0x5c,0xa7,0x36,0x93,0x1c,0x3a,0x48,0x11,0xd8,0xd6,0xcd,0xcd,0x0b,0xf5,0x18,0x50, - 0x56,0xab,0xb5,0xd8,0xfd,0xc9,0x9d,0x91,0x48,0xda,0xa3,0x24,0x0a,0xe4,0xe2,0x9e, - 0x2d,0xe9,0xaa,0xbc,0x25,0x69,0x75,0x15,0x14,0x97,0x5e,0x3d,0xa9,0x8a,0xb9,0x30, - 0x1b,0x55,0x96,0x8e,0xad,0xd4,0xb3,0x74,0x90,0x66,0x9d,0x54,0xfc,0x07,0x4c,0xa9, - 0x60,0x30,0x08,0xcd,0x55,0x47,0xe6,0x5e,0xd3,0xf9,0x01,0x11,0xff,0x5e,0x40,0xcf, - 0xcb,0xc4,0xf0,0x68,0xa8,0x07,0xef,0x82,0xa4,0xa8,0xde,0x48,0xa0,0xd1,0x6e,0x9e, - 0x40,0x3e,0x31,0x93,0x10,0x5f,0x79,0xae,0xd7,0xda,0x9b,0x1d,0x63,0xb8,0x72,0x39, - 0x55,0xc3,0xdd,0xab,0x54,0xcd,0xcb,0xa2,0xaa,0x77,0x45,0x64,0x75,0x7a,0x77,0x9e, - 0xc6,0x35,0x0c,0x15,0x33,0xb0,0xc7,0x0d,0x58,0x8a,0xb4,0x4e,0x65,0xd6,0xd3,0xd0, - 0x25,0x35,0x3c,0x24,0x2b,0x57,0xa7,0x75,0xa6,0x8e,0xdf,0xcb,0xba,0x56,0x15,0xec, - 0x08,0x8e,0xd6,0x13,0x06,0x9b,0x97,0x07,0x66,0x0c,0x96,0xb0,0x5e,0xd0,0xdf,0xb0, - 0x2a,0x8a,0x7a,0xd9,0xeb,0x45,0x95,0x92,0xd3,0xf0,0x61,0xff,0x55,0xff,0xac,0xff, - 0x62,0xd0,0xeb,0xa5,0xf9,0x65,0xf8,0xf0,0xcd,0xeb,0x37,0xcf,0x5e,0x9f,0xa1,0x35, - 0x9d,0xd5,0x2a,0x0e,0x1f,0x3e,0x3f,0x7d,0x7e,0xf4,0xec,0x08,0xed,0x44,0xa6,0x79, - 0x1d,0x3e,0x7c,0x72,0xfa,0xe4,0xc9,0xe3,0xa7,0x83,0x9d,0x5e,0xaf,0x9a,0x65,0x2a, - 0x7c,0x78,0xf4,0xf8,0xe8,0xf0,0xf0,0xf9,0xc0,0x34,0x7b,0xba,0x48,0x30,0xe7,0xf0, - 0xec,0xf0,0xc5,0xe1,0x63,0xf4,0x65,0x04,0xe1,0xed,0xdb,0x27,0xaf,0x8e,0xde,0xa0, - 0x55,0x00,0xfc,0x93,0x67,0x67,0xcf,0x9f,0xbd,0x45,0x63,0x2e,0xab,0x3c,0x7c,0xf8, - 0xfa,0xc5,0x69,0xff,0xd1,0x5b,0x82,0x56,0xca,0x5c,0x65,0x58,0xfa,0xe8,0xf0,0xf0, - 0xb0,0x4f,0x1d,0x5a,0xe6,0x3a,0x74,0xbe,0x03,0x11,0x2a,0xc7,0x9f,0xa5,0xdc,0xee, - 0x69,0x55,0xa5,0x89,0xaf,0x17,0xba,0x56,0xd3,0xde,0x2c,0xf5,0xd7,0x9d,0xb4,0x64, - 0x5a,0xe4,0x45,0xe8,0xfc,0x45,0xd5,0x67,0x15,0x90,0xd5,0xe2,0x1c,0x6d,0x5e,0x4b, - 0x03,0xba,0x94,0x91,0xf2,0x2f,0xde,0x52,0x67,0xef,0x83,0x1a,0xcf,0x32,0x59,0xf9, - 0xe7,0x2a,0xcf,0x0a,0xbf,0x1d,0x5e,0xed,0xfc,0x61,0x39,0x2a,0xae,0x7b,0x3a,0xfd, - 0x47,0x9a,0x8f,0xc3,0x51,0x51,0xc5,0xaa,0xea,0xa1,0x67,0xb5,0x33,0x2a,0xe2,0xc5, - 0x72,0x2a,0xab,0x71,0x9a,0x87,0xfd,0xc1,0x48,0x46,0x97,0xe3,0xaa,0x98,0xe5,0x71, - 0x08,0xed,0x70,0x2d,0x2d,0xbd,0x41,0x54,0x64,0x45,0x65,0xbb,0x40,0x4e,0x6f,0x90, - 0x80,0x8b,0x20,0xdd,0x34,0xcd,0x16,0xb6,0x9b,0x50,0xf6,0x06,0x3b,0x59,0x9a,0xab, - 0xde,0x44,0xb1,0xbd,0x3f,0x0c,0x9e,0x0c,0x7a,0x73,0x35,0xba,0x4c,0x31,0x97,0x16, - 0xe8,0x29,0x38,0x34,0x21,0x14,0x64,0x4e,0x8c,0x4e,0xa5,0x56,0xf1,0x6a,0x27,0x98, - 0x57,0xb2,0x04,0x12,0xd7,0x46,0x0c,0xc2,0xa7,0xcf,0xfb,0xe5,0xf5,0xa0,0x41,0x4a, - 0xc8,0x59,0x5d,0x0c,0x4a,0x19,0xc7,0xb4,0xf0,0xd1,0x51,0x79,0x2d,0x8e,0x30,0x2e, - 0x9e,0x3e,0x2e,0x81,0x3f,0xb9,0x48,0x55,0x2d,0x61,0xed,0xcb,0x8c,0x7c,0x49,0xa6, - 0xae,0x07,0x00,0x3c,0xce,0x7b,0x29,0x68,0xa9,0xc3,0x48,0x11,0xa5,0x07,0x63,0x59, - 0x86,0xcf,0x01,0xd4,0x82,0xc1,0xd9,0xeb,0xba,0x98,0x86,0x87,0x80,0x36,0xd8,0x69, - 0xe9,0x61,0xfa,0x00,0x1b,0x8e,0x34,0x8d,0x85,0x39,0x18,0x09,0x80,0x07,0x24,0x63, - 0x08,0x97,0xc1,0xef,0x19,0x16,0xd9,0x23,0xd2,0xcf,0x5b,0x44,0x83,0x78,0x78,0x03, - 0x0b,0xb4,0x92,0x71,0x0a,0xeb,0x76,0xc8,0xb8,0x1e,0x2e,0x0d,0x19,0xd2,0x7f,0xa8, - 0xf0,0xf0,0x09,0x96,0x72,0x73,0x6e,0x40,0x3d,0xed,0xf7,0xdb,0x33,0x0f,0xe8,0x1c, - 0xe1,0x21,0x76,0xd5,0xb3,0xd1,0xf2,0x36,0xb1,0x89,0xb3,0x96,0x09,0x06,0x18,0xe0, - 0x6f,0x30,0x89,0xa5,0x1a,0x58,0x6b,0xc5,0x4a,0x6a,0x39,0xdc,0x23,0xeb,0x7f,0xf4, - 0x94,0x71,0x39,0x5a,0x6e,0x2d,0xdf,0xc6,0x25,0x53,0xa4,0x73,0x3d,0x12,0x20,0x22, - 0x7c,0xd0,0x7f,0xa1,0xa6,0x03,0x32,0x1a,0xbd,0xba,0x02,0xaf,0x61,0x07,0xa6,0xe1, - 0xac,0x2c,0x55,0x15,0x81,0x8b,0x83,0x9d,0xee,0xee,0xac,0x63,0xde,0x9a,0x83,0x7d, - 0xf1,0x9c,0xf6,0x8c,0xb3,0x8e,0xa4,0x19,0xf2,0x10,0x42,0x77,0x11,0x9c,0x35,0x8e, - 0xa8,0x0e,0x63,0xf6,0x79,0xe6,0x8e,0xb0,0x33,0x09,0x1c,0xb3,0x97,0x79,0xd9,0x88, - 0xc9,0x33,0x92,0x92,0xfb,0xf1,0xb6,0xd9,0x2a,0xae,0x97,0x86,0xea,0xc2,0x8a,0x5c, - 0x87,0x3e,0x8f,0xb6,0xc8,0xbb,0x75,0x40,0x2c,0x8e,0xd7,0x47,0x83,0x97,0xb2,0x82, - 0xdc,0x1f,0xdc,0x83,0x75,0x84,0x26,0x93,0x95,0x4f,0x16,0x56,0xc4,0x80,0x41,0x71, - 0xa5,0x2a,0xb2,0x76,0x3d,0x52,0x0d,0x68,0xcb,0x62,0x3e,0x51,0x95,0xa2,0x7d,0x82, - 0x51,0x3a,0x5e,0x6e,0xa1,0xb6,0xc5,0x3a,0x90,0xad,0xb8,0x5c,0x76,0xf1,0x85,0x5f, - 0x58,0x05,0x64,0x9a,0x36,0x7a,0xa9,0x03,0xfd,0x23,0x19,0x6f,0x74,0x93,0xf8,0x02, - 0xc4,0x48,0x56,0x4b,0x2b,0xe5,0x8f,0xee,0x92,0xf2,0x35,0xed,0x06,0x1d,0xe9,0x22, - 0xa9,0x36,0x87,0x3f,0xec,0xf7,0xbf,0x35,0x60,0x44,0xda,0xb2,0x70,0x94,0x15,0xd1, - 0x65,0xa3,0x3c,0x34,0xe3,0x6e,0xed,0xc1,0xba,0xbc,0xa8,0xd5,0x72,0x8b,0x4c,0xb7, - 0x25,0xbc,0x61,0x01,0xe4,0x0b,0x6c,0x83,0xd0,0x6e,0x98,0x9f,0xc7,0x4f,0x00,0xa8, - 0x2e,0x8a,0x4c,0xdf,0xcb,0x40,0xf0,0x1e,0x9d,0xb3,0x1c,0xb2,0x85,0x19,0xcd,0x20, - 0x3a,0xf9,0xb2,0x2c,0x74,0x4a,0xaa,0x14,0x56,0x2a,0x93,0x35,0xc2,0xdd,0xc1,0x3d, - 0xd5,0xf2,0x96,0x1e,0x3d,0xff,0xa2,0x1e,0x35,0x12,0x0c,0x2d,0x15,0x8c,0x50,0x87, - 0x40,0xbc,0xa0,0x94,0x15,0x50,0xbe,0x4b,0x1c,0x8d,0xac,0xdf,0x6d,0xc0,0xb6,0xec, - 0x11,0x53,0x73,0x56,0x69,0x80,0x28,0x8b,0x94,0x28,0xd0,0x1c,0x34,0x9c,0x90,0xec, - 0x2d,0x6f,0x59,0x7c,0xbb,0xfe,0xb6,0xa0,0xfc,0x71,0xaa,0xe2,0x54,0xba,0x16,0x4c, - 0x18,0x15,0x94,0xa8,0x20,0x1e,0x37,0xd0,0x42,0x99,0xd4,0x0c,0x8e,0xbd,0x7e,0xb8, - 0xbb,0x3b,0x68,0x09,0x29,0x47,0xc0,0x12,0x98,0x0f,0x88,0xd6,0x3d,0xb2,0xce,0x56, - 0x4b,0xf9,0x77,0xa6,0xe0,0x72,0x7b,0xe0,0xc0,0x80,0x35,0x82,0x7f,0xae,0xc0,0xce, - 0xa9,0x1e,0xff,0x2b,0x06,0x91,0xd6,0x23,0xa0,0xe9,0xdd,0xa5,0x20,0x76,0x48,0x55, - 0xd5,0x1d,0xfa,0xf0,0x30,0x2a,0x89,0x69,0x5b,0x92,0xbc,0x16,0xf5,0x56,0xa8,0x3b, - 0xce,0xcb,0x48,0x51,0xbf,0x63,0x97,0xe8,0x64,0x95,0x62,0x14,0x41,0xe5,0x3a,0x45, - 0xa0,0x03,0x1b,0xb5,0xad,0x02,0x1c,0x34,0xdc,0xe1,0x75,0x7f,0x27,0x83,0x89,0x1c, - 0x4c,0x89,0x03,0x52,0x05,0xd1,0xa1,0xd4,0x6a,0xe7,0xd3,0x24,0x8d,0x63,0x95,0xff, - 0xdc,0x9e,0x27,0x2f,0x72,0xf5,0x20,0x9d,0x52,0x90,0x06,0xe7,0x0c,0x42,0x11,0x91, - 0x92,0x84,0x14,0x4a,0xc0,0xba,0x15,0x24,0xbe,0x48,0xee,0x02,0xa8,0x54,0x02,0x2f, - 0x0e,0x9e,0x76,0xce,0xf8,0xa8,0x63,0x7b,0xcd,0xa1,0x59,0x72,0xbf,0x6c,0xe3,0x11, - 0x2d,0xfc,0x0b,0x8c,0x94,0xcb,0xdb,0x1a,0xb0,0xda,0x41,0xf4,0xc9,0xc1,0xe0,0xcb, - 0x03,0x93,0x3b,0x53,0x78,0x73,0xfc,0x32,0x4e,0xaf,0x44,0x94,0x49,0xad,0x11,0x75, - 0xc2,0x9e,0x36,0xa9,0xb5,0xaa,0x90,0x14,0x83,0xd6,0xcd,0x18,0x3c,0x3c,0xe7,0xcb, - 0xe8,0x3a,0x7e,0x39,0x39,0x3c,0x6e,0xe2,0x4c,0xfc,0xdc,0x98,0x07,0x9f,0xbc,0x2b, - 0xd2,0x18,0xb9,0x38,0x40,0xf5,0x9a,0x05,0x07,0x16,0xe4,0xce,0xce,0xcb,0x07,0xbd, - 0x9e,0xf8,0x38,0x49,0xb5,0x28,0xe5,0x58,0x09,0xfc,0x9d,0x4f,0x64,0x2d,0x90,0x0d, - 0x6b,0x81,0xe0,0x58,0xa9,0x5c,0x4f,0x20,0xe9,0x2a,0xf6,0x45,0x51,0x61,0x8e,0xe6, - 0x9f,0xb0,0xf0,0x39,0x68,0x34,0x55,0x84,0xb3,0x90,0xfa,0x52,0x0b,0xd8,0x86,0x1d, - 0x41,0xff,0x9b,0xa8,0xac,0x0c,0x05,0x82,0x7e,0x61,0xd4,0x0a,0x51,0x74,0x99,0x2a, - 0x2d,0x14,0x84,0x68,0x21,0x92,0x54,0x65,0xb1,0xe8,0x64,0xeb,0x62,0x22,0xb5,0x2f, - 0x60,0x41,0xc5,0x2f,0x33,0x5d,0xf3,0x3a,0xf8,0x51,0x1d,0x88,0x5e,0x0f,0x47,0xef, - 0x50,0x83,0x6d,0x23,0xce,0x6c,0xa1,0x52,0x31,0x62,0xb8,0x6b,0x1a,0xe6,0x84,0xd8, - 0x67,0xb1,0x7b,0xfc,0x0a,0xff,0x0a,0x28,0xf9,0x18,0xf1,0x24,0x64,0x16,0x24,0x31, - 0x73,0x36,0xc9,0x02,0xf5,0xb1,0x8b,0x4a,0xfa,0xd9,0x52,0xf2,0x00,0x1b,0x52,0x24, - 0x0f,0xed,0x81,0xe9,0x92,0x76,0x0a,0x35,0x77,0x05,0xda,0x71,0x91,0x67,0x0b,0x61, - 0xc4,0x51,0xc8,0x2a,0x95,0xbd,0x4c,0x8e,0x54,0x36,0xdc,0x7d,0xbd,0xde,0x90,0x80, - 0x35,0xeb,0x89,0xc0,0x36,0xac,0x01,0x97,0x8e,0x8e,0x4d,0x0a,0x08,0xf2,0x1f,0x81, - 0xd1,0xd9,0x31,0x08,0xd6,0x3d,0x21,0xce,0x8d,0xc5,0x71,0x7d,0xfc,0x36,0xad,0xa6, - 0x70,0x78,0x98,0x88,0xc6,0xcb,0x38,0x66,0x34,0x92,0x39,0x33,0x30,0x8e,0x1b,0x2c, - 0x3f,0xb3,0xf8,0x8c,0x52,0xbe,0x8d,0x95,0xa3,0x2c,0xbe,0xdf,0xd2,0x77,0x60,0x2f, - 0x8e,0x09,0xc6,0x54,0xf5,0x06,0x84,0xaa,0xba,0xef,0xde,0x7a,0xb1,0xb9,0x35,0x3a, - 0xee,0xb7,0xf4,0x3d,0xd9,0x11,0x41,0x7a,0xb4,0x01,0xa0,0xcc,0xb7,0x96,0xe3,0x4f, - 0x46,0xec,0xb2,0x54,0xdd,0x22,0xf0,0x07,0x95,0x43,0xae,0xbf,0x4a,0x60,0xde,0x6c, - 0x73,0x9f,0xf9,0x3d,0x8f,0xf8,0xb6,0x42,0x92,0x28,0x2a,0x59,0xaf,0xf1,0xb4,0x53, - 0x10,0xec,0x18,0x99,0x4a,0x4a,0xfd,0x7b,0x60,0x51,0x4e,0xbd,0xc9,0xe9,0xfa,0x7e, - 0xcb,0x4f,0x23,0xf2,0xed,0x50,0x4a,0x4e,0x38,0x37,0x40,0xc8,0xa8,0xbe,0x8b,0x6e, - 0x3b,0x2f,0xcb,0x06,0x0a,0xc5,0x2c,0xbb,0xcd,0xd1,0xb9,0x81,0x99,0xe5,0xe7,0x29, - 0x6b,0xd3,0x5a,0xfd,0x55,0xda,0x7e,0x2c,0x6a,0xb9,0x45,0xdb,0xfa,0xfe,0x92,0x5b, - 0x0b,0x98,0xcc,0x02,0x66,0xe2,0x0e,0x0d,0x28,0xcb,0x7b,0x8a,0x71,0x01,0xe3,0x16, - 0x8b,0xa4,0x2a,0xa6,0x42,0xd7,0x45,0x05,0xc3,0xb6,0x09,0x67,0xfa,0x3b,0xd4,0x21, - 0x03,0x30,0xa1,0xa8,0x44,0xb1,0x01,0x23,0x53,0xf7,0xa6,0x2f,0x41,0xb8,0x07,0x81, - 0xcf,0xd5,0xb4,0xa8,0x16,0x5f,0x25,0x2f,0x67,0xe3,0xb9,0xcc,0x60,0x67,0x65,0x89, - 0x33,0xaa,0xcd,0xb3,0x4d,0xd2,0xfb,0x9e,0xad,0x1a,0x43,0xd1,0x19,0x80,0xe0,0xf8, - 0x60,0x13,0x4c,0x76,0x4f,0xad,0xbd,0xf8,0x70,0x7a,0x7e,0x07,0x16,0xe5,0x7d,0xa8, - 0xb3,0x75,0x16,0xf8,0x1e,0xb2,0xfe,0x3a,0x92,0x55,0xa4,0x04,0x5c,0xbc,0xe8,0x71, - 0xc7,0xbb,0x37,0xaf,0x05,0x47,0x19,0x8e,0x16,0xaf,0xcf,0x4f,0xe1,0x56,0x92,0x44, - 0x55,0x7a,0x87,0xea,0xb8,0x34,0xa1,0x52,0x81,0x38,0x6d,0xb4,0x40,0x4c,0x8b,0x18, - 0xfe,0x9a,0xd9,0xa6,0x05,0x9b,0xeb,0x34,0x11,0x29,0xf8,0xa8,0xe4,0x15,0xbc,0x90, - 0xf1,0x02,0x6c,0xd3,0xf4,0xd5,0xee,0xf1,0xd1,0x63,0xf1,0xfd,0x99,0x35,0xfe,0x3b, - 0x05,0x4f,0xa4,0xc3,0x0c,0x44,0x35,0xcb,0x01,0x63,0xae,0x2a,0x21,0xf3,0x98,0xd1, - 0xc8,0x55,0x3d,0x2f,0xaa,0xcb,0x06,0x4d,0xae,0x5b,0x0a,0xae,0x10,0x08,0x08,0xec, - 0xb8,0x20,0xaf,0x98,0x66,0x8c,0xd1,0x0e,0xa3,0x2b,0x2e,0x95,0x2a,0xb5,0x88,0x2b, - 0x39,0xc7,0xa4,0xe0,0x8b,0xcc,0xbf,0x68,0xc4,0xf4,0xab,0x86,0xcb,0x9c,0xf2,0x2e, - 0xb1,0x4e,0xee,0x69,0x75,0xec,0x5e,0x77,0x08,0x75,0xa2,0xef,0x94,0xea,0x2e,0x14, - 0xa4,0x48,0x80,0x92,0xda,0xe9,0xd4,0x12,0x1c,0xc5,0xd8,0x1a,0x59,0xd8,0x27,0x29, - 0x4f,0xed,0xf2,0x8d,0xe3,0x72,0xa0,0x31,0x2f,0x04,0xa2,0xb4,0x4b,0x22,0x21,0xa2, - 0x8c,0x08,0xac,0x18,0x29,0x31,0x2b,0x05,0x1a,0x45,0x1e,0xa9,0x70,0x83,0xd2,0xf4, - 0xdb,0x50,0xf2,0x17,0x04,0xec,0xd0,0x68,0x97,0x68,0xcb,0x41,0x06,0xc5,0x0e,0x80, - 0xeb,0xb5,0xdc,0x69,0x04,0xa4,0x98,0xe7,0x02,0x01,0x8b,0x2e,0x8b,0x3a,0x10,0x54, - 0xfc,0x33,0xa1,0x4c,0x5a,0x9b,0xa1,0x4e,0x68,0xd1,0x25,0xff,0x0f,0x66,0xc7,0xaf, - 0x92,0xbf,0x9d,0xd7,0x21,0x9a,0xbe,0x2f,0xd9,0x91,0xc7,0x6d,0x19,0xc6,0xea,0x9e, - 0x4b,0x4f,0xe3,0x18,0x5e,0x59,0x6f,0xac,0x4d,0xef,0x69,0x0c,0x7f,0x80,0x8b,0xd9, - 0x5c,0x99,0xdf,0xd3,0xfc,0xfd,0xd9,0x90,0x71,0x53,0xb1,0xf5,0xef,0x5a,0x4b,0xda, - 0xb8,0x65,0x19,0xf4,0xef,0xdb,0x1d,0xfd,0x29,0x92,0x31,0xbd,0x05,0x24,0xba,0x1f, - 0x90,0x0f,0x0a,0x12,0x00,0xc1,0xb9,0x4a,0xe5,0x06,0x00,0xb4,0xbf,0x1a,0x56,0x98, - 0xcc,0xe1,0xf8,0xa5,0x14,0x93,0x4a,0x25,0xc3,0xdd,0x83,0x5d,0xe0,0x44,0x9e,0x5a, - 0x1e,0x8b,0xbd,0x29,0x42,0xc0,0xa2,0x1e,0x88,0xf5,0xa8,0xb5,0x3f,0xa0,0xcf,0xda, - 0x53,0x4a,0x40,0xb4,0x60,0x76,0x9a,0x7d,0x9a,0x8a,0x73,0x5b,0xc0,0xfe,0xc6,0x4d, - 0xdb,0xaa,0x79,0x5c,0x44,0xb3,0x29,0x8e,0x1b,0x40,0x68,0xdf,0x64,0x8a,0x7e,0x9e, - 0x2d,0xbe,0x8b,0x31,0x63,0xb5,0x5e,0x70,0x39,0x72,0x47,0xed,0x8a,0xd1,0xf1,0xf0, - 0xb0,0xff,0xf8,0xf9,0x93,0x67,0x4f,0x4f,0xdc,0xd1,0x81,0xfd,0xe9,0x05,0x75,0xf1, - 0x36,0xbd,0x56,0xb1,0x7b,0xe4,0xed,0x3b,0xe2,0xfc,0xcc,0x09,0xf9,0x4e,0x82,0xf3, - 0x36,0x9e,0x76,0xf4,0x98,0x06,0xbe,0x3f,0x73,0x3a,0x70,0xe3,0x59,0x45,0x55,0x74, - 0xd0,0x92,0x6e,0x1b,0x62,0x73,0x8d,0x91,0x64,0x45,0x81,0xee,0x83,0xe7,0x4f,0x1f, - 0xf7,0xfb,0x9e,0x3f,0xd9,0xe8,0xfd,0x96,0x7b,0x0f,0x1e,0x3d,0xa5,0x21,0xa3,0x98, - 0x62,0xba,0x39,0x83,0xc6,0x0e,0x9e,0x62,0xf8,0x7a,0xa8,0xbf,0x7d,0xda,0x1f,0x60, - 0x56,0x9a,0xb8,0xb1,0xd7,0x1c,0x78,0xdf,0x89,0x85,0xb3,0x3f,0xd9,0x77,0x26,0xce, - 0x00,0x03,0x93,0x66,0x80,0x7a,0x30,0x30,0xdd,0x77,0xa6,0x8e,0x5d,0x34,0x6d,0xc6, - 0xa8,0x13,0x63,0xd7,0xfb,0x8e,0x76,0x9a,0xdb,0x02,0xd3,0xd8,0xe9,0x1c,0x27,0xca, - 0xb4,0xab,0x32,0x3f,0xf2,0x96,0x2a,0x0b,0x58,0x30,0x48,0x15,0x86,0x0e,0xc2,0x34, - 0x2c,0x8e,0x3a,0x33,0xcb,0x59,0xed,0xa6,0xb1,0x5f,0xd3,0x5c,0x3a,0xba,0x1a,0x82, - 0x27,0xb1,0x37,0xd8,0xba,0xd8,0x43,0x7b,0x0d,0x26,0xba,0xb9,0x71,0x40,0xbc,0x83, - 0x03,0xa1,0x74,0xf9,0x77,0x68,0xa7,0xaa,0xf1,0xaf,0xe4,0xeb,0x09,0x24,0x44,0x8d, - 0x8b,0xe0,0x20,0x86,0xef,0x15,0xc8,0x06,0xf9,0x08,0x6e,0x04,0x0c,0x48,0x0c,0x1b, - 0xf4,0x71,0xc2,0x3e,0x4e,0x0b,0x09,0x87,0x36,0x26,0x40,0x91,0x9c,0x69,0x6a,0x63, - 0x41,0xa5,0x62,0x98,0x42,0x18,0xcb,0x0a,0x19,0x56,0x41,0xb6,0x0b,0xc6,0x51,0xcc, - 0x81,0x86,0x98,0xe5,0xf0,0x9d,0x48,0x22,0x62,0x58,0x4e,0x5e,0x61,0xe2,0x9c,0x8e, - 0xdd,0x24,0x58,0x52,0x44,0x95,0xd4,0x13,0xb8,0xb0,0x02,0xc6,0x56,0x5d,0x23,0x1e, - 0x84,0x27,0xcc,0xd2,0x4b,0x85,0xa1,0x09,0x19,0x4d,0x4a,0xa1,0xc8,0x6e,0x66,0xb3, - 0x71,0xb0,0x43,0xa7,0xfe,0xf0,0x61,0xb8,0x2c,0xc9,0xe3,0x15,0x79,0xe8,0xf0,0x0f, - 0xcc,0x71,0x7c,0x50,0x20,0x74,0xf8,0x7c,0xa2,0x4c,0xd1,0xd6,0xf3,0xd0,0xa1,0x4a, - 0xda,0xdc,0xe0,0xc9,0xb9,0x82,0xe3,0xc3,0xfc,0xa6,0x51,0xe8,0x98,0x4d,0x5d,0x6e, - 0x79,0x0e,0x89,0x04,0xa2,0xb9,0xbf,0xcf,0x63,0x80,0xe0,0xfb,0xb3,0xb8,0x18,0x0b, - 0x97,0x6b,0x2f,0xd5,0xac,0xac,0x31,0xa3,0x46,0xee,0xb8,0x3d,0x4e,0x7d,0x18,0xda, - 0xe8,0x65,0x58,0x31,0xf9,0xd3,0x0c,0xff,0x50,0xff,0xa5,0x3d,0x38,0x75,0x0a,0xee, - 0x75,0xfc,0x11,0x14,0x3f,0x2f,0x66,0x58,0xd7,0xfc,0x02,0x2e,0x7c,0x92,0x38,0x2d, - 0x3d,0x67,0x35,0xe0,0x83,0x9e,0xfd,0x78,0xf1,0x1f,0x74,0x19,0xcc,0x5e,0x10,0xa0, - 0xaa,0xb4,0x26,0x27,0xde,0x28,0xb0,0xe3,0xcf,0xca,0x98,0x2f,0x8a,0x2b,0x15,0xa9, - 0xf4,0x8a,0xc6,0x1a,0x4e,0x12,0x08,0x86,0x01,0x39,0xa8,0x87,0x74,0x19,0x3a,0xe8, - 0x88,0x11,0x65,0xfb,0x74,0x7b,0x0a,0x54,0x79,0x3c,0x26,0xc1,0xfd,0xc6,0x75,0x66, - 0xa5,0xe3,0x6d,0x88,0x12,0x7a,0x20,0x82,0xa4,0x6f,0x71,0x00,0x3a,0x20,0xfe,0xf7, - 0x68,0x2a,0xf8,0x76,0x9f,0x4c,0xfc,0x33,0xd9,0x37,0x27,0xde,0x3e,0xfa,0x0d,0x24, - 0x44,0x32,0x13,0x92,0xc3,0x42,0x68,0xb9,0xa0,0xd0,0x04,0xae,0xb0,0x15,0x48,0xf6, - 0xbe,0x6b,0xa0,0xb4,0x59,0x91,0x04,0xe2,0x55,0x81,0x31,0x4c,0xd7,0x94,0x8f,0x53, - 0x64,0x63,0x40,0x01,0xf3,0x4a,0x8a,0xdf,0x7e,0xfd,0x27,0x01,0xb5,0xe1,0x4f,0xac, - 0x12,0x39,0x43,0xb4,0xde,0x46,0x47,0x45,0xaa,0x19,0x69,0xca,0xe4,0x21,0x85,0xf5, - 0x04,0x93,0x03,0xac,0x27,0xe5,0x72,0x92,0xb9,0xe3,0xc7,0xc1,0x15,0xa2,0x36,0x10, - 0x6a,0xdf,0xc5,0xb1,0xaf,0x28,0x7b,0xce,0xeb,0xbd,0xbd,0xf6,0xe7,0x83,0xe1,0xd0, - 0x89,0x80,0x80,0xe3,0x9d,0x38,0xe2,0x7f,0xff,0x87,0x08,0xd4,0x0c,0xed,0xaf,0x17, - 0xfc,0xbb,0x81,0x81,0x29,0x9d,0x71,0xdb,0x19,0x3a,0x8e,0x47,0xff,0x67,0x62,0xf2, - 0xbe,0xc8,0x7c,0x69,0x63,0xbe,0x05,0x85,0xc2,0xf6,0x9c,0xf5,0x50,0x55,0x39,0xfe, - 0x87,0x0f,0x9f,0xe2,0x80,0x65,0xfb,0x03,0xab,0xee,0xcf,0x37,0x37,0x1b,0x6d,0x5e, - 0xe2,0x1f,0xb0,0x38,0xdf,0x40,0x1e,0x6f,0x1a,0xb1,0x3a,0x00,0x3b,0x35,0x78,0xdd, - 0x9d,0x0c,0xb4,0x47,0x32,0x76,0x08,0x81,0xf5,0xfe,0x48,0x7f,0x0d,0x02,0x7a,0x71, - 0xe2,0x92,0xe0,0x7d,0x32,0x0d,0xde,0x89,0x7e,0x00,0x61,0xba,0x20,0xb3,0x53,0x1c, - 0x2a,0x73,0x6f,0x82,0x28,0x73,0x1a,0xe5,0xf0,0xa6,0x39,0x81,0xe1,0x0a,0x76,0x8d, - 0x7b,0xa6,0x1e,0x81,0x10,0x78,0x60,0x62,0xe7,0x79,0x0a,0x7d,0x11,0x14,0x19,0xeb, - 0x46,0xc3,0xed,0x5b,0x0b,0x44,0x45,0x53,0xc5,0xa2,0x15,0x88,0x33,0x70,0x57,0xc4, - 0x12,0xa6,0x82,0x21,0xf1,0xbb,0x08,0x63,0x78,0x0c,0xe7,0x60,0xaf,0xc0,0x7c,0xbf, - 0xb5,0x30,0xcc,0x60,0x7e,0x10,0xa1,0xd1,0x47,0x17,0x9b,0xe8,0x99,0xb2,0x71,0x81, - 0x3b,0x85,0x44,0x53,0x6d,0xd0,0xca,0x4a,0x4a,0x11,0xb9,0x98,0x43,0xe6,0x48,0x60, - 0x48,0x3c,0x24,0x6b,0xa7,0x29,0x1f,0x01,0x5a,0xec,0x1b,0xe3,0xd8,0xe6,0xd1,0x24, - 0x35,0xac,0xca,0x23,0x95,0x14,0x95,0x09,0xa0,0x19,0x96,0x89,0xfd,0xd8,0xea,0x11, - 0xb2,0x01,0xf7,0x9a,0xa1,0x53,0x82,0x4b,0xd1,0x77,0x15,0x1b,0xf4,0x8d,0x48,0x5b, - 0xf9,0xa3,0xd9,0x14,0x52,0xca,0x2c,0x33,0x11,0x65,0xc5,0x65,0x01,0x42,0xb8,0x64, - 0xad,0xa0,0x4b,0xee,0x52,0xc5,0x06,0x56,0x54,0xd0,0x69,0x05,0x3b,0x47,0xe1,0xd2, - 0x9c,0x0b,0x64,0xb0,0x54,0x49,0x25,0x6d,0xea,0x44,0xa1,0x50,0x95,0xb9,0x09,0x25, - 0x49,0xbd,0xed,0x09,0xe8,0x84,0x5c,0xda,0x32,0xc0,0x0c,0x99,0x2c,0x95,0xb6,0x0e, - 0x06,0xed,0xb1,0x1b,0x07,0xe2,0x9d,0x4a,0x08,0x3f,0xca,0x72,0xea,0x56,0xe7,0xb5, - 0x4c,0x2d,0x4e,0xbb,0x92,0x2c,0x9c,0x2f,0x9e,0xf6,0x05,0x55,0x13,0x84,0x1c,0x91, - 0x31,0x93,0x04,0xee,0x1f,0x2a,0x37,0xf8,0xf8,0x0d,0x87,0x3b,0x8a,0x5c,0xaa,0xa2, - 0x04,0xaf,0x35,0xce,0xcb,0x66,0xc2,0x40,0xc3,0x24,0xb6,0x13,0xad,0x99,0x08,0xc4, - 0x77,0xb0,0x2c,0x05,0x98,0xce,0x69,0x4c,0xb8,0x05,0xb9,0xa1,0xa3,0xe1,0x9c,0xcf, - 0x86,0x88,0xce,0xd8,0x42,0x13,0x49,0x26,0xc7,0x74,0x1e,0x8a,0xe0,0x73,0xc4,0xf4, - 0x45,0x3e,0xc6,0xa9,0xf9,0x75,0x06,0x1b,0x90,0xc0,0x46,0x11,0x06,0xea,0xf0,0xc1, - 0x83,0x38,0xe8,0x10,0x76,0xd0,0xc4,0x18,0xe0,0xd3,0x30,0x0e,0x78,0x17,0x52,0x08, - 0x2b,0xaa,0xef,0x59,0x4c,0x6e,0x6e,0xcc,0xe2,0xb5,0x26,0xcc,0xa1,0xb2,0xa6,0xef, - 0xc4,0x61,0xf4,0x72,0x0d,0xdb,0xce,0xf6,0x19,0xe7,0x32,0x06,0xb5,0x13,0xe7,0x98, - 0x2d,0x4f,0xc7,0xea,0x5c,0x23,0xe2,0x41,0x88,0xe2,0x85,0x1c,0xa3,0xd8,0x0d,0x4f, - 0x1c,0x73,0x3c,0x27,0xdc,0xda,0xf8,0xc4,0x31,0x72,0xca,0x50,0xad,0x9b,0x80,0x4e, - 0x32,0x47,0xd6,0x08,0xb0,0xae,0xd3,0x01,0xb6,0x94,0x96,0x8d,0xf8,0x5a,0xeb,0xd6, - 0xc4,0x29,0x8d,0x7b,0x37,0xe2,0x64,0xb2,0x38,0xda,0x60,0xa4,0xe8,0x00,0x45,0xa9, - 0xf2,0xd0,0x8e,0xcd,0xd8,0x90,0x1a,0x50,0x20,0xef,0xac,0xe4,0x12,0xc4,0x88,0x3c, - 0x21,0x29,0x91,0xa4,0x65,0x53,0x48,0x37,0xbd,0x4d,0xe2,0xec,0x73,0x0d,0x11,0x09, - 0xd0,0x94,0xcb,0x08,0x36,0x17,0x05,0x87,0xc8,0xbd,0x29,0xe6,0xc7,0x37,0x4c,0x42, - 0xca,0xbc,0xb7,0xdc,0x91,0x39,0x12,0xd3,0xe6,0xc4,0xf9,0x78,0x87,0xba,0x10,0xb1, - 0x23,0x32,0x1f,0x46,0x47,0x7a,0x08,0xeb,0xa9,0x32,0x82,0x68,0x02,0xd6,0x87,0xb6, - 0x97,0x1b,0x95,0xa8,0x46,0x7c,0x4c,0x68,0xc6,0xfc,0x21,0x2d,0x90,0x46,0x63,0x06, - 0x02,0x46,0x6a,0xc2,0xcb,0x38,0x12,0x40,0xd4,0x82,0xb8,0x17,0xb9,0x99,0x15,0x27, - 0x99,0x41,0xe9,0x46,0x14,0xa7,0x24,0x4a,0x02,0x00,0x8c,0x92,0x34,0x1e,0x1a,0xe2, - 0x2e,0xd9,0xe8,0xcc,0x58,0x26,0xc9,0xa3,0xb1,0xc4,0xc1,0x28,0x2d,0x48,0x2d,0x55, - 0x96,0x30,0xec,0x94,0xe6,0x41,0xa7,0x67,0x30,0x0b,0x20,0x18,0x82,0x22,0xc8,0x7b, - 0xc2,0x3b,0x92,0xaf,0x25,0x65,0x02,0xb2,0x26,0x3b,0x1f,0xc3,0xa2,0xb3,0xad,0xbb, - 0xcc,0x8b,0x91,0x66,0x6d,0x6e,0x4e,0x61,0x8c,0x29,0x6d,0x4c,0x96,0x31,0x86,0xb9, - 0x45,0xf0,0x55,0xa7,0x59,0xc7,0x22,0xd8,0xf8,0x47,0x0f,0x60,0x8d,0x29,0x50,0x6b, - 0x34,0xf2,0x6f,0x69,0xef,0x6d,0x6a,0x75,0x1a,0x2b,0x32,0xb2,0xab,0x97,0x9a,0xb5, - 0x8e,0xe8,0x08,0x84,0x65,0x74,0xd9,0xaa,0xaf,0x9c,0xb6,0xa4,0x63,0x8b,0x53,0xa6, - 0x18,0x94,0xe4,0x82,0xd9,0x92,0x74,0x6d,0x07,0x30,0x1c,0x23,0xcc,0x08,0x1c,0xe6, - 0x56,0x68,0x45,0xd9,0xb2,0xce,0x94,0x50,0x8b,0x24,0xf1,0xed,0xae,0x76,0x03,0x1b, - 0x17,0x06,0xb0,0x9a,0x0b,0x3e,0x27,0x51,0xc5,0x96,0xd1,0x49,0xa8,0x4d,0x68,0x0a, - 0x3e,0xc7,0x85,0x15,0x50,0xeb,0x47,0x3e,0xeb,0x41,0xda,0xed,0xb7,0x14,0xa7,0x49, - 0x7d,0x98,0x62,0x46,0xb2,0x49,0xfe,0x20,0x65,0x1c,0x88,0x18,0x5d,0x1a,0x6c,0xe8, - 0x00,0x48,0x38,0x23,0x8a,0x70,0x50,0xd3,0x18,0xf1,0x18,0x76,0x31,0x20,0x95,0x62, - 0x67,0xc7,0x86,0xa4,0xd4,0xb0,0x14,0x2c,0x40,0x3f,0xea,0x93,0x43,0xf5,0xf4,0xa0, - 0x6d,0x85,0x7d,0x1b,0x67,0x61,0xce,0x96,0x64,0xaf,0x57,0xb8,0x46,0x55,0xe1,0x93, - 0x9c,0x10,0xf3,0xda,0x04,0xe9,0x90,0xf2,0x20,0x5e,0x18,0xc2,0xb9,0x12,0x20,0xca, - 0x1b,0x1a,0x60,0xbe,0x59,0xc5,0x4b,0x8e,0x87,0x8f,0x9f,0x9c,0x38,0xc5,0xa5,0x6d, - 0x1c,0x3d,0x69,0x15,0x9f,0x4c,0x81,0xd7,0xe0,0x50,0x7f,0x0e,0x85,0xbd,0xbd,0x07, - 0x0c,0xcd,0x6d,0x7b,0x8c,0x55,0xda,0xcc,0xd5,0xa6,0xda,0x69,0x10,0x01,0x34,0xe8, - 0xd5,0x2d,0x70,0x64,0x0d,0x40,0x58,0x8a,0x9c,0x8c,0xda,0x0d,0x11,0x38,0xf5,0x9c, - 0x13,0xc7,0x0e,0xfc,0xf6,0xeb,0x7f,0x3b,0x8d,0xa1,0x33,0x13,0x28,0x7c,0x32,0xbf, - 0xbe,0xd3,0xe7,0x5c,0x1e,0x43,0xf8,0x24,0x5c,0x53,0x29,0xf3,0xb0,0x1f,0x1a,0x23, - 0x5b,0x7b,0xf5,0x6c,0x80,0x41,0xc6,0xe2,0xf6,0xde,0x4d,0x8c,0x3c,0xa0,0xe1,0xf2, - 0xf6,0x30,0x87,0x43,0xda,0x22,0x5f,0x4e,0x6f,0x4d,0x30,0x5b,0xea,0xd6,0xa4,0x67, - 0xf4,0x10,0x93,0x4f,0xc4,0x8f,0xc0,0x10,0xe0,0xd0,0xd5,0xde,0x46,0x5f,0x37,0xaa, - 0xb2,0xb2,0x90,0xc5,0x96,0x0c,0xfc,0x76,0xce,0x24,0x87,0x59,0x0c,0x3a,0xc2,0xbb, - 0x70,0xf4,0xcd,0xdb,0x37,0x65,0xd7,0xed,0xd8,0x9b,0xab,0xba,0x9d,0x2a,0x61,0x08, - 0xcf,0xd1,0x2e,0xdf,0x62,0x09,0xa4,0xc3,0xa6,0xb5,0xc4,0x19,0x21,0x86,0x82,0x6f, - 0x81,0xec,0x0a,0xfa,0xf9,0x85,0x05,0xfb,0x98,0x9b,0x15,0x11,0x05,0x27,0xcd,0x7c, - 0xd3,0xfc,0xe2,0x1a,0x10,0x90,0xb3,0x03,0x5a,0xc0,0xbf,0x6f,0xcf,0x66,0x29,0xa1, - 0x73,0xaf,0x84,0xca,0xe0,0x2c,0xbe,0x7a,0xe4,0x1f,0x8a,0xee,0x81,0xe1,0xd9,0x74, - 0x4a,0xf9,0x24,0x59,0xac,0xc0,0x00,0xda,0x59,0x7b,0x2e,0x9e,0x52,0xd9,0x52,0x1a, - 0xa7,0xf1,0x8d,0xa8,0x98,0x0a,0x35,0xc7,0xbc,0xd5,0x95,0xf2,0x9a,0x54,0x80,0xaf, - 0x5d,0x59,0x73,0xa7,0x2c,0x1e,0xda,0xb7,0x61,0x65,0x61,0xad,0x47,0x56,0xcc,0x2a, - 0x4a,0x51,0x32,0x8e,0x89,0x6a,0x0a,0xfa,0xc8,0xb2,0x61,0x09,0x19,0xea,0x84,0x8e, - 0x65,0xe3,0xa6,0x90,0x32,0x5f,0x2e,0x06,0x56,0xb0,0xb5,0x71,0x13,0x71,0xb0,0x79, - 0xe2,0xb8,0xca,0x9a,0xf7,0xe7,0xe2,0xfb,0x33,0xc1,0xe5,0xda,0x26,0xd0,0xa8,0xf4, - 0xd5,0xd0,0x6d,0x04,0xcc,0x20,0x4a,0x1a,0x72,0x27,0xe2,0x37,0x37,0x47,0x8f,0x9f, - 0x3c,0x7b,0x6a,0xc5,0x14,0x2b,0xb7,0xe8,0x75,0x39,0x72,0xd1,0xb9,0x0e,0xc1,0x27, - 0xa9,0xe3,0xa3,0x2f,0x0e,0xa8,0x4c,0xdd,0xd4,0xac,0x61,0x1b,0x36,0xda,0xc7,0x58, - 0xb2,0xff,0xfc,0xf0,0xc5,0x91,0xb1,0x11,0xb7,0x07,0xef,0x34,0x17,0x93,0xec,0xf6, - 0xde,0x66,0xa9,0x2d,0xd0,0xb7,0x13,0xcb,0xcf,0x4d,0x7c,0xaf,0x2b,0x7a,0xf6,0xb6, - 0x63,0x2b,0x2b,0x41,0x02,0x1d,0x87,0x9f,0x52,0xb1,0x55,0x05,0xa2,0x0e,0x59,0x67, - 0xb2,0x44,0xfa,0x47,0x8a,0xbf,0x21,0xe9,0xdc,0xe0,0x9b,0x9a,0x01,0x4f,0x32,0x59, - 0x9a,0xe6,0x73,0x62,0xb8,0x47,0x0b,0xd8,0x32,0xd2,0xf5,0x00,0x08,0xed,0xec,0x9b, - 0x01,0x93,0x5b,0x19,0x4b,0x47,0xd5,0x5f,0x20,0xc5,0xe5,0xdf,0xc0,0xbc,0x90,0xa4, - 0x29,0x27,0xb4,0xf6,0x00,0x3f,0xfe,0x00,0xc1,0x0d,0xfb,0x80,0xf2,0xed,0x86,0xb0, - 0xae,0xf7,0xe2,0x20,0x6e,0x6a,0x90,0x45,0xcb,0xd0,0xe5,0x73,0xa0,0x9d,0xbe,0x33, - 0x80,0x9c,0xb6,0xa8,0xb2,0x95,0x48,0xf4,0xb6,0xdd,0xb0,0x3d,0x5b,0x56,0x03,0x32, - 0x74,0x61,0x2a,0xc6,0x26,0xe0,0x0d,0xe0,0xa1,0x29,0xcf,0x29,0xda,0x5a,0xb3,0xad, - 0x30,0xc3,0x2b,0xb2,0xd0,0xda,0x12,0x32,0x95,0xa6,0x5d,0x13,0x69,0x40,0xd8,0x11, - 0xfe,0x37,0x11,0x70,0x27,0x4b,0xaf,0x6c,0x8d,0x91,0x9e,0xb9,0x7a,0x7e,0xeb,0xc2, - 0xa4,0xd5,0x64,0xce,0x4e,0xcc,0x05,0x37,0x64,0x9d,0x72,0xe6,0x46,0x64,0x27,0xe4, - 0xd1,0xec,0x46,0x8d,0x25,0xe3,0xc3,0x69,0x4d,0x27,0x99,0xa7,0x49,0x7a,0x02,0x1b, - 0xa0,0xd3,0x18,0xd2,0x74,0x95,0xca,0x3f,0x37,0x53,0x27,0x3a,0x98,0x21,0x7c,0x6d, - 0x70,0xa4,0x1c,0x10,0x47,0xb5,0x0f,0x20,0x1c,0xdf,0xda,0x7e,0x5e,0x6f,0x65,0xf1, - 0x8e,0xd5,0x77,0x08,0x63,0x75,0xdb,0x7d,0x5a,0x24,0x2a,0x60,0x01,0x69,0x88,0xcf, - 0xa6,0x5d,0xef,0x94,0x96,0x9f,0x9b,0x9f,0x96,0xcd,0x34,0x6b,0x54,0x46,0x85,0xac, - 0x62,0x6b,0x53,0xf8,0x2d,0x65,0x93,0x78,0xac,0x2b,0x0a,0x94,0x0a,0x81,0x21,0x26, - 0xea,0x31,0xe1,0x95,0x09,0xf0,0xa9,0x88,0xd1,0xd0,0x2c,0x9f,0x0e,0x3f,0xfd,0xdc, - 0x14,0x11,0x41,0x3c,0x5d,0x9f,0x12,0x34,0x2f,0x9f,0x9a,0xd7,0xdc,0x9d,0xbe,0x7d, - 0x27,0x20,0x73,0x9b,0x99,0xe3,0xb5,0xf3,0xc9,0x2a,0xd0,0xdf,0x07,0xc3,0xe1,0xe7, - 0x01,0x6c,0xae,0xc5,0x51,0xf3,0x6d,0x5f,0x86,0xf9,0x24,0x32,0xae,0x29,0x55,0x78, - 0x9c,0x9a,0xaf,0x0d,0x06,0x18,0x68,0xc9,0x0c,0x89,0xe3,0x5a,0x06,0x9a,0xc4,0xc9, - 0x7d,0x17,0x3f,0xd2,0xb2,0xa9,0x70,0x70,0xc3,0x94,0x2d,0xc0,0xbe,0xf5,0xa2,0xcb, - 0x36,0x9d,0x20,0xd5,0xd7,0xdb,0x9b,0x63,0x1a,0x15,0xd6,0xdb,0x4d,0x79,0x52,0x74, - 0x7b,0x12,0x60,0xe1,0x5f,0x5b,0x41,0xef,0x30,0x0e,0xe2,0x70,0x8b,0x73,0x6b,0x11, - 0x39,0x71,0xda,0x00,0xf7,0xb7,0x5f,0xff,0x4b,0x37,0xba,0x00,0x94,0x3a,0xc1,0xa4, - 0xd3,0xe8,0x15,0x7c,0x4b,0x13,0xa7,0xd3,0xe5,0x8a,0x30,0xf7,0x71,0xeb,0x3a,0xd6, - 0x04,0xee,0x12,0x76,0xfe,0xfc,0xdf,0x3e,0x7e,0x64,0x4f,0xe1,0x23,0xc8,0x2f,0xe5, - 0x28,0xcd,0xd2,0x7a,0xd1,0x1b,0x4b,0xaa,0x67,0xf5,0x6c,0xae,0x23,0xa8,0xfc,0x63, - 0x56,0x55,0x8a,0x1e,0xf7,0x98,0xa0,0x5f,0x35,0x1b,0x38,0xda,0xe6,0xfa,0x08,0xf7, - 0xa9,0xa4,0x4a,0xd7,0x7e,0x5b,0x43,0x06,0x52,0xa7,0xfe,0x01,0x65,0x5c,0x4f,0x21, - 0xe9,0x23,0xf0,0x3e,0x43,0x05,0x5d,0x10,0x67,0xb7,0xd5,0x53,0xf3,0x24,0x26,0x10, - 0x2e,0xbd,0x20,0x31,0x70,0x3a,0xcf,0x48,0x6c,0x44,0x1d,0xc9,0xaa,0x4a,0x95,0x6e, - 0x33,0x62,0xf3,0x9e,0xc5,0x96,0x7b,0xcd,0x3e,0x50,0x7d,0xa4,0xf8,0x79,0x1c,0x78, - 0x3b,0x70,0xab,0x80,0xf2,0xdb,0x3f,0x7f,0xc5,0x7f,0x62,0xfb,0x61,0x8a,0xed,0xff, - 0xff,0xfa,0x8f,0x30,0x7b,0xd3,0x79,0x92,0x53,0x98,0x04,0x29,0xa3,0x52,0x50,0x2d, - 0xec,0xdb,0x1c,0xe8,0xe9,0xee,0xa5,0x5a,0x84,0xd0,0xbd,0x6c,0xa6,0x76,0x99,0x81, - 0xf4,0x5a,0x47,0xd1,0x33,0x20,0x51,0xf0,0x77,0x31,0x9a,0x20,0xc5,0xf6,0x8d,0x10, - 0x7c,0xbe,0x34,0xaf,0x84,0xcc,0x3d,0x3e,0xdd,0xb5,0x6a,0x3d,0x53,0x26,0x71,0x8d, - 0xb8,0xec,0x48,0xf5,0x45,0xae,0x98,0x98,0xcb,0x53,0xd9,0xa9,0x5f,0x98,0x32,0x38, - 0x55,0xb8,0x83,0x75,0x5d,0x35,0x01,0x4e,0x6e,0xe1,0x97,0x7e,0x31,0xab,0xd9,0xa7, - 0x21,0x21,0x72,0xc9,0x18,0x5c,0x52,0x39,0xbd,0xe8,0xb8,0xb9,0xab,0x61,0xf1,0xe9, - 0xf2,0x67,0x3f,0x1f,0x96,0x27,0x25,0x54,0x18,0x4e,0x2b,0xbc,0x34,0xfe,0x0a,0xea, - 0x7f,0xb5,0xb7,0x47,0x6f,0x86,0x70,0xce,0x2b,0x8a,0x98,0x0d,0xf2,0x0e,0xa2,0xf2, - 0xd3,0xaa,0x92,0x8b,0x20,0xd5,0xfc,0xd7,0xbd,0xf2,0x3c,0xde,0xf0,0xca,0xcf,0x79, - 0x43,0xb3,0x9e,0x5d,0x17,0x9a,0xc6,0x48,0xe4,0xfb,0x0e,0x47,0x8d,0xdb,0x2b,0x4f, - 0xae,0x82,0xa9,0x2c,0xd7,0x9f,0x1b,0x5c,0xb7,0xd7,0x34,0xd7,0xed,0xe6,0xd7,0x9d, - 0xcd,0x4f,0xfe,0x72,0xf1,0xd7,0x1f,0xe0,0xe5,0xa8,0x86,0x91,0x26,0x0b,0xcc,0x0f, - 0xaf,0x57,0x9e,0x35,0x2c,0x3e,0xcc,0x4a,0x78,0x65,0x1c,0x2e,0xf9,0xbd,0xe6,0xb3, - 0x88,0x59,0x4d,0xd7,0x1c,0x20,0xd4,0xa4,0xae,0xcb,0x90,0xa2,0x23,0x5b,0xac,0x81, - 0xc3,0x89,0x48,0x0d,0xf9,0xfd,0xe2,0x75,0xcd,0xcc,0xc8,0xe5,0x55,0x0a,0x25,0x2b, - 0x2a,0xb2,0x02,0x25,0x5b,0x61,0x9a,0x3f,0xd3,0x33,0x99,0x21,0x77,0x9f,0x82,0x39, - 0x54,0xe6,0x05,0x14,0x4e,0xae,0x9b,0x5b,0x54,0x69,0x2e,0x1c,0x07,0x42,0x5d,0xab, - 0xe8,0x55,0x31,0x9d,0x22,0x2f,0x76,0x1d,0x7a,0x60,0xe5,0x78,0x6b,0x8d,0x31,0x0a, - 0xd4,0x49,0x70,0xc1,0x0f,0xbe,0x11,0x61,0xb1,0xa2,0x5a,0xa9,0x36,0x61,0x5e,0x1b, - 0x19,0x72,0x1e,0x3e,0xa2,0xea,0x63,0x22,0x6d,0x22,0x4d,0xa8,0x12,0x4a,0x54,0x5c, - 0xe3,0x2c,0x91,0x76,0x21,0x0d,0xa7,0x5b,0x8a,0x8e,0x14,0x64,0x6a,0x2c,0xa3,0x05, - 0x29,0x91,0x5b,0xb7,0x97,0x54,0x72,0xd8,0xde,0x98,0xd1,0xeb,0xff,0x5a,0xd9,0x4b, - 0x33,0xd7,0x69,0x5e,0x60,0x21,0x87,0x2b,0x2e,0x87,0x89,0xcc,0xe8,0xbd,0xaa,0x10, - 0x32,0x60,0x49,0x1e,0xd6,0x03,0x49,0xd1,0xf5,0x69,0x0d,0xda,0xc3,0xe7,0x2b,0x38, - 0x3f,0xfb,0xba,0x0b,0x81,0x88,0xb1,0xbc,0xd2,0x86,0x1f,0x91,0xd6,0x1f,0x01,0x6c, - 0xe8,0xb4,0x8f,0x41,0x39,0x62,0xe5,0x97,0xa0,0x7d,0xf3,0xf4,0xb3,0x3f,0x68,0x9e, - 0x1c,0xf6,0xd9,0xc4,0xb6,0x48,0x51,0x81,0x3e,0x30,0xe6,0xe0,0xd5,0x24,0xcd,0x62, - 0x57,0x36,0xa0,0x55,0x06,0x01,0x70,0x3d,0x83,0xc6,0x05,0xb7,0x00,0xfa,0x83,0xcc, - 0xc7,0xca,0xed,0xfb,0x75,0x90,0xa9,0x7c,0x5c,0x4f,0x78,0x76,0x5d,0x2d,0x96,0x38, - 0x43,0x0b,0xf4,0x0e,0x9e,0xac,0x22,0xaa,0x0e,0xd1,0x27,0x5a,0xab,0x5b,0xdb,0x57, - 0x08,0x7e,0xaf,0x54,0x77,0x7b,0xfa,0xfe,0x66,0xeb,0xeb,0x1b,0x86,0x03,0x11,0x4b, - 0x28,0x78,0xa3,0xf3,0x77,0x2f,0xd2,0x30,0x44,0x14,0xb0,0x74,0xc7,0x6a,0xfb,0x49, - 0x5e,0xaa,0x2f,0x58,0xe2,0x5e,0x19,0x81,0xdb,0xdb,0xbb,0x43,0xda,0xee,0xec,0x0c, - 0xb8,0x28,0x45,0x30,0x3d,0xd6,0xae,0xe6,0xf3,0xa7,0x2f,0xcd,0xc4,0xee,0xe6,0x4b, - 0x22,0xfe,0x04,0xad,0xf3,0xc9,0xd2,0x86,0x60,0xac,0xf8,0x80,0x5f,0xfc,0x9c,0x6a, - 0x6b,0xc5,0x60,0xe1,0xf2,0xaa,0xd5,0xce,0x37,0x0d,0x35,0x03,0x88,0x3f,0x4c,0x63, - 0x5e,0xbf,0x4b,0x61,0xc8,0x72,0x45,0xe4,0x21,0x51,0x77,0xba,0xdb,0x5a,0x09,0x9c, - 0x0e,0x69,0x19,0x3d,0x22,0x84,0xa4,0x49,0xd3,0x20,0x62,0xb4,0x51,0xc7,0x03,0xaa, - 0x15,0x7b,0xcb,0xf7,0x6f,0x03,0x18,0x3e,0x04,0x12,0x45,0x63,0x5b,0x17,0xaa,0x86, - 0xac,0x29,0xba,0x87,0x98,0x36,0x1f,0x3d,0xad,0x2c,0x54,0x48,0x5b,0xf7,0x33,0x22, - 0xbb,0x82,0x43,0x06,0x3a,0xd4,0x6b,0x48,0xba,0x4b,0x29,0xe1,0x77,0x17,0x7f,0xbd, - 0x60,0xeb,0xe1,0x52,0xb0,0xce,0xe3,0x9c,0x62,0x02,0x45,0x1b,0xc7,0xfc,0x67,0xee, - 0xec,0xb3,0x3d,0x23,0x34,0x20,0xda,0xfe,0xa7,0x9f,0x1b,0x03,0x83,0x21,0x46,0xb2, - 0xc3,0xdf,0xad,0x6f,0xb5,0xbc,0xa5,0x0c,0xcc,0x63,0xc7,0x61,0x5d,0xcd,0xd4,0xa0, - 0x39,0x04,0xbf,0xe8,0xa4,0x90,0x1d,0x81,0x0a,0x90,0x5f,0x6d,0xd1,0x65,0x53,0xcd, - 0x2c,0x00,0xa3,0x82,0x32,0x48,0x20,0x9c,0xda,0x0a,0xbe,0xd5,0x01,0x5e,0xd2,0xc0, - 0x36,0xbd,0x6b,0xd3,0x60,0x2a,0xe9,0x54,0x97,0x63,0x11,0x4d,0x89,0x68,0x69,0x9e, - 0x14,0x4c,0x35,0x32,0x8d,0xc4,0x3b,0x8a,0xf2,0x01,0xa0,0xf3,0x8d,0x9e,0x7b,0x44, - 0x59,0xf3,0x80,0x7a,0xed,0xf7,0x79,0xa5,0x79,0x84,0xbf,0xb3,0xfe,0xc8,0xf4,0xc0, - 0xbc,0xb8,0x3d,0x30,0x9f,0xb4,0xfe,0x1f,0xad,0x8a,0x95,0x31,0xe3,0x3a,0x00,0x00, + 0x1f,0x8b,0x08,0x00,0x00,0x00,0x00,0x00,0x02,0xff,0x95,0x5b,0xdd,0x72,0xdb,0x38, + 0x96,0xbe,0xf7,0x53,0x20,0x4e,0xb7,0x49,0xb6,0x29,0x5a,0x52,0x6c,0xc7,0xa1,0x42, + 0x6b,0xfd,0xdb,0x93,0xed,0xa4,0x27,0x65,0xa7,0x77,0x6a,0x3b,0xdb,0x35,0x05,0x91, + 0xa0,0xc4,0x36,0x45,0x72,0x09,0x4a,0xb2,0x46,0x56,0x55,0x3f,0xc4,0xdc,0xec,0x0b, + 0xec,0x2b,0xec,0xfd,0x3e,0x4a,0x3f,0xc9,0x7e,0x07,0x00,0x25,0x4a,0x76,0x9c,0x6c, + 0xca,0x91,0x08,0xe0,0xe0,0xe0,0xe0,0xfc,0x1f,0x10,0x7a,0xfb,0x22,0xca,0xc3,0x6a, + 0x5e,0x08,0x36,0xaa,0xc6,0xe9,0xe9,0xce,0x5b,0xfa,0x62,0x29,0xcf,0x86,0xc1,0xae, + 0xc8,0x76,0xa9,0x43,0xf0,0xe8,0xf4,0xed,0x58,0x54,0x9c,0x85,0x23,0x5e,0x4a,0x51, + 0x05,0xbb,0x93,0x2a,0x6e,0x9d,0xd0,0xa0,0x0c,0xcb,0xa4,0xa8,0x98,0x2c,0xc3,0x60, + 0xf7,0xa0,0x88,0x5b,0x61,0x9e,0xc9,0x3c,0x15,0xde,0xef,0xb2,0x3f,0x0a,0x44,0x1c, + 0xc7,0xd1,0x51,0xfb,0x68,0xf7,0xf4,0xed,0x81,0x06,0x3c,0x35,0x13,0x4e,0x93,0xd8, + 0x7e,0x31,0x4b,0xb2,0x28,0x9f,0x79,0x1f,0xaf,0x1d,0x3b,0x9e,0x64,0x61,0x95,0xe4, + 0x99,0xed,0x2c,0xa6,0xbc,0x64,0x7f,0x0b,0xf4,0x98,0xfb,0x4b,0x60,0x1d,0xf0,0x22, + 0x39,0x90,0x15,0xaf,0x26,0xd2,0x72,0xdf,0x07,0x9f,0x7f,0x73,0x3f,0x04,0x5d,0xf1, + 0xc6,0xbd,0x75,0x7f,0x75,0x3f,0x06,0x7f,0xc3,0xf4,0x60,0x41,0xc3,0xc2,0xb7,0xd2, + 0x64,0x2a,0x2c,0x37,0x4a,0xca,0x6a,0xee,0xbf,0xe8,0xb8,0x53,0x3f,0x9b,0xa4,0xa9, + 0x3b,0x14,0x95,0xff,0xa3,0x9b,0x64,0x71,0x9a,0x0c,0x47,0x95,0xff,0xf3,0x64,0x3c, + 0x10,0xa5,0x3b,0xca,0xd3,0xc8,0xff,0xeb,0xe0,0x77,0x11,0x56,0xae,0xe4,0x73,0x7f, + 0x45,0x41,0xe5,0xde,0xb9,0xc2,0x59,0x88,0xbd,0x3d,0x5b,0x78,0x95,0xb8,0xaf,0x2e, + 0xf2,0xac,0x12,0x59,0x15,0x54,0xce,0xd2,0x1d,0x4c,0x64,0x03,0x74,0xe0,0x16,0xce, + 0xa2,0x6e,0xb1,0x18,0xc4,0x0f,0xbc,0x28,0x91,0x7c,0x90,0x8a,0x28,0x78,0xd1,0x59, + 0x36,0x5b,0xed,0x5e,0xe1,0x55,0x23,0x91,0xd9,0xb1,0x1b,0x3b,0xbd,0x52,0x54,0x93, + 0x32,0x63,0xc5,0xd2,0x2d,0xf2,0x34,0x5d,0x63,0x94,0xee,0xd8,0x0d,0xdd,0x5c,0x73, + 0xa1,0x72,0x63,0x77,0x18,0x74,0x7a,0xab,0x15,0x6e,0xb0,0xc2,0x70,0x6f,0xef,0x45, + 0x0c,0xda,0xe2,0xa0,0xe3,0x86,0xa9,0xe0,0xe5,0xa7,0x64,0x2c,0xf2,0x49,0x65,0x57, + 0x8e,0x6b,0x4b,0x2f,0xe4,0x69,0xda,0x97,0xb6,0xe3,0xff,0x68,0x4b,0xc7,0x31,0x2b, + 0xd6,0xd8,0x23,0x67,0x11,0xda,0x91,0x4b,0x5c,0xc1,0x5e,0x56,0xdd,0x82,0xba,0x15, + 0xab,0x84,0xb3,0x34,0x73,0x7e,0x72,0x7f,0x72,0x9c,0xe5,0x6a,0xe5,0x9f,0xb0,0x72, + 0x1c,0xb4,0x7b,0x5b,0x2b,0xf6,0x40,0x8d,0x5d,0x05,0x50,0x89,0xba,0xef,0xc6,0x1d, + 0x63,0x5e,0xbe,0xb7,0x97,0x7b,0x71,0x52,0xca,0x2a,0x08,0xc0,0x88,0x3e,0xa6,0xfb, + 0x20,0xde,0x6c,0x1b,0xe2,0xca,0x0b,0xbf,0x21,0xf2,0xe1,0x63,0xcc,0x4b,0x17,0x48, + 0xd7,0x30,0x99,0xb3,0x18,0x07,0x59,0x2f,0x7e,0x78,0x00,0xaa,0xa5,0x9b,0xe5,0x33, + 0xff,0x66,0x09,0x18,0xa5,0x16,0x6b,0xb0,0x10,0x3b,0xd9,0xdb,0x7b,0xef,0x15,0x13, + 0x39,0x42,0xa3,0x77,0xdb,0x47,0x33,0xb4,0x6f,0x1d,0xff,0xd7,0xfe,0xaf,0x1e,0x66, + 0x81,0x8e,0x5f,0x83,0x8f,0x1e,0x31,0xdd,0xfe,0xc5,0xfd,0xe0,0x5e,0x02,0xd9,0x8c, + 0x57,0xe1,0xe8,0x76,0x0b,0xd3,0xd8,0x59,0x7c,0xf4,0x34,0x7a,0xd0,0xfd,0xab,0x07, + 0x6a,0xec,0x0f,0xc1,0x07,0x5e,0x8d,0xbc,0x71,0x82,0x61,0xf7,0x83,0xa3,0xa6,0x26, + 0xd5,0x75,0x5e,0x5e,0x8a,0x69,0x12,0x8a,0xf5,0x64,0xc8,0x2f,0x0f,0xf2,0x87,0x87, + 0xc5,0xb2,0x16,0x74,0x26,0x66,0xec,0x63,0x99,0x8f,0x13,0x29,0xd6,0xe2,0x98,0xbb, + 0x99,0x16,0xf4,0x68,0x4d,0x52,0x57,0xbc,0x5a,0x0b,0x46,0x3a,0x0b,0x09,0x71,0xdb, + 0xb9,0x37,0x98,0x24,0x69,0xb4,0xb7,0x27,0xf5,0x43,0x10,0x98,0x1e,0x07,0xec,0x1f, + 0x79,0xc4,0x4e,0xdb,0x71,0xe7,0x24,0xf1,0xa5,0xd3,0x6b,0x48,0xa3,0xc1,0xe3,0x1a, + 0xac,0x97,0xd9,0x57,0x65,0x99,0x97,0xb6,0x55,0x69,0x20,0x8b,0x36,0x92,0x7b,0xa6, + 0xf5,0xf0,0xf0,0x46,0x1c,0x02,0xcb,0x72,0xb9,0x56,0xbb,0x1f,0xed,0x09,0xa9,0xa4, + 0xd9,0x4a,0x2c,0xc0,0x2e,0xd5,0xb3,0xa5,0x5d,0xa5,0xb3,0x20,0xab,0x2e,0xbd,0xfc, + 0xce,0xa9,0x46,0x65,0x3e,0x63,0x7a,0xa1,0xd2,0xf0,0x71,0xa5,0xf5,0x4a,0x3b,0xc8, + 0xb2,0xfa,0xa5,0xfa,0x82,0x50,0x4a,0x38,0x0c,0x22,0x73,0xd9,0xd0,0xb9,0x4b,0xda, + 0x3f,0x30,0xe2,0xf3,0x16,0x76,0x5e,0xc4,0x5a,0x46,0x81,0xec,0xbd,0xf7,0xe2,0xbc, + 0xbc,0xe2,0x20,0x63,0xb5,0x78,0x0c,0xfd,0x04,0x24,0x11,0xbe,0x74,0x6c,0x67,0xe5, + 0x6f,0x76,0xb4,0xe3,0xca,0xf8,0x58,0x04,0xbb,0xd3,0x44,0xcc,0x8a,0xbc,0xac,0x76, + 0x59,0x68,0x6c,0x7a,0x77,0x96,0x44,0xd5,0x28,0x88,0x94,0x00,0x5b,0xaa,0x01,0x4f, + 0x91,0x54,0x09,0x4f,0x5b,0x12,0xb6,0x24,0x82,0x0e,0x79,0xb9,0x2a,0xa9,0x52,0x71, + 0xfa,0x91,0x57,0x95,0x28,0xe1,0x47,0xb0,0xb5,0x16,0xd3,0xd4,0xbc,0x3d,0xd0,0x63, + 0xf0,0x84,0xd5,0x9c,0xbe,0x0f,0x7e,0x60,0x17,0xda,0x07,0xb2,0x01,0x97,0xc2,0x67, + 0x60,0x12,0x93,0x58,0x9e,0x0d,0xd2,0x3c,0xbc,0x63,0x79,0x21,0x32,0xc9,0xc4,0x54, + 0x94,0x73,0x50,0x51,0x0a,0x56,0xf0,0xa1,0xf0,0xd8,0x7b,0xf2,0x4d,0x2c,0xc9,0x98, + 0x5f,0xe6,0x79,0xc5,0x6c,0x9a,0x15,0x82,0x89,0x63,0x61,0xc9,0x1d,0xc6,0x30,0x42, + 0x9e,0x4a,0x44,0x4c,0x39,0x31,0x4c,0x4a,0x05,0x88,0x61,0xe4,0xc5,0x24,0x2d,0x21, + 0xcd,0x22,0x53,0x9e,0x4e,0x84,0xf4,0xd9,0x9d,0x10,0x85,0x5a,0xba,0x9a,0xe5,0x84, + 0x56,0x56,0xa2,0x70,0x5c,0xc2,0x14,0xf1,0xf2,0x8e,0x4d,0xb2,0x48,0x94,0xca,0xe3, + 0x7f,0x8e,0x78,0xc5,0x5b,0x80,0x04,0x83,0x68,0xe8,0x37,0x8f,0xfd,0x9c,0xb3,0x99, + 0x18,0xb0,0x18,0x3c,0x92,0x9a,0xfe,0x51,0x5e,0xc9,0x02,0x64,0x8d,0xb8,0x64,0x19, + 0xe1,0x23,0x3e,0x88,0xca,0x63,0x3f,0x1c,0xec,0x28,0x82,0x17,0x61,0x9e,0xe6,0x25, + 0x58,0x46,0x78,0x7c,0x45,0x63,0xaf,0xd5,0x0a,0x4b,0xc1,0xc7,0xfe,0xcb,0xeb,0xc3, + 0xab,0xeb,0xab,0x63,0xb4,0x0b,0x9e,0x89,0x14,0xed,0xeb,0xeb,0x8b,0xeb,0x33,0xb4, + 0x93,0xec,0xce,0x7f,0xd9,0x39,0xeb,0x9c,0x74,0x0e,0xd1,0x1a,0x4f,0xb0,0x3f,0xff, + 0xe5,0x71,0xf7,0xe8,0xe2,0xa8,0x83,0x76,0xcc,0xb1,0x8e,0xff,0xf2,0xf5,0xe5,0xeb, + 0xe3,0xe3,0x13,0xb4,0xcb,0x49,0x2a,0xfc,0x97,0x97,0x97,0x97,0x47,0x17,0xc7,0xa6, + 0xd9,0x92,0x79,0x0c,0x90,0xab,0x93,0xab,0xce,0x25,0xa1,0x18,0x8e,0x72,0x49,0xed, + 0x57,0x97,0x17,0x17,0x57,0x68,0xa7,0x84,0xf0,0xfa,0xfa,0xe8,0xa2,0x4b,0xad,0x01, + 0x47,0xeb,0xfc,0xe4,0xd5,0x45,0xe7,0x02,0xad,0x1c,0x6b,0x77,0xcf,0x5f,0x9f,0x1d, + 0x9e,0xa3,0x31,0xe3,0x65,0xe6,0xbf,0x3c,0x39,0x3a,0x6e,0xb7,0xaf,0x7a,0x3b,0xad, + 0x96,0xe4,0x99,0xf4,0xe5,0x1c,0x5c,0x1b,0xb7,0x26,0x89,0xdb,0xe2,0x45,0x41,0xab, + 0xa9,0x0e,0xd7,0xba,0x15,0xc3,0x5c,0xb0,0x5f,0xde,0x59,0xee,0x4d,0x3e,0xc8,0xab, + 0xdc,0x25,0xf0,0x96,0x14,0x65,0x12,0xd3,0x3e,0xf2,0x2c,0xf7,0x27,0x89,0xfa,0x96, + 0x05,0x0f,0x85,0x6b,0x5d,0x70,0x28,0x53,0x94,0x70,0xf6,0x01,0x7d,0x16,0x10,0x5c, + 0x9b,0xa7,0x0f,0x22,0x4b,0x73,0x57,0x2b,0x0c,0x97,0xae,0xa5,0xf1,0x99,0xc1,0x15, + 0x82,0xe5,0xce,0x93,0x92,0xda,0x64,0x3a,0x75,0xad,0x79,0xde,0xbe,0x68,0x9f,0xb7, + 0xdf,0xac,0x79,0xde,0x79,0xd5,0xe9,0x74,0xda,0x35,0xcf,0xaf,0x2e,0xaf,0x5e,0x5f, + 0x9e,0xaf,0x79,0x7e,0xf6,0xea,0xcd,0xf9,0xc9,0x59,0x83,0xe7,0x67,0xaf,0x5f,0x1d, + 0x1f,0xae,0x78,0xde,0x3d,0xef,0xbe,0xee,0x76,0x36,0x79,0xde,0xb9,0xee,0x5c,0x74, + 0x4e,0xd6,0x3c,0xef,0x9e,0x75,0x8f,0xbb,0xed,0x9a,0xcb,0xd7,0xd7,0xaf,0xcf,0x8e, + 0x8e,0x0c,0x97,0x8f,0x5e,0x9f,0x9f,0xbc,0xbe,0x5e,0x71,0xf9,0xf2,0xcd,0x59,0xfb, + 0xd5,0xf5,0x72,0xe7,0x87,0xc5,0x20,0xbf,0x6f,0xc9,0xe4,0x1f,0x49,0x36,0xf4,0x07, + 0x79,0x09,0x8d,0x6c,0xa1,0x67,0xf9,0x79,0x94,0x44,0x91,0xc8,0x7e,0x5b,0x20,0x78, + 0x16,0x29,0x42,0x73,0x96,0x67,0xe2,0x45,0x32,0x26,0xcb,0xe5,0x59,0xb5,0xdc,0x19, + 0xe4,0xd1,0x7c,0x31,0xe6,0xe5,0x30,0xc9,0xfc,0x76,0x6f,0xc0,0xc3,0xbb,0x61,0x99, + 0x43,0xa3,0x7d,0xf8,0x52,0xdb,0x70,0xc0,0xe9,0x29,0xe6,0x98,0x2e,0x6c,0xda,0xe9, + 0x91,0x3e,0xfb,0x9d,0xa3,0xe2,0xfe,0xa0,0xe3,0x1d,0x31,0x3d,0x40,0x82,0x73,0x7a, + 0x2d,0xa8,0xfb,0x5d,0x52,0xb5,0x08,0xa2,0x25,0xc7,0xd0,0xe7,0x11,0xd1,0x84,0xc5, + 0xe0,0x07,0x12,0x58,0x71,0xb4,0xdc,0xe1,0x9a,0xdb,0x7e,0x92,0x8d,0x20,0xe7,0x6a, + 0xe9,0xc7,0x79,0x38,0x91,0xad,0x69,0x22,0x13,0x04,0xf8,0x05,0x5c,0x67,0x9a,0x64, + 0xc2,0xef,0x16,0xf7,0x0c,0xb2,0x4c,0x22,0xb3,0x00,0x34,0xd0,0xe9,0x99,0xc1,0x56, + 0x1e,0xc7,0x14,0xd5,0x00,0xb3,0xdc,0xf1,0x66,0x25,0x2f,0xb0,0x8b,0x7b,0xed,0x75, + 0xfc,0xd7,0xdd,0x76,0x71,0xdf,0xab,0x77,0xc5,0xf8,0xa4,0xca,0x7b,0x05,0x8f,0x22, + 0x22,0x84,0x86,0x58,0xe7,0x18,0x1f,0x87,0x27,0x34,0x77,0xd4,0x5d,0x6d,0x9f,0xb5, + 0x19,0xfa,0x7b,0x9a,0xf2,0xe4,0x1f,0xc2,0xef,0xbc,0xaa,0x9b,0x33,0xa1,0xf2,0x1d, + 0xa8,0xf4,0x06,0x2f,0x94,0xc8,0x1d,0x10,0x10,0xf2,0x32,0x5a,0x3c,0xe2,0x9e,0xd2, + 0x17,0xa7,0xa7,0xe5,0xe1,0x77,0xb6,0xb6,0x43,0x0a,0x50,0x0f,0xb6,0x4a,0xa8,0x34, + 0x62,0x66,0x87,0x08,0xaf,0x49,0x25,0x2a,0x81,0x7b,0x50,0x65,0x2b,0xf1,0x25,0x99, + 0xda,0x7c,0x9c,0x8a,0xfb,0x1e,0xd8,0x39,0xcc,0x5a,0x09,0xac,0x48,0xfa,0xa1,0x20, + 0x37,0xd2,0xfb,0x7d,0x22,0xab,0x24,0x9e,0xb7,0x8c,0x4b,0xae,0xbb,0x87,0xbc,0xf0, + 0x69,0x67,0x88,0xb6,0xad,0x91,0xde,0xc9,0x61,0x73,0xa1,0xb6,0x62,0xc8,0xff,0x87, + 0xce,0x13,0x02,0xff,0xc2,0x76,0x9f,0x56,0x96,0xa3,0x36,0x56,0x39,0x04,0xea,0xa6, + 0xb2,0x50,0xc4,0x6a,0x45,0x02,0xbe,0x9b,0x53,0xe4,0x51,0xda,0xd9,0x0b,0x27,0xa5, + 0x04,0x82,0x22,0x57,0xae,0xb1,0x57,0x95,0x00,0x4d,0xd4,0xb0,0xa1,0x41,0x2d,0xc0, + 0xbc,0xce,0x91,0x74,0xd7,0x34,0xe8,0xf6,0x7a,0x68,0xb9,0xf3,0x2f,0x63,0x01,0x37, + 0x61,0x8f,0x72,0xc4,0x07,0x5f,0x7d,0x3a,0x0b,0x62,0xa6,0x7e,0x5e,0x34,0x91,0x35, + 0xa8,0x5d,0xc6,0x50,0x59,0xf8,0x73,0x6e,0xc0,0x1e,0x8d,0x6b,0x89,0xf8,0x75,0x36, + 0xba,0xc8,0xe1,0x55,0x12,0x24,0xc9,0xde,0xe1,0x51,0x4d,0x7a,0x24,0x62,0x3e,0x49, + 0xab,0xde,0x13,0x4b,0x28,0x66,0x02,0xc5,0x58,0x0e,0x17,0xda,0x88,0xba,0x2b,0x9e, + 0x90,0x8f,0xda,0x64,0x9f,0x72,0x21,0xce,0x12,0xf1,0x1a,0xc6,0xbf,0x41,0x0a,0xf2, + 0x02,0xd5,0x2d,0xca,0x4d,0x12,0xe1,0x31,0x80,0x5e,0xef,0x61,0xa5,0x35,0xdf,0xaa, + 0x2e,0xca,0x41,0xb6,0x06,0xa2,0x9a,0x09,0x91,0x29,0xad,0x51,0x7a,0xa1,0xed,0xa3, + 0x45,0x89,0xa6,0x52,0x1b,0xb3,0x2d,0x6a,0x3f,0xad,0x2b,0xdf,0xbc,0xb1,0x9d,0x9a, + 0xd9,0xdf,0xaa,0xe0,0xdb,0x1a,0xfc,0xd8,0x18,0x9f,0xd4,0x29,0x98,0x39,0x0a,0xaf, + 0xe7,0x38,0x42,0xf9,0x05,0xad,0xdc,0xa3,0x91,0x16,0xf9,0x13,0x9f,0x3e,0x14,0x13, + 0x48,0x6b,0x69,0x37,0x35,0x23,0x10,0x56,0xaa,0x7c,0xac,0x3c,0x09,0x10,0x77,0x16, + 0x34,0xc5,0xef,0xac,0xfc,0x4d,0xc3,0x81,0x74,0xbb,0x4f,0x38,0x10,0x95,0x6d,0x20, + 0xd4,0x90,0xde,0xc0,0xf8,0x5a,0x5e,0xbb,0x23,0xc6,0x50,0x09,0x39,0x19,0x7c,0xbb, + 0x4a,0xec,0x48,0xa1,0x92,0xb5,0x45,0x43,0x38,0x5d,0xe5,0xd0,0x90,0x9a,0x57,0xa2, + 0xf6,0x69,0xe8,0x81,0x57,0x6b,0x6f,0xfb,0xb4,0x27,0x7d,0x18,0xb4,0xda,0x8b,0x78, + 0xf6,0x58,0x9d,0xbe,0x64,0x48,0x04,0xfd,0x65,0x63,0x52,0x53,0x81,0x96,0x87,0x95, + 0xdc,0xe4,0xfb,0x16,0x8b,0x9f,0x10,0x33,0x71,0xfd,0x64,0x53,0xf3,0x3a,0xda,0xd3, + 0xdf,0x4d,0x1b,0xd1,0xea,0x2b,0x7a,0xa8,0xc0,0x4f,0xa3,0x64,0xfa,0x0d,0x62,0x57, + 0xca,0xde,0x6d,0xf8,0x44,0xc5,0xb9,0xde,0x2a,0x94,0x2a,0x89,0x3f,0xb5,0x8a,0x0a, + 0xe1,0xce,0x16,0x83,0xd5,0xca,0x2c,0xaa,0x8c,0x6a,0x30,0x13,0x83,0x9e,0x60,0xbb, + 0x82,0x8b,0xd6,0x7b,0x22,0x05,0xd7,0x41,0xac,0x5d,0x9b,0x91,0x77,0xb4,0xa5,0x0e, + 0x4a,0xc3,0xd5,0x2e,0xfc,0x52,0x65,0x87,0x24,0x02,0xca,0xa4,0x35,0x53,0x79,0x36, + 0x9f,0x21,0xae,0x42,0xe9,0x91,0x3f,0xff,0xf9,0xcf,0x3f,0xf0,0x67,0x12,0xec,0xba, + 0x85,0x4c,0x93,0xd6,0xf5,0x06,0x89,0x76,0x42,0x86,0xf0,0xc3,0xc7,0xca,0x8a,0x8d, + 0x3c,0xe5,0x78,0x28,0xf9,0xd8,0xe8,0xa5,0x0e,0xf4,0x43,0xe6,0x4f,0x78,0x23,0x74, + 0xaf,0x2d,0x4f,0x65,0xee,0x3d,0x63,0xc3,0xaf,0xd6,0x61,0xfa,0x48,0x69,0x2a,0x69, + 0xbe,0x62,0xd5,0x3a,0x98,0x77,0x8e,0xdb,0x4f,0x05,0x1b,0x95,0x2a,0xa9,0x25,0x4b, + 0x96,0x3c,0x8d,0xbd,0xd3,0x6e,0x7f,0xff,0x78,0x62,0xaa,0xb4,0xfd,0x65,0x58,0x10, + 0x1b,0xb7,0x26,0x9a,0x15,0x69,0x5e,0x8d,0xe3,0xa4,0xfd,0x58,0x0f,0x37,0x94,0x44, + 0x85,0xec,0x52,0x28,0x16,0x42,0x0e,0x55,0x82,0x52,0xe7,0x4b,0x09,0x53,0x57,0x25, + 0x4c,0x87,0x47,0x4d,0x71,0x82,0x3f,0xe4,0xde,0xe3,0x98,0xf4,0x90,0x19,0x7d,0x58, + 0x05,0x95,0xa3,0xe5,0x0e,0xea,0x2f,0x55,0x0e,0xbd,0x3d,0xd0,0xa7,0x47,0x94,0xb2, + 0x51,0x2d,0x06,0x47,0xc0,0x42,0x64,0xba,0x12,0x95,0x17,0xe4,0x5e,0x1f,0x2f,0x89, + 0xf2,0xf4,0xed,0xa8,0x73,0x5a,0x97,0x54,0x78,0x7c,0x0b,0x57,0xb3,0x02,0x85,0x97, + 0xd9,0x65,0x49,0x14,0xec,0x4e,0x30,0xa3,0x05,0xdc,0x18,0x33,0xa8,0x31,0x73,0x67, + 0xe7,0x2d,0xec,0xa5,0x86,0x25,0xd3,0xdd,0xc5,0x82,0x13,0x68,0xff,0x0a,0x01,0x0c, + 0x7f,0x97,0xd1,0x21,0x17,0x1e,0xd5,0x80,0x46,0x17,0xe6,0xc5,0x7c,0xf7,0xf4,0x02, + 0x9f,0x0c,0xce,0x62,0x88,0x6c,0x1b,0x9c,0xc0,0xfa,0x1a,0x66,0x93,0x06,0x04,0x3f, + 0x33,0xa9,0xa0,0xc7,0xd3,0x15,0x15,0x58,0x9b,0x2a,0x44,0x08,0x86,0x23,0xf1,0xac, + 0xc1,0x29,0xb9,0xaa,0xe1,0x69,0x6c,0x97,0x61,0x30,0xca,0xb3,0x74,0xce,0x74,0x7a, + 0xcb,0x78,0x99,0xf0,0x56,0xca,0x07,0x22,0x0d,0x76,0x2f,0xd7,0xab,0x13,0xe6,0x1a, + 0x19,0x6d,0xcd,0xf8,0x4c,0xf0,0xa7,0x7b,0xaa,0xcf,0x19,0xb0,0xf1,0xee,0xe9,0xdb, + 0x28,0xad,0x97,0xba,0x9b,0x82,0x8d,0x8c,0x11,0x13,0xd0,0x5d,0x9d,0x5e,0x27,0xe5, + 0x18,0xba,0x0d,0x38,0x34,0xde,0x46,0x91,0xa2,0x22,0x9e,0x29,0xce,0x45,0x51,0x4d, + 0x71,0x63,0xc2,0x39,0x1d,0x25,0x6c,0x40,0x0f,0xd2,0xe8,0xcb,0xe0,0xef,0xb9,0xac, + 0xb0,0x1b,0x14,0xf5,0x65,0xb5,0x31,0xab,0x2c,0x9f,0x5b,0x43,0xce,0x37,0x97,0x40, + 0xc7,0x97,0xc1,0x3f,0x52,0x5a,0xc6,0x48,0x3f,0x37,0x26,0x15,0xd9,0xd6,0x14,0x7c, + 0xa5,0x24,0x0a,0xc3,0xa4,0x35,0xbf,0x34,0xeb,0x4b,0xb9,0x6b,0xd8,0xad,0xf8,0xa7, + 0x28,0x0f,0x4b,0x2e,0x47,0x5f,0xe3,0xa1,0x29,0xf5,0x37,0x16,0x0f,0xcb,0xe2,0xcb, + 0x04,0x7f,0xe2,0xf2,0x6e,0x0b,0xba,0x7a,0x86,0x1b,0x30,0x70,0xe4,0x88,0xa1,0xd8, + 0x9a,0xf2,0x8c,0x90,0x2e,0xe8,0xac,0x20,0x9a,0x8c,0x8b,0xad,0x29,0xd1,0x53,0x1c, + 0xf9,0x26,0x8b,0x60,0x08,0x85,0x4f,0x5a,0x45,0x19,0xa6,0x90,0xe4,0x05,0x1d,0xc8, + 0x41,0xce,0x48,0x4a,0xa2,0xaf,0x99,0x44,0xf9,0xd8,0x24,0x9e,0x12,0x8a,0x12,0xc2, + 0x8d,0xa0,0xb3,0x87,0xaf,0x0b,0x00,0x0a,0xb0,0x29,0xfb,0xd9,0x33,0xea,0x75,0x5d, + 0xd2,0xe1,0x07,0xb2,0xa7,0x35,0x47,0xeb,0x8d,0x26,0x86,0xc8,0xb8,0x90,0x5f,0x9b, + 0x4f,0x67,0x60,0x9b,0x56,0xf3,0x8c,0x0c,0xcf,0xb0,0xa5,0x29,0x1d,0xdf,0x3c,0xd6, + 0x14,0x70,0xfc,0x69,0xa9,0x14,0x35,0x55,0x94,0xef,0xec,0xd6,0xdb,0x52,0x0d,0x40, + 0x16,0x5f,0x66,0x9a,0xd1,0x47,0xf9,0x35,0xb6,0x7d,0xca,0x2b,0xbe,0xc5,0xb6,0xea, + 0x79,0xc3,0xa7,0x43,0xa7,0x2a,0x67,0xf1,0x53,0x4e,0xa3,0x78,0x46,0xe1,0xdf,0xe7, + 0x70,0xbe,0x11,0x8b,0xcb,0x7c,0xcc,0x64,0x85,0xc4,0x75,0xb8,0x35,0x77,0xfc,0x15, + 0x0f,0x92,0x02,0x01,0x13,0x74,0x42,0xb8,0x31,0x2f,0x15,0xdf,0xcc,0x3a,0xc2,0xf0, + 0x0d,0xbc,0xfb,0x20,0xc6,0x79,0x39,0xff,0x1a,0xe7,0xde,0xa9,0x33,0x2d,0x9e,0x32, + 0x44,0x95,0x02,0xdb,0x12,0x9b,0xdb,0x19,0x25,0xcf,0x6d,0xa7,0x1c,0xc2,0x1d,0xaa, + 0x49,0xfa,0x94,0x6f,0x73,0x6a,0xfa,0x8c,0x9f,0xbb,0xbd,0x39,0xfb,0xf0,0xc4,0x6a, + 0xc5,0xb7,0x30,0x61,0x8b,0xe6,0x44,0xea,0xb3,0x46,0x84,0x9e,0x50,0x30,0x54,0x10, + 0xfa,0xec,0x4e,0x55,0xb8,0x7b,0xa5,0xfc,0xcf,0x49,0xde,0x93,0xec,0xf2,0xc3,0x19, + 0x1b,0x4c,0xe2,0x58,0x94,0x92,0xd1,0xeb,0x12,0x02,0x29,0x85,0xc7,0xce,0xd8,0x38, + 0x8f,0x90,0x1a,0x2a,0xb1,0x48,0xa6,0x02,0x55,0x12,0xb3,0xa4,0xda,0x81,0x1b,0x98, + 0x0a,0xc9,0xb4,0xe5,0x2b,0x37,0x2f,0xc1,0xba,0xee,0x21,0xfb,0xe9,0xdc,0x18,0xbc, + 0xa2,0xbf,0xc7,0x10,0xc9,0xf2,0x19,0xf0,0xf1,0x4a,0xad,0x9b,0xa1,0x24,0xcb,0xcb, + 0x3b,0x36,0xcc,0x31,0x5b,0xbd,0x0b,0x70,0xd9,0x6c,0x94,0xa4,0x62,0x4d,0x95,0x3a, + 0xa5,0x94,0x2c,0x2a,0xf9,0x0c,0x99,0x89,0xf7,0xac,0x14,0x6f,0x6b,0x1d,0xfb,0x26, + 0xc7,0xbd,0xad,0x92,0xa7,0x6b,0xf2,0x63,0xd9,0xc8,0x21,0x9a,0xee,0x0c,0x59,0x19, + 0x34,0x29,0x31,0x40,0xd4,0x62,0x2a,0x89,0x31,0x87,0xc4,0x7e,0x9b,0xf4,0x2c,0x69, + 0x78,0xb9,0xa7,0x04,0x6a,0xc8,0x7c,0x42,0xb1,0x63,0x29,0xbe,0x35,0x78,0xa9,0x0d, + 0xff,0xac,0xf9,0xf7,0xb5,0x0d,0xaf,0xc0,0x1a,0x4b,0xc9,0x67,0x3c,0xdd,0x2d,0x52, + 0xf0,0x2d,0x17,0x51,0x3e,0x03,0x7e,0x16,0x45,0x08,0xf5,0x72,0x03,0x3e,0x79,0xc6, + 0x2d,0xfc,0x0c,0x3f,0xba,0x09,0x9d,0x3d,0xe3,0x08,0xfe,0xa2,0xcf,0x95,0x37,0x75, + 0x5f,0x7e,0x15,0x9e,0x74,0x75,0xcb,0x60,0xe4,0xd7,0x57,0x01,0x07,0x13,0x94,0x6a, + 0x72,0x6b,0x62,0xf8,0xe5,0x89,0x37,0x82,0x87,0x23,0x38,0xb9,0x69,0xc2,0x37,0x26, + 0xa1,0xfd,0x55,0x51,0xea,0x53,0x82,0xd3,0xb7,0x9c,0x8d,0x4a,0x11,0x07,0xbb,0x07, + 0x94,0x68,0xaa,0xf7,0x02,0x6f,0x0f,0x78,0xa3,0xdb,0xc4,0x0f,0x6c,0x7a,0xed,0xe3, + 0x31,0x7e,0x60,0xe6,0x03,0x33,0x25,0xcd,0xab,0x37,0xad,0xa7,0x3b,0xab,0xf7,0x23, + 0xdf,0xd9,0xc9,0xea,0xa5,0x4c,0x94,0x87,0x93,0x31,0x36,0xe7,0x0d,0x45,0x75,0x95, + 0x0a,0x7a,0x3c,0x9f,0xbf,0x8b,0x00,0xb1,0x5c,0x4f,0xb8,0x1b,0xd8,0x83,0xd5,0x8c, + 0xc1,0x69,0xd0,0x69,0x1f,0x9e,0x1c,0xbd,0x3e,0xee,0xdb,0x83,0x03,0xf3,0xe8,0x78, + 0x55,0x7e,0x9d,0xdc,0x8b,0xc8,0xee,0x3a,0xfb,0x16,0xfb,0x70,0x6e,0xf9,0xea,0x95, + 0x97,0xaa,0x45,0x14,0x58,0xf7,0x90,0x06,0x7e,0x3a,0xb7,0x1a,0x78,0xa3,0x49,0x49, + 0x2f,0x69,0xc0,0x39,0x7a,0x99,0x15,0xe9,0xb7,0x64,0xa8,0xed,0x72,0x74,0x1f,0x9c, + 0x1c,0x1f,0xb6,0xdb,0x8e,0x3b,0xda,0xe8,0xfd,0x5e,0xf5,0x1e,0xbc,0x3a,0xa6,0x21, + 0x7a,0x29,0x81,0x7f,0xe3,0x4d,0x08,0x1a,0x3b,0x38,0xc6,0xf0,0x7d,0x20,0xbf,0x3f, + 0x6e,0xf7,0x00,0x95,0xc4,0x76,0xe4,0xd4,0x1b,0xde,0xb7,0x22,0x66,0xed,0x8f,0xf6, + 0xad,0x91,0xd5,0xc3,0xc0,0xa8,0x1e,0xa0,0x1e,0x0c,0x8c,0xf7,0xad,0xb1,0x65,0x26, + 0x8d,0xeb,0x31,0xea,0xc4,0xd8,0xfd,0xbe,0x25,0xad,0xfa,0x65,0x94,0x6e,0xec,0x34, + 0xb6,0x53,0x4c,0x2a,0x3b,0x89,0xdc,0xca,0x0d,0xf5,0xdb,0x39,0x11,0x80,0xd3,0x91, + 0xd3,0xdb,0x7a,0x1b,0x8c,0xb6,0x32,0x49,0xd2,0xf8,0x20,0x7c,0x78,0xb0,0xc0,0x92, + 0x83,0x03,0x26,0x64,0xf1,0x77,0x58,0x8c,0xa8,0xf0,0xc9,0xd5,0x3b,0x2d,0x7a,0xfd, + 0x02,0x13,0x8d,0xa4,0xc7,0x3e,0x8d,0x94,0x63,0x96,0x8c,0xc3,0x09,0x0f,0x59,0xc8, + 0x27,0x92,0x1a,0xc8,0xec,0x4a,0x11,0xf9,0x3a,0x98,0xc2,0x3b,0x12,0x9e,0xda,0x7d, + 0x72,0x9d,0xb0,0xc2,0x2f,0xe7,0x77,0x92,0x89,0x7b,0xe4,0x16,0x70,0xcd,0x69,0x72, + 0x27,0x30,0x34,0xe2,0x59,0x04,0x84,0xda,0xa3,0xa6,0x93,0xa1,0xb7,0x43,0x04,0xdf, + 0xdc,0x04,0x8b,0x22,0x9f,0x89,0x32,0xcf,0x7c,0x4b,0x3d,0x00,0xc6,0x72,0x41,0xbc, + 0x6f,0x29,0xd2,0x58,0x91,0xa0,0x2d,0x67,0xbe,0x45,0x07,0x01,0x33,0xbd,0xbe,0xca, + 0xe7,0x2d,0x17,0xbe,0x2d,0x09,0x7d,0x4b,0x2f,0x6a,0xab,0x96,0x63,0x91,0x8c,0x90, + 0x26,0xfc,0x7d,0x16,0x01,0x85,0x7a,0x5f,0x1a,0xe5,0x43,0x66,0xab,0xc3,0xce,0x72, + 0x52,0x54,0x80,0xa8,0x90,0x00,0x6f,0x8f,0x53,0x1f,0x86,0x36,0x7a,0x15,0xae,0x88, + 0xfc,0x7e,0x8a,0x0f,0xea,0xc7,0x4e,0xd4,0xc6,0xa9,0x93,0xa9,0x5e,0xcb,0x1d,0x94, + 0xf9,0x2c,0xcb,0x27,0x98,0x57,0x3f,0x81,0x16,0xb5,0x93,0x28,0x29,0x1c,0x6b,0xd9, + 0x53,0x1b,0x3d,0xff,0xe5,0xf6,0xdf,0xe9,0xe5,0xbf,0x72,0xb9,0x40,0x55,0x26,0x15, + 0xe2,0x48,0x9d,0x92,0x49,0xcb,0x9d,0x14,0x91,0xba,0x18,0x80,0x2c,0x56,0x24,0x53, + 0x1a,0xab,0xf3,0x1c,0x42,0xa1,0x70,0x40,0x84,0x55,0x40,0x2f,0xbf,0x7b,0x0d,0x0d, + 0xa0,0xf3,0x2a,0x7a,0x5b,0x0e,0x52,0xd5,0x78,0x44,0x9a,0x44,0x7a,0x61,0x4d,0x40, + 0x1c,0x3e,0xa0,0x45,0xa4,0xf5,0x91,0x87,0xcd,0x23,0x69,0x74,0x5c,0x0b,0x35,0xaa, + 0xe5,0xac,0xc0,0xe2,0x99,0xe5,0x46,0x1e,0x4a,0x6b,0x09,0x7c,0xfb,0x36,0x00,0xa7, + 0x54,0xef,0x65,0xd5,0xde,0xde,0xea,0xf1,0x45,0x10,0x58,0xf4,0x06,0xd0,0x72,0xfa, + 0x16,0xfb,0xdf,0xff,0x21,0x94,0xf5,0xd0,0xfe,0x7a,0xc2,0xbf,0x69,0x1c,0x00,0x69, + 0x8c,0x9b,0x4e,0xdf,0xb2,0x1c,0xfa,0xbf,0x5e,0x17,0x85,0x1b,0x2d,0xac,0x5e,0x0e, + 0x43,0x25,0x5b,0x0d,0x92,0xca,0xd2,0x72,0x6f,0x6e,0x3e,0x47,0x9e,0x52,0x81,0x1b, + 0xa5,0x9c,0xbf,0x3d,0x3c,0x6c,0xb4,0xd5,0x14,0xf7,0x40,0x49,0xfd,0x01,0x62,0x7b, + 0xa8,0xb9,0x7f,0x00,0xdd,0x97,0x60,0x49,0x13,0x18,0x64,0x0f,0x78,0x64,0x11,0x01, + 0xeb,0xf5,0x51,0xd5,0x69,0x02,0xe4,0xbc,0x6f,0x93,0x7c,0x3e,0xeb,0x86,0x5a,0x89, + 0x1e,0x40,0x30,0xbd,0xdb,0x32,0x20,0x16,0x1d,0xc2,0x6c,0xa2,0x28,0x32,0x1a,0x55, + 0xf9,0x41,0xbd,0x03,0x0c,0xc1,0x22,0x2e,0x94,0x4a,0xea,0x82,0x84,0xd9,0xf0,0x78, + 0xf2,0x80,0xb4,0xb6,0xc5,0x8b,0xc4,0x1b,0x47,0x8e,0xcf,0x8a,0x11,0x97,0x42,0xa7, + 0x2e,0x3c,0x56,0x47,0xb4,0x4c,0xed,0x84,0xe5,0x25,0xab,0xd5,0xcf,0x33,0x5e,0x2a, + 0x0c,0x22,0x4f,0xe9,0xb8,0x3b,0x09,0xe8,0x8e,0x80,0x47,0xa5,0x15,0xd1,0xf0,0x9d, + 0x0d,0xdd,0x97,0x96,0xe3,0xe9,0xba,0x31,0x78,0x11,0x1a,0x2f,0x12,0x2a,0x75,0x30, + 0x44,0xa2,0x16,0xb4,0xdc,0xd0,0x53,0x2b,0xf6,0x6d,0x3c,0x68,0x95,0x03,0xc1,0x74, + 0x74,0x6b,0x91,0x8f,0x54,0x12,0x35,0x20,0x7e,0x2d,0x89,0xd5,0x74,0x58,0xda,0xa4, + 0x3f,0xf1,0xc8,0x42,0x34,0xac,0xf2,0x05,0x98,0x31,0xf1,0xd4,0xd3,0xbe,0x6d,0x1e, + 0x82,0xa0,0x7b,0xf2,0xf0,0xb0,0x6e,0xbc,0x81,0x26,0x20,0xc3,0x22,0xc0,0x29,0x47, + 0x70,0xd6,0x3a,0xb0,0x8d,0x7e,0x46,0xe8,0x81,0x62,0x50,0x17,0x9a,0x0f,0x0f,0x9f, + 0x7f,0x73,0xbc,0xdf,0xf3,0x24,0xb3,0xa1,0x49,0x0e,0x61,0x2c,0xc2,0xc7,0xf3,0x22, + 0x33,0x8f,0x0c,0xf2,0xd3,0x28,0x91,0x37,0x24,0xee,0xbe,0x55,0xe1,0x91,0x29,0xd1, + 0x43,0x56,0xc8,0x98,0x50,0x19,0x22,0x96,0x96,0xa6,0xcb,0xec,0x56,0x29,0x1d,0x08, + 0xbb,0xad,0x4a,0x58,0x1a,0x2d,0xae,0xae,0x28,0x78,0x32,0x4d,0x42,0x61,0xb7,0xdd, + 0x13,0x47,0x6d,0x2a,0x57,0x1e,0x43,0x44,0x5a,0xe9,0x0b,0x38,0x9d,0x84,0xa7,0x96, + 0xde,0x86,0x62,0x9e,0x3b,0xd9,0xdb,0x7b,0x44,0x41,0x53,0xd3,0x96,0x46,0x21,0x2e, + 0x79,0x79,0xe7,0xea,0x5c,0x53,0xdd,0xe9,0xd1,0x9e,0x54,0xf9,0x92,0x81,0x88,0xa9, + 0x5e,0x4e,0x2a,0x36,0x83,0xb3,0x56,0xef,0xbc,0x29,0xc7,0x85,0x8f,0x4b,0x53,0x7a, + 0x89,0x9e,0xe7,0x05,0x83,0x5a,0x97,0x3c,0xbb,0x93,0x1a,0x99,0x76,0x47,0x8d,0xdc, + 0x54,0x8e,0xf2,0x99,0x04,0x06,0xa9,0x5c,0x00,0xd0,0x52,0x85,0x48,0xfe,0x36,0xe4, + 0x59,0xa6,0xb2,0x09,0xaa,0x8f,0x69,0x1c,0x26,0xc2,0xe2,0x94,0x0f,0x65,0xad,0x5c, + 0x20,0xe1,0x1f,0xa4,0x3a,0x2f,0x22,0x8f,0x56,0xba,0xd5,0xab,0xf6,0xea,0x00,0x09, + 0x62,0xa0,0x7d,0x6a,0x41,0xb2,0x0a,0x73,0x65,0xea,0x23,0x09,0x18,0x46,0xab,0x27, + 0xaf,0xcd,0x61,0x06,0xbb,0xd5,0x7d,0x7d,0x8b,0xd6,0xe5,0x99,0x84,0x1f,0x54,0xbe, + 0x0c,0x7a,0xad,0xfd,0x50,0x23,0x48,0xeb,0x25,0xcf,0x86,0xe2,0x83,0x44,0xb8,0x46, + 0x7c,0x75,0x7c,0x25,0x60,0xb3,0x60,0xdf,0xe2,0xda,0xc5,0xfa,0x5b,0x0b,0xf7,0xad, + 0x42,0x7d,0x2b,0xac,0xc6,0xa5,0x92,0xb0,0xc9,0x3f,0xaf,0x09,0x50,0x62,0xa0,0x0d, + 0x6c,0x59,0xee,0x77,0x8a,0x50,0xaa,0x4a,0x60,0x39,0xcd,0x30,0xa9,0x27,0x2a,0x0a, + 0xfa,0x16,0x85,0xbf,0x52,0x95,0xff,0x5a,0x02,0x74,0x9d,0x80,0x6e,0xa5,0x14,0x14, + 0xfc,0xc6,0x39,0x15,0x85,0x88,0x6b,0x30,0x5f,0x2d,0x05,0x9d,0xc9,0x67,0x74,0x57, + 0x82,0xe9,0x68,0x2d,0xb5,0x74,0xb9,0x91,0x86,0x3d,0x4b,0xaa,0x51,0xf3,0x42,0x82, + 0x4b,0x43,0x82,0x03,0x54,0x30,0xba,0x9b,0x43,0x5c,0x82,0x3f,0xe7,0x2a,0x7a,0x4e, + 0x0a,0x2d,0x33,0x49,0x01,0x99,0xb3,0x71,0x92,0x4d,0x2a,0x41,0xbe,0x01,0x81,0xd6, + 0xf1,0xd8,0xbb,0x58,0xdf,0x97,0x48,0x48,0xcc,0x83,0x1c,0xa5,0x91,0xae,0x4e,0x86, + 0x70,0x81,0xc0,0xe3,0xb2,0xbb,0x2c,0x1f,0x48,0xa5,0x01,0x35,0x69,0x12,0xeb,0x87, + 0xf4,0xaa,0x58,0x29,0x5e,0x24,0x50,0xd0,0x4e,0xb2,0x2a,0x49,0x1b,0x5a,0x64,0xe2, + 0xaa,0xec,0xb1,0x1b,0x31,0xa0,0x9b,0x1d,0x26,0x5a,0xff,0x2d,0x69,0x5d,0x27,0xea, + 0xee,0x07,0x38,0x90,0xa4,0x29,0x65,0x08,0x77,0xc8,0x10,0xde,0xad,0x8e,0xd6,0xa4, + 0x06,0x85,0xe1,0x99,0xe5,0xe0,0xe0,0x92,0x10,0x09,0x01,0xb8,0x8c,0x9a,0xad,0xa1, + 0xe2,0xc4,0x42,0x10,0x36,0x44,0xd4,0xf2,0x2c,0xc5,0x6a,0xdf,0x48,0xdb,0xf0,0x5d, + 0x9f,0xa6,0xe5,0x71,0xec,0x9a,0xc5,0x0c,0x11,0x26,0xc7,0x80,0x65,0x64,0x73,0xb5, + 0x3d,0xe2,0x86,0x39,0x27,0x22,0xb9,0x93,0xfa,0xbb,0x8a,0x43,0x54,0x57,0xaa,0xcd, + 0xd6,0x53,0x8d,0xee,0xb0,0x51,0x3e,0x36,0x57,0x58,0x56,0x2b,0x6f,0xa9,0x55,0x9d, + 0xd6,0x2a,0x1e,0x0d,0x04,0x71,0x8b,0x02,0x35,0xb4,0x83,0x55,0x79,0xad,0x69,0xbd, + 0x0d,0x91,0x83,0x03,0xc8,0x66,0x25,0xca,0x45,0x02,0xa2,0x82,0x12,0x5c,0x88,0xe0, + 0x1b,0x3c,0x52,0x38,0x15,0x0f,0x94,0x99,0x15,0x12,0x76,0xa4,0x54,0xe1,0x17,0xd9, + 0xef,0x88,0xe3,0x83,0x55,0xcb,0x6f,0xaf,0x43,0x71,0x21,0x29,0xa0,0xd4,0x60,0xb6, + 0xd6,0xde,0x3f,0xff,0xf8,0xa7,0xe5,0x63,0x68,0x95,0xf0,0x76,0xc8,0x8b,0x11,0xac, + 0xf2,0xa6,0xae,0x35,0x48,0x86,0x30,0x2e,0x03,0xad,0x40,0x4f,0x83,0xc3,0xa3,0xbe, + 0x95,0xdf,0x99,0x46,0xf7,0x68,0x65,0x03,0x64,0x15,0x8d,0x20,0x1c,0x57,0x8d,0x05, + 0xf7,0xf6,0x5e,0x28,0x1c,0xf6,0xaa,0x47,0x9b,0xe5,0x66,0xa6,0x3d,0x96,0x96,0xbf, + 0x11,0xad,0x91,0xe6,0x11,0x12,0xaa,0xc6,0xc1,0x31,0xca,0x1a,0xb8,0x3a,0x6f,0x0a, + 0x90,0x34,0xb4,0xac,0xbe,0x65,0x06,0xfe,0xfc,0xe3,0xbf,0xad,0xda,0xbe,0x35,0x00, + 0xa5,0x0e,0xfa,0xe9,0x9d,0xfc,0xa0,0x4a,0x7a,0x38,0x5a,0x66,0xeb,0xea,0xde,0xc1, + 0x2a,0x68,0x0c,0xcc,0xb9,0x8f,0xa3,0xa8,0x5e,0x79,0x9a,0x4a,0x07,0x5e,0x2d,0x2e, + 0xa7,0xa7,0x3b,0x0b,0xd5,0xa9,0x1c,0xfd,0xaa,0x6f,0x4c,0x7d,0x1a,0xa3,0x5c,0x93, + 0x9c,0x8a,0x9a,0x62,0x75,0xef,0xab,0x8e,0x85,0xcd,0xbe,0xa6,0x1f,0x37,0x42,0x4c, + 0xa3,0x40,0x03,0xa8,0xeb,0x72,0xda,0x95,0xd4,0x07,0x3b,0x5b,0xbe,0x24,0x8d,0xc0, + 0x34,0xf8,0xd2,0xbe,0xa5,0x0e,0x90,0x1a,0x07,0x16,0x3e,0x89,0xaa,0x1e,0xde,0xe2, + 0x2f,0x04,0x6b,0x2a,0x0c,0x62,0x33,0x63,0x01,0x53,0x47,0xf1,0x66,0x06,0x3d,0x3e, + 0x33,0x61,0x1f,0xb0,0x69,0x1e,0x22,0x89,0x5c,0xc1,0xeb,0xe6,0xb3,0x73,0xc0,0x2b, + 0x95,0x22,0xd2,0x04,0xf5,0xfc,0x18,0xda,0x88,0xfc,0xe7,0xbc,0xb9,0x0d,0x78,0x5f, + 0x99,0x64,0x21,0xcc,0x04,0x9e,0xc2,0xb3,0x56,0x89,0x4f,0x9e,0xe6,0x13,0xb8,0xad, + 0x38,0x4f,0xf5,0x09,0x8b,0x81,0x2e,0x2d,0xf4,0x51,0xb9,0xe4,0x53,0xf9,0x40,0x59, + 0x01,0x4c,0x55,0xdf,0xec,0x22,0xbb,0xe5,0x14,0x13,0x19,0xb9,0x48,0xf8,0xbb,0x13, + 0xd4,0x6c,0x75,0x78,0x2a,0xe5,0x34,0xb0,0x6b,0x01,0xea,0xd3,0x31,0x52,0xb0,0x66, + 0x5b,0xe5,0x79,0xe5,0x54,0x20,0x51,0xe8,0x1e,0xa2,0x30,0x5c,0x67,0x91,0x72,0x6a, + 0xb9,0x28,0x24,0xf1,0xdd,0xd0,0xf8,0x51,0xa2,0x3a,0x23,0x8f,0x0e,0xa2,0xea,0x53, + 0x29,0xc7,0xdd,0x6c,0x9f,0x62,0xce,0xfe,0x49,0xe7,0x4d,0x57,0x1b,0xd1,0xe3,0xc1, + 0x4d,0x7b,0x5a,0x23,0x4f,0x1b,0xc8,0xcd,0x59,0x5b,0x73,0xed,0xa2,0x31,0xfc,0x51, + 0xc2,0xc6,0x8c,0x52,0x53,0xb9,0xe8,0xc5,0x30,0x02,0xf8,0x64,0x11,0x99,0xec,0x8d, + 0xf6,0x4f,0x7e,0x89,0x1c,0x87,0xfc,0x05,0x0f,0x2e,0x74,0x46,0x35,0xd4,0xd1,0x69, + 0x23,0x19,0x8a,0xa5,0xc2,0x8b,0xe1,0x16,0x4d,0x50,0xee,0x81,0x0e,0xf8,0xf2,0x18, + 0x92,0xd5,0x03,0x8e,0x49,0x9e,0xbe,0x23,0xe8,0x01,0x2f,0xa1,0xaf,0xea,0xc4,0xc8, + 0xd3,0xb7,0x0a,0x09,0xa4,0x4f,0x73,0x0f,0xf0,0xf0,0x03,0x54,0xc0,0x6f,0x03,0xcb, + 0xf7,0xaa,0x2c,0x5d,0x32,0x91,0x22,0xdf,0x5b,0xac,0xd7,0x52,0xc1,0x7d,0xac,0x89, + 0x25,0xff,0xa3,0x78,0xf0,0x25,0xd4,0x56,0xdb,0xea,0x21,0x0f,0x5a,0x91,0xaa,0xcc, + 0x2c,0x96,0xdb,0x86,0x67,0x7a,0xb6,0xcc,0x0e,0x2a,0x55,0x1f,0x89,0x4c,0x0a,0x97, + 0xe2,0x67,0x5d,0x6c,0x52,0x7e,0x48,0xb1,0x98,0x1b,0x15,0x56,0xf7,0xaa,0x09,0xa2, + 0xa2,0xc0,0x4a,0xf7,0x43,0x6a,0x1d,0x1a,0x91,0xeb,0x35,0xd7,0x02,0x6b,0xcb,0x55, + 0xb4,0x48,0xe5,0x6d,0x67,0x49,0x9c,0xf4,0xa1,0xfc,0x32,0x89,0x20,0xe8,0x69,0xc2, + 0xff,0x52,0x83,0x8e,0x24,0x6a,0xa6,0xbe,0x55,0xdf,0x28,0xa4,0x6c,0x1d,0x94,0x99, + 0x57,0x89,0x96,0x6b,0x7c,0x99,0x9a,0x6f,0xd4,0xe4,0x89,0xd9,0x4f,0xea,0x49,0xd9, + 0x5c,0xb9,0xc4,0xd2,0x90,0x58,0x74,0x3e,0xde,0x72,0xac,0x49,0xd1,0x80,0x4a,0x8a, + 0xd5,0x20,0x6d,0x2a,0x1b,0x07,0x9f,0x7f,0xab,0x4f,0x1a,0x3c,0x7a,0x59,0x7b,0x46, + 0xd7,0xbf,0x9c,0x6c,0xac,0x6f,0x14,0x37,0xfa,0xf6,0x2d,0x8f,0x1c,0x41,0xaa,0x27, + 0xaf,0xe0,0xc9,0x8e,0xe8,0x1b,0xb5,0xdd,0x97,0x11,0x6c,0xce,0x55,0x54,0x65,0x70, + 0xa9,0x00,0x32,0xf9,0x39,0x95,0x0e,0xce,0x56,0xf9,0x36,0xc2,0xee,0xcc,0xee,0x11, + 0x82,0x55,0x71,0x81,0x26,0x31,0x78,0xdf,0xc6,0x43,0x52,0xd4,0x45,0xa4,0x6a,0xe8, + 0x74,0x1a,0x5c,0x5d,0x4f,0xba,0xdb,0x2c,0xb3,0x46,0x72,0xac,0xc6,0xe8,0x28,0xed, + 0xd1,0x52,0x61,0x3d,0x0d,0x9f,0xe6,0xd4,0x6c,0x93,0x8b,0x90,0x89,0x2a,0x70,0x57, + 0xa2,0xa1,0x1a,0xc1,0xe4,0x3c,0x7f,0xfe,0xf1,0x5f,0xb2,0xbe,0x31,0x8a,0x35,0x1b, + 0x89,0x06,0x21,0x40,0xf2,0xae,0x4a,0xac,0x30,0x25,0x9d,0xce,0x33,0xa0,0x0f,0xef, + 0x82,0xc6,0x45,0x65,0x23,0x8b,0x71,0xa0,0xc0,0xc6,0x72,0xb8,0x62,0xf1,0x0b,0x64, + 0x16,0x54,0xbf,0xdb,0xd6,0x55,0x49,0x95,0x9e,0xca,0x43,0x9a,0x45,0x21,0xe5,0x66, + 0x3a,0x39,0x31,0xef,0xca,0xfa,0x08,0x70,0x3a,0x73,0xec,0x69,0xbd,0xff,0x78,0x4d, + 0x27,0x64,0x94,0x4e,0xfc,0x78,0xf5,0x49,0x29,0x9f,0xc7,0x0e,0xdb,0x87,0xd0,0xf8, + 0x94,0x22,0xc1,0x9c,0x0d,0xe9,0x36,0x17,0x10,0xcd,0x99,0xce,0xb2,0x4a,0x11,0x4f, + 0x24,0x47,0x35,0xc0,0xe7,0x94,0x8f,0xcc,0x49,0xff,0x81,0x84,0x8a,0x57,0x9b,0x92, + 0x33,0x57,0x5f,0x87,0xd6,0xbf,0x52,0x50,0xc4,0x58,0xee,0x62,0x2c,0xaa,0x51,0x1e, + 0xf9,0xd6,0xe5,0xd5,0xfb,0xab,0x4f,0x57,0x28,0x14,0xe9,0x18,0x91,0xaa,0x9b,0x16, + 0xa5,0x3b,0xc2,0x5a,0x3e,0x71,0x75,0x5a,0x29,0x3e,0x36,0x49,0xd7,0xa7,0x1f,0x1e, + 0xea,0x3b,0xd3,0x41,0x00,0xea,0xea,0xe3,0x2b,0xac,0x0b,0x32,0xc0,0x15,0xca,0x62, + 0x95,0x93,0x80,0x50,0xdd,0xb1,0x71,0x42,0x06,0xa8,0xbe,0x49,0xbd,0xb5,0xc2,0xef, + 0xce,0x42,0xdf,0xc9,0xfe,0xdd,0x13,0xc6,0x49,0xfc,0xe5,0xd3,0xa7,0x8f,0xd0,0x97, + 0x7a,0xa9,0xc6,0x0f,0x02,0x6a,0xd8,0x47,0x20,0xba,0xfc,0x72,0x50,0x8c,0x56,0xcd, + 0xcb,0xd7,0xc2,0x59,0xd4,0xb4,0xad,0x2a,0x23,0x04,0x24,0xe4,0x58,0x14,0x04,0xe9, + 0x0d,0x23,0x9c,0x26,0x52,0x17,0x4b,0xaa,0xd2,0xd0,0xea,0x0b,0x7f,0xad,0x2e,0x2c, + 0x4a,0x22,0xb6,0x2e,0x6c,0x2c,0xc7,0xb5,0x04,0x9d,0x55,0x8c,0x9d,0x25,0xf9,0x57, + 0x3a,0xa7,0x81,0xe0,0xae,0xd4,0x85,0x69,0x4d,0x06,0x8b,0x13,0x81,0x4a,0x13,0xc5, + 0xc3,0xee,0x9d,0x98,0xfb,0xfa,0xce,0xf3,0x2e,0xdc,0x14,0x02,0x83,0x88,0xf4,0x01, + 0x5b,0x94,0x23,0x8d,0x89,0xbc,0xf5,0x99,0x0e,0x0a,0xb4,0xca,0xce,0xdd,0xc2,0x45, + 0xd9,0xa7,0xb8,0x8d,0xec,0xd9,0x26,0x45,0xbb,0xa3,0x72,0x20,0x6f,0x84,0x87,0x69, + 0x90,0x7f,0xbe,0xfb,0xcd,0xcd,0x82,0xa2,0x5f,0xc0,0x54,0xe1,0xec,0xfd,0xbb,0x5e, + 0x2d,0x9e,0xe9,0xde,0x9e,0xd9,0xcf,0x94,0x52,0xb1,0x5c,0xfd,0x80,0xc4,0x42,0x92, + 0x77,0x56,0x96,0x7c,0xee,0x25,0x52,0x7d,0xdb,0x08,0x8e,0x6a,0xc1,0xa9,0x9b,0xa9, + 0x05,0xf5,0x7c,0xe5,0xf2,0xd1,0xd4,0xce,0x20,0xdb,0xb7,0x54,0xde,0xb2,0x3d,0xb3, + 0x3f,0xf5,0xc6,0xbc,0x58,0x73,0xf7,0x7e,0x75,0x66,0x7b,0xbf,0x5a,0xfc,0xbe,0xb1, + 0x78,0xff,0x5f,0x6f,0xff,0xfa,0xb3,0xa7,0x59,0x9b,0xc4,0x73,0xc0,0xfb,0xf7,0xcb, + 0xba,0xd6,0x77,0xe1,0x49,0x7c,0x13,0xaa,0x29,0x5e,0xd4,0x57,0xf0,0x27,0x15,0x99, + 0x22,0x18,0x3b,0xaa,0xaa,0xc2,0xa7,0x6f,0x7d,0xb5,0x3b,0xe3,0xd3,0x64,0xc8,0xa1, + 0xa5,0x64,0xf7,0xc5,0x20,0xe7,0x65,0xd4,0x63,0xe2,0x5e,0x84,0x17,0xf9,0x78,0x0c, + 0xfb,0x82,0xf6,0xe5,0xc5,0xdc,0x72,0x56,0x67,0x95,0x4c,0x19,0x30,0x61,0xda,0xac, + 0x63,0x62,0x24,0x31,0x28,0xce,0x62,0x6e,0xca,0x21,0x4a,0xe4,0xc8,0xee,0xa8,0xac, + 0x56,0x99,0x3f,0xe1,0x61,0x83,0xb9,0x3a,0xc3,0x6c,0xc8,0x29,0x15,0x43,0x1e,0xce, + 0xe9,0x4e,0x86,0x5d,0xad,0x9c,0x01,0x0f,0x56,0x07,0xdc,0x74,0xbb,0xb7,0x12,0xe6, + 0x8c,0xdb,0xb6,0xea,0xab,0x12,0x50,0x9b,0x1c,0x6e,0x84,0x83,0xc3,0xb4,0x55,0xee, + 0x29,0xa5,0x08,0xaa,0x1e,0xa7,0x0c,0xec,0xac,0x02,0x77,0x50,0xd8,0x08,0x44,0x09, + 0x73,0x0d,0x03,0xd6,0xa3,0x1d,0x0b,0x37,0x81,0x35,0x94,0xf2,0x13,0x90,0x05,0x56, + 0x91,0x9b,0xab,0x9b,0x31,0x25,0x6b,0x3d,0xba,0x40,0x43,0x17,0xe0,0xe2,0x0a,0x5f, + 0xf5,0x1d,0x97,0xb6,0x0a,0xe2,0x2b,0xa2,0xe8,0x7a,0x8b,0xc7,0x51,0xc7,0x66,0xd1, + 0xc5,0x28,0x49,0x23,0x9b,0xd7,0xa8,0x45,0x0a,0x11,0xd9,0x8e,0x26,0xe3,0x56,0xb5, + 0x80,0xfa,0x86,0x67,0x43,0x3a,0x12,0xa9,0xbc,0x54,0x64,0xc3,0x6a,0xa4,0xa0,0xab, + 0x72,0xbe,0xc0,0x1e,0x56,0x48,0x9f,0xe0,0xfa,0x52,0x1b,0x1e,0xec,0x6d,0xf9,0x68, + 0xf9,0x12,0x89,0xdb,0x54,0x34,0x97,0x27,0x97,0xb9,0xf5,0x5b,0x0c,0x85,0x47,0x3b, + 0x35,0x11,0x69,0x77,0xbc,0xe2,0x3c,0x0d,0x11,0x07,0x0c,0xdf,0x31,0xdb,0xfc,0x40, + 0x2b,0x91,0xb7,0x22,0x44,0x49,0xad,0xf2,0xf0,0x7b,0xc4,0xb8,0x27,0x94,0xe4,0xc9, + 0x4e,0x8f,0x0a,0x3d,0x41,0x38,0x9d,0xa6,0x8b,0x7a,0x16,0x12,0xab,0x6b,0xd7,0xa5, + 0x7e,0x90,0xd4,0x70,0x49,0x1b,0x8a,0xa1,0x3d,0xd1,0xb3,0x3f,0xae,0xd9,0x9a,0xd1, + 0x9b,0xdb,0x6a,0xd6,0x52,0x85,0x1f,0xc5,0x4d,0x8f,0x47,0x11,0xfc,0x4a,0x56,0xbd, + 0x4f,0xe0,0x38,0x32,0x41,0xec,0x21,0x65,0xb6,0xdc,0x2f,0x85,0xa3,0x42,0x85,0x23, + 0x97,0xeb,0x06,0x31,0x63,0x1d,0x9c,0xe8,0x94,0xa8,0xe9,0x07,0x6b,0x67,0x35,0x17, + 0xa8,0xab,0x6a,0x97,0x66,0xde,0x3a,0x2c,0x0d,0x56,0x68,0x5b,0xf3,0x47,0x25,0x66, + 0x86,0x0a,0xe3,0xb4,0xa9,0x4b,0x68,0x3a,0x79,0xf2,0xfc,0xdd,0xed,0x5f,0xcd,0xa9, + 0xda,0xea,0x64,0x51,0x95,0x21,0x20,0xd1,0x64,0x14,0xff,0x91,0x59,0xfb,0xca,0xe3, + 0x10,0x19,0x50,0x6d,0x77,0x7d,0xdc,0x87,0x21,0x45,0x64,0x43,0xbe,0x5b,0xc1,0xc1, + 0x59,0xf0,0xfa,0xb8,0xb3,0x2a,0x27,0xa2,0xb7,0x0a,0x34,0x79,0x91,0x34,0xe2,0xcc, + 0x72,0x8b,0x2f,0x9b,0x66,0x66,0x10,0x68,0x13,0xe4,0x9e,0xba,0x1e,0x6f,0x14,0xdf, + 0xd8,0x80,0x9a,0x52,0xe3,0xd6,0xbd,0x6b,0xd7,0xa0,0xdf,0x1e,0xab,0xf3,0x35,0x52, + 0xd1,0x84,0x98,0x96,0x64,0x71,0x5e,0x07,0x38,0x25,0x3b,0xca,0x5f,0x81,0xa0,0xf1, + 0x8b,0x2d,0xbb,0x4b,0x95,0x55,0x8f,0x7a,0xcd,0xaf,0xb5,0xd4,0xe1,0x3d,0x7a,0xd6, + 0x3f,0x39,0x3c,0x50,0xb7,0xcf,0xde,0x1e,0xe8,0x1f,0x38,0xfe,0x1f,0x4b,0xc5,0xe8, + 0x57,0xf1,0x38,0x00,0x00, }; constexpr size_t STATUS_INDEX_HTML_GZ_LEN = sizeof(STATUS_INDEX_HTML_GZ); // diff --git a/firmware/patternflow/src/theme_index.h b/firmware/patternflow/src/theme_index.h index 05df763c..7078d794 100644 --- a/firmware/patternflow/src/theme_index.h +++ b/firmware/patternflow/src/theme_index.h @@ -11,24 +11,45 @@ // node --check on it, and it names no feature (check_boundaries.py R5): // the only paths it knows are the core's NAV below. // -// At parse time (no network): the theme stamp from localStorage 'pf-theme' -// before first paint; head CSS (light palette = the union of every page's -// variable families, color-scheme, a placeholder band of exactly -// --pf-chrome-h so nothing moves when the header mounts, 16px inputs on -// touch screens because iOS zooms into smaller ones, .pf-busy); the fetch +// At parse time (no network): the theme stamp before first paint, always +// set: data-theme=dark only when localStorage 'pf-theme' says 'dark', else +// light (the default). Head CSS: the font stacks for every page; the light +// palette, under every token name any page uses, as html:root[data-theme= +// light] so it outranks both a page's :root and a feature page's own +// html[data-theme=light] block (core pages carry the same values in :root so +// they are light without this file; feature pages keep dark in :root); +// color-scheme; a placeholder band of --pf-chrome-h where the header will +// mount; 16px inputs on touch screens because iOS zooms into smaller ones; +// .pf-busy. --pf-chrome-h is a guess (85px, 125px below 370px where even the +// core's six links wrap, 49px from 1024px) overridden by the height this +// browser measured at this viewport width before: 'pf-nav'.h, {width: px}, +// at most eight, written as media queries because the viewport meta below +// this script is not parsed yet, so innerWidth would be wrong on a phone. +// The mounted header is held at that height (min-height) until the first +// status says which nav to draw; then it takes its own height, which is +// measured and kept. So only a first visit at a new width can move the page +// (a firmware update keeps the heights, though it drops the cached nav). +// Then the fetch // wrapper, which gives every same-origin GET/HEAD without its own signal an // AbortController so a tab switch can free the panel's single connection // instead of queueing behind it; and window.PF. // // At DOMContentLoaded it mounts as body's first // child, everything inside an open shadow root so no page CSS reaches it and -// its CSS reaches no page (pages must not reach into it either). Heights are -// fixed: phone = 40px brand row + 40px nav row + 1px rule = 81px, >=720px = -// one 40px row + rule = 41px, the values of --pf-chrome-h. +// its CSS reaches no page (pages must not reach into it either). Phone: a +// 44px brand row (chip, theme toggle) over the nav, which wraps in 40px rows +// rather than scrolling sideways; >=1024px: one 48px row. A desktop row the +// nav cannot fit (many features, or the chip showing) wraps under it with +// brand, chip and toggle staying on the first line. The chip and the toggle +// are 40px tap targets (the chip draws its 30px pill inside a transparent +// border). // // The nav is the core's pages only; features contribute theirs through -// /api/status featureNav, spliced in after Patterns. (This file once named -// three optional features by path, so a build without them drew dead links.) +// /api/status featureNav, spliced in after Patterns. Order: Console, +// Patterns, the features, then the setup group (Wi-Fi, Knobs, Update, +// Status) in its own , so it wraps as one and sits right on +// a desktop. (This file once named three optional features by path, so a +// build without them drew dead links.) // Every href carries ?v=, which the device serves immutable, so a tab // switch after the first visit costs no request. The last nav drawn is kept // in localStorage 'pf-nav' and reused when its build matches the page's. @@ -74,32 +95,34 @@ const char PF_CONSOLE_JS[] PROGMEM = R"JS((function(){ if(window.PF)return; -var W=window,D=document,H=D.documentElement,L=location,light=false; +var W=window,D=document,H=D.documentElement,L=location,dark=false,cn=null,hm={},k, +st=D.createElement('style'),c; // Theme before first paint: this script is parser-blocking in . -try{light=localStorage.getItem('pf-theme')==='light'}catch(e){} -if(light)H.setAttribute('data-theme','light'); -var st=D.createElement('style'); -// --pf-chrome-h is the header's exact height (see the shadow CSS below). -st.textContent='html[data-theme=light]{color-scheme:light;--cream:#F4EFE6;--cream2:#FFFCFA;--ink:#1A1814;--muted:#6B6558;--faint:#9A9486;--rule:#D9D1C2;--rule-soft:#E8E2D6;--led:#FF5C2E;--ok:#2F8A55;--warn:#B88120;--panel:#FFFCFA;--bg:#F4EFE6;--ghost:#E0D9CC;--card:#FFFCFA;--card2:#E8E2D6;--fg:#1A1814;--mut:#6B6558;--ln:#D9D1C2;--bad:#B8402E;--bar:#E0D9CC;--bar-on:#FF5C2E;--accent:#FF5C2E}' -+':root{color-scheme:dark;--pf-chrome-h:81px}@media(min-width:720px){:root{--pf-chrome-h:41px}}' -+'html:not(.pf-mounted) body::before{content:"";display:block;box-sizing:border-box;height:var(--pf-chrome-h);border-bottom:1px solid var(--rule,#242118);background:var(--cream,var(--bg,#0C0B09))}' +try{dark=localStorage.getItem('pf-theme')==='dark';cn=JSON.parse(localStorage.getItem('pf-nav'));hm=cn&&cn.h||{}}catch(e){} +H.setAttribute('data-theme',dark?'dark':'light'); +// Light palette = the core pages' :root values; outranks any page's own. +c='html[data-theme]{--sans:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;--mono:ui-monospace,"Cascadia Mono","SF Mono",Menlo,Consolas,"Roboto Mono",monospace}' ++'html:root[data-theme=light]{color-scheme:light;--cream:#F4EFE6;--panel:#FFFCFA;--ink:#1A1814;--muted:#625C51;--faint:#7D7668;--rule:#DDD5C6;--rule-soft:#E8E1D4;--ghost:#E3DCCE;--led:#FF5C2E;--bad:#B83C1C;--ok:#2B7A4B;--warn:#85600E;--cream2:#FFFCFA}' ++'html[data-theme=dark]{color-scheme:dark}:root{--pf-chrome-h:85px}@media(max-width:369px){:root{--pf-chrome-h:125px}}@media(min-width:1024px){:root{--pf-chrome-h:49px}}' ++'html:not(.pf-mounted) body::before{content:"";display:block;box-sizing:border-box;height:var(--pf-chrome-h);border-bottom:1px solid var(--rule,#DDD5C6);background:var(--cream,#F4EFE6)}' +'@media(pointer:coarse){input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file]),select,textarea{font-size:16px!important}}' +'.pf-busy{opacity:.55;cursor:progress}'; +for(k in hm)c+='@media(min-width:'+(k-1)+'px) and (max-width:'+k+'.9px){:root{--pf-chrome-h:'+(hm[k]|0)+'px}}'; +st.textContent=c; (D.head||H).appendChild(st); var PF=W.PF={v:null,state:'connecting',dirty:false}, nf=W.fetch,T=[],cur=null,Q=[],lb=0,run=1,P=[],S=null,subs=[],sq=null,sb=0,wms=1/0,sp=null, rtt=null,fails=0,off=0,alien=0,rs=0,holds=0,guards=0,leaveOk=0,RH={},bc=null, me=Math.random().toString(36).slice(2),touched=0,pd=0,nv=0,nvT,pgT, -adopted=0,healed=0,stale=null,pft=0,ct=0,cn=null,fnav=[],drawn='', +adopted=0,healed=0,stale=null,pft=0,ct=0,fnav=[],drawn='', sr,hl,ver,vb,nav,ch,tg,pg,tw,tq,tt,SKIP={}, // The core's pages and nothing else; features add theirs via featureNav at // NAV_AT. Third field: prefetch order, smallest page first. -NAV=[['/','Console',5],['/patterns','Patterns',6],['/status','Status',2],['/wifi','Wi-Fi',4],['/knobs','Knobs',1],['/update','Update',3]],NAV_AT=2; +NAV=[['/','Console',5],['/patterns','Patterns',6],['/wifi','Wi-Fi',4],['/knobs','Knobs',1],['/update','Update',3],['/status','Status',2]],NAV_AT=2; try{PF.v=new URLSearchParams(L.search).get('v')}catch(e){} var docB=PF.v; H.classList.add('pf-connecting'); -try{cn=JSON.parse(localStorage.getItem('pf-nav'))}catch(e){} if(cn&&Array.isArray(cn.nav)&&(!PF.v||cn.build===PF.v))fnav=cn.nav; function now(){return Date.now()} @@ -221,10 +244,12 @@ else if(sp)sp.set(ms); else sp=PF.poll(stGet,ms,function(){},{first:false}); } function deliver(s){ +var fn=Array.isArray(s.featureNav)?s.featureNav:[],r=!S; S=W.pfStatus=s;sb=0; -var fn=Array.isArray(s.featureNav)?s.featureNav:[]; -if(s.build+JSON.stringify(fn)!==drawn){fnav=fn;try{localStorage.setItem('pf-nav',JSON.stringify({build:s.build,nav:fn}))}catch(x){}drawNav()} +if(s.build+JSON.stringify(fn)!==drawn){fnav=fn;drawNav();r=1} badge(); +if(r&&sr){sr.host.style.minHeight='';if(Object.keys(hm).length>7)hm={};hm[W.innerWidth]=sr.host.offsetHeight} +if(r)try{localStorage.setItem('pf-nav',JSON.stringify({build:s.build,nav:fn,h:hm}))}catch(x){} for(var i=0;i1);nav.classList.toggle('fr',s
              '; ++'
              '; function q(c){return sr.querySelector('.'+c)} hl=q('h');ver=q('ver');vb=q('vb');nav=q('n');ch=q('c');tg=q('tg');pg=q('pg');tw=q('tw'); +if(!S)host.style.minHeight='var(--pf-chrome-h)'; D.body.insertBefore(host,D.body.firstChild); H.classList.add('pf-mounted'); -nav.addEventListener('scroll',fades,{passive:true}); -W.addEventListener('resize',fades); -tg.onclick=function(){var l=H.getAttribute('data-theme')!=='light';H.setAttribute('data-theme',l?'light':'dark');try{localStorage.setItem('pf-theme',l?'light':'dark')}catch(x){}tgl()}; +tg.onclick=function(){var t=dk()?'light':'dark';H.setAttribute('data-theme',t);try{localStorage.setItem('pf-theme',t)}catch(x){}tgl()}; ch.onclick=function(){if(stale)L.replace(vurl(stale));else if(PF.state==='offline')fetchStatus()}; tgl();drawNav();badge();chip(); setTimeout(function(){ct=1;chip()},800); @@ -468,7 +489,7 @@ if(u.pathname+u.search===L.pathname+L.search)return; if(guards){if(!confirm('An upload is running \u2014 leave anyway?')){e.preventDefault();return}leaveOk=1} nv=1;run=0; for(i=T.length;i--;)if(!(T[i].k==='prefetch'&&T[i].u===u.href)){T[i].why='nav';T[i].c.abort()} -if(nav)for(i=0;i GENERATED by firmware/toolchain/console_pages.py build — do not edit. -// gzip -9 of PF_CONSOLE_JS (22934 bytes → 9654 bytes), served with Content-Encoding: gzip. +// gzip -9 of PF_CONSOLE_JS (22826 bytes → 9639 bytes), served with Content-Encoding: gzip. const uint8_t PF_CONSOLE_JS_GZ[] PROGMEM = { - 0x1f,0x8b,0x08,0x00,0x00,0x00,0x00,0x00,0x02,0xff,0x95,0x7c,0xff,0x77,0x9b,0xc6, - 0xb6,0xef,0xef,0xfe,0x2b,0xb0,0x73,0x2a,0x44,0x8d,0xb0,0xe4,0x38,0x69,0x82,0x42, - 0x7c,0x13,0x3b,0xb9,0xe9,0x3d,0x69,0x9b,0x53,0xbb,0xed,0x5b,0xcb,0xf5,0x3d,0x0b, - 0xc1,0x48,0x22,0x42,0x80,0x01,0x49,0xf6,0x91,0xf4,0xfe,0xf6,0xf7,0xd9,0x7b,0x06, - 0x18,0x64,0x39,0xed,0x5b,0x2b,0x91,0x61,0x18,0xf6,0xec,0xd9,0xb3,0xbf,0xcf,0x1e, - 0xba,0xe3,0x45,0x12,0x94,0x51,0x9a,0x74,0xad,0xf5,0x41,0x34,0xee,0xae,0xa2,0x24, - 0x4c,0x57,0xce,0x97,0x8f,0x56,0x2e,0xca,0x45,0x9e,0x0c,0x0f,0x96,0x7e,0x6e,0xfc, - 0xe1,0xc9,0x76,0xfb,0xd2,0x0b,0xd3,0x60,0x31,0x17,0x49,0x69,0x7f,0xf2,0x2e,0x9d, - 0xea,0xe6,0x43,0x2c,0xb8,0xed,0xb3,0x17,0xa7,0x81,0x4f,0xe0,0xec,0x38,0x9a,0x4c, - 0x4b,0x6f,0xec,0xc7,0x85,0x18,0x1e,0x9c,0x9c,0x18,0xd7,0x53,0x74,0x31,0x46,0x62, - 0x9c,0xe6,0xc2,0x18,0x47,0x79,0x51,0x1a,0x99,0x1f,0x25,0xa5,0x6b,0x94,0xd3,0xa8, - 0x30,0x8a,0x20,0x8f,0xb2,0xd2,0xc0,0x55,0xe6,0xe7,0x85,0xc8,0x7b,0x23,0x40,0x9a, - 0x45,0xc9,0xc4,0x88,0x12,0xe3,0xcd,0x54,0xf8,0xe1,0x5b,0xe7,0xa0,0xcc,0x1f,0xd6, - 0x12,0x2e,0x0d,0x13,0x5f,0x95,0x69,0xee,0x4f,0x84,0x33,0x11,0xe5,0x8f,0xa5,0x98, - 0x77,0xcd,0x6c,0xdc,0x2b,0x69,0x18,0xd3,0xf2,0x3c,0xcf,0xe4,0x9e,0xe6,0x16,0xf8, - 0x04,0xd3,0xae,0xb0,0xd6,0x5b,0x9a,0x1f,0x37,0x5a,0x9f,0x9c,0x42,0x94,0xef,0xca, - 0x32,0x8f,0x46,0x8b,0x52,0x74,0xcd,0xd0,0x2f,0x7d,0xf5,0xaa,0xad,0xde,0xb3,0xe4, - 0xcc,0x8b,0x12,0xd3,0x0c,0x72,0xe1,0x97,0x42,0x4d,0xb2,0x6b,0x16,0xe5,0x43,0x2c, - 0xa8,0x03,0xa6,0xd5,0xeb,0x61,0xd0,0x60,0x9a,0xa7,0x73,0xd1,0x9b,0x12,0xfa,0x80, - 0x62,0x10,0xba,0x22,0x37,0x0b,0x43,0xdc,0xfb,0x41,0x89,0x5b,0x82,0x68,0x74,0x0b, - 0x21,0xf8,0x71,0x31,0xf5,0x41,0x4b,0xe3,0xe2,0xea,0x0a,0xf4,0x88,0xd3,0x95,0xe5, - 0x1c,0x14,0xa5,0x53,0x8a,0xfb,0xf2,0x22,0x4d,0x4a,0x0c,0xe1,0x99,0xd3,0x72,0x1e, - 0xdf,0x34,0x58,0x79,0x8c,0xd3,0xed,0x3a,0x48,0xe3,0x34,0xef,0x15,0x01,0xb5,0xb9, - 0xdc,0x36,0xec,0xf5,0x08,0xbb,0xb9,0xfb,0xec,0xe3,0xd9,0x87,0x8f,0x1f,0x5e,0x56, - 0xf7,0xa7,0x68,0xf8,0xf8,0xf1,0xe2,0xe3,0x3b,0x34,0x44,0xc9,0xcc,0x7d,0x36,0x78, - 0x37,0x78,0x35,0x38,0xc3,0xdd,0x1c,0x33,0x0e,0xdd,0x67,0x2f,0xdf,0xbf,0x7c,0xf1, - 0xe2,0x15,0xee,0xc7,0xbc,0x0a,0xcf,0x5e,0xbf,0x7b,0x7d,0xf6,0x8a,0x5e,0xcf,0x17, - 0xb1,0x70,0x9f,0x5d,0xbe,0xbe,0x1c,0x5c,0x9c,0xaa,0xdb,0x5e,0x91,0x8e,0xd1,0xe5, - 0xc3,0xab,0x0f,0xa7,0x97,0xd4,0x25,0x26,0x00,0x1f,0x3f,0xbe,0xb8,0x38,0xfd,0x80, - 0xbb,0x14,0xd0,0x4f,0x3f,0xbe,0x7a,0xf7,0xe2,0x05,0x6e,0x56,0x7e,0x9e,0xb8,0xcf, - 0xde,0xbf,0x7a,0x35,0x38,0xed,0xe3,0x36,0xf3,0x13,0x11,0x6b,0xa8,0x8c,0x26,0x1a, - 0xa2,0x93,0x69,0x5a,0x10,0xd8,0xfe,0xe5,0xeb,0x8b,0x0b,0x42,0xdc,0xcf,0x43,0xad, - 0x2f,0xdd,0x9e,0x6a,0xa3,0x8e,0x27,0xed,0x59,0x68,0x73,0x88,0x13,0x0d,0xe3,0x91, - 0x1f,0x12,0x06,0x67,0x7d,0xc6,0x6e,0xe4,0xe7,0xda,0x10,0xb8,0xeb,0xa5,0x89,0x86, - 0xbc,0x1f,0x04,0x82,0xa6,0x2f,0x1b,0xb6,0xe6,0xc1,0xb1,0xe9,0xe6,0x69,0x5a,0xb6, - 0x49,0x1d,0xfa,0xf9,0x6c,0xd8,0x5a,0x6a,0xf7,0xd5,0x20,0xbb,0xdf,0xfe,0xd7,0x5c, - 0x84,0x91,0xdf,0x9d,0x47,0x49,0x6f,0x15,0x85,0xe5,0xd4,0xfd,0xe1,0xb4,0x9f,0xdd, - 0x5b,0x6b,0x09,0xa2,0xfd,0xc2,0x19,0xbd,0xc0,0x03,0xd0,0xe2,0xba,0x49,0x5a,0x76, - 0x1d,0x3c,0x9f,0xa7,0x0b,0xac,0x79,0x68,0x19,0xa3,0x34,0x7c,0x70,0x5d,0x29,0x20, - 0x18,0x9d,0x19,0xc1,0x3d,0x3a,0x1a,0x86,0x51,0x91,0xc5,0xfe,0x83,0xcb,0x02,0x31, - 0x1c,0xa5,0xf7,0xbd,0x22,0xfa,0x0f,0x04,0xc3,0x1d,0xa5,0x79,0x48,0x82,0x92,0xde, - 0x0f,0x25,0x87,0xb9,0x60,0xd8,0x6e,0x6b,0x50,0x6b,0x58,0x77,0x2a,0xcb,0x74,0xee, - 0x02,0x05,0xa3,0x48,0xe3,0x28,0x34,0x64,0x57,0x5a,0x5c,0xfb,0xd9,0xe9,0xd9,0xe9, - 0x60,0xf0,0x0a,0x7d,0xfd,0x60,0x36,0xc9,0x81,0x4f,0xa8,0x20,0x31,0x27,0xd9,0xf2, - 0x7a,0x34,0xb1,0x9f,0xf5,0x2f,0xfa,0xef,0xfb,0xaf,0x2d,0x8b,0x67,0xa1,0xe6,0x9e, - 0xa5,0x60,0x1f,0x91,0xbb,0x41,0x4a,0x72,0x6b,0xad,0xa3,0x24,0xc3,0xd2,0xd0,0xec, - 0x6e,0xca,0x87,0x4c,0x78,0x20,0x60,0x30,0x03,0x8e,0xb7,0x96,0xd6,0x98,0xfb,0x61, - 0x94,0xee,0xb4,0x24,0x13,0xd1,0x6a,0x19,0x47,0x31,0x1a,0xec,0x42,0xc4,0x22,0x28, - 0x6d,0x92,0x0e,0x1f,0xe8,0xac,0xc7,0xa0,0x0c,0x51,0x40,0xb8,0x83,0x97,0xd9,0xfd, - 0x61,0x34,0xcf,0xd2,0xbc,0xf4,0x93,0x52,0x92,0x96,0x28,0x3a,0x5a,0x14,0x0f,0xeb, - 0x34,0xf3,0x83,0xa8,0x7c,0x70,0x1d,0x70,0x65,0xb0,0xc8,0x8b,0x34,0x77,0xb3,0x3c, - 0x9d,0xe4,0xa2,0x28,0xb6,0xe6,0xf0,0xa0,0x7b,0xe9,0x90,0x90,0x6e,0x36,0x9f,0x2c, - 0xc7,0xcf,0x32,0x91,0x84,0x17,0xd3,0x28,0x0e,0xbb,0x45,0x09,0xb1,0x66,0xc1,0xff, - 0xf2,0xd1,0xfb,0x03,0x6a,0xd0,0x5b,0x2f,0xdd,0x64,0x11,0xc7,0x76,0x51,0x42,0x05, - 0xb8,0x26,0x16,0x26,0x01,0x3e,0xa0,0xbf,0x69,0x87,0x51,0x8e,0x11,0x58,0xc5,0x6d, - 0xed,0x83,0x64,0x8c,0x17,0xc6,0x02,0xca,0xc6,0xbe,0xf6,0x6e,0x6e,0x6d,0x8c,0xea, - 0xf1,0x9b,0xff,0xa2,0xbb,0x78,0xe4,0xf5,0xed,0x7c,0x91,0x78,0x03,0xfb,0x0b,0xdd, - 0x5f,0xc9,0x67,0xc5,0x62,0x54,0xd0,0x6d,0x71,0xa7,0xee,0xa9,0xdb,0x6a,0x5e,0x78, - 0x83,0x93,0xbe,0x5d,0x64,0xb2,0xf1,0x20,0x2f,0x4b,0x79,0x05,0x59,0x8d,0x0b,0xf4, - 0x48,0xc7,0x63,0xfc,0xfa,0x71,0x24,0x12,0x02,0x4b,0x4d,0xd3,0x34,0x0e,0xe9,0xef, - 0x64,0x01,0xb1,0xa1,0x8b,0x58,0xf8,0x4b,0xf1,0xcb,0x0c,0x57,0xbf,0x7e,0xf2,0xd6, - 0x5b,0x7b,0x14,0x28,0x68,0x50,0x27,0x3f,0xf9,0xe5,0xd4,0x01,0xc9,0xc3,0x74,0xde, - 0xb5,0x9c,0x32,0xbd,0x82,0x26,0x4c,0x26,0xdd,0xe7,0x2f,0x2d,0xa7,0x88,0xa3,0x40, - 0x74,0x4f,0x2d,0xbb,0x4c,0x17,0x58,0xbb,0x10,0xef,0x67,0xf4,0x93,0x2c,0xf9,0xe7, - 0xda,0xce,0x26,0xd7,0xf6,0x01,0x14,0x58,0x56,0xf2,0x43,0x90,0x31,0xe6,0x0b,0x10, - 0x28,0x16,0x72,0x88,0x6c,0x5c,0xa2,0x21,0xe0,0x9f,0x44,0x61,0x9e,0xf8,0x4b,0x9a, - 0x68,0x98,0xfb,0xab,0xc4,0x33,0x4d,0xfb,0xa0,0xc8,0xed,0x69,0x6c,0x2f,0x45,0x6e, - 0x2f,0x47,0x36,0x9e,0xda,0x20,0x5c,0x39,0x01,0x78,0xbb,0x5c,0xd9,0xe5,0x9d,0x5d, - 0x96,0xf6,0xd5,0x3f,0x7f,0xfc,0x42,0xa8,0x2b,0xfb,0x61,0x04,0x10,0x0d,0x93,0x2c, - 0xc4,0x44,0x14,0x06,0xb0,0x37,0xc0,0x2d,0x53,0xb2,0x11,0x82,0xac,0x8c,0x31,0x86, - 0x96,0x5e,0xe4,0xf4,0x28,0x0c,0x49,0xd1,0xc2,0xc8,0x18,0xcb,0xc8,0xaf,0xda,0x7f, - 0xf6,0x97,0x86,0x5f,0x12,0xac,0x9f,0xdf,0xfd,0xfe,0xef,0x77,0xd7,0x0e,0x60,0x46, - 0x79,0x08,0x63,0x24,0xe2,0xd0,0x35,0xb2,0x5c,0xf0,0xea,0x19,0x2c,0x31,0xb6,0x51, - 0xcc,0xfd,0x38,0x16,0x6c,0xa6,0x26,0xca,0x62,0x39,0x07,0x78,0xd3,0xbb,0xb9,0x31, - 0x4f,0x60,0x26,0xa0,0xab,0x21,0x4a,0x30,0x18,0x2f,0x6e,0x6d,0xb4,0x64,0x7e,0x09, - 0x31,0x48,0x0a,0x3c,0xf9,0x52,0x5f,0xbe,0xe4,0x47,0xc4,0x3a,0x0b,0x7a,0x70,0xa5, - 0x2e,0x4e,0xb9,0x79,0x15,0x8d,0x23,0x34,0xfe,0x11,0xf5,0x3e,0xe2,0xef,0x19,0xb7, - 0xcd,0x92,0x74,0x44,0x3d,0xff,0x29,0xff,0x0e,0xb8,0x71,0x91,0xc1,0x12,0x90,0x65, - 0xfa,0x4d,0x5d,0x3c,0xbf,0xbd,0xb5,0xe5,0x1c,0xbc,0xd3,0x21,0x5b,0xc5,0x2f,0x1f, - 0x9d,0xa5,0x97,0x88,0x95,0xf1,0xdb,0xaf,0x9f,0xaf,0x84,0x9f,0x07,0xd3,0x2f,0x7e, - 0xee,0xcf,0x8b,0xee,0x67,0x58,0x3a,0xba,0xb5,0xc8,0x4a,0x76,0xcd,0xa5,0x69,0xe9, - 0x06,0x91,0x18,0x1d,0x36,0xfc,0xbd,0x47,0xef,0x0f,0x0f,0x3e,0x39,0x41,0xec,0x17, - 0xc5,0xe7,0x08,0x53,0x05,0x0d,0xd9,0xa0,0x6a,0x0c,0x6f,0xc9,0xb1,0xb0,0xa4,0xff, - 0x73,0xf5,0xcb,0xcf,0x0e,0x1b,0xea,0xee,0x93,0x96,0x18,0x4b,0x6a,0x5a,0xd6,0x8e, - 0xf9,0x0d,0x92,0x4e,0xe7,0x5d,0x9e,0xfb,0x0f,0x4e,0x54,0xf0,0x5f,0xb4,0x38,0xe8, - 0x69,0x75,0x3a,0xdd,0x43,0x42,0x62,0xb3,0x41,0xc3,0x68,0x01,0x59,0x84,0x09,0xa7, - 0x06,0xcb,0x62,0xd6,0x91,0xdd,0x20,0x9b,0x95,0xbf,0x82,0xb5,0x5f,0xc1,0x67,0x91, - 0x5e,0x8a,0x71,0x09,0xba,0x38,0xdc,0xb2,0x6d,0x7a,0x00,0xb1,0xb8,0x3b,0xb6,0x7d, - 0x7b,0x64,0xad,0x09,0xf1,0x71,0x97,0x2e,0x15,0x46,0xd0,0xd2,0xf0,0x01,0xae,0xa3, - 0xb9,0x48,0x17,0x65,0x57,0xf3,0x82,0x4a,0xe8,0xce,0x95,0x71,0xbf,0xb5,0xb6,0x1a, - 0x28,0xb1,0xec,0x26,0x76,0x68,0xad,0xe1,0xf2,0x40,0x1d,0x13,0x80,0x0f,0x4b,0xf2, - 0x05,0x88,0xe4,0x17,0x8b,0x02,0xba,0x55,0xdd,0xdb,0xeb,0x50,0x94,0x90,0x52,0x37, - 0xdc,0x5a,0x3a,0x2e,0x61,0x9e,0x66,0x44,0x05,0xa2,0x78,0xe4,0x5d,0x3b,0x70,0xa7, - 0xc4,0xfd,0x2f,0x63,0x34,0x0d,0x41,0x96,0xe8,0x6d,0x6f,0x60,0x5d,0x3b,0x50,0xf4, - 0x24,0x7c,0x91,0x3d,0xc0,0xab,0xc4,0xa8,0x92,0x21,0x57,0x39,0xa9,0xa8,0xdc,0x35, - 0x0a,0x1f,0x4a,0x3d,0xcd,0xa3,0x09,0x7c,0xa2,0xff,0xfe,0x70,0x5d,0x18,0xa0,0xb7, - 0xe1,0x1b,0x64,0x2c,0xf2,0x14,0xcc,0x9a,0xe3,0xa6,0xf4,0x47,0x46,0xb1,0x8a,0xe8, - 0xbd,0xc0,0x4f,0x0c,0x1f,0x06,0x00,0x7c,0x5b,0xe3,0x51,0x8e,0xbb,0x0b,0x3b,0xb5, - 0x67,0xf0,0xf6,0x08,0x95,0x80,0x99,0xe6,0x1d,0x75,0xba,0xa8,0xa1,0x74,0x2d,0x5b, - 0x78,0xeb,0xc0,0x0d,0xec,0x85,0xab,0x78,0x0a,0x2f,0x7d,0x76,0xa6,0x90,0x10,0x8b, - 0x7f,0x6d,0xd2,0xf0,0x20,0xb6,0x3d,0x73,0x67,0x5b,0x3b,0x22,0x19,0x85,0xa7,0x08, - 0xcb,0xd5,0x4d,0xc8,0x5f,0x4b,0xad,0xe8,0x26,0xb9,0xf5,0x52,0xfc,0x0c,0x0f,0x22, - 0xa7,0x88,0x26,0x89,0x1f,0x7b,0x81,0xba,0x50,0x1d,0x41,0x84,0x58,0x24,0x93,0x72, - 0x3a,0x4c,0x7a,0xbd,0xa1,0x05,0x2a,0x08,0xa7,0xec,0x5d,0xe3,0x1d,0xa7,0x7c,0xfb, - 0x52,0x9c,0x35,0xf4,0x48,0x40,0x8f,0xe1,0xc1,0xb5,0x93,0x2d,0x8a,0xa9,0x22,0x18, - 0x94,0xab,0x85,0xff,0x75,0xd3,0x81,0x70,0xa0,0x2c,0xc7,0x0e,0x2f,0xf9,0x1f,0xf6, - 0xc2,0x8e,0xd0,0xa6,0x78,0x03,0xfe,0xe7,0xf6,0x40,0xa9,0x66,0xaf,0x5e,0x68,0x50, - 0x41,0xd1,0x60,0xee,0x29,0xf5,0x97,0x76,0x3a,0xa9,0x33,0x17,0xe5,0x34,0x85,0x61, - 0x30,0x41,0x60,0x93,0x74,0xe3,0x6f,0x44,0xfb,0x0b,0x1f,0xac,0x6e,0xd9,0xe9,0x4c, - 0x7a,0xb4,0xb6,0x90,0xa2,0x80,0xfb,0x2e,0x19,0xab,0x74,0x6c,0x2c,0xc8,0xe5,0x2c, - 0x18,0x90,0xb9,0xd9,0x2c,0x40,0x06,0x88,0x7d,0x12,0xd0,0x23,0xd0,0x8f,0xf8,0x7b, - 0x4e,0x3d,0x08,0xea,0x66,0xc3,0x97,0x9f,0x3e,0xbc,0xbb,0x34,0xf1,0xe0,0x90,0x06, - 0xee,0xa6,0x8a,0x3a,0x9b,0x4d,0xea,0xcc,0x84,0xc8,0xa0,0xdc,0x97,0xc2,0xc2,0xe3, - 0xc7,0x2b,0x20,0x39,0x00,0x20,0x3e,0xab,0xcb,0x86,0xa5,0x59,0xc8,0x0e,0xd3,0x99, - 0xf2,0xde,0x0d,0x90,0x04,0xbc,0x13,0x3f,0x80,0x26,0x7e,0x3e,0x61,0x5f,0xbd,0x20, - 0x6a,0x79,0x92,0x0f,0x36,0x1b,0xac,0x1c,0xe8,0x78,0x6e,0x8e,0x26,0xa6,0x6b,0x8e, - 0x59,0xc6,0x41,0x4a,0x07,0xda,0x33,0xd1,0x85,0x42,0xb1,0xef,0xd6,0xde,0xd3,0xa6, - 0x51,0xda,0xc9,0x40,0x6b,0xf6,0x8b,0x07,0xa7,0x06,0xf4,0x70,0x69,0xc0,0x0a,0xc1, - 0xc7,0x33,0x88,0xf3,0xc8,0x31,0x4e,0xcb,0x22,0x4b,0x4b,0x03,0xfc,0x39,0x95,0xae, - 0x70,0xc9,0xd1,0x82,0x01,0xa2,0xa6,0xc6,0x22,0xb3,0xc1,0xc0,0xac,0x77,0x8d,0x5c, - 0x00,0x6b,0x02,0x74,0xb7,0x10,0x0b,0x11,0xc2,0x51,0x86,0xa2,0x0f,0xf1,0x98,0xb5, - 0xb1,0x02,0x97,0x26,0xa2,0xd6,0x4f,0x68,0x42,0xd8,0x00,0x73,0x62,0x94,0x69,0x3a, - 0x33,0xce,0x7a,0x2f,0x80,0x40,0x77,0x24,0x92,0x60,0x4a,0x60,0x4e,0xfb,0xa7,0x2f, - 0x7b,0xfd,0xd7,0xbd,0xd3,0x33,0xcb,0x66,0xd3,0xe1,0x1b,0xd4,0x61,0x1c,0xa7,0x69, - 0xce,0x8a,0x42,0x90,0xc9,0x20,0x84,0xe1,0x2d,0x1b,0x21,0x7c,0x03,0x5d,0x68,0xe6, - 0x69,0xa3,0x6a,0xd8,0x76,0xc2,0xe3,0xeb,0x3e,0x17,0x67,0xb6,0xbc,0xf1,0xef,0xbb, - 0x83,0x53,0xf1,0xdc,0x3e,0xfb,0x1e,0x96,0x9a,0xc4,0x1e,0x03,0xbe,0x83,0x07,0x81, - 0x59,0x00,0x7d,0x98,0x90,0xee,0x3c,0x0d,0x85,0xf1,0xb5,0x48,0x93,0x0d,0x39,0x32, - 0x9b,0xa2,0xdc,0xc0,0xdc,0x58,0x88,0x7d,0x72,0xf8,0x5c,0x18,0x1a,0x3e,0x60,0x14, - 0x1b,0x51,0x59,0xb0,0x1b,0x48,0x71,0x04,0x7c,0x9d,0x70,0x48,0xb3,0x24,0x60,0xa5, - 0x54,0x53,0x78,0x08,0xe7,0x4c,0xc6,0x18,0x4c,0x20,0x9e,0x09,0xad,0x15,0x37,0xd1, - 0xab,0xb0,0x5b,0x29,0xe6,0x36,0x8a,0x26,0x12,0x12,0xd1,0xd6,0x28,0x10,0x64,0xf0, - 0xc4,0x08,0x16,0x60,0xc3,0x6c,0x1a,0x47,0xf0,0x21,0xd0,0x24,0x8e,0x1c,0xe3,0x97, - 0x04,0x90,0x08,0x5e,0x04,0xdb,0x39,0x86,0xe7,0x28,0x43,0x32,0xa5,0x68,0xba,0x04, - 0x9a,0xfd,0x78,0x0b,0xfa,0x88,0xc9,0x24,0x8c,0x5f,0xaf,0xaf,0x09,0x96,0x1a,0xde, - 0xd0,0x17,0x81,0xfc,0xa4,0x21,0x9e,0xc0,0x34,0x63,0x31,0x8a,0xa8,0x84,0xf1,0x84, - 0x42,0x24,0x33,0x5d,0xf8,0x0f,0x45,0x6d,0xb3,0xa1,0x99,0x30,0xa3,0x48,0x57,0x4e, - 0xf9,0x1d,0x98,0x72,0x66,0x13,0xb1,0xec,0x79,0xa1,0xc4,0x13,0x84,0xf2,0xa8,0x85, - 0x44,0x06,0xd7,0xa6,0xad,0x98,0x17,0xd7,0xe7,0xeb,0xad,0xbb,0x0e,0x7c,0xb8,0x29, - 0xae,0x99,0xa4,0x3d,0xa8,0x61,0x3c,0xdf,0x42,0xb9,0xd9,0x65,0xdf,0x93,0x0a,0xaa, - 0x4c,0x6d,0xd2,0x9a,0x95,0x6d,0x84,0x8e,0x59,0xec,0x11,0xa0,0x61,0x83,0x83,0x9f, - 0xc3,0x25,0x5a,0x07,0xe0,0xdb,0xbc,0x32,0x0e,0x65,0x6a,0x0d,0xcb,0xd4,0xdb,0x6f, - 0x2e,0x84,0xb3,0x9a,0x3e,0x78,0x66,0x69,0x0e,0x85,0x13,0x38,0xac,0x6e,0x61,0x84, - 0x80,0xfe,0x66,0xc3,0x6c,0xa3,0x1b,0x81,0x71,0x94,0xec,0x83,0x5d,0x09,0xd2,0x01, - 0x8f,0xad,0x4b,0xd3,0x8e,0x1c,0xe6,0x20,0x89,0xea,0x03,0x31,0x27,0x0e,0x52,0x5d, - 0x73,0xc7,0x27,0x63,0xfa,0x7e,0x31,0x1e,0x93,0xfa,0xe6,0xc7,0x34,0x6d,0x69,0x6f, - 0x42,0x49,0x8b,0x5e,0xd9,0x1f,0x92,0x27,0x49,0xff,0x3d,0xf6,0xca,0xce,0x43,0x17, - 0x37,0xdf,0x3b,0x3f,0x1c,0x87,0xdf,0x3b,0xcf,0x59,0x77,0x54,0x94,0x2e,0x10,0x01, - 0xd7,0xc0,0x89,0x67,0xc9,0x4f,0x6c,0x21,0x53,0x4a,0xe0,0x05,0x43,0x1a,0x92,0x36, - 0x2c,0x74,0xbf,0xa0,0xb4,0x74,0xad,0x04,0x5e,0x79,0x9f,0xa7,0x33,0x30,0x2a,0x75, - 0xa9,0xe2,0x64,0xe6,0x2a,0x38,0x74,0xe9,0x2a,0x51,0xfc,0x3c,0x81,0x48,0xc3,0xde, - 0xa5,0xc9,0xc4,0x66,0x2e,0xad,0xd8,0x28,0x14,0x4b,0x58,0x02,0x87,0xb5,0x5b,0xd1, - 0xe9,0x9c,0xfc,0xef,0x9f,0xc5,0xf7,0x7f,0xae,0x4f,0x80,0x59,0x01,0x1a,0x5a,0x64, - 0xcb,0x81,0x1b,0x96,0x89,0x8d,0xf7,0x87,0x3c,0x87,0x85,0x31,0x17,0x09,0x09,0x91, - 0x3f,0x8a,0xa5,0x9e,0x81,0xe7,0x65,0x49,0xfd,0x88,0xb5,0x39,0x2c,0x1c,0xe8,0x8a, - 0x02,0x13,0xb1,0xd6,0x14,0x71,0x41,0xf7,0xee,0xbe,0xce,0xe3,0x1b,0xca,0x99,0x1b, - 0x93,0x08,0x49,0x7c,0xad,0xad,0x72,0xbc,0x87,0x6a,0x50,0x45,0xa7,0x62,0xab,0x11, - 0x5e,0x3d,0xe2,0xe1,0x72,0xa7,0x51,0xc8,0xdf,0x26,0xe6,0xbd,0x27,0xc7,0x2e,0x61, - 0x7f,0x3e,0x5d,0x5f,0x7f,0x31,0xcc,0xe3,0xdc,0x91,0xb8,0x5b,0xc3,0x7b,0x75,0xe5, - 0x55,0x4d,0xc3,0xda,0x55,0xa9,0x79,0xa6,0x5a,0x3e,0x1a,0xc4,0x3c,0xaf,0x06,0x73, - 0x73,0x87,0x74,0x0f,0xe1,0xb3,0xdd,0x1d,0x19,0x4e,0x0d,0xb3,0x65,0x35,0x8b,0x50, - 0xd3,0xf1,0xf7,0xd5,0xb3,0x03,0xb6,0xce,0xc4,0xe8,0x04,0xdb,0xb4,0xd6,0x15,0xa2, - 0xa6,0xd2,0x4c,0x26,0xa1,0xa7,0xae,0xbd,0xc1,0xb6,0xd5,0x9f,0x1d,0xc1,0x7b,0x72, - 0xdf,0xbc,0x01,0xac,0x0b,0xcc,0xa7,0xa1,0xa8,0x04,0xdb,0x07,0x26,0x86,0xf1,0xab, - 0xdf,0xdd,0x6c,0xa8,0xe3,0x9c,0xe7,0x70,0x0d,0xc3,0xca,0xa3,0xc0,0x8d,0x24,0x0d, - 0xd2,0xf4,0x22,0x47,0xb4,0x99,0xba,0x74,0x95,0xde,0xd7,0xc1,0xab,0x11,0x63,0x99, - 0xc8,0xe4,0x88,0x5a,0xff,0x42,0x8d,0x8d,0x39,0x5d,0x62,0x1b,0x1f,0x7f,0xfc,0xf8, - 0x8b,0xc3,0xd1,0x04,0xaf,0xa6,0x34,0x1a,0x05,0xf5,0x26,0x28,0x9a,0x22,0xf3,0x69, - 0xf5,0x69,0xc0,0x21,0xa5,0xa4,0xc8,0x42,0xc4,0x46,0x06,0xff,0xa8,0x30,0x56,0xe9, - 0x22,0x0e,0xf1,0x06,0xf8,0x95,0xad,0x13,0xf1,0x72,0x2e,0x34,0x3d,0x26,0x92,0x3b, - 0xf8,0x9d,0x64,0x44,0x6a,0xab,0x41,0xfa,0xe7,0x0b,0x54,0x6b,0x04,0xd1,0xa8,0xe9, - 0xfb,0x60,0x83,0xfb,0xfe,0x25,0x9d,0x98,0xf5,0xd8,0x1d,0xdb,0x0f,0x2e,0x9a,0x5c, - 0x78,0x9a,0x2e,0xbd,0x8c,0xc9,0x65,0x8b,0x79,0x06,0x75,0xa2,0x2b,0x11,0xd9,0xc4, - 0xec,0xf2,0x75,0xb8,0x42,0xd4,0x2a,0xba,0x87,0xf1,0xa8,0xd3,0x41,0x70,0x09,0x7a, - 0x72,0x18,0xd8,0xe9,0xfc,0x4b,0xf9,0x56,0xd6,0xfa,0xab,0xf7,0x2f,0xa7,0x98,0x46, - 0x63,0xb0,0x01,0x39,0x4e,0x5f,0x9d,0xb0,0xd3,0xc1,0x0f,0x14,0xd3,0x57,0x27,0xe9, - 0x52,0x84,0x65,0x0d,0x79,0x4d,0x26,0x69,0xf7,0x6b,0xcb,0xe7,0xe5,0x06,0x1e,0x27, - 0x1b,0x22,0x7c,0x1d,0xb0,0x8c,0x67,0x9e,0x9a,0x85,0x83,0x40,0x2b,0x8d,0x97,0x02, - 0x10,0xc7,0x5d,0x4b,0x13,0x76,0xbd,0xc7,0x57,0xd0,0x12,0x6d,0xdb,0x83,0xec,0x31, - 0xd7,0x51,0x44,0x3c,0xfc,0xea,0x3c,0xe0,0xba,0x9a,0x66,0x8b,0xf3,0xd4,0x73,0xba, - 0xd6,0xc8,0xc0,0x96,0xb5,0x46,0xb1,0x48,0x17,0x79,0x20,0x5c,0x56,0x26,0xec,0xdf, - 0x61,0x25,0xa3,0x92,0xa4,0x3d,0x87,0x29,0x2d,0x1e,0x12,0x4a,0x84,0x24,0xe9,0xa2, - 0x20,0x53,0x99,0x8b,0xda,0xdc,0xfa,0x05,0x01,0x6a,0xf2,0x1d,0x6c,0xc8,0x8a,0x29, - 0x77,0x01,0x28,0x62,0x20,0xa8,0x25,0xc5,0x6d,0xda,0xc2,0x8e,0x8b,0x3c,0xe8,0x8e, - 0x6b,0xdf,0x99,0x42,0x7c,0xc4,0xb4,0x99,0x74,0x06,0x1f,0x93,0xe6,0x6f,0x10,0xa6, - 0xca,0x12,0xd4,0x12,0xfc,0x34,0xa3,0xf0,0xa0,0x4f,0xda,0x21,0x5d,0x7d,0xec,0x97, - 0xca,0x61,0xe0,0x40,0xd3,0x7d,0x80,0xb9,0x6c,0xde,0x13,0x4f,0x19,0x30,0x6b,0xc8, - 0x22,0x07,0xc7,0x9b,0x16,0x60,0x6b,0x4b,0x5b,0x36,0xdc,0xb7,0x90,0x8f,0xec,0x99, - 0xd2,0x7d,0xb4,0xb2,0xed,0x25,0x7d,0xd4,0x93,0x43,0x42,0xa7,0x48,0xe7,0xa2,0x85, - 0x52,0x6d,0x00,0x35,0x0d,0x82,0x70,0xaa,0xd1,0x3a,0x8c,0xa5,0x08,0x79,0x7e,0xac, - 0x57,0xb6,0xb5,0x5e,0xb9,0xef,0x74,0xf6,0xaa,0x10,0x9e,0x0e,0x46,0xe4,0xe9,0xb0, - 0x22,0x24,0xcd,0xd1,0xb8,0x1e,0x24,0x38,0x5d,0xb6,0x05,0x5d,0x22,0xe5,0x8c,0x74, - 0xc6,0xaf,0x9f,0x28,0x2c,0x91,0xc6,0xf3,0xd7,0x4f,0x37,0xb3,0xdb,0x37,0x2f,0xc5, - 0x8b,0x4a,0x93,0x97,0xf9,0x42,0x54,0x4a,0x53,0xe6,0x80,0x0e,0x10,0xaf,0x92,0x86, - 0x68,0x82,0x0c,0xb0,0x0b,0x3c,0x01,0x3b,0x18,0xd5,0xc1,0xc6,0xd4,0x5b,0xcf,0x0b, - 0x17,0x6d,0x62,0xe5,0xf6,0x11,0x46,0xf5,0xed,0x34,0x71,0x07,0xf6,0x1d,0x2e,0xd0, - 0xb0,0xd5,0x1c,0x91,0x84,0x55,0x77,0xdb,0xeb,0x84,0xa3,0x89,0xdf,0xc2,0x3e,0xfd, - 0x7e,0xea,0x88,0x95,0x7d,0xd5,0xe9,0x5c,0x39,0x94,0xee,0xda,0x6c,0x14,0xfe,0xe7, - 0x2f,0xc4,0x73,0xb7,0xaf,0xab,0x0b,0xca,0x60,0x86,0x8f,0x96,0x69,0xea,0x40,0x89, - 0xe2,0x47,0x67,0xa6,0xa9,0x33,0x49,0x11,0xe3,0x6e,0x0f,0xe8,0xc2,0xd3,0xf7,0x05, - 0xf6,0xbf,0xda,0x97,0xd6,0x6d,0xea,0xc0,0xab,0xdd,0x4c,0x9d,0xbb,0x7a,0xc7,0x00, - 0xad,0x97,0xce,0x34,0x0a,0x43,0x81,0x07,0x87,0xd0,0x4b,0xd6,0x7a,0xea,0xac,0xc0, - 0x80,0xb2,0x83,0x4c,0x3b,0xc0,0x19,0xc1,0x3b,0x6c,0x11,0x48,0x5b,0x6a,0xf1,0xb7, - 0x72,0xdd,0x2a,0xda,0xaa,0xf8,0x0a,0xa4,0xa4,0x15,0xad,0x3a,0x9a,0xe7,0x44,0x5c, - 0x27,0x1b,0x9f,0x93,0x48,0x5a,0x2e,0x4b,0x26,0xfe,0x5b,0x30,0x76,0x77,0x4c,0x77, - 0x0a,0x69,0x6c,0x93,0xec,0x9e,0xd9,0x8e,0x5b,0x14,0x5c,0x46,0x7c,0x6b,0x1d,0x3c, - 0x66,0xe8,0x10,0xc6,0xa0,0x3b,0xb0,0x77,0xb9,0x97,0x9b,0xfb,0xb6,0x64,0x9f,0x26, - 0xb0,0xa7,0xd6,0x74,0x66,0x2f,0x41,0x27,0x9a,0x90,0xa4,0xca,0x12,0xb8,0x92,0x6e, - 0xc5,0xfc,0x69,0x18,0x9d,0x34,0x88,0xce,0x3a,0x9d,0x25,0xfe,0x71,0xde,0x63,0x87, - 0x32,0x78,0x0e,0x5f,0x61,0xd7,0x6b,0xa3,0xc5,0xf6,0xe8,0xe7,0x9c,0x7e,0x2a,0x7f, - 0xcd,0x0d,0xb7,0x07,0x72,0x75,0x25,0xab,0x00,0xab,0x74,0x76,0xce,0x11,0x30,0x18, - 0x6e,0x69,0x93,0x5a,0xb1,0xdc,0xea,0x9e,0xb3,0x70,0x4b,0x66,0x7a,0x70,0xd9,0x17, - 0x69,0x74,0xa6,0x52,0x04,0x39,0xf0,0xe5,0x0c,0x17,0xd0,0x66,0x5e,0xb6,0x24,0x60, - 0x38,0xe1,0xd2,0x38,0x10,0x4b,0x34,0xae,0xe9,0x1a,0x9e,0x76,0xe6,0x6a,0x24,0xa5, - 0x39,0x62,0xe6,0x8f,0xf9,0x44,0xa6,0x3b,0xbe,0xd4,0xe9,0x8e,0x69,0x93,0xee,0xf8, - 0xd2,0x4a,0x77,0xd8,0x07,0x98,0xad,0xcb,0x2c,0x08,0x96,0x6c,0x60,0x33,0xef,0xcc, - 0xe1,0x64,0xd2,0x7b,0x04,0xb2,0x35,0xe3,0xed,0x96,0xe3,0xcd,0x46,0x8a,0x45,0x81, - 0x18,0x57,0x13,0xe3,0x08,0x58,0x45,0x6f,0xbe,0x54,0x99,0x86,0xe8,0xf8,0x98,0x24, - 0xfa,0xcb,0x4d,0x74,0xeb,0xac,0xac,0xb5,0xfc,0x8b,0x2e,0x7c,0x41,0x33,0xdc,0x4a, - 0x67,0x42,0x26,0xea,0xa4,0x0f,0x21,0x6d,0x8c,0x01,0x88,0x86,0x80,0xcb,0xf8,0xc0, - 0x11,0xa8,0x66,0x13,0x8a,0xf2,0xbf,0x85,0x26,0xa8,0xe0,0x3e,0xf3,0xc4,0xcf,0xa2, - 0x26,0xeb,0xc7,0x7c,0x48,0xfe,0xb4,0xe4,0xb4,0x50,0x50,0x64,0x9f,0x43,0xce,0xf8, - 0x4d,0xb0,0x30,0x09,0x01,0x06,0xfd,0x8d,0x63,0x40,0x5f,0xf9,0xaa,0xb0,0x57,0x79, - 0x44,0x9e,0x89,0x9e,0xe3,0x84,0xe1,0x9a,0x15,0x8c,0x0a,0x30,0x53,0xa1,0x1e,0x29, - 0x36,0x98,0x34,0x99,0x2a,0x8a,0xd8,0xb0,0x01,0x95,0x3c,0x42,0x5b,0x17,0x41,0xb8, - 0x0d,0x0e,0x5d,0x8c,0x38,0xfc,0x2a,0x53,0xe3,0x79,0xdf,0x28,0x2c,0x44,0x9b,0xb1, - 0x28,0xe0,0xf3,0xc8,0x08,0x90,0x32,0xaa,0x79,0x3a,0xc2,0x22,0xfb,0xb0,0x79,0x3e, - 0x39,0xe9,0x29,0x10,0xd4,0xcd,0x1e,0xc1,0x96,0x34,0xc1,0x44,0xd9,0x83,0xbe,0xb3, - 0x8a,0x3b,0x8f,0x24,0x97,0x27,0xa1,0xa6,0x16,0xc4,0xb9,0x2e,0x68,0xb8,0xad,0x5c, - 0x60,0x68,0xaa,0xc3,0x1c,0x7e,0x09,0x34,0x24,0x07,0x88,0x87,0x10,0xe5,0x6a,0x78, - 0x4b,0xd3,0x41,0x09,0x02,0x4c,0x4a,0x82,0xd7,0x41,0xf6,0xe9,0xf7,0xc5,0x68,0xb3, - 0xa1,0xe0,0x1a,0xe1,0xb6,0xc5,0x76,0xa9,0x4e,0xef,0x11,0xf8,0xb5,0xca,0x9c,0xeb, - 0x8e,0x0b,0x01,0x51,0x78,0x5e,0x59,0x2d,0xdc,0xb7,0xcd,0xe2,0x52,0x0c,0xc2,0x59, - 0x87,0x99,0xc8,0x4a,0x44,0xbd,0xa2,0x98,0x1a,0xec,0x4d,0xb1,0x03,0x41,0x81,0x2a, - 0x29,0x56,0x9b,0xda,0x40,0x57,0xce,0x13,0x14,0x15,0x03,0x40,0xc7,0x13,0xa0,0x2e, - 0x45,0x08,0xf2,0x1d,0xbc,0x4e,0x3e,0x07,0x6d,0xcb,0x18,0x2b,0x3f,0x2a,0x3f,0xa6, - 0xf9,0x25,0x87,0x2c,0x72,0x18,0x8a,0x43,0xe4,0x7b,0xbc,0xa3,0x09,0x7a,0x69,0xfc, - 0x43,0x69,0x4c,0x99,0x88,0x2a,0x9a,0x79,0xaf,0xa0,0xe5,0x2b,0x62,0x79,0x1a,0xb1, - 0x36,0x9b,0x5a,0xe9,0x33,0x49,0x59,0xe1,0x0f,0x4e,0xfa,0x92,0xce,0x80,0xe0,0xd1, - 0xd6,0x01,0x4f,0xbe,0xc8,0x40,0x9d,0xcc,0x21,0x59,0xc5,0x32,0xa8,0xcd,0x84,0x6d, - 0x63,0x29,0xf1,0x9c,0x1e,0x83,0x79,0xe7,0x9c,0x13,0xa2,0x66,0xf4,0x52,0x46,0x4c, - 0x2e,0x2c,0x59,0x2f,0x6d,0x45,0xb7,0xf6,0x9a,0xd5,0x84,0xda,0xf4,0x68,0x9b,0x50, - 0xc5,0xd8,0x5d,0x0a,0xdc,0xaf,0xbc,0x3f,0xc0,0xd8,0x92,0xd2,0x5e,0x31,0xa4,0x6d, - 0x0d,0xb9,0x9f,0x3a,0x4e,0xbc,0x76,0xfe,0xb7,0x70,0x9a,0xf4,0xbc,0x75,0xae,0xdf, - 0xb9,0x37,0xb7,0x32,0x9e,0x92,0xf9,0xe0,0x63,0x0e,0x2e,0x65,0x86,0x2d,0x1a,0x3f, - 0x74,0xc7,0x89,0x05,0x2e,0xe2,0xfd,0x04,0xc8,0x3b,0x79,0x01,0xe3,0x84,0x5d,0xd4, - 0x56,0x46,0xba,0xd8,0xc9,0x48,0xdb,0x3b,0x50,0xd6,0x0c,0xdb,0x55,0x63,0xd0,0x3e, - 0x84,0x3b,0x4e,0xb6,0xba,0xdb,0xb6,0xa5,0x21,0x80,0x0e,0x71,0xcf,0xc8,0x0f,0x27, - 0x82,0x78,0xba,0xad,0x5f,0x68,0x17,0x47,0x57,0x31,0xac,0x73,0xa9,0x11,0x6a,0xc5, - 0x66,0xda,0x2e,0x19,0x81,0x4a,0x25,0x14,0x4a,0x2a,0xb2,0x71,0x69,0xd1,0x5e,0x89, - 0x26,0x00,0xd5,0x2e,0x84,0x8d,0x75,0x3d,0x6e,0x6d,0xd3,0x7c,0x3f,0x80,0x00,0x0c, - 0x0f,0x88,0x5d,0x74,0xb2,0x58,0x4b,0xde,0x61,0xab,0x6f,0x6b,0x47,0xb2,0xa0,0xc5, - 0x51,0x2c,0x84,0x45,0xa8,0x57,0x31,0x18,0x31,0x77,0xd0,0x1f,0xc6,0x9b,0x0d,0x01, - 0xee,0x48,0xc1,0x5e,0x59,0x95,0xb9,0xb8,0xb2,0xb6,0x52,0x8c,0xc8,0x58,0x7c,0x74, - 0x56,0x7e,0x2d,0x45,0x0d,0x24,0x4a,0xd1,0x30,0xd3,0xbd,0x01,0xb7,0x5a,0xb4,0x6b, - 0x35,0x2f,0x86,0xf2,0xad,0x21,0xa1,0x29,0x5f,0x9d,0x88,0x72,0x27,0xeb,0x9a,0x7a, - 0x9c,0x78,0x1c,0x36,0x1a,0x73,0x61,0x53,0xee,0xd1,0x4e,0x39,0x5e,0x3d,0x97,0xd1, - 0xab,0x2b,0x6d,0x37,0x35,0xaa,0xa8,0x4f,0xc2,0x8b,0x92,0xb1,0xaa,0x2d,0x68,0xf8, - 0xb2,0x5a,0x0e,0xda,0x0d,0x93,0x4b,0x72,0xbd,0xa3,0xf2,0xaf,0x49,0xc3,0xcf,0xd8, - 0x83,0x98,0x98,0x9d,0x8e,0x34,0xad,0xdc,0x58,0xbe,0xa1,0x64,0x73,0x72,0x7c,0x5c, - 0xe1,0x93,0x60,0x1c,0x12,0xef,0x8b,0x9d,0xa4,0x96,0x2e,0xb3,0xa2,0x24,0x62,0x08, - 0xe6,0x75,0x32,0x28,0x8d,0xa4,0x16,0xba,0xad,0x2f,0x74,0xd1,0xb5,0x68,0xd7,0x4e, - 0xba,0x36,0x07,0xf5,0x0b,0x45,0x7b,0xd7,0x25,0x17,0x73,0xe8,0xde,0xdd,0x8d,0x17, - 0x9b,0xee,0x2b,0x30,0x7c,0xd3,0xe8,0x1b,0x53,0x71,0xc3,0x21,0x57,0x3c,0x2c,0x31, - 0xcc,0xbe,0x5d,0x1c,0xf3,0x98,0x78,0x2e,0x98,0x46,0x99,0x5a,0x9b,0xa1,0xc6,0x92, - 0x42,0x72,0xa4,0xee,0x34,0xb2,0x07,0xbf,0x56,0xdb,0x8d,0xc4,0x19,0x9c,0xf2,0x80, - 0x9b,0x59,0x4f,0xaf,0x3d,0xeb,0x7a,0x92,0x15,0x69,0x71,0xff,0x16,0x2c,0x6c,0x95, - 0xa9,0x5f,0x94,0x5d,0x93,0x42,0x2d,0x8a,0x98,0xe5,0x04,0xd2,0x99,0x49,0x51,0x82, - 0xa2,0xa1,0x42,0x9b,0x75,0xf3,0xf5,0x2a,0x25,0xc7,0xdb,0x37,0x10,0xdd,0xdb,0x55, - 0x00,0x06,0x5d,0x9d,0x04,0xf1,0x22,0x14,0xa1,0xb2,0xc8,0x94,0x8b,0x91,0xf9,0x22, - 0xd2,0xae,0x4d,0x7a,0x53,0xe5,0x8c,0x08,0x10,0xc5,0xcf,0xd2,0x48,0x52,0x68,0x47, - 0x5c,0x2d,0x35,0x6f,0x65,0xf4,0x8c,0xb9,0x1f,0xc2,0x9c,0x22,0x96,0xcf,0xb9,0xca, - 0x24,0x90,0xbb,0x79,0x46,0x4c,0xc9,0xe3,0x11,0xa7,0xaa,0x74,0x03,0xc8,0xc1,0x03, - 0x31,0xfb,0xf1,0x31,0x13,0xe5,0xed,0x80,0xb5,0xaf,0xd5,0x4c,0xa2,0x5e,0x65,0x3d, - 0xdf,0x57,0x25,0x96,0xd6,0x32,0x63,0x34,0x18,0x72,0x3a,0x68,0xef,0x6b,0x43,0xb9, - 0x38,0x5b,0x29,0x6d,0x9a,0x05,0x69,0xf8,0x9c,0x62,0x88,0x4a,0x76,0x58,0x99,0x8a, - 0x44,0x39,0x89,0xc7,0xdd,0xb4,0xc9,0xa0,0xbc,0x06,0x3b,0xdb,0xf0,0x1a,0x0f,0xf2, - 0x02,0x4c,0xdd,0x0c,0xd5,0xe6,0x99,0xbf,0x0e,0x37,0x9b,0x94,0xe8,0xc4,0xe7,0xc4, - 0xa5,0xa6,0xa6,0xe0,0xa6,0xd5,0x11,0x09,0xc2,0x98,0xfe,0x31,0x2c,0x74,0x8f,0x51, - 0x69,0x25,0x3c,0xc9,0xa5,0x62,0x01,0xe1,0x47,0x6f,0xe1,0x30,0x84,0xd2,0x2e,0xf7, - 0x7a,0xa0,0x42,0xcd,0x5f,0xfb,0xa8,0xa8,0x78,0x35,0xe9,0xee,0x86,0xb4,0x96,0xee, - 0x2c,0x27,0xcb,0x5d,0xb4,0x5e,0xf4,0xfb,0x4d,0x8f,0x3a,0x96,0x38,0x78,0xda,0x2d, - 0x83,0x63,0xf1,0x7c,0x37,0x29,0x56,0xe5,0x9e,0xd3,0x99,0x77,0x08,0xd2,0xb2,0x66, - 0xed,0x74,0x8a,0x7a,0x63,0x32,0xad,0x95,0xad,0x72,0xd8,0xf3,0xa2,0xd7,0x53,0x71, - 0xef,0xf6,0xa0,0x31,0x84,0xec,0x91,0xc3,0xc6,0x59,0xae,0xa2,0x21,0xa4,0xcc,0xe6, - 0x4b,0x96,0x37,0xe9,0x53,0xb3,0x19,0x95,0x2a,0xe7,0x13,0x65,0x6f,0x5c,0xb0,0xac, - 0xb1,0xc8,0xe2,0x94,0x38,0x78,0x95,0xc8,0x54,0x29,0xb1,0x37,0x6f,0x58,0x93,0x8b, - 0x36,0x82,0xcb,0x46,0x39,0x0a,0x76,0x35,0x8a,0xa1,0x21,0x73,0xa4,0x68,0x2f,0x64, - 0x96,0x03,0x50,0xf4,0xdd,0x0b,0x04,0x8d,0xf6,0x9d,0x0c,0x2a,0xe6,0xde,0x3a,0x0a, - 0xdd,0xb9,0xb0,0xa7,0xee,0xf4,0x7c,0x80,0xa0,0xf3,0xce,0xbd,0xe3,0xbf,0x6a,0x43, - 0x6f,0xcb,0x16,0x74,0x14,0x9c,0x8f,0x10,0x5b,0xa5,0x45,0xf9,0x13,0xfc,0x44,0x98, - 0xd1,0xee,0xdc,0x72,0x9f,0x34,0xab,0x14,0x79,0x3e,0xb2,0xab,0xf3,0x96,0x09,0xd5, - 0x03,0xed,0x7b,0x3c,0x63,0x1e,0x98,0x6f,0x36,0x73,0x27,0x22,0x72,0xce,0x85,0xa6, - 0x55,0xe6,0xce,0xd4,0x42,0xb0,0x4d,0x8f,0x6e,0xd5,0xea,0xb1,0x6b,0x02,0xaa,0x8a, - 0x52,0x18,0xd5,0x23,0xd9,0xf5,0xae,0xd3,0xe1,0x8c,0x97,0x85,0x49,0xd2,0xa6,0xa9, - 0xc4,0x9e,0xb3,0xfd,0xef,0x73,0x10,0x30,0x80,0xf6,0xb9,0x98,0xfa,0xd0,0xa4,0x31, - 0xe1,0x0a,0xc6,0xc7,0xc4,0xd2,0x64,0x2e,0xa7,0xe5,0xb5,0x92,0x0e,0xf7,0x5d,0xe1, - 0x50,0xc9,0x15,0x82,0x80,0x1a,0x73,0x55,0x34,0x41,0xfb,0x87,0x50,0xa5,0xbc,0xc7, - 0x4b,0x7a,0x55,0x24,0x58,0x5e,0xb3,0x90,0xc4,0x30,0x6d,0x1d,0x0c,0x67,0x3e,0x67, - 0x82,0xf3,0x16,0x15,0x71,0x3a,0x1d,0xe1,0x00,0xa5,0xdf,0xfd,0x78,0x21,0x2c,0x42, - 0x11,0x63,0x69,0xa9,0x72,0xed,0x61,0x8b,0x68,0x94,0xe6,0x24,0xf1,0x1a,0xe8,0xf1, - 0xe6,0x68,0xa1,0x46,0x91,0x85,0x1e,0x50,0x43,0xaa,0xd0,0x83,0x32,0x39,0xf0,0x1e, - 0x08,0xc5,0x4b,0x31,0xf6,0x17,0x31,0x65,0xfc,0x28,0xd7,0x44,0x84,0x65,0xe8,0x9e, - 0x69,0x56,0x36,0xce,0x34,0xb7,0xac,0x69,0x08,0xbd,0x86,0x0a,0x59,0x9d,0xa6,0x98, - 0x78,0x1c,0x13,0xf2,0x18,0x36,0x55,0x77,0x94,0xc3,0x03,0x26,0x34,0x34,0x0a,0xf1, - 0xfc,0x04,0xe3,0x4a,0x04,0x60,0x5e,0xf7,0xd1,0x46,0xd6,0x31,0x21,0xd0,0xc0,0x22, - 0x98,0xf6,0x68,0x21,0xe7,0xd2,0x9a,0x89,0xdc,0x0d,0x01,0xb0,0xb0,0x5a,0xfd,0x90, - 0x92,0x54,0xad,0x1c,0x91,0x35,0xe4,0x51,0x21,0x5c,0x6a,0xd0,0x5e,0x4f,0x0e,0x8b, - 0x41,0xa5,0xad,0xfc,0xcb,0x71,0x89,0xfd,0x09,0xc6,0xdb,0x7e,0x9d,0x47,0x3c,0x68, - 0xb9,0x5c,0xc0,0xc3,0xa6,0xbc,0x0e,0xac,0xf2,0x4e,0xfe,0x2e,0xab,0xf4,0x02,0x7a, - 0x8c,0x59,0x66,0x15,0xf9,0xb2,0x5a,0x62,0x7f,0x97,0x9b,0x08,0x06,0x3b,0x61,0x6e, - 0xbd,0x3f,0x19,0x87,0x24,0x90,0x60,0x3c,0x99,0x9a,0x8c,0xf2,0xf9,0x8a,0x24,0x93, - 0x8c,0x96,0x1f,0x70,0x92,0xb2,0x10,0xf1,0xd8,0x86,0x85,0x0a,0x74,0xbf,0x62,0xb9, - 0xc8,0xe3,0xee,0x48,0x8a,0xea,0xa2,0xde,0xb3,0x52,0xfb,0xbe,0xc3,0x85,0xaa,0xe5, - 0x90,0xa5,0x1d,0xec,0xb9,0x9b,0xf0,0x68,0x47,0x75,0xaa,0x64,0xc1,0x1d,0xf5,0x48, - 0xc8,0x5f,0x52,0x0a,0x8c,0x94,0x30,0xb1,0x9d,0x74,0x91,0x32,0x91,0x83,0x46,0x73, - 0xda,0x9c,0x26,0xe7,0xec,0x43,0x42,0xc1,0x62,0xf1,0xfe,0x81,0x7b,0x52,0xae,0x2d, - 0x9a,0xf0,0xa6,0xac,0x69,0xdd,0xf4,0x59,0xcc,0x12,0x18,0x75,0x87,0xd2,0x30,0xf5, - 0xae,0x32,0xdf,0xe9,0xfb,0x3b,0xcc,0xd4,0xf2,0xe1,0x4d,0x05,0x82,0x2c,0x7d,0x2e, - 0xe4,0x42,0xb0,0x03,0xf0,0x6f,0x0c,0x0b,0x32,0x84,0xe6,0xed,0x8d,0x8e,0x43,0x33, - 0x22,0x83,0xbd,0xdd,0x6c,0x1a,0x08,0xfa,0x18,0x15,0xeb,0xd6,0x0f,0x5b,0x2e,0x8b, - 0x72,0x83,0x47,0xd2,0xde,0x1c,0x52,0x55,0x0b,0xd8,0x45,0xd5,0x29,0xc1,0x00,0xab, - 0x82,0xa5,0x01,0xa7,0x24,0x4a,0xaf,0xa6,0x0c,0x4d,0xb0,0x54,0x49,0x46,0x09,0x76, - 0xb3,0xe1,0x7b,0x85,0x3a,0xe5,0x7d,0x54,0x81,0x8c,0x37,0x62,0xed,0x08,0x1d,0x0c, - 0xc1,0x7f,0x70,0xd4,0x62,0x4a,0xcb,0x55,0x35,0xb2,0x43,0x64,0x9b,0xa6,0xad,0x96, - 0xb2,0x25,0xd0,0x6c,0xb2,0x18,0x9c,0xe7,0x49,0x2f,0x9c,0x8b,0xa6,0x60,0xc2,0xea, - 0xda,0xa9,0xca,0x0d,0xa8,0x2c,0x98,0x7c,0x32,0x92,0xf6,0xde,0xf7,0x2e,0x1d,0x1f, - 0xd3,0x5d,0x56,0xb5,0xa8,0xf6,0x4c,0x2a,0x19,0xe1,0xc7,0xae,0x79,0xfc,0x19,0xfa, - 0xa4,0x9c,0x52,0xee,0xf3,0x18,0xde,0xf3,0xf1,0x88,0xca,0x15,0xaa,0x24,0x1d,0x57, - 0x69,0x35,0xfb,0x03,0x54,0x6b,0xc3,0xe5,0x6b,0xb8,0x54,0x85,0x5e,0x54,0x88,0xe0, - 0xd3,0xd6,0x5a,0xf7,0xc7,0x9f,0xbf,0xfc,0x76,0xbd,0xb9,0xfe,0xf0,0x7f,0xae,0xdf, - 0xfd,0xfa,0xe1,0xdd,0xe6,0xea,0xc3,0xe7,0x0f,0x17,0xd7,0xd6,0x3f,0xd4,0x6e,0x9b, - 0xef,0x94,0xfe,0xe4,0x67,0xda,0xe3,0xb2,0x14,0x5b,0x71,0x42,0x00,0x1a,0x15,0xcb, - 0xb0,0x5b,0x08,0x34,0xe3,0xad,0xb9,0xd6,0xa3,0xca,0x74,0xcc,0x6c,0x73,0x00,0x7d, - 0x0c,0x1c,0x07,0xdb,0x9d,0x02,0x06,0xb2,0xa8,0xaa,0xae,0x6c,0x30,0xfc,0x5c,0x11, - 0xba,0x5b,0x91,0xb4,0x36,0xef,0xdb,0xca,0xa3,0x55,0x7b,0x40,0x5f,0x54,0xf0,0xe4, - 0x1a,0x6a,0x6f,0x4f,0x84,0x55,0xb1,0x18,0x84,0x2f,0x9a,0xcf,0x17,0x25,0xed,0x06, - 0x72,0xe2,0x84,0x1c,0x49,0xb9,0x65,0x40,0x65,0x64,0x2b,0xe9,0x49,0xe2,0x31,0xc1, - 0x51,0xf5,0x33,0x5c,0x0f,0x10,0xc0,0x0a,0x52,0x12,0xa6,0xde,0x4b,0x8a,0xb1,0xc0, - 0xb9,0xda,0x34,0xe7,0xdd,0xf5,0xbb,0x45,0x84,0x40,0x19,0x3e,0x22,0x1e,0x4e,0xd3, - 0xb9,0xb2,0xd5,0xda,0x86,0xcd,0xf8,0x17,0xc9,0x97,0x72,0xbb,0xf4,0xca,0x0e,0x3c, - 0xc5,0x6b,0x69,0xee,0x34,0x1b,0x4e,0x76,0x64,0xc7,0xc3,0x6a,0x7b,0x52,0xb9,0x1a, - 0x70,0xea,0x47,0x74,0xb3,0x8c,0xfc,0x4f,0xb2,0x74,0x42,0x3e,0xa2,0x8c,0x2f,0xaf, - 0x64,0x9d,0xf8,0xdd,0x6c,0xda,0x29,0xd7,0xcd,0x46,0x4f,0xb5,0xb0,0x87,0xbd,0xd9, - 0x04,0x9d,0x4e,0xe0,0x14,0xb0,0x16,0x97,0x30,0x73,0xe8,0x87,0xa0,0x71,0x91,0x91, - 0x63,0xf5,0xf6,0x79,0x1f,0x10,0x9a,0x8d,0x61,0xbe,0x7e,0xeb,0x0d,0x5e,0xf4,0x37, - 0x1b,0xe9,0xcf,0x67,0xe1,0x1b,0xb8,0x78,0x96,0x9e,0xef,0x96,0x51,0xf0,0x37,0x32, - 0x6c,0x08,0x0c,0x07,0x8f,0xde,0xe1,0x08,0xfd,0x6f,0x15,0x90,0x55,0xa1,0x30,0xb9, - 0x7c,0x54,0xb9,0xdd,0xe9,0xc4,0x30,0xfc,0x87,0xe4,0x2e,0x54,0x51,0x46,0x2c,0xa3, - 0x37,0x10,0x65,0x3f,0xfb,0x31,0x98,0x31,0xc9,0x82,0xb5,0x83,0xc6,0x93,0xde,0x4c, - 0x3d,0xea,0xa3,0x4c,0x81,0x74,0x9c,0x2a,0x47,0x49,0x97,0xec,0xf6,0x2e,0x80,0xbe, - 0x3f,0xa0,0x6b,0xa8,0x0a,0x72,0x95,0xee,0x19,0x79,0x2a,0x95,0x43,0x89,0x87,0xcc, - 0xa3,0x3d,0xc4,0xae,0x65,0xc7,0xb4,0xa3,0x44,0x71,0xac,0xef,0xfd,0xfc,0xee,0x77, - 0x55,0x8e,0x69,0x39,0x05,0x6d,0xcb,0x34,0xd9,0x68,0xfb,0xa1,0x1e,0xf5,0xfe,0xe6, - 0xf4,0xb6,0xf7,0x80,0x9f,0xad,0x45,0xdc,0x04,0xa4,0xba,0x94,0x0d,0xb1,0xec,0x48, - 0x5f,0x20,0x7f,0x67,0x81,0x7c,0x2c,0x50,0xa7,0x43,0xbf,0x50,0xf2,0xa0,0x69,0xc6, - 0x9b,0xea,0x27,0xdd,0xf3,0xc3,0x3f,0x4f,0xac,0x4a,0xd2,0xe5,0x53,0xcb,0x8a,0x65, - 0x92,0xa0,0xba,0x1f,0x1e,0x74,0xb5,0xec,0xa7,0xc8,0x94,0x8f,0x7f,0x08,0x56,0x3d, - 0x54,0xcc,0xae,0x07,0xc1,0x31,0xda,0xab,0xfd,0x48,0x66,0xcb,0x2a,0x82,0xef,0x5a, - 0x2d,0xe7,0x9d,0x40,0xd9,0xc4,0x30,0x2d,0x07,0xff,0xad,0x17,0xd7,0x7b,0x99,0x5c, - 0x64,0xb0,0x5f,0x9b,0x34,0x2b,0xcd,0x6a,0x45,0x53,0x28,0x15,0xb0,0x9d,0x6d,0x86, - 0x26,0xf9,0x10,0xdf,0x20,0xf8,0xbf,0x3d,0x36,0xcf,0x97,0x1e,0xbf,0xde,0x70,0x00, - 0xd7,0x9b,0x58,0x8f,0xb6,0xcc,0xf7,0x63,0xdd,0xda,0x63,0x90,0x3b,0x4c,0x8d,0x66, - 0xba,0xa2,0xbd,0xc5,0xd0,0xf8,0xed,0x47,0xdd,0x2f,0xe7,0xd0,0xb8,0xe4,0x5a,0x3c, - 0x22,0x5f,0xb9,0xc2,0x0c,0xef,0xbc,0x1b,0xb4,0xdc,0xd6,0x24,0x68,0xbb,0x43,0xf0, - 0x87,0xca,0x55,0xfb,0xa4,0x80,0x29,0x4d,0x43,0xf8,0xf8,0x98,0x42,0x18,0x2d,0xa1, - 0x5f,0x43,0x99,0x0d,0x20,0x85,0x4d,0x9b,0x7c,0xc7,0x5d,0x4e,0x82,0x88,0x3c,0x37, - 0xcf,0x4d,0x43,0xc0,0x4e,0x70,0x1f,0x1d,0x64,0x49,0x63,0xe8,0x45,0xd1,0x14,0xe7, - 0x94,0xe5,0x13,0xfb,0x8e,0x8f,0xf0,0xd9,0xda,0xcd,0x08,0x2f,0xc5,0x73,0xf7,0x39, - 0x47,0x64,0x2a,0x17,0xe5,0x3f,0x34,0xde,0x26,0xe6,0x69,0x8b,0x18,0x93,0x9f,0x79, - 0x33,0xd8,0x7c,0x70,0x45,0x6a,0x4a,0x05,0x48,0xad,0x0d,0x75,0xf6,0xd3,0x42,0xc4, - 0xce,0xbf,0xb3,0xf1,0x35,0x27,0x2e,0x77,0xd0,0xa7,0x96,0xbd,0x29,0x95,0x74,0x26, - 0xb3,0x27,0x84,0x1b,0x5f,0xf0,0x98,0xd6,0xce,0x0b,0x4d,0xce,0x64,0x26,0x03,0x3c, - 0x9e,0x0f,0xe5,0x2c,0xd4,0x98,0x4f,0x50,0x82,0x3c,0xff,0x36,0x2a,0x9e,0x57,0xc2, - 0x2b,0x8f,0x77,0x17,0xec,0x1b,0xe8,0x21,0xf6,0xb0,0xcf,0x48,0x04,0x0e,0x64,0xb2, - 0x8b,0x54,0x7d,0x43,0xb1,0x91,0x9d,0xe9,0x71,0x39,0x0c,0x65,0x22,0x43,0xa9,0x51, - 0x25,0x6f,0x23,0xaa,0x4f,0x25,0x1b,0x17,0x7a,0x29,0xdd,0x35,0xe3,0x94,0xe9,0x64, - 0x12,0xcb,0x71,0x08,0xa8,0x69,0xe3,0x5d,0x52,0xab,0xf8,0x33,0xda,0x39,0x0d,0xe3, - 0xe7,0x91,0xaf,0x3a,0x99,0xa4,0xc5,0x4c,0x15,0x81,0x8d,0x14,0xb2,0x7b,0xbb,0x52, - 0xd8,0xdf,0xa5,0xde,0xdf,0xf2,0xa3,0xb5,0x8c,0x5d,0x57,0xee,0x33,0xb5,0xa4,0xa6, - 0x6e,0xdc,0xe7,0x69,0x5f,0xd7,0x07,0x6b,0x6c,0x99,0x22,0x52,0xc7,0x68,0xe8,0xa8, - 0x85,0x63,0x7c,0x8c,0xee,0x21,0x5f,0xf2,0x1c,0x44,0x61,0x1b,0x73,0x12,0x7f,0xca, - 0xeb,0xb4,0xcf,0x60,0x10,0x9c,0x6c,0x4a,0xa6,0xff,0xac,0x9f,0xdd,0x1b,0x23,0xca, - 0xad,0x52,0xaa,0xc9,0x38,0x96,0x0d,0xd0,0x9b,0xea,0x96,0x4e,0x4a,0xd0,0xe9,0x08, - 0xc3,0x33,0x5e,0x0d,0x86,0x08,0x3e,0x8b,0x59,0x99,0x66,0xe8,0x45,0xcf,0xd0,0x78, - 0x36,0x70,0x58,0xee,0x2e,0xae,0xae,0x3c,0xd3,0xa5,0x83,0x2c,0x6b,0x3f,0x8e,0xdd, - 0x28,0x89,0xca,0xc8,0x8f,0xdb,0x87,0x36,0x9a,0x43,0x0a,0x43,0xc4,0xd4,0x11,0x4d, - 0xd6,0x85,0x77,0xe9,0x93,0x23,0xb7,0xff,0x40,0x87,0xf6,0xc6,0x93,0x47,0x3b,0xb4, - 0x3e,0x74,0xf2,0xa1,0xd7,0xf3,0x55,0x27,0x2c,0xbe,0xad,0xce,0xb3,0x58,0x74,0xfa, - 0x47,0x35,0x53,0x7e,0x4c,0x5e,0x8d,0x27,0xf6,0xb3,0x0f,0x97,0x1f,0x7e,0xb8,0x7c, - 0x6f,0x51,0x87,0x85,0xbb,0xef,0x30,0x48,0xaf,0x37,0xfb,0x8b,0x53,0x20,0x3c,0xec, - 0xb7,0xcf,0x97,0x2c,0xf6,0x1c,0x2a,0x81,0x50,0xf1,0xd9,0x1a,0x75,0xcb,0xc7,0x91, - 0xec,0x67,0xaf,0xde,0xbd,0x3a,0xfd,0xe1,0xd4,0x1a,0xd2,0xf1,0x0e,0x77,0x80,0xb5, - 0x38,0x19,0x28,0x18,0xf3,0x34,0x49,0xed,0x45,0xc4,0x7f,0x8b,0x0c,0x7e,0xa0,0x7d, - 0xf5,0xf1,0x27,0x5c,0xf7,0x7e,0x15,0x93,0x45,0xec,0xe7,0xf6,0x4f,0x02,0xd1,0x9e, - 0x5d,0x3f,0x96,0xa7,0x53,0x6e,0xa4,0x33,0x74,0xbb,0xae,0x56,0x22,0xc1,0xaa,0x6b, - 0xa7,0x45,0x7c,0x79,0xc0,0x07,0x0b,0x06,0xbb,0x1b,0x41,0xe5,0x41,0x44,0x7b,0xa1, - 0x08,0x60,0x55,0x78,0x7d,0xa8,0xfb,0xd6,0x77,0xc7,0x69,0xb0,0x28,0x7a,0xcb,0xa8, - 0x88,0xc8,0x6f,0x04,0xcf,0x97,0x78,0xd6,0x6a,0x5c,0x43,0x0b,0x50,0xca,0xca,0x3d, - 0xdd,0x99,0xbb,0x0f,0xdf,0x56,0x3e,0xa2,0x64,0x28,0x6d,0x6a,0xf6,0xd0,0x45,0x9e, - 0x52,0x89,0x92,0xf5,0xdc,0xbf,0x57,0x67,0x86,0x06,0x03,0xe2,0xbd,0x6a,0xa1,0x5f, - 0xd1,0xf5,0xdc,0xcf,0x27,0x51,0xe2,0xf6,0x0d,0x7f,0x51,0xa6,0xc3,0x0c,0x1a,0x89, - 0xf8,0xa3,0x6f,0xd0,0x91,0x97,0x9a,0xb7,0x26,0x79,0x14,0x0e,0xe9,0xa7,0x07,0x03, - 0x98,0x91,0x5b,0xea,0x1e,0x8d,0x8c,0xc0,0x28,0x8f,0x24,0x33,0x1f,0x21,0x50,0x33, - 0x12,0x79,0x73,0x32,0x8f,0x12,0x99,0xab,0x1b,0x8c,0x73,0x8b,0xc1,0x4a,0xd8,0xa0, - 0xc2,0x62,0x9e,0xf4,0x26,0x7e,0xe6,0x0e,0x80,0xde,0xd0,0x87,0x65,0x4e,0x7a,0x11, - 0x20,0x16,0x2e,0x9d,0x8c,0x12,0xb9,0xc4,0x78,0xb4,0xe6,0x91,0xe8,0x08,0x8e,0x3b, - 0xaa,0x51,0x18,0xc7,0x62,0xdf,0x2b,0x43,0x02,0xf7,0x8a,0xe6,0x51,0x1f,0x8c,0xea, - 0x0f,0x69,0x4f,0x91,0xaa,0xe8,0x5c,0xb9,0x32,0x54,0xba,0x54,0x8a,0x1e,0x2f,0x19, - 0xb9,0x54,0xb9,0x9f,0xc9,0x91,0xa6,0xeb,0xbf,0x0b,0x3d,0x16,0x54,0x9a,0xc7,0x20, - 0x88,0x3c,0xce,0xe0,0x4c,0xcc,0xe5,0x42,0x96,0x10,0xec,0x82,0x02,0x4c,0x77,0x41, - 0xd5,0xdb,0x81,0x0f,0xbf,0x8c,0x81,0x87,0x6b,0x02,0xca,0xab,0x3b,0x94,0x88,0xbd, - 0x68,0x48,0x4f,0x97,0x8a,0x9f,0xe9,0x80,0xd2,0xa2,0x70,0x5f,0xf4,0xbf,0x7b,0xcc, - 0xc0,0xbe,0x25,0x85,0x96,0xd5,0x0f,0x16,0xa5,0x6f,0x60,0x55,0xea,0x45,0xdf,0x52, - 0x5d,0xe1,0x5a,0xe7,0x70,0x3e,0x60,0x67,0x3f,0x7b,0xf1,0xee,0xc5,0x8b,0xb3,0x97, - 0xd6,0x23,0xac,0xfb,0xaf,0xc4,0x5c,0x22,0xb7,0x1c,0xad,0xab,0xa5,0x26,0x41,0x7a, - 0x59,0xa3,0xf3,0x48,0xae,0x7c,0x6b,0x07,0x51,0x5a,0x3a,0x7d,0x4c,0xff,0xf1,0x38, - 0x83,0x6f,0x11,0x07,0x63,0x43,0x89,0x11,0xe6,0xfb,0xa6,0xab,0x43,0x9e,0x49,0xf9, - 0x72,0x12,0x8d,0x21,0x92,0xd6,0x42,0x2b,0x6a,0x32,0x53,0xb7,0x96,0xb2,0xe2,0x80, - 0xde,0xbd,0xcb,0xbc,0x57,0x04,0x74,0xde,0x80,0x8e,0xe4,0xc9,0x57,0x79,0x55,0x64, - 0x63,0xaf,0x48,0xfc,0xac,0x47,0xe9,0x00,0xf7,0x9e,0x76,0xa7,0xef,0xa3,0x79,0x54, - 0x3e,0x34,0x72,0xd1,0x63,0x49,0x68,0x04,0x83,0xee,0xb4,0x93,0x61,0x83,0x3d,0xdc, - 0xd1,0x27,0xee,0x90,0x9a,0x32,0x73,0x9f,0xf5,0xfb,0x74,0x3c,0xf1,0xae,0xba,0x98, - 0xbb,0x24,0xa8,0xc0,0x64,0x42,0x04,0x25,0xf7,0xe9,0x75,0x3f,0x14,0x13,0xa5,0xf5, - 0x32,0xcb,0xa6,0x7e,0xc6,0xe9,0x59,0x76,0x2f,0xaf,0x10,0x3c,0x04,0xdd,0x41,0xbf, - 0xff,0x9d,0xd1,0xe3,0x56,0x4b,0xf5,0xbc,0x23,0x8d,0xba,0x12,0xa3,0x59,0x54,0xf6, - 0xe6,0x7e,0x31,0xeb,0x45,0x73,0x78,0xa9,0x95,0xaa,0xb3,0x86,0x7b,0xda,0x14,0x39, - 0x5d,0xb7,0x7a,0xaf,0x26,0x4b,0x4b,0x77,0x6d,0x9d,0x71,0x4c,0x27,0x0a,0x5d,0x5e, - 0x3e,0x84,0x4e,0x40,0x12,0x6d,0xf9,0x9a,0x66,0xa1,0xb7,0x49,0x70,0x86,0xaf,0x71, - 0xfa,0x5f,0x09,0x94,0xa6,0x60,0x68,0xd1,0x1e,0x4b,0x66,0x6b,0x55,0x18,0x40,0xa5, - 0x1c,0x68,0x24,0xc5,0x39,0x6d,0xfe,0xe3,0x27,0x4e,0x9a,0xb4,0x9a,0xa3,0x96,0xe8, - 0x44,0x09,0xb4,0x22,0xc4,0x87,0xf4,0x62,0x23,0x3f,0x6c,0x55,0x58,0xd1,0xb2,0x45, - 0x5d,0x50,0xa7,0x6f,0x1f,0x7b,0xa4,0x05,0xd0,0xe6,0xf0,0xc3,0x3e,0xb9,0xd1,0x28, - 0xb4,0x4f,0x74,0xd4,0x09,0x41,0x79,0x96,0x71,0x1f,0x01,0xfe,0xbf,0xa4,0x89,0x97, - 0x20,0xd0,0x04,0x24,0x18,0x36,0xea,0xfe,0xec,0xe5,0x72,0xf5,0x48,0x19,0x32,0xa8, - 0xba,0x51,0xc4,0x71,0x94,0x15,0x51,0x51,0x61,0xaa,0x93,0x70,0x61,0xed,0xe7,0xec, - 0x1d,0x64,0x24,0xcb,0x04,0xce,0x6a,0xbd,0x07,0xc6,0x8e,0x48,0xd3,0x62,0x95,0x13, - 0x0d,0xdd,0x92,0xee,0xf7,0xac,0x6a,0xa4,0x98,0x35,0x9b,0xac,0x6b,0x47,0xc6,0x1f, - 0x81,0xc0,0xb0,0xe1,0xc0,0x6a,0x5c,0x42,0xfa,0xa9,0x32,0xa8,0x3f,0xdc,0xd1,0x05, - 0x44,0xe3,0x3d,0x8a,0x65,0x0b,0x48,0xc4,0x22,0x7e,0x02,0x91,0x60,0x68,0xd9,0xc4, - 0x38,0x2b,0x8c,0x60,0x31,0x8a,0x82,0xde,0x48,0xfc,0x27,0x12,0x79,0xd7,0x19,0xd8, - 0xce,0x2b,0xdb,0x39,0xb5,0x07,0x96,0xa1,0x12,0x88,0xc5,0xf6,0xbf,0x66,0xe2,0x61, - 0x9c,0x23,0xa6,0x29,0x0c,0x20,0x53,0xa6,0x6b,0x39,0xe0,0xeb,0xd3,0xef,0xd4,0x39, - 0xd0,0x72,0xd5,0xa0,0x38,0x26,0xdf,0xb0,0xc2,0x2f,0x67,0x84,0xfa,0x43,0xe5,0xb1, - 0x30,0xef,0xb4,0xe4,0xe3,0xeb,0xa2,0x28,0x11,0xef,0xf7,0xaa,0x23,0xb8,0x95,0x8c, - 0x48,0xde,0xe8,0x71,0x82,0xbd,0x90,0x72,0xf5,0x9f,0x1e,0xd7,0x35,0xb9,0xa7,0x83, - 0xb3,0x1f,0xce,0x5e,0x3d,0x7f,0x79,0xf6,0x83,0x1a,0x5c,0xb3,0xee,0x95,0xa6,0x58, - 0xae,0xa0,0x2a,0x9e,0x83,0x10,0xd6,0x13,0xcc,0x5c,0x31,0x30,0x2c,0x9b,0xc1,0xa6, - 0x78,0x57,0x9f,0x91,0x27,0xa1,0xe8,0x39,0x70,0xce,0x9e,0x32,0x0d,0x8b,0x7d,0xa6, - 0xe1,0x11,0xed,0xb9,0xf6,0xd9,0xd6,0xfc,0xbb,0x53,0xfb,0xd9,0xe0,0xf9,0x60,0x30, - 0xe8,0x5b,0x6d,0xde,0x88,0x88,0x37,0x1c,0xb1,0x9f,0x8b,0xf4,0x73,0xc0,0x1a,0x83, - 0xff,0xf0,0x9a,0xce,0x40,0xb3,0x1d,0x6c,0x69,0xb0,0x56,0xf7,0xdd,0x23,0xd3,0xe4, - 0x13,0xe9,0x96,0xa3,0x91,0x67,0x7a,0xfe,0xd8,0xcf,0x49,0x1a,0x4f,0xe7,0x84,0xdd, - 0x99,0xbf,0xe3,0xe1,0x80,0xb6,0xd0,0x4b,0xeb,0xca,0x8c,0x34,0x83,0x6c,0xe9,0x50, - 0x70,0x1d,0x58,0xc9,0x54,0x4c,0x95,0x22,0x68,0xd2,0xaa,0x75,0x26,0xf2,0xe4,0xcf, - 0x93,0xe3,0x7f,0x9c,0xd8,0x88,0x9d,0x11,0xb8,0x9e,0x98,0x5a,0x92,0x67,0xc4,0x55, - 0xa4,0xea,0xcd,0x26,0xbf,0xc3,0x79,0x0f,0x3e,0xdc,0xd8,0xe9,0x54,0x07,0x1c,0xf1, - 0xaa,0xfe,0x26,0xa5,0xee,0x11,0x29,0xad,0x65,0x6a,0x88,0xe1,0x54,0x79,0xfd,0xec, - 0xb8,0x3b,0x3a,0x57,0xf9,0x09,0x0a,0xd8,0x5b,0xe7,0x0a,0x55,0x71,0x8a,0xcc,0x21, - 0x50,0xe2,0x47,0xff,0x70,0x43,0xac,0x27,0x91,0x9a,0x24,0x53,0x64,0x7f,0xa5,0x0c, - 0x93,0x2d,0xec,0x5c,0x4f,0x12,0x51,0xde,0x48,0xcf,0x13,0xad,0x85,0x47,0x4d,0x37, - 0x11,0x6f,0x03,0x88,0x4e,0x47,0xd5,0x62,0x8a,0x9b,0xfe,0xad,0x76,0xdc,0x6d,0x4f, - 0xea,0x88,0x7a,0x58,0x9d,0x8e,0xb8,0x19,0xdc,0x5a,0x71,0x55,0xe8,0x27,0x8f,0xac, - 0x1e,0x7f,0x3d,0x3e,0xb6,0xfb,0x36,0x9d,0x77,0x91,0x87,0x81,0x79,0xaa,0x8f,0xab, - 0x7b,0x7c,0x2a,0x56,0x24,0x84,0x76,0xd3,0x1d,0x0d,0xbe,0x71,0x0b,0x59,0xff,0x71, - 0x0a,0xc4,0x47,0xe8,0xea,0xf3,0xa6,0x88,0xc7,0xe4,0x8d,0xab,0x0c,0x96,0xdf,0x82, - 0xca,0xcd,0x03,0x9e,0xa4,0xea,0x81,0xd9,0x61,0x29,0x7c,0x3d,0x77,0x92,0x26,0x26, - 0x5e,0xdb,0x13,0x29,0xc3,0x78,0x90,0x59,0xa1,0x8c,0x02,0xed,0x06,0x5a,0x5b,0x42, - 0x5a,0xcf,0x9f,0xf8,0x54,0x53,0x1b,0x6b,0x68,0x80,0x65,0xe4,0xd7,0x23,0xa4,0x59, - 0xfd,0x0c,0xcd,0x24,0x4b,0x17,0xe4,0xfd,0x8f,0x49,0x99,0xfe,0x1e,0x89,0x55,0x55, - 0x7c,0x26,0xcf,0x1b,0xc8,0x67,0xea,0xdc,0xcc,0x84,0x76,0x79,0x53,0xe7,0xc0,0xa7, - 0x54,0xb2,0x73,0xb7,0x10,0xf9,0xc3,0x15,0x1f,0x94,0xa7,0xad,0x72,0x87,0xf6,0x6e, - 0x38,0x73,0xe1,0xd3,0x12,0x52,0x8f,0x89,0x28,0xdf,0x93,0xf8,0x83,0xba,0x17,0x54, - 0x8a,0x50,0xfe,0x4a,0x05,0xe8,0x60,0x30,0xcf,0x7f,0xf2,0x19,0xbd,0xd7,0x20,0x78, - 0xec,0xe5,0x0e,0xa9,0xd0,0xe3,0xdc,0x61,0xb9,0x3d,0x39,0xed,0x09,0x6e,0xc0,0x1f, - 0xd5,0x00,0xb6,0x44,0x78,0x5e,0x74,0xdb,0xb5,0x24,0xaa,0x6d,0xbd,0xc3,0x9e,0xea, - 0xec,0x50,0x6b,0x0c,0x1e,0xf2,0x71,0xca,0x62,0x1c,0x9b,0x76,0xf1,0x76,0x60,0x3d, - 0xf5,0x38,0xc7,0xe3,0x37,0x0d,0xa4,0x3f,0x08,0x9b,0x9e,0xec,0x4b,0xd3,0x91,0xf7, - 0x03,0x5d,0x6a,0x54,0x1d,0x97,0x94,0x99,0x22,0xdf,0x6c,0x0e,0xaf,0x5a,0x62,0x43, - 0x59,0xfa,0xa5,0x57,0x38,0x4b,0x5a,0xe0,0xa4,0xa4,0xad,0x7f,0x75,0x49,0xd9,0x74, - 0x3e,0x86,0x8e,0x8e,0x22,0x6f,0x71,0x51,0x7d,0xa2,0xe8,0xbc,0xbb,0x3c,0xe7,0x4e, - 0xc6,0xd2,0x74,0xe9,0xc8,0xf5,0x71,0xfd,0xc8,0xe5,0x7c,0xdd,0x48,0xa5,0xea,0xbd, - 0xc3,0xa5,0xac,0x16,0x86,0xd4,0x8f,0x76,0x61,0xc9,0xf1,0x8e,0xbb,0xf5,0xa5,0xda, - 0x6a,0x3c,0x37,0x0d,0xf3,0x78,0xb7,0x91,0xd3,0x78,0x04,0x97,0x78,0xcc,0x9c,0x96, - 0x65,0x56,0xb8,0x27,0xd5,0x99,0x74,0xf2,0x29,0x1c,0x2a,0x74,0x39,0x81,0xea,0xa5, - 0xf9,0x17,0xcf,0xcc,0x63,0x91,0x04,0x69,0x28,0x7e,0xfb,0xf5,0xc7,0x8b,0x14,0x41, - 0x71,0x42,0xe2,0x52,0x03,0xb5,0xb6,0xfa,0x02,0xca,0x3d,0x16,0x49,0xab,0x60,0xba, - 0x43,0xa7,0x6a,0x97,0xc1,0x06,0xce,0x72,0xd7,0xac,0x2e,0x32,0x39,0xd7,0x6e,0xfe, - 0x5c,0xd0,0x01,0x4c,0xd3,0xe5,0x3a,0x98,0xf3,0x27,0x8f,0x56,0xb9,0xad,0x82,0xa9, - 0xf3,0xea,0xca,0xa0,0xd7,0x07,0x67,0x5c,0xd7,0xfa,0x40,0xa0,0x5d,0xde,0x16,0x3b, - 0xaf,0xce,0xe0,0x1b,0xf2,0x84,0x7c,0x58,0xf5,0x2b,0xfd,0x8c,0x8a,0x5d,0xd5,0x0e, - 0x9e,0x04,0xaa,0x95,0x53,0x41,0x05,0x97,0xe7,0x5a,0x43,0x85,0x1c,0xad,0x4d,0x30, - 0xdd,0x49,0x26,0xa2,0xa1,0x5a,0x2c,0xbe,0x69,0x34,0x02,0xb1,0xc5,0x61,0x1b,0x32, - 0xa0,0x1a,0x2b,0x00,0x0a,0xda,0x7b,0x93,0xe5,0x24,0x56,0x27,0x34,0x62,0xef,0x13, - 0xc9,0xdb,0xfe,0x4f,0xcf,0x68,0x5f,0xad,0x19,0x96,0x93,0xb6,0x86,0x3a,0x37,0x2f, - 0xfd,0x7c,0x06,0xd0,0x9f,0x9b,0xe7,0x51,0x19,0x43,0x2f,0x5d,0xa9,0xcd,0xaa,0x14, - 0x6c,0xd1,0x45,0xbf,0x50,0xf6,0x53,0x5f,0xb1,0x39,0x36,0x0d,0x09,0x5c,0x43,0x87, - 0x3f,0x39,0xa2,0x56,0x94,0x99,0xff,0xd2,0xa1,0xb3,0xa2,0xad,0x22,0xf5,0xcb,0x5d, - 0xa5,0x42,0x9b,0x5a,0x37,0xa0,0xe8,0xff,0xf5,0x8e,0x22,0xcc,0xf8,0xe8,0xd6,0xb4, - 0xd4,0x69,0xf6,0xe0,0xb1,0xde,0xa5,0xce,0x60,0xc8,0x28,0x80,0xc5,0x8c,0x3d,0x93, - 0x5e,0x30,0xe9,0x4e,0xb2,0x27,0x4d,0xd9,0xe5,0x20,0xe8,0xa4,0x58,0x4e,0x8e,0xef, - 0xe7,0xb1,0xfd,0xdd,0xf3,0x0b,0x5c,0x1a,0xb8,0x4c,0x0a,0xef,0x88,0xf8,0x17,0xec, - 0xbb,0x5a,0x81,0x6d,0x9f,0x3b,0x69,0x3e,0x39,0x39,0x45,0xd0,0x45,0x9d,0x8f,0x8c, - 0x25,0x14,0xe3,0xfb,0xf4,0xde,0x3b,0xa2,0xc0,0x7b,0xf0,0x12,0xff,0x8e,0xbe,0x7b, - 0xfe,0x01,0xef,0x07,0x51,0x1e,0x80,0x15,0x02,0x3c,0x7a,0x75,0x64,0x04,0x0f,0xfc, - 0x27,0xf7,0x8e,0x5e,0x1c,0x19,0xe3,0x28,0x8e,0xbd,0xa3,0xef,0x4e,0x9f,0xcb,0xac, - 0xd8,0xd1,0x89,0x7c,0x83,0x00,0xe2,0xca,0x1c,0x3e,0xf5,0x35,0x90,0x28,0xb0,0xe4, - 0x61,0x05,0xca,0xef,0x3d,0x9e,0x65,0x9d,0x8d,0x33,0xa9,0x4c,0x01,0x1a,0x29,0x0a, - 0x69,0x3b,0xf6,0x42,0xb6,0x0d,0x0f,0x8a,0xdc,0xe3,0x66,0xb0,0xb9,0x1f,0x4c,0xaf, - 0x38,0xe8,0xe9,0xae,0xe9,0x88,0x9e,0x6b,0xa6,0x18,0xc5,0xa4,0x3c,0x67,0x91,0xc3, - 0xf3,0x49,0x44,0xfe,0xe9,0xfa,0xa7,0xcf,0x9e,0xf9,0x86,0xbf,0x2d,0xf4,0xd6,0x3c, - 0xbe,0xb8,0xba,0x3a,0x36,0xdf,0x9c,0xc8,0xdb,0x37,0x61,0xb4,0x34,0x98,0xf1,0x40, - 0xfc,0xe4,0xa8,0x75,0x3f,0xc2,0xad,0x5f,0xdd,0x4c,0x8f,0xd8,0x8d,0xf0,0x8e,0x4e, - 0xd0,0x1a,0x55,0xad,0x21,0x6e,0x4e,0xa2,0xb7,0xba,0xb8,0xbd,0x41,0xf0,0x90,0x54, - 0xcf,0xa1,0x9b,0xa8,0x07,0x35,0xe1,0x8f,0xaf,0xc1,0x5b,0x8e,0x8e,0x20,0x4c,0x39, - 0x95,0x70,0x1e,0xfd,0x7b,0x14,0xfb,0xc9,0xec,0x88,0xa4,0xca,0x3b,0x4a,0x52,0x9a, - 0x00,0xde,0x33,0xa4,0x70,0xc8,0xf7,0x4e,0x80,0xd7,0x5b,0x72,0xf2,0x48,0x1b,0x57, - 0x30,0x92,0x23,0x83,0xcd,0x64,0xec,0x8f,0xe8,0x4d,0x25,0xb2,0x34,0x20,0x3a,0xbd, - 0x7d,0x23,0x43,0xbd,0xaa,0x73,0x70,0xc4,0xe7,0x3d,0x30,0x2d,0x6e,0xd6,0xc0,0xcb, - 0x86,0xdd,0xfe,0xe5,0x64,0xe7,0x05,0xad,0x27,0x63,0xa3,0x93,0x2a,0x9b,0x1c,0xed, - 0x69,0x2d,0x57,0x15,0x82,0xd1,0x12,0x70,0x32,0xf8,0xd2,0xa5,0xa8,0x3a,0xea,0x5e, - 0xe1,0x5d,0x37,0xa8,0x3d,0x3b,0xac,0xda,0xae,0xd1,0x35,0x8f,0x03,0xb6,0xf3,0xde, - 0x5d,0xd7,0x9c,0x92,0x36,0x16,0x39,0x5d,0xe2,0x0f,0xab,0x66,0xbe,0x1e,0x99,0x6c, - 0xc4,0xe8,0x3a,0xe1,0x92,0x3f,0xba,0x0a,0x70,0x55,0x4e,0xe8,0xaa,0xa4,0xca,0xbc, - 0x8c,0x2f,0x33,0xba,0x2c,0x57,0xdc,0xba,0x22,0x6b,0x2e,0x65,0xd4,0xa1,0x98,0x39, - 0x2f,0xdf,0x73,0x95,0x4b,0x97,0xb8,0xcb,0x56,0x0f,0xb8,0x2e,0x9b,0xd9,0xd6,0xda, - 0xff,0x21,0x0f,0xf5,0xa1,0x21,0x53,0x79,0x55,0x7b,0xca,0x99,0xd8,0x7a,0x9a,0x36, - 0x1b,0x6c,0x7b,0x9d,0x01,0x00,0x28,0xe2,0x52,0x4e,0x9f,0x18,0x75,0x5f,0x95,0x0f, - 0x14,0x3c,0xe2,0x13,0xf5,0x0a,0xed,0x12,0x51,0x38,0x07,0xab,0x1b,0xcc,0xbc,0x9d, - 0xa3,0x69,0xdf,0x56,0x7c,0x87,0x8d,0xe2,0xfb,0xd6,0xa7,0xb9,0xa0,0xdd,0x64,0x2f, - 0x57,0x6a,0x39,0xeb,0xdb,0xf5,0xde,0x4f,0xbd,0xa5,0x6d,0x0e,0xb2,0x66,0xde,0xb2, - 0xda,0xdf,0x83,0x78,0x5d,0xa0,0xb1,0x53,0x83,0x20,0x1b,0xd5,0xae,0xc8,0x13,0x85, - 0xb0,0x3b,0x07,0x0f,0x88,0x36,0x74,0xfe,0xac,0xf6,0xd7,0x87,0x55,0x2d,0x79,0x55, - 0xc2,0xb0,0x7f,0x53,0x29,0xe0,0x83,0x7b,0xb2,0x26,0xc4,0x7e,0xd5,0x57,0x75,0xfe, - 0xe5,0x5d,0xbd,0x41,0x76,0x07,0x6f,0xd5,0xc6,0x2f,0x1c,0xed,0x61,0xa9,0xce,0x42, - 0x54,0xdb,0x25,0xec,0x8f,0xca,0xe3,0xd5,0xf2,0x8b,0x6b,0xaa,0x86,0xa1,0x30,0x26, - 0x69,0xf5,0x29,0x36,0x75,0xda,0xa4,0xbb,0x60,0x93,0x39,0x70,0x4e,0x8d,0xc2,0x72, - 0xd4,0x09,0xad,0xaa,0x78,0xb2,0xd9,0xee,0x5d,0x49,0xaa,0x5c,0x6d,0x36,0xc5,0x5d, - 0xbb,0x12,0xb8,0xd9,0xd4,0xad,0x36,0xe7,0xcb,0xfe,0x9b,0x01,0xf4,0xb6,0x7e,0xbc, - 0x63,0x65,0xbf,0xe8,0x2b,0xb2,0xb5,0xc9,0xa3,0x6d,0x95,0xfe,0xdc,0x94,0x03,0x69, - 0x25,0xa8,0x49,0x30,0x85,0x94,0x89,0xea,0x9b,0x00,0x9e,0x70,0x02,0xf2,0x5b,0x0a, - 0x11,0x42,0xad,0x4d,0xcf,0xdb,0xb7,0x5d,0xcb,0xbd,0xb9,0x45,0x90,0x53,0xa2,0x9b, - 0x54,0x5f,0x7a,0xd4,0x90,0xed,0x6c,0x85,0x67,0x5c,0x05,0x2e,0x6b,0x5a,0x68,0x05, - 0xdf,0xc1,0x41,0xe0,0x36,0xae,0xba,0xaa,0x42,0x30,0x0a,0x82,0xea,0xad,0x7d,0x44, - 0x42,0x90,0x30,0x0c,0x57,0x94,0xe7,0xf5,0x15,0xc2,0x81,0x1b,0x7a,0x05,0x96,0xd1, - 0x95,0x47,0x3a,0xe5,0x71,0x13,0xfa,0x02,0x02,0x19,0xc4,0xc2,0x08,0xfc,0x3c,0x7f, - 0x90,0x1f,0x57,0xa8,0x8e,0xbe,0x43,0xa6,0xe8,0x9b,0x54,0x46,0xf7,0x5c,0x1e,0x71, - 0xf7,0x2c,0xae,0x4e,0xe1,0x6f,0x63,0xd0,0x8b,0x54,0x4e,0x4f,0xa1,0x81,0x3c,0xd6, - 0xc3,0xdb,0x69,0x86,0xee,0xd6,0x31,0xf3,0x4b,0x67,0xc7,0xa7,0xf3,0x3f,0xa5,0x03, - 0x4d,0xf1,0x58,0x50,0x99,0xb5,0xdb,0x85,0x8a,0xaa,0xa2,0xa8,0xa6,0xac,0x1d,0x65, - 0xd2,0xf4,0x53,0x65,0xc8,0xc9,0xff,0x4a,0x1f,0xf2,0x4f,0x44,0xba,0xdd,0x1b,0xbf, - 0xf7,0x9f,0x7e,0xef,0x75,0xef,0xf6,0xf8,0x4f,0xc7,0xfa,0x5e,0x1b,0xfd,0x4f,0xf6, - 0x2a,0xbb,0x37,0x7f,0x9e,0x9c,0x3f,0xbb,0xdd,0xfc,0xa3,0xae,0x1b,0x90,0x94,0x6b, - 0xfc,0x89,0xcc,0x43,0x80,0x18,0xe2,0xed,0xd6,0x4f,0x55,0x50,0xf4,0xd9,0x21,0x3d, - 0x46,0xf1,0xb5,0x75,0xde,0x5c,0xbb,0x1c,0x3c,0x57,0x38,0x45,0x59,0x0d,0xad,0x2a, - 0x64,0x6b,0xea,0xe3,0xd4,0x78,0x5c,0x5c,0xbd,0x53,0x23,0x37,0xf5,0x0b,0xa8,0x10, - 0xa6,0x2c,0xf9,0x2b,0xfb,0x2a,0xe8,0xd4,0x53,0x4c,0xbf,0x8e,0x15,0x55,0x1d,0x9d, - 0x5e,0x95,0xb4,0xb5,0xd5,0xde,0x66,0xcd,0x92,0x51,0xf1,0x85,0x2a,0x63,0x33,0x45, - 0xc9,0x68,0xd8,0x70,0x18,0xc5,0xdc,0x6d,0x1e,0x43,0x8b,0x16,0x58,0x2a,0x36,0x1a, - 0x7c,0x2b,0xf6,0xc6,0x4b,0x2a,0xf6,0xee,0x74,0xd4,0xc5,0xe3,0xd7,0xd5,0x55,0xbf, - 0x7d,0x94,0x55,0xf0,0x87,0x30,0xc8,0xc1,0xa3,0x8f,0x76,0x0d,0xeb,0x0f,0x81,0x0d, - 0xf9,0xfb,0x63,0xed,0x0a,0xcb,0x6c,0x72,0x6d,0xb5,0x5b,0x92,0xe5,0xb5,0xfa,0xbe, - 0xc4,0xc4,0xca,0x26,0x7a,0x04,0x0c,0x9b,0x34,0x3c,0x68,0x14,0x98,0x3a,0xe9,0x51, - 0x9d,0xb9,0x53,0xc5,0x95,0x8a,0xeb,0xdf,0xe9,0x1f,0x22,0x1a,0xe7,0x42,0xc8,0x32, - 0x66,0x45,0x6c,0x3e,0x6f,0xaf,0x1f,0xb6,0xa7,0x23,0x6d,0xf4,0x9c,0xce,0xf5,0xc9, - 0xf0,0xd6,0x2f,0x55,0x71,0xe4,0xdf,0x65,0x67,0x2e,0xbc,0x0d,0x65,0xe5,0xeb,0x17, - 0x59,0x07,0x2b,0xe0,0xd2,0x09,0x47,0xfa,0x03,0x30,0x31,0x7d,0xba,0x9b,0x8b,0xd2, - 0xff,0xa7,0x78,0xa0,0xcb,0xa0,0xcc,0x63,0x75,0xc9,0x27,0xe4,0xd5,0xb5,0x1f,0xd3, - 0x55,0x2b,0xce,0xd1,0xc5,0x64,0x42,0x5f,0x17,0xaa,0x25,0xc5,0x27,0x16,0xd3,0xcd, - 0x15,0x94,0xad,0xac,0x1b,0xac,0x01,0x4c,0x64,0x8c,0xad,0x75,0x92,0x3a,0xc9,0xb4, - 0x54,0x51,0xeb,0x84,0xcc,0xdf,0xbf,0xa9,0x20,0xd4,0x6c,0x71,0xf9,0x2e,0x87,0x57, - 0x1f,0x00,0xda,0x2d,0x3c,0xe2,0x42,0x99,0x85,0xfa,0x94,0xc9,0x61,0xf3,0x55,0x93, - 0x1a,0x18,0xaf,0x87,0x66,0x0c,0xa4,0x36,0x82,0xd2,0xa8,0x4e,0x3d,0xc8,0x22,0x39, - 0xfa,0x9a,0x14,0x2d,0x82,0x8a,0x55,0x29,0xc3,0x50,0x29,0xa3,0x07,0x74,0x9d,0xf3, - 0x17,0x12,0xe8,0xa3,0x2d,0xe9,0x58,0x7e,0xf5,0x85,0xbe,0xbc,0x02,0x60,0x33,0x79, - 0x6a,0x9e,0xeb,0xcf,0xc3,0x54,0x7e,0xa4,0x63,0x9f,0x1c,0x2e,0xe9,0x6b,0x47,0x9c, - 0xed,0xe9,0x74,0x94,0xf4,0x2c,0xea,0xdc,0xda,0x13,0xd2,0x49,0xf5,0xad,0xf4,0xc6, - 0xae,0x6c,0xca,0x29,0xd7,0xf5,0x8e,0xd5,0xab,0xfc,0x4d,0x97,0xba,0xb5,0xfe,0xf8, - 0x99,0x16,0xd9,0xa8,0xf2,0x61,0x4e,0x47,0x60,0xfa,0x54,0x9a,0xdb,0x35,0xdf,0xd5, - 0x05,0xf7,0xf4,0xe5,0x9d,0x45,0x92,0x50,0xe9,0x80,0x0a,0x26,0x59,0x7c,0x60,0x80, - 0x1e,0x56,0xfe,0xc3,0x39,0x29,0x91,0x3d,0x65,0xd6,0x6a,0x1d,0x2a,0x49,0x1b,0x6c, - 0x49,0xf2,0x06,0x2c,0x6f,0xfd,0x4a,0xc8,0x9b,0x13,0x49,0xea,0x73,0x57,0x87,0xda, - 0xa1,0xa4,0xba,0xe6,0xa8,0xd3,0xe1,0x46,0xfa,0x9a,0xd4,0x42,0xa9,0xd1,0x35,0xb7, - 0xf0,0x21,0x7d,0x3a,0xc1,0x36,0xe4,0xdb,0xe6,0xa4,0x3e,0x57,0x49,0xf9,0x4b,0xab, - 0x51,0x25,0x9c,0xfa,0x20,0x4f,0x30,0x17,0x89,0xae,0x52,0xf4,0x76,0x86,0x51,0x8b, - 0xf6,0xee,0x93,0x8a,0x82,0x8c,0x45,0x75,0x73,0x4e,0x29,0x30,0x19,0x24,0xff,0x1d, - 0x0d,0x82,0xf6,0xa7,0x0b,0x65,0xf6,0xe8,0x16,0x88,0xbb,0xb9,0xb5,0x07,0xe4,0x20, - 0xc8,0x53,0x9a,0x08,0xda,0x0c,0xb5,0xc9,0x6e,0x3c,0x37,0x48,0x65,0xc8,0x0f,0x3d, - 0x34,0xf5,0xc2,0xc6,0xca,0x27,0xa3,0x0a,0x35,0x41,0xdf,0x6f,0x72,0x40,0xf4,0xbd, - 0x43,0x1a,0xc1,0x74,0x26,0x87,0xad,0x6a,0x24,0xad,0x9d,0x9e,0xe8,0xc0,0xc7,0x40, - 0xa4,0x6b,0x52,0x2b,0xd0,0xad,0x6c,0x3c,0x78,0xca,0xfd,0x25,0xb1,0x01,0x40,0xfd, - 0x04,0x00,0x14,0x91,0xb6,0xea,0xd5,0xd7,0xde,0xf4,0x95,0xff,0xab,0xe5,0x7c,0xa2, - 0xa8,0xb2,0x7d,0x7e,0x9f,0xeb,0x2b,0x4b,0x4b,0x1e,0x5c,0x2e,0xab,0x13,0xcc,0x2b, - 0xf5,0xe9,0x95,0xfa,0x48,0x44,0xff,0x2f,0x90,0x2f,0xa6,0xe9,0xea,0xb1,0x1a,0x3d, - 0x04,0x87,0x93,0x0a,0x28,0xa8,0x72,0xba,0x12,0x9d,0x2b,0xf5,0x99,0x0a,0x1d,0xfc, - 0x80,0xeb,0x74,0x14,0xb1,0xea,0x53,0x8e,0xaa,0xc5,0x54,0x63,0xef,0xd3,0xdf,0xbc, - 0xac,0x11,0x02,0xad,0x87,0x60,0x4a,0x1f,0xfe,0x6c,0x11,0x50,0xa6,0x1f,0xe4,0x42, - 0xf1,0x67,0x4d,0xac,0xca,0xc4,0x10,0xb8,0x1b,0x53,0xed,0xc4,0x90,0xa6,0x35,0x6d, - 0x73,0x26,0x1e,0xd4,0x15,0x7f,0x79,0xd4,0xbc,0x7d,0xfc,0x91,0x8b,0x92,0xbe,0xdb, - 0xf7,0x08,0x89,0x52,0x1f,0xb3,0xfa,0xd8,0xe5,0xa0,0x2e,0x07,0xd7,0x47,0xb1,0x32, - 0x75,0xce,0x4a,0x79,0x01,0xea,0x43,0x3f,0x97,0x0e,0x9f,0xeb,0xbd,0xaa,0x5c,0x7f, - 0x52,0x20,0x7c,0xba,0x6a,0xdf,0x94,0x2f,0x7f,0xf9,0x49,0x25,0x73,0x3e,0xa3,0x1b, - 0xe2,0x30,0x9b,0x03,0x32,0xc5,0x72,0x2a,0x25,0x53,0x15,0x0d,0xfe,0x3f,0x22,0x33, - 0x2b,0xd0,0x96,0x59,0x00,0x00, + 0x1f,0x8b,0x08,0x00,0x00,0x00,0x00,0x00,0x02,0xff,0x8d,0x7c,0x7d,0x77,0xdb,0x36, + 0xb2,0xf7,0xff,0xfe,0x14,0xb4,0xb3,0x15,0xc9,0x9a,0xa2,0x25,0xdb,0x49,0x53,0x32, + 0x8c,0x6f,0x62,0x27,0x37,0x7d,0x36,0x6d,0xb3,0xb5,0xbb,0xbd,0xe7,0xb8,0xbe,0x3d, + 0x14,0x09,0x49,0x8c,0x28,0x92,0x26,0x29,0xc9,0x5e,0x49,0xcf,0x67,0xbf,0xbf,0x19, + 0x80,0x6f,0xb2,0xdc,0x76,0xb7,0x91,0x49,0x10,0x18,0x00,0x83,0x79,0xc7,0x00,0xc6, + 0x78,0x91,0x04,0x65,0x94,0x26,0x86,0xb9,0x3e,0x88,0xc6,0xc6,0x2a,0x4a,0xc2,0x74, + 0x65,0x7f,0xf9,0x68,0xe6,0xa2,0x5c,0xe4,0x89,0x7b,0xb0,0xf4,0x73,0xed,0x37,0x4f, + 0x96,0x5b,0x57,0x5e,0x98,0x06,0x8b,0xb9,0x48,0x4a,0xeb,0x93,0x77,0x65,0x57,0x2f, + 0x1f,0x62,0xc1,0x65,0x9f,0xbd,0x38,0x0d,0x7c,0x02,0x67,0x85,0x7e,0x3e,0xf3,0xc6, + 0x7e,0x5c,0x08,0x2b,0x48,0xbc,0x64,0x11,0xc7,0xd6,0x74,0xee,0xad,0xb7,0xd6,0xcc, + 0x3a,0x28,0x4a,0xb4,0x0d,0x72,0xe1,0x97,0x42,0xb5,0x34,0xf4,0xa2,0x7c,0x8c,0x85, + 0x6e,0x5a,0x81,0x7b,0x70,0x72,0xa2,0xdd,0x4c,0x51,0xac,0x8d,0xc4,0x38,0xcd,0x85, + 0x36,0x8e,0xf2,0xa2,0xd4,0x32,0x3f,0x4a,0x4a,0x47,0x2b,0xa7,0x51,0xa1,0x15,0x41, + 0x1e,0x65,0xa5,0x86,0xa7,0xcc,0xcf,0x0b,0x91,0xf7,0x47,0xe8,0x77,0x16,0x25,0x13, + 0x2d,0x4a,0xb4,0x37,0x53,0xe1,0x87,0x6f,0xed,0x83,0x32,0x7f,0x5c,0xf3,0x28,0x68, + 0x4c,0xf1,0x75,0x99,0xe6,0xfe,0x44,0xd8,0x13,0x51,0xfe,0x50,0x8a,0xb9,0xa1,0x67, + 0xe3,0x7e,0x49,0xbd,0xe8,0xa6,0xe7,0x79,0x3a,0x55,0xd4,0x5d,0x8c,0xf4,0xff,0x5d, + 0xff,0xfc,0x93,0xcd,0x50,0x8d,0x67,0xdb,0x25,0xfe,0x52,0x37,0x4d,0x17,0xf3,0x09, + 0x92,0x5e,0x2f,0x48,0xec,0xe9,0x66,0xb3,0xde,0x6e,0x31,0xf3,0x60,0x6a,0x08,0x73, + 0xbd,0x3d,0xf8,0x64,0x17,0xa2,0x7c,0x57,0x96,0x79,0x34,0x5a,0x94,0xc2,0x00,0xf8, + 0xd2,0x57,0xdd,0x31,0x66,0x2e,0x64,0x87,0x8e,0x1e,0x47,0x93,0x69,0xa9,0x9b,0x3c, + 0xe9,0xcf,0xf4,0x8c,0x19,0xc5,0xa2,0x2c,0x85,0xe6,0x61,0xaa,0x42,0x0b,0x08,0x01, + 0x19,0x06,0x50,0xe8,0x9a,0x93,0xa7,0x69,0xa9,0x2d,0xfd,0x78,0x21,0x0a,0x57,0x4b, + 0x17,0x65,0xee,0x27,0xb3,0x42,0xf3,0x93,0x47,0xae,0xa1,0x17,0x5a,0xba,0x4a,0xec, + 0x83,0xc0,0xd3,0xa7,0xe5,0x3c,0xbe,0x6d,0xfa,0xbc,0x5b,0xf7,0xfb,0x85,0x9f,0x14, + 0x4e,0xf1,0x58,0x60,0x0e,0xfd,0x45,0x64,0xf5,0xfd,0x2c,0x8b,0x45,0x5f,0x16,0x58, + 0x47,0xd7,0x62,0x92,0x0a,0xed,0xd7,0x1f,0x8e,0xac,0x5f,0xd2,0x51,0x5a,0xa6,0x16, + 0x55,0xef,0x03,0xb3,0xd1,0xd8,0xed,0xf7,0xe7,0x69,0x92,0x3a,0x8b,0x88,0xff,0x16, + 0x99,0x1f,0x08,0xeb,0xe8,0xd2,0x2f,0x02,0x3f,0x8c,0x7c,0xed,0x47,0x94,0x1d,0x01, + 0xc0,0x47,0xf5,0xf4,0xa3,0x48,0xe2,0xd4,0xba,0x4c,0x93,0x22,0x8d,0xfd,0xc2,0x3a, + 0x92,0xf0,0xd4,0xc7,0x1a,0xc0,0x56,0x3f,0x38,0xe6,0x51,0xf2,0x9c,0x5a,0x43,0xf5, + 0x18,0x21,0x77,0xeb,0x20,0x8d,0xd3,0xbc,0x5f,0x04,0x54,0xe6,0x70,0x19,0xc6,0x41, + 0x24,0x33,0x77,0x5e,0x7c,0x3c,0xff,0xf0,0xf1,0xc3,0x2b,0xbc,0x67,0x7e,0x22,0x62, + 0xbc,0x7f,0xfc,0x78,0xf9,0xf1,0x1d,0xde,0xa3,0x64,0xe6,0xbc,0x18,0xbe,0x1b,0xbe, + 0x1e,0x9e,0xd3,0xa8,0x81,0xf9,0xd0,0x79,0xf1,0xea,0xf4,0xe5,0xe5,0xcb,0x21,0xde, + 0xc7,0x4c,0x40,0x2f,0xbe,0xbb,0xfa,0xee,0xd5,0xab,0xd7,0x78,0xcf,0x17,0xb1,0x70, + 0x5e,0x5c,0x5d,0x5d,0xbd,0xbc,0x7c,0xa5,0x5e,0xfb,0x45,0x3a,0x46,0x95,0x0f,0xaf, + 0x3f,0x0c,0xaf,0x08,0xc4,0x64,0x9a,0x16,0xf4,0x7e,0x76,0x75,0x79,0xf9,0x01,0xef, + 0x31,0x01,0xfc,0xf8,0xf1,0xe5,0xe5,0x29,0xbd,0x8d,0x7c,0xbc,0xbd,0x7f,0x7d,0x76, + 0x39,0xbc,0xc4,0x5b,0x8a,0xbe,0x4f,0xdf,0x7f,0xf7,0xee,0xfc,0x3d,0x5e,0x56,0x7e, + 0x9e,0x38,0x2f,0x5e,0xbf,0x7c,0x35,0x18,0x7c,0xa8,0x06,0x7e,0x5a,0x8d,0xb4,0x9e, + 0x7c,0x7b,0xde,0x44,0x0f,0x3b,0xd3,0xa6,0xa2,0x2d,0x23,0x08,0xeb,0x07,0xb2,0x0b, + 0xa6,0x79,0x3a,0x17,0xfd,0xa9,0xf3,0xfa,0x65,0xf6,0xb0,0xfd,0xaf,0xb9,0x00,0xfe, + 0x8d,0xb9,0xff,0xd0,0x5f,0x45,0x61,0x39,0x75,0xce,0x5e,0x7d,0x9f,0x3d,0x98,0xeb, + 0x7d,0x0d,0x86,0xa7,0xd4,0xa2,0x6e,0x12,0x25,0xaa,0xc9,0x70,0x70,0x7a,0xfe,0x5c, + 0x9b,0xf3,0xef,0xa9,0x49,0xbd,0x4e,0x49,0x5a,0x1a,0x36,0xbe,0xcf,0xd3,0x45,0x02, + 0xbc,0x9a,0xda,0x28,0x0d,0x1f,0x1d,0x47,0xb2,0x28,0xc6,0x8d,0x42,0x60,0xf7,0xe8, + 0xc8,0x0d,0xa3,0x22,0x8b,0xfd,0x47,0x87,0x59,0xd2,0x1d,0xa5,0x0f,0xfd,0x22,0xfa, + 0x0f,0x58,0xd3,0x19,0xa5,0x79,0x48,0xac,0x9a,0x3e,0xb8,0x53,0x41,0x2b,0xea,0x40, + 0xb4,0x18,0x9d,0x4e,0x4d,0xb7,0xae,0x54,0x96,0xe9,0xdc,0x19,0x66,0x0f,0x1a,0xe8, + 0x28,0x0a,0x35,0x59,0x95,0xd6,0xc8,0x52,0x4b,0x86,0xba,0x7e,0x30,0x9b,0xe4,0x18, + 0x4f,0xa8,0x20,0x31,0x9a,0x2d,0x45,0x1f,0x26,0x8f,0x5d,0x4d,0x39,0x4b,0xb1,0xf6, + 0x22,0x77,0x82,0x94,0x38,0xdb,0x5c,0x47,0x49,0xb6,0x28,0x79,0x4e,0xb7,0xe5,0x63, + 0x26,0x3c,0x20,0x3c,0x98,0x61,0x64,0x77,0x66,0xab,0x30,0x07,0x81,0xa7,0x3b,0x25, + 0xc9,0x44,0x74,0x4a,0xc6,0x51,0x8c,0x02,0xab,0x10,0xb1,0x08,0x4a,0xab,0x14,0x0f, + 0xa5,0x8f,0x41,0xac,0xc7,0xc0,0x07,0xcd,0x5b,0x38,0xc3,0x57,0xd9,0xc3,0x61,0x34, + 0xcf,0xd2,0xbc,0xf4,0x93,0x52,0x22,0x94,0xf0,0x38,0x5a,0x14,0x8f,0xeb,0x14,0xbc, + 0x10,0x95,0x8f,0x8e,0xfd,0xf2,0xa5,0x1b,0x2c,0xf2,0x22,0xcd,0x9d,0x2c,0x4f,0x27, + 0xb9,0x28,0x8a,0xad,0xee,0x1e,0x00,0xb5,0xc6,0x8c,0x44,0xda,0x74,0x6e,0x06,0xc7, + 0x9e,0xfe,0x64,0xfd,0xf4,0x63,0x63,0xd6,0x1f,0x9a,0xc7,0x3a,0x16,0x11,0x72,0x20, + 0xd4,0x5a,0xe4,0xa0,0x1f,0xcf,0xd0,0xd3,0xb3,0x24,0x81,0xa6,0xd3,0xf9,0xed,0xec, + 0x6e,0x33,0xe0,0xe6,0x5b,0xea,0xaf,0x28,0x6d,0x9a,0xc1,0xa5,0x5c,0x4c,0x0f,0xa2, + 0xd8,0xb8,0xb2,0x49,0x9a,0x6e,0x36,0x9f,0x4c,0x1b,0x12,0x43,0x24,0xe1,0xe5,0x34, + 0x8a,0x43,0xa3,0x28,0x21,0xb2,0x58,0x35,0x7c,0xf9,0xe8,0xfd,0x06,0x75,0xe1,0xad, + 0x97,0x0e,0x0b,0xf9,0xa2,0x84,0x54,0x77,0x74,0x10,0x44,0x02,0x8c,0x60,0xdd,0x21, + 0xf0,0xa2,0x1c,0x73,0x64,0x5d,0xb0,0xb5,0x0e,0x92,0x31,0x1a,0x8c,0x05,0x44,0xa5, + 0x75,0xe3,0xdd,0xde,0x59,0x98,0xb7,0x54,0x0f,0xff,0xa2,0xb7,0x78,0xe4,0x0d,0xac, + 0x7c,0x91,0x78,0x43,0xeb,0x0b,0xbd,0x5f,0xcb,0x6f,0xc5,0x62,0x54,0xd0,0x6b,0x71, + 0xaf,0xde,0xa9,0xda,0x6a,0x5e,0x78,0xc3,0x93,0x81,0x55,0x64,0xb2,0xf0,0x20,0x2f, + 0x4b,0xf9,0x04,0x56,0x8f,0x0b,0xd4,0x48,0xc7,0x63,0xfc,0xfa,0x71,0x24,0x12,0x02, + 0x4b,0x45,0xd3,0x34,0x0e,0xe9,0xef,0x64,0xe1,0xe7,0xfc,0x10,0x0b,0x7f,0x29,0x7e, + 0x9e,0xe1,0xe9,0x97,0x4f,0xa4,0x9f,0x46,0x81,0x82,0x06,0xa6,0xfc,0xd1,0x2f,0xa7, + 0x36,0x16,0x3d,0x4c,0xe7,0x86,0x69,0x97,0xe9,0x35,0x04,0x7a,0x32,0x31,0xce,0x5e, + 0x99,0x76,0x11,0x47,0x81,0x30,0x4e,0x4d,0xab,0x4c,0x17,0xa0,0x9e,0x10,0xed,0x33, + 0xfa,0x49,0x96,0xfc,0x73,0x63,0x65,0x93,0x1b,0xeb,0xc0,0x0f,0xd3,0xac,0xe4,0x8f, + 0x40,0x63,0xcc,0x0f,0x40,0x50,0x2c,0x64,0x17,0xd9,0xb8,0x44,0x41,0x40,0x3f,0x63, + 0xe8,0x14,0x9a,0x60,0x98,0xfb,0xab,0xc4,0xd3,0x75,0x28,0xc9,0xdc,0x9a,0xc6,0xd6, + 0x52,0xe4,0xd6,0x72,0x64,0xe1,0xab,0x05,0x84,0x95,0x13,0x80,0xb5,0xca,0x95,0x55, + 0xde,0x5b,0x65,0x69,0x5d,0xff,0xf3,0x87,0x2f,0x34,0x64,0xa5,0x31,0x59,0x59,0xe8, + 0x85,0x54,0x17,0x4c,0x0e,0xa0,0xd3,0x29,0x69,0x45,0x01,0xd4,0xbb,0xda,0x18,0x0a, + 0x77,0x91,0xd3,0xa7,0x30,0x24,0xe5,0x02,0xb5,0xaa,0x2d,0x21,0xc2,0x55,0xf9,0x4f, + 0xfe,0x52,0xf3,0x4b,0x82,0xf5,0xd3,0xbb,0x7f,0xff,0xf1,0xee,0xc6,0x06,0xcc,0x28, + 0x0f,0xa1,0x7e,0x45,0x1c,0x3a,0x5a,0x96,0x0b,0x5e,0x35,0x8d,0x39,0xd4,0xd2,0x8a, + 0xb9,0x1f,0xc7,0x82,0x15,0xf3,0x44,0xe9,0x68,0xfb,0x00,0x2d,0xbd,0xdb,0x5b,0xfd, + 0x44,0xb7,0x74,0xa9,0x02,0xa0,0xef,0x5e,0xde,0x59,0x28,0xc9,0x7c,0x28,0xb5,0x3c, + 0x29,0xf0,0xe5,0x4b,0xfd,0xf8,0x8a,0x3f,0xad,0xa2,0x71,0x84,0xe2,0xdf,0xa2,0xfe, + 0x47,0xfc,0x3d,0xe7,0xb2,0x59,0x92,0x8e,0xa8,0xee,0x3f,0xe5,0xdf,0x21,0x17,0x2e, + 0x32,0x08,0x4c,0x40,0xd4,0x7f,0x55,0x0f,0x67,0x5c,0x4c,0x24,0xb7,0xa0,0xca,0xd7, + 0xea,0xe1,0xf4,0xee,0xce,0x92,0x73,0xf0,0x4e,0x5d,0xb6,0x03,0xbe,0x7c,0xb4,0x97, + 0x5e,0x22,0x56,0xda,0xaf,0xbf,0x7c,0xbe,0x16,0x7e,0x1e,0x4c,0xbf,0xf8,0xb9,0x3f, + 0x2f,0x8c,0xcf,0x50,0xd4,0xf4,0x6a,0x92,0x82,0x37,0x74,0xe8,0xf5,0xb6,0x1a,0x27, + 0x02,0x87,0x8d,0xf3,0xde,0xa3,0xf6,0x2e,0xb4,0x7a,0x00,0x9d,0x56,0x7c,0x8e,0x30, + 0x55,0xe0,0x90,0x6d,0x81,0x16,0xa1,0x83,0x25,0x60,0x41,0x91,0x49,0xf0,0x2e,0xcf, + 0xfd,0x47,0x3b,0x2a,0xf8,0x2f,0x4a,0x6c,0xac,0x9f,0xd9,0xeb,0x19,0x87,0x04,0x67, + 0xb3,0x41,0xc1,0x68,0x01,0x36,0x82,0xe1,0x41,0x05,0xa6,0xc9,0xab,0x2f,0xab,0x81, + 0xad,0x2a,0x93,0x0c,0xcb,0xb7,0x82,0x59,0x26,0x0d,0x31,0xed,0x0a,0x33,0xb6,0xb9, + 0x64,0xdb,0xd4,0x80,0x81,0x12,0x1b,0x63,0xcb,0xb7,0x46,0xe6,0x9a,0xe6,0x39,0x36, + 0xe8,0x51,0x4d,0x01,0x9c,0x0f,0x2b,0xe4,0x26,0x9a,0x0b,0xd8,0x0a,0x46,0xcb,0xd0, + 0x2b,0x21,0x04,0x56,0xda,0xc3,0xd6,0xdc,0xb6,0x40,0x89,0xa5,0x01,0xab,0xcd,0x5c, + 0xc3,0xaa,0x83,0x04,0x27,0x00,0x1f,0x96,0x64,0x99,0x11,0xd6,0x2e,0x17,0x05,0xc4, + 0xb1,0x7a,0xb7,0xd6,0xa1,0x28,0xc1,0x60,0x4e,0xb8,0x35,0xdb,0x63,0x09,0xf3,0x34, + 0x23,0xb4,0x11,0xd2,0x22,0xef,0xc6,0x86,0xc5,0x28,0x1e,0x7e,0x1e,0xa3,0xc8,0x05, + 0x5a,0xa2,0xb7,0x10,0x54,0x37,0x36,0x74,0x03,0xf1,0x4d,0x64,0x0d,0xd1,0x94,0x68, + 0x4d,0xd2,0xd4,0x2a,0x27,0xe9,0x92,0x3b,0x5a,0xe1,0x43,0x3a,0xa5,0x79,0x34,0x81, + 0xd4,0xfb,0xef,0x0f,0x37,0x85,0x86,0x65,0xd1,0x7c,0x8d,0xf4,0x4b,0x9e,0x82,0xde, + 0x72,0xbc,0x94,0xfe,0x48,0x2b,0x56,0x11,0xb5,0x0b,0xfc,0x44,0xf3,0xa1,0x33,0x40, + 0x7a,0xf5,0x38,0xca,0xb1,0xb1,0xb0,0x52,0x6b,0x06,0x83,0x96,0x86,0x12,0xf0,0xba, + 0xbf,0xa3,0x4a,0x97,0x35,0x14,0xc3,0xb4,0x84,0xb7,0x0e,0x9c,0xc0,0x5a,0x38,0x8a, + 0x2c,0xd0,0xe8,0xb3,0x3d,0x05,0x91,0x9b,0xfc,0x6b,0x91,0x7a,0x00,0xb2,0xad,0x99, + 0x33,0xdb,0x5a,0x11,0xb1,0x59,0x22,0x25,0x72,0x42,0x12,0x39,0x35,0xa3,0xdb,0xe4, + 0xce,0x4b,0xf1,0x83,0x55,0xb7,0x8b,0x68,0x92,0xf8,0xb1,0x17,0xa8,0x07,0x55,0x11, + 0x48,0x88,0x45,0x32,0x29,0xa7,0x6e,0xd2,0xef,0xbb,0x26,0xb0,0x20,0xec,0xb2,0x7f, + 0x83,0x36,0x76,0xf9,0xf6,0x95,0x38,0x6f,0xf0,0x91,0x00,0x1f,0xee,0xc1,0x8d,0x9d, + 0x2d,0x8a,0xa9,0x42,0x18,0xe4,0xa2,0x89,0x7f,0x75,0xd1,0x81,0xb0,0x21,0xe7,0xc6, + 0x36,0x2f,0xf9,0x6f,0xd6,0xc2,0x8a,0x50,0xa6,0x68,0x43,0xb8,0x07,0xdb,0x03,0x25, + 0x55,0xbd,0x7a,0xa1,0x81,0x05,0x85,0x83,0xb9,0xa7,0x24,0x57,0xda,0xeb,0xa5,0xf6, + 0x5c,0x94,0xd3,0x14,0x32,0x5d,0x07,0x82,0x75,0x12,0x6b,0xbf,0x12,0xee,0x61,0xd1, + 0x09,0x4c,0x37,0xad,0xac,0x76,0x21,0x39,0x07,0xef,0x06,0x69,0xba,0x74,0xac,0x2d, + 0xc8,0x50,0x2e,0x18,0x90,0xbe,0xd9,0x2c,0x80,0x06,0x70,0x5e,0x12,0xd0,0x27,0xe0, + 0x8f,0xe8,0x7b,0x4e,0x35,0x08,0xea,0x66,0xc3,0x8f,0x9f,0x3e,0xbc,0xbb,0xd2,0xf1, + 0xe1,0x90,0x3a,0x36,0x52,0x85,0x9d,0xcd,0x26,0xb5,0x67,0x42,0xc0,0xd0,0x8d,0x96, + 0xc2,0xc4,0xe7,0xa7,0x2b,0x20,0x29,0x00,0x20,0x3e,0xab,0xc7,0x86,0xa4,0xb7,0xc4, + 0x64,0x87,0xe9,0x4c,0x39,0x28,0x1a,0x50,0x42,0xb6,0xec,0x23,0x70,0xe2,0xe7,0x13, + 0x76,0x47,0x0a,0xc2,0x96,0x27,0xe9,0x80,0x2c,0x73,0xd2,0x31,0x17,0xfa,0x68,0x02, + 0x7b,0x7b,0xcc,0x6c,0x0a,0x54,0xda,0x10,0x80,0x49,0x9b,0x29,0x14,0xf9,0x6e,0xad, + 0x3d,0x65,0x2d,0x4c,0xdb,0x19,0x70,0xcd,0xe6,0xfa,0xf0,0x54,0x83,0x28,0x2d,0x35, + 0x28,0x10,0x18,0x89,0x1a,0x51,0x1e,0x64,0xf0,0x34,0x2d,0x8b,0x0c,0x66,0x3a,0xe8, + 0x73,0xca,0x05,0x24,0x9d,0x88,0x2c,0x81,0xd4,0x54,0x5b,0x64,0x16,0x08,0x98,0x45, + 0xa7,0x96,0x0b,0x8c,0x9a,0x00,0xdd,0x2f,0xc4,0x42,0x84,0xf0,0x76,0x20,0xab,0x43, + 0x7c,0x66,0x81,0xaa,0xc0,0xa5,0x89,0xa8,0x45,0x0c,0x8a,0x60,0x91,0x43,0x23,0x68, + 0x65,0x9a,0xce,0xb4,0xf3,0xfe,0x4b,0x0c,0xc0,0x18,0x89,0x24,0x98,0x12,0x98,0xd3, + 0xc1,0xe9,0xab,0xfe,0xe0,0xfb,0xfe,0xe9,0xb9,0x69,0xb1,0xf4,0xf7,0x35,0xaa,0x30, + 0x8e,0xd3,0x34,0x67,0x41,0x21,0x48,0xea,0xd3,0x80,0x61,0x27,0x6b,0x21,0xd4,0x7a, + 0x9b,0x69,0xe6,0x69,0x23,0x6a,0x58,0xed,0xc1,0xca,0x30,0xce,0xc4,0xb9,0x25,0x5f, + 0xfc,0x07,0x63,0x78,0x2a,0xce,0xac,0xf3,0x6f,0xa1,0x64,0x89,0xed,0xd1,0xe1,0x3b, + 0x28,0x7f,0xcc,0x02,0xc3,0x87,0x16,0x30,0xe6,0x69,0x28,0xb4,0xaf,0x45,0x9a,0x6c, + 0xc8,0x86,0xd8,0x14,0xe5,0x06,0x1a,0xc3,0x84,0xc3,0x96,0xc3,0x4c,0x43,0xd7,0x30, + 0x1b,0xa3,0x58,0x8b,0xca,0x82,0x2d,0x47,0xf2,0xdd,0x60,0x28,0x85,0x2e,0xcd,0x92, + 0x80,0x95,0x52,0x4c,0xe1,0x23,0xec,0xb9,0x82,0xe7,0xce,0x08,0xe2,0x99,0xd0,0x5a, + 0x71,0x11,0x35,0x85,0xea,0x49,0x31,0xb7,0x51,0x34,0x91,0x90,0x08,0xb7,0x5a,0x11, + 0x43,0xa2,0xd1,0xc4,0x08,0x16,0x60,0x43,0xf3,0x69,0x47,0x50,0xff,0x28,0x12,0x47, + 0xb6,0xf6,0x73,0x02,0x48,0x04,0x2f,0x82,0xfa,0x1b,0xc3,0x04,0x92,0x7e,0xa4,0x12, + 0x34,0x06,0x81,0x66,0x67,0xc2,0x84,0x3c,0x62,0x34,0x09,0xed,0x97,0x9b,0x1b,0x82, + 0xa5,0xba,0xd7,0xda,0x8b,0x40,0x26,0x8e,0x8b,0x2f,0xd0,0xae,0x58,0x8c,0x22,0x82, + 0xb7,0x46,0x16,0x07,0x69,0xda,0xc2,0x7f,0x2c,0x6a,0xb5,0x0b,0xc9,0x84,0x19,0x45, + 0x6d,0xe1,0x94,0xdf,0x83,0x28,0x67,0x16,0x21,0xcb,0x9a,0x17,0x8a,0x3d,0x81,0x28, + 0x8f,0x4a,0x88,0x65,0xf0,0xac,0x5b,0x8a,0x78,0xf1,0x7c,0xb1,0xde,0x3a,0xeb,0xc0, + 0x87,0x85,0xe1,0xe8,0x49,0xda,0x87,0x18,0xc6,0x77,0x38,0xd1,0xb0,0x3b,0x06,0x9e, + 0x14,0x50,0xec,0xad,0xc1,0x58,0xa9,0xb8,0x48,0xd8,0x8b,0x3d,0x0c,0xe4,0x36,0x63, + 0xf0,0x73,0x58,0x33,0xeb,0x00,0x74,0x9b,0x57,0xca,0xa1,0x4c,0x4d,0xb7,0x4c,0xbd, + 0xfd,0xea,0x42,0xd8,0xab,0xe9,0xa3,0xa7,0x97,0xba,0x2b,0xec,0xc0,0x66,0x71,0x0b, + 0x25,0x84,0xe1,0x6f,0x36,0x4c,0x36,0x6d,0x25,0x30,0x8e,0x92,0x7d,0xb0,0x2b,0x46, + 0x3a,0xe0,0xbe,0xdb,0xdc,0xb4,0xc3,0x87,0x39,0x50,0xa2,0xea,0x80,0xcd,0x89,0x82, + 0x54,0xd5,0xdc,0xf6,0x49,0x99,0xbe,0x5f,0x8c,0xc7,0x24,0xbe,0xf9,0x33,0x4d,0x5b, + 0xea,0x9b,0x50,0xe2,0xa2,0x5f,0x0e,0x5c,0x32,0x02,0xe9,0x9f,0xc7,0x36,0xd6,0x45, + 0xe8,0xe0,0xe5,0x5b,0xfb,0xbb,0xe3,0xf0,0x5b,0xfb,0x8c,0x65,0x47,0x85,0xe9,0x02, + 0x7e,0x77,0x0d,0x9c,0x68,0x96,0x4c,0xbc,0xce,0x60,0x4a,0x09,0xbc,0x60,0x48,0x2e, + 0x49,0xc3,0xa2,0x1d,0x1f,0x28,0xcd,0xb6,0x54,0x02,0xad,0xbc,0xcf,0xd3,0x19,0x08, + 0x95,0xaa,0x10,0x0d,0xd6,0x54,0x25,0x3d,0x73,0x45,0xcf,0x13,0xb0,0x34,0xf4,0x5d, + 0x9a,0x4c,0x2c,0xa6,0xd2,0x8a,0x8c,0x42,0xb1,0x84,0x26,0xb0,0x59,0xba,0x15,0xbd, + 0xde,0xc9,0xff,0xfe,0x5e,0x7c,0xfb,0xfb,0xfa,0x04,0x23,0x2b,0x80,0x43,0x93,0x74, + 0x39,0xc6,0x86,0x65,0x62,0xe5,0xfd,0x21,0xcf,0xa1,0x61,0xf4,0x45,0x42,0x4c,0xe4, + 0x8f,0x62,0x29,0x67,0x60,0xfc,0x98,0x52,0x3e,0x62,0x6d,0x0e,0x0b,0x1b,0xb2,0xa2, + 0xc0,0x44,0xcc,0x35,0x39,0x69,0x90,0xbd,0xbb,0xcd,0xb9,0x7f,0x4d,0xd9,0x63,0x63, + 0x62,0x21,0x39,0x5e,0x73,0xab,0x6c,0x66,0x57,0x75,0xaa,0xf0,0x54,0x6c,0x5b,0x88, + 0x57,0x9f,0xb8,0xbb,0xdc,0x6e,0x04,0xf2,0x9f,0x23,0xf3,0xc1,0x93,0x7d,0x97,0xd0, + 0x3f,0x9f,0x6e,0x6e,0xbe,0x68,0xfa,0x71,0x6e,0xcb,0xb1,0x9b,0xee,0x83,0x7a,0xf2, + 0xaa,0x22,0xb7,0x36,0x55,0x6a,0x9a,0xa9,0x96,0x8f,0x3a,0xd1,0x2f,0xaa,0xce,0x9c, + 0xdc,0x26,0xd9,0x43,0xe3,0xd9,0xee,0xf6,0x0c,0xa3,0x86,0xc9,0xb2,0x9a,0x45,0xd8, + 0x92,0xf1,0x0f,0xd5,0xb7,0x03,0xd6,0xce,0x44,0xe8,0x04,0x5b,0x37,0xd7,0xd5,0x40, + 0x75,0x25,0x99,0x74,0x1a,0x9e,0x7a,0xf6,0x86,0xdb,0x4e,0x7d,0x0e,0x08,0x3d,0x90, + 0xf9,0xe6,0x0d,0xa1,0x5d,0xa0,0x3e,0x35,0x85,0x25,0xe8,0x3e,0x10,0x31,0x94,0x5f, + 0xdd,0x76,0xb3,0xa1,0x8a,0x73,0x9e,0xc3,0x0d,0x14,0x2b,0xf7,0xa2,0xc3,0x06,0x84, + 0x04,0x69,0x6a,0x61,0x40,0xad,0xa9,0x4b,0x53,0xe9,0x7d,0xed,0xef,0x6a,0x31,0x96, + 0x89,0x54,0x8e,0xa8,0xe5,0x2f,0xc4,0xd8,0x98,0xe3,0x24,0x96,0xf6,0xf1,0x87,0x8f, + 0x3f,0xdb,0xec,0x10,0xf0,0x6a,0x4a,0xa5,0x51,0x50,0x6d,0x82,0xd2,0x12,0x64,0x3e, + 0xad,0x3e,0x75,0xe8,0x52,0x1c,0x8d,0x34,0x44,0xac,0x65,0xb0,0x8f,0x0a,0x6d,0x95, + 0x2e,0xe2,0x10,0x2d,0x40,0xaf,0xac,0x9d,0x88,0x96,0x73,0xd1,0x92,0x63,0x22,0xb9, + 0x87,0xdd,0x49,0x4a,0xa4,0xd6,0x1a,0x24,0x7f,0xbe,0x40,0xb4,0x46,0x60,0x8d,0x1a, + 0xbf,0x8f,0x16,0xa8,0xef,0x5f,0xd2,0x88,0x59,0x8f,0x9d,0xb1,0xf5,0xe8,0xa0,0xc8, + 0x81,0xa5,0xe9,0x50,0x63,0x4c,0x2e,0x5b,0xcc,0x33,0x88,0x93,0xb6,0x10,0x91,0x45, + 0x4c,0x2e,0x5f,0xdd,0x15,0x1c,0x4e,0x61,0x1c,0xc6,0xa3,0x5e,0x0f,0x7e,0x21,0xf0, + 0xc9,0x1e,0x5c,0xaf,0xf7,0x2f,0x65,0x5b,0x99,0xeb,0xaf,0xde,0xbf,0xec,0x62,0x1a, + 0x8d,0x41,0x06,0x64,0x38,0x7d,0xb5,0xc3,0x5e,0x0f,0x3f,0x10,0x4c,0x5f,0xed,0xc4, + 0x20,0x27,0xc9,0x74,0x79,0x4d,0x26,0xa9,0xf1,0xb5,0x63,0xf3,0x72,0x01,0xf7,0x93, + 0xb9,0xf0,0x3c,0x87,0xcc,0xe3,0x99,0xa7,0x66,0x61,0xc3,0x57,0x4a,0xe3,0xa5,0x00, + 0xc4,0x31,0x49,0xb9,0x9a,0xd9,0xdb,0x35,0xbe,0x02,0x97,0x28,0xdb,0x1e,0x64,0x4f, + 0xa9,0x8e,0x9c,0x59,0xf7,0xab,0xfd,0x88,0xe7,0x6a,0x9a,0x1d,0xca,0x53,0xdf,0xe9, + 0xb9,0x85,0x06,0xd6,0xac,0xf5,0x10,0x8b,0x74,0x91,0x07,0xc2,0x61,0x61,0xc2,0xf6, + 0x1d,0x56,0x32,0x2a,0x89,0xdb,0x73,0xa8,0xd2,0xe2,0x31,0x21,0x8f,0x3e,0x49,0x17, + 0x05,0xa9,0xca,0x5c,0xd4,0xea,0xd6,0x2f,0x08,0x50,0x13,0x22,0x61,0x45,0x56,0x4c, + 0xb9,0x0a,0x40,0x11,0x01,0x41,0x2c,0x29,0x6a,0x6b,0x2d,0xec,0xb8,0xc8,0x03,0x63, + 0x5c,0xdb,0xce,0xe4,0x9d,0xc3,0x2d,0xcd,0xa4,0x31,0xf8,0x14,0x35,0x7f,0x03,0x31, + 0x95,0x83,0x5f,0x73,0xf0,0xf3,0x84,0xc2,0x9d,0x3e,0xab,0x87,0xda,0xe2,0x63,0x3f, + 0x57,0xba,0x81,0x0d,0x49,0xf7,0x01,0xea,0xb2,0x69,0x27,0x9e,0x53,0x60,0xa6,0xcb, + 0x2c,0x07,0xc3,0x9b,0x16,0x60,0x6b,0x49,0x5d,0xe6,0xee,0x5b,0xc8,0x27,0xfa,0x4c, + 0xc9,0x3e,0x5a,0xd9,0xee,0x92,0x3e,0xa9,0xc9,0x2e,0xa1,0x5d,0xa4,0x73,0xd1,0x19, + 0x52,0xad,0x00,0x5b,0x12,0x04,0xee,0x54,0x23,0x75,0x78,0x94,0x22,0xe4,0xf9,0xb1, + 0x5c,0xd9,0xd6,0x72,0xe5,0xa1,0xd7,0xdb,0x2b,0x42,0x78,0x3a,0xe8,0x91,0xa7,0xc3, + 0x82,0x90,0x24,0x47,0x63,0x7a,0x10,0xe3,0x18,0xac,0x0b,0x0c,0x42,0x25,0x47,0x96, + 0x7e,0xf9,0x44,0x6e,0x89,0x54,0x9e,0xbf,0x7c,0xba,0x9d,0xdd,0xbd,0x79,0x25,0x5e, + 0x56,0x92,0xbc,0xcc,0x17,0xa2,0x12,0x9a,0x32,0x7c,0x73,0x00,0x7f,0x95,0x24,0x44, + 0xe3,0x64,0x80,0x5c,0x60,0x09,0x58,0xc1,0xa8,0x76,0x36,0xa6,0xde,0x7a,0x5e,0x38, + 0x28,0x13,0x2b,0x67,0x00,0x37,0x6a,0x60,0xa5,0x89,0x33,0xb4,0xee,0xf1,0x80,0x82, + 0x6d,0xcb,0x10,0x49,0x58,0x74,0x77,0xad,0x4e,0x18,0x9a,0xf8,0x2d,0xac,0xd3,0x6f, + 0xa7,0xb6,0x58,0x59,0xd7,0xbd,0xde,0xb5,0x4d,0xb1,0xb2,0xcd,0x46,0x8d,0xff,0xe2, + 0xa5,0x38,0x73,0x06,0x6d,0x71,0x41,0xe1,0xd2,0xf0,0xc9,0x32,0x4d,0x6d,0x08,0x51, + 0xfc,0xb4,0x89,0x69,0x6a,0x4f,0x52,0xf8,0xb8,0xdb,0x03,0x7a,0xf0,0xda,0x5b,0x1f, + 0xfb,0x9b,0x0e,0xa4,0x76,0x9b,0xda,0xb0,0x6a,0x37,0x53,0xfb,0xbe,0xde,0x14,0x41, + 0xe9,0x95,0x3d,0x8d,0xc2,0x50,0xe0,0xc3,0x21,0xe4,0x92,0xb9,0x9e,0xda,0x2b,0x10, + 0xa0,0xac,0x20,0x23,0x07,0x30,0x46,0xd0,0x86,0x35,0x02,0x49,0xcb,0x96,0xff,0xad, + 0x4c,0xb7,0x0a,0xb7,0xca,0xbf,0x02,0x2a,0x69,0x45,0xab,0x8a,0xfa,0x05,0x21,0xd7, + 0xce,0xc6,0x17,0xc4,0x92,0xa6,0xc3,0x9c,0x89,0x7f,0x26,0x94,0xdd,0x3d,0xe3,0x9d, + 0x5c,0x1a,0x4b,0x27,0xbd,0xa7,0x77,0xfd,0x16,0x05,0x97,0x07,0xbe,0x35,0x0f,0x9e, + 0x12,0x74,0x08,0x65,0x60,0x0c,0xad,0x5d,0xea,0xe5,0xe2,0x81,0x25,0xc9,0xa7,0x71, + 0xec,0xa9,0x34,0x9d,0x59,0x4b,0xe0,0x89,0x26,0x24,0xb1,0xb2,0xc4,0x58,0x49,0xb6, + 0x62,0xfe,0xd4,0x4d,0x1b,0x35,0xf0,0xce,0x7a,0xbd,0x25,0xfe,0xe3,0xb8,0xc7,0x0e, + 0x66,0xf0,0x1d,0xb6,0xc2,0xae,0xd5,0x46,0x8b,0xed,0xd1,0xcf,0x05,0xfd,0x54,0xf6, + 0x9a,0x13,0x6e,0x0f,0xe4,0xea,0x4a,0x52,0xc1,0xa8,0xd2,0xd9,0x05,0x7b,0xc0,0x20, + 0xb8,0xa5,0x45,0x62,0xc5,0x74,0xaa,0x77,0x8e,0xa9,0x2d,0x99,0xe8,0x41,0x65,0x5f, + 0xa4,0xd2,0x99,0x4a,0x16,0x64,0xc7,0x97,0x83,0x54,0x18,0x36,0xd3,0xb2,0x29,0x01, + 0xc3,0x08,0x97,0xca,0x81,0x48,0xa2,0x31,0x4d,0xd7,0xb0,0xb4,0x33,0xa7,0x85,0x52, + 0x9a,0x23,0x66,0xfe,0x94,0x4e,0x64,0xb8,0xe3,0x4b,0x1d,0xee,0x98,0x36,0xe1,0x8e, + 0x2f,0x9d,0x70,0x87,0x75,0x80,0xd9,0x3a,0x4c,0x82,0x20,0xc9,0x06,0x36,0xd3,0xce, + 0x1c,0x46,0x26,0xb5,0x23,0x90,0x9d,0x19,0x6f,0xb7,0xec,0x6f,0x36,0x5c,0x2c,0x0a, + 0xf8,0xb8,0x2d,0x36,0x8e,0x30,0xaa,0xe8,0xcd,0x97,0x2a,0xd2,0x10,0x1d,0x1f,0x13, + 0x47,0x7f,0xb9,0x8d,0xee,0xec,0x95,0xb9,0x96,0x7f,0x51,0x85,0x1f,0x68,0x86,0x5b, + 0x69,0x4c,0xc8,0x58,0x99,0xb4,0x21,0xa4,0x8e,0xd1,0x00,0x51,0x13,0x30,0x19,0xe5, + 0x76,0x52,0x4b,0x27,0x14,0xe5,0x7f,0x8b,0x16,0xa3,0x82,0xfa,0xf4,0x13,0x3f,0x8b, + 0x9a,0xc0,0x1b,0xd3,0x21,0xd9,0xd3,0x92,0xd2,0x42,0x41,0x9e,0x7d,0x0e,0x3e,0xe3, + 0x96,0x20,0x61,0x62,0x02,0x74,0xfa,0x2b,0xfb,0x80,0xbe,0xb2,0x55,0xa1,0xaf,0xf2, + 0x88,0x2c,0x93,0x76,0x98,0x12,0x8a,0x6b,0x56,0xf0,0x50,0x30,0x32,0xe5,0xea,0x91, + 0x60,0x83,0x4a,0x93,0xa1,0xa2,0x88,0x15,0x1b,0x86,0x92,0x47,0x28,0x33,0xe0,0x84, + 0x5b,0xa0,0xd0,0xc5,0x88,0xdd,0xaf,0x32,0xd5,0xce,0x06,0x5a,0x61,0xc2,0xdb,0x8c, + 0x45,0x01,0x9b,0x47,0x7a,0x80,0x14,0x14,0xcd,0xd3,0x11,0x16,0xd9,0x87,0xce,0xf3, + 0xc9,0x48,0x4f,0x31,0xc0,0xb6,0xda,0x23,0xd8,0x12,0x27,0x98,0x28,0x5b,0xd0,0xf7, + 0x66,0x71,0xef,0x11,0xe7,0xf2,0x24,0xd4,0xd4,0x82,0x38,0x6f,0x33,0x1a,0x5e,0x2b, + 0x13,0x18,0x92,0xea,0x30,0x87,0x5d,0x02,0x09,0xc9,0x0e,0xe2,0x21,0x58,0xb9,0xea, + 0xde,0x6c,0xc9,0xa0,0x04,0x0e,0x26,0xc5,0xaf,0x6b,0x27,0xfb,0xf4,0xdb,0x62,0xb4, + 0xd9,0x90,0x73,0x0d,0x77,0xdb,0x64,0xbd,0x54,0x87,0xf7,0x08,0xfc,0x5a,0x05,0xbd, + 0xdb,0x86,0x0b,0x01,0x51,0xe3,0xbc,0x36,0x3b,0x63,0xdf,0x36,0x8b,0x4b,0x3e,0x08, + 0x47,0x1d,0x66,0x22,0x2b,0xe1,0xf5,0x8a,0x62,0xaa,0xb1,0x35,0xc5,0x06,0x04,0x39, + 0xaa,0x24,0x58,0x2d,0x2a,0x03,0x5e,0x39,0x4e,0x50,0x54,0x04,0x00,0x19,0x4f,0x80, + 0x0c,0xf2,0x10,0x64,0x1b,0x34,0x27,0x9b,0x83,0x76,0x72,0xb4,0x95,0x1f,0x95,0x1f, + 0xd3,0xfc,0x8a,0x5d,0x16,0xd9,0x0d,0xf9,0x21,0xb2,0x1d,0x6f,0xc3,0x02,0x5f,0x2d, + 0xfa,0xa1,0x30,0xa6,0x0c,0x44,0x15,0xcd,0xbc,0x57,0x90,0xf2,0x15,0xb2,0xbc,0x16, + 0xb2,0x36,0x9b,0x5a,0xe8,0x33,0x4a,0x59,0xe0,0x0f,0x4f,0x06,0x12,0xcf,0x80,0xe0, + 0x51,0xd4,0x9f,0x27,0x5f,0x64,0xc0,0x4e,0x66,0x13,0xaf,0x62,0x19,0xd4,0x3e,0xc0, + 0xb6,0xd1,0x94,0xf8,0x4e,0x9f,0x41,0xbc,0x73,0x8e,0x09,0x51,0x31,0x6a,0x29,0x25, + 0x26,0x17,0x96,0xb4,0x57,0x6b,0x45,0xb7,0xd6,0x9a,0xc5,0x84,0xda,0xaf,0xe8,0xaa, + 0x50,0x45,0xd8,0x46,0xe5,0xb8,0x8f,0x13,0xaf,0x1b,0xea,0x2d,0xec,0x26,0x98,0x6e, + 0x5e,0xb4,0xdf,0x1c,0x58,0x51,0xb9,0x77,0x78,0xed,0x1e,0x5c,0x7b,0xbf,0x81,0x23, + 0xe4,0x12,0x79,0x85,0x4b,0x5b,0x19,0xd2,0xa9,0x92,0x41,0xe1,0x63,0xf6,0x30,0x65, + 0x98,0x2d,0x1a,0x3f,0x1a,0xe3,0xc4,0x04,0x29,0xf1,0xbe,0x00,0x98,0x9e,0x4c,0x81, + 0x71,0xe2,0xd2,0x2b,0x80,0x92,0x0a,0x21,0xcb,0x60,0xe4,0x87,0x13,0xa1,0x08,0x31, + 0xef,0xf5,0x0a,0x78,0xd1,0x45,0x6e,0xd3,0xe6,0xa5,0xcd,0xfb,0xea,0x84,0xef,0x4f, + 0xbc,0xe7,0xe6,0xe9,0x3a,0x89,0x99,0x9f,0x47,0x64,0x9e,0xd9,0x33,0xf1,0x58,0x18, + 0xd3,0xb9,0xa9,0x84,0xc7,0xdb,0xef,0x4c,0xde,0xa3,0x77,0xa7,0xf3,0xdb,0xdf,0x20, + 0xd0,0x12,0x91,0xff,0x46,0x7b,0x49,0x77,0x5e,0x05,0x0d,0x6b,0x04,0x6c,0x4a,0x50, + 0x2c,0xca,0x73,0x93,0xec,0xc2,0xce,0x4e,0x79,0xb1,0xb3,0x53,0x6e,0xed,0x4c,0x68, + 0xcd,0xd3,0x74,0xd4,0x74,0x69,0x6b,0xc3,0x19,0x27,0xd6,0xd4,0x99,0xce,0xb7,0x66, + 0xc7,0xa3,0xee,0x4a,0x38,0xda,0x02,0x6a,0x0b,0x39,0x96,0xfa,0x54,0x08,0xc1,0x66, + 0xf1,0xea,0x2e,0xb9,0xcb,0x4a,0x28,0x15,0x8a,0x2f,0xb3,0x71,0x69,0xd2,0x46,0x4b, + 0x8b,0x05,0xab,0xad,0x0c,0x0b,0x94,0x75,0xdc,0xd9,0xe3,0xf9,0x76,0x08,0x16,0x74, + 0x0f,0x88,0x60,0xdb,0x6b,0x62,0x2e,0x79,0x83,0xb0,0x7e,0xad,0x4d,0xd9,0x82,0xc8, + 0x43,0x11,0x31,0x56,0xb3,0xa6,0xa3,0x60,0xc4,0xf4,0x49,0x7f,0x78,0xdc,0xac,0x8a, + 0xf0,0x46,0xb8,0xbf,0x36,0x2b,0x85,0x75,0x6d,0x6e,0x25,0x23,0x93,0xba,0xfa,0x68, + 0xaf,0xfc,0x9a,0x8f,0x1b,0x48,0x14,0x24,0x62,0xb2,0x7f,0x03,0x7e,0x31,0x69,0xcb, + 0x6b,0x5e,0xb8,0xb2,0x95,0x4b,0xc3,0x94,0x4d,0x27,0xa2,0xdc,0x89,0xfb,0xa6,0x1e, + 0x87,0x3e,0xdd,0x46,0x66,0x2f,0x2c,0x8a,0x7e,0x5a,0x29,0x7b,0xcc,0x17,0xd2,0x7f, + 0x76,0xa4,0xf5,0x40,0x85,0xca,0xef,0x94,0xf0,0xa2,0x44,0x3a,0x93,0x6d,0x1b,0xa9, + 0x5a,0x0e,0xda,0x4a,0x93,0x4b,0x72,0xb3,0xa3,0x74,0x6e,0x48,0xc7,0xcc,0xd8,0x86, + 0x99,0xe8,0xbd,0x9e,0x54,0xee,0x5c,0x58,0xbe,0xa1,0x70,0x77,0x72,0x7c,0x5c,0x8d, + 0x27,0x41,0x3f,0x24,0x60,0x2e,0x77,0xc2,0x6a,0x6d,0xa9,0x21,0x4a,0x42,0x86,0x60, + 0x6e,0x23,0x95,0xd6,0xc8,0x8a,0xa2,0x6d,0x6d,0x14,0x6d,0xe1,0x61,0xd2,0x96,0x9f, + 0x34,0xae,0x0e,0xea,0x06,0x45,0x77,0xeb,0x26,0x17,0x73,0x48,0xff,0xdd,0xdd,0x1b, + 0x8b,0xde,0x2b,0x30,0xfc,0xd2,0x48,0x3c,0xb5,0xb5,0x53,0x90,0x4c,0x27,0x09,0xa0, + 0x9b,0xfb,0xb6,0x82,0xf4,0x63,0xa2,0xb9,0x60,0x1a,0x65,0x6a,0x6d,0xdc,0x16,0x49, + 0x0a,0x49,0x91,0x6d,0xb3,0x95,0x7d,0x88,0xb5,0xda,0xab,0x24,0xca,0xe0,0xa0,0x0b, + 0x0c,0xdd,0x7a,0x7a,0xdd,0x59,0xd7,0x93,0xac,0x50,0x8b,0xf7,0xb7,0x20,0x61,0xb3, + 0x4c,0xfd,0xa2,0x34,0x74,0x72,0xf6,0xc8,0x67,0x97,0x13,0x48,0x67,0x3a,0xf9,0x29, + 0x0a,0x87,0x6a,0xd8,0xac,0x1d,0x6e,0x56,0x29,0x99,0xfe,0xbe,0x96,0xa7,0x2b,0xab, + 0x72,0x01,0xa1,0x2d,0x92,0x20,0x5e,0x84,0x22,0x54,0x36,0x01,0x45,0x83,0x64,0xc4, + 0x8a,0xe4,0x7b,0x13,0x60,0x55,0x51,0x2b,0x02,0x44,0x1e,0xbc,0x54,0xd3,0xe4,0x5c, + 0x12,0x55,0x4b,0xd9,0x5f,0xa9,0x5d,0x6d,0xee,0x87,0x50,0xe8,0xda,0x2a,0xcd,0x39, + 0x39,0x27,0x90,0x5b,0x82,0x5a,0x4c,0xe1,0xeb,0x11,0x07,0xcb,0xda,0x2a,0x98,0xdd, + 0x17,0x22,0xf6,0xe3,0x63,0x46,0xca,0xdb,0x21,0xcb,0x7f,0xb3,0x99,0x44,0xbd,0xca, + 0xed,0x88,0x63,0x15,0xda,0x5a,0xcb,0x98,0xd5,0xd0,0xe5,0x80,0xd4,0xde,0x66,0xae, + 0x5c,0x9c,0xad,0xe4,0xb6,0x96,0x0e,0x6b,0xe8,0x9c,0xbc,0x98,0x8a,0x77,0x58,0xc6, + 0x8b,0x44,0x99,0xa9,0xc7,0x46,0xda,0xc4,0x70,0xbe,0x07,0x39,0x5b,0xb0,0x5b,0x0f, + 0xf2,0x02,0x44,0xdd,0x74,0xd5,0xa5,0x99,0xbf,0x76,0x78,0x9b,0xa0,0xec,0xc4,0xe7, + 0xd0,0x69,0x4b,0x4c,0xc1,0x50,0xac,0x7d,0x22,0x38,0x52,0x83,0x63,0xd8,0x08,0x7d, + 0x1e,0x4a,0x27,0xe4,0x4a,0x46,0x1d,0x33,0x08,0x7f,0x7a,0x0b,0x93,0x25,0x94,0x96, + 0x41,0xbf,0x0f,0x2c,0xd4,0xf4,0xb5,0x0f,0x8b,0x8a,0x56,0x13,0x63,0xd7,0xa9,0x36, + 0xdb,0xe6,0x7a,0xb2,0xdc,0x1d,0xd6,0xcb,0xc1,0xa0,0xa9,0x51,0x7b,0x33,0x07,0xcf, + 0x1b,0x86,0x30,0x6d,0xce,0x76,0xc3,0x72,0x95,0x12,0x4d,0x67,0xde,0x21,0x50,0xcb, + 0x92,0x15,0xba,0xab,0xde,0x1a,0x4d,0x6b,0x61,0xab,0x5c,0x86,0xbc,0xe8,0xf7,0x95, + 0xe7,0xbd,0x3d,0x68,0x54,0x31,0xfb,0x04,0x50,0xbd,0xa6,0xa3,0x70,0x08,0x2e,0xb3, + 0xf8,0x91,0xf9,0x4d,0x5a,0xf5,0xac,0xc8,0xa5,0xc8,0xf9,0x44,0xf1,0x23,0x07,0x24, + 0xab,0x2d,0xb2,0x38,0x25,0x0a,0x5e,0x25,0x32,0x58,0x4b,0xe4,0xcd,0xbb,0xde,0x64, + 0x24,0x8e,0x60,0x34,0x52,0x94,0x84,0x8d,0x1d,0xca,0xb7,0xe2,0x28,0x2d,0xca,0x0b, + 0x19,0x67,0x01,0x94,0xf6,0xfe,0x09,0xdc,0x56,0xeb,0x5e,0xba,0x35,0x50,0xa1,0x51, + 0xe8,0xcc,0x05,0xe9,0xb4,0x8b,0x21,0xdc,0xde,0x7b,0xe7,0x9e,0xff,0xaa,0x2d,0xc5, + 0x2d,0x87,0x99,0x46,0xc1,0xc5,0x08,0xde,0x1d,0xb4,0xea,0x8f,0xb0,0x54,0xa1,0x38, + 0x8d,0xb9,0xe9,0x3c,0xab,0x48,0xc9,0xf7,0x7d,0xa2,0x49,0xe7,0x1d,0x7d,0xd9,0x76, + 0xf5,0x1f,0xf0,0x8d,0x69,0x60,0xbe,0xd9,0xcc,0xed,0x88,0xd0,0x39,0x17,0x2d,0xa9, + 0x32,0xb7,0xa7,0x26,0xdc,0x7d,0xfa,0x74,0xa7,0x56,0x8f,0x8d,0x23,0x60,0x55,0x94, + 0x42,0xab,0x3e,0xc9,0xaa,0xf7,0xbd,0x1e,0xc7,0xdc,0x4c,0x4c,0x92,0xb6,0x6d,0xe5, + 0xe8,0x79,0xbf,0xe1,0x7d,0x0e,0x04,0x06,0x90,0x3e,0x97,0x53,0x1f,0x92,0x34,0xa6, + 0xb1,0x82,0xf0,0x31,0xb1,0x34,0x99,0xcb,0x69,0x79,0x9d,0xb0,0xc7,0x83,0x21,0x6c, + 0xca,0x7a,0x32,0xb7,0xcd,0xc8,0x55,0xc6,0x05,0xed,0x60,0x42,0x94,0xf2,0x2e,0x33, + 0xc9,0x55,0x01,0xf3,0x83,0xf2,0x02,0x19,0x19,0xba,0xd5,0x06,0xc3,0xb1,0x57,0x18, + 0x2f,0xbc,0x61,0xa2,0x90,0xd3,0xeb,0x09,0x1b,0x43,0xfa,0x37,0xa5,0xc7,0xb1,0x51, + 0x82,0xbe,0x5a,0xc1,0xfa,0xd6,0xc7,0x0e,0xd2,0x28,0xd0,0x4a,0xec,0x35,0x6c,0x7b, + 0xbc,0xa3,0x85,0xea,0x45,0x66,0x89,0x40,0x0c,0xa9,0x2c,0x11,0x8a,0x25,0xc1,0x7a, + 0xa0,0x21,0x5e,0x89,0xb1,0xbf,0x88,0x29,0xe6,0x48,0xd1,0x2e,0x42,0x2c,0x43,0x27, + 0x03,0x4b,0xb1,0xbd,0xae,0x6f,0x59,0xd2,0xd0,0xf0,0x1a,0x2c,0x64,0x75,0xa0,0x64, + 0xe2,0xb1,0x57,0xca,0x7d,0x58,0x94,0x1a,0x52,0xba,0x07,0x8c,0x68,0x48,0x14,0xa2, + 0xf9,0x09,0xfa,0x95,0x03,0x80,0x7a,0xdd,0x87,0x1b,0x99,0x7c,0x05,0x57,0x07,0x8b, + 0xa0,0x5b,0xa3,0x85,0x9c,0x4b,0x67,0x26,0x72,0x3f,0x06,0xc0,0xc2,0x6a,0xf5,0x43, + 0x0a,0x93,0x75,0xa2,0x54,0xa6,0xcb,0xbd,0x82,0xb9,0x54,0xa7,0xfd,0xbe,0xec,0x16, + 0x9d,0x4a,0x5d,0xf9,0x97,0xfd,0x12,0xf9,0x13,0x8c,0xb7,0x83,0x3a,0x92,0x79,0xd0, + 0x31,0xb9,0x30,0x0e,0x8b,0x22,0x4b,0xd0,0xca,0x3b,0x11,0xc4,0xac,0x92,0x0b,0xa8, + 0x31,0x66,0x9e,0x55,0xe8,0xcb,0x6a,0x8e,0xfd,0xb7,0xdc,0xc6,0xd0,0xd8,0x08,0x73, + 0xea,0x1d,0xd2,0x38,0x24,0x86,0x04,0xe1,0xc9,0xe0,0x68,0x94,0xcf,0x57,0xc4,0x99, + 0xa4,0xb4,0xfc,0x80,0xc3,0xa4,0x85,0x88,0xc7,0x16,0x34,0x54,0xd0,0xb6,0x2b,0x96, + 0x8b,0x3c,0x36,0x46,0x92,0x55,0x17,0xf5,0xae,0x99,0xda,0x79,0x76,0x17,0x2a,0x21, + 0x44,0xe6,0x87,0xb0,0xef,0xa0,0xc3,0x86,0x1d,0xd5,0xc1,0x9a,0x05,0x57,0x6c,0xfb, + 0x62,0xfe,0x92,0x82,0x70,0x24,0x84,0x89,0xec,0xa4,0x89,0x94,0x89,0x1c,0x38,0x9a, + 0xd3,0xf6,0x38,0x19,0x67,0x1f,0x12,0x72,0x57,0x8b,0xf7,0x8f,0x5c,0x93,0xa2,0x7d, + 0xd1,0x84,0xb7,0x85,0x75,0xf3,0x76,0xc0,0x6c,0x96,0x40,0xa9,0xdb,0x14,0x08,0xaa, + 0xf7,0xb5,0xf9,0xad,0x6d,0x0f,0x33,0x51,0xcb,0x8f,0xb7,0x15,0x08,0xd2,0xf4,0xb9, + 0x90,0x0b,0xc1,0x06,0xc0,0x1f,0xe8,0x16,0x68,0x08,0xf5,0xbb,0xdb,0xf6,0x18,0x9a, + 0x1e,0x19,0xec,0xdd,0x66,0xd3,0x40,0x68,0xf7,0x51,0x91,0x6e,0xfd,0xb1,0x63,0xb2, + 0x28,0x33,0x78,0x24,0xf5,0xcd,0x21,0xa5,0xc6,0x80,0x5c,0x54,0x92,0x13,0x14,0xb0, + 0xca,0x76,0x1a,0x72,0x50,0xa4,0xf4,0x6a,0xcc,0xd0,0x04,0x4b,0x15,0xe6,0x94,0x60, + 0x37,0x1b,0x7e,0x57,0x43,0xa7,0xc8,0x93,0xca,0xb2,0xf1,0x46,0x2c,0x1d,0x21,0x83, + 0xc1,0xf8,0x8f,0xb6,0x5a,0x4c,0xa9,0xb9,0xaa,0x42,0x36,0x88,0x2c,0x5d,0xb7,0xd4, + 0x52,0x76,0x18,0x9a,0x55,0x16,0x83,0xf3,0x3c,0x69,0x85,0x73,0xc6,0x15,0x54,0x58, + 0x9d,0x78,0x55,0x99,0x01,0x95,0x06,0x93,0x5f,0x46,0x52,0xdf,0xfb,0xde,0x95,0xed, + 0x63,0xba,0xcb,0x2a,0x37,0xd9,0x9a,0x49,0x21,0x23,0xfc,0xd8,0xd1,0x8f,0x3f,0x43, + 0x9e,0x94,0x53,0x8a,0xbe,0x1e,0xc3,0x7a,0x3e,0x1e,0x51,0xc2,0x44,0x15,0x26,0xe4, + 0x14,0xaf,0x66,0x87,0x82,0xb2,0x7d,0x38,0xf7,0x0d,0x8f,0x2a,0x4b,0x8c,0x52,0x21, + 0x7c,0xda,0xdc,0x33,0x7e,0xf8,0xe9,0xcb,0xaf,0x37,0x9b,0x9b,0x0f,0xff,0x73,0xf3, + 0xee,0x97,0x0f,0xef,0x36,0xd7,0x1f,0x3e,0x7f,0xb8,0xbc,0x31,0xff,0xa1,0xf6,0xfb, + 0x7c,0xbb,0xf4,0x27,0x3f,0xd1,0x2e,0x9b,0xa9,0xc8,0x8a,0x43,0x12,0x90,0xa8,0x58, + 0x86,0xdd,0x94,0xe4,0x19,0x6f,0x0e,0x76,0x3e,0x55,0xaa,0x63,0x66,0xe9,0x43,0xc8, + 0x63,0x8c,0x71,0xb8,0xdd,0x49,0xa1,0x20,0x8d,0xaa,0x92,0xd2,0x86,0xee,0xe7,0x0a, + 0xd1,0x46,0x85,0xd2,0x5a,0xbd,0x6f,0x2b,0x8b,0x56,0xed,0x42,0x7d,0x51,0xce,0x93, + 0xa3,0xa9,0xdd,0x45,0x11,0x56,0x19,0x67,0x60,0xbe,0x68,0x3e,0x5f,0x94,0xb4,0x1f, + 0xc9,0xa1,0x1b,0x32,0x24,0xe5,0xa6,0x05,0xe5,0xa2,0xad,0xa4,0x25,0x89,0xcf,0x04, + 0x47,0x65,0xf0,0x70,0x46,0x42,0x00,0x2d,0x48,0x61,0xa0,0x7a,0x37,0x2b,0xc6,0x02, + 0xe7,0x6a,0xdb,0x9e,0xf7,0xf7,0xef,0x17,0x11,0x5c,0x75,0xd8,0x88,0xf8,0x38,0x4d, + 0xe7,0x4a,0x57,0xb7,0xb6,0x8c,0xc6,0x3f,0x4b,0xba,0x94,0x1b,0xb6,0xd7,0x56,0xe0, + 0x29,0x5a,0x4b,0x73,0xbb,0xd9,0xf2,0xb2,0x22,0x2b,0x76,0xab,0x0d,0x52,0x65,0x6a, + 0xc0,0xa8,0x1f,0xd1,0xcb,0x32,0xf2,0x3f,0xc9,0xe4,0x0d,0xf9,0x89,0x62,0xce,0xbc, + 0x92,0x75,0xe8,0x79,0xb3,0xe9,0x06,0x7d,0x37,0x9b,0x76,0xb0,0x87,0x2d,0xec,0xcd, + 0x26,0xe8,0xf5,0x02,0xbb,0x80,0xb6,0xb8,0x82,0x9a,0x43,0x3d,0x38,0x8d,0x8b,0x8c, + 0x0c,0xab,0xb7,0x67,0x03,0x40,0x68,0xb6,0xa6,0xf9,0xf9,0xad,0x37,0x7c,0x39,0xd8, + 0x6c,0xa4,0x3d,0x9f,0x85,0x6f,0x60,0xe2,0x99,0xed,0x88,0xbb,0xcc,0x24,0xfa,0x93, + 0x18,0x1f,0x1c,0xc3,0xe1,0x93,0x36,0xec,0x93,0xff,0xad,0x54,0xf6,0xca,0x15,0x26, + 0x93,0x0f,0x20,0xe3,0x5e,0x2f,0x86,0xe2,0x3f,0x24,0x73,0xa1,0xf2,0x32,0x62,0xe9, + 0xbd,0x01,0x29,0xfb,0xc9,0x8f,0xc1,0x8c,0x89,0x17,0xcc,0x9d,0x61,0x3c,0x6b,0xcd, + 0xd4,0xbd,0x3e,0x89,0x0d,0x48,0xc3,0xa9,0x32,0x94,0xda,0x9c,0xdd,0xdd,0x87,0x68, + 0xef,0x50,0xb4,0x25,0x54,0x05,0xb9,0x0a,0x38,0x8d,0x3c,0x15,0x4c,0xa2,0x50,0x43, + 0xe6,0xd1,0x2e,0xa6,0x61,0x5a,0x31,0xed,0x69,0x91,0x1f,0xeb,0x7b,0x3f,0xbd,0xfb, + 0xb7,0xca,0xe5,0x34,0xed,0x82,0x36,0x86,0x9a,0x78,0xb8,0xf5,0x58,0xf7,0xfa,0x70, + 0x7b,0x7a,0xd7,0x7f,0xc4,0xcf,0xd6,0x24,0x6a,0xc2,0xa0,0x0c,0x0a,0xc5,0x98,0x56, + 0xd4,0x5e,0x20,0x7f,0x67,0x81,0x7c,0x2c,0x50,0xaf,0x47,0xbf,0x10,0xf2,0xc0,0x69, + 0xc6,0xdb,0xfa,0x27,0xc6,0xc5,0xe1,0xef,0x27,0x66,0xc5,0xe9,0xf2,0xab,0x69,0xc6, + 0x32,0x48,0x50,0xbd,0xbb,0x07,0x46,0x2b,0xfe,0x2a,0x32,0x65,0xe3,0x1f,0x82,0x54, + 0x0f,0x15,0xb1,0xb7,0x9d,0xe0,0x18,0xe5,0xd5,0x8e,0x28,0x93,0x65,0xe5,0xc1,0x1b, + 0x66,0xc7,0x78,0x27,0x50,0x16,0x11,0x4c,0xc7,0xc0,0x7f,0xeb,0xc5,0xf5,0x6e,0x2a, + 0xa7,0x39,0xec,0x97,0x26,0xcd,0x4a,0xb3,0x58,0x69,0x09,0x94,0x0a,0xd8,0xce,0x46, + 0x47,0x13,0x7c,0x88,0x6f,0xe1,0xfc,0xdf,0x1d,0xeb,0x17,0x4b,0x8f,0x9b,0x37,0x14, + 0xc0,0x19,0x2f,0xe6,0x93,0x4d,0xfb,0xfd,0xa3,0xee,0xec,0x72,0xc8,0x3d,0xae,0x46, + 0x32,0x5d,0xd3,0xee,0x66,0xa8,0xfd,0xfa,0x43,0xdb,0x2e,0x67,0xd7,0xb8,0xe4,0x6c, + 0x40,0x42,0x5f,0xb9,0xc2,0x0c,0xef,0xbd,0x5b,0x94,0xdc,0xd5,0x28,0xe8,0x9a,0x43, + 0xb0,0x87,0xca,0x55,0x27,0x11,0x1a,0x26,0x9d,0x4c,0x05,0x7d,0x7a,0x6c,0x25,0x8c, + 0x96,0x90,0xaf,0xa1,0x8c,0x06,0x90,0xc0,0xa6,0x6d,0xc6,0x63,0x83,0x83,0x20,0x22, + 0xcf,0xf5,0x0b,0x5d,0x13,0xd0,0x13,0x5c,0xa7,0x0d,0xb2,0xa4,0x3e,0xda,0x19,0xd5, + 0xe4,0xe7,0x94,0xe5,0x33,0x3b,0x9f,0x4f,0xc6,0xb3,0xb5,0x9a,0x1e,0x5e,0x89,0x33, + 0xe7,0x8c,0x3d,0x32,0x15,0x8b,0xf2,0x1f,0x1b,0x6b,0x13,0xf3,0xb4,0x44,0x8c,0xc9, + 0xcf,0xbc,0x19,0x74,0x3e,0xa8,0x22,0xd5,0xa5,0x00,0xa4,0xd2,0x06,0x3b,0xfb,0x71, + 0x21,0x62,0xfb,0x8f,0x6c,0x7c,0xc3,0xa1,0xd3,0x9d,0xe1,0x53,0xc9,0xde,0x90,0x4a, + 0x3a,0x93,0xd1,0x13,0x1a,0x1b,0x3f,0x70,0x9f,0xe6,0x4e,0x83,0x26,0x66,0x32,0x93, + 0x0e,0x1e,0xcf,0x87,0x62,0x16,0xaa,0xcf,0x67,0x30,0x41,0x96,0x7f,0x77,0x28,0x9e, + 0x57,0xc2,0x2a,0x8f,0x77,0x17,0xec,0x4f,0x86,0x07,0xdf,0xc3,0x3a,0x27,0x16,0x38, + 0x90,0xc1,0x2e,0x12,0xf5,0x0d,0xc6,0x46,0x56,0xd6,0xf6,0xcb,0xa1,0x28,0x13,0xe9, + 0x4a,0x8d,0x2a,0x7e,0x1b,0x51,0x86,0x2c,0xe9,0xb8,0xd0,0x4b,0xe9,0xad,0xe9,0xa7, + 0x4c,0x27,0x93,0x58,0xf6,0x43,0x40,0x75,0x0b,0x6d,0x49,0xac,0xe2,0xcf,0x68,0xe7, + 0x44,0x90,0x9f,0x47,0xbe,0xaa,0xa4,0x93,0x14,0xd3,0x95,0x07,0x36,0x52,0x83,0xdd, + 0x5b,0x95,0xdc,0x7e,0x83,0x6a,0xff,0x99,0x1d,0xdd,0x8a,0xd8,0x19,0x72,0xa7,0xab, + 0xc3,0x35,0x75,0xe1,0x3e,0x4b,0x9b,0x92,0x46,0x28,0xe1,0x9f,0x12,0xbc,0x39,0x44, + 0x54,0x4c,0x61,0xd4,0xad,0x34,0x3a,0x40,0x60,0x33,0x0f,0x5c,0x5e,0x5f,0x7b,0xba, + 0x43,0xc1,0xe0,0xb5,0x1f,0xc7,0x4e,0x94,0x44,0x65,0xe4,0xc7,0xdd,0x53,0x1f,0xcd, + 0x79,0x07,0x17,0xfe,0x6d,0x44,0x1d,0x3b,0xb0,0xf4,0x7c,0x32,0xaa,0xf6,0x9f,0x08, + 0x69,0xb5,0xa0,0x03,0x12,0xfd,0xbe,0xaf,0x8e,0x74,0x00,0xc9,0x96,0x3a,0x71,0x63, + 0xd2,0x09,0x1f,0x55,0x4c,0x71,0x28,0x75,0xd2,0x87,0x8a,0x17,0xce,0xbe,0xf3,0x21, + 0xfd,0xfe,0x6c,0xef,0xc1,0x10,0xee,0xe2,0xcf,0x0f,0x9a,0x2c,0xf6,0x9c,0x2e,0x01, + 0xa1,0xf2,0xf1,0x1c,0xf5,0xca,0xc7,0x8b,0x2c,0x75,0xbc,0xc8,0x74,0xe9,0xc4,0x87, + 0x33,0x3c,0xcf,0x1e,0x4e,0x86,0xf6,0xa9,0x82,0x42,0x87,0xa8,0xac,0xe6,0xcc,0x55, + 0x73,0xa6,0x4a,0x9e,0x4d,0xb9,0x95,0xb6,0xc4,0xdd,0xba,0x42,0x5e,0x02,0x7b,0xa9, + 0x75,0x56,0xc4,0x97,0xc7,0x81,0x80,0x63,0xa8,0xad,0x08,0x12,0x03,0x14,0xde,0x0f, + 0x45,0x00,0xa1,0xcc,0x28,0xa5,0xea,0x5b,0xdf,0x19,0xa7,0xc1,0xa2,0xe8,0x2f,0xa3, + 0x22,0x22,0xb3,0x0b,0x24,0x53,0xe2,0x5b,0xa7,0x70,0x0d,0x26,0xa2,0x88,0x8f,0x73, + 0xba,0x33,0x4d,0x1f,0xa6,0xa1,0xfc,0xd4,0x97,0x91,0x7d,0xa7,0x8f,0x2a,0xf2,0x8c, + 0x4a,0x94,0xac,0x9b,0xc3,0x24,0xc3,0xe1,0xf9,0x20,0x7b,0x70,0xe7,0x7e,0x3e,0x89, + 0x12,0x67,0xa0,0xf9,0x8b,0x32,0x75,0x33,0x30,0x31,0x2d,0xe3,0x40,0x7b,0x05,0xb8, + 0x03,0x8d,0x8e,0xba,0xd4,0x84,0x30,0xc9,0xa3,0xd0,0xa5,0x9f,0x3e,0x66,0x9f,0x91, + 0x3d,0xe7,0x1c,0x8d,0xb4,0x40,0x2b,0x8f,0xb4,0x73,0x60,0x49,0x3b,0x82,0x87,0xa3, + 0x25,0x47,0x27,0xf3,0x28,0x91,0xf1,0xad,0xe1,0x38,0x37,0x19,0xae,0x04,0x8e,0xa9, + 0x2f,0xe6,0x49,0x7f,0xe2,0x67,0x0e,0xaa,0xbb,0x3e,0x94,0x59,0xd2,0x8f,0x00,0xab, + 0x70,0x02,0x41,0x67,0x79,0xe4,0x28,0x47,0x6b,0xee,0x83,0x0e,0xdd,0x38,0xa3,0xba, + 0xf3,0x71,0x2c,0xf6,0x35,0x71,0x09,0xda,0x90,0x27,0x52,0x1f,0xa1,0x19,0xb8,0xb4, + 0x13,0x48,0xb9,0x6f,0x8e,0x5c,0x0e,0x4a,0x38,0x2a,0x45,0x9f,0x8f,0xa9,0x91,0x19, + 0x92,0xfb,0x99,0xec,0x6a,0xba,0xfe,0x3b,0xe0,0x5f,0x03,0x7a,0x9b,0x4c,0x22,0x49, + 0x1a,0xfd,0x95,0x3c,0xf2,0xf4,0x6a,0x30,0x90,0xd0,0xc2,0x35,0x41,0xe1,0x35,0x74, + 0xe5,0x48,0xa8,0xa5,0x3a,0x18,0x45,0x8f,0x8a,0x40,0xe9,0x10,0xd2,0xa2,0xa0,0x95, + 0x7b,0x4a,0x91,0xbe,0xa4,0x23,0x4a,0xf3,0xb3,0xec,0xe5,0x68,0x2d,0x69,0x70,0x48, + 0x34,0xa8,0x16,0x98,0x0e,0xdc,0x35,0xa7,0xee,0xcc,0x2d,0x55,0x5d,0xff,0x05,0x15, + 0x57,0x43,0x3d,0xaf,0x86,0x0a,0xc8,0xbb,0x38,0x62,0x52,0xac,0x0b,0x45,0x1c,0x47, + 0x59,0x11,0x15,0x35,0x45,0xd0,0x02,0x37,0x73,0x78,0xc2,0x5d,0xbe,0xb9,0x33,0xbb, + 0xef,0xbf,0x7f,0x82,0x35,0x0c,0x75,0x04,0x31,0x43,0xe3,0xdd,0x33,0x6f,0x55,0xf9, + 0xc5,0x78,0x3c,0x96,0x63,0x4c,0x2c,0x7b,0xd2,0x5d,0x1f,0xfa,0xe9,0xd3,0xea,0x39, + 0xbc,0x84,0x76,0xd2,0xa2,0x94,0x44,0x51,0x32,0xc4,0xcb,0x18,0x34,0xff,0xba,0xa2, + 0x79,0x48,0xbc,0xbf,0x5c,0xe4,0x69,0x85,0x1d,0x0c,0xb9,0xe1,0x00,0x9a,0xed,0x53, + 0xc2,0x79,0x32,0xa7,0x89,0xb6,0xc3,0xd4,0x75,0xbf,0x6a,0xae,0x3b,0xa4,0xb3,0xcb, + 0xf2,0xc0,0x81,0xc8,0x89,0x5f,0xe5,0x97,0xfa,0xb5,0x62,0x5f,0x20,0x7e,0x4b,0xd0, + 0xec,0x34,0xf9,0x2b,0x50,0x4c,0x79,0x2c,0x8c,0x59,0xc4,0x63,0x40,0x00,0x00,0x2e, + 0x26,0x09,0xa0,0x75,0xe8,0x4b,0x8a,0x14,0x16,0xf7,0x0b,0xaa,0xf4,0xcc,0xa1,0xbe, + 0x0a,0x71,0x11,0xef,0x40,0xf4,0xff,0x8c,0x49,0x5e,0x35,0xa4,0xbe,0x83,0x47,0xe6, + 0xce,0x2e,0x71,0x50,0xe5,0xea,0x70,0x9c,0x3c,0xc6,0xb7,0x8f,0x45,0x95,0xdc,0xdb, + 0x87,0x45,0x89,0xe3,0xa0,0xb5,0xfe,0xc1,0xce,0x29,0xc5,0x46,0xd0,0xc1,0x61,0x09, + 0x8c,0xe1,0x60,0xb0,0x5c,0x69,0x7d,0x6d,0xf8,0x1d,0x46,0x63,0xba,0x3c,0x1d,0x35, + 0xdc,0x33,0x1a,0x5f,0xeb,0x74,0x1f,0xf1,0xe5,0xdf,0x64,0x0e,0xc5,0x0c,0x2f,0x6b, + 0x66,0xa0,0xf3,0xbb,0x98,0x43,0x0e,0xbc,0xec,0xe3,0x87,0x3d,0x6b,0x43,0xff,0x1f, + 0xd6,0xcb,0xb3,0x00,0x41,0x05,0xf6,0x6a,0xfd,0x97,0x15,0x7d,0xd3,0xdd,0x87,0x91, + 0x72,0xd2,0x42,0x49,0xd9,0x5e,0x84,0x33,0x22,0xa3,0x72,0xa2,0x45,0xeb,0xfd,0x4b, + 0xad,0x94,0xc2,0x79,0xb3,0x8c,0xfc,0x5c,0xb1,0xbb,0x5d,0xcf,0x71,0x67,0x5e,0x2f, + 0x07,0xdf,0xb4,0xa5,0x18,0x21,0xd6,0xcf,0xfb,0x13,0xfa,0x4a,0xd6,0xf4,0xf7,0x83, + 0x50,0x4c,0xe8,0xb0,0x06,0xa1,0xe4,0x92,0x46,0xac,0xa1,0x85,0xf5,0x62,0x80,0xff, + 0x69,0x03,0x35,0xea,0x6c,0xb2,0xae,0x8d,0x0a,0x7f,0x84,0x5e,0x20,0xc1,0x5c,0xe6, + 0xe3,0x81,0x4b,0x39,0x3b,0x03,0xb7,0x12,0xec,0x6a,0x68,0xcf,0x89,0x4e,0x40,0x22, + 0x36,0xf1,0x93,0x68,0x2e,0x39,0x22,0x9b,0x68,0xe7,0x85,0x16,0x2c,0x46,0x51,0xd0, + 0x1f,0x89,0xff,0x44,0x22,0x37,0xec,0xa1,0x65,0xbf,0xb6,0xec,0x53,0x6b,0x68,0x6a, + 0x2a,0xb0,0x56,0x6c,0xff,0x6b,0x26,0x1e,0xc7,0x39,0x6c,0xfd,0x42,0xc3,0x60,0xca, + 0x74,0x2d,0x3b,0xfc,0xfe,0xf4,0x1b,0x75,0xbc,0xb3,0x5c,0x35,0x43,0x1c,0x47,0x0f, + 0x22,0xac,0xc6,0x97,0xf3,0x80,0x06,0xae,0xb2,0x3a,0xe8,0xf0,0x6d,0x57,0x67,0x7d, + 0x5d,0x14,0x25,0xfc,0xe0,0x7e,0x75,0x9e,0x56,0xf1,0x8c,0xa2,0xfb,0x3e,0x07,0x9e, + 0x0b,0xc9,0xba,0xff,0xe9,0x73,0xc6,0x91,0x73,0x3a,0x3c,0xff,0xee,0xfc,0xf5,0xd9, + 0xab,0xf3,0xef,0x54,0xe7,0xeb,0x67,0xa8,0xf9,0xec,0x94,0x88,0x79,0xff,0x82,0x56, + 0x6b,0x4f,0xec,0xa7,0xf1,0x5a,0xb6,0x68,0x1c,0xd4,0xd0,0xe1,0x81,0xa1,0x7d,0xbe, + 0xb3,0xae,0xaf,0xf7,0xa1,0x38,0xea,0x52,0xdd,0x0c,0x18,0x2f,0x6d,0xf1,0x94,0x5a, + 0xcf,0xd9,0x78,0xe8,0x48,0x9c,0x27,0x67,0x9b,0xcf,0xbf,0xe3,0x83,0xac,0x52,0xd3, + 0x81,0x30,0xc1,0x36,0x49,0xc7,0x6c,0xda,0x76,0xda,0x3d,0x39,0xe0,0x4c,0xd6,0x4c, + 0x8b,0xbe,0x4f,0xb9,0xcb,0x53,0xe2,0xe5,0x27,0x26,0x4a,0x22,0x8d,0x14,0x65,0x97, + 0x9c,0x63,0x6a,0x16,0x19,0x24,0xe6,0x09,0xdb,0x26,0x7f,0xc3,0x5c,0x61,0x33,0xa8, + 0x2d,0xf8,0x78,0x47,0x70,0x0b,0x53,0xa5,0x12,0x79,0xa4,0x6f,0xec,0x80,0x26,0xb2, + 0x56,0x7a,0x88,0x08,0x57,0xca,0xee,0x75,0x65,0x63,0xb1,0x1c,0xef,0x36,0xa9,0xab, + 0x33,0x39,0x51,0xbf,0x7c,0x6c,0xb7,0xf6,0x55,0x64,0x74,0xa3,0xf2,0xba,0x9b,0x48, + 0x65,0x1d,0xdc,0x3b,0xf9,0xfd,0xe4,0xf8,0x1f,0x27,0x16,0xdc,0x51,0xf8,0x82,0x27, + 0x7a,0x2b,0x6e,0x32,0xe2,0xd4,0x50,0xd5,0xb2,0x09,0x99,0x70,0x28,0x81,0x4f,0x2c, + 0xf2,0x3d,0x07,0xaa,0x4c,0x6f,0xb7,0xa4,0x68,0x38,0x9c,0x8f,0xb5,0x8c,0xb6,0x30, + 0x9c,0x2a,0x54,0x9e,0x1d,0x1b,0xa3,0x0b,0xe5,0xf2,0x93,0x0f,0xdc,0x39,0x2c,0xa8, + 0x12,0x58,0xa4,0x5b,0x4e,0xb1,0x94,0xf6,0x85,0x13,0x71,0x3b,0x2e,0xd3,0xc4,0x6d, + 0x22,0xeb,0x2b,0x05,0x6d,0x2c,0x61,0x4d,0xf6,0xdc,0x20,0x01,0x9a,0x20,0x3f,0xb3, + 0x89,0xc7,0x50,0x88,0xa6,0x1d,0x92,0x59,0x0b,0x8f,0x8a,0x6e,0x23,0x8e,0xb8,0x8b, + 0x5e,0x4f,0x25,0x5e,0x8a,0xdb,0xc1,0x5d,0xeb,0x6c,0xdb,0x9e,0x28,0x0d,0xd5,0x30, + 0x7b,0x3d,0x71,0x3b,0xbc,0x33,0xe3,0x2a,0xab,0x4f,0x1e,0x31,0x3d,0xfe,0x7a,0x7c, + 0x6c,0x0d,0x2c,0x3a,0xdc,0x22,0x0f,0xef,0x32,0x0a,0x9e,0x66,0xf1,0xf8,0x94,0x99, + 0x48,0x03,0xda,0x71,0x54,0x27,0xed,0xd8,0xc1,0x44,0x6f,0x8f,0x3f,0xee,0x0c,0xde, + 0x7f,0x3a,0x65,0x1f,0xf3,0xf5,0x79,0x3f,0xc2,0xe3,0x65,0x88,0xab,0xe0,0x91,0xdf, + 0xe9,0x85,0x8b,0x87,0x3c,0x69,0x55,0x03,0xb3,0xc5,0x92,0xf9,0xed,0xae,0xd3,0x44, + 0x47,0xb3,0x3d,0x4e,0xaa,0x92,0xc7,0xe4,0xcc,0xd3,0x46,0x9c,0xb9,0x35,0xa2,0x37, + 0xd5,0xd4,0x2f,0x28,0x7b,0x67,0xd2,0x3d,0x1b,0x0e,0x1e,0xe6,0x79,0xb6,0xcb,0x26, + 0x98,0xfb,0x34,0x6e,0x8d,0x14,0xd4,0xd7,0xcd,0x86,0x50,0x29,0x4c,0x92,0x1c,0x8a, + 0x7c,0xb3,0x39,0xbc,0xee,0x50,0x04,0xc5,0x74,0x97,0x5e,0x61,0x2f,0x69,0x4c,0x49, + 0x49,0x1b,0xc5,0xea,0x91,0x62,0xaf,0x7c,0xf2,0x19,0x15,0x45,0xde,0x99,0x78,0x7d, + 0x02,0xe6,0xc2,0x58,0x5e,0x70,0x25,0x6d,0xa9,0x3b,0x74,0xca,0xf7,0xb8,0xfe,0xe4, + 0x70,0x74,0x67,0xa4,0x02,0xbb,0xde,0xe1,0x52,0x66,0xb7,0x82,0xa0,0x47,0xbb,0xb0, + 0x64,0x7f,0xc7,0x46,0xfd,0xa8,0x36,0xa6,0x2e,0x74,0x4d,0x3f,0xde,0x2d,0xe4,0xa0, + 0x0f,0xc1,0xa5,0x39,0xeb,0xd3,0xb2,0xcc,0x0a,0xe7,0xa4,0x3a,0x06,0x4d,0x96,0x81, + 0x4d,0x69,0x11,0x27,0x10,0x58,0x34,0xff,0xe2,0x85,0x7e,0x2c,0x92,0x20,0x0d,0xc5, + 0xaf,0xbf,0xfc,0x70,0x99,0xc2,0x07,0x4c,0x68,0x85,0x6b,0xa0,0xe6,0xb6,0x8d,0x2c, + 0x19,0x91,0x97,0xb8,0x0a,0xa6,0x3b,0x78,0xaa,0x62,0xd2,0x16,0xc6,0x2c,0xf7,0x58, + 0xea,0x94,0x84,0x8b,0xd6,0xcb,0xef,0x0b,0x3a,0x30,0xa8,0x3b,0x9c,0x35,0x71,0xf1, + 0xec,0x51,0x20,0xa7,0x93,0x5e,0x73,0x51,0x3d,0x69,0xd4,0x7c,0x78,0xce,0x79,0x98, + 0x8f,0x04,0xda,0xe1,0x4d,0x94,0x8b,0xea,0xd8,0xb7,0x26,0xcf,0x6a,0x87,0x55,0xbd, + 0xd2,0xcf,0x28,0x39,0x53,0xed,0xf7,0x48,0xa0,0xad,0xe4,0x1b,0x48,0x97,0xf2,0xa2, + 0x55,0x50,0x0d,0x8e,0xd6,0x26,0x98,0xee,0x84,0x9e,0x50,0x50,0x2d,0x16,0xbf,0x34, + 0x44,0x4c,0x64,0x71,0xd8,0x85,0x0c,0xa8,0xda,0x0a,0x80,0x82,0xee,0x4e,0x56,0x38, + 0x6b,0xc4,0xdd,0x27,0x0a,0x65,0xef,0xbf,0xac,0xa5,0xb9,0x1b,0xa6,0xd5,0xb6,0x9c, + 0xc4,0x46,0x95,0xc0,0x4c,0x70,0xdc,0x72,0x62,0xc7,0xbc,0x07,0x0e,0x52,0xef,0x8c, + 0x35,0xbc,0xd0,0xf9,0x52,0x17,0xf4,0x7f,0xc5,0x17,0xcc,0xa0,0x66,0x19,0x95,0x31, + 0xd8,0xed,0x5a,0x6d,0x7f,0xa4,0x20,0x1d,0x03,0xf5,0x62,0x55,0x8f,0x3b,0x33,0x8f, + 0x75,0x4d,0x0e,0x80,0x5a,0xec,0xe1,0xc9,0xd8,0x1f,0x61,0x6d,0xac,0x0a,0x5c,0x5b, + 0xb2,0xf2,0x05,0x1d,0x8a,0x38,0x98,0x8f,0xae,0x6c,0x3a,0x26,0xd9,0xc9,0xcf,0xbe, + 0xb2,0xef,0x17,0x22,0x7f,0xbc,0xe6,0x5b,0x2b,0x28,0xf1,0x83,0x76,0x53,0x6e,0xb1, + 0x38,0xff,0xdf,0x3b,0x8a,0x80,0xbc,0xa3,0x3b,0xdd,0x54,0x07,0xb9,0x83,0xa7,0x52, + 0x87,0x2a,0x83,0xb6,0xa3,0x00,0x7a,0x25,0xf6,0x74,0x6a,0xa0,0xd3,0x9b,0xa4,0x74, + 0xc2,0x9e,0x03,0x93,0x6a,0x22,0x4e,0x8a,0xe5,0xe4,0xf8,0x61,0x1e,0x5b,0xdf,0x9c, + 0x5d,0xe2,0x51,0xc3,0x63,0x52,0x78,0x47,0xc4,0x0a,0xe0,0x84,0xd5,0x0a,0x1c,0x70, + 0x66,0xa7,0xf9,0xe4,0xe4,0x14,0xf6,0x1d,0x55,0x3e,0xd2,0x96,0x91,0x58,0xbd,0x4f, + 0x1f,0xbc,0x23,0x36,0x60,0x5f,0xe1,0xbf,0xa3,0x6f,0xce,0x3e,0xa0,0x7d,0x10,0xe5, + 0x01,0xa8,0x2a,0xc0,0xa7,0xd7,0x47,0x5a,0xf0,0xc8,0x7f,0x72,0xef,0xe8,0xe5,0x91, + 0x36,0x8e,0xe2,0xd8,0x3b,0xfa,0xe6,0xf4,0x4c,0x86,0x89,0x8e,0x4e,0x64,0x0b,0x02, + 0x88,0x27,0xdd,0x7d,0xee,0x0e,0x8b,0x28,0x30,0x65,0x9e,0x3e,0x05,0xb3,0x9e,0xce, + 0xb2,0xbe,0x2c,0x43,0xa7,0xfd,0xf1,0xa2,0xa4,0x2c,0x0a,0x14,0x5e,0xca,0x32,0xf7, + 0xa0,0xc8,0x3d,0x2e,0x06,0xc7,0xf8,0xc1,0xf4,0x9a,0x2d,0x1a,0x63,0x4d,0xa7,0xd3, + 0x1c,0x3d,0x45,0x2f,0x3a,0x05,0xd8,0x8a,0x5c,0xa6,0x51,0x7e,0xba,0xf9,0xf1,0xb3, + 0xa7,0xbf,0xe1,0x64,0xcc,0xb7,0xfa,0xf1,0xe5,0xf5,0xf5,0xb1,0xfe,0xe6,0x44,0xbe, + 0xbe,0x09,0xa3,0xa5,0xc6,0x34,0x0c,0xe4,0x27,0x47,0x9d,0xf7,0x11,0x5e,0xfd,0xea, + 0x65,0x7a,0xc4,0xca,0xd6,0x3b,0x3a,0x41,0x69,0x54,0x95,0x86,0x78,0x39,0x89,0xde, + 0xb6,0x39,0xf7,0x0d,0xa9,0xc2,0xea,0x3b,0xc4,0x1c,0xd5,0xa0,0x22,0xfc,0xf1,0x5b, + 0xf0,0x96,0xa3,0x23,0xf0,0x65,0x4e,0xb9,0x83,0x47,0x7f,0x8c,0x62,0x3f,0x99,0x1d, + 0x11,0x83,0x7a,0x47,0x49,0x4a,0x13,0x40,0x3b,0x4d,0xf2,0x99,0x6c,0x77,0x82,0x71, + 0xbd,0x25,0x2b,0xeb,0x0d,0xa4,0x7b,0x05,0x23,0x39,0xd2,0x1a,0x82,0xf4,0x8e,0x14, + 0xf7,0x53,0x87,0xa8,0xf4,0xf6,0x8d,0xf4,0xca,0xaa,0xca,0xc1,0x11,0x1f,0x75,0xc0, + 0xb4,0xb8,0xb8,0x05,0x5e,0x16,0xec,0xd6,0x2f,0x27,0x3b,0x0d,0x30,0x6d,0x9e,0xec, + 0x1b,0x35,0x1b,0xf5,0xa7,0x6a,0xcd,0x23,0x6c,0xa3,0x2f,0x9b,0x1c,0xed,0x29,0x2d, + 0x57,0xd5,0xa0,0xa3,0x25,0x60,0x67,0x70,0x54,0x4a,0x51,0x55,0x6c,0xdb,0x53,0xf7, + 0x46,0x50,0x0b,0x09,0xac,0xe4,0x0e,0xcf,0xd8,0xfa,0x31,0x51,0xd0,0x34,0xf6,0xee, + 0x0d,0x7d,0x4a,0xc2,0x5e,0xe4,0xf4,0x88,0x3f,0x2c,0xf9,0xf9,0x79,0x84,0x47,0xca, + 0xd9,0xc5,0x73,0xc2,0xf9,0x67,0xf4,0x14,0xe8,0x24,0x33,0xe8,0xa9,0xa4,0x34,0xb1, + 0x8c,0x1f,0x33,0x7a,0x2c,0x57,0x5c,0xba,0xd2,0xab,0xa4,0x72,0x73,0x7f,0x12,0xef, + 0xd3,0x9b,0x73,0x30,0x6e,0xc9,0xe8,0x36,0x59,0xd5,0x79,0xf9,0x9e,0x73,0x34,0x0c, + 0x6a,0x6e,0xa9,0x0f,0x9c,0xd7,0xcc,0xb4,0x6f,0xee,0xbf,0xcb,0x42,0xdd,0xed,0x43, + 0x9d,0x97,0xe4,0x1b,0x05,0x30,0x73,0x66,0xde,0xce,0x09,0xac,0x92,0x65,0xde,0x8e, + 0xc0,0x72,0xff,0xec,0xc2,0x2b,0xda,0x50,0xf9,0xb3,0x24,0xe1,0xba,0x5a,0x6b,0x43, + 0x89,0x65,0xec,0x96,0x85,0xff,0x9e,0x71,0xd4,0x9b,0xfa,0x3b,0xfb,0xd6,0xb2,0x50, + 0x45,0xd2,0x9f,0x49,0x9e,0xdc,0x49,0x97,0xa7,0xa9,0xd2,0xa9,0xa9,0x26,0xa3,0xba, + 0x4a,0xa6,0xae,0xb6,0xbd,0xf7,0x6f,0x44,0x04,0x7c,0xdc,0x4c,0xe6,0x11,0x58,0xaf, + 0x07,0x2a,0x3b,0xbd,0xbc,0xaf,0x37,0x55,0xee,0x61,0x66,0x59,0xf8,0x85,0xc5,0xe8, + 0x96,0x2a,0x83,0xbf,0x0a,0xb1,0x37,0xd7,0x75,0xa9,0xcb,0xcd,0xd4,0xbe,0x77,0xa1, + 0x4d,0xd2,0xea,0xd6,0x33,0x75,0x46,0xc2,0x58,0xb0,0xe2,0xa4,0xa0,0x72,0x61,0xda, + 0xea,0x5c,0x51,0x95,0x70,0xd7,0x6c,0x11,0xae,0x24,0x56,0xae,0x37,0x9b,0xe2,0xbe, + 0x9b,0x3d,0xda,0x6c,0x04,0x56,0x1b,0xba,0xe5,0xe0,0xcd,0x10,0x22,0xb7,0x7d,0x28, + 0x61,0x65,0xbd,0x1c,0x28,0xb4,0x75,0xd1,0xd3,0xda,0x5e,0xfb,0xa9,0x49,0x21,0x69, + 0xa5,0x2d,0x26,0xc1,0x14,0xcc,0x20,0xaa,0x93,0xec,0x9e,0xb0,0x03,0xb2,0x5e,0x0a, + 0x11,0x42,0x22,0x4d,0x2f,0xba,0xaf,0x86,0x49,0x69,0xf0,0x11,0x2c,0x13,0x61,0x4b, + 0xc9,0xd3,0x36,0x77,0xb3,0x9d,0xed,0xd3,0x8c,0x33,0x87,0x65,0x1e,0x04,0xad,0xe0, + 0x3b,0x98,0x09,0x5c,0xc6,0x99,0x3a,0x95,0x8f,0x41,0xd6,0x7c,0xbd,0x1d,0x0c,0x93, + 0x1e,0x74,0x8d,0xee,0x8a,0xf2,0xa2,0x7e,0x82,0xce,0xbc,0xa5,0x26,0x50,0x6a,0x8e, + 0x3c,0x88,0x28,0x0f,0x49,0xd0,0xb9,0x7d,0xd2,0x65,0xf0,0xf9,0xfd,0x3c,0x7f,0x94, + 0x57,0x02,0x54,0x07,0xb6,0xc1,0x13,0x74,0x0d,0x93,0x66,0x5c,0xc8,0x83,0xd9,0x9e, + 0xc9,0x19,0x0d,0x7c,0xa3,0x03,0x35,0xa4,0x14,0x6c,0xca,0x84,0x95,0x87,0x51,0x78, + 0x0b,0x46,0x6b,0x1b,0x77,0x4c,0xed,0xd2,0xe4,0xf1,0xe9,0xd4,0x4a,0x69,0x83,0x3f, + 0x9f,0xa6,0x70,0x31,0x69,0x77,0x93,0xdb,0x54,0x16,0x4a,0x8d,0x59,0x2b,0xca,0xa4, + 0x30,0xa0,0x6c,0x82,0x93,0xff,0x95,0x96,0xe4,0xef,0x70,0xe5,0x8c,0x5b,0xbf,0xff, + 0x9f,0x41,0xff,0xfb,0xfe,0xdd,0xf1,0xef,0xb6,0xf9,0x6d,0xab,0xf7,0xdf,0xd9,0xb6, + 0x34,0x6e,0x7f,0x3f,0xb9,0x78,0x71,0xb7,0xf9,0x47,0xbd,0xd7,0x2c,0x31,0xd7,0x98, + 0x02,0x99,0x07,0x4f,0x27,0x44,0xeb,0xce,0x4f,0x95,0x84,0xf2,0x99,0x73,0xfe,0xc9, + 0x81,0x34,0x2f,0x9a,0x67,0x87,0xbd,0xc3,0x6a,0x4c,0x51,0x56,0x43,0xab,0x92,0x9f, + 0x9a,0x9c,0x2a,0xd5,0x1f,0x27,0xe4,0xee,0xe4,0x55,0x4d,0xfd,0x02,0x42,0x82,0x31, + 0x4b,0xa6,0xc6,0xbe,0xac,0x2b,0xf5,0x15,0xd3,0xaf,0x9d,0x1c,0x95,0x7b,0xd5,0xce, + 0x64,0xd9,0x5a,0x6a,0x3f,0xac,0x26,0xc9,0xa8,0xf8,0x42,0xd9,0x94,0x99,0xc2,0x64, + 0xe4,0x36,0x14,0x46,0x4e,0x65,0x97,0xc6,0x50,0xd2,0xf2,0x88,0x14,0x19,0x0d,0xff, + 0xcc,0x89,0x44,0x23,0xe5,0x44,0xf6,0x7a,0xea,0xe1,0x69,0x73,0xf5,0x34,0xe8,0x1e, + 0xc0,0x14,0x7c,0x7d,0x03,0xd9,0x66,0x74,0x4b,0x94,0x5b,0xdf,0x3c,0xe5,0xf2,0x85, + 0x57,0xdd,0xac,0xbc,0x6c,0x72,0x63,0x76,0x4b,0x92,0xe5,0x8d,0xba,0x15,0x61,0x62, + 0x66,0x1d,0xaf,0x31,0x23,0xb7,0xb1,0x11,0x60,0xea,0x74,0x40,0x75,0x52,0x4c,0x25, + 0xe4,0x29,0xaa,0x7f,0xd7,0xbe,0x3e,0x67,0x9c,0x0b,0x21,0x53,0x5f,0x15,0xb2,0xf9, + 0x94,0x78,0xfb,0x88,0x38,0x1d,0xc4,0xa2,0xef,0x74,0x1a,0x4d,0xa6,0xdd,0xf9,0xa5, + 0x4a,0xa8,0xfb,0xbb,0xe4,0xcc,0xc9,0x9a,0xa1,0xcc,0x96,0xfc,0x22,0x73,0x27,0x05, + 0xac,0x31,0x61,0x4b,0xb5,0x0d,0x7b,0x7d,0x40,0x6f,0x73,0x51,0xfa,0xff,0x14,0x8f, + 0xf4,0x18,0x94,0x79,0xac,0x1e,0xf9,0x5c,0xb7,0x7a,0xf6,0x63,0x7a,0xea,0x78,0x3b, + 0x6d,0x36,0x99,0xd0,0x9d,0x38,0x35,0xa7,0xf8,0x44,0x62,0x6d,0x85,0x04,0x61,0x2b, + 0x73,0xcd,0x6a,0x00,0x13,0xcf,0xdf,0xb1,0xfc,0xa5,0x4c,0xd2,0x4d,0x95,0x08,0x39, + 0x21,0x5f,0xe2,0x0f,0x4a,0x22,0xd4,0x3b,0x54,0xbe,0x4b,0xe1,0xd5,0xb5,0x35,0xbb, + 0xc9,0x2a,0x9c,0x5c,0xb1,0x50,0x17,0x70,0x1c,0x36,0x77,0x71,0xd4,0xc0,0x78,0x3d, + 0x5a,0xca,0x40,0x4a,0x23,0x08,0x8d,0x2a,0x53,0x5e,0x26,0x56,0xd1,0x1d,0x48,0xb4, + 0x08,0xca,0x63,0xa5,0xeb,0x5f,0x2a,0x61,0xf4,0x88,0xaa,0x73,0x3e,0xd7,0x4f,0x57, + 0x8d,0xa4,0x63,0x79,0x57,0x09,0xdd,0x17,0x02,0x60,0x33,0x79,0xd6,0x9b,0x73,0x96, + 0xc3,0x54,0x5e,0x2d,0xb1,0x8f,0x0f,0x97,0x74,0x47,0x0f,0x87,0x2d,0x7a,0x3d,0xc5, + 0x3d,0x8b,0x3a,0x78,0xf4,0x0c,0x77,0x52,0x4e,0x24,0xb5,0xd8,0xe5,0x4d,0x39,0xe5, + 0x3a,0x47,0xae,0x6a,0xca,0x37,0x91,0xd4,0xa5,0xf5,0xad,0x5b,0x2d,0xa7,0x44,0xa5, + 0x9c,0xf2,0x56,0x3b,0xa6,0x4f,0xe9,0x9c,0x86,0xfe,0xae,0x4e,0xd2,0xa6,0xfb,0x62, + 0x16,0x49,0x42,0xc7,0x08,0x94,0x4b,0xc9,0xec,0x43,0xb7,0x5f,0xae,0xfc,0xc7,0x0b, + 0x12,0x22,0x7b,0x52,0x73,0xd5,0x3a,0x54,0x9c,0x36,0xdc,0x12,0xe7,0x0d,0x99,0xdf, + 0x06,0x15,0x93,0x37,0xa7,0x58,0xd4,0x25,0x4d,0x87,0xad,0x83,0x2c,0x75,0x9e,0x4a, + 0xaf,0xc7,0x85,0x74,0x07,0xd2,0x42,0x89,0xd1,0x35,0x97,0xf0,0xd1,0x72,0x3a,0xe7, + 0xe4,0xf2,0x6b,0x73,0xbe,0x9c,0x33,0x6b,0xfc,0xa5,0x49,0xbd,0x4c,0x28,0x1b,0xad, + 0x6b,0x40,0xbe,0x8b,0x63,0x0e,0xe0,0xa8,0xb3,0x34,0x93,0xb6,0x8c,0x99,0x30,0xa4, + 0x9a,0xc1,0xf9,0xb5,0x42,0x1e,0x0f,0xa0,0x7a,0xb9,0xa0,0xb0,0x8d,0xf4,0x92,0xff, + 0x8e,0xf0,0x40,0xf9,0xf3,0x79,0x15,0x7b,0xc4,0x0a,0x38,0x5d,0xdf,0x5a,0x43,0xb2, + 0x0d,0xe4,0xb1,0x42,0xb8,0x5a,0x9a,0xda,0x54,0xd6,0xce,0x34,0x92,0x16,0xf2,0x66, + 0x82,0x26,0xbd,0x54,0x5b,0xf9,0xa4,0x4f,0x21,0x21,0xe8,0xc2,0x21,0x1b,0xf8,0xde, + 0xdb,0xa5,0x16,0x4c,0x67,0xb2,0xdb,0x2a,0xa5,0xce,0xdc,0xa9,0x89,0x0a,0x7c,0x6a, + 0x40,0x5a,0x25,0xb5,0xec,0xdc,0xca,0x42,0x4e,0xe8,0xdf,0x97,0x13,0x4d,0x1c,0x03, + 0x80,0xed,0x84,0x71,0xc8,0xa0,0xd6,0x82,0x57,0xd7,0x93,0xb5,0x17,0xfd,0xaf,0x56, + 0xf2,0x99,0x1c,0xbc,0xee,0x81,0x73,0x4e,0xc7,0x83,0xad,0x2b,0xff,0x56,0x47,0x6e, + 0x57,0xea,0xae,0x90,0x3a,0x83,0x7e,0xf0,0x17,0x83,0x2f,0xa6,0xe9,0xea,0xa9,0x04, + 0x3d,0x04,0x71,0x13,0xf7,0x17,0x94,0x68,0x5b,0x71,0xcd,0xb5,0xba,0x57,0xa1,0x0d, + 0x7e,0xc8,0x69,0x1d,0x0a,0x59,0xf5,0xa1,0x38,0x55,0xa2,0xab,0xbe,0xf7,0x89,0x6e, + 0x5e,0xd6,0x08,0xae,0xd0,0x63,0x30,0xa5,0x6b,0x2e,0x3b,0x08,0x94,0x41,0x03,0xb9, + 0x50,0x7c,0x0f,0x87,0x59,0x69,0x17,0x02,0x77,0xab,0xab,0x0d,0x0a,0x12,0xb2,0xba, + 0xa5,0xcf,0xc4,0xa3,0x7a,0xe2,0x7b,0x36,0xf5,0xbb,0xa7,0xb7,0x32,0x94,0x74,0xd1, + 0xdc,0x93,0x41,0x94,0xed,0x3e,0xab,0x8b,0x15,0x87,0x75,0xf6,0x70,0xbb,0x17,0x33, + 0x53,0xc7,0x72,0x94,0x01,0xa0,0x6e,0xa6,0xb9,0xb2,0xf9,0x20,0xea,0x75,0x65,0xf5, + 0x93,0xec,0xe0,0xc3,0x38,0xfb,0xa6,0x7c,0xf5,0xf3,0x8f,0x2a,0x52,0xf3,0x19,0xd5, + 0xe0,0xf8,0x58,0xec,0x01,0x29,0x92,0x53,0x81,0x94,0x2a,0xc7,0xec,0xff,0x00,0x05, + 0xd5,0xff,0xef,0x2a,0x59,0x00,0x00, }; constexpr size_t PF_CONSOLE_JS_GZ_LEN = sizeof(PF_CONSOLE_JS_GZ); // diff --git a/firmware/patternflow/src/web_update_index.h b/firmware/patternflow/src/web_update_index.h index 0b8b4ad6..131f8833 100644 --- a/firmware/patternflow/src/web_update_index.h +++ b/firmware/patternflow/src/web_update_index.h @@ -27,103 +27,82 @@ const char WEB_UPDATE_HTML[] PROGMEM = R"HTML( - + Patternflow — Firmware update -
              -
              -

              Update

              …
              +
              +

              Update

              …
              +

              Drop a firmware .bin: the panel flashes itself over the network and restarts on the new build.

              -

              Drop a firmware .bin — the device flashes itself over the LAN and reboots on the new build.

              - - -
              - Linked build -
              - + + - Two steps -
              01

              Get a .bin: patternflow.work/update sends the latest release straight back to this page. Or bring your own — an arduino-cli export.

              -
              02

              Drop it below. Keep the device powered; it verifies, flashes, and reboots itself.

              - -
              This build requires arming first. On the device: hold K2 → NETWORK, then turn K4 → UPDATE.
              + -
              - Drop .bin here - Checking the panel… +
              + Drop a .bin here + Checking the panel…
              - + does the same; an explicit any-type accept is what reliably brings + up the file manager. The JS checks extension and size instead. --> +
              - + + +

              No file? patternflow.work/update sends the + latest release straight back to this page. Your own arduino-cli export works too. Keep the panel + powered: it checks, flashes and restarts by itself.

              + +
              -
              + Patternflow - Wi-Fi
              -

              Wi-Fi

              -
              +.s1 i+i,.s2 i+i+i,.s3 i+i+i+i{background:var(--ghost)} +.dym,.dym span+span{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.dym{margin-top:8px;padding:6px 6px 6px 12px} +#join{margin-top:12px}#join.ok{border-left-color:var(--ok)}#join.err{border-left-color:var(--bad)} +#join .big{display:inline-block;margin:6px 10px 6px 0;font:18px var(--mono);overflow-wrap:anywhere} +.seg{display:flex;gap:2px;max-width:360px;padding:2px;border:1px solid var(--rule);border-radius:9px;background:var(--cream)} +.seg label{flex:1;position:relative} +.seg input{position:absolute;opacity:0;margin:0} +.seg span{display:flex;align-items:center;justify-content:center;min-height:40px;border-radius:7px;color:var(--muted);font-weight:500;font-size:14px;cursor:pointer;transition:background .15s,color .15s} +.seg :checked+span{background:var(--panel);color:var(--ink);box-shadow:0 0 0 1px var(--rule),inset 0 -2px var(--led)} +.seg :focus-visible+span{outline:2px solid var(--led)} +.pf-offline #st,.pf-offline #list,.pf-offline #hsstate{opacity:.5} +@media(hover:hover){.btn.dan:hover{border-color:var(--bad)}summary:hover,.seg span:hover{color:var(--ink)}.scan button:hover .nm{text-decoration:underline}} + + +
              +

              Wi-Fi

              -
              - -

              Add a network

              -
              - - Network name + + + + +
              + - - +
              -
              - -
              - - -
              -
              -
              - - - - +
              + + +
              -
              - -

              Saved networks are tried in order starting from the boot slot, - wrapping around — so the device joins whichever one it can find, useful if it - moves between home, a studio and a venue. A new network is only stored; leave - switch now unticked and this page keeps working. Ticking it drops the - current connection immediately. On the panel's hotspot a network is tried at once, - and the panel shows the outcome on its screen as well as here: the address to open, - or why it could not join.

              + +

              Save stores it and this page keeps working; Switch now drops this connection to join it.

              +
              How saved networks are tried +

              In order from the boot slot, wrapping around, so the panel joins whichever it finds: home, studio, venue.

              Hotspot

              -

              -

              +

              -

              +

              The panel’s own network, patternflow-????: join it and open + http://192.168.4.1/, the same console anywhere. Phones stay on it though it has no internet. + Auto raises it about 15 s after the panel finds no Wi-Fi and drops it once it joins one.

              -
              - - -
              -
              - - +

              Mode

              +
              + + +
              -
              - + + +
              + +
              -
              -

              The panel as a network of its own: patternflow-????. - Join it from a phone or a laptop and open http://192.168.4.1/ — the - same console, anywhere. auto raises it about fifteen seconds after the panel - finds no network and drops it once one is joined; a network added above from the - hotspot is tried at once. The phone is told the network works, so it stays on it; - it has no internet through the panel.

              - -
              + + )HTML"; // GENERATED by firmware/toolchain/console_pages.py build — do not edit. -// gzip -9 of WIFI_INDEX_HTML (24284 bytes → 9034 bytes), served with Content-Encoding: gzip. +// gzip -9 of WIFI_INDEX_HTML (24463 bytes → 9067 bytes), served with Content-Encoding: gzip. static const uint8_t WIFI_INDEX_HTML_GZ[] PROGMEM = { - 0x1f,0x8b,0x08,0x00,0x00,0x00,0x00,0x00,0x02,0xff,0xb5,0x3c,0xdb,0x72,0xdb,0x48, - 0x76,0xef,0xfa,0x0a,0x88,0x93,0x08,0xc0,0x0a,0x84,0x44,0xc9,0x96,0x6d,0x40,0x90, - 0xe2,0xeb,0x8e,0x77,0xed,0xb1,0xcb,0xf2,0xd4,0x54,0x8d,0xe3,0xda,0x02,0x89,0x26, - 0xd1,0x16,0x08,0x60,0xd0,0xa0,0x68,0x2e,0xc5,0xaa,0x7c,0x44,0xbe,0x21,0xbf,0x90, - 0xf7,0x7c,0x4a,0xbe,0x24,0xe7,0x9c,0xee,0x06,0x1a,0x20,0xe9,0xf1,0x24,0x9b,0x99, - 0x2a,0x8b,0x6c,0xf4,0xe5,0xf4,0xb9,0xdf,0xc0,0xcb,0xc3,0xa4,0x98,0xd4,0xab,0x92, - 0x59,0x69,0x3d,0xcf,0xae,0x0e,0x2e,0xf1,0x8f,0x95,0xc5,0xf9,0x2c,0x1a,0xb0,0x7c, - 0x80,0x03,0x2c,0x4e,0xae,0x2e,0xe7,0xac,0x8e,0xad,0x49,0x1a,0x57,0x82,0xd5,0xd1, - 0x60,0x51,0x4f,0x87,0x8f,0xf1,0xa1,0x98,0x54,0xbc,0xac,0x2d,0x51,0x4d,0xa2,0xc1, - 0x49,0x39,0x1d,0x4e,0x8a,0x5c,0x14,0x19,0xf3,0xbf,0x88,0xeb,0x34,0x4a,0x4e,0xcf, - 0xc6,0xe7,0xe7,0x67,0x67,0x83,0xab,0xcb,0x13,0x39,0xf1,0x4a,0x2d,0xb8,0xe2,0x53, - 0xe7,0x70,0xc9,0xf3,0xa4,0x58,0xfa,0xef,0x5f,0xb9,0xce,0x74,0x91,0x4f,0x6a,0x5e, - 0xe4,0x8e,0xbb,0xbe,0x8b,0x2b,0xeb,0x97,0x48,0x3e,0xf3,0x7e,0x8e,0xec,0x93,0xb8, - 0xe4,0x27,0xa2,0x8e,0xeb,0x85,0xb0,0xbd,0x37,0xd1,0xa7,0xcf,0xde,0xdb,0xe8,0x8c, - 0x3d,0xf1,0x6e,0xbc,0x5f,0xbd,0xf7,0xd1,0x2f,0xb0,0x3c,0x5a,0xe3,0x63,0x16,0xd8, - 0x19,0xbf,0x63,0xb6,0x97,0xf0,0xaa,0x5e,0x05,0x87,0x23,0xef,0x2e,0xc8,0x17,0x59, - 0xe6,0xcd,0x58,0x1d,0xfc,0xd9,0xe3,0xf9,0x34,0xe3,0xb3,0xb4,0x0e,0x7e,0x5a,0xcc, - 0xc7,0xac,0xf2,0xd2,0x22,0x4b,0x82,0x77,0xe3,0x2f,0x6c,0x52,0x7b,0x22,0x5e,0x05, - 0x0d,0x04,0xb5,0x77,0xeb,0x31,0x77,0xcd,0x8e,0x8e,0x1c,0xe6,0xd7,0xec,0x6b,0xfd, - 0xbc,0xc8,0x6b,0x96,0xd7,0x51,0xed,0x6e,0xbc,0xf1,0x42,0x18,0x53,0xc7,0x5e,0xe9, - 0xae,0xf5,0x37,0x6b,0x0a,0xc0,0x8f,0xfd,0x84,0x8b,0x78,0x9c,0xb1,0x24,0x3a,0x1c, - 0x6d,0xcc,0x6f,0xa7,0x61,0xe9,0xd7,0x29,0xcb,0x9d,0xa9,0x37,0x75,0xc3,0x8a,0xd5, - 0x8b,0x2a,0xb7,0xca,0x8d,0x57,0x16,0x59,0xd6,0xee,0x28,0xbc,0xb9,0x37,0xf1,0x0a, - 0x89,0x85,0xda,0x9b,0x7a,0xb3,0x68,0x14,0x36,0x27,0x7c,0x80,0x13,0x66,0x47,0x47, - 0x87,0x53,0x80,0x6d,0x1a,0x8d,0xbc,0x49,0xc6,0xe2,0xea,0x23,0x9f,0xb3,0x62,0x51, - 0x3b,0xb5,0xeb,0x39,0xc2,0x9f,0xc4,0x59,0x76,0x2d,0x1c,0x37,0xf8,0xb3,0x23,0x5c, - 0x57,0x9d,0xa8,0x77,0x4f,0xdc,0xf5,0xc4,0x49,0x3c,0xc4,0x0a,0xdc,0xa5,0x19,0x66, - 0x38,0x4c,0xa8,0x62,0xee,0x46,0xad,0xf9,0xab,0xf7,0x57,0xd7,0xdd,0x34,0x27,0xff, - 0x15,0x4e,0x9e,0x46,0xa7,0x61,0xef,0xc4,0x10,0xa0,0x71,0xea,0x08,0x58,0x42,0x8f, - 0x7d,0xf0,0xe6,0xb0,0xae,0x38,0x3a,0x2a,0xfc,0x29,0xaf,0x44,0x1d,0x45,0x80,0x88, - 0x6b,0x58,0x1e,0x00,0xf0,0xea,0xda,0x40,0xae,0xa2,0x0c,0x0c,0x92,0xcf,0xb6,0x77, - 0xde,0x78,0xb0,0x69,0x3b,0x27,0x77,0xd7,0xf3,0x28,0x0f,0xa7,0xf7,0xf7,0xb0,0xd5, - 0xc6,0xcb,0x8b,0x65,0xf0,0x61,0x03,0x73,0x88,0x2d,0xda,0x69,0x13,0xb8,0xc9,0xd1, - 0xd1,0x1b,0xbf,0x5c,0x88,0x14,0xbe,0x84,0x37,0xd7,0xf0,0x75,0xe2,0xdc,0xb8,0xc1, - 0xaf,0xd7,0xbf,0xfa,0xb0,0x0a,0xe0,0xf8,0x35,0x7a,0xef,0x23,0xd2,0x9d,0x9f,0xbd, - 0xb7,0xde,0x0b,0xd8,0x6c,0x19,0xd7,0x93,0xf4,0xa6,0xb7,0xd3,0xdc,0x5d,0xbf,0xf7, - 0xe5,0xf6,0x00,0xf7,0xaf,0x3e,0x40,0xe3,0xbc,0x8d,0xde,0xc6,0x75,0xea,0xcf,0x39, - 0x3c,0xf6,0xde,0xba,0xb4,0x94,0xd7,0xaf,0x8a,0xea,0x05,0xbb,0xe3,0x13,0xd6,0x2e, - 0x06,0xfa,0x15,0x51,0x71,0x7f,0xbf,0xde,0x68,0x42,0xe7,0x6c,0x69,0xbd,0xaf,0x8a, - 0x39,0x17,0xac,0x25,0xc7,0xca,0xcb,0x25,0xa1,0xd3,0x16,0xa4,0x33,0x76,0xde,0x12, - 0x46,0xb8,0x6b,0x01,0xe4,0x76,0x0a,0x7f,0xbc,0xe0,0x59,0x72,0x74,0x24,0xe4,0x87, - 0x28,0x52,0x23,0x2e,0xa0,0x3f,0xf5,0x11,0x9d,0x8e,0xeb,0xad,0x90,0xe2,0x1b,0x37, - 0x34,0xa8,0x61,0xe0,0x58,0x4f,0x0b,0x73,0xe7,0x65,0x55,0x15,0x95,0x63,0xd7,0x72, - 0x92,0x8d,0x17,0x29,0x7c,0xf5,0xed,0xfe,0xfe,0x09,0x7b,0x00,0xbb,0x6c,0x36,0x2d, - 0xdb,0xfd,0xd9,0x59,0x20,0x4b,0xaa,0xab,0x4c,0x19,0xa0,0x8b,0x46,0x7a,0xdc,0x55, - 0xb9,0x6b,0x94,0xea,0xca,0x2f,0x6e,0xdd,0x3a,0xad,0x8a,0xa5,0x25,0x0f,0xaa,0x14, - 0x1e,0x1b,0xae,0x27,0xee,0x40,0xc9,0xba,0xae,0xe8,0x0f,0x10,0xa5,0x02,0x85,0x81, - 0x60,0x6e,0x0c,0x9e,0x7b,0x81,0xf7,0x87,0x1d,0xe1,0xdf,0x1b,0x90,0xf3,0x72,0x2a, - 0x69,0x14,0x89,0xf0,0x8d,0x3f,0x2d,0xaa,0x97,0x31,0x80,0xd1,0x1c,0x3e,0x05,0xfe, - 0x84,0x99,0x08,0xf8,0xc6,0x75,0xdc,0x46,0xdf,0x1c,0x48,0xc5,0x95,0xc7,0x73,0x16, - 0x0d,0xee,0x38,0x5b,0x96,0x45,0x55,0x0f,0xac,0x89,0x92,0xe9,0xc1,0x92,0x27,0x35, - 0x28,0x2a,0x22,0xe0,0x90,0xbe,0x80,0xa6,0xe0,0x35,0x8f,0xb3,0xa1,0x00,0x59,0x62, - 0xd1,0x08,0xb5,0x5c,0xcd,0xeb,0x8c,0x5d,0xbd,0x8f,0xeb,0x9a,0x55,0xa0,0x47,0xe0, - 0x6a,0x43,0xeb,0x17,0x3e,0x7c,0xc5,0x2f,0x4f,0xe4,0x23,0x50,0x84,0xf5,0x0a,0xff, - 0x9e,0xfc,0xc9,0x7a,0x11,0x57,0xb7,0x16,0xcf,0x45,0x5d,0x2d,0xe6,0x70,0x86,0x55, - 0x17,0xb7,0x2c,0x17,0xd6,0x7f,0xff,0xdb,0xbf,0x5b,0x82,0x31,0xab,0x94,0xbb,0x88, - 0xbf,0x81,0x86,0x63,0x5f,0xfd,0xd4,0x82,0xbb,0x58,0x80,0x49,0x2b,0x2f,0x6a,0xe6, - 0x5b,0x7f,0x3a,0x39,0x08,0xaa,0xa2,0xa8,0xd7,0xc3,0xe1,0xa4,0x62,0xf1,0x3c,0xf8, - 0xe1,0xf4,0xf9,0xe9,0xb3,0xd3,0x27,0xe1,0x70,0xc8,0xf3,0xdb,0xe0,0x87,0x97,0x2f, - 0x5e,0x3e,0x7a,0xf1,0x0c,0xbe,0xcd,0x17,0x35,0x4b,0x82,0x1f,0x1e,0x3f,0x7d,0x7c, - 0xf6,0xe8,0x0c,0xbe,0x4f,0x63,0x9e,0xd7,0xc1,0x0f,0x0f,0x9f,0x3e,0x7c,0xf8,0xe0, - 0x22,0x3c,0x18,0x0e,0xab,0x45,0xc6,0x82,0x1f,0xce,0x1e,0x9c,0x8d,0x46,0x8f,0x43, - 0xf9,0x75,0x28,0x8a,0x29,0xcc,0x19,0x3d,0x1b,0x3d,0x19,0x3d,0x80,0xb1,0x0c,0x77, - 0x78,0xf5,0xea,0xe1,0xf3,0xb3,0x97,0xf0,0xad,0x80,0xed,0x1f,0x3e,0x7a,0xf6,0xf8, - 0xd1,0x2b,0xf8,0x52,0xc6,0x39,0xcb,0x60,0xea,0xf9,0x68,0x34,0x3a,0xc5,0xed,0x44, - 0x9c,0x8b,0xc0,0x7e,0x0d,0x78,0xab,0x6c,0x6f,0xc1,0xe9,0xfb,0x50,0xb0,0x8a,0x4f, - 0x3d,0xb1,0x12,0x35,0x9b,0x0f,0x17,0xdc,0x6b,0x07,0x71,0xc9,0xbc,0xc8,0x8b,0xc0, - 0xfe,0x0b,0xab,0x9f,0x55,0x00,0x9c,0xb0,0xde,0xc2,0x77,0x5a,0x8b,0x0f,0x44,0x19, - 0x4f,0x98,0x77,0xf3,0x0a,0x07,0x87,0x1f,0xd8,0x6c,0x91,0xc5,0x95,0xf7,0x96,0xe5, - 0x59,0xe1,0x35,0x8f,0x37,0x07,0x7f,0x5a,0x8f,0x8b,0xaf,0x43,0xc1,0xff,0xce,0xf3, - 0x59,0x30,0x2e,0xaa,0x84,0x55,0x43,0x18,0xd9,0x1c,0x7c,0x4a,0x79,0x92,0xb0,0xfc, - 0xf3,0x1a,0x54,0x6c,0x99,0x81,0x02,0xcf,0x8b,0x9c,0x1d,0xf2,0x39,0xd2,0x37,0xce, - 0xeb,0xcd,0xc1,0xb8,0x48,0x56,0xeb,0x79,0x5c,0xcd,0x78,0x1e,0x9c,0x86,0xe3,0x78, - 0x72,0x3b,0xab,0x8a,0x45,0x9e,0x04,0x20,0x71,0x8e,0x42,0xae,0x1b,0x4e,0x8a,0xac, - 0xa8,0xd4,0x10,0xe0,0xd7,0x0d,0xa7,0xc0,0x19,0x80,0xcb,0x39,0xcf,0x56,0x6a,0x18, - 0xef,0xe4,0x86,0x07,0x19,0xcf,0xd9,0x30,0x65,0x64,0x43,0x46,0xfe,0xc3,0x70,0xb8, - 0x64,0xe3,0x5b,0x0e,0x73,0x71,0x81,0x98,0x03,0xc9,0x52,0x84,0x11,0xce,0x06,0xe6, - 0xe1,0xb1,0x60,0xc9,0xe6,0xc0,0x5f,0x56,0x71,0x09,0x40,0x7c,0x95,0xac,0x15,0x5c, - 0x3c,0x3e,0x2d,0xbf,0x86,0x1a,0x28,0x2b,0x5e,0xd4,0x45,0x58,0xc6,0x49,0x82,0x0b, - 0xcf,0xcf,0xca,0xaf,0xd6,0x19,0x3c,0xb7,0x2e,0x1e,0x94,0x70,0x41,0x34,0xbb,0xac, - 0x6a,0xae,0x37,0xcd,0xd8,0xd7,0x10,0x36,0x9e,0xe5,0x43,0x0e,0xc8,0x16,0xc1,0x84, - 0x21,0x29,0xc2,0x59,0x5c,0x06,0x8f,0x61,0x53,0xb5,0x0d,0x20,0xa7,0xae,0x8b,0x79, - 0x30,0x82,0xdd,0xc2,0x83,0x06,0x61,0x72,0x0c,0xf6,0x06,0xe3,0xcc,0x13,0x4b,0x5e, - 0x0c,0x39,0xc2,0x05,0x20,0x13,0xe0,0x36,0x09,0xdf,0x23,0x58,0xa4,0xae,0x88,0x1f, - 0xb7,0x90,0x06,0xfc,0xe2,0x86,0x6a,0xd3,0x2a,0x4e,0x38,0x68,0xcc,0x11,0xc1,0x3a, - 0x5a,0x4b,0x34,0xf0,0xbf,0xb3,0x60,0xf4,0x10,0x96,0xd2,0xd7,0xa5,0xdc,0xea,0xe2, - 0xf4,0xb4,0xb9,0x73,0x88,0xf7,0x08,0x46,0x70,0xaa,0x58,0x8c,0xd7,0xdb,0xc8,0x46, - 0xd2,0x2b,0x22,0xc8,0xcd,0x60,0xff,0x0e,0x91,0x88,0xcd,0xdd,0x10,0xf5,0xc6,0x90, - 0xd0,0x11,0x54,0x78,0xc8,0xe6,0x40,0x30,0xd2,0x04,0x8a,0xe4,0x43,0x34,0x31,0x67, - 0x17,0x04,0xdc,0xd9,0xba,0xb7,0x5f,0x1f,0xb8,0x8c,0xa1,0x48,0x0e,0x91,0xe5,0x90, - 0x12,0xfe,0xe9,0x13,0x36,0x97,0x27,0xd4,0x15,0x10,0x1f,0x04,0x74,0x1e,0x2c,0xca, - 0x92,0x55,0x13,0x20,0x6b,0x78,0x60,0x82,0x43,0x52,0xe8,0xb6,0x24,0x3d,0xb5,0x1e, - 0xe3,0x99,0x8b,0x6c,0x9d,0x71,0x01,0x67,0xa2,0x56,0x20,0xd6,0x6c,0x31,0xa0,0x09, - 0x7e,0xaa,0x31,0x89,0xa0,0xee,0xa2,0x0d,0x49,0x2b,0x10,0x28,0xe3,0xdf,0xc5,0x05, - 0xa3,0xd3,0x96,0x0d,0x82,0x27,0xc8,0x4c,0xdf,0xc7,0x02,0xf2,0x18,0x13,0xe7,0xe7, - 0x78,0x85,0x8c,0x5b,0x7e,0xfe,0x7f,0xa0,0x10,0x98,0x49,0xc5,0xf5,0xc8,0xd4,0x40, - 0xf0,0x7c,0xbe,0x96,0xc4,0x37,0x1e,0x9d,0x86,0xc5,0x1d,0xab,0x50,0xa3,0x06,0x52, - 0x9e,0x25,0xda,0x9b,0x41,0x96,0x65,0xbc,0x14,0x5c,0x84,0xcb,0x14,0xee,0x4b,0x14, - 0x42,0x74,0xa2,0x58,0x85,0x07,0xdf,0x01,0xdb,0x19,0x9d,0x5c,0xc7,0xb3,0xef,0xb9, - 0x08,0xe2,0x6f,0x8b,0x15,0x2e,0x80,0x15,0x0e,0xf6,0xf2,0x82,0x46,0x37,0xe2,0xf5, - 0x02,0x25,0x86,0xb0,0xbd,0x85,0x66,0xb0,0x88,0x0d,0x25,0x94,0xdc,0x9c,0xf5,0xb0, - 0x06,0x53,0x40,0x67,0x2d,0x80,0x4a,0x12,0xe9,0x01,0xcf,0x53,0xd0,0xa3,0x75,0x38, - 0x59,0x54,0x02,0x66,0x95,0x05,0x27,0x4a,0x97,0x85,0xe0,0xc8,0xe7,0x41,0xc5,0xb2, - 0xb8,0x06,0x87,0x57,0xaf,0xf2,0x13,0x96,0xad,0xbf,0x41,0x16,0xc5,0xa9,0x86,0x4c, - 0x13,0x5b,0x1e,0xec,0x01,0x99,0x94,0x43,0xb8,0x0d,0xb3,0xbe,0x31,0x6a,0x2b,0x62, - 0xf5,0xf6,0xf4,0x20,0x45,0xba,0xad,0xd5,0x1a,0xf3,0x6c,0xd2,0x1b,0xfd,0x81,0xcd, - 0x01,0x22,0xb3,0x55,0xd1,0x9a,0xc7,0x67,0x15,0x4f,0x5a,0x86,0x6e,0x95,0xe7,0xf9, - 0x63,0xe2,0xa3,0x2c,0x1e,0xb3,0xcc,0xf3,0xb3,0x71,0xd6,0x48,0xc5,0x38,0x2b,0x26, - 0xb7,0x3d,0xb2,0x87,0xdf,0x96,0x52,0x62,0x71,0x9e,0x97,0x0b,0xad,0xfa,0x46,0xa7, - 0xa7,0xff,0x1c,0x76,0x10,0xff,0x1d,0x1c,0x73,0x6e,0x20,0x04,0x14,0xa6,0xf5,0x84, - 0x24,0xae,0xaf,0x35,0xc9,0x94,0xee,0x30,0x35,0x7f,0x0c,0xf3,0x0a,0xdc,0x60,0x5a, - 0x4c,0x16,0x62,0x0d,0xae,0x1a,0x1a,0x24,0x49,0xc3,0x3d,0x18,0x07,0xce,0x8f,0x49, - 0x27,0x8a,0xae,0xfe,0xd0,0x26,0x63,0x87,0x1e,0x31,0x95,0xa7,0x41,0xdc,0xb2,0xe2, - 0xf0,0x64,0xb5,0xde,0xba,0x1a,0xdd,0xc3,0x3c,0x57,0x99,0xd5,0x3d,0x57,0xa3,0xe9, - 0x07,0xfb,0x99,0x0a,0x71,0x38,0xba,0xd0,0x1a,0xda,0x50,0x46,0x5d,0x38,0x34,0xa3, - 0x7d,0xcb,0x3c,0xed,0x40,0xc6,0x24,0x65,0x93,0xdb,0x6d,0x54,0x5c,0xec,0x46,0xc5, - 0xef,0xb2,0x93,0xde,0xd1,0x32,0xd9,0x08,0x4d,0xfa,0x06,0xbd,0xc0,0x0f,0x4c,0x2c, - 0xb2,0xda,0x42,0x22,0x09,0xcf,0x9a,0xf2,0x0c,0x80,0xb0,0xc6,0x2b,0xeb,0xfd,0x2b, - 0x1f,0xc2,0x4d,0x08,0x08,0xbe,0xa2,0x67,0x48,0x8e,0x1f,0x3a,0xa3,0x55,0x91,0xc1, - 0xe7,0xb8,0xb6,0x26,0xf1,0x02,0x5c,0x07,0x1c,0x9f,0x93,0x33,0xe8,0xcf,0xc5,0xcc, - 0xb4,0x69,0x24,0x14,0xdf,0xa9,0x95,0x51,0xcf,0x6a,0x8f,0xe5,0x62,0xdf,0x1d,0x60, - 0x7f,0xf0,0xad,0x41,0xff,0xac,0xfb,0xea,0x48,0x3f,0x62,0x55,0xb5,0xde,0x46,0xe7, - 0x0f,0x5f,0x40,0x23,0x75,0x20,0x33,0x29,0x89,0x60,0x5a,0x34,0xf2,0x07,0xf5,0xcb, - 0x41,0x4f,0xbc,0xfa,0x6e,0x57,0xab,0x0f,0x1e,0x4a,0xbb,0x42,0x70,0x40,0xcc,0xb1, - 0x4b,0xef,0x90,0x52,0x95,0x13,0xf0,0x12,0x7b,0x59,0x83,0xa6,0x58,0xbf,0xe3,0x8f, - 0xb4,0x0e,0x83,0x5e,0x10,0xaf,0xfb,0x32,0xad,0x9f,0xf8,0x63,0x3e,0x6b,0x38,0x8d, - 0xe7,0x74,0x09,0xa9,0xa5,0x94,0x0e,0xba,0x40,0xf4,0x9c,0x4a,0xab,0x61,0x9d,0xee, - 0x25,0xa8,0x81,0x0d,0xbc,0x6d,0xb8,0x84,0x1b,0x0c,0xc7,0x20,0x63,0xb7,0x01,0xfd, - 0x0b,0x2e,0x50,0x86,0xa6,0x15,0x42,0x87,0xf5,0x37,0x18,0x56,0x9b,0x64,0x79,0xf6, - 0x63,0x3c,0x12,0x0e,0xed,0xa2,0xf6,0xc1,0x43,0x54,0xc8,0xb0,0x51,0x65,0x12,0xf5, - 0xdc,0x20,0x6a,0x4b,0xe5,0xdf,0x71,0x5c,0xdc,0xef,0xb2,0xcd,0xbb,0xfd,0x86,0xcd, - 0x41,0x17,0xab,0x0d,0x9f,0x82,0x11,0xf7,0xc7,0x18,0x18,0xed,0xa0,0xa2,0xd2,0xef, - 0xbb,0xd6,0x09,0x96,0x81,0x63,0x68,0x2a,0xf9,0x9e,0x45,0xf9,0x5f,0x29,0xfd,0x83, - 0xbe,0xd6,0xff,0xc7,0x28,0xfd,0x83,0x1d,0x5a,0x5f,0xa9,0x3e,0x70,0x6f,0x8b,0x3c, - 0x41,0x25,0xdc,0x03,0x65,0xfb,0x84,0x7f,0x90,0x85,0xd7,0xca,0x78,0x1b,0x84,0x3f, - 0x6c,0xe8,0xfd,0x59,0x5a,0x88,0xfa,0x7f,0xe3,0x4c,0xfe,0x83,0xbd,0x96,0x73,0x25, - 0x76,0x1a,0xa4,0x3f,0x7e,0x93,0xaa,0x58,0xee,0x34,0x21,0xf2,0x91,0xb2,0x05,0xdb, - 0x7e,0x2e,0xd9,0x84,0x5f,0x50,0xc1,0xa3,0xc6,0x4f,0x8b,0x5a,0x94,0x45,0x6d,0x0b, - 0xcc,0xa8,0xe6,0xc0,0x28,0x96,0x98,0xc4,0xb9,0x35,0xc5,0x0b,0x7a,0x96,0x00,0x6b, - 0x90,0xcf,0x98,0xa8,0x2d,0x4a,0xa1,0x05,0x56,0x6c,0xe1,0xd6,0x68,0x2d,0xe2,0x52, - 0x1a,0x05,0x9a,0x0e,0x71,0x41,0x13,0x4b,0x6c,0xd4,0x98,0xf6,0x20,0x25,0x00,0xdf, - 0x1d,0x36,0x98,0x1e,0xb9,0xde,0xf3,0x71,0x13,0x42,0xf4,0x90,0xaf,0x70,0x7f,0xba, - 0xcd,0x7a,0x46,0x54,0x96,0xb1,0x69,0xdd,0x05,0x4a,0xe2,0xda,0xc2,0x38,0x60,0x07, - 0x5e,0x71,0x22,0x6c,0x5f,0x3b,0x3e,0xa8,0x34,0x17,0xee,0x16,0xe4,0x75,0x3a,0x9c, - 0xa4,0x3c,0x4b,0x9c,0xfc,0xf8,0x91,0xdb,0x89,0xf3,0x61,0xfe,0x38,0xae,0xc4,0xfe, - 0xb0,0x08,0x07,0x86,0x2c,0x97,0x7e,0xe4,0x59,0x1b,0xd5,0x2a,0xda,0xe3,0x62,0x8b, - 0x2b,0xa5,0x70,0xde,0x3e,0x7e,0x00,0xea,0x61,0x4b,0x92,0x75,0x98,0x2c,0x17,0x1d, - 0xf3,0x75,0xaa,0x0d,0xc1,0x3f,0x6f,0x9a,0xc1,0x76,0xf8,0x71,0x77,0xb8,0x7d,0x80, - 0xca,0x07,0x2f,0x3a,0xb2,0x78,0x40,0x84,0x95,0xb7,0xf3,0x7c,0x71,0x66,0x99,0xb7, - 0x1d,0xe6,0xc7,0x67,0x2e,0x8c,0x9e,0xf7,0x47,0xcf,0x71,0xf4,0x01,0xc0,0xbd,0x05, - 0x62,0x6b,0xcc,0x8b,0x8a,0x79,0xe0,0x96,0xf0,0x52,0x73,0xc2,0x5e,0xe2,0xb5,0x61, - 0xe8,0x77,0x18,0x1f,0x92,0x4b,0xa2,0x6e,0x02,0x4a,0xa0,0x8a,0x29,0x02,0x81,0x3d, - 0x59,0x85,0x46,0x44,0x1d,0x6c,0xda,0x8d,0x8b,0xfd,0x2e,0x13,0x2f,0xbb,0x74,0x23, - 0x5a,0x61,0x44,0x17,0x50,0x58,0xb7,0x87,0x4d,0x1f,0x68,0x7f,0xa2,0x77,0x48,0xa3, - 0x89,0x1f,0xca,0x80,0x64,0x9f,0x82,0xdd,0x91,0xba,0x38,0xfb,0x6e,0x4f,0x4a,0xc1, - 0xad,0x71,0xba,0xcd,0xbe,0x20,0xdc,0x37,0x73,0x60,0x5c,0xed,0xcb,0x09,0xeb,0x96, - 0xb1,0x12,0x65,0x9d,0x57,0x56,0x56,0x14,0xb7,0xa1,0x55,0x80,0xbf,0x00,0x32,0xbc, - 0x98,0xa4,0x20,0xec,0x15,0x63,0xb9,0xd4,0x04,0xbc,0xb6,0x62,0xb0,0xe4,0x16,0xd0, - 0x68,0x29,0x48,0xb8,0xff,0x65,0xce,0x12,0x1e,0x3b,0x2a,0xe0,0x0b,0x26,0x05,0xd6, - 0x5c,0xdc,0x75,0x47,0xbe,0x1b,0x37,0xeb,0x4c,0x0a,0x29,0x66,0x71,0x20,0x00,0x0b, - 0xe2,0x29,0x2c,0xf1,0x88,0x1a,0xcd,0x17,0x03,0x72,0x3d,0xa6,0x9c,0x14,0xa9,0x02, - 0xe5,0xd8,0x5a,0xa5,0x44,0x83,0xc1,0x00,0x70,0xaa,0x83,0xcc,0x78,0x0c,0x38,0x04, - 0xd2,0x85,0x3c,0xc7,0x7c,0xfc,0x10,0x91,0x61,0x0d,0x51,0xdd,0x6d,0x0e,0xb0,0x50, - 0x44,0x9e,0xe3,0x74,0x8a,0x4c,0x60,0xfd,0x20,0x6a,0x6f,0x6b,0x0c,0xb3,0x20,0xdb, - 0xa3,0xa9,0xa0,0x8a,0xcd,0xba,0xc0,0x30,0xbb,0x5e,0x05,0xe4,0x7c,0x5c,0x9e,0xc8, - 0x1c,0xea,0xe5,0x89,0xac,0x38,0x61,0x02,0xef,0xea,0x32,0xe1,0x77,0xd6,0x24,0x8b, - 0x85,0x88,0x06,0xc8,0x1e,0xba,0x20,0xc5,0xaa,0xab,0x4b,0x08,0xd2,0x73,0xfd,0x2c, - 0x29,0x6a,0xaa,0x32,0xc1,0xd0,0xd5,0x65,0x3a,0xba,0x52,0xd9,0x59,0xf8,0xd4,0x99, - 0x26,0x16,0xe3,0x81,0xc5,0x13,0xf8,0x00,0xd3,0x87,0x7a,0xfe,0x89,0xda,0xf1,0xe0, - 0xe0,0x52,0x65,0x91,0xe4,0x9c,0xf8,0x8e,0x25,0x03,0x4b,0x26,0x26,0xae,0x0e,0x2c, - 0xeb,0x32,0x3d,0xbb,0xba,0xc1,0x41,0x70,0xdf,0x6b,0x70,0xc5,0x6e,0x05,0x2c,0x3d, - 0xa3,0x27,0x8b,0x8c,0x96,0xe0,0x75,0x11,0x8e,0x45,0x46,0xa3,0xa5,0x3e,0x17,0x1d, - 0x34,0x79,0xf0,0x04,0xef,0x70,0x79,0x52,0xd2,0x73,0xe3,0x72,0xe0,0x6b,0x0f,0x9a, - 0x2d,0xf0,0x0b,0x4c,0x82,0xc7,0x57,0x88,0x16,0x09,0x53,0x1f,0x3c,0x74,0x86,0xe0, - 0x7b,0x1f,0xc0,0x67,0x30,0xac,0xe1,0x6b,0xc0,0xa3,0xa0,0x19,0x33,0xcd,0x72,0xd9, - 0xe0,0xea,0x63,0xb5,0x92,0xd6,0x05,0xf9,0xb2,0x2c,0x96,0x20,0x15,0x8b,0xf2,0xf2, - 0x84,0xe6,0xd1,0x0a,0xe9,0x37,0x35,0x07,0x11,0x72,0x69,0xa8,0x0f,0xb8,0x0a,0x31, - 0x07,0x16,0x51,0x0f,0x2e,0xd2,0x8d,0x55,0x06,0x38,0x1f,0x56,0x48,0x06,0x6c,0xe8, - 0xa0,0x5d,0x89,0x81,0x85,0xb5,0x47,0x38,0x83,0x1e,0x0f,0xda,0x8b,0x01,0x9a,0x07, - 0x84,0x6c,0x0b,0xbf,0x5a,0x65,0xc5,0xa6,0xac,0x62,0xf9,0x84,0x5d,0x9e,0xc8,0xb9, - 0x7f,0x7c,0xdf,0x8a,0xc9,0x9b,0x7c,0xa0,0xbf,0xe6,0x3e,0x0a,0xd5,0x7b,0x28,0x82, - 0xb3,0x4d,0x8a,0x6c,0x11,0xf6,0xea,0xa6,0x06,0x81,0x4b,0x10,0x95,0x28,0xd3,0xb2, - 0x9a,0xe0,0x5b,0xf2,0x1c,0xb4,0xdb,0x24,0x6e,0x14,0xea,0x81,0xc4,0xb1,0x5c,0x93, - 0x47,0x51,0xe0,0x68,0x9e,0xc4,0x22,0x0d,0x61,0x5f,0x9a,0xb2,0xa8,0x2a,0x2c,0x1b, - 0x08,0x26,0x04,0x91,0x5a,0x60,0x65,0xc0,0x12,0x4b,0x5e,0x43,0xb8,0x99,0x58,0x58, - 0xe4,0xf4,0x89,0x81,0xf6,0x72,0x06,0x28,0x87,0x81,0x66,0x87,0xa7,0x49,0x02,0x7a, - 0xa7,0xcf,0x0f,0x98,0x79,0xa1,0xa9,0x53,0x4d,0x1f,0xbc,0x38,0x31,0x3d,0x68,0x99, - 0x71,0xf1,0xd5,0xe4,0x2a,0x7a,0xde,0xdc,0x38,0x1b,0x67,0x83,0xab,0x9f,0x14,0xff, - 0x13,0xc4,0xe4,0xea,0x4a,0xe7,0x45,0xdf,0x05,0x3d,0x15,0x40,0x07,0x53,0x8c,0x4e, - 0x3b,0x80,0x88,0x68,0x2a,0xc1,0x19,0x83,0xe6,0xb4,0x56,0x5e,0x76,0x10,0x14,0x35, - 0xd9,0x2e,0x5a,0xca,0x71,0x05,0x63,0x8f,0x25,0x34,0x95,0xe4,0xad,0x9a,0x8d,0x0d, - 0x19,0x00,0xcc,0x26,0xcd,0x25,0xa8,0x38,0x64,0xb0,0x3e,0x4d,0x26,0x3f,0x4e,0x82, - 0x88,0x73,0x55,0x01,0x49,0x7e,0x86,0x00,0x22,0x63,0xf9,0xac,0x4e,0xa3,0xc1,0xf9, - 0xd9,0xc0,0xaa,0xd8,0x6f,0x0b,0x5e,0x11,0x65,0x44,0x8d,0x2c,0xc8,0xf2,0x81,0xda, - 0xc6,0xa2,0x14,0xff,0xa4,0x98,0x97,0x19,0xab,0x61,0x3d,0x28,0xc0,0x81,0x1c,0x8a, - 0x4b,0x5e,0x83,0xa5,0xfb,0x3b,0x43,0x16,0xca,0x99,0x1a,0x2d,0x80,0xf4,0x93,0x5a, - 0xcd,0x13,0x25,0xcb,0x32,0xca,0x30,0x00,0x99,0xe2,0x4c,0xb0,0x41,0x03,0x5d,0x12, - 0xe3,0x62,0xa1,0x00,0xc4,0x03,0x91,0x37,0xd5,0x60,0x3b,0xab,0xe5,0x65,0xb4,0x01, - 0x12,0x71,0xc9,0x6a,0xde,0xe0,0xed,0x05,0x58,0xc7,0x55,0xb1,0xb0,0xe6,0x0c,0x34, - 0xe4,0x51,0x16,0xff,0xb6,0x28,0xc2,0xcb,0xb1,0x9e,0x46,0x9b,0x8e,0xaf,0x8e,0x2a, - 0x1a,0xbf,0x6e,0xae,0xa4,0x29,0xb4,0x4d,0x14,0x58,0xb4,0x40,0x30,0xe1,0x1f,0x8b, - 0xb7,0x02,0xf6,0xcd,0x05,0x68,0x27,0x07,0x57,0x64,0x2d,0xc1,0x09,0x6d,0xa5,0xdb, - 0xa4,0xe2,0xef,0x13,0xb4,0x84,0x5b,0x0e,0xae,0xde,0xc3,0xbf,0x18,0x21,0xf7,0x89, - 0x69,0x20,0x02,0xac,0x6c,0x83,0xc6,0x0e,0x99,0x69,0x07,0x45,0x66,0xf9,0x59,0x82, - 0x5b,0xaa,0x3d,0x3b,0x64,0xbf,0x38,0x6f,0x29,0xfc,0xff,0x44,0xe3,0x2d,0x49,0x20, - 0x63,0xbd,0x4b,0x14,0xca,0x25,0x0c,0xc7,0x15,0x8f,0x87,0xda,0xf9,0xd0,0x37,0xa0, - 0x41,0xd0,0x9c,0x42,0xb0,0xa4,0xd9,0x5f,0xa4,0xc5,0xb2,0x2b,0x30,0x5d,0x0c,0x77, - 0x91,0xdd,0xd7,0xf2,0x7b,0xc4,0x54,0x25,0xe6,0x34,0x78,0x60,0x66,0xe7,0xbc,0x1e, - 0x68,0x65,0x34,0xae,0x73,0xa9,0xcb,0xb7,0xce,0x35,0x6d,0x33,0xa1,0x41,0xae,0xc9, - 0x8b,0x25,0xea,0xa0,0x2d,0x42,0xc9,0xdd,0x69,0x22,0xe9,0x28,0x35,0x17,0x18,0xd5, - 0xe0,0x05,0x1c,0xe8,0x1a,0xa3,0xe0,0x14,0xae,0x4d,0xfa,0x13,0x54,0xe9,0xb2,0xcf, - 0x1d,0xd2,0x09,0xe8,0x5e,0xfe,0xf2,0x04,0x35,0xe4,0x5e,0x8b,0xd0,0xb3,0x06,0x5a, - 0x77,0xa2,0x9a,0x37,0x94,0xd2,0x3e,0x63,0xd1,0xf1,0x20,0xd0,0xfb,0xb3,0xea,0x8a, - 0xc3,0x10,0xd8,0x08,0xf2,0x4f,0x01,0xf8,0xb8,0xaa,0xc1,0xbd,0xb3,0xa6,0x55,0x31, - 0x27,0x15,0x4b,0x86,0x44,0x64,0x45,0xed,0xc1,0x86,0xe8,0x07,0x95,0xf8,0x38,0xae, - 0x3a,0x3a,0x57,0x14,0x86,0xf9,0x21,0x9b,0x23,0xac,0x65,0xca,0x01,0x5d,0x18,0x77, - 0x01,0x07,0x82,0x50,0x5a,0x14,0x67,0x72,0x0c,0x33,0x41,0x4a,0xa7,0xe8,0xb6,0x4c, - 0x61,0x18,0x76,0x9d,0x43,0x74,0x26,0xac,0x31,0x80,0x85,0xbe,0x69,0x5a,0xcc,0x99, - 0x07,0x86,0x43,0xd4,0x8b,0x84,0x17,0x56,0x9c,0xa3,0x15,0xb9,0x63,0xf9,0x02,0xec, - 0xda,0x53,0x6a,0x67,0xd0,0x36,0x0c,0xec,0x53,0x91,0x67,0x2b,0x98,0x89,0x36,0x30, - 0xb4,0x32,0x06,0xf7,0xc3,0x6b,0xb3,0x79,0x07,0xe9,0xf0,0xd5,0x5a,0xe4,0x35,0x9f, - 0xdc,0xc2,0x55,0x71,0xbf,0x3a,0x85,0xa5,0x65,0x3c,0x63,0xe4,0x2b,0x03,0xa8,0xb0, - 0x1b,0xdc,0xca,0xb7,0x3e,0xc2,0x1c,0xbc,0x1e,0x40,0x9b,0x54,0x45,0x49,0x46,0x06, - 0x36,0xd4,0x86,0x11,0xb8,0x3c,0xd7,0xc6,0x6e,0x4e,0xbe,0x72,0xcd,0xb2,0x95,0x6f, - 0xbd,0xcb,0x5b,0x6b,0x04,0x91,0xb5,0x8a,0xb1,0x5b,0xe3,0x87,0x90,0x4a,0x44,0xc7, - 0xe8,0xf5,0x4c,0x18,0xe2,0x52,0x02,0xa2,0x6d,0x18,0xca,0x86,0xb4,0x69,0xc5,0xa2, - 0x06,0x91,0x66,0x68,0xd2,0x79,0x2d,0xb4,0xc7,0x1e,0x03,0x94,0x20,0xb0,0xf8,0x37, - 0x05,0x6f,0x24,0xa0,0xa9,0xc0,0xe0,0x28,0x62,0x68,0xe7,0x8b,0x92,0xe5,0xb8,0x6b, - 0x51,0x01,0xe2,0x57,0x84,0xee,0x62,0x91,0x25,0x64,0xc0,0x29,0x0b,0xb9,0xd7,0x6e, - 0x6b,0x73,0xfd,0xa3,0x84,0xba,0x31,0xd4,0x3b,0x3c,0x48,0xe5,0x3c,0x93,0xff,0x5a, - 0x76,0xad,0x79,0x2a,0x4c,0x7b,0xbe,0x4b,0x51,0xa6,0x62,0x5e,0x24,0xb0,0xf6,0x2d, - 0xfc,0xdb,0x17,0x04,0xc3,0xe5,0xd3,0xd3,0x5a,0x01,0x2c,0x4a,0xc2,0xf8,0x5d,0x9c, - 0x2d,0x40,0xae,0x50,0x93,0x0d,0xae,0xf0,0xdf,0x86,0xfb,0x16,0x25,0xdc,0x99,0xe5, - 0xc4,0x6b,0x2d,0x46,0xd3,0x18,0xdd,0x17,0xdd,0x2c,0x21,0x37,0xd9,0xbf,0x6b,0xb6, - 0x8c,0x57,0xa0,0x63,0xe4,0xdf,0xdf,0x9d,0x8e,0x1a,0xf4,0x0a,0xfe,0xe9,0x4f,0x34, - 0x3d,0xd5,0xef,0xb2,0x1e,0xa9,0xf8,0xb6,0xfd,0x68,0xad,0x84,0x9a,0xa9,0x14,0x11, - 0x06,0xc4,0x7b,0xed,0xc2,0xff,0x83,0x55,0xf8,0x87,0xeb,0x66,0x60,0xa5,0xc6,0xcf, - 0xde,0xe3,0x44,0xfd,0x8e,0x26,0x04,0x46,0xf9,0xa6,0x67,0xfc,0xb1,0xe1,0x04,0x60, - 0x84,0x56,0x14,0x8b,0x29,0x89,0x55,0xb1,0xcc,0x03,0x4b,0x79,0x1c,0x00,0x0b,0x79, - 0x65,0x65,0xdb,0x65,0x33,0xbc,0x86,0xff,0xd0,0x07,0xf1,0x61,0xe7,0xbf,0xa0,0x0f, - 0x0d,0x12,0x45,0x3a,0x31,0xb6,0xca,0x14,0x15,0x1a,0x08,0x5a,0x6c,0x65,0x71,0x09, - 0xd1,0x06,0x89,0x32,0x0a,0xa0,0x75,0x39,0x01,0xde,0xbd,0x4a,0xeb,0xba,0x0c,0x4e, - 0x4e,0x46,0x4f,0xce,0xfc,0xd1,0xc5,0x63,0xff,0x81,0x3f,0x3a,0xb9,0x3c,0xa1,0x27, - 0xad,0x93,0x4a,0x7a,0x45,0x80,0xcd,0xb7,0x54,0xdf,0x22,0xa8,0xbc,0x7c,0xb5,0x44, - 0xd9,0xf6,0x49,0x81,0x21,0x6d,0xa4,0xea,0xaa,0x62,0x0e,0x1e,0x39,0x02,0x10,0x8f, - 0x41,0x39,0x80,0x0e,0x85,0xa8,0x19,0x0e,0x93,0x11,0x07,0xdc,0x0d,0x83,0xe8,0x96, - 0xf1,0x61,0x5f,0xd4,0xb2,0xc4,0xfc,0xfa,0xd2,0x08,0xa0,0x54,0x68,0x5c,0x2a,0x20, - 0xa9,0x93,0x05,0xa9,0x06,0x54,0x9d,0x2d,0x7e,0x40,0xa7,0xa0,0x9e,0x1a,0x83,0x4e, - 0x6e,0x8c,0x00,0x6c,0xa9,0x75,0x5a,0x5f,0x93,0x81,0xce,0xc4,0x83,0x53,0xb5,0x5f, - 0x5d,0x64,0x52,0xab,0xe9,0xed,0xc8,0xd2,0x78,0x68,0x20,0xe0,0x64,0xd0,0x1e,0x2b, - 0x21,0x15,0x1b,0xc6,0x1c,0x30,0xa2,0x84,0x94,0xf2,0x0d,0xb0,0xc4,0xc2,0xee,0xad, - 0xc5,0x2c,0x6d,0x6f,0xb3,0x43,0x6f,0xc9,0x52,0xc1,0xd5,0x65,0x6c,0xa5,0x10,0x9a, - 0x45,0x83,0x93,0x01,0xe8,0x2e,0xf4,0xa3,0x63,0xc4,0x2f,0x98,0xc0,0xa2,0x0e,0xad, - 0xf6,0xa9,0x6c,0x01,0xc3,0x48,0x09,0xff,0xe2,0x2c,0x64,0x2c,0xda,0xe2,0x40,0x07, - 0xba,0xba,0x65,0xab,0xe9,0x00,0xfb,0x27,0x87,0x37,0x6d,0x67,0x49,0x31,0xa1,0x76, - 0x2a,0x7f,0xc6,0xea,0x97,0x19,0xc3,0x8f,0xcf,0x56,0xaf,0x13,0x98,0xb1,0x69,0x17, - 0x30,0x31,0x71,0xea,0x66,0xc9,0x0d,0xe0,0x28,0x9f,0xc1,0x80,0x5f,0xb1,0x32,0x8b, - 0x27,0xcc,0x39,0xf9,0x74,0x74,0x79,0x35,0xf8,0x7c,0x32,0xf3,0xcc,0x96,0x41,0x40, - 0x82,0x5a,0xb1,0xb6,0x8f,0xec,0xc0,0x3e,0x8a,0xe7,0x65,0x68,0x7b,0xf6,0x25,0x7e, - 0xce,0x6a,0xfc,0x78,0x85,0x1f,0x67,0xf4,0x71,0x80,0x1f,0xc1,0x23,0x86,0x2f,0x9b, - 0x4f,0x93,0xcf,0x9b,0xce,0xf9,0xb9,0x79,0x3e,0x7c,0x03,0x76,0xfb,0xf9,0xc3,0xeb, - 0xe7,0xa0,0x06,0x80,0x34,0x39,0x35,0x36,0xb6,0xb3,0xbf,0x60,0xcb,0x9c,0x9a,0xdb, - 0x34,0xc0,0xb5,0x8f,0x4b,0xf0,0xf7,0x9c,0x85,0x87,0x69,0x90,0xad,0xe6,0xbb,0xf5, - 0x9c,0xd5,0x69,0x91,0x04,0xf6,0xfb,0x77,0x37,0x1f,0x6d,0x34,0x39,0x32,0x85,0x21, - 0x82,0xb5,0xad,0xfa,0x55,0x87,0x1f,0x41,0xdc,0x01,0x56,0xf0,0x15,0x32,0x3e,0xa1, - 0x8c,0xdd,0xc9,0xd7,0xe1,0x72,0xb9,0x1c,0xa2,0x40,0x0f,0x17,0x55,0x26,0xe1,0x4b, - 0xec,0x0d,0x9d,0x11,0xe0,0x3f,0xba,0xfd,0xf3,0x8b,0x09,0x88,0x88,0x57,0xd8,0x15, - 0xeb,0xae,0x65,0xed,0x12,0x3f,0xdf,0xdf,0xdb,0xc5,0xad,0xed,0xfd,0x93,0x63,0x83, - 0xf8,0x63,0x07,0x61,0x67,0xf6,0x8f,0x62,0xff,0x7c,0x52,0x18,0xdd,0x15,0xe3,0x6f, - 0x1e,0xa0,0xa2,0x6f,0x5a,0x72,0x72,0x02,0x56,0x1e,0x7c,0x8d,0x62,0x51,0xa1,0xee, - 0x90,0x0d,0x86,0x02,0x7c,0x9a,0xaa,0x5a,0x19,0xbe,0x8f,0x4d,0xce,0x44,0x22,0xfc, - 0xf6,0x0c,0x30,0xc7,0xd8,0xe7,0xaa,0x29,0x73,0x74,0xc4,0x7c,0xf2,0xf5,0xa3,0xc8, - 0xa6,0x4d,0x6c,0x1c,0x99,0x83,0x19,0x07,0x7f,0x04,0xce,0x9e,0xc6,0x3c,0x43,0xcc, - 0x1c,0xc8,0x23,0x77,0x3a,0x16,0x68,0xe9,0x56,0x14,0x31,0x00,0xfb,0x80,0xf2,0x40, - 0xf7,0x49,0x55,0x74,0x69,0xaa,0xa7,0x1d,0x32,0xe5,0x20,0xe0,0x56,0xe0,0x7d,0x51, - 0x30,0x1f,0xaf,0x2c,0xd2,0x32,0xca,0x09,0x52,0x82,0x1b,0x5a,0x46,0xb3,0x33,0xce, - 0xf5,0x0f,0xb0,0x3d,0xf4,0x8e,0xc7,0x3f,0x8a,0x88,0x0c,0x81,0xa7,0x9c,0x1f,0xf0, - 0xe7,0xeb,0x6a,0xc1,0x3c,0xb0,0x3d,0xd1,0xc3,0x90,0x66,0x61,0x18,0xf8,0x13,0x00, - 0x23,0xb0,0x45,0x1a,0xbf,0xfc,0x95,0xad,0x22,0xdb,0xf6,0x52,0x81,0xa3,0xf2,0xd3, - 0x9b,0x02,0x98,0x24,0x89,0xb0,0xd7,0x37,0x44,0x70,0x7e,0xce,0x29,0xc7,0x65,0x95, - 0xe0,0x71,0x49,0xbf,0x07,0xa4,0x15,0x60,0x4d,0x2d,0x08,0xe4,0x6a,0x72,0x5b,0x30, - 0x67,0xbf,0xac,0x78,0xcd,0x24,0x2c,0x48,0x8d,0x17,0xd8,0x60,0xad,0xe0,0x49,0x85, - 0xf1,0x4d,0x02,0x72,0xcb,0xca,0x1a,0x8f,0x83,0xe0,0xee,0x97,0x98,0xd7,0xd1,0xa9, - 0x87,0xfa,0x4d,0x41,0x83,0x01,0xaa,0xfa,0x38,0x8d,0x21,0x5c,0x96,0xbb,0x00,0xfd, - 0xe2,0x7c,0x06,0x90,0x9d,0x86,0x2d,0xc9,0xa8,0x8f,0xdb,0x21,0xa6,0xa0,0x8f,0x51, - 0x73,0xf6,0xfd,0xbd,0x3a,0xf6,0xfe,0xfe,0xf0,0xd0,0x01,0x1e,0x41,0xcb,0x61,0xbb, - 0x3e,0xf9,0x05,0xf7,0xf7,0x30,0x80,0x56,0x5a,0x0f,0xb8,0x92,0x8a,0x4f,0x05,0x3a, - 0x9e,0xaa,0xa2,0x82,0x57,0xcd,0x80,0x48,0xa0,0x93,0xc5,0x92,0x55,0xa4,0x91,0xc7, - 0x0c,0x04,0x83,0xc9,0x47,0x00,0xa5,0x68,0xe8,0x27,0xb3,0x34,0xa8,0x43,0x56,0xb8, - 0x51,0x9c,0x55,0x20,0x6b,0x2b,0xeb,0x36,0x47,0x67,0x91,0x88,0xe3,0x1f,0x20,0xe7, - 0xca,0x6e,0x61,0xb3,0x7b,0x57,0x52,0xee,0xf0,0x50,0xf8,0xf8,0x49,0xf2,0x4d,0x88, - 0x4d,0xb2,0x92,0x28,0xd8,0xd0,0xab,0xb8,0xc9,0x55,0x64,0x6a,0x06,0x7c,0xbc,0x53, - 0x08,0x47,0x3c,0x2b,0xbe,0x62,0x5f,0x6c,0x28,0x39,0x11,0x53,0x48,0x31,0x5a,0x0b, - 0x4d,0x29,0x8f,0x80,0x45,0x38,0xf3,0x86,0x33,0x9d,0xd6,0xff,0xd2,0xf7,0x43,0x06, - 0x6b,0x1d,0x67,0xba,0x06,0xba,0xc6,0xd8,0xf5,0xeb,0x86,0x8a,0x95,0xcf,0x2c,0x8a, - 0x17,0x32,0xc0,0x8a,0xcc,0x58,0x71,0x8c,0x07,0xd0,0x13,0x27,0x63,0x23,0x19,0x00, - 0xfb,0x94,0x23,0xb8,0x2d,0xf5,0x2b,0x1b,0xfd,0xc5,0xa0,0x82,0xf0,0xf1,0x32,0x6c, - 0xb5,0xa9,0x54,0x56,0xb2,0x81,0x7f,0xc9,0xa7,0xdc,0x6e,0x94,0xcb,0x76,0x5f,0xfa, - 0x32,0x4a,0xc2,0x1d,0xcb,0x74,0xa1,0xac,0x55,0x4b,0x2e,0xb9,0x27,0xbd,0x0d,0xd2, - 0x46,0xb2,0xd7,0xcb,0x60,0xe9,0xa5,0x41,0xba,0xe9,0xf6,0xb7,0x03,0xce,0x97,0xae, - 0x31,0x65,0x13,0xca,0x0e,0x65,0x86,0x88,0xdd,0x78,0x0f,0x4f,0x4f,0x4f,0x3d,0x13, - 0x1c,0x98,0x0f,0xff,0x62,0x38,0xf0,0x0b,0x00,0xee,0x24,0xfe,0xd2,0x45,0xba,0x25, - 0x7e,0xea,0xe2,0x20,0x28,0x38,0xfc,0xb8,0xc1,0xd5,0x88,0x4b,0xb4,0xbe,0x4a,0x6a, - 0x51,0xf1,0x58,0xce,0x4f,0xef,0xac,0x9b,0x9b,0xd7,0x2f,0x3c,0xeb,0xe9,0xcf,0x1f, - 0x7f,0xb4,0x5e,0x3d,0x7d,0xfd,0xc6,0xf7,0x7d,0x17,0x11,0x8a,0x35,0x85,0xa4,0xe9, - 0xf9,0x7d,0xf3,0xf2,0x85,0xa9,0xa5,0x44,0xfd,0x11,0xfb,0xa1,0x13,0x42,0x27,0x9d, - 0xd7,0xc8,0xbb,0x86,0xde,0x86,0x69,0xf6,0x31,0x3c,0x90,0xd1,0xd0,0xb1,0x6d,0xfd, - 0xd7,0x7f,0xd2,0x00,0x2f,0x0d,0xd4,0xdb,0x24,0xba,0xb9,0xf4,0xbb,0xa9,0xf5,0xd8, - 0x3e,0x76,0x0e,0x13,0x5f,0x47,0x9d,0xbe,0xf4,0x58,0xaf,0x71,0x1e,0xb6,0xad,0x5a, - 0x52,0x0f,0xac,0x58,0x6d,0x07,0x84,0xe0,0x44,0xb1,0x33,0x2a,0x48,0x75,0x19,0x1b, - 0x67,0xab,0x89,0x4d,0x48,0x05,0x96,0x0b,0x25,0x77,0xc7,0xaa,0xe6,0xe6,0xb0,0x2e, - 0xee,0x2d,0x03,0xde,0xd5,0x5d,0x2e,0x96,0x4e,0xb0,0x80,0xa9,0xaa,0xab,0x15,0x71, - 0x1b,0x62,0xb3,0x13,0x24,0xdb,0x48,0x25,0x85,0x68,0x25,0xa2,0x92,0xbd,0x91,0x31, - 0xa5,0x5f,0xd9,0x06,0xa4,0xca,0x27,0x22,0x4f,0x0e,0xf4,0xac,0x03,0xbe,0x35,0xfb, - 0x1b,0xf2,0xdf,0xdf,0xd0,0x25,0x04,0xb2,0x19,0x08,0xd7,0xf2,0xa5,0xda,0xf0,0x49, - 0x5e,0x8f,0x8e,0x0e,0x1b,0xac,0x87,0xa0,0x4b,0x64,0x32,0x02,0x58,0x50,0x06,0xf6, - 0x51,0x8a,0x9c,0x90,0xba,0xf2,0x09,0x0c,0x93,0xa3,0x0e,0xba,0x8b,0x54,0x19,0xe8, - 0x9a,0x96,0x9a,0x0d,0xfb,0x34,0xe2,0x91,0x47,0x2d,0x0d,0x40,0x49,0x67,0x91,0x32, - 0x70,0xb6,0xeb,0x61,0x10,0x0c,0x4f,0xf1,0xdb,0xeb,0xe4,0xab,0x07,0xf4,0xf5,0x6e, - 0x91,0xa2,0xa8,0xe2,0x13,0x1f,0xfe,0x0d,0x5b,0xdd,0x6f,0xf0,0x05,0x4e,0x41,0x0d, - 0x48,0x32,0x62,0xbc,0xad,0xd2,0xb0,0x12,0x4e,0x00,0xcc,0xbd,0x60,0x13,0x9e,0x50, - 0x8e,0x1a,0x02,0x5f,0x9d,0xa9,0x36,0x34,0x9b,0xd4,0x89,0x14,0x49,0xca,0x51,0xa9, - 0x09,0x04,0xab,0x29,0x13,0x01,0xd1,0x9d,0x03,0x7a,0x83,0xb6,0x32,0x2a,0xd8,0x1e, - 0x7a,0xe0,0x1d,0x1e,0x72,0x3d,0x8b,0x01,0x1e,0x40,0x71,0x60,0x9b,0x7c,0xa3,0x4c, - 0xa5,0x87,0x4a,0x1f,0xc9,0x01,0x95,0x3b,0x49,0x8c,0x82,0x16,0xca,0x32,0xd2,0x60, - 0xad,0xca,0x45,0x38,0x64,0x2c,0xf9,0x65,0x31,0x2f,0x85,0x06,0xb8,0x2d,0x9c,0x0b, - 0xdc,0x3e,0xe3,0xf9,0xad,0x2f,0x45,0xe5,0x50,0xdb,0x10,0xc2,0x35,0x84,0x5f,0xda, - 0xa4,0x8c,0x42,0xfa,0x0e,0x53,0x88,0xba,0x60,0x32,0x72,0xc5,0xfd,0x48,0x42,0xb0, - 0xcd,0x80,0xb8,0x32,0x46,0x59,0xfa,0x09,0xbc,0x21,0x1f,0x0b,0x5d,0x55,0xfd,0x8c, - 0x6c,0x81,0xa3,0x27,0xa0,0x1f,0x42,0xb7,0x03,0x2f,0x44,0x6e,0xd7,0x0e,0x68,0xb6, - 0x50,0x16,0xd0,0xb2,0x36,0x12,0x20,0x47,0x91,0x16,0x7c,0xe6,0x76,0x92,0x3e,0xfa, - 0xf2,0xcc,0x3d,0x3a,0x6a,0xec,0x99,0xbb,0xee,0x99,0xd5,0x50,0xd9,0x3e,0xdc,0x4a, - 0xb3,0xa8,0xa2,0xe3,0x07,0x86,0x6f,0x7e,0xd4,0x32,0xe7,0x82,0x41,0x5e,0x8a,0xd7, - 0xc4,0x1e,0x01,0x99,0x72,0x47,0xd4,0x81,0x8b,0xac,0x32,0x36,0xb2,0x76,0x02,0xd6, - 0xdd,0x12,0x8b,0xea,0x8e,0xdf,0xa1,0x6a,0x57,0x86,0x03,0xf1,0x76,0x1b,0xfd,0xe5, - 0xe6,0xdd,0x4f,0x20,0xb4,0xe8,0x28,0xf3,0xe9,0xca,0xf9,0x94,0x7b,0x06,0x6f,0x1d, - 0x1d,0x35,0x7a,0xc6,0x6b,0x38,0xf3,0x33,0x41,0x02,0x17,0xbc,0x3d,0x8c,0x22,0x65, - 0xce,0x15,0xce,0xb5,0x71,0xbf,0x6d,0x50,0x6e,0x5c,0x51,0x05,0xa7,0xb0,0x5f,0x54, - 0x62,0x45,0xf3,0x35,0xb8,0xc3,0x20,0x01,0xd2,0x46,0x7b,0xa3,0x53,0x85,0x58,0x5a, - 0x06,0x93,0x2e,0x1b,0x2a,0x91,0x60,0xc0,0x48,0x88,0x7c,0xf5,0x2d,0x4c,0x49,0xd4, - 0x13,0x6d,0x10,0x12,0xc0,0x3a,0x87,0x8b,0x54,0x3f,0x7e,0x7c,0xfb,0xa6,0x41,0xfc, - 0xb5,0x0d,0x7a,0xe6,0x32,0xe3,0xdd,0xb2,0x60,0x3e,0x6f,0x32,0x86,0xdb,0x1d,0x49, - 0x83,0x2b,0x8c,0xd3,0x5b,0x0d,0xd9,0x54,0x0d,0x61,0x17,0x5b,0x02,0x8d,0xf7,0x68, - 0xcf,0xb2,0xd5,0x68,0xbe,0xfd,0x9a,0xca,0x57,0x8f,0x37,0x98,0x40,0x7d,0x90,0xf1, - 0xa8,0x09,0x6c,0xb0,0x75,0xb2,0x66,0x2a,0xb6,0xc1,0x2b,0xd8,0x0d,0x4a,0x32,0x6e, - 0x6e,0xdf,0x05,0x7d,0x70,0x05,0xaa,0x9d,0x1f,0x8f,0xdc,0x63,0x5b,0x43,0xd6,0xbb, - 0x9a,0xae,0x8b,0xda,0xc7,0x4d,0x72,0xc2,0xe9,0x50,0xf9,0x2b,0x39,0x1b,0xa0,0xb4, - 0x1b,0x72,0x5f,0x77,0x0f,0xa9,0x63,0x88,0xf1,0x9b,0x05,0x7a,0xbb,0xc0,0xb6,0xdd, - 0xce,0x96,0x8a,0x43,0x60,0x23,0xbe,0xbd,0x81,0x25,0x8b,0x6f,0xb2,0xf4,0xb6,0x6b, - 0x03,0xbb,0x97,0xb7,0x48,0x58,0xd6,0x4b,0x77,0x5f,0x49,0xde,0x6e,0x32,0x15,0xb6, - 0x81,0x1f,0xea,0x92,0x00,0xd0,0x3f,0x8d,0x3e,0x77,0xf4,0xa0,0xbc,0x9b,0x31,0x11, - 0x36,0xaf,0x9f,0xe3,0x64,0x1f,0x54,0x21,0x44,0x46,0xb7,0x91,0xe1,0xc5,0xc0,0x99, - 0x8e,0x5c,0xe1,0xa1,0x67,0xee,0x6e,0xf4,0xc2,0x96,0xa7,0x20,0x9e,0x62,0x79,0x42, - 0x3b,0x38,0x19,0x6f,0x48,0x84,0x2c,0x00,0xba,0xc2,0x41,0x4b,0xf4,0x8e,0xd2,0x51, - 0x8e,0xa2,0x8a,0x0f,0xb6,0x57,0xed,0xc9,0x3d,0xe9,0xef,0x12,0x4b,0x03,0x96,0xb4, - 0x36,0xd9,0xb8,0x5a,0x71,0xc0,0x39,0x93,0xb8,0xec,0xe9,0x72,0xcd,0xbc,0x60,0xec, - 0x8b,0x29,0xec,0x06,0xc6,0x00,0x3e,0x62,0x3a,0x58,0x60,0x16,0x37,0x51,0x84,0x75, - 0xf4,0xbc,0xab,0x08,0x66,0x5c,0xdb,0x64,0xf7,0x65,0xa3,0x00,0xa8,0x03,0x50,0xd2, - 0xa0,0x4d,0x55,0x24,0x2c,0x94,0x07,0x4c,0xf5,0x5f,0x46,0x84,0x90,0xde,0x03,0xb6, - 0x74,0x60,0xa6,0x05,0xa8,0x89,0x2e,0x20,0xd9,0x5d,0x34,0xbc,0xd4,0xba,0x34,0x66, - 0x13,0x8c,0x76,0x00,0x86,0x66,0xb5,0x36,0xba,0x02,0x33,0xb3,0x2a,0x35,0xa3,0x72, - 0x29,0x86,0xe5,0xf5,0x53,0x95,0xe3,0xe5,0x10,0x8c,0xe3,0x76,0x37,0x29,0x86,0x6f, - 0x98,0x0e,0x06,0xc5,0x0f,0x16,0x60,0xce,0xf3,0x45,0xcd,0x74,0x16,0x05,0x93,0x14, - 0x60,0x77,0x32,0xcc,0x89,0x54,0x2c,0xa9,0x62,0x98,0xdb,0xd5,0x77,0x86,0x09,0x37, - 0x60,0xfe,0xd2,0xd8,0x59,0x30,0xd8,0x68,0x5a,0xf1,0x0a,0xa0,0xbd,0x8b,0x1c,0x7c, - 0xf0,0x2f,0x47,0x47,0x5f,0xc8,0x3d,0x61,0xa0,0xb7,0x6c,0x14,0x69,0x88,0xf9,0x1c, - 0x35,0x84,0x0e,0x8b,0xf4,0x3e,0xec,0xfb,0xfb,0x2f,0x7e,0x3c,0x2b,0x2e,0xa3,0x8b, - 0xd3,0x53,0xd7,0x23,0xc4,0xd6,0x51,0x91,0x37,0xab,0x8d,0xa9,0xde,0x2d,0x3c,0xb8, - 0xfe,0xa4,0x1e,0x78,0x5f,0x24,0x91,0xbf,0x80,0x2b,0x06,0xff,0x40,0xc4,0xf9,0x99, - 0x70,0xe8,0xd8,0xf7,0xb6,0x0b,0x18,0xf6,0x72,0x6f,0xe1,0x2d,0x95,0xf2,0xac,0x01, - 0x08,0x0c,0x80,0xdc,0x35,0x85,0x41,0x75,0x88,0xfe,0x34,0xbd,0x65,0x58,0x5f,0x9f, - 0x81,0x37,0x1a,0xa0,0x4b,0xea,0x2a,0x53,0x72,0x0b,0x67,0xaa,0x18,0x4a,0xb9,0x81, - 0xb8,0x8b,0x8e,0xaa,0x6e,0x43,0xb8,0xad,0x36,0x30,0x87,0x45,0xae,0x4e,0x80,0x4f, - 0xc6,0xe4,0x1c,0xd4,0xc6,0x18,0x34,0x00,0x66,0x48,0x24,0x9c,0xa4,0x2f,0xc6,0x52, - 0x86,0x70,0x03,0x92,0x3a,0x15,0x2a,0x6a,0x20,0x95,0xae,0xc2,0xc7,0x86,0xfe,0x41, - 0x5c,0x23,0x4f,0xd9,0xc7,0xf9,0xb1,0x7d,0x94,0xd2,0x3b,0x1a,0xa1,0xf5,0x7a,0x6a, - 0x64,0xf4,0xf1,0x5d,0x43,0xa1,0x82,0x0f,0x98,0xea,0xf5,0x32,0xc2,0x58,0x6b,0x48, - 0xac,0x46,0x1f,0xd9,0x14,0x36,0xc7,0x0d,0x37,0x05,0xaa,0xda,0x6d,0x46,0xc7,0x30, - 0x85,0xce,0x6b,0xd3,0x7c,0x9d,0xdc,0x7b,0x2f,0x85,0xdf,0x6e,0xdd,0x49,0xe0,0x83, - 0xa6,0x94,0x71,0x16,0x08,0x87,0x03,0x9e,0x0b,0xa0,0x44,0xa5,0x09,0x25,0x66,0xe4, - 0x43,0x94,0xd9,0xac,0x98,0xc4,0xd9,0x09,0x22,0x48,0x05,0xea,0x18,0xe7,0xcb,0x1a, - 0x85,0x2b,0xf5,0x96,0xed,0x13,0x9a,0x36,0xe4,0xed,0x00,0xb2,0x0c,0xf6,0x90,0xf9, - 0x3c,0xdb,0xc0,0x9e,0x81,0xdc,0xe2,0x56,0xa9,0xad,0x45,0x64,0x77,0x0e,0x47,0xbe, - 0x81,0x6d,0x4f,0xd4,0xe3,0x1d,0x48,0x27,0x9c,0xe5,0xa8,0x51,0x6e,0xf6,0xe2,0xc7, - 0x93,0xa1,0x20,0x62,0xe8,0x72,0x5c,0xb5,0x3a,0xdf,0xbe,0x8c,0xb5,0x62,0x1d,0xf3, - 0xd9,0x40,0xe5,0xe5,0xec,0xe3,0xc5,0xb1,0x8d,0x16,0x64,0x81,0xdc,0x10,0x5f,0x7d, - 0x6f,0xcd,0x71,0x52,0x94,0x2b,0xb4,0x08,0xe5,0xaa,0x55,0xc6,0xcd,0x49,0xdd,0xb2, - 0x5e,0x93,0x2a,0xc6,0xd9,0x2a,0x59,0x2c,0xcd,0x14,0x80,0xf7,0x71,0x47,0xe9,0x45, - 0xd3,0xb4,0x5b,0x7a,0x99,0x52,0xaa,0x57,0xea,0x09,0xdf,0x6e,0x9c,0x30,0xdc,0x14, - 0xb4,0xe5,0x0e,0x55,0xae,0xfd,0x0d,0x98,0xe0,0xb4,0x88,0x46,0x24,0x23,0x8e,0xfb, - 0x21,0x68,0x93,0x7b,0xc2,0x1d,0x21,0xd8,0xb5,0x3d,0x9d,0x7f,0x52,0x60,0xd3,0x6b, - 0xb2,0x8d,0xad,0xda,0xb1,0x8e,0xea,0xac,0xc4,0x9d,0xa9,0x4e,0xbf,0x1a,0x95,0x21, - 0xdc,0x05,0xae,0x03,0xdb,0xb2,0xaa,0xea,0xef,0xab,0xad,0x40,0xc3,0x4f,0x3b,0x19, - 0x07,0x17,0xca,0x89,0xcb,0x88,0xd4,0xcb,0xfd,0x3d,0x06,0x64,0x52,0xc8,0xda,0x52, - 0xa1,0x92,0xa1,0xdd,0x5c,0xf4,0xbc,0x53,0x8e,0x92,0x1c,0x13,0x58,0x92,0xff,0x96, - 0xca,0x56,0xa9,0x4b,0x3a,0x4b,0x64,0xe5,0x25,0xb6,0xa2,0xb6,0x31,0xda,0xb5,0xfd, - 0x9c,0x5e,0x79,0x30,0x03,0x37,0xba,0x33,0xe8,0xff,0x84,0x92,0xe3,0x33,0xf0,0x99, - 0x7c,0x15,0x0e,0x12,0xa8,0xa8,0x6c,0x75,0xa3,0x05,0x9c,0x4b,0x6d,0x22,0x9d,0x7d, - 0x50,0xb2,0x3c,0x59,0xa8,0xc4,0x00,0x19,0xdf,0xab,0x1a,0xc2,0x76,0xd8,0x81,0x76, - 0x47,0x09,0xf9,0x44,0x2a,0x07,0x33,0xa1,0x0d,0xf3,0x74,0xe8,0x69,0x1c,0x66,0x63, - 0x57,0x13,0x41,0x00,0xcc,0x57,0x08,0xcc,0xc7,0xcb,0x70,0xa2,0x2a,0x80,0x6b,0x2a, - 0xdf,0x56,0x66,0x56,0x9a,0x34,0xc5,0x13,0xba,0xc1,0x26,0xc6,0x54,0xfe,0xa2,0x92, - 0xaf,0x63,0x80,0xf1,0x0d,0x00,0xb0,0x3b,0x3e,0x8b,0xeb,0xa2,0x02,0x2a,0xf0,0x72, - 0x5c,0xc4,0x70,0x57,0x8e,0x16,0x77,0x11,0x67,0x60,0x8c,0xe6,0x1c,0xd4,0x68,0x3e, - 0x33,0x4c,0x11,0xf1,0x5a,0xad,0x83,0xf7,0x1d,0xcb,0x8f,0x8e,0xd4,0x6f,0x1e,0x70, - 0x71,0x43,0x87,0x3d,0x97,0x67,0xe9,0xe0,0x7e,0xc7,0x12,0x9f,0x92,0x6f,0x14,0xcb, - 0xd5,0x04,0x3e,0xe5,0xfa,0xf6,0x7a,0x8d,0x28,0x59,0x60,0xf1,0x66,0x11,0xf8,0x49, - 0x37,0x54,0x05,0x23,0x2e,0xf5,0xaa,0xfe,0x8a,0x0f,0x88,0x3b,0x78,0x50,0xdc,0xb6, - 0x91,0x8b,0xe8,0xfe,0xde,0x81,0x1c,0x22,0xbf,0xd8,0x9f,0x08,0x81,0x30,0x44,0x76, - 0xd3,0x1a,0x38,0xe5,0x5f,0xc1,0x3e,0x63,0x9b,0x17,0xbe,0xbc,0xcf,0xcb,0x00,0x0b, - 0x58,0xce,0xa9,0x47,0xff,0xbb,0x9d,0x17,0xf6,0x40,0x34,0x9a,0xd7,0x65,0xc1,0x5f, - 0xa9,0x86,0xb2,0x3e,0x17,0xe0,0x69,0x61,0x7f,0x80,0x18,0xb7,0x01,0x16,0x93,0xd2, - 0x1d,0x97,0x0b,0x5f,0x23,0xf7,0xe5,0x74,0x8c,0xd7,0x14,0xb0,0x02,0xc7,0x67,0x7e, - 0xc5,0xd0,0xb2,0x3c,0xcd,0x32,0xba,0x1d,0xbe,0xb9,0x3f,0x43,0xa7,0x4c,0xde,0xb5, - 0x22,0xf4,0x81,0xe9,0x5e,0xc3,0x9d,0x9b,0x03,0xd8,0x57,0x36,0x79,0x5e,0xcc,0xe7, - 0xc0,0x66,0x5a,0x9b,0x6c,0x26,0xf8,0x8b,0x00,0x98,0x36,0x42,0xfb,0xbb,0x6b,0xd7, - 0x2e,0x78,0xf2,0x79,0x03,0x5e,0x9b,0x85,0x29,0x6e,0xaf,0xd5,0x4b,0xff,0xbe,0xca, - 0x10,0x3b,0x6e,0xd0,0x8e,0xe0,0x6f,0x4f,0xe8,0x97,0xf1,0xe5,0xc9,0xd2,0xdd,0xea, - 0x24,0x10,0x30,0xd5,0xd4,0xb8,0x35,0x29,0x78,0xe8,0x3a,0xf2,0x9e,0x44,0x29,0x72, - 0x09,0x5e,0x9e,0xd2,0xbd,0x11,0xb8,0x6c,0xf8,0xf7,0xfe,0xfe,0xd3,0x67,0xd7,0x9f, - 0x42,0x8c,0xc8,0xaa,0x4e,0x32,0x52,0x01,0x25,0xc8,0xfa,0x6f,0x5c,0x6f,0x2b,0xf8, - 0xc3,0xd5,0x04,0xbe,0xca,0x45,0xa6,0xd2,0x75,0xa6,0xa4,0xbd,0xca,0xb0,0x9a,0xfc, - 0x91,0x36,0x9e,0x75,0x93,0x58,0x5e,0x63,0xd5,0x38,0x48,0x7d,0xfc,0xe3,0xa1,0x7e, - 0x08,0x12,0x1f,0xff,0x6c,0xb4,0x4f,0xa2,0xf2,0xb6,0xee,0xba,0x5c,0xd4,0x8e,0xac, - 0x06,0xc0,0x54,0x60,0x58,0xb9,0xc6,0x0d,0x9b,0x71,0x99,0xc1,0xf5,0xe4,0x7a,0x57, - 0xb9,0xc6,0xaa,0xf4,0xbd,0x05,0xc8,0xa2,0xbc,0xb6,0xdf,0xe5,0xa8,0xc8,0x24,0x50, - 0xe8,0x28,0xe7,0x4d,0x6f,0x3d,0x8e,0xaa,0xcf,0x3a,0x5d,0xa6,0x75,0xdc,0xe4,0x7a, - 0x72,0xec,0x4c,0x40,0x47,0x8d,0xc0,0x23,0x50,0xc5,0x83,0x40,0x7f,0x12,0x68,0xe5, - 0xa5,0x19,0x82,0xc1,0xbc,0x40,0x62,0xab,0xaf,0xae,0xde,0x28,0x05,0x7b,0x22,0xf5, - 0x8f,0xbc,0x01,0xaa,0xbb,0x62,0x3a,0x05,0x05,0xf7,0x6e,0x3a,0x05,0xd5,0xd4,0x8e, - 0x62,0x29,0x51,0x0e,0x93,0x5f,0xae,0xca,0xe5,0x7b,0x4a,0xe5,0xb0,0xb2,0x99,0xa9, - 0xbb,0x41,0x7c,0xed,0x96,0xa1,0x3b,0xa8,0x12,0xfc,0xda,0xc3,0xa3,0xd4,0x40,0xb5, - 0x33,0x35,0xe0,0xa9,0x3c,0x0a,0x75,0x6b,0x63,0x9a,0x20,0x5d,0xcc,0xc7,0x28,0xd9, - 0xaa,0x44,0x70,0x1b,0xb6,0x95,0x03,0xfc,0xe4,0xcf,0xe3,0xf2,0x5b,0x7c,0xa3,0x13, - 0x4e,0xe8,0x4e,0x99,0x11,0x36,0x8e,0x4d,0xe2,0xee,0x98,0xf4,0x24,0x69,0xd7,0xad, - 0x50,0x58,0x28,0x13,0x8d,0x7c,0x5d,0xec,0xd5,0x67,0xb2,0xb2,0x0f,0x8c,0xf0,0x3b, - 0x91,0xb2,0x36,0xcd,0xe3,0xbd,0xb3,0xa4,0x9b,0x02,0x33,0x7f,0x8b,0x84,0x5f,0xc1, - 0x5d,0xae,0xa2,0xe1,0xc3,0x87,0xd7,0x0f,0x82,0xe6,0xdb,0xc5,0xa3,0xeb,0xf3,0xf6, - 0xdb,0xa3,0xc7,0xd7,0x67,0x81,0x4a,0x1e,0x15,0x32,0x4b,0x11,0x89,0x46,0x22,0xd4, - 0xf5,0x4d,0xcd,0x54,0x28,0x33,0x3e,0xf6,0xc9,0x5f,0xd2,0x07,0xea,0xc1,0xbd,0x01, - 0x7c,0x1b,0xa0,0x77,0xc6,0xe9,0x15,0x02,0x61,0x1f,0xff,0x06,0x0e,0x1a,0x18,0x30, - 0xcc,0x4d,0xf0,0xf9,0x4c,0x35,0x75,0x51,0x7f,0x42,0x34,0x10,0x7c,0x96,0xc7,0xc8, - 0xe0,0xbf,0xb5,0xce,0x17,0x44,0x67,0x0f,0x60,0x47,0x0e,0x9b,0xf2,0x3d,0x7f,0x54, - 0xf8,0xad,0x21,0xa3,0x7c,0x9f,0x0c,0x89,0x3b,0x19,0x44,0x23,0x72,0x1e,0xef,0xf2, - 0xb0,0x7a,0x85,0x17,0xbd,0x60,0x92,0xde,0xa2,0x8a,0x94,0x79,0x99,0xb0,0x2d,0xc6, - 0xd0,0x8b,0xab,0x8e,0x0e,0xa9,0x21,0x12,0x37,0x91,0x37,0xa6,0x99,0x8a,0x81,0x76, - 0x84,0xd8,0x2d,0xdf,0xc9,0xfe,0xd4,0x36,0xa5,0x76,0x48,0xec,0x25,0x03,0x5f,0x35, - 0x07,0x9b,0x43,0xdb,0x09,0xc6,0xf3,0xcb,0x47,0x94,0x51,0xc5,0xd4,0x9e,0xce,0x62, - 0xf5,0x2e,0xe1,0x4a,0xe8,0x0f,0xcc,0x3a,0x2c,0xa8,0x24,0x96,0x79,0x77,0x54,0x15, - 0xd0,0x09,0x2b,0x08,0xdf,0xee,0x5c,0xfd,0x25,0xba,0x33,0xa6,0xcb,0xdd,0xd7,0x2d, - 0x18,0x26,0x4e,0xdb,0x3b,0x12,0x95,0xdf,0x70,0x51,0x63,0x22,0xa6,0xc6,0x9f,0xc7, - 0x70,0x6c,0x70,0x2b,0x6c,0xf7,0xda,0x46,0xb5,0x6f,0x4d,0x19,0x78,0x75,0xa0,0x05, - 0xe8,0x0b,0xbe,0x59,0x60,0x1f,0x37,0x72,0xaa,0x6e,0xb3,0x39,0x68,0x0f,0xd9,0x43, - 0x9e,0xfe,0x59,0x75,0x31,0x9b,0x65,0x4c,0x9d,0x14,0x4a,0x50,0x37,0xb2,0x86,0x41, - 0x3b,0x53,0x37,0x5a,0xd7,0x03,0x93,0x6d,0x09,0x20,0xa4,0xe8,0xef,0x20,0x77,0x51, - 0xb4,0xd1,0xfa,0xed,0x0c,0xbb,0x22,0x30,0x07,0x0f,0x5c,0x5f,0xe0,0x46,0xce,0x00, - 0x93,0xde,0xf0,0xdf,0x80,0x9c,0xf6,0xc1,0x52,0x7d,0xf3,0xac,0xb3,0xd3,0xb3,0x8b, - 0xe1,0xe9,0x93,0xe1,0xd9,0x99,0x8b,0x5d,0x64,0xb2,0x13,0xc5,0xc2,0x1f,0xd1,0x11, - 0xca,0xbf,0xc7,0x54,0xa5,0x99,0x9e,0x87,0x73,0x95,0xa2,0x40,0xeb,0x21,0xee,0xef, - 0xb1,0xb4,0x80,0x05,0x46,0xf8,0xd4,0x60,0x83,0x7e,0x48,0xe5,0x1d,0xfe,0xfc,0xcb, - 0xd5,0x70,0xd4,0xd4,0x49,0x88,0xbd,0x61,0x57,0x07,0x15,0x0c,0x8f,0x4e,0xbd,0x0c, - 0x18,0xb4,0x2e,0xde,0x60,0xaf,0xfb,0x73,0xb8,0x21,0x50,0x99,0x5f,0xf6,0x51,0x1a, - 0xf2,0xe3,0x63,0x57,0x27,0x2f,0x9b,0x87,0x9f,0xf8,0xe7,0xee,0x4a,0x00,0x22,0xd3, - 0x07,0x99,0xb3,0xcc,0x0a,0x8c,0xdd,0x61,0x22,0x64,0x2b,0xbc,0xa8,0x51,0x09,0xa0, - 0xcb,0xf5,0xd8,0xcf,0x07,0x63,0x3c,0x77,0x5c,0xcd,0xec,0xd8,0x85,0xdb,0xcf,0x05, - 0x85,0x72,0xdc,0x10,0x80,0x3c,0xd4,0xa5,0xd6,0x1c,0x1c,0x4c,0xf8,0x7b,0x3d,0x02, - 0x9f,0xac,0x05,0x25,0xef,0xb2,0x33,0xa0,0x58,0x15,0x3c,0x96,0x91,0x5a,0xd8,0x93, - 0x59,0xaa,0x96,0x61,0x32,0xcb,0xa5,0x9c,0xab,0xb5,0x2d,0xc3,0x07,0x12,0x08,0xf0, - 0xdd,0xf6,0xf3,0x9e,0xa9,0x1a,0x76,0xde,0x25,0x94,0xa0,0x00,0xff,0xc9,0xc7,0x98, - 0x22,0xda,0xbd,0x1d,0x95,0x94,0x77,0xa3,0xaa,0xb3,0x89,0x7a,0x5e,0xe4,0xd4,0xb1, - 0x65,0xee,0xd0,0xb9,0xa0,0x9c,0xac,0xae,0xa4,0x27,0xd3,0x33,0xa3,0xfc,0x5c,0x2e, - 0x9d,0x22,0x27,0x72,0xb5,0x73,0x49,0xb1,0x17,0xf9,0xb5,0x4d,0x8e,0x6a,0x60,0x37, - 0x71,0x91,0xa2,0x57,0xb9,0xec,0x97,0x61,0x70,0x32,0xd0,0x89,0x29,0x49,0xb6,0xc3, - 0x66,0x96,0x60,0xf5,0xd3,0x1a,0xee,0x00,0x66,0x02,0x05,0xd2,0x68,0xd5,0xb5,0xbd, - 0x42,0x26,0xde,0x9a,0xb9,0x3b,0x50,0x02,0xe0,0xf5,0xe0,0x02,0x1f,0xa3,0x81,0x87, - 0x84,0xba,0xb9,0x0a,0x91,0x52,0x33,0x1e,0xa5,0x7a,0x77,0xe3,0x12,0x7f,0x57,0x2b, - 0xea,0xd7,0xb5,0xbc,0x52,0x3b,0x6f,0x94,0x35,0xd2,0x5c,0x8f,0x21,0x2f,0xbd,0x9d, - 0x65,0x74,0x2d,0x61,0x24,0xa7,0x22,0x5c,0xad,0x5e,0x11,0xef,0x23,0x97,0x18,0xa2, - 0xe1,0x17,0xc5,0x44,0xaa,0xde,0xab,0x72,0x5c,0x70,0xe6,0xd1,0xd1,0xa1,0x51,0x80, - 0x03,0xab,0x34,0x77,0x6c,0x9d,0xe8,0x28,0xac,0x01,0x28,0x40,0x72,0xed,0x06,0x58, - 0x12,0xb9,0xfe,0xd7,0xfc,0x5f,0xf3,0x8f,0x4d,0xbe,0x69,0x89,0x15,0x24,0x4c,0x3a, - 0x61,0x2b,0x46,0x59,0xe4,0x89,0xcc,0x4f,0x19,0x4d,0xda,0xcd,0x7f,0x36,0x9f,0xaa, - 0x50,0xb2,0xd3,0xc3,0x9a,0xf0,0x29,0xbd,0x60,0x52,0xb7,0x91,0x34,0x2a,0x3a,0x6c, - 0x8f,0x47,0x9d,0x58,0x40,0x50,0xeb,0x1a,0xf9,0xb4,0x32,0xa2,0xf6,0x1c,0xa3,0xb8, - 0xed,0x11,0x46,0x23,0x88,0xa5,0xf3,0x89,0xa3,0x12,0x6c,0x47,0x48,0x10,0x35,0xd4, - 0xef,0x4a,0x38,0xc6,0x2b,0x5f,0xdb,0x47,0x2a,0xad,0x1e,0x8d,0x28,0xad,0x44,0x78, - 0x7b,0xff,0xca,0xc7,0x1f,0x9a,0x53,0xe5,0xa7,0x71,0x8d,0x5e,0x4b,0xb9,0xa3,0x68, - 0xae,0x15,0xd5,0x61,0x82,0xbf,0xb9,0x65,0x50,0x26,0xc1,0xd7,0xdd,0x8b,0xaa,0x6d, - 0x64,0x31,0x88,0x22,0xad,0x68,0x4f,0x38,0x4d,0xc8,0xd0,0x6b,0xdb,0x5d,0xdc,0x0c, - 0x55,0x6b,0x47,0x08,0xcc,0x47,0x03,0xad,0xca,0x90,0x65,0x10,0x64,0x0a,0x59,0x27, - 0xc1,0x8a,0x34,0x21,0xa1,0x29,0x05,0xc1,0x77,0xa2,0x25,0x50,0xc6,0x35,0x72,0xb6, - 0xfa,0xb7,0xfb,0x74,0x4a,0x15,0x17,0x05,0x89,0x4a,0x97,0x97,0x98,0x36,0x5d,0xa6, - 0x2b,0xfc,0x13,0xcf,0x8a,0xe0,0x54,0xa7,0x4a,0x28,0x5b,0x4a,0xbf,0xe7,0x46,0xce, - 0x81,0x67,0x08,0x06,0x02,0x41,0x6c,0x44,0xe5,0x6a,0xaa,0x6d,0x92,0x1b,0xdd,0x9a, - 0xac,0x79,0xbc,0xb2,0xc6,0xb2,0x41,0x5a,0xb6,0x19,0x07,0x16,0xfd,0x06,0x9c,0x99, - 0x28,0x0c,0x7a,0x98,0xdb,0x68,0x81,0x9c,0x92,0x38,0x4a,0xd3,0x15,0x99,0x2d,0x06, - 0xcc,0x07,0xf1,0xbd,0x03,0x06,0x7a,0xc1,0xa6,0xf1,0x22,0xab,0x01,0x34,0x92,0xbb, - 0x8e,0x24,0x62,0x7d,0x81,0xae,0x36,0x6e,0x8c,0x5a,0xc3,0xea,0xaf,0x64,0xe1,0xae, - 0xe5,0x73,0xe2,0xf1,0xd7,0x53,0xb3,0xa9,0x9b,0xab,0x4a,0x28,0xe6,0x2c,0x34,0xef, - 0xea,0x8e,0xbe,0x2d,0x76,0xb7,0x9b,0x8a,0x8d,0xce,0xb4,0x13,0xdb,0x7f,0x95,0x6d, - 0xd5,0x5d,0x7e,0xd6,0x2c,0x37,0xf6,0xfa,0x5d,0x1b,0xd7,0x7d,0xb6,0x6e,0x9b,0xcf, - 0x5e,0xbc,0x7c,0xf3,0xf2,0xe3,0x4b,0xbb,0x6d,0x1a,0xdb,0xc7,0xa3,0x2a,0x47,0x86, - 0x6c,0x7a,0x6d,0x63,0x11,0xa7,0xa8,0x2d,0x79,0xcd,0x60,0x9b,0x55,0xe5,0xac,0xe2, - 0x16,0x68,0xa0,0x73,0x65,0xea,0x9d,0x38,0x14,0x8f,0x6f,0x50,0x5e,0x67,0xe2,0x3a, - 0x94,0xeb,0x2e,0xdf,0xf4,0x23,0x6c,0x60,0x56,0x7c,0x61,0xae,0x55,0x8f,0xbc,0xad, - 0x4e,0x36,0xb5,0xf9,0x6e,0x8d,0x52,0xff,0x76,0x63,0x57,0xfe,0x4f,0x68,0xaa,0x67, - 0xeb,0x1a,0x98,0x7d,0xcc,0x7f,0x57,0x62,0xcd,0x5f,0xc9,0xdb,0xc2,0x83,0x8e,0x26, - 0x76,0xd7,0x3b,0x75,0xb3,0x0c,0xd7,0xce,0x31,0x31,0xa7,0x02,0x17,0xcc,0x05,0x85, - 0x7e,0xa6,0xbd,0x68,0xf7,0xc1,0xc6,0xb1,0xae,0x25,0xd4,0x2f,0xdc,0xed,0x4d,0xa4, - 0x6a,0xee,0x40,0xc6,0xf3,0x5a,0x9c,0xf5,0x2f,0xc8,0xe1,0x18,0xa2,0x40,0xf3,0xba, - 0x82,0xd5,0xd4,0x26,0x55,0xfd,0x14,0x25,0xb1,0x6a,0xde,0x8b,0xc3,0xde,0xc4,0x95, - 0x6d,0x12,0x55,0xe5,0x58,0x4d,0xc1,0xa2,0x2d,0x65,0xff,0x9e,0x29,0x8c,0xa1,0x6e, - 0xbc,0x81,0xab,0x26,0x19,0x95,0xb6,0x28,0x52,0x36,0x8b,0x01,0xaa,0xa5,0x4b,0xb0, - 0xea,0x4e,0x16,0xc1,0x56,0xb2,0x53,0x01,0xbd,0x9a,0xa6,0xd7,0x00,0x25,0x02,0xf7, - 0x52,0x0d,0x3c,0x5c,0x34,0xe6,0x01,0xc1,0xf4,0xad,0x1f,0x59,0x96,0x04,0x4d,0x93, - 0xc3,0x6f,0x0b,0xb6,0x60,0x8d,0xbb,0x2a,0x9b,0x50,0x30,0x3b,0xa9,0xb2,0x88,0x35, - 0xf5,0x0e,0x23,0x52,0xe5,0x2d,0xf7,0xa2,0xb4,0x23,0xfa,0xed,0x7b,0x8e,0x66,0xaf, - 0xb0,0xc6,0x53,0x73,0x1d,0x32,0x18,0xa6,0xd4,0xca,0xbe,0xbf,0x1c,0x7b,0xec,0xca, - 0xa8,0xa5,0xcb,0x36,0x59,0x54,0x95,0x1a,0x66,0x8e,0x42,0x49,0x22,0xb9,0x39,0xde, - 0x08,0x49,0xb2,0x83,0x60,0xff,0xfd,0x6f,0xff,0x01,0xec,0xcc,0xf3,0x69,0xa1,0x59, - 0x71,0x4f,0x5b,0xd7,0x89,0xba,0x69,0xaf,0x29,0x15,0xb4,0x82,0xcc,0xa3,0x19,0xf7, - 0x56,0xdd,0x5b,0x5b,0xfd,0x5b,0x4d,0x92,0x01,0x18,0xad,0xf3,0x9b,0x9a,0x8e,0x4a, - 0x8e,0x6f,0x3a,0x76,0x91,0xd8,0x10,0xbe,0x60,0xa2,0xdd,0x29,0xdd,0xed,0x5c,0x3e, - 0x56,0x6f,0xf0,0xa3,0x5f,0xb1,0xac,0x88,0x51,0x09,0x77,0xba,0xc1,0xa4,0x60,0x21, - 0x1a,0x04,0xd5,0xba,0x5b,0x0c,0x27,0x5c,0x66,0xc7,0xe9,0xdd,0x0f,0x7c,0x75,0xde, - 0xa2,0xc4,0x33,0xc6,0x37,0x2a,0x97,0x8f,0xd8,0xa2,0x57,0x66,0x54,0xd7,0x76,0x53, - 0x59,0x6d,0xeb,0x1f,0x8a,0x86,0xde,0x76,0x99,0x0a,0x75,0x35,0xd5,0x39,0xba,0x55, - 0xa9,0xa0,0xc3,0xdd,0x8d,0x44,0x83,0xe1,0x30,0x52,0x64,0xad,0x54,0x1b,0xf9,0xb1, - 0x5d,0xae,0xae,0x6e,0xc9,0xdc,0x96,0xf3,0x54,0xec,0xb5,0x5e,0x70,0xe6,0x0e,0x03, - 0xa6,0x58,0x6c,0x1e,0x99,0x70,0x48,0x65,0x58,0xde,0x75,0xe0,0x90,0x83,0x45,0xa4, - 0x53,0x81,0x52,0x8c,0xb1,0xed,0x52,0xa5,0xa1,0xba,0xe2,0xa9,0x3b,0x15,0x8d,0x9e, - 0x4b,0x69,0xa9,0x65,0xcd,0x58,0xf7,0x40,0xa6,0x6e,0x60,0xc5,0x7a,0x27,0xd5,0xf1, - 0x9f,0x37,0xef,0x05,0xf9,0xf4,0xea,0x04,0xf8,0x7e,0x75,0x0a,0xb2,0x28,0x5b,0xdd, - 0xb0,0x23,0x48,0xbe,0x82,0x44,0xd8,0x4e,0x8a,0x65,0xee,0xcb,0xd6,0x34,0x58,0xa1, - 0xfb,0xbb,0x9c,0xc3,0xe2,0xfe,0x7e,0x7e,0x88,0x3f,0xa3,0x8a,0x77,0xba,0xbf,0x2f, - 0xef,0xe8,0x0b,0xa5,0x1a,0x25,0xe4,0x48,0xfb,0x68,0x4e,0x79,0x3b,0x7a,0x19,0xc5, - 0x86,0x05,0x4d,0x16,0xcf,0xec,0x10,0x6b,0xdc,0xe3,0xf2,0x4e,0x27,0x19,0x1e,0xe3, - 0x86,0xba,0x14,0x7f,0x71,0x6e,0x38,0x66,0x3f,0x42,0x90,0xdf,0x29,0x9d,0x00,0x63, - 0x3d,0x46,0x45,0x78,0x71,0x4e,0xbf,0xad,0x1c,0xc3,0x8e,0x95,0xe8,0xb9,0xd0,0x08, - 0xb9,0xe1,0x12,0xcf,0xdb,0x14,0xe3,0x47,0xdc,0xd6,0x44,0x27,0x0c,0x1b,0x8e,0xb1, - 0x7e,0x1d,0x80,0x50,0x16,0xec,0xaf,0xce,0xfa,0x46,0x3e,0x54,0x5f,0x8b,0xfa,0x64, - 0xac,0x8f,0x3b,0x93,0x93,0xa4,0x39,0xb5,0x3e,0x24,0xcf,0xbb,0x62,0x31,0xb9,0x4f, - 0xe0,0x53,0x04,0x0d,0xfa,0xae,0xed,0x0f,0x92,0xe4,0x26,0x88,0x7d,0xf0,0xe4,0xfb, - 0x10,0x00,0x37,0xae,0xb6,0x3e,0x30,0x2a,0x4b,0x81,0x0b,0x24,0x53,0xd0,0x8d,0x48, - 0x0d,0x24,0x95,0x3b,0xb8,0x83,0x59,0xe5,0x1d,0xb8,0x49,0xaa,0xe2,0xd9,0x8a,0x2c, - 0xa9,0x60,0x55,0x15,0x22,0xd5,0x7a,0x6d,0x5c,0x44,0xe2,0xa3,0xbd,0x07,0xfe,0x8c, - 0xd3,0x42,0x95,0x89,0x34,0x1a,0x67,0x05,0x70,0x0f,0xb2,0x0e,0x6a,0xdc,0xa0,0x15, - 0x63,0xdd,0xd8,0x20,0x95,0xfc,0xfe,0x78,0x40,0x77,0xa9,0x79,0x36,0x25,0x7f,0xa5, - 0xef,0x34,0xef,0xc5,0x03,0xe5,0xdd,0x96,0xb3,0x2f,0x5f,0xc7,0xf9,0x83,0xce,0x3e, - 0xf5,0xa2,0x7e,0xcb,0xdd,0xef,0xb6,0x68,0xf7,0x1d,0x76,0x64,0x49,0xd9,0xc9,0x76, - 0xec,0x34,0xc5,0x85,0x5e,0x2a,0x9b,0x94,0x9e,0xc1,0x63,0x2a,0x52,0x69,0xca,0x13, - 0x5b,0x9e,0x18,0x81,0x09,0x94,0x75,0xd5,0x01,0xdb,0xbe,0xb4,0xba,0x0a,0xce,0x69, - 0x91,0xb8,0x17,0x52,0xb9,0x8d,0x64,0x2a,0xa5,0x47,0x74,0xa3,0xa8,0x06,0x4b,0x3a, - 0x16,0xaa,0xaa,0xa9,0xb8,0xc7,0xde,0xc1,0x35,0xc8,0x34,0x81,0x6d,0xae,0x04,0x8e, - 0x98,0x15,0xb8,0x1d,0x52,0xbc,0x63,0xf2,0x30,0x2b,0xd6,0x57,0x52,0x60,0xff,0xb1, - 0x39,0x12,0x03,0x45,0xec,0x20,0x78,0x60,0x09,0x57,0xb6,0x4e,0x62,0xd1,0xb7,0x6d, - 0x4f,0xa4,0xe6,0x18,0x55,0x9a,0xe5,0x42,0xee,0xa5,0xfc,0x03,0x72,0x2d,0xb0,0xfd, - 0x44,0xc8,0x4e,0x4b,0xd5,0xc0,0x88,0xb7,0x73,0x65,0xdb,0x8b,0x53,0x7a,0x0f,0xb0, - 0xfd,0xe3,0x1b,0x4c,0xe0,0x50,0x73,0x5e,0x81,0xa5,0x00,0x19,0x89,0xb9,0xad,0x05, - 0x25,0xb3,0xb8,0x65,0x49,0xd7,0xea,0x17,0x9e,0x83,0xd1,0x63,0xd8,0x1b,0x22,0xa9, - 0x6d,0xb3,0xa9,0x5b,0x99,0x24,0xd1,0x48,0x96,0x7a,0x7d,0x97,0x7d,0xff,0xbb,0x69, - 0x5d,0xda,0x69,0xec,0xc9,0x86,0xb5,0xbe,0xb6,0xbe,0xdb,0xbc,0x2d,0x2a,0xec,0xfe, - 0x5d,0x6c,0x77,0xad,0x7e,0x2b,0x7d,0x65,0x32,0xd5,0xca,0xe9,0xfd,0xba,0xf5,0x0a, - 0x77,0xc2,0xf7,0x6f,0xda,0x5f,0x97,0x3f,0x91,0x3f,0x1a,0x72,0x22,0x7f,0xcb,0xfe, - 0x7f,0x00,0xca,0xe6,0xde,0x64,0xdc,0x5e,0x00,0x00, + 0x1f,0x8b,0x08,0x00,0x00,0x00,0x00,0x00,0x02,0xff,0xb5,0x3c,0x5b,0x72,0xdb,0x48, + 0x92,0xff,0x3a,0x05,0x44,0xcf,0x0a,0x44,0x0b,0x04,0x49,0xc9,0x92,0xd5,0x80,0x40, + 0xad,0x2c,0x59,0xd3,0x9e,0xb6,0xdb,0x0e,0xdb,0x1d,0x1d,0xd1,0x1e,0xc7,0x04,0x48, + 0x14,0x09,0x58,0x20,0x80,0x06,0x40,0xd1,0x1c,0x8a,0x11,0x73,0x88,0x39,0xc3,0x5e, + 0x61,0xff,0xf7,0x28,0x73,0x92,0xcd,0xcc,0x7a,0xa0,0x00,0x92,0x6e,0xf7,0x6e,0x4c, + 0x3f,0x44,0xa0,0x50,0x8f,0xac,0x7c,0x67,0x56,0x02,0x97,0x87,0x61,0x36,0xa9,0x56, + 0x39,0x33,0xa2,0x6a,0x9e,0x8c,0x0e,0x2e,0xf1,0xc7,0x48,0x82,0x74,0xe6,0x77,0x58, + 0xda,0xc1,0x06,0x16,0x84,0xa3,0xcb,0x39,0xab,0x02,0x63,0x12,0x05,0x45,0xc9,0x2a, + 0xbf,0xb3,0xa8,0xa6,0xbd,0x0b,0x7c,0x58,0x4e,0x8a,0x38,0xaf,0x8c,0xb2,0x98,0xf8, + 0x9d,0x7e,0x3e,0xed,0x4d,0xb2,0xb4,0xcc,0x12,0xe6,0x7c,0x2e,0xaf,0x22,0x9f,0x4d, + 0xa7,0xd3,0xf0,0x6c,0x70,0xd6,0x19,0x5d,0xf6,0x79,0xc7,0x91,0x18,0x30,0x8a,0xa7, + 0xdd,0xc3,0x65,0x9c,0x86,0xd9,0xd2,0x79,0x7b,0x67,0x75,0xa7,0x8b,0x74,0x52,0xc5, + 0x59,0xda,0xb5,0xd6,0x0f,0x41,0x61,0xfc,0xe2,0xf3,0x67,0xf6,0xcf,0xbe,0xd9,0x0f, + 0xf2,0xb8,0x5f,0x56,0x41,0xb5,0x28,0x4d,0xfb,0x95,0xff,0xf1,0x93,0xfd,0xda,0x3f, + 0x61,0xdf,0xdb,0xef,0xed,0x5f,0xed,0xb7,0xfe,0x2f,0x30,0xdc,0x5f,0xe3,0x63,0xe6, + 0x9a,0x49,0xfc,0xc0,0x4c,0x3b,0x8c,0x8b,0x6a,0xe5,0x1e,0x0e,0xed,0x07,0x37,0x5d, + 0x24,0x89,0x3d,0x63,0x95,0xfb,0x67,0x3b,0x4e,0xa7,0x49,0x3c,0x8b,0x2a,0xf7,0xa7, + 0xc5,0x7c,0xcc,0x0a,0x3b,0xca,0x92,0xd0,0x7d,0x33,0xfe,0xcc,0x26,0x95,0x5d,0x06, + 0x2b,0x57,0x41,0x50,0xd9,0xf7,0x36,0xb3,0xd6,0xec,0xe8,0xa8,0xcb,0x9c,0x8a,0x7d, + 0xa9,0x6e,0xb2,0xb4,0x62,0x69,0xe5,0x57,0xd6,0xc6,0x1e,0x2f,0x4a,0xad,0xeb,0xd8, + 0xce,0xad,0xb5,0xbc,0x33,0xa6,0x00,0xfc,0xd8,0x09,0xe3,0x32,0x18,0x27,0x2c,0xf4, + 0x0f,0x87,0x1b,0xfd,0x6e,0xe0,0xe5,0x4e,0x15,0xb1,0xb4,0x3b,0xb5,0xa7,0x96,0x57, + 0xb0,0x6a,0x51,0xa4,0x46,0xbe,0xb1,0xf3,0x2c,0x49,0xea,0x19,0x4b,0x7b,0x6e,0x4f, + 0xec,0x8c,0x63,0xa1,0xb2,0xa7,0xf6,0xcc,0x1f,0x7a,0x6a,0x85,0x77,0xb0,0xc2,0xec, + 0xe8,0xe8,0x70,0x0a,0xb0,0x4d,0xfd,0xa1,0x3d,0x49,0x58,0x50,0x7c,0x88,0xe7,0x2c, + 0x5b,0x54,0xdd,0xca,0xb2,0xbb,0xa5,0x33,0x09,0x92,0xe4,0xaa,0xec,0x5a,0xee,0x9f, + 0xbb,0xa5,0x65,0x89,0x15,0xe5,0xec,0xa1,0xb5,0x9e,0x74,0x43,0x1b,0xb1,0x02,0x7b, + 0x51,0xcd,0x0c,0x9b,0x09,0x55,0xcc,0xda,0x88,0x31,0x3f,0xda,0x3f,0x5a,0xd6,0x46, + 0xad,0xfc,0x23,0xac,0x3c,0xf5,0x07,0x5e,0x6b,0x45,0x0f,0xa0,0xe9,0x56,0x3e,0xb0, + 0x84,0x6c,0x7b,0x67,0xcf,0x61,0x5c,0x76,0x74,0x94,0x39,0xd3,0xb8,0x28,0x2b,0xdf, + 0x07,0x44,0x5c,0xc1,0x70,0x17,0x80,0x17,0xdb,0x06,0x72,0x65,0xb9,0xab,0x91,0x7c, + 0xb6,0x3d,0xf3,0xc6,0x86,0x49,0xeb,0x3e,0xa9,0xb5,0x9e,0xfb,0xa9,0x37,0x7d,0x7c, + 0x84,0xa9,0x36,0x76,0x9a,0x2d,0xdd,0x77,0x1b,0xe8,0x43,0x6c,0x51,0x77,0x9b,0xc0, + 0x4e,0x8e,0x8e,0x5e,0x39,0xf9,0xa2,0x8c,0xe0,0xc6,0x7b,0x7f,0x05,0xb7,0x93,0xee, + 0x7b,0xcb,0xfd,0xf5,0xea,0x57,0x07,0x46,0x01,0x1c,0xbf,0xfa,0x6f,0x1d,0x44,0x7a, + 0xf7,0x67,0xfb,0xb5,0x7d,0x0b,0x93,0x2d,0x83,0x6a,0x12,0xbd,0x6f,0xcd,0x34,0xb7, + 0xd6,0x6f,0x1d,0x3e,0x3d,0xc0,0xfd,0xab,0x03,0xd0,0x74,0x5f,0xfb,0xaf,0x83,0x2a, + 0x72,0xe6,0x31,0x3c,0xb6,0x5f,0x5b,0x34,0x34,0xae,0xee,0xb2,0xe2,0x96,0x3d,0xc4, + 0x13,0x56,0x0f,0x06,0xfa,0x65,0x7e,0xf6,0xf8,0xb8,0xde,0x48,0x42,0xa7,0x6c,0x69, + 0xbc,0x2d,0xb2,0x79,0x5c,0xb2,0x9a,0x1c,0x2b,0x3b,0xe5,0x84,0x8e,0x6a,0x90,0x4e, + 0xd8,0x69,0x4d,0x98,0xd2,0x5a,0x97,0x40,0xee,0x6e,0xe6,0x8c,0x17,0x71,0x12,0x1e, + 0x1d,0x95,0xfc,0xc2,0xf7,0x45,0x8b,0x05,0xe8,0x8f,0x1c,0x44,0x67,0xd7,0xb2,0x57, + 0x48,0xf1,0x8d,0xe5,0x69,0xd4,0xd0,0x70,0x2c,0xbb,0x79,0x69,0xf7,0x45,0x51,0x64, + 0x45,0xd7,0xac,0x78,0x27,0x13,0x37,0x92,0x39,0xe2,0xee,0xf1,0xf1,0x7b,0xf6,0x14, + 0x66,0xd9,0x6c,0x6a,0xb6,0xfb,0x73,0x77,0x81,0x2c,0x29,0xb6,0x32,0x65,0x80,0x2e, + 0x6a,0x69,0x71,0x57,0x61,0xad,0x51,0xaa,0x0b,0x27,0xbb,0xb7,0xaa,0xa8,0xc8,0x96, + 0x06,0x5f,0xa8,0x10,0x78,0x54,0x5c,0x4f,0xdc,0x81,0x92,0x75,0x55,0xd0,0x0f,0x10, + 0xa5,0x00,0x85,0x81,0x60,0x6e,0x34,0x9e,0xbb,0xc5,0xfd,0xc3,0x8c,0xf0,0xf7,0x3d, + 0xc8,0x79,0x3e,0xe5,0x34,0xf2,0x4b,0xef,0x95,0x33,0xcd,0x8a,0x17,0x01,0x80,0xa1, + 0x16,0x9f,0x02,0x7f,0x42,0x4f,0x04,0x7c,0x63,0x75,0x2d,0xa5,0x6f,0x0e,0xb8,0xe2, + 0x4a,0x83,0x39,0xf3,0x3b,0x0f,0x31,0x5b,0xe6,0x59,0x51,0x75,0x8c,0x89,0x90,0xe9, + 0xce,0x32,0x0e,0xab,0xc8,0x0f,0x89,0x80,0x3d,0xba,0x01,0x4d,0x11,0x57,0x71,0x90, + 0xf4,0x4a,0x90,0x25,0xe6,0x0f,0x51,0xcb,0x55,0x71,0x95,0xb0,0xd1,0xdb,0xa0,0xaa, + 0x58,0x01,0x7a,0x04,0xb6,0xd6,0x33,0x7e,0x89,0x7b,0x77,0xf1,0x65,0x9f,0x3f,0x02, + 0x45,0x58,0xad,0xf0,0xb7,0xff,0x9d,0x71,0xc3,0x55,0xa0,0x31,0x0e,0x4a,0xe6,0x1a, + 0x80,0x23,0xa3,0x84,0xd5,0x8d,0x71,0x92,0x4d,0xee,0x8d,0x2c,0x67,0x69,0x69,0xb0, + 0x07,0x56,0xac,0x00,0x88,0x82,0x19,0x79,0x30,0x63,0x8e,0xf1,0x0a,0x55,0x93,0x11, + 0xa7,0x86,0x5b,0x64,0x59,0x65,0x74,0x71,0xd4,0x04,0x70,0x38,0x67,0x66,0x79,0x60, + 0x18,0xf0,0x04,0x15,0x15,0x0b,0x0d,0xd2,0x61,0x30,0x28,0x61,0x00,0x8b,0x81,0x4a, + 0xac,0xc4,0x25,0x4a,0xb1,0xc8,0x43,0x90,0x2c,0x58,0xe9,0x1a,0xf7,0x8c,0xe5,0xb4, + 0x74,0xb5,0xcc,0x70,0xda,0xb2,0x62,0xb9,0x65,0xe3,0x4c,0x61,0x50,0xdc,0x1b,0x8b, + 0x34,0x64,0x05,0x29,0xfc,0x8f,0x61,0x50,0x05,0x3d,0xe8,0x09,0xf8,0xc1,0x47,0x9f, + 0x1c,0xe3,0xa7,0xcc,0x58,0xb2,0xb1,0x31,0x05,0x14,0x95,0x1c,0xfe,0x28,0xab,0xca, + 0x1c,0xc0,0x8a,0x82,0xd2,0x48,0x71,0x3e,0x44,0x03,0xab,0x1c,0xe3,0xbb,0xfe,0x01, + 0x01,0xbc,0x9e,0x64,0x49,0x56,0x00,0xc6,0x70,0x1e,0x97,0x60,0xf4,0x7a,0xbd,0x49, + 0xc1,0x82,0xb9,0xfb,0xe4,0xee,0xe9,0x8b,0xbb,0x17,0xe7,0x70,0x9f,0x07,0x29,0x4b, + 0xe0,0xfe,0xee,0xee,0xe6,0xee,0x1a,0xee,0xe3,0xf4,0xde,0x7d,0x32,0xbc,0x1e,0x5e, + 0x0c,0x9f,0xc2,0xdd,0x7c,0x01,0xfb,0x73,0x9f,0x9c,0x9f,0x9c,0xdd,0x9c,0x0d,0xe1, + 0x7e,0x1a,0xc0,0x3a,0xee,0x93,0x67,0xb7,0xcf,0xce,0xcf,0x2f,0xe0,0xbe,0x58,0x24, + 0xcc,0x7d,0x72,0x7b,0x7b,0x7b,0x76,0x73,0x2e,0x6e,0x7b,0x65,0x36,0x85,0x2e,0x2f, + 0x2e,0x5e,0x0c,0x6f,0x71,0x8a,0x59,0x94,0x95,0x78,0x7f,0x7a,0x7b,0x73,0xf3,0x02, + 0xee,0x13,0x9c,0xf0,0xee,0xee,0xec,0xe6,0x04,0xef,0xc6,0x01,0xdc,0x3d,0xbf,0x38, + 0xbd,0x19,0xde,0xc0,0x5d,0x06,0x6b,0x9f,0x3c,0x7f,0x76,0xfd,0xf4,0x39,0xdc,0x2c, + 0x83,0x22,0x75,0x9f,0x5c,0x9c,0x9d,0x0f,0x06,0x2f,0xbc,0x83,0x5e,0xaf,0x0c,0xd2, + 0xd2,0x2d,0x57,0x80,0xb5,0x79,0x6f,0x11,0xdb,0xbd,0x20,0xcf,0x71,0x35,0x6a,0xb0, + 0xcd,0xf7,0x6c,0x96,0x31,0xe3,0xe7,0x97,0xa6,0xfd,0x2e,0x1b,0x67,0x55,0x66,0x63, + 0xf7,0x5e,0xc9,0x8a,0x78,0x8a,0xfb,0xc8,0xd2,0xcc,0x5d,0xc4,0xf4,0x5b,0xe6,0xc1, + 0x84,0xd9,0xe6,0x4d,0x00,0xbc,0x14,0xc6,0x81,0xf1,0x1a,0xda,0x4c,0x98,0xe0,0x4e, + 0x5c,0xbd,0x66,0x69,0x92,0xd9,0x9c,0x61,0x82,0xd2,0x36,0xf9,0x7c,0xe2,0xa1,0x9a, + 0x60,0x73,0xb0,0x93,0x52,0x4d,0xa4,0x63,0x53,0x8d,0xf3,0xc1,0xcd,0xe0,0xf9,0xe0, + 0xfb,0x1a,0xe7,0xc3,0xd3,0xe1,0x70,0x38,0x90,0x38,0x7f,0x71,0xfb,0xe2,0xd9,0xed, + 0xf3,0x1a,0xe7,0xd7,0xa7,0xdf,0x3f,0xbf,0xb8,0xd6,0x70,0x7e,0xfd,0xec,0xf4,0xfc, + 0xa9,0xc2,0xf9,0xc9,0xf3,0x93,0x67,0x27,0xc3,0x26,0xce,0x87,0x77,0xc3,0x9b,0xe1, + 0x45,0x8d,0xf3,0x93,0xeb,0x93,0xf3,0x93,0x81,0xc4,0xf2,0xdd,0xdd,0xb3,0xeb,0xb3, + 0x33,0x81,0xe5,0xb3,0x67,0xcf,0x2f,0x9e,0xdd,0x29,0x2c,0xdf,0x7e,0x7f,0x3d,0x38, + 0xbd,0xdb,0x1c,0x7c,0xb7,0x1e,0x67,0x5f,0x7a,0x65,0xfc,0xf7,0x38,0x9d,0xb9,0xe3, + 0xac,0x00,0x8e,0xec,0x41,0xcb,0xe6,0x63,0x14,0x87,0x21,0x4b,0x3f,0xad,0xc1,0x76, + 0xe6,0x09,0x58,0xe6,0x34,0x4b,0xd9,0x61,0x3c,0x47,0xc1,0x0d,0xd2,0x6a,0x73,0x30, + 0xce,0xc2,0xd5,0x7a,0x1e,0x14,0xb3,0x38,0x75,0x07,0xde,0x38,0x98,0xdc,0xcf,0x8a, + 0x0c,0x38,0xda,0x05,0x55,0xda,0x15,0x18,0xb0,0x3c,0x42,0x8e,0x68,0x82,0x4d,0x5b, + 0x1e,0xf2,0xb3,0x3b,0x3c,0xcb,0xbf,0xf4,0x87,0xce,0x99,0xc1,0x1f,0x20,0xe1,0x2c, + 0xaf,0x07,0xec,0x7e,0x1f,0x57,0x3d,0xec,0xd1,0x2b,0xe7,0xc0,0xcf,0x11,0xc2,0x04, + 0x8b,0x81,0x1a,0x88,0x41,0x8a,0xc3,0xcd,0x41,0xc0,0xb1,0xed,0xc6,0x69,0x04,0x74, + 0xae,0x36,0xee,0x34,0x9b,0x2c,0xca,0xde,0x43,0x5c,0xc6,0x60,0xdf,0xd7,0xa0,0x39, + 0x93,0x38,0x65,0xee,0x49,0xfe,0xc5,0x00,0x5a,0xc6,0xa1,0x58,0x00,0x38,0xd0,0xf2, + 0xc4,0xc3,0x5e,0x36,0x9d,0xa2,0x51,0x83,0x3e,0x9b,0x03,0x67,0x59,0x04,0x39,0xec, + 0xe2,0x0b,0x57,0x3a,0xee,0xb3,0x93,0x41,0xfe,0xc5,0x93,0xbb,0x32,0x82,0x45,0x95, + 0x79,0x79,0x10,0x86,0x08,0x08,0x3e,0x32,0x86,0xe7,0xf0,0xe7,0xe9,0x05,0x8e,0x8d, + 0x4e,0xd4,0xf6,0x8d,0x81,0x01,0xed,0x1e,0x87,0x3c,0xfe,0x3b,0x73,0x87,0xa7,0xf2, + 0x76,0xc9,0xc8,0xdd,0x01,0x96,0x6e,0xe0,0x82,0x48,0x6e,0x01,0x00,0x93,0xa0,0x08, + 0xd7,0x5b,0xd8,0x23,0x7e,0xb1,0x3c,0x4e,0x0f,0x77,0xd8,0xda,0x0e,0x32,0x80,0x7c, + 0xd8,0x2b,0x80,0xa5,0xc1,0x64,0x0e,0x11,0x70,0x09,0x2a,0x42,0x09,0x73,0x8f,0xab, + 0x54,0x91,0x2f,0x4e,0x69,0xf3,0xd3,0x84,0x7d,0xf1,0x00,0x9d,0xb3,0xb4,0x17,0x83, + 0x14,0x95,0xee,0x84,0xa1,0x1a,0xf1,0x3e,0x2f,0xca,0x2a,0x9e,0xae,0x7a,0x42,0x23, + 0xcb,0xe6,0x59,0x90,0xbb,0xb8,0x33,0x30,0xb6,0xbd,0x88,0xef,0xe4,0xa9,0xbe,0xd0, + 0x80,0x10,0xf2,0x47,0xe0,0xbc,0xc0,0xee,0x7b,0xb6,0xbb,0x9b,0x59,0xce,0x06,0xb0, + 0xca,0x53,0x98,0x5a,0x67,0x16,0x34,0x58,0xbd,0x90,0x81,0xee,0x0e,0xd0,0xf0,0x10, + 0x77,0x7a,0x93,0x45,0x51,0xc2,0x04,0x79,0x46,0xaa,0xd1,0xab,0x0a,0xe8,0x1a,0xd3, + 0x63,0x01,0x03,0x2d,0x60,0x38,0xc3,0xb3,0xd2,0xae,0x61,0xe0,0xf7,0xf5,0x23,0x8e, + 0x38,0x27,0x2f,0xe2,0x6d,0xba,0x10,0x54,0xfa,0x64,0x7a,0xbb,0xde,0xc0,0xd9,0x7f, + 0x73,0xf0,0x9f,0x73,0x06,0x1a,0xa7,0x1b,0x65,0x60,0x6a,0x5c,0xfa,0x6b,0xad,0x71, + 0x7a,0x7e,0xbd,0xde,0x33,0xd5,0x46,0x42,0x20,0xbb,0xb5,0xe1,0x20,0x86,0xde,0x31, + 0x98,0xda,0x79,0xc3,0x13,0xf0,0xfa,0x37,0x53,0x90,0x22,0x30,0x31,0x81,0x98,0x67, + 0x6b,0x1d,0xbe,0x57,0x57,0xfa,0xc7,0xeb,0x0c,0x14,0x5d,0x0c,0x6e,0xbb,0xf3,0xf4, + 0x4c,0x62,0x33,0x64,0xd3,0x60,0x91,0x54,0xbb,0x56,0x23,0xfa,0x0a,0x74,0xc5,0x93, + 0x35,0x17,0xa1,0x26,0x7f,0xc0,0xd3,0x79,0x39,0x5b,0x73,0xa9,0x3f,0x51,0x44,0x44, + 0xa5,0xda,0x44,0x18,0xe9,0x3c,0xd8,0x38,0x04,0x2f,0xd9,0x7d,0x03,0xd0,0xec,0x9e, + 0x37,0xb3,0xa2,0xb9,0x01,0x50,0x71,0x24,0x40,0x51,0x9c,0xb7,0xb9,0x9c,0x8c,0xbc, + 0xc6,0xa4,0x17,0x7f,0x8c,0x47,0xbf,0xff,0x5e,0x48,0xaf,0x3b,0x84,0xee,0x7d,0x12, + 0xfc,0x7d,0x70,0x73,0x41,0xf6,0x96,0x11,0x88,0x53,0x8f,0xec,0x04,0xf0,0x22,0x6a, + 0x15,0x00,0x2d,0xcd,0x2a,0xf0,0x67,0x14,0x70,0x4d,0xa8,0x08,0x1b,0xc8,0xd7,0xdf, + 0x04,0x59,0xc2,0x40,0xdd,0x9f,0xee,0x52,0x6b,0xdf,0x2e,0x5e,0x9a,0x7a,0xc2,0x65, + 0x77,0x89,0xd0,0x46,0xc0,0x6c,0x8c,0xd7,0xba,0xf6,0x02,0x11,0xdc,0xb9,0x43,0xce, + 0x5e,0x6a,0x83,0xdf,0xaa,0x5c,0x68,0x92,0xde,0x98,0x55,0x4b,0xc6,0x52,0xd2,0x31, + 0xa4,0x45,0xb8,0x36,0xed,0x61,0x54,0x42,0x4c,0x24,0x76,0x86,0xf7,0xbb,0x71,0xf3, + 0xcd,0x5c,0x75,0x20,0xe5,0xe0,0x5b,0xd5,0x61,0x5b,0xdf,0xed,0xa0,0xf8,0x4e,0xf4, + 0x1d,0x60,0x94,0xfe,0x35,0x8c,0xa0,0x37,0x8a,0x2b,0x7b,0xf8,0xa4,0x87,0x58,0x74, + 0xf1,0x0f,0x21,0x01,0x75,0x1c,0xee,0x46,0x22,0x02,0x9c,0x90,0x2a,0x9b,0x93,0xdd, + 0x81,0x89,0x87,0x6b,0x1c,0xe2,0x0e,0x95,0x75,0xd2,0xe8,0x79,0x72,0xb2,0xc3,0xdc, + 0x90,0x6f,0x5a,0x10,0xc5,0x90,0xdf,0x7a,0xce,0x60,0xc8,0xe6,0xc0,0x95,0xe5,0x62, + 0xfc,0xed,0xf2,0x78,0x50,0x32,0xf2,0xec,0xd7,0x1a,0x71,0x4e,0xc8,0xfc,0x21,0xa7, + 0x30,0x69,0x01,0xa1,0x05,0x6c,0xe0,0xa0,0x6d,0x01,0x77,0x5a,0x3c,0xd4,0x16,0xe5, + 0x7c,0xad,0xa1,0xf8,0xf4,0xa4,0x69,0x52,0x4e,0xb6,0x6c,0xa9,0x18,0x16,0x06,0xe9, + 0x2e,0x15,0x10,0x4c,0xaa,0xb2,0x89,0xf2,0x16,0x76,0x77,0x50,0x18,0x11,0x7e,0xd1, + 0x64,0xba,0x21,0xb9,0x04,0x42,0x63,0x0b,0x23,0xe2,0x4e,0x32,0xcc,0xb5,0x70,0xa5, + 0x8d,0x60,0xe7,0x99,0x30,0x29,0x05,0x4b,0x80,0xf0,0x0f,0x6c,0x23,0x9e,0xb8,0x6e, + 0x30,0xad,0x48,0xc7,0x72,0x26,0xef,0x74,0x3c,0xd5,0x37,0x18,0x03,0xf3,0xc2,0xfe, + 0xbd,0x38,0x45,0xd7,0xa3,0x87,0x94,0x1e,0xa0,0xf2,0x9d,0xb6,0x94,0x83,0x40,0x27, + 0x51,0x66,0x60,0x3c,0xfd,0xba,0x4f,0x71,0xb6,0xc7,0xa7,0x88,0xd3,0x7c,0x51,0x01, + 0x3f,0x56,0xdd,0x8f,0x98,0x51,0xfa,0x64,0xd9,0xd4,0x42,0x37,0x3e,0xb2,0xed,0x27, + 0xbd,0x21,0x0f,0xca,0x72,0x09,0x92,0xf6,0x09,0x42,0xfd,0x04,0x88,0x2d,0xd4,0xf8, + 0x70,0x30,0xf8,0x8f,0xbd,0x66,0x1f,0xc9,0x3d,0x1c,0xfc,0xbb,0x0d,0x7f,0xdb,0xe8, + 0x6f,0xed,0xcc,0x07,0x9f,0x7b,0x72,0x0f,0xde,0xea,0x27,0x4b,0x6b,0xc4,0xf5,0x32, + 0x68,0x21,0xd7,0x50,0x6c,0x8a,0xdf,0x28,0xff,0x90,0xbc,0x85,0x7d,0xb6,0x17,0x4d, + 0xca,0xfd,0xb7,0x2a,0x0a,0xc5,0x46,0x2d,0x4c,0xb5,0x74,0xed,0xb6,0xaf,0xd0,0x70, + 0x55,0xf8,0x9a,0x06,0x6d,0x4f,0xe2,0x1f,0x67,0x15,0x33,0x0e,0x2f,0x34,0xa7,0xd4, + 0x0b,0x26,0xb8,0xf6,0x2e,0xb8,0x93,0xb8,0xac,0x6a,0x97,0x5c,0x09,0x94,0x87,0xed, + 0x3d,0x8a,0x8c,0x1b,0x3b,0xdf,0xab,0x58,0xc5,0x54,0xa3,0x24,0xfe,0x5d,0xd5,0x4e, + 0xca,0x7b,0xd0,0x42,0xc0,0x85,0xc6,0x2a,0xe8,0x2f,0x0f,0x3c,0x15,0x58,0x90,0x46, + 0xdb,0xb5,0x28,0x05,0x34,0x72,0x65,0xc3,0x49,0xe7,0x4a,0xdd,0xc1,0xc4,0x1c,0x25, + 0x03,0x0f,0x9d,0x18,0x8c,0xfb,0x5d,0x1e,0x9c,0x70,0x1d,0xac,0x1a,0x59,0x92,0xc4, + 0x79,0x19,0x97,0x3b,0x4c,0x55,0x8b,0x1e,0xf5,0x32,0xeb,0x7a,0xfa,0x93,0x81,0xb2, + 0xfa,0xdf,0xa2,0x18,0xc9,0xf1,0x70,0xc0,0x57,0xd9,0xc1,0x47,0x59,0xcb,0x1b,0x44, + 0x17,0xe6,0x20,0x64,0x55,0x10,0x27,0xa5,0xae,0x49,0x2f,0xb6,0x05,0x7c,0x97,0x40, + 0x97,0x8b,0x39,0x0c,0x5a,0xad,0x5b,0xfe,0xad,0xf2,0x24,0x06,0x84,0x63,0xe2,0x53, + 0x41,0x82,0x93,0xc1,0x9e,0xa9,0xfa,0xdf,0x19,0xff,0xfa,0xe7,0x3f,0xe0,0x3f,0x9e, + 0x38,0x91,0x37,0xdf,0xf5,0x41,0x9f,0xcf,0xed,0x27,0x65,0x19,0x87,0xf6,0x13,0x54, + 0x09,0xf6,0x93,0xa8,0xe4,0xbf,0x63,0xb0,0x9d,0xe0,0x1e,0x87,0xcc,0x26,0x95,0x6f, + 0x8c,0xed,0x96,0x93,0x30,0x0d,0xe6,0x71,0xb2,0x72,0x35,0x84,0x09,0xeb,0xb0,0xc3, + 0x8b,0xd8,0xe6,0x5a,0x3e,0x3f,0xad,0x29,0x48,0x6e,0xc0,0xbf,0xe7,0x92,0xa7,0x04, + 0xe9,0xa1,0xdf,0x94,0xcb,0x47,0x13,0x30,0x2d,0x76,0x3b,0x3d,0x27,0x43,0xe9,0x40, + 0x90,0x9f,0x22,0xeb,0xd6,0xfc,0xaf,0xf1,0xe6,0x40,0x74,0x30,0xc6,0x0b,0x60,0xc5, + 0x54,0x32,0xd9,0x1f,0x62,0x73,0xc9,0x8d,0x3a,0xcb,0x3f,0xd5,0x2d,0x98,0xae,0xe7, + 0x34,0x81,0x73,0xe5,0xf6,0x45,0xf4,0xca,0xd9,0x97,0x56,0x73,0xd1,0xbf,0xdb,0x56, + 0x0a,0x08,0x29,0x69,0x36,0x27,0x48,0x12,0xcb,0x48,0x62,0x37,0xad,0xa2,0x1e,0x30, + 0x5e,0x12,0x76,0xd3,0xe3,0x67,0x56,0x23,0x38,0x47,0x1b,0x09,0xb6,0x6a,0xbf,0xc8, + 0x92,0x51,0x64,0x69,0x48,0xbb,0x39,0xd1,0xd4,0x0b,0x0f,0x85,0x71,0xb0,0x11,0x0b, + 0xf5,0x73,0x5a,0x3f,0x7e,0x0a,0x86,0x60,0x67,0x20,0xa4,0xc6,0x1c,0xc7,0xeb,0x48, + 0xfa,0x1d,0xff,0xb1,0x51,0x8d,0x75,0xf3,0x45,0xb3,0xb9,0x7e,0x80,0x56,0x06,0xf7, + 0x39,0xc4,0x07,0xb6,0x53,0x9e,0xf0,0x0e,0x70,0x75,0xaa,0xba,0x6e,0xad,0x4d,0xb9, + 0x0e,0x5c,0x3d,0x5c,0xcd,0x6d,0xfc,0x63,0x00,0x43,0xa4,0xc7,0xf8,0xe7,0xff,0xe8, + 0x05,0x6c,0x70,0x96,0xb6,0x60,0xea,0x1a,0x4c,0xfe,0xcf,0x51,0xf5,0xe4,0x33,0x50, + 0x68,0xdd,0xf6,0x1c,0xa8,0x55,0x53,0x08,0x48,0xd2,0xb6,0x56,0x10,0x9d,0x30,0x94, + 0xd9,0xd7,0x8b,0xfb,0x34,0xd4,0xcf,0x70,0xc6,0xf1,0x6c,0x77,0x6c,0x23,0xb4,0xfc, + 0xb9,0x30,0xc4,0x06,0x57,0xb3,0x5c,0x7b,0x5d,0xb4,0xb4,0x97,0x54,0x90,0x1c,0x15, + 0x41,0xba,0x5a,0x02,0xfb,0x21,0xbb,0x94,0x6c,0xd6,0x44,0x98,0x64,0x8c,0x96,0x50, + 0xd5,0x29,0x90,0x3f,0x18,0x47,0xed,0x32,0xf9,0x32,0x32,0xc6,0xd5,0x8d,0x24,0x18, + 0xb3,0x44,0x0a,0xe1,0xb6,0x6b,0xc5,0x3b,0x71,0xb3,0xb8,0xed,0x4c,0xc9,0x60,0x75, + 0xa0,0x6c,0xa3,0x18,0xb0,0xcd,0x09,0xdf,0x9e,0xf1,0x68,0x1b,0xf2,0xe6,0x8e,0x9e, + 0xed,0x8e,0x05,0xda,0x5a,0xae,0x6d,0xfd,0xf7,0xe7,0x25,0xbe,0x96,0x85,0xc0,0xad, + 0xb8,0xe4,0xe2,0xb0,0x90,0x73,0xf7,0x37,0xfb,0x4f,0x94,0xd3,0x8b,0x82,0x10,0xac, + 0x22,0x26,0xa7,0xc0,0xa7,0x56,0x3c,0x41,0x84,0xb2,0xc9,0x11,0x85,0xf6,0x5e,0x6d, + 0xea,0x12,0xee,0x9e,0xd3,0xaa,0x8d,0xb4,0x1a,0x5f,0xfb,0x6b,0xb9,0x35,0x18,0x86, + 0x71,0xfb,0x74,0x8a,0x3d,0x8c,0x27,0x65,0x65,0x37,0xee,0xd1,0xd6,0x36,0x5b,0xa2, + 0x92,0xce,0x0a,0xeb,0x7c,0xc3,0xd9,0xfe,0x54,0x09,0x7a,0xfd,0xfb,0xd3,0x25,0x24, + 0x2e,0xc2,0x40,0xf2,0x5e,0xb6,0xe2,0x81,0x7d,0x59,0x0f,0xdd,0x02,0xf0,0x3e,0xe4, + 0x70,0xb4,0x23,0x3a,0x4a,0xcf,0x23,0xbc,0xe0,0xa9,0x5f,0xf6,0xf9,0xc9,0xc2,0x65, + 0x9f,0x9f,0xc3,0x62,0xf6,0x73,0x74,0x70,0x70,0x39,0x07,0x6f,0xc0,0x98,0x24,0x60, + 0x8f,0xfc,0x0e,0x8a,0x97,0x3c,0xa9,0x65,0xc5,0xe8,0x32,0x1a,0x8e,0xc4,0xe9,0x04, + 0x5c,0x5d,0x22,0x40,0xb2,0x27,0x84,0x60,0x1d,0x23,0x0e,0xe1,0xa2,0xea,0x8c,0x7a, + 0x30,0x37,0x3c,0x13,0x53,0xc3,0x40,0x98,0x56,0x44,0x5d,0xbc,0x4f,0xf0,0xc0,0xc2, + 0x8e,0xc1,0x5d,0x9e,0xd1,0x81,0x61,0x5c,0x46,0x27,0xa3,0xf7,0xd8,0x68,0xa4,0x10, + 0x42,0x67,0xc5,0x7d,0x09,0x43,0x4f,0xe8,0xc9,0x22,0x91,0x4b,0x20,0xce,0xf9,0x1a, + 0x74,0x05,0xb3,0x2f,0x12,0xea,0x92,0xcb,0x1e,0x68,0xa1,0x3b,0x02,0x2e,0xec,0x37, + 0x41,0xe8,0x05,0x2c,0x46,0x03,0xdc,0x79,0x39,0xab,0xa7,0xc2,0x9b,0x91,0x02,0x39, + 0xa7,0x39,0xc3,0xf8,0x81,0x9e,0xa3,0x45,0x06,0xd0,0x75,0x60,0xe1,0x29,0x09,0xb9, + 0x9c,0x6b,0xda,0x31,0xa6,0x59,0xc1,0xbb,0x76,0x46,0xcf,0xf1,0x98,0x45,0xec,0xc2, + 0x36,0xaa,0x22,0x86,0x4d,0xd1,0x81,0xa5,0x31,0x2d,0xb2,0x39,0x9d,0x7a,0xa4,0x40, + 0x15,0xa3,0x60,0xd8,0xff,0xb2,0x4f,0x53,0x89,0x69,0x71,0x51,0x31,0x29,0x06,0x80, + 0x1d,0x83,0x28,0x04,0xd0,0x0a,0x65,0xd0,0xe1,0xfd,0xa0,0x27,0x8f,0x01,0x14,0x84, + 0x04,0x3e,0x35,0xa9,0x1e,0x9c,0x17,0xe4,0x74,0xc0,0x74,0x1d,0x83,0x62,0x89,0x0e, + 0x7f,0xd0,0xa9,0x77,0x07,0x78,0xef,0x18,0x41,0x11,0x07,0x3d,0x82,0xc5,0xef,0x20, + 0x25,0x8c,0xb1,0xb6,0x8f,0x0e,0x11,0xe7,0xb2,0xcf,0x87,0xfe,0xb1,0x25,0xf8,0x3e, + 0x3b,0xa3,0x77,0x62,0xbf,0xad,0x39,0x76,0x12,0x05,0x7b,0xea,0x44,0xe1,0xd8,0xe9, + 0x03,0x7a,0x88,0x36,0xfc,0x02,0x77,0x4c,0x2c,0xd5,0xe6,0x2e,0xd0,0xec,0x1d,0xc9, + 0x54,0xd7,0x61,0x68,0x04,0x72,0x1b,0x8a,0xa7,0x80,0x5c,0x73,0xea,0x3a,0xed,0x68, + 0x98,0x27,0xce,0x04,0x31,0x02,0x4d,0xd3,0x24,0xb7,0xce,0x62,0x30,0xe2,0x27,0xc1, + 0xa2,0x44,0x4b,0x52,0x57,0x40,0x7f,0xd4,0x77,0x47,0xf3,0x30,0x28,0x23,0xcf,0xa8, + 0x82,0xdc,0x00,0xef,0x45,0xb0,0x12,0x8d,0x6f,0x72,0xb1,0x81,0xcb,0x74,0xd4,0x82, + 0x35,0x2b,0xef,0x46,0xab,0x51,0xce,0x77,0x61,0x76,0x9e,0x15,0x4c,0x02,0xda,0xe4, + 0x14,0x69,0xf0,0x71,0x62,0x1d,0xdf,0x0a,0x85,0xfb,0x58,0x18,0x9d,0x65,0xb5,0x41, + 0x3a,0xdb,0x6c,0x32,0x28,0xd9,0x2d,0x0e,0x37,0xf6,0x14,0xa7,0x9f,0xfc,0x1a,0x2c, + 0x6c,0xc2,0xd2,0x59,0x15,0xf9,0x9d,0xd3,0x93,0x0e,0xf0,0xf7,0x6f,0x8b,0xb8,0xa0, + 0x83,0xc5,0xb2,0x82,0x3e,0x8c,0xa5,0x1d,0xb1,0x43,0x3c,0x83,0x98,0x64,0xf3,0x3c, + 0x61,0x15,0x8c,0x06,0x0d,0xda,0xe1,0x4d,0x41,0x1e,0x57,0x60,0xd6,0xfe,0xce,0x50, + 0x92,0x53,0x26,0x5a,0xb3,0xa2,0x00,0xe2,0x8a,0x7e,0x65,0x0e,0x21,0x11,0x19,0x10, + 0x00,0x3a,0x48,0x4a,0xa6,0x08,0x18,0xe0,0xd0,0x52,0x00,0x87,0x8b,0xc1,0xde,0x65, + 0xe3,0xb6,0x78,0x09,0x77,0x1f,0x7c,0x24,0x8e,0x4b,0xba,0x10,0x34,0x27,0x9e,0x1c, + 0xdd,0x82,0x39,0x58,0x65,0x0b,0x63,0xce,0x80,0x41,0x8f,0x92,0xe0,0xb7,0x45,0xe6, + 0x5d,0x8e,0x65,0x67,0x9a,0x7e,0x3c,0x3a,0x2a,0xa8,0xfd,0x4a,0xe7,0x53,0xc1,0xd4, + 0xa3,0x6f,0xa6,0x23,0x4c,0xb7,0xc0,0x8d,0xfc,0x5c,0x32,0x23,0xae,0x25,0xe4,0x8f, + 0x8c,0xc7,0x53,0xd9,0xce,0xe8,0x47,0x3c,0x9b,0x05,0x6b,0x5f,0x4b,0xaa,0x52,0x6a, + 0xbf,0x43,0x77,0x8c,0x3e,0x3a,0xa3,0xb7,0x22,0x6f,0xf2,0x7f,0x55,0x4a,0x35,0x77, + 0xd0,0x7c,0x82,0x3b,0xf8,0x35,0x07,0x5b,0x66,0x66,0x1a,0xdc,0x72,0x7e,0x2a,0x19, + 0xe3,0xdf,0xc2,0x1a,0xdf,0xaa,0xa8,0xf2,0x65,0x25,0xd4,0x20,0xfa,0x4f,0x45,0x96, + 0x94,0x12,0x76,0x6a,0xcc,0x0b,0x56,0x96,0x2c,0x54,0x73,0xbf,0x8f,0xb2,0xe5,0x7e, + 0xe9,0x6a,0x23,0xed,0x2b,0xc2,0x9d,0x17,0xb1,0x04,0x07,0x8c,0xe8,0x3c,0x16,0x36, + 0x0e,0xb4,0x18,0x01,0xba,0xa5,0x98,0xbf,0xae,0x93,0x1b,0xe4,0x9d,0x44,0xf7,0x7c, + 0xb2,0x34,0x5b,0xa2,0x56,0x1b,0x09,0x12,0xf1,0xd5,0x64,0xaa,0x49,0x75,0x81,0x29, + 0x97,0x71,0x35,0x89,0x8c,0x14,0xb7,0xa6,0xf1,0xc0,0x3e,0x3d,0xfd,0x35,0x1d,0x8d, + 0x3a,0x56,0x59,0xd2,0x86,0xd4,0xf1,0xb1,0x18,0x0d,0xd4,0x22,0xa7,0xc6,0xed,0xb5, + 0xe5,0x00,0x13,0x6f,0x22,0x24,0x94,0x15,0x28,0x3f,0x88,0xbd,0x2a,0x23,0x00,0xc5, + 0x5b,0x45,0x71,0x49,0xf5,0x0e,0x54,0x9e,0x50,0x1a,0x88,0x1f,0x70,0xee,0x3d,0xa3, + 0xde,0x8f,0x11,0x16,0x59,0x5e,0xf2,0x9e,0x40,0xdf,0x54,0xd8,0x8b,0x2a,0x33,0x28, + 0x2c,0x89,0x2b,0x47,0xca,0x8a,0x5a,0x31,0x2a,0x69,0x41,0x09,0x23,0xad,0x8b,0xf6, + 0x9c,0x2f,0x5b,0x81,0x8a,0x9f,0x30,0xc7,0x78,0x39,0xd5,0x96,0xc7,0x4a,0x98,0x12, + 0x40,0x2a,0x97,0x10,0x0d,0xa7,0x33,0xbb,0x36,0x11,0x47,0x45,0x89,0x9a,0xa2,0x04, + 0xd5,0x5f,0x30,0xd4,0xd7,0xc0,0x3e,0xdc,0x82,0x00,0xa1,0x91,0xb5,0x10,0x14,0xac, + 0xe1,0xb0,0x0d,0x70,0x97,0x97,0xd1,0x0a,0x17,0x99,0x64,0x8b,0x04,0x5c,0x22,0x30, + 0xc4,0x14,0x63,0xb5,0x5d,0x14,0x9e,0x7d,0x01,0x88,0xb9,0xaf,0x38,0xfa,0x01,0xb6, + 0x59,0x36,0xbc,0x28,0xe0,0x1e,0x0e,0x33,0xc8,0xb4,0xec,0x45,0x58,0x1e,0xbd,0x4c, + 0x0d,0xf2,0x3c,0x6b,0xaf,0x84,0x0c,0x7e,0x99,0x64,0x95,0x6d,0xa0,0xdb,0x97,0x03, + 0xfc,0x30,0x1c,0x0d,0x9b,0x0d,0xde,0xb1,0x66,0xec,0x10,0x16,0xc0,0x71,0x14,0x03, + 0x03,0xa1,0xa3,0x09,0x80,0x4e,0xe3,0x34,0x2c,0x5d,0x23,0xca,0xe6,0x0c,0x3a,0x57, + 0x8b,0x30,0xce,0x6c,0xe3,0x81,0xa5,0x0b,0xe6,0x20,0xb4,0x40,0x5e,0x01,0xeb,0x4e, + 0xab,0x2d,0x8d,0xf5,0x0f,0xbc,0x1a,0x44,0x99,0xe9,0x26,0x2f,0xb4,0xf5,0xce,0x56, + 0x88,0xd0,0x11,0x54,0x23,0x3f,0x9c,0x9c,0xd0,0x7c,0x17,0x4b,0x7d,0xd8,0x22,0x09, + 0x70,0x56,0xed,0xb1,0x09,0x1d,0x0f,0xd3,0x90,0x2d,0xcc,0xeb,0xd2,0x9c,0xde,0x15, + 0xfc,0x83,0x5a,0xdf,0x95,0x3c,0x43,0xcc,0x87,0x44,0xc3,0x65,0x30,0x9f,0x34,0x8a, + 0xaa,0x2a,0x77,0xfb,0xfd,0xe1,0xf7,0x27,0xce,0xf0,0xfc,0xc2,0x79,0xea,0x0c,0xfb, + 0x97,0x7d,0x7a,0x62,0xd7,0x05,0x3b,0xa2,0x94,0xd1,0x90,0x71,0xab,0x63,0xbc,0x8d, + 0x40,0xa9,0x01,0x6f,0x54,0xc1,0xca,0xc8,0x68,0xe6,0x2a,0xca,0x16,0xb3,0x08,0xaf, + 0xda,0xb5,0x31,0xb0,0xd6,0x35,0x28,0x3f,0xa3,0x08,0xe2,0x52,0xf0,0xe2,0x18,0x22, + 0x19,0x63,0x78,0x66,0x00,0xbd,0x31,0x61,0xaf,0x3b,0x26,0x48,0x18,0x1c,0xce,0x73, + 0x63,0x08,0x2f,0x17,0x84,0x98,0xf3,0x2f,0xfe,0x72,0x7a,0x02,0x00,0x8e,0xc4,0x98, + 0xf2,0x8f,0xa2,0x52,0x79,0x48,0xba,0x07,0xf4,0x1a,0x36,0xa4,0xbc,0x1b,0x5d,0xc0, + 0x21,0x4c,0x91,0x54,0x98,0x43,0x1f,0xf0,0x02,0x32,0x24,0x19,0xe5,0xaf,0x31,0xca, + 0xcb,0x9b,0xfa,0x4c,0x10,0xdc,0xa0,0xbe,0x4d,0x15,0xd6,0x54,0x55,0x34,0x81,0x34, + 0x28,0x60,0x04,0xa9,0x1a,0x09,0x74,0x24,0xe0,0xa1,0x63,0x88,0x78,0x52,0xd8,0x6d, + 0xc4,0x8d,0x12,0x94,0xa6,0x0e,0xfb,0x03,0x13,0x27,0xcb,0x60,0x55,0x76,0xe4,0x94, + 0x74,0xf7,0xff,0x9e,0x14,0xad,0x94,0x98,0xf1,0xcd,0x74,0xba,0x6b,0xba,0xdf,0xb5, + 0xd3,0x3c,0x4f,0xb8,0xcf,0x52,0xd7,0xf6,0x57,0xf4,0x13,0xf0,0x60,0x14,0xb8,0xc7, + 0xe2,0xfe,0x5b,0x5c,0xb1,0xff,0xb7,0xed,0x03,0xe1,0xdb,0x1d,0x94,0x88,0x7a,0xb1, + 0x3d,0xb6,0x6f,0xa7,0x8d,0x02,0x4e,0xfc,0x5d,0x2b,0xd5,0x50,0x4a,0xfc,0x60,0x75, + 0x74,0x19,0x18,0x51,0xc1,0xa6,0x7e,0xa7,0xdf,0x19,0x89,0xc2,0xbb,0xcb,0x7e,0xa0, + 0x35,0xf3,0xda,0x43,0x00,0x85,0x7e,0xe9,0x59,0x5f,0x8c,0x85,0x09,0x31,0x8a,0x56, + 0x55,0xcc,0xa3,0x03,0x55,0x7b,0xf8,0xa7,0x6e,0xac,0x0a,0x1e,0xc3,0x6c,0xb2,0x98, + 0xb3,0xb4,0x72,0x66,0xac,0x7a,0x91,0x30,0xbc,0x7c,0xbe,0x7a,0x19,0x42,0x8f,0x4d, + 0x3d,0x80,0x95,0x93,0x6e,0xa5,0x86,0xbc,0xaf,0xd0,0xae,0x40,0x83,0x53,0xb0,0x3c, + 0x09,0x26,0xac,0xdb,0xff,0x78,0x74,0x39,0xea,0x7c,0xea,0xcf,0x6c,0xbd,0x58,0x15, + 0x36,0x27,0x46,0xac,0xcd,0x23,0xd3,0x35,0x8f,0x82,0x79,0xee,0x99,0xb6,0x79,0x89, + 0xd7,0x49,0x85,0x97,0x23,0xbc,0x9c,0xd1,0x65,0x07,0x2f,0x41,0x17,0xc2,0xcd,0xe6, + 0xe3,0xe4,0xd3,0xa6,0xb1,0x7e,0xaa,0xaf,0x0f,0x77,0x20,0xab,0x3f,0xbf,0x7b,0x79, + 0x03,0x4c,0x03,0x8c,0x91,0x52,0x49,0x6d,0xdd,0xfb,0x33,0x16,0x6b,0x8a,0xbe,0xaa, + 0xf4,0xb2,0x7e,0x9c,0x67,0x65,0xd5,0x5d,0xd8,0x98,0x6a,0xd8,0x2a,0xfb,0x5c,0xcf, + 0x19,0x28,0xbd,0xd0,0x35,0xdf,0xbe,0x79,0xff,0xc1,0xc4,0x92,0x42,0x9e,0x3c,0x28, + 0xdd,0xb5,0x29,0x2a,0xa5,0x7b,0x1f,0x80,0x55,0x00,0x56,0xac,0x9a,0x8b,0x27,0x94, + 0xd2,0xe8,0x7f,0xe9,0x2d,0x97,0xcb,0x1e,0xd2,0xb1,0xb7,0x28,0x12,0x0e,0x5f,0x68, + 0x6e,0x68,0x0d,0x17,0xff,0xc8,0xc2,0xe3,0xcf,0x3a,0x20,0x65,0xb0,0xc2,0x7a,0x6c, + 0x6b,0xfd,0xf6,0xce,0x11,0xd7,0x8f,0x8f,0x66,0x76,0x6f,0xda,0x7f,0xea,0x9a,0xc0, + 0x30,0x58,0xbb,0xda,0xe8,0xfd,0x43,0xb9,0xbf,0x3f,0xb1,0x58,0x73,0xc4,0xf8,0xab, + 0x0b,0x88,0xf8,0x96,0x86,0xf4,0xfb,0xc6,0x9b,0x34,0x01,0xb5,0xbf,0x28,0xc8,0x10, + 0x51,0x69,0x2b,0xf8,0x28,0x41,0x51,0xac,0x48,0x91,0xf3,0x12,0x52,0x93,0xbc,0x9a, + 0xb0,0x74,0xea,0x35,0xc0,0x49,0xc0,0x0a,0x6b,0x49,0x99,0xa3,0x23,0xe6,0x90,0xca, + 0xf1,0x7d,0x93,0x26,0x31,0xb1,0x65,0x0e,0xae,0x05,0x78,0x26,0xb0,0xf6,0x14,0x0c, + 0x30,0x62,0xe6,0x80,0x2f,0xd9,0xa8,0xbf,0xe4,0x65,0xa3,0x38,0xda,0x88,0xb5,0xb8, + 0xd6,0x03,0xab,0x00,0xa0,0x69,0x85,0xf2,0x88,0x0a,0xb4,0xfd,0x60,0xb5,0x8c,0x25, + 0x43,0xf7,0xc2,0x39,0xc0,0x42,0xe3,0x87,0x38,0xf8,0xa1,0xf4,0x49,0x0d,0xd8,0xc2, + 0xbd,0x02,0x5f,0xb9,0x2a,0x16,0xcc,0x06,0xbd,0xe3,0x9f,0x79,0xd4,0x0b,0xe3,0xb2, + 0x9f,0x60,0x91,0x12,0x8b,0xed,0xf1,0xe6,0x47,0xb6,0xf2,0x4d,0xd3,0x8e,0x4a,0x6c, + 0xe5,0x57,0xaf,0x32,0x20,0x7a,0xe8,0x63,0xd5,0xb8,0x87,0x90,0xfe,0x9c,0x72,0x97, + 0x26,0x8f,0x27,0x22,0xe4,0x06,0xf1,0x03,0x87,0x29,0x32,0x20,0x6a,0xaa,0xc8,0x39, + 0xc2,0x74,0xd7,0xb2,0x88,0x2b,0x01,0x0b,0x62,0xf7,0x16,0x4b,0xf5,0x05,0x3c,0x51, + 0xa9,0xdd,0x71,0x40,0xee,0x59,0x5e,0xe1,0x72,0x10,0x3a,0xfd,0x12,0xc4,0x95,0x3f, + 0xb0,0xd1,0x08,0x0a,0x68,0x30,0x62,0x14,0x97,0xd3,0x00,0x62,0x57,0x3e,0x0b,0xd0, + 0x23,0x48,0x67,0x00,0xd9,0xc0,0xab,0x49,0x40,0x6f,0x04,0x74,0x89,0xc8,0x74,0xe9, + 0xab,0xb5,0x1f,0x1f,0xc5,0xb2,0x8f,0x8f,0x87,0x87,0x5d,0xa0,0x39,0xba,0x13,0xa6, + 0xe5,0x90,0x2d,0x78,0x7c,0x84,0x06,0xd4,0xd0,0xb2,0xc1,0xe2,0x54,0xb9,0x2e,0xef, + 0x65,0xb6,0x48,0xf9,0x5c,0x08,0x4d,0x69,0x8a,0x14,0x12,0x0a,0xfd,0xca,0x08,0x92, + 0x02,0x24,0x63,0x65,0xdc,0xa7,0xe8,0x43,0x12,0xea,0x9d,0x03,0xe4,0x33,0x5e,0x55, + 0xae,0x57,0x79,0x73,0xba,0x1c,0x1e,0x96,0x0e,0x5e,0x71,0x6a,0x7b,0x58,0x4c,0xcd, + 0x51,0x8e,0x85,0xdf,0x82,0x07,0x2c,0x41,0x04,0xd5,0xe0,0x20,0xc4,0x1e,0x2c,0xf1, + 0x3c,0xfb,0x82,0xf5,0xd3,0x44,0x8d,0x57,0x18,0x62,0xa3,0x38,0x49,0xd6,0xb1,0xf1, + 0xd5,0x0d,0x08,0x39,0x43,0xa3,0x0b,0xea,0x40,0x77,0xad,0xc1,0x43,0x0e,0x0c,0x2c, + 0xfe,0xb6,0x3c,0xe3,0xc4,0x20,0x87,0x11,0x7c,0x1e,0xc4,0x33,0x68,0x30,0x4e,0x29, + 0x2c,0x4d,0xf7,0x01,0x70,0x2a,0x51,0xd7,0x4a,0xca,0x41,0xf6,0xf1,0xf1,0xd2,0xab, + 0xd5,0x18,0xd7,0x12,0xfc,0x9d,0x8d,0x65,0x3c,0x8d,0x4d,0x25,0xd5,0xdb,0xaf,0x22, + 0x2c,0xfd,0xd0,0xdb,0x31,0x4c,0x40,0x5c,0x8f,0xdc,0x58,0x64,0x0e,0x5a,0x13,0x44, + 0x4a,0xa4,0xd6,0x4b,0x77,0x69,0x47,0x6e,0xb4,0x69,0xbe,0xd2,0x00,0xe8,0x5b,0x5a, + 0x5a,0x97,0x8d,0xc7,0x8b,0xd2,0x19,0xe2,0x68,0x63,0x9f,0x0d,0x06,0x03,0x5b,0x07, + 0x07,0xfa,0xc3,0x5f,0x74,0xf8,0x7f,0x01,0xc0,0xbb,0xa1,0xb3,0xb4,0x90,0x04,0xa1, + 0x13,0x59,0xd8,0x08,0x9a,0x05,0x2f,0x37,0x38,0x1a,0x51,0x8c,0xfe,0xa9,0x10,0x36, + 0x94,0x78,0xa3,0xfb,0xd3,0x1b,0xe3,0xfd,0xfb,0x97,0xb7,0xb6,0x71,0xfd,0xf3,0x87, + 0x1f,0x8c,0xbb,0xeb,0x97,0xaf,0x1c,0xc7,0xb1,0x50,0x50,0x31,0x1f,0x1f,0xa2,0x67, + 0x40,0x9c,0xf2,0xea,0xc5,0xad,0xae,0x1e,0xca,0xea,0x03,0x96,0xc0,0x87,0x84,0x4e, + 0x5a,0x4f,0x09,0xa6,0x84,0xde,0x84,0x6e,0xe6,0x31,0x3c,0x58,0x80,0x61,0x4f,0xab, + 0x63,0xd3,0xf8,0x9f,0xff,0xa6,0x86,0x38,0xd7,0x50,0x6f,0x92,0x8c,0xa5,0xf2,0x78, + 0xf5,0x1f,0xff,0x84,0x2e,0xdd,0xc3,0xd0,0x91,0x41,0x86,0xc3,0xdd,0x8a,0x2b,0xec, + 0x87,0xf5,0xad,0x22,0x06,0x59,0xb1,0xca,0x74,0x09,0xc1,0xa1,0xe0,0x4c,0xd4,0x4c, + 0x62,0x33,0x26,0xf6,0x6e,0x06,0x2b,0x58,0x4d,0x4e,0x22,0xb6,0x63,0x94,0xda,0x39, + 0x8c,0x0b,0x5a,0xc3,0x40,0x1d,0x2c,0x20,0x30,0x17,0x5a,0x8b,0xfb,0x45,0x60,0x23, + 0xaa,0x62,0x85,0xb0,0x70,0xa7,0x5b,0x8f,0x89,0x4c,0xa4,0x92,0x40,0x34,0x46,0x47, + 0xca,0x57,0xe6,0xb1,0x5d,0x40,0xaf,0x54,0xc8,0xc9,0x45,0x98,0x47,0xae,0x3a,0xf8, + 0xe6,0x5d,0xac,0xac,0xff,0x1b,0xf2,0xdf,0xdf,0xd0,0xd3,0x07,0xb2,0x69,0x08,0x97, + 0xa2,0x22,0xde,0xbc,0x20,0xd1,0x3b,0x3a,0x3a,0x54,0x58,0xf7,0x40,0xe8,0x79,0x20, + 0x0e,0x2c,0xc8,0x83,0x4a,0x9f,0x37,0x61,0x60,0x52,0xb7,0x45,0x1e,0xd9,0x94,0x66, + 0xe3,0x61,0x84,0x3c,0x13,0x59,0x7c,0x00,0xb4,0x0b,0xaf,0x97,0x6b,0x27,0x50,0x1f, + 0x35,0xdd,0x15,0xa3,0x29,0x41,0x4a,0xfd,0x9a,0x5a,0x58,0x3e,0xe2,0x0b,0x1b,0x64, + 0x5a,0x36,0x06,0xcc,0xf0,0x14,0xef,0x5e,0x86,0x5f,0x80,0x6f,0x41,0x22,0x53,0x41, + 0xd1,0x11,0x3c,0x00,0xed,0x6d,0x03,0x7b,0xd8,0xf7,0xc8,0x10,0xa8,0xca,0xa9,0xc9, + 0xab,0x75,0xbc,0xc6,0x56,0xd8,0x05,0x35,0x1d,0x89,0x98,0xf6,0x7e,0x93,0xe2,0x44, + 0xec,0x40,0xc6,0x67,0x22,0xde,0x5f,0xa0,0x40,0x83,0xab,0x36,0xae,0x1e,0x4a,0x56, + 0x55,0x48,0xb6,0x45,0xee,0xd5,0xda,0xcf,0xc0,0xb7,0x61,0x48,0x21,0xc6,0x98,0x92, + 0xc6,0x98,0xf3,0xf3,0x62,0x9e,0x97,0x0e,0x67,0xec,0x43,0xa9,0x9a,0x69,0xbf,0xe0, + 0xd3,0x4a,0x4d,0x3d,0xf4,0xe8,0x1e,0xba,0x10,0x2d,0x40,0x13,0xcb,0x9d,0x21,0x1a, + 0x21,0xee,0x06,0x38,0xf3,0x00,0x39,0xff,0x27,0x70,0x1a,0x1c,0x3c,0x40,0x2a,0xaa, + 0xe7,0x0c,0x80,0x62,0x5d,0xd9,0x01,0xcd,0x35,0xf1,0x0f,0x18,0x6b,0x3e,0x5d,0xdd, + 0x20,0x69,0x23,0x0c,0x8b,0x61,0x6c,0x38,0x40,0x5d,0x81,0x5e,0x70,0x2a,0xeb,0x4e, + 0x72,0xe9,0xcb,0x13,0xeb,0xe8,0x48,0x99,0x09,0x6b,0xdd,0xb2,0x56,0x9e,0x30,0x29, + 0x38,0x95,0x64,0x28,0x81,0xb6,0x77,0x0c,0x5f,0xcd,0x21,0x51,0x04,0x9d,0x0b,0x3b, + 0x24,0xeb,0xec,0xa2,0x96,0x0d,0x72,0x0a,0xed,0x02,0x1e,0xc4,0xa3,0xa9,0x34,0xca, + 0x45,0xf1,0x10,0x83,0xcb,0x1c,0x48,0x7b,0x89,0xd8,0xba,0xf7,0xff,0xf2,0xfe,0xcd, + 0x4f,0x20,0x58,0xe8,0x45,0xc6,0xd3,0x55,0xf7,0x63,0x6a,0x6b,0x04,0x3c,0x3a,0x52, + 0xba,0xc0,0x56,0x3c,0xf1,0x89,0xd6,0x87,0x6d,0xdd,0x1f,0xfa,0xbe,0xb0,0x8d,0x02, + 0xd3,0xd2,0x52,0xde,0x2b,0x44,0x6b,0x1b,0x13,0x5e,0x39,0xcc,0xe7,0xe7,0x58,0x70, + 0xf6,0x12,0x7c,0x45,0xe0,0x3d,0x6e,0xf0,0xec,0xe1,0x40,0xa0,0x93,0x86,0x41,0xa7, + 0x4b,0x45,0x1b,0x62,0x49,0x68,0xf1,0x18,0xe0,0xe3,0x6b,0xf8,0xe1,0x08,0x27,0x8a, + 0x20,0x24,0x80,0xeb,0x18,0x36,0x52,0xfc,0xf0,0xe1,0xf5,0x2b,0x85,0xee,0x2b,0x13, + 0x74,0xc1,0x65,0x12,0xcb,0xec,0xc1,0x76,0xdd,0x4b,0x67,0xf4,0x53,0x5b,0x6f,0x41, + 0x58,0x15,0x8f,0x4c,0x0e,0x1f,0x82,0x5c,0x4f,0x6b,0x8a,0xd6,0x74,0xfb,0x45,0xa1, + 0x2f,0x76,0xac,0x36,0x8d,0x42,0x97,0xc4,0xbe,0x72,0xf0,0xf1,0x84,0xb8,0x62,0xc2, + 0xc7,0x47,0x68,0x4d,0xb5,0xfb,0x24,0xd6,0xa7,0x6f,0xc4,0x30,0x69,0x67,0x04,0x9a, + 0x36,0x3e,0x1e,0x5a,0xc7,0x66,0x23,0x3b,0x25,0x9f,0xcf,0x55,0x5c,0x63,0x1e,0xab, + 0x14,0x6a,0xb7,0x41,0xd0,0x2f,0x64,0xc6,0x41,0x87,0x2a,0xca,0x5e,0x35,0x17,0xc1, + 0x9a,0x1f,0x23,0xbb,0xef,0x8c,0xd4,0x20,0x39,0xa5,0x6b,0x9a,0x56,0x63,0x5a,0xc1, + 0x10,0x30,0x59,0xbc,0x63,0x92,0xce,0x88,0x1f,0xf0,0xec,0x1a,0x6c,0xee,0x4c,0x66, + 0x1b,0x61,0xd0,0xce,0xc4,0x8e,0xee,0xb2,0x62,0xc6,0xea,0x24,0xb8,0xa9,0xa1,0x89, + 0x8a,0x44,0x60,0x07,0x1f,0x87,0x9f,0x1a,0xea,0x85,0x6f,0x51,0xeb,0x08,0x6b,0x54, + 0x37,0xd8,0xd9,0x01,0xdd,0x0d,0x81,0xc2,0xbd,0xaf,0xf9,0x16,0x21,0x4b,0xba,0x7c, + 0x84,0x8d,0x79,0x3b,0x6b,0x23,0x07,0xd6,0x5c,0x04,0xe1,0x05,0x4b,0x43,0x9a,0xa1, + 0x9b,0xc4,0x8a,0x52,0xc8,0x09,0xa0,0x13,0xba,0x68,0x1f,0xde,0xe4,0x34,0x9d,0x20, + 0x8e,0x03,0x16,0x51,0xcc,0x19,0xdb,0xdc,0x5d,0x24,0x26,0x06,0x44,0x49,0xad,0xb1, + 0xb1,0xa4,0x82,0x80,0x75,0x20,0xbc,0x6e,0xa9,0x48,0xc9,0xae,0x60,0x82,0xb3,0x29, + 0xcc,0x06,0x3a,0x16,0x2e,0x31,0x1b,0x57,0x1a,0x68,0xdd,0x04,0x7d,0xbb,0xa8,0x9f, + 0xaf,0x4c,0x32,0xc2,0xbc,0x80,0x01,0xe4,0x1e,0xb8,0x17,0x34,0xa2,0x88,0x07,0xb9, + 0x8b,0x8c,0x18,0xc0,0x8c,0x0e,0x91,0xc1,0xd3,0x17,0x25,0x31,0x70,0x48,0x0a,0x7c, + 0x3e,0x19,0x97,0x05,0x7c,0x85,0xc6,0xc2,0xee,0xdc,0xf0,0xe3,0x39,0xf1,0x5f,0xc0, + 0x55,0x03,0xaa,0xa3,0xc7,0x46,0x26,0x53,0x3a,0x27,0x34,0xb9,0x34,0x90,0x25,0x40, + 0xcf,0x73,0x88,0x81,0xca,0x6b,0xd5,0x56,0x12,0x0c,0xa4,0x4d,0xfe,0xc9,0x70,0x80, + 0x07,0x13,0x3c,0x3d,0xa5,0x99,0x4c,0x6d,0xa1,0xcf,0xca,0x5a,0x81,0x81,0x44,0x03, + 0x85,0xeb,0x82,0xfe,0xcd,0xc0,0xf6,0x1d,0x7e,0x3e,0x3a,0xfa,0x4c,0xee,0x00,0x03, + 0x1d,0x64,0x62,0x46,0x02,0x82,0x9b,0xae,0x68,0x42,0x07,0x81,0x5b,0x7b,0xf3,0xf1, + 0xf1,0xb3,0x13,0xcc,0xb2,0x4b,0xff,0x7c,0x30,0xe0,0x6f,0xa5,0x19,0x95,0x9f,0xa5, + 0x6a,0xb4,0xd6,0xd5,0xbe,0x87,0x07,0x57,0x1f,0xc5,0x03,0xfb,0x33,0x27,0xdf,0x67, + 0x70,0x7d,0xe0,0x0f,0x84,0x56,0x9f,0x68,0xe3,0x5d,0xf3,0xd1,0xb4,0x00,0x2b,0x76, + 0x6a,0x2f,0xec,0xa5,0x50,0x84,0x15,0x00,0x81,0x91,0x81,0xb5,0xa6,0xf8,0xa0,0xf2, + 0xd0,0x7f,0xa5,0x17,0x39,0xab,0xab,0x13,0xf0,0xfe,0xb0,0x60,0x62,0x60,0x09,0x63, + 0x70,0x0f,0x6b,0x8a,0xe0,0x42,0xb8,0x5d,0x38,0x8b,0x0c,0x37,0xee,0xb1,0xae,0x41, + 0xd9,0xf8,0x2c,0x15,0x2b,0xc0,0x95,0xd6,0x39,0x05,0xbd,0x30,0x06,0x11,0xc7,0x54, + 0x00,0x87,0x93,0x14,0xc2,0x98,0x4b,0x07,0x4e,0x40,0x62,0xc5,0x63,0x28,0x9e,0x63, + 0x37,0x25,0xa8,0x42,0x25,0x61,0x27,0x4d,0xcd,0xfc,0x85,0x7b,0xe1,0xc0,0x68,0xe9, + 0xb1,0x79,0x14,0x51,0x99,0xa1,0xf7,0xad,0xa9,0x6c,0xca,0x48,0xce,0x33,0xd4,0x95, + 0x4a,0xed,0x98,0x15,0x46,0x2b,0x81,0xe2,0x0b,0xd7,0x28,0x79,0xea,0x9d,0x4f,0x88, + 0xe9,0x4d,0x4c,0x70,0xd3,0x7a,0x2a,0x6b,0xda,0x4c,0x7f,0xab,0xe9,0x79,0x6a,0xbc, + 0x9e,0x9a,0x12,0xa2,0x32,0x71,0x0e,0x0a,0x91,0x07,0x2a,0x20,0x05,0x5d,0x60,0x2c, + 0x40,0x8c,0xc8,0xb9,0x72,0xfc,0xf0,0x87,0x28,0x93,0x49,0x36,0x09,0x92,0x3e,0xa2, + 0x49,0xc4,0xab,0x18,0xde,0xf2,0xb3,0x01,0x8b,0xab,0x27,0xd3,0x21,0x34,0x6d,0xd0, + 0xce,0x20,0xb2,0x34,0x26,0x41,0xfa,0xa0,0x89,0xaf,0xb1,0xb7,0x85,0x62,0x50,0x9a, + 0x42,0x3b,0x2d,0x7c,0xb3,0x01,0x03,0x32,0x11,0xcc,0xde,0x17,0x8f,0x77,0xe0,0x9e, + 0x50,0x97,0xa2,0xe2,0x78,0xbf,0x17,0x4d,0x36,0x8f,0xb3,0x10,0x51,0x97,0xe3,0xa2, + 0xd6,0xf0,0xe6,0x65,0xa0,0xd4,0x68,0x3c,0xeb,0x88,0x74,0x94,0x79,0xbc,0x38,0x36, + 0xd1,0x5e,0x2c,0x90,0x35,0x82,0x6f,0x3f,0x3d,0x9c,0x64,0xf9,0x0a,0x33,0x5c,0xf9, + 0xaa,0x56,0xba,0x6a,0xa9,0x3d,0x39,0x35,0x1c,0xd3,0x28,0x9a,0x00,0xf8,0x3e,0xb4, + 0xe8,0xa7,0xd3,0xb6,0x41,0x41,0xd2,0x07,0x01,0xaa,0x83,0x05,0x44,0xec,0xa6,0x72, + 0xaa,0x70,0x52,0x50,0x50,0x3b,0x54,0xb6,0xf4,0x24,0xa0,0x43,0xb7,0xc6,0x34,0x62, + 0x19,0x91,0xdc,0x0e,0x00,0x55,0xca,0x05,0x67,0x8c,0x81,0x88,0xb6,0x4c,0xbb,0x08, + 0xb0,0xe9,0xbd,0x64,0x65,0x9a,0x76,0x8c,0xa3,0xa3,0x3e,0xe2,0x52,0x7c,0x0d,0xb6, + 0x7d,0x48,0x83,0xb3,0xc0,0x76,0x60,0x5a,0x56,0x14,0xed,0x79,0xa5,0xb6,0x57,0x7c, + 0xf5,0x35,0x06,0xc2,0xf1,0xbc,0xff,0xd2,0x27,0x9d,0xf3,0xf8,0x88,0x51,0x11,0x97, + 0x39,0xbd,0x5c,0x84,0x44,0x6a,0x37,0x37,0xdd,0x34,0x8e,0x88,0x38,0xe7,0xb8,0x06, + 0xe7,0xc3,0xa5,0x30,0x4d,0x62,0xaf,0xdd,0x25,0x72,0xf6,0xb2,0xc8,0x40,0xf0,0x55, + 0xa0,0x74,0x65,0xde,0x60,0x20,0xd1,0x88,0x9e,0x68,0xeb,0xa0,0xd8,0x43,0x3a,0x5c, + 0x98,0x81,0x97,0xe4,0x88,0x98,0x8c,0x40,0x45,0x0d,0x2c,0x2b,0x04,0x60,0x5d,0xaa, + 0x7d,0x68,0xcc,0x83,0x82,0x66,0xf3,0x03,0x22,0x8c,0x52,0x27,0x41,0xc9,0x7a,0x30, + 0x1d,0x16,0x88,0x3d,0xc0,0x03,0x7e,0x62,0x17,0x54,0xfa,0xe6,0xb0,0x9f,0x8c,0xff, + 0xb4,0xc5,0xcc,0x0f,0xc5,0x8a,0x43,0x00,0x3c,0x98,0x95,0x78,0xb4,0xc1,0xb3,0x22, + 0x45,0xb6,0x40,0x4b,0x62,0x0a,0xab,0xca,0x8d,0x93,0x60,0x0d,0x9c,0x0b,0x01,0x83, + 0x40,0x91,0x81,0xcf,0xc3,0xf8,0x3b,0xd9,0x5f,0x2a,0x17,0x00,0x7b,0x88,0x67,0x41, + 0x95,0x15,0x40,0x8c,0x38,0x1f,0x67,0x01,0xec,0x35,0x46,0x03,0xbb,0x08,0x92,0x64, + 0x05,0x2c,0x09,0xba,0x15,0x73,0x13,0xca,0x3e,0x11,0xcb,0x55,0x32,0x82,0xde,0x31, + 0xfc,0xe8,0x48,0x7c,0x6b,0x22,0x2e,0xdf,0xd3,0x62,0x37,0x7c,0x2d,0x19,0x61,0xef, + 0x18,0xe2,0x50,0xaa,0x8a,0x22,0xa2,0x8a,0xc0,0xa7,0xcc,0xd8,0x5e,0x5f,0x11,0x05, + 0x0c,0xcc,0xe0,0xcc,0x07,0xb7,0xe8,0x3d,0x95,0xf8,0x10,0xb3,0xda,0x45,0x7b,0xc4, + 0x3b,0xc4,0x1d,0x3c,0xc8,0xee,0xeb,0x80,0xa4,0x6c,0x7e,0x67,0x82,0x37,0x09,0x17, + 0xa0,0x2c,0x11,0x06,0xdf,0x54,0x55,0x88,0xd3,0xf8,0x0b,0xc4,0x70,0x58,0x3a,0x82, + 0x1f,0x4d,0x88,0x73,0x17,0x93,0xfd,0xdd,0x81,0x4d,0xff,0x36,0xdf,0x07,0x03,0x09, + 0x51,0x2f,0xb7,0x82,0x7b,0x52,0xf4,0xc4,0x3b,0x09,0xb8,0x9a,0xd7,0x6e,0x20,0xc6, + 0x55,0xc0,0x62,0x4a,0xb6,0xe1,0x61,0xe1,0xeb,0xfb,0x0e,0xef,0x8e,0x61,0x98,0x00, + 0xb6,0xc4,0xf6,0x99,0x53,0x30,0x34,0x34,0xd7,0x49,0x42,0xbb,0xc3,0x2f,0x26,0xcc, + 0xd0,0x07,0xe3,0x7b,0x2d,0x08,0x7d,0x60,0xcf,0xd7,0xb0,0x67,0xb5,0x00,0xfb,0xc2, + 0x26,0x37,0xd9,0x7c,0x0e,0x6c,0x26,0x95,0xca,0x66,0x82,0x5f,0x62,0xc0,0xdc,0x0d, + 0x1a,0xe5,0x5d,0xb3,0x36,0xc1,0xe3,0xcf,0x15,0x78,0x75,0x2a,0x24,0xbb,0xbf,0x12, + 0x1f,0x5b,0x80,0x3e,0xe0,0xeb,0x3c,0x00,0xc2,0xdd,0xba,0x05,0x5f,0xa5,0x97,0x1f, + 0x41,0xe0,0x2b,0x73,0xc7,0xa9,0x11,0x9b,0x63,0xbe,0x47,0xf9,0x3a,0x11,0xf8,0xe5, + 0x32,0x93,0x36,0xf1,0x23,0xe4,0x12,0xdc,0x3c,0x25,0x47,0x7d,0x70,0xbe,0xf0,0xf7, + 0xf1,0xf1,0xe3,0x27,0xcb,0x99,0x42,0xe8,0xc7,0x8a,0x46,0x72,0x4f,0x00,0x55,0x92, + 0x4b,0xb0,0x81,0x58,0xbe,0x1d,0xdd,0xe1,0x68,0x02,0x5f,0xe4,0xf6,0x22,0xee,0x29, + 0x53,0x7a,0x41,0xe4,0x23,0x75,0xfe,0x88,0x94,0x23,0xad,0xd2,0xb0,0x6b,0x3c,0x9a, + 0x73,0x23,0x07,0x7f,0x6c,0xd4,0x0f,0x6e,0xe8,0xe0,0xcf,0x46,0x3a,0x2a,0x22,0xcb, + 0x69,0xad,0xf3,0x45,0xd5,0xa5,0x89,0xa7,0x30,0xeb,0xdc,0xe6,0x43,0x2c,0x4f,0x35, + 0xf3,0x74,0xa7,0xcd,0x87,0x5b,0xc2,0x11,0x16,0x87,0xb5,0x5b,0x70,0x2c,0xf2,0x2b, + 0xf3,0x4d,0x8a,0x7a,0x8c,0xc3,0x84,0x6e,0x31,0x8f,0x7a,0xd1,0xc0,0x60,0xab,0xb8, + 0x96,0x29,0x2b,0xa9,0xe2,0x26,0x57,0x93,0xe3,0xee,0x04,0x54,0xd4,0x10,0xfc,0x03, + 0x91,0x39,0x77,0xe5,0x55,0x89,0x36,0x9f,0x1b,0x23,0x68,0x4c,0x33,0xa4,0xb5,0xb8, + 0xb5,0xe4,0x44,0x11,0x58,0x15,0xae,0x7e,0xf8,0x0e,0x50,0xdb,0x65,0xd3,0x29,0xe8, + 0xb7,0x37,0xd3,0x29,0x68,0xa6,0xba,0x15,0x4f,0xc4,0x78,0x33,0xb9,0xe3,0x8b,0x1c, + 0xdd,0x8c,0xb4,0xe5,0x26,0xc9,0x93,0x57,0x18,0xa9,0x7a,0xc2,0x96,0x0b,0x4c,0x72, + 0x38,0xd2,0x49,0x43,0x17,0x51,0x64,0xc3,0xa5,0xd7,0x47,0x01,0x3f,0x4f,0x75,0x14, + 0x22,0xec,0xe7,0x9f,0x56,0x80,0x60,0x3f,0x5a,0xcc,0xc7,0x28,0xc8,0x22,0x7f,0x7e, + 0xef,0xd5,0x69,0x75,0xbc,0x72,0xe6,0x41,0xfe,0x35,0x36,0x91,0x59,0x1a,0x74,0xa6, + 0xf4,0x88,0x19,0xdb,0x26,0x41,0xb3,0x8d,0x87,0x03,0x34,0xeb,0x56,0xbc,0x5b,0x0a, + 0xc3,0x8c,0x6c,0x9c,0xed,0x55,0x5f,0x19,0x05,0x4a,0x40,0xf8,0xdf,0x09,0x87,0xa5, + 0x41,0x1e,0xef,0xed,0xc5,0x9d,0x13,0xe8,0xf9,0x9b,0x5f,0x3a,0x05,0xec,0x65,0xe4, + 0xf7,0xce,0xce,0xae,0x9e,0xba,0xea,0xee,0xfc,0xd9,0xd5,0x69,0x7d,0xf7,0xec,0xe2, + 0xea,0xc4,0x15,0x29,0xa0,0x8c,0x67,0x1d,0xfc,0x52,0x09,0x80,0xd8,0xbe,0xae,0x88, + 0x32,0x61,0xbc,0xc7,0x0e,0x79,0x49,0x72,0x41,0xd9,0xb8,0x37,0x4a,0xaf,0xa3,0xf0, + 0x46,0x3b,0xbd,0x1c,0x50,0x9a,0xc7,0xbf,0x81,0x5f,0x26,0xce,0xc1,0xe3,0xf9,0xac, + 0x79,0xa8,0x59,0xc6,0xb3,0x34,0x40,0x86,0xfe,0xad,0x76,0xb9,0x20,0xfe,0x7b,0x8a, + 0x95,0x3a,0x30,0x69,0xbc,0xe7,0x47,0xc4,0xd8,0x12,0x32,0x4a,0xa0,0xf1,0x80,0xb7, + 0x91,0x76,0xd3,0xe2,0xe2,0xf1,0x2e,0xbf,0xaa,0x75,0x2a,0x21,0x07,0x4c,0xa2,0x7b, + 0xd4,0x88,0x3c,0xcf,0xe2,0xd5,0x27,0x15,0x54,0x41,0xdd,0x95,0x01,0x33,0xc4,0xd9, + 0x3a,0xf2,0xc6,0xd4,0x53,0x30,0xd0,0x8e,0x00,0xba,0xe6,0x3b,0x5e,0x61,0xa9,0x65, + 0x36,0x89,0xbd,0x78,0xf8,0x2b,0xfa,0x60,0x65,0x63,0xdd,0x41,0x7b,0x7e,0xf9,0x8c, + 0xd2,0x90,0x98,0xa0,0x93,0x59,0xa9,0xd6,0x26,0x2c,0x0e,0xfd,0x81,0x7e,0xe8,0x08, + 0x2a,0x88,0x25,0xf6,0x03,0x65,0xe2,0x65,0x02,0x0a,0x42,0xb8,0x07,0x4b,0xde,0xf8, + 0x0f,0x5a,0x77,0x3e,0xfb,0xba,0x06,0x43,0xc7,0x69,0xbd,0x47,0xa2,0x32,0x1e,0x87, + 0x60,0xb6,0xa5,0x02,0x0f,0xa5,0xec,0x9a,0xe0,0x45,0x98,0xd6,0x95,0x89,0xa5,0x61, + 0xc6,0x94,0x81,0x13,0x07,0x52,0x4f,0x37,0xf0,0x00,0x88,0xac,0xe4,0x54,0xec,0x66, + 0x73,0x50,0x2f,0xb2,0x87,0x3c,0xed,0xb5,0xaa,0x6c,0x36,0x4b,0x98,0x58,0xc9,0xe3, + 0xa0,0x6e,0xf8,0xb9,0xc1,0x35,0x77,0xb4,0xe8,0xc4,0x0e,0x3d,0x19,0x64,0x24,0xfe, + 0x7d,0x1a,0x3d,0xb6,0x62,0x58,0x32,0x82,0x59,0x6e,0xe0,0xf1,0x0c,0xa2,0x34,0x3a, + 0x75,0xa7,0x04,0xab,0x7e,0x76,0x90,0xb2,0xa0,0x10,0x02,0x8e,0x4a,0xbe,0x7c,0x7c, + 0xc4,0x34,0x3c,0x9e,0x9a,0xc1,0x95,0xda,0x05,0x78,0x3e,0xec,0xcb,0x1b,0xfc,0x3a, + 0xce,0xa8,0x37,0x54,0x67,0x0a,0xc4,0x96,0xa0,0x2d,0xba,0xa8,0x18,0x62,0x7f,0x60, + 0x27,0xc0,0x58,0x55,0xf6,0x2a,0x03,0x7c,0xdc,0x00,0x84,0x40,0x9d,0xf8,0xb2,0x8d, + 0x0a,0x2f,0x3e,0x3e,0xb6,0x64,0x12,0x51,0x3d,0xfc,0x18,0x7f,0x6a,0x8e,0x04,0x20, + 0x12,0xb9,0x90,0xde,0x4b,0x3f,0xad,0x30,0x1b,0xc4,0x47,0x76,0xc0,0xdd,0x69,0xb9, + 0x70,0xda,0x5c,0x8b,0x6d,0x1c,0xb0,0x99,0xf3,0xae,0x25,0x99,0x14,0x2b,0x3c,0xdb, + 0x19,0x1a,0x8f,0xb7,0x6b,0x8c,0x9b,0x7a,0xf2,0xfc,0x30,0x05,0x3f,0x10,0x7e,0xaf, + 0x86,0xe0,0x3a,0xd5,0xa0,0xa4,0x4d,0x36,0x04,0xf2,0x8a,0xc3,0x81,0xa5,0x2f,0x06, + 0xb6,0x64,0x8d,0x4e,0x96,0x30,0xc5,0x64,0x51,0xee,0xd3,0xd8,0x96,0xbd,0x03,0x0e, + 0x04,0xb8,0x58,0xfb,0x79,0x46,0x17,0xe9,0x9d,0x7b,0xf1,0x38,0x28,0xc0,0x37,0xfc, + 0x31,0xe6,0xaa,0x76,0x4f,0x47,0xe7,0xa4,0xbb,0x51,0xd5,0x98,0x44,0x3c,0xcf,0x52, + 0x2a,0x41,0xd1,0x67,0x68,0x6c,0x90,0x77,0x16,0x5b,0x92,0x9d,0xe9,0x99,0x76,0xa6, + 0x9a,0x2f,0xbb,0x59,0x4a,0xe4,0xaa,0xfb,0x92,0x42,0xce,0xd2,0x2b,0x93,0xfc,0x49, + 0xd7,0x54,0xe1,0x8b,0xa0,0x57,0xbe,0x6c,0x9f,0x39,0x60,0xe7,0x1f,0xe2,0x90,0x09, + 0x09,0x34,0x3d,0xd5,0xab,0x64,0xd5,0x75,0x05,0x7b,0x00,0xf5,0x8e,0x82,0xa4,0xd5, + 0x76,0x9a,0x76,0xc6,0x33,0x5d,0xaa,0xef,0x0e,0x94,0x00,0x78,0x2d,0xb8,0xc0,0x17, + 0x50,0xf0,0x90,0x30,0xaa,0xad,0x10,0x29,0x25,0xe3,0x51,0x1e,0x76,0x37,0x2e,0xf1, + 0xb3,0x63,0x7e,0xfb,0x64,0xc7,0xce,0xa5,0x8f,0x45,0x19,0x1f,0xc9,0xf5,0x18,0xa0, + 0xd2,0x7b,0x3d,0x75,0x5d,0x3b,0x09,0xb9,0x88,0x47,0xa5,0x5a,0x44,0xbc,0x0f,0x2d, + 0x62,0x08,0xc5,0x2f,0x82,0x89,0xc4,0xd9,0xa8,0xc8,0x4f,0xc1,0x9a,0x47,0x47,0x87, + 0xda,0x61,0x15,0x58,0x93,0x79,0xd7,0x94,0x79,0x89,0xcc,0xe8,0x80,0xe2,0x22,0x17, + 0xac,0x83,0x07,0x12,0x57,0x7f,0x4d,0xff,0x9a,0x7e,0x50,0x59,0xa2,0x65,0x0c,0x9a, + 0x0d,0x53,0x45,0xc0,0xf3,0xe0,0xc4,0xa6,0x21,0xcf,0x2a,0xa9,0xe8,0x5a,0xfb,0xc7, + 0x8c,0xa7,0x22,0xe2,0x5b,0xea,0x51,0x5f,0x18,0x4f,0xa7,0xac,0x50,0x59,0x44,0xec, + 0x81,0x59,0x10,0x2c,0xbd,0xc6,0xe2,0xc5,0x0c,0x62,0x4f,0x4b,0xcb,0x85,0xe5,0x3e, + 0xd5,0x90,0x68,0x07,0xc1,0x36,0x61,0xd4,0x87,0x90,0x37,0x9d,0x74,0x45,0x72,0xec, + 0x08,0x09,0x22,0x9a,0xda,0x47,0xed,0xc7,0xb8,0xe5,0x2b,0xf3,0x48,0xe4,0xbb,0xfd, + 0x21,0x25,0x83,0x08,0x6f,0x6f,0xef,0x1c,0xfc,0x0e,0x9f,0x38,0xfc,0x19,0x57,0xe8, + 0x6d,0xe4,0x3b,0x0e,0x98,0xa5,0xa2,0x3a,0x0c,0xf1,0x93,0x64,0x1a,0x65,0x42,0x7c, + 0x0f,0x2e,0x2b,0xea,0x6a,0x0b,0x8d,0x28,0xdc,0xfa,0xb5,0x84,0x53,0x87,0x0c,0xbd, + 0xad,0xdd,0xc7,0x7b,0x9e,0xa8,0x57,0xf0,0x80,0xf9,0xa8,0xa1,0x56,0x19,0xfc,0x8c, + 0x02,0x99,0x82,0x9f,0x5e,0xe0,0xe9,0x2d,0x21,0x41,0x1d,0xc9,0xc0,0x3d,0xd1,0x12, + 0x28,0x63,0x69,0xf9,0x56,0xf9,0x69,0x43,0x99,0x0e,0xc5,0x41,0x6e,0x28,0x92,0xd8, + 0x39,0xa6,0x3c,0x97,0xd1,0x0a,0x7f,0x82,0x59,0xe6,0x0e,0x64,0x62,0x83,0x32,0x9d, + 0xf4,0xb9,0x3b,0x32,0xea,0xb6,0x26,0x18,0x08,0x04,0xb1,0x11,0x1d,0xed,0xf2,0x4a, + 0x05,0x74,0x77,0x6b,0xe3,0x33,0x0f,0x56,0xc6,0x58,0x95,0x00,0x80,0xdb,0xed,0x1a, + 0xf4,0x89,0x3c,0x3d,0xbd,0xe7,0xb6,0x30,0xb7,0x91,0x02,0x39,0x25,0x71,0xe4,0xf6, + 0xca,0xd7,0x8f,0xe3,0x99,0x03,0xe2,0xfb,0x00,0x0c,0x74,0xcb,0x3f,0xeb,0x02,0xa0, + 0x91,0xdc,0x35,0x24,0x11,0xb3,0xfe,0xb4,0xb5,0xb1,0x32,0x6a,0x8a,0xd5,0xf9,0x91, + 0x83,0xc6,0xe7,0xc4,0xe3,0x2f,0xa7,0x5a,0xf5,0x0d,0x32,0x2a,0x56,0x0b,0x23,0xdf, + 0x2b,0xde,0xad,0xa8,0x5e,0xb3,0xdc,0x66,0x77,0x53,0x1d,0xa5,0x80,0xd3,0x5e,0xc5, + 0x49,0xfd,0xd2,0x0f,0xd6,0x5e,0x36,0xf9,0x59,0xb2,0xdc,0xd8,0x6e,0x57,0x38,0x5c, + 0xb5,0xd9,0xba,0xae,0x90,0xba,0x7d,0xf1,0xea,0xc5,0x87,0x17,0x66,0x5d,0xd9,0xb4, + 0x8f,0x47,0x45,0x46,0x0b,0xd9,0xf4,0xca,0x04,0x9b,0x3c,0xcb,0x2a,0x83,0x6f,0xd3, + 0xdd,0x66,0x55,0xde,0x2b,0xbb,0x07,0x1a,0xc8,0xcc,0x96,0x78,0x3b,0x0a,0xc5,0xe3, + 0x2b,0x94,0x97,0x79,0xb3,0x06,0xe5,0x9a,0xc3,0x37,0xed,0x40,0x18,0x98,0x15,0xdf, + 0x94,0xaa,0xd5,0x63,0x5c,0x9f,0x12,0xaa,0xd3,0xe9,0xe6,0x59,0xa1,0xfc,0xb4,0x65, + 0x53,0xfe,0xfb,0xd4,0xd5,0x36,0xe5,0xe1,0x94,0x79,0x1c,0xff,0xae,0xc4,0xea,0x1f, + 0x11,0xdc,0xc2,0x83,0x8c,0x02,0x76,0x9f,0x3b,0xca,0xc2,0x92,0x58,0x3a,0xb5,0xc4, + 0x9c,0x02,0x5c,0x30,0x17,0x74,0x30,0xab,0xdb,0x8b,0x7a,0x1e,0xac,0x86,0x6a,0x5a, + 0x42,0xf9,0xfa,0xd5,0xde,0xb4,0xa7,0xe4,0x0e,0x64,0x3c,0xbb,0xc6,0x59,0x7b,0x83, + 0x31,0x2c,0x43,0x14,0x50,0x35,0xdc,0x86,0x3a,0x38,0x14,0xa7,0x9a,0x28,0x89,0xfc, + 0x55,0x2c,0xd4,0xe9,0x58,0x40,0xb7,0x32,0x75,0xa2,0x8a,0x8c,0xa8,0x2e,0x58,0x34, + 0x25,0x2f,0x32,0xd3,0x85,0xd1,0x93,0xe7,0x40,0x5c,0xa8,0x41,0xef,0x63,0x3c,0x5b, + 0x82,0x64,0xe3,0xc1,0x39,0xb8,0x65,0xc5,0x03,0x3f,0x97,0x5a,0xf1,0x53,0x55,0xf4, + 0x64,0x3c,0x23,0x62,0x09,0xaf,0x23,0x97,0x45,0x22,0xbf,0x2d,0xd8,0x02,0xdc,0x3f, + 0x44,0x03,0x87,0x6b,0x2f,0x12,0x1a,0xc2,0xba,0xf5,0x6e,0x1a,0xa5,0x13,0xe5,0xce, + 0x14,0x54,0xa4,0xe2,0x75,0x39,0xe3,0xe5,0x67,0x29,0x96,0x7a,0xe5,0x7e,0x8d,0xc9, + 0x6d,0x44,0x8a,0x43,0x5f,0xe8,0x39,0xf4,0x38,0x52,0xf9,0xe4,0x08,0x33,0x22,0x71, + 0x07,0x8a,0xff,0xf5,0x8f,0xff,0x02,0x06,0x8c,0xd3,0x69,0x26,0x99,0x67,0x4f,0xd1, + 0x52,0x5f,0xec,0xb4,0x55,0xeb,0x08,0x72,0xcc,0x13,0x54,0xda,0xbe,0x45,0x6d,0xd2, + 0x56,0x75,0x92,0x0a,0xe7,0x81,0x35,0x1a,0x1f,0x09,0xed,0x8a,0xe4,0xf3,0xa6,0x61, + 0xc9,0x88,0x71,0xe0,0x06,0x13,0xd9,0xdd,0xdc,0xda,0xce,0x95,0xe3,0x29,0x09,0x5e, + 0x3a,0x05,0x4b,0xb2,0x00,0xd5,0x66,0xa3,0xd6,0x89,0x8b,0x02,0xa2,0xa1,0xa4,0xa3, + 0xe3,0x1a,0xc3,0x61,0xcc,0xd3,0xce,0x93,0x0c,0x3f,0x41,0x19,0x4c,0xa8,0x56,0x86, + 0x14,0xa4,0xcc,0x95,0x23,0xb6,0xc0,0xe6,0x8b,0x0a,0x73,0x16,0xaa,0xa3,0xca,0xfa, + 0x7c,0x41,0x55,0xdd,0x6f,0x1d,0x07,0xa1,0x76,0xa5,0x73,0x84,0xe6,0xe9,0x8f,0xdb, + 0xe0,0x47,0x25,0x83,0xa0,0xea,0x79,0x1d,0x66,0x16,0x32,0x5d,0x0e,0xb5,0xcc,0xd3, + 0x2e,0xe7,0x54,0x56,0x06,0x6e,0x4b,0x26,0x4f,0x63,0xed,0xb4,0x37,0xb0,0xe6,0x0e, + 0x93,0x23,0x58,0x6c,0xee,0x23,0x0a,0x9c,0xb9,0xd0,0x5c,0xf9,0x43,0x03,0x04,0xde, + 0x98,0xf9,0x32,0xbd,0xc6,0x65,0x8e,0xc7,0x6e,0xbc,0xa2,0x43,0x49,0x93,0x5e,0x9d, + 0x29,0x24,0x8b,0xd7,0x3e,0xa2,0x59,0xe5,0x07,0xac,0xb2,0x4e,0x2f,0xb2,0xb0,0x06, + 0x84,0x1f,0x1e,0xf1,0x97,0x07,0xa8,0xc4,0xdf,0xe1,0x55,0x54,0x70,0x29,0x4b,0x91, + 0xba,0x87,0xd9,0xe3,0xe3,0xfc,0x10,0x3f,0xf2,0x8a,0x88,0x7a,0x7c,0xcc,0x1f,0xe8, + 0x86,0x32,0x72,0x1c,0x16,0x24,0xa4,0x3f,0xa7,0xf4,0x16,0x15,0xbd,0x9b,0x30,0x40, + 0x25,0xbb,0xf4,0x62,0x26,0xf0,0x4e,0x09,0x72,0x7c,0x00,0x5e,0x60,0x15,0x01,0x08, + 0xbc,0x40,0x2c,0x89,0xd3,0x7b,0x23,0x61,0x20,0x62,0x44,0xc5,0x30,0x5b,0xa6,0x5c, + 0x88,0xf3,0x07,0x19,0xc5,0x5f,0xe0,0xd2,0xb2,0xc8,0xe8,0xfc,0x54,0xf3,0xa0,0x7e, + 0x80,0x28,0xba,0x71,0x14,0x01,0xfc,0x74,0x81,0x1a,0xeb,0xfc,0x94,0xbe,0x11,0x1d, + 0xc0,0xda,0x45,0xd9,0xf2,0x75,0x71,0x8f,0x9a,0xef,0x3a,0xaf,0x73,0x76,0x1f,0x70, + 0x5a,0x1d,0x95,0xd0,0xac,0x79,0xb0,0x84,0xb2,0x58,0xf0,0x98,0xbb,0xff,0xf0,0xd3, + 0xd1,0x12,0x8c,0x12,0x01,0x54,0x58,0xa2,0xe9,0x41,0x3d,0xdb,0xd7,0x50,0x74,0xe4, + 0x22,0x17,0x2c,0x20,0x3f,0x07,0x8c,0xbf,0xab,0x10,0x7d,0x65,0xbe,0xe3,0xe4,0xd6, + 0x41,0x6c,0x83,0xc7,0x5f,0xd7,0x00,0xb8,0x71,0xb4,0xf1,0x8e,0xd1,0x31,0x0f,0xf8, + 0x2a,0x3c,0xa5,0xab,0x24,0xa9,0xc3,0x89,0xd0,0xc0,0x1d,0xf4,0xca,0x1f,0xc0,0x9f, + 0x11,0x27,0x89,0xb5,0xa4,0xd2,0x4b,0x27,0xe2,0x94,0x85,0x34,0xea,0x95,0xb6,0x11, + 0x8e,0x8f,0x7a,0x1f,0x78,0x6c,0xbf,0x10,0xc7,0x2e,0x12,0x8d,0xb3,0x0c,0x88,0x1b, + 0xd2,0x7b,0x33,0xd9,0xd2,0xad,0xa5,0x57,0x56,0x00,0x54,0x7c,0x85,0xbd,0x8e,0xbb, + 0x2c,0xc5,0xb4,0x4d,0xca,0xa6,0x72,0x27,0x67,0xde,0x72,0xdc,0xf3,0x87,0x2d,0xaf, + 0x9c,0xbf,0x18,0xf0,0x07,0xbd,0x72,0x2a,0xb0,0xfc,0x9a,0x5f,0xde,0x2c,0x10,0x6e, + 0x7b,0xd6,0xc8,0x92,0xbc,0xe0,0xeb,0xb8,0xab,0x92,0xf5,0xad,0xdc,0x30,0xe9,0x3a, + 0x8d,0xc7,0x44,0x48,0xa1,0xd2,0xfd,0x5b,0x2e,0x13,0x81,0x09,0x94,0xb5,0xc4,0x02, + 0xdb,0x4e,0xaf,0xd8,0x0a,0xf6,0xa9,0x91,0xb8,0x17,0x52,0x3e,0x0d,0x67,0x2a,0xa1, + 0x43,0x64,0xf5,0xa3,0x04,0x8b,0x7b,0x00,0xe2,0x94,0x50,0x70,0x8f,0xb9,0x83,0x6b, + 0x90,0x69,0x5c,0x53,0x1f,0x09,0x1c,0x31,0xcb,0x70,0x3a,0xa4,0x78,0xc3,0xd2,0xa1, + 0xea,0x52,0x6f,0x1c,0xe1,0x1b,0xbf,0x0c,0x2b,0xf9,0x30,0x98,0xc3,0xb3,0xf9,0xa7, + 0x46,0x69,0xf1,0x3a,0x3f,0x3c,0x46,0x95,0x34,0x97,0x72,0x25,0x8f,0xe9,0x64,0x4c, + 0x88,0x45,0x1d,0xb2,0xa4,0x0f,0x37,0x62,0x61,0x2d,0x60,0xc2,0xba,0xb9,0xfd,0x14, + 0xcb,0x29,0xbe,0x42,0xef,0x2e,0x15,0xae,0x65,0x98,0x46,0xe7,0xd1,0x91,0x55,0xdb, + 0x48,0x32,0x7c,0x5b,0xb6,0x72,0x2d,0x3e,0x4a,0xed,0x0e,0x2f,0x60,0x6e,0x88,0x6e, + 0xb6,0x0d,0xa3,0x2c,0xfa,0xe1,0xf4,0x21,0xb1,0xc9,0x1a,0xba,0xc4,0x6c,0xfb,0xc4, + 0xaa,0xc8,0x67,0xa7,0x39,0x27,0x2b,0x55,0xfb,0xbf,0x72,0x6f,0xf3,0x3a,0x41,0xbf, + 0xfb,0x53,0xde,0xd6,0x5a,0x7c,0xde,0x7d,0xa5,0xf3,0xcf,0xaa,0xdb,0xfa,0x20,0xf7, + 0x0a,0x67,0xc2,0x17,0x37,0xea,0x0f,0xe2,0xf7,0xe9,0x8b,0x0e,0x97,0x7d,0xfe,0xf9, + 0xfd,0xff,0x05,0x0b,0x53,0xe7,0x7c,0x8f,0x5f,0x00,0x00, }; constexpr size_t WIFI_INDEX_HTML_GZ_LEN = sizeof(WIFI_INDEX_HTML_GZ); // diff --git a/firmware/toolchain/README.md b/firmware/toolchain/README.md index 45d1bcb9..8ca9933a 100644 --- a/firmware/toolchain/README.md +++ b/firmware/toolchain/README.md @@ -4,12 +4,13 @@ The repository-level scripts around the firmware: what builds a module or a pack **Building things.** `build_module.py` compiles a module folder into a `.pfm` with the Xtensa toolchain; `port_preset.py` turns a curated preset header into a module folder; `make_pack.py` bundles built modules into the shareable Basics pack. `build_clock_glyphs.py` generates the Clock edition's digit glyphs. `release.py` is the two-command release (cut, publish) described in [`docs/RELEASING.md`](../../docs/RELEASING.md). -**Console pages.** The device serves each console page from a C string literal in a header. `console_pages.py` splices `../patternflow/console/*.html` into those headers (`build` to regenerate, `check` in CI); `console_serve.py` previews the pages locally; `build_audio_in_page.py` assembles the `/audio-in` page from the audio extension's editor in `tools/`. How to edit a page is [`../patternflow/console/README.md`](../patternflow/console/README.md). +**Console pages.** The device serves each console page from a C string literal in a header. `console_pages.py` splices `../patternflow/console/*.html` into those headers (`build` to regenerate, `check` in CI). One page is not written as a page: `/audio-in` is assembled by `build_audio_in_page.py` from the audio extension's mapping editor in `tools/patternflow-audio-extension/` and the panel's own half in `../patternflow/console/_audio_in_*`, with comments and indentation dropped on the way. `console_pages.py build` runs that step first, so one command regenerates everything; `console_pages.py check` and `build_audio_in_page.py --check` fail when the generated page is not what its sources assemble to, or is over its size budget (`BUDGET` in the script: bytes gzipped, as the panel sends the page). `console_serve.py` previews the pages locally against a mock panel, assembling `/audio-in` from its sources on every request; its audio endpoints are a port of the firmware's handlers, with `/mock?audio=` choosing what the panel hears, and when a handler in `features/audio_in/` or `features/audio/` changes that port changes with it. How to edit a page is [`../patternflow/console/README.md`](../patternflow/console/README.md). **Checks.** These are what `firmware-checks.yml` runs on every pull request that touches the firmware, and what you run before pushing: - the boundary rule — `check_boundaries.py`: the core references no feature, no feature flag, no feature name on a core page (the rule itself is [`docs/EDITIONS.md`](../../docs/EDITIONS.md)); - the frozen host ⇄ module ABI — `check_abi_freeze.py`, `check_module_elf.py`; +- the module SDK's inline libm — `check_libm.py` and `check_module_libm.py`: `abi/pf_libm.h` puts inline code behind `floorf`, `fmodf` and five others in every `.pfm`, on the claim that each returns the libm result bit for bit. `tests/libm_test.cpp` holds the seven to that: every one of the 2^32 floats through the one-argument functions and about three billion comparisons through the rest, against the libm of the machine running it; it takes a minute or two. What a PC cannot compile is the half of the header only a module build has, so `check_module_libm.py` builds `tests/modules/_libm_shapes` with the Xtensa compiler and reads the object: every spelling the header claims is inline, with the library call left only as its fallback. It needs the toolchain, and CI runs it in the firmware build job. What the two hold is the seven functions; a rebuilt pattern's own arithmetic can still differ in its last bit, because `-O2` and inline code move where GCC fuses `a*b+c` (`abi/pf_libm.h` says where that shows); - the preset twins — `check_presets.py`: `web/src/lib/presets/` and `../patternflow/presets/` stay in step, with the deliberate exceptions listed inside it; - versions — `check_versions.py`: the version the firmware reports, the editions, `AGENTS.md`, the flasher manifest and the shelf all agree; - images — `check_footprint.py` (each edition's size against its baseline); @@ -17,6 +18,6 @@ The repository-level scripts around the firmware: what builds a module or a pack - the vendored web server — `check_parser.py`: the real `src/webserver/Parsing.cpp` against a scripted socket and a fake clock, failing on a wait that does not sleep or does not end. Its `KNOWN:` lines are what the parser does today that the test would otherwise fail, pinned so that fixing one turns the check red until the pin is removed; - `check_sources.py`, a fast pre-compile sanity pass. -`tests/` also holds `modules/_ctor_probe/`, a module that exists only to exercise the loader's `.init_array` path; build it with `build_module.py` and inspect the ELF as its header comment says. Beside it, `modules/_crash_probe/` writes through a null pointer from `draw()` when a knob moves: the bench case for the crash record in `/api/status`. And `modules/_hang_probe/` is a module whose `draw()` stops returning eight seconds after it is picked - the bench tool for "the console outlives a hung render loop" (`src/core_loop_sync.h`), which no well-behaved pattern can show. With K1 or K2 turned while it counts down the stop ends after thirty or twenty seconds, for the other half: what the loop does when it comes back to requests that gave up on it. Its header comment has the three commands. +`tests/` also holds `modules/_ctor_probe/`, a module that exists only to exercise the loader's `.init_array` path; build it with `build_module.py` and inspect the ELF as its header comment says. Beside it, `modules/_crash_probe/` writes through a null pointer from `draw()` when a knob moves: the bench case for the crash record in `/api/status`. And `modules/_hang_probe/` is a module whose `draw()` stops returning eight seconds after it is picked - the bench tool for "the console outlives a hung render loop" (`src/core_loop_sync.h`), which no well-behaved pattern can show. With K1 or K2 turned while it counts down the stop ends after thirty or twenty seconds, for the other half: what the loop does when it comes back to requests that gave up on it. Its header comment has the three commands. `modules/_math_probe/` is the on-board half of `check_libm.py`: it runs the SDK's inline `floorf`/`fmodf` and the firmware's own libm side by side for 1.3 s in `setup()` and puts the verdict in its own name (`"active": "math ok 9/9 ..."` in `/api/status`), then keeps comparing every frame, green or red. It is how to tell that a board's FPU does what the PC proof assumes - above all that its multiply-subtract is fused. `module.ld` is the linker script every `.pfm` is linked with. diff --git a/firmware/toolchain/build_audio_in_page.py b/firmware/toolchain/build_audio_in_page.py index dce59f60..ba1740c2 100644 --- a/firmware/toolchain/build_audio_in_page.py +++ b/firmware/toolchain/build_audio_in_page.py @@ -1,447 +1,476 @@ # -*- coding: utf-8 -*- -"""Assemble console/audio-in.html from the extension's mapping editor. - - python firmware/toolchain/build_audio_in_page.py - python firmware/toolchain/console_pages.py build # then bake the header - -The editor module (tools/patternflow-audio-extension/editor.js) is the single -source of truth for the mapping UI - the port philosophy is COPY, not -re-extract (port_audio_ui.py's function-lifting era ended with it; that script -is retired). This assembler: - - - reads editor.html, editor.css and editor.js from the extension, - - swaps the cream instrument tokens for the console's dark ones (editor.js - reads its canvas colors from CSS variables, so the theme travels free) - and adds the phone layout the extension's wide tab never needed, - - injects a device bar (microphone switch, input gain, reset) above the - editor - the two controls that exist only on this side, - - replaces the chrome adapter with a fetch('/api/audio-in') adapter that - also converts levels between the device's linear scale and the editor's - dB-normalized display axis (calibration constants live HERE, in JS, - tweakable without a reflash). - -Run it after editing the editor, then console_pages.py build. CI's page sync -check keeps the generated header honest; nothing checks that YOU reran this - -the marker comment carries the source hash so drift is at least visible. +"""Assemble console/audio-in.html, the panel's /audio-in page, from its sources. + + python firmware/toolchain/console_pages.py build # the build: this page, then every header + python firmware/toolchain/console_pages.py check # everything in sync, this page included + + python firmware/toolchain/build_audio_in_page.py # only assemble this page + python firmware/toolchain/build_audio_in_page.py --check # CI: page == sources, and inside its budget + --sketch DIR work on another copy of firmware/patternflow (as console_pages.py) + +/audio-in is the one console page that is not written as a page. Its editor is +the browser extension's mapping editor, copied, never re-extracted, so the two +cannot drift apart; what is the panel's own sits beside the other console +sources. This script holds none of their text: + + tools/patternflow-audio-extension/ + editor.html the markup: what is between its two EDITOR BODY comment lines + is taken (the editor itself; the extension's own source bar + is above them and stays behind), and the \n' - '\n' - 'Patternflow - Mic mapping\n' - '\n' - '\n' - '\n\n' - + body + - '\n\n' - '\n' - '\n\n' -) +SKETCH = ROOT / 'firmware' / 'patternflow' + +PAGE = 'audio-in' +EDITOR_HTML, EDITOR_CSS, EDITOR_JS = 'editor.html', 'editor.css', 'editor.js' +EXT_ADAPTER = 'editor-adapter.js' # the extension's PFAdapter; the panel's goes in its place +BAR_FILE, CSS_FILE, ADAPTER_FILE = '_audio_in_bar.html', '_audio_in.css', '_audio_in_adapter.js' +# The line a shared script is taken down to: a comment that says only this +# (a rule drawn around the words is fine; a sentence that mentions them is not). +EXTENSION_ONLY = re.compile(r'//\W*EXTENSION ONLY\W*') +EXTENSION_ONLY_CSS = re.compile(r'/\*\W*EXTENSION ONLY\W*\*/') +# editor.html: the editor itself is what is between these two lines. Above them +# is the extension's own source bar, which the panel replaces with its frame. +BODY_BLOCK = re.compile(r'(.*)', re.S) +# _audio_in_bar.html: the line where the editor's body goes. +BODY_SLOT = re.compile(r'') + +# Bytes of the page as the panel sends it: stamped by console_pages.py (the +# chrome's ?h= and the fallback PF), gzip -9. This is the heaviest page the +# console has, and on the panel's own hotspot (2-5 KB/s measured) a kilobyte is +# 0.2 to 0.5 s before anything paints - so, like check_footprint.py's PINS, it +# is pinned, and growing it is a decision somebody makes rather than something +# that happens. It was 19,920 before the page stopped shipping its sources' +# comments and about 15,300 right after. To raise it, change the number and say +# in the commit what the bytes bought. (zlib builds differ by tens of bytes for +# the same input; a page that close to the line is over it.) +BUDGET = 17500 + +# The panel's TCP send buffer (lwIP TCP_SND_BUF; console_serve.py's TCP_WINDOW +# is the same number, and its --slow link is where this was measured). A reply +# leaves one of these at a time and the next waits for the last to be +# acknowledged, so a page one byte into another window costs a whole round +# trip more: 0.4 s there, for the document and for everything behind it. The +# budget is the line the build holds; a multiple of this is the line worth +# getting under inside it, and weight() says how full the page's last window +# is: a few dozen bytes in it are a round trip that taking them out gives back. +# This is the mock's model of the link, counted on the body alone. On a panel +# the reply's headers go into the first window too, and lwIP may start with +# less than a full buffer; where the steps fall there has not been measured. +WINDOW = 5760 + +# The one comment that does travel: whoever opens the generated page is told +# not to edit it. Short, because the panel serves it too. +GENERATED = '' + + +class BuildError(ValueError): + """A source this page cannot be assembled from; the message says which.""" + + +# ── the sources ──────────────────────────────────────────────────────────── + +def console_dir(sketch=None) -> Path: + return Path(sketch or SKETCH) / 'console' + + +def out_path(sketch=None) -> Path: + return console_dir(sketch) / (PAGE + '.html') + + +def shown(path) -> str: + try: + return Path(path).resolve().relative_to(ROOT).as_posix() + except ValueError: + return str(path) + + +def text_of(path: Path) -> str: + try: + return path.read_text(encoding='utf-8') # universal newlines: LF from here on + except OSError as e: + raise BuildError('%s: %s' % (shown(path), e.strerror or e)) + + +SCRIPT_TAG = re.compile(r'') + + +def script_files(editor_html: str, sketch=None) -> list: + """(name, path) of each script the page carries: the ones editor.html + loads, in its order, with the extension's adapter swapped for the panel's.""" + names = SCRIPT_TAG.findall(editor_html) + if len(names) != editor_html.lower().count('; one here is not') + for need in (EXT_ADAPTER, EDITOR_JS): + if names.count(need) != 1: + raise BuildError('%s: must load %s exactly once' % (EDITOR_HTML, need)) + return [(ADAPTER_FILE, console_dir(sketch) / ADAPTER_FILE) if name == EXT_ADAPTER + else (name, EXT / name) for name in names] + + +def sources(sketch=None) -> list: + """The files the page is assembled from. A preview server watches these + to know the page changed; it never raises, so that it can.""" + console = console_dir(sketch) + try: + scripts = [path for _, path in script_files(text_of(EXT / EDITOR_HTML), sketch)] + except BuildError: + scripts = [console / ADAPTER_FILE, EXT / EDITOR_JS] + return [EXT / EDITOR_HTML, EXT / EDITOR_CSS, console / BAR_FILE, console / CSS_FILE] + scripts + + +# ── what does not travel ─────────────────────────────────────────────────── +# Each takes a source's text and the name to blame, and returns it lean. See +# the docstring for why these are line rules and what they refuse. + +def lean_lines(lines) -> str: + return '\n'.join(s for s in (line.strip() for line in lines) if s) + + +def lean_js(text: str, name: str) -> str: + out = [] + for n, line in enumerate(text.split('\n'), 1): + s = line.strip() + if EXTENSION_ONLY.fullmatch(s): + break + if s.startswith('//') and '*/' in s: + raise BuildError( + '%s:%d: a // line holding */. If it closes a block comment, dropping ' + 'the line would leave that comment open; reword it.' % (name, n)) + if not s or s.startswith('//'): + continue + if s.count('`') % 2: + raise BuildError( + '%s:%d: an odd number of backticks. A template literal that runs past ' + 'the end of its line would have its inside stripped; keep each on one ' + 'line (or build the string with +).' % (name, n)) + if s.endswith('\\'): + raise BuildError( + '%s:%d: the line ends in a backslash. A string continued onto the next ' + 'line would lose that line\'s indentation; join it with + instead.' % (name, n)) + out.append(s) + text = '\n'.join(out) + if ' early' % name) + return text + + +# A CSS line that is left as written: it holds a string, an escape or a url(), +# and telling the inside of one from the sheet's own punctuation is what would +# need a tokenizer. +CSS_AS_WRITTEN = re.compile(r'''["'\\]|url\(''', re.I) + + +def lean_css(text: str, name: str) -> str: + text = EXTENSION_ONLY_CSS.split(text, 1)[0] + out, at = [], 0 + while True: + a = text.find('/*', at) + if a < 0: + out.append(text[at:]) + break + out.append(text[at:a]) + before = ''.join(out).rsplit('\n', 1)[-1] # this line, earlier comments already gone + if before.count('"') % 2 or before.count("'") % 2: + raise BuildError( + '%s:%d: /* inside a quoted string; this build would cut it out as a comment' + % (name, text.count('\n', 0, a) + 1)) + b = text.find('*/', a + 2) + if b < 0: + raise BuildError('%s:%d: a /* comment that never closes' + % (name, text.count('\n', 0, a) + 1)) + # A comment separates tokens (`a/**/b` is not `ab`), and it keeps its + # line breaks so that a line number below is still the source's. + out.append(' ' + '\n' * text.count('\n', a, b)) + at = b + 2 + + # White space the sheet does not need. Inside a line: around { } ; and , + # and after a colon (never before one: `a :hover` is not `a:hover`), and + # the semicolon that ends a block. Between lines: a break goes where it + # follows { ; , or comes before { }, which leaves one rule to a line, as + # the console's pages are written. Such a break is always between tokens + # (a string cannot be open at the end of a line, and the one way to make + # it so is refused), and every other break stays: `.a` over `.b {` is + # still two selectors. + text, written = '', False # written: the line just added was left as written + for n, line in enumerate(''.join(out).split('\n'), 1): + s = line.strip() + if not s: + continue + if s.endswith('\\'): + raise BuildError( + '%s:%d: the line ends in a backslash. A string continued onto the next ' + 'line would be joined to it wrongly; keep it on one line.' % (name, n)) + as_written = bool(CSS_AS_WRITTEN.search(s)) + if not as_written: + s = re.sub(r'\s*([{};,])\s*', r'\1', s) + s = re.sub(r':\s+', ':', s).replace(';}', '}') + if text and (text[-1] in '{;,' or s[0] in '{}'): + if text[-1] == ';' and s[0] == '}' and not written: + text = text[:-1] + text += s + else: + text += ('\n' if text else '') + s + written = as_written + if ' early' % name) + return text + + +def lean_html(text: str, name: str) -> str: + for tag in ('pre', 'textarea', 'script', 'style'): + if re.search(r'<%s\b' % tag, text, re.I): + raise BuildError( + '%s: <%s> in the markup. This build strips indentation and comments ' + 'line by line, which is not safe inside one.' % (name, tag)) + text = re.sub(r'', '', text, flags=re.S) + if ' line and a line') + frame = BODY_SLOT.split(text_of(console / BAR_FILE)) + if len(frame) != 2: + raise BuildError(BAR_FILE + ': needs exactly one line, where the ' + 'editor\'s body goes') + body = '\n'.join(part for part in ( + lean_html(frame[0], BAR_FILE), + lean_html(block.group(1), EDITOR_HTML), + lean_html(frame[1], BAR_FILE)) if part) + + css = '\n'.join((lean_css(text_of(EXT / EDITOR_CSS), EDITOR_CSS), + lean_css(text_of(console / CSS_FILE), CSS_FILE))) + + # One '] + + return '\n'.join([ + '', + '', + '', + '', + '', + '', + 'Patternflow - Audio', + GENERATED, + '', + '', + '', + body, + ] + scripts + [ + '', + '', + '', + ]) + + +def stamped_gzip(page: str, sketch=None) -> int: + """Bytes of `page` as the panel sends it: console_pages.py's own stamp and + its own gzip, so this cannot measure something other than what ships.""" + if str(HERE) not in sys.path: + sys.path.insert(0, str(HERE)) + import console_pages as cp + + sk = Path(sketch or SKETCH) + name, rel, delim = cp.CHROME + try: + _, chrome, _ = cp.split(cp.read(str(sk / rel)), name, delim) + shim = cp.load_shim(str(sk / 'console' / cp.SHIM_FILE)) + stamped = cp.stamp(page, cp.crc_of(chrome), shim, PAGE) + except (OSError, ValueError) as e: + raise BuildError(str(e)) + return len(gzip.compress(cp.payload(stamped), compresslevel=9, mtime=0)) + + +def weight(page: str, sketch=None): + """(gzip bytes as the panel sends it, one line saying so against the budget + and against the send windows it takes).""" + gz = stamped_gzip(page, sketch) + windows = -(-gz // WINDOW) + last = gz - (windows - 1) * WINDOW + return gz, ('%d bytes, %d gzip -9 as the panel sends it (budget %d, %s; ' + '%d send windows of %d: %d in the last, %d left in it)' % ( + len(page.encode('utf-8')), gz, BUDGET, + '%d spare' % (BUDGET - gz) if gz <= BUDGET else '%d OVER' % (gz - BUDGET), + windows, WINDOW, last, WINDOW - last)) + + +def on_disk(sketch=None): + """The page as written, LF, or None when there is none.""" + out = out_path(sketch) + return out.read_text(encoding='utf-8') if out.exists() else None + + +def write(sketch=None): + """Assemble and write the page. Returns (page, changed). Line endings stay + the file's own (a Windows checkout is CRLF, as every file around it), and a + page that already says this is not rewritten, so a build with nothing to + do touches nothing.""" + page = assemble(sketch) + out = out_path(sketch) + if on_disk(sketch) == page: + return page, False + nl = '\r\n' if out.exists() and b'\r\n' in out.read_bytes() else '\n' + with io.open(out, 'w', encoding='utf-8', newline='') as f: + f.write(page.replace('\n', nl)) + return page, True + + +def check(sketch=None) -> list: + """What is wrong, as lines to print; [] when the page on disk is what the + sources assemble to and is inside its budget.""" + try: + page = assemble(sketch) + gz, _ = weight(page, sketch) + except BuildError as e: + return ['console/%s.html cannot be assembled: %s' % (PAGE, e)] + problems = [] + if on_disk(sketch) != page: + problems.append( + 'console/%s.html is not what its sources assemble to (one of them was ' + 'edited, or the page was edited by hand).\n' + ' run: python firmware/toolchain/console_pages.py build' % PAGE) + if gz > BUDGET: + problems.append( + 'console/%s.html is %d bytes gzip -9 as the panel sends it: %d over its ' + 'budget of %d.\n' + ' On the panel\'s own hotspot every kilobyte is 0.2 to 0.5 s before anything ' + 'paints.\n' + ' Take the bytes back out, or raise BUDGET in ' + 'firmware/toolchain/build_audio_in_page.py and say in the commit what they bought.' + % (PAGE, gz, gz - BUDGET, BUDGET)) + return problems + + +def main(argv=None) -> int: + ap = argparse.ArgumentParser( + description='Assemble console/audio-in.html from its sources. ' + 'The usual way in is console_pages.py build.') + ap.add_argument('--check', action='store_true', + help='write nothing: fail if the page on disk is not what the sources ' + 'assemble to, or is over its size budget') + ap.add_argument('--sketch', metavar='DIR', + help='another copy of firmware/patternflow to read console/_audio_in* ' + 'from and write the page into') + args = ap.parse_args(argv) + # Sources are UTF-8 and so are their names in a report; a cp949 console + # must not turn one into a traceback. + for stream in (sys.stdout, sys.stderr): + if hasattr(stream, 'reconfigure'): + stream.reconfigure(errors='replace') + + if args.check: + problems = check(args.sketch) + for problem in problems: + print(problem, file=sys.stderr) + if problems: + return 1 + print('console/%s.html is what its sources assemble to: %s' + % (PAGE, weight(assemble(args.sketch), args.sketch)[1])) + return 0 + + try: + page, changed = write(args.sketch) + gz, line = weight(page, args.sketch) + except BuildError as e: + print('build_audio_in_page: %s' % e, file=sys.stderr) + return 1 + print('%s %s: %s' % ('wrote' if changed else 'unchanged', shown(out_path(args.sketch)), line)) + if gz > BUDGET: + print(' over its size budget: --check (and CI) fails until it is not', file=sys.stderr) + if changed: + print('the header still has to be baked: python firmware/toolchain/console_pages.py build ' + '(which also does this step)') + return 0 -import sys -if '--check' in sys.argv: - # CI: the page on disk must be what this script would write now — so an - # edit to the extension's editor, or to the adapter/overrides above, that - # was not baked into console/audio-in.html fails the build instead of - # shipping a stale page under a fresh-looking header. - current = OUT.read_text(encoding='utf-8') if OUT.exists() else '' - if current == page: - print('console/audio-in.html is up to date (editor %s)' % src_hash) - sys.exit(0) - print('console/audio-in.html is stale: rerun firmware/toolchain/build_audio_in_page.py, ' - 'then firmware/toolchain/console_pages.py build', file=sys.stderr) - sys.exit(1) - -io.open(OUT, 'w', encoding='utf-8', newline='\n').write(page) -print('wrote', OUT.relative_to(ROOT).as_posix(), len(page), 'bytes (editor %s)' % src_hash) +if __name__ == '__main__': + sys.exit(main()) diff --git a/firmware/toolchain/build_module.py b/firmware/toolchain/build_module.py index 8dc8584e..c7207de3 100644 --- a/firmware/toolchain/build_module.py +++ b/firmware/toolchain/build_module.py @@ -62,10 +62,37 @@ # no hardware). pf_module.h includes these rather than carrying a second copy. SHARED_HEADERS = ["core_color.h", "core_math.h", "core_noise.h"] -# -Os matches how the Arduino core builds the firmware, but a module is not -# competing for the app partition: it is 6-22 KB in a 10 MB filesystem, so -# trading size for speed is nearly free here. --opt selects it. -DEFAULT_OPT = "s" +# -O2. The default was -Os until 2026-10-04, because that is how the firmware +# itself is built and because abi/pf_module.h said -O2 bought about 2 %. It +# does not: measured on one board in one boot, the Basics presets as modules, +# GCC 8.4, frame time -Os -> -O2: +# 0510 11.9 -> 10.3 ms 0520 23.4 -> 19.7 ms +# 0512 23.2 -> 22.6 ms 0531 13.7 -> 12.4 ms +# 0515-4 15.0 -> 11.2 ms 0601 15.0 -> 13.6 ms +# which is 3 to 34 % more frames a second. (GCC 14.2 at -O2 was another 1 to +# 8 % faster than GCC 8.4 at -O2 on the same six.) --opt still selects another +# level. It costs two things. +# +# Size. The builds timed above are 67 -> 90 KB of code across the 33 Basics +# modules; with abi/pf_libm.h compiled in as well - the other half of the same +# change: floorf, fmodf and their kin as inline code instead of calls into the +# firmware's libm - the same 33 are 75 KB at -Os and 104 KB at -O2, and 145 +# community patterns built both ways came out at 1.36 times their old code +# (the median; GCC 8.4, 2026-10-04). From firmware 3.10.5 a module's code runs +# from PSRAM (src/core_module_memory.h) and its size does not decide whether it +# loads. Before 3.10.5 code is placed in internal RAM and admitted against +# what the services leave - about 7.8 KB on an Audio build - so a module that +# fitted there as an -Os build can be refused as an -O2 one. Nothing here +# builds a large module smaller for that firmware; it is told to update. +# +# The last bit. -O2 inlines helpers -Os left out of line, and GCC fuses a*b+c +# into one instruction - one rounding where there were two +# (-ffp-contract=fast) - wherever it then sees both halves together. A rebuilt +# pattern's own arithmetic can therefore differ in its last bit, anywhere in +# it. That shows only where the pattern amplifies it: a float hash, state +# carried between frames, rarely the edge of a floorf band. abi/pf_libm.h has +# the worked cases. +DEFAULT_OPT = "2" CXXFLAGS = [ "-std=gnu++17", @@ -87,9 +114,13 @@ def toolchain_roots() -> list[Path]: """Where an xtensa-esp32s3 toolchain may live, most specific first. - Patternflow's firmware and the community build worker are both arduino-cli, - so the Arduino core's copy is the one that matches what devices actually - run. PlatformIO is kept as a fallback for the fork this came from. + The order is the community build worker's: the compiler it runs is the + Arduino core's (docs/SERVICES.md), so that is looked for before + PlatformIO's, and nothing here should reorder them under it. The firmware + itself is a PlatformIO build (bundles/build.sh) and its compiler - + toolchain-xtensa-esp32s3, GCC 8.4 - is therefore the LAST place looked; + PF_XTENSA_BIN names it when a build has to be the firmware compiler's, as + the committed Basics pack is (web/src/lib/pattern/packs.ts). """ roots: list[Path] = [] explicit = os.environ.get("PF_XTENSA_BIN") @@ -309,7 +340,7 @@ def main() -> None: ap.add_argument("--compile-only", action="store_true", help="Skip link + manifest output") ap.add_argument("--clean", action="store_true", help="Remove previous build output first") ap.add_argument("--opt", default=DEFAULT_OPT, choices=["s", "1", "2", "3"], - help="Optimisation level (default: s, matching the firmware)") + help="Optimisation level (default: 2 - see DEFAULT_OPT)") ap.add_argument("--out", type=Path, default=OUT, help="Where to write .pfm/.json (default: the sketch data dir)") args = ap.parse_args() diff --git a/firmware/toolchain/check_abi_freeze.py b/firmware/toolchain/check_abi_freeze.py index c736fd65..fb32e889 100644 --- a/firmware/toolchain/check_abi_freeze.py +++ b/firmware/toolchain/check_abi_freeze.py @@ -26,6 +26,16 @@ A comment-only edit trips this too. That is intended: refreshing the sums is the two-second acknowledgement that you know which directory you are in. +One header here lays out nothing: pf_libm.h is inline code every module compiles +into itself, behind floorf, fmodf and five more, and it is pinned because an +edit to it changes what every module built afterwards computes. What an edit +there owes is not a version bump but check_libm.py (and check_module_libm.py, +where there is an xtensa toolchain) passing first. + +--update rewrites the hashes and nothing else: the comment lines in abi.sums +are notes people wrote about what the file does and does not cover, and they +are kept as they stand. + License: MIT """ @@ -39,6 +49,26 @@ ABI = ROOT / "patternflow" / "abi" SUMS = ABI / "abi.sums" +# Written only when abi.sums has no comment of its own to keep. +DEFAULT_HEADER = [ + "# sha256 of each LF-normalized header in abi/. Verified in CI by", + "# check_abi_freeze.py; regenerate with --update ONLY alongside a", + "# deliberate, append-only ABI change. The file exists so no change", + "# to the one contract installed modules read can be an accident.", +] + +# Headers that compute rather than lay out, and what a change to one owes. +# Anything not named here gets the layout rule, which is the strict one. +COMPUTES = { + "pf_libm.h": ( + "pf_libm.h lays out nothing, but every module built after an edit to it\n" + "computes with the edited code. Run\n" + " python firmware/toolchain/check_libm.py\n" + " python firmware/toolchain/check_module_libm.py (needs an xtensa toolchain)\n" + "and only when they pass:\n" + " python firmware/toolchain/check_abi_freeze.py --update"), +} + if hasattr(sys.stdout, "reconfigure"): sys.stdout.reconfigure(errors="replace") @@ -57,13 +87,18 @@ def main() -> int: now = current() if "--update" in sys.argv[1:]: - lines = ["# sha256 of each LF-normalized header in abi/. Verified in CI by", - "# check_abi_freeze.py; regenerate with --update ONLY alongside a", - "# deliberate, append-only ABI change. The file exists so no change", - "# to the one contract installed modules read can be an accident."] + # Every comment line already in the file is kept, in order. They are + # hand-written - what the hashes do not cover, what pf_libm.h is doing + # here - and this command used to replace them with four lines of its + # own, which is how a note gets lost by the person following it. + kept = [] + if SUMS.exists(): + kept = [line.rstrip() for line in SUMS.read_text(encoding="utf-8").splitlines() + if line.lstrip().startswith("#")] + lines = list(kept or DEFAULT_HEADER) lines += [f"{h} {n}" for n, h in now.items()] SUMS.write_text("\n".join(lines) + "\n", encoding="utf-8") - print(f"pinned {len(now)} abi header(s)") + print(f"pinned {len(now)} abi header(s), {len(kept)} comment line(s) kept") return 0 if not SUMS.exists(): @@ -79,24 +114,32 @@ def main() -> int: pinned[n] = h problems = [] + moved = [] for name, h in now.items(): if name not in pinned: problems.append(f" {name}: new abi header, not pinned") + moved.append(name) elif pinned[name] != h: problems.append(f" {name}: content changed") + moved.append(name) for name in pinned: if name not in now: problems.append(f" {name}: pinned but gone") + moved.append(name) if problems: print("the module ABI moved:\n") print("\n".join(problems)) - print( - "\nEvery installed .pfm on every panel reads this layout without\n" - "recompiling. If this change is deliberate: append-only, bump\n" - "PF_ABI_MODULE_VERSION, teach the loader the new range, then\n" - " python firmware/toolchain/check_abi_freeze.py --update\n" - "in the same commit. If it is not deliberate, revert it.") + for name in moved: + if name in COMPUTES: + print("\n" + COMPUTES[name]) + if any(name not in COMPUTES for name in moved): + print( + "\nEvery installed .pfm on every panel reads this layout without\n" + "recompiling. If this change is deliberate: append-only, bump\n" + "PF_ABI_MODULE_VERSION, teach the loader the new range, then\n" + " python firmware/toolchain/check_abi_freeze.py --update\n" + "in the same commit. If it is not deliberate, revert it.") return 1 print(f"abi frozen: {len(now)} header(s) match abi.sums") diff --git a/firmware/toolchain/check_footprint.py b/firmware/toolchain/check_footprint.py index 503d34ac..dfefb442 100644 --- a/firmware/toolchain/check_footprint.py +++ b/firmware/toolchain/check_footprint.py @@ -63,12 +63,18 @@ # Again the same day: static DRAM +304 in every edition - the crash record's 296 B # (224 of them the SDK's own strings behind esp_core_dump_get_summary, 64 the # breadcrumb in .noinit) and 8 B of loop-sync state. +# Re-pinned 2026-10-03: static DRAM +112..+120 in every edition, IRAM unchanged - +# resident modules' bookkeeping in core_module_loader.h: the current module's +# path (72 B, the key it is parked under), the table header (24 B: slot pointer, +# use counter, four status counters), the storage generation and the current +# module's (8 B) and four small flags and counters. The slots themselves are +# allocated in PSRAM on the first park. PINS = { - "default": (141576, 71471), - "audio": (161104, 71951), - "performance": (158080, 71471), - "clock": (141856, 71471), - "midi": (154168, 70671), + "default": (141688, 71471), + "audio": (161216, 71951), + "performance": (158200, 71471), + "clock": (141976, 71471), + "midi": (154288, 70671), } # Enough that an intentional, well-understood adjustment does not fire the check diff --git a/firmware/toolchain/check_libm.py b/firmware/toolchain/check_libm.py new file mode 100644 index 00000000..7c886101 --- /dev/null +++ b/firmware/toolchain/check_libm.py @@ -0,0 +1,58 @@ +"""Prove that the module SDK's inline floorf/fmodf and kin return what libm returns. + +python firmware/toolchain/check_libm.py [--sanitize] +No board required. Takes a minute or two: it runs every float there is. + +abi/pf_libm.h puts inline FPU code behind floorf, ceilf, truncf, roundf, fminf, +fmaxf and fmodf in every .pfm, so that a pattern nobody edits stops calling into +the firmware's libm per pixel. The header's claim is that each of the seven +returns exactly what libm returns, so this is not a tolerance check like +check_math.py: tests/libm_test.cpp compares bit patterns against this machine's +libm - all 2^32 floats through the one-argument functions, about three billion +comparisons through the others - and one mismatch fails. + +Two things it does not do. It cannot compile the part of the header that only a +module build has (the bodies behind the C names, the fmod dispatch): that is +check_module_libm.py, which needs the Xtensa compiler. And it cannot check that +the S3's multiply-subtract is fused: that is tests/modules/_math_probe, on a +board. +""" +import argparse +from pathlib import Path +import subprocess +import tempfile + +from check_module_elf import ROOT, compiler_environment + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--sanitize', action='store_true') + args = parser.parse_args() + compiler, env = compiler_environment() + headers = ROOT / 'firmware/patternflow/abi' + source = ROOT / 'firmware/toolchain/tests/libm_test.cpp' + with tempfile.TemporaryDirectory(prefix='patternflow-libm-') as directory: + work = Path(directory) + exe = work / ('libm_test.exe' if Path(compiler).suffix.lower() == '.exe' else 'libm_test') + if Path(compiler).stem.lower() == 'cl': + if args.sanitize: + raise SystemExit('--sanitize requires GCC or Clang') + command = [compiler, '/nologo', '/std:c++17', '/EHsc', '/O2', '/W4', '/WX', '/utf-8', + f'/I{headers}', str(source), f'/Fe:{exe}'] + else: + # The names under test stay calls into this machine's libm. Left as + # builtins, GCC answers some of them inline on a PC, and its floor + # hands a signalling NaN back unquieted where glibc's does not. + calls = [f'-fno-builtin-{name}' for name in + ('floorf', 'ceilf', 'truncf', 'roundf', 'fminf', 'fmaxf', 'fmodf')] + command = [compiler, '-std=c++17', '-O2', '-Wall', '-Wextra', '-Werror', '-pthread', + *calls, f'-I{headers}', str(source), '-o', str(exe)] + if args.sanitize: + command += ['-fsanitize=address,undefined', '-fno-omit-frame-pointer'] + subprocess.run(command, cwd=work, env=env, check=True) + subprocess.run([str(exe)], cwd=work, env=env, check=True) + + +if __name__ == '__main__': + main() diff --git a/firmware/toolchain/check_module_libm.py b/firmware/toolchain/check_module_libm.py new file mode 100644 index 00000000..cbfbbc4c --- /dev/null +++ b/firmware/toolchain/check_module_libm.py @@ -0,0 +1,356 @@ +"""Read the module SDK's inline libm out of a module the real toolchain built. + + python firmware/toolchain/check_module_libm.py + +check_libm.py proves abi/pf_libm.h's algorithms on a PC: twenty billion +comparisons against libm. What it cannot do is compile the half of that header +a module actually uses - the bodies behind floorf, ceilf, truncf, roundf, fminf, +fmaxf and fmodf, the redirection of libstdc++'s float overloads, and the choice +between fmod's three paths - because that half exists only under +PF_MODULE_BUILD on the Xtensa compiler. Swap two wrappers there, or break one +of the __builtin_ macros, and check_libm.py still says twenty billion matches. + +So this builds tests/modules/_libm_shapes with build_module.py - the same +flags, linker script and import check a pattern gets - and reads the result +(the code out of the object file, where the assembler's own record of what is +an instruction and what is padding is still attached; the imports out of the +linked .pfm): + + - the fixture's static_asserts are evaluated through the module-only wiring, + so a wrapper bound to the wrong function does not compile; + - for every spelling the header claims, the function is disassembled and + walked: there has to be a way from its entry to its return that makes no + call (one call, to __divsf3, for fmodf by a divisor that is not a + constant), it has to contain the FPU instructions the inline code is made + of, and the only library function it may call at all is the one it stands + in for - the fallback; + - the three controls (`__builtin_floorf(x)`, a call through a pointer to + floorf, lroundf) have to be plain calls still, which is what shows the + walk can tell a call from inline code; + - the module imports the seven names and __divsf3 and does not import fmaf: + the fused multiply-subtract is an instruction (MSUB.S) or it is a symbol + no firmware exports. + +It says nothing about the values the inline code computes on a panel - that is +tests/modules/_math_probe, on a board. + +Needs the xtensa-esp32s3 toolchain, found the way build_module.py finds it +(PF_XTENSA_BIN, an Arduino core, PlatformIO). CI runs it in the firmware build +job, which has PlatformIO's. +""" + +from __future__ import annotations + +import re +import subprocess +import sys +import tempfile +from dataclasses import dataclass, field +from pathlib import Path + +import build_module + +if hasattr(sys.stdout, "reconfigure"): + sys.stdout.reconfigure(encoding="utf-8", errors="replace") + +HERE = Path(__file__).resolve().parent +FIXTURE = HERE / "tests" / "modules" / "_libm_shapes" + +RETURNS = {"retw", "retw.n", "ret", "ret.n"} +CALLS = {"call0", "call4", "call8", "call12", "callx0", "callx4", "callx8", "callx12"} +LOOPS = {"loop", "loopnez", "loopgtz"} + +SECTION = re.compile(r"^Disassembly of section (\S+):") +FUNCTION = re.compile(r"^([0-9a-f]+) <([^>]+)>:\s*$") +INSTRUCTION = re.compile(r"^\s*([0-9a-f]+):\t[0-9a-f ]+\t([a-z_.0-9]+)(?:\t(.*?))?\s*$") +RELOCATION = re.compile(r"^\s+([0-9a-f]+): (R_XTENSA_\w+)\s+(\S+)") +ADDRESS = re.compile(r"\b([0-9a-f]+) <([^>+-]+)") + + +@dataclass +class Instruction: + address: int + mnemonic: str + operands: str + following: int = -1 # the next instruction's address + + +@dataclass +class Function: + name: str + start: int + section: str + code: dict[int, Instruction] = field(default_factory=dict) + # call instruction address -> what it calls ("?" when it cannot be named) + calls: dict[int, str] = field(default_factory=dict) + + def mnemonics(self) -> set[str]: + return {i.mnemonic for i in self.code.values()} + + def targets(self) -> set[str]: + return set(self.calls.values()) + + +def disassemble(obj: Path, objdump: Path) -> dict[str, Function]: + text = subprocess.run([str(objdump), "-dr", str(obj)], capture_output=True, + text=True, errors="replace", check=True).stdout + functions: dict[str, Function] = {} + # Keyed by section as well as address: an object has several code sections + # (libstdc++'s out-of-line overloads get one each), all starting at zero. + literal: dict[tuple[str, int], str] = {} # where a literal is -> the symbol stored there + expanded: dict[tuple[str, int], str] = {} # where an l32r is -> the symbol it is about to call + section = "" + current: Function | None = None + previous: Instruction | None = None + for line in text.splitlines(): + match = SECTION.match(line) + if match: + section = match.group(1) + current = previous = None + continue + match = FUNCTION.match(line) + if match: + current = Function(match.group(2), int(match.group(1), 16), section) + functions[current.name] = current + previous = None + continue + match = RELOCATION.match(line) + if match: + address, kind, symbol = int(match.group(1), 16), match.group(2), match.group(3) + if kind == "R_XTENSA_32": + literal[(section, address)] = symbol + elif kind == "R_XTENSA_ASM_EXPAND": + expanded[(section, address)] = symbol + continue + match = INSTRUCTION.match(line) + if match and current is not None: + instruction = Instruction(int(match.group(1), 16), match.group(2), match.group(3) or "") + current.code[instruction.address] = instruction + if previous is not None: + previous.following = instruction.address + previous = instruction + + # Name every call. With -mlongcalls a call to another section is + # `l32r aN, ` ... `callx8 aN`; a call inside the module may have + # been relaxed to a direct `call8 `. + for function in functions.values(): + loaded: dict[str, str] = {} + for address in sorted(function.code): + instruction = function.code[address] + if instruction.mnemonic == "l32r": + register = instruction.operands.split(",")[0].strip() + target = ADDRESS.search(instruction.operands) + symbol = expanded.get((function.section, address)) + if symbol is None and target: + symbol = literal.get((function.section, int(target.group(1), 16))) + if symbol is None: + loaded.pop(register, None) + else: + loaded[register] = symbol + elif instruction.mnemonic in CALLS: + if instruction.mnemonic.startswith("callx"): + function.calls[address] = loaded.get(instruction.operands.strip(), "?") + else: + target = ADDRESS.search(instruction.operands) + function.calls[address] = target.group(2) if target else "?" + return functions + + +def reaches_return(function: Function, allowed: set[str]) -> bool: + """Is there a way from entry to return that calls nothing outside `allowed`?""" + seen: set[int] = set() + pending = [function.start] + while pending: + address = pending.pop() + if address in seen or address not in function.code: + continue + seen.add(address) + instruction = function.code[address] + mnemonic = instruction.mnemonic + if mnemonic in RETURNS: + return True + if mnemonic in CALLS: + if function.calls.get(address, "?") in allowed: + pending.append(instruction.following) + continue + if mnemonic == "jx": + continue # an indirect jump: not followed + target = ADDRESS.search(instruction.operands) + if mnemonic == "j": + if target: + pending.append(int(target.group(1), 16)) + continue + if (mnemonic.startswith("b") and mnemonic != "break") or mnemonic in LOOPS: + if target: + pending.append(int(target.group(1), 16)) + pending.append(instruction.following) + return False + + +@dataclass +class Shape: + symbol: str + written: str + fallback: set[str] # every library call it may contain + needs: set[str] # instructions the inline code is made of + fast: set[str] = field(default_factory=set) # calls allowed on the way through + never: set[str] = field(default_factory=set) # instructions or calls that mean a wrong path + libstdcxx: bool = False # reached through 's float overloads + + +CONVERT = {"trunc.s", "float.s"} +SHAPES = [ + Shape("pf_shape_floorf", "floorf(x)", {"floorf"}, CONVERT), + Shape("pf_shape_ceilf", "ceilf(x)", {"ceilf"}, CONVERT), + Shape("pf_shape_truncf", "truncf(x)", {"truncf"}, CONVERT), + Shape("pf_shape_roundf", "roundf(x)", {"roundf"}, CONVERT), + Shape("pf_shape_fminf", "fminf(x, y)", {"fminf"}, {"olt.s"}), + Shape("pf_shape_fmaxf", "fmaxf(x, y)", {"fmaxf"}, {"olt.s"}), + Shape("pf_shape_fmodf", "fmodf(x, y)", {"fmodf", "__divsf3"}, CONVERT | {"msub.s"}, + fast={"__divsf3"}), + Shape("pf_shape_fmodf_one", "fmodf(x, 1.0f)", {"fmodf"}, CONVERT, + never={"msub.s", "mul.s", "__divsf3"}), + Shape("pf_shape_fmodf_const", "fmodf(x, 6.2831853f)", {"fmodf"}, CONVERT | {"msub.s", "mul.s"}, + never={"__divsf3"}), + Shape("pf_shape_fmodf_negconst", "fmodf(x, -360.0f)", {"fmodf"}, CONVERT | {"msub.s", "mul.s"}, + never={"__divsf3"}), + Shape("pf_shape_floor", "floor(x)", {"floorf"}, CONVERT, libstdcxx=True), + Shape("pf_shape_std_floor", "std::floor(x)", {"floorf"}, CONVERT, libstdcxx=True), + Shape("pf_shape_ceil", "ceil(x)", {"ceilf"}, CONVERT, libstdcxx=True), + Shape("pf_shape_trunc", "trunc(x)", {"truncf"}, CONVERT, libstdcxx=True), + Shape("pf_shape_round", "round(x)", {"roundf"}, CONVERT, libstdcxx=True), + Shape("pf_shape_fmin", "fmin(x, y)", {"fminf"}, {"olt.s"}, libstdcxx=True), + Shape("pf_shape_fmax", "fmax(x, y)", {"fmaxf"}, {"olt.s"}, libstdcxx=True), + Shape("pf_shape_fmod", "fmod(x, y)", {"fmodf", "__divsf3"}, CONVERT | {"msub.s"}, + fast={"__divsf3"}, libstdcxx=True), + Shape("pf_shape_std_fmod", "std::fmod(x, y)", {"fmodf", "__divsf3"}, CONVERT | {"msub.s"}, + fast={"__divsf3"}, libstdcxx=True), + Shape("pf_shape_std_fmod_const", "std::fmod(x, 6.2831853f)", {"fmodf"}, + CONVERT | {"msub.s", "mul.s"}, never={"__divsf3"}, libstdcxx=True), +] + +# symbol -> (what was written, what it has to call; None for a call that +# cannot be named from the object) +CONTROLS = { + "pf_control_builtin": ("__builtin_floorf(x)", "floorf"), + "pf_control_pointer": ("a call through a pointer to floorf", None), + "pf_control_lroundf": ("lroundf(x)", "lroundf"), +} + +IMPORTED = {"floorf", "ceilf", "truncf", "roundf", "fminf", "fmaxf", "fmodf", "__divsf3"} + + +def build(opt: str, gxx: Path, work: Path) -> tuple[Path, Path]: + """The fixture as a linked module and as the object it was linked from.""" + broken = (f"_libm_shapes does not build at -O{opt}: the module half of abi/pf_libm.h " + "is broken, or one of the fixture's static_asserts no longer holds") + # The whole pipeline, as a pattern gets it: this is what proves it links + # and imports nothing the loader lacks. + out = work / f"O{opt}" + command = [sys.executable, str(HERE / "build_module.py"), "--opt", opt, "--out", str(out), + str(FIXTURE)] + result = subprocess.run(command, capture_output=True, text=True, errors="replace") + if result.returncode != 0: + print(result.stdout + result.stderr) + raise SystemExit(broken) + # And the same compile again, kept as an object. The linked module has + # lost the assembler's map of instructions and padding, and a disassembler + # reading it falls out of step after the first literal pool. + objects = work / f"O{opt}-obj" + _, ok, log = build_module.build_one(FIXTURE, gxx, True, build_module.ABI, objects, out, opt) + if not ok: + print(log) + raise SystemExit(broken) + return out / "_libm_shapes.pfm", objects / FIXTURE.name / "pattern.o" + + +def check(opt: str, redirected: bool, gxx: Path, tools: dict[str, Path], work: Path) -> list[str]: + """Every problem found in the fixture built at -O.""" + pfm, obj = build(opt, gxx, work) + functions = disassemble(obj, tools["objdump"]) + problems: list[str] = [] + + for shape in SHAPES: + if shape.libstdcxx and not redirected: + continue + function = functions.get(shape.symbol) + if function is None: + problems.append(f"-O{opt} {shape.written}: {shape.symbol} is not in the module") + continue + found: list[str] = [] + if not reaches_return(function, shape.fast): + found.append("every way through it makes a library call - it is not inline") + missing = shape.needs - function.mnemonics() + if missing: + found.append("no " + ", ".join(sorted(missing)) + " in it") + stray = function.targets() - shape.fallback + if stray: + found.append("calls " + ", ".join(sorted(stray)) + ", which is not its fallback") + wrong = shape.never & (function.mnemonics() | function.targets()) + if wrong: + found.append("has " + ", ".join(sorted(wrong)) + " - the wrong fmod path") + if found: + problems.append(f"-O{opt} {shape.written}: " + "; ".join(found)) + else: + through = f", through {'/'.join(sorted(shape.fast))} only" if shape.fast else "" + print(f" ok -O{opt} {shape.written:<26} inline{through}; fallback " + f"{'/'.join(sorted(function.targets() - shape.fast)) or '-'}") + + for symbol, (written, target) in CONTROLS.items(): + function = functions.get(symbol) + if function is None: + problems.append(f"-O{opt} control {written}: {symbol} is not in the module") + elif (reaches_return(function, set()) or not function.calls + or (target is not None and target not in function.targets()) + or "trunc.s" in function.mnemonics()): + problems.append(f"-O{opt} control {written}: expected a plain call and found " + "something else - the walk cannot be trusted") + else: + print(f" ok -O{opt} {written:<34} still a call") + + listed = subprocess.run([str(tools["nm"]), "-u", str(pfm)], capture_output=True, text=True, + check=True).stdout + imports = {line.split()[-1] for line in listed.splitlines() if line.strip()} + if "fmaf" in imports: + problems.append(f"-O{opt}: the module imports fmaf - the multiply-subtract is a call, " + "and no firmware exports that name") + if not IMPORTED <= imports: + problems.append(f"-O{opt}: expected imports missing: {', '.join(sorted(IMPORTED - imports))}") + unknown = imports - build_module.host_symbol_table() + if unknown: + problems.append(f"-O{opt}: imports no loader resolves: {', '.join(sorted(unknown))}") + print(f" -O{opt} imports: {' '.join(sorted(imports))}") + return problems + + +def main() -> int: + gxx = build_module.find_tool("g++") + tools = {"objdump": build_module.find_tool("objdump"), "nm": build_module.find_tool("nm")} + version = subprocess.run([str(gxx), "-dumpversion"], capture_output=True, text=True, + check=True).stdout.strip() + major = int(version.split(".")[0]) + # The same range abi/pf_libm.h redirects libstdc++'s overloads for. + redirected = 8 <= major <= 14 + print(f"xtensa g++ {version}" + ("" if redirected else + " - outside 8..14, so floor()/std::floor() and kin are expected to stay calls")) + + problems: list[str] = [] + with tempfile.TemporaryDirectory(prefix="patternflow-module-libm-") as directory: + work = Path(directory) + # The default level, every spelling. Then -Os, where the C names still + # have to be inline (always_inline) but GCC is free to keep libstdc++'s + # overloads as functions of the module's own. + problems += check(build_module.DEFAULT_OPT, redirected, gxx, tools, work) + problems += check("s", False, gxx, tools, work) + + if problems: + print("\nthe module half of abi/pf_libm.h is not what it claims:") + for problem in problems: + print(" " + problem) + return 1 + print("module libm inline: every claimed spelling, library kept as the fallback only") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/firmware/toolchain/check_runtime.py b/firmware/toolchain/check_runtime.py index 4de32649..f6d19c69 100644 --- a/firmware/toolchain/check_runtime.py +++ b/firmware/toolchain/check_runtime.py @@ -1,7 +1,12 @@ -"""Exercise module admission, loader lifecycle and conditional loop requests. +"""Exercise module admission, loader lifecycle, resident modules and conditional +loop requests. python firmware/toolchain/check_runtime.py [--sanitize] -Production code is compiled against deterministic hardware fixtures. +Production code is compiled against deterministic hardware fixtures. Two +programs: runtime_test.cpp (admission, the frame-boundary hand-off, the +registry's load/park/resume lifecycle against a fake loader) and +resident_test.cpp (the resident table, and the loader's own park, resume, +unload and eviction code lifted out of core_module_loader.h). """ import argparse from pathlib import Path @@ -18,7 +23,8 @@ def main(): compiler, env = compiler_environment() with tempfile.TemporaryDirectory(prefix='patternflow-runtime-') as directory: work = Path(directory) - for name in ('core_module_memory.h', 'core_loop_sync.h', 'core_net_maintenance.h'): + for name in ('core_module_memory.h', 'core_module_resident.h', 'core_loop_sync.h', + 'core_net_maintenance.h'): (work / name).write_bytes((ROOT / 'firmware/patternflow/src' / name).read_bytes()) for name in ('Arduino.h', 'esp_heap_caps.h', 'core_runtime.h'): (work / name).write_text('// Hardware supplied by runtime_test.cpp\n') @@ -29,24 +35,44 @@ def main(): start = registry.index('inline bool jsonStringValue(') end = registry.index('// Deliberately a substring scan', start) (work / 'sidecar_name.h').write_text(registry[start:end], encoding='utf-8') + # The sidecar cache's forget calls: every module-file writer reports + # its write through one, so they move the loader's storage generation. + start = registry.index('inline int sidecarFind(') + end = registry.index('inline void sidecarRemember(', start) + (work / 'sidecar_forget.h').write_text(registry[start:end], encoding='utf-8') manager = (ROOT / 'firmware/patternflow/src/core_patterns_http.h').read_text(encoding='utf-8') start = manager.index('inline char restorePath[') end = manager.index('// The rescan-and-reload', start) (work / 'patterns_lifecycle.h').write_text(manager[start:end], encoding='utf-8') - source = ROOT / 'firmware/toolchain/tests/runtime_test.cpp' - exe = work / ('runtime_test.exe' if Path(compiler).suffix.lower() == '.exe' else 'runtime_test') - if Path(compiler).stem.lower() == 'cl': - if args.sanitize: - raise SystemExit('--sanitize requires GCC or Clang') - command = [compiler, '/nologo', '/std:c++20', '/EHsc', '/O2', '/W4', '/WX', '/utf-8', - f'/I{work}', str(source), f'/Fe:{exe}'] - else: - command = [compiler, '-std=c++20', '-O2', '-Wall', '-Wextra', '-Werror', - f'-I{work}', str(source), '-o', str(exe)] - if args.sanitize: - command += ['-fsanitize=address,undefined', '-fno-omit-frame-pointer'] - subprocess.run(command, cwd=work, env=env, check=True) - subprocess.run([str(exe)], cwd=work, env=env, check=True) + # The loader's residency, in the pieces that need no ELF, no flash and + # no cache: its state, api->alloc(), unload(), the load timings, and + # the "Staying loaded" block. Each runs from one anchor to the next. + loader = (ROOT / 'firmware/patternflow/src/core_module_loader.h').read_text(encoding='utf-8') + for name, begin, finish in ( + ('loader_state.h', 'constexpr int MAX_SECTIONS = 8;', 'inline bool fail(const char* message) {'), + ('loader_alloc.h', 'inline void* moduleAlloc(size_t bytes) {', 'inline void hostLog('), + ('loader_unload.h', "// The current module's globals back to", 'inline bool copyExecutable('), + ('loader_timings.h', '// Phase timings from the last successful load()', '// Cheap structural check'), + ('loader_resident.h', '// \u2500\u2500 Staying loaded', '// The two calls a frame makes into the module'), + ): + start = loader.index(begin) + end = loader.index(finish, start) + (work / name).write_text(loader[start:end], encoding='utf-8') + for test in ('runtime_test', 'resident_test'): + source = ROOT / f'firmware/toolchain/tests/{test}.cpp' + exe = work / (f'{test}.exe' if Path(compiler).suffix.lower() == '.exe' else test) + if Path(compiler).stem.lower() == 'cl': + if args.sanitize: + raise SystemExit('--sanitize requires GCC or Clang') + command = [compiler, '/nologo', '/std:c++20', '/EHsc', '/O2', '/W4', '/WX', '/utf-8', + f'/I{work}', str(source), f'/Fe:{exe}'] + else: + command = [compiler, '-std=c++20', '-O2', '-Wall', '-Wextra', '-Werror', + f'-I{work}', str(source), '-o', str(exe)] + if args.sanitize: + command += ['-fsanitize=address,undefined', '-fno-omit-frame-pointer'] + subprocess.run(command, cwd=work, env=env, check=True) + subprocess.run([str(exe)], cwd=work, env=env, check=True) if __name__ == '__main__': diff --git a/firmware/toolchain/console_demo.py b/firmware/toolchain/console_demo.py index a44db1ec..63464450 100644 --- a/firmware/toolchain/console_demo.py +++ b/firmware/toolchain/console_demo.py @@ -1182,20 +1182,19 @@ def main(): watch(D); // The chrome marks the tab whose device path is this page's location; here // the location is the demo file's, so the tab is marked the way drawNav() - // would have, centred in a nav that scrolls (the phone's), before the - // first paint of every redraw. + // would have, before the first paint of every redraw. The nav wraps (no + // sideways scroll) and its setup group sits in a nested , + // so the links are found at any depth, not as the nav's children. function markNav(root) { - var nav = root.querySelector('nav'), on = null, i, a; + var nav = root.querySelector('nav'), on = null, i, a, l; if (!nav) return; - for (i = 0; i < nav.children.length; i++) { - a = nav.children[i]; + for (l = nav.querySelectorAll('a'), i = 0; i < l.length; i++) { + a = l[i]; if (a.pathname !== L.pathname) continue; on = a; if (a.className !== 'on') { a.className = 'on'; a.setAttribute('aria-current', 'page'); - var e = nav.getBoundingClientRect(), r = a.getBoundingClientRect(); - nav.scrollLeft += r.left + r.width / 2 - e.left - e.width / 2; } } return on; diff --git a/firmware/toolchain/console_pages.py b/firmware/toolchain/console_pages.py index bed53517..3b74d9b1 100644 --- a/firmware/toolchain/console_pages.py +++ b/firmware/toolchain/console_pages.py @@ -85,6 +85,13 @@ `extract` is lossless by construction (build right after it is a no-op), and `build` is idempotent. +One page is not written as a page. console/audio-in.html is assembled by +build_audio_in_page.py from the audio extension's mapping editor and the +console/_audio_in* files (an underscore marks a source that is not a page; +PAGES below is the list of pages). `build` assembles it first and `check` +holds it to its sources and to its size budget, so there is still one command +to build and one to check. Edit those sources, never audio-in.html. + --sketch DIR work on another copy of firmware/patternflow (a scratch copy to try a change of this script against) """ @@ -388,6 +395,18 @@ def unstamp(body, name="page"): return STAMPED_RE.sub(lambda m: CHROME_TAG, body) +# ── the page that is assembled ───────────────────────────────────────────── + +def assembled(): + """build_audio_in_page: console/audio-in.html is made by it, from sources + that are not pages (see the docstring). It takes the sketch as an argument + and reads this file's functions, never its state, so --sketch holds.""" + if HERE not in sys.path: + sys.path.insert(0, HERE) + import build_audio_in_page + return build_audio_in_page + + # ── commands ─────────────────────────────────────────────────────────────── def stamping(): @@ -417,6 +436,16 @@ def cmd_build(): crc, shim = stamping() changed = [] pages_gz = chrome_gz = 0 + gen = assembled() + try: + page, wrote = gen.write(SKETCH) + gz, weight = gen.weight(page, SKETCH) + except ValueError as e: + raise SystemExit("%s: %s" % (gen.PAGE, e)) + print(" %s.html %s its sources: %s" + % (gen.PAGE, "assembled from" if wrote else "already matches", weight)) + if gz > gen.BUDGET: + print(" %s: over its size budget; `check` (and CI) fails until it is not" % gen.PAGE) print(" stamping chrome h=%s, fallback %d bytes" % (crc, len(shim))) for name, rel, delim, src in targets(): dest = header_path(rel) @@ -480,13 +509,22 @@ def cmd_check(): problem = gz_problem(block, ident, storage, body) if problem: stale.append("%s (%s)" % (name, problem)) + # The assembled page against its own sources and its size budget. A header + # can match a page that no longer matches what it is made from. + gen = assembled() + problems = gen.check(SKETCH) if stale: print("out of sync: " + ", ".join(stale)) print("run: python firmware/toolchain/console_pages.py build") + for problem in problems: + print(problem) + if stale or problems: return 1 print("all %d console headers match their HTML, stamped with chrome h=%s; " "all %d gzip twins decompress to their literals" % (len(PAGES), crc, len(PAGES) + len(ASSETS))) + print("%s.html is what its sources assemble to: %s" + % (gen.PAGE, gen.weight(gen.assemble(SKETCH), SKETCH)[1])) return 0 diff --git a/firmware/toolchain/console_serve.py b/firmware/toolchain/console_serve.py index 98da06f6..51acca58 100644 --- a/firmware/toolchain/console_serve.py +++ b/firmware/toolchain/console_serve.py @@ -28,6 +28,40 @@ here, and a page that works against the mock still has to be tried on a device before you believe it. +One exception, because one page is an editor: /audio-in. Its endpoints +(/api/audio-in, /api/audio-in/reset, /api/audio) are a port of the firmware's +handlers, not a fixture: a request changes only the fields it carries, the +pairs settle as they do on the panel, a bad curve table is ignored with +{"ok":true}, replies have the handler's field order and decimals. The +sound is made up; what is done with it is not. And what the panel hears is +a state you pick, each entered as if it had been so for a while (each of +these addresses lands on /audio-in): + + /mock?audio=music the microphone on, a room with music in it (the default) + /mock?audio=silence on, a quiet room: the gates shut, every knob at rest + /mock?audio=off switched off after music: the analysis is parked and + the poll's numbers are the last window's, not zeros + /mock?audio=fresh a panel nobody has configured: off, nothing heard, + nothing learned, the factory mapping + /mock?audio=nomic on, nothing on the data pin: + source "pdm (no mic - data pin idle)" + /mock?audio=stalled on, the microphone stopped answering: source + "synth (mic stalled)", and the numbers are the + built-in test tones (levels far above 1), not a room + /mock?audio=phone off, the phone app posting monitor frames: source + "phone", ext:true, its own 0..1 units - while levelsN + and outputs stay the microphone's stale ones + /mock?audio=ext off, the browser extension holding the :81 socket + (audioClients 1); nothing of its sound reaches a page + /mock?audio=noread the configuration read (GET /api/audio-in) gets no + reply, and only that: the poll and every POST still + answer. Any other state ends it. + +The switches on the page work from any of them (mic=1 says "synth" for the +quarter second the driver takes, as on the panel), and the routes follow the +caps: /mock?caps=patterns,params,sleep,audio-in is the microphone without the +browser path, where /api/audio is a 404. + Pages and the chrome, though, go out exactly as the panel sends them (src/core_send.h): each page stamped by console_pages.py's own code (the chrome's ?h= and the fallback PF), with the same ETag, the same 304, and the @@ -36,7 +70,9 @@ `h`, and nothing cacheable under a Host that is not an address, a bare name or .local. The build here changes whenever a page, the chrome or the fallback is saved, so a saved edit still shows on refresh: the old `v` is -redirected. +redirected. /audio-in is not read off disk: it is assembled from its sources +(build_audio_in_page.py, on every request), so saving the editor, the +adapter or the panel's CSS shows on refresh like any other page. The link can be made as bad as the panel's. On the desk every request is instant and parallel; on the panel's own hotspot it is a phone in power @@ -54,16 +90,25 @@ --chrome404 /pf-console.js answers 404: the pages run on the fallback PF stamped into them --sketch DIR serve another copy of firmware/patternflow + +Timing something on the desk? Open it as http://127.0.0.1:8322. This listens +on IPv4 only, a browser tries IPv6 first under the name localhost, and every +request here is a connection of its own - so that detour is paid each time +(Chromium on Windows: /audio-in polls 9.4 times a second under 127.0.0.1 and +4.8 under localhost). """ import argparse import gzip import json +import math import threading import time import os import posixpath +import random import re +import struct import sys import urllib.parse import zlib @@ -75,6 +120,7 @@ sys.path.insert(0, HERE) import console_pages # noqa: E402 the stamp is its code, not a copy of it +import build_audio_in_page # noqa: E402 /audio-in is assembled, by its code PORT = int(os.environ.get("PF_CONSOLE_PORT", "8322")) @@ -110,7 +156,10 @@ class Link: REVALIDATE = "no-cache" NO_STORE = "no-store" DESK_CACHE = "no-cache" -TCP_WINDOW = 5760 # lwIP TCP_SND_BUF on the panel: one round trip per this many bytes +# lwIP TCP_SND_BUF on the panel: one round trip per this many bytes of a reply's +# body. build_audio_in_page.py counts /audio-in in the same number (its WINDOW), +# so the weight line it prints is in this link's steps; change them together. +TCP_WINDOW = 5760 def payload_etag(payload): @@ -168,7 +217,7 @@ def host_cacheable(host): CHROME_SRC = RAW_ASSETS["/pf-console.js"][:2] CAPS_FULL = ["patterns", "params", "sleep", "osc", "shows", "weather", "mqtt", "clock", - "audio-in", "midi"] + "audio", "audio-in", "midi"] CAPS_BARE = ["patterns", "params", "sleep"] @@ -201,13 +250,19 @@ def console_build(): """The panel's `build` changes whenever its pages could have; here, whenever a page, the chrome, the fallback or the stamping code is saved - so the self-heal path runs on the desk, and an immutable page never outlives - an edit.""" + an edit. The assembled page's sources count too: most of them are not in + console/ at all (the editor is the extension's).""" h = 0 for name in sorted(os.listdir(HTML_DIR)): if name.endswith((".html", ".js")): h = zlib.crc32(("%s:%d" % (name, os.stat(os.path.join(HTML_DIR, name)).st_mtime_ns)).encode(), h) for f in (os.path.join(SKETCH, "src", "theme_index.h"), os.path.join(HERE, "console_pages.py")): h = zlib.crc32(str(os.stat(f).st_mtime_ns).encode(), h) + for f in build_audio_in_page.sources(SKETCH) + [build_audio_in_page.__file__]: + try: + h = zlib.crc32(str(os.stat(f).st_mtime_ns).encode(), h) + except OSError: + h = zlib.crc32(b"missing", h) # assemble() says which, when the page is asked for return "%08x" % (h & 0xFFFFFFFF) # What the hotspot's channel scan "saw", for the /wifi page's name suggestions. SEEN_NETWORKS = ["wifiiii", "SK_WiFiGIGA7A1C", "olleh_WiFi_2F3B", "U+Net4E21", "iptime"] @@ -265,7 +320,7 @@ def __init__(self): def status(self): p = self.patterns[self.active] if self.patterns else None hs = getattr(self, "hotspot", {"mode": "auto", "up": False}) - return { + return dict({ "version": "3.10.4", "build": console_build(), "variant": self.variant, @@ -311,7 +366,7 @@ def status(self): "mqttRole": "off", "mqttState": "off", "mqttConnected": False, - } + }, **AUDIO.status(self.caps)) # appended by the features, when the build has them def show_state(self): cur = next((s for s in self.shows if s["slug"] == self.show_slug), None) @@ -333,6 +388,775 @@ def show_state(self): } +# ── the audio features: /api/audio-in and /api/audio ───────────────────── +# +# Everything else here answers with a fixture. This cannot: /audio-in is an +# editor, and what has to be right on the desk is what the firmware DOES with +# what the page sends it - a request carrying one edge of one box, a pair of +# fields that settle together, a curve table refused without a word, levels +# that are stale rather than zero. A kinder mock hides exactly the mistakes +# that lose somebody's mapping on a panel. +# +# So this part is a port, not a fixture: of features/audio_in/ +# core_audio_in_http.h (handleGet, handleSet, applyBandArgs, handleReset, +# parseFrame), core_audio_in_map.h (the defaults, clampRange, the gate, the +# mapping), the analysis task in feature_audio_in.h and the two /api/audio +# routes in features/audio/feature_audio.h - same order of fields, same +# decimals, same clamps, same refusals. docs/rest-api.md ("Microphone") is the +# contract in prose. WHEN ONE OF THOSE HANDLERS CHANGES, CHANGE THIS WITH IT. +# +# What is not ported is the sound. There is no FFT here: Room below is what one +# would have produced, for a room this file makes up. + +BIN_HZ = 16000.0 / 512.0 # a 512-point transform at 16 kHz +MAX_BIN = 256 +MIN_HZ, MAX_HZ = BIN_HZ, MAX_BIN * BIN_HZ +LUT_POINTS = 33 +SPEC_BUCKETS = 64 +BAND_FIELDS = ("hzMin", "hzMax", "inMin", "inMax", "gain", "outMin", "outMax", "knob", + "muted", "lut", "meta") +# core_audio_in_map.h resetBand(): hzMin hzMax inMin inMax gain outMin outMax +BAND_DEFAULTS = ((62.0, 375.0, 0.200, 0.900, 1.0, 0.30, 0.85), + (375.0, 1500.0, 0.120, 0.650, 1.2, 0.30, 0.85), + (1500.0, 5000.0, 0.045, 0.160, 1.6, 0.30, 0.85), + (5000.0, 8000.0, 0.012, 0.030, 1.8, 0.30, 0.85)) +AUDIO_ROUTES = ("/api/audio-in", "/api/audio-in/reset", "/api/audio") + +# /mock?audio=: the microphone switch, what is on the data pin, whether +# the phone app is posting monitor frames, how many senders hold the :81 +# socket. Each is entered as if the panel had been that way for a while. +# +# The start: the gate's noise reference is whatever the first two seconds of +# analysis bottom out at (trackEnvelopes' warm-up), and the first window is +# half empty with the transform's own smoothing climbing from zero - so a +# microphone that comes up in a steady room teaches the reference a fraction +# of the real level, and the gate then stands open on a sound that never +# changes. The real headers do exactly that when built on a host and fed a +# steady tone (reference at half the level, gate open, knob at its top). +# Whether a panel does it in a quiet room depends on what its microphone's +# first windows really hold, and nobody at this desk has measured that. The +# room here starts with a pop (see window()), which lands the reference on +# the floor: the behaviour the firmware's own notes describe. Until a panel +# says otherwise, "silence: every gate shut" is that assumption. +AUDIO_STATES = { + # switch, on the pin, phone frames, socket senders + "music": (True, "music", False, 0), + "silence": (True, "silence", False, 0), + "off": (False, "music", False, 0), + "fresh": (False, "music", False, 0), + "nomic": (True, "rail", False, 0), + "stalled": (True, "tones", False, 0), + "phone": (False, "music", True, 1), + "ext": (False, "music", False, 1), +} + + +def f32(v): + """A C float. Every number the firmware keeps is one, and what it prints is + the float's value, not the decimal the request carried.""" + try: + return struct.unpack("f", struct.pack("f", v))[0] + except OverflowError: + return math.copysign(math.inf, v) + + +def fstr(v, prec): + """Arduino's String(float, prec), which is the ESP32 core's dtostrf: half a + last digit is added and the rest cut off, digit by digit. Not printf: an + input gain of 8.25 reads back as 8.3 here and on the panel, 8.2 from '%.1f'.""" + if v != v: + return "nan" + if v in (math.inf, -math.inf): + return "inf" + neg = v < 0.0 + if neg: + v = -v + rounding = 2.0 + for _ in range(prec): + rounding *= 10.0 + v += 1.0 / rounding + tenpow, digits = 1.0, 1 + while v >= 10.0 * tenpow: + tenpow *= 10.0 + digits += 1 + v /= tenpow + out = ["-"] if neg else [] + left = digits + prec + while left > 0: + left -= 1 + d = min(9, int(v)) + out.append(chr(48 + d)) + if left == prec and prec > 0: + out.append(".") + v = (v - d) * 10.0 + return "".join(out) + + +ATOF = re.compile(r"[ \t\n\v\f\r]*([+-]?(?:[0-9]+\.?[0-9]*(?:[eE][+-]?[0-9]+)?" + r"|\.[0-9]+(?:[eE][+-]?[0-9]+)?|inf(?:inity)?|nan))", re.I) +ATOL = re.compile(r"[ \t\n\v\f\r]*([+-]?[0-9]+)") + + +def to_float(s): + """String::toFloat(): the number the text starts with, 0 when it starts + with none - so "abc" is 0, "1.5x" is 1.5 and "nan" is a NaN. Every clamp + lets a NaN through, so nothing takes one from here without finite_or().""" + m = ATOF.match(s) + return f32(float(m.group(1))) if m else 0.0 + + +def finite_or(v, fallback): + """PFAudioInMap::finiteOr(): NaN and the infinities are not settings. The + field keeps what it had (and a number too large for a float is an inf).""" + return v if math.isfinite(v) else fallback + + +def to_int(s): + m = ATOL.match(s) + return int(m.group(1)) if m else 0 + + +def constrain(v, lo, hi): + return lo if v < lo else hi if v > hi else v + + +def clamp01(v): + return 0.0 if v < 0.0 else 1.0 if v > 1.0 else v + + +def truthy(v): + return v in ("1", "true") + + +def bin_of(hz): + """core_audio_in_map.h binOf(). A NaN edge lands on bin 1 here; on the + panel that cast is undefined, and nothing sane depends on it.""" + b = int(hz / BIN_HZ + 0.5) if hz == hz else 1 + return 1 if b < 1 else MAX_BIN if b > MAX_BIN else b + + +class Args: + """server.hasArg() and server.arg(), over what the panel's web server + would have parsed (src/webserver/Parsing.cpp). Its rules, because a page + can be wrong about each of them and still work on a kinder parser: + + - the query string first, then the body, and arg() is the FIRST value; + - the body only under Content-Type: application/x-www-form-urlencoded. A + POST without that header reaches the handler with no body arguments at + all - a `band=2` reset sent that way resets every band; + - a name with no `=` is not an argument (`?levels` is the configuration); + - a name with an empty value is one (`lut2=` clears a table). + + Not read here, though the panel reads it: a multipart body (FormData). + + Values are kept as bytes-in-a-str (latin-1), as the panel keeps bytes: a + curve's `meta` is cut to 31 of them, not 31 characters.""" + + def __init__(self, query, body, ctype): + data = query + if body and ctype.startswith("application/x-www-form-urlencoded"): + data = (data + "&" if data else "") + body + self.list = [] + for part in data.split("&"): + k, eq, v = part.partition("=") + if eq: + self.list.append(tuple( + urllib.parse.unquote_to_bytes(x.replace("+", " ")).decode("latin-1") + for x in (k, v))) + + def has(self, name): + return any(k == name for k, _ in self.list) + + def get(self, name): + return next((v for k, v in self.list if k == name), "") + + def num(self, name, fallback): + """argFloat(): absent, empty, or not a finite number leaves the + stored value alone.""" + v = self.get(name) + return finite_or(to_float(v), fallback) if v else fallback + + +def parse_lut(csv): + """33 samples, 0..255, commas and digits only; anything else is no table.""" + out, acc, has = [], 0, False + for c in csv + ",": + if "0" <= c <= "9": + acc, has = acc * 10 + ord(c) - 48, True + if acc > 255: + return None + elif c == ",": + if not has or len(out) >= LUT_POINTS: + return None + out.append(acc) + acc, has = 0, False + else: + return None + return out if len(out) == LUT_POINTS else None + + +class Room: + """What a window of sound would have come out of the transform as: a + magnitude per FFT bin, in the firmware's unit (linear, unnormalized), built + from a few fixed shapes whose loudness is all that changes from one window + to the next. fold() and spectrum() are averages over bins, so the mock + takes the same averages of these. + + The numbers are sized from core_audio_in_map.h's own measurements at input + gain 8: the quiet-room medians (0.146 / 0.040 / 0.020 / 0.017 per default + band) for the floor, and music's p10..p90 (0.24..0.66, 0.16..0.49, + 0.05..0.12, 0.011..0.019) for the four voices - including the top band + barely clearing its own noise, which is the content and not a bug.""" + + FLOOR = ((31, 0.30), (62, 0.26), (125, 0.185), (250, 0.118), (375, 0.078), (750, 0.044), + (1500, 0.027), (3000, 0.019), (5000, 0.017), (8000, 0.016)) + # centre Hz, width in decades, loudness + VOICES = ((150.0, 0.45, 0.76), # kick and bass + (750.0, 0.26, 0.52), # the mids: chords, a voice + (2600.0, 0.22, 0.125), # presence + (6600.0, 0.10, 0.09)) # hats: the only thing the top band hears + # The built-in three-tone test signal (120 / 900 / 4200 Hz), which the + # analysis runs on when the microphone is not delivering: its 64-bucket + # spectrum as the real handler printed it on a host build (GET + # ?levels=1 with the I2S install failing). Nothing here is under 1.0 by + # accident - the transform is not normalized. + TONES = (0.89, 0.89, 0.89, 0.89, 0.89, 4.416, 4.416, 4.416, 4.416, 4.416, 4.416, 79.407, + 79.407, 79.407, 79.407, 125.878, 125.878, 125.878, 48.977, 48.977, 2.49, 2.49, + 0.699, 0.699, 0.295, 0.152, 0.089, 0.089, 0.056, 0.037, 0.021, 0.011, 0.006, + 0.003, 0.012, 0.034, 0.103, 0.513, 26.728, 34.585, 0.267, 0.043, 0.015, 0.006, + 0.003, 0.002, 0.001, 0.001, 0.0, 0.0, 0.0, 0.0, 0.0, 0.001, 0.001, 0.013, 8.995, + 0.023, 0.001, 0.0, 0.0, 0.0, 0.0, 0.0) + + def __init__(self): + # PFAudioFFT::spectrum(): which bins each of the 64 log buckets averages. + lg0, lg1 = math.log10(MIN_HZ), math.log10(MAX_HZ) + self.buckets = [] + for i in range(SPEC_BUCKETS): + k0 = bin_of(10 ** (lg0 + (lg1 - lg0) * i / SPEC_BUCKETS)) + k1 = bin_of(10 ** (lg0 + (lg1 - lg0) * (i + 1) / SPEC_BUCKETS)) + self.buckets.append((k0, min(MAX_BIN, max(k1, k0 + 1)))) + hz = [k * BIN_HZ for k in range(MAX_BIN)] + floor = [0.0] + [self.floor_at(f) for f in hz[1:]] + voices = [[0.0] + [amp * math.exp(-((math.log10(f / fc) / width) ** 2)) for f in hz[1:]] + for fc, width, amp in self.VOICES] + tones = [0.0] * MAX_BIN + for (k0, k1), v in zip(self.buckets, self.TONES): + tones[k0:k1] = [v] * (k1 - k0) + # Running sums, so a band of any width is two lookups. + self.sums = [self.running(bins) for bins in [floor] + voices + [tones]] + + def floor_at(self, f): + pts = self.FLOOR + for (f0, v0), (f1, v1) in zip(pts, pts[1:]): + if f <= f1: + u = math.log(max(f, f0) / f0) / math.log(f1 / f0) + return v0 * (v1 / v0) ** u + return pts[-1][1] + + @staticmethod + def running(bins): + out, s = [0.0], 0.0 + for v in bins: + s += v + out.append(s) + return out + + def mean(self, part, lo, hi): + """The average of one shape over bins lo..hi-1, as fold() takes it. A + band with no bins in it (a low edge in the top bin) hears nothing.""" + s = self.sums[part] + return (s[hi] - s[lo]) / (hi - lo) if hi > lo else 0.0 + + @staticmethod + def music(t): + """How loud each voice is at time t, on a 24 second loop at 120 bpm: + a verse with next to no treble (the top band's gate stays shut, as it + does on a panel at listening volume), a chorus with hats, then two + seconds of near silence - so every gate can be watched opening and + closing.""" + bar = t % 24.0 + kick = math.exp(-5.0 * ((t * 2.0) % 1.0)) + hat = math.exp(-2.2 * ((t * 2.0 + 0.5) % 1.0)) + swell = 0.5 + 0.5 * math.sin(t * 2 * math.pi / 5.3) + lead = 0.5 + 0.5 * math.sin(t * 2 * math.pi / 3.1 + 1.0) + k = 0.05 if bar >= 22.0 else 1.0 + return [k * (0.20 + 0.80 * kick), k * (0.40 + 0.60 * swell), k * (0.35 + 0.65 * lead), + k * (1.0 if bar >= 10.0 else 0.06) * (0.25 + 0.75 * hat)] + + +ROOM = Room() +NOISE = 0 # Room.sums: the floor, then the four voices, then the test tones + + +class Audio: + """The two audio features of a panel, as far as /audio-in can tell.""" + + def __init__(self): + self.lock = threading.Lock() + self.rng = random.Random(7) + self.runtime = True # AUD, /api/audio's switch (feature_audio.h: on by default) + self.clients = 0 # senders on the :81 socket + self.noread = False # /mock?audio=noread + self.luts = [[0] * LUT_POINTS for _ in range(4)] + self.factory() + self.scene("music") + + # ── settings (core_audio_in_map.h) ─────────────────────────────────── + def reset_band(self, i): + """resetBand(): the measured defaults, and the band back on its gain + exponent. The table's bytes stay; only lutSet says it is not used.""" + d = BAND_DEFAULTS[i] + self.bands[i] = {"hzMin": d[0], "hzMax": d[1], "inMin": f32(d[2]), "inMax": f32(d[3]), + "gain": f32(d[4]), "outMin": f32(d[5]), "outMax": f32(d[6]), + "knob": i, "muted": False} + self.lut_set[i] = False + self.metas[i] = "" + + def reset_bands(self): + for i in range(4): + self.reset_band(i) + self.smoothing, self.attack = f32(0.35), f32(0.65) + + def factory(self): + """A panel nobody has configured: load() with nothing in NVS.""" + self.bands, self.lut_set, self.metas = [None] * 4, [False] * 4, [""] * 4 + self.reset_bands() + self.mic_on, self.mic_gain, self.auto = False, 8.0, True + + @staticmethod + def clamp_range(x): + x["hzMin"] = constrain(x["hzMin"], MIN_HZ, MAX_HZ) + x["hzMax"] = constrain(x["hzMax"], MIN_HZ, MAX_HZ) + if x["hzMin"] > x["hzMax"]: + x["hzMin"], x["hzMax"] = x["hzMax"], x["hzMin"] + if f32(x["hzMax"] - x["hzMin"]) < BIN_HZ: + x["hzMax"] = min(MAX_HZ, f32(x["hzMin"] + BIN_HZ)) + + # ── /mock?audio= ───────────────────────────────────────────────────── + def scene(self, name): + mic, room, phone, clients = AUDIO_STATES[name] + self.noread = False + if name == "fresh": + self.factory() + # The analysis as it is at boot: nothing heard, no floor learned. + self.fft = [0.0] * 4 # PFAudioFFT::bands + self.noise_ref = [-1.0] * 4 # < 0 = unlearned + self.env_hi = [0.0] * 4 + self.gate = [False] * 4 + self.votes = [0] * 4 + self.warmup = 120 + self.smooth = [0.0] * 4 # smoothLevel + self.raw_peak = self.raw_dc = 0.0 + self.windows = self.passes = 0 + self.live = self.stalled = False + self.park = self.since = 0 # passes left asleep; windows since I2S came up + self.t = 0.0 + self.last = None # the window spectrum() would read + self.ext_levels, self.ext_env, self.ext_spec = [0.0] * 4, [0.0] * 8, [0.0] * 64 + self.ext_at = self.poll_at = None + self.phone, self.phone_at = phone, 0.0 + self.phone_lo, self.phone_hi = [1.0] * 4, [0.0] * 4 + self.clients = clients + self.state = name + # ...and then as if it had been in this state for a while. The states + # with the switch off (fresh aside) heard music first, so what they + # hold is what a panel holds: the last window, not zeros. So did the + # microphone that then stopped answering. + if name != "fresh": + self.mic_on = self.live = True + if room in ("music", "tones"): + self.hear("silence", 180) # the warm-up finds the floor + self.hear("music", 720) + if room != "music": + self.hear(room, 1800 if room == "silence" else 300) # envelopes settle slowly + self.room, self.mic_on = room, mic + if not mic: + self.live = self.stalled = False + self.clock = time.monotonic() + if phone: + for back in range(120, 0, -1): + self.phone_frame(self.clock - back * 0.25, True) + + def hear(self, room, windows): + self.room, self.stalled = room, room == "tones" + for _ in range(windows): + self.window() + + # ── the analysis task (feature_audio_in.h), 60 passes a second ─────── + def advance(self, now): + """Catch the analysis up to now. No thread: nothing changes that a + request cannot see, so it runs when one arrives - two seconds' worth at + most, which is all a tab that was away needs.""" + n = int((now - self.clock) * 60.0) + if n > 0: + self.clock = now if n > 120 else self.clock + n / 60.0 + for _ in range(min(n, 120)): + self.tick() + if self.phone: + self.phone_frame(now) + + def tick(self): + if self.park: + self.park -= 1 + return + if not self.mic_on: + # Parked: I2S released, and a look at the switch every 250 ms. + # That wait is why `source` says "synth" for a moment after mic=1. + self.live = self.stalled = False + self.park = 14 + return + if not self.live: # PFAudioPdm::begin() + self.live, self.stalled, self.since = True, self.room == "tones", 0 + self.window() + + def window(self): + """analyze(), then the gate and the damping, as the task runs them.""" + self.t += 1.0 / 60.0 + self.passes += 1 + jit = [1.0 + 0.16 * (self.rng.random() - 0.5) for _ in range(4)] + if self.room == "tones": + # The microphone is not delivering, so the window is the test + # signal: no input gain, and a little drift as the window slides. + wob = 1.0 + 0.015 * math.sin(self.passes * 0.61) + scale, amps, noisy = 1.0, [0.0] * 4 + [wob], False + self.raw_peak, self.raw_dc = 0.99439, -0.0158 + 0.006 * math.sin(self.passes * 0.61) + elif self.room == "rail": + # Nothing on the data pin: it floats to a rail, every sample is + # the same, and a constant has no energy outside bin 0. Measured. + scale, amps, noisy = 0.0, [0.0] * 5, False + self.raw_peak, self.raw_dc = 0.94406, -0.94406 + self.windows += 1 + else: + voices = ROOM.music(self.t) if self.room == "music" else [0.0] * 4 + scale, amps, noisy = self.mic_gain / 8.0, voices + [0.0], True + # A microphone starts with a pop: its first few windows are far + # above the room. That is an assumption about the part, and it + # decides what the gate learns - see "The start", above + # AUDIO_STATES. + pop = 10.0 * 0.45 ** self.since if self.since < 10 else 0.0 + jit = [j + pop for j in jit] + self.raw_peak = 0.0036 + 0.014 * voices[0] + 0.0004 * jit[0] + self.raw_dc = -0.0041 + 0.0003 * (jit[1] - 1.0 - pop) + self.windows += 1 + self.since += 1 + self.last = (scale, amps, noisy, self.passes) + for b in range(4): + lo, hi = self.bins(b) + if hi <= lo: # fold(): no bin, so 0 at once + self.fft[b] = 0.0 + continue + v = scale * self.heard((lo, hi), amps, jit[b] if noisy else 0.0) + self.fft[b] = f32(self.fft[b] * 0.7 + v * 0.3) # fold()'s own smoothing + self.track() + a30, k30 = constrain(self.smoothing, 0.05, 0.9), constrain(self.attack, 0.05, 0.9) + rel60, atk60 = 1.0 - math.sqrt(1.0 - a30), 1.0 - math.sqrt(1.0 - k30) + for i in range(4): # smoothLevels() + a = atk60 if self.fft[i] > self.smooth[i] else rel60 + self.smooth[i] = f32(self.smooth[i] + (self.fft[i] - self.smooth[i]) * a) + + def bins(self, b): + """fold(): the FFT bins band b is averaged over, lo..hi-1.""" + lo = bin_of(self.bands[b]["hzMin"]) + return lo, min(MAX_BIN, max(bin_of(self.bands[b]["hzMax"]), lo + 1)) + + @staticmethod + def heard(bins, amps, floor): + """The average magnitude over those bins: the voices (and the test + tones) at these loudnesses, over this much of the noise floor.""" + return floor * ROOM.mean(NOISE, *bins) + sum( + a * ROOM.mean(i + 1, *bins) for i, a in enumerate(amps)) + + def track(self): + """trackEnvelopes(): the noise reference, the peak envelope, the gate.""" + warming = self.warmup > 0 + if warming: + self.warmup -= 1 + for i in range(4): + v, ref = clamp01(self.fft[i]), self.noise_ref[i] + if ref < 0.0: + ref = v + elif warming: + ref = min(ref, v) + elif not self.gate[i] and v < ref * 1.5 + 0.005: + ref += (v - ref) * 0.005 + self.noise_ref[i] = ref + self.env_hi[i] += (v - self.env_hi[i]) * (0.3 if v > self.env_hi[i] else 0.002) + lo = ref * 1.5 + if self.env_hi[i] < lo + 0.02: + self.env_hi[i] = lo + 0.02 + open_abs = (0.012, 0.050, 0.015, 0.012)[i] + if not self.gate[i]: + self.votes[i] = self.votes[i] + 1 if v > ref * 1.8 + open_abs else 0 + if self.votes[i] >= 8: + self.gate[i], self.votes[i] = True, 0 + else: + self.votes[i] = self.votes[i] + 1 if v < ref * 1.4 + open_abs \ + else max(0, self.votes[i] - 3) + if self.votes[i] >= 25: + self.gate[i], self.votes[i] = False, 0 + + # ── a level to a knob (core_audio_in_map.h) ────────────────────────── + def normalized(self, b, level): + if not self.gate[b]: + return 0.0 + lo = self.noise_ref[b] * 1.5 + return clamp01((level - lo) / max(self.env_hi[b] - lo, 0.02)) + + def mapped_window(self, b, level, in_min, in_max): + x = self.bands[b] + in_min = clamp01(in_min) + in_max = max(in_min + f32(0.01), clamp01(in_max)) + u = clamp01((level - in_min) / (in_max - in_min)) + if self.lut_set[b] and u == u: + pos = clamp01(u) * (LUT_POINTS - 1) + i = int(pos) + j = min(i + 1, LUT_POINTS - 1) + u = (self.luts[b][i] * (1.0 - (pos - i)) + self.luts[b][j] * (pos - i)) / 255.0 + else: + u = u ** (1.0 / constrain(x["gain"], f32(0.2), 4.0)) + return f32(x["outMin"] + u * (x["outMax"] - x["outMin"])) + + def mapped(self, b, level): + if self.auto: + return self.mapped_window(b, self.normalized(b, level), f32(0.10), f32(0.95)) + return self.mapped_window(b, level, self.bands[b]["inMin"], self.bands[b]["inMax"]) + + def source(self): + """sourceLabel(): five strings, matched exactly by the page.""" + if not self.mic_on: + return "off" + if not self.live: + return "synth" + if self.stalled: + return "synth (mic stalled)" + dead = abs(self.raw_dc) >= 0.5 and not any(v > 1e-4 for v in self.fft) + return "pdm (no mic - data pin idle)" if dead else "pdm" + + def spectrum(self): + """PFAudioFFT::spectrum(): 64 log buckets of the LAST window - so with + the analysis parked it is what that window left, again and again.""" + if not self.last: + return [0.0] * SPEC_BUCKETS + scale, amps, noisy, seed = self.last + rng = random.Random(seed) + return [scale * self.heard(bucket, amps, 1.0 + 0.5 * (rng.random() - 0.5) if noisy else 0.0) + for bucket in ROOM.buckets] + + # ── the phone app's monitor frames ─────────────────────────────────── + def ext_fresh(self, now): + return self.ext_at is not None and now - self.ext_at < 3.0 and not self.mic_on + + def watching(self, now): + return self.poll_at is not None and now - self.poll_at < 2.5 + + def parse_frame(self, s, now): + """parseFrame(): 4 levels; 4 x lo,hi; 64 buckets. 76 finite numbers + or nothing.""" + vals = [to_float(tok) for tok in re.split("[,;]", s) if tok][:76] + if len(vals) != 76 or not all(math.isfinite(v) for v in vals): + return + self.ext_levels, self.ext_env, self.ext_spec = vals[:4], vals[4:12], vals[12:] + self.ext_at = now + + def phone_frame(self, now, due=False): + """/mock?audio=phone: what the capture app posts, in its own text and + through the same parser - four frames a second while a page is + polling, one every two seconds when none is (it reads `watch`). The + phone analyses on the phone, in its own unit: 0..1 across its dB axis.""" + if not due and now - self.phone_at < (0.25 if self.watching(now) else 2.0): + return + self.phone_at = now + + def unit(v): + return clamp01((20.0 * math.log10(max(v, 1e-4)) + 45.0) / 47.0) + + amps = ROOM.music(now % 240.0) + levels = [] + for b in range(4): + v = unit(self.heard(self.bins(b), amps, 1.0)) + levels.append(v) + self.phone_hi[b] = v if v > self.phone_hi[b] else \ + self.phone_hi[b] + (v - self.phone_hi[b]) * 0.03 + self.phone_lo[b] = v if v < self.phone_lo[b] else \ + self.phone_lo[b] + (v - self.phone_lo[b]) * 0.03 + self.phone_hi[b] = max(self.phone_hi[b], self.phone_lo[b] + 0.06) + spec = [unit(self.heard(bucket, amps, 1.0)) for bucket in ROOM.buckets] + self.parse_frame("%s;%s;%s" % ( + ",".join("%.3f" % v for v in levels), + ",".join("%.3f,%.3f" % (self.phone_lo[b], self.phone_hi[b]) for b in range(4)), + ",".join("%.2f" % v for v in spec)), now) + + # ── the replies (core_audio_in_http.h): field order and decimals are the + # handler's, because the page compares what it re-reads ────────────── + def poll_json(self, now): + ext = self.ext_fresh(now) + j = '{"source":"%s","ext":%s' % ("phone" if ext else self.source(), "true" if ext else "false") + j += ',"rawPeak":%s,"rawDc":%s,"dropped":0' % (fstr(self.raw_peak, 5), fstr(self.raw_dc, 5)) + j += ',"levels":[' + ",".join(fstr(v, 4) for v in (self.ext_levels if ext else self.smooth)) + # Always the microphone's, also while the phone's frames are served. + j += '],"levelsN":[' + ",".join(fstr(self.normalized(i, self.smooth[i]), 4) for i in range(4)) + j += '],"outputs":[' + ",".join(fstr(self.mapped(i, self.smooth[i]), 4) for i in range(4)) + if ext: + j += '],"env":[' + ",".join('{"lo":%s,"hi":%s}' % (fstr(self.ext_env[i * 2], 4), + fstr(self.ext_env[i * 2 + 1], 4)) + for i in range(4)) + elif self.auto: + j += '],"env":[' + ",".join('{"lo":%s,"hi":%s}' % (fstr(self.noise_ref[i] * 1.5, 4), + fstr(self.env_hi[i], 4)) + for i in range(4)) + j += '],"spectrum":[' + ",".join(fstr(v, 3) for v in (self.ext_spec if ext else self.spectrum())) + return j + "]}" + + def config_json(self): + j = '{"source":"%s","micOn":%s,"micGain":%s,"autoRange":%s,"smoothing":%s,"attack":%s' % ( + self.source(), "true" if self.mic_on else "false", fstr(self.mic_gain, 1), + "true" if self.auto else "false", fstr(self.smoothing, 3), fstr(self.attack, 3)) + j += ',"rawPeak":%s,"rawDc":%s,"windows":%d' % (fstr(self.raw_peak, 5), fstr(self.raw_dc, 5), + self.windows) + j += ',"level":[' + ",".join(fstr(v, 4) for v in self.fft) + j += '],"out":[' + ",".join(fstr(self.mapped(i, self.fft[i]), 4) for i in range(4)) + bands = [] + for i, b in enumerate(self.bands): + bands.append( + '{"hzMin":%s,"hzMax":%s,"inMin":%s,"inMax":%s,"gain":%s,"outMin":%s,"outMax":%s,' + '"knob":%d,"muted":%s,"meta":"%s","lutSet":%s}' % ( + fstr(b["hzMin"], 1), fstr(b["hzMax"], 1), fstr(b["inMin"], 4), + fstr(b["inMax"], 4), fstr(b["gain"], 3), fstr(b["outMin"], 3), + fstr(b["outMax"], 3), b["knob"], "true" if b["muted"] else "false", + self.metas[i].replace("\\", "\\\\").replace('"', '\\"'), + "true" if self.lut_set[i] else "false")) + j += '],"bands":[' + ",".join(bands) + j += '],"hzRange":[%s,%s' % (fstr(MIN_HZ, 2), fstr(MAX_HZ, 2)) + j += '],"spec":[' + ",".join(fstr(v, 3) for v in self.spectrum()) + return j + "]}" + + def apply_band(self, b, sfx, a): + """applyBandArgs(): whatever arrived under these names; the two pairs + (hzMin/hzMax, inMin/inMax) are settled after, whichever half came.""" + x = self.bands[b] + x["hzMin"] = a.num("hzMin" + sfx, x["hzMin"]) + x["hzMax"] = a.num("hzMax" + sfx, x["hzMax"]) + self.clamp_range(x) + x["inMin"] = constrain(a.num("inMin" + sfx, x["inMin"]), 0.0, 1.0) + x["inMax"] = constrain(a.num("inMax" + sfx, x["inMax"]), 0.0, 1.0) + x["gain"] = constrain(a.num("gain" + sfx, x["gain"]), f32(0.2), 4.0) + x["outMin"] = constrain(a.num("outMin" + sfx, x["outMin"]), 0.0, 1.0) + x["outMax"] = constrain(a.num("outMax" + sfx, x["outMax"]), 0.0, 1.0) + if a.has("knob" + sfx): + x["knob"] = constrain(to_int(a.get("knob" + sfx)), 0, 3) + if a.has("muted" + sfx): + x["muted"] = truthy(a.get("muted" + sfx)) + floor = f32(x["inMin"] + f32(0.01)) + if x["inMax"] < floor: + x["inMax"] = min(1.0, floor) + if a.has("lut" + sfx): + csv = a.get("lut" + sfx) + table = parse_lut(csv) if csv else None + if not csv: + self.lut_set[b] = False # empty clears; the band runs `gain` + elif table: + self.luts[b], self.lut_set[b] = table, True + # ...and anything else is ignored: the reply is still {"ok":true} + if a.has("meta" + sfx): + self.metas[b] = a.get("meta" + sfx).split("\0")[0][:31] + + def handle_set(self, a, now): + if a.has("frame"): + # A monitor frame is state, not settings: nothing else is read. + self.parse_frame(a.get("frame"), now) + return 200, '{"ok":true,"watch":%s}' % ("true" if self.watching(now) else "false") + single = -1 + if a.has("band"): + single = to_int(a.get("band")) + if single < 0 or single > 3: # refused before anything is applied + return 400, '{"ok":false,"error":"band must be 0-3"}' + if a.has("mic"): + self.mic_on = truthy(a.get("mic")) + if self.mic_on and not self.live: + # The parked task looks at the switch every 250 ms, so on a + # panel the driver is up some time inside that. Here it is + # always the whole quarter second: the "synth" a page must + # not take for a verdict is then there every time. + self.park = 15 + if a.has("auto"): + self.auto = truthy(a.get("auto")) + if a.has("micGain"): + self.mic_gain = constrain(finite_or(to_float(a.get("micGain")), self.mic_gain), 1.0, 16.0) + if a.has("smoothing"): + self.smoothing = constrain(finite_or(to_float(a.get("smoothing")), self.smoothing), + f32(0.05), f32(0.9)) + if a.has("attack"): + self.attack = constrain(finite_or(to_float(a.get("attack")), self.attack), + f32(0.05), f32(0.9)) + if single >= 0: + self.apply_band(single, "", a) + for b in range(4): + # A band is applied when the request names ANY of its fields. + if any(a.has(f + str(b)) for f in BAND_FIELDS): + self.apply_band(b, str(b), a) + return 200, '{"ok":true}' + + def handle_reset(self, a): + if a.has("band"): + v = a.get("band") + if len(v) != 1 or not "0" <= v <= "3": # one digit, or nothing happens + return 400, '{"ok":false,"error":"band must be 0-3"}' + self.reset_band(int(v)) + return 200, '{"ok":true,"band":%s}' % v + self.reset_bands() # every band and curve, damping, attack... + self.mic_gain = 8.0 # ...and the input gain. Not the switch, not auto. + return 200, '{"ok":true}' + + def request(self, method, path, a, caps): + """(status, body) as the firmware answers it, or None where this build + has no such route and the web server's own 404 goes out.""" + now = time.monotonic() + self.advance(now) + if path == "/api/audio" and "audio" in caps: + if method == "GET": + return 200, '{"audioRuntime":%s,"audioClients":%d}' % ( + "true" if self.runtime else "false", self.clients) + if method == "POST": + if a.has("on"): + self.runtime = truthy(a.get("on")) + return 200, '{"ok":true,"audioRuntime":%s}' % ("true" if self.runtime else "false") + if path == "/api/audio-in" and "audio-in" in caps: + if method == "GET" and a.has("levels"): + if not a.has("idle"): + self.poll_at = now # a page is watching: the phone reads this + return 200, self.poll_json(now) + if method == "GET": + return 200, self.config_json() + if method == "POST": + return self.handle_set(a, now) + if path == "/api/audio-in/reset" and "audio-in" in caps and method == "POST": + return self.handle_reset(a) + return None + + def status(self, caps): + """What the two features append to /api/status.""" + with self.lock: + self.advance(time.monotonic()) + out = {} + if "audio" in caps: + out.update(audioRuntime=self.runtime, audioClients=self.clients) + if "audio-in" in caps: + # The timings are of the right order (the transform measured + # 329 us on a panel), not a capture; the rest is the model's. + runs = max(0, self.passes - 60) + out["audioIn"] = { + "core": 0, "lastUs": 1764 if runs else 0, "avgUs": 1742 if runs else 0, + "fillUs": 1307 if runs else 0, "fftUs": 329 if runs else 0, + "foldUs": 96 if runs else 0, "maxUs": 2410 if runs else 0, "runs": runs, + "heap": 0, "staticBytes": 9472, "source": self.source(), + "micWindows": self.windows, "micDropped": 0, + "rawPeak": round(self.raw_peak, 5), "rawDc": round(self.raw_dc, 5), + "bands": [round(v, 4) for v in self.fft], + } + return out + + +AUDIO = Audio() DEV = Device() FEATURE_NAV = feature_nav() @@ -354,10 +1178,12 @@ def log_message(self, fmt, *a): sys.stderr.write(" %s %s\n" % (self.command, self.path)) def log_request(self, code="-", size="-"): - if Link.rtt or Link.serial: - sys.stderr.write(" %s %s -> %s\n" % (self.command, self.path, code)) + # `note`: what an audio request carried, or how it ended (see audio()). + note, self.note = getattr(self, "note", ""), "" + if Link.rtt or Link.serial or (note and code != 200): + sys.stderr.write(" %s %s%s -> %s\n" % (self.command, self.path, note, code)) else: - sys.stderr.write(" %s %s\n" % (self.command, self.path)) + sys.stderr.write(" %s %s%s\n" % (self.command, self.path, note)) # Each request is a connection of its own (Connection: close, here as on # the panel): the handshake and the request cost a round trip @@ -366,10 +1192,16 @@ def log_request(self, code="-", size="-"): def handle_one_request(self): if Link.rtt: time.sleep(Link.rtt) - if Link.serial: - with Link.lock: - return BaseHTTPRequestHandler.handle_one_request(self) - return BaseHTTPRequestHandler.handle_one_request(self) + try: + if Link.serial: + with Link.lock: + return BaseHTTPRequestHandler.handle_one_request(self) + return BaseHTTPRequestHandler.handle_one_request(self) + except ConnectionError: + # The browser left mid-reply: a link clicked, a tab closed. A page + # that polls ten times a second does that on most navigations, + # and a traceback for each one buries the log. + self.close_connection = True def write_body(self, body): if not (Link.rtt or Link.bps): @@ -432,14 +1264,18 @@ def send_console(self, text, ctype, cache, etag=True): def send_page(self, path, name): """PFSend::PAGE: a page stamped exactly as console_pages.py build - stamps it into the header.""" + stamps it into the header. One page is not a file to read: /audio-in + is assembled from its sources by the code `build` assembles it with, + here on every request, so its sources are save-and-refresh too.""" f = os.path.join(HTML_DIR, name + ".html") - if not os.path.exists(f): + assembled = name == build_audio_in_page.PAGE + if not assembled and not os.path.exists(f): return self.send("no console/%s.html — run console_pages.py extract" % name, "text/plain", 404) try: - text = console_pages.stamp(read(f), console_pages.crc_of(raw_literal(*CHROME_SRC)), - console_pages.load_shim(), name) + text = console_pages.stamp( + build_audio_in_page.assemble(SKETCH) if assembled else read(f), + console_pages.crc_of(raw_literal(*CHROME_SRC)), console_pages.load_shim(), name) except (ValueError, RuntimeError, OSError) as e: # build would refuse this page; show why instead of a page the # device could never have served @@ -539,6 +1375,18 @@ def do_GET(self): else DESK_CACHE, etag=False) if path == "/mock": + dest = "/" + if any(k == "audio" for k, _ in self.qlist): + want = self.arg("audio") + if want != "noread" and want not in AUDIO_STATES: + return self.send("audio= is one of: %s, noread\n" % ", ".join(AUDIO_STATES), + "text/plain", 400) + with AUDIO.lock: + if want == "noread": + AUDIO.noread = True + else: + AUDIO.scene(want) + dest = "/audio-in" if "variant" in q: DEV.variant = q["variant"] or "core" want = q.get("caps", "full") if "caps" in q else None @@ -549,9 +1397,12 @@ def do_GET(self): DEV.via_hotspot = q["hotspot"] in ("1", "true", "on") if any(k == "busy" for k, _ in self.qlist): DEV.busy = self.arg("busy") if self.arg("busy") in ("update", "storage") else "" - return self.reply(302, [("Content-Type", "text/plain"), ("Location", "/"), + return self.reply(302, [("Content-Type", "text/plain"), ("Location", dest), ("Cache-Control", NO_STORE), ("Content-Length", "0")]) + if path in AUDIO_ROUTES: + return self.audio(path) + if path.startswith("/api/") or path == "/update/status": return self.api(path, q, {}) @@ -559,14 +1410,48 @@ def do_GET(self): def do_POST(self): path, q = self.parts() + if path in AUDIO_ROUTES: + return self.audio(path) p = dict(q) p.update(self.body_params()) self.api(path, q, p) def do_DELETE(self): path, q = self.parts() + if path in AUDIO_ROUTES: + return self.audio(path) self.api(path, q, dict(q), delete=True) + def audio(self, path): + """The audio features' routes, answered by the port above. The body is + read here, raw: body_params() drops empty values, and to these + handlers `lut2=` and `meta1=` mean something.""" + n = int(self.headers.get("Content-Length") or 0) + body = self.rfile.read(n).decode("latin-1") if n else "" + ctype = self.headers.get("Content-Type") or "" + args = Args(urllib.parse.urlsplit(self.path).query, body, ctype) + # The log line says what was sent - a save is judged by what it + # carried - with a curve table or a phone frame cut short. And it says + # when a body was sent that the panel would not have read. + self.note = "".join(" " + (v if len(v) <= 60 else "%s...(%d bytes)" % (v[:44], len(v))) + for v in body.split("&") if v) + if body and ctype.startswith("multipart/"): + self.note = " (a multipart body: the panel reads those, this mock does not)" + elif body and not ctype.startswith("application/x-www-form-urlencoded"): + self.note += " (BODY NOT READ: Content-Type %r is not the form one)" % ctype + with AUDIO.lock: + if (AUDIO.noread and self.command == "GET" and path == "/api/audio-in" + and not args.has("levels") and "audio-in" in DEV.caps): + # /mock?audio=noread: no reply at all, which is what a failed + # read of a panel is - it has no error to send for this route. + self.note = " (/mock?audio=noread)" + self.close_connection = True + return self.log_request("no reply") + answer = AUDIO.request(self.command, path, args, DEV.caps) + if answer is None: + return self.not_found() + self.send(answer[1].encode("latin-1"), "application/json", answer[0]) + # ── the fake device ────────────────────────────────────────────────── def api(self, path, q, p, delete=False): d = DEV @@ -835,6 +1720,7 @@ def api(self, path, q, p, delete=False): print(" caps /mock?caps=bare /mock?caps=full") print(" variant /mock?variant=simone-pd /mock?variant=core") print(" status /mock?hotspot=1 /mock?busy=update (hotspot=0, busy= to clear)") + print(" audio /mock?audio=" + "|".join(AUDIO_STATES) + "|noread") print(" editing firmware/patternflow/console/*.html — just save and refresh") print(" shipping python firmware/toolchain/console_pages.py build") if Link.device or Link.rtt or Link.bps or Link.serial: diff --git a/firmware/toolchain/editor_demo.py b/firmware/toolchain/editor_demo.py new file mode 100644 index 00000000..852d57ae --- /dev/null +++ b/firmware/toolchain/editor_demo.py @@ -0,0 +1,334 @@ +"""The audio extension's mapping editor as static files, for the Audio guide. + + python firmware/toolchain/editor_demo.py # write web/public/guide/audio-editor/ + python firmware/toolchain/editor_demo.py --check # is what is there current? + +The guide at /guide/audio shows the extension's real editor - the page the +popup's "Open editor" opens, not a screenshot - in a window on the page, and +what it maps drives the 3D board beside it (web/src/components/guide/ +EditorWindow.tsx is the other end). This writes what that window loads: + + index.html tools/patternflow-audio-extension/editor.html, with one + tag added: bridge.js, between the adapter and the editor + editor.css ) + mapping.js ) the extension's files, byte for byte (line ends + editor-adapter.js ) apart) + editor.js ) + bridge.js BRIDGE_JS below: the guide's end of the editor + +Nothing of the extension is rewritten. A page that is not an extension has no +chrome.storage, so editor-adapter.js runs its own demo source (a synthesized +spectrum; the bar says "demo source"), and bridge.js stands between that +adapter and the editor - its header comment says what it does and why. The +extension's folder is not touched: this only reads it. + +Rerun it after any change to those four files, to editor.html or to BRIDGE_JS; +`--check` exits 1 when the folder is stale. The folder is this script's alone: +it is emptied and rewritten each run. + +bridge.js maps levels to knobs the way the capture does (offscreen.js tick()), +with PFMap's own functions, but the few lines that join them are written +again there. CHAIN below is those lines as offscreen.js has them: if one is +gone the capture's chain has changed, this script stops, and BRIDGE_JS is to +be brought back in step before the list is. +""" + +import argparse +import os +import sys + +HERE = os.path.dirname(os.path.abspath(__file__)) +REPO = os.path.normpath(os.path.join(HERE, "..", "..")) +EXT = os.path.join(REPO, "tools", "patternflow-audio-extension") +OUT = os.path.join(REPO, "web", "public", "guide", "audio-editor") + +# Copied as they are. +VERBATIM = ["editor.css", "mapping.js", "editor-adapter.js", "editor.js"] + +# The one edit to editor.html. +ADAPTER_TAG = '' +BRIDGE_TAG = '' + +# What bridge.js follows, as offscreen.js writes it (see the docstring). +CHAIN = [ + "const TICK_MS = 33;", + "const REPORT_MS = 120;", + "const a = raw > levels[i] ? config.attack : config.smoothing;", + "levels[i] = a * raw + (1 - a) * levels[i];", + "if (auto) PFMap.trackEnvelope(envelopes[i], levels[i]);", + "const pos = PFMap.position(levels[i], bands[i], auto ? envelopes[i] : null);", + "const output = PFMap.output(bands[i], pos);", + "if (!bands[i].muted) lanes[bands[i].knob] = output;", + "if (running) config = PFMap.normalizeConfig(message.config, PFMap.SOURCE);", +] +# And the two things it leans on in the demo adapter. +ADAPTER = [ + "onFrame(fn) { frameFn = fn; demoFrame(); setInterval(demoFrame, 120); }", + "e && e.hi - e.lo > PFMap.ENV_MIN_SPAN + 1e-6 ? e : null);", +] + + +def read(name): + with open(os.path.join(EXT, name), encoding="utf-8", newline="") as f: + return f.read().replace("\r\n", "\n") + + +def render(): + """{file name: bytes} - everything the folder should hold.""" + for name, lines in (("offscreen.js", CHAIN), ("editor-adapter.js", ADAPTER)): + text = read(name) + for line in lines: + if line not in text: + raise SystemExit( + "%s no longer has the line\n %s\nbridge.js (BRIDGE_JS in " + "editor_demo.py) follows it: bring the bridge in step, then " + "this list." % (name, line)) + html = read("editor.html") + if html.count(ADAPTER_TAG) != 1: + raise SystemExit("editor.html: expected one %s" % ADAPTER_TAG) + html = html.replace(ADAPTER_TAG, ADAPTER_TAG + "\n" + BRIDGE_TAG) + head = "\n" + if not html.startswith(head): + raise SystemExit("editor.html: does not start with its doctype") + html = (head + "\n" + + html[len(head):]) + files = {"index.html": html, "bridge.js": BRIDGE_JS} + for name in VERBATIM: + files[name] = read(name) + return {n: t.encode("utf-8") for n, t in files.items()} + + +def main(): + for stream in (sys.stdout, sys.stderr): + try: + stream.reconfigure(encoding="utf-8", errors="replace") + except (AttributeError, ValueError): + pass + ap = argparse.ArgumentParser(description="Write the Audio guide's editor demo.") + ap.add_argument("--check", action="store_true", help="exit 1 if the folder is stale") + args = ap.parse_args() + files = render() + total = sum(len(b) for b in files.values()) + if args.check: + have = set(os.listdir(OUT)) if os.path.isdir(OUT) else set() + # A Windows checkout has the folder's files with CRLF line ends; what + # is written here, and what git stores, has LF. + def on_disk(n): + with open(os.path.join(OUT, n), "rb") as f: + return f.read().replace(b"\r\n", b"\n") + stale = sorted(n for n, b in files.items() if n not in have or on_disk(n) != b) + stale += sorted(have - set(files)) + if stale: + print("editor demo is stale: %s\nrun: python firmware/toolchain/editor_demo.py" + % ", ".join(stale)) + sys.exit(1) + print("editor demo current (%d files, %d bytes)" % (len(files), total)) + return + os.makedirs(OUT, exist_ok=True) + for n in os.listdir(OUT): + p = os.path.join(OUT, n) + if os.path.isfile(p): + os.remove(p) + for n, b in sorted(files.items()): + with open(os.path.join(OUT, n), "wb") as f: + f.write(b) + print("wrote %d files, %d bytes -> %s" % (len(files), total, os.path.relpath(OUT, REPO))) + for n, b in sorted(files.items()): + print(" %-18s %7d" % (n, len(b))) + + +# ── the guide's end of the editor ────────────────────────────────────────── + +BRIDGE_JS = r'''/* Patternflow Audio guide: between the editor and its demo source. + * GENERATED by firmware/toolchain/editor_demo.py -- edit BRIDGE_JS there, rerun. + * + * Loaded after editor-adapter.js and before editor.js. Outside the extension + * the adapter is its demo: a synthesized spectrum, the mapping in memory, and + * each band's output straight off its level. That is a preview of the editor. + * The guide shows the editor driving a board, so five things are added here, + * around window.PFAdapter and without a change to any of the extension's + * files: + * + * the capture's chain The knob values are worked out as the capture works + * them out (offscreen.js tick()): every 33 ms, the + * level glided by Attack and Damping, auto range's + * envelope at its real rate, PFMap.position and + * PFMap.output, and a muted band sends nothing. The + * editor is handed these values too, so its cursor + * and readout are what the board was told. + * to the board {type:'pf-audio', outputs:[v|null x4]} to the page + * that holds this frame, each tick: v is 0..1 as the + * extension's a= message carries it, null is a lane + * left alone (its '-'). + * one mapping per tab The mapping is kept in sessionStorage, so the + * miniature in the card and the window lifted from + * it are one editor: an edit in either is the other's + * at once (the storage event), and survives a reload. + * Esc {type:'pf-audio', action:'escape'} when Esc is + * pressed and the editor's Preview is not what it + * closes. + * its scroll its own A wheel or a finger that runs past the editor's end + * stops there. Without this the page that holds the + * frame scrolls on, and the guide's step changes + * under the window. + * + * The source stays the adapter's: its spectrum, its levels per band, its + * "demo source" chip. Nothing leaves this folder. + */ +(function () { + 'use strict'; + + var source = window.PFAdapter; + if (!source || typeof PFMap === 'undefined') return; + + var KEY = 'pf-audio-editor:mapping'; + var TICK_MS = 33; // offscreen.js TICK_MS + var REPORT_MS = 120; // offscreen.js REPORT_MS + var CAPS = source.caps(); + + var host = window.parent !== window ? window.parent : null; + function tell(message) { + message.type = 'pf-audio'; + if (host) host.postMessage(message, window.location.origin); + } + + // ── the mapping ───────────────────────────────────────────────────────── + + function stored() { + try { + var text = window.sessionStorage.getItem(KEY); + var raw = text ? JSON.parse(text) : null; + return raw && Array.isArray(raw.bands) ? raw : null; + } catch (e) { + return null; + } + } + function keep(config) { + try { window.sessionStorage.setItem(KEY, JSON.stringify(config)); } catch (e) { /* private mode */ } + } + // A copy made whole, as the capture takes one out of a 'config' message. + function whole(raw) { + return PFMap.normalizeConfig(raw ? JSON.parse(JSON.stringify(raw)) : null, CAPS); + } + + var config = whole(stored()); // what is being mapped with + var configFn = null; + + // The mapping changed somewhere else (the other frame of this tab). + function adopt(raw) { + if (!raw) return; + config = whole(raw); + source.saveConfig(whole(raw)); // the demo source listens where the boxes are + if (configFn) configFn(raw); + } + window.addEventListener('storage', function (e) { + if (e.key === KEY && e.storageArea === window.sessionStorage) adopt(stored()); + }); + + // ── the chain (offscreen.js tick()) ───────────────────────────────────── + + var levels = [0, 0, 0, 0]; + var envelopes = [0, 1, 2, 3].map(PFMap.newEnvelope); + var peakLevels = [-1, -1, -1, -1]; + var peakPos = [0, 0, 0, 0]; + var peakOutputs = [0, 0, 0, 0]; + var heard = null; // the demo source's last frame + var frameFn = null; + var lastReport = 0; + + // editor-adapter.js learned(): an envelope at its minimum span is not one. + function learned(e) { + return e.hi - e.lo > PFMap.ENV_MIN_SPAN + 1e-6 ? { lo: e.lo, hi: e.hi } : null; + } + + function tick() { + if (!heard || !heard.levels || heard.levels.length < 4) return; + var bands = config.bands; + var auto = config.autoRange; + var i; + + for (i = 0; i < 4; i++) { + var raw = PFMap.clamp01(heard.levels[i]); + var a = raw > levels[i] ? config.attack : config.smoothing; + levels[i] = a * raw + (1 - a) * levels[i]; + if (auto) PFMap.trackEnvelope(envelopes[i], levels[i]); + } + + var lanes = [null, null, null, null]; + for (i = 0; i < 4; i++) { + var pos = PFMap.position(levels[i], bands[i], auto ? envelopes[i] : null); + var output = PFMap.output(bands[i], pos); + if (!bands[i].muted) lanes[bands[i].knob] = output; + if (levels[i] > peakLevels[i]) { + peakLevels[i] = levels[i]; + peakPos[i] = pos; + peakOutputs[i] = output; + } + } + tell({ outputs: lanes }); + + var now = performance.now(); + if (frameFn && now - lastReport > REPORT_MS) { + lastReport = now; + frameFn({ + running: true, connected: false, demo: true, + levels: peakLevels.slice(), + pos: peakPos.slice(), + outputs: peakOutputs.slice(), + spectrum: heard.spectrum, + env: auto ? envelopes.map(learned) : [], + autoRange: auto + }); + peakLevels = [-1, -1, -1, -1]; + } + } + + // ── the adapter the editor talks to ───────────────────────────────────── + + window.PFAdapter = { + caps: function () { return CAPS; }, + // What this tab has stored, or nothing (a first run), as the extension's + // own adapter answers from chrome.storage. + loadConfig: function () { + var raw = stored(); + if (raw) source.saveConfig(whole(raw)); + return Promise.resolve(raw); + }, + saveConfig: function (next) { + keep(next); + config = whole(next); + return source.saveConfig(next); + }, + resetBand: function (index, current) { + return source.resetBand(index, current).then(function (next) { + keep(next); + config = whole(next); + return next; + }); + }, + onConfig: function (fn) { configFn = fn; }, + onFrame: function (fn) { + frameFn = fn; + source.onFrame(function (state) { heard = state; }); + tick(); + setInterval(tick, TICK_MS); + } + }; + + document.addEventListener('keydown', function (e) { + if (e.key !== 'Escape') return; + var preview = document.getElementById('pv'); + if (preview && !preview.hidden) return; // the editor closes its Preview + tell({ action: 'escape' }); + }); + + // This page scrolls at its root; what is left of a scroll at its end is + // not handed on to the page that holds the frame. + document.documentElement.style.overscrollBehavior = 'contain'; +})(); +''' + + +if __name__ == "__main__": + main() diff --git a/firmware/toolchain/tests/libm_test.cpp b/firmware/toolchain/tests/libm_test.cpp new file mode 100644 index 00000000..c9a97bf9 --- /dev/null +++ b/firmware/toolchain/tests/libm_test.cpp @@ -0,0 +1,500 @@ +// Proves that abi/pf_libm.h returns what libm returns - as bits, not as +// values within a tolerance. +// +// WHY THIS EXISTS. pf_module.h puts the functions in pf_libm.h behind floorf, +// ceilf, truncf, roundf, fminf, fmaxf and fmodf in every .pfm built from now +// on, so that a pattern nobody edits gets inline FPU code where it used to get +// a call into the firmware's libm. The claim that makes that acceptable is +// that each of the seven returns exactly what the library function returns. +// An approximation would be measured in bands (toolchain/check_math.py does +// that for core_math.h); this is not one, so the test is equality of bit +// patterns and any single mismatch fails. +// +// WHAT IT RUNS. The reference is this machine's libm: floorf, fmodf and the +// rest, called as functions - check_libm.py passes -fno-builtin- for each, +// because left alone GCC answers some of them itself on a PC, and its inline +// floor does not quiet a signalling NaN the way glibc's function does. +// pf_libm.h handles inline only inputs whose answer IEEE 754 fixes, and hands +// the rest to libm - so on any machine with a correct libm the two must agree +// everywhere, NaN payloads and signed zeros included, and no NaN is ever +// "treated as equal to any NaN" here: where a NaN comes back it came from the +// same libm call on both sides. +// +// floor, ceil, trunc, round every one of the 2^32 float bit patterns. +// fmin, fmax every pair of a set of edge values, random bit +// patterns, and for a stride through all floats +// x against itself, its neighbour and its +// negation - the ties are where the care is. +// fmod the path a variable divisor takes (a division) +// AND the path a constant divisor takes (the +// reciprocal, instantiated explicitly: the test +// passes 1.0f / |y|, which is what the compiler +// folds), on the same pairs: edge values, +// random bits, quotients spread from under 1 to +// past the fast path's 2^22 limit, x = k*y +- 0..4 +// ulp for k up to 2^24, the divisors patterns +// actually write (1, 2*pi, 360, 0.1 ...), divisors +// so large their reciprocal is subnormal and so +// small it overflows. fmodOne - the y = +-1 path - +// over every float in [1, 2^23), both signs. +// +// The run also counts which way each fmod went (returned x, no correction, +// stepped down, stepped up, library), because a correction branch that no +// input reaches is a branch this file has not tested. +// +// WHAT IT CANNOT RUN. The module-only half of the header - the bodies behind +// the C names and the choice between fmod's paths exist only under the Xtensa +// compiler; check_module_libm.py builds a module and reads them out of it. And +// the panel's arithmetic: that the S3's MSUB.S is fused and its conversions +// round as IEEE says. tests/modules/_math_probe checks those on a board, +// against the firmware's own libm. +// +// License: MIT +#include "pf_libm.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace { + +uint32_t bitsOf(float f) { + uint32_t u; + std::memcpy(&u, &f, sizeof u); + return u; +} + +float fromBits(uint32_t u) { + float f; + std::memcpy(&f, &u, sizeof f); + return f; +} + +// SplitMix64: every block of work seeds its own, from its own index, so the +// inputs are the same whatever number of threads happens to run them. +struct Random { + uint64_t state; + explicit Random(uint64_t seed) : state(seed * 0x9E3779B97F4A7C15ull + 0x1234567ull) {} + uint64_t next() { + uint64_t z = (state += 0x9E3779B97F4A7C15ull); + z = (z ^ (z >> 30)) * 0xBF58476D1CE4E5B9ull; + z = (z ^ (z >> 27)) * 0x94D049BB133111EBull; + return z ^ (z >> 31); + } + uint32_t bits() { return (uint32_t)(next() >> 32); } + uint32_t below(uint32_t n) { return (uint32_t)((next() >> 32) * (uint64_t)n >> 32); } +}; + +enum Route { EARLY, PLAIN, DOWN, UP, LIBRARY, ROUTES }; + +struct Miss { + const char* what; + uint32_t x, y, got, want; +}; + +struct Tally { + uint64_t checked = 0; + uint64_t wrong = 0; + uint64_t inlined = 0; // unary / min-max inputs the library never saw + uint64_t divided[ROUTES] = {}; // fmod with a division + uint64_t multiplied[ROUTES] = {}; // fmod with a reciprocal + Miss first[6] = {}; + int misses = 0; + + void same(const char* what, float x, float y, float got, float want) { + ++checked; + if (bitsOf(got) == bitsOf(want)) return; + ++wrong; + if (misses < (int)(sizeof first / sizeof first[0])) + first[misses++] = Miss{what, bitsOf(x), bitsOf(y), bitsOf(got), bitsOf(want)}; + } + + void merge(const Tally& other) { + checked += other.checked; + wrong += other.wrong; + inlined += other.inlined; + for (int r = 0; r < ROUTES; ++r) { + divided[r] += other.divided[r]; + multiplied[r] += other.multiplied[r]; + } + for (int i = 0; i < other.misses && misses < (int)(sizeof first / sizeof first[0]); ++i) + first[misses++] = other.first[i]; + } +}; + +template +Tally inParallel(uint64_t blocks, Work work) { + unsigned count = std::thread::hardware_concurrency(); + if (count == 0) count = 1; + if (count > 32) count = 32; + std::atomic next{0}; + std::vector tallies(count); + std::vector threads; + for (unsigned t = 0; t < count; ++t) { + threads.emplace_back([&next, &tallies, &work, blocks, t] { + Tally mine; // on this thread's stack: no cache line shared with a neighbour + for (;;) { + const uint64_t block = next.fetch_add(1); + if (block >= blocks) break; + work(block, mine); + } + tallies[t] = mine; + }); + } + for (auto& thread : threads) thread.join(); + Tally total; + for (const Tally& tally : tallies) total.merge(tally); + return total; +} + +int failures = 0; + +void report(const char* title, const Tally& tally) { + std::printf("%-28s %14llu checks, %llu mismatches\n", title, + (unsigned long long)tally.checked, (unsigned long long)tally.wrong); + for (int i = 0; i < tally.misses; ++i) { + const Miss& m = tally.first[i]; + std::printf(" %s(%08x, %08x) = %08x, libm says %08x\n", m.what, m.x, m.y, m.got, m.want); + } + if (tally.wrong) ++failures; +} + +void require(bool ok, const char* what) { + if (ok) return; + std::printf(" NOT COVERED: %s\n", what); + ++failures; +} + +// ── Values every list of floats should contain ────────────────────── +const uint32_t EDGES[] = { + 0x00000000u, 0x80000000u, // +-0 + 0x00000001u, 0x80000001u, // smallest subnormal + 0x00000002u, 0x00000003u, 0x00400000u, 0x80400000u, + 0x007fffffu, 0x807fffffu, // largest subnormal + 0x00800000u, 0x80800000u, // smallest normal + 0x00800001u, 0x00ffffffu, 0x01000000u, + 0x3e800000u, 0x3effffffu, 0x3f000000u, 0xbf000000u, // 0.25, just under 0.5, +-0.5 + 0x3f000001u, 0x3f7fffffu, 0xbf7fffffu, // just over 0.5, just under 1 + 0x3f800000u, 0xbf800000u, 0x3f800001u, 0xbf800001u, // +-1 and a bit + 0x3fc00000u, 0xbfc00000u, 0x40000000u, 0xc0000000u, // +-1.5, +-2 + 0x40200000u, 0xc0200000u, 0x40490fdbu, 0x40c90fdbu, // +-2.5, pi, 2*pi + 0x43b40000u, 0x3dcccccdu, 0x3a83126fu, // 360, 0.1, 0.001 + 0x4a7ffffcu, 0x4a7ffffeu, 0x4a800000u, 0xca800000u, // around 2^22 + 0x4a800002u, 0x4afffffeu, 0x4affffffu, 0xcaffffffu, // ... and under 2^23 + 0x4b000000u, 0xcb000000u, 0x4b000001u, 0xcb000001u, // 2^23 and a bit + 0x4b7fffffu, 0x4b800000u, 0xcb800000u, 0x4b800001u, // around 2^24 + 0x4f000000u, 0xcf000000u, 0x4effffffu, 0x4f800000u, // 2^31, just under, 2^32 + 0x5f000000u, 0x7e800000u, 0x7effffffu, 0x7f000000u, // 2^63, 2^126, 2^127 + 0x7f7fffffu, 0xff7fffffu, // +-FLT_MAX + 0x7f800000u, 0xff800000u, // +-inf + 0x7fc00000u, 0xffc00000u, // quiet NaN, both signs + 0x7f800001u, 0xff800001u, // signalling NaN + 0x7fa00000u, 0x7fffffffu, 0xffffffffu, 0x7fc12345u, // payloads +}; +const int EDGE_COUNT = (int)(sizeof EDGES / sizeof EDGES[0]); + +// A float with every exponent equally likely - random bits already are, but +// this one can be steered. +float anyExponent(Random& random, int exponentField) { + if (exponentField < 0) exponentField = 0; + if (exponentField > 254) exponentField = 254; + return fromBits((random.bits() & 0x807fffffu) | ((uint32_t)exponentField << 23)); +} + +int exponentFieldOf(float f) { + return (int)((bitsOf(f) >> 23) & 0xffu); +} + +// d units in the last place away, by magnitude; never past zero or FLT_MAX. +float stepped(float x, int d) { + const uint32_t u = bitsOf(x); + int64_t magnitude = (int64_t)(u & 0x7fffffffu) + d; + if (magnitude < 0) magnitude = 0; + if (magnitude > 0x7f7fffff) magnitude = 0x7f7fffff; + return fromBits((u & 0x80000000u) | (uint32_t)magnitude); +} + +// ── floor, ceil, trunc, round: every float ────────────────────────── +const uint64_t UNARY_BLOCKS = 4096; // of 2^20 bit patterns each + +void unaryBlock(uint64_t block, Tally& tally) { + const uint32_t base = (uint32_t)(block << 20); + for (uint32_t i = 0; i < (1u << 20); ++i) { + const float x = fromBits(base + i); + tally.same("floor", x, 0.0f, PFLibm::floor(x), ::floorf(x)); + tally.same("ceil", x, 0.0f, PFLibm::ceil(x), ::ceilf(x)); + tally.same("trunc", x, 0.0f, PFLibm::trunc(x), ::truncf(x)); + tally.same("round", x, 0.0f, PFLibm::round(x), ::roundf(x)); + if (std::fabs(x) < PFLibm::WHOLE_FROM) ++tally.inlined; + } +} + +// ── fmin, fmax ────────────────────────────────────────────────────── +void minMax(float x, float y, Tally& tally) { + tally.same("fmin", x, y, PFLibm::fmin(x, y), ::fminf(x, y)); + tally.same("fmax", x, y, PFLibm::fmax(x, y), ::fmaxf(x, y)); + // What the header's last test decides: equal values with the same sign bit. + if (x < y || y < x || (x == y && std::signbit(x) == std::signbit(y))) ++tally.inlined; +} + +void minMaxEdges(uint64_t, Tally& tally) { + for (int i = 0; i < EDGE_COUNT; ++i) + for (int j = 0; j < EDGE_COUNT; ++j) minMax(fromBits(EDGES[i]), fromBits(EDGES[j]), tally); +} + +const uint64_t MINMAX_RANDOM_BLOCKS = 64; // of 2^20 pairs +void minMaxRandomBlock(uint64_t block, Tally& tally) { + Random random(0x100000 + block); + for (uint32_t i = 0; i < (1u << 20); ++i) minMax(fromBits(random.bits()), fromBits(random.bits()), tally); +} + +const uint64_t MINMAX_NEAR_BLOCKS = 256; // each a 2^24 slice of the float line, every 61st +void minMaxNearBlock(uint64_t block, Tally& tally) { + const uint32_t base = (uint32_t)(block << 24); + for (uint32_t i = (uint32_t)(block % 61); i < (1u << 24); i += 61) { + const float x = fromBits(base + i); + minMax(x, x, tally); + minMax(x, fromBits(base + i + 1), tally); + minMax(fromBits(base + i + 1), x, tally); + minMax(x, -x, tally); + minMax(-x, x, tally); + } +} + +// ── fmod ──────────────────────────────────────────────────────────── +// Which branch fmodFrom() takes for this estimate of the quotient. It repeats +// the header's conditions on purpose: this is bookkeeping about coverage, and +// the answers themselves are compared separately, against libm. +Route routeOf(float ax, float ay, float p) { + if (ax < ay) return EARLY; + if (!(p < PFLibm::FMOD_QUOTIENT_BELOW)) return LIBRARY; + const float q = (float)(int)p; + const float r = PFLibm::fnma(q, ay, ax); + if (r < 0.0f) return DOWN; + if (r >= ay) return UP; + return PLAIN; +} + +void mod(float x, float y, Tally& tally) { + const float want = ::fmodf(x, y); + const float ax = std::fabs(x); + const float ay = std::fabs(y); + const float reciprocal = 1.0f / ay; // rounded to nearest: what a compiler folds + tally.same("fmod", x, y, PFLibm::fmod(x, y), want); + tally.same("fmodRecip", x, y, PFLibm::fmodRecip(x, y, reciprocal), want); + ++tally.divided[routeOf(ax, ay, ax / ay)]; + ++tally.multiplied[routeOf(ax, ay, ax * reciprocal)]; + if (ay == 1.0f) tally.same("fmodOne", x, y, PFLibm::fmodOne(x, y), want); +} + +void modAllSigns(float x, float y, Tally& tally) { + mod(x, y, tally); + mod(-x, y, tally); + mod(x, -y, tally); + mod(-x, -y, tally); +} + +void modEdges(uint64_t, Tally& tally) { + for (int i = 0; i < EDGE_COUNT; ++i) + for (int j = 0; j < EDGE_COUNT; ++j) mod(fromBits(EDGES[i]), fromBits(EDGES[j]), tally); +} + +// Random bit patterns: mostly |x| < |y| or a quotient far past the limit, so +// mostly a test that those are routed to "return x" and to the library. +const uint64_t MOD_RANDOM_BLOCKS = 16; // of 2^20 pairs +void modRandomBlock(uint64_t block, Tally& tally) { + Random random(0x200000 + block); + for (uint32_t i = 0; i < (1u << 20); ++i) mod(fromBits(random.bits()), fromBits(random.bits()), tally); +} + +// Any divisor at all - subnormal to FLT_MAX - and a dividend whose exponent is +// -1 to +25 above it: quotients from under one, through the whole fast path, +// to eight times its limit. +const uint64_t MOD_SPREAD_BLOCKS = 192; // of 2^20 pairs +void modSpreadBlock(uint64_t block, Tally& tally) { + Random random(0x300000 + block); + for (uint32_t i = 0; i < (1u << 20); ++i) { + const float y = anyExponent(random, (int)random.below(255)); + const float x = anyExponent(random, exponentFieldOf(y) - 1 + (int)random.below(27)); + mod(x, y, tally); + } +} + +// The divisors patterns write. +const float DIVISORS[] = { + 1.0f, 2.0f, 3.0f, 0.5f, 0.25f, 1.5f, 0.1f, + 0.01f, 0.001f, 1e-6f, 3.14159265f, 6.2831853f, 1.57079633f, 2.71828183f, + 360.0f, 180.0f, 255.0f, 256.0f, 100.0f, 1000.0f, 10.0f, + 6.0f, 60.0f, 64.0f, 128.0f, 0.33333334f, 1e6f, 7.0f, +}; +const int DIVISOR_COUNT = (int)(sizeof DIVISORS / sizeof DIVISORS[0]); + +// x = k*y, and the four floats either side of it. k is drawn three ways: +// anywhere up to 2^24, small, and on the boundaries the proof turns on. +uint32_t multiple(Random& random) { + static const uint32_t BOUNDARY[] = { + 1u, 2u, 3u, 4u, 5u, 7u, 255u, 256u, 65535u, 65536u, + (1u << 22) - 3, (1u << 22) - 2, (1u << 22) - 1, (1u << 22), (1u << 22) + 1, (1u << 22) + 2, + (1u << 23) - 2, (1u << 23) - 1, (1u << 23), (1u << 23) + 1, (1u << 24) - 1, (1u << 24), + }; + switch (random.below(4)) { + case 0: return 1u + random.below(1u << 24); + case 1: return 1u + random.below(1u << 22); // all of it inside the fast path + case 2: return 1u + random.below(1u << (1 + random.below(22))); + default: return BOUNDARY[random.below((uint32_t)(sizeof BOUNDARY / sizeof BOUNDARY[0]))]; + } +} + +void nearMultiples(Random& random, float y, uint32_t count, Tally& tally) { + for (uint32_t i = 0; i < count; ++i) { + const double product = (double)multiple(random) * (double)y; + if (!(product < 3.0e38)) continue; + const float x = (float)product; + for (int d = -4; d <= 4; ++d) { + const float beside = stepped(x, d); + if (random.below(8) == 0) modAllSigns(beside, y, tally); + else mod(beside, y, tally); + } + } +} + +const uint64_t MOD_CONSTANT_BLOCKS = (uint64_t)DIVISOR_COUNT * 4; +void modConstantBlock(uint64_t block, Tally& tally) { + Random random(0x400000 + block); + nearMultiples(random, DIVISORS[block % DIVISOR_COUNT], 60000, tally); +} + +const uint64_t MOD_MULTIPLE_BLOCKS = 96; +void modMultipleBlock(uint64_t block, Tally& tally) { + Random random(0x500000 + block); + for (int i = 0; i < 6000; ++i) { + // Exponents -40..+40: k*y stays finite and x keeps all its bits. + const float y = anyExponent(random, 87 + (int)random.below(81)); + nearMultiples(random, y, 10, tally); + } +} + +// Every 67th float from y/4 up to y * 2^24, for each written divisor. +const uint64_t MOD_SWEEP_BLOCKS = (uint64_t)DIVISOR_COUNT; +void modSweepBlock(uint64_t block, Tally& tally) { + const float y = DIVISORS[block]; + const uint32_t from = bitsOf(y * 0.25f); + const uint32_t to = bitsOf(y * 16777216.0f); + for (uint32_t u = from + (uint32_t)(block % 67); u < to; u += 67) { + const float x = fromBits(u); + if ((u & 0x100u) != 0) mod(-x, y, tally); + else mod(x, y, tally); + } +} + +// Divisors at the two ends of the range. From 2^126 up, 1/y is subnormal and +// its error is absolute, not relative; below 2^-128 it overflows to infinity. +const uint64_t MOD_EXTREME_BLOCKS = 16; // of 2^20 pairs +void modExtremeBlock(uint64_t block, Tally& tally) { + Random random(0x600000 + block); + for (uint32_t i = 0; i < (1u << 20); ++i) { + const bool huge = (i & 1u) != 0; + const float y = anyExponent(random, huge ? 251 + (int)random.below(4) : (int)random.below(4)); + const float x = anyExponent(random, exponentFieldOf(y) + (int)random.below(huge ? 4 : 26)); + mod(x, y, tally); + } +} + +// fmod(x, +-1): every float in [1, 2^23), both signs and both divisors, which +// is all of what fmodOne computes itself; under 1 it returns x, past 2^23 it +// asks the library, and a stride through each is enough to see that it does. +const uint64_t MOD_ONE_BLOCKS = 23 * 8 + 128 + 16; +void modOneBlock(uint64_t block, Tally& tally) { + uint32_t from, to, step; + if (block < 23 * 8) { // [1, 2^23): 23 binades, each in eight slices + from = 0x3f800000u + (uint32_t)block * (1u << 20); + to = from + (1u << 20); + step = 1; + } else if (block < 23 * 8 + 128) { // [0, 1), every 16th + from = (uint32_t)(block - 23 * 8) * (0x3f800000u / 128); + to = from + 0x3f800000u / 128; + step = 16; + } else { // 2^23 and up, infinities and NaN included, every 4099th + from = 0x4b000000u + (uint32_t)(block - 23 * 8 - 128) * (0x35000000u / 16); + to = from + 0x35000000u / 16; + step = 4099; + } + for (uint32_t u = from; u < to; u += step) { + const float x = fromBits(u); + for (int sign = 0; sign < 4; ++sign) { + const float sx = (sign & 1) ? -x : x; + const float sy = (sign & 2) ? -1.0f : 1.0f; + tally.same("fmodOne", sx, sy, PFLibm::fmodOne(sx, sy), ::fmodf(sx, sy)); + } + // The general paths on the same input now and then: a divisor of one is + // also just a divisor. + if ((u & 0x3fu) == 0) mod(x, 1.0f, tally); + } +} + +void routes(const char* title, const uint64_t* count) { + std::printf(" %-22s returned x %llu, exact at once %llu, stepped down %llu, " + "stepped up %llu, library %llu\n", + title, (unsigned long long)count[EARLY], (unsigned long long)count[PLAIN], + (unsigned long long)count[DOWN], (unsigned long long)count[UP], + (unsigned long long)count[LIBRARY]); +} + +} // namespace + +int main() { + // Unary: all of them. + const Tally unary = inParallel(UNARY_BLOCKS, unaryBlock); + report("floor ceil trunc round", unary); + std::printf(" every float bit pattern through each; %llu of 4294967296 inputs inline, " + "the rest (2^23 and up, inf, NaN) the library's\n", + (unsigned long long)unary.inlined); + require(unary.checked == 4ull * 4294967296ull, "not every float was run"); + require(unary.inlined == 2ull * 150ull * 8388608ull, "the inline range is not |x| < 2^23"); + + Tally minmax = inParallel(1, minMaxEdges); + minmax.merge(inParallel(MINMAX_RANDOM_BLOCKS, minMaxRandomBlock)); + minmax.merge(inParallel(MINMAX_NEAR_BLOCKS, minMaxNearBlock)); + report("fmin fmax", minmax); + std::printf(" %llu pairs; %llu settled inline, %llu (a NaN, or +0 against -0) by the library\n", + (unsigned long long)(minmax.checked / 2), (unsigned long long)minmax.inlined, + (unsigned long long)(minmax.checked / 2 - minmax.inlined)); + require(minmax.checked / 2 > minmax.inlined, "no pair reached the library"); + + Tally fmod = inParallel(1, modEdges); + fmod.merge(inParallel(MOD_RANDOM_BLOCKS, modRandomBlock)); + fmod.merge(inParallel(MOD_SPREAD_BLOCKS, modSpreadBlock)); + fmod.merge(inParallel(MOD_CONSTANT_BLOCKS, modConstantBlock)); + fmod.merge(inParallel(MOD_MULTIPLE_BLOCKS, modMultipleBlock)); + fmod.merge(inParallel(MOD_SWEEP_BLOCKS, modSweepBlock)); + fmod.merge(inParallel(MOD_EXTREME_BLOCKS, modExtremeBlock)); + report("fmod (both paths)", fmod); + uint64_t pairs = 0; + for (int r = 0; r < ROUTES; ++r) pairs += fmod.divided[r]; + std::printf(" %llu pairs, each through the division path and the reciprocal path\n", + (unsigned long long)pairs); + routes("division:", fmod.divided); + routes("reciprocal:", fmod.multiplied); + for (int r = 0; r < ROUTES; ++r) { + // A correctly rounded quotient is never under the true one by a whole + // step, so the division path has no reason to step up; the branch is + // there for a divider that rounds less well than this machine's. + if (r != UP) require(fmod.divided[r] > 1000, "a branch of the division path saw under 1000 pairs"); + require(fmod.multiplied[r] > 1000, "a branch of the reciprocal path saw under 1000 pairs"); + } + + const Tally one = inParallel(MOD_ONE_BLOCKS, modOneBlock); + report("fmod by +-1", one); + + if (failures) { + std::printf("pf_libm.h does NOT match libm\n"); + return 1; + } + std::printf("pf_libm.h exact: %llu comparisons, bit for bit\n", + (unsigned long long)(unary.checked + minmax.checked + fmod.checked + one.checked)); + return 0; +} diff --git a/firmware/toolchain/tests/math_test.cpp b/firmware/toolchain/tests/math_test.cpp index ff22419f..8a438eef 100644 --- a/firmware/toolchain/tests/math_test.cpp +++ b/firmware/toolchain/tests/math_test.cpp @@ -2,7 +2,7 @@ // core_color.h, core_noise.h — against double-precision references, and pins // every number it measures. // -// WHY THIS EXISTS. abi/pf_module.h:241-243 includes these three files into every +// WHY THIS EXISTS. abi/pf_module.h:260-262 includes these three files into every // .pfm, so they are not internal helpers: they are the published pattern SDK. Up // to this file, nothing in the repository ran a single number through them. Their // accuracy was asserted only in doc-comments, and two of those comments turned out diff --git a/firmware/toolchain/tests/module_elf_test.cpp b/firmware/toolchain/tests/module_elf_test.cpp index fbc00f5a..836758fe 100644 --- a/firmware/toolchain/tests/module_elf_test.cpp +++ b/firmware/toolchain/tests/module_elf_test.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include using namespace PFModuleLoader; @@ -80,31 +81,72 @@ static T read(const std::vector& image, size_t offset) { return value; } -// The largest executable-section total a module shipped from this repository may -// have. It is NOT a guarantee that a board will admit it: the device compares -// codeBytes against budget() = serviceFree() - PF_MODULE_INTERNAL_RESERVE, and -// serviceFree() is ambient - an HTTP response in flight, an rtpMIDI session and a -// DHCP renew all move it. This is a floor of observations, and its job is to stop -// a module being added here that the tightest edition cannot load. +// What a module shipped from this repository may cost in code, as the loader +// prices it (the sum of its executable sections). Two numbers, because since +// 3.10.5 there are two places code can go. // -// Set 2026-09-10 from docs/investigations/2026-09-firmware-runtime.md: +// SHIPPED_CODE_CEILING - every module, no exceptions. Code is placed in PSRAM +// (PF_MODULE_CODE_PSRAM_FIRST, src/core_module_memory.h) and charged nothing +// against internal RAM, and the loader keeps megabytes of PSRAM free before a +// load, so whether a module comes on no longer depends on its size. What still +// does is what it costs to run: code in PSRAM is fetched through the 16 KB +// instruction cache (CONFIG_ESP32S3_INSTRUCTION_CACHE_SIZE 0x4000). Measured +// 2026-10-01, A-B-B-A on one boot (core_module_memory.h has the table): nine +// catalogue modules ran within -0.1..+0.8 % of their internal-RAM frame time; +// 10, 23 and 32 KB with every byte executed every frame cost 2.6..5.3 %. The +// ceiling is the cache: a module under it can sit in the cache whole, one over +// it refetches part of itself from PSRAM every frame. Going over is allowed +// and is a decision, not a formality - measure that module's frame time with +// its code placed both ways and say in the commit what it cost. +// +// INTERNAL_PLACEMENT_LINE - where the ceiling used to be, and why it is still +// written down. Code goes to internal RAM wherever PSRAM is not used for it: +// on every firmware before 3.10.5, and on a unit that has demoted itself after +// a failed read-back (codeDemoted). There it is admitted against +// budget() = serviceFree() - PF_MODULE_INTERNAL_RESERVE, and serviceFree() is +// ambient - an HTTP response in flight, an rtpMIDI session and a DHCP renew +// all move it. Set 2026-09-10 from docs/investigations/2026-09-firmware-runtime.md: // the module's internal share with none resident measures 7,816 B (section 4.4) // boot-to-boot ambient variance is about 700 B (section 4.1) -// so the worst reasonable budget is about 7,116 B -// the largest module currently shipped, breakout_arcade.pfm, is 5,364 B -// 6,144 sits 780 B above what ships and 972 B below the worst budget. Section 4.2 -// is what the absence of this number cost: a firmware that could not load its own -// pack, 24,576 + 5,364 = 29,940 against ~28,320 free, found by hardware bisect. +// so the worst reasonable budget is about 7,116 B - on the Audio edition, the +// tightest (41,036 B internal free after smoke, against Default's 73,172 B) +// and 6,144 sits 972 B below that. Section 4.2 is what the absence of this +// number once cost: a firmware that could not load its own pack, 24,576 + +// 5,364 = 29,940 against ~28,320 free, found by hardware bisect. +// +// A module over the line loads wherever code runs from PSRAM. Where it does +// not, it is refused once it is past the budget: about 7.8 KB on an Audio +// build before 3.10.5 (7.1 on a bad boot), some 48 KB on the default one. +// Nothing builds a module smaller for that firmware - it is told to update - +// so this is a real cost, and it is taken by name: a module over the line has +// to be listed in OVER_INTERNAL_LINE to pass, and listing one is the decision +// to give up loading it there, said in the commit. // -// Raising it is allowed and is a decision, not a formality: re-measure -// moduleMemory.budget from /api/status on the Audio edition (the tightest - 41,036 B -// internal free after smoke, against Default's 73,172 B), with the console open and -// a module resident, sample repeatedly against the ambient variance, and say in the -// commit which observation the new number came from. -static constexpr size_t SHIPPED_CODE_CEILING = 6144; +// breakout_arcade was listed 2026-10-04, when modules began to be built at -O2 +// with libm inline (build_module.py DEFAULT_OPT, abi/pf_libm.h): 5,564 B the +// old way, 6,568 B at -Os with libm inline, 14,204 B at -O2. The other 32 +// modules in the pack are under 4.5 KB. +static constexpr size_t SHIPPED_CODE_CEILING = 16384; +static constexpr size_t INTERNAL_PLACEMENT_LINE = 6144; +static const char* const OVER_INTERNAL_LINE[] = {"breakout_arcade"}; static size_t largestShippedCode = 0; static const char* largestShippedName = ""; +static std::string overInternalLine; + +// "dir/26-breakout_arcade.pfm" -> "breakout_arcade": check_module_elf.py names +// each fixture -. +static std::string slugOf(const char* path) { + std::string name = path; + const size_t slash = name.find_last_of("/\\"); + if (slash != std::string::npos) name.erase(0, slash + 1); + const size_t dash = name.find('-'); + if (dash != std::string::npos && name.find_first_not_of("0123456789") == dash) + name.erase(0, dash + 1); + const size_t dot = name.rfind(".pfm"); + if (dot != std::string::npos) name.erase(dot); + return name; +} // Exercise the production bounds functions on the modules actually shipped // in Basics. This checks compatibility of the file checks, not Xtensa execution. @@ -162,12 +204,27 @@ static void shippedModule(const char* path) { if (codeBytes > SHIPPED_CODE_CEILING) { std::cerr << path << ": " << codeBytes << " B of executable sections, over the " << SHIPPED_CODE_CEILING << " B ceiling for a module shipped from this\n" - << "repository. A board admits code only against " - << "serviceFree() - PF_MODULE_INTERNAL_RESERVE, so this one may simply\n" - << "not come on. Read the ceiling's provenance in this file before " - << "raising it.\n"; + << "repository - more than the instruction cache holds, so part of it " + << "is fetched from PSRAM again every frame.\n" + << "Read the ceiling's provenance in this file before raising it.\n"; throw std::runtime_error("shipped module over the code ceiling"); } + if (codeBytes > INTERNAL_PLACEMENT_LINE) { + const std::string slug = slugOf(path); + bool listed = false; + for (const char* name : OVER_INTERNAL_LINE) listed = listed || slug == name; + if (!listed) { + std::cerr << path << ": " << codeBytes << " B of executable sections, over the " + << INTERNAL_PLACEMENT_LINE << " B internal RAM can be counted on to take.\n" + << "It loads where code runs from PSRAM (3.10.5 and later) and may be " + << "refused where it does not.\n" + << "If that is meant, list it in OVER_INTERNAL_LINE and say so in the " + << "commit; the reasoning is in this file.\n"; + throw std::runtime_error("shipped module over the internal placement line, unlisted"); + } + if (!overInternalLine.empty()) overInternalLine += ", "; + overInternalLine += slug + " " + std::to_string(codeBytes) + " B"; + } } int main(int argc, char** argv) { @@ -184,6 +241,9 @@ int main(int argc, char** argv) { << "), ceiling " << SHIPPED_CODE_CEILING << " B, headroom " << (SHIPPED_CODE_CEILING - largestShippedCode) << " B\n"; + std::cout << "over the " << INTERNAL_PLACEMENT_LINE + << " B internal placement line, so PSRAM only: " + << (overInternalLine.empty() ? "none" : overInternalLine) << '\n'; } return 0; } catch (const std::exception& error) { diff --git a/firmware/toolchain/tests/modules/_libm_shapes/pattern.cpp b/firmware/toolchain/tests/modules/_libm_shapes/pattern.cpp new file mode 100644 index 00000000..8cbed6a7 --- /dev/null +++ b/firmware/toolchain/tests/modules/_libm_shapes/pattern.cpp @@ -0,0 +1,106 @@ +// Build-time fixture for firmware/toolchain/check_module_libm.py, not a +// showable pattern. check_libm.py proves abi/pf_libm.h's algorithms on a PC, +// but the half of that header a module actually uses - the bodies behind the C +// names, the redirection of libstdc++'s float overloads, the choice between +// the three fmod paths - exists only under PF_MODULE_BUILD on the Xtensa +// compiler, and no PC test compiles it. This file is that half, one spelling a +// function, so the check can read each one out of the built .pfm: +// +// pf_shape_ what a pattern writes; must be inline code with the +// library call left only as the fallback +// pf_control_ what must STILL be a plain call - there so the check +// is known to tell the two apart +// +// The static_asserts are evaluated by the compiler THROUGH the module-only +// wiring (std::floor on a float is PFLibm::floor once pf_module.h has been +// read, std::fmod is fmodAny and whichever path it picks), so a wrapper bound +// to the wrong function, or a wrong test in the fmod dispatch, does not build. +#include "pf_module.h" + +#define SHAPE extern "C" __attribute__((noinline)) + +// ── values, through the wiring ────────────────────────────────────── +static_assert(std::floor(2.5f) == 2.0f && std::floor(-2.5f) == -3.0f, "floor"); +static_assert(std::ceil(2.5f) == 3.0f && std::ceil(-2.5f) == -2.0f, "ceil"); +static_assert(std::trunc(2.7f) == 2.0f && std::trunc(-2.7f) == -2.0f, "trunc"); +static_assert(std::round(2.5f) == 3.0f && std::round(-2.5f) == -3.0f, "round"); +static_assert(std::round(0.49999997f) == 0.0f && std::round(2.4999998f) == 2.0f, "round below a half"); +static_assert(std::fmin(1.0f, 2.0f) == 1.0f && std::fmin(2.0f, 1.0f) == 1.0f, "fmin"); +static_assert(std::fmax(1.0f, 2.0f) == 2.0f && std::fmax(2.0f, 1.0f) == 2.0f, "fmax"); +static_assert(std::fmod(7.5f, 2.0f) == 1.5f && std::fmod(-7.5f, 2.0f) == -1.5f, "fmod, reciprocal path"); +static_assert(std::fmod(7.5f, -2.0f) == 1.5f && std::fmod(10.0f, 3.0f) == 1.0f, "fmod, reciprocal path"); +static_assert(std::fmod(7.25f, 1.0f) == 0.25f && std::fmod(-7.25f, -1.0f) == -0.25f, "fmod by one"); +static_assert(std::fmod(0.25f, 2.0f) == 0.25f && std::fmod(-0.25f, 2.0f) == -0.25f, "fmod under the divisor"); +// (Nothing here goes past the inline range: there the answer is the builtin's, +// and GCC 8.4 does not fold a constant fmodf at all.) +// signs of zero: the cases the inline code has to get right by hand +static_assert(__builtin_signbit(std::floor(-0.0f)) && !__builtin_signbit(std::floor(0.3f)), "floor zero"); +static_assert(__builtin_signbit(std::ceil(-0.3f)) && !__builtin_signbit(std::ceil(0.0f)), "ceil zero"); +static_assert(__builtin_signbit(std::trunc(-0.3f)) && !__builtin_signbit(std::trunc(0.3f)), "trunc zero"); +static_assert(__builtin_signbit(std::round(-0.3f)) && !__builtin_signbit(std::round(0.3f)), "round zero"); +static_assert(__builtin_signbit(std::fmod(-6.0f, 3.0f)) && !__builtin_signbit(std::fmod(6.0f, 3.0f)), "fmod zero"); +static_assert(__builtin_signbit(std::fmod(-2.0f, 1.0f)) && __builtin_signbit(std::fmod(-0.0f, 1.0f)), "fmod-by-one zero"); + +// ── the C names ───────────────────────────────────────────────────── +SHAPE float pf_shape_floorf(float x) { return floorf(x); } +SHAPE float pf_shape_ceilf(float x) { return ceilf(x); } +SHAPE float pf_shape_truncf(float x) { return truncf(x); } +SHAPE float pf_shape_roundf(float x) { return roundf(x); } +SHAPE float pf_shape_fminf(float x, float y) { return fminf(x, y); } +SHAPE float pf_shape_fmaxf(float x, float y) { return fmaxf(x, y); } +SHAPE float pf_shape_fmodf(float x, float y) { return fmodf(x, y); } +SHAPE float pf_shape_fmodf_one(float x) { return fmodf(x, 1.0f); } +SHAPE float pf_shape_fmodf_const(float x) { return fmodf(x, 6.2831853f); } +SHAPE float pf_shape_fmodf_negconst(float x) { return fmodf(x, -360.0f); } + +// ── the spellings that go through libstdc++'s float overloads ──────── +SHAPE float pf_shape_floor(float x) { return floor(x); } +SHAPE float pf_shape_std_floor(float x) { return std::floor(x); } +SHAPE float pf_shape_ceil(float x) { return ceil(x); } +SHAPE float pf_shape_trunc(float x) { return trunc(x); } +SHAPE float pf_shape_round(float x) { return round(x); } +SHAPE float pf_shape_fmin(float x, float y) { return fmin(x, y); } +SHAPE float pf_shape_fmax(float x, float y) { return fmax(x, y); } +SHAPE float pf_shape_fmod(float x, float y) { return fmod(x, y); } +SHAPE float pf_shape_std_fmod(float x, float y) { return std::fmod(x, y); } +SHAPE float pf_shape_std_fmod_const(float x) { return std::fmod(x, 6.2831853f); } + +// ── what stays a call ─────────────────────────────────────────────── +SHAPE float pf_control_builtin(float x) { return __builtin_floorf(x); } +SHAPE float pf_control_pointer(float x) { + float (*volatile through)(float) = floorf; // not a call to floorf: the firmware's symbol + return through(x); +} +SHAPE long pf_control_lroundf(float x) { return lroundf(x); } + +namespace LibmShapes { + +const char* NAME = "Libm Shapes"; +const char* const KNOB_LABELS[4] = {"-", "-", "-", "-"}; + +volatile float in = 1.25f; +float out; + +void setup() {} + +void update(float dt, const InputFrame& input) { + (void)dt; + (void)input; +} + +void draw() { + const float a = in, b = in; + out = pf_shape_floorf(a) + pf_shape_ceilf(a) + pf_shape_truncf(a) + pf_shape_roundf(a) + + pf_shape_fminf(a, b) + pf_shape_fmaxf(a, b) + pf_shape_fmodf(a, b) + + pf_shape_fmodf_one(a) + pf_shape_fmodf_const(a) + pf_shape_fmodf_negconst(a) + + pf_shape_floor(a) + pf_shape_std_floor(a) + pf_shape_ceil(a) + pf_shape_trunc(a) + + pf_shape_round(a) + pf_shape_fmin(a, b) + pf_shape_fmax(a, b) + pf_shape_fmod(a, b) + + pf_shape_std_fmod(a, b) + pf_shape_std_fmod_const(a) + pf_control_builtin(a) + + pf_control_pointer(a) + (float)pf_control_lroundf(a); + PFCanvas::clear(); + PFCanvas::present(); +} + +} // namespace LibmShapes + +PF_REGISTER_PATTERN(LibmShapes) diff --git a/firmware/toolchain/tests/modules/_math_probe/pattern.cpp b/firmware/toolchain/tests/modules/_math_probe/pattern.cpp new file mode 100644 index 00000000..27a1719e --- /dev/null +++ b/firmware/toolchain/tests/modules/_math_probe/pattern.cpp @@ -0,0 +1,372 @@ +// Bench probe for the module SDK's inline libm (abi/pf_libm.h), not a showable +// pattern. check_libm.py proves on a PC that floorf, ceilf, truncf, roundf, +// fminf, fmaxf and fmodf compute what libm computes - given arithmetic that +// behaves as IEEE 754 says. Whether the panel's does is the part a PC cannot +// see: that MSUB.S multiplies and subtracts with ONE rounding (fmod's +// remainder is wrong in half of all cases if it does not), that TRUNC.S and +// FLOAT.S convert exactly, that the compares treat zeros and NaN properly, +// and that __divsf3 rounds to nearest (the reciprocal the compiler folds for +// `fmodf(x, TWO_PI)` is the one the board would compute). This module runs +// the inline code and the firmware's own libm side by side, on the board, +// and compares bits. +// +// python firmware/toolchain/build_module.py --out firmware/toolchain/tests/modules/_math_probe +// curl -X PUT --data-binary @/_math_probe.pfm -H "X-PF-Name: _math_probe.pfm" http:///api/patterns +// curl "http:///api/patterns/select?index=" # N: its index in GET /api/patterns +// curl http:///api/status # "active": "math ok 9/9 1532k" +// +// setup() spends 1.3 s on it, by the clock - a setup past 5 s trips the +// loader's watchdog, and a call into libm is 0.2-0.8 us, so that is on the +// order of two million comparisons, not the twenty billion a PC does - and +// writes the verdict into the pattern's NAME, which the registry copies once +// setup() returns: +// +// math ok 9/9 1532k nine checks, 1,532,000 comparisons, all equal +// math FAIL fmodf 1234 the first check that differed and how often +// math FAIL fmodf 1234 +2 ... and two more checks differed as well +// +// It then keeps going, a few thousand comparisons a frame with fresh inputs: +// the panel is green while everything has matched and red from the first +// mismatch on, the bar across the top fills at eight million comparisons, +// and the serial log gets a line every few seconds with the totals and the +// first mismatches as bits. +// +// The nine: floorf ceilf truncf roundf fminf fmaxf, "fmodf" (any divisor - +// the division path - and the same pairs through the reciprocal path with +// the reciprocal divided on the board), "fmodf const" (divisors written as +// literals, so the compiler folds the reciprocal: 1, 2*pi, 360, 0.1 ...), and +// "div" (__divsf3 rounds to nearest, and gives the folded reciprocals). +#include "pf_module.h" + +#include + +// The firmware's libm, under names the SDK has not put inline code behind. +extern "C" { +float host_floorf(float) __asm__("floorf"); +float host_ceilf(float) __asm__("ceilf"); +float host_truncf(float) __asm__("truncf"); +float host_roundf(float) __asm__("roundf"); +float host_fminf(float, float) __asm__("fminf"); +float host_fmaxf(float, float) __asm__("fmaxf"); +float host_fmodf(float, float) __asm__("fmodf"); +} + +namespace MathProbe { + +char verdict[40] = "math probe (not run)"; +const char* NAME = verdict; +const char* const KNOB_LABELS[4] = {"-", "-", "-", "-"}; + +enum Check { FLOOR, CEIL, TRUNC, ROUND, FMIN, FMAX, FMOD, FMOD_CONST, DIV, CHECKS }; +const char* const CHECK_NAMES[CHECKS] = { + "floorf", "ceilf", "truncf", "roundf", "fminf", "fmaxf", "fmodf", "fmodf const", "div", +}; + +uint32_t done[CHECKS]; +uint32_t wrong[CHECKS]; +int logged = 0; +uint32_t lastLogMs = 0; + +uint32_t bitsOf(float f) { + uint32_t u; + memcpy(&u, &f, sizeof u); + return u; +} + +float fromBits(uint32_t u) { + float f; + memcpy(&f, &u, sizeof f); + return f; +} + +// xorshift32, fixed seed: every board runs the same inputs in setup(). +uint32_t randomState = 0x9E3779B9u; +uint32_t randomBits() { + uint32_t x = randomState; + x ^= x << 13; + x ^= x >> 17; + x ^= x << 5; + return randomState = x; +} + +void same(Check check, float x, float y, float got, float want) { + ++done[check]; + if (bitsOf(got) == bitsOf(want)) return; + ++wrong[check]; + if (logged < 12) { + ++logged; + Serial.printf("[MATH] %s(%08x, %08x) inline %08x, libm %08x\n", CHECK_NAMES[check], + (unsigned)bitsOf(x), (unsigned)bitsOf(y), (unsigned)bitsOf(got), + (unsigned)bitsOf(want)); + } +} + +float withExponent(int field) { + if (field < 0) field = 0; + if (field > 254) field = 254; + return fromBits((randomBits() & 0x807fffffu) | ((uint32_t)field << 23)); +} + +int exponentOf(float f) { + return (int)((bitsOf(f) >> 23) & 0xffu); +} + +// d units in the last place away, by magnitude. +float stepped(float x, int d) { + const uint32_t u = bitsOf(x); + int32_t magnitude = (int32_t)(u & 0x7fffffffu) + d; + if (magnitude < 0) magnitude = 0; + if (magnitude > 0x7f7fffff) magnitude = 0x7f7fffff; + return fromBits((u & 0x80000000u) | (uint32_t)magnitude); +} + +const uint32_t EDGES[] = { + 0x00000000u, 0x80000000u, 0x00000001u, 0x80000001u, 0x007fffffu, 0x00800000u, + 0x3effffffu, 0x3f000000u, 0xbf000000u, 0x3f000001u, 0x3f7fffffu, 0xbf7fffffu, + 0x3f800000u, 0xbf800000u, 0x3f800001u, 0x3fc00000u, 0xbfc00000u, 0x40200000u, + 0xc0200000u, 0x4a800000u, 0x4affffffu, 0xcaffffffu, 0x4b000000u, 0xcb000000u, + 0x4b000001u, 0x4b800000u, 0x4f000000u, 0xcf000000u, 0x7f7fffffu, 0xff7fffffu, + 0x7f800000u, 0xff800000u, 0x7fc00000u, 0xffc00000u, 0x7f800001u, 0x7fffffffu, +}; +const int EDGE_COUNT = (int)(sizeof EDGES / sizeof EDGES[0]); + +// What a pattern feeds these functions, and what breaks an implementation. +float anyInput() { + const uint32_t r = randomBits(); + switch (r & 7u) { + case 0: // any bit pattern: huge, tiny, NaN, infinity + return fromBits(randomBits()); + case 1: // a whole number and the floats beside it + return stepped((float)((int)(randomBits() >> 9) - (1 << 22)), (int)((r >> 3) % 5u) - 2); + case 2: // n + 0.5 and the floats beside it: where round() decides + return stepped((float)((int)(randomBits() >> 10) - (1 << 21)) + 0.5f, + (int)((r >> 3) % 5u) - 2); + case 3: // around 2^23, where the inline range ends + return withExponent(148 + (int)((r >> 3) % 5u)); + case 4: // under 4: fractions, and the zero results whose sign matters + return withExponent(110 + (int)((r >> 3) % 19u)); + default: // 2^-23 .. 2^24, all of pixel arithmetic + return withExponent(104 + (int)((r >> 3) % 48u)); + } +} + +void unary(float x) { + same(FLOOR, x, 0.0f, floorf(x), host_floorf(x)); + same(CEIL, x, 0.0f, ceilf(x), host_ceilf(x)); + same(TRUNC, x, 0.0f, truncf(x), host_truncf(x)); + same(ROUND, x, 0.0f, roundf(x), host_roundf(x)); +} + +void minMax(float x, float y) { + same(FMIN, x, y, fminf(x, y), host_fminf(x, y)); + same(FMAX, x, y, fmaxf(x, y), host_fmaxf(x, y)); +} + +void minMaxInput() { + const float x = anyInput(); + const uint32_t r = randomBits(); + switch (r & 7u) { + case 0: minMax(x, x); break; + case 1: minMax(x, -x); break; + case 2: minMax(x, stepped(x, 1)); break; + case 3: minMax(stepped(x, 1), x); break; + case 4: minMax(x, fromBits(EDGES[(r >> 3) % (uint32_t)EDGE_COUNT])); break; + case 5: minMax(fromBits(EDGES[(r >> 3) % (uint32_t)EDGE_COUNT]), x); break; + default: minMax(x, anyInput()); break; + } +} + +// A multiplier for "x is k times y": anywhere in the fast path, small, or on +// the boundaries the proof turns on. +uint32_t multiplier() { + static const uint32_t BOUNDARY[] = { + 1u, 2u, 3u, 255u, 65536u, (1u << 22) - 2, (1u << 22) - 1, (1u << 22), + (1u << 22) + 1, (1u << 23) - 1, (1u << 23), (1u << 24) - 1, + }; + const uint32_t r = randomBits(); + switch (r & 3u) { + case 0: return 1u + (randomBits() >> 10); // up to 2^22 + case 1: return 1u + (randomBits() >> (10 + (r >> 2) % 22u)); // small + case 2: return 1u + (randomBits() >> 8); // up to 2^24, past the limit + default: return BOUNDARY[(r >> 2) % (uint32_t)(sizeof BOUNDARY / sizeof BOUNDARY[0])]; + } +} + +void mod(float x, float y) { + const float want = host_fmodf(x, y); + same(FMOD, x, y, fmodf(x, y), want); // y is not a constant here: the division path + same(FMOD, x, y, PFLibm::fmodRecip(x, y, 1.0f / fabsf(y)), want); +} + +void modInput() { + const uint32_t r = randomBits(); + if ((r & 3u) == 0) { // any divisor at all, quotients from under 1 to 2^25 + const float y = withExponent((int)((r >> 2) % 255u)); + mod(withExponent(exponentOf(y) - 1 + (int)((r >> 10) % 27u)), y); + return; + } + // x = k*y and its neighbours, for a y a pattern might hold. + const float y = withExponent(100 + (int)((r >> 2) % 40u)); + mod(stepped((float)multiplier() * y, (int)((r >> 10) % 9u) - 4), y); +} + +// A divisor written as a literal: the shadow sees a constant and multiplies by +// a reciprocal the compiler folded (and, for 1.0f, takes the fraction). +#define PROBE_LITERAL(C) \ + do { \ + const uint32_t r = randomBits(); \ + float x = stepped((float)multiplier() * (C), (int)((r >> 2) % 9u) - 4); \ + if ((r & 3u) == 0) x = withExponent(exponentOf(C) - 2 + (int)((r >> 6) % 27u)); \ + if ((r & 0x100u) != 0) x = -x; \ + same(FMOD_CONST, x, (C), fmodf(x, (C)), host_fmodf(x, (C))); \ + same(FMOD_CONST, x, -(C), fmodf(x, -(C)), host_fmodf(x, -(C))); \ + } while (0) + +void modLiterals() { + PROBE_LITERAL(1.0f); + PROBE_LITERAL(6.2831853f); + PROBE_LITERAL(3.14159265f); + PROBE_LITERAL(360.0f); + PROBE_LITERAL(0.1f); + PROBE_LITERAL(2.0f); + PROBE_LITERAL(255.0f); + PROBE_LITERAL(0.001f); +} + +// The folded reciprocal is the one this board's divider gives. +#define PROBE_RECIPROCAL(C) \ + do { \ + volatile float one = 1.0f, divisor = (C); \ + same(DIV, 1.0f, (C), one / divisor, 1.0f / (C)); \ + } while (0) + +void reciprocals() { + PROBE_RECIPROCAL(6.2831853f); + PROBE_RECIPROCAL(3.14159265f); + PROBE_RECIPROCAL(360.0f); + PROBE_RECIPROCAL(0.1f); + PROBE_RECIPROCAL(255.0f); + PROBE_RECIPROCAL(0.001f); +} + +// q = a / b is the nearest float to the quotient exactly when neither float +// beside it leaves a smaller remainder a - q*b. The remainders are taken +// fused; rounding them cannot reorder them. +void division() { + volatile float a = withExponent(100 + (int)(randomBits() % 50u)); + volatile float b = withExponent(100 + (int)(randomBits() % 50u)); + const float x = a, y = b; + const float q = a / b; + const float here = fabsf(PFLibm::fnma(q, y, x)); + const float above = fabsf(PFLibm::fnma(stepped(q, 1), y, x)); + const float below = fabsf(PFLibm::fnma(stepped(q, -1), y, x)); + ++done[DIV]; + if (here <= above && here <= below) return; + ++wrong[DIV]; + if (logged < 12) { + ++logged; + Serial.printf("[MATH] div %08x / %08x gave %08x, not the nearest float\n", + (unsigned)bitsOf(x), (unsigned)bitsOf(y), (unsigned)bitsOf(q)); + } +} + +// Run `body` in batches until `ms` have passed. +template +void spend(uint32_t ms, Body body) { + const uint32_t start = millis(); + do { + for (int i = 0; i < 32; ++i) body(); + } while (millis() - start < ms); +} + +void run(uint32_t unaryMs, uint32_t minMaxMs, uint32_t modMs, uint32_t literalMs, + uint32_t divisionMs) { + spend(unaryMs, [] { unary(anyInput()); }); + spend(minMaxMs, minMaxInput); + spend(modMs, modInput); + spend(literalMs, modLiterals); + spend(divisionMs, division); +} + +uint32_t total(const uint32_t* counts) { + uint32_t sum = 0; + for (int c = 0; c < CHECKS; ++c) sum += counts[c]; + return sum; +} + +void writeVerdict() { + int failed = 0, first = -1; + for (int c = 0; c < CHECKS; ++c) { + if (!wrong[c]) continue; + if (first < 0) first = c; + ++failed; + } + if (first < 0) { + snprintf(verdict, sizeof verdict, "math ok %d/%d %uk", (int)CHECKS, (int)CHECKS, + (unsigned)(total(done) / 1000u)); + } else if (failed == 1) { + snprintf(verdict, sizeof verdict, "math FAIL %s %u", CHECK_NAMES[first], + (unsigned)wrong[first]); + } else { + snprintf(verdict, sizeof verdict, "math FAIL %s %u +%d", CHECK_NAMES[first], + (unsigned)wrong[first], failed - 1); + } +} + +void logTotals() { + Serial.printf("[MATH] %s - %u comparisons, %u differ\n", verdict, (unsigned)total(done), + (unsigned)total(wrong)); + for (int c = 0; c < CHECKS; ++c) { + Serial.printf("[MATH] %-11s %8u differ %u\n", CHECK_NAMES[c], (unsigned)done[c], + (unsigned)wrong[c]); + } +} + +void setup() { + for (int i = 0; i < EDGE_COUNT; ++i) { + const float x = fromBits(EDGES[i]); + unary(x); + for (int j = 0; j < EDGE_COUNT; ++j) { + const float y = fromBits(EDGES[j]); + minMax(x, y); + mod(x, y); + } + } + reciprocals(); + run(450, 200, 350, 250, 80); + writeVerdict(); + logTotals(); + lastLogMs = millis(); +} + +void update(float dt, const InputFrame& input) { + (void)dt; + (void)input; +} + +void draw() { + // The inputs carry on from where setup() left the generator. NAME is not + // rewritten here: the registry took its copy when setup() returned, and a + // name that changed afterwards would only be seen by half the readers. + run(2, 1, 2, 2, 1); + const bool ok = total(wrong) == 0; + const uint32_t comparisons = total(done); + const int bar = comparisons >= (8u << 20) ? PANEL_RES_W : (int)(comparisons >> 16); + for (int y = 0; y < PANEL_RES_H; ++y) { + for (int x = 0; x < PANEL_RES_W; ++x) { + const bool lit = y < 4 && x < bar; + const uint8_t level = lit ? 200 : 40; + PFCanvas::setPixel(x, y, ok ? 0 : level, ok ? level : 0, 0); + } + } + PFCanvas::present(); + if (millis() - lastLogMs >= 5000) { + lastLogMs = millis(); + Serial.printf("[MATH] %s so far: %u comparisons, %u differ\n", ok ? "ok" : "FAIL", + (unsigned)comparisons, (unsigned)total(wrong)); + } +} + +} // namespace MathProbe + +PF_REGISTER_PATTERN(MathProbe) diff --git a/firmware/toolchain/tests/resident_test.cpp b/firmware/toolchain/tests/resident_test.cpp new file mode 100644 index 00000000..ed124fec --- /dev/null +++ b/firmware/toolchain/tests/resident_test.cpp @@ -0,0 +1,512 @@ +// Modules that stay loaded: the bookkeeping (src/core_module_resident.h) on +// its own, then the loader's own park/resume/unload/evict code, lifted from +// src/core_module_loader.h by check_runtime.py, against a heap that knows +// which blocks are PSRAM and a crash breadcrumb that records what it is told. +#include +#include +#include +#include +#include +#include +#ifdef _MSC_VER +#include +#define __ATOMIC_ACQUIRE 0 +#define __ATOMIC_ACQ_REL 0 +template T __atomic_load_n(T* p, int) { return std::atomic_ref(*p).load(); } +template T __atomic_add_fetch(T* p, V v, int) { + return std::atomic_ref(*p).fetch_add(static_cast(v)) + static_cast(v); +} +#endif + +constexpr unsigned MALLOC_CAP_INTERNAL=1, MALLOC_CAP_8BIT=2, MALLOC_CAP_SPIRAM=4; +constexpr unsigned MALLOC_CAP_EXEC=8, MALLOC_CAP_32BIT=16; + +// PSRAM and the internal heap, block by block. A free() of anything neither +// handed out is an assert: that is what freeing a code block through its +// line-aligned `memory` instead of the heap's `block` would look like. +static size_t psramSize=64*1024, psramUsed=0, internalFree=100000; +static bool psramRefuses=false; +// PSRAM that says no to the next N asks with plenty free in total: no block +// that large, which eviction may or may not cure. +static int psramNoBlock=0; +static std::map psramOwned, internalOwned; +static unsigned frees=0; +static size_t heap_caps_get_free_size(unsigned caps) { + return (caps & MALLOC_CAP_SPIRAM) ? psramSize-psramUsed : internalFree; +} +static size_t heap_caps_get_largest_free_block(unsigned caps) { return heap_caps_get_free_size(caps); } +static void* heap_caps_malloc(size_t n, unsigned caps) { + if(caps & MALLOC_CAP_SPIRAM) { + if(psramNoBlock>0) { --psramNoBlock; return nullptr; } + if(psramRefuses || n>psramSize-psramUsed) return nullptr; + void* p=malloc(n); assert(p); psramOwned[p]=n; psramUsed+=n; return p; + } + if(n>internalFree) return nullptr; + void* p=malloc(n); assert(p); internalOwned[p]=n; internalFree-=n; return p; +} +static void* heap_caps_calloc(size_t n, size_t s, unsigned caps) { + void* p=heap_caps_malloc(n*s,caps); if(p) memset(p,0,n*s); return p; +} +static void testFree(void* p) { + if(!p) return; + ++frees; + auto e=psramOwned.find(p); + if(e!=psramOwned.end()) { psramUsed-=e->second; psramOwned.erase(e); free(p); return; } + auto i=internalOwned.find(p); + assert(i!=internalOwned.end()); + internalFree+=i->second; internalOwned.erase(i); free(p); +} +static bool esp_ptr_external_ram(const void* p) { + const char* at=static_cast(p); + for(const auto& block : psramOwned) { + const char* base=static_cast(block.first); + if(at>=base && at void printf(const char*,T...) {} } Serial; +namespace PFCrash { +enum Phase : uint32_t { IDLE=0, LOADING, CONSTRUCTORS, SETUP, UPDATE, DRAW }; +static const void* owner=nullptr; +static char slug[80]; +static uintptr_t codeBase=0; +static uint32_t codeSize=0, phase=IDLE; +inline void forget() { owner=nullptr; slug[0]=0; codeBase=0; codeSize=0; phase=IDLE; } +inline void running(const void* who, const char* path) { + owner=who; snprintf(slug,sizeof(slug),"%s",path); codeBase=0; codeSize=0; +} +inline void code(uintptr_t base, uint32_t bytes) { codeBase=base; codeSize=bytes; } +inline void enter(Phase p) { phase=p; } +} +struct PFPatternModule { const char* name; }; + +#define free(p) testFree(p) +namespace PFModuleLoader { +#include "loader_state.h" +#include "loader_alloc.h" +#include "loader_unload.h" +#include "loader_timings.h" +#include "loader_resident.h" +} +#undef free + +// ── The bookkeeping alone ───────────────────────────────────────────── +struct Toy { void* block; int id; }; +static unsigned toyReleases=0; +static void releaseToy(Toy& toy) { ++toyReleases; free(toy.block); toy.block=nullptr; } +static bool tableRefuses=false; +// A table keeps its slots for the boot. These do not outlive the case. +static void* toyTables[4]; static int toyTableCount=0; +static void* toyTable(size_t bytes) { + if(tableRefuses) return nullptr; + assert(toyTableCount<4); + return toyTables[toyTableCount++]=calloc(1,bytes); +} +using Toys=PFModuleResident::Table; +static Toy toy(int id) { return Toy{malloc(8),id}; } +template +static bool parkToy(Table& table, const char* path, int id, const char* keep=nullptr) { + Toy fresh=toy(id); + if(table.park(path,10,[&](Toy& slot){ slot=fresh; },keep)) return true; + free(fresh.block); // not taken: the caller still owns it and unloads it + return false; +} +template +static int takeToy(Table& table, const char* path) { + Toy back{nullptr,-1}; + if(!table.take(path,[&](Toy& slot){ back=slot; })) return -1; + free(back.block); + return back.id; +} + +static void bookkeeping() { + // No slots compiled in: nothing parks and nothing is allocated to find out. + { + PFModuleResident::Table none; + bool filled=false; + assert(!none.park("/a",1,[&](Toy&){ filled=true; }) && !filled && !none.allocated()); + assert(none.capacity()==0 && !none.has("/a") && none.evictAll()==0); + } + Toys table; + // No table to be had: the park is refused before anything is taken, and the + // next park tries again. + tableRefuses=true; + assert(!parkToy(table,"/a",1) && !table.allocated() && table.count==0); + tableRefuses=false; + // Park, find, take: the module comes back exactly once. + assert(parkToy(table,"/a",1) && table.allocated() && table.count==1 && table.bytes==10); + assert(table.has("/a") && !table.has("/b") && !table.has(nullptr)); + assert(takeToy(table,"/a")==1 && takeToy(table,"/a")==-1); + assert(table.count==0 && table.bytes==0 && table.resumes==1 && toyReleases==0); + // No key, an empty key, a key too long to hold whole: never parked. + char longPath[PFModuleResident::PATH_BYTES+1]; + memset(longPath,'x',sizeof(longPath)-1); longPath[sizeof(longPath)-1]=0; + assert(!parkToy(table,nullptr,9) && !parkToy(table,"",9) && !parkToy(table,longPath,9)); + longPath[PFModuleResident::PATH_BYTES-1]=0; // 71 characters: fits + assert(parkToy(table,longPath,9) && takeToy(table,longPath)==9); + // Full: the least recently PARKED goes first, by counter, not by clock. + assert(parkToy(table,"/a",1) && parkToy(table,"/b",2) && parkToy(table,"/c",3)); + assert(table.count==3 && table.oldest()==table.find("/a")); + assert(parkToy(table,"/d",4)); + assert(!table.has("/a") && table.count==3 && toyReleases==1 && table.evictions==1); + // Taking one back and parking it again makes it the newest. + assert(takeToy(table,"/b")==2 && parkToy(table,"/b",2)); + assert(table.oldest()==table.find("/c")); + // `keep` - the one about to be resumed - is never what a full table evicts. + assert(parkToy(table,"/e",5,"/c")); + assert(table.has("/c") && !table.has("/d") && table.count==3 && toyReleases==2); + // A path parked twice is one slot: the older copy is released. + assert(parkToy(table,"/c",33)); + assert(table.count==3 && toyReleases==3 && takeToy(table,"/c")==33); + // Until enough: least recently used first, and it stops there. + assert(parkToy(table,"/f",6)); // order now: b, e, f + int asked=0; + assert(table.evictUntil([&]{ return ++asked>1; })==1); + assert(!table.has("/b") && table.has("/e") && table.has("/f")); + // ...or until nothing is parked. + assert(table.evictUntil([]{ return false; })==2 && table.count==0 && table.bytes==0); + assert(!table.evictOldest() && table.evictUntil([]{ return false; })==0); + // A table whose only resident is `keep` cannot make room for another. + { + PFModuleResident::Table one; + assert(parkToy(one,"/a",1) && !parkToy(one,"/b",2,"/a") && one.has("/a")); + assert(parkToy(one,"/b",2) && !one.has("/a") && one.evictAll()==1); + } + assert(parkToy(table,"/a",1) && parkToy(table,"/b",2) && table.evictAll()==2); + assert(table.count==0 && table.bytes==0); + // Looking without taking; evicting by a test of the module itself - only + // the slots it names, released, the rest left exactly where they were. + assert(parkToy(table,"/a",1) && parkToy(table,"/b",2) && parkToy(table,"/c",3)); + assert(table.peek("/b") && table.peek("/b")->id==2 && !table.peek("/x") && table.count==3); + const unsigned released=toyReleases; + assert(table.evictIf([](const Toy& t){ return t.id!=2; })==2); + assert(table.count==1 && table.has("/b") && toyReleases==released+2 && table.bytes==10); + assert(table.evictIf([](const Toy&){ return false; })==0 && table.count==1); + assert(takeToy(table,"/b")==2 && table.count==0); + for(int i=0; i(heap_caps_malloc( + CODE+LINE, codeExternal ? MALLOC_CAP_SPIRAM : MALLOC_CAP_INTERNAL|MALLOC_CAP_EXEC)); + assert(block); + uint8_t* code=codeExternal ? block+16 : block; + LoadedSection& text=sections[sectionCount++]; + text.index=1; text.size=(uint32_t)CODE; text.memory=code; text.block=block; + text.exec=codeExternal ? (uintptr_t)code+0x06000000u : (uintptr_t)code; + text.executable=true; + uint8_t* bytes=static_cast(heap_caps_calloc( + 1,data,dataInternal ? MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT : MALLOC_CAP_SPIRAM)); + assert(bytes); + LoadedSection& rodata=sections[sectionCount++]; + rodata.index=2; rodata.size=(uint32_t)data; rodata.memory=bytes; + (codeExternal ? lastPsramBytes : lastInternalBytes)+=(uint32_t)(CODE+LINE); + (dataInternal ? lastInternalBytes : lastPsramBytes)+=(uint32_t)data; + lastCodeExternal=codeExternal; + lastCodeBytes=(uint32_t)CODE; + lastReadUs=1000u*which+1; lastRelocateUs=1000u*which+2; + lastSetupUs=1000u*which+3; lastTotalUs=1000u*which+6; + active=&descriptors[which]; + PFCrash::running(path,path); + PFCrash::code(text.exec,text.size); +} + +static bool same(const ResidentModule& a, const ResidentModule& b) { + if(a.sectionCount!=b.sectionCount || a.moduleAllocCount!=b.moduleAllocCount) return false; + for(int i=0; i0 && resident.count==0 && resident.bytes==0 && resident.resumes==1); + assert(PFCrash::owner==asked && !strcmp(PFCrash::slug,A) && PFCrash::phase==PFCrash::IDLE); + assert(PFCrash::codeBase==sections[0].exec && PFCrash::codeSize==sections[0].size); + assert(frees==freesParked && psramUsed==withA); + // ...and it goes on allocating as itself. + assert(moduleAlloc(50) && runtimeBytes==150 && moduleAllocCount==2); + + // Leaving it again: /api/status's load.resumed describes the module on the + // panel, and with a preset there it is false - the timings stay as A had + // them. + leave(); + assert(!resumed && lastResumeUs==0 && lastTotalUs==6 && isParked(A)); + + // Resuming over a current module would orphan it: refused. + fakeLoad(B,1,512); + assert(!resume(A) && active==&descriptors[1] && isParked(A)); + + // Not kept, so unloaded, and every byte comes back: any internal RAM at + // all, a file that may have changed, code that never left internal RAM. + const size_t internalBaseline=internalFree; + const unsigned beforeNotKept=resident.count; + fakeLoad(C,2,512,true); + assert(!parkable()); + leave(); + assert(nothingCurrent() && resident.count==beforeNotKept && internalFree==internalBaseline); + fakeLoad(C,2,512,false,false); + assert(!parkable()); + leave(); + assert(!isParked(C) && internalFree==internalBaseline); + fakeLoad(C,2,512); + psramRefuses=true; + assert(moduleAlloc(64) && runtimeInternalBytes==64); // the internal fallback + psramRefuses=false; + assert(!parkable()); + leave(); + assert(!isParked(C) && internalFree==internalBaseline); + + // A module file written, from any task: one atomic add, nothing freed. + // A is parked from before it, so A is never resumed again - isParked() and + // resume() say no while its slot is still held - and the running C, loaded + // before it too, is unloaded when left rather than parked. + fakeLoad(A,0,512); leave(); + assert(isParked(A) && resident.count==1); + fakeLoad(C,2,512); + const size_t beforeWrite=psramUsed; + const unsigned freesBeforeWrite=frees; + noteStorageWrite(); + assert(frees==freesBeforeWrite && psramUsed==beforeWrite && resident.count==1); + assert(!isParked(A) && !parkable()); + leave(); + assert(nothingCurrent() && !resume(A) && nothingCurrent() && resident.count==1); + // The loop evicts it the next time it owns the table: here, a park. A + // module loaded after the write is kept as usual. + const unsigned evictionsBefore=resident.evictions; + fakeLoad(B,1,512); + assert(parkable()); + leave(); + assert(isParked(B) && resident.count==1 && resident.evictions==evictionsBefore+1); + // ...or a fresh load making room, or a module's own allocation. + noteStorageWrite(); + makeRoom(); + assert(resident.count==0 && resident.evictions==evictionsBefore+2); + fakeLoad(B,1,512); leave(); + fakeLoad(C,2,512); + noteStorageWrite(); + assert(moduleAlloc(16) && resident.count==0 && resident.evictions==evictionsBefore+3); + leave(); + // A module parked and resumed keeps the generation it loaded under. + fakeLoad(A,0,512); leave(); + assert(resume(A) && parkable()); + noteStorageWrite(); + assert(!parkable()); + leave(); + // The rebuild after a write: stale parked copies go, and so does the + // running module, unloaded, when it is one of them. After a rebuild with + // nothing written since, nothing is touched. + fakeLoad(B,1,512); leave(); + fakeLoad(C,2,512); + assert(!dropStale() && active==&descriptors[2] && isParked(B)); + noteStorageWrite(); + assert(dropStale() && nothingCurrent() && resident.count==0); + assert(!dropStale()); + + // Evicting a parked module frees exactly what unloading it would have: + // the heap is back where it was before it loaded. + const size_t empty=psramUsed; + fakeLoad(A,0,512); + assert(moduleAlloc(200)); + leave(); + assert(psramUsed>empty && resident.count==1); + const unsigned evictedBefore=resident.evictions; + dropParked(); + assert(psramUsed==empty && resident.count==0 && resident.evictions==evictedBefore+1); + + // A full table (3 here) makes room by its least recently used - never the + // one the caller is about to resume. + fakeLoad(A,0,512); leave(); + fakeLoad(B,1,512); leave(); + fakeLoad(C,2,512); leave(); + assert(resident.count==3); + fakeLoad(D,3,512); + const unsigned evicted=resident.evictions; + leave(A); // A is the oldest: B goes instead + assert(resident.count==3 && isParked(A) && !isParked(B) && isParked(C) && isParked(D)); + assert(resident.evictions==evicted+1 && resume(A) && active==&descriptors[0]); + + // The same file parked twice is one slot: the older copy goes, whole. + leave(); // A parked again: table C, D, A + fakeLoad(A,4,512); // a second copy of the same file + const size_t twoCopies=psramUsed; + leave(); + assert(resident.count==3 && isParked(A) && psramUsed=floorBytes); + // A floor that cannot be met empties the table, and goes no further. + psramSize=psramUsed+100; + makeRoom(); + assert(resident.count==0 && heap_caps_get_free_size(MALLOC_CAP_SPIRAM)alloc() while modules are parked may not leave less than the + // headroom free: parked modules go, oldest first, only as many as that + // takes, and the allocation is in PSRAM. + const size_t headroom=PF_MODULE_RESIDENT_HEADROOM; + psramSize=psramUsed+3*footprint+2000; + fakeLoad(A,0,2000); leave(); + fakeLoad(B,1,2000); leave(); + fakeLoad(C,2,2000); // C runs with 2000 bytes free + const uint32_t refusals=PFModuleMemory::refusals; + assert(moduleAlloc(500) && isParked(A) && isParked(B)); // leaves 1500: nothing goes + assert(moduleAlloc(700) && !isParked(A) && isParked(B)); // would leave 800: A goes + assert(heap_caps_get_free_size(MALLOC_CAP_SPIRAM)>=headroom); + assert(runtimeInternalBytes==0 && runtimeBytes==1200 && PFModuleMemory::refusals==refusals); + leave(); + dropParked(); + // When evicting every parked module cannot give it the headroom, the + // allocation still gets what is free, as it would have before residency: + // the headroom is never a refusal. + const size_t small=CODE+LINE+100; + psramSize=psramUsed+3*small+100; + fakeLoad(A,0,100); leave(); + fakeLoad(B,1,100); leave(); + fakeLoad(C,2,100); // C runs with 100 bytes free + assert(moduleAlloc(500) && resident.count==0 && runtimeInternalBytes==0); + assert(heap_caps_get_free_size(MALLOC_CAP_SPIRAM)=headroom); + dropParked(); + psramSize=psramUsed+footprint+600; + fakeLoad(A,0,2000); // alone, 600 bytes free + leave(); + assert(resident.count==0 && nothingCurrent()); + psramSize=psramUsed+2*footprint+600; + fakeLoad(A,0,2000); leave(); + fakeLoad(B,1,2000); // B runs with 600 bytes free + leave(A); // `keep` survives the floor + assert(isParked(A) && !isParked(B) && nothingCurrent()); + dropParked(); + // PSRAM that refuses for want of a block, with plenty free: the refusal + // costs the oldest parked module and PSRAM is asked again, before the + // internal heap is - and no refusal is counted for a request that ended in + // PSRAM. + psramSize=psramUsed+3*footprint+20000; + fakeLoad(A,0,2000); leave(); + fakeLoad(B,1,2000); leave(); + fakeLoad(C,2,2000); + psramNoBlock=1; + assert(moduleAlloc(100) && !isParked(A) && isParked(B)); + assert(runtimeInternalBytes==0 && PFModuleMemory::refusals==refusals); + // Nothing left to give back: data()'s internal fallback, as before + // residency, and the module can no longer be kept. + psramNoBlock=3; + assert(moduleAlloc(16) && resident.count==0 && runtimeInternalBytes==16 && !parkable()); + psramNoBlock=0; + leave(); + + // Everything given back. + dropParked(); unload(); + psramSize=64*1024; + assert(internalOwned.empty() && internalFree==100000); + assert(psramOwned.size()==1); // the table itself, kept for the boot +} + +int main() { + bookkeeping(); + loader(); + puts("PASS: resident table (park/take/peek/LRU/keep/double park/evict-until/evict-if/no table) and the loader's park/resume/unload/evict, storage generation, floor, alloc headroom and backstop"); +} diff --git a/firmware/toolchain/tests/runtime_test.cpp b/firmware/toolchain/tests/runtime_test.cpp index 879dc718..decb73b5 100644 --- a/firmware/toolchain/tests/runtime_test.cpp +++ b/firmware/toolchain/tests/runtime_test.cpp @@ -50,6 +50,7 @@ static void heap_caps_free(void* p) { free(p); } #include "core_module_memory.h" +#include "core_module_resident.h" #include "sidecar_name.h" using TaskHandle_t = void*; @@ -127,7 +128,8 @@ static bool posted() { return __atomic_load_n(&PFLoopSync::pendingFn,__ATOMIC_AC constexpr int MODULE_PATH_BYTES=96, MODULE_NAME_BYTES=64, NUM_PRESETS=1, PF_CUSTOM_SLOT_COUNT=0; struct PatternEntry { const char* name; const char* modulePath; }; -static PatternEntry entries[]={{"Origin",nullptr},{"A","/a.pfm"},{"B","/b.pfm"}}; +// C is past NUM_PATTERNS until the resident cases, which need a third module. +static PatternEntry entries[]={{"Origin",nullptr},{"A","/a.pfm"},{"B","/b.pfm"},{"C","/c.pfm"}}; static PatternEntry* patterns=entries; static int NUM_PATTERNS=3, activePatternIdx=1, currentPatternIdx=1, numModules=2; static char moduleNames[2][MODULE_NAME_BYTES]{}; @@ -136,22 +138,100 @@ static bool createFails=false, notified=false, reorder=false, removeB=false; static unsigned created=0; static unsigned retryPauseMs=0; static void vTaskDelay(unsigned ms) { retryPauseMs+=ms; } +// The loader at the registry's boundary: real residency bookkeeping +// (core_module_resident.h), a malloc'd block standing in for a module's +// memory so the sanitizers see every free. What a module IS and when the real +// loader parks one is resident_test.cpp's business; here it is whether the +// registry parks, resumes and drops at the right moments. namespace PFModuleLoader { struct Descriptor { const char* name; } descriptor{"loaded"}; inline Descriptor* active=&descriptor; -inline unsigned unloads=0, loads=0; +inline unsigned unloads=0, loads=0, resumes=0, makeRooms=0, drops=0; inline unsigned allocationFailures=0; inline bool invalidFile=false; -inline void unload() { ++unloads; active=nullptr; } -inline bool load(int,const char*) { +// The module loaded next may be kept; the current one may be kept. The boot +// module of the cases below holds no block and is never kept, so the cases +// written before residency see what they always did. +inline bool parkableLoads=true, currentParkable=false; +inline char* memory=nullptr; +inline char currentPath[96]{}; +// The storage generation, as the real loader keeps it: moved by every write, +// stamped on every load, carried through a park and a resume. +inline unsigned storage=0, currentGeneration=0; +inline void noteStorageWrite() { ++storage; } +struct Parked { Descriptor* active; char* memory; unsigned generation; }; +inline void releaseParked(Parked& parked) { free(parked.memory); parked.memory=nullptr; } +inline void* allocateTable(size_t bytes) { return calloc(1,bytes); } +// Two slots, so a third module fills the table. +inline PFModuleResident::Table table; +inline void unload() { + ++unloads; active=nullptr; free(memory); memory=nullptr; + currentPath[0]=0; currentParkable=false; +} +inline bool load(int,const char* path) { ++loads; + unload(); + currentGeneration=storage; if(invalidFile) return false; if(allocationFailures) { --allocationFailures; ++PFModuleMemory::refusals; return false; } - active=&descriptor; return true; + active=&descriptor; memory=static_cast(malloc(16)); + snprintf(currentPath,sizeof(currentPath),"%s",path); + currentParkable=parkableLoads; return true; } inline bool fail(const char*) { return false; } inline const char* error() { return "test"; } +inline bool parkable() { return active && memory && currentParkable && currentGeneration==storage; } +inline bool fresh(const Parked& parked) { return parked.generation==storage; } +inline void evictStale() { table.evictIf([](const Parked& parked){ return !fresh(parked); }); } +inline bool park(const char* keep=nullptr) { + if(!parkable()) return false; + evictStale(); + Descriptor* leaving=active; char* held=memory; const unsigned generation=currentGeneration; + if(!table.park(currentPath,16,[&](Parked& slot){ + slot.active=leaving; slot.memory=held; slot.generation=generation; },keep)) + return false; + active=nullptr; memory=nullptr; currentPath[0]=0; currentParkable=false; + return true; +} +inline void leave(const char* keep=nullptr) { if(!park(keep)) unload(); } +inline bool isParked(const char* path) { + const Parked* parked=table.peek(path); + return parked && fresh(*parked); +} +inline bool resume(const char* path) { + if(active || !isParked(path)) return false; + Parked back{}; + if(!table.take(path,[&](Parked& slot){ back=slot; })) return false; + active=back.active; memory=back.memory; currentParkable=true; currentGeneration=back.generation; + snprintf(currentPath,sizeof(currentPath),"%s",path); + ++resumes; return true; +} +inline void makeRoom() { ++makeRooms; evictStale(); } +inline void dropParked() { ++drops; table.evictAll(); } +inline bool dropStale() { + evictStale(); + if(!active || currentGeneration==storage) return false; + unload(); return true; +} } +// The sidecar cache's forget calls, lifted from the registry: what every +// module-file writer already calls, core and feature alike. +static const char* const MODULE_DIR="/patterns"; +static char (*sidecarPaths)[MODULE_PATH_BYTES]=nullptr; +static char (*sidecarNames)[MODULE_NAME_BYTES]=nullptr; +static bool* sidecarAbs=nullptr; +static int sidecarCount=0; +// sidecarForgetPath() compacts by copying the last slot over slot i, under +// `if (i != last)`; g++'s -Wrestrict does not follow the guard and calls the +// two slots possibly the same object. They are not, on the device or here. +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wrestrict" +#endif +#include "sidecar_forget.h" +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif static int xTaskCreatePinnedToCore(void(*)(void*),const char*,uint32_t,void*,unsigned,void** out,int) { if(createFails) return 0; ++created; *out=reinterpret_cast(3); return pdPASS; @@ -487,6 +567,148 @@ int main() { retryPauseMs=0; beforeLoads=PFModuleLoader::loads; loadPatternJob(); assert(!loadResult && PFModuleLoader::loads-beforeLoads==1 && retryPauseMs==0); + + // ── Resident modules (src/core_module_resident.h) ── + // Every write reported to the sidecar cache moves the storage generation: + // a path the cache never held is still a file written, and a format is + // every file at once. + { + sidecarPaths=static_cast(calloc(2,MODULE_PATH_BYTES)); + sidecarNames=static_cast(calloc(2,MODULE_NAME_BYTES)); + sidecarAbs=static_cast(calloc(2,sizeof(bool))); + const unsigned before=PFModuleLoader::storage; + sidecarForgetSlug("never_cached"); + assert(PFModuleLoader::storage==before+1); + snprintf(sidecarPaths[0],MODULE_PATH_BYTES,"%s","/patterns/x.pfm"); sidecarCount=1; + sidecarForgetSlug("x"); + assert(PFModuleLoader::storage==before+2 && sidecarCount==0); + sidecarCount=1; sidecarForgetAll(); + assert(PFModuleLoader::storage==before+3 && sidecarCount==0); + free(sidecarPaths); free(sidecarNames); free(sidecarAbs); + sidecarPaths=nullptr; sidecarNames=nullptr; sidecarAbs=nullptr; + } + // Back to the list the cases above started from, Origin running. + PFModuleLoader::invalidFile=false; PFModuleLoader::allocationFailures=0; + entries[1]={"A","/a.pfm"}; entries[2]={"B","/b.pfm"}; NUM_PATTERNS=3; removeB=false; + PFModuleLoader::unload(); PFModuleLoader::dropParked(); + activePatternIdx=0; loadQueuedIdx=-1; loadTargetIdx=-1; loadFinished=false; + assert(!loadInFlight && !patternLoadsHeld); + // A loads on the worker, as it always did. + notified=false; + assert(activatePatternAsync(1) && loadInFlight && notified); + loadPatternJob(); serviceAsyncLoad(); + assert(activePatternIdx==1 && PFModuleLoader::active && !loadInFlight); + const unsigned loadsWithA=PFModuleLoader::loads; + // Leaving it for a preset parks it: nothing freed, nothing loaded. + assert(switchesInstantly(0)); + assert(activatePatternAsync(0) && activePatternIdx==0 && !PFModuleLoader::active); + assert(PFModuleLoader::isParked("/a.pfm") && PFModuleLoader::table.count==1); + // Coming back resumes it inside the call, on this task: no worker, no load, + // no thumbnail frame in between - and the name slot is refreshed as a load + // refreshes it. + notified=false; moduleNames[0][0]=0; + assert(isResidentOrPreset(1) && switchesInstantly(1)); + assert(activatePatternAsync(1)); + assert(activePatternIdx==1 && PFModuleLoader::active && !loadInFlight && !notified); + assert(PFModuleLoader::loads==loadsWithA && PFModuleLoader::resumes==1); + assert(PFModuleLoader::table.count==0 && !strcmp(moduleNames[0],"loaded")); + // A module that is not parked still loads on the worker; the outgoing one + // is parked before the worker starts, and room is made for the incoming. + const unsigned rooms=PFModuleLoader::makeRooms; + notified=false; + assert(!isResidentOrPreset(2) && !switchesInstantly(2)); + assert(activatePatternAsync(2) && loadInFlight && notified && activePatternIdx==-1); + assert(PFModuleLoader::isParked("/a.pfm") && PFModuleLoader::makeRooms==rooms+1); + // The worker owns the table now: the loop answers nothing from it, and no + // switch is instant, presets included - it would only be queued. + assert(!isResidentOrPreset(1) && isResidentOrPreset(0)); + assert(!switchesInstantly(0) && !switchesInstantly(1)); + // A request for A while B loads waits its turn, then resumes A rather + // than loading it, and parks B on the way. + assert(activatePatternAsync(1) && loadQueuedIdx==1); + loadPatternJob(); serviceAsyncLoad(); + assert(activePatternIdx==1 && !loadInFlight && PFModuleLoader::resumes==2); + assert(PFModuleLoader::loads==loadsWithA+1); + assert(PFModuleLoader::isParked("/b.pfm") && !PFModuleLoader::isParked("/a.pfm")); + // Leaving a module that cannot be kept would cost its whole load when the + // knob came back, so SELECT does not switch away from it on a detent. + PFModuleLoader::currentParkable=false; + assert(!switchesInstantly(0) && !switchesInstantly(2)); + PFModuleLoader::currentParkable=true; + // The boot path (synchronous) resumes too, and parks what it leaves. + assert(activatePattern(2) && activePatternIdx==2 && PFModuleLoader::resumes==3); + assert(PFModuleLoader::isParked("/a.pfm") && PFModuleLoader::loads==loadsWithA+1); + assert(activatePattern(1) && PFModuleLoader::resumes==4 && PFModuleLoader::isParked("/b.pfm")); + // A storage mutation starts with nothing in memory: the running module AND + // every parked one, since a file may be replaced or deleted from here on - + // the parked ones even when a preset is what keeps running through it. + assert(activatePatternAsync(0) && PFModuleLoader::isParked("/a.pfm")); + assert(Manager::captureSelectionOnce()); + assert(PFModuleLoader::table.count==0 && activePatternIdx==0); + assert(Manager::restoreSelection() && activePatternIdx==0 && !loadInFlight); + assert(activatePatternAsync(1)); loadPatternJob(); serviceAsyncLoad(); + assert(activePatternIdx==1); + assert(Manager::captureSelectionOnce()); + assert(PFModuleLoader::table.count==0 && !PFModuleLoader::active && activePatternIdx==-1); + assert(Manager::restoreSelection()); + assert(loadInFlight && loadTargetIdx==1); // a fresh load, not a resume + loadPatternJob(); serviceAsyncLoad(); + assert(activePatternIdx==1 && PFModuleLoader::loads==loadsWithA+3); + // A rebuild with nothing written since touches nothing: restorePath is + // A's (from the capture), A is parked and loaded from the storage as it is, + // so it is resumed, and B is parked on the way. + assert(activatePatternAsync(2)); loadPatternJob(); serviceAsyncLoad(); + assert(activePatternIdx==2 && PFModuleLoader::isParked("/a.pfm")); + unsigned resumesBefore=PFModuleLoader::resumes; + assert(Manager::restoreSelection()); + assert(!loadInFlight && activePatternIdx==1 && PFModuleLoader::resumes==resumesBefore+1); + assert(PFModuleLoader::isParked("/b.pfm")); + // A writer that does not empty memory first - a feature installing a + // pattern - reports the write through sidecarForgetSlug(), which moves the + // storage generation. From that moment nothing loaded before it comes back + // from memory: not the parked B, and not by an instant SELECT detent - not + // even onto a preset, because leaving A would now cost its reload. + PFModuleLoader::noteStorageWrite(); + assert(!PFModuleLoader::isParked("/b.pfm") && !isResidentOrPreset(2)); + assert(!switchesInstantly(2) && !switchesInstantly(0)); + notified=false; + assert(activatePatternAsync(2) && loadInFlight && notified && loadTargetIdx==2); + assert(PFModuleLoader::table.count==0); // A unloaded, stale B evicted + loadPatternJob(); serviceAsyncLoad(); + assert(activePatternIdx==2); + // And the rebuild the write asks for does not leave the old code running + // under the new file's name, even when the restore lands on the pattern + // that is already on the panel: it is unloaded, and loaded from the file. + assert(activatePatternAsync(1)); loadPatternJob(); serviceAsyncLoad(); + assert(activePatternIdx==1 && PFModuleLoader::isParked("/b.pfm")); + PFModuleLoader::noteStorageWrite(); + const unsigned unloadsBefore=PFModuleLoader::unloads, loadsBefore=PFModuleLoader::loads; + assert(Manager::restoreSelection()); // restorePath is still A's + assert(PFModuleLoader::table.count==0 && PFModuleLoader::unloads==unloadsBefore+1); + assert(loadInFlight && loadTargetIdx==1 && activePatternIdx==-1); + loadPatternJob(); serviceAsyncLoad(); + assert(activePatternIdx==1 && PFModuleLoader::loads==loadsBefore+1); + // A refused load gets every parked module back before it retries. + assert(activatePatternAsync(0) && PFModuleLoader::isParked("/a.pfm")); + const unsigned dropsBefore=PFModuleLoader::drops; + loadTargetIdx=2; PFModuleLoader::allocationFailures=1; retryPauseMs=0; + loadPatternJob(); + assert(loadResult && PFModuleLoader::table.count==0 && PFModuleLoader::drops==dropsBefore+1); + PFModuleLoader::dropParked(); PFModuleLoader::unload(); + // A full table makes room by its least recently used module - never the + // one the switch is about to resume, even when that one is the oldest. + NUM_PATTERNS=4; activePatternIdx=0; + assert(activatePatternAsync(1)); loadPatternJob(); serviceAsyncLoad(); + assert(activatePatternAsync(2)); loadPatternJob(); serviceAsyncLoad(); + assert(activatePatternAsync(3)); loadPatternJob(); serviceAsyncLoad(); + assert(activePatternIdx==3 && PFModuleLoader::table.count==2); + assert(PFModuleLoader::isParked("/a.pfm") && PFModuleLoader::isParked("/b.pfm")); + resumesBefore=PFModuleLoader::resumes; + notified=false; + assert(activatePatternAsync(1) && !loadInFlight && !notified); + assert(activePatternIdx==1 && PFModuleLoader::resumes==resumesBefore+1); + assert(PFModuleLoader::isParked("/c.pfm") && !PFModuleLoader::isParked("/b.pfm")); + PFModuleLoader::dropParked(); PFModuleLoader::unload(); delete PFLoopSync::doneSignal; delete PFLoopSync::callerLock; - puts("PASS: reserve/fallback/fragmentation/race, deferred loop requests, stalled-loop hand-off, task failure, mutation hold, rescan and deleted selection"); + puts("PASS: reserve/fallback/fragmentation/race, deferred loop requests, stalled-loop hand-off, task failure, mutation hold, rescan and deleted selection, park/resume/drop around loads, storage mutations and writes nobody captured for"); } diff --git a/tools/README.md b/tools/README.md index 09f9ffff..2ba25996 100644 --- a/tools/README.md +++ b/tools/README.md @@ -4,13 +4,13 @@ Clients that Patternflow ships and that run on their own — a browser extension | Folder | What it is | Contract | | :--- | :--- | :--- | -| [`patternflow-audio-extension/`](patternflow-audio-extension/README.md) | Chrome/Edge extension: captures the current tab's audio, splits it into four bands you shape on a response graph, streams the levels to the panel | [`audio-ws-spec.md`](../docs/audio-ws-spec.md) | -| [`patternflow-audio-android/`](patternflow-audio-android/README.md) | The phone-side twin: captures what the phone is playing and drives the knobs with it, for filming | [`audio-ws-spec.md`](../docs/audio-ws-spec.md) | +| [`patternflow-audio-extension/`](patternflow-audio-extension/README.md) | Chrome/Edge extension: captures the current tab's audio, splits it into four bands, one per knob, that you shape as boxes on the live spectrum, and streams the four knob values to the panel | [`audio-ws-spec.md`](../docs/audio-ws-spec.md) | +| [`patternflow-audio-android/`](patternflow-audio-android/README.md) | The phone-side twin: captures what the phone is playing and drives the knobs with it, for filming. It takes its mapping from the panel | [`audio-ws-spec.md`](../docs/audio-ws-spec.md), and the microphone endpoints of [`rest-api.md`](../docs/rest-api.md#microphone-audio-edition) | | [`rtpmidi-probe/`](rtpmidi-probe/README.md) | A plain-Python RTP-MIDI session initiator that walks the MIDI contract against a panel from a machine with no MIDI driver | [`midi-spec.md`](../docs/midi-spec.md) | ## The extension is also firmware source -The band editor in `patternflow-audio-extension/` (`editor.html`, `editor.css`, `editor.js`, `editor-adapter.js`) is the **authoring source of the device's own `/audio-in` console page**: `firmware/toolchain/build_audio_in_page.py` assembles `firmware/patternflow/console/audio-in.html` from those files, and the `console-sync` workflow fails if the two drift. Edit the editor here, never the generated page, and do not move this folder — the build script and the workflow hold its path. +The mapping editor in `patternflow-audio-extension/` is the **authoring source of the device's own `/audio-in` console page**. Four of its files travel to the panel: `editor.html` (the editor's body), `editor.css` and `mapping.js` (each down to its `EXTENSION ONLY` line) and `editor.js`. `editor-adapter.js` does not: the panel has its own adapter, frame and styles beside the other console sources (`firmware/patternflow/console/_audio_in_*`). `python firmware/toolchain/console_pages.py build` assembles `firmware/patternflow/console/audio-in.html` from both halves (the step is `firmware/toolchain/build_audio_in_page.py`) and bakes the header the panel serves; `python firmware/toolchain/console_serve.py` previews the page from the sources without building. The `console-sync` workflow fails when the generated page is not what its sources assemble to, or is over its size budget. Edit the editor here, never the generated page, and do not move this folder — the build script and the workflow hold its path. ## License diff --git a/tools/patternflow-audio-extension/README.md b/tools/patternflow-audio-extension/README.md index 92265107..664875e0 100644 --- a/tools/patternflow-audio-extension/README.md +++ b/tools/patternflow-audio-extension/README.md @@ -1,71 +1,194 @@ # Patternflow Audio Chrome Extension -Captures audio from the currently active Chrome/Edge tab, shows a live -spectrum, maps editable frequency bands to Patternflow knobs, and streams -the four knob lanes over WebSocket. The wire protocol is a written contract: +Captures the audio of a Chrome or Edge tab, splits it into four frequency +bands, and sends each band to one of the panel's four knobs over your local +network. The panel has to run the **Audio edition** +([patternflow.work/editions](https://patternflow.work/editions)). +[`AUDIO_GUIDE.md`](../../AUDIO_GUIDE.md) is the guide to everything around it; +the wire protocol is a written contract, [`docs/audio-ws-spec.md`](../../docs/audio-ws-spec.md). -The extension uses the same Patternflow mark as the web app for its toolbar -icon and popup header. +## Install + +It is not on the Chrome Web Store. Load it from this folder: + +1. Get the repository: `git clone`, or **Code → Download ZIP** on GitHub and + unzip it. +2. Open `chrome://extensions` (in Edge, `edge://extensions`). +3. Enable **Developer mode**. +4. Click **Load unpacked** and pick this folder, + `tools/patternflow-audio-extension`. + +**After pulling a newer copy, press Reload** on the extension's card in +`chrome://extensions`. Chrome goes on running the files it loaded until you +do. The card shows the version (`version` in `manifest.json`), which is how to +tell the copy that is loaded from the one on disk. + +**Reload keeps the mapping you had.** A copy before 0.2.0 started with Auto +range off and no curve, and unless you changed them its mapping still says so +after Reload: the Auto range switch is off, the four boxes are full height, +and under the curves it reads "This knob uses the default response." The new +defaults (Auto range on, the Smooth curve) are what a new install starts +with. To get them on a copy you already had, do one of: + +- in the mapping editor, select each knob in turn and press its Reset + (**Reset K1** to **Reset K4**), then switch **Auto range** on. Reset also + puts that knob's box and output range back to the default. The panel's + address is kept; +- **Remove** the extension on `chrome://extensions` and load it again from + this folder. That empties everything it stored, the panel's address + included. + +## Use + +1. Go to the tab that is playing: YouTube, SoundCloud, a DJ set, a web DAW. +2. Click the Patternflow Audio button in the toolbar, on that tab. +3. Under **Device**, type the panel's address: `patternflow.local`, or its IP + (the panel's NETWORK screen shows it). +4. Press **Start**. The button now reads **Stop**. + +The word in the popup's corner is the state, and the line under the address +says the rest: + +| | | +|---|---| +| **Idle** | Nothing is captured. | +| **Connecting** | The connection is not open yet, or the panel has not yet said whether it is listening. | +| **Live** | The tab is captured and the panel is being driven. The line reads `Capturing: `, or "No sound from this tab yet." / "... right now." while the tab is silent. | +| **Aud off** | Connected, but Audio-React is switched off on the panel, which then ignores what arrives. Press **Turn on**, which appears under the address. It is the same switch as `AUD` on the panel's NETWORK screen and *Browser extension and phone app* on the console's Audio page. | +| **Test** | The test connection (below) is up. | +| **Error** | The line says what: nothing at that address, a panel that does not run the Audio edition, a page Chrome does not let be captured. A connection that fails is retried by itself. | + +**While the tab is silent or paused the knobs are held**, each where its band +rests, until you press Stop. A knob you turn by hand during a pause goes back +there about five seconds after you let go. Stop hands all four back to the +encoders. Closing the captured tab ends the capture, and the popup says so. + +Enter in the address field starts a capture when nothing is running. While +one is running it only changes the address, and the capture follows it; it +never stops one. + +**Spectrum** is what the tab sounds like, 20 Hz to 20 kHz. + +**Test without audio** (click it open) connects without capturing anything: +**Connect**, then move K1 to K4. Each knob stays where its slider was put +until **Disconnect**. It is how to tell a panel that does not react from a +tab that is not being heard. ## The mapping editor -**Editor ↗** in the popup opens the full editor in its own tab -(`editor.html`): every band is a box drawn on the live spectrum — its width -is the frequencies it listens to, its height the level window it maps — and -a response curve turns position-in-window into knob position. Curves come as -presets (rise, fall, gate, steps, arch) or hand-dragged beziers, and every -shape bakes to a 33-point table the analysis side just interpolates — the -same table the device firmware will consume, which is the point: the editor -module (`editor.js`) is adapter-isolated (`editor-adapter.js`) and ports to -the device console by swapping the adapter only. Auto range makes the boxes -breathe with the room; grabbing a breathing edge takes manual control. -Opened outside the extension (plain http), it runs a synthesized demo -source — the fast way to iterate on the editor itself. - -## Install for local testing - -1. Open `chrome://extensions`. -2. Enable **Developer mode**. -3. Click **Load unpacked**. -4. Select this folder: `tools/patternflow-audio-extension`. -5. Open a tab that is playing audio. -6. Click the Patternflow Audio extension button. -7. Set the device host to `patternflow.local` or the board IP address. -8. Press **Start**. -9. Ensure **Audio-React (AUD)** is switched ON on the device (at the top of the console's `/audio-in` page or via the panel's NETWORK screen). - -Use the extension for YouTube, Spotify Web, browser games, or any other tab -or system audio. The built-in `http://patternflow.local/` page remains useful -for microphone input and audio-file experiments, but browser tab/system capture -is often blocked there because the ESP32 serves normal HTTP. - -For WebSocket-only debugging, press **WS Test** instead of **Start** and move -the K1-K4 sliders. This bypasses tab capture and audio analysis completely. - -## Controls - -- `Spectrum`: live frequency strength display. Drag across it to set the active band's range. -- `Add Band`: starts with one band, then adds more bands as needed. -- `Output`: min/max Patternflow knob output from 0.0 to 1.0. -- `Clamp / gain`: optional input low/high clamp and sensitivity. -- `K`: target Patternflow knob index. -- `Smoothing`: global response smoothing. -- `WS Test`: connect to the device and send manual K1-K4 movements. - -The extension sends delta messages like `d=0,v=0.125` to `ws://:81`. +**Mapping editor ↗** opens the editor in a tab of its own. From the top: + +- **Auto range**, **Attack** and **Damping** hold for all four knobs. With + Auto range on, each box follows the loudness of what it hears; with it off, + each has the window you set by hand. Attack is how fast a knob rises to a + hit, Damping how slowly it falls back. +- **The plot** is the live spectrum with four boxes on it, K1 to K4. A box's + width is the frequencies that knob listens to; its height is the + quiet-to-loud range mapped onto it, in dB. Drag a box to move it and an edge + to resize it. The line across a box is where the level sits in it now. +- **K1 to K4**, the tabs under the plot, select a knob. K1 is always knob 1. +- **Preview**, at the end of that row, puts a scope where the plot is: a test + signal (kick pulses, a noisy room, a slow swell) run through the selected + knob's attack, damping, curve and output range, which all stay adjustable + around it. The same button closes it, and so does Esc. +- **The knob's card**: its **response curve** (Smooth, Sharp, Fall, Gate, + Steps, Arch, or drag the two handles for a shape of your own), its **output + range** (how far the knob travels) and **Mute** (the knob goes back to its + encoder). +- **Reset K1** puts the selected knob back to its default. + +Every edit saves itself and reaches a running capture at once. A new install +starts with Auto range on and the Smooth curve on all four knobs. Switching +Auto range off goes back to the windows you set by hand; dragging a top or +bottom edge while it is on switches it off and starts all four windows from +where the boxes are at that moment. + +The mapping is kept in this browser (`chrome.storage`), not on the panel. The +panel's own Audio page has the same editor for its on-board microphone, with a +mapping of its own that is stored on the panel. + +The bar at the top says what is feeding the editor, in the popup's words, and +where it is going, and has a Stop. Opened outside the extension the editor +runs on a synthesized demo source, which is the fast way to work on the +editor itself: + + python -m http.server 8000 --bind 127.0.0.1 --directory tools/patternflow-audio-extension + # http://127.0.0.1:8000/editor.html + +Nothing is captured there and nothing is kept. + +## What it sends + +To `ws://
              :81` (a port typed with the address is used instead), one +text message per frame: + + a=0.412,0.300,-,0.871 + +Four knob values from 0 to 1, with a `-` for a muted knob. The analysis runs +30 times a second. A frame that has not changed is sent again about every +210 ms, because the panel hands a knob back to its encoder 500 ms after the +last message that set it. Muting a knob sends `off=N` once, and Stop sends +`off`. The test sliders use the same `a=` message. Measured against a loopback +socket: 23–26 messages a second on music, 5 a second on silence. + +It also asks the panel three things over HTTP, on port 80 of the same address. +Each is asked once, never polled, because the panel serves one connection at +a time: + +- `GET /api/audio` when the socket opens, and once more when the popup is + opened while it reads *Aud off*: is Audio-React on? +- `POST /api/audio` with `on=1`: the **Turn on** button. +- `GET /api/status` once when the socket will not open: is anything there, + and does it run the Audio edition? + +Builds before 0.2.0 sent the test sliders as `k=N,v=F`, and the first ones +sent `d=N,v=F` deltas. The firmware still takes both. + +## The files + +| File | What it is | +|---|---| +| `manifest.json` | The extension itself. Permissions: `tabCapture`, `offscreen`, `storage`, `activeTab`, and `http://*/*` with `ws://*/*` to reach a panel on the local network. | +| `popup.html` `.css` `.js` | The capture console. It owns the panel's address (`patternflowAudioHost` in `chrome.storage.local`) and asks Chrome for the tab's stream. | +| `background.js` | The service worker: creates the offscreen document and relays messages between it and the pages. | +| `offscreen.html` `.js` | Holds the capture, runs the analysis and the mapping, owns the socket, and is the one place that knows the state every page prints (`phase`). | +| `mapping.js` | The mapping model, one object, `PFMap`: the defaults, what a stored mapping is turned into, the curves, and the chain from a level to a knob value. The mapping is stored under `patternflowAudioConfig`. | +| `editor.html` `.css` `.js` | The mapping editor. `editor.js` reaches its source only through `window.PFAdapter`; the contract is the comment at its top. | +| `editor-adapter.js` | `PFAdapter` for the extension (storage, the capture's frames), the editor's source bar, and the demo source. | +| `icons/` | The Patternflow mark, as the web app has it. | ## This folder is also firmware source -The editor (`editor.html`, `editor.css`, `editor.js`, `editor-adapter.js`) is -the authoring source of the panel's own `/audio-in` console page: -`firmware/toolchain/build_audio_in_page.py` assembles -`firmware/patternflow/console/audio-in.html` from these files, and the -`console-sync` workflow fails a pull request in which the two have drifted. -Edit the editor here and rebuild the page; never edit the generated page, and -do not move this folder. +The panel's own `/audio-in` console page is this editor. +`firmware/toolchain/build_audio_in_page.py` assembles that page from two +places: + +- here: `editor.html` (what is between its two `EDITOR BODY` lines), + `editor.css` and `mapping.js` (what is above their `EXTENSION ONLY` line) + and all of `editor.js`; +- the panel's half, in `firmware/patternflow/console/`: `_audio_in_bar.html` + (the page around the editor), `_audio_in.css` (the console's look) and + `_audio_in_adapter.js` (`PFAdapter` over `/api/audio-in`). + +So an edit to one of those four files here changes the panel's page as well. +After one: + + python firmware/toolchain/console_serve.py # preview: /audio-in is assembled from the sources on every request + python firmware/toolchain/console_pages.py build # regenerate the page and the header the panel serves + +Never edit the generated `firmware/patternflow/console/audio-in.html` or +`firmware/patternflow/features/audio_in/audio_in_index.h`, and do not move +this folder: the builder and the `console-sync` workflow hold its path. CI +fails a pull request whose generated page is not what its sources assemble +to, or whose page is over its size budget (17.5 KB gzipped, as the panel sends +it). Every byte of the shared half crosses the panel's Wi-Fi, so what only +the extension needs goes below the `EXTENSION ONLY` lines or into +`editor-adapter.js`. That includes the cream skin: above its line +`editor.css` is layout, and each surface brings its own colours. The rest is +in [`firmware/patternflow/console/README.md`](../../firmware/patternflow/console/README.md#the-page-that-is-assembled-audio-in). -The larger controller ideas that used to be listed here as "next" — spectrum +The larger controller ideas that used to be listed here as "next" (spectrum and waveform views, drag-to-select bands, per-band curves, gates and -smoothing — shipped as the mapping editor above; what remains open is tracked +smoothing) shipped as the mapping editor above. What remains open is tracked in the repository's issues. diff --git a/tools/patternflow-audio-extension/background.js b/tools/patternflow-audio-extension/background.js index 9d3f303f..653eadb0 100644 --- a/tools/patternflow-audio-extension/background.js +++ b/tools/patternflow-audio-extension/background.js @@ -1,26 +1,49 @@ +// Patternflow Audio — service worker: a relay. +// +// The popup and the editor talk to this; the offscreen document does the work +// and owns the truth about it (offscreen.js: is a capture running, to which +// address, did the socket open, why did it end). Chrome puts this worker to +// sleep when it likes and wakes it with empty memory, so nothing is kept here +// that cannot be asked for again. `state` is a copy of the offscreen +// document's last word, there so a `status` request is answered without a +// round trip; after a wake it is fetched afresh before anyone is told it. + const OFFSCREEN_URL = 'offscreen.html'; -let state = { +const IDLE = { running: false, - connected: false, manual: false, + connected: false, + phase: 'idle', + host: '', tabTitle: '', - host: 'patternflow.local', error: '', + note: '', + panelAudio: null, + heard: false, + silent: false, + held: [], levels: [], + pos: [], outputs: [], - spectrum: [] + spectrum: [], + env: [], + autoRange: false }; -async function ensureOffscreen() { - const offscreenUrl = chrome.runtime.getURL(OFFSCREEN_URL); +let state = { ...IDLE }; +let heard = false; // from the offscreen document, since this worker last woke + +async function offscreenExists() { const contexts = await chrome.runtime.getContexts({ contextTypes: ['OFFSCREEN_DOCUMENT'], - documentUrls: [offscreenUrl] + documentUrls: [chrome.runtime.getURL(OFFSCREEN_URL)] }); + return contexts.length > 0; +} - if (contexts.length > 0) return; - +async function ensureOffscreen() { + if (await offscreenExists()) return; await chrome.offscreen.createDocument({ url: OFFSCREEN_URL, reasons: ['USER_MEDIA'], @@ -28,109 +51,76 @@ async function ensureOffscreen() { }); } +function toOffscreen(message) { + return chrome.runtime.sendMessage({ ...message, target: 'offscreen' }); +} + +function take(patch) { + if (!patch) return; + state = { ...state, ...patch }; + heard = true; +} + +// Make `state` worth telling. With no offscreen document nothing is running, +// whatever the copy says; after a wake the copy is empty and the document is +// asked. +async function refresh() { + if (!(await offscreenExists())) { + state = { ...IDLE }; + heard = true; + return; + } + if (heard) return; + const reply = await toOffscreen({ type: 'status' }).catch(() => null); + take(reply && reply.state); +} + function broadcast() { chrome.runtime.sendMessage({ type: 'state', state }).catch(() => {}); } +// What the popup and the editor may ask the offscreen document to do. The +// message goes through as it came. +const COMMANDS = ['start', 'manual-connect', 'host', 'config', 'manual-value', 'panel-on', 'panel-check', 'stop']; + chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { (async () => { - if (message.type === 'start') { - await ensureOffscreen(); - state = { - ...state, - running: true, - connected: false, - manual: false, - tabTitle: message.tabTitle || '', - host: message.config.host, - error: '' - }; - await chrome.runtime.sendMessage({ - target: 'offscreen', - type: 'start', - streamId: message.streamId, - config: message.config - }); - broadcast(); - sendResponse({ ok: true, state }); - return; - } - - if (message.type === 'manual-connect') { - await ensureOffscreen(); - state = { - ...state, - running: false, - connected: false, - manual: true, - tabTitle: '', - host: message.config.host, - error: '' - }; - await chrome.runtime.sendMessage({ - target: 'offscreen', - type: 'manual-connect', - config: message.config - }); - broadcast(); - sendResponse({ ok: true, state }); - return; - } - - if (message.type === 'manual-value') { - await chrome.runtime.sendMessage({ - target: 'offscreen', - type: 'manual-value', - knob: message.knob, - value: message.value - }).catch(() => {}); - sendResponse({ ok: true, state }); - return; - } - - if (message.type === 'stop') { - await chrome.runtime.sendMessage({ target: 'offscreen', type: 'stop' }).catch(() => {}); - state = { ...state, running: false, connected: false, manual: false, error: '' }; + if (message.type === 'offscreen-state') { + take(message.patch); broadcast(); - sendResponse({ ok: true, state }); + sendResponse({ ok: true }); return; } - if (message.type === 'config') { - state = { ...state, host: message.config.host }; - await chrome.runtime.sendMessage({ - target: 'offscreen', - type: 'config', - config: message.config - }).catch(() => {}); - broadcast(); + if (message.type === 'status') { + await refresh(); sendResponse({ ok: true, state }); return; } - if (message.type === 'release') { - await chrome.runtime.sendMessage({ target: 'offscreen', type: 'release' }).catch(() => {}); - sendResponse({ ok: true, state }); + if (!COMMANDS.includes(message.type)) { + sendResponse({ ok: false, error: 'Unknown message' }); return; } - if (message.type === 'status') { + // Only a start makes the document. Anything else with no document has + // nothing to act on: a mapping saved in the editor while nothing runs, a + // Stop pressed twice. + if (message.type === 'start' || message.type === 'manual-connect') { + await ensureOffscreen(); + } else if (!(await offscreenExists())) { + await refresh(); sendResponse({ ok: true, state }); return; } - if (message.type === 'offscreen-state') { - state = { ...state, ...message.patch }; - broadcast(); - sendResponse({ ok: true }); - return; - } - - sendResponse({ ok: false, error: 'Unknown message' }); - })().catch((error) => { - state = { ...state, running: false, connected: false, error: String(error.message || error) }; - broadcast(); - sendResponse({ ok: false, error: state.error, state }); + const reply = await toOffscreen(message); + if (!reply) throw new Error('The capture page did not answer.'); + take(reply.state); + if (message.type !== 'manual-value') broadcast(); + sendResponse({ ok: reply.ok !== false, error: reply.error, state }); + })().catch((failure) => { + sendResponse({ ok: false, error: String(failure.message || failure), state }); }); return true; diff --git a/tools/patternflow-audio-extension/editor-adapter.js b/tools/patternflow-audio-extension/editor-adapter.js index 4db7562c..32cce65f 100644 --- a/tools/patternflow-audio-extension/editor-adapter.js +++ b/tools/patternflow-audio-extension/editor-adapter.js @@ -1,68 +1,141 @@ // Patternflow Audio — editor adapter, extension flavor. // -// The editor module (editor.js) knows nothing about Chrome, the device, or -// where audio comes from: it talks to exactly this surface. Porting the -// editor to the device console means swapping THIS file for one that speaks -// fetch('/api/audio-in') — the module itself moves verbatim. That boundary is -// the whole architecture; do not let editor.js grow a chrome.* call. +// The editor module (editor.js) knows nothing about Chrome, the panel, or +// where audio comes from: it talks to exactly window.PFAdapter. The panel's +// /audio-in page is the same editor over another adapter +// (firmware/patternflow/console/_audio_in_adapter.js, which speaks +// fetch('/api/audio-in')). That boundary is the whole architecture; do not +// let editor.js grow a chrome.* call. The contract both adapters keep is +// written at the top of editor.js. // -// caps() -> { hzMin, hzMax } the source can actually analyze -// loadConfig() -> the shared config (host, autoRange, bands[4]) -// saveConfig(config) -> persist + hand to the live analysis -// onFrame(fn) -> fn({running, connected, levels, env, spectrum, ...}) -// requestStatus() -> ask for one immediate state push -// stop() -> stop capture +// This file is also the extension's SOURCE BAR: the
              in editor.html +// (what is feeding the editor, where it is going, Stop). The editor never +// looks in there; every state that arrives is drawn here first and then +// handed on. // -// Without chrome.* (opened as a plain file) it runs a demo: synthesized -// music-ish spectrum, config in memory. That is a preview of the editor, not -// of the product — nothing persists and nothing is captured. +// Without chrome.* (opened as a plain file, or from a static server) it runs +// a demo: a synthesized music-ish spectrum mapped by the same model, the +// mapping kept in memory. That is a preview of the editor, not of the +// product: nothing persists and nothing is captured. (function () { const hasChrome = typeof chrome !== 'undefined' && chrome.storage && chrome.runtime; + const $ = (id) => document.getElementById(id); + + // What this source is: tab audio through an AnalyserNode. The numbers are + // the model's (PFMap.SOURCE), the two edge rules are its minimum window, and + // a band is never narrower than the 10 Hz the model keeps on load. + const CAPS = { + hzMin: PFMap.SOURCE.hzMin, + hzMax: PFMap.SOURCE.hzMax, + db: PFMap.SOURCE.db, + levelMax: 1, + hzGap: 10, + top: (lo) => lo + PFMap.MIN_WINDOW, + bottom: (hi) => hi - PFMap.MIN_WINDOW + }; + + // ── the source bar ──────────────────────────────────────────────────── + // + // The chip is the capture's own phase (offscreen.js phase()), in the same + // word the popup prints for it, so the two cannot disagree about whether + // the panel is being driven: a socket that is open to a panel whose + // Audio-React switch is off is not "live". Beside it, one sentence when + // there is something to do or something wrong: + // error the capture's own sentence, and both it and the chip in red + // aud off what is off and where its switch is (Turn on is the popup's) + // idle why the last capture ended, if it ended by itself (the tab + // was closed), then how to start one + // otherwise nothing: a capture or the popup's test connection is up, + // and "start a capture" is not what to say beside it + const WORDS = { connecting: 'connecting', off: 'aud off', live: 'live', test: 'test', error: 'error' }; + + function drawBar(state, idleHint) { + const chip = $('sourceChip'); + const hint = $('captureHint'); + const bad = state.phase === 'error'; + chip.textContent = state.demo ? 'demo source' : WORDS[state.phase] || 'idle'; + chip.className = state.phase === 'live' ? 'chip okChip' : bad ? 'chip badChip' : 'chip'; + // Where the capture is being sent, as the socket has it. Nothing is + // connected: no chip, rather than an address nothing is using. + $('hostChip').textContent = state.host || ''; + $('hostChip').hidden = !state.host; + hint.textContent = state.demo ? '' + : state.phase === 'off' ? 'Connected, but Audio-React is off on the panel. Turn it on in the extension popup.' + : state.error || (state.running || state.manual ? '' : (state.note ? state.note + ' ' : '') + idleHint); + hint.classList.toggle('badText', bad); + $('stopBtn').disabled = !state.running || !!state.demo; + } + + // An envelope that is at its minimum span has learned nothing: the band has + // been silent, or has sat at one level, for as long as an envelope + // remembers. The editor is told there is none (see `env` in its contract), + // so it draws the band's own window, and a drag that takes the four windows + // over by hand leaves that one as it was. Seeded from such an envelope it + // became a 4 dB sliver at the floor, and the first sound pegged that knob at + // its top. (A band that has heard a single click does have an envelope, and + // is seeded from it: this guards silence, not thinness.) + const learned = (env) => (env || []).map((e) => + e && e.hi - e.lo > PFMap.ENV_MIN_SPAN + 1e-6 ? e : null); if (hasChrome) { + const HINT = 'Start capture from the extension popup, on the tab that is playing.'; let frameFn = null; + + const take = (state) => { + state = state || {}; + drawBar(state, HINT); + if (frameFn) frameFn({ ...state, env: learned(state.env) }); + }; + const ask = () => chrome.runtime.sendMessage({ type: 'status' }, (response) => { + void chrome.runtime.lastError; + if (response && response.state) take(response.state); + }); + chrome.runtime.onMessage.addListener((message) => { - if (message.type === 'state' && frameFn) frameFn(message.state || {}); + if (message.type === 'state') take(message.state); + }); + $('stopBtn').addEventListener('click', () => { + chrome.runtime.sendMessage({ type: 'stop' }, () => void chrome.runtime.lastError); }); window.PFAdapter = { caps() { - return { hzMin: 20, hzMax: 20000 }; + return CAPS; }, loadConfig() { return new Promise((resolve) => - chrome.storage.local.get('patternflowAudioConfig', (stored) => - resolve(stored.patternflowAudioConfig || null))); + chrome.storage.local.get(PFMap.CONFIG_KEY, (stored) => + resolve(stored[PFMap.CONFIG_KEY] || null))); }, + // Stored, then told to the capture: that message is how a running + // capture gets the edit (nothing listens to storage). saveConfig(config) { return new Promise((resolve) => - chrome.storage.local.set({ patternflowAudioConfig: config }, () => { + chrome.storage.local.set({ [PFMap.CONFIG_KEY]: config }, () => { chrome.runtime.sendMessage({ type: 'config', config }, () => { void chrome.runtime.lastError; // background may be asleep; storage holds resolve(); }); })); }, + resetBand(index, config) { + config.bands[index] = PFMap.defaultBand(index, CAPS); + return this.saveConfig(config).then(() => config); + }, + // The editor is up: one state now, without waiting for the next push. onFrame(fn) { frameFn = fn; - }, - requestStatus() { - chrome.runtime.sendMessage({ type: 'status' }, (response) => { - void chrome.runtime.lastError; - if (response && response.state && frameFn) frameFn(response.state); - }); - }, - stop() { - chrome.runtime.sendMessage({ type: 'stop' }, () => void chrome.runtime.lastError); - }, - captureHint: 'Start capture from the extension popup, on the tab that is playing.' + ask(); + } }; + ask(); return; } // ── demo adapter ────────────────────────────────────────────────────── - let demoConfig = null; + const HINT = 'Demo mode — open this page from the extension for live audio.'; + let demoConfig = PFMap.normalizeConfig(null, CAPS); let frameFn = null; let t = 0; @@ -86,42 +159,45 @@ return values; } - const envLo = [1, 1, 1, 1]; - const envHi = [0, 0, 0, 0]; + // The same envelope and the same level-to-knob chain the capture runs + // (offscreen.js), so the demo's cursor and readout are the model's too. + const envelopes = [0, 1, 2, 3].map(() => PFMap.newEnvelope()); function demoFrame() { - if (!frameFn) return; const spectrum = demoSpectrum(); - const bands = (demoConfig && demoConfig.bands) || []; - const levels = bands.map((band) => { + const auto = demoConfig.autoRange; + const levels = [], pos = [], outputs = []; + demoConfig.bands.forEach((band, i) => { const lo = Math.log10(band.hzMin / 20) / 3 * 64; const hi = Math.log10(band.hzMax / 20) / 3 * 64; let sum = 0, n = 0; - for (let i = Math.max(0, Math.floor(lo)); i <= Math.min(63, Math.ceil(hi)); i++) { sum += spectrum[i]; n++; } - return n ? sum / n : 0; - }); - levels.forEach((v, i) => { - if (v > envHi[i]) envHi[i] = v; else envHi[i] += (v - envHi[i]) * 0.01; - if (v < envLo[i]) envLo[i] = v; else envLo[i] += (v - envLo[i]) * 0.01; - if (envHi[i] < envLo[i] + 0.06) envHi[i] = envLo[i] + 0.06; + for (let k = Math.max(0, Math.floor(lo)); k <= Math.min(63, Math.ceil(hi)); k++) { sum += spectrum[k]; n++; } + levels[i] = n ? sum / n : 0; + PFMap.trackEnvelope(envelopes[i], levels[i], 0.01); + pos[i] = PFMap.position(levels[i], band, auto ? envelopes[i] : null); + outputs[i] = PFMap.output(band, pos[i]); }); - frameFn({ + const state = { running: true, connected: false, demo: true, - levels, outputs: [], - spectrum, - env: demoConfig && demoConfig.autoRange ? envLo.map((lo, i) => ({ lo, hi: envHi[i] })) : [], - autoRange: !!(demoConfig && demoConfig.autoRange) - }); + levels, pos, outputs, spectrum, + env: auto ? learned(envelopes.map((e) => ({ lo: e.lo, hi: e.hi }))) : [], + autoRange: auto + }; + drawBar(state, HINT); + if (frameFn) frameFn(state); } window.PFAdapter = { - caps() { return { hzMin: 20, hzMax: 20000 }; }, - loadConfig() { return Promise.resolve(demoConfig); }, + caps() { return CAPS; }, + // Nothing is stored: the editor starts as a first run does. + loadConfig() { return Promise.resolve(null); }, saveConfig(config) { demoConfig = config; return Promise.resolve(); }, - onFrame(fn) { frameFn = fn; setInterval(demoFrame, 120); }, - requestStatus() { demoFrame(); }, - stop() {}, - captureHint: 'Demo mode — open this page from the extension for live audio.', - _setDemoConfig(config) { demoConfig = config; } + resetBand(index, config) { + config.bands[index] = PFMap.defaultBand(index, CAPS); + demoConfig = config; + return Promise.resolve(config); + }, + onFrame(fn) { frameFn = fn; demoFrame(); setInterval(demoFrame, 120); } }; + drawBar({ demo: true }, HINT); })(); diff --git a/tools/patternflow-audio-extension/editor.css b/tools/patternflow-audio-extension/editor.css index 8aa3e9fe..da75d6c4 100644 --- a/tools/patternflow-audio-extension/editor.css +++ b/tools/patternflow-audio-extension/editor.css @@ -1,27 +1,25 @@ -/* Patternflow Audio — mapping editor. Same cream instrument palette as the - popup (popup.css is the source of these values). */ -:root { - --cream: #f4efe6; - --cream-2: #ede7db; - --ink: #141414; - --muted: #6b655a; - --faint: #a69f90; - --rule: #d9d1c0; - --led: #e8552e; - --ok: #267a43; - --bad: #b43b2d; - --field: #fbf7ef; - --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; -} +/* Patternflow Audio — mapping editor. -* { box-sizing: border-box; } + Two parts. Down to the line that says EXTENSION ONLY these are the editor's + own rules, the same on every surface: the panel's /audio-in page takes + exactly that part (firmware/toolchain/build_audio_in_page.py stops reading + at the line) and brings its own colours and its own bar. Below the line is + what only the extension's page has: the cream instrument palette and the + source bar. Every colour above the line is a variable, so that works. -body { - margin: 0; - background: var(--cream); - color: var(--ink); - font: 12px/1.45 Inter, ui-sans-serif, system-ui, sans-serif; -} + Three things the part above the line leaves to the surface, because a + console page has them already and would carry them twice: the page itself + (body: margin, background, type), --mono, and that the hidden attribute + hides ([hidden] { display: none !important }: three of the editor's parts + are flex boxes that come and go by that attribute, and a display of their + own would otherwise keep them on screen). + + The layout rules are here too, and not in either surface's own sheet: the + editor is used in a full tab, in half a screen beside the tab that is + playing, and on a phone, and it has to hold in all three wherever it is + shown. Three steps: everything in a row; at 1100 px the output range drops + under the curve; at 700 px each row of controls becomes a column. */ +* { box-sizing: border-box; } .page { max-width: 1280px; @@ -34,89 +32,143 @@ body { .mono { font-family: var(--mono); } .dimText { color: var(--muted); } -.hint { font-size: 11px; color: var(--faint); } +.hint { font-size: 12px; color: var(--muted); } +.hint:empty { display: none; } .spacer { flex-grow: 1; } -/* ── header ── */ -header { display: flex; align-items: center; justify-content: space-between; gap: 12px; } -.brand { display: flex; align-items: center; gap: 9px; } -.led { width: 8px; height: 8px; background: var(--led); border-radius: 1px; } -.title { font-size: 14px; font-weight: 600; } -.kicker { font-family: var(--mono); font-size: 10px; letter-spacing: 0.09em; color: var(--muted); } -.cluster { display: flex; align-items: center; gap: 8px; } - -.chip { - border: 1px solid var(--rule); border-radius: 2px; background: var(--field); - padding: 5px 10px; font-family: var(--mono); font-size: 11px; color: var(--ink); -} -.chip.dim { color: var(--muted); } -.okChip { border-color: var(--ok); color: var(--ok); } - -.primaryBtn { - font: inherit; font-size: 12px; cursor: pointer; - border: 1px solid var(--ink); border-radius: 2px; - background: var(--ink); color: var(--cream); padding: 6px 14px; -} -.primaryBtn:disabled { opacity: 0.35; cursor: default; } .ghostBtn { font: inherit; font-size: 11px; cursor: pointer; border: 1px solid var(--rule); border-radius: 2px; background: var(--field); color: var(--muted); padding: 6px 12px; } -.ghostBtn:hover { border-color: var(--led); color: var(--led); } +.ghostBtn[aria-pressed=true] { border-color: var(--led); color: var(--ink); } +.ghostBtn.dan { color: var(--bad); } /* ── toolbar / toggles ── */ -.toolbar { display: flex; align-items: center; gap: 10px; } +/* The hint belongs to the switch and takes what the row has left; the two + glide sliders stay together and drop to a line of their own before the + hint is squeezed. */ +.toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; } +#autoHint { flex: 1 1 22em; } +.glide { display: flex; flex-wrap: wrap; gap: 10px 18px; } .damping { display: flex; align-items: center; gap: 8px; } .damping input { width: 130px; accent-color: var(--led); } -.damping .mono { font-size: 11px; min-width: 56px; text-align: right; } +.damping .mono { font-size: 11px; min-width: 56px; } .toggleWrap { - font: inherit; cursor: pointer; display: flex; align-items: center; gap: 7px; + font: inherit; color: inherit; cursor: pointer; display: flex; align-items: center; gap: 7px; border: 1px solid var(--ink); border-radius: 2px; background: var(--field); padding: 5px 11px; } .toggleLabel { font-size: 11px; font-weight: 500; } .toggle { width: 22px; height: 12px; background: var(--rule); border-radius: 7px; - position: relative; display: inline-block; border: 0; cursor: pointer; padding: 0; - flex: none; + position: relative; flex: none; pointer-events: none; } -.toggle .dotK, .toggleWrap .dotK { +.dotK { position: absolute; left: 1px; top: 1px; width: 10px; height: 10px; background: var(--field); border-radius: 50%; transition: left 0.12s; } -.toggleWrap .toggle { pointer-events: none; } .toggleWrap.on .toggle { background: var(--ok); } .toggleWrap.on .dotK { left: 11px; } -.toggle.small.on { background: var(--ok); } -.toggle.small.on .dotK { left: 11px; } +/* Mute is the same switch as Auto range, the whole button its target; in its + column it is as wide as its words, not as the column. */ +#muteToggle { align-self: flex-start; } /* ── main plot ── */ +/* Clipped: the canvas, and the Preview layer that lies exactly over it, are + square, and a surface may round this box's corners; and a tag has no place + of its own until editor.js gives it one (until then it is below the canvas, + out of the box). Isolated: what is stacked in here (the selected tag over + its neighbours, Preview over them all) is stacked against nothing outside + it. */ .plotWrap { - position: relative; background: var(--field); - border: 1px solid var(--rule); border-radius: 2px; + position: relative; isolation: isolate; background: var(--field); + border: 1px solid var(--rule); border-radius: 2px; overflow: hidden; } -#plot { display: block; cursor: default; touch-action: none; } +/* No touch-action here or on #curve, on purpose. A finger that lands on a box + or a handle drags it and one that misses scrolls the page, and that is + decided per touch, by editor.js, in a touchstart listener. touch-action is + per element: `none` trapped every swipe that began on the plot, and `pan-y` + would hand the browser the vertical drag of a box's top or bottom edge. + + EVERY CANVAS HAS ITS BOX HERE, not from editor.js: the plot, the curve + (#curve) and the three kinds of glyph. A canvas is 300 x 150 until somebody + says otherwise, and editor.js says nothing until the mapping has been read, + which on the panel's own hotspot is seconds after the first paint: sized + there, the page came up with a 150 px plot and 131 px tabs and then jumped. + The plot is as wide as the page and 45% of the window tall, between 240 and + 400 px, so the plot, the knobs and the top of the card share one screen (a + phone gets 260 at the most: see the 700 px step). editor.js only makes the + pixels for the box it finds. */ +#plot { display: block; cursor: default; width: 100%; height: clamp(240px, 45vh, 400px); } +.tag canvas, .chipCard canvas { width: 18px; height: 12px; } .tag { position: absolute; display: flex; align-items: center; gap: 6px; background: var(--cream-2); border: 1px solid var(--rule); border-radius: 2px; - padding: 2px 7px; pointer-events: none; + padding: 2px 7px; pointer-events: none; white-space: nowrap; } -.tag.sel { border-color: var(--led); } +.tag.sel { border-color: var(--led); z-index: 1; } +/* A muted knob's tag says so, in the room its curve glyph took. */ .tag.mutedTag { opacity: 0.55; } +.mutedTag canvas { display: none; } .tagText { font-family: var(--mono); font-size: 10px; } -/* ── curve panel ── */ +/* ── preview: the scope, in the plot's place while it runs ── */ +.pv { + position: absolute; inset: 0; z-index: 2; display: flex; flex-direction: column; + background: var(--field); +} +.pvHead { + display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; + gap: 8px 14px; padding: 10px 12px; +} +.pvChips { display: flex; flex-wrap: wrap; gap: 8px; } +.pvSig { + font: inherit; font-size: 11px; cursor: pointer; + border: 1px solid var(--rule); border-radius: 2px; + background: var(--field); color: var(--ink); padding: 5px 11px; +} +.pvSig.on { border-color: var(--led); font-weight: 600; } +/* Sized by the column it is in, never by a style of its own: editor.js reads + the box it was given and draws to that. */ +#pvScope { display: block; flex: 1 1 0; min-height: 0; width: 100%; } +.pvLegend { display: flex; gap: 7px; align-items: center; } +.pvKeyIn, .pvKeyOut { width: 16px; height: 3px; display: inline-block; } +.pvKeyIn { background: var(--faint); } +.pvKeyOut { background: var(--led); } + +/* ── the knobs, as tabs under the plot ── */ +.knobRow { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; } +.chips { display: flex; gap: 8px; flex: 1 1 auto; } +.chipCard { + font: inherit; color: inherit; cursor: pointer; + display: flex; align-items: center; gap: 8px; + background: var(--field); border: 1px solid var(--rule); border-radius: 2px; + padding: 7px 12px; +} +.chipCard.sel { border-color: var(--led); } +.chipCard.sel .chipName { font-weight: 600; } +.chipCard.mutedChip { opacity: 0.55; } +.chipDot { width: 6px; height: 6px; background: var(--led); border-radius: 1px; } +.chipCard .mono { font-size: 11px; } + +/* ── the selected knob's card ── */ .panel { - display: flex; gap: 20px; background: var(--field); + display: flex; flex-wrap: wrap; gap: 14px 20px; background: var(--field); border: 1px solid var(--rule); border-radius: 2px; padding: 16px 18px; } -.col { display: flex; flex-direction: column; gap: 8px; } -.col.grow { flex-grow: 1; gap: 12px; } +.bandHead { flex: 0 0 100%; display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 8px; } +.bandName { font-size: 13px; font-weight: 600; white-space: nowrap; } +.col { display: flex; flex-direction: column; gap: 8px; min-width: 0; } +.col.grow { flex: 1 1 0; gap: 12px; } +.curveCol { flex: none; } .colKicker { font-family: var(--mono); font-size: 10px; letter-spacing: 0.09em; color: var(--muted); } .vr { width: 1px; background: var(--rule); } -#curve { display: block; touch-action: none; } +/* The curve, and its shapes beside it while there is room for both. The + curve is 356 px wide where it has that, its row's width where it has not. */ +.curveRow { display: flex; flex-wrap: wrap; gap: 12px 20px; } +#curve { display: block; flex: none; width: 356px; max-width: 100%; height: 218px; } -.presetsCol { width: 268px; } +.presetsCol { width: 268px; flex: none; display: flex; flex-direction: column; gap: 8px; } .presetGrid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; } .preset { font: inherit; cursor: pointer; @@ -124,85 +176,158 @@ header { display: flex; align-items: center; justify-content: space-between; gap border: 1px solid var(--rule); border-radius: 2px; background: var(--cream); padding: 9px 4px 7px; } +.preset canvas { width: 40px; height: 24px; } .preset span { font-size: 10px; font-weight: 500; color: var(--ink); } .preset.on { border-color: var(--led); } .preset.on span { font-weight: 600; } .stepsRow { display: flex; align-items: center; gap: 7px; font-size: 11px; color: var(--muted); } -.stepsRow[hidden] { display: none; } +/* Minus and plus, at 32 px: the size of a small button on the panel's + console, and a finger's. (The extension keeps its instrument's smaller + ones: below the line.) */ .stepsRow button { - font: inherit; cursor: pointer; width: 18px; height: 18px; padding: 0; line-height: 1; + font: inherit; cursor: pointer; width: 32px; height: 32px; padding: 0; line-height: 1; border: 1px solid var(--rule); border-radius: 2px; background: var(--cream); color: var(--ink); } -/* ── settings column ── */ -.bandHead { display: flex; align-items: baseline; gap: 8px; } -.bandName { font-size: 13px; font-weight: 600; white-space: nowrap; } .field { display: flex; flex-direction: column; gap: 6px; } .fieldRow { display: flex; align-items: center; justify-content: space-between; gap: 8px; } .fieldLabel { font-size: 11px; color: var(--muted); } -.seg { display: flex; } -.knobBtn { - font: inherit; font-family: var(--mono); font-size: 11px; cursor: pointer; - border: 1px solid var(--rule); background: var(--cream); color: var(--muted); - padding: 6px 14px; border-left-width: 0; -} -.knobBtn:first-child { border-left-width: 1px; border-radius: 2px 0 0 2px; } -.knobBtn:last-child { border-radius: 0 2px 2px 0; } -.knobBtn.on { border-color: var(--ink); background: var(--ink); color: var(--cream); } - -.outTrack { position: relative; height: 18px; cursor: pointer; touch-action: none; } -.outRail { position: absolute; left: 4px; right: 4px; top: 8px; height: 2px; background: var(--rule); } -.outFill { position: absolute; top: 7.5px; height: 3px; background: var(--led); } +/* A sideways drag is the track's, an up-and-down one is the page's. */ +.outTrack { position: relative; height: 18px; cursor: pointer; touch-action: pan-y; } +.outRail { position: absolute; left: 5px; right: 5px; top: 50%; height: 2px; margin-top: -1px; background: var(--rule); } +.outFill { position: absolute; top: 50%; height: 3px; margin-top: -1.5px; background: var(--led); } .outHandle { - position: absolute; top: 4px; width: 9px; height: 10px; + position: absolute; top: 50%; width: 9px; height: 10px; margin: -5px 0 0 -4.5px; background: var(--field); border: 1px solid var(--ink); } - -/* ── footer chips ── */ -.chips { display: flex; align-items: center; gap: 8px; } -.chipCard { - font: inherit; cursor: pointer; - display: flex; align-items: center; gap: 8px; - background: var(--field); border: 1px solid var(--rule); border-radius: 2px; - padding: 7px 12px; -} -.chipCard.sel { border-color: var(--led); } -.chipCard.mutedChip { opacity: 0.55; } -.chipDot { width: 6px; height: 6px; background: var(--led); border-radius: 1px; } -.chipCard .mono { font-size: 11px; } -.arrow { font-size: 10px; color: var(--faint); } - -/* ── output presets + preview ── */ -.outPresets { display: flex; gap: 6px; } +.outPresets { display: flex; flex-wrap: wrap; gap: 6px; } .outPreset { font: inherit; font-size: 10px; cursor: pointer; border: 1px solid var(--rule); border-radius: 2px; background: var(--cream); color: var(--muted); padding: 3px 8px; } -.outPreset:hover { border-color: var(--led); color: var(--led); } -.pvOverlay { - position: fixed; inset: 0; z-index: 50; - background: rgba(20, 20, 20, 0.45); - display: flex; align-items: center; justify-content: center; +/* ── the resets, at the foot ── */ +.acts { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; } + +/* The editor body has no box of its own: its parts are laid out by .page. + Until editor.js has the mapping it is inert, and looks it. + + And until the FIRST mapping is in, the page says nothing it does not know: + no switch position, no slider thumb, no output handles, and the toolbar's + hint keeps its lines without showing its words. Everything keeps its box, + so nothing moves when the mapping arrives. That state is `inert="inert"`, + as editor.html writes it; a source that makes the editor inert again later + (the panel's adapter, around a reset) sets the property, which leaves the + attribute empty, so what is on screen then stays on screen. */ +#ed { display: contents; } +#ed[inert] > * { opacity: 0.5; } +#ed[inert=inert] :is(.toggle, input, .outHandle, .hint) { visibility: hidden; } + +/* ── narrower than a full tab ── */ +/* Half a screen: the output range goes under the curve, across the card. The + shapes stay beside the curve for as long as both fit, then wrap under it. */ +@media (max-width: 1100px) { + .vr { display: none; } + .curveCol, .col.grow { flex: 1 1 100%; } + .presetsCol { flex: 1 1 200px; width: auto; } } -.pvOverlay[hidden] { display: none; } -.pvCard { - background: var(--cream); border: 1px solid var(--ink); border-radius: 3px; - padding: 18px; display: flex; flex-direction: column; gap: 12px; - width: 640px; max-width: 94vw; +/* A phone: a 16 px gutter all round (never edge to edge: the gutter is where + a thumb scrolls from), one control to a row, the knobs four across. */ +@media (max-width: 700px) { + .page { padding: 16px; gap: 12px; } + #plot { max-height: 260px; } + .panel { padding: 14px 12px; } + .glide, .damping { flex: 1 1 100%; } + .damping .fieldLabel { min-width: 56px; } + .damping input { flex: 1; width: auto; min-width: 0; } + .chips { flex-basis: 100%; gap: 6px; } + .chipCard { flex: 1 1 0; min-width: 0; flex-direction: column; gap: 0; padding: 6px 2px; } + .chipDot, .chipCard canvas { display: none; } + #openPreview { margin-left: auto; } } -.pvHead { display: flex; justify-content: space-between; align-items: center; } -.pvChips { display: flex; gap: 8px; } -.pvSig { - font: inherit; font-size: 11px; cursor: pointer; - border: 1px solid var(--rule); border-radius: 2px; - background: var(--field); color: var(--ink); padding: 5px 11px; +/* Touch: what a finger drags is drawn larger, and what it presses gets a hit + area that grows invisibly while the control keeps its size. */ +@media (pointer: coarse) { + .outTrack { height: 36px; } + .outHandle { width: 16px; height: 22px; margin: -11px 0 0 -8px; } + .damping input { min-height: 32px; } + :is(.toggleWrap, .ghostBtn, .pvSig, .chipCard, .outPreset, .stepsRow button) { position: relative; } + :is(.toggleWrap, .ghostBtn, .pvSig, .chipCard, .outPreset, .stepsRow button)::after { + content: ""; + position: absolute; inset: -8px -2px; + } } -.pvSig.on { border-color: var(--led); font-weight: 600; } -#pvScope { display: block; background: var(--field); border: 1px solid var(--rule); } -.pvLegend { display: flex; gap: 7px; align-items: center; } -.pvKeyIn, .pvKeyOut { width: 16px; height: 3px; display: inline-block; } -.pvKeyIn { background: var(--faint); } -.pvKeyOut { background: var(--led); } + +/* ── EXTENSION ONLY ── */ + +/* The page itself and its type: a surface's own. The panel's /audio-in is a + console page and brings the console's (console/_audio_in.css), and with it + the rule that hidden means hidden. */ +[hidden] { display: none !important; } +/* Keyboard focus, as the popup has it: the browser's own ring is near black, + and on the filled Stop that is black on black. (The console's base block + has the same rule for the panel's page.) */ +:focus-visible { outline: 2px solid var(--led); outline-offset: 2px; } +body { + margin: 0; + background: var(--cream); + color: var(--ink); + font: 12px/1.45 Inter, ui-sans-serif, system-ui, sans-serif; +} + +/* The cream instrument palette, the same as the popup's (popup.css). */ +:root { + --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + --cream: #f4efe6; + --cream-2: #ede7db; + --ink: #141414; + --muted: #6b655a; + --faint: #a69f90; + --rule: #d9d1c0; + --led: #e8552e; + --ok: #267a43; + --bad: #b43b2d; + --field: #fbf7ef; +} + +/* The instrument's small capitals, on the two headings inside the card, and + its small steppers. */ +.colKicker { text-transform: uppercase; } +.stepsRow button { width: 18px; height: 18px; } + +/* A button under the mouse goes orange here. (On the panel orange is for what + is selected or on, never a hover, so this is not the shared part's to say.) + :where keeps it weaker than a button's own state: a pressed Preview stays + ink, as it did when this rule stood above the pressed one. */ +:where(.ghostBtn, .outPreset):hover { border-color: var(--led); color: var(--led); } + +/* ── the source bar ── */ +header { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 12px; } +.brand { display: flex; align-items: center; gap: 9px; } +.led { width: 8px; height: 8px; background: var(--led); border-radius: 1px; } +.title { font-size: 14px; font-weight: 600; white-space: nowrap; } +.kicker { font-family: var(--mono); font-size: 10px; letter-spacing: 0.09em; color: var(--muted); } +.cluster { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; } + +.chip { + border: 1px solid var(--rule); border-radius: 2px; background: var(--field); + padding: 5px 10px; font-family: var(--mono); font-size: 11px; color: var(--ink); + white-space: nowrap; +} +.chip.dim { color: var(--muted); } +.okChip { border-color: var(--ok); color: var(--ok); } +/* An error looks like one: the chip, and the sentence beside it. */ +.badChip { border-color: var(--bad); color: var(--bad); } +.badText { color: var(--bad); } + +/* Stop is the popup's Stop: the same filled button, in the instrument's small + capitals. */ +.primaryBtn { + font: inherit; font-family: var(--mono); font-size: 10px; letter-spacing: .1em; + text-transform: uppercase; cursor: pointer; min-height: 32px; + border: 1px solid var(--ink); border-radius: 0; + background: var(--ink); color: var(--cream); padding: 0 14px; +} +.primaryBtn:disabled { opacity: 0.35; cursor: default; } diff --git a/tools/patternflow-audio-extension/editor.html b/tools/patternflow-audio-extension/editor.html index 5fb9e676..a8a299b9 100644 --- a/tools/patternflow-audio-extension/editor.html +++ b/tools/patternflow-audio-extension/editor.html @@ -2,12 +2,17 @@ + Patternflow Audio — Mapping
              +
              @@ -17,78 +22,125 @@
              idle - patternflow.local:81 +
              + + +
              +
              - - - -
              - Attack - - quick -
              -
              - Damping - - balanced + Each box follows the loudness of what it hears. Drag a top or bottom edge to take all four over by hand. +
              +
              + + + +
              +
              + + + +
              -
              +
              -
              B1·K1
              -
              B2·K2
              -
              B3·K3
              -
              B4·K4
              +
              K1
              +
              K2
              +
              K3
              +
              K4
              + +
              + + +
              +
              + + + + +
              +
              -
              - RESPONSE - +
              + Knob 1 +
              -
              - CURVE -
              - - - - - - -
              - - - + diff --git a/tools/patternflow-audio-extension/editor.js b/tools/patternflow-audio-extension/editor.js index d815e02f..6cd2030c 100644 --- a/tools/patternflow-audio-extension/editor.js +++ b/tools/patternflow-audio-extension/editor.js @@ -1,137 +1,142 @@ // Patternflow Audio — the mapping editor. // -// One module, no chrome.* and no fetch: everything outside this file arrives -// through window.PFAdapter (editor-adapter.js). The console port copies this -// file verbatim and swaps the adapter. +// One module, no chrome.* and no fetch. It stands on two things: +// +// PFMap (mapping.js) the model: defaults, what a stored mapping is turned +// into, the curves, the level-to-knob chain. +// window.PFAdapter the SOURCE: the extension's is editor-adapter.js, the +// panel's is console/_audio_in_adapter.js. This file +// moves to the panel verbatim; only the adapter differs. // // The model, in one sentence: a band is a BOX drawn on the live spectrum // (width = the frequencies it listens to, height = the level window it maps), // and a RESPONSE CURVE that turns position-in-window into position-in-output- // range. Curves bake to a 33-point table on save; the analysis side — and -// later the firmware — only interpolates, and never learns what a bezier is. +// the firmware — only interpolates, and never learns what a bezier is. +// +// THE ADAPTER CONTRACT. The source owns its limits, its axis, its defaults +// and where a level sits in a window; the editor draws what it is told and +// asks rather than assumes. +// +// loadConfig() -> Promise +// { autoRange, smoothing, attack, bands[4] }, each band { hzMin, hzMax, +// inMin, inMax, gain, outMin, outMax, muted, curve, lut }. Levels are +// 0..1 along the source's own level axis. null means NOTHING IS STORED +// (a first run): the editor then starts from PFMap's defaults. A source +// that could not read must not answer null - it keeps the promise open +// until it can; the editor stays inert meanwhile. +// caps() -> asked once, after loadConfig() has settled +// hzMin, hzMax the ends of the frequency axis +// db [bottom, top]: the dB at 0 and at 1 of the level axis +// levelMax the highest a window's top edge is stored, 0..1 +// hzGap the narrowest band the source stores, in Hz +// top(lo) the lowest top edge it stores over a bottom at `lo` +// bottom(hi) the highest bottom edge it stores under a top at `hi` +// Only hzMin and hzMax are required; the rest default to an axis with no +// dB printed on it, the whole axis, and PFMap.MIN_WINDOW. +// saveConfig(config) -> Promise store it and hand it to what is mapping +// resetBand(i, config) -> Promise +// band i back to the SOURCE's default. Answers the whole mapping as it +// now stands (as loadConfig would); the editor takes that and does not +// save it back. +// resetAll(button) optional: the source's own "reset everything", where it +// has one. The editor then shows Reset mapping beside +// the knob's own Reset and calls this with the button +// pressed (for PFConfirm); what comes back goes in +// through onConfig. A source without it has no such +// button. +// onConfig(fn) optional: fn(config) replaces the editor's copy without +// saving it (after a reset the source did by itself) +// onFrame(fn) fn(frame), about ten times a second while there is +// something to show. Called once, when the editor is up. +// levels[4] each band's level, 0..1 on the level axis. Empty or +// missing = nothing is being heard: no level line, no +// cursor. +// spectrum[64] log-spaced over hzMin..hzMax, same axis +// env[4] { lo, hi } per band while auto range has a learned +// envelope; missing, or null for that band, otherwise +// (the stored window is drawn, dashed). "Learned" is +// the source's to say: what a band has while it has +// heard nothing is not an envelope and is not sent, for +// a drag that takes the windows over by hand seeds each +// window from what is sent here. +// pos[4] where each level sits in its window, 0..1, AS THE +// MAPPER COMPUTED IT: the curve's input +// outputs[4] what each knob is being sent +// db optional [bottom, top] when this frame's levels are on +// another axis than caps().db (the phone app's) +// pos and outputs are what make the cursor and the readout true. A frame +// without them (one the source did not map itself) gets the editor's own +// arithmetic: PFMap.position / PFMap.output on the frame's levels. 'use strict'; const A = window.PFAdapter; - -// ── config ────────────────────────────────────────────────────────────── - -const HZ_DEFAULTS = [[60, 250], [250, 2000], [2000, 5000], [5000, 16000]]; -const LUT_POINTS = 33; - -const PRESETS = { - smooth: { label: 'Smooth', curve: { type: 'bezier', id: 'smooth', y0: 0, y1: 1, p1x: 0.45, p1y: 0.05, p2x: 0.55, p2y: 0.95 } }, - sharp: { label: 'Sharp', curve: { type: 'bezier', id: 'sharp', y0: 0, y1: 1, p1x: 0.10, p1y: 0.65, p2x: 0.35, p2y: 1.00 } }, - fall: { label: 'Fall', curve: { type: 'bezier', id: 'fall', y0: 1, y1: 0, p1x: 0.45, p1y: 0.95, p2x: 0.55, p2y: 0.05 } }, - gate: { label: 'Gate', curve: { type: 'steps', id: 'gate', n: 2 } }, - steps: { label: 'Steps', curve: { type: 'steps', id: 'steps', n: 3 } }, - arch: { label: 'Arch', curve: { type: 'arch', id: 'arch' } } -}; - -const clamp01 = (v) => Math.max(0, Math.min(1, Number(v) || 0)); -const clampHz = (v) => Math.max(CAPS.hzMin, Math.min(CAPS.hzMax, Number(v) || CAPS.hzMin)); - -const CAPS = A.caps(); - -function defaultBand(index) { - const [hzMin, hzMax] = HZ_DEFAULTS[index % HZ_DEFAULTS.length]; - return { - hzMin, hzMax, - knob: Math.min(index, 3), muted: false, - inMin: 0, inMax: 1, gain: 1, - outMin: 0.30, outMax: 0.85, - curve: null, lut: null - }; -} - -function normalizeBand(raw, index) { - const band = { ...defaultBand(index), ...(raw || {}) }; - band.hzMin = clampHz(band.hzMin); band.hzMax = clampHz(band.hzMax); - if (band.hzMin > band.hzMax) [band.hzMin, band.hzMax] = [band.hzMax, band.hzMin]; - if (band.hzMax - band.hzMin < 10) band.hzMax = Math.min(CAPS.hzMax, band.hzMin + 10); - band.inMin = clamp01(band.inMin); band.inMax = clamp01(band.inMax); - if (band.inMax - band.inMin < 0.02) band.inMax = Math.min(1, band.inMin + 0.02); - band.outMin = clamp01(band.outMin); band.outMax = clamp01(band.outMax); - if (band.outMax - band.outMin < 0.05) band.outMax = Math.min(1, band.outMin + 0.05); - // Four bands, four knobs, one line between them: band i drives knob i. - // A free assignment existed and earned nothing but a control to explain. - band.knob = index; - band.muted = band.muted === true; - band.gain = Math.max(0.2, Math.min(4, Number(band.gain) || 1)); - if (band.curve && typeof band.curve !== 'object') band.curve = null; - if (band.lut && (!Array.isArray(band.lut) || band.lut.length < 2)) band.lut = null; - return band; -} - -function normalizeConfig(raw) { - const stored = Array.isArray(raw && raw.bands) ? raw.bands : []; - const bands = []; - for (let i = 0; i < 4; i++) bands.push(normalizeBand(stored[i], i)); - return { - host: (raw && raw.host) || 'patternflow.local', - smoothing: (raw && raw.smoothing) || 0.35, - sendIntervalMs: (raw && raw.sendIntervalMs) || 33, - autoRange: !!(raw && raw.autoRange), - attack: Math.max(0.05, Math.min(0.9, Number(raw && raw.attack) || 0.65)), - bands, - ...(raw && raw.manualExtra ? { manualExtra: raw.manualExtra } : {}) +const { clamp01, PRESETS, evalCurve, bakeLut } = PFMap; + +// What the source says about itself. Read once the mapping is in (the panel +// learns its frequency range from the same reply), so nothing above init() +// may use it at load. +let CAPS = null; +let DECADES = 1; +let TICKS = []; + +function readCaps() { + const c = A.caps(); + CAPS = { + hzMin: c.hzMin, hzMax: c.hzMax, + db: c.db || null, + levelMax: c.levelMax || 1, + hzGap: c.hzGap || 0, + top: c.top || ((lo) => lo + PFMap.MIN_WINDOW), + bottom: c.bottom || ((hi) => hi - PFMap.MIN_WINDOW) }; + DECADES = Math.log10(CAPS.hzMax / CAPS.hzMin); + // Inner gridlines only — the endpoints are drawn from CAPS, so the same + // axis code serves the extension's 20–20k and the microphone's 31–8k. + TICKS = [[50, '50'], [100, '100'], [200, '200'], [500, '500'], + [1000, '1k'], [2000, '2k'], [5000, '5k'], [10000, '10k']] + .filter(([hz]) => hz > CAPS.hzMin * 1.15 && hz < CAPS.hzMax * 0.87); } -// ── curve engine ──────────────────────────────────────────────────────── - -function evalCurve(curve, u) { - u = clamp01(u); - if (!curve) return u; - if (curve.type === 'steps') { - const n = Math.max(2, Math.min(8, Math.round(curve.n) || 2)); - const k = Math.min(n - 1, Math.floor(u * n)); - return k / (n - 1); - } - if (curve.type === 'arch') return Math.sin(Math.PI * u); - if (curve.type === 'bezier') { - // Cubic from (0, y0) to (1, y1); x(t) is monotone while both control xs - // stay in [0,1], so a short bisection recovers t for any u. - const { y0 = 0, y1 = 1, p1x, p1y, p2x, p2y } = curve; - const bx = (t) => 3 * (1 - t) * (1 - t) * t * p1x + 3 * (1 - t) * t * t * p2x + t * t * t; - const by = (t) => (1 - t) * (1 - t) * (1 - t) * y0 + 3 * (1 - t) * (1 - t) * t * p1y + 3 * (1 - t) * t * t * p2y + t * t * t * y1; - let lo = 0, hi = 1; - for (let i = 0; i < 24; i++) { - const mid = (lo + hi) / 2; - if (bx(mid) < u) lo = mid; else hi = mid; - } - return clamp01(by((lo + hi) / 2)); - } - return u; -} - -function legacyCurve(band, u) { - return Math.pow(clamp01(u), 1 / band.gain); -} +const clampHz = (v) => Math.max(CAPS.hzMin, Math.min(CAPS.hzMax, Number(v) || CAPS.hzMin)); +// What the editor DRAWS: the curve itself where there is one (a table would +// round a step's corner), the model's answer otherwise. function bandCurveValue(band, u) { - return band.curve ? evalCurve(band.curve, u) : legacyCurve(band, u); -} - -function bakeLut(curve) { - const lut = []; - for (let i = 0; i < LUT_POINTS; i++) { - lut.push(Number(evalCurve(curve, i / (LUT_POINTS - 1)).toFixed(4))); - } - return lut; + return band.curve ? evalCurve(band.curve, u) : PFMap.curveValue(band, u); } // ── state ─────────────────────────────────────────────────────────────── -let cfg = normalizeConfig(null); +// The mapping being edited (null until init() has it), and the knob selected. +let cfg = null; let sel = 0; -let frame = { levels: [], env: [], spectrum: [], running: false, connected: false }; -let lastEnv = null; // most recent non-empty env, for the auto→manual handoff -let saveTimer = null; +let frame = {}; +let saveTimer = 0; +// Every edit saves itself, 150 ms after the last one of a burst. function persist() { clearTimeout(saveTimer); - saveTimer = setTimeout(() => A.saveConfig(cfg), 150); + saveTimer = setTimeout(flush, 150); +} + +// Save now what is waiting to be saved. Also on the way out of the page, so +// an edit made in the last 150 ms is not left behind. +function flush() { + if (!saveTimer) return null; + clearTimeout(saveTimer); + saveTimer = 0; + return A.saveConfig(cfg); +} +window.addEventListener('pagehide', flush); + +// The source's copy replaces the editor's: after a reset. Not saved back. +function applyConfig(raw) { + cfg = PFMap.normalizeConfig(raw, CAPS); + syncAutoToggle(); + syncDamping(); + selectBand(sel); } function touchCurve(band, curve) { @@ -144,69 +149,132 @@ function touchCurve(band, curve) { const $ = (id) => document.getElementById(id); const plotCanvas = $('plot'); const pctx = plotCanvas.getContext('2d'); -const M = { l: 48, r: 6, t: 22, b: 40 }; -let PW = 0, PH = 0; // plot area, css px - +const M = { l: 48, r: 6, t: 26, b: 40 }; +// The plot area inside those margins, css px. +let PW = 0, PH = 0; + +// A finger, not a mouse: what it has to land on is made larger, here and in +// the stylesheet. EDGE is how far from a box's edge still grabs it, GRIP the +// square drawn on a corner or a curve handle, REACH how far from a curve +// handle still grabs that. +const COARSE = matchMedia('(pointer: coarse)').matches; +const EDGE = COARSE ? 18 : 7; +const GRIP = COARSE ? 11 : 7; +const REACH = COARSE ? 22 : 12; + +// What the toolbar says beside the Auto range switch, in each of its two +// states (syncAutoToggle). The first, as a mouse has it, is also written in +// editor.html in the hint's own place: unseen until the mapping is in, and +// there so that its lines are already taken and the plot under it does not +// move when the words arrive. +const HINT_AUTO = 'Each box follows the loudness of what it hears. ' + + (COARSE ? 'Switch Auto range off to set a height by hand.' : 'Drag a top or bottom edge to take all four over by hand.'); +const HINT_HAND = 'Width: the frequencies a knob listens to. Height: the quiet-to-loud range mapped onto it.'; + +// The plot's box is the stylesheet's (#plot in editor.css: as wide as the +// page, 45% of the window tall between 240 and 400 px, 260 at the most at the +// phone width), so it has its size before this file has run and nothing below +// it moves when the mapping arrives. That is the one rule for its height +// (Preview's scope is the same box). Here the height is only snapped to a +// whole pixel, so the canvas is not resampled, and the pixels are made. function sizePlot() { - const wrap = $('plotWrap'); - const cssW = wrap.clientWidth; - const cssH = 400; + plotCanvas.style.height = ''; + const cssW = plotCanvas.clientWidth, cssH = plotCanvas.clientHeight; const dpr = window.devicePixelRatio || 1; plotCanvas.width = Math.round(cssW * dpr); plotCanvas.height = Math.round(cssH * dpr); - plotCanvas.style.width = cssW + 'px'; plotCanvas.style.height = cssH + 'px'; pctx.setTransform(dpr, 0, 0, dpr, 0, 0); PW = cssW - M.l - M.r; PH = cssH - M.t - M.b; } -const DECADES = Math.log10(CAPS.hzMax / CAPS.hzMin); const xOf = (hz) => M.l + PW * (Math.log10(clampHz(hz) / CAPS.hzMin) / DECADES); const hzOf = (x) => clampHz(CAPS.hzMin * Math.pow(10, DECADES * ((x - M.l) / PW))); const yOf = (v) => M.t + (1 - clamp01(v)) * PH; const vOf = (y) => clamp01(1 - (y - M.t) / PH); -// Inner gridlines only — the endpoints are drawn from CAPS, so the same axis -// code serves the extension's 20–20k and the microphone's 31–8k. -const TICKS = [[50, '50'], [100, '100'], [200, '200'], [500, '500'], - [1000, '1k'], [2000, '2k'], [5000, '5k'], [10000, '10k']] - .filter(([hz]) => hz > CAPS.hzMin * 1.15 && hz < CAPS.hzMax * 0.87); -const DB_LINES = [[1, '0'], [0.75, '-18'], [0.5, '-35'], [0.25, '-53'], [0, '-70']]; +// The level axis is the source's own dB: [bottom, top], or null when it gave +// none. The frame may be on another axis than the source's usual one (see the +// contract). Every level the editor prints goes through these, so the axis, +// the knob's heading and the curve's labels are in one unit. +const axisDb = () => frame.db || CAPS.db; +const minus = (n) => String(Math.round(n)).replace('-', '−'); + +// A height on the level axis, 0..1, as dB: '−43'. '' on an axis without dB. +function dbText(v) { + const db = axisDb(); + return db ? minus(db[0] + v * (db[1] - db[0])) : ''; +} + +// A line every 10 dB: [[v, label]]. +function dbLines() { + const db = axisDb(); + const lines = []; + if (!db) return lines; + for (let d = Math.ceil(db[0] / 10) * 10; d <= db[1]; d += 10) { + lines.push([(d - db[0]) / (db[1] - db[0]), minus(d)]); + } + return lines; +} // Canvas colors come from the page's CSS variables, read fresh each paint so // a theme toggle repaints correctly. The extension page is the cream -// instrument; the device console wraps the same module in its dark tokens. +// instrument; the panel's console wraps the same module in its own tokens. +// Three of them are also painted see-through, which takes the colour apart: +// --led, --rule and --ink have to be written #rrggbb on every surface. function theme() { const s = getComputedStyle(document.body); - const v = (name, fallback) => (s.getPropertyValue(name) || fallback).trim(); - const led = v('--led', '#e8552e'); - const m = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(led.replace('#', '')); - const ledRgb = m ? `${parseInt(m[1], 16)}, ${parseInt(m[2], 16)}, ${parseInt(m[3], 16)}` : '232, 85, 46'; + const v = (name) => s.getPropertyValue(name).trim(); + const alpha = (name) => { + const n = parseInt(v(name).slice(1), 16); + return (a) => `rgba(${n >> 16}, ${n >> 8 & 255}, ${n & 255}, ${a})`; + }; return { - field: v('--field', '#fbf7ef'), - cream: v('--cream', '#f4efe6'), - ink: v('--ink', '#141414'), - muted: v('--muted', '#6b655a'), - faint: v('--faint', '#a69f90'), - rule: v('--rule', '#d9d1c0'), - led, - ledA: (a) => `rgba(${ledRgb}, ${a})`, - ruleA: (a) => `rgba(${parseInt(v('--rule', '#d9d1c0').replace('#', '').slice(0, 2), 16)}, ${parseInt(v('--rule', '#d9d1c0').replace('#', '').slice(2, 4), 16)}, ${parseInt(v('--rule', '#d9d1c0').replace('#', '').slice(4, 6), 16)}, ${a})`, - inkA: (a) => `rgba(${parseInt(v('--ink', '#141414').replace('#', '').slice(0, 2), 16)}, ${parseInt(v('--ink', '#141414').replace('#', '').slice(2, 4), 16)}, ${parseInt(v('--ink', '#141414').replace('#', '').slice(4, 6), 16)}, ${a})` + field: v('--field'), + cream: v('--cream'), + ink: v('--ink'), + muted: v('--muted'), + faint: v('--faint'), + rule: v('--rule'), + led: v('--led'), + ledA: alpha('--led'), + ruleA: alpha('--rule'), + inkA: alpha('--ink') }; } -// The window a band maps right now: its own handles, or the breathing -// envelope while auto range runs. +// The envelope auto range is mapping band `index` through right now, or null: +// auto range is off, the source has not learned one, or what it sent has no +// height (a panel that has never heard anything). +function liveEnv(index) { + const env = cfg.autoRange && frame.env && frame.env[index]; + return env && env.hi > env.lo ? { lo: clamp01(env.lo), hi: clamp01(env.hi) } : null; +} + +// The window a band is drawn with: the breathing envelope while auto range +// has one, the band's own hand-set window otherwise. Auto range without an +// envelope yet shows the stored window (dashed), never a made-up one. function bandWindow(index) { const band = cfg.bands[index]; - if (cfg.autoRange) { - const env = (frame.env && frame.env[index]) || (lastEnv && lastEnv[index]); - if (env) return { lo: clamp01(env.lo), hi: clamp01(env.hi) }; - return { lo: 0.1, hi: 0.7 }; - } - return { lo: band.inMin, hi: band.inMax }; + return liveEnv(index) || { lo: band.inMin, hi: band.inMax }; +} + +// Is anything being heard? Without levels there is no line and no cursor. +const hearing = () => !!(frame.levels && frame.levels.length); + +// Where band `index`'s level sits in its window, 0..1: the mapper's own +// figure when the frame carries it, the model's arithmetic when it does not. +function bandPos(index) { + if (frame.pos && frame.pos.length) return clamp01(frame.pos[index]); + return PFMap.position(frame.levels[index] || 0, cfg.bands[index], liveEnv(index)); +} + +// A window the source would store as it is: the top no higher than it keeps +// one, the two edges no closer than it keeps them. +function fitWindow(band) { + band.inMin = Math.max(0, Math.min(band.inMin, CAPS.bottom(CAPS.levelMax))); + band.inMax = Math.min(CAPS.levelMax, Math.max(band.inMax, CAPS.top(band.inMin))); } function formatHz(hz) { @@ -215,7 +283,10 @@ function formatHz(hz) { // ── main plot drawing ─────────────────────────────────────────────────── -const MONO = '10px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace'; +// Everything written on a canvas: 11 px, in --muted. (10 px in --faint, as it +// was, is 2.3:1 on the extension's cream; the axis and the one number that +// says what the knob gets were the least legible text on the page.) +const MONO = '11px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace'; function drawPlot() { const T = theme(); @@ -227,9 +298,9 @@ function drawPlot() { // grid pctx.font = MONO; pctx.strokeStyle = T.ruleA(0.55); - pctx.fillStyle = T.faint; + pctx.fillStyle = T.muted; pctx.lineWidth = 1; - for (const [v, label] of DB_LINES) { + for (const [v, label] of dbLines()) { const y = Math.round(yOf(v)) + 0.5; if (v > 0 && v < 1) { pctx.beginPath(); pctx.moveTo(M.l, y); pctx.lineTo(M.l + PW, y); pctx.stroke(); @@ -237,6 +308,11 @@ function drawPlot() { pctx.textAlign = 'right'; pctx.fillText(label, M.l - 8, y + 3); } + // The two units, once each, in the margin the level labels are in: dB heads + // their column, Hz starts the row of frequencies. + pctx.textAlign = 'right'; + if (axisDb()) pctx.fillText('dB', M.l - 8, M.t - 9); + pctx.fillText('Hz', M.l - 8, M.t + PH + 16); pctx.textAlign = 'center'; for (const [hz, label] of TICKS) { const x = Math.round(xOf(hz)) + 0.5; @@ -289,26 +365,30 @@ function drawPlot() { pctx.beginPath(); pctx.moveTo(x0, yBot); pctx.lineTo(x1, yBot); pctx.stroke(); pctx.setLineDash([]); - // live level as a horizontal line across the box — the band's average. - // The line BELONGS to the box: below the window it sits pinned on the - // bottom edge ("resting under the floor"), above it on the top. Drawn at - // the raw position it wandered out of its own box, which read as broken. - const level = (frame.levels && frame.levels[i]) || 0; - const ly = Math.max(yTop + 1.5, Math.min(yBot - 1.5, yOf(level))); - pctx.strokeStyle = T.led; - pctx.lineWidth = 2; - pctx.beginPath(); - pctx.moveTo(x0 + 1, ly); - pctx.lineTo(x1 - 1, ly); - pctx.stroke(); + // The live level, as a line across the box: how far through its window + // the band is, which is what the curve is fed and so what the knob gets. + // It is the mapper's position and not the level's height on the axis: the + // two differ wherever the mapper is not linear in this axis (the panel + // maps linear amplitude on a dB axis, and auto range squelches the floor), + // and a line drawn at the height says "fires" where the knob does not. + // Resting, it sits on the bottom edge; at full scale, on the top. + if (hearing()) { + const ly = Math.max(yTop + 1.5, Math.min(yBot - 1.5, yBot - bandPos(i) * (yBot - yTop))); + pctx.strokeStyle = T.led; + pctx.lineWidth = 2; + pctx.beginPath(); + pctx.moveTo(x0 + 1, ly); + pctx.lineTo(x1 - 1, ly); + pctx.stroke(); + } if (isSel) { pctx.fillStyle = T.field; pctx.strokeStyle = T.ink; pctx.lineWidth = 1; for (const [cx, cy] of [[x0, yTop], [x1, yTop], [x0, yBot], [x1, yBot]]) { - pctx.fillRect(cx - 3.5, cy - 3.5, 7, 7); - pctx.strokeRect(cx - 3.5, cy - 3.5, 7, 7); + pctx.fillRect(cx - GRIP / 2, cy - GRIP / 2, GRIP, GRIP); + pctx.strokeRect(cx - GRIP / 2, cy - GRIP / 2, GRIP, GRIP); } } }); @@ -316,31 +396,37 @@ function drawPlot() { placeTags(); } +// Band N drives knob N, and is named after it everywhere: K1..K4. A tag sits +// ON its box's top edge, outside the box: auto range can make a box thinner +// than the tag, which would otherwise cover it and its level line. It starts +// at the box's left edge, and stops at the plot's right one: on a phone the +// highest knob's box is narrower than its tag, which was cut off there. function placeTags() { const T = theme(); cfg.bands.forEach((band, i) => { const tag = $('tag' + i); const win = bandWindow(i); - tag.style.left = Math.round(xOf(band.hzMin)) + 'px'; - tag.style.top = Math.round(yOf(win.hi) - 11) + 'px'; tag.classList.toggle('sel', i === sel); tag.classList.toggle('mutedTag', band.muted); - tag.querySelector('.tagText').textContent = 'B' + (i + 1) + (band.muted ? '·M' : ''); + tag.querySelector('.tagText').textContent = 'K' + (i + 1) + (band.muted ? ' muted' : ''); drawGlyph(tag.querySelector('canvas'), band, 18, 12, T.ink); + tag.style.left = Math.round(Math.min(xOf(band.hzMin), M.l + PW + M.r - tag.offsetWidth - 1)) + 'px'; + tag.style.top = Math.round(yOf(win.hi) - 21) + 'px'; }); } -// tiny curve glyph, used by tags and footer chips +// The tiny curve glyph on tags, tabs and shapes. Its box (w x h css px) is the +// stylesheet's, so a glyph that is not drawn yet already has its size; here +// only the pixels are made. function drawGlyph(canvas, band, w, h, color) { const dpr = window.devicePixelRatio || 1; if (canvas.width !== w * dpr) { canvas.width = w * dpr; canvas.height = h * dpr; - canvas.style.width = w + 'px'; canvas.style.height = h + 'px'; } const g = canvas.getContext('2d'); g.setTransform(dpr, 0, 0, dpr, 0, 0); g.clearRect(0, 0, w, h); - g.strokeStyle = color || theme().ink; + g.strokeStyle = color; g.lineWidth = 1.4; g.lineCap = 'round'; g.beginPath(); @@ -356,7 +442,6 @@ function drawGlyph(canvas, band, w, h, color) { // ── main plot interaction ─────────────────────────────────────────────── -const EDGE = 7; let drag = null; function hitTest(x, y) { @@ -371,8 +456,18 @@ function hitTest(x, y) { const insideX = x > x0 - EDGE && x < x1 + EDGE; const insideY = y > yTop - EDGE && y < yBot + EDGE; if (!insideX || !insideY) continue; - const nearL = Math.abs(x - x0) <= EDGE, nearR = Math.abs(x - x1) <= EDGE; - const nearT = Math.abs(y - yTop) <= EDGE, nearB = Math.abs(y - yBot) <= EDGE; + // An edge is grabbed from EDGE px outside it, and from inside too, but + // never from further in than a third of the box: the middle third of + // even the smallest box stays "move", so a fat edge cannot swallow it. + const inX = Math.min(EDGE, (x1 - x0) / 3), inY = Math.min(EDGE, (yBot - yTop) / 3); + const nearL = x <= x0 + inX, nearR = x >= x1 - inX; + // Under a finger, in auto range, a top or bottom edge is not a handle: + // with a finger's EDGE those two zones are a quarter of the plot, and a + // thumb that only meant to scroll the page took all four knobs over (see + // takeOver). There the touch scrolls, or moves the box if it is inside it; + // a height is set by hand after the Auto range switch is put off. + const tall = !(COARSE && cfg.autoRange); + const nearT = tall && y <= yTop + inY, nearB = tall && y >= yBot - inY; // Corners first — a grab there resizes both axes at once. if (nearL && nearT) return { band: i, mode: 'tl' }; if (nearR && nearT) return { band: i, mode: 'tr' }; @@ -387,16 +482,22 @@ function hitTest(x, y) { return null; } -// Grabbing a breathing edge is the gesture that says "I'll take it from +// Dragging a breathing edge is the gesture that says "I'll take it from // here": auto range switches off, and every band's window is seeded from the -// envelope it was just breathing at, so nothing jumps. -function freezeAutoWindows() { - const env = (frame.env && frame.env.length ? frame.env : lastEnv) || []; +// envelope it was just breathing at, so nothing jumps. A band with no live +// envelope keeps the window it had (and a source sends none for a band that +// has learned nothing: seeded from silence, a window is a sliver at the floor +// that any sound pegs at its top). This is the ONLY thing that writes an +// envelope into a window: the Auto range switch itself just goes back to the +// hand-set windows, so trying auto costs nothing. With a mouse only: see +// hitTest. +function takeOver() { cfg.bands.forEach((band, i) => { - const e = env[i]; - if (!e) return; - band.inMin = clamp01(e.lo); - band.inMax = Math.max(band.inMin + 0.02, clamp01(e.hi)); + const env = liveEnv(i); + if (!env) return; + band.inMin = env.lo; + band.inMax = env.hi; + fitWindow(band); }); cfg.autoRange = false; syncAutoToggle(); @@ -407,26 +508,40 @@ function plotPointer(event) { return { x: event.clientX - rect.left, y: event.clientY - rect.top }; } +// A FINGER ON A CANVAS. One that lands on something draggable is a drag, and +// the page must not scroll under it; one that misses is the browser's, so the +// page still scrolls from anywhere else on the plot. That cannot be said with +// touch-action, which is per element and not per touch (`none` made the plot +// a trap no swipe got out of; `pan-y` would give the browser the vertical +// drag of a top or bottom edge, and it takes it with a pointercancel that +// preventDefault on pointerdown does not stop). What does decide it per touch +// is a touchstart listener that is not passive: prevented, that touch never +// scrolls and its pointer events run to the end. `hits(x, y)` says whether +// the point, in the canvas's own px, is on something. +function dragOnHit(canvas, hits) { + canvas.addEventListener('touchstart', (event) => { + const rect = canvas.getBoundingClientRect(); + const touch = event.changedTouches[0]; + if (hits(touch.clientX - rect.left, touch.clientY - rect.top)) event.preventDefault(); + }, { passive: false }); +} +dragOnHit(plotCanvas, hitTest); + plotCanvas.addEventListener('pointerdown', (event) => { const { x, y } = plotPointer(event); const hit = hitTest(x, y); if (!hit) return; if (hit.band !== sel) selectBand(hit.band); - if (cfg.autoRange && hit.mode !== 'move' && hit.mode !== 'l' && hit.mode !== 'r') { - freezeAutoWindows(); // any grab that touches a breathing edge takes over - } - const band = cfg.bands[hit.band]; - const win = bandWindow(hit.band); - drag = { - ...hit, - startX: x, startY: y, - hzMin: band.hzMin, hzMax: band.hzMax, - lo: win.lo, hi: win.hi - }; + // Armed, not moving: a press selects. Nothing is moved, resized or taken + // over until the pointer has travelled SLOP px, so a tap (or the jitter of + // one) changes nothing. + drag = { ...hit, startX: x, startY: y, live: false }; plotCanvas.setPointerCapture(event.pointerId); event.preventDefault(); }); +const SLOP = 4; + plotCanvas.addEventListener('pointermove', (event) => { const { x, y } = plotPointer(event); if (!drag) { @@ -439,7 +554,16 @@ plotCanvas.addEventListener('pointermove', (event) => { return; } const band = cfg.bands[drag.band]; - const MINR = 1.12; // narrowest box, as a frequency ratio + if (!drag.live) { + if (Math.hypot(x - drag.startX, y - drag.startY) < SLOP) return; + // A top or bottom edge of a box in auto range (or a corner, which is one + // too): this drag takes over. ('move' has neither letter in it.) + if (cfg.autoRange && /[tb]/.test(drag.mode)) takeOver(); + const win = bandWindow(drag.band); + Object.assign(drag, { live: true, hzMin: band.hzMin, hzMax: band.hzMax, lo: win.lo, hi: win.hi }); + } + // The narrowest box, as a frequency ratio. + const MINR = 1.12; if (drag.mode === 'move') { // Translate in log-frequency: the box keeps its RATIO width, which is @@ -448,30 +572,34 @@ plotCanvas.addEventListener('pointermove', (event) => { let ratio = Math.pow(10, shift); ratio = Math.max(CAPS.hzMin / drag.hzMin, Math.min(CAPS.hzMax / drag.hzMax, ratio)); band.hzMin = drag.hzMin * ratio; - band.hzMax = drag.hzMax * ratio; + band.hzMax = Math.max(drag.hzMax * ratio, band.hzMin + CAPS.hzGap); if (!cfg.autoRange) { const dv = vOf(y) - vOf(drag.startY); const span = drag.hi - drag.lo; - const lo = Math.max(0, Math.min(1 - span, drag.lo + dv)); - band.inMin = lo; - band.inMax = lo + span; + band.inMin = Math.max(0, Math.min(CAPS.levelMax - span, drag.lo + dv)); + band.inMax = band.inMin + span; + fitWindow(band); } } else { // Edges and corners share the same four moves; a corner just does two. - if (drag.mode.includes('l')) band.hzMin = Math.min(hzOf(x), band.hzMax / MINR); - if (drag.mode.includes('r')) band.hzMax = Math.max(hzOf(x), band.hzMin * MINR); - if (drag.mode.includes('t')) band.inMax = Math.max(vOf(y), band.inMin + 0.02); - if (drag.mode.includes('b')) band.inMin = Math.min(vOf(y), band.inMax - 0.02); + // Each edge stops where the source would stop it, so what is drawn is + // what is stored and nothing moves on the next load. + if (drag.mode.includes('l')) band.hzMin = Math.max(CAPS.hzMin, Math.min(hzOf(x), band.hzMax / MINR, band.hzMax - CAPS.hzGap)); + if (drag.mode.includes('r')) band.hzMax = Math.min(CAPS.hzMax, Math.max(hzOf(x), band.hzMin * MINR, band.hzMin + CAPS.hzGap)); + if (drag.mode.includes('t')) band.inMax = Math.min(CAPS.levelMax, Math.max(vOf(y), CAPS.top(band.inMin))); + if (drag.mode.includes('b')) band.inMin = Math.max(0, Math.min(vOf(y), CAPS.bottom(band.inMax))); } drawPlot(); + drawCurve(); renderChips(); renderSettings(); }); const releasePlot = () => { if (!drag) return; + const moved = drag.live; drag = null; - persist(); + if (moved) persist(); }; plotCanvas.addEventListener('pointerup', releasePlot); plotCanvas.addEventListener('pointercancel', releasePlot); @@ -480,15 +608,17 @@ plotCanvas.addEventListener('pointercancel', releasePlot); const curveCanvas = $('curve'); const cctx = curveCanvas.getContext('2d'); -const CM = { l: 30, r: 10, t: 12, b: 26 }; +const CM = { l: 34, r: 10, t: 12, b: 26 }; let CW = 0, CH = 0; let curveDrag = null; +// The curve's box is the stylesheet's too (#curve: 356 px wide where there is +// room, the width of its row where there is not, 218 tall). The pointer maths +// below go by CW and CH, so any size works. function sizeCurve() { const dpr = window.devicePixelRatio || 1; - const cssW = 356, cssH = 218; + const cssW = curveCanvas.clientWidth, cssH = 218; curveCanvas.width = cssW * dpr; curveCanvas.height = cssH * dpr; - curveCanvas.style.width = cssW + 'px'; curveCanvas.style.height = cssH + 'px'; cctx.setTransform(dpr, 0, 0, dpr, 0, 0); CW = cssW - CM.l - CM.r; CH = cssH - CM.t - CM.b; } @@ -538,47 +668,62 @@ function drawCurve() { cctx.strokeStyle = T.ink; cctx.lineWidth = 1; for (const [hx, hy] of [[c.p1x, c.p1y], [c.p2x, c.p2y]]) { - cctx.fillRect(cxOf(hx) - 3.5, cyOf(hy) - 3.5, 7, 7); - cctx.strokeRect(cxOf(hx) - 3.5, cyOf(hy) - 3.5, 7, 7); + cctx.fillRect(cxOf(hx) - GRIP / 2, cyOf(hy) - GRIP / 2, GRIP, GRIP); + cctx.strokeRect(cxOf(hx) - GRIP / 2, cyOf(hy) - GRIP / 2, GRIP, GRIP); } } - // live cursor: where the music is on this curve right now + // live cursor: where the music is on this curve right now, and (top + // left) what that is: the level heard, in the axis's dB, -> the knob's + // value. The position and the value are the mapper's own figures when the + // frame has them. const win = bandWindow(sel); - const level = (frame.levels && frame.levels[sel]) || 0; - const u = clamp01((level - win.lo) / Math.max(0.001, win.hi - win.lo)); - const v = bandCurveValue(band, u); - cctx.strokeStyle = T.led; - cctx.setLineDash([3, 3]); - cctx.lineWidth = 1; - cctx.beginPath(); cctx.moveTo(cxOf(u), CM.t + CH); cctx.lineTo(cxOf(u), cyOf(v)); cctx.stroke(); - cctx.setLineDash([]); - cctx.fillStyle = T.led; - cctx.beginPath(); cctx.arc(cxOf(u), cyOf(v), 4.5, 0, Math.PI * 2); cctx.fill(); + if (hearing()) { + const u = bandPos(sel); + const v = bandCurveValue(band, u); + cctx.strokeStyle = T.led; + cctx.setLineDash([3, 3]); + cctx.lineWidth = 1; + cctx.beginPath(); cctx.moveTo(cxOf(u), CM.t + CH); cctx.lineTo(cxOf(u), cyOf(v)); cctx.stroke(); + cctx.setLineDash([]); + cctx.fillStyle = T.led; + cctx.beginPath(); cctx.arc(cxOf(u), cyOf(v), 4.5, 0, Math.PI * 2); cctx.fill(); + const out = frame.outputs && frame.outputs.length ? frame.outputs[sel] : PFMap.output(band, u); + const heard = dbText(frame.levels[sel] || 0); + cctx.font = MONO; + cctx.fillStyle = T.muted; + cctx.textAlign = 'left'; + cctx.fillText((heard && heard + ' dB ') + '→ ' + Number(out).toFixed(2), CM.l + 6, CM.t + 12); + } - // labels + // labels: along the bottom the window this curve is spread over, quiet end + // to loud end, in the plot's dB (auto range keeps moving it, so it gets the + // word); up the side the two ends of the output range. + const lo = dbText(win.lo); cctx.font = MONO; - cctx.fillStyle = T.faint; + cctx.fillStyle = T.muted; cctx.textAlign = 'left'; - cctx.fillText(cfg.autoRange ? 'in auto' : 'in ' + win.lo.toFixed(2), CM.l, CM.t + CH + 16); + cctx.fillText(cfg.autoRange ? 'auto range' : lo, CM.l, CM.t + CH + 16); cctx.textAlign = 'right'; - cctx.fillText(cfg.autoRange ? '' : win.hi.toFixed(2), CM.l + CW, CM.t + CH + 16); - cctx.fillText(cfg.bands[sel].outMax.toFixed(2), CM.l - 6, CM.t + 8); - cctx.fillText(cfg.bands[sel].outMin.toFixed(2), CM.l - 6, CM.t + CH); - cctx.textAlign = 'left'; - cctx.fillText(u.toFixed(2) + ' → ' + (cfg.bands[sel].outMin + v * (cfg.bands[sel].outMax - cfg.bands[sel].outMin)).toFixed(2), CM.l + 6, CM.t + 12); + cctx.fillText(cfg.autoRange || !lo ? '' : dbText(win.hi) + ' dB', CM.l + CW, CM.t + CH + 16); + cctx.fillText(band.outMax.toFixed(2), CM.l - 6, CM.t + 8); + cctx.fillText(band.outMin.toFixed(2), CM.l - 6, CM.t + CH); +} + +// Which handle of the selected knob's curve is at this point: 'p1', 'p2', or +// nothing (only a bezier has handles). +function curveHit(x, y) { + const c = cfg.bands[sel].curve; + if (!c || c.type !== 'bezier') return null; + const near = (hx, hy) => Math.hypot(x - cxOf(hx), y - cyOf(hy)) < REACH; + return near(c.p1x, c.p1y) ? 'p1' : near(c.p2x, c.p2y) ? 'p2' : null; } +dragOnHit(curveCanvas, curveHit); curveCanvas.addEventListener('pointerdown', (event) => { - const band = cfg.bands[sel]; - if (!band.curve || band.curve.type !== 'bezier') return; const rect = curveCanvas.getBoundingClientRect(); - const x = event.clientX - rect.left, y = event.clientY - rect.top; - const c = band.curve; - const near = (hx, hy) => Math.hypot(x - cxOf(hx), y - cyOf(hy)) < 12; - if (near(c.p1x, c.p1y)) curveDrag = 'p1'; - else if (near(c.p2x, c.p2y)) curveDrag = 'p2'; - else return; + curveDrag = curveHit(event.clientX - rect.left, event.clientY - rect.top); + if (!curveDrag) return; curveCanvas.setPointerCapture(event.pointerId); event.preventDefault(); }); @@ -615,13 +760,18 @@ function renderPresetChips() { const id = band.curve ? (band.curve.id || 'custom') : null; document.querySelectorAll('.preset').forEach((el) => { el.classList.toggle('on', el.dataset.p === id); + el.setAttribute('aria-pressed', el.dataset.p === id); }); - $('legacyHint').hidden = !!band.curve; + // One line under the shapes, and only when it is true: a knob that has no + // shape is on the source's default response (nothing to fix, so nothing is + // asked), and only a bezier has handles to drag. + $('curveHint').textContent = !band.curve ? 'This knob uses the default response.' + : band.curve.type === 'bezier' ? 'Drag the two handles for a custom shape.' : ''; $('stepsRow').hidden = !(band.curve && band.curve.type === 'steps'); if (band.curve && band.curve.type === 'steps') $('stepsN').textContent = String(band.curve.n); document.querySelectorAll('.preset canvas').forEach((canvas) => { const preset = PRESETS[canvas.parentElement.dataset.p]; - drawGlyph(canvas, { curve: preset.curve, gain: 1 }, 40, 24, theme().ink); + drawGlyph(canvas, { curve: preset, gain: 1 }, 40, 24, theme().ink); }); } @@ -629,7 +779,7 @@ document.querySelectorAll('.preset').forEach((el) => { el.addEventListener('click', () => { const preset = PRESETS[el.dataset.p]; if (!preset) return; - touchCurve(cfg.bands[sel], structuredClone(preset.curve)); + touchCurve(cfg.bands[sel], structuredClone(preset)); drawCurve(); renderPresetChips(); placeTags(); @@ -653,19 +803,26 @@ function stepsAdjust(delta) { persist(); } +// The selected knob's card: which knob, what it listens to (its frequencies, +// and its window in the plot's dB unless auto range is setting that), its +// output range and its mute. The knob's name is also on its Reset and in +// Preview's legend. function renderSettings() { const band = cfg.bands[sel]; - $('bandName').textContent = 'Band ' + (sel + 1); + const lo = dbText(band.inMin); + $('bandName').textContent = 'Knob ' + (sel + 1); $('bandMeta').textContent = formatHz(band.hzMin) + ' – ' + formatHz(band.hzMax) + ' Hz' - + (cfg.autoRange ? ' · in auto' : ' · in ' + band.inMin.toFixed(2) + '–' + band.inMax.toFixed(2)); + + (cfg.autoRange ? ' · auto range' : lo && ' · ' + lo + ' to ' + dbText(band.inMax) + ' dB'); $('outLabel').textContent = band.outMin.toFixed(2) + ' – ' + band.outMax.toFixed(2); - const track = $('outTrack'); - const w = track.clientWidth - 10; - $('outLo').style.left = (5 + band.outMin * w - 5) + 'px'; - $('outHi').style.left = (5 + band.outMax * w - 5) + 'px'; + const w = $('outTrack').clientWidth - 10; + $('outLo').style.left = (5 + band.outMin * w) + 'px'; + $('outHi').style.left = (5 + band.outMax * w) + 'px'; $('outFill').style.left = (5 + band.outMin * w) + 'px'; $('outFill').style.width = Math.max(0, (band.outMax - band.outMin) * w) + 'px'; $('muteToggle').classList.toggle('on', band.muted); + $('muteToggle').setAttribute('aria-pressed', band.muted); + $('resetBand').textContent = 'Reset K' + (sel + 1); + $('pvOut').textContent = 'K' + (sel + 1) + ' output'; } document.querySelectorAll('.outPreset').forEach((el) => { @@ -677,12 +834,16 @@ document.querySelectorAll('.outPreset').forEach((el) => { }); }); +// The track is dragged sideways and lets the page scroll up and down +// (touch-action: pan-y). A touch that turns out to be a scroll ends in +// pointercancel after it has already moved the handle a little: that puts +// back what the press found and saves nothing. let outDrag = null; $('outTrack').addEventListener('pointerdown', (event) => { const band = cfg.bands[sel]; const rect = $('outTrack').getBoundingClientRect(); const v = clamp01((event.clientX - rect.left - 5) / (rect.width - 10)); - outDrag = Math.abs(v - band.outMin) < Math.abs(v - band.outMax) ? 'lo' : 'hi'; + outDrag = { lo: Math.abs(v - band.outMin) < Math.abs(v - band.outMax), was: [band.outMin, band.outMax] }; $('outTrack').setPointerCapture(event.pointerId); event.preventDefault(); }); @@ -691,13 +852,19 @@ $('outTrack').addEventListener('pointermove', (event) => { const band = cfg.bands[sel]; const rect = $('outTrack').getBoundingClientRect(); const v = clamp01((event.clientX - rect.left - 5) / (rect.width - 10)); - if (outDrag === 'lo') band.outMin = Math.min(v, band.outMax - 0.05); - else band.outMax = Math.max(v, band.outMin + 0.05); + if (outDrag.lo) band.outMin = Math.min(v, band.outMax - PFMap.MIN_OUT); + else band.outMax = Math.max(v, band.outMin + PFMap.MIN_OUT); + renderSettings(); drawCurve(); +}); +$('outTrack').addEventListener('pointerup', () => { + if (outDrag) { outDrag = null; persist(); } +}); +$('outTrack').addEventListener('pointercancel', () => { + if (!outDrag) return; + [cfg.bands[sel].outMin, cfg.bands[sel].outMax] = outDrag.was; + outDrag = null; renderSettings(); drawCurve(); }); -const releaseOut = () => { if (outDrag) { outDrag = null; persist(); } }; -$('outTrack').addEventListener('pointerup', releaseOut); -$('outTrack').addEventListener('pointercancel', releaseOut); $('muteToggle').addEventListener('click', () => { const band = cfg.bands[sel]; @@ -705,21 +872,30 @@ $('muteToggle').addEventListener('click', () => { renderSettings(); renderChips(); drawPlot(); persist(); }); -$('resetBand').addEventListener('click', () => { - cfg.bands[sel] = defaultBand(sel); - touchCurve(cfg.bands[sel], structuredClone(PRESETS.smooth.curve)); - selectBand(sel); - persist(); +// Reset band asks the SOURCE for its default: the editor has no table of its +// own, so this and the source's own "reset everything" cannot disagree. +// Whatever is waiting to be saved goes first, so the reset is the last word. +$('resetBand').addEventListener('click', async () => { + await flush(); + const next = await A.resetBand(sel, cfg); + if (next) applyConfig(next); }); -// ── footer chips ──────────────────────────────────────────────────────── +// The source's own "reset everything", where it has one (see the contract): +// the button is in the page, hidden, and only such a source brings it out. +if (A.resetAll) { + $('resetMap').hidden = false; + $('resetMap').addEventListener('click', function () { A.resetAll(this); }); +} + +// ── the knobs, as tabs under the plot ─────────────────────────────────── function renderChips() { cfg.bands.forEach((band, i) => { const chip = $('chip' + i); chip.classList.toggle('sel', i === sel); + chip.setAttribute('aria-pressed', i === sel); chip.classList.toggle('mutedChip', band.muted); - chip.querySelector('.chipName').textContent = 'B' + (i + 1); chip.querySelector('.chipHz').textContent = formatHz(band.hzMin) + '–' + formatHz(band.hzMax); drawGlyph(chip.querySelector('canvas'), band, 18, 12, theme().muted); }); @@ -740,13 +916,21 @@ function selectBand(index) { // ── preview ───────────────────────────────────────────────────────────── // -// Ableton-style: synthetic test signals through the SELECTED band's whole +// Ableton-style: synthetic test signals through the SELECTED knob's whole // chain - window, glide ballistics, curve, output range - drawn as a // scrolling scope. What the knob will do, audible music not required. +// +// The scope takes the plot's place while it runs (#pv lies over it: a test +// signal needs no spectrum), so everything it demonstrates - attack and +// damping above it, the knobs, the curve and the output range below - stays +// live around it. The Preview button, right under it, closes it again; so +// does Escape. -const pvOverlay = $('pvOverlay'); +const pv = $('pv'); const pvScope = $('pvScope'); const pvCtx = pvScope.getContext('2d'); +// The scope's box, css px: whatever the layer gives it (see pvSize). +let pvW = 0, pvH = 0; let pvSig = 'pulse'; let pvRaf = 0; let pvTimer = 0; @@ -764,40 +948,32 @@ function pvSignal(t) { // output shows comes from the glide, which is the point of the demo. const phase = t % 0.8; const spike = phase < 0.06 ? 0.85 : 0; - return Math.min(1, Math.max(0, spike + 0.07 + (Math.random() - 0.5) * 0.03)); + return clamp01(spike + 0.07 + (Math.random() - 0.5) * 0.03); } if (pvSig === 'noisy') { const spike = (t % 1.7) < 0.06 ? 0.5 : 0; - return Math.min(1, Math.max(0, - 0.30 + 0.10 * Math.sin(t * 1.3) + (Math.random() - 0.5) * 0.22 + spike)); + return clamp01(0.30 + 0.10 * Math.sin(t * 1.3) + (Math.random() - 0.5) * 0.22 + spike); } // swell - return Math.min(1, Math.max(0, - 0.45 + 0.35 * Math.sin(t * Math.PI * 2 * 0.35) + (Math.random() - 0.5) * 0.04)); + return clamp01(0.45 + 0.35 * Math.sin(t * Math.PI * 2 * 0.35) + (Math.random() - 0.5) * 0.04); } function pvChain(sig) { // Mirror the live pipeline: asymmetric glide on the LEVEL, then window, // curve, output range. - const a = pvLevel < sig - ? Math.max(0.05, Math.min(0.9, cfg.attack)) - : Math.max(0.05, Math.min(0.9, cfg.smoothing)); + // (Both alphas are already inside 0.05..0.9: the model holds them there on + // load and the two sliders cannot leave it.) + const a = pvLevel < sig ? cfg.attack : cfg.smoothing; pvLevel += (sig - pvLevel) * a; const band = cfg.bands[sel]; - let u; - if (cfg.autoRange) { - u = clamp01((pvLevel - 0.10) / 0.85); - } else { - const inMin = clamp01(band.inMin); - const inMax = Math.max(inMin + 0.01, clamp01(band.inMax)); - u = clamp01((pvLevel - inMin) / (inMax - inMin)); - } - return band.outMin + bandCurveValue(band, u) * (band.outMax - band.outMin); + // The model's own two functions. In auto range the test signal is taken + // as already sitting in its envelope. + return PFMap.output(band, PFMap.position(pvLevel, band, cfg.autoRange ? { lo: 0, hi: 1 } : null)); } function pvDraw() { const T = theme(); - const w = 600, h = 230; + const w = pvW, h = pvH; pvCtx.clearRect(0, 0, w, h); pvCtx.strokeStyle = T.ruleA(0.5); pvCtx.lineWidth = 1; @@ -821,7 +997,7 @@ function pvDraw() { } function pvTick(now) { - if (pvOverlay.hidden) return; + if (pv.hidden) return; if (!pvLast) pvLast = now; // Wall-clock scheduling, not per-frame increments: a throttled tab still // simulates the RIGHT amount of time when frames do arrive (capped per @@ -853,26 +1029,31 @@ function pvSchedule() { }, 300); } -function pvOpen() { +// The scope draws into the box the stylesheet gives it: what is left of the +// plot's box under the row of test signals. No size of its own. +function pvSize() { const dpr = window.devicePixelRatio || 1; - pvScope.width = 600 * dpr; pvScope.height = 230 * dpr; - pvScope.style.width = '600px'; pvScope.style.height = '230px'; + pvW = pvScope.clientWidth; pvH = pvScope.clientHeight; + pvScope.width = pvW * dpr; pvScope.height = pvH * dpr; pvCtx.setTransform(dpr, 0, 0, dpr, 0, 0); - $('pvTitle').textContent = 'Preview — Band ' + (sel + 1); - pvIn = []; pvOut = []; pvT = 0; pvLast = 0; pvLevel = 0; - pvOverlay.hidden = false; - pvSchedule(); } -function pvClose() { - pvOverlay.hidden = true; +// On, off: the one switch. The button says which it is (aria-pressed). +function pvSet(on) { + pv.hidden = !on; + $('openPreview').setAttribute('aria-pressed', on); cancelAnimationFrame(pvRaf); clearTimeout(pvTimer); + if (!on) return; + pvSize(); + pvIn = []; pvOut = []; pvT = 0; pvLast = 0; pvLevel = 0; + pvSchedule(); } -$('openPreview').addEventListener('click', pvOpen); -$('pvClose').addEventListener('click', pvClose); -pvOverlay.addEventListener('click', (e) => { if (e.target === pvOverlay) pvClose(); }); +$('openPreview').addEventListener('click', () => pvSet(pv.hidden)); +document.addEventListener('keydown', (event) => { + if (event.key === 'Escape' && !pv.hidden) pvSet(false); +}); document.querySelectorAll('.pvSig').forEach((el) => { el.addEventListener('click', () => { pvSig = el.dataset.s; @@ -882,23 +1063,23 @@ document.querySelectorAll('.pvSig').forEach((el) => { // ── auto toggle, status, wiring ───────────────────────────────────────── +// The switch, and beside it the one thing to know about the boxes in the mode +// it is in. function syncAutoToggle() { $('autoToggle').classList.toggle('on', cfg.autoRange); - $('autoHint').textContent = cfg.autoRange - ? 'boxes breathe with the room — drag a top or bottom edge to take manual control' - : 'manual windows — the box edges are exactly what maps'; + $('autoToggle').setAttribute('aria-pressed', cfg.autoRange); + $('autoHint').textContent = cfg.autoRange ? HINT_AUTO : HINT_HAND; } +// The switch only switches: off goes back to the windows set by hand, which +// auto range never touched. (Dragging an edge is what keeps an envelope.) $('autoToggle').addEventListener('click', () => { - if (cfg.autoRange) freezeAutoWindows(); - else cfg.autoRange = true; + cfg.autoRange = !cfg.autoRange; syncAutoToggle(); drawPlot(); drawCurve(); renderSettings(); persist(); }); -$('stopBtn').addEventListener('click', () => A.stop()); - // Damping = the level smoothing the analysis already runs (an EMA on each // band, before the window and curve). Right means calmer: the slider's 0..1 // maps to alpha 0.9..0.05, so a gentle word scale replaces a number nobody @@ -907,68 +1088,90 @@ $('stopBtn').addEventListener('click', () => A.stop()); const DAMP_WORDS = [[0.25, 'tight'], [0.5, 'balanced'], [0.75, 'smooth'], [1.01, 'glassy']]; const ATK_WORDS = [[0.25, 'snap'], [0.5, 'quick'], [0.75, 'soft'], [1.01, 'lazy']]; -function dampWord(v) { - for (const [top, word] of DAMP_WORDS) if (v < top) return word; - return 'glassy'; -} +const dampWord = (v) => DAMP_WORDS.find(([top]) => v < top)[1]; function syncDamping() { const v = clamp01((0.9 - cfg.smoothing) / 0.85); - $('damping').value = String(v); + $('damping').value = v; $('dampingWord').textContent = dampWord(v); const k = clamp01((0.9 - cfg.attack) / 0.85); - $('attack').value = String(k); + $('attack').value = k; $('attackWord').textContent = ATK_WORDS.find(([top]) => k < top)[1]; } $('attack').addEventListener('input', () => { const v = clamp01($('attack').value); - cfg.attack = Math.max(0.05, Math.min(0.9, 0.9 - v * 0.85)); + cfg.attack = 0.9 - v * 0.85; $('attackWord').textContent = ATK_WORDS.find(([top]) => v < top)[1]; persist(); }); $('damping').addEventListener('input', () => { const v = clamp01($('damping').value); - cfg.smoothing = Math.max(0.05, Math.min(0.9, 0.9 - v * 0.85)); + cfg.smoothing = 0.9 - v * 0.85; $('dampingWord').textContent = dampWord(v); persist(); }); -function renderStatus() { - const el = $('sourceChip'); - if (frame.demo) { el.textContent = 'demo source'; el.className = 'chip'; } - else if (frame.running && frame.connected) { el.textContent = (A.labels && A.labels.live) || 'live · tab audio'; el.className = 'chip okChip'; } - else if (frame.running) { el.textContent = 'capturing · connecting'; el.className = 'chip'; } - else { el.textContent = 'idle'; el.className = 'chip'; } - $('hostChip').textContent = cfg.host + ':81'; - $('captureHint').textContent = frame.running || frame.demo ? '' : A.captureHint; - $('stopBtn').disabled = !frame.running; -} - -A.onFrame((state) => { - frame = state || {}; - if (frame.env && frame.env.length) lastEnv = frame.env; - drawPlot(); - drawCurve(); - renderStatus(); -}); - +// Everything drawn is sized from the page, so a window made narrower (half a +// screen beside the music, a phone turned round) is measured again. window.addEventListener('resize', () => { + if (!cfg) return; sizePlot(); + sizeCurve(); + if (!pv.hidden) pvSize(); drawPlot(); + drawCurve(); renderSettings(); }); +// An in-page "are you sure": the first press arms the button and makes it ask, +// a second press within four seconds answers yes. A second press that comes +// within half a second of the first is the same double click or double tap, +// not an answer to a question nobody has had time to read: it is ignored, and +// the button stays armed. window.confirm is not used anywhere on these pages: +// the phone app shows /audio-in in a WebView that has none (it answers "no" +// without showing anything). Use: +// if (!PFConfirm(button, 'Reset everything? Press again')) return; +window.PFConfirm = function (button, ask) { + if (button._armed) { + if (Date.now() - button._at < 500) return false; + clearTimeout(button._armed); + button._armed = 0; + button.textContent = button._label; + return true; + } + button._label = button.textContent; + button.textContent = ask; + button._at = Date.now(); + button._armed = setTimeout(() => { + button._armed = 0; + button.textContent = button._label; + }, 4000); + return false; +}; + (async function init() { const stored = await A.loadConfig(); - cfg = normalizeConfig(stored); - if (A._setDemoConfig) A._setDemoConfig(cfg); + readCaps(); + cfg = PFMap.normalizeConfig(stored, CAPS); sizePlot(); sizeCurve(); syncAutoToggle(); syncDamping(); selectBand(0); - renderStatus(); - A.requestStatus(); + // The attribute, not the property: a browser too old to know `inert` would + // keep the attribute, and with it what the stylesheet hides before the + // first mapping. + $('ed').removeAttribute('inert'); + if (A.onConfig) A.onConfig(applyConfig); + A.onFrame((state) => { + const was = String(axisDb()); + frame = state || {}; + // A frame on another level axis (`db` in the contract) changes the dB the + // card prints, not only the plot's labels. + if (String(axisDb()) !== was) renderSettings(); + drawPlot(); + drawCurve(); + }); })(); diff --git a/tools/patternflow-audio-extension/manifest.json b/tools/patternflow-audio-extension/manifest.json index c4f409b7..8e2ac534 100644 --- a/tools/patternflow-audio-extension/manifest.json +++ b/tools/patternflow-audio-extension/manifest.json @@ -1,8 +1,8 @@ { "manifest_version": 3, "name": "Patternflow Audio", - "description": "Capture tab audio, map spectrum bands, and send knob deltas to Patternflow.", - "version": "0.1.0", + "description": "Capture a tab's audio, map four frequency bands onto the four knobs, and stream them to a Patternflow panel.", + "version": "0.2.0", "icons": { "16": "icons/icon-16.png", "32": "icons/icon-32.png", diff --git a/tools/patternflow-audio-extension/mapping.js b/tools/patternflow-audio-extension/mapping.js new file mode 100644 index 00000000..975fc629 --- /dev/null +++ b/tools/patternflow-audio-extension/mapping.js @@ -0,0 +1,236 @@ +// Patternflow Audio — the mapping model. +// +// What a mapping is, written once. Four bands, one per knob. A band listens +// to a range of frequencies and takes the level it hears there through +// +// level -> window -> response curve -> [rests at .. peaks at] +// +// where the window is the band's own (manual) or the envelope the band has +// recently moved in (auto range). offscreen.js runs that chain for the knobs; +// the editor draws it and previews it; both ask this file, so a box on the +// screen and a knob on the panel cannot be told two different things. +// +// It is ONE object on purpose. These are classic scripts sharing a global +// scope, where a second top-level `clamp01` in any file is a SyntaxError that +// kills the page at load and that `node --check` on each file cannot see. +// Everything here is reached as PFMap.something; nothing else is declared. +// +// Two halves. Down to the line that says so, this is the model: no chrome.*, +// no DOM, none of the extension's own numbers. That half also travels to the +// panel, inside its /audio-in page (firmware/toolchain/build_audio_in_page.py +// stops reading at the line), so every byte of it is sent over the panel's +// Wi-Fi: keep it to what the editor needs on both sides. Below the line is +// what only the extension's audio source knows. + +var PFMap = (function () { + 'use strict'; + + const within = (v, lo, hi) => Math.max(lo, Math.min(hi, Number(v) || 0)); + const clamp01 = (v) => within(v, 0, 1); + + // ── response curves ───────────────────────────────────────────────────── + // + // A curve is authored as one of these shapes and BAKED to a 33-point table. + // Whatever maps for real - offscreen.js here, the firmware on the panel - + // only interpolates the table, and never learns what a bezier is. + + const PRESETS = { + smooth: { type: 'bezier', id: 'smooth', y0: 0, y1: 1, p1x: 0.45, p1y: 0.05, p2x: 0.55, p2y: 0.95 }, + sharp: { type: 'bezier', id: 'sharp', y0: 0, y1: 1, p1x: 0.10, p1y: 0.65, p2x: 0.35, p2y: 1.00 }, + fall: { type: 'bezier', id: 'fall', y0: 1, y1: 0, p1x: 0.45, p1y: 0.95, p2x: 0.55, p2y: 0.05 }, + gate: { type: 'steps', id: 'gate', n: 2 }, + steps: { type: 'steps', id: 'steps', n: 3 }, + arch: { type: 'arch', id: 'arch' } + }; + + function evalCurve(curve, u) { + u = clamp01(u); + if (!curve) return u; + if (curve.type === 'steps') { + const n = within(Math.round(curve.n) || 2, 2, 8); + return Math.min(n - 1, Math.floor(u * n)) / (n - 1); + } + if (curve.type === 'arch') return Math.sin(Math.PI * u); + if (curve.type === 'bezier') { + // Cubic from (0, y0) to (1, y1); x(t) is monotone while both control xs + // stay in [0,1], so a short bisection recovers t for any u. + const { y0 = 0, y1 = 1, p1x, p1y, p2x, p2y } = curve; + const bx = (t) => 3 * (1 - t) * (1 - t) * t * p1x + 3 * (1 - t) * t * t * p2x + t * t * t; + const by = (t) => (1 - t) * (1 - t) * (1 - t) * y0 + 3 * (1 - t) * (1 - t) * t * p1y + 3 * (1 - t) * t * t * p2y + t * t * t * y1; + let lo = 0, hi = 1; + for (let i = 0; i < 24; i++) { + const mid = (lo + hi) / 2; + if (bx(mid) < u) lo = mid; else hi = mid; + } + return clamp01(by((lo + hi) / 2)); + } + return u; + } + + function bakeLut(curve) { + const lut = []; + for (let i = 0; i <= 32; i++) lut.push(Number(evalCurve(curve, i / 32).toFixed(4))); + return lut; + } + + // ── level to knob ─────────────────────────────────────────────────────── + + // The narrowest window and the shortest stretch of knob a band may have. + const MIN_WINDOW = 0.02; + const MIN_OUT = 0.05; + + const api = { + clamp01, PRESETS, evalCurve, bakeLut, MIN_WINDOW, MIN_OUT, + + // The response at a position in the window: the band's curve, or, for a + // band saved before there were curves, its boost exponent (above 1x a + // quiet band reaches its top early, below 1x a loud one holds back). + curveValue(band, u) { + return band.curve ? evalCurve(band.curve, u) + : Math.pow(clamp01(u), 1 / within(Number(band.gain) || 1, 0.2, 4)); + }, + + // Where a level sits in the band's window, 0..1: the input of the curve. + // `env` is the band's { lo, hi } envelope while auto range runs, null for + // the band's own inMin..inMax. Auto range maps a fixed slice of the + // envelope: under 0.10 of it is squelched, so the resting noise at the + // floor does not dance, and 0.95 of it is already full scale. + position(level, band, env) { + if (env) { + return clamp01((clamp01((level - env.lo) / Math.max(0.001, env.hi - env.lo)) - 0.10) / (0.95 - 0.10)); + } + const lo = clamp01(band.inMin); + return clamp01((level - lo) / Math.max(MIN_WINDOW, clamp01(band.inMax) - lo)); + }, + + // The knob value for a position in the window, through the curve. + output(band, u) { + const lo = clamp01(band.outMin); + return lo + api.curveValue(band, u) * (clamp01(band.outMax) - lo); + }, + + // The default band: what Reset gives, and what a first run starts with. + defaultBand(index, caps) { + return normalizeBand(null, index, caps); + }, + + // The whole mapping, made whole: { autoRange, smoothing, attack, bands[4] }. + // `caps` is the source's { hzMin, hzMax }. smoothing and attack are the + // two alphas of the level glide: how fast a level falls, how fast it rises. + // + // Nothing stored is a first run, and starts the way the panel does: auto + // range on, four default bands. Something stored is somebody's mapping + // and is not improved behind their back; in particular one saved before + // auto range existed has no `autoRange` and stays manual. + normalizeConfig(raw, caps) { + const stored = raw && Array.isArray(raw.bands) ? raw : { autoRange: true, bands: [] }; + return { + autoRange: stored.autoRange === true, + smoothing: within(Number(stored.smoothing) || 0.35, 0.05, 0.9), + attack: within(Number(stored.attack) || 0.65, 0.05, 0.9), + bands: [0, 1, 2, 3].map((i) => normalizeBand(stored.bands[i], i, caps)) + }; + } + }; + + const HZ = [[60, 250], [250, 2000], [2000, 5000], [5000, 16000]]; + + // lo and hi inside floor..ceiling, in that order, at least `gap` apart. + function pair(lo, hi, floor, ceiling, gap) { + lo = within(lo, floor, ceiling); + hi = within(hi, floor, ceiling); + if (lo > hi) [lo, hi] = [hi, lo]; + if (hi - lo < gap) { + hi = Math.min(ceiling, lo + gap); + lo = Math.min(lo, hi - gap); + } + return [lo, hi]; + } + + // One band, made whole. No band at all is the default: its slice of the + // spectrum, the full window, the Smooth curve already baked. A band that IS + // stored keeps what it has and gains nothing: saved before curves existed + // it has no curve and keeps its boost exponent, and saved when the output + // range was `base` + `range` it is read as that. + function normalizeBand(raw, index, caps) { + const b = raw && typeof raw === 'object' ? raw + : { outMin: 0.30, outMax: 0.85, curve: { ...PRESETS.smooth }, lut: bakeLut(PRESETS.smooth) }; + const base = b.base ?? 0.30; + const [hzMin, hzMax] = pair(b.hzMin ?? HZ[index][0], b.hzMax ?? HZ[index][1], caps.hzMin, caps.hzMax, 10); + const [inMin, inMax] = pair(b.inMin ?? 0, b.inMax ?? 1, 0, 1, MIN_WINDOW); + const [outMin, outMax] = pair(b.outMin ?? base, b.outMax ?? base + (b.range ?? 0.55), 0, 1, MIN_OUT); + return { + hzMin, hzMax, + // Four bands, four knobs, one line between them: band i drives knob i. + knob: index, + muted: b.muted === true, + inMin, inMax, + gain: within(Number(b.gain) || 1, 0.2, 4), + outMin, outMax, + curve: b.curve && typeof b.curve === 'object' ? b.curve : null, + lut: Array.isArray(b.lut) && b.lut.length > 1 ? b.lut : null + }; + } + + return api; +})(); + +// ── EXTENSION ONLY ──────────────────────────────────────────────────────── +// +// What the extension's audio source knows and the panel's does not. + +(function (M) { + 'use strict'; + + // Where the mapping is stored. The panel's address is not in it: that is the + // popup's, under a key of its own, so the editor and the popup never write + // the same value and neither can put the other's old one back. + M.CONFIG_KEY = 'patternflowAudioConfig'; + + // Tab audio through an AnalyserNode: the frequencies worth drawing, and the + // dB the level axis runs between, bottom then top. Levels everywhere in the + // extension are 0..1 along that axis. + M.SOURCE = { hzMin: 20, hzMax: 20000, db: [-80, -10] }; + + M.normalizeDb = function (db) { + return M.clamp01((db - M.SOURCE.db[0]) / (M.SOURCE.db[1] - M.SOURCE.db[0])); + }; + + // The knob gets the band's TABLE where it has one, interpolated the way the + // firmware interpolates it, and not the curve the table was baked from: a + // table rounds a step's corner, and what is sent should be what a panel + // running the same mapping would do. Whatever in the extension asks for a + // knob value gets this: PFMap.output() reaches it, so offscreen.js and the + // editor's Preview do. + const drawn = M.curveValue; + M.curveValue = function (band, u) { + const lut = band.lut; + if (!lut || lut.length < 2) return drawn(band, u); + const at = M.clamp01(u) * (lut.length - 1); + const i = Math.floor(at); + const j = Math.min(lut.length - 1, i + 1); + return M.clamp01(lut[i]) * (1 - (at - i)) + M.clamp01(lut[j]) * (at - i); + }; + + // Auto-range envelope, one per band: the floor and peak the band has + // recently seen, in the same units as its level. Both edges jump to a new + // extreme at once and drift back slowly (ENV_RELEASE per 33 ms tick is about + // 8 s, the panel's own time constant). ENV_MIN_SPAN stops silence from + // collapsing the window into a noise amplifier. There is no microphone gate + // here, unlike the panel: tab audio is digital, and silent means zero. + M.ENV_RELEASE = 0.004; + M.ENV_MIN_SPAN = 0.06; + + M.newEnvelope = function () { + return { lo: 1, hi: 0 }; + }; + + M.trackEnvelope = function (env, level, release) { + const v = M.clamp01(level); + const r = release || M.ENV_RELEASE; + if (v > env.hi) env.hi = v; else env.hi += (v - env.hi) * r; + if (v < env.lo) env.lo = v; else env.lo += (v - env.lo) * r; + if (env.hi < env.lo + M.ENV_MIN_SPAN) env.hi = env.lo + M.ENV_MIN_SPAN; + return env; + }; +})(PFMap); diff --git a/tools/patternflow-audio-extension/offscreen.html b/tools/patternflow-audio-extension/offscreen.html index 15521ab9..ab66731b 100644 --- a/tools/patternflow-audio-extension/offscreen.html +++ b/tools/patternflow-audio-extension/offscreen.html @@ -2,6 +2,7 @@ + diff --git a/tools/patternflow-audio-extension/offscreen.js b/tools/patternflow-audio-extension/offscreen.js index e216c148..dd3cf4d3 100644 --- a/tools/patternflow-audio-extension/offscreen.js +++ b/tools/patternflow-audio-extension/offscreen.js @@ -1,4 +1,47 @@ -let config = null; +// Patternflow Audio — offscreen document: the analysis, the socket, and the +// truth about both. +// +// Tab capture hands over a MediaStream, and a MediaStream needs a document to +// live in; a service worker has none and is put to sleep besides. So this +// page holds the capture, runs the mapping (mapping.js) 30 times a second and +// writes the four knob lanes to the panel. +// +// It is also the one place that KNOWS whether a capture is running, which +// address the socket points at, whether that socket opened, whether the panel +// on the other end is listening, and why a capture ended. Every state message +// it sends carries all of that (see status()), so the service worker, which +// Chrome restarts with empty memory whenever it likes, can rebuild from any +// single one of them, or ask. +// +// An offscreen document has chrome.runtime and nothing else. No +// chrome.storage: the mapping arrives in messages (the popup reads it at +// Start, the editor sends every save) and is normalised HERE, on receipt, +// whoever sent it. A `host` inside a mapping is ignored; the address comes +// only with start, manual-connect and host, which only the popup sends. + +const WS_PORT = 81; // where the panel's audio socket listens +const TICK_MS = 33; // analyse and send at about 30 Hz +const REPORT_MS = 120; // levels and spectrum to the popup and the editor + +// The panel hands a lane back to its encoder 500 ms after the last message +// that set it, and docs/audio-ws-spec.md asks for a resend at least every 250. +// A frame that has not changed is resent on the first tick past this. The +// timer's 33 ms come out nearer 35, so that is six ticks, about 210 ms; at +// 200 it was seven, 245, and once in eight minutes 251. +const HOLD_MS = 180; + +const QUIET_MS = 2000; // a spectrum flat for this long is "no sound" +const ASK_MS = 4000; // how long the panel gets to answer over HTTP + +let config = null; // the mapping: PFMap.normalizeConfig() of what arrived +let address = ''; // as the popup sent it: a name or an IP, a port if one was typed +let host = ''; // 'name:port' the socket is pointed at, '' when idle +let tabTitle = ''; +let error = ''; // what is wrong right now +let note = ''; // why the last capture ended, when nobody pressed Stop +let running = false; // a tab is being analysed +let manual = false; // the test connection: a socket, no audio + let audioCtx = null; let analyser = null; let sourceNode = null; @@ -8,109 +51,251 @@ let ws = null; let tickTimer = null; let reconnectTimer = null; let lastLevelReport = 0; -let smoothing = [0, 0, 0, 0]; -let running = false; -let manual = false; let wsWanted = false; let wsSerial = 0; -let lastSentValues = [-1, -1, -1, -1]; + +// What the panel was last told, and when. `driven` is the lanes this socket +// has set and not yet handed back. let lastSentBody = ''; +let lastSentAt = 0; +let driven = [false, false, false, false]; + +// The test connection's sliders: a knob that was moved is held where it was +// put, null is a knob nobody touched. +let manualLanes = [null, null, null, null]; + +// Audio-React on the panel (the AUD row of its NETWORK screen): true, false, +// or null when the panel has not said. The socket opens either way and the +// panel ignores every frame while it is off, so an open socket alone is not +// "live". +let panelAudio = null; +let checking = false; // that question is on its way + +// Socket attempts in a row that never opened. The first one asks the panel's +// HTTP side what is there, once; `streak` changes whenever the answer to that +// would no longer be about the current attempt. +let failures = 0; +let streak = 0; + +// Per band: the smoothed level, and the envelope auto range maps it through. +let levels = [0, 0, 0, 0]; +let envelopes = [0, 1, 2, 3].map(PFMap.newEnvelope); + +// Per band, the loudest tick since the last report: its level, where that sat +// in the window and what the knob got. A report every 120 ms that carried the +// latest tick showed a kick at anything from 42% to 100% of its height. +let peakLevels = [-1, -1, -1, -1]; +let peakPos = [0, 0, 0, 0]; +let peakOutputs = [0, 0, 0, 0]; + +let heard = false; // this capture has had sound at least once +let heardAt = 0; // when it last had + +function connected() { + return !!ws && ws.readyState === WebSocket.OPEN; +} -// Auto-range envelopes, one pair per band: the floor and peak the band has -// recently seen, in the same dbNorm units as the levels. Attack is instant on -// both edges, release is a slow exponential (~8 s at the 30 Hz tick — the -// device runs 0.002 at 60 Hz, this is the same time constant). MIN_SPAN stops -// silence from collapsing the window into a noise amplifier. Same idea as the -// device's auto range, minus its microphone gate — tab audio is digital and -// silent means zero, so there is no whine floor to fence off. -let envLo = [1, 1, 1, 1]; -let envHi = [0, 0, 0, 0]; -const ENV_RELEASE = 0.004; -const ENV_MIN_SPAN = 0.06; -const AUTO_LO = 0.10; // relative squelch inside the tracked window -const AUTO_HI = 0.95; // relative full-scale - -function resetEnvelopes() { - envLo = [1, 1, 1, 1]; - envHi = [0, 0, 0, 0]; +// The one word for all of it. The popup and the editor both print from this, +// so they cannot disagree about what "live" means: +// idle nothing is running +// connecting no open socket yet, or the panel has not yet said whether it listens +// off connected, and Audio-React is switched off on the panel +// live a tab is captured and the panel is being driven +// test the test connection is up +// error see `error` +function phase() { + if (error) return 'error'; + if (!running && !manual) return 'idle'; + if (!connected() || checking) return 'connecting'; + if (panelAudio === false) return 'off'; + return running ? 'live' : 'test'; } -function trackEnvelopes(levels) { - for (let i = 0; i < 4; i++) { - const v = clamp01(levels[i] || 0); - if (v > envHi[i]) envHi[i] = v; - else envHi[i] += (v - envHi[i]) * ENV_RELEASE; - if (v < envLo[i]) envLo[i] = v; - else envLo[i] += (v - envLo[i]) * ENV_RELEASE; - if (envHi[i] < envLo[i] + ENV_MIN_SPAN) envHi[i] = envLo[i] + ENV_MIN_SPAN; +function status() { + return { + running, + manual, + connected: connected(), + phase: phase(), + host, + tabTitle, + error, + note, + panelAudio, + heard, + silent: running && performance.now() - heardAt > QUIET_MS, + held: manual ? manualLanes.slice() : [] + }; +} + +// Always the whole status, plus whatever is live. A patch that carried only +// `connected` left the service worker to remember the rest, and after a +// restart it remembered nothing: the popup said Idle over a running capture. +function patchState(live) { + chrome.runtime.sendMessage({ type: 'offscreen-state', patch: { ...status(), ...live } }).catch(() => {}); +} + +// The popup sends a checked address, with a port only if one was typed. +function wsHost() { + return /:\d+$/.test(address) ? address : `${address}:${WS_PORT}`; +} + +// The same panel over HTTP. Never the socket's port: a port that was typed is +// where the audio socket is, and the panel's API is on 80 whatever that says. +function httpName() { + return address.replace(/:\d+$/, ''); +} + +// One question to the panel's HTTP API. Returns null when nobody answered in +// time, else { status, json } with json null for anything that is not JSON. +// The panel serves one HTTP connection at a time, so these are single +// requests at moments that matter, never a poll. +async function ask(path, options) { + const abort = new AbortController(); + const timer = setTimeout(() => abort.abort(), ASK_MS); + try { + const reply = await fetch(`http://${httpName()}${path}`, { ...options, cache: 'no-store', signal: abort.signal }); + return { status: reply.status, json: reply.ok ? await reply.json().catch(() => null) : null }; + } catch (failure) { + return null; + } finally { + clearTimeout(timer); } } -const MIN_HZ = 20; -const MAX_HZ = 20000; +// Is the panel listening? Asked once when the socket opens (`opening`: until +// the answer is in, nobody is told "live"), and again each time the popup +// opens onto an open socket: the switch is on the panel and may have been +// thrown either way since, so "off" and "live" are both old news by then. +// Anything but a clear yes or no (no answer, a 404 from firmware that has no +// such route) is "not known": nothing is claimed about it, and an earlier +// answer stands. +async function checkPanel(opening) { + const serial = wsSerial; + if (opening) { + checking = true; + patchState(); + } + const answer = await ask('/api/audio'); + if (serial !== wsSerial) return; + const on = answer && answer.json && answer.json.audioRuntime; + if (typeof on === 'boolean') panelAudio = on; + checking = false; + patchState(); +} -function patchState(patch) { - chrome.runtime.sendMessage({ type: 'offscreen-state', patch }).catch(() => {}); +// The popup's Turn on. Returns what to tell the person when it did not work. +async function turnOn() { + const serial = wsSerial; + const answer = await ask('/api/audio', { + method: 'POST', + // The panel's web server reads a body as arguments only under this type. + headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, + body: 'on=1' + }); + const on = answer && answer.json && answer.json.audioRuntime; + if (typeof on !== 'boolean') { + return 'The panel did not answer. Audio-React can also be switched on at the panel: the AUD row of its NETWORK screen.'; + } + if (serial === wsSerial) panelAudio = on; + return ''; } -function normalizeHost(host) { - let value = (host || 'patternflow.local').trim(); - value = value.replace(/^https?:\/\//, '').replace(/^wss?:\/\//, '').replace(/\/.*$/, ''); - if (!value.includes(':')) value += ':81'; - return value; +// A socket that will not open says nothing about why. One look at the +// panel's HTTP side does: nobody there, a panel without the audio feature, or +// something that answers and still refuses the socket. Asked once per run of +// failures: the socket retries every 1.2 s, and a question per retry would +// queue up on an address that never answers. +async function diagnose() { + const mine = streak; + const name = httpName(); + const answer = await ask('/api/status'); + if (mine !== streak) return; + const caps = answer && answer.json && answer.json.caps; + if (!answer) { + error = `No panel at ${name}. Check the address (the panel's NETWORK screen shows its IP) and that this computer is on the same Wi-Fi. Retrying.`; + } else if (Array.isArray(caps) && !caps.includes('audio')) { + error = `The panel at ${name} does not run the Audio edition. Get it at patternflow.work/editions.`; + } else { + // Firmware from before `caps`, a wrong port, or not a panel at all: no + // more is known than that the socket stays shut. + error = `${Array.isArray(caps) ? 'The panel' : 'Something'} at ${name} answers, but the audio connection to ${host} does not open. Retrying.`; + } + patchState(); } -function wsUrl() { - return `ws://${normalizeHost(config.host)}`; +// A new target, or the current one answered: what was learned about the last +// run of failures no longer applies. +function newStreak() { + failures = 0; + streak++; } function connectWs() { clearTimeout(reconnectTimer); const previous = ws; + ws = null; try { if (previous) { previous.onopen = null; - previous.onerror = null; previous.onclose = null; previous.close(); } - } catch (error) {} + } catch (failure) {} const serial = ++wsSerial; - const socket = new WebSocket(wsUrl()); + panelAudio = null; + checking = false; + + let socket; + try { + socket = new WebSocket(`ws://${host}`); + } catch (failure) { + // Not something a socket can be opened to. The popup checks what is + // typed, so this came from elsewhere; trying again would fail the same. + error = `"${host}" is not an address a connection can be made to.`; + patchState(); + return; + } ws = socket; + let opened = false; socket.onopen = () => { if (socket !== ws || serial !== wsSerial) return; - patchState({ connected: true, error: '' }); - }; - - socket.onerror = () => { - if (socket !== ws || serial !== wsSerial) return; - patchState({ connected: false, error: 'WebSocket error' }); + opened = true; + error = ''; + newStreak(); + // The panel let go of every lane when the last socket closed: it has been + // told nothing, so the next tick says everything again. + resetWire(); + checkPanel(true); }; socket.onclose = () => { if (socket !== ws || serial !== wsSerial) return; - patchState({ connected: false }); - if (wsWanted && config) reconnectTimer = setTimeout(connectWs, 1200); + if (!opened && ++failures === 1) diagnose(); + patchState(); + if (wsWanted) reconnectTimer = setTimeout(connectWs, 1200); }; + + // The old socket is gone and this one is not open yet: say so now, before + // anyone reads a `connected` that was about the old address. + patchState(); } function send(msg, options = {}) { - if (!ws || ws.readyState !== WebSocket.OPEN) return false; + if (!connected()) return false; if (!options.control && ws.bufferedAmount > 0) return false; ws.send(msg); return true; } -// -1 is "nothing sent yet", so the first real level always goes out. With -// absolute values there is no baseline to match — only a cache of what the -// lane was last told. -function resetOutputBaselines() { - lastSentValues = [-1, -1, -1, -1]; +// The panel on the other end has been told nothing (a new socket, or `off`). +function resetWire() { lastSentBody = ''; + lastSentAt = 0; + driven = [false, false, false, false]; } // Absolute, not a delta. @@ -131,24 +316,35 @@ function resetOutputBaselines() { // also the quietest one. // // A '-' leaves a lane alone, which is what a muted band sends. +// +// An unchanged frame is NOT news, but it still has to be said: the panel +// releases a lane 500 ms after the last message that set it. Skipping +// unchanged frames handed the knobs back half a second into any silence, any +// pause, any curve resting on one step, while the popup said Live. So a frame +// goes out when it differs from the last one or when HOLD_MS have passed. function sendLanes(values) { + // A lane this socket set and no longer drives is handed back now, once, + // and not left to the panel's timeout. + for (let i = 0; i < 4; i++) { + if (values[i] === null && driven[i] && send(`off=${i}`, { control: true })) driven[i] = false; + } + if (values.every((v) => v === null)) return; + const body = values .map((v) => (v === null ? '-' : Math.max(0, Math.min(1, v)).toFixed(3))) .join(','); - if (body === lastSentBody) return; - if (send(`a=${body}`)) lastSentBody = body; -} - -function sendOutputValue(knob, value) { - const idx = Math.max(0, Math.min(3, Number(knob) || 0)); - const normalized = Math.max(0, Math.min(1, Number(value) || 0)); - if (Math.abs(normalized - lastSentValues[idx]) < 0.002) return; - if (send(`k=${idx},v=${normalized.toFixed(3)}`)) { - lastSentValues[idx] = normalized; + const now = performance.now(); + if (body === lastSentBody && now - lastSentAt < HOLD_MS) return; + if (!send(`a=${body}`)) return; + lastSentBody = body; + lastSentAt = now; + for (let i = 0; i < 4; i++) { + if (values[i] !== null) driven[i] = true; } } -async function stop() { +// Everything off and forgotten. `why` is for a capture that ended by itself. +async function stop(why = '') { running = false; manual = false; wsWanted = false; @@ -157,27 +353,30 @@ async function stop() { tickTimer = null; reconnectTimer = null; wsSerial++; + newStreak(); send('off', { control: true }); - resetOutputBaselines(); + resetWire(); try { if (ws) { ws.onopen = null; - ws.onerror = null; ws.onclose = null; ws.close(); } - } catch (error) {} + } catch (failure) {} ws = null; try { if (sourceNode) sourceNode.disconnect(); - } catch (error) {} + } catch (failure) {} sourceNode = null; if (mediaStream) { - mediaStream.getTracks().forEach((track) => track.stop()); + mediaStream.getTracks().forEach((track) => { + track.onended = null; + track.stop(); + }); mediaStream = null; } @@ -188,65 +387,93 @@ async function stop() { analyser = null; freqBuf = null; - smoothing = [0, 0, 0, 0]; - resetEnvelopes(); - patchState({ running: false, manual: false, connected: false, levels: [], outputs: [], spectrum: [], env: [] }); + config = null; + address = ''; + host = ''; + tabTitle = ''; + error = ''; + note = why; + panelAudio = null; + checking = false; + heard = false; + manualLanes = [null, null, null, null]; + levels = [0, 0, 0, 0]; + peakLevels = [-1, -1, -1, -1]; + envelopes = [0, 1, 2, 3].map(PFMap.newEnvelope); + patchState({ levels: [], pos: [], outputs: [], spectrum: [], env: [] }); } -async function start(streamId, nextConfig) { +async function start(message) { await stop(); - config = nextConfig; - running = true; - manual = false; - wsWanted = true; - resetOutputBaselines(); - - audioCtx = new AudioContext(); - mediaStream = await navigator.mediaDevices.getUserMedia({ - audio: { - mandatory: { - chromeMediaSource: 'tab', - chromeMediaSourceId: streamId - } - }, - video: false - }); - - analyser = audioCtx.createAnalyser(); - analyser.fftSize = 2048; - analyser.smoothingTimeConstant = 0.3; - freqBuf = new Float32Array(analyser.frequencyBinCount); + try { + config = PFMap.normalizeConfig(message.config, PFMap.SOURCE); + address = String(message.host || '').trim(); + host = wsHost(); + tabTitle = message.tabTitle || ''; + running = true; + wsWanted = true; + + audioCtx = new AudioContext(); + mediaStream = await navigator.mediaDevices.getUserMedia({ + audio: { + mandatory: { + chromeMediaSource: 'tab', + chromeMediaSourceId: message.streamId + } + }, + video: false + }); - sourceNode = audioCtx.createMediaStreamSource(mediaStream); - sourceNode.connect(analyser); - analyser.connect(audioCtx.destination); + // The stream ends when its tab is closed. Nothing else says so: the + // analyser keeps returning silence and the socket stays open, so the popup + // went on reading Live over a tab that no longer existed. + mediaStream.getAudioTracks().forEach((track) => { + track.onended = () => stop('The captured tab was closed.'); + }); - connectWs(); - tickTimer = setInterval(tick, config.sendIntervalMs || 33); - patchState({ running: true, manual: false, error: '' }); + analyser = audioCtx.createAnalyser(); + analyser.fftSize = 2048; + analyser.smoothingTimeConstant = 0.3; + freqBuf = new Float32Array(analyser.frequencyBinCount); + + sourceNode = audioCtx.createMediaStreamSource(mediaStream); + sourceNode.connect(analyser); + analyser.connect(audioCtx.destination); + + // "No sound yet" is said after QUIET_MS of nothing, counted from here. + heardAt = performance.now(); + + connectWs(); + tickTimer = setInterval(tick, TICK_MS); + } catch (failure) { + // Half a capture is not one: undo what was set up, then let the caller + // say why. + await stop(); + throw failure; + } } -async function manualConnect(nextConfig) { +async function manualConnect(target) { await stop(); - config = nextConfig; + address = String(target || '').trim(); + host = wsHost(); manual = true; wsWanted = true; - resetOutputBaselines(); connectWs(); - patchState({ running: false, manual: true, error: '' }); -} - -function sendManualValue(knob, value) { - const idx = Math.max(0, Math.min(3, Number(knob) || 0)); - const normalized = Math.max(0, Math.min(1, Number(value) || 0)); - const outputs = [0, 0, 0, 0]; - outputs[idx] = normalized; - sendOutputValue(idx, normalized); - patchState({ outputs }); + tickTimer = setInterval(tick, TICK_MS); } -function clamp01(value) { - return Math.max(0, Math.min(1, Number(value) || 0)); +// The popup committed another address while something is connected: follow it. +function retarget(target) { + if (!wsWanted) return; + const was = host; + address = String(target || '').trim(); + host = wsHost(); + if (host === was) return; + error = ''; + newStreak(); + resetWire(); // a different panel has been told nothing yet + connectWs(); } function hzToBin(hz) { @@ -256,158 +483,95 @@ function hzToBin(hz) { // Raw level only. Gain used to be folded in here, which meant it scaled the // signal BEFORE the input window clipped it — so raising boost also slid the // band out of its own window, and the two controls fought. It shapes the -// curve now, in mapBandOutput, which is the curve the popup draws. -function normalizeDb(db) { - return clamp01((db + 80) / 70); -} - -function bandEnergy(band) { - const minBin = Math.max(0, hzToBin(band.hzMin)); - const maxBin = Math.min(freqBuf.length - 1, hzToBin(band.hzMax)); +// curve now (PFMap.curveValue), which is the curve the editor draws. +function binsLevel(minBin, maxBin) { + minBin = Math.max(0, minBin); + maxBin = Math.min(freqBuf.length - 1, maxBin); if (maxBin < minBin) return 0; let sum = 0; for (let i = minBin; i <= maxBin; i++) sum += freqBuf[i]; - const avgDb = sum / (maxBin - minBin + 1); - return normalizeDb(avgDb); -} - -// The whole chain: -// -// level -> window (manual in/full, or the auto-tracked envelope) -// -> response curve -> [rests at .. peaks at] -// -// The response curve has two generations. Bands the editor has touched carry -// a baked 33-point lookup table (band.lut) — any shape at all, including -// falling and non-monotonic ones — and this side only interpolates, exactly -// like the firmware will. Bands never touched keep the legacy boost exponent -// (band.gain): above 1x a quiet band reaches its top early, below 1x a loud -// one holds back. Ends stay put either way. -function curveValue(band, u) { - const lut = band.lut; - if (Array.isArray(lut) && lut.length >= 2) { - const pos = clamp01(u) * (lut.length - 1); - const i = Math.floor(pos); - const j = Math.min(lut.length - 1, i + 1); - const f = pos - i; - return clamp01(Number(lut[i]) || 0) * (1 - f) + clamp01(Number(lut[j]) || 0) * f; - } - const gain = Math.max(0.2, Math.min(4, Number(band.gain) || 1)); - return Math.pow(clamp01(u), 1 / gain); -} - -function mapBandOutput(energy, band, index) { - let u; - if (config.autoRange && index !== undefined) { - // The band's own recent floor..peak is the window; a fixed relative - // squelch keeps the resting noise of the window's bottom from dancing. - const lo = envLo[index]; - const span = Math.max(0.001, envHi[index] - envLo[index]); - u = clamp01((clamp01((energy - lo) / span) - AUTO_LO) / (AUTO_HI - AUTO_LO)); - } else { - const inMin = clamp01(band.inMin ?? 0); - const inMax = Math.max(inMin + 0.01, clamp01(band.inMax ?? 1)); - u = clamp01((energy - inMin) / (inMax - inMin)); - } - const outMin = clamp01(band.outMin ?? 0); - const outMax = clamp01(band.outMax ?? 1); - return outMin + curveValue(band, u) * (outMax - outMin); + return PFMap.normalizeDb(sum / (maxBin - minBin + 1)); } function computeSpectrum() { const count = 64; const values = []; - const logMin = Math.log10(MIN_HZ); - const logMax = Math.log10(MAX_HZ); + const logMin = Math.log10(PFMap.SOURCE.hzMin); + const logMax = Math.log10(PFMap.SOURCE.hzMax); for (let i = 0; i < count; i++) { - const t0 = i / count; - const t1 = (i + 1) / count; - const hz0 = 10 ** (logMin + (logMax - logMin) * t0); - const hz1 = 10 ** (logMin + (logMax - logMin) * t1); - const minBin = Math.max(0, hzToBin(hz0)); - const maxBin = Math.min(freqBuf.length - 1, hzToBin(hz1)); - if (maxBin < minBin) { - values.push(0); - continue; - } - let sum = 0; - for (let bin = minBin; bin <= maxBin; bin++) sum += freqBuf[bin]; - values.push(normalizeDb(sum / (maxBin - minBin + 1))); + const hz0 = 10 ** (logMin + (logMax - logMin) * (i / count)); + const hz1 = 10 ** (logMin + (logMax - logMin) * ((i + 1) / count)); + values.push(binsLevel(hzToBin(hz0), hzToBin(hz1))); } return values; } function tick() { + // The test connection has no audio: it keeps saying where the sliders are. + if (manual) { + sendLanes(manualLanes); + return; + } if (!running || !analyser || !freqBuf || !config) return; analyser.getFloatFrequencyData(freqBuf); - const bands = config.bands || []; - const outputs = []; - const levels = []; - const alpha = config.smoothing ?? 0.35; - - while (smoothing.length < bands.length) smoothing.push(0); - if (smoothing.length > bands.length) smoothing.length = bands.length; - - // A lane nothing drives stays null, and goes out as '-'. Two bands on the - // same knob: the last one wins, deterministically, rather than whichever - // happened to get the socket first. - const lanes = [null, null, null, null]; + const bands = config.bands; + const auto = config.autoRange; // Levels first, for every band including muted ones — the editor shows a // muted band's level so you can see what it WOULD do, and the auto-range // envelopes keep tracking so unmuting does not open on a stale window. - for (let i = 0; i < bands.length; i++) { - const raw = bandEnergy(bands[i]); + for (let i = 0; i < 4; i++) { + const raw = binsLevel(hzToBin(bands[i].hzMin), hzToBin(bands[i].hzMax)); // Glide ballistics: a hit ATTACKS at its own speed, the fall RELEASES at // the damping - two user-set alphas. Symmetric smoothing made percussive // music feel late; this is the VU-meter split every reactive light wants. - const atk = Math.max(0.05, Math.min(0.9, config.attack ?? 0.65)); - const a = raw > smoothing[i] ? atk : alpha; - smoothing[i] = a * raw + (1 - a) * smoothing[i]; - levels[i] = smoothing[i]; - } - if (config.autoRange) trackEnvelopes(levels); - - for (let i = 0; i < bands.length; i++) { - const band = bands[i]; - const idx = Math.max(0, Math.min(3, Number(band.knob) || 0)); - if (band.muted) { - outputs[i] = 0; - continue; - } - const mapped = mapBandOutput(levels[i], band, i); - outputs[i] = mapped; - lanes[idx] = mapped; + const a = raw > levels[i] ? config.attack : config.smoothing; + levels[i] = a * raw + (1 - a) * levels[i]; + if (auto) PFMap.trackEnvelope(envelopes[i], levels[i]); } - // Muting a band hands its lane back once, and only if this client had it. + // The mapping itself is PFMap's: where each level sits in its window, and + // what the knob gets for that. Both go out with the levels, so the editor + // can draw what was mapped instead of working it out a second time. + // + // A lane nothing drives stays null, and goes out as '-'. + const lanes = [null, null, null, null]; for (let i = 0; i < 4; i++) { - if (lanes[i] === null && lastSentValues[i] !== -1) { - send(`off=${i}`, { control: true }); - lastSentValues[i] = -1; - } else if (lanes[i] !== null) { - lastSentValues[i] = lanes[i]; + const pos = PFMap.position(levels[i], bands[i], auto ? envelopes[i] : null); + const output = PFMap.output(bands[i], pos); + if (!bands[i].muted) lanes[bands[i].knob] = output; + if (levels[i] > peakLevels[i]) { + peakLevels[i] = levels[i]; + peakPos[i] = pos; + peakOutputs[i] = output; } } sendLanes(lanes); const now = performance.now(); - if (now - lastLevelReport > 120) { + if (now - lastLevelReport > REPORT_MS) { lastLevelReport = now; + const spectrum = computeSpectrum(); + // Flat is what the plot shows as flat: nothing above a hundredth of it. + if (spectrum.some((v) => v > 0.01)) { + heard = true; + heardAt = now; + } patchState({ - levels, - outputs, - spectrum: computeSpectrum(), + levels: peakLevels.slice(), + pos: peakPos.slice(), + outputs: peakOutputs.slice(), + spectrum, // The editor draws these as each box's breathing top and bottom edge. - env: config.autoRange - ? envLo.map((lo, i) => ({ lo, hi: envHi[i] })) - : [], - autoRange: config.autoRange === true + env: auto ? envelopes.map((e) => ({ lo: e.lo, hi: e.hi })) : [], + autoRange: auto }); + peakLevels = [-1, -1, -1, -1]; } } @@ -415,49 +579,52 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.target !== 'offscreen') return; (async () => { - if (message.type === 'start') { - await start(message.streamId, message.config); - sendResponse({ ok: true }); - return; - } - - if (message.type === 'stop') { - await stop(); - config = null; - sendResponse({ ok: true }); - return; - } - - if (message.type === 'config') { - const oldUrl = config ? wsUrl() : ''; - config = message.config; - if (running) resetOutputBaselines(); - if (ws && oldUrl !== wsUrl()) connectWs(); - sendResponse({ ok: true }); - return; - } - - if (message.type === 'manual-connect') { - await manualConnect(message.config); - sendResponse({ ok: true }); - return; - } - - if (message.type === 'manual-value') { - sendManualValue(message.knob, message.value); - sendResponse({ ok: true }); - return; - } - - if (message.type === 'release') { - send('off', { control: true }); - resetOutputBaselines(); - sendResponse({ ok: true }); - return; + // What went wrong with the request itself, for whoever sent it. Not a + // fault of the capture, so it does not go into `error`. + let refused = ''; + + switch (message.type) { + case 'start': + await start(message); + break; + case 'manual-connect': + await manualConnect(message.host); + break; + case 'stop': + await stop(); + break; + case 'host': + retarget(message.host); + break; + case 'config': + // Only a running capture has a use for a mapping; the next Start + // brings its own. What changed goes out with the next tick: a new + // value as a new frame, a band that was muted as `off=N`. + if (running) config = PFMap.normalizeConfig(message.config, PFMap.SOURCE); + break; + case 'manual-value': + if (manual) { + manualLanes[Math.max(0, Math.min(3, Number(message.knob) || 0))] = PFMap.clamp01(message.value); + sendLanes(manualLanes); + } + break; + case 'panel-on': + refused = await turnOn(); + break; + case 'panel-check': + if (connected() && !checking) await checkPanel(false); + break; + case 'status': + break; + default: + sendResponse({ ok: false, error: 'Unknown message', state: status() }); + return; } - })().catch((error) => { - patchState({ running: false, connected: false, error: String(error.message || error) }); - sendResponse({ ok: false, error: String(error.message || error) }); + sendResponse({ ok: !refused, error: refused || undefined, state: status() }); + })().catch((failure) => { + error = String(failure.message || failure); + patchState(); + sendResponse({ ok: false, error, state: status() }); }); return true; diff --git a/tools/patternflow-audio-extension/popup.css b/tools/patternflow-audio-extension/popup.css index cc83cb1b..742541b6 100644 --- a/tools/patternflow-audio-extension/popup.css +++ b/tools/patternflow-audio-extension/popup.css @@ -1,9 +1,7 @@ :root { --cream: #f4efe6; - --cream-2: #ede7db; --ink: #141414; --muted: #6b655a; - --faint: #a69f90; --rule: #d9d1c0; --led: #e8552e; --ok: #267a43; @@ -15,6 +13,26 @@ box-sizing: border-box; } +/* `hidden` has to win over the display values set below. */ +[hidden] { + display: none !important; +} + +/* Keyboard focus. The browser's own ring is near black, and on the filled + buttons that is black on black. */ +:focus-visible { + outline: 2px solid var(--led); + outline-offset: 2px; +} + +/* A text field shows its ring on a mouse click too. In the accent colour + that read as a warning, right against the red border of text that is not + an address, so the address keeps an ink ring. */ +input[type="text"]:focus-visible { + outline-color: var(--ink); + outline-offset: 0; +} + body { width: 460px; max-height: 620px; @@ -27,9 +45,7 @@ body { } header, -.section-head, -.band-head, -.band-actions { +.section-head { display: flex; align-items: center; } @@ -69,12 +85,18 @@ section { background: transparent; } +/* Neutral unless it has something to claim: green is "the panel is being + driven" (or the test connection is up), red is something wrong. Idle and + connecting are neither. Colour is the only thing that tells Live from + Idle, so the word is heavy enough to carry it. */ .status { min-width: 82px; padding: 5px 8px 4px; border: 1px solid var(--ink); background: var(--field); - font-size: 10px; + color: var(--muted); + font-size: 11px; + font-weight: 600; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; letter-spacing: .12em; text-align: center; @@ -101,25 +123,26 @@ section { } input, -select, button { font: inherit; } -input[type="text"], -select { +input[type="text"] { width: 100%; + padding: 8px 9px; border: 1px solid var(--rule); background: var(--field); color: var(--ink); } -input[type="text"] { - padding: 8px 9px; +/* Text that is not an address: nothing was stored, nothing connects. The + ring of a focused field would hide a red border, so it turns red with it. */ +input[aria-invalid="true"] { + border-color: var(--bad); } -select { - padding: 3px 6px; +input[aria-invalid="true"]:focus-visible { + outline-color: var(--bad); } button { @@ -139,28 +162,25 @@ button:hover { color: var(--ink); } -.small-button, -.remove-band { - min-height: 26px; - padding: 5px 8px; -} - -.remove-band { +/* A control that has nothing to act on in the current state. */ +button:disabled { border-color: var(--rule); - background: transparent; + background: var(--rule); color: var(--muted); + cursor: default; } -.actions { - display: grid; - /* Two, since WS Test moved into the diagnostics fold. */ - grid-template-columns: 1fr 1fr; - gap: 7px; +/* The second button of a section: an action offered, not the main one. */ +button.quiet { margin-top: 8px; + padding: 0 14px; + background: transparent; + color: var(--ink); } -.actions button { - padding: 8px; +button.quiet:hover { + background: var(--ink); + color: var(--cream); } .detail { @@ -175,6 +195,13 @@ button:hover { gap: 8px; } +/* A test connection has no audio, so the plot has nothing to show, and with + it the open fold made the popup taller than the 600 px Chrome allows: K3 + and K4 ended up under the edge. The fold takes the plot's place. */ +body:has(#test[open]) .spectrum-section { + display: none; +} + .spectrum-wrap { position: relative; height: 132px; @@ -188,223 +215,29 @@ button:hover { height: 100%; } -.spectrum-bands { - position: absolute; - inset: 0; - pointer-events: none; -} - -.spectrum-band { - position: absolute; - top: 0; - bottom: 0; - border-left: 1px solid var(--led); - border-right: 1px solid var(--led); - background: rgba(232, 85, 46, .16); -} - -.spectrum-band span { - position: absolute; - left: 5px; - top: 5px; - color: var(--ink); - font-size: 10px; - font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; -} - +/* The plot is logarithmic, so the labels are placed, not spread: popup.js + gives each one its `left`. Spread evenly, "1k" sat over 317 Hz. */ .spectrum-scale { - display: grid; - grid-template-columns: repeat(5, 1fr); + position: relative; + height: 15px; margin-top: 5px; color: var(--muted); font-size: 10px; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; } -.spectrum-scale span:last-child { - text-align: right; -} - -.global { - display: grid; - grid-template-columns: 1fr 42px; - align-items: center; - gap: 8px; -} - -.global label, -.manual-grid label { - display: grid; - align-items: center; - gap: 8px; - color: var(--muted); - font-size: 11px; -} - -.global label { - grid-template-columns: 74px 1fr; -} - -.manual-grid { - display: grid; - gap: 8px; -} - -.manual-grid label { - grid-template-columns: 28px 1fr 40px; -} - -.value, -output { - color: var(--ink); - text-align: right; - font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; - font-variant-numeric: tabular-nums; -} - -.band { - margin: 0; - padding-top: 12px; -} - -.band-head { - justify-content: space-between; - gap: 8px; - margin-bottom: 8px; -} - -.band-title { - font-size: 11px; - font-weight: 600; - text-transform: uppercase; -} - -.band-actions { - gap: 7px; -} - -.target { - display: grid; - grid-template-columns: auto 48px; - align-items: center; - gap: 5px; - color: var(--muted); - font-size: 11px; -} - -.band-strip, -.map-row { - display: grid; - align-items: center; - gap: 8px; - margin: 8px 0; -} - -.band-strip { - grid-template-columns: 1fr 1fr 92px; -} - -.map-row { - grid-template-columns: 48px 1fr 92px; -} - -.map-row > span, -.row span { - color: var(--muted); - font-size: 10px; - font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; - text-transform: uppercase; -} - -.band-readout { - display: grid; - grid-template-columns: 1fr auto 1fr; - gap: 4px; - align-items: center; - color: var(--muted); - font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; - font-size: 10px; -} - -.map-sliders { - display: grid; - gap: 4px; -} - -.row { - display: grid; - grid-template-columns: 54px 1fr 48px; - align-items: center; - gap: 8px; - margin: 6px 0; -} - -details.advanced { - margin-top: 8px; -} - -details.advanced summary { - color: var(--muted); - cursor: pointer; - font-size: 10px; - font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; - letter-spacing: .08em; - text-transform: uppercase; -} - -input[type="range"] { - width: 100%; - accent-color: var(--ink); -} - -.meter { - height: 6px; - margin-top: 8px; - overflow: hidden; - background: var(--cream-2); - border: 1px solid var(--rule); -} - -.meter.out { - height: 3px; - margin-top: 3px; -} - -.meter div { - width: 0; - height: 100%; - background: var(--led); - transition: width .08s linear; -} - -.meter.out div { - background: var(--ink); -} - -/* ── Named frequency ranges, per band ──────────────────────────────────── - The sliders are log-scaled now, so a range is reachable by hand — but - nobody should have to drag for "bass" when the name is the thing they - mean. Same six ranges the device's own audio page offers. */ -.hz-presets { - display: flex; - flex-wrap: wrap; - gap: 4px; - margin-top: 6px; +.spectrum-scale span { + position: absolute; + top: 0; + transform: translateX(-50%); } -.hz-presets button { - min-height: 0; - padding: 3px 7px; - font-size: 10px; - letter-spacing: 0.04em; - border-color: var(--rule); - background: transparent; - color: var(--muted); +.spectrum-scale span:first-child { + transform: none; } -.hz-presets button:hover { - color: var(--ink); - border-color: var(--ink); +.spectrum-scale span:last-child { + transform: translateX(-100%); } /* ── Diagnostics, folded ───────────────────────────────────────────────── @@ -428,241 +261,40 @@ input[type="range"] { width: 100%; } -/* ── Band selector ─────────────────────────────────────────────────────── - Four bands, one editor on screen. These say which one you are on, which - knob it drives, and whether it is muted — the three things worth knowing - before you switch. */ -.band-tabs { +.manual-grid { display: grid; - /* Four bands and All. */ - grid-template-columns: repeat(4, 1fr) 1.2fr; - gap: 6px; - margin-top: 10px; + gap: 8px; } -.band-tab { - display: flex; - flex-direction: column; +.manual-grid label { + display: grid; + grid-template-columns: 28px 1fr 40px; align-items: center; - gap: 1px; - padding: 6px 0 5px; - min-height: 0; - border-color: var(--rule); - background: transparent; - color: var(--muted); -} - -.band-tab strong { font-size: 13px; font-weight: 600; } -.band-tab span { font-size: 9px; letter-spacing: 0.08em; } - -.band-tab:hover { color: var(--ink); border-color: var(--ink); } - -.band-tab.active { - background: var(--ink); - border-color: var(--ink); - color: var(--cream); -} - -/* A muted band stays in place and stays reachable — it just is not driving - anything, and should not look like it is. */ -.band-tab.muted { opacity: 0.45; } -.band-tab.muted strong { text-decoration: line-through; } - -.spectrum-band { cursor: pointer; } -.spectrum-band.muted { opacity: 0.35; } - -.mute-band { - min-height: 26px; - padding: 5px 8px; - border-color: var(--rule); - background: transparent; - color: var(--muted); -} - -.mute-band.on { - border-color: var(--led); - color: var(--led); -} - -.hint { - font-size: 10px; - letter-spacing: 0.04em; - color: var(--faint); -} - -/* `hidden` loses to any explicit display, and .band sets one. Say it again. */ -.band[hidden] { display: none; } - -.band-tab.all strong { font-size: 11px; letter-spacing: 0.06em; } - -/* ── The response graph ────────────────────────────────────────────────── - Six sliders became one picture. Left to right is how loud the band is, - bottom to top is where the knob lands, and the line between them is every - number at once — which is the thing the sliders could not show. */ -.band-row { - display: flex; - align-items: baseline; - justify-content: space-between; - gap: 10px; - margin-bottom: 6px; -} - -.band-key { + gap: 8px; color: var(--muted); - font-size: 10px; - letter-spacing: 0.14em; - text-transform: uppercase; - font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; - white-space: nowrap; -} - -.band-note { color: var(--faint); font-size: 10px; } - -.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; } - -.plot-wrap { display: flex; gap: 10px; } - -.plot-axis { - display: flex; - flex-direction: column; - justify-content: space-between; - align-items: flex-end; - width: 26px; - height: 180px; - padding: 2px 0; - color: var(--faint); - font-size: 9px; - font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + font-size: 11px; } -.plot-col { flex: 1; min-width: 0; } - -.plot { - position: relative; - height: 180px; - border: 1px solid var(--ink); - background: var(--field); - overflow: hidden; - cursor: grab; - /* The handles are dragged; the popup must not pan or select instead. */ - touch-action: none; - user-select: none; +output { color: var(--ink); -} - -.plot:active { cursor: grabbing; } - -/* the stretch of knob the music is allowed to use */ -.plot-out { position: absolute; left: 0; right: 0; background: #f0e6d6; } -/* the levels this band responds to at all */ -.plot-in { - position: absolute; top: 0; bottom: 0; - border-left: 1px dashed var(--faint); - border-right: 1px dashed var(--faint); -} - -.plot-curve { position: absolute; inset: 0; width: 100%; height: 100%; } - -.plot-vline { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--led); opacity: .55; } -.plot-hline { position: absolute; left: 0; right: 0; height: 1px; background: var(--led); opacity: .55; } - -.plot-dot { - position: absolute; - width: 9px; height: 9px; - margin: -5px 0 0 -5px; - border-radius: 50%; - background: var(--led); - box-shadow: 0 0 0 2px var(--field); -} - -/* Handles: triangles pointing at the edge they move. Input on the floor, - output on the right wall — so which axis a handle belongs to is visible - before it is dragged. */ -.h { position: absolute; width: 14px; height: 14px; background: var(--ink); } -.h-in-min, .h-in-max { bottom: 0; margin-left: -7px; clip-path: polygon(50% 0, 100% 100%, 0 100%); } -.h-out-min, .h-out-max { right: 0; margin-top: -7px; clip-path: polygon(0 50%, 100% 0, 100% 100%); } -.h.on { background: var(--led); } - -.plot-scale { - display: flex; - justify-content: space-between; - margin-top: 3px; - color: var(--faint); - font-size: 9px; + text-align: right; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + font-variant-numeric: tabular-nums; } -/* The same four numbers as words, for anyone who wants to read rather than - aim — and so a value can be checked without hovering a handle. */ -.readout { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 7px 14px; - margin-top: 12px; - padding: 10px 11px; - background: var(--field); - border: 1px solid var(--rule); -} - -.readout > div { - display: flex; - align-items: baseline; - justify-content: space-between; - gap: 8px; -} - -.readout span { color: var(--muted); font-size: 11px; } -.readout output { font-size: 11px; } - -/* Boost bends the line rather than moving its ends, so it is not something - to drag on the plot. Seven steps, and the hint says what the shape means. */ -.boost-row { display: flex; align-items: center; gap: 12px; margin-top: 13px; } -.boost-steps { flex: 1; display: flex; gap: 3px; } - -.boost-steps button { - flex: 1; - min-height: 0; - height: 22px; - padding: 0; - border-color: var(--rule); - background: var(--field); +input[type="range"] { + width: 100%; + accent-color: var(--ink); } -.boost-steps button.soft { background: var(--cream-2); } -.boost-steps button.on { background: var(--ink); border-color: var(--ink); } -.boost-steps button:hover { border-color: var(--ink); } - -.boost-row .gainOut { width: 34px; text-align: right; font-size: 11px; } -.boost-hint { margin-top: 6px; color: var(--muted); font-size: 11px; } - -/* The meters said nothing about which was which. */ -.meter-row { display: flex; align-items: center; gap: 8px; margin-top: 8px; } -.meter-row .meter { flex: 1; margin-top: 0; } - -.meter-key { - width: 26px; - color: var(--faint); - font-size: 9px; - letter-spacing: 0.1em; - text-transform: uppercase; - font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; +input[type="range"]:disabled { + opacity: .45; } -/* How much of its knob this band actually uses. The number that names a - band which is technically working and doing nothing. */ -.travelOut { width: 42px; text-align: right; font-size: 10px; color: var(--muted); } -.travelOut.weak { color: var(--led); } - -/* The one road to the mapping editor — mapping no longer lives in the popup. */ -.editor-cta { +/* The one road to the mapping editor: mapping no longer lives in the popup. + Full width, and outlined like every second action (button.quiet), so the + one filled button of the popup's first screen is Start. */ +button.editor-cta { width: 100%; - font: inherit; - font-weight: 600; - cursor: pointer; - border: 1px solid var(--ink); - border-radius: 2px; - background: var(--ink); - color: var(--cream); - padding: 9px 12px; + margin-top: 0; } -.editor-cta:hover { background: var(--led); border-color: var(--led); } diff --git a/tools/patternflow-audio-extension/popup.html b/tools/patternflow-audio-extension/popup.html index af06a852..40940e9b 100644 --- a/tools/patternflow-audio-extension/popup.html +++ b/tools/patternflow-audio-extension/popup.html @@ -12,27 +12,27 @@

              Patternflow Audio

              -
              Idle
              +
              Idle
              -
              + +
              - -
              + +
              Open the tab you want to hear, then press Start.
              -
              - - -
              + +
              - -
              Bands, boxes and response curves live there.
              + +
              Boxes and response curves for K1–K4 live there.
              @@ -42,31 +42,27 @@

              Patternflow Audio

              -
              - 20 - 100 - 1k - 10k - 20k -
              + +
              -
              +
              Test without audio - -
              - - - - -
              + +
              + + + + +
              + diff --git a/tools/patternflow-audio-extension/popup.js b/tools/patternflow-audio-extension/popup.js index 02ea9437..200fcd36 100644 --- a/tools/patternflow-audio-extension/popup.js +++ b/tools/patternflow-audio-extension/popup.js @@ -1,45 +1,37 @@ // Patternflow Audio — popup: the capture console. // -// This used to also be the mapping editor — band strips, drag handles, boost -// buttons — until the real editor moved to its own tab (editor.html), where -// boxes on a spectrum have room to be dragged. What remains here is what a -// popup is good at: start/stop the capture, point at the device, see that -// audio is flowing, and one button to the place where mapping actually -// happens. The popup never edits bands anymore; it only ships the stored -// config along with start/config messages. - -const MIN_HZ = 20; -const MAX_HZ = 20000; - -const DEFAULT_CONFIG = { - host: 'patternflow.local', - smoothing: 0.35, - sendIntervalMs: 33, - // Four, because the panel has four knobs. Shipping one meant a first run - // moved a single knob and looked broken; the device page has defaulted to - // four the whole time. - bands: [ - { hzMin: 60, hzMax: 250, knob: 0, outMin: 0.30, outMax: 0.85, inMin: 0, inMax: 1, gain: 1 }, - { hzMin: 250, hzMax: 2000, knob: 1, outMin: 0.30, outMax: 0.85, inMin: 0, inMax: 1, gain: 1 }, - { hzMin: 2000, hzMax: 5000, knob: 2, outMin: 0.30, outMax: 0.85, inMin: 0, inMax: 1, gain: 1 }, - { hzMin: 5000, hzMax: 16000, knob: 3, outMin: 0.30, outMax: 0.85, inMin: 0, inMax: 1, gain: 1 } - ] -}; - -let config = structuredClone(DEFAULT_CONFIG); +// What a popup is good at: start and stop the capture, say where the panel +// is, see that audio is flowing, and one button to the tab where mapping +// happens (editor.html). Two things are this file's and nobody else's. +// +// The panel's address. It has a storage key of its own, so the editor, +// which owns the mapping (PFMap.CONFIG_KEY), and the popup never write the +// same value, and an editor tab left open cannot put an old address back. +// It is committed on Enter, on leaving the field and on Start. Committing +// per keystroke dropped a live socket for every character and tried +// 192.168.0.4 on the way to 192.168.0.42. +// +// Asking for the capture. The tab's stream id needs the click that opened +// this popup. +// +// The mapping is read at Start and handed over as it is stored; nothing here +// edits it or writes it. What is running, what it is connected to, whether +// the panel is listening and why a capture ended belong to the offscreen +// document: it sums them up as one `phase`, and this file prints that. Every +// control here follows it too, so none is offered that has nothing to act on. + +const HOST_KEY = 'patternflowAudioHost'; +const DEFAULT_HOST = 'patternflow.local'; + +let host = DEFAULT_HOST; // the committed address +let hostError = ''; // why the field's text is not an address +let failure = ''; // what the last thing asked for here could not do let state = null; -let saveTimer = null; const $ = (id) => document.getElementById(id); -const clamp01 = (value) => Math.max(0, Math.min(1, Number(value) || 0)); -const clampHz = (value) => Math.max(MIN_HZ, Math.min(MAX_HZ, Number(value) || MIN_HZ)); -function storageGet(key) { - return new Promise((resolve) => chrome.storage.local.get(key, resolve)); -} - -function storageSet(value) { - return new Promise((resolve) => chrome.storage.local.set(value, resolve)); +function storageGet(keys) { + return new Promise((resolve) => chrome.storage.local.get(keys, resolve)); } function sendMessage(message) { @@ -60,123 +52,155 @@ function getStreamId(tabId) { }); } -function newBand(index = config.bands.length) { - const presets = [ - [60, 250], - [250, 2000], - [2000, 5000], - [5000, 16000] - ]; - const [hzMin, hzMax] = presets[index % presets.length]; - return { - hzMin, - hzMax, - knob: Math.min(index, 3), - outMin: 0.30, - outMax: 0.85, - inMin: 0.00, - inMax: 1.00, - gain: 1.00 - }; +// What someone typed, as the address to store: a host name or an IP, with a +// port only if they gave one. A pasted URL loses its scheme and path. Returns +// null when what is left is not an address. +// +// The shape is checked here and not left to `new URL`: Chrome's parser turns +// "not a host" into not%20a%20host and calls it valid. +const ADDRESS = /^(\[[0-9a-f:.]+\]|[\p{L}\p{N}](?:[\p{L}\p{N}._-]*[\p{L}\p{N}])?)(?::(\d+))?$/iu; + +function parseHost(text) { + const value = String(text || '').trim() + .replace(/^[a-z][a-z0-9+.-]*:\/\//i, '') + .replace(/[/?#].*$/, ''); + if (!value) return DEFAULT_HOST; + + const parts = ADDRESS.exec(value); + if (!parts) return null; + const [, name, port] = parts; + if (port !== undefined && !(Number(port) >= 1 && Number(port) <= 65535)) return null; + try { + // The parser's spelling of it: lower case, 192.168.1 written out in full. + return new URL(`ws://${name}`).hostname + (port === undefined ? '' : `:${Number(port)}`); + } catch (error) { + return null; + } } -function normalizeBand(raw, index) { - const legacyBase = Number.isFinite(Number(raw.base)) ? Number(raw.base) : 0.30; - const legacyRange = Number.isFinite(Number(raw.range)) ? Number(raw.range) : 0.55; - let outMin = Number.isFinite(Number(raw.outMin)) ? Number(raw.outMin) : legacyBase; - let outMax = Number.isFinite(Number(raw.outMax)) ? Number(raw.outMax) : legacyBase + legacyRange; - - outMin = clamp01(outMin); - outMax = clamp01(outMax); - if (outMin > outMax) [outMin, outMax] = [outMax, outMin]; - - let inMin = Number.isFinite(Number(raw.inMin)) ? clamp01(raw.inMin) : 0; - let inMax = Number.isFinite(Number(raw.inMax)) ? clamp01(raw.inMax) : 1; - if (inMin > inMax) [inMin, inMax] = [inMax, inMin]; - if (inMax - inMin < 0.01) inMax = Math.min(1, inMin + 0.01); - - let hzMin = clampHz(raw.hzMin ?? 60); - let hzMax = clampHz(raw.hzMax ?? 250); - if (hzMin > hzMax) [hzMin, hzMax] = [hzMax, hzMin]; - if (hzMax - hzMin < 10) hzMax = Math.min(MAX_HZ, hzMin + 10); - - const band = { - hzMin, - hzMax, - knob: Math.max(0, Math.min(3, Number(raw.knob ?? index) || 0)), - muted: raw.muted === true, - outMin, - outMax, - inMin, - inMax, - gain: Math.max(0.2, Math.min(4, Number(raw.gain) || 1)) - }; - // The editor's fields ride through untouched. Rebuilding the band without - // them meant every popup save quietly erased the curves — Start wiped the - // mapping it was about to use. - if (raw.curve && typeof raw.curve === 'object') band.curve = raw.curve; - if (Array.isArray(raw.lut) && raw.lut.length >= 2) band.lut = raw.lut; - return band; +// state.host is the address as the socket uses it, port included. In a +// sentence the port every panel has is left off: the address as it was typed. +function shown(socketHost) { + return String(socketHost || '').replace(/:81$/, ''); } -// Four bands, always, because the panel has four knobs. -function normalizeConfig(raw) { - const stored = Array.isArray(raw?.bands) ? raw.bands : []; - const bands = []; - for (let i = 0; i < 4; i++) { - bands.push(normalizeBand(stored[i] || newBand(i), i)); +// Chrome's own words for the two ways it refuses a capture, in plain ones. +// Anything else is passed on as it came. +function plain(message) { + const text = String(message || ''); + if (/active stream/i.test(text)) { + return 'This tab is already being captured. Press Stop first.'; } - - return { - ...DEFAULT_CONFIG, - ...raw, - smoothing: Math.max(0.05, Math.min(0.9, Number(raw?.smoothing) || DEFAULT_CONFIG.smoothing)), - bands - }; + if (/activeTab|cannot be captured/i.test(text)) { + return 'Chrome does not let this page be captured. Go to the tab that is playing, open this popup there and press Start.'; + } + return text; } -function persistConfig() { - clearTimeout(saveTimer); - saveTimer = setTimeout(async () => { - await storageSet({ patternflowAudioConfig: config }); - await sendMessage({ type: 'config', config }); - }, 120); +// Take the field's text as the address. Returns it, or null when the text is +// not one; then nothing is stored and nothing connects. +function commitHost() { + const field = $('host'); + const next = parseHost(field.value); + if (!next) { + hostError = `"${field.value.trim()}" is not an address. Use a name or an IP, like patternflow.local or 192.168.0.42.`; + field.setAttribute('aria-invalid', 'true'); + renderState(); + return null; + } + + hostError = ''; + field.removeAttribute('aria-invalid'); + field.value = next; + if (next !== host) { + host = next; + chrome.storage.local.set({ [HOST_KEY]: host }); + // A capture or a test connection that is up follows the address. + if (state && (state.running || state.manual)) { + sendMessage({ type: 'host', host }).then((response) => { + if (response && response.state) state = response.state; + renderState(); + }); + } + } + renderState(); + return host; } -function setStatus(text, ok) { +// `tone`: 'ok' is green, 'bad' is red, nothing is neutral. +function setStatus(text, tone) { const el = $('status'); el.textContent = text; - el.className = 'status ' + (ok ? 'ok' : 'bad'); + el.className = 'status' + (tone ? ' ' + tone : ''); } +// The status, the sentence under the address and every control, from the +// state. The words are per phase (offscreen.js phase()); the editor's chip +// uses the same ones. function renderState() { - if (!state) { - setStatus('Idle', false); - drawSpectrum([]); - return; + const s = state || {}; + const at = shown(s.host); + + let label = 'Idle'; + let tone = ''; + let detail = s.note || 'Open the tab you want to hear, then press Start.'; + + if (failure) { + label = 'Error'; + tone = 'bad'; + detail = failure; + } else if (s.phase === 'error') { + label = 'Error'; + tone = 'bad'; + detail = plain(s.error); + } else if (s.phase === 'connecting') { + label = 'Connecting'; + detail = `Connecting to ${at}.`; + } else if (s.phase === 'off') { + // The socket is open and the panel ignores every frame: not live. + label = 'Aud off'; + detail = 'Connected, but Audio-React is off on the panel.'; + } else if (s.phase === 'live') { + label = 'Live'; + tone = 'ok'; + if (!s.silent) detail = s.tabTitle ? `Capturing: ${s.tabTitle}` : 'Capturing this tab.'; + else detail = s.heard ? 'No sound from this tab right now.' : 'No sound from this tab yet.'; + } else if (s.phase === 'test') { + label = 'Test'; + tone = 'ok'; + detail = `Test without audio: connected to ${at}. Move a slider.`; } - if (state.error) { - setStatus('Error', false); - $('detail').textContent = state.error; - } else if (state.running && state.connected) { - setStatus('Live', true); - $('detail').textContent = state.tabTitle ? `Capturing: ${state.tabTitle}` : 'Capturing current tab.'; - } else if (state.manual && state.connected) { - setStatus('WS Test', true); - $('detail').textContent = `Manual test connected to ws://${config.host}:81`; - } else if (state.running) { - setStatus('Connecting', false); - $('detail').textContent = `Connecting to ws://${config.host}:81`; - } else if (state.manual) { - setStatus('Connecting', false); - $('detail').textContent = `Connecting manual test to ws://${config.host}:81`; - } else { - setStatus('Idle', false); - $('detail').textContent = 'Open the tab you want to hear, then press Start.'; - } + // Text in the field that is not an address is about the field, which has + // its red border. Nothing was stored and nothing reconnected, so the status + // word stays what is true: a capture to the old address is still Live. + if (hostError) detail = hostError; + + setStatus(label, tone); + $('detail').textContent = detail; + + // One button: Start until a tab is captured, Stop while one is. + $('connect').textContent = s.running ? 'Stop' : 'Start'; + $('panelOn').hidden = s.phase !== 'off' || !!hostError; - drawSpectrum(state.spectrum || []); + // The test connection and a capture are one or the other. Its button + // disconnects what it connected; its sliders move a panel only while the + // test is up and the panel listens. + $('manualConnect').textContent = s.manual ? 'Disconnect' : 'Connect'; + $('manualConnect').disabled = !!s.running; + $('manualConnect').title = s.running ? 'Stop the capture first.' : ''; + document.querySelectorAll('.manual').forEach((input) => { + input.disabled = s.phase !== 'test'; + // A knob that is being held shows where it is held, also in a popup + // opened later. Not under the hand that is moving it. + const held = s.held && s.held[Number(input.dataset.knob)]; + if (typeof held === 'number' && input !== document.activeElement) { + input.value = held; + input.parentElement.querySelector('output').textContent = held.toFixed(2); + } + }); + + drawSpectrum(s.spectrum || []); } function drawSpectrum(values) { @@ -201,39 +225,68 @@ function drawSpectrum(values) { const barCount = Math.max(1, values.length); const barWidth = width / barCount; for (let i = 0; i < barCount; i++) { - const v = clamp01(values[i] || 0); + const v = PFMap.clamp01(values[i]); const h = Math.max(1, v * (height - 12)); ctx.fillStyle = i % 2 ? '#6b655a' : '#141414'; ctx.fillRect(i * barWidth, height - h, Math.max(1, barWidth - 1), h); } } +// The frequency labels under the plot. Its bars are equal steps of log +// frequency from the source's lowest to its highest (offscreen.js +// computeSpectrum), so a label belongs at the same fraction of the width. +function placeScale() { + const { hzMin, hzMax } = PFMap.SOURCE; + const span = Math.log(hzMax / hzMin); + for (const [hz, text] of [[20, '20'], [100, '100'], [1000, '1k'], [10000, '10k'], [20000, '20k']]) { + const mark = document.createElement('span'); + mark.textContent = text; + mark.style.left = `${(Math.log(hz / hzMin) / span * 100).toFixed(2)}%`; + $('scale').append(mark); + } +} + +// The reply to a request: the state to draw, or the reason there is none. +function adopt(response, fallback) { + if (response && response.state) state = response.state; + if (!response || !response.ok) throw new Error((response && response.error) || fallback); + renderState(); +} + async function startCapture() { - config.host = $('host').value.trim() || 'patternflow.local'; - await storageSet({ patternflowAudioConfig: config }); + const address = commitHost(); + if (!address) return; const [tab] = await tabQuery({ active: true, currentWindow: true }); if (!tab || !tab.id) throw new Error('No active tab to capture.'); const streamId = await getStreamId(tab.id); - const response = await sendMessage({ + const stored = await storageGet(PFMap.CONFIG_KEY); + adopt(await sendMessage({ type: 'start', streamId, tabTitle: tab.title || '', - config - }); + host: address, + config: stored[PFMap.CONFIG_KEY] || null + }), 'Failed to start capture.'); +} - if (!response || !response.ok) throw new Error(response?.error || 'Failed to start capture.'); - state = response.state; +async function startManual() { + const address = commitHost(); + if (!address) return; + adopt(await sendMessage({ type: 'manual-connect', host: address }), 'Failed to connect.'); +} + +async function stopAll() { + const response = await sendMessage({ type: 'stop' }); + if (response && response.state) state = response.state; renderState(); } -async function startManual() { - config.host = $('host').value.trim() || 'patternflow.local'; - await storageSet({ patternflowAudioConfig: config }); - const response = await sendMessage({ type: 'manual-connect', config }); - if (!response || !response.ok) throw new Error(response?.error || 'Failed to connect manual test.'); - state = response.state; +// Something asked for here did not happen. The state is still the offscreen +// document's; this is said over it until the next thing is asked. +function showFailure(error) { + failure = plain(error.message || error); renderState(); } @@ -242,11 +295,14 @@ function bindManualControls() { const output = input.parentElement.querySelector('output'); const update = async () => { output.textContent = Number(input.value).toFixed(2); - await sendMessage({ + const response = await sendMessage({ type: 'manual-value', knob: parseInt(input.dataset.knob, 10), value: parseFloat(input.value) }); + // Not drawn (nothing else changed), but kept: it carries where each + // knob is now held. + if (response && response.state) state = response.state; }; input.addEventListener('input', update); input.addEventListener('change', update); @@ -254,55 +310,137 @@ function bindManualControls() { } async function init() { - const stored = await storageGet('patternflowAudioConfig'); - config = normalizeConfig(stored.patternflowAudioConfig); - - $('host').value = config.host; + const stored = await storageGet([HOST_KEY, PFMap.CONFIG_KEY]); + if (typeof stored[HOST_KEY] === 'string') { + host = parseHost(stored[HOST_KEY]) || DEFAULT_HOST; + } else { + // Before the address had a key of its own it rode inside the mapping. + // Move it across once; the copy left behind is never read again. + const legacy = stored[PFMap.CONFIG_KEY] && stored[PFMap.CONFIG_KEY].host; + const moved = typeof legacy === 'string' ? parseHost(legacy) : null; + if (moved) { + host = moved; + chrome.storage.local.set({ [HOST_KEY]: host }); + } + } + $('host').value = host; + + // `change` is Enter and leaving the field. The popup closing is neither (it + // closes with the field still focused), so that is asked for by name. + $('host').addEventListener('change', commitHost); + window.addEventListener('pagehide', commitHost); + // "Not an address" was about the text as it stood. Once it is being typed + // over, the sentence goes; the next commit judges the new text. $('host').addEventListener('input', () => { - config.host = $('host').value.trim() || 'patternflow.local'; - persistConfig(); + if (!hostError) return; + hostError = ''; + $('host').removeAttribute('aria-invalid'); + renderState(); + }); + + // The form makes Enter in the address a submit, the same event as a click + // on the button, and the two are not always the same wish. + let typedEnter = false; + $('host').addEventListener('keydown', (event) => { + typedEnter = event.key === 'Enter'; }); - $('connect').addEventListener('click', async () => { + $('device').addEventListener('submit', async (event) => { + event.preventDefault(); + const entered = typedEnter; + typedEnter = false; + failure = ''; + const s = state || {}; + // A capture is running and the button reads Stop. A click on it stops. + // Enter in the address never does: there it means "this address", and + // the capture moves to it. + if (s.running) { + if (entered) commitHost(); + else await stopAll(); + return; + } + // Enter over a test connection means the same, not "capture". + if (entered && s.manual) { + commitHost(); + return; + } try { - setStatus('Starting', false); + setStatus('Starting'); await startCapture(); } catch (error) { - state = { running: false, connected: false, error: String(error.message || error) }; - renderState(); + showFailure(error); } }); $('manualConnect').addEventListener('click', async () => { + failure = ''; + if (state && state.manual) { + await stopAll(); + return; + } try { - setStatus('Connecting', false); + setStatus('Connecting'); await startManual(); } catch (error) { - state = { running: false, connected: false, manual: false, error: String(error.message || error) }; - renderState(); + showFailure(error); } }); - $('stop').addEventListener('click', async () => { - const response = await sendMessage({ type: 'stop' }); - state = response.state; + // Audio-React is a switch on the panel (the AUD row of its NETWORK screen); + // this throws it from here. + $('panelOn').addEventListener('click', async () => { + failure = ''; + $('panelOn').disabled = true; + const response = await sendMessage({ type: 'panel-on' }); + $('panelOn').disabled = false; + if (response && response.state) state = response.state; + if (!response || !response.ok) failure = (response && response.error) || 'The panel did not answer.'; renderState(); }); - $('release').addEventListener('click', () => sendMessage({ type: 'release' })); - // The mapping editor gets a real tab: boxes dragged on a spectrum need // more room than a popup that closes the moment focus leaves it. - $('openEditor').addEventListener('click', () => { - chrome.tabs.create({ url: chrome.runtime.getURL('editor.html') }); + // + // One tab. Every editor tab holds its own copy of the mapping and saves the + // whole of it, so a second tab's next edit put its older copy over the + // first one's work. If the editor is open anywhere, that tab comes forward. + $('openEditor').addEventListener('click', async () => { + const url = chrome.runtime.getURL('editor.html'); + let open = []; + try { + const tabs = await chrome.runtime.getContexts({ contextTypes: ['TAB'] }); + open = tabs.filter((tab) => String(tab.documentUrl).split(/[?#]/)[0] === url); + } catch (error) {} + if (!open.length) { + chrome.tabs.create({ url }); + return; + } + // Both asked for before either lands: the popup closes as soon as focus + // leaves it, and takes whatever was still waiting with it. + chrome.windows.update(open[0].windowId, { focused: true }); + chrome.tabs.update(open[0].tabId, { active: true }); }); bindManualControls(); + placeScale(); drawSpectrum([]); const response = await sendMessage({ type: 'status' }); state = response?.state || null; + // A test connection that is up is what this popup is about: show it. + if (state && state.manual) $('test').open = true; renderState(); + + // Whether the panel listens was learned when the socket opened, and the + // switch is on the panel (its AUD row, its console), where it may have been + // thrown either way since: "off" that is on again, or Live over a panel + // that now ignores every frame. Opening the popup is the moment someone + // looks, so ask once more: one request per popup, never a poll. + if (state && ['off', 'live', 'test'].includes(state.phase)) { + const again = await sendMessage({ type: 'panel-check' }); + if (again && again.state) state = again.state; + renderState(); + } } chrome.runtime.onMessage.addListener((message) => { diff --git a/web/eslint.config.mjs b/web/eslint.config.mjs index 9a5dd851..05e00001 100644 --- a/web/eslint.config.mjs +++ b/web/eslint.config.mjs @@ -17,6 +17,10 @@ const eslintConfig = defineConfig([ // fflate vendored beside them. `console_demo.py --check` keeps it in step // with the firmware; lint rules for hand-written code don't apply. "public/guide/console-demo/**", + // The Audio guide's editor is generated the same way: the audio + // extension's own editor files plus a bridge to the page, by + // firmware/toolchain/editor_demo.py. + "public/guide/audio-editor/**", ]), // Layering: lib/ is the bottom. It is imported by app/ and components/ and // must not import from them — a type it needs from a component belongs in diff --git a/web/public/flash/bin/audio-v0.6.5/boot_app0.bin b/web/public/flash/bin/audio-v0.7.0/boot_app0.bin similarity index 100% rename from web/public/flash/bin/audio-v0.6.5/boot_app0.bin rename to web/public/flash/bin/audio-v0.7.0/boot_app0.bin diff --git a/web/public/flash/bin/audio-v0.6.5/patternflow.ino.bin b/web/public/flash/bin/audio-v0.7.0/patternflow.ino.bin similarity index 50% rename from web/public/flash/bin/audio-v0.6.5/patternflow.ino.bin rename to web/public/flash/bin/audio-v0.7.0/patternflow.ino.bin index e404c88c..eb17c8fe 100644 Binary files a/web/public/flash/bin/audio-v0.6.5/patternflow.ino.bin and b/web/public/flash/bin/audio-v0.7.0/patternflow.ino.bin differ diff --git a/web/public/flash/bin/audio-v0.6.5/patternflow.ino.bootloader.bin b/web/public/flash/bin/audio-v0.7.0/patternflow.ino.bootloader.bin similarity index 100% rename from web/public/flash/bin/audio-v0.6.5/patternflow.ino.bootloader.bin rename to web/public/flash/bin/audio-v0.7.0/patternflow.ino.bootloader.bin diff --git a/web/public/flash/bin/audio-v0.6.5/patternflow.ino.partitions.bin b/web/public/flash/bin/audio-v0.7.0/patternflow.ino.partitions.bin similarity index 100% rename from web/public/flash/bin/audio-v0.6.5/patternflow.ino.partitions.bin rename to web/public/flash/bin/audio-v0.7.0/patternflow.ino.partitions.bin diff --git a/web/public/flash/bin/core-v3.10.5/boot_app0.bin b/web/public/flash/bin/core-v3.11.0/boot_app0.bin similarity index 100% rename from web/public/flash/bin/core-v3.10.5/boot_app0.bin rename to web/public/flash/bin/core-v3.11.0/boot_app0.bin diff --git a/web/public/flash/bin/core-v3.10.5/patternflow.ino.bin b/web/public/flash/bin/core-v3.11.0/patternflow.ino.bin similarity index 66% rename from web/public/flash/bin/core-v3.10.5/patternflow.ino.bin rename to web/public/flash/bin/core-v3.11.0/patternflow.ino.bin index 0d5c51a0..020d6072 100644 Binary files a/web/public/flash/bin/core-v3.10.5/patternflow.ino.bin and b/web/public/flash/bin/core-v3.11.0/patternflow.ino.bin differ diff --git a/web/public/flash/bin/core-v3.10.5/patternflow.ino.bootloader.bin b/web/public/flash/bin/core-v3.11.0/patternflow.ino.bootloader.bin similarity index 100% rename from web/public/flash/bin/core-v3.10.5/patternflow.ino.bootloader.bin rename to web/public/flash/bin/core-v3.11.0/patternflow.ino.bootloader.bin diff --git a/web/public/flash/bin/core-v3.10.5/patternflow.ino.partitions.bin b/web/public/flash/bin/core-v3.11.0/patternflow.ino.partitions.bin similarity index 100% rename from web/public/flash/bin/core-v3.10.5/patternflow.ino.partitions.bin rename to web/public/flash/bin/core-v3.11.0/patternflow.ino.partitions.bin diff --git a/web/public/flash/manifest.json b/web/public/flash/manifest.json index 1c897e60..84b58338 100644 --- a/web/public/flash/manifest.json +++ b/web/public/flash/manifest.json @@ -1,6 +1,6 @@ { "name": "Patternflow", - "version": "v3.10.5", + "version": "v3.11.0", "new_install_prompt_erase": false, "new_install_improv_wait_time": 20, "builds": [ @@ -8,19 +8,19 @@ "chipFamily": "ESP32-S3", "parts": [ { - "path": "bin/core-v3.10.5/patternflow.ino.bootloader.bin", + "path": "bin/core-v3.11.0/patternflow.ino.bootloader.bin", "offset": 0 }, { - "path": "bin/core-v3.10.5/patternflow.ino.partitions.bin", + "path": "bin/core-v3.11.0/patternflow.ino.partitions.bin", "offset": 32768 }, { - "path": "bin/core-v3.10.5/boot_app0.bin", + "path": "bin/core-v3.11.0/boot_app0.bin", "offset": 57344 }, { - "path": "bin/core-v3.10.5/patternflow.ino.bin", + "path": "bin/core-v3.11.0/patternflow.ino.bin", "offset": 65536 } ] diff --git a/web/public/guide/audio-editor/bridge.js b/web/public/guide/audio-editor/bridge.js new file mode 100644 index 00000000..974a6750 --- /dev/null +++ b/web/public/guide/audio-editor/bridge.js @@ -0,0 +1,187 @@ +/* Patternflow Audio guide: between the editor and its demo source. + * GENERATED by firmware/toolchain/editor_demo.py -- edit BRIDGE_JS there, rerun. + * + * Loaded after editor-adapter.js and before editor.js. Outside the extension + * the adapter is its demo: a synthesized spectrum, the mapping in memory, and + * each band's output straight off its level. That is a preview of the editor. + * The guide shows the editor driving a board, so five things are added here, + * around window.PFAdapter and without a change to any of the extension's + * files: + * + * the capture's chain The knob values are worked out as the capture works + * them out (offscreen.js tick()): every 33 ms, the + * level glided by Attack and Damping, auto range's + * envelope at its real rate, PFMap.position and + * PFMap.output, and a muted band sends nothing. The + * editor is handed these values too, so its cursor + * and readout are what the board was told. + * to the board {type:'pf-audio', outputs:[v|null x4]} to the page + * that holds this frame, each tick: v is 0..1 as the + * extension's a= message carries it, null is a lane + * left alone (its '-'). + * one mapping per tab The mapping is kept in sessionStorage, so the + * miniature in the card and the window lifted from + * it are one editor: an edit in either is the other's + * at once (the storage event), and survives a reload. + * Esc {type:'pf-audio', action:'escape'} when Esc is + * pressed and the editor's Preview is not what it + * closes. + * its scroll its own A wheel or a finger that runs past the editor's end + * stops there. Without this the page that holds the + * frame scrolls on, and the guide's step changes + * under the window. + * + * The source stays the adapter's: its spectrum, its levels per band, its + * "demo source" chip. Nothing leaves this folder. + */ +(function () { + 'use strict'; + + var source = window.PFAdapter; + if (!source || typeof PFMap === 'undefined') return; + + var KEY = 'pf-audio-editor:mapping'; + var TICK_MS = 33; // offscreen.js TICK_MS + var REPORT_MS = 120; // offscreen.js REPORT_MS + var CAPS = source.caps(); + + var host = window.parent !== window ? window.parent : null; + function tell(message) { + message.type = 'pf-audio'; + if (host) host.postMessage(message, window.location.origin); + } + + // ── the mapping ───────────────────────────────────────────────────────── + + function stored() { + try { + var text = window.sessionStorage.getItem(KEY); + var raw = text ? JSON.parse(text) : null; + return raw && Array.isArray(raw.bands) ? raw : null; + } catch (e) { + return null; + } + } + function keep(config) { + try { window.sessionStorage.setItem(KEY, JSON.stringify(config)); } catch (e) { /* private mode */ } + } + // A copy made whole, as the capture takes one out of a 'config' message. + function whole(raw) { + return PFMap.normalizeConfig(raw ? JSON.parse(JSON.stringify(raw)) : null, CAPS); + } + + var config = whole(stored()); // what is being mapped with + var configFn = null; + + // The mapping changed somewhere else (the other frame of this tab). + function adopt(raw) { + if (!raw) return; + config = whole(raw); + source.saveConfig(whole(raw)); // the demo source listens where the boxes are + if (configFn) configFn(raw); + } + window.addEventListener('storage', function (e) { + if (e.key === KEY && e.storageArea === window.sessionStorage) adopt(stored()); + }); + + // ── the chain (offscreen.js tick()) ───────────────────────────────────── + + var levels = [0, 0, 0, 0]; + var envelopes = [0, 1, 2, 3].map(PFMap.newEnvelope); + var peakLevels = [-1, -1, -1, -1]; + var peakPos = [0, 0, 0, 0]; + var peakOutputs = [0, 0, 0, 0]; + var heard = null; // the demo source's last frame + var frameFn = null; + var lastReport = 0; + + // editor-adapter.js learned(): an envelope at its minimum span is not one. + function learned(e) { + return e.hi - e.lo > PFMap.ENV_MIN_SPAN + 1e-6 ? { lo: e.lo, hi: e.hi } : null; + } + + function tick() { + if (!heard || !heard.levels || heard.levels.length < 4) return; + var bands = config.bands; + var auto = config.autoRange; + var i; + + for (i = 0; i < 4; i++) { + var raw = PFMap.clamp01(heard.levels[i]); + var a = raw > levels[i] ? config.attack : config.smoothing; + levels[i] = a * raw + (1 - a) * levels[i]; + if (auto) PFMap.trackEnvelope(envelopes[i], levels[i]); + } + + var lanes = [null, null, null, null]; + for (i = 0; i < 4; i++) { + var pos = PFMap.position(levels[i], bands[i], auto ? envelopes[i] : null); + var output = PFMap.output(bands[i], pos); + if (!bands[i].muted) lanes[bands[i].knob] = output; + if (levels[i] > peakLevels[i]) { + peakLevels[i] = levels[i]; + peakPos[i] = pos; + peakOutputs[i] = output; + } + } + tell({ outputs: lanes }); + + var now = performance.now(); + if (frameFn && now - lastReport > REPORT_MS) { + lastReport = now; + frameFn({ + running: true, connected: false, demo: true, + levels: peakLevels.slice(), + pos: peakPos.slice(), + outputs: peakOutputs.slice(), + spectrum: heard.spectrum, + env: auto ? envelopes.map(learned) : [], + autoRange: auto + }); + peakLevels = [-1, -1, -1, -1]; + } + } + + // ── the adapter the editor talks to ───────────────────────────────────── + + window.PFAdapter = { + caps: function () { return CAPS; }, + // What this tab has stored, or nothing (a first run), as the extension's + // own adapter answers from chrome.storage. + loadConfig: function () { + var raw = stored(); + if (raw) source.saveConfig(whole(raw)); + return Promise.resolve(raw); + }, + saveConfig: function (next) { + keep(next); + config = whole(next); + return source.saveConfig(next); + }, + resetBand: function (index, current) { + return source.resetBand(index, current).then(function (next) { + keep(next); + config = whole(next); + return next; + }); + }, + onConfig: function (fn) { configFn = fn; }, + onFrame: function (fn) { + frameFn = fn; + source.onFrame(function (state) { heard = state; }); + tick(); + setInterval(tick, TICK_MS); + } + }; + + document.addEventListener('keydown', function (e) { + if (e.key !== 'Escape') return; + var preview = document.getElementById('pv'); + if (preview && !preview.hidden) return; // the editor closes its Preview + tell({ action: 'escape' }); + }); + + // This page scrolls at its root; what is left of a scroll at its end is + // not handed on to the page that holds the frame. + document.documentElement.style.overscrollBehavior = 'contain'; +})(); diff --git a/web/public/guide/audio-editor/editor-adapter.js b/web/public/guide/audio-editor/editor-adapter.js new file mode 100644 index 00000000..32cce65f --- /dev/null +++ b/web/public/guide/audio-editor/editor-adapter.js @@ -0,0 +1,203 @@ +// Patternflow Audio — editor adapter, extension flavor. +// +// The editor module (editor.js) knows nothing about Chrome, the panel, or +// where audio comes from: it talks to exactly window.PFAdapter. The panel's +// /audio-in page is the same editor over another adapter +// (firmware/patternflow/console/_audio_in_adapter.js, which speaks +// fetch('/api/audio-in')). That boundary is the whole architecture; do not +// let editor.js grow a chrome.* call. The contract both adapters keep is +// written at the top of editor.js. +// +// This file is also the extension's SOURCE BAR: the
              in editor.html +// (what is feeding the editor, where it is going, Stop). The editor never +// looks in there; every state that arrives is drawn here first and then +// handed on. +// +// Without chrome.* (opened as a plain file, or from a static server) it runs +// a demo: a synthesized music-ish spectrum mapped by the same model, the +// mapping kept in memory. That is a preview of the editor, not of the +// product: nothing persists and nothing is captured. + +(function () { + const hasChrome = typeof chrome !== 'undefined' && chrome.storage && chrome.runtime; + const $ = (id) => document.getElementById(id); + + // What this source is: tab audio through an AnalyserNode. The numbers are + // the model's (PFMap.SOURCE), the two edge rules are its minimum window, and + // a band is never narrower than the 10 Hz the model keeps on load. + const CAPS = { + hzMin: PFMap.SOURCE.hzMin, + hzMax: PFMap.SOURCE.hzMax, + db: PFMap.SOURCE.db, + levelMax: 1, + hzGap: 10, + top: (lo) => lo + PFMap.MIN_WINDOW, + bottom: (hi) => hi - PFMap.MIN_WINDOW + }; + + // ── the source bar ──────────────────────────────────────────────────── + // + // The chip is the capture's own phase (offscreen.js phase()), in the same + // word the popup prints for it, so the two cannot disagree about whether + // the panel is being driven: a socket that is open to a panel whose + // Audio-React switch is off is not "live". Beside it, one sentence when + // there is something to do or something wrong: + // error the capture's own sentence, and both it and the chip in red + // aud off what is off and where its switch is (Turn on is the popup's) + // idle why the last capture ended, if it ended by itself (the tab + // was closed), then how to start one + // otherwise nothing: a capture or the popup's test connection is up, + // and "start a capture" is not what to say beside it + const WORDS = { connecting: 'connecting', off: 'aud off', live: 'live', test: 'test', error: 'error' }; + + function drawBar(state, idleHint) { + const chip = $('sourceChip'); + const hint = $('captureHint'); + const bad = state.phase === 'error'; + chip.textContent = state.demo ? 'demo source' : WORDS[state.phase] || 'idle'; + chip.className = state.phase === 'live' ? 'chip okChip' : bad ? 'chip badChip' : 'chip'; + // Where the capture is being sent, as the socket has it. Nothing is + // connected: no chip, rather than an address nothing is using. + $('hostChip').textContent = state.host || ''; + $('hostChip').hidden = !state.host; + hint.textContent = state.demo ? '' + : state.phase === 'off' ? 'Connected, but Audio-React is off on the panel. Turn it on in the extension popup.' + : state.error || (state.running || state.manual ? '' : (state.note ? state.note + ' ' : '') + idleHint); + hint.classList.toggle('badText', bad); + $('stopBtn').disabled = !state.running || !!state.demo; + } + + // An envelope that is at its minimum span has learned nothing: the band has + // been silent, or has sat at one level, for as long as an envelope + // remembers. The editor is told there is none (see `env` in its contract), + // so it draws the band's own window, and a drag that takes the four windows + // over by hand leaves that one as it was. Seeded from such an envelope it + // became a 4 dB sliver at the floor, and the first sound pegged that knob at + // its top. (A band that has heard a single click does have an envelope, and + // is seeded from it: this guards silence, not thinness.) + const learned = (env) => (env || []).map((e) => + e && e.hi - e.lo > PFMap.ENV_MIN_SPAN + 1e-6 ? e : null); + + if (hasChrome) { + const HINT = 'Start capture from the extension popup, on the tab that is playing.'; + let frameFn = null; + + const take = (state) => { + state = state || {}; + drawBar(state, HINT); + if (frameFn) frameFn({ ...state, env: learned(state.env) }); + }; + const ask = () => chrome.runtime.sendMessage({ type: 'status' }, (response) => { + void chrome.runtime.lastError; + if (response && response.state) take(response.state); + }); + + chrome.runtime.onMessage.addListener((message) => { + if (message.type === 'state') take(message.state); + }); + $('stopBtn').addEventListener('click', () => { + chrome.runtime.sendMessage({ type: 'stop' }, () => void chrome.runtime.lastError); + }); + + window.PFAdapter = { + caps() { + return CAPS; + }, + loadConfig() { + return new Promise((resolve) => + chrome.storage.local.get(PFMap.CONFIG_KEY, (stored) => + resolve(stored[PFMap.CONFIG_KEY] || null))); + }, + // Stored, then told to the capture: that message is how a running + // capture gets the edit (nothing listens to storage). + saveConfig(config) { + return new Promise((resolve) => + chrome.storage.local.set({ [PFMap.CONFIG_KEY]: config }, () => { + chrome.runtime.sendMessage({ type: 'config', config }, () => { + void chrome.runtime.lastError; // background may be asleep; storage holds + resolve(); + }); + })); + }, + resetBand(index, config) { + config.bands[index] = PFMap.defaultBand(index, CAPS); + return this.saveConfig(config).then(() => config); + }, + // The editor is up: one state now, without waiting for the next push. + onFrame(fn) { + frameFn = fn; + ask(); + } + }; + ask(); + return; + } + + // ── demo adapter ────────────────────────────────────────────────────── + const HINT = 'Demo mode — open this page from the extension for live audio.'; + let demoConfig = PFMap.normalizeConfig(null, CAPS); + let frameFn = null; + let t = 0; + + function demoSpectrum() { + t += 0.08; + const values = []; + for (let i = 0; i < 64; i++) { + const frac = i / 63; + const hz = 20 * Math.pow(1000, frac); + const lg = Math.log10(hz); + let v = 0.05; + const beat = 0.5 + 0.5 * Math.max(0, Math.sin(t * 2.2)); + v += (0.45 + 0.35 * beat) * Math.exp(-Math.pow(lg - Math.log10(70), 2) / 0.05); + v += 0.32 * (0.7 + 0.3 * Math.sin(t * 1.1 + 1)) * Math.exp(-Math.pow(lg - Math.log10(900), 2) / 0.16); + v += 0.2 * (0.6 + 0.4 * Math.sin(t * 3.1 + 2)) * Math.exp(-Math.pow(lg - Math.log10(3500), 2) / 0.06); + v += 0.16 * Math.max(0, Math.sin(t * 4.4)) * Math.exp(-Math.pow(lg - Math.log10(9500), 2) / 0.035); + if (hz > 12000) v *= Math.max(0.25, 1 - (hz - 12000) / 16000); + v += (Math.sin(i * 7.3 + t * 5) * 0.02); + values.push(Math.max(0.02, Math.min(0.95, v))); + } + return values; + } + + // The same envelope and the same level-to-knob chain the capture runs + // (offscreen.js), so the demo's cursor and readout are the model's too. + const envelopes = [0, 1, 2, 3].map(() => PFMap.newEnvelope()); + + function demoFrame() { + const spectrum = demoSpectrum(); + const auto = demoConfig.autoRange; + const levels = [], pos = [], outputs = []; + demoConfig.bands.forEach((band, i) => { + const lo = Math.log10(band.hzMin / 20) / 3 * 64; + const hi = Math.log10(band.hzMax / 20) / 3 * 64; + let sum = 0, n = 0; + for (let k = Math.max(0, Math.floor(lo)); k <= Math.min(63, Math.ceil(hi)); k++) { sum += spectrum[k]; n++; } + levels[i] = n ? sum / n : 0; + PFMap.trackEnvelope(envelopes[i], levels[i], 0.01); + pos[i] = PFMap.position(levels[i], band, auto ? envelopes[i] : null); + outputs[i] = PFMap.output(band, pos[i]); + }); + const state = { + running: true, connected: false, demo: true, + levels, pos, outputs, spectrum, + env: auto ? learned(envelopes.map((e) => ({ lo: e.lo, hi: e.hi }))) : [], + autoRange: auto + }; + drawBar(state, HINT); + if (frameFn) frameFn(state); + } + + window.PFAdapter = { + caps() { return CAPS; }, + // Nothing is stored: the editor starts as a first run does. + loadConfig() { return Promise.resolve(null); }, + saveConfig(config) { demoConfig = config; return Promise.resolve(); }, + resetBand(index, config) { + config.bands[index] = PFMap.defaultBand(index, CAPS); + demoConfig = config; + return Promise.resolve(config); + }, + onFrame(fn) { frameFn = fn; demoFrame(); setInterval(demoFrame, 120); } + }; + drawBar({ demo: true }, HINT); +})(); diff --git a/web/public/guide/audio-editor/editor.css b/web/public/guide/audio-editor/editor.css new file mode 100644 index 00000000..da75d6c4 --- /dev/null +++ b/web/public/guide/audio-editor/editor.css @@ -0,0 +1,333 @@ +/* Patternflow Audio — mapping editor. + + Two parts. Down to the line that says EXTENSION ONLY these are the editor's + own rules, the same on every surface: the panel's /audio-in page takes + exactly that part (firmware/toolchain/build_audio_in_page.py stops reading + at the line) and brings its own colours and its own bar. Below the line is + what only the extension's page has: the cream instrument palette and the + source bar. Every colour above the line is a variable, so that works. + + Three things the part above the line leaves to the surface, because a + console page has them already and would carry them twice: the page itself + (body: margin, background, type), --mono, and that the hidden attribute + hides ([hidden] { display: none !important }: three of the editor's parts + are flex boxes that come and go by that attribute, and a display of their + own would otherwise keep them on screen). + + The layout rules are here too, and not in either surface's own sheet: the + editor is used in a full tab, in half a screen beside the tab that is + playing, and on a phone, and it has to hold in all three wherever it is + shown. Three steps: everything in a row; at 1100 px the output range drops + under the curve; at 700 px each row of controls becomes a column. */ +* { box-sizing: border-box; } + +.page { + max-width: 1280px; + margin: 0 auto; + padding: 24px; + display: flex; + flex-direction: column; + gap: 14px; +} + +.mono { font-family: var(--mono); } +.dimText { color: var(--muted); } +.hint { font-size: 12px; color: var(--muted); } +.hint:empty { display: none; } +.spacer { flex-grow: 1; } + +.ghostBtn { + font: inherit; font-size: 11px; cursor: pointer; + border: 1px solid var(--rule); border-radius: 2px; + background: var(--field); color: var(--muted); padding: 6px 12px; +} +.ghostBtn[aria-pressed=true] { border-color: var(--led); color: var(--ink); } +.ghostBtn.dan { color: var(--bad); } + +/* ── toolbar / toggles ── */ +/* The hint belongs to the switch and takes what the row has left; the two + glide sliders stay together and drop to a line of their own before the + hint is squeezed. */ +.toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; } +#autoHint { flex: 1 1 22em; } +.glide { display: flex; flex-wrap: wrap; gap: 10px 18px; } +.damping { display: flex; align-items: center; gap: 8px; } +.damping input { width: 130px; accent-color: var(--led); } +.damping .mono { font-size: 11px; min-width: 56px; } +.toggleWrap { + font: inherit; color: inherit; cursor: pointer; display: flex; align-items: center; gap: 7px; + border: 1px solid var(--ink); border-radius: 2px; background: var(--field); padding: 5px 11px; +} +.toggleLabel { font-size: 11px; font-weight: 500; } +.toggle { + width: 22px; height: 12px; background: var(--rule); border-radius: 7px; + position: relative; flex: none; pointer-events: none; +} +.dotK { + position: absolute; left: 1px; top: 1px; width: 10px; height: 10px; + background: var(--field); border-radius: 50%; transition: left 0.12s; +} +.toggleWrap.on .toggle { background: var(--ok); } +.toggleWrap.on .dotK { left: 11px; } +/* Mute is the same switch as Auto range, the whole button its target; in its + column it is as wide as its words, not as the column. */ +#muteToggle { align-self: flex-start; } + +/* ── main plot ── */ +/* Clipped: the canvas, and the Preview layer that lies exactly over it, are + square, and a surface may round this box's corners; and a tag has no place + of its own until editor.js gives it one (until then it is below the canvas, + out of the box). Isolated: what is stacked in here (the selected tag over + its neighbours, Preview over them all) is stacked against nothing outside + it. */ +.plotWrap { + position: relative; isolation: isolate; background: var(--field); + border: 1px solid var(--rule); border-radius: 2px; overflow: hidden; +} +/* No touch-action here or on #curve, on purpose. A finger that lands on a box + or a handle drags it and one that misses scrolls the page, and that is + decided per touch, by editor.js, in a touchstart listener. touch-action is + per element: `none` trapped every swipe that began on the plot, and `pan-y` + would hand the browser the vertical drag of a box's top or bottom edge. + + EVERY CANVAS HAS ITS BOX HERE, not from editor.js: the plot, the curve + (#curve) and the three kinds of glyph. A canvas is 300 x 150 until somebody + says otherwise, and editor.js says nothing until the mapping has been read, + which on the panel's own hotspot is seconds after the first paint: sized + there, the page came up with a 150 px plot and 131 px tabs and then jumped. + The plot is as wide as the page and 45% of the window tall, between 240 and + 400 px, so the plot, the knobs and the top of the card share one screen (a + phone gets 260 at the most: see the 700 px step). editor.js only makes the + pixels for the box it finds. */ +#plot { display: block; cursor: default; width: 100%; height: clamp(240px, 45vh, 400px); } +.tag canvas, .chipCard canvas { width: 18px; height: 12px; } +.tag { + position: absolute; display: flex; align-items: center; gap: 6px; + background: var(--cream-2); border: 1px solid var(--rule); border-radius: 2px; + padding: 2px 7px; pointer-events: none; white-space: nowrap; +} +.tag.sel { border-color: var(--led); z-index: 1; } +/* A muted knob's tag says so, in the room its curve glyph took. */ +.tag.mutedTag { opacity: 0.55; } +.mutedTag canvas { display: none; } +.tagText { font-family: var(--mono); font-size: 10px; } + +/* ── preview: the scope, in the plot's place while it runs ── */ +.pv { + position: absolute; inset: 0; z-index: 2; display: flex; flex-direction: column; + background: var(--field); +} +.pvHead { + display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; + gap: 8px 14px; padding: 10px 12px; +} +.pvChips { display: flex; flex-wrap: wrap; gap: 8px; } +.pvSig { + font: inherit; font-size: 11px; cursor: pointer; + border: 1px solid var(--rule); border-radius: 2px; + background: var(--field); color: var(--ink); padding: 5px 11px; +} +.pvSig.on { border-color: var(--led); font-weight: 600; } +/* Sized by the column it is in, never by a style of its own: editor.js reads + the box it was given and draws to that. */ +#pvScope { display: block; flex: 1 1 0; min-height: 0; width: 100%; } +.pvLegend { display: flex; gap: 7px; align-items: center; } +.pvKeyIn, .pvKeyOut { width: 16px; height: 3px; display: inline-block; } +.pvKeyIn { background: var(--faint); } +.pvKeyOut { background: var(--led); } + +/* ── the knobs, as tabs under the plot ── */ +.knobRow { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; } +.chips { display: flex; gap: 8px; flex: 1 1 auto; } +.chipCard { + font: inherit; color: inherit; cursor: pointer; + display: flex; align-items: center; gap: 8px; + background: var(--field); border: 1px solid var(--rule); border-radius: 2px; + padding: 7px 12px; +} +.chipCard.sel { border-color: var(--led); } +.chipCard.sel .chipName { font-weight: 600; } +.chipCard.mutedChip { opacity: 0.55; } +.chipDot { width: 6px; height: 6px; background: var(--led); border-radius: 1px; } +.chipCard .mono { font-size: 11px; } + +/* ── the selected knob's card ── */ +.panel { + display: flex; flex-wrap: wrap; gap: 14px 20px; background: var(--field); + border: 1px solid var(--rule); border-radius: 2px; padding: 16px 18px; +} +.bandHead { flex: 0 0 100%; display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 8px; } +.bandName { font-size: 13px; font-weight: 600; white-space: nowrap; } +.col { display: flex; flex-direction: column; gap: 8px; min-width: 0; } +.col.grow { flex: 1 1 0; gap: 12px; } +.curveCol { flex: none; } +.colKicker { font-family: var(--mono); font-size: 10px; letter-spacing: 0.09em; color: var(--muted); } +.vr { width: 1px; background: var(--rule); } +/* The curve, and its shapes beside it while there is room for both. The + curve is 356 px wide where it has that, its row's width where it has not. */ +.curveRow { display: flex; flex-wrap: wrap; gap: 12px 20px; } +#curve { display: block; flex: none; width: 356px; max-width: 100%; height: 218px; } + +.presetsCol { width: 268px; flex: none; display: flex; flex-direction: column; gap: 8px; } +.presetGrid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; } +.preset { + font: inherit; cursor: pointer; + display: flex; flex-direction: column; align-items: center; gap: 5px; + border: 1px solid var(--rule); border-radius: 2px; background: var(--cream); + padding: 9px 4px 7px; +} +.preset canvas { width: 40px; height: 24px; } +.preset span { font-size: 10px; font-weight: 500; color: var(--ink); } +.preset.on { border-color: var(--led); } +.preset.on span { font-weight: 600; } +.stepsRow { display: flex; align-items: center; gap: 7px; font-size: 11px; color: var(--muted); } +/* Minus and plus, at 32 px: the size of a small button on the panel's + console, and a finger's. (The extension keeps its instrument's smaller + ones: below the line.) */ +.stepsRow button { + font: inherit; cursor: pointer; width: 32px; height: 32px; padding: 0; line-height: 1; + border: 1px solid var(--rule); border-radius: 2px; background: var(--cream); color: var(--ink); +} + +.field { display: flex; flex-direction: column; gap: 6px; } +.fieldRow { display: flex; align-items: center; justify-content: space-between; gap: 8px; } +.fieldLabel { font-size: 11px; color: var(--muted); } + +/* A sideways drag is the track's, an up-and-down one is the page's. */ +.outTrack { position: relative; height: 18px; cursor: pointer; touch-action: pan-y; } +.outRail { position: absolute; left: 5px; right: 5px; top: 50%; height: 2px; margin-top: -1px; background: var(--rule); } +.outFill { position: absolute; top: 50%; height: 3px; margin-top: -1.5px; background: var(--led); } +.outHandle { + position: absolute; top: 50%; width: 9px; height: 10px; margin: -5px 0 0 -4.5px; + background: var(--field); border: 1px solid var(--ink); +} +.outPresets { display: flex; flex-wrap: wrap; gap: 6px; } +.outPreset { + font: inherit; font-size: 10px; cursor: pointer; + border: 1px solid var(--rule); border-radius: 2px; + background: var(--cream); color: var(--muted); padding: 3px 8px; +} + +/* ── the resets, at the foot ── */ +.acts { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; } + +/* The editor body has no box of its own: its parts are laid out by .page. + Until editor.js has the mapping it is inert, and looks it. + + And until the FIRST mapping is in, the page says nothing it does not know: + no switch position, no slider thumb, no output handles, and the toolbar's + hint keeps its lines without showing its words. Everything keeps its box, + so nothing moves when the mapping arrives. That state is `inert="inert"`, + as editor.html writes it; a source that makes the editor inert again later + (the panel's adapter, around a reset) sets the property, which leaves the + attribute empty, so what is on screen then stays on screen. */ +#ed { display: contents; } +#ed[inert] > * { opacity: 0.5; } +#ed[inert=inert] :is(.toggle, input, .outHandle, .hint) { visibility: hidden; } + +/* ── narrower than a full tab ── */ +/* Half a screen: the output range goes under the curve, across the card. The + shapes stay beside the curve for as long as both fit, then wrap under it. */ +@media (max-width: 1100px) { + .vr { display: none; } + .curveCol, .col.grow { flex: 1 1 100%; } + .presetsCol { flex: 1 1 200px; width: auto; } +} +/* A phone: a 16 px gutter all round (never edge to edge: the gutter is where + a thumb scrolls from), one control to a row, the knobs four across. */ +@media (max-width: 700px) { + .page { padding: 16px; gap: 12px; } + #plot { max-height: 260px; } + .panel { padding: 14px 12px; } + .glide, .damping { flex: 1 1 100%; } + .damping .fieldLabel { min-width: 56px; } + .damping input { flex: 1; width: auto; min-width: 0; } + .chips { flex-basis: 100%; gap: 6px; } + .chipCard { flex: 1 1 0; min-width: 0; flex-direction: column; gap: 0; padding: 6px 2px; } + .chipDot, .chipCard canvas { display: none; } + #openPreview { margin-left: auto; } +} +/* Touch: what a finger drags is drawn larger, and what it presses gets a hit + area that grows invisibly while the control keeps its size. */ +@media (pointer: coarse) { + .outTrack { height: 36px; } + .outHandle { width: 16px; height: 22px; margin: -11px 0 0 -8px; } + .damping input { min-height: 32px; } + :is(.toggleWrap, .ghostBtn, .pvSig, .chipCard, .outPreset, .stepsRow button) { position: relative; } + :is(.toggleWrap, .ghostBtn, .pvSig, .chipCard, .outPreset, .stepsRow button)::after { + content: ""; + position: absolute; inset: -8px -2px; + } +} + +/* ── EXTENSION ONLY ── */ + +/* The page itself and its type: a surface's own. The panel's /audio-in is a + console page and brings the console's (console/_audio_in.css), and with it + the rule that hidden means hidden. */ +[hidden] { display: none !important; } +/* Keyboard focus, as the popup has it: the browser's own ring is near black, + and on the filled Stop that is black on black. (The console's base block + has the same rule for the panel's page.) */ +:focus-visible { outline: 2px solid var(--led); outline-offset: 2px; } +body { + margin: 0; + background: var(--cream); + color: var(--ink); + font: 12px/1.45 Inter, ui-sans-serif, system-ui, sans-serif; +} + +/* The cream instrument palette, the same as the popup's (popup.css). */ +:root { + --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + --cream: #f4efe6; + --cream-2: #ede7db; + --ink: #141414; + --muted: #6b655a; + --faint: #a69f90; + --rule: #d9d1c0; + --led: #e8552e; + --ok: #267a43; + --bad: #b43b2d; + --field: #fbf7ef; +} + +/* The instrument's small capitals, on the two headings inside the card, and + its small steppers. */ +.colKicker { text-transform: uppercase; } +.stepsRow button { width: 18px; height: 18px; } + +/* A button under the mouse goes orange here. (On the panel orange is for what + is selected or on, never a hover, so this is not the shared part's to say.) + :where keeps it weaker than a button's own state: a pressed Preview stays + ink, as it did when this rule stood above the pressed one. */ +:where(.ghostBtn, .outPreset):hover { border-color: var(--led); color: var(--led); } + +/* ── the source bar ── */ +header { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 12px; } +.brand { display: flex; align-items: center; gap: 9px; } +.led { width: 8px; height: 8px; background: var(--led); border-radius: 1px; } +.title { font-size: 14px; font-weight: 600; white-space: nowrap; } +.kicker { font-family: var(--mono); font-size: 10px; letter-spacing: 0.09em; color: var(--muted); } +.cluster { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; } + +.chip { + border: 1px solid var(--rule); border-radius: 2px; background: var(--field); + padding: 5px 10px; font-family: var(--mono); font-size: 11px; color: var(--ink); + white-space: nowrap; +} +.chip.dim { color: var(--muted); } +.okChip { border-color: var(--ok); color: var(--ok); } +/* An error looks like one: the chip, and the sentence beside it. */ +.badChip { border-color: var(--bad); color: var(--bad); } +.badText { color: var(--bad); } + +/* Stop is the popup's Stop: the same filled button, in the instrument's small + capitals. */ +.primaryBtn { + font: inherit; font-family: var(--mono); font-size: 10px; letter-spacing: .1em; + text-transform: uppercase; cursor: pointer; min-height: 32px; + border: 1px solid var(--ink); border-radius: 0; + background: var(--ink); color: var(--cream); padding: 0 14px; +} +.primaryBtn:disabled { opacity: 0.35; cursor: default; } diff --git a/web/public/guide/audio-editor/editor.js b/web/public/guide/audio-editor/editor.js new file mode 100644 index 00000000..6cd2030c --- /dev/null +++ b/web/public/guide/audio-editor/editor.js @@ -0,0 +1,1177 @@ +// Patternflow Audio — the mapping editor. +// +// One module, no chrome.* and no fetch. It stands on two things: +// +// PFMap (mapping.js) the model: defaults, what a stored mapping is turned +// into, the curves, the level-to-knob chain. +// window.PFAdapter the SOURCE: the extension's is editor-adapter.js, the +// panel's is console/_audio_in_adapter.js. This file +// moves to the panel verbatim; only the adapter differs. +// +// The model, in one sentence: a band is a BOX drawn on the live spectrum +// (width = the frequencies it listens to, height = the level window it maps), +// and a RESPONSE CURVE that turns position-in-window into position-in-output- +// range. Curves bake to a 33-point table on save; the analysis side — and +// the firmware — only interpolates, and never learns what a bezier is. +// +// THE ADAPTER CONTRACT. The source owns its limits, its axis, its defaults +// and where a level sits in a window; the editor draws what it is told and +// asks rather than assumes. +// +// loadConfig() -> Promise +// { autoRange, smoothing, attack, bands[4] }, each band { hzMin, hzMax, +// inMin, inMax, gain, outMin, outMax, muted, curve, lut }. Levels are +// 0..1 along the source's own level axis. null means NOTHING IS STORED +// (a first run): the editor then starts from PFMap's defaults. A source +// that could not read must not answer null - it keeps the promise open +// until it can; the editor stays inert meanwhile. +// caps() -> asked once, after loadConfig() has settled +// hzMin, hzMax the ends of the frequency axis +// db [bottom, top]: the dB at 0 and at 1 of the level axis +// levelMax the highest a window's top edge is stored, 0..1 +// hzGap the narrowest band the source stores, in Hz +// top(lo) the lowest top edge it stores over a bottom at `lo` +// bottom(hi) the highest bottom edge it stores under a top at `hi` +// Only hzMin and hzMax are required; the rest default to an axis with no +// dB printed on it, the whole axis, and PFMap.MIN_WINDOW. +// saveConfig(config) -> Promise store it and hand it to what is mapping +// resetBand(i, config) -> Promise +// band i back to the SOURCE's default. Answers the whole mapping as it +// now stands (as loadConfig would); the editor takes that and does not +// save it back. +// resetAll(button) optional: the source's own "reset everything", where it +// has one. The editor then shows Reset mapping beside +// the knob's own Reset and calls this with the button +// pressed (for PFConfirm); what comes back goes in +// through onConfig. A source without it has no such +// button. +// onConfig(fn) optional: fn(config) replaces the editor's copy without +// saving it (after a reset the source did by itself) +// onFrame(fn) fn(frame), about ten times a second while there is +// something to show. Called once, when the editor is up. +// levels[4] each band's level, 0..1 on the level axis. Empty or +// missing = nothing is being heard: no level line, no +// cursor. +// spectrum[64] log-spaced over hzMin..hzMax, same axis +// env[4] { lo, hi } per band while auto range has a learned +// envelope; missing, or null for that band, otherwise +// (the stored window is drawn, dashed). "Learned" is +// the source's to say: what a band has while it has +// heard nothing is not an envelope and is not sent, for +// a drag that takes the windows over by hand seeds each +// window from what is sent here. +// pos[4] where each level sits in its window, 0..1, AS THE +// MAPPER COMPUTED IT: the curve's input +// outputs[4] what each knob is being sent +// db optional [bottom, top] when this frame's levels are on +// another axis than caps().db (the phone app's) +// pos and outputs are what make the cursor and the readout true. A frame +// without them (one the source did not map itself) gets the editor's own +// arithmetic: PFMap.position / PFMap.output on the frame's levels. + +'use strict'; + +const A = window.PFAdapter; +const { clamp01, PRESETS, evalCurve, bakeLut } = PFMap; + +// What the source says about itself. Read once the mapping is in (the panel +// learns its frequency range from the same reply), so nothing above init() +// may use it at load. +let CAPS = null; +let DECADES = 1; +let TICKS = []; + +function readCaps() { + const c = A.caps(); + CAPS = { + hzMin: c.hzMin, hzMax: c.hzMax, + db: c.db || null, + levelMax: c.levelMax || 1, + hzGap: c.hzGap || 0, + top: c.top || ((lo) => lo + PFMap.MIN_WINDOW), + bottom: c.bottom || ((hi) => hi - PFMap.MIN_WINDOW) + }; + DECADES = Math.log10(CAPS.hzMax / CAPS.hzMin); + // Inner gridlines only — the endpoints are drawn from CAPS, so the same + // axis code serves the extension's 20–20k and the microphone's 31–8k. + TICKS = [[50, '50'], [100, '100'], [200, '200'], [500, '500'], + [1000, '1k'], [2000, '2k'], [5000, '5k'], [10000, '10k']] + .filter(([hz]) => hz > CAPS.hzMin * 1.15 && hz < CAPS.hzMax * 0.87); +} + +const clampHz = (v) => Math.max(CAPS.hzMin, Math.min(CAPS.hzMax, Number(v) || CAPS.hzMin)); + +// What the editor DRAWS: the curve itself where there is one (a table would +// round a step's corner), the model's answer otherwise. +function bandCurveValue(band, u) { + return band.curve ? evalCurve(band.curve, u) : PFMap.curveValue(band, u); +} + +// ── state ─────────────────────────────────────────────────────────────── + +// The mapping being edited (null until init() has it), and the knob selected. +let cfg = null; +let sel = 0; +let frame = {}; +let saveTimer = 0; + +// Every edit saves itself, 150 ms after the last one of a burst. +function persist() { + clearTimeout(saveTimer); + saveTimer = setTimeout(flush, 150); +} + +// Save now what is waiting to be saved. Also on the way out of the page, so +// an edit made in the last 150 ms is not left behind. +function flush() { + if (!saveTimer) return null; + clearTimeout(saveTimer); + saveTimer = 0; + return A.saveConfig(cfg); +} +window.addEventListener('pagehide', flush); + +// The source's copy replaces the editor's: after a reset. Not saved back. +function applyConfig(raw) { + cfg = PFMap.normalizeConfig(raw, CAPS); + syncAutoToggle(); + syncDamping(); + selectBand(sel); +} + +function touchCurve(band, curve) { + band.curve = curve; + band.lut = bakeLut(curve); +} + +// ── main plot geometry ────────────────────────────────────────────────── + +const $ = (id) => document.getElementById(id); +const plotCanvas = $('plot'); +const pctx = plotCanvas.getContext('2d'); +const M = { l: 48, r: 6, t: 26, b: 40 }; +// The plot area inside those margins, css px. +let PW = 0, PH = 0; + +// A finger, not a mouse: what it has to land on is made larger, here and in +// the stylesheet. EDGE is how far from a box's edge still grabs it, GRIP the +// square drawn on a corner or a curve handle, REACH how far from a curve +// handle still grabs that. +const COARSE = matchMedia('(pointer: coarse)').matches; +const EDGE = COARSE ? 18 : 7; +const GRIP = COARSE ? 11 : 7; +const REACH = COARSE ? 22 : 12; + +// What the toolbar says beside the Auto range switch, in each of its two +// states (syncAutoToggle). The first, as a mouse has it, is also written in +// editor.html in the hint's own place: unseen until the mapping is in, and +// there so that its lines are already taken and the plot under it does not +// move when the words arrive. +const HINT_AUTO = 'Each box follows the loudness of what it hears. ' + + (COARSE ? 'Switch Auto range off to set a height by hand.' : 'Drag a top or bottom edge to take all four over by hand.'); +const HINT_HAND = 'Width: the frequencies a knob listens to. Height: the quiet-to-loud range mapped onto it.'; + +// The plot's box is the stylesheet's (#plot in editor.css: as wide as the +// page, 45% of the window tall between 240 and 400 px, 260 at the most at the +// phone width), so it has its size before this file has run and nothing below +// it moves when the mapping arrives. That is the one rule for its height +// (Preview's scope is the same box). Here the height is only snapped to a +// whole pixel, so the canvas is not resampled, and the pixels are made. +function sizePlot() { + plotCanvas.style.height = ''; + const cssW = plotCanvas.clientWidth, cssH = plotCanvas.clientHeight; + const dpr = window.devicePixelRatio || 1; + plotCanvas.width = Math.round(cssW * dpr); + plotCanvas.height = Math.round(cssH * dpr); + plotCanvas.style.height = cssH + 'px'; + pctx.setTransform(dpr, 0, 0, dpr, 0, 0); + PW = cssW - M.l - M.r; + PH = cssH - M.t - M.b; +} + +const xOf = (hz) => M.l + PW * (Math.log10(clampHz(hz) / CAPS.hzMin) / DECADES); +const hzOf = (x) => clampHz(CAPS.hzMin * Math.pow(10, DECADES * ((x - M.l) / PW))); +const yOf = (v) => M.t + (1 - clamp01(v)) * PH; +const vOf = (y) => clamp01(1 - (y - M.t) / PH); + +// The level axis is the source's own dB: [bottom, top], or null when it gave +// none. The frame may be on another axis than the source's usual one (see the +// contract). Every level the editor prints goes through these, so the axis, +// the knob's heading and the curve's labels are in one unit. +const axisDb = () => frame.db || CAPS.db; +const minus = (n) => String(Math.round(n)).replace('-', '−'); + +// A height on the level axis, 0..1, as dB: '−43'. '' on an axis without dB. +function dbText(v) { + const db = axisDb(); + return db ? minus(db[0] + v * (db[1] - db[0])) : ''; +} + +// A line every 10 dB: [[v, label]]. +function dbLines() { + const db = axisDb(); + const lines = []; + if (!db) return lines; + for (let d = Math.ceil(db[0] / 10) * 10; d <= db[1]; d += 10) { + lines.push([(d - db[0]) / (db[1] - db[0]), minus(d)]); + } + return lines; +} + +// Canvas colors come from the page's CSS variables, read fresh each paint so +// a theme toggle repaints correctly. The extension page is the cream +// instrument; the panel's console wraps the same module in its own tokens. +// Three of them are also painted see-through, which takes the colour apart: +// --led, --rule and --ink have to be written #rrggbb on every surface. +function theme() { + const s = getComputedStyle(document.body); + const v = (name) => s.getPropertyValue(name).trim(); + const alpha = (name) => { + const n = parseInt(v(name).slice(1), 16); + return (a) => `rgba(${n >> 16}, ${n >> 8 & 255}, ${n & 255}, ${a})`; + }; + return { + field: v('--field'), + cream: v('--cream'), + ink: v('--ink'), + muted: v('--muted'), + faint: v('--faint'), + rule: v('--rule'), + led: v('--led'), + ledA: alpha('--led'), + ruleA: alpha('--rule'), + inkA: alpha('--ink') + }; +} + +// The envelope auto range is mapping band `index` through right now, or null: +// auto range is off, the source has not learned one, or what it sent has no +// height (a panel that has never heard anything). +function liveEnv(index) { + const env = cfg.autoRange && frame.env && frame.env[index]; + return env && env.hi > env.lo ? { lo: clamp01(env.lo), hi: clamp01(env.hi) } : null; +} + +// The window a band is drawn with: the breathing envelope while auto range +// has one, the band's own hand-set window otherwise. Auto range without an +// envelope yet shows the stored window (dashed), never a made-up one. +function bandWindow(index) { + const band = cfg.bands[index]; + return liveEnv(index) || { lo: band.inMin, hi: band.inMax }; +} + +// Is anything being heard? Without levels there is no line and no cursor. +const hearing = () => !!(frame.levels && frame.levels.length); + +// Where band `index`'s level sits in its window, 0..1: the mapper's own +// figure when the frame carries it, the model's arithmetic when it does not. +function bandPos(index) { + if (frame.pos && frame.pos.length) return clamp01(frame.pos[index]); + return PFMap.position(frame.levels[index] || 0, cfg.bands[index], liveEnv(index)); +} + +// A window the source would store as it is: the top no higher than it keeps +// one, the two edges no closer than it keeps them. +function fitWindow(band) { + band.inMin = Math.max(0, Math.min(band.inMin, CAPS.bottom(CAPS.levelMax))); + band.inMax = Math.min(CAPS.levelMax, Math.max(band.inMax, CAPS.top(band.inMin))); +} + +function formatHz(hz) { + return hz >= 1000 ? (hz / 1000).toFixed(hz >= 9950 ? 0 : 1).replace(/\.0$/, '') + 'k' : String(Math.round(hz)); +} + +// ── main plot drawing ─────────────────────────────────────────────────── + +// Everything written on a canvas: 11 px, in --muted. (10 px in --faint, as it +// was, is 2.3:1 on the extension's cream; the axis and the one number that +// says what the knob gets were the least legible text on the page.) +const MONO = '11px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace'; + +function drawPlot() { + const T = theme(); + const W = M.l + PW + M.r, H = M.t + PH + M.b; + pctx.clearRect(0, 0, W, H); + pctx.fillStyle = T.field; + pctx.fillRect(0, 0, W, H); + + // grid + pctx.font = MONO; + pctx.strokeStyle = T.ruleA(0.55); + pctx.fillStyle = T.muted; + pctx.lineWidth = 1; + for (const [v, label] of dbLines()) { + const y = Math.round(yOf(v)) + 0.5; + if (v > 0 && v < 1) { + pctx.beginPath(); pctx.moveTo(M.l, y); pctx.lineTo(M.l + PW, y); pctx.stroke(); + } + pctx.textAlign = 'right'; + pctx.fillText(label, M.l - 8, y + 3); + } + // The two units, once each, in the margin the level labels are in: dB heads + // their column, Hz starts the row of frequencies. + pctx.textAlign = 'right'; + if (axisDb()) pctx.fillText('dB', M.l - 8, M.t - 9); + pctx.fillText('Hz', M.l - 8, M.t + PH + 16); + pctx.textAlign = 'center'; + for (const [hz, label] of TICKS) { + const x = Math.round(xOf(hz)) + 0.5; + pctx.beginPath(); pctx.moveTo(x, M.t); pctx.lineTo(x, M.t + PH); pctx.stroke(); + pctx.fillText(label, x, M.t + PH + 16); + } + pctx.textAlign = 'left'; + pctx.fillText(formatHz(CAPS.hzMin), M.l, M.t + PH + 16); + pctx.textAlign = 'right'; + pctx.fillText(formatHz(CAPS.hzMax), M.l + PW, M.t + PH + 16); + pctx.strokeStyle = T.rule; + pctx.beginPath(); pctx.moveTo(M.l, M.t + PH + 0.5); pctx.lineTo(M.l + PW, M.t + PH + 0.5); pctx.stroke(); + + // spectrum staircase + const spec = frame.spectrum || []; + if (spec.length > 1) { + pctx.fillStyle = T.inkA(0.14); + pctx.beginPath(); + pctx.moveTo(M.l, yOf(spec[0])); + const bw = PW / spec.length; + for (let i = 0; i < spec.length; i++) { + const y = yOf(spec[i]); + pctx.lineTo(M.l + i * bw, y); + pctx.lineTo(M.l + (i + 1) * bw, y); + } + pctx.lineTo(M.l + PW, M.t + PH); + pctx.lineTo(M.l, M.t + PH); + pctx.closePath(); + pctx.fill(); + } + + // boxes + cfg.bands.forEach((band, i) => { + const win = bandWindow(i); + const x0 = xOf(band.hzMin), x1 = xOf(band.hzMax); + const yTop = yOf(win.hi), yBot = yOf(win.lo); + const isSel = i === sel; + const stroke = isSel ? T.led : (band.muted ? T.faint : T.ink); + const alpha = band.muted ? 0.04 : (isSel ? 0.12 : 0.07); + + pctx.fillStyle = T.ledA(alpha); + pctx.fillRect(x0, yTop, x1 - x0, yBot - yTop); + + pctx.strokeStyle = stroke; + pctx.lineWidth = isSel ? 1.5 : 1; + pctx.beginPath(); pctx.moveTo(x0, yTop); pctx.lineTo(x0, yBot); pctx.stroke(); + pctx.beginPath(); pctx.moveTo(x1, yTop); pctx.lineTo(x1, yBot); pctx.stroke(); + if (cfg.autoRange) pctx.setLineDash([5, 4]); + pctx.beginPath(); pctx.moveTo(x0, yTop); pctx.lineTo(x1, yTop); pctx.stroke(); + pctx.beginPath(); pctx.moveTo(x0, yBot); pctx.lineTo(x1, yBot); pctx.stroke(); + pctx.setLineDash([]); + + // The live level, as a line across the box: how far through its window + // the band is, which is what the curve is fed and so what the knob gets. + // It is the mapper's position and not the level's height on the axis: the + // two differ wherever the mapper is not linear in this axis (the panel + // maps linear amplitude on a dB axis, and auto range squelches the floor), + // and a line drawn at the height says "fires" where the knob does not. + // Resting, it sits on the bottom edge; at full scale, on the top. + if (hearing()) { + const ly = Math.max(yTop + 1.5, Math.min(yBot - 1.5, yBot - bandPos(i) * (yBot - yTop))); + pctx.strokeStyle = T.led; + pctx.lineWidth = 2; + pctx.beginPath(); + pctx.moveTo(x0 + 1, ly); + pctx.lineTo(x1 - 1, ly); + pctx.stroke(); + } + + if (isSel) { + pctx.fillStyle = T.field; + pctx.strokeStyle = T.ink; + pctx.lineWidth = 1; + for (const [cx, cy] of [[x0, yTop], [x1, yTop], [x0, yBot], [x1, yBot]]) { + pctx.fillRect(cx - GRIP / 2, cy - GRIP / 2, GRIP, GRIP); + pctx.strokeRect(cx - GRIP / 2, cy - GRIP / 2, GRIP, GRIP); + } + } + }); + + placeTags(); +} + +// Band N drives knob N, and is named after it everywhere: K1..K4. A tag sits +// ON its box's top edge, outside the box: auto range can make a box thinner +// than the tag, which would otherwise cover it and its level line. It starts +// at the box's left edge, and stops at the plot's right one: on a phone the +// highest knob's box is narrower than its tag, which was cut off there. +function placeTags() { + const T = theme(); + cfg.bands.forEach((band, i) => { + const tag = $('tag' + i); + const win = bandWindow(i); + tag.classList.toggle('sel', i === sel); + tag.classList.toggle('mutedTag', band.muted); + tag.querySelector('.tagText').textContent = 'K' + (i + 1) + (band.muted ? ' muted' : ''); + drawGlyph(tag.querySelector('canvas'), band, 18, 12, T.ink); + tag.style.left = Math.round(Math.min(xOf(band.hzMin), M.l + PW + M.r - tag.offsetWidth - 1)) + 'px'; + tag.style.top = Math.round(yOf(win.hi) - 21) + 'px'; + }); +} + +// The tiny curve glyph on tags, tabs and shapes. Its box (w x h css px) is the +// stylesheet's, so a glyph that is not drawn yet already has its size; here +// only the pixels are made. +function drawGlyph(canvas, band, w, h, color) { + const dpr = window.devicePixelRatio || 1; + if (canvas.width !== w * dpr) { + canvas.width = w * dpr; canvas.height = h * dpr; + } + const g = canvas.getContext('2d'); + g.setTransform(dpr, 0, 0, dpr, 0, 0); + g.clearRect(0, 0, w, h); + g.strokeStyle = color; + g.lineWidth = 1.4; + g.lineCap = 'round'; + g.beginPath(); + for (let i = 0; i <= 16; i++) { + const u = i / 16; + const v = bandCurveValue(band, u); + const x = 2 + u * (w - 4); + const y = h - 2 - v * (h - 4); + if (i === 0) g.moveTo(x, y); else g.lineTo(x, y); + } + g.stroke(); +} + +// ── main plot interaction ─────────────────────────────────────────────── + +let drag = null; + +function hitTest(x, y) { + // Selected band's edges win first — reaching for a handle should not + // reselect the neighbour underneath it. + const order = [sel, ...cfg.bands.map((_, i) => i).filter((i) => i !== sel)]; + for (const i of order) { + const band = cfg.bands[i]; + const win = bandWindow(i); + const x0 = xOf(band.hzMin), x1 = xOf(band.hzMax); + const yTop = yOf(win.hi), yBot = yOf(win.lo); + const insideX = x > x0 - EDGE && x < x1 + EDGE; + const insideY = y > yTop - EDGE && y < yBot + EDGE; + if (!insideX || !insideY) continue; + // An edge is grabbed from EDGE px outside it, and from inside too, but + // never from further in than a third of the box: the middle third of + // even the smallest box stays "move", so a fat edge cannot swallow it. + const inX = Math.min(EDGE, (x1 - x0) / 3), inY = Math.min(EDGE, (yBot - yTop) / 3); + const nearL = x <= x0 + inX, nearR = x >= x1 - inX; + // Under a finger, in auto range, a top or bottom edge is not a handle: + // with a finger's EDGE those two zones are a quarter of the plot, and a + // thumb that only meant to scroll the page took all four knobs over (see + // takeOver). There the touch scrolls, or moves the box if it is inside it; + // a height is set by hand after the Auto range switch is put off. + const tall = !(COARSE && cfg.autoRange); + const nearT = tall && y <= yTop + inY, nearB = tall && y >= yBot - inY; + // Corners first — a grab there resizes both axes at once. + if (nearL && nearT) return { band: i, mode: 'tl' }; + if (nearR && nearT) return { band: i, mode: 'tr' }; + if (nearL && nearB) return { band: i, mode: 'bl' }; + if (nearR && nearB) return { band: i, mode: 'br' }; + if (nearL) return { band: i, mode: 'l' }; + if (nearR) return { band: i, mode: 'r' }; + if (nearT) return { band: i, mode: 't' }; + if (nearB) return { band: i, mode: 'b' }; + if (x > x0 && x < x1 && y > yTop && y < yBot) return { band: i, mode: 'move' }; + } + return null; +} + +// Dragging a breathing edge is the gesture that says "I'll take it from +// here": auto range switches off, and every band's window is seeded from the +// envelope it was just breathing at, so nothing jumps. A band with no live +// envelope keeps the window it had (and a source sends none for a band that +// has learned nothing: seeded from silence, a window is a sliver at the floor +// that any sound pegs at its top). This is the ONLY thing that writes an +// envelope into a window: the Auto range switch itself just goes back to the +// hand-set windows, so trying auto costs nothing. With a mouse only: see +// hitTest. +function takeOver() { + cfg.bands.forEach((band, i) => { + const env = liveEnv(i); + if (!env) return; + band.inMin = env.lo; + band.inMax = env.hi; + fitWindow(band); + }); + cfg.autoRange = false; + syncAutoToggle(); +} + +function plotPointer(event) { + const rect = plotCanvas.getBoundingClientRect(); + return { x: event.clientX - rect.left, y: event.clientY - rect.top }; +} + +// A FINGER ON A CANVAS. One that lands on something draggable is a drag, and +// the page must not scroll under it; one that misses is the browser's, so the +// page still scrolls from anywhere else on the plot. That cannot be said with +// touch-action, which is per element and not per touch (`none` made the plot +// a trap no swipe got out of; `pan-y` would give the browser the vertical +// drag of a top or bottom edge, and it takes it with a pointercancel that +// preventDefault on pointerdown does not stop). What does decide it per touch +// is a touchstart listener that is not passive: prevented, that touch never +// scrolls and its pointer events run to the end. `hits(x, y)` says whether +// the point, in the canvas's own px, is on something. +function dragOnHit(canvas, hits) { + canvas.addEventListener('touchstart', (event) => { + const rect = canvas.getBoundingClientRect(); + const touch = event.changedTouches[0]; + if (hits(touch.clientX - rect.left, touch.clientY - rect.top)) event.preventDefault(); + }, { passive: false }); +} +dragOnHit(plotCanvas, hitTest); + +plotCanvas.addEventListener('pointerdown', (event) => { + const { x, y } = plotPointer(event); + const hit = hitTest(x, y); + if (!hit) return; + if (hit.band !== sel) selectBand(hit.band); + // Armed, not moving: a press selects. Nothing is moved, resized or taken + // over until the pointer has travelled SLOP px, so a tap (or the jitter of + // one) changes nothing. + drag = { ...hit, startX: x, startY: y, live: false }; + plotCanvas.setPointerCapture(event.pointerId); + event.preventDefault(); +}); + +const SLOP = 4; + +plotCanvas.addEventListener('pointermove', (event) => { + const { x, y } = plotPointer(event); + if (!drag) { + const hit = hitTest(x, y); + plotCanvas.style.cursor = !hit ? 'default' + : hit.mode === 'move' ? 'move' + : (hit.mode === 'tl' || hit.mode === 'br') ? 'nwse-resize' + : (hit.mode === 'tr' || hit.mode === 'bl') ? 'nesw-resize' + : (hit.mode === 'l' || hit.mode === 'r') ? 'ew-resize' : 'ns-resize'; + return; + } + const band = cfg.bands[drag.band]; + if (!drag.live) { + if (Math.hypot(x - drag.startX, y - drag.startY) < SLOP) return; + // A top or bottom edge of a box in auto range (or a corner, which is one + // too): this drag takes over. ('move' has neither letter in it.) + if (cfg.autoRange && /[tb]/.test(drag.mode)) takeOver(); + const win = bandWindow(drag.band); + Object.assign(drag, { live: true, hzMin: band.hzMin, hzMax: band.hzMax, lo: win.lo, hi: win.hi }); + } + // The narrowest box, as a frequency ratio. + const MINR = 1.12; + + if (drag.mode === 'move') { + // Translate in log-frequency: the box keeps its RATIO width, which is + // what a log axis means by "same size somewhere else". + const shift = (x - drag.startX) / PW * DECADES; + let ratio = Math.pow(10, shift); + ratio = Math.max(CAPS.hzMin / drag.hzMin, Math.min(CAPS.hzMax / drag.hzMax, ratio)); + band.hzMin = drag.hzMin * ratio; + band.hzMax = Math.max(drag.hzMax * ratio, band.hzMin + CAPS.hzGap); + if (!cfg.autoRange) { + const dv = vOf(y) - vOf(drag.startY); + const span = drag.hi - drag.lo; + band.inMin = Math.max(0, Math.min(CAPS.levelMax - span, drag.lo + dv)); + band.inMax = band.inMin + span; + fitWindow(band); + } + } else { + // Edges and corners share the same four moves; a corner just does two. + // Each edge stops where the source would stop it, so what is drawn is + // what is stored and nothing moves on the next load. + if (drag.mode.includes('l')) band.hzMin = Math.max(CAPS.hzMin, Math.min(hzOf(x), band.hzMax / MINR, band.hzMax - CAPS.hzGap)); + if (drag.mode.includes('r')) band.hzMax = Math.min(CAPS.hzMax, Math.max(hzOf(x), band.hzMin * MINR, band.hzMin + CAPS.hzGap)); + if (drag.mode.includes('t')) band.inMax = Math.min(CAPS.levelMax, Math.max(vOf(y), CAPS.top(band.inMin))); + if (drag.mode.includes('b')) band.inMin = Math.max(0, Math.min(vOf(y), CAPS.bottom(band.inMax))); + } + drawPlot(); + drawCurve(); + renderChips(); + renderSettings(); +}); + +const releasePlot = () => { + if (!drag) return; + const moved = drag.live; + drag = null; + if (moved) persist(); +}; +plotCanvas.addEventListener('pointerup', releasePlot); +plotCanvas.addEventListener('pointercancel', releasePlot); + +// ── curve panel ───────────────────────────────────────────────────────── + +const curveCanvas = $('curve'); +const cctx = curveCanvas.getContext('2d'); +const CM = { l: 34, r: 10, t: 12, b: 26 }; +let CW = 0, CH = 0; +let curveDrag = null; + +// The curve's box is the stylesheet's too (#curve: 356 px wide where there is +// room, the width of its row where there is not, 218 tall). The pointer maths +// below go by CW and CH, so any size works. +function sizeCurve() { + const dpr = window.devicePixelRatio || 1; + const cssW = curveCanvas.clientWidth, cssH = 218; + curveCanvas.width = cssW * dpr; curveCanvas.height = cssH * dpr; + cctx.setTransform(dpr, 0, 0, dpr, 0, 0); + CW = cssW - CM.l - CM.r; CH = cssH - CM.t - CM.b; +} + +const cxOf = (u) => CM.l + clamp01(u) * CW; +const cyOf = (v) => CM.t + (1 - clamp01(v)) * CH; + +function drawCurve() { + const T = theme(); + const band = cfg.bands[sel]; + const W = CM.l + CW + CM.r, H = CM.t + CH + CM.b; + cctx.clearRect(0, 0, W, H); + + cctx.fillStyle = T.cream; + cctx.fillRect(CM.l, CM.t, CW, CH); + cctx.strokeStyle = T.rule; + cctx.lineWidth = 1; + cctx.strokeRect(CM.l + 0.5, CM.t + 0.5, CW - 1, CH - 1); + cctx.strokeStyle = T.ruleA(0.5); + cctx.beginPath(); cctx.moveTo(cxOf(0.5), CM.t); cctx.lineTo(cxOf(0.5), CM.t + CH); cctx.stroke(); + cctx.beginPath(); cctx.moveTo(CM.l, cyOf(0.5)); cctx.lineTo(CM.l + CW, cyOf(0.5)); cctx.stroke(); + + // curve + cctx.strokeStyle = T.ink; + cctx.lineWidth = 2; + cctx.lineCap = 'round'; + cctx.beginPath(); + const steps = band.curve && band.curve.type === 'steps'; + const N = steps ? 200 : 64; + for (let i = 0; i <= N; i++) { + const u = i / N; + const v = bandCurveValue(band, u); + const x = cxOf(u), y = cyOf(v); + if (i === 0) cctx.moveTo(x, y); else cctx.lineTo(x, y); + } + cctx.stroke(); + + // bezier handles + if (band.curve && band.curve.type === 'bezier') { + const c = band.curve; + cctx.strokeStyle = T.faint; + cctx.setLineDash([2, 3]); + cctx.beginPath(); cctx.moveTo(cxOf(0), cyOf(c.y0)); cctx.lineTo(cxOf(c.p1x), cyOf(c.p1y)); cctx.stroke(); + cctx.beginPath(); cctx.moveTo(cxOf(1), cyOf(c.y1)); cctx.lineTo(cxOf(c.p2x), cyOf(c.p2y)); cctx.stroke(); + cctx.setLineDash([]); + cctx.fillStyle = T.led; + cctx.strokeStyle = T.ink; + cctx.lineWidth = 1; + for (const [hx, hy] of [[c.p1x, c.p1y], [c.p2x, c.p2y]]) { + cctx.fillRect(cxOf(hx) - GRIP / 2, cyOf(hy) - GRIP / 2, GRIP, GRIP); + cctx.strokeRect(cxOf(hx) - GRIP / 2, cyOf(hy) - GRIP / 2, GRIP, GRIP); + } + } + + // live cursor: where the music is on this curve right now, and (top + // left) what that is: the level heard, in the axis's dB, -> the knob's + // value. The position and the value are the mapper's own figures when the + // frame has them. + const win = bandWindow(sel); + if (hearing()) { + const u = bandPos(sel); + const v = bandCurveValue(band, u); + cctx.strokeStyle = T.led; + cctx.setLineDash([3, 3]); + cctx.lineWidth = 1; + cctx.beginPath(); cctx.moveTo(cxOf(u), CM.t + CH); cctx.lineTo(cxOf(u), cyOf(v)); cctx.stroke(); + cctx.setLineDash([]); + cctx.fillStyle = T.led; + cctx.beginPath(); cctx.arc(cxOf(u), cyOf(v), 4.5, 0, Math.PI * 2); cctx.fill(); + const out = frame.outputs && frame.outputs.length ? frame.outputs[sel] : PFMap.output(band, u); + const heard = dbText(frame.levels[sel] || 0); + cctx.font = MONO; + cctx.fillStyle = T.muted; + cctx.textAlign = 'left'; + cctx.fillText((heard && heard + ' dB ') + '→ ' + Number(out).toFixed(2), CM.l + 6, CM.t + 12); + } + + // labels: along the bottom the window this curve is spread over, quiet end + // to loud end, in the plot's dB (auto range keeps moving it, so it gets the + // word); up the side the two ends of the output range. + const lo = dbText(win.lo); + cctx.font = MONO; + cctx.fillStyle = T.muted; + cctx.textAlign = 'left'; + cctx.fillText(cfg.autoRange ? 'auto range' : lo, CM.l, CM.t + CH + 16); + cctx.textAlign = 'right'; + cctx.fillText(cfg.autoRange || !lo ? '' : dbText(win.hi) + ' dB', CM.l + CW, CM.t + CH + 16); + cctx.fillText(band.outMax.toFixed(2), CM.l - 6, CM.t + 8); + cctx.fillText(band.outMin.toFixed(2), CM.l - 6, CM.t + CH); +} + +// Which handle of the selected knob's curve is at this point: 'p1', 'p2', or +// nothing (only a bezier has handles). +function curveHit(x, y) { + const c = cfg.bands[sel].curve; + if (!c || c.type !== 'bezier') return null; + const near = (hx, hy) => Math.hypot(x - cxOf(hx), y - cyOf(hy)) < REACH; + return near(c.p1x, c.p1y) ? 'p1' : near(c.p2x, c.p2y) ? 'p2' : null; +} +dragOnHit(curveCanvas, curveHit); + +curveCanvas.addEventListener('pointerdown', (event) => { + const rect = curveCanvas.getBoundingClientRect(); + curveDrag = curveHit(event.clientX - rect.left, event.clientY - rect.top); + if (!curveDrag) return; + curveCanvas.setPointerCapture(event.pointerId); + event.preventDefault(); +}); + +curveCanvas.addEventListener('pointermove', (event) => { + if (!curveDrag) return; + const rect = curveCanvas.getBoundingClientRect(); + const u = clamp01((event.clientX - rect.left - CM.l) / CW); + const v = clamp01(1 - (event.clientY - rect.top - CM.t) / CH); + const band = cfg.bands[sel]; + const c = band.curve; + if (curveDrag === 'p1') { c.p1x = u; c.p1y = v; } + else { c.p2x = u; c.p2y = v; } + c.id = 'custom'; + band.lut = bakeLut(c); + drawCurve(); + renderPresetChips(); + placeTags(); +}); + +const releaseCurve = () => { + if (!curveDrag) return; + curveDrag = null; + persist(); + renderChips(); +}; +curveCanvas.addEventListener('pointerup', releaseCurve); +curveCanvas.addEventListener('pointercancel', releaseCurve); + +// ── presets, steps, settings column ───────────────────────────────────── + +function renderPresetChips() { + const band = cfg.bands[sel]; + const id = band.curve ? (band.curve.id || 'custom') : null; + document.querySelectorAll('.preset').forEach((el) => { + el.classList.toggle('on', el.dataset.p === id); + el.setAttribute('aria-pressed', el.dataset.p === id); + }); + // One line under the shapes, and only when it is true: a knob that has no + // shape is on the source's default response (nothing to fix, so nothing is + // asked), and only a bezier has handles to drag. + $('curveHint').textContent = !band.curve ? 'This knob uses the default response.' + : band.curve.type === 'bezier' ? 'Drag the two handles for a custom shape.' : ''; + $('stepsRow').hidden = !(band.curve && band.curve.type === 'steps'); + if (band.curve && band.curve.type === 'steps') $('stepsN').textContent = String(band.curve.n); + document.querySelectorAll('.preset canvas').forEach((canvas) => { + const preset = PRESETS[canvas.parentElement.dataset.p]; + drawGlyph(canvas, { curve: preset, gain: 1 }, 40, 24, theme().ink); + }); +} + +document.querySelectorAll('.preset').forEach((el) => { + el.addEventListener('click', () => { + const preset = PRESETS[el.dataset.p]; + if (!preset) return; + touchCurve(cfg.bands[sel], structuredClone(preset)); + drawCurve(); + renderPresetChips(); + placeTags(); + renderChips(); + persist(); + }); +}); + +$('stepsMinus').addEventListener('click', () => stepsAdjust(-1)); +$('stepsPlus').addEventListener('click', () => stepsAdjust(1)); +function stepsAdjust(delta) { + const band = cfg.bands[sel]; + if (!band.curve || band.curve.type !== 'steps') return; + band.curve.n = Math.max(2, Math.min(8, band.curve.n + delta)); + band.curve.id = band.curve.n === 2 ? 'gate' : 'steps'; + band.lut = bakeLut(band.curve); + drawCurve(); + renderPresetChips(); + placeTags(); + renderChips(); + persist(); +} + +// The selected knob's card: which knob, what it listens to (its frequencies, +// and its window in the plot's dB unless auto range is setting that), its +// output range and its mute. The knob's name is also on its Reset and in +// Preview's legend. +function renderSettings() { + const band = cfg.bands[sel]; + const lo = dbText(band.inMin); + $('bandName').textContent = 'Knob ' + (sel + 1); + $('bandMeta').textContent = formatHz(band.hzMin) + ' – ' + formatHz(band.hzMax) + ' Hz' + + (cfg.autoRange ? ' · auto range' : lo && ' · ' + lo + ' to ' + dbText(band.inMax) + ' dB'); + $('outLabel').textContent = band.outMin.toFixed(2) + ' – ' + band.outMax.toFixed(2); + const w = $('outTrack').clientWidth - 10; + $('outLo').style.left = (5 + band.outMin * w) + 'px'; + $('outHi').style.left = (5 + band.outMax * w) + 'px'; + $('outFill').style.left = (5 + band.outMin * w) + 'px'; + $('outFill').style.width = Math.max(0, (band.outMax - band.outMin) * w) + 'px'; + $('muteToggle').classList.toggle('on', band.muted); + $('muteToggle').setAttribute('aria-pressed', band.muted); + $('resetBand').textContent = 'Reset K' + (sel + 1); + $('pvOut').textContent = 'K' + (sel + 1) + ' output'; +} + +document.querySelectorAll('.outPreset').forEach((el) => { + el.addEventListener('click', () => { + const band = cfg.bands[sel]; + band.outMin = Number(el.dataset.lo); + band.outMax = Number(el.dataset.hi); + renderSettings(); drawCurve(); renderChips(); persist(); + }); +}); + +// The track is dragged sideways and lets the page scroll up and down +// (touch-action: pan-y). A touch that turns out to be a scroll ends in +// pointercancel after it has already moved the handle a little: that puts +// back what the press found and saves nothing. +let outDrag = null; +$('outTrack').addEventListener('pointerdown', (event) => { + const band = cfg.bands[sel]; + const rect = $('outTrack').getBoundingClientRect(); + const v = clamp01((event.clientX - rect.left - 5) / (rect.width - 10)); + outDrag = { lo: Math.abs(v - band.outMin) < Math.abs(v - band.outMax), was: [band.outMin, band.outMax] }; + $('outTrack').setPointerCapture(event.pointerId); + event.preventDefault(); +}); +$('outTrack').addEventListener('pointermove', (event) => { + if (!outDrag) return; + const band = cfg.bands[sel]; + const rect = $('outTrack').getBoundingClientRect(); + const v = clamp01((event.clientX - rect.left - 5) / (rect.width - 10)); + if (outDrag.lo) band.outMin = Math.min(v, band.outMax - PFMap.MIN_OUT); + else band.outMax = Math.max(v, band.outMin + PFMap.MIN_OUT); + renderSettings(); drawCurve(); +}); +$('outTrack').addEventListener('pointerup', () => { + if (outDrag) { outDrag = null; persist(); } +}); +$('outTrack').addEventListener('pointercancel', () => { + if (!outDrag) return; + [cfg.bands[sel].outMin, cfg.bands[sel].outMax] = outDrag.was; + outDrag = null; + renderSettings(); drawCurve(); +}); + +$('muteToggle').addEventListener('click', () => { + const band = cfg.bands[sel]; + band.muted = !band.muted; + renderSettings(); renderChips(); drawPlot(); persist(); +}); + +// Reset band asks the SOURCE for its default: the editor has no table of its +// own, so this and the source's own "reset everything" cannot disagree. +// Whatever is waiting to be saved goes first, so the reset is the last word. +$('resetBand').addEventListener('click', async () => { + await flush(); + const next = await A.resetBand(sel, cfg); + if (next) applyConfig(next); +}); + +// The source's own "reset everything", where it has one (see the contract): +// the button is in the page, hidden, and only such a source brings it out. +if (A.resetAll) { + $('resetMap').hidden = false; + $('resetMap').addEventListener('click', function () { A.resetAll(this); }); +} + +// ── the knobs, as tabs under the plot ─────────────────────────────────── + +function renderChips() { + cfg.bands.forEach((band, i) => { + const chip = $('chip' + i); + chip.classList.toggle('sel', i === sel); + chip.setAttribute('aria-pressed', i === sel); + chip.classList.toggle('mutedChip', band.muted); + chip.querySelector('.chipHz').textContent = formatHz(band.hzMin) + '–' + formatHz(band.hzMax); + drawGlyph(chip.querySelector('canvas'), band, 18, 12, theme().muted); + }); +} + +for (let i = 0; i < 4; i++) { + $('chip' + i).addEventListener('click', () => selectBand(i)); +} + +function selectBand(index) { + sel = Math.max(0, Math.min(3, index)); + drawPlot(); + drawCurve(); + renderPresetChips(); + renderSettings(); + renderChips(); +} + +// ── preview ───────────────────────────────────────────────────────────── +// +// Ableton-style: synthetic test signals through the SELECTED knob's whole +// chain - window, glide ballistics, curve, output range - drawn as a +// scrolling scope. What the knob will do, audible music not required. +// +// The scope takes the plot's place while it runs (#pv lies over it: a test +// signal needs no spectrum), so everything it demonstrates - attack and +// damping above it, the knobs, the curve and the output range below - stays +// live around it. The Preview button, right under it, closes it again; so +// does Escape. + +const pv = $('pv'); +const pvScope = $('pvScope'); +const pvCtx = pvScope.getContext('2d'); +// The scope's box, css px: whatever the layer gives it (see pvSize). +let pvW = 0, pvH = 0; +let pvSig = 'pulse'; +let pvRaf = 0; +let pvTimer = 0; +let pvT = 0; +let pvLast = 0; +let pvLevel = 0; +const PV_SECONDS = 4; +const PV_STEP = 1 / 30; +let pvIn = []; +let pvOut = []; + +function pvSignal(t) { + if (pvSig === 'pulse') { + // A kick every 0.8 s: one sharp burst, then near-silence. The tail the + // output shows comes from the glide, which is the point of the demo. + const phase = t % 0.8; + const spike = phase < 0.06 ? 0.85 : 0; + return clamp01(spike + 0.07 + (Math.random() - 0.5) * 0.03); + } + if (pvSig === 'noisy') { + const spike = (t % 1.7) < 0.06 ? 0.5 : 0; + return clamp01(0.30 + 0.10 * Math.sin(t * 1.3) + (Math.random() - 0.5) * 0.22 + spike); + } + // swell + return clamp01(0.45 + 0.35 * Math.sin(t * Math.PI * 2 * 0.35) + (Math.random() - 0.5) * 0.04); +} + +function pvChain(sig) { + // Mirror the live pipeline: asymmetric glide on the LEVEL, then window, + // curve, output range. + // (Both alphas are already inside 0.05..0.9: the model holds them there on + // load and the two sliders cannot leave it.) + const a = pvLevel < sig ? cfg.attack : cfg.smoothing; + pvLevel += (sig - pvLevel) * a; + const band = cfg.bands[sel]; + // The model's own two functions. In auto range the test signal is taken + // as already sitting in its envelope. + return PFMap.output(band, PFMap.position(pvLevel, band, cfg.autoRange ? { lo: 0, hi: 1 } : null)); +} + +function pvDraw() { + const T = theme(); + const w = pvW, h = pvH; + pvCtx.clearRect(0, 0, w, h); + pvCtx.strokeStyle = T.ruleA(0.5); + pvCtx.lineWidth = 1; + for (const v of [0.25, 0.5, 0.75]) { + const y = Math.round(h - v * h) + 0.5; + pvCtx.beginPath(); pvCtx.moveTo(0, y); pvCtx.lineTo(w, y); pvCtx.stroke(); + } + const trace = (arr, color, width) => { + pvCtx.strokeStyle = color; + pvCtx.lineWidth = width; + pvCtx.beginPath(); + for (let i = 0; i < arr.length; i++) { + const x = (i / (PV_SECONDS / PV_STEP)) * w; + const y = h - clamp01(arr[i]) * h; + if (i === 0) pvCtx.moveTo(x, y); else pvCtx.lineTo(x, y); + } + pvCtx.stroke(); + }; + trace(pvIn, T.faint, 1.2); + trace(pvOut, T.led, 2); +} + +function pvTick(now) { + if (pv.hidden) return; + if (!pvLast) pvLast = now; + // Wall-clock scheduling, not per-frame increments: a throttled tab still + // simulates the RIGHT amount of time when frames do arrive (capped per + // frame so a background stint cannot demand thousands of steps at once). + const target = pvT + (now - pvLast) / 1000; + pvLast = now; + let budget = 60; + while (pvT < target && budget-- > 0) { + pvT += PV_STEP; + const sig = pvSignal(pvT); + pvIn.push(sig); + pvOut.push(pvChain(sig)); + const cap = PV_SECONDS / PV_STEP; + if (pvIn.length > cap) { pvIn.shift(); pvOut.shift(); } + } + if (budget < 0) pvT = target; + pvDraw(); + pvSchedule(); +} + +// rAF when the surface is live, a timer backstop when it is throttled or +// hidden - the preview keeps moving either way. +function pvSchedule() { + pvRaf = requestAnimationFrame(pvTick); + clearTimeout(pvTimer); + pvTimer = setTimeout(() => { + cancelAnimationFrame(pvRaf); + pvTick(performance.now()); + }, 300); +} + +// The scope draws into the box the stylesheet gives it: what is left of the +// plot's box under the row of test signals. No size of its own. +function pvSize() { + const dpr = window.devicePixelRatio || 1; + pvW = pvScope.clientWidth; pvH = pvScope.clientHeight; + pvScope.width = pvW * dpr; pvScope.height = pvH * dpr; + pvCtx.setTransform(dpr, 0, 0, dpr, 0, 0); +} + +// On, off: the one switch. The button says which it is (aria-pressed). +function pvSet(on) { + pv.hidden = !on; + $('openPreview').setAttribute('aria-pressed', on); + cancelAnimationFrame(pvRaf); + clearTimeout(pvTimer); + if (!on) return; + pvSize(); + pvIn = []; pvOut = []; pvT = 0; pvLast = 0; pvLevel = 0; + pvSchedule(); +} + +$('openPreview').addEventListener('click', () => pvSet(pv.hidden)); +document.addEventListener('keydown', (event) => { + if (event.key === 'Escape' && !pv.hidden) pvSet(false); +}); +document.querySelectorAll('.pvSig').forEach((el) => { + el.addEventListener('click', () => { + pvSig = el.dataset.s; + document.querySelectorAll('.pvSig').forEach((x) => x.classList.toggle('on', x === el)); + }); +}); + +// ── auto toggle, status, wiring ───────────────────────────────────────── + +// The switch, and beside it the one thing to know about the boxes in the mode +// it is in. +function syncAutoToggle() { + $('autoToggle').classList.toggle('on', cfg.autoRange); + $('autoToggle').setAttribute('aria-pressed', cfg.autoRange); + $('autoHint').textContent = cfg.autoRange ? HINT_AUTO : HINT_HAND; +} + +// The switch only switches: off goes back to the windows set by hand, which +// auto range never touched. (Dragging an edge is what keeps an envelope.) +$('autoToggle').addEventListener('click', () => { + cfg.autoRange = !cfg.autoRange; + syncAutoToggle(); + drawPlot(); drawCurve(); renderSettings(); + persist(); +}); + +// Damping = the level smoothing the analysis already runs (an EMA on each +// band, before the window and curve). Right means calmer: the slider's 0..1 +// maps to alpha 0.9..0.05, so a gentle word scale replaces a number nobody +// would know the direction of. It smooths the INPUT level, not the output — +// gate and steps curves stay crisp, their trigger just stops flickering. +const DAMP_WORDS = [[0.25, 'tight'], [0.5, 'balanced'], [0.75, 'smooth'], [1.01, 'glassy']]; +const ATK_WORDS = [[0.25, 'snap'], [0.5, 'quick'], [0.75, 'soft'], [1.01, 'lazy']]; + +const dampWord = (v) => DAMP_WORDS.find(([top]) => v < top)[1]; + +function syncDamping() { + const v = clamp01((0.9 - cfg.smoothing) / 0.85); + $('damping').value = v; + $('dampingWord').textContent = dampWord(v); + const k = clamp01((0.9 - cfg.attack) / 0.85); + $('attack').value = k; + $('attackWord').textContent = ATK_WORDS.find(([top]) => k < top)[1]; +} + +$('attack').addEventListener('input', () => { + const v = clamp01($('attack').value); + cfg.attack = 0.9 - v * 0.85; + $('attackWord').textContent = ATK_WORDS.find(([top]) => v < top)[1]; + persist(); +}); + +$('damping').addEventListener('input', () => { + const v = clamp01($('damping').value); + cfg.smoothing = 0.9 - v * 0.85; + $('dampingWord').textContent = dampWord(v); + persist(); +}); + +// Everything drawn is sized from the page, so a window made narrower (half a +// screen beside the music, a phone turned round) is measured again. +window.addEventListener('resize', () => { + if (!cfg) return; + sizePlot(); + sizeCurve(); + if (!pv.hidden) pvSize(); + drawPlot(); + drawCurve(); + renderSettings(); +}); + +// An in-page "are you sure": the first press arms the button and makes it ask, +// a second press within four seconds answers yes. A second press that comes +// within half a second of the first is the same double click or double tap, +// not an answer to a question nobody has had time to read: it is ignored, and +// the button stays armed. window.confirm is not used anywhere on these pages: +// the phone app shows /audio-in in a WebView that has none (it answers "no" +// without showing anything). Use: +// if (!PFConfirm(button, 'Reset everything? Press again')) return; +window.PFConfirm = function (button, ask) { + if (button._armed) { + if (Date.now() - button._at < 500) return false; + clearTimeout(button._armed); + button._armed = 0; + button.textContent = button._label; + return true; + } + button._label = button.textContent; + button.textContent = ask; + button._at = Date.now(); + button._armed = setTimeout(() => { + button._armed = 0; + button.textContent = button._label; + }, 4000); + return false; +}; + +(async function init() { + const stored = await A.loadConfig(); + readCaps(); + cfg = PFMap.normalizeConfig(stored, CAPS); + sizePlot(); + sizeCurve(); + syncAutoToggle(); + syncDamping(); + selectBand(0); + // The attribute, not the property: a browser too old to know `inert` would + // keep the attribute, and with it what the stylesheet hides before the + // first mapping. + $('ed').removeAttribute('inert'); + if (A.onConfig) A.onConfig(applyConfig); + A.onFrame((state) => { + const was = String(axisDb()); + frame = state || {}; + // A frame on another level axis (`db` in the contract) changes the dB the + // card prints, not only the plot's labels. + if (String(axisDb()) !== was) renderSettings(); + drawPlot(); + drawCurve(); + }); +})(); diff --git a/web/public/guide/audio-editor/index.html b/web/public/guide/audio-editor/index.html new file mode 100644 index 00000000..f7fb6b86 --- /dev/null +++ b/web/public/guide/audio-editor/index.html @@ -0,0 +1,185 @@ + + + + + + +Patternflow Audio — Mapping + + + +
              + + +
              +
              + + Patternflow Audio + MAPPING +
              +
              + + idle + + +
              +
              + + + +
              + +
              + + Each box follows the loudness of what it hears. Drag a top or bottom edge to take all four over by hand. +
              +
              + + + +
              +
              + + + +
              +
              +
              + + +
              + +
              K1
              +
              K2
              +
              K3
              +
              K4
              + +
              + + +
              +
              + + + + +
              + +
              + +
              +
              + Knob 1 + +
              + +
              + Response curve +
              + +
              +
              + + + + + + +
              + + +
              +
              +
              + +
              + +
              +
              +
              + Output range + +
              +
              +
              +
              +
              +
              +
              +
              + + + + + + +
              +
              + + +
              +
              + + +
              + + +
              + +
              + + +
              + + + + + + diff --git a/web/public/guide/audio-editor/mapping.js b/web/public/guide/audio-editor/mapping.js new file mode 100644 index 00000000..975fc629 --- /dev/null +++ b/web/public/guide/audio-editor/mapping.js @@ -0,0 +1,236 @@ +// Patternflow Audio — the mapping model. +// +// What a mapping is, written once. Four bands, one per knob. A band listens +// to a range of frequencies and takes the level it hears there through +// +// level -> window -> response curve -> [rests at .. peaks at] +// +// where the window is the band's own (manual) or the envelope the band has +// recently moved in (auto range). offscreen.js runs that chain for the knobs; +// the editor draws it and previews it; both ask this file, so a box on the +// screen and a knob on the panel cannot be told two different things. +// +// It is ONE object on purpose. These are classic scripts sharing a global +// scope, where a second top-level `clamp01` in any file is a SyntaxError that +// kills the page at load and that `node --check` on each file cannot see. +// Everything here is reached as PFMap.something; nothing else is declared. +// +// Two halves. Down to the line that says so, this is the model: no chrome.*, +// no DOM, none of the extension's own numbers. That half also travels to the +// panel, inside its /audio-in page (firmware/toolchain/build_audio_in_page.py +// stops reading at the line), so every byte of it is sent over the panel's +// Wi-Fi: keep it to what the editor needs on both sides. Below the line is +// what only the extension's audio source knows. + +var PFMap = (function () { + 'use strict'; + + const within = (v, lo, hi) => Math.max(lo, Math.min(hi, Number(v) || 0)); + const clamp01 = (v) => within(v, 0, 1); + + // ── response curves ───────────────────────────────────────────────────── + // + // A curve is authored as one of these shapes and BAKED to a 33-point table. + // Whatever maps for real - offscreen.js here, the firmware on the panel - + // only interpolates the table, and never learns what a bezier is. + + const PRESETS = { + smooth: { type: 'bezier', id: 'smooth', y0: 0, y1: 1, p1x: 0.45, p1y: 0.05, p2x: 0.55, p2y: 0.95 }, + sharp: { type: 'bezier', id: 'sharp', y0: 0, y1: 1, p1x: 0.10, p1y: 0.65, p2x: 0.35, p2y: 1.00 }, + fall: { type: 'bezier', id: 'fall', y0: 1, y1: 0, p1x: 0.45, p1y: 0.95, p2x: 0.55, p2y: 0.05 }, + gate: { type: 'steps', id: 'gate', n: 2 }, + steps: { type: 'steps', id: 'steps', n: 3 }, + arch: { type: 'arch', id: 'arch' } + }; + + function evalCurve(curve, u) { + u = clamp01(u); + if (!curve) return u; + if (curve.type === 'steps') { + const n = within(Math.round(curve.n) || 2, 2, 8); + return Math.min(n - 1, Math.floor(u * n)) / (n - 1); + } + if (curve.type === 'arch') return Math.sin(Math.PI * u); + if (curve.type === 'bezier') { + // Cubic from (0, y0) to (1, y1); x(t) is monotone while both control xs + // stay in [0,1], so a short bisection recovers t for any u. + const { y0 = 0, y1 = 1, p1x, p1y, p2x, p2y } = curve; + const bx = (t) => 3 * (1 - t) * (1 - t) * t * p1x + 3 * (1 - t) * t * t * p2x + t * t * t; + const by = (t) => (1 - t) * (1 - t) * (1 - t) * y0 + 3 * (1 - t) * (1 - t) * t * p1y + 3 * (1 - t) * t * t * p2y + t * t * t * y1; + let lo = 0, hi = 1; + for (let i = 0; i < 24; i++) { + const mid = (lo + hi) / 2; + if (bx(mid) < u) lo = mid; else hi = mid; + } + return clamp01(by((lo + hi) / 2)); + } + return u; + } + + function bakeLut(curve) { + const lut = []; + for (let i = 0; i <= 32; i++) lut.push(Number(evalCurve(curve, i / 32).toFixed(4))); + return lut; + } + + // ── level to knob ─────────────────────────────────────────────────────── + + // The narrowest window and the shortest stretch of knob a band may have. + const MIN_WINDOW = 0.02; + const MIN_OUT = 0.05; + + const api = { + clamp01, PRESETS, evalCurve, bakeLut, MIN_WINDOW, MIN_OUT, + + // The response at a position in the window: the band's curve, or, for a + // band saved before there were curves, its boost exponent (above 1x a + // quiet band reaches its top early, below 1x a loud one holds back). + curveValue(band, u) { + return band.curve ? evalCurve(band.curve, u) + : Math.pow(clamp01(u), 1 / within(Number(band.gain) || 1, 0.2, 4)); + }, + + // Where a level sits in the band's window, 0..1: the input of the curve. + // `env` is the band's { lo, hi } envelope while auto range runs, null for + // the band's own inMin..inMax. Auto range maps a fixed slice of the + // envelope: under 0.10 of it is squelched, so the resting noise at the + // floor does not dance, and 0.95 of it is already full scale. + position(level, band, env) { + if (env) { + return clamp01((clamp01((level - env.lo) / Math.max(0.001, env.hi - env.lo)) - 0.10) / (0.95 - 0.10)); + } + const lo = clamp01(band.inMin); + return clamp01((level - lo) / Math.max(MIN_WINDOW, clamp01(band.inMax) - lo)); + }, + + // The knob value for a position in the window, through the curve. + output(band, u) { + const lo = clamp01(band.outMin); + return lo + api.curveValue(band, u) * (clamp01(band.outMax) - lo); + }, + + // The default band: what Reset gives, and what a first run starts with. + defaultBand(index, caps) { + return normalizeBand(null, index, caps); + }, + + // The whole mapping, made whole: { autoRange, smoothing, attack, bands[4] }. + // `caps` is the source's { hzMin, hzMax }. smoothing and attack are the + // two alphas of the level glide: how fast a level falls, how fast it rises. + // + // Nothing stored is a first run, and starts the way the panel does: auto + // range on, four default bands. Something stored is somebody's mapping + // and is not improved behind their back; in particular one saved before + // auto range existed has no `autoRange` and stays manual. + normalizeConfig(raw, caps) { + const stored = raw && Array.isArray(raw.bands) ? raw : { autoRange: true, bands: [] }; + return { + autoRange: stored.autoRange === true, + smoothing: within(Number(stored.smoothing) || 0.35, 0.05, 0.9), + attack: within(Number(stored.attack) || 0.65, 0.05, 0.9), + bands: [0, 1, 2, 3].map((i) => normalizeBand(stored.bands[i], i, caps)) + }; + } + }; + + const HZ = [[60, 250], [250, 2000], [2000, 5000], [5000, 16000]]; + + // lo and hi inside floor..ceiling, in that order, at least `gap` apart. + function pair(lo, hi, floor, ceiling, gap) { + lo = within(lo, floor, ceiling); + hi = within(hi, floor, ceiling); + if (lo > hi) [lo, hi] = [hi, lo]; + if (hi - lo < gap) { + hi = Math.min(ceiling, lo + gap); + lo = Math.min(lo, hi - gap); + } + return [lo, hi]; + } + + // One band, made whole. No band at all is the default: its slice of the + // spectrum, the full window, the Smooth curve already baked. A band that IS + // stored keeps what it has and gains nothing: saved before curves existed + // it has no curve and keeps its boost exponent, and saved when the output + // range was `base` + `range` it is read as that. + function normalizeBand(raw, index, caps) { + const b = raw && typeof raw === 'object' ? raw + : { outMin: 0.30, outMax: 0.85, curve: { ...PRESETS.smooth }, lut: bakeLut(PRESETS.smooth) }; + const base = b.base ?? 0.30; + const [hzMin, hzMax] = pair(b.hzMin ?? HZ[index][0], b.hzMax ?? HZ[index][1], caps.hzMin, caps.hzMax, 10); + const [inMin, inMax] = pair(b.inMin ?? 0, b.inMax ?? 1, 0, 1, MIN_WINDOW); + const [outMin, outMax] = pair(b.outMin ?? base, b.outMax ?? base + (b.range ?? 0.55), 0, 1, MIN_OUT); + return { + hzMin, hzMax, + // Four bands, four knobs, one line between them: band i drives knob i. + knob: index, + muted: b.muted === true, + inMin, inMax, + gain: within(Number(b.gain) || 1, 0.2, 4), + outMin, outMax, + curve: b.curve && typeof b.curve === 'object' ? b.curve : null, + lut: Array.isArray(b.lut) && b.lut.length > 1 ? b.lut : null + }; + } + + return api; +})(); + +// ── EXTENSION ONLY ──────────────────────────────────────────────────────── +// +// What the extension's audio source knows and the panel's does not. + +(function (M) { + 'use strict'; + + // Where the mapping is stored. The panel's address is not in it: that is the + // popup's, under a key of its own, so the editor and the popup never write + // the same value and neither can put the other's old one back. + M.CONFIG_KEY = 'patternflowAudioConfig'; + + // Tab audio through an AnalyserNode: the frequencies worth drawing, and the + // dB the level axis runs between, bottom then top. Levels everywhere in the + // extension are 0..1 along that axis. + M.SOURCE = { hzMin: 20, hzMax: 20000, db: [-80, -10] }; + + M.normalizeDb = function (db) { + return M.clamp01((db - M.SOURCE.db[0]) / (M.SOURCE.db[1] - M.SOURCE.db[0])); + }; + + // The knob gets the band's TABLE where it has one, interpolated the way the + // firmware interpolates it, and not the curve the table was baked from: a + // table rounds a step's corner, and what is sent should be what a panel + // running the same mapping would do. Whatever in the extension asks for a + // knob value gets this: PFMap.output() reaches it, so offscreen.js and the + // editor's Preview do. + const drawn = M.curveValue; + M.curveValue = function (band, u) { + const lut = band.lut; + if (!lut || lut.length < 2) return drawn(band, u); + const at = M.clamp01(u) * (lut.length - 1); + const i = Math.floor(at); + const j = Math.min(lut.length - 1, i + 1); + return M.clamp01(lut[i]) * (1 - (at - i)) + M.clamp01(lut[j]) * (at - i); + }; + + // Auto-range envelope, one per band: the floor and peak the band has + // recently seen, in the same units as its level. Both edges jump to a new + // extreme at once and drift back slowly (ENV_RELEASE per 33 ms tick is about + // 8 s, the panel's own time constant). ENV_MIN_SPAN stops silence from + // collapsing the window into a noise amplifier. There is no microphone gate + // here, unlike the panel: tab audio is digital, and silent means zero. + M.ENV_RELEASE = 0.004; + M.ENV_MIN_SPAN = 0.06; + + M.newEnvelope = function () { + return { lo: 1, hi: 0 }; + }; + + M.trackEnvelope = function (env, level, release) { + const v = M.clamp01(level); + const r = release || M.ENV_RELEASE; + if (v > env.hi) env.hi = v; else env.hi += (v - env.hi) * r; + if (v < env.lo) env.lo = v; else env.lo += (v - env.lo) * r; + if (env.hi < env.lo + M.ENV_MIN_SPAN) env.hi = env.lo + M.ENV_MIN_SPAN; + return env; + }; +})(PFMap); diff --git a/web/public/guide/audio/audio-in-listening.webp b/web/public/guide/audio/audio-in-listening.webp new file mode 100644 index 00000000..7d0f6b51 Binary files /dev/null and b/web/public/guide/audio/audio-in-listening.webp differ diff --git a/web/public/guide/audio/audio-in-mapping.webp b/web/public/guide/audio/audio-in-mapping.webp new file mode 100644 index 00000000..41768973 Binary files /dev/null and b/web/public/guide/audio/audio-in-mapping.webp differ diff --git a/web/public/guide/audio/audio-in-nomic.webp b/web/public/guide/audio/audio-in-nomic.webp new file mode 100644 index 00000000..8c4e62c4 Binary files /dev/null and b/web/public/guide/audio/audio-in-nomic.webp differ diff --git a/web/public/guide/audio/audio-in-off.webp b/web/public/guide/audio/audio-in-off.webp new file mode 100644 index 00000000..dc3f2734 Binary files /dev/null and b/web/public/guide/audio/audio-in-off.webp differ diff --git a/web/public/guide/audio/live-remote.webp b/web/public/guide/audio/live-remote.webp new file mode 100644 index 00000000..f33d2748 Binary files /dev/null and b/web/public/guide/audio/live-remote.webp differ diff --git a/web/public/guide/audio/mic-part.webp b/web/public/guide/audio/mic-part.webp new file mode 100644 index 00000000..f48a445a Binary files /dev/null and b/web/public/guide/audio/mic-part.webp differ diff --git a/web/public/guide/audio/mic-seated.webp b/web/public/guide/audio/mic-seated.webp new file mode 100644 index 00000000..30349dd1 Binary files /dev/null and b/web/public/guide/audio/mic-seated.webp differ diff --git a/web/public/guide/audio/mic-wiring.webp b/web/public/guide/audio/mic-wiring.webp new file mode 100644 index 00000000..f4a92134 Binary files /dev/null and b/web/public/guide/audio/mic-wiring.webp differ diff --git a/web/public/guide/audio/midi-knobs.webp b/web/public/guide/audio/midi-knobs.webp new file mode 100644 index 00000000..c0ddafd8 Binary files /dev/null and b/web/public/guide/audio/midi-knobs.webp differ diff --git a/web/public/guide/audio/midi-session.webp b/web/public/guide/audio/midi-session.webp new file mode 100644 index 00000000..77c5ee97 Binary files /dev/null and b/web/public/guide/audio/midi-session.webp differ diff --git a/web/public/guide/audio/popup-audoff.webp b/web/public/guide/audio/popup-audoff.webp new file mode 100644 index 00000000..41b2e80c Binary files /dev/null and b/web/public/guide/audio/popup-audoff.webp differ diff --git a/web/public/guide/audio/popup-idle.webp b/web/public/guide/audio/popup-idle.webp new file mode 100644 index 00000000..8c4dfcac Binary files /dev/null and b/web/public/guide/audio/popup-idle.webp differ diff --git a/web/public/guide/audio/popup-live.webp b/web/public/guide/audio/popup-live.webp new file mode 100644 index 00000000..490416c4 Binary files /dev/null and b/web/public/guide/audio/popup-live.webp differ diff --git a/web/public/guide/audio/popup-nosound.webp b/web/public/guide/audio/popup-nosound.webp new file mode 100644 index 00000000..0def11ca Binary files /dev/null and b/web/public/guide/audio/popup-nosound.webp differ diff --git a/web/public/guide/audio/rtpmidi-1.webp b/web/public/guide/audio/rtpmidi-1.webp new file mode 100644 index 00000000..6a4d4d42 Binary files /dev/null and b/web/public/guide/audio/rtpmidi-1.webp differ diff --git a/web/public/guide/audio/rtpmidi-2.webp b/web/public/guide/audio/rtpmidi-2.webp new file mode 100644 index 00000000..a6593b93 Binary files /dev/null and b/web/public/guide/audio/rtpmidi-2.webp differ diff --git a/web/public/guide/audio/rtpmidi-3.webp b/web/public/guide/audio/rtpmidi-3.webp new file mode 100644 index 00000000..95543473 Binary files /dev/null and b/web/public/guide/audio/rtpmidi-3.webp differ diff --git a/web/public/guide/audio/rtpmidi-4.webp b/web/public/guide/audio/rtpmidi-4.webp new file mode 100644 index 00000000..89416e40 Binary files /dev/null and b/web/public/guide/audio/rtpmidi-4.webp differ diff --git a/web/public/guide/console-demo/index.html b/web/public/guide/console-demo/index.html index 280f8d21..d75da0b2 100644 --- a/web/public/guide/console-demo/index.html +++ b/web/public/guide/console-demo/index.html @@ -2,323 +2,162 @@ - + Patternflow - + - - -
              -

              Console

              device
              - - +
              +
              -
              -
              -
              - Now playing +
              +
              +
              +

              Now playing

              -
              - - -
              + +
              —
              -
              - - -
              - Panel -
              - - +
              + +
              +
              + Panel +
              + + +
              -
              -
              -
              - Brightness - - — +
              +
              —
              +
              -
              -
              - Live Knobs - -
              -
              - K1 - -
              -
              - K2 - -
              -
              - K3 - -
              -
              - K4 - -
              +
              +
              Knobs
              +
              +
              +
              +
              -
              -
              Patterns
              —
              -
              Storage
              —
              -
              -
              Wi-Fi
              —
              -
              Address
              —
              +
              +
              Patterns—
              +
              Storage—
              +
              Wi-Fi—
              +
              Address—
              - -
              Memory
              —
              -
              Uptime
              —
              -
              Firmware
              —
              - - - Edition -
              —
              -
              -
              + from the one above, so its own line. --> + +
              Memory—
              +
              Uptime—
              - - - - update - how to update →
              - @@ -330,85 +169,70 @@ function $(i){return document.getElementById(i)} $('host').textContent=location.hostname; +// A range's filled part (--p) follows its value. +function fill(el){el.style.setProperty('--p',(el.value-el.min)/(el.max-el.min)*100+'%')} +document.addEventListener('input',function(e){if(e.target.type==='range')fill(e.target)}); + // ── What this build actually has ─────────────────────────────── -// Feature rows are not written into this page. Whatever features the firmware -// carries report their pages in /api/status featureNav — path, label, one -// line — and the Features group is built from that. A build with none gets -// no group, and a page the core has never heard of still gets a row. This -// used to work the other way round: rows for every feature in the markup, -// deleted when the capability was absent, which meant a core page naming -// features and a new feature's page getting no row at all. -// -// Rebuilt from scratch whenever featureNav changes (not on every status: the -// card polls, and a rebuild under the pointer drops hover and focus), then -// everything renumbers so the list still reads 01, 02, 03. +// Feature rows, and the edition card's chips, come from /api/status +// featureNav (path, label, one line): a build with none gets none, and a +// page the core never heard of still gets a row. Rebuilt only when it +// changes: a rebuild under the pointer drops hover and focus. var navKey; function gate(s){ - var i,nav=(s&&s.featureNav)||[],k=JSON.stringify(nav); + var i,nav=(s&&s.featureNav)||[],k=JSON.stringify(nav),f=$('featRows'),c=$('s-feat'); if(k===navKey)return; navKey=k; - var f=$('featRows'),fk=$('featKick'); - if(f){ - f.innerHTML=''; - for(i=0;i
              '; - a.querySelector('.pf-row-t').textContent=e[1]; - a.querySelector('.pf-row-d').textContent=e[2]||''; - f.appendChild(a); - } - f.hidden=fk.hidden=!f.querySelector('.pf-row'); + f.innerHTML=c.innerHTML=''; + for(i=0;i'; + a.firstChild.textContent=e[1]; + a.lastChild.textContent=e[2]||''; + f.appendChild(a); + a=document.createElement('a'); + a.href=e[0];a.className='btn';a.textContent=e[1]; + c.appendChild(a); } - var ghosts=document.querySelectorAll('.rows .pf-ghost'); - for(i=0;i 100) v = 100; - var sl = $('sl-' + i); - if (sl) { - sl.value = v; - lastReported[i] = v; - } - } -} - -function pumpParams() { - if (inFlight) return; // Drop redundant updates while in-flight; keep newest in queue - var keys = Object.keys(queuedParams); - if (!keys.length) return; - - var bodyParts = []; - for (var i = 0; i < keys.length; i++) { - var k = keys[i]; - bodyParts.push(k + '=' + encodeURIComponent(queuedParams[k])); +// ── Knob sliders (K1..K4 · 0..100) ──────────────────────────── +var activeSlider=-1,inFlight=false,queuedParams={}; +function updateSliders(params){ + if(!params||params.length<4)return; + for(var i=0;i<4;i++){ + if(activeSlider===i)continue; + var v=Math.round(params[i]/10); + if(v<0)v=0;if(v>100)v=100; + var sl=$('sl-'+i);sl.value=v;fill(sl); } - queuedParams = {}; - inFlight = true; - - fetch('/api/params', { - method: 'POST', - cache: 'no-store', - headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, - body: bodyParts.join('&') - }).then(function(r) { - if (!r.ok) throw Error('refused'); - $('ctrlmsg').textContent = ''; - }).catch(function(x) { - // Next to the sliders, not swallowed: a drag that never arrived looks - // exactly like a pattern ignoring its knobs. - PF.say('not sent — ' + (x && x.message === 'refused' ? 'refused' : 'no answer'), 'err', $('ctrlmsg')); - }).then(function() { - inFlight = false; - if (Object.keys(queuedParams).length) { - pumpParams(); // Flush pending latest updates - } - }); } - -function onSliderInput(idx, val) { - var v = parseInt(val, 10); - if (isNaN(v)) return; - lastReported[idx] = v; - - // pX on the 0..1000 bus. The device derives the clicks a legacy pattern - // reads from the change in the held value, so no dX alongside — sending - // both counted every move twice. - queuedParams['p' + (idx + 1)] = v * 10; - - pumpParams(); -} - -function initSliders() { - for (var i = 0; i < 4; i++) { - (function(idx) { - var sl = $('sl-' + idx); - if (!sl) return; - held(sl, function() { activeSlider = idx; }, function() { - if (activeSlider === idx) activeSlider = -1; - pumpParams(); - }); - sl.addEventListener('input', function() { - var v = sl.value; - onSliderInput(idx, v); - }); - })(i); - } +function pumpParams(){ + if(inFlight)return; // one in flight; the newest values wait in the queue + var keys=Object.keys(queuedParams),body=[],i; + if(!keys.length)return; + for(i=0;i 0 ? 'Next…' : 'Prev…'; - PF.get('/api/patterns/select?step=' + dir).then(function(d) { - $('now').textContent = d.name || prev; - $('nowsub').textContent = 'switched'; - $('stepmsg').textContent = ''; - }, function(x) { - $('now').textContent = prev; - if (!x || !x.nav) - PF.say(x && x.status === 404 ? 'nothing to switch to' : 'not switched', 'err', $('stepmsg')); +// ── Pattern stepping (← / →) ───────────────────────────────── +// The reply names the new pattern; the next status confirms it. One step +// in flight (?step is ±1); presses meanwhile add up in pend. +var stepping=0,pend=0; +function stepPattern(dir){ + if(stepping){pend+=dir;return} + var prev=$('now').textContent; + $('now').textContent=dir>0?'Next…':'Prev…'; + stepping=1; + PF.get('/api/patterns/select?step='+dir).then(function(d){ + stepping=0; + $('now').textContent=d.name||prev; + $('nowsub').textContent='switched'; + $('stepmsg').textContent=''; + if(pend){dir=pend>0?1:-1;pend-=dir;stepPattern(dir)} + },function(x){ + stepping=pend=0; + $('now').textContent=prev; + if(!x||!x.nav) + PF.say(x&&x.status===404?'nothing to switch to':'not switched','err',$('stepmsg')); }); } -$('pat-prev').addEventListener('click', function() { stepPattern(-1); }); -$('pat-next').addEventListener('click', function() { stepPattern(1); }); +$('pat-prev').addEventListener('click',function(){stepPattern(-1)}); +$('pat-next').addEventListener('click',function(){stepPattern(1)}); +// The arrow keys too, unless a field or slider has them or a modifier is +// held (Alt+Left is Back). +document.addEventListener('keydown',function(e){ + var d=e.key=='ArrowLeft'?-1:e.key=='ArrowRight'?1:0; + if(!d||e.repeat||e.altKey||e.ctrlKey||e.metaKey||e.shiftKey||/^(INPUT|SELECT|TEXTAREA)$/.test(e.target.tagName))return; + e.preventDefault();stepPattern(d); +}); +[].forEach.call(document.querySelectorAll('input[type=range]'),fill); PF.watchStatus(3000); PF.status(paint); diff --git a/web/public/guide/console-demo/knobs.html b/web/public/guide/console-demo/knobs.html index 3521e587..04178361 100644 --- a/web/public/guide/console-demo/knobs.html +++ b/web/public/guide/console-demo/knobs.html @@ -1,101 +1,86 @@ - + Patternflow - Knobs
              -

              Knobs

              -
              -
              -

              Direction and click size

              - - - -
              PositionDirectionEdges per click
              -
              - - -
              -
              -

              Turn a knob clockwise. Its position should go up by one per click. - Goes down: set that knob to Reversed. Moves two per click, or needs two clicks to - move one: change Edges per click until one click is one step (most encoders are 4; - the edge counter under the position shows how many one click really makes). - These are properties of the encoder soldered in, stored on the device — nothing to - rebuild, and a saved change does not move the knob.

              -
              - +tr.all td{border-color:var(--rule)} +.pf-offline .pos,.pf-offline #st{opacity:.5} + +
              +

              Knobs

              -
              +

              Turn a knob one click clockwise: its position should go up by one.

              + + + + + +
              PositionDirectionEdges per click
              All
              +
              + + +
              +

              Goes down: Reversed. Moves two, or needs two clicks to move one: change edges per click +(most encoders are 4; the count under the position is what one click makes). Saved on the panel; saving does not move a knob.

              + +
              diff --git a/web/public/guide/console-demo/patterns.html b/web/public/guide/console-demo/patterns.html index 6c2f4b4f..78c7a428 100644 --- a/web/public/guide/console-demo/patterns.html +++ b/web/public/guide/console-demo/patterns.html @@ -1,194 +1,149 @@ - + Patternflow - Patterns
              -

              Patterns

              -
              - - - -
              -

              Storage needs formatting

              -

              This board's pattern partition isn't readable — either it has never been - formatted, or its filesystem is damaged. Format it once, and patterns install - normally from then on.

              -
              - - -
              +/* Touch: no drag and drop on a phone, so arrows instead, in the room the + module chip leaves (the checkbox says it is a module). Narrow buttons keep + the name wide; their 40px hit areas meet in the 6px gaps. */ +@media(pointer:coarse){.btn.mv{display:inline-flex;width:34px;padding:0;font-size:16px}.chip.mod,.fine{display:none} +.list>li{gap:6px;padding:4px 6px}.list .btn.sm::after{inset:-4px -3px}.list .dan{padding:0 8px}#bulk{row-gap:10px}} +@media(min-width:960px){.wrap{max-width:1080px;padding-top:32px} +.grid{display:grid;grid-template-columns:380px minmax(0,1fr);gap:48px;align-items:start} +.grid section{margin-top:0}.lc{position:sticky;top:8px}.q{max-height:55vh;overflow:auto}} + + +
              +

              Patterns

              —
              + + + -
              -
              +
              +

              Upload

              -
                +
                -
                -
                - -
                +
                -
                +
                -

                Installed

                -
                - - - - - +
                +

                Installed

                +
                -
                -
                  +
                    + +

                    Tap or click a name to play it. Drag modules to reorder. Presets are built into the firmware and can’t be removed here.

                    -
                    Presets are built into the firmware and cannot be removed here. - Home
                    -
                    + +
                    \ No newline at end of file + diff --git a/web/public/guide/console-demo/pf-console.js b/web/public/guide/console-demo/pf-console.js index 696f93fa..64adfbf6 100644 --- a/web/public/guide/console-demo/pf-console.js +++ b/web/public/guide/console-demo/pf-console.js @@ -1,31 +1,33 @@ (function(){ if(window.PF)return; -var W=window,D=document,H=D.documentElement,L=location,light=false; +var W=window,D=document,H=D.documentElement,L=location,dark=false,cn=null,hm={},k, +st=D.createElement('style'),c; // Theme before first paint: this script is parser-blocking in . -try{light=localStorage.getItem('pf-theme')==='light'}catch(e){} -if(light)H.setAttribute('data-theme','light'); -var st=D.createElement('style'); -// --pf-chrome-h is the header's exact height (see the shadow CSS below). -st.textContent='html[data-theme=light]{color-scheme:light;--cream:#F4EFE6;--cream2:#FFFCFA;--ink:#1A1814;--muted:#6B6558;--faint:#9A9486;--rule:#D9D1C2;--rule-soft:#E8E2D6;--led:#FF5C2E;--ok:#2F8A55;--warn:#B88120;--panel:#FFFCFA;--bg:#F4EFE6;--ghost:#E0D9CC;--card:#FFFCFA;--card2:#E8E2D6;--fg:#1A1814;--mut:#6B6558;--ln:#D9D1C2;--bad:#B8402E;--bar:#E0D9CC;--bar-on:#FF5C2E;--accent:#FF5C2E}' -+':root{color-scheme:dark;--pf-chrome-h:81px}@media(min-width:720px){:root{--pf-chrome-h:41px}}' -+'html:not(.pf-mounted) body::before{content:"";display:block;box-sizing:border-box;height:var(--pf-chrome-h);border-bottom:1px solid var(--rule,#242118);background:var(--cream,var(--bg,#0C0B09))}' +try{dark=localStorage.getItem('pf-theme')==='dark';cn=JSON.parse(localStorage.getItem('pf-nav'));hm=cn&&cn.h||{}}catch(e){} +H.setAttribute('data-theme',dark?'dark':'light'); +// Light palette = the core pages' :root values; outranks any page's own. +c='html[data-theme]{--sans:system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;--mono:ui-monospace,"Cascadia Mono","SF Mono",Menlo,Consolas,"Roboto Mono",monospace}' ++'html:root[data-theme=light]{color-scheme:light;--cream:#F4EFE6;--panel:#FFFCFA;--ink:#1A1814;--muted:#625C51;--faint:#7D7668;--rule:#DDD5C6;--rule-soft:#E8E1D4;--ghost:#E3DCCE;--led:#FF5C2E;--bad:#B83C1C;--ok:#2B7A4B;--warn:#85600E;--cream2:#FFFCFA}' ++'html[data-theme=dark]{color-scheme:dark}:root{--pf-chrome-h:85px}@media(max-width:369px){:root{--pf-chrome-h:125px}}@media(min-width:1024px){:root{--pf-chrome-h:49px}}' ++'html:not(.pf-mounted) body::before{content:"";display:block;box-sizing:border-box;height:var(--pf-chrome-h);border-bottom:1px solid var(--rule,#DDD5C6);background:var(--cream,#F4EFE6)}' +'@media(pointer:coarse){input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file]),select,textarea{font-size:16px!important}}' +'.pf-busy{opacity:.55;cursor:progress}'; +for(k in hm)c+='@media(min-width:'+(k-1)+'px) and (max-width:'+k+'.9px){:root{--pf-chrome-h:'+(hm[k]|0)+'px}}'; +st.textContent=c; (D.head||H).appendChild(st); var PF=W.PF={v:null,state:'connecting',dirty:false}, nf=W.fetch,T=[],cur=null,Q=[],lb=0,run=1,P=[],S=null,subs=[],sq=null,sb=0,wms=1/0,sp=null, rtt=null,fails=0,off=0,alien=0,rs=0,holds=0,guards=0,leaveOk=0,RH={},bc=null, me=Math.random().toString(36).slice(2),touched=0,pd=0,nv=0,nvT,pgT, -adopted=0,healed=0,stale=null,pft=0,ct=0,cn=null,fnav=[],drawn='', +adopted=0,healed=0,stale=null,pft=0,ct=0,fnav=[],drawn='', sr,hl,ver,vb,nav,ch,tg,pg,tw,tq,tt,SKIP={}, // The core's pages and nothing else; features add theirs via featureNav at // NAV_AT. Third field: prefetch order, smallest page first. -NAV=[['/','Console',5],['/patterns','Patterns',6],['/status','Status',2],['/wifi','Wi-Fi',4],['/knobs','Knobs',1],['/update','Update',3]],NAV_AT=2; +NAV=[['/','Console',5],['/patterns','Patterns',6],['/wifi','Wi-Fi',4],['/knobs','Knobs',1],['/update','Update',3],['/status','Status',2]],NAV_AT=2; try{PF.v=new URLSearchParams(L.search).get('v')}catch(e){} var docB=PF.v; H.classList.add('pf-connecting'); -try{cn=JSON.parse(localStorage.getItem('pf-nav'))}catch(e){} if(cn&&Array.isArray(cn.nav)&&(!PF.v||cn.build===PF.v))fnav=cn.nav; function now(){return Date.now()} @@ -147,10 +149,12 @@ else if(sp)sp.set(ms); else sp=PF.poll(stGet,ms,function(){},{first:false}); } function deliver(s){ +var fn=Array.isArray(s.featureNav)?s.featureNav:[],r=!S; S=W.pfStatus=s;sb=0; -var fn=Array.isArray(s.featureNav)?s.featureNav:[]; -if(s.build+JSON.stringify(fn)!==drawn){fnav=fn;try{localStorage.setItem('pf-nav',JSON.stringify({build:s.build,nav:fn}))}catch(x){}drawNav()} +if(s.build+JSON.stringify(fn)!==drawn){fnav=fn;drawNav();r=1} badge(); +if(r&&sr){sr.host.style.minHeight='';if(Object.keys(hm).length>7)hm={};hm[W.innerWidth]=sr.host.offsetHeight} +if(r)try{localStorage.setItem('pf-nav',JSON.stringify({build:s.build,nav:fn,h:hm}))}catch(x){} for(var i=0;i1);nav.classList.toggle('fr',s
                    '; ++'
                    '; function q(c){return sr.querySelector('.'+c)} hl=q('h');ver=q('ver');vb=q('vb');nav=q('n');ch=q('c');tg=q('tg');pg=q('pg');tw=q('tw'); +if(!S)host.style.minHeight='var(--pf-chrome-h)'; D.body.insertBefore(host,D.body.firstChild); H.classList.add('pf-mounted'); -nav.addEventListener('scroll',fades,{passive:true}); -W.addEventListener('resize',fades); -tg.onclick=function(){var l=H.getAttribute('data-theme')!=='light';H.setAttribute('data-theme',l?'light':'dark');try{localStorage.setItem('pf-theme',l?'light':'dark')}catch(x){}tgl()}; +tg.onclick=function(){var t=dk()?'light':'dark';H.setAttribute('data-theme',t);try{localStorage.setItem('pf-theme',t)}catch(x){}tgl()}; ch.onclick=function(){if(stale)L.replace(vurl(stale));else if(PF.state==='offline')fetchStatus()}; tgl();drawNav();badge();chip(); setTimeout(function(){ct=1;chip()},800); @@ -394,7 +394,7 @@ if(u.pathname+u.search===L.pathname+L.search)return; if(guards){if(!confirm('An upload is running \u2014 leave anyway?')){e.preventDefault();return}leaveOk=1} nv=1;run=0; for(i=T.length;i--;)if(!(T[i].k==='prefetch'&&T[i].u===u.href)){T[i].why='nav';T[i].c.abort()} -if(nav)for(i=0;i, + // so the links are found at any depth, not as the nav's children. function markNav(root) { - var nav = root.querySelector('nav'), on = null, i, a; + var nav = root.querySelector('nav'), on = null, i, a, l; if (!nav) return; - for (i = 0; i < nav.children.length; i++) { - a = nav.children[i]; + for (l = nav.querySelectorAll('a'), i = 0; i < l.length; i++) { + a = l[i]; if (a.pathname !== L.pathname) continue; on = a; if (a.className !== 'on') { a.className = 'on'; a.setAttribute('aria-current', 'page'); - var e = nav.getBoundingClientRect(), r = a.getBoundingClientRect(); - nav.scrollLeft += r.left + r.width / 2 - e.left - e.width / 2; } } return on; diff --git a/web/public/guide/console-demo/status.html b/web/public/guide/console-demo/status.html index 5b4b9793..d0f370f6 100644 --- a/web/public/guide/console-demo/status.html +++ b/web/public/guide/console-demo/status.html @@ -1,113 +1,109 @@ - + Patternflow - Status
                    -

                    Status

                    -
                    - - -
                    - - -

                    Device

                    -
                    Firmware
                    -
                    -
                    Build
                    -
                    -
                    Last restart
                    -
                    -
                    Busy
                    -
                    -
                    Panel size
                    -
                    +/* Console base: the same block opens every core page. Light in :root (the chrome's + injected light palette holds these same values: keep the two in step), + dark under html[data-theme=dark]. No web fonts: the hotspot has no internet. */ +:root{color-scheme:light;--cream:#F4EFE6;--panel:#FFFCFA;--ink:#1A1814;--muted:#625C51;--faint:#7D7668;--rule:#DDD5C6;--rule-soft:#E8E1D4;--ghost:#E3DCCE;--led:#FF5C2E;--bad:#B83C1C;--ok:#2B7A4B;--warn:#85600E; +--sans:system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;--mono:ui-monospace,'Cascadia Mono','SF Mono',Menlo,Consolas,'Roboto Mono',monospace} +html[data-theme=dark]{color-scheme:dark;--cream:#0C0B09;--panel:#131110;--ink:#EDE7DB;--muted:#A39B8A;--faint:#7A7364;--rule:#2B2721;--rule-soft:#1F1C18;--ghost:#2A2620;--bad:#FF7A55;--ok:#57B87F;--warn:#D9A03F} +*{box-sizing:border-box}[hidden]{display:none!important} +body{margin:0;background:var(--cream);color:var(--ink);font:15px/1.5 var(--sans);-webkit-font-smoothing:antialiased} +a{color:inherit}:focus-visible{outline:2px solid var(--led);outline-offset:2px} +.wrap{max-width:720px;margin:0 auto;padding:20px 16px 48px} +h2{margin:0 0 6px;font-size:13px;font-weight:600;color:var(--muted)} +.card{background:var(--panel);border:1px solid var(--rule);border-radius:10px;padding:16px} +.btn{display:inline-flex;align-items:center;justify-content:center;gap:6px;min-height:40px;padding:0 16px;border:1px solid var(--rule);border-radius:8px;background:var(--panel);color:var(--ink);font:500 14px var(--sans);text-decoration:none;cursor:pointer;transition:border-color .15s,background .15s,color .15s} +@media(hover:hover){.btn:hover{border-color:var(--ink)}footer a:hover{color:var(--ink)}} +.btn:disabled{opacity:.45;cursor:default;border-color:var(--rule)} +.msg{font:12px var(--mono);color:var(--faint)}.pf-ok{color:var(--ok)}.pf-err{color:var(--bad)} +footer{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-top:40px;border-top:1px solid var(--rule);font:12px var(--mono);color:var(--faint)} +footer a{display:inline-flex;align-items:center;min-height:40px;color:var(--muted);text-decoration:none} +header{display:flex;align-items:baseline;flex-wrap:wrap;gap:4px 12px;margin-bottom:20px} +h1{flex:1;margin:0;font-size:22px;font-weight:600;letter-spacing:-.01em} +.sub{font:12px var(--mono);color:var(--faint)} +section{margin-top:28px} +.note{margin:8px 0 0;font-size:13px;color:var(--muted)} +.btn.dan{color:var(--bad)}@media(hover:hover){.btn.dan:hover{border-color:var(--bad)}} +.acts{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-top:12px} +.kv{margin:0;border-top:1px solid var(--rule)} +.kv>div{display:flex;align-items:baseline;gap:12px;padding:8px 0;border-bottom:1px solid var(--rule-soft);font-size:13px} +.kv dt{flex:1 0 auto;color:var(--muted)}.kv dd{margin:0;min-width:0;font:12.5px var(--mono);text-align:right;overflow-wrap:anywhere} +/* ── Status ── */ +.kv .big{font-size:14px;font-weight:600} +.ok{color:var(--ok)}.warn{color:var(--warn)}.bad{color:var(--bad)} +.bar{display:block;height:3px;margin:5px 0 2px auto;max-width:160px;background:var(--ghost)}.bar i{display:block;height:100%;background:var(--led)} +#cptext{display:block;width:100%;height:180px;margin-top:12px;padding:8px 10px;resize:vertical;color:var(--ink);font:12px/1.45 var(--mono)} +.pf-offline .kv dd{opacity:.5} + +
                    +

                    Status

                    -
                    + +
                    + + +

                    Device

                    +
                    Firmware
                    -
                    +
                    Build
                    -
                    +
                    Last restart
                    -
                    +
                    Busy
                    -
                    +
                    Panel size
                    -
                    -

                    Render

                    -
                    Panel
                    -
                    -
                    Frame rate
                    -
                    -
                    Frame time
                    -
                    -
                    Active pattern
                    -
                    + + +

                    Render

                    +
                    Panel
                    -
                    +
                    Frame rate
                    -
                    +
                    Frame time
                    -
                    +
                    Active pattern
                    -

                    -

                    Patterns

                    -
                    Total
                    -
                    -
                    Built into firmware
                    -
                    -
                    Loaded from storage
                    -
                    -
                    Last load error
                    -
                    +

                    Patterns

                    +
                    Total
                    -
                    +
                    Built into firmware
                    -
                    +
                    Loaded from storage
                    -
                    +
                    Last load error
                    -

                    -

                    Memory

                    -
                    Internal heap free
                    -
                    -
                    Largest free block
                    -
                    -
                    PSRAM free
                    -
                    +

                    Memory

                    +
                    Internal heap free
                    -
                    +
                    Largest free block
                    -
                    +
                    PSRAM free
                    -
                    -

                    Internal heap is the scarce one - the LED panel's DMA buffers -live there. A pattern module loads only if it leaves 24 KB -of it free; run lower and the network is the first thing to go, while the -panel keeps drawing.

                    +

                    Internal heap is the scarce one: the panel’s DMA buffers live there. A module loads only if it +leaves 24 KB free; below that the network goes first, while the panel keeps drawing.

                    -

                    Storage

                    -
                    Pattern storage
                    -
                    -
                    Storage error
                    -
                    -
                    +

                    Storage

                    +
                    Pattern storage
                    -
                    +
                    Storage error
                    -
                    +
                    - -

                    Network

                    -
                    Network
                    -
                    -
                    Signal
                    -
                    -
                    Address
                    -
                    -
                    Names
                    -
                    -
                    Hotspot
                    -
                    -
                    Hotspot mode
                    -
                    -
                    Hotspot clients
                    -
                    -
                    Reached via
                    -
                    +

                    Network

                    +
                    Network
                    -
                    +
                    Signal
                    -
                    +
                    Address
                    -
                    +
                    Names
                    -
                    +
                    Hotspot
                    -
                    +
                    Hotspot mode
                    -
                    +
                    Hotspot clients
                    -
                    +
                    Reached via
                    -
                    - -
                    + +
                    + Patternflow — Firmware update -
                    -
                    -

                    Update

                    …
                    +
                    +

                    Update

                    …
                    +

                    Drop a firmware .bin: the panel flashes itself over the network and restarts on the new build.

                    -

                    Drop a firmware .bin — the device flashes itself over the LAN and reboots on the new build.

                    - - -
                    - Linked build -
                    - + + - Two steps -
                    01

                    Get a .bin: patternflow.work/update sends the latest release straight back to this page. Or bring your own — an arduino-cli export.

                    -
                    02

                    Drop it below. Keep the device powered; it verifies, flashes, and reboots itself.

                    - -
                    This build requires arming first. On the device: hold K2 → NETWORK, then turn K4 → UPDATE.
                    + -
                    - Drop .bin here - Checking the panel… +
                    + Drop a .bin here + Checking the panel…
                    - + does the same; an explicit any-type accept is what reliably brings + up the file manager. The JS checks extension and size instead. --> +
                    - + + +

                    No file? patternflow.work/update sends the + latest release straight back to this page. Your own arduino-cli export works too. Keep the panel + powered: it checks, flashes and restarts by itself.

                    + +
                    -
                    + Patternflow - Wi-Fi
                    -

                    Wi-Fi

                    -
                    +.s1 i+i,.s2 i+i+i,.s3 i+i+i+i{background:var(--ghost)} +.dym,.dym span+span{display:flex;flex-wrap:wrap;align-items:center;gap:8px}.dym{margin-top:8px;padding:6px 6px 6px 12px} +#join{margin-top:12px}#join.ok{border-left-color:var(--ok)}#join.err{border-left-color:var(--bad)} +#join .big{display:inline-block;margin:6px 10px 6px 0;font:18px var(--mono);overflow-wrap:anywhere} +.seg{display:flex;gap:2px;max-width:360px;padding:2px;border:1px solid var(--rule);border-radius:9px;background:var(--cream)} +.seg label{flex:1;position:relative} +.seg input{position:absolute;opacity:0;margin:0} +.seg span{display:flex;align-items:center;justify-content:center;min-height:40px;border-radius:7px;color:var(--muted);font-weight:500;font-size:14px;cursor:pointer;transition:background .15s,color .15s} +.seg :checked+span{background:var(--panel);color:var(--ink);box-shadow:0 0 0 1px var(--rule),inset 0 -2px var(--led)} +.seg :focus-visible+span{outline:2px solid var(--led)} +.pf-offline #st,.pf-offline #list,.pf-offline #hsstate{opacity:.5} +@media(hover:hover){.btn.dan:hover{border-color:var(--bad)}summary:hover,.seg span:hover{color:var(--ink)}.scan button:hover .nm{text-decoration:underline}} + + +
                    +

                    Wi-Fi

                    -
                    - -

                    Add a network

                    -
                    - - Network name + + + + +
                    + - - +
                    -
                    - -
                    - - -
                    -
                    -
                    - - - - +
                    + + +
                    -
                    - -

                    Saved networks are tried in order starting from the boot slot, - wrapping around — so the device joins whichever one it can find, useful if it - moves between home, a studio and a venue. A new network is only stored; leave - switch now unticked and this page keeps working. Ticking it drops the - current connection immediately. On the panel's hotspot a network is tried at once, - and the panel shows the outcome on its screen as well as here: the address to open, - or why it could not join.

                    + +

                    Save stores it and this page keeps working; Switch now drops this connection to join it.

                    +
                    How saved networks are tried +

                    In order from the boot slot, wrapping around, so the panel joins whichever it finds: home, studio, venue.

                    Hotspot

                    -

                    -

                    +

                    -

                    +

                    The panel’s own network, patternflow-????: join it and open + http://192.168.4.1/, the same console anywhere. Phones stay on it though it has no internet. + Auto raises it about 15 s after the panel finds no Wi-Fi and drops it once it joins one.

                    -
                    - - -
                    -
                    - - +

                    Mode

                    +
                    + + +
                    -
                    - + + +
                    + +
                    -
                    -

                    The panel as a network of its own: patternflow-????. - Join it from a phone or a laptop and open http://192.168.4.1/ — the - same console, anywhere. auto raises it about fifteen seconds after the panel - finds no network and drops it once one is joined; a network added above from the - hotspot is tried at once. The phone is told the network works, so it stays on it; - it has no internet through the panel.

                    - -
                    + +