From 0cd6381ae2e4146ad386961ae7803e5224d3ecfe Mon Sep 17 00:00:00 2001 From: n30nex Date: Sun, 23 Aug 2026 11:16:39 -0400 Subject: [PATCH 1/8] feat: add Heltec V3 Ultimate companions --- .github/workflows/neonpocket-build.yml | 73 +- README.md | 17 +- examples/companion_radio/AbstractUITask.h | 22 + examples/companion_radio/MyMesh.cpp | 439 ++- examples/companion_radio/MyMesh.h | 35 +- .../companion_radio/UltimateOtaPublicKey.h | 12 + examples/companion_radio/UltimateService.cpp | 1275 +++++++ examples/companion_radio/UltimateService.h | 393 ++ examples/companion_radio/UltimateWebApi.cpp | 469 +++ examples/companion_radio/UltimateWebApi.h | 66 + examples/companion_radio/main.cpp | 90 + examples/companion_radio/ui-new/UITask.cpp | 110 +- examples/companion_radio/webui/.gitignore | 5 + examples/companion_radio/webui/README.md | 35 + .../companion_radio/webui/Rcc6WebUiAssets.h | 3341 +++++++++++++++++ .../webui/THIRD_PARTY_NOTICES.md | 13 + examples/companion_radio/webui/build.mjs | 58 + .../companion_radio/webui/package-lock.json | 815 ++++ examples/companion_radio/webui/package.json | 16 + examples/companion_radio/webui/src/app.js | 1426 +++++++ examples/companion_radio/webui/src/index.html | 262 ++ examples/companion_radio/webui/src/styles.css | 301 ++ examples/companion_radio/webui/verify.mjs | 50 + scripts/ultimate_build_flags.py | 22 + scripts/verify_ultimate_mono.py | 74 + src/helpers/esp32/SerialWebInterface.cpp | 766 ++++ src/helpers/esp32/SerialWebInterface.h | 135 + tools/package_ultimate_ota.py | 171 + tools/ultimate_ota_public.pem | 3 + variants/heltec_v3/platformio.ini | 47 + 30 files changed, 10501 insertions(+), 40 deletions(-) create mode 100644 examples/companion_radio/UltimateOtaPublicKey.h create mode 100644 examples/companion_radio/UltimateService.cpp create mode 100644 examples/companion_radio/UltimateService.h create mode 100644 examples/companion_radio/UltimateWebApi.cpp create mode 100644 examples/companion_radio/UltimateWebApi.h create mode 100644 examples/companion_radio/webui/.gitignore create mode 100644 examples/companion_radio/webui/README.md create mode 100644 examples/companion_radio/webui/Rcc6WebUiAssets.h create mode 100644 examples/companion_radio/webui/THIRD_PARTY_NOTICES.md create mode 100644 examples/companion_radio/webui/build.mjs create mode 100644 examples/companion_radio/webui/package-lock.json create mode 100644 examples/companion_radio/webui/package.json create mode 100644 examples/companion_radio/webui/src/app.js create mode 100644 examples/companion_radio/webui/src/index.html create mode 100644 examples/companion_radio/webui/src/styles.css create mode 100644 examples/companion_radio/webui/verify.mjs create mode 100644 scripts/ultimate_build_flags.py create mode 100644 scripts/verify_ultimate_mono.py create mode 100644 src/helpers/esp32/SerialWebInterface.cpp create mode 100644 src/helpers/esp32/SerialWebInterface.h create mode 100644 tools/package_ultimate_ota.py create mode 100644 tools/ultimate_ota_public.pem diff --git a/.github/workflows/neonpocket-build.yml b/.github/workflows/neonpocket-build.yml index 6794cf8d..949776ef 100644 --- a/.github/workflows/neonpocket-build.yml +++ b/.github/workflows/neonpocket-build.yml @@ -6,8 +6,46 @@ on: pull_request: workflow_dispatch: +permissions: + contents: read + jobs: + tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + - name: Verify exact checkout + run: test "$(git rev-parse HEAD)" = "${{ github.event.pull_request.head.sha || github.sha }}" + - uses: ./.github/actions/setup-build-environment + - name: Run upstream unit tests + run: pio test -e native -vv + + webui: + runs-on: ubuntu-latest + defaults: + run: + working-directory: examples/companion_radio/webui + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + - name: Verify exact checkout + run: test "$(git rev-parse HEAD)" = "${{ github.event.pull_request.head.sha || github.sha }}" + - uses: actions/setup-node@v4 + with: + node-version: "22" + cache: npm + cache-dependency-path: examples/companion_radio/webui/package-lock.json + - name: Build and verify embedded WebUI + run: | + npm ci + npm test + git diff --exit-code -- Rcc6WebUiAssets.h + firmware: + needs: [tests, webui] runs-on: ubuntu-latest strategy: fail-fast: false @@ -15,32 +53,57 @@ jobs: include: - environment: heltec_v3_neonpocket_companion_ble transport: BLE + ultimate: false - environment: heltec_v3_neonpocket_companion_usb transport: USB + ultimate: false + - environment: heltec_v3_ultimate_companion_ble + transport: Ultimate-BLE + ultimate: true + - environment: heltec_v3_ultimate_companion_web + transport: Ultimate-Web + ultimate: true steps: - uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.sha || github.sha }} + - name: Verify exact checkout + run: test "$(git rev-parse HEAD)" = "${{ github.event.pull_request.head.sha || github.sha }}" - uses: actions/setup-python@v5 with: python-version: "3.12" - name: Install PlatformIO run: pip install platformio - - name: Verify product contract - run: python scripts/verify_neonpocket_mono.py + - name: Verify product contracts + run: | + python scripts/verify_neonpocket_mono.py + python scripts/verify_ultimate_mono.py - name: Build application and recovery images + env: + ULTIMATE_BUILD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} run: pio run -e ${{ matrix.environment }} -t mergebin - - name: Package checksums + - name: Verify and package images shell: bash run: | out=.pio/build/${{ matrix.environment }} + python - "$out/firmware.bin" "$out/firmware-merged.bin" <<'PY' + from pathlib import Path + import sys + app, merged = map(lambda value: Path(value).read_bytes(), sys.argv[1:]) + assert app and app[0] == 0xE9, "not an ESP application image" + assert len(app) < 3 * 1024 * 1024, "application exceeds 3 MiB budget" + assert len(merged) > len(app), "recovery image is incomplete" + PY mkdir -p dist cp "$out/firmware.bin" "dist/NeonPocketMC-Heltec-V3-${{ matrix.transport }}-app.bin" cp "$out/firmware-merged.bin" "dist/NeonPocketMC-Heltec-V3-${{ matrix.transport }}-recovery-preserves-settings.bin" (cd dist && sha256sum *.bin > SHA256SUMS.txt) - uses: actions/upload-artifact@v4 with: - name: NeonPocketMC-Heltec-V3-${{ matrix.transport }}-${{ github.sha }} + name: NeonPocketMC-Heltec-V3-${{ matrix.transport }}-${{ github.event.pull_request.head.sha || github.sha }} path: dist/* if-no-files-found: error + retention-days: 14 required-firmware: name: required-firmware @@ -48,7 +111,7 @@ jobs: needs: firmware runs-on: ubuntu-latest steps: - - name: Require both firmware images + - name: Require every firmware image env: FIRMWARE_RESULT: ${{ needs.firmware.result }} run: test "$FIRMWARE_RESULT" = success diff --git a/README.md b/README.md index a23f6e9c..67c7d4e5 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,10 @@ # NeonPocketMC for Heltec V3 -Experimental MeshCore BLE or native-USB companion firmware for the **Heltec WiFi LoRa 32 V3 with its integrated 128×64 SSD1306 OLED and SX1262**. +Experimental MeshCore BLE, native-USB, and Ultimate Wi-Fi/Web companion firmware for the **Heltec WiFi LoRa 32 V3 with its integrated 128×64 SSD1306 OLED and SX1262**. > [!WARNING] -> **Heltec V3 only. Do not flash this on Heltec V4, RCC6, RC52, Wireless Tracker, or a board with different display/radio hardware.** This repository ships separate BLE and native-USB companion images—no Wi-Fi, repeater, or room-server firmware. +> **Heltec V3 only. Do not flash this on Heltec V4, RCC6, RC52, Wireless Tracker, or a board with different display/radio hardware.** BLE, USB, and Web are separate images. Never flash a build for another board. **Guided install:** [flasher.canadaverse.org](https://flasher.canadaverse.org/) @@ -20,6 +20,19 @@ Experimental MeshCore BLE or native-USB companion firmware for the **Heltec WiFi - 60-second OLED timeout while BLE and LoRa continue running. - Fail-closed storage mounting. Existing identity, contacts, channels, and preferences are not silently formatted after a mount error. +## Ultimate experimental builds + +Two additional targets bring the portable RCC6 Ultimate feature set to the 128×64 OLED: + +- `heltec_v3_ultimate_companion_ble`: standard MeshCore BLE companion compatibility plus persistent history, metrics, recent-node signal tracking, composer state, and diagnostics. +- `heltec_v3_ultimate_companion_web`: setup AP, local 2.4 GHz Wi-Fi mode, authenticated offline WebUI, full TCP companion protocol on port 5000, history/export/settings/location APIs, and signed app-slot OTA support. + +Both use 350 contacts, 40 channels, 256 pending companion frames, a 32-event callback queue, a 64-node recent-heard cache, selectable 0/128/512/2048-message history, seven-day hourly metrics, a 32 KiB runtime allocation gate, and the existing NeonPocket demo-scene startup. The OLED adds compact Network and Ultimate status pages; the full composer, charts, history manager, location transfer, and Wi-Fi onboarding live in the WebUI. + +BLE and Wi-Fi never run together. In Web mode, HTTP is authenticated, but TCP port 5000 intentionally exposes the complete MeshCore companion/admin protocol to the trusted local LAN. Do not use station mode on an untrusted network. + +The RCC6 indexed color framebuffer, 220×128 tile animations, and color-only notification effects are hardware-specific and are not copied to this monochrome OLED build. + ## Button controls - Screen off: the first gesture only wakes the OLED. diff --git a/examples/companion_radio/AbstractUITask.h b/examples/companion_radio/AbstractUITask.h index b25b1442..13ff5190 100644 --- a/examples/companion_radio/AbstractUITask.h +++ b/examples/companion_radio/AbstractUITask.h @@ -22,6 +22,12 @@ enum class UIEventType { ack }; +enum class UIRadioEvent : uint8_t { + Rx, + TxComplete, + TxFailed +}; + class AbstractUITask { protected: mesh::MainBoard* _board; @@ -36,11 +42,27 @@ class AbstractUITask { void setHasConnection(bool connected) { _connected = connected; } bool hasConnection() const { return _connected; } uint16_t getBattMilliVolts() const { return _board->getBattMilliVolts(); } + bool isSerialEnabled() const { return _interfaceManager->isEnabled(); } + void enableSerial() { _interfaceManager->enable(); } + void disableSerial() { _interfaceManager->disable(); } bool isBluetoothEnabled() const { return _interfaceManager->isBluetoothEnabled(); } void enableBluetooth() { _interfaceManager->enableBluetooth(); } void disableBluetooth() { _interfaceManager->disableBluetooth(); } virtual void msgRead(int msgcount) = 0; virtual void newMsg(uint8_t path_len, const char* from_name, const char* text, int msgcount) = 0; + virtual void newMsgWithEvent(uint8_t path_len, const char* from_name, const char* text, + int msgcount, UIEventType type) { + (void) type; + newMsg(path_len, from_name, text, msgcount); + } virtual void notify(UIEventType t = UIEventType::none) = 0; + virtual void onNewContactVisual() { } + virtual void onRadioEvent(UIRadioEvent event, uint8_t payload_type, + int16_t rssi_dbm = 0, int16_t snr_quarter_db = 0) { + (void) event; + (void) payload_type; + (void) rssi_dbm; + (void) snr_quarter_db; + } virtual void loop() = 0; }; diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 62d98597..215cbd89 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -3,6 +3,20 @@ #include // needed for PlatformIO #include +static constexpr uint8_t displayHopCount(uint8_t encoded_path_len) { + return encoded_path_len == 0xFF ? 0xFF : (encoded_path_len & 0x3F); +} + +static_assert(displayHopCount(0x80) == 0, "3-byte zero-hop path must display as zero hops"); +static_assert(displayHopCount(0x81) == 1, "3-byte one-hop path must display as one hop"); +static_assert(displayHopCount(0xC2) == 2, "encoded path must display only its hop count"); +static_assert(displayHopCount(0xFF) == 0xFF, "direct-route sentinel must remain unknown"); + +#ifdef RCC6_WEB_AP +#include +extern SerialWebInterface web_interface; +#endif + #define CMD_APP_START 1 #define CMD_SEND_TXT_MSG 2 #define CMD_SEND_CHANNEL_TXT_MSG 3 @@ -240,20 +254,6 @@ void MyMesh::addToOfflineQueue(const uint8_t frame[], int len) { } } -int MyMesh::getFromOfflineQueue(uint8_t frame[]) { - if (offline_queue_len > 0) { // check offline queue - size_t len = offline_queue[0].len; // take from top of queue - memcpy(frame, offline_queue[0].buf, len); - - offline_queue_len--; - for (int i = 0; i < offline_queue_len; i++) { // delete top item from queue - offline_queue[i] = offline_queue[i + 1]; - } - return len; - } - return 0; // queue is empty -} - float MyMesh::getAirtimeBudgetFactor() const { return _prefs.airtime_factor; } @@ -284,6 +284,24 @@ uint8_t MyMesh::getExtraAckTransmitCount() const { } void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) { +#ifdef NEONPOCKET_ULTIMATE + if (len > 0) { + const uint8_t payload_type = (raw[0] >> PH_TYPE_SHIFT) & PH_TYPE_MASK; + _ultimate_last_rx_millis = millis(); + _ultimate_last_rx_rssi = (int16_t)rssi; + _ultimate_last_rx_snr_q4 = (int8_t)(snr * 4); + _ultimate_last_rx_payload = payload_type; + ultimate_service.enqueueRadio(UltimateRadioEvent::Rx, payload_type, + (int16_t)rssi, (int8_t)(snr * 4), + _radio->getEstAirtimeFor(len)); + } +#endif + if (_ui && len > 0) { + uint8_t payload_type = (raw[0] >> PH_TYPE_SHIFT) & PH_TYPE_MASK; + _ui->onRadioEvent(UIRadioEvent::Rx, payload_type, + (int16_t)rssi, (int16_t)(snr * 4)); + } + if (_serial->isConnected() && len + 3 <= MAX_FRAME_SIZE) { int i = 0; out_frame[i++] = PUSH_CODE_LOG_RX_DATA; @@ -296,6 +314,58 @@ void MyMesh::logRxRaw(float snr, float rssi, const uint8_t raw[], int len) { } } +void MyMesh::logTx(mesh::Packet* packet, int len) { +#ifdef NEONPOCKET_ULTIMATE + ultimate_service.enqueueRadio(UltimateRadioEvent::Tx, packet->getPayloadType(), 0, 0, + _radio->getEstAirtimeFor(len)); + if (_ultimate_delivery_active) { + uint8_t packet_hash[MAX_HASH_SIZE]; + packet->calculatePacketHash(packet_hash); + if (memcmp(packet_hash, _ultimate_delivery_hash, sizeof(packet_hash)) == 0) { + ultimate_service.markDeliveryOnAir(); + _ultimate_delivery_active = false; + } + } +#else + (void) len; +#endif +#ifdef NEONPOCKET_UI + const bool report = packet->getPayloadType() != PAYLOAD_TYPE_ADVERT || packet == _ui_advert_packet; + if (packet == _ui_advert_packet) _ui_advert_packet = nullptr; +#else + const bool report = true; +#endif + if (_ui && report) { + _ui->onRadioEvent(UIRadioEvent::TxComplete, packet->getPayloadType()); + } +} + +void MyMesh::logTxFail(mesh::Packet* packet, int len) { +#ifdef NEONPOCKET_ULTIMATE + ultimate_service.enqueueRadio(UltimateRadioEvent::TxFailed, packet->getPayloadType(), 0, 0, + _radio->getEstAirtimeFor(len)); + if (_ultimate_delivery_active) { + uint8_t packet_hash[MAX_HASH_SIZE]; + packet->calculatePacketHash(packet_hash); + if (memcmp(packet_hash, _ultimate_delivery_hash, sizeof(packet_hash)) == 0) { + ultimate_service.markDeliveryFailed(); + _ultimate_delivery_active = false; + } + } +#else + (void) len; +#endif +#ifdef NEONPOCKET_UI + const bool report = packet->getPayloadType() != PAYLOAD_TYPE_ADVERT || packet == _ui_advert_packet; + if (packet == _ui_advert_packet) _ui_advert_packet = nullptr; +#else + const bool report = true; +#endif + if (_ui && report) { + _ui->onRadioEvent(UIRadioEvent::TxFailed, packet->getPayloadType()); + } +} + bool MyMesh::isAutoAddEnabled() const { return (_prefs.manual_add_contacts & 1) == 0; } @@ -351,6 +421,16 @@ void MyMesh::onContactsFull() { } void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path_len, const uint8_t* path) { +#ifdef NEONPOCKET_ULTIMATE + const bool attributable = _ultimate_last_rx_payload == PAYLOAD_TYPE_ADVERT && + millis() - _ultimate_last_rx_millis <= 250; + ultimate_service.enqueueNode(contact.id.pub_key, contact.name, contact.type, + displayHopCount(path_len), + getRTCClock()->getCurrentTime(), + attributable ? _ultimate_last_rx_rssi : 0, + attributable ? _ultimate_last_rx_snr_q4 : 0, + attributable); +#endif if (_serial->isConnected()) { if (is_new) { writeContactRespFrame(PUSH_CODE_NEW_ADVERT, contact); @@ -359,11 +439,17 @@ void MyMesh::onDiscoveredContact(ContactInfo &contact, bool is_new, uint8_t path memcpy(&out_frame[1], contact.id.pub_key, PUB_KEY_SIZE); _serial->writeFrame(out_frame, 1 + PUB_KEY_SIZE); } - } else { + } + #ifdef DISPLAY_CLASS - if (_ui) _ui->notify(UIEventType::newContactMessage); -#endif + if (is_new && _ui) { + if (_serial->isConnected()) { + _ui->onNewContactVisual(); + } else { + _ui->notify(UIEventType::newContactMessage); + } } +#endif // add inbound-path to mem cache if (path && mesh::Packet::isValidPathLen(path_len)) { // check path is valid @@ -420,6 +506,9 @@ ContactInfo* MyMesh::processAck(const uint8_t *data) { uint32_t trip_time = _ms->getMillis() - expected_ack_table[i].msg_sent; memcpy(&out_frame[5], &trip_time, 4); _serial->writeFrame(out_frame, 9); +#ifdef NEONPOCKET_ULTIMATE + ultimate_service.markDeliveryAcked(expected_ack_table[i].ack, trip_time); +#endif // NOTE: the same ACK can be received multiple times! expected_ack_table[i].ack = 0; // clear expected hash, now that we have received ACK @@ -443,6 +532,7 @@ void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packe memcpy(&out_frame[i], from.id.pub_key, 6); i += 6; // just 6-byte prefix uint8_t path_len = out_frame[i++] = pkt->isRouteFlood() ? pkt->path_len : 0xFF; + const uint8_t hop_count = displayHopCount(path_len); out_frame[i++] = txt_type; memcpy(&out_frame[i], &sender_timestamp, 4); i += 4; @@ -467,8 +557,22 @@ void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packe #ifdef DISPLAY_CLASS // we only want to show text messages on display, not cli data bool should_display = txt_type == TXT_TYPE_PLAIN || txt_type == TXT_TYPE_SIGNED_PLAIN; +#ifdef NEONPOCKET_ULTIMATE + if (should_display) { + const bool attributable = millis() - _ultimate_last_rx_millis <= 250; + ultimate_service.enqueueMessage( + UltimateMessageKind::Direct, true, 0, from.id.pub_key, from.name, text, + sender_timestamp, hop_count, attributable ? _ultimate_last_rx_rssi : 0, + attributable ? _ultimate_last_rx_snr_q4 : (int8_t)(pkt->getSNR() * 4)); + } +#endif if (should_display && _ui) { - _ui->newMsg(path_len, from.name, text, offline_queue_len); +#ifdef NEONPOCKET_RCC6_UI_EXTENSIONS + memcpy(_ui_reply_pubkey_prefix, from.id.pub_key, sizeof(_ui_reply_pubkey_prefix)); + _ui_reply_target = UIReplyTarget::Direct; +#endif + _ui->newMsgWithEvent(hop_count, from.name, text, offline_queue_len, + UIEventType::contactMessage); if (!_serial->isConnected()) { _ui->notify(UIEventType::contactMessage); } @@ -554,9 +658,15 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe out_frame[i++] = RESP_CODE_CHANNEL_MSG_RECV; } +#ifdef NEONPOCKET_RCC6_UI_EXTENSIONS + int found_channel_idx = findChannelIdx(channel); + uint8_t channel_idx = (uint8_t)found_channel_idx; +#else uint8_t channel_idx = findChannelIdx(channel); +#endif out_frame[i++] = channel_idx; uint8_t path_len = out_frame[i++] = pkt->isRouteFlood() ? pkt->path_len : 0xFF; + const uint8_t hop_count = displayHopCount(path_len); out_frame[i++] = TXT_TYPE_PLAIN; memcpy(&out_frame[i], ×tamp, 4); @@ -584,8 +694,24 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe ChannelDetails channel_details; if (getChannel(channel_idx, channel_details)) { channel_name = channel_details.name; +#ifdef NEONPOCKET_RCC6_UI_EXTENSIONS + if (found_channel_idx >= 0) { + _ui_reply_channel = channel_idx; + _ui_reply_target = UIReplyTarget::Channel; + } +#endif + } +#ifdef NEONPOCKET_ULTIMATE + const bool attributable = millis() - _ultimate_last_rx_millis <= 250; + ultimate_service.enqueueMessage( + UltimateMessageKind::Channel, true, channel_idx, nullptr, channel_name, text, + timestamp, hop_count, attributable ? _ultimate_last_rx_rssi : 0, + attributable ? _ultimate_last_rx_snr_q4 : (int8_t)(pkt->getSNR() * 4)); +#endif + if (_ui) { + _ui->newMsgWithEvent(hop_count, channel_name, text, offline_queue_len, + UIEventType::channelMessage); } - if (_ui) _ui->newMsg(path_len, channel_name, text, offline_queue_len); #endif } @@ -1122,13 +1248,20 @@ void MyMesh::handleCmdFrame(size_t len) { memcpy(&out_frame[2], &expected_ack, 4); memcpy(&out_frame[6], &est_timeout, 4); _serial->writeFrame(out_frame, 10); +#ifdef NEONPOCKET_ULTIMATE + if (txt_type == TXT_TYPE_PLAIN) { + ultimate_service.enqueueMessage(UltimateMessageKind::Direct, false, 0, + recipient->id.pub_key, recipient->name, text, msg_timestamp, + recipient->out_path_len, 0, 0); + } +#endif } } else { writeErrFrame(recipient == NULL ? ERR_CODE_NOT_FOUND : ERR_CODE_UNSUPPORTED_CMD); // unknown recipient, or unsupported TXT_TYPE_* } - } else if (cmd_frame[0] == CMD_SEND_CHANNEL_TXT_MSG) { // send GroupChannel text msg + } else if (cmd_frame[0] == CMD_SEND_CHANNEL_TXT_MSG && len >= 7) { // send GroupChannel text msg int i = 1; uint8_t txt_type = cmd_frame[i++]; // should be TXT_TYPE_PLAIN uint8_t channel_idx = cmd_frame[i++]; @@ -1143,6 +1276,10 @@ void MyMesh::handleCmdFrame(size_t len) { ChannelDetails channel; bool success = getChannel(channel_idx, channel); if (success && sendGroupMessage(msg_timestamp, channel.channel, _prefs.node_name, text, len - i)) { +#ifdef NEONPOCKET_ULTIMATE + ultimate_service.enqueueMessage(UltimateMessageKind::Channel, false, channel_idx, + nullptr, channel.name, text, msg_timestamp, OUT_PATH_UNKNOWN, 0, 0); +#endif writeOKFrame(); } else { writeErrFrame(ERR_CODE_NOT_FOUND); // bad channel_idx @@ -1366,12 +1503,17 @@ void MyMesh::handleCmdFrame(size_t len) { writeErrFrame(ERR_CODE_ILLEGAL_ARG); } } else if (cmd_frame[0] == CMD_SYNC_NEXT_MESSAGE) { - int out_len; - if ((out_len = getFromOfflineQueue(out_frame)) > 0) { - _serial->writeFrame(out_frame, out_len); + if (offline_queue_len > 0) { + const size_t out_len = offline_queue[0].len; + if (_serial->writeFrame(offline_queue[0].buf, out_len) == out_len) { + offline_queue_len--; + for (int i = 0; i < offline_queue_len; i++) { + offline_queue[i] = offline_queue[i + 1]; + } #ifdef DISPLAY_CLASS - if (_ui) _ui->msgRead(offline_queue_len); + if (_ui) _ui->msgRead(offline_queue_len); #endif + } } else { out_frame[0] = RESP_CODE_NO_MORE_MESSAGES; _serial->writeFrame(out_frame, 1); @@ -1916,7 +2058,14 @@ void MyMesh::handleCmdFrame(size_t len) { } else { writeErrFrame(ERR_CODE_ILLEGAL_ARG); // invalid stats sub-type } - } else if (cmd_frame[0] == CMD_FACTORY_RESET && memcmp(&cmd_frame[1], "reset", 5) == 0) { + } else if (cmd_frame[0] == CMD_FACTORY_RESET && len >= 6 && + memcmp(&cmd_frame[1], "reset", 5) == 0) { +#ifdef RCC6_WEB_AP + if (!web_interface.clearStoredNetworkConfig()) { + writeErrFrame(ERR_CODE_FILE_IO_ERROR); + return; + } +#endif if (_serial) { MESH_DEBUG_PRINTLN("Factory reset: disabling serial interface to prevent reconnects (BLE/WiFi)"); _serial->disable(); // Phone app disconnects before we can send OK frame so it's safe here @@ -2030,6 +2179,53 @@ void MyMesh::enterCLIRescue() { Serial.println("========= CLI Rescue ========="); } +#ifdef NEONPOCKET_ULTIMATE +static void printUltimateJsonString(const char* text) { + Serial.print('"'); + for (const unsigned char* p = reinterpret_cast(text); p && *p; p++) { + if (*p == '"' || *p == '\\') { Serial.print('\\'); Serial.print(static_cast(*p)); } + else if (*p == '\n') Serial.print("\\n"); + else if (*p == '\r') Serial.print("\\r"); + else if (*p == '\t') Serial.print("\\t"); + else if (*p >= 0x20) Serial.print(static_cast(*p)); + } + Serial.print('"'); +} + +static bool exportUltimateRecord(const UltimateHistoryRecord& record, void*) { + Serial.printf("{\"sequence\":%lu,\"timestamp\":%lu,\"incoming\":%s,\"kind\":%u,\"target\":%u,\"sender\":", + (unsigned long)record.sequence, (unsigned long)record.timestamp, + (record.flags & ULTIMATE_HISTORY_INCOMING) ? "true" : "false", + record.kind, record.target); + printUltimateJsonString(record.sender); + Serial.print(",\"text\":"); + printUltimateJsonString(record.text); + Serial.println('}'); + yield(); + return true; +} + +static void exportUltimateHistoryToSerial() { + ultimate_service.visitHistory(0, 0, true, exportUltimateRecord, nullptr); +} + +static void setUltimateBatterySize(const char* value) { + char* end = nullptr; + const long capacity = strtol(value, &end, 10); + if (!value[0] || *end || (capacity != 0 && (capacity < 50 || capacity > 20000))) { + Serial.println(" Error: battery size must be 50..20000 mAh, or 0 for unknown"); + return; + } + UltimateSettings updated = ultimate_service.getSettings(); + updated.battery_capacity_mah = static_cast(capacity); + if (ultimate_service.updateSettings(updated)) { + Serial.printf(" > battery size is now %u mAh\n", updated.battery_capacity_mah); + } else { + Serial.println(" Error: settings write failed"); + } +} +#endif + void MyMesh::checkCLIRescueCmd() { int len = strlen(cli_command); while (Serial.available() && len < sizeof(cli_command)-1) { @@ -2056,6 +2252,82 @@ void MyMesh::checkCLIRescueCmd() { } else { Serial.printf(" Error: unknown config: %s\n", config); } +#ifdef NEONPOCKET_ULTIMATE + } else if (memcmp(cli_command, "set.batterysize ", 16) == 0) { + setUltimateBatterySize(&cli_command[16]); + } else if (strcmp(cli_command, "help") == 0) { + Serial.println(" np status | np history export | np history clear CONFIRM"); + Serial.println(" np history capacity off|128|512|2048 | np private on|off"); + Serial.println(" np cadence 450|650|900|1200 | np power balanced|field|battery"); + Serial.println(" set.batterysize | np battery size | np battery offset "); + Serial.println(" np phrase <1..8> "); + } else if (strcmp(cli_command, "np status") == 0) { + const UltimateSnapshot& status = ultimate_service.getSnapshot(); + Serial.printf("Ultimate %s history=%u/%u unread=%u rx=%lu tx=%lu fail=%lu heap=%lu largest=%lu gate=%s battery=%umV/%umAh trend=%+dmV/h usb-host=%s profile=%u delivery=%u\n", + NEONPOCKET_ULTIMATE_VERSION, status.history_count, status.history_capacity, + status.unread_count, (unsigned long)status.rx_packets, + (unsigned long)status.tx_packets, (unsigned long)status.tx_failures, + (unsigned long)status.free_heap, (unsigned long)status.largest_allocation, + status.memory_gate_passed ? "pass" : "pending", status.battery_mv, + ultimate_service.getSettings().battery_capacity_mah, + status.battery_trend_mv_per_hour, + status.usb_host_connected ? "connected" : "absent", + ultimate_service.getSettings().power_profile, + static_cast(ultimate_service.getDelivery().state)); + } else if (strcmp(cli_command, "np history export") == 0) { + exportUltimateHistoryToSerial(); + } else if (strcmp(cli_command, "np history clear CONFIRM") == 0) { + Serial.println(ultimate_service.clearHistory() ? " > Ultimate history cleared" + : " Error: history clear failed"); + } else if (memcmp(cli_command, "np history capacity ", 20) == 0) { + const char* value = &cli_command[20]; + const uint16_t capacity = strcmp(value, "off") == 0 ? 0 : atoi(value); + Serial.println(ultimate_service.setHistoryCapacity(capacity) + ? " > history capacity saved" : " Error: capacity must be off/128/512/2048"); + } else if (memcmp(cli_command, "np private ", 11) == 0) { + UltimateSettings updated = ultimate_service.getSettings(); + if (strcmp(&cli_command[11], "on") == 0) updated.private_notifications = true; + else if (strcmp(&cli_command[11], "off") == 0) updated.private_notifications = false; + else { Serial.println(" Error: use np private on|off"); cli_command[0] = 0; return; } + Serial.println(ultimate_service.updateSettings(updated) ? " > privacy saved" + : " Error: settings write failed"); + } else if (memcmp(cli_command, "np cadence ", 11) == 0) { + UltimateSettings updated = ultimate_service.getSettings(); + updated.scan_cadence_ms = atoi(&cli_command[11]); + Serial.println(ultimate_service.updateSettings(updated) + ? " > scan cadence saved" : " Error: cadence must be 450/650/900/1200"); + } else if (memcmp(cli_command, "np power ", 9) == 0) { + UltimateSettings updated = ultimate_service.getSettings(); + const char* profile = &cli_command[9]; + if (strcmp(profile, "balanced") == 0) { + updated.power_profile = static_cast(UltimatePowerProfile::Balanced); + } else if (strcmp(profile, "field") == 0) { + updated.power_profile = static_cast(UltimatePowerProfile::Field); + } else if (strcmp(profile, "battery") == 0) { + updated.power_profile = static_cast(UltimatePowerProfile::Battery); + } else { + Serial.println(" Error: use np power balanced|field|battery"); + cli_command[0] = 0; + return; + } + Serial.println(ultimate_service.updateSettings(updated) + ? " > power profile saved" : " Error: settings write failed"); + } else if (memcmp(cli_command, "np battery offset ", 18) == 0) { + UltimateSettings updated = ultimate_service.getSettings(); + updated.battery_calibration_mv = atoi(&cli_command[18]); + Serial.println(ultimate_service.updateSettings(updated) + ? " > battery calibration saved" + : " Error: offset must be -300..300 mV"); + } else if (memcmp(cli_command, "np battery size ", 16) == 0) { + setUltimateBatterySize(&cli_command[16]); + } else if (memcmp(cli_command, "np phrase ", 10) == 0 && cli_command[10] >= '1' && cli_command[10] <= '8' && + cli_command[11] == ' ') { + UltimateSettings updated = ultimate_service.getSettings(); + const uint8_t index = cli_command[10] - '1'; + StrHelper::strncpy(updated.quick_phrases[index], &cli_command[12], sizeof(updated.quick_phrases[index])); + Serial.println(ultimate_service.updateSettings(updated) ? " > quick phrase saved" + : " Error: phrase write failed"); +#endif } else if (strcmp(cli_command, "rebuild") == 0) { bool success = _store->formatFileSystem(); if (success) { @@ -2247,9 +2519,104 @@ void MyMesh::loop() { #ifdef DISPLAY_CLASS if (_ui) _ui->setHasConnection(_serial->isConnected()); #endif +#ifdef NEONPOCKET_ULTIMATE + ultimate_service.setQueueDepth((uint8_t)_mgr->getOutboundTotal()); +#endif } -bool MyMesh::advert() { +#ifdef NEONPOCKET_RCC6_UI_EXTENSIONS +bool MyMesh::sendQuickReplyToLatest(const char* text) { + if (text == nullptr || text[0] == 0) return false; + + uint32_t timestamp = getRTCClock()->getCurrentTimeUnique(); + if (_ui_reply_target == UIReplyTarget::Direct) { + ContactInfo* recipient = lookupContactByPubKey(_ui_reply_pubkey_prefix, + sizeof(_ui_reply_pubkey_prefix)); + if (recipient == nullptr) return false; + + uint32_t expected_ack = 0; + uint32_t est_timeout = 0; + int result = sendMessage(*recipient, timestamp, 0, text, expected_ack, est_timeout); + if (result == MSG_SEND_FAILED) return false; + + if (expected_ack) { + expected_ack_table[next_ack_idx].msg_sent = _ms->getMillis(); + expected_ack_table[next_ack_idx].ack = expected_ack; + expected_ack_table[next_ack_idx].contact = recipient; + next_ack_idx = (next_ack_idx + 1) % EXPECTED_ACK_TABLE_SIZE; + } + return true; + } + + if (_ui_reply_target == UIReplyTarget::Channel) { + ChannelDetails channel; + return getChannel(_ui_reply_channel, channel) && + sendGroupMessage(timestamp, channel.channel, _prefs.node_name, text, strlen(text)); + } + return false; +} +#endif + +#ifdef NEONPOCKET_ULTIMATE +bool MyMesh::sendUltimateDirect(const uint8_t pubkey_prefix[6], const char* text) { + if (text == nullptr || text[0] == 0) return false; + ContactInfo* recipient = lookupContactByPubKey(pubkey_prefix, 6); + if (recipient == nullptr) return false; + const uint32_t timestamp = getRTCClock()->getCurrentTimeUnique(); + uint32_t expected_ack = 0; + uint32_t est_timeout = 0; + const int queued_before = _mgr->getOutboundTotal(); + _ultimate_delivery_active = false; + memset(_ultimate_delivery_hash, 0, sizeof(_ultimate_delivery_hash)); + const int result = sendMessage(*recipient, timestamp, 0, text, expected_ack, + est_timeout, _ultimate_delivery_hash); + if (result == MSG_SEND_FAILED || _mgr->getOutboundTotal() <= queued_before) { + _ultimate_delivery_active = false; + return false; + } + _ultimate_delivery_active = true; + ultimate_service.startDelivery(static_cast(UltimateMessageKind::Direct), + recipient->name, text, expected_ack, est_timeout); + if (expected_ack) { + expected_ack_table[next_ack_idx].msg_sent = _ms->getMillis(); + expected_ack_table[next_ack_idx].ack = expected_ack; + expected_ack_table[next_ack_idx].contact = recipient; + next_ack_idx = (next_ack_idx + 1) % EXPECTED_ACK_TABLE_SIZE; + } + ultimate_service.enqueueMessage(UltimateMessageKind::Direct, false, 0, + recipient->id.pub_key, recipient->name, text, timestamp, recipient->out_path_len, 0, 0); + return true; +} + +bool MyMesh::sendUltimateChannel(uint8_t channel_index, const char* text) { + if (text == nullptr || text[0] == 0) return false; + ChannelDetails channel; + if (!getChannel(channel_index, channel)) return false; + const uint32_t timestamp = getRTCClock()->getCurrentTimeUnique(); + const int queued_before = _mgr->getOutboundTotal(); + _ultimate_delivery_active = false; + memset(_ultimate_delivery_hash, 0, sizeof(_ultimate_delivery_hash)); + if (!sendGroupMessage(timestamp, channel.channel, _prefs.node_name, text, + strlen(text), _ultimate_delivery_hash) || + _mgr->getOutboundTotal() <= queued_before) { + _ultimate_delivery_active = false; + return false; + } + _ultimate_delivery_active = true; + char label[32]; + snprintf(label, sizeof(label), "#%s", channel.name); + ultimate_service.startDelivery(static_cast(UltimateMessageKind::Channel), + label, text, 0, 0); + ultimate_service.enqueueMessage(UltimateMessageKind::Channel, false, channel_index, + nullptr, label, text, timestamp, 0xFF, 0, 0); + return true; +} +#endif + +bool MyMesh::advert(bool flood) { +#ifdef NEONPOCKET_UI + if (_ui_advert_packet != nullptr) return false; +#endif mesh::Packet* pkt; if (_prefs.advert_loc_policy == ADVERT_LOC_NONE) { pkt = createSelfAdvert(_prefs.node_name); @@ -2257,7 +2624,25 @@ bool MyMesh::advert() { pkt = createSelfAdvert(_prefs.node_name, sensors.node_lat, sensors.node_lon); } if (pkt) { - sendZeroHop(pkt); + if (flood) { + TransportKey default_scope; + memcpy(&default_scope.key, _prefs.default_scope_key, sizeof(default_scope.key)); + sendFloodScoped(default_scope, pkt, 0); + } else { + sendZeroHop(pkt); + } +#ifdef NEONPOCKET_UI + bool queued = false; + const int outbound_count = _mgr->getOutboundTotal(); + for (int i = 0; i < outbound_count; i++) { + if (_mgr->getOutboundByIdx(i) == pkt) { + queued = true; + break; + } + } + if (!queued) return false; + _ui_advert_packet = pkt; +#endif return true; } else { return false; diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index f73fe0e0..b6a40abe 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -3,6 +3,9 @@ #include #include #include "AbstractUITask.h" +#ifdef NEONPOCKET_ULTIMATE + #include "UltimateService.h" +#endif /*------------ Frame Protocol --------------*/ #define FIRMWARE_VER_CODE 13 @@ -97,7 +100,17 @@ class MyMesh : public BaseChatMesh, public DataStoreHost { void loop(); void handleCmdFrame(size_t len); - bool advert(); + bool advert(bool flood = false); +#ifdef NEONPOCKET_UI + bool isAdvertPending() const { return _ui_advert_packet != nullptr; } +#endif +#ifdef NEONPOCKET_RCC6_UI_EXTENSIONS + bool sendQuickReplyToLatest(const char* text); +#endif +#ifdef NEONPOCKET_ULTIMATE + bool sendUltimateDirect(const uint8_t pubkey_prefix[6], const char* text); + bool sendUltimateChannel(uint8_t channel_index, const char* text); +#endif void enterCLIRescue(); int getRecentlyHeard(AdvertPath dest[], int max_num); @@ -118,6 +131,8 @@ class MyMesh : public BaseChatMesh, public DataStoreHost { void sendFloodScoped(const mesh::GroupChannel& channel, mesh::Packet* pkt, uint32_t delay_millis=0) override; void logRxRaw(float snr, float rssi, const uint8_t raw[], int len) override; + void logTx(mesh::Packet* packet, int len) override; + void logTxFail(mesh::Packet* packet, int len) override; bool isAutoAddEnabled() const override; bool shouldAutoAddContactType(uint8_t type) const override; bool shouldOverwriteWhenFull() const override; @@ -192,7 +207,6 @@ class MyMesh : public BaseChatMesh, public DataStoreHost { void writeContactRespFrame(uint8_t code, const ContactInfo &contact); void updateContactFromFrame(ContactInfo &contact, uint32_t& last_mod, const uint8_t *frame, int len); void addToOfflineQueue(const uint8_t frame[], int len); - int getFromOfflineQueue(uint8_t frame[]); int getBlobByKey(const uint8_t key[], int key_len, uint8_t dest_buf[]) override { return _store->getBlobByKey(key, key_len, dest_buf); } @@ -216,6 +230,23 @@ class MyMesh : public BaseChatMesh, public DataStoreHost { uint32_t pending_req; // pending _BINARY_REQ BaseSerialInterface *_serial; AbstractUITask* _ui; +#ifdef NEONPOCKET_UI + mesh::Packet* _ui_advert_packet = nullptr; +#endif +#ifdef NEONPOCKET_RCC6_UI_EXTENSIONS + enum class UIReplyTarget : uint8_t { None, Direct, Channel }; + UIReplyTarget _ui_reply_target = UIReplyTarget::None; + uint8_t _ui_reply_pubkey_prefix[6] = {}; + uint8_t _ui_reply_channel = 0; +#endif +#ifdef NEONPOCKET_ULTIMATE + uint32_t _ultimate_last_rx_millis = 0; + int16_t _ultimate_last_rx_rssi = 0; + int8_t _ultimate_last_rx_snr_q4 = 0; + uint8_t _ultimate_last_rx_payload = 0; + uint8_t _ultimate_delivery_hash[MAX_HASH_SIZE] = {}; + bool _ultimate_delivery_active = false; +#endif ContactsIterator _iter; uint32_t _iter_filter_since; diff --git a/examples/companion_radio/UltimateOtaPublicKey.h b/examples/companion_radio/UltimateOtaPublicKey.h new file mode 100644 index 00000000..de53f14a --- /dev/null +++ b/examples/companion_radio/UltimateOtaPublicKey.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +// Public half of the NeonPocket Ultimate v2 release signing key. +// The private half is stored only as the ULTIMATE_OTA_SIGNING_KEY_PEM GitHub secret. +static constexpr uint8_t ULTIMATE_OTA_PUBLIC_KEY[32] = { + 0xfc, 0xbf, 0xf9, 0xdf, 0xbf, 0xcf, 0x3e, 0x13, + 0x47, 0xe8, 0x55, 0x26, 0xd6, 0xcc, 0xcf, 0x5f, + 0x92, 0x2d, 0xd7, 0x27, 0x89, 0xec, 0xb5, 0x6b, + 0x1f, 0x2e, 0xb9, 0xfe, 0xeb, 0xfd, 0x32, 0x27, +}; diff --git a/examples/companion_radio/UltimateService.cpp b/examples/companion_radio/UltimateService.cpp new file mode 100644 index 00000000..20efdafa --- /dev/null +++ b/examples/companion_radio/UltimateService.cpp @@ -0,0 +1,1275 @@ +#ifdef NEONPOCKET_ULTIMATE + +#include "UltimateService.h" + +#include "DataStore.h" +#include +#include +#if defined(ESP32) && (defined(HELTEC_RCC6) || defined(HELTEC_LORA_V3) || defined(HELTEC_LORA_V4)) +#include +#endif +#include +#include + +namespace { +constexpr const char* history_path = "/np/history.bin"; +constexpr const char* history_new_path = "/np/history.new"; +constexpr const char* history_old_path = "/np/history.old"; +constexpr const char* meta_a_path = "/np/meta-a.bin"; +constexpr const char* meta_b_path = "/np/meta-b.bin"; +constexpr const char* settings_path = "/np/settings.bin"; +constexpr const char* settings_tmp_path = "/np/settings.tmp"; +constexpr const char* composer_path = "/np/composer.bin"; +constexpr const char* composer_tmp_path = "/np/composer.tmp"; +constexpr const char* metrics_path = "/np/metrics.bin"; + +bool generationAfter(uint32_t lhs, uint32_t rhs) { + return static_cast(lhs - rhs) > 0; +} +} + +UltimateService ultimate_service; + +static constexpr uint8_t displayHopCount(uint8_t encoded_path_len) { + return encoded_path_len == 0xFF ? 0xFF : (encoded_path_len & 0x3F); +} + +static_assert(displayHopCount(0x80) == 0, "3-byte zero-hop path must display as zero hops"); +static_assert(displayHopCount(0x81) == 1, "3-byte one-hop path must display as one hop"); +static_assert(displayHopCount(0xC2) == 2, "encoded path must display only its hop count"); +static_assert(displayHopCount(0xFF) == 0xFF, "direct-route sentinel must remain unknown"); + +const char* UltimateService::getBuildSha() const { + return ULTIMATE_BUILD_SHA; +} + +uint32_t UltimateService::crc32(const void* data, size_t length) { + const uint8_t* bytes = static_cast(data); + uint32_t crc = 0xFFFFFFFFU; + while (length--) { + crc ^= *bytes++; + for (uint8_t bit = 0; bit < 8; bit++) { + crc = (crc >> 1) ^ (0xEDB88320U & (0U - (crc & 1U))); + } + } + return ~crc; +} + +bool UltimateService::validCapacity(uint16_t capacity) { + return capacity == 0 || capacity == 128 || capacity == 512 || capacity == 2048; +} + +void UltimateService::copyText(char* destination, size_t capacity, const char* source) { + if (capacity == 0) return; + if (source == nullptr) source = ""; + strncpy(destination, source, capacity - 1); + destination[capacity - 1] = 0; +} + +void UltimateService::setDefaults() { + memset(&settings, 0, sizeof(settings)); + settings.history_capacity = 512; + settings.scan_cadence_ms = 650; + settings.private_notifications = false; + settings.battery_calibration_mv = 0; + settings.battery_capacity_mah = 0; + settings.power_profile = static_cast(UltimatePowerProfile::Balanced); + static const char* defaults[8] = { + "On my way", "All good", "Need help", "Please repeat", + "Received", "Where are you?", "Yes", "No"}; + for (uint8_t i = 0; i < 8; i++) { + copyText(settings.quick_phrases[i], sizeof(settings.quick_phrases[i]), defaults[i]); + } +} + +bool UltimateService::loadSettings() { + setDefaults(); + if (!filesystem->exists(settings_path)) { + if (filesystem->exists(settings_tmp_path)) { + filesystem->rename(settings_tmp_path, settings_path); + } else { + return saveSettings(); + } + } + + File file = filesystem->open(settings_path, FILE_READ); + if (!file) return false; + const size_t stored_size = file.size(); + SettingsFile stored = {}; + bool valid = false; + if (stored_size == sizeof(stored) && + file.read(reinterpret_cast(&stored), sizeof(stored)) == sizeof(stored)) { + if (stored.magic == settings_magic && stored.version == settings_format_version && + stored.crc32 == crc32(&stored, offsetof(SettingsFile, crc32))) { + valid = true; + } else if (stored.version == format_version) { + SettingsFileV1 previous; + memcpy(&previous, &stored, sizeof(previous)); + if (previous.magic == settings_magic && + previous.crc32 == crc32(&previous, offsetof(SettingsFileV1, crc32))) { + stored.magic = settings_magic; + stored.version = format_version; // saveSettings() upgrades this v1 file below + stored.history_capacity = previous.history_capacity; + stored.scan_cadence_ms = previous.scan_cadence_ms; + stored.private_notifications = previous.private_notifications; + memcpy(stored.quick_phrases, previous.quick_phrases, sizeof(stored.quick_phrases)); + valid = true; + } + } + } + file.close(); + if (!valid || !validCapacity(stored.history_capacity)) { + Serial.println("Ultimate: settings invalid; using safe defaults"); + return saveSettings(); + } + + settings.history_capacity = stored.history_capacity; + settings.scan_cadence_ms = stored.scan_cadence_ms; + if (settings.scan_cadence_ms != 450 && settings.scan_cadence_ms != 650 && + settings.scan_cadence_ms != 900 && settings.scan_cadence_ms != 1200) { + settings.scan_cadence_ms = 650; + } + settings.private_notifications = stored.private_notifications != 0; + settings.battery_calibration_mv = constrain(stored.battery_calibration_mv, -300, 300); + settings.battery_capacity_mah = stored.battery_capacity_mah == 0 || + (stored.battery_capacity_mah >= 50 && stored.battery_capacity_mah <= 20000) + ? stored.battery_capacity_mah : 0; + settings.power_profile = stored.power_profile <= + static_cast(UltimatePowerProfile::Battery) + ? stored.power_profile : static_cast(UltimatePowerProfile::Balanced); + for (uint8_t i = 0; i < 8; i++) { + copyText(settings.quick_phrases[i], sizeof(settings.quick_phrases[i]), + stored.quick_phrases[i]); + } + return stored.version == settings_format_version ? true : saveSettings(); +} + +bool UltimateService::saveSettings() { + SettingsFile stored = {}; + stored.magic = settings_magic; + stored.version = settings_format_version; + stored.history_capacity = settings.history_capacity; + stored.scan_cadence_ms = settings.scan_cadence_ms; + stored.private_notifications = settings.private_notifications ? 1 : 0; + stored.battery_calibration_mv = settings.battery_calibration_mv; + stored.battery_capacity_mah = settings.battery_capacity_mah; + stored.power_profile = settings.power_profile; + for (uint8_t i = 0; i < 8; i++) { + copyText(stored.quick_phrases[i], sizeof(stored.quick_phrases[i]), + settings.quick_phrases[i]); + } + stored.crc32 = crc32(&stored, offsetof(SettingsFile, crc32)); + + if (!removeIfExists(settings_tmp_path)) return false; + File file = filesystem->open(settings_tmp_path, FILE_WRITE); + const bool written = file && + file.write(reinterpret_cast(&stored), sizeof(stored)) == sizeof(stored); + if (file) { + file.flush(); + file.close(); + } + if (!written) { + removeIfExists(settings_tmp_path); + return false; + } + if (!removeIfExists(settings_path)) return false; + return filesystem->rename(settings_tmp_path, settings_path); +} + +bool UltimateService::loadComposer() { + memset(&composer, 0, sizeof(composer)); + if (!filesystem->exists(composer_path)) return saveComposer(); + File file = filesystem->open(composer_path, FILE_READ); + ComposerFile stored = {}; + const bool complete = file && file.size() == sizeof(stored) && + file.read(reinterpret_cast(&stored), sizeof(stored)) == sizeof(stored); + if (file) file.close(); + const bool valid = complete && stored.magic == composer_magic && + stored.version == format_version && + stored.crc32 == crc32(&stored, offsetof(ComposerFile, crc32)) && + stored.state.pinned_kind <= static_cast(UltimateMessageKind::Channel) && + stored.state.draft_kind <= static_cast(UltimateMessageKind::Channel); + if (!valid) { + Serial.println("Ultimate: composer state invalid; starting clean"); + return saveComposer(); + } + composer = stored.state; + composer.pinned_label[sizeof(composer.pinned_label) - 1] = 0; + composer.draft_label[sizeof(composer.draft_label) - 1] = 0; + composer.draft_text[sizeof(composer.draft_text) - 1] = 0; + return true; +} + +bool UltimateService::saveComposer() { + ComposerFile stored = {}; + stored.magic = composer_magic; + stored.version = format_version; + stored.state = composer; + stored.crc32 = crc32(&stored, offsetof(ComposerFile, crc32)); + if (!removeIfExists(composer_tmp_path)) return false; + File file = filesystem->open(composer_tmp_path, FILE_WRITE); + const bool written = file && + file.write(reinterpret_cast(&stored), sizeof(stored)) == sizeof(stored); + if (file) { + file.flush(); + file.close(); + } + if (!written) { + removeIfExists(composer_tmp_path); + return false; + } + if (!removeIfExists(composer_path)) return false; + return filesystem->rename(composer_tmp_path, composer_path); +} + +bool UltimateService::removeIfExists(const char* path) { + return !filesystem->exists(path) || filesystem->remove(path); +} + +bool UltimateService::loadMeta() { + auto readCopy = [this](const char* path, JournalMeta& copy) { + File file = filesystem->open(path, FILE_READ); + const bool complete = file && file.size() == sizeof(copy) && + file.read(reinterpret_cast(©), sizeof(copy)) == sizeof(copy); + if (file) file.close(); + return complete && copy.magic == meta_magic && copy.version == format_version && + validCapacity(copy.capacity) && copy.capacity != 0 && copy.count <= copy.capacity && + copy.head < copy.capacity && + copy.crc32 == crc32(©, offsetof(JournalMeta, crc32)); + }; + + JournalMeta a = {}; + JournalMeta b = {}; + const bool a_valid = readCopy(meta_a_path, a); + const bool b_valid = readCopy(meta_b_path, b); + if (a_valid || b_valid) { + meta = !b_valid || (a_valid && generationAfter(a.generation, b.generation)) ? a : b; + return true; + } + + memset(&meta, 0, sizeof(meta)); + meta.magic = meta_magic; + meta.version = format_version; + meta.capacity = settings.history_capacity == 0 ? 512 : settings.history_capacity; + meta.next_sequence = 1; + return true; +} + +bool UltimateService::writeMeta() { + JournalMeta next = meta; + next.magic = meta_magic; + next.version = format_version; + next.generation = meta.generation + 1; + next.crc32 = crc32(&next, offsetof(JournalMeta, crc32)); + const char* path = (next.generation & 1U) ? meta_a_path : meta_b_path; + File file = filesystem->open(path, FILE_WRITE); + const bool written = file && + file.write(reinterpret_cast(&next), sizeof(next)) == sizeof(next); + if (file) { + file.flush(); + file.close(); + } + if (!written) return false; + meta = next; + return true; +} + +bool UltimateService::recoverJournalSwap() { + if (!removeIfExists(history_new_path)) return false; + if (!filesystem->exists(history_old_path)) return true; + + bool current_matches = false; + if (filesystem->exists(history_path)) { + File current = filesystem->open(history_path, FILE_READ); + current_matches = current && current.size() == + static_cast(meta.capacity) * sizeof(UltimateHistoryRecord); + if (current) current.close(); + } + if (current_matches) { + return removeIfExists(history_old_path); + } + + if (!removeIfExists(history_path)) return false; + return filesystem->rename(history_old_path, history_path); +} + +bool UltimateService::ensureJournalFile(uint16_t capacity) { + const size_t expected = static_cast(capacity) * sizeof(UltimateHistoryRecord); + if (filesystem->exists(history_path)) { + File existing = filesystem->open(history_path, FILE_READ); + const bool valid = existing && existing.size() == expected; + if (existing) existing.close(); + return valid; + } + + File file = filesystem->open(history_path, FILE_WRITE); + if (!file) return false; + UltimateHistoryRecord empty = {}; + bool ok = true; + for (uint16_t slot = 0; slot < capacity && ok; slot++) { + ok = file.write(reinterpret_cast(&empty), sizeof(empty)) == sizeof(empty); + if ((slot & 31U) == 31U) yield(); + } + file.flush(); + file.close(); + return ok; +} + +bool UltimateService::readSlot(uint16_t slot, UltimateHistoryRecord& record) const { + if (slot >= meta.capacity) return false; + File file = filesystem->open(history_path, FILE_READ); + if (!file || !file.seek(static_cast(slot) * sizeof(record))) { + if (file) file.close(); + return false; + } + const bool complete = file.read(reinterpret_cast(&record), sizeof(record)) == sizeof(record); + file.close(); + const bool valid = complete && record.magic == history_magic && + record.crc32 == crc32(&record, offsetof(UltimateHistoryRecord, crc32)); + if (valid) record.path_len = displayHopCount(record.path_len); + return valid; +} + +bool UltimateService::writeSlot(uint16_t slot, const UltimateHistoryRecord& record) { + if (slot >= meta.capacity) return false; + File file = filesystem->open(history_path, "r+"); + if (!file || !file.seek(static_cast(slot) * sizeof(record))) { + if (file) file.close(); + return false; + } + const bool written = file.write(reinterpret_cast(&record), sizeof(record)) == sizeof(record); + file.flush(); + file.close(); + return written; +} + +bool UltimateService::appendRecord(const PendingEvent& event) { + if (settings.history_capacity == 0) return true; + UltimateHistoryRecord record = {}; + record.magic = history_magic; + record.sequence = meta.next_sequence; + record.timestamp = event.timestamp; + record.flags = event.flags; + record.kind = event.subtype; + record.path_len = displayHopCount(event.path_len); + record.snr_quarter_db = event.snr_quarter_db; + record.rssi_dbm = event.rssi_dbm; + record.target = event.target; + memcpy(record.peer_key, event.peer_key, sizeof(record.peer_key)); + copyText(record.sender, sizeof(record.sender), event.sender); + copyText(record.text, sizeof(record.text), event.text); + record.crc32 = crc32(&record, offsetof(UltimateHistoryRecord, crc32)); + + if (!writeSlot(meta.head, record)) return false; + const JournalMeta previous = meta; + meta.head = (meta.head + 1) % meta.capacity; + if (meta.count < meta.capacity) meta.count++; + meta.next_sequence++; + if (!writeMeta()) { + meta = previous; + return false; + } + + snapshot.last_sequence = record.sequence; + snapshot.history_count = meta.count; + if ((record.flags & ULTIMATE_HISTORY_INCOMING) == 0 && + delivery.history_sequence == 0 && delivery.kind == record.kind && + strncmp(delivery.target, record.sender, sizeof(delivery.target) - 1) == 0 && + strncmp(delivery.text, record.text, sizeof(delivery.text) - 1) == 0) { + delivery.history_sequence = record.sequence; + } + if ((record.flags & (ULTIMATE_HISTORY_INCOMING | ULTIMATE_HISTORY_READ)) == + ULTIMATE_HISTORY_INCOMING) { + if (snapshot.unread_count < 0xFFFF) snapshot.unread_count++; + } + updateThread(record, false); + return true; +} + +bool UltimateService::pushEvent(const PendingEvent& event) { + bool accepted = false; + portENTER_CRITICAL(&pending_lock); + if (pending_count < pending_capacity) { + pending[pending_head] = event; + pending_head = (pending_head + 1) % pending_capacity; + pending_count++; + accepted = true; + } else { + snapshot.event_drops++; + } + portEXIT_CRITICAL(&pending_lock); + return accepted; +} + +bool UltimateService::popEvent(PendingEvent& event) { + bool available = false; + portENTER_CRITICAL(&pending_lock); + if (pending_count > 0) { + event = pending[pending_tail]; + pending_tail = (pending_tail + 1) % pending_capacity; + pending_count--; + available = true; + } + portEXIT_CRITICAL(&pending_lock); + return available; +} + +bool UltimateService::enqueueMessage( + UltimateMessageKind kind, bool incoming, uint8_t target, const uint8_t peer_key[6], + const char* sender, const char* text, uint32_t timestamp, uint8_t path_len, + int16_t rssi_dbm, int8_t snr_quarter_db) { + PendingEvent event = {}; + event.type = PendingType::Message; + event.subtype = static_cast(kind); + event.flags = incoming ? ULTIMATE_HISTORY_INCOMING : ULTIMATE_HISTORY_READ; + event.target = target; + event.timestamp = timestamp ? timestamp : (clock ? clock->getCurrentTime() : 0); + event.path_len = displayHopCount(path_len); + event.rssi_dbm = rssi_dbm; + event.snr_quarter_db = snr_quarter_db; + if (peer_key) memcpy(event.peer_key, peer_key, sizeof(event.peer_key)); + if (kind == UltimateMessageKind::Channel && sender && sender[0] != '#') { + event.sender[0] = '#'; + copyText(event.sender + 1, sizeof(event.sender) - 1, sender); + } else { + copyText(event.sender, sizeof(event.sender), sender); + } + copyText(event.text, sizeof(event.text), text); + return pushEvent(event); +} + +bool UltimateService::enqueueRadio( + UltimateRadioEvent radio_event, uint8_t payload_type, int16_t rssi_dbm, + int8_t snr_quarter_db, uint32_t airtime_ms) { + PendingEvent event = {}; + event.type = PendingType::Radio; + event.subtype = static_cast(radio_event); + event.target = payload_type; + event.timestamp = clock ? clock->getCurrentTime() : 0; + event.airtime_ms = airtime_ms; + event.rssi_dbm = rssi_dbm; + event.snr_quarter_db = snr_quarter_db; + return pushEvent(event); +} + +bool UltimateService::enqueueNode( + const uint8_t peer_key[6], const char* name, uint8_t role, uint8_t path_len, + uint32_t timestamp, int16_t rssi_dbm, int8_t snr_quarter_db, + bool signal_attributable) { + PendingEvent event = {}; + event.type = PendingType::Node; + event.flags = signal_attributable ? 1 : 0; + event.role = role; + event.path_len = displayHopCount(path_len); + event.timestamp = timestamp; + event.rssi_dbm = rssi_dbm; + event.snr_quarter_db = snr_quarter_db; + if (peer_key) memcpy(event.peer_key, peer_key, sizeof(event.peer_key)); + copyText(event.sender, sizeof(event.sender), name); + return pushEvent(event); +} + +void UltimateService::updateNetwork(const PendingEvent& event) { + uint8_t use = network_count; + uint32_t oldest = UINT32_MAX; + for (uint8_t i = 0; i < network_count; i++) { + if (memcmp(network[i].key, event.peer_key, sizeof(network[i].key)) == 0) { + use = i; + break; + } + if (network[i].last_seen < oldest) { + oldest = network[i].last_seen; + if (network_count == network_capacity) use = i; + } + } + if (use == network_count && network_count < network_capacity) network_count++; + + UltimateNetworkNode node = use < network_count ? network[use] : UltimateNetworkNode{}; + memcpy(node.key, event.peer_key, sizeof(node.key)); + copyText(node.name, sizeof(node.name), event.sender); + node.last_seen = event.timestamp; + node.packet_count++; + node.role = event.role; + node.path_len = displayHopCount(event.path_len); + node.signal_attributable = event.flags != 0; + node.rssi_dbm = node.signal_attributable ? event.rssi_dbm : 0; + node.snr_quarter_db = node.signal_attributable ? event.snr_quarter_db : 0; + if (use > 0) memmove(&network[1], &network[0], use * sizeof(network[0])); + network[0] = node; +} + +void UltimateService::updateThread(const UltimateHistoryRecord& record, bool newest_first) { + uint8_t found = thread_count; + for (uint8_t i = 0; i < thread_count; i++) { + const bool same = threads[i].kind == record.kind && + (record.kind == static_cast(UltimateMessageKind::Channel) + ? threads[i].target == record.target + : memcmp(threads[i].peer_key, record.peer_key, sizeof(record.peer_key)) == 0); + if (same) { + found = i; + break; + } + } + + if (found == thread_count) { + if (thread_count < thread_capacity) { + thread_count++; + } else if (newest_first) { + return; + } else { + found = thread_capacity - 1; + } + } + + UltimateThreadSummary summary = found < thread_count ? threads[found] : UltimateThreadSummary{}; + if (summary.newest_sequence == 0 || record.sequence >= summary.newest_sequence) { + summary.kind = record.kind; + summary.target = record.target; + memcpy(summary.peer_key, record.peer_key, sizeof(summary.peer_key)); + copyText(summary.label, sizeof(summary.label), record.sender); + summary.newest_sequence = record.sequence; + } + if ((record.flags & (ULTIMATE_HISTORY_INCOMING | ULTIMATE_HISTORY_READ)) == + ULTIMATE_HISTORY_INCOMING && summary.unread < 0xFFFF) { + summary.unread++; + } + + if (!newest_first && found > 0) { + memmove(&threads[1], &threads[0], found * sizeof(threads[0])); + threads[0] = summary; + } else { + threads[found] = summary; + } +} + +void UltimateService::rebuildThreads() { + memset(threads, 0, sizeof(threads)); + thread_count = 0; + snapshot.unread_count = 0; + if (meta.count == 0 || meta.capacity == 0) return; + + File file = filesystem->open(history_path, FILE_READ); + if (!file) return; + for (uint16_t offset = 0; offset < meta.count; offset++) { + const uint16_t slot = (meta.head + meta.capacity - 1 - offset) % meta.capacity; + UltimateHistoryRecord record; + const bool valid = file.seek(static_cast(slot) * sizeof(record)) && + file.read(reinterpret_cast(&record), sizeof(record)) == sizeof(record) && + record.magic == history_magic && + record.crc32 == crc32(&record, offsetof(UltimateHistoryRecord, crc32)); + if (valid) { + updateThread(record, true); + if ((record.flags & (ULTIMATE_HISTORY_INCOMING | ULTIMATE_HISTORY_READ)) == + ULTIMATE_HISTORY_INCOMING && snapshot.unread_count < 0xFFFF) { + snapshot.unread_count++; + } + } + if ((offset & 31U) == 31U) yield(); + } + file.close(); +} + +void UltimateService::updateMetrics(const PendingEvent& event) { + const uint32_t now = event.timestamp ? event.timestamp : (clock ? clock->getCurrentTime() : 0); + const uint32_t hour = now / 3600U; + const uint16_t slot = hour % hourly_capacity; + UltimateMetricBucket& bucket = hourly[slot]; + if (bucket.hour != hour) { + memset(&bucket, 0, sizeof(bucket)); + bucket.hour = hour; + } + if (event.type == PendingType::Radio) { + const UltimateRadioEvent kind = static_cast(event.subtype); + if (kind == UltimateRadioEvent::Rx) { + bucket.rx_packets++; + bucket.rssi_total += event.rssi_dbm; + bucket.snr_quarter_total += event.snr_quarter_db; + if (bucket.signal_samples < 0xFFFF) bucket.signal_samples++; + } else if (kind == UltimateRadioEvent::Tx) { + bucket.tx_packets++; + } else if (kind == UltimateRadioEvent::TxFailed) { + bucket.tx_failures++; + } + bucket.airtime_ms += event.airtime_ms; + } + bucket.battery_mv = snapshot.battery_mv; + bucket.crc32 = crc32(&bucket, offsetof(UltimateMetricBucket, crc32)); +} + +void UltimateService::processEvent(const PendingEvent& event) { + if (event.type == PendingType::Message) { + if (!appendRecord(event)) snapshot.event_drops++; + } else if (event.type == PendingType::Node) { + updateNetwork(event); + } else if (event.type == PendingType::Radio) { + const UltimateRadioEvent kind = static_cast(event.subtype); + if (kind == UltimateRadioEvent::Rx) { + snapshot.rx_packets++; + snapshot.last_rssi_dbm = event.rssi_dbm; + snapshot.last_snr_quarter_db = event.snr_quarter_db; + snapshot.last_signal_valid = true; + last_signal_millis = millis(); + } else if (kind == UltimateRadioEvent::Tx) { + snapshot.tx_packets++; + } else if (kind == UltimateRadioEvent::TxFailed) { + snapshot.tx_failures++; + } + snapshot.airtime_ms += event.airtime_ms; + } + updateMetrics(event); +} + +bool UltimateService::saveMetricBucket(uint16_t slot) { + if (slot >= hourly_capacity) return false; + File file = filesystem->open(metrics_path, "r+"); + if (!file) return false; + UltimateMetricBucket bucket = hourly[slot]; + bucket.crc32 = crc32(&bucket, offsetof(UltimateMetricBucket, crc32)); + const bool ok = file.seek(static_cast(slot) * sizeof(bucket)) && + file.write(reinterpret_cast(&bucket), sizeof(bucket)) == sizeof(bucket); + file.flush(); + file.close(); + return ok; +} + +void UltimateService::loadMetrics() { + memset(hourly, 0, sizeof(hourly)); + File file = filesystem->open(metrics_path, FILE_READ); + if (file && file.size() == sizeof(hourly)) { + file.read(reinterpret_cast(hourly), sizeof(hourly)); + file.close(); + for (uint16_t i = 0; i < hourly_capacity; i++) { + if (hourly[i].crc32 != crc32(&hourly[i], offsetof(UltimateMetricBucket, crc32))) { + memset(&hourly[i], 0, sizeof(hourly[i])); + } + } + return; + } + if (file) file.close(); + file = filesystem->open(metrics_path, FILE_WRITE); + if (file) { + file.write(reinterpret_cast(hourly), sizeof(hourly)); + file.flush(); + file.close(); + } +} + +void UltimateService::sampleHighResolution() { + HighResolutionMetric& sample = high_resolution[high_resolution_head]; + sample.timestamp = clock->getCurrentTime(); + sample.rx_packets = static_cast(snapshot.rx_packets - last_metric_rx); + sample.tx_packets = static_cast(snapshot.tx_packets - last_metric_tx); + sample.tx_failures = static_cast(snapshot.tx_failures - last_metric_fail); + sample.rssi_dbm = snapshot.last_rssi_dbm; + sample.snr_quarter_db = snapshot.last_snr_quarter_db; + sample.queue_depth = snapshot.outbound_queue_depth; + sample.battery_mv = snapshot.battery_mv; + last_metric_rx = snapshot.rx_packets; + last_metric_tx = snapshot.tx_packets; + last_metric_fail = snapshot.tx_failures; + high_resolution_head = (high_resolution_head + 1) % high_resolution_capacity; + if (high_resolution_count < high_resolution_capacity) high_resolution_count++; +} + +void UltimateService::sampleBatteryProjection() { + snapshot.battery_projection_valid = false; + snapshot.battery_trend_mv_per_hour = 0; + snapshot.battery_runtime_minutes = 0; + if (snapshot.usb_host_connected || high_resolution_count < 10 || snapshot.battery_mv == 0) return; + + const uint8_t newest_index = + (high_resolution_head + high_resolution_capacity - 1) % high_resolution_capacity; + const uint8_t oldest_index = + (high_resolution_head + high_resolution_capacity - high_resolution_count) % + high_resolution_capacity; + const HighResolutionMetric& newest = high_resolution[newest_index]; + const HighResolutionMetric& oldest = high_resolution[oldest_index]; + if (newest.timestamp <= oldest.timestamp) return; + const uint32_t elapsed = newest.timestamp - oldest.timestamp; + if (elapsed < 600U) return; + + int32_t trend = (static_cast(newest.battery_mv) - oldest.battery_mv) * + 3600L / static_cast(elapsed); + trend = constrain(trend, -32768, 32767); + snapshot.battery_trend_mv_per_hour = static_cast(trend); + snapshot.battery_projection_valid = true; + if (trend <= -3 && snapshot.battery_mv > 3450) { + const uint32_t minutes = + static_cast(snapshot.battery_mv - 3450) * 60U / + static_cast(-trend); + snapshot.battery_runtime_minutes = + static_cast(minutes > 65535UL ? 65535UL : minutes); + } +} + +void UltimateService::sampleUsbHostState() { +#if defined(ESP32) && (defined(HELTEC_RCC6) || defined(HELTEC_LORA_V3) || defined(HELTEC_LORA_V4)) + const bool connected = usb_serial_jtag_is_connected(); + if (connected) { + usb_host_seen = true; + snapshot.battery_projection_valid = false; + snapshot.battery_trend_mv_per_hour = 0; + snapshot.battery_runtime_minutes = 0; + } + if (!connected && usb_host_connected && usb_host_seen) { + high_resolution_head = 0; + high_resolution_count = 0; + last_metric_rx = snapshot.rx_packets; + last_metric_tx = snapshot.tx_packets; + last_metric_fail = snapshot.tx_failures; + snapshot.battery_projection_valid = false; + snapshot.battery_trend_mv_per_hour = 0; + snapshot.battery_runtime_minutes = 0; + Serial.println("Ultimate: USB host disconnected; battery learning started"); + } + usb_host_connected = connected; + snapshot.usb_host_connected = connected; +#else + snapshot.usb_host_connected = false; +#endif +} + +void UltimateService::sampleStatus() { + snapshot.uptime_seconds = millis() / 1000U; + snapshot.free_heap = ESP.getFreeHeap(); + snapshot.largest_allocation = heap_caps_get_largest_free_block(MALLOC_CAP_8BIT); + snapshot.storage_used_kb = store->getStorageUsedKb(); + snapshot.storage_total_kb = store->getStorageTotalKb(); + const uint16_t raw_battery = board->getBattMilliVolts(); + if (raw_battery) { + const int32_t calibrated = static_cast(raw_battery) + + settings.battery_calibration_mv; + snapshot.battery_mv = calibrated > 0 && calibrated <= 4500 + ? static_cast(calibrated) : 0; + } else { + snapshot.battery_mv = 0; + } + snapshot.history_count = meta.count; + snapshot.history_capacity = settings.history_capacity; + + if (snapshot.last_signal_valid && millis() - last_signal_millis > 120000U) { + snapshot.last_signal_valid = false; + snapshot.last_rssi_dbm = 0; + snapshot.last_snr_quarter_db = 0; + } + + const uint32_t now = clock->getCurrentTime(); + for (uint8_t i = 0; i < network_count; i++) { + if (network[i].signal_attributable && now - network[i].last_seen > 120) { + network[i].signal_attributable = false; + network[i].rssi_dbm = 0; + network[i].snr_quarter_db = 0; + } + } + sampleHighResolution(); + sampleBatteryProjection(); + snapshot.animation_frame_millis = getRecommendedFrameMillis(); + snapshot.display_timeout_millis = getDisplayTimeoutMillis(); + const uint32_t hour = now / 3600U; + if (last_hour != 0 && hour != last_hour) { + saveMetricBucket(last_hour % hourly_capacity); + } + last_hour = hour; +} + +bool UltimateService::begin( + fs::FS& fs, mesh::RTCClock& rtc, mesh::MainBoard& main_board, DataStore& data_store) { + filesystem = &fs; + clock = &rtc; + board = &main_board; + store = &data_store; + if (!filesystem->exists("/np") && !filesystem->mkdir("/np")) { + Serial.println("Ultimate: cannot create /np storage namespace"); + return false; + } + if (!loadSettings() || !loadComposer() || !loadMeta() || !recoverJournalSwap() || + !ensureJournalFile(meta.capacity)) { + Serial.println("Ultimate: journal initialization failed"); + return false; + } + if (meta.generation == 0 && !writeMeta()) return false; + if (settings.history_capacity != 0 && settings.history_capacity != meta.capacity && + !setHistoryCapacity(settings.history_capacity)) { + return false; + } + + snapshot.history_count = meta.count; + snapshot.history_capacity = settings.history_capacity; + snapshot.last_sequence = meta.next_sequence ? meta.next_sequence - 1 : 0; + rebuildThreads(); + loadMetrics(); + last_hour = clock->getCurrentTime() / 3600U; + sampleStatus(); + next_status_sample = millis() + 60000; + Serial.print("Ultimate: history ready, capacity="); + Serial.print(settings.history_capacity); + Serial.print(" records="); + Serial.println(meta.count); + Serial.print("Ultimate: build="); + Serial.println(getBuildSha()); + return true; +} + +void UltimateService::loop() { + PendingEvent event; + uint8_t budget = 1; // keep journal writes inside one 66 ms UI frame budget + while (budget-- && popEvent(event)) processEvent(event); + const uint32_t now = millis(); + refreshDeliveryState(now); + if (static_cast(now - next_usb_host_sample) >= 0) { + next_usb_host_sample = now + 1000; + sampleUsbHostState(); + } + if (static_cast(now - next_status_sample) >= 0) { + next_status_sample = now + 60000; + sampleStatus(); + } +} + +void UltimateService::refreshStatusNow() { + sampleStatus(); + next_status_sample = millis() + 60000; +} + +const UltimateNetworkNode* UltimateService::getNetworkNode(uint8_t index) const { + return index < network_count ? &network[index] : nullptr; +} + +const UltimateThreadSummary* UltimateService::getThread(uint8_t index) const { + return index < thread_count ? &threads[index] : nullptr; +} + +const UltimateMetricBucket* UltimateService::getHourlyMetricNewest(uint16_t offset) const { + if (offset >= hourly_capacity || clock == nullptr) return nullptr; + const uint32_t hour = clock->getCurrentTime() / 3600U; + const UltimateMetricBucket& bucket = hourly[(hour + hourly_capacity - offset) % hourly_capacity]; + return bucket.hour == hour - offset ? &bucket : nullptr; +} + +bool UltimateService::getHighResolutionNewest( + uint8_t offset, uint32_t& timestamp, uint16_t& rx, uint16_t& tx, + uint16_t& failures, int16_t& rssi, int8_t& snr_quarter, + uint8_t& queue_depth, uint16_t& battery_mv) const { + if (offset >= high_resolution_count) return false; + const uint8_t index = (high_resolution_head + high_resolution_capacity - 1 - offset) % + high_resolution_capacity; + const HighResolutionMetric& sample = high_resolution[index]; + timestamp = sample.timestamp; + rx = sample.rx_packets; + tx = sample.tx_packets; + failures = sample.tx_failures; + rssi = sample.rssi_dbm; + snr_quarter = sample.snr_quarter_db; + queue_depth = sample.queue_depth; + battery_mv = sample.battery_mv; + return true; +} + +bool UltimateService::getHistoryNewest(uint16_t offset, UltimateHistoryRecord& record) const { + if (offset >= meta.count || meta.capacity == 0) return false; + const uint16_t slot = (meta.head + meta.capacity - 1 - offset) % meta.capacity; + return readSlot(slot, record); +} + +bool UltimateService::getNewestUnread(UltimateHistoryRecord& record) const { + struct Search { + UltimateHistoryRecord* output; + bool found; + } search = {&record, false}; + visitHistory(0, 0, false, + [](const UltimateHistoryRecord& candidate, void* context) { + Search* result = static_cast(context); + if ((candidate.flags & (ULTIMATE_HISTORY_INCOMING | ULTIMATE_HISTORY_READ)) == + ULTIMATE_HISTORY_INCOMING) { + *result->output = candidate; + result->found = true; + return false; + } + return true; + }, + &search); + return search.found; +} + +uint16_t UltimateService::visitHistory(uint32_t before_sequence, uint16_t limit, + bool oldest_first, UltimateHistoryVisitor visitor, void* context) const { + if (meta.count == 0 || meta.capacity == 0 || filesystem == nullptr || visitor == nullptr) { + return 0; + } + File file = filesystem->open(history_path, FILE_READ); + if (!file) return 0; + uint16_t visited = 0; + for (uint16_t step = 0; step < meta.count && (limit == 0 || visited < limit); step++) { + const uint16_t offset = oldest_first ? meta.count - 1 - step : step; + const uint16_t slot = (meta.head + meta.capacity - 1 - offset) % meta.capacity; + UltimateHistoryRecord record; + const bool valid = file.seek(static_cast(slot) * sizeof(record)) && + file.read(reinterpret_cast(&record), sizeof(record)) == sizeof(record) && + record.magic == history_magic && + record.crc32 == crc32(&record, offsetof(UltimateHistoryRecord, crc32)); + if (valid && (before_sequence == 0 || record.sequence < before_sequence)) { + record.path_len = displayHopCount(record.path_len); + visited++; + if (!visitor(record, context)) break; + } + if ((step & 31U) == 31U) yield(); + } + file.close(); + return visited; +} + +bool UltimateService::getThreadMessage(uint8_t kind, uint8_t target, + const uint8_t* peer_key, uint16_t ordinal, UltimateHistoryRecord& record) const { + if (meta.count == 0 || meta.capacity == 0 || filesystem == nullptr) return false; + File file = filesystem->open(history_path, "r"); + if (!file) return false; + uint16_t matched = 0; + bool found = false; + for (uint16_t offset = 0; offset < meta.count; offset++) { + const uint16_t slot = (meta.head + meta.capacity - 1 - offset) % meta.capacity; + UltimateHistoryRecord candidate; + if (!file.seek(static_cast(slot) * sizeof(candidate)) || + file.read(reinterpret_cast(&candidate), sizeof(candidate)) != sizeof(candidate) || + candidate.magic != history_magic || + candidate.crc32 != crc32(&candidate, offsetof(UltimateHistoryRecord, crc32)) || + candidate.kind != kind) { + continue; + } + const bool same = kind == static_cast(UltimateMessageKind::Channel) + ? candidate.target == target + : peer_key != nullptr && memcmp(candidate.peer_key, peer_key, sizeof(candidate.peer_key)) == 0; + if (same && matched++ == ordinal) { + candidate.path_len = displayHopCount(candidate.path_len); + record = candidate; + found = true; + break; + } + if ((offset & 31U) == 31U) yield(); + } + file.close(); + return found; +} + +bool UltimateService::markRead(uint32_t sequence) { + File file = filesystem->open(history_path, "r+"); + if (!file) return false; + UltimateHistoryRecord record; + for (uint16_t offset = 0; offset < meta.count; offset++) { + const uint16_t slot = (meta.head + meta.capacity - 1 - offset) % meta.capacity; + const size_t position = static_cast(slot) * sizeof(record); + const bool valid = file.seek(position) && + file.read(reinterpret_cast(&record), sizeof(record)) == sizeof(record) && + record.magic == history_magic && + record.crc32 == crc32(&record, offsetof(UltimateHistoryRecord, crc32)); + if (!valid || record.sequence != sequence) { + if ((offset & 31U) == 31U) yield(); + continue; + } + if ((record.flags & ULTIMATE_HISTORY_READ) != 0) { + file.close(); + return true; + } + record.flags |= ULTIMATE_HISTORY_READ; + record.path_len = displayHopCount(record.path_len); + record.crc32 = crc32(&record, offsetof(UltimateHistoryRecord, crc32)); + const bool written = file.seek(position) && + file.write(reinterpret_cast(&record), sizeof(record)) == sizeof(record); + file.flush(); + file.close(); + if (!written) return false; + if (snapshot.unread_count) snapshot.unread_count--; + for (uint8_t i = 0; i < thread_count; i++) { + const bool same = threads[i].kind == record.kind && + (record.kind == static_cast(UltimateMessageKind::Channel) + ? threads[i].target == record.target + : memcmp(threads[i].peer_key, record.peer_key, sizeof(record.peer_key)) == 0); + if (same && threads[i].unread) { + threads[i].unread--; + break; + } + } + return true; + } + file.close(); + return false; +} + +bool UltimateService::markAllRead() { + if (snapshot.unread_count == 0) return true; + File file = filesystem->open(history_path, "r+"); + if (!file) return false; + bool success = true; + UltimateHistoryRecord record; + for (uint16_t offset = 0; offset < meta.count; offset++) { + const uint16_t slot = (meta.head + meta.capacity - 1 - offset) % meta.capacity; + const size_t position = static_cast(slot) * sizeof(record); + const bool valid = file.seek(position) && + file.read(reinterpret_cast(&record), sizeof(record)) == sizeof(record) && + record.magic == history_magic && + record.crc32 == crc32(&record, offsetof(UltimateHistoryRecord, crc32)); + if (!valid) { + success = false; + } else if ((record.flags & (ULTIMATE_HISTORY_INCOMING | ULTIMATE_HISTORY_READ)) == + ULTIMATE_HISTORY_INCOMING) { + record.flags |= ULTIMATE_HISTORY_READ; + record.crc32 = crc32(&record, offsetof(UltimateHistoryRecord, crc32)); + success = file.seek(position) && + file.write(reinterpret_cast(&record), sizeof(record)) == sizeof(record) && + success; + } + if ((offset & 31U) == 31U) yield(); + } + file.flush(); + file.close(); + rebuildThreads(); + return success; +} + +bool UltimateService::clearHistory() { + if (!removeIfExists(history_path)) return false; + if (!ensureJournalFile(meta.capacity)) return false; + meta.count = 0; + meta.head = 0; + if (!writeMeta()) return false; + snapshot.history_count = 0; + snapshot.unread_count = 0; + memset(threads, 0, sizeof(threads)); + thread_count = 0; + return true; +} + +bool UltimateService::setHistoryCapacity(uint16_t capacity) { + if (!validCapacity(capacity)) return false; + if (capacity == 0) { + settings.history_capacity = 0; + snapshot.history_capacity = 0; + return saveSettings(); + } + if (capacity == meta.capacity) { + settings.history_capacity = capacity; + snapshot.history_capacity = capacity; + return saveSettings(); + } + + const uint16_t retain = meta.count < capacity ? meta.count : capacity; + if (!removeIfExists(history_new_path)) return false; + File next = filesystem->open(history_new_path, FILE_WRITE); + if (!next) return false; + File current = filesystem->open(history_path, FILE_READ); + if (!current) { + next.close(); + removeIfExists(history_new_path); + return false; + } + UltimateHistoryRecord empty = {}; + bool ok = true; + for (uint16_t slot = 0; slot < capacity && ok; slot++) { + ok = next.write(reinterpret_cast(&empty), sizeof(empty)) == sizeof(empty); + } + UltimateHistoryRecord record; + for (uint16_t i = 0; i < retain && ok; i++) { + const uint16_t offset = retain - 1 - i; + const uint16_t slot = (meta.head + meta.capacity - 1 - offset) % meta.capacity; + const bool valid = current.seek(static_cast(slot) * sizeof(record)) && + current.read(reinterpret_cast(&record), sizeof(record)) == sizeof(record) && + record.magic == history_magic && + record.crc32 == crc32(&record, offsetof(UltimateHistoryRecord, crc32)); + if (!valid || + !next.seek(static_cast(i) * sizeof(record))) { + ok = false; + break; + } + ok = next.write(reinterpret_cast(&record), sizeof(record)) == sizeof(record); + if ((i & 31U) == 31U) yield(); + } + current.close(); + next.flush(); + next.close(); + if (!ok) { + removeIfExists(history_new_path); + return false; + } + + if (!removeIfExists(history_old_path)) { + removeIfExists(history_new_path); + return false; + } + if (!filesystem->rename(history_path, history_old_path) || + !filesystem->rename(history_new_path, history_path)) { + removeIfExists(history_path); + filesystem->rename(history_old_path, history_path); + removeIfExists(history_new_path); + return false; + } + + const JournalMeta old_meta = meta; + meta.capacity = capacity; + meta.count = retain; + meta.head = retain % capacity; + if (!writeMeta()) { + meta = old_meta; + removeIfExists(history_path); + filesystem->rename(history_old_path, history_path); + return false; + } + removeIfExists(history_old_path); + settings.history_capacity = capacity; + snapshot.history_capacity = capacity; + snapshot.history_count = retain; + rebuildThreads(); + return saveSettings(); +} + +bool UltimateService::updateSettings(const UltimateSettings& updated) { + if (!validCapacity(updated.history_capacity) || + (updated.scan_cadence_ms != 450 && updated.scan_cadence_ms != 650 && + updated.scan_cadence_ms != 900 && updated.scan_cadence_ms != 1200) || + updated.battery_calibration_mv < -300 || updated.battery_calibration_mv > 300 || + (updated.battery_capacity_mah != 0 && + (updated.battery_capacity_mah < 50 || updated.battery_capacity_mah > 20000)) || + updated.power_profile > static_cast(UltimatePowerProfile::Battery)) { + return false; + } + UltimateSettings safe = updated; + for (uint8_t i = 0; i < 8; i++) { + safe.quick_phrases[i][sizeof(safe.quick_phrases[i]) - 1] = 0; + } + if (safe.history_capacity != settings.history_capacity && + !setHistoryCapacity(safe.history_capacity)) { + return false; + } + settings = safe; + snapshot.history_capacity = settings.history_capacity; + snapshot.animation_frame_millis = getRecommendedFrameMillis(); + snapshot.display_timeout_millis = getDisplayTimeoutMillis(); + return saveSettings(); +} + +bool UltimateService::setPinnedTarget(uint8_t kind, uint8_t target, + const uint8_t* peer_key, const char* label) { + if (kind < static_cast(UltimateMessageKind::Direct) || + kind > static_cast(UltimateMessageKind::Channel) || + (kind == static_cast(UltimateMessageKind::Direct) && peer_key == nullptr)) { + return false; + } + composer.pinned_kind = kind; + composer.pinned_target = target; + memset(composer.pinned_key, 0, sizeof(composer.pinned_key)); + if (peer_key) memcpy(composer.pinned_key, peer_key, sizeof(composer.pinned_key)); + copyText(composer.pinned_label, sizeof(composer.pinned_label), label); + return saveComposer(); +} + +bool UltimateService::clearPinnedTarget() { + composer.pinned_kind = 0; + composer.pinned_target = 0; + memset(composer.pinned_key, 0, sizeof(composer.pinned_key)); + composer.pinned_label[0] = 0; + return saveComposer(); +} + +bool UltimateService::saveDraft(uint8_t kind, uint8_t target, + const uint8_t* peer_key, const char* label, const char* text) { + if (text == nullptr || text[0] == 0) return clearDraft(); + if (kind < static_cast(UltimateMessageKind::Direct) || + kind > static_cast(UltimateMessageKind::Channel) || + (kind == static_cast(UltimateMessageKind::Direct) && peer_key == nullptr)) { + return false; + } + composer.draft_kind = kind; + composer.draft_target = target; + memset(composer.draft_key, 0, sizeof(composer.draft_key)); + if (peer_key) memcpy(composer.draft_key, peer_key, sizeof(composer.draft_key)); + copyText(composer.draft_label, sizeof(composer.draft_label), label); + copyText(composer.draft_text, sizeof(composer.draft_text), text); + return saveComposer(); +} + +bool UltimateService::clearDraft() { + composer.draft_kind = 0; + composer.draft_target = 0; + memset(composer.draft_key, 0, sizeof(composer.draft_key)); + composer.draft_label[0] = 0; + composer.draft_text[0] = 0; + return saveComposer(); +} + +void UltimateService::startDelivery(uint8_t kind, const char* target, + const char* text, uint32_t expected_ack, uint32_t timeout_millis) { + delivery = {}; + delivery.state = UltimateDeliveryState::Queued; + delivery.kind = kind; + delivery.ack_expected = expected_ack != 0; + delivery.expected_ack = expected_ack; + delivery.started_millis = delivery.changed_millis = millis(); + const uint32_t ack_timeout = timeout_millis < 5000UL ? 5000UL : timeout_millis; + delivery.deadline_millis = delivery.started_millis + + (delivery.ack_expected ? ack_timeout + 2000UL : 30000UL); + copyText(delivery.target, sizeof(delivery.target), target); + copyText(delivery.text, sizeof(delivery.text), text); +} + +void UltimateService::markDeliveryOnAir() { + if (delivery.state != UltimateDeliveryState::Queued) return; + delivery.state = delivery.ack_expected + ? UltimateDeliveryState::OnAir : UltimateDeliveryState::Transmitted; + delivery.changed_millis = millis(); +} + +void UltimateService::markDeliveryFailed() { + if (delivery.state == UltimateDeliveryState::Idle || + delivery.state == UltimateDeliveryState::Acked) return; + delivery.state = UltimateDeliveryState::Failed; + delivery.changed_millis = millis(); +} + +void UltimateService::markDeliveryAcked(uint32_t expected_ack, + uint32_t round_trip_millis) { + if (!delivery.ack_expected || expected_ack == 0 || + delivery.expected_ack != expected_ack) return; + delivery.state = UltimateDeliveryState::Acked; + delivery.round_trip_millis = round_trip_millis; + delivery.changed_millis = millis(); +} + +void UltimateService::refreshDeliveryState(uint32_t now) { + if (delivery.deadline_millis == 0 || + (delivery.state != UltimateDeliveryState::Queued && + delivery.state != UltimateDeliveryState::OnAir)) return; + if (static_cast(now - delivery.deadline_millis) >= 0) { + delivery.state = delivery.ack_expected + ? UltimateDeliveryState::NoAck : UltimateDeliveryState::Unconfirmed; + delivery.changed_millis = now; + } +} + +uint16_t UltimateService::getRecommendedFrameMillis() const { + uint16_t interval = settings.power_profile == + static_cast(UltimatePowerProfile::Battery) ? 125 : 66; + const uint32_t flush = snapshot.display_flush_ema_micros; + if (snapshot.outbound_queue_depth >= 12 || flush >= 90000 || + (snapshot.largest_allocation && snapshot.largest_allocation < 32768)) { + if (interval < 150) interval = 150; + } else if (snapshot.outbound_queue_depth >= 6 || flush >= 45000) { + if (interval < 100) interval = 100; + } + return interval; +} + +uint32_t UltimateService::getDisplayTimeoutMillis() const { + switch (static_cast(settings.power_profile)) { + case UltimatePowerProfile::Field: return 300000U; + case UltimatePowerProfile::Battery: return 30000U; + default: return 60000U; + } +} + +void UltimateService::setDisplayTransfer(uint32_t micros, uint16_t tiles) { + snapshot.display_flush_micros = micros; + snapshot.display_tiles_sent = tiles; + snapshot.display_flush_ema_micros = snapshot.display_flush_ema_micros + ? (snapshot.display_flush_ema_micros * 3U + micros) / 4U : micros; + snapshot.animation_frame_millis = getRecommendedFrameMillis(); +} + +#endif diff --git a/examples/companion_radio/UltimateService.h b/examples/companion_radio/UltimateService.h new file mode 100644 index 00000000..f2b7770c --- /dev/null +++ b/examples/companion_radio/UltimateService.h @@ -0,0 +1,393 @@ +#pragma once + +#ifdef NEONPOCKET_ULTIMATE + +#include +#include +#include + +class DataStore; + +enum class UltimateMessageKind : uint8_t { + Direct = 1, + Channel = 2, +}; + +enum class UltimateRadioEvent : uint8_t { + Rx = 1, + Tx = 2, + TxFailed = 3, +}; + +enum class UltimateDeliveryState : uint8_t { + Idle = 0, + Queued, + OnAir, + Transmitted, + Acked, + NoAck, + Unconfirmed, + Failed, +}; + +enum class UltimatePowerProfile : uint8_t { + Balanced = 0, + Field = 1, + Battery = 2, +}; + +enum UltimateHistoryFlags : uint8_t { + ULTIMATE_HISTORY_INCOMING = 0x01, + ULTIMATE_HISTORY_READ = 0x02, +}; + +#pragma pack(push, 1) +struct UltimateHistoryRecord { + uint32_t magic; + uint32_t sequence; + uint32_t timestamp; + uint8_t flags; + uint8_t kind; + uint8_t path_len; + int8_t snr_quarter_db; + int16_t rssi_dbm; + uint8_t target; + uint8_t reserved; + uint8_t peer_key[6]; + char sender[32]; + char text[194]; + uint32_t crc32; +}; + +struct UltimateMetricBucket { + uint32_t hour; + uint32_t rx_packets; + uint32_t tx_packets; + uint32_t tx_failures; + uint32_t airtime_ms; + int32_t rssi_total; + int32_t snr_quarter_total; + uint16_t signal_samples; + uint16_t battery_mv; + uint32_t crc32; +}; +#pragma pack(pop) + +static_assert(sizeof(UltimateHistoryRecord) == 256, + "Ultimate journal records must remain exactly 256 bytes"); + +using UltimateHistoryVisitor = bool (*)(const UltimateHistoryRecord& record, void* context); + +struct UltimateNetworkNode { + uint8_t key[6]; + char name[28]; + uint32_t last_seen; + uint32_t packet_count; + int16_t rssi_dbm; + int8_t snr_quarter_db; + uint8_t role; + uint8_t path_len; + bool signal_attributable; +}; + +struct UltimateThreadSummary { + uint8_t kind; + uint8_t target; + uint8_t peer_key[6]; + char label[32]; + uint32_t newest_sequence; + uint16_t unread; +}; + +struct UltimateDeliverySnapshot { + UltimateDeliveryState state = UltimateDeliveryState::Idle; + uint8_t kind = 0; + bool ack_expected = false; + uint32_t expected_ack = 0; + uint32_t history_sequence = 0; + uint32_t started_millis = 0; + uint32_t changed_millis = 0; + uint32_t deadline_millis = 0; + uint32_t round_trip_millis = 0; + char target[32] = {}; + char text[48] = {}; +}; + +struct UltimateComposerState { + uint8_t pinned_kind = 0; + uint8_t pinned_target = 0; + uint8_t pinned_key[6] = {}; + char pinned_label[32] = {}; + uint8_t draft_kind = 0; + uint8_t draft_target = 0; + uint8_t draft_key[6] = {}; + char draft_label[32] = {}; + char draft_text[141] = {}; +}; + +struct UltimateSnapshot { + uint32_t uptime_seconds; + uint32_t rx_packets; + uint32_t tx_packets; + uint32_t tx_failures; + uint32_t airtime_ms; + uint32_t event_drops; + uint32_t free_heap; + uint32_t largest_allocation; + uint32_t storage_used_kb; + uint32_t storage_total_kb; + uint32_t last_sequence; + uint16_t battery_mv; + uint16_t history_count; + uint16_t history_capacity; + uint16_t unread_count; + uint16_t display_tiles_sent; + uint32_t display_flush_micros; + uint32_t display_flush_ema_micros; + uint32_t memory_gate_last_pass_seconds; + int16_t battery_trend_mv_per_hour; + uint16_t battery_runtime_minutes; + uint16_t animation_frame_millis; + uint32_t display_timeout_millis; + int16_t last_rssi_dbm; + int8_t last_snr_quarter_db; + uint8_t outbound_queue_depth; + bool last_signal_valid; + bool memory_gate_passed; + bool battery_projection_valid; + bool usb_host_connected; +}; + +struct UltimateSettings { + uint16_t history_capacity; + uint16_t scan_cadence_ms; + bool private_notifications; + int16_t battery_calibration_mv; + uint16_t battery_capacity_mah; + uint8_t power_profile; + char quick_phrases[8][48]; +}; + +class UltimateService { + enum class PendingType : uint8_t { Message, Radio, Node }; + + struct PendingEvent { + PendingType type; + uint8_t subtype; + uint8_t flags; + uint8_t path_len; + uint8_t target; + uint8_t peer_key[6]; + uint32_t timestamp; + uint32_t airtime_ms; + int16_t rssi_dbm; + int8_t snr_quarter_db; + uint8_t role; + char sender[32]; + char text[194]; + }; + +#pragma pack(push, 1) + struct JournalMeta { + uint32_t magic; + uint32_t generation; + uint32_t next_sequence; + uint16_t version; + uint16_t capacity; + uint16_t count; + uint16_t head; + uint32_t reserved[2]; + uint32_t crc32; + }; + + struct SettingsFile { + uint32_t magic; + uint16_t version; + uint16_t history_capacity; + uint16_t scan_cadence_ms; + uint8_t private_notifications; + int16_t battery_calibration_mv; + uint8_t power_profile; + uint16_t battery_capacity_mah; + char quick_phrases[8][48]; + uint32_t crc32; + }; + + struct SettingsFileV1 { + uint32_t magic; + uint16_t version; + uint16_t history_capacity; + uint16_t scan_cadence_ms; + uint8_t private_notifications; + uint8_t reserved[5]; + char quick_phrases[8][48]; + uint32_t crc32; + }; + static_assert(sizeof(SettingsFile) == sizeof(SettingsFileV1), + "Ultimate settings migration must remain in-place"); + + struct ComposerFile { + uint32_t magic; + uint16_t version; + uint16_t reserved; + UltimateComposerState state; + uint32_t crc32; + }; + + struct HighResolutionMetric { + uint32_t timestamp; + uint16_t rx_packets; + uint16_t tx_packets; + uint16_t tx_failures; + int16_t rssi_dbm; + int8_t snr_quarter_db; + uint8_t queue_depth; + uint16_t battery_mv; + }; +#pragma pack(pop) + + static constexpr uint32_t history_magic = 0x32504E48; // HNP2 + static constexpr uint32_t meta_magic = 0x32504E4D; // MNP2 + static constexpr uint32_t settings_magic = 0x32504E53; // SNP2 + static constexpr uint32_t composer_magic = 0x32504E43; // CNP2 + static constexpr uint16_t format_version = 1; + static constexpr uint16_t settings_format_version = 2; + static constexpr uint8_t pending_capacity = 32; + static constexpr uint8_t network_capacity = 64; + static constexpr uint8_t thread_capacity = 32; + static constexpr uint16_t hourly_capacity = 168; + static constexpr uint8_t high_resolution_capacity = 120; + + fs::FS* filesystem = nullptr; + mesh::RTCClock* clock = nullptr; + mesh::MainBoard* board = nullptr; + DataStore* store = nullptr; + + PendingEvent pending[pending_capacity] = {}; + volatile uint8_t pending_head = 0; + volatile uint8_t pending_tail = 0; + volatile uint8_t pending_count = 0; + portMUX_TYPE pending_lock = portMUX_INITIALIZER_UNLOCKED; + + UltimateNetworkNode network[network_capacity] = {}; + uint8_t network_count = 0; + UltimateThreadSummary threads[thread_capacity] = {}; + uint8_t thread_count = 0; + UltimateMetricBucket hourly[hourly_capacity] = {}; + HighResolutionMetric high_resolution[high_resolution_capacity] = {}; + uint8_t high_resolution_head = 0; + uint8_t high_resolution_count = 0; + + JournalMeta meta = {}; + UltimateSettings settings = {}; + UltimateComposerState composer = {}; + UltimateDeliverySnapshot delivery = {}; + UltimateSnapshot snapshot = {}; + uint32_t last_hour = 0; + uint32_t next_status_sample = 0; + uint32_t last_metric_rx = 0; + uint32_t last_metric_tx = 0; + uint32_t last_metric_fail = 0; + uint32_t last_signal_millis = 0; + uint32_t next_usb_host_sample = 0; + bool usb_host_seen = false; + bool usb_host_connected = false; + + static uint32_t crc32(const void* data, size_t length); + static bool validCapacity(uint16_t capacity); + static void copyText(char* destination, size_t capacity, const char* source); + void setDefaults(); + bool loadSettings(); + bool saveSettings(); + bool loadComposer(); + bool saveComposer(); + bool loadMeta(); + bool writeMeta(); + bool removeIfExists(const char* path); + bool ensureJournalFile(uint16_t capacity); + bool recoverJournalSwap(); + bool readSlot(uint16_t slot, UltimateHistoryRecord& record) const; + bool writeSlot(uint16_t slot, const UltimateHistoryRecord& record); + bool appendRecord(const PendingEvent& event); + bool popEvent(PendingEvent& event); + bool pushEvent(const PendingEvent& event); + void processEvent(const PendingEvent& event); + void updateNetwork(const PendingEvent& event); + void updateThread(const UltimateHistoryRecord& record, bool newest_first); + void rebuildThreads(); + void updateMetrics(const PendingEvent& event); + void sampleStatus(); + void sampleHighResolution(); + void sampleBatteryProjection(); + void sampleUsbHostState(); + void refreshDeliveryState(uint32_t now); + bool saveMetricBucket(uint16_t slot); + void loadMetrics(); + +public: + bool begin(fs::FS& fs, mesh::RTCClock& rtc, mesh::MainBoard& main_board, DataStore& data_store); + void loop(); + void refreshStatusNow(); + + bool enqueueMessage(UltimateMessageKind kind, bool incoming, uint8_t target, + const uint8_t peer_key[6], const char* sender, const char* text, + uint32_t timestamp, uint8_t path_len, int16_t rssi_dbm, + int8_t snr_quarter_db); + bool enqueueRadio(UltimateRadioEvent event, uint8_t payload_type, + int16_t rssi_dbm = 0, int8_t snr_quarter_db = 0, + uint32_t airtime_ms = 0); + bool enqueueNode(const uint8_t peer_key[6], const char* name, uint8_t role, + uint8_t path_len, uint32_t timestamp, int16_t rssi_dbm, + int8_t snr_quarter_db, bool signal_attributable); + + const UltimateSnapshot& getSnapshot() const { return snapshot; } + const char* getBuildSha() const; + const UltimateSettings& getSettings() const { return settings; } + const UltimateComposerState& getComposerState() const { return composer; } + const UltimateDeliverySnapshot& getDelivery() const { return delivery; } + uint8_t getNetworkCount() const { return network_count; } + const UltimateNetworkNode* getNetworkNode(uint8_t index) const; + uint8_t getThreadCount() const { return thread_count; } + const UltimateThreadSummary* getThread(uint8_t index) const; + const UltimateMetricBucket* getHourlyMetricNewest(uint16_t offset) const; + bool getHighResolutionNewest(uint8_t offset, uint32_t& timestamp, uint16_t& rx, + uint16_t& tx, uint16_t& failures, int16_t& rssi, + int8_t& snr_quarter, uint8_t& queue_depth, + uint16_t& battery_mv) const; + uint16_t getHistoryCount() const { return meta.count; } + bool getHistoryNewest(uint16_t offset, UltimateHistoryRecord& record) const; + bool getNewestUnread(UltimateHistoryRecord& record) const; + uint16_t visitHistory(uint32_t before_sequence, uint16_t limit, bool oldest_first, + UltimateHistoryVisitor visitor, void* context) const; + bool getThreadMessage(uint8_t kind, uint8_t target, const uint8_t* peer_key, + uint16_t ordinal, UltimateHistoryRecord& record) const; + uint32_t getNewestSequence() const { return meta.count ? meta.next_sequence - 1 : 0; } + bool markRead(uint32_t sequence); + bool markAllRead(); + bool clearHistory(); + bool setHistoryCapacity(uint16_t capacity); + bool updateSettings(const UltimateSettings& updated); + bool setPinnedTarget(uint8_t kind, uint8_t target, const uint8_t* peer_key, + const char* label); + bool clearPinnedTarget(); + bool saveDraft(uint8_t kind, uint8_t target, const uint8_t* peer_key, + const char* label, const char* text); + bool clearDraft(); + void startDelivery(uint8_t kind, const char* target, const char* text, + uint32_t expected_ack, uint32_t timeout_millis); + void markDeliveryOnAir(); + void markDeliveryFailed(); + void markDeliveryAcked(uint32_t expected_ack, uint32_t round_trip_millis); + uint16_t getRecommendedFrameMillis() const; + uint32_t getDisplayTimeoutMillis() const; + void setMemoryGatePassed(bool passed) { + snapshot.memory_gate_passed = passed; + if (passed) snapshot.memory_gate_last_pass_seconds = millis() / 1000U; + } + void setQueueDepth(uint8_t depth) { snapshot.outbound_queue_depth = depth; } + void setDisplayTransfer(uint32_t micros, uint16_t tiles); +}; + +extern UltimateService ultimate_service; + +#endif diff --git a/examples/companion_radio/UltimateWebApi.cpp b/examples/companion_radio/UltimateWebApi.cpp new file mode 100644 index 00000000..2df175de --- /dev/null +++ b/examples/companion_radio/UltimateWebApi.cpp @@ -0,0 +1,469 @@ +#if defined(NEONPOCKET_ULTIMATE) && defined(RCC6_WEB_AP) + +#include "UltimateWebApi.h" + +#include "MyMesh.h" +#include "UltimateOtaPublicKey.h" +#include "UltimateService.h" +#include +#include +#include + +namespace { +constexpr uint16_t ota_format_version = 1; +#ifndef NEONPOCKET_ULTIMATE_OTA_TARGET + #define NEONPOCKET_ULTIMATE_OTA_TARGET "heltec_ultimate" +#endif +constexpr char ota_target[] = NEONPOCKET_ULTIMATE_OTA_TARGET; +constexpr char ota_mode[] = "web"; + +String jsonEscape(const char* text) { + String output; + if (!text) return output; + output.reserve(strlen(text) + 8); + for (const unsigned char* p = reinterpret_cast(text); *p; p++) { + if (*p == '"' || *p == '\\') { + output += '\\'; output += static_cast(*p); + } else if (*p == '\n') output += F("\\n"); + else if (*p == '\r') output += F("\\r"); + else if (*p == '\t') output += F("\\t"); + else if (*p >= 0x20) output += static_cast(*p); + } + return output; +} + +bool extractNumber(const String& body, const char* key, double& value) { + const String needle = String('"') + key + F("\""); + int at = body.indexOf(needle); + if (at < 0 || (at = body.indexOf(':', at + needle.length())) < 0) return false; + const char* start = body.c_str() + at + 1; + char* end = nullptr; + value = strtod(start, &end); + return end != start; +} + +bool extractBool(const String& body, const char* key, bool& value) { + const String needle = String('"') + key + F("\""); + int at = body.indexOf(needle); + if (at < 0 || (at = body.indexOf(':', at + needle.length())) < 0) return false; + const String tail = body.substring(at + 1); + if (tail.startsWith("true")) { value = true; return true; } + if (tail.startsWith("false")) { value = false; return true; } + return false; +} + +bool extractQuoted(const String& body, int& at, String& value) { + at = body.indexOf('"', at); + if (at < 0) return false; + value = ""; + bool escaped = false; + for (int i = at + 1; i < static_cast(body.length()); i++) { + const char c = body[i]; + if (escaped) { + if (c == 'n') value += '\n'; + else if (c == 'r') value += '\r'; + else if (c == 't') value += '\t'; + else value += c; + escaped = false; + } else if (c == '\\') escaped = true; + else if (c == '"') { at = i + 1; return true; } + else value += c; + } + return false; +} + +const char* otaResultName(UltimateWebApi::OtaResult result) { + switch (result) { + case UltimateWebApi::OtaResult::Success: return "success"; + case UltimateWebApi::OtaResult::Unauthorized: return "unauthorized"; + case UltimateWebApi::OtaResult::BadSignature: return "bad-signature"; + case UltimateWebApi::OtaResult::WrongTarget: return "wrong-target"; + case UltimateWebApi::OtaResult::HashMismatch: return "hash-mismatch"; + case UltimateWebApi::OtaResult::FlashError: return "flash-error"; + case UltimateWebApi::OtaResult::Aborted: return "aborted"; + case UltimateWebApi::OtaResult::BadPackage: return "bad-package"; + default: return "idle"; + } +} + +const char* deliveryStateName(UltimateDeliveryState state) { + switch (state) { + case UltimateDeliveryState::Queued: return "queued"; + case UltimateDeliveryState::OnAir: return "on-air"; + case UltimateDeliveryState::Transmitted: return "transmitted"; + case UltimateDeliveryState::Acked: return "acked"; + case UltimateDeliveryState::NoAck: return "no-ack"; + case UltimateDeliveryState::Unconfirmed: return "unconfirmed"; + case UltimateDeliveryState::Failed: return "failed"; + default: return "idle"; + } +} + +struct HistoryJsonContext { + String* json; + bool first; +}; + +bool appendHistoryJson(const UltimateHistoryRecord& record, void* raw) { + auto& context = *static_cast(raw); + String& json = *context.json; + if (!context.first) json += ','; + context.first = false; + json += F("{\"sequence\":"); json += record.sequence; + json += F(",\"timestamp\":"); json += record.timestamp; + json += F(",\"incoming\":"); json += (record.flags & ULTIMATE_HISTORY_INCOMING) ? F("true") : F("false"); + json += F(",\"read\":"); json += (record.flags & ULTIMATE_HISTORY_READ) ? F("true") : F("false"); + json += F(",\"kind\":"); json += record.kind; + json += F(",\"target\":"); json += record.target; + json += F(",\"sender\":\""); json += jsonEscape(record.sender); + json += F("\",\"text\":\""); json += jsonEscape(record.text); + json += F("\",\"path\":"); json += record.path_len; + json += F(",\"rssi\":"); json += record.rssi_dbm; + json += F(",\"snr\":"); json += String(record.snr_quarter_db / 4.0f, 2); + json += '}'; + return true; +} + +struct HistoryExportContext { + WebServer* server; + uint16_t sent; +}; + +bool streamHistoryNdjson(const UltimateHistoryRecord& record, void* raw) { + auto& context = *static_cast(raw); + String line = F("{\"sequence\":"); line += record.sequence; + line += F(",\"timestamp\":"); line += record.timestamp; + line += F(",\"incoming\":"); line += (record.flags & ULTIMATE_HISTORY_INCOMING) ? F("true") : F("false"); + line += F(",\"kind\":"); line += record.kind; + line += F(",\"target\":"); line += record.target; + line += F(",\"sender\":\""); line += jsonEscape(record.sender); + line += F("\",\"text\":\""); line += jsonEscape(record.text); line += F("\"}\n"); + context.server->sendContent(line); + if ((++context.sent & 7U) == 0) yield(); + return context.server->client().connected(); +} +} + +UltimateWebApi ultimate_web_api; + +bool UltimateWebApi::authorized() { + return interface && interface->authorizeHttpRequest(); +} + +void UltimateWebApi::sendJson(int status, const String& json) { + server->sendHeader("Cache-Control", "no-store"); + server->send(status, "application/json", json); +} + +void UltimateWebApi::begin(SerialWebInterface& web_interface) { + interface = &web_interface; + server = &web_interface.httpServer(); + server->on("/api/ultimate/status", HTTP_GET, [this]() { handleStatus(); }); + server->on("/api/ultimate/history", HTTP_GET, [this]() { handleHistory(); }); + server->on("/api/ultimate/export", HTTP_GET, [this]() { handleExport(); }); + server->on("/api/ultimate/history", HTTP_DELETE, [this]() { handleDeleteHistory(); }); + server->on("/api/ultimate/settings", HTTP_GET, [this]() { handleGetSettings(); }); + server->on("/api/ultimate/settings", HTTP_PUT, [this]() { handlePutSettings(); }); + server->on("/api/ultimate/location", HTTP_PUT, [this]() { handleLocation(); }); + server->on("/api/ultimate/ota", HTTP_POST, + [this]() { handleOtaComplete(); }, [this]() { handleOtaUpload(); }); +} + +void UltimateWebApi::loop() { + if (restart_at && static_cast(millis() - restart_at) >= 0) ESP.restart(); +} + +void UltimateWebApi::handleStatus() { + if (!authorized()) return; + const UltimateSnapshot& s = ultimate_service.getSnapshot(); + String json; + json.reserve(8192); + json = F("{\"version\":\""); json += NEONPOCKET_ULTIMATE_VERSION; + json += F("\",\"git\":\""); json += ultimate_service.getBuildSha(); + json += F("\",\"uptime\":"); json += s.uptime_seconds; + json += F(",\"rx\":"); json += s.rx_packets; + json += F(",\"tx\":"); json += s.tx_packets; + json += F(",\"txFailures\":"); json += s.tx_failures; + json += F(",\"airtimeMs\":"); json += s.airtime_ms; + json += F(",\"eventDrops\":"); json += s.event_drops; + json += F(",\"freeHeap\":"); json += s.free_heap; + json += F(",\"largestAllocation\":"); json += s.largest_allocation; + json += F(",\"storageUsedKb\":"); json += s.storage_used_kb; + json += F(",\"storageTotalKb\":"); json += s.storage_total_kb; + json += F(",\"batteryMv\":"); json += s.battery_mv; + json += F(",\"batteryTrendMvPerHour\":"); json += s.battery_trend_mv_per_hour; + json += F(",\"batteryRuntimeMinutes\":"); json += s.battery_runtime_minutes; + json += F(",\"batteryCapacityMah\":"); + json += ultimate_service.getSettings().battery_capacity_mah; + json += F(",\"batteryProjectionValid\":"); + json += s.battery_projection_valid ? F("true") : F("false"); + json += F(",\"usbHostConnected\":"); + json += s.usb_host_connected ? F("true") : F("false"); + json += F(",\"historyCount\":"); json += s.history_count; + json += F(",\"historyCapacity\":"); json += s.history_capacity; + json += F(",\"unread\":"); json += s.unread_count; + json += F(",\"queueDepth\":"); json += s.outbound_queue_depth; + json += F(",\"displayFlushUs\":"); json += s.display_flush_micros; + json += F(",\"displayFlushEmaUs\":"); json += s.display_flush_ema_micros; + json += F(",\"displayTiles\":"); json += s.display_tiles_sent; + json += F(",\"animationFrameMs\":"); json += s.animation_frame_millis; + json += F(",\"displayTimeoutMs\":"); json += s.display_timeout_millis; + json += F(",\"powerProfile\":"); json += ultimate_service.getSettings().power_profile; + json += F(",\"memoryGate\":"); json += s.memory_gate_passed ? F("true") : F("false"); + json += F(",\"memoryGateLastPass\":"); json += s.memory_gate_last_pass_seconds; + json += F(",\"signal\":"); + if (s.last_signal_valid) { + json += F("{\"rssi\":"); json += s.last_rssi_dbm; + json += F(",\"snr\":"); json += String(s.last_snr_quarter_db / 4.0f, 2); json += '}'; + } else json += F("null"); + + const UltimateDeliverySnapshot& delivery = ultimate_service.getDelivery(); + json += F(",\"delivery\":{\"state\":\""); json += deliveryStateName(delivery.state); + json += F("\",\"target\":\""); json += jsonEscape(delivery.target); + json += F("\",\"roundTripMs\":"); json += delivery.round_trip_millis; + json += F(",\"historySequence\":"); json += delivery.history_sequence; + json += F(",\"ackExpected\":"); json += delivery.ack_expected ? F("true") : F("false"); + json += '}'; + + json += F(",\"nodes\":["); + for (uint8_t i = 0; i < ultimate_service.getNetworkCount(); i++) { + const UltimateNetworkNode* node = ultimate_service.getNetworkNode(i); + if (!node) continue; + if (i) json += ','; + json += F("{\"name\":\""); json += jsonEscape(node->name); + json += F("\",\"role\":"); json += node->role; + json += F(",\"path\":"); json += node->path_len; + json += F(",\"seen\":"); json += node->last_seen; + json += F(",\"packets\":"); json += node->packet_count; + if (node->signal_attributable) { + json += F(",\"rssi\":"); json += node->rssi_dbm; + json += F(",\"snr\":"); json += String(node->snr_quarter_db / 4.0f, 2); + } + json += '}'; + } + json += F("],\"minutes\":["); + for (uint8_t i = 0; i < 120; i++) { + uint32_t timestamp; uint16_t rx, tx, failures, battery; int16_t rssi; + int8_t snr; uint8_t queue; + if (!ultimate_service.getHighResolutionNewest(i, timestamp, rx, tx, failures, + rssi, snr, queue, battery)) break; + if (i) json += ','; + json += '['; json += timestamp; json += ','; json += rx; json += ','; json += tx; + json += ','; json += failures; json += ','; json += rssi; json += ','; + json += String(snr / 4.0f, 2); json += ','; json += queue; json += ','; json += battery; + json += ']'; + } + json += F("],\"hours\":["); + bool first = true; + for (uint16_t i = 0; i < 168; i++) { + const UltimateMetricBucket* bucket = ultimate_service.getHourlyMetricNewest(i); + if (!bucket) continue; + if (!first) json += ','; first = false; + json += '['; json += bucket->hour; json += ','; json += bucket->rx_packets; + json += ','; json += bucket->tx_packets; json += ','; json += bucket->tx_failures; + json += ','; json += bucket->battery_mv; json += ']'; + } + json += F("]}"); + sendJson(200, json); +} + +void UltimateWebApi::handleHistory() { + if (!authorized()) return; + const uint32_t before = server->hasArg("before") ? strtoul(server->arg("before").c_str(), nullptr, 10) : 0; + uint16_t limit = server->hasArg("limit") ? server->arg("limit").toInt() : 20; + if (limit < 1) limit = 1; + if (limit > 50) limit = 50; + String json; + json.reserve(static_cast(limit) * 260 + 16); + json = F("{\"records\":["); + HistoryJsonContext context = {&json, true}; + ultimate_service.visitHistory(before, limit, false, appendHistoryJson, &context); + json += F("]}"); + sendJson(200, json); +} + +void UltimateWebApi::handleExport() { + if (!authorized()) return; + server->sendHeader("Content-Disposition", "attachment; filename=neonpocket-history.ndjson"); + server->setContentLength(CONTENT_LENGTH_UNKNOWN); + server->send(200, "application/x-ndjson", ""); + HistoryExportContext context = {server, 0}; + ultimate_service.visitHistory(0, 0, true, streamHistoryNdjson, &context); + server->sendContent(""); +} + +void UltimateWebApi::handleDeleteHistory() { + if (!authorized()) return; + if (!server->hasHeader("X-NP-Confirm") || server->header("X-NP-Confirm") != "clear") { + sendJson(409, F("{\"error\":\"confirmation-required\"}")); return; + } + const bool cleared = ultimate_service.clearHistory(); + sendJson(cleared ? 200 : 500, + cleared ? F("{\"cleared\":true}") : F("{\"cleared\":false}")); +} + +void UltimateWebApi::handleGetSettings() { + if (!authorized()) return; + const UltimateSettings& settings = ultimate_service.getSettings(); + String json = F("{\"historyCapacity\":"); json += settings.history_capacity; + json += F(",\"scanCadenceMs\":"); json += settings.scan_cadence_ms; + json += F(",\"privateNotifications\":"); json += settings.private_notifications ? F("true") : F("false"); + json += F(",\"batteryCalibrationMv\":"); json += settings.battery_calibration_mv; + json += F(",\"batteryCapacityMah\":"); json += settings.battery_capacity_mah; + json += F(",\"powerProfile\":"); json += settings.power_profile; + json += F(",\"quickPhrases\":["); + for (uint8_t i = 0; i < 8; i++) { + if (i) json += ','; + json += '"'; json += jsonEscape(settings.quick_phrases[i]); json += '"'; + } + json += F("]}"); + sendJson(200, json); +} + +void UltimateWebApi::handlePutSettings() { + if (!authorized()) return; + const String body = server->arg("plain"); + UltimateSettings updated = ultimate_service.getSettings(); + double number; + bool boolean; + if (extractNumber(body, "historyCapacity", number)) updated.history_capacity = number; + if (extractNumber(body, "scanCadenceMs", number)) updated.scan_cadence_ms = number; + if (extractNumber(body, "batteryCalibrationMv", number)) updated.battery_calibration_mv = number; + if (extractNumber(body, "batteryCapacityMah", number)) { + if (number != number || number < 0 || number > 20000 || + number != static_cast(number) || + (number != 0 && (number < 50 || number > 20000))) { + sendJson(400, F("{\"error\":\"invalid-battery-capacity\"}")); return; + } + updated.battery_capacity_mah = static_cast(number); + } + if (extractNumber(body, "powerProfile", number)) updated.power_profile = number; + if (extractBool(body, "privateNotifications", boolean)) updated.private_notifications = boolean; + int at = body.indexOf("\"quickPhrases\""); + if (at >= 0 && (at = body.indexOf('[', at)) >= 0) { + for (uint8_t i = 0; i < 8; i++) { + String phrase; + if (!extractQuoted(body, at, phrase) || phrase.length() >= sizeof(updated.quick_phrases[i])) { + sendJson(400, F("{\"error\":\"invalid-phrases\"}")); return; + } + phrase.toCharArray(updated.quick_phrases[i], sizeof(updated.quick_phrases[i])); + } + } + if (!ultimate_service.updateSettings(updated)) { + sendJson(400, F("{\"error\":\"invalid-settings\"}")); return; + } + handleGetSettings(); +} + +void UltimateWebApi::handleLocation() { + if (!authorized()) return; + const String body = server->arg("plain"); + double latitude, longitude, accuracy = 0; + if (!extractNumber(body, "latitude", latitude) || !extractNumber(body, "longitude", longitude) || + latitude < -90 || latitude > 90 || longitude < -180 || longitude > 180) { + sendJson(400, F("{\"error\":\"invalid-location\"}")); return; + } + extractNumber(body, "accuracy", accuracy); + bool advertise = false; + const bool has_advertise = extractBool(body, "advertise", advertise); + sensors.node_lat = latitude; + sensors.node_lon = longitude; + if (has_advertise) { + the_mesh.getNodePrefs()->advert_loc_policy = advertise ? ADVERT_LOC_PREFS : ADVERT_LOC_NONE; + } + the_mesh.savePrefs(); + String json = F("{\"saved\":true,\"latitude\":"); json += String(latitude, 6); + json += F(",\"longitude\":"); json += String(longitude, 6); + json += F(",\"accuracy\":"); json += String(accuracy, 1); + json += F(",\"advertise\":"); + json += (the_mesh.getNodePrefs()->advert_loc_policy == ADVERT_LOC_NONE ? F("false") : F("true")); + json += '}'; + sendJson(200, json); +} + +void UltimateWebApi::failOta(OtaResult result) { + ota_result = result; + if (ota_update_started) Update.abort(); + ota_update_started = false; +} + +bool UltimateWebApi::validateOtaHeader() { + if (memcmp(ota_header.magic, "NPU2", 4) != 0 || + ota_header.format_version != ota_format_version || + ota_header.header_size != sizeof(UltimateOtaHeader) || + ota_header.application_length == 0) { + failOta(OtaResult::BadPackage); return false; + } + if (strncmp(ota_header.target, ota_target, sizeof(ota_header.target)) != 0 || + strncmp(ota_header.mode, ota_mode, sizeof(ota_header.mode)) != 0) { + failOta(OtaResult::WrongTarget); return false; + } + const size_t signed_length = offsetof(UltimateOtaHeader, signature); + if (!Ed25519::verify(ota_header.signature, ULTIMATE_OTA_PUBLIC_KEY, + reinterpret_cast(&ota_header), signed_length)) { + failOta(OtaResult::BadSignature); return false; + } + if (!Update.begin(ota_header.application_length, U_FLASH)) { + failOta(OtaResult::FlashError); return false; + } + ota_hash.reset(); + ota_update_started = true; + return true; +} + +bool UltimateWebApi::writeOtaPayload(const uint8_t* data, size_t length) { + if (!ota_update_started || ota_payload_bytes + length > ota_header.application_length) { + failOta(OtaResult::BadPackage); return false; + } + ota_hash.update(data, length); + if (Update.write(const_cast(data), length) != length) { + failOta(OtaResult::FlashError); return false; + } + ota_payload_bytes += length; + return true; +} + +void UltimateWebApi::handleOtaUpload() { + HTTPUpload& upload = server->upload(); + if (upload.status == UPLOAD_FILE_START) { + ota_header = {}; + ota_header_bytes = ota_payload_bytes = 0; + ota_update_started = false; + ota_result = authorized() ? OtaResult::Receiving : OtaResult::Unauthorized; + } else if (upload.status == UPLOAD_FILE_WRITE && ota_result == OtaResult::Receiving) { + const uint8_t* data = upload.buf; + size_t length = upload.currentSize; + if (ota_header_bytes < sizeof(ota_header)) { + const size_t take = min(length, sizeof(ota_header) - ota_header_bytes); + memcpy(reinterpret_cast(&ota_header) + ota_header_bytes, data, take); + ota_header_bytes += take; data += take; length -= take; + if (ota_header_bytes == sizeof(ota_header) && !validateOtaHeader()) return; + } + if (length) writeOtaPayload(data, length); + } else if (upload.status == UPLOAD_FILE_END && ota_result == OtaResult::Receiving) { + if (!ota_update_started || ota_payload_bytes != ota_header.application_length) { + failOta(OtaResult::BadPackage); return; + } + uint8_t digest[32]; ota_hash.finalize(digest, sizeof(digest)); + if (memcmp(digest, ota_header.application_sha256, sizeof(digest)) != 0) { + failOta(OtaResult::HashMismatch); return; + } + if (!Update.end(false)) { failOta(OtaResult::FlashError); return; } + ota_update_started = false; + ota_result = OtaResult::Success; + } else if (upload.status == UPLOAD_FILE_ABORTED) { + failOta(OtaResult::Aborted); + } +} + +void UltimateWebApi::handleOtaComplete() { + const OtaResult result = ota_result; + const int status = result == OtaResult::Success ? 200 : + result == OtaResult::Unauthorized ? 401 : 400; + String json = F("{\"result\":\""); json += otaResultName(result); json += F("\"}"); + sendJson(status, json); + if (result == OtaResult::Success) restart_at = millis() + 900; + ota_result = OtaResult::Idle; +} + +#endif diff --git a/examples/companion_radio/UltimateWebApi.h b/examples/companion_radio/UltimateWebApi.h new file mode 100644 index 00000000..fb601010 --- /dev/null +++ b/examples/companion_radio/UltimateWebApi.h @@ -0,0 +1,66 @@ +#pragma once + +#if defined(NEONPOCKET_ULTIMATE) && defined(RCC6_WEB_AP) + +#include +#include +#include + +class SerialWebInterface; + +#pragma pack(push, 1) +struct UltimateOtaHeader { + char magic[4]; + uint16_t format_version; + uint16_t header_size; + char target[16]; + char mode[8]; + char version[16]; + char git_sha[41]; + uint32_t application_length; + uint8_t application_sha256[32]; + uint8_t signature[64]; +}; +#pragma pack(pop) + +class UltimateWebApi { +public: + enum class OtaResult : uint8_t { + Idle, Receiving, Success, Unauthorized, BadPackage, BadSignature, + WrongTarget, HashMismatch, FlashError, Aborted + }; + +private: + SerialWebInterface* interface = nullptr; + WebServer* server = nullptr; + UltimateOtaHeader ota_header = {}; + SHA256 ota_hash; + OtaResult ota_result = OtaResult::Idle; + size_t ota_header_bytes = 0; + size_t ota_payload_bytes = 0; + bool ota_update_started = false; + uint32_t restart_at = 0; + + bool authorized(); + void sendJson(int status, const String& json); + void handleStatus(); + void handleHistory(); + void handleExport(); + void handleDeleteHistory(); + void handleGetSettings(); + void handlePutSettings(); + void handleLocation(); + void handleOtaComplete(); + void handleOtaUpload(); + bool validateOtaHeader(); + bool writeOtaPayload(const uint8_t* data, size_t length); + void failOta(OtaResult result); + +public: + void begin(SerialWebInterface& web_interface); + void loop(); +}; + +extern UltimateWebApi ultimate_web_api; + +#endif diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index a0a1933f..bb28f010 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -1,6 +1,10 @@ #include // needed for PlatformIO +#include #include #include "MyMesh.h" +#ifdef NEONPOCKET_ULTIMATE + #include "UltimateService.h" +#endif // Believe it or not, this std C function is busted on some platforms! static uint32_t _atoi(const char* sp) { @@ -45,6 +49,19 @@ MultiSerialInterface interface_manager; #endif #endif +// NeonPocket Ultimate AP/LAN Web companion. This is a separate transport image; +// it is never enabled alongside BLE. +#ifdef RCC6_WEB_AP + #include + SerialWebInterface web_interface; + #ifdef NEONPOCKET_ULTIMATE + #include "UltimateWebApi.h" + #endif + #ifndef TCP_PORT + #define TCP_PORT 5000 + #endif +#endif + // include usb interface #if defined(ENABLE_USB_INTERFACE) #include @@ -149,6 +166,17 @@ static bool beginSpiffsPreservingData() { } #endif +#ifdef NEONPOCKET_MEMORY_GATE_BYTES +static unsigned long next_neon_memory_probe = 0; + +static bool probeNeonMemory() { + void* probe = malloc(NEONPOCKET_MEMORY_GATE_BYTES); + if (probe == nullptr) return false; + free(probe); + return true; +} +#endif + /* WIFI RECONNECT TRACKERS */ #if defined(ESP32) && defined(WIFI_SSID) bool wifi_needs_reconnect = false; @@ -248,6 +276,15 @@ void setup() { false #endif ); +#ifdef NEONPOCKET_ULTIMATE + if (!ultimate_service.begin(SPIFFS, rtc_clock, board, store)) { + Serial.println("ERROR: Ultimate storage initialization failed; data preserved"); + #ifdef DISPLAY_CLASS + showFatal(disp, "ULTIMATE STORAGE", "Data preserved"); + #endif + halt(); + } +#endif #else #error "need to define filesystem" #endif @@ -278,6 +315,16 @@ void setup() { interface_manager.addInterface(InterfaceType::WiFi, &wifi_interface); #endif +// add NeonPocket AP/LAN Web interface +#ifdef RCC6_WEB_AP + board.setInhibitSleep(true); + web_interface.begin(the_mesh.getNodePrefs()->node_name, TCP_PORT); +#ifdef NEONPOCKET_ULTIMATE + ultimate_web_api.begin(web_interface); +#endif + interface_manager.addInterface(InterfaceType::WiFi, &web_interface); +#endif + // add usb interface #if defined(ENABLE_USB_INTERFACE) usb_serial_interface.begin(Serial); @@ -309,11 +356,37 @@ void setup() { ui_task.begin(disp, &sensors, the_mesh.getNodePrefs()); // still want to pass this in as dependency, as prefs might be moved #endif +#ifdef NEONPOCKET_MEMORY_GATE_BYTES + if (!probeNeonMemory()) { + Serial.print("ERROR: NeonPocket memory gate failed: "); + Serial.print(NEONPOCKET_MEMORY_GATE_BYTES / 1024); + Serial.println(" KB"); + #ifdef DISPLAY_CLASS + showFatal(disp, "MEMORY GATE", "Activity halted"); + #endif + halt(); + } + Serial.print("NeonPocket: memory gate passed: "); + Serial.print(NEONPOCKET_MEMORY_GATE_BYTES / 1024); + Serial.println(" KB"); +#ifdef NEONPOCKET_ULTIMATE + ultimate_service.setMemoryGatePassed(true); + ultimate_service.refreshStatusNow(); +#endif + next_neon_memory_probe = millis() + 60000; +#endif + board.onBootComplete(); } void loop() { the_mesh.loop(); +#ifdef NEONPOCKET_ULTIMATE + ultimate_service.loop(); + #ifdef RCC6_WEB_AP + ultimate_web_api.loop(); + #endif +#endif interface_manager.loop(); sensors.loop(); #ifdef DISPLAY_CLASS @@ -324,6 +397,23 @@ void loop() { external_watchdog.loop(); #endif +#ifdef NEONPOCKET_MEMORY_GATE_BYTES + const unsigned long memory_now = millis(); + if ((long)(memory_now - next_neon_memory_probe) >= 0) { + next_neon_memory_probe = memory_now + 60000; + if (!probeNeonMemory()) { + Serial.println("ERROR: NeonPocket runtime memory gate failed"); + #ifdef DISPLAY_CLASS + showFatal(&display, "MEMORY GATE", "Activity halted"); + #endif + halt(); + } + #ifdef NEONPOCKET_ULTIMATE + ultimate_service.setMemoryGatePassed(true); + #endif + } +#endif + if (!the_mesh.hasPendingWork()) { #if defined(NRF52_PLATFORM) board.sleep(0); // nrf ignores seconds param, sleeps whenever possible diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index eff8c419..f1013920 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -5,6 +5,13 @@ #ifdef NEONPOCKET_MONO_UI #include "NeonPocketMono.h" #endif +#ifdef NEONPOCKET_ULTIMATE + #include "../UltimateService.h" +#endif +#ifdef RCC6_WEB_AP + #include + extern SerialWebInterface web_interface; +#endif #ifdef WIFI_SSID #include #endif @@ -103,7 +110,13 @@ class HomeScreen : public UIScreen { enum HomePage { FIRST, RECENT, +#ifdef NEONPOCKET_ULTIMATE + NETWORK, +#endif RADIO, +#ifdef NEONPOCKET_ULTIMATE + ULTIMATE, +#endif BLUETOOTH, ADVERT, #if ENV_INCLUDE_GPS == 1 @@ -379,7 +392,36 @@ class HomeScreen : public UIScreen { display.setCursor(display.width() - timestamp_width - 1, y); display.print(tmp); } - } else if (_page == HomePage::RADIO) { + } +#ifdef NEONPOCKET_ULTIMATE + else if (_page == HomePage::NETWORK) { + const uint8_t count = ultimate_service.getNetworkCount(); + display.setTextSize(1); + display.setColor(UIColor::secondary_txt); + display.setCursor(2, 19); + display.print("NETWORK"); + display.setColor(UIColor::primary_txt); + snprintf(tmp, sizeof(tmp), "%u HEARD", count); + display.drawTextRightAlign(126, 19, tmp); + for (uint8_t row = 0; row < 3 && row < count; row++) { + const UltimateNetworkNode* node = ultimate_service.getNetworkNode(row); + if (node == nullptr) continue; + const int y = 30 + row * 11; + display.drawTextEllipsized(2, y, 78, node->name); + if (node->signal_attributable) { + snprintf(tmp, sizeof(tmp), "%dd %dH", node->rssi_dbm, node->path_len); + } else { + snprintf(tmp, sizeof(tmp), "-- %dH", node->path_len); + } + display.drawTextRightAlign(126, y, tmp); + } + if (count == 0) { + display.setColor(UIColor::secondary_txt); + display.drawTextCentered(64, 40, "Listening on preset"); + } + } +#endif + else if (_page == HomePage::RADIO) { display.setColor(UIColor::primary_txt); display.setTextSize(1); // freq / sf @@ -398,7 +440,44 @@ class HomeScreen : public UIScreen { display.setCursor(0, 53); sprintf(tmp, "Noise floor: %d", radio_driver.getNoiseFloor()); display.print(tmp); - } else if (_page == HomePage::BLUETOOTH) { + } +#ifdef NEONPOCKET_ULTIMATE + else if (_page == HomePage::ULTIMATE) { + const UltimateSnapshot& status = ultimate_service.getSnapshot(); + display.setTextSize(1); + display.setColor(UIColor::secondary_txt); + display.setCursor(2, 19); + display.print("ULTIMATE"); + display.setColor(UIColor::primary_txt); + snprintf(tmp, sizeof(tmp), "HIST %u/%u", status.history_count, status.history_capacity); + display.setCursor(2, 30); + display.print(tmp); + snprintf(tmp, sizeof(tmp), "UNREAD %u HEAP %luK", status.unread_count, + (unsigned long)(status.free_heap / 1024)); + display.setCursor(2, 41); + display.print(tmp); + snprintf(tmp, sizeof(tmp), "RX%lu TX%lu E%lu", (unsigned long)status.rx_packets, + (unsigned long)status.tx_packets, (unsigned long)status.tx_failures); + display.setCursor(2, 52); + display.print(tmp); + } +#endif + else if (_page == HomePage::BLUETOOTH) { +#ifdef RCC6_WEB_AP + display.setTextSize(1); + display.setColor(UIColor::secondary_txt); + display.setCursor(2, 19); + display.print(web_interface.isStationMode() ? "LOCAL WIFI" : "SETUP AP"); + display.setColor(UIColor::primary_txt); + display.drawTextEllipsized(2, 31, 124, web_interface.getCurrentSsid()); + IPAddress ip = web_interface.getCurrentIP(); + snprintf(tmp, sizeof(tmp), "%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]); + display.setCursor(2, 43); + display.print(tmp); + display.setColor(UIColor::secondary_txt); + display.setCursor(2, 54); + display.print("HTTP + TCP/5000"); +#else display.setColor(UIColor::corp_blue); display.drawXbm((display.width() - 32) / 2, 18, _task->isBluetoothEnabled() ? bluetooth_on : bluetooth_off, @@ -406,6 +485,7 @@ class HomeScreen : public UIScreen { display.setColor(UIColor::secondary_txt); display.setTextSize(1); display.drawTextCentered(display.width() / 2, 64 - 11, "toggle: " PRESS_LABEL); +#endif } else if (_page == HomePage::ADVERT) { display.setColor(UIColor::corp_blue); display.drawXbm((display.width() - 32) / 2, 18, advert_icon, 32, 32); @@ -601,16 +681,26 @@ class HomeScreen : public UIScreen { } #endif if (c == KEY_ENTER && _page == HomePage::BLUETOOTH) { +#ifdef RCC6_WEB_AP + _task->showAlert(web_interface.isStationMode() ? "Local Wi-Fi active" : "Setup AP active", 1000); +#else if (_task->isBluetoothEnabled()) { // toggle Bluetooth on/off _task->disableBluetooth(); } else { _task->enableBluetooth(); } +#endif return true; } if (c == KEY_ENTER && _page == HomePage::ADVERT) { _task->notify(UIEventType::ack); - if (the_mesh.advert()) { + if (the_mesh.advert( +#ifdef NEONPOCKET_ULTIMATE + true +#else + false +#endif + )) { _task->showAlert("Advert sent!", 1000); } else { _task->showAlert("Advert failed..", 1000); @@ -806,8 +896,13 @@ switch(t){ void UITask::msgRead(int msgcount) { +#ifdef NEONPOCKET_ULTIMATE + (void)msgcount; + _msgcount = ultimate_service.getSnapshot().unread_count; +#else _msgcount = msgcount; - if (msgcount == 0) { +#endif + if (_msgcount == 0) { gotoHomeScreen(); } } @@ -890,6 +985,9 @@ bool UITask::isButtonPressed() const { } void UITask::loop() { +#ifdef NEONPOCKET_ULTIMATE + _msgcount = ultimate_service.getSnapshot().unread_count; +#endif char c = 0; #if UI_HAS_JOYSTICK int ev = user_btn.check(); @@ -1086,6 +1184,10 @@ char UITask::handleTripleClick(char c) { c = checkDisplayOn(KEY_SELECT); if (c == 0) return 0; if (curr == msg_preview) curr->handleInput(KEY_ENTER); +#ifdef NEONPOCKET_ULTIMATE + ultimate_service.markAllRead(); + _msgcount = 0; +#endif static_cast(home)->neonGoHome(); gotoHomeScreen(); #else diff --git a/examples/companion_radio/webui/.gitignore b/examples/companion_radio/webui/.gitignore new file mode 100644 index 00000000..444ae09c --- /dev/null +++ b/examples/companion_radio/webui/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +.cache/ +.tmp/ +.build/ +.inspect/ diff --git a/examples/companion_radio/webui/README.md b/examples/companion_radio/webui/README.md new file mode 100644 index 00000000..55e4504f --- /dev/null +++ b/examples/companion_radio/webui/README.md @@ -0,0 +1,35 @@ +# NeonPocket Ultimate MeshCore WebUI + +Responsive, offline WebUI shared by the Heltec V3/V4 Ultimate companion radios. It starts on a 2.4 GHz setup AP, can save a local 2.4 GHz SSID/password, reboot into station mode, and shows its DHCP address on the device screen. + +The browser uses the MeshCore companion protocol through a polling HTTP transport: + +- `POST /api/frame` accepts one raw companion payload as `application/octet-stream`. +- `GET /api/frame` returns one raw companion payload, or HTTP `204` when none is queued. +- Frames larger than 176 bytes are rejected by the browser transport. + +The generated UI is a single gzip-compressed HTML document. The firmware translation unit that owns the asset must define `RCC6_WEB_UI_ASSETS_IMPLEMENTATION` before including `Rcc6WebUiAssets.h`. Other translation units include the header normally. + +## Build + +Keep npm cache and temporary files on the F: worktree: + +```powershell +$env:npm_config_cache = "$PWD\.cache" +$env:TEMP = "$PWD\.tmp" +$env:TMP = "$PWD\.tmp" +npm ci +npm test +``` + +`npm test` bundles the app, creates `Rcc6WebUiAssets.h`, decompresses the generated byte array, and checks the endpoint and screen contract. The UI imports `Connection` and `Constants` directly from MeshCore.js so Node-only serial transports are not included. + +MeshCore.js 1.14.0 parses the current firmware's core-stats payload incorrectly, so this UI intentionally uses `getBatteryVoltage()`, `getStatsRadio()`, and `getStatsPackets()` and does not display core queue depth. + +## Security boundary + +This browser code exposes only query, sync, send, and self-advert actions. The HTTP transport must enforce the same command allowlist and one-controller policy server-side; browser validation is not a security boundary. + +Station-mode HTTP uses Basic authentication with username `meshcore` and the device key shown on the TFT. Raw TCP port 5000 remains the full, unauthenticated MeshCore companion/admin protocol for native clients. Only enable station mode on a trusted private LAN: any host on that LAN can control the radio over TCP, including administrative commands. + +See `THIRD_PARTY_NOTICES.md` for bundled software attribution. diff --git a/examples/companion_radio/webui/Rcc6WebUiAssets.h b/examples/companion_radio/webui/Rcc6WebUiAssets.h new file mode 100644 index 00000000..14bb72ad --- /dev/null +++ b/examples/companion_radio/webui/Rcc6WebUiAssets.h @@ -0,0 +1,3341 @@ +// Generated by examples/companion_radio/webui/build.mjs. Do not edit. +// gzip sha256: a55cd5362e1df89380df8f66d4380119dc18070919d60499f4ad9d3b108b595f +#pragma once +#include + +extern const uint8_t RCC6_WEB_UI_INDEX_GZ[] PROGMEM; +extern const size_t RCC6_WEB_UI_INDEX_GZ_LEN; + +#ifdef RCC6_WEB_UI_ASSETS_IMPLEMENTATION +const uint8_t RCC6_WEB_UI_INDEX_GZ[] PROGMEM = { + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0xbd, 0xdb, 0x72, 0xdb, 0x48, + 0xb6, 0x20, 0xfa, 0xde, 0x5f, 0x91, 0x66, 0x95, 0x65, 0xa2, 0x94, 0xa4, 0x70, 0x25, 0x41, 0x52, + 0xa0, 0x47, 0x96, 0xe5, 0xb2, 0xbb, 0x6d, 0xc9, 0x65, 0xc9, 0x2e, 0x57, 0x39, 0xdc, 0x55, 0xb8, + 0x24, 0x48, 0xd8, 0x20, 0xc0, 0x02, 0x40, 0x5d, 0x4a, 0xe6, 0x44, 0x3f, 0xcd, 0x07, 0x9c, 0x39, + 0x11, 0xf3, 0x07, 0x13, 0x71, 0xbe, 0x60, 0xbf, 0xef, 0xf3, 0x27, 0xfd, 0x25, 0x27, 0x56, 0x66, + 0x02, 0x48, 0x5c, 0x48, 0x51, 0x76, 0xed, 0x89, 0xb3, 0x6b, 0xb7, 0x45, 0x00, 0x89, 0xcc, 0x95, + 0x99, 0xeb, 0xbe, 0x56, 0x2e, 0x1c, 0x3e, 0xf0, 0x62, 0x37, 0xbb, 0x59, 0x12, 0x34, 0xcf, 0x16, + 0xe1, 0xf4, 0x6f, 0x87, 0xf0, 0x07, 0x85, 0x76, 0x34, 0xb3, 0x3a, 0x24, 0xea, 0xc0, 0x0d, 0x62, + 0x7b, 0xd3, 0xbf, 0x21, 0x74, 0xb8, 0x20, 0x99, 0x8d, 0xdc, 0xb9, 0x9d, 0xa4, 0x24, 0xb3, 0x3a, + 0xab, 0xcc, 0xef, 0x99, 0x9d, 0xf2, 0x41, 0x64, 0x2f, 0x88, 0xd5, 0xb9, 0x0c, 0xc8, 0xd5, 0x32, + 0x4e, 0xb2, 0x0e, 0x72, 0xe3, 0x28, 0x23, 0x51, 0x66, 0x75, 0xae, 0x02, 0x2f, 0x9b, 0x5b, 0x1e, + 0xb9, 0x0c, 0x5c, 0xd2, 0xa3, 0x17, 0x38, 0x88, 0x82, 0x2c, 0xb0, 0xc3, 0x5e, 0xea, 0xda, 0x21, + 0xb1, 0x14, 0x9c, 0xbf, 0xd5, 0xf3, 0x83, 0xcc, 0x72, 0xe3, 0x4b, 0x92, 0x34, 0x3a, 0xce, 0xe6, + 0x64, 0x41, 0x7a, 0x6e, 0x1c, 0xc6, 0x89, 0xd0, 0xf7, 0x77, 0xb2, 0x21, 0x0f, 0xe4, 0x26, 0x18, + 0xf6, 0x72, 0x19, 0x92, 0xde, 0x22, 0x76, 0x82, 0x90, 0xf4, 0xae, 0x88, 0xd3, 0xb3, 0x97, 0xcb, + 0x9e, 0x6b, 0x2f, 0x6d, 0x27, 0x24, 0xc2, 0xeb, 0x37, 0x24, 0xdd, 0xed, 0xd5, 0x34, 0xb3, 0xb3, + 0x55, 0xda, 0x73, 0xec, 0xa4, 0x97, 0x66, 0x37, 0x95, 0x3e, 0x9c, 0xd0, 0x76, 0x3f, 0xf7, 0xb2, + 0xc4, 0x8e, 0xd2, 0x70, 0xe5, 0x92, 0x28, 0x6b, 0xed, 0x31, 0x70, 0xed, 0x2c, 0x88, 0xa3, 0x1e, + 0xdc, 0x11, 0x5e, 0x3e, 0x25, 0x71, 0xf4, 0x3a, 0x76, 0x3f, 0x93, 0xec, 0xd5, 0xb1, 0xf0, 0xde, + 0x3c, 0xcb, 0x96, 0x3d, 0xf2, 0xc7, 0x2a, 0xb8, 0xb4, 0x3a, 0xc7, 0xac, 0x6d, 0xef, 0x9c, 0xb8, + 0xab, 0x24, 0xc8, 0x6e, 0x7a, 0xaf, 0xe3, 0x30, 0x70, 0x6f, 0x84, 0x4e, 0x3c, 0xe2, 0xdb, 0xab, + 0x30, 0xeb, 0xa5, 0x89, 0x8b, 0x1e, 0xa5, 0x24, 0xf4, 0x1f, 0x4d, 0xe0, 0x61, 0x44, 0xdc, 0xea, + 0xbd, 0x60, 0x31, 0x13, 0xae, 0x91, 0x67, 0x67, 0xf6, 0x78, 0x82, 0x52, 0x37, 0x09, 0x96, 0xbc, + 0xe1, 0x2a, 0x4a, 0x6d, 0x9f, 0xf4, 0x82, 0x28, 0x0c, 0x22, 0xf2, 0x68, 0x82, 0xe8, 0x5c, 0xdb, + 0x1f, 0x39, 0x76, 0x4a, 0x7a, 0xab, 0x24, 0x40, 0x8f, 0xa2, 0x98, 0xde, 0x88, 0x9d, 0x4f, 0xc5, + 0x80, 0xf4, 0x16, 0x9b, 0x4f, 0x18, 0x44, 0x9f, 0x51, 0x42, 0x42, 0xab, 0x13, 0xb8, 0x71, 0xd4, + 0x41, 0xf3, 0x84, 0xf8, 0x56, 0x87, 0x8e, 0x1d, 0x2c, 0xec, 0x19, 0x39, 0x48, 0x2f, 0x67, 0xfb, + 0xd7, 0x8b, 0x10, 0x3f, 0xd4, 0x8e, 0xd3, 0xcb, 0x19, 0xba, 0x5e, 0x84, 0x51, 0x6a, 0x3d, 0x82, + 0x15, 0x18, 0x1f, 0x1c, 0x5c, 0x5d, 0x5d, 0xf5, 0xaf, 0xb4, 0x7e, 0x9c, 0xcc, 0x0e, 0x54, 0x59, + 0x96, 0xa1, 0xf1, 0x23, 0x04, 0xd8, 0xf2, 0x24, 0xbe, 0xb6, 0x1e, 0xc9, 0x48, 0x46, 0x03, 0x1d, + 0x0d, 0xf4, 0x47, 0x0f, 0xb5, 0x93, 0x87, 0xda, 0x71, 0x42, 0xdc, 0x0c, 0x31, 0x74, 0x7b, 0x34, + 0xd0, 0x1f, 0xa1, 0x39, 0x09, 0x66, 0xf3, 0x8c, 0xfd, 0x4e, 0xae, 0xad, 0x47, 0x8a, 0xfe, 0x08, + 0xf9, 0x41, 0x18, 0x5a, 0x8f, 0x1e, 0xaa, 0x1a, 0xc3, 0x9c, 0x47, 0x07, 0xec, 0xd5, 0xa5, 0x9d, + 0xcd, 0x91, 0x67, 0x3d, 0x7a, 0xa5, 0xc8, 0x48, 0x55, 0xe7, 0xba, 0x7e, 0xa9, 0xe9, 0xcf, 0x15, + 0xf9, 0xcf, 0xfc, 0x05, 0x3a, 0x23, 0x94, 0x66, 0x49, 0xfc, 0x99, 0xd0, 0xd7, 0xb5, 0x11, 0x19, + 0xfa, 0x7e, 0x7e, 0xab, 0xc7, 0x47, 0xd5, 0xeb, 0xfd, 0x2d, 0x34, 0x15, 0x69, 0x72, 0x4f, 0xd1, + 0x91, 0x62, 0xce, 0x55, 0x73, 0x73, 0x7f, 0xaa, 0x6e, 0xb4, 0xf6, 0x97, 0xdf, 0x80, 0x65, 0xff, + 0x14, 0x07, 0x91, 0xf5, 0x28, 0x89, 0x57, 0x91, 0xd7, 0x84, 0x7b, 0x80, 0x14, 0x7d, 0x3e, 0x78, + 0xa5, 0x29, 0x68, 0x70, 0x69, 0xbe, 0xd2, 0x75, 0xa4, 0x28, 0xa1, 0xd1, 0xd3, 0xbe, 0x75, 0x02, + 0xaf, 0x54, 0x13, 0xa9, 0x83, 0xb9, 0x79, 0x69, 0xce, 0x7b, 0xe6, 0x9f, 0xaf, 0x14, 0x1d, 0xe9, + 0x7a, 0x71, 0xa5, 0xab, 0xc2, 0x95, 0xb0, 0xb6, 0xba, 0xee, 0xcb, 0xa6, 0x9d, 0x77, 0x05, 0xbb, + 0xf6, 0x50, 0x3b, 0x61, 0xf8, 0x90, 0x05, 0x59, 0x48, 0xa6, 0x22, 0xde, 0xa3, 0xff, 0xfc, 0x0f, + 0xf4, 0x36, 0xcc, 0x82, 0x85, 0x9d, 0x91, 0xc3, 0x03, 0xf6, 0x1c, 0x5a, 0x52, 0xe4, 0x9b, 0x8e, + 0x93, 0x38, 0xce, 0x6e, 0x29, 0xdd, 0xf7, 0x52, 0x17, 0x98, 0xc0, 0xd8, 0xb3, 0x93, 0xcf, 0x93, + 0x5e, 0xcf, 0x99, 0x8d, 0x11, 0xa7, 0xff, 0x49, 0xaf, 0xb7, 0xb4, 0x23, 0x12, 0xc2, 0x0d, 0x57, + 0xf6, 0x15, 0x2d, 0xbf, 0xd1, 0x53, 0xc7, 0xe8, 0x3b, 0x45, 0x51, 0x0c, 0xc5, 0x99, 0xf4, 0xe8, + 0x1a, 0x8e, 0xd1, 0x77, 0xaa, 0xaa, 0x8e, 0x34, 0x7d, 0xd2, 0xeb, 0x65, 0xe4, 0x3a, 0x1b, 0xa3, + 0xef, 0x7c, 0xc3, 0x37, 0x7d, 0x78, 0xbe, 0x58, 0x65, 0xc4, 0x1b, 0xa3, 0xef, 0x4c, 0x6f, 0x64, + 0xda, 0xc3, 0x49, 0xaf, 0x37, 0x4b, 0x08, 0x89, 0xc6, 0xe8, 0x3b, 0x36, 0x1f, 0x18, 0x33, 0x5c, + 0x41, 0x0f, 0xba, 0x6a, 0x1b, 0xbe, 0x3f, 0xe9, 0xf5, 0x6e, 0x48, 0x18, 0xc6, 0x57, 0xd0, 0x87, + 0xef, 0xe9, 0x3a, 0xbc, 0x12, 0x27, 0x76, 0x34, 0x23, 0xf4, 0x8e, 0x69, 0x6b, 0xde, 0xa4, 0xd7, + 0x4b, 0x68, 0x9f, 0xbe, 0x6f, 0xe8, 0x03, 0x00, 0x34, 0xb1, 0xbd, 0x60, 0x95, 0x8e, 0x91, 0xaa, + 0x2e, 0xaf, 0x27, 0xbd, 0x5e, 0x3a, 0xb7, 0x3d, 0xe8, 0x41, 0x46, 0xaa, 0xbc, 0xbc, 0x46, 0x43, + 0xf8, 0x27, 0x99, 0x39, 0x76, 0x57, 0xc6, 0x88, 0xfd, 0x7f, 0x5f, 0x53, 0xa5, 0x89, 0x1f, 0x47, + 0x59, 0xcf, 0xb7, 0x17, 0x41, 0x78, 0x33, 0xee, 0x31, 0x16, 0x95, 0xde, 0xa4, 0x19, 0x59, 0xe0, + 0x27, 0x40, 0x61, 0xaf, 0x6c, 0xf7, 0x9c, 0x5e, 0x3e, 0x8b, 0xa3, 0x0c, 0x9f, 0x3f, 0x43, 0xaf, + 0x93, 0x18, 0x3d, 0x0d, 0xd2, 0x65, 0x68, 0xdf, 0xe0, 0x73, 0x32, 0x8b, 0x09, 0x7a, 0xfb, 0x02, + 0xa7, 0x76, 0x94, 0xf6, 0x52, 0x92, 0x04, 0xfe, 0xc4, 0xb1, 0xdd, 0xcf, 0x33, 0x8a, 0x49, 0xe3, + 0x4b, 0x3b, 0xe9, 0xc2, 0x6a, 0x4a, 0x13, 0xba, 0xca, 0xfc, 0x1a, 0xd6, 0x86, 0x0f, 0x9b, 0xde, + 0x44, 0xd9, 0x9c, 0xa4, 0x41, 0x3a, 0x06, 0x24, 0x5a, 0xff, 0x70, 0xeb, 0xc4, 0xd7, 0xbd, 0x34, + 0xf8, 0x33, 0x88, 0x66, 0x63, 0x27, 0x4e, 0x3c, 0x92, 0xf4, 0x9c, 0xf8, 0x7a, 0xfd, 0x61, 0x1e, + 0x78, 0x1e, 0x89, 0x3e, 0xde, 0x7a, 0x6c, 0x5c, 0xda, 0xfa, 0x41, 0xb0, 0x00, 0xc6, 0x6e, 0x47, + 0xd9, 0x1a, 0x04, 0xcb, 0xed, 0x22, 0x88, 0x7a, 0x8c, 0x2c, 0xc7, 0x8a, 0x2c, 0x3f, 0x6c, 0x85, + 0x63, 0xed, 0xc4, 0xde, 0xcd, 0xed, 0xc2, 0x4e, 0x66, 0x41, 0x34, 0x96, 0x27, 0xd5, 0x57, 0x2e, + 0xe7, 0xb5, 0x1b, 0xde, 0xe5, 0x7c, 0x02, 0x12, 0xc3, 0x0f, 0xe3, 0xab, 0xde, 0xf5, 0x98, 0x01, + 0x21, 0xf6, 0x0b, 0x0b, 0x6e, 0x87, 0xbd, 0x19, 0xfc, 0x25, 0x51, 0xd6, 0x75, 0x83, 0xc4, 0x0d, + 0x09, 0xb2, 0x33, 0x34, 0x34, 0x1e, 0xa2, 0x9e, 0x62, 0x3c, 0xc4, 0x74, 0xc1, 0x07, 0x03, 0xac, + 0x0c, 0x0c, 0xac, 0x1a, 0x06, 0xee, 0x2b, 0xaa, 0x84, 0x29, 0x4b, 0x5f, 0xda, 0x09, 0x89, 0x32, + 0xa4, 0xa9, 0x09, 0x59, 0x48, 0x78, 0x73, 0x57, 0xaa, 0xfc, 0x10, 0x29, 0x8a, 0x9c, 0x77, 0x65, + 0x62, 0x55, 0x97, 0xb1, 0xa2, 0x99, 0xb8, 0x2f, 0x9b, 0xb5, 0xae, 0x0c, 0xda, 0x95, 0x30, 0xdb, + 0x55, 0x96, 0xc5, 0x11, 0x86, 0x05, 0xb7, 0x13, 0x62, 0xe3, 0x94, 0x84, 0xc4, 0xcd, 0x6e, 0x61, + 0xe9, 0xc7, 0x41, 0x34, 0x27, 0x49, 0x90, 0xf1, 0x9d, 0xe1, 0x57, 0xfc, 0x8d, 0x5b, 0xb6, 0xf6, + 0x63, 0x79, 0xe2, 0xae, 0x92, 0x34, 0x4e, 0xc6, 0xcb, 0x38, 0x88, 0x32, 0x92, 0xac, 0x83, 0x68, + 0xb9, 0xda, 0xe1, 0xfd, 0xb1, 0x1f, 0xbb, 0xab, 0xb4, 0x77, 0x19, 0xa4, 0x81, 0x13, 0x92, 0x62, + 0xfc, 0xda, 0x6d, 0x06, 0x4d, 0xf5, 0xe6, 0x6d, 0xbc, 0xca, 0x28, 0x2d, 0xa9, 0xcb, 0x6b, 0x94, + 0xc6, 0x61, 0xe0, 0x21, 0x3e, 0x9d, 0x70, 0x45, 0xa4, 0x09, 0x7f, 0xda, 0x8b, 0x7d, 0x3f, 0x25, + 0x19, 0x34, 0x62, 0x20, 0x7d, 0x6b, 0x27, 0xe9, 0xe5, 0xec, 0x96, 0xb2, 0xa9, 0x31, 0xa5, 0x1d, + 0x8e, 0x02, 0xf4, 0x37, 0x70, 0x1e, 0x8a, 0x6f, 0x13, 0xc6, 0xce, 0xc6, 0xee, 0x2a, 0x81, 0xd5, + 0x3e, 0x86, 0x79, 0x4f, 0x44, 0x16, 0x37, 0x56, 0xfa, 0xe6, 0x44, 0xe0, 0xa9, 0xae, 0xbd, 0x1c, + 0x53, 0x3c, 0x99, 0xd4, 0x18, 0x2d, 0xbb, 0xbb, 0xee, 0xa7, 0x97, 0xb3, 0x9e, 0x47, 0xfc, 0xf4, + 0x76, 0x19, 0xa7, 0x01, 0xc8, 0xf0, 0xb1, 0xed, 0xa4, 0x71, 0xb8, 0xca, 0xc8, 0x84, 0x75, 0x28, + 0xe7, 0x90, 0xc8, 0xeb, 0x7e, 0x9a, 0xf4, 0xe2, 0x28, 0xbc, 0xd9, 0xd8, 0x56, 0x29, 0xe1, 0x86, + 0x9f, 0x4b, 0xdb, 0xf3, 0x80, 0x8a, 0xe4, 0x09, 0xc7, 0xf6, 0x1e, 0xdc, 0xcd, 0xb1, 0x39, 0xc7, + 0x65, 0x37, 0x0c, 0x96, 0x63, 0x90, 0x6d, 0x5d, 0x19, 0xd3, 0xff, 0xa4, 0xc9, 0xd5, 0x3c, 0xc8, + 0x48, 0x2f, 0x5d, 0xda, 0x2e, 0x19, 0x47, 0xf1, 0x55, 0x62, 0x2f, 0x27, 0x39, 0x36, 0xac, 0xfb, + 0xe9, 0x9c, 0x84, 0x61, 0x41, 0x85, 0xb3, 0x24, 0xf0, 0x26, 0xf0, 0x4f, 0x2f, 0x23, 0x8b, 0x65, + 0x68, 0x67, 0x54, 0x8b, 0x5a, 0x2d, 0xa2, 0x74, 0xac, 0xaa, 0xfa, 0xf2, 0x1a, 0x2d, 0x82, 0x68, + 0x61, 0x5f, 0x77, 0x65, 0xac, 0xf8, 0x89, 0xb4, 0x03, 0xb5, 0xad, 0xfb, 0x69, 0xe0, 0x11, 0xc7, + 0x4e, 0xca, 0x59, 0xfa, 0xc1, 0x35, 0xf1, 0x26, 0x7f, 0xf6, 0x82, 0xc8, 0x23, 0xd7, 0x63, 0x55, + 0x9e, 0x04, 0x11, 0x6c, 0x9a, 0x8c, 0xec, 0x55, 0x16, 0x23, 0x19, 0xc9, 0x93, 0x7c, 0xdf, 0x74, + 0x61, 0xd6, 0xaa, 0xb9, 0xbc, 0x46, 0x0a, 0xfc, 0x43, 0xf7, 0x30, 0x07, 0xd8, 0x0f, 0xc9, 0xf5, + 0x04, 0xfe, 0xe9, 0x79, 0x01, 0xcc, 0x1a, 0x06, 0x60, 0x10, 0xf3, 0x39, 0xf6, 0x92, 0x7c, 0x01, + 0x39, 0xde, 0x50, 0x9a, 0x03, 0xba, 0xcd, 0xff, 0xd7, 0x97, 0x0d, 0x43, 0x12, 0x79, 0xc0, 0x77, + 0x20, 0x25, 0x64, 0x87, 0xc8, 0xf4, 0xa6, 0x97, 0xc4, 0xcb, 0x9e, 0x1f, 0x84, 0x19, 0x49, 0xc6, + 0x4e, 0xb8, 0x4a, 0xba, 0x30, 0xbe, 0xb4, 0xee, 0x3b, 0x89, 0x1d, 0x79, 0xb7, 0x15, 0x38, 0xec, + 0x30, 0x98, 0x45, 0xbd, 0x20, 0x23, 0x8b, 0x74, 0x0c, 0xca, 0x1d, 0x49, 0x26, 0x33, 0x7b, 0x39, + 0x56, 0x54, 0x61, 0x1e, 0xb0, 0x88, 0x0a, 0xf0, 0x6d, 0x8d, 0xa2, 0x22, 0xb0, 0xcc, 0x2b, 0xb6, + 0x60, 0x43, 0x5d, 0x9e, 0x84, 0x24, 0xcb, 0x48, 0x42, 0xf7, 0x0a, 0x1a, 0xf7, 0xfa, 0xb2, 0x46, + 0x16, 0x9c, 0xb1, 0x06, 0x7f, 0x92, 0xb1, 0x32, 0x5a, 0x5e, 0xf3, 0xa1, 0x51, 0xba, 0xb0, 0x85, + 0x9d, 0x73, 0xc2, 0xd8, 0xfd, 0xcc, 0x31, 0xa3, 0x97, 0xc5, 0xcb, 0x71, 0xd1, 0x3d, 0x7d, 0x71, + 0xb4, 0xbc, 0xae, 0x77, 0xde, 0x57, 0x06, 0x64, 0x51, 0x61, 0xe3, 0x54, 0xa4, 0x49, 0x13, 0x20, + 0x6e, 0xa6, 0xa3, 0xfa, 0x71, 0xb2, 0x18, 0xaf, 0x96, 0x4b, 0x92, 0xb8, 0x76, 0x4a, 0xf8, 0xc0, + 0xbd, 0x85, 0x9d, 0x7c, 0xe6, 0xb4, 0xa5, 0xc9, 0x25, 0x8e, 0xd2, 0xdf, 0xb0, 0x0e, 0x63, 0x50, + 0xb5, 0x60, 0x33, 0x4b, 0xe4, 0xe4, 0x64, 0x3c, 0xe1, 0xeb, 0x48, 0xd7, 0x94, 0x09, 0xb3, 0x2e, + 0x34, 0x1e, 0xe6, 0x72, 0xcc, 0x18, 0x62, 0x55, 0x53, 0xd8, 0xb6, 0x68, 0x92, 0x24, 0x8e, 0xd8, + 0x0f, 0xed, 0x64, 0x46, 0xf8, 0xb8, 0x86, 0x59, 0x8e, 0x4b, 0x7f, 0x6f, 0xe8, 0x18, 0x96, 0xbe, + 0xa5, 0x67, 0x13, 0xba, 0x8e, 0xec, 0xcb, 0x1a, 0xde, 0xdb, 0xcb, 0xf1, 0x10, 0xd6, 0x37, 0xb2, + 0x2f, 0x11, 0x67, 0x9b, 0x05, 0xd6, 0x26, 0x24, 0xb4, 0xb3, 0xe0, 0x92, 0x88, 0x28, 0xae, 0x8f, + 0x44, 0xb2, 0x44, 0x8a, 0x56, 0x47, 0xcc, 0x4d, 0x08, 0x01, 0x0d, 0x5b, 0x16, 0x3e, 0xc7, 0x58, + 0x26, 0xf4, 0x15, 0x40, 0x7f, 0x01, 0x2d, 0x05, 0xb9, 0xc0, 0xb6, 0x88, 0xf6, 0x3e, 0x0e, 0x89, + 0x9f, 0x4d, 0xe8, 0xb3, 0x20, 0xc7, 0xfe, 0x38, 0x41, 0x7d, 0x35, 0x45, 0xc4, 0x4e, 0x09, 0x2e, + 0x3b, 0x28, 0xef, 0x15, 0x7b, 0x5b, 0xdc, 0x12, 0xe7, 0x3c, 0x9e, 0xc3, 0xb6, 0xdd, 0x36, 0xe5, + 0xbb, 0x48, 0x22, 0x3e, 0xfd, 0x3f, 0xd9, 0x11, 0x5f, 0xec, 0xdb, 0x2e, 0xac, 0x50, 0xe5, 0x4d, + 0xaa, 0x14, 0x55, 0x5e, 0x05, 0x96, 0x69, 0x27, 0xa5, 0x58, 0x1c, 0xc9, 0x1e, 0x99, 0x61, 0xae, + 0x35, 0xa9, 0x4a, 0xfe, 0x4b, 0x1e, 0xc1, 0x7a, 0x5c, 0xe7, 0x2a, 0x0f, 0xe5, 0x11, 0x08, 0x76, + 0x53, 0x46, 0x62, 0xcf, 0x15, 0xc0, 0xf9, 0xf8, 0x25, 0xea, 0x52, 0x93, 0xb1, 0xdb, 0x1f, 0x99, + 0x95, 0x76, 0x28, 0x5d, 0xda, 0xd1, 0xad, 0x40, 0x54, 0x7a, 0x8d, 0x12, 0x07, 0x86, 0x5c, 0x69, + 0xef, 0x70, 0xfd, 0xa2, 0x07, 0x6b, 0x3d, 0xa6, 0x98, 0x0d, 0x58, 0xc0, 0xd9, 0x94, 0x40, 0x02, + 0xf4, 0x77, 0x89, 0x10, 0x03, 0x01, 0x1f, 0x28, 0x86, 0x2d, 0x43, 0xdb, 0x25, 0x55, 0x7c, 0xa8, + 0xed, 0xb9, 0x5c, 0x60, 0xc6, 0x77, 0x8a, 0x2a, 0x13, 0x59, 0x6e, 0x2a, 0x3d, 0x4c, 0x8d, 0x94, + 0x44, 0xae, 0x20, 0x03, 0xd6, 0x72, 0x46, 0xdb, 0xf3, 0x41, 0x07, 0x16, 0x18, 0x01, 0x85, 0x37, + 0x07, 0x4a, 0xa1, 0xfc, 0x07, 0xb8, 0xa1, 0x5c, 0x45, 0x55, 0xc0, 0x4b, 0x20, 0xa4, 0x16, 0x94, + 0x6d, 0xc1, 0x54, 0x61, 0x6c, 0x85, 0x8e, 0xcd, 0x4c, 0x5e, 0x2f, 0xce, 0x38, 0x81, 0x0a, 0xf4, + 0x69, 0x0a, 0xab, 0xc0, 0x2c, 0xc2, 0x1e, 0x63, 0x56, 0x55, 0x5e, 0x51, 0x5d, 0x08, 0xa3, 0x4d, + 0xdd, 0xcb, 0x67, 0x2e, 0xe0, 0x45, 0x41, 0xe4, 0xa2, 0x0c, 0x5f, 0xf7, 0xb9, 0x45, 0x4b, 0x3c, + 0x24, 0x80, 0x86, 0x85, 0xdf, 0x65, 0x8b, 0x3b, 0xd0, 0xb5, 0x8a, 0x6c, 0x09, 0xe1, 0xef, 0x05, + 0xd1, 0x6c, 0x63, 0xd7, 0x62, 0x23, 0xdc, 0x77, 0x56, 0xe9, 0xcd, 0xc6, 0xa6, 0xf0, 0xb0, 0x02, + 0x40, 0x31, 0xc5, 0x0d, 0x73, 0x5f, 0xf7, 0x63, 0xdf, 0x87, 0x35, 0xdc, 0xd8, 0x25, 0x7f, 0x5e, + 0xe9, 0x35, 0xa1, 0xdc, 0xa5, 0xde, 0x25, 0xdc, 0x5d, 0x2f, 0xec, 0x20, 0xba, 0xa5, 0x12, 0x9f, + 0x89, 0xcd, 0xb1, 0xca, 0xc5, 0xef, 0x22, 0x88, 0xba, 0xa0, 0x75, 0x63, 0x45, 0x03, 0xb4, 0x96, + 0x72, 0xad, 0x83, 0x6f, 0x57, 0x21, 0x97, 0x01, 0xa1, 0xdc, 0xd0, 0x5e, 0x2c, 0xbb, 0xf0, 0x13, + 0xeb, 0x97, 0x57, 0x78, 0x00, 0x12, 0x12, 0x19, 0x3a, 0x60, 0x46, 0x16, 0x2f, 0x41, 0xfa, 0x0b, + 0x5c, 0x73, 0xa8, 0xee, 0xc0, 0x26, 0x3f, 0xad, 0xd2, 0x2c, 0xf0, 0x6f, 0x7a, 0xdc, 0x6f, 0x31, + 0xa6, 0xda, 0x4b, 0xcf, 0x21, 0xd9, 0x15, 0x21, 0x11, 0x45, 0x56, 0x4a, 0x6b, 0x1c, 0xcd, 0x9d, + 0x38, 0xcb, 0xe2, 0x05, 0xd5, 0xee, 0xf2, 0x11, 0xd1, 0x5c, 0xc9, 0xad, 0x02, 0xc6, 0x35, 0x64, + 0x01, 0x69, 0x39, 0xbc, 0xc3, 0xe5, 0x35, 0xd6, 0x2e, 0xaf, 0xb0, 0x66, 0xc2, 0xfc, 0x28, 0x66, + 0xe6, 0xca, 0x4b, 0x8b, 0x24, 0xd6, 0x0d, 0xb2, 0x58, 0xf7, 0xc9, 0x0d, 0x71, 0x92, 0xf8, 0xaa, + 0xb4, 0x38, 0xb6, 0x52, 0x07, 0x15, 0xbb, 0x62, 0xbf, 0x7d, 0xb5, 0x2a, 0xf3, 0x07, 0x72, 0x53, + 0x2c, 0x9b, 0x64, 0xb1, 0x45, 0x06, 0x67, 0xf1, 0xb2, 0x67, 0x53, 0x45, 0x27, 0xdd, 0x49, 0xfb, + 0x60, 0x1a, 0x83, 0x0d, 0x63, 0xdc, 0xe0, 0x3e, 0xd8, 0x7f, 0x3d, 0x77, 0x1e, 0x2c, 0x6f, 0x73, + 0xa9, 0x6d, 0x6e, 0x96, 0x61, 0x9c, 0x5a, 0xb7, 0xf5, 0x0e, 0x6f, 0x73, 0x65, 0x52, 0xa9, 0xa9, + 0xe6, 0xf0, 0x6e, 0x43, 0xa6, 0x69, 0x55, 0x99, 0xf6, 0x9d, 0xec, 0x29, 0xb2, 0x62, 0x78, 0xca, + 0xdd, 0x4c, 0xa6, 0xba, 0x6e, 0x72, 0x73, 0xdd, 0x40, 0x55, 0x5a, 0x97, 0x13, 0xe4, 0x62, 0x8c, + 0x8f, 0xcf, 0x79, 0xaa, 0x36, 0xd2, 0x35, 0x43, 0x69, 0xda, 0xae, 0xeb, 0x7e, 0xea, 0x02, 0x89, + 0x57, 0xcc, 0xd1, 0x89, 0x1d, 0x81, 0x9f, 0x01, 0x84, 0x2a, 0x7b, 0xda, 0x0b, 0x22, 0xd4, 0x57, + 0xf5, 0x14, 0xb9, 0x2b, 0x27, 0x70, 0x7b, 0x0e, 0xf9, 0x33, 0x20, 0x49, 0xb7, 0xaf, 0xe2, 0xfe, + 0xd0, 0xc0, 0x7d, 0xd5, 0xc0, 0x4a, 0xd1, 0x51, 0x2e, 0x0b, 0x2b, 0xea, 0xd9, 0xfa, 0xbf, 0x7d, + 0x26, 0x37, 0x7e, 0x62, 0x2f, 0x48, 0x8a, 0x8a, 0x1e, 0x6f, 0xe5, 0x87, 0xb7, 0x31, 0xcc, 0x21, + 0xbb, 0x19, 0xcb, 0x93, 0x72, 0xcb, 0x99, 0x93, 0xd0, 0xce, 0xc8, 0x2f, 0x5d, 0x40, 0xcf, 0x75, + 0x16, 0x17, 0xcd, 0x14, 0xa1, 0x19, 0xb5, 0xb2, 0xd7, 0x7d, 0xd7, 0x4e, 0xbc, 0x16, 0x65, 0xa5, + 0xb1, 0x35, 0x2d, 0xda, 0xef, 0x50, 0xad, 0xef, 0x12, 0x67, 0x0f, 0xf4, 0x62, 0xab, 0xec, 0x56, + 0x74, 0x83, 0x0a, 0x6f, 0x45, 0x55, 0x06, 0x8a, 0xeb, 0xcb, 0xf8, 0x3b, 0xd9, 0x96, 0x3d, 0x45, + 0xf1, 0x8d, 0x0a, 0x93, 0x66, 0xfd, 0xb1, 0x0b, 0xa9, 0x6e, 0xb1, 0x30, 0xd8, 0xc7, 0xb6, 0x9f, + 0x51, 0xa5, 0x83, 0x91, 0x7b, 0xa7, 0x33, 0x69, 0x5a, 0x45, 0xdc, 0x64, 0xed, 0x91, 0x4b, 0x12, + 0x65, 0xcc, 0xbf, 0x90, 0xdb, 0x0e, 0xb5, 0x09, 0xe4, 0x96, 0xec, 0x36, 0xd0, 0x35, 0xaa, 0x77, + 0xb4, 0xac, 0x87, 0x6a, 0xd4, 0x6d, 0xf0, 0x87, 0x12, 0x03, 0xb2, 0x07, 0xde, 0x6d, 0xdc, 0x4f, + 0x99, 0x9d, 0x41, 0xaf, 0x6e, 0xbf, 0x9d, 0x91, 0x29, 0x03, 0xa0, 0x50, 0xe8, 0x1f, 0xcd, 0xb5, + 0x6a, 0xef, 0x68, 0xae, 0xe6, 0x8c, 0xc6, 0xe0, 0x4c, 0xac, 0xc9, 0x97, 0x54, 0xca, 0x97, 0xf8, + 0xfb, 0xa2, 0x5a, 0x63, 0x42, 0xbf, 0x73, 0x92, 0xc4, 0x22, 0xf7, 0x55, 0x07, 0x22, 0xc1, 0x73, + 0x46, 0x38, 0xe0, 0x8c, 0x5b, 0xd7, 0x80, 0x11, 0xee, 0x60, 0x0c, 0x2a, 0x7d, 0xd5, 0x4f, 0x50, + 0xdf, 0xf4, 0x93, 0xb6, 0x39, 0xef, 0xe6, 0x4f, 0x31, 0x1f, 0x22, 0xad, 0xcd, 0x07, 0xa2, 0xd4, + 0xd6, 0x5f, 0x31, 0xa9, 0x0f, 0x64, 0xc3, 0x06, 0x7e, 0xa7, 0x18, 0x8a, 0xab, 0xf8, 0xbe, 0x8d, + 0xc1, 0x6c, 0xb3, 0x65, 0xd7, 0xb7, 0x25, 0x36, 0xeb, 0xb1, 0x43, 0xfc, 0x38, 0x21, 0xdb, 0x71, + 0xaa, 0xc0, 0x9f, 0x02, 0xe4, 0x1e, 0xf5, 0x34, 0x37, 0xf0, 0xa5, 0xe9, 0xaa, 0x51, 0x0d, 0x09, + 0x29, 0xcb, 0xeb, 0x2a, 0xac, 0xcb, 0xeb, 0x26, 0xa4, 0xa3, 0x12, 0xd3, 0x76, 0xea, 0x40, 0x04, + 0x86, 0xee, 0x24, 0x95, 0xb1, 0xd4, 0x1a, 0x5e, 0xd8, 0xe9, 0xe7, 0x0d, 0x00, 0xb2, 0x51, 0x84, + 0xbe, 0xf0, 0x77, 0xb2, 0x2c, 0xf3, 0xb5, 0xe8, 0xb9, 0xf1, 0xf2, 0x06, 0xb3, 0x9f, 0xa0, 0x2b, + 0xa4, 0xb8, 0xbf, 0x20, 0xe9, 0xbc, 0x17, 0x27, 0x4e, 0x90, 0xb5, 0xf0, 0x8d, 0xc2, 0x3a, 0x17, + 0x5e, 0x17, 0x90, 0xd1, 0xa4, 0xfa, 0xe3, 0xc2, 0xbe, 0xe6, 0x3a, 0xf0, 0x80, 0x0a, 0xe3, 0xba, + 0x7c, 0xd5, 0x46, 0xcb, 0x6b, 0x3c, 0xb8, 0xbc, 0xc2, 0x20, 0xf1, 0xab, 0xf2, 0xb5, 0x3f, 0x32, + 0x5b, 0x30, 0x79, 0x40, 0x31, 0xb9, 0x18, 0x70, 0xba, 0x1c, 0x87, 0x76, 0x9a, 0x01, 0x37, 0x0f, + 0xbd, 0xbb, 0x05, 0x2e, 0x77, 0x78, 0xae, 0x82, 0xde, 0x22, 0x8e, 0x62, 0x4a, 0x67, 0xf8, 0xfc, + 0xd9, 0xab, 0x38, 0x8a, 0x7b, 0x6f, 0xc8, 0x6c, 0x15, 0xda, 0x09, 0x3e, 0x8e, 0xa3, 0x34, 0x0e, + 0xed, 0x14, 0x17, 0x2d, 0x1a, 0x9a, 0x6c, 0xb9, 0x44, 0xb7, 0x0c, 0xb3, 0x21, 0xfe, 0x31, 0x26, + 0x11, 0xa7, 0x04, 0xae, 0x24, 0x29, 0xe8, 0x00, 0xf5, 0x94, 0xa6, 0x06, 0xad, 0xeb, 0xa5, 0x52, + 0x42, 0x8d, 0x70, 0xb3, 0xda, 0x27, 0xf2, 0x82, 0x16, 0xa3, 0xd3, 0xa8, 0x35, 0x62, 0x96, 0xfd, + 0x56, 0x89, 0x68, 0xd6, 0x05, 0xa2, 0x21, 0xb7, 0xda, 0xf7, 0xd5, 0x7e, 0xb3, 0x24, 0x8e, 0x66, + 0x22, 0x8b, 0xd0, 0x60, 0x64, 0x01, 0x13, 0x72, 0x6e, 0x45, 0x27, 0xcd, 0xa9, 0x99, 0x3b, 0xa3, + 0x40, 0x9d, 0x9b, 0xd8, 0xe9, 0x12, 0xe2, 0x36, 0x09, 0x48, 0xc5, 0xb1, 0xb2, 0xa3, 0x71, 0x53, + 0x17, 0x3f, 0x22, 0x11, 0xa8, 0x0d, 0xe1, 0x43, 0x35, 0xff, 0xaa, 0x82, 0x3f, 0x00, 0x7f, 0x09, + 0xb7, 0x0b, 0x95, 0x21, 0x66, 0xb6, 0x20, 0x3c, 0xd0, 0x85, 0x07, 0xb2, 0x2f, 0xce, 0x84, 0x13, + 0xbf, 0x88, 0xe6, 0xbb, 0x88, 0x98, 0x36, 0x50, 0xee, 0x9a, 0x82, 0x02, 0xd6, 0x65, 0x63, 0xe4, + 0x5b, 0xc6, 0x59, 0x54, 0xb9, 0xba, 0xc2, 0x1c, 0x08, 0xf6, 0x50, 0x37, 0x6b, 0xda, 0x10, 0x9f, + 0xa2, 0x2a, 0xbe, 0xc1, 0xac, 0x55, 0xbe, 0x09, 0xa2, 0x4b, 0x10, 0x7e, 0xe7, 0x74, 0xaa, 0xec, + 0x62, 0x3e, 0xe5, 0xc6, 0x4d, 0x75, 0x71, 0x41, 0x69, 0xae, 0x99, 0xd4, 0xc2, 0xe0, 0xc1, 0x46, + 0xef, 0xe4, 0xa0, 0x04, 0x65, 0x50, 0x28, 0x81, 0x5b, 0xc7, 0x67, 0xbe, 0xda, 0x36, 0xe3, 0x4d, + 0x78, 0x5e, 0x1d, 0x7d, 0x1c, 0x65, 0x73, 0x46, 0xff, 0x5d, 0x45, 0xba, 0x05, 0x8a, 0x52, 0x0c, + 0xea, 0xc9, 0xf2, 0xb3, 0xf1, 0xd0, 0xac, 0x2e, 0x6d, 0xa5, 0xb5, 0x2a, 0xdd, 0x26, 0xa5, 0xc7, + 0x97, 0xdb, 0x07, 0x5a, 0x4d, 0xfb, 0xbc, 0xdb, 0xaa, 0xac, 0xd9, 0x5e, 0x1b, 0x06, 0xd3, 0xa4, + 0x5b, 0x0a, 0x11, 0xd3, 0x6e, 0xd9, 0x58, 0x86, 0xde, 0x36, 0x16, 0x0b, 0xf8, 0x6c, 0x1b, 0x8b, + 0xb7, 0x58, 0xf7, 0x3d, 0x3b, 0x9d, 0x3b, 0x31, 0x68, 0x1d, 0x40, 0x5d, 0xa2, 0x41, 0xaf, 0x98, + 0x75, 0xbf, 0xc2, 0x06, 0x21, 0xed, 0x27, 0x48, 0xf1, 0xb9, 0xcb, 0x89, 0x2e, 0x56, 0x44, 0xb2, + 0xab, 0x38, 0xf9, 0xdc, 0x73, 0xec, 0x28, 0x22, 0x49, 0x2d, 0xbe, 0x52, 0xe5, 0x5c, 0x8a, 0xa8, + 0x26, 0xd0, 0x68, 0x53, 0xc5, 0xef, 0xba, 0x65, 0x58, 0xc0, 0xea, 0xaa, 0x9f, 0x98, 0x59, 0x8c, + 0xb9, 0xae, 0xd3, 0xaa, 0x31, 0x54, 0xb4, 0x74, 0x16, 0x31, 0xd3, 0xb4, 0xad, 0xfa, 0x9b, 0xca, + 0xc5, 0xbf, 0xae, 0x7a, 0xba, 0xe6, 0xba, 0x54, 0xf5, 0xf4, 0x15, 0xc3, 0x37, 0xa4, 0xfa, 0x3c, + 0x41, 0x2d, 0xaa, 0x68, 0x51, 0x7a, 0x73, 0x2d, 0xd0, 0x72, 0x1c, 0xc5, 0x59, 0x37, 0x37, 0xef, + 0xa4, 0x1d, 0xed, 0x3b, 0x4a, 0x84, 0x55, 0x03, 0x4f, 0x37, 0xca, 0xbe, 0x21, 0x8a, 0xcd, 0x49, + 0x57, 0x17, 0x3c, 0x22, 0xba, 0x79, 0x7f, 0xc7, 0xd0, 0xa0, 0xe6, 0x32, 0xe4, 0xc4, 0x24, 0x32, + 0x0f, 0xba, 0x6a, 0x8a, 0xdf, 0x74, 0x9b, 0x51, 0x14, 0x33, 0x29, 0xab, 0x64, 0x6d, 0xf4, 0x75, + 0xdf, 0x27, 0x76, 0xb6, 0x4a, 0x48, 0x8f, 0x1a, 0x0e, 0xa2, 0x7e, 0xa8, 0x88, 0x3e, 0x2c, 0xaa, + 0x74, 0xdc, 0xed, 0x6b, 0xaf, 0x76, 0xb7, 0x93, 0xe2, 0x45, 0x09, 0x46, 0x9e, 0x50, 0x21, 0x29, + 0x90, 0x28, 0xfd, 0xcd, 0x7d, 0xcb, 0x0d, 0xae, 0x22, 0x23, 0x0d, 0x1c, 0xe7, 0x54, 0xf9, 0x68, + 0x90, 0x96, 0xed, 0xc2, 0xc2, 0x35, 0x49, 0x6b, 0x50, 0x90, 0x16, 0x6f, 0xb1, 0xee, 0xb3, 0x1f, + 0x9c, 0xac, 0x6f, 0xf3, 0x07, 0xe3, 0x3a, 0xb5, 0xf3, 0x66, 0x8c, 0x22, 0x1b, 0xcd, 0x0a, 0x42, + 0x75, 0xe3, 0x55, 0x94, 0xdd, 0x96, 0x0e, 0x41, 0xcd, 0xac, 0xf9, 0xc4, 0xef, 0x2d, 0x25, 0x59, + 0x00, 0x7b, 0x11, 0x5c, 0x77, 0x83, 0x08, 0xa5, 0xc9, 0xcc, 0xc1, 0x95, 0x09, 0x54, 0x14, 0xc6, + 0x81, 0xf1, 0xb0, 0x61, 0x64, 0x2b, 0x35, 0x5c, 0x29, 0x96, 0xa6, 0x5c, 0xb3, 0xfb, 0x0c, 0x31, + 0x52, 0x1e, 0x4a, 0x75, 0xaf, 0x05, 0xb8, 0x1b, 0x67, 0x91, 0x1d, 0x42, 0x96, 0xcb, 0x16, 0x3f, + 0x04, 0xd5, 0x9b, 0xec, 0x25, 0xdd, 0x4b, 0x31, 0x04, 0xd7, 0x02, 0x5d, 0xa5, 0x47, 0x14, 0xdc, + 0x6e, 0x42, 0x02, 0xb5, 0xca, 0x57, 0x2b, 0xb1, 0xbb, 0xda, 0xde, 0x0f, 0x1b, 0x7e, 0xc1, 0xca, + 0x10, 0x55, 0xe1, 0x92, 0x7b, 0xa7, 0x98, 0x2b, 0xb5, 0xbd, 0x99, 0x5a, 0x34, 0x63, 0x9a, 0xd3, + 0xa6, 0x76, 0x5a, 0xd1, 0x8e, 0xc9, 0xff, 0x65, 0x42, 0x20, 0x0f, 0xa5, 0x70, 0x48, 0x31, 0x8f, + 0x54, 0x0b, 0x1f, 0xcf, 0x95, 0x41, 0xfe, 0x42, 0x8b, 0xc6, 0x56, 0x7d, 0x0c, 0xaa, 0xc1, 0x76, + 0x06, 0xa5, 0xb6, 0x30, 0xa8, 0xb6, 0x88, 0x60, 0x3d, 0x8e, 0x48, 0x1d, 0x50, 0xc5, 0x4d, 0x12, + 0x86, 0xc1, 0x32, 0x0d, 0xd2, 0x62, 0xe8, 0x3e, 0x59, 0x2c, 0xb3, 0x9b, 0xbb, 0xc7, 0x5e, 0xf7, + 0x59, 0xa8, 0x82, 0x72, 0x8b, 0x6d, 0x86, 0x46, 0x25, 0xae, 0x52, 0xd3, 0xa2, 0xab, 0x1e, 0x6c, + 0x99, 0x5b, 0xc2, 0x15, 0xa4, 0xdb, 0x6e, 0x66, 0x6f, 0xb7, 0x51, 0xc5, 0xc0, 0xca, 0xa6, 0xac, + 0x86, 0x4d, 0x5e, 0xa8, 0xca, 0xfc, 0x9a, 0xfb, 0xc1, 0x70, 0x1b, 0x57, 0xf4, 0xab, 0x4a, 0x97, + 0xc0, 0xd6, 0x85, 0xe0, 0x4d, 0x25, 0x32, 0x53, 0xe9, 0x9b, 0xb9, 0xb3, 0xd8, 0x08, 0x2d, 0x4e, + 0xa2, 0xae, 0x46, 0x83, 0x92, 0x40, 0x23, 0xa0, 0xed, 0x27, 0xc1, 0xf2, 0xb6, 0xc5, 0x5e, 0x11, + 0x05, 0x7e, 0x25, 0x2a, 0x49, 0x31, 0xb2, 0xc2, 0xec, 0xb7, 0xe9, 0x15, 0x06, 0x6c, 0x01, 0xd5, + 0x2e, 0x46, 0xf2, 0x72, 0xa3, 0x6b, 0x50, 0xa5, 0x62, 0x56, 0x80, 0x68, 0xea, 0x05, 0x97, 0x63, + 0x3f, 0x48, 0x0a, 0x93, 0x6e, 0x87, 0xb1, 0x68, 0x4c, 0x58, 0xa9, 0xb9, 0x19, 0x20, 0x9d, 0x0c, + 0x70, 0x3a, 0x67, 0x2d, 0xc8, 0xac, 0x8d, 0x84, 0x0a, 0x37, 0x6d, 0xdb, 0x22, 0x54, 0x7c, 0xc6, + 0x46, 0xfd, 0xd5, 0x06, 0xc9, 0xa9, 0x5a, 0x33, 0x76, 0x0a, 0xee, 0x60, 0x30, 0xae, 0x2a, 0x2f, + 0xde, 0x4d, 0x8c, 0x72, 0x39, 0xd8, 0x82, 0x80, 0x1d, 0xb0, 0x51, 0x81, 0xae, 0xfb, 0x49, 0x55, + 0x45, 0x35, 0x35, 0xb5, 0xe9, 0x3b, 0x13, 0x3a, 0x43, 0x41, 0x2d, 0x18, 0x5c, 0xcb, 0x38, 0x60, + 0x29, 0x34, 0xf7, 0xf5, 0x93, 0x31, 0xb7, 0x42, 0x19, 0x30, 0xc0, 0x15, 0x21, 0x89, 0xab, 0x88, + 0x5d, 0x97, 0xbd, 0x46, 0xcd, 0xb4, 0x10, 0x51, 0x9d, 0x42, 0x87, 0xfa, 0x7a, 0x1e, 0x80, 0x2c, + 0x66, 0x62, 0xdf, 0x6e, 0xa5, 0x6c, 0xaa, 0x80, 0xe4, 0x72, 0x85, 0xb2, 0xd0, 0xe6, 0x9a, 0x8b, + 0xda, 0x33, 0x75, 0xc4, 0x06, 0xd9, 0xcd, 0x1d, 0xf4, 0xa0, 0xca, 0x46, 0x1b, 0x3d, 0xb4, 0xf5, + 0x84, 0x5c, 0x3b, 0xba, 0xb4, 0xd3, 0xdc, 0x26, 0x83, 0x65, 0x2d, 0x88, 0x4a, 0x0c, 0xf9, 0x89, + 0x21, 0x79, 0xca, 0x0c, 0x91, 0xcc, 0x30, 0xb5, 0xa5, 0xcb, 0xfe, 0x1f, 0xab, 0x80, 0x64, 0x98, + 0xaf, 0x42, 0x7e, 0x9b, 0xaa, 0x53, 0xfc, 0xd9, 0x2e, 0xa8, 0x55, 0x76, 0x3c, 0x27, 0x76, 0x98, + 0xcd, 0x6f, 0xb7, 0xf8, 0x2c, 0xb6, 0x10, 0x5d, 0x42, 0x96, 0xc4, 0xce, 0xba, 0x3a, 0x4b, 0xf2, + 0xc8, 0x53, 0x18, 0xc0, 0x95, 0x00, 0x9d, 0xf6, 0xb2, 0x20, 0x24, 0x15, 0xdb, 0xa1, 0xa2, 0x32, + 0x36, 0xa5, 0x1a, 0x23, 0xdd, 0x7c, 0xff, 0x04, 0x95, 0xa0, 0xd2, 0x23, 0xf7, 0x7a, 0x54, 0xa9, + 0x93, 0x72, 0xfe, 0x3b, 0x23, 0x2c, 0x77, 0x46, 0x54, 0x54, 0xe6, 0x08, 0x11, 0xc6, 0x6a, 0x10, + 0xb9, 0xbe, 0x89, 0xc8, 0xbf, 0x45, 0x58, 0x56, 0x86, 0xbc, 0x93, 0x3d, 0xc0, 0x54, 0xbe, 0x61, + 0x30, 0x01, 0xab, 0x22, 0x3b, 0xbc, 0xc9, 0x02, 0x37, 0xfd, 0xca, 0xfd, 0x57, 0xfa, 0x8a, 0x41, + 0xdd, 0xbb, 0x46, 0xc5, 0x7a, 0x2c, 0xba, 0x6d, 0x1a, 0x0d, 0x95, 0x28, 0x92, 0x40, 0x3c, 0xd5, + 0x57, 0xb6, 0x10, 0x8e, 0x66, 0x6e, 0x24, 0x1c, 0xbd, 0xb4, 0xd9, 0xb8, 0xbe, 0x45, 0xc3, 0xfa, + 0xb7, 0xf7, 0x90, 0xff, 0x77, 0x2d, 0x3b, 0x58, 0x6c, 0x76, 0xe2, 0xdc, 0x30, 0x6d, 0x56, 0xb4, + 0x82, 0x87, 0x6d, 0x2a, 0x1a, 0x23, 0xb6, 0xf2, 0x95, 0x5d, 0x04, 0x19, 0x37, 0x88, 0x41, 0x60, + 0x32, 0x9b, 0x98, 0xdf, 0xa0, 0xc4, 0x83, 0x95, 0xbe, 0x6a, 0x54, 0x0d, 0x65, 0xa5, 0x55, 0xb0, + 0x8a, 0xa3, 0xe6, 0x48, 0x5c, 0x5a, 0x1e, 0xf2, 0x8e, 0xb8, 0xd2, 0x86, 0x65, 0x75, 0x3f, 0xa8, + 0x30, 0x4e, 0x1f, 0xb2, 0x0a, 0xb2, 0xc4, 0x76, 0x3f, 0x0b, 0x4a, 0x72, 0x63, 0xa8, 0xaa, 0x64, + 0xa9, 0x7b, 0xb6, 0x54, 0x59, 0x1d, 0x6a, 0xca, 0x86, 0x6e, 0x1b, 0x92, 0xeb, 0x2f, 0x93, 0x58, + 0xd4, 0x4a, 0xde, 0x2e, 0xa4, 0x46, 0xa5, 0x0b, 0x51, 0x97, 0xab, 0xeb, 0x0b, 0x14, 0x5b, 0xae, + 0xee, 0xa0, 0x5d, 0xd6, 0xd4, 0x08, 0x58, 0xc8, 0xcc, 0xa1, 0x0e, 0x28, 0xea, 0x34, 0x4a, 0xed, + 0x19, 0x49, 0x7b, 0xa1, 0x7d, 0x13, 0xaf, 0xb2, 0x7c, 0x0d, 0x5d, 0x3b, 0x74, 0xbb, 0x34, 0x6b, + 0x0e, 0xf5, 0x90, 0xa2, 0x53, 0x17, 0xb9, 0x40, 0x52, 0xc6, 0x40, 0xde, 0x4d, 0x1b, 0xe3, 0x78, + 0xa4, 0x82, 0xa9, 0x89, 0x35, 0xe5, 0xa1, 0x04, 0xea, 0xd2, 0xba, 0x9f, 0xcd, 0x13, 0x62, 0x7b, + 0x29, 0xcb, 0x6c, 0xbe, 0xad, 0x44, 0xeb, 0xa9, 0x0b, 0x6d, 0x43, 0x76, 0x9c, 0x18, 0xba, 0x6d, + 0x28, 0x1b, 0x95, 0x3e, 0x51, 0x35, 0x0e, 0x56, 0x86, 0x90, 0x61, 0x41, 0x15, 0x6a, 0x46, 0x6d, + 0x69, 0xbf, 0x8b, 0xb6, 0xa4, 0x08, 0x7d, 0xd4, 0x57, 0xed, 0x21, 0xea, 0x51, 0xb7, 0x70, 0x09, + 0x65, 0xef, 0x86, 0x49, 0x8b, 0xd4, 0x4d, 0xe2, 0x30, 0x04, 0xdc, 0x62, 0xdb, 0x96, 0xcd, 0x83, + 0x02, 0x74, 0x91, 0xf3, 0x14, 0xf2, 0x4a, 0xe5, 0x19, 0x79, 0xbb, 0x2c, 0x36, 0x84, 0x34, 0x36, + 0xfa, 0xb6, 0x94, 0xad, 0xbe, 0xad, 0x0d, 0x11, 0xf0, 0x2d, 0x59, 0x5d, 0x39, 0xd4, 0x79, 0x24, + 0xbb, 0x99, 0x7e, 0x65, 0xe7, 0x4d, 0xf2, 0x70, 0x73, 0x8b, 0x3f, 0xc8, 0xcd, 0xdb, 0xa0, 0xbe, + 0x7d, 0x69, 0x67, 0x76, 0x92, 0x5b, 0xd6, 0x23, 0xc1, 0x4d, 0x31, 0x2a, 0x97, 0x9a, 0x86, 0x5e, + 0x2a, 0x4c, 0xa5, 0xc1, 0xff, 0xb4, 0x5a, 0x6b, 0xce, 0x89, 0x70, 0xf5, 0x1e, 0x15, 0xe7, 0xdf, + 0x20, 0xcf, 0x9a, 0x03, 0xdc, 0x36, 0x6c, 0xcb, 0xfa, 0x78, 0x05, 0x08, 0x59, 0xb0, 0x20, 0xbb, + 0x68, 0x4c, 0xbc, 0xb9, 0x63, 0x7b, 0x33, 0x72, 0xbb, 0x53, 0x52, 0x97, 0xf1, 0x6d, 0x49, 0x5d, + 0x86, 0x22, 0xef, 0x96, 0xd4, 0x55, 0x57, 0x6c, 0x4c, 0x30, 0x36, 0xf9, 0x0e, 0xde, 0x35, 0xfe, + 0xb6, 0xcc, 0xaa, 0x46, 0xf2, 0xe1, 0x46, 0x4f, 0xa2, 0x5b, 0xf7, 0x23, 0x34, 0xc0, 0x71, 0xe3, + 0xe8, 0x92, 0x24, 0x29, 0xcd, 0x91, 0xd8, 0x82, 0x32, 0x15, 0x52, 0x4a, 0xe2, 0xab, 0xd2, 0xac, + 0xa3, 0x00, 0x56, 0xfb, 0x61, 0xec, 0x44, 0xcc, 0x17, 0xd2, 0x44, 0xed, 0x52, 0x6f, 0xcd, 0x00, + 0xde, 0x96, 0x79, 0x5b, 0x9c, 0x3a, 0xa0, 0x1a, 0x65, 0x2b, 0xab, 0x6b, 0x81, 0xa0, 0x46, 0x2d, + 0xba, 0x80, 0x10, 0x3a, 0xc5, 0x9c, 0xe6, 0x1b, 0x60, 0xde, 0x8a, 0x01, 0xcb, 0x3b, 0x49, 0xa8, + 0xd9, 0x05, 0xc7, 0xf3, 0xaf, 0xa6, 0x9a, 0x49, 0x3d, 0xb6, 0xd7, 0x32, 0xc4, 0xdd, 0xc1, 0xc5, + 0x11, 0x0f, 0xac, 0x80, 0xd8, 0xea, 0x85, 0x41, 0x9a, 0xdd, 0x8a, 0xaa, 0xdd, 0x2e, 0xb9, 0xd7, + 0xb9, 0x96, 0x54, 0xe7, 0xce, 0xeb, 0xfe, 0x15, 0x09, 0xdd, 0x78, 0x41, 0x7a, 0xce, 0xca, 0x81, + 0x13, 0x03, 0xcc, 0x8f, 0x44, 0xe3, 0x93, 0x24, 0x77, 0x8f, 0x6d, 0x52, 0xfa, 0x05, 0xf6, 0x98, + 0xe3, 0xf9, 0x36, 0x3f, 0x97, 0x61, 0xac, 0xfb, 0x6c, 0x94, 0xdb, 0x32, 0x52, 0x0d, 0x59, 0x6d, + 0x43, 0xf3, 0x21, 0x36, 0x4c, 0x2a, 0x3c, 0x0a, 0xb4, 0x02, 0xb4, 0xa0, 0xb8, 0xb5, 0xc9, 0x27, + 0x37, 0x11, 0xa2, 0xc1, 0x74, 0xce, 0x69, 0x66, 0x27, 0xd9, 0xbd, 0x72, 0x9f, 0x86, 0x5c, 0x30, + 0xb2, 0x7f, 0x8c, 0x9a, 0x86, 0xa4, 0x18, 0x8a, 0xad, 0x2a, 0x39, 0xc8, 0x7d, 0xd0, 0x14, 0xea, + 0x43, 0x82, 0xe9, 0x54, 0x8b, 0x94, 0x50, 0xbd, 0x05, 0x22, 0x25, 0x1b, 0x86, 0x6a, 0x8c, 0x32, + 0xd2, 0x35, 0xd5, 0xb5, 0x07, 0xf9, 0x38, 0x68, 0x59, 0x46, 0x3a, 0xaa, 0xb8, 0xd3, 0xf4, 0x1a, + 0x16, 0x7b, 0x09, 0x08, 0x39, 0xb6, 0xa3, 0x9b, 0xab, 0x39, 0x49, 0x48, 0x45, 0x99, 0x5c, 0x26, + 0x84, 0x3e, 0x2d, 0xba, 0xdf, 0x01, 0xdb, 0x18, 0x23, 0x2e, 0xa7, 0xcd, 0xdf, 0x69, 0x2a, 0x52, + 0xbc, 0x89, 0x6f, 0x07, 0x21, 0x69, 0xc3, 0x63, 0x9a, 0xcf, 0xd8, 0x77, 0xe3, 0xc5, 0x32, 0x4e, + 0x49, 0x52, 0xe0, 0x2c, 0x4c, 0x86, 0x85, 0x01, 0xa8, 0xbe, 0x0d, 0x49, 0x8a, 0x24, 0xba, 0xec, + 0xd2, 0x13, 0x8a, 0x70, 0x86, 0xa5, 0x47, 0x83, 0x24, 0x9c, 0x3f, 0x48, 0xc5, 0xae, 0x51, 0xf5, + 0xbf, 0x7d, 0x5b, 0xc5, 0xfc, 0xb5, 0xa1, 0x3c, 0x92, 0x5d, 0x47, 0x2b, 0xc7, 0x45, 0xfc, 0x48, + 0x4e, 0xd3, 0xc8, 0x11, 0xdd, 0x9a, 0x9c, 0x1b, 0x0d, 0xab, 0xc9, 0x77, 0x72, 0x99, 0x4d, 0x27, + 0xdf, 0xe5, 0xd4, 0xcf, 0x73, 0xc6, 0xc5, 0xcd, 0x55, 0x94, 0x81, 0xe2, 0x35, 0x3c, 0xb0, 0x1c, + 0x32, 0xe0, 0xbc, 0xb7, 0x3b, 0x86, 0x0d, 0x21, 0x11, 0x28, 0xcf, 0xef, 0x13, 0xa6, 0x96, 0x1f, + 0xfb, 0x11, 0x27, 0xd7, 0xf4, 0xdb, 0x5e, 0x6f, 0x70, 0x0c, 0x50, 0x1a, 0x83, 0x8e, 0x13, 0x42, + 0xa1, 0xa3, 0x09, 0x5c, 0xf7, 0x21, 0x20, 0xe8, 0x3e, 0x3f, 0x08, 0x24, 0x37, 0x67, 0xee, 0x6e, + 0xe3, 0x07, 0xaa, 0xdc, 0x3a, 0x13, 0x76, 0xcc, 0xe8, 0xb6, 0x2d, 0xfe, 0x38, 0x1a, 0xad, 0xfb, + 0x29, 0x89, 0xbc, 0x1e, 0x3f, 0x0a, 0xc0, 0x05, 0x81, 0x2e, 0x08, 0x02, 0xfd, 0xfe, 0xba, 0x80, + 0x5e, 0xea, 0x02, 0xb2, 0xa6, 0x68, 0xb2, 0xbd, 0x6b, 0x9c, 0x9e, 0x07, 0xf0, 0x38, 0xc1, 0x57, + 0x40, 0xe3, 0x0a, 0x62, 0x7e, 0x48, 0x85, 0x92, 0x4b, 0x44, 0xd2, 0xb4, 0xab, 0xc0, 0x81, 0xb2, + 0x6a, 0x53, 0x2f, 0x48, 0xe1, 0x5c, 0xb3, 0x57, 0x24, 0x13, 0xf6, 0x35, 0x23, 0x3f, 0x13, 0x16, + 0xc5, 0x40, 0x71, 0x61, 0x7c, 0x45, 0xbc, 0x72, 0xa9, 0xa6, 0x9b, 0xcf, 0x99, 0xd0, 0x70, 0x2a, + 0x0b, 0xc7, 0xdd, 0x49, 0xe1, 0x2d, 0x86, 0xd1, 0x12, 0xc4, 0x0b, 0x93, 0xf6, 0x62, 0x74, 0x96, + 0x87, 0x4c, 0xf2, 0x87, 0x90, 0x76, 0x24, 0xf8, 0x69, 0xcc, 0xea, 0xc3, 0x66, 0xd8, 0xb6, 0xc8, + 0x49, 0x32, 0xcb, 0x88, 0x36, 0xd0, 0x19, 0x8d, 0x19, 0xdc, 0x37, 0x98, 0x0b, 0x32, 0x04, 0x82, + 0xb8, 0x7f, 0x09, 0x0e, 0xec, 0x9e, 0x21, 0xbb, 0x9d, 0xce, 0xd9, 0x7d, 0x6a, 0x54, 0x49, 0x15, + 0xf0, 0x5a, 0x4e, 0x6e, 0x14, 0xb8, 0xd4, 0x94, 0x1a, 0xba, 0xb7, 0xee, 0x2f, 0xec, 0x65, 0x2f, + 0xbd, 0x0a, 0x32, 0x77, 0xce, 0xa7, 0x06, 0xeb, 0xc7, 0x7d, 0x28, 0xc5, 0x31, 0x33, 0x93, 0x45, + 0xab, 0xf4, 0xea, 0x29, 0xa6, 0x66, 0xf2, 0x93, 0x76, 0xcf, 0x5c, 0x60, 0xb5, 0x2d, 0x43, 0x22, + 0x9f, 0x56, 0x09, 0x59, 0x7e, 0x14, 0x47, 0x60, 0x34, 0xf5, 0xa4, 0x65, 0xbd, 0xe1, 0x3f, 0x1f, + 0xdd, 0xb9, 0x8a, 0xa2, 0xe1, 0x55, 0x43, 0xd5, 0x8a, 0x3f, 0x51, 0x95, 0x5b, 0x80, 0xa9, 0x1e, + 0x75, 0xf9, 0x06, 0x4a, 0x56, 0x47, 0x85, 0xcb, 0x81, 0xa6, 0x79, 0xec, 0xee, 0x9d, 0x55, 0x71, + 0xc5, 0x0e, 0xe5, 0x7e, 0x5a, 0x43, 0xf0, 0xdd, 0xd4, 0xbd, 0x74, 0x8a, 0x5a, 0xcf, 0xe9, 0xd8, + 0x29, 0x9d, 0x43, 0xe5, 0xc1, 0x9e, 0xd2, 0xd0, 0xd5, 0x6a, 0x86, 0x2e, 0x55, 0x85, 0x2a, 0xc3, + 0xd6, 0xb5, 0x67, 0xe1, 0x08, 0xa6, 0x5e, 0x8f, 0xff, 0x16, 0x39, 0x31, 0x15, 0xfb, 0x16, 0xce, + 0x43, 0x2b, 0x7e, 0xb5, 0xd3, 0x32, 0x8f, 0x83, 0x32, 0x1d, 0x7a, 0xbe, 0xa6, 0x76, 0x8c, 0xe7, + 0x1b, 0xec, 0x4d, 0x71, 0xa4, 0xe5, 0xd7, 0xe7, 0x7f, 0x18, 0xd5, 0x9e, 0x1a, 0x86, 0x68, 0x3e, + 0xdd, 0xba, 0x02, 0x54, 0x59, 0x3d, 0x20, 0x41, 0xdb, 0x2d, 0xe2, 0xa6, 0x95, 0xf3, 0x19, 0x8d, + 0xd0, 0x05, 0x65, 0x41, 0x95, 0xcc, 0x3d, 0xa6, 0x9b, 0x56, 0x23, 0xa6, 0xdb, 0x6d, 0xbe, 0x0d, + 0xd4, 0xd0, 0x1a, 0xf1, 0xa4, 0x59, 0x50, 0x40, 0x12, 0x75, 0x04, 0xd3, 0xdb, 0xce, 0x64, 0xd4, + 0x35, 0x50, 0x79, 0x28, 0x3b, 0x8a, 0xbc, 0xfe, 0x2e, 0xef, 0xa5, 0x4d, 0x51, 0xaa, 0xb8, 0x63, + 0x58, 0xea, 0x3c, 0xa5, 0x41, 0x66, 0xa6, 0x37, 0x93, 0x49, 0xca, 0x28, 0x32, 0xd5, 0xda, 0x00, + 0x2f, 0xb8, 0x63, 0x6b, 0x20, 0x2a, 0x20, 0x34, 0x6c, 0xa9, 0xb6, 0x29, 0xf8, 0x6a, 0x1b, 0xeb, + 0x6a, 0x9c, 0x1b, 0x54, 0xef, 0x62, 0x61, 0x50, 0x61, 0xc0, 0x94, 0x5d, 0x4f, 0x69, 0x3f, 0x39, + 0x4a, 0xf3, 0xf8, 0xcb, 0x89, 0xe4, 0xd6, 0x5e, 0x0b, 0xbf, 0xae, 0x7b, 0xc1, 0x84, 0x77, 0x76, + 0x09, 0x23, 0xb0, 0x17, 0x58, 0x88, 0x7e, 0xdb, 0x6a, 0xe5, 0xd9, 0xcf, 0x2d, 0xf2, 0x2b, 0x77, + 0xa4, 0xd7, 0xd2, 0xd8, 0x1b, 0x26, 0xb6, 0xa8, 0xc1, 0x6a, 0x35, 0x39, 0xcf, 0x9b, 0xb5, 0xe4, + 0xed, 0x0b, 0x00, 0xb6, 0xf8, 0x76, 0x86, 0x95, 0x19, 0x70, 0x3f, 0x6c, 0x21, 0xd8, 0x19, 0x96, + 0x7d, 0x8d, 0x24, 0xe7, 0x24, 0x96, 0xae, 0x1c, 0x51, 0xeb, 0xc8, 0x23, 0x95, 0xf5, 0xe7, 0x2d, + 0xc9, 0xf7, 0x82, 0x1e, 0xa2, 0x96, 0xa1, 0xdc, 0x5d, 0x19, 0x77, 0xc9, 0xaf, 0xfb, 0xba, 0x21, + 0x84, 0x00, 0x54, 0x70, 0x64, 0xc2, 0x19, 0x92, 0x22, 0xda, 0x36, 0x10, 0x82, 0x1d, 0x94, 0xc8, + 0x68, 0x87, 0x49, 0x7c, 0x05, 0x71, 0x8d, 0x08, 0xa9, 0x25, 0x13, 0xa7, 0x70, 0x87, 0xc1, 0x25, + 0xe9, 0x2d, 0x83, 0xb0, 0xf4, 0xf3, 0x1a, 0xdc, 0x92, 0x6d, 0x3a, 0xe5, 0xb7, 0x9f, 0x76, 0xcb, + 0x33, 0x50, 0xed, 0x2d, 0xa9, 0xc6, 0x66, 0xdb, 0xd9, 0x1b, 0x13, 0x22, 0x6c, 0x1c, 0x62, 0xf0, + 0xa8, 0x81, 0xf9, 0x9a, 0x67, 0xbb, 0x30, 0xfd, 0xc1, 0xd8, 0xe6, 0xb3, 0xa9, 0x44, 0xf4, 0xc5, + 0x14, 0x1c, 0xde, 0x57, 0x13, 0x49, 0x58, 0x8a, 0x39, 0x64, 0xd2, 0xe1, 0x21, 0xa4, 0x98, 0x9b, + 0xcd, 0x14, 0xf3, 0x96, 0x78, 0xde, 0xb0, 0x09, 0xe6, 0x2e, 0x9e, 0x68, 0xad, 0x0d, 0x22, 0x31, + 0x5c, 0x59, 0x1e, 0x0c, 0xdd, 0x4a, 0xcc, 0x40, 0x9a, 0xdf, 0xe5, 0xfb, 0x3a, 0xb7, 0x93, 0x36, + 0x2b, 0x51, 0xd1, 0x8d, 0x26, 0xf3, 0xa3, 0x8d, 0x7b, 0x21, 0x99, 0x91, 0xfa, 0x99, 0x73, 0x8a, + 0x2f, 0xc3, 0x1d, 0x22, 0x13, 0xd5, 0x4e, 0xd8, 0xac, 0x77, 0xf1, 0xa1, 0x0d, 0x1a, 0xaf, 0xe6, + 0xb9, 0x59, 0xf7, 0xc9, 0x00, 0xae, 0x1d, 0xcf, 0x14, 0xbb, 0xeb, 0xd3, 0x65, 0x6a, 0xe1, 0x82, + 0x20, 0x69, 0xb2, 0x24, 0x70, 0x77, 0x26, 0xb0, 0x4a, 0xc2, 0xab, 0xca, 0x9c, 0x58, 0xd0, 0x41, + 0x55, 0x09, 0x1a, 0x6c, 0x52, 0x82, 0xb6, 0xf8, 0xb3, 0x36, 0x25, 0x21, 0xe4, 0x23, 0x7c, 0x65, + 0xf0, 0x9a, 0xca, 0xb9, 0x46, 0xb4, 0x5a, 0xc9, 0x8f, 0xfc, 0x97, 0xb9, 0xfd, 0xe5, 0x38, 0xed, + 0x74, 0x40, 0x8f, 0x5e, 0xd2, 0xa3, 0x8c, 0x3a, 0xa5, 0x83, 0x06, 0xe2, 0xd3, 0x93, 0x15, 0x79, + 0x1f, 0x8c, 0x9f, 0x56, 0x2a, 0x05, 0xdc, 0x8d, 0x3d, 0x4b, 0x1b, 0x2a, 0xf0, 0x30, 0x76, 0x54, + 0x2c, 0x9f, 0x56, 0x7b, 0xb2, 0x53, 0xae, 0xc2, 0xa8, 0xf1, 0x12, 0x49, 0x92, 0x38, 0x69, 0xf1, + 0xe5, 0xf0, 0x46, 0xec, 0x38, 0x86, 0x08, 0xf1, 0x8e, 0x99, 0x4a, 0x2d, 0x89, 0x0c, 0xb2, 0x30, + 0xf8, 0x96, 0x23, 0x19, 0x7a, 0xa3, 0x59, 0x33, 0x65, 0xa0, 0xd9, 0xd5, 0xce, 0xde, 0xae, 0x96, + 0x34, 0x8f, 0xb6, 0x58, 0x7d, 0x75, 0x9d, 0xdb, 0x72, 0x43, 0x36, 0x47, 0xd4, 0x87, 0x83, 0x3b, + 0x22, 0xea, 0xd4, 0x92, 0x5e, 0xf1, 0x5a, 0x4a, 0xbd, 0xfa, 0x39, 0x31, 0x96, 0xda, 0x55, 0x91, + 0xec, 0x3b, 0x52, 0x60, 0x61, 0x27, 0xa8, 0x1b, 0xce, 0x93, 0x37, 0x22, 0xc7, 0xf7, 0x3e, 0x2e, + 0x26, 0x2a, 0x65, 0x8a, 0x5e, 0xcd, 0x56, 0x55, 0x47, 0x0f, 0x37, 0x1f, 0x16, 0x63, 0x85, 0xb3, + 0x14, 0x5b, 0xa8, 0x32, 0x50, 0x5b, 0x83, 0x7b, 0x9c, 0x1a, 0xeb, 0x69, 0xf2, 0x43, 0xa4, 0x43, + 0x59, 0x20, 0xf8, 0x41, 0xa7, 0xcd, 0xa3, 0x47, 0x55, 0xad, 0xb0, 0x0e, 0x8c, 0x78, 0x46, 0x6b, + 0x53, 0x98, 0x5a, 0x9c, 0x52, 0xc3, 0x80, 0xd4, 0x8c, 0xea, 0x99, 0x09, 0xe1, 0x38, 0x68, 0x12, + 0x83, 0x27, 0xbd, 0xab, 0x98, 0x1e, 0x99, 0x49, 0xc2, 0x01, 0xd6, 0x62, 0x8e, 0x0e, 0xb1, 0x17, + 0x88, 0x27, 0x5d, 0xf5, 0x82, 0xa8, 0x07, 0xa2, 0x2c, 0x88, 0x7c, 0x28, 0xb2, 0x47, 0x6a, 0x4b, + 0xc1, 0x14, 0xa0, 0x5c, 0xed, 0x1a, 0xaa, 0x0d, 0x7f, 0x8a, 0xd2, 0x76, 0xea, 0x0b, 0x76, 0x9d, + 0x1d, 0x26, 0x6c, 0x9e, 0xaa, 0xee, 0xcb, 0x6a, 0x1b, 0x6f, 0xa2, 0xcc, 0xa9, 0x3a, 0xf4, 0x8e, + 0x7e, 0x9d, 0xbb, 0xcc, 0xb3, 0x66, 0x54, 0x60, 0x60, 0x08, 0x43, 0xcd, 0x20, 0xea, 0x90, 0x9f, + 0x6f, 0x12, 0x22, 0x3c, 0xec, 0xe2, 0x1b, 0xb4, 0xe2, 0xc1, 0x6e, 0xf6, 0x84, 0x2e, 0x6d, 0x97, + 0x9a, 0xdc, 0x9e, 0x70, 0xcc, 0xf6, 0x9c, 0x7e, 0xd0, 0x14, 0x10, 0xc7, 0x69, 0x55, 0xc1, 0x14, + 0x35, 0xa8, 0x6f, 0x81, 0x95, 0x6b, 0x83, 0x34, 0xff, 0xca, 0x54, 0x2b, 0x47, 0x89, 0x04, 0x93, + 0x5c, 0x0c, 0x93, 0x7d, 0x4d, 0xfd, 0x85, 0x41, 0x23, 0xcf, 0xba, 0x39, 0x6c, 0x7f, 0x69, 0xa7, + 0xe9, 0xed, 0xc6, 0xc2, 0x0a, 0xb5, 0x17, 0x76, 0x3b, 0x00, 0xd7, 0x26, 0x39, 0x1b, 0x5d, 0x35, + 0xac, 0x8b, 0x41, 0x85, 0xef, 0x31, 0x99, 0x98, 0x4b, 0x17, 0xfa, 0x14, 0xc9, 0xdf, 0x92, 0x24, + 0x57, 0xef, 0x19, 0xb5, 0x29, 0x21, 0x35, 0x20, 0xbe, 0x4a, 0xc1, 0xa9, 0x76, 0xc1, 0x54, 0x2a, + 0x90, 0x09, 0xb8, 0xbc, 0x19, 0xc5, 0x1e, 0x49, 0x85, 0xeb, 0x79, 0x90, 0x66, 0x71, 0x72, 0x53, + 0x91, 0xdf, 0x95, 0x24, 0x48, 0x53, 0xde, 0xd0, 0xe9, 0xd7, 0x08, 0x1a, 0x16, 0x42, 0xd1, 0x1b, + 0xfa, 0x63, 0x1f, 0x98, 0xd6, 0x6d, 0xc3, 0x1d, 0x51, 0x6f, 0xc5, 0xcf, 0x5d, 0xb4, 0xf8, 0x4e, + 0xea, 0x2d, 0x93, 0x5a, 0x15, 0x0f, 0xa6, 0x3c, 0x54, 0x56, 0x81, 0x86, 0x31, 0x9b, 0x2b, 0xc1, + 0xa2, 0x9b, 0x77, 0x26, 0x7c, 0x99, 0xd5, 0xb8, 0x86, 0x6a, 0x88, 0xa1, 0x4d, 0x1e, 0xd8, 0xac, + 0x0c, 0x27, 0x8c, 0x04, 0xd5, 0x40, 0x04, 0x95, 0x89, 0x45, 0x41, 0xd4, 0xaf, 0x10, 0xa7, 0x06, + 0x0f, 0x51, 0x7e, 0x8b, 0x8f, 0x35, 0x4f, 0x1c, 0x31, 0x6a, 0xf0, 0x16, 0x19, 0x1c, 0x35, 0xb0, + 0xff, 0xcb, 0x72, 0xcc, 0xaa, 0xa4, 0xc2, 0x40, 0x58, 0xda, 0x51, 0x0b, 0x00, 0xbb, 0xf9, 0x3b, + 0xaa, 0x5d, 0x39, 0x77, 0xf8, 0x54, 0xaa, 0x58, 0xce, 0x47, 0xaa, 0x65, 0xcc, 0x1f, 0x14, 0xa9, + 0xe2, 0x35, 0x29, 0xd3, 0x7a, 0x2c, 0xa0, 0x79, 0x06, 0xac, 0xe8, 0x3e, 0x25, 0x19, 0x54, 0x82, + 0x49, 0x9b, 0xe7, 0xf8, 0x06, 0xf7, 0xca, 0xf0, 0xd2, 0x64, 0xea, 0x26, 0x30, 0x45, 0xd7, 0x01, + 0xf8, 0x12, 0x94, 0x9a, 0xdf, 0xa0, 0x31, 0xf0, 0x6d, 0xeb, 0x69, 0xae, 0x32, 0x9b, 0x51, 0x6f, + 0x7d, 0x0b, 0x85, 0xb6, 0x43, 0xc2, 0xa6, 0x66, 0x3c, 0xb8, 0xd3, 0xf9, 0x22, 0xb7, 0x39, 0x0d, + 0x9b, 0xfd, 0xb3, 0x38, 0x29, 0x6e, 0x79, 0x42, 0x0b, 0x00, 0x7e, 0x80, 0x3a, 0xc3, 0x16, 0x2c, + 0xf6, 0x47, 0xfc, 0x5d, 0xd1, 0x26, 0xce, 0x6c, 0x70, 0xa9, 0x91, 0x6d, 0x11, 0x48, 0x21, 0x30, + 0xa0, 0xdc, 0x93, 0x5c, 0xea, 0x41, 0xd6, 0x66, 0xe1, 0x2b, 0x28, 0x6f, 0xa1, 0x0c, 0xdb, 0xe6, + 0xd3, 0xcf, 0xe2, 0xd9, 0x2c, 0x64, 0x61, 0xd6, 0x7b, 0x1c, 0x66, 0xc8, 0x13, 0x3b, 0xb7, 0x76, + 0xc8, 0x96, 0xe4, 0x76, 0x43, 0x72, 0x12, 0x3f, 0xa8, 0xd6, 0x66, 0x41, 0x17, 0xbd, 0x2e, 0xe7, + 0x89, 0x9d, 0x92, 0xf4, 0xbe, 0x52, 0xa6, 0x6a, 0x24, 0xd4, 0xeb, 0xce, 0x94, 0x38, 0x34, 0x68, + 0x6b, 0x87, 0xfa, 0xec, 0x07, 0x93, 0x20, 0x9c, 0x95, 0x6c, 0x96, 0x14, 0x72, 0x6b, 0x62, 0x52, + 0xdd, 0x15, 0xda, 0x18, 0x44, 0xc0, 0x16, 0xc0, 0x8c, 0x8f, 0xb7, 0xa2, 0xbf, 0x37, 0x5f, 0x27, + 0xca, 0x45, 0x6b, 0x1c, 0x40, 0xa8, 0xc6, 0x52, 0x51, 0x8f, 0x6f, 0xe5, 0x87, 0x38, 0x8b, 0x5b, + 0x4f, 0xd9, 0xf4, 0x68, 0xda, 0x08, 0xaa, 0x6a, 0xd9, 0x45, 0x24, 0x55, 0x35, 0xd6, 0x86, 0xfc, + 0xb0, 0xf5, 0x45, 0x65, 0xdb, 0x8b, 0xca, 0x7a, 0xdd, 0x5f, 0xc4, 0x09, 0xb9, 0xaf, 0x27, 0x31, + 0x77, 0x1c, 0xd6, 0x18, 0x82, 0x56, 0xe5, 0x07, 0xbc, 0x98, 0xb6, 0x47, 0x32, 0x3b, 0x08, 0xd3, + 0x3a, 0x03, 0x2a, 0x9e, 0x37, 0x9c, 0xf9, 0xa6, 0x5c, 0x2b, 0xbf, 0xb8, 0x8b, 0xa3, 0xa4, 0xc5, + 0xdc, 0x6b, 0x38, 0x84, 0x8b, 0x21, 0x85, 0xe3, 0xb1, 0x8a, 0x2c, 0xea, 0xa3, 0xf4, 0x82, 0x33, + 0x5f, 0x9d, 0x9d, 0xf7, 0x52, 0xef, 0x5f, 0x4c, 0x8d, 0x1a, 0xad, 0x77, 0x1a, 0x97, 0x45, 0xe9, + 0xb9, 0xc1, 0xd6, 0xd2, 0x73, 0x54, 0xb9, 0xae, 0x95, 0x1b, 0x10, 0xa6, 0x81, 0x9a, 0xb5, 0x11, + 0x45, 0x83, 0xc2, 0x90, 0x6b, 0x2b, 0x2d, 0x38, 0x97, 0x45, 0xb7, 0xb2, 0xa6, 0xb4, 0x1e, 0x43, + 0xa0, 0x06, 0x92, 0xf0, 0xf6, 0x74, 0xc3, 0x69, 0xe5, 0x9d, 0xc3, 0xdc, 0x55, 0x58, 0xbc, 0xb0, + 0xc2, 0x4e, 0xcb, 0x2c, 0x2c, 0xe8, 0xac, 0xde, 0x94, 0xfa, 0x4b, 0x2a, 0xc9, 0xb3, 0xf7, 0x3a, + 0x8a, 0x57, 0x9c, 0x02, 0xbf, 0x33, 0xcd, 0xa6, 0x71, 0xb8, 0x45, 0x04, 0xa3, 0xcd, 0xc5, 0x54, + 0x6b, 0x22, 0x94, 0x0c, 0x11, 0x2b, 0x95, 0x3c, 0x9c, 0x7c, 0x63, 0x76, 0xdd, 0x57, 0xd5, 0x19, + 0x81, 0x24, 0x0c, 0xae, 0x06, 0xa4, 0x19, 0x64, 0xd2, 0x6f, 0x60, 0xa1, 0x02, 0xc3, 0xc4, 0x7d, + 0xdb, 0x89, 0x57, 0x59, 0x33, 0x8c, 0xab, 0x57, 0x4e, 0x2a, 0x35, 0xfc, 0xea, 0xf7, 0x3e, 0x0a, + 0x49, 0x7d, 0x35, 0x6a, 0x6d, 0xf4, 0xba, 0x05, 0x2e, 0x42, 0xb3, 0xc5, 0x3a, 0xd7, 0x45, 0xeb, + 0xdc, 0xfc, 0xca, 0xac, 0x8b, 0x01, 0x1c, 0x33, 0x0d, 0x16, 0x76, 0x72, 0xc3, 0x13, 0x1b, 0x68, + 0x55, 0xa4, 0x38, 0xf2, 0xca, 0x3b, 0x77, 0x1e, 0x25, 0xcd, 0x0b, 0x66, 0xa8, 0x55, 0x0b, 0xa7, + 0xae, 0x20, 0x0c, 0x1a, 0x35, 0x4b, 0x5b, 0xd2, 0x31, 0xb6, 0x05, 0x44, 0x4d, 0xb9, 0x0e, 0x6c, + 0x91, 0x14, 0x30, 0x50, 0x0c, 0xd9, 0xd9, 0xb9, 0x0c, 0x87, 0xc0, 0x5b, 0x54, 0x8a, 0x2e, 0xb5, + 0x09, 0x6f, 0x55, 0x63, 0xb6, 0xeb, 0x2c, 0x79, 0x6a, 0xa1, 0x07, 0xd6, 0x53, 0x52, 0x05, 0x53, + 0x2c, 0x10, 0x58, 0x49, 0x12, 0x61, 0x35, 0xc8, 0xf5, 0x61, 0x7d, 0x7a, 0x45, 0x06, 0x51, 0x73, + 0x53, 0x5a, 0x92, 0x8b, 0xf4, 0x22, 0xb9, 0xe8, 0xca, 0x0e, 0xb2, 0x02, 0xc3, 0x58, 0xfb, 0xda, + 0xc1, 0xf1, 0xea, 0x3e, 0x14, 0xda, 0x47, 0x5e, 0x4c, 0x01, 0x98, 0x77, 0x9c, 0x17, 0x75, 0x86, + 0x3c, 0x4e, 0x03, 0x44, 0x03, 0x2e, 0xce, 0x51, 0x5c, 0xa1, 0x1e, 0x02, 0x31, 0x25, 0x49, 0xa2, + 0xad, 0x46, 0x13, 0x3e, 0x07, 0x62, 0x43, 0x8f, 0x9e, 0xb8, 0xe0, 0x2d, 0x2b, 0x64, 0x54, 0x3f, + 0x4a, 0xd0, 0x58, 0xf2, 0xef, 0x54, 0x5b, 0xd3, 0xf4, 0x7a, 0x6a, 0x9e, 0xaa, 0xdf, 0xa5, 0x35, + 0x6e, 0xac, 0x9b, 0xa6, 0x29, 0xa6, 0x0a, 0x55, 0xd3, 0x46, 0xb2, 0xab, 0xc8, 0x35, 0x9c, 0xa0, + 0xd2, 0x86, 0xd6, 0xe5, 0x40, 0x50, 0xc9, 0x49, 0x96, 0x65, 0xc7, 0xac, 0xaf, 0xc6, 0x78, 0x9c, + 0x07, 0x9a, 0x2b, 0xa9, 0xff, 0x79, 0xf3, 0x6d, 0x61, 0x68, 0xd6, 0xc1, 0x1d, 0x45, 0xcc, 0x58, + 0x22, 0xc1, 0xae, 0x35, 0xcc, 0x84, 0x2e, 0xb7, 0x07, 0x4f, 0x87, 0x9b, 0x4f, 0xd4, 0xf2, 0x3e, + 0xdc, 0x30, 0x4e, 0xc9, 0xa6, 0x74, 0xab, 0xad, 0x04, 0xab, 0xef, 0x90, 0x27, 0x39, 0x52, 0x7c, + 0x75, 0x28, 0x02, 0x64, 0xd4, 0x79, 0x50, 0xb9, 0xd0, 0xdc, 0xb1, 0x55, 0x44, 0x33, 0x59, 0xed, + 0x54, 0x31, 0x1f, 0x7d, 0xb7, 0x54, 0xf4, 0xfb, 0x9a, 0x22, 0x7a, 0xb3, 0x52, 0x22, 0x35, 0x3c, + 0x6a, 0x70, 0x51, 0x71, 0x7c, 0x77, 0xaa, 0x79, 0xfd, 0xad, 0x1d, 0x23, 0x15, 0x4d, 0x17, 0x1b, + 0xec, 0x51, 0xa3, 0xb3, 0xbf, 0x30, 0x6b, 0x5d, 0x6d, 0x92, 0x3c, 0x02, 0x5d, 0xba, 0x29, 0x30, + 0x47, 0x2d, 0x2d, 0x99, 0xcd, 0x2a, 0xa8, 0xb8, 0x7a, 0x95, 0x7d, 0xab, 0x6d, 0xa6, 0x69, 0xad, + 0x0f, 0xd1, 0xd4, 0x6a, 0xd8, 0x97, 0x2d, 0xd5, 0x32, 0xbf, 0xb1, 0xfc, 0xa5, 0xa2, 0x0c, 0x27, + 0x75, 0xef, 0x24, 0xf8, 0x4a, 0xaf, 0xe2, 0xc4, 0xbb, 0x4f, 0xca, 0x6e, 0x95, 0x69, 0x8a, 0x3d, + 0x88, 0x09, 0x74, 0xb9, 0x16, 0x54, 0x93, 0x8a, 0xcd, 0x5c, 0x81, 0xdd, 0x8f, 0x89, 0xd8, 0x5b, + 0x17, 0x18, 0x62, 0x97, 0x7e, 0x40, 0x42, 0x38, 0x92, 0x1c, 0x2e, 0xab, 0x25, 0x59, 0x07, 0x5f, + 0x93, 0xc7, 0x51, 0xb0, 0x88, 0xdc, 0x18, 0x15, 0x76, 0x7b, 0x78, 0x97, 0x2e, 0x54, 0x39, 0x3c, + 0x5e, 0x91, 0x2e, 0x09, 0x49, 0x57, 0x61, 0x71, 0xd0, 0x81, 0xf6, 0x66, 0x34, 0x9f, 0x0b, 0x29, + 0x68, 0xad, 0x21, 0x10, 0x55, 0x6d, 0x79, 0x27, 0x2e, 0xf2, 0x32, 0x58, 0x9e, 0x40, 0xa3, 0x02, + 0x4a, 0xc3, 0xa3, 0xa4, 0xb5, 0x86, 0x2d, 0x6a, 0xdd, 0xd6, 0x95, 0xb0, 0xfb, 0x46, 0x42, 0x8c, + 0x46, 0x97, 0xd3, 0x4a, 0x96, 0xab, 0x60, 0x0c, 0xd0, 0xe5, 0x30, 0x59, 0x71, 0x5d, 0x38, 0xe0, + 0xc2, 0x54, 0xd8, 0x0d, 0x15, 0xfd, 0x4d, 0x99, 0x27, 0x5f, 0xd1, 0x74, 0x7b, 0x73, 0x43, 0xba, + 0x3d, 0x6d, 0x42, 0xb3, 0xed, 0x69, 0xac, 0x7c, 0x6b, 0xdb, 0x22, 0x35, 0xbf, 0x94, 0xfd, 0x5a, + 0x53, 0xf6, 0x6b, 0x03, 0x2a, 0xd1, 0xdb, 0xf8, 0x44, 0x7b, 0x0a, 0x12, 0x9d, 0x4b, 0xed, 0x98, + 0x80, 0xb1, 0x9b, 0x4b, 0xce, 0x64, 0x39, 0x91, 0x3b, 0x9c, 0xfb, 0xdb, 0x8d, 0x2f, 0x34, 0x8e, + 0x69, 0xb0, 0x82, 0xaa, 0x46, 0x55, 0x23, 0xa0, 0x81, 0x5b, 0x16, 0xf4, 0x61, 0x12, 0x7e, 0xa8, + 0xb5, 0x4b, 0x78, 0xe8, 0x4f, 0x8c, 0xfe, 0xb1, 0x5d, 0xa3, 0xd5, 0x6b, 0x0d, 0x16, 0xfa, 0x43, + 0x4e, 0x9c, 0xcd, 0x9b, 0x87, 0x2d, 0xa1, 0x5d, 0x91, 0xdf, 0x51, 0xab, 0xe1, 0xfd, 0xd5, 0x15, + 0xe6, 0x68, 0xde, 0x5d, 0xd5, 0x2d, 0x45, 0x07, 0xea, 0x5f, 0xd9, 0x49, 0x84, 0x82, 0xdb, 0x9d, + 0xe3, 0x4c, 0x72, 0xb3, 0x22, 0x1b, 0xeb, 0x88, 0xe6, 0x0f, 0xb4, 0xf5, 0xc4, 0xf5, 0xda, 0x4d, + 0xdd, 0xb0, 0x40, 0x01, 0xeb, 0x83, 0x1e, 0xb8, 0xa9, 0xad, 0x18, 0x04, 0x47, 0xf3, 0x12, 0xf9, + 0x20, 0x85, 0xae, 0xec, 0xc4, 0x4b, 0x45, 0xbf, 0x51, 0xbe, 0xb0, 0x3b, 0x16, 0xf1, 0x45, 0x42, + 0x41, 0xfa, 0x66, 0x37, 0x00, 0x81, 0x50, 0xe5, 0x77, 0x43, 0x3f, 0xb0, 0xb5, 0xeb, 0x75, 0xdf, + 0x81, 0x2a, 0xef, 0x1b, 0x28, 0x50, 0x91, 0xe5, 0xbf, 0x22, 0xb5, 0xaf, 0xa9, 0x82, 0xb0, 0xef, + 0x27, 0x09, 0x45, 0x3c, 0x38, 0xac, 0xa8, 0xaf, 0x71, 0xbc, 0xc2, 0xf4, 0x4b, 0x0f, 0x41, 0x58, + 0xb9, 0xc9, 0xa0, 0xed, 0x33, 0x1d, 0x40, 0x98, 0x5f, 0xd9, 0xb6, 0x38, 0x62, 0x0c, 0x0d, 0x8b, + 0xa0, 0x43, 0xc9, 0x7b, 0x46, 0x55, 0x2e, 0x6b, 0x6c, 0xd6, 0x1f, 0x59, 0x07, 0x3b, 0x9f, 0x3a, + 0x8a, 0xe3, 0x8c, 0x52, 0x64, 0x51, 0x60, 0x51, 0xf4, 0xad, 0x56, 0xab, 0x10, 0x99, 0x77, 0x1e, + 0x7a, 0xaf, 0x07, 0x5b, 0x40, 0xb9, 0x87, 0xf3, 0xb7, 0xc5, 0x28, 0x1b, 0x4a, 0xb4, 0xe8, 0xc6, + 0xc3, 0xc9, 0xd6, 0xef, 0x1c, 0x6d, 0xa0, 0xab, 0x7a, 0xe5, 0x46, 0x81, 0xe2, 0xe9, 0x90, 0xa9, + 0x6b, 0x47, 0x48, 0xe9, 0xcb, 0xc6, 0x86, 0x70, 0xbf, 0x80, 0x81, 0x45, 0xfb, 0xdb, 0x0d, 0x0e, + 0xcc, 0x1e, 0x7c, 0xb0, 0x88, 0x56, 0xa1, 0x6e, 0x7b, 0xaa, 0xea, 0x50, 0x24, 0x79, 0xfd, 0xdf, + 0x16, 0xc4, 0x0b, 0xec, 0x6e, 0xe9, 0x0e, 0x18, 0x51, 0xb7, 0xe7, 0x2d, 0xff, 0xca, 0xcc, 0x06, + 0xe5, 0xc5, 0x6c, 0x7c, 0x58, 0xa6, 0xfc, 0x6a, 0x0c, 0xdf, 0x16, 0x53, 0x2f, 0x6a, 0x19, 0x6f, + 0xf1, 0x59, 0x9b, 0x72, 0x2d, 0x29, 0x73, 0x63, 0xdb, 0x0d, 0xd9, 0x95, 0x2c, 0x92, 0x27, 0x66, + 0x3a, 0x35, 0x93, 0x72, 0x5a, 0x66, 0x49, 0xed, 0x4b, 0xe9, 0x96, 0x7e, 0x89, 0x2a, 0x97, 0xf4, + 0x5c, 0xae, 0x51, 0x09, 0x05, 0x11, 0x52, 0xb4, 0x8f, 0xb6, 0x89, 0xb6, 0xfa, 0x87, 0x78, 0x78, + 0xde, 0x5f, 0xbe, 0x0c, 0x8c, 0x9a, 0x6b, 0x1f, 0xc8, 0xa1, 0x1a, 0x9f, 0x78, 0x2e, 0x7e, 0x60, + 0xdc, 0x39, 0x8e, 0xa0, 0xbc, 0x82, 0x0c, 0xdb, 0xd2, 0xb4, 0x71, 0x10, 0xad, 0xea, 0xa9, 0x6b, + 0x29, 0x9c, 0x6d, 0xb6, 0x1d, 0x8d, 0x23, 0x0e, 0xff, 0x88, 0x0b, 0xae, 0x7e, 0xa0, 0x42, 0x2c, + 0xb4, 0xce, 0xbe, 0xc5, 0x92, 0xa7, 0x12, 0xde, 0x2f, 0xb7, 0xcb, 0x10, 0xe2, 0xef, 0xb5, 0x6f, + 0xb7, 0x88, 0xd5, 0x14, 0x44, 0xbd, 0x7d, 0xc8, 0x4f, 0x2d, 0x19, 0xcb, 0x5d, 0xd3, 0xfd, 0x04, + 0xa6, 0xd8, 0x2c, 0x48, 0x47, 0xe5, 0x7f, 0x8b, 0x93, 0xbb, 0xf9, 0x61, 0x94, 0x16, 0xa7, 0x72, + 0x83, 0x84, 0xb7, 0x79, 0x0d, 0xcc, 0xf6, 0x42, 0xd2, 0x4a, 0x35, 0x5b, 0x68, 0xeb, 0xa7, 0x4e, + 0xe4, 0xea, 0x1a, 0x21, 0xe1, 0x33, 0x59, 0xb5, 0xa8, 0x52, 0xed, 0x0b, 0x28, 0xcd, 0x24, 0xa8, + 0xdc, 0xbc, 0xa2, 0x3a, 0x2d, 0xc5, 0x3f, 0x43, 0x7e, 0x88, 0xf6, 0xa1, 0xea, 0x9e, 0x24, 0xba, + 0xe0, 0x86, 0x82, 0x63, 0xbf, 0x7a, 0xde, 0xb1, 0x6a, 0x9c, 0x81, 0x7e, 0x49, 0xbf, 0x46, 0xd1, + 0x52, 0xa4, 0x61, 0xab, 0x7a, 0x98, 0xc5, 0x4b, 0x49, 0x62, 0xca, 0x91, 0xba, 0xe1, 0x9b, 0x13, + 0x46, 0xf3, 0xe8, 0xa5, 0xa2, 0x57, 0x3f, 0x16, 0x51, 0x3f, 0x63, 0xb6, 0xf9, 0x93, 0x09, 0x35, + 0x1f, 0xa2, 0xf8, 0x81, 0x85, 0xfa, 0x52, 0xf3, 0x92, 0x17, 0x1f, 0xe0, 0x13, 0x96, 0x3d, 0x56, + 0xf2, 0xdf, 0x9a, 0xc7, 0x0b, 0xf2, 0xb1, 0xfe, 0x7d, 0x80, 0x6d, 0xb5, 0x35, 0x1b, 0x25, 0xdc, + 0xab, 0x0e, 0x60, 0x80, 0x66, 0x0b, 0x53, 0x94, 0xcb, 0xea, 0xd2, 0x79, 0x15, 0xef, 0x46, 0xa1, + 0x6e, 0xa8, 0xff, 0xae, 0xa8, 0x97, 0x57, 0xd8, 0x18, 0xb0, 0x23, 0x0a, 0x65, 0x45, 0xe8, 0x62, + 0x33, 0x6a, 0x75, 0x8c, 0x2b, 0x55, 0x8e, 0xd9, 0x5a, 0x6e, 0xa8, 0x72, 0xac, 0x69, 0x5b, 0xea, + 0xf4, 0xf2, 0xaa, 0xbe, 0xa3, 0x1c, 0x8f, 0xf4, 0xd1, 0x2e, 0x45, 0x7d, 0xc5, 0x42, 0xbb, 0xf5, + 0x0a, 0xcb, 0xad, 0x95, 0x79, 0xcd, 0x6a, 0x11, 0xe0, 0x4a, 0x99, 0x6f, 0xea, 0xfe, 0x36, 0x9a, + 0xc2, 0xfd, 0x8e, 0xd2, 0xd9, 0x4a, 0x4b, 0x41, 0xdd, 0x0d, 0x27, 0xa8, 0x74, 0x66, 0x2d, 0x34, + 0x6a, 0xce, 0xd6, 0x1d, 0xc7, 0x2d, 0x19, 0xa0, 0x25, 0x2d, 0x34, 0x2a, 0x02, 0x33, 0xf6, 0x2c, + 0x64, 0x29, 0x70, 0x95, 0x60, 0x33, 0x32, 0x08, 0x59, 0x4a, 0x9b, 0xca, 0xbf, 0x2a, 0xc6, 0xa0, + 0x56, 0xcb, 0xab, 0xda, 0x16, 0xd5, 0x6b, 0x59, 0x2a, 0x85, 0x29, 0xbf, 0xb9, 0xfa, 0x20, 0x73, + 0x4d, 0x6c, 0x87, 0x4a, 0xab, 0x94, 0xc6, 0xb3, 0x6f, 0x5b, 0x2d, 0x77, 0x7e, 0xd0, 0xec, 0x8e, + 0xe2, 0x6e, 0xd5, 0x72, 0x91, 0xe6, 0x9d, 0x85, 0xd1, 0xb6, 0x83, 0xc7, 0x76, 0x6e, 0x43, 0xe9, + 0xb3, 0x51, 0xbd, 0x9e, 0x5b, 0x6b, 0xe5, 0xad, 0x5d, 0xe6, 0xaf, 0xde, 0x51, 0x61, 0x4b, 0x95, + 0x1b, 0xfb, 0x22, 0x14, 0xa0, 0xda, 0x1a, 0x26, 0x36, 0xeb, 0x35, 0xa7, 0xca, 0x1b, 0x03, 0x55, + 0x28, 0xf5, 0xb0, 0xa9, 0x42, 0x11, 0x73, 0x98, 0x2b, 0x86, 0x56, 0x2f, 0x51, 0x44, 0x61, 0xda, + 0xa2, 0x63, 0x55, 0x6b, 0x10, 0x55, 0x25, 0xfe, 0xd6, 0x92, 0x1f, 0x03, 0xa5, 0x71, 0x6c, 0x4b, + 0xdf, 0x58, 0x93, 0x82, 0x56, 0x19, 0x64, 0xeb, 0xd7, 0x28, 0xf7, 0x60, 0x9a, 0x0f, 0x5b, 0x2a, + 0x02, 0x8c, 0xf2, 0x6f, 0xf0, 0x51, 0xb1, 0x22, 0xdf, 0xe1, 0x75, 0xa8, 0x1e, 0xd0, 0xdc, 0x32, + 0xdd, 0x4d, 0xc7, 0x2e, 0xf3, 0xa3, 0x08, 0x1b, 0x4e, 0xcd, 0x69, 0x03, 0xb9, 0xf1, 0x1c, 0x95, + 0xc7, 0xe3, 0xf2, 0x56, 0x2c, 0x84, 0x5c, 0x9e, 0x82, 0x2b, 0x52, 0x26, 0x38, 0x4b, 0x2c, 0x1e, + 0x57, 0xce, 0xf3, 0x32, 0xce, 0xd1, 0x3c, 0x4c, 0x4b, 0x5d, 0xe8, 0x4a, 0x55, 0x59, 0x2e, 0xd0, + 0x50, 0xd4, 0x8d, 0xc5, 0x93, 0x12, 0x1b, 0x4e, 0xf8, 0x14, 0x29, 0xf9, 0xad, 0xe7, 0x2d, 0x2a, + 0x75, 0x46, 0x73, 0xf7, 0x66, 0xf3, 0x28, 0x81, 0x32, 0xaa, 0x67, 0xd3, 0xdf, 0xee, 0x5a, 0xd5, + 0x7c, 0x63, 0x0e, 0x7b, 0xad, 0x96, 0xf5, 0x56, 0xde, 0x88, 0x46, 0x46, 0x35, 0x25, 0xb3, 0x4c, + 0x0d, 0x1e, 0x09, 0x9a, 0xd1, 0x48, 0x6e, 0xb4, 0xaa, 0x7d, 0x88, 0x91, 0xa1, 0x77, 0x23, 0xbf, + 0xf4, 0x0e, 0xfe, 0x52, 0x49, 0x05, 0xc5, 0x9b, 0x92, 0xc7, 0xb6, 0x20, 0xdf, 0xb7, 0x25, 0x82, + 0xd6, 0x16, 0x92, 0x27, 0x0b, 0x61, 0x21, 0x2b, 0x65, 0xcb, 0xd0, 0x9b, 0x12, 0x48, 0x18, 0x4a, + 0x6e, 0x8a, 0x68, 0x8b, 0x1e, 0x33, 0xa1, 0xa0, 0xca, 0x06, 0x1d, 0xa8, 0x16, 0x37, 0x14, 0x54, + 0xc4, 0x0d, 0x2f, 0xdc, 0x19, 0x40, 0x16, 0xe9, 0x43, 0xf4, 0x6a, 0x56, 0x0c, 0x37, 0x73, 0x07, + 0xc3, 0xad, 0x16, 0x9f, 0xac, 0x12, 0x4c, 0xcd, 0x57, 0xdd, 0x0a, 0x6c, 0x2f, 0x21, 0xc0, 0x0a, + 0x49, 0xbd, 0x35, 0x6a, 0x42, 0xda, 0xb4, 0x3d, 0xb5, 0x11, 0xb3, 0xb0, 0xb7, 0xdb, 0xc6, 0x15, + 0x95, 0xae, 0x8a, 0xa9, 0x82, 0x26, 0xb4, 0x43, 0x9c, 0x4f, 0x38, 0x55, 0x75, 0x07, 0x5e, 0x20, + 0x3e, 0x6c, 0xfb, 0xb1, 0x25, 0x7a, 0xa2, 0x25, 0x9f, 0xcd, 0x32, 0x21, 0x3e, 0x49, 0x52, 0x70, + 0xc7, 0xad, 0x5c, 0xe2, 0xf5, 0x16, 0x31, 0x8f, 0xec, 0xc3, 0xa5, 0x74, 0xfb, 0x03, 0xfe, 0x21, + 0xff, 0x9c, 0xc7, 0x0f, 0x5c, 0xb3, 0x64, 0x45, 0xde, 0x7a, 0x0e, 0x99, 0xdb, 0x97, 0x41, 0x9c, + 0x50, 0x09, 0x5b, 0x7e, 0x3d, 0xba, 0xf4, 0x83, 0xf4, 0xbc, 0x55, 0xc2, 0x1c, 0x22, 0x7d, 0x59, + 0x59, 0xa4, 0xad, 0x4d, 0x82, 0x8c, 0xb0, 0x36, 0x3d, 0x5a, 0xc9, 0x7d, 0xac, 0x08, 0xad, 0x4a, + 0x57, 0xd7, 0xc6, 0x9e, 0xd6, 0xeb, 0xc3, 0x03, 0xf6, 0xf5, 0xf1, 0xbf, 0x1d, 0x1e, 0x80, 0x20, + 0x9b, 0xfe, 0xed, 0x10, 0x3c, 0x01, 0xec, 0xb3, 0xe4, 0x97, 0x33, 0xf8, 0x38, 0x5e, 0x9a, 0x5a, + 0x9d, 0xfc, 0xd3, 0xc0, 0x1d, 0x64, 0x27, 0x81, 0xdd, 0x63, 0x3a, 0xa6, 0xd5, 0xc9, 0x92, 0x15, + 0xa1, 0x1f, 0x3b, 0x87, 0xd6, 0x37, 0x0b, 0x27, 0x0e, 0x51, 0xe0, 0x59, 0x1d, 0x96, 0x4d, 0xb4, + 0xa4, 0x5f, 0x3d, 0xef, 0x14, 0x1f, 0xb1, 0xef, 0x14, 0x1f, 0xb1, 0xe7, 0xaf, 0x20, 0x74, 0xc8, + 0xbf, 0xbd, 0xde, 0xa9, 0x7f, 0x8c, 0xbe, 0xc3, 0xbe, 0xb0, 0xde, 0x81, 0x5d, 0xee, 0xe4, 0xdf, + 0x72, 0xef, 0xf0, 0xa3, 0x01, 0x9d, 0xea, 0x97, 0xdc, 0x3b, 0x7a, 0xe7, 0xa0, 0xd1, 0x61, 0xfd, + 0x6b, 0xf4, 0x1b, 0x3a, 0x64, 0xc7, 0x0f, 0x9a, 0x1d, 0x36, 0xbe, 0x45, 0xdf, 0xa1, 0x56, 0x6d, + 0xcb, 0x40, 0x1b, 0x3f, 0x47, 0xff, 0xcd, 0x53, 0xd8, 0xfd, 0x7b, 0xf4, 0xf9, 0x50, 0x3c, 0x03, + 0xa3, 0x1c, 0x8d, 0x8e, 0xcd, 0x7b, 0x3e, 0x3c, 0x60, 0x3b, 0xd4, 0xdc, 0xae, 0xa0, 0x07, 0x56, + 0x5d, 0x6d, 0xa3, 0x54, 0x1d, 0xa9, 0x7a, 0x67, 0x5a, 0x42, 0xa3, 0x21, 0x45, 0xe9, 0x1b, 0x48, + 0x51, 0x91, 0x1e, 0x8e, 0xd0, 0xb0, 0x6f, 0x5c, 0x9a, 0xb6, 0x82, 0x14, 0xf6, 0x7d, 0x86, 0x9e, + 0x82, 0x94, 0x79, 0xcf, 0xb8, 0xec, 0x0d, 0x9e, 0x8f, 0x2e, 0x07, 0xcf, 0x75, 0xf1, 0x49, 0x4f, + 0xf9, 0xb3, 0x73, 0x30, 0xdd, 0x36, 0xfc, 0x22, 0x9d, 0xdd, 0x35, 0xba, 0x8e, 0x8c, 0xbe, 0x31, + 0x57, 0x06, 0x97, 0x8a, 0xf2, 0x7c, 0x14, 0xf6, 0x0c, 0xa4, 0xdd, 0xd5, 0x29, 0x28, 0x34, 0x1b, + 0x7a, 0xe5, 0x07, 0xb3, 0xdc, 0x6b, 0xab, 0xa3, 0xa8, 0x1d, 0xe4, 0xde, 0xb0, 0xbf, 0x89, 0xd5, + 0x51, 0xa1, 0xd3, 0x62, 0xd0, 0x61, 0xdf, 0x44, 0xc3, 0xbe, 0x69, 0x0f, 0xd0, 0x80, 0x4e, 0x06, + 0xfe, 0x33, 0xfb, 0xfa, 0xc2, 0xec, 0xeb, 0x3d, 0xb3, 0xaf, 0x17, 0xf7, 0x15, 0x76, 0xff, 0x95, + 0xde, 0x1f, 0x20, 0xbd, 0x3f, 0xb0, 0x15, 0x19, 0x16, 0x0a, 0xfe, 0xc9, 0xdf, 0x52, 0xf4, 0xbe, + 0xb9, 0x80, 0x7f, 0x7a, 0xf0, 0x4f, 0xad, 0x81, 0xc2, 0x1b, 0xdc, 0x31, 0x21, 0xaa, 0xed, 0xdc, + 0xb5, 0x4e, 0x06, 0x52, 0xe5, 0x77, 0x8a, 0xbc, 0x80, 0xee, 0xdf, 0x0d, 0xe0, 0x8f, 0xfe, 0x6e, + 0x04, 0x7f, 0x94, 0x77, 0xda, 0x5d, 0xbb, 0x10, 0x27, 0xe4, 0xee, 0x05, 0x33, 0x2a, 0xeb, 0xa5, + 0xf4, 0x69, 0xaf, 0x9b, 0x17, 0xb4, 0xa5, 0xc1, 0xa8, 0xad, 0xc1, 0x16, 0xb8, 0xa0, 0x00, 0xd0, + 0x1d, 0xd3, 0x5e, 0x68, 0x08, 0x08, 0x1d, 0x99, 0x3d, 0xf8, 0x07, 0x69, 0x40, 0x21, 0x03, 0xa4, + 0xa8, 0x73, 0xc5, 0xb8, 0x6b, 0x51, 0x89, 0x9f, 0x90, 0x74, 0x7e, 0xd7, 0xb2, 0xaa, 0x32, 0x1a, + 0x5e, 0x1a, 0xf3, 0x9e, 0xf1, 0x4a, 0x47, 0xca, 0xf0, 0xb2, 0x67, 0xcc, 0x8d, 0xc5, 0xa8, 0x6f, + 0xf4, 0xf4, 0x23, 0x18, 0x8f, 0xed, 0xb1, 0x81, 0x46, 0x8b, 0xbe, 0x81, 0x4c, 0xe1, 0x96, 0x32, + 0x42, 0x0d, 0x08, 0x0e, 0x0f, 0xd2, 0xcb, 0xd9, 0xf4, 0x6f, 0xf0, 0xcb, 0x0b, 0x2e, 0x0b, 0x06, + 0x0b, 0x0e, 0xd4, 0x9c, 0x95, 0xda, 0xe0, 0x6c, 0x2c, 0x9e, 0x30, 0xc7, 0x63, 0xc9, 0x33, 0x85, + 0xb7, 0x28, 0xa3, 0xed, 0x4c, 0x45, 0x4e, 0x5d, 0x66, 0x72, 0xb6, 0xf2, 0xea, 0xc3, 0x55, 0x4a, + 0xd0, 0x3c, 0x21, 0xbe, 0xd5, 0xf9, 0xae, 0xc2, 0xa6, 0x29, 0x90, 0x97, 0xb3, 0xe9, 0x21, 0x38, + 0xdd, 0xa6, 0xa7, 0x24, 0x8e, 0x5e, 0xd3, 0x07, 0xaf, 0x8e, 0x0f, 0xa9, 0x8a, 0x38, 0x7d, 0xcb, + 0xf5, 0x2b, 0x74, 0x1c, 0x2f, 0x96, 0x76, 0x14, 0xc4, 0xd1, 0xe1, 0x01, 0x7b, 0x72, 0x78, 0x40, + 0xdf, 0x39, 0x3c, 0xf0, 0x82, 0xcb, 0x02, 0x4a, 0xf0, 0xbf, 0x71, 0x90, 0x22, 0xfb, 0x92, 0xc3, + 0x42, 0x33, 0x1c, 0xac, 0xce, 0x6b, 0xa6, 0xde, 0x14, 0x53, 0x42, 0xe8, 0x90, 0xbb, 0xea, 0xf8, + 0x1b, 0xcc, 0xbf, 0xd4, 0x41, 0xd4, 0xf3, 0x04, 0x3b, 0x63, 0x75, 0x28, 0x8b, 0xa2, 0x33, 0xad, + 0xcc, 0x81, 0xf3, 0xae, 0x2a, 0xf4, 0x67, 0x97, 0x24, 0x81, 0xb7, 0x0a, 0xc0, 0x58, 0xef, 0xcd, + 0xe1, 0x84, 0xfe, 0x73, 0x33, 0xb2, 0x75, 0x0c, 0x60, 0x50, 0xd5, 0x21, 0x5e, 0xf1, 0xe6, 0xf9, + 0x10, 0x0e, 0xc5, 0xa7, 0xc8, 0xbe, 0xec, 0xad, 0x22, 0x30, 0x30, 0x3a, 0x88, 0xad, 0xfb, 0x54, + 0x3e, 0x3c, 0x70, 0x76, 0x83, 0x80, 0x59, 0x60, 0xad, 0xe3, 0x53, 0x5e, 0x56, 0xdf, 0x21, 0x68, + 0x7d, 0x9f, 0x19, 0x32, 0xfe, 0xd1, 0xd6, 0x3d, 0x7b, 0x52, 0xed, 0xff, 0x0d, 0xdc, 0xbb, 0x4f, + 0xf7, 0xb9, 0xfe, 0xdd, 0xbe, 0x80, 0xc0, 0x5b, 0xaa, 0x03, 0x3c, 0xa5, 0x8a, 0xf7, 0x86, 0x11, + 0x0e, 0x0f, 0x22, 0xfb, 0xb2, 0x0d, 0xe3, 0x45, 0x37, 0x7c, 0x87, 0xf5, 0x54, 0x3c, 0x2a, 0x3e, + 0x23, 0xdc, 0xa1, 0x9b, 0x01, 0x4d, 0xe9, 0x55, 0x81, 0xa0, 0xb4, 0x75, 0xf1, 0x88, 0xb5, 0xef, + 0x4c, 0x8f, 0x8b, 0xef, 0x1d, 0x37, 0x11, 0xf9, 0xf0, 0x80, 0xd2, 0x22, 0x25, 0x57, 0x84, 0x0e, + 0xc1, 0xc3, 0x5b, 0x40, 0x05, 0xea, 0x12, 0x49, 0xf2, 0xd1, 0x99, 0x2f, 0x56, 0x44, 0x69, 0xe8, + 0xe4, 0x70, 0x99, 0x3f, 0xe7, 0x99, 0x0e, 0x1c, 0x34, 0xf6, 0xfd, 0xd4, 0xcf, 0x81, 0xfb, 0x99, + 0x24, 0x9d, 0xe9, 0xe9, 0xc9, 0xd9, 0xe9, 0xeb, 0xb3, 0xe3, 0x7f, 0x9c, 0x5c, 0xbc, 0x3a, 0x3e, + 0x3c, 0x58, 0x4e, 0x0f, 0xe7, 0x8a, 0xd8, 0x2c, 0x0b, 0xb2, 0x90, 0x74, 0x04, 0xac, 0x9e, 0x2b, + 0x55, 0x62, 0xab, 0x2e, 0x91, 0xf0, 0x8d, 0x5d, 0x01, 0x1c, 0x60, 0x7c, 0xc2, 0x62, 0x71, 0x97, + 0x2f, 0x03, 0x87, 0x5f, 0x50, 0x3f, 0x71, 0x67, 0xfa, 0xef, 0x7f, 0xfd, 0xdf, 0xe8, 0x1d, 0x5f, + 0x0a, 0xf1, 0xf5, 0x2a, 0x89, 0x16, 0x8e, 0x65, 0xd6, 0x05, 0xff, 0x68, 0x34, 0xb7, 0x4f, 0x36, + 0xef, 0x4c, 0x73, 0x2f, 0x68, 0x47, 0x94, 0x31, 0xb4, 0x6e, 0x45, 0x03, 0xf3, 0x2a, 0x5c, 0xe6, + 0x80, 0x6d, 0x02, 0xdf, 0x1f, 0x98, 0x23, 0x33, 0x4e, 0x8a, 0x91, 0xe9, 0x0a, 0xa2, 0x0a, 0x3f, + 0xe1, 0x9e, 0x6c, 0xce, 0x51, 0xca, 0x8e, 0xed, 0x24, 0x0b, 0xa8, 0x90, 0x62, 0xaf, 0xd2, 0x93, + 0xbc, 0x60, 0xec, 0x55, 0x17, 0x51, 0x58, 0xe8, 0xc2, 0x2d, 0x5d, 0x69, 0x01, 0x1a, 0x5c, 0x7d, + 0xd3, 0xa7, 0xbf, 0x9c, 0xbd, 0x7d, 0x83, 0x5e, 0x9d, 0x9c, 0x3f, 0xc7, 0xe8, 0xcd, 0x8b, 0x1f, + 0x9f, 0x5f, 0xa0, 0xd3, 0xb3, 0x9f, 0x61, 0xa7, 0xab, 0xef, 0xcd, 0x55, 0xc6, 0x45, 0xe0, 0xc8, + 0x5f, 0x64, 0x03, 0x74, 0x6f, 0x8e, 0x9e, 0xbe, 0x38, 0x3b, 0x3c, 0x98, 0xab, 0x8d, 0x11, 0x8a, + 0x86, 0x81, 0xd7, 0x99, 0xfe, 0x6c, 0x07, 0xf4, 0xa3, 0xde, 0x7e, 0x9c, 0x20, 0x37, 0x67, 0xcf, + 0x08, 0x56, 0xf6, 0xdf, 0xff, 0xfa, 0xdf, 0xb5, 0x71, 0x6a, 0x98, 0x53, 0x9d, 0x52, 0x69, 0x68, + 0xb5, 0x4b, 0x8f, 0x60, 0x7a, 0x78, 0x10, 0xd4, 0xff, 0xf0, 0x9d, 0x6a, 0x0a, 0x81, 0xd6, 0x05, + 0xa3, 0xd6, 0x5a, 0x7d, 0xc5, 0x28, 0xad, 0x30, 0x69, 0x72, 0x7c, 0x76, 0x7a, 0x7a, 0x72, 0x7c, + 0xf1, 0xe2, 0xec, 0xb4, 0x90, 0x2f, 0xcc, 0x00, 0xa3, 0x53, 0xa6, 0x5d, 0xc0, 0xc4, 0x3a, 0xd3, + 0x73, 0x30, 0xb9, 0x19, 0xaa, 0xd0, 0xe7, 0xcd, 0xb1, 0xab, 0x1d, 0xff, 0xe3, 0xf4, 0xec, 0xe7, + 0x53, 0x74, 0x7a, 0xf6, 0xf4, 0xe4, 0x7c, 0x63, 0xcf, 0xbc, 0x74, 0x55, 0x4a, 0xa9, 0x60, 0xd7, + 0x8e, 0xcf, 0x4e, 0x5f, 0xbe, 0x38, 0x3d, 0x41, 0xcf, 0xce, 0xde, 0x6c, 0xec, 0x77, 0xb5, 0x84, + 0x52, 0x5a, 0x77, 0xf4, 0x5a, 0xa7, 0xe9, 0x03, 0x8e, 0x91, 0x05, 0x76, 0x37, 0x91, 0x94, 0xba, + 0xdb, 0xaa, 0x8e, 0xf9, 0x8d, 0xe8, 0x2a, 0x7e, 0xd7, 0xad, 0x7d, 0x77, 0xef, 0x12, 0x3c, 0x6d, + 0x7b, 0xdb, 0xc2, 0xe2, 0xa6, 0x2f, 0xcf, 0x8e, 0x8f, 0x5e, 0x22, 0x71, 0x23, 0x81, 0xa7, 0x69, + 0x0c, 0x67, 0x39, 0x14, 0x9c, 0xa9, 0x9d, 0x93, 0x0c, 0xad, 0x96, 0x28, 0x8c, 0x5d, 0x3b, 0x44, + 0x3f, 0x07, 0xbd, 0x67, 0xc1, 0xe1, 0xc1, 0x5c, 0x9b, 0xe6, 0x08, 0xce, 0x1b, 0xa7, 0xab, 0x05, + 0x53, 0x18, 0xde, 0xa6, 0x04, 0xdd, 0xc4, 0xab, 0x04, 0xa9, 0x7d, 0x1d, 0xfd, 0xf8, 0xfc, 0x4f, + 0x04, 0x44, 0x9c, 0x2f, 0x00, 0x0a, 0xa2, 0x34, 0x83, 0xf4, 0xdd, 0xd8, 0x47, 0x69, 0x66, 0xdf, + 0x00, 0x45, 0xc4, 0x11, 0xca, 0xe6, 0x04, 0xa5, 0x24, 0x5b, 0x2d, 0xd1, 0x3c, 0xce, 0xd2, 0x65, + 0x9c, 0xf5, 0x29, 0x3c, 0x8d, 0xc9, 0x54, 0xf9, 0x5b, 0xd5, 0x05, 0xd3, 0xa9, 0x82, 0x03, 0xbd, + 0x15, 0xac, 0xae, 0x6d, 0x0a, 0x4d, 0xae, 0xd5, 0xb2, 0x97, 0xc2, 0xde, 0x54, 0xa3, 0x23, 0xd5, + 0x2d, 0x6c, 0xdb, 0xf2, 0x4a, 0x54, 0xa3, 0xf2, 0x45, 0xb8, 0x16, 0xca, 0x12, 0xdf, 0xa4, 0xde, + 0xea, 0xce, 0x74, 0xd3, 0xce, 0xbd, 0x38, 0x7d, 0x72, 0xf6, 0x9e, 0x6f, 0xd7, 0xf4, 0x2d, 0xd5, + 0x64, 0xd0, 0xa2, 0x50, 0x75, 0x60, 0x63, 0x0e, 0x18, 0xe2, 0x0b, 0xdc, 0x9d, 0x3a, 0x1e, 0xd8, + 0x02, 0xc1, 0x6e, 0xe4, 0x0a, 0x10, 0x68, 0x3e, 0xed, 0x2c, 0xa1, 0x0a, 0x55, 0xfe, 0x39, 0x30, + 0x5a, 0x2e, 0x8b, 0x75, 0x93, 0x7b, 0xca, 0xf9, 0xa3, 0xce, 0xf4, 0x34, 0x46, 0xab, 0x3a, 0x30, + 0xcd, 0x4e, 0xab, 0x1b, 0x28, 0x7c, 0x7e, 0x8a, 0x33, 0xfe, 0x78, 0x09, 0xb8, 0x5e, 0x2a, 0x7a, + 0x67, 0x4b, 0x12, 0x15, 0x1d, 0x32, 0xf1, 0x38, 0xfd, 0xf7, 0xff, 0xf8, 0xbf, 0x36, 0x8a, 0x9e, + 0xf6, 0x6d, 0xbc, 0xc7, 0x0e, 0xb1, 0x13, 0xe0, 0xdf, 0xb6, 0x43, 0x20, 0x44, 0xd0, 0xd1, 0xf1, + 0xc5, 0x8b, 0x77, 0x2f, 0x2e, 0x7e, 0xc9, 0x77, 0xea, 0x0d, 0x81, 0xfe, 0xc3, 0x1b, 0x14, 0x71, + 0xad, 0xb0, 0x7d, 0xa7, 0x84, 0x2f, 0xbe, 0x6d, 0xe2, 0xe5, 0x5f, 0xbb, 0x65, 0x3c, 0x7c, 0x50, + 0xec, 0x98, 0x28, 0x91, 0x6c, 0xef, 0x92, 0x24, 0xd9, 0x37, 0x6c, 0x59, 0xae, 0x19, 0x9f, 0x13, + 0xc2, 0x67, 0xf8, 0x57, 0xef, 0x96, 0x10, 0xfc, 0x6b, 0x15, 0x4d, 0x2d, 0x3c, 0xee, 0xc5, 0xbb, + 0x13, 0xc4, 0xc5, 0xf3, 0xb2, 0xca, 0xf0, 0x81, 0x08, 0x92, 0x34, 0x0d, 0x98, 0x2a, 0xe5, 0x3d, + 0x59, 0x94, 0x2c, 0xbf, 0xd0, 0x7a, 0x68, 0xa3, 0x34, 0x4a, 0x3a, 0xd3, 0xf3, 0xd3, 0x37, 0x88, + 0x89, 0x85, 0x1d, 0x56, 0x5e, 0xf8, 0x62, 0x16, 0xec, 0x20, 0xed, 0x4a, 0xb8, 0x07, 0x79, 0x94, + 0x61, 0x87, 0xef, 0xe4, 0x6e, 0x1d, 0xd9, 0x9d, 0x3a, 0x54, 0x7e, 0x42, 0xfe, 0x58, 0x91, 0xc8, + 0xbd, 0x61, 0xf0, 0xbf, 0x7a, 0xfe, 0x67, 0x43, 0x67, 0xa3, 0xed, 0x58, 0xb8, 0x83, 0x89, 0x4a, + 0xc4, 0x2f, 0x36, 0x6a, 0x01, 0xe5, 0x56, 0xdc, 0xb1, 0x13, 0xcd, 0xf0, 0xe8, 0xb7, 0xd1, 0xcb, + 0xcb, 0xa3, 0xf3, 0x0b, 0xb0, 0xe7, 0x9f, 0x9f, 0xbd, 0x7d, 0x73, 0x9e, 0xd3, 0xcb, 0x2b, 0x92, + 0xce, 0x51, 0x96, 0xd8, 0xbe, 0x1f, 0xb8, 0x0d, 0x62, 0x81, 0x09, 0x36, 0xa1, 0xe8, 0x65, 0x71, + 0x66, 0x87, 0x9d, 0xbc, 0x7f, 0x5a, 0x29, 0x8a, 0x32, 0xa6, 0xd4, 0x5e, 0x2c, 0x43, 0x92, 0xa2, + 0x1b, 0x92, 0x6d, 0xd9, 0x43, 0x56, 0x81, 0x62, 0x53, 0xdf, 0x34, 0xec, 0xd1, 0x41, 0x2c, 0xf6, + 0x60, 0x75, 0x14, 0x65, 0x50, 0x35, 0x98, 0x9f, 0xc7, 0xab, 0x24, 0xbc, 0x41, 0x09, 0x71, 0x49, + 0x70, 0x49, 0x3c, 0x64, 0x43, 0x0d, 0x34, 0x38, 0xbc, 0xc0, 0x96, 0x1d, 0x71, 0x8a, 0x3e, 0x60, + 0xa3, 0x6c, 0x59, 0x2f, 0xa1, 0xf2, 0x44, 0x27, 0x57, 0xdc, 0x82, 0xfc, 0x29, 0x4d, 0xe2, 0x61, + 0xd8, 0xf3, 0x86, 0x8f, 0x24, 0xee, 0xbc, 0xd0, 0x12, 0xd2, 0xe9, 0x59, 0xc3, 0x73, 0x12, 0x65, + 0x1b, 0x1a, 0x25, 0xc4, 0x63, 0x6d, 0x9e, 0xd1, 0x9a, 0xe7, 0xf7, 0x43, 0x8d, 0x56, 0xd9, 0xc8, + 0xe2, 0xd8, 0x75, 0x7c, 0x68, 0xc3, 0x22, 0x21, 0xe2, 0xdd, 0xc9, 0xd5, 0xb4, 0x37, 0x27, 0xc7, + 0x27, 0x2f, 0xde, 0x9d, 0x3c, 0x6d, 0x53, 0xd2, 0xca, 0x31, 0x92, 0xeb, 0x9a, 0x96, 0x46, 0xa1, + 0xae, 0xa3, 0x77, 0x0b, 0xc8, 0xf7, 0x80, 0xe4, 0xfc, 0xe4, 0xf4, 0x62, 0x3b, 0x14, 0xd9, 0xff, + 0x01, 0x28, 0x9e, 0x9e, 0x00, 0x0f, 0x7b, 0xf3, 0xcb, 0x76, 0x48, 0x3c, 0x02, 0x05, 0x1a, 0x41, + 0xf7, 0x7a, 0xf1, 0xf4, 0xe5, 0x49, 0x0b, 0x27, 0x6b, 0x36, 0xed, 0x45, 0x31, 0xb8, 0x01, 0x4e, + 0x63, 0xae, 0x13, 0x81, 0x03, 0xef, 0x2f, 0x82, 0xf9, 0xf9, 0x8b, 0xf3, 0x8b, 0xb3, 0xbb, 0x40, + 0xe6, 0xf1, 0xc1, 0x96, 0x15, 0x6c, 0x6f, 0xc8, 0xe1, 0x85, 0xdf, 0x44, 0xd4, 0x30, 0x36, 0x43, + 0xbc, 0xd5, 0xca, 0x69, 0x49, 0xa8, 0xd8, 0x05, 0x65, 0xab, 0xe9, 0x13, 0xb5, 0x37, 0xee, 0xcd, + 0xf6, 0xce, 0x5f, 0xfc, 0x78, 0x7a, 0xf4, 0x12, 0x9d, 0x1f, 0xbd, 0x7a, 0xfd, 0xf2, 0xa4, 0xe0, + 0x7b, 0x3f, 0xcf, 0xed, 0x8c, 0xea, 0xc3, 0x94, 0xfd, 0xa3, 0xb9, 0x9d, 0xc2, 0x2a, 0x27, 0xde, + 0x1d, 0x3c, 0x90, 0xeb, 0x0d, 0x74, 0x99, 0x6a, 0x0c, 0x50, 0x94, 0xf3, 0x77, 0xc8, 0x80, 0xcd, + 0x7c, 0x50, 0x2c, 0x2a, 0x29, 0x70, 0x41, 0xcd, 0xac, 0x72, 0xc1, 0xa7, 0x01, 0x08, 0x68, 0x67, + 0x45, 0x0d, 0xfc, 0xd8, 0xa7, 0xec, 0x30, 0xca, 0xd0, 0x9b, 0xf3, 0xf3, 0x17, 0x39, 0x17, 0xde, + 0xc4, 0x04, 0xeb, 0x4e, 0xa4, 0xf2, 0x4b, 0x6c, 0x39, 0x1b, 0x7c, 0x47, 0x92, 0x1b, 0x74, 0x45, + 0xec, 0xcf, 0x15, 0x6e, 0xf6, 0x96, 0x1e, 0x09, 0xad, 0xdc, 0x3a, 0xa7, 0xf8, 0xb4, 0x85, 0xd5, + 0xdf, 0x03, 0xc5, 0xff, 0xda, 0x3d, 0x07, 0x06, 0x77, 0x7a, 0xf1, 0xf2, 0x17, 0xf4, 0xfc, 0xe4, + 0xe8, 0xcd, 0xd3, 0x7c, 0xcf, 0x9f, 0x81, 0x7f, 0x9b, 0xa4, 0x59, 0xae, 0x39, 0xd1, 0x68, 0xba, + 0xb8, 0xe1, 0xf7, 0xd7, 0xc1, 0xec, 0x30, 0x44, 0x54, 0xf5, 0xe2, 0x3a, 0x57, 0xfb, 0x56, 0x03, + 0xf0, 0xd5, 0x7d, 0x16, 0xbe, 0x53, 0xd7, 0x29, 0xad, 0xd2, 0xf2, 0x9e, 0x38, 0xa9, 0xf2, 0xcb, + 0x2a, 0x55, 0x24, 0xa3, 0x25, 0x72, 0xb8, 0x46, 0xd9, 0x6e, 0xc8, 0x6d, 0x12, 0x29, 0x75, 0xeb, + 0xba, 0xe2, 0x47, 0xe2, 0x7e, 0xa3, 0x3b, 0x1c, 0x49, 0x35, 0x0f, 0x52, 0x69, 0x4a, 0xfc, 0x6d, + 0x83, 0x33, 0x45, 0xcc, 0x4c, 0x6a, 0x71, 0x26, 0x55, 0x1c, 0xfd, 0x95, 0x84, 0xa3, 0x2d, 0x4a, + 0x8f, 0xf8, 0x25, 0xb2, 0xcd, 0xc8, 0x70, 0x7c, 0x76, 0xfa, 0xee, 0xe4, 0xcd, 0xf9, 0x11, 0xd8, + 0xdf, 0x9c, 0xfe, 0x55, 0xc1, 0x6b, 0x3d, 0x57, 0x1b, 0xf4, 0x5e, 0x7c, 0x8f, 0x0a, 0xcc, 0xb9, + 0x5d, 0xad, 0x37, 0x0e, 0x74, 0x47, 0xec, 0x01, 0x12, 0x9c, 0x36, 0xee, 0xe5, 0x31, 0xf7, 0xb0, + 0x50, 0x1d, 0xc6, 0x9d, 0x83, 0xcb, 0x22, 0x4c, 0x91, 0xbd, 0x5c, 0x12, 0x3b, 0x41, 0x34, 0x3e, + 0x8e, 0xd2, 0x9b, 0xc8, 0xdd, 0x60, 0xa3, 0xe7, 0xee, 0xd8, 0x0d, 0x34, 0x22, 0xa4, 0x65, 0x6d, + 0xd3, 0x1a, 0xeb, 0xd9, 0x5b, 0x5b, 0x09, 0x8f, 0xd5, 0x82, 0x2f, 0xbc, 0x9c, 0xe5, 0x9b, 0xfc, + 0xc1, 0xf4, 0xbb, 0x8d, 0xe8, 0x5f, 0x91, 0x4f, 0x95, 0x77, 0xb9, 0xf7, 0xe3, 0x78, 0x1e, 0xc7, + 0x29, 0x41, 0x36, 0x12, 0x1f, 0x0a, 0x22, 0x0b, 0x84, 0x5c, 0xf3, 0xe5, 0x74, 0xe5, 0x14, 0xef, + 0xd3, 0xf5, 0x43, 0x71, 0x82, 0x58, 0x5e, 0x06, 0xe2, 0x0e, 0xac, 0x32, 0x34, 0xd3, 0x42, 0x1c, + 0x5b, 0x37, 0x54, 0xcc, 0x52, 0xab, 0x5a, 0xe3, 0x7c, 0x5b, 0x85, 0xb6, 0xd5, 0xcf, 0x21, 0x01, + 0x6f, 0x80, 0xa2, 0x48, 0x30, 0x9d, 0x12, 0x2e, 0x0e, 0x10, 0xca, 0x62, 0x44, 0x73, 0x64, 0x50, + 0x66, 0x87, 0x9f, 0x81, 0x98, 0x6d, 0x37, 0x89, 0xd3, 0x94, 0x4a, 0x23, 0xf0, 0x3a, 0xf6, 0x39, + 0x3a, 0xb6, 0x40, 0x47, 0x3f, 0x29, 0x5d, 0xec, 0x1d, 0x4b, 0x87, 0xcb, 0xf7, 0x83, 0x5f, 0x35, + 0x56, 0x9f, 0x8a, 0x8c, 0x82, 0x62, 0x92, 0x5e, 0x1c, 0x85, 0x37, 0x1d, 0x60, 0x20, 0x56, 0x27, + 0xb3, 0x93, 0x19, 0xa4, 0x69, 0x51, 0x60, 0x3b, 0xa0, 0xea, 0x06, 0xcb, 0x80, 0xaa, 0xb1, 0xf4, + 0xa5, 0x46, 0x57, 0xac, 0x21, 0x43, 0xf0, 0xca, 0xab, 0x15, 0xe1, 0x54, 0x74, 0xd3, 0x99, 0x1e, + 0xc6, 0x4b, 0xca, 0x3a, 0x2e, 0xed, 0x70, 0x45, 0xac, 0x4e, 0xb1, 0xcd, 0x49, 0x39, 0x12, 0x6b, + 0x01, 0xe6, 0x36, 0xed, 0x6a, 0x2b, 0xe3, 0x2f, 0x3f, 0x80, 0xd3, 0x98, 0xe6, 0xf6, 0x89, 0xe6, + 0x1b, 0x47, 0xcf, 0x04, 0x77, 0x72, 0xe2, 0xdf, 0x30, 0x4d, 0x84, 0x0e, 0xf3, 0xcf, 0xca, 0x54, + 0x76, 0x9d, 0xbd, 0x8c, 0xe0, 0xc3, 0x67, 0x56, 0x47, 0xe9, 0x40, 0xd6, 0x61, 0x48, 0xa2, 0x19, + 0x24, 0x2d, 0x28, 0xba, 0xdc, 0x41, 0xf4, 0x54, 0xd6, 0x3c, 0x0e, 0x3d, 0x92, 0x58, 0x1d, 0x3e, + 0x44, 0xb1, 0xa9, 0xff, 0xfe, 0xd7, 0xff, 0x06, 0x89, 0x9c, 0x77, 0xdc, 0x32, 0x26, 0x17, 0x3e, + 0xb4, 0x00, 0x52, 0x27, 0x5d, 0x39, 0x0b, 0xf0, 0x3c, 0x17, 0x8c, 0xae, 0xf8, 0xf2, 0x4b, 0x75, + 0xad, 0xcf, 0xa1, 0x70, 0x1e, 0x07, 0xb0, 0xd5, 0x6d, 0x49, 0x43, 0xc6, 0xa5, 0xdb, 0xb2, 0xe9, + 0x1a, 0xd8, 0x40, 0x08, 0xb0, 0xdb, 0x02, 0xcd, 0xb1, 0x85, 0xcf, 0x19, 0x22, 0x3a, 0x40, 0x8a, + 0x2e, 0x23, 0xe7, 0x26, 0xa3, 0xca, 0x21, 0xa5, 0xaf, 0x1a, 0x65, 0x01, 0xa6, 0xfe, 0x17, 0xcb, + 0x9c, 0x5c, 0x0e, 0xff, 0xed, 0x2e, 0xd1, 0x80, 0x8a, 0xef, 0xcc, 0x6c, 0x16, 0x12, 0xa7, 0x27, + 0x47, 0x6f, 0x9e, 0xfc, 0x92, 0xfb, 0xc4, 0x99, 0x8c, 0xe0, 0x3a, 0x62, 0x90, 0x72, 0x25, 0x11, + 0x8e, 0x61, 0x81, 0x92, 0xc8, 0x64, 0xc6, 0xb2, 0x44, 0x66, 0x3b, 0xe4, 0x72, 0x38, 0x48, 0x89, + 0x47, 0x55, 0x7d, 0x96, 0xf8, 0x05, 0x1e, 0x1f, 0x34, 0x87, 0x20, 0x54, 0xe4, 0xf5, 0xa8, 0x8e, + 0xe6, 0xde, 0xd0, 0xf0, 0x7a, 0x1f, 0x5d, 0x14, 0xaa, 0x27, 0xf8, 0x67, 0x53, 0xf0, 0xce, 0x52, + 0x6f, 0xae, 0x1b, 0x47, 0x7e, 0x30, 0x5b, 0x81, 0xfa, 0xbd, 0x4c, 0x48, 0x4a, 0x44, 0x17, 0x6d, + 0x4d, 0x3f, 0x11, 0xbe, 0x18, 0x53, 0xf1, 0x3c, 0x15, 0x81, 0xfc, 0x2a, 0x51, 0xd2, 0x9b, 0xa8, + 0x74, 0xeb, 0xb7, 0x45, 0x3f, 0xf9, 0x9b, 0x0d, 0x7c, 0xd9, 0x12, 0x63, 0x2b, 0x73, 0x52, 0x81, + 0x34, 0x42, 0x02, 0xc6, 0x73, 0xbc, 0x5a, 0x56, 0x47, 0x67, 0x21, 0x5a, 0xc4, 0x1c, 0x62, 0x9b, + 0x3d, 0xcd, 0x79, 0x70, 0x8a, 0xd1, 0x40, 0x3e, 0x35, 0xba, 0xe9, 0x30, 0xcc, 0x22, 0xf6, 0x88, + 0xd5, 0xa1, 0x64, 0x96, 0x76, 0xa6, 0x47, 0xe5, 0x8a, 0xb3, 0x5b, 0xad, 0x8e, 0xaf, 0x0a, 0x55, + 0x6d, 0xe8, 0x91, 0x2a, 0xfe, 0x9d, 0xe9, 0xf3, 0x78, 0x89, 0x58, 0x54, 0x71, 0x6b, 0xa4, 0x6d, + 0x83, 0x02, 0x5b, 0xc9, 0xfb, 0xad, 0x4e, 0x52, 0xd0, 0xf4, 0xf3, 0x56, 0xa5, 0x6e, 0xaf, 0x2b, + 0x72, 0x75, 0xad, 0x5e, 0xd9, 0x4b, 0x50, 0xe9, 0xf3, 0x8d, 0x42, 0x19, 0x60, 0x61, 0x81, 0x5e, + 0xc8, 0x2e, 0xf0, 0xab, 0x5d, 0xc3, 0xaf, 0xef, 0x0d, 0xcd, 0x32, 0xee, 0x54, 0xe4, 0x2f, 0xdc, + 0xa6, 0x9d, 0x80, 0x5b, 0x42, 0x46, 0xfc, 0x67, 0x8b, 0x7d, 0x08, 0x0d, 0x57, 0x91, 0xd0, 0xf4, + 0x2a, 0xc8, 0xe6, 0xa0, 0xbf, 0xe5, 0x20, 0xec, 0x12, 0xea, 0x2a, 0x3e, 0x00, 0xd1, 0x29, 0xfa, + 0x64, 0x97, 0x39, 0x4c, 0x60, 0x1e, 0xb7, 0x92, 0x0f, 0xf3, 0x23, 0x89, 0x66, 0xff, 0x39, 0x4b, + 0x7e, 0xce, 0x62, 0x94, 0x6f, 0x16, 0x95, 0xa6, 0x84, 0xe4, 0x16, 0x10, 0x55, 0xe9, 0x0b, 0x30, + 0x83, 0x08, 0xce, 0x89, 0x53, 0x11, 0x5b, 0x74, 0x4b, 0x55, 0xe7, 0x6c, 0x4e, 0x16, 0xfd, 0x76, + 0xe0, 0x5b, 0x34, 0xe6, 0x8d, 0x7c, 0xa5, 0xfa, 0xa1, 0x89, 0xfb, 0x99, 0x23, 0xea, 0xf4, 0x94, + 0xc2, 0x4a, 0xc3, 0x32, 0x41, 0xca, 0xa9, 0x5d, 0x54, 0x47, 0x37, 0xd3, 0x9c, 0x90, 0xc8, 0x5e, + 0xa1, 0x7c, 0xae, 0x97, 0xb4, 0xa1, 0x67, 0x45, 0xf3, 0x3c, 0x15, 0xec, 0x9f, 0x42, 0xe1, 0x4c, + 0x73, 0x5b, 0x02, 0xd9, 0x49, 0x12, 0x5c, 0x92, 0x7e, 0xb9, 0x12, 0x7f, 0x05, 0xb7, 0xe6, 0x39, + 0x17, 0x7f, 0xdb, 0xec, 0x82, 0xdd, 0x2d, 0xde, 0x23, 0x24, 0xb5, 0x7f, 0x9b, 0x3b, 0x94, 0xfb, + 0x05, 0x9e, 0x9f, 0x1c, 0xbd, 0xbc, 0x78, 0x9e, 0x9b, 0x88, 0x2f, 0x83, 0x4b, 0x82, 0xde, 0x3c, + 0xdb, 0x14, 0x36, 0x28, 0xbe, 0xd0, 0xc1, 0xbc, 0xde, 0xbb, 0x19, 0x06, 0xd5, 0x64, 0xfb, 0x2a, + 0x29, 0xb2, 0x99, 0x17, 0x2e, 0xf2, 0x1a, 0xba, 0x33, 0x87, 0x39, 0x33, 0xb8, 0x0b, 0x61, 0xcc, + 0x5e, 0xf9, 0x63, 0x65, 0x87, 0xd4, 0x23, 0x2c, 0x5a, 0x84, 0xcc, 0xf4, 0xdf, 0xa6, 0xf1, 0x0a, + 0xac, 0x68, 0x83, 0xab, 0x41, 0xd5, 0x1b, 0xda, 0x5c, 0xe9, 0x5a, 0xa0, 0xaf, 0x20, 0xf6, 0xca, + 0x5f, 0xea, 0x64, 0x3d, 0x3f, 0x7f, 0xd1, 0x70, 0xad, 0xf3, 0x48, 0x44, 0x4b, 0x64, 0xa0, 0xd6, + 0x24, 0x8a, 0x83, 0x94, 0x7a, 0xd9, 0x80, 0x3f, 0x6e, 0x09, 0x20, 0xdc, 0xe9, 0x5f, 0x15, 0x12, + 0xa2, 0x77, 0xf1, 0x53, 0xb1, 0xe6, 0x85, 0x47, 0xee, 0xd9, 0x9b, 0x93, 0x9f, 0xde, 0x9e, 0x9c, + 0x1e, 0xb7, 0xfa, 0xe4, 0x18, 0xa4, 0xd5, 0x80, 0x42, 0x6d, 0xb7, 0x85, 0xf0, 0xc2, 0x3d, 0x1c, + 0x27, 0x35, 0x20, 0x9e, 0x1c, 0x9d, 0x3e, 0xfd, 0xf9, 0xc5, 0x53, 0xc0, 0xe8, 0x4d, 0x40, 0x38, + 0x76, 0xe4, 0xd1, 0x64, 0xdb, 0x36, 0x20, 0x3e, 0xff, 0x15, 0x40, 0x9c, 0xbf, 0x7e, 0x73, 0x72, + 0xf4, 0xf4, 0xc5, 0xe9, 0x8f, 0x9b, 0x81, 0x48, 0x97, 0x40, 0x0f, 0x41, 0x34, 0xdb, 0xe4, 0x9b, + 0x64, 0xcd, 0xdc, 0x98, 0xb5, 0x39, 0xae, 0x46, 0x86, 0xbe, 0x1a, 0xb2, 0x8b, 0xf7, 0xe8, 0xf5, + 0xd9, 0xcf, 0x27, 0x6f, 0x36, 0x03, 0xb6, 0x8c, 0xaf, 0xc0, 0x8a, 0xda, 0x4a, 0x8c, 0xbb, 0x39, + 0x42, 0xdb, 0xc0, 0x11, 0x8e, 0xcc, 0x7c, 0x1b, 0xf7, 0x7a, 0x7d, 0x04, 0x49, 0x52, 0x85, 0x3b, + 0xf3, 0x2c, 0xea, 0xd9, 0x01, 0x68, 0x8f, 0x2b, 0x38, 0xb4, 0x9c, 0xb6, 0x7a, 0x31, 0xf9, 0xd8, + 0xb4, 0xc8, 0x46, 0x5a, 0xf7, 0x5e, 0xca, 0x88, 0xdd, 0xdf, 0x31, 0xec, 0x29, 0x9c, 0xef, 0xc9, + 0x61, 0x14, 0x56, 0x92, 0x3f, 0xce, 0x83, 0x34, 0xf5, 0xd5, 0x64, 0x31, 0x88, 0x32, 0xae, 0x22, + 0x70, 0xab, 0x4d, 0x5d, 0xa5, 0xd4, 0x9c, 0x6c, 0xe9, 0x86, 0x47, 0x5d, 0x76, 0xe8, 0x82, 0xb9, + 0x05, 0x5a, 0x3b, 0x79, 0xca, 0x3c, 0x06, 0x6f, 0xde, 0xef, 0xd6, 0x93, 0x1f, 0xc6, 0x71, 0xfb, + 0xa4, 0x9e, 0xc1, 0x93, 0x66, 0x3f, 0x5f, 0x1f, 0x03, 0x6c, 0xf9, 0x56, 0xc8, 0xb7, 0xe1, 0x0d, + 0x04, 0xff, 0x5e, 0xfe, 0xd2, 0x08, 0x9b, 0x9f, 0x43, 0x45, 0x9d, 0x9e, 0x67, 0xdf, 0x6c, 0x8d, + 0x05, 0xd6, 0xa0, 0xf9, 0xcb, 0xe2, 0x80, 0xf5, 0x59, 0xd6, 0x44, 0xd2, 0x70, 0x50, 0x37, 0x7a, + 0x73, 0x60, 0xe7, 0x2c, 0x1a, 0xf8, 0x5f, 0x16, 0xfb, 0x7b, 0x7f, 0x67, 0xd4, 0xef, 0xe2, 0xfd, + 0x2e, 0x31, 0xbf, 0x55, 0x42, 0xee, 0x13, 0x10, 0xfe, 0x2b, 0xf4, 0xad, 0x32, 0x09, 0x75, 0xa3, + 0xdd, 0x52, 0xfd, 0x54, 0x47, 0x6b, 0x82, 0x5f, 0x1b, 0x0a, 0x5d, 0x3c, 0x7f, 0x71, 0x2e, 0xc4, + 0xfb, 0xe7, 0xea, 0xf4, 0x3c, 0x8b, 0x13, 0x7b, 0x46, 0x30, 0xa2, 0xcc, 0x93, 0xfa, 0x33, 0xf9, + 0xe1, 0xa4, 0xdc, 0x54, 0x06, 0x5f, 0xf9, 0x55, 0x1e, 0x72, 0x61, 0x07, 0xcd, 0x50, 0x90, 0xa2, + 0x55, 0x1a, 0x44, 0x33, 0x8c, 0x3e, 0x13, 0xb2, 0xcc, 0xbd, 0x18, 0x88, 0x87, 0xa2, 0x30, 0xca, + 0x56, 0x11, 0xf3, 0x9f, 0xb0, 0x29, 0x61, 0xda, 0x2f, 0x24, 0x36, 0x81, 0x26, 0x04, 0xaa, 0x08, + 0xf1, 0xd0, 0x6a, 0xe9, 0xd9, 0x19, 0xd9, 0xe0, 0xfe, 0x16, 0xf7, 0xbc, 0x72, 0xd4, 0x4f, 0x0c, + 0xf4, 0x57, 0x1e, 0xd4, 0x12, 0x33, 0x29, 0x0d, 0xbf, 0x3a, 0x79, 0x75, 0xf6, 0x86, 0xaa, 0xee, + 0x4f, 0xdf, 0x9c, 0x9d, 0xbd, 0x6a, 0x93, 0x1c, 0xb5, 0xce, 0x8f, 0xe7, 0xc4, 0xfd, 0xbc, 0x25, + 0x1d, 0xef, 0x0e, 0x13, 0xa3, 0x7e, 0xe6, 0xf0, 0x6e, 0x9d, 0xb8, 0x26, 0xe4, 0x9e, 0x9e, 0xbc, + 0x7b, 0x71, 0x7c, 0x82, 0xb6, 0x44, 0x08, 0x1b, 0xdf, 0x4f, 0xe0, 0x9e, 0x9d, 0x76, 0x51, 0xdc, + 0x6c, 0x6d, 0xb3, 0xda, 0x3f, 0x9d, 0x29, 0xab, 0xb9, 0xbf, 0x3d, 0x42, 0xb8, 0x9b, 0xea, 0x74, + 0x02, 0x2b, 0xfc, 0x7a, 0x2b, 0xb0, 0x7e, 0x42, 0xa8, 0x1b, 0x67, 0x79, 0x27, 0xa0, 0x21, 0x78, + 0x27, 0xc1, 0x16, 0xe2, 0x3f, 0xee, 0x52, 0x1e, 0x76, 0x5a, 0xd5, 0x17, 0xe7, 0xaf, 0x5f, 0x1e, + 0xfd, 0x82, 0x9e, 0xbd, 0x7c, 0x7b, 0xfe, 0x7c, 0x3b, 0x9c, 0xe1, 0x2a, 0x9d, 0xdf, 0x09, 0x23, + 0x44, 0x73, 0xd3, 0xce, 0x94, 0xfe, 0xf9, 0x2b, 0xe0, 0x3b, 0x79, 0x77, 0x72, 0x7a, 0x81, 0x7e, + 0x7a, 0x7b, 0xf2, 0xf6, 0x64, 0x2b, 0x74, 0x50, 0x65, 0x2c, 0xbd, 0x13, 0xba, 0x3f, 0x56, 0x04, + 0x58, 0x5c, 0xbc, 0xca, 0x1c, 0x38, 0xee, 0xf5, 0x57, 0x00, 0xf8, 0xe4, 0xe8, 0xe2, 0xe2, 0xe4, + 0xcd, 0x2f, 0xe8, 0xe2, 0xcd, 0xc9, 0xe9, 0xd3, 0xad, 0x20, 0xe6, 0x29, 0xd9, 0x77, 0x01, 0x99, + 0xac, 0x22, 0x96, 0x4c, 0xda, 0x34, 0x82, 0xbe, 0x19, 0x25, 0x5f, 0x1e, 0x5d, 0x9c, 0x9c, 0x5f, + 0xa0, 0x6d, 0xa9, 0x01, 0xe5, 0x92, 0xde, 0x95, 0x19, 0xd0, 0x68, 0xd9, 0x63, 0xfe, 0x73, 0x2a, + 0x2e, 0xe1, 0x50, 0x22, 0xe3, 0x88, 0xdb, 0xd3, 0x03, 0xb6, 0x38, 0x04, 0x2a, 0xc7, 0x8e, 0xef, + 0xe6, 0x17, 0x2d, 0x87, 0x8c, 0xab, 0xe1, 0x8c, 0x1d, 0x14, 0x89, 0x8b, 0x9f, 0xcf, 0xaa, 0x99, + 0x44, 0xdc, 0x50, 0xcc, 0xa5, 0xf8, 0xd7, 0x98, 0xd0, 0xa2, 0x4e, 0x50, 0x72, 0xc1, 0x20, 0x5a, + 0xe5, 0xa0, 0x8a, 0x4a, 0x81, 0x2c, 0x88, 0xfa, 0xff, 0xe3, 0xd2, 0x3d, 0xcf, 0x5f, 0x84, 0x46, + 0x4f, 0x18, 0xb6, 0x56, 0x67, 0xb2, 0x33, 0xda, 0xfd, 0x15, 0x7b, 0x71, 0x0e, 0xb4, 0x8f, 0x9e, + 0x1e, 0xfd, 0x52, 0x6c, 0x06, 0x4f, 0x93, 0x6a, 0xdb, 0x8c, 0xcd, 0x2b, 0x7d, 0x45, 0xc8, 0xe7, + 0xff, 0x9f, 0xad, 0xf3, 0x76, 0x2d, 0xea, 0x2b, 0x56, 0x99, 0xfa, 0xbc, 0xee, 0xbd, 0xc0, 0xcc, + 0x81, 0xc7, 0x94, 0x9e, 0x72, 0x8d, 0xc1, 0x6d, 0x8c, 0x9c, 0x1b, 0xe6, 0xbf, 0xf3, 0xf8, 0x21, + 0x9e, 0x16, 0xb5, 0xb9, 0xfa, 0x05, 0xa3, 0x22, 0x88, 0x42, 0xb5, 0xde, 0xda, 0x84, 0xf2, 0x4c, + 0x81, 0xe6, 0x47, 0x8f, 0x3a, 0x0d, 0x5a, 0x2f, 0x1f, 0x6d, 0x8a, 0x30, 0xbf, 0x0c, 0xd2, 0x8c, + 0x44, 0x42, 0xf2, 0xf7, 0xd6, 0xe8, 0xc2, 0xfd, 0x17, 0xb3, 0x48, 0x2f, 0xba, 0xe7, 0x72, 0xd6, + 0x15, 0x94, 0x0a, 0x03, 0x69, 0x4b, 0xb2, 0xde, 0x92, 0x98, 0x51, 0x59, 0x2d, 0x72, 0x0d, 0xa7, + 0xb9, 0x3b, 0xd3, 0x13, 0xfa, 0x17, 0x9d, 0x3e, 0xfd, 0xfb, 0xf9, 0xd9, 0x29, 0xfa, 0xf7, 0xff, + 0xf8, 0x5f, 0xb5, 0x48, 0x46, 0x73, 0x9d, 0xc5, 0x0f, 0x49, 0x35, 0x97, 0xba, 0xf2, 0x74, 0xd3, + 0x6a, 0x83, 0xe5, 0x53, 0x4d, 0x9c, 0xda, 0xbe, 0xba, 0xbb, 0xf0, 0xf3, 0x4a, 0xdd, 0x88, 0x2a, + 0x63, 0xaf, 0xc4, 0x82, 0x2b, 0xdb, 0x92, 0xbf, 0x54, 0x5b, 0x9c, 0xa2, 0x2f, 0x78, 0xf3, 0xdb, + 0xac, 0xc8, 0x5c, 0x17, 0xda, 0xab, 0xef, 0x61, 0x79, 0x5a, 0x12, 0xe5, 0xc3, 0x35, 0x78, 0xcf, + 0xdf, 0x9a, 0x51, 0xdb, 0xe9, 0x73, 0xb6, 0xc0, 0x28, 0xd7, 0x2c, 0xc5, 0x50, 0x73, 0x9b, 0xfa, + 0xd9, 0x08, 0x2d, 0xcb, 0x9d, 0xe9, 0x99, 0xef, 0x97, 0xe1, 0xe4, 0xea, 0x53, 0x45, 0x35, 0x3b, + 0x53, 0x45, 0x35, 0x37, 0x3d, 0x37, 0x14, 0xb5, 0x33, 0x35, 0x14, 0x75, 0xd3, 0x73, 0x55, 0xd6, + 0xcd, 0xce, 0x14, 0xfe, 0x6d, 0x06, 0xac, 0x5b, 0xa3, 0xc8, 0x7c, 0x5a, 0xff, 0x20, 0x37, 0x34, + 0xdf, 0x07, 0xb9, 0xb6, 0x47, 0x22, 0x97, 0xb4, 0x4e, 0x8b, 0x3f, 0x6b, 0x4c, 0x49, 0x37, 0xe4, + 0xce, 0x54, 0x37, 0x64, 0xb4, 0x48, 0x37, 0xc1, 0x35, 0x80, 0x26, 0x83, 0xad, 0x4d, 0x46, 0xb2, + 0xdc, 0x99, 0x8e, 0xe4, 0x6d, 0x4d, 0x14, 0x15, 0xda, 0xc0, 0xbf, 0x95, 0x46, 0xbb, 0x4c, 0xf0, + 0x35, 0x35, 0xf8, 0x96, 0x49, 0x0c, 0x9f, 0xec, 0x69, 0x9d, 0x1d, 0x35, 0x09, 0x7b, 0xbc, 0x45, + 0xdb, 0xb6, 0x3d, 0xb1, 0x43, 0x3b, 0x72, 0x89, 0x87, 0xfe, 0xf3, 0x3f, 0xd0, 0x40, 0x46, 0x29, + 0x37, 0xf7, 0x36, 0x02, 0xdb, 0x99, 0x3e, 0x83, 0x3a, 0xd4, 0xd0, 0xdc, 0x80, 0x4a, 0x48, 0x77, + 0xb4, 0x57, 0x3b, 0xb9, 0x80, 0x86, 0x37, 0xb4, 0xb6, 0x01, 0x76, 0x99, 0x68, 0xde, 0x47, 0x8e, + 0xa0, 0xa8, 0xbb, 0x38, 0x9a, 0x4b, 0x87, 0x34, 0x27, 0xa0, 0x55, 0x79, 0x2d, 0x8d, 0x24, 0x1e, + 0x7c, 0x8c, 0x56, 0x0b, 0x07, 0xb2, 0x35, 0x16, 0x50, 0x43, 0x41, 0xa6, 0xe9, 0x03, 0x80, 0x58, + 0xb2, 0x2c, 0x43, 0x95, 0x02, 0xb2, 0xa4, 0x39, 0x05, 0xb4, 0x3f, 0x16, 0x95, 0x8c, 0x56, 0x0b, + 0x02, 0x0a, 0x69, 0x35, 0xaf, 0x40, 0x46, 0x16, 0x5a, 0x45, 0x9f, 0xa3, 0xf8, 0x8a, 0x4a, 0xd7, + 0x5d, 0x00, 0x0e, 0x03, 0x87, 0x95, 0xbc, 0x40, 0xb1, 0xef, 0x43, 0xb9, 0xc2, 0xee, 0xe2, 0xdd, + 0x1d, 0x90, 0x17, 0xaf, 0xb4, 0x02, 0xdf, 0xd3, 0xe4, 0x1c, 0x7e, 0xed, 0x2e, 0xe8, 0xb7, 0xc1, + 0x58, 0x1e, 0xd3, 0xcc, 0xbf, 0x8f, 0x05, 0x99, 0xfc, 0x4d, 0xb8, 0x96, 0x49, 0x70, 0x09, 0x1c, + 0x96, 0xc3, 0xe2, 0x82, 0x79, 0xed, 0xc4, 0xd7, 0xb9, 0xd2, 0xf1, 0x1c, 0x32, 0xc7, 0x72, 0xef, + 0x81, 0x13, 0x7b, 0x01, 0x44, 0x00, 0xe7, 0x41, 0xc8, 0x9c, 0x07, 0x17, 0xcf, 0x2e, 0xc0, 0xd3, + 0x00, 0x75, 0x38, 0x85, 0xcc, 0xeb, 0x36, 0x98, 0x1a, 0x32, 0x81, 0x97, 0xcf, 0x69, 0x8a, 0x83, + 0xfc, 0xc1, 0xf4, 0xee, 0x83, 0x1c, 0xf5, 0xc3, 0x53, 0x95, 0xf4, 0x8e, 0xe9, 0xb9, 0x7d, 0x49, + 0x04, 0x46, 0xd9, 0x76, 0x70, 0xa3, 0x91, 0x48, 0xd1, 0x26, 0x26, 0xda, 0x0e, 0xb9, 0x6e, 0x4a, + 0xb4, 0x2c, 0xeb, 0xf8, 0x6c, 0xe6, 0xef, 0x27, 0xef, 0x5f, 0xbf, 0x7c, 0x71, 0xfc, 0xe2, 0x02, + 0xc1, 0xf9, 0x35, 0xe1, 0xdc, 0xda, 0xf4, 0x82, 0x16, 0x8d, 0x25, 0x09, 0xd3, 0x79, 0xa0, 0x71, + 0x4a, 0x92, 0x47, 0xa9, 0x10, 0x10, 0xa6, 0x47, 0xd6, 0xa6, 0x6f, 0x20, 0xce, 0x91, 0x66, 0xc4, + 0x43, 0x90, 0x85, 0x83, 0xae, 0xe6, 0x84, 0xe6, 0x37, 0xa0, 0xcc, 0x5e, 0x22, 0x87, 0x84, 0xf1, + 0x55, 0x1f, 0x1d, 0xc7, 0x71, 0xe2, 0x05, 0x11, 0xb8, 0x6f, 0x30, 0x1c, 0x0b, 0x5a, 0x25, 0xb6, + 0x7b, 0xc3, 0x9c, 0x3c, 0x2c, 0x10, 0x89, 0xd2, 0xb9, 0x9d, 0xd0, 0xe0, 0x6d, 0xc2, 0x35, 0x97, + 0x64, 0x41, 0x3c, 0xc4, 0x4a, 0xc8, 0xa0, 0xd4, 0xbe, 0x0c, 0xa2, 0x19, 0x93, 0xb0, 0xf7, 0xc2, + 0xa6, 0x1c, 0x54, 0x28, 0xe1, 0x99, 0x34, 0x91, 0x0a, 0xd1, 0x5f, 0xc4, 0xe3, 0xc8, 0xf5, 0x22, + 0x72, 0xc3, 0x95, 0x47, 0x87, 0x23, 0x1e, 0x72, 0x4b, 0x98, 0x51, 0xc0, 0x03, 0xb7, 0xa0, 0x86, + 0x3d, 0x4a, 0xcb, 0xa3, 0x3d, 0x15, 0x0c, 0x6b, 0x35, 0x17, 0x0b, 0x08, 0xc4, 0x53, 0x5d, 0x45, + 0x7c, 0x3a, 0xc9, 0x97, 0xae, 0xe6, 0x3e, 0xda, 0xe1, 0x54, 0x5e, 0x63, 0x84, 0x4e, 0xbe, 0x11, + 0xc2, 0x06, 0x15, 0x8a, 0x50, 0x7b, 0x10, 0xa5, 0xa1, 0x56, 0x88, 0x08, 0x53, 0x1d, 0x85, 0x7e, + 0xc4, 0x8f, 0xea, 0x12, 0xdb, 0x62, 0xac, 0x27, 0x4f, 0xd1, 0xdb, 0xd7, 0x4f, 0x8f, 0x2e, 0x4e, + 0x72, 0x1c, 0x7a, 0xc1, 0x5d, 0x78, 0x36, 0x12, 0xb4, 0x85, 0x7e, 0xb4, 0x5c, 0x51, 0x3f, 0x2e, + 0xcd, 0xcb, 0x62, 0x48, 0x74, 0x51, 0xfa, 0x0a, 0xe9, 0xa6, 0xb0, 0x14, 0x39, 0x2a, 0x50, 0x31, + 0x02, 0x36, 0x83, 0xa1, 0xda, 0x0b, 0x81, 0xe4, 0xed, 0x39, 0x3b, 0x08, 0x02, 0xf1, 0x49, 0x38, + 0x6b, 0x96, 0x23, 0xc9, 0x55, 0xc2, 0xfc, 0x02, 0xf0, 0x5e, 0x10, 0xb1, 0xf4, 0x12, 0x08, 0x78, + 0xa3, 0x34, 0xcc, 0x0f, 0x3f, 0xb6, 0x60, 0x48, 0xfe, 0x71, 0xc2, 0x1c, 0x37, 0xd8, 0x6f, 0x38, + 0xb9, 0xb6, 0xcc, 0xac, 0x0e, 0x40, 0xda, 0xa1, 0xbb, 0x14, 0x24, 0x80, 0x26, 0xbb, 0x6c, 0x4e, + 0x95, 0xea, 0xdf, 0x91, 0x24, 0xf0, 0x6f, 0xd0, 0x5e, 0xee, 0xcc, 0x14, 0xf6, 0xa4, 0x4e, 0xed, + 0xdf, 0x42, 0xc4, 0x4f, 0x8f, 0x4e, 0x7f, 0x3c, 0x79, 0x83, 0x7e, 0x3d, 0x3b, 0x2d, 0x96, 0xfe, + 0x04, 0xf8, 0x96, 0xe0, 0x70, 0xe0, 0x1a, 0x55, 0xbe, 0xe0, 0x4f, 0xe9, 0x77, 0x7f, 0x60, 0xc1, + 0xf8, 0x03, 0x14, 0x41, 0x49, 0x2b, 0x44, 0x28, 0xbb, 0x43, 0x41, 0x06, 0x79, 0x4b, 0x41, 0x2a, + 0x10, 0x23, 0x03, 0x04, 0x2d, 0x49, 0xb2, 0xb0, 0x23, 0x76, 0xf0, 0xce, 0x0d, 0x89, 0x9d, 0xa4, + 0x8c, 0xee, 0xd9, 0x81, 0xd3, 0xa5, 0x9d, 0x40, 0xb5, 0x8c, 0x83, 0x68, 0x79, 0x80, 0x3e, 0xc5, + 0xab, 0x24, 0xb2, 0xc3, 0xcd, 0x59, 0x4d, 0xf5, 0x02, 0x5f, 0xa8, 0xf2, 0xb5, 0xa3, 0x1a, 0x06, + 0xd2, 0xb1, 0x3a, 0xd3, 0x63, 0xf8, 0x83, 0xea, 0x73, 0xda, 0x86, 0xe8, 0xdb, 0xd2, 0xd1, 0x6a, + 0x31, 0xe1, 0xbc, 0x74, 0x1a, 0xaa, 0x7e, 0x80, 0xef, 0x6e, 0x2f, 0x8b, 0x50, 0x4f, 0xed, 0xee, + 0x63, 0xf1, 0xcc, 0x24, 0xa2, 0xab, 0x22, 0x1e, 0xa0, 0x28, 0xbf, 0x4c, 0xf7, 0x17, 0xd7, 0x36, + 0x69, 0x11, 0x5c, 0xfc, 0xcc, 0x3d, 0x9d, 0x70, 0xfd, 0xcc, 0x3d, 0x3f, 0x85, 0x4c, 0x9f, 0x01, + 0xe5, 0x85, 0x34, 0x89, 0x72, 0x7e, 0x0c, 0x44, 0x26, 0x54, 0x41, 0xa9, 0x1f, 0xe3, 0xf7, 0x42, + 0x8e, 0x9d, 0x5e, 0x36, 0x7d, 0x16, 0x24, 0x8b, 0x2b, 0x3b, 0x21, 0x87, 0x07, 0x5e, 0x36, 0x3d, + 0xf4, 0xbc, 0xb2, 0x43, 0x9f, 0x3f, 0xe1, 0xfe, 0x3e, 0xcf, 0xab, 0xc4, 0xdf, 0xbc, 0x6c, 0xfa, + 0x64, 0x15, 0x84, 0x5e, 0xf3, 0x3d, 0x07, 0x6e, 0x6f, 0x7c, 0xe9, 0xf5, 0xca, 0x09, 0x03, 0x17, + 0x7d, 0x26, 0x37, 0xcd, 0x37, 0x3f, 0x93, 0x9b, 0xc6, 0x7b, 0x07, 0x5e, 0x78, 0xaf, 0x64, 0x81, + 0xea, 0x87, 0xe0, 0xfe, 0x32, 0xd1, 0xfb, 0xfa, 0xcd, 0xc9, 0xf9, 0xc9, 0xe9, 0x71, 0x41, 0xb2, + 0x45, 0x9a, 0x5b, 0x29, 0x6d, 0x72, 0x72, 0xfd, 0x09, 0x7c, 0xb4, 0xc8, 0x46, 0x34, 0x1e, 0x59, + 0x08, 0xcd, 0x38, 0x3f, 0xde, 0x50, 0x6c, 0x10, 0xcb, 0xf3, 0x81, 0xec, 0x45, 0x2f, 0x48, 0x5d, + 0x28, 0x8f, 0x0b, 0xb2, 0x78, 0x33, 0x09, 0xb6, 0x89, 0x16, 0xd6, 0x7b, 0x71, 0xd2, 0x9b, 0x41, + 0x85, 0xa2, 0xf8, 0xea, 0x4e, 0x81, 0xf2, 0xd5, 0x2b, 0xd1, 0x7e, 0x6a, 0x9e, 0xa1, 0x67, 0xe3, + 0xe8, 0xfc, 0x6a, 0x89, 0x20, 0x7a, 0x50, 0x1e, 0x98, 0xb7, 0x97, 0x3d, 0xdb, 0xf3, 0x12, 0x92, + 0x96, 0x75, 0x4d, 0x88, 0x87, 0xb2, 0x79, 0x12, 0xaf, 0x66, 0x73, 0xd1, 0x65, 0x83, 0xec, 0x0c, + 0x29, 0x23, 0xb5, 0xaf, 0x0c, 0xcc, 0xbe, 0xde, 0x57, 0x84, 0x55, 0x11, 0x53, 0xdf, 0x2b, 0x75, + 0x0b, 0x3b, 0x77, 0x31, 0x2d, 0xb6, 0x66, 0x3c, 0x9d, 0xb2, 0x3c, 0x1e, 0x7f, 0x13, 0xb9, 0xd5, + 0x25, 0xbb, 0x3f, 0xeb, 0x4b, 0x48, 0xb6, 0x4a, 0xa2, 0x9e, 0x5d, 0x1c, 0xba, 0xcf, 0xab, 0xeb, + 0xbc, 0xa1, 0x0f, 0x58, 0x3a, 0x5c, 0xbe, 0x1a, 0x55, 0x9f, 0xc7, 0x5f, 0xbd, 0x3f, 0xaf, 0xdf, + 0xbc, 0x78, 0x77, 0x74, 0x5c, 0x98, 0xfe, 0x2f, 0xe9, 0x09, 0xb7, 0x5a, 0x14, 0x2f, 0x47, 0xd5, + 0xb7, 0x4b, 0x80, 0x4c, 0x51, 0xe5, 0xf2, 0x94, 0x39, 0x68, 0x75, 0x9f, 0xc9, 0x32, 0x2b, 0x14, + 0x29, 0xae, 0x4d, 0xf2, 0x04, 0x3d, 0x8e, 0xeb, 0x7d, 0xc4, 0x98, 0x3b, 0x64, 0xec, 0x51, 0x35, + 0x92, 0x70, 0xf4, 0x45, 0x61, 0xf0, 0x99, 0xec, 0x2e, 0x46, 0x78, 0x96, 0x3c, 0xf4, 0x55, 0xf8, + 0x11, 0xf2, 0x7d, 0x61, 0x23, 0xec, 0x24, 0x37, 0x36, 0xad, 0x57, 0x51, 0x0f, 0x73, 0xf3, 0x72, + 0x1d, 0x3d, 0x39, 0x7b, 0x7b, 0xd1, 0xe2, 0x27, 0x69, 0xab, 0x26, 0xc5, 0x16, 0xed, 0xcc, 0xf7, + 0xe9, 0xa7, 0x7e, 0xfc, 0x20, 0x49, 0xb3, 0x92, 0x9c, 0xcb, 0xb2, 0x26, 0xff, 0xf9, 0x1f, 0xe8, + 0xb2, 0x74, 0x33, 0xda, 0xcb, 0x65, 0x0f, 0x8e, 0x46, 0x50, 0xbd, 0x4f, 0x88, 0xd7, 0x38, 0x09, + 0x33, 0xd3, 0x09, 0x75, 0x56, 0xe6, 0xdd, 0xf4, 0x3f, 0xf1, 0x83, 0xfe, 0xbc, 0xf4, 0x8c, 0x93, + 0xc0, 0x62, 0xb5, 0xbc, 0x0f, 0xa3, 0xbc, 0x7a, 0x71, 0x81, 0xfe, 0xf3, 0xff, 0x41, 0x2f, 0x03, + 0x7b, 0x81, 0x8e, 0xe3, 0x2c, 0x83, 0x24, 0xb3, 0xed, 0x7e, 0xc3, 0xdd, 0xd2, 0xbd, 0xd9, 0x65, + 0x5e, 0x4b, 0x88, 0xb7, 0x62, 0xf5, 0xc0, 0xe8, 0x37, 0xbc, 0x6a, 0xf5, 0x3a, 0xd8, 0xdd, 0x6a, + 0x05, 0x8a, 0xfc, 0x5e, 0x99, 0x20, 0x10, 0x12, 0xcf, 0xb9, 0xa9, 0x37, 0xc8, 0x59, 0xc5, 0xdf, + 0x1a, 0x67, 0x14, 0xcb, 0x8f, 0xdd, 0x6d, 0xf1, 0xff, 0x82, 0x20, 0x44, 0xa7, 0x27, 0x17, 0x3f, + 0x9f, 0xbd, 0xf9, 0x07, 0x8f, 0x7a, 0xb7, 0x40, 0xb1, 0xb5, 0x94, 0x87, 0x5a, 0x43, 0xd4, 0x6a, + 0x4a, 0x72, 0x15, 0x1a, 0xfa, 0xd9, 0xbc, 0xb6, 0x79, 0xe6, 0x4f, 0xc4, 0x74, 0x88, 0x63, 0xb8, + 0x55, 0x54, 0xfe, 0xa0, 0xd4, 0xdf, 0x99, 0xfe, 0xbf, 0xff, 0xab, 0x2d, 0x79, 0xbb, 0xad, 0x08, + 0x0a, 0xff, 0xfa, 0xdb, 0xc6, 0x42, 0x45, 0xc8, 0xcd, 0xb9, 0x67, 0x19, 0x19, 0x17, 0x8b, 0xd4, + 0xbc, 0x7d, 0xf3, 0x06, 0x7c, 0xe3, 0xdb, 0x8b, 0xd5, 0x34, 0x06, 0xcb, 0x63, 0xe4, 0xcc, 0x0b, + 0x4e, 0x6b, 0xf3, 0x54, 0xad, 0x1d, 0x01, 0x68, 0x6a, 0x92, 0x88, 0xbd, 0x54, 0x1c, 0x98, 0xdc, + 0xfa, 0xa3, 0xe7, 0x3c, 0x8a, 0xb2, 0x24, 0x29, 0x78, 0x4b, 0xf3, 0xca, 0x28, 0x74, 0x07, 0x10, + 0xa8, 0x34, 0x35, 0xc3, 0x5f, 0x50, 0xfe, 0x2b, 0x6f, 0xd2, 0xb6, 0x56, 0x87, 0xfd, 0x66, 0xfb, + 0x04, 0x3e, 0xe7, 0x52, 0xeb, 0x17, 0xcf, 0x7e, 0x68, 0x6a, 0x87, 0x7e, 0xb4, 0x0d, 0xc8, 0x32, + 0x24, 0x19, 0xb1, 0x3a, 0x31, 0x54, 0xb5, 0xac, 0x78, 0x6d, 0x9e, 0x43, 0x6d, 0x16, 0x0a, 0xc6, + 0x56, 0xb0, 0xf3, 0xaf, 0xbd, 0x75, 0xa6, 0x0c, 0xe4, 0xfc, 0xba, 0x0e, 0x76, 0x25, 0x13, 0xab, + 0xfc, 0x40, 0x5c, 0xc5, 0xde, 0x6d, 0xf4, 0xc9, 0x27, 0x55, 0x5e, 0xb3, 0x89, 0x95, 0xd7, 0xc2, + 0x9c, 0x06, 0x7a, 0x7d, 0x4e, 0x11, 0xb9, 0x12, 0xba, 0xaa, 0x4c, 0xee, 0x25, 0x01, 0x2f, 0x06, + 0x75, 0x85, 0xb3, 0x22, 0x17, 0x11, 0x82, 0x83, 0x92, 0x39, 0x46, 0x76, 0x36, 0x60, 0xbc, 0x08, + 0x24, 0x54, 0xce, 0x85, 0x54, 0x22, 0x8a, 0xd6, 0x10, 0x99, 0x48, 0x89, 0x67, 0x75, 0x7c, 0x3b, + 0x84, 0xbc, 0xce, 0xf3, 0x79, 0x45, 0xcb, 0x10, 0x39, 0x49, 0x41, 0xa9, 0xe5, 0xa7, 0xe9, 0x3a, + 0xd3, 0xf3, 0xf3, 0x17, 0x4f, 0xc7, 0x48, 0xf9, 0xf7, 0xbf, 0xfe, 0xa7, 0xa6, 0xb2, 0x33, 0x2a, + 0x7d, 0xf4, 0x9a, 0x83, 0x3e, 0x66, 0x70, 0x62, 0x38, 0x9a, 0x61, 0xfe, 0xfb, 0x5f, 0xff, 0x73, + 0xa0, 0xd3, 0x6c, 0x56, 0xdb, 0x85, 0xac, 0x39, 0x6e, 0xdd, 0xf0, 0x63, 0x19, 0xab, 0x25, 0xc4, + 0x10, 0xd2, 0xa2, 0xbe, 0x0e, 0x87, 0x95, 0x59, 0x36, 0x7d, 0x41, 0xd7, 0x6d, 0xe1, 0x26, 0x85, + 0x3f, 0x66, 0x2b, 0xb1, 0xb7, 0xcb, 0xa8, 0x82, 0x54, 0xc0, 0x2b, 0x0a, 0x95, 0xc0, 0xe8, 0xdf, + 0x86, 0xce, 0xd0, 0x7a, 0x4e, 0x68, 0x6b, 0xa9, 0x1e, 0xfb, 0x92, 0x70, 0x8f, 0xd3, 0x1e, 0x4a, + 0x08, 0x3d, 0x82, 0xd6, 0xca, 0x23, 0x04, 0x23, 0xb4, 0x8d, 0x5f, 0xb0, 0x6f, 0xcc, 0xd5, 0xf7, + 0x8f, 0xdd, 0xe3, 0xba, 0xc8, 0xdf, 0x36, 0x9a, 0x39, 0xa7, 0x27, 0xef, 0x2f, 0xd0, 0xf9, 0xc5, + 0xc9, 0xeb, 0xf3, 0xb6, 0x4a, 0x48, 0xac, 0x9b, 0x9c, 0x8b, 0xbe, 0x21, 0x9c, 0xf3, 0x80, 0xea, + 0x40, 0x0f, 0xc7, 0xcc, 0x0b, 0x32, 0xa2, 0x22, 0x32, 0x1f, 0x86, 0x97, 0xb0, 0xac, 0xf5, 0x02, + 0x5e, 0x49, 0xd8, 0x81, 0x30, 0xa0, 0xe9, 0x01, 0x79, 0xde, 0x3f, 0xdf, 0xde, 0x2c, 0xce, 0xd7, + 0xa0, 0x7f, 0x78, 0x10, 0x06, 0xb4, 0x99, 0x30, 0x20, 0xcd, 0xc8, 0x9f, 0xc7, 0x11, 0x41, 0xbc, + 0x8e, 0xd8, 0x0a, 0x8e, 0x63, 0x36, 0xe1, 0x10, 0x5f, 0xb6, 0x3d, 0xda, 0x7f, 0x44, 0xae, 0xd0, + 0xd3, 0xe7, 0xc7, 0xaf, 0xd1, 0x8b, 0xd7, 0x45, 0xcd, 0x25, 0x5e, 0x7b, 0x2d, 0xf2, 0x18, 0x65, + 0x04, 0x54, 0xe9, 0xa1, 0x5d, 0x71, 0xa5, 0x87, 0xf7, 0x73, 0x10, 0x87, 0xc2, 0xe2, 0xb5, 0x4c, + 0x8a, 0x9d, 0xcf, 0x7f, 0x11, 0xc1, 0xa9, 0x21, 0x6a, 0x79, 0x33, 0x37, 0x08, 0xf5, 0x8b, 0x70, + 0xf5, 0x69, 0x61, 0xdf, 0xe4, 0x3e, 0x24, 0xf4, 0xfc, 0xe2, 0xe2, 0x35, 0x7a, 0x62, 0xa7, 0x81, + 0x0b, 0xe4, 0x3c, 0x87, 0xc3, 0x10, 0xcc, 0x0d, 0xd4, 0x47, 0x50, 0x3f, 0x6a, 0x95, 0x92, 0x04, + 0xb8, 0x02, 0xca, 0x4f, 0x62, 0xc0, 0xf1, 0x14, 0x37, 0x06, 0x1b, 0x8d, 0xdf, 0xa0, 0x30, 0x43, + 0xef, 0x66, 0x8f, 0x7d, 0xa8, 0x0b, 0xec, 0x29, 0x94, 0xce, 0xe3, 0xab, 0x28, 0x9f, 0x1c, 0x38, + 0x58, 0x6d, 0xe6, 0x9a, 0xc9, 0x19, 0x44, 0x85, 0x3a, 0xb6, 0x51, 0xc0, 0x36, 0x7c, 0xf5, 0xa0, + 0x62, 0xef, 0xf4, 0xc7, 0x38, 0x43, 0x41, 0x56, 0x75, 0x8d, 0x16, 0xc8, 0x0a, 0x3f, 0x80, 0xde, + 0x1a, 0x35, 0x44, 0x85, 0x42, 0xe0, 0xfc, 0x3c, 0x2e, 0xdc, 0xc8, 0x6b, 0x36, 0x43, 0x0a, 0x82, + 0xd5, 0x59, 0xc6, 0x61, 0x40, 0x53, 0xc1, 0x8a, 0xde, 0xc4, 0x72, 0xa2, 0x71, 0x5e, 0x3b, 0xd1, + 0xe1, 0xe5, 0x15, 0xdb, 0x6c, 0x6f, 0x44, 0xd3, 0x8b, 0xbe, 0xb6, 0xba, 0x28, 0x3f, 0xfc, 0x22, + 0xd6, 0x17, 0xad, 0xa5, 0x93, 0x16, 0x2a, 0x5f, 0xa1, 0x23, 0xba, 0x8d, 0x8a, 0xa3, 0x35, 0xa0, + 0x69, 0x9d, 0xe6, 0xb2, 0xa4, 0x51, 0x55, 0x96, 0x1e, 0xa6, 0x6e, 0x12, 0x2c, 0xb3, 0x69, 0xb7, + 0x2b, 0x59, 0xd3, 0xdb, 0x4b, 0x3b, 0x41, 0x9f, 0x63, 0xeb, 0xcc, 0xf9, 0x44, 0xdc, 0xac, 0xef, + 0x11, 0x3f, 0x88, 0xc8, 0xeb, 0x24, 0x5e, 0x92, 0x24, 0xbb, 0x99, 0xc0, 0xc3, 0x94, 0x58, 0xdd, + 0x0c, 0x13, 0xc9, 0x9a, 0x42, 0xfb, 0x6e, 0xb6, 0xb7, 0xd7, 0x25, 0x56, 0xd6, 0xcd, 0x2c, 0x59, + 0x92, 0x30, 0x91, 0x68, 0x9b, 0x57, 0x71, 0xde, 0xe6, 0xd6, 0x8f, 0x93, 0x2e, 0x7d, 0x0d, 0x90, + 0x9b, 0x48, 0x9f, 0xe3, 0x6e, 0x86, 0x53, 0x7c, 0x3b, 0x23, 0xd9, 0x98, 0x7c, 0x48, 0x3f, 0x62, + 0x42, 0x63, 0x02, 0x50, 0x45, 0x60, 0xfc, 0x40, 0x5e, 0x4b, 0x6b, 0xfa, 0xfe, 0x25, 0xc1, 0x69, + 0x64, 0xa5, 0x84, 0x83, 0x44, 0x2c, 0xc0, 0x93, 0xd8, 0x47, 0xb3, 0x30, 0x76, 0xec, 0x10, 0xf8, + 0xb1, 0x65, 0x75, 0x62, 0x0a, 0x62, 0x67, 0x6f, 0xaf, 0xe3, 0x26, 0x37, 0xcb, 0x2c, 0xee, 0x04, + 0x91, 0xd0, 0xe0, 0x71, 0xf9, 0xb3, 0xcf, 0x9e, 0x8f, 0x2f, 0xe3, 0xc0, 0x43, 0xf2, 0x5a, 0x9a, + 0xf8, 0xab, 0x88, 0x79, 0xa8, 0x56, 0xa4, 0x9b, 0x49, 0xb7, 0x09, 0x37, 0x92, 0x98, 0xe7, 0x2d, + 0x72, 0x61, 0xa4, 0xb7, 0x41, 0x94, 0x99, 0x47, 0x49, 0x62, 0xdf, 0x7c, 0xf9, 0x42, 0xff, 0x3c, + 0x59, 0xf9, 0x3e, 0x49, 0xfa, 0x41, 0xfa, 0x2e, 0x20, 0x57, 0xdd, 0x4c, 0xda, 0xdb, 0xcb, 0xe0, + 0x33, 0x17, 0x69, 0x96, 0xac, 0xdc, 0x2c, 0x4e, 0xfa, 0x54, 0x8e, 0x5a, 0x56, 0xa7, 0x7c, 0xb1, + 0xb3, 0x2e, 0xc6, 0xf9, 0x47, 0x06, 0xe3, 0x04, 0x7e, 0xf7, 0xc1, 0x29, 0x0d, 0x99, 0xf4, 0x83, + 0xf4, 0xdc, 0xf6, 0xc9, 0x8b, 0x28, 0x23, 0x33, 0x92, 0x74, 0x33, 0xe9, 0xcb, 0x97, 0xec, 0x50, + 0x96, 0xc0, 0x0e, 0xbd, 0xa2, 0xac, 0xe2, 0x04, 0xd2, 0xae, 0xbb, 0x1d, 0xf6, 0x5d, 0xa6, 0x4b, + 0x70, 0x53, 0xd2, 0x96, 0x88, 0x5c, 0x2f, 0xa9, 0xd2, 0x85, 0xd1, 0x2c, 0xce, 0x50, 0x67, 0x3f, + 0x93, 0xca, 0x41, 0x7e, 0xe9, 0x66, 0xb8, 0xdf, 0xef, 0x13, 0x36, 0x10, 0x9d, 0x5a, 0xb3, 0xc7, + 0x12, 0xbc, 0xa2, 0xaf, 0x8e, 0x34, 0x09, 0xfc, 0x2e, 0xe9, 0x33, 0x31, 0x3f, 0x95, 0xf7, 0xf6, + 0x1e, 0x90, 0x7e, 0xc0, 0x3c, 0xdc, 0x69, 0x37, 0xe3, 0xf7, 0x77, 0xeb, 0x0b, 0xce, 0xa8, 0xb1, + 0xf6, 0xa8, 0xb3, 0x4f, 0xf6, 0x3b, 0x0c, 0xce, 0x5c, 0x81, 0xd8, 0x2f, 0x3a, 0x2b, 0xa1, 0xfe, + 0x31, 0x03, 0x4c, 0xb1, 0x1e, 0xc8, 0x14, 0x6c, 0xc0, 0x38, 0xc0, 0xf6, 0x1b, 0xe2, 0x35, 0xc7, + 0x7b, 0x0e, 0x1e, 0xdd, 0x7c, 0x8f, 0x68, 0x71, 0x0e, 0x07, 0x18, 0x68, 0xf1, 0x06, 0x9f, 0x08, + 0xec, 0x0c, 0x7c, 0x03, 0x2f, 0x9d, 0x6f, 0xea, 0xe4, 0x3b, 0x2f, 0x80, 0xfc, 0xbf, 0xae, 0x44, + 0x3b, 0xb1, 0x43, 0x38, 0x74, 0x70, 0xc3, 0x3a, 0x73, 0x6d, 0xb0, 0x14, 0x3a, 0x02, 0x80, 0x71, + 0x44, 0x51, 0xf9, 0xf6, 0x97, 0x6e, 0x26, 0xc1, 0x77, 0x09, 0x51, 0x6a, 0x11, 0xf8, 0x9e, 0xe4, + 0x72, 0x95, 0xbd, 0x24, 0xd1, 0x84, 0x02, 0xcd, 0x66, 0x75, 0x98, 0x36, 0x87, 0x63, 0x23, 0xbd, + 0x88, 0xb2, 0xb8, 0x2b, 0xf1, 0x55, 0x4a, 0x11, 0x7b, 0x1b, 0x39, 0x14, 0xa5, 0x84, 0x25, 0xb3, + 0x61, 0xa9, 0xe0, 0x23, 0x9d, 0x9d, 0xfd, 0x54, 0x5c, 0x22, 0xd2, 0xed, 0xf7, 0xfb, 0x99, 0x44, + 0x69, 0x09, 0x20, 0x20, 0x96, 0x3c, 0x21, 0x87, 0xf9, 0xb0, 0x13, 0xb2, 0xbf, 0x2f, 0x65, 0x1f, + 0xc8, 0xc7, 0x3e, 0x54, 0xb7, 0xea, 0xca, 0xc2, 0x9b, 0x76, 0x26, 0xe0, 0x37, 0x15, 0x41, 0x76, + 0x66, 0x33, 0x04, 0xee, 0xb3, 0xd1, 0x71, 0xd6, 0x07, 0x65, 0xe8, 0x8c, 0xc6, 0x00, 0xf9, 0xc5, + 0xcb, 0xfa, 0x1e, 0x3d, 0x21, 0x1c, 0x7d, 0x61, 0x0d, 0x70, 0x12, 0x49, 0x39, 0xc5, 0xf4, 0xb3, + 0xf8, 0x39, 0xb9, 0xee, 0xb2, 0x75, 0x81, 0xa3, 0xdc, 0x93, 0x1c, 0xc4, 0xd4, 0x92, 0x27, 0x69, + 0x09, 0x62, 0xba, 0xbf, 0x2f, 0x91, 0x7d, 0xeb, 0x69, 0xfc, 0x21, 0xfb, 0x90, 0x7e, 0xfc, 0x38, + 0xe1, 0x1d, 0x90, 0x72, 0x8c, 0x28, 0xe2, 0x63, 0x64, 0x53, 0x6b, 0x4e, 0xfa, 0xbf, 0xc9, 0x7b, + 0x7b, 0xd9, 0x21, 0xfd, 0x35, 0x2a, 0x86, 0xeb, 0xd1, 0x07, 0x93, 0xa2, 0xd1, 0x51, 0xde, 0xe6, + 0x59, 0xd9, 0xa4, 0x0b, 0xf7, 0x7b, 0xf0, 0xbd, 0xf5, 0xa2, 0x99, 0x9d, 0x37, 0xf3, 0xab, 0xcd, + 0x6c, 0x68, 0x56, 0x42, 0x10, 0xe4, 0x44, 0xca, 0x99, 0x4d, 0xf6, 0x00, 0x0c, 0x1e, 0x88, 0x35, + 0x75, 0x9a, 0xfb, 0x3a, 0x27, 0xd7, 0x88, 0x3d, 0x6c, 0xd2, 0x24, 0x7f, 0x9d, 0x42, 0x50, 0x2e, + 0x56, 0x49, 0x2d, 0x7d, 0x3f, 0x89, 0x17, 0xb0, 0x6e, 0x59, 0xbe, 0x70, 0xf9, 0x3a, 0xe1, 0xd4, + 0x22, 0x07, 0x2a, 0xc5, 0xe2, 0x87, 0xea, 0x3d, 0x06, 0x5d, 0x45, 0xf0, 0x61, 0x0a, 0x02, 0x35, + 0x81, 0xae, 0xab, 0x24, 0xc8, 0x46, 0x88, 0x2c, 0xe8, 0xa5, 0x84, 0xa0, 0x9b, 0x4a, 0xc5, 0x4e, + 0xc5, 0x96, 0x8c, 0x13, 0x4b, 0x9e, 0xc4, 0x87, 0xe9, 0x24, 0xde, 0xdf, 0xc7, 0xc9, 0xbe, 0xa5, + 0xc2, 0x87, 0xb5, 0x32, 0x14, 0x58, 0xb0, 0x29, 0x7d, 0x50, 0x85, 0x8f, 0x63, 0x8f, 0x1c, 0x65, + 0xdd, 0x44, 0x92, 0xb0, 0xd7, 0xbc, 0xbb, 0xaf, 0x48, 0x74, 0xb2, 0x81, 0x65, 0x59, 0x8c, 0xc9, + 0x7e, 0xf9, 0xe2, 0x15, 0xbf, 0x59, 0x67, 0xae, 0x95, 0x7d, 0x48, 0x3e, 0xee, 0x67, 0x1f, 0x92, + 0x7d, 0xe5, 0xe3, 0xa4, 0x3e, 0xb5, 0x47, 0x1b, 0xa7, 0x16, 0xd1, 0x63, 0x95, 0xd7, 0xa5, 0x42, + 0x8e, 0x3a, 0x8f, 0xf6, 0xdd, 0xfd, 0x47, 0x1d, 0xa0, 0x16, 0xfa, 0x45, 0x54, 0xf4, 0x68, 0x3f, + 0x91, 0xd6, 0xd1, 0x87, 0xf8, 0xa3, 0x15, 0xfc, 0xa0, 0x0c, 0xf6, 0xbd, 0x75, 0x8e, 0xf0, 0xe5, + 0xde, 0x2e, 0xc9, 0x3d, 0xf6, 0xb6, 0x06, 0x47, 0x47, 0x9a, 0x08, 0x14, 0x24, 0x2c, 0x22, 0x5c, + 0x5e, 0x90, 0xeb, 0xec, 0x24, 0x72, 0x63, 0x8f, 0x24, 0x5d, 0xa9, 0x4f, 0xe8, 0x2f, 0x60, 0xbc, + 0xe5, 0xd0, 0x3f, 0x8b, 0x34, 0x98, 0x0f, 0x6f, 0x15, 0xc3, 0xef, 0xed, 0x75, 0x33, 0x8b, 0x82, + 0x27, 0x61, 0x4a, 0x5e, 0x59, 0xf9, 0xea, 0xdf, 0x39, 0xdd, 0xe7, 0x34, 0x9f, 0x6f, 0x59, 0x64, + 0xc9, 0x93, 0xa8, 0x24, 0xae, 0x68, 0x7f, 0x9f, 0xb5, 0x89, 0xad, 0xec, 0x43, 0xf4, 0x71, 0xf2, + 0x4b, 0x37, 0x96, 0x30, 0xd9, 0xb7, 0x62, 0xde, 0x60, 0xcd, 0x08, 0xb2, 0x06, 0x3d, 0x91, 0xc4, + 0xfe, 0x70, 0xbc, 0xa1, 0xcf, 0x84, 0xf5, 0x99, 0xf6, 0x53, 0x92, 0x75, 0x13, 0x1c, 0x4b, 0x38, + 0xde, 0xb7, 0x92, 0xbc, 0x67, 0x3e, 0xad, 0x54, 0x60, 0x39, 0x94, 0x8e, 0x19, 0xc4, 0x91, 0x35, + 0xcd, 0xba, 0x52, 0x9f, 0xa5, 0xe3, 0x77, 0x7f, 0xce, 0xba, 0x91, 0x24, 0xf5, 0x73, 0xde, 0x8b, + 0x53, 0x2b, 0xeb, 0x16, 0x4b, 0x2b, 0xb0, 0x54, 0x2b, 0x2d, 0x7f, 0x63, 0xd2, 0xa7, 0x1f, 0xcb, + 0x64, 0xb7, 0xf3, 0x9f, 0x98, 0xf4, 0xdd, 0x84, 0xd8, 0x19, 0xb1, 0x40, 0x3f, 0x80, 0xbe, 0x04, + 0x46, 0xf2, 0x33, 0xe9, 0x66, 0x96, 0xa6, 0xd2, 0xdd, 0xbe, 0x04, 0x61, 0xc0, 0x96, 0xfc, 0x92, + 0xf4, 0x67, 0x24, 0x7b, 0x63, 0x47, 0x5e, 0xbc, 0x78, 0x07, 0x49, 0x20, 0xa0, 0x42, 0xe4, 0x2f, + 0x75, 0x72, 0x22, 0x6d, 0xb6, 0xea, 0xd6, 0x96, 0x2d, 0x63, 0xa8, 0x5e, 0xed, 0x39, 0xa1, 0x2f, + 0x3c, 0x01, 0xab, 0xb2, 0xad, 0xd7, 0x1a, 0xe9, 0x77, 0xab, 0x6f, 0xd0, 0x2e, 0x1b, 0x48, 0xc8, + 0x74, 0x96, 0x3a, 0x34, 0x68, 0xb1, 0x4a, 0x33, 0xe4, 0x40, 0x04, 0x14, 0x14, 0x33, 0x10, 0x54, + 0xa0, 0x2f, 0x25, 0x11, 0x7e, 0x1a, 0xe3, 0x39, 0xc1, 0x49, 0x86, 0x9f, 0x92, 0x42, 0x71, 0x4a, + 0x23, 0x58, 0xe0, 0x28, 0xd7, 0x9e, 0xea, 0x60, 0x7c, 0xf8, 0x28, 0x31, 0x06, 0x2e, 0xc2, 0x5c, + 0x4c, 0xab, 0xc9, 0xaf, 0xc4, 0x66, 0xf8, 0x69, 0x6c, 0x09, 0x5d, 0xdd, 0x32, 0x7c, 0x18, 0xab, + 0xc6, 0x60, 0x8d, 0xb9, 0xea, 0x47, 0xfa, 0x59, 0x7c, 0x4e, 0x91, 0xbc, 0xab, 0x0c, 0xa4, 0xfe, + 0xd2, 0xf6, 0x68, 0x45, 0xe2, 0xae, 0x8a, 0x3b, 0x72, 0x47, 0x92, 0x26, 0x73, 0x62, 0xdd, 0xfe, + 0x26, 0x8f, 0x75, 0x13, 0xff, 0x36, 0x1a, 0x1b, 0x43, 0x7c, 0x34, 0x1e, 0x18, 0xf8, 0xd9, 0x78, + 0x28, 0x63, 0x7b, 0x3c, 0x1a, 0x62, 0x7f, 0xac, 0xc8, 0xea, 0x7a, 0x92, 0x64, 0x16, 0x55, 0x64, + 0x6f, 0xd7, 0xa2, 0xe2, 0x76, 0x06, 0x6a, 0x24, 0xc1, 0x29, 0x8e, 0x2a, 0x54, 0x0d, 0x48, 0xfa, + 0x24, 0x98, 0x01, 0xe8, 0x03, 0xbd, 0x6d, 0x27, 0xaa, 0x2d, 0xba, 0xac, 0x87, 0x09, 0xa3, 0x9d, + 0x27, 0xc1, 0xec, 0x45, 0x94, 0x75, 0x35, 0x55, 0xc2, 0x49, 0x7e, 0xa1, 0xab, 0x23, 0x7d, 0x34, + 0x18, 0xaa, 0x23, 0x43, 0xc2, 0x81, 0xc5, 0x94, 0xb7, 0x6e, 0x3a, 0x9d, 0xc6, 0x7b, 0x09, 0x70, + 0xc2, 0xfc, 0x06, 0x5c, 0xb9, 0x56, 0xf4, 0x58, 0x1f, 0xcb, 0x78, 0x65, 0x45, 0x8f, 0xe5, 0xb1, + 0x3e, 0xa1, 0x43, 0xc1, 0x38, 0x9a, 0xda, 0x25, 0xfb, 0x2e, 0x0e, 0x70, 0x24, 0xe1, 0xea, 0xcd, + 0x15, 0xf6, 0x70, 0xc4, 0x76, 0xd0, 0xcd, 0xf0, 0x7b, 0xec, 0x96, 0x3a, 0xef, 0x53, 0xd2, 0x95, + 0x26, 0x2e, 0x9f, 0x3b, 0x22, 0xd7, 0x19, 0x89, 0xbc, 0x14, 0x25, 0xd9, 0xad, 0xa0, 0x73, 0x32, + 0xf0, 0x71, 0x2c, 0xdd, 0xa6, 0xab, 0x25, 0xf0, 0x1f, 0x0c, 0x16, 0x6a, 0xa1, 0xfb, 0x58, 0x0f, + 0x14, 0x76, 0x83, 0xab, 0x5f, 0x32, 0xbb, 0x5a, 0xc6, 0x69, 0xfe, 0xb3, 0xd0, 0x9c, 0x8a, 0xa6, + 0x05, 0xb1, 0x11, 0x76, 0x2d, 0x10, 0x25, 0x7f, 0xdb, 0xf6, 0x98, 0xae, 0x60, 0x45, 0xec, 0x46, + 0x90, 0xbe, 0x3c, 0xb1, 0x62, 0xfe, 0x36, 0x55, 0x29, 0x9a, 0x8c, 0x86, 0x3d, 0xa5, 0x65, 0xf0, + 0x41, 0x23, 0x29, 0x9b, 0x4a, 0x6b, 0xce, 0x1b, 0x88, 0x74, 0xfb, 0x23, 0x7b, 0x22, 0x61, 0x62, + 0xfd, 0x9c, 0xc1, 0x4b, 0xbf, 0x74, 0x99, 0xf4, 0xba, 0x85, 0x17, 0xc7, 0x29, 0x66, 0xaf, 0x8c, + 0x23, 0x9c, 0x03, 0x39, 0x8e, 0xd7, 0x16, 0xbc, 0x82, 0x13, 0x2b, 0xd7, 0x5e, 0x0b, 0x96, 0x16, + 0x58, 0xf2, 0x24, 0x38, 0x4c, 0x26, 0x8c, 0x15, 0x79, 0xd6, 0x2b, 0x3b, 0x9b, 0xf7, 0xe1, 0xdb, + 0xf2, 0x71, 0x2f, 0x5f, 0x05, 0x9c, 0xf4, 0x02, 0x4a, 0xcb, 0x20, 0xaa, 0xe2, 0x5c, 0x4a, 0xd9, + 0x59, 0xce, 0x19, 0x27, 0xf1, 0xa1, 0x95, 0xf4, 0x82, 0x49, 0xb0, 0x6f, 0xc5, 0x12, 0x7b, 0x29, + 0x89, 0x5d, 0x92, 0xa6, 0x5d, 0x17, 0x07, 0xd2, 0x04, 0x4a, 0x31, 0x04, 0xd1, 0x8a, 0xac, 0x23, + 0xca, 0x1a, 0x49, 0x3f, 0x5d, 0x39, 0x36, 0x9d, 0x70, 0x80, 0x83, 0x7d, 0x4f, 0x2a, 0x56, 0xbb, + 0xfc, 0xb5, 0x6f, 0x79, 0x38, 0x80, 0x7f, 0x8a, 0x8d, 0xb0, 0xac, 0x18, 0x04, 0x80, 0xd8, 0x79, + 0x8a, 0x65, 0x49, 0xd8, 0x29, 0x29, 0xe7, 0xb1, 0xc2, 0x56, 0xee, 0x17, 0xf3, 0x65, 0x0d, 0xe9, + 0xb7, 0x77, 0x20, 0x5e, 0x11, 0x71, 0x1c, 0x58, 0x0b, 0x6a, 0xa7, 0xb8, 0xb4, 0x71, 0xd4, 0x65, + 0x1b, 0xdb, 0x40, 0x15, 0x99, 0xae, 0x34, 0x5f, 0xe2, 0x14, 0xd3, 0x25, 0x17, 0x57, 0x1a, 0xc3, + 0x3e, 0x8f, 0x13, 0xbe, 0xe0, 0xf0, 0xc1, 0xd8, 0x71, 0xc0, 0x2e, 0x26, 0xe9, 0x87, 0x60, 0x7f, + 0xff, 0xa3, 0xa5, 0xa8, 0x26, 0xfe, 0x91, 0x88, 0xbb, 0x2b, 0xac, 0x88, 0x24, 0xd5, 0xd0, 0x67, + 0x1a, 0xf7, 0x82, 0xfa, 0xcc, 0x23, 0x98, 0x79, 0x60, 0xc9, 0xa5, 0x60, 0x9a, 0x5b, 0xc1, 0x64, + 0x7e, 0x18, 0x4f, 0xe6, 0xfb, 0xfb, 0x52, 0xfa, 0x61, 0xfe, 0xd1, 0x92, 0x27, 0x67, 0x71, 0x37, + 0xc2, 0x71, 0xcf, 0xc4, 0x9c, 0x42, 0x85, 0x65, 0xf9, 0xc1, 0x94, 0x70, 0x92, 0x0f, 0x24, 0xf4, + 0x39, 0x61, 0x28, 0x40, 0x77, 0x16, 0xbb, 0x56, 0x15, 0xb1, 0x01, 0x01, 0xdc, 0x87, 0x7a, 0x53, + 0x0b, 0xf8, 0x2d, 0x9d, 0xdb, 0xea, 0x18, 0x15, 0x0d, 0xc1, 0xbf, 0xb1, 0x0a, 0x21, 0xd3, 0x08, + 0xd1, 0xaf, 0x9b, 0x41, 0xdc, 0x30, 0x46, 0x9a, 0x0a, 0xb5, 0xe0, 0xd9, 0x10, 0x2b, 0xcb, 0x3d, + 0xd0, 0xb1, 0xcf, 0x06, 0x98, 0x11, 0x90, 0x6e, 0x81, 0xdf, 0x5d, 0x4d, 0xfd, 0xdc, 0xea, 0xb9, + 0x7b, 0x0c, 0x27, 0x98, 0xcd, 0x68, 0x1e, 0x95, 0xcd, 0x9c, 0x39, 0xa4, 0x23, 0x2e, 0x86, 0x3c, + 0x99, 0x1f, 0xae, 0xe8, 0x62, 0x78, 0x02, 0x1f, 0xd1, 0x7f, 0x98, 0x63, 0xff, 0xc3, 0xfc, 0x23, + 0x4e, 0xa4, 0x75, 0x2e, 0x5c, 0x6f, 0x85, 0xdd, 0x24, 0xb8, 0xe8, 0x7f, 0x9c, 0xf2, 0x3d, 0x63, + 0xe4, 0x2f, 0x22, 0x09, 0xd7, 0x13, 0x49, 0x3f, 0x0d, 0x03, 0x97, 0x74, 0x65, 0x9c, 0x16, 0xb2, + 0x59, 0xe4, 0x15, 0x5d, 0x09, 0x47, 0xeb, 0xdf, 0xdc, 0x30, 0x8e, 0x48, 0x8e, 0x5d, 0xe4, 0xcb, + 0x97, 0x2e, 0xa1, 0x44, 0x4f, 0x1b, 0x0a, 0x0c, 0x4a, 0xc2, 0x84, 0xd2, 0x06, 0x68, 0x31, 0xc5, + 0x9a, 0x30, 0x9a, 0x2e, 0x30, 0x4b, 0xa0, 0x6b, 0x2e, 0x43, 0x62, 0x9c, 0xa3, 0xe5, 0x38, 0xc1, + 0x05, 0x8b, 0x1a, 0x07, 0x18, 0x90, 0xce, 0xe3, 0x13, 0x28, 0xd4, 0x86, 0x92, 0x87, 0x05, 0x98, + 0x94, 0x08, 0x9d, 0xe0, 0x9c, 0x44, 0xac, 0x18, 0x13, 0x4a, 0x4a, 0x1e, 0x8e, 0x1f, 0xa6, 0x7b, + 0x7b, 0xa4, 0xc0, 0x4e, 0x02, 0x0a, 0x09, 0x26, 0x6b, 0x3a, 0x9b, 0x6e, 0xa9, 0xa3, 0x01, 0xa8, + 0xc2, 0x14, 0xa5, 0xf5, 0x1a, 0xbf, 0xb7, 0xd8, 0x62, 0x8b, 0xd2, 0x53, 0x19, 0x0e, 0x47, 0xb2, + 0xa6, 0x0d, 0x65, 0x0d, 0xeb, 0xb2, 0x39, 0x52, 0x35, 0x63, 0xa8, 0xca, 0x58, 0x53, 0x74, 0x5d, + 0xd1, 0x74, 0x75, 0x38, 0xc4, 0xaa, 0xaa, 0x0e, 0xcd, 0xa1, 0x66, 0x8c, 0x0c, 0xac, 0xc8, 0x8a, + 0x36, 0x92, 0x75, 0x55, 0x57, 0xb1, 0xae, 0x0e, 0x15, 0x65, 0x68, 0x0c, 0x55, 0x0d, 0xab, 0xc3, + 0xa1, 0xa6, 0x9b, 0xf2, 0x70, 0xa0, 0x62, 0xc5, 0x18, 0x19, 0x43, 0x43, 0x56, 0xd4, 0x11, 0x56, + 0x34, 0x63, 0x64, 0x8e, 0x34, 0x45, 0x19, 0x61, 0x75, 0xa4, 0x0c, 0x8d, 0x81, 0xa1, 0x68, 0x43, + 0xac, 0x0e, 0x64, 0xd9, 0x54, 0xd5, 0x91, 0xaa, 0xe3, 0xa1, 0x6a, 0x18, 0x8a, 0xa2, 0x8c, 0x46, + 0xd8, 0x50, 0xcd, 0xa1, 0xa6, 0x0f, 0x34, 0x03, 0xeb, 0xaa, 0x62, 0x68, 0xe6, 0xc8, 0xd0, 0x87, + 0x58, 0x31, 0x74, 0x45, 0x37, 0x46, 0xaa, 0x6a, 0x60, 0x4d, 0x1d, 0xca, 0x9a, 0xa6, 0xca, 0xa3, + 0x8f, 0x92, 0x28, 0x3d, 0x4f, 0x63, 0x66, 0x73, 0x2b, 0xc5, 0x9c, 0xc9, 0xe3, 0xdb, 0xf9, 0x98, + 0x4b, 0xb3, 0x6c, 0xcf, 0xcb, 0x24, 0x1c, 0x16, 0x97, 0xd3, 0xa9, 0x17, 0xc1, 0xad, 0xf5, 0x58, + 0x68, 0x93, 0xdf, 0xfc, 0x22, 0x0b, 0x2d, 0xd9, 0x8d, 0x75, 0xa9, 0x8b, 0x85, 0x51, 0x31, 0x10, + 0xd3, 0x41, 0x0b, 0x43, 0xa7, 0xb4, 0x48, 0xf8, 0xa2, 0x76, 0x81, 0x3d, 0xb5, 0xdc, 0x2c, 0xd0, + 0x1f, 0x6c, 0x94, 0xe4, 0x30, 0x9d, 0x24, 0x5c, 0x33, 0xbd, 0x9d, 0x8f, 0x03, 0x1c, 0x02, 0x42, + 0xc0, 0x74, 0x3e, 0x24, 0x1f, 0xc1, 0xf9, 0xf4, 0x21, 0x82, 0x1f, 0xf1, 0x87, 0xe4, 0xe3, 0x47, + 0xeb, 0x43, 0x80, 0xbd, 0x8f, 0x9c, 0x7b, 0x7e, 0x88, 0x70, 0xfc, 0x51, 0x80, 0x8b, 0x94, 0xea, + 0x03, 0x13, 0xfe, 0x5d, 0x32, 0x9d, 0x4e, 0x65, 0x69, 0xbf, 0x1b, 0xd1, 0xbf, 0x1c, 0xbf, 0x6e, + 0xe7, 0xe3, 0x6c, 0x3f, 0xdd, 0xef, 0xc6, 0x07, 0xea, 0x0f, 0x3f, 0x68, 0xea, 0x17, 0x99, 0xcd, + 0x37, 0x86, 0x49, 0x82, 0xd4, 0xf6, 0x32, 0xec, 0x45, 0xf8, 0xd7, 0x0c, 0xbf, 0xcf, 0xf0, 0x05, + 0xc1, 0x47, 0x04, 0xff, 0x4a, 0xf0, 0x7b, 0x82, 0xfd, 0x08, 0xcf, 0x23, 0xbc, 0x8a, 0xf0, 0x32, + 0xc2, 0x8b, 0x08, 0xdf, 0x44, 0x78, 0x56, 0x8a, 0x76, 0x2f, 0x6b, 0xd3, 0x2e, 0xbc, 0x48, 0x50, + 0x40, 0x26, 0xbf, 0x66, 0x16, 0x83, 0x10, 0xf4, 0xdb, 0xe9, 0x74, 0x9a, 0xe2, 0xf7, 0xe2, 0x9d, + 0xc3, 0x43, 0x4d, 0xed, 0xa5, 0x5f, 0x08, 0x7d, 0x72, 0x41, 0x6a, 0x6d, 0xbf, 0x10, 0xf6, 0x1c, + 0x1f, 0x91, 0x4d, 0xef, 0xfc, 0x5a, 0x7d, 0x32, 0xd0, 0xf3, 0x27, 0x3d, 0x4d, 0xc5, 0xef, 0xeb, + 0x1d, 0xf6, 0x34, 0x15, 0xfa, 0x84, 0x56, 0x13, 0x3f, 0x2a, 0x1f, 0x02, 0x1a, 0xd0, 0x45, 0xcb, + 0x17, 0x2f, 0xa5, 0x7f, 0xf1, 0x3c, 0x6f, 0x83, 0x23, 0xd0, 0x03, 0xf7, 0xd3, 0xfd, 0x68, 0xbf, + 0x9b, 0x89, 0x6b, 0xb8, 0xaa, 0xb4, 0xd8, 0xd0, 0x4f, 0xbe, 0x19, 0x78, 0x59, 0xb6, 0xc6, 0x71, + 0xd1, 0x63, 0x5c, 0xeb, 0x73, 0x51, 0x6b, 0x75, 0x47, 0xaf, 0xfb, 0xdd, 0x98, 0xf5, 0x7e, 0x23, + 0xbe, 0x87, 0x13, 0xa1, 0xff, 0xa4, 0x3a, 0xc2, 0x9a, 0x39, 0x37, 0xaf, 0x22, 0xfc, 0x2c, 0xc6, + 0xcf, 0x63, 0x7c, 0x4d, 0xb0, 0x43, 0xf0, 0x9f, 0x19, 0x3e, 0x23, 0xf8, 0xb8, 0xdc, 0x5f, 0x17, + 0xb4, 0xee, 0x19, 0xfc, 0x43, 0x95, 0xb8, 0xab, 0xc8, 0x0a, 0xa3, 0xee, 0x87, 0x8e, 0x7c, 0xad, + 0xab, 0xa6, 0xad, 0xfa, 0x23, 0xd3, 0x1b, 0xaa, 0xa6, 0x4d, 0x54, 0xb5, 0x83, 0x3b, 0xf2, 0xf5, + 0x50, 0xd1, 0x86, 0xba, 0x3e, 0x52, 0x54, 0x8d, 0xf8, 0x03, 0xc3, 0xf5, 0xe8, 0x4d, 0xc7, 0x70, + 0x65, 0xdf, 0x71, 0x7d, 0xe2, 0xea, 0x9e, 0xe6, 0xa8, 0x3e, 0xbd, 0x49, 0x46, 0x8e, 0xe1, 0x39, + 0xb6, 0x61, 0x2a, 0xe6, 0xc8, 0x73, 0x1c, 0x97, 0xde, 0xd4, 0x46, 0xc6, 0xc0, 0x55, 0x0d, 0xc7, + 0xd7, 0x74, 0xd3, 0x31, 0x34, 0x93, 0xde, 0x34, 0x46, 0xbe, 0xa2, 0x28, 0xbe, 0xe2, 0x0c, 0x64, + 0xc3, 0x93, 0x95, 0x11, 0xbd, 0x39, 0x52, 0x35, 0xdf, 0x54, 0x6d, 0xdd, 0xf6, 0x95, 0x91, 0xee, + 0x8f, 0x1c, 0x7a, 0xd3, 0x76, 0x14, 0xd7, 0x20, 0x9e, 0xe1, 0xd9, 0x03, 0xcf, 0x54, 0x14, 0xf6, + 0xba, 0x67, 0xca, 0x43, 0xdb, 0x1e, 0x99, 0xb6, 0x26, 0x6b, 0xb2, 0xaa, 0x33, 0x38, 0x15, 0xd5, + 0xd4, 0x0c, 0x47, 0x56, 0x74, 0x63, 0x28, 0x0f, 0x7c, 0x87, 0xd0, 0x9b, 0xaa, 0xae, 0x29, 0xa6, + 0xe1, 0x10, 0x9d, 0x10, 0xdd, 0x51, 0x4d, 0x06, 0x92, 0x61, 0xc8, 0xee, 0xd0, 0x73, 0x35, 0xcf, + 0xf0, 0x7d, 0x47, 0x27, 0x7c, 0x9a, 0xaa, 0x43, 0x0c, 0x6f, 0xa8, 0xfb, 0xea, 0xd0, 0x31, 0x47, + 0x03, 0x36, 0x23, 0xf8, 0xe4, 0xbd, 0xa3, 0xf8, 0x44, 0x73, 0x94, 0xc1, 0x68, 0xe0, 0x28, 0x0c, + 0x4e, 0xc7, 0x73, 0xe5, 0x81, 0x3d, 0x54, 0x0d, 0x77, 0xa8, 0xa8, 0x9a, 0x41, 0x6f, 0xba, 0xca, + 0xc8, 0xf1, 0x95, 0xa1, 0xee, 0xfa, 0x83, 0x91, 0x3a, 0x18, 0xe9, 0x6c, 0x41, 0xf4, 0x91, 0x33, + 0x18, 0xb9, 0xca, 0x88, 0xf8, 0x8a, 0x6e, 0x7b, 0x6c, 0x20, 0xe2, 0x3b, 0x44, 0x1f, 0x9a, 0x03, + 0xcd, 0xd4, 0x7d, 0xd5, 0x20, 0x1a, 0xbd, 0x29, 0xfb, 0xae, 0x32, 0xf2, 0xdc, 0x81, 0xe9, 0x98, + 0xae, 0x67, 0x38, 0x06, 0x07, 0x5e, 0x76, 0x6d, 0xc5, 0x75, 0x87, 0x43, 0xdb, 0x1d, 0xb9, 0x03, + 0x7e, 0xd3, 0x23, 0x23, 0xd5, 0x1d, 0xf8, 0xc6, 0x48, 0x75, 0x64, 0x75, 0xc8, 0x6e, 0xea, 0xf6, + 0x50, 0x37, 0x75, 0xdb, 0x1e, 0x10, 0x7b, 0x40, 0x74, 0x93, 0xf5, 0x69, 0xb8, 0x8e, 0x6c, 0x8f, + 0x3c, 0xd7, 0xf1, 0x74, 0xc5, 0x77, 0x3c, 0x06, 0xd2, 0x70, 0xe0, 0x8f, 0x4c, 0xd3, 0xb3, 0x4d, + 0x4d, 0x51, 0x0c, 0x8d, 0x0f, 0x34, 0x32, 0x35, 0x62, 0x28, 0x86, 0x4a, 0xc8, 0x60, 0xe0, 0xf9, + 0x36, 0x5f, 0x79, 0x53, 0x53, 0xdc, 0xc1, 0xc0, 0x53, 0x3d, 0x47, 0xd7, 0x54, 0x45, 0x66, 0xfb, + 0x2e, 0xcb, 0x9a, 0x3a, 0x74, 0xcd, 0x91, 0xe9, 0x3b, 0xaa, 0xa2, 0xb1, 0x55, 0x72, 0x7c, 0x63, + 0x34, 0xf4, 0xdd, 0xa1, 0x43, 0x88, 0x2f, 0x13, 0xc2, 0x06, 0x72, 0x07, 0x44, 0x96, 0x1d, 0x5f, + 0xd3, 0x3c, 0xdb, 0x34, 0x7d, 0x97, 0xcd, 0xdd, 0x33, 0xec, 0xe1, 0x48, 0xd1, 0x87, 0x23, 0x4d, + 0xb6, 0x61, 0x01, 0xd9, 0xdc, 0x07, 0xae, 0x3d, 0xd0, 0x0c, 0x85, 0xc8, 0xb2, 0x66, 0xaa, 0x7c, + 0xe5, 0x15, 0x5d, 0x1d, 0xa9, 0xa3, 0xc1, 0x50, 0xb6, 0x65, 0x32, 0x20, 0x43, 0x36, 0xba, 0x3a, + 0x74, 0x86, 0xb2, 0x6d, 0x1a, 0xfa, 0xc0, 0x53, 0x55, 0xdf, 0x67, 0xbb, 0xa9, 0x12, 0x05, 0x40, + 0x31, 0x0d, 0x57, 0x1d, 0xb8, 0x23, 0x75, 0xc0, 0x16, 0xc4, 0x53, 0xdd, 0x81, 0xe7, 0xbb, 0x86, + 0xed, 0xea, 0xaa, 0x4d, 0x18, 0xd2, 0x1a, 0x9a, 0x66, 0xca, 0x9e, 0xa2, 0x8d, 0xbc, 0x91, 0xe1, + 0x68, 0x1e, 0x1b, 0x68, 0x60, 0xc8, 0xf6, 0x50, 0x33, 0x74, 0xd3, 0xb1, 0xfd, 0x81, 0xe6, 0x11, + 0xbe, 0x4a, 0x03, 0x5b, 0xb6, 0x1d, 0x47, 0x73, 0x87, 0x43, 0x47, 0xb5, 0x19, 0xd6, 0x99, 0x8a, + 0xab, 0xba, 0x23, 0x95, 0xe8, 0x43, 0xe2, 0xd9, 0x84, 0x0c, 0x38, 0xd2, 0x0e, 0x55, 0xd5, 0x35, + 0x0d, 0x45, 0x37, 0x55, 0xcd, 0xd0, 0xf8, 0xd2, 0xa9, 0x8e, 0x4f, 0x4c, 0x5b, 0xd1, 0x5d, 0x5f, + 0x91, 0xb5, 0x81, 0xce, 0xd7, 0x53, 0xb1, 0x07, 0x03, 0xdd, 0x71, 0x5c, 0x5d, 0xd5, 0x64, 0x99, + 0xe1, 0x92, 0xab, 0xea, 0x8e, 0xe9, 0x0c, 0x65, 0x4f, 0xf6, 0xcd, 0xd1, 0x70, 0xc4, 0x6f, 0x0e, + 0x07, 0xae, 0xa1, 0xd8, 0x9a, 0x3c, 0x30, 0x74, 0x87, 0x68, 0x6c, 0xee, 0x9e, 0x32, 0x52, 0x89, + 0xa9, 0x8c, 0xbc, 0x01, 0xf1, 0x0d, 0x35, 0x27, 0x84, 0xc1, 0x68, 0x24, 0x0f, 0x54, 0xdd, 0x30, + 0x06, 0x86, 0x3d, 0xe2, 0x7b, 0xe4, 0xeb, 0x32, 0xd1, 0x0c, 0xd3, 0x30, 0x86, 0x43, 0x45, 0x95, + 0x55, 0x9b, 0xad, 0xa7, 0x3c, 0xb0, 0x6d, 0x79, 0x28, 0x6b, 0xaa, 0xe3, 0x78, 0x8a, 0xc3, 0x5e, + 0x57, 0x46, 0xb6, 0xee, 0x2a, 0xca, 0xc0, 0x31, 0x3d, 0xd5, 0x93, 0x5d, 0x7e, 0x93, 0x68, 0xc3, + 0x81, 0x2b, 0xc3, 0x94, 0x14, 0xdb, 0x31, 0x34, 0xbe, 0xf2, 0xba, 0x39, 0x1c, 0xea, 0xae, 0xe7, + 0x9b, 0x84, 0x38, 0x23, 0x46, 0xb0, 0x9a, 0xee, 0xc8, 0x8e, 0xeb, 0x18, 0x44, 0x19, 0x39, 0xba, + 0xc9, 0x57, 0x49, 0x1b, 0x29, 0xae, 0xec, 0x3a, 0x9a, 0x6b, 0xb8, 0x23, 0xc3, 0x1e, 0xb0, 0xd7, + 0x75, 0xe2, 0x99, 0xb6, 0xad, 0xdb, 0x44, 0xd3, 0x15, 0xd3, 0x76, 0xd9, 0x2a, 0x19, 0xce, 0xc8, + 0x75, 0x6d, 0xdd, 0x1f, 0x0e, 0x07, 0x1a, 0xd1, 0x86, 0xac, 0xe5, 0xc0, 0x54, 0xc9, 0xc0, 0xf7, + 0x35, 0x6f, 0xe0, 0xa8, 0x8e, 0x69, 0xb3, 0x9b, 0x43, 0xdd, 0xf4, 0x4d, 0x95, 0x10, 0xc3, 0x23, + 0xbe, 0xa3, 0xf2, 0x7d, 0x1f, 0x9a, 0xb6, 0x31, 0xd0, 0x06, 0xbe, 0xae, 0x29, 0x43, 0xd5, 0x1f, + 0xb0, 0xb9, 0x9b, 0xba, 0x6b, 0x0e, 0x4d, 0x45, 0xb7, 0x15, 0x5f, 0xb6, 0x9d, 0x21, 0xc3, 0x3a, + 0xd3, 0x75, 0x87, 0xb2, 0x2a, 0xc3, 0xfa, 0xeb, 0xda, 0x88, 0xb0, 0xd7, 0x47, 0xb2, 0x43, 0x7c, + 0xdf, 0xb7, 0x55, 0x6d, 0xa0, 0x29, 0x44, 0x65, 0xc0, 0xdb, 0xba, 0x21, 0x0f, 0x5c, 0xe2, 0x78, + 0xc4, 0x54, 0x1d, 0x8f, 0xb0, 0x69, 0x3a, 0xc4, 0x1f, 0xd9, 0x9a, 0x3f, 0x74, 0x54, 0x77, 0x30, + 0x1c, 0x29, 0x9c, 0xde, 0x07, 0x43, 0x65, 0x68, 0xfa, 0x2a, 0xd1, 0x86, 0xaa, 0xa1, 0xa9, 0x6c, + 0x46, 0xae, 0xad, 0x0e, 0x35, 0xe2, 0x12, 0x62, 0xab, 0x83, 0x81, 0x32, 0x72, 0xf9, 0xc6, 0x99, + 0x03, 0xc7, 0x74, 0x87, 0x2a, 0xac, 0x8b, 0x2a, 0x0f, 0x19, 0x13, 0xb0, 0x3d, 0x7b, 0xe8, 0x79, + 0x03, 0xd7, 0x23, 0x32, 0x71, 0x14, 0x86, 0x75, 0xbe, 0x31, 0xf4, 0x74, 0x7f, 0xe8, 0x13, 0x32, + 0x20, 0x9e, 0x32, 0x34, 0x39, 0x75, 0xf8, 0xf2, 0x60, 0x08, 0xd4, 0xa2, 0x0c, 0x07, 0xbe, 0xc3, + 0x76, 0x53, 0xb6, 0x07, 0xda, 0xd0, 0x73, 0x0d, 0x5b, 0x05, 0x52, 0xb4, 0x19, 0x2a, 0x2a, 0x8a, + 0xe6, 0x8f, 0x4c, 0x59, 0x07, 0x78, 0x65, 0xcf, 0x66, 0x7d, 0x2a, 0xce, 0x50, 0x91, 0x1d, 0xcd, + 0x50, 0x34, 0xc5, 0xd5, 0x87, 0x0a, 0x83, 0x53, 0x35, 0x3d, 0x67, 0x38, 0xf4, 0x0d, 0x55, 0x93, + 0xf5, 0xa1, 0x67, 0x32, 0xfc, 0xd4, 0x54, 0xd7, 0xb6, 0x9d, 0xa1, 0xa3, 0xcb, 0xee, 0x50, 0xd5, + 0x47, 0x6c, 0xe5, 0x35, 0x77, 0x44, 0x1c, 0x22, 0xdb, 0x8a, 0xe1, 0x8e, 0x1c, 0xc2, 0x59, 0xba, + 0xae, 0x29, 0xde, 0x60, 0xe8, 0xea, 0x23, 0x17, 0x3e, 0xcf, 0xae, 0xf3, 0x9b, 0xae, 0x6b, 0x78, + 0xba, 0x43, 0x5c, 0x47, 0x23, 0xba, 0xea, 0x0c, 0x38, 0x9f, 0x1f, 0xfa, 0xea, 0x68, 0xe4, 0xfa, + 0xee, 0xc0, 0x18, 0x12, 0x8e, 0x8a, 0x86, 0xef, 0x3a, 0x03, 0xdf, 0x76, 0x34, 0xdb, 0x1b, 0xf8, + 0x36, 0xdf, 0x8e, 0x81, 0xab, 0xeb, 0xca, 0xc8, 0x74, 0x75, 0x5b, 0x1f, 0x1a, 0xa6, 0x32, 0xec, + 0x7c, 0x84, 0xcf, 0x86, 0x77, 0x33, 0x6b, 0x9a, 0x9b, 0x51, 0x92, 0x24, 0xe1, 0x67, 0xb1, 0x75, + 0x15, 0x7d, 0x90, 0x3f, 0xe2, 0xe7, 0xf4, 0x87, 0xf2, 0x11, 0x5f, 0x93, 0x86, 0x4a, 0x66, 0xca, + 0x12, 0x76, 0xda, 0x6f, 0xff, 0x59, 0xf7, 0x57, 0xb8, 0x35, 0x7f, 0x85, 0x35, 0xd0, 0x73, 0x67, + 0x05, 0x98, 0x8b, 0x04, 0x2b, 0x03, 0xfc, 0x40, 0xe1, 0x26, 0xdb, 0xd1, 0xdc, 0x7a, 0xff, 0x41, + 0xfe, 0xf8, 0x85, 0x3b, 0x27, 0x8e, 0x42, 0xeb, 0xfd, 0x07, 0xa5, 0xb8, 0x7c, 0x02, 0x4f, 0xd5, + 0xf2, 0x12, 0x9e, 0x6a, 0xc5, 0xe5, 0x31, 0x3c, 0xd5, 0xcb, 0x4b, 0x78, 0x6a, 0x14, 0x97, 0x4f, + 0xe1, 0xe9, 0xa0, 0xbc, 0x84, 0xa7, 0xc3, 0xe2, 0xf2, 0x04, 0x9e, 0x9a, 0xe5, 0x25, 0x3c, 0x1d, + 0x15, 0x97, 0xcf, 0xe0, 0xa9, 0x52, 0x82, 0xf5, 0x8c, 0x82, 0x55, 0xc2, 0xf5, 0x23, 0x7d, 0x5e, + 0x02, 0xf6, 0x23, 0x7d, 0x5e, 0x42, 0xf6, 0x9c, 0x3e, 0x2f, 0x41, 0x7b, 0x4e, 0x9f, 0x03, 0x6c, + 0x6b, 0x6a, 0x0e, 0x51, 0x55, 0xf6, 0x68, 0x3e, 0x26, 0xf8, 0x28, 0x1c, 0xa7, 0xf8, 0xc9, 0x7c, + 0x1c, 0xe1, 0x27, 0xe1, 0x38, 0xc6, 0xc7, 0xf3, 0x71, 0x82, 0x8f, 0xc3, 0x71, 0x80, 0x9f, 0xce, + 0xc7, 0x1e, 0x7e, 0x1a, 0x8e, 0x5d, 0x7c, 0x32, 0x1f, 0xaf, 0xf0, 0x49, 0x38, 0xf6, 0xf1, 0xb3, + 0xf9, 0x78, 0x8e, 0x9f, 0x85, 0xe3, 0x10, 0xff, 0x38, 0x1f, 0x2f, 0xf0, 0x8f, 0xe1, 0x78, 0x86, + 0x9f, 0xcf, 0xc7, 0xd7, 0xf8, 0x79, 0x38, 0xbe, 0xa9, 0xd8, 0x48, 0x1f, 0x0a, 0xcd, 0x06, 0x07, + 0xd8, 0xc3, 0x2e, 0x5e, 0x61, 0x1f, 0xcf, 0x71, 0x88, 0x17, 0x78, 0x86, 0xaf, 0xf1, 0xcd, 0xc7, + 0x35, 0x75, 0x5f, 0x6c, 0x6f, 0x24, 0xdd, 0xe6, 0x5b, 0x44, 0x84, 0xfd, 0x49, 0x85, 0xcd, 0x89, + 0x84, 0x9d, 0x89, 0x85, 0x6d, 0x49, 0x84, 0x3d, 0x09, 0x84, 0x0d, 0xf1, 0x84, 0xdd, 0x70, 0x85, + 0xad, 0x58, 0x09, 0xfb, 0xe0, 0x0b, 0x9b, 0x30, 0x17, 0x36, 0x20, 0x14, 0x16, 0x7f, 0x21, 0x2c, + 0xfc, 0x4c, 0x58, 0xf4, 0x6b, 0x61, 0xc1, 0x6f, 0xbe, 0xc8, 0xeb, 0xdc, 0x21, 0x00, 0x9a, 0x6b, + 0x11, 0x48, 0x73, 0x2c, 0x79, 0xe2, 0x1c, 0x2a, 0x83, 0x89, 0xb3, 0xbf, 0x8f, 0xd3, 0x7d, 0x4b, + 0x97, 0xae, 0xc9, 0x07, 0xe7, 0xa3, 0x45, 0xbd, 0xc0, 0xdc, 0xbc, 0x4e, 0x01, 0xdd, 0x1b, 0x37, + 0xa1, 0xed, 0xa4, 0xec, 0x06, 0xba, 0x38, 0x34, 0x65, 0xe8, 0x87, 0x59, 0x14, 0xaf, 0x2d, 0xe8, + 0xa9, 0x47, 0x37, 0x1a, 0xbf, 0xb2, 0x9c, 0xf2, 0xe2, 0xcc, 0xba, 0x20, 0xdd, 0xd7, 0xf8, 0x15, + 0x56, 0xa4, 0x7f, 0xf2, 0x5f, 0xa6, 0xf4, 0xcf, 0x5f, 0x33, 0xfa, 0x6b, 0x28, 0xe1, 0x53, 0xeb, + 0xa8, 0x78, 0x7e, 0x54, 0x3c, 0x7f, 0x5f, 0x3c, 0x7f, 0xca, 0x7a, 0xa6, 0x18, 0x77, 0xce, 0x3a, + 0xa6, 0xbf, 0x9f, 0x40, 0xbf, 0x4f, 0xf1, 0x39, 0x56, 0x46, 0xd2, 0x3f, 0x7f, 0x65, 0x3f, 0x07, + 0x0a, 0xed, 0x99, 0xfe, 0x94, 0xf0, 0x67, 0xe8, 0x39, 0x6f, 0xf1, 0xbe, 0x6c, 0xf1, 0xbe, 0x68, + 0xf1, 0xd6, 0x5a, 0x45, 0xdd, 0x53, 0xfc, 0x99, 0xce, 0xb8, 0x37, 0xfc, 0xc8, 0xfe, 0x2a, 0x83, + 0x8f, 0x12, 0xbe, 0xb0, 0x96, 0x51, 0xf7, 0x2d, 0x3e, 0xc3, 0x4f, 0xf0, 0x35, 0x7f, 0x78, 0x9d, + 0x3f, 0x9c, 0xb0, 0x55, 0xbb, 0xf8, 0x22, 0xf3, 0xa5, 0x7a, 0xfb, 0x45, 0x5e, 0x73, 0xbc, 0x8e, + 0x00, 0xaf, 0x63, 0xc0, 0xeb, 0x04, 0xf0, 0x3a, 0x00, 0xbc, 0xf6, 0x00, 0xaf, 0x5d, 0xc0, 0xeb, + 0x15, 0xe0, 0xb5, 0x0f, 0x78, 0x3d, 0x07, 0xbc, 0x0e, 0x01, 0xaf, 0x17, 0x80, 0xd7, 0x33, 0xc0, + 0xeb, 0x6b, 0xc0, 0xeb, 0x1b, 0xc0, 0xeb, 0x63, 0xc0, 0xeb, 0x4b, 0x8e, 0xd7, 0xd5, 0xdd, 0xab, + 0xae, 0xfa, 0x05, 0xe9, 0x02, 0xda, 0x2a, 0x3a, 0x5d, 0x5c, 0xfa, 0xd3, 0xa4, 0xcb, 0x01, 0x3f, + 0x75, 0x45, 0xc2, 0xaf, 0x60, 0x11, 0xf2, 0x26, 0x47, 0x65, 0x93, 0xf7, 0x65, 0x93, 0x33, 0x6b, + 0xbe, 0xb7, 0xf8, 0xe7, 0x7f, 0x9f, 0xef, 0x5d, 0xe3, 0x53, 0x2b, 0xdc, 0x9b, 0xfd, 0xf3, 0xbf, + 0x87, 0x7b, 0x37, 0xf8, 0xa9, 0xb5, 0x88, 0xba, 0x97, 0xf8, 0x15, 0x3e, 0xc5, 0xcf, 0xe3, 0x0f, + 0x0e, 0x5b, 0x9b, 0x8f, 0x12, 0x3e, 0xb7, 0x6e, 0xa2, 0xee, 0x53, 0x7c, 0x8c, 0x5f, 0xe3, 0x33, + 0xfc, 0x8c, 0x3e, 0xb9, 0x66, 0x4f, 0x9e, 0x58, 0x4f, 0xbf, 0xc8, 0xf8, 0x33, 0xc0, 0x04, 0xa4, + 0xa5, 0x32, 0x40, 0xe0, 0xa7, 0xa6, 0x97, 0x3f, 0x47, 0xb0, 0xec, 0x47, 0x65, 0x93, 0xf7, 0x65, + 0x93, 0xf7, 0x65, 0x93, 0x0b, 0x2b, 0xda, 0x4b, 0xfe, 0x19, 0xed, 0x79, 0xff, 0x4c, 0xf6, 0x3c, + 0xfc, 0x9b, 0x15, 0xef, 0x05, 0xff, 0x8c, 0xf7, 0xdc, 0x7f, 0x06, 0x7b, 0xee, 0xe4, 0x98, 0x22, + 0xfb, 0x25, 0xa0, 0x39, 0xbe, 0xa6, 0x04, 0x71, 0x43, 0x49, 0x61, 0x41, 0x89, 0x66, 0x46, 0xc9, + 0xe5, 0x76, 0x8e, 0xc3, 0xb5, 0x15, 0x92, 0x2e, 0xd0, 0x17, 0xd0, 0xd5, 0x39, 0xa0, 0xcc, 0x17, + 0x59, 0xc2, 0x2b, 0x4a, 0x8a, 0x3e, 0x25, 0x42, 0x8f, 0x92, 0xab, 0x4b, 0x09, 0x35, 0xa1, 0x24, + 0x1d, 0x00, 0x31, 0x53, 0xdf, 0xd0, 0x33, 0xcb, 0x8f, 0xba, 0x4f, 0xf0, 0x5b, 0xfc, 0x9b, 0x34, + 0x89, 0xac, 0x79, 0xd4, 0x7d, 0x86, 0xcf, 0xf1, 0x67, 0x7c, 0x01, 0x66, 0xfb, 0xb3, 0x2f, 0xf2, + 0xba, 0x7b, 0x0b, 0x9b, 0x1d, 0x82, 0x53, 0x36, 0xe4, 0x6e, 0xc0, 0xa3, 0x79, 0xc9, 0x2c, 0xbe, + 0xc8, 0x18, 0xfa, 0x8b, 0xbf, 0x40, 0x9a, 0xc8, 0x2d, 0x60, 0x43, 0x08, 0x1e, 0xc4, 0xbc, 0xe9, + 0x93, 0x79, 0xc9, 0x3f, 0x60, 0x70, 0x18, 0x1a, 0xa0, 0xbb, 0x05, 0x6c, 0x09, 0xc7, 0x6e, 0xd9, + 0xf2, 0x78, 0x5e, 0x72, 0x14, 0x00, 0x19, 0x00, 0xe6, 0x2d, 0x57, 0x38, 0x1c, 0xfb, 0x65, 0xcb, + 0xa7, 0xf3, 0x92, 0xc7, 0x80, 0xcd, 0xc9, 0x66, 0x2e, 0x95, 0x6b, 0xc1, 0x79, 0x4e, 0xc9, 0x71, + 0xbe, 0xc8, 0x18, 0xae, 0x42, 0xde, 0xdf, 0x02, 0x87, 0xe3, 0x59, 0xd9, 0xf2, 0x99, 0xc0, 0x83, + 0x60, 0x79, 0x61, 0x71, 0x79, 0xcb, 0x6b, 0x4c, 0xd9, 0x6e, 0xde, 0xf2, 0xc7, 0x79, 0xc9, 0x95, + 0x60, 0x53, 0x60, 0x4b, 0x78, 0xcb, 0x63, 0x4c, 0x11, 0x39, 0x6f, 0xf9, 0x7c, 0x5e, 0xf2, 0xa9, + 0x2f, 0x32, 0x3e, 0x86, 0x8d, 0xfc, 0x92, 0x3b, 0x80, 0xa9, 0x87, 0x6a, 0x33, 0x53, 0xc6, 0xc7, + 0xf8, 0x52, 0x5a, 0x8b, 0xce, 0xdf, 0xdb, 0x1f, 0x49, 0x97, 0x5a, 0xa5, 0xd2, 0xba, 0xf0, 0xd7, + 0xdd, 0x56, 0x9d, 0xb2, 0x42, 0xd7, 0x32, 0xde, 0xfa, 0x1f, 0xf8, 0xbd, 0xce, 0x88, 0x65, 0x47, + 0xd4, 0xac, 0x05, 0x79, 0xff, 0x67, 0x56, 0x71, 0x23, 0x85, 0x2c, 0x75, 0xa3, 0xd3, 0xa9, 0x47, + 0x56, 0x9d, 0x38, 0x06, 0x78, 0x3a, 0xb9, 0xd7, 0x87, 0xec, 0xed, 0xfd, 0xde, 0xf9, 0xfe, 0x96, + 0xac, 0x3b, 0xbf, 0x37, 0x02, 0x5d, 0xe9, 0x7e, 0xa7, 0x48, 0x1e, 0xe1, 0xef, 0xb1, 0xc0, 0x2f, + 0x4b, 0x0d, 0x2b, 0xa3, 0xe9, 0x85, 0x1f, 0xbc, 0x74, 0xe3, 0xfc, 0xc9, 0xdd, 0x38, 0x14, 0x04, + 0xe6, 0xbf, 0xa4, 0x69, 0x2f, 0x38, 0xb6, 0xb2, 0xc7, 0xb9, 0xa7, 0x29, 0xb1, 0xc8, 0x83, 0x3c, + 0x14, 0x0d, 0xfe, 0xd8, 0x07, 0xd1, 0x97, 0x2f, 0xc9, 0xde, 0x5e, 0xfc, 0xc0, 0xb2, 0x48, 0x1e, + 0xe7, 0x4e, 0x19, 0x80, 0xe9, 0xba, 0x83, 0x7e, 0x07, 0x22, 0x78, 0xfc, 0xbb, 0x10, 0x45, 0x07, + 0xc0, 0x7f, 0x1f, 0x77, 0x3a, 0x34, 0xc2, 0xf3, 0x3b, 0x77, 0x2a, 0x7e, 0x7f, 0x1b, 0xaf, 0x7f, + 0x1f, 0xff, 0x4e, 0x81, 0xfc, 0xfe, 0x36, 0x07, 0x72, 0xdd, 0x9c, 0x5f, 0x20, 0xcc, 0x4f, 0xc8, + 0x0d, 0xda, 0xf7, 0xf2, 0xe4, 0x98, 0xce, 0xbe, 0xdb, 0x36, 0xb7, 0xeb, 0xe8, 0xaf, 0xce, 0x46, + 0xc8, 0xc7, 0x80, 0x2c, 0xa5, 0xce, 0xe3, 0xbf, 0x67, 0x63, 0xae, 0xfa, 0x75, 0xe4, 0xeb, 0x6a, + 0x22, 0x91, 0x2f, 0x46, 0xac, 0xaf, 0xa3, 0xee, 0x93, 0x5a, 0x44, 0x3b, 0x12, 0xb3, 0xa6, 0x68, + 0xcc, 0x9a, 0x35, 0xaa, 0xc7, 0x17, 0x33, 0xa9, 0x0f, 0x29, 0xbc, 0x49, 0x4a, 0xba, 0x92, 0xf8, + 0x3e, 0x84, 0x3c, 0x21, 0xa3, 0x86, 0xf7, 0x00, 0x79, 0x17, 0x1b, 0x42, 0x85, 0xe4, 0x07, 0x1e, + 0x2c, 0x2c, 0x5f, 0x9e, 0x67, 0x95, 0x97, 0x79, 0x5f, 0xe5, 0x40, 0x42, 0xd2, 0x15, 0xf7, 0x53, + 0x31, 0xcc, 0x98, 0x94, 0x4b, 0x49, 0x2c, 0x61, 0x29, 0x93, 0x9b, 0xdb, 0xc8, 0x0a, 0x20, 0x34, + 0xb0, 0x76, 0xed, 0xcc, 0x9d, 0x77, 0x13, 0xd0, 0x75, 0xaa, 0xeb, 0x9b, 0xed, 0x77, 0x8a, 0xc0, + 0xab, 0xb0, 0xd4, 0x71, 0x22, 0xec, 0x28, 0x46, 0xae, 0xbd, 0x4a, 0xc9, 0x18, 0x75, 0x20, 0x1b, + 0x81, 0x84, 0x29, 0x41, 0xe0, 0xfc, 0x87, 0xd0, 0x96, 0x14, 0x59, 0xf5, 0xa5, 0x21, 0xd2, 0x84, + 0x36, 0xf9, 0x9a, 0x91, 0x3a, 0x79, 0xc0, 0x32, 0xca, 0x83, 0x5e, 0xe5, 0xd4, 0x20, 0xfc, 0xcc, + 0x4f, 0xe5, 0x33, 0x14, 0x6f, 0xa6, 0x24, 0xc1, 0x10, 0x62, 0x8a, 0x48, 0xba, 0xdf, 0x69, 0x20, + 0x4d, 0x5c, 0xa6, 0x3c, 0x94, 0xeb, 0xe9, 0xf0, 0x44, 0x28, 0x21, 0xe3, 0x09, 0x68, 0x28, 0x0f, + 0x6e, 0xb0, 0x17, 0x1e, 0x28, 0x93, 0x3c, 0xfd, 0x67, 0x4b, 0xb6, 0x42, 0xfa, 0x85, 0x66, 0x15, + 0xfc, 0x93, 0x40, 0x6a, 0x41, 0x9e, 0x43, 0x60, 0x59, 0x96, 0x5c, 0x8e, 0x96, 0xa5, 0x02, 0x92, + 0x35, 0x31, 0xab, 0x6c, 0xf8, 0x7d, 0x9c, 0xef, 0x33, 0x6f, 0xfc, 0x29, 0xa3, 0xe9, 0x79, 0x9f, + 0x60, 0x4f, 0xe9, 0x9f, 0x54, 0xda, 0xdb, 0x23, 0x87, 0x56, 0x06, 0x69, 0x40, 0x42, 0xa6, 0xc2, + 0x29, 0xa9, 0xc4, 0x90, 0x1f, 0x7c, 0x1f, 0xf3, 0x78, 0x70, 0x93, 0xbe, 0x0a, 0x0a, 0xbe, 0xb4, + 0xc3, 0xc0, 0x03, 0xaa, 0xda, 0xef, 0x8c, 0xf9, 0xda, 0x1d, 0x5a, 0x28, 0x42, 0x87, 0xa8, 0xb3, + 0x1f, 0x95, 0x14, 0x4d, 0x04, 0xf8, 0x4e, 0xca, 0x84, 0x08, 0xba, 0x24, 0x90, 0xc8, 0x91, 0x4d, + 0xff, 0x9e, 0x4d, 0xb2, 0xe9, 0xd4, 0xfa, 0x29, 0x83, 0x44, 0x0d, 0x45, 0x6a, 0x49, 0x88, 0x7a, + 0x13, 0xb5, 0xe7, 0x8d, 0x08, 0x91, 0xfa, 0xf6, 0x64, 0xc2, 0x32, 0xb6, 0x25, 0x40, 0xb1, 0xc8, + 0x59, 0xe5, 0xed, 0x9a, 0xcd, 0x36, 0xfb, 0xf2, 0x45, 0xe0, 0x2c, 0x3c, 0x97, 0xf2, 0xce, 0x99, + 0xb3, 0xe2, 0x18, 0x29, 0xca, 0xdb, 0x97, 0xc2, 0x20, 0xea, 0x52, 0x39, 0x95, 0xc7, 0x5b, 0xb3, + 0x0f, 0xf1, 0x47, 0x9a, 0x12, 0xb4, 0xb7, 0x27, 0x64, 0x02, 0xb1, 0xd9, 0x4c, 0x78, 0x3e, 0x10, + 0x1b, 0xde, 0xa3, 0x11, 0xb8, 0x07, 0x96, 0x95, 0xb0, 0xa4, 0xa1, 0x68, 0x15, 0x86, 0x0d, 0x38, + 0x7e, 0x87, 0xd3, 0xc2, 0x0b, 0xd4, 0x01, 0xb6, 0xdb, 0x81, 0xca, 0x0d, 0x41, 0x44, 0x01, 0x1a, + 0x97, 0x99, 0x87, 0xdf, 0xdf, 0x26, 0x6b, 0xb6, 0xfe, 0xdf, 0xdf, 0xba, 0xeb, 0xdf, 0xa5, 0x35, + 0x4f, 0x61, 0x25, 0x51, 0x96, 0x04, 0x24, 0xed, 0x12, 0xa9, 0xef, 0xc7, 0xc9, 0x89, 0xed, 0xce, + 0xbb, 0xdd, 0x0f, 0x31, 0x4e, 0x3e, 0x82, 0x50, 0xa3, 0x40, 0x3f, 0x50, 0x24, 0x09, 0xd7, 0x5a, + 0xa7, 0x9b, 0x5b, 0xcb, 0x15, 0x5e, 0x98, 0x0a, 0xe9, 0x2e, 0xe4, 0x0a, 0xfd, 0x4c, 0xec, 0xcf, + 0xaf, 0xec, 0x25, 0xdf, 0xcf, 0x6e, 0x0a, 0x19, 0x99, 0xe0, 0xe2, 0xe6, 0x61, 0x06, 0x6a, 0x43, + 0x40, 0x54, 0x23, 0xf0, 0xbb, 0xf1, 0x83, 0xda, 0xba, 0xa0, 0x78, 0xc2, 0x33, 0x6e, 0xf2, 0xf7, + 0xca, 0x30, 0x17, 0xc8, 0xeb, 0x14, 0xa2, 0x9b, 0x09, 0x8b, 0x3c, 0xfc, 0x3d, 0xc3, 0x3f, 0x65, + 0xf8, 0x53, 0x86, 0x3f, 0x11, 0x9c, 0xa6, 0xf8, 0x35, 0xa9, 0x66, 0x0e, 0xd0, 0x7f, 0xfe, 0x5e, + 0x04, 0x1a, 0x64, 0x09, 0xff, 0x54, 0x5c, 0x28, 0xd2, 0xe4, 0x53, 0x66, 0x65, 0xd6, 0x54, 0x40, + 0x2a, 0x27, 0x98, 0x05, 0x11, 0xe4, 0xd2, 0xfe, 0x3d, 0x3b, 0xb4, 0xb2, 0xc9, 0x27, 0x02, 0xcf, + 0xbb, 0x3f, 0x65, 0x87, 0x87, 0xa5, 0x7f, 0xa0, 0xf7, 0x53, 0x36, 0x49, 0x53, 0x9a, 0x72, 0xd3, + 0xe0, 0x90, 0x9d, 0x08, 0x32, 0xb2, 0xe1, 0x58, 0xc8, 0x82, 0x44, 0x34, 0x67, 0xaa, 0x92, 0xae, + 0xf1, 0x3d, 0xe3, 0x1c, 0x3c, 0x06, 0xf4, 0x90, 0x14, 0x34, 0x3f, 0xb5, 0x7e, 0x7a, 0x9c, 0x8e, + 0xc9, 0xbe, 0x40, 0x96, 0x31, 0xa9, 0xf0, 0x6d, 0x2b, 0x63, 0xc1, 0x78, 0xd2, 0xeb, 0xa1, 0xe9, + 0x4f, 0x13, 0x29, 0xfa, 0xc1, 0x8a, 0x70, 0xf4, 0xd0, 0x4a, 0x5b, 0x38, 0xd4, 0xb9, 0xc0, 0xa1, + 0x2c, 0xcb, 0xfa, 0xa9, 0x89, 0xce, 0x50, 0xfb, 0x3f, 0xc9, 0x04, 0xac, 0x81, 0xf4, 0xb2, 0x3f, + 0xa1, 0xd0, 0x29, 0xe7, 0x9a, 0x2c, 0x85, 0xf4, 0x70, 0xa7, 0x77, 0x8b, 0xec, 0xdc, 0x45, 0xec, + 0xad, 0xc2, 0x55, 0x5a, 0x52, 0x3f, 0xe7, 0x81, 0x6c, 0xda, 0x18, 0xb2, 0x29, 0x62, 0xeb, 0x27, + 0x9c, 0x58, 0x9f, 0x70, 0x60, 0x7d, 0xc2, 0x9e, 0xf5, 0x13, 0x9b, 0x53, 0xfa, 0xc0, 0xb2, 0x7e, + 0xe2, 0x29, 0x0a, 0x2b, 0x2b, 0x3a, 0x48, 0xb1, 0x6f, 0x45, 0x0f, 0x53, 0x3c, 0xb7, 0xe2, 0x5e, + 0xf0, 0xc3, 0x0a, 0x87, 0x56, 0xd2, 0xf3, 0x7e, 0x58, 0x4d, 0x20, 0xfb, 0x22, 0xb5, 0x7c, 0x1c, + 0x5b, 0x01, 0x4e, 0x20, 0x87, 0xc0, 0x9a, 0x63, 0xcf, 0x0a, 0xd7, 0x81, 0xdf, 0x8d, 0x1e, 0x58, + 0xd6, 0xa7, 0xcd, 0xb0, 0x7a, 0x31, 0x81, 0x13, 0x93, 0x19, 0x22, 0xd7, 0x50, 0xa4, 0xaa, 0x40, + 0xa8, 0xef, 0xbb, 0x31, 0x16, 0x99, 0x54, 0x9c, 0xe6, 0x8b, 0x4e, 0x79, 0x43, 0x9f, 0xfc, 0x11, + 0x76, 0xb3, 0x7e, 0xfa, 0x47, 0x02, 0x21, 0xf3, 0xb4, 0x85, 0x23, 0x1e, 0xdb, 0x11, 0x74, 0xeb, + 0x07, 0x50, 0x60, 0xe1, 0x8f, 0x15, 0x1c, 0xdb, 0x4c, 0x20, 0x27, 0x5e, 0xe8, 0xf3, 0xb7, 0x48, + 0xdc, 0xf5, 0x6e, 0xd6, 0x3f, 0x7b, 0xf3, 0xf4, 0xe4, 0xcd, 0xfe, 0x27, 0xe9, 0xe0, 0x22, 0xc2, + 0x91, 0x95, 0xf5, 0x97, 0xf1, 0x15, 0x35, 0xc7, 0x73, 0xa0, 0x28, 0x10, 0x11, 0x5d, 0x31, 0x21, + 0xab, 0x39, 0x6e, 0xeb, 0xa5, 0x77, 0x14, 0x49, 0x07, 0xaf, 0x59, 0x37, 0x8b, 0x55, 0xd8, 0x25, + 0xf8, 0x25, 0xa1, 0x6a, 0x1f, 0xed, 0x34, 0xc2, 0x29, 0xe4, 0xf0, 0xe4, 0x8f, 0x62, 0xc8, 0x2a, + 0x60, 0x17, 0xec, 0xdf, 0x84, 0xb5, 0x86, 0x0c, 0x9e, 0xfc, 0x46, 0x06, 0xc9, 0x0a, 0xdd, 0x00, + 0x67, 0xfd, 0xb3, 0xd3, 0x13, 0xa9, 0x0a, 0x92, 0x07, 0x20, 0x79, 0x82, 0x62, 0x11, 0x97, 0x34, + 0x3f, 0xa3, 0xfa, 0x66, 0x6a, 0xbd, 0x00, 0x6e, 0x8d, 0x23, 0x0b, 0x1c, 0x0c, 0xa4, 0x1f, 0x91, + 0x59, 0x97, 0xb0, 0x9e, 0x70, 0x4c, 0xef, 0x45, 0x00, 0x50, 0xf9, 0x30, 0x92, 0x00, 0xa6, 0x6e, + 0xb6, 0xff, 0x47, 0x2c, 0x1d, 0xbc, 0x8c, 0x72, 0x76, 0xe1, 0x61, 0x37, 0xe7, 0x15, 0x2b, 0xcb, + 0xa3, 0x73, 0x81, 0x5c, 0x13, 0xec, 0x5b, 0x1e, 0x85, 0x73, 0x05, 0xfd, 0xcc, 0x8b, 0x8b, 0x58, + 0xc2, 0x61, 0x71, 0x91, 0x48, 0x78, 0x61, 0x79, 0x74, 0xe7, 0x3c, 0xba, 0x73, 0xbe, 0x84, 0x5d, + 0x09, 0xcf, 0x2a, 0xf7, 0xe6, 0x70, 0x6f, 0x02, 0x7d, 0xbb, 0x8b, 0xf8, 0xb2, 0x0b, 0x06, 0xc5, + 0x82, 0x75, 0x4f, 0xaf, 0x43, 0x3c, 0xc7, 0x33, 0x86, 0xbd, 0xd7, 0x2d, 0x7d, 0xdd, 0x88, 0xef, + 0x5d, 0x8b, 0x8b, 0xe4, 0xe1, 0x1b, 0xda, 0x40, 0x88, 0xfd, 0xbe, 0x28, 0x14, 0xd8, 0xc3, 0x27, + 0x51, 0x4b, 0x92, 0xe5, 0x1f, 0x49, 0x86, 0x02, 0x86, 0x9c, 0x3c, 0x99, 0x8d, 0x15, 0xea, 0xa4, + 0x9f, 0x34, 0xa0, 0x27, 0xb7, 0x3a, 0x45, 0x7a, 0x6c, 0xef, 0x13, 0xce, 0x35, 0x8a, 0x09, 0x79, + 0xf8, 0x12, 0x12, 0xed, 0x7f, 0x9a, 0x48, 0xe4, 0xc0, 0x7a, 0x49, 0x70, 0xba, 0xbf, 0xcf, 0x73, + 0x17, 0x5e, 0x02, 0x99, 0xd1, 0x0d, 0x61, 0x2d, 0x2f, 0x81, 0x61, 0x47, 0x92, 0x65, 0x59, 0xca, + 0x44, 0x02, 0x5a, 0xd9, 0xdf, 0x9f, 0x2a, 0x44, 0xdb, 0x19, 0x99, 0xc7, 0x50, 0xc2, 0xca, 0xb1, + 0x1d, 0xf8, 0x6e, 0x34, 0xad, 0xcb, 0x12, 0x2c, 0x08, 0x7a, 0xcd, 0x58, 0x04, 0x68, 0x2b, 0x4a, + 0xce, 0xb7, 0x7f, 0x8b, 0x38, 0xe3, 0x8e, 0x39, 0xfa, 0x11, 0xba, 0xbb, 0x04, 0x70, 0xfd, 0x65, + 0xc1, 0xec, 0xf2, 0x95, 0xe9, 0xba, 0x78, 0x45, 0x17, 0xc6, 0xed, 0x07, 0xa9, 0xdc, 0x5d, 0x49, + 0x79, 0x37, 0x2b, 0x9a, 0x58, 0xc8, 0x9e, 0x3f, 0x80, 0xee, 0x77, 0xa7, 0x3a, 0x3a, 0xbe, 0x6f, + 0x01, 0xeb, 0x70, 0x29, 0x3e, 0xb8, 0x80, 0x7d, 0x80, 0x13, 0xa1, 0xe5, 0x52, 0xa0, 0x56, 0x00, + 0xd4, 0xa2, 0xb8, 0x08, 0xf8, 0x1a, 0x3d, 0x70, 0xe9, 0x2e, 0x87, 0x98, 0xbe, 0x20, 0x4d, 0x04, + 0xc0, 0xc2, 0x02, 0x30, 0xb7, 0xff, 0xeb, 0xc9, 0x9b, 0x33, 0x3a, 0xc6, 0xcc, 0x52, 0xf0, 0xb5, + 0xe5, 0x52, 0xa4, 0x08, 0xab, 0x7d, 0x5c, 0x17, 0x7d, 0x04, 0x7e, 0x77, 0xb6, 0xbf, 0x5f, 0xb4, + 0xbb, 0x06, 0x2c, 0xb4, 0x2c, 0xff, 0xbe, 0xf3, 0xb9, 0xb1, 0x3e, 0x15, 0xf2, 0xc7, 0xef, 0xcd, + 0x7a, 0x8a, 0x84, 0x8f, 0xf9, 0x04, 0xe6, 0xf8, 0x46, 0x9a, 0xf8, 0xd6, 0x8c, 0xce, 0x17, 0xc6, + 0x38, 0x66, 0x33, 0x85, 0xa9, 0x87, 0x78, 0xce, 0x66, 0x0a, 0x17, 0x0b, 0x7c, 0x5c, 0x58, 0x57, + 0x0b, 0x01, 0x3b, 0x7f, 0x8c, 0x45, 0xed, 0xea, 0xe1, 0x45, 0x64, 0x59, 0xd6, 0x93, 0xe8, 0xf1, + 0x6f, 0xd1, 0x38, 0x7b, 0xf8, 0x1a, 0x2e, 0x8e, 0xa2, 0xc7, 0xbf, 0xc4, 0xe3, 0xec, 0xe1, 0x4b, + 0xb8, 0x78, 0x17, 0x3f, 0xa6, 0x44, 0x3f, 0xa6, 0x48, 0x5d, 0xf6, 0xf2, 0x59, 0x48, 0x76, 0xbd, + 0xa5, 0x8c, 0x69, 0x9c, 0x8b, 0x52, 0xfc, 0xea, 0xe8, 0xfc, 0x1f, 0xe5, 0xd5, 0x93, 0x5f, 0x2e, + 0x4e, 0xce, 0xc7, 0xb9, 0x6e, 0x8e, 0x9f, 0xbc, 0xb8, 0x28, 0xaf, 0xd6, 0x38, 0xb5, 0x7e, 0x8e, + 0xfb, 0x09, 0xf1, 0x56, 0x2e, 0xe9, 0x76, 0x79, 0xf4, 0x9a, 0x26, 0x34, 0x0b, 0x09, 0x97, 0x11, + 0x3d, 0x5d, 0x93, 0x4f, 0x05, 0x84, 0x64, 0x5a, 0x49, 0x16, 0xfe, 0x35, 0x16, 0x58, 0x78, 0x7a, + 0xd8, 0x2e, 0xc0, 0xa8, 0x12, 0x07, 0xf5, 0x10, 0xa1, 0x2c, 0x0b, 0x46, 0x11, 0x99, 0xd9, 0x20, + 0xc1, 0x52, 0xb4, 0x8a, 0xf8, 0xd1, 0xc6, 0xfc, 0x18, 0x45, 0x4a, 0x05, 0x68, 0x91, 0x36, 0x79, + 0x76, 0x7a, 0x92, 0xdf, 0xfd, 0x94, 0xdf, 0x25, 0x9c, 0xee, 0xe8, 0x53, 0x1c, 0x5b, 0x84, 0x4b, + 0x35, 0x10, 0xd3, 0xe9, 0xde, 0xa7, 0xbd, 0xbd, 0x6e, 0xce, 0x9e, 0x61, 0x4a, 0x8c, 0x3d, 0xc3, + 0x5e, 0xc5, 0x12, 0x4e, 0xa7, 0x53, 0xeb, 0x53, 0x8b, 0x04, 0x7f, 0x9b, 0xab, 0xa9, 0x79, 0xca, + 0x08, 0xcb, 0x13, 0xe1, 0x89, 0x84, 0xb9, 0xa9, 0xc1, 0x0e, 0x3a, 0xa4, 0x8f, 0x33, 0x8a, 0x97, + 0xfc, 0x5c, 0x0f, 0xf4, 0x4f, 0x8a, 0x45, 0xa4, 0x0e, 0x13, 0xc8, 0x5f, 0xa0, 0xa8, 0xec, 0x49, + 0x8f, 0x83, 0x71, 0x37, 0xfa, 0xe0, 0x7e, 0xb4, 0x80, 0xc5, 0x03, 0x48, 0x01, 0xf6, 0x20, 0x29, + 0x8d, 0x62, 0x2c, 0x95, 0x15, 0x41, 0x74, 0xd9, 0x8d, 0x05, 0x8d, 0x8b, 0xf5, 0xf4, 0x06, 0x0a, + 0x9e, 0x6e, 0xe9, 0x2e, 0xef, 0x0c, 0x2e, 0xa4, 0x4a, 0xd7, 0x49, 0x45, 0x8c, 0x5d, 0xb6, 0x0a, + 0xc3, 0x1e, 0x65, 0x10, 0x15, 0x61, 0x48, 0x57, 0x09, 0xc8, 0x29, 0x12, 0x81, 0xcb, 0x6f, 0xc0, + 0x7c, 0x99, 0x34, 0xcb, 0xef, 0x80, 0x34, 0xc9, 0x85, 0x16, 0xc8, 0xee, 0x78, 0x6f, 0xef, 0x41, + 0xb2, 0xb7, 0xf7, 0x20, 0xd8, 0xbc, 0xff, 0x2f, 0x69, 0x75, 0x56, 0xa8, 0x96, 0x75, 0xb3, 0x70, + 0xe2, 0x10, 0xf1, 0x73, 0x9a, 0x25, 0x3f, 0x7f, 0xac, 0x8c, 0x93, 0xc7, 0xf2, 0xb8, 0xa7, 0x94, + 0xf0, 0xbf, 0xe2, 0xf0, 0x97, 0xbe, 0x94, 0xbd, 0xbd, 0x7f, 0x64, 0xdd, 0x42, 0xd5, 0x29, 0xef, + 0x3f, 0x26, 0x63, 0xc1, 0x9c, 0x57, 0xa5, 0x32, 0xe3, 0x87, 0xe6, 0x60, 0xba, 0x24, 0x08, 0xbb, + 0xe9, 0x81, 0x59, 0xa4, 0xd8, 0x44, 0x4f, 0x82, 0x8c, 0x9d, 0x38, 0x19, 0xa7, 0x38, 0x7a, 0x52, + 0x9c, 0x3f, 0x19, 0x47, 0x02, 0xb9, 0xce, 0x4a, 0xbc, 0xc0, 0x51, 0x6e, 0xc3, 0x64, 0x9b, 0xd4, + 0x34, 0x3a, 0x4b, 0x2a, 0x42, 0x04, 0x6d, 0x8d, 0x2e, 0x38, 0x9a, 0x22, 0xb9, 0x3c, 0x3b, 0xc5, + 0xec, 0x68, 0x30, 0x5b, 0xa0, 0x5f, 0xaf, 0xe6, 0x22, 0x28, 0xce, 0x97, 0x91, 0x07, 0xcc, 0x28, + 0x81, 0x31, 0x23, 0xc0, 0xe0, 0xec, 0xcb, 0x97, 0x96, 0xa3, 0x3e, 0x2e, 0x63, 0x66, 0xe9, 0x92, + 0xb8, 0x50, 0xc8, 0x29, 0x5e, 0x66, 0xac, 0x1c, 0xd8, 0x55, 0x8c, 0xec, 0x64, 0xb6, 0x02, 0xc5, + 0x38, 0xe5, 0x9c, 0x2d, 0xb4, 0xc8, 0x24, 0xec, 0x03, 0x13, 0xd8, 0xdb, 0xeb, 0xc6, 0x16, 0xfb, + 0x29, 0xe1, 0x90, 0x76, 0xbe, 0xb7, 0xd7, 0x4d, 0x2c, 0xf6, 0x53, 0xc2, 0x1c, 0x9e, 0x90, 0xe5, + 0xd9, 0x96, 0xae, 0xb5, 0xbd, 0xbd, 0x6e, 0x6a, 0xb1, 0xbb, 0x42, 0xa3, 0x45, 0xec, 0x3d, 0x4b, + 0xca, 0x44, 0x74, 0xa1, 0x71, 0x60, 0x55, 0x9f, 0x82, 0xee, 0x13, 0xf6, 0xed, 0x30, 0x8c, 0xaf, + 0x88, 0xc7, 0x16, 0x3c, 0x65, 0x5e, 0x0b, 0x61, 0x05, 0x0a, 0x4f, 0x42, 0x37, 0xb6, 0x40, 0x2b, + 0x2b, 0xc1, 0x63, 0x3f, 0x59, 0x2a, 0x9f, 0xb0, 0x81, 0x6e, 0x65, 0x03, 0x57, 0x6b, 0x8b, 0xa2, + 0x4d, 0xcc, 0xf3, 0x20, 0xa1, 0x52, 0xe6, 0x7d, 0xf7, 0x2b, 0xf6, 0xc1, 0xc0, 0x86, 0x37, 0xd9, + 0xa1, 0xed, 0x5c, 0xd4, 0xe1, 0x79, 0x7e, 0x44, 0x11, 0xea, 0xbf, 0xff, 0x49, 0xba, 0x9c, 0xfd, + 0x66, 0x2c, 0x51, 0x35, 0x65, 0x1c, 0xd6, 0xe5, 0x7c, 0x77, 0xc5, 0xb8, 0xf1, 0x27, 0xd2, 0x75, + 0x25, 0x4c, 0x79, 0xc6, 0x4f, 0xf8, 0xec, 0xf4, 0x64, 0xfc, 0x09, 0x57, 0x57, 0x60, 0xec, 0x61, + 0x76, 0xb0, 0x60, 0x1c, 0x5a, 0xd3, 0xef, 0xbb, 0x21, 0xce, 0x24, 0x1c, 0xa4, 0xef, 0xa8, 0xa5, + 0x19, 0x5a, 0x53, 0xc1, 0x1d, 0x17, 0x3e, 0x28, 0x8c, 0xa6, 0x3b, 0xa6, 0x84, 0x08, 0x33, 0x8a, + 0x84, 0xa9, 0xb1, 0x17, 0x6b, 0x1e, 0xba, 0xb0, 0xa0, 0xbf, 0x9f, 0x0e, 0xad, 0x70, 0x6f, 0x2f, + 0x3c, 0xcc, 0xd6, 0x38, 0x48, 0x65, 0x18, 0x38, 0x04, 0x66, 0x9c, 0x43, 0x72, 0x1a, 0x67, 0xf4, + 0xe6, 0x83, 0x39, 0x17, 0xd3, 0x7b, 0x7b, 0xf0, 0x8b, 0x3e, 0xeb, 0x86, 0x00, 0xf0, 0x99, 0x47, + 0xc1, 0xed, 0x86, 0x7b, 0x9f, 0x40, 0xfb, 0xf9, 0x84, 0x23, 0x32, 0x63, 0x33, 0xea, 0xd1, 0x29, + 0x91, 0x3f, 0xc2, 0x71, 0x37, 0xc4, 0x0b, 0x89, 0xf5, 0xbc, 0xc0, 0xe9, 0x1f, 0x09, 0x9f, 0xf1, + 0x0f, 0xb4, 0x81, 0xed, 0x79, 0x79, 0x83, 0xef, 0xbb, 0xe1, 0xfe, 0x02, 0xee, 0xa5, 0x2b, 0x47, + 0xb8, 0xd7, 0xa3, 0xf7, 0x16, 0xab, 0x50, 0xb8, 0xf7, 0x03, 0xbd, 0xb7, 0x8c, 0xaf, 0xf2, 0x7b, + 0xbf, 0xc6, 0x34, 0xa6, 0xb2, 0x90, 0xb0, 0x17, 0x5c, 0x8a, 0x0d, 0xcf, 0xa3, 0x2e, 0x6d, 0x0b, + 0xdd, 0xfe, 0x91, 0x9c, 0xd2, 0x29, 0xfe, 0x10, 0xc2, 0xb0, 0xa7, 0x05, 0x60, 0xfb, 0x0b, 0x18, + 0xb2, 0xbc, 0xee, 0x2d, 0x60, 0xb8, 0xf2, 0xfa, 0x87, 0x05, 0x0e, 0xa2, 0x4b, 0x78, 0xf5, 0x3c, + 0x62, 0x3b, 0x05, 0x28, 0x39, 0x4e, 0xbe, 0x7c, 0xe9, 0xc2, 0xdc, 0xfd, 0x2f, 0x5f, 0xba, 0xbe, + 0x45, 0xc5, 0xbc, 0x84, 0x7d, 0x80, 0x03, 0x22, 0xdc, 0x59, 0x4c, 0xb1, 0x1f, 0xde, 0x4a, 0x1f, + 0xcf, 0xb3, 0x6e, 0x88, 0x57, 0xd2, 0x98, 0xa4, 0xf4, 0x2f, 0xf6, 0x73, 0xe2, 0xa0, 0x63, 0xc0, + 0x91, 0x44, 0xb6, 0xe5, 0x1e, 0xb3, 0x8a, 0xbc, 0xf2, 0x50, 0x64, 0x98, 0x4b, 0xa2, 0x2f, 0x5f, + 0xf2, 0x9f, 0xd3, 0x55, 0x13, 0x0f, 0x68, 0x49, 0xd2, 0x7e, 0xd9, 0x6d, 0x89, 0x01, 0xd4, 0xff, + 0xcb, 0x30, 0x3a, 0x47, 0x83, 0xa2, 0x4f, 0xae, 0x7e, 0xd7, 0xb2, 0xe8, 0x57, 0xd2, 0xe4, 0x9a, + 0x7a, 0x01, 0x42, 0x9c, 0x3e, 0x96, 0xc7, 0xd7, 0xbc, 0x75, 0xaf, 0x78, 0x0d, 0x87, 0xd6, 0x35, + 0x58, 0x84, 0x61, 0xe9, 0x9b, 0xbb, 0x27, 0x48, 0xab, 0xed, 0x20, 0xcd, 0xac, 0xf4, 0x71, 0x44, + 0xba, 0xa1, 0x34, 0xf6, 0x33, 0xd0, 0xfd, 0x98, 0x77, 0xa7, 0x3b, 0xb3, 0xbe, 0xef, 0xce, 0x30, + 0xac, 0xf2, 0x83, 0xc5, 0xde, 0xde, 0x83, 0x12, 0x15, 0x67, 0xd2, 0xce, 0x94, 0x11, 0xaf, 0x32, + 0xfa, 0xa5, 0x67, 0xf8, 0x68, 0x39, 0x54, 0x37, 0x42, 0x72, 0x9f, 0x09, 0xc8, 0x52, 0x28, 0xcd, + 0xd6, 0x98, 0x19, 0xb5, 0x4f, 0xc0, 0x4b, 0x0b, 0x1b, 0xf8, 0x96, 0x46, 0xec, 0x24, 0x0c, 0x16, + 0x09, 0xdd, 0x31, 0x3c, 0x93, 0xac, 0xe9, 0xec, 0xf1, 0x62, 0x1c, 0xae, 0x8b, 0xd7, 0xaa, 0x4c, + 0x42, 0x3c, 0xd4, 0xf8, 0xb4, 0x74, 0x16, 0x3c, 0x00, 0xb1, 0x7e, 0xe6, 0x79, 0x1b, 0x96, 0x8b, + 0x5a, 0xd1, 0xd1, 0xa3, 0x0c, 0xcd, 0xa1, 0x4c, 0x02, 0x6d, 0xd9, 0xc9, 0xe5, 0x1e, 0xd5, 0x68, + 0xa8, 0x1c, 0x2c, 0x54, 0x25, 0xda, 0xa0, 0x9b, 0x4a, 0x8f, 0x99, 0x70, 0x4e, 0xa5, 0x71, 0x4a, + 0x5d, 0x36, 0x3f, 0xe1, 0x4f, 0xf8, 0x25, 0xc1, 0x4f, 0x22, 0x7c, 0x11, 0xe1, 0xa3, 0x08, 0xff, + 0x11, 0xe3, 0xd7, 0x11, 0x7e, 0x17, 0xe3, 0x97, 0x11, 0xbe, 0x21, 0xf8, 0xe7, 0x18, 0x3f, 0x2b, + 0xbd, 0x38, 0xaf, 0xc1, 0x81, 0xf3, 0x93, 0xe0, 0xbf, 0xf9, 0x54, 0xba, 0x6f, 0xf0, 0x4b, 0x92, + 0x5f, 0xa8, 0x12, 0x7e, 0x52, 0x26, 0x8e, 0x4a, 0xf8, 0xa2, 0xb8, 0xd0, 0x25, 0x7c, 0x54, 0x5c, + 0x18, 0x12, 0xfe, 0xa3, 0x38, 0xe0, 0x32, 0x94, 0xf0, 0xeb, 0xe2, 0x89, 0x29, 0xe1, 0x77, 0xc5, + 0x93, 0x91, 0x84, 0x5f, 0x16, 0x4f, 0x94, 0x81, 0x34, 0xb9, 0x29, 0xcf, 0x7a, 0x33, 0x5f, 0x46, + 0xce, 0x50, 0x7e, 0x8e, 0xad, 0x0f, 0x1d, 0xc6, 0x2a, 0x3b, 0xb8, 0xc3, 0x77, 0x9c, 0xfe, 0x82, + 0x44, 0xa1, 0x88, 0xcc, 0xe0, 0x77, 0x74, 0xd9, 0xc1, 0xd4, 0xce, 0x63, 0x7f, 0x3a, 0xb8, 0x43, + 0xfe, 0x08, 0x3b, 0xb8, 0x63, 0x7b, 0xd0, 0x34, 0x5d, 0x41, 0xb6, 0xcc, 0x62, 0x05, 0x77, 0x96, + 0xf1, 0x55, 0x07, 0x77, 0xbc, 0xe0, 0x92, 0x3d, 0x3d, 0x65, 0x8f, 0x4f, 0xd9, 0xf3, 0x53, 0xf6, + 0xfa, 0x69, 0xe7, 0x63, 0xe5, 0x04, 0x50, 0x45, 0xa1, 0xa2, 0x56, 0x35, 0x1c, 0x24, 0x29, 0xf7, + 0x01, 0xfc, 0x4a, 0xe5, 0x6e, 0xff, 0x44, 0xc4, 0xe6, 0xa0, 0x66, 0xf6, 0x9f, 0x2d, 0x31, 0xa1, + 0x59, 0x30, 0x91, 0x35, 0x8d, 0xfa, 0xbf, 0x4a, 0x82, 0xee, 0x07, 0x77, 0xb9, 0x1e, 0x9e, 0x51, + 0x62, 0x39, 0xf2, 0xc1, 0x42, 0xed, 0x46, 0xfd, 0x2c, 0xe6, 0x3f, 0xd3, 0x0f, 0xf1, 0xc7, 0x4a, + 0x54, 0xe3, 0xb9, 0x80, 0x4f, 0x5b, 0x4e, 0x79, 0x5b, 0xf2, 0x97, 0x2f, 0xd9, 0x94, 0x6c, 0x26, + 0x8d, 0xab, 0x20, 0xf2, 0xe2, 0x2b, 0x94, 0x06, 0x7f, 0x12, 0x5c, 0x52, 0xe7, 0x07, 0xa5, 0xdf, + 0xa7, 0x27, 0xaa, 0x3f, 0x32, 0xe2, 0xfc, 0xd9, 0xaa, 0xc6, 0x6c, 0x12, 0x1e, 0x53, 0xe1, 0x50, + 0x70, 0xec, 0x2c, 0x35, 0x2e, 0x72, 0x90, 0x49, 0xfb, 0xa0, 0x3b, 0xa9, 0x3f, 0xfc, 0xd0, 0xcd, + 0xc0, 0x94, 0x8a, 0xe1, 0x67, 0x06, 0x4e, 0x29, 0xea, 0xc2, 0x08, 0xac, 0xc2, 0xd3, 0x97, 0x6b, + 0x66, 0x0c, 0x92, 0x74, 0x9c, 0x62, 0xf6, 0xeb, 0x3c, 0xf8, 0x93, 0x8c, 0x23, 0xbc, 0xb0, 0xd3, + 0xcf, 0xe3, 0x04, 0x2f, 0xec, 0x6b, 0x36, 0xcd, 0x71, 0x8c, 0xd3, 0x79, 0xe0, 0x67, 0x4f, 0x6e, + 0xc6, 0x81, 0xa0, 0xb2, 0x9d, 0x46, 0x82, 0x1f, 0xef, 0xb6, 0xd9, 0x43, 0x2c, 0xf4, 0x90, 0x08, + 0x3d, 0x58, 0x69, 0x79, 0xb0, 0x2a, 0xdb, 0x8b, 0xe9, 0x89, 0x8d, 0xe9, 0x34, 0x98, 0x78, 0xd3, + 0x68, 0x6f, 0xaf, 0xeb, 0xf5, 0xac, 0x04, 0xbb, 0xfb, 0x96, 0x9b, 0xe6, 0x67, 0x2e, 0xc8, 0x0f, + 0x11, 0xf6, 0xad, 0xd5, 0x3e, 0x9d, 0xab, 0xed, 0xa4, 0x5d, 0x4f, 0xea, 0x29, 0xe0, 0x24, 0x81, + 0x78, 0x02, 0xf8, 0x47, 0x0e, 0x21, 0x12, 0x4e, 0x1e, 0xaa, 0x0f, 0x2c, 0xc8, 0x20, 0xe7, 0x4a, + 0x27, 0xb9, 0xce, 0x4e, 0xc7, 0x2e, 0x66, 0xb5, 0x8f, 0xc7, 0x3e, 0x0e, 0xd2, 0x5f, 0x49, 0x12, + 0x8f, 0xe7, 0x38, 0x48, 0x4f, 0x41, 0x58, 0xb2, 0xbf, 0xcf, 0xc6, 0x0b, 0xde, 0xe4, 0xd9, 0x78, + 0x25, 0xcc, 0xed, 0x7d, 0x5c, 0xee, 0x35, 0x0b, 0x4b, 0x04, 0x69, 0x71, 0x3c, 0xb0, 0xb1, 0xb3, + 0x76, 0xfd, 0xb8, 0x7d, 0x56, 0xfa, 0x96, 0x59, 0x02, 0x34, 0xed, 0xa8, 0x9b, 0x8a, 0x05, 0x08, + 0xc8, 0x16, 0xee, 0xb9, 0x8c, 0x03, 0xf8, 0xa6, 0x46, 0x7e, 0xfe, 0xb5, 0xb3, 0x1f, 0x49, 0x6b, + 0x01, 0x17, 0xfe, 0xfc, 0x0a, 0xe8, 0x20, 0x76, 0xe4, 0xda, 0x21, 0xd4, 0xe8, 0xbc, 0x13, 0x50, + 0x52, 0xb0, 0xf8, 0x74, 0x0b, 0x90, 0xac, 0xbb, 0xcd, 0x50, 0x06, 0x62, 0x80, 0xe7, 0xfb, 0x88, + 0x3a, 0xc6, 0x81, 0x44, 0x04, 0xa3, 0xe3, 0x6d, 0xe1, 0x42, 0x02, 0x61, 0x96, 0x6d, 0xa1, 0x99, + 0xd3, 0xa3, 0x67, 0xa2, 0xeb, 0x71, 0x91, 0x95, 0xa1, 0x9d, 0xf7, 0x71, 0x37, 0x05, 0xed, 0xe0, + 0xcf, 0x98, 0x3a, 0x66, 0x78, 0xec, 0x2c, 0x2d, 0xc3, 0xc3, 0x62, 0x18, 0x0d, 0x7c, 0xf2, 0xc9, + 0x86, 0x35, 0x4a, 0x61, 0x91, 0xe8, 0xd2, 0xa7, 0xac, 0xae, 0x2c, 0x5f, 0x2f, 0x1a, 0xaf, 0xa3, + 0xa2, 0x81, 0xc0, 0xf7, 0x37, 0x79, 0x64, 0x8d, 0x4b, 0x08, 0x30, 0xd5, 0x40, 0xe1, 0xc4, 0x9e, + 0x75, 0x12, 0x75, 0x39, 0x89, 0x81, 0x31, 0xec, 0x5a, 0xca, 0xc4, 0x9b, 0x2a, 0xea, 0x63, 0xd7, + 0xf2, 0x7a, 0xda, 0xd8, 0x9b, 0xea, 0xf4, 0x97, 0x3a, 0xf6, 0xa0, 0x02, 0x43, 0xd7, 0xb5, 0xd4, + 0x1c, 0xc1, 0x99, 0xd6, 0xea, 0x0b, 0x86, 0x30, 0xa7, 0x8e, 0x95, 0xb4, 0xaf, 0x70, 0x5b, 0x38, + 0x00, 0xb7, 0x20, 0xa5, 0x00, 0x28, 0xfb, 0x98, 0x74, 0xbb, 0x84, 0x9a, 0x14, 0x3d, 0x45, 0x3a, + 0x70, 0xa5, 0x1f, 0x5c, 0x1c, 0x5a, 0x41, 0x11, 0x8f, 0x5b, 0x58, 0xf3, 0xc9, 0x62, 0x6a, 0xc9, + 0x93, 0x45, 0xcf, 0x72, 0xa5, 0x5b, 0x3f, 0xef, 0xa1, 0x68, 0x70, 0x6d, 0xc9, 0x93, 0xeb, 0xc3, + 0x64, 0x72, 0x9d, 0xa7, 0xd5, 0xdc, 0x58, 0xd1, 0x87, 0xeb, 0x8f, 0xf8, 0x38, 0x27, 0xcb, 0x9b, + 0x69, 0x9e, 0x36, 0xb8, 0x90, 0xf6, 0x56, 0xd2, 0xc4, 0xff, 0x70, 0xfc, 0xd1, 0x82, 0x7f, 0xfa, + 0x36, 0x48, 0xbe, 0x0f, 0xd7, 0x1f, 0xa5, 0x35, 0xd3, 0x18, 0x02, 0xa1, 0xd3, 0xfc, 0x18, 0x54, + 0x4f, 0xc1, 0x37, 0x56, 0x30, 0xb9, 0x9e, 0xca, 0x93, 0xeb, 0x5e, 0x4f, 0xba, 0xb1, 0x6e, 0xe8, + 0x6b, 0x3e, 0xbc, 0x86, 0x67, 0xd6, 0x8c, 0x5e, 0xdd, 0x50, 0xfd, 0x22, 0x04, 0x1b, 0xc5, 0x03, + 0x55, 0x02, 0x2f, 0x80, 0x7c, 0xa5, 0x2a, 0x88, 0x2e, 0x05, 0x11, 0x1a, 0x79, 0xf1, 0xca, 0x09, + 0x21, 0xfa, 0xcb, 0xf1, 0x29, 0x2c, 0x71, 0xe1, 0x59, 0x24, 0xf8, 0x45, 0x18, 0x55, 0x10, 0xa6, + 0x5c, 0x3c, 0xb0, 0xac, 0x6c, 0x93, 0x86, 0xc4, 0xac, 0x0f, 0xba, 0xb5, 0x0b, 0x50, 0x38, 0x50, + 0x9c, 0x78, 0x24, 0x19, 0xa3, 0x67, 0x4b, 0x64, 0x59, 0x68, 0x89, 0xd1, 0xb3, 0x08, 0x7e, 0x44, + 0xa5, 0x82, 0xf2, 0x39, 0x02, 0x5f, 0x39, 0x61, 0x46, 0x54, 0xae, 0xb4, 0x80, 0xd4, 0xb9, 0x65, + 0x16, 0x89, 0x88, 0xfb, 0x7f, 0x44, 0x45, 0x34, 0x8e, 0x47, 0x1f, 0x23, 0xab, 0xb4, 0xab, 0xc1, + 0x6d, 0x02, 0x41, 0x7a, 0xe2, 0x5d, 0xd9, 0x89, 0x97, 0x76, 0x24, 0xfc, 0x80, 0x14, 0xd1, 0x3a, + 0xb2, 0x25, 0x5a, 0xf7, 0x7b, 0x2d, 0x5a, 0xf7, 0xfd, 0x6d, 0xb6, 0x46, 0xc7, 0x6f, 0xdf, 0xbc, + 0x3b, 0xe1, 0x01, 0xbb, 0xdf, 0xcb, 0x4d, 0x76, 0x51, 0xec, 0x7f, 0xe8, 0x2c, 0x41, 0x5a, 0x77, + 0x70, 0x67, 0xde, 0xf9, 0x98, 0x87, 0x21, 0xc8, 0x07, 0x97, 0x46, 0xee, 0x1e, 0xe4, 0xa6, 0xce, + 0x4a, 0x8c, 0x0f, 0xad, 0xa6, 0xab, 0x16, 0x26, 0xf2, 0x3b, 0x1d, 0xa4, 0x0f, 0x81, 0xb7, 0x22, + 0x7c, 0x5d, 0x44, 0x47, 0xd8, 0xbb, 0xbf, 0x33, 0x94, 0x88, 0xad, 0x67, 0x51, 0x97, 0xf4, 0x97, + 0x38, 0x05, 0xf9, 0x4b, 0xbd, 0xe2, 0xf4, 0x46, 0x04, 0x37, 0x22, 0xb8, 0xe1, 0x59, 0x1f, 0x3a, + 0x3f, 0x5e, 0x77, 0x70, 0xe7, 0xc7, 0x1b, 0xd0, 0x06, 0x3a, 0x98, 0xae, 0xc5, 0x15, 0x09, 0x48, + 0x92, 0x66, 0x89, 0x9d, 0xa6, 0x9d, 0xc7, 0x1d, 0xa7, 0x33, 0xee, 0x78, 0x9d, 0x8f, 0x95, 0xc9, + 0x20, 0x4f, 0xa2, 0xfe, 0x8f, 0x82, 0x39, 0xc1, 0x44, 0x76, 0x03, 0xb5, 0x45, 0x27, 0x85, 0x0e, + 0x59, 0xcb, 0x67, 0xcb, 0xdf, 0x4b, 0xcd, 0xa0, 0x66, 0x74, 0xf2, 0x2b, 0x3b, 0x85, 0x1a, 0xd3, + 0xdd, 0xdb, 0x35, 0x26, 0x90, 0xdc, 0x44, 0xdf, 0x1b, 0x13, 0xfc, 0x6c, 0x39, 0x8e, 0xf1, 0xb3, + 0x68, 0xcc, 0x43, 0x76, 0xab, 0x0c, 0xbb, 0x29, 0xb6, 0x53, 0xfc, 0x7d, 0x84, 0x97, 0x19, 0xf6, + 0xd2, 0xaa, 0xb2, 0xf7, 0x0c, 0xfe, 0x59, 0x89, 0x21, 0x3b, 0x37, 0x15, 0x42, 0x76, 0x76, 0x2a, + 0xc6, 0x17, 0xf1, 0xf7, 0x51, 0x25, 0xdc, 0xb8, 0xcc, 0x4f, 0x49, 0xd7, 0x8e, 0x07, 0xf3, 0xbc, + 0xcd, 0x27, 0x47, 0xe7, 0x27, 0x16, 0xa1, 0x7f, 0x58, 0xfa, 0x0f, 0xb0, 0x24, 0x8b, 0x30, 0xce, + 0xc4, 0x32, 0x9a, 0xe0, 0xe8, 0xdf, 0x33, 0x7e, 0xa6, 0xd7, 0x09, 0xb2, 0xd4, 0x4a, 0xd7, 0xbf, + 0xad, 0xa2, 0xd4, 0xf6, 0xc9, 0x4b, 0x28, 0x2d, 0xc1, 0x0f, 0x1b, 0x5b, 0xc5, 0xdb, 0xf9, 0xc1, + 0xa9, 0xc2, 0xe9, 0xb7, 0xca, 0xc0, 0xeb, 0xe7, 0xa6, 0x14, 0x7f, 0x23, 0x4a, 0xb6, 0xcc, 0xe9, + 0x17, 0x17, 0xd4, 0x49, 0xdd, 0x7e, 0xae, 0x10, 0xb9, 0x5b, 0xc2, 0x97, 0xe2, 0x68, 0x19, 0xad, + 0x9f, 0xa9, 0x7e, 0xd0, 0xad, 0xe9, 0x0b, 0xe9, 0x38, 0x12, 0x75, 0x8f, 0x78, 0x6d, 0x25, 0x70, + 0x40, 0xb6, 0x00, 0xf2, 0xff, 0x63, 0xef, 0xcd, 0x97, 0x1a, 0x47, 0xba, 0xc4, 0xd1, 0x57, 0x01, + 0x4d, 0x7d, 0x8c, 0x64, 0xd2, 0x2e, 0xcb, 0x1b, 0x46, 0x6e, 0x41, 0x80, 0xa1, 0x96, 0xee, 0x62, + 0x69, 0xa0, 0xaa, 0xba, 0x8b, 0xcb, 0xb8, 0x84, 0x94, 0xb6, 0xd5, 0xd8, 0x92, 0x3b, 0x95, 0x06, + 0x5c, 0x46, 0x11, 0xf3, 0x10, 0xbf, 0x67, 0xf8, 0xbd, 0xc2, 0xfd, 0xff, 0x3e, 0xca, 0x3c, 0xc9, + 0x8d, 0x93, 0x8b, 0x94, 0x92, 0x65, 0x1b, 0xba, 0xaa, 0x67, 0x89, 0xf9, 0x3a, 0xa2, 0x29, 0x2b, + 0xf7, 0x3c, 0x99, 0x79, 0xf2, 0xec, 0x09, 0xdb, 0xe7, 0xfa, 0x06, 0xf9, 0x36, 0x46, 0x9e, 0x82, + 0x6b, 0x5c, 0xbb, 0xda, 0x71, 0x7f, 0x0a, 0x3a, 0x2e, 0x20, 0x30, 0x70, 0x0c, 0x24, 0x95, 0xc9, + 0x34, 0x1a, 0xea, 0x5e, 0xba, 0xfd, 0xa7, 0xb6, 0xd9, 0x99, 0xfe, 0x14, 0x76, 0xa6, 0xe0, 0x4c, + 0x69, 0x7b, 0x6c, 0xb8, 0xbe, 0xa1, 0x14, 0xf4, 0x6d, 0x6f, 0x01, 0xab, 0x90, 0x18, 0x2e, 0x1a, + 0x5d, 0xb1, 0x19, 0x10, 0xb0, 0x4b, 0xf6, 0x5c, 0xb0, 0xf6, 0x42, 0x4c, 0xac, 0x37, 0x12, 0x48, + 0x82, 0x80, 0x4f, 0x2e, 0x12, 0xf2, 0x61, 0x86, 0x58, 0x99, 0x61, 0x32, 0x62, 0xcf, 0xae, 0x76, + 0xbc, 0x9f, 0xfc, 0x8a, 0x80, 0x4c, 0xc7, 0x93, 0x7e, 0x70, 0x39, 0x5a, 0x66, 0x2a, 0x69, 0x99, + 0xbe, 0xa0, 0x65, 0x86, 0x92, 0x96, 0x19, 0x25, 0xb4, 0xcc, 0x38, 0xb6, 0x4f, 0x03, 0x3d, 0x40, + 0x1e, 0xe8, 0x01, 0x02, 0xdb, 0x45, 0xfd, 0x7d, 0x62, 0x13, 0x06, 0x84, 0x33, 0x60, 0xa6, 0xa3, + 0xeb, 0xf1, 0x8d, 0x61, 0x58, 0xb0, 0x76, 0x22, 0x6d, 0x88, 0xa2, 0xeb, 0xe9, 0x8d, 0x91, 0x80, + 0xe2, 0x63, 0x00, 0xbe, 0x93, 0x73, 0xd8, 0xe1, 0x7d, 0xd8, 0xe0, 0x00, 0x98, 0x8f, 0x6c, 0xbb, + 0x48, 0x53, 0xe2, 0xfc, 0x56, 0x21, 0x4b, 0x67, 0x26, 0x7c, 0xb5, 0xe5, 0xcc, 0xb6, 0xb6, 0x02, + 0x76, 0xdd, 0x77, 0xfc, 0xec, 0x0c, 0x3d, 0x39, 0x43, 0x57, 0xce, 0x70, 0x2a, 0x66, 0xd8, 0x17, + 0xd3, 0xf1, 0x11, 0x61, 0xd7, 0x46, 0x60, 0x7b, 0x68, 0x73, 0xca, 0xbb, 0x1d, 0xda, 0x11, 0x20, + 0x34, 0x39, 0x95, 0xfe, 0xfe, 0x30, 0x61, 0x15, 0xac, 0xa1, 0x11, 0x67, 0xe7, 0x13, 0x82, 0x4d, + 0xf6, 0x79, 0xb2, 0x29, 0x23, 0x3d, 0xe3, 0x23, 0xe8, 0x44, 0x52, 0x7b, 0x2f, 0x05, 0xa5, 0x4f, + 0x4f, 0xba, 0x98, 0xe7, 0xc2, 0x4e, 0x8e, 0x40, 0xdf, 0x02, 0x52, 0x51, 0x13, 0xdc, 0x9c, 0x39, + 0x26, 0x0d, 0xb2, 0xe6, 0x1b, 0x7a, 0x68, 0x07, 0xec, 0x8c, 0x38, 0x91, 0xd0, 0xef, 0x87, 0x20, + 0xa3, 0x08, 0x63, 0xd7, 0x71, 0x87, 0xd8, 0xcb, 0x76, 0xee, 0x47, 0x2a, 0x93, 0x09, 0x5d, 0xb2, + 0xbd, 0x28, 0x3c, 0xef, 0x73, 0xc3, 0x0e, 0x11, 0xd3, 0x38, 0x46, 0x46, 0x3c, 0xcd, 0xac, 0x89, + 0x5c, 0x89, 0x4c, 0x63, 0x04, 0x54, 0x59, 0xfb, 0xdc, 0xeb, 0x75, 0xe1, 0xc4, 0x87, 0x86, 0x95, + 0x74, 0x26, 0xd6, 0x97, 0x14, 0x76, 0x49, 0x44, 0x97, 0x28, 0x34, 0x62, 0xce, 0x18, 0x76, 0x61, + 0x16, 0xfc, 0x44, 0xbf, 0x0b, 0xb2, 0xa7, 0xf6, 0x95, 0xf0, 0x99, 0x07, 0x79, 0xb7, 0x03, 0xbe, + 0xc5, 0x10, 0x4e, 0x12, 0x4c, 0xb0, 0x86, 0x4e, 0x24, 0xaa, 0xa5, 0xe6, 0x61, 0x30, 0x56, 0x80, + 0x63, 0x9c, 0x31, 0x29, 0xf8, 0x35, 0xcc, 0xbb, 0x70, 0x72, 0xe5, 0x82, 0xd0, 0xfc, 0xc2, 0xcf, + 0x80, 0x4b, 0xca, 0x61, 0xd9, 0x85, 0x2a, 0xb8, 0xe2, 0x80, 0xce, 0x94, 0x70, 0xc5, 0x2f, 0x4f, + 0x07, 0xd5, 0x85, 0xcc, 0xf5, 0xc4, 0x2f, 0xf0, 0x02, 0x34, 0x14, 0x8e, 0x1e, 0x64, 0xed, 0x20, + 0xd4, 0x57, 0x22, 0xb3, 0x70, 0x0f, 0x5a, 0x10, 0x44, 0x73, 0x4e, 0xeb, 0xcf, 0x40, 0x4f, 0xee, + 0x6b, 0x04, 0x03, 0xc3, 0x95, 0x37, 0x93, 0x37, 0x01, 0x48, 0x67, 0xe7, 0x6f, 0x26, 0x56, 0x00, + 0xf7, 0x40, 0x08, 0xbc, 0x0d, 0xb1, 0xa3, 0x0a, 0xbb, 0x20, 0xc0, 0x18, 0xd4, 0x8f, 0x6d, 0xd2, + 0x19, 0x03, 0x90, 0xe6, 0x31, 0x9a, 0x4f, 0xef, 0x2f, 0x20, 0x0c, 0x9f, 0x95, 0xee, 0x91, 0x58, + 0xfa, 0xae, 0x8f, 0xa2, 0x44, 0x13, 0x16, 0x56, 0x98, 0x4c, 0xb3, 0xd4, 0x36, 0xca, 0x6f, 0x91, + 0x6b, 0xcf, 0x80, 0x79, 0xe5, 0x00, 0xd7, 0x67, 0x60, 0xcd, 0x8b, 0x2b, 0xa2, 0x9d, 0xa7, 0x27, + 0x5d, 0x9f, 0xa1, 0x2e, 0x33, 0xd7, 0x20, 0x33, 0x01, 0xce, 0xb9, 0x14, 0x67, 0x6e, 0x56, 0x11, + 0x7b, 0x0b, 0xc7, 0xe2, 0xa2, 0x5c, 0x3d, 0xa8, 0x78, 0xfe, 0x3d, 0x2b, 0x6f, 0xc4, 0xdc, 0x12, + 0x6e, 0xa1, 0x86, 0x29, 0x6a, 0x1c, 0x63, 0xb6, 0x14, 0x80, 0xff, 0x7e, 0x05, 0xa2, 0x9a, 0x20, + 0x52, 0x79, 0xfb, 0x08, 0x7f, 0x66, 0x05, 0xe8, 0xef, 0xd6, 0xf1, 0x36, 0xdc, 0x29, 0xb9, 0x87, + 0x40, 0x80, 0xc4, 0x19, 0x47, 0xd6, 0xc6, 0x00, 0x07, 0x98, 0x38, 0x34, 0x24, 0x9c, 0x9a, 0x56, + 0xad, 0x88, 0xfa, 0x3a, 0xb3, 0x6a, 0x4d, 0xd4, 0x3f, 0xb7, 0xf6, 0xfd, 0xfe, 0x5b, 0xeb, 0x38, + 0xd1, 0xa1, 0x9e, 0x62, 0x5d, 0x4b, 0x9f, 0xdb, 0xd8, 0xd0, 0xb6, 0xa1, 0xf8, 0x2d, 0xf2, 0x0c, + 0xd4, 0x55, 0x4c, 0x04, 0xf5, 0x19, 0xc7, 0xce, 0xfa, 0x4c, 0xe5, 0xa8, 0x8a, 0xe4, 0x51, 0xc7, + 0xcc, 0x51, 0x05, 0x7b, 0xe7, 0x8c, 0xa3, 0x4a, 0x39, 0x9f, 0x98, 0x4b, 0xe9, 0x83, 0x28, 0x01, + 0x21, 0xe0, 0xa1, 0xdf, 0xac, 0x7b, 0xf4, 0xbb, 0x75, 0x8b, 0xbe, 0x58, 0xe7, 0xb1, 0x3d, 0x43, + 0x27, 0xf6, 0x8c, 0x49, 0x60, 0x8d, 0x4e, 0x97, 0x9b, 0x2d, 0x6d, 0x6d, 0xe9, 0x5d, 0xfb, 0x64, + 0xff, 0x8f, 0xd0, 0x0a, 0x98, 0x3a, 0xe9, 0x9c, 0x3b, 0xbb, 0x6f, 0x9c, 0xd9, 0xae, 0x7e, 0x5f, + 0xea, 0x82, 0x6d, 0xbf, 0xab, 0xdf, 0xc2, 0x8f, 0x23, 0x3b, 0x60, 0x3b, 0xed, 0x1c, 0x75, 0x19, + 0x20, 0x4f, 0x84, 0x4e, 0x6d, 0xfe, 0x68, 0x1d, 0x63, 0x34, 0xb3, 0xde, 0xc6, 0x90, 0x7a, 0xb4, + 0x69, 0xdb, 0x6f, 0x0b, 0x2f, 0x94, 0x2f, 0x1b, 0x0f, 0x4e, 0x62, 0x04, 0x95, 0xd0, 0xa0, 0xf3, + 0x47, 0xeb, 0x0c, 0xcd, 0xac, 0xd3, 0x38, 0x06, 0x05, 0x68, 0x10, 0xe9, 0x33, 0x31, 0x70, 0xc7, + 0xea, 0x22, 0x0f, 0xcc, 0x8e, 0x09, 0x34, 0x2b, 0x46, 0x5d, 0xbc, 0x54, 0x6c, 0x49, 0xac, 0x0d, + 0xc0, 0xdb, 0xfc, 0xaa, 0x9a, 0xff, 0x66, 0xdd, 0xa2, 0xdf, 0xad, 0x73, 0xf4, 0xc5, 0x3a, 0x41, + 0x57, 0xd6, 0x19, 0xcc, 0x9c, 0xcf, 0xe3, 0x16, 0xe6, 0xe1, 0xea, 0xe7, 0xa5, 0x73, 0xb0, 0x82, + 0x77, 0xf5, 0x93, 0xd2, 0x09, 0x18, 0xbd, 0xbb, 0xfa, 0x65, 0xe9, 0x12, 0xbc, 0x0d, 0x5c, 0xfd, + 0x14, 0xe6, 0xfa, 0x91, 0xa5, 0xb8, 0xfa, 0xdd, 0xf6, 0x91, 0x01, 0x86, 0xec, 0xae, 0x7e, 0xb8, + 0x0d, 0xf0, 0x80, 0xec, 0x23, 0x83, 0xab, 0xad, 0x3e, 0x6e, 0xda, 0xf6, 0xd5, 0xca, 0x01, 0x01, + 0x6f, 0x25, 0x7c, 0xb7, 0xfb, 0x74, 0x63, 0xd3, 0xde, 0x20, 0xa0, 0x99, 0xdb, 0xd0, 0x4d, 0x43, + 0xdc, 0xa8, 0x3d, 0x36, 0xa6, 0x73, 0x03, 0xbd, 0x61, 0x43, 0x39, 0x63, 0xed, 0xf6, 0x36, 0x6d, + 0xfb, 0xcd, 0x5f, 0x6a, 0xb7, 0x66, 0x24, 0x60, 0x85, 0xb8, 0x84, 0x1d, 0xee, 0xd8, 0x34, 0xc8, + 0x10, 0x58, 0x5d, 0x74, 0x8f, 0x6e, 0xd1, 0xb9, 0x20, 0xb2, 0x7e, 0xb3, 0xfb, 0xba, 0xf6, 0xa8, + 0xa1, 0xae, 0x30, 0xb1, 0xfe, 0x1d, 0xbe, 0x67, 0x1a, 0xba, 0x17, 0xdf, 0x5f, 0xe0, 0xfb, 0x9b, + 0x86, 0x6e, 0xc1, 0x84, 0x8c, 0x27, 0x5d, 0x41, 0x12, 0xd5, 0xd0, 0x79, 0x62, 0x7f, 0x26, 0x08, + 0x4b, 0x16, 0xa3, 0x23, 0x66, 0xe1, 0x3c, 0x5d, 0x4e, 0x87, 0xa6, 0xf1, 0x09, 0x88, 0x4c, 0x57, + 0x04, 0x53, 0x5d, 0xb6, 0xe1, 0xbb, 0xd9, 0xed, 0x5e, 0x60, 0xcc, 0xc7, 0x77, 0xbb, 0x10, 0x20, + 0x80, 0xb6, 0x4a, 0xe8, 0x3f, 0xc4, 0x52, 0x3f, 0x5a, 0xf7, 0x68, 0x66, 0xdd, 0xc6, 0x76, 0xf7, + 0xe9, 0x69, 0x1e, 0x27, 0x86, 0x0b, 0x6c, 0x5a, 0xf7, 0x20, 0x46, 0x87, 0xf9, 0xdc, 0x1a, 0x08, + 0xda, 0x1c, 0xe8, 0x30, 0xf9, 0xb7, 0x08, 0x96, 0xf2, 0xd6, 0x30, 0xd4, 0x41, 0xf1, 0xb0, 0x46, + 0xd9, 0x73, 0x1c, 0x70, 0xb4, 0x85, 0xe6, 0x8e, 0x75, 0x8e, 0x3c, 0xeb, 0x04, 0xf6, 0x62, 0xd7, + 0xa6, 0x91, 0xfe, 0x0d, 0xeb, 0x70, 0x84, 0x35, 0x81, 0x0b, 0x0c, 0x34, 0xa2, 0xfa, 0x3d, 0xd2, + 0xbe, 0xf9, 0x93, 0x9a, 0xd9, 0xd4, 0xe4, 0x01, 0xa2, 0x91, 0xce, 0xce, 0x4f, 0x17, 0x5c, 0x4e, + 0x6e, 0x3a, 0x67, 0xec, 0x1f, 0xfb, 0x74, 0xab, 0x6c, 0xd6, 0x76, 0x59, 0x91, 0x23, 0x3b, 0xc0, + 0xfa, 0x19, 0x6c, 0xc4, 0xfb, 0x7d, 0xcf, 0x0a, 0x38, 0x03, 0xd7, 0x01, 0x7c, 0xc1, 0x1a, 0xae, + 0xcc, 0x34, 0x74, 0x84, 0x8e, 0x31, 0xba, 0xe4, 0x2d, 0xc2, 0x36, 0x3d, 0x2a, 0x1d, 0xf1, 0x6d, + 0x7a, 0x58, 0x7e, 0xcb, 0xb7, 0xe9, 0x49, 0xe9, 0xb0, 0x7c, 0x6e, 0xa0, 0x04, 0xe5, 0x5d, 0x09, + 0x8c, 0xd7, 0x8b, 0xed, 0xa9, 0x7e, 0x87, 0x3e, 0x72, 0xa4, 0xb7, 0x7a, 0xa3, 0x4a, 0x72, 0x6f, + 0xa6, 0xbc, 0x0b, 0x24, 0xa6, 0xf1, 0xc6, 0xd6, 0x7b, 0x5b, 0x6f, 0x41, 0x82, 0xfa, 0x16, 0x7d, + 0xb2, 0xf5, 0xd3, 0x2d, 0xb3, 0xd6, 0x36, 0x98, 0x58, 0x0a, 0x9a, 0xbd, 0xdf, 0xda, 0xea, 0xd9, + 0xb6, 0x7d, 0x8c, 0xb7, 0xb6, 0x3e, 0xad, 0xec, 0xe1, 0xd1, 0xae, 0x32, 0x01, 0xc4, 0x63, 0xaf, + 0x6a, 0x9b, 0x29, 0x03, 0xfa, 0x09, 0xf6, 0xfa, 0xd6, 0x96, 0x0e, 0x07, 0xa1, 0xdc, 0x33, 0x0c, + 0x34, 0x50, 0x45, 0x97, 0xf3, 0x47, 0xab, 0x87, 0x66, 0xd6, 0x51, 0x9c, 0x59, 0x29, 0x88, 0x42, + 0x27, 0xd7, 0x49, 0xf2, 0xac, 0xa9, 0x76, 0x40, 0xff, 0x45, 0x40, 0x8f, 0x6d, 0xea, 0x7b, 0x03, + 0xa8, 0xa3, 0x8d, 0xc7, 0x5c, 0xa4, 0x8c, 0x44, 0x22, 0x6a, 0x54, 0x1e, 0x59, 0x81, 0xd9, 0xf2, + 0x02, 0x33, 0x85, 0xe2, 0xd7, 0xbb, 0x76, 0x1b, 0x7a, 0xae, 0xa6, 0xa6, 0xe6, 0x15, 0x95, 0x70, + 0x60, 0x81, 0x66, 0xa0, 0xdb, 0xa7, 0x27, 0xd6, 0xcc, 0x18, 0xde, 0x77, 0x99, 0x8c, 0x66, 0xfa, + 0x48, 0x44, 0xaf, 0x89, 0x9d, 0x28, 0xc2, 0x84, 0xb2, 0x95, 0xf2, 0x29, 0xf4, 0x3a, 0x16, 0xe7, + 0x86, 0xc9, 0x61, 0x60, 0xc3, 0xcc, 0x87, 0x7a, 0x57, 0x22, 0x31, 0x05, 0x75, 0xf3, 0x18, 0x36, + 0xbf, 0x59, 0x27, 0xe8, 0x77, 0xeb, 0x0c, 0x7d, 0xb1, 0x4e, 0x63, 0xbb, 0xcb, 0x50, 0xd9, 0x7d, + 0xe9, 0x54, 0xa2, 0xb2, 0x73, 0x8e, 0xca, 0x6e, 0x21, 0x05, 0xf6, 0xc8, 0x59, 0xe9, 0x3c, 0x81, + 0xf4, 0x91, 0x6d, 0xdb, 0x97, 0x5b, 0x5b, 0x87, 0xb6, 0x6d, 0xdf, 0xc5, 0x0c, 0x97, 0x66, 0x26, + 0x2c, 0xba, 0x1f, 0x70, 0xe2, 0x37, 0x96, 0xe4, 0xa6, 0x1a, 0x87, 0x71, 0xa0, 0xbb, 0x3a, 0x0f, + 0x44, 0xf4, 0x9b, 0xc4, 0x16, 0x02, 0x49, 0x20, 0x99, 0x71, 0x65, 0x18, 0xb1, 0x64, 0x40, 0x24, + 0x12, 0x8f, 0x6d, 0x82, 0x0a, 0xa6, 0x72, 0xc2, 0x86, 0x7e, 0x0f, 0x9e, 0x4b, 0x02, 0x2f, 0x03, + 0x82, 0x1e, 0x45, 0x25, 0x8e, 0x9c, 0x39, 0x9a, 0xee, 0x02, 0x76, 0xbe, 0xb4, 0xef, 0xb7, 0x6f, + 0xd9, 0xc4, 0x38, 0x96, 0x2e, 0x9f, 0x94, 0xcf, 0x60, 0x7e, 0x47, 0xdb, 0x67, 0xe8, 0xa3, 0x7d, + 0x57, 0x3e, 0x45, 0x57, 0xf6, 0x51, 0xf9, 0x0c, 0xc1, 0x1e, 0x3a, 0x2c, 0x7d, 0xe4, 0xc8, 0xf4, + 0xae, 0x74, 0x65, 0xa0, 0x4f, 0x2c, 0xe5, 0xca, 0x40, 0x9f, 0x6d, 0x57, 0xff, 0x58, 0xba, 0xcb, + 0x04, 0xc5, 0x1b, 0xe8, 0x3d, 0xf4, 0x06, 0x7d, 0x46, 0x9f, 0x8c, 0x18, 0xa8, 0x2a, 0x15, 0xf0, + 0x8e, 0x75, 0x8f, 0x3c, 0x40, 0x29, 0x6c, 0xe4, 0xe7, 0xe8, 0x77, 0xeb, 0x04, 0x7d, 0xb1, 0xce, + 0xd0, 0x15, 0x40, 0x5d, 0x2e, 0xc4, 0x11, 0xfa, 0xdd, 0xba, 0x44, 0x5f, 0xac, 0x43, 0x74, 0x65, + 0xdd, 0xc1, 0x62, 0x7c, 0x64, 0xf7, 0xca, 0x11, 0xbf, 0x2e, 0x4e, 0xe0, 0x3a, 0xe9, 0xb1, 0xeb, + 0xe4, 0xb6, 0x74, 0xc7, 0xc7, 0x74, 0x56, 0x3a, 0xe4, 0x63, 0xd2, 0xcf, 0xb7, 0x4f, 0x8c, 0x92, + 0x7e, 0xb4, 0x7d, 0x69, 0x94, 0x3f, 0x96, 0xd9, 0xf8, 0xde, 0x94, 0x7b, 0xe8, 0x9b, 0xfd, 0x66, + 0xbb, 0x87, 0xbe, 0xd8, 0xae, 0x7e, 0x55, 0xbe, 0x87, 0x89, 0x50, 0x6c, 0xbb, 0xfa, 0xa7, 0xd2, + 0x67, 0x03, 0x39, 0xf0, 0xeb, 0x5b, 0xe9, 0x8b, 0x81, 0x7e, 0xe1, 0x69, 0x5f, 0x0c, 0x74, 0x02, + 0xbf, 0x3e, 0x97, 0xbe, 0xe5, 0x66, 0x45, 0x31, 0x72, 0x30, 0x3a, 0xc1, 0xe8, 0x17, 0x6c, 0xc4, + 0xd1, 0xf4, 0x96, 0x42, 0x08, 0x42, 0x98, 0x9e, 0xba, 0xdc, 0x6c, 0xc6, 0x09, 0x53, 0x61, 0xc4, + 0xc9, 0x46, 0xe5, 0x48, 0x39, 0x95, 0x48, 0x80, 0xe2, 0x56, 0xef, 0xae, 0xe5, 0x10, 0x15, 0x9d, + 0x9c, 0x09, 0xca, 0xef, 0xc8, 0xdd, 0xf8, 0x89, 0x53, 0x50, 0x95, 0x40, 0xe0, 0xe9, 0x89, 0x75, + 0x8f, 0xfa, 0x00, 0xd4, 0xc7, 0x8a, 0xe0, 0x15, 0xf8, 0x69, 0x41, 0xe7, 0xf6, 0xde, 0xaf, 0x58, + 0x1f, 0xa0, 0xf3, 0x94, 0x7c, 0x65, 0xdf, 0xd7, 0xf7, 0xe8, 0xf6, 0xc6, 0xb8, 0xae, 0xde, 0x24, + 0xa3, 0x13, 0xc4, 0x3c, 0x1c, 0x7e, 0xb1, 0x51, 0xb3, 0x83, 0x7d, 0xd9, 0x40, 0xab, 0x05, 0x03, + 0x15, 0xfd, 0x77, 0x19, 0x4e, 0xdb, 0xe7, 0x9d, 0x58, 0x22, 0x66, 0x58, 0x55, 0x37, 0x9e, 0x9e, + 0x20, 0xe7, 0x2d, 0x63, 0x3c, 0xac, 0xc7, 0x8a, 0x60, 0x54, 0xc4, 0x3c, 0x6e, 0xc5, 0x3c, 0x6e, + 0x19, 0xda, 0xf1, 0xa3, 0x4b, 0xb0, 0x0a, 0x3c, 0x23, 0x2c, 0xca, 0x62, 0x06, 0xf8, 0xb9, 0xe9, + 0xf8, 0x06, 0x6f, 0x3c, 0xf6, 0xa3, 0xab, 0x90, 0x3d, 0x5a, 0xf1, 0x86, 0x60, 0xe5, 0xec, 0x65, + 0xfb, 0x21, 0x95, 0x40, 0x96, 0x4f, 0x90, 0x54, 0xba, 0xb8, 0x23, 0x89, 0x82, 0x62, 0xf6, 0x40, + 0x48, 0x37, 0xec, 0x3b, 0xec, 0xd6, 0x4f, 0x39, 0x93, 0x74, 0xfc, 0xc5, 0x63, 0x89, 0x85, 0xba, + 0x59, 0x1c, 0xe0, 0x47, 0xab, 0x8b, 0x66, 0xd2, 0x8b, 0x51, 0x41, 0x8b, 0x08, 0x6e, 0x47, 0x59, + 0xf4, 0x3e, 0x01, 0xdc, 0xed, 0xf5, 0x6d, 0x22, 0x31, 0xbd, 0x79, 0xb2, 0xbb, 0x5b, 0x6f, 0xf7, + 0xcd, 0x5a, 0xdb, 0xaa, 0xa2, 0xdb, 0x58, 0x84, 0x5d, 0x95, 0x03, 0x39, 0x14, 0x1e, 0x5e, 0x49, + 0x77, 0xd0, 0xb3, 0x30, 0xc0, 0x91, 0x85, 0xbe, 0xfe, 0xc4, 0x69, 0xdc, 0x57, 0xf3, 0x74, 0x09, + 0xf6, 0x35, 0x46, 0xe6, 0x59, 0xa2, 0x32, 0x6b, 0x33, 0xde, 0xfb, 0xca, 0xb0, 0x38, 0xce, 0xe3, + 0xf9, 0xdf, 0x78, 0x72, 0x1e, 0xbb, 0xff, 0xce, 0x93, 0xbf, 0xe5, 0x92, 0xbf, 0xf0, 0x64, 0x9a, + 0x4b, 0xbe, 0x92, 0x97, 0x4f, 0x10, 0x92, 0xb1, 0x33, 0xf2, 0xbf, 0xe1, 0x2f, 0x0a, 0xc4, 0xd9, + 0x92, 0x77, 0x93, 0x0b, 0x6a, 0x1c, 0x8d, 0x61, 0x7f, 0x26, 0xd9, 0x63, 0xaa, 0x0f, 0x50, 0xc8, + 0xbd, 0xde, 0x7a, 0x11, 0xa6, 0x9f, 0x13, 0xc9, 0x10, 0xb4, 0x91, 0x63, 0xc3, 0xf5, 0x2e, 0x00, + 0xe1, 0xc2, 0x79, 0x90, 0x2b, 0x90, 0xbd, 0x92, 0x44, 0x6a, 0x1c, 0x0f, 0xb8, 0x70, 0x8c, 0x9f, + 0xf7, 0x84, 0x8f, 0x61, 0x74, 0x0d, 0x7c, 0x95, 0x38, 0x57, 0x83, 0xf8, 0xf6, 0x15, 0xc5, 0x8e, + 0x31, 0x7a, 0x8b, 0xde, 0xa2, 0x63, 0x0c, 0xe9, 0x6f, 0x26, 0x76, 0x00, 0xff, 0x04, 0x76, 0xa8, + 0x68, 0xee, 0x27, 0x6c, 0xac, 0xdc, 0xae, 0x27, 0x51, 0x61, 0xb3, 0xae, 0xd4, 0x31, 0xb6, 0x0d, + 0x34, 0x50, 0xfc, 0xa2, 0x48, 0xd6, 0x47, 0x43, 0x91, 0xf8, 0xa6, 0x81, 0x0c, 0x17, 0x22, 0xa7, + 0x6a, 0xf0, 0xd0, 0x9d, 0xb6, 0x91, 0x86, 0x3a, 0x65, 0x9e, 0x12, 0xf0, 0x62, 0x81, 0x78, 0x96, + 0xe2, 0x5f, 0x0d, 0x60, 0x31, 0x23, 0xc6, 0x62, 0x3a, 0xde, 0x1f, 0xd3, 0x88, 0x5e, 0xb2, 0xe3, + 0xcb, 0x0d, 0x00, 0x14, 0x0b, 0x43, 0x25, 0x26, 0xa5, 0x9a, 0xec, 0x85, 0xf0, 0x14, 0x8b, 0x9a, + 0x32, 0x21, 0x18, 0x3a, 0x55, 0x93, 0xc6, 0xce, 0xe4, 0x2a, 0xec, 0xc2, 0xd9, 0x5f, 0x60, 0x5f, + 0xe7, 0xb2, 0x78, 0x00, 0xfc, 0xef, 0x1c, 0x60, 0x00, 0x72, 0xd1, 0x89, 0x45, 0x80, 0x29, 0x86, + 0x28, 0x72, 0xc8, 0xb3, 0x23, 0x35, 0x22, 0xe6, 0xd3, 0xd3, 0x67, 0x8c, 0x5c, 0x1b, 0x50, 0x6b, + 0x6e, 0xb8, 0x4f, 0x4f, 0xfa, 0xa5, 0xbd, 0x77, 0x09, 0x7c, 0x6e, 0x54, 0xe1, 0x03, 0x03, 0x36, + 0xf7, 0x12, 0x1d, 0xa2, 0x3b, 0xa1, 0x9e, 0x1a, 0x51, 0xfd, 0x0e, 0x69, 0x93, 0x61, 0x7f, 0xe4, + 0x0c, 0x34, 0x03, 0x1d, 0x8a, 0x33, 0xf4, 0xf4, 0x74, 0x57, 0x60, 0x7f, 0x1a, 0x06, 0xf0, 0x84, + 0x47, 0xf4, 0x7a, 0x42, 0x70, 0x99, 0x3f, 0x18, 0xc8, 0x5e, 0xc6, 0xa2, 0x1b, 0xaa, 0x35, 0xa4, + 0x58, 0xbe, 0xcb, 0x38, 0xc3, 0xa8, 0x5e, 0xa6, 0x78, 0x40, 0xf2, 0xdf, 0x01, 0xd6, 0x2f, 0x33, + 0xbe, 0x6a, 0x50, 0x88, 0x53, 0x9b, 0xb7, 0x95, 0x08, 0xbb, 0x04, 0xd3, 0x5f, 0xf0, 0xac, 0x73, + 0x69, 0x03, 0x5d, 0xc5, 0x9f, 0x2f, 0x85, 0xc8, 0xf9, 0x1a, 0xba, 0x44, 0x87, 0x9c, 0x42, 0xbc, + 0x83, 0x2c, 0x18, 0x08, 0x7b, 0x8b, 0x5b, 0x29, 0x01, 0x2d, 0xa3, 0x1a, 0x5c, 0x7a, 0x70, 0x35, + 0xde, 0x25, 0x51, 0xda, 0x0e, 0x19, 0x4b, 0x25, 0xbf, 0x0f, 0x79, 0x91, 0x9e, 0xdd, 0xd7, 0x3f, + 0xa6, 0x31, 0xac, 0xb0, 0xe3, 0x59, 0x1f, 0x61, 0xd1, 0xfa, 0xfe, 0xa3, 0x75, 0x85, 0x04, 0xf2, + 0xee, 0xa9, 0x71, 0xba, 0xc4, 0x40, 0x79, 0xd9, 0x43, 0x59, 0xf6, 0x4e, 0x96, 0xfd, 0x18, 0xdb, + 0x30, 0x17, 0x74, 0x65, 0x87, 0x29, 0x1d, 0xf6, 0x11, 0x3a, 0xba, 0x4a, 0xcf, 0x52, 0xa6, 0xbf, + 0x82, 0x36, 0x10, 0x27, 0x5f, 0xaf, 0xf8, 0xbf, 0x7c, 0x9b, 0xa9, 0x83, 0x18, 0x2b, 0x20, 0x85, + 0x01, 0x55, 0xd2, 0x72, 0x8a, 0x21, 0xa0, 0x7e, 0xa9, 0x3a, 0xd3, 0x85, 0x7d, 0xdd, 0x00, 0x57, + 0x99, 0x43, 0x0e, 0xe7, 0x3b, 0x9b, 0x85, 0xcc, 0x3d, 0x34, 0x52, 0xd6, 0x05, 0xeb, 0x40, 0xb9, + 0xff, 0x02, 0x62, 0x03, 0xb6, 0xd6, 0x1a, 0xba, 0x34, 0xd0, 0xe6, 0x66, 0x90, 0xd1, 0xdd, 0x3f, + 0xf2, 0x1d, 0xc4, 0x0e, 0x1e, 0x5b, 0x1d, 0xf1, 0x1e, 0x15, 0x2b, 0x0c, 0x6a, 0xe7, 0x4b, 0x3b, + 0x80, 0xa5, 0x95, 0xdb, 0x19, 0x26, 0xf6, 0x51, 0x4e, 0x2c, 0x37, 0x21, 0x7b, 0xa4, 0x1f, 0x02, + 0x8d, 0x32, 0xd0, 0xef, 0x2a, 0xa2, 0x4f, 0xf4, 0x11, 0xda, 0xf9, 0xa4, 0x02, 0xef, 0x8d, 0x91, + 0x02, 0x0e, 0x7d, 0xce, 0x14, 0xfe, 0x84, 0x7a, 0x50, 0xfc, 0x9b, 0x9d, 0xec, 0xab, 0x37, 0xdb, + 0x9f, 0x4b, 0x57, 0x9c, 0xf7, 0xf0, 0x93, 0xfb, 0xf9, 0x5b, 0xc1, 0xfd, 0x0c, 0x6a, 0x88, 0x8d, + 0xbe, 0xe3, 0x8f, 0xb0, 0x97, 0x32, 0x22, 0xd2, 0x66, 0xee, 0x8b, 0xfd, 0xb3, 0xfe, 0x09, 0xf9, + 0x49, 0xbf, 0xdf, 0x52, 0x1a, 0xe1, 0x1b, 0xd6, 0xbf, 0xa0, 0xdb, 0x4a, 0xf2, 0x52, 0x26, 0xd2, + 0xa4, 0x4d, 0x68, 0xcc, 0xf5, 0x7f, 0x73, 0xce, 0x8e, 0x41, 0x88, 0xfe, 0xf4, 0x0c, 0x74, 0x39, + 0xd4, 0xd0, 0x47, 0x7b, 0xc6, 0x77, 0x8f, 0x98, 0x81, 0x75, 0x85, 0x44, 0xf1, 0x5e, 0x6c, 0x7f, + 0x44, 0x6f, 0x6c, 0xa5, 0x65, 0xbe, 0xf9, 0x93, 0x4f, 0xd8, 0xfa, 0x6f, 0x80, 0xd2, 0x56, 0x61, + 0x7e, 0x08, 0x14, 0x29, 0x1c, 0x11, 0xf6, 0x56, 0xdf, 0x2f, 0xb0, 0xfd, 0xef, 0xd0, 0x6d, 0x25, + 0xf9, 0x34, 0x50, 0x4f, 0xb1, 0x42, 0x1d, 0x51, 0xbd, 0x97, 0xb2, 0x8b, 0x6c, 0xb5, 0x0e, 0xed, + 0x40, 0x3f, 0x14, 0xb2, 0x97, 0x4f, 0xf6, 0x9b, 0xd7, 0x35, 0xf4, 0xd9, 0xbe, 0x4c, 0xa3, 0x51, + 0x56, 0xd1, 0x27, 0x00, 0x2f, 0x9c, 0xd6, 0x34, 0xf1, 0x13, 0x7a, 0x63, 0x18, 0xe8, 0x0b, 0x62, + 0x24, 0x5f, 0x07, 0x04, 0x66, 0x5f, 0x6c, 0xaa, 0xb0, 0x42, 0x77, 0xa8, 0xc7, 0x88, 0x48, 0x35, + 0xed, 0x33, 0xa4, 0x39, 0x58, 0x59, 0x57, 0x71, 0xf5, 0x7f, 0x33, 0x32, 0x22, 0xb4, 0x4d, 0x13, + 0x0c, 0xb3, 0x36, 0x7b, 0x5b, 0x5b, 0x5f, 0x2a, 0x59, 0x5a, 0x26, 0xeb, 0x35, 0xf9, 0x0b, 0xb6, + 0x07, 0xfa, 0x15, 0xa2, 0x58, 0xd9, 0x1b, 0x5f, 0x94, 0xdf, 0xe9, 0xbe, 0xa6, 0x98, 0x91, 0x9d, + 0x5f, 0xf2, 0x1d, 0xff, 0x82, 0x0d, 0xa3, 0x92, 0x10, 0xaa, 0x0e, 0x36, 0x2a, 0x39, 0xda, 0x45, + 0x90, 0x3b, 0xd0, 0xdb, 0xbd, 0x4d, 0x04, 0x4f, 0x7e, 0x6b, 0xcf, 0x13, 0xec, 0x64, 0xdd, 0xa3, + 0x04, 0xd0, 0xd6, 0x3d, 0x4a, 0x56, 0xca, 0xaa, 0x95, 0xee, 0x51, 0x84, 0x31, 0x88, 0x91, 0xd2, + 0x2d, 0x70, 0xae, 0x5f, 0xda, 0x9e, 0x0e, 0xc8, 0x0d, 0x7b, 0x46, 0x72, 0x7a, 0xbf, 0x61, 0xfd, + 0x12, 0xf1, 0x44, 0xa4, 0xc1, 0x5f, 0x55, 0x37, 0x7f, 0x92, 0x22, 0xc5, 0x23, 0x81, 0xf4, 0x2f, + 0x65, 0xe7, 0x7a, 0x72, 0x59, 0x2a, 0x03, 0x3a, 0x54, 0x06, 0x34, 0xd6, 0x0f, 0x0d, 0x05, 0x65, + 0x9c, 0x29, 0x28, 0x63, 0x0a, 0x28, 0x72, 0x6b, 0xeb, 0x52, 0x86, 0x7d, 0xb4, 0x6d, 0x9f, 0x4f, + 0x4f, 0x31, 0x49, 0x81, 0x0d, 0x6b, 0x28, 0xd2, 0xd0, 0xcd, 0x4d, 0x75, 0x39, 0x21, 0x33, 0xbf, + 0x6e, 0x31, 0x97, 0x2d, 0xc0, 0x7b, 0x15, 0x89, 0xac, 0x30, 0x19, 0xcd, 0x08, 0xe5, 0xc6, 0x6f, + 0x9d, 0x4b, 0x23, 0xcd, 0x34, 0xe9, 0x4c, 0x26, 0xa5, 0xf5, 0x4e, 0x11, 0x0d, 0x4f, 0xc2, 0x80, + 0x0e, 0xc2, 0x31, 0x26, 0x33, 0x89, 0x7b, 0x67, 0xd6, 0x61, 0x9c, 0xd9, 0x5e, 0x4c, 0x88, 0xa6, + 0xec, 0x7b, 0xe0, 0xd0, 0x6c, 0xdb, 0xae, 0xb3, 0x00, 0xf1, 0x9b, 0x1f, 0xb7, 0xb6, 0xee, 0x36, + 0x6d, 0xbb, 0xb9, 0xb3, 0x88, 0x06, 0xd8, 0x2b, 0xab, 0xaa, 0x53, 0x0d, 0x04, 0x8c, 0xdc, 0x65, + 0x32, 0x83, 0x46, 0xa3, 0x2d, 0xd0, 0xc1, 0x95, 0xfd, 0x71, 0x9f, 0x30, 0xa1, 0xef, 0xdb, 0xed, + 0x43, 0xf4, 0xb6, 0x7c, 0x68, 0x58, 0xfc, 0xf3, 0xb0, 0xfc, 0x16, 0x1d, 0x6e, 0xbf, 0x4d, 0xf5, + 0x03, 0xc9, 0x0e, 0xbc, 0x32, 0xe2, 0xcc, 0xc8, 0xf9, 0x24, 0x8b, 0x2f, 0x39, 0xb6, 0x07, 0x92, + 0x5b, 0x2d, 0x73, 0xd4, 0xd8, 0xad, 0x25, 0x5b, 0x77, 0xf5, 0x3b, 0x23, 0x9b, 0x15, 0x0b, 0xa8, + 0x9e, 0xf3, 0xfb, 0x8f, 0x83, 0x55, 0x21, 0x9a, 0x2e, 0xed, 0x36, 0x3a, 0xb4, 0x29, 0x23, 0xa6, + 0x52, 0x12, 0x58, 0x25, 0xab, 0x2e, 0xc1, 0xa5, 0x33, 0x8e, 0x8b, 0x6d, 0x00, 0xe7, 0x77, 0x78, + 0x36, 0xc0, 0x81, 0x75, 0x82, 0x40, 0x83, 0x91, 0xee, 0x2c, 0xb6, 0xd5, 0xad, 0x47, 0x74, 0xcf, + 0xde, 0xe1, 0xb5, 0xba, 0x68, 0x4a, 0xfd, 0x51, 0x64, 0x1d, 0x21, 0x46, 0x37, 0x5b, 0x54, 0x84, + 0x27, 0x8d, 0xac, 0x5b, 0x55, 0x93, 0x4f, 0x89, 0xe2, 0x01, 0xe2, 0x58, 0xb4, 0xe2, 0x20, 0xcf, + 0xa2, 0x15, 0x0f, 0x4d, 0x2c, 0xb0, 0x30, 0xe3, 0x62, 0x28, 0x14, 0x58, 0xb4, 0x12, 0xa0, 0xa1, + 0x45, 0x2b, 0x43, 0xf4, 0xf6, 0xd1, 0xa2, 0x40, 0x66, 0xbe, 0x9d, 0xc1, 0xbf, 0x33, 0x70, 0xfe, + 0x60, 0xb6, 0x68, 0x01, 0xb8, 0x2e, 0x81, 0x22, 0x9c, 0x56, 0x52, 0x8b, 0x6a, 0x26, 0x2a, 0x0c, + 0x6d, 0x50, 0x21, 0x44, 0x40, 0x2d, 0x05, 0x48, 0x2a, 0x09, 0xa8, 0x14, 0xf3, 0xc7, 0x88, 0xd8, + 0x73, 0x95, 0x74, 0xa3, 0x2a, 0x29, 0x85, 0x16, 0x29, 0x3e, 0xba, 0x48, 0x56, 0x49, 0x1a, 0x8f, + 0x0a, 0x9a, 0x2a, 0xa1, 0xf0, 0x68, 0x45, 0xfc, 0x52, 0x09, 0x3c, 0x5a, 0x49, 0x3f, 0x24, 0x90, + 0x13, 0xa5, 0x37, 0xe3, 0xff, 0xce, 0x26, 0x34, 0xb2, 0x42, 0x24, 0x9a, 0x60, 0xf5, 0xb1, 0xe7, + 0x45, 0x0e, 0x4b, 0x27, 0xca, 0x01, 0x8e, 0x48, 0xd6, 0x8a, 0x8f, 0xc1, 0x3a, 0xb7, 0x6e, 0x8a, + 0x76, 0x1d, 0xcd, 0x33, 0x02, 0x7b, 0x2b, 0x42, 0xbc, 0x5b, 0x8a, 0x54, 0x2f, 0x02, 0x50, 0xc8, + 0x02, 0xe1, 0x9d, 0x31, 0x46, 0xe7, 0xa9, 0x0c, 0x1f, 0x28, 0xcb, 0x37, 0xa0, 0x62, 0xf9, 0x0a, + 0xc6, 0x1f, 0xf1, 0xf1, 0x07, 0xca, 0xd0, 0x21, 0xe0, 0x34, 0x61, 0x6f, 0x60, 0xd8, 0x3f, 0x87, + 0x5c, 0x7d, 0xe5, 0xdb, 0x98, 0xe8, 0x84, 0x29, 0xd7, 0x12, 0xaf, 0x56, 0x98, 0x94, 0xcf, 0xe3, + 0x7a, 0x33, 0x56, 0x61, 0x14, 0xa1, 0x3f, 0x42, 0x34, 0xa3, 0xe8, 0x53, 0x90, 0xd5, 0xf9, 0x7b, + 0x91, 0x54, 0xfc, 0x1f, 0x63, 0x45, 0xf1, 0xff, 0x56, 0xb1, 0xf5, 0x1c, 0x45, 0x8a, 0xad, 0xe7, + 0x1f, 0x61, 0x6a, 0xb7, 0xd9, 0x99, 0x15, 0xea, 0xfc, 0x05, 0x23, 0x84, 0x27, 0x36, 0x5e, 0x14, + 0xc9, 0x62, 0x63, 0x1e, 0x01, 0xd7, 0x93, 0x93, 0x00, 0xca, 0xe4, 0xef, 0x17, 0xef, 0x2d, 0xe1, + 0x94, 0x8b, 0xc5, 0x75, 0x62, 0xa0, 0x95, 0x7c, 0x46, 0xca, 0x8c, 0xe3, 0x9c, 0x60, 0x6d, 0x52, + 0x51, 0xb2, 0xfe, 0x0a, 0x17, 0xbc, 0x91, 0x61, 0x75, 0x8b, 0x85, 0x04, 0x9b, 0xd5, 0x62, 0x81, + 0xc3, 0xa6, 0xc9, 0x84, 0x5b, 0xb9, 0x31, 0xf1, 0xc1, 0x5f, 0x3a, 0xa0, 0xc5, 0x13, 0xa2, 0x3c, + 0x3f, 0xf0, 0x45, 0x5c, 0x6a, 0x98, 0x1c, 0x54, 0xa9, 0xe0, 0x89, 0xa1, 0x08, 0x90, 0x5e, 0xd8, + 0x44, 0x5a, 0x8f, 0xb7, 0x93, 0x10, 0x91, 0xb9, 0x76, 0x32, 0x95, 0x94, 0x42, 0x46, 0x5e, 0xe8, + 0xf3, 0x9c, 0x7a, 0x49, 0x51, 0x45, 0x0a, 0xb9, 0xb4, 0x92, 0x2c, 0xb1, 0x28, 0xec, 0x5c, 0x2c, + 0x9b, 0x0a, 0xca, 0x14, 0x8c, 0xad, 0xc6, 0x6f, 0x58, 0xac, 0x92, 0x2b, 0xf8, 0x3c, 0xc6, 0x5e, + 0xe5, 0xd6, 0x2e, 0x70, 0x62, 0x11, 0xf9, 0x33, 0xb3, 0x76, 0xfd, 0x99, 0x83, 0xfa, 0x27, 0x66, + 0x8c, 0x6b, 0x9b, 0x3f, 0xfd, 0xd4, 0x2e, 0xad, 0x30, 0xc9, 0x7d, 0x5f, 0x3b, 0xbb, 0x3c, 0xdf, + 0x60, 0xaf, 0x28, 0x5a, 0xa9, 0xe3, 0x50, 0x54, 0xf4, 0x00, 0x02, 0x8e, 0x0d, 0xf9, 0x0c, 0x8f, + 0x12, 0x00, 0x03, 0x97, 0xcd, 0x4e, 0x00, 0x26, 0x77, 0x41, 0xb9, 0x6c, 0x44, 0xd7, 0xc1, 0x8d, + 0x4d, 0xb7, 0x6a, 0xcd, 0x26, 0x82, 0x08, 0xb5, 0x76, 0x7b, 0xc9, 0xf3, 0x22, 0xea, 0x13, 0x40, + 0x61, 0x06, 0x4f, 0xe6, 0x9f, 0xa4, 0x48, 0xcc, 0xf4, 0x57, 0x85, 0xdc, 0x60, 0xbd, 0x2e, 0x04, + 0xdd, 0x50, 0x54, 0xda, 0x78, 0xcd, 0x1b, 0x55, 0x8b, 0xf0, 0xe1, 0xfe, 0x41, 0xaa, 0xda, 0x34, + 0x75, 0xdb, 0x96, 0xa6, 0x9b, 0xfc, 0x21, 0xaa, 0xe4, 0x55, 0x89, 0x95, 0x61, 0x6c, 0x8e, 0x2e, + 0xaf, 0x12, 0x9b, 0x2c, 0xe5, 0x71, 0x29, 0xf0, 0x0a, 0x16, 0x55, 0x3a, 0x4b, 0xdf, 0x51, 0xd9, + 0x67, 0x6f, 0xa8, 0x58, 0x8a, 0x43, 0xe4, 0x83, 0x62, 0x04, 0xfa, 0xbb, 0x58, 0x74, 0xf6, 0xbc, + 0xc0, 0xef, 0xdc, 0x52, 0x4f, 0x3a, 0x6e, 0xd4, 0x9a, 0x4d, 0xf6, 0x86, 0x59, 0xa0, 0xff, 0xac, + 0x4f, 0xb0, 0xae, 0xbd, 0xab, 0x75, 0xcb, 0x67, 0x9f, 0x8e, 0x2f, 0x2e, 0xdf, 0x7f, 0x39, 0x2e, + 0x1f, 0x5d, 0x5e, 0x95, 0x35, 0xf0, 0xbc, 0x14, 0xcc, 0x5f, 0x1a, 0x4a, 0x3d, 0x4c, 0xa3, 0xe3, + 0x93, 0xd8, 0x0e, 0x90, 0x9f, 0xf1, 0x6f, 0xe3, 0x3e, 0x4f, 0xd1, 0x5e, 0xab, 0xd9, 0xac, 0x37, + 0x9f, 0x9e, 0x7c, 0xe8, 0xa5, 0x30, 0xba, 0x86, 0x13, 0x78, 0x3d, 0xc1, 0xf9, 0xf4, 0x1e, 0xc7, + 0x0a, 0xdb, 0x36, 0xc2, 0xc1, 0x7b, 0x76, 0x81, 0x49, 0xfe, 0xcd, 0xb3, 0x7f, 0xd6, 0x31, 0xba, + 0xc0, 0x89, 0x1f, 0x24, 0x32, 0x99, 0x1d, 0xe9, 0x05, 0xc8, 0x01, 0x08, 0x88, 0x44, 0x2e, 0x40, + 0xcc, 0x53, 0xcb, 0xda, 0x8a, 0x32, 0xcb, 0x50, 0x98, 0x9a, 0x8b, 0x28, 0x9a, 0x22, 0x56, 0xd8, + 0x34, 0xc0, 0x43, 0xb1, 0xd3, 0xbf, 0xae, 0xde, 0xb0, 0xac, 0x21, 0x24, 0x9b, 0x49, 0x72, 0x62, + 0x27, 0x6a, 0x76, 0xc6, 0x3f, 0xd9, 0x7e, 0x67, 0x2c, 0xed, 0x40, 0x07, 0xf6, 0x75, 0x48, 0x74, + 0x88, 0x36, 0x3f, 0x2e, 0x9b, 0x37, 0x06, 0xd4, 0x1a, 0x6f, 0xf3, 0x7a, 0x37, 0x9d, 0xfe, 0xf5, + 0x98, 0xb7, 0x56, 0xa9, 0x54, 0x06, 0xa9, 0xa1, 0x10, 0xfb, 0xee, 0x1b, 0x4a, 0x58, 0x79, 0xc5, + 0x68, 0x9d, 0x28, 0x01, 0x9b, 0x93, 0xf7, 0xa5, 0x96, 0x2f, 0x93, 0x34, 0x5f, 0x49, 0x01, 0x5d, + 0x2b, 0x05, 0xe0, 0x4a, 0x08, 0x2c, 0x98, 0xe0, 0x95, 0xe7, 0x9e, 0x58, 0x92, 0xe4, 0xdd, 0x99, + 0x25, 0x6b, 0x9a, 0xe4, 0xe3, 0xf4, 0x4d, 0x9a, 0x58, 0x5d, 0xb3, 0x4c, 0xcf, 0x6b, 0x97, 0x2e, + 0xec, 0x2f, 0x59, 0x3a, 0x69, 0x2b, 0x94, 0x1b, 0x60, 0x94, 0x0e, 0x90, 0x26, 0xbf, 0xc4, 0x02, + 0xaa, 0x43, 0x4b, 0x73, 0xd4, 0x65, 0x4f, 0x47, 0x9c, 0x4a, 0xa4, 0x92, 0x10, 0x0e, 0x5c, 0xda, + 0x37, 0x49, 0x9d, 0x8f, 0xc7, 0xa9, 0xe3, 0xf1, 0x5d, 0xfa, 0x33, 0x27, 0xc2, 0x93, 0xc2, 0x3a, + 0x2b, 0x40, 0xcc, 0x38, 0xdf, 0x22, 0xbc, 0x84, 0x8f, 0xf8, 0x54, 0x21, 0x06, 0xe5, 0xe5, 0x15, + 0x44, 0x62, 0x8b, 0x18, 0xff, 0x71, 0x11, 0xe8, 0x11, 0x23, 0xed, 0xd6, 0xc7, 0xcb, 0x61, 0x12, + 0x4a, 0xa3, 0xf3, 0x7b, 0x82, 0x79, 0x85, 0x65, 0x73, 0x50, 0xe4, 0x27, 0xda, 0x57, 0x96, 0x57, + 0x9f, 0x6e, 0x87, 0xc6, 0xeb, 0x36, 0x6c, 0x61, 0x5c, 0x22, 0xa5, 0x3e, 0x1a, 0xc9, 0xf7, 0x3a, + 0xb4, 0xc7, 0xb1, 0xa7, 0x19, 0x23, 0x9b, 0x1d, 0x72, 0x17, 0x0d, 0xc1, 0x90, 0x4d, 0x06, 0x5d, + 0xe2, 0xf9, 0x61, 0x1f, 0xf2, 0xd9, 0x16, 0x83, 0xfc, 0x70, 0xa1, 0x44, 0x0f, 0x1e, 0xd5, 0x23, + 0x81, 0x33, 0xea, 0xc1, 0x0b, 0x95, 0x50, 0x98, 0x16, 0x07, 0x65, 0xfa, 0x57, 0x0e, 0x80, 0x04, + 0x35, 0xb1, 0xbe, 0x01, 0x2b, 0xb1, 0x4e, 0xfe, 0x95, 0xcf, 0x66, 0xac, 0x3a, 0x2a, 0xa7, 0x87, + 0x68, 0x60, 0x57, 0x3b, 0x83, 0x9f, 0x70, 0x67, 0x20, 0xcf, 0xd0, 0xa3, 0x52, 0x8e, 0xa4, 0xe5, + 0x66, 0x76, 0xb5, 0x33, 0xfb, 0x89, 0x74, 0x66, 0xb2, 0x5c, 0xd7, 0xee, 0x97, 0xf4, 0xd9, 0xf6, + 0xa0, 0x44, 0x0c, 0x74, 0x0f, 0xee, 0xa0, 0x92, 0x45, 0xea, 0xa2, 0xee, 0x76, 0xdf, 0xe8, 0x3c, + 0x5e, 0xcf, 0x6e, 0xec, 0x57, 0x7a, 0x40, 0xf4, 0x7b, 0x03, 0x1e, 0x91, 0x19, 0x5f, 0x0f, 0x6e, + 0xec, 0xc7, 0xd4, 0xd5, 0x3d, 0x8d, 0x56, 0xa1, 0xda, 0x3f, 0x3f, 0x4b, 0xa4, 0xac, 0xa5, 0xe4, + 0xbd, 0x6e, 0x2c, 0xbe, 0x38, 0xa4, 0xc6, 0xfc, 0x21, 0xe9, 0xf5, 0xab, 0x2a, 0x9b, 0x31, 0xbc, + 0x14, 0xa6, 0xdc, 0x5e, 0x50, 0x8e, 0x5b, 0xfa, 0x91, 0xbc, 0x40, 0xa2, 0x93, 0x08, 0x51, 0x85, + 0xda, 0x54, 0x38, 0x39, 0x4b, 0xe7, 0x6e, 0xdd, 0x5f, 0xa0, 0x0f, 0x81, 0xb6, 0x16, 0x5c, 0x87, + 0x87, 0xfb, 0xce, 0x74, 0x94, 0x10, 0xeb, 0x72, 0xd4, 0x4a, 0x20, 0xa2, 0x05, 0x26, 0x22, 0x82, + 0x80, 0x1a, 0xae, 0xdd, 0x07, 0x93, 0xb5, 0x1a, 0x58, 0x0e, 0x4d, 0xed, 0x40, 0x77, 0xaf, 0xab, + 0x37, 0x0c, 0x6f, 0xea, 0xee, 0xb5, 0x79, 0x93, 0x1e, 0x5a, 0x7d, 0xca, 0xad, 0x06, 0x0d, 0x23, + 0x46, 0xfc, 0x71, 0xaf, 0x82, 0x2e, 0x22, 0xf1, 0xee, 0xd7, 0xd1, 0xe5, 0xd5, 0xfe, 0x1c, 0xce, + 0x88, 0x92, 0x10, 0x5b, 0xf3, 0x18, 0xb9, 0x45, 0xa3, 0x00, 0xdb, 0x4b, 0x34, 0xe5, 0xe3, 0x30, + 0x11, 0xb7, 0xf0, 0xd7, 0xa7, 0x30, 0x8e, 0xb4, 0xf7, 0xbe, 0x11, 0x2b, 0x8c, 0x17, 0x40, 0x71, + 0xd1, 0xa7, 0x83, 0xac, 0x3a, 0x7f, 0x89, 0x73, 0x07, 0x47, 0x05, 0x91, 0xf2, 0x9c, 0x88, 0xcf, + 0xbc, 0xed, 0x8c, 0x74, 0x53, 0xf8, 0xab, 0x5c, 0x5a, 0x57, 0xb5, 0x98, 0x0c, 0x37, 0x60, 0xcb, + 0x2e, 0x56, 0x82, 0xb3, 0x97, 0x2a, 0x9c, 0x28, 0xb0, 0x60, 0x9c, 0x21, 0x2e, 0x84, 0xc8, 0x7c, + 0x62, 0x79, 0x68, 0x6c, 0x99, 0x0c, 0xcf, 0x0c, 0xa3, 0x18, 0x4e, 0xac, 0x94, 0xe3, 0x4a, 0x28, + 0x5d, 0x57, 0x6f, 0x40, 0x8b, 0xc9, 0x2d, 0xaa, 0x03, 0x82, 0x86, 0x11, 0x7a, 0x1b, 0x14, 0x18, + 0x52, 0x07, 0xc4, 0xee, 0xd3, 0xce, 0x30, 0xb2, 0x19, 0xf6, 0x57, 0x06, 0x54, 0xd6, 0x32, 0x31, + 0x0c, 0x1d, 0xa2, 0x78, 0xa1, 0xb0, 0xa8, 0x0a, 0xab, 0x75, 0x24, 0x93, 0xf0, 0xe1, 0x7c, 0xec, + 0x07, 0xd3, 0xa8, 0x96, 0x41, 0x9d, 0x39, 0xf3, 0x9d, 0x39, 0xbc, 0x78, 0xa2, 0xf2, 0x99, 0x9f, + 0x95, 0x40, 0x11, 0xac, 0x4b, 0x34, 0x3f, 0xb7, 0x22, 0xe6, 0xb8, 0x6d, 0x05, 0x05, 0x3c, 0x7a, + 0xa8, 0x76, 0x44, 0x32, 0x9a, 0x19, 0x3f, 0x66, 0xd6, 0xce, 0x01, 0x43, 0x72, 0x4c, 0xb0, 0xa3, + 0x31, 0x7c, 0xec, 0x71, 0xbb, 0x59, 0xed, 0x91, 0x89, 0x78, 0x96, 0xd2, 0xb2, 0xd0, 0xa5, 0x20, + 0x28, 0x20, 0xce, 0x27, 0x68, 0x5a, 0xa6, 0xb6, 0xb7, 0x5f, 0x6b, 0x36, 0xad, 0x46, 0xa3, 0x0d, + 0xa1, 0x60, 0xf6, 0xeb, 0x35, 0xab, 0xd9, 0x42, 0x43, 0xc9, 0x6a, 0x7a, 0xfb, 0xbb, 0x56, 0x13, + 0xdc, 0x4b, 0x93, 0x6f, 0xb3, 0x66, 0xb6, 0x5a, 0x4d, 0xab, 0xbe, 0x5b, 0x6d, 0x9b, 0x2c, 0xea, + 0xcc, 0x7e, 0x97, 0x96, 0x4a, 0x92, 0x4d, 0x6d, 0x36, 0x0c, 0x4b, 0xf9, 0x6e, 0x34, 0x76, 0x58, + 0x14, 0x9a, 0xfd, 0x84, 0x73, 0x2d, 0x65, 0x4a, 0x9b, 0x46, 0xf9, 0x1d, 0xb6, 0x12, 0xaf, 0xc5, + 0x52, 0xa6, 0x6a, 0x13, 0x32, 0x21, 0xa8, 0xe9, 0xf6, 0x60, 0xfb, 0x1d, 0x46, 0x33, 0xfb, 0x0a, + 0x1c, 0x88, 0xaf, 0x80, 0xf1, 0xee, 0xda, 0xf7, 0xfa, 0x50, 0x59, 0xc9, 0x7b, 0xfd, 0x2a, 0x59, + 0xc8, 0x21, 0xd5, 0x67, 0xfa, 0x95, 0x81, 0xfa, 0xca, 0x12, 0xdc, 0x42, 0x3e, 0x37, 0x14, 0xfb, + 0x45, 0xd7, 0xa6, 0xaa, 0x75, 0x0e, 0xba, 0x42, 0xfd, 0x64, 0xab, 0x79, 0x60, 0x42, 0x73, 0x5d, + 0x37, 0x6f, 0xb6, 0x6c, 0xb3, 0xb6, 0x63, 0x20, 0x78, 0xe7, 0x4f, 0xef, 0xa9, 0xe8, 0xec, 0x5c, + 0xe9, 0x29, 0xc0, 0x7a, 0x08, 0x66, 0x32, 0xc2, 0xb8, 0x9b, 0xb5, 0x64, 0x64, 0xa4, 0x9d, 0x57, + 0xa8, 0xc7, 0xfb, 0x7d, 0x63, 0x1f, 0xe9, 0xb7, 0x7a, 0xcf, 0x40, 0x50, 0x9f, 0x91, 0x87, 0x6f, + 0x6c, 0xdb, 0xc6, 0x2b, 0xdc, 0x9a, 0xa4, 0x06, 0x08, 0x2c, 0x25, 0x99, 0xa0, 0x8a, 0xbd, 0xb3, + 0x4c, 0xb0, 0x8b, 0xfd, 0x7b, 0x55, 0x33, 0x75, 0xaf, 0xbf, 0x31, 0x54, 0xa1, 0x68, 0x3a, 0x3c, + 0xe8, 0xbe, 0x6b, 0xa8, 0x12, 0xd0, 0x2b, 0xd4, 0x43, 0x6f, 0xf8, 0x80, 0x3e, 0xd9, 0x33, 0xfd, + 0xaa, 0xa4, 0xf7, 0xca, 0x6f, 0x52, 0x71, 0x5c, 0xcf, 0x9e, 0xe9, 0xbd, 0xf2, 0x27, 0x50, 0x60, + 0xcc, 0xf4, 0x37, 0xdb, 0x9f, 0x0c, 0x34, 0x7f, 0xec, 0xd5, 0xac, 0x1e, 0x7a, 0xec, 0xd5, 0xad, + 0x37, 0x8a, 0xe4, 0xe6, 0x88, 0x4f, 0x0c, 0x0c, 0xac, 0xa6, 0x30, 0x6d, 0xcc, 0xe2, 0x91, 0xc0, + 0xa7, 0x04, 0x45, 0x0f, 0x8d, 0x21, 0x48, 0x10, 0x9f, 0x7a, 0x0f, 0x7d, 0xb2, 0xaf, 0xd0, 0x67, + 0xfb, 0x1d, 0x46, 0xdf, 0x6c, 0x4c, 0xd1, 0x17, 0x1b, 0xe4, 0xdc, 0xf0, 0xe9, 0x60, 0x1b, 0xd3, + 0x04, 0x2b, 0x9d, 0x24, 0x72, 0x90, 0x69, 0xd9, 0x34, 0x3a, 0x27, 0x78, 0x0f, 0x72, 0x4f, 0x70, + 0xb9, 0xcc, 0xc7, 0x7c, 0x46, 0xed, 0x37, 0x7b, 0x7b, 0x27, 0x78, 0xeb, 0x1d, 0xee, 0x38, 0xf8, + 0xdf, 0xec, 0x33, 0xca, 0x07, 0xf8, 0x99, 0x0d, 0xf0, 0x4b, 0x6c, 0x9f, 0xea, 0x0e, 0x46, 0x9f, + 0xd1, 0x17, 0x31, 0xf0, 0x6f, 0x2c, 0x9d, 0x62, 0x91, 0xf1, 0x0d, 0x51, 0xcc, 0x44, 0xf6, 0x67, + 0x94, 0x0d, 0xec, 0x94, 0xda, 0x9f, 0xb7, 0xbf, 0xa1, 0x8f, 0xd4, 0x9e, 0xe9, 0xa7, 0xb4, 0x74, + 0x4a, 0x0d, 0xf4, 0x86, 0xda, 0x9f, 0xcb, 0xdf, 0xd0, 0x6f, 0x11, 0x00, 0x80, 0x96, 0xde, 0x80, + 0xeb, 0x58, 0x64, 0x7f, 0xa4, 0xe5, 0xdf, 0x22, 0xd4, 0x0b, 0xed, 0x2f, 0xdb, 0x14, 0xa3, 0xf7, + 0xa1, 0xfd, 0xa5, 0x4c, 0x31, 0xfa, 0x03, 0x0a, 0xbd, 0x0f, 0x59, 0xbd, 0x5f, 0xe1, 0x77, 0x2f, + 0x64, 0x15, 0x7e, 0x8e, 0xec, 0x3f, 0xa2, 0xed, 0x5f, 0x23, 0x84, 0x03, 0xfb, 0x8f, 0xa8, 0xfc, + 0x6b, 0xd4, 0xf9, 0x62, 0xcf, 0xf4, 0x9f, 0xa3, 0xd2, 0xcf, 0x11, 0x53, 0x22, 0xcc, 0xf4, 0x4f, + 0xa5, 0x99, 0x8e, 0x83, 0x12, 0x86, 0x40, 0x50, 0x9f, 0xed, 0x99, 0xfe, 0x91, 0x96, 0x7e, 0x8b, + 0x40, 0x2b, 0x31, 0xd3, 0xbf, 0x45, 0x25, 0xfd, 0x23, 0xdd, 0x9e, 0xe9, 0xa3, 0xd2, 0xb7, 0x08, + 0x36, 0x93, 0xbe, 0x64, 0x86, 0x2b, 0xa6, 0x28, 0xd5, 0x09, 0x44, 0x4f, 0x4d, 0x5a, 0x66, 0xfa, + 0xe7, 0x12, 0xd8, 0xb1, 0x40, 0xd6, 0xa5, 0x2a, 0xf1, 0xef, 0x2b, 0x02, 0xf6, 0x3e, 0x97, 0xf1, + 0xf7, 0x63, 0x74, 0x68, 0xeb, 0x60, 0x7c, 0xd3, 0x37, 0xc0, 0xfb, 0xf7, 0x77, 0xfd, 0x0a, 0x5d, + 0x72, 0x29, 0x3f, 0xba, 0x52, 0x0e, 0xdd, 0x5d, 0x7a, 0xa8, 0x0e, 0xf5, 0xab, 0x02, 0xe1, 0x7d, + 0x4f, 0x69, 0xfb, 0x4c, 0xef, 0x25, 0x06, 0xf7, 0x52, 0x0a, 0x7b, 0x07, 0x52, 0xd8, 0xcb, 0xa1, + 0x43, 0xb0, 0x10, 0x99, 0x5b, 0x6c, 0x5f, 0xd9, 0x7b, 0xfc, 0xe0, 0x64, 0x65, 0xb4, 0x57, 0xf6, + 0x1e, 0x6c, 0x6e, 0xa1, 0x78, 0x3b, 0x99, 0x8e, 0xa8, 0x75, 0xa2, 0x7c, 0x1c, 0x3a, 0x11, 0xb6, + 0xce, 0x84, 0xdc, 0x76, 0x9e, 0x17, 0xcd, 0x1f, 0x2e, 0x8a, 0x95, 0x0f, 0x63, 0xf4, 0x76, 0xca, + 0xb1, 0x6a, 0x57, 0x30, 0x28, 0x46, 0x22, 0xe6, 0xbd, 0xe4, 0x77, 0x0c, 0xa6, 0xe8, 0x1d, 0x46, + 0x5d, 0x8a, 0xbe, 0x14, 0x5d, 0x33, 0x58, 0xf5, 0xd7, 0x79, 0x87, 0x15, 0xb9, 0x5d, 0x97, 0xa6, + 0x72, 0x3b, 0xf1, 0x70, 0x8c, 0x13, 0xda, 0xf3, 0xb8, 0x73, 0x12, 0xea, 0x4e, 0x88, 0xe6, 0xc7, + 0x47, 0x0c, 0x77, 0xf7, 0xae, 0xce, 0x2e, 0x2e, 0xdf, 0x9f, 0x9d, 0xf6, 0x2e, 0x3f, 0x1e, 0xbe, + 0xbd, 0x38, 0xfb, 0x78, 0x6e, 0x41, 0x0f, 0xc7, 0x04, 0x5d, 0xf8, 0x11, 0x25, 0x98, 0xd2, 0x90, + 0x4b, 0x3a, 0x21, 0xf5, 0x9e, 0x20, 0xec, 0xb1, 0x5a, 0xec, 0xd3, 0xc7, 0xf2, 0xb3, 0xc7, 0x34, + 0xc6, 0x84, 0xa5, 0x76, 0x23, 0x99, 0xea, 0xd2, 0x47, 0x96, 0xe2, 0x26, 0xd5, 0x26, 0x43, 0x96, + 0xe0, 0x41, 0x02, 0x33, 0x55, 0xbf, 0x52, 0x84, 0xf9, 0x2c, 0xeb, 0xa2, 0x30, 0x0b, 0x40, 0xc6, + 0xb2, 0x2f, 0x8b, 0xb3, 0x21, 0x32, 0x84, 0x61, 0xef, 0x91, 0x30, 0x4b, 0x23, 0xb1, 0xc4, 0x2b, + 0xa2, 0x52, 0x66, 0x2c, 0xe9, 0x50, 0x26, 0x25, 0x73, 0x84, 0xdb, 0x07, 0x72, 0x0e, 0x78, 0x4e, + 0x8f, 0x86, 0x3d, 0x92, 0xcf, 0x3b, 0x27, 0x68, 0x21, 0xed, 0x36, 0x9b, 0xa6, 0xc2, 0xe1, 0x31, + 0x42, 0x8f, 0x29, 0xac, 0x46, 0x44, 0xbd, 0xf4, 0xb1, 0x12, 0xc0, 0x4d, 0xae, 0x58, 0x15, 0xa2, + 0xb8, 0xc9, 0x15, 0xab, 0x42, 0x28, 0x37, 0x79, 0x09, 0x31, 0x09, 0xbc, 0xbc, 0xbc, 0xaa, 0x20, + 0xfb, 0x7d, 0x80, 0x0b, 0x98, 0x96, 0xe8, 0x3f, 0x08, 0xfc, 0x8f, 0x5c, 0x3b, 0xc4, 0xba, 0x87, + 0x1e, 0x29, 0x22, 0x46, 0xc9, 0xfb, 0x07, 0x41, 0x53, 0x48, 0x70, 0x51, 0x1f, 0x43, 0x02, 0x94, + 0xe8, 0x43, 0xc2, 0x14, 0xfd, 0x1c, 0x40, 0xc2, 0xf4, 0x1f, 0x04, 0x22, 0xfc, 0x61, 0xbd, 0x8f, + 0x58, 0x81, 0xfe, 0x3f, 0x08, 0x1a, 0xc1, 0xf7, 0x10, 0x41, 0x84, 0xc7, 0xd2, 0xf0, 0x1f, 0x04, + 0x8d, 0xe1, 0x7b, 0x84, 0x58, 0xf9, 0xd1, 0x3f, 0x08, 0x1a, 0xc0, 0xf7, 0x98, 0x3d, 0x4f, 0x54, + 0x1a, 0xff, 0x83, 0xa0, 0x47, 0xf8, 0x1e, 0xa4, 0xdf, 0x33, 0xf8, 0x7e, 0x4c, 0xda, 0x93, 0xe7, + 0x70, 0x12, 0x3e, 0xf4, 0x26, 0xbd, 0x66, 0xaf, 0x6d, 0x85, 0x58, 0x9f, 0x89, 0x11, 0xc2, 0x80, + 0x6e, 0x6b, 0x96, 0xa7, 0x20, 0x73, 0x9a, 0x89, 0x86, 0x75, 0x5d, 0xbd, 0xd9, 0xb2, 0x6b, 0x8d, + 0x36, 0xa2, 0xc9, 0x35, 0xc8, 0x7f, 0x3e, 0xd9, 0xad, 0x86, 0x1a, 0x72, 0x6a, 0x10, 0xa9, 0x52, + 0xa9, 0x07, 0x08, 0x64, 0xf4, 0x4a, 0xc7, 0x25, 0x5c, 0x12, 0x81, 0x8c, 0x5e, 0xe9, 0x41, 0x29, + 0xe0, 0x1f, 0xc4, 0x06, 0xa0, 0x97, 0x42, 0xd0, 0xea, 0x8b, 0x31, 0x21, 0x1f, 0x02, 0x24, 0x96, + 0x82, 0x12, 0x41, 0x2c, 0x1c, 0x0d, 0x54, 0xf5, 0x4b, 0x3e, 0x7c, 0xb8, 0xb6, 0x8f, 0xa6, 0xf6, + 0x2b, 0xdd, 0x2f, 0x4d, 0x23, 0xf8, 0xee, 0x33, 0x6f, 0x6e, 0x2a, 0xbc, 0xba, 0x5f, 0xe9, 0x65, + 0x76, 0xd9, 0x8c, 0x92, 0x2f, 0x5e, 0x2c, 0x21, 0x16, 0x59, 0xbc, 0x1b, 0xd7, 0x40, 0xfa, 0xf0, + 0xe9, 0x09, 0xc2, 0xa3, 0xe8, 0xbe, 0x3d, 0x35, 0xd0, 0x0c, 0xeb, 0xd0, 0x38, 0xfb, 0x7c, 0xa5, + 0x97, 0xe1, 0xb7, 0x62, 0xea, 0xdb, 0x7f, 0x7a, 0x1a, 0x0a, 0x6b, 0x5f, 0xd5, 0x8b, 0x3d, 0x50, + 0xa3, 0x6a, 0xad, 0x96, 0x08, 0x08, 0x63, 0x12, 0x30, 0xbd, 0xa1, 0x61, 0x08, 0xf4, 0x71, 0x7a, + 0x43, 0x73, 0xc9, 0xd2, 0xa5, 0x3f, 0x38, 0xe6, 0x27, 0x71, 0x23, 0x08, 0x37, 0xe4, 0x4f, 0x37, + 0x1c, 0x8d, 0x7c, 0x90, 0x42, 0x83, 0xa7, 0x67, 0x4e, 0xb4, 0x77, 0x1d, 0xed, 0x9b, 0x56, 0x35, + 0x11, 0x82, 0xdc, 0x18, 0x08, 0x23, 0xd5, 0xed, 0x7f, 0xaa, 0xa8, 0x9f, 0xf4, 0x07, 0x5c, 0xfe, + 0x39, 0x30, 0x5e, 0xcf, 0xa2, 0x74, 0x27, 0x37, 0xda, 0xad, 0x56, 0xab, 0x06, 0xbb, 0xf9, 0x82, + 0xb9, 0xc4, 0x50, 0x70, 0xf7, 0xba, 0x10, 0x31, 0xb9, 0x1e, 0x65, 0x04, 0x24, 0xfb, 0x82, 0x71, + 0x39, 0x01, 0xba, 0x90, 0x11, 0xa7, 0x2e, 0x44, 0xe8, 0x0a, 0xe4, 0x8b, 0x8a, 0x2c, 0x58, 0xe2, + 0x85, 0x08, 0x6f, 0xc5, 0x9c, 0xf2, 0x65, 0x2b, 0x91, 0xd1, 0x91, 0x1f, 0x2e, 0x22, 0x3c, 0x07, + 0x5a, 0x73, 0x39, 0x9f, 0x95, 0xe6, 0x08, 0xa6, 0x9f, 0xb7, 0xc7, 0x3c, 0xec, 0xf8, 0xcf, 0x29, + 0x13, 0x5d, 0x89, 0x28, 0x86, 0x9e, 0xfa, 0xe1, 0xf2, 0xfe, 0x79, 0xa7, 0x53, 0x19, 0xe4, 0xee, + 0x82, 0xc5, 0xca, 0xf2, 0xc1, 0x97, 0xbb, 0x2f, 0x72, 0xfb, 0x32, 0x77, 0x98, 0x24, 0xfc, 0x16, + 0xa4, 0x3d, 0xf4, 0xd5, 0x76, 0x3c, 0x19, 0x82, 0xe9, 0x42, 0xf8, 0xf5, 0xb8, 0x40, 0xc2, 0x5e, + 0xf0, 0xc0, 0x86, 0x43, 0xd4, 0x87, 0x90, 0x22, 0x03, 0x51, 0x9a, 0x80, 0x4b, 0xd3, 0x63, 0xd2, + 0x26, 0x35, 0x3a, 0xf2, 0xe3, 0x11, 0x0d, 0x89, 0x8c, 0x52, 0x27, 0x53, 0x7e, 0x0b, 0x80, 0x2a, + 0x95, 0x13, 0x56, 0xfa, 0x7f, 0x2c, 0xe8, 0xff, 0x3e, 0xe9, 0xbf, 0x0b, 0x06, 0x80, 0xa2, 0xff, + 0x19, 0x7a, 0x44, 0xf7, 0x69, 0xc5, 0x71, 0x41, 0xc5, 0x73, 0x75, 0xe0, 0x27, 0xb2, 0xe2, 0x00, + 0x11, 0xe6, 0x45, 0x20, 0xbf, 0x6f, 0xd1, 0x18, 0xbe, 0x4f, 0xed, 0x0b, 0x11, 0x8f, 0xe4, 0x2c, + 0x0d, 0x89, 0x22, 0xcb, 0x9c, 0x21, 0xbe, 0xcc, 0x67, 0x06, 0x3a, 0xdf, 0xb4, 0xed, 0x53, 0xa0, + 0x27, 0x4e, 0x40, 0x31, 0xfa, 0x78, 0xe2, 0x59, 0x21, 0x9a, 0x9d, 0x04, 0xe0, 0x4e, 0x72, 0xe2, + 0x59, 0x7d, 0xac, 0x9a, 0xe9, 0xc8, 0x2d, 0xc7, 0x0a, 0x63, 0x56, 0x38, 0x62, 0x85, 0x03, 0x56, + 0x38, 0x8c, 0xed, 0xa9, 0xd0, 0x91, 0x5d, 0x24, 0x01, 0x38, 0x3b, 0x24, 0x81, 0xe8, 0x84, 0x48, + 0xcf, 0x78, 0x9e, 0x12, 0x71, 0xa7, 0xdf, 0x0b, 0x16, 0x80, 0x13, 0xf3, 0xd3, 0xcf, 0x77, 0x10, + 0xfb, 0x98, 0x26, 0xf3, 0x67, 0x2c, 0x32, 0x9f, 0xfb, 0x14, 0x5d, 0x70, 0x29, 0x01, 0x6b, 0x97, + 0x4d, 0x86, 0x88, 0x24, 0x24, 0x16, 0x9b, 0x25, 0xfa, 0x7c, 0x47, 0x43, 0x9a, 0x27, 0xd3, 0xbc, + 0x34, 0xcd, 0x95, 0x69, 0x6e, 0x92, 0x06, 0x43, 0xbb, 0x1e, 0xa2, 0xd1, 0x0d, 0xc4, 0x08, 0xb9, + 0x40, 0xd7, 0x3e, 0x72, 0x6f, 0x40, 0x09, 0x9b, 0x7a, 0xd8, 0xc8, 0x79, 0x0c, 0x0d, 0x34, 0x13, + 0x1f, 0x1e, 0x1a, 0xa9, 0x56, 0x09, 0x7f, 0x48, 0x66, 0x6f, 0xee, 0x59, 0x38, 0xb6, 0x29, 0x45, + 0x02, 0x39, 0x76, 0xed, 0xbd, 0x0b, 0x29, 0x06, 0xec, 0xb2, 0xe7, 0x0e, 0xf5, 0x49, 0x54, 0xa2, + 0x25, 0x06, 0xac, 0x40, 0xd7, 0xc3, 0xed, 0x3e, 0x36, 0x4a, 0x0f, 0x44, 0x89, 0xc6, 0x01, 0x91, + 0x3a, 0x3c, 0xc8, 0xf3, 0xcb, 0xb8, 0x14, 0x1a, 0xa5, 0x40, 0x0f, 0xb7, 0xb1, 0x8a, 0xb7, 0x5c, + 0x81, 0xb4, 0xa6, 0xb1, 0x3d, 0x00, 0x26, 0xd9, 0x13, 0x82, 0x84, 0x12, 0x85, 0xf0, 0x2d, 0x7a, + 0x1f, 0x45, 0x06, 0x8b, 0x84, 0x14, 0xe8, 0xe5, 0x3e, 0x88, 0x8b, 0x9f, 0x9e, 0xf4, 0xa9, 0xdd, + 0xe7, 0x3f, 0x7c, 0x3b, 0x94, 0xc7, 0x26, 0xd0, 0xfd, 0x92, 0x1e, 0x96, 0xa1, 0xff, 0x2e, 0x29, + 0x7b, 0x80, 0x5c, 0xa7, 0xa5, 0x29, 0x78, 0x11, 0x81, 0x10, 0x6e, 0x6a, 0x94, 0x3c, 0x38, 0x15, + 0x81, 0x3e, 0x2c, 0xcd, 0x08, 0x1c, 0x89, 0x40, 0xef, 0xe3, 0xf2, 0x08, 0x42, 0x82, 0xc2, 0xaf, + 0xed, 0x51, 0xc6, 0x70, 0xda, 0x17, 0x8a, 0x5c, 0x3d, 0xd0, 0xc7, 0xa5, 0x99, 0x81, 0x02, 0xfd, + 0xb1, 0x34, 0x80, 0x7f, 0x06, 0xfc, 0x6b, 0x5c, 0x7a, 0xcc, 0x88, 0x87, 0xd8, 0xed, 0xf3, 0xbb, + 0x4e, 0x51, 0xab, 0x21, 0x43, 0x7e, 0x8e, 0x41, 0x10, 0xa4, 0x98, 0x05, 0xd4, 0x6b, 0x06, 0x5c, + 0xd0, 0x7f, 0x30, 0x99, 0x70, 0x90, 0xcb, 0xae, 0xd7, 0xa0, 0x26, 0x00, 0xf4, 0x0f, 0x70, 0xe3, + 0x54, 0x87, 0xe2, 0x62, 0x3d, 0x92, 0xbe, 0xc8, 0x8a, 0xe0, 0x59, 0xbd, 0xef, 0x7c, 0x5c, 0x61, + 0x24, 0x63, 0x25, 0x63, 0x94, 0xc1, 0xed, 0x8f, 0x34, 0x94, 0x89, 0x7c, 0x7f, 0x49, 0xd6, 0xd5, + 0x13, 0x26, 0x11, 0x6a, 0x6d, 0x20, 0xfd, 0x7c, 0x02, 0xd4, 0xc0, 0x23, 0x45, 0xbf, 0x06, 0x40, + 0x05, 0xcc, 0x22, 0xf4, 0x80, 0x11, 0xa5, 0x68, 0x1a, 0xa1, 0x0b, 0x14, 0x85, 0xe8, 0x21, 0x42, + 0x3e, 0x46, 0x2e, 0x41, 0x1e, 0x41, 0x23, 0x82, 0xfa, 0x04, 0x0d, 0x09, 0xfa, 0x0d, 0xf4, 0xf0, + 0xa8, 0x1b, 0xa1, 0x49, 0x84, 0x66, 0x04, 0x0d, 0x08, 0x7a, 0x20, 0xa8, 0x4b, 0xd0, 0xb7, 0x00, + 0x3d, 0x12, 0x34, 0x8e, 0x90, 0x8b, 0xd1, 0x2d, 0x41, 0x8f, 0x11, 0x02, 0x7a, 0x91, 0xa0, 0x7b, + 0x82, 0x0e, 0x09, 0xba, 0x22, 0xe8, 0x80, 0xa0, 0x73, 0x82, 0xfc, 0x30, 0x21, 0x58, 0xbb, 0xc9, + 0x6b, 0x84, 0x4c, 0xe3, 0xfc, 0x09, 0x3e, 0xdf, 0x06, 0x92, 0x88, 0xfd, 0x02, 0xbf, 0x18, 0x4d, + 0xeb, 0x13, 0x85, 0x9c, 0xed, 0xab, 0xe4, 0xec, 0xa3, 0x42, 0xce, 0xa2, 0x5f, 0xd5, 0x90, 0x43, + 0x3f, 0xab, 0x51, 0x86, 0x66, 0x91, 0x12, 0x58, 0xe8, 0x21, 0x69, 0x00, 0xde, 0x13, 0xeb, 0x7f, + 0xcf, 0x7f, 0xc0, 0xed, 0x22, 0x4a, 0xed, 0xf9, 0xc4, 0x82, 0x93, 0xa4, 0x3c, 0x5d, 0x60, 0x56, + 0x57, 0xff, 0x67, 0x36, 0x3c, 0xdc, 0xdf, 0xf5, 0xb0, 0x53, 0xeb, 0xef, 0xec, 0xba, 0x5e, 0xab, + 0xd9, 0x36, 0x6b, 0xad, 0xba, 0xe9, 0x34, 0xdd, 0x7e, 0xd3, 0xab, 0xb3, 0x76, 0x87, 0xd6, 0x2c, + 0x42, 0x8e, 0xf5, 0xc3, 0xc6, 0xea, 0x6a, 0x06, 0xf2, 0x94, 0xe6, 0x9a, 0xb5, 0x6a, 0xbd, 0xe5, + 0x62, 0x5c, 0xbb, 0x6d, 0xf5, 0xfb, 0x78, 0xa7, 0x0e, 0x0f, 0xa6, 0x35, 0xaa, 0x3b, 0xbb, 0x3b, + 0x3b, 0x3b, 0xbb, 0xb8, 0xbd, 0xdb, 0xae, 0x56, 0x77, 0xaa, 0x55, 0xa7, 0xe1, 0x35, 0xcc, 0x86, + 0xe9, 0xb5, 0x9d, 0xdb, 0x9d, 0x26, 0xbe, 0x6d, 0x78, 0xae, 0x03, 0x0f, 0xc1, 0xee, 0xc0, 0x23, + 0x6d, 0x06, 0x18, 0x8d, 0xa4, 0xed, 0xd5, 0xcc, 0xd6, 0x6e, 0xbd, 0xe5, 0xd5, 0x5d, 0xaf, 0x85, + 0x9b, 0xf5, 0x3e, 0x76, 0xab, 0x4e, 0x03, 0xd7, 0xea, 0x66, 0xdf, 0xf3, 0x5a, 0x9e, 0xdb, 0x74, + 0x5b, 0xbb, 0x35, 0xd7, 0xdd, 0x69, 0x55, 0x77, 0x9b, 0xb5, 0xa6, 0x03, 0xef, 0xaa, 0xed, 0x36, + 0x5b, 0x35, 0xaf, 0x55, 0x6d, 0xf7, 0x6b, 0x4d, 0xaf, 0x69, 0x3a, 0xd0, 0xde, 0x4c, 0x69, 0xaf, + 0xf5, 0x5d, 0xff, 0x35, 0xdb, 0x9a, 0x11, 0x77, 0xa6, 0xc9, 0xc2, 0x6b, 0xe6, 0x6e, 0xab, 0x6d, + 0x9a, 0x2d, 0xb3, 0xbe, 0xd3, 0xda, 0xa9, 0xee, 0x34, 0xab, 0xcd, 0xdd, 0x66, 0xab, 0x5d, 0xdd, + 0xa9, 0xc2, 0xb3, 0x84, 0x8d, 0xdd, 0x76, 0xbb, 0xd9, 0xa8, 0x55, 0xcd, 0x66, 0xa3, 0xd1, 0xaa, + 0xb6, 0x5a, 0x4d, 0xb3, 0xb9, 0x5b, 0xab, 0xb7, 0x77, 0xab, 0x66, 0xab, 0xb6, 0xd3, 0x68, 0x54, + 0x6b, 0x66, 0x75, 0xa7, 0x6e, 0xd6, 0xea, 0xed, 0xda, 0xee, 0x4e, 0xbb, 0xb1, 0xd3, 0xac, 0x69, + 0x46, 0xe7, 0x82, 0xc5, 0xf4, 0xa5, 0x95, 0x89, 0x88, 0xa4, 0x01, 0xee, 0x78, 0x28, 0x0a, 0x45, + 0x6a, 0xa0, 0xa6, 0x3e, 0x44, 0x36, 0x13, 0xbd, 0x51, 0x30, 0x2f, 0xbe, 0xe0, 0xfa, 0x8b, 0x33, + 0x5c, 0x64, 0x03, 0x13, 0x26, 0x86, 0x34, 0x03, 0x10, 0x34, 0x62, 0x7b, 0x40, 0xf5, 0x07, 0x20, + 0x65, 0x08, 0xfc, 0x82, 0x46, 0x1e, 0x12, 0xab, 0x98, 0x20, 0x8c, 0x0d, 0xe4, 0xb1, 0x8c, 0x05, + 0xa9, 0xe5, 0x43, 0x84, 0xe6, 0x49, 0xb1, 0xc4, 0x6c, 0xe6, 0x0c, 0xc7, 0xe0, 0xc8, 0x46, 0x6c, + 0x7e, 0xfc, 0x00, 0xa5, 0x51, 0xfb, 0x42, 0xf8, 0xa4, 0x09, 0xf4, 0xf1, 0x39, 0xd0, 0xe7, 0xe7, + 0x16, 0xe5, 0x32, 0x40, 0x29, 0xc9, 0x53, 0xc5, 0x7e, 0x58, 0x38, 0x6f, 0xa6, 0x44, 0x7c, 0x04, + 0x74, 0x7b, 0x10, 0x03, 0x29, 0x8d, 0xd5, 0x50, 0xde, 0xa0, 0xef, 0xf9, 0x35, 0x40, 0xd4, 0x28, + 0xc1, 0x9f, 0xb8, 0x70, 0xbe, 0xb1, 0x11, 0x1b, 0x3a, 0xc4, 0xb8, 0x03, 0x3a, 0x71, 0xfb, 0x57, + 0x4e, 0x27, 0x0e, 0x89, 0xa0, 0xa6, 0x1e, 0x29, 0xea, 0x13, 0x03, 0xfd, 0x26, 0xe8, 0x44, 0xaa, + 0x73, 0xca, 0xe0, 0x42, 0x04, 0x16, 0x9d, 0x10, 0xfb, 0x28, 0xd0, 0x2f, 0x04, 0xbd, 0xa0, 0xd2, + 0x96, 0x0d, 0x50, 0x8c, 0x76, 0x23, 0xfb, 0x97, 0x40, 0x97, 0x58, 0x1f, 0x51, 0x7b, 0x0f, 0x88, + 0x04, 0x26, 0x52, 0x67, 0xf2, 0x70, 0xe9, 0x19, 0xcd, 0x99, 0xd5, 0xdf, 0x4e, 0x8e, 0xac, 0xcb, + 0x77, 0x07, 0xe5, 0xa6, 0x59, 0xeb, 0x1d, 0x7f, 0xf8, 0x50, 0xeb, 0x5d, 0x9c, 0xf5, 0x34, 0x94, + 0x48, 0xcc, 0xd7, 0x95, 0x3e, 0xfd, 0xd8, 0xd3, 0x10, 0x43, 0x03, 0x20, 0x2a, 0xbe, 0xb3, 0xd8, + 0x43, 0x72, 0x5c, 0x43, 0xc5, 0x14, 0x33, 0x28, 0x59, 0x01, 0x34, 0x89, 0xec, 0x29, 0xa0, 0xce, + 0x64, 0x6b, 0xd6, 0x9a, 0xd5, 0x56, 0xbd, 0xda, 0x6a, 0xef, 0xc2, 0x5b, 0x95, 0x8d, 0x56, 0xad, + 0xde, 0xd8, 0x69, 0x98, 0xa6, 0x09, 0x07, 0xaf, 0xd9, 0xde, 0xa9, 0xd6, 0xcc, 0x66, 0x6d, 0xa7, + 0x6a, 0xd6, 0x1a, 0x8d, 0x66, 0xdd, 0x6c, 0x56, 0x6b, 0x8d, 0xdd, 0x5a, 0xab, 0xd9, 0x6a, 0xb4, + 0xaa, 0xd5, 0x9d, 0xdd, 0x9a, 0x59, 0x6d, 0xb4, 0x6b, 0x2d, 0xb3, 0xda, 0xa8, 0xc3, 0xa6, 0x86, + 0x87, 0x49, 0x0d, 0x34, 0x48, 0x9b, 0x86, 0x0d, 0xbd, 0x5b, 0x87, 0x93, 0xdc, 0xde, 0xad, 0xee, + 0xd6, 0xcd, 0xd6, 0x6e, 0xad, 0xba, 0xbb, 0xdb, 0x6c, 0x9b, 0xf5, 0x76, 0xab, 0xbd, 0xd3, 0x80, + 0xe7, 0x1e, 0x5b, 0xd5, 0x66, 0x7d, 0x17, 0xce, 0x34, 0xbc, 0xbd, 0x59, 0xdb, 0x69, 0x34, 0x5b, + 0xbb, 0x35, 0xb3, 0x56, 0x6d, 0xd6, 0xcd, 0x5a, 0x7b, 0xb7, 0x55, 0x37, 0xcd, 0x9d, 0x9a, 0x59, + 0x85, 0xa7, 0xa1, 0xdb, 0x9a, 0x81, 0x1e, 0xd2, 0xa6, 0x4d, 0xb3, 0xb9, 0xdb, 0x6e, 0xd4, 0xab, + 0x20, 0x75, 0x6d, 0xef, 0xec, 0xec, 0xb6, 0x77, 0x76, 0xcd, 0xdd, 0xfa, 0xce, 0x4e, 0x13, 0x1e, + 0xaa, 0x6c, 0x36, 0x9b, 0xed, 0x56, 0x0b, 0xde, 0xfc, 0xdc, 0xa9, 0x37, 0x77, 0x76, 0x9a, 0xbb, + 0x3b, 0xf0, 0xfe, 0xf3, 0x4e, 0xab, 0xd9, 0x68, 0xd4, 0xa1, 0xe4, 0x4e, 0xad, 0xda, 0xde, 0x69, + 0x99, 0xa6, 0xd9, 0xae, 0xb6, 0xda, 0x75, 0x68, 0xb9, 0x9b, 0xb6, 0xdc, 0xa8, 0x36, 0x1a, 0xd5, + 0x76, 0xbd, 0x01, 0x6f, 0x48, 0x57, 0xdb, 0xcd, 0x7a, 0xab, 0xdd, 0x6c, 0x9b, 0x55, 0x13, 0x1e, + 0xaa, 0x6e, 0xed, 0xd6, 0x6b, 0x75, 0x73, 0xb7, 0xda, 0xae, 0xb5, 0x6a, 0x8d, 0x76, 0x7d, 0x77, + 0xd7, 0x04, 0x78, 0xb5, 0x77, 0xaa, 0xed, 0x7a, 0xb3, 0x56, 0x6b, 0x54, 0xcd, 0x7a, 0xbd, 0x56, + 0xab, 0xb6, 0xd9, 0xe3, 0xd4, 0xb5, 0x56, 0x73, 0x17, 0x8e, 0x2a, 0xfa, 0x06, 0x91, 0x6b, 0xf6, + 0x06, 0x91, 0xde, 0x07, 0x36, 0x05, 0x3d, 0x92, 0x1f, 0x76, 0x01, 0xf4, 0xfb, 0x9a, 0x81, 0xc6, + 0x11, 0x34, 0x2f, 0xf7, 0x19, 0x98, 0xd7, 0xa5, 0x86, 0xf7, 0xd4, 0xd8, 0x7a, 0x24, 0x46, 0xc7, + 0xc5, 0xe2, 0x09, 0x42, 0x9a, 0x3c, 0x42, 0x38, 0xa3, 0x79, 0x0b, 0x29, 0xfe, 0x02, 0x21, 0x36, + 0x0a, 0x1c, 0x69, 0x71, 0xc6, 0x4d, 0x8f, 0x26, 0x9b, 0x3a, 0xa3, 0xdd, 0x32, 0x8c, 0x38, 0x63, + 0xb3, 0xc3, 0x9d, 0xcd, 0xb1, 0xea, 0x7d, 0x5b, 0x64, 0x2e, 0x91, 0x15, 0xe9, 0xa8, 0x66, 0x13, + 0xcc, 0xda, 0x25, 0xdf, 0x75, 0x3a, 0x3e, 0x55, 0xb5, 0x95, 0x96, 0x0a, 0x99, 0xe0, 0x39, 0x91, + 0x7f, 0x80, 0x92, 0x43, 0x43, 0x98, 0x51, 0x42, 0x85, 0x96, 0x5f, 0xbf, 0xeb, 0x18, 0xc8, 0x28, + 0xe1, 0x95, 0x17, 0x21, 0x0f, 0x50, 0x09, 0xa5, 0x28, 0x04, 0xe2, 0x94, 0xd8, 0xe7, 0x0a, 0x71, + 0x7a, 0x0e, 0x14, 0xe8, 0x98, 0x05, 0x89, 0x80, 0xa9, 0xdd, 0x06, 0xfa, 0x45, 0x62, 0x5a, 0xe3, + 0x83, 0x35, 0xc4, 0xd3, 0x13, 0xe3, 0xa6, 0xc3, 0x15, 0x3e, 0x62, 0xaa, 0x60, 0x66, 0x83, 0x9d, + 0x6e, 0x78, 0xc9, 0xc6, 0x4c, 0x8c, 0x19, 0x88, 0xee, 0x97, 0x18, 0x87, 0x48, 0x80, 0x80, 0x8c, + 0x4a, 0x8c, 0xf3, 0x83, 0xdf, 0x65, 0xf6, 0xbb, 0x6f, 0x13, 0xdd, 0x2d, 0xb9, 0xa0, 0xfa, 0x25, + 0xfa, 0xb4, 0x34, 0x05, 0x8a, 0x94, 0xe8, 0x51, 0x29, 0x28, 0xf5, 0xcb, 0x43, 0x85, 0xfe, 0x1d, + 0x0b, 0xfa, 0x77, 0x10, 0xdb, 0xdf, 0x02, 0x9d, 0xe8, 0xa3, 0xd2, 0xd0, 0x00, 0x02, 0x95, 0xe8, + 0x03, 0xa8, 0x34, 0x63, 0x3f, 0x1e, 0x4b, 0x23, 0x60, 0xcd, 0x88, 0xae, 0xfb, 0xdb, 0xbe, 0x51, + 0x7a, 0x64, 0xa4, 0x71, 0x17, 0x85, 0x06, 0xf3, 0xd8, 0x27, 0x7a, 0xb9, 0x6b, 0x48, 0xae, 0x0c, + 0x7a, 0x9d, 0x01, 0x47, 0x46, 0xf4, 0x6e, 0xe9, 0x9e, 0xcf, 0x7f, 0xcc, 0xa6, 0x7b, 0x8b, 0x42, + 0xe3, 0xe9, 0xe9, 0x1e, 0x0c, 0x8b, 0xc9, 0x4b, 0xa7, 0x5d, 0xd3, 0x32, 0x54, 0x29, 0xd5, 0x33, + 0x64, 0x32, 0xf8, 0x91, 0xf7, 0x31, 0xca, 0x79, 0x51, 0x0b, 0xcb, 0xbc, 0x8c, 0x7e, 0x35, 0x71, + 0xcb, 0x4d, 0x97, 0x1d, 0x3f, 0xc2, 0xaa, 0x03, 0x7d, 0xac, 0xfa, 0x4d, 0xa9, 0x06, 0x55, 0x2c, + 0x1a, 0x5a, 0x7a, 0x6a, 0x02, 0x26, 0xcf, 0xc8, 0xf9, 0xa6, 0xfd, 0x66, 0x61, 0xf4, 0xbb, 0x15, + 0xa1, 0x2f, 0x56, 0x80, 0xae, 0x98, 0x9d, 0x23, 0xb7, 0xbd, 0xe2, 0xd2, 0x2e, 0x1f, 0xc2, 0x4c, + 0x5c, 0x28, 0x61, 0x26, 0x3c, 0xdb, 0xd7, 0x7d, 0x3d, 0xd8, 0x8e, 0x8c, 0x92, 0xaf, 0x07, 0xe5, + 0x88, 0x19, 0xa2, 0xf8, 0x3a, 0x2e, 0x31, 0x8e, 0xcd, 0xe7, 0x2b, 0x37, 0xe7, 0x2b, 0xd3, 0x67, + 0x2b, 0xe3, 0xeb, 0x5e, 0x69, 0x6a, 0xc0, 0x72, 0xfa, 0x7a, 0xbf, 0xc4, 0x18, 0x0c, 0xf8, 0xc1, + 0x98, 0x6f, 0x5f, 0x1f, 0x96, 0x46, 0xa5, 0xd0, 0x40, 0x03, 0x16, 0x9e, 0x00, 0xeb, 0x61, 0x69, + 0x0c, 0x21, 0x38, 0x44, 0xf0, 0x31, 0x5f, 0x8f, 0x4a, 0x13, 0xa6, 0xf1, 0x81, 0x1e, 0x26, 0x11, + 0xd8, 0x7b, 0xcc, 0x50, 0x64, 0x77, 0xd1, 0x80, 0x55, 0x1d, 0xc8, 0x07, 0x97, 0x06, 0xf6, 0x08, + 0x16, 0x16, 0xb3, 0xda, 0x2c, 0x42, 0xb2, 0xaf, 0xc3, 0xd8, 0x84, 0x5f, 0x96, 0xaf, 0xb3, 0xa1, + 0x96, 0x06, 0xa9, 0x48, 0x1c, 0x84, 0x66, 0xac, 0x28, 0xe4, 0x96, 0x1f, 0x0d, 0x03, 0xa5, 0x1b, + 0xfd, 0x31, 0x71, 0x24, 0x96, 0x46, 0x95, 0x99, 0x43, 0x2f, 0xdc, 0x8a, 0x23, 0xf4, 0x3b, 0x3b, + 0x47, 0x02, 0x5c, 0xf3, 0xdf, 0xac, 0x10, 0xfd, 0x0e, 0x16, 0x41, 0x60, 0xac, 0x87, 0x7c, 0x7b, + 0xaa, 0x00, 0x6e, 0xca, 0x01, 0x17, 0x95, 0x08, 0xf8, 0x80, 0xfb, 0x7a, 0x50, 0x0a, 0x39, 0xdc, + 0x02, 0x99, 0x12, 0x95, 0x52, 0x33, 0x52, 0xef, 0xe9, 0xc9, 0x5d, 0xee, 0x5d, 0x2c, 0xd4, 0xe4, + 0x71, 0xdc, 0x71, 0x71, 0xea, 0xc2, 0xe6, 0xe2, 0x14, 0x63, 0x31, 0xfb, 0x68, 0xc8, 0x4d, 0x3c, + 0xd7, 0xd4, 0x5c, 0xce, 0x3e, 0xbb, 0x10, 0xa6, 0xc4, 0xbe, 0x60, 0xff, 0x06, 0x76, 0x14, 0x76, + 0x6e, 0x89, 0x3d, 0xe7, 0x12, 0x67, 0x17, 0xc7, 0xe8, 0x31, 0xb2, 0xe7, 0x2a, 0xf2, 0x51, 0x6d, + 0x75, 0xf7, 0x2b, 0x47, 0x97, 0x57, 0x4f, 0x4f, 0x5a, 0x46, 0x00, 0xab, 0xde, 0xd6, 0x17, 0xb5, + 0x66, 0xf3, 0xe4, 0xe0, 0x9c, 0x5f, 0xef, 0x01, 0xb7, 0xd4, 0x88, 0x50, 0xab, 0x81, 0xce, 0x52, + 0xe2, 0x25, 0x0c, 0xf5, 0x20, 0xaf, 0x31, 0x66, 0x81, 0x55, 0x84, 0x0a, 0x38, 0x91, 0x2d, 0x32, + 0x5b, 0x2e, 0xe8, 0x31, 0xd7, 0x40, 0x14, 0x2a, 0x77, 0x00, 0x98, 0x06, 0xc6, 0xe8, 0x98, 0xd8, + 0xd7, 0x5a, 0x75, 0x1d, 0x5f, 0xb0, 0xe6, 0x3f, 0x0d, 0x69, 0xee, 0x8e, 0xb9, 0xd3, 0x72, 0x76, + 0xaa, 0x75, 0xaf, 0xe1, 0x79, 0xed, 0x46, 0xff, 0xd6, 0xa9, 0xbb, 0xd5, 0xdb, 0x9d, 0x56, 0xd5, + 0x33, 0xab, 0xad, 0x9d, 0x6a, 0xbf, 0xe6, 0xc0, 0xdd, 0xdd, 0x77, 0x6a, 0x6e, 0x7d, 0xd7, 0x75, + 0xdd, 0x56, 0x03, 0xbb, 0x3b, 0x7d, 0x6f, 0x67, 0x67, 0xb7, 0xe6, 0xb8, 0xd5, 0xfa, 0x0e, 0x7b, + 0x8d, 0xf7, 0xbb, 0xfe, 0x6b, 0xc3, 0x28, 0x6a, 0x2d, 0xdc, 0xde, 0x6d, 0xc2, 0x13, 0xdf, 0xb7, + 0xb5, 0xda, 0xce, 0x6d, 0xb5, 0xd1, 0x74, 0xeb, 0xfd, 0x46, 0x7b, 0xb7, 0x5f, 0xc3, 0xfd, 0xdd, + 0x76, 0xbf, 0xea, 0x35, 0xbd, 0xbe, 0xe3, 0x56, 0x9b, 0x5e, 0xdd, 0x6d, 0xd5, 0xeb, 0xf5, 0xdd, + 0x5b, 0xb3, 0xde, 0xae, 0xd6, 0xda, 0xed, 0x96, 0xd7, 0xac, 0xf7, 0xdd, 0x2a, 0x3c, 0xa7, 0x8c, + 0xdd, 0xef, 0xb9, 0x77, 0x77, 0xfa, 0xdf, 0x3d, 0x8a, 0x76, 0xf3, 0xbb, 0x61, 0xf1, 0x03, 0x56, + 0xa4, 0xef, 0x68, 0x37, 0x9d, 0x0b, 0x62, 0x93, 0xb0, 0x73, 0x4f, 0x6c, 0x17, 0xa3, 0x43, 0x62, + 0x77, 0xb9, 0x09, 0x93, 0xd8, 0xdf, 0xe8, 0x8a, 0xa5, 0x64, 0x94, 0x18, 0xe8, 0x80, 0xd8, 0x8f, + 0xd9, 0x52, 0xe7, 0xf9, 0x14, 0xa1, 0xdf, 0x79, 0x57, 0x64, 0x90, 0x2d, 0x50, 0x07, 0x7f, 0x7c, + 0xd1, 0xbe, 0xbe, 0x51, 0xf0, 0x6e, 0xb1, 0xd9, 0xa7, 0xfa, 0x56, 0x63, 0xfc, 0x40, 0x7c, 0x8a, + 0x93, 0x1b, 0x3c, 0xd3, 0x12, 0xf0, 0x39, 0xe9, 0x37, 0x38, 0xc1, 0xe1, 0x9b, 0xb4, 0x7c, 0x52, + 0x5c, 0x69, 0xe1, 0x1a, 0xdf, 0x88, 0x16, 0x3f, 0xbe, 0x0f, 0xa8, 0xd9, 0xfa, 0x70, 0xac, 0x2f, + 0x33, 0x29, 0xad, 0x19, 0x1d, 0x48, 0x39, 0x72, 0xa8, 0xf3, 0xc9, 0xc7, 0x0f, 0x7a, 0x32, 0x22, + 0x88, 0xdb, 0x04, 0xe5, 0xcc, 0x96, 0x5e, 0x45, 0x38, 0x8d, 0x64, 0xa2, 0x74, 0x13, 0x29, 0x9d, + 0xd4, 0x6b, 0x2b, 0x3a, 0x69, 0xac, 0xeb, 0xa4, 0x5e, 0x5b, 0xdb, 0xc9, 0x77, 0xf5, 0xf1, 0xfe, + 0x59, 0x5d, 0x08, 0x0b, 0xb5, 0x02, 0x80, 0x42, 0xcb, 0x57, 0xf8, 0x91, 0x1e, 0xb3, 0x0d, 0x03, + 0xde, 0x55, 0x7c, 0xeb, 0x30, 0x0a, 0x91, 0x95, 0xeb, 0xca, 0xca, 0xe9, 0x7b, 0x56, 0xb9, 0x01, + 0x26, 0xd6, 0x60, 0x87, 0x6c, 0x5c, 0x80, 0xc3, 0x40, 0x30, 0xb8, 0xbc, 0x65, 0xa6, 0x2a, 0x87, + 0xed, 0x46, 0x20, 0x9c, 0xed, 0x4f, 0xd1, 0xd6, 0x16, 0xf9, 0x29, 0x94, 0x96, 0xbe, 0x64, 0x7b, + 0xdb, 0x08, 0xae, 0xc9, 0x8d, 0x1d, 0x5e, 0x93, 0x9b, 0x4e, 0x70, 0x1d, 0xa4, 0x1e, 0xe4, 0x76, + 0x75, 0x61, 0x86, 0x01, 0xa0, 0xcb, 0xf7, 0xf6, 0x3b, 0xca, 0xb6, 0xef, 0xab, 0x55, 0xfe, 0x04, + 0xcc, 0xc5, 0x11, 0x13, 0xd9, 0x88, 0xd8, 0x84, 0xb9, 0xb9, 0x60, 0xe6, 0x42, 0x70, 0x81, 0x81, + 0x1d, 0xf6, 0x83, 0x01, 0xeb, 0xa4, 0x1b, 0x4e, 0x83, 0xbc, 0x17, 0x38, 0xaf, 0x2d, 0x87, 0xa6, + 0xb6, 0x1f, 0x13, 0xec, 0x78, 0x6c, 0xff, 0x66, 0x6b, 0xc8, 0xe4, 0x48, 0x37, 0x59, 0x14, 0x83, + 0xf4, 0x3b, 0x7d, 0x56, 0x4c, 0x69, 0x99, 0xeb, 0x72, 0xd5, 0x86, 0x91, 0xfa, 0xb1, 0x9d, 0x8b, + 0xa4, 0x26, 0x93, 0x6d, 0x8c, 0x22, 0xd6, 0x74, 0x76, 0x0a, 0x4b, 0xc7, 0x22, 0xe3, 0xa0, 0x15, + 0xce, 0xd8, 0x60, 0x2d, 0xe5, 0xfd, 0x0f, 0xe4, 0xc2, 0x1e, 0x61, 0xb9, 0xb0, 0x1e, 0xfb, 0xa5, + 0x27, 0x4d, 0xe7, 0xfb, 0xe6, 0xed, 0x74, 0xd3, 0x5d, 0xc8, 0xa7, 0x7b, 0x7d, 0x23, 0xa3, 0x31, + 0xaa, 0xb0, 0x48, 0xec, 0x28, 0x42, 0x30, 0xd1, 0xe2, 0x41, 0x0a, 0x43, 0x08, 0x84, 0x6c, 0xac, + 0xeb, 0x7f, 0xc1, 0xf8, 0xdc, 0xe8, 0x44, 0x3c, 0x18, 0x62, 0xc8, 0x94, 0xf6, 0x8e, 0xf7, 0x3e, + 0xa0, 0x6d, 0x5d, 0x2a, 0x9b, 0x16, 0x56, 0x45, 0x25, 0x6f, 0x93, 0xd3, 0x86, 0x93, 0xd3, 0x36, + 0x60, 0xa7, 0xad, 0xad, 0x57, 0xf9, 0x74, 0x3e, 0x7e, 0x6f, 0x63, 0x6c, 0xa8, 0x6a, 0x6b, 0x0c, + 0x9d, 0x2d, 0x69, 0xb0, 0xf6, 0xcc, 0x06, 0x19, 0x52, 0xdb, 0xcc, 0x34, 0x7a, 0xf8, 0x83, 0x1a, + 0x35, 0xd3, 0x46, 0x19, 0xbe, 0x5a, 0xd2, 0x68, 0xe3, 0x99, 0x8d, 0x32, 0xb4, 0xa5, 0x8e, 0xb4, + 0x5e, 0x3b, 0xfc, 0x41, 0x8d, 0x8a, 0x91, 0xfe, 0x00, 0x90, 0xbe, 0xcf, 0x41, 0xf4, 0x07, 0x00, + 0xf4, 0x7d, 0x0e, 0x9e, 0x3f, 0x00, 0x9c, 0xef, 0x73, 0xd0, 0x7c, 0x1f, 0xd0, 0x5a, 0x83, 0x8d, + 0x92, 0x99, 0x7f, 0x64, 0x9b, 0xd4, 0x8d, 0x9f, 0x7e, 0x32, 0x5b, 0x4f, 0x0b, 0x69, 0xed, 0x7c, + 0x92, 0x7c, 0xd1, 0x0d, 0x6f, 0xb5, 0xeb, 0xed, 0x76, 0xab, 0xca, 0xe3, 0x3c, 0x81, 0x9f, 0x41, + 0xd9, 0x36, 0x5b, 0x3b, 0x3b, 0x3b, 0x35, 0xb3, 0x05, 0xd1, 0x82, 0x63, 0xf4, 0xa7, 0xfd, 0x8a, + 0xe3, 0xdf, 0x3f, 0x25, 0xfe, 0x15, 0x6c, 0xdc, 0xa5, 0x0c, 0x41, 0xd0, 0x0d, 0xc7, 0x13, 0x27, + 0xf0, 0xc3, 0xe0, 0x9c, 0x84, 0x34, 0x74, 0xc3, 0xd1, 0x27, 0xcc, 0xbc, 0x96, 0xec, 0x7a, 0x47, + 0x16, 0xc5, 0xc4, 0x77, 0x46, 0x6f, 0x88, 0x33, 0xc6, 0x57, 0xb3, 0x09, 0x8e, 0xec, 0xf9, 0xfb, + 0xc0, 0x0d, 0xc7, 0x7e, 0x30, 0xb0, 0x5a, 0x35, 0x74, 0x36, 0xa5, 0x83, 0x90, 0xfd, 0xae, 0xc6, + 0xb2, 0xc6, 0xe1, 0x08, 0xdb, 0xf3, 0x4b, 0x4c, 0xee, 0x7d, 0x17, 0x7f, 0x9c, 0xfa, 0x9e, 0xa5, + 0xb5, 0x8e, 0x1b, 0x4c, 0x5e, 0x5e, 0x3e, 0x6c, 0x1e, 0xd4, 0xcb, 0x6f, 0xea, 0xbb, 0xf5, 0xf2, + 0x71, 0xf5, 0x60, 0xb7, 0x7c, 0xdc, 0xac, 0x1e, 0xd7, 0x1a, 0x47, 0xdd, 0xee, 0xc1, 0xee, 0xb1, + 0x86, 0xba, 0x43, 0x07, 0x5c, 0x91, 0x30, 0x50, 0xfd, 0xbe, 0x0b, 0x35, 0x2f, 0x1e, 0x93, 0xba, + 0xb5, 0x97, 0xd6, 0xbd, 0x4a, 0xeb, 0xd6, 0x57, 0xd6, 0x4d, 0xc6, 0x0d, 0x6b, 0x28, 0xa6, 0x78, + 0x84, 0xef, 0x2d, 0x66, 0xaa, 0x9f, 0x64, 0x5e, 0x52, 0x87, 0x46, 0x22, 0xb7, 0x1b, 0x12, 0x6c, + 0x55, 0xd1, 0x85, 0xe3, 0xf9, 0xa1, 0x65, 0xa2, 0x73, 0xc7, 0xbd, 0xc3, 0x34, 0xb2, 0x6a, 0x49, + 0xe1, 0x6e, 0x38, 0x1e, 0x3b, 0x81, 0xd7, 0x0d, 0x3d, 0x28, 0x7e, 0x30, 0x99, 0xb0, 0x27, 0x87, + 0x2d, 0x13, 0x5d, 0xe2, 0xc0, 0xbb, 0x7a, 0xa4, 0x27, 0xd1, 0xc0, 0xaa, 0xb1, 0x8f, 0xee, 0xd0, + 0x09, 0x02, 0x3c, 0x12, 0x69, 0x75, 0xf4, 0x16, 0x53, 0xd0, 0xf5, 0x3b, 0x2e, 0x8d, 0xac, 0x06, + 0x7c, 0x1d, 0x61, 0x00, 0xe3, 0x95, 0x3f, 0xc6, 0x56, 0x13, 0x5d, 0x66, 0xbe, 0x5b, 0xac, 0x85, + 0x4b, 0x3c, 0xea, 0x1f, 0x78, 0xec, 0x49, 0xc6, 0x1d, 0x28, 0xc0, 0x7f, 0x9f, 0x3a, 0x63, 0x6c, + 0xb5, 0xd1, 0x81, 0xe7, 0x7d, 0x64, 0x46, 0xfe, 0xa2, 0x51, 0x6b, 0x17, 0x5d, 0xce, 0x02, 0xf7, + 0x14, 0x3f, 0xd2, 0x13, 0xee, 0x5d, 0x60, 0x99, 0x55, 0xa8, 0xc5, 0xe6, 0x72, 0xce, 0x83, 0x28, + 0x9a, 0x30, 0x4e, 0x7a, 0xf5, 0x78, 0x1e, 0x3e, 0x60, 0x62, 0x99, 0x35, 0x74, 0x81, 0x23, 0x4c, + 0xcf, 0x1d, 0x3a, 0xb4, 0xcc, 0x7a, 0xda, 0xc5, 0x07, 0x87, 0x7e, 0x08, 0x03, 0xcb, 0x6c, 0xa0, + 0x0b, 0x3c, 0x0e, 0xef, 0x93, 0x2e, 0xcc, 0x26, 0x62, 0x46, 0x57, 0xc9, 0x77, 0x0b, 0x1d, 0x3f, + 0xc2, 0x6e, 0x4b, 0x12, 0x76, 0xd0, 0xfb, 0x71, 0x26, 0xa1, 0x8d, 0x2e, 0xf0, 0x2d, 0xbc, 0x8a, + 0x67, 0xee, 0xc2, 0x9c, 0x0f, 0x1d, 0x4a, 0x31, 0x99, 0x7d, 0x0a, 0x47, 0x14, 0x46, 0x58, 0x63, + 0x23, 0xbc, 0x9a, 0xc2, 0xc5, 0x25, 0x86, 0x58, 0x33, 0x11, 0x07, 0xc4, 0xaf, 0x53, 0x30, 0x2f, + 0xaa, 0xd5, 0x44, 0x17, 0x8a, 0x01, 0x56, 0xad, 0x2e, 0x7a, 0x51, 0xd3, 0x1a, 0x0c, 0x64, 0x17, + 0xce, 0x03, 0xac, 0xb5, 0x55, 0x6b, 0xb2, 0xcf, 0x0f, 0xe1, 0xc0, 0x0f, 0xac, 0x9a, 0x00, 0x27, + 0x75, 0xe8, 0x34, 0xba, 0xc0, 0x7f, 0x5a, 0xb5, 0x1d, 0xb6, 0x1a, 0x7c, 0x81, 0xac, 0x3a, 0x03, + 0x4a, 0xf2, 0x55, 0x43, 0x97, 0xfe, 0x20, 0xe0, 0xcb, 0x5a, 0xaf, 0xb3, 0x0f, 0xd6, 0x64, 0xbd, + 0xc1, 0x7e, 0xbf, 0xf1, 0x03, 0x3f, 0x1a, 0x5a, 0x75, 0xde, 0xc1, 0x15, 0x71, 0x5c, 0xcc, 0xe0, + 0x57, 0x87, 0x4e, 0xe8, 0x19, 0x1d, 0x62, 0x22, 0x66, 0x52, 0x6f, 0xf3, 0x22, 0x2c, 0x36, 0x34, + 0x25, 0x33, 0xe8, 0xb9, 0xbe, 0xcb, 0xd2, 0x0e, 0xfd, 0xc0, 0xe1, 0x09, 0x4d, 0x06, 0x81, 0x37, + 0xa3, 0x30, 0xf4, 0x2e, 0xdd, 0x70, 0x82, 0xad, 0x26, 0xdb, 0x1b, 0x6c, 0x4f, 0x82, 0x3d, 0xad, + 0xb2, 0x93, 0xd8, 0x28, 0x5a, 0xe9, 0x56, 0xbc, 0xc0, 0xd1, 0x24, 0x0c, 0x22, 0x2c, 0xf6, 0xe2, + 0xd9, 0x9d, 0x55, 0x45, 0xc7, 0x84, 0x58, 0x26, 0x92, 0xfb, 0x8c, 0x4f, 0xa2, 0x26, 0xbf, 0xad, + 0x3a, 0x3a, 0x0e, 0xbc, 0xb3, 0xbe, 0xb2, 0x0d, 0x61, 0x8b, 0xbd, 0x0f, 0xfa, 0x21, 0xdb, 0x81, + 0x01, 0xb5, 0x5a, 0xb2, 0xec, 0x49, 0x34, 0xb8, 0xc0, 0xee, 0xbd, 0xb5, 0x83, 0x44, 0xef, 0x32, + 0xa1, 0x8d, 0xba, 0x53, 0x42, 0xd8, 0x2e, 0xdd, 0x45, 0xa7, 0xe1, 0x49, 0x48, 0xb0, 0xd8, 0x6f, + 0x11, 0x6c, 0xb8, 0xdc, 0x7e, 0x30, 0x51, 0x6e, 0xc5, 0xcd, 0x9a, 0x58, 0x5e, 0xd6, 0xab, 0x59, + 0x47, 0xca, 0x1a, 0x9a, 0x0d, 0x74, 0xe4, 0x47, 0xce, 0x2d, 0xc4, 0xa1, 0x30, 0x9b, 0xb9, 0x91, + 0x7c, 0xaa, 0xc3, 0x76, 0xcb, 0x0e, 0x06, 0xd2, 0x92, 0x01, 0xf2, 0x06, 0xdb, 0xca, 0xea, 0x99, + 0xbb, 0xec, 0x43, 0x04, 0x22, 0xa8, 0x22, 0x0e, 0xd4, 0x5a, 0x03, 0x29, 0x00, 0x65, 0x73, 0xaa, + 0xed, 0x24, 0x40, 0x3d, 0x9f, 0x46, 0x43, 0x79, 0xb8, 0xf9, 0xd1, 0x03, 0x4d, 0x01, 0xac, 0x30, + 0x3f, 0x6d, 0x9e, 0x65, 0xd6, 0xf8, 0x1a, 0x76, 0xc3, 0xa0, 0xef, 0x93, 0x31, 0xa4, 0xd4, 0xab, + 0xe8, 0x24, 0x1a, 0x7c, 0x76, 0x7c, 0x0a, 0xf8, 0xd2, 0xac, 0x9b, 0x48, 0xee, 0x43, 0xb3, 0x5e, + 0x43, 0x6c, 0x13, 0x5e, 0x4e, 0x5d, 0x17, 0x47, 0x91, 0x65, 0xd6, 0xeb, 0x3c, 0xe1, 0x8d, 0xe3, + 0x8f, 0x2c, 0x13, 0x36, 0x95, 0xd8, 0x96, 0x7c, 0x31, 0x2d, 0xb3, 0xde, 0x84, 0x02, 0x17, 0x8f, + 0xa2, 0x7e, 0x0b, 0xb1, 0x3d, 0x26, 0xbe, 0x76, 0xd0, 0x29, 0x7e, 0x90, 0x03, 0xab, 0xb7, 0x91, + 0xb2, 0xb9, 0x92, 0xfa, 0xbb, 0x48, 0x6e, 0x2f, 0x99, 0xd4, 0x48, 0x31, 0x38, 0x93, 0x3b, 0x8a, + 0x09, 0x7e, 0x4c, 0x5f, 0xef, 0xeb, 0x8e, 0x3d, 0xcb, 0x44, 0xa7, 0x21, 0x7d, 0x13, 0x4e, 0x03, + 0xcf, 0xaa, 0xa1, 0x2b, 0x58, 0x86, 0x37, 0xd3, 0xd1, 0xc8, 0xaa, 0xa3, 0x43, 0xf6, 0xb0, 0x3a, + 0xc5, 0x56, 0x03, 0xbd, 0xf1, 0x47, 0xf8, 0x7d, 0xc8, 0x1a, 0xb1, 0x9a, 0xe8, 0xfd, 0x68, 0x84, + 0x07, 0xce, 0xe8, 0x80, 0x0c, 0xac, 0x56, 0xd2, 0xc3, 0x81, 0x77, 0x0f, 0xc8, 0xd4, 0x9e, 0x9f, + 0x86, 0x01, 0xe0, 0xd2, 0xee, 0xd0, 0x01, 0xfc, 0x78, 0x81, 0x27, 0x20, 0x70, 0x21, 0x56, 0x0d, + 0x5d, 0x84, 0xe1, 0xd8, 0xaa, 0xa7, 0xf8, 0x37, 0x41, 0x73, 0x02, 0x09, 0x43, 0x94, 0xe6, 0x77, + 0xe1, 0xc4, 0xaa, 0x22, 0x76, 0x2e, 0x2c, 0x33, 0x29, 0x7a, 0xf5, 0x28, 0xcb, 0x9c, 0x8f, 0x40, + 0x79, 0x56, 0x45, 0xdd, 0x91, 0xcf, 0x41, 0xc3, 0x96, 0x1a, 0x7b, 0x3c, 0x3d, 0x3d, 0x24, 0xc9, + 0x49, 0x9b, 0xe2, 0x48, 0xd6, 0x7d, 0x8b, 0x69, 0x02, 0x37, 0x7e, 0xb4, 0xe1, 0xc8, 0x1d, 0xdc, + 0x0f, 0x4e, 0xfc, 0xe0, 0xc4, 0x79, 0xe4, 0xd8, 0xf9, 0x80, 0xad, 0xd7, 0x07, 0x3f, 0xa2, 0x56, + 0x13, 0xbe, 0x4f, 0xb1, 0x3f, 0x18, 0xde, 0x86, 0x53, 0x12, 0x59, 0xad, 0x38, 0x46, 0x8e, 0xfd, + 0x27, 0xbf, 0x7f, 0x3f, 0xad, 0x60, 0xdf, 0xf1, 0x3d, 0x0e, 0x28, 0x34, 0x01, 0xa1, 0x6b, 0xa3, + 0x13, 0x67, 0xc2, 0x58, 0x9f, 0x13, 0x67, 0x12, 0x87, 0x81, 0x12, 0x67, 0x7d, 0xa1, 0x18, 0x48, + 0x09, 0x74, 0x10, 0x97, 0x2f, 0xc9, 0xe6, 0x51, 0x90, 0xaf, 0x6f, 0x04, 0xf7, 0xb9, 0x58, 0x00, + 0x02, 0x4e, 0x63, 0x83, 0x13, 0xe2, 0x91, 0x11, 0x87, 0xfd, 0xbe, 0x9e, 0xf8, 0xf2, 0xac, 0xea, + 0x31, 0x79, 0x3d, 0x79, 0x65, 0xb3, 0x7d, 0x7f, 0x44, 0x31, 0xd1, 0x43, 0x7b, 0x8f, 0xbf, 0x15, + 0xdf, 0x59, 0x39, 0x4c, 0x60, 0x17, 0xc3, 0xc0, 0xc5, 0x2a, 0x37, 0x0b, 0xde, 0x7d, 0x21, 0x7f, + 0x11, 0xda, 0x8f, 0x2a, 0x7c, 0x78, 0x10, 0x06, 0x1a, 0x53, 0x40, 0x2c, 0xe1, 0x94, 0x32, 0x0d, + 0x68, 0xc4, 0x8b, 0xa1, 0xaa, 0x11, 0xf3, 0x2e, 0x18, 0xd4, 0x02, 0x23, 0xc6, 0x63, 0x50, 0x7c, + 0x80, 0xf0, 0xe2, 0x39, 0x73, 0x4a, 0xdc, 0x5b, 0x81, 0xb9, 0x59, 0x39, 0x33, 0x23, 0x37, 0x80, + 0x40, 0x67, 0x5d, 0xc0, 0x00, 0x62, 0x44, 0x03, 0xfb, 0x13, 0x5f, 0xf2, 0xdf, 0x73, 0x24, 0x17, + 0x7b, 0xfd, 0xea, 0x2a, 0xcc, 0x4a, 0xde, 0x15, 0x2f, 0x5f, 0x6c, 0xb0, 0xa7, 0x81, 0x22, 0x7b, + 0x2f, 0x4a, 0x5d, 0xd7, 0xcc, 0x96, 0x51, 0x99, 0xb0, 0x83, 0x45, 0xa8, 0x5e, 0x43, 0x5a, 0x15, + 0x7c, 0x0b, 0xff, 0x08, 0xfd, 0x40, 0xd7, 0xb4, 0x54, 0x85, 0x83, 0x1f, 0xaf, 0xc2, 0x84, 0x63, + 0x5d, 0xf2, 0xac, 0x3d, 0x3c, 0x3c, 0x44, 0xdd, 0xa1, 0xfe, 0xba, 0x32, 0x37, 0x51, 0x2d, 0x7e, + 0x3d, 0x48, 0xba, 0x9b, 0x38, 0x24, 0x02, 0x59, 0x87, 0x1e, 0x21, 0xb3, 0x65, 0xa4, 0xa2, 0xfe, + 0x5b, 0x27, 0xc2, 0xad, 0xc6, 0xfa, 0x96, 0x1d, 0x1a, 0xde, 0xb2, 0xa7, 0x9c, 0x61, 0xe4, 0xee, + 0xd0, 0x61, 0x88, 0xe3, 0x00, 0xcc, 0x3c, 0x92, 0xa6, 0x1c, 0x82, 0xb9, 0x08, 0x22, 0x3a, 0x06, + 0x09, 0xb3, 0x9e, 0x37, 0x7a, 0x84, 0x0d, 0x22, 0x1d, 0x8d, 0x93, 0xa8, 0x35, 0x59, 0x87, 0x63, + 0xac, 0x3a, 0x1c, 0x43, 0xd5, 0xeb, 0xe0, 0x06, 0xaa, 0x5d, 0x07, 0x37, 0x69, 0x95, 0xc4, 0xc9, + 0x3e, 0x46, 0x97, 0xd8, 0xfe, 0x9d, 0x2f, 0xc4, 0x6d, 0x24, 0xd5, 0x76, 0x72, 0x29, 0x0e, 0x8e, + 0x3e, 0xf5, 0xae, 0x7e, 0x3f, 0x3f, 0xee, 0x9d, 0x9e, 0x9d, 0x1e, 0xdb, 0xd5, 0x4e, 0x3e, 0xb9, + 0xfb, 0xee, 0xe0, 0xca, 0x36, 0x17, 0x92, 0x2f, 0x8e, 0xcf, 0x8f, 0x0f, 0xae, 0x8e, 0x2f, 0xec, + 0xda, 0x62, 0xd6, 0xd9, 0xd9, 0x49, 0x4a, 0x32, 0x27, 0xc9, 0x97, 0xc7, 0xa7, 0x97, 0x67, 0x17, + 0x76, 0x43, 0xcd, 0xf8, 0x70, 0x70, 0xf5, 0xe1, 0xec, 0xb4, 0x07, 0x8f, 0x06, 0xda, 0x66, 0x4b, + 0xcd, 0x79, 0x73, 0x7c, 0x70, 0x65, 0xf2, 0x8c, 0x7a, 0x2d, 0x9f, 0x51, 0xe3, 0x19, 0xad, 0x4c, + 0x5b, 0xa7, 0x07, 0x27, 0xc7, 0xa2, 0xa5, 0x5a, 0xbb, 0x93, 0x7b, 0x9f, 0x81, 0x7b, 0xbe, 0x72, + 0x21, 0x84, 0x34, 0x73, 0xb7, 0x45, 0x90, 0x7c, 0xea, 0x8f, 0x71, 0x44, 0x9d, 0xf1, 0xc4, 0x16, + 0xb1, 0xd3, 0x93, 0x58, 0x3c, 0xb6, 0x78, 0xa7, 0xc1, 0x99, 0x4c, 0x00, 0xeb, 0xd9, 0x22, 0x08, + 0x3c, 0xdb, 0x24, 0x9e, 0x9d, 0xfe, 0x3e, 0xe0, 0xf9, 0x7a, 0xb1, 0x3e, 0x30, 0x95, 0x97, 0xfd, + 0xc9, 0x8d, 0xaa, 0x54, 0xce, 0xa9, 0x5e, 0x63, 0x2f, 0x57, 0x57, 0xb2, 0x4c, 0x2b, 0x8b, 0x5e, + 0x9e, 0x16, 0x6a, 0x35, 0x40, 0x4f, 0x58, 0x2c, 0xb6, 0xc8, 0xa9, 0xbb, 0x10, 0x7b, 0x66, 0x93, + 0xc9, 0x87, 0xde, 0x8c, 0x9c, 0x41, 0x5e, 0xa6, 0x22, 0x66, 0x02, 0xa2, 0x9d, 0x01, 0x66, 0xd8, + 0x3d, 0x57, 0x20, 0xad, 0xb7, 0x65, 0x36, 0x65, 0x99, 0x44, 0xb8, 0xa2, 0x30, 0x7f, 0x49, 0xf5, + 0xf4, 0xcd, 0x0e, 0xdb, 0xa6, 0x95, 0xcc, 0x76, 0xda, 0xd7, 0xe0, 0xaf, 0x66, 0xe5, 0xb2, 0x60, + 0x4b, 0xed, 0x6b, 0xf0, 0x77, 0x21, 0x4b, 0x6e, 0xab, 0x7d, 0x4d, 0xfe, 0x5a, 0x2c, 0x72, 0x76, + 0x76, 0xb2, 0xaf, 0xc1, 0xdf, 0x85, 0x2c, 0xbe, 0xc5, 0xf6, 0x35, 0xfe, 0xaf, 0x66, 0x41, 0xac, + 0xf0, 0xd8, 0x89, 0x66, 0x81, 0xbb, 0xe1, 0x47, 0x9f, 0x20, 0xde, 0x8c, 0x8f, 0x3d, 0xa1, 0x88, + 0x93, 0x16, 0xfe, 0x38, 0xb6, 0x9d, 0x07, 0xc7, 0xa7, 0x1b, 0xe7, 0x24, 0x1c, 0xfb, 0x11, 0xbc, + 0x47, 0x1b, 0x85, 0x23, 0xf0, 0xc8, 0xac, 0xd0, 0x21, 0x0e, 0x18, 0x56, 0xd3, 0xfd, 0x50, 0x37, + 0x90, 0x13, 0x32, 0xd3, 0x38, 0x00, 0xf4, 0xfb, 0x44, 0x2d, 0xa2, 0x8a, 0xf9, 0xb2, 0xdb, 0xcb, + 0x40, 0x22, 0x33, 0x59, 0xd6, 0xec, 0x6e, 0x4b, 0xf2, 0x95, 0xca, 0x62, 0x79, 0xc0, 0x1b, 0x9b, + 0x87, 0xc7, 0xd1, 0xb3, 0x1b, 0x12, 0x29, 0x91, 0x17, 0x50, 0xae, 0xbb, 0x38, 0xbb, 0x15, 0xc5, + 0x5a, 0xf1, 0x6d, 0x97, 0x6d, 0x3c, 0x62, 0xef, 0xf7, 0x26, 0x1c, 0x34, 0xf3, 0xf0, 0x63, 0x71, + 0xd5, 0x41, 0x20, 0x3a, 0x1d, 0x8d, 0x3a, 0xd1, 0x16, 0x07, 0xaa, 0x72, 0x3c, 0xd9, 0x6b, 0x23, + 0xbc, 0x5a, 0xba, 0x4b, 0xc3, 0x7c, 0x0a, 0x6f, 0x8c, 0x64, 0x5b, 0x49, 0x8f, 0x32, 0x7b, 0xd2, + 0x14, 0x57, 0xb2, 0xa2, 0x24, 0x5e, 0xc7, 0x5f, 0xac, 0x53, 0x93, 0x75, 0xfc, 0x25, 0x75, 0x3c, + 0x5e, 0x47, 0x2e, 0x85, 0xa8, 0x9a, 0xe0, 0x01, 0x78, 0xc5, 0x4c, 0xd4, 0x94, 0x1b, 0xd8, 0x40, + 0x73, 0x66, 0x06, 0xa4, 0xee, 0x60, 0x99, 0x87, 0x46, 0x0e, 0xb5, 0x02, 0x34, 0x0a, 0x21, 0xe4, + 0x40, 0x00, 0x2c, 0xa3, 0x87, 0xfa, 0xd8, 0xa1, 0xa6, 0x45, 0xd8, 0xbf, 0x35, 0xb0, 0x57, 0x8f, + 0x91, 0x1b, 0xda, 0xb7, 0x11, 0xeb, 0xfe, 0x38, 0xfa, 0xdb, 0x2f, 0xb5, 0x18, 0x8d, 0x42, 0xfb, + 0x98, 0x77, 0x77, 0x91, 0xa2, 0xee, 0x05, 0xe4, 0x26, 0x51, 0x9b, 0x43, 0x87, 0x60, 0x9e, 0x00, + 0xa1, 0x3a, 0x25, 0x76, 0x93, 0x69, 0x4c, 0x5a, 0x2a, 0x31, 0x1c, 0x24, 0xbe, 0xa7, 0x78, 0x1c, + 0xd9, 0x81, 0x8a, 0xb2, 0x80, 0x68, 0x3f, 0x08, 0xc4, 0x23, 0xad, 0xc9, 0xc5, 0x04, 0x6f, 0x51, + 0x83, 0x21, 0xbe, 0x44, 0x34, 0x00, 0x73, 0x7e, 0x17, 0xbd, 0xc2, 0x15, 0xfc, 0xc8, 0x42, 0xa6, + 0x9c, 0x2b, 0x3d, 0xeb, 0xdc, 0x27, 0x61, 0x21, 0x8f, 0xcb, 0x6b, 0x99, 0x8f, 0x42, 0x58, 0x82, + 0x38, 0x0e, 0x58, 0x35, 0x3c, 0xe5, 0x2f, 0x8a, 0xf8, 0xe2, 0x66, 0xfb, 0x89, 0x2c, 0x74, 0xe7, + 0x81, 0x28, 0xd6, 0x15, 0x1b, 0xda, 0x4f, 0xc5, 0xf3, 0x53, 0xbb, 0x9a, 0xbe, 0x30, 0xc3, 0x09, + 0x38, 0x57, 0xc1, 0x9d, 0x81, 0x51, 0x80, 0x22, 0x3d, 0x11, 0xd4, 0x06, 0x86, 0x56, 0x18, 0xb9, + 0x26, 0x01, 0x10, 0x5b, 0x32, 0x6c, 0xef, 0x8d, 0xc2, 0x4a, 0x66, 0x7d, 0xe5, 0x22, 0x21, 0xb6, + 0x4a, 0xfd, 0xd0, 0xbe, 0xe0, 0xab, 0x74, 0xb9, 0x70, 0xc1, 0x9e, 0xbf, 0xeb, 0x5d, 0x9c, 0x7d, + 0xbc, 0x12, 0x77, 0x53, 0x72, 0x2f, 0x9e, 0xbf, 0x13, 0x38, 0xeb, 0xdd, 0xfb, 0x37, 0x57, 0xe9, + 0x2d, 0x2a, 0x93, 0xf9, 0x45, 0xd6, 0x54, 0x92, 0x3f, 0x1d, 0x5f, 0x88, 0xc2, 0xad, 0x5c, 0x6a, + 0xb6, 0x61, 0xde, 0x19, 0x6b, 0xe4, 0xea, 0xe2, 0xe0, 0xf4, 0xf2, 0xfc, 0xec, 0xe2, 0xaa, 0xf7, + 0xe6, 0xc3, 0xd9, 0xd9, 0x51, 0x7a, 0xbb, 0x2b, 0x65, 0x78, 0x8e, 0x59, 0x90, 0x73, 0xf4, 0xfe, + 0xe2, 0xb8, 0xab, 0x0c, 0xad, 0xb0, 0x61, 0x51, 0x28, 0x9d, 0xd6, 0xc1, 0xef, 0x1f, 0xce, 0x0e, + 0x8e, 0x24, 0x36, 0xff, 0x35, 0xed, 0x33, 0x97, 0x73, 0x79, 0x7e, 0x76, 0x7a, 0x79, 0x9c, 0x76, + 0x9c, 0xc9, 0xbe, 0xfa, 0xed, 0xaa, 0x77, 0x72, 0xf9, 0x56, 0x01, 0x8b, 0x9a, 0x7b, 0xd0, 0xfd, + 0x65, 0x49, 0x87, 0x07, 0x47, 0x9f, 0x8e, 0x2f, 0xae, 0x52, 0x1a, 0x23, 0x93, 0xf9, 0xf6, 0xe2, + 0x1c, 0x1a, 0xb6, 0x9b, 0x4b, 0x73, 0x8f, 0x0e, 0xae, 0x0e, 0x14, 0xe8, 0x66, 0x5a, 0x3e, 0x3d, + 0x3b, 0x65, 0xf3, 0xd9, 0x29, 0xcc, 0x3e, 0x3f, 0xb8, 0x7a, 0x67, 0xb7, 0x8b, 0xe7, 0x72, 0x71, + 0xd0, 0x3d, 0xb6, 0x77, 0x8b, 0xc1, 0x70, 0xf0, 0xb9, 0xd7, 0xfd, 0x78, 0x79, 0x75, 0x76, 0x02, + 0x6b, 0x5d, 0x40, 0xb3, 0xb0, 0xcb, 0x9c, 0x1f, 0x6e, 0x08, 0xf7, 0x89, 0x89, 0x7a, 0xac, 0x3f, + 0xe0, 0x40, 0x3d, 0xd0, 0x92, 0x5a, 0x99, 0x38, 0xb3, 0x51, 0xe8, 0x78, 0x92, 0x5a, 0xa1, 0xc4, + 0x09, 0x22, 0x2e, 0x62, 0xf0, 0xb0, 0x69, 0x93, 0x82, 0xd4, 0x1a, 0xbc, 0xd6, 0xce, 0xe4, 0xaa, + 0xe1, 0x94, 0xe2, 0x1e, 0xa0, 0xc8, 0xe4, 0x92, 0xbf, 0x80, 0x24, 0x7e, 0xd3, 0xe7, 0xcb, 0xf4, + 0x78, 0x48, 0x98, 0xc5, 0xa2, 0x09, 0x4a, 0x55, 0xc7, 0x93, 0x6d, 0xf6, 0x9c, 0x27, 0xaa, 0x0d, + 0xab, 0xe5, 0xf2, 0x4d, 0x2b, 0xc5, 0x8b, 0x1b, 0xbf, 0x17, 0xa2, 0xda, 0x5c, 0x85, 0x4f, 0xa0, + 0x7a, 0x11, 0xa1, 0x9e, 0xa2, 0xde, 0xd8, 0x21, 0x77, 0xd8, 0xeb, 0x79, 0x61, 0x2f, 0x08, 0x69, + 0x8f, 0x60, 0x06, 0x84, 0xb1, 0x4f, 0x6d, 0x51, 0xe0, 0x84, 0xe5, 0x1f, 0x85, 0xa7, 0x21, 0xbd, + 0x48, 0x32, 0x5f, 0x4e, 0xd5, 0xb1, 0x9b, 0x31, 0xd8, 0xa2, 0x95, 0xcc, 0xd1, 0x07, 0xb4, 0xc7, + 0xc8, 0x95, 0xe5, 0x07, 0xf4, 0xe9, 0x69, 0x45, 0x09, 0x7e, 0xd2, 0x94, 0xcb, 0x12, 0xc9, 0xfb, + 0x8f, 0x5d, 0x91, 0x51, 0xee, 0x8a, 0xe4, 0xb1, 0x7e, 0x73, 0xb7, 0x26, 0xf7, 0x46, 0x4f, 0xd3, + 0xdb, 0x3a, 0x98, 0x11, 0xd1, 0x42, 0x2c, 0xce, 0x4c, 0xc2, 0x68, 0x31, 0x12, 0x77, 0x8d, 0xd2, + 0x14, 0x8d, 0x32, 0x34, 0xea, 0x10, 0x0c, 0x8c, 0x9e, 0x47, 0xa2, 0xba, 0xf0, 0x56, 0x35, 0x62, + 0x0f, 0xfd, 0x08, 0xc8, 0x16, 0xf5, 0x9f, 0xdc, 0xa1, 0x3a, 0xde, 0xdb, 0x6b, 0x19, 0xdb, 0xe6, + 0x92, 0xc2, 0x7c, 0x44, 0xe9, 0x8d, 0x8b, 0xb7, 0x5a, 0x75, 0xf6, 0xbc, 0x94, 0x43, 0x87, 0x29, + 0x56, 0xef, 0x87, 0x95, 0xc5, 0x1b, 0x2e, 0x39, 0x3a, 0x99, 0x33, 0x65, 0xc8, 0xda, 0xc9, 0x50, + 0x14, 0xe3, 0xb0, 0xa2, 0x91, 0x2e, 0xad, 0x9c, 0xd7, 0xc9, 0x16, 0x03, 0x73, 0x69, 0x75, 0x69, + 0x34, 0xb6, 0x81, 0xe1, 0xea, 0x8b, 0x54, 0x5a, 0x0e, 0x8a, 0xa7, 0x57, 0xa0, 0x08, 0x43, 0x95, + 0x6c, 0xfd, 0x4c, 0xef, 0x8c, 0x49, 0xc4, 0x42, 0xb2, 0x51, 0xa0, 0x54, 0xcd, 0xce, 0x54, 0x79, + 0x9d, 0x37, 0xce, 0x9e, 0xfe, 0xcc, 0xfd, 0xc8, 0x91, 0x51, 0x7e, 0x8b, 0xc7, 0x45, 0x48, 0x60, + 0x1e, 0x3d, 0xf8, 0xc0, 0x6c, 0x17, 0x20, 0x14, 0x63, 0xee, 0x3a, 0x10, 0x07, 0xa6, 0x92, 0xbf, + 0x8f, 0x2c, 0xde, 0x97, 0xc6, 0x3e, 0xb4, 0xce, 0x62, 0x29, 0x7e, 0x18, 0x64, 0x31, 0xfe, 0x55, + 0x54, 0x2e, 0x77, 0xc0, 0x64, 0x85, 0x5c, 0xf2, 0xea, 0x9a, 0xd9, 0xbe, 0xf2, 0xe9, 0x5a, 0x47, + 0xc4, 0x46, 0xb1, 0x14, 0x82, 0x25, 0x8e, 0xfd, 0x88, 0xcd, 0x93, 0x89, 0xe3, 0x16, 0x59, 0x2c, + 0x05, 0x06, 0xf9, 0x03, 0xcf, 0x06, 0x24, 0xab, 0x1f, 0xf9, 0x04, 0xbb, 0xf4, 0x45, 0xf5, 0xf9, + 0xa8, 0xe2, 0x3c, 0x8a, 0x2d, 0x58, 0xbd, 0xbd, 0x3d, 0xb6, 0x7c, 0x29, 0x15, 0xb2, 0x95, 0x7e, + 0xcb, 0xd5, 0x2c, 0xc0, 0xbb, 0xf9, 0xf5, 0xcc, 0x74, 0x93, 0xac, 0x68, 0x9e, 0x0e, 0x90, 0xe0, + 0xbb, 0x38, 0xfe, 0x35, 0x81, 0x76, 0x21, 0x45, 0x90, 0x16, 0xe4, 0xdf, 0xc5, 0xa5, 0x05, 0x81, + 0x90, 0x2c, 0x0a, 0xff, 0x2c, 0x2e, 0x7b, 0xd0, 0xfd, 0x45, 0x96, 0x3b, 0xe8, 0xfe, 0xb2, 0xa4, + 0x0c, 0x23, 0x1c, 0x92, 0x62, 0xec, 0xab, 0xb8, 0xa4, 0xa0, 0x22, 0x64, 0x51, 0xf1, 0xb9, 0xbc, + 0x2c, 0xd0, 0x14, 0x6a, 0x61, 0xf8, 0x5e, 0x32, 0x06, 0x41, 0x62, 0x24, 0xa3, 0x10, 0xdf, 0xc5, + 0xa5, 0x81, 0xe2, 0x90, 0x25, 0xe1, 0xf7, 0x12, 0x38, 0x01, 0xf1, 0xa1, 0x6c, 0xdd, 0xee, 0x12, + 0x78, 0xa6, 0x84, 0x48, 0x02, 0xff, 0x24, 0x65, 0xc9, 0x0e, 0xcf, 0x5d, 0xb2, 0xcb, 0x37, 0x58, + 0x42, 0xb8, 0x6e, 0x25, 0x9f, 0x6c, 0x7b, 0xc1, 0x6d, 0xbc, 0x70, 0xd7, 0x66, 0x68, 0x9d, 0x5a, + 0xb3, 0x19, 0x2f, 0xbd, 0x95, 0x0b, 0x3a, 0xe4, 0x8c, 0x0a, 0x67, 0x84, 0xc5, 0xd8, 0xfe, 0xd2, + 0x7e, 0x55, 0x60, 0xbb, 0x91, 0x4a, 0x79, 0x94, 0x8a, 0xfc, 0x6e, 0xe9, 0xac, 0xde, 0xeb, 0xc5, + 0x55, 0x2f, 0xf0, 0x9f, 0x4b, 0x6b, 0x66, 0x4e, 0xc0, 0xb2, 0xea, 0x5c, 0x45, 0xb1, 0xa4, 0x8d, + 0xec, 0xb9, 0x28, 0x6e, 0x82, 0xeb, 0x57, 0x97, 0x34, 0x90, 0x1e, 0x96, 0xe2, 0xca, 0x07, 0xee, + 0xdd, 0xb2, 0x9a, 0xea, 0x11, 0x5a, 0x52, 0x99, 0xe9, 0x2c, 0x96, 0xd5, 0xcf, 0x6e, 0xff, 0x25, + 0x2d, 0x04, 0x61, 0xc0, 0x21, 0x58, 0xb8, 0x27, 0x8b, 0xd7, 0x69, 0x51, 0x14, 0x22, 0xa8, 0xc6, + 0x05, 0x51, 0x88, 0x0c, 0xab, 0x41, 0x5c, 0x4b, 0x4d, 0x47, 0x1e, 0x8e, 0xa8, 0x15, 0x2d, 0xb6, + 0xcf, 0x86, 0xf2, 0xec, 0xe6, 0x51, 0x90, 0xfd, 0x94, 0xe2, 0x93, 0x25, 0xf4, 0x12, 0x1b, 0x46, + 0x20, 0xfa, 0x06, 0xbf, 0x1d, 0x32, 0x9b, 0x80, 0x3a, 0x2d, 0x2c, 0x1a, 0x87, 0xdc, 0x15, 0x7f, + 0xdf, 0x5c, 0xe5, 0xb6, 0xf9, 0xfb, 0x7a, 0x60, 0x7b, 0x4b, 0xbe, 0x86, 0xe8, 0xb8, 0x77, 0x3d, + 0x30, 0x98, 0xb2, 0xd4, 0x2e, 0x0a, 0xea, 0x88, 0x2d, 0x25, 0x46, 0xe5, 0x86, 0x8a, 0xf9, 0x7e, + 0x66, 0x70, 0x49, 0xa8, 0x06, 0x26, 0x30, 0xeb, 0xdd, 0xe1, 0x99, 0x85, 0x95, 0x90, 0xdf, 0x89, + 0x58, 0xce, 0xc2, 0xa9, 0x88, 0x0e, 0x39, 0x93, 0x49, 0xcf, 0x03, 0x7d, 0x17, 0xce, 0x0b, 0x7a, + 0xe3, 0xa5, 0x3b, 0xf3, 0x3b, 0xe1, 0xc3, 0x45, 0xc2, 0x12, 0x44, 0x31, 0x7a, 0x85, 0xed, 0x4b, + 0x2e, 0x5a, 0xb8, 0xcf, 0xc9, 0x9b, 0x80, 0x0e, 0x60, 0x91, 0x56, 0x40, 0x5f, 0xa1, 0xfa, 0x21, + 0x60, 0x25, 0xb2, 0x1f, 0xd3, 0x45, 0x28, 0x0f, 0x96, 0x47, 0x06, 0x52, 0xbe, 0xd8, 0x4f, 0x1e, + 0xae, 0x45, 0x37, 0x4a, 0x7a, 0x54, 0xc6, 0xdb, 0xa6, 0x61, 0x6c, 0x83, 0xb9, 0xcc, 0x61, 0x64, + 0xdf, 0xf3, 0x7e, 0xaf, 0xa4, 0x48, 0x43, 0x3a, 0xfa, 0xd0, 0x60, 0xce, 0x85, 0xaa, 0x61, 0xd0, + 0x0d, 0x83, 0x80, 0x39, 0xd7, 0xe8, 0x3c, 0xdc, 0x3b, 0x17, 0xa5, 0xb2, 0x59, 0x7b, 0xa9, 0x4d, + 0x82, 0xee, 0x54, 0xd6, 0x1a, 0xd9, 0xc8, 0x88, 0xf0, 0x31, 0xd7, 0x37, 0x01, 0x8a, 0x87, 0xa7, + 0x24, 0x02, 0xe9, 0xba, 0x13, 0x06, 0x47, 0x7e, 0xe4, 0xaa, 0xdd, 0xa5, 0xe5, 0x3c, 0x25, 0x47, + 0x33, 0x84, 0xc4, 0xd7, 0x1d, 0x85, 0xec, 0x40, 0x2c, 0x78, 0x90, 0x5c, 0x0d, 0xfd, 0x68, 0x63, + 0x8c, 0xe9, 0x30, 0x4c, 0x43, 0x0d, 0xfa, 0xe3, 0x09, 0x7f, 0x8f, 0x1a, 0x7b, 0x1b, 0xb7, 0xb3, + 0x0d, 0x3a, 0xc4, 0x1b, 0xd1, 0xf4, 0x96, 0x4d, 0xbb, 0x92, 0xb4, 0x18, 0x81, 0x6d, 0x42, 0xc8, + 0xac, 0x43, 0x98, 0x09, 0x10, 0x37, 0xda, 0xfc, 0x91, 0xad, 0x0b, 0x5b, 0x19, 0x69, 0x24, 0x93, + 0xd9, 0x4c, 0xef, 0x3b, 0x38, 0x95, 0x17, 0xeb, 0x4e, 0x45, 0xb5, 0xab, 0xa9, 0xc8, 0x1a, 0x20, + 0x39, 0x4e, 0x0b, 0x99, 0x99, 0xcf, 0x28, 0x6f, 0x7b, 0xd8, 0x32, 0x92, 0x7c, 0x41, 0xdc, 0x69, + 0x14, 0x47, 0x54, 0x33, 0x90, 0xb2, 0x8c, 0x8b, 0x93, 0x56, 0x03, 0x3f, 0x2f, 0x0c, 0x3d, 0x35, + 0xea, 0x49, 0x45, 0x18, 0x32, 0x26, 0x21, 0x9f, 0x85, 0xbf, 0x7c, 0x16, 0x69, 0x65, 0x03, 0xa9, + 0xc5, 0x70, 0xf6, 0x33, 0x4a, 0x3e, 0x13, 0xd1, 0x7a, 0x90, 0x29, 0x11, 0xe9, 0x61, 0x12, 0x07, + 0x15, 0x26, 0xe9, 0x67, 0x26, 0x49, 0x56, 0xcf, 0xcf, 0x5f, 0x3b, 0xbf, 0x8c, 0x9d, 0x52, 0xfe, + 0xc9, 0x5f, 0xa1, 0x1e, 0x58, 0x3d, 0xc9, 0x4c, 0x0b, 0xf0, 0x1a, 0x76, 0x66, 0xae, 0x24, 0x3b, + 0x57, 0xb2, 0x38, 0x57, 0x92, 0x99, 0x50, 0xb8, 0x7a, 0x42, 0x64, 0xe5, 0x84, 0x14, 0x23, 0xab, + 0xac, 0x58, 0xe3, 0x7d, 0x27, 0x5a, 0x3e, 0x09, 0xa5, 0x96, 0x81, 0xf0, 0xd6, 0x56, 0x5e, 0xd5, + 0x81, 0x57, 0x0f, 0x29, 0x5a, 0x37, 0xa4, 0xd4, 0xb2, 0xeb, 0xf9, 0x67, 0x20, 0x53, 0xed, 0xfb, + 0xf6, 0xb0, 0xda, 0xff, 0xf3, 0x81, 0x72, 0x99, 0x1d, 0xc0, 0x0f, 0x85, 0x49, 0xd6, 0xba, 0xed, + 0x45, 0x83, 0x52, 0x2b, 0x66, 0x94, 0x4e, 0xdf, 0x3b, 0x22, 0xc5, 0xbc, 0xee, 0x65, 0x50, 0x4a, + 0xeb, 0x25, 0xe3, 0x49, 0xcc, 0xae, 0xbf, 0x63, 0x44, 0x79, 0x03, 0x3f, 0x55, 0x88, 0x8a, 0x3c, + 0xe4, 0x22, 0xf1, 0x54, 0x79, 0x5f, 0x0c, 0xb2, 0xbf, 0x02, 0x9f, 0xe6, 0x9a, 0x32, 0x50, 0xbf, + 0x92, 0xf1, 0x11, 0x51, 0xbf, 0xe1, 0x90, 0xaa, 0x9f, 0x41, 0xf6, 0x33, 0xcc, 0x55, 0x26, 0xc9, + 0xb7, 0x34, 0x36, 0xf7, 0xc1, 0xad, 0x50, 0x26, 0x26, 0xfb, 0xc5, 0x4b, 0x92, 0x64, 0x8a, 0xbb, + 0x90, 0x32, 0x5d, 0x0d, 0xaf, 0xfe, 0xea, 0x15, 0xcc, 0x5a, 0x3f, 0x3e, 0xff, 0xa4, 0xe5, 0x2a, + 0x7e, 0xe7, 0x59, 0x53, 0xcc, 0x2d, 0xff, 0x02, 0x32, 0x55, 0xab, 0x2f, 0xe2, 0x4a, 0xbc, 0x98, + 0x14, 0x65, 0x11, 0x6c, 0x90, 0xfd, 0xfc, 0x2e, 0x5c, 0x9a, 0x5a, 0x8a, 0xbe, 0xec, 0x3c, 0x88, + 0x4a, 0x3f, 0xf0, 0x70, 0x26, 0x56, 0xaa, 0x2f, 0x18, 0x48, 0x52, 0x27, 0xa7, 0x99, 0xfe, 0x31, + 0x58, 0x82, 0x5b, 0xc8, 0xe6, 0x1d, 0x74, 0xde, 0x77, 0x82, 0x67, 0x60, 0x0a, 0x5e, 0xd7, 0x40, + 0x41, 0x25, 0xe7, 0x8b, 0x94, 0x4f, 0x89, 0x56, 0x0f, 0x35, 0x58, 0x03, 0x33, 0xc5, 0x72, 0xf7, + 0x45, 0x70, 0x53, 0xea, 0xfd, 0x50, 0xd8, 0x29, 0x96, 0xc3, 0x2f, 0xd9, 0x50, 0x4a, 0xb5, 0x1f, + 0x39, 0x9c, 0x8c, 0xa1, 0x2a, 0xc4, 0xf1, 0x9f, 0x8e, 0x46, 0xcf, 0x1e, 0x54, 0xa6, 0xb2, 0x4a, + 0x32, 0xfc, 0x80, 0x81, 0x65, 0x0c, 0xa8, 0x5f, 0x00, 0xa8, 0x4c, 0xbd, 0x1f, 0x09, 0x29, 0x6e, + 0xc0, 0xfd, 0x7c, 0x7c, 0xca, 0xcb, 0x2f, 0x10, 0xe3, 0x84, 0x25, 0x7f, 0x17, 0x39, 0xbe, 0x60, + 0x40, 0xfe, 0x22, 0x72, 0x2a, 0x5b, 0xf5, 0x7b, 0xc6, 0xa1, 0x18, 0xa8, 0xbf, 0x60, 0x81, 0x94, + 0x5a, 0x3f, 0x10, 0x37, 0xe6, 0xad, 0xe3, 0x9f, 0x0f, 0x92, 0x7c, 0xcd, 0xef, 0x81, 0x48, 0xde, + 0x1e, 0xff, 0xc5, 0xfb, 0x56, 0x1d, 0xc6, 0x8f, 0xc4, 0xd7, 0x89, 0x4b, 0xc0, 0x0b, 0x71, 0x75, + 0x52, 0x2f, 0xc1, 0xca, 0x7c, 0xa9, 0xa4, 0x2d, 0xa3, 0x9a, 0x1a, 0x29, 0xb8, 0x5b, 0x3a, 0x81, + 0x7e, 0x07, 0xe6, 0x4e, 0x1c, 0x17, 0x5e, 0x3c, 0x64, 0x56, 0x6b, 0xe9, 0xd0, 0xc4, 0x41, 0xfc, + 0xee, 0xb1, 0x25, 0x7e, 0x14, 0x2f, 0xa5, 0xdb, 0x65, 0xbd, 0x1f, 0xbd, 0xc2, 0xaa, 0x87, 0xc5, + 0x0b, 0xc7, 0xa4, 0x56, 0xcd, 0x1e, 0xca, 0xea, 0x0b, 0x3e, 0x7f, 0xc0, 0x1c, 0x12, 0x3b, 0xf5, + 0x17, 0x2f, 0x7a, 0x52, 0xf3, 0xef, 0xdd, 0x93, 0x8a, 0x8f, 0xca, 0xcb, 0xc8, 0xc1, 0xb4, 0xde, + 0xdf, 0x08, 0x42, 0xe9, 0x32, 0xf3, 0x32, 0xa6, 0x9f, 0x55, 0xf9, 0xa1, 0x4c, 0x64, 0xc6, 0x59, + 0xe7, 0x2f, 0xca, 0x8c, 0x94, 0x16, 0x9e, 0x29, 0x38, 0x92, 0xb6, 0x61, 0xaa, 0x20, 0x89, 0x19, + 0x49, 0x84, 0xb9, 0x22, 0xdf, 0x25, 0x2c, 0x4a, 0x5d, 0xa6, 0x5e, 0x28, 0x5a, 0xe1, 0x95, 0x7e, + 0x2c, 0xb3, 0x9e, 0x8c, 0x84, 0x1b, 0x0c, 0xf2, 0x80, 0xb4, 0x7c, 0x34, 0xe1, 0xca, 0xed, 0x98, + 0x8c, 0x26, 0xcc, 0x8e, 0x26, 0xcc, 0x32, 0xb1, 0x11, 0x63, 0x62, 0xc3, 0x1c, 0x7c, 0x57, 0x0d, + 0x39, 0x5c, 0x3d, 0x64, 0xe9, 0x95, 0xf4, 0x02, 0xbe, 0x54, 0x56, 0xf9, 0x2e, 0x8e, 0x54, 0xb8, + 0xaf, 0xbd, 0xe4, 0xcc, 0x8a, 0x2a, 0x3f, 0x14, 0xc5, 0x25, 0x9e, 0x73, 0x2f, 0x9b, 0x3f, 0xaf, + 0xf3, 0xdd, 0x22, 0x5c, 0xe9, 0xa5, 0xf7, 0xe2, 0xed, 0xa2, 0xd4, 0x4d, 0x76, 0x83, 0xca, 0x8e, + 0x87, 0x8b, 0xec, 0x78, 0x98, 0xc5, 0x71, 0x3f, 0x70, 0x0b, 0x65, 0xdc, 0x0b, 0x5f, 0x86, 0x84, + 0x95, 0x8a, 0x7f, 0xfd, 0x1c, 0xc2, 0x8b, 0x87, 0xce, 0x18, 0x5f, 0x88, 0x97, 0x24, 0xd2, 0x57, + 0x23, 0x99, 0xda, 0x82, 0x40, 0x44, 0x29, 0xf9, 0xc6, 0xdd, 0x12, 0x4b, 0xb2, 0x0e, 0xbc, 0x3f, + 0xe2, 0x54, 0x32, 0x9e, 0x8b, 0x95, 0xb3, 0xbb, 0x7d, 0xe1, 0xdd, 0x73, 0x76, 0x97, 0xa8, 0xfe, + 0x22, 0xc3, 0x2a, 0x2a, 0x7b, 0x4c, 0x88, 0x2c, 0x7c, 0x4c, 0xc8, 0xba, 0xd2, 0xd2, 0xbb, 0x51, + 0x56, 0x91, 0xdf, 0xeb, 0xea, 0x49, 0x0f, 0x47, 0x59, 0x4f, 0x7e, 0xaf, 0xab, 0x97, 0x75, 0x87, + 0x94, 0xb5, 0xb3, 0xa9, 0x6b, 0xfb, 0xce, 0x78, 0x3d, 0x26, 0x23, 0xc8, 0xa4, 0xa6, 0x6d, 0x80, + 0x4b, 0x7b, 0x71, 0x33, 0x19, 0x47, 0xc9, 0xa4, 0x99, 0x4c, 0xea, 0x73, 0x9a, 0x51, 0x3d, 0x49, + 0x73, 0x83, 0xe1, 0x89, 0xcf, 0x9c, 0x4f, 0xae, 0xee, 0xda, 0x75, 0x56, 0xfd, 0x54, 0x93, 0x15, + 0x57, 0x13, 0xd7, 0xaf, 0x7d, 0x40, 0xd3, 0x75, 0x0f, 0xd6, 0xf7, 0xa8, 0xb2, 0xf4, 0x49, 0x8f, + 0x6a, 0xe2, 0xba, 0x16, 0xb2, 0xfc, 0xa5, 0x6c, 0x22, 0x9b, 0xba, 0xae, 0x8d, 0xd4, 0x37, 0x56, + 0xd6, 0x4f, 0x53, 0xd6, 0xd5, 0x4d, 0x59, 0x28, 0x59, 0x37, 0x4d, 0x59, 0xdb, 0xaf, 0xf0, 0xbb, + 0x4d, 0x7a, 0x15, 0xdf, 0x2f, 0xdb, 0xad, 0x9f, 0xea, 0xeb, 0xf7, 0x6b, 0xf5, 0x39, 0xfb, 0x55, + 0x69, 0x68, 0xe9, 0x8e, 0x5d, 0xdd, 0x90, 0x0a, 0x42, 0x25, 0x69, 0xed, 0xae, 0x91, 0x77, 0x6e, + 0xb2, 0x75, 0x64, 0xc2, 0x73, 0x6a, 0x32, 0xaf, 0x16, 0xb5, 0x26, 0x4b, 0x58, 0x5b, 0x13, 0xa8, + 0xd0, 0xa4, 0x16, 0x7c, 0xac, 0x85, 0x7a, 0xd6, 0x7f, 0x39, 0x37, 0x4f, 0x99, 0xbc, 0xd8, 0x4a, + 0xe2, 0xdc, 0x5c, 0xe1, 0x92, 0x48, 0x59, 0x91, 0x7f, 0x9d, 0x73, 0x6f, 0xd1, 0x85, 0xb2, 0x8a, + 0xf3, 0x73, 0xb2, 0xb1, 0xd2, 0xa4, 0x65, 0xb5, 0x32, 0x2e, 0xd2, 0xca, 0x41, 0x4c, 0x13, 0x97, + 0xd5, 0x4c, 0x5d, 0xa9, 0x65, 0xb5, 0x34, 0x65, 0x59, 0x1d, 0xc1, 0xac, 0xcb, 0x0a, 0xe2, 0x73, + 0x59, 0x69, 0xd5, 0x23, 0x5b, 0x56, 0x51, 0xd3, 0x96, 0xce, 0x29, 0xe3, 0xaa, 0xad, 0x2e, 0x59, + 0x9a, 0xba, 0xa2, 0x4f, 0xee, 0xd3, 0xad, 0x74, 0xc8, 0x13, 0x96, 0xd5, 0x58, 0xf0, 0xed, 0x96, + 0x35, 0x17, 0x32, 0x96, 0xb6, 0x20, 0x3d, 0xc7, 0x93, 0x9a, 0x32, 0x61, 0x59, 0x8d, 0xc4, 0xbb, + 0x3c, 0xb9, 0xbc, 0x64, 0xc2, 0xb2, 0x1a, 0x59, 0x5f, 0xf3, 0x04, 0xeb, 0x65, 0x52, 0x65, 0x5d, + 0x70, 0x09, 0x08, 0x47, 0xb8, 0x32, 0x0a, 0x07, 0xfa, 0xd7, 0x69, 0x30, 0x74, 0x02, 0x6f, 0x04, + 0x6f, 0xcd, 0x03, 0x55, 0x61, 0x6d, 0x80, 0x7d, 0x8c, 0xfd, 0x6a, 0x1e, 0xc4, 0x5f, 0x11, 0x06, + 0x62, 0x43, 0xe9, 0x37, 0x43, 0x69, 0x2c, 0xee, 0x63, 0x34, 0x4f, 0x9f, 0x0b, 0xca, 0xd9, 0x9e, + 0xc4, 0xd0, 0x52, 0x7e, 0xbb, 0x2e, 0x6d, 0x4e, 0x29, 0xb8, 0xae, 0xcd, 0xc5, 0xad, 0xbc, 0xb4, + 0xd5, 0x4c, 0x51, 0x04, 0xb6, 0x40, 0x90, 0x6e, 0xe1, 0x05, 0x37, 0x49, 0xf0, 0xbd, 0xbf, 0x22, + 0xfe, 0x64, 0x21, 0x8b, 0xf5, 0x98, 0x3b, 0x05, 0x4b, 0xbb, 0x4b, 0xcb, 0xa1, 0x39, 0xab, 0xa8, + 0x9e, 0x86, 0xa5, 0xb5, 0x44, 0x21, 0x34, 0x1f, 0x39, 0x11, 0xbd, 0x0c, 0x88, 0x95, 0x38, 0xc3, + 0xb5, 0x75, 0xe3, 0x75, 0x03, 0x41, 0xf2, 0x45, 0x14, 0xf9, 0x99, 0x74, 0x44, 0x70, 0x84, 0xc9, + 0x3d, 0xf6, 0xb2, 0x26, 0x51, 0xc2, 0x44, 0xc8, 0x2a, 0x36, 0x08, 0x63, 0x63, 0x5a, 0x38, 0x6e, + 0x4b, 0x07, 0xa6, 0x96, 0x44, 0xf3, 0x25, 0x1d, 0x4e, 0x6f, 0x7f, 0xc1, 0xb3, 0x73, 0x82, 0xfb, + 0xfe, 0x63, 0x66, 0xb5, 0x5a, 0x62, 0xb1, 0x16, 0x8f, 0xe8, 0xf2, 0xd5, 0xca, 0x94, 0xfd, 0x0b, + 0x3d, 0xa2, 0x88, 0xb5, 0x70, 0xc4, 0x2d, 0xad, 0x56, 0x41, 0x40, 0x39, 0xff, 0xab, 0xa6, 0xcf, + 0x8b, 0xbd, 0x74, 0x65, 0x9c, 0x87, 0x55, 0xbd, 0x17, 0xe3, 0x90, 0xa5, 0xa3, 0x58, 0x28, 0xfe, + 0x57, 0xe0, 0x32, 0x9a, 0x4c, 0x2e, 0x71, 0x10, 0x85, 0x64, 0x1d, 0x68, 0x0a, 0xb0, 0xc7, 0xd2, + 0x91, 0x65, 0xcb, 0x2e, 0x1b, 0x16, 0x75, 0x06, 0x05, 0xe7, 0x4d, 0x54, 0x5a, 0x37, 0x9c, 0x2c, + 0xd6, 0x4c, 0x63, 0x99, 0x16, 0x19, 0x43, 0x0a, 0x3f, 0x94, 0xce, 0x12, 0x30, 0xca, 0x96, 0x94, + 0x71, 0x46, 0x48, 0x78, 0x4b, 0x58, 0x01, 0xea, 0x83, 0xb3, 0xb2, 0x95, 0x69, 0xa9, 0x78, 0xe8, + 0xce, 0x94, 0x0e, 0x8b, 0x91, 0xc8, 0x24, 0x71, 0xb7, 0xc8, 0x80, 0x3f, 0xe0, 0x39, 0x97, 0x01, + 0x59, 0x48, 0x2f, 0xde, 0x56, 0x6c, 0xe6, 0x59, 0xec, 0xbf, 0x74, 0x0d, 0x92, 0x62, 0x2b, 0x90, + 0x26, 0x8f, 0x58, 0x9f, 0x81, 0x99, 0x3a, 0x5b, 0x69, 0x44, 0x3a, 0x65, 0x3a, 0x5b, 0x80, 0x46, + 0x66, 0x37, 0x8b, 0xf4, 0xec, 0x8e, 0x6a, 0x18, 0xc8, 0xf1, 0xee, 0x59, 0x3c, 0x24, 0x9e, 0x2e, + 0x85, 0x37, 0xd0, 0x1d, 0xcc, 0x4a, 0x04, 0x4a, 0x59, 0x84, 0x9e, 0x77, 0xff, 0xc1, 0xa1, 0x56, + 0xde, 0x01, 0x18, 0x92, 0xc3, 0x60, 0x21, 0x19, 0x5a, 0x3a, 0x09, 0xbd, 0x62, 0xa4, 0xac, 0xb0, + 0xac, 0x39, 0xf0, 0xe4, 0x99, 0x5c, 0x81, 0x8c, 0x55, 0xb6, 0x55, 0xd9, 0x4a, 0xcb, 0xe2, 0x01, + 0xee, 0x55, 0xf7, 0x55, 0x00, 0x31, 0x27, 0xf0, 0xce, 0xf2, 0x6e, 0x8e, 0x09, 0x41, 0x73, 0x4c, + 0x58, 0xa0, 0x06, 0x78, 0x2c, 0x3a, 0x5e, 0xc6, 0xad, 0xad, 0x1c, 0x6c, 0xa6, 0x0a, 0x9a, 0xbb, + 0x30, 0x94, 0xe2, 0xe9, 0xe7, 0x59, 0xa3, 0xe7, 0x34, 0xfb, 0xbf, 0x6f, 0x93, 0x14, 0x33, 0xae, + 0x2b, 0x61, 0x95, 0xa9, 0x82, 0xe6, 0xe3, 0x30, 0xa2, 0x20, 0x77, 0x09, 0xe8, 0x07, 0x27, 0xa2, + 0xe3, 0x65, 0x1d, 0x65, 0x18, 0xdd, 0x95, 0xed, 0x43, 0x49, 0x86, 0x84, 0xc0, 0xea, 0x3c, 0x03, + 0x45, 0x19, 0x60, 0xfe, 0xc0, 0xbd, 0xeb, 0x26, 0xb6, 0xbc, 0x0a, 0x5c, 0x20, 0x8e, 0x0e, 0x8f, + 0x91, 0xb2, 0x64, 0xb2, 0x85, 0x3c, 0xf3, 0xea, 0xc9, 0xaa, 0x55, 0xe0, 0x29, 0x5d, 0x86, 0x72, + 0x58, 0xec, 0x35, 0xfe, 0x10, 0xc5, 0xaa, 0xbb, 0xa2, 0x98, 0xbf, 0x5e, 0xd9, 0x5f, 0xb6, 0x0e, + 0x9a, 0xdf, 0xf2, 0xef, 0x13, 0x7f, 0x34, 0xf2, 0x21, 0x51, 0x45, 0xc1, 0xdc, 0xd9, 0x90, 0x75, + 0x55, 0xc0, 0x8a, 0xaf, 0xec, 0x26, 0x2d, 0x8f, 0xe6, 0x40, 0xfc, 0x3d, 0x38, 0x04, 0x7f, 0xc2, + 0x64, 0x2d, 0x05, 0xc5, 0x2e, 0x4b, 0x59, 0xa1, 0x77, 0x3b, 0xf5, 0x47, 0x1e, 0xd8, 0x6d, 0xe7, + 0x37, 0xb0, 0x59, 0x33, 0xd0, 0xd8, 0x09, 0xa6, 0xf0, 0x26, 0xf6, 0x94, 0x60, 0x72, 0x12, 0x7a, + 0x78, 0x64, 0x65, 0x43, 0x04, 0x70, 0xf2, 0x77, 0x51, 0x0c, 0xb0, 0x72, 0xdc, 0x69, 0x79, 0x34, + 0x9f, 0xa4, 0xe1, 0xb9, 0x72, 0xc7, 0x2a, 0x16, 0x56, 0xcc, 0x59, 0x39, 0xc1, 0x6a, 0x80, 0x88, + 0xd2, 0x02, 0x15, 0x16, 0xf1, 0xe5, 0xb9, 0xdb, 0x55, 0x5e, 0x82, 0xf2, 0x7a, 0x55, 0x0f, 0x6f, + 0xb8, 0x02, 0x6d, 0x76, 0xc0, 0xf5, 0xd4, 0x6c, 0xed, 0xaf, 0x40, 0x46, 0x69, 0xe7, 0x68, 0xee, + 0x8a, 0x0f, 0xef, 0xd1, 0x8a, 0x78, 0xb4, 0x84, 0x00, 0xf1, 0xd7, 0x48, 0x33, 0xb3, 0x0e, 0x8d, + 0x38, 0xc7, 0xc4, 0x28, 0xad, 0xc0, 0x7b, 0x0b, 0x1b, 0xd3, 0x20, 0x79, 0xf2, 0x1a, 0x1e, 0xdd, + 0xe5, 0xca, 0x62, 0x6b, 0xe3, 0xd5, 0x3c, 0x8c, 0xbf, 0xb2, 0x03, 0xba, 0x20, 0x4e, 0x58, 0x7d, + 0x4a, 0x65, 0xf1, 0x65, 0x74, 0xcd, 0xd8, 0x79, 0x94, 0xe2, 0xfa, 0x14, 0x1d, 0xe6, 0x91, 0xc2, + 0x82, 0x20, 0x62, 0x6d, 0x9f, 0x3c, 0x3c, 0xc7, 0x3c, 0x89, 0xd4, 0x51, 0xb8, 0xf6, 0x59, 0x51, + 0xc5, 0x9a, 0x85, 0xcb, 0x9f, 0x5d, 0x11, 0xad, 0xf8, 0x4f, 0x78, 0x78, 0x8b, 0x47, 0xd9, 0x98, + 0xc7, 0x9d, 0x88, 0xf1, 0x96, 0x69, 0x3c, 0xc6, 0x0a, 0x84, 0x63, 0xdc, 0x27, 0x76, 0xc1, 0x01, + 0x0d, 0xf3, 0x0e, 0xc2, 0xd3, 0x09, 0x38, 0x3a, 0x5c, 0x62, 0x57, 0xcd, 0x13, 0x28, 0xeb, 0xcf, + 0x29, 0x9e, 0x62, 0x80, 0x4f, 0xa8, 0x8e, 0x2d, 0xb6, 0x16, 0xfa, 0x63, 0x92, 0x6f, 0xe8, 0x30, + 0x08, 0xfd, 0x88, 0xb9, 0x3b, 0x12, 0x51, 0x27, 0xed, 0x28, 0x21, 0xb7, 0x43, 0xf5, 0x18, 0x4b, + 0x22, 0x2a, 0xcc, 0xd2, 0xe6, 0xf4, 0xf1, 0xc0, 0x27, 0xc5, 0x83, 0x22, 0x4b, 0xb3, 0x0a, 0x06, + 0x26, 0x02, 0x51, 0xb2, 0x90, 0x23, 0x73, 0x02, 0x31, 0xeb, 0x16, 0xda, 0x8b, 0x20, 0x7e, 0xdf, + 0x42, 0x2a, 0xbb, 0x12, 0x78, 0x18, 0xb5, 0xe2, 0x3c, 0xee, 0x94, 0x59, 0x90, 0x09, 0x22, 0xa4, + 0x65, 0x15, 0x21, 0x6f, 0x65, 0x45, 0xe6, 0x3e, 0x00, 0x33, 0x5b, 0x4a, 0xd7, 0xd8, 0x8d, 0xfd, + 0x7c, 0x55, 0x1e, 0xe1, 0x46, 0x06, 0x35, 0x2b, 0xdc, 0x9b, 0x00, 0x11, 0x11, 0xf5, 0x24, 0x62, + 0x2c, 0x4e, 0x80, 0x98, 0x47, 0x0b, 0x51, 0x11, 0xca, 0x82, 0x00, 0x2c, 0xb7, 0x37, 0x93, 0xd5, + 0x59, 0xe2, 0xc1, 0x24, 0x3e, 0x49, 0xa6, 0x38, 0xf2, 0xb3, 0xb9, 0xde, 0x42, 0x18, 0x17, 0xe4, + 0xe6, 0xf6, 0xfe, 0xd4, 0x56, 0x8f, 0x8d, 0x6b, 0x74, 0xd6, 0x62, 0x23, 0x39, 0x72, 0x34, 0x8f, + 0x02, 0x02, 0x33, 0x14, 0x87, 0xde, 0xb4, 0x82, 0xe4, 0x77, 0xcd, 0x0a, 0x91, 0x82, 0xaf, 0x48, + 0xc2, 0x3e, 0xf8, 0x0c, 0x14, 0xb0, 0x5f, 0x2c, 0x8f, 0xfd, 0x84, 0x44, 0x97, 0xc3, 0x67, 0x2a, + 0xc8, 0x83, 0x9c, 0xfe, 0x63, 0x0d, 0x89, 0xc0, 0x4b, 0x0b, 0x78, 0x67, 0xf9, 0x29, 0x11, 0x42, + 0x34, 0x8f, 0x8c, 0xae, 0x8a, 0xd2, 0x97, 0x93, 0x7b, 0x2f, 0xa3, 0xb1, 0x0a, 0xaf, 0xca, 0x3a, + 0xbf, 0x04, 0x9d, 0xd1, 0x81, 0xe7, 0x25, 0x81, 0x2d, 0xb3, 0xeb, 0xc8, 0xd5, 0x59, 0xf8, 0xcf, + 0x02, 0x0e, 0x10, 0xb2, 0x0e, 0x1f, 0x96, 0x64, 0x5c, 0xf6, 0x0b, 0x1a, 0xea, 0xe6, 0x26, 0x17, + 0xa4, 0x94, 0x65, 0xe6, 0xd2, 0x5d, 0xd0, 0x19, 0xad, 0x26, 0x8d, 0x45, 0x69, 0x34, 0xc7, 0x93, + 0xd0, 0x1d, 0x32, 0xb4, 0x50, 0x88, 0xcc, 0x97, 0x28, 0x93, 0x56, 0x36, 0x9e, 0xad, 0x23, 0x2f, + 0xdf, 0x62, 0x09, 0x3d, 0xb3, 0x3e, 0x49, 0x63, 0x28, 0x41, 0x14, 0x9b, 0xc8, 0x98, 0x07, 0xb9, + 0x93, 0xc3, 0x50, 0xf6, 0x92, 0xe3, 0xc2, 0x3f, 0xe3, 0xb5, 0x6c, 0x80, 0xe8, 0x99, 0x71, 0x03, + 0x4b, 0x45, 0x06, 0x93, 0x0c, 0x9d, 0x9f, 0x6c, 0x3e, 0x16, 0xea, 0x28, 0x9b, 0x08, 0xaa, 0x4b, + 0xcc, 0x60, 0x28, 0x1d, 0xdf, 0x72, 0x4b, 0x0a, 0xaf, 0xec, 0xe6, 0x16, 0x0a, 0xc1, 0x29, 0x0b, + 0x54, 0xd4, 0xf1, 0x5f, 0x01, 0x8e, 0x4c, 0xcf, 0x19, 0x5a, 0x24, 0x83, 0x7f, 0xfe, 0x93, 0x40, + 0x01, 0x0f, 0xab, 0x8b, 0x83, 0x9f, 0x58, 0xc5, 0x2a, 0x11, 0x2f, 0x44, 0x5c, 0x31, 0x9d, 0x69, + 0xa7, 0xd9, 0x2b, 0xac, 0x49, 0x14, 0x47, 0x88, 0xf1, 0xb8, 0x14, 0x87, 0x84, 0xf6, 0xde, 0x9c, + 0x13, 0x50, 0x08, 0x6f, 0xb2, 0x66, 0xb7, 0xb6, 0x94, 0x68, 0x8b, 0x01, 0x5a, 0x54, 0x44, 0x2f, + 0xb8, 0x8f, 0xc5, 0xaa, 0x52, 0x3c, 0x75, 0x1d, 0x59, 0x3b, 0x3a, 0x32, 0x13, 0x0a, 0x7f, 0x3d, + 0x13, 0x72, 0xb2, 0x80, 0x45, 0x0f, 0xc5, 0xe5, 0x53, 0x94, 0x0d, 0xac, 0x35, 0x18, 0xb5, 0xeb, + 0x46, 0x8c, 0xc8, 0x0f, 0x69, 0x2a, 0xd0, 0xd3, 0xc0, 0x96, 0x05, 0xb0, 0x53, 0x1b, 0x29, 0xca, + 0x17, 0xad, 0x14, 0x43, 0x6d, 0xc1, 0xc3, 0x86, 0x3b, 0x24, 0xea, 0xa1, 0x31, 0x0f, 0x58, 0x6c, + 0x7e, 0x15, 0x96, 0xec, 0x9a, 0x4f, 0x7c, 0x4f, 0x05, 0x3c, 0x73, 0xed, 0x8a, 0x6c, 0xa7, 0x92, + 0x0b, 0xd7, 0x5a, 0x61, 0x95, 0xd5, 0xd6, 0x44, 0xf8, 0xd6, 0xbf, 0xda, 0x9e, 0xa8, 0x6e, 0xc4, + 0x51, 0xde, 0x25, 0xe7, 0x9f, 0x0b, 0x5d, 0xb8, 0xd0, 0x59, 0x28, 0x2d, 0xae, 0x73, 0x94, 0x75, + 0x3c, 0x18, 0x64, 0xbc, 0x6b, 0x17, 0x81, 0x09, 0x0e, 0x2b, 0x0a, 0x30, 0x9f, 0x01, 0x01, 0xb2, + 0x06, 0x02, 0x3e, 0x87, 0x00, 0xf2, 0x7f, 0x48, 0x53, 0xe1, 0x5a, 0x60, 0x92, 0x75, 0xc0, 0xf4, + 0xd7, 0x02, 0x53, 0xf1, 0xef, 0x10, 0x00, 0x25, 0xc6, 0x3c, 0xd4, 0x89, 0x04, 0xa8, 0xe2, 0x18, + 0xf3, 0xcf, 0x5d, 0x59, 0x0c, 0xc8, 0x14, 0x44, 0x85, 0x5b, 0xb2, 0xd0, 0x4f, 0x6a, 0x29, 0x2c, + 0x61, 0xcd, 0x14, 0x58, 0x7a, 0xeb, 0x01, 0xe0, 0xad, 0x01, 0x80, 0x6b, 0x20, 0x02, 0xb0, 0x74, + 0x7f, 0x48, 0x53, 0xfe, 0x5a, 0x58, 0x7a, 0xeb, 0x60, 0xe9, 0xae, 0x80, 0x65, 0x11, 0xb0, 0x04, + 0x54, 0x3d, 0x63, 0xee, 0xeb, 0x1e, 0x83, 0xea, 0x40, 0x71, 0x7e, 0x5d, 0x01, 0x4b, 0xd8, 0xd4, + 0xe2, 0x79, 0xd9, 0x00, 0x42, 0x31, 0x85, 0x36, 0xb1, 0xf7, 0xe6, 0x01, 0x8f, 0xa9, 0x44, 0x94, + 0x58, 0xcb, 0xcb, 0xe4, 0xb7, 0xab, 0xb7, 0x45, 0x46, 0x70, 0xb9, 0x1a, 0xb6, 0x4a, 0x83, 0xe0, + 0xba, 0x16, 0x2f, 0x83, 0x80, 0xea, 0xd5, 0x9b, 0x52, 0x01, 0x7d, 0x3f, 0x90, 0x14, 0xff, 0xe1, + 0x2c, 0x7b, 0x47, 0xe8, 0x4a, 0x3b, 0x19, 0xa8, 0x40, 0xf0, 0xea, 0xc0, 0xd3, 0x03, 0x7b, 0x2f, + 0xa8, 0x08, 0xb1, 0xb0, 0x6d, 0xdb, 0xb8, 0xb0, 0xc9, 0x73, 0xc9, 0xb6, 0x70, 0xd2, 0xf4, 0x45, + 0xad, 0x4b, 0x0b, 0xc3, 0x34, 0x26, 0x83, 0x1a, 0x1d, 0x32, 0xf1, 0x22, 0x90, 0xd1, 0xa9, 0x2e, + 0x71, 0x65, 0x31, 0x90, 0x32, 0x10, 0x4b, 0x31, 0xb3, 0xc8, 0x53, 0xbc, 0x1a, 0x85, 0x01, 0xe3, + 0xd2, 0xc5, 0x05, 0x63, 0x63, 0xe5, 0xa0, 0xf8, 0xf6, 0x70, 0xd5, 0x02, 0x30, 0x29, 0xb0, 0xbf, + 0x66, 0x7b, 0x7b, 0x80, 0x73, 0x87, 0x46, 0x8c, 0xd6, 0x9d, 0xba, 0xe7, 0xb6, 0x46, 0x56, 0x1f, + 0x96, 0x6c, 0x33, 0xcb, 0x8e, 0x8b, 0x27, 0xe3, 0xbf, 0x05, 0xfb, 0xfb, 0x4e, 0x45, 0x06, 0x86, + 0xaf, 0xb0, 0xf8, 0xef, 0x68, 0x6a, 0x57, 0x51, 0x5f, 0x0d, 0x14, 0x71, 0xe4, 0x50, 0x5c, 0x09, + 0xc2, 0x07, 0xdd, 0x78, 0x6d, 0xe2, 0xba, 0xd1, 0x59, 0x4e, 0x34, 0x09, 0x3f, 0x78, 0x17, 0x4d, + 0x51, 0x1f, 0x29, 0xf8, 0xdf, 0x37, 0xe6, 0x44, 0xf7, 0x05, 0xf6, 0x4a, 0x7d, 0xde, 0xb3, 0x4b, + 0xf3, 0xbf, 0xfd, 0x5a, 0x15, 0xa1, 0x4d, 0x17, 0xd6, 0xc3, 0xfd, 0x6b, 0x6b, 0x91, 0x0d, 0x4d, + 0xe0, 0x21, 0x8c, 0xdc, 0xe2, 0x0b, 0x79, 0xc1, 0xf5, 0xf7, 0x79, 0xc8, 0xcf, 0x7b, 0x06, 0x72, + 0x92, 0x7c, 0x59, 0xb0, 0x02, 0x84, 0x39, 0x16, 0x2e, 0x5c, 0x5f, 0x34, 0x11, 0xf4, 0xac, 0x5a, + 0x99, 0x1c, 0xdf, 0x0e, 0x4f, 0xe8, 0xea, 0x10, 0x2d, 0x97, 0x0d, 0x4b, 0xbc, 0xf1, 0xe2, 0xc5, + 0x46, 0x8c, 0xc2, 0xff, 0x99, 0x33, 0x11, 0x7d, 0xa9, 0x33, 0x21, 0xff, 0x93, 0x67, 0xc2, 0xec, + 0x08, 0xd8, 0x34, 0xd6, 0x1d, 0xc9, 0xff, 0x9e, 0xf3, 0x60, 0xcc, 0xfe, 0x2a, 0x24, 0xb0, 0x6c, + 0xd8, 0x85, 0x65, 0x97, 0x8c, 0x7b, 0x45, 0xd9, 0xc5, 0x81, 0x17, 0x15, 0x5e, 0x1c, 0xf9, 0x12, + 0x04, 0x92, 0x47, 0x0a, 0x09, 0xe1, 0x30, 0xc0, 0x54, 0x18, 0x48, 0xc9, 0x56, 0x94, 0xc8, 0x94, + 0x2c, 0x0c, 0x65, 0xa7, 0x23, 0x25, 0xc6, 0x6a, 0xdb, 0xf9, 0x06, 0xd9, 0x2b, 0xcf, 0x91, 0x71, + 0x4b, 0xb0, 0x73, 0xd7, 0xc1, 0xc9, 0x6b, 0x1b, 0x6a, 0xc4, 0xc9, 0x4c, 0xe4, 0x8f, 0x75, 0x48, + 0x49, 0xde, 0x0d, 0x01, 0x23, 0xc8, 0x96, 0x2f, 0xb3, 0x94, 0x15, 0x06, 0x6b, 0xb0, 0x3a, 0x23, + 0xab, 0x08, 0xc3, 0x0f, 0x6b, 0xb6, 0xe3, 0x0b, 0x5a, 0x8c, 0x56, 0xdf, 0x13, 0x8b, 0x4d, 0x2d, + 0xbb, 0x2d, 0xc2, 0x15, 0xd4, 0x9e, 0x0a, 0x36, 0x81, 0xee, 0x03, 0x63, 0xce, 0x9f, 0x9e, 0x64, + 0xdc, 0x43, 0x36, 0xa2, 0xc9, 0xf3, 0x59, 0x30, 0xff, 0x47, 0x70, 0x60, 0xfe, 0xff, 0x14, 0x16, + 0x2c, 0x03, 0xa5, 0xa5, 0x02, 0xa0, 0x59, 0xe0, 0x66, 0xb6, 0x69, 0xa6, 0x39, 0xb5, 0x8d, 0xe5, + 0x77, 0xb8, 0x11, 0xfb, 0x79, 0xaf, 0xf5, 0x7f, 0xae, 0x49, 0xc1, 0x9a, 0xe4, 0x7d, 0xfb, 0x17, + 0xd7, 0x04, 0x17, 0x86, 0x25, 0xf8, 0x41, 0xc0, 0xcc, 0xda, 0x59, 0xfc, 0x30, 0xb8, 0xbe, 0xb8, + 0xd9, 0x35, 0x20, 0x5e, 0xd2, 0xde, 0x8b, 0xa1, 0x9d, 0x0b, 0xf1, 0x50, 0x24, 0x87, 0xc8, 0xc5, + 0xa4, 0xf8, 0xe7, 0xa6, 0x2d, 0x42, 0x24, 0x59, 0x20, 0x2d, 0x42, 0x91, 0xe4, 0x43, 0x8d, 0xfc, + 0x53, 0x24, 0x56, 0x00, 0xc6, 0x7c, 0x40, 0x96, 0x02, 0x7c, 0xec, 0x79, 0x67, 0x64, 0x6d, 0xcc, + 0xa7, 0xa5, 0xc0, 0xed, 0xa3, 0xa1, 0x0a, 0xdc, 0xd1, 0x7a, 0x88, 0x8c, 0xd6, 0x40, 0x64, 0x6c, + 0xa0, 0x3e, 0x00, 0x77, 0xfc, 0x43, 0x9a, 0x1a, 0xae, 0x05, 0xee, 0x68, 0x1d, 0x70, 0xc7, 0x4b, + 0x95, 0x44, 0xeb, 0x83, 0x65, 0x09, 0x78, 0x8f, 0x8c, 0x39, 0xfc, 0x11, 0x74, 0x84, 0x28, 0x2e, + 0x7d, 0x43, 0x9f, 0xcf, 0xbf, 0x7b, 0xf9, 0x57, 0xac, 0x5b, 0x8d, 0x85, 0x37, 0xb6, 0x85, 0x94, + 0x07, 0x9e, 0xda, 0x6e, 0x35, 0xd8, 0x23, 0xdb, 0x1e, 0x3c, 0xb2, 0x1d, 0xc1, 0x23, 0xdb, 0x92, + 0x48, 0xac, 0xa4, 0xc6, 0x8c, 0xc9, 0x43, 0x59, 0x28, 0x49, 0xb5, 0x3d, 0x75, 0xbe, 0x45, 0xfb, + 0x43, 0x09, 0xfa, 0x89, 0x2b, 0xa0, 0xa7, 0x47, 0xb8, 0xc2, 0x4c, 0x26, 0x91, 0xda, 0x74, 0xfa, + 0x81, 0xb0, 0x94, 0x7b, 0x81, 0x14, 0x2a, 0x31, 0x7f, 0xe4, 0xc9, 0x1f, 0x1c, 0xf9, 0x23, 0x89, + 0x60, 0x09, 0x72, 0x8f, 0x50, 0xc8, 0x3d, 0x88, 0x1a, 0x86, 0xe9, 0x9f, 0x67, 0xbc, 0xf0, 0x8c, + 0xa7, 0x10, 0x2a, 0xc2, 0x93, 0x22, 0x90, 0xce, 0xf3, 0x19, 0x81, 0x35, 0x17, 0x2f, 0x21, 0xec, + 0xd9, 0x3c, 0x98, 0x51, 0xee, 0xe9, 0xba, 0xb5, 0x95, 0x80, 0xb2, 0x46, 0x40, 0xbb, 0xad, 0x99, + 0x6b, 0xb0, 0x7c, 0xae, 0x7c, 0x36, 0x8b, 0xf4, 0xf9, 0xa0, 0x20, 0x4c, 0xcf, 0x0f, 0xe2, 0x7d, + 0x72, 0x96, 0x9b, 0x3f, 0x8c, 0x03, 0x7a, 0x79, 0xbb, 0x6b, 0xf8, 0xa0, 0x65, 0x0d, 0xfe, 0x15, + 0x6e, 0x28, 0x0f, 0xcc, 0x45, 0x88, 0x7b, 0xd9, 0x80, 0x44, 0x3f, 0x88, 0x8c, 0x51, 0xec, 0x57, + 0x7f, 0x18, 0x39, 0xf3, 0xb2, 0x36, 0xd7, 0x9c, 0xd5, 0xa2, 0xc6, 0x5e, 0x7c, 0x66, 0xb3, 0xc1, + 0x9c, 0x96, 0x51, 0xe4, 0x99, 0x00, 0x4b, 0xcf, 0xdf, 0xcc, 0x2b, 0xe1, 0xab, 0xd8, 0xd9, 0x3e, + 0x63, 0xc3, 0x2d, 0x07, 0xa9, 0xb4, 0xaa, 0x25, 0xb0, 0xdb, 0xfd, 0x67, 0xec, 0xf6, 0xbf, 0xa1, + 0xe3, 0x67, 0x21, 0xe8, 0xbf, 0xa5, 0x5f, 0x88, 0x97, 0xcc, 0x3e, 0xb5, 0x95, 0x7b, 0xa5, 0xa8, + 0xef, 0x15, 0x87, 0x71, 0xc5, 0xa6, 0x53, 0x7a, 0x5f, 0xc5, 0x77, 0xa8, 0x3b, 0x66, 0xf1, 0xc4, + 0xfa, 0x05, 0x01, 0xb3, 0x9e, 0x7f, 0x6c, 0xbd, 0xef, 0xbf, 0x06, 0xd7, 0x43, 0xd7, 0x07, 0xe8, + 0x7a, 0x3f, 0xec, 0xde, 0x7d, 0x56, 0x87, 0xcf, 0x55, 0x68, 0xfc, 0xb8, 0xfe, 0x9e, 0xb9, 0x7d, + 0x9e, 0x49, 0x16, 0x3c, 0x63, 0xdb, 0xf8, 0xab, 0x85, 0x03, 0x99, 0x2d, 0xb1, 0x88, 0x8d, 0x46, + 0x32, 0x30, 0x18, 0x0a, 0x6c, 0xb8, 0xbd, 0x9f, 0xad, 0x09, 0xf4, 0xb2, 0x0f, 0xd4, 0xb5, 0x32, + 0x0f, 0x15, 0x72, 0x4d, 0xda, 0x70, 0x1d, 0xd9, 0xd0, 0xe7, 0x0a, 0x9e, 0x91, 0x3d, 0xac, 0xa4, + 0x5e, 0x20, 0xdb, 0x41, 0xc7, 0xb7, 0x5f, 0x44, 0x7d, 0xf4, 0x57, 0x2c, 0x0e, 0x53, 0xfe, 0xb9, + 0x99, 0x02, 0x4b, 0x5c, 0x41, 0xa7, 0xa0, 0x48, 0xd4, 0x28, 0xef, 0x55, 0x33, 0x62, 0x34, 0x32, + 0x62, 0x34, 0x65, 0x93, 0x00, 0xb9, 0x6c, 0x81, 0x4a, 0xd5, 0x43, 0xc3, 0x8a, 0x6a, 0x6c, 0x08, + 0xef, 0x65, 0x28, 0xc6, 0xfc, 0x5a, 0xd6, 0x2b, 0x75, 0xc3, 0x8f, 0x36, 0x82, 0x90, 0x6e, 0xf4, + 0x43, 0xc2, 0x96, 0x6a, 0x83, 0x81, 0x7e, 0x83, 0xf0, 0xa7, 0xa4, 0xd1, 0x86, 0x3f, 0x08, 0x42, + 0xb0, 0x9d, 0xab, 0x54, 0x2a, 0x9a, 0x54, 0xe4, 0xc6, 0xee, 0x08, 0x3b, 0x44, 0x42, 0x62, 0x9d, + 0x2a, 0xf4, 0x87, 0xc1, 0x81, 0xab, 0x67, 0xfb, 0xfc, 0xd8, 0xfc, 0xd7, 0x0c, 0x61, 0x8d, 0x4e, + 0x37, 0xd3, 0xd7, 0x52, 0xa5, 0xaf, 0x9b, 0x2d, 0xb1, 0xbc, 0xb7, 0xe5, 0x2a, 0xc3, 0x34, 0x70, + 0x5e, 0x6a, 0x1f, 0x41, 0x52, 0xfb, 0x08, 0xee, 0xdc, 0x0b, 0xf9, 0x6b, 0x4e, 0x4f, 0x8e, 0xdd, + 0xf3, 0x0b, 0x4f, 0x0f, 0xb1, 0x95, 0xf7, 0x1c, 0xfb, 0xeb, 0xb6, 0xfd, 0x94, 0x9f, 0x9e, 0xa1, + 0xdd, 0x57, 0x4f, 0x4f, 0xd4, 0x21, 0x2f, 0x3b, 0x3d, 0xd3, 0x75, 0x4b, 0xe6, 0x2d, 0x59, 0xb2, + 0x9c, 0x63, 0xb3, 0x0b, 0xfb, 0x46, 0x3d, 0x3f, 0x43, 0x66, 0x0d, 0xd3, 0x5f, 0x7e, 0x7e, 0x7c, + 0xd4, 0x5f, 0x73, 0x7e, 0x16, 0xfd, 0xac, 0x17, 0x4e, 0x11, 0x77, 0x8e, 0x7e, 0xc9, 0x31, 0x5a, + 0xa7, 0xbc, 0xfe, 0x71, 0x00, 0x11, 0xeb, 0xc3, 0x1d, 0x53, 0xfa, 0x95, 0xd4, 0x8f, 0xdb, 0x40, + 0x23, 0xee, 0x84, 0xd2, 0x1b, 0x83, 0x0b, 0x4a, 0xef, 0x9e, 0xf9, 0xa0, 0x0c, 0x17, 0x1c, 0x00, + 0xa6, 0x84, 0xf4, 0xe8, 0x63, 0x8f, 0xf9, 0x9b, 0xf4, 0x46, 0x38, 0x58, 0x2c, 0xc2, 0x1c, 0x4b, + 0x7a, 0x4c, 0x88, 0x2e, 0x32, 0xb3, 0x9e, 0x25, 0x3d, 0x02, 0xae, 0x25, 0xf9, 0x9c, 0xa0, 0x37, + 0xe1, 0x0e, 0x20, 0x3d, 0xe6, 0xfb, 0x31, 0x5c, 0xf0, 0xb9, 0x48, 0xf2, 0x99, 0x17, 0xc8, 0x42, + 0x3e, 0x0d, 0xa9, 0x33, 0xea, 0x39, 0x3e, 0xe9, 0xc1, 0x7a, 0xf7, 0x22, 0x30, 0x2f, 0x5f, 0x52, + 0x68, 0x3a, 0x59, 0x55, 0x26, 0x60, 0x1d, 0xb0, 0xf1, 0x7b, 0x4b, 0x73, 0x3d, 0xee, 0x19, 0x52, + 0x90, 0x0d, 0xa3, 0x5f, 0x5e, 0x99, 0xe5, 0x2e, 0xab, 0x8c, 0x09, 0xe9, 0xb1, 0x77, 0xdb, 0x0b, + 0xe0, 0xce, 0x00, 0x07, 0x06, 0xcc, 0x8b, 0x70, 0xe3, 0xcd, 0xf5, 0xbc, 0xe9, 0xa4, 0xa0, 0x5e, + 0xc0, 0xc7, 0x52, 0x9c, 0x1b, 0x77, 0x02, 0x9d, 0x5f, 0x29, 0x8b, 0x68, 0xf5, 0x3f, 0x6f, 0x4b, + 0xae, 0xb3, 0xcb, 0xc8, 0xf4, 0xb6, 0x14, 0xb1, 0x7a, 0xcb, 0x10, 0xeb, 0x62, 0x7f, 0x2b, 0xcc, + 0x89, 0x95, 0xb8, 0x9f, 0x8b, 0xc2, 0x21, 0x78, 0x7f, 0x58, 0x06, 0x09, 0xf8, 0xdf, 0x89, 0x5f, + 0x17, 0x63, 0x24, 0xfc, 0x1d, 0x28, 0xb6, 0x30, 0x70, 0xc3, 0x02, 0x96, 0xa5, 0xb2, 0xd4, 0x7f, + 0x0b, 0x44, 0x5b, 0x08, 0x99, 0x40, 0xef, 0xff, 0x67, 0x9f, 0xae, 0x25, 0x2b, 0xf4, 0x77, 0x1e, + 0xb0, 0xc2, 0x2e, 0x57, 0x58, 0x9f, 0x65, 0x43, 0xd9, 0x2e, 0x1e, 0xb3, 0x48, 0x8d, 0xf8, 0x0a, + 0x0b, 0xfb, 0x52, 0x5e, 0x60, 0xf1, 0xc5, 0xd5, 0x94, 0xf8, 0xf7, 0x81, 0xb0, 0xcf, 0xb8, 0x7f, + 0xa3, 0xbf, 0xc4, 0x0f, 0x78, 0xff, 0x49, 0xfc, 0x40, 0x2e, 0xf6, 0xc7, 0x6a, 0x8e, 0xc0, 0xdf, + 0xb4, 0xed, 0x61, 0x85, 0x3a, 0x83, 0x85, 0x23, 0x95, 0x6d, 0x66, 0xe1, 0x2c, 0x89, 0x13, 0xb4, + 0x41, 0x9d, 0xc1, 0x73, 0x4e, 0x91, 0xf7, 0x77, 0x91, 0xdc, 0x8b, 0xb3, 0x0d, 0x75, 0xb8, 0xb4, + 0xd2, 0x20, 0x26, 0x4b, 0x98, 0x80, 0xff, 0xc4, 0x11, 0xfd, 0xbd, 0x6c, 0xc0, 0x62, 0x7f, 0xcb, + 0x4f, 0x52, 0x36, 0xa0, 0xb2, 0x38, 0x48, 0x2e, 0x30, 0x03, 0xae, 0x54, 0xfe, 0x64, 0x23, 0x1a, + 0xff, 0x53, 0xf7, 0x5b, 0x6c, 0x44, 0x92, 0x81, 0xd2, 0xa2, 0x40, 0x62, 0xa0, 0x44, 0x5e, 0x7e, + 0x19, 0x08, 0x7d, 0xa6, 0xaf, 0x02, 0xf3, 0xeb, 0xad, 0x2d, 0x7d, 0xf9, 0x06, 0x64, 0x2e, 0xbf, + 0xcf, 0x04, 0xe9, 0x33, 0x60, 0xfa, 0xec, 0xf6, 0xd6, 0x80, 0x35, 0xd7, 0xce, 0x8b, 0x21, 0xab, + 0x44, 0xac, 0x5e, 0x0e, 0x54, 0xf0, 0x7f, 0x5f, 0x7c, 0x2f, 0x97, 0xd7, 0x5b, 0x70, 0x94, 0x4f, + 0xab, 0x31, 0x77, 0x81, 0x67, 0xd5, 0x63, 0x25, 0xd3, 0x8a, 0xc2, 0xcd, 0xfc, 0x59, 0x55, 0x45, + 0xd9, 0x8c, 0x39, 0x74, 0x3e, 0xea, 0xf5, 0xd2, 0x0d, 0x01, 0xcf, 0x65, 0x2b, 0x1b, 0xc2, 0x5d, + 0x4d, 0xdd, 0x9d, 0xdd, 0xe5, 0x50, 0x50, 0xe1, 0x15, 0xed, 0xab, 0x04, 0xc5, 0x77, 0xb6, 0xe5, + 0xad, 0x3d, 0x53, 0xee, 0xba, 0x95, 0x5f, 0x89, 0x9d, 0x8a, 0xe1, 0x95, 0x62, 0x29, 0x4f, 0x60, + 0x29, 0x86, 0xc7, 0x95, 0x13, 0x98, 0x5d, 0x98, 0x2c, 0x0e, 0xbb, 0xbe, 0x31, 0xe2, 0x89, 0x1f, + 0x0c, 0x2e, 0xf0, 0x04, 0x3b, 0x14, 0x13, 0xe1, 0x48, 0xf7, 0x32, 0xbd, 0x76, 0x2e, 0x46, 0x79, + 0xc2, 0x80, 0xa5, 0x06, 0x5f, 0xb9, 0x90, 0xe2, 0xd7, 0xa6, 0x59, 0x43, 0x66, 0xb5, 0x89, 0x4c, + 0xb3, 0x8a, 0xcc, 0x6a, 0xfd, 0x26, 0x97, 0x9f, 0xa1, 0xeb, 0x6b, 0x86, 0x34, 0x11, 0x57, 0x42, + 0x8d, 0x0b, 0x52, 0x3f, 0xed, 0xa1, 0x93, 0x6e, 0x09, 0x4e, 0x5e, 0xa4, 0x59, 0x65, 0x82, 0xc6, + 0xf6, 0x2b, 0xac, 0x3c, 0x8b, 0xd9, 0xaf, 0x10, 0xe7, 0xc1, 0xe8, 0x8c, 0xe5, 0xbb, 0x94, 0x3d, + 0x81, 0x54, 0x5e, 0xe1, 0x65, 0x6f, 0x02, 0x6f, 0x6d, 0x15, 0x10, 0xde, 0x49, 0x7d, 0xe4, 0x19, + 0xab, 0xd0, 0xd1, 0x22, 0xfd, 0x59, 0x14, 0xaa, 0x8d, 0x91, 0xb7, 0x73, 0x42, 0xa9, 0x35, 0x62, + 0x8e, 0xb5, 0xfd, 0x8a, 0x88, 0x0f, 0x81, 0x18, 0x7b, 0xcf, 0x3f, 0x21, 0x88, 0x44, 0x6c, 0x3c, + 0x63, 0xcb, 0x3e, 0xbb, 0xcf, 0x17, 0x91, 0xb2, 0xcb, 0x9b, 0x89, 0x0a, 0x1c, 0x74, 0x7f, 0xd8, + 0x08, 0x55, 0xfa, 0x2c, 0x5a, 0x75, 0x44, 0xe4, 0xbb, 0x1d, 0xd7, 0x99, 0x1d, 0x64, 0x1a, 0x37, + 0xc8, 0x2b, 0x66, 0x3f, 0x95, 0xe0, 0xf4, 0x3f, 0xe8, 0x2e, 0x57, 0x63, 0xc4, 0xfc, 0xb0, 0x4b, + 0xfd, 0x85, 0x8d, 0xae, 0xb9, 0x86, 0x0a, 0x5b, 0xfb, 0x2b, 0x97, 0x51, 0x0a, 0xbd, 0x42, 0x77, + 0xbd, 0x5c, 0xbc, 0xfd, 0xe7, 0xfb, 0x1f, 0xad, 0xc5, 0xc6, 0xcf, 0x72, 0x3f, 0x7a, 0x86, 0xf7, + 0xd1, 0xf3, 0x9a, 0x22, 0x6b, 0x11, 0xfb, 0x33, 0x3c, 0x8f, 0x96, 0x13, 0x4b, 0x59, 0x30, 0x2d, + 0xba, 0x0e, 0x71, 0x93, 0x5b, 0x0f, 0x8f, 0x30, 0xc5, 0x8b, 0xfb, 0x35, 0x6b, 0x79, 0x9b, 0xb6, + 0xa5, 0x69, 0x28, 0x67, 0x6d, 0x64, 0xb6, 0x0c, 0x75, 0xcf, 0x3f, 0xc7, 0xdf, 0x8f, 0x47, 0x31, + 0xa8, 0x76, 0xf8, 0xbe, 0x57, 0x0c, 0xdd, 0x53, 0x97, 0xa4, 0x9c, 0x5f, 0x9b, 0xe8, 0x3f, 0x30, + 0x3a, 0x61, 0xe2, 0x1c, 0xc8, 0x9f, 0xb5, 0x65, 0x26, 0xef, 0x71, 0xb0, 0xbd, 0x9d, 0x18, 0xbb, + 0x33, 0xd7, 0x7e, 0xd5, 0x8b, 0x8e, 0x57, 0x5e, 0xe7, 0x98, 0x97, 0x0c, 0x5f, 0x75, 0xcc, 0x0b, + 0x8a, 0xbc, 0xf2, 0x64, 0x7b, 0x97, 0x2c, 0x2c, 0xd3, 0x8b, 0x5a, 0x2c, 0xf4, 0xae, 0x0b, 0x2a, + 0x3c, 0xc0, 0x53, 0x1a, 0x69, 0xdf, 0x1f, 0x04, 0x2f, 0xc1, 0x1d, 0x24, 0x0d, 0x72, 0xef, 0xdb, + 0xbc, 0x84, 0x04, 0xf3, 0xb0, 0x43, 0x56, 0x04, 0xbe, 0x31, 0x6b, 0xed, 0xfd, 0xa1, 0x4d, 0x94, + 0x40, 0x17, 0x66, 0xad, 0x6d, 0x58, 0x32, 0x69, 0x21, 0x52, 0xd2, 0x92, 0xcd, 0x26, 0x1f, 0x76, + 0xe0, 0x9e, 0x79, 0x7c, 0x0e, 0x82, 0xd5, 0x5d, 0xd1, 0x7b, 0x55, 0xda, 0x77, 0xfb, 0xc9, 0x93, + 0xcd, 0xf1, 0xf2, 0x0e, 0xe4, 0x93, 0x0d, 0x20, 0xb5, 0xca, 0xf4, 0xb0, 0x9c, 0xbc, 0x4e, 0x22, + 0x56, 0x81, 0x83, 0xed, 0xf2, 0x71, 0x0c, 0x2b, 0xd9, 0xd0, 0x55, 0xc6, 0xf2, 0x83, 0x1d, 0xae, + 0xf6, 0xc0, 0xc5, 0xeb, 0x38, 0x58, 0x75, 0x44, 0x2b, 0x4b, 0xf1, 0xa8, 0x57, 0x53, 0xae, 0x15, + 0x76, 0xa8, 0xd3, 0xa3, 0x61, 0xd8, 0x1b, 0x85, 0xc1, 0x40, 0xcb, 0x81, 0x0a, 0xbc, 0x7d, 0x85, + 0x60, 0xe1, 0xbf, 0xd1, 0xa8, 0x23, 0x7d, 0x58, 0x49, 0x22, 0x76, 0x31, 0x51, 0xc0, 0x7f, 0xb3, + 0x01, 0x06, 0xb0, 0x57, 0x97, 0x7a, 0x17, 0x67, 0x7c, 0xa4, 0x9f, 0xd3, 0x5d, 0xb0, 0xba, 0xb7, + 0xb5, 0x52, 0x88, 0xe5, 0xdb, 0x5e, 0xc6, 0x1b, 0x59, 0xb8, 0x08, 0xa9, 0xfa, 0x90, 0x88, 0x5d, + 0x7d, 0x09, 0x59, 0x7d, 0xc8, 0xd0, 0x53, 0xfa, 0xc4, 0x79, 0x15, 0x35, 0x6a, 0xbb, 0x8d, 0xdd, + 0xd6, 0x4e, 0x6d, 0xb7, 0xf9, 0x5f, 0x24, 0xed, 0x5e, 0x2d, 0x2b, 0x4d, 0x42, 0xd5, 0x3e, 0x83, + 0x5d, 0x5a, 0x21, 0xe5, 0xee, 0x83, 0xc8, 0x6d, 0xd3, 0xb6, 0xfd, 0x9c, 0xd8, 0x4d, 0x0a, 0xd2, + 0x36, 0x18, 0x4c, 0x37, 0xe0, 0xbc, 0x31, 0x91, 0x9b, 0xb3, 0xe1, 0xf9, 0x80, 0xa8, 0x71, 0x40, + 0x45, 0x96, 0x90, 0xbf, 0xfd, 0x05, 0xa1, 0xf5, 0x8f, 0x9b, 0xe0, 0x5f, 0x13, 0x56, 0xff, 0x48, + 0x00, 0xaf, 0x77, 0x99, 0xf6, 0x8a, 0x49, 0xfb, 0x82, 0x7e, 0xfe, 0x12, 0xcb, 0x9a, 0x3e, 0xa2, + 0xe3, 0xa3, 0x2a, 0x2a, 0x96, 0x4d, 0xe7, 0x1f, 0xa8, 0xf9, 0xa7, 0x91, 0x70, 0x31, 0x99, 0x98, + 0x05, 0xd3, 0xf2, 0xd0, 0x3c, 0xf4, 0x60, 0x4a, 0x43, 0x25, 0xb0, 0xd9, 0x92, 0x68, 0x3a, 0x99, + 0xf6, 0x36, 0x4d, 0xa5, 0xfe, 0x49, 0x3e, 0x34, 0xda, 0xb3, 0x5a, 0xa8, 0x2a, 0x9e, 0x92, 0xa7, + 0xd8, 0x1f, 0x0c, 0x6f, 0xc3, 0x29, 0x11, 0x56, 0x13, 0x55, 0x14, 0xd8, 0x55, 0x14, 0xda, 0x55, + 0x44, 0xec, 0xf6, 0xaa, 0x77, 0xcd, 0x32, 0x5a, 0x0a, 0x2e, 0x32, 0x7a, 0xab, 0x36, 0x97, 0x7d, + 0xcc, 0xac, 0xba, 0xec, 0x6d, 0xb3, 0x44, 0x21, 0x1b, 0x64, 0x4b, 0x64, 0x9f, 0x36, 0x83, 0x63, + 0x98, 0x17, 0x59, 0xac, 0xc4, 0xb8, 0x89, 0xab, 0x7a, 0x76, 0x89, 0xf2, 0xaa, 0x15, 0xf5, 0x45, + 0x34, 0xe4, 0x0a, 0x8a, 0xcf, 0x83, 0xd0, 0x7e, 0x6e, 0x5e, 0x99, 0xdc, 0x5f, 0x4c, 0x1a, 0x4a, + 0x32, 0x1b, 0xd0, 0xfb, 0x08, 0x68, 0xef, 0x9f, 0xfa, 0x9d, 0xd1, 0xf6, 0x36, 0x07, 0xdb, 0x58, + 0x54, 0x48, 0x9e, 0x66, 0x1b, 0x28, 0x2d, 0x08, 0xad, 0xf7, 0xa3, 0x48, 0x4a, 0x22, 0x8d, 0x0d, + 0x39, 0x25, 0x9e, 0x86, 0x50, 0x16, 0x71, 0xd3, 0xc6, 0x68, 0x88, 0x1d, 0xe2, 0x5d, 0x62, 0x37, + 0x0c, 0xbc, 0xe8, 0x60, 0x10, 0x5a, 0x03, 0x26, 0x79, 0x78, 0x8c, 0xa5, 0x43, 0xea, 0x9c, 0xe9, + 0xf7, 0x53, 0xf8, 0x33, 0x4a, 0xcc, 0x9a, 0xa2, 0x20, 0x49, 0xb1, 0x86, 0x71, 0x1c, 0xa3, 0x61, + 0x68, 0x5f, 0x45, 0xec, 0x42, 0x9a, 0x86, 0xb6, 0xb9, 0xd3, 0x42, 0x97, 0xd4, 0x36, 0x1b, 0x55, + 0x14, 0x51, 0xdb, 0xac, 0x55, 0xd1, 0x07, 0x62, 0xef, 0x56, 0x51, 0x8f, 0xd8, 0x75, 0xdc, 0x40, + 0x07, 0x91, 0x5d, 0x43, 0xb7, 0xd4, 0xd6, 0x88, 0xeb, 0xb6, 0xca, 0x2c, 0x3e, 0x5e, 0x39, 0x7c, + 0x08, 0x30, 0xd1, 0xd0, 0x7b, 0x62, 0xef, 0xe0, 0x3a, 0xba, 0x23, 0x76, 0x0d, 0xd7, 0xd1, 0x83, + 0x4d, 0xed, 0x3d, 0x2f, 0x74, 0xa7, 0x63, 0x1c, 0x50, 0x58, 0x96, 0x63, 0xd0, 0x73, 0x05, 0xf4, + 0x70, 0xf6, 0xde, 0xd3, 0xa9, 0x81, 0xfe, 0xc4, 0x50, 0x40, 0xc5, 0x13, 0xd8, 0xde, 0x53, 0x2e, + 0x32, 0x8c, 0xa8, 0x61, 0xa0, 0x7b, 0xca, 0x56, 0x00, 0x82, 0x2e, 0x1c, 0x07, 0xf0, 0x5c, 0x04, + 0xe9, 0xf4, 0xa7, 0x81, 0x4b, 0xfd, 0x30, 0xd8, 0xf8, 0x20, 0x9d, 0x78, 0xa9, 0xbd, 0xc8, 0x2b, + 0x49, 0x77, 0x0b, 0x97, 0xcc, 0x26, 0x34, 0x4c, 0xb7, 0xc5, 0x27, 0x67, 0x34, 0xc5, 0x11, 0x0c, + 0xe0, 0xba, 0x52, 0xa9, 0xd0, 0x9b, 0xca, 0xd8, 0x99, 0x40, 0xcf, 0xf0, 0xa4, 0x98, 0x8c, 0xb2, + 0xdb, 0x32, 0x2a, 0x13, 0x88, 0x93, 0x06, 0x24, 0x41, 0x0d, 0x69, 0x55, 0xcd, 0x30, 0x2a, 0x7f, + 0x84, 0x7e, 0xa0, 0x6b, 0x9a, 0x11, 0x03, 0x98, 0x8e, 0xa9, 0x0d, 0x9d, 0xa7, 0x43, 0x39, 0x21, + 0x7a, 0xca, 0x55, 0x51, 0x7b, 0x14, 0xba, 0xce, 0xe8, 0x92, 0x86, 0xc4, 0x19, 0xb0, 0x80, 0xb5, + 0xef, 0x29, 0x1e, 0xeb, 0x1c, 0x62, 0x11, 0x8e, 0x22, 0x3f, 0x0c, 0x34, 0xe6, 0x4a, 0xfc, 0xfa, + 0xdf, 0xae, 0xab, 0xe5, 0x5d, 0xa7, 0xdc, 0xbf, 0x99, 0xd7, 0x6b, 0xf1, 0xab, 0xd7, 0x15, 0x0a, + 0x7b, 0x90, 0x3e, 0x3d, 0x69, 0x9a, 0x61, 0x48, 0xb1, 0x5f, 0x87, 0xfb, 0x29, 0xb3, 0x0e, 0x45, + 0x5a, 0xa6, 0xfd, 0xa8, 0xb0, 0x7d, 0x88, 0xae, 0x86, 0x05, 0xef, 0x26, 0x6a, 0x41, 0x0b, 0x31, + 0x1b, 0xff, 0x21, 0xb5, 0xdd, 0x91, 0x13, 0x45, 0x1b, 0xf8, 0x91, 0xe2, 0xc0, 0x8b, 0x36, 0x86, + 0x21, 0xbb, 0xaa, 0x29, 0x99, 0xba, 0x34, 0x84, 0xb9, 0x44, 0xd3, 0x09, 0x26, 0xba, 0x40, 0x78, + 0x64, 0x1a, 0x00, 0x35, 0x6f, 0x6f, 0x9a, 0xfc, 0xbb, 0xef, 0xf8, 0xa3, 0x29, 0xc1, 0x91, 0x5d, + 0xe5, 0xdf, 0x7e, 0x40, 0x31, 0x21, 0xd3, 0x09, 0xc5, 0x5e, 0x52, 0x46, 0x8c, 0xe3, 0xbd, 0x67, + 0x9f, 0x24, 0xed, 0x80, 0x08, 0xec, 0xc8, 0x8f, 0x26, 0x30, 0x28, 0xec, 0x5d, 0xc2, 0x91, 0x0b, + 0x5c, 0x2c, 0x5b, 0x99, 0xe0, 0xc0, 0xf3, 0x83, 0xc1, 0x81, 0x7b, 0x27, 0x53, 0xe0, 0x18, 0xcc, + 0x92, 0x16, 0xdd, 0x30, 0x08, 0xb0, 0x4b, 0xd5, 0x81, 0x78, 0xc4, 0xf1, 0x83, 0x2b, 0x27, 0xba, + 0xe3, 0xda, 0x4d, 0xde, 0x09, 0x76, 0x22, 0xe6, 0xbc, 0x4a, 0x94, 0xc4, 0x88, 0xc3, 0xea, 0x83, + 0x1f, 0x51, 0x9c, 0x99, 0x0a, 0x18, 0x30, 0xb1, 0x2c, 0x1b, 0xdb, 0x7b, 0x73, 0x5c, 0xb9, 0xc3, + 0x33, 0xdb, 0xb6, 0x6f, 0xe9, 0xd6, 0x96, 0x3a, 0xf3, 0xad, 0xad, 0x4d, 0x5e, 0xfa, 0x21, 0x88, + 0x3e, 0x40, 0xfb, 0xba, 0x21, 0x0a, 0xf0, 0x07, 0x4d, 0x58, 0xda, 0x87, 0x30, 0x62, 0x00, 0xe6, + 0x78, 0x54, 0x8c, 0x56, 0x37, 0x98, 0xde, 0x5f, 0x57, 0x5b, 0x7b, 0x7a, 0xca, 0xcd, 0xc7, 0x10, + 0xac, 0x90, 0x3a, 0xc3, 0x6a, 0x07, 0xb6, 0x93, 0xe4, 0xbb, 0xf2, 0xe3, 0x7f, 0x7a, 0xd2, 0x1f, + 0xfc, 0xc0, 0x0b, 0x1f, 0xc0, 0x43, 0xe8, 0x18, 0x6c, 0x64, 0x78, 0x16, 0x26, 0xba, 0x26, 0xca, + 0x6a, 0xb9, 0x09, 0x1a, 0xcb, 0x20, 0x51, 0x35, 0xd0, 0xa6, 0x82, 0x13, 0xdd, 0x91, 0xe3, 0x8f, + 0xc5, 0x24, 0x33, 0xaf, 0xbd, 0x51, 0xe2, 0x04, 0x11, 0xd8, 0xab, 0x96, 0x6f, 0xa7, 0xd1, 0x2c, + 0xa5, 0xcd, 0xe4, 0x18, 0x93, 0x5d, 0x52, 0xb8, 0x7a, 0xce, 0xad, 0x70, 0xcc, 0x24, 0xe1, 0x68, + 0x84, 0x39, 0x27, 0x7d, 0x90, 0x4d, 0xcb, 0xaf, 0x69, 0xfa, 0x79, 0x81, 0xa9, 0xe3, 0x07, 0xd8, + 0x63, 0x8f, 0xd1, 0x01, 0x83, 0xac, 0x0e, 0x38, 0xa9, 0xf0, 0xf4, 0xb4, 0x99, 0x05, 0xf3, 0xc2, + 0xa2, 0x89, 0x33, 0xd5, 0x51, 0xc7, 0x27, 0x77, 0x5f, 0x38, 0x1a, 0xa5, 0xbd, 0xc2, 0x97, 0xae, + 0x86, 0xa5, 0xd5, 0xc4, 0xda, 0x30, 0xa3, 0xe1, 0xbe, 0x1f, 0x38, 0xa3, 0xd1, 0x6c, 0x71, 0xd1, + 0xcc, 0x38, 0x59, 0xfe, 0x51, 0x18, 0xa5, 0x6f, 0x55, 0xa7, 0xfd, 0x75, 0x0a, 0x0f, 0xd4, 0x4a, + 0x50, 0xed, 0xf3, 0x04, 0x78, 0x61, 0x82, 0xcc, 0xe6, 0x45, 0x13, 0x17, 0xc7, 0x3c, 0xce, 0xe5, + 0xcb, 0x29, 0xc9, 0x6c, 0x2c, 0xf6, 0x2c, 0x8b, 0xe1, 0x9d, 0xcc, 0x27, 0x39, 0xe4, 0x98, 0x9d, + 0x1c, 0x01, 0xa9, 0xe2, 0xcd, 0xb2, 0xb5, 0x25, 0x76, 0x1d, 0xf7, 0xff, 0xfc, 0xce, 0x8d, 0x67, + 0xc6, 0x30, 0x6f, 0xd1, 0x61, 0x82, 0x3d, 0xb1, 0xfd, 0xf3, 0xe5, 0xd9, 0x69, 0x65, 0xe2, 0x90, + 0x08, 0xeb, 0x85, 0x88, 0xf4, 0x96, 0x1a, 0x4f, 0x4f, 0x1a, 0x1c, 0xee, 0x64, 0x13, 0x6e, 0xc0, + 0xdc, 0x66, 0x13, 0x1c, 0xf6, 0xc1, 0xbf, 0x0e, 0xae, 0x23, 0xdb, 0xd6, 0x22, 0x86, 0xce, 0xb5, + 0xad, 0xad, 0xd3, 0xe9, 0xf8, 0x16, 0x93, 0x8a, 0x1f, 0x81, 0x70, 0x83, 0x3d, 0xe9, 0x8c, 0x1f, + 0x27, 0x3e, 0xc1, 0x91, 0xb1, 0x8f, 0x79, 0x04, 0xe2, 0x0c, 0x9e, 0x64, 0x29, 0xb1, 0xb2, 0x6d, + 0xf2, 0xab, 0x28, 0x92, 0x93, 0xbe, 0xf7, 0x65, 0x97, 0xf6, 0x31, 0xdd, 0xda, 0x4a, 0x5a, 0xdf, + 0x4b, 0x95, 0x18, 0x31, 0xa3, 0x53, 0xd4, 0xa9, 0xae, 0xc2, 0xe4, 0xb7, 0x14, 0x31, 0x18, 0xf0, + 0x09, 0xf8, 0xfd, 0x99, 0x3e, 0x67, 0x1d, 0x58, 0xc7, 0x14, 0x89, 0xb6, 0xad, 0xb4, 0xed, 0xed, + 0xf7, 0x24, 0x36, 0x24, 0x69, 0x9a, 0x0e, 0x3a, 0x33, 0x27, 0xd8, 0x99, 0x72, 0x63, 0xa6, 0xc7, + 0x7b, 0xdd, 0xbc, 0x60, 0x2e, 0xac, 0xe3, 0xcd, 0xe5, 0x33, 0x93, 0xa7, 0x4c, 0x9d, 0xe0, 0xd3, + 0x93, 0x10, 0xd1, 0xfd, 0x89, 0xf5, 0x36, 0xa0, 0x97, 0x85, 0x73, 0xb8, 0xbf, 0x69, 0x5a, 0x09, + 0x66, 0x9b, 0xbc, 0xc3, 0x0e, 0xa1, 0xb7, 0xd8, 0xa1, 0xc9, 0x45, 0x91, 0xe2, 0x70, 0x80, 0x09, + 0x5c, 0x2f, 0xf7, 0xce, 0x88, 0xf3, 0xb6, 0xfa, 0x42, 0x6b, 0x45, 0x43, 0x58, 0x8e, 0x9f, 0xd1, + 0x1d, 0x31, 0xe0, 0x29, 0xb9, 0x38, 0xd7, 0x33, 0xe7, 0xf1, 0x92, 0xbe, 0x72, 0xe3, 0x30, 0x0a, + 0x2f, 0x97, 0x78, 0xa1, 0xf9, 0xf9, 0xf2, 0x49, 0x25, 0x77, 0x89, 0xfe, 0x1d, 0x38, 0x00, 0xad, + 0x40, 0xc8, 0x40, 0xf6, 0xa9, 0x67, 0xb8, 0x70, 0x2c, 0x0c, 0x89, 0xe4, 0x57, 0x3c, 0xbb, 0x81, + 0x33, 0x7b, 0x92, 0x9f, 0x74, 0x79, 0xee, 0x24, 0x2a, 0x89, 0x47, 0xd8, 0xb9, 0xc7, 0xe7, 0xdc, + 0xd1, 0xef, 0xbb, 0xa7, 0x93, 0x1d, 0xb6, 0x12, 0x57, 0x34, 0xfb, 0xe8, 0x6b, 0x12, 0xd2, 0x7b, + 0xc3, 0x0f, 0x22, 0xea, 0x04, 0x2e, 0x1c, 0xf7, 0x94, 0xea, 0x83, 0xb3, 0x9c, 0x25, 0x03, 0xb1, + 0x88, 0xd7, 0x22, 0x9c, 0x6c, 0x9f, 0x9e, 0xe4, 0xaf, 0xbd, 0x69, 0x68, 0xd0, 0x21, 0x09, 0x1f, + 0x18, 0x9f, 0xca, 0x22, 0x97, 0xeb, 0xda, 0xfb, 0xe0, 0xde, 0x19, 0xf9, 0xde, 0x86, 0x1b, 0x8e, + 0x27, 0x4e, 0x00, 0x54, 0x1d, 0x7b, 0xa5, 0x6c, 0x23, 0xf2, 0xbf, 0x61, 0x4e, 0xae, 0x6d, 0xa6, + 0x33, 0x5b, 0xac, 0xde, 0x4d, 0xaa, 0x8d, 0xfc, 0xe0, 0x6e, 0x83, 0xd9, 0x3d, 0xfb, 0xa3, 0xd1, + 0x06, 0xc3, 0xd2, 0x3e, 0x93, 0x28, 0x26, 0x4d, 0xa8, 0x47, 0x81, 0x37, 0x54, 0xbc, 0x59, 0x91, + 0xd2, 0xfe, 0x41, 0x10, 0x02, 0xc3, 0xb6, 0x41, 0x9d, 0xdb, 0x0d, 0xa8, 0xbf, 0x41, 0x87, 0x58, + 0x19, 0x2b, 0x74, 0xaa, 0xf1, 0xd5, 0xe5, 0x7e, 0x68, 0xfc, 0x04, 0xf6, 0x31, 0xf0, 0x99, 0xda, + 0x6b, 0x67, 0xe2, 0xbf, 0x66, 0xd3, 0xd1, 0xd0, 0x7c, 0x8c, 0xe9, 0x30, 0xf4, 0x2c, 0xed, 0xfc, + 0xec, 0xf2, 0x4a, 0x03, 0x26, 0xc2, 0xc3, 0x24, 0xb2, 0xe6, 0x1a, 0xac, 0x0f, 0x0e, 0x68, 0x19, + 0x38, 0x37, 0xcd, 0xd2, 0x9c, 0xc9, 0x64, 0xe4, 0xbb, 0x0e, 0xd0, 0xb7, 0xaf, 0x43, 0x97, 0x62, + 0x5a, 0x8e, 0x28, 0xc1, 0xce, 0x58, 0x43, 0xda, 0x6f, 0xe5, 0x8b, 0x6e, 0xb7, 0x55, 0xbe, 0x14, + 0xb4, 0xa6, 0x95, 0xa5, 0xf8, 0x62, 0x74, 0x1b, 0x7a, 0x33, 0x2b, 0x42, 0xae, 0xe3, 0x0e, 0xb1, + 0xa5, 0x05, 0x61, 0x19, 0x90, 0x3f, 0xd6, 0x90, 0x4b, 0xb0, 0x87, 0x03, 0xea, 0x3b, 0xa3, 0xc8, + 0xd2, 0x22, 0x67, 0x8c, 0xcb, 0x21, 0xf1, 0x07, 0x7e, 0xa0, 0x21, 0x26, 0xc5, 0x1c, 0x59, 0x45, + 0x9b, 0x85, 0x4b, 0x38, 0x47, 0x31, 0x87, 0x5f, 0x50, 0x09, 0xef, 0x16, 0x80, 0xff, 0x95, 0xed, + 0x90, 0x0d, 0x98, 0xd0, 0x06, 0x10, 0xa7, 0xd8, 0xdb, 0xd0, 0x5f, 0xcd, 0x03, 0x61, 0xce, 0x1d, + 0x1b, 0x5f, 0x45, 0x20, 0xf6, 0xb1, 0x43, 0xee, 0xde, 0x61, 0x67, 0x44, 0x87, 0x19, 0x0f, 0x2d, + 0xd1, 0x1c, 0xd3, 0x42, 0x6c, 0xda, 0xb6, 0xc6, 0xe8, 0x12, 0xd6, 0xb2, 0xb6, 0xb5, 0xa5, 0x07, + 0x15, 0x20, 0xac, 0xaf, 0xe4, 0x61, 0x7b, 0xc3, 0x89, 0xdf, 0x84, 0x70, 0x80, 0x36, 0x45, 0x9a, + 0x1e, 0x18, 0x06, 0x0a, 0x24, 0xa6, 0x65, 0xa0, 0xe7, 0x5b, 0x57, 0x62, 0xda, 0xf9, 0x81, 0xeb, + 0xe2, 0x09, 0xfd, 0x1e, 0xd8, 0x76, 0x72, 0xb4, 0xf2, 0xd6, 0x96, 0x8e, 0xaf, 0x65, 0xa5, 0x03, + 0xf7, 0x4e, 0xbb, 0xb1, 0x05, 0x1b, 0x93, 0x2b, 0x68, 0xc8, 0x87, 0x7a, 0x97, 0x6f, 0x0c, 0xb9, + 0x15, 0xf0, 0xdf, 0xb1, 0x72, 0x91, 0x58, 0x0d, 0xdb, 0xb6, 0x6b, 0xd5, 0x86, 0xa1, 0x1a, 0x35, + 0x2c, 0x52, 0xfe, 0x99, 0x85, 0x42, 0x9b, 0xa6, 0x38, 0xc7, 0xcb, 0x97, 0x1e, 0x68, 0x1d, 0x65, + 0xe9, 0x23, 0x75, 0xe9, 0xf9, 0x79, 0xe0, 0x44, 0x80, 0x1e, 0x55, 0xc4, 0x2c, 0x81, 0x9c, 0xd0, + 0x53, 0x68, 0xff, 0xa9, 0x19, 0x7c, 0x83, 0x25, 0xc4, 0x02, 0x5c, 0x05, 0x03, 0x4c, 0xf4, 0xc0, + 0x78, 0x7a, 0x0a, 0x7e, 0x32, 0x9f, 0x9e, 0x82, 0x3d, 0x45, 0x9a, 0xb0, 0x14, 0x7d, 0x08, 0xa4, + 0x21, 0x98, 0x1b, 0xcd, 0xe8, 0xa4, 0x2f, 0x4f, 0x2b, 0xa8, 0x89, 0x2f, 0x42, 0x54, 0x61, 0x4a, + 0x6e, 0xae, 0xa7, 0xd2, 0xc5, 0x00, 0xc2, 0x04, 0x5d, 0x85, 0xcf, 0x41, 0x57, 0x8c, 0x05, 0xe7, + 0xbd, 0xa6, 0xe4, 0x50, 0xb0, 0x69, 0xdb, 0x2b, 0x58, 0xae, 0xc4, 0xdc, 0x21, 0xf7, 0xbc, 0x73, + 0xb8, 0x9a, 0x51, 0x0b, 0x50, 0x72, 0xad, 0x57, 0x0d, 0xf5, 0xb7, 0xb1, 0xc0, 0xc1, 0x05, 0x85, + 0xeb, 0x58, 0x95, 0xda, 0xd0, 0x22, 0xa2, 0x7e, 0xce, 0xb4, 0x93, 0xea, 0x65, 0xd2, 0xe1, 0xcc, + 0xd3, 0x12, 0x2a, 0x7e, 0x19, 0xce, 0xdc, 0x34, 0x25, 0xeb, 0xa4, 0x72, 0x09, 0xea, 0x69, 0x14, + 0x0d, 0x6c, 0x56, 0x05, 0x0a, 0xc0, 0x4a, 0x3f, 0x09, 0xf3, 0x80, 0xa5, 0x52, 0x52, 0x45, 0x07, + 0xb2, 0xa6, 0xd9, 0x59, 0x38, 0xfb, 0x58, 0x81, 0x08, 0x0b, 0x9b, 0x34, 0xf6, 0x03, 0xbd, 0xd6, + 0xac, 0x96, 0x6a, 0xa5, 0x52, 0xf2, 0x9d, 0x61, 0x9f, 0x51, 0xdd, 0x40, 0x35, 0x16, 0x4a, 0x29, + 0x4e, 0xa8, 0x34, 0x0e, 0x1f, 0xce, 0x7e, 0x3c, 0x1f, 0x20, 0xf3, 0x62, 0x50, 0x74, 0xb8, 0xd2, + 0x36, 0xc7, 0x16, 0xa8, 0x90, 0x78, 0x7a, 0x4a, 0xc6, 0xfc, 0x21, 0x31, 0x46, 0x7a, 0x01, 0x38, + 0x78, 0x20, 0xb4, 0x1f, 0x0b, 0x8b, 0x38, 0xdb, 0xd2, 0x3c, 0x53, 0x6e, 0xdb, 0xce, 0xc9, 0x20, + 0xf6, 0xec, 0xba, 0xe4, 0xce, 0x15, 0x31, 0x84, 0xdc, 0xe0, 0x19, 0xc9, 0x44, 0xb5, 0x98, 0x78, + 0x02, 0x4c, 0x81, 0x35, 0xa4, 0x11, 0x9c, 0xb2, 0x72, 0x20, 0x3e, 0x31, 0xe2, 0xcc, 0xee, 0x55, + 0x89, 0x65, 0xa5, 0xd9, 0xce, 0x73, 0x24, 0x22, 0x92, 0xf3, 0x62, 0xa4, 0x83, 0xf8, 0x9d, 0x1f, + 0x06, 0xc1, 0x0c, 0xd5, 0x02, 0x7f, 0x19, 0xa3, 0x89, 0x3d, 0x4f, 0x18, 0x34, 0x6b, 0xd3, 0x44, + 0x70, 0xbb, 0x73, 0x67, 0x0c, 0x2b, 0x37, 0xce, 0x08, 0xa4, 0x54, 0xd8, 0x3b, 0xa0, 0x0a, 0x47, + 0x80, 0xee, 0x7d, 0xfc, 0x60, 0x69, 0xc3, 0x10, 0xb0, 0x7a, 0x84, 0x47, 0x7d, 0x8b, 0x1b, 0x7f, + 0x33, 0xb7, 0x77, 0xfe, 0x5b, 0x3c, 0xaf, 0x63, 0x55, 0x91, 0x2b, 0x5f, 0xd0, 0xb8, 0xbe, 0x91, + 0x8f, 0x8c, 0xb0, 0xdf, 0x63, 0x11, 0x28, 0x0f, 0x7e, 0x07, 0xd8, 0x21, 0xb7, 0x33, 0xf8, 0x15, + 0xe1, 0x11, 0x1f, 0x94, 0xa6, 0xf1, 0xc7, 0x31, 0x78, 0x73, 0xc2, 0x17, 0x89, 0x7f, 0x90, 0xfe, + 0xa5, 0x33, 0x9e, 0x8c, 0x64, 0x55, 0xfa, 0x10, 0x92, 0x3b, 0x9e, 0x33, 0x1d, 0x51, 0x7f, 0x0c, + 0xcf, 0x5a, 0x65, 0xbe, 0xde, 0xf9, 0x30, 0x73, 0xd6, 0xbc, 0x4c, 0xba, 0xc4, 0x14, 0xa6, 0x27, + 0x1a, 0x1c, 0xf2, 0x02, 0xa7, 0xf0, 0xc2, 0x9d, 0xa6, 0xa1, 0xb1, 0x33, 0x39, 0x61, 0x3f, 0x27, + 0x23, 0xc7, 0xc5, 0x91, 0x16, 0xa3, 0x77, 0x0c, 0xbd, 0x1e, 0x52, 0x34, 0x09, 0x6d, 0x21, 0x4d, + 0x04, 0x2b, 0xe9, 0x70, 0x74, 0x0f, 0xac, 0xec, 0x05, 0x85, 0x35, 0x38, 0x8f, 0xe0, 0x6f, 0x0f, + 0xc4, 0x4d, 0x57, 0xd4, 0xae, 0xa6, 0xb2, 0x3b, 0x0f, 0xeb, 0x54, 0xae, 0xee, 0x24, 0xac, 0xd0, + 0x21, 0x0e, 0x38, 0xa7, 0xc1, 0xae, 0x4b, 0x14, 0xd8, 0xaa, 0x8c, 0x52, 0x5a, 0xbb, 0x44, 0x0b, + 0x3a, 0xb7, 0x54, 0x2c, 0xbe, 0x40, 0x10, 0xba, 0x5c, 0x0b, 0xb0, 0x01, 0x0a, 0x33, 0x6f, 0x83, + 0xa9, 0xcc, 0x3a, 0x3e, 0xa3, 0x2a, 0x84, 0x7e, 0x40, 0x34, 0x04, 0x5b, 0x94, 0x99, 0x8d, 0xa0, + 0x1e, 0x31, 0xe2, 0x04, 0x99, 0x27, 0x33, 0x72, 0x5c, 0xac, 0x5f, 0x2f, 0xcc, 0x8f, 0x8f, 0x98, + 0x1a, 0x28, 0xb8, 0x61, 0x06, 0x10, 0x20, 0x9a, 0x60, 0x43, 0xca, 0x28, 0xad, 0x22, 0x43, 0x69, + 0x71, 0x02, 0x2f, 0x57, 0xf0, 0xc3, 0xce, 0xc6, 0x0e, 0x4f, 0x34, 0x88, 0xef, 0x08, 0x58, 0xac, + 0x68, 0xbf, 0x60, 0x74, 0x4f, 0x4f, 0x22, 0x39, 0x4f, 0x0a, 0x19, 0x5b, 0x5b, 0x47, 0x60, 0x64, + 0x03, 0x02, 0xc7, 0x04, 0xaa, 0x90, 0x02, 0x68, 0xe4, 0x3c, 0x7a, 0x7a, 0xea, 0xe1, 0x3d, 0xfb, + 0x20, 0x32, 0xe6, 0xfc, 0xdf, 0xad, 0xad, 0x0f, 0xba, 0xc6, 0xe8, 0x7a, 0x16, 0x88, 0x0e, 0x28, + 0xe5, 0x89, 0x33, 0x8d, 0xb0, 0x57, 0xd9, 0xb8, 0x72, 0x26, 0x8c, 0xac, 0x65, 0x14, 0xb4, 0x70, + 0x1b, 0xa4, 0xe1, 0x06, 0x01, 0xf7, 0x88, 0x8a, 0x86, 0x34, 0xcc, 0xb1, 0x8e, 0x94, 0x3c, 0xc1, + 0x92, 0x56, 0x51, 0x0f, 0x03, 0x52, 0xe8, 0x62, 0x3d, 0x7b, 0x32, 0x0c, 0xf4, 0x41, 0xff, 0x7a, + 0x81, 0xdd, 0xf0, 0x1e, 0x33, 0xa5, 0x63, 0x96, 0x54, 0x06, 0x22, 0xa1, 0x87, 0xe3, 0xd7, 0xaf, + 0xe6, 0x07, 0x40, 0x24, 0x20, 0xed, 0xc1, 0x21, 0x81, 0x06, 0x0f, 0x69, 0x24, 0xab, 0x9a, 0x1a, + 0x78, 0x70, 0xa1, 0xee, 0x3b, 0x55, 0xfe, 0x52, 0x11, 0x02, 0x1e, 0xb1, 0xb9, 0x1e, 0xa0, 0xf3, + 0x3e, 0xc1, 0xd1, 0xb0, 0x7c, 0x3b, 0xa5, 0x14, 0xe4, 0xbb, 0x15, 0xe9, 0x65, 0x0e, 0xf9, 0xc9, + 0x06, 0xfd, 0x10, 0xea, 0xef, 0x24, 0x72, 0x7c, 0x57, 0x49, 0x84, 0x84, 0x82, 0xaf, 0x82, 0x49, + 0x84, 0xfd, 0xfe, 0xc8, 0x0f, 0x30, 0x1b, 0xbf, 0xb2, 0x87, 0x08, 0x9f, 0xc9, 0x4c, 0x10, 0x39, + 0x29, 0x30, 0x12, 0x69, 0x14, 0xdb, 0xe0, 0x71, 0x8c, 0xaa, 0x46, 0xba, 0x0a, 0xc7, 0x3a, 0x45, + 0xd8, 0x98, 0x3f, 0xe8, 0xd4, 0xa8, 0xc0, 0x13, 0x20, 0x82, 0xc4, 0xb7, 0xf1, 0xfe, 0xbe, 0xf6, + 0x1f, 0xff, 0xfe, 0x7f, 0xb4, 0xb4, 0x24, 0xc1, 0x50, 0xd4, 0xa6, 0xfb, 0x82, 0xe6, 0xd8, 0xdf, + 0x4f, 0x75, 0xdd, 0x74, 0x9f, 0xc9, 0xca, 0x2b, 0xd1, 0xc8, 0x77, 0x31, 0x04, 0xb3, 0x36, 0xb8, + 0xd8, 0x3c, 0xb2, 0xf7, 0xa2, 0x67, 0x8a, 0xcd, 0x2d, 0x4d, 0xe9, 0xeb, 0x8c, 0xa4, 0x27, 0xae, + 0x66, 0xb6, 0x5a, 0x66, 0xbd, 0x55, 0x6b, 0x99, 0x4c, 0x6d, 0xc2, 0x49, 0xd4, 0x6a, 0x27, 0xfa, + 0x89, 0x8a, 0x81, 0x74, 0x00, 0xe7, 0x1b, 0x98, 0x87, 0x13, 0xf6, 0xc7, 0xd3, 0x91, 0x8e, 0xff, + 0x8d, 0x56, 0xdc, 0xa1, 0xc3, 0x5e, 0xbc, 0x3c, 0x80, 0x9d, 0x8d, 0xcc, 0xd6, 0xce, 0xce, 0x4e, + 0xcb, 0xdc, 0x95, 0x3b, 0x43, 0xc7, 0x7b, 0x7b, 0x7b, 0x55, 0x23, 0x33, 0x36, 0x65, 0x6b, 0x2a, + 0x2a, 0xb1, 0x49, 0x05, 0xf0, 0xe3, 0x7e, 0x1a, 0x53, 0x69, 0xff, 0x2b, 0x13, 0xa3, 0x0b, 0x94, + 0x53, 0x7e, 0x35, 0x27, 0x58, 0xe7, 0x85, 0xd2, 0x32, 0x46, 0xfc, 0x35, 0x33, 0x9f, 0x5e, 0xda, + 0x60, 0xb6, 0xb6, 0x20, 0x88, 0xca, 0xaf, 0xe6, 0xef, 0x14, 0x12, 0xfe, 0x6b, 0x5a, 0x73, 0x1c, + 0x02, 0xd4, 0x55, 0x03, 0xbb, 0xaf, 0xaf, 0xe6, 0x34, 0xb6, 0x5e, 0xcd, 0x31, 0xfc, 0x89, 0xe0, + 0xcf, 0x19, 0x90, 0xa0, 0x6a, 0xa5, 0xf7, 0x38, 0xd1, 0x7b, 0xc0, 0x54, 0x9e, 0x9e, 0xa0, 0x7f, + 0xce, 0xf9, 0x15, 0x49, 0x93, 0x16, 0x84, 0x49, 0x93, 0x8a, 0xc4, 0xed, 0x62, 0x45, 0xcb, 0x11, + 0x35, 0x8c, 0x94, 0xb7, 0x4f, 0x3a, 0x9a, 0x85, 0xb0, 0x4e, 0xcf, 0x17, 0xcc, 0x31, 0xb9, 0xdc, + 0xf5, 0x4d, 0x4a, 0x86, 0x32, 0x5a, 0xb7, 0xe2, 0x47, 0x92, 0x1d, 0xdf, 0xc7, 0x95, 0xbe, 0x3f, + 0xa2, 0x40, 0x86, 0xdb, 0x7b, 0x51, 0x22, 0xb2, 0x8b, 0x2a, 0xbe, 0xa7, 0xca, 0xeb, 0x92, 0x64, + 0x26, 0x8d, 0x2f, 0x48, 0x87, 0xad, 0x9c, 0x66, 0x18, 0xca, 0x34, 0xe4, 0xbe, 0xd4, 0xe7, 0xbe, + 0x67, 0x41, 0xbb, 0xc9, 0xa6, 0x35, 0x41, 0x12, 0x75, 0x87, 0x67, 0x16, 0x6b, 0x36, 0x4d, 0xae, + 0x56, 0xc5, 0xeb, 0x38, 0xbc, 0xd9, 0x24, 0xe3, 0x92, 0x96, 0xe0, 0x75, 0xd2, 0xe4, 0x49, 0x9d, + 0xbc, 0x10, 0x31, 0xaa, 0x24, 0x79, 0xc6, 0xbe, 0xf2, 0x61, 0x55, 0x11, 0xf7, 0x40, 0xf5, 0xc3, + 0xc0, 0x8a, 0x2a, 0xc9, 0x6f, 0x20, 0x9d, 0x00, 0xf5, 0xef, 0xb3, 0xbf, 0x96, 0xc6, 0x78, 0x27, + 0x7e, 0xa5, 0x27, 0xf3, 0x92, 0x1c, 0x92, 0x9c, 0xd9, 0xbe, 0x4c, 0x4a, 0x87, 0xdb, 0x32, 0x18, + 0x05, 0xc0, 0xe8, 0x75, 0x0d, 0x4d, 0x03, 0x20, 0x29, 0xac, 0xcd, 0xcd, 0xa8, 0xc2, 0x7f, 0x32, + 0x0d, 0xe0, 0xe2, 0x50, 0xa3, 0x80, 0xc0, 0x20, 0x21, 0xf3, 0x3e, 0xf4, 0xbd, 0x8d, 0x6a, 0xfe, + 0xcd, 0xcf, 0x05, 0xae, 0x27, 0xca, 0xb9, 0x85, 0x41, 0xf5, 0x5c, 0x1d, 0xde, 0x52, 0x6c, 0x18, + 0xd6, 0xf5, 0x4d, 0x46, 0xda, 0x78, 0x7d, 0xa3, 0x6c, 0xa2, 0x53, 0x92, 0xd9, 0xad, 0x5c, 0x08, + 0xf2, 0xf4, 0x34, 0xa9, 0x28, 0xb7, 0xba, 0x6d, 0xdb, 0x54, 0xca, 0xe6, 0xb3, 0x19, 0x52, 0xfb, + 0x05, 0x08, 0x88, 0x6d, 0x47, 0xc4, 0x7f, 0xf4, 0x98, 0x7a, 0xb6, 0x52, 0xa9, 0xa4, 0x9b, 0xf9, + 0x06, 0x45, 0x0c, 0xcb, 0x5e, 0x62, 0xda, 0x49, 0x53, 0xed, 0x64, 0xcb, 0x05, 0xf6, 0xde, 0x26, + 0x10, 0xc9, 0x91, 0x1e, 0x54, 0x7c, 0x30, 0xf9, 0x66, 0xe1, 0xd5, 0xf8, 0x87, 0xba, 0x85, 0x16, + 0x4f, 0x92, 0x22, 0x03, 0x63, 0xfd, 0xca, 0x93, 0x02, 0x07, 0x31, 0x9d, 0xe8, 0x01, 0x85, 0xd3, + 0x22, 0x4e, 0xb1, 0x3b, 0xb4, 0x5e, 0xcd, 0x19, 0x9a, 0x12, 0x2f, 0x3b, 0xa9, 0x07, 0xf8, 0x0e, + 0x2b, 0x25, 0xbd, 0xb1, 0xc5, 0x90, 0x0c, 0xcd, 0xe0, 0x17, 0x05, 0x59, 0x46, 0x3a, 0xb5, 0xb5, + 0x7d, 0x2d, 0xc5, 0xc6, 0x15, 0x4a, 0xfc, 0xb1, 0x6e, 0x54, 0xa2, 0xc9, 0xc8, 0xa7, 0xfa, 0xeb, + 0xff, 0x27, 0xda, 0x7e, 0x6d, 0x24, 0x1b, 0xa4, 0x66, 0x24, 0x1a, 0xcd, 0xeb, 0xea, 0x4d, 0x8a, + 0x80, 0x2b, 0x34, 0xfc, 0x38, 0x99, 0x60, 0xd2, 0x15, 0x22, 0x53, 0x6d, 0x5f, 0x41, 0x60, 0x58, + 0x19, 0xd0, 0x46, 0x7e, 0xf7, 0x50, 0x63, 0x9f, 0x56, 0x68, 0xf8, 0x01, 0x20, 0x82, 0xe5, 0xe3, + 0x51, 0x56, 0xee, 0xfa, 0x18, 0x84, 0x4a, 0x0b, 0x74, 0x1f, 0xd6, 0x01, 0xe8, 0x51, 0x9d, 0x96, + 0xc0, 0x4d, 0x31, 0xa9, 0x0e, 0x77, 0xab, 0x68, 0xe2, 0xfa, 0x06, 0xcd, 0x87, 0xe1, 0x94, 0x58, + 0x5a, 0x30, 0x1d, 0x63, 0xe2, 0xbb, 0x1a, 0x1a, 0xfb, 0xc1, 0x94, 0x62, 0x4b, 0xab, 0x95, 0x3d, + 0x7f, 0xe0, 0x53, 0x2d, 0x36, 0x40, 0x04, 0xf1, 0xa0, 0x74, 0x13, 0x30, 0x18, 0xb3, 0x2d, 0x24, + 0xf6, 0x8b, 0xf6, 0xc7, 0x34, 0xa2, 0x1b, 0x50, 0x4a, 0xec, 0x14, 0xce, 0x5f, 0x38, 0x8f, 0x7a, + 0x15, 0x2d, 0x8f, 0x5e, 0x5b, 0xa6, 0xa9, 0x94, 0xfb, 0xa7, 0x56, 0x75, 0xff, 0x2b, 0x20, 0xdc, + 0x68, 0xc3, 0x19, 0x84, 0x5f, 0x2d, 0xfc, 0x53, 0xbd, 0x55, 0x65, 0x49, 0x4a, 0x7d, 0xfc, 0xba, + 0x55, 0x35, 0xe2, 0xb1, 0x2c, 0xd1, 0x6e, 0x35, 0x0a, 0x8a, 0x40, 0x3d, 0x23, 0x1e, 0xf2, 0x42, + 0xf9, 0x4c, 0x56, 0xc5, 0x88, 0x3d, 0x96, 0x9b, 0xce, 0xe8, 0x31, 0x4c, 0x8e, 0x87, 0x52, 0x5c, + 0x19, 0x6f, 0x79, 0x52, 0x49, 0x68, 0x7c, 0xe3, 0x75, 0x0b, 0x37, 0x92, 0x91, 0xd3, 0x9f, 0xcc, + 0x7d, 0xed, 0x34, 0x7c, 0xd0, 0x2c, 0x2a, 0xe6, 0x40, 0xe3, 0x71, 0xbe, 0x63, 0xca, 0x06, 0x3e, + 0xdc, 0x78, 0x35, 0xa7, 0xff, 0x68, 0x55, 0xe3, 0xb1, 0xd2, 0xf5, 0x6d, 0xb8, 0x7a, 0xd5, 0x75, + 0xfa, 0xda, 0xc4, 0x2d, 0x58, 0xbc, 0x37, 0xfe, 0x23, 0xf6, 0xf4, 0xfa, 0xc2, 0xaa, 0x7f, 0x24, + 0xcf, 0x68, 0xa1, 0x9e, 0xb6, 0x40, 0xff, 0x61, 0xe2, 0xfa, 0xbe, 0x69, 0x55, 0x17, 0x5a, 0x7a, + 0xa3, 0xb6, 0x44, 0xf7, 0xec, 0x72, 0xbb, 0xba, 0xaf, 0x1d, 0x3f, 0xba, 0x78, 0x34, 0xc2, 0x01, + 0xd5, 0x2c, 0x48, 0xda, 0x6d, 0xee, 0x6b, 0x6f, 0xc3, 0xd0, 0xe3, 0x5f, 0xa6, 0x59, 0xdd, 0xd7, + 0xde, 0x38, 0x3e, 0xd1, 0x2c, 0xed, 0x33, 0x76, 0xee, 0x94, 0xc6, 0xde, 0x47, 0x29, 0x7d, 0x21, + 0xc4, 0x3e, 0x74, 0x5f, 0x44, 0x3b, 0x34, 0x50, 0x94, 0x4b, 0x0b, 0x03, 0x09, 0xd1, 0xcd, 0x05, + 0x45, 0x11, 0xe8, 0x16, 0x16, 0xb0, 0x29, 0xe8, 0x37, 0x38, 0x5b, 0xeb, 0xdc, 0x82, 0x31, 0xcf, + 0xde, 0x6e, 0xf5, 0xe9, 0x29, 0xf9, 0x8e, 0x8c, 0x3d, 0xb8, 0x6a, 0x40, 0x2e, 0xf7, 0xda, 0x36, + 0x71, 0x0b, 0x45, 0xec, 0x1f, 0x03, 0x01, 0x96, 0xab, 0x6e, 0x6d, 0x81, 0x00, 0x4c, 0x2d, 0xbf, + 0xa4, 0xfe, 0x3e, 0x70, 0x3e, 0xd6, 0x7c, 0x04, 0x6f, 0x25, 0xa2, 0x11, 0x5c, 0x25, 0x2a, 0x4e, + 0x8d, 0x18, 0xb1, 0x66, 0xd6, 0xaa, 0xd9, 0xc3, 0xb0, 0x91, 0x98, 0x13, 0x46, 0xe9, 0x39, 0xa8, + 0xb5, 0xab, 0x08, 0xe8, 0x55, 0x1f, 0x07, 0xf4, 0xb3, 0xef, 0x81, 0x60, 0xa9, 0x0a, 0xaf, 0xb4, + 0x26, 0x05, 0x70, 0x92, 0xfd, 0x0e, 0x0c, 0x43, 0x28, 0xcb, 0x0f, 0xed, 0x84, 0x51, 0xe7, 0x7c, + 0xe3, 0xb9, 0xff, 0x88, 0x47, 0x17, 0x20, 0xb2, 0x7c, 0x7a, 0x32, 0x51, 0xcd, 0xe8, 0xd0, 0xca, + 0x03, 0xb4, 0xc6, 0xcb, 0x91, 0x70, 0x1a, 0x78, 0x7a, 0x54, 0x02, 0x09, 0x52, 0x65, 0xc8, 0x9a, + 0x51, 0x33, 0x82, 0x52, 0xc8, 0x05, 0x61, 0xc4, 0xa6, 0xf2, 0x01, 0x15, 0xfc, 0x48, 0x75, 0xad, + 0xe6, 0xa5, 0xd4, 0x02, 0xa9, 0x44, 0x80, 0x23, 0x74, 0xe6, 0xd6, 0x40, 0x2a, 0x8c, 0x7d, 0xb9, + 0x00, 0xf2, 0xb8, 0x8a, 0xaa, 0x8c, 0x44, 0x42, 0xec, 0x29, 0x04, 0xb8, 0xab, 0x09, 0x62, 0x7d, + 0x5b, 0x11, 0xe2, 0x7d, 0x59, 0x81, 0x02, 0x9d, 0x63, 0x41, 0x54, 0xd9, 0xb5, 0x86, 0x21, 0x84, + 0xe1, 0x0c, 0x60, 0xa6, 0x59, 0x15, 0x32, 0x64, 0x09, 0x2e, 0x66, 0x1a, 0x9f, 0x34, 0x1a, 0x8a, + 0x46, 0x89, 0x6c, 0xd4, 0x8f, 0xed, 0x00, 0x79, 0xec, 0xee, 0xc1, 0x4f, 0x4f, 0xd7, 0x37, 0x37, + 0x15, 0x82, 0xef, 0x31, 0x90, 0x3f, 0xe9, 0x7d, 0x01, 0x96, 0xf2, 0x11, 0x25, 0xe1, 0x1d, 0xbe, + 0xa4, 0xb3, 0x11, 0xb6, 0x35, 0x32, 0xb8, 0x75, 0x74, 0xb3, 0x61, 0x22, 0xb3, 0x59, 0x03, 0xc2, + 0x14, 0x55, 0x4c, 0xd3, 0xd0, 0x50, 0x58, 0x01, 0xaa, 0x9e, 0xc1, 0xdf, 0x4e, 0x49, 0xdd, 0x91, + 0x6d, 0x76, 0x46, 0x3f, 0x35, 0x3a, 0x23, 0xa0, 0x71, 0x85, 0x89, 0x90, 0x02, 0xb6, 0x51, 0x49, + 0xf7, 0xcb, 0xb5, 0x86, 0xf1, 0xba, 0x61, 0x6c, 0x57, 0x9a, 0x9d, 0xb0, 0x72, 0x8b, 0x07, 0x7e, + 0xc0, 0x2c, 0xf2, 0x0c, 0x14, 0x56, 0xe0, 0x56, 0xba, 0x0a, 0xf5, 0x2a, 0x04, 0x49, 0xe5, 0xed, + 0x5f, 0x85, 0x3a, 0xe1, 0x5f, 0x7c, 0x4c, 0xba, 0x01, 0xea, 0xf9, 0x4d, 0x4f, 0xbe, 0x3e, 0xa3, + 0xce, 0x7b, 0xc3, 0xb5, 0x3d, 0x76, 0x57, 0x8c, 0x80, 0x5c, 0x22, 0x8f, 0x82, 0x06, 0xd0, 0x47, + 0xd7, 0xe6, 0x8d, 0xf1, 0xf4, 0x54, 0x45, 0x54, 0x49, 0xaa, 0xf1, 0x24, 0x60, 0x3e, 0xd2, 0xc4, + 0x3a, 0x4b, 0x04, 0xe5, 0xe3, 0x34, 0xdd, 0x4d, 0x26, 0xdc, 0xca, 0x2e, 0x44, 0x24, 0xa5, 0x27, + 0xbc, 0xf1, 0xeb, 0x51, 0x85, 0x3c, 0xa2, 0x51, 0x85, 0xc2, 0x1f, 0x68, 0xe1, 0xc6, 0x00, 0xdb, + 0x29, 0xf2, 0xda, 0x95, 0xd1, 0x4f, 0x87, 0x4a, 0xf5, 0x4a, 0x13, 0x25, 0x3b, 0xaf, 0x8d, 0xfa, + 0xa5, 0x4a, 0x0d, 0x5c, 0xc8, 0xa4, 0x35, 0x4f, 0xa5, 0x1f, 0x92, 0x63, 0x07, 0xf8, 0xd6, 0x11, + 0x1a, 0x4b, 0xde, 0x6c, 0x60, 0x8f, 0x4b, 0xfd, 0x6d, 0xbd, 0x5f, 0x1e, 0x96, 0xea, 0xc6, 0xeb, + 0x5a, 0xe7, 0x9a, 0xf7, 0xa8, 0xfd, 0x4b, 0xa3, 0xd1, 0xaf, 0xb6, 0x1d, 0xed, 0x06, 0x5d, 0xb3, + 0xde, 0xb5, 0x7f, 0x69, 0xd4, 0x9c, 0x66, 0xbf, 0xcf, 0x13, 0x60, 0x24, 0x48, 0xfb, 0x97, 0x7e, + 0xbf, 0xd9, 0x68, 0xb5, 0xb5, 0x9b, 0x9b, 0xb4, 0xe9, 0xeb, 0x47, 0x34, 0xbb, 0x41, 0x5d, 0xd9, + 0xfc, 0x7d, 0x3a, 0xba, 0xc7, 0xfd, 0x9a, 0x55, 0x45, 0x8f, 0xaf, 0xa7, 0x6c, 0x61, 0x5a, 0x06, + 0xac, 0x7e, 0xdf, 0x1f, 0x8d, 0xf8, 0xda, 0xcf, 0x50, 0x58, 0x19, 0x8c, 0xc2, 0x5b, 0x67, 0x74, + 0x30, 0x9a, 0x0c, 0x1d, 0xbb, 0xd2, 0x6e, 0x20, 0x9e, 0xcf, 0xb6, 0xf1, 0x60, 0xbb, 0x5b, 0x1a, + 0x22, 0xbf, 0x6c, 0xd6, 0xca, 0xf7, 0x48, 0x81, 0xd7, 0xb0, 0x6c, 0x1a, 0xe8, 0x1e, 0x9c, 0x29, + 0x8c, 0x5c, 0x03, 0x26, 0x02, 0x5b, 0x30, 0x6f, 0xea, 0x62, 0x39, 0xdf, 0xd1, 0xf6, 0xb8, 0x42, + 0x1e, 0xb7, 0xc7, 0x30, 0x1d, 0x95, 0xd1, 0x3b, 0xe0, 0x5b, 0x5e, 0xee, 0xf7, 0xc4, 0xee, 0xca, + 0x25, 0xe0, 0x18, 0x28, 0x4c, 0xaf, 0x74, 0xaa, 0xea, 0x76, 0xf5, 0xa0, 0xc2, 0x8c, 0x80, 0xd8, + 0x8b, 0x48, 0x58, 0xf1, 0x01, 0xd3, 0x83, 0x0c, 0xaf, 0x18, 0x81, 0x4e, 0x23, 0x35, 0xba, 0x62, + 0x98, 0x47, 0x0b, 0xef, 0x34, 0xa9, 0x8e, 0x3b, 0x80, 0x80, 0x6a, 0xf7, 0x1a, 0xfa, 0x4a, 0x43, + 0x27, 0xa2, 0x1b, 0x70, 0x93, 0x7e, 0x35, 0x3a, 0x51, 0xc5, 0x99, 0x00, 0xfb, 0xa3, 0x1f, 0xe8, + 0x9a, 0xaf, 0x19, 0xe8, 0x40, 0xd7, 0xa2, 0x89, 0x13, 0x68, 0xe0, 0xd9, 0x02, 0x86, 0x5d, 0x0f, + 0xba, 0xc6, 0xca, 0x47, 0x9a, 0x21, 0x4b, 0x46, 0x19, 0xf6, 0x1b, 0x38, 0xef, 0x88, 0x8f, 0x10, + 0x6c, 0x06, 0x18, 0xad, 0xc6, 0x48, 0x67, 0x03, 0xd5, 0x6b, 0x40, 0xb4, 0x2f, 0x94, 0xe5, 0x64, + 0x9a, 0x6e, 0xa0, 0x7a, 0xb3, 0xaa, 0xc2, 0xa6, 0xab, 0x08, 0x78, 0x28, 0x90, 0xe1, 0xa9, 0x2d, + 0x47, 0x67, 0x52, 0x49, 0x3e, 0x6c, 0x8c, 0x26, 0x95, 0x54, 0xd0, 0x26, 0x48, 0xcf, 0xc8, 0xc6, + 0xfb, 0xa0, 0x19, 0x13, 0x15, 0x2c, 0xd6, 0x40, 0x46, 0xd0, 0xb0, 0xaf, 0x5d, 0xa8, 0x9f, 0xbc, + 0x04, 0xd3, 0xc8, 0xee, 0x67, 0x94, 0x76, 0x8e, 0x4b, 0xfd, 0x7b, 0x50, 0xac, 0x9d, 0x09, 0x0e, + 0xbf, 0x73, 0xac, 0x6b, 0xd0, 0x5f, 0x79, 0xe4, 0xdc, 0xe2, 0x91, 0x06, 0xbe, 0x71, 0xc7, 0xba, + 0x16, 0xf9, 0x1e, 0x2e, 0x73, 0xaa, 0x5f, 0x26, 0x0d, 0x31, 0x09, 0xcb, 0x4c, 0xc1, 0x07, 0x09, + 0x0f, 0x89, 0x31, 0x4a, 0x2a, 0x6b, 0x48, 0x17, 0xf2, 0x2b, 0x6b, 0xd1, 0x1d, 0xfa, 0x13, 0xb8, + 0xf2, 0xe3, 0xaf, 0x50, 0x9c, 0x35, 0xe9, 0x85, 0x34, 0x5b, 0x90, 0xf7, 0x01, 0xe9, 0xbc, 0xa4, + 0xc2, 0xcf, 0x4a, 0x2a, 0x8b, 0x72, 0xd2, 0x23, 0xfa, 0xec, 0xd3, 0xa1, 0xae, 0xb9, 0x43, 0x4b, + 0x33, 0xe4, 0xaa, 0x4f, 0x24, 0x6d, 0x1b, 0x25, 0x4e, 0x37, 0x07, 0x54, 0x0f, 0x0c, 0x46, 0xca, + 0xcb, 0x5d, 0x16, 0xed, 0xcf, 0x81, 0xe3, 0xa2, 0xfc, 0xc9, 0xdc, 0xaf, 0xff, 0x02, 0x8a, 0x19, + 0xf8, 0xf9, 0xf4, 0x24, 0x9f, 0x51, 0xdf, 0x80, 0xa4, 0x0c, 0x95, 0xfc, 0x15, 0x45, 0xd3, 0x5b, + 0xea, 0xd3, 0x11, 0xb6, 0xb4, 0x13, 0x1c, 0x0d, 0x37, 0x44, 0xae, 0x86, 0x9c, 0x7b, 0x87, 0x3a, + 0xc4, 0xd2, 0xfe, 0x45, 0x93, 0x82, 0x4a, 0x2b, 0x8a, 0xb9, 0xfd, 0x86, 0x90, 0xde, 0x55, 0xa4, + 0x30, 0x93, 0x8f, 0x29, 0xb2, 0xf7, 0xee, 0xe0, 0xbe, 0xce, 0x8c, 0x09, 0x67, 0xc6, 0x94, 0xc4, + 0x46, 0x7e, 0x7a, 0xd2, 0x3e, 0x06, 0x90, 0x04, 0x0a, 0x60, 0xd6, 0x88, 0x96, 0x0e, 0xe4, 0x2b, + 0x7f, 0xf9, 0x7a, 0xe3, 0xff, 0xfb, 0x7f, 0x37, 0x18, 0xad, 0xae, 0xc6, 0x60, 0x6e, 0x18, 0xf1, + 0x57, 0x39, 0xb4, 0x33, 0x70, 0x7e, 0x15, 0x0d, 0x1a, 0x52, 0xb2, 0x6a, 0x61, 0x31, 0xc8, 0x04, + 0xbe, 0xef, 0x14, 0xf9, 0x47, 0xc1, 0x98, 0x69, 0x05, 0x6e, 0x99, 0x99, 0x2e, 0x3c, 0x0a, 0xa2, + 0xb4, 0xb3, 0xeb, 0xf0, 0xc6, 0xb6, 0xed, 0xc0, 0x50, 0x26, 0x73, 0x07, 0x54, 0x8a, 0xf5, 0xd5, + 0x1b, 0x97, 0x27, 0xdc, 0x2c, 0x94, 0x33, 0x13, 0x19, 0x16, 0xe2, 0x23, 0xc8, 0x27, 0x98, 0x10, + 0x83, 0x6d, 0xe2, 0xad, 0xad, 0x1e, 0xd5, 0xd3, 0x4f, 0x20, 0x67, 0xd2, 0x2f, 0x65, 0x61, 0xaf, + 0xab, 0x37, 0xfb, 0x07, 0x50, 0x52, 0x49, 0x30, 0xac, 0x74, 0xc0, 0x90, 0x7f, 0x07, 0xd2, 0x11, + 0x25, 0x01, 0x04, 0x3d, 0xca, 0xd1, 0xbb, 0x53, 0x28, 0x31, 0xe0, 0xc8, 0xbf, 0xbe, 0x9a, 0x2b, + 0x46, 0x31, 0x65, 0x41, 0xa9, 0x12, 0x66, 0x99, 0xa9, 0x03, 0x20, 0x41, 0xce, 0x14, 0x27, 0x62, + 0x4a, 0x45, 0x50, 0x11, 0x8e, 0x31, 0x97, 0x37, 0x81, 0xa8, 0xc0, 0xc6, 0xc0, 0xb4, 0x71, 0x12, + 0x49, 0x15, 0x67, 0x30, 0x43, 0x59, 0x6c, 0x20, 0x25, 0x49, 0x68, 0xd0, 0x22, 0xba, 0xb5, 0xa5, + 0x36, 0x37, 0x11, 0xec, 0xd2, 0x42, 0x49, 0xe0, 0x00, 0xd1, 0x7b, 0xf6, 0x36, 0x38, 0xd8, 0x27, + 0xdc, 0xc1, 0x1f, 0xac, 0xcc, 0xe8, 0x24, 0x4a, 0xce, 0x46, 0xf6, 0x5d, 0xa6, 0x04, 0xc3, 0xe4, + 0xd2, 0x51, 0x64, 0xbf, 0x23, 0x7c, 0xc7, 0xa4, 0x31, 0x7e, 0xe4, 0x0c, 0xef, 0x22, 0x26, 0xa8, + 0x18, 0x87, 0xba, 0xe6, 0x8d, 0x35, 0x24, 0xb7, 0x56, 0x5a, 0x10, 0xe1, 0x4a, 0xee, 0x6d, 0x5f, + 0x08, 0xfa, 0x8d, 0x1f, 0xa9, 0x90, 0x65, 0xc8, 0x87, 0x7d, 0xe1, 0x1f, 0x45, 0x58, 0xb1, 0x58, + 0x2b, 0x95, 0x49, 0xa8, 0x92, 0x87, 0x45, 0x51, 0x42, 0x72, 0x73, 0x0c, 0x7d, 0xcf, 0xc3, 0x01, + 0x30, 0xea, 0xa0, 0x4d, 0x00, 0xb5, 0xb8, 0x84, 0x94, 0x06, 0x89, 0x72, 0xbf, 0x6c, 0xda, 0x76, + 0xc4, 0x84, 0x0e, 0x18, 0xa4, 0x0b, 0xb1, 0x01, 0xd4, 0x7d, 0xcc, 0xc1, 0x93, 0x79, 0xec, 0x49, + 0x01, 0x4f, 0x26, 0x1d, 0x45, 0x36, 0x67, 0x96, 0x71, 0x06, 0x0d, 0x14, 0x00, 0x28, 0xc1, 0x03, + 0xd1, 0xff, 0x4c, 0xa0, 0x48, 0xda, 0x2b, 0xdd, 0x4b, 0x17, 0x21, 0x3b, 0x97, 0x5c, 0xbb, 0xc2, + 0xc8, 0xcc, 0xf4, 0x2c, 0xdd, 0x48, 0xb1, 0x05, 0x05, 0x84, 0x90, 0x86, 0x6d, 0x37, 0x2a, 0x11, + 0x58, 0xce, 0x30, 0xb9, 0x2e, 0x58, 0x2d, 0xa7, 0x39, 0xe5, 0x5c, 0x31, 0x29, 0x10, 0x68, 0x24, + 0x4e, 0x95, 0x09, 0xb3, 0x95, 0x1a, 0x4e, 0x4b, 0x49, 0x74, 0xe6, 0x51, 0xc3, 0x4e, 0x3a, 0x0c, + 0x9b, 0x8a, 0xfe, 0xb8, 0xfb, 0x86, 0x1e, 0x28, 0x7d, 0x00, 0x9f, 0x50, 0xd6, 0xa3, 0x5c, 0x8a, + 0x21, 0xf8, 0xf0, 0x4d, 0x45, 0xa0, 0xbb, 0x11, 0xf6, 0x95, 0xc3, 0xcc, 0x79, 0x16, 0x21, 0x86, + 0x53, 0xee, 0x97, 0x14, 0x87, 0x69, 0x86, 0x01, 0x03, 0xf0, 0x83, 0x29, 0x16, 0xda, 0x7b, 0x55, + 0x68, 0x67, 0x32, 0xf6, 0x24, 0x8f, 0x38, 0x89, 0xbd, 0x47, 0xb0, 0x4e, 0x14, 0x29, 0x8a, 0xda, + 0x38, 0xe0, 0xcc, 0x70, 0x6b, 0x4b, 0xe7, 0x32, 0xc5, 0x3b, 0xe6, 0x4c, 0x00, 0x36, 0x1c, 0x06, + 0x58, 0x45, 0xb2, 0xe3, 0x0e, 0x8b, 0x81, 0x00, 0x53, 0xa2, 0xdf, 0x53, 0x23, 0xa4, 0x14, 0x65, + 0xa7, 0x20, 0xab, 0x66, 0xde, 0x8b, 0xc2, 0x09, 0x08, 0x8b, 0x1f, 0x8b, 0xc2, 0x52, 0x47, 0xba, + 0x6d, 0x9f, 0x30, 0xcf, 0x0f, 0xba, 0x6d, 0x5f, 0x51, 0xa6, 0x82, 0x42, 0x14, 0x54, 0x21, 0x8c, + 0x6d, 0x67, 0x8a, 0x7d, 0x01, 0x9f, 0x57, 0x73, 0xa0, 0x20, 0xcc, 0x7d, 0x4d, 0xb3, 0xb4, 0x48, + 0x8b, 0x13, 0xdd, 0x44, 0x7e, 0x48, 0xaf, 0x22, 0xae, 0x61, 0x51, 0x48, 0x18, 0xd5, 0x06, 0x5d, + 0x59, 0xd9, 0x7c, 0x34, 0xf1, 0xce, 0xa4, 0x22, 0xf4, 0x7f, 0x36, 0x95, 0xbf, 0x4e, 0x20, 0xca, + 0x21, 0x94, 0x88, 0x54, 0x6b, 0xd2, 0x85, 0xa6, 0xd4, 0x60, 0x23, 0x9d, 0x49, 0x85, 0xa9, 0x00, + 0x6d, 0x5a, 0x01, 0x5f, 0x2e, 0x94, 0x67, 0x94, 0x45, 0x76, 0x12, 0xf9, 0xc0, 0x00, 0x14, 0x9c, + 0x28, 0x07, 0x39, 0xb2, 0x5e, 0x28, 0x83, 0x94, 0x22, 0xb6, 0x5a, 0x5c, 0x30, 0x5c, 0xf5, 0x96, + 0xa1, 0x0e, 0x70, 0x52, 0x11, 0xaa, 0x47, 0x5b, 0xcf, 0x8f, 0x32, 0x89, 0x6c, 0x62, 0xb0, 0xe1, + 0xc9, 0x5a, 0x6c, 0x75, 0xf3, 0xb0, 0xbc, 0x0c, 0xf5, 0x45, 0xd8, 0xa9, 0x26, 0x32, 0x42, 0x8d, + 0xa9, 0x18, 0xc9, 0x14, 0xda, 0xf2, 0xfc, 0x11, 0x81, 0xe9, 0xfd, 0x82, 0x0d, 0x8f, 0x2a, 0xfd, + 0x7f, 0x81, 0x6d, 0x8d, 0x30, 0x79, 0xa2, 0x85, 0x76, 0x2f, 0xa7, 0x7c, 0x44, 0x52, 0x4d, 0x96, + 0xda, 0xbe, 0xd0, 0xbc, 0xed, 0x8b, 0xdc, 0xa2, 0xb4, 0x02, 0xc3, 0xe3, 0xfb, 0x12, 0x57, 0xc6, + 0xa1, 0xc7, 0x8c, 0x9d, 0x9c, 0x89, 0x06, 0x96, 0x9d, 0xf2, 0x13, 0xea, 0x32, 0xff, 0x84, 0xa5, + 0x06, 0x27, 0x02, 0x14, 0x1b, 0x50, 0x43, 0x83, 0x4d, 0x20, 0x12, 0xec, 0x39, 0xa4, 0x58, 0xbc, + 0x29, 0x14, 0x45, 0xbe, 0x67, 0x25, 0x26, 0xb9, 0xf0, 0xa5, 0x88, 0xb5, 0x79, 0x02, 0xe8, 0x72, + 0xfd, 0x49, 0x5a, 0xc8, 0x9f, 0x64, 0x8a, 0xf8, 0x13, 0x28, 0xd0, 0x77, 0x46, 0xa3, 0x5b, 0xc7, + 0xbd, 0xb3, 0x36, 0x37, 0x71, 0x45, 0x7e, 0xc4, 0x72, 0x31, 0xdf, 0x46, 0x8b, 0x47, 0xf5, 0x13, + 0xd7, 0x64, 0xcd, 0xe3, 0xec, 0x83, 0x6e, 0x7c, 0x35, 0x29, 0x9a, 0xbf, 0xc0, 0xb6, 0x09, 0x18, + 0x7f, 0xf4, 0x8c, 0x05, 0x87, 0x72, 0xd2, 0xae, 0xe8, 0xe9, 0x69, 0x1e, 0xc7, 0xb1, 0xb1, 0xc2, + 0x62, 0xe9, 0xa3, 0x50, 0x72, 0x6f, 0x1c, 0x9c, 0xbf, 0x5f, 0x66, 0xb3, 0x24, 0x09, 0xe8, 0xfc, + 0xe4, 0xde, 0xd3, 0x74, 0xa3, 0x5e, 0x33, 0x96, 0xef, 0x46, 0xcc, 0x4f, 0xea, 0x85, 0x9d, 0xd1, + 0x48, 0xbf, 0xfe, 0x84, 0xc5, 0xbe, 0x95, 0x0a, 0xf5, 0xd7, 0x82, 0xa3, 0x10, 0xda, 0x62, 0xe6, + 0x74, 0xcf, 0x77, 0x83, 0x81, 0x16, 0x0b, 0x0b, 0x09, 0xfc, 0xfe, 0xc8, 0x1f, 0xfb, 0xd4, 0xae, + 0x55, 0x9f, 0x59, 0x2d, 0x12, 0x5a, 0xfb, 0xa2, 0xe2, 0x37, 0xb0, 0x53, 0x64, 0x49, 0x9b, 0xa2, + 0xf4, 0x43, 0xa8, 0xff, 0xed, 0x9c, 0xb2, 0xa8, 0x02, 0x4c, 0x16, 0xf1, 0xc0, 0x46, 0x5b, 0xfe, + 0x04, 0x0b, 0x81, 0xb4, 0x9e, 0xb4, 0x11, 0xb0, 0x13, 0x8c, 0x75, 0x44, 0x17, 0x77, 0xc3, 0x7d, + 0x28, 0x25, 0xc6, 0xef, 0xb8, 0xf9, 0xc5, 0xd3, 0xd3, 0xe6, 0xbb, 0x02, 0x03, 0x4c, 0x65, 0xa3, + 0xc3, 0xd9, 0xcd, 0xab, 0x8b, 0x45, 0x84, 0x38, 0xd6, 0x82, 0x96, 0x3d, 0x56, 0xc5, 0x68, 0xe2, + 0x2f, 0x18, 0x59, 0x3e, 0x96, 0x1f, 0x1e, 0x1e, 0xca, 0xfd, 0x90, 0x8c, 0xcb, 0x53, 0x32, 0xc2, + 0xcc, 0xdd, 0xc9, 0x5b, 0x87, 0x4d, 0x98, 0xa9, 0x25, 0x33, 0x2e, 0xbb, 0xf8, 0x70, 0x89, 0x1d, + 0xe2, 0x0e, 0x85, 0x4b, 0x1f, 0x35, 0x5e, 0x8e, 0x69, 0xf0, 0x4a, 0x4c, 0x33, 0x65, 0xef, 0xd0, + 0x28, 0x3b, 0x16, 0x2b, 0x3b, 0x36, 0x0f, 0xf7, 0x73, 0xe9, 0xfa, 0x72, 0x41, 0x8d, 0xf9, 0x05, + 0x33, 0x6d, 0x58, 0xad, 0x24, 0xaf, 0x2a, 0xea, 0x75, 0x40, 0xc7, 0x1d, 0x8e, 0xde, 0xb9, 0xf0, + 0x31, 0xb9, 0x82, 0xd4, 0x67, 0x2f, 0x9c, 0xca, 0xe5, 0x74, 0x02, 0x76, 0x08, 0xd8, 0x4b, 0xf4, + 0xe4, 0xe7, 0x24, 0xa4, 0xa1, 0x1b, 0x8e, 0x3e, 0x61, 0x02, 0x10, 0x4a, 0xee, 0x0a, 0xae, 0xc2, + 0xcd, 0xdc, 0x64, 0x78, 0xd4, 0x87, 0x18, 0x2a, 0x7a, 0x0b, 0x5e, 0x44, 0x01, 0x2d, 0x95, 0x50, + 0xcb, 0xbf, 0x89, 0x92, 0xce, 0x25, 0xe3, 0x93, 0xb9, 0x5c, 0xb2, 0xf1, 0x26, 0x52, 0x1a, 0x0d, + 0x98, 0x47, 0xd9, 0x9f, 0x52, 0xf5, 0xfa, 0x26, 0xfe, 0x98, 0xc6, 0x74, 0x78, 0x97, 0xfe, 0x7c, + 0x95, 0xfe, 0x84, 0x53, 0xcd, 0xc9, 0x0e, 0xa9, 0xcc, 0x7f, 0x96, 0x59, 0x41, 0xac, 0x08, 0x3d, + 0x81, 0x51, 0x11, 0xb7, 0x17, 0x9f, 0x2b, 0x48, 0x17, 0x82, 0xd0, 0xc3, 0xe5, 0x80, 0x19, 0x77, + 0xd2, 0x7d, 0xc1, 0x79, 0x6b, 0x17, 0x07, 0x47, 0xef, 0xcf, 0x34, 0x26, 0x58, 0x60, 0xf9, 0xbe, + 0xc7, 0x72, 0x15, 0x0d, 0xf8, 0xfb, 0xa3, 0x8d, 0xbc, 0x4a, 0x0a, 0xb5, 0x8d, 0xf8, 0x3f, 0xfe, + 0xfd, 0xff, 0x7e, 0xb5, 0x34, 0xf1, 0x8e, 0x26, 0xf3, 0xdc, 0x4e, 0x0f, 0x89, 0xcf, 0xf6, 0x15, + 0x9d, 0xfd, 0xc7, 0xbf, 0xff, 0x5f, 0x2d, 0x95, 0x59, 0x48, 0x0a, 0x4d, 0x43, 0x0a, 0xb5, 0x26, + 0xad, 0x1b, 0x99, 0x06, 0x20, 0x2d, 0x3b, 0x9d, 0x00, 0xa5, 0xae, 0x21, 0x50, 0x88, 0xc8, 0x03, + 0xa6, 0x70, 0x67, 0x02, 0xd2, 0x9e, 0xbd, 0xe7, 0x09, 0x55, 0xa7, 0x01, 0x33, 0x04, 0x1b, 0xa7, + 0x32, 0xff, 0xd6, 0xd2, 0x47, 0xb6, 0xd9, 0xdc, 0x9c, 0xfb, 0x82, 0x8c, 0x87, 0x4c, 0x86, 0x21, + 0x08, 0x7a, 0x7b, 0x53, 0x96, 0x90, 0x32, 0x9f, 0x8a, 0x43, 0x75, 0x10, 0xcb, 0x05, 0xf6, 0x83, + 0x2e, 0x69, 0x7c, 0xa0, 0x4e, 0x81, 0xec, 0xe7, 0x96, 0xdc, 0x41, 0x85, 0x60, 0x66, 0x80, 0xd4, + 0x1d, 0xfa, 0x23, 0x8f, 0xe0, 0x40, 0x37, 0xa4, 0xfc, 0xcd, 0xb3, 0x7b, 0x94, 0x53, 0x9a, 0x46, + 0x47, 0x15, 0xb2, 0x69, 0xb2, 0x01, 0x14, 0x28, 0xd2, 0x31, 0x90, 0xd4, 0x82, 0x25, 0x97, 0xa6, + 0x21, 0x2f, 0x59, 0xa4, 0xd3, 0x94, 0x1e, 0xcc, 0x09, 0xcf, 0xb8, 0x9a, 0xda, 0x30, 0x62, 0x3c, + 0x8a, 0xf0, 0x46, 0x51, 0xf3, 0x1b, 0x78, 0x3c, 0xa1, 0x33, 0xe8, 0x44, 0x15, 0xe3, 0x69, 0xa7, + 0xe1, 0x06, 0x9f, 0xf6, 0x46, 0xc2, 0xb3, 0x08, 0xdb, 0x54, 0xc9, 0x82, 0x5c, 0x57, 0x6f, 0x10, + 0x81, 0x09, 0xf3, 0x4f, 0x65, 0xbe, 0xa4, 0x60, 0xb2, 0xe1, 0xbe, 0x4e, 0x0a, 0x27, 0x47, 0x8a, + 0x27, 0x17, 0x16, 0x88, 0x5a, 0x02, 0x46, 0x43, 0x64, 0xe6, 0xf7, 0xf5, 0xd5, 0x3c, 0xa0, 0x7a, + 0xa8, 0xf2, 0x32, 0x71, 0x22, 0x78, 0x09, 0x73, 0x82, 0x17, 0xc3, 0x30, 0xac, 0xc2, 0x51, 0x48, + 0x18, 0x90, 0x2c, 0x0c, 0xd4, 0xcd, 0xeb, 0xb0, 0xc6, 0x23, 0x81, 0xcd, 0x7d, 0x5b, 0x90, 0xa2, + 0xfb, 0x09, 0x2d, 0x9a, 0xec, 0x2f, 0x88, 0xe0, 0xa5, 0x2d, 0x90, 0xb7, 0xbe, 0x01, 0x2a, 0x37, + 0x3f, 0xde, 0xf0, 0x0e, 0xc7, 0x5f, 0x99, 0x3e, 0x0b, 0x7e, 0x89, 0x1d, 0x0d, 0xd5, 0xa2, 0x80, + 0x68, 0xcb, 0x88, 0xe2, 0x7d, 0x19, 0x1e, 0xcc, 0xd8, 0xff, 0x7a, 0x79, 0x7a, 0xb1, 0xf1, 0x6a, + 0xae, 0x52, 0xc2, 0x97, 0x01, 0x49, 0x74, 0x66, 0x26, 0xb3, 0x36, 0x81, 0x32, 0xe9, 0x79, 0x81, + 0xd6, 0xfb, 0x84, 0x9b, 0xe5, 0xce, 0xd8, 0xe9, 0x25, 0xdc, 0x4f, 0x02, 0xff, 0x09, 0x63, 0x02, + 0xc5, 0x5e, 0x9a, 0x62, 0xc4, 0x1b, 0x27, 0xef, 0xbe, 0x89, 0x11, 0x9e, 0xbc, 0xfb, 0xa6, 0xb4, + 0x21, 0x68, 0x67, 0x38, 0x9e, 0xe2, 0x27, 0xd3, 0x84, 0xc2, 0x28, 0xc5, 0x37, 0x5c, 0xbd, 0xf7, + 0x46, 0xbc, 0x21, 0x3e, 0x45, 0x2b, 0xb2, 0x1e, 0x3b, 0x4f, 0xdc, 0x07, 0x78, 0x8c, 0x29, 0x26, + 0x65, 0x90, 0x75, 0x83, 0x3d, 0x06, 0x88, 0xc3, 0x85, 0x7a, 0xa8, 0x18, 0x6c, 0x89, 0xf8, 0xbb, + 0x91, 0x4a, 0xfb, 0xcd, 0x6a, 0x15, 0xe9, 0xfe, 0xb6, 0x59, 0x6b, 0x1a, 0xa5, 0x9a, 0x61, 0xc4, + 0xff, 0xf8, 0x6a, 0x69, 0x55, 0x0d, 0xfd, 0x19, 0xa9, 0x5a, 0xf6, 0x3f, 0x23, 0x05, 0xcf, 0x49, + 0x52, 0x80, 0x1b, 0x2e, 0x91, 0xc7, 0xfd, 0xfd, 0x74, 0x22, 0x6c, 0xe4, 0x28, 0x82, 0x0c, 0x9a, + 0xcd, 0x80, 0x88, 0xe5, 0xb0, 0xb6, 0x9e, 0x13, 0x0d, 0x6f, 0x43, 0x87, 0x78, 0x65, 0xf2, 0xa8, + 0x21, 0x0c, 0x82, 0x34, 0x06, 0x9a, 0x34, 0x9d, 0xf2, 0xf4, 0xc8, 0x90, 0x46, 0xe4, 0x74, 0xbf, + 0xe2, 0xe1, 0x91, 0x0f, 0xe2, 0x39, 0xa0, 0xf3, 0xb2, 0xad, 0xc8, 0x1c, 0x0d, 0x09, 0x15, 0x37, + 0x77, 0x40, 0x80, 0xfd, 0xee, 0x7b, 0x23, 0x9c, 0xd7, 0xc1, 0xe7, 0xfa, 0x92, 0xb5, 0xcb, 0x41, + 0x08, 0xe6, 0xa8, 0x41, 0x85, 0x3a, 0x64, 0x80, 0xc1, 0x17, 0xf8, 0x34, 0xe4, 0x6e, 0x62, 0xcc, + 0x25, 0x46, 0xcb, 0x55, 0x13, 0xa4, 0x1a, 0x1b, 0x27, 0xdd, 0x97, 0xb6, 0x13, 0xcc, 0xd3, 0xdb, + 0x28, 0x2e, 0x2a, 0x3a, 0xa0, 0xfb, 0x5f, 0xc3, 0xfe, 0x06, 0x5b, 0x6d, 0x9a, 0x54, 0x73, 0x26, + 0x8e, 0xeb, 0xd3, 0x19, 0xf0, 0xf1, 0xf1, 0x86, 0xa0, 0xba, 0xbe, 0x5a, 0x1a, 0xb7, 0x4c, 0x4d, + 0xf1, 0x86, 0x34, 0x6a, 0x3f, 0x0e, 0xf5, 0x07, 0xb5, 0x07, 0x26, 0xe2, 0xf6, 0xe9, 0xac, 0x0c, + 0xe6, 0x59, 0x20, 0xa9, 0x87, 0x11, 0x81, 0x9f, 0x39, 0x48, 0x21, 0xb2, 0xc0, 0x4a, 0x8a, 0x32, + 0x1f, 0x75, 0x0d, 0x85, 0x62, 0xe7, 0x85, 0xe9, 0x66, 0xdb, 0x88, 0x86, 0xe1, 0x43, 0xf0, 0xd5, + 0x02, 0x00, 0x44, 0x9c, 0x0b, 0xdc, 0x98, 0x61, 0x68, 0xf6, 0x15, 0xc9, 0xf6, 0xcb, 0xfd, 0x0c, + 0x92, 0x5e, 0x15, 0xb6, 0xd1, 0x40, 0x7f, 0x12, 0x75, 0xfb, 0xbc, 0x22, 0x8c, 0x39, 0xb8, 0xbe, + 0x91, 0xcc, 0x01, 0xd7, 0x0c, 0xd6, 0xdb, 0xf2, 0x69, 0x67, 0x61, 0x85, 0xa2, 0xea, 0x05, 0x03, + 0xa1, 0x17, 0x0c, 0xa5, 0x5e, 0x90, 0xc4, 0x76, 0x84, 0xfc, 0xd4, 0xb2, 0x24, 0xb7, 0xc5, 0x13, + 0xe5, 0x60, 0xab, 0x9a, 0x9b, 0xb4, 0x18, 0x27, 0x5f, 0x00, 0x5f, 0x5a, 0xda, 0x01, 0x16, 0x11, + 0xbf, 0x19, 0xd0, 0x21, 0x0e, 0x0a, 0x9f, 0x6f, 0x9a, 0x91, 0x91, 0x08, 0x64, 0x2f, 0xe1, 0xf4, + 0x30, 0x06, 0x39, 0x9d, 0x69, 0xc8, 0xe2, 0x5a, 0xac, 0x55, 0x4f, 0x36, 0x0c, 0x0d, 0x5d, 0x57, + 0x6a, 0x4d, 0x54, 0x69, 0xa2, 0xca, 0x4e, 0x33, 0x55, 0x84, 0x8d, 0xec, 0xbd, 0x79, 0x90, 0xd1, + 0x39, 0x06, 0xa9, 0xce, 0x91, 0x94, 0x46, 0xf0, 0x2d, 0xb4, 0x8e, 0xa1, 0xfc, 0x4e, 0xf4, 0x8e, + 0x32, 0xdc, 0xc2, 0x75, 0xd9, 0xac, 0x57, 0x51, 0xd9, 0x34, 0x9b, 0xa8, 0x0c, 0x41, 0x23, 0xcb, + 0xbb, 0x4d, 0x54, 0x6e, 0x37, 0x51, 0x79, 0xa7, 0x7a, 0x83, 0x40, 0x05, 0x29, 0x05, 0x55, 0x65, + 0x93, 0x9b, 0xae, 0x08, 0x4d, 0x97, 0x2f, 0xa1, 0x3b, 0xb0, 0xf7, 0x06, 0x7b, 0xf6, 0x68, 0x6b, + 0x6b, 0xf0, 0x93, 0x77, 0x3d, 0xde, 0x36, 0x6f, 0x8c, 0xe4, 0x36, 0x5f, 0x50, 0x3b, 0x82, 0x5e, + 0xb1, 0x8d, 0x86, 0xb6, 0x1e, 0x96, 0xfb, 0x25, 0x5d, 0xea, 0x17, 0xcb, 0xa6, 0x61, 0x24, 0xca, + 0xc6, 0xce, 0x32, 0x1d, 0x62, 0xd2, 0xd4, 0x68, 0xbf, 0x69, 0x99, 0x68, 0x04, 0x4a, 0x3e, 0x52, + 0xae, 0xb5, 0x0d, 0x88, 0xc9, 0x10, 0x08, 0x4d, 0xda, 0x07, 0x1f, 0xee, 0xc6, 0xb7, 0x80, 0xf3, + 0x30, 0x0b, 0x30, 0x41, 0xca, 0x03, 0x54, 0x45, 0xc4, 0xe8, 0x3c, 0x82, 0x86, 0xaa, 0x1b, 0x8e, + 0x42, 0x72, 0x49, 0xc3, 0x09, 0x28, 0x65, 0xf7, 0xec, 0xfa, 0xff, 0xdf, 0xda, 0xb7, 0x2e, 0xb7, + 0x8d, 0x64, 0x69, 0xfe, 0xaf, 0xa7, 0x80, 0x51, 0x6e, 0x05, 0x20, 0x25, 0x21, 0x92, 0xba, 0x16, + 0x69, 0x9a, 0x21, 0xd3, 0x72, 0xd9, 0x53, 0xb2, 0xc4, 0xb5, 0x54, 0xae, 0xad, 0x51, 0x6b, 0x9b, + 0x20, 0x90, 0x24, 0x31, 0x06, 0x01, 0x16, 0x12, 0x90, 0xc4, 0x11, 0x19, 0x31, 0xbf, 0xe6, 0x01, + 0x66, 0x5e, 0x64, 0xf7, 0xd7, 0xfe, 0xef, 0x47, 0xe9, 0x27, 0xd9, 0x38, 0x27, 0x2f, 0x48, 0x5c, + 0x48, 0xc9, 0xb5, 0x1d, 0x1d, 0x5d, 0xa6, 0x80, 0xbc, 0x21, 0x2f, 0x27, 0xcf, 0xe5, 0x3b, 0xe7, + 0xf4, 0x95, 0x0d, 0xb2, 0x33, 0x7f, 0xdb, 0x6b, 0xf7, 0xcd, 0x1f, 0x0f, 0x7e, 0xa2, 0x27, 0x93, + 0x89, 0xd9, 0x01, 0xc3, 0xe3, 0xa9, 0x7b, 0x00, 0xe7, 0xb9, 0x54, 0xb3, 0x45, 0xf8, 0x22, 0x1d, + 0x1c, 0x93, 0xd3, 0x13, 0xd2, 0x3e, 0x3a, 0x22, 0x4e, 0xfb, 0xd4, 0xc6, 0xe5, 0xcd, 0x4d, 0x8d, + 0x8f, 0xe2, 0x2f, 0x5c, 0xeb, 0xf9, 0xae, 0x35, 0xdb, 0x9b, 0xd8, 0x30, 0x96, 0x46, 0xeb, 0x98, + 0xcc, 0xc8, 0xb4, 0x58, 0x98, 0xb7, 0xd7, 0x3e, 0x3c, 0x22, 0xed, 0xc3, 0x53, 0xd2, 0x3e, 0x6a, + 0x11, 0xe7, 0xe4, 0xd4, 0x06, 0x1a, 0x33, 0x89, 0xe1, 0x56, 0xfc, 0x69, 0xf1, 0x68, 0xb0, 0x25, + 0x4b, 0xe9, 0xbc, 0x91, 0x05, 0xc4, 0x60, 0x6e, 0xc4, 0x1a, 0x8c, 0x26, 0xc1, 0x44, 0xf2, 0x10, + 0x67, 0x61, 0x30, 0x8d, 0x7a, 0x26, 0xec, 0x4c, 0x08, 0xf4, 0xc0, 0xdb, 0x86, 0x98, 0x0c, 0x96, + 0xa0, 0x74, 0xa1, 0x4d, 0xc4, 0x20, 0xf6, 0x66, 0xfb, 0x6d, 0x92, 0x34, 0x0e, 0xc0, 0x98, 0x59, + 0xa8, 0x1c, 0xd2, 0x49, 0xaa, 0x61, 0x34, 0x2e, 0xd2, 0x3a, 0x8c, 0x46, 0x9e, 0xbf, 0x32, 0xc7, + 0x69, 0xe8, 0x70, 0x8c, 0x3e, 0xdd, 0x69, 0x1f, 0x1d, 0x75, 0x60, 0x52, 0xa2, 0x1e, 0xba, 0x12, + 0x1d, 0x1d, 0x71, 0x33, 0x00, 0xdb, 0x39, 0x3e, 0x10, 0x52, 0xf3, 0xd3, 0x2c, 0x5e, 0xb0, 0x4e, + 0x44, 0xd0, 0xac, 0xd6, 0x01, 0xe0, 0x1d, 0x94, 0xe8, 0x9b, 0x49, 0x9c, 0xa5, 0xd4, 0xc8, 0xa2, + 0x6f, 0x51, 0xfc, 0x10, 0x99, 0xf8, 0xa2, 0xd9, 0x37, 0xb9, 0x0e, 0xd8, 0x04, 0xd3, 0x44, 0xb4, + 0x36, 0x66, 0xf1, 0xe2, 0xf5, 0x53, 0x54, 0x38, 0x6d, 0x1a, 0x93, 0xfd, 0x47, 0x8e, 0x65, 0x2b, + 0xd0, 0x1f, 0xc1, 0x2f, 0x8d, 0xdd, 0x04, 0x68, 0x63, 0x5a, 0x65, 0x96, 0x74, 0x6c, 0x52, 0x2d, + 0x20, 0x89, 0x30, 0xc5, 0x85, 0xa9, 0x03, 0x71, 0x5c, 0x74, 0x5f, 0xb4, 0x9f, 0x52, 0x8d, 0xb1, + 0x5a, 0x00, 0x02, 0x18, 0x78, 0x1c, 0xe5, 0xbf, 0xa0, 0xd3, 0x04, 0xe0, 0xab, 0x24, 0x83, 0xe3, + 0x98, 0xca, 0x5e, 0xb4, 0x66, 0x6a, 0xef, 0x47, 0x62, 0xdf, 0xc7, 0x3a, 0x60, 0x8a, 0x36, 0xc4, + 0x52, 0x14, 0xd5, 0xbc, 0x70, 0x2f, 0x26, 0x79, 0xb9, 0x23, 0x00, 0x4d, 0x36, 0x0a, 0xd7, 0x76, + 0xbc, 0xdf, 0x86, 0xf8, 0x84, 0x24, 0x50, 0xd6, 0x60, 0x33, 0x9f, 0x13, 0xd3, 0x26, 0x7e, 0x2f, + 0x67, 0xf1, 0xc6, 0x6e, 0xd2, 0x80, 0x30, 0x4f, 0xdf, 0x4c, 0x88, 0xaf, 0xb2, 0xc1, 0x52, 0x0d, + 0x36, 0xe3, 0xae, 0x57, 0x60, 0x24, 0x46, 0xaf, 0x9f, 0x92, 0xf5, 0x5f, 0x46, 0xc4, 0x97, 0xb3, + 0xe0, 0x41, 0x00, 0x98, 0x5a, 0x56, 0x33, 0xda, 0xcc, 0x6a, 0xfa, 0x65, 0x66, 0xf3, 0x02, 0xac, + 0x96, 0x0e, 0xee, 0x15, 0xc1, 0x67, 0x46, 0x69, 0xe1, 0xfb, 0x91, 0xcd, 0x24, 0x6a, 0xee, 0x03, + 0x30, 0xd4, 0xab, 0x1d, 0x81, 0xc9, 0x3d, 0x41, 0xbe, 0xeb, 0xf2, 0x6d, 0x21, 0x94, 0xf8, 0xd2, + 0x34, 0x0a, 0xf4, 0x2c, 0x00, 0x78, 0x05, 0xd8, 0x25, 0xce, 0x30, 0x39, 0x93, 0x32, 0x85, 0x06, + 0xc8, 0xeb, 0x17, 0xec, 0x9d, 0x83, 0xaa, 0xa9, 0x73, 0x2d, 0x61, 0x8e, 0x4a, 0x7e, 0x2a, 0xb4, + 0xf9, 0x8d, 0xaa, 0x36, 0x83, 0x17, 0x99, 0x32, 0x4d, 0x6e, 0xca, 0x34, 0x35, 0x93, 0xa5, 0xaa, + 0x68, 0xaf, 0x6d, 0xfb, 0x0e, 0x6e, 0xac, 0x74, 0x06, 0x72, 0x42, 0xc3, 0x03, 0x2e, 0xc2, 0x24, + 0x12, 0x89, 0x2d, 0x77, 0xf1, 0x83, 0x2a, 0x10, 0x06, 0x18, 0xa4, 0x8b, 0xd6, 0x48, 0x07, 0xa9, + 0x92, 0xf3, 0xe8, 0xd6, 0x5d, 0x2b, 0xad, 0x0e, 0x06, 0x38, 0x48, 0xc8, 0x79, 0x33, 0xa0, 0x86, + 0x9b, 0x18, 0xee, 0x24, 0xa5, 0x09, 0x7a, 0x09, 0xe0, 0x26, 0x96, 0xf6, 0x84, 0x00, 0xec, 0x09, + 0xa9, 0x94, 0xb5, 0xaa, 0x42, 0x22, 0xdc, 0x18, 0x22, 0x92, 0x47, 0x80, 0x52, 0x18, 0xde, 0x33, + 0x42, 0xa2, 0xcb, 0x7a, 0x7e, 0xa1, 0x9c, 0x10, 0x26, 0x25, 0x14, 0x65, 0x02, 0x3b, 0x55, 0x08, + 0xdb, 0x64, 0xc4, 0xbf, 0xf3, 0xb5, 0x66, 0x3c, 0x95, 0x6d, 0xf6, 0xcd, 0xdc, 0xa0, 0x6f, 0x02, + 0xe0, 0x61, 0xc2, 0x63, 0x9b, 0xcb, 0xba, 0x22, 0x54, 0x5b, 0xbe, 0xd9, 0xf8, 0x7c, 0xc3, 0xb5, + 0xcf, 0x7f, 0x41, 0x82, 0xa1, 0xfc, 0xad, 0x9a, 0xf1, 0x05, 0xaa, 0x97, 0xea, 0xb7, 0x35, 0xdf, + 0x31, 0x5c, 0x5c, 0x9a, 0xbb, 0x61, 0x88, 0x0f, 0xbd, 0x3e, 0x52, 0xd9, 0xd5, 0x2a, 0x70, 0xe4, + 0x22, 0x0b, 0x76, 0x75, 0xde, 0xcb, 0xfa, 0x95, 0x1e, 0xc6, 0xae, 0x0f, 0x21, 0x1a, 0x32, 0xbb, + 0x73, 0xc6, 0x23, 0xb8, 0xe5, 0xaf, 0xf8, 0x5f, 0x5e, 0x7f, 0x1a, 0x5b, 0x9e, 0x06, 0x6e, 0x06, + 0x8b, 0x6e, 0x77, 0x22, 0x47, 0x34, 0x23, 0x21, 0x26, 0x89, 0xae, 0x09, 0x3c, 0xe2, 0x85, 0x81, + 0xf7, 0xcd, 0x24, 0x00, 0xc6, 0xf8, 0x0a, 0xbb, 0x0a, 0x66, 0x1e, 0x0c, 0x98, 0xa2, 0xe6, 0xc4, + 0x5e, 0x73, 0x96, 0x0b, 0xf6, 0x0f, 0xf2, 0xb6, 0x0d, 0x3e, 0xa9, 0xa6, 0xf0, 0x0f, 0x2d, 0x6f, + 0x22, 0x50, 0x41, 0x5d, 0x2d, 0xe0, 0x98, 0x59, 0xe6, 0x60, 0x16, 0xc7, 0x0c, 0x02, 0xc3, 0x79, + 0xc1, 0x02, 0x6e, 0x5e, 0xf8, 0x74, 0x1b, 0x38, 0x3d, 0x75, 0xd4, 0x24, 0x91, 0xe4, 0xc2, 0xdc, + 0x26, 0xb2, 0x12, 0x2f, 0xd2, 0x69, 0x12, 0x67, 0x0b, 0xf4, 0xc0, 0xc1, 0x43, 0xdf, 0x93, 0xa7, + 0x8e, 0x99, 0x6a, 0x83, 0xf9, 0xdc, 0x60, 0x25, 0xdb, 0xb6, 0x15, 0x99, 0xd1, 0x86, 0x04, 0x47, + 0xd8, 0x97, 0x47, 0xf8, 0x0c, 0x52, 0x2e, 0xd8, 0x1a, 0xea, 0x25, 0x40, 0xe0, 0x55, 0x45, 0xeb, + 0xf1, 0x67, 0x86, 0x27, 0xb0, 0x07, 0x4a, 0x97, 0x52, 0x19, 0xa5, 0x78, 0x51, 0x3b, 0x4a, 0x7f, + 0x1b, 0x51, 0xf8, 0x46, 0xab, 0xa3, 0x66, 0xce, 0x3d, 0x44, 0x48, 0xea, 0xe5, 0x3b, 0x5e, 0x88, + 0x3e, 0x45, 0xcc, 0x00, 0x50, 0x09, 0x2f, 0x8e, 0x00, 0x0d, 0x87, 0xca, 0xcb, 0x06, 0x6e, 0x3c, + 0x93, 0x44, 0x4a, 0x83, 0x21, 0x56, 0xcc, 0x35, 0xf4, 0x62, 0x5c, 0x7a, 0x29, 0x54, 0x94, 0x9b, + 0x16, 0xeb, 0xca, 0x3f, 0xb0, 0x3e, 0x07, 0x88, 0xc4, 0x89, 0xe1, 0x17, 0x66, 0xa0, 0xa6, 0x0d, + 0x79, 0xb6, 0xa2, 0xbe, 0x38, 0x5c, 0xab, 0x95, 0xf9, 0xa3, 0x92, 0x52, 0x34, 0x0d, 0x8e, 0x20, + 0x58, 0xf1, 0x86, 0x1b, 0x3a, 0xa9, 0x21, 0x26, 0x01, 0xb0, 0xa2, 0x9c, 0x98, 0xe8, 0xdf, 0x9f, + 0x68, 0x5e, 0xba, 0xf9, 0x61, 0xe5, 0x57, 0xdf, 0x03, 0x0d, 0x3d, 0x10, 0xaf, 0xc7, 0xd9, 0x78, + 0xcc, 0x3f, 0x6c, 0x74, 0x19, 0x2b, 0xe9, 0xc9, 0x78, 0x08, 0x52, 0xc0, 0xe3, 0x72, 0x7f, 0xf3, + 0x35, 0x88, 0x34, 0x0e, 0x48, 0xf8, 0xd8, 0x34, 0xcc, 0x57, 0xfe, 0xe1, 0xe2, 0x8b, 0xc1, 0x07, + 0x0a, 0x6d, 0x99, 0x46, 0xea, 0x86, 0xdf, 0xe0, 0x8e, 0x77, 0xbd, 0x24, 0x66, 0x3c, 0x04, 0xc0, + 0x9c, 0xb2, 0x59, 0x95, 0x3a, 0x26, 0x92, 0x3a, 0x9e, 0x59, 0xa6, 0x9b, 0xa4, 0x81, 0x07, 0xe3, + 0x18, 0xf1, 0x01, 0x19, 0x70, 0xf7, 0x6c, 0xf3, 0x4b, 0x58, 0x63, 0x09, 0xe5, 0xae, 0x6d, 0x0a, + 0x97, 0xa3, 0xbe, 0xfc, 0xc1, 0x29, 0x1e, 0xf1, 0x7a, 0x75, 0xcd, 0x8c, 0xf8, 0x25, 0x2a, 0xeb, + 0xaf, 0x47, 0x15, 0x77, 0x84, 0x80, 0xbb, 0x23, 0x60, 0x41, 0xae, 0xfb, 0xc0, 0x27, 0x25, 0x8d, + 0x87, 0xd9, 0xf5, 0x4b, 0x17, 0x07, 0x10, 0x40, 0x6e, 0xe5, 0x2f, 0x10, 0xc0, 0xd1, 0xeb, 0xa7, + 0x69, 0x6c, 0x05, 0x1a, 0xc5, 0x5a, 0xbf, 0x7e, 0xf2, 0xf0, 0xd2, 0x56, 0x4b, 0xe3, 0x43, 0x6c, + 0x0c, 0x8c, 0xf0, 0x76, 0x16, 0x81, 0xc6, 0x20, 0x10, 0x9e, 0xcf, 0xdc, 0xfd, 0x2d, 0x76, 0x98, + 0x07, 0x1e, 0xeb, 0x37, 0xf1, 0xa2, 0x27, 0x7f, 0x73, 0x28, 0xec, 0x1a, 0x35, 0x1c, 0x72, 0xf7, + 0x04, 0xd1, 0x02, 0xf0, 0x67, 0x9a, 0x0a, 0x36, 0x5a, 0xad, 0x5e, 0x69, 0xf6, 0x57, 0x8e, 0xcd, + 0x9a, 0x2f, 0x62, 0x46, 0x13, 0xd3, 0x76, 0xfe, 0x00, 0x35, 0x35, 0x5f, 0x57, 0xb0, 0x2b, 0xd4, + 0xa8, 0x70, 0x4b, 0xf5, 0x73, 0xbe, 0xe2, 0x2b, 0xc2, 0x4c, 0xb4, 0x6b, 0x27, 0x55, 0xeb, 0x4b, + 0x4b, 0xd6, 0x74, 0x19, 0xb4, 0x2a, 0x05, 0xc8, 0xb1, 0xb8, 0xce, 0x7a, 0xaf, 0x5a, 0x76, 0xb7, + 0x88, 0x62, 0xd1, 0xda, 0x2e, 0x70, 0xb1, 0x82, 0x4f, 0x13, 0x27, 0x03, 0x91, 0x1b, 0xd5, 0xdb, + 0xbc, 0x7e, 0xf2, 0x3e, 0xa7, 0x36, 0x79, 0xa5, 0xd8, 0xd6, 0x3a, 0x2e, 0x55, 0x6d, 0x3d, 0xd3, + 0x73, 0x13, 0xdf, 0x28, 0x5e, 0xfd, 0x97, 0x58, 0x0f, 0x19, 0xb3, 0xfc, 0xc6, 0x67, 0x92, 0x6d, + 0x35, 0xdc, 0x24, 0x09, 0xee, 0xa9, 0xce, 0xbd, 0x96, 0x66, 0x80, 0xf7, 0xab, 0xc1, 0x10, 0x4b, + 0xbd, 0x89, 0x2f, 0x83, 0xdf, 0x20, 0x3d, 0xd5, 0xdc, 0xc5, 0x05, 0xbc, 0x16, 0x80, 0x0a, 0xc4, + 0x11, 0x06, 0x76, 0xf7, 0x02, 0x22, 0x4e, 0x73, 0x1a, 0x4c, 0x82, 0xde, 0x27, 0xb0, 0xdd, 0x77, + 0xe3, 0x32, 0x2b, 0xb3, 0xa4, 0xe3, 0x24, 0x7e, 0x00, 0x05, 0xb3, 0xc8, 0x41, 0xd0, 0xee, 0x9b, + 0x5f, 0xce, 0x87, 0xe7, 0x67, 0x37, 0xe7, 0x5f, 0xcc, 0x8e, 0x7a, 0x7a, 0xd0, 0x37, 0xbf, 0x5c, + 0x5d, 0x7d, 0x36, 0x3b, 0xe6, 0xe0, 0xea, 0xf2, 0xe6, 0x6c, 0x70, 0xc3, 0x75, 0x9e, 0xb3, 0x03, + 0xdc, 0xc0, 0x74, 0xab, 0x62, 0x94, 0xef, 0x7c, 0xa5, 0x93, 0xd7, 0xa1, 0x67, 0x47, 0x4a, 0x33, + 0xba, 0x7e, 0xfd, 0x14, 0xe4, 0x27, 0x2f, 0x74, 0x53, 0x0d, 0xb4, 0xbf, 0x26, 0xfc, 0x61, 0x1c, + 0xe9, 0x0f, 0x47, 0xfc, 0x10, 0x4b, 0x29, 0x3d, 0x67, 0x06, 0x4c, 0x12, 0xa5, 0x96, 0x8e, 0x2b, + 0xc1, 0x30, 0x88, 0x1a, 0x53, 0x64, 0x0a, 0xaa, 0x84, 0x2a, 0x1c, 0x7c, 0x20, 0x10, 0x0f, 0xc6, + 0x3f, 0xfe, 0xf3, 0xbf, 0x90, 0x6b, 0x2f, 0x30, 0x42, 0xc4, 0xdb, 0xce, 0x2a, 0xe8, 0x9b, 0x1c, + 0xc1, 0x6c, 0xe4, 0x5f, 0x99, 0x95, 0xc3, 0x6a, 0x40, 0x8e, 0x54, 0xf7, 0x13, 0xe4, 0xcc, 0xf0, + 0x31, 0xa8, 0x69, 0x8e, 0x19, 0xd5, 0xc4, 0xb3, 0xdf, 0x93, 0x22, 0x0a, 0xb6, 0x2a, 0x51, 0x1d, + 0x2a, 0x60, 0x7b, 0xbd, 0x14, 0x96, 0xf4, 0x22, 0x49, 0xf7, 0x67, 0xbd, 0xb7, 0x17, 0xa9, 0x35, + 0xb3, 0x1d, 0x90, 0x26, 0x41, 0x54, 0x94, 0x2c, 0x22, 0x5c, 0x7d, 0x73, 0x77, 0xd1, 0x00, 0x9d, + 0x1c, 0x40, 0x42, 0x51, 0x63, 0xad, 0xf4, 0x37, 0x18, 0x93, 0x71, 0x84, 0x57, 0x14, 0x14, 0xca, + 0x22, 0xbd, 0x58, 0xb2, 0x96, 0x77, 0x19, 0x17, 0x34, 0xa4, 0x7e, 0x22, 0x59, 0x1b, 0x28, 0x9c, + 0xfa, 0xfb, 0x42, 0x3a, 0x1d, 0x71, 0xba, 0xe3, 0x2e, 0x1a, 0x5c, 0x8f, 0x9d, 0x1b, 0x2a, 0x5e, + 0x45, 0xba, 0xa5, 0x22, 0xe8, 0x49, 0x16, 0x02, 0x30, 0x19, 0x6e, 0xa8, 0x14, 0x13, 0x74, 0xbf, + 0x4d, 0xd8, 0x7e, 0x9b, 0xb4, 0xda, 0x44, 0xfe, 0xd4, 0xf0, 0xfe, 0xcc, 0xde, 0x75, 0x8e, 0xdb, + 0xc0, 0x63, 0x94, 0x74, 0x16, 0xe6, 0x8f, 0xad, 0x66, 0xbb, 0xd9, 0x76, 0x4d, 0x14, 0xad, 0x4a, + 0x5a, 0x89, 0x1f, 0x9b, 0x27, 0xcd, 0x71, 0xab, 0x09, 0x6a, 0x3d, 0x4d, 0xb9, 0x10, 0x88, 0xbf, + 0x94, 0xd6, 0x09, 0x9a, 0x17, 0xbb, 0x0a, 0xba, 0xf6, 0x7a, 0xd0, 0xb9, 0xa6, 0xa9, 0x39, 0x6e, + 0xe7, 0x2a, 0x5f, 0x3e, 0x94, 0xc3, 0x36, 0xe8, 0x6d, 0x6e, 0xb3, 0x5d, 0xa7, 0x7d, 0x4a, 0xb2, + 0x5d, 0xe7, 0xe8, 0x18, 0xfe, 0x7b, 0xda, 0xbe, 0xeb, 0xa6, 0x35, 0xca, 0xab, 0xa3, 0x13, 0xd2, + 0x3e, 0x68, 0x71, 0xc5, 0x08, 0xea, 0xae, 0x52, 0x1d, 0x5a, 0x4f, 0x26, 0xb9, 0x7a, 0x67, 0x46, + 0x42, 0x0c, 0x0c, 0x5b, 0x50, 0x61, 0xa5, 0x8e, 0x9b, 0x78, 0x96, 0x4f, 0x3c, 0x32, 0x23, 0xc2, + 0x45, 0x68, 0xf8, 0x09, 0x5d, 0xfd, 0x94, 0xee, 0xaa, 0xf0, 0x7d, 0x35, 0x1a, 0xb3, 0xe3, 0x36, + 0xf6, 0xfa, 0xac, 0xf2, 0x24, 0xcd, 0xf5, 0x24, 0x21, 0x57, 0x36, 0xbc, 0xff, 0xf4, 0xe5, 0x7c, + 0x70, 0x63, 0x76, 0x42, 0xae, 0x61, 0x68, 0x19, 0x1f, 0xaf, 0x86, 0x66, 0xc7, 0x6c, 0xef, 0x19, + 0xfb, 0xc6, 0xaf, 0x97, 0xbf, 0x5c, 0x5e, 0xfd, 0x76, 0x69, 0x12, 0x7f, 0xef, 0x94, 0x78, 0x8d, + 0xd9, 0x5e, 0x4b, 0x28, 0x51, 0xca, 0xdf, 0xc3, 0x79, 0x7b, 0xdc, 0x9c, 0x64, 0xda, 0x9b, 0xab, + 0x0d, 0xda, 0x6f, 0x76, 0xd4, 0x1f, 0xad, 0x7e, 0xab, 0xd3, 0x26, 0x8f, 0x28, 0x8f, 0xce, 0x72, + 0x92, 0xc1, 0xbd, 0x18, 0x72, 0x05, 0x83, 0x42, 0x97, 0x5f, 0x92, 0xf7, 0xe4, 0xda, 0xee, 0xbd, + 0xbd, 0xdc, 0x7b, 0xbf, 0x6b, 0x5d, 0xef, 0x1d, 0xd8, 0xa4, 0x69, 0xaf, 0x56, 0xe1, 0xee, 0xc9, + 0x01, 0x59, 0xf6, 0x1e, 0xff, 0x72, 0x70, 0xdc, 0xdc, 0x15, 0x53, 0xb5, 0xdf, 0x3a, 0x6d, 0x92, + 0x41, 0x6f, 0x72, 0x3b, 0xbd, 0xdb, 0xb3, 0x1e, 0xff, 0xd2, 0x3a, 0x69, 0x9c, 0xda, 0xbb, 0xce, + 0x29, 0xb9, 0xef, 0xf9, 0x7b, 0x58, 0xc4, 0x8b, 0x99, 0xb5, 0xb4, 0x77, 0x07, 0x64, 0xdc, 0xf3, + 0xf8, 0x13, 0x16, 0x44, 0xfc, 0xc9, 0x50, 0x57, 0x36, 0xc4, 0x52, 0xd9, 0x30, 0x2b, 0x28, 0x1b, + 0x62, 0xdb, 0x26, 0x9f, 0xf3, 0x72, 0xce, 0x01, 0x69, 0x69, 0xba, 0x06, 0x32, 0x04, 0x4d, 0x43, + 0x13, 0x42, 0x8e, 0x5c, 0x41, 0x6e, 0x37, 0x45, 0x75, 0xa5, 0x3a, 0xad, 0x33, 0xd3, 0x88, 0xee, + 0x8f, 0x93, 0x89, 0x7f, 0x78, 0x78, 0x02, 0xda, 0x36, 0x81, 0xfc, 0xef, 0xa6, 0x05, 0x80, 0xfd, + 0xe7, 0xc2, 0x52, 0x5f, 0xc1, 0x1e, 0x98, 0xb9, 0x7e, 0xfc, 0x80, 0xfb, 0x5e, 0xfb, 0xfb, 0x5d, + 0x98, 0x25, 0x10, 0x08, 0xb7, 0x6e, 0x2b, 0xdd, 0x93, 0x31, 0x39, 0x2c, 0x6f, 0x25, 0x68, 0x14, + 0x4b, 0x68, 0xf5, 0x9b, 0x24, 0x7c, 0xd3, 0x3a, 0x85, 0xd0, 0x14, 0xfa, 0xf6, 0xfa, 0x71, 0x72, + 0x34, 0x39, 0x9d, 0x8c, 0xf3, 0x0d, 0xd5, 0x6a, 0xbe, 0x68, 0x47, 0x59, 0xb3, 0xea, 0xb5, 0x61, + 0xda, 0x9a, 0xbb, 0xab, 0x4d, 0xee, 0xf7, 0x4e, 0xc9, 0xb8, 0x71, 0x8c, 0xda, 0x8b, 0x82, 0x5b, + 0xc1, 0xba, 0xb4, 0xc5, 0xa5, 0x92, 0xb2, 0xf4, 0xfd, 0xd5, 0xe7, 0x7c, 0x1e, 0x8e, 0xf4, 0x23, + 0xef, 0x37, 0x8e, 0x88, 0xd7, 0x00, 0x9d, 0x10, 0x01, 0x60, 0x5e, 0xe9, 0x8b, 0xb7, 0x7f, 0xea, + 0x49, 0xb3, 0x69, 0xbc, 0xf0, 0x73, 0xcd, 0x9b, 0x8f, 0x9f, 0xae, 0x0d, 0x6e, 0xbb, 0x26, 0xfe, + 0x5e, 0xab, 0x45, 0xbc, 0xbd, 0xc3, 0x67, 0x4f, 0xea, 0xc9, 0x77, 0x1f, 0x54, 0xf3, 0x0b, 0xf0, + 0xd7, 0x3d, 0x43, 0x98, 0x3c, 0xb8, 0xce, 0xf0, 0xef, 0xff, 0xd7, 0x18, 0x27, 0xc0, 0x0f, 0x46, + 0x90, 0x25, 0xbc, 0x27, 0x58, 0x14, 0x03, 0x2d, 0xf3, 0xf0, 0xc8, 0x24, 0xad, 0x43, 0xc2, 0x1a, + 0xad, 0xb6, 0x0e, 0x14, 0xce, 0x0d, 0xf2, 0x97, 0xcc, 0xe2, 0x5c, 0xe4, 0xac, 0x31, 0x77, 0x17, + 0xe0, 0xc0, 0x70, 0x2c, 0x3c, 0x91, 0xd2, 0x3a, 0x7b, 0x03, 0xad, 0x3a, 0x37, 0xf5, 0xd2, 0x2e, + 0x8a, 0x93, 0x22, 0xf4, 0x05, 0x30, 0xdb, 0x78, 0xd1, 0x98, 0xf6, 0xd3, 0xef, 0x89, 0x48, 0x3d, + 0x22, 0xb9, 0x24, 0x2e, 0xf4, 0x7c, 0x62, 0xc2, 0x6d, 0x1d, 0x2e, 0x35, 0x75, 0x11, 0x82, 0xee, + 0x68, 0x09, 0xba, 0x23, 0x89, 0x4d, 0x5f, 0x12, 0xbc, 0x9f, 0x00, 0x02, 0xfb, 0x09, 0x8e, 0xed, + 0x3a, 0x07, 0x35, 0x2c, 0x7b, 0x6f, 0x97, 0x8e, 0x7c, 0x6b, 0xd7, 0xdd, 0x7a, 0x89, 0xba, 0xf5, + 0xc4, 0xd3, 0x4d, 0xf7, 0x9e, 0x76, 0xea, 0x4b, 0xec, 0x62, 0x43, 0xb6, 0x61, 0xaf, 0x51, 0x2e, + 0x8a, 0xb3, 0xd4, 0x90, 0x7d, 0x6e, 0xb9, 0x04, 0x2d, 0x4d, 0xfc, 0x92, 0xf6, 0x5b, 0xba, 0x49, + 0x45, 0x0f, 0xff, 0x8b, 0x6a, 0x2f, 0xbb, 0xa6, 0xdf, 0x3a, 0x05, 0x74, 0xd3, 0xd6, 0xcb, 0x8e, + 0x16, 0x2e, 0x3b, 0x5a, 0xbc, 0xec, 0xd0, 0x2d, 0x8d, 0x3e, 0x73, 0x4b, 0x35, 0x7f, 0xb2, 0x11, + 0x83, 0x50, 0xe3, 0x00, 0xb6, 0xec, 0x35, 0xbb, 0xcb, 0x37, 0x3d, 0xd6, 0x5d, 0xee, 0xe5, 0x34, + 0xef, 0xe0, 0x90, 0xb0, 0xfd, 0x56, 0xdb, 0xb6, 0x69, 0x81, 0xdc, 0x50, 0x69, 0x7c, 0x59, 0x92, + 0xa6, 0x2d, 0xda, 0xc3, 0xbf, 0xb4, 0x21, 0x58, 0x76, 0xb9, 0xe9, 0xa8, 0xdc, 0x74, 0xb4, 0x7f, + 0xba, 0xb1, 0xe5, 0x26, 0x59, 0x6a, 0x2d, 0x33, 0xfe, 0x97, 0x6a, 0x99, 0x6e, 0x3f, 0x6c, 0x47, + 0x47, 0xf8, 0x99, 0x1a, 0x15, 0xcb, 0x82, 0xc6, 0x3c, 0x8e, 0x62, 0xb6, 0x70, 0x3d, 0x4a, 0x0c, + 0xf5, 0xd3, 0x14, 0xb3, 0xc8, 0x0f, 0xdc, 0xa5, 0x49, 0x5a, 0x30, 0x59, 0xd0, 0x55, 0xed, 0xa0, + 0xda, 0x4d, 0x72, 0xa0, 0x7f, 0x70, 0xbb, 0x49, 0x8e, 0xda, 0x95, 0x59, 0x57, 0xb4, 0x4a, 0x1b, + 0xb0, 0xb0, 0x40, 0x39, 0x8e, 0x93, 0xc8, 0xad, 0xaf, 0x6d, 0x69, 0xd0, 0xab, 0xc6, 0xfd, 0xdb, + 0xf8, 0xae, 0x73, 0x7b, 0x77, 0xd7, 0xad, 0xf1, 0x93, 0x13, 0x5e, 0x72, 0xea, 0xf6, 0x71, 0x1c, + 0xc7, 0xd7, 0xda, 0x71, 0xd3, 0xa2, 0xe7, 0x5b, 0xdd, 0xeb, 0xc9, 0xc6, 0xda, 0x31, 0x04, 0x8b, + 0x9b, 0x6d, 0xac, 0x8d, 0xaf, 0x43, 0xed, 0x75, 0xb3, 0xd9, 0x26, 0x56, 0xd6, 0xf0, 0xec, 0x5d, + 0xa7, 0x75, 0x6c, 0x93, 0x79, 0xf9, 0xd5, 0xac, 0x31, 0xe1, 0xaf, 0xba, 0x5e, 0xa3, 0x17, 0x92, + 0x6c, 0xaf, 0x17, 0x92, 0x49, 0xa3, 0x37, 0x27, 0xb3, 0xbd, 0xde, 0xbc, 0xcb, 0x4d, 0x5c, 0x16, + 0xfa, 0x93, 0x2e, 0xd1, 0x9f, 0x74, 0xb0, 0x06, 0x3c, 0xf7, 0xd3, 0x63, 0xe7, 0xe0, 0x78, 0xcf, + 0x1a, 0x34, 0x26, 0xf6, 0x3e, 0x6f, 0xc2, 0x62, 0x8d, 0x93, 0xb6, 0x4d, 0x96, 0x9d, 0x83, 0xe6, + 0x9e, 0x95, 0x35, 0x96, 0xf6, 0x3e, 0xef, 0xd5, 0x8a, 0xc0, 0x9e, 0xb9, 0x06, 0x63, 0x58, 0xdc, + 0x9f, 0x5a, 0xb1, 0x8d, 0x4e, 0x2d, 0xdd, 0x47, 0x94, 0x3a, 0x19, 0x4d, 0xe1, 0xd8, 0xbd, 0x77, + 0xd9, 0xcc, 0xba, 0x3d, 0x24, 0x27, 0x77, 0xcf, 0x1f, 0x8a, 0x16, 0x58, 0xa0, 0x92, 0x9c, 0xb9, + 0x79, 0x52, 0x84, 0x68, 0xb9, 0x96, 0x5c, 0xce, 0xa0, 0x37, 0xb5, 0x96, 0xb0, 0xef, 0x6a, 0xf7, + 0xc5, 0xa3, 0xf3, 0x48, 0x1e, 0x1d, 0x7d, 0xc3, 0x0e, 0x9c, 0x47, 0x32, 0x70, 0x0a, 0x9b, 0x76, + 0xcd, 0x5d, 0x28, 0xb4, 0xe1, 0x81, 0xf7, 0x61, 0xa2, 0xee, 0xc9, 0xc3, 0xa6, 0xad, 0x8d, 0xa1, + 0x86, 0x2c, 0x0e, 0xd6, 0xe4, 0x5e, 0x8e, 0x67, 0xdc, 0x9b, 0x5a, 0x03, 0x9b, 0x0c, 0x7b, 0xcb, + 0x3a, 0xce, 0x63, 0xb9, 0x9d, 0xf3, 0x28, 0x7f, 0x05, 0xf0, 0x0e, 0x63, 0xe7, 0x91, 0x8c, 0x9d, + 0x25, 0x5c, 0x9e, 0x05, 0x06, 0x42, 0x3f, 0x69, 0xa3, 0xd7, 0x4f, 0xc3, 0x75, 0xbb, 0x3d, 0x12, + 0x0f, 0xad, 0xea, 0x39, 0xd1, 0x5b, 0x3a, 0xdc, 0xdc, 0xd0, 0x10, 0x66, 0x42, 0xbb, 0xde, 0xf3, + 0xbf, 0xf9, 0xb5, 0xde, 0xd6, 0x7b, 0x28, 0xdc, 0xe2, 0xf7, 0x6f, 0x5a, 0xc7, 0xb8, 0xce, 0x75, + 0x97, 0xb9, 0x3a, 0xf1, 0xad, 0xcd, 0x17, 0xac, 0x76, 0xde, 0xad, 0xe5, 0x56, 0xbe, 0xa5, 0xdd, + 0xb4, 0xc9, 0xd8, 0x79, 0xdc, 0xfb, 0x09, 0xbe, 0xa6, 0x71, 0x62, 0xe3, 0x8e, 0xab, 0xf4, 0x9d, + 0x9f, 0x75, 0x45, 0x8d, 0x1f, 0x9d, 0xc7, 0xc6, 0x11, 0xec, 0x87, 0x9c, 0x17, 0xa1, 0x1b, 0xb8, + 0x99, 0xe2, 0x67, 0x1f, 0x57, 0x28, 0x89, 0x3f, 0x99, 0x4c, 0xa8, 0x46, 0x49, 0x54, 0xfb, 0x27, + 0xd8, 0xfe, 0x09, 0xdc, 0xf6, 0xad, 0xc3, 0xca, 0x2c, 0x6d, 0x9f, 0x1e, 0xe4, 0x75, 0x5e, 0x36, + 0x45, 0x9c, 0xd7, 0x79, 0x7f, 0xfe, 0xf5, 0xd3, 0xe0, 0xdc, 0x24, 0x8f, 0xce, 0x23, 0xb0, 0x3b, + 0x8f, 0xce, 0x72, 0xef, 0xd0, 0x2e, 0xae, 0xe8, 0x06, 0x8e, 0x87, 0x6a, 0x1c, 0xcf, 0x4b, 0x48, + 0xf0, 0xe8, 0xf5, 0x93, 0xa7, 0xeb, 0x06, 0xfe, 0xfe, 0xbf, 0x41, 0xe3, 0xf8, 0xfa, 0x29, 0x2b, + 0x3d, 0xbc, 0x1c, 0xc1, 0x97, 0x47, 0xc0, 0xe7, 0x08, 0x87, 0x1b, 0x61, 0xdb, 0x45, 0xde, 0xa8, + 0xdc, 0xe4, 0xa4, 0xae, 0xc9, 0x59, 0xe9, 0xe1, 0xf9, 0x08, 0xf8, 0xa6, 0xda, 0x46, 0x4b, 0x06, + 0xe3, 0xdf, 0x59, 0x09, 0xe0, 0x48, 0xa8, 0x84, 0x6c, 0xa1, 0xf1, 0x54, 0xc0, 0x20, 0x80, 0x4f, + 0xe1, 0x88, 0xa4, 0x7a, 0xd4, 0x16, 0xcd, 0xf1, 0x5d, 0x80, 0x2c, 0x96, 0xbf, 0x3b, 0x39, 0x1a, + 0x91, 0xd7, 0x86, 0x9c, 0x61, 0x01, 0x20, 0xc8, 0xb6, 0x36, 0xf0, 0x21, 0xb1, 0xf2, 0x36, 0xec, + 0x22, 0x2e, 0x83, 0x43, 0x38, 0xf4, 0x36, 0x6b, 0x01, 0x61, 0xb4, 0x0a, 0x05, 0xa3, 0x75, 0x20, + 0x30, 0xc3, 0x7f, 0x57, 0xc6, 0x81, 0xf1, 0x56, 0xa3, 0x38, 0x60, 0xb4, 0xb6, 0x5d, 0x7c, 0x03, + 0xe9, 0x42, 0xa1, 0xe9, 0x4b, 0xf8, 0x03, 0x1b, 0xcf, 0x1f, 0x4b, 0xec, 0x1a, 0x7f, 0x57, 0x6a, + 0x57, 0x03, 0x98, 0x01, 0xa0, 0x4c, 0xc3, 0x98, 0xd9, 0x5a, 0xa9, 0xb1, 0x1b, 0xf9, 0xc8, 0xae, + 0x9a, 0xe4, 0xd7, 0x44, 0x95, 0x7a, 0xf7, 0xa0, 0x97, 0x61, 0x0b, 0xd0, 0x49, 0x62, 0xb8, 0x3a, + 0x59, 0xe0, 0x7a, 0xd2, 0x1f, 0x5d, 0x7f, 0x00, 0x67, 0x05, 0xf1, 0xe7, 0x7a, 0x54, 0x59, 0x02, + 0x2f, 0x2e, 0xd6, 0x19, 0x24, 0xfd, 0xd1, 0xe0, 0x8b, 0x71, 0xb8, 0xaf, 0xaa, 0x0d, 0x12, 0xa8, + 0x36, 0xa8, 0x4c, 0xc9, 0x22, 0x7e, 0xa0, 0x35, 0x53, 0x8d, 0x50, 0xb0, 0x21, 0xbc, 0xc3, 0x90, + 0x26, 0xfc, 0xa7, 0xd6, 0x2d, 0xdf, 0x42, 0x8d, 0xdc, 0xfd, 0x0b, 0xb0, 0x5f, 0x1c, 0x48, 0x66, + 0xeb, 0x05, 0x18, 0x1a, 0xa0, 0xf8, 0x4b, 0xf8, 0x5d, 0x78, 0x29, 0xf0, 0x04, 0xe2, 0x75, 0xf4, + 0x85, 0x7a, 0xf7, 0xdc, 0x82, 0x53, 0x28, 0x05, 0xea, 0x27, 0xbf, 0x50, 0x08, 0xd3, 0xba, 0x2a, + 0xa0, 0x99, 0x7c, 0x8a, 0x10, 0x6c, 0xd6, 0xef, 0x37, 0xbb, 0x79, 0x5d, 0x0c, 0xfc, 0xc5, 0x90, + 0xb1, 0xa6, 0x10, 0x73, 0x76, 0x6d, 0xe0, 0x93, 0x32, 0x6e, 0x01, 0xb9, 0xe7, 0x62, 0x15, 0xdd, + 0xff, 0x37, 0xea, 0x8b, 0x08, 0x62, 0x1d, 0xf3, 0x8f, 0x2c, 0xa0, 0x29, 0x80, 0x00, 0x01, 0x39, + 0x89, 0xfe, 0xcd, 0x30, 0x8b, 0xe6, 0xce, 0xce, 0x16, 0x9d, 0xf9, 0x3b, 0x70, 0x97, 0xfa, 0x05, + 0xa0, 0x79, 0x9a, 0xcc, 0xf3, 0x4b, 0x7e, 0x46, 0x39, 0x34, 0x8c, 0x2f, 0x47, 0x05, 0x16, 0x96, + 0x23, 0xf7, 0x24, 0x3e, 0xec, 0xa4, 0x6d, 0xe7, 0x27, 0xb7, 0x8c, 0x0d, 0x4b, 0x05, 0x36, 0x2c, + 0x7d, 0x01, 0x36, 0x2c, 0x1f, 0xcc, 0xbb, 0x58, 0x57, 0x73, 0x97, 0x20, 0x62, 0xe8, 0x88, 0x98, + 0x87, 0xc4, 0x00, 0x9c, 0x60, 0x21, 0x06, 0x06, 0xfa, 0xc8, 0xbc, 0x69, 0x37, 0x57, 0x2b, 0xf6, + 0xa6, 0xdd, 0xd4, 0xf9, 0xc1, 0xe8, 0xcf, 0x04, 0xc6, 0xa0, 0xbb, 0x51, 0x7d, 0x60, 0x0c, 0xb6, + 0x1b, 0x49, 0x33, 0x55, 0x4d, 0x60, 0x8c, 0x58, 0x44, 0xc4, 0x88, 0x80, 0xc5, 0x8f, 0x4b, 0xe8, + 0x2e, 0xd0, 0x9d, 0x91, 0xe7, 0x83, 0x4f, 0xb4, 0x37, 0x06, 0x9f, 0x08, 0x7a, 0xad, 0x6e, 0xf0, + 0xe6, 0xb0, 0x1b, 0x40, 0xf0, 0x89, 0x8d, 0xb1, 0x25, 0x82, 0x5d, 0xb6, 0x7f, 0xa8, 0xc5, 0x97, + 0xa0, 0xf9, 0x93, 0x02, 0x9f, 0x9d, 0x14, 0xbc, 0xb5, 0x84, 0xdb, 0x6d, 0xab, 0xaf, 0x3d, 0xec, + 0xdc, 0x36, 0x20, 0x9d, 0x0c, 0xfc, 0xe7, 0xae, 0x14, 0xcb, 0x42, 0xe3, 0x03, 0x45, 0x9e, 0x68, + 0x11, 0xa0, 0x62, 0x97, 0xee, 0x6b, 0x10, 0xaf, 0x24, 0x07, 0x74, 0x91, 0xac, 0xc7, 0x1a, 0xe5, + 0x58, 0x3f, 0x5c, 0x45, 0x04, 0x18, 0xd2, 0x83, 0xa6, 0x0d, 0x81, 0x6f, 0xec, 0x5d, 0xd6, 0xf5, + 0xfb, 0x6a, 0xec, 0x1e, 0xd8, 0xbd, 0xd5, 0xb7, 0xc1, 0x5f, 0xeb, 0xca, 0x14, 0x2a, 0x2e, 0x41, + 0x9f, 0xb4, 0x36, 0x94, 0xaa, 0xe5, 0x25, 0x62, 0x9d, 0x07, 0x38, 0xd5, 0x03, 0x6f, 0xa8, 0x8d, + 0xf8, 0xb3, 0x7e, 0x73, 0x09, 0x0f, 0x11, 0xbc, 0xbc, 0xb4, 0x60, 0x03, 0x32, 0x64, 0x40, 0x37, + 0xed, 0xa7, 0xe8, 0x40, 0xd5, 0xd3, 0x7c, 0xb1, 0xfa, 0x16, 0xe0, 0xd9, 0x79, 0x45, 0x69, 0x68, + 0x1d, 0xa9, 0x98, 0x04, 0xfc, 0x62, 0x4d, 0xd1, 0xa5, 0x6a, 0xb5, 0x32, 0x39, 0x54, 0xf4, 0xb7, + 0xa0, 0xf1, 0x21, 0x40, 0x22, 0xa0, 0x55, 0x65, 0xd9, 0x7c, 0xee, 0x02, 0x56, 0x14, 0x7c, 0x0e, + 0x9d, 0x60, 0xb1, 0x5a, 0x99, 0xef, 0x3f, 0x0e, 0x86, 0x86, 0xeb, 0xfb, 0x09, 0x28, 0x36, 0x44, + 0xb8, 0x3a, 0x73, 0x0d, 0xaf, 0xa5, 0xaf, 0x55, 0xdf, 0x04, 0xf5, 0x87, 0xfc, 0x2b, 0x07, 0x3d, + 0xc0, 0x53, 0x74, 0x87, 0x31, 0x30, 0x52, 0x5c, 0x96, 0x08, 0x90, 0xaa, 0x74, 0x81, 0x29, 0xf5, + 0xed, 0x65, 0x49, 0x82, 0x64, 0x73, 0xa4, 0x8d, 0xf5, 0x42, 0x1f, 0x2b, 0xd7, 0x89, 0xeb, 0xe3, + 0x52, 0xe3, 0x11, 0x7a, 0x85, 0x38, 0xa1, 0x8d, 0xd2, 0x34, 0x14, 0x9a, 0xc0, 0x52, 0xee, 0xa2, + 0x21, 0xbe, 0xa7, 0xd8, 0xd7, 0x35, 0x9f, 0x4c, 0xee, 0xb6, 0xf6, 0x4c, 0x67, 0x0f, 0xda, 0x9c, + 0xd1, 0x34, 0x5b, 0xe4, 0xde, 0x17, 0x05, 0x34, 0x39, 0x98, 0xa8, 0xa7, 0xd4, 0xd0, 0x67, 0xdc, + 0xee, 0xd4, 0xac, 0x95, 0x3e, 0x99, 0xda, 0x78, 0x8d, 0x88, 0x52, 0x9f, 0x19, 0xe0, 0x7f, 0x19, + 0xe1, 0x3a, 0x83, 0x11, 0x38, 0x35, 0xb2, 0x45, 0xb1, 0xc5, 0xcd, 0x2b, 0x28, 0xbe, 0x0c, 0x87, + 0x68, 0x9c, 0x0d, 0x4b, 0x5f, 0xd5, 0xfa, 0xa9, 0xed, 0xb4, 0x8e, 0x4f, 0x9d, 0x43, 0xa7, 0x55, + 0xb7, 0xa2, 0x62, 0x73, 0xa9, 0x95, 0x95, 0x56, 0xdd, 0xe7, 0xd6, 0xec, 0x25, 0xbd, 0x6d, 0x5e, + 0x2f, 0x7d, 0x14, 0x1f, 0xe4, 0x9e, 0x52, 0x4d, 0xe2, 0x04, 0xf0, 0x9f, 0xdb, 0xd7, 0x12, 0x4b, + 0xcd, 0xe2, 0x94, 0x2d, 0xe2, 0xf4, 0x99, 0x81, 0xbc, 0x70, 0x2d, 0xeb, 0x66, 0xbe, 0x6e, 0x2d, + 0x5f, 0xbe, 0x44, 0xe6, 0xaf, 0x8c, 0xf2, 0x83, 0xd1, 0x76, 0x0e, 0x8d, 0x9f, 0x3f, 0xfe, 0xbb, + 0x01, 0x98, 0x7b, 0xe5, 0x3e, 0x09, 0x39, 0x0c, 0xc0, 0x19, 0x03, 0x02, 0xff, 0xa5, 0xee, 0x12, + 0xb8, 0xc6, 0x38, 0x42, 0x8b, 0x3e, 0xd3, 0xbf, 0xce, 0x31, 0xeb, 0x97, 0xc4, 0x2c, 0xf9, 0x7f, + 0x66, 0x91, 0x7b, 0xef, 0x06, 0x21, 0xd8, 0x95, 0xcd, 0xcd, 0xa7, 0x85, 0x87, 0x5d, 0x95, 0x47, + 0xb4, 0x32, 0xc7, 0xe6, 0x80, 0x37, 0x8f, 0x87, 0x04, 0x11, 0x59, 0x9f, 0x86, 0x86, 0x9b, 0xd0, + 0x52, 0xeb, 0xff, 0x1f, 0x53, 0xba, 0xa5, 0xae, 0xb2, 0x8a, 0x53, 0xee, 0xf7, 0x04, 0x37, 0x6e, + 0xc3, 0xd5, 0x0a, 0x08, 0xed, 0x5f, 0xda, 0xaf, 0x38, 0xab, 0x6e, 0xa8, 0x90, 0xb7, 0x98, 0x13, + 0xe3, 0x1b, 0xe0, 0x0a, 0x80, 0x22, 0xeb, 0x23, 0x01, 0x9f, 0x3b, 0x4d, 0xbd, 0xd8, 0xd2, 0xdf, + 0x25, 0x94, 0x65, 0x61, 0xaa, 0xbd, 0x6d, 0x16, 0xbe, 0x81, 0x05, 0x20, 0xbd, 0x4a, 0xcc, 0x8c, + 0x78, 0xdc, 0xaf, 0x92, 0x70, 0xf5, 0x4e, 0xb9, 0xbe, 0x6a, 0xad, 0x2c, 0x5c, 0xc6, 0x1e, 0xe2, + 0x24, 0x6f, 0x69, 0xe3, 0x6b, 0x6e, 0x5a, 0x55, 0x0f, 0x8a, 0x03, 0xbd, 0xa7, 0x6e, 0x58, 0x59, + 0x84, 0x19, 0xd8, 0xa7, 0xeb, 0x8a, 0x41, 0xae, 0xc1, 0x34, 0x4d, 0x82, 0x71, 0x96, 0x52, 0xb0, + 0x9d, 0x07, 0x2e, 0x38, 0xfc, 0x30, 0x86, 0x3e, 0x87, 0x13, 0x37, 0x64, 0x32, 0x78, 0xbd, 0x80, + 0x09, 0xf0, 0xea, 0x60, 0x65, 0x8c, 0xa7, 0x88, 0x72, 0x05, 0x16, 0xec, 0x73, 0xec, 0xbb, 0x61, + 0x5f, 0xfb, 0x6d, 0xd9, 0x9d, 0xb4, 0xd4, 0x74, 0xbc, 0xa0, 0x88, 0xb6, 0xb4, 0x6b, 0x26, 0x6e, + 0x12, 0x7b, 0x59, 0xc1, 0xbf, 0xe3, 0x37, 0x56, 0x04, 0x27, 0x54, 0x7a, 0xc5, 0xc0, 0xbb, 0x7d, + 0x15, 0x80, 0xb7, 0x93, 0x8a, 0xb8, 0x3f, 0xe5, 0x0e, 0xf5, 0xe0, 0x48, 0xe8, 0x03, 0xba, 0x79, + 0xbd, 0x9b, 0x5b, 0xd7, 0xbb, 0x95, 0xc3, 0x1e, 0x8b, 0x65, 0x1a, 0x2c, 0xa5, 0x0b, 0x56, 0x8f, + 0x7f, 0x14, 0x7e, 0x64, 0x69, 0x61, 0x0f, 0xdc, 0xa2, 0x7c, 0x88, 0xc2, 0x21, 0x9c, 0x73, 0x1e, + 0xb9, 0x1f, 0xe3, 0x1b, 0x23, 0xba, 0x07, 0x22, 0x1c, 0xab, 0x50, 0x42, 0x18, 0xab, 0xdd, 0x58, + 0xcc, 0xe2, 0x88, 0x1a, 0xc2, 0xd3, 0x2e, 0x03, 0x30, 0x64, 0x1a, 0x73, 0x9a, 0x82, 0xb4, 0x04, + 0x0f, 0x15, 0xaf, 0xe6, 0xfa, 0xd8, 0x26, 0x06, 0x5e, 0x84, 0xeb, 0xec, 0xd3, 0x50, 0x91, 0x13, + 0x2f, 0xa1, 0x34, 0xc2, 0xd3, 0x0c, 0x13, 0x63, 0x40, 0x36, 0x63, 0x71, 0x63, 0x03, 0x78, 0x81, + 0xd1, 0xc4, 0x31, 0xef, 0x3a, 0xcf, 0x8c, 0x0d, 0xaa, 0x48, 0x32, 0x8d, 0xd4, 0xe1, 0xa5, 0x83, + 0x85, 0xc6, 0xf8, 0x6d, 0x87, 0x1c, 0x7b, 0x71, 0x50, 0xd0, 0xc8, 0x15, 0x8c, 0x49, 0x23, 0xda, + 0x35, 0x83, 0x53, 0x1c, 0x6b, 0x8c, 0xd4, 0xd2, 0xd6, 0x11, 0xa5, 0x61, 0xc0, 0x9d, 0xcb, 0x6c, + 0x94, 0x1f, 0x4a, 0x0b, 0x24, 0x2f, 0x9d, 0xc2, 0x2a, 0xe8, 0xc3, 0xae, 0x4c, 0xa6, 0xd9, 0x29, + 0xbe, 0xce, 0xe9, 0x31, 0xdc, 0x49, 0x42, 0x00, 0xa8, 0xee, 0x04, 0xf4, 0xe8, 0x00, 0x67, 0xbf, + 0x1a, 0x20, 0x6c, 0xa1, 0x73, 0xe5, 0xf8, 0x67, 0x7e, 0x8a, 0xd4, 0x15, 0x8c, 0x0e, 0xf3, 0xd8, + 0x95, 0xf8, 0x66, 0x63, 0xee, 0x2e, 0x65, 0x12, 0x5c, 0xe3, 0xe3, 0xcd, 0xcd, 0xd0, 0x78, 0xe7, + 0xb2, 0xc0, 0x33, 0xdc, 0x0c, 0xfc, 0xaa, 0x53, 0xe1, 0x30, 0xec, 0x18, 0x70, 0xc3, 0x64, 0x8c, + 0x26, 0xa0, 0x44, 0x33, 0x4c, 0x52, 0x44, 0x8b, 0xa2, 0xc1, 0xc9, 0x03, 0xbf, 0x5f, 0x9b, 0x98, + 0xb8, 0xfc, 0xd0, 0xc3, 0x69, 0x23, 0xa4, 0x10, 0xf0, 0xc1, 0xf8, 0x46, 0x97, 0xc5, 0x25, 0xb9, + 0xf9, 0x70, 0x03, 0xf0, 0x1b, 0xf8, 0x29, 0xe9, 0x8b, 0x63, 0xda, 0x9d, 0x92, 0x17, 0xe1, 0x8d, + 0x28, 0x09, 0x55, 0x99, 0x36, 0x3d, 0x82, 0xa3, 0x81, 0x81, 0x40, 0x57, 0xb2, 0x05, 0x01, 0xe1, + 0x55, 0x3b, 0x5c, 0x93, 0x15, 0x93, 0x3a, 0x7d, 0x0e, 0x17, 0xa9, 0x08, 0x93, 0x49, 0x4c, 0x68, + 0xdf, 0x99, 0xbb, 0x51, 0x36, 0x71, 0x3d, 0x48, 0xf2, 0x9c, 0x80, 0xf1, 0x2b, 0xc4, 0xf4, 0x88, + 0x22, 0xfa, 0xa9, 0xf9, 0xd7, 0xa6, 0x49, 0x5a, 0xf6, 0x6d, 0xf3, 0x4e, 0x4e, 0xbd, 0xb5, 0x7f, + 0xfb, 0xbf, 0xfe, 0xfa, 0xd8, 0x6e, 0x36, 0xfe, 0xfa, 0x78, 0x42, 0xef, 0xf6, 0xa7, 0x40, 0x78, + 0xf2, 0x77, 0x7f, 0x83, 0x07, 0x06, 0x10, 0x49, 0x8c, 0xa1, 0xda, 0x55, 0x17, 0xe5, 0x66, 0x27, + 0x59, 0x7c, 0x0f, 0x4b, 0x04, 0xa1, 0x67, 0x56, 0x2b, 0x0c, 0x4a, 0x35, 0x88, 0x13, 0x6a, 0x28, + 0xa7, 0x63, 0xad, 0xdc, 0x24, 0x48, 0xe6, 0x0f, 0x2e, 0xf8, 0x5a, 0xd3, 0xbe, 0x23, 0xff, 0xf8, + 0x0a, 0x19, 0xb4, 0xd0, 0x07, 0x61, 0x24, 0xfd, 0x93, 0x51, 0x11, 0xa3, 0xbd, 0x2f, 0xa8, 0x3f, + 0xb0, 0xa5, 0x71, 0x16, 0x84, 0x7e, 0xa1, 0x99, 0xbf, 0xe1, 0xa3, 0xbf, 0xf9, 0xdc, 0xa5, 0xac, + 0x58, 0xfa, 0x1b, 0x5d, 0x96, 0xbd, 0x78, 0x37, 0xbb, 0xf0, 0xaa, 0xaa, 0xee, 0x62, 0xd1, 0xb8, + 0xe7, 0x9e, 0xd2, 0x26, 0x31, 0xdb, 0xce, 0x81, 0xd3, 0x6c, 0x24, 0x9e, 0x73, 0xcc, 0xdf, 0x86, + 0xc1, 0x38, 0x71, 0x93, 0xa5, 0x56, 0xa2, 0xe5, 0xb4, 0x0e, 0x9d, 0x26, 0x27, 0xe4, 0xdc, 0x06, + 0x5a, 0xe7, 0x97, 0x5c, 0x06, 0xc4, 0x81, 0xd4, 0xaa, 0x9c, 0xcf, 0x6a, 0xcb, 0x97, 0x62, 0x30, + 0xe5, 0x9b, 0xe4, 0x41, 0x8b, 0x8f, 0x5d, 0xb6, 0x97, 0x1a, 0x71, 0xaf, 0x4e, 0x00, 0xd7, 0x1c, + 0x15, 0x8a, 0x22, 0x7e, 0x1b, 0x02, 0x40, 0x07, 0xd5, 0xb7, 0x5c, 0xe0, 0x27, 0x2d, 0xb0, 0xc9, + 0x4a, 0xd1, 0x3d, 0xd9, 0x8d, 0x73, 0x71, 0x3d, 0xd8, 0x8d, 0x85, 0x25, 0xa9, 0x46, 0x44, 0xf7, + 0xf5, 0xa0, 0x95, 0x7e, 0x49, 0x44, 0x4f, 0x48, 0x00, 0x0f, 0xab, 0x22, 0x3a, 0x58, 0x40, 0xe4, + 0xff, 0x9d, 0x26, 0x1a, 0xce, 0xfc, 0x5a, 0x19, 0x7d, 0xd6, 0x6b, 0x9d, 0x76, 0x67, 0x6f, 0x82, + 0xee, 0x6c, 0xaf, 0x77, 0x70, 0x64, 0xfb, 0x05, 0xa9, 0xd9, 0xcf, 0xa5, 0xf4, 0x99, 0x2d, 0x1a, + 0xc0, 0x08, 0x90, 0xb3, 0xbc, 0x57, 0x71, 0x15, 0x79, 0xb5, 0x51, 0x2c, 0x41, 0xcb, 0x21, 0xfd, + 0xa2, 0xde, 0xb4, 0xf5, 0xb9, 0xcd, 0x7a, 0x4c, 0x03, 0x42, 0xcd, 0x80, 0xd9, 0x12, 0x81, 0x62, + 0xcc, 0xdc, 0xa8, 0x55, 0x8d, 0xf6, 0x38, 0xeb, 0xbd, 0xcd, 0x64, 0x4c, 0x49, 0x89, 0xee, 0xb8, + 0x0d, 0x31, 0x4c, 0x24, 0x07, 0x1a, 0x97, 0xb1, 0x2c, 0xc5, 0xc9, 0x89, 0x6e, 0xc3, 0xbb, 0xc2, + 0x44, 0x84, 0xfd, 0x96, 0x73, 0xdc, 0x69, 0x3b, 0x87, 0xf0, 0x3d, 0x9a, 0xfc, 0x2d, 0x0a, 0x6a, + 0xa2, 0x77, 0xd8, 0x3f, 0xec, 0xfc, 0x44, 0x0a, 0xf3, 0x23, 0x40, 0xf0, 0xaf, 0x5a, 0xba, 0xcb, + 0xd7, 0x94, 0x3c, 0x4a, 0xf5, 0xc2, 0xb2, 0xf7, 0xb8, 0x9b, 0xec, 0xeb, 0x5e, 0x62, 0x64, 0x20, + 0x7d, 0x91, 0xa6, 0xb7, 0xb3, 0xbb, 0x52, 0x3e, 0x20, 0xa1, 0x44, 0x1c, 0xd8, 0xab, 0xd5, 0xac, + 0xa7, 0xcd, 0xc7, 0xce, 0xce, 0xe0, 0x0d, 0x24, 0x86, 0xc7, 0x9e, 0x34, 0x1b, 0xfc, 0x7d, 0xaf, + 0x50, 0xac, 0xbf, 0x41, 0x59, 0x31, 0x68, 0x1c, 0xd0, 0x03, 0x7b, 0xbf, 0x05, 0x31, 0x0d, 0xed, + 0xce, 0x60, 0x7f, 0x42, 0xc6, 0xbd, 0xa0, 0xd1, 0x6a, 0x36, 0xee, 0x45, 0x8c, 0xc9, 0xee, 0xbc, + 0xef, 0xe7, 0xb6, 0xde, 0xb1, 0xdd, 0xf1, 0x73, 0x3b, 0xf0, 0x18, 0x0c, 0x81, 0xaf, 0x9a, 0x6b, + 0x7d, 0xbd, 0x8b, 0xf3, 0xd2, 0xd4, 0xf5, 0x73, 0xef, 0xd3, 0x1a, 0xe7, 0xd9, 0x22, 0x10, 0xe1, + 0xdc, 0x32, 0xe5, 0x8b, 0xc6, 0x14, 0x71, 0x9a, 0xa9, 0x33, 0xa7, 0xf3, 0x38, 0x59, 0xfe, 0x0c, + 0xea, 0x3a, 0x73, 0x78, 0x76, 0x7d, 0x6d, 0x76, 0xcc, 0xe1, 0xf9, 0xe5, 0xfb, 0x4f, 0x97, 0x3f, + 0x73, 0x52, 0x50, 0xa8, 0x50, 0x09, 0x34, 0x58, 0xac, 0x0f, 0x17, 0x03, 0x88, 0x9a, 0x44, 0xef, + 0x48, 0x12, 0x07, 0xe1, 0x70, 0x52, 0x70, 0x49, 0x85, 0x47, 0xb8, 0x7d, 0xea, 0x6b, 0x08, 0x8f, + 0x55, 0xa9, 0x0d, 0xdd, 0xea, 0xca, 0x6a, 0xc8, 0xd2, 0xa3, 0x62, 0x63, 0x93, 0x84, 0xd2, 0xc6, + 0x8c, 0xba, 0x8b, 0x1c, 0xac, 0xc0, 0xb5, 0x73, 0x80, 0xd4, 0x49, 0x28, 0xfd, 0x48, 0xdd, 0x05, + 0xb4, 0xb3, 0xdf, 0x6a, 0x42, 0x34, 0x2d, 0xe3, 0x97, 0x77, 0xa5, 0x06, 0x42, 0x70, 0x67, 0x60, + 0x20, 0x28, 0x8b, 0x5f, 0x46, 0xb9, 0x19, 0xf1, 0xfc, 0x2c, 0x94, 0x76, 0xc5, 0xad, 0xed, 0x4d, + 0xc2, 0x8c, 0xcd, 0x70, 0x30, 0x50, 0xd7, 0x0f, 0xd8, 0x22, 0x74, 0x97, 0x1f, 0xe0, 0xe1, 0xaf, + 0x8c, 0x57, 0xd4, 0xa3, 0x2a, 0x83, 0x71, 0x60, 0xce, 0x4a, 0x4d, 0xa4, 0x41, 0x48, 0xa5, 0xd4, + 0x2c, 0x1a, 0xb8, 0x81, 0x47, 0x10, 0xb0, 0xd5, 0xd8, 0x37, 0x5a, 0x27, 0xc7, 0x06, 0x16, 0x29, + 0x55, 0xf3, 0x93, 0x78, 0xc1, 0x60, 0xcd, 0x29, 0x00, 0x36, 0xdf, 0xc3, 0x5f, 0x18, 0x0e, 0x58, + 0x2f, 0xf3, 0x47, 0x46, 0x33, 0xd0, 0x40, 0xc5, 0x59, 0x3a, 0x86, 0xef, 0x43, 0xd9, 0x1b, 0x1f, + 0xbe, 0xa7, 0x0b, 0x0c, 0x2f, 0x8c, 0x12, 0xb9, 0x1b, 0x24, 0x95, 0x69, 0x70, 0x83, 0x04, 0x60, + 0xa6, 0x9f, 0xf9, 0x57, 0x40, 0x30, 0xeb, 0xf5, 0x5c, 0x45, 0x29, 0xba, 0x35, 0xdf, 0x9d, 0x5d, + 0x9c, 0x5d, 0x0e, 0xce, 0xdf, 0x9b, 0xc4, 0xfc, 0xf0, 0xe9, 0xfc, 0x02, 0xfe, 0x7d, 0x77, 0x76, + 0x73, 0x73, 0xfe, 0xe5, 0x77, 0xf3, 0x4e, 0x8b, 0xdb, 0x2c, 0x63, 0x56, 0xdd, 0x24, 0x34, 0xf2, + 0x3f, 0xdf, 0x0f, 0x69, 0xf2, 0x31, 0xce, 0x12, 0x68, 0xb2, 0xb0, 0x7d, 0xe5, 0xc9, 0x23, 0x79, + 0x90, 0xab, 0xfb, 0xb7, 0x18, 0x27, 0xdb, 0xd2, 0x9e, 0x14, 0xe7, 0xb2, 0x6d, 0xaf, 0x8d, 0xaf, + 0x5c, 0x9f, 0xc0, 0xde, 0x36, 0xfb, 0xe6, 0x1e, 0xaa, 0x45, 0x20, 0x13, 0x83, 0x31, 0xff, 0xba, + 0x3f, 0xd3, 0x2f, 0x4f, 0xd5, 0x4f, 0x92, 0x45, 0x1c, 0x3b, 0x9b, 0x3a, 0x19, 0x1b, 0x7f, 0x8c, + 0x59, 0xaa, 0x74, 0x73, 0x7d, 0xf3, 0xd7, 0xeb, 0x77, 0xc6, 0x2c, 0x66, 0xa9, 0xa1, 0xee, 0x44, + 0x68, 0x3d, 0x8b, 0x16, 0x61, 0x36, 0xcd, 0x71, 0xfd, 0xae, 0x01, 0x17, 0x47, 0x64, 0xf8, 0x01, + 0x03, 0x0d, 0xf5, 0x94, 0x1a, 0x3c, 0xd1, 0xaa, 0xd9, 0x51, 0x23, 0xfd, 0xc2, 0x7b, 0xf9, 0x8c, + 0x31, 0xd2, 0x61, 0x6c, 0x23, 0x77, 0x0c, 0x68, 0x99, 0x62, 0xb4, 0xef, 0xfa, 0xd2, 0x79, 0x0c, + 0xf0, 0xfa, 0xf7, 0x18, 0x19, 0x1c, 0x46, 0x73, 0xe0, 0x1c, 0x1e, 0x19, 0x5f, 0x31, 0x86, 0x38, + 0xbd, 0x4d, 0x1d, 0xb4, 0xa3, 0x0c, 0x93, 0x78, 0x12, 0x84, 0xf4, 0x6e, 0xb5, 0xca, 0xd7, 0x47, + 0xa9, 0x5c, 0xdc, 0x82, 0x69, 0x00, 0xd6, 0xf5, 0xf8, 0x18, 0x76, 0x23, 0x4f, 0x48, 0xc6, 0x54, + 0xf6, 0xb5, 0xb4, 0xe2, 0x37, 0x9f, 0x6f, 0xb9, 0x3f, 0xe1, 0x36, 0x5f, 0xa9, 0xdc, 0xe0, 0x1e, + 0x45, 0xb9, 0xdf, 0x7c, 0x1f, 0x51, 0xb9, 0xfc, 0xf7, 0x1a, 0x7e, 0xe2, 0x1e, 0xbc, 0x49, 0x82, + 0xc5, 0x67, 0x26, 0x91, 0xd2, 0x85, 0x87, 0x78, 0x86, 0x50, 0x07, 0x86, 0x46, 0x85, 0x38, 0x6a, + 0x70, 0xee, 0x42, 0x78, 0xdd, 0x0b, 0x8e, 0xc3, 0x4a, 0x1d, 0x1e, 0xa5, 0x9e, 0xc1, 0x55, 0xca, + 0x1d, 0x96, 0x27, 0x22, 0x7e, 0xf2, 0xe4, 0xb6, 0x75, 0x07, 0x41, 0x93, 0x27, 0xb7, 0xed, 0x3b, + 0x95, 0x60, 0x69, 0x72, 0x7b, 0x72, 0xb7, 0x46, 0xff, 0x49, 0xa0, 0x4d, 0x60, 0xeb, 0x78, 0xa6, + 0xa2, 0x4c, 0x25, 0xd5, 0x99, 0xdc, 0x1e, 0x40, 0xcd, 0xee, 0x03, 0x5a, 0x52, 0xd4, 0x17, 0xf3, + 0xee, 0x95, 0x21, 0x23, 0x26, 0xb7, 0x26, 0x84, 0x2f, 0x30, 0x21, 0x56, 0x81, 0xba, 0x6c, 0xee, + 0xc8, 0x6d, 0xae, 0xa2, 0x56, 0x4e, 0xc7, 0x18, 0xed, 0x78, 0x7c, 0x7a, 0xe8, 0x9b, 0x77, 0x36, + 0x29, 0x35, 0xfb, 0x40, 0xe9, 0x37, 0xd5, 0x68, 0x52, 0x68, 0x54, 0x8e, 0x68, 0x5b, 0xab, 0x3c, + 0x86, 0xb2, 0xad, 0x40, 0xc9, 0x08, 0xea, 0x87, 0x2f, 0x2d, 0x2c, 0x35, 0xc6, 0x9b, 0x11, 0x44, + 0xbe, 0xe8, 0xf5, 0x0e, 0xe2, 0xe6, 0x06, 0x6f, 0x77, 0x09, 0xe3, 0x79, 0x28, 0x37, 0x24, 0x5c, + 0x16, 0xfc, 0x1a, 0xa1, 0x2b, 0x50, 0xf0, 0x30, 0x7f, 0xab, 0xf7, 0xa1, 0xca, 0x46, 0x2c, 0x85, + 0x20, 0x2f, 0x8e, 0x26, 0xc1, 0x34, 0x03, 0xe8, 0x1f, 0x28, 0x43, 0x28, 0xe8, 0xdf, 0xa0, 0x39, + 0x2d, 0xff, 0x86, 0xe2, 0x25, 0x26, 0x82, 0x8f, 0x98, 0xe5, 0x4e, 0xaf, 0x85, 0x01, 0x9a, 0x76, + 0x77, 0x56, 0xef, 0xc8, 0x37, 0x91, 0x62, 0x86, 0x84, 0xfb, 0x73, 0x83, 0x1b, 0xe2, 0xfd, 0xc7, + 0x58, 0xa2, 0xcc, 0x75, 0x4c, 0x9c, 0x04, 0x0d, 0xce, 0x68, 0x57, 0x87, 0x9f, 0xc2, 0x74, 0x8b, + 0x7f, 0x2f, 0x5c, 0x44, 0x9e, 0xc7, 0x0b, 0xa0, 0xe6, 0x05, 0xf7, 0xd6, 0x2f, 0x31, 0xba, 0xfb, + 0x4c, 0x9c, 0x24, 0x0e, 0x69, 0xee, 0xde, 0x3a, 0x71, 0x18, 0xa5, 0x90, 0x54, 0xa6, 0x12, 0x7a, + 0x65, 0x22, 0xed, 0xe8, 0xd2, 0xd2, 0xa6, 0x3d, 0x2a, 0xae, 0x8a, 0x9d, 0x3b, 0xe2, 0xce, 0xa4, + 0xb7, 0xbf, 0x57, 0x58, 0x24, 0x79, 0x47, 0x8b, 0x75, 0xf2, 0x6a, 0xd6, 0xa9, 0x12, 0x05, 0x4c, + 0xad, 0x9b, 0xb7, 0x75, 0xdd, 0xc0, 0xea, 0x57, 0x8c, 0x49, 0xe1, 0x70, 0x77, 0xc0, 0x72, 0x73, + 0x2f, 0x59, 0x2b, 0xce, 0x1c, 0x6c, 0x5b, 0x2d, 0x1e, 0x37, 0x74, 0xb5, 0xb2, 0x26, 0xce, 0xb7, + 0x20, 0xf2, 0x05, 0xee, 0x47, 0x06, 0x24, 0x55, 0x3e, 0xb5, 0x76, 0xcd, 0x74, 0x06, 0x91, 0x17, + 0xcf, 0x83, 0x68, 0x8a, 0x6a, 0x07, 0x6e, 0x56, 0x06, 0x2d, 0x77, 0xa4, 0xd4, 0xd6, 0xb8, 0x1c, + 0x9a, 0xef, 0xd2, 0x48, 0xf3, 0xfa, 0x98, 0x08, 0xd7, 0x4e, 0xf4, 0x75, 0xf4, 0x2a, 0xd3, 0x9d, + 0xf5, 0xaa, 0xd1, 0xd0, 0x80, 0x9b, 0xcb, 0x50, 0xd9, 0x55, 0xc7, 0x2b, 0x29, 0x05, 0xa3, 0x20, + 0xb5, 0x59, 0x99, 0x5d, 0x2a, 0xf2, 0x73, 0x9e, 0xeb, 0xf3, 0x24, 0x96, 0xe5, 0x5a, 0xcc, 0x73, + 0xa3, 0x01, 0x7f, 0xf9, 0x99, 0x15, 0xeb, 0xe0, 0x9d, 0xd1, 0x58, 0xf0, 0x4b, 0xa3, 0x5a, 0x53, + 0xbf, 0x52, 0x90, 0xb3, 0x78, 0xa8, 0xde, 0xd8, 0x39, 0x57, 0x57, 0xa9, 0x2e, 0x4a, 0xc8, 0xe1, + 0x7e, 0x76, 0x67, 0x5b, 0x1a, 0x41, 0x01, 0x96, 0xbb, 0x1d, 0x6e, 0x6c, 0x47, 0x95, 0xf9, 0x7c, + 0x5f, 0x69, 0x69, 0x91, 0x04, 0xf7, 0xb0, 0xdf, 0x6c, 0xc7, 0x9b, 0x51, 0xef, 0x1b, 0xc8, 0xaa, + 0xaf, 0x32, 0x47, 0x3c, 0xbd, 0x8c, 0xd3, 0x60, 0x22, 0x34, 0x31, 0x0c, 0x97, 0x63, 0x52, 0xd8, + 0xfd, 0x8b, 0x59, 0xe2, 0x32, 0x8c, 0x95, 0x32, 0x71, 0x3c, 0xb1, 0xe3, 0xd5, 0xee, 0xb6, 0x32, + 0xe7, 0x8f, 0x2c, 0xf0, 0xbe, 0x0d, 0x79, 0x21, 0x7e, 0x25, 0xd4, 0xe3, 0xfc, 0x41, 0xc7, 0xc5, + 0xe3, 0x83, 0xc3, 0x96, 0xfa, 0x1f, 0x50, 0xcb, 0xe0, 0x6d, 0x1b, 0xaf, 0x9f, 0xc2, 0xbd, 0x16, + 0x6c, 0x98, 0x29, 0x94, 0x12, 0xde, 0x69, 0xdd, 0xa9, 0x50, 0x12, 0xc3, 0xd6, 0x31, 0xc9, 0x14, + 0x44, 0x8e, 0x0b, 0x4e, 0x55, 0x0f, 0x4f, 0xc8, 0x14, 0x63, 0x61, 0x02, 0x5d, 0xe3, 0x6d, 0xf4, + 0x42, 0x32, 0x15, 0x73, 0x33, 0x23, 0x73, 0xb9, 0xc3, 0xa6, 0xe8, 0xe0, 0xcb, 0x7f, 0xcf, 0xc1, + 0xc2, 0x0f, 0x51, 0x77, 0x5e, 0x8c, 0x0f, 0xf8, 0x85, 0x69, 0x22, 0x07, 0x44, 0x35, 0xe3, 0x51, + 0x4a, 0xc5, 0x94, 0x4b, 0x01, 0x04, 0xd8, 0x30, 0xf5, 0xb0, 0x86, 0x09, 0x1b, 0xc1, 0xcd, 0xa1, + 0xd6, 0x72, 0x16, 0x2c, 0x20, 0x6e, 0x3c, 0x0f, 0x7a, 0x55, 0x79, 0xc1, 0x99, 0xb2, 0x2e, 0x06, + 0x6b, 0x46, 0x87, 0x37, 0x70, 0x71, 0x23, 0x80, 0x18, 0x22, 0xa8, 0xea, 0x07, 0xb9, 0x87, 0xfc, + 0x52, 0x08, 0x04, 0xf3, 0xaf, 0xc2, 0xc1, 0x0e, 0x3f, 0x29, 0x25, 0xca, 0x21, 0xb7, 0xe0, 0xb3, + 0x77, 0x16, 0x86, 0x96, 0xe9, 0x70, 0x5d, 0xa5, 0x99, 0x2f, 0x10, 0x2b, 0x46, 0xa6, 0x4f, 0xe3, + 0xe9, 0x34, 0x04, 0x15, 0x3a, 0x37, 0x8d, 0x12, 0xa6, 0xe6, 0x98, 0xd7, 0xc4, 0xf0, 0xe3, 0xf6, + 0xd6, 0x2e, 0x22, 0xf7, 0xde, 0x50, 0x9a, 0x91, 0xef, 0xee, 0x46, 0x2c, 0x4b, 0xaa, 0xe2, 0xa6, + 0x3d, 0x81, 0x42, 0xb3, 0x73, 0x6b, 0x5e, 0xdd, 0xd3, 0x84, 0xc7, 0xe4, 0x32, 0x2f, 0xcf, 0xaf, + 0x2e, 0x87, 0x57, 0x83, 0x5f, 0xce, 0x6f, 0x3e, 0x0f, 0x4c, 0x3d, 0xbb, 0xa3, 0xf4, 0xde, 0x42, + 0xf3, 0xcf, 0xc7, 0xb3, 0xcb, 0xcb, 0xf3, 0x8b, 0x6b, 0x63, 0xc7, 0x10, 0x9e, 0x2a, 0x79, 0xee, + 0x47, 0xe1, 0xae, 0x07, 0x4d, 0x5d, 0xbd, 0x3f, 0x87, 0x22, 0x17, 0x57, 0x83, 0xb3, 0x9b, 0x4f, + 0x57, 0x97, 0xd7, 0xe6, 0x9d, 0xc8, 0x05, 0x79, 0xcb, 0x0d, 0x4c, 0x70, 0xb1, 0x7e, 0xfa, 0x7a, + 0x6e, 0x7c, 0xf9, 0x60, 0xe6, 0x29, 0x1d, 0x3b, 0xb7, 0xe6, 0x0d, 0x68, 0x89, 0x13, 0x51, 0xe2, + 0xfa, 0xe6, 0xea, 0xcb, 0xd9, 0xcf, 0xe7, 0xc4, 0x18, 0x5e, 0xfd, 0x76, 0xfe, 0x05, 0x7b, 0xbc, + 0x1e, 0x5e, 0x9c, 0xfd, 0x6e, 0xde, 0x21, 0x7b, 0xc8, 0xa7, 0x4e, 0x6a, 0x70, 0xe9, 0x6d, 0x7a, + 0xd7, 0x77, 0x6e, 0x9b, 0xc0, 0x84, 0xca, 0xf0, 0x99, 0x9c, 0x0b, 0x17, 0xe5, 0xbe, 0x05, 0xde, + 0x37, 0x80, 0xfc, 0x88, 0x82, 0x2d, 0x28, 0x58, 0xf8, 0x64, 0xa1, 0x88, 0x55, 0x7e, 0x65, 0x10, + 0x8b, 0x36, 0x0f, 0x88, 0xfe, 0x95, 0xe9, 0xce, 0xcd, 0xbc, 0xa8, 0xdc, 0xe4, 0xb8, 0x8d, 0xf0, + 0x09, 0x9f, 0x8a, 0x8d, 0xfb, 0x1e, 0xdc, 0x21, 0xb1, 0x9c, 0xfc, 0x64, 0x73, 0x67, 0x07, 0x23, + 0xfb, 0x71, 0x5e, 0x5e, 0x39, 0x99, 0x5a, 0x4f, 0x69, 0x0c, 0x69, 0xc2, 0xc6, 0x74, 0xe6, 0xde, + 0x07, 0x71, 0xd2, 0x31, 0xd9, 0x3c, 0x8e, 0xd3, 0x99, 0xb9, 0xae, 0x44, 0x51, 0xfc, 0x19, 0x23, + 0xc5, 0xa7, 0x0e, 0x44, 0x35, 0x03, 0x79, 0x8c, 0x4e, 0xdc, 0x2c, 0x4c, 0x2d, 0x2d, 0x12, 0x43, + 0xc9, 0x37, 0x95, 0x40, 0xec, 0x3a, 0x3c, 0xcf, 0x42, 0xc3, 0x49, 0x2a, 0x9e, 0xdb, 0x18, 0x53, + 0x64, 0xb5, 0x52, 0x59, 0x50, 0xe0, 0xc1, 0x7d, 0xea, 0xf0, 0x78, 0x93, 0x16, 0x93, 0x6e, 0x6e, + 0x6f, 0xaf, 0x55, 0x56, 0x99, 0x0b, 0x4b, 0xb9, 0xf8, 0x05, 0xcc, 0x80, 0x34, 0x84, 0x46, 0xeb, + 0xb0, 0x69, 0x8c, 0x97, 0x29, 0xee, 0x1a, 0x99, 0x95, 0x91, 0x73, 0xc8, 0x10, 0xb1, 0x1b, 0x22, + 0x53, 0xe4, 0x5d, 0x8a, 0xf4, 0x6a, 0x5a, 0x24, 0xee, 0x2d, 0x1e, 0x78, 0x5a, 0x48, 0x6e, 0x70, + 0x27, 0x56, 0x31, 0xb9, 0x99, 0x30, 0xcf, 0xab, 0x94, 0x67, 0xad, 0x35, 0x18, 0x5e, 0x94, 0xb9, + 0x6c, 0x28, 0x43, 0x46, 0x72, 0x2c, 0x0a, 0x8f, 0xab, 0xe8, 0x73, 0xb0, 0x52, 0x24, 0x3d, 0xf9, + 0xfb, 0x1f, 0xf1, 0x2a, 0x17, 0xae, 0xe5, 0x37, 0x5a, 0xb0, 0x66, 0x55, 0x44, 0x8b, 0x42, 0x4e, + 0x98, 0xdd, 0xe1, 0x15, 0x4a, 0x25, 0x39, 0x48, 0x58, 0xd3, 0xac, 0x32, 0x9b, 0xa7, 0x81, 0x41, + 0x28, 0x87, 0x89, 0xa2, 0xb0, 0x6f, 0x92, 0x57, 0xaa, 0xd1, 0x9d, 0x9d, 0x4a, 0x7e, 0xb5, 0xa4, + 0x5f, 0x4e, 0xb0, 0xb6, 0xb3, 0x63, 0xc5, 0xa5, 0x67, 0xbd, 0xa4, 0x5c, 0x88, 0x87, 0xa6, 0xd7, + 0x16, 0x44, 0x74, 0x26, 0x03, 0x4f, 0xe6, 0xa3, 0x90, 0x79, 0x22, 0xcb, 0x15, 0x2a, 0xf9, 0x23, + 0xe3, 0x82, 0x73, 0xf0, 0x30, 0x47, 0x4d, 0xe5, 0x9b, 0xa2, 0xc6, 0x07, 0x1a, 0x27, 0x5e, 0x77, + 0x89, 0x14, 0x7e, 0xcf, 0x1a, 0x7b, 0x9f, 0x82, 0xa2, 0xe1, 0xf5, 0xd3, 0x75, 0xba, 0xe6, 0x5b, + 0x65, 0x64, 0x6b, 0xfe, 0xd1, 0xa2, 0x5c, 0x41, 0x51, 0xf4, 0xf6, 0x3a, 0xcd, 0x41, 0x69, 0x7a, + 0xac, 0x20, 0x9e, 0x87, 0xd0, 0x89, 0x23, 0xcb, 0x75, 0xbe, 0x50, 0xb6, 0x88, 0x23, 0x46, 0x21, + 0xfb, 0x23, 0x73, 0x44, 0xf8, 0x90, 0xcf, 0x6c, 0x0a, 0x68, 0x39, 0x42, 0x7b, 0x6f, 0x9f, 0x6e, + 0x78, 0xa8, 0xed, 0xa7, 0x62, 0x18, 0xfe, 0x0e, 0xc5, 0xfc, 0x2a, 0xf5, 0x8d, 0x88, 0x88, 0xf4, + 0x75, 0x8d, 0x14, 0x82, 0xd5, 0xeb, 0x8d, 0x68, 0x59, 0x44, 0xb8, 0x1f, 0xeb, 0xab, 0x54, 0x05, + 0x98, 0x4d, 0xf5, 0x00, 0xb3, 0xab, 0x95, 0x05, 0x79, 0x3d, 0xf3, 0xe2, 0xb8, 0x1c, 0xdc, 0x12, + 0xaf, 0x3f, 0x2c, 0xe5, 0x33, 0x79, 0xb0, 0xcc, 0x71, 0x9c, 0xab, 0xd2, 0xf2, 0x1c, 0x28, 0xdc, + 0x2e, 0x69, 0xda, 0xa4, 0x0d, 0x58, 0x69, 0x9f, 0x5a, 0xc3, 0xd4, 0xd6, 0xb2, 0xd6, 0x42, 0x86, + 0xdb, 0xb5, 0xad, 0x65, 0x77, 0xbd, 0x00, 0x5b, 0x53, 0x00, 0x41, 0x5f, 0x79, 0xbe, 0xd5, 0xf2, + 0x06, 0xb0, 0xf3, 0x4f, 0x02, 0xf5, 0x43, 0xf1, 0xab, 0x0a, 0x19, 0x49, 0x65, 0xb1, 0x3c, 0x69, + 0x31, 0x62, 0x96, 0xf8, 0xe7, 0x43, 0x49, 0xfc, 0x13, 0x57, 0xf9, 0x99, 0xa1, 0x5f, 0x58, 0x85, + 0xd4, 0x28, 0x1e, 0x4f, 0x19, 0xcf, 0xb8, 0x1d, 0x91, 0xc7, 0x41, 0x34, 0x06, 0x60, 0xe0, 0x35, + 0x82, 0x14, 0x2d, 0x63, 0x91, 0x91, 0x3e, 0x93, 0x1b, 0x56, 0x04, 0x49, 0xaf, 0x8e, 0x51, 0xc8, + 0x0c, 0x6a, 0x90, 0x95, 0x2c, 0xb1, 0xe6, 0x05, 0x46, 0xf8, 0xcd, 0x53, 0x36, 0x03, 0x9c, 0xd4, + 0x28, 0xa6, 0x59, 0xde, 0xdc, 0xbc, 0xca, 0xdb, 0x9c, 0xf7, 0x50, 0x5a, 0x6a, 0x6c, 0x5e, 0x16, + 0x13, 0x6d, 0xc3, 0x4a, 0xf0, 0xfe, 0x5f, 0xb4, 0x80, 0x5f, 0x68, 0xfd, 0xd2, 0xe5, 0x3b, 0x7a, + 0x98, 0x81, 0x55, 0x0a, 0x76, 0xf3, 0x67, 0x36, 0x15, 0x30, 0x64, 0x3e, 0xa2, 0x54, 0xa6, 0x9d, + 0xf6, 0xa9, 0xf5, 0x91, 0x95, 0x1a, 0x96, 0x54, 0xe1, 0xe5, 0xcd, 0x5f, 0xd2, 0x07, 0xee, 0xaa, + 0x89, 0xe7, 0x44, 0x25, 0x83, 0xd1, 0x63, 0xf1, 0x7f, 0x8a, 0x7c, 0xfa, 0x08, 0x11, 0xa6, 0x12, + 0x20, 0x97, 0x79, 0x40, 0xfe, 0x5e, 0xaf, 0x57, 0xf4, 0x60, 0x07, 0x93, 0xc0, 0xdb, 0x5e, 0xb3, + 0xaf, 0xa5, 0x10, 0x61, 0x77, 0x3d, 0xaa, 0xe5, 0x18, 0x71, 0xb2, 0x88, 0xcd, 0x82, 0x09, 0xf8, + 0xe2, 0xf3, 0x08, 0xfd, 0xbf, 0x73, 0x72, 0x06, 0x0a, 0x2a, 0xcd, 0x8f, 0xe0, 0x8c, 0x3b, 0xcc, + 0xaf, 0x31, 0x4e, 0x33, 0xde, 0xd2, 0xa3, 0xf2, 0x92, 0xe9, 0xdf, 0x20, 0x3e, 0xa0, 0x32, 0x3d, + 0x1f, 0x0a, 0xd3, 0xf3, 0xb4, 0xae, 0xad, 0x7c, 0x0d, 0x37, 0x08, 0xa8, 0x09, 0x92, 0x39, 0xf5, + 0xb5, 0x49, 0xb8, 0x2d, 0x66, 0xa3, 0xd4, 0x72, 0x85, 0xc9, 0x04, 0x39, 0x51, 0x35, 0x5c, 0xc6, + 0xce, 0x4e, 0x54, 0x93, 0x07, 0xa1, 0x63, 0xda, 0xf0, 0x22, 0x8f, 0xc0, 0x21, 0xa8, 0x3c, 0x3c, + 0x2c, 0xdd, 0x10, 0x90, 0x0b, 0xc5, 0xf5, 0xbe, 0xc1, 0xd0, 0xec, 0x2e, 0xc3, 0x0c, 0x07, 0xf2, + 0x06, 0x10, 0xba, 0x31, 0x75, 0x09, 0x20, 0x67, 0x7c, 0x61, 0xc9, 0x80, 0x32, 0x62, 0xa4, 0x46, + 0x5e, 0xac, 0x7e, 0xc1, 0x2f, 0xe2, 0xe9, 0x97, 0xc7, 0xf7, 0x10, 0xe0, 0x9f, 0xa2, 0xb7, 0x3e, + 0x8f, 0xf9, 0xff, 0x84, 0x5f, 0x8b, 0xbf, 0x41, 0xa3, 0x47, 0x14, 0x90, 0x3e, 0xc7, 0xc3, 0x13, + 0x81, 0x62, 0xef, 0x28, 0x3c, 0xfb, 0x9a, 0x54, 0x12, 0x00, 0xd0, 0xef, 0x0a, 0xfd, 0xcf, 0xf3, + 0xb1, 0xfc, 0xce, 0x61, 0xc7, 0xdf, 0xcd, 0x50, 0x63, 0x98, 0xe6, 0xad, 0xc1, 0x08, 0x40, 0x2a, + 0x28, 0xb0, 0xd5, 0x60, 0xb5, 0xda, 0xd2, 0xcf, 0x2d, 0x94, 0x6d, 0x00, 0xac, 0xe2, 0xee, 0xcf, + 0xf7, 0x03, 0xd5, 0xa1, 0x9f, 0x6a, 0x5c, 0xa4, 0xba, 0x46, 0x10, 0x73, 0x08, 0x98, 0x06, 0x08, + 0xb2, 0x94, 0x0a, 0x0d, 0xa7, 0xb8, 0x8e, 0xb1, 0x0d, 0x2d, 0x00, 0x49, 0xb5, 0x3a, 0xcb, 0xc6, + 0xf3, 0x20, 0x35, 0xc9, 0xcf, 0x09, 0x96, 0x2d, 0xdf, 0xea, 0xd5, 0x0a, 0xfc, 0x0d, 0x19, 0xc6, + 0x58, 0x5e, 0x84, 0xd4, 0x10, 0x41, 0xa9, 0xeb, 0x07, 0x98, 0x7f, 0xa5, 0x38, 0x51, 0x8a, 0xb4, + 0x5d, 0x58, 0x32, 0xe6, 0x87, 0x68, 0x00, 0xf7, 0x5c, 0x99, 0xd2, 0xe1, 0x9b, 0x9a, 0x5b, 0xea, + 0xf9, 0x75, 0x00, 0x57, 0x51, 0x30, 0xd4, 0x7f, 0xdf, 0x5a, 0x3c, 0xe5, 0x6e, 0xb5, 0xf9, 0x9a, + 0x88, 0x27, 0xe4, 0x4f, 0xf5, 0x09, 0xc9, 0x57, 0xb7, 0x08, 0x6e, 0x54, 0x08, 0x84, 0x1b, 0xa5, + 0x88, 0x35, 0x5f, 0xc8, 0x4a, 0xe8, 0xef, 0xe7, 0x26, 0xbb, 0x70, 0x8f, 0x5c, 0x58, 0xe6, 0x35, + 0x10, 0x75, 0xd8, 0x0e, 0x21, 0x05, 0x71, 0xa9, 0x34, 0xd3, 0xd7, 0xf5, 0xcc, 0x40, 0xb7, 0xc6, + 0xb6, 0xbf, 0xb1, 0xe3, 0x3c, 0x47, 0xfa, 0x56, 0x44, 0x80, 0x1e, 0xdb, 0x5d, 0xf0, 0xe4, 0x9c, + 0xb1, 0x46, 0xa7, 0x07, 0x4e, 0x8d, 0x41, 0xb3, 0x0f, 0x6c, 0x01, 0x77, 0xad, 0x96, 0x3c, 0xb4, + 0x24, 0xe2, 0x9c, 0xbb, 0xcd, 0xdf, 0x6f, 0xcc, 0x82, 0xbe, 0x75, 0x1c, 0x2d, 0x48, 0xcc, 0x50, + 0x97, 0x3f, 0x6d, 0xeb, 0xcc, 0x5a, 0x70, 0x23, 0x13, 0x0d, 0xd5, 0xd0, 0x17, 0x73, 0xfd, 0x82, + 0xe9, 0xec, 0x68, 0x49, 0xde, 0x8b, 0xe5, 0x07, 0x71, 0x16, 0xfa, 0x22, 0xa9, 0x80, 0x28, 0xa2, + 0x2f, 0x83, 0x38, 0x6e, 0xb5, 0x48, 0xc9, 0x8d, 0xa3, 0xbd, 0x89, 0x0b, 0xd5, 0x38, 0x6a, 0xae, + 0x81, 0x48, 0xb9, 0x6d, 0xd5, 0x7e, 0x63, 0x85, 0x6a, 0x9e, 0x1b, 0x79, 0x34, 0xfc, 0x8e, 0x0a, + 0x7e, 0x1c, 0x7d, 0x4f, 0xfb, 0x0a, 0x81, 0xb8, 0xfd, 0x58, 0x56, 0x40, 0x80, 0x39, 0x0c, 0x12, + 0xdd, 0x37, 0x84, 0xb3, 0x65, 0x0e, 0x86, 0xec, 0x8a, 0x67, 0xb4, 0xcf, 0x75, 0x5f, 0x9d, 0xef, + 0xc0, 0x49, 0xd2, 0xbe, 0xf9, 0x31, 0xf0, 0x01, 0xd7, 0xfe, 0xe7, 0x00, 0x93, 0x12, 0xae, 0x04, + 0x57, 0x68, 0xb7, 0x8a, 0x34, 0xdc, 0x4c, 0x88, 0xb9, 0xc0, 0x9f, 0x22, 0x23, 0xb2, 0x59, 0xca, + 0xaf, 0x83, 0x9b, 0x12, 0xd6, 0xdb, 0x02, 0x21, 0x25, 0x91, 0x26, 0xbf, 0x29, 0x41, 0x8d, 0xc4, + 0xbd, 0x1a, 0x51, 0x1f, 0xc3, 0xd9, 0xbf, 0x69, 0xad, 0x56, 0xd1, 0xdb, 0x83, 0xb6, 0x26, 0xf4, + 0x6b, 0x98, 0xae, 0x39, 0x64, 0x59, 0x1e, 0x53, 0xa3, 0xf5, 0x8f, 0xff, 0xf8, 0xef, 0x83, 0x76, + 0x59, 0xf4, 0xdf, 0x0c, 0xec, 0x84, 0xa6, 0xe3, 0x57, 0x98, 0xf8, 0xd6, 0x8a, 0xdf, 0x9c, 0xae, + 0x56, 0xf1, 0xdb, 0xe3, 0x43, 0x5b, 0xeb, 0x63, 0x28, 0x81, 0x62, 0xb2, 0x07, 0x54, 0xf4, 0x03, + 0x8a, 0xf0, 0xf4, 0x1f, 0xff, 0xf1, 0xdf, 0xc7, 0x87, 0xdb, 0xba, 0xd2, 0xbe, 0x59, 0x76, 0xa7, + 0x0f, 0xc4, 0xbd, 0xa7, 0x1b, 0xa9, 0xd0, 0x7d, 0x6c, 0xf1, 0x74, 0x35, 0x39, 0x86, 0x18, 0x71, + 0xb9, 0x94, 0xc8, 0x46, 0x3b, 0x6c, 0x6d, 0x93, 0xb3, 0xd8, 0xca, 0x33, 0xe2, 0x20, 0x2f, 0xaf, + 0xc1, 0xf7, 0xa1, 0x03, 0xdf, 0x31, 0xb8, 0x24, 0x07, 0x72, 0x0b, 0x07, 0xb9, 0x05, 0xd1, 0xd4, + 0xa9, 0x21, 0x5c, 0xf9, 0x79, 0x87, 0x7a, 0xb2, 0x09, 0x99, 0x31, 0xa5, 0x96, 0x90, 0x6d, 0xfc, + 0x12, 0x49, 0xc7, 0x6a, 0x30, 0xcf, 0x2f, 0x20, 0xd5, 0x00, 0xfa, 0xe1, 0xfc, 0x2b, 0x5c, 0xb7, + 0x3c, 0xe3, 0xb2, 0x0e, 0xf9, 0x2c, 0xe0, 0x3c, 0xfb, 0x06, 0x20, 0xfe, 0x84, 0x29, 0xf4, 0x21, + 0x08, 0xc3, 0x1c, 0xcb, 0x67, 0xa3, 0x99, 0x61, 0x1b, 0xee, 0x7a, 0xc3, 0x94, 0xbb, 0x0b, 0x73, + 0x6d, 0x13, 0x40, 0x62, 0xe3, 0x04, 0x63, 0x48, 0x0c, 0xa0, 0xa0, 0xb2, 0x5f, 0x71, 0x31, 0xfe, + 0xa9, 0xc9, 0x95, 0x29, 0xa4, 0xf3, 0xcf, 0xa8, 0x9f, 0xdb, 0xad, 0xc3, 0x6e, 0xad, 0xe5, 0x45, + 0x51, 0x8f, 0x5d, 0xdb, 0x4e, 0xb8, 0x40, 0xc1, 0x66, 0x55, 0x51, 0x6d, 0xab, 0xd5, 0x2b, 0x35, + 0xed, 0x03, 0x68, 0x97, 0x23, 0xe3, 0xc3, 0x65, 0xc9, 0x94, 0x65, 0x88, 0xee, 0x04, 0x16, 0x17, + 0x33, 0xcf, 0xf8, 0xb4, 0x0f, 0x97, 0xc2, 0xd3, 0xb6, 0xd4, 0xf4, 0xef, 0xf5, 0xd4, 0xf4, 0x79, + 0xef, 0xbd, 0xdb, 0x3b, 0x29, 0x34, 0x89, 0xdd, 0x5b, 0xee, 0x07, 0x3f, 0x12, 0x75, 0x48, 0xfc, + 0xa3, 0x95, 0x09, 0x43, 0xee, 0xce, 0x7f, 0x0a, 0x15, 0x03, 0xf1, 0xe8, 0x59, 0x6e, 0x8a, 0x1b, + 0x25, 0xee, 0x4c, 0xfb, 0x0e, 0x0d, 0xe4, 0xa8, 0x01, 0xe7, 0xac, 0xad, 0xd2, 0xed, 0xc9, 0x7c, + 0x4e, 0x5b, 0xd2, 0x0f, 0x69, 0xd9, 0x77, 0x7e, 0x7d, 0x69, 0xf2, 0x1d, 0x4c, 0xe4, 0x24, 0xd2, + 0xe9, 0xfc, 0xcb, 0xf5, 0xd5, 0xa5, 0xc3, 0x13, 0x51, 0x05, 0x93, 0xa5, 0xf5, 0x54, 0xb2, 0x91, + 0xc9, 0x04, 0xc8, 0xcf, 0xd8, 0xd8, 0x6c, 0x52, 0xb0, 0x92, 0xd5, 0xd5, 0x2a, 0xd9, 0xd7, 0x6c, + 0xa2, 0x5b, 0xc7, 0xea, 0x2a, 0xd4, 0x1a, 0xd7, 0x6c, 0x52, 0xb5, 0x8a, 0xd5, 0x55, 0xde, 0x64, + 0x5d, 0xd3, 0xea, 0x6b, 0xd6, 0xb0, 0xed, 0x2d, 0x54, 0x4c, 0x6b, 0x36, 0xa9, 0xb3, 0x8b, 0x75, + 0xb6, 0x9a, 0xd3, 0x88, 0x6e, 0x03, 0xe3, 0xba, 0xb5, 0x6e, 0x4d, 0x7e, 0x27, 0xbe, 0xde, 0x4c, + 0x64, 0x92, 0xe2, 0xc6, 0x1c, 0x10, 0x63, 0x25, 0x3c, 0x83, 0x17, 0xe3, 0xe4, 0x78, 0x0b, 0xc5, + 0xf5, 0x8b, 0xe5, 0x35, 0xb2, 0x50, 0xda, 0xf6, 0xf4, 0x11, 0x14, 0x3b, 0xcf, 0x1e, 0x73, 0xa1, + 0xd5, 0x97, 0x80, 0x2e, 0xc7, 0x65, 0xe0, 0x63, 0x5a, 0xde, 0x94, 0xb2, 0xb1, 0x52, 0x1f, 0x78, + 0xe6, 0xbe, 0x9f, 0x5c, 0x0f, 0x69, 0x32, 0x77, 0x23, 0x1a, 0xa5, 0xe1, 0xd2, 0xa0, 0x68, 0x03, + 0x04, 0xca, 0xbd, 0x1f, 0x2d, 0xf6, 0xd5, 0xa9, 0xfe, 0xb7, 0x38, 0x4b, 0x22, 0x37, 0xec, 0x1b, + 0x0a, 0xa7, 0x2c, 0x73, 0xf3, 0xf0, 0x10, 0xcb, 0x2a, 0xde, 0x72, 0x42, 0x39, 0xc4, 0x21, 0x81, + 0x5b, 0xcc, 0xb4, 0xed, 0x9c, 0x52, 0x6f, 0x4c, 0x08, 0xa6, 0x9d, 0xac, 0xf7, 0xe7, 0x17, 0xe7, + 0x37, 0xe7, 0xfa, 0xe1, 0xfa, 0x9f, 0x8d, 0xcb, 0x61, 0x43, 0x28, 0x46, 0xcc, 0x0e, 0x27, 0x9d, + 0xe6, 0x7a, 0x5d, 0xc8, 0x71, 0x74, 0x61, 0x99, 0x57, 0x0a, 0x58, 0x53, 0xa6, 0x3f, 0x55, 0xb2, + 0xfe, 0x51, 0x2f, 0x51, 0xe5, 0xf9, 0xd7, 0x76, 0xb7, 0x64, 0x2f, 0x19, 0xc4, 0x32, 0x02, 0x60, + 0x01, 0x9e, 0x27, 0x96, 0x48, 0x25, 0xb4, 0xe1, 0xb0, 0x38, 0x69, 0x6e, 0x3c, 0xe6, 0x21, 0x12, + 0xa9, 0xfe, 0x00, 0x6c, 0xf3, 0x7f, 0xff, 0x3f, 0x05, 0xf8, 0x1a, 0x03, 0x98, 0xdd, 0x48, 0xf3, + 0x01, 0xa8, 0xb6, 0xcf, 0x66, 0x00, 0xfd, 0xce, 0xf7, 0x77, 0x0c, 0x5e, 0xd6, 0x68, 0xf5, 0x92, + 0x45, 0x8c, 0xdf, 0x3e, 0x5d, 0x5c, 0x00, 0x9b, 0x13, 0x44, 0x5e, 0x98, 0xf9, 0xd4, 0x07, 0x77, + 0x07, 0x15, 0xa8, 0xbd, 0x53, 0x2a, 0x2c, 0xee, 0xdb, 0xb9, 0x1b, 0x44, 0x86, 0x38, 0x3d, 0x8e, + 0xd9, 0x95, 0xbb, 0x61, 0x74, 0x0d, 0xdb, 0x3a, 0x2d, 0xd4, 0x48, 0x63, 0xb5, 0xee, 0xfd, 0x1f, + 0x7e, 0x78, 0xee, 0x2b, 0x7f, 0x38, 0xf3, 0xbc, 0x2c, 0x71, 0xbd, 0x65, 0xed, 0xb7, 0x42, 0xfd, + 0x78, 0x3d, 0x02, 0xc3, 0xc4, 0xa6, 0x7d, 0x21, 0xfb, 0xfd, 0xa7, 0x93, 0xdc, 0xd0, 0x4d, 0x83, + 0x34, 0xf3, 0x69, 0x27, 0x85, 0x60, 0x35, 0x53, 0xfe, 0x9b, 0x12, 0x57, 0x8c, 0xb7, 0xc3, 0x08, + 0x9f, 0xb4, 0x80, 0xd1, 0x4e, 0x84, 0x7a, 0xba, 0x0b, 0x2b, 0xe2, 0xde, 0x95, 0x38, 0x0f, 0x48, + 0x0c, 0x70, 0xc3, 0xd3, 0x08, 0x2f, 0xf5, 0x42, 0xca, 0x9f, 0x4e, 0xb9, 0xa0, 0x98, 0xdc, 0x70, + 0x09, 0x3a, 0xb0, 0xba, 0xa5, 0x7d, 0xa1, 0xb4, 0x92, 0x9f, 0x5a, 0x91, 0xc0, 0x2a, 0x89, 0xe7, + 0x8b, 0xd4, 0x32, 0xdf, 0x09, 0xbf, 0x0e, 0xb5, 0x4e, 0x41, 0xc1, 0x63, 0x8f, 0xa3, 0x8f, 0x02, + 0x26, 0xed, 0x01, 0xe8, 0xdc, 0x71, 0x0e, 0x0a, 0x69, 0x43, 0x4e, 0x84, 0x36, 0x0d, 0xe8, 0x4b, + 0x25, 0x92, 0x8b, 0xe6, 0x90, 0xed, 0x5b, 0x46, 0xa2, 0xbb, 0x1e, 0x95, 0x7e, 0x11, 0xc4, 0xe4, + 0x18, 0x33, 0x4e, 0xc5, 0xeb, 0xa1, 0xcc, 0xcc, 0x5e, 0xad, 0x2a, 0x0f, 0x23, 0x9d, 0x3b, 0xdf, + 0x34, 0x06, 0xcd, 0xea, 0x97, 0x93, 0x8d, 0x01, 0x44, 0x8c, 0x8a, 0x20, 0x2d, 0xbd, 0x3a, 0xc0, + 0x85, 0x69, 0xae, 0x39, 0xc0, 0x38, 0xac, 0xc8, 0xbd, 0x0f, 0xa6, 0x6e, 0x1a, 0x27, 0xce, 0x94, + 0xc6, 0x2a, 0x3a, 0x93, 0x64, 0xe4, 0x2c, 0xbb, 0x5b, 0x5b, 0x00, 0xa1, 0xf8, 0xdc, 0x5d, 0x71, + 0x18, 0xb3, 0x00, 0xa3, 0x5d, 0xd3, 0xde, 0xdb, 0x41, 0x6c, 0x51, 0xc7, 0x8b, 0x21, 0x71, 0x8d, + 0xa3, 0xc6, 0x9d, 0x3f, 0x91, 0x1f, 0x90, 0x3f, 0x92, 0x5b, 0xaa, 0x24, 0x9e, 0x6f, 0x1f, 0xb9, + 0x4d, 0x52, 0xf2, 0xc4, 0x77, 0xd6, 0xc7, 0x60, 0x3a, 0x93, 0xc7, 0xa8, 0xf3, 0xaa, 0x89, 0x56, + 0xce, 0x38, 0x4b, 0x3b, 0xad, 0x36, 0x3d, 0x20, 0x73, 0xf7, 0x31, 0x98, 0x67, 0xf3, 0xb3, 0x29, + 0xed, 0x34, 0xd7, 0xe5, 0x3b, 0x20, 0x4e, 0xdd, 0x7f, 0x96, 0x7c, 0x58, 0x6c, 0x93, 0xf3, 0x06, + 0xf0, 0x0f, 0xa4, 0xe4, 0xe6, 0x3b, 0x65, 0xb5, 0x7a, 0x45, 0x11, 0x75, 0xe6, 0xa4, 0xf1, 0x05, + 0x70, 0x11, 0x1c, 0x4d, 0xe9, 0xd0, 0xc8, 0x17, 0x6a, 0x67, 0x27, 0x5a, 0x64, 0xa6, 0xbe, 0xf8, + 0x2a, 0x82, 0xf7, 0x25, 0x8d, 0xa3, 0x61, 0x0c, 0x18, 0x30, 0x03, 0x0a, 0x21, 0x32, 0x0c, 0x12, + 0x93, 0xe5, 0x9b, 0x00, 0xba, 0x50, 0x64, 0xe9, 0x2b, 0x04, 0xb4, 0xe1, 0xb7, 0x0d, 0x38, 0xb9, + 0xba, 0x21, 0xf7, 0x76, 0xc1, 0x68, 0xd7, 0x5c, 0x9c, 0xe0, 0xa2, 0x86, 0x2e, 0x4d, 0x18, 0x71, + 0x14, 0x2e, 0x85, 0xb3, 0x10, 0xe6, 0xd4, 0x20, 0xc2, 0x2b, 0x6a, 0xe6, 0xb2, 0x19, 0xc1, 0xb6, + 0x30, 0x82, 0x03, 0x78, 0x01, 0x19, 0x48, 0x5e, 0x19, 0xfa, 0x18, 0x39, 0x23, 0x5b, 0x77, 0x5b, + 0x60, 0x3d, 0x70, 0x80, 0xfb, 0x10, 0x27, 0x73, 0x50, 0x58, 0xe7, 0x81, 0xcd, 0x4d, 0x35, 0x64, + 0x58, 0x79, 0x8c, 0xa2, 0x5f, 0x43, 0xc7, 0xb5, 0x05, 0xd9, 0x10, 0xba, 0xb9, 0x1b, 0x55, 0x84, + 0x1b, 0x6e, 0xf5, 0xae, 0xa6, 0x82, 0x54, 0x34, 0x32, 0x4e, 0xdd, 0x4a, 0x3e, 0x48, 0xa4, 0x79, + 0x6c, 0x5b, 0x3e, 0x46, 0x22, 0x6d, 0xd8, 0xb1, 0xe0, 0x7b, 0xb4, 0xed, 0x69, 0x3d, 0x81, 0x86, + 0x10, 0xe6, 0x3c, 0x76, 0xe2, 0x6f, 0xab, 0x15, 0x64, 0x67, 0x03, 0x37, 0x34, 0x74, 0x89, 0xcd, + 0x3c, 0x0f, 0x7c, 0x79, 0x2b, 0x69, 0x1c, 0x65, 0xa1, 0xd5, 0xca, 0xcc, 0x16, 0x61, 0xec, 0xfa, + 0x0d, 0xb1, 0xa9, 0xed, 0x2e, 0xc8, 0x5e, 0x6a, 0x72, 0xef, 0x61, 0xf5, 0x02, 0x94, 0xbd, 0x94, + 0xc8, 0x05, 0xc6, 0xb1, 0x58, 0xf9, 0x17, 0xba, 0x8b, 0x45, 0x59, 0x06, 0xb3, 0x62, 0xfb, 0xe9, + 0xc2, 0x1a, 0xfd, 0xca, 0x33, 0x44, 0x26, 0xf4, 0xdf, 0x50, 0x4f, 0xd6, 0x31, 0x5e, 0x3f, 0xc5, + 0x52, 0x10, 0x81, 0x44, 0xc6, 0x52, 0x86, 0x8f, 0x2a, 0xb2, 0x2c, 0xe7, 0xa5, 0xaa, 0xc7, 0x20, + 0xa1, 0x2c, 0xf8, 0x77, 0xaa, 0x14, 0xb5, 0x12, 0x80, 0x04, 0xf8, 0x16, 0x73, 0x67, 0xa7, 0x04, + 0x4b, 0x52, 0xf8, 0x0c, 0x88, 0xbd, 0x58, 0x03, 0x57, 0xb2, 0x54, 0xd0, 0x12, 0xfd, 0xad, 0x86, + 0xd6, 0xc0, 0x04, 0xa2, 0x9a, 0xa2, 0xb9, 0x3a, 0x9e, 0xfb, 0x80, 0x05, 0xe3, 0x00, 0x22, 0x03, + 0x49, 0x45, 0x3c, 0xb7, 0xfd, 0x96, 0x32, 0x84, 0x03, 0xba, 0x44, 0xa9, 0x0b, 0xd1, 0xb4, 0x94, + 0xdf, 0x0c, 0xdb, 0x12, 0x41, 0x42, 0xe2, 0xcb, 0xb2, 0xed, 0xef, 0x43, 0x9c, 0xd0, 0x29, 0x5e, + 0xcb, 0x1b, 0xad, 0x74, 0x9b, 0xe7, 0x6f, 0xe1, 0x4e, 0xe9, 0x0c, 0x14, 0x5b, 0x84, 0x2b, 0x5d, + 0x43, 0xea, 0xde, 0xd3, 0x21, 0x66, 0x81, 0x7e, 0xa6, 0x16, 0x43, 0x3d, 0x98, 0x20, 0x3c, 0xe0, + 0x01, 0xc6, 0xf0, 0x53, 0x20, 0x8d, 0x6a, 0x16, 0x01, 0x64, 0x76, 0x67, 0xc7, 0xaa, 0x31, 0xa7, + 0xfe, 0x59, 0x7d, 0xe7, 0xda, 0xee, 0x5e, 0xc4, 0xd6, 0x47, 0xbb, 0x0b, 0xb2, 0x6a, 0x97, 0xd1, + 0x14, 0x80, 0x12, 0xc9, 0xbd, 0x1b, 0x72, 0x53, 0x5c, 0x6d, 0x96, 0x48, 0xf2, 0xfc, 0xb4, 0xbf, + 0x2e, 0x5b, 0xf4, 0x48, 0x25, 0x97, 0x3b, 0x30, 0xa6, 0x6b, 0x72, 0x40, 0x0f, 0xed, 0x6e, 0xfd, + 0xe0, 0x9f, 0x0a, 0x46, 0xf0, 0x97, 0x59, 0xb7, 0xf3, 0xaf, 0x45, 0x9f, 0xdb, 0x62, 0x36, 0x59, + 0xed, 0x06, 0xb4, 0xbb, 0x6b, 0xdb, 0xb2, 0xbb, 0x6f, 0xf6, 0x99, 0x97, 0x04, 0x8b, 0xf4, 0xed, + 0x0f, 0x6f, 0xf6, 0x81, 0x36, 0xc0, 0xbf, 0xb3, 0x74, 0x1e, 0xbe, 0xfd, 0xe1, 0xff, 0x01, 0xd1, + 0xca, 0x01, 0xda, 0x6d, 0xcb, 0x02, 0x00, +}; +const size_t RCC6_WEB_UI_INDEX_GZ_LEN = sizeof(RCC6_WEB_UI_INDEX_GZ); +#endif diff --git a/examples/companion_radio/webui/THIRD_PARTY_NOTICES.md b/examples/companion_radio/webui/THIRD_PARTY_NOTICES.md new file mode 100644 index 00000000..4a98c13e --- /dev/null +++ b/examples/companion_radio/webui/THIRD_PARTY_NOTICES.md @@ -0,0 +1,13 @@ +# Third-party notices + +The compiled WebUI bundles [MeshCore.js](https://github.com/meshcore-dev/meshcore.js) 1.14.0. + +MIT License + +Copyright (c) 2025-2026 Liam Cottle + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/examples/companion_radio/webui/build.mjs b/examples/companion_radio/webui/build.mjs new file mode 100644 index 00000000..54c425a0 --- /dev/null +++ b/examples/companion_radio/webui/build.mjs @@ -0,0 +1,58 @@ +import { build, transform } from "esbuild"; +import { createHash } from "node:crypto"; +import { gzipSync, gunzipSync } from "node:zlib"; +import { mkdir, readFile, writeFile } from "node:fs/promises"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const root = path.dirname(fileURLToPath(import.meta.url)); +const source = path.join(root, "src"); +const output = path.join(root, ".build"); +const packageJson = JSON.parse(await readFile(path.join(root, "package.json"), "utf8")); + +const jsResult = await build({ + entryPoints: [path.join(source, "app.js")], + bundle: true, + write: false, + format: "iife", + platform: "browser", + target: ["safari15", "chrome100", "firefox100"], + minify: true, + charset: "utf8", + legalComments: "none", + define: { + __APP_VERSION__: JSON.stringify(packageJson.version), + __MESHCORE_JS_VERSION__: JSON.stringify(packageJson.dependencies["@liamcottle/meshcore.js"]), + }, +}); +const cssResult = await transform(await readFile(path.join(source, "styles.css"), "utf8"), { + loader: "css", + minify: true, +}); + +const template = await readFile(path.join(source, "index.html"), "utf8"); +const html = template + .replace("/*__RCC6_CSS__*/", cssResult.code.trim()) + .replace("/*__RCC6_JS__*/", jsResult.outputFiles[0].text.trim()); + +if (html.includes("/*__RCC6_")) throw new Error("Unreplaced WebUI build token"); +if (/]*src=|]*rel=[\"']stylesheet/i.test(html)) { + throw new Error("Runtime network asset reference detected"); +} + +const gzip = gzipSync(Buffer.from(html), { level: 9, mtime: 0 }); +gzip[9] = 0xff; // RFC 1952 OS=unknown keeps the asset identical across build hosts. +if (gunzipSync(gzip).toString("utf8") !== html) throw new Error("Gzip round-trip failed"); +const digest = createHash("sha256").update(gzip).digest("hex"); +const rows = []; +for (let offset = 0; offset < gzip.length; offset += 16) { + rows.push(` ${[...gzip.subarray(offset, offset + 16)].map((byte) => `0x${byte.toString(16).padStart(2, "0")}`).join(", ")},`); +} + +const header = `// Generated by examples/companion_radio/webui/build.mjs. Do not edit.\n// gzip sha256: ${digest}\n#pragma once\n#include \n\nextern const uint8_t RCC6_WEB_UI_INDEX_GZ[] PROGMEM;\nextern const size_t RCC6_WEB_UI_INDEX_GZ_LEN;\n\n#ifdef RCC6_WEB_UI_ASSETS_IMPLEMENTATION\nconst uint8_t RCC6_WEB_UI_INDEX_GZ[] PROGMEM = {\n${rows.join("\n")}\n};\nconst size_t RCC6_WEB_UI_INDEX_GZ_LEN = sizeof(RCC6_WEB_UI_INDEX_GZ);\n#endif\n`; + +await mkdir(output, { recursive: true }); +await writeFile(path.join(output, "index.html"), html); +await writeFile(path.join(output, "index.html.gz"), gzip); +await writeFile(path.join(root, "Rcc6WebUiAssets.h"), header); +console.log(`RCC6 WebUI: ${Buffer.byteLength(html)} bytes HTML, ${gzip.length} bytes gzip, sha256 ${digest}`); diff --git a/examples/companion_radio/webui/package-lock.json b/examples/companion_radio/webui/package-lock.json new file mode 100644 index 00000000..83171fad --- /dev/null +++ b/examples/companion_radio/webui/package-lock.json @@ -0,0 +1,815 @@ +{ + "name": "neonpocket-ultimate-webui", + "version": "2.0.0-rc.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "neonpocket-ultimate-webui", + "version": "2.0.0-rc.1", + "dependencies": { + "@liamcottle/meshcore.js": "1.14.0" + }, + "devDependencies": { + "esbuild": "0.27.2" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@liamcottle/meshcore.js": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@liamcottle/meshcore.js/-/meshcore.js-1.14.0.tgz", + "integrity": "sha512-WOJppqrFMXN8gx+by0M6OAD5x4smus49C+ro61735TsyEM8vLhnv6ZIDDB8Y/9m/B7QUrHgdVBHM+chz+wH2Yw==", + "license": "MIT", + "dependencies": { + "@noble/curves": "^1.9.7", + "serialport": "^13.0.0" + } + }, + "node_modules/@noble/curves": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@serialport/binding-mock": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@serialport/binding-mock/-/binding-mock-10.2.2.tgz", + "integrity": "sha512-HAFzGhk9OuFMpuor7aT5G1ChPgn5qSsklTFOTUX72Rl6p0xwcSVsRtG/xaGp6bxpN7fI9D/S8THLBWbBgS6ldw==", + "license": "MIT", + "dependencies": { + "@serialport/bindings-interface": "^1.2.1", + "debug": "^4.3.3" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@serialport/bindings-cpp": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-13.0.0.tgz", + "integrity": "sha512-r25o4Bk/vaO1LyUfY/ulR6hCg/aWiN6Wo2ljVlb4Pj5bqWGcSRC4Vse4a9AcapuAu/FeBzHCbKMvRQeCuKjzIQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@serialport/bindings-interface": "1.2.2", + "@serialport/parser-readline": "12.0.0", + "debug": "4.4.0", + "node-addon-api": "8.3.0", + "node-gyp-build": "4.8.4" + }, + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-delimiter": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-12.0.0.tgz", + "integrity": "sha512-gu26tVt5lQoybhorLTPsH2j2LnX3AOP2x/34+DUSTNaUTzu2fBXw+isVjQJpUBFWu6aeQRZw5bJol5X9Gxjblw==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/bindings-cpp/node_modules/@serialport/parser-readline": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-12.0.0.tgz", + "integrity": "sha512-O7cywCWC8PiOMvo/gglEBfAkLjp/SENEML46BXDykfKP5mTPM46XMaX1L0waWU6DXJpBgjaL7+yX6VriVPbN4w==", + "license": "MIT", + "dependencies": { + "@serialport/parser-delimiter": "12.0.0" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/bindings-interface": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@serialport/bindings-interface/-/bindings-interface-1.2.2.tgz", + "integrity": "sha512-CJaUd5bLvtM9c5dmO9rPBHPXTa9R2UwpkJ0wdh9JCYcbrPWsKz+ErvR0hBLeo7NPeiFdjFO4sonRljiw4d2XiA==", + "license": "MIT", + "engines": { + "node": "^12.22 || ^14.13 || >=16" + } + }, + "node_modules/@serialport/parser-byte-length": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-byte-length/-/parser-byte-length-13.0.0.tgz", + "integrity": "sha512-32yvqeTAqJzAEtX5zCrN1Mej56GJ5h/cVFsCDPbF9S1ZSC9FWjOqNAgtByseHfFTSTs/4ZBQZZcZBpolt8sUng==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-cctalk": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-cctalk/-/parser-cctalk-13.0.0.tgz", + "integrity": "sha512-RErAe57g9gvnlieVYGIn1xymb1bzNXb2QtUQd14FpmbQQYlcrmuRnJwKa1BgTCujoCkhtaTtgHlbBWOxm8U2uA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-delimiter": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-13.0.0.tgz", + "integrity": "sha512-Qqyb0FX1avs3XabQqNaZSivyVbl/yl0jywImp7ePvfZKLwx7jBZjvL+Hawt9wIG6tfq6zbFM24vzCCK7REMUig==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-inter-byte-timeout": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-13.0.0.tgz", + "integrity": "sha512-a0w0WecTW7bD2YHWrpTz1uyiWA2fDNym0kjmPeNSwZ2XCP+JbirZt31l43m2ey6qXItTYVuQBthm75sPVeHnGA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-packet-length": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-packet-length/-/parser-packet-length-13.0.0.tgz", + "integrity": "sha512-60ZDDIqYRi0Xs2SPZUo4Jr5LLIjtb+rvzPKMJCohrO6tAqSDponcNpcB1O4W21mKTxYjqInSz+eMrtk0LLfZIg==", + "license": "MIT", + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@serialport/parser-readline": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-13.0.0.tgz", + "integrity": "sha512-dov3zYoyf0dt1Sudd1q42VVYQ4WlliF0MYvAMA3MOyiU1IeG4hl0J6buBA2w4gl3DOCC05tGgLDN/3yIL81gsA==", + "license": "MIT", + "dependencies": { + "@serialport/parser-delimiter": "13.0.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-ready": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-ready/-/parser-ready-13.0.0.tgz", + "integrity": "sha512-JNUQA+y2Rfs4bU+cGYNqOPnNMAcayhhW+XJZihSLQXOHcZsFnOa2F9YtMg9VXRWIcnHldHYtisp62Etjlw24bw==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-regex": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-regex/-/parser-regex-13.0.0.tgz", + "integrity": "sha512-m7HpIf56G5XcuDdA3DB34Z0pJiwxNRakThEHjSa4mG05OnWYv0IG8l2oUyYfuGMowQWaVnQ+8r+brlPxGVH+eA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-slip-encoder": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-slip-encoder/-/parser-slip-encoder-13.0.0.tgz", + "integrity": "sha512-fUHZEExm6izJ7rg0A1yjXwu4sOzeBkPAjDZPfb+XQoqgtKAk+s+HfICiYn7N2QU9gyaeCO8VKgWwi+b/DowYOg==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/parser-spacepacket": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/parser-spacepacket/-/parser-spacepacket-13.0.0.tgz", + "integrity": "sha512-DoXJ3mFYmyD8X/8931agJvrBPxqTaYDsPoly9/cwQSeh/q4EjQND9ySXBxpWz5WcpyCU4jOuusqCSAPsbB30Eg==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/@serialport/stream": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-13.0.0.tgz", + "integrity": "sha512-F7xLJKsjGo2WuEWMSEO1SimRcOA+WtWICsY13r0ahx8s2SecPQH06338g28OT7cW7uRXI7oEQAk62qh5gHJW3g==", + "license": "MIT", + "dependencies": { + "@serialport/bindings-interface": "1.2.2", + "debug": "4.4.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/esbuild": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/node-addon-api": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.3.0.tgz", + "integrity": "sha512-8VOpLHFrOQlAH+qA0ZzuGRlALRA6/LVh8QJldbrC4DY0hXoMP0l4Acq8TzFC018HztWiRqyCEj2aTWY2UvnJUg==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/serialport": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/serialport/-/serialport-13.0.0.tgz", + "integrity": "sha512-PHpnTd8isMGPfFTZNCzOZp9m4mAJSNWle9Jxu6BPTcWq7YXl5qN7tp8Sgn0h+WIGcD6JFz5QDgixC2s4VW7vzg==", + "license": "MIT", + "dependencies": { + "@serialport/binding-mock": "10.2.2", + "@serialport/bindings-cpp": "13.0.0", + "@serialport/parser-byte-length": "13.0.0", + "@serialport/parser-cctalk": "13.0.0", + "@serialport/parser-delimiter": "13.0.0", + "@serialport/parser-inter-byte-timeout": "13.0.0", + "@serialport/parser-packet-length": "13.0.0", + "@serialport/parser-readline": "13.0.0", + "@serialport/parser-ready": "13.0.0", + "@serialport/parser-regex": "13.0.0", + "@serialport/parser-slip-encoder": "13.0.0", + "@serialport/parser-spacepacket": "13.0.0", + "@serialport/stream": "13.0.0", + "debug": "4.4.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/serialport/donate" + } + } + } +} diff --git a/examples/companion_radio/webui/package.json b/examples/companion_radio/webui/package.json new file mode 100644 index 00000000..5aaae511 --- /dev/null +++ b/examples/companion_radio/webui/package.json @@ -0,0 +1,16 @@ +{ + "name": "neonpocket-ultimate-webui", + "version": "2.0.0-rc.1", + "private": true, + "type": "module", + "scripts": { + "build": "node build.mjs", + "test": "node build.mjs && node verify.mjs" + }, + "dependencies": { + "@liamcottle/meshcore.js": "1.14.0" + }, + "devDependencies": { + "esbuild": "0.27.2" + } +} diff --git a/examples/companion_radio/webui/src/app.js b/examples/companion_radio/webui/src/app.js new file mode 100644 index 00000000..b2e2ea57 --- /dev/null +++ b/examples/companion_radio/webui/src/app.js @@ -0,0 +1,1426 @@ +import Connection from "@liamcottle/meshcore.js/src/connection/connection.js"; +import Constants from "@liamcottle/meshcore.js/src/constants.js"; + +const MAX_FRAME_BYTES = 176; +const MAX_MESSAGE_BYTES = 140; +const MAX_HISTORY_MESSAGES = 120; +const POLL_DELAY_MS = 90; +const COMMAND_TIMEOUT_MS = 30000; +const MAX_RECOVERY_ATTEMPTS = 2; +const RADIO_LEASE_KEY = "rcc6-radio-owner"; +const RADIO_LEASE_MS = 7000; +const RADIO_HEARTBEAT_MS = 2000; +const $ = (id) => document.getElementById(id); +const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); +const encoder = new TextEncoder(); + +function createSessionId() { + const bytes = new Uint8Array(16); + crypto.getRandomValues(bytes); + return [...bytes].map((value) => value.toString(16).padStart(2, "0")).join(""); +} + +const TAB_OWNER_ID = createSessionId(); + +function loadSessionId() { + try { + const saved = localStorage.getItem("rcc6-session"); + if (/^[0-9a-f]{32}$/.test(saved || "")) return saved; + const created = createSessionId(); + localStorage.setItem("rcc6-session", created); + return created; + } catch { + return createSessionId(); + } +} + +class HttpPollingConnection extends Connection { + constructor() { + super(); + this.running = false; + this.failures = 0; + this.interrupted = false; + this.sessionId = loadSessionId(); + this.lastDispatchedSequence = 0; + this.pendingAck = 0; + this.ready = false; + this.connecting = false; + this.drainTask = null; + this.leaseTimer = null; + this.storageListening = false; + this.onStorage = (event) => { + if (event.key === RADIO_LEASE_KEY && this.running && !this.ownsLease()) this.handleLeaseLoss(); + }; + } + + async connect() { + if (this.running || this.connecting) return; + this.connecting = true; + try { + if (!this.storageListening) { + window.addEventListener("storage", this.onStorage); + this.storageListening = true; + } + if (!await this.claimLease()) { + this.emit("transport-busy"); + return; + } + this.running = true; + this.ready = false; + this.abortController = new AbortController(); + this.drainTask = this.drainRetainedFrames(); + const drained = await this.drainTask; + if (!drained || !this.running || !this.ownsLease()) return; + this.ready = true; + this.pollTask = this.poll(); + this.emit("connected"); + } finally { + this.connecting = false; + } + } + + async close() { + const wasReady = this.ready; + this.running = false; + this.ready = false; + this.abortController?.abort(); + try { await this.drainTask; } catch { /* aborted */ } + try { await this.pollTask; } catch { /* aborted */ } + if (wasReady) this.onDisconnected(); + this.releaseLease(); + if (this.storageListening) window.removeEventListener("storage", this.onStorage); + this.storageListening = false; + } + + readLease() { + try { + const lease = JSON.parse(localStorage.getItem(RADIO_LEASE_KEY) || "null"); + return lease && typeof lease.owner === "string" && Number.isFinite(lease.expires) ? lease : null; + } catch { return null; } + } + + ownsLease() { + const lease = this.readLease(); + return lease?.owner === TAB_OWNER_ID && lease.expires > Date.now(); + } + + writeLease() { + try { + localStorage.setItem(RADIO_LEASE_KEY, JSON.stringify({ owner: TAB_OWNER_ID, expires: Date.now() + RADIO_LEASE_MS })); + return this.ownsLease(); + } catch { return false; } + } + + async claimLease() { + const current = this.readLease(); + if (current && current.owner !== TAB_OWNER_ID && current.expires > Date.now()) return false; + if (!this.writeLease()) return false; + await delay(80); + if (!this.ownsLease()) return false; + this.stopHeartbeat(); + this.leaseTimer = setInterval(() => { + if (!this.ownsLease() || !this.writeLease()) this.handleLeaseLoss(); + }, RADIO_HEARTBEAT_MS); + return true; + } + + stopHeartbeat() { + clearInterval(this.leaseTimer); + this.leaseTimer = null; + } + + handleLeaseLoss() { + this.stopHeartbeat(); + if (!this.running) return; + this.running = false; + this.ready = false; + this.abortController?.abort(); + this.emit("transport-busy"); + } + + releaseLease() { + this.stopHeartbeat(); + try { + if (this.readLease()?.owner === TAB_OWNER_ID) localStorage.removeItem(RADIO_LEASE_KEY); + } catch { /* storage unavailable */ } + } + + leavePage() { + this.running = false; + this.ready = false; + this.abortController?.abort(); + this.releaseLease(); + } + + async sendToRadioFrame(data) { + const frame = data instanceof Uint8Array ? data : new Uint8Array(data); + if (!frame.length || frame.length > MAX_FRAME_BYTES) throw new Error("Invalid companion frame size"); + if (!this.ready) throw new Error("Companion link is still draining"); + if (!this.ownsLease()) { + this.handleLeaseLoss(); + throw new Error("Another tab owns the companion link"); + } + try { + const response = await fetch("/api/frame", { + method: "POST", + headers: { + "Content-Type": "application/octet-stream", + "X-RCC6-Session": this.sessionId, + }, + body: frame, + cache: "no-store", + credentials: "same-origin", + signal: this.abortController.signal, + }); + if (!response.ok) throw new Error(`Frame POST failed (${response.status})`); + this.markHealthy(); + } catch (error) { + if (error.name !== "AbortError") { + error.rcc6TransportFailure = true; + this.markFailure(error); + } + throw error; + } + } + + async fetchFrame() { + const headers = { Accept: "application/octet-stream", "X-RCC6-Session": this.sessionId }; + if (this.pendingAck) headers["X-RCC6-Ack"] = String(this.pendingAck); + const response = await fetch("/api/frame", { headers, cache: "no-store", credentials: "same-origin", signal: this.abortController.signal }); + if (response.status === 204) { + this.pendingAck = 0; + this.markHealthy(); + return false; + } + if (!response.ok) throw new Error(`Frame poll failed (${response.status})`); + const sequence = Number(response.headers.get("X-RCC6-Seq")); + if (!Number.isInteger(sequence) || sequence < 1 || sequence > 0xffffffff) throw new Error("Invalid frame sequence"); + const frame = new Uint8Array(await response.arrayBuffer()); + if (!frame.length || frame.length > MAX_FRAME_BYTES) throw new Error("Invalid radio frame"); + if (sequence !== this.lastDispatchedSequence) { + this.onFrameReceived(frame); + this.lastDispatchedSequence = sequence; + // MeshCore.js defers `once` handlers twice; let message persistence finish before ACK. + await delay(0); + await delay(0); + } + this.pendingAck = sequence; + this.markHealthy(); + return true; + } + + async drainRetainedFrames() { + while (this.running) { + if (!this.ownsLease()) { this.handleLeaseLoss(); return false; } + try { + if (!await this.fetchFrame()) return true; + } catch (error) { + if (!this.running || error.name === "AbortError") return false; + this.markFailure(error); + await delay(Math.min(250 * 2 ** Math.min(this.failures, 3), 2000)); + } + } + return false; + } + + async poll() { + while (this.running) { + if (!this.ownsLease()) { this.handleLeaseLoss(); break; } + try { + if (!await this.fetchFrame()) await delay(POLL_DELAY_MS); + } catch (error) { + if (!this.running || error.name === "AbortError") break; + this.markFailure(error); + await delay(Math.min(250 * 2 ** Math.min(this.failures, 3), 2000)); + } + } + } + + markFailure(error) { + this.failures += 1; + if (this.failures >= 3 && !this.interrupted) { + this.interrupted = true; + this.emit("transport-state", "reconnecting", error); + } + } + + markHealthy() { + const restored = this.interrupted; + this.failures = 0; + this.interrupted = false; + if (restored && this.ready) this.emit("transport-restored"); + } +} + +const state = { + connected: false, + linkStatus: "reconnecting", + startedAt: Date.now(), + view: "home", + self: null, + device: null, + battery: 0, + contacts: [], + channels: [], + messages: [], + nearby: [], + selected: "", + radio: null, + packets: null, + rfSamples: [], + network: null, + ultimate: null, + ultimateHistory: [], + ultimateSettings: null, + historyNode: "", + mapMode: "places", +}; + +let connection = new HttpPollingConnection(); +let commandTail = Promise.resolve(); +let syncing = false; +let recovering = false; +let recoveryAttempts = 0; +let unannouncedMessages = 0; + +function exclusive(task) { + const result = commandTail.then(() => { + let timer; + const timeout = new Promise((_, reject) => { + timer = setTimeout(() => { + const error = new Error("Companion command timed out"); + error.rcc6CommandTimeout = true; + reject(error); + }, COMMAND_TIMEOUT_MS); + }); + return Promise.race([Promise.resolve().then(task), timeout]).finally(() => clearTimeout(timer)); + }); + commandTail = result.catch(() => {}); + result.catch((error) => { + if (error?.rcc6CommandTimeout || error?.rcc6TransportFailure) scheduleConnectionRecovery(); + }); + return result; +} + +function scheduleConnectionRecovery() { + if (recovering || recoveryAttempts >= MAX_RECOVERY_ATTEMPTS) { + if (recoveryAttempts >= MAX_RECOVERY_ATTEMPTS) toast("Radio sync is paused. Tap the link status to retry.", "error"); + return; + } + recovering = true; + recoveryAttempts += 1; + setLink("reconnecting"); + toast(`Recovering companion link (${recoveryAttempts}/${MAX_RECOVERY_ATTEMPTS})`, "warn"); + setTimeout(async () => { + const previous = connection; + try { + await previous.close(); + syncing = false; + $("refresh-button").disabled = false; + connection = new HttpPollingConnection(); + bindConnectionEvents(connection); + await connection.connect(); + } catch { + setLink("offline"); + toast("Companion recovery failed", "error"); + } finally { + recovering = false; + } + }, 0); +} + +function text(id, value) { $(id).textContent = value ?? "—"; } +function hex(bytes, length = bytes?.length ?? 0) { return bytes ? [...bytes.slice(0, length)].map((value) => value.toString(16).padStart(2, "0")).join("") : ""; } +function hashText(value) { + let hash = 2166136261; + for (let index = 0; index < value.length; index += 1) hash = Math.imul(hash ^ value.charCodeAt(index), 16777619); + return (hash >>> 0).toString(16); +} +function historyKey() { return state.self?.publicKey ? `rcc6-history-${hex(state.self.publicKey)}` : ""; } +function pendingHistoryKey() { return `rcc6-history-pending-${connection.sessionId}`; } +function messageId(kind, key, timestamp, value) { return `${kind}:${key}:${timestamp}:${hashText(value)}`; } +function persistHistory() { + const key = historyKey() || pendingHistoryKey(); + try { localStorage.setItem(key, JSON.stringify(state.messages.slice(-MAX_HISTORY_MESSAGES))); } catch { /* storage is optional */ } +} +function readHistory(key) { + try { + const saved = JSON.parse(localStorage.getItem(key) || "[]"); + if (!Array.isArray(saved)) return []; + return saved.filter((item) => item && typeof item.id === "string" && typeof item.key === "string" && typeof item.text === "string") + .slice(-MAX_HISTORY_MESSAGES) + .map((item) => ({ + id: item.id.slice(0, 180), key: item.key.slice(0, 100), text: item.text.slice(0, MAX_MESSAGE_BYTES * 2), + timestamp: Number.isFinite(item.timestamp) ? item.timestamp : 0, + direction: item.direction === "out" ? "out" : "in", + status: typeof item.status === "string" ? item.status.slice(0, 16) : "received", + unread: Boolean(item.unread), + snr: Number.isFinite(item.snr) ? item.snr : undefined, + expectedAckCrc: Number.isInteger(item.expectedAckCrc) ? item.expectedAckCrc : undefined, + })); + } catch { return []; } +} +function loadHistoryForNode() { + const key = historyKey(); + if (!key || state.historyNode === key) return; + state.historyNode = key; + const merged = [...readHistory(key), ...readHistory(pendingHistoryKey()), ...state.messages]; + const seen = new Set(); + state.messages = merged.filter((item) => !seen.has(item.id) && seen.add(item.id)).slice(-MAX_HISTORY_MESSAGES); + try { localStorage.removeItem(pendingHistoryKey()); } catch { /* storage is optional */ } + persistHistory(); +} +function channelKey(channel) { return `ch:${channel.channelIdx}`; } +function contactKey(contact) { return `dm:${hex(contact.publicKey)}`; } +function initials(name = "?") { return name.trim().split(/\s+/).slice(0, 2).map((part) => part[0]).join("").toUpperCase() || "?"; } +function formatNumber(value) { return Number.isFinite(value) ? value.toLocaleString() : "—"; } +function formatClock(epochSecs) { return epochSecs ? new Date(epochSecs * 1000).toLocaleTimeString([], { hour: "numeric", minute: "2-digit" }) : "now"; } +function formatAge(epochSecs) { + if (!epochSecs) return "just now"; + const seconds = Math.max(0, Math.floor(Date.now() / 1000) - epochSecs); + if (seconds < 60) return `${seconds}s ago`; + if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`; + if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`; + return `${Math.floor(seconds / 86400)}d ago`; +} +function formatSession() { + const minutes = Math.floor((Date.now() - state.startedAt) / 60000); + return minutes < 1 ? "Now" : minutes < 60 ? `${minutes}m` : `${Math.floor(minutes / 60)}h ${minutes % 60}m`; +} +function radioFrequency(value) { return Number.isFinite(value) ? (value / 1e6).toFixed(3) : "—"; } +function radioBandwidth(value) { return Number.isFinite(value) ? (value / 1000).toFixed(value % 1000 ? 1 : 0) : "—"; } +function signalQuality(rssi) { return rssi >= -80 ? "Excellent" : rssi >= -95 ? "Good" : rssi >= -110 ? "Fair" : "Weak"; } + +function advertisedLocation(node) { + let lat = Number(node?.advLat); + let lon = Number(node?.advLon); + if (!Number.isFinite(lat) || !Number.isFinite(lon)) return null; + if (Math.abs(lat) > 90 || Math.abs(lon) > 180) { lat /= 1000000; lon /= 1000000; } + if ((lat === 0 && lon === 0) || Math.abs(lat) > 90 || Math.abs(lon) > 180) return null; + return { lat, lon }; +} + +function canvasContext(canvas, minimumHeight = 120) { + if (!canvas) return null; + const width = Math.max(280, canvas.clientWidth || 0); + const height = Math.max(minimumHeight, canvas.clientHeight || 0); + const ratio = Math.min(devicePixelRatio || 1, 2); + canvas.width = Math.round(width * ratio); + canvas.height = Math.round(height * ratio); + const context = canvas.getContext("2d"); + context.scale(ratio, ratio); + context.clearRect(0, 0, width, height); + return { context, width, height }; +} + +function drawActivityBars(canvas, rows, limit = 24) { + const surface = canvasContext(canvas, 110); + if (!surface) return 0; + const { context, width, height } = surface; + const ordered = [...(rows || [])].reverse().slice(-limit); + context.strokeStyle = "rgba(141,152,167,.11)"; + context.lineWidth = 1; + for (let row = 1; row < 4; row += 1) { + const y = Math.round(row * (height - 24) / 4) + .5; + context.beginPath(); context.moveTo(0, y); context.lineTo(width, y); context.stroke(); + } + if (!ordered.length) return 0; + const values = ordered.map((row) => ({ rx: Number(row[1]) || 0, tx: Number(row[2]) || 0, fail: Number(row[3]) || 0 })); + const maximum = Math.max(1, ...values.flatMap((row) => [row.rx, row.tx, row.fail])); + const group = width / values.length; + const barWidth = Math.max(1.5, Math.min(8, group * .22)); + values.forEach((row, index) => { + const x = index * group + (group - barWidth * 3) / 2; + [[row.rx, "#44f08a"], [row.tx, "#42a5ff"], [row.fail, "#ff5468"]].forEach(([value, color], series) => { + const barHeight = Math.max(value ? 2 : 0, value / maximum * (height - 26)); + context.fillStyle = color; + context.globalAlpha = .84; + context.fillRect(x + series * barWidth, height - 12 - barHeight, Math.max(1, barWidth - 1), barHeight); + }); + }); + context.globalAlpha = 1; + return values.reduce((total, row) => total + row.rx + row.tx, 0); +} + +function element(tag, className, content) { + const node = document.createElement(tag); + if (className) node.className = className; + if (content != null) node.textContent = content; + return node; +} + +function toast(message, kind = "ok") { + const item = element("div", `toast ${kind}`); + item.append(element("i"), element("span", "", message)); + $("toasts").append(item); + setTimeout(() => item.classList.add("out"), 3200); + setTimeout(() => item.remove(), 3500); +} + +function setLink(status) { + const online = status === "connected"; + state.connected = online; + state.linkStatus = status; + const label = online ? "Connected" : status === "reconnecting" ? "Reconnecting" : status === "busy" ? "Another tab active" : "Offline"; + text("link-label", label); + text("side-status", label); + text("hero-link", label); + $("connect-button").className = `link-chip ${status}`; + $("side-dot").className = `status-dot ${status}`; +} + +function targetInfo(key) { + if (key.startsWith("ch:")) { + const channel = state.channels.find((item) => channelKey(item) === key); + return channel ? { key, name: `#${channel.name || `Channel ${channel.channelIdx}`}`, subtitle: "Mesh channel", avatar: "#", channel } : null; + } + const contact = state.contacts.find((item) => contactKey(item) === key); + return contact ? { key, name: contact.advName || "Unnamed contact", subtitle: `Direct · ${hex(contact.publicKey, 4)}`, avatar: initials(contact.advName), contact } : null; +} + +function targetForContactPrefix(prefix) { + const contact = state.contacts.find((item) => prefix.every((byte, index) => item.publicKey[index] === byte)); + return contact ? contactKey(contact) : `dm-prefix:${hex(prefix)}`; +} + +function normalizeTargets() { + if (state.selected && targetInfo(state.selected)) return; + state.selected = state.channels[0] ? channelKey(state.channels[0]) : state.contacts[0] ? contactKey(state.contacts[0]) : ""; +} + +function addMessage(message) { + const item = { id: `${Date.now()}-${Math.random()}`, ...message }; + if (state.messages.some((existing) => existing.id === item.id)) return null; + state.messages.push(item); + if (state.messages.length > MAX_HISTORY_MESSAGES) state.messages.splice(0, state.messages.length - MAX_HISTORY_MESSAGES); + persistHistory(); + renderMessages(); + renderHome(); + return item; +} + +function ingestWaiting(item) { + if (item.contactMessage) { + const message = item.contactMessage; + const key = targetForContactPrefix(message.pubKeyPrefix); + return addMessage({ id: messageId("dm", hex(message.pubKeyPrefix), message.senderTimestamp, message.text), key, text: message.text, timestamp: message.senderTimestamp, direction: "in", status: "received", unread: document.hidden || state.view !== "messages" || state.selected !== key, snr: message.snr }) ? 1 : 0; + } + if (item.channelMessage) { + const message = item.channelMessage; + const key = `ch:${message.channelIdx}`; + return addMessage({ id: messageId("channel", key, message.senderTimestamp, message.text), key, text: message.text, timestamp: message.senderTimestamp, direction: "in", status: "received", unread: document.hidden || state.view !== "messages" || state.selected !== key, snr: message.snr }) ? 1 : 0; + } + return 0; +} + +function refreshNearbyFromContacts() { + state.nearby = [...state.contacts].filter((item) => item.lastAdvert).sort((a, b) => b.lastAdvert - a.lastAdvert).slice(0, 24); +} + +async function refreshContacts() { + const contacts = await connection.getContacts(); + state.contacts = contacts.sort((a, b) => (b.lastAdvert || 0) - (a.lastAdvert || 0)); + let historyChanged = false; + for (const message of state.messages) { + if (!message.key.startsWith("dm-prefix:")) continue; + const prefix = message.key.slice("dm-prefix:".length); + const contact = state.contacts.find((item) => hex(item.publicKey).startsWith(prefix)); + if (contact) { message.key = contactKey(contact); historyChanged = true; } + } + if (historyChanged) persistHistory(); + refreshNearbyFromContacts(); + normalizeTargets(); + renderAll(); +} + +async function syncMessages() { + let added = 0; + while (true) { + const waiting = await connection.syncNextMessage(); + if (!waiting) break; + added += ingestWaiting(waiting); + } + added += unannouncedMessages; + unannouncedMessages = 0; + if (added) toast(`${added} new message${added === 1 ? "" : "s"}`, "warn"); +} + +async function refreshStats() { + if (!state.connected) return; + try { + const battery = await connection.getBatteryVoltage(); + state.battery = battery.batteryMilliVolts; + } catch { /* optional */ } + try { + const radio = await connection.getStatsRadio(); + state.radio = radio.data; + if (Number.isFinite(state.radio.lastRssi)) state.rfSamples.push(state.radio.lastRssi); + state.rfSamples = state.rfSamples.slice(-36); + } catch { /* optional */ } + try { state.packets = (await connection.getStatsPackets()).data; } catch { /* optional */ } + renderAll(); +} + +async function refreshNetworkStatus() { + try { + const response = await fetch("/api/network", { + headers: { Accept: "application/json", "X-RCC6-Session": connection.sessionId }, + cache: "no-store", + credentials: "same-origin", + }); + if (!response.ok) throw new Error(`Network status failed (${response.status})`); + const data = await response.json(); + if (data.mode !== "ap" && data.mode !== "station") throw new Error("Invalid network mode"); + state.network = { + mode: data.mode, + ssid: typeof data.ssid === "string" ? data.ssid : "", + ip: typeof data.ip === "string" ? data.ip : "", + fallback: Boolean(data.fallback), + }; + } catch { /* network configuration endpoint is optional on older builds */ } + renderNetwork(); +} + +async function ultimateFetch(path, options = {}) { + const response = await fetch(path, { + cache: "no-store", + credentials: "same-origin", + ...options, + headers: { Accept: "application/json", ...(options.headers || {}) }, + }); + if (!response.ok) throw new Error(`Ultimate API failed (${response.status})`); + return response; +} + +async function refreshUltimate() { + try { + const [status, history, settings] = await Promise.all([ + ultimateFetch("/api/ultimate/status").then((response) => response.json()), + ultimateFetch("/api/ultimate/history?limit=20").then((response) => response.json()), + ultimateFetch("/api/ultimate/settings").then((response) => response.json()), + ]); + state.ultimate = status; + state.ultimateHistory = Array.isArray(history.records) ? history.records : []; + state.ultimateSettings = settings; + } catch { /* non-Ultimate firmware remains compatible with the standard WebUI */ } + renderUltimate(); +} + +async function setNetwork(fields) { + if (!connection.ready || !connection.ownsLease()) throw new Error("RCC6 companion link is not ready"); + const response = await fetch("/api/network", { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + "X-RCC6-Session": connection.sessionId, + }, + body: new URLSearchParams(fields), + cache: "no-store", + credentials: "same-origin", + }); + if (!response.ok) throw new Error(`Network update failed (${response.status})`); +} + +async function syncAll() { + if (syncing) return; + syncing = true; + $("refresh-button").disabled = true; + try { + await refreshNetworkStatus(); + try { state.device = await connection.deviceQuery(Constants.SupportedCompanionProtocolVersion); } catch { /* older firmware */ } + state.self = await connection.getSelfInfo(6000); + loadHistoryForNode(); + await refreshContacts(); + try { state.channels = (await connection.getChannels()).filter((item) => item.name); } catch { state.channels = []; } + normalizeTargets(); + await syncMessages(); + await refreshStats(); + await refreshUltimate(); + renderAll(); + } finally { + syncing = false; + $("refresh-button").disabled = false; + } +} + +function renderHome() { + const self = state.self; + text("node-name", self?.name || "RADIO"); + text("node-id", self?.publicKey ? `ID ${hex(self.publicKey, 8)}…` : "Waiting for companion identity…"); + text("hero-contacts", state.contacts.length || "—"); + text("hero-uptime", formatSession()); + const unread = state.messages.filter((item) => item.unread); + text("home-unread", unread.length); + text("nav-unread", unread.length); + $("nav-unread").hidden = !unread.length; + const latest = unread.at(-1); + const preview = $("message-preview"); + preview.replaceChildren(); + if (latest) { + const target = targetInfo(latest.key); + preview.className = "preview"; + preview.append(element("strong", "", target?.name || "New message"), element("span", "", latest.text)); + } else { + preview.className = "preview empty"; + preview.textContent = "No unread messages"; + } + const nearby = state.nearby[0]; + const nearbyPreview = $("nearby-preview"); + nearbyPreview.replaceChildren(); + if (nearby) { + nearbyPreview.className = "preview"; + nearbyPreview.append(element("strong", "", nearby.advName || "Unnamed node"), element("span", "", `${formatAge(nearby.lastAdvert)} · ${hex(nearby.publicKey, 4)}`)); + } else { + nearbyPreview.className = "preview empty"; + nearbyPreview.textContent = "Waiting for adverts"; + } + const rssi = state.radio?.lastRssi; + text("home-rssi", Number.isFinite(rssi) ? `${rssi} dBm` : "— dBm"); + text("home-snr", Number.isFinite(state.radio?.lastSnr) ? `SNR ${state.radio.lastSnr.toFixed(1)}` : "SNR —"); + text("home-frequency", self?.radioFreq ? `${radioFrequency(self.radioFreq)} MHz` : "— MHz"); + text("home-packets", state.packets ? `${formatNumber(state.packets.recv)} packets` : "— packets"); + $("radio-meter-fill").style.width = Number.isFinite(rssi) ? `${Math.max(4, Math.min(100, (rssi + 125) * 2))}%` : "0"; + renderDashboardAnalytics(); +} + +function renderDashboardAnalytics() { + const ultimate = state.ultimate; + const received = ultimate?.rx ?? state.packets?.recv; + const sent = ultimate?.tx ?? state.packets?.sent; + text("dashboard-rx", formatNumber(received)); + text("dashboard-tx", formatNumber(sent)); + const delivery = ultimate?.delivery || {}; + text("dashboard-delivery", String(delivery.state || "idle").toUpperCase()); + text("dashboard-delivery-note", delivery.target || "No local send"); + text("dashboard-history", formatNumber(ultimate?.historyCount)); + text("dashboard-history-note", ultimate ? `of ${formatNumber(ultimate.historyCapacity || 0)} records` : "stored messages"); + const total = drawActivityBars($("dashboard-activity-chart"), ultimate?.hours, 24); + text("dashboard-activity-total", total ? `${formatNumber(total)} packets shown` : "No samples yet"); + drawSignalHistogram($("dashboard-signal-chart"), state.rfSamples); + renderNearbyBars(); +} + +function drawSignalHistogram(canvas, samples = []) { + const surface = canvasContext(canvas, 138); + if (!surface) return; + const { context, width, height } = surface; + const values = samples.filter(Number.isFinite).slice(-60); + text("dashboard-signal-note", values.length ? `${values.length} recent sample${values.length === 1 ? "" : "s"}` : "Waiting for packets"); + context.clearRect(0, 0, width, height); + context.strokeStyle = "rgba(141,152,167,.14)"; + [0.25, 0.5, 0.75].forEach((part) => { context.beginPath(); context.moveTo(0, height * part); context.lineTo(width, height * part); context.stroke(); }); + const edges = [-130, -115, -105, -95, -85, -70]; + const counts = edges.slice(0, -1).map((low, index) => values.filter((value) => value >= low && value < edges[index + 1]).length); + const max = Math.max(1, ...counts); const gap = 8; const barWidth = (width - gap * (counts.length - 1)) / counts.length; + counts.forEach((count, index) => { + const barHeight = Math.max(count ? 5 : 1, count / max * (height - 28)); + const gradient = context.createLinearGradient(0, height - barHeight, 0, height); + gradient.addColorStop(0, index >= 3 ? "#44f08a" : index >= 2 ? "#39e7ff" : "#ff8a3d"); gradient.addColorStop(1, "rgba(36,87,255,.28)"); + context.fillStyle = gradient; context.fillRect(index * (barWidth + gap), height - barHeight - 16, barWidth, barHeight); + context.fillStyle = "rgba(245,248,251,.78)"; context.font = "9px system-ui, sans-serif"; context.textAlign = "center"; + context.fillText(String(count), index * (barWidth + gap) + barWidth / 2, height - 3); + }); + context.textAlign = "left"; +} + +function routeSummary(contact) { + const raw = Number(contact?.outPathLen); const packed = Number.isFinite(raw) ? raw & 0xff : 0xff; + const hops = packed === 0xff ? null : packed & 0x3f; + return { hops, label: hops === null ? "route unknown" : hops === 0 ? "direct" : `${hops} hop${hops === 1 ? "" : "s"}` }; +} + +function renderNearbyBars() { + const host = $("dashboard-nearby-bars"); host.replaceChildren(); + const now = Math.floor(Date.now() / 1000); const nodes = state.nearby.slice(0, 6); + if (!nodes.length) { host.append(element("p", "empty-state", "Waiting for node adverts.")); return; } + nodes.forEach((contact) => { + const age = Math.max(0, now - Number(contact.lastAdvert || now)); + const freshness = Math.max(5, 100 - Math.min(100, age / 216)); + const row = element("div", "nearby-bar"); const track = element("span", "bar-track"); const fill = document.createElement("i"); + fill.style.width = `${freshness}%`; track.append(fill); + row.append(element("strong", "", contact.advName || "Unnamed node"), track, element("span", "", `${routeSummary(contact).label} · ${formatAge(contact.lastAdvert)}`)); + host.append(row); + }); +} + +function renderMessages() { + normalizeTargets(); + const targets = [ + ...state.channels.map((channel) => ({ key: channelKey(channel), name: `#${channel.name}`, subtitle: "Channel", avatar: "#" })), + ...state.contacts.map((contact) => ({ key: contactKey(contact), name: contact.advName || "Unnamed contact", subtitle: "Direct", avatar: initials(contact.advName) })), + ]; + text("thread-count", targets.length); + const threadList = $("thread-list"); + threadList.replaceChildren(); + if (!targets.length) threadList.append(element("p", "empty-state", "Contacts and channels appear after sync.")); + for (const target of targets) { + const messages = state.messages.filter((item) => item.key === target.key); + const latest = messages.at(-1); + const unread = messages.filter((item) => item.unread).length; + const button = element("button", `thread${state.selected === target.key ? " active" : ""}`); + button.type = "button"; + const avatar = element("span", "avatar", target.avatar); + const copy = element("span", "thread-copy"); + copy.append(element("strong", "", target.name), element("small", "", latest?.text || target.subtitle)); + const meta = unread ? element("span", "thread-badge", unread) : element("time", "thread-time", latest ? formatClock(latest.timestamp) : ""); + button.append(avatar, copy, meta); + button.addEventListener("click", () => selectTarget(target.key)); + threadList.append(button); + } + + const select = $("target-select"); + select.replaceChildren(new Option("Choose recipient", "")); + if (state.channels.length) { + const group = document.createElement("optgroup"); group.label = "Channels"; + for (const channel of state.channels) group.append(new Option(`#${channel.name}`, channelKey(channel))); + select.append(group); + } + if (state.contacts.length) { + const group = document.createElement("optgroup"); group.label = "Direct contacts"; + for (const contact of state.contacts) group.append(new Option(contact.advName || "Unnamed contact", contactKey(contact))); + select.append(group); + } + select.value = state.selected; + const info = targetInfo(state.selected); + text("conversation-title", info?.name || "Choose a conversation"); + text("conversation-subtitle", info?.subtitle || "Channel or direct contact"); + text("conversation-avatar", info?.avatar || "#"); + const list = $("message-list"); + list.replaceChildren(); + const messages = state.messages.filter((item) => item.key === state.selected); + if (!messages.length) list.append(element("div", "welcome-bubble", info ? `No messages with ${info.name} yet.` : "Select a channel or contact to start talking across the mesh.")); + for (const message of messages) { + const bubble = element("article", `bubble ${message.direction === "out" ? "out" : "in"} ${message.status === "failed" ? "failed" : ""}`); + const status = message.direction === "out" ? ` · ${message.status}` : Number.isFinite(message.snr) ? ` · SNR ${message.snr.toFixed(1)}` : ""; + bubble.append(element("p", "", message.text), element("small", "", `${formatClock(message.timestamp)}${status}`)); + list.append(bubble); + } + requestAnimationFrame(() => { list.scrollTop = list.scrollHeight; }); + $("message-input").disabled = !info || !state.connected; + $("composer").querySelector("button").disabled = !info || !state.connected; +} + +function selectTarget(key) { + state.selected = key; + for (const message of state.messages) if (message.key === key) message.unread = false; + persistHistory(); + renderMessages(); + renderHome(); +} + +function renderNearby() { + const list = $("nearby-list"); + list.replaceChildren(); + requestAnimationFrame(drawMeshMap); + if (!state.nearby.length) { + list.append(element("article", "card empty-state", "Nearby nodes appear as adverts arrive.")); + return; + } + for (const contact of state.nearby) { + const card = element("article", "card nearby-card"); + const avatar = element("span", "avatar", initials(contact.advName)); + const body = element("div"); + const route = routeSummary(contact).label; + const location = advertisedLocation(contact); + body.append(element("p", "eyebrow", contact.type === 2 ? "REPEATER" : contact.type === 3 ? "ROOM" : "CONTACT"), element("h3", "", contact.advName || "Unnamed node"), element("p", "", `ID ${hex(contact.publicKey, 5)} · ${route}${location ? ` · ${location.lat.toFixed(3)}, ${location.lon.toFixed(3)}` : ""}`)); + const time = element("time", "", formatAge(contact.lastAdvert)); + const action = element("button", "contact-action", "Message →"); + action.type = "button"; + action.addEventListener("click", () => { state.selected = contactKey(contact); showView("messages"); }); + card.append(avatar, body, time, action); + list.append(card); + } +} + +function drawHeardView(context, width, height) { + const nodes = state.nearby.slice(0, 64); const now = Math.floor(Date.now() / 1000); + const direct = nodes.filter((node) => routeSummary(node).hops === 0).length; + text("map-located", `${nodes.length} heard`); text("map-unlocated", `${direct} direct · ${nodes.length - direct} routed/unknown`); + $("map-empty").hidden = Boolean(nodes.length); + const gradient = context.createRadialGradient(width / 2, height / 2, 12, width / 2, height / 2, Math.max(width, height) * .62); + gradient.addColorStop(0, "#10202a"); gradient.addColorStop(1, "#070b10"); context.fillStyle = gradient; context.fillRect(0, 0, width, height); + const cx = width / 2; const cy = height / 2; const maxRadius = Math.max(62, Math.min(width, height) * .42); + const rings = [maxRadius * .28, maxRadius * .56, maxRadius * .82]; + context.strokeStyle = "rgba(57,231,255,.14)"; context.lineWidth = 1; + rings.forEach((radius, index) => { context.beginPath(); context.arc(cx, cy, radius, 0, Math.PI * 2); context.stroke(); context.fillStyle = "rgba(141,152,167,.62)"; context.font = "9px system-ui, sans-serif"; context.fillText(index === 0 ? "DIRECT" : index === 1 ? "1 HOP" : "2+ / UNKNOWN", cx + 8, cy - radius + 13); }); + nodes.forEach((contact, index) => { + const route = routeSummary(contact); const ring = route.hops === 0 ? 0 : route.hops === 1 ? 1 : 2; + const seed = [...(contact.publicKey || [])].slice(0, 6).reduce((sum, value, offset) => sum + value * (offset + 3), 0) || index * 73; + const angle = (seed % 360) * Math.PI / 180; const radius = rings[ring] + ((seed % 17) - 8) * .8; + const x = cx + Math.cos(angle) * radius; const y = cy + Math.sin(angle) * radius; + const age = Math.max(0, now - Number(contact.lastAdvert || now)); const alpha = Math.max(.3, 1 - Math.min(1, age / 21600)); + const color = contact.type === 2 ? "#ff8a3d" : contact.type === 3 ? "#ffd447" : "#42a5ff"; + context.globalAlpha = alpha; context.fillStyle = color; context.shadowColor = color; context.shadowBlur = 10; context.beginPath(); context.arc(x, y, 4, 0, Math.PI * 2); context.fill(); context.shadowBlur = 0; + if (index < 18) { context.fillStyle = "#f5f8fb"; context.font = "10px system-ui, sans-serif"; context.fillText((contact.advName || "Unnamed").slice(0, 18), x + 8, y - 6); } + context.globalAlpha = 1; + }); + context.fillStyle = "#44f08a"; context.shadowColor = "#44f08a"; context.shadowBlur = 15; context.fillRect(cx - 5, cy - 5, 10, 10); context.shadowBlur = 0; + context.fillStyle = "#f5f8fb"; context.font = "700 10px system-ui, sans-serif"; context.fillText("THIS RADIO", cx + 11, cy + 4); + context.fillStyle = "rgba(141,152,167,.7)"; context.font = "9px system-ui, sans-serif"; context.fillText("Ring = stored route · brightness = advert freshness", 14, height - 12); +} + +function drawMeshMap() { + const surface = canvasContext($("mesh-map"), 360); + if (!surface) return; + const { context, width, height } = surface; + if (state.mapMode === "heard") { drawHeardView(context, width, height); return; } + const selfLocation = advertisedLocation(state.self); + const located = state.nearby.map((contact) => ({ contact, location: advertisedLocation(contact) })).filter((entry) => entry.location); + text("map-located", `${located.length} located`); + text("map-unlocated", `${Math.max(0, state.nearby.length - located.length)} without location`); + $("map-empty").hidden = Boolean(located.length || selfLocation); + + const gradient = context.createLinearGradient(0, 0, 0, height); + gradient.addColorStop(0, "#0d1820"); gradient.addColorStop(1, "#070b10"); + context.fillStyle = gradient; context.fillRect(0, 0, width, height); + context.strokeStyle = "rgba(57,231,255,.09)"; context.lineWidth = 1; + for (let x = 0; x <= width; x += Math.max(34, width / 12)) { context.beginPath(); context.moveTo(x, 0); context.lineTo(x, height); context.stroke(); } + for (let y = 0; y <= height; y += Math.max(34, height / 8)) { context.beginPath(); context.moveTo(0, y); context.lineTo(width, y); context.stroke(); } + context.fillStyle = "rgba(141,152,167,.55)"; context.font = "10px ui-monospace, monospace"; + context.fillText("N", 17, 23); context.beginPath(); context.moveTo(20, 30); context.lineTo(20, 52); context.strokeStyle = "#44f08a"; context.stroke(); + + const points = [...located.map((entry) => entry.location), ...(selfLocation ? [selfLocation] : [])]; + if (!points.length) return; + let minLat = Math.min(...points.map((point) => point.lat)); let maxLat = Math.max(...points.map((point) => point.lat)); + let minLon = Math.min(...points.map((point) => point.lon)); let maxLon = Math.max(...points.map((point) => point.lon)); + const latPad = Math.max(.002, (maxLat - minLat) * .16); const lonPad = Math.max(.002, (maxLon - minLon) * .16); + minLat -= latPad; maxLat += latPad; minLon -= lonPad; maxLon += lonPad; + const project = ({ lat, lon }) => ({ + x: 36 + (lon - minLon) / (maxLon - minLon) * (width - 72), + y: 30 + (maxLat - lat) / (maxLat - minLat) * (height - 60), + }); + const selfPoint = selfLocation ? project(selfLocation) : null; + if (selfPoint) { + context.setLineDash([4, 7]); context.strokeStyle = "rgba(57,231,255,.18)"; + located.forEach(({ location }) => { const point = project(location); context.beginPath(); context.moveTo(selfPoint.x, selfPoint.y); context.lineTo(point.x, point.y); context.stroke(); }); + context.setLineDash([]); + } + located.slice(0, 40).forEach(({ contact, location }, index) => { + const point = project(location); const color = contact.type === 2 ? "#ff8a3d" : contact.type === 3 ? "#ffd447" : "#42a5ff"; + context.beginPath(); context.arc(point.x, point.y, 10, 0, Math.PI * 2); context.fillStyle = `${color}22`; context.fill(); + context.beginPath(); context.arc(point.x, point.y, 4, 0, Math.PI * 2); context.fillStyle = color; context.shadowColor = color; context.shadowBlur = 12; context.fill(); context.shadowBlur = 0; + if (index < 16) { context.fillStyle = "#f5f8fb"; context.font = "11px system-ui, sans-serif"; context.fillText((contact.advName || "Unnamed").slice(0, 20), point.x + 9, point.y - 7); } + }); + if (selfPoint) { + context.fillStyle = "#44f08a"; context.fillRect(selfPoint.x - 5, selfPoint.y - 5, 10, 10); + context.shadowColor = "#44f08a"; context.shadowBlur = 16; context.strokeStyle = "#dfffea"; context.strokeRect(selfPoint.x - 7, selfPoint.y - 7, 14, 14); context.shadowBlur = 0; + context.fillStyle = "#f5f8fb"; context.font = "700 11px system-ui, sans-serif"; context.fillText("THIS DEVICE", selfPoint.x + 11, selfPoint.y + 4); + } + context.fillStyle = "rgba(141,152,167,.7)"; context.font = "9px ui-monospace, monospace"; + context.fillText(`${minLat.toFixed(3)}° to ${maxLat.toFixed(3)}° N`, 14, height - 12); + context.textAlign = "right"; context.fillText(`${minLon.toFixed(3)}° to ${maxLon.toFixed(3)}° E`, width - 14, height - 12); context.textAlign = "left"; +} + +function renderRadio() { + const self = state.self; + const radio = state.radio; + const packets = state.packets; + text("radio-rssi", Number.isFinite(radio?.lastRssi) ? radio.lastRssi : "—"); + text("radio-quality", Number.isFinite(radio?.lastRssi) ? signalQuality(radio.lastRssi) : "Waiting for sample"); + text("radio-snr", Number.isFinite(radio?.lastSnr) ? `SNR ${radio.lastSnr.toFixed(1)} dB` : "SNR —"); + text("radio-noise", Number.isFinite(radio?.noiseFloor) ? `Noise ${radio.noiseFloor} dBm` : "Noise —"); + text("radio-frequency", radioFrequency(self?.radioFreq)); + text("radio-bandwidth", radioBandwidth(self?.radioBw)); + text("radio-spreading", self?.radioSf ? `SF${self.radioSf}` : "—"); + text("radio-coding", self?.radioCr ? `CR 4/${self.radioCr}` : "CR —"); + text("radio-power", Number.isFinite(self?.txPower) ? self.txPower : "—"); + text("packet-received", formatNumber(packets?.recv)); + text("packet-sent", formatNumber(packets?.sent)); + text("packet-direct", formatNumber(packets?.nRecvDirect)); + text("packet-flood", formatNumber(packets?.nRecvFlood)); + const errors = packets?.nRecvErrors ?? 0; + text("packet-errors", `${formatNumber(errors)} error${errors === 1 ? "" : "s"}`); + $("packet-errors").className = errors ? "error" : "quiet"; + if (state.view === "radio") requestAnimationFrame(() => { drawSignalChart(); drawRadioActivity(); }); +} + +function drawRadioActivity() { + const total = drawActivityBars($("radio-activity-chart"), state.ultimate?.hours, 72); + text("radio-activity-total", total ? `${formatNumber(total)} packets shown` : "No samples yet"); +} + +function drawSignalChart() { + const canvas = $("signal-chart"); + const width = canvas.clientWidth; + const height = canvas.clientHeight; + if (width < 20 || height < 20) return; + const ratio = Math.min(devicePixelRatio || 1, 2); + canvas.width = Math.round(width * ratio); canvas.height = Math.round(height * ratio); + const context = canvas.getContext("2d"); context.scale(ratio, ratio); + context.clearRect(0, 0, width, height); + context.strokeStyle = "rgba(141,152,167,.12)"; context.lineWidth = 1; + for (let row = 1; row < 4; row += 1) { context.beginPath(); context.moveTo(0, row * height / 4); context.lineTo(width, row * height / 4); context.stroke(); } + const samples = state.rfSamples.length > 1 ? state.rfSamples : [-120, -120]; + context.beginPath(); + samples.forEach((sample, index) => { + const x = index * width / Math.max(1, samples.length - 1); + const y = height - Math.max(0, Math.min(1, (sample + 130) / 60)) * height; + index ? context.lineTo(x, y) : context.moveTo(x, y); + }); + context.strokeStyle = "#44f08a"; context.lineWidth = 2; context.shadowColor = "#44f08a"; context.shadowBlur = 8; context.stroke(); +} + +function renderNetwork() { + const network = state.network; + const busy = state.linkStatus === "busy"; + if (!network) { + text("network-title", "Set up local Wi-Fi"); + text("network-summary", "Use your 2.4 GHz home network instead of staying on the setup hotspot."); + text("network-current", "Network status unavailable"); + text("more-network-title", "Radio network"); + text("ap-address", "Current mode and IP are unavailable"); + $("network-setup-button").textContent = "Set up local Wi-Fi"; + } else if (network.mode === "station") { + text("network-title", `Connected to ${network.ssid || "local Wi-Fi"}`); + text("network-summary", `${network.ip || "DHCP address pending"}${network.fallback ? " · fallback active" : " · ready on your local network"}`); + text("network-current", `${network.ssid || "Local Wi-Fi"} · ${network.ip || "DHCP pending"}`); + text("more-network-title", "Local Wi-Fi"); + text("ap-address", `${network.ssid || "Station mode"} · ${network.ip || "DHCP pending"}`); + $("network-setup-button").textContent = "Change local Wi-Fi"; + } else { + text("network-title", network.fallback ? "Local Wi-Fi needs attention" : "Set up local Wi-Fi"); + text("network-summary", `${network.ssid || "setup AP"} · ${network.ip || "192.168.4.1"}${network.fallback ? " · station fallback" : ""}`); + text("network-current", `${network.ssid || "setup AP"} · ${network.ip || "192.168.4.1"}`); + text("more-network-title", network.fallback ? "Fallback setup AP" : "Setup AP"); + text("ap-address", `${network.ssid || "Setup hotspot"} · ${network.ip || "192.168.4.1"}`); + $("network-setup-button").textContent = "Set up local Wi-Fi"; + } + $("network-setup-button").disabled = busy; + $("return-ap-button").hidden = network?.mode !== "station"; + $("return-ap-button").disabled = busy; +} + +function openNetworkDialog() { + renderNetwork(); + $("network-form").hidden = false; + $("network-result").hidden = true; + $("network-ssid").value = state.network?.mode === "station" ? state.network.ssid : ""; + $("network-password").value = ""; + $("network-password").type = "password"; + $("network-reveal").textContent = "Show"; + $("network-reveal").setAttribute("aria-pressed", "false"); + const dialog = $("network-dialog"); + if (dialog.showModal) dialog.showModal(); else dialog.setAttribute("open", ""); + $("network-ssid").focus(); +} + +function closeNetworkDialog() { + const dialog = $("network-dialog"); + if (dialog.close) dialog.close(); else dialog.removeAttribute("open"); +} + +function showNetworkResult(mode) { + $("network-form").hidden = true; + $("network-result").hidden = false; + const steps = $("network-result-steps"); + steps.replaceChildren(); + const values = mode === "station" + ? ["Wait for the radio to restart.", "Reconnect this phone or computer to your home Wi-Fi.", "Read the new DHCP IP on the screen and open it in your browser."] + : ["Wait for the radio to restart in setup AP mode.", "Reconnect this phone or computer to the Wi-Fi shown on the screen.", "Open 192.168.4.1 in your browser."]; + for (const value of values) steps.append(element("li", "", value)); + text("network-result-title", mode === "station" ? "Reconnect to your home Wi-Fi" : "Reconnect to the setup AP"); + const note = $("network-result-note"); + note.replaceChildren(); + if (mode === "station") { + note.append("In station mode, the browser may request HTTP Basic authentication. Use username ", element("strong", "", "meshcore"), " and the 8-letter key shown on the TFT as the password."); + } else { + note.textContent = "The TFT shows the setup Wi-Fi name and password after restart."; + } +} + +function renderMore() { + const self = state.self; + const device = state.device; + const model = String(device?.manufacturerModel || "").split("\0", 1)[0] + .replace(/[^\x20-\x7e]/g, "").replace(/_/g, " ").trim(); + text("more-name", self?.name || "RADIO"); + text("more-model", model || "MeshCore Companion"); + text("more-firmware", device?.firmwareVer != null ? `Protocol ${device.firmwareVer}` : "—"); + text("more-build", device?.firmware_build_date || "—"); + text("more-key", self?.publicKey ? `${hex(self.publicKey, 8)}…` : "—"); + text("app-version", __APP_VERSION__); + text("library-version", __MESHCORE_JS_VERSION__); + $("advert-button").disabled = !state.connected; + $("clear-history-button").disabled = !state.messages.length; +} + +function drawUltimateChart(canvas, rows, keys, colors) { + if (!canvas) return; + const ratio = devicePixelRatio || 1; + const width = Math.max(canvas.clientWidth, 280); + const height = Math.max(canvas.clientHeight, 160); + canvas.width = width * ratio; canvas.height = height * ratio; + const ctx = canvas.getContext("2d"); + ctx.scale(ratio, ratio); ctx.clearRect(0, 0, width, height); + ctx.strokeStyle = "rgba(255,255,255,.055)"; ctx.lineWidth = 1; + for (let y = 18; y < height; y += 35) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(width, y); ctx.stroke(); } + const ordered = [...(rows || [])].reverse(); + if (ordered.length < 2) return; + const trafficKeys = keys.filter((key) => key !== "battery"); + const trafficMaximum = Math.max(1, + ...ordered.flatMap((row) => trafficKeys.map((key) => Number(row[key]) || 0))); + keys.forEach((key, series) => { + ctx.strokeStyle = colors[series]; ctx.lineWidth = series ? 1.6 : 2.4; + ctx.shadowColor = colors[series]; ctx.shadowBlur = series ? 4 : 9; ctx.beginPath(); + let drawing = false; + ordered.forEach((row, index) => { + const x = index * width / (ordered.length - 1); + const raw = Number(row[key]); + if (!Number.isFinite(raw) || (key === "battery" && raw <= 0)) { + drawing = false; + return; + } + const scaled = key === "battery" ? Math.max(0, Math.min(1, (raw - 3000) / 1200)) + : raw / trafficMaximum; + const y = height - 10 - scaled * (height - 26); + drawing ? ctx.lineTo(x, y) : ctx.moveTo(x, y); + drawing = true; + }); + ctx.stroke(); ctx.shadowBlur = 0; + }); +} + +function renderUltimate() { + const ultimate = state.ultimate; + if (!ultimate) return; + text("ultimate-gate", ultimate.memoryGate ? "PASS" : "PENDING"); + $("ultimate-gate-dot").className = ultimate.memoryGate ? "pass" : ""; + text("ultimate-history-count", formatNumber(ultimate.historyCount || 0)); + text("ultimate-history-capacity", `${formatNumber(ultimate.historyCapacity || 0)} record capacity`); + text("ultimate-free-heap", `${Math.round((ultimate.freeHeap || 0) / 1024)} KB`); + text("ultimate-largest", `largest ${Math.round((ultimate.largestAllocation || 0) / 1024)} KB`); + text("ultimate-flush", `${((ultimate.displayFlushUs || 0) / 1000).toFixed(1)} ms`); + text("ultimate-tiles", `${ultimate.displayTiles || 0} / 176 tiles`); + text("ultimate-drops", ultimate.eventDrops || 0); + text("ultimate-queue", `outbound ${ultimate.queueDepth || 0} · air ${Math.round((ultimate.airtimeMs || 0) / 60000)}m`); + const profileNames = ["BALANCED", "FIELD", "BATTERY"]; + const trend = Number(ultimate.batteryTrendMvPerHour || 0); + text("ultimate-battery", ultimate.batteryMv > 0 + ? `${(ultimate.batteryMv / 1000).toFixed(2)} V · ${trend > 0 ? "+" : ""}${trend} mV/h` + : "—"); + text("ultimate-runtime", ultimate.usbHostConnected + ? "USB host connected · unplug to start a clean discharge window" + : ultimate.batteryRuntimeMinutes > 0 + ? `about ${Math.floor(ultimate.batteryRuntimeMinutes / 60)}h ${ultimate.batteryRuntimeMinutes % 60}m to 3.45 V` + : `${profileNames[ultimate.powerProfile] || "BALANCED"} · ${ultimate.animationFrameMs || 66} ms frames`); + const delivery = ultimate.delivery || {}; + text("ultimate-delivery", String(delivery.state || "idle").toUpperCase()); + text("ultimate-delivery-target", delivery.target + ? `${delivery.target}${delivery.roundTripMs ? ` · ${delivery.roundTripMs} ms` : ""}` + : "No on-device send"); + + const minuteRows = (ultimate.minutes || []) + .map((row) => ({ rx: row[1], tx: row[2], battery: row[7] })); + const hourRows = (ultimate.hours || []) + .map((row) => ({ rx: row[1], tx: row[2], failures: row[3] })); + drawUltimateChart($("ultimate-minute-chart"), minuteRows, + ["rx", "tx", "battery"], ["#44f08a", "#39e7ff", "#ffb84d"]); + drawUltimateChart($("ultimate-week-chart"), hourRows, ["rx", "tx", "failures"], ["#44f08a", "#39e7ff", "#ff5468"]); + + const nodes = ultimate.nodes || []; + text("ultimate-node-count", `${nodes.length} radio${nodes.length === 1 ? "" : "s"}`); + const nodeList = $("ultimate-node-list"); nodeList.replaceChildren(); + if (!nodes.length) nodeList.append(element("p", "empty-state", "Listening on the configured preset.")); + nodes.slice(0, 16).forEach((node) => { + const item = element("div", "ultimate-node"); + item.append(element("strong", "", node.name || "Unnamed radio"), + element("b", "", Number.isFinite(node.rssi) ? `${node.rssi} dBm` : `${node.path} hops`), + element("span", "", `Role ${node.role} · ${formatAge(node.seen)}`), + element("span", "", `${node.packets} packet${node.packets === 1 ? "" : "s"}`)); + nodeList.append(item); + }); + + const historyList = $("ultimate-history-list"); historyList.replaceChildren(); + if (!state.ultimateHistory.length) historyList.append(element("p", "empty-state", "No stored messages.")); + state.ultimateHistory.forEach((record) => { + const item = element("div", "ultimate-record"); + item.append(element("strong", "", record.sender || (record.kind === 2 ? "#channel" : "Direct")), + element("span", "", `${record.incoming ? "Received" : "Sent"} · ${formatAge(record.timestamp)}`), + element("p", "", record.text || "")); + historyList.append(item); + }); + + const settings = state.ultimateSettings; + if (settings) { + $("ultimate-history-cap").value = String(settings.historyCapacity); + $("ultimate-cadence").value = String(settings.scanCadenceMs); + $("ultimate-power-profile").value = String(settings.powerProfile || 0); + $("ultimate-battery-capacity").value = String(settings.batteryCapacityMah || 0); + $("ultimate-battery-calibration").value = String(settings.batteryCalibrationMv || 0); + $("ultimate-private").checked = Boolean(settings.privateNotifications); + const phrases = $("ultimate-phrases"); + if (!phrases.children.length) (settings.quickPhrases || []).forEach((phrase, index) => { + const label = element("label", "", `Quick phrase ${index + 1}`); + const input = element("input"); input.type = "text"; input.maxLength = 47; + input.dataset.phrase = index; input.value = phrase; label.append(input); phrases.append(label); + }); + } + renderDashboardAnalytics(); + if (state.view === "radio") requestAnimationFrame(drawRadioActivity); +} + +function renderAll() { + if (state.battery) { + const volts = `${(state.battery / 1000).toFixed(2)} V`; + text("battery-chip", volts); + } else { + text("battery-chip", "—"); + } + renderHome(); renderMessages(); renderNearby(); renderRadio(); renderNetwork(); renderUltimate(); renderMore(); +} + +function showView(view) { + state.view = view; + document.querySelectorAll(".screen").forEach((screen) => screen.classList.toggle("active", screen.dataset.screen === view)); + document.querySelectorAll(".nav button").forEach((button) => button.classList.toggle("active", button.dataset.view === view)); + const labels = { + home: ["Overview", "NEONPOCKETMC"], messages: ["Messages", "CHANNELS & DIRECT"], + nearby: ["Nearby", "NODES & LOCATIONS"], radio: ["Radio", "LIVE RF"], ultimate: ["This radio", "STORAGE, POWER & DISPLAY"], + }; + text("screen-title", labels[view]?.[0] || "Ultimate"); + text("screen-kicker", labels[view]?.[1] || "NEONPOCKETMC"); + if (view === "messages" && state.selected) selectTarget(state.selected); + if (view === "radio") renderRadio(); + if (view === "nearby") requestAnimationFrame(drawMeshMap); + if (view === "ultimate") refreshUltimate(); + window.scrollTo({ top: 0, behavior: "smooth" }); +} + +async function sendMessage(event) { + event.preventDefault(); + const input = $("message-input"); + const messageText = input.value.trim(); + const info = targetInfo(state.selected); + if (!messageText || !info) return; + if (encoder.encode(messageText).length > MAX_MESSAGE_BYTES) return toast("Message is over 140 bytes", "error"); + const outgoing = addMessage({ key: state.selected, text: messageText, timestamp: Math.floor(Date.now() / 1000), direction: "out", status: "sending", unread: false }); + input.value = ""; updateComposeCount(); + try { + const response = await exclusive(() => info.channel ? connection.sendChannelTextMessage(info.channel.channelIdx, messageText) : connection.sendTextMessage(info.contact.publicKey, messageText)); + outgoing.status = "queued"; + if (!info.channel && Number.isInteger(response?.expectedAckCrc)) outgoing.expectedAckCrc = response.expectedAckCrc; + persistHistory(); + toast("Message queued"); + } catch { + outgoing.status = "failed"; + persistHistory(); + toast("Message failed", "error"); + } + renderMessages(); +} + +function updateComposeCount() { + const count = encoder.encode($("message-input").value).length; + text("compose-count", `${count} / ${MAX_MESSAGE_BYTES} bytes`); + $("compose-count").className = count > MAX_MESSAGE_BYTES ? "error" : ""; +} + +function bindConnectionEvents(radio) { + radio.on(Constants.ResponseCodes.ContactMsgRecv, (message) => { unannouncedMessages += ingestWaiting({ contactMessage: message }); }); + radio.on(Constants.ResponseCodes.ChannelMsgRecv, (message) => { unannouncedMessages += ingestWaiting({ channelMessage: message }); }); + radio.on("connected", () => { + if (!radio.ready || !radio.ownsLease()) return; + setLink("connected"); + toast("Radio connected"); + setTimeout(() => $("boot").classList.add("hidden"), 220); + exclusive(syncAll).then(() => { recoveryAttempts = 0; }).catch(() => toast("Initial sync failed", "error")); + }); + radio.on("disconnected", () => setLink("offline")); + radio.on("transport-busy", () => { + setLink("busy"); + $("boot").classList.add("hidden"); + toast("Another tab controls this radio. Close it, then tap the link status to retry.", "warn"); + }); + radio.on("transport-state", () => { setLink("reconnecting"); toast("Link interrupted — reconnecting", "warn"); }); + radio.on("transport-restored", () => { + setLink("connected"); + toast("Link restored — resyncing"); + exclusive(syncAll).then(() => { recoveryAttempts = 0; }).catch(() => toast("Resync failed", "error")); + }); + radio.on(Constants.PushCodes.MsgWaiting, () => { if (radio.ready) exclusive(syncMessages).catch(() => toast("Message sync failed", "error")); }); + radio.on(Constants.PushCodes.NewAdvert, (advert) => { + const existing = state.contacts.findIndex((item) => hex(item.publicKey) === hex(advert.publicKey)); + if (existing >= 0) state.contacts[existing] = advert; else state.contacts.unshift(advert); + refreshNearbyFromContacts(); renderAll(); toast(`${advert.advName || "A node"} is nearby`, "warn"); + }); + radio.on(Constants.PushCodes.Advert, () => { if (radio.ready) exclusive(refreshContacts).catch(() => {}); }); + radio.on(Constants.PushCodes.SendConfirmed, (confirmation) => { + const pending = [...state.messages].reverse().find((item) => item.direction === "out" && item.key.startsWith("dm:") && item.status === "queued" && item.expectedAckCrc === confirmation.ackCode); + if (pending) { pending.status = "delivered"; persistHistory(); renderMessages(); toast("Direct message delivered"); } + }); + radio.on(Constants.PushCodes.LogRxData, (sample) => { + state.radio = { ...(state.radio || {}), lastRssi: sample.lastRssi, lastSnr: sample.lastSnr }; + state.rfSamples.push(sample.lastRssi); state.rfSamples = state.rfSamples.slice(-36); renderHome(); renderRadio(); + }); +} + +document.querySelectorAll(".nav button").forEach((button) => button.addEventListener("click", () => showView(button.dataset.view))); +document.querySelectorAll("[data-open]").forEach((button) => button.addEventListener("click", () => showView(button.dataset.open))); +$("target-select").addEventListener("change", (event) => selectTarget(event.target.value)); +$("composer").addEventListener("submit", sendMessage); +$("message-input").addEventListener("input", updateComposeCount); +$("nearby-refresh").addEventListener("click", () => exclusive(refreshContacts).then(() => toast("Nearby refreshed")).catch(() => toast("Refresh failed", "error"))); +document.querySelectorAll("[data-map-mode]").forEach((button) => button.addEventListener("click", () => { + state.mapMode = button.dataset.mapMode; + document.querySelectorAll("[data-map-mode]").forEach((item) => item.classList.toggle("active", item === button)); + requestAnimationFrame(drawMeshMap); +})); +$("refresh-button").addEventListener("click", () => exclusive(syncAll).then(() => toast("Sync complete")).catch(() => toast("Sync failed", "error"))); +$("advert-button").addEventListener("click", async () => { + $("advert-button").disabled = true; + try { await exclusive(() => connection.sendFloodAdvert()); toast("Advert queued", "warn"); } + catch { toast("Advert failed", "error"); } + finally { $("advert-button").disabled = false; } +}); +$("connect-button").addEventListener("click", () => { + recoveryAttempts = 0; + if (state.connected) return exclusive(syncAll).catch(() => toast("Sync failed", "error")); + return connection.connect().catch(() => toast("Could not reconnect", "error")); +}); +$("network-setup-button").addEventListener("click", openNetworkDialog); +$("network-dialog-close").addEventListener("click", closeNetworkDialog); +$("network-cancel").addEventListener("click", closeNetworkDialog); +$("network-done").addEventListener("click", closeNetworkDialog); +$("network-reveal").addEventListener("click", () => { + const password = $("network-password"); + const revealed = password.type === "password"; + password.type = revealed ? "text" : "password"; + $("network-reveal").textContent = revealed ? "Hide" : "Show"; + $("network-reveal").setAttribute("aria-pressed", String(revealed)); +}); +$("network-form").addEventListener("submit", async (event) => { + event.preventDefault(); + const ssid = $("network-ssid").value; + const password = $("network-password").value; + const ssidBytes = encoder.encode(ssid).length; + const passwordBytes = encoder.encode(password).length; + if (ssidBytes < 1 || ssidBytes > 32) { toast("Wi-Fi name must be 1–32 bytes", "error"); return $("network-ssid").focus(); } + if (passwordBytes !== 0 && (passwordBytes < 8 || passwordBytes > 64)) { toast("Password must be empty or 8–64 bytes", "error"); return $("network-password").focus(); } + $("network-save").disabled = true; + try { + await setNetwork({ mode: "station", ssid, password }); + showNetworkResult("station"); + toast("Local Wi-Fi saved. Radio is restarting.", "warn"); + } catch { toast("Could not save Wi-Fi settings", "error"); } + finally { $("network-save").disabled = false; } +}); +$("return-ap-button").addEventListener("click", async () => { + if (!confirm("Return the radio to setup AP mode? The device will restart.")) return; + $("return-ap-button").disabled = true; + try { + await setNetwork({ mode: "ap" }); + openNetworkDialog(); + showNetworkResult("ap"); + toast("Setup AP requested. Radio is restarting.", "warn"); + } catch { toast("Could not return to setup AP", "error"); } + finally { $("return-ap-button").disabled = false; } +}); +$("clear-history-button").addEventListener("click", () => { + if (!state.messages.length || !confirm("Clear locally stored message history for this node?")) return; + try { localStorage.removeItem(historyKey()); } catch { /* storage is optional */ } + state.messages = []; + renderAll(); + toast("Local message history cleared"); +}); +$("ultimate-settings-form").addEventListener("submit", async (event) => { + event.preventDefault(); + const quickPhrases = [...document.querySelectorAll("[data-phrase]")].map((input) => input.value); + try { + const response = await ultimateFetch("/api/ultimate/settings", { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + historyCapacity: Number($("ultimate-history-cap").value), + scanCadenceMs: Number($("ultimate-cadence").value), + powerProfile: Number($("ultimate-power-profile").value), + batteryCapacityMah: Number($("ultimate-battery-capacity").value), + batteryCalibrationMv: Number($("ultimate-battery-calibration").value), + privateNotifications: $("ultimate-private").checked, + quickPhrases, + }), + }); + state.ultimateSettings = await response.json(); + renderUltimate(); toast("Device settings saved"); + } catch { toast("Could not save device settings", "error"); } +}); +$("ultimate-export").addEventListener("click", () => { + window.location.assign("/api/ultimate/export"); +}); +$("ultimate-clear").addEventListener("click", async () => { + if (!confirm("Permanently erase the /np/ message journal? MeshCore identity and contacts are preserved.")) return; + try { + await ultimateFetch("/api/ultimate/history", { method: "DELETE", headers: { "X-NP-Confirm": "clear" } }); + await refreshUltimate(); toast("On-device history cleared", "warn"); + } catch { toast("History clear failed", "error"); } +}); + +async function saveBrowserLocation(latitude, longitude, accuracy) { + text("ultimate-location-preview", `${latitude.toFixed(6)}, ${longitude.toFixed(6)} · ±${Math.round(accuracy)} m`); + const advertise = $("ultimate-location-share").checked; + const sharing = advertise ? "This location WILL be included in adverts." : "This location will remain private."; + if (!confirm(`Save this location to MeshCore?\n\n${latitude.toFixed(6)}, ${longitude.toFixed(6)}\nAccuracy ±${Math.round(accuracy)} m\n\n${sharing}`)) return; + await ultimateFetch("/api/ultimate/location", { + method: "PUT", headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ latitude, longitude, accuracy, advertise }), + }); + toast(advertise ? "Location saved and enabled for adverts" : "Location saved privately"); +} + +$("ultimate-location").addEventListener("click", () => { + const manual = async () => { + const value = prompt("Browser location is unavailable on this connection. Enter latitude,longitude:", ""); + if (!value) return; + const [latitude, longitude] = value.split(",").map(Number); + if (!Number.isFinite(latitude) || !Number.isFinite(longitude)) return toast("Enter latitude,longitude", "error"); + try { await saveBrowserLocation(latitude, longitude, 0); } catch { toast("Location save failed", "error"); } + }; + if (!navigator.geolocation) return manual(); + navigator.geolocation.getCurrentPosition( + (position) => saveBrowserLocation(position.coords.latitude, position.coords.longitude, + position.coords.accuracy).catch(() => toast("Location save failed", "error")), + manual, { enableHighAccuracy: true, timeout: 12000, maximumAge: 0 }); +}); + +$("ultimate-ota-form").addEventListener("submit", async (event) => { + event.preventDefault(); + const file = $("ultimate-ota-file").files[0]; + if (!file || !file.name.toLowerCase().endsWith(".npu")) return toast("Choose a NeonPocket .npu package", "error"); + if (!confirm(`Verify and install ${file.name}? The radio will restart only after board, mode, hash, and signature checks pass.`)) return; + const body = new FormData(); body.append("package", file, file.name); + const button = $("ultimate-ota-form").querySelector("button"); button.disabled = true; + try { + const response = await fetch("/api/ultimate/ota", { method: "POST", body, credentials: "same-origin" }); + const result = await response.json().catch(() => ({})); + if (!response.ok || result.result !== "success") throw new Error(result.result || "upload-failed"); + toast("Signature verified. Restarting into the new app.", "warn"); + } catch (error) { toast(`Update rejected: ${error.message}`, "error"); button.disabled = false; } +}); +window.addEventListener("resize", () => { + if (state.view === "home") renderDashboardAnalytics(); + if (state.view === "nearby") drawMeshMap(); + if (state.view === "radio") { drawSignalChart(); drawRadioActivity(); } + if (state.view === "ultimate") renderUltimate(); +}); +document.addEventListener("visibilitychange", () => { + if (!document.hidden && state.connected) exclusive(async () => { + await syncMessages(); + await refreshStats(); + await refreshNetworkStatus(); + }).catch(() => toast("Foreground sync failed", "error")); +}); +window.addEventListener("pagehide", () => connection.leavePage()); +window.addEventListener("pageshow", (event) => { + if (event.persisted && !connection.running) { + setLink("reconnecting"); + connection.connect().catch(() => toast("Could not reconnect", "error")); + } +}); + +bindConnectionEvents(connection); +renderAll(); +setInterval(() => { + text("hero-uptime", formatSession()); + if (!document.hidden && state.connected) exclusive(refreshStats).catch(() => {}); + if (!document.hidden) refreshUltimate(); +}, 30000); +connection.connect().catch(() => { setLink("offline"); $("boot").classList.add("hidden"); toast("Could not open companion link", "error"); }); diff --git a/examples/companion_radio/webui/src/index.html b/examples/companion_radio/webui/src/index.html new file mode 100644 index 00000000..c9473f49 --- /dev/null +++ b/examples/companion_radio/webui/src/index.html @@ -0,0 +1,262 @@ + + + + + + + + + + + + NeonPocketMC · Ultimate + + + + + +
+ + +
+
+

NEONPOCKETMC

Overview

+
+ — V + +
+
+ +
+
+
+

YOUR MESH, RIGHT NOW

+

RADIO

+

Waiting for companion link…

+
+ +
+
CONNECTIONStarting
+
KNOWN NODES
+
ONLINE FOR
+
+
+ +
+ +

LOCAL CONNECTION

Set up local Wi-Fi

Use your 2.4 GHz home network instead of staying on the setup hotspot.

+ +
+ +
+
+

INBOX

Unread messages

0
+
No unread messages
+ +
+ +
+

MESH ACTIVITY

Recently nearby

+
Waiting for adverts
+ +
+ +
+

LIVE RADIO

— dBmSNR —
+
+
— MHz— packets
+
+
+

LAST 24 HOURS

Mesh traffic

No samples yet
+ +
ReceivedSentFailed
+
+
+
RECEIVEDpackets
+
SENTpackets
+
DELIVERYIDLENo local send
+
HISTORYstored messages
+
+
+
+

SIGNAL SAMPLES

What the radio has heard

Waiting for packets
+ +
Very weakUsableStrong
+
+
+

RECENTLY HEARD

Freshest nearby nodes

+

Waiting for node adverts.

+
+
+
+
+ +
+
+ +
+
+
#
+
Choose a conversationChannel or direct contact
+
+
Select a channel or contact to start talking across the mesh.
+
+ + +
+ + + +
+ 0 / 140 bytes +
+
+
+
+ +
+

NEARBY NODES

What this radio can hear

Choose real advertised locations or a hop-and-recency view. The radio stays on your configured preset.

+
+ + +
+
+ +
0 located0 without location
+
No advertised locations yetSwitch to Hop view to see recent nodes without inventing a location for them.
+
+

RECENTLY HEARD

Nodes on this preset

+
Nearby nodes appear as adverts arrive.
+
+ +
+
+
+

SIGNAL HEALTH

Live RF

LIVE
+
dBmWaiting for sample
+ +
RSSISNR —Noise —
+
+
+
FREQUENCYMHz
+
BANDWIDTHkHz
+
SPREADINGCR —
+
TX POWERdBm
+
+
+

PACKETS

On-air counters

0 errors
+
Received
Sent
Direct RX
Flood RX
+
+
+

HOURLY ACTIVITY

Seven-day traffic

No samples yet
+ +
RXTXFailures
+
+
+
+ +
+
+

THIS RADIO

Storage, power and display

See what the device is using, keep message history, tune the screen, and install signed updates.

+
MEMORY HEADROOMChecking
+
+
+
DEVICE HISTORYrecords
+
FREE HEAPlargest —
+
DISPLAY FLUSHtiles —
+
EVENT QUEUEoutbound —
+
BATTERY TRENDWaiting for samples
+
LATEST DELIVERYIDLENo on-device send
+
+
+

TWO HOURS

Recent activity

LIVE
RXTXBattery
+

SEVEN DAYS

Hourly activity

RXTXFailures
+

RECENT RADIOS

Heard by this device

0 radios

Listening on the configured preset.

+

DEVICE HISTORY

Recent messages

No stored messages.

+
+
+
+

DISPLAY & HISTORY

NeonPocket settings

+ + + + + + +
+ +
+
+

EXPLICIT LOCATION

Transfer this browser's location

Requested only when you tap below. Coordinates, accuracy, and advert sharing are confirmed before saving.

No location requested
+

SIGNED UPDATE

Install a NeonPocket .npu package

The device checks the board, mode, file hash and signature before writing the inactive app slot.

+

DANGER ZONE

Erase on-device history

Disabling history never erases it. This confirmed action permanently clears only the separate /np/ journal.

+
+
+
+
+

DEVICE

+

RADIO

MeshCore Companion

+
Firmware
Build
Public key
+
+
+

PRESENCE

Advertise this node

Queue a flood advert so nearby MeshCore nodes can discover you.

+

CONNECTION

Setup AP

Connected through this device at 192.168.4.1

+

PRIVACY

Local message history

Up to 120 messages are kept in this browser for this node. Clear them whenever you like.

+

ABOUT

NeonPocket Ultimate Companion

Offline-first MeshCore companion · v
Powered by MeshCore.js · MIT © Liam Cottle

+
+
+
+
+
+ + +

RADIO NETWORK

Set up local Wi-Fi

+
CURRENT CONNECTIONChecking radio…
+
+ + + +
+

SSID: 1–32 bytes. Password: empty, or 8–64 characters. This radio supports 2.4 GHz networks only.

+
+
+ +
+ +
+
NeonPocketMCMeshCore Ultimate companion
+ + + diff --git a/examples/companion_radio/webui/src/styles.css b/examples/companion_radio/webui/src/styles.css new file mode 100644 index 00000000..a1568238 --- /dev/null +++ b/examples/companion_radio/webui/src/styles.css @@ -0,0 +1,301 @@ +:root { + color-scheme: dark; + --bg: #050608; + --panel: #0c0f13; + --panel-2: #11151b; + --line: #222934; + --text: #f5f8fb; + --muted: #8d98a7; + --green: #44f08a; + --blue: #42a5ff; + --yellow: #ffd447; + --orange: #ff8a3d; + --red: #ff5468; + --radius: 22px; + --shadow: 0 20px 70px rgba(0, 0, 0, .32); + font-family: -apple-system, BlinkMacSystemFont, "SF Pro Display", "Segoe UI", sans-serif; + background: var(--bg); + color: var(--text); + font-synthesis: none; +} + +* { box-sizing: border-box; } +[hidden] { display: none !important; } +html { min-height: 100%; background: var(--bg); } +body { margin: 0; min-height: 100vh; min-height: 100dvh; overflow-x: hidden; background: + radial-gradient(circle at 75% -15%, rgba(66, 165, 255, .12), transparent 32rem), + radial-gradient(circle at 20% 110%, rgba(68, 240, 138, .08), transparent 35rem), var(--bg); } +button, textarea, select { font: inherit; color: inherit; } +button { border: 0; cursor: pointer; } +input { font: inherit; color: inherit; } +button:focus-visible, textarea:focus-visible, select:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; } +input:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; } +svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; } +.svg-defs { position: absolute; width: 0; height: 0; } +.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; } + +.shell { display: grid; grid-template-columns: 224px minmax(0, 1fr); min-height: 100vh; min-height: 100dvh; } +.sidebar { position: fixed; z-index: 20; inset: 0 auto 0 0; width: 224px; padding: 28px 18px 22px; display: flex; flex-direction: column; border-right: 1px solid rgba(255, 255, 255, .055); background: rgba(6, 8, 11, .88); backdrop-filter: blur(22px); } +.brand { display: flex; align-items: center; gap: 12px; padding: 4px 10px 32px; font-weight: 740; letter-spacing: -.03em; font-size: 19px; } +.brand small { display: block; margin-top: 2px; font-size: 9px; letter-spacing: .16em; color: var(--muted); text-transform: uppercase; } +.brand-mark { width: 30px; height: 30px; flex: 0 0 auto; overflow: visible; filter: drop-shadow(0 0 7px rgba(57, 231, 255, .3)); } +.brand-mark.large { width: 58px; height: 58px; filter: drop-shadow(0 0 12px rgba(57, 231, 255, .38)); } + +.nav { display: grid; gap: 7px; } +.nav button { position: relative; min-height: 49px; padding: 0 13px; display: flex; align-items: center; gap: 13px; color: var(--muted); border-radius: 14px; background: transparent; text-align: left; transition: color .2s ease, background .2s ease, transform .2s ease; } +.nav button:hover { color: var(--text); background: rgba(255, 255, 255, .045); } +.nav button.active { color: var(--green); background: linear-gradient(90deg, rgba(68, 240, 138, .13), rgba(68, 240, 138, .035)); box-shadow: inset 2px 0 var(--green); } +.nav button:active { transform: scale(.98); } +.nav button span { font-size: 14px; font-weight: 650; } +.nav button b { margin-left: auto; min-width: 20px; height: 20px; padding: 0 6px; display: grid; place-items: center; border-radius: 10px; color: #120e00; background: var(--yellow); font-size: 10px; } +.sidebar-foot { margin-top: auto; padding: 14px 11px 0; display: flex; gap: 8px; align-items: center; color: var(--muted); font-size: 11px; } +.status-dot { width: 8px; height: 8px; display: inline-block; flex: 0 0 auto; border-radius: 50%; background: var(--yellow); box-shadow: 0 0 12px currentColor; } +.connected .status-dot, .status-dot.connected { color: var(--green); background: var(--green); } +.reconnecting .status-dot, .status-dot.reconnecting { color: var(--yellow); background: var(--yellow); } +.busy .status-dot, .status-dot.busy { color: var(--yellow); background: var(--yellow); } +.offline .status-dot, .status-dot.offline { color: var(--red); background: var(--red); } + +main { grid-column: 2; width: min(100%, 1320px); margin: 0 auto; padding: 24px clamp(24px, 4vw, 62px) 54px; } +.topbar { min-height: 72px; display: flex; align-items: center; justify-content: space-between; gap: 20px; margin-bottom: 22px; } +.topbar h1 { margin: 2px 0 0; font-size: clamp(27px, 3vw, 38px); line-height: 1; letter-spacing: -.045em; } +.eyebrow { margin: 0; color: var(--muted); font-size: 9px; line-height: 1.2; font-weight: 760; letter-spacing: .18em; text-transform: uppercase; } +.top-actions { display: flex; align-items: center; gap: 9px; } +.battery, .link-chip { height: 38px; padding: 0 13px; display: inline-flex; align-items: center; gap: 8px; border: 1px solid var(--line); border-radius: 13px; background: rgba(13, 16, 21, .82); color: var(--muted); font-size: 11px; font-weight: 700; letter-spacing: .03em; } +.link-chip:hover { border-color: #394351; color: var(--text); } + +.screen { display: none; animation: screen-in .24s cubic-bezier(.2, .75, .25, 1); } +.screen.active { display: block; } +@keyframes screen-in { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } } +.card { position: relative; border: 1px solid rgba(255, 255, 255, .072); border-radius: var(--radius); background: linear-gradient(145deg, rgba(18, 22, 28, .94), rgba(10, 13, 17, .96)); box-shadow: var(--shadow); overflow: hidden; } +.card::after { content: ""; position: absolute; pointer-events: none; inset: 0; border-radius: inherit; background: linear-gradient(130deg, rgba(255, 255, 255, .025), transparent 35%); } +.card-head, .section-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; } +.card h3, .section-head h2 { margin: 5px 0 0; letter-spacing: -.025em; } +.card h3 { font-size: 18px; } + +.hero { min-height: 268px; padding: clamp(26px, 4vw, 43px); display: grid; grid-template-columns: 1.2fr .8fr; align-items: center; background: + radial-gradient(circle at 78% 30%, rgba(68, 240, 138, .15), transparent 18rem), + linear-gradient(130deg, rgba(21, 28, 31, .98), rgba(6, 10, 12, .98)); } +.hero::before { content: ""; position: absolute; inset: 0; background-image: linear-gradient(rgba(68, 240, 138, .025) 1px, transparent 1px), linear-gradient(90deg, rgba(68, 240, 138, .025) 1px, transparent 1px); background-size: 24px 24px; mask-image: linear-gradient(90deg, transparent, #000); } +.hero-copy, .hero-stats, .mesh-orbit { position: relative; z-index: 2; } +.hero-copy h2 { margin: 8px 0 8px; max-width: 620px; font-size: clamp(39px, 6vw, 72px); line-height: .98; letter-spacing: -.065em; } +.hero-copy > p:last-child { margin: 0; color: var(--muted); font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 11px; } +.hero-stats { align-self: end; grid-column: 1 / -1; display: flex; gap: 44px; margin-top: 28px; } +.hero-stats div { display: grid; gap: 5px; } +.hero-stats small { color: var(--muted); font-size: 8px; font-weight: 750; letter-spacing: .16em; } +.hero-stats strong { font-size: 13px; } +.mesh-orbit { justify-self: center; width: 154px; aspect-ratio: 1; display: grid; place-items: center; border: 1px solid rgba(68, 240, 138, .22); border-radius: 50%; box-shadow: 0 0 60px rgba(68, 240, 138, .09), inset 0 0 40px rgba(68, 240, 138, .06); } +.mesh-orbit::before, .mesh-orbit::after { content: ""; position: absolute; border-radius: 50%; border: 1px solid rgba(68, 240, 138, .18); } +.mesh-orbit::before { inset: 20px; } +.mesh-orbit::after { inset: 48px; background: rgba(68, 240, 138, .07); } +.mesh-orbit span { width: 11px; height: 11px; z-index: 1; border-radius: 50%; background: var(--green); box-shadow: 0 0 22px var(--green); } +.mesh-orbit i { position: absolute; width: 6px; height: 6px; border-radius: 50%; background: var(--blue); box-shadow: 0 0 12px var(--blue); } +.mesh-orbit i:nth-child(1) { top: 15px; left: 78px; } +.mesh-orbit i:nth-child(2) { right: 22px; bottom: 33px; background: var(--yellow); box-shadow: 0 0 12px var(--yellow); } +.mesh-orbit i:nth-child(3) { left: 13px; bottom: 54px; background: var(--orange); box-shadow: 0 0 12px var(--orange); } + +.dashboard-grid { margin-top: 18px; display: grid; grid-template-columns: 1fr 1fr; gap: 18px; } +.network-banner { min-height: 100px; margin-top: 18px; padding: 20px 22px; display: grid; grid-template-columns: 48px minmax(0, 1fr) auto; gap: 16px; align-items: center; border-color: rgba(66, 165, 255, .2); background: linear-gradient(120deg, rgba(20, 45, 67, .8), rgba(10, 15, 21, .96)); } +.network-banner h3 { margin: 5px 0 4px; }.network-banner p:not(.eyebrow) { margin: 0; color: var(--muted); font-size: 11px; line-height: 1.45; } +.network-icon { width: 48px; height: 48px; display: grid; place-items: center; border-radius: 16px; color: var(--blue); background: rgba(66, 165, 255, .12); box-shadow: inset 0 0 18px rgba(66, 165, 255, .08); } +.feature-card { min-height: 210px; padding: 24px; display: flex; flex-direction: column; } +.feature-card::before { content: ""; position: absolute; left: 0; top: 22px; bottom: 22px; width: 3px; border-radius: 0 3px 3px 0; background: var(--accent); box-shadow: 0 0 16px var(--accent); } +.accent-yellow { --accent: var(--yellow); } +.accent-orange { --accent: var(--orange); } +.count { min-width: 38px; height: 30px; display: grid; place-items: center; border: 1px solid color-mix(in srgb, var(--accent), transparent 65%); border-radius: 11px; color: var(--accent); background: color-mix(in srgb, var(--accent), transparent 91%); font-weight: 760; } +.signal-bars { display: flex; align-items: end; gap: 3px; height: 22px; color: var(--accent); } +.signal-bars i { width: 3px; border-radius: 2px; background: currentColor; box-shadow: 0 0 7px currentColor; } +.signal-bars i:nth-child(1) { height: 7px; }.signal-bars i:nth-child(2) { height: 13px; }.signal-bars i:nth-child(3) { height: 20px; } +.preview { margin: 22px 0 18px; display: grid; gap: 5px; } +.preview strong { font-size: 15px; } +.preview span { color: var(--muted); font-size: 12px; line-height: 1.45; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +.preview.empty { color: var(--muted); font-size: 12px; } +.text-action { position: relative; z-index: 2; min-height: 44px; margin-top: auto; padding: 10px 0 0; display: flex; justify-content: space-between; align-items: center; background: transparent; color: var(--text); font-size: 11px; font-weight: 700; } +.text-action span { color: var(--accent, var(--green)); font-size: 16px; transition: transform .2s; } +.text-action:hover span { transform: translateX(3px); } +.radio-strip { grid-column: 1 / -1; min-height: 112px; padding: 22px 24px; display: grid; grid-template-columns: 150px 1fr 190px; align-items: center; gap: 24px; } +.radio-strip > div:first-child { display: grid; grid-template-columns: auto 1fr; align-items: baseline; gap: 3px 8px; } +.radio-strip .eyebrow { grid-column: 1 / -1; margin-bottom: 5px; } +.radio-strip strong { font-size: 23px; letter-spacing: -.04em; } +.radio-strip span { color: var(--muted); font-size: 10px; } +.radio-meter { height: 6px; border-radius: 3px; background: #212832; overflow: hidden; } +.radio-meter i { display: block; width: 0; height: 100%; border-radius: inherit; background: linear-gradient(90deg, var(--red), var(--yellow), var(--green)); box-shadow: 0 0 15px var(--green); transition: width .4s ease; } +.radio-meta { display: flex; justify-content: flex-end; gap: 18px; color: var(--muted); } +.dashboard-activity { grid-column: 1 / -1; min-height: 205px; padding: 22px 24px; } +.dashboard-activity canvas { width: 100%; height: 116px; display: block; margin: 12px 0 8px; } +.dashboard-activity .quiet, .radio-activity-card .quiet { color: var(--muted); font-size: 10px; } +.dashboard-health { grid-column: 1 / -1; display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; } +.health-tile { min-height: 110px; padding: 18px; display: grid; align-content: end; gap: 3px; } +.health-tile small { margin-bottom: auto; color: var(--muted); font-size: 9px; font-weight: 760; letter-spacing: .12em; } +.health-tile strong { font-size: 24px; letter-spacing: -.04em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +.health-tile span { color: var(--muted); font-size: 9px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +.dashboard-analytics { grid-column: 1 / -1; display: grid; grid-template-columns: 1.15fr .85fr; gap: 18px; } +.analytics-card { min-height: 238px; padding: 22px 24px; } +.analytics-card canvas { width: 100%; height: 138px; display: block; margin: 14px 0 4px; } +.signal-scale { display: flex; justify-content: space-between; color: var(--muted); font-size: 9px; } +.nearby-bars { margin-top: 17px; display: grid; gap: 10px; } +.nearby-bar { display: grid; grid-template-columns: minmax(90px, 1fr) minmax(110px, 1.25fr) auto; gap: 11px; align-items: center; } +.nearby-bar strong { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; } +.nearby-bar .bar-track { height: 7px; overflow: hidden; border-radius: 8px; background: #202731; } +.nearby-bar .bar-track i { display: block; height: 100%; border-radius: inherit; background: linear-gradient(90deg, var(--blue), var(--green)); box-shadow: 0 0 9px rgba(68,240,138,.25); } +.nearby-bar span { min-width: 68px; color: var(--muted); font-size: 9px; text-align: right; } + +.messages-layout { height: calc(100vh - 142px); min-height: 560px; display: grid; grid-template-columns: minmax(230px, 31%) 1fr; } +.threads-panel { padding: 24px 15px; border-right: 1px solid var(--line); overflow: hidden; } +.threads-panel .section-head { padding: 0 9px 17px; } +.threads-panel .section-head span { color: var(--muted); font-size: 11px; } +.threads { height: calc(100% - 60px); overflow-y: auto; scrollbar-width: thin; } +.thread { width: 100%; padding: 12px 10px; display: grid; grid-template-columns: 39px minmax(0, 1fr) auto; gap: 11px; align-items: center; border-radius: 13px; background: transparent; text-align: left; } +.thread:hover { background: rgba(255, 255, 255, .04); } +.thread.active { background: rgba(66, 165, 255, .11); } +.thread .avatar { width: 39px; height: 39px; } +.thread-copy { min-width: 0; display: grid; gap: 3px; } +.thread-copy strong, .thread-copy small { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +.thread-copy strong { font-size: 12px; }.thread-copy small { color: var(--muted); font-size: 10px; } +.thread-time { color: var(--muted); font-size: 10px; } +.thread-badge { min-width: 20px; height: 20px; padding: 0 5px; display: grid; place-items: center; border-radius: 10px; color: #151000; background: var(--yellow); font-size: 9px; font-weight: 800; } +.avatar { display: grid; place-items: center; flex: 0 0 auto; border-radius: 13px; color: var(--blue); background: rgba(66, 165, 255, .11); font-size: 12px; font-weight: 800; } +.conversation { min-width: 0; display: grid; grid-template-rows: auto 1fr auto; } +.conversation-head { min-height: 73px; padding: 14px 22px; display: flex; align-items: center; gap: 12px; border-bottom: 1px solid var(--line); } +.conversation-head .avatar { width: 40px; height: 40px; } +.conversation-head div:last-child { min-width: 0; display: grid; gap: 3px; } +.conversation-head strong { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 13px; } +.conversation-head small { color: var(--muted); font-size: 9px; } +.message-list { padding: 22px; display: flex; flex-direction: column; gap: 10px; overflow-y: auto; } +.welcome-bubble, .empty-state { margin: auto; color: var(--muted); text-align: center; font-size: 12px; line-height: 1.55; } +.bubble { max-width: min(78%, 580px); padding: 11px 14px 8px; display: grid; gap: 5px; align-self: flex-start; border: 1px solid var(--line); border-radius: 17px 17px 17px 5px; background: #151a21; } +.bubble.out { align-self: flex-end; border-color: rgba(68, 240, 138, .2); border-radius: 17px 17px 5px 17px; background: rgba(25, 67, 44, .65); } +.bubble p { margin: 0; font-size: 13px; line-height: 1.45; overflow-wrap: anywhere; white-space: pre-wrap; } +.bubble small { color: var(--muted); font-size: 10px; } +.bubble.out small { text-align: right; }.bubble.failed small { color: var(--red); } +.composer { padding: 13px 16px max(14px, env(safe-area-inset-bottom)); border-top: 1px solid var(--line); background: rgba(7, 9, 12, .7); } +.composer select { width: 100%; height: 44px; margin-bottom: 7px; padding: 0 10px; border: 0; border-radius: 11px; color: var(--muted); background: #11161d; font-size: 12px; } +.compose-row { display: grid; grid-template-columns: 1fr 43px; gap: 8px; } +.composer textarea { width: 100%; min-height: 44px; max-height: 110px; padding: 11px 13px; resize: none; border: 1px solid var(--line); border-radius: 14px; outline: 0; background: #11161c; font-size: 12px; line-height: 20px; } +.composer textarea:focus { border-color: rgba(66, 165, 255, .6); } +.send-button { width: 44px; height: 44px; display: grid; place-items: center; border-radius: 14px; color: #03130a; background: var(--green); box-shadow: 0 0 18px rgba(68, 240, 138, .2); } +.send-button:hover { filter: brightness(1.08); }.send-button:disabled { opacity: .35; cursor: not-allowed; } +.composer > small { display: block; margin: 5px 3px 0; color: var(--muted); font-size: 10px; text-align: right; } + +.page-head { margin: 5px 0 20px; }.page-head h2 { font-size: 28px; } +.page-head p:not(.eyebrow) { max-width: 680px; margin: 7px 0 0; color: var(--muted); font-size: 11px; line-height: 1.55; } +.icon-button { width: 44px; height: 44px; display: grid; place-items: center; border: 1px solid var(--line); border-radius: 13px; color: var(--muted); background: var(--panel); } +.icon-button:hover { color: var(--green); border-color: rgba(68, 240, 138, .3); } +.map-switch { width: max-content; margin: -8px 0 14px; padding: 4px; display: flex; gap: 3px; border: 1px solid var(--line); border-radius: 12px; background: var(--panel); } +.map-switch button { min-height: 38px; padding: 0 14px; border-radius: 9px; color: var(--muted); background: transparent; font-size: 10px; font-weight: 720; } +.map-switch button.active { color: #03130a; background: var(--green); box-shadow: 0 0 18px rgba(68,240,138,.16); } +.nearby-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 15px; } +.nearby-card { min-height: 128px; padding: 20px; display: grid; grid-template-columns: 42px 1fr auto; gap: 13px; align-items: start; } +.nearby-card .avatar { width: 42px; height: 42px; color: var(--orange); background: rgba(255, 138, 61, .12); } +.nearby-card h3 { margin: 3px 0 6px; font-size: 14px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } +.nearby-card p { margin: 0; color: var(--muted); font-size: 11px; line-height: 1.5; } +.nearby-card time { color: var(--orange); font-size: 10px; } +.nearby-card .contact-action { grid-column: 2 / -1; min-height: 44px; justify-self: start; padding: 10px 0; color: var(--blue); background: transparent; font-size: 11px; font-weight: 700; } +.mesh-map-card { min-height: 420px; margin-bottom: 25px; background: #070b10; } +#mesh-map { width: 100%; height: 410px; display: block; } +.map-badge { position: absolute; z-index: 2; top: 16px; right: 16px; padding: 10px 12px; display: grid; gap: 2px; border: 1px solid rgba(57,231,255,.2); border-radius: 12px; background: rgba(5,8,12,.82); backdrop-filter: blur(8px); } +.map-badge strong { color: var(--green); font-size: 11px; }.map-badge span { color: var(--muted); font-size: 9px; } +.map-empty { position: absolute; z-index: 2; inset: 0; display: grid; place-content: center; justify-items: center; gap: 7px; padding: 30px; text-align: center; pointer-events: none; } +.map-empty strong { font-size: 17px; }.map-empty span { max-width: 420px; color: var(--muted); font-size: 11px; line-height: 1.55; } +.nearby-subhead { margin: 0 0 15px; }.nearby-subhead h2 { margin: 5px 0 0; font-size: 22px; } + +.radio-grid { display: grid; grid-template-columns: minmax(0, 1.45fr) minmax(260px, .75fr); gap: 16px; } +.signal-card { grid-row: span 2; padding: 25px; } +.live-pill { padding: 5px 8px; border-radius: 8px; color: var(--green); background: rgba(68, 240, 138, .1); font-size: 8px; font-weight: 800; letter-spacing: .08em; } +.signal-readout { margin: 28px 0 5px; display: flex; align-items: baseline; gap: 7px; } +.signal-readout strong { font-size: clamp(48px, 7vw, 78px); line-height: .9; letter-spacing: -.07em; } +.signal-readout span { color: var(--muted); font-size: 13px; }.signal-readout small { margin-left: auto; color: var(--green); font-size: 9px; } +#signal-chart { width: 100%; height: 145px; display: block; } +.chart-legend { display: flex; gap: 17px; color: var(--muted); font-size: 9px; } +.chart-legend span { display: flex; align-items: center; gap: 6px; }.chart-legend i { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }.chart-legend .green { color: var(--green); } +.metric-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; } +.metric { min-height: 126px; padding: 20px; display: flex; flex-direction: column; justify-content: flex-end; } +.metric small { margin-bottom: auto; color: var(--muted); font-size: 10px; letter-spacing: .11em; font-weight: 750; } +.metric strong { font-size: clamp(24px, 3vw, 34px); letter-spacing: -.05em; }.metric span { margin-top: 2px; color: var(--muted); font-size: 9px; } +.packet-card { padding: 23px; }.packet-card .quiet { color: var(--muted); font-size: 9px; } +.packet-card .error { color: var(--red); } +.packet-stats { margin-top: 24px; display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; } +.packet-stats div { display: grid; gap: 4px; }.packet-stats strong { font-size: 20px; }.packet-stats small { color: var(--muted); font-size: 10px; } +.radio-activity-card { grid-column: 1 / -1; padding: 23px; } +.radio-activity-card canvas { width: 100%; height: 176px; display: block; margin: 14px 0 8px; } + +.ultimate-hero { min-height: 190px; padding: 30px; display: grid; grid-template-columns: 1fr auto; gap: 28px; align-items: center; overflow: hidden; background: radial-gradient(circle at 78% 30%, rgba(57,231,255,.14), transparent 29%), linear-gradient(130deg, rgba(36,87,255,.1), rgba(68,240,138,.035)); } +.ultimate-hero::before { content:""; position:absolute; inset:-30% 45% -30% auto; width:2px; background:linear-gradient(transparent,var(--blue),var(--green),transparent); box-shadow:0 0 35px var(--blue); transform:rotate(18deg); animation:ultimate-beam 4s ease-in-out infinite; } +.ultimate-hero h2 { max-width:720px; margin:7px 0 10px; font-size:clamp(28px,4vw,48px); line-height:1.02; letter-spacing:-.055em; }.ultimate-hero p:not(.eyebrow){max-width:680px;margin:0;color:var(--muted);font-size:12px;line-height:1.65} +.ultimate-gate { width:150px; height:150px; display:grid; place-content:center; justify-items:center; gap:6px; border:1px solid rgba(57,231,255,.24); border-radius:50%; background:rgba(5,8,12,.72); box-shadow:inset 0 0 45px rgba(36,87,255,.13),0 0 38px rgba(57,231,255,.08); }.ultimate-gate span{width:12px;height:12px;border-radius:50%;background:var(--yellow);box-shadow:0 0 16px currentColor}.ultimate-gate span.pass{background:var(--green)}.ultimate-gate small{color:var(--muted);font-size:8px;letter-spacing:.11em}.ultimate-gate strong{font-size:16px} +.ultimate-metrics { margin:16px 0; display:grid; grid-template-columns:repeat(4,1fr); gap:12px }.ultimate-metrics .metric{min-height:116px} +.ultimate-grid { display:grid; grid-template-columns:1fr 1fr; gap:16px }.ultimate-chart-card,.ultimate-nodes,.ultimate-history{padding:23px;min-height:280px}.ultimate-chart-card canvas{width:100%;height:176px;display:block;margin-top:14px}.chart-legend .blue{color:var(--blue)}.chart-legend .orange{color:var(--orange)}.chart-legend .red{color:var(--red)} +.ultimate-node-list,.ultimate-history-list{margin-top:17px;display:grid;gap:8px;max-height:250px;overflow:auto}.ultimate-node,.ultimate-record{padding:11px 12px;display:grid;grid-template-columns:1fr auto;gap:5px 14px;border:1px solid var(--line);border-radius:12px;background:rgba(255,255,255,.018)}.ultimate-node strong,.ultimate-record strong{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:12px}.ultimate-node span,.ultimate-record span{color:var(--muted);font-size:9px}.ultimate-node b{color:var(--green);font-size:10px}.ultimate-record p{grid-column:1/-1;margin:0;color:var(--text);font-size:11px;line-height:1.45} +.ultimate-settings-grid{margin-top:16px;display:grid;grid-template-columns:minmax(300px,.85fr) minmax(0,1.15fr);gap:16px}.ultimate-settings{padding:24px;display:grid;gap:14px}.ultimate-settings label{display:grid;gap:6px;color:var(--muted);font-size:10px;font-weight:700}.ultimate-settings select,.ultimate-settings input[type=text],#ultimate-ota-file{width:100%;min-height:44px;padding:0 11px;border:1px solid var(--line);border-radius:11px;color:var(--text);background:#0d1117}.ultimate-settings .toggle-row{grid-template-columns:auto 1fr;align-items:center}.ultimate-settings .toggle-row input{width:20px;height:20px;accent-color:var(--green)}.ultimate-phrases{display:grid;grid-template-columns:1fr 1fr;gap:8px}.ultimate-actions{display:grid;gap:16px}.ultimate-actions .action-card strong{display:block;margin-top:10px;color:var(--blue);font-size:11px}.ultimate-actions input[type=file]{padding:10px;height:auto;font-size:10px} +@keyframes ultimate-beam{0%,100%{transform:translateX(-80px) rotate(18deg);opacity:.25}50%{transform:translateX(180px) rotate(18deg);opacity:1}} + +.more-grid { display: grid; grid-template-columns: minmax(260px, .7fr) minmax(0, 1.3fr); gap: 16px; } +.device-details { margin-top: 16px; } +.device-card { min-height: 480px; padding: 28px; display: flex; flex-direction: column; align-items: center; text-align: center; } +.device-icon { width: 102px; height: 102px; margin: 40px 0 26px; display: grid; place-items: center; border-radius: 30px; background: radial-gradient(circle, rgba(68, 240, 138, .15), rgba(68, 240, 138, .035)); box-shadow: inset 0 0 30px rgba(68, 240, 138, .06); } +.device-icon .brand-mark { width: 50px; height: 50px; } +.device-card h2 { margin: 0; font-size: 31px; letter-spacing: -.05em; }.device-card > p:not(.eyebrow) { margin: 7px 0 0; color: var(--muted); font-size: 11px; } +.device-card dl { width: 100%; margin: auto 0 0; } +.device-card dl div { padding: 12px 0; display: flex; justify-content: space-between; gap: 16px; border-top: 1px solid var(--line); font-size: 10px; } +.device-card dt { color: var(--muted); }.device-card dd { margin: 0; max-width: 62%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; } +.settings-stack { display: grid; gap: 16px; } +.action-card, .about-card { min-height: 145px; padding: 25px; display: flex; justify-content: space-between; align-items: center; gap: 22px; } +.action-card p:not(.eyebrow), .about-card p:not(.eyebrow) { max-width: 480px; margin: 8px 0 0; color: var(--muted); font-size: 11px; line-height: 1.6; } +.primary-button, .secondary-button { position: relative; z-index: 2; min-width: 112px; min-height: 44px; padding: 0 16px; flex: 0 0 auto; border-radius: 13px; font-size: 11px; font-weight: 780; } +.primary-button { color: #06150b; background: var(--green); box-shadow: 0 0 20px rgba(68, 240, 138, .18); } +.secondary-button { border: 1px solid var(--line); color: var(--text); background: #151a21; } +.danger-button { color: var(--red); border-color: rgba(255, 84, 104, .28); } +.primary-button:disabled, .secondary-button:disabled { opacity: .45; cursor: wait; } +.action-buttons { display: flex; flex: 0 0 auto; gap: 8px; } + +.network-dialog { width: min(540px, calc(100vw - 28px)); max-height: min(760px, calc(100dvh - 28px)); padding: 25px; overflow-y: auto; border: 1px solid #2a3340; border-radius: 24px; color: var(--text); background: linear-gradient(145deg, #131820, #090c10); box-shadow: 0 30px 100px rgba(0, 0, 0, .72); } +.network-dialog::backdrop { background: rgba(0, 0, 0, .72); backdrop-filter: blur(8px); } +.dialog-head { display: flex; align-items: start; justify-content: space-between; gap: 16px; }.dialog-head h2 { margin: 5px 0 0; font-size: 27px; letter-spacing: -.04em; } +.dialog-close { width: 44px; height: 44px; flex: 0 0 auto; border-radius: 14px; color: var(--muted); background: #191f27; font-size: 25px; line-height: 1; } +.network-current { margin: 21px 0; padding: 14px; display: flex; align-items: center; gap: 11px; border: 1px solid var(--line); border-radius: 14px; background: #0d1117; }.network-current div { min-width: 0; display: grid; gap: 3px; }.network-current small { color: var(--muted); font-size: 10px; letter-spacing: .1em; }.network-current strong { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 12px; } +.network-dialog form { display: grid; gap: 9px; }.network-dialog label { margin-top: 4px; font-size: 12px; font-weight: 700; }.network-dialog input { width: 100%; min-height: 48px; padding: 0 13px; border: 1px solid var(--line); border-radius: 13px; background: #0d1117; font-size: 16px; } +.password-row { display: grid; grid-template-columns: 1fr auto; gap: 8px; }.password-row button { min-width: 62px; min-height: 48px; border-radius: 13px; color: var(--blue); background: rgba(66, 165, 255, .1); font-size: 12px; font-weight: 750; } +.field-help { margin: 2px 0 6px; color: var(--muted); font-size: 11px; line-height: 1.5; }.dialog-actions { margin-top: 7px; display: flex; justify-content: flex-end; gap: 8px; } +.network-result { padding-top: 5px; }.network-result h3 { margin: 7px 0 10px; font-size: 22px; }.network-result ol { padding-left: 22px; color: var(--text); font-size: 13px; line-height: 1.65; }.network-result p:not(.eyebrow) { color: var(--muted); font-size: 12px; line-height: 1.55; }.network-result > button { width: 100%; margin-top: 8px; } + +.toast-stack { position: fixed; z-index: 80; right: max(18px, env(safe-area-inset-right)); bottom: max(18px, env(safe-area-inset-bottom)); width: min(340px, calc(100vw - 36px)); display: grid; gap: 9px; pointer-events: none; } +.toast { padding: 13px 15px; display: grid; grid-template-columns: 8px 1fr; gap: 11px; align-items: center; border: 1px solid var(--line); border-radius: 15px; background: rgba(18, 22, 28, .96); box-shadow: 0 14px 45px rgba(0, 0, 0, .45); backdrop-filter: blur(15px); animation: toast-in .25s ease both; font-size: 11px; } +.toast i { width: 8px; height: 8px; border-radius: 50%; background: var(--green); box-shadow: 0 0 10px var(--green); }.toast.warn i { background: var(--yellow); box-shadow: 0 0 10px var(--yellow); }.toast.error i { background: var(--red); box-shadow: 0 0 10px var(--red); } +.toast.out { animation: toast-out .2s ease forwards; } +@keyframes toast-in { from { opacity: 0; transform: translateY(8px) scale(.98); } } +@keyframes toast-out { to { opacity: 0; transform: translateY(5px); } } + +.boot { position: fixed; z-index: 100; inset: 0; display: grid; place-content: center; justify-items: center; gap: 14px; background: #050608; transition: opacity .35s ease, visibility .35s ease; } +.boot.hidden { opacity: 0; visibility: hidden; } +.boot strong { margin-top: 9px; font-size: 25px; letter-spacing: -.04em; }.boot small { color: var(--muted); font-size: 10px; } +.boot-line { width: 110px; height: 2px; margin-top: 8px; overflow: hidden; border-radius: 2px; background: #18201c; }.boot-line i { display: block; width: 45%; height: 100%; background: var(--green); box-shadow: 0 0 12px var(--green); animation: boot-scan 1.05s ease-in-out infinite; } +@keyframes boot-scan { from { transform: translateX(-110%); } to { transform: translateX(245%); } } + +@media (max-width: 980px) { + .shell { grid-template-columns: 184px minmax(0, 1fr); }.sidebar { width: 184px; }.hero { grid-template-columns: 1fr 180px; }.radio-grid { grid-template-columns: 1fr; }.signal-card { grid-row: auto; }.packet-card { grid-column: 1 / -1; } +} + +@media (max-width: 760px) { + body { padding-bottom: calc(76px + env(safe-area-inset-bottom)); } + .shell { display: block; }.sidebar { inset: auto 0 0; width: auto; height: calc(65px + env(safe-area-inset-bottom)); padding: 0 8px env(safe-area-inset-bottom); border: 0; border-top: 1px solid rgba(255, 255, 255, .08); background: rgba(7, 9, 12, .92); } + .brand, .sidebar-foot { display: none; }.nav { height: 64px; display: grid; grid-template-columns: repeat(5, 1fr); gap: 1px; } + .nav button { min-height: 58px; padding: 7px 3px 5px; flex-direction: column; justify-content: center; gap: 3px; border-radius: 11px; text-align: center; } + .nav button.active { box-shadow: inset 0 2px var(--green); background: linear-gradient(180deg, rgba(68, 240, 138, .1), transparent); }.nav button span { font-size: 10px; }.nav button svg { width: 20px; height: 20px; }.nav button b { position: absolute; top: 4px; left: calc(50% + 7px); min-width: 17px; height: 17px; padding: 0 4px; font-size: 8px; } + main { width: 100%; padding: max(18px, env(safe-area-inset-top)) 14px 24px; }.topbar { min-height: 54px; margin-bottom: 14px; }.topbar h1 { font-size: 28px; }.battery, .link-chip { height: 44px; padding: 0 9px; }.battery { font-size: 10px; } + section[data-screen="home"].active { display: flex; flex-direction: column; } + .hero { min-height: 245px; padding: 24px; grid-template-columns: 1fr 105px; }.hero-copy h2 { font-size: clamp(36px, 12vw, 56px); }.mesh-orbit { width: 100px; }.mesh-orbit::before { inset: 14px; }.mesh-orbit::after { inset: 33px; }.mesh-orbit i:nth-child(1) { top: 9px; left: 49px; }.mesh-orbit i:nth-child(2) { right: 13px; bottom: 20px; }.mesh-orbit i:nth-child(3) { left: 8px; bottom: 33px; }.hero-stats { gap: 25px; overflow: hidden; }.hero-stats strong { font-size: 11px; } + .network-banner { grid-template-columns: 44px 1fr; }.network-banner .primary-button { grid-column: 1 / -1; width: 100%; }.dashboard-grid { order: -1; margin: 0 0 12px; grid-template-columns: 1fr; gap: 12px; }.feature-card { min-height: 156px; padding: 18px; }.feature-card .preview { margin: 12px 0 6px; }.radio-strip { grid-column: auto; grid-template-columns: 1fr; gap: 13px; }.radio-meta { justify-content: flex-start; } + .dashboard-activity { grid-column: auto; padding: 18px; }.dashboard-health { grid-column: auto; grid-template-columns: 1fr 1fr; }.health-tile { min-height: 98px; }.dashboard-analytics { grid-column: auto; grid-template-columns: 1fr; gap: 12px; }.analytics-card { min-height: 220px; padding: 18px; }.nearby-bar { grid-template-columns: minmax(80px,1fr) minmax(80px,1fr) 62px; } + .messages-layout { height: calc(100dvh - 153px); min-height: 520px; grid-template-columns: 1fr; }.threads-panel { display: none; }.conversation-head { min-height: 61px; padding: 10px 14px; }.message-list { padding: 15px 12px; }.bubble { max-width: 88%; }.composer { padding: 9px 10px max(10px, env(safe-area-inset-bottom)); } + .nearby-grid { grid-template-columns: 1fr; }.nearby-card { min-height: 112px; }.mesh-map-card { min-height: 360px; }.mesh-map-card #mesh-map { height: 350px; }.map-badge { top: 10px; right: 10px; }.map-switch { width: 100%; }.map-switch button { flex: 1; } + .radio-grid { gap: 12px; }.signal-card { padding: 20px; }.signal-readout { margin-top: 24px; }.metric { min-height: 112px; padding: 16px; }.packet-card { padding: 19px; }.packet-stats { grid-template-columns: 1fr 1fr; gap: 18px; } + .ultimate-hero{min-height:210px;padding:22px;grid-template-columns:1fr 95px}.ultimate-gate{width:90px;height:90px}.ultimate-gate small{display:none}.ultimate-metrics{grid-template-columns:1fr 1fr}.ultimate-grid,.ultimate-settings-grid{grid-template-columns:1fr}.ultimate-chart-card,.ultimate-nodes,.ultimate-history{padding:18px}.ultimate-phrases{grid-template-columns:1fr} + .more-grid { grid-template-columns: 1fr; }.device-card { min-height: 410px; }.action-card, .about-card { align-items: flex-start; flex-direction: column; }.action-buttons { width: 100%; flex-direction: column; }.primary-button, .secondary-button { width: 100%; }.toast-stack { bottom: calc(78px + env(safe-area-inset-bottom)); }.network-dialog { padding: 20px; }.dialog-actions { flex-direction: column-reverse; }.dialog-actions button { width: 100%; } +} + +@media (max-width: 390px) { + .hero { grid-template-columns: 1fr; }.mesh-orbit { display: none; }.hero-stats { justify-content: space-between; gap: 12px; }.metric-grid { grid-template-columns: 1fr 1fr; }.metric strong { font-size: 23px; } +} + +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { scroll-behavior: auto !important; animation-duration: .01ms !important; animation-iteration-count: 1 !important; transition-duration: .01ms !important; } +} diff --git a/examples/companion_radio/webui/verify.mjs b/examples/companion_radio/webui/verify.mjs new file mode 100644 index 00000000..807b3cd5 --- /dev/null +++ b/examples/companion_radio/webui/verify.mjs @@ -0,0 +1,50 @@ +import { gunzipSync } from "node:zlib"; +import { readFile } from "node:fs/promises"; + +const header = await readFile(new URL("./Rcc6WebUiAssets.h", import.meta.url), "utf8"); +const appSource = await readFile(new URL("./src/app.js", import.meta.url), "utf8"); +const body = header.match(/RCC6_WEB_UI_INDEX_GZ\[\] PROGMEM = \{([\s\S]*?)\n\};/); +if (!body) throw new Error("Generated gzip array is missing"); +const bytes = Uint8Array.from([...body[1].matchAll(/0x([0-9a-f]{2})/g)], (match) => Number.parseInt(match[1], 16)); +const html = gunzipSync(bytes).toString("utf8"); + +for (const required of [ + "Overview", "Messages", "What this radio can hear", "Radio", "This radio", "/api/frame", "/api/network", + "X-RCC6-Session", "application/x-www-form-urlencoded", "Set up local Wi-Fi", + "meshcore", "localStorage", "rcc6-radio-owner", "rcc6-history-pending-", "syncNextMessage", "MeshCore.js", + "NeonPocketMC", "brand-pocket", + "NeonPocket Ultimate Companion", "LAST 24 HOURS", "Advertised places", "MEMORY HEADROOM", + "SIGNAL SAMPLES", "Freshest nearby nodes", "Hop view", + "/api/ultimate/status", "/api/ultimate/history", "/api/ultimate/export", + "/api/ultimate/settings", "/api/ultimate/location", "/api/ultimate/ota", + "SIGNED UPDATE", "NeonPocket settings", "BATTERY TREND", "ultimate-location-share", + "LATEST DELIVERY", "Power profile", "Battery capacity", "Battery calibration offset", +]) { + if (!html.includes(required)) throw new Error(`Built UI is missing ${required}`); +} +if (!header.includes("extern const size_t RCC6_WEB_UI_INDEX_GZ_LEN;")) throw new Error("Length declaration is missing"); +if (/]*src=|]*rel=[\"']stylesheet/i.test(html)) throw new Error("External runtime asset detected"); +if (html.includes("location.reload")) throw new Error("Reload-based transport recovery detected"); +if (html.includes("navigator.locks")) throw new Error("Secure-context Web Locks dependency detected"); +if (appSource.includes(".getWaitingMessages(")) throw new Error("Destructive batch message drain detected"); +for (const required of ["this.drainTask = this.drainRetainedFrames()", "if (!this.ready) throw"]) { + if (!appSource.includes(required)) throw new Error(`Takeover drain guard is missing ${required}`); +} +for (const required of [ + "async function refreshUltimate()", "function drawUltimateChart(", + "function drawActivityBars(", "function drawMeshMap(", "function advertisedLocation(", + 'ultimateFetch("/api/ultimate/settings"', 'fetch("/api/ultimate/ota"', + "navigator.geolocation.getCurrentPosition", "batteryTrendMvPerHour", "advertise }),", + "batteryRuntimeMinutes", "batteryCapacityMah", "usbHostConnected", "ultimate.delivery", + 'split("\\0", 1)', +]) { + if (!appSource.includes(required)) throw new Error(`Ultimate WebUI behavior is missing ${required}`); +} +for (const required of ["raw & 0xff", "packed === 0xff", "packed & 0x3f", "route unknown"]) { + if (!appSource.includes(required)) throw new Error(`Mesh route decoding is missing ${required}`); +} +const decodeHopCount = (rawPath) => ((Number(rawPath) & 0xff) === 0xff ? null : Number(rawPath) & 0x3f); +for (const [rawPath, expected] of [[-1, null], [255, null], [0x80, 0], [0x81, 1], [0xc2, 2]]) { + if (decodeHopCount(rawPath) !== expected) throw new Error(`Mesh route decode failed for ${rawPath}`); +} +console.log(`Verified ${bytes.length}-byte NeonPocket Ultimate WebUI gzip asset`); diff --git a/scripts/ultimate_build_flags.py b/scripts/ultimate_build_flags.py new file mode 100644 index 00000000..97088294 --- /dev/null +++ b/scripts/ultimate_build_flags.py @@ -0,0 +1,22 @@ +"""Embed exact Git provenance in Ultimate firmware without changing source files.""" + +import os +from pathlib import Path +import subprocess + +Import("env") + +sha = os.environ.get("ULTIMATE_BUILD_SHA") +if not sha: + sha = subprocess.check_output( + ["git", "rev-parse", "HEAD"], cwd=env.subst("$PROJECT_DIR"), text=True + ).strip() +if len(sha) != 40 or any(char not in "0123456789abcdefABCDEF" for char in sha): + raise RuntimeError("ULTIMATE_BUILD_SHA must be an exact 40-character Git SHA") +build_dir = Path(env.subst("$BUILD_DIR")) +build_dir.mkdir(parents=True, exist_ok=True) +(build_dir / "ultimate_build_sha.h").write_text( + '#pragma once\n#define ULTIMATE_BUILD_SHA "%s"\n' % sha.lower(), + encoding="ascii", +) +env.Prepend(CPPPATH=[str(build_dir)]) diff --git a/scripts/verify_ultimate_mono.py b/scripts/verify_ultimate_mono.py new file mode 100644 index 00000000..339d87b2 --- /dev/null +++ b/scripts/verify_ultimate_mono.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +"""Static contract for the Heltec OLED Ultimate companion targets.""" + +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] + + +def text(path: str) -> str: + return (ROOT / path).read_text(encoding="utf-8") + + +board = "" +platform_path = "" +for candidate in ("v3", "v4"): + candidate_path = f"variants/heltec_{candidate}/platformio.ini" + candidate_text = text(candidate_path) + if f"[env:heltec_{candidate}_ultimate_companion_ble]" in candidate_text: + board = candidate + platform_path = candidate_path + break +if not board: + raise SystemExit("Ultimate verifier supports only Heltec V3/V4 repositories") + +platform = text(platform_path) +main = text("examples/companion_radio/main.cpp") +mesh_h = text("examples/companion_radio/MyMesh.h") +mesh_cpp = text("examples/companion_radio/MyMesh.cpp") +service_h = text("examples/companion_radio/UltimateService.h") +service_cpp = text("examples/companion_radio/UltimateService.cpp") +web = text("examples/companion_radio/UltimateWebApi.cpp") +ui = text("examples/companion_radio/ui-new/UITask.cpp") +asset = text("examples/companion_radio/webui/Rcc6WebUiAssets.h") + +ble_env = f"[env:heltec_{board}_ultimate_companion_ble]" +web_env = f"[env:heltec_{board}_ultimate_companion_web]" +ota_target = f"-D NEONPOCKET_ULTIMATE_OTA_TARGET='\"heltec_{board}\"'" + +checks = { + "Ultimate BLE target": ble_env in platform, + "Ultimate Web target": web_env in platform, + "MeshCore 1.17.1": "-D FIRMWARE_VERSION='\"v1.17.1\"'" in platform, + "mutually exclusive transports": platform.index(ble_env) != platform.index(web_env) + and "-D BLE_PIN_CODE=123456" in platform[platform.index(ble_env):platform.index(web_env)] + and "-D RCC6_WEB_AP=1" in platform[platform.index(web_env):], + "OLED NeonPocket renderer": "-D NEONPOCKET_MONO_UI=1" in platform, + "32 KiB runtime gate": "-D NEONPOCKET_MEMORY_GATE_BYTES=32768" in platform, + "capacity profile": all(token in platform for token in ( + "-D MAX_CONTACTS=350", "-D MAX_GROUP_CHANNELS=40", "-D OFFLINE_QUEUE_SIZE=256")), + "target-specific OTA marker": ota_target in platform, + "fail-closed storage": "beginSpiffsPreservingData()" in main + and "Ultimate storage initialization failed; data preserved" in main, + "service lifecycle": "ultimate_service.begin(SPIFFS" in main + and "ultimate_service.loop();" in main, + "Web lifecycle": "web_interface.begin(" in main and "ultimate_web_api.begin(" in main, + "256-byte journal": "static_assert(sizeof(UltimateHistoryRecord) == 256" in service_h, + "bounded event and network caches": "pending_capacity = 32" in service_h + and "network_capacity = 64" in service_h, + "history retention choices": "capacity == 128 || capacity == 512 || capacity == 2048" in service_cpp, + "message composer": "sendUltimateDirect" in mesh_h and "sendUltimateChannel" in mesh_h + and "UltimateComposerState" in service_h, + "safe hop decoding": "encoded_path_len & 0x3F" in service_cpp + and "encoded_path_len & 0x3F" in mesh_cpp, + "OLED Network and Ultimate pages": "HomePage::NETWORK" in ui and "HomePage::ULTIMATE" in ui, + "flood advert action": "the_mesh.advert(" in ui and "true" in ui, + "authenticated Ultimate APIs": "/api/ultimate/status" in web + and "/api/ultimate/history" in web and "/api/ultimate/location" in web, + "generated embedded WebUI": "RCC6_WEB_UI_INDEX_GZ[] PROGMEM" in asset, +} + +failed = [name for name, ok in checks.items() if not ok] +if failed: + raise SystemExit("Ultimate OLED contract failed: " + ", ".join(failed)) +print(f"Heltec {board.upper()} Ultimate OLED contract verified") diff --git a/src/helpers/esp32/SerialWebInterface.cpp b/src/helpers/esp32/SerialWebInterface.cpp new file mode 100644 index 00000000..2d5aa63f --- /dev/null +++ b/src/helpers/esp32/SerialWebInterface.cpp @@ -0,0 +1,766 @@ +#ifdef RCC6_WEB_AP + +#include "SerialWebInterface.h" +#include +#include +#include +#include +#define RCC6_WEB_UI_ASSETS_IMPLEMENTATION +#include + +#include +#include + +namespace { +constexpr char PASSWORD_ALPHABET[] = "abcdefghjkmnpqrstuvwxyz"; +constexpr char HTTP_SESSION_HEADER[] = "X-RCC6-Session"; +constexpr char HTTP_ACK_HEADER[] = "X-RCC6-Ack"; +constexpr char HTTP_SEQUENCE_HEADER[] = "X-RCC6-Seq"; +constexpr char HTTP_AUTH_USER[] = "meshcore"; + +bool isSafeSsidChar(char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || c == '-' || c == '_'; +} + +bool isAllowedHttpOpcode(uint8_t opcode) { + switch (opcode) { + case 1: // AppStart + case 2: // SendTxtMsg + case 3: // SendChannelTxtMsg + case 4: // GetContacts + case 7: // SendSelfAdvert + case 10: // SyncNextMessage + case 20: // GetBatteryVoltage + case 22: // DeviceQuery + case 31: // GetChannel + case 56: // GetStats + return true; + default: + return false; + } +} + +String jsonEscape(const char* value) { + String escaped; + if (value == nullptr) return escaped; + escaped.reserve(strlen(value) + 8); + constexpr char HEX_DIGITS[] = "0123456789abcdef"; + for (const uint8_t* p = reinterpret_cast(value); *p != 0; p++) { + if (*p == '"' || *p == '\\') { + escaped += '\\'; + escaped += static_cast(*p); + } else if (*p < 0x20) { + escaped += "\\u00"; + escaped += HEX_DIGITS[*p >> 4]; + escaped += HEX_DIGITS[*p & 0x0f]; + } else { + escaped += static_cast(*p); + } + } + return escaped; +} +} + +void SerialWebInterface::FrameQueue::clear() { + head = 0; + count = 0; +} + +bool SerialWebInterface::FrameQueue::full() const { + return count == FRAME_QUEUE_SIZE; +} + +bool SerialWebInterface::FrameQueue::empty() const { + return count == 0; +} + +bool SerialWebInterface::FrameQueue::push(const uint8_t* data, size_t len) { + if (data == nullptr || len < 1 || len > MAX_FRAME_SIZE || full()) return false; + + Frame& frame = frames[(head + count) % FRAME_QUEUE_SIZE]; + frame.len = static_cast(len); + memcpy(frame.data, data, len); + count++; + return true; +} + +SerialWebInterface::Frame* SerialWebInterface::FrameQueue::front() { + return empty() ? nullptr : &frames[head]; +} + +void SerialWebInterface::FrameQueue::pop() { + if (empty()) return; + head = (head + 1) % FRAME_QUEUE_SIZE; + count--; +} + +size_t SerialWebInterface::FrameQueue::pop(uint8_t* dest) { + Frame* frame = front(); + if (frame == nullptr || dest == nullptr) return 0; + + const size_t len = frame->len; + memcpy(dest, frame->data, len); + pop(); + return len; +} + +SerialWebInterface::SerialWebInterface() + : _initialized(false), _enabled(false), _routes_registered(false), + _tcp_connected(false), _tcp_port(5000), _ap_ssid{}, _ap_password{}, + _prefer_station(false), _station_active(false), _fallback_active(false), + _station_ssid{}, _station_password{}, _current_ip(), _restart_at(0), + _tcp_server(), _tcp_client(), _http_server(80), _session(Session::NONE), + _http_remote(), _http_session_id{}, _http_last_activity(0), + _http_inflight_seq(0), _http_next_seq(1), _recv_queue{}, _send_queue{}, + _tcp_rx_state(TcpRxState::MARKER), _tcp_rx_length(0), _tcp_rx_offset(0), + _tcp_rx_data{}, _tcp_tx_data{}, _tcp_tx_length(0), _tcp_tx_offset(0), + _http_post_result(HttpPostResult::NONE), _http_post_expected(0), + _http_post_length(0), _http_post_data{} { + _recv_queue.clear(); + _send_queue.clear(); +} + +void SerialWebInterface::begin(const char* node_name, uint16_t tcp_port) { + _tcp_port = tcp_port == 0 ? 5000 : tcp_port; + configureIdentity(node_name); + registerHttpRoutes(); + _initialized = true; +} + +IPAddress SerialWebInterface::getApIP() const { + return IPAddress(192, 168, 4, 1); +} + +const char* SerialWebInterface::getCurrentSsid() const { + return _station_active ? _station_ssid : _ap_ssid; +} + +void SerialWebInterface::configureIdentity(const char* node_name) { + char suffix[24] = {}; + size_t out = 0; + bool last_was_separator = false; + + if (node_name != nullptr) { + for (size_t i = 0; node_name[i] != '\0' && out < sizeof(suffix) - 1; i++) { + const char c = node_name[i]; + if (isSafeSsidChar(c)) { + suffix[out++] = c; + last_was_separator = false; + } else if (out > 0 && !last_was_separator) { + suffix[out++] = '-'; + last_was_separator = true; + } + } + } + while (out > 0 && suffix[out - 1] == '-') out--; + suffix[out] = '\0'; + if (out == 0) strcpy(suffix, "RCC6"); + snprintf(_ap_ssid, sizeof(_ap_ssid), "MeshCore-%s", suffix); + + Preferences preferences; + String password; + String station_ssid; + String station_password; + const bool opened = preferences.begin("rcc6-web", false); + if (opened) { + if (preferences.isKey("ap-pass")) password = preferences.getString("ap-pass", ""); + if (preferences.isKey("sta-mode")) _prefer_station = preferences.getBool("sta-mode", false); + if (preferences.isKey("sta-ssid")) station_ssid = preferences.getString("sta-ssid", ""); + if (preferences.isKey("sta-pass")) station_password = preferences.getString("sta-pass", ""); + } + + if (password.length() != sizeof(_ap_password) - 1) { + for (size_t i = 0; i < sizeof(_ap_password) - 1; i++) { + _ap_password[i] = PASSWORD_ALPHABET[esp_random() % (sizeof(PASSWORD_ALPHABET) - 1)]; + } + _ap_password[sizeof(_ap_password) - 1] = '\0'; + if (opened) preferences.putString("ap-pass", _ap_password); + } else { + password.toCharArray(_ap_password, sizeof(_ap_password)); + } + + const bool valid_station = station_ssid.length() >= 1 && station_ssid.length() <= 32 && + (station_password.length() == 0 || + (station_password.length() >= 8 && station_password.length() <= 64)); + if (valid_station) { + station_ssid.toCharArray(_station_ssid, sizeof(_station_ssid)); + station_password.toCharArray(_station_password, sizeof(_station_password)); + } else { + _prefer_station = false; + _station_ssid[0] = '\0'; + _station_password[0] = '\0'; + } + + if (opened) preferences.end(); +} + +void SerialWebInterface::registerHttpRoutes() { + if (_routes_registered) return; + + static const char* collected_headers[] = { + HTTP_SESSION_HEADER, HTTP_ACK_HEADER +#ifdef NEONPOCKET_ULTIMATE + , "X-NP-Confirm" +#endif + }; + _http_server.collectHeaders(collected_headers, + sizeof(collected_headers) / sizeof(collected_headers[0])); + _http_server.on("/", HTTP_GET, [this]() { handleIndex(); }); + _http_server.on("/index.html", HTTP_GET, [this]() { handleIndex(); }); + _http_server.on("/api/frame", HTTP_GET, [this]() { handleHttpGetFrame(); }); + _http_server.on("/api/frame", HTTP_POST, + [this]() { handleHttpPostFrame(); }, + [this]() { handleHttpPostRaw(); }); + _http_server.on("/api/network", HTTP_GET, [this]() { handleHttpGetNetwork(); }); + _http_server.on("/api/network", HTTP_POST, [this]() { handleHttpPostNetwork(); }); + _http_server.onNotFound([this]() { + if (httpAuthorized()) sendHttpStatus(404); + }); + _routes_registered = true; +} + +bool SerialWebInterface::startAccessPoint() { + WiFi.mode(WIFI_AP); + const IPAddress ap_ip = getApIP(); + if (!WiFi.softAPConfig(ap_ip, ap_ip, IPAddress(255, 255, 255, 0)) || + !WiFi.softAP(_ap_ssid, _ap_password, 1, false, 1)) { + WiFi.softAPdisconnect(true); + Serial.println("Web AP: failed to start"); + return false; + } + + _station_active = false; + _current_ip = ap_ip; + return true; +} + +void SerialWebInterface::startServers() { + _tcp_server.begin(_tcp_port); + _tcp_server.setNoDelay(true); + _http_server.begin(); + _enabled = true; +} + +void SerialWebInterface::enable() { + if (_enabled || !_initialized) return; + + clearSession(); + _station_active = false; + _fallback_active = false; + _current_ip = IPAddress(); + + if (_prefer_station && _station_ssid[0] != '\0') { + WiFi.mode(WIFI_STA); + WiFi.setAutoReconnect(true); + WiFi.begin(_station_ssid, _station_password); + const uint32_t started = millis(); + while (WiFi.status() != WL_CONNECTED && + static_cast(millis() - started) < STA_CONNECT_TIMEOUT_MS) { + delay(50); + } + + const IPAddress station_ip = WiFi.localIP(); + const bool has_ip = station_ip[0] != 0 || station_ip[1] != 0 || + station_ip[2] != 0 || station_ip[3] != 0; + if (WiFi.status() == WL_CONNECTED && has_ip) { + _station_active = true; + _current_ip = station_ip; + startServers(); + Serial.printf("Web WiFi: %s at %s (HTTP, TCP/%u)\n", + _station_ssid, station_ip.toString().c_str(), _tcp_port); + return; + } + + Serial.printf("Web WiFi: failed to join %s; starting setup AP\n", _station_ssid); + WiFi.disconnect(true, false); + if (!saveNetworkConfig(false, String(), String())) { + _prefer_station = false; + _station_ssid[0] = '\0'; + _station_password[0] = '\0'; + Serial.println("Web WiFi: failed to persist setup AP fallback"); + } + _fallback_active = true; + } + + if (!startAccessPoint()) return; + startServers(); + Serial.printf("Web AP: %s at %s (HTTP, TCP/%u)%s\n", + _ap_ssid, _current_ip.toString().c_str(), _tcp_port, + _fallback_active ? " [station fallback]" : ""); +} + +void SerialWebInterface::disable() { + if (!_enabled) return; + + _enabled = false; + _http_server.stop(); + _tcp_server.end(); + clearSession(); + if (_station_active) { + WiFi.disconnect(true, false); + } else { + WiFi.softAPdisconnect(true); + } + _station_active = false; + _fallback_active = false; + _current_ip = IPAddress(); +} + +bool SerialWebInterface::isConnected() const { + if (!_enabled) return false; + if (_session == Session::TCP) return _tcp_connected; + if (_session == Session::HTTP) { + return static_cast(millis() - _http_last_activity) < HTTP_SESSION_TIMEOUT_MS; + } + return false; +} + +bool SerialWebInterface::isWriteBusy() const { + return static_cast(_recv_queue.count) + _send_queue.count >= FRAME_QUEUE_SIZE; +} + +size_t SerialWebInterface::writeFrame(const uint8_t src[], size_t len) { + if (!isConnected() || isWriteBusy() || src == nullptr || len < 1 || len > MAX_FRAME_SIZE) return 0; + return _send_queue.push(src, len) ? len : 0; +} + +size_t SerialWebInterface::checkRecvFrame(uint8_t dest[]) { + if (_restart_at != 0 && static_cast(millis() - _restart_at) >= 0) { + ESP.restart(); + } + if (!_enabled) return 0; + if (_station_active && WiFi.status() == WL_CONNECTED) _current_ip = WiFi.localIP(); + + expireHttpSession(); + _http_server.handleClient(); + serviceTcp(); + return _recv_queue.pop(dest); +} + +bool SerialWebInterface::saveNetworkConfig(bool station, const String& ssid, + const String& password) { + Preferences preferences; + if (!preferences.begin("rcc6-web", false)) return false; + + // Disable station preference first so a partial NVS write always boots the setup AP. + bool saved = preferences.putBool("sta-mode", false) == sizeof(bool); + if (station && saved) { + saved = preferences.putString("sta-ssid", ssid) == ssid.length(); + if (password.length() == 0) { + if (preferences.isKey("sta-pass")) saved = preferences.remove("sta-pass") && saved; + } else { + saved = preferences.putString("sta-pass", password) == password.length() && saved; + } + } else if (!station && saved) { + if (preferences.isKey("sta-ssid")) saved = preferences.remove("sta-ssid") && saved; + if (preferences.isKey("sta-pass")) saved = preferences.remove("sta-pass") && saved; + } + if (station && saved) saved = preferences.putBool("sta-mode", true) == sizeof(bool); + preferences.end(); + + if (saved) { + _prefer_station = station; + if (station) { + ssid.toCharArray(_station_ssid, sizeof(_station_ssid)); + password.toCharArray(_station_password, sizeof(_station_password)); + } else { + _station_ssid[0] = '\0'; + _station_password[0] = '\0'; + } + } + return saved; +} + +void SerialWebInterface::scheduleRestart() { + _restart_at = millis() + RESTART_DELAY_MS; + if (_restart_at == 0) _restart_at = 1; +} + +bool SerialWebInterface::selectSetupAp() { + if (!saveNetworkConfig(false, String(), String())) return false; + scheduleRestart(); + return true; +} + +bool SerialWebInterface::clearStoredNetworkConfig() { + Preferences preferences; + if (!preferences.begin("rcc6-web", false)) return false; + const bool cleared = preferences.clear(); + preferences.end(); + if (cleared) { + WiFi.disconnect(true, true); + _prefer_station = false; + _station_ssid[0] = '\0'; + _station_password[0] = '\0'; + } + return cleared; +} + +void SerialWebInterface::clearSession() { + if (_tcp_client) _tcp_client.stop(); + _tcp_client = WiFiClient(); + _tcp_connected = false; + _session = Session::NONE; + _http_remote = IPAddress(); + _http_session_id[0] = '\0'; + _http_last_activity = 0; + _http_inflight_seq = 0; + _recv_queue.clear(); + _send_queue.clear(); + _tcp_tx_length = 0; + _tcp_tx_offset = 0; + _http_post_result = HttpPostResult::NONE; + _http_post_expected = 0; + _http_post_length = 0; + resetTcpParser(); +} + +void SerialWebInterface::resetTcpParser() { + _tcp_rx_state = TcpRxState::MARKER; + _tcp_rx_length = 0; + _tcp_rx_offset = 0; +} + +void SerialWebInterface::expireHttpSession() { + if (_session == Session::HTTP && + static_cast(millis() - _http_last_activity) >= HTTP_SESSION_TIMEOUT_MS && + _recv_queue.empty() && _send_queue.empty() && _http_inflight_seq == 0) { + clearSession(); + } +} + +bool SerialWebInterface::httpSessionTokenValid(const String& token) const { + if (token.length() != 32) return false; + for (size_t i = 0; i < 32; i++) { + const char c = token[i]; + if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || + (c >= 'A' && c <= 'F'))) return false; + } + return true; +} + +bool SerialWebInterface::claimHttpSession() { + expireHttpSession(); + const IPAddress remote = _http_server.client().remoteIP(); + const String token = _http_server.header(HTTP_SESSION_HEADER); + + if (!httpSessionTokenValid(token)) return false; + + if (_session == Session::TCP) { + if (_tcp_client.connected()) return false; + clearSession(); + } + if (_session == Session::HTTP && + (_http_remote != remote || strncmp(_http_session_id, token.c_str(), 32) != 0)) { + if (static_cast(millis() - _http_last_activity) < HTTP_SESSION_TIMEOUT_MS) { + return false; + } + // The old controller is gone. Preserve outbound/in-flight responses for the + // new browser to drain, but discard commands the abandoned controller queued. + _recv_queue.clear(); + _http_post_result = HttpPostResult::NONE; + _http_post_expected = 0; + _http_post_length = 0; + _http_remote = remote; + token.toCharArray(_http_session_id, sizeof(_http_session_id)); + } + if (_session == Session::NONE) { + _session = Session::HTTP; + _http_remote = remote; + token.toCharArray(_http_session_id, sizeof(_http_session_id)); + } + _http_last_activity = millis(); + return true; +} + +bool SerialWebInterface::acknowledgeHttpFrame() { + if (_http_inflight_seq == 0) return false; + const String value = _http_server.header(HTTP_ACK_HEADER); + if (value.length() < 1 || value.length() > 10) return false; + + char* end = nullptr; + const unsigned long ack = strtoul(value.c_str(), &end, 10); + if (end == value.c_str() || *end != '\0' || ack != _http_inflight_seq) return false; + + _send_queue.pop(); + _http_inflight_seq = 0; + return true; +} + +uint32_t SerialWebInterface::nextHttpSequence() { + uint32_t sequence = _http_next_seq++; + if (sequence == 0) sequence = _http_next_seq++; + return sequence; +} + +void SerialWebInterface::rejectTcpClient(WiFiClient& client) { + client.stop(); +} + +void SerialWebInterface::serviceTcp() { + if (_session == Session::TCP && !_tcp_client.connected()) clearSession(); + + WiFiClient incoming = _tcp_server.accept(); + if (incoming) { + if (_session == Session::NONE) { + _tcp_client = incoming; + _tcp_client.setNoDelay(true); + _tcp_connected = true; + _session = Session::TCP; + resetTcpParser(); + } else { + rejectTcpClient(incoming); + } + } + + if (_session != Session::TCP) return; + + serviceTcpWrite(); + serviceTcpRead(); +} + +void SerialWebInterface::serviceTcpRead() { + size_t budget = TCP_READ_BUDGET; + while (budget-- > 0 && _tcp_client.available() > 0) { + if (_tcp_rx_state == TcpRxState::MARKER && isWriteBusy()) return; + + const int value = _tcp_client.read(); + if (value < 0) return; + const uint8_t byte = static_cast(value); + + switch (_tcp_rx_state) { + case TcpRxState::MARKER: + if (byte == '<') _tcp_rx_state = TcpRxState::LENGTH_LOW; + break; + case TcpRxState::LENGTH_LOW: + _tcp_rx_length = byte; + _tcp_rx_state = TcpRxState::LENGTH_HIGH; + break; + case TcpRxState::LENGTH_HIGH: + _tcp_rx_length |= static_cast(byte) << 8; + if (_tcp_rx_length < 1 || _tcp_rx_length > MAX_FRAME_SIZE) { + _tcp_client.stop(); + clearSession(); + return; + } + _tcp_rx_offset = 0; + _tcp_rx_state = TcpRxState::PAYLOAD; + break; + case TcpRxState::PAYLOAD: + _tcp_rx_data[_tcp_rx_offset++] = byte; + if (_tcp_rx_offset == _tcp_rx_length) { + _recv_queue.push(_tcp_rx_data, _tcp_rx_length); + resetTcpParser(); + } + break; + } + } +} + +void SerialWebInterface::serviceTcpWrite() { + if (_tcp_tx_length == 0) { + Frame* frame = _send_queue.front(); + if (frame == nullptr) return; + _tcp_tx_data[0] = '>'; + _tcp_tx_data[1] = frame->len & 0xff; + _tcp_tx_data[2] = frame->len >> 8; + memcpy(&_tcp_tx_data[3], frame->data, frame->len); + _tcp_tx_length = frame->len + 3; + _tcp_tx_offset = 0; + } + + const int fd = _tcp_client.fd(); + if (fd < 0) return; + const int sent = ::send(fd, _tcp_tx_data + _tcp_tx_offset, + _tcp_tx_length - _tcp_tx_offset, MSG_DONTWAIT); + if (sent > 0) { + _tcp_tx_offset += static_cast(sent); + if (_tcp_tx_offset == _tcp_tx_length) { + _send_queue.pop(); + _tcp_tx_length = 0; + _tcp_tx_offset = 0; + } + } else if (sent < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { + _tcp_client.stop(); + clearSession(); + } +} + +bool SerialWebInterface::httpAuthorized() { + if (!_station_active || _http_server.authenticate(HTTP_AUTH_USER, _ap_password)) return true; + _http_server.requestAuthentication(); + return false; +} + +void SerialWebInterface::handleIndex() { + if (!httpAuthorized()) return; + _http_server.sendHeader("Content-Encoding", "gzip"); + _http_server.sendHeader("Cache-Control", "no-store"); + _http_server.sendHeader("X-Content-Type-Options", "nosniff"); + _http_server.send_P(200, PSTR("text/html; charset=utf-8"), + reinterpret_cast(RCC6_WEB_UI_INDEX_GZ), RCC6_WEB_UI_INDEX_GZ_LEN); +} + +void SerialWebInterface::handleHttpGetFrame() { + if (!httpAuthorized()) return; + if (!claimHttpSession()) { + sendHttpStatus(409); + return; + } + + acknowledgeHttpFrame(); + Frame* frame = _send_queue.front(); + if (frame == nullptr) { + sendHttpStatus(204); + return; + } + + if (_http_inflight_seq == 0) _http_inflight_seq = nextHttpSequence(); + _http_server.sendHeader("Cache-Control", "no-store"); + _http_server.sendHeader(HTTP_SEQUENCE_HEADER, String(_http_inflight_seq)); + _http_server.setContentLength(frame->len); + _http_server.send(200, "application/octet-stream", ""); + _http_server.sendContent(reinterpret_cast(frame->data), frame->len); +} + +void SerialWebInterface::handleHttpPostRaw() { + HTTPRaw& raw = _http_server.raw(); + switch (raw.status) { + case RAW_START: + _http_post_length = 0; + _http_post_expected = static_cast(_http_server.clientContentLength()); + _http_server.client().setTimeout(HTTP_BODY_TIMEOUT_MS); + if (_http_server.clientContentLength() < 1 || + _http_server.clientContentLength() > MAX_FRAME_SIZE) { + _http_post_result = HttpPostResult::INVALID; + _http_server.client().stop(); + } else if (_station_active && !_http_server.authenticate(HTTP_AUTH_USER, _ap_password)) { + _http_post_result = HttpPostResult::UNAUTHORIZED; + } else if (!claimHttpSession()) { + _http_post_result = HttpPostResult::CONFLICT; + } else if (isWriteBusy()) { + _http_post_result = HttpPostResult::FULL; + } else { + _http_post_result = HttpPostResult::PENDING; + } + break; + case RAW_WRITE: + if (_http_post_result == HttpPostResult::PENDING) { + if (_http_post_length + raw.currentSize > MAX_FRAME_SIZE) { + _http_post_result = HttpPostResult::INVALID; + } else { + memcpy(_http_post_data + _http_post_length, raw.buf, raw.currentSize); + _http_post_length += raw.currentSize; + } + } + break; + case RAW_END: + if (_http_post_result == HttpPostResult::PENDING) { + if (_http_post_length != _http_post_expected) { + _http_post_result = HttpPostResult::INVALID; + } else if (!isAllowedHttpOpcode(_http_post_data[0])) { + _http_post_result = HttpPostResult::FORBIDDEN; + } else if (_recv_queue.push(_http_post_data, _http_post_length)) { + _http_post_result = HttpPostResult::ACCEPTED; + } else { + _http_post_result = HttpPostResult::FULL; + } + } + break; + case RAW_ABORTED: + _http_post_result = HttpPostResult::ABORTED; + break; + } +} + +void SerialWebInterface::handleHttpPostFrame() { + switch (_http_post_result) { + case HttpPostResult::ACCEPTED: + sendHttpStatus(204); + break; + case HttpPostResult::UNAUTHORIZED: + _http_server.requestAuthentication(); + break; + case HttpPostResult::FORBIDDEN: + sendHttpStatus(403); + break; + case HttpPostResult::CONFLICT: + sendHttpStatus(409); + break; + case HttpPostResult::FULL: + sendHttpStatus(429); + break; + case HttpPostResult::INVALID: + case HttpPostResult::ABORTED: + case HttpPostResult::NONE: + case HttpPostResult::PENDING: + sendHttpStatus(400); + break; + } + _http_post_result = HttpPostResult::NONE; +} + +void SerialWebInterface::handleHttpGetNetwork() { + if (!httpAuthorized()) return; + if (!claimHttpSession()) { + sendHttpStatus(409); + return; + } + + String response; + response.reserve(128); + response += F("{\"mode\":\""); + response += _station_active ? F("station") : F("ap"); + response += F("\",\"ssid\":\""); + response += jsonEscape(getCurrentSsid()); + response += F("\",\"ip\":\""); + response += _current_ip.toString(); + response += F("\",\"fallback\":"); + response += _fallback_active ? F("true") : F("false"); + response += '}'; + _http_server.sendHeader("Cache-Control", "no-store"); + _http_server.send(200, "application/json", response); +} + +void SerialWebInterface::handleHttpPostNetwork() { + if (!httpAuthorized()) return; + if (!claimHttpSession()) { + sendHttpStatus(409); + return; + } + + const String mode = _http_server.arg("mode"); + if (mode == "ap") { + if (!saveNetworkConfig(false, String(), String())) { + sendHttpStatus(500); + return; + } + } else if (mode == "station") { + const String ssid = _http_server.arg("ssid"); + const String password = _http_server.arg("password"); + if (ssid.length() < 1 || ssid.length() > 32 || + (password.length() != 0 && (password.length() < 8 || password.length() > 64))) { + sendHttpStatus(400); + return; + } + if (!saveNetworkConfig(true, ssid, password)) { + sendHttpStatus(500); + return; + } + } else { + sendHttpStatus(400); + return; + } + + _http_server.sendHeader("Cache-Control", "no-store"); + _http_server.send(202, "application/json", "{\"ok\":true,\"restart\":true}"); + scheduleRestart(); +} + +void SerialWebInterface::sendHttpStatus(int status) { + _http_server.sendHeader("Cache-Control", "no-store"); + _http_server.send(status, "text/plain", ""); +} + +#endif diff --git a/src/helpers/esp32/SerialWebInterface.h b/src/helpers/esp32/SerialWebInterface.h new file mode 100644 index 00000000..220f0cc5 --- /dev/null +++ b/src/helpers/esp32/SerialWebInterface.h @@ -0,0 +1,135 @@ +#pragma once + +#include "../BaseSerialInterface.h" +#include +#include + +class SerialWebInterface : public BaseSerialInterface { +public: + SerialWebInterface(); + + void begin(const char* node_name, uint16_t tcp_port = 5000); + + void enable() override; + void disable() override; + bool isEnabled() const override { return _enabled; } + bool isConnected() const override; + bool isWriteBusy() const override; + size_t writeFrame(const uint8_t src[], size_t len) override; + size_t checkRecvFrame(uint8_t dest[]) override; + + const char* getApSsid() const { return _ap_ssid; } + const char* getApPassword() const { return _ap_password; } + IPAddress getApIP() const; + bool isStationMode() const { return _station_active; } + const char* getCurrentSsid() const; + IPAddress getCurrentIP() const { return _current_ip; } + bool isFallbackActive() const { return _fallback_active; } + bool selectSetupAp(); + bool clearStoredNetworkConfig(); +#ifdef NEONPOCKET_ULTIMATE + WebServer& httpServer() { return _http_server; } + bool authorizeHttpRequest() { return httpAuthorized(); } +#endif + +private: + static constexpr uint8_t FRAME_QUEUE_SIZE = 8; + static constexpr uint32_t HTTP_SESSION_TIMEOUT_MS = 10000; + static constexpr uint32_t HTTP_BODY_TIMEOUT_MS = 250; + static constexpr uint32_t STA_CONNECT_TIMEOUT_MS = 15000; + static constexpr uint32_t RESTART_DELAY_MS = 750; + static constexpr size_t TCP_READ_BUDGET = 384; + + struct Frame { + uint16_t len; + uint8_t data[MAX_FRAME_SIZE]; + }; + + struct FrameQueue { + Frame frames[FRAME_QUEUE_SIZE]; + uint8_t head; + uint8_t count; + + void clear(); + bool full() const; + bool empty() const; + bool push(const uint8_t* data, size_t len); + Frame* front(); + void pop(); + size_t pop(uint8_t* dest); + }; + + enum class Session : uint8_t { NONE, TCP, HTTP }; + enum class TcpRxState : uint8_t { MARKER, LENGTH_LOW, LENGTH_HIGH, PAYLOAD }; + enum class HttpPostResult : uint8_t { + NONE, PENDING, ACCEPTED, INVALID, UNAUTHORIZED, FORBIDDEN, CONFLICT, FULL, ABORTED + }; + + bool _initialized; + bool _enabled; + bool _routes_registered; + bool _tcp_connected; + uint16_t _tcp_port; + char _ap_ssid[33]; + char _ap_password[9]; + bool _prefer_station; + bool _station_active; + bool _fallback_active; + char _station_ssid[33]; + char _station_password[65]; + IPAddress _current_ip; + uint32_t _restart_at; + + WiFiServer _tcp_server; + WiFiClient _tcp_client; + WebServer _http_server; + Session _session; + IPAddress _http_remote; + char _http_session_id[33]; + uint32_t _http_last_activity; + uint32_t _http_inflight_seq; + uint32_t _http_next_seq; + + FrameQueue _recv_queue; + FrameQueue _send_queue; + + TcpRxState _tcp_rx_state; + uint16_t _tcp_rx_length; + uint16_t _tcp_rx_offset; + uint8_t _tcp_rx_data[MAX_FRAME_SIZE]; + uint8_t _tcp_tx_data[MAX_FRAME_SIZE + 3]; + uint16_t _tcp_tx_length; + uint16_t _tcp_tx_offset; + + HttpPostResult _http_post_result; + uint16_t _http_post_expected; + uint16_t _http_post_length; + uint8_t _http_post_data[MAX_FRAME_SIZE]; + + void configureIdentity(const char* node_name); + void registerHttpRoutes(); + bool startAccessPoint(); + void startServers(); + bool httpAuthorized(); + bool saveNetworkConfig(bool station, const String& ssid, const String& password); + void scheduleRestart(); + void clearSession(); + void resetTcpParser(); + void expireHttpSession(); + bool claimHttpSession(); + bool httpSessionTokenValid(const String& token) const; + bool acknowledgeHttpFrame(); + uint32_t nextHttpSequence(); + void serviceTcp(); + void serviceTcpRead(); + void serviceTcpWrite(); + void rejectTcpClient(WiFiClient& client); + + void handleIndex(); + void handleHttpGetFrame(); + void handleHttpPostFrame(); + void handleHttpPostRaw(); + void handleHttpGetNetwork(); + void handleHttpPostNetwork(); + void sendHttpStatus(int status); +}; diff --git a/tools/package_ultimate_ota.py b/tools/package_ultimate_ota.py new file mode 100644 index 00000000..0f18e717 --- /dev/null +++ b/tools/package_ultimate_ota.py @@ -0,0 +1,171 @@ +#!/usr/bin/env python3 +"""Build and verify signed NeonPocket Ultimate Web OTA packages.""" + +from __future__ import annotations + +import argparse +import hashlib +import pathlib +import struct +import subprocess +import tempfile + + +PREFIX = struct.Struct("<4sHH16s8s16s41sI32s") +HEADER = struct.Struct("<4sHH16s8s16s41sI32s64s") +MAGIC = b"NPU2" +FORMAT_VERSION = 1 + + +def fixed(value: str, size: int, label: str) -> bytes: + raw = value.encode("ascii") + if len(raw) >= size: + raise ValueError(f"{label} must be shorter than {size} ASCII bytes") + return raw + bytes(size - len(raw)) + + +def run_openssl(*args: str) -> bytes: + result = subprocess.run( + ["openssl", *args], check=True, capture_output=True + ) + return result.stdout + + +def sign(prefix: bytes, private_key: pathlib.Path) -> bytes: + with tempfile.NamedTemporaryFile(suffix=".bin") as source: + source.write(prefix) + source.flush() + signature = run_openssl( + "pkeyutl", "-sign", "-rawin", "-inkey", str(private_key), + "-in", source.name, + ) + if len(signature) != 64: + raise ValueError(f"unexpected Ed25519 signature length: {len(signature)}") + return signature + + +def verify_signature(prefix: bytes, signature: bytes, public_key: pathlib.Path) -> None: + with tempfile.NamedTemporaryFile(suffix=".bin") as source, \ + tempfile.NamedTemporaryFile(suffix=".sig") as sig_file: + source.write(prefix) + source.flush() + sig_file.write(signature) + sig_file.flush() + subprocess.run( + ["openssl", "pkeyutl", "-verify", "-rawin", "-pubin", + "-inkey", str(public_key), "-in", source.name, + "-sigfile", sig_file.name], + check=True, capture_output=True, + ) + + +def pack(args: argparse.Namespace) -> None: + application = args.app.read_bytes() + digest = hashlib.sha256(application).digest() + prefix = PREFIX.pack( + MAGIC, + FORMAT_VERSION, + HEADER.size, + fixed(args.target, 16, "target"), + fixed(args.mode, 8, "mode"), + fixed(args.version, 16, "version"), + fixed(args.git_sha, 41, "git SHA"), + len(application), + digest, + ) + package = prefix + sign(prefix, args.key) + application + args.output.parent.mkdir(parents=True, exist_ok=True) + args.output.write_bytes(package) + print(f"wrote {args.output} ({len(package)} bytes)") + print(f"sha256 {hashlib.sha256(package).hexdigest()}") + + +def verify(args: argparse.Namespace) -> None: + package = args.package.read_bytes() + if len(package) < HEADER.size: + raise ValueError("package is shorter than the fixed header") + fields = HEADER.unpack_from(package) + magic, version, header_size, target, mode, release, git_sha, length, digest, signature = fields + target = target.split(b"\0", 1)[0].decode("ascii") + mode = mode.split(b"\0", 1)[0].decode("ascii") + if magic != MAGIC or version != FORMAT_VERSION or header_size != HEADER.size: + raise ValueError("invalid Ultimate OTA header") + if target != args.target or mode != args.mode: + raise ValueError(f"wrong target/mode: {target}/{mode}") + application = package[HEADER.size:] + if len(application) != length: + raise ValueError("application length does not match header") + if hashlib.sha256(application).digest() != digest: + raise ValueError("application SHA-256 mismatch") + verify_signature(package[:PREFIX.size], signature, args.public_key) + print( + f"verified {args.package}: {target}/{mode} " + f"{release.split(bytes(1), 1)[0].decode('ascii')} " + f"{git_sha.split(bytes(1), 1)[0].decode('ascii')}" + ) + + +def self_test() -> None: + with tempfile.TemporaryDirectory() as directory: + root = pathlib.Path(directory) + private_key = root / "private.pem" + public_key = root / "public.pem" + app = root / "firmware.bin" + package = root / "firmware.npu" + run_openssl("genpkey", "-algorithm", "ED25519", "-out", str(private_key)) + run_openssl("pkey", "-in", str(private_key), "-pubout", "-out", str(public_key)) + app.write_bytes(b"\xE9" + bytes(range(1, 128))) + pack(argparse.Namespace( + app=app, output=package, key=private_key, target="heltec_rcc6", + mode="web", version="2.1.0-rc.1", git_sha="1" * 40, + )) + verify(argparse.Namespace( + package=package, public_key=public_key, + target="heltec_rcc6", mode="web", + )) + corrupted = bytearray(package.read_bytes()) + corrupted[-1] ^= 1 + package.write_bytes(corrupted) + try: + verify(argparse.Namespace( + package=package, public_key=public_key, + target="heltec_rcc6", mode="web", + )) + except (ValueError, subprocess.CalledProcessError): + print("self-test rejected corrupted payload") + else: + raise AssertionError("corrupted payload was accepted") + + +def parser() -> argparse.ArgumentParser: + root = argparse.ArgumentParser(description=__doc__) + commands = root.add_subparsers(dest="command", required=True) + pack_parser = commands.add_parser("pack") + pack_parser.add_argument("--app", type=pathlib.Path, required=True) + pack_parser.add_argument("--output", type=pathlib.Path, required=True) + pack_parser.add_argument("--key", type=pathlib.Path, required=True) + pack_parser.add_argument("--version", required=True) + pack_parser.add_argument("--git-sha", required=True) + pack_parser.add_argument("--target", default="heltec_rcc6") + pack_parser.add_argument("--mode", default="web") + pack_parser.set_defaults(func=pack) + + verify_parser = commands.add_parser("verify") + verify_parser.add_argument("--package", type=pathlib.Path, required=True) + verify_parser.add_argument("--public-key", type=pathlib.Path, required=True) + verify_parser.add_argument("--target", default="heltec_rcc6") + verify_parser.add_argument("--mode", default="web") + verify_parser.set_defaults(func=verify) + + self_test_parser = commands.add_parser("self-test") + self_test_parser.set_defaults(func=lambda _: self_test()) + return root + + +def main() -> None: + args = parser().parse_args() + args.func(args) + + +if __name__ == "__main__": + main() diff --git a/tools/ultimate_ota_public.pem b/tools/ultimate_ota_public.pem new file mode 100644 index 00000000..5a325ba3 --- /dev/null +++ b/tools/ultimate_ota_public.pem @@ -0,0 +1,3 @@ +-----BEGIN PUBLIC KEY----- +MCowBQYDK2VwAyEA/L/537/PPhNH6FUm1szPX5It1yeJ7LVrHy65/uv9Mic= +-----END PUBLIC KEY----- diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index a44bd7f1..2773d388 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -232,6 +232,53 @@ lib_deps = ${Heltec_lora32_v3.lib_deps} densaugeo/base64 @ ~1.4.0 +[heltec_v3_ultimate_companion] +extends = Heltec_lora32_v3 +extra_scripts = pre:scripts/ultimate_build_flags.py +build_flags = + ${Heltec_lora32_v3.build_flags} + -I examples/companion_radio + -I examples/companion_radio/ui-new + -D FIRMWARE_VERSION='"v1.17.1"' + -D DISPLAY_CLASS=SSD1306Display + -D DISPLAY_REQUIRED=1 + -D NEONPOCKET_MONO_UI=1 + -D NEONPOCKET_UI=1 + -D NEONPOCKET_DEVICE_LABEL='"HELTEC V3 ULTIMATE"' + -D NEONPOCKET_ULTIMATE=1 + -D NEONPOCKET_ULTIMATE_VERSION='"2.0.0-rc.1"' + -D NEONPOCKET_ULTIMATE_OTA_TARGET='"heltec_v3"' + -D NEONPOCKET_MEMORY_GATE_BYTES=32768 + -D NEONPOCKET_SAFE_SPIFFS_BOOTSTRAP=1 + -D MAX_CONTACTS=350 + -D MAX_GROUP_CHANNELS=40 + -D OFFLINE_QUEUE_SIZE=256 + -D AUTO_OFF_MILLIS=60000 +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + + + + + + +<../examples/companion_radio/*.cpp> + +<../examples/companion_radio/ui-new/*.cpp> +lib_deps = + ${Heltec_lora32_v3.lib_deps} + densaugeo/base64 @ ~1.4.0 + +[env:heltec_v3_ultimate_companion_ble] +extends = heltec_v3_ultimate_companion +build_flags = + ${heltec_v3_ultimate_companion.build_flags} + -D BLE_PIN_CODE=123456 + -D SERIAL_BLE_FRAME_QUEUE_SIZE=8 + +[env:heltec_v3_ultimate_companion_web] +extends = heltec_v3_ultimate_companion +build_flags = + ${heltec_v3_ultimate_companion.build_flags} + -I examples/companion_radio/webui + -D RCC6_WEB_AP=1 + -D TCP_PORT=5000 + [env:Heltec_v3_companion_radio_wifi] extends = Heltec_lora32_v3 build_flags = From 54b8cf4477fbca5ffcc5fa27ad42e9b33bb8672b Mon Sep 17 00:00:00 2001 From: n30nex Date: Sun, 23 Aug 2026 11:20:05 -0400 Subject: [PATCH 2/8] fix: regenerate deterministic Ultimate WebUI asset --- .../companion_radio/webui/Rcc6WebUiAssets.h | 546 +++++++++--------- 1 file changed, 273 insertions(+), 273 deletions(-) diff --git a/examples/companion_radio/webui/Rcc6WebUiAssets.h b/examples/companion_radio/webui/Rcc6WebUiAssets.h index 14bb72ad..505cebdb 100644 --- a/examples/companion_radio/webui/Rcc6WebUiAssets.h +++ b/examples/companion_radio/webui/Rcc6WebUiAssets.h @@ -1,5 +1,5 @@ // Generated by examples/companion_radio/webui/build.mjs. Do not edit. -// gzip sha256: a55cd5362e1df89380df8f66d4380119dc18070919d60499f4ad9d3b108b595f +// gzip sha256: 69d636af07aa016bc2a5123d3a08924c5bc913861d83d10db47b3ce7f949233e #pragma once #include @@ -3064,278 +3064,278 @@ const uint8_t RCC6_WEB_UI_INDEX_GZ[] PROGMEM = { 0x0a, 0x19, 0xb4, 0xd0, 0x07, 0x61, 0x24, 0xfd, 0x93, 0x51, 0x11, 0xa3, 0xbd, 0x2f, 0xa8, 0x3f, 0xb0, 0xa5, 0x71, 0x16, 0x84, 0x7e, 0xa1, 0x99, 0xbf, 0xe1, 0xa3, 0xbf, 0xf9, 0xdc, 0xa5, 0xac, 0x58, 0xfa, 0x1b, 0x5d, 0x96, 0xbd, 0x78, 0x37, 0xbb, 0xf0, 0xaa, 0xaa, 0xee, 0x62, 0xd1, 0xb8, - 0xe7, 0x9e, 0xd2, 0x26, 0x31, 0xdb, 0xce, 0x81, 0xd3, 0x6c, 0x24, 0x9e, 0x73, 0xcc, 0xdf, 0x86, - 0xc1, 0x38, 0x71, 0x93, 0xa5, 0x56, 0xa2, 0xe5, 0xb4, 0x0e, 0x9d, 0x26, 0x27, 0xe4, 0xdc, 0x06, - 0x5a, 0xe7, 0x97, 0x5c, 0x06, 0xc4, 0x81, 0xd4, 0xaa, 0x9c, 0xcf, 0x6a, 0xcb, 0x97, 0x62, 0x30, - 0xe5, 0x9b, 0xe4, 0x41, 0x8b, 0x8f, 0x5d, 0xb6, 0x97, 0x1a, 0x71, 0xaf, 0x4e, 0x00, 0xd7, 0x1c, - 0x15, 0x8a, 0x22, 0x7e, 0x1b, 0x02, 0x40, 0x07, 0xd5, 0xb7, 0x5c, 0xe0, 0x27, 0x2d, 0xb0, 0xc9, - 0x4a, 0xd1, 0x3d, 0xd9, 0x8d, 0x73, 0x71, 0x3d, 0xd8, 0x8d, 0x85, 0x25, 0xa9, 0x46, 0x44, 0xf7, - 0xf5, 0xa0, 0x95, 0x7e, 0x49, 0x44, 0x4f, 0x48, 0x00, 0x0f, 0xab, 0x22, 0x3a, 0x58, 0x40, 0xe4, - 0xff, 0x9d, 0x26, 0x1a, 0xce, 0xfc, 0x5a, 0x19, 0x7d, 0xd6, 0x6b, 0x9d, 0x76, 0x67, 0x6f, 0x82, - 0xee, 0x6c, 0xaf, 0x77, 0x70, 0x64, 0xfb, 0x05, 0xa9, 0xd9, 0xcf, 0xa5, 0xf4, 0x99, 0x2d, 0x1a, - 0xc0, 0x08, 0x90, 0xb3, 0xbc, 0x57, 0x71, 0x15, 0x79, 0xb5, 0x51, 0x2c, 0x41, 0xcb, 0x21, 0xfd, - 0xa2, 0xde, 0xb4, 0xf5, 0xb9, 0xcd, 0x7a, 0x4c, 0x03, 0x42, 0xcd, 0x80, 0xd9, 0x12, 0x81, 0x62, - 0xcc, 0xdc, 0xa8, 0x55, 0x8d, 0xf6, 0x38, 0xeb, 0xbd, 0xcd, 0x64, 0x4c, 0x49, 0x89, 0xee, 0xb8, - 0x0d, 0x31, 0x4c, 0x24, 0x07, 0x1a, 0x97, 0xb1, 0x2c, 0xc5, 0xc9, 0x89, 0x6e, 0xc3, 0xbb, 0xc2, - 0x44, 0x84, 0xfd, 0x96, 0x73, 0xdc, 0x69, 0x3b, 0x87, 0xf0, 0x3d, 0x9a, 0xfc, 0x2d, 0x0a, 0x6a, - 0xa2, 0x77, 0xd8, 0x3f, 0xec, 0xfc, 0x44, 0x0a, 0xf3, 0x23, 0x40, 0xf0, 0xaf, 0x5a, 0xba, 0xcb, - 0xd7, 0x94, 0x3c, 0x4a, 0xf5, 0xc2, 0xb2, 0xf7, 0xb8, 0x9b, 0xec, 0xeb, 0x5e, 0x62, 0x64, 0x20, - 0x7d, 0x91, 0xa6, 0xb7, 0xb3, 0xbb, 0x52, 0x3e, 0x20, 0xa1, 0x44, 0x1c, 0xd8, 0xab, 0xd5, 0xac, - 0xa7, 0xcd, 0xc7, 0xce, 0xce, 0xe0, 0x0d, 0x24, 0x86, 0xc7, 0x9e, 0x34, 0x1b, 0xfc, 0x7d, 0xaf, - 0x50, 0xac, 0xbf, 0x41, 0x59, 0x31, 0x68, 0x1c, 0xd0, 0x03, 0x7b, 0xbf, 0x05, 0x31, 0x0d, 0xed, - 0xce, 0x60, 0x7f, 0x42, 0xc6, 0xbd, 0xa0, 0xd1, 0x6a, 0x36, 0xee, 0x45, 0x8c, 0xc9, 0xee, 0xbc, - 0xef, 0xe7, 0xb6, 0xde, 0xb1, 0xdd, 0xf1, 0x73, 0x3b, 0xf0, 0x18, 0x0c, 0x81, 0xaf, 0x9a, 0x6b, - 0x7d, 0xbd, 0x8b, 0xf3, 0xd2, 0xd4, 0xf5, 0x73, 0xef, 0xd3, 0x1a, 0xe7, 0xd9, 0x22, 0x10, 0xe1, - 0xdc, 0x32, 0xe5, 0x8b, 0xc6, 0x14, 0x71, 0x9a, 0xa9, 0x33, 0xa7, 0xf3, 0x38, 0x59, 0xfe, 0x0c, - 0xea, 0x3a, 0x73, 0x78, 0x76, 0x7d, 0x6d, 0x76, 0xcc, 0xe1, 0xf9, 0xe5, 0xfb, 0x4f, 0x97, 0x3f, - 0x73, 0x52, 0x50, 0xa8, 0x50, 0x09, 0x34, 0x58, 0xac, 0x0f, 0x17, 0x03, 0x88, 0x9a, 0x44, 0xef, - 0x48, 0x12, 0x07, 0xe1, 0x70, 0x52, 0x70, 0x49, 0x85, 0x47, 0xb8, 0x7d, 0xea, 0x6b, 0x08, 0x8f, - 0x55, 0xa9, 0x0d, 0xdd, 0xea, 0xca, 0x6a, 0xc8, 0xd2, 0xa3, 0x62, 0x63, 0x93, 0x84, 0xd2, 0xc6, - 0x8c, 0xba, 0x8b, 0x1c, 0xac, 0xc0, 0xb5, 0x73, 0x80, 0xd4, 0x49, 0x28, 0xfd, 0x48, 0xdd, 0x05, - 0xb4, 0xb3, 0xdf, 0x6a, 0x42, 0x34, 0x2d, 0xe3, 0x97, 0x77, 0xa5, 0x06, 0x42, 0x70, 0x67, 0x60, - 0x20, 0x28, 0x8b, 0x5f, 0x46, 0xb9, 0x19, 0xf1, 0xfc, 0x2c, 0x94, 0x76, 0xc5, 0xad, 0xed, 0x4d, - 0xc2, 0x8c, 0xcd, 0x70, 0x30, 0x50, 0xd7, 0x0f, 0xd8, 0x22, 0x74, 0x97, 0x1f, 0xe0, 0xe1, 0xaf, - 0x8c, 0x57, 0xd4, 0xa3, 0x2a, 0x83, 0x71, 0x60, 0xce, 0x4a, 0x4d, 0xa4, 0x41, 0x48, 0xa5, 0xd4, - 0x2c, 0x1a, 0xb8, 0x81, 0x47, 0x10, 0xb0, 0xd5, 0xd8, 0x37, 0x5a, 0x27, 0xc7, 0x06, 0x16, 0x29, - 0x55, 0xf3, 0x93, 0x78, 0xc1, 0x60, 0xcd, 0x29, 0x00, 0x36, 0xdf, 0xc3, 0x5f, 0x18, 0x0e, 0x58, - 0x2f, 0xf3, 0x47, 0x46, 0x33, 0xd0, 0x40, 0xc5, 0x59, 0x3a, 0x86, 0xef, 0x43, 0xd9, 0x1b, 0x1f, - 0xbe, 0xa7, 0x0b, 0x0c, 0x2f, 0x8c, 0x12, 0xb9, 0x1b, 0x24, 0x95, 0x69, 0x70, 0x83, 0x04, 0x60, - 0xa6, 0x9f, 0xf9, 0x57, 0x40, 0x30, 0xeb, 0xf5, 0x5c, 0x45, 0x29, 0xba, 0x35, 0xdf, 0x9d, 0x5d, - 0x9c, 0x5d, 0x0e, 0xce, 0xdf, 0x9b, 0xc4, 0xfc, 0xf0, 0xe9, 0xfc, 0x02, 0xfe, 0x7d, 0x77, 0x76, - 0x73, 0x73, 0xfe, 0xe5, 0x77, 0xf3, 0x4e, 0x8b, 0xdb, 0x2c, 0x63, 0x56, 0xdd, 0x24, 0x34, 0xf2, - 0x3f, 0xdf, 0x0f, 0x69, 0xf2, 0x31, 0xce, 0x12, 0x68, 0xb2, 0xb0, 0x7d, 0xe5, 0xc9, 0x23, 0x79, - 0x90, 0xab, 0xfb, 0xb7, 0x18, 0x27, 0xdb, 0xd2, 0x9e, 0x14, 0xe7, 0xb2, 0x6d, 0xaf, 0x8d, 0xaf, - 0x5c, 0x9f, 0xc0, 0xde, 0x36, 0xfb, 0xe6, 0x1e, 0xaa, 0x45, 0x20, 0x13, 0x83, 0x31, 0xff, 0xba, - 0x3f, 0xd3, 0x2f, 0x4f, 0xd5, 0x4f, 0x92, 0x45, 0x1c, 0x3b, 0x9b, 0x3a, 0x19, 0x1b, 0x7f, 0x8c, - 0x59, 0xaa, 0x74, 0x73, 0x7d, 0xf3, 0xd7, 0xeb, 0x77, 0xc6, 0x2c, 0x66, 0xa9, 0xa1, 0xee, 0x44, - 0x68, 0x3d, 0x8b, 0x16, 0x61, 0x36, 0xcd, 0x71, 0xfd, 0xae, 0x01, 0x17, 0x47, 0x64, 0xf8, 0x01, - 0x03, 0x0d, 0xf5, 0x94, 0x1a, 0x3c, 0xd1, 0xaa, 0xd9, 0x51, 0x23, 0xfd, 0xc2, 0x7b, 0xf9, 0x8c, - 0x31, 0xd2, 0x61, 0x6c, 0x23, 0x77, 0x0c, 0x68, 0x99, 0x62, 0xb4, 0xef, 0xfa, 0xd2, 0x79, 0x0c, - 0xf0, 0xfa, 0xf7, 0x18, 0x19, 0x1c, 0x46, 0x73, 0xe0, 0x1c, 0x1e, 0x19, 0x5f, 0x31, 0x86, 0x38, - 0xbd, 0x4d, 0x1d, 0xb4, 0xa3, 0x0c, 0x93, 0x78, 0x12, 0x84, 0xf4, 0x6e, 0xb5, 0xca, 0xd7, 0x47, - 0xa9, 0x5c, 0xdc, 0x82, 0x69, 0x00, 0xd6, 0xf5, 0xf8, 0x18, 0x76, 0x23, 0x4f, 0x48, 0xc6, 0x54, - 0xf6, 0xb5, 0xb4, 0xe2, 0x37, 0x9f, 0x6f, 0xb9, 0x3f, 0xe1, 0x36, 0x5f, 0xa9, 0xdc, 0xe0, 0x1e, - 0x45, 0xb9, 0xdf, 0x7c, 0x1f, 0x51, 0xb9, 0xfc, 0xf7, 0x1a, 0x7e, 0xe2, 0x1e, 0xbc, 0x49, 0x82, - 0xc5, 0x67, 0x26, 0x91, 0xd2, 0x85, 0x87, 0x78, 0x86, 0x50, 0x07, 0x86, 0x46, 0x85, 0x38, 0x6a, - 0x70, 0xee, 0x42, 0x78, 0xdd, 0x0b, 0x8e, 0xc3, 0x4a, 0x1d, 0x1e, 0xa5, 0x9e, 0xc1, 0x55, 0xca, - 0x1d, 0x96, 0x27, 0x22, 0x7e, 0xf2, 0xe4, 0xb6, 0x75, 0x07, 0x41, 0x93, 0x27, 0xb7, 0xed, 0x3b, - 0x95, 0x60, 0x69, 0x72, 0x7b, 0x72, 0xb7, 0x46, 0xff, 0x49, 0xa0, 0x4d, 0x60, 0xeb, 0x78, 0xa6, - 0xa2, 0x4c, 0x25, 0xd5, 0x99, 0xdc, 0x1e, 0x40, 0xcd, 0xee, 0x03, 0x5a, 0x52, 0xd4, 0x17, 0xf3, - 0xee, 0x95, 0x21, 0x23, 0x26, 0xb7, 0x26, 0x84, 0x2f, 0x30, 0x21, 0x56, 0x81, 0xba, 0x6c, 0xee, - 0xc8, 0x6d, 0xae, 0xa2, 0x56, 0x4e, 0xc7, 0x18, 0xed, 0x78, 0x7c, 0x7a, 0xe8, 0x9b, 0x77, 0x36, - 0x29, 0x35, 0xfb, 0x40, 0xe9, 0x37, 0xd5, 0x68, 0x52, 0x68, 0x54, 0x8e, 0x68, 0x5b, 0xab, 0x3c, - 0x86, 0xb2, 0xad, 0x40, 0xc9, 0x08, 0xea, 0x87, 0x2f, 0x2d, 0x2c, 0x35, 0xc6, 0x9b, 0x11, 0x44, - 0xbe, 0xe8, 0xf5, 0x0e, 0xe2, 0xe6, 0x06, 0x6f, 0x77, 0x09, 0xe3, 0x79, 0x28, 0x37, 0x24, 0x5c, - 0x16, 0xfc, 0x1a, 0xa1, 0x2b, 0x50, 0xf0, 0x30, 0x7f, 0xab, 0xf7, 0xa1, 0xca, 0x46, 0x2c, 0x85, - 0x20, 0x2f, 0x8e, 0x26, 0xc1, 0x34, 0x03, 0xe8, 0x1f, 0x28, 0x43, 0x28, 0xe8, 0xdf, 0xa0, 0x39, - 0x2d, 0xff, 0x86, 0xe2, 0x25, 0x26, 0x82, 0x8f, 0x98, 0xe5, 0x4e, 0xaf, 0x85, 0x01, 0x9a, 0x76, - 0x77, 0x56, 0xef, 0xc8, 0x37, 0x91, 0x62, 0x86, 0x84, 0xfb, 0x73, 0x83, 0x1b, 0xe2, 0xfd, 0xc7, - 0x58, 0xa2, 0xcc, 0x75, 0x4c, 0x9c, 0x04, 0x0d, 0xce, 0x68, 0x57, 0x87, 0x9f, 0xc2, 0x74, 0x8b, - 0x7f, 0x2f, 0x5c, 0x44, 0x9e, 0xc7, 0x0b, 0xa0, 0xe6, 0x05, 0xf7, 0xd6, 0x2f, 0x31, 0xba, 0xfb, - 0x4c, 0x9c, 0x24, 0x0e, 0x69, 0xee, 0xde, 0x3a, 0x71, 0x18, 0xa5, 0x90, 0x54, 0xa6, 0x12, 0x7a, - 0x65, 0x22, 0xed, 0xe8, 0xd2, 0xd2, 0xa6, 0x3d, 0x2a, 0xae, 0x8a, 0x9d, 0x3b, 0xe2, 0xce, 0xa4, - 0xb7, 0xbf, 0x57, 0x58, 0x24, 0x79, 0x47, 0x8b, 0x75, 0xf2, 0x6a, 0xd6, 0xa9, 0x12, 0x05, 0x4c, - 0xad, 0x9b, 0xb7, 0x75, 0xdd, 0xc0, 0xea, 0x57, 0x8c, 0x49, 0xe1, 0x70, 0x77, 0xc0, 0x72, 0x73, - 0x2f, 0x59, 0x2b, 0xce, 0x1c, 0x6c, 0x5b, 0x2d, 0x1e, 0x37, 0x74, 0xb5, 0xb2, 0x26, 0xce, 0xb7, - 0x20, 0xf2, 0x05, 0xee, 0x47, 0x06, 0x24, 0x55, 0x3e, 0xb5, 0x76, 0xcd, 0x74, 0x06, 0x91, 0x17, - 0xcf, 0x83, 0x68, 0x8a, 0x6a, 0x07, 0x6e, 0x56, 0x06, 0x2d, 0x77, 0xa4, 0xd4, 0xd6, 0xb8, 0x1c, - 0x9a, 0xef, 0xd2, 0x48, 0xf3, 0xfa, 0x98, 0x08, 0xd7, 0x4e, 0xf4, 0x75, 0xf4, 0x2a, 0xd3, 0x9d, - 0xf5, 0xaa, 0xd1, 0xd0, 0x80, 0x9b, 0xcb, 0x50, 0xd9, 0x55, 0xc7, 0x2b, 0x29, 0x05, 0xa3, 0x20, - 0xb5, 0x59, 0x99, 0x5d, 0x2a, 0xf2, 0x73, 0x9e, 0xeb, 0xf3, 0x24, 0x96, 0xe5, 0x5a, 0xcc, 0x73, - 0xa3, 0x01, 0x7f, 0xf9, 0x99, 0x15, 0xeb, 0xe0, 0x9d, 0xd1, 0x58, 0xf0, 0x4b, 0xa3, 0x5a, 0x53, - 0xbf, 0x52, 0x90, 0xb3, 0x78, 0xa8, 0xde, 0xd8, 0x39, 0x57, 0x57, 0xa9, 0x2e, 0x4a, 0xc8, 0xe1, - 0x7e, 0x76, 0x67, 0x5b, 0x1a, 0x41, 0x01, 0x96, 0xbb, 0x1d, 0x6e, 0x6c, 0x47, 0x95, 0xf9, 0x7c, - 0x5f, 0x69, 0x69, 0x91, 0x04, 0xf7, 0xb0, 0xdf, 0x6c, 0xc7, 0x9b, 0x51, 0xef, 0x1b, 0xc8, 0xaa, - 0xaf, 0x32, 0x47, 0x3c, 0xbd, 0x8c, 0xd3, 0x60, 0x22, 0x34, 0x31, 0x0c, 0x97, 0x63, 0x52, 0xd8, - 0xfd, 0x8b, 0x59, 0xe2, 0x32, 0x8c, 0x95, 0x32, 0x71, 0x3c, 0xb1, 0xe3, 0xd5, 0xee, 0xb6, 0x32, - 0xe7, 0x8f, 0x2c, 0xf0, 0xbe, 0x0d, 0x79, 0x21, 0x7e, 0x25, 0xd4, 0xe3, 0xfc, 0x41, 0xc7, 0xc5, - 0xe3, 0x83, 0xc3, 0x96, 0xfa, 0x1f, 0x50, 0xcb, 0xe0, 0x6d, 0x1b, 0xaf, 0x9f, 0xc2, 0xbd, 0x16, - 0x6c, 0x98, 0x29, 0x94, 0x12, 0xde, 0x69, 0xdd, 0xa9, 0x50, 0x12, 0xc3, 0xd6, 0x31, 0xc9, 0x14, - 0x44, 0x8e, 0x0b, 0x4e, 0x55, 0x0f, 0x4f, 0xc8, 0x14, 0x63, 0x61, 0x02, 0x5d, 0xe3, 0x6d, 0xf4, - 0x42, 0x32, 0x15, 0x73, 0x33, 0x23, 0x73, 0xb9, 0xc3, 0xa6, 0xe8, 0xe0, 0xcb, 0x7f, 0xcf, 0xc1, - 0xc2, 0x0f, 0x51, 0x77, 0x5e, 0x8c, 0x0f, 0xf8, 0x85, 0x69, 0x22, 0x07, 0x44, 0x35, 0xe3, 0x51, - 0x4a, 0xc5, 0x94, 0x4b, 0x01, 0x04, 0xd8, 0x30, 0xf5, 0xb0, 0x86, 0x09, 0x1b, 0xc1, 0xcd, 0xa1, - 0xd6, 0x72, 0x16, 0x2c, 0x20, 0x6e, 0x3c, 0x0f, 0x7a, 0x55, 0x79, 0xc1, 0x99, 0xb2, 0x2e, 0x06, - 0x6b, 0x46, 0x87, 0x37, 0x70, 0x71, 0x23, 0x80, 0x18, 0x22, 0xa8, 0xea, 0x07, 0xb9, 0x87, 0xfc, - 0x52, 0x08, 0x04, 0xf3, 0xaf, 0xc2, 0xc1, 0x0e, 0x3f, 0x29, 0x25, 0xca, 0x21, 0xb7, 0xe0, 0xb3, - 0x77, 0x16, 0x86, 0x96, 0xe9, 0x70, 0x5d, 0xa5, 0x99, 0x2f, 0x10, 0x2b, 0x46, 0xa6, 0x4f, 0xe3, - 0xe9, 0x34, 0x04, 0x15, 0x3a, 0x37, 0x8d, 0x12, 0xa6, 0xe6, 0x98, 0xd7, 0xc4, 0xf0, 0xe3, 0xf6, - 0xd6, 0x2e, 0x22, 0xf7, 0xde, 0x50, 0x9a, 0x91, 0xef, 0xee, 0x46, 0x2c, 0x4b, 0xaa, 0xe2, 0xa6, - 0x3d, 0x81, 0x42, 0xb3, 0x73, 0x6b, 0x5e, 0xdd, 0xd3, 0x84, 0xc7, 0xe4, 0x32, 0x2f, 0xcf, 0xaf, - 0x2e, 0x87, 0x57, 0x83, 0x5f, 0xce, 0x6f, 0x3e, 0x0f, 0x4c, 0x3d, 0xbb, 0xa3, 0xf4, 0xde, 0x42, - 0xf3, 0xcf, 0xc7, 0xb3, 0xcb, 0xcb, 0xf3, 0x8b, 0x6b, 0x63, 0xc7, 0x10, 0x9e, 0x2a, 0x79, 0xee, - 0x47, 0xe1, 0xae, 0x07, 0x4d, 0x5d, 0xbd, 0x3f, 0x87, 0x22, 0x17, 0x57, 0x83, 0xb3, 0x9b, 0x4f, - 0x57, 0x97, 0xd7, 0xe6, 0x9d, 0xc8, 0x05, 0x79, 0xcb, 0x0d, 0x4c, 0x70, 0xb1, 0x7e, 0xfa, 0x7a, - 0x6e, 0x7c, 0xf9, 0x60, 0xe6, 0x29, 0x1d, 0x3b, 0xb7, 0xe6, 0x0d, 0x68, 0x89, 0x13, 0x51, 0xe2, - 0xfa, 0xe6, 0xea, 0xcb, 0xd9, 0xcf, 0xe7, 0xc4, 0x18, 0x5e, 0xfd, 0x76, 0xfe, 0x05, 0x7b, 0xbc, - 0x1e, 0x5e, 0x9c, 0xfd, 0x6e, 0xde, 0x21, 0x7b, 0xc8, 0xa7, 0x4e, 0x6a, 0x70, 0xe9, 0x6d, 0x7a, - 0xd7, 0x77, 0x6e, 0x9b, 0xc0, 0x84, 0xca, 0xf0, 0x99, 0x9c, 0x0b, 0x17, 0xe5, 0xbe, 0x05, 0xde, - 0x37, 0x80, 0xfc, 0x88, 0x82, 0x2d, 0x28, 0x58, 0xf8, 0x64, 0xa1, 0x88, 0x55, 0x7e, 0x65, 0x10, - 0x8b, 0x36, 0x0f, 0x88, 0xfe, 0x95, 0xe9, 0xce, 0xcd, 0xbc, 0xa8, 0xdc, 0xe4, 0xb8, 0x8d, 0xf0, - 0x09, 0x9f, 0x8a, 0x8d, 0xfb, 0x1e, 0xdc, 0x21, 0xb1, 0x9c, 0xfc, 0x64, 0x73, 0x67, 0x07, 0x23, - 0xfb, 0x71, 0x5e, 0x5e, 0x39, 0x99, 0x5a, 0x4f, 0x69, 0x0c, 0x69, 0xc2, 0xc6, 0x74, 0xe6, 0xde, - 0x07, 0x71, 0xd2, 0x31, 0xd9, 0x3c, 0x8e, 0xd3, 0x99, 0xb9, 0xae, 0x44, 0x51, 0xfc, 0x19, 0x23, - 0xc5, 0xa7, 0x0e, 0x44, 0x35, 0x03, 0x79, 0x8c, 0x4e, 0xdc, 0x2c, 0x4c, 0x2d, 0x2d, 0x12, 0x43, - 0xc9, 0x37, 0x95, 0x40, 0xec, 0x3a, 0x3c, 0xcf, 0x42, 0xc3, 0x49, 0x2a, 0x9e, 0xdb, 0x18, 0x53, - 0x64, 0xb5, 0x52, 0x59, 0x50, 0xe0, 0xc1, 0x7d, 0xea, 0xf0, 0x78, 0x93, 0x16, 0x93, 0x6e, 0x6e, - 0x6f, 0xaf, 0x55, 0x56, 0x99, 0x0b, 0x4b, 0xb9, 0xf8, 0x05, 0xcc, 0x80, 0x34, 0x84, 0x46, 0xeb, - 0xb0, 0x69, 0x8c, 0x97, 0x29, 0xee, 0x1a, 0x99, 0x95, 0x91, 0x73, 0xc8, 0x10, 0xb1, 0x1b, 0x22, - 0x53, 0xe4, 0x5d, 0x8a, 0xf4, 0x6a, 0x5a, 0x24, 0xee, 0x2d, 0x1e, 0x78, 0x5a, 0x48, 0x6e, 0x70, - 0x27, 0x56, 0x31, 0xb9, 0x99, 0x30, 0xcf, 0xab, 0x94, 0x67, 0xad, 0x35, 0x18, 0x5e, 0x94, 0xb9, - 0x6c, 0x28, 0x43, 0x46, 0x72, 0x2c, 0x0a, 0x8f, 0xab, 0xe8, 0x73, 0xb0, 0x52, 0x24, 0x3d, 0xf9, - 0xfb, 0x1f, 0xf1, 0x2a, 0x17, 0xae, 0xe5, 0x37, 0x5a, 0xb0, 0x66, 0x55, 0x44, 0x8b, 0x42, 0x4e, - 0x98, 0xdd, 0xe1, 0x15, 0x4a, 0x25, 0x39, 0x48, 0x58, 0xd3, 0xac, 0x32, 0x9b, 0xa7, 0x81, 0x41, - 0x28, 0x87, 0x89, 0xa2, 0xb0, 0x6f, 0x92, 0x57, 0xaa, 0xd1, 0x9d, 0x9d, 0x4a, 0x7e, 0xb5, 0xa4, - 0x5f, 0x4e, 0xb0, 0xb6, 0xb3, 0x63, 0xc5, 0xa5, 0x67, 0xbd, 0xa4, 0x5c, 0x88, 0x87, 0xa6, 0xd7, - 0x16, 0x44, 0x74, 0x26, 0x03, 0x4f, 0xe6, 0xa3, 0x90, 0x79, 0x22, 0xcb, 0x15, 0x2a, 0xf9, 0x23, - 0xe3, 0x82, 0x73, 0xf0, 0x30, 0x47, 0x4d, 0xe5, 0x9b, 0xa2, 0xc6, 0x07, 0x1a, 0x27, 0x5e, 0x77, - 0x89, 0x14, 0x7e, 0xcf, 0x1a, 0x7b, 0x9f, 0x82, 0xa2, 0xe1, 0xf5, 0xd3, 0x75, 0xba, 0xe6, 0x5b, - 0x65, 0x64, 0x6b, 0xfe, 0xd1, 0xa2, 0x5c, 0x41, 0x51, 0xf4, 0xf6, 0x3a, 0xcd, 0x41, 0x69, 0x7a, - 0xac, 0x20, 0x9e, 0x87, 0xd0, 0x89, 0x23, 0xcb, 0x75, 0xbe, 0x50, 0xb6, 0x88, 0x23, 0x46, 0x21, - 0xfb, 0x23, 0x73, 0x44, 0xf8, 0x90, 0xcf, 0x6c, 0x0a, 0x68, 0x39, 0x42, 0x7b, 0x6f, 0x9f, 0x6e, - 0x78, 0xa8, 0xed, 0xa7, 0x62, 0x18, 0xfe, 0x0e, 0xc5, 0xfc, 0x2a, 0xf5, 0x8d, 0x88, 0x88, 0xf4, - 0x75, 0x8d, 0x14, 0x82, 0xd5, 0xeb, 0x8d, 0x68, 0x59, 0x44, 0xb8, 0x1f, 0xeb, 0xab, 0x54, 0x05, - 0x98, 0x4d, 0xf5, 0x00, 0xb3, 0xab, 0x95, 0x05, 0x79, 0x3d, 0xf3, 0xe2, 0xb8, 0x1c, 0xdc, 0x12, - 0xaf, 0x3f, 0x2c, 0xe5, 0x33, 0x79, 0xb0, 0xcc, 0x71, 0x9c, 0xab, 0xd2, 0xf2, 0x1c, 0x28, 0xdc, - 0x2e, 0x69, 0xda, 0xa4, 0x0d, 0x58, 0x69, 0x9f, 0x5a, 0xc3, 0xd4, 0xd6, 0xb2, 0xd6, 0x42, 0x86, - 0xdb, 0xb5, 0xad, 0x65, 0x77, 0xbd, 0x00, 0x5b, 0x53, 0x00, 0x41, 0x5f, 0x79, 0xbe, 0xd5, 0xf2, - 0x06, 0xb0, 0xf3, 0x4f, 0x02, 0xf5, 0x43, 0xf1, 0xab, 0x0a, 0x19, 0x49, 0x65, 0xb1, 0x3c, 0x69, - 0x31, 0x62, 0x96, 0xf8, 0xe7, 0x43, 0x49, 0xfc, 0x13, 0x57, 0xf9, 0x99, 0xa1, 0x5f, 0x58, 0x85, - 0xd4, 0x28, 0x1e, 0x4f, 0x19, 0xcf, 0xb8, 0x1d, 0x91, 0xc7, 0x41, 0x34, 0x06, 0x60, 0xe0, 0x35, - 0x82, 0x14, 0x2d, 0x63, 0x91, 0x91, 0x3e, 0x93, 0x1b, 0x56, 0x04, 0x49, 0xaf, 0x8e, 0x51, 0xc8, - 0x0c, 0x6a, 0x90, 0x95, 0x2c, 0xb1, 0xe6, 0x05, 0x46, 0xf8, 0xcd, 0x53, 0x36, 0x03, 0x9c, 0xd4, - 0x28, 0xa6, 0x59, 0xde, 0xdc, 0xbc, 0xca, 0xdb, 0x9c, 0xf7, 0x50, 0x5a, 0x6a, 0x6c, 0x5e, 0x16, - 0x13, 0x6d, 0xc3, 0x4a, 0xf0, 0xfe, 0x5f, 0xb4, 0x80, 0x5f, 0x68, 0xfd, 0xd2, 0xe5, 0x3b, 0x7a, - 0x98, 0x81, 0x55, 0x0a, 0x76, 0xf3, 0x67, 0x36, 0x15, 0x30, 0x64, 0x3e, 0xa2, 0x54, 0xa6, 0x9d, - 0xf6, 0xa9, 0xf5, 0x91, 0x95, 0x1a, 0x96, 0x54, 0xe1, 0xe5, 0xcd, 0x5f, 0xd2, 0x07, 0xee, 0xaa, - 0x89, 0xe7, 0x44, 0x25, 0x83, 0xd1, 0x63, 0xf1, 0x7f, 0x8a, 0x7c, 0xfa, 0x08, 0x11, 0xa6, 0x12, - 0x20, 0x97, 0x79, 0x40, 0xfe, 0x5e, 0xaf, 0x57, 0xf4, 0x60, 0x07, 0x93, 0xc0, 0xdb, 0x5e, 0xb3, - 0xaf, 0xa5, 0x10, 0x61, 0x77, 0x3d, 0xaa, 0xe5, 0x18, 0x71, 0xb2, 0x88, 0xcd, 0x82, 0x09, 0xf8, - 0xe2, 0xf3, 0x08, 0xfd, 0xbf, 0x73, 0x72, 0x06, 0x0a, 0x2a, 0xcd, 0x8f, 0xe0, 0x8c, 0x3b, 0xcc, - 0xaf, 0x31, 0x4e, 0x33, 0xde, 0xd2, 0xa3, 0xf2, 0x92, 0xe9, 0xdf, 0x20, 0x3e, 0xa0, 0x32, 0x3d, - 0x1f, 0x0a, 0xd3, 0xf3, 0xb4, 0xae, 0xad, 0x7c, 0x0d, 0x37, 0x08, 0xa8, 0x09, 0x92, 0x39, 0xf5, - 0xb5, 0x49, 0xb8, 0x2d, 0x66, 0xa3, 0xd4, 0x72, 0x85, 0xc9, 0x04, 0x39, 0x51, 0x35, 0x5c, 0xc6, - 0xce, 0x4e, 0x54, 0x93, 0x07, 0xa1, 0x63, 0xda, 0xf0, 0x22, 0x8f, 0xc0, 0x21, 0xa8, 0x3c, 0x3c, - 0x2c, 0xdd, 0x10, 0x90, 0x0b, 0xc5, 0xf5, 0xbe, 0xc1, 0xd0, 0xec, 0x2e, 0xc3, 0x0c, 0x07, 0xf2, - 0x06, 0x10, 0xba, 0x31, 0x75, 0x09, 0x20, 0x67, 0x7c, 0x61, 0xc9, 0x80, 0x32, 0x62, 0xa4, 0x46, - 0x5e, 0xac, 0x7e, 0xc1, 0x2f, 0xe2, 0xe9, 0x97, 0xc7, 0xf7, 0x10, 0xe0, 0x9f, 0xa2, 0xb7, 0x3e, - 0x8f, 0xf9, 0xff, 0x84, 0x5f, 0x8b, 0xbf, 0x41, 0xa3, 0x47, 0x14, 0x90, 0x3e, 0xc7, 0xc3, 0x13, - 0x81, 0x62, 0xef, 0x28, 0x3c, 0xfb, 0x9a, 0x54, 0x12, 0x00, 0xd0, 0xef, 0x0a, 0xfd, 0xcf, 0xf3, - 0xb1, 0xfc, 0xce, 0x61, 0xc7, 0xdf, 0xcd, 0x50, 0x63, 0x98, 0xe6, 0xad, 0xc1, 0x08, 0x40, 0x2a, - 0x28, 0xb0, 0xd5, 0x60, 0xb5, 0xda, 0xd2, 0xcf, 0x2d, 0x94, 0x6d, 0x00, 0xac, 0xe2, 0xee, 0xcf, - 0xf7, 0x03, 0xd5, 0xa1, 0x9f, 0x6a, 0x5c, 0xa4, 0xba, 0x46, 0x10, 0x73, 0x08, 0x98, 0x06, 0x08, - 0xb2, 0x94, 0x0a, 0x0d, 0xa7, 0xb8, 0x8e, 0xb1, 0x0d, 0x2d, 0x00, 0x49, 0xb5, 0x3a, 0xcb, 0xc6, - 0xf3, 0x20, 0x35, 0xc9, 0xcf, 0x09, 0x96, 0x2d, 0xdf, 0xea, 0xd5, 0x0a, 0xfc, 0x0d, 0x19, 0xc6, - 0x58, 0x5e, 0x84, 0xd4, 0x10, 0x41, 0xa9, 0xeb, 0x07, 0x98, 0x7f, 0xa5, 0x38, 0x51, 0x8a, 0xb4, - 0x5d, 0x58, 0x32, 0xe6, 0x87, 0x68, 0x00, 0xf7, 0x5c, 0x99, 0xd2, 0xe1, 0x9b, 0x9a, 0x5b, 0xea, - 0xf9, 0x75, 0x00, 0x57, 0x51, 0x30, 0xd4, 0x7f, 0xdf, 0x5a, 0x3c, 0xe5, 0x6e, 0xb5, 0xf9, 0x9a, - 0x88, 0x27, 0xe4, 0x4f, 0xf5, 0x09, 0xc9, 0x57, 0xb7, 0x08, 0x6e, 0x54, 0x08, 0x84, 0x1b, 0xa5, - 0x88, 0x35, 0x5f, 0xc8, 0x4a, 0xe8, 0xef, 0xe7, 0x26, 0xbb, 0x70, 0x8f, 0x5c, 0x58, 0xe6, 0x35, - 0x10, 0x75, 0xd8, 0x0e, 0x21, 0x05, 0x71, 0xa9, 0x34, 0xd3, 0xd7, 0xf5, 0xcc, 0x40, 0xb7, 0xc6, - 0xb6, 0xbf, 0xb1, 0xe3, 0x3c, 0x47, 0xfa, 0x56, 0x44, 0x80, 0x1e, 0xdb, 0x5d, 0xf0, 0xe4, 0x9c, - 0xb1, 0x46, 0xa7, 0x07, 0x4e, 0x8d, 0x41, 0xb3, 0x0f, 0x6c, 0x01, 0x77, 0xad, 0x96, 0x3c, 0xb4, - 0x24, 0xe2, 0x9c, 0xbb, 0xcd, 0xdf, 0x6f, 0xcc, 0x82, 0xbe, 0x75, 0x1c, 0x2d, 0x48, 0xcc, 0x50, - 0x97, 0x3f, 0x6d, 0xeb, 0xcc, 0x5a, 0x70, 0x23, 0x13, 0x0d, 0xd5, 0xd0, 0x17, 0x73, 0xfd, 0x82, - 0xe9, 0xec, 0x68, 0x49, 0xde, 0x8b, 0xe5, 0x07, 0x71, 0x16, 0xfa, 0x22, 0xa9, 0x80, 0x28, 0xa2, - 0x2f, 0x83, 0x38, 0x6e, 0xb5, 0x48, 0xc9, 0x8d, 0xa3, 0xbd, 0x89, 0x0b, 0xd5, 0x38, 0x6a, 0xae, - 0x81, 0x48, 0xb9, 0x6d, 0xd5, 0x7e, 0x63, 0x85, 0x6a, 0x9e, 0x1b, 0x79, 0x34, 0xfc, 0x8e, 0x0a, - 0x7e, 0x1c, 0x7d, 0x4f, 0xfb, 0x0a, 0x81, 0xb8, 0xfd, 0x58, 0x56, 0x40, 0x80, 0x39, 0x0c, 0x12, - 0xdd, 0x37, 0x84, 0xb3, 0x65, 0x0e, 0x86, 0xec, 0x8a, 0x67, 0xb4, 0xcf, 0x75, 0x5f, 0x9d, 0xef, - 0xc0, 0x49, 0xd2, 0xbe, 0xf9, 0x31, 0xf0, 0x01, 0xd7, 0xfe, 0xe7, 0x00, 0x93, 0x12, 0xae, 0x04, - 0x57, 0x68, 0xb7, 0x8a, 0x34, 0xdc, 0x4c, 0x88, 0xb9, 0xc0, 0x9f, 0x22, 0x23, 0xb2, 0x59, 0xca, - 0xaf, 0x83, 0x9b, 0x12, 0xd6, 0xdb, 0x02, 0x21, 0x25, 0x91, 0x26, 0xbf, 0x29, 0x41, 0x8d, 0xc4, - 0xbd, 0x1a, 0x51, 0x1f, 0xc3, 0xd9, 0xbf, 0x69, 0xad, 0x56, 0xd1, 0xdb, 0x83, 0xb6, 0x26, 0xf4, - 0x6b, 0x98, 0xae, 0x39, 0x64, 0x59, 0x1e, 0x53, 0xa3, 0xf5, 0x8f, 0xff, 0xf8, 0xef, 0x83, 0x76, - 0x59, 0xf4, 0xdf, 0x0c, 0xec, 0x84, 0xa6, 0xe3, 0x57, 0x98, 0xf8, 0xd6, 0x8a, 0xdf, 0x9c, 0xae, - 0x56, 0xf1, 0xdb, 0xe3, 0x43, 0x5b, 0xeb, 0x63, 0x28, 0x81, 0x62, 0xb2, 0x07, 0x54, 0xf4, 0x03, - 0x8a, 0xf0, 0xf4, 0x1f, 0xff, 0xf1, 0xdf, 0xc7, 0x87, 0xdb, 0xba, 0xd2, 0xbe, 0x59, 0x76, 0xa7, - 0x0f, 0xc4, 0xbd, 0xa7, 0x1b, 0xa9, 0xd0, 0x7d, 0x6c, 0xf1, 0x74, 0x35, 0x39, 0x86, 0x18, 0x71, - 0xb9, 0x94, 0xc8, 0x46, 0x3b, 0x6c, 0x6d, 0x93, 0xb3, 0xd8, 0xca, 0x33, 0xe2, 0x20, 0x2f, 0xaf, - 0xc1, 0xf7, 0xa1, 0x03, 0xdf, 0x31, 0xb8, 0x24, 0x07, 0x72, 0x0b, 0x07, 0xb9, 0x05, 0xd1, 0xd4, - 0xa9, 0x21, 0x5c, 0xf9, 0x79, 0x87, 0x7a, 0xb2, 0x09, 0x99, 0x31, 0xa5, 0x96, 0x90, 0x6d, 0xfc, - 0x12, 0x49, 0xc7, 0x6a, 0x30, 0xcf, 0x2f, 0x20, 0xd5, 0x00, 0xfa, 0xe1, 0xfc, 0x2b, 0x5c, 0xb7, - 0x3c, 0xe3, 0xb2, 0x0e, 0xf9, 0x2c, 0xe0, 0x3c, 0xfb, 0x06, 0x20, 0xfe, 0x84, 0x29, 0xf4, 0x21, - 0x08, 0xc3, 0x1c, 0xcb, 0x67, 0xa3, 0x99, 0x61, 0x1b, 0xee, 0x7a, 0xc3, 0x94, 0xbb, 0x0b, 0x73, - 0x6d, 0x13, 0x40, 0x62, 0xe3, 0x04, 0x63, 0x48, 0x0c, 0xa0, 0xa0, 0xb2, 0x5f, 0x71, 0x31, 0xfe, - 0xa9, 0xc9, 0x95, 0x29, 0xa4, 0xf3, 0xcf, 0xa8, 0x9f, 0xdb, 0xad, 0xc3, 0x6e, 0xad, 0xe5, 0x45, - 0x51, 0x8f, 0x5d, 0xdb, 0x4e, 0xb8, 0x40, 0xc1, 0x66, 0x55, 0x51, 0x6d, 0xab, 0xd5, 0x2b, 0x35, - 0xed, 0x03, 0x68, 0x97, 0x23, 0xe3, 0xc3, 0x65, 0xc9, 0x94, 0x65, 0x88, 0xee, 0x04, 0x16, 0x17, - 0x33, 0xcf, 0xf8, 0xb4, 0x0f, 0x97, 0xc2, 0xd3, 0xb6, 0xd4, 0xf4, 0xef, 0xf5, 0xd4, 0xf4, 0x79, - 0xef, 0xbd, 0xdb, 0x3b, 0x29, 0x34, 0x89, 0xdd, 0x5b, 0xee, 0x07, 0x3f, 0x12, 0x75, 0x48, 0xfc, - 0xa3, 0x95, 0x09, 0x43, 0xee, 0xce, 0x7f, 0x0a, 0x15, 0x03, 0xf1, 0xe8, 0x59, 0x6e, 0x8a, 0x1b, - 0x25, 0xee, 0x4c, 0xfb, 0x0e, 0x0d, 0xe4, 0xa8, 0x01, 0xe7, 0xac, 0xad, 0xd2, 0xed, 0xc9, 0x7c, - 0x4e, 0x5b, 0xd2, 0x0f, 0x69, 0xd9, 0x77, 0x7e, 0x7d, 0x69, 0xf2, 0x1d, 0x4c, 0xe4, 0x24, 0xd2, - 0xe9, 0xfc, 0xcb, 0xf5, 0xd5, 0xa5, 0xc3, 0x13, 0x51, 0x05, 0x93, 0xa5, 0xf5, 0x54, 0xb2, 0x91, - 0xc9, 0x04, 0xc8, 0xcf, 0xd8, 0xd8, 0x6c, 0x52, 0xb0, 0x92, 0xd5, 0xd5, 0x2a, 0xd9, 0xd7, 0x6c, - 0xa2, 0x5b, 0xc7, 0xea, 0x2a, 0xd4, 0x1a, 0xd7, 0x6c, 0x52, 0xb5, 0x8a, 0xd5, 0x55, 0xde, 0x64, - 0x5d, 0xd3, 0xea, 0x6b, 0xd6, 0xb0, 0xed, 0x2d, 0x54, 0x4c, 0x6b, 0x36, 0xa9, 0xb3, 0x8b, 0x75, - 0xb6, 0x9a, 0xd3, 0x88, 0x6e, 0x03, 0xe3, 0xba, 0xb5, 0x6e, 0x4d, 0x7e, 0x27, 0xbe, 0xde, 0x4c, - 0x64, 0x92, 0xe2, 0xc6, 0x1c, 0x10, 0x63, 0x25, 0x3c, 0x83, 0x17, 0xe3, 0xe4, 0x78, 0x0b, 0xc5, - 0xf5, 0x8b, 0xe5, 0x35, 0xb2, 0x50, 0xda, 0xf6, 0xf4, 0x11, 0x14, 0x3b, 0xcf, 0x1e, 0x73, 0xa1, - 0xd5, 0x97, 0x80, 0x2e, 0xc7, 0x65, 0xe0, 0x63, 0x5a, 0xde, 0x94, 0xb2, 0xb1, 0x52, 0x1f, 0x78, - 0xe6, 0xbe, 0x9f, 0x5c, 0x0f, 0x69, 0x32, 0x77, 0x23, 0x1a, 0xa5, 0xe1, 0xd2, 0xa0, 0x68, 0x03, - 0x04, 0xca, 0xbd, 0x1f, 0x2d, 0xf6, 0xd5, 0xa9, 0xfe, 0xb7, 0x38, 0x4b, 0x22, 0x37, 0xec, 0x1b, - 0x0a, 0xa7, 0x2c, 0x73, 0xf3, 0xf0, 0x10, 0xcb, 0x2a, 0xde, 0x72, 0x42, 0x39, 0xc4, 0x21, 0x81, - 0x5b, 0xcc, 0xb4, 0xed, 0x9c, 0x52, 0x6f, 0x4c, 0x08, 0xa6, 0x9d, 0xac, 0xf7, 0xe7, 0x17, 0xe7, - 0x37, 0xe7, 0xfa, 0xe1, 0xfa, 0x9f, 0x8d, 0xcb, 0x61, 0x43, 0x28, 0x46, 0xcc, 0x0e, 0x27, 0x9d, - 0xe6, 0x7a, 0x5d, 0xc8, 0x71, 0x74, 0x61, 0x99, 0x57, 0x0a, 0x58, 0x53, 0xa6, 0x3f, 0x55, 0xb2, - 0xfe, 0x51, 0x2f, 0x51, 0xe5, 0xf9, 0xd7, 0x76, 0xb7, 0x64, 0x2f, 0x19, 0xc4, 0x32, 0x02, 0x60, - 0x01, 0x9e, 0x27, 0x96, 0x48, 0x25, 0xb4, 0xe1, 0xb0, 0x38, 0x69, 0x6e, 0x3c, 0xe6, 0x21, 0x12, - 0xa9, 0xfe, 0x00, 0x6c, 0xf3, 0x7f, 0xff, 0x3f, 0x05, 0xf8, 0x1a, 0x03, 0x98, 0xdd, 0x48, 0xf3, - 0x01, 0xa8, 0xb6, 0xcf, 0x66, 0x00, 0xfd, 0xce, 0xf7, 0x77, 0x0c, 0x5e, 0xd6, 0x68, 0xf5, 0x92, - 0x45, 0x8c, 0xdf, 0x3e, 0x5d, 0x5c, 0x00, 0x9b, 0x13, 0x44, 0x5e, 0x98, 0xf9, 0xd4, 0x07, 0x77, - 0x07, 0x15, 0xa8, 0xbd, 0x53, 0x2a, 0x2c, 0xee, 0xdb, 0xb9, 0x1b, 0x44, 0x86, 0x38, 0x3d, 0x8e, - 0xd9, 0x95, 0xbb, 0x61, 0x74, 0x0d, 0xdb, 0x3a, 0x2d, 0xd4, 0x48, 0x63, 0xb5, 0xee, 0xfd, 0x1f, - 0x7e, 0x78, 0xee, 0x2b, 0x7f, 0x38, 0xf3, 0xbc, 0x2c, 0x71, 0xbd, 0x65, 0xed, 0xb7, 0x42, 0xfd, - 0x78, 0x3d, 0x02, 0xc3, 0xc4, 0xa6, 0x7d, 0x21, 0xfb, 0xfd, 0xa7, 0x93, 0xdc, 0xd0, 0x4d, 0x83, - 0x34, 0xf3, 0x69, 0x27, 0x85, 0x60, 0x35, 0x53, 0xfe, 0x9b, 0x12, 0x57, 0x8c, 0xb7, 0xc3, 0x08, - 0x9f, 0xb4, 0x80, 0xd1, 0x4e, 0x84, 0x7a, 0xba, 0x0b, 0x2b, 0xe2, 0xde, 0x95, 0x38, 0x0f, 0x48, - 0x0c, 0x70, 0xc3, 0xd3, 0x08, 0x2f, 0xf5, 0x42, 0xca, 0x9f, 0x4e, 0xb9, 0xa0, 0x98, 0xdc, 0x70, - 0x09, 0x3a, 0xb0, 0xba, 0xa5, 0x7d, 0xa1, 0xb4, 0x92, 0x9f, 0x5a, 0x91, 0xc0, 0x2a, 0x89, 0xe7, - 0x8b, 0xd4, 0x32, 0xdf, 0x09, 0xbf, 0x0e, 0xb5, 0x4e, 0x41, 0xc1, 0x63, 0x8f, 0xa3, 0x8f, 0x02, - 0x26, 0xed, 0x01, 0xe8, 0xdc, 0x71, 0x0e, 0x0a, 0x69, 0x43, 0x4e, 0x84, 0x36, 0x0d, 0xe8, 0x4b, - 0x25, 0x92, 0x8b, 0xe6, 0x90, 0xed, 0x5b, 0x46, 0xa2, 0xbb, 0x1e, 0x95, 0x7e, 0x11, 0xc4, 0xe4, - 0x18, 0x33, 0x4e, 0xc5, 0xeb, 0xa1, 0xcc, 0xcc, 0x5e, 0xad, 0x2a, 0x0f, 0x23, 0x9d, 0x3b, 0xdf, - 0x34, 0x06, 0xcd, 0xea, 0x97, 0x93, 0x8d, 0x01, 0x44, 0x8c, 0x8a, 0x20, 0x2d, 0xbd, 0x3a, 0xc0, - 0x85, 0x69, 0xae, 0x39, 0xc0, 0x38, 0xac, 0xc8, 0xbd, 0x0f, 0xa6, 0x6e, 0x1a, 0x27, 0xce, 0x94, - 0xc6, 0x2a, 0x3a, 0x93, 0x64, 0xe4, 0x2c, 0xbb, 0x5b, 0x5b, 0x00, 0xa1, 0xf8, 0xdc, 0x5d, 0x71, - 0x18, 0xb3, 0x00, 0xa3, 0x5d, 0xd3, 0xde, 0xdb, 0x41, 0x6c, 0x51, 0xc7, 0x8b, 0x21, 0x71, 0x8d, - 0xa3, 0xc6, 0x9d, 0x3f, 0x91, 0x1f, 0x90, 0x3f, 0x92, 0x5b, 0xaa, 0x24, 0x9e, 0x6f, 0x1f, 0xb9, - 0x4d, 0x52, 0xf2, 0xc4, 0x77, 0xd6, 0xc7, 0x60, 0x3a, 0x93, 0xc7, 0xa8, 0xf3, 0xaa, 0x89, 0x56, - 0xce, 0x38, 0x4b, 0x3b, 0xad, 0x36, 0x3d, 0x20, 0x73, 0xf7, 0x31, 0x98, 0x67, 0xf3, 0xb3, 0x29, - 0xed, 0x34, 0xd7, 0xe5, 0x3b, 0x20, 0x4e, 0xdd, 0x7f, 0x96, 0x7c, 0x58, 0x6c, 0x93, 0xf3, 0x06, - 0xf0, 0x0f, 0xa4, 0xe4, 0xe6, 0x3b, 0x65, 0xb5, 0x7a, 0x45, 0x11, 0x75, 0xe6, 0xa4, 0xf1, 0x05, - 0x70, 0x11, 0x1c, 0x4d, 0xe9, 0xd0, 0xc8, 0x17, 0x6a, 0x67, 0x27, 0x5a, 0x64, 0xa6, 0xbe, 0xf8, - 0x2a, 0x82, 0xf7, 0x25, 0x8d, 0xa3, 0x61, 0x0c, 0x18, 0x30, 0x03, 0x0a, 0x21, 0x32, 0x0c, 0x12, - 0x93, 0xe5, 0x9b, 0x00, 0xba, 0x50, 0x64, 0xe9, 0x2b, 0x04, 0xb4, 0xe1, 0xb7, 0x0d, 0x38, 0xb9, - 0xba, 0x21, 0xf7, 0x76, 0xc1, 0x68, 0xd7, 0x5c, 0x9c, 0xe0, 0xa2, 0x86, 0x2e, 0x4d, 0x18, 0x71, - 0x14, 0x2e, 0x85, 0xb3, 0x10, 0xe6, 0xd4, 0x20, 0xc2, 0x2b, 0x6a, 0xe6, 0xb2, 0x19, 0xc1, 0xb6, - 0x30, 0x82, 0x03, 0x78, 0x01, 0x19, 0x48, 0x5e, 0x19, 0xfa, 0x18, 0x39, 0x23, 0x5b, 0x77, 0x5b, - 0x60, 0x3d, 0x70, 0x80, 0xfb, 0x10, 0x27, 0x73, 0x50, 0x58, 0xe7, 0x81, 0xcd, 0x4d, 0x35, 0x64, - 0x58, 0x79, 0x8c, 0xa2, 0x5f, 0x43, 0xc7, 0xb5, 0x05, 0xd9, 0x10, 0xba, 0xb9, 0x1b, 0x55, 0x84, - 0x1b, 0x6e, 0xf5, 0xae, 0xa6, 0x82, 0x54, 0x34, 0x32, 0x4e, 0xdd, 0x4a, 0x3e, 0x48, 0xa4, 0x79, - 0x6c, 0x5b, 0x3e, 0x46, 0x22, 0x6d, 0xd8, 0xb1, 0xe0, 0x7b, 0xb4, 0xed, 0x69, 0x3d, 0x81, 0x86, - 0x10, 0xe6, 0x3c, 0x76, 0xe2, 0x6f, 0xab, 0x15, 0x64, 0x67, 0x03, 0x37, 0x34, 0x74, 0x89, 0xcd, - 0x3c, 0x0f, 0x7c, 0x79, 0x2b, 0x69, 0x1c, 0x65, 0xa1, 0xd5, 0xca, 0xcc, 0x16, 0x61, 0xec, 0xfa, - 0x0d, 0xb1, 0xa9, 0xed, 0x2e, 0xc8, 0x5e, 0x6a, 0x72, 0xef, 0x61, 0xf5, 0x02, 0x94, 0xbd, 0x94, - 0xc8, 0x05, 0xc6, 0xb1, 0x58, 0xf9, 0x17, 0xba, 0x8b, 0x45, 0x59, 0x06, 0xb3, 0x62, 0xfb, 0xe9, - 0xc2, 0x1a, 0xfd, 0xca, 0x33, 0x44, 0x26, 0xf4, 0xdf, 0x50, 0x4f, 0xd6, 0x31, 0x5e, 0x3f, 0xc5, - 0x52, 0x10, 0x81, 0x44, 0xc6, 0x52, 0x86, 0x8f, 0x2a, 0xb2, 0x2c, 0xe7, 0xa5, 0xaa, 0xc7, 0x20, - 0xa1, 0x2c, 0xf8, 0x77, 0xaa, 0x14, 0xb5, 0x12, 0x80, 0x04, 0xf8, 0x16, 0x73, 0x67, 0xa7, 0x04, - 0x4b, 0x52, 0xf8, 0x0c, 0x88, 0xbd, 0x58, 0x03, 0x57, 0xb2, 0x54, 0xd0, 0x12, 0xfd, 0xad, 0x86, - 0xd6, 0xc0, 0x04, 0xa2, 0x9a, 0xa2, 0xb9, 0x3a, 0x9e, 0xfb, 0x80, 0x05, 0xe3, 0x00, 0x22, 0x03, - 0x49, 0x45, 0x3c, 0xb7, 0xfd, 0x96, 0x32, 0x84, 0x03, 0xba, 0x44, 0xa9, 0x0b, 0xd1, 0xb4, 0x94, - 0xdf, 0x0c, 0xdb, 0x12, 0x41, 0x42, 0xe2, 0xcb, 0xb2, 0xed, 0xef, 0x43, 0x9c, 0xd0, 0x29, 0x5e, - 0xcb, 0x1b, 0xad, 0x74, 0x9b, 0xe7, 0x6f, 0xe1, 0x4e, 0xe9, 0x0c, 0x14, 0x5b, 0x84, 0x2b, 0x5d, - 0x43, 0xea, 0xde, 0xd3, 0x21, 0x66, 0x81, 0x7e, 0xa6, 0x16, 0x43, 0x3d, 0x98, 0x20, 0x3c, 0xe0, - 0x01, 0xc6, 0xf0, 0x53, 0x20, 0x8d, 0x6a, 0x16, 0x01, 0x64, 0x76, 0x67, 0xc7, 0xaa, 0x31, 0xa7, - 0xfe, 0x59, 0x7d, 0xe7, 0xda, 0xee, 0x5e, 0xc4, 0xd6, 0x47, 0xbb, 0x0b, 0xb2, 0x6a, 0x97, 0xd1, - 0x14, 0x80, 0x12, 0xc9, 0xbd, 0x1b, 0x72, 0x53, 0x5c, 0x6d, 0x96, 0x48, 0xf2, 0xfc, 0xb4, 0xbf, - 0x2e, 0x5b, 0xf4, 0x48, 0x25, 0x97, 0x3b, 0x30, 0xa6, 0x6b, 0x72, 0x40, 0x0f, 0xed, 0x6e, 0xfd, - 0xe0, 0x9f, 0x0a, 0x46, 0xf0, 0x97, 0x59, 0xb7, 0xf3, 0xaf, 0x45, 0x9f, 0xdb, 0x62, 0x36, 0x59, - 0xed, 0x06, 0xb4, 0xbb, 0x6b, 0xdb, 0xb2, 0xbb, 0x6f, 0xf6, 0x99, 0x97, 0x04, 0x8b, 0xf4, 0xed, - 0x0f, 0x6f, 0xf6, 0x81, 0x36, 0xc0, 0xbf, 0xb3, 0x74, 0x1e, 0xbe, 0xfd, 0xe1, 0xff, 0x01, 0xd1, - 0xca, 0x01, 0xda, 0x6d, 0xcb, 0x02, 0x00, + 0xe7, 0x9e, 0xd2, 0x26, 0x31, 0xdb, 0x4e, 0xd3, 0x69, 0x36, 0x12, 0xcf, 0x69, 0xf1, 0xb7, 0x61, + 0x30, 0x4e, 0xdc, 0x64, 0xa9, 0x95, 0x68, 0x39, 0xad, 0x43, 0xa7, 0xc9, 0x09, 0x39, 0xb7, 0x81, + 0xd6, 0xf9, 0x25, 0x97, 0x01, 0x71, 0x20, 0xb5, 0x2a, 0xe7, 0xb3, 0xda, 0xf2, 0xa5, 0x18, 0x4c, + 0xf9, 0x26, 0x79, 0xd0, 0xe2, 0x63, 0x97, 0xed, 0xa5, 0x46, 0xdc, 0xab, 0x13, 0xc0, 0x35, 0x47, + 0x85, 0xa2, 0x88, 0xdf, 0x86, 0x00, 0xd0, 0x41, 0xf5, 0x2d, 0x17, 0xf8, 0x49, 0x0b, 0x6c, 0xb2, + 0x52, 0x74, 0x4f, 0x76, 0xe3, 0x5c, 0x5c, 0x0f, 0x76, 0x63, 0x61, 0x49, 0xaa, 0x11, 0xd1, 0x7d, + 0x3d, 0x68, 0xa5, 0x5f, 0x12, 0xd1, 0x13, 0x12, 0xc0, 0xc3, 0xaa, 0x88, 0x0e, 0x16, 0x10, 0xf9, + 0x7f, 0xa7, 0x89, 0x86, 0x33, 0xbf, 0x56, 0x46, 0x9f, 0xf5, 0x5a, 0xa7, 0xdd, 0xd9, 0x9b, 0xa0, + 0x3b, 0xdb, 0xeb, 0x1d, 0x1c, 0xd9, 0x7e, 0x41, 0x6a, 0xf6, 0x73, 0x29, 0x7d, 0x66, 0x8b, 0x06, + 0x30, 0x02, 0xe4, 0x2c, 0xef, 0x55, 0x5c, 0x45, 0x5e, 0x6d, 0x14, 0x4b, 0xd0, 0x72, 0x48, 0xbf, + 0xa8, 0x37, 0x6d, 0x7d, 0x6e, 0xb3, 0x1e, 0xd3, 0x80, 0x50, 0x33, 0x60, 0xb6, 0x44, 0xa0, 0x18, + 0x33, 0x37, 0x6a, 0x55, 0xa3, 0x3d, 0xce, 0x7a, 0x6f, 0x33, 0x19, 0x53, 0x52, 0xa2, 0x3b, 0x6e, + 0x43, 0x0c, 0x13, 0xc9, 0x81, 0xc6, 0x65, 0x2c, 0x4b, 0x71, 0x72, 0xa2, 0xdb, 0xf0, 0xae, 0x30, + 0x11, 0x61, 0xbf, 0xe5, 0x1c, 0x77, 0xda, 0xce, 0x21, 0x7c, 0x8f, 0x26, 0x7f, 0x8b, 0x82, 0x9a, + 0xe8, 0x1d, 0xf6, 0x0f, 0x3b, 0x3f, 0x91, 0xc2, 0xfc, 0x08, 0x10, 0xfc, 0xab, 0x96, 0xee, 0xf2, + 0x35, 0x25, 0x8f, 0x52, 0xbd, 0xb0, 0xec, 0x3d, 0xee, 0x26, 0xfb, 0xba, 0x97, 0x18, 0x19, 0x48, + 0x5f, 0xa4, 0xe9, 0xed, 0xec, 0xae, 0x94, 0x0f, 0x48, 0x28, 0x11, 0x07, 0xf6, 0x6a, 0x35, 0xeb, + 0x69, 0xf3, 0xb1, 0xb3, 0x33, 0x78, 0x03, 0x89, 0xe1, 0xb1, 0x27, 0xcd, 0x06, 0x7f, 0xdf, 0x2b, + 0x14, 0xeb, 0x6f, 0x50, 0x56, 0x0c, 0x1a, 0x07, 0xf4, 0xc0, 0xde, 0x6f, 0x41, 0x4c, 0x43, 0xbb, + 0x33, 0xd8, 0x9f, 0x90, 0x71, 0x2f, 0x68, 0xb4, 0x9a, 0x8d, 0x7b, 0x11, 0x63, 0xb2, 0x3b, 0xef, + 0xfb, 0xb9, 0xad, 0x77, 0x6c, 0x77, 0xfc, 0xdc, 0x0e, 0x3c, 0x06, 0x43, 0xe0, 0xab, 0xe6, 0x5a, + 0x5f, 0xef, 0xe2, 0xbc, 0x34, 0x75, 0xfd, 0xdc, 0xfb, 0xb4, 0xc6, 0x79, 0xb6, 0x08, 0x44, 0x38, + 0xb7, 0x4c, 0xf9, 0xa2, 0x31, 0x45, 0x9c, 0x66, 0xea, 0xcc, 0xe9, 0x3c, 0x4e, 0x96, 0x3f, 0x83, + 0xba, 0xce, 0x1c, 0x9e, 0x5d, 0x5f, 0x9b, 0x1d, 0x73, 0x78, 0x7e, 0xf9, 0xfe, 0xd3, 0xe5, 0xcf, + 0x9c, 0x14, 0x14, 0x2a, 0x54, 0x02, 0x0d, 0x16, 0xeb, 0xc3, 0xc5, 0x00, 0xa2, 0x26, 0xd1, 0x3b, + 0x92, 0xc4, 0x41, 0x38, 0x9c, 0x14, 0x5c, 0x52, 0xe1, 0x11, 0x6e, 0x9f, 0xfa, 0x1a, 0xc2, 0x63, + 0x55, 0x6a, 0x43, 0xb7, 0xba, 0xb2, 0x1a, 0xb2, 0xf4, 0xa8, 0xd8, 0xd8, 0x24, 0xa1, 0xb4, 0x31, + 0xa3, 0xee, 0x22, 0x07, 0x2b, 0x70, 0xed, 0x1c, 0x20, 0x75, 0x12, 0x4a, 0x3f, 0x52, 0x77, 0x01, + 0xed, 0xec, 0xb7, 0x9a, 0x10, 0x4d, 0xcb, 0xf8, 0xe5, 0x5d, 0xa9, 0x81, 0x10, 0xdc, 0x19, 0x18, + 0x08, 0xca, 0xe2, 0x97, 0x51, 0x6e, 0x46, 0x3c, 0x3f, 0x0b, 0xa5, 0x5d, 0x71, 0x6b, 0x7b, 0x93, + 0x30, 0x63, 0x33, 0x1c, 0x0c, 0xd4, 0xf5, 0x03, 0xb6, 0x08, 0xdd, 0xe5, 0x07, 0x78, 0xf8, 0x2b, + 0xe3, 0x15, 0xf5, 0xa8, 0xca, 0x60, 0x1c, 0x98, 0xb3, 0x52, 0x13, 0x69, 0x10, 0x52, 0x29, 0x35, + 0x8b, 0x06, 0x6e, 0xe0, 0x11, 0x04, 0x6c, 0x35, 0xf6, 0x8d, 0xd6, 0xc9, 0xb1, 0x81, 0x45, 0x4a, + 0xd5, 0xfc, 0x24, 0x5e, 0x30, 0x58, 0x73, 0x0a, 0x80, 0xcd, 0xf7, 0xf0, 0x17, 0x86, 0x03, 0xd6, + 0xcb, 0xfc, 0x91, 0xd1, 0x0c, 0x34, 0x50, 0x71, 0x96, 0x8e, 0xe1, 0xfb, 0x50, 0xf6, 0xc6, 0x87, + 0xef, 0xe9, 0x02, 0xc3, 0x0b, 0xa3, 0x44, 0xee, 0x06, 0x49, 0x65, 0x1a, 0xdc, 0x20, 0x01, 0x98, + 0xe9, 0x67, 0xfe, 0x15, 0x10, 0xcc, 0x7a, 0x3d, 0x57, 0x51, 0x8a, 0x6e, 0xcd, 0x77, 0x67, 0x17, + 0x67, 0x97, 0x83, 0xf3, 0xf7, 0x26, 0x31, 0x3f, 0x7c, 0x3a, 0xbf, 0x80, 0x7f, 0xdf, 0x9d, 0xdd, + 0xdc, 0x9c, 0x7f, 0xf9, 0xdd, 0xbc, 0xd3, 0xe2, 0x36, 0xcb, 0x98, 0x55, 0x37, 0x09, 0x8d, 0xfc, + 0xcf, 0xf7, 0x43, 0x9a, 0x7c, 0x8c, 0xb3, 0x04, 0x9a, 0x2c, 0x6c, 0x5f, 0x79, 0xf2, 0x48, 0x1e, + 0xe4, 0xea, 0xfe, 0x2d, 0xc6, 0xc9, 0xb6, 0xb4, 0x27, 0xc5, 0xb9, 0x6c, 0xdb, 0x6b, 0xe3, 0x2b, + 0xd7, 0x27, 0xb0, 0xb7, 0xcd, 0xbe, 0xb9, 0x87, 0x6a, 0x11, 0xc8, 0xc4, 0x60, 0xcc, 0xbf, 0xee, + 0xcf, 0xf4, 0xcb, 0x53, 0xf5, 0x93, 0x64, 0x11, 0xc7, 0xce, 0xa6, 0x4e, 0xc6, 0xc6, 0x1f, 0x63, + 0x96, 0x2a, 0xdd, 0x5c, 0xdf, 0xfc, 0xf5, 0xfa, 0x9d, 0x31, 0x8b, 0x59, 0x6a, 0xa8, 0x3b, 0x11, + 0x5a, 0xcf, 0xa2, 0x45, 0x98, 0x4d, 0x73, 0x5c, 0xbf, 0x6b, 0xc0, 0xc5, 0x11, 0x19, 0x7e, 0xc0, + 0x40, 0x43, 0x3d, 0xa5, 0x06, 0x4f, 0xb4, 0x6a, 0x76, 0xd4, 0x48, 0xbf, 0xf0, 0x5e, 0x3e, 0x63, + 0x8c, 0x74, 0x18, 0xdb, 0xc8, 0x1d, 0x03, 0x5a, 0xa6, 0x18, 0xed, 0xbb, 0xbe, 0x74, 0x1e, 0x03, + 0xbc, 0xfe, 0x3d, 0x46, 0x06, 0x87, 0xd1, 0x1c, 0x38, 0x87, 0x47, 0xc6, 0x57, 0x8c, 0x21, 0x4e, + 0x6f, 0x53, 0x07, 0xed, 0x28, 0xc3, 0x24, 0x9e, 0x04, 0x21, 0xbd, 0x5b, 0xad, 0xf2, 0xf5, 0x51, + 0x2a, 0x17, 0xb7, 0x60, 0x1a, 0x80, 0x75, 0x3d, 0x3e, 0x86, 0xdd, 0xc8, 0x13, 0x92, 0x31, 0x95, + 0x7d, 0x2d, 0xad, 0xf8, 0xcd, 0xe7, 0x5b, 0xee, 0x4f, 0xb8, 0xcd, 0x57, 0x2a, 0x37, 0xb8, 0x47, + 0x51, 0xee, 0x37, 0xdf, 0x47, 0x54, 0x2e, 0xff, 0xbd, 0x86, 0x9f, 0xb8, 0x07, 0x6f, 0x92, 0x60, + 0xf1, 0x99, 0x49, 0xa4, 0x74, 0xe1, 0x21, 0x9e, 0x21, 0xd4, 0x81, 0xa1, 0x51, 0x21, 0x8e, 0x1a, + 0x9c, 0xbb, 0x10, 0x5e, 0xf7, 0x82, 0xe3, 0xb0, 0x52, 0x87, 0x47, 0xa9, 0x67, 0x70, 0x95, 0x72, + 0x87, 0xe5, 0x89, 0x88, 0x9f, 0x3c, 0xb9, 0x6d, 0xdd, 0x41, 0xd0, 0xe4, 0xc9, 0x6d, 0xfb, 0x4e, + 0x25, 0x58, 0x9a, 0xdc, 0x9e, 0xdc, 0xad, 0xd1, 0x7f, 0x12, 0x68, 0x13, 0xd8, 0x3a, 0x9e, 0xa9, + 0x28, 0x53, 0x49, 0x75, 0x26, 0xb7, 0x07, 0x50, 0xb3, 0xfb, 0x80, 0x96, 0x14, 0xf5, 0xc5, 0xbc, + 0x7b, 0x65, 0xc8, 0x88, 0xc9, 0xad, 0x09, 0xe1, 0x0b, 0x4c, 0x88, 0x55, 0xa0, 0x2e, 0x9b, 0x3b, + 0x72, 0x9b, 0xab, 0xa8, 0x95, 0xd3, 0x31, 0x46, 0x3b, 0x1e, 0x9f, 0x1e, 0xfa, 0xe6, 0x9d, 0x4d, + 0x4a, 0xcd, 0x3e, 0x50, 0xfa, 0x4d, 0x35, 0x9a, 0x14, 0x1a, 0x95, 0x23, 0xda, 0xd6, 0x2a, 0x8f, + 0xa1, 0x6c, 0x2b, 0x50, 0x32, 0x82, 0xfa, 0xe1, 0x4b, 0x0b, 0x4b, 0x8d, 0xf1, 0x66, 0x04, 0x91, + 0x2f, 0x7a, 0xbd, 0x83, 0xb8, 0xb9, 0xc1, 0xdb, 0x5d, 0xc2, 0x78, 0x1e, 0xca, 0x0d, 0x09, 0x97, + 0x05, 0xbf, 0x46, 0xe8, 0x0a, 0x14, 0x3c, 0xcc, 0xdf, 0xea, 0x7d, 0xa8, 0xb2, 0x11, 0x4b, 0x21, + 0xc8, 0x8b, 0xa3, 0x49, 0x30, 0xcd, 0x00, 0xfa, 0x07, 0xca, 0x10, 0x0a, 0xfa, 0x37, 0x68, 0x4e, + 0xcb, 0xbf, 0xa1, 0x78, 0x89, 0x89, 0xe0, 0x23, 0x66, 0xb9, 0xd3, 0x6b, 0x61, 0x80, 0xa6, 0xdd, + 0x9d, 0xd5, 0x3b, 0xf2, 0x4d, 0xa4, 0x98, 0x21, 0xe1, 0xfe, 0xdc, 0xe0, 0x86, 0x78, 0xff, 0x31, + 0x96, 0x28, 0x73, 0x1d, 0x13, 0x27, 0x41, 0x83, 0x33, 0xda, 0xd5, 0xe1, 0xa7, 0x30, 0xdd, 0xe2, + 0xdf, 0x0b, 0x17, 0x91, 0xe7, 0xf1, 0x02, 0xa8, 0x79, 0xc1, 0xbd, 0xf5, 0x4b, 0x8c, 0xee, 0x3e, + 0x13, 0x27, 0x89, 0x43, 0x9a, 0xbb, 0xb7, 0x4e, 0x1c, 0x46, 0x29, 0x24, 0x95, 0xa9, 0x84, 0x5e, + 0x99, 0x48, 0x3b, 0xba, 0xb4, 0xb4, 0x69, 0x8f, 0x8a, 0xab, 0x62, 0xe7, 0x8e, 0xb8, 0x33, 0xe9, + 0xed, 0xef, 0x15, 0x16, 0x49, 0xde, 0xd1, 0x62, 0x9d, 0xbc, 0x9a, 0x75, 0xaa, 0x44, 0x01, 0x53, + 0xeb, 0xe6, 0x6d, 0x5d, 0x37, 0xb0, 0xfa, 0x15, 0x63, 0x52, 0x38, 0xdc, 0x1d, 0xb0, 0xdc, 0xdc, + 0x4b, 0xd6, 0x8a, 0x33, 0x07, 0xdb, 0x56, 0x8b, 0xc7, 0x0d, 0x5d, 0xad, 0xac, 0x89, 0xf3, 0x2d, + 0x88, 0x7c, 0x81, 0xfb, 0x91, 0x01, 0x49, 0x95, 0x4f, 0xad, 0x5d, 0x33, 0x9d, 0x41, 0xe4, 0xc5, + 0xf3, 0x20, 0x9a, 0xa2, 0xda, 0x81, 0x9b, 0x95, 0x41, 0xcb, 0x1d, 0x29, 0xb5, 0x35, 0x2e, 0x87, + 0xe6, 0xbb, 0x34, 0xd2, 0xbc, 0x3e, 0x26, 0xc2, 0xb5, 0x13, 0x7d, 0x1d, 0xbd, 0xca, 0x74, 0x67, + 0xbd, 0x6a, 0x34, 0x34, 0xe0, 0xe6, 0x32, 0x54, 0x76, 0xd5, 0xf1, 0x4a, 0x4a, 0xc1, 0x28, 0x48, + 0x6d, 0x56, 0x66, 0x97, 0x8a, 0xfc, 0x9c, 0xe7, 0xfa, 0x3c, 0x89, 0x65, 0xb9, 0x16, 0xf3, 0xdc, + 0x68, 0xc0, 0x5f, 0x7e, 0x66, 0xc5, 0x3a, 0x78, 0x67, 0x34, 0x16, 0xfc, 0xd2, 0xa8, 0xd6, 0xd4, + 0xaf, 0x14, 0xe4, 0x2c, 0x1e, 0xaa, 0x37, 0x76, 0xce, 0xd5, 0x55, 0xaa, 0x8b, 0x12, 0x72, 0xb8, + 0x9f, 0xdd, 0xd9, 0x96, 0x46, 0x50, 0x80, 0xe5, 0x6e, 0x87, 0x1b, 0xdb, 0x51, 0x65, 0x3e, 0xdf, + 0x57, 0x5a, 0x5a, 0x24, 0xc1, 0x3d, 0xec, 0x37, 0xdb, 0xf1, 0x66, 0xd4, 0xfb, 0x06, 0xb2, 0xea, + 0xab, 0xcc, 0x11, 0x4f, 0x2f, 0xe3, 0x34, 0x98, 0x08, 0x4d, 0x0c, 0xc3, 0xe5, 0x98, 0x14, 0x76, + 0xff, 0x62, 0x96, 0xb8, 0x0c, 0x63, 0xa5, 0x4c, 0x1c, 0x4f, 0xec, 0x78, 0xb5, 0xbb, 0xad, 0xcc, + 0xf9, 0x23, 0x0b, 0xbc, 0x6f, 0x43, 0x5e, 0x88, 0x5f, 0x09, 0xf5, 0x38, 0x7f, 0xd0, 0x71, 0xf1, + 0xf8, 0xe0, 0xb0, 0xa5, 0xfe, 0x07, 0xd4, 0x32, 0x78, 0xdb, 0xc6, 0xeb, 0xa7, 0x70, 0xaf, 0x05, + 0x1b, 0x66, 0x0a, 0xa5, 0x84, 0x77, 0x5a, 0x77, 0x2a, 0x94, 0xc4, 0xb0, 0x75, 0x4c, 0x32, 0x05, + 0x91, 0xe3, 0x82, 0x53, 0xd5, 0xc3, 0x13, 0x32, 0xc5, 0x58, 0x98, 0x40, 0xd7, 0x78, 0x1b, 0xbd, + 0x90, 0x4c, 0xc5, 0xdc, 0xcc, 0xc8, 0x5c, 0xee, 0xb0, 0x29, 0x3a, 0xf8, 0xf2, 0xdf, 0x73, 0xb0, + 0xf0, 0x43, 0xd4, 0x9d, 0x17, 0xe3, 0x03, 0x7e, 0x61, 0x9a, 0xc8, 0x01, 0x51, 0xcd, 0x78, 0x94, + 0x52, 0x31, 0xe5, 0x52, 0x00, 0x01, 0x36, 0x4c, 0x3d, 0xac, 0x61, 0xc2, 0x46, 0x70, 0x73, 0xa8, + 0xb5, 0x9c, 0x05, 0x0b, 0x88, 0x1b, 0xcf, 0x83, 0x5e, 0x55, 0x5e, 0x70, 0xa6, 0xac, 0x8b, 0xc1, + 0x9a, 0xd1, 0xe1, 0x0d, 0x5c, 0xdc, 0x08, 0x20, 0x86, 0x08, 0xaa, 0xfa, 0x41, 0xee, 0x21, 0xbf, + 0x14, 0x02, 0xc1, 0xfc, 0xab, 0x70, 0xb0, 0xc3, 0x4f, 0x4a, 0x89, 0x72, 0xc8, 0x2d, 0xf8, 0xec, + 0x9d, 0x85, 0xa1, 0x65, 0x3a, 0x5c, 0x57, 0x69, 0xe6, 0x0b, 0xc4, 0x8a, 0x91, 0xe9, 0xd3, 0x78, + 0x3a, 0x0d, 0x41, 0x85, 0xce, 0x4d, 0xa3, 0x84, 0xa9, 0x39, 0xe6, 0x35, 0x31, 0xfc, 0xb8, 0xbd, + 0xb5, 0x8b, 0xc8, 0xbd, 0x37, 0x94, 0x66, 0xe4, 0xbb, 0xbb, 0x11, 0xcb, 0x92, 0xaa, 0xb8, 0x69, + 0x4f, 0xa0, 0xd0, 0xec, 0xdc, 0x9a, 0x57, 0xf7, 0x34, 0xe1, 0x31, 0xb9, 0xcc, 0xcb, 0xf3, 0xab, + 0xcb, 0xe1, 0xd5, 0xe0, 0x97, 0xf3, 0x9b, 0xcf, 0x03, 0x53, 0xcf, 0xee, 0x28, 0xbd, 0xb7, 0xd0, + 0xfc, 0xf3, 0xf1, 0xec, 0xf2, 0xf2, 0xfc, 0xe2, 0xda, 0xd8, 0x31, 0x84, 0xa7, 0x4a, 0x9e, 0xfb, + 0x51, 0xb8, 0xeb, 0x41, 0x53, 0x57, 0xef, 0xcf, 0xa1, 0xc8, 0xc5, 0xd5, 0xe0, 0xec, 0xe6, 0xd3, + 0xd5, 0xe5, 0xb5, 0x79, 0x27, 0x72, 0x41, 0xde, 0x72, 0x03, 0x13, 0x5c, 0xac, 0x9f, 0xbe, 0x9e, + 0x1b, 0x5f, 0x3e, 0x98, 0x79, 0x4a, 0xc7, 0xce, 0xad, 0x79, 0x03, 0x5a, 0xe2, 0x44, 0x94, 0xb8, + 0xbe, 0xb9, 0xfa, 0x72, 0xf6, 0xf3, 0x39, 0x31, 0x86, 0x57, 0xbf, 0x9d, 0x7f, 0xc1, 0x1e, 0xaf, + 0x87, 0x17, 0x67, 0xbf, 0x9b, 0x77, 0xc8, 0x1e, 0xf2, 0xa9, 0x93, 0x1a, 0x5c, 0x7a, 0x9b, 0xde, + 0xf5, 0x9d, 0xdb, 0x26, 0x30, 0xa1, 0x32, 0x7c, 0x26, 0xe7, 0xc2, 0x45, 0xb9, 0x6f, 0x81, 0xf7, + 0x0d, 0x20, 0x3f, 0xa2, 0x60, 0x0b, 0x0a, 0x16, 0x3e, 0x59, 0x28, 0x62, 0x95, 0x5f, 0x19, 0xc4, + 0xa2, 0xcd, 0x03, 0xa2, 0x7f, 0x65, 0xba, 0x73, 0x33, 0x2f, 0x2a, 0x37, 0x39, 0x6e, 0x23, 0x7c, + 0xc2, 0xa7, 0x62, 0xe3, 0xbe, 0x07, 0x77, 0x48, 0x2c, 0x27, 0x3f, 0xd9, 0xdc, 0xd9, 0xc1, 0xc8, + 0x7e, 0x9c, 0x97, 0x57, 0x4e, 0xa6, 0xd6, 0x53, 0x1a, 0x43, 0x9a, 0xb0, 0x31, 0x9d, 0xb9, 0xf7, + 0x41, 0x9c, 0x74, 0x4c, 0x36, 0x8f, 0xe3, 0x74, 0x66, 0xae, 0x2b, 0x51, 0x14, 0x7f, 0xc6, 0x48, + 0xf1, 0xa9, 0x03, 0x51, 0xcd, 0x40, 0x1e, 0xa3, 0x13, 0x37, 0x0b, 0x53, 0x4b, 0x8b, 0xc4, 0x50, + 0xf2, 0x4d, 0x25, 0x10, 0xbb, 0x0e, 0xcf, 0xb3, 0xd0, 0x70, 0x92, 0x8a, 0xe7, 0x36, 0xc6, 0x14, + 0x59, 0xad, 0x54, 0x16, 0x14, 0x78, 0x70, 0x9f, 0x3a, 0x3c, 0xde, 0xa4, 0xc5, 0xa4, 0x9b, 0xdb, + 0xdb, 0x6b, 0x95, 0x55, 0xe6, 0xc2, 0x52, 0x2e, 0x7e, 0x01, 0x33, 0x20, 0x0d, 0xa1, 0xd1, 0x3a, + 0x6c, 0x1a, 0xe3, 0x65, 0x8a, 0xbb, 0x46, 0x66, 0x65, 0xe4, 0x1c, 0x32, 0x44, 0xec, 0x86, 0xc8, + 0x14, 0x79, 0x97, 0x22, 0xbd, 0x9a, 0x16, 0x89, 0x7b, 0x8b, 0x07, 0x9e, 0x16, 0x92, 0x1b, 0xdc, + 0x89, 0x55, 0x4c, 0x6e, 0x26, 0xcc, 0xf3, 0x2a, 0xe5, 0x59, 0x6b, 0x0d, 0x86, 0x17, 0x65, 0x2e, + 0x1b, 0xca, 0x90, 0x91, 0x1c, 0x8b, 0xc2, 0xe3, 0x2a, 0xfa, 0x1c, 0xac, 0x14, 0x49, 0x4f, 0xfe, + 0xfe, 0x47, 0xbc, 0xca, 0x85, 0x6b, 0xf9, 0x8d, 0x16, 0xac, 0x59, 0x15, 0xd1, 0xa2, 0x90, 0x13, + 0x66, 0x77, 0x78, 0x85, 0x52, 0x49, 0x0e, 0x12, 0xd6, 0x34, 0xab, 0xcc, 0xe6, 0x69, 0x60, 0x10, + 0xca, 0x61, 0xa2, 0x28, 0xec, 0x9b, 0xe4, 0x95, 0x6a, 0x74, 0x67, 0xa7, 0x92, 0x5f, 0x2d, 0xe9, + 0x97, 0x13, 0xac, 0xed, 0xec, 0x58, 0x71, 0xe9, 0x59, 0x2f, 0x29, 0x17, 0xe2, 0xa1, 0xe9, 0xb5, + 0x05, 0x11, 0x9d, 0xc9, 0xc0, 0x93, 0xf9, 0x28, 0x64, 0x9e, 0xc8, 0x72, 0x85, 0x4a, 0xfe, 0xc8, + 0xb8, 0xe0, 0x1c, 0x3c, 0xcc, 0x51, 0x53, 0xf9, 0xa6, 0xa8, 0xf1, 0x81, 0xc6, 0x89, 0xd7, 0x5d, + 0x22, 0x85, 0xdf, 0xb3, 0xc6, 0xde, 0xa7, 0xa0, 0x68, 0x78, 0xfd, 0x74, 0x9d, 0xae, 0xf9, 0x56, + 0x19, 0xd9, 0x9a, 0x7f, 0xb4, 0x28, 0x57, 0x50, 0x14, 0xbd, 0xbd, 0x4e, 0x73, 0x50, 0x9a, 0x1e, + 0x2b, 0x88, 0xe7, 0x21, 0x74, 0xe2, 0xc8, 0x72, 0x9d, 0x2f, 0x94, 0x2d, 0xe2, 0x88, 0x51, 0xc8, + 0xfe, 0xc8, 0x1c, 0x11, 0x3e, 0xe4, 0x33, 0x9b, 0x02, 0x5a, 0x8e, 0xd0, 0xde, 0xdb, 0xa7, 0x1b, + 0x1e, 0x6a, 0xfb, 0xa9, 0x18, 0x86, 0xbf, 0x43, 0x31, 0xbf, 0x4a, 0x7d, 0x23, 0x22, 0x22, 0x7d, + 0x5d, 0x23, 0x85, 0x60, 0xf5, 0x7a, 0x23, 0x5a, 0x16, 0x11, 0xee, 0xc7, 0xfa, 0x2a, 0x55, 0x01, + 0x66, 0x53, 0x3d, 0xc0, 0xec, 0x6a, 0x65, 0x41, 0x5e, 0xcf, 0xbc, 0x38, 0x2e, 0x07, 0xb7, 0xc4, + 0xeb, 0x0f, 0x4b, 0xf9, 0x4c, 0x1e, 0x2c, 0x73, 0x1c, 0xe7, 0xaa, 0xb4, 0x3c, 0x07, 0x0a, 0xb7, + 0x4b, 0x9a, 0x36, 0x69, 0x03, 0x56, 0xda, 0xa7, 0xd6, 0x30, 0xb5, 0xb5, 0xac, 0xb5, 0x90, 0xe1, + 0x76, 0x6d, 0x6b, 0xd9, 0x5d, 0x2f, 0xc0, 0xd6, 0x14, 0x40, 0xd0, 0x57, 0x9e, 0x6f, 0xb5, 0xbc, + 0x01, 0xec, 0xfc, 0x93, 0x40, 0xfd, 0x50, 0xfc, 0xaa, 0x42, 0x46, 0x52, 0x59, 0x2c, 0x4f, 0x5a, + 0x8c, 0x98, 0x25, 0xfe, 0xf9, 0x50, 0x12, 0xff, 0xc4, 0x55, 0x7e, 0x66, 0xe8, 0x17, 0x56, 0x21, + 0x35, 0x8a, 0xc7, 0x53, 0xc6, 0x33, 0x6e, 0x47, 0xe4, 0x71, 0x10, 0x8d, 0x01, 0x18, 0x78, 0x8d, + 0x20, 0x45, 0xcb, 0x58, 0x64, 0xa4, 0xcf, 0xe4, 0x86, 0x15, 0x41, 0xd2, 0xab, 0x63, 0x14, 0x32, + 0x83, 0x1a, 0x64, 0x25, 0x4b, 0xac, 0x79, 0x81, 0x11, 0x7e, 0xf3, 0x94, 0xcd, 0x00, 0x27, 0x35, + 0x8a, 0x69, 0x96, 0x37, 0x37, 0xaf, 0xf2, 0x36, 0xe7, 0x3d, 0x94, 0x96, 0x1a, 0x9b, 0x97, 0xc5, + 0x44, 0xdb, 0xb0, 0x12, 0xbc, 0xff, 0x17, 0x2d, 0xe0, 0x17, 0x5a, 0xbf, 0x74, 0xf9, 0x8e, 0x1e, + 0x66, 0x60, 0x95, 0x82, 0xdd, 0xfc, 0x99, 0x4d, 0x05, 0x0c, 0x99, 0x8f, 0x28, 0x95, 0x69, 0xa7, + 0x7d, 0x6a, 0x7d, 0x64, 0xa5, 0x86, 0x25, 0x55, 0x78, 0x79, 0xf3, 0x97, 0xf4, 0x81, 0xbb, 0x6a, + 0xe2, 0x39, 0x51, 0xc9, 0x60, 0xf4, 0x58, 0xfc, 0x9f, 0x22, 0x9f, 0x3e, 0x42, 0x84, 0xa9, 0x04, + 0xc8, 0x65, 0x1e, 0x90, 0xbf, 0xd7, 0xeb, 0x15, 0x3d, 0xd8, 0xc1, 0x24, 0xf0, 0xb6, 0xd7, 0xec, + 0x6b, 0x29, 0x44, 0xd8, 0x5d, 0x8f, 0x6a, 0x39, 0x46, 0x9c, 0x2c, 0x62, 0xb3, 0x60, 0x02, 0xbe, + 0xf8, 0x3c, 0x42, 0xff, 0xef, 0x9c, 0x9c, 0x81, 0x82, 0x4a, 0xf3, 0x23, 0x38, 0xe3, 0x0e, 0xf3, + 0x6b, 0x8c, 0xd3, 0x8c, 0xb7, 0xf4, 0xa8, 0xbc, 0x64, 0xfa, 0x37, 0x88, 0x0f, 0xa8, 0x4c, 0xcf, + 0x87, 0xc2, 0xf4, 0x3c, 0xad, 0x6b, 0x2b, 0x5f, 0xc3, 0x0d, 0x02, 0x6a, 0x82, 0x64, 0x4e, 0x7d, + 0x6d, 0x12, 0x6e, 0x8b, 0xd9, 0x28, 0xb5, 0x5c, 0x61, 0x32, 0x41, 0x4e, 0x54, 0x0d, 0x97, 0xb1, + 0xb3, 0x13, 0xd5, 0xe4, 0x41, 0xe8, 0x98, 0x36, 0xbc, 0xc8, 0x23, 0x70, 0x08, 0x2a, 0x0f, 0x0f, + 0x4b, 0x37, 0x04, 0xe4, 0x42, 0x71, 0xbd, 0x6f, 0x30, 0x34, 0xbb, 0xcb, 0x30, 0xc3, 0x81, 0xbc, + 0x01, 0x84, 0x6e, 0x4c, 0x5d, 0x02, 0xc8, 0x19, 0x5f, 0x58, 0x32, 0xa0, 0x8c, 0x18, 0xa9, 0x91, + 0x17, 0xab, 0x5f, 0xf0, 0x8b, 0x78, 0xfa, 0xe5, 0xf1, 0x3d, 0x04, 0xf8, 0xa7, 0xe8, 0xad, 0xcf, + 0x63, 0xfe, 0x3f, 0xe1, 0xd7, 0xe2, 0x6f, 0xd0, 0xe8, 0x11, 0x05, 0xa4, 0xcf, 0xf1, 0xf0, 0x44, + 0xa0, 0xd8, 0x3b, 0x0a, 0xcf, 0xbe, 0x26, 0x95, 0x04, 0x00, 0xf4, 0xbb, 0x42, 0xff, 0xf3, 0x7c, + 0x2c, 0xbf, 0x73, 0xd8, 0xf1, 0x77, 0x33, 0xd4, 0x18, 0xa6, 0x79, 0x6b, 0x30, 0x02, 0x90, 0x0a, + 0x0a, 0x6c, 0x35, 0x58, 0xad, 0xb6, 0xf4, 0x73, 0x0b, 0x65, 0x1b, 0x00, 0xab, 0xb8, 0xfb, 0xf3, + 0xfd, 0x40, 0x75, 0xe8, 0xa7, 0x1a, 0x17, 0xa9, 0xae, 0x11, 0xc4, 0x1c, 0x02, 0xa6, 0x01, 0x82, + 0x2c, 0xa5, 0x42, 0xc3, 0x29, 0xae, 0x63, 0x6c, 0x43, 0x0b, 0x40, 0x52, 0xad, 0xce, 0xb2, 0xf1, + 0x3c, 0x48, 0x4d, 0xf2, 0x73, 0x82, 0x65, 0xcb, 0xb7, 0x7a, 0xb5, 0x02, 0x7f, 0x43, 0x86, 0x31, + 0x96, 0x17, 0x21, 0x35, 0x44, 0x50, 0xea, 0xfa, 0x01, 0xe6, 0x5f, 0x29, 0x4e, 0x94, 0x22, 0x6d, + 0x17, 0x96, 0x8c, 0xf9, 0x21, 0x1a, 0xc0, 0x3d, 0x57, 0xa6, 0x74, 0xf8, 0xa6, 0xe6, 0x96, 0x7a, + 0x7e, 0x1d, 0xc0, 0x55, 0x14, 0x0c, 0xf5, 0xdf, 0xb7, 0x16, 0x4f, 0xb9, 0x5b, 0x6d, 0xbe, 0x26, + 0xe2, 0x09, 0xf9, 0x53, 0x7d, 0x42, 0xf2, 0xd5, 0x2d, 0x82, 0x1b, 0x15, 0x02, 0xe1, 0x46, 0x29, + 0x62, 0xcd, 0x17, 0xb2, 0x12, 0xfa, 0xfb, 0xb9, 0xc9, 0x2e, 0xdc, 0x23, 0x17, 0x96, 0x79, 0x0d, + 0x44, 0x1d, 0xb6, 0x43, 0x48, 0x41, 0x5c, 0x2a, 0xcd, 0xf4, 0x75, 0x3d, 0x33, 0xd0, 0xad, 0xb1, + 0xed, 0x6f, 0xec, 0x38, 0xcf, 0x91, 0xbe, 0x15, 0x11, 0xa0, 0xc7, 0x76, 0x17, 0x3c, 0x39, 0x67, + 0xac, 0xd1, 0xe9, 0x81, 0x53, 0x63, 0xd0, 0xec, 0x03, 0x5b, 0xc0, 0x5d, 0xab, 0x25, 0x0f, 0x2d, + 0x89, 0x38, 0xe7, 0x6e, 0xf3, 0xf7, 0x1b, 0xb3, 0xa0, 0x6f, 0x1d, 0x47, 0x0b, 0x12, 0x33, 0xd4, + 0xe5, 0x4f, 0xdb, 0x3a, 0xb3, 0x16, 0xdc, 0xc8, 0x44, 0x43, 0x35, 0xf4, 0xc5, 0x5c, 0xbf, 0x60, + 0x3a, 0x3b, 0x5a, 0x92, 0xf7, 0x62, 0xf9, 0x41, 0x9c, 0x85, 0xbe, 0x48, 0x2a, 0x20, 0x8a, 0xe8, + 0xcb, 0x20, 0x8e, 0x5b, 0x2d, 0x52, 0x72, 0xe3, 0x68, 0x6f, 0xe2, 0x42, 0x35, 0x8e, 0x9a, 0x6b, + 0x20, 0x52, 0x6e, 0x5b, 0xb5, 0xdf, 0x58, 0xa1, 0x9a, 0xe7, 0x46, 0x1e, 0x0d, 0xbf, 0xa3, 0x82, + 0x1f, 0x47, 0xdf, 0xd3, 0xbe, 0x42, 0x20, 0x6e, 0x3f, 0x96, 0x15, 0x10, 0x60, 0x0e, 0x83, 0x44, + 0xf7, 0x0d, 0xe1, 0x6c, 0x99, 0x83, 0x21, 0xbb, 0xe2, 0x19, 0xed, 0x73, 0xdd, 0x57, 0xe7, 0x3b, + 0x70, 0x92, 0xb4, 0x6f, 0x7e, 0x0c, 0x7c, 0xc0, 0xb5, 0xff, 0x39, 0xc0, 0xa4, 0x84, 0x2b, 0xc1, + 0x15, 0xda, 0xad, 0x22, 0x0d, 0x37, 0x13, 0x62, 0x2e, 0xf0, 0xa7, 0xc8, 0x88, 0x6c, 0x96, 0xf2, + 0xeb, 0xe0, 0xa6, 0x84, 0xf5, 0xb6, 0x40, 0x48, 0x49, 0xa4, 0xc9, 0x6f, 0x4a, 0x50, 0x23, 0x71, + 0xaf, 0x46, 0xd4, 0xc7, 0x70, 0xf6, 0x6f, 0x5a, 0xab, 0x55, 0xf4, 0xf6, 0xa0, 0xad, 0x09, 0xfd, + 0x1a, 0xa6, 0x6b, 0x0e, 0x59, 0x96, 0xc7, 0xd4, 0x68, 0xfd, 0xe3, 0x3f, 0xfe, 0xfb, 0xa0, 0x5d, + 0x16, 0xfd, 0x37, 0x03, 0x3b, 0xa1, 0xe9, 0xf8, 0x15, 0x26, 0xbe, 0xb5, 0xe2, 0x37, 0xa7, 0xab, + 0x55, 0xfc, 0xf6, 0xf8, 0xd0, 0xd6, 0xfa, 0x18, 0x4a, 0xa0, 0x98, 0xec, 0x01, 0x15, 0xfd, 0x80, + 0x22, 0x3c, 0xfd, 0xc7, 0x7f, 0xfc, 0xf7, 0xf1, 0xe1, 0xb6, 0xae, 0xb4, 0x6f, 0x96, 0xdd, 0xe9, + 0x03, 0x71, 0xef, 0xe9, 0x46, 0x2a, 0x74, 0x1f, 0x5b, 0x3c, 0x5d, 0x4d, 0x8e, 0x21, 0x46, 0x5c, + 0x2e, 0x25, 0xb2, 0xd1, 0x0e, 0x5b, 0xdb, 0xe4, 0x2c, 0xb6, 0xf2, 0x8c, 0x38, 0xc8, 0xcb, 0x6b, + 0xf0, 0x7d, 0xe8, 0xc0, 0x77, 0x0c, 0x2e, 0xc9, 0x81, 0xdc, 0xc2, 0x41, 0x6e, 0x41, 0x34, 0x75, + 0x6a, 0x08, 0x57, 0x7e, 0xde, 0xa1, 0x9e, 0x6c, 0x42, 0x66, 0x4c, 0xa9, 0x25, 0x64, 0x1b, 0xbf, + 0x44, 0xd2, 0xb1, 0x1a, 0xcc, 0xf3, 0x0b, 0x48, 0x35, 0x80, 0x7e, 0x38, 0xff, 0x0a, 0xd7, 0x2d, + 0xcf, 0xb8, 0xac, 0x43, 0x3e, 0x0b, 0x38, 0xcf, 0xbe, 0x01, 0x88, 0x3f, 0x61, 0x0a, 0x7d, 0x08, + 0xc2, 0x30, 0xc7, 0xf2, 0xd9, 0x68, 0x66, 0xd8, 0x86, 0xbb, 0xde, 0x30, 0xe5, 0xee, 0xc2, 0x5c, + 0xdb, 0x04, 0x90, 0xd8, 0x38, 0xc1, 0x18, 0x12, 0x03, 0x28, 0xa8, 0xec, 0x57, 0x5c, 0x8c, 0x7f, + 0x6a, 0x72, 0x65, 0x0a, 0xe9, 0xfc, 0x33, 0xea, 0xe7, 0x76, 0xeb, 0xb0, 0x5b, 0x6b, 0x79, 0x51, + 0xd4, 0x63, 0xd7, 0xb6, 0x13, 0x2e, 0x50, 0xb0, 0x59, 0x55, 0x54, 0xdb, 0x6a, 0xf5, 0x4a, 0x4d, + 0xfb, 0x00, 0xda, 0xe5, 0xc8, 0xf8, 0x70, 0x59, 0x32, 0x65, 0x19, 0xa2, 0x3b, 0x81, 0xc5, 0xc5, + 0xcc, 0x33, 0x3e, 0xed, 0xc3, 0xa5, 0xf0, 0xb4, 0x2d, 0x35, 0xfd, 0x7b, 0x3d, 0x35, 0x7d, 0xde, + 0x7b, 0xef, 0xf6, 0x4e, 0x0a, 0x4d, 0x62, 0xf7, 0x96, 0xfb, 0xc1, 0x8f, 0x44, 0x1d, 0x12, 0xff, + 0x68, 0x65, 0xc2, 0x90, 0xbb, 0xf3, 0x9f, 0x42, 0xc5, 0x40, 0x3c, 0x7a, 0x96, 0x9b, 0xe2, 0x46, + 0x89, 0x3b, 0xd3, 0xbe, 0x43, 0x03, 0x39, 0x6a, 0xc0, 0x39, 0x6b, 0xab, 0x74, 0x7b, 0x32, 0x9f, + 0xd3, 0x96, 0xf4, 0x43, 0x5a, 0xf6, 0x9d, 0x5f, 0x5f, 0x9a, 0x7c, 0x07, 0x13, 0x39, 0x89, 0x74, + 0x3a, 0xff, 0x72, 0x7d, 0x75, 0xe9, 0xf0, 0x44, 0x54, 0xc1, 0x64, 0x69, 0x3d, 0x95, 0x6c, 0x64, + 0x32, 0x01, 0xf2, 0x33, 0x36, 0x36, 0x9b, 0x14, 0xac, 0x64, 0x75, 0xb5, 0x4a, 0xf6, 0x35, 0x9b, + 0xe8, 0xd6, 0xb1, 0xba, 0x0a, 0xb5, 0xc6, 0x35, 0x9b, 0x54, 0xad, 0x62, 0x75, 0x95, 0x37, 0x59, + 0xd7, 0xb4, 0xfa, 0x9a, 0x35, 0x6c, 0x7b, 0x0b, 0x15, 0xd3, 0x9a, 0x4d, 0xea, 0xec, 0x62, 0x9d, + 0xad, 0xe6, 0x34, 0xa2, 0xdb, 0xc0, 0xb8, 0x6e, 0xad, 0x5b, 0x93, 0xdf, 0x89, 0xaf, 0x37, 0x13, + 0x99, 0xa4, 0xb8, 0x31, 0x07, 0xc4, 0x58, 0x09, 0xcf, 0xe0, 0xc5, 0x38, 0x39, 0xde, 0x42, 0x71, + 0xfd, 0x62, 0x79, 0x8d, 0x2c, 0x94, 0xb6, 0x3d, 0x7d, 0x04, 0xc5, 0xce, 0xb3, 0xc7, 0x5c, 0x68, + 0xf5, 0x25, 0xa0, 0xcb, 0x71, 0x19, 0xf8, 0x98, 0x96, 0x37, 0xa5, 0x6c, 0xac, 0xd4, 0x07, 0x9e, + 0xb9, 0xef, 0x27, 0xd7, 0x43, 0x9a, 0xcc, 0xdd, 0x88, 0x46, 0x69, 0xb8, 0x34, 0x28, 0xda, 0x00, + 0x81, 0x72, 0xef, 0x47, 0x8b, 0x7d, 0x75, 0xaa, 0xff, 0x2d, 0xce, 0x92, 0xc8, 0x0d, 0xfb, 0x86, + 0xc2, 0x29, 0xcb, 0xdc, 0x3c, 0x3c, 0xc4, 0xb2, 0x8a, 0xb7, 0x9c, 0x50, 0x0e, 0x71, 0x48, 0xe0, + 0x16, 0x33, 0x6d, 0x3b, 0xa7, 0xd4, 0x1b, 0x13, 0x82, 0x69, 0x27, 0xeb, 0xfd, 0xf9, 0xc5, 0xf9, + 0xcd, 0xb9, 0x7e, 0xb8, 0xfe, 0x67, 0xe3, 0x72, 0xd8, 0x10, 0x8a, 0x11, 0xb3, 0xc3, 0x49, 0xa7, + 0xb9, 0x5e, 0x17, 0x72, 0x1c, 0x5d, 0x58, 0xe6, 0x95, 0x02, 0xd6, 0x94, 0xe9, 0x4f, 0x95, 0xac, + 0x7f, 0xd4, 0x4b, 0x54, 0x79, 0xfe, 0xb5, 0xdd, 0x2d, 0xd9, 0x4b, 0x06, 0xb1, 0x8c, 0x00, 0x58, + 0x80, 0xe7, 0x89, 0x25, 0x52, 0x09, 0x6d, 0x38, 0x2c, 0x4e, 0x9a, 0x1b, 0x8f, 0x79, 0x88, 0x44, + 0xaa, 0x3f, 0x00, 0xdb, 0xfc, 0xdf, 0xff, 0x4f, 0x01, 0xbe, 0xc6, 0x00, 0x66, 0x37, 0xd2, 0x7c, + 0x00, 0xaa, 0xed, 0xb3, 0x19, 0x40, 0xbf, 0xf3, 0xfd, 0x1d, 0x83, 0x97, 0x35, 0x5a, 0xbd, 0x64, + 0x11, 0xe3, 0xb7, 0x4f, 0x17, 0x17, 0xc0, 0xe6, 0x04, 0x91, 0x17, 0x66, 0x3e, 0xf5, 0xc1, 0xdd, + 0x41, 0x05, 0x6a, 0xef, 0x94, 0x0a, 0x8b, 0xfb, 0x76, 0xee, 0x06, 0x91, 0x21, 0x4e, 0x8f, 0x63, + 0x76, 0xe5, 0x6e, 0x18, 0x5d, 0xc3, 0xb6, 0x4e, 0x0b, 0x35, 0xd2, 0x58, 0xad, 0x7b, 0xff, 0x87, + 0x1f, 0x9e, 0xfb, 0xca, 0x1f, 0xce, 0x3c, 0x2f, 0x4b, 0x5c, 0x6f, 0x59, 0xfb, 0xad, 0x50, 0x3f, + 0x5e, 0x8f, 0xc0, 0x30, 0xb1, 0x69, 0x5f, 0xc8, 0x7e, 0xff, 0xe9, 0x24, 0x37, 0x74, 0xd3, 0x20, + 0xcd, 0x7c, 0xda, 0x49, 0x21, 0x58, 0xcd, 0x94, 0xff, 0xa6, 0xc4, 0x15, 0xe3, 0xed, 0x30, 0xc2, + 0x27, 0x2d, 0x60, 0xb4, 0x13, 0xa1, 0x9e, 0xee, 0xc2, 0x8a, 0xb8, 0x77, 0x25, 0xce, 0x03, 0x12, + 0x03, 0xdc, 0xf0, 0x34, 0xc2, 0x4b, 0xbd, 0x90, 0xf2, 0xa7, 0x53, 0x2e, 0x28, 0x26, 0x37, 0x5c, + 0x82, 0x0e, 0xac, 0x6e, 0x69, 0x5f, 0x28, 0xad, 0xe4, 0xa7, 0x56, 0x24, 0xb0, 0x4a, 0xe2, 0xf9, + 0x22, 0xb5, 0xcc, 0x77, 0xc2, 0xaf, 0x43, 0xad, 0x53, 0x50, 0xf0, 0xd8, 0xe3, 0xe8, 0xa3, 0x80, + 0x49, 0x7b, 0x00, 0x3a, 0x77, 0x9c, 0x83, 0x42, 0xda, 0x90, 0x13, 0xa1, 0x4d, 0x03, 0xfa, 0x52, + 0x89, 0xe4, 0xa2, 0x39, 0x64, 0xfb, 0x96, 0x91, 0xe8, 0xae, 0x47, 0xa5, 0x5f, 0x04, 0x31, 0x39, + 0xc6, 0x8c, 0x53, 0xf1, 0x7a, 0x28, 0x33, 0xb3, 0x57, 0xab, 0xca, 0xc3, 0x48, 0xe7, 0xce, 0x37, + 0x8d, 0x41, 0xb3, 0xfa, 0xe5, 0x64, 0x63, 0x00, 0x11, 0xa3, 0x22, 0x48, 0x4b, 0xaf, 0x0e, 0x70, + 0x61, 0x9a, 0x6b, 0x0e, 0x30, 0x0e, 0x2b, 0x72, 0xef, 0x83, 0xa9, 0x9b, 0xc6, 0x89, 0x33, 0xa5, + 0xb1, 0x8a, 0xce, 0x24, 0x19, 0x39, 0xcb, 0xee, 0xd6, 0x16, 0x40, 0x28, 0x3e, 0x77, 0x57, 0x1c, + 0xc6, 0x2c, 0xc0, 0x68, 0xd7, 0xb4, 0xf7, 0x76, 0x10, 0x5b, 0xd4, 0xf1, 0x62, 0x48, 0x5c, 0xe3, + 0xa8, 0x71, 0xe7, 0x4f, 0xe4, 0x07, 0xe4, 0x8f, 0xe4, 0x96, 0x2a, 0x89, 0xe7, 0xdb, 0x47, 0x6e, + 0x93, 0x94, 0x3c, 0xf1, 0x9d, 0xf5, 0x31, 0x98, 0xce, 0xe4, 0x31, 0xea, 0xbc, 0x6a, 0xa2, 0x95, + 0x33, 0xce, 0xd2, 0x4e, 0xab, 0x4d, 0x0f, 0xc8, 0xdc, 0x7d, 0x0c, 0xe6, 0xd9, 0xfc, 0x6c, 0x4a, + 0x3b, 0xcd, 0x75, 0xf9, 0x0e, 0x88, 0x53, 0xf7, 0x9f, 0x25, 0x1f, 0x16, 0xdb, 0xe4, 0xbc, 0x01, + 0xfc, 0x03, 0x29, 0xb9, 0xf9, 0x4e, 0x59, 0xad, 0x5e, 0x51, 0x44, 0x9d, 0x39, 0x69, 0x7c, 0x01, + 0x5c, 0x04, 0x47, 0x53, 0x3a, 0x34, 0xf2, 0x85, 0xda, 0xd9, 0x89, 0x16, 0x99, 0xa9, 0x2f, 0xbe, + 0x8a, 0xe0, 0x7d, 0x49, 0xe3, 0x68, 0x18, 0x03, 0x06, 0xcc, 0x80, 0x42, 0x88, 0x0c, 0x83, 0xc4, + 0x64, 0xf9, 0x26, 0x80, 0x2e, 0x14, 0x59, 0xfa, 0x0a, 0x01, 0x6d, 0xf8, 0x6d, 0x03, 0x4e, 0xae, + 0x6e, 0xc8, 0xbd, 0x5d, 0x30, 0xda, 0x35, 0x17, 0x27, 0xb8, 0xa8, 0xa1, 0x4b, 0x13, 0x46, 0x1c, + 0x85, 0x4b, 0xe1, 0x2c, 0x84, 0x39, 0x35, 0x88, 0xf0, 0x8a, 0x9a, 0xb9, 0x6c, 0x46, 0xb0, 0x2d, + 0x8c, 0xe0, 0x00, 0x5e, 0x40, 0x06, 0x92, 0x57, 0x86, 0x3e, 0x46, 0xce, 0xc8, 0xd6, 0xdd, 0x16, + 0x58, 0x0f, 0x1c, 0xe0, 0x3e, 0xc4, 0xc9, 0x1c, 0x14, 0xd6, 0x79, 0x60, 0x73, 0x53, 0x0d, 0x19, + 0x56, 0x1e, 0xa3, 0xe8, 0xd7, 0xd0, 0x71, 0x6d, 0x41, 0x36, 0x84, 0x6e, 0xee, 0x46, 0x15, 0xe1, + 0x86, 0x5b, 0xbd, 0xab, 0xa9, 0x20, 0x15, 0x8d, 0x8c, 0x53, 0xb7, 0x92, 0x0f, 0x12, 0x69, 0x1e, + 0xdb, 0x96, 0x8f, 0x91, 0x48, 0x1b, 0x76, 0x2c, 0xf8, 0x1e, 0x6d, 0x7b, 0x5a, 0x4f, 0xa0, 0x21, + 0x84, 0x39, 0x8f, 0x9d, 0xf8, 0xdb, 0x6a, 0x05, 0xd9, 0xd9, 0xc0, 0x0d, 0x0d, 0x5d, 0x62, 0x33, + 0xcf, 0x03, 0x5f, 0xde, 0x4a, 0x1a, 0x47, 0x59, 0x68, 0xb5, 0x32, 0xb3, 0x45, 0x18, 0xbb, 0x7e, + 0x43, 0x6c, 0x6a, 0xbb, 0x0b, 0xb2, 0x97, 0x9a, 0xdc, 0x7b, 0x58, 0xbd, 0x00, 0x65, 0x2f, 0x25, + 0x72, 0x81, 0x71, 0x2c, 0x56, 0xfe, 0x85, 0xee, 0x62, 0x51, 0x96, 0xc1, 0xac, 0xd8, 0x7e, 0xba, + 0xb0, 0x46, 0xbf, 0xf2, 0x0c, 0x91, 0x09, 0xfd, 0x37, 0xd4, 0x93, 0x75, 0x8c, 0xd7, 0x4f, 0xb1, + 0x14, 0x44, 0x20, 0x91, 0xb1, 0x94, 0xe1, 0xa3, 0x8a, 0x2c, 0xcb, 0x79, 0xa9, 0xea, 0x31, 0x48, + 0x28, 0x0b, 0xfe, 0x9d, 0x2a, 0x45, 0xad, 0x04, 0x20, 0x01, 0xbe, 0xc5, 0xdc, 0xd9, 0x29, 0xc1, + 0x92, 0x14, 0x3e, 0x03, 0x62, 0x2f, 0xd6, 0xc0, 0x95, 0x2c, 0x15, 0xb4, 0x44, 0x7f, 0xab, 0xa1, + 0x35, 0x30, 0x81, 0xa8, 0xa6, 0x68, 0xae, 0x8e, 0xe7, 0x3e, 0x60, 0xc1, 0x38, 0x80, 0xc8, 0x40, + 0x52, 0x11, 0xcf, 0x6d, 0xbf, 0xa5, 0x0c, 0xe1, 0x80, 0x2e, 0x51, 0xea, 0x42, 0x34, 0x2d, 0xe5, + 0x37, 0xc3, 0xb6, 0x44, 0x90, 0x90, 0xf8, 0xb2, 0x6c, 0xfb, 0xfb, 0x10, 0x27, 0x74, 0x8a, 0xd7, + 0xf2, 0x46, 0x2b, 0xdd, 0xe6, 0xf9, 0x5b, 0xb8, 0x53, 0x3a, 0x03, 0xc5, 0x16, 0xe1, 0x4a, 0xd7, + 0x90, 0xba, 0xf7, 0x74, 0x88, 0x59, 0xa0, 0x9f, 0xa9, 0xc5, 0x50, 0x0f, 0x26, 0x08, 0x0f, 0x78, + 0x80, 0x31, 0xfc, 0x14, 0x48, 0xa3, 0x9a, 0x45, 0x00, 0x99, 0xdd, 0xd9, 0xb1, 0x6a, 0xcc, 0xa9, + 0x7f, 0x56, 0xdf, 0xb9, 0xb6, 0xbb, 0x17, 0xb1, 0xf5, 0xd1, 0xee, 0x82, 0xac, 0xda, 0x65, 0x34, + 0x05, 0xa0, 0x44, 0x72, 0xef, 0x86, 0xdc, 0x14, 0x57, 0x9b, 0x25, 0x92, 0x3c, 0x3f, 0xed, 0xaf, + 0xcb, 0x16, 0x3d, 0x52, 0xc9, 0xe5, 0x0e, 0x8c, 0xe9, 0x9a, 0x1c, 0xd0, 0x43, 0xbb, 0x5b, 0x3f, + 0xf8, 0xa7, 0x82, 0x11, 0xfc, 0x65, 0xd6, 0xed, 0xfc, 0x6b, 0xd1, 0xe7, 0xb6, 0x98, 0x4d, 0x56, + 0xbb, 0x01, 0xed, 0xee, 0xda, 0xb6, 0xec, 0xee, 0x9b, 0x7d, 0xe6, 0x25, 0xc1, 0x22, 0x7d, 0xfb, + 0xc3, 0x9b, 0x7d, 0xa0, 0x0d, 0xf0, 0xef, 0x2c, 0x9d, 0x87, 0x6f, 0x7f, 0xf8, 0x7f, 0x67, 0x3b, + 0x66, 0x88, 0x6d, 0xcb, 0x02, 0x00, }; const size_t RCC6_WEB_UI_INDEX_GZ_LEN = sizeof(RCC6_WEB_UI_INDEX_GZ); #endif From 900c82ce19d2bccc41a7653b1b921dde3c354876 Mon Sep 17 00:00:00 2001 From: n30nex Date: Sun, 23 Aug 2026 11:26:52 -0400 Subject: [PATCH 3/8] fix: retain merged recovery target in Ultimate builds --- variants/heltec_v3/platformio.ini | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index 2773d388..fe099e68 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -234,7 +234,9 @@ lib_deps = [heltec_v3_ultimate_companion] extends = Heltec_lora32_v3 -extra_scripts = pre:scripts/ultimate_build_flags.py +extra_scripts = + merge-bin.py + pre:scripts/ultimate_build_flags.py build_flags = ${Heltec_lora32_v3.build_flags} -I examples/companion_radio From 5df640dd07a7534d2112c536375ed3cb2fd8005d Mon Sep 17 00:00:00 2001 From: n30nex Date: Sun, 23 Aug 2026 11:37:20 -0400 Subject: [PATCH 4/8] fix: adapt Ultimate transport hooks to ESP32-S3 --- examples/companion_radio/UltimateService.cpp | 6 +++++- src/helpers/BaseChatMesh.cpp | 6 ++++-- src/helpers/BaseChatMesh.h | 4 ++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/examples/companion_radio/UltimateService.cpp b/examples/companion_radio/UltimateService.cpp index 20efdafa..e63abc43 100644 --- a/examples/companion_radio/UltimateService.cpp +++ b/examples/companion_radio/UltimateService.cpp @@ -5,7 +5,7 @@ #include "DataStore.h" #include #include -#if defined(ESP32) && (defined(HELTEC_RCC6) || defined(HELTEC_LORA_V3) || defined(HELTEC_LORA_V4)) +#if defined(ESP32) && defined(HELTEC_RCC6) #include #endif #include @@ -704,7 +704,11 @@ void UltimateService::sampleBatteryProjection() { void UltimateService::sampleUsbHostState() { #if defined(ESP32) && (defined(HELTEC_RCC6) || defined(HELTEC_LORA_V3) || defined(HELTEC_LORA_V4)) +#if defined(HELTEC_RCC6) const bool connected = usb_serial_jtag_is_connected(); +#else + const bool connected = static_cast(Serial); +#endif if (connected) { usb_host_seen = true; snapshot.battery_projection_valid = false; diff --git a/src/helpers/BaseChatMesh.cpp b/src/helpers/BaseChatMesh.cpp index 972a97e9..0d5c23f7 100644 --- a/src/helpers/BaseChatMesh.cpp +++ b/src/helpers/BaseChatMesh.cpp @@ -439,9 +439,10 @@ mesh::Packet* BaseChatMesh::composeMsgPacket(const ContactInfo& recipient, uint3 return createDatagram(PAYLOAD_TYPE_TXT_MSG, recipient.id, recipient.getSharedSecret(self_id), temp, len); } -int BaseChatMesh::sendMessage(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char* text, uint32_t& expected_ack, uint32_t& est_timeout) { +int BaseChatMesh::sendMessage(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char* text, uint32_t& expected_ack, uint32_t& est_timeout, uint8_t* queued_hash) { mesh::Packet* pkt = composeMsgPacket(recipient, timestamp, attempt, text, expected_ack); if (pkt == NULL) return MSG_SEND_FAILED; + if (queued_hash) pkt->calculatePacketHash(queued_hash); uint32_t t = _radio->getEstAirtimeFor(pkt->getRawLength()); @@ -484,7 +485,7 @@ int BaseChatMesh::sendCommandData(const ContactInfo& recipient, uint32_t timest return rc; } -bool BaseChatMesh::sendGroupMessage(uint32_t timestamp, mesh::GroupChannel& channel, const char* sender_name, const char* text, int text_len) { +bool BaseChatMesh::sendGroupMessage(uint32_t timestamp, mesh::GroupChannel& channel, const char* sender_name, const char* text, int text_len, uint8_t* queued_hash) { uint8_t temp[5+MAX_TEXT_LEN+32]; memcpy(temp, ×tamp, 4); // mostly an extra blob to help make packet_hash unique temp[4] = 0; // TXT_TYPE_PLAIN @@ -499,6 +500,7 @@ bool BaseChatMesh::sendGroupMessage(uint32_t timestamp, mesh::GroupChannel& chan auto pkt = createGroupDatagram(PAYLOAD_TYPE_GRP_TXT, channel, temp, 5 + prefix_len + text_len); if (pkt) { + if (queued_hash) pkt->calculatePacketHash(queued_hash); sendFloodScoped(channel, pkt); return true; } diff --git a/src/helpers/BaseChatMesh.h b/src/helpers/BaseChatMesh.h index d9878547..df2513a9 100644 --- a/src/helpers/BaseChatMesh.h +++ b/src/helpers/BaseChatMesh.h @@ -155,9 +155,9 @@ class BaseChatMesh : public mesh::Mesh { public: mesh::Packet* createSelfAdvert(const char* name); mesh::Packet* createSelfAdvert(const char* name, double lat, double lon); - int sendMessage(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char* text, uint32_t& expected_ack, uint32_t& est_timeout); + int sendMessage(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char* text, uint32_t& expected_ack, uint32_t& est_timeout, uint8_t* queued_hash = nullptr); int sendCommandData(const ContactInfo& recipient, uint32_t timestamp, uint8_t attempt, const char* text, uint32_t& est_timeout); - bool sendGroupMessage(uint32_t timestamp, mesh::GroupChannel& channel, const char* sender_name, const char* text, int text_len); + bool sendGroupMessage(uint32_t timestamp, mesh::GroupChannel& channel, const char* sender_name, const char* text, int text_len, uint8_t* queued_hash = nullptr); bool sendGroupData(mesh::GroupChannel& channel, uint8_t* path, uint8_t path_len, uint16_t data_type, const uint8_t* data, int data_len); int sendLogin(const ContactInfo& recipient, const char* password, uint32_t& est_timeout); int sendAnonReq(const ContactInfo& recipient, const uint8_t* data, uint8_t len, uint32_t& tag, uint32_t& est_timeout); From 94b9599eb035ac08dbd4e429e1973eaeef1c9d00 Mon Sep 17 00:00:00 2001 From: n30nex Date: Sun, 23 Aug 2026 11:44:03 -0400 Subject: [PATCH 5/8] feat: add Heltec V3 Ultimate repeater WebUI --- .github/workflows/neonpocket-build.yml | 3 + README.md | 5 +- .../simple_repeater/UltimateRepeaterWeb.h | 292 ++++++++++++++++++ examples/simple_repeater/main.cpp | 80 +++++ scripts/verify_ultimate_mono.py | 18 ++ variants/heltec_v3/platformio.ini | 27 ++ 6 files changed, 424 insertions(+), 1 deletion(-) create mode 100644 examples/simple_repeater/UltimateRepeaterWeb.h diff --git a/.github/workflows/neonpocket-build.yml b/.github/workflows/neonpocket-build.yml index 949776ef..0ac3c222 100644 --- a/.github/workflows/neonpocket-build.yml +++ b/.github/workflows/neonpocket-build.yml @@ -63,6 +63,9 @@ jobs: - environment: heltec_v3_ultimate_companion_web transport: Ultimate-Web ultimate: true + - environment: heltec_v3_ultimate_repeater_web + transport: Ultimate-Repeater-Web + ultimate: true steps: - uses: actions/checkout@v4 with: diff --git a/README.md b/README.md index 67c7d4e5..ade8ce46 100644 --- a/README.md +++ b/README.md @@ -22,15 +22,18 @@ Experimental MeshCore BLE, native-USB, and Ultimate Wi-Fi/Web companion firmware ## Ultimate experimental builds -Two additional targets bring the portable RCC6 Ultimate feature set to the 128×64 OLED: +Three additional targets bring the portable RCC6 Ultimate experience to the 128×64 OLED: - `heltec_v3_ultimate_companion_ble`: standard MeshCore BLE companion compatibility plus persistent history, metrics, recent-node signal tracking, composer state, and diagnostics. - `heltec_v3_ultimate_companion_web`: setup AP, local 2.4 GHz Wi-Fi mode, authenticated offline WebUI, full TCP companion protocol on port 5000, history/export/settings/location APIs, and signed app-slot OTA support. +- `heltec_v3_ultimate_repeater_web`: a dedicated MeshCore repeater with an authenticated live dashboard, recent-neighbor view, radio/packet/battery/airtime graphs, flood-advert controls, restricted radio/location/name configuration, and AP-to-LAN onboarding. Both use 350 contacts, 40 channels, 256 pending companion frames, a 32-event callback queue, a 64-node recent-heard cache, selectable 0/128/512/2048-message history, seven-day hourly metrics, a 32 KiB runtime allocation gate, and the existing NeonPocket demo-scene startup. The OLED adds compact Network and Ultimate status pages; the full composer, charts, history manager, location transfer, and Wi-Fi onboarding live in the WebUI. BLE and Wi-Fi never run together. In Web mode, HTTP is authenticated, but TCP port 5000 intentionally exposes the complete MeshCore companion/admin protocol to the trusted local LAN. Do not use station mode on an untrusted network. +The Ultimate Repeater is a separate role image: it does not expose the companion TCP protocol and it never runs BLE. On first boot, join the `NeonPocket-XXXXXX` setup AP and open `http://192.168.4.1`. The OLED and USB serial output show the generated 12-character key; use username `neonpocket`. After local Wi-Fi is saved, the OLED shows the LAN IP at boot. If the saved network cannot be reached within ten seconds, the private setup AP returns. Wi-Fi stays awake in this build, so use the standard repeater image for solar/battery deployments. + The RCC6 indexed color framebuffer, 220×128 tile animations, and color-only notification effects are hardware-specific and are not copied to this monochrome OLED build. ## Button controls diff --git a/examples/simple_repeater/UltimateRepeaterWeb.h b/examples/simple_repeater/UltimateRepeaterWeb.h new file mode 100644 index 00000000..35e6dd68 --- /dev/null +++ b/examples/simple_repeater/UltimateRepeaterWeb.h @@ -0,0 +1,292 @@ +#pragma once + +#ifdef NEONPOCKET_ULTIMATE_REPEATER_WEB + +#include +#include +#include +#include +#include +#include + +#include "MyMesh.h" + +#ifndef NEONPOCKET_REPEATER_WEB_DEVICE +#define NEONPOCKET_REPEATER_WEB_DEVICE "HELTEC" +#endif + +class UltimateRepeaterWeb { + static constexpr const char* kUser = "neonpocket"; + WebServer server{80}; + DNSServer dns; + Preferences prefs; + MyMesh* mesh = nullptr; + String access_key; + String ap_ssid; + bool ap_mode = true; + bool reboot_pending = false; + uint32_t reboot_at = 0; + + static const char kIndex[] PROGMEM; + + static String jsonQuote(const String& value) { + String out; + out.reserve(value.length() + 8); + out += '"'; + for (size_t i = 0; i < value.length(); ++i) { + const char c = value[i]; + if (c == '"' || c == '\\') { + out += '\\'; + out += c; + } else if (c == '\n') { + out += "\\n"; + } else if (c == '\r') { + out += "\\r"; + } else if (static_cast(c) >= 0x20) { + out += c; + } + } + out += '"'; + return out; + } + + String runCommand(const String& input) { + if (!mesh || input.length() == 0 || input.length() > 95) return "Invalid command"; + char command[96]; + char reply[512]; + input.toCharArray(command, sizeof(command)); + reply[0] = 0; + mesh->handleCommand(0, command, reply); + return String(reply); + } + + bool authenticate() { + if (server.authenticate(kUser, access_key.c_str())) return true; + server.requestAuthentication(BASIC_AUTH, "NeonPocket Ultimate Repeater"); + return false; + } + + static bool isAllowedCommand(String command) { + command.trim(); + static const char* exact[] = { + "advert", "advert.zerohop", "discover.neighbors", "neighbors", + "stats-core", "stats-radio", "stats-packets", "clear stats", + "gps advert", "gps advert none", "gps advert prefs", + "set repeat on", "set repeat off" + }; + for (const char* value : exact) if (command == value) return true; + + static const char* reads[] = { + "get name", "get role", "get public.key", "get radio", "get freq", + "get tx", "get lat", "get lon", "get repeat", "get path.hash.mode", + "get advert.interval", "get flood.advert.interval", "get flood.max.advert", + "get radio.rxgain", "get dutycycle" + }; + for (const char* value : reads) if (command == value) return true; + + static const char* prefixes[] = { + "set name ", "set freq ", "set bw ", "set sf ", "set cr ", "set tx ", + "set lat ", "set lon ", "set path.hash.mode ", "set advert.interval ", + "set flood.advert.interval ", "set flood.max.advert ", + "set radio.rxgain ", "set dutycycle " + }; + for (const char* value : prefixes) if (command.startsWith(value)) return true; + return false; + } + + void loadAccessKey() { + access_key = prefs.getString("web-key", ""); + if (access_key.length() == 12) return; + static const char alphabet[] = "23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ"; + access_key = ""; + for (uint8_t i = 0; i < 12; ++i) { + access_key += alphabet[esp_random() % (sizeof(alphabet) - 1)]; + } + prefs.putString("web-key", access_key); + } + + void startAccessPoint() { + WiFi.disconnect(true); + WiFi.mode(WIFI_AP); + char suffix[7]; + snprintf(suffix, sizeof(suffix), "%06lX", static_cast(ESP.getEfuseMac() & 0xFFFFFFULL)); + ap_ssid = String("NeonPocket-") + suffix; + WiFi.softAP(ap_ssid.c_str(), access_key.c_str()); + dns.start(53, "*", WiFi.softAPIP()); + ap_mode = true; + } + + void startNetwork() { + const bool prefer_sta = prefs.getBool("sta-mode", false); + const String ssid = prefs.getString("sta-ssid", ""); + const String password = prefs.getString("sta-pass", ""); + if (prefer_sta && ssid.length()) { + WiFi.mode(WIFI_STA); + WiFi.setSleep(false); + WiFi.begin(ssid.c_str(), password.c_str()); + const uint32_t started = millis(); + while (WiFi.status() != WL_CONNECTED && millis() - started < 10000U) { + delay(50); + } + if (WiFi.status() == WL_CONNECTED) { + ap_mode = false; + return; + } + } + startAccessPoint(); + } + + void sendStatus() { + if (!authenticate()) return; + String core = runCommand("stats-core"); + String radio = runCommand("stats-radio"); + String packets = runCommand("stats-packets"); + if (!core.startsWith("{")) core = "{}"; + if (!radio.startsWith("{")) radio = "{}"; + if (!packets.startsWith("{")) packets = "{}"; + const String neighbors = runCommand("neighbors"); + const String radio_config = runCommand("get radio"); + const NodePrefs* node = mesh->getNodePrefs(); + + String body; + body.reserve(1400); + body += "{\"device\":"; + body += jsonQuote(NEONPOCKET_REPEATER_WEB_DEVICE); + body += ",\"role\":\"repeater\",\"version\":"; + body += jsonQuote(NEONPOCKET_ULTIMATE_VERSION); + body += ",\"mode\":\""; + body += ap_mode ? "AP" : "LAN"; + body += "\",\"ssid\":"; + body += jsonQuote(ap_mode ? ap_ssid : WiFi.SSID()); + body += ",\"ip\":"; + body += jsonQuote(ipText()); + body += ",\"wifi_rssi\":"; + body += ap_mode ? "0" : String(WiFi.RSSI()); + body += ",\"free_heap\":"; + body += String(ESP.getFreeHeap()); + body += ",\"name\":"; + body += jsonQuote(node ? String(node->node_name) : String("")); + body += ",\"lat\":"; + body += node ? String(node->node_lat, 6) : "0"; + body += ",\"lon\":"; + body += node ? String(node->node_lon, 6) : "0"; + body += ",\"path_hash_mode\":"; + body += node ? String(node->path_hash_mode) : "0"; + body += ",\"forwarding\":"; + body += (node && !node->disable_fwd) ? "true" : "false"; + body += ",\"core\":" + core; + body += ",\"radio\":" + radio; + body += ",\"packets\":" + packets; + body += ",\"radio_config\":" + jsonQuote(radio_config); + body += ",\"neighbors\":" + jsonQuote(neighbors); + body += "}"; + server.send(200, "application/json", body); + } + + void sendCommand() { + if (!authenticate()) return; + const String command = server.arg("plain"); + if (!isAllowedCommand(command)) { + server.send(403, "application/json", "{\"ok\":false,\"reply\":\"Command not allowed\"}"); + return; + } + const String reply = runCommand(command); + String body = String("{\"ok\":true,\"reply\":") + jsonQuote(reply) + "}"; + server.send(200, "application/json", body); + } + + void saveNetwork() { + if (!authenticate()) return; + if (server.arg("mode") == "ap") { + prefs.putBool("sta-mode", false); + prefs.remove("sta-ssid"); + prefs.remove("sta-pass"); + } else { + const String ssid = server.arg("ssid"); + const String password = server.arg("password"); + if (!ssid.length() || ssid.length() > 32 || password.length() > 63) { + server.send(400, "application/json", "{\"ok\":false,\"error\":\"Invalid Wi-Fi credentials\"}"); + return; + } + if (!prefs.putString("sta-ssid", ssid) || !prefs.putString("sta-pass", password) || + !prefs.putBool("sta-mode", true)) { + server.send(500, "application/json", "{\"ok\":false,\"error\":\"Could not save settings\"}"); + return; + } + } + server.send(202, "application/json", "{\"ok\":true,\"restarting\":true}"); + reboot_pending = true; + reboot_at = millis() + 750U; + } + + void registerRoutes() { + server.on("/", HTTP_GET, [this]() { + if (!authenticate()) return; + server.send_P(200, "text/html", kIndex); + }); + server.on("/api/status", HTTP_GET, [this]() { sendStatus(); }); + server.on("/api/command", HTTP_POST, [this]() { sendCommand(); }); + server.on("/api/network", HTTP_POST, [this]() { saveNetwork(); }); + server.onNotFound([this]() { + if (ap_mode) { + server.sendHeader("Location", String("http://") + WiFi.softAPIP().toString(), true); + server.send(302, "text/plain", ""); + } else { + server.send(404, "text/plain", "Not found"); + } + }); + } + +public: + bool begin(MyMesh& instance) { + mesh = &instance; + if (!prefs.begin("np-rpt-web", false)) return false; + loadAccessKey(); + startNetwork(); + registerRoutes(); + server.begin(); + Serial.printf("Ultimate Repeater Web: %s http://%s user=%s key=%s\n", + ap_mode ? ap_ssid.c_str() : WiFi.SSID().c_str(), + ipText().c_str(), kUser, access_key.c_str()); + return true; + } + + void loop() { + if (ap_mode) dns.processNextRequest(); + server.handleClient(); + if (reboot_pending && static_cast(millis() - reboot_at) >= 0) ESP.restart(); + } + + bool isAccessPoint() const { return ap_mode; } + const String& accessKey() const { return access_key; } + const String& accessPointSsid() const { return ap_ssid; } + String ipText() const { return (ap_mode ? WiFi.softAPIP() : WiFi.localIP()).toString(); } + String networkName() const { return ap_mode ? ap_ssid : WiFi.SSID(); } +}; + +const char UltimateRepeaterWeb::kIndex[] PROGMEM = R"HTML( + +NeonPocket Ultimate Repeater
NeonPocketMC

Ultimate
Repeater

+
Node
--
repeater
Radio
--
--
Traffic
--
--
+
Seven-minute signal trace
System
--
+
Recently heard
--
Actions
+
Repeater setup
+
+
Wi-Fi deployment

Join a trusted 2.4 GHz network or return to the private setup AP. The device restarts after saving.

+ +)HTML"; + +#endif diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index a714db68..cc47e292 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -1,8 +1,16 @@ #include // needed for PlatformIO #include +#if defined(ESP32) && defined(NEONPOCKET_SAFE_SPIFFS_BOOTSTRAP) + #include +#endif + #include "MyMesh.h" +#ifdef NEONPOCKET_ULTIMATE_REPEATER_WEB + #include "UltimateRepeaterWeb.h" +#endif + #ifdef DISPLAY_CLASS #include "UITask.h" static UITask ui_task(board, display); @@ -18,10 +26,36 @@ SimpleMeshTables tables; MyMesh the_mesh(board, radio_driver, *new ArduinoMillis(), fast_rng, rtc_clock, tables); +#ifdef NEONPOCKET_ULTIMATE_REPEATER_WEB +UltimateRepeaterWeb ultimate_web; +#endif + void halt() { while (1) ; } +#if defined(ESP32) && defined(NEONPOCKET_SAFE_SPIFFS_BOOTSTRAP) +static bool isSpiffsPartitionErased() { + const esp_partition_t* partition = esp_partition_find_first( + ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_SPIFFS, nullptr); + if (partition == nullptr) return false; + uint8_t chunk[1024]; + for (size_t offset = 0; offset < partition->size; offset += sizeof(chunk)) { + const size_t remaining = partition->size - offset; + const size_t length = remaining < sizeof(chunk) ? remaining : sizeof(chunk); + if (esp_partition_read(partition, offset, chunk, length) != ESP_OK) return false; + for (size_t i = 0; i < length; ++i) if (chunk[i] != 0xFF) return false; + } + return true; +} + +static bool beginSpiffsPreservingData() { + if (SPIFFS.begin(false)) return true; + if (!isSpiffsPartitionErased()) return false; + return SPIFFS.format() && SPIFFS.begin(false); +} +#endif + static char command[160]; #ifdef ETHERNET_ENABLED static char ethernet_command[160]; @@ -57,6 +91,11 @@ void setup() { display.setCursor(0, 0); display.print("Please wait..."); display.endFrame(); +#ifdef DISPLAY_REQUIRED + } else { + Serial.println("DISPLAY ERROR: required display did not initialize"); + halt(); +#endif } #endif @@ -73,7 +112,22 @@ void setup() { fs = &InternalFS; IdentityStore store(InternalFS, ""); #elif defined(ESP32) +#ifdef NEONPOCKET_SAFE_SPIFFS_BOOTSTRAP + if (!beginSpiffsPreservingData()) { + Serial.println("STORAGE ERROR: SPIFFS mount failed; refusing to format"); +#ifdef DISPLAY_CLASS + display.startFrame(); + display.setCursor(0, 0); + display.print("STORAGE ERROR"); + display.setCursor(0, 16); + display.print("USB RECOVERY"); + display.endFrame(); +#endif + halt(); + } +#else SPIFFS.begin(true); +#endif fs = &SPIFFS; IdentityStore store(SPIFFS, "/identity"); #elif defined(RP2040_PLATFORM) @@ -106,6 +160,27 @@ void setup() { the_mesh.begin(fs); +#ifdef NEONPOCKET_ULTIMATE_REPEATER_WEB + if (!ultimate_web.begin(the_mesh)) { + Serial.println("Ultimate Repeater Web failed to start"); + halt(); + } +#ifdef DISPLAY_CLASS + display.startFrame(); + display.setCursor(0, 0); + display.print(ultimate_web.isAccessPoint() ? "ULTIMATE SETUP AP" : "ULTIMATE LOCAL WIFI"); + display.setCursor(0, 16); + display.print(ultimate_web.networkName().c_str()); + display.setCursor(0, 32); + display.print(ultimate_web.ipText().c_str()); + display.setCursor(0, 48); + display.print("KEY "); + display.print(ultimate_web.accessKey().c_str()); + display.endFrame(); + delay(3000); +#endif +#endif + #ifdef DISPLAY_CLASS ui_task.begin(the_mesh.getNodePrefs(), FIRMWARE_BUILD_DATE, FIRMWARE_VERSION); #endif @@ -186,6 +261,9 @@ void loop() { #endif the_mesh.loop(); +#ifdef NEONPOCKET_ULTIMATE_REPEATER_WEB + ultimate_web.loop(); +#endif sensors.loop(); #ifdef DISPLAY_CLASS ui_task.loop(); @@ -195,6 +273,7 @@ void loop() { #ifdef HAS_EXTERNAL_WATCHDOG external_watchdog.loop(); #endif +#ifndef NEONPOCKET_ULTIMATE_REPEATER_WEB if (the_mesh.getNodePrefs()->powersaving_enabled && !the_mesh.hasPendingWork()) { #if defined(NRF52_PLATFORM) board.sleep(0); // nrf ignores seconds param, sleeps whenever possible @@ -204,4 +283,5 @@ void loop() { } #endif } +#endif } diff --git a/scripts/verify_ultimate_mono.py b/scripts/verify_ultimate_mono.py index 339d87b2..18f50469 100644 --- a/scripts/verify_ultimate_mono.py +++ b/scripts/verify_ultimate_mono.py @@ -31,14 +31,18 @@ def text(path: str) -> str: web = text("examples/companion_radio/UltimateWebApi.cpp") ui = text("examples/companion_radio/ui-new/UITask.cpp") asset = text("examples/companion_radio/webui/Rcc6WebUiAssets.h") +repeater_main = text("examples/simple_repeater/main.cpp") +repeater_web = text("examples/simple_repeater/UltimateRepeaterWeb.h") ble_env = f"[env:heltec_{board}_ultimate_companion_ble]" web_env = f"[env:heltec_{board}_ultimate_companion_web]" +repeater_env = f"[env:heltec_{board}_ultimate_repeater_web]" ota_target = f"-D NEONPOCKET_ULTIMATE_OTA_TARGET='\"heltec_{board}\"'" checks = { "Ultimate BLE target": ble_env in platform, "Ultimate Web target": web_env in platform, + "Ultimate Repeater Web target": repeater_env in platform, "MeshCore 1.17.1": "-D FIRMWARE_VERSION='\"v1.17.1\"'" in platform, "mutually exclusive transports": platform.index(ble_env) != platform.index(web_env) and "-D BLE_PIN_CODE=123456" in platform[platform.index(ble_env):platform.index(web_env)] @@ -66,6 +70,20 @@ def text(path: str) -> str: "authenticated Ultimate APIs": "/api/ultimate/status" in web and "/api/ultimate/history" in web and "/api/ultimate/location" in web, "generated embedded WebUI": "RCC6_WEB_UI_INDEX_GZ[] PROGMEM" in asset, + "repeater fail-closed storage": "SPIFFS.begin(false)" in repeater_main + and "refusing to format" in repeater_main, + "repeater Wi-Fi onboarding": all(token in repeater_web for token in ( + "WiFi.softAP", "WiFi.begin", 'server.on("/api/network"', "Preferences")), + "repeater authenticated dashboard": "server.authenticate" in repeater_web + and 'server.on("/api/status"' in repeater_web, + "repeater restricted command surface": "isAllowedCommand" in repeater_web + and "Command not allowed" in repeater_web, + "repeater live mesh metrics": all(token in repeater_web for token in ( + 'runCommand("stats-core")', 'runCommand("stats-radio")', + 'runCommand("stats-packets")', 'runCommand("neighbors")')), + "repeater three-byte advert hashes": "-D DEFAULT_PATH_HASH_MODE=2" in platform, + "repeater 64-node recent view": "-D MAX_NEIGHBOURS=64" in platform, + "repeater Wi-Fi stays awake": "#ifndef NEONPOCKET_ULTIMATE_REPEATER_WEB" in repeater_main, } failed = [name for name, ok in checks.items() if not ok] diff --git a/variants/heltec_v3/platformio.ini b/variants/heltec_v3/platformio.ini index fe099e68..1cce4ff2 100644 --- a/variants/heltec_v3/platformio.ini +++ b/variants/heltec_v3/platformio.ini @@ -281,6 +281,33 @@ build_flags = -D RCC6_WEB_AP=1 -D TCP_PORT=5000 +[env:heltec_v3_ultimate_repeater_web] +extends = Heltec_lora32_v3 +extra_scripts = merge-bin.py +build_flags = + ${Heltec_lora32_v3.build_flags} + -D ESP32_CPU_FREQ=160 + -D FIRMWARE_VERSION='"v1.17.1-ultimate-rpt-rc1"' + -D DISPLAY_CLASS=SSD1306Display + -D DISPLAY_REQUIRED=1 + -D NEONPOCKET_ULTIMATE_REPEATER_WEB=1 + -D NEONPOCKET_ULTIMATE_VERSION='"2.0.0-rc.1"' + -D NEONPOCKET_REPEATER_WEB_DEVICE='"HELTEC V3"' + -D NEONPOCKET_SAFE_SPIFFS_BOOTSTRAP=1 + -D DISABLE_WIFI_OTA=1 + -D ADVERT_NAME='"V3 Ultimate Repeater"' + -D ADVERT_LAT=0.0 + -D ADVERT_LON=0.0 + -D ADMIN_PASSWORD='"password"' + -D MAX_NEIGHBOURS=64 + -D DEFAULT_PATH_HASH_MODE=2 +build_src_filter = ${Heltec_lora32_v3.build_src_filter} + + + +<../examples/simple_repeater> +lib_deps = + ${Heltec_lora32_v3.lib_deps} + bakercp/CRC32 @ ^2.0.0 + [env:Heltec_v3_companion_radio_wifi] extends = Heltec_lora32_v3 build_flags = From 106e3910226a67917685812ea5b8314240cc82ff Mon Sep 17 00:00:00 2001 From: n30nex Date: Sun, 23 Aug 2026 11:48:53 -0400 Subject: [PATCH 6/8] fix: keep repeater onboarding dependency-free --- examples/simple_repeater/UltimateRepeaterWeb.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/examples/simple_repeater/UltimateRepeaterWeb.h b/examples/simple_repeater/UltimateRepeaterWeb.h index 35e6dd68..7886b31b 100644 --- a/examples/simple_repeater/UltimateRepeaterWeb.h +++ b/examples/simple_repeater/UltimateRepeaterWeb.h @@ -3,7 +3,6 @@ #ifdef NEONPOCKET_ULTIMATE_REPEATER_WEB #include -#include #include #include #include @@ -18,7 +17,6 @@ class UltimateRepeaterWeb { static constexpr const char* kUser = "neonpocket"; WebServer server{80}; - DNSServer dns; Preferences prefs; MyMesh* mesh = nullptr; String access_key; @@ -112,7 +110,6 @@ class UltimateRepeaterWeb { snprintf(suffix, sizeof(suffix), "%06lX", static_cast(ESP.getEfuseMac() & 0xFFFFFFULL)); ap_ssid = String("NeonPocket-") + suffix; WiFi.softAP(ap_ssid.c_str(), access_key.c_str()); - dns.start(53, "*", WiFi.softAPIP()); ap_mode = true; } @@ -252,7 +249,6 @@ class UltimateRepeaterWeb { } void loop() { - if (ap_mode) dns.processNextRequest(); server.handleClient(); if (reboot_pending && static_cast(millis() - reboot_at) >= 0) ESP.restart(); } From 59040581d65cdc73f2ac367c8e6a2b09c0d24713 Mon Sep 17 00:00:00 2001 From: n30nex Date: Sun, 23 Aug 2026 11:54:11 -0400 Subject: [PATCH 7/8] fix: expose repeater web framework libraries --- examples/simple_repeater/main.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index cc47e292..748c8e20 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -8,6 +8,9 @@ #include "MyMesh.h" #ifdef NEONPOCKET_ULTIMATE_REPEATER_WEB + #include + #include + #include #include "UltimateRepeaterWeb.h" #endif From 4a36efad9ebf0a57783ec97bc78e4c3f07ec2659 Mon Sep 17 00:00:00 2001 From: n30nex Date: Sun, 23 Aug 2026 12:07:55 -0400 Subject: [PATCH 8/8] fix: close Ultimate companion review gaps --- examples/companion_radio/MyMesh.cpp | 44 ++++++++++++++++++-- examples/companion_radio/UltimateService.cpp | 26 ++++++++++++ examples/companion_radio/UltimateService.h | 1 + examples/companion_radio/ui-new/UITask.cpp | 7 +++- scripts/verify_ultimate_mono.py | 7 ++++ 5 files changed, 80 insertions(+), 5 deletions(-) diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 215cbd89..75672df5 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -571,7 +571,11 @@ void MyMesh::queueMessage(const ContactInfo &from, uint8_t txt_type, mesh::Packe memcpy(_ui_reply_pubkey_prefix, from.id.pub_key, sizeof(_ui_reply_pubkey_prefix)); _ui_reply_target = UIReplyTarget::Direct; #endif - _ui->newMsgWithEvent(hop_count, from.name, text, offline_queue_len, + const char* preview = text; +#ifdef NEONPOCKET_ULTIMATE + if (ultimate_service.getSettings().private_notifications) preview = "Message hidden"; +#endif + _ui->newMsgWithEvent(hop_count, from.name, preview, offline_queue_len, UIEventType::contactMessage); if (!_serial->isConnected()) { _ui->notify(UIEventType::contactMessage); @@ -709,7 +713,11 @@ void MyMesh::onChannelMessageRecv(const mesh::GroupChannel &channel, mesh::Packe attributable ? _ultimate_last_rx_snr_q4 : (int8_t)(pkt->getSNR() * 4)); #endif if (_ui) { - _ui->newMsgWithEvent(hop_count, channel_name, text, offline_queue_len, + const char* preview = text; +#ifdef NEONPOCKET_ULTIMATE + if (ultimate_service.getSettings().private_notifications) preview = "Message hidden"; +#endif + _ui->newMsgWithEvent(hop_count, channel_name, preview, offline_queue_len, UIEventType::channelMessage); } #endif @@ -1230,7 +1238,14 @@ void MyMesh::handleCmdFrame(size_t len) { result = sendCommandData(*recipient, msg_timestamp, attempt, text, est_timeout); expected_ack = 0; // no Ack expected } else { +#ifdef NEONPOCKET_ULTIMATE + _ultimate_delivery_active = false; + memset(_ultimate_delivery_hash, 0, sizeof(_ultimate_delivery_hash)); + result = sendMessage(*recipient, msg_timestamp, attempt, text, expected_ack, + est_timeout, _ultimate_delivery_hash); +#else result = sendMessage(*recipient, msg_timestamp, attempt, text, expected_ack, est_timeout); +#endif } // TODO: add expected ACK to table if (result == MSG_SEND_FAILED) { @@ -1250,6 +1265,9 @@ void MyMesh::handleCmdFrame(size_t len) { _serial->writeFrame(out_frame, 10); #ifdef NEONPOCKET_ULTIMATE if (txt_type == TXT_TYPE_PLAIN) { + ultimate_service.startDelivery(static_cast(UltimateMessageKind::Direct), + recipient->name, text, expected_ack, est_timeout); + _ultimate_delivery_active = true; ultimate_service.enqueueMessage(UltimateMessageKind::Direct, false, 0, recipient->id.pub_key, recipient->name, text, msg_timestamp, recipient->out_path_len, 0, 0); @@ -1275,10 +1293,28 @@ void MyMesh::handleCmdFrame(size_t len) { } else { ChannelDetails channel; bool success = getChannel(channel_idx, channel); - if (success && sendGroupMessage(msg_timestamp, channel.channel, _prefs.node_name, text, len - i)) { + bool sent = false; +#ifdef NEONPOCKET_ULTIMATE + _ultimate_delivery_active = false; + memset(_ultimate_delivery_hash, 0, sizeof(_ultimate_delivery_hash)); + if (success) { + sent = sendGroupMessage(msg_timestamp, channel.channel, _prefs.node_name, text, + len - i, _ultimate_delivery_hash); + } +#else + if (success) { + sent = sendGroupMessage(msg_timestamp, channel.channel, _prefs.node_name, text, len - i); + } +#endif + if (success && sent) { #ifdef NEONPOCKET_ULTIMATE + char label[32]; + snprintf(label, sizeof(label), "#%s", channel.name); + ultimate_service.startDelivery(static_cast(UltimateMessageKind::Channel), + label, text, 0, 0); + _ultimate_delivery_active = true; ultimate_service.enqueueMessage(UltimateMessageKind::Channel, false, channel_idx, - nullptr, channel.name, text, msg_timestamp, OUT_PATH_UNKNOWN, 0, 0); + nullptr, label, text, msg_timestamp, OUT_PATH_UNKNOWN, 0, 0); #endif writeOKFrame(); } else { diff --git a/examples/companion_radio/UltimateService.cpp b/examples/companion_radio/UltimateService.cpp index e63abc43..226c8cb8 100644 --- a/examples/companion_radio/UltimateService.cpp +++ b/examples/companion_radio/UltimateService.cpp @@ -360,6 +360,8 @@ bool UltimateService::appendRecord(const PendingEvent& event) { copyText(record.text, sizeof(record.text), event.text); record.crc32 = crc32(&record, offsetof(UltimateHistoryRecord, crc32)); + UltimateHistoryRecord evicted = {}; + const bool evicted_valid = meta.count == meta.capacity && readSlot(meta.head, evicted); if (!writeSlot(meta.head, record)) return false; const JournalMeta previous = meta; meta.head = (meta.head + 1) % meta.capacity; @@ -372,6 +374,7 @@ bool UltimateService::appendRecord(const PendingEvent& event) { snapshot.last_sequence = record.sequence; snapshot.history_count = meta.count; + if (evicted_valid) removeThreadRecord(evicted); if ((record.flags & ULTIMATE_HISTORY_INCOMING) == 0 && delivery.history_sequence == 0 && delivery.kind == record.kind && strncmp(delivery.target, record.sender, sizeof(delivery.target) - 1) == 0 && @@ -542,6 +545,29 @@ void UltimateService::updateThread(const UltimateHistoryRecord& record, bool new } } +void UltimateService::removeThreadRecord(const UltimateHistoryRecord& record) { + const bool unread = (record.flags & (ULTIMATE_HISTORY_INCOMING | ULTIMATE_HISTORY_READ)) == + ULTIMATE_HISTORY_INCOMING; + if (unread && snapshot.unread_count) snapshot.unread_count--; + + for (uint8_t i = 0; i < thread_count; i++) { + const bool same = threads[i].kind == record.kind && + (record.kind == static_cast(UltimateMessageKind::Channel) + ? threads[i].target == record.target + : memcmp(threads[i].peer_key, record.peer_key, sizeof(record.peer_key)) == 0); + if (!same) continue; + if (unread && threads[i].unread) threads[i].unread--; + if (threads[i].newest_sequence == record.sequence) { + if (i + 1 < thread_count) { + memmove(&threads[i], &threads[i + 1], (thread_count - i - 1) * sizeof(threads[0])); + } + memset(&threads[thread_count - 1], 0, sizeof(threads[0])); + thread_count--; + } + break; + } +} + void UltimateService::rebuildThreads() { memset(threads, 0, sizeof(threads)); thread_count = 0; diff --git a/examples/companion_radio/UltimateService.h b/examples/companion_radio/UltimateService.h index f2b7770c..e1940d30 100644 --- a/examples/companion_radio/UltimateService.h +++ b/examples/companion_radio/UltimateService.h @@ -314,6 +314,7 @@ class UltimateService { void processEvent(const PendingEvent& event); void updateNetwork(const PendingEvent& event); void updateThread(const UltimateHistoryRecord& record, bool newest_first); + void removeThreadRecord(const UltimateHistoryRecord& record); void rebuildThreads(); void updateMetrics(const PendingEvent& event); void sampleStatus(); diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index f1013920..4eb13920 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -476,7 +476,12 @@ class HomeScreen : public UIScreen { display.print(tmp); display.setColor(UIColor::secondary_txt); display.setCursor(2, 54); - display.print("HTTP + TCP/5000"); + if (web_interface.isStationMode()) { + display.print("HTTP + TCP/5000"); + } else { + snprintf(tmp, sizeof(tmp), "KEY %s", web_interface.getApPassword()); + display.print(tmp); + } #else display.setColor(UIColor::corp_blue); display.drawXbm((display.width() - 32) / 2, 18, diff --git a/scripts/verify_ultimate_mono.py b/scripts/verify_ultimate_mono.py index 18f50469..c8779c3d 100644 --- a/scripts/verify_ultimate_mono.py +++ b/scripts/verify_ultimate_mono.py @@ -66,6 +66,13 @@ def text(path: str) -> str: "safe hop decoding": "encoded_path_len & 0x3F" in service_cpp and "encoded_path_len & 0x3F" in mesh_cpp, "OLED Network and Ultimate pages": "HomePage::NETWORK" in ui and "HomePage::ULTIMATE" in ui, + "setup AP key visible on OLED": "getApPassword()" in ui and '"KEY %s"' in ui, + "bounded unread accounting": "removeThreadRecord(evicted)" in service_cpp + and "snapshot.unread_count--" in service_cpp, + "private OLED previews": "private_notifications) preview" in mesh_cpp + and 'preview = "Message hidden"' in mesh_cpp, + "companion send delivery tracking": mesh_cpp.count("ultimate_service.startDelivery") >= 4 + and "_ultimate_delivery_hash" in mesh_cpp, "flood advert action": "the_mesh.advert(" in ui and "true" in ui, "authenticated Ultimate APIs": "/api/ultimate/status" in web and "/api/ultimate/history" in web and "/api/ultimate/location" in web,