diff --git a/.github/workflows/run-unit-tests.yml b/.github/workflows/run-unit-tests.yml index ccc67d86f..1b63da757 100644 --- a/.github/workflows/run-unit-tests.yml +++ b/.github/workflows/run-unit-tests.yml @@ -584,6 +584,7 @@ jobs: python3 -B test/test_host_kiss_usb.py -v python3 -B test/test_host_kiss_ingress.py -v python3 -B test/test_host_kiss_capabilities.py -v + python3 -B test/test_host_kiss_ram.py -v python3 -B test/test_ethernet_kiss.py -v python3 -B test/test_ethernet_radio.py -v python3 -B test/test_companion_ethernet.py -v @@ -1008,7 +1009,7 @@ jobs: working-directory: test run: >- python3 -B -m unittest - test_message_navigation test_companion_reader test_reader_touch_coordinates + test_message_navigation test_companion_reader test_reader_stream test_small_font_metrics test_arm_instruction_meter test_reader_touch_coordinates test_pocket_gui test_pocket_world_map test_pocket_eink_refresh test_pocket_panel_spi test_touch_debug_overlay test_touch_debug_cli test_color_theme test_indicator_messages_profile test_t096_message_footer_profile @@ -1132,6 +1133,7 @@ jobs: - name: Verify nRF52 infrastructure trims and unchanged Companion scope run: | python3 -B test/test_nrf52_flash_trim.py -v + python3 -B test/test_nrf52_program_lifetime.py -v python3 -B test/test_nrf52_tft_font_trim.py -v python3 -B test/test_ota_sd_cache.py -v python3 -B test/test_rak15002_sd_seeder.py -v diff --git a/scripts/arm_instruction_meter.c b/scripts/arm_instruction_meter.c new file mode 100644 index 000000000..ad5a9033e --- /dev/null +++ b/scripts/arm_instruction_meter.c @@ -0,0 +1,27 @@ +/* Host-only instruction measurements for the pinned Unicorn 2.1.4 checker. + * Match the Python callback exactly: read SP before every ARM instruction. */ +#include +#include + +typedef int (*mesh_reg_read)(void *, int, void *); +struct mesh_arm_measurement { + mesh_reg_read read_register; + int sp_register, error; + uint64_t minimum_sp, instructions; +}; + +size_t mesh_arm_measurement_size(void) { return sizeof(struct mesh_arm_measurement); } + +void mesh_arm_measure_instruction(void *engine, uint64_t address, + uint32_t size, void *user) +{ + struct mesh_arm_measurement *state = (struct mesh_arm_measurement *) user; + uint32_t sp = 0; + int error; + (void) address; + (void) size; + error = state->read_register(engine, state->sp_register, &sp); + if (error) state->error = error; + if (sp < state->minimum_sp) state->minimum_sp = sp; + ++state->instructions; +} diff --git a/scripts/arm_instruction_meter.py b/scripts/arm_instruction_meter.py new file mode 100644 index 000000000..582d128fb --- /dev/null +++ b/scripts/arm_instruction_meter.py @@ -0,0 +1,93 @@ +"""Measure every ARM instruction with a C callback, or the identical Python one. + +The optional native callback avoids billions of crossings into Python while +keeping instruction/stack coverage. It uses the reviewed private handle API of +pinned Unicorn 2.1.4. Other versions or hosts without a C compiler use Python. +No Unicorn headers, linker development package, or network access are needed. +""" +import ctypes +from functools import lru_cache +from pathlib import Path +import shutil +import subprocess +import sys +import tempfile +import unicorn +from unicorn import UC_HOOK_CODE +from unicorn import arm_const as arm + + +class _NativeState(ctypes.Structure): + _fields_ = [('read_register', ctypes.c_void_p), ('sp_register', ctypes.c_int), + ('error', ctypes.c_int), ('minimum_sp', ctypes.c_uint64), + ('instructions', ctypes.c_uint64)] + + +@lru_cache(maxsize=1) +def _native_library(): + compiler = shutil.which('cc') or shutil.which('gcc') or shutil.which('clang') + if not compiler: + raise RuntimeError('no host C compiler; use the Python instruction callback') + directory = tempfile.TemporaryDirectory(prefix='mesh-arm-instruction-meter-') + library = Path(directory.name) / 'meter.so' + result = subprocess.run([compiler, '-std=c11', '-O2', '-shared', '-fPIC', + str(Path(__file__).with_suffix('.c')), '-o', str(library)], + capture_output=True, text=True) + if result.returncode: + raise RuntimeError('native callback compilation failed: ' + result.stderr) + loaded = ctypes.CDLL(str(library)) + loaded.mesh_arm_measurement_size.restype = ctypes.c_size_t + if loaded.mesh_arm_measurement_size() != ctypes.sizeof(_NativeState): + raise RuntimeError('native/Python measurement layout mismatch') + return loaded, directory + + +class ArmInstructionMeter: + def __init__(self, machine, prefer_native=True): + self.backend = 'python' + self._state = {'minimum_sp': 0, 'instructions': 0} + self._native = None + if prefer_native: + try: + self._install_native(machine) + self.backend = 'native' + return + except (RuntimeError, OSError, AttributeError) as error: + print('ARM meter Python fallback: ' + str(error), file=sys.stderr) + self.handle = machine.hook_add(UC_HOOK_CODE, self._measure_python) + + def _install_native(self, machine): + if unicorn.__version__ != '2.1.4': + raise RuntimeError('native callback requires reviewed Unicorn 2.1.4 bindings') + from unicorn.unicorn_py3 import unicorn as bindings + library, directory = _native_library() + state = _NativeState(ctypes.cast(bindings.uclib.uc_reg_read, ctypes.c_void_p), + arm.UC_ARM_REG_SP, 0, 0, 0) + callback = bindings.HOOK_CODE_CFUNC(('mesh_arm_measure_instruction', library)) + handle = bindings.uc_hook_h() + result = bindings.uclib.uc_hook_add(machine._uch, ctypes.byref(handle), UC_HOOK_CODE, + callback, ctypes.byref(state), ctypes.c_uint64(1), ctypes.c_uint64(0)) + if result: + raise RuntimeError('native Unicorn hook registration failed: ' + str(result)) + # Keep the C callback, its context, loaded code and temporary file alive + # for at least the lifetime of the registered machine hook. + machine._callbacks[handle.value] = (callback, state, library, directory) + self._native, self.handle = state, handle.value + + def _measure_python(self, machine, address, size, data): + self._state['minimum_sp'] = min(self._state['minimum_sp'], machine.reg_read(arm.UC_ARM_REG_SP)) + self._state['instructions'] += 1 + + def reset(self, top): + if self._native is not None: + self._native.minimum_sp, self._native.instructions, self._native.error = top, 0, 0 + else: + self._state.update(minimum_sp=top, instructions=0) + + def snapshot(self): + if self._native is not None: + if self._native.error: + raise RuntimeError('Unicorn SP read failed: ' + str(self._native.error)) + return {'minimum_sp': self._native.minimum_sp, + 'instructions': self._native.instructions} + return dict(self._state) diff --git a/scripts/check_nrf52_companion_reader.py b/scripts/check_nrf52_companion_reader.py index df35f2f0b..857aca6b6 100644 --- a/scripts/check_nrf52_companion_reader.py +++ b/scripts/check_nrf52_companion_reader.py @@ -1,7 +1,8 @@ #!/usr/bin/env python3 """Check canonical Reader replies and stack using the linked RAK Full ARM ELF. -Requires unicorn==2.1.4. Every first page and all pages of the three longest +Requires unicorn==2.1.4. A host C compiler accelerates the identical per- +instruction measurement callback; otherwise Python is used. Every first page and all pages of the three longest verses are compared with the independently ASCII-normalized pinned source. --every-page reconstructs every verse. Only replyVerse and its callee frames are measured; the fixed caller reserve is not a physical high-water reading @@ -14,7 +15,8 @@ from pathlib import Path import re import sys from firmware_elf import FirmwareElf -from unicorn import Uc, UC_ARCH_ARM, UC_MODE_THUMB, UC_MODE_MCLASS, UC_HOOK_CODE +from unicorn import Uc, UC_ARCH_ARM, UC_MODE_THUMB, UC_MODE_MCLASS +from arm_instruction_meter import ArmInstructionMeter from unicorn import arm_const as arm ROOT = Path(__file__).resolve().parents[1] @@ -41,11 +43,7 @@ def check(path, every_page=False): uc.mem_write(section[3], elf.section_bytes(section)) stop, top, reply = 0x1000, 0x2003e000, 0x20030000 uc.mem_write(stop, b'\0\xbe') - state = {'minimum_sp': top, 'instructions': 0} - def instruction(machine, address, size, data): - state['minimum_sp'] = min(state['minimum_sp'], machine.reg_read(arm.UC_ARM_REG_SP)) - state['instructions'] += 1 - uc.hook_add(UC_HOOK_CODE, instruction) + meter = ArmInstructionMeter(uc) document = json.loads((ROOT / 'tools/bible/reader-web.json').read_text(encoding='utf-8')) counts = (51, 25, 36, 54, 47, 71, 53, 59, 41, 42, 57, 50, 38, 31, 27, 33, 26, 40, 42, 31, 25) @@ -59,6 +57,7 @@ def check(path, every_page=False): reverse=True)[:3]) maximum, calls, total_instructions, checked_bytes = 0, 0, 0, 0 max_ref = None + maximum_instructions, instruction_ref, instruction_page = 0, None, None guard = b'\x91\xa2\xb3\xc4' for index, ref in enumerate(references): raw = document['verses'][ref] @@ -72,7 +71,7 @@ def check(path, every_page=False): for register, value in zip((arm.UC_ARM_REG_R0, arm.UC_ARM_REG_R1, arm.UC_ARM_REG_R2, arm.UC_ARM_REG_R3), (selection, page, reply, capacity)): uc.reg_write(register, value) uc.mem_write(reply-4, guard + b'?'*capacity + guard) - state['minimum_sp'], state['instructions'] = top, 0 + meter.reset(top) uc.emu_start(elf.address(name) | 1, stop, timeout=10000000, count=10000000) if uc.reg_read(arm.UC_ARM_REG_PC) != stop or uc.reg_read(arm.UC_ARM_REG_SP) != top: raise ValueError('Function return/stack restoration failed for ' + ref) @@ -92,6 +91,7 @@ def check(path, every_page=False): raise ValueError('Canonical linked text mismatch for ' + ref + ' page ' + str(page)) reconstructed += fragment checked_bytes += len(fragment) + state = meter.snapshot() used = top - state['minimum_sp'] if used + 1536 > 8192: raise ValueError('Reader frames exceed 8 KiB with caller reserve: ' + str(used)) @@ -99,6 +99,8 @@ def check(path, every_page=False): maximum, max_ref = used, ref calls += 1 total_instructions += state['instructions'] + if state['instructions'] > maximum_instructions: + maximum_instructions, instruction_ref, instruction_page = state['instructions'], ref, page if next_command is None: if reconstructed != expected: raise ValueError('Canonical text incomplete for ' + ref) @@ -120,7 +122,9 @@ def check(path, every_page=False): 'canonical_source_sha256': source_digest, 'maximum_reply_verse_stack_bytes': maximum, 'maximum_stack_reference': max_ref, 'caller_reserve_bytes': 1536, 'loop_stack_bytes': 8192, - 'total_arm_instructions': total_instructions, + 'total_arm_instructions': total_instructions, 'measurement_backend': meter.backend, + 'maximum_reply_arm_instructions': maximum_instructions, + 'maximum_instruction_reference': instruction_ref, 'maximum_instruction_page': instruction_page, 'scope': 'Actual linked replyVerse instructions and callees; excludes firmware caller/ISR/task and physical high-water.'} diff --git a/scripts/nrf52_flash_trim.py b/scripts/nrf52_flash_trim.py index 0f7ba0783..a90096781 100644 --- a/scripts/nrf52_flash_trim.py +++ b/scripts/nrf52_flash_trim.py @@ -48,6 +48,11 @@ TRIM_DEFINES = { # Keep MeshCore's own startup logo; only omit the invisible library bitmap. "SSD1306_NO_SPLASH": 1, } +# The reviewed main runs the Arduino loop forever. Scheduler failure and every +# normal reset/update path reset the MCU; global process-exit cleanup cannot run. +# Unknown/custom core revisions retain the standard ABI registration behavior. +NRF52_MAIN_NO_EXIT_SHA256 = "25c3a05b2631a6daff58048a517b8f86c33e97aa978d8b365f03a5b4fa371231" + CC310_ARCHIVE_SHA256 = "97dc648d44520e47252f62c402a71f976e36d7b3aef99235bbdc4de79a928577" CC310_ARCHIVE = "src/cortex-m4/fpv4-sp-d16-hard/libnrf_cc310_0.9.13-no-interrupts.a" @@ -149,6 +154,13 @@ def supported_cc310(framework_dir, mcu, defines): return archive.is_file() and hashlib.sha256(archive.read_bytes()).hexdigest() == CC310_ARCHIVE_SHA256 +def supported_program_lifetime(framework_dir): + if not framework_dir: + return False + main = Path(framework_dir) / "cores/nRF5/main.cpp" + return main.is_file() and hashlib.sha256(main.read_bytes()).hexdigest() == NRF52_MAIN_NO_EXIT_SHA256 + + def companion_trim_policy(platform, mcu, src_filter, flags=(), cppdefines=()): """Qualify the actual Companion sources, independent of board/alias name.""" if platform != "nordicnrf52" or not str(mcu).lower().startswith("nrf52"): @@ -237,6 +249,9 @@ def install(build_env): print("nRF52 Companion flash trim: small crypto table, unused USB device classes/GPS cache/vendor splash; fonts and Reader retained") framework = platform.get_package_dir("framework-arduinoadafruitnrf52") values = flag_defines(flags, cppdefines) + if "MESH_NRF52_NO_PROGRAM_EXIT" not in values and supported_program_lifetime(framework): + build_env.AppendUnique(CPPDEFINES=[("MESH_NRF52_NO_PROGRAM_EXIT", 1)]) + print("nRF52 reviewed firmware lifetime: omit unreachable process-exit registrations") if infrastructure and values.get("ST7789") is not None: # Keep the font license with this qualified display build, including # cached DFU packages. The hook independently checks the effective diff --git a/src/Nrf52ProgramLifetime.cpp b/src/Nrf52ProgramLifetime.cpp new file mode 100644 index 000000000..34d1fb973 --- /dev/null +++ b/src/Nrf52ProgramLifetime.cpp @@ -0,0 +1,15 @@ +#if defined(NRF52_PLATFORM) && defined(MESH_NRF52_NO_PROGRAM_EXIT) && MESH_NRF52_NO_PROGRAM_EXIT + +// The qualified Adafruit nRF52 Arduino main never exits: the scheduler runs +// forever and its only return path invokes NVIC_SystemReset(). Device reboot +// and update paths also reset the MCU rather than terminating a C++ process. +// Do not retain registrations for global process-exit cleanup which cannot +// run. Automatic RAII destructors, explicit close/shutdown and dynamic delete +// remain ordinary C++ operations and are unaffected by this ABI entry point. +// nrf52_flash_trim.py enables this only for the exact reviewed core main hash. +extern "C" __attribute__((visibility("hidden"))) int __aeabi_atexit( + void*, void (*)(void*), void*) { + return 0; +} + +#endif diff --git a/src/helpers/CompanionReader.cpp b/src/helpers/CompanionReader.cpp index 422a0e76b..3f454a126 100644 --- a/src/helpers/CompanionReader.cpp +++ b/src/helpers/CompanionReader.cpp @@ -4,7 +4,11 @@ #include #include "bible/ReaderLookup.h" +#if defined(NRF52_PLATFORM) +#include "bible/ReaderStreamData.generated.h" +#else #include "bible/ReaderData.generated.h" +#endif namespace mesh { bible::LookupResult readReaderVerse(bible::Reference ref, char* scratch, diff --git a/src/helpers/bible/ReaderStreamData.generated.h b/src/helpers/bible/ReaderStreamData.generated.h new file mode 100644 index 000000000..b651eca54 --- /dev/null +++ b/src/helpers/bible/ReaderStreamData.generated.h @@ -0,0 +1,1461 @@ +// Generated by tools/bible/pack_reader_stream.py; do not edit. +// Offline Reader corpus. Public domain; see tools/bible/README.md. +// Google Zopfli, 1000 iterations, 4096-byte history; exact ASCII text retained. +// Canonical source SHA-256: 916bf9d1feae0ad410d69f61e6c5f328ac6acba10651d751b5bb29719732c49e +#pragma once +#include "ReaderStreamLookup.h" +namespace mesh { namespace bible { namespace generated { +static_assert(kBlockSize == 4096, "Reader history must remain 4096 bytes"); +static const uint8_t readerData[] = { + 0x00,0x00,0x3f,0x42,0x01,0x25,0x85,0x77,0x59,0x7b,0x14,0xd7,0xb9,0xae,0x7e,0xca,0x32,0x37,0x8a,0x9f, + 0x67,0xa5,0x7f,0xc0,0xe6,0x82,0xcc,0x31,0x39,0x3b,0xc3,0xb3,0x9d,0xf3,0x64,0xb8,0x2b,0x75,0x2d,0xa9, + 0x2b,0xaa,0xae,0xd2,0xae,0x55,0x8d,0xdc,0xfb,0xaa,0xaa,0xba,0x55,0xb4,0x24,0x2a,0x0d,0xee,0x06,0xc9, + 0x2d,0xc5,0x68,0x40,0x08,0x43,0x24,0x40,0xb6,0x00,0xe3,0x60,0x47,0xd8,0x41,0x51,0x4e,0xec,0x36,0x78, + 0x8c,0x83,0x77,0xb0,0xf0,0x84,0x87,0x0c,0x64,0xb2,0xe5,0x80,0xcf,0xfb,0xad,0x55,0xd5,0x6a,0xb0,0xcf, + 0x3e,0x19,0x44,0xf5,0x1a,0xbe,0xf5,0xcd,0xdf,0xfb,0xee,0x76,0x82,0xf5,0x01,0xcb,0x71,0x2c,0x67,0xa0, + 0x11,0xb0,0x1f,0xb8,0x9e,0xc9,0x23,0xfd,0x6f,0x63,0x76,0xa5,0xf3,0xb9,0x92,0xeb,0x79,0x89,0x49,0xa3, + 0x28,0x1a,0xcd,0xce,0xf1,0x59,0x2c,0x7e,0xd9,0xb6,0x1f,0x3a,0xfb,0x08,0xf3,0x0b,0x9e,0x5b,0x1a,0x28, + 0x54,0x73,0xec,0x07,0x96,0x5f,0x70,0x4b,0x7e,0x95,0x2f,0x16,0x48,0xe2,0x23,0x95,0xe5,0x75,0xe1,0x3c, + 0x92,0xeb,0xd9,0xed,0x54,0x1b,0x17,0x21,0xee,0x22,0x1e,0xb1,0xad,0x81,0x82,0x5f,0x9b,0x70,0x20,0x53, + 0x7f,0x33,0x89,0xc3,0x42,0x36,0x03,0x66,0x1a,0xde,0x20,0xbe,0x24,0x8f,0xb6,0xbf,0x97,0x9d,0x5e,0x9f, + 0xb9,0x7b,0x84,0x97,0x77,0x8b,0x62,0x15,0x97,0x3c,0x71,0xb4,0xbd,0x76,0x7e,0x6a,0x85,0xd7,0xa5,0x60, + 0x0e,0x29,0xc5,0xbe,0xe5,0x16,0x9c,0x54,0x45,0x6c,0xca,0x36,0x1b,0xb6,0x7c,0x25,0xa7,0x92,0xb0,0xa2, + 0x7a,0xc2,0x17,0xd2,0xb7,0xfa,0xcb,0xa7,0xd3,0xe7,0x79,0xe5,0xb2,0xde,0x98,0xd9,0x56,0xbe,0xe7,0x3e, + 0xd1,0x58,0xcc,0xf6,0xe7,0x1a,0xe7,0xff,0x5f,0x77,0xe9,0x21,0xdf,0x2b,0x09,0xfd,0xab,0xc2,0x84,0xa3, + 0x3e,0x84,0x23,0x99,0x80,0x9a,0x65,0xd7,0x81,0x46,0x50,0x16,0x0e,0xb8,0x11,0x3c,0xac,0xc4,0x36,0x83, + 0x87,0x61,0xd2,0xc3,0x8d,0x6d,0x57,0xd1,0xcf,0x6b,0x64,0x99,0x27,0xf2,0xee,0x80,0x63,0xfd,0x97,0x50, + 0x0a,0x1c,0x8d,0x27,0x99,0x3b,0xec,0xf0,0xe8,0xf1,0xfa,0x59,0xf5,0x45,0x67,0x0e,0x60,0xeb,0x2b,0x25, + 0xbf,0x2d,0xd7,0xca,0x6d,0x79,0xc0,0xac,0xf2,0xf8,0x76,0xf2,0x7c,0xc0,0x3c,0x7a,0x35,0x5e,0x27,0xb7, + 0xac,0xf4,0x4a,0x96,0x2f,0x58,0xb6,0xe9,0x09,0x87,0xc7,0x8f,0xd7,0x67,0x9a,0x93,0xca,0x33,0xff,0xd6, + 0x33,0x5c,0x70,0xcf,0xb2,0x3e,0xd7,0x73,0xf8,0x62,0x8d,0xf5,0xd9,0xae,0x6b,0xf2,0x9b,0x5e,0x2d,0x18, + 0xab,0x05,0xac,0xdf,0x16,0xb2,0x90,0xfe,0x5a,0xe3,0x73,0x35,0x0a,0xb2,0x0a,0xf7,0x7a,0x1e,0x57,0xf5, + 0x76,0x04,0x23,0xf7,0x08,0xb3,0x5d,0x74,0x9d,0x01,0x56,0x92,0x08,0xb0,0x38,0x36,0xc9,0x06,0x6c,0xd7, + 0x2b,0x73,0x26,0x4b,0xf9,0x82,0xfe,0x6e,0xcb,0x5a,0x70,0xcb,0x2e,0xab,0x87,0xde,0xac,0x05,0xfb,0x38, + 0xeb,0x2f,0xd9,0x76,0x8d,0x0d,0x78,0x46,0x5e,0x44,0xe4,0x2d,0xbf,0x90,0xeb,0xa1,0x28,0xa5,0xce,0xb4, + 0x84,0x79,0x1a,0xe9,0x72,0x9f,0x60,0x79,0x0f,0xdf,0x67,0xf8,0xdb,0xf0,0x16,0x67,0x3b,0xbe,0x5f,0xb0, + 0x64,0x23,0xa9,0xd5,0x8b,0x0b,0x23,0x9c,0xf5,0xde,0x27,0xea,0x4b,0xb2,0xdd,0xef,0x0b,0x6f,0x62,0x19, + 0xaf,0x79,0x43,0x86,0x94,0xc2,0x9c,0xe0,0xd3,0x49,0x63,0xfd,0x4f,0x62,0x22,0xd7,0xbb,0xa3,0xe7,0x1b, + 0x9e,0x5b,0x9c,0x54,0xaf,0x51,0xbc,0xaf,0x5f,0x3e,0x60,0xea,0x47,0xaf,0xb8,0x8e,0xfe,0xc8,0xf5,0x7c, + 0xc3,0xf5,0x10,0x39,0x63,0xb8,0x71,0xc4,0xc9,0xdc,0xcf,0xbe,0xed,0x4a,0x01,0x6b,0xbe,0xd9,0xa5,0xdf, + 0x59,0x84,0xc2,0xb0,0x11,0x08,0x33,0x3b,0x35,0xfe,0x4e,0xae,0xe7,0x3b,0xee,0xd6,0xf2,0xa6,0xb3,0xd2, + 0xf6,0xdb,0x4e,0xf9,0xdd,0x1c,0x7b,0xa9,0x63,0x25,0xaf,0xb7,0x9a,0x01,0x3e,0xa5,0x5b,0x24,0x83,0x97, + 0x99,0x29,0xf2,0xb6,0xe1,0x09,0x13,0xc1,0x22,0x2b,0x5a,0x2a,0x29,0x7b,0xa5,0x36,0x18,0xfe,0x2b,0xf3, + 0x0b,0xc1,0x89,0xf3,0x6c,0xc8,0xb3,0xb0,0x20,0x23,0xf6,0xef,0x62,0x8f,0x85,0xbd,0xa9,0x57,0xe3,0xb6, + 0x1c,0xac,0xc2,0xf6,0x1f,0x14,0xdc,0xe3,0xe1,0xae,0x1d,0xc8,0x82,0x8e,0x2c,0x1e,0x51,0xfc,0xf1,0x13, + 0xb7,0xe7,0x92,0xce,0x32,0x0e,0xef,0x6e,0x17,0x17,0x83,0x77,0x72,0x3b,0x7a,0x5e,0x2a,0xe3,0xba,0x30, + 0xb5,0x00,0xc3,0x0f,0x9c,0x5d,0xec,0xcb,0x9e,0x08,0xd9,0xd7,0x6d,0xeb,0x27,0x46,0x61,0xd7,0x0e,0xf8, + 0x78,0x24,0x3d,0x9f,0xdb,0xc1,0x76,0xd0,0x5e,0xf0,0x86,0x5a,0xde,0x8f,0xe5,0xef,0xb8,0x4a,0xc4,0xc8, + 0xde,0x78,0x5b,0x03,0xf6,0x4d,0x84,0x1c,0xc1,0x6e,0x3b,0x6d,0x47,0x0e,0x0b,0x2f,0xfe,0x3d,0xeb,0x33, + 0xf2,0x83,0x48,0xab,0xf3,0x3a,0x05,0xf0,0xcc,0x46,0xf8,0xf6,0xe9,0xd0,0x93,0xc2,0xee,0x27,0x85,0xb3, + 0x17,0x02,0xb6,0xc7,0xb5,0xf2,0xa2,0xb6,0x85,0xa8,0x96,0x11,0x50,0x38,0x68,0x18,0x49,0x29,0x3c,0x55, + 0x8b,0xac,0xf7,0xdb,0xc6,0xa0,0x60,0xd2,0xf7,0x0c,0xca,0x5c,0xec,0x19,0x65,0xa4,0xe1,0xef,0x78,0x6f, + 0x5b,0x2e,0x48,0xc3,0x32,0x0a,0xc1,0x1b,0x23,0xa4,0xce,0x96,0xac,0x9f,0x43,0xdb,0x38,0x7f,0x76,0x2a, + 0xf8,0x75,0xee,0x4e,0x03,0xcb,0x81,0xb3,0x11,0x42,0x9d,0x21,0x1f,0x81,0x62,0x56,0x7f,0x78,0x1c,0x5e, + 0x40,0x0e,0xa7,0xe6,0xe2,0x8b,0x8c,0x53,0x99,0xb6,0xff,0x36,0x29,0x95,0x9d,0x6d,0xe2,0x35,0xe4,0x11, + 0x9f,0x53,0xa9,0x1c,0x42,0x8b,0x0f,0xe4,0x56,0xbd,0x18,0x6e,0xc0,0xbf,0x87,0xa9,0xf2,0x5a,0xc1,0x56, + 0x27,0xd9,0x10,0x5b,0xc4,0x49,0xf4,0x0b,0x0f,0xce,0xa6,0x4c,0x53,0xad,0x46,0x7e,0x60,0xd8,0x4a,0xfb, + 0xa1,0x85,0xde,0xe2,0x22,0x1b,0x76,0x3d,0xbf,0x50,0x8e,0x19,0x6a,0x4a,0x0a,0x87,0xf4,0x96,0xe2,0xa1, + 0xb3,0x1b,0x0e,0x9e,0xfa,0x8a,0xf0,0x0b,0x86,0x53,0x5e,0x47,0x2b,0x30,0x03,0xe4,0x81,0x67,0x1a,0x0e, + 0x7f,0x54,0x25,0x44,0x23,0x55,0x08,0xde,0xa1,0x72,0x73,0xc4,0x03,0xfe,0x27,0x3c,0x39,0x36,0x9e,0x36, + 0x0c,0x84,0x21,0x22,0x67,0x7e,0x45,0x14,0x5c,0xdb,0xe4,0x01,0xfb,0x77,0xa3,0xd8,0x57,0x43,0xab,0xfb, + 0xbd,0x6c,0xc3,0x5d,0x01,0x93,0x96,0x53,0x0b,0x1e,0xbe,0x47,0xa7,0x58,0xa7,0x50,0xbe,0xac,0xd4,0x86, + 0xfa,0x6b,0x77,0x6b,0xbe,0x5d,0x2b,0x3d,0xbb,0xaf,0x91,0xad,0x55,0x3e,0x37,0x7d,0x88,0x52,0x5d,0xba, + 0xce,0xc2,0xd1,0x6d,0x6d,0x32,0x07,0x55,0x92,0xa7,0xd6,0xff,0x10,0x2f,0x48,0xcf,0x10,0x36,0x8c,0xba, + 0xb3,0x66,0xb3,0x32,0xdd,0x3d,0xba,0xe9,0x04,0xec,0xfe,0x21,0xcb,0xb3,0x28,0x3b,0x65,0x5e,0x38,0x26, + 0x76,0xd0,0x2c,0x06,0x45,0x7b,0x63,0x8f,0x38,0x53,0x4b,0x8c,0x3d,0x02,0x6d,0x6c,0xf5,0x2f,0xc2,0xbc, + 0x55,0x45,0xff,0xbf,0xab,0xd7,0x21,0x9b,0xeb,0xe7,0x27,0xe2,0xbb,0xa2,0x33,0x12,0x4f,0xc0,0x98,0xef, + 0x3a,0xf5,0x22,0x35,0xd2,0x70,0x6c,0xf3,0x73,0xde,0x88,0xfe,0x82,0x3f,0xb7,0xaa,0xad,0xa4,0x9e,0x5d, + 0xa6,0xc1,0x71,0x9f,0x6b,0x97,0xd3,0xb3,0xb0,0x94,0xd4,0x8b,0x46,0xb7,0xd5,0xae,0x1c,0x6a,0x05,0x6f, + 0xd6,0x56,0x60,0xce,0x97,0x07,0x0c,0xcb,0xe1,0x41,0xea,0xf7,0x34,0x22,0xc8,0x05,0x9a,0x67,0xcc,0x1f, + 0x76,0x6b,0x93,0xaf,0x4b,0xde,0xf3,0x41,0x42,0x51,0x45,0xd2,0xb5,0xfd,0xf1,0xb6,0x4c,0xa0,0x9c,0x8d, + 0x1f,0x9f,0x1b,0x99,0x7b,0x10,0x77,0xba,0xf8,0xba,0x4c,0x0c,0xcf,0xac,0x3e,0x87,0xbe,0x5e,0xfe,0xa9, + 0x30,0xc7,0x73,0x3d,0x3f,0x63,0x7e,0x09,0x89,0x6f,0x46,0xc7,0x6e,0xff,0x14,0xe2,0x71,0x3b,0xbe,0x9d, + 0x16,0xe9,0xf1,0x50,0xc9,0xc7,0xea,0x34,0x2a,0x11,0xe5,0xa7,0xaa,0xef,0x3f,0x8c,0xbe,0x3e,0x6b,0x07, + 0xfb,0xc2,0x70,0xc1,0xca,0x17,0x5a,0xf1,0xdb,0x7c,0x9d,0xc2,0x02,0xb7,0x20,0xa0,0x3e,0x15,0xfd,0xf7, + 0x85,0x91,0x2f,0x08,0xef,0x5e,0x9c,0x1d,0xc6,0xbf,0x02,0x62,0xa4,0x6f,0x50,0xa1,0xe9,0x22,0x54,0xf2, + 0xbf,0x8a,0x71,0x10,0x6d,0xe6,0x48,0xec,0xd1,0xe8,0xd8,0xa3,0x49,0x23,0x3b,0x43,0x9a,0xa9,0x6f,0x61, + 0xce,0x56,0x2b,0x9f,0xe4,0x16,0xfc,0x06,0x26,0x1a,0xc6,0x96,0x5f,0x60,0x98,0xd9,0x5e,0xae,0xe7,0xbb, + 0x8e,0xa8,0x05,0x64,0x4c,0x9d,0x6c,0x51,0xce,0x89,0x60,0x4b,0xb5,0xc1,0xbe,0xec,0x60,0xb2,0x0c,0x73, + 0x76,0x3f,0x35,0xb2,0xdf,0xa0,0xa9,0xf5,0x79,0xae,0x0f,0x15,0xa8,0x70,0x7e,0xcb,0xfa,0xdd,0x92,0x63, + 0xaa,0x69,0x95,0xad,0xa7,0x27,0xc9,0x66,0x55,0xb7,0x62,0x54,0x1f,0x0a,0xd8,0xb7,0x85,0x94,0xa8,0xf4, + 0x7b,0x3a,0x76,0xde,0x6d,0xe5,0x3b,0xf7,0x92,0x50,0x12,0x84,0x0e,0xec,0x57,0xe3,0xf1,0xdc,0x78,0x27, + 0x18,0x55,0x15,0x81,0x17,0x8f,0x6b,0xe9,0xc1,0x55,0xa7,0x06,0x25,0x1d,0xa3,0x90,0x63,0x2f,0x02,0x43, + 0x18,0xb6,0xbd,0xfe,0x29,0xfb,0xaa,0x18,0x2a,0x18,0xb2,0x23,0xfe,0xd9,0x4c,0xb4,0xe1,0x5b,0x50,0xe8, + 0x37,0xf7,0x92,0x99,0x59,0xfc,0xe1,0x1c,0x53,0x60,0x1f,0x95,0x27,0xcc,0xf8,0xe5,0x33,0x37,0xd8,0x37, + 0x31,0x01,0x6c,0x21,0x78,0x94,0x68,0x85,0xd9,0x87,0xb9,0x71,0x6d,0xc3,0x37,0x5c,0xdb,0x76,0x87,0x27, + 0x72,0x3b,0x30,0x12,0x86,0xd9,0x87,0x8d,0x29,0x55,0xe5,0x68,0x5b,0xa6,0x81,0x64,0xc8,0x5b,0x7e,0xb9, + 0x96,0xba,0x29,0xfa,0x4d,0xae,0xe7,0xc3,0xf4,0xfa,0x77,0x0c,0xea,0x04,0x28,0xa5,0xcf,0xb8,0xa2,0x8a, + 0xda,0xd5,0x63,0xa8,0xa9,0xa6,0x53,0x74,0x2a,0x78,0x43,0x72,0x36,0x0c,0x07,0x8a,0x7f,0x1b,0xaf,0xe1, + 0xe6,0x7f,0x19,0x1e,0x5e,0xe0,0xa9,0x99,0x12,0x76,0xd1,0xe3,0x99,0x40,0x2d,0xee,0xab,0x86,0x83,0xc1, + 0xf4,0xb2,0x4b,0x33,0x0b,0x7e,0x5c,0x3a,0xb3,0x7d,0x11,0x99,0xf5,0xa1,0x3e,0x94,0xa5,0x44,0xcf,0xcf, + 0x8e,0x6d,0x2b,0xd4,0xdd,0x70,0x4e,0x57,0xb7,0x13,0xbb,0xed,0xe8,0xda,0xc7,0x6c,0x6a,0x9a,0x02,0x01, + 0x69,0xd6,0x8b,0xad,0x9b,0x34,0x7a,0x84,0xe5,0xdf,0xf3,0x19,0x05,0xee,0x73,0x87,0x37,0xc2,0xc3,0x13, + 0xbb,0x76,0x8c,0xef,0xd7,0x42,0xfe,0x24,0xd8,0x87,0x9f,0x86,0xfc,0x42,0x78,0x96,0xc1,0x4a,0xe1,0x01, + 0x67,0x58,0x03,0x98,0xb0,0x70,0xea,0xc2,0xb1,0xb0,0xdb,0x82,0xfd,0x9d,0xa4,0xe7,0xe1,0x71,0xaa,0xcf, + 0x7b,0x18,0x42,0xfb,0xbf,0xa0,0x54,0x4d,0xab,0x70,0x0f,0x34,0x4e,0xa5,0xe6,0x8d,0x92,0x14,0x0b,0xb1, + 0x6d,0x86,0x68,0x10,0xbb,0x8f,0x85,0x5a,0xb6,0x23,0x20,0xab,0xeb,0x81,0xde,0x8d,0x70,0x66,0x17,0x7b, + 0x71,0x6c,0x13,0xa3,0x5f,0x50,0x43,0x79,0xe8,0xc9,0x40,0x8a,0x7b,0x74,0x71,0x90,0x20,0xf8,0xdb,0xff, + 0x85,0x5d,0xe6,0x0b,0xff,0x27,0xbc,0xcc,0x13,0x9c,0xe9,0xf7,0x55,0x9f,0x51,0xdd,0x8a,0xb9,0x43,0xc2, + 0xa1,0x32,0x0f,0xda,0xce,0x80,0xb0,0x65,0x6d,0xa5,0xdd,0x69,0x3a,0x5d,0x0d,0xe8,0x16,0x54,0x65,0xdf, + 0x36,0x54,0xc7,0x27,0xaf,0x7b,0xe6,0x27,0x3c,0xf0,0x44,0xa3,0x7d,0xdd,0x34,0x55,0x1f,0x45,0x4c,0x8c, + 0x5a,0x96,0x49,0xb9,0xf1,0x5e,0x56,0x54,0x35,0xd1,0xc0,0x21,0x74,0x85,0x53,0x8d,0x26,0xcd,0x7d,0x93, + 0xcf,0x52,0xab,0x89,0x03,0x7d,0x2b,0xd7,0xf3,0x83,0x82,0x70,0x68,0x5c,0x3a,0x82,0x79,0x86,0x73,0x86, + 0x77,0xee,0x69,0xcd,0x5f,0x2a,0x8f,0xde,0x54,0xbb,0x14,0xc6,0x91,0x38,0xf1,0x28,0x97,0xdc,0x22,0xe6, + 0xca,0xc7,0x1b,0x42,0x56,0x46,0xe3,0x8d,0xd9,0x30,0x9a,0xd8,0xc5,0xbe,0x5d,0x56,0xc5,0xbc,0xbc,0xc8, + 0xca,0xc2,0x5f,0xca,0xc1,0x74,0x4b,0x76,0x04,0x05,0x4c,0x0a,0x6f,0x8f,0xe1,0xf8,0x32,0x6d,0x44,0xd4, + 0x66,0x93,0xb7,0x65,0x1c,0xf2,0x8d,0x55,0x9d,0xd9,0x50,0xf2,0x2c,0x93,0xd6,0x03,0xba,0x21,0xb3,0x21, + 0xd7,0x97,0x35,0x26,0x7d,0x60,0x5a,0xdc,0xf5,0xb1,0xab,0x26,0x64,0x70,0xa2,0x57,0x4d,0x6f,0x36,0x54, + 0xf2,0xac,0x7e,0xd5,0x5d,0x90,0x4f,0x8e,0x8f,0xe6,0x8a,0x6f,0x6a,0x21,0xcc,0xf5,0x08,0x46,0x09,0x31, + 0xe1,0x53,0x5d,0xcb,0xf6,0x90,0x25,0xf2,0x64,0xbf,0xee,0x52,0xdf,0xb0,0x6c,0x3b,0xe8,0x7a,0x63,0x56, + 0x7f,0xa3,0x6d,0xdd,0xef,0xa2,0x4d,0xf5,0x63,0x5b,0x98,0xb7,0xaf,0x40,0xe3,0x3e,0xcf,0x2a,0xe6,0x3a, + 0xdd,0x8d,0x6a,0xcf,0xf4,0x8c,0xe1,0xab,0x45,0x71,0x86,0x47,0xbf,0x5f,0xc5,0x01,0xaf,0x64,0x0b,0x8f, + 0xe0,0xac,0x30,0xa4,0x9f,0x0a,0x88,0xdd,0xc1,0x55,0xed,0xd2,0xbb,0xf6,0x99,0x8f,0x3f,0xc2,0x4c,0x9f, + 0x7e,0x45,0x81,0x67,0xe5,0x56,0x8d,0xb4,0x0e,0x3f,0xba,0x7a,0x74,0x8a,0x7d,0xa1,0xaf,0xe4,0x77,0x5c, + 0x05,0x2c,0xa2,0x5e,0x74,0xd2,0x3b,0xaf,0xdd,0xcb,0xef,0x92,0xf9,0xa9,0x52,0xd2,0x14,0x03,0x9e,0xeb, + 0x16,0x7b,0x3e,0xd0,0xf1,0xfa,0x7a,0xca,0x04,0x94,0x18,0x21,0x03,0x94,0xa8,0x7a,0xe7,0xb7,0x3c,0xba, + 0x10,0xb0,0x81,0x12,0x61,0xc0,0x51,0x08,0x2e,0x39,0x83,0xac,0x1f,0x6e,0x42,0x52,0x06,0x4e,0x65,0xbe, + 0x45,0xb0,0x42,0x0a,0x34,0xb4,0x51,0x36,0x28,0x86,0xfc,0xce,0x3d,0x24,0xbc,0x6f,0xd9,0xaf,0x20,0x97, + 0x69,0xf2,0x67,0x1c,0xac,0x36,0xf9,0x84,0x1c,0xbf,0x76,0x57,0xca,0xf1,0xe8,0x0f,0x29,0x44,0xdf,0x19, + 0x21,0xc7,0x66,0xcc,0x26,0x06,0xb0,0x42,0x08,0x87,0x78,0x72,0xdd,0xf1,0x37,0x86,0x9d,0x18,0x37,0x86, + 0x50,0x3f,0x46,0xa9,0xc8,0x13,0x1e,0x4d,0xa6,0x19,0xc2,0x27,0xb3,0x06,0x2e,0x39,0x5d,0xdd,0xb9,0x3d, + 0x32,0x28,0xf2,0x30,0x77,0xf8,0x13,0x49,0xb0,0xe5,0x7b,0x86,0x94,0x44,0xc9,0x6a,0xc1,0x89,0x46,0xdb, + 0x67,0x85,0x0f,0x78,0x34,0x0e,0xc9,0x57,0xe2,0x57,0x29,0x54,0xba,0xc1,0x35,0x69,0xb8,0x14,0x87,0x6c, + 0xf1,0x78,0xfd,0xaa,0x6d,0x32,0xf7,0x01,0xe1,0xf0,0xf7,0xc4,0x10,0x8f,0x80,0x0f,0x14,0xc1,0xcb,0xa3, + 0x0d,0x0c,0xe0,0xa9,0x1a,0x5e,0x77,0x04,0x1e,0xb2,0x7c,0x1f,0x36,0x91,0x84,0x47,0xda,0x0c,0xed,0x7b, + 0xa8,0x86,0xa4,0xf2,0x4c,0x19,0xc1,0x4f,0x78,0xed,0xf2,0x99,0x5a,0x26,0x92,0x03,0x78,0xa3,0xf2,0x21, + 0x0e,0x72,0x48,0xf2,0x4e,0x34,0xec,0x21,0xe4,0x3d,0xa8,0xc4,0xb6,0xe0,0x5e,0x2d,0x38,0x52,0xec,0x91, + 0x92,0x71,0x38,0xb0,0x3c,0x64,0x40,0x9f,0x2d,0x24,0x40,0xba,0xab,0x14,0x0b,0x94,0x3a,0xc9,0x08,0x91, + 0x0f,0xc0,0xd3,0x00,0x20,0x0e,0xe8,0xc5,0x13,0xf7,0xb0,0xaf,0xb9,0x84,0x59,0x8a,0x58,0x3c,0xb9,0xaf, + 0x57,0x52,0x59,0x49,0xd1,0xc6,0x6f,0x6f,0x50,0xf8,0x43,0xb6,0x91,0x57,0x8d,0xc5,0x92,0xaf,0x4b,0xe6, + 0x89,0xa2,0x28,0xf6,0x09,0xbc,0x5e,0x59,0x6d,0xfc,0x15,0x92,0x7e,0x0c,0x26,0x31,0x1d,0x7a,0xfa,0xce, + 0x18,0x43,0x2f,0x9a,0xb8,0x42,0xfd,0xe2,0xc4,0xde,0xfd,0x19,0x50,0x7f,0x02,0x38,0x56,0x16,0x90,0xce, + 0x25,0xc9,0x37,0x61,0x76,0x25,0xdc,0xa0,0xb7,0x77,0x51,0xcf,0xa3,0x4c,0xff,0x1a,0x32,0xc4,0x73,0xcb, + 0x87,0x32,0x8b,0xa3,0xa6,0xae,0xa7,0x4f,0xe4,0xc2,0x18,0x03,0x82,0x96,0x62,0x35,0x15,0xa9,0xc0,0xb7, + 0x8f,0x94,0x9f,0xf6,0xcb,0x5f,0xa4,0xd2,0x2d,0x0b,0xc3,0x93,0xc0,0x56,0x25,0x60,0xef,0xec,0xfe,0x3d, + 0xa0,0xe6,0xb6,0x1d,0x66,0x37,0x3b,0xc2,0xf0,0x1e,0xa8,0x65,0xf2,0xaf,0x8e,0x6b,0x6b,0xc8,0x01,0xd7, + 0x2c,0xeb,0xea,0xd9,0x9b,0x34,0xf4,0x0d,0x73,0x0a,0x84,0x5c,0x18,0x26,0x9f,0xbc,0xd3,0xdc,0x64,0xe4, + 0x10,0xe1,0x8a,0x19,0x33,0xf8,0x63,0x14,0x3c,0x38,0x3f,0x7e,0x6e,0x24,0x47,0x5d,0xe4,0x42,0xd2,0x68, + 0xbe,0xda,0xf6,0x83,0x4e,0xa2,0x70,0x66,0x96,0x3c,0x58,0x99,0x96,0x0c,0x5f,0xc3,0x95,0x94,0xa3,0x72, + 0xe6,0xf6,0x51,0x91,0x60,0x17,0xd9,0x3c,0x9f,0x5c,0x53,0x64,0x77,0x5c,0x01,0x46,0xe1,0x80,0x9a,0x49, + 0xff,0x12,0x6a,0xff,0x60,0xf2,0x5a,0x87,0x5e,0x03,0xa1,0x1d,0x4c,0xd4,0x01,0x47,0x08,0x73,0xba,0xed, + 0xd0,0x62,0x9c,0xf1,0xf4,0xbf,0xad,0xed,0x9c,0x4e,0x2e,0xbd,0xf6,0x71,0xa3,0xb9,0xa6,0x94,0x51,0xcd, + 0x79,0xad,0x16,0xfc,0x5a,0x3d,0x87,0x99,0x6c,0xe5,0x5d,0x53,0x14,0xe1,0xf7,0x76,0x56,0xc8,0x27,0x34, + 0xf7,0x1e,0x7f,0x96,0x39,0x44,0x4d,0xa2,0x2e,0x40,0xc6,0xaf,0x1f,0xae,0x84,0xc7,0xdb,0xcc,0xd7,0xe0, + 0x6b,0x69,0x6a,0x85,0x4f,0xdf,0x04,0xc1,0x31,0x1c,0x8a,0xd7,0x13,0x12,0x71,0xe3,0x2f,0xac,0xb4,0x66, + 0xab,0x39,0x8a,0x5c,0xd7,0x90,0xc1,0x8c,0xe1,0x2f,0x6c,0xb5,0x14,0x5b,0x6c,0x3b,0x40,0x4f,0x09,0x2e, + 0x41,0xe7,0x4d,0x90,0x75,0x35,0xe5,0x4c,0xb7,0x88,0x3b,0x1d,0x6d,0x3a,0xf3,0x94,0x8e,0xb5,0xd7,0xd6, + 0xd5,0xc5,0x0b,0x49,0x8b,0x61,0x2a,0xef,0xa2,0x0a,0x4f,0xfe,0xd9,0x66,0x52,0xa0,0xcd,0x9a,0xef,0xde, + 0xc8,0x2a,0x16,0xa2,0x86,0xdd,0x62,0x5f,0xa4,0x4f,0xab,0xe4,0xf9,0x7c,0x0d,0x6a,0xba,0x7b,0x45,0x29, + 0x48,0xce,0x94,0xf9,0xe7,0x8d,0x6e,0x6d,0xd0,0x5b,0x0c,0x7f,0x3e,0xbd,0x80,0x50,0xa1,0x4e,0x0a,0x2d, + 0xfd,0x4f,0x8e,0xdd,0xb9,0xa7,0xc5,0xb4,0x98,0xd4,0x98,0xbb,0x27,0x2b,0x16,0x34,0x3b,0xbb,0xb2,0x30, + 0x12,0xd3,0x9c,0x7e,0x91,0xc1,0x2c,0xff,0xf2,0x7a,0xc7,0x05,0x80,0xe6,0x2f,0x51,0x37,0x33,0x59,0x1f, + 0xb0,0x94,0x7c,0x74,0x95,0x0d,0x53,0x87,0x8d,0x79,0x14,0x02,0x71,0xae,0xca,0xab,0x68,0x1b,0x7c,0x6e, + 0x43,0x37,0xe2,0x25,0x39,0x15,0x3d,0xba,0xda,0x7a,0x3f,0x87,0x8e,0xde,0xea,0x84,0xbe,0x7e,0x97,0x06, + 0xdd,0x1e,0xdc,0xdf,0xe5,0x40,0xaa,0xa5,0xf5,0x5d,0x59,0x48,0x14,0xeb,0xcd,0x42,0x98,0xa2,0x0b,0x1e, + 0xd1,0x43,0x1a,0x48,0x80,0x01,0xa8,0xe2,0xeb,0xe9,0xf2,0xdc,0xf5,0xe7,0x2a,0xf3,0xd7,0x0f,0x47,0x59, + 0x56,0xd5,0xf0,0x0b,0xb4,0x02,0xaa,0xd2,0xb5,0x03,0x0c,0xcd,0x06,0xa6,0xf8,0xc4,0x6c,0x41,0x6f,0xfa, + 0x15,0x36,0x41,0xa1,0xa3,0xcf,0xd8,0xe5,0x87,0xf4,0xa1,0x19,0xce,0x50,0xe0,0x63,0xe1,0x0c,0x88,0x2a, + 0x24,0x2a,0x90,0x81,0xcd,0x5d,0x48,0xca,0xad,0x65,0x8d,0x2d,0x84,0x79,0x43,0x2d,0x83,0x0e,0xa1,0xbe, + 0xd2,0xa5,0x8c,0x3d,0xa5,0x40,0x03,0x6c,0xb4,0xa9,0x16,0xd0,0xc5,0xa5,0x86,0x8c,0x20,0x5a,0xe8,0xe7, + 0xe6,0x15,0x35,0xa5,0x80,0x5a,0xfc,0xbb,0x98,0xb6,0xc0,0xe9,0xab,0xca,0xfb,0xa9,0x8c,0xf5,0xec,0x06, + 0xef,0xf1,0x11,0xc7,0x3a,0xb9,0x73,0x46,0x36,0xab,0xff,0x58,0x64,0x98,0x03,0x96,0x2c,0xf0,0xb9,0xd1, + 0x3f,0x5f,0xcc,0xf5,0x7c,0xc3,0xf5,0x56,0xae,0xfe,0xdc,0x0c,0x1e,0x06,0x0f,0x7c,0x7e,0xf2,0x96,0x5d, + 0x56,0xde,0x7e,0x93,0x57,0xfe,0xc7,0x1b,0xaa,0x14,0xa5,0x00,0x09,0x78,0xf3,0x46,0xf0,0x70,0xfc,0x2b, + 0x5c,0x9f,0xab,0x04,0x0f,0xff,0x63,0x9d,0x49,0xe8,0x4d,0xd8,0x54,0xa1,0xf9,0x2a,0x55,0x59,0x9d,0x84, + 0xb4,0x16,0x7f,0x65,0xe6,0x18,0x7e,0x00,0xbb,0x90,0xa3,0x96,0x41,0xf7,0x7f,0x65,0xfe,0x37,0x8a,0x7c, + 0x79,0x11,0x8d,0x21,0x50,0x95,0x5a,0x0b,0xb2,0xf7,0x89,0xbb,0x69,0xc6,0x1b,0x3c,0xc6,0x2b,0x80,0xcb, + 0x54,0xa4,0xcb,0x4b,0x78,0x8b,0x47,0x13,0x0e,0xd4,0x65,0x26,0xba,0x32,0x99,0x8e,0x6e,0x45,0x65,0xf1, + 0x64,0x7a,0x86,0x4f,0xa3,0xe3,0xd3,0x48,0x1d,0x94,0x67,0xe1,0x14,0xcb,0x56,0xfa,0x6e,0x27,0x13,0x9e, + 0x57,0xcb,0x8c,0xb0,0x90,0x4c,0x2f,0x45,0x5a,0xa9,0xa5,0x38,0x13,0xc2,0x50,0x00,0xfe,0xa4,0x16,0xf3, + 0xd4,0x3a,0x13,0x0f,0x0c,0x21,0x06,0xa6,0x6a,0x53,0x09,0x89,0x08,0x18,0xba,0x94,0x5f,0x58,0x92,0x9d, + 0x1b,0x95,0xf4,0xf4,0x33,0xeb,0x7f,0x80,0xbe,0xe5,0x51,0x98,0xb7,0xe1,0x88,0x26,0x18,0xa8,0x1e,0xc4, + 0x94,0x6c,0x7c,0xfc,0x28,0x41,0xc0,0x1b,0xb8,0xf3,0x01,0x00,0x7e,0x09,0xed,0x95,0x5c,0xb2,0x3d,0x6f, + 0x67,0x6f,0x47,0x19,0xc5,0xc5,0x63,0xc4,0xc6,0x4e,0x35,0xba,0xf9,0xfa,0xd7,0x1d,0xd7,0x61,0x0e,0x52, + 0x8e,0xdd,0x8f,0xc1,0x5f,0xe4,0x07,0xa9,0xd1,0x21,0xe8,0xf9,0x82,0xae,0x75,0xc2,0x9b,0x8a,0x05,0x9e, + 0xed,0x88,0xd9,0xd9,0xf3,0x27,0xcd,0x79,0x15,0x3c,0x54,0x71,0x19,0x76,0x6e,0xb0,0x21,0xc4,0xd3,0x75, + 0x30,0xd8,0x3d,0xc0,0xf7,0x36,0x33,0x2d,0x39,0x54,0xf2,0x45,0xdb,0x83,0x99,0xb7,0x02,0x36,0x84,0xa4, + 0xae,0xa9,0x6b,0xbd,0x18,0x76,0xb3,0x80,0x5e,0x27,0x4e,0x6b,0xe0,0x67,0xe5,0x89,0x55,0xe1,0x22,0x5e, + 0x89,0xd5,0x89,0x3b,0x3a,0x67,0x52,0x6f,0xcc,0x86,0xeb,0x70,0xb4,0x19,0x60,0xd3,0x33,0x91,0xca,0x71, + 0xbd,0x18,0x76,0x11,0x73,0xbe,0xae,0x38,0x47,0xd2,0xa1,0xf2,0x3c,0xea,0x04,0xa7,0xd5,0x61,0x28,0xf0, + 0x1a,0xc9,0xde,0x4f,0x85,0xbc,0x46,0xc5,0x7d,0x60,0x91,0xc8,0xce,0x0b,0xab,0x94,0x36,0x47,0x9c,0xea, + 0x54,0x5a,0x1d,0x2f,0x86,0x9e,0x14,0x36,0xc6,0x79,0x56,0xb3,0x68,0x44,0x44,0x17,0xda,0xc5,0xc5,0xe0, + 0x1d,0xde,0x3b,0x47,0xdf,0x14,0x8a,0xf3,0xeb,0x7f,0x12,0xd5,0x5c,0x2f,0x25,0xe3,0x72,0x8a,0xdd,0x5a, + 0xdd,0x18,0x6e,0xe7,0x1c,0x7a,0x9f,0x67,0x21,0x9f,0x6b,0xdd,0xcb,0xbc,0xce,0xd0,0x27,0x64,0x84,0x56, + 0x25,0x61,0xa4,0x27,0x7e,0xe2,0x5a,0x79,0x21,0x35,0xd7,0xb0,0xcb,0x07,0xef,0x38,0x8c,0xae,0xba,0x87, + 0xb6,0x73,0x4c,0x39,0xf5,0x24,0xfb,0x89,0x5b,0x6e,0x3d,0xc2,0xfa,0x4b,0xb6,0x4d,0x55,0xa0,0x6a,0xb3, + 0x99,0xc7,0x4d,0x29,0xf8,0xdc,0x82,0xfa,0xc9,0x4c,0xa1,0x17,0x72,0x3d,0x3b,0xa0,0x1a,0xfa,0x5f,0xbb, + 0x0f,0xb3,0xb3,0xa5,0xfe,0x5e,0x56,0xe5,0xd2,0x9a,0x0a,0x74,0x8f,0x59,0xb7,0x5d,0x67,0x40,0xc6,0xe9, + 0xaf,0xe8,0x39,0x59,0x4b,0x3f,0xe9,0x18,0xae,0x6a,0x97,0x64,0x57,0x7b,0x08,0x70,0x24,0xcb,0x9b,0x0e, + 0xe9,0x6e,0xf2,0x5a,0x25,0xe9,0x84,0x00,0x18,0xef,0xe6,0xd6,0x01,0x39,0xb9,0xdd,0xcb,0xc8,0x2b,0x07, + 0xcc,0xce,0xc2,0x32,0xe1,0xfe,0x49,0xfc,0x31,0xec,0xf8,0x10,0xaf,0xac,0xb4,0x28,0xd9,0x85,0xaa,0xa3, + 0xa4,0x5e,0x5c,0x59,0x3e,0xff,0x9c,0x0c,0x1e,0x04,0x57,0xda,0x39,0xbd,0x72,0x44,0x66,0x5d,0x79,0xd6, + 0x2d,0xf9,0x13,0x86,0x04,0x1e,0x43,0x6e,0xec,0xfb,0xb9,0x0c,0xde,0xe4,0xd1,0xf2,0x11,0xe7,0xf2,0x43, + 0x37,0x26,0x01,0x16,0x89,0x82,0xab,0x3e,0x10,0xbc,0xb9,0xfc,0xe7,0x8b,0x7c,0x6e,0xab,0x4e,0x29,0xe7, + 0xf6,0x89,0x32,0x0e,0xa2,0x70,0x68,0x48,0x62,0x19,0x2d,0x8d,0xaa,0xb9,0xb6,0xf2,0x17,0x79,0x8b,0x72, + 0x40,0x39,0x12,0xe0,0xf9,0x77,0xaf,0x55,0x82,0x5f,0x9f,0x23,0x4b,0x2a,0xe3,0x0d,0xc2,0x67,0x44,0xd0, + 0xb6,0x0b,0x03,0x93,0xd1,0x13,0xaf,0xcb,0x27,0x55,0x3e,0xf6,0x7c,0xc1,0xb0,0xfd,0x02,0x35,0x9f,0xf1, + 0x4b,0xd4,0xa5,0xd2,0x63,0xf0,0x39,0x4a,0xef,0x5e,0xde,0x53,0x10,0x28,0xee,0x7e,0x5f,0x97,0x1f,0xb1, + 0x3c,0x4a,0x76,0x34,0xe5,0x0c,0x4b,0x93,0x3b,0x14,0xc8,0x10,0x66,0x8c,0x42,0x90,0x32,0xeb,0x65,0xa8, + 0x39,0x0c,0x3b,0xcb,0xc8,0xf5,0xdc,0xef,0x26,0x47,0xe3,0x36,0xcb,0x5b,0x7e,0xb9,0x96,0xad,0x7e,0xca, + 0xee,0x2f,0x03,0x85,0x7a,0x5c,0xd5,0xa7,0x2a,0xa1,0xbc,0xb0,0x6b,0x48,0x15,0x42,0xc6,0x15,0xf6,0x2d, + 0x23,0xef,0xf6,0x3d,0x1f,0x4f,0x5e,0x75,0x32,0x66,0xdf,0xa3,0x96,0x7a,0xe5,0x75,0xdb,0x56,0x44,0x71, + 0x7c,0x2f,0x5f,0x57,0x54,0xca,0xf2,0x00,0xbe,0x26,0x91,0x3e,0x25,0x0f,0x58,0x96,0x33,0x69,0x10,0x80, + 0x7f,0x36,0xc0,0xc1,0xdc,0x82,0xdf,0x38,0x1d,0x10,0x5b,0xf3,0x0b,0x8a,0xf5,0x61,0x40,0x10,0x1c,0x30, + 0x9c,0x8e,0x22,0x28,0x4b,0x22,0x2f,0x29,0xbb,0x1a,0x4f,0x89,0xe3,0x37,0xad,0x3d,0x62,0xa2,0x8d,0x1d, + 0xcb,0x19,0x84,0x63,0x11,0x4a,0x78,0xe3,0xdc,0xcb,0x8e,0x68,0x83,0xd4,0xdd,0x08,0x94,0x2d,0x84,0x1f, + 0x41,0xc4,0x5c,0xd7,0x44,0x08,0x53,0x71,0xbe,0xe1,0x68,0xf9,0x7b,0x3b,0x60,0xa5,0xb5,0x5a,0x09,0x95, + 0xa6,0x6d,0xf6,0x2d,0x60,0x9c,0xb6,0x1c,0x9c,0x4e,0x05,0x4f,0x4d,0xf0,0xf6,0x5d,0xf7,0x00,0x50,0xd8, + 0x17,0xf0,0xda,0x09,0x70,0x59,0xf8,0xdb,0xc6,0x35,0x39,0xbb,0x7d,0x46,0xe6,0xee,0xc5,0xbc,0x56,0x1a, + 0xee,0xee,0x0f,0x5f,0x03,0x4d,0xc2,0xcc,0xaa,0xad,0xf0,0xa8,0xbe,0xda,0xaa,0x2b,0x96,0xca,0x7a,0xbb, + 0x55,0xe7,0xbd,0xe1,0x53,0xa3,0x78,0x51,0x98,0x55,0x1e,0x25,0x4f,0x8d,0x1e,0x71,0x42,0xf4,0x5f,0x02, + 0x92,0xa9,0xbd,0xc8,0x19,0xfd,0xae,0x56,0xf7,0x7e,0xcb,0xe3,0xe1,0xa8,0xea,0x1d,0xda,0x2d,0xb3,0x3c, + 0x22,0x3f,0xb6,0xa0,0x8c,0x18,0x22,0x9c,0xf1,0x28,0x40,0xf9,0x80,0xf0,0x2b,0x77,0x88,0xd9,0xd5,0x43, + 0xf0,0x41,0x97,0x39,0x46,0x8b,0x9a,0xfc,0xfd,0x6a,0xcc,0xe8,0x38,0xf2,0xfa,0xf3,0x00,0xf1,0x24,0x28, + 0x22,0xa9,0xb0,0x7c,0xf5,0x12,0x1c,0x71,0x6d,0x12,0x44,0x04,0x33,0xd9,0x13,0x4e,0x34,0x49,0x02,0x51, + 0x71,0x6e,0x7e,0x50,0x61,0x92,0xc4,0xeb,0xa2,0x88,0x75,0x6d,0x8d,0xac,0x1d,0xd2,0xef,0x8d,0x21,0xc7, + 0x2c,0x40,0x92,0x5f,0xf2,0x9e,0xbe,0x92,0x1e,0xd5,0xd9,0x89,0x94,0x83,0x56,0x16,0xc6,0x8e,0x54,0xc7, + 0x98,0x43,0x5b,0xd9,0x61,0x34,0xad,0xee,0x4d,0xc5,0x6c,0x9b,0xd5,0x36,0xb4,0x4a,0xa1,0x1f,0xa0,0x1a, + 0x21,0x70,0xfc,0xef,0x4a,0x8c,0xc1,0xfd,0x39,0xbe,0x39,0x32,0x91,0x6a,0xc0,0xaf,0x56,0x16,0x14,0x38, + 0x82,0x2f,0x52,0xf9,0x65,0x4a,0x65,0x8b,0x8c,0x5e,0xba,0x4c,0x0f,0x95,0x41,0x91,0xb4,0x13,0x09,0xf7, + 0x66,0x79,0xe5,0x72,0xb4,0x67,0xdb,0x26,0xe6,0x53,0x92,0x7d,0x60,0x85,0x4b,0x38,0xb6,0xfd,0xd4,0x7e, + 0x8a,0x2c,0x62,0xaf,0x36,0x73,0x3b,0xb2,0x74,0x7c,0x71,0x04,0x5a,0x22,0xb6,0x9d,0x2d,0xde,0xdb,0xf3, + 0xa7,0x70,0xf4,0x1c,0xeb,0x87,0xd3,0xf4,0x8a,0x04,0xc1,0xc3,0xe8,0x78,0x65,0xb4,0xb5,0x98,0x09,0x27, + 0x44,0x6a,0xc9,0x70,0x74,0x84,0x1a,0x92,0x5d,0xfe,0x3c,0x83,0x16,0x08,0xaa,0xe4,0x05,0xa4,0x10,0x84, + 0x7f,0x03,0x7d,0xa7,0x13,0x3a,0xa9,0x88,0x36,0x38,0xa6,0x30,0x9b,0x87,0xd0,0x32,0x40,0xaf,0x0d,0x8b, + 0xd0,0xdd,0x89,0xb7,0x2b,0xcd,0x57,0x5b,0xc1,0xdf,0x1f,0xc5,0x5d,0x17,0x7c,0x88,0x51,0xb5,0xfb,0x71, + 0x76,0xbc,0xcb,0xda,0x1f,0xd0,0x63,0x7c,0x66,0x82,0x07,0xaa,0xf8,0xb2,0x79,0x75,0x21,0x73,0x54,0x97, + 0xdc,0x9b,0x5e,0xf3,0xd5,0xb1,0x30,0x93,0x11,0xec,0xc3,0xac,0xca,0x7e,0x54,0xe6,0x43,0x05,0x78,0x73, + 0xec,0x07,0x22,0x5b,0x23,0xe0,0xb9,0x73,0x1a,0xa5,0x6e,0xef,0x51,0x03,0x16,0x6d,0xff,0x84,0x82,0x1b, + 0xfa,0xa5,0x25,0x8c,0xc9,0x57,0x5a,0xfc,0x82,0xc2,0x1d,0x9d,0x4b,0xb0,0x69,0x6c,0xfb,0x85,0x66,0x8a, + 0xcc,0x23,0x8d,0x4d,0x00,0x84,0xf6,0x6d,0x0e,0x4a,0x26,0x81,0x0e,0xe2,0xf5,0xc9,0xae,0x3b,0xb9,0x9e, + 0x6f,0xba,0x66,0x86,0xe3,0x79,0xf4,0x78,0x3d,0xdb,0xab,0xaa,0xa1,0x94,0xfd,0xba,0x4b,0xde,0xdd,0xde, + 0xde,0x7d,0xb8,0xc2,0xbe,0x2d,0xa4,0xb4,0x8c,0x42,0xe6,0x07,0x8c,0xff,0xd6,0xa7,0xef,0xe4,0x18,0xf1, + 0xc8,0x64,0x79,0x89,0x27,0x63,0x34,0xe0,0x6c,0x83,0xf2,0xa6,0x24,0x2f,0x3f,0xd4,0xe5,0x49,0x4c,0xe8, + 0x84,0x07,0x5b,0xf5,0xe7,0x50,0xe3,0x34,0xec,0x09,0xb7,0x3a,0x44,0x35,0x8f,0x12,0x80,0x49,0x59,0x05, + 0xb1,0xcd,0xc6,0x73,0x10,0x3c,0xdb,0xd6,0x2f,0xef,0x24,0x0c,0x73,0x73,0x6b,0x24,0x25,0xd4,0xc7,0x51, + 0xfc,0xae,0x4b,0xc3,0x60,0x1a,0x6d,0xc6,0xf5,0xd4,0x72,0x79,0x23,0x7c,0x6e,0x36,0xf1,0x50,0x74,0xf7, + 0xbb,0x81,0xbe,0xa6,0x1a,0x7e,0xe2,0xa5,0x45,0x31,0xe4,0xfa,0xfc,0xba,0xe3,0x77,0x35,0x3e,0x0e,0x0c, + 0x13,0xa4,0xc1,0xe7,0x3d,0x3b,0xbe,0x8a,0x3a,0xe2,0x9b,0xed,0xb5,0x3a,0x00,0xfe,0x84,0x06,0x27,0x3e, + 0x75,0x90,0xca,0xc2,0x28,0x20,0x5d,0x8e,0x88,0xd9,0xa1,0xf5,0xe0,0x1d,0xbc,0xf0,0x52,0x19,0x82,0xce, + 0xd4,0x32,0x29,0x67,0x3b,0xf8,0x05,0xfc,0xc0,0x09,0x26,0x0c,0x67,0xb8,0x60,0x41,0x64,0x00,0x06,0x5d, + 0xf2,0x06,0xa8,0x69,0xbd,0x8d,0xed,0x0e,0x5a,0x62,0x68,0x2f,0x39,0xcd,0xc6,0x47,0xe2,0xdb,0xaa,0x52, + 0x54,0xfb,0x8d,0x69,0xa3,0xa2,0x93,0xe4,0x34,0xf9,0xfd,0x75,0x89,0xf6,0xbb,0xd5,0x76,0x14,0x01,0xa4, + 0x2e,0x6c,0x48,0xcd,0x80,0x01,0x3f,0x54,0xb2,0x56,0x01,0xd1,0x94,0x8e,0xea,0x2a,0x35,0x1c,0x2d,0xf0, + 0xdb,0xba,0x9f,0xb7,0xe2,0x8d,0x60,0xac,0x56,0xad,0x9f,0x9f,0x88,0xe2,0x76,0x1e,0x4a,0x0e,0xd9,0x80, + 0xf3,0x1a,0xb1,0xa6,0x44,0x2e,0x7c,0x1b,0xe5,0x88,0x79,0x7b,0x9c,0x5c,0x8c,0x4b,0x28,0x9b,0xa2,0xeb, + 0xf8,0x05,0x68,0xee,0xf8,0x96,0x8d,0x34,0xa4,0x90,0x48,0xbf,0x77,0x17,0xfb,0x8a,0x50,0x10,0x8e,0x18, + 0x93,0xe2,0x18,0x57,0x50,0x9f,0xa2,0x2c,0x64,0xa4,0xa2,0xd1,0xf6,0x03,0x54,0xb1,0xb0,0x4d,0x49,0x38, + 0xf8,0x38,0x83,0x0b,0x7c,0x31,0x9d,0x5d,0xff,0x6f,0x85,0x36,0x18,0x1a,0xeb,0x90,0x3c,0x20,0x11,0x91, + 0x01,0xba,0x37,0x90,0x16,0x68,0xbf,0x57,0xb2,0x7c,0xb4,0x2a,0x5e,0x61,0x7d,0xb0,0x35,0xa9,0x5f,0x1d, + 0x06,0xf8,0x4a,0x8f,0x3f,0x93,0xe1,0xaf,0x78,0x40,0xd0,0x71,0x02,0x24,0xa8,0xb8,0x80,0x5c,0xaa,0x21, + 0x0a,0x2c,0x00,0xcc,0xc0,0x1d,0x1e,0xa5,0xbe,0xd2,0x37,0x01,0xfc,0x76,0x9f,0x0f,0x63,0xf5,0xa3,0x32, + 0x3d,0x1f,0x8e,0x52,0xa3,0xb3,0x0d,0xf0,0x0c,0xc0,0x6c,0xf6,0x5d,0xf5,0xf6,0x68,0xb6,0x80,0x86,0x30, + 0xfa,0x4f,0xe0,0x01,0xd0,0x08,0xbd,0x44,0xe3,0xd2,0x73,0x8b,0x15,0x15,0xe4,0xb5,0x72,0x2d,0xe8,0x1a, + 0x5f,0x60,0x2e,0x55,0xa0,0xc0,0x07,0x6b,0x69,0xa6,0xf1,0x7a,0x17,0xd2,0x45,0x9c,0xc4,0xe7,0xa7,0x10, + 0xe5,0xe6,0x85,0x6e,0x39,0x47,0x51,0x52,0x41,0x79,0x7d,0x80,0x72,0x24,0x56,0x7c,0x60,0xf6,0xf6,0x9d, + 0xd4,0x80,0xf9,0xc3,0xee,0x27,0x28,0x5d,0x30,0xbb,0xb2,0xc2,0x3b,0x33,0xe6,0xc1,0xda,0xe4,0x83,0x04, + 0xc4,0x91,0xbc,0xfa,0x75,0xbc,0xf8,0x1d,0x77,0xf8,0xfa,0x0c,0x5f,0x3c,0x58,0x0b,0x3d,0x2a,0x9c,0x9d, + 0xd3,0xd7,0x47,0x09,0x36,0x4d,0xa3,0xa7,0xa5,0x48,0x99,0x47,0x87,0x2b,0x87,0x5a,0x4d,0x53,0x08,0x13, + 0x10,0x99,0x94,0xd8,0x63,0xb9,0x5e,0x2d,0x78,0xb8,0xc3,0x54,0xf4,0x53,0x09,0xa5,0xf6,0x14,0x5e,0x8e, + 0xf0,0xd1,0x85,0x8e,0xe0,0xf4,0xf1,0x4b,0xdb,0x56,0x56,0xda,0x6f,0x2c,0xa3,0x87,0xbb,0x0e,0x62,0x31, + 0xc9,0xc0,0x2d,0x58,0x9e,0x3a,0xa0,0x57,0xce,0x91,0x8d,0xc9,0xd1,0xce,0x4d,0x1e,0xa4,0x5f,0x30,0xf7, + 0x00,0x2a,0x01,0x19,0x41,0x03,0x77,0xd3,0xb9,0x1c,0x3c,0x54,0x49,0xae,0x35,0x5f,0x55,0x89,0x23,0x0c, + 0x49,0x14,0xae,0x7c,0x0a,0x8f,0xc6,0xe9,0xef,0x5c,0xcf,0xcf,0x8e,0xee,0xfd,0x65,0x4c,0x75,0x67,0xd4, + 0x3a,0xe2,0x1e,0x4d,0x1e,0x49,0xc7,0xdf,0x0d,0x00,0x57,0x47,0xc3,0xee,0x46,0xfb,0x17,0x37,0xfb,0x6c, + 0x01,0x5f,0xd4,0xa5,0xb8,0xea,0x34,0x98,0xb4,0xf2,0xc8,0x48,0x5c,0x45,0xe3,0x73,0x8c,0x52,0x31,0xd7, + 0xa3,0x9a,0x93,0x86,0x92,0xe7,0x96,0xce,0xa4,0x34,0x6c,0x5b,0xcb,0x84,0x1e,0xae,0x46,0x3a,0x14,0x95, + 0xe4,0xa9,0x25,0x40,0x2f,0x60,0x68,0x1b,0xc0,0x8d,0x4f,0x27,0x0d,0x52,0x72,0xc8,0xb5,0x1c,0xd0,0x23, + 0x5c,0xf3,0x0b,0xd0,0x2d,0x45,0x46,0xff,0xdb,0xb1,0x85,0x94,0x61,0xda,0x46,0x36,0x9f,0x90,0x11,0xe1, + 0x5a,0x13,0xa9,0xc5,0xc3,0xb1,0xe6,0x4d,0x1a,0xa0,0x33,0x54,0xcb,0x4a,0xbf,0xee,0x91,0x45,0x2f,0xac, + 0x83,0x2f,0x68,0xec,0x00,0x5c,0x2c,0xa4,0x6e,0x94,0x55,0x35,0x5f,0x43,0x8f,0xae,0xe6,0xd8,0x8b,0xde, + 0x55,0x47,0x61,0x0a,0xec,0xb2,0x97,0xd6,0x66,0xcc,0xe0,0xc1,0xca,0xf8,0xbf,0x70,0x28,0x22,0x95,0x27, + 0xe9,0x10,0xc0,0xa0,0x4c,0x1a,0xef,0x43,0x1e,0x27,0xf8,0xee,0xed,0x31,0x1c,0x5f,0x4e,0xf8,0xd5,0x08, + 0x99,0x3f,0xe4,0x12,0xca,0x55,0xfd,0x07,0xe3,0xd7,0xd3,0x8f,0x69,0x79,0xf7,0x50,0x3a,0x26,0xcd,0xff, + 0x2c,0x11,0xf0,0xac,0xdd,0xd6,0x13,0xe7,0x42,0xb2,0x3e,0x60,0x38,0x31,0xe1,0x81,0x75,0x9f,0x90,0x15, + 0x43,0xa2,0xed,0x55,0x3a,0xfd,0x48,0x48,0x2c,0x98,0x46,0x99,0x9c,0x21,0x91,0xdc,0x4e,0x0a,0x46,0x39, + 0x02,0x46,0xb9,0xae,0xfa,0x6d,0x55,0x65,0x79,0x90,0x0e,0xdf,0xd7,0x2a,0xab,0xf0,0x5d,0x45,0x1d,0x6b, + 0xce,0x8f,0x6b,0xe3,0xee,0x30,0xe9,0x3e,0x64,0xb4,0xc2,0x50,0x75,0x5b,0xd0,0x31,0x22,0x45,0x34,0xee, + 0x5b,0xbf,0xa4,0x47,0xf2,0x70,0xe5,0x13,0x95,0xf1,0x6b,0x59,0xda,0x7c,0x36,0x72,0xb9,0x2e,0x9e,0x1d, + 0x50,0x26,0xe8,0xe4,0xa9,0x05,0x27,0x78,0x34,0x0e,0x0f,0x5d,0x89,0x5f,0xcd,0xf5,0xa0,0x3c,0x9a,0xaf, + 0x3e,0x1b,0xbc,0x27,0x86,0xa8,0xe7,0x08,0x3a,0xd8,0x6a,0x23,0xa0,0xae,0xcd,0xe7,0x5b,0x9f,0x36,0xa1, + 0x44,0x9f,0x27,0x86,0xa1,0xda,0x57,0xa8,0xc5,0x48,0xd3,0xd8,0x91,0x3d,0xa8,0x91,0x09,0xbc,0x98,0xc7, + 0xba,0x6a,0xef,0x52,0x30,0x1b,0x3e,0xd0,0x28,0xf1,0xe9,0xda,0xe3,0xf5,0xb3,0x2a,0xdb,0x38,0xeb,0x03, + 0xca,0x35,0x39,0x36,0x31,0x46,0x30,0x95,0x88,0x0a,0x18,0x76,0x19,0x5c,0x9c,0x23,0x48,0x96,0x4f,0xe3, + 0x2a,0x40,0x09,0x93,0xd4,0x0c,0xde,0x81,0xa5,0xb7,0x9d,0xb6,0x33,0x20,0x6c,0x68,0x8a,0xf8,0xb5,0xfd, + 0x5f,0xbc,0x2b,0x6f,0x04,0x4a,0xb3,0x88,0xa1,0xd4,0x3c,0x84,0xe6,0x4a,0x7a,0x98,0x06,0xac,0xab,0xfc, + 0x8c,0x38,0x0c,0x11,0x9c,0xf9,0x6d,0x5b,0x99,0xae,0x0f,0x76,0x89,0x6d,0x20,0x71,0x11,0xd2,0x8f,0x35, + 0xa0,0xb4,0x24,0x51,0xcd,0xe4,0x1c,0x3c,0xf5,0x8b,0x35,0xa2,0x1c,0xf5,0x73,0xa0,0xc6,0x4a,0xe9,0x69, + 0x85,0xf9,0xfc,0xf2,0x17,0x85,0x85,0x69,0xc2,0xca,0x02,0x84,0x57,0x97,0xca,0xf8,0xb1,0x2a,0xb3,0xcb, + 0x90,0x89,0xf3,0x3c,0x7a,0xad,0x92,0x6c,0xdf,0x69,0x33,0xa2,0xa4,0xef,0xf2,0x24,0x05,0xe4,0x6c,0xc7, + 0xd7,0xdc,0x10,0x0f,0xa3,0x80,0xd6,0x1f,0x01,0xbe,0xa3,0x01,0xa9,0x4e,0xae,0xed,0xef,0xc0,0xb3,0xd1, + 0x9b,0x5b,0x60,0x55,0xa0,0x8a,0xa9,0x71,0x17,0x52,0x4d,0x5b,0x1d,0x23,0xf9,0x1c,0x53,0x53,0x73,0xa1, + 0xb7,0x98,0xc1,0x8a,0xac,0x99,0x93,0xb9,0x52,0x57,0xcb,0xc4,0x76,0x89,0x7c,0xd9,0x83,0x61,0xfc,0xf7, + 0x34,0x85,0x8a,0x06,0xf0,0x0c,0x04,0xda,0xc4,0x71,0x76,0x17,0x8b,0xc2,0xb4,0x20,0xdc,0x2e,0xf3,0x60, + 0xad,0x41,0x1a,0xf1,0x28,0x76,0x07,0xaf,0x4c,0xd2,0x39,0x7d,0x8c,0xfa,0x3f,0x72,0xa2,0xf2,0x49,0x03, + 0xc4,0x05,0x33,0x59,0x55,0x34,0xb2,0xf6,0x04,0x44,0xd7,0x1b,0x2c,0x5f,0x82,0x46,0x34,0x97,0xfd,0x56, + 0xd0,0xd9,0x5f,0xf0,0x5b,0x8b,0x08,0xee,0x30,0x88,0xfd,0x74,0x18,0x03,0xfd,0x7a,0x5e,0x39,0x20,0x91, + 0x78,0xf3,0x3e,0xb1,0xff,0xb6,0xea,0xfb,0xf5,0x47,0x26,0xf0,0xde,0x48,0x3c,0x81,0x99,0xf4,0x7d,0x63, + 0x50,0xa4,0xda,0xa5,0xca,0xf5,0x32,0xb8,0xc6,0x09,0xca,0x1d,0xc7,0x21,0xa0,0xad,0x60,0xad,0x3e,0x12, + 0x87,0x9f,0x77,0xbe,0x77,0x97,0x06,0x09,0x75,0x1d,0x4f,0x62,0xb5,0x87,0xeb,0xab,0x0d,0x3e,0x3d,0x7e, + 0x6e,0x96,0xf0,0xb8,0xc3,0x41,0x46,0x3d,0x77,0xd8,0x24,0x56,0xd6,0x0c,0xfe,0x9e,0x96,0xc2,0x30,0x9a, + 0xdc,0x38,0x8d,0x72,0xc7,0xac,0x36,0xd1,0xd5,0x05,0xc6,0xbe,0x88,0xb4,0xd3,0xd2,0x19,0x1e,0x1e,0x27, + 0xb7,0x80,0x07,0x01,0xb0,0xaa,0xa9,0x02,0x1e,0xa0,0xd8,0x92,0x02,0x81,0x02,0xb9,0x8f,0xfc,0x72,0x34, + 0x46,0x7b,0x69,0x2d,0x45,0x4d,0xf0,0xa2,0x6d,0x06,0x27,0x50,0xd1,0xe3,0xf5,0x73,0x8f,0x54,0x21,0x40, + 0x4d,0x85,0x43,0x70,0x05,0xaa,0x36,0x38,0x41,0x10,0x1c,0x15,0x5b,0x42,0xbf,0x19,0x8f,0xae,0x6a,0x2c, + 0x3d,0x68,0xd9,0x76,0x95,0x1f,0x4c,0xae,0x51,0x7d,0xde,0xda,0x76,0x26,0xd9,0x35,0xbe,0x5f,0x43,0x95, + 0x7d,0x94,0x04,0x38,0xa7,0x60,0x09,0xc5,0xfd,0xea,0x42,0xbb,0xd8,0xf9,0x11,0xbb,0xd0,0x61,0xfb,0x99, + 0xbd,0xc1,0x09,0x25,0xfa,0x72,0xa0,0xf4,0xde,0x7e,0x61,0xf1,0x96,0x5d,0x26,0x58,0x34,0x28,0x3a,0xaf, + 0xf0,0xb9,0x53,0x9f,0xae,0xa8,0xe1,0xb4,0x8f,0xc3,0xab,0x24,0xee,0x12,0x13,0xff,0x59,0x32,0xec,0xd9, + 0x15,0x6a,0xe0,0xfa,0x7d,0x57,0xfa,0xbf,0xb0,0xcb,0x0a,0xd6,0x04,0x6f,0xe2,0x11,0x67,0x43,0x79,0xa2, + 0x76,0x89,0xcf,0x7d,0x9c,0x6c,0xca,0x60,0xdf,0x06,0x7e,0xe5,0x18,0x54,0x50,0xf5,0xf4,0x50,0xb2,0x21, + 0x24,0x87,0x39,0xc1,0x9b,0xa7,0xf0,0x85,0x3e,0x36,0x28,0x86,0x2d,0xa9,0x27,0x64,0xb0,0x6f,0xb9,0xdd, + 0xdf,0x2f,0xf2,0x04,0xe5,0xa7,0x83,0x37,0x91,0x94,0xb2,0xe0,0x0e,0xcb,0xea,0x65,0x8c,0xba,0x4b,0x38, + 0x4d,0xb3,0x7d,0x4c,0xad,0x55,0x33,0x96,0xa9,0x0c,0x95,0x4f,0x42,0x20,0xaf,0x84,0xcf,0xa4,0x08,0x58, + 0x09,0x6b,0xe3,0x6d,0xe6,0x19,0x90,0x2d,0x03,0x9a,0x3a,0x66,0x74,0x44,0xde,0xbe,0xc8,0x19,0x75,0xde, + 0xab,0x78,0xfd,0x88,0xbc,0x18,0xd7,0x8b,0x09,0xb6,0x24,0x3a,0xb8,0x4c,0x15,0xf8,0x95,0xbc,0xb9,0xc5, + 0xe7,0x92,0xe5,0x23,0xce,0xe5,0xc7,0x62,0xa8,0xd0,0xe3,0x03,0x31,0x5f,0x7e,0x46,0x8f,0x6a,0xfc,0x56, + 0xd7,0x21,0xba,0x9c,0xae,0xec,0x23,0x9d,0xea,0xd0,0x0d,0xd9,0x95,0x1d,0xba,0xe3,0xd7,0xbe,0xfa,0x79, + 0xe0,0xda,0xcc,0x4f,0xe4,0xa6,0xa4,0x8e,0x39,0x2a,0x4f,0x3e,0x18,0xcd,0x48,0xc2,0x96,0xcb,0x00,0x6e, + 0x91,0xbe,0xb2,0x74,0xe3,0x31,0x3e,0xb7,0x8c,0xce,0x27,0xa5,0x30,0xcf,0xa4,0xb3,0xf2,0xc6,0xc5,0x5c, + 0x4f,0xd7,0xed,0xbb,0x99,0x0d,0x59,0x36,0x96,0x18,0x78,0xb6,0xb6,0xd2,0x2b,0xd9,0x1e,0x82,0x7a,0x3b, + 0xc1,0x4e,0xb0,0x34,0xa6,0xa6,0x44,0xc7,0x19,0xcb,0x17,0x9b,0x97,0x52,0xf3,0x93,0xe7,0x63,0x78,0x20, + 0x1e,0xc5,0x0a,0x21,0xcb,0x53,0xcf,0x57,0xdb,0xe0,0x2a,0xae,0x07,0xa8,0x16,0x33,0xf1,0x80,0xca,0xc2, + 0xc7,0x90,0x15,0xad,0xf6,0x55,0xa7,0xb6,0x96,0x62,0xdd,0xd4,0xb9,0x6d,0xff,0x10,0x9f,0xce,0xb4,0x68, + 0xce,0x5f,0xae,0x1f,0x6f,0x06,0x71,0xb1,0x4f,0x92,0x12,0x93,0xfa,0xfd,0x9e,0x0f,0xc6,0x96,0xce,0xec, + 0x7c,0xbc,0x4e,0xa8,0xed,0x65,0xd7,0xe4,0x80,0x24,0xf0,0x70,0xc9,0xf3,0x74,0x68,0x6b,0x17,0xa1,0xa0, + 0xda,0x84,0x36,0x96,0xfd,0x99,0xed,0xc7,0x30,0x28,0x28,0x93,0x6a,0x27,0xa5,0xb0,0xfb,0x75,0x3e,0xe5, + 0xd8,0x97,0xe5,0x02,0x5e,0xe0,0x0b,0xbf,0xda,0x19,0x9d,0x7c,0xac,0xc5,0x3c,0x6a,0xb9,0x02,0x03,0x8f, + 0x1f,0x54,0x14,0x7c,0x73,0xf0,0x24,0xa5,0xea,0x18,0x9f,0x03,0x66,0x3f,0x09,0xaf,0x4f,0xc0,0xeb,0xbb, + 0xfb,0x17,0x52,0xcc,0x55,0x3e,0xad,0x84,0xf1,0x93,0x40,0x3d,0xbe,0x23,0xa4,0x44,0x37,0xda,0x83,0x11, + 0x68,0xe2,0x2d,0xbf,0x95,0xb6,0xca,0x6d,0x18,0x2a,0x4f,0x4f,0xe4,0x16,0x0e,0x57,0x02,0xbd,0x00,0x48, + 0x5f,0x9e,0x4f,0xba,0x37,0x35,0x72,0x06,0x3b,0x1d,0x3d,0x1f,0xb3,0x6f,0xb9,0x05,0x07,0x78,0x62,0x79, + 0x1b,0xde,0xc5,0x8a,0x71,0xa6,0x85,0xda,0x2d,0x64,0xe1,0x40,0x6b,0x71,0x6a,0x0d,0x39,0xe3,0x0e,0x23, + 0x10,0xb0,0xe6,0x6d,0xaa,0x6c,0x64,0xee,0x3a,0x93,0xc6,0x1e,0x61,0x52,0x34,0x1a,0x01,0xeb,0x2b,0x79, + 0x0e,0x8c,0xa6,0xfc,0xb7,0xe8,0x83,0xa6,0xe2,0x10,0x20,0xf6,0xd9,0x31,0x22,0x2b,0x19,0xa2,0x9f,0x6e, + 0xeb,0x9e,0x0f,0x04,0x69,0x93,0x37,0x3e,0xfb,0xda,0x68,0x2b,0xab,0x93,0x27,0x2b,0x35,0xa5,0xe7,0xce, + 0xe9,0x40,0x97,0xcc,0x7c,0xb0,0xef,0xf9,0x89,0x2e,0x5a,0x83,0xc4,0x22,0xbc,0xad,0x37,0x2b,0x0b,0x1b, + 0xbc,0xe3,0xb8,0x09,0x5e,0x41,0xf2,0x90,0x3f,0x5f,0xda,0x77,0x89,0xc3,0xb1,0xbc,0xcb,0x52,0xf8,0x89, + 0xbd,0x38,0x9a,0x31,0x79,0x02,0x88,0x69,0x0e,0xb4,0x7d,0x70,0xad,0x77,0xf9,0x4d,0x6f,0xd3,0x99,0x9c, + 0x2e,0xc2,0x53,0x14,0xa4,0xd1,0xc9,0x07,0x29,0x27,0xa9,0xdf,0x86,0xfc,0xa0,0xa2,0x6a,0x33,0x55,0x14, + 0xe1,0x79,0xc4,0xea,0x45,0x26,0x85,0x01,0x9c,0x10,0xfc,0x51,0x62,0x8b,0x11,0xf0,0x1f,0xac,0x34,0x6f, + 0x87,0xa3,0x7f,0x46,0xba,0xc0,0x4b,0xc7,0x83,0xf2,0x7c,0x47,0x27,0x28,0xf3,0x23,0xe1,0x87,0x63,0x8b, + 0x4b,0xf1,0x04,0x15,0xfe,0xe8,0x45,0xc4,0x91,0xc4,0x1d,0x60,0x03,0xb6,0xeb,0x95,0xa7,0x26,0x1c,0xe5, + 0x8d,0x85,0xc3,0x21,0xb6,0xd5,0xd3,0xa8,0x8e,0x9f,0x37,0xc3,0x14,0xce,0xe3,0xf4,0xe8,0x52,0xf3,0xe4, + 0x3e,0x54,0x8c,0x43,0x80,0x23,0x0a,0xd5,0x65,0x04,0xbd,0x3f,0xcd,0x85,0x25,0xa9,0x81,0xb9,0xde,0x0f, + 0xc7,0x0e,0xa0,0x8a,0x11,0x35,0xca,0xcb,0x70,0x86,0xd7,0xb3,0x77,0x32,0x7a,0x09,0x09,0x3a,0x0b,0x03, + 0xbd,0x51,0x59,0x92,0x53,0x01,0x7a,0xea,0xca,0xae,0x9e,0x1d,0xba,0x7a,0xb4,0x41,0x0b,0x63,0x70,0x39, + 0x1a,0x71,0x18,0x07,0xfb,0x14,0xf6,0x6e,0x6d,0xd5,0xf5,0x8a,0x0c,0x75,0x71,0x32,0x14,0xbb,0x90,0xfc, + 0x56,0xbd,0x18,0x8e,0x32,0x09,0x23,0x3d,0xd4,0xda,0x90,0x2e,0x63,0x66,0xf5,0x87,0x33,0x66,0x7a,0x22, + 0x7c,0x6a,0x66,0x62,0xe7,0x74,0xc2,0x86,0x3d,0xd7,0x17,0xf0,0x08,0xd9,0x8b,0x03,0xda,0xab,0x93,0x58, + 0x56,0xa0,0x49,0x72,0x5c,0x1f,0x1e,0x0b,0x67,0x4e,0x3e,0x28,0x31,0x10,0xb7,0xd1,0xde,0x78,0x3a,0x97, + 0x90,0xab,0x29,0x4e,0xb0,0x4c,0x51,0x23,0xd4,0xb8,0x4d,0x17,0x00,0xef,0x4e,0x7d,0x8a,0x81,0x40,0x4b, + 0xdf,0xb7,0xfa,0x84,0x67,0x19,0x12,0x63,0x32,0x05,0x6f,0x3f,0xa5,0x29,0x7c,0x30,0x28,0x1f,0x9b,0x7c, + 0x42,0xce,0x27,0xd7,0x6e,0x65,0x58,0x0e,0xc3,0x81,0x90,0x23,0xb0,0x4a,0x91,0x48,0x8d,0x61,0x51,0x75, + 0x20,0xbf,0xfd,0xc0,0x13,0xb3,0x93,0xaf,0x4b,0x05,0x28,0x03,0xf6,0x3d,0xb4,0x39,0x97,0x8a,0x20,0xd8, + 0xc6,0x9c,0x40,0xbb,0xac,0xf0,0x01,0x4d,0x17,0x4d,0x92,0x61,0x00,0x80,0x87,0xa2,0xc9,0x9b,0x44,0x04, + 0x33,0xdc,0xd8,0x48,0x41,0x0e,0xa6,0xf2,0x48,0xcc,0x3e,0x54,0x58,0x40,0x80,0x86,0x5f,0x8f,0x51,0x3b, + 0xe5,0x8f,0x90,0xb1,0x52,0x3c,0xa3,0x49,0x3d,0x2a,0x6a,0xe4,0x50,0xac,0x32,0xa7,0x0a,0x2e,0x72,0xe9, + 0xb5,0x8f,0x93,0xa7,0x36,0x72,0x3d,0x1f,0x2a,0x74,0xf5,0xfd,0x61,0x97,0x15,0x30,0xe9,0x3d,0x01,0x0a, + 0x21,0x1c,0xf0,0x4a,0x8b,0x2a,0xc0,0x2f,0xd4,0x3e,0x42,0x83,0x90,0xa5,0xfe,0x7e,0x2b,0x6f,0x01,0xa9, + 0x4f,0xdf,0xe6,0x15,0x4d,0x48,0xb7,0x6a,0xb7,0x9f,0x39,0xd0,0x86,0x72,0xbe,0x6f,0x13,0x25,0x05,0x75, + 0xae,0xc1,0x26,0xce,0xbe,0x0c,0x29,0x04,0x82,0xef,0xa7,0xda,0xfb,0x0d,0x12,0xab,0xcf,0xd3,0x99,0x41, + 0xd0,0xa1,0x67,0x07,0x85,0xba,0x0d,0x7e,0x5e,0x4e,0x80,0x20,0x97,0x35,0x2e,0xee,0x33,0x3c,0x5b,0x94, + 0x81,0x07,0x51,0xfa,0x32,0x22,0x02,0x89,0x75,0x59,0xc0,0xe4,0x3c,0x0e,0xfd,0xdb,0x90,0x33,0x70,0x75, + 0xad,0xbc,0x8b,0x30,0x1b,0xc1,0x23,0x9c,0x0a,0x52,0xa2,0x04,0x3f,0x13,0xe0,0x05,0x01,0x80,0x2b,0x09, + 0xb7,0xb3,0x62,0x29,0x5f,0x80,0x73,0xe0,0xd3,0x66,0xe5,0xef,0x80,0x26,0x6e,0x30,0xe1,0x90,0xcf,0x89, + 0xd6,0x34,0x99,0x53,0x2a,0x22,0x7a,0xa7,0xf5,0xb3,0x3e,0xf1,0x04,0x72,0x32,0x60,0x5d,0x90,0xbe,0xce, + 0xa3,0x14,0xb1,0x1f,0x71,0x9e,0x1c,0x94,0x3c,0x21,0xdc,0xeb,0x7b,0x56,0x1f,0xc1,0x91,0x38,0x80,0x7d, + 0x11,0xfe,0xc4,0x3a,0xc2,0x3e,0x85,0x85,0xe4,0x76,0xa6,0xf7,0xa9,0x5a,0xa0,0x34,0x6f,0x4b,0xa5,0x07, + 0xcd,0xc7,0x74,0xae,0x9a,0x1a,0x0f,0x07,0xe5,0xb3,0x38,0x60,0x03,0x86,0xf1,0x64,0x24,0x86,0xbf,0x88, + 0xa4,0x29,0x8a,0x73,0x25,0xd0,0xb0,0xc3,0x61,0x43,0x96,0xc8,0x0b,0x39,0x7f,0x5c,0xf1,0x20,0xa6,0x32, + 0x43,0xa3,0xaa,0x75,0xe8,0x28,0x7d,0x4d,0x8c,0xca,0x6c,0x40,0x5d,0x13,0xe6,0xed,0x2b,0x3c,0x4a,0x65, + 0xc2,0x73,0xc2,0x56,0xfe,0x04,0x32,0xf4,0xe5,0xec,0x9d,0x12,0xa7,0x82,0xcf,0xf1,0x36,0x9f,0x3f,0xbb, + 0xfd,0xce,0xb3,0x8f,0xd7,0xcf,0x51,0xb2,0x08,0x47,0x6b,0xbb,0x37,0x73,0xf3,0xb1,0xe0,0x89,0x79,0xb0, + 0xa7,0xa0,0x4c,0xfe,0x27,0x5a,0xa5,0xfa,0xbe,0x5d,0x0e,0xde,0xa8,0x2f,0xc9,0x1b,0x44,0xfe,0x55,0xa6, + 0x02,0xb9,0xe5,0x85,0x6a,0x6b,0x15,0x18,0x7a,0x3a,0x5e,0x8a,0x7e,0x5f,0x7d,0x76,0x3a,0x2f,0x62,0x82, + 0x4d,0xa2,0xca,0x14,0x12,0x9b,0xa5,0xf4,0xf8,0x65,0xbc,0x5d,0x14,0xcf,0x5e,0xd2,0xaf,0xa9,0xaa,0xc7, + 0x89,0xa3,0x1c,0x7e,0xd1,0x44,0x26,0x56,0x65,0x88,0x86,0x5b,0xfe,0xa7,0x30,0x51,0x47,0x7d,0x2e,0x41, + 0xf4,0xb3,0xef,0x2b,0x6d,0xd5,0x5e,0xdc,0x45,0xce,0x17,0xfc,0xc6,0x2b,0xcc,0x34,0xbc,0x41,0x50,0xb6, + 0x73,0xe8,0x88,0xb7,0x71,0x91,0xce,0x34,0x62,0xc2,0x11,0xcf,0xa6,0x35,0x43,0x74,0xdf,0x04,0xcb,0x72, + 0x87,0xf1,0x56,0x66,0x67,0xf9,0x1c,0x03,0x02,0x46,0x1b,0x27,0x17,0x3a,0x60,0x32,0xca,0x53,0xae,0x97, + 0x12,0x1b,0x26,0x7d,0x03,0x64,0x80,0xa3,0xc6,0xc7,0x15,0xa6,0xc6,0xd5,0x5b,0xea,0xfd,0x88,0x11,0x84, + 0xc6,0x4f,0xe6,0xa0,0x6b,0xc7,0x5a,0xc5,0x9d,0x11,0xcc,0x6f,0xf7,0x03,0x74,0x99,0xaa,0x1b,0x21,0xd4, + 0xb7,0x35,0x0f,0x58,0xc8,0x31,0xd5,0x05,0xd7,0xd3,0xdd,0x1d,0x30,0x4d,0x0d,0xb3,0xbd,0xf1,0x81,0x6a, + 0x6a,0x60,0x6e,0x61,0x9b,0x7a,0xe8,0x95,0x06,0x51,0x65,0xfb,0x83,0x47,0x21,0xf5,0xfd,0x1c,0x2a,0x2d, + 0xc0,0x63,0x0f,0xf8,0x9f,0xf0,0xe0,0xe9,0x0a,0x34,0x73,0x5d,0xf3,0xd6,0x67,0x7b,0xd7,0xb1,0x0a,0x95, + 0xc3,0xcd,0x74,0x9d,0xa4,0x10,0x05,0x03,0xa8,0xc9,0x8b,0x21,0x3f,0xd8,0x6a,0xce,0xc3,0xc9,0x08,0x76, + 0x11,0xd9,0x00,0x16,0xc1,0xa3,0xca,0xf8,0x39,0xe8,0x05,0x37,0x53,0x67,0xca,0x5c,0x3d,0x47,0x87,0x5e, + 0x76,0x04,0xf5,0xc7,0x36,0xe8,0x1a,0xba,0x6f,0x36,0xb1,0xd5,0x01,0x39,0xd5,0x69,0x87,0x47,0x53,0x07, + 0xfc,0x1d,0x5a,0xb6,0x7d,0x11,0x7c,0x94,0x92,0xca,0xdf,0x9d,0x53,0x95,0x94,0xa6,0xff,0xd3,0x7b,0x8f, + 0x55,0xc6,0x1b,0x78,0x88,0x94,0xb9,0xe9,0x41,0x3c,0x7c,0x7a,0x5b,0xcf,0xa2,0x97,0xfd,0xf4,0x59,0x19, + 0x1d,0xed,0x0a,0x2b,0xdf,0x24,0x6f,0x8f,0x67,0xf5,0x93,0x32,0x92,0xcf,0xb1,0x98,0x77,0x31,0xa2,0xff, + 0x00,0x74,0xb7,0xf8,0x85,0x6b,0xe1,0x12,0xda,0x0c,0x35,0x8e,0x0c,0xa4,0x67,0xf0,0x31,0xdc,0x1c,0x9c, + 0xe0,0x8b,0x07,0xc3,0x63,0x4f,0x48,0x3e,0x77,0x30,0x6c,0xfb,0x10,0x92,0x35,0xa0,0xac,0x44,0x33,0xbc, + 0x47,0x50,0x60,0x1a,0xc5,0x03,0x47,0xcf,0x53,0x8e,0xa3,0xc0,0x05,0x6e,0x65,0x4b,0x7f,0x91,0x31,0xa0, + 0xeb,0x3c,0x80,0x27,0xfb,0xb6,0xe1,0x8c,0x1d,0x89,0x43,0x05,0xf3,0x57,0x00,0x16,0x48,0x2d,0x1b,0x1a, + 0xa9,0x38,0x8f,0xec,0x8d,0x35,0x59,0x33,0x7c,0x74,0x07,0xe9,0x5f,0xdf,0xe0,0x95,0xeb,0xcf,0x28,0xe9, + 0x29,0x16,0xa9,0xad,0x64,0xaa,0xea,0x22,0xd3,0xcb,0xb5,0x15,0x8c,0xee,0x99,0x26,0xe1,0x83,0xe5,0xf3, + 0x77,0x4b,0x0a,0x9c,0x8d,0x70,0x63,0xba,0xfd,0x04,0x89,0xda,0x8c,0x66,0xc2,0x5d,0x8c,0x56,0xd5,0x3d, + 0x6c,0xec,0xea,0xf9,0x6e,0xc9,0x63,0xfd,0xaa,0x53,0x48,0x8a,0xca,0x9a,0x63,0x34,0x21,0xd5,0xb2,0x4d, + 0xe1,0x11,0xfa,0x23,0x38,0xb9,0xda,0xfa,0x2b,0x68,0xe3,0x7d,0xe2,0xf9,0xdb,0x1f,0x9d,0xa9,0x25,0xf0, + 0x80,0x13,0x53,0x13,0xc8,0xf5,0x52,0x51,0x8f,0xc4,0x77,0xb3,0x9b,0x55,0x0a,0x9f,0x1e,0xbd,0xf5,0xe7, + 0xc3,0x20,0xbb,0xc3,0xe7,0x4e,0xee,0x3b,0x22,0x43,0x05,0xf8,0x44,0xb6,0xa8,0xc9,0xc4,0x47,0xb5,0x95, + 0x56,0x65,0x7e,0x89,0xb8,0x77,0xba,0x1e,0x11,0xe9,0x08,0x1e,0xee,0x36,0xe6,0x77,0xbc,0x6d,0x23,0xcb, + 0xe4,0x11,0x56,0x92,0x87,0x3e,0xa2,0x86,0x92,0x16,0x4d,0xbb,0x08,0x01,0x17,0x73,0xb0,0xcb,0xa5,0xc4, + 0x5b,0x92,0xf1,0xc4,0xd8,0xe2,0x3a,0x4d,0xab,0x01,0x0f,0x64,0xb2,0x4e,0x8b,0x33,0xb2,0x39,0x31,0xc6, + 0x1c,0xfa,0x5c,0x57,0x25,0x2b,0xfd,0xb2,0xc6,0x3d,0xe0,0x9a,0x61,0x25,0x1c,0xdd,0x74,0x26,0xd0,0x25, + 0xcb,0xc2,0xd7,0xa8,0x00,0xb3,0xdb,0xb6,0x1f,0xaf,0x17,0x03,0xa8,0x3c,0x31,0x06,0xec,0xa4,0xd8,0x0b, + 0x89,0x5e,0x18,0x6b,0xde,0x64,0xd0,0x03,0x52,0xd0,0x10,0xce,0x28,0x03,0x16,0x46,0x97,0xa0,0xfa,0x94, + 0x36,0x73,0x31,0xde,0xe8,0x02,0xd8,0x55,0x85,0xaf,0x55,0xb0,0x32,0xb8,0xcd,0x2b,0xb5,0xcb,0xc4,0x9b, + 0x20,0xeb,0x1f,0xd4,0xb0,0x85,0xea,0xdd,0x7c,0xee,0x1f,0x9a,0x84,0x55,0xaf,0xa8,0x62,0xc6,0x9c,0xff, + 0x64,0xfb,0x62,0xb0,0x85,0x8b,0xd9,0x64,0x45,0x99,0xd5,0x41,0x18,0xdf,0x8c,0x60,0x55,0xf5,0x1f,0x0a, + 0xf6,0x2d,0x8c,0x7d,0xf6,0x32,0x7c,0x77,0x62,0x2f,0x12,0xc9,0xc3,0xff,0x84,0xf9,0x37,0x22,0xaf,0x23, + 0x99,0xbf,0xe6,0x8f,0x6e,0xfb,0x5a,0x3b,0x99,0x76,0x88,0x5a,0x1d,0x1a,0xe7,0xc1,0x55,0x87,0x30,0xb0, + 0x14,0x43,0x05,0x5e,0x97,0x22,0x4d,0x8f,0x88,0x15,0x55,0x41,0x5d,0x3f,0xbc,0x8b,0x50,0x39,0xf2,0x4a, + 0xc8,0xe4,0x6d,0x24,0x06,0x00,0xe2,0xb6,0xb0,0xde,0x5d,0x90,0xe6,0x89,0x3f,0x09,0xcd,0xba,0x75,0x7d, + 0x68,0x1d,0xd4,0x98,0xce,0x70,0x25,0x41,0x9b,0x2d,0xc2,0x88,0xf0,0xed,0x0b,0x01,0xb9,0x45,0xf5,0x49, + 0x59,0xed,0xb6,0xa5,0x99,0x99,0x42,0xd4,0xe3,0xaf,0xcd,0xe0,0x0d,0x89,0xe7,0x5e,0x2a,0x8f,0x5d,0x46, + 0x0c,0x0d,0x62,0xe7,0xcf,0xae,0xe4,0x7a,0x99,0x7a,0x6e,0xdb,0x33,0xc4,0x19,0xa7,0x82,0x7d,0xd1,0x32, + 0xb3,0x85,0x81,0x1c,0x36,0x39,0x85,0x8d,0xde,0xf3,0x2b,0x6d,0x87,0x8e,0x2c,0x6f,0x3a,0xc1,0xbe,0xac, + 0xd3,0x25,0xf5,0xd6,0xd4,0x0a,0xa2,0xab,0x57,0x3b,0x0c,0x92,0x6b,0x02,0x0a,0x0f,0x13,0xef,0x84,0x02, + 0x3a,0xcb,0x00,0xcf,0xff,0xc7,0x72,0x89,0x68,0x9e,0x03,0xed,0x67,0x91,0xfb,0xe8,0x8e,0xac,0xe6,0x5a, + 0x01,0x0d,0xb8,0x6a,0xab,0xd1,0x22,0x9d,0xd5,0xb2,0x53,0xb4,0x7f,0x67,0x60,0x16,0xfa,0xf5,0x79,0x3a, + 0x2e,0x6b,0x87,0x3e,0xe2,0x89,0x66,0xaa,0xd3,0x64,0x6c,0x8e,0xfd,0x48,0x48,0x8e,0x17,0x16,0xc6,0x8e, + 0x4c,0x07,0x17,0x6b,0xc1,0xc3,0xad,0x93,0xac,0xdf,0x16,0xb2,0x90,0x46,0x3e,0xef,0x3a,0x18,0xd4,0x26, + 0x1a,0x76,0x07,0x7b,0xbf,0x8d,0x27,0x10,0x95,0x14,0x9e,0x1f,0x5a,0xa3,0x5a,0x9a,0xd4,0x97,0x62,0x0d, + 0x02,0xef,0xae,0x68,0x72,0xc3,0x0b,0x21,0xed,0x05,0xfa,0x5c,0x2d,0x6d,0x63,0x34,0xd7,0x80,0xd3,0x27, + 0x69,0x50,0x82,0xb9,0x6a,0xea,0x70,0xb1,0x8b,0x37,0xa0,0x5e,0x94,0xde,0xa9,0x52,0xe9,0x79,0xfc,0x54, + 0x17,0x88,0xcc,0x7f,0x6e,0xd6,0x52,0x3d,0xa5,0x57,0x5a,0xaa,0x7d,0x36,0x4d,0x41,0xe3,0x27,0xbd,0xd7, + 0xd2,0x62,0xd4,0xe2,0xff,0xef,0x0d,0xe5,0x2b,0x04,0x10,0x0f,0x35,0xab,0xa8,0x68,0x99,0x79,0x79,0xdf, + 0x79,0x5a,0x53,0xdb,0x07,0x6b,0x48,0x85,0xab,0x49,0x9d,0xf5,0x43,0xa0,0xbc,0x35,0x31,0x76,0x2a,0x5d, + 0xce,0xaa,0xf6,0xce,0x5a,0xf9,0x22,0x1c,0x09,0xec,0xe6,0x7e,0xa6,0x69,0x46,0x3a,0xec,0x2c,0xd5,0x08, + 0x1d,0xaa,0x3b,0x54,0x0a,0x5b,0x13,0x99,0x68,0x06,0x6f,0xf1,0xb6,0x4c,0xd2,0xf4,0x6d,0x76,0x61,0x13, + 0x5d,0x35,0x6b,0x65,0x85,0x91,0x2f,0x04,0x65,0x62,0x88,0x87,0x78,0x07,0x4e,0xb5,0x59,0x01,0x0b,0x14, + 0xbf,0x7b,0xa8,0xd1,0x51,0xf8,0x20,0x5f,0x22,0xc0,0xf1,0x2a,0xa2,0x86,0x4e,0x36,0x7e,0x98,0xd8,0xe2, + 0xa5,0x0a,0xee,0x77,0x8a,0xbe,0xed,0x43,0x44,0xac,0x0b,0x51,0xc8,0x43,0xb8,0xa5,0x18,0x15,0x93,0x3e, + 0x60,0xae,0x2d,0x76,0xf5,0xbc,0xe4,0x7c,0x4c,0x4c,0xe8,0xa9,0xcd,0x34,0xaa,0x6d,0x99,0x47,0xca,0x10, + 0x5f,0x78,0x34,0x69,0xac,0xff,0x09,0x27,0x50,0x79,0x01,0x93,0x43,0x16,0x48,0x52,0x1d,0x4d,0x19,0x54, + 0x4c,0xfb,0x9a,0x0d,0x79,0x6e,0xbf,0xe5,0x4b,0xd5,0xbc,0xb0,0xfa,0x20,0xb8,0xf0,0x73,0x71,0x78,0x3c, + 0x3d,0xcb,0xa3,0xe3,0x17,0x35,0xcd,0x06,0xd9,0xb8,0x5a,0x14,0xb5,0xb0,0xae,0x1b,0xeb,0x0e,0x1a,0x7d, + 0xe3,0xaf,0x4d,0x05,0xeb,0x03,0x96,0x43,0x68,0xae,0x0e,0xd0,0x52,0xbf,0x46,0x7b,0xe8,0xd5,0xab,0x8d, + 0xfa,0x53,0xeb,0xbe,0x67,0x94,0x89,0x44,0x0a,0xb2,0x1e,0xa7,0x53,0xbd,0x17,0x46,0x47,0xe2,0xb0,0x72, + 0x33,0xeb,0x1a,0xfc,0x85,0xd5,0x16,0x9a,0x68,0xf5,0xd9,0x93,0xfb,0x72,0xa0,0x6b,0xb0,0x14,0xce,0xd3, + 0xc0,0x10,0x08,0x36,0x3f,0x18,0x29,0x1c,0x26,0xcc,0x9b,0xe8,0x5b,0xa0,0x53,0x10,0xf8,0x33,0x4c,0x92, + 0x20,0x85,0xc0,0x90,0xac,0x88,0xf6,0x29,0x9c,0x72,0xfc,0xf8,0x65,0x02,0x30,0x7c,0x23,0x84,0x2b,0x35, + 0x2d,0xd9,0xaf,0x47,0x4e,0x5c,0x2f,0x3e,0x75,0xfd,0xe5,0x5d,0x60,0xee,0xc1,0x83,0xb2,0x46,0x8d,0xe0, + 0x07,0x62,0x74,0x29,0x9e,0x89,0x0e,0x57,0xc2,0xe3,0xc1,0x3b,0x1c,0x6e,0xcb,0x92,0x6a,0x85,0x46,0x92, + 0x6e,0x7a,0x96,0x09,0xc9,0x0b,0x2c,0x5f,0x70,0x5d,0xb8,0x9b,0x77,0xde,0x8c,0xb6,0x6a,0x21,0x22,0x69, + 0x8a,0x3d,0x96,0x8d,0x97,0xc0,0x48,0x92,0x7f,0xa1,0xd3,0x96,0x4c,0x43,0xea,0xa6,0xab,0xde,0x5e,0x90, + 0x79,0xd0,0x2b,0xd7,0xe7,0xd3,0xab,0x8d,0x24,0x73,0x07,0x5f,0xc7,0x13,0x5b,0xb5,0x4c,0x54,0xae,0x9b, + 0x9f,0x36,0x32,0xc4,0xd9,0xec,0xd0,0xd1,0xe9,0xe4,0x29,0xa8,0xa0,0xd6,0x9b,0xf4,0x80,0x30,0xc0,0x3f, + 0x4f,0x5c,0xa5,0xa4,0x8b,0x81,0xb2,0x6d,0xbb,0xaa,0xb9,0xe5,0x36,0xa1,0x0c,0xd8,0x37,0xd4,0x27,0xfb, + 0x8a,0x8b,0x5e,0x21,0x3b,0x04,0xf3,0x3e,0xab,0x43,0xd0,0x24,0x5a,0x43,0x95,0xec,0x13,0x43,0x86,0xe7, + 0x4f,0x01,0x38,0x45,0x2f,0xdf,0x48,0xa5,0x57,0x42,0xef,0x75,0x79,0xea,0x99,0xcd,0xd0,0xd3,0x48,0x65, + 0x3e,0xdc,0x50,0xa5,0x7c,0x73,0x0b,0x63,0x02,0x5d,0xcc,0x2f,0x28,0xfd,0xa4,0xc8,0x7b,0x02,0x7a,0x15, + 0xa0,0x65,0xb2,0x39,0x28,0xe3,0xf5,0xc3,0x0e,0x03,0x69,0x77,0xec,0x32,0xba,0x5d,0xb8,0xa1,0x0c,0x62, + 0x1e,0x8a,0xc6,0xb0,0x55,0x2b,0xe9,0xd7,0x28,0x00,0x92,0x14,0xfa,0x9f,0xec,0xe8,0xa2,0x32,0x06,0x65, + 0xbd,0xdd,0xb0,0xca,0xef,0x2e,0x2f,0xd2,0x04,0x5f,0xe2,0x73,0xa1,0xf7,0x6e,0x4b,0xa3,0x05,0xc8,0x32, + 0xcc,0x32,0xaa,0xeb,0x61,0xa4,0x0d,0xb9,0x04,0xe0,0x07,0x21,0x99,0x5b,0x55,0x1f,0x72,0x82,0x1f,0x5c, + 0x50,0x8c,0xd7,0xea,0x2f,0x9f,0x5e,0xe5,0x95,0x55,0xa9,0xd5,0x3f,0xce,0x28,0x46,0xe8,0xf6,0x2f,0x5f, + 0x89,0x53,0x27,0xe5,0x16,0xda,0x45,0x25,0xff,0xfd,0x2b,0xf1,0x21,0xbd,0xc4,0x0f,0x9e,0x7c,0xb7,0xa5, + 0xd6,0x58,0x7f,0xc9,0x4e,0x31,0x21,0x6a,0x5d,0xf1,0x43,0x55,0xef,0xd0,0x2c,0x21,0x6e,0x50,0x16,0x66, + 0x27,0x38,0xaa,0x2c,0x2e,0x6c,0x5b,0x42,0xf8,0xb9,0xf3,0x0a,0x0f,0x9c,0xe4,0x14,0x52,0xf8,0x0a,0x5f, + 0x64,0x43,0xa5,0x3e,0xdb,0xca,0x63,0x16,0xcd,0xb5,0xe5,0xea,0xd9,0xcc,0x79,0x39,0x6a,0xee,0x2a,0x92, + 0xd5,0xb6,0x9f,0xdd,0x8a,0x46,0x32,0x66,0xdf,0x4a,0x68,0x02,0x67,0x44,0x37,0xed,0x04,0x50,0x47,0x4d, + 0xdf,0xe0,0x69,0xf9,0xb7,0x2a,0x71,0xde,0xa2,0x50,0x44,0x19,0x4c,0xfb,0x65,0xd7,0x5c,0x43,0x51,0x7e, + 0x57,0xa9,0x42,0x8b,0x98,0x93,0x57,0xf9,0x5c,0x42,0xd3,0xd3,0x94,0xc1,0xd3,0x6d,0x49,0xf9,0x07,0xab, + 0x7e,0x24,0xfc,0x9b,0x5b,0xff,0x4a,0x83,0x55,0xab,0x4e,0xd3,0xcb,0x1e,0x32,0x47,0x9b,0xb3,0xda,0x78, + 0x05,0x84,0xcd,0x32,0x4d,0x5b,0xd4,0x32,0xa5,0xc6,0xc9,0x0e,0x40,0x76,0x5f,0x14,0x41,0x11,0xa3,0xb4, + 0xe9,0xe5,0x34,0x2c,0x31,0xbc,0x3d,0x02,0xee,0xea,0x1a,0x49,0x48,0x94,0x43,0x6b,0x87,0xf1,0xae,0xef, + 0x43,0x15,0x9e,0xd2,0xec,0x14,0xb4,0x11,0xfb,0x33,0x4b,0x79,0xc4,0xcc,0x54,0x43,0x4a,0xc3,0xf5,0x32, + 0x64,0x1b,0x79,0x4a,0x2d,0x84,0xa1,0x68,0x39,0x02,0x84,0x44,0x01,0xae,0xdd,0xd9,0xe4,0xd4,0xe4,0x5a, + 0xc6,0x1b,0x93,0x63,0x98,0x9e,0x87,0x4f,0x07,0x9d,0x1b,0x1c,0x59,0x28,0xc8,0xea,0x55,0x00,0x00,0x62, + 0x72,0x56,0x3f,0x22,0xfc,0x1c,0x76,0xa6,0x4e,0x52,0xe2,0xa9,0xe9,0xf2,0x9c,0x9c,0xba,0x84,0x34,0x9d, + 0x24,0x40,0xc7,0x06,0x6c,0xd7,0x2b,0xc3,0x35,0x75,0xac,0x04,0xfa,0x17,0xe1,0xbb,0xaa,0xe2,0xba,0xa8, + 0xee,0x9b,0xac,0xe4,0x78,0x16,0x4c,0x14,0x6e,0x49,0x12,0x1a,0x68,0x51,0x7e,0x52,0x37,0xc8,0xf0,0xf0, + 0x91,0x90,0xc6,0xdc,0xb0,0x46,0x9a,0x37,0x1d,0x74,0x45,0x36,0x28,0xc4,0x90,0x54,0xab,0x84,0xcc,0xcb, + 0x1b,0xe0,0x1d,0xba,0x42,0x27,0x28,0x8e,0x4f,0xef,0xa7,0x46,0x35,0x4a,0x3d,0x03,0xf1,0x53,0xad,0x1f, + 0x8f,0xeb,0x03,0x61,0x87,0x0b,0xec,0xbe,0xb6,0xa5,0xd2,0x36,0x0a,0x2f,0xa7,0x8e,0x3d,0x58,0x5b,0x55, + 0x50,0x46,0x48,0x00,0xcd,0x90,0xe5,0x2d,0x2f,0x5f,0x2a,0xe6,0x2d,0x69,0xb9,0x0e,0xfb,0x82,0x03,0x24, + 0xb4,0xda,0xaa,0x69,0x95,0xf8,0x5c,0x2d,0xc8,0x66,0xd9,0xbd,0x3c,0x02,0x6d,0xba,0x1f,0xd4,0x08,0xbf, + 0xbb,0x6e,0x89,0x36,0x68,0x57,0x59,0x79,0x95,0x3e,0x0e,0xc8,0x3b,0x04,0x6e,0x5f,0xe1,0x15,0x65,0x48, + 0x2a,0xf9,0x19,0x80,0x70,0xe4,0xf8,0xa0,0x70,0xf8,0xf1,0xb0,0x4d,0x60,0x7c,0x76,0xe2,0xe0,0xc2,0x23, + 0xed,0x35,0x20,0x13,0x4a,0x08,0xa2,0xaf,0x89,0x61,0xfb,0x85,0xf2,0xb6,0x84,0x5d,0x3d,0xff,0x17,0x33, + 0x01,0x14,0x42,0xb0,0x25,0x8d,0xb7,0xe9,0x5f,0x1c,0xc7,0xb5,0x3e,0xce,0x9f,0x52,0xd2,0x1b,0xee,0x52, + 0xbf,0xf9,0x03,0xa2,0x17,0x7c,0x9c,0x5d,0xd9,0x13,0xf9,0xf3,0xb9,0xcb,0xbb,0x66,0xba,0x60,0x3a,0xf4, + 0x74,0x73,0xbb,0x1a,0xe1,0xc9,0xab,0xee,0x9e,0x61,0x18,0x96,0xf6,0xc4,0x06,0x31,0x78,0x20,0x62,0xb1, + 0x07,0x1c,0x6c,0x81,0x2c,0x07,0x09,0xdb,0x91,0x25,0xbc,0x21,0x7e,0x89,0xdc,0x59,0x24,0x65,0x93,0xb2, + 0xa0,0x5c,0x79,0xcd,0xa6,0xac,0x46,0x12,0xfe,0x3e,0xa7,0xaa,0x7b,0x80,0xbb,0x6f,0x16,0xd3,0x5d,0x5d, + 0x75,0xea,0x9c,0xe7,0x3c,0xe7,0x79,0x3e,0xee,0x3a,0x9d,0xfe,0xeb,0x49,0x3e,0xef,0x7a,0xa6,0xe5,0xf4, + 0x56,0x93,0xfe,0x7e,0x61,0x78,0x86,0x93,0x17,0x7c,0xf1,0x75,0xe6,0x59,0xbd,0x05,0x5f,0xb8,0x03,0xf2, + 0x5b,0xb9,0xa3,0x1d,0xd7,0x3d,0xf1,0x3b,0x71,0xb3,0x28,0x6a,0x7b,0xb5,0x1f,0x0f,0x71,0x76,0xf4,0xb8, + 0xc4,0xa7,0x53,0x71,0xbd,0x18,0x94,0x76,0xfa,0xaa,0xac,0xcf,0xb2,0xed,0xae,0x8e,0x8f,0x8a,0x82,0x6b, + 0x9b,0x3c,0x7e,0x53,0x32,0xb7,0x5f,0x38,0x76,0x89,0x47,0x41,0xe9,0xce,0x72,0x81,0xf6,0xae,0xe4,0xd8, + 0xc7,0x0c,0x67,0x7d,0xab,0x1c,0x30,0x6f,0xc0,0x16,0x9e,0x9c,0x34,0x85,0x30,0xe7,0xca,0x53,0x4d,0xe6, + 0xe3,0x41,0x29,0x78,0xab,0xab,0xe3,0xd3,0xee,0xa0,0x38,0x29,0x3c,0x7e,0x7b,0xee,0xf9,0xa9,0xcd,0x33, + 0x72,0x86,0x2f,0x5e,0x0c,0xde,0x3a,0x23,0xf9,0xdd,0xdd,0xd1,0xb9,0xe7,0x63,0x3c,0x40,0x20,0xdf,0x18, + 0x66,0x79,0xcf,0x12,0xe6,0xf9,0xc9,0x80,0xf9,0xa2,0xd8,0x6f,0x0b,0x8e,0x7f,0x8d,0x3c,0x1d,0x12,0xdd, + 0xc1,0x7f,0x10,0xdc,0x35,0xd6,0xed,0xfa,0x85,0xb9,0x06,0x8f,0xe6,0x9e,0x6f,0x25,0xc5,0x99,0x5c,0x6b, + 0x64,0xf9,0x4c,0x6d,0x4d,0x0a,0xbb,0x87,0x2f,0xc6,0xf5,0x17,0x1b,0xea,0x4c,0xc1,0xeb,0xc5,0x70,0x1b, + 0xd7,0x98,0xcb,0x75,0x1c,0x9f,0xab,0xf0,0x53,0xb4,0xb4,0xc2,0xa3,0xf8,0xc5,0x06,0x5d,0xb8,0x74,0x73, + 0x00,0xf7,0x1f,0xae,0xfe,0xac,0x72,0x6c,0xf1,0xee,0x2e,0xb3,0x0d,0xcb,0x4c,0x58,0xe1,0xfd,0x07,0x58, + 0x38,0xcd,0x0a,0xee,0x80,0xb7,0xb1,0xcc,0x4a,0xc2,0x3f,0x93,0xeb,0xf8,0xe8,0x80,0x5f,0x0b,0x5e,0xe5, + 0x9b,0xa5,0x79,0x73,0x12,0xd7,0xbc,0x5e,0xa2,0x04,0xfd,0x53,0x41,0x38,0x2a,0xf8,0x98,0x0d,0x52,0x9a, + 0xb7,0x59,0xd1,0xf5,0xc4,0x4b,0xf2,0xe5,0x17,0x9e,0x9c,0xda,0x5c,0xdd,0x76,0x04,0x1f,0x8d,0xbb,0x70, + 0xcc,0x95,0xd8,0xf0,0xcc,0xe0,0x55,0x56,0x1c,0xf0,0xf0,0x7f,0x88,0x3f,0x90,0xe2,0x89,0x3f,0x21,0x8c, + 0x80,0xe1,0x4e,0xa2,0x87,0xf5,0xe3,0xb6,0xd2,0x97,0x51,0x70,0xe5,0xc5,0x5a,0x8f,0x95,0x47,0xe6,0xaa, + 0x89,0xe7,0xe1,0x51,0x25,0xd7,0x71,0xdd,0x19,0x53,0xc5,0x18,0xdd,0x5a,0x08,0x13,0x66,0x5b,0xbe,0x6f, + 0x0b,0x36,0x58,0xb0,0xf0,0x5f,0xdb,0x75,0x7a,0x91,0xca,0xc0,0x69,0xdd,0xa8,0x56,0xea,0xb8,0x52,0xc7, + 0xb5,0xd1,0x9d,0xbe,0x46,0xa4,0xa3,0x61,0x3d,0x96,0x63,0x36,0x72,0xec,0x1a,0xcb,0x1b,0xf8,0x79,0x86, + 0xd2,0x44,0x97,0x7e,0x66,0x78,0x28,0x29,0xe2,0xcb,0x3d,0xe4,0xea,0xa4,0x90,0xfa,0x1e,0x9e,0x18,0x9d, + 0xda,0xbc,0x51,0xbe,0x7d,0xe0,0xd3,0x4a,0x17,0xfb,0x27,0x94,0x3c,0xbe,0x51,0x0d,0xd8,0xc7,0x2d,0xd9, + 0x8f,0xa8,0x2c,0xd7,0x51,0x9f,0x06,0xec,0x53,0x9e,0x10,0x7d,0x32,0xd2,0x65,0xc9,0x7e,0x76,0x75,0xfc, + 0x53,0xc1,0xf0,0x9b,0x53,0x8f,0x97,0x63,0x84,0xdc,0xa9,0x82,0xe1,0x87,0xa2,0x39,0xd6,0x19,0xb1,0x4e, + 0x75,0x1c,0x82,0xe1,0x61,0x1a,0x59,0x27,0x92,0xf4,0x05,0x77,0xf0,0x41,0x80,0x0a,0x48,0x3f,0x62,0xbd, + 0x9e,0x30,0x7c,0xdc,0xfe,0x5e,0x2d,0x60,0x3d,0x02,0x8f,0xf8,0x18,0x93,0xbe,0xeb,0x9a,0x51,0x8a,0x0b, + 0xca,0x47,0x4f,0xe2,0x94,0x5c,0x47,0xa0,0xcc,0x05,0xcb,0x93,0x7e,0x89,0x33,0x5b,0xf8,0x95,0x33,0x55, + 0x5c,0xdf,0x44,0x8e,0xfb,0x8e,0x74,0x7c,0x5a,0xd4,0xe7,0xe5,0x64,0x83,0x27,0x32,0xf8,0xed,0xea,0x10, + 0x9f,0x59,0xb0,0x9c,0xca,0x28,0xeb,0xb1,0xdd,0x41,0xe0,0x1e,0x18,0x94,0x35,0xa4,0xf3,0x24,0xca,0xc1, + 0x06,0x71,0x9c,0x87,0xdc,0xa0,0xca,0xf1,0xd0,0xd4,0x73,0x01,0x3b,0xd1,0x6f,0x79,0x96,0xcf,0x9f,0x7c, + 0x61,0xcb,0xb6,0x04,0x2d,0x99,0xac,0x5c,0xa8,0x3e,0x96,0x63,0x9f,0x74,0xbd,0x80,0x7d,0xda,0xb5,0x4b, + 0xe9,0x8a,0x09,0x85,0x8f,0xa7,0x1c,0x7e,0x6a,0x6c,0x82,0x2e,0x89,0x1f,0xac,0xd7,0x76,0x3d,0xab,0x07, + 0x61,0xe6,0x3a,0x3e,0x6f,0x38,0x25,0xc0,0x66,0x98,0x5f,0x0c,0x4a,0x0a,0x03,0x52,0x3c,0x2e,0x39,0x95, + 0xf3,0xe1,0x82,0x25,0xb3,0xae,0xf8,0x15,0x4e,0xfe,0xa2,0x8f,0xa4,0xc8,0xf6,0x9b,0xe0,0xad,0xdc,0x51, + 0x86,0x68,0xd0,0x8d,0xf4,0x8c,0xd2,0xca,0xb7,0x85,0x04,0xd8,0xce,0xd7,0xd8,0xa7,0x0c,0x1b,0xd5,0x17, + 0xe8,0x25,0x83,0x3a,0x34,0xf8,0xed,0x50,0x99,0x50,0x58,0x0b,0x6a,0x3d,0xb2,0x9f,0xe0,0x55,0x63,0x1f, + 0x37,0x4e,0x5a,0x26,0x8f,0x66,0xd8,0x47,0x85,0x5f,0xb0,0x45,0x41,0x14,0x79,0xc0,0x4e,0xa2,0x9a,0x46, + 0xaf,0x78,0x5e,0xbf,0x9d,0x40,0xd2,0x4f,0xb8,0x09,0x33,0x91,0x02,0x55,0x58,0xcf,0x95,0x62,0x32,0x78, + 0xf5,0x54,0xad,0x92,0xc3,0x0b,0xa2,0x81,0x4b,0x23,0x1a,0x85,0xc2,0xac,0xf0,0xac,0x3f,0xd0,0x1e,0xf2, + 0x01,0xc1,0x32,0x05,0xea,0xf0,0x52,0xf5,0x3f,0xa2,0xf8,0xca,0x31,0x50,0xc2,0x50,0x75,0x4f,0x05,0x5e, + 0xfa,0x25,0x42,0x0c,0x59,0x37,0xc5,0x55,0xc1,0x91,0xd9,0x77,0x5f,0xc7,0xdb,0x2f,0xb8,0x9b,0x8c,0xc8, + 0xe0,0x3e,0xea,0xd0,0x27,0xa6,0x36,0x8f,0x50,0xaf,0x0c,0x7f,0x9d,0x3e,0xbc,0xb6,0x82,0xcf,0xce,0xa2, + 0xa4,0x66,0x22,0x7d,0xcf,0x28,0xf1,0x95,0x90,0xee,0x7b,0x52,0x24,0xc8,0x68,0x46,0x2f,0xcc,0xf5,0xd4, + 0xcf,0x2b,0xd4,0x94,0x5d,0x54,0xbd,0xa9,0x57,0xcb,0x48,0x13,0xf5,0x3b,0x61,0x69,0xb0,0xc9,0xf2,0x03, + 0x9e,0x44,0x25,0x80,0x2f,0x2b,0xef,0x9a,0xa2,0x38,0x20,0xd9,0xdf,0x15,0x44,0x7d,0xa9,0x5a,0xb9,0xca, + 0x1c,0x22,0x3f,0xbe,0x85,0xc0,0x76,0x6b,0x7b,0x7f,0x4f,0x01,0x77,0x1c,0xfd,0xb8,0x2b,0x24,0x43,0xe3, + 0xd3,0xd7,0xaf,0x27,0x9b,0x3f,0x5c,0xff,0x2e,0xea,0x26,0x67,0x2a,0x8c,0xe0,0xef,0x3a,0x86,0x6d,0x97, + 0xa2,0x39,0xf9,0x41,0x8c,0x53,0xe8,0x32,0xa5,0xaf,0x57,0x10,0xec,0x43,0x9e,0x08,0xcf,0xce,0xb4,0xeb, + 0xc2,0x4e,0x80,0x61,0xf3,0x85,0x68,0xa7,0x7c,0xf7,0x57,0xab,0x89,0x67,0x49,0xe1,0xec,0x57,0x0d,0xa1, + 0x7c,0x02,0x57,0x26,0xe8,0xde,0x76,0xfc,0xea,0x34,0x73,0x07,0x1d,0xa2,0x1a,0x29,0x78,0x47,0xf7,0x80, + 0x3f,0x46,0x0f,0x03,0xf6,0x79,0x77,0xc0,0xf1,0x6b,0xec,0x8b,0xc0,0xa7,0x90,0x39,0x6a,0x0e,0x46,0x1f, + 0x31,0x6c,0x6c,0x97,0x26,0x03,0xe2,0x19,0x87,0x78,0x30,0x5e,0xba,0xfc,0x4e,0x9b,0x23,0xa3,0xef,0x07, + 0x08,0xd3,0xc5,0x9f,0x4b,0x44,0xc5,0x9f,0x16,0x4c,0x1a,0xfe,0xf6,0xa0,0x83,0x2e,0x35,0x88,0xe7,0xf6, + 0x50,0x35,0x26,0xd1,0x41,0xdd,0x82,0xa8,0x06,0x35,0x71,0xe9,0x71,0x82,0x06,0x2d,0x1a,0xce,0xcf,0x9c, + 0xc9,0xc4,0x1c,0xb0,0x7d,0x1c,0x83,0x6f,0x0d,0xd5,0x17,0x52,0xf8,0xb1,0x47,0xc7,0x59,0xa6,0x69,0x23, + 0x40,0x80,0xb4,0x54,0xb5,0xcd,0x0a,0xc1,0x94,0x1a,0x9f,0x78,0x9c,0xf5,0x20,0x56,0x73,0x4a,0x6f,0xd2, + 0xde,0x82,0xe3,0x2b,0x8a,0x38,0xc9,0xfb,0x2a,0xfc,0xc9,0x2c,0xa9,0x1c,0x77,0x93,0xc8,0x71,0xde,0x2d, + 0x16,0xdf,0x17,0x26,0x1b,0x90,0x55,0xea,0x6d,0x07,0xc1,0x0e,0xe4,0x0b,0xb4,0x8b,0x70,0x72,0x8c,0x00, + 0x1f,0x38,0xdb,0xe1,0x9d,0xe7,0x62,0x4f,0xe5,0x79,0x68,0x8a,0x11,0x23,0x10,0x88,0x78,0x39,0x28,0x21, + 0x22,0x44,0x3e,0x82,0xf6,0xf0,0xd5,0xe4,0xc1,0x7c,0x43,0x06,0x2b,0xb5,0x1c,0x35,0x8d,0x26,0x8b,0x7e, + 0x61,0xaa,0xbb,0x0f,0x7a,0xae,0x2f,0x40,0x2d,0xbd,0x1e,0xc5,0xb9,0x30,0x4d,0x44,0x04,0xd6,0x54,0x94, + 0x8e,0xb6,0x44,0x20,0x0e,0xb6,0x1d,0x20,0xa0,0xf5,0xa9,0xed,0x63,0xf0,0xaa,0xdb,0x27,0xcc,0x1f,0x44, + 0x1a,0xc3,0xa0,0x91,0xe6,0x82,0x3b,0xe0,0x33,0x69,0x69,0xf6,0x0b,0x35,0xd5,0x80,0x78,0x3c,0x77,0x30, + 0xf8,0xae,0x0e,0x3f,0x41,0xa6,0x50,0xdb,0x87,0x7a,0x0d,0xcb,0x89,0xff,0x4f,0x01,0xe0,0x56,0x19,0x2f, + 0xac,0x2b,0x14,0x52,0x28,0x27,0xad,0x3c,0xda,0xed,0x6a,0x60,0x79,0xf4,0x4b,0xe6,0x2d,0x41,0x93,0x1a, + 0xa0,0x38,0xbf,0x7b,0x75,0x97,0x6f,0xf5,0x5a,0x0e,0xd5,0x7d,0x26,0x60,0x18,0xc2,0xc8,0x07,0xa7,0xfe, + 0x71,0xaa,0x9a,0x38,0x73,0x63,0x13,0x08,0xac,0xc7,0x4f,0x6c,0xc4,0xb3,0x10,0xe8,0x8a,0x3c,0xff,0xee, + 0x04,0x6f,0x57,0x30,0xd7,0xf1,0x0d,0x8e,0xd8,0xde,0xc7,0x16,0x3f,0xe0,0x4f,0xc7,0x5e,0xa4,0xc8,0x85, + 0x96,0xf1,0xe7,0x57,0x42,0x4f,0x67,0xd1,0x93,0x5d,0xe0,0x7b,0x13,0xfd,0x8e,0x08,0xd0,0x32,0x4e,0x48, + 0x4c,0x51,0x10,0x43,0xaa,0x63,0x77,0x39,0xfb,0x49,0xee,0xe8,0x98,0xfa,0x21,0x2c,0x62,0xad,0xed,0x56, + 0x7b,0x61,0x8e,0x7d,0xca,0x0d,0x3d,0xd0,0x69,0x09,0x54,0xe9,0xb9,0xc5,0x1f,0x3d,0xe0,0x94,0xb5,0xbb, + 0x6a,0x2e,0x66,0xd9,0xe1,0xc3,0x7c,0xec,0x3e,0x32,0x9b,0x0e,0xf2,0xe3,0x49,0x11,0xe1,0x53,0x39,0x6b, + 0xc1,0x69,0x42,0x6d,0xfd,0x51,0xd9,0x18,0x5d,0xc6,0x26,0x76,0x1f,0x02,0x37,0x0d,0xaf,0xcf,0x11,0x52, + 0xf2,0xc5,0xd1,0x91,0x6c,0xdd,0x2b,0x39,0x45,0x14,0x43,0xd5,0x0a,0x31,0x85,0x06,0x46,0x4f,0xe9,0xb9, + 0xd0,0xa3,0xe9,0x8f,0x81,0xe7,0xe9,0x47,0xa8,0x55,0xa9,0xb9,0xcc,0x4e,0xa2,0xf1,0x4c,0x52,0x15,0x8a, + 0x58,0xd0,0x7c,0x0e,0xb3,0x7a,0x5a,0xed,0xaf,0xb4,0x64,0x58,0xdb,0xff,0x24,0x15,0x0e,0xb3,0x2d,0x08, + 0x8b,0xa5,0x19,0x1e,0xd1,0xdc,0x7c,0xef,0xd8,0xa2,0x96,0x11,0xea,0x11,0x78,0x47,0x3d,0xc4,0xbc,0x3d, + 0xa0,0xac,0x02,0x8c,0x16,0x21,0x0b,0xb9,0xd6,0xeb,0x77,0x77,0x73,0x1d,0xd9,0x39,0xdb,0xaf,0xf3,0xb5, + 0x6f,0x65,0x5b,0xe2,0xa3,0x65,0x55,0x1d,0xbe,0x88,0x3f,0x17,0x82,0x71,0x35,0xb4,0x8f,0xfb,0x9d,0xf2, + 0xec,0x1f,0x27,0x43,0xd5,0x1f,0xe5,0x60,0x3f,0x92,0x1a,0xf3,0x07,0xdd,0xb4,0xa5,0x9b,0xe9,0x3d,0x3a, + 0x90,0xaf,0xdd,0x7a,0x1a,0x3d,0xf8,0x36,0x8d,0x3f,0x52,0x7b,0x1d,0x78,0xac,0xf5,0xcd,0xd0,0x70,0xb5, + 0x92,0x0d,0xf8,0x66,0xe8,0x8d,0x77,0x1d,0x1d,0x23,0xe2,0xbd,0x36,0xc7,0x1c,0x5d,0xbc,0xc6,0x5d,0x6f, + 0x6d,0x3c,0xd7,0xea,0x09,0x7f,0xda,0xe0,0xe1,0xa5,0xb9,0xb5,0xf1,0xb3,0x94,0xa9,0xfb,0x6a,0x50,0x91, + 0xfa,0xc2,0x24,0x96,0x03,0x68,0xe7,0x44,0xc6,0x29,0x8b,0xb4,0x35,0x59,0x8e,0xfd,0x8b,0xf0,0xef,0xee, + 0x66,0x23,0xe2,0x3f,0x6b,0xa6,0x6f,0x0c,0x0d,0x5f,0x56,0x1d,0x74,0x1c,0xb9,0x4a,0x00,0x0a,0x1e,0x85, + 0x24,0x09,0xe8,0x1f,0x8c,0x1d,0x81,0x1b,0x13,0x3a,0x64,0x8e,0x69,0x45,0x70,0xa3,0x2d,0x08,0x52,0x99, + 0x42,0x91,0x93,0x02,0x51,0xd2,0xf3,0x0d,0x7e,0x2a,0xbe,0x23,0xf9,0xbe,0x7c,0x78,0xef,0xa0,0x7e,0xc0, + 0xb0,0xa7,0x6e,0xa5,0xc1,0x31,0xb3,0xe5,0x40,0x3e,0xa0,0x0e,0x90,0x7a,0x49,0xb7,0x7b,0x52,0x00,0x11, + 0x2b,0xb5,0xa9,0xd3,0xf4,0x60,0x19,0xff,0x22,0x85,0x48,0x4b,0x58,0x3e,0x1c,0xc4,0xb1,0xd9,0x1f,0x86, + 0xf3,0x65,0x2c,0x89,0xf9,0xe1,0x17,0x87,0xf3,0xe8,0xae,0x84,0xc8,0xa1,0x3e,0xea,0x33,0x03,0xd2,0xff, + 0xa0,0x35,0xb2,0x25,0x1c,0x82,0x72,0x35,0x9c,0x09,0xb2,0xfe,0xc4,0x11,0x23,0x9b,0xa5,0x27,0xaa,0x6f, + 0x46,0xd5,0xd7,0xff,0x84,0xae,0xc8,0x44,0xef,0xbe,0x2e,0xc5,0x88,0x7c,0xe2,0xc9,0x16,0xf5,0x3e,0xe4, + 0x28,0xb2,0xdd,0xba,0x53,0x05,0xfa,0xe9,0x28,0x9a,0x97,0x0c,0x44,0x21,0x3c,0x34,0x6a,0x39,0x46,0xa7, + 0x3c,0x17,0x8c,0x23,0x99,0xc3,0xd9,0x44,0x15,0x4e,0x38,0x82,0x46,0xe8,0xf1,0x41,0x51,0xc1,0xaf,0x6b, + 0x0c,0x22,0x03,0x12,0x30,0x1c,0x9d,0xd3,0xd1,0x47,0xad,0x6d,0xa5,0xc6,0x33,0x2d,0x9c,0xc8,0xb5,0xf1, + 0xb4,0x74,0x0d,0xde,0xba,0x43,0xd2,0x33,0x87,0x6c,0x51,0x24,0x0b,0x0d,0x28,0xd9,0xf1,0x55,0x3a,0x90, + 0x78,0xa3,0xa1,0xa1,0x09,0x94,0xda,0x28,0x96,0xdc,0x0e,0x9e,0x28,0xaf,0x30,0x14,0xda,0x90,0x5a,0xdc, + 0xa3,0x7f,0x65,0x7c,0x9f,0x36,0x48,0x75,0xb0,0x8e,0xea,0x85,0x67,0xea,0x1b,0xf3,0x28,0x3f,0x69,0xb2, + 0xf0,0x0f,0x93,0x6b,0x8f,0x53,0x34,0x2b,0x5a,0xdb,0xac,0xdd,0x92,0x24,0x4a,0xe7,0x02,0xfa,0xe9,0x17, + 0x78,0x94,0xfe,0x31,0xca,0x8a,0x46,0x9f,0x08,0x59,0x8f,0x47,0x13,0x31,0x9b,0xa3,0xff,0x24,0x56,0xd8, + 0x43,0xdd,0x9e,0x51,0x30,0x8a,0x9d,0x32,0x13,0x33,0x3c,0x1a,0x61,0x0e,0x65,0x0f,0x79,0x9e,0x84,0xe8, + 0x77,0x4c,0x68,0x98,0xaa,0x16,0x7f,0x2a,0xb3,0x98,0x12,0x4a,0x6c,0x6e,0x7d,0x53,0x6d,0x07,0x30,0xa4, + 0xad,0x8e,0x41,0xe3,0x7f,0xdb,0x2e,0xb5,0xbe,0x03,0xc2,0x16,0xe9,0xcc,0xad,0xab,0xc1,0x63,0xf9,0x92, + 0x6a,0xdb,0x0c,0xd4,0x7e,0xa0,0xbc,0x93,0x06,0x8d,0x5b,0x3c,0xca,0x75,0x3c,0x74,0xf0,0x99,0x16,0x13, + 0x24,0x13,0x21,0x8d,0xf4,0x98,0x9e,0xa5,0xad,0x72,0xec,0xa1,0x9b,0xce,0x1f,0xa4,0xfa,0x1b,0xc5,0xee, + 0x19,0x0e,0x7e,0xad,0x6e,0x24,0xf5,0x95,0x00,0xa1,0x2d,0xf5,0x87,0x32,0x40,0x64,0x3d,0xca,0xe1,0x7f, + 0x75,0x35,0x6a,0x94,0x30,0xf3,0x56,0x0d,0x7e,0x6a,0xed,0x71,0x25,0x93,0xe5,0xdd,0x3f,0x4f,0x86,0xf8, + 0xea,0x0e,0x61,0x64,0x64,0xc7,0x59,0x58,0x1b,0x3f,0x16,0x85,0x67,0xb7,0xf1,0x33,0xa4,0x9f,0x40,0x66, + 0x8f,0x41,0xdd,0xbb,0x9f,0xba,0x2f,0x0e,0x64,0xcf,0x9a,0xd9,0x39,0xb9,0x0c,0xa7,0x28,0xcb,0x85,0xfd, + 0xc3,0x49,0xac,0xd9,0xa6,0x27,0x1c,0xf4,0xfb,0x36,0x0d,0x09,0xaf,0x4f,0xd6,0xb2,0xd7,0x6a,0x2a,0xfe, + 0xe8,0x40,0x48,0xc9,0x66,0x7d,0x15,0x13,0x3f,0x4c,0xeb,0x96,0x42,0xf6,0x5c,0x2e,0xfb,0x80,0xd0,0xba, + 0x3d,0x85,0x1a,0xb7,0xb7,0x3a,0x10,0x1c,0xd9,0x24,0xdd,0x37,0xe2,0xc2,0x32,0xd2,0xea,0x39,0xc8,0xb1, + 0x78,0x64,0xc0,0xb0,0x99,0x55,0xc4,0x48,0x00,0xa1,0xf9,0x25,0x74,0xbf,0x18,0xd9,0x1d,0xe7,0xe7,0x32, + 0x07,0xda,0x8e,0xfa,0xdc,0x85,0xf6,0x5e,0x88,0xf5,0xd9,0x06,0x90,0xb9,0x74,0x3e,0x1a,0x39,0x43,0xc7, + 0x43,0x88,0xb7,0x46,0x70,0xf6,0xbe,0xe5,0x23,0x26,0x85,0xdc,0x24,0x96,0x0e,0xdb,0xfd,0xb3,0xc6,0xe0, + 0x5b,0x44,0xbe,0xd0,0x05,0x21,0x9c,0x37,0x50,0xbd,0x94,0x32,0x70,0x8b,0xb5,0xc7,0x11,0xf5,0xca,0x7e, + 0xb8,0x18,0x35,0x02,0x0a,0x19,0xe4,0x84,0xd1,0x03,0x11,0xb6,0x4d,0x0f,0xa4,0xe5,0x89,0x83,0x57,0xc2, + 0x78,0x9a,0x48,0xc8,0xc7,0x61,0x7b,0xe1,0xed,0x37,0x3c,0x8f,0x52,0xa4,0xe0,0xcc,0xc9,0x0c,0xec,0xa7, + 0x02,0x90,0xee,0x5d,0xfd,0x83,0x4c,0x23,0xb5,0x2e,0x7c,0x33,0x99,0x38,0xc1,0xf1,0xc7,0x03,0x25,0xfe, + 0x8e,0xcd,0xc6,0x4d,0x7a,0x64,0x78,0xe8,0x8e,0xf4,0x9c,0x1a,0x2d,0x91,0xaa,0x18,0xa7,0x5a,0xdf,0xc9, + 0xf6,0x53,0x13,0x68,0x5e,0x5d,0xd3,0xca,0x17,0x6a,0x61,0xa6,0x17,0x64,0x43,0x41,0xb7,0xab,0xd5,0xb3, + 0xbf,0x96,0x0d,0x22,0x15,0xe1,0xf2,0x7c,0xa3,0x8b,0x7a,0xbd,0x59,0x3b,0x47,0x32,0x36,0x78,0x5c,0xd6, + 0x74,0xee,0xa6,0x98,0xce,0xc6,0xb6,0xce,0x05,0x3f,0x15,0xae,0x2c,0xd7,0x54,0x05,0x9c,0xe0,0x19,0x02, + 0x14,0x84,0x30,0x5e,0xdd,0xbe,0x73,0xdb,0xb6,0x01,0xdd,0x84,0x9d,0x30,0x8a,0x06,0xfc,0x8e,0xe1,0xa0, + 0x1d,0x13,0x24,0x06,0xe3,0x49,0xf5,0x19,0x15,0x8a,0x36,0xc9,0x1e,0x62,0xb0,0xa1,0x4d,0x1c,0x17,0x53, + 0x05,0x89,0x34,0x2d,0xa9,0x7e,0x34,0xd4,0x55,0x5a,0xb4,0x70,0xa7,0x6f,0x8d,0x6e,0xad,0x5c,0x12,0x6a, + 0x8f,0x8a,0x37,0x77,0xeb,0x3b,0xb0,0x92,0xaf,0xe3,0xbe,0x69,0xc7,0x72,0xd5,0xb2,0x56,0x4f,0x92,0xaa, + 0x6e,0xd6,0x27,0x44,0xbf,0x04,0xb1,0xc4,0xa3,0x9a,0x0a,0x76,0x70,0x18,0x12,0x95,0xc6,0xab,0x31,0x06, + 0x91,0x79,0x1b,0x6d,0x96,0x05,0xd2,0x46,0x28,0x71,0xb9,0x89,0xf9,0xf5,0xcb,0xe0,0x57,0x12,0x5d,0x44, + 0x5c,0x01,0xef,0xb8,0xf9,0x9f,0xf6,0x04,0x47,0x62,0x8c,0xd5,0xd2,0x9d,0x3b,0x3b,0x48,0xd4,0xa7,0x4e, + 0xd4,0x7b,0x99,0xb9,0xed,0xfe,0xca,0xf6,0xe5,0x75,0xb5,0x73,0x17,0xbb,0xfe,0x2b,0xa9,0xfe,0xa2,0x02, + 0xbb,0xdb,0xa1,0x66,0x36,0x25,0x45,0xce,0x57,0xb7,0xda,0x49,0xea,0x69,0xa5,0xc6,0xb0,0x94,0x0a,0x0e, + 0x9d,0x81,0xa6,0xe2,0xea,0x5c,0xcb,0x6f,0xae,0x8d,0xd7,0xdb,0xd6,0x51,0x36,0x78,0xad,0x5e,0x0c,0xef, + 0x94,0xe3,0x26,0x9d,0x7c,0x0e,0x48,0x1d,0x59,0x9e,0x73,0x2a,0xc8,0xee,0x5c,0x25,0x87,0x3a,0x0f,0xd1, + 0x2d,0x94,0x20,0xa9,0xf0,0xce,0xd6,0xa5,0x2d,0x65,0xa0,0x42,0xae,0x71,0xa4,0x44,0x30,0x16,0x46,0xea, + 0x8e,0xd3,0x84,0xf3,0xff,0x18,0x3c,0xf3,0xc4,0x57,0x5d,0xf8,0x30,0xb3,0xba,0xb3,0x76,0x8f,0x50,0xfd, + 0xf4,0x7a,0x34,0x81,0xe3,0x0d,0x33,0x1d,0xbd,0x5a,0x5a,0xad,0xa8,0x51,0xce,0x7a,0x30,0x6c,0x4a,0xf8, + 0x0b,0x10,0x22,0xdd,0x79,0x84,0x91,0x11,0x0b,0x77,0x9c,0x6c,0x37,0xba,0xa4,0x6e,0xda,0x83,0x05,0xdc, + 0xfa,0x9d,0xc8,0x16,0x2c,0xbd,0xc3,0xc4,0x23,0x96,0xf4,0x95,0x98,0x6d,0xb1,0x87,0x3e,0x9f,0xf5,0x7a, + 0x50,0xaa,0xba,0x7d,0x3f,0xd0,0x0a,0x5a,0x56,0xb5,0xa2,0x4e,0x7c,0xdc,0x73,0x8c,0x15,0x2c,0xf3,0x8d, + 0x88,0x84,0x6f,0xad,0x6d,0x70,0x58,0x41,0xd9,0x92,0x1b,0x8e,0x50,0x2b,0x31,0xd2,0x32,0x61,0x5b,0xdb, + 0xe3,0xd1,0x4d,0xd6,0x6f,0x48,0x09,0xf1,0x9c,0xa3,0x39,0x95,0xfd,0xe0,0xf1,0xd3,0x28,0x76,0xb7,0x0d, + 0x5a,0x9d,0x61,0xdd,0x96,0xe7,0x17,0x30,0xfd,0x2c,0x79,0x4b,0x42,0xee,0x0b,0x35,0xb5,0xbe,0x62,0x74, + 0x77,0x5b,0xbc,0x4e,0x2d,0xe4,0x00,0x29,0x53,0x9b,0x90,0x77,0xd3,0xf8,0x1c,0x14,0xe9,0x4b,0x5e,0x8e, + 0x27,0x14,0x73,0xe9,0x2d,0xb2,0x72,0x92,0x49,0xdf,0x24,0xee,0xa3,0xaf,0xf8,0x5d,0xef,0x97,0xfb,0x1f, + 0x2c,0x96,0x33,0x26,0x3c,0xa7,0x5d,0xca,0xd6,0x2f,0x68,0x52,0x1e,0x07,0x6b,0x48,0x5f,0xbd,0xc9,0xde, + 0x57,0x48,0xb1,0x0d,0x82,0x8d,0xc5,0x7a,0x13,0x45,0xb8,0xae,0x0d,0x69,0x93,0x26,0x14,0x51,0xca,0x45, + 0x52,0xe0,0x86,0xa3,0x16,0xab,0x9e,0xb7,0x49,0xf5,0x4c,0x06,0xa7,0x79,0xeb,0xa0,0x64,0x46,0x2a,0x15, + 0xb1,0x6c,0x0c,0x4d,0xf1,0x18,0x74,0x67,0xf8,0x6d,0xaf,0xc1,0xbf,0x89,0x43,0x4d,0x38,0x01,0x69,0x60, + 0x82,0x19,0x3c,0x71,0x5c,0xcb,0x81,0x6c,0x08,0xf4,0x65,0x36,0x31,0x0c,0x44,0x49,0x48,0x2c,0xc0,0x32, + 0xde,0xf1,0xbe,0xae,0xfa,0xa7,0x5c,0x3e,0x51,0x00,0x89,0xf5,0xbb,0xae,0x5d,0x63,0x27,0x2c,0xdb,0x35, + 0x8a,0x47,0xd9,0xdf,0x0d,0x12,0xe9,0x34,0x0c,0x47,0xb2,0xa3,0x27,0x70,0xd1,0xa3,0x7f,0x9f,0x63,0x27, + 0xdc,0x18,0x05,0x22,0x65,0x87,0x2f,0x90,0xba,0x68,0x89,0x75,0x1b,0xf9,0xbe,0x1d,0x42,0x74,0x5a,0x5f, + 0xf4,0x16,0xe2,0x44,0x02,0x65,0xf4,0x42,0xfd,0x69,0x24,0x53,0x1f,0x0d,0x6c,0x0c,0x51,0x57,0x10,0x67, + 0x4e,0xc5,0x75,0x32,0xaa,0xd1,0x56,0x6f,0xaf,0xa0,0x04,0x7f,0x91,0x80,0x2a,0x2f,0x64,0xf6,0xc0,0x6f, + 0xc6,0x98,0x24,0x27,0x7c,0x8c,0x23,0xe6,0x1e,0x7a,0x05,0x83,0x4b,0x4e,0x4d,0x2a,0xf0,0x57,0xb0,0x08, + 0x1a,0x50,0xab,0xcd,0x58,0x0d,0xc6,0xe1,0x0b,0x99,0xa5,0xc3,0x5a,0x77,0x90,0xa6,0x09,0x5d,0x97,0xc1, + 0x99,0xa1,0xd0,0x4a,0x33,0x52,0x2d,0x1f,0xda,0xfc,0x70,0x4c,0x25,0xaa,0x9d,0x9f,0x35,0xb5,0x8e,0xc3, + 0xff,0x35,0xd0,0x67,0x9f,0x72,0xab,0x87,0x93,0x11,0x4d,0x80,0x1f,0x70,0xf9,0x56,0x7a,0xf9,0x48,0x5f, + 0xbe,0xf5,0x98,0xc9,0x24,0x15,0x45,0xd3,0x52,0x29,0x03,0x58,0xaa,0xbe,0xe3,0xae,0x2c,0x40,0xdd,0xb8, + 0x2a,0xc4,0xcc,0x83,0x57,0xea,0x1b,0x5b,0x22,0xc5,0x58,0x35,0xb8,0x42,0x9e,0x60,0x02,0xa4,0x0b,0x64, + 0xa2,0x6d,0x2f,0x8e,0x7d,0x53,0x55,0x28,0x4a,0x23,0x9f,0x56,0xe1,0xe5,0xb4,0x9b,0x1a,0x0e,0xae,0x9c, + 0xd5,0x27,0x81,0x77,0x07,0xe3,0xc7,0xf0,0x56,0x07,0x81,0xc3,0x32,0xff,0xca,0xfa,0x07,0x7c,0xda,0xe0, + 0x41,0x7a,0xb1,0x56,0x1a,0xf0,0x0e,0x62,0x38,0x01,0xc3,0x3d,0x5c,0x0b,0xae,0x0c,0x65,0xa8,0x6e,0x2e, + 0xcf,0x9c,0x83,0xd4,0x4e,0xe7,0x19,0x31,0x48,0x90,0x05,0x82,0x14,0xeb,0xe2,0x0c,0xe9,0x8c,0x12,0x3c, + 0x21,0x10,0x9a,0x89,0xee,0x1d,0x6f,0x5b,0x79,0xf8,0x97,0x64,0x17,0xea,0xe5,0x62,0xf8,0x4d,0x80,0x17, + 0x4f,0x5a,0xd2,0x72,0xb5,0x6f,0xde,0xcb,0x40,0x41,0xa9,0xd9,0x47,0xe1,0x65,0x95,0x22,0xc3,0x27,0xb7, + 0x5f,0xc1,0xc9,0xe9,0x2d,0xd3,0x6a,0x65,0x59,0xc3,0x35,0x9a,0xc9,0xaf,0x34,0x3b,0x51,0xe3,0xcd,0xff, + 0xa9,0x82,0xde,0xdc,0xcf,0x5a,0xb4,0xd1,0xbe,0x3a,0xc7,0xf8,0x07,0x5c,0x82,0xd2,0x87,0x41,0xd6,0x95, + 0xe8,0xb5,0xfd,0xf7,0x40,0xba,0x0a,0x40,0xa9,0x0c,0x39,0x15,0x7a,0x37,0x1d,0x4e,0x6c,0x7b,0xa8,0xd1, + 0x49,0x59,0x06,0x0e,0xb2,0x10,0xff,0x68,0x87,0xa0,0x62,0xc9,0x6c,0x2f,0x68,0x4b,0x52,0x34,0x73,0xe5, + 0x29,0x22,0x67,0xfa,0x38,0x3a,0x4c,0x12,0xc7,0x3a,0xba,0x91,0x6f,0x54,0x03,0x9f,0x4a,0x7e,0x5b,0x55, + 0xfb,0x18,0x48,0xa5,0x7e,0xa8,0x7e,0xe9,0x0b,0xaa,0x53,0xb3,0x96,0xf4,0x0a,0x0c,0x2c,0xd9,0x57,0xa1, + 0x9f,0xa3,0x6f,0xce,0xbe,0x91,0x3b,0x74,0xe4,0x10,0x89,0xee,0x53,0x41,0x89,0xf5,0x80,0x83,0x91,0xb9, + 0x67,0x8e,0xcd,0x06,0xcf,0x6c,0xfc,0x3c,0xc1,0x78,0x12,0x66,0x99,0x26,0xa5,0x53,0xda,0xbc,0x44,0x7a, + 0xa0,0x47,0x48,0x59,0x49,0xe4,0x5b,0x3c,0xc6,0x50,0x40,0xd9,0xc1,0x9e,0xff,0x9e,0xa6,0xbd,0x4d,0x51, + 0x94,0xcd,0xc3,0x87,0x12,0x0c,0x28,0x5d,0x9b,0xf5,0xb4,0x3f,0x51,0x51,0x41,0xa6,0xfe,0x6d,0x1e,0xa5, + 0xa4,0x00,0x05,0xac,0x27,0x56,0x15,0x0a,0x42,0xdd,0x7e,0xb3,0x5d,0x77,0x0a,0x56,0x0c,0xb7,0xb5,0xc0, + 0x1c,0x02,0x8a,0xf7,0x5f,0xb2,0x2f,0x2a,0xbe,0x46,0x23,0xb6,0xb6,0xe7,0x3e,0x52,0xc6,0x9f,0x04,0xf9, + 0x56,0x7a,0x12,0xff,0x91,0x82,0xa1,0x52,0x8a,0x19,0x12,0x7e,0x19,0x6f,0x57,0x43,0x55,0x81,0x5f,0x6a, + 0x34,0xb4,0xb1,0x40,0x2d,0xab,0xca,0x46,0x9a,0xf4,0xe7,0xd0,0x6a,0x8a,0x83,0x6d,0x35,0x5a,0x72,0x8c, + 0xe4,0x5f,0xaa,0xde,0x20,0x6a,0xd6,0x2f,0x77,0xb1,0x6b,0x14,0xcf,0x59,0xfd,0x68,0x0b,0xbc,0x2a,0xa6, + 0x6f,0x49,0xbe,0x1d,0x76,0xd1,0x81,0x93,0x72,0xc0,0x26,0xdf,0x1a,0x0d,0xa9,0x51,0x37,0x7d,0x8b,0x2f, + 0xde,0x5e,0xb9,0x05,0x2d,0x8c,0x21,0x46,0xed,0x45,0x97,0x3c,0xb7,0x7a,0xdf,0xa9,0xa6,0x0f,0x68,0xaa, + 0x26,0x72,0x76,0x6a,0x33,0xad,0xdc,0xf3,0xf1,0x19,0x39,0x43,0xa1,0x6f,0xaa,0x90,0x10,0x6d,0x52,0x34, + 0xbe,0x86,0x6b,0x1e,0xa1,0x63,0xb3,0x05,0x4a,0xe3,0x67,0x98,0xa6,0xfe,0x6b,0x6f,0x0d,0x6c,0xed,0x07, + 0x5f,0x4d,0xb3,0x85,0xf9,0xa1,0xcb,0xe9,0x3a,0xc0,0x3b,0x71,0xbf,0x2c,0x58,0x50,0x41,0xb5,0x73,0xa4, + 0x30,0xa7,0x47,0x79,0x9c,0x7e,0x20,0xab,0x18,0x2a,0x27,0x2c,0x0c,0xd4,0xe0,0xf4,0x56,0xaf,0xe1,0xac, + 0xaf,0xb6,0x5d,0x12,0x49,0xf4,0x5a,0x59,0xef,0x91,0x1e,0x1c,0xa8,0x83,0x6b,0x37,0x8b,0x82,0x9e,0xed, + 0x43,0x95,0xbc,0xcb,0xd4,0xe6,0x05,0x6a,0xf9,0x18,0xf0,0x19,0xb0,0x4d,0x6d,0x1e,0xf7,0x8d,0xc5,0xb5, + 0x0b,0x89,0xed,0xbb,0xbd,0x82,0xfa,0x5e,0x7d,0x38,0x49,0x91,0x82,0x1c,0x91,0x69,0x5f,0x18,0xf9,0x02, + 0x1b,0xa0,0x1e,0x25,0xc2,0xa3,0xa1,0x2c,0x06,0x2b,0xe7,0x61,0x0b,0x29,0x84,0x72,0x50,0xda,0xd0,0x13, + 0xdd,0xa9,0x9c,0xe7,0x91,0x72,0x36,0x8a,0x8d,0xe3,0x21,0xa5,0x2f,0xc3,0xf9,0x49,0xd8,0xd7,0x73,0x6d, + 0x1e,0x86,0x7a,0x6a,0xc6,0x9c,0xfd,0x84,0x97,0x5b,0xaf,0xcd,0x4f,0x56,0xb4,0xa8,0x50,0x21,0x8c,0xe0, + 0x60,0xbf,0xb0,0x03,0xfd,0x13,0xad,0x37,0xe3,0xfa,0x9b,0x72,0x21,0x54,0x90,0xa3,0x7d,0x7e,0xc2,0x5b, + 0xf3,0x47,0x8e,0x46,0x71,0x3b,0x55,0x26,0xd9,0x52,0x45,0xb3,0x4b,0xef,0x4c,0x9d,0x9e,0xfd,0x16,0x2f, + 0xbf,0x50,0x57,0xc2,0xf3,0xb5,0x9d,0x63,0x11,0xfe,0xde,0x79,0x4d,0x01,0x41,0xdf,0x1f,0xdb,0x3c,0x5c, + 0x40,0x75,0x41,0x80,0xf5,0x0b,0x0b,0x15,0x0a,0x9b,0x5a,0x2d,0x43,0x3d,0x34,0xe0,0xed,0xb3,0xfb,0x93, + 0x7f,0xdf,0x4e,0xe9,0x67,0x70,0x25,0x23,0x77,0x29,0x1d,0xc7,0x16,0x7f,0xa4,0x84,0xe5,0x3f,0x89,0x1d, + 0x4c,0x06,0xd5,0x6d,0xa1,0x47,0x2f,0xfe,0x80,0x62,0x1f,0x12,0x45,0xbb,0x75,0x5d,0xf6,0xbf,0x5e,0x0d, + 0xb6,0x5d,0xef,0x9d,0xe0,0x5d,0xd1,0xcf,0x7a,0xa0,0xa9,0x50,0xf6,0xbc,0x6d,0x15,0xbb,0xe5,0x0f,0x50, + 0x23,0x3d,0xe7,0x18,0xcd,0x55,0x60,0x00,0xad,0x23,0x7a,0xa2,0x84,0x79,0x6e,0x77,0xb7,0xf0,0x94,0x92, + 0xde,0xad,0xff,0x55,0x4e,0xaa,0x1d,0x9a,0xc1,0xbb,0xfd,0x58,0x6b,0xd6,0x68,0x27,0x34,0x3a,0xeb,0x85, + 0x5e,0x25,0xd2,0x16,0x9e,0x2a,0xbd,0x0c,0xd4,0x93,0x59,0xa4,0x8e,0x56,0x64,0x68,0x9b,0x66,0x27,0x49, + 0xfe,0x11,0xdb,0x80,0xc4,0x6d,0x5b,0x2a,0x2b,0x82,0x05,0x57,0x99,0x63,0x14,0x45,0xc4,0xe0,0xfa,0x4d, + 0xb9,0x87,0x52,0x92,0xb6,0x20,0x5c,0xc5,0x18,0x55,0xa8,0x9e,0x3c,0x9f,0xad,0xe4,0xf1,0x0d,0x21,0x31, + 0xc7,0xf7,0x8e,0xa9,0x9d,0x1f,0xad,0xf0,0xd9,0xa0,0x34,0x97,0x6d,0x0c,0xf8,0x8c,0x5e,0xbd,0x4b,0x72, + 0xe1,0x51,0x10,0x81,0xef,0x19,0x4e,0xaf,0xf0,0xf8,0xe2,0x28,0xeb,0xb1,0x85,0x98,0xa9,0x1c,0xc3,0x5a, + 0xd5,0x23,0x81,0x5e,0x5f,0x6b,0xaf,0x91,0x39,0x24,0xfa,0xfe,0x14,0x71,0x95,0xd1,0x6d,0x0b,0x64,0x7c, + 0x31,0x28,0x51,0x97,0xb7,0x3d,0xdd,0x07,0xf1,0xc4,0x77,0x10,0xc9,0x1e,0xca,0x3c,0x8c,0x8a,0x5c,0x3e, + 0x2c,0x3b,0x49,0x1c,0x51,0x3c,0x9d,0x12,0xd9,0xc1,0x8c,0xb4,0xed,0xfa,0x12,0xa2,0x6c,0xac,0x50,0x1e, + 0x71,0x11,0x19,0xa5,0x89,0x44,0xa7,0xd1,0xba,0x03,0x04,0x52,0xc5,0x96,0xc7,0xf1,0x35,0x7d,0xd8,0x4a, + 0x7b,0x90,0xf2,0xdc,0xe0,0x31,0xec,0xbc,0x34,0x4e,0xd2,0xd0,0x1c,0xbd,0x31,0x19,0xdd,0x38,0x1f,0x8d, + 0x2a,0x38,0x93,0xc8,0xf4,0x07,0x3c,0xba,0xae,0x2e,0xd3,0x03,0xbb,0x74,0x06,0x3a,0x16,0xbb,0x19,0x36, + 0x57,0x16,0x1a,0xc0,0x37,0x05,0x2e,0xe7,0x96,0x72,0xad,0x25,0xb4,0xc4,0x6b,0x23,0xaf,0xf0,0xe8,0xb5, + 0x91,0xf5,0xa4,0x1b,0xf7,0x01,0x2d,0xd9,0x25,0x20,0x84,0x4e,0xbd,0xe1,0x9a,0xba,0x94,0x50,0x82,0xed, + 0xbf,0x99,0x6d,0x94,0xe4,0xf6,0xa0,0x33,0xfd,0xca,0xac,0x2e,0x30,0x99,0xb9,0x04,0xda,0x18,0x53,0x80, + 0x15,0xde,0xe7,0xd1,0x72,0xa2,0x17,0xf2,0x14,0x57,0x54,0x1c,0x5a,0xc9,0x77,0x24,0xc9,0x4c,0xbb,0x27, + 0x53,0x93,0x54,0x51,0xdc,0x5e,0xbd,0x8b,0x54,0x15,0x24,0x0e,0xd2,0x4b,0xa4,0x63,0xf8,0xf9,0x82,0x7e, + 0x19,0x31,0x99,0x37,0x7c,0x1f,0xf9,0xa1,0x71,0xdd,0x3e,0x49,0x7f,0x71,0x2a,0x3e,0x70,0x78,0x66,0x79, + 0xf3,0x98,0x2a,0x69,0x74,0x87,0xef,0xd1,0x9a,0x53,0x56,0x0f,0x42,0xa3,0xb3,0x38,0xe7,0x5c,0x55,0x3f, + 0x8e,0x75,0xa0,0x06,0x4e,0x22,0x83,0xf1,0x39,0xd9,0xc0,0x9b,0xb9,0x60,0x3c,0xd7,0xa2,0x5b,0xe2,0x92, + 0x6b,0xd9,0x25,0x8f,0x8f,0xe8,0x0e,0xc0,0x8f,0x27,0xe1,0x4b,0xa7,0x54,0x87,0x60,0x19,0x29,0x68,0x0d, + 0xc3,0xbd,0xb3,0xc0,0x73,0x69,0x94,0x6c,0x7c,0x86,0x64,0x00,0x4e,0x35,0xf6,0x2e,0x42,0x75,0xf3,0x7d, + 0x0b,0xbb,0x3a,0x8a,0x4c,0x73,0x8c,0x3f,0x8b,0xf3,0x4e,0xed,0x1f,0x45,0x2c,0xf3,0xb3,0xf5,0xcb,0xb9, + 0x8e,0x2f,0xb8,0xbb,0x3f,0x93,0xeb,0xa4,0xe3,0x66,0x1a,0x7c,0x51,0x2d,0x58,0xc7,0x8a,0xab,0xca,0xa6, + 0xe5,0x5a,0x23,0x90,0x7e,0x83,0xc2,0xab,0x66,0xcf,0x79,0xd4,0x7e,0x44,0x9f,0x43,0xf4,0x4d,0x51,0x8a, + 0x8b,0xef,0x17,0x31,0x5a,0x67,0xd6,0xc6,0x33,0x67,0x93,0xec,0x6b,0x1f,0x0f,0x04,0x73,0x59,0x29,0xa0, + 0xe0,0x99,0x53,0x35,0xf0,0xcb,0xe3,0x64,0x75,0x0d,0xa7,0x54,0xdb,0xd3,0xa3,0x78,0x35,0xf5,0xac,0x51, + 0x73,0x52,0x1a,0x8e,0x38,0xa2,0x07,0x59,0x86,0x4a,0x62,0xc2,0x7d,0xd9,0x75,0x5d,0x8a,0x95,0xe5,0x80, + 0x14,0xb0,0xac,0xed,0x22,0x10,0x29,0x85,0xf2,0x39,0xe9,0x16,0xe4,0x24,0xa9,0x28,0xf4,0xc2,0x42,0xff, + 0xcc,0xa6,0xcf,0xab,0x8a,0x0d,0xd2,0x31,0x90,0x2a,0x2f,0xde,0x5c,0xc7,0xd6,0x50,0x9a,0x01,0xfb,0xa4, + 0x00,0x90,0xf1,0xfc,0xe3,0xc2,0xb4,0x50,0x7e,0x8a,0xda,0xff,0xb1,0x52,0xa1,0x6c,0x90,0x24,0xb1,0xc7, + 0xa3,0x31,0xfc,0x69,0xd8,0xa4,0xa7,0x27,0xdb,0x86,0x6c,0x12,0x3a,0xcf,0x76,0xb1,0x7d,0xa7,0xc4,0x89, + 0x5e,0xbe,0x90,0x23,0x79,0xb6,0x94,0x28,0xf7,0x51,0x49,0x49,0x94,0x84,0xa2,0x8d,0xcb,0x8f,0x86,0xac, + 0x80,0x3a,0x62,0x5e,0x60,0x84,0x0c,0x48,0x22,0x27,0xd1,0xd5,0xea,0x09,0x57,0x82,0xb7,0xf8,0x77,0xf0, + 0x94,0xf5,0xdb,0xd0,0xb3,0x76,0x89,0x08,0x60,0x7f,0xc8,0x63,0xc6,0x13,0x51,0xcc,0xa3,0xc0,0xda,0x36, + 0x95,0x5b,0xdb,0x93,0x6b,0xe3,0x38,0x90,0x88,0x8a,0x23,0x9b,0x08,0x46,0xfa,0xf0,0xd5,0xcf,0x35,0x14, + 0x37,0xea,0xe5,0xfc,0x54,0x08,0xe4,0xac,0x11,0xda,0x13,0xd9,0xa2,0x8d,0x90,0xf2,0x12,0x7e,0xee,0x43, + 0x86,0xd0,0xb7,0x47,0x30,0x7a,0xb4,0x81,0x9b,0x3e,0xf5,0xfb,0x57,0xd0,0xff,0x84,0x22,0x3d,0x47,0x19, + 0x18,0xd4,0x92,0x05,0x1e,0xdd,0xdd,0x1d,0x4d,0x1b,0x64,0xef,0x7c,0x6d,0x0d,0xb8,0xa7,0x8d,0xc6,0xeb, + 0xab,0x4f,0x39,0x7b,0xd5,0x46,0x93,0x41,0x82,0x19,0xc8,0xcf,0xcb,0xdf,0xcf,0x31,0x20,0xa9,0x99,0x10, + 0x67,0xed,0xaf,0xa7,0x40,0xd5,0x27,0xc7,0xa3,0x60,0x7c,0x65,0xb7,0x6d,0x7b,0x9f,0x39,0x60,0x7b,0x2f, + 0x57,0xf5,0x1f,0x95,0x5c,0x76,0xef,0x11,0x26,0xa1,0x1e,0x9d,0x70,0xb3,0x84,0x66,0xd2,0xb7,0x26,0x64, + 0xb1,0x4f,0xba,0xde,0x93,0xb5,0x17,0xf4,0x03,0xe0,0x43,0x7d,0xd5,0xe8,0x22,0x49,0x4c,0xa3,0x1a,0x9a, + 0x94,0xae,0xae,0x1f,0x87,0xb3,0x49,0xf6,0x2d,0x5f,0x9c,0x45,0xbd,0x0d,0x89,0x76,0x28,0x96,0x90,0x17, + 0xbe,0x85,0x1a,0x26,0x9b,0x9c,0x15,0x8d,0x3e,0x8c,0x29,0x82,0xf9,0xb9,0xfd,0x9c,0x53,0x4b,0xaf,0xff, + 0x71,0x32,0xf4,0x00,0xf8,0x41,0x8c,0xb4,0xe3,0x43,0x3c,0x5c,0xb9,0x61,0xca,0xce,0x2e,0x48,0x85,0xf8, + 0xc3,0x3d,0xfc,0xc9,0xab,0xf5,0x62,0xf0,0x78,0xed,0xdc,0x12,0xfb,0xbb,0xf7,0x83,0xdf,0xa2,0xff,0xf1, + 0xc9,0x16,0x39,0x99,0x3e,0xe1,0xfc,0x3d,0xef,0x30,0xdd,0xf0,0x4e,0xad,0x82,0x25,0xe3,0x0c,0x68,0xce, + 0xa3,0x34,0x96,0x30,0xa3,0x17,0xdf,0x09,0x4e,0x63,0xbb,0x6b,0xed,0x50,0x04,0xef,0x3c,0xd5,0x1a,0xc2, + 0x23,0x22,0x0d,0xe8,0x00,0x75,0x40,0x8b,0x6e,0xb0,0x9d,0x1a,0x64,0x64,0x8f,0xab,0x62,0xea,0xc2,0x32, + 0x0b,0xaf,0x11,0xa3,0xd6,0x8f,0xa1,0x7e,0xc3,0xe7,0xd3,0xc5,0xbc,0x1c,0xbe,0x36,0x17,0xcd,0x97,0x83, + 0xf1,0xe6,0x24,0x11,0xcb,0x64,0xa0,0x5a,0xb1,0x74,0x93,0xec,0x15,0xa5,0x58,0x58,0x5f,0x13,0x98,0x82, + 0xe4,0x58,0x21,0x8a,0x2d,0x8f,0xca,0x02,0x3a,0x4d,0x3d,0xdc,0xe5,0x2d,0xb0,0x3d,0x64,0xd2,0x67,0x5c, + 0x0f,0xac,0xfc,0x4e,0xf0,0xe7,0xe7,0xf1,0x67,0xc1,0x81,0x48,0x35,0xfa,0x7d,0x8b,0x54,0x5d,0xe2,0x7f, + 0x17,0x5f,0x23,0xbb,0x46,0x09,0x7a,0xca,0x13,0xba,0x85,0x97,0xaa,0x15,0x02,0x0d,0x75,0x26,0xad,0x9f, + 0x34,0x21,0xc8,0x7f,0x79,0xf7,0x25,0x8c,0x73,0x02,0x51,0x49,0xc9,0xde,0xb2,0xda,0x6a,0xe8,0x39,0x28, + 0x66,0xe6,0x7b,0x03,0x02,0x71,0xd1,0xa7,0xf3,0xe6,0x64,0x45,0xed,0xf3,0x05,0x28,0xc7,0x6f,0x6f,0x4e, + 0x40,0x2f,0xe4,0xfb,0x38,0xfb,0x9c,0xf1,0x35,0xc3,0x1b,0x90,0x33,0xec,0xa3,0xd0,0x5c,0x58,0xc6,0x6b, + 0x98,0x91,0x18,0x24,0x46,0xaf,0x80,0x34,0x35,0xbc,0x52,0x14,0x93,0xb4,0x90,0xd4,0x94,0xf4,0x1b,0x8b, + 0x54,0xa3,0x96,0xd5,0xcb,0xfa,0xc6,0xbe,0xc9,0xff,0xc9,0x02,0xa3,0x3f,0x89,0x98,0x22,0xb4,0x71,0x3f, + 0x39,0x8f,0x1e,0x21,0xfc,0x05,0x6c,0x50,0x30,0x2c,0x8f,0xd7,0xa5,0xa0,0xb2,0x11,0xb7,0x64,0xc7,0xea, + 0x28,0xd0,0xc6,0xe9,0x11,0x72,0xf8,0x45,0x34,0x31,0xd1,0x8d,0xd2,0x5a,0x5b,0xd4,0xc1,0x3c,0x9e,0x85, + 0x5d,0x1a,0xd1,0xd0,0x4f,0x7a,0x7a,0x44,0x9e,0x08,0xa3,0xa9,0xbf,0x3c,0x4a,0xa5,0xba,0x38,0x46,0x4a, + 0x6a,0x9d,0xc7,0x99,0xab,0x54,0xef,0x80,0x75,0xd9,0x5c,0xae,0x32,0x13,0x9f,0x15,0x80,0xca,0x40,0x1b, + 0x8a,0xda,0x39,0x5e,0x3e,0xd7,0x29,0x7f,0xfd,0xda,0x96,0xfa,0xad,0xe0,0x72,0x75,0x1d,0x1b,0x21,0x2f, + 0x63,0xcf,0x9a,0xe9,0x1d,0xf9,0x81,0x6b,0x47,0x59,0xb4,0x5a,0x9e,0x0c,0xc7,0x4a,0x6e,0xc6,0x69,0x06, + 0xb3,0x1a,0x31,0x7f,0xd0,0xbd,0x27,0x27,0x51,0xca,0x78,0x02,0x17,0x72,0x92,0x1e,0x7c,0x3b,0x15,0x0f, + 0x55,0x83,0x5b,0x12,0x61,0x7d,0x4e,0xf8,0x9d,0xf2,0xc6,0x3b,0xec,0x33,0x03,0x88,0xe7,0x32,0x61,0xe5, + 0x96,0x6c,0x5b,0xf6,0xaf,0xc0,0xf0,0x5a,0x3c,0x78,0xe6,0x02,0xfb,0x2a,0xcd,0x1c,0xdf,0x2b,0x21,0x03, + 0x69,0x9f,0x86,0x30,0x4d,0x9e,0x08,0xdf,0x43,0xed,0x2e,0x77,0x51,0xdf,0x68,0xa1,0x08,0x38,0xe2,0x09, + 0x0e,0x15,0x36,0xac,0x12,0x30,0xea,0xc9,0xda,0x3d,0x9b,0xfc,0x26,0x48,0x2e,0xd9,0x54,0xe4,0x89,0x68, + 0xee,0xf1,0xcc,0x5a,0x43,0x39,0x14,0xc1,0x16,0x70,0xbc,0x34,0xab,0xd5,0xca,0xda,0xd4,0xe9,0x0c,0xea, + 0xfb,0x5f,0x30,0x87,0x5e,0xf1,0x38,0xfb,0x40,0xf2,0x53,0xe9,0x72,0x62,0xfa,0xc9,0x8a,0x96,0xc2,0x5a, + 0xb6,0xaa,0x3b,0x96,0x79,0xac,0x95,0xea,0x17,0x07,0x3c,0xd6,0xe3,0x59,0xc2,0x31,0xb3,0x84,0xad,0xb2, + 0x1e,0xa8,0x3d,0x1a,0xc0,0xb6,0x00,0x35,0x2e,0x42,0x2c,0xbd,0x77,0x13,0x93,0x10,0x2d,0xd0,0x27,0x2a, + 0xe7,0x6b,0x4c,0x3d,0xd7,0xc9,0x18,0xd6,0xf2,0x9a,0xcc,0xdc,0xe1,0xcf,0x20,0x87,0x98,0x07,0x7b,0x08, + 0x80,0xa7,0x55,0xda,0xb8,0xef,0xd4,0xa6,0xb3,0xba,0x06,0xa5,0xb4,0x45,0xfd,0x72,0x7c,0xbf,0xc6,0x7c, + 0x83,0x66,0x06,0x3e,0x90,0xfe,0x64,0xba,0x3d,0xdc,0xe6,0x18,0x22,0xcc,0x08,0x3f,0x70,0x70,0x50,0x1a, + 0x60,0x93,0x76,0x31,0x95,0x92,0x00,0x1a,0x0c,0x73,0x96,0x74,0x34,0x62,0x03,0xed,0x4f,0x2c,0x23,0xbf, + 0xfc,0x26,0xfa,0x7c,0x1e,0x5c,0x4b,0xed,0x05,0xe0,0x22,0x1d,0x28,0xa6,0xad,0x6a,0x59,0xad,0x68,0x39, + 0x5f,0x34,0xe4,0x30,0xaf,0x37,0x3f,0x64,0x1f,0xb7,0xcc,0x12,0x34,0x03,0x1f,0xaa,0x12,0xf0,0x6d,0xdb, + 0x1d,0xdc,0xaf,0xfc,0xd9,0x1b,0xbc,0x7c,0xfb,0x35,0x0c,0x69,0xb1,0xa0,0xfd,0xef,0xc5,0xb1,0x25,0xe4, + 0xb8,0x87,0x26,0x57,0x39,0xde,0x80,0x8d,0x9a,0x0c,0xaa,0xc5,0x6e,0x7a,0xe0,0xdd,0x93,0x3f,0x57,0x5d, + 0x9a,0xb5,0xe4,0x04,0x73,0xe0,0xc3,0x7f,0xcc,0x9f,0x83,0x08,0x44,0xc2,0x85,0x43,0x88,0x33,0x2d,0x83, + 0x88,0x24,0x9d,0xec,0xb0,0xe7,0xec,0xab,0xe8,0x3e,0x61,0x12,0x3f,0xa1,0x05,0xf5,0x4c,0x4c,0xc1,0x1c, + 0xa9,0x5e,0xe5,0x55,0x72,0xec,0xd2,0xb5,0xc5,0xde,0x9f,0x40,0x48,0x59,0x2b,0x12,0x56,0x2f,0xa6,0x0b, + 0x15,0xb0,0x31,0x79,0x53,0x11,0xf7,0x2e,0xd8,0x2a,0x6a,0xf8,0x15,0xbe,0xa8,0x36,0x48,0x81,0x0e,0x88, + 0x20,0xdf,0x52,0xa4,0x52,0x27,0xfd,0x74,0xa8,0x3a,0x96,0xd5,0x0f,0x36,0x85,0x6c,0x21,0xb2,0xb7,0x96, + 0x1d,0x72,0x09,0xd0,0x19,0xa1,0xdb,0x23,0xd7,0x9f,0x80,0x22,0xfb,0x51,0x6b,0xae,0xfc,0x01,0xe5,0x34, + 0x44,0x0f,0xa0,0x23,0xcf,0x8d,0x3e,0x15,0xe6,0x94,0xe5,0x89,0x3d,0xb2,0x60,0xed,0xe8,0x50,0x7a,0x4b, + 0x0a,0xea,0x97,0xec,0x98,0x0a,0x8d,0x8d,0xb9,0x72,0x9c,0xbe,0x41,0x34,0xa8,0xf5,0x80,0x07,0x84,0x68, + 0x1d,0x01,0xb0,0x42,0x5c,0xdc,0x3b,0xb0,0x1b,0x8a,0x7b,0x78,0x51,0x14,0xbc,0x42,0x56,0xa4,0x3e,0x2f, + 0x27,0x1b,0xa3,0xb8,0x15,0xe8,0x0e,0x08,0x3f,0x29,0x38,0x23,0xb5,0x48,0x08,0xa4,0x50,0x55,0xbb,0xbb, + 0x14,0xa4,0x7a,0x29,0x23,0x5a,0x9e,0x8d,0x6b,0xbc,0xcf,0x31,0x32,0x95,0x53,0x5d,0xa8,0x66,0x41,0xe8, + 0xb8,0xfe,0x45,0x48,0x38,0x4a,0xa8,0xb8,0x33,0xd5,0xf9,0xb2,0x52,0x1a,0xc4,0x34,0x3c,0xae,0x9f,0x91, + 0x98,0x47,0x08,0x89,0xf8,0xe3,0xdd,0x8d,0xa1,0x29,0x95,0x5a,0xaa,0x5f,0xf4,0xa1,0xae,0xcd,0x01,0x92, + 0x95,0x22,0xef,0x09,0x08,0xf5,0x8c,0x06,0xaf,0xb3,0x87,0xc9,0xf8,0x0a,0xaf,0x89,0x94,0x46,0x4d,0x65, + 0xa0,0xf0,0x26,0xcc,0xb6,0xa3,0xa2,0x61,0x43,0xa5,0xfb,0xd8,0xbf,0x0d,0x80,0x93,0xec,0x52,0x84,0xed, + 0x81,0x4f,0xdd,0x2d,0xcb,0xac,0x24,0xfc,0x33,0xef,0xb4,0x79,0x9d,0x2f,0x4e,0x10,0x4d,0xa5,0x85,0x43, + 0x7d,0x09,0x02,0xc1,0x33,0x70,0xa4,0xb1,0x97,0x15,0x37,0xba,0x90,0x82,0x05,0x27,0x21,0x89,0x17,0x83, + 0xd2,0xd3,0x3a,0xd0,0xf2,0xbb,0x8c,0x0e,0xfa,0xc1,0xc1,0x93,0xce,0xf3,0x47,0x85,0x89,0x55,0x69,0xc0, + 0x48,0x47,0xf3,0xbd,0x2a,0xc1,0xb9,0x7a,0x5b,0xf4,0xa3,0x8b,0x32,0x1d,0xa2,0x60,0x86,0x19,0xf6,0xfc, + 0xd8,0x44,0xf4,0x74,0x85,0xbf,0xab,0xba,0x04,0x22,0x37,0xf1,0xf5,0xa4,0x50,0x1b,0x54,0x2b,0xff,0x37, + 0x1c,0xe9,0xdb,0x8f,0x0d,0x3f,0x1d,0x7b,0x38,0x85,0x4e,0x8e,0x82,0x67,0xf4,0x5f,0xf5,0x25,0xdc,0x84, + 0xc7,0x98,0x18,0xae,0xe1,0x28,0xc0,0xca,0x7e,0xcb,0xb3,0xfc,0x68,0x82,0xf9,0xe8,0x0b,0x90,0x9a,0xc9, + 0x3b,0xde,0x27,0xbe,0xc1,0x16,0x9e,0x18,0x09,0x19,0x98,0xc1,0x84,0xf0,0xc5,0x68,0x85,0x76,0xd3,0x01, + 0x9c,0x89,0x76,0x08,0x43,0xb7,0xfb,0x7d,0xa5,0x32,0x69,0xf5,0x09,0x41,0x4c,0x3b,0x08,0x63,0x90,0x2f, + 0xb4,0x07,0x51,0xbc,0x31,0x5b,0x39,0x42,0x1d,0x5d,0x14,0x5a,0x60,0x7f,0xcc,0x1d,0xb0,0x4d,0xc4,0x39, + 0xb5,0xc9,0xeb,0x4a,0x06,0x0b,0x33,0x15,0xc2,0x95,0xfa,0x44,0x2a,0x85,0x47,0xce,0xb2,0x3e,0xd1,0x8f, + 0x25,0x33,0xcc,0x24,0xae,0x27,0x62,0x1f,0xe6,0x97,0x75,0xc4,0xa4,0x79,0xdf,0xe0,0x4b,0x2a,0x83,0x24, + 0xf3,0x06,0xd7,0x27,0x12,0x54,0x1d,0xf8,0x8c,0x12,0x3d,0x13,0xc8,0x1d,0x5c,0x96,0xfe,0x3a,0x24,0x9c, + 0x1a,0x79,0x60,0x2c,0x02,0x53,0xa0,0xde,0x22,0xdd,0xd9,0x10,0x0b,0x52,0x50,0xa9,0x93,0x89,0xe9,0xf8, + 0x50,0x9a,0xde,0xab,0x53,0x6f,0xa3,0x30,0x4d,0xda,0x4f,0x38,0xf9,0x02,0x9f,0x8d,0x57,0x91,0x6b,0xb5, + 0x26,0xe5,0xa0,0x03,0x0d,0x04,0x62,0xc3,0x7d,0xe0,0x76,0xcb,0x54,0x95,0x79,0x93,0x87,0x97,0x76,0x00, + 0x6c,0x3d,0x4f,0x09,0xff,0x6e,0x50,0x82,0xc2,0x3c,0x10,0xc2,0x0c,0xf0,0x15,0xa8,0xdd,0x20,0x34,0x6c, + 0xba,0x61,0x6e,0x8c,0xd9,0xc4,0x5e,0xe6,0x0f,0xa6,0x55,0x32,0x22,0x0a,0xfc,0x93,0x06,0x15,0x95,0xb7, + 0x5e,0xee,0x0b,0xcb,0x99,0x01,0x11,0x66,0x15,0x02,0x0c,0x1d,0x1e,0x26,0x36,0x36,0x94,0x99,0x2f,0x81, + 0x5b,0x82,0x9b,0x79,0x95,0xc1,0x92,0x63,0x3b,0xcd,0x6f,0x2d,0x74,0x12,0x99,0x5c,0x34,0xdb,0x8b,0x8d, + 0x14,0x11,0x31,0xb5,0x57,0xcc,0xf2,0x1e,0x40,0xb2,0x90,0xc0,0x0a,0x0c,0x98,0xa9,0x00,0x6f,0xb3,0x3e, + 0x3f,0x73,0xfe,0x08,0x4d,0xb1,0x34,0x29,0x4b,0xe7,0x39,0xeb,0xa6,0xed,0xc8,0x65,0xe2,0xf6,0xae,0xbf, + 0xc0,0x06,0x3d,0xa3,0x9f,0xa0,0x24,0x79,0x04,0x6c,0x1a,0x79,0x31,0xa1,0x1f,0x09,0x53,0x9f,0x8c,0x9d, + 0xf3,0x36,0x20,0x99,0xa3,0xc9,0x42,0x37,0xf1,0x04,0xc9,0x3d,0x1a,0x0d,0x95,0x1b,0x19,0xde,0x37,0x89, + 0x98,0xeb,0x4b,0x55,0xad,0xa3,0x9e,0xfe,0x60,0xec,0x97,0x24,0xc2,0xd4,0xc8,0xbd,0x49,0x68,0x49,0x39, + 0xa0,0x1a,0x5c,0x89,0x00,0xbb,0xbd,0xe0,0x89,0x27,0xc7,0x36,0xb6,0x1d,0x65,0xef,0xf3,0x64,0xef,0x59, + 0x3f,0x2e,0x21,0x7d,0x39,0x1c,0x05,0x57,0x58,0xaf,0xca,0x95,0x30,0x71,0x30,0x02,0xc8,0x5b,0xb0,0xfa, + 0x1a,0xc1,0x86,0xbf,0x72,0x7b,0x9b,0x30,0x44,0x82,0x7d,0x6a,0x13,0xe3,0x7e,0xb3,0xf4,0x92,0x44,0x0a, + 0x7b,0x6e,0x6b,0x17,0x5e,0x49,0x6c,0x85,0x19,0xab,0x4f,0x4c,0x71,0x2d,0x14,0xf1,0x7b,0x74,0x7e,0x12, + 0x01,0x07,0xec,0x2b,0x18,0x60,0x8e,0x1c,0x3d,0x13,0xfd,0x8c,0x62,0x61,0xdd,0xb8,0x14,0x03,0x00,0xfe, + 0x1c,0xd1,0x7f,0x99,0xa3,0xdc,0x99,0x96,0x59,0xc9,0xb7,0x77,0x6b,0xb8,0xeb,0xc7,0x0c,0xcb,0xe8,0x2f, + 0x18,0x52,0x09,0x7b,0x56,0x80,0x32,0x48,0xe3,0x2c,0x83,0x67,0x0c,0x8f,0xeb,0x84,0x5c,0x9b,0x5b,0x2e, + 0x28,0x19,0xfb,0x7d,0xde,0xe1,0xb8,0xde,0x76,0xa8,0xf8,0xc4,0x32,0xa1,0x17,0xd6,0x9b,0x89,0x79,0xd2, + 0x70,0x7c,0x50,0x11,0xa8,0x66,0x16,0x5e,0xac,0xbc,0xbb,0xf9,0x17,0xea,0x6a,0x68,0xb4,0x7e,0xe1,0x92, + 0xd5,0x8b,0xca,0x41,0xdd,0x16,0xe9,0xf1,0xcb,0xa9,0x3b,0xd2,0xe3,0x3f,0xfe,0x25,0xc0,0x78,0x67,0xaa, + 0xf6,0x06,0x5f,0xfc,0x6f,0x22,0x88,0xf1,0xd3,0x85,0xe8,0x97,0x96,0xc0,0x38,0xbb,0x94,0xee,0xac,0xf7, + 0x42,0xe3,0x2f,0xb7,0x7f,0x3c,0x00,0xcb,0x2e,0x96,0x63,0x56,0x24,0x79,0x73,0x36,0x4d,0x71,0xb5,0x57, + 0xd0,0x3f,0xef,0xec,0x06,0x54,0x05,0xdb,0x84,0xd6,0xaa,0x9d,0xab,0xaf,0x30,0x09,0xa7,0xea,0xab,0x0a, + 0x80,0x8c,0x0c,0x93,0x64,0xc5,0x4c,0xf9,0xde,0xec,0x20,0xe8,0x57,0xc1,0x5f,0xd5,0x05,0xf6,0x06,0x80, + 0xd4,0x1b,0xb2,0xfe,0x01,0xbf,0x92,0xea,0xcf,0x1c,0xfa,0x5b,0x29,0x2c,0x61,0xde,0x65,0x45,0xd7,0x13, + 0x8a,0x19,0xec,0x52,0xea,0xd3,0xf9,0x22,0x96,0xf5,0xa3,0x69,0x85,0x39,0x83,0xbe,0x04,0x57,0xd3,0x66, + 0xd0,0x81,0x6a,0xf4,0x63,0xa6,0x23,0x0e,0xe1,0x91,0xa2,0xe5,0x55,0x00,0xc0,0xf2,0x4b,0x1f,0xb2,0x4f, + 0xf4,0x17,0x3c,0xc3,0x2a,0xd2,0x24,0xdb,0xb7,0x09,0x0b,0xd3,0xb7,0xa4,0xa2,0xfd,0x80,0x7d,0xc9,0x90, + 0x92,0x44,0x13,0x30,0x38,0x91,0xf8,0xe4,0x3f,0x18,0x69,0x05,0x80,0xee,0x07,0x33,0xed,0xfd,0xab,0x3f, + 0xde,0x82,0x3f,0x6c,0xaf,0xe5,0x55,0x04,0xed,0xc1,0xdd,0xee,0x49,0xd2,0x95,0x52,0x4d,0x06,0x6d,0x6a, + 0x66,0xc7,0xa2,0xfb,0x0b,0xbb,0x89,0xa3,0x78,0x38,0x91,0x41,0x89,0x3a,0xdd,0x35,0xdb,0xee,0x3c,0xc5, + 0x22,0x0a,0x4d,0x2e,0xa4,0xef,0xff,0xf3,0xf1,0x23,0x26,0xc9,0x98,0xaa,0x89,0x6a,0xc0,0x7a,0xc8,0xf6, + 0x03,0x11,0xa0,0x0c,0x8a,0xf0,0x30,0xc8,0x81,0xf1,0x0d,0x5a,0x5a,0x7c,0x1f,0x45,0x23,0x45,0xea,0x10, + 0x4a,0x7f,0x0a,0x35,0xcd,0xe3,0xbf,0x60,0x7c,0xc3,0xf0,0xfb,0xeb,0xbc,0x9d,0xdc,0xd4,0x61,0x51,0x80, + 0x1c,0x2c,0xf7,0xc8,0x3d,0x79,0xf8,0x22,0x63,0x68,0xbb,0xcc,0xd2,0x3c,0xdf,0xb6,0x1b,0xbc,0xbe,0x91, + 0xd1,0x1c,0xaf,0x17,0x63,0x86,0x04,0x4a,0x4a,0x79,0x2a,0x03,0x89,0xc8,0xbe,0x59,0x01,0x29,0x0e,0xa0, + 0xd3,0x3d,0x9a,0x5c,0x29,0x93,0xe2,0x34,0xef,0x24,0xa6,0xc6,0x62,0x7b,0xa7,0xdd,0xda,0x0b,0x75,0x48, + 0x45,0x5c,0x07,0x09,0x30,0xba,0x6d,0xd2,0x75,0xfb,0x82,0x48,0xd1,0x21,0xeb,0x27,0xa6,0xa8,0xb5,0x5d, + 0x51,0x8d,0x72,0x4b,0x98,0xf6,0x4c,0xce,0xa8,0x0b,0x71,0x75,0x91,0xb7,0xd0,0x03,0x3c,0xca,0x6c,0xd4, + 0x58,0x27,0x4d,0xc2,0xff,0xda,0x40,0xc1,0xf9,0xe9,0xe1,0x3c,0xc1,0x7a,0x30,0xc9,0x41,0x6f,0x48,0xa9, + 0x67,0xf4,0x7a,0xb0,0xbd,0xa2,0x16,0xb4,0xcf,0xa1,0x9c,0x90,0xcf,0x30,0x64,0x0b,0x98,0xf5,0x2c,0xd7, + 0xe7,0xec,0x84,0x05,0xa0,0x75,0xca,0x9b,0x0e,0xdf,0xad,0x01,0x22,0xbc,0x7e,0x69,0xcb,0xf7,0x8c,0x52, + 0x85,0x0f,0xf1,0x0e,0xd4,0xad,0x34,0x41,0x03,0xab,0xbd,0xc3,0x4d,0xdb,0x9c,0x45,0x19,0x3d,0x9a,0x77, + 0xb8,0x02,0x30,0x8f,0x04,0x39,0xd8,0xca,0x8a,0x9e,0x72,0xaa,0x01,0x2e,0xe6,0x7a,0xba,0x88,0x31,0xc8, + 0x76,0xf9,0x14,0xe8,0xd6,0xc0,0xa2,0x59,0xfd,0x06,0x44,0x1d,0x63,0x62,0xf9,0x54,0x5d,0x1e,0x21,0xf4, + 0x93,0x28,0x7f,0xc0,0x10,0x80,0x20,0x7e,0x79,0x84,0xa3,0xe3,0xc1,0xf2,0x34,0x7a,0x0c,0xfb,0x83,0x09, + 0x6a,0x93,0x77,0xd6,0x15,0x2b,0x8e,0x0d,0x11,0x49,0x13,0x67,0xc5,0x9e,0x22,0xad,0x1c,0x83,0x98,0x58, + 0xd5,0xb3,0x72,0x36,0xb8,0x57,0x83,0x10,0x00,0x3c,0x0d,0x3b,0xd7,0x01,0xae,0x4b,0x07,0xc4,0x88,0x3e, + 0x75,0x21,0xe4,0x8b,0xca,0xa5,0xa7,0x4f,0x69,0x1e,0x3c,0x84,0x41,0xe9,0xf5,0x0a,0x0c,0x03,0x77,0xd0, + 0x1c,0x46,0x23,0x10,0x21,0xa2,0x8d,0xc8,0xbd,0xa1,0xba,0xc7,0xa2,0xa0,0xb4,0xc4,0x97,0x67,0x91,0x74, + 0x12,0xfd,0x9a,0x0d,0x32,0x80,0xed,0x64,0xa5,0x3e,0x4b,0x48,0xd9,0x38,0x0c,0x15,0x15,0xed,0x7f,0x00, + 0x30,0x11,0x1c,0xe9,0x0c,0x5c,0x8d,0xae,0x94,0x7d,0x9e,0x12,0xc0,0x59,0xde,0xd1,0x2d,0xf2,0x06,0x6e, + 0xfe,0x20,0xc9,0xab,0xe6,0xab,0x55,0xd4,0x7c,0xc8,0x84,0x20,0x46,0xc3,0x58,0xae,0xe3,0x8b,0x0e,0xb8, + 0x49,0x3c,0xe2,0xdf,0x4b,0xb4,0xdf,0x7d,0x75,0xe3,0x4c,0xd6,0x38,0x39,0x46,0xe3,0x2d,0x28,0x1d,0x16, + 0xe9,0xd5,0x1f,0xf3,0x0e,0x74,0x24,0x01,0x2e,0x60,0xdd,0x04,0x05,0xf0,0x5e,0x0d,0x29,0x31,0xec,0x22, + 0xf3,0x51,0x41,0xa9,0x14,0x5a,0xb5,0x21,0x7c,0xd0,0xbd,0x1e,0x8b,0xe7,0x91,0xe5,0x4f,0xbb,0xd2,0x70, + 0x1c,0xe3,0x08,0xfb,0x28,0x79,0x1a,0x61,0x36,0x49,0xa4,0x92,0xfb,0x33,0x30,0x99,0x60,0xe6,0x79,0xc0, + 0x3e,0x8b,0xed,0x6b,0x2d,0xe9,0x19,0xc2,0x3e,0x02,0x31,0xc0,0xd3,0x4a,0x6a,0xd3,0x92,0x84,0x4e,0xef, + 0xb6,0xd3,0x27,0x4a,0x9c,0xba,0xe0,0xc1,0x3a,0x5c,0xaa,0x5c,0x6f,0xfe,0x11,0x68,0xfa,0xb8,0x8b,0x22, + 0x6c,0x25,0x40,0xa6,0x05,0x9c,0x9b,0x06,0x91,0x07,0x58,0x88,0xfd,0x2b,0x0d,0x12,0xf4,0xa4,0xf2,0xf4, + 0xa1,0xa7,0xb6,0x3f,0x23,0xa9,0x79,0x7d,0x1f,0x7f,0x3e,0x48,0xd4,0x76,0x9d,0x94,0x47,0xdb,0x27,0x97, + 0x69,0xc9,0x5b,0x92,0xb8,0x9e,0xe1,0x38,0xe1,0x61,0xee,0x93,0xe9,0x4c,0xfc,0xef,0xf2,0xc5,0x8b,0xb8, + 0x7e,0xdb,0xbc,0xf3,0x00,0x49,0x01,0x41,0x14,0x45,0xb1,0x9b,0xe8,0xb9,0x4c,0xcb,0x2e,0xfc,0xf1,0x39, + 0x5c,0x17,0xc5,0xa4,0x81,0x4a,0x0f,0xaa,0xd4,0x9e,0xaf,0x0e,0x97,0x27,0x16,0x2a,0x17,0xe3,0x0f,0xb3, + 0xe2,0x9c,0xaf,0x91,0xc8,0x8a,0xd2,0xe2,0x56,0xd2,0xea,0x42,0x26,0xa2,0xa0,0x60,0x41,0x84,0xf5,0x1c, + 0x90,0x49,0x73,0x95,0xa9,0xda,0x9d,0x0d,0x5e,0xcd,0x3c,0xd1,0xa9,0xb4,0x12,0x31,0x9d,0x30,0xf5,0x12, + 0x76,0xbf,0x32,0x3c,0xa4,0xe8,0x3c,0xe5,0xce,0x7d,0xc1,0x18,0x52,0xc5,0xc1,0x91,0x18,0x61,0x6a,0x28, + 0xb6,0xb3,0x10,0x9c,0x5e,0xbd,0xe1,0x08,0xe5,0xa2,0x2b,0x7a,0xb8,0x01,0x95,0x17,0xbe,0xcd,0x3e,0x85, + 0xba,0xf5,0x49,0xb5,0xd9,0x0b,0x75,0x22,0xec,0x2a,0x5c,0x9d,0x27,0x0b,0x56,0x7f,0xe2,0x67,0x68,0xd0, + 0x64,0xc3,0x71,0x37,0x30,0xdd,0x6f,0x78,0x7d,0x62,0x46,0xf1,0x9d,0x44,0xd6,0x8d,0x1a,0xfb,0x94,0x61, + 0x5b,0xb6,0x20,0xc5,0x28,0xfb,0x70,0xb1,0xb6,0x34,0xb7,0x3c,0x7e,0x1b,0x73,0x08,0xee,0x60,0x67,0x0c, + 0x27,0xfe,0x66,0x89,0x94,0x07,0x7b,0x88,0x7a,0x7c,0x90,0x47,0x93,0xcc,0x1f,0xf0,0x1c,0x9e,0xfe,0x5e, + 0x5a,0xc0,0xbe,0x68,0x11,0xac,0x18,0xc3,0xfc,0xfa,0xfa,0x1e,0x99,0x91,0xb7,0x57,0xcf,0xcc,0x06,0xbf, + 0xae,0xd1,0x28,0xa9,0x6e,0xed,0x17,0x01,0x3e,0xd4,0x95,0xfe,0xb7,0xed,0x12,0x54,0x22,0xff,0x21,0x81, + 0x17,0x5e,0xbb,0xc6,0x06,0x0b,0xc0,0xb0,0x72,0xf3,0x12,0x33,0x4d,0x10,0x97,0x46,0xca,0x5b,0xf1,0xf5, + 0x3f,0xc8,0xab,0xeb,0x48,0x54,0x4f,0xda,0xe7,0xe8,0x27,0x90,0xfe,0x7a,0xfa,0x72,0xcb,0xf0,0xa4,0x92, + 0xd9,0x20,0xb8,0x01,0x0b,0x97,0x85,0x58,0x7b,0x56,0x4e,0xbf,0x32,0x0a,0x3d,0x27,0x05,0xc0,0x86,0xdf, + 0x80,0xa3,0x2f,0xf0,0x6c,0x72,0xea,0xf4,0x28,0x18,0x42,0xf4,0xaf,0x57,0x7f,0xff,0x0a,0x64,0x4f,0x3a, + 0x39,0x34,0x65,0xcb,0x06,0x57,0x9a,0xec,0xd1,0x86,0x6a,0x1f,0x4f,0xb4,0x92,0x22,0x47,0x92,0xd7,0xd4, + 0x6b,0xe4,0x61,0xf4,0xec,0x56,0xae,0xf5,0x1f,0x3f,0x09,0xc6,0x47,0x51,0x37,0xc8,0x16,0x80,0xe6,0x28, + 0xaa,0xb2,0x76,0x73,0xc0,0x6e,0xb6,0xbd,0x03,0xed,0x84,0x5b,0xc9,0x02,0xae,0xd5,0xba,0xd3,0xc5,0x3a, + 0x53,0x19,0xcb,0x24,0x88,0xab,0x31,0x33,0xf5,0x76,0x67,0x17,0x5d,0xa7,0xb5,0x54,0x9d,0x7a,0x7b,0x36, + 0x05,0x0e,0x40,0xa4,0x17,0xa5,0x29,0x2b,0x85,0x9e,0x6a,0xb5,0x23,0xf0,0x1c,0x4e,0xa2,0x05,0xea,0x12, + 0xd0,0xc8,0x64,0x5f,0xdb,0xf8,0x1d,0x1f,0xd1,0x6a,0xac,0x9d,0xe4,0xf5,0x68,0x34,0xfb,0x7c,0xfd,0x72, + 0x26,0x32,0x83,0x57,0xeb,0x7a,0x12,0x5f,0x8d,0x08,0x8f,0xeb,0x43,0xe5,0xf5,0x0d,0x70,0xaf,0xea,0x19, + 0x8a,0xf5,0x8b,0x74,0xac,0x24,0x62,0x7d,0xc8,0x49,0x9c,0x5e,0x61,0xaf,0xde,0x77,0xaa,0x40,0x1b,0x0a, + 0x4a,0xd6,0xa1,0x60,0x49,0x7d,0xfa,0x2a,0x3a,0xed,0xcc,0xec,0x9a,0x62,0x42,0xbe,0x38,0x8b,0xf8,0xe8, + 0x2f,0x2d,0x26,0x9a,0xc1,0xb7,0x6a,0x53,0xa7,0x95,0x17,0x09,0x88,0xee,0x68,0xde,0x20,0xeb,0x5b,0xb8, + 0x9b,0xf4,0xcf,0xe7,0x3a,0x80,0x97,0x16,0x39,0x36,0x24,0x37,0x53,0xf9,0x33,0x69,0xb9,0xf9,0x28,0x33, + 0x3d,0x63,0xf0,0xfb,0xa9,0xb0,0xab,0xae,0x51,0xcd,0xb5,0x9e,0xa4,0xd9,0xf1,0x92,0xee,0xb2,0xab,0x1f, + 0xb0,0x3e,0x0b,0xb3,0x52,0x73,0x65,0xac,0xc4,0x20,0xf5,0xeb,0xd7,0x2b,0x24,0x2b,0xc4,0x08,0xdd,0x8c, + 0xb2,0x63,0x1b,0x83,0xe5,0xe0,0xad,0x3f,0xc8,0x59,0x52,0xb1,0x40,0x81,0x3b,0xb8,0x1d,0xde,0xe1,0xac, + 0xf3,0xba,0x46,0x24,0x20,0x23,0xfd,0xad,0x2c,0x04,0x94,0x01,0x06,0xbe,0x39,0xa5,0xdf,0x29,0x8b,0x45, + 0xda,0x94,0x6c,0xba,0x9f,0x60,0x91,0xef,0xdb,0x02,0x08,0x45,0x9f,0x60,0x63,0xa2,0xfc,0xe6,0x42,0x88, + 0xda,0x42,0xa4,0xe9,0xa7,0xe1,0x48,0xfa,0x9c,0x97,0x41,0x63,0x5e,0x1f,0xc9,0x2f,0xa8,0x6b,0x62,0x24, + 0x92,0x18,0x3e,0xd2,0x13,0x6a,0x24,0x92,0xb0,0x23,0xf2,0x3c,0xbc,0x6a,0xee,0xf9,0xb8,0xf9,0x5e,0x0e, + 0xa6,0xe4,0xd0,0x5e,0xf3,0x93,0xed,0x5d,0xc3,0xd7,0xb6,0xc0,0x0b,0x62,0x5f,0x66,0xaa,0x17,0xe0,0xbe, + 0xb1,0x21,0xa2,0x2c,0x1e,0xc5,0x6d,0x55,0x88,0x31,0x6a,0x99,0x6f,0xcc,0xec,0xa9,0xc1,0x83,0xf2,0xba, + 0x60,0x55,0xc5,0x3c,0x37,0x49,0xec,0x43,0xfa,0xe0,0x5e,0x70,0xf9,0x41,0x89,0x08,0x93,0x94,0x7d,0x07, + 0xe9,0xaf,0xe0,0x71,0xf0,0x37,0xe4,0x7a,0x21,0xf8,0x95,0x1e,0x6a,0x5b,0xac,0x67,0xc0,0xd6,0xe2,0x81, + 0x3f,0x19,0xdf,0xff,0x08,0x59,0xc1,0xfa,0xea,0xbc,0x49,0x5a,0x3f,0xd5,0x58,0x5d,0xec,0x61,0xb7,0x5e, + 0x5c,0x0d,0x12,0xaf,0x48,0x73,0x00,0x72,0xe9,0x17,0x48,0xdd,0x3e,0x05,0x82,0x6b,0xf3,0xa9,0xc3,0x9d, + 0xe7,0xb3,0x7a,0xfb,0xa1,0xcb,0x1f,0xe9,0x38,0xfa,0x69,0xb1,0xaa,0xdd,0x2d,0x84,0xa8,0xe5,0x69,0x93, + 0x17,0xa3,0x49,0x3d,0x48,0x07,0xf5,0x28,0xc6,0x4d,0xa8,0x1b,0x25,0xc2,0xfc,0xcb,0xce,0x42,0xb6,0x88, + 0x47,0x24,0xef,0xf3,0xc2,0x3a,0x29,0x16,0xb2,0x55,0xd1,0x25,0xcd,0x44,0x51,0xeb,0x52,0x6c,0xd8,0x7b, + 0xc0,0xcb,0x71,0x7d,0x10,0xe5,0xe5,0x62,0xfc,0xf4,0xb4,0xf6,0x9e,0x3c,0xba,0x5f,0xab,0x00,0xa1,0x84, + 0x06,0xc0,0x9c,0x26,0x99,0x32,0x38,0x0e,0x4d,0x56,0xe6,0x0d,0xd8,0x40,0x3e,0x59,0x2e,0x65,0x1d,0xaf, + 0xe8,0xe4,0xd0,0x98,0xee,0xc1,0xc2,0x75,0x7e,0x13,0xe3,0xe1,0x12,0xcd,0x2a,0x1a,0xd7,0x40,0xd7,0xbf, + 0xf3,0x8e,0xdf,0x05,0xa5,0x67,0xcd,0x06,0x84,0x12,0xeb,0x57,0x23,0x41,0x49,0xf5,0x97,0xcf,0xb5,0x7f, + 0x83,0x06,0xd3,0x11,0x9a,0x9a,0x2f,0x97,0x8e,0x9e,0x97,0x93,0x0d,0x3e,0x2f,0x97,0xf1,0xdf,0xc5,0xc9, + 0x4a,0x1d,0x56,0x94,0xf8,0x6a,0x47,0x36,0x76,0xa4,0xfe,0x75,0x7c,0xe4,0x4c,0x22,0x13,0x5d,0xdd,0x77, + 0x82,0xd3,0xbc,0x5c,0xcf,0x3e,0x7b,0x6d,0xf9,0x0f,0x07,0x70,0xb3,0xcf,0x5e,0xa9,0xeb,0x95,0xd5,0x35, + 0x22,0x03,0x19,0x11,0xa2,0x28,0xe1,0x2d,0x52,0x38,0xaf,0x57,0x72,0xe4,0xf5,0x5a,0x4b,0xcb,0xd5,0xd7, + 0xb1,0xdb,0x62,0x55,0x31,0x4f,0x70,0x5a,0x1d,0x8b,0x32,0x7e,0x55,0xe4,0x7d,0xb0,0x58,0xfa,0xd1,0x63, + 0x7a,0x0b,0xbe,0xba,0x5b,0x7f,0x5d,0xe2,0xcb,0xeb,0x8f,0x97,0x5b,0xf7,0x47,0x5f,0xaf,0x10,0x0e,0xd1, + 0xbe,0xf7,0xd4,0x7c,0x6b,0xdd,0x5f,0x9e,0x51,0xad,0xc9,0x17,0x83,0x71,0xc4,0xfc,0xbd,0x46,0xa2,0xc5, + 0x38,0xc9,0x41,0xfe,0x41,0xeb,0x2f,0x77,0x22,0xfc,0xe7,0x4d,0x65,0xcc,0xa7,0xf7,0xdf,0x34,0xc1,0xb8, + 0xec,0xfa,0x13,0xc3,0x4f,0xb6,0xde,0xd4,0x89,0x87,0x31,0x18,0x5f,0x1d,0x82,0x59,0xbf,0xd9,0x7a,0x53, + 0x4f,0x34,0x60,0x33,0x1d,0x59,0xb5,0x83,0xf2,0x9c,0xcf,0x21,0xa8,0xf2,0xf4,0xdb,0x1b,0x67,0xca,0xf1, + 0xa5,0x14,0xe6,0x33,0x53,0xa7,0xab,0xc1,0x78,0xa6,0x33,0x9e,0x85,0xfc,0x75,0x07,0x9d,0xfa,0x85,0x49, + 0xdc,0x31,0x7e,0xd6,0xdc,0x83,0x10,0x12,0x0e,0x26,0xcd,0xc7,0x07,0x3c,0xbc,0x4e,0x45,0x3a,0xa7,0x91, + 0x7d,0xd2,0xb2,0xd3,0x8f,0x7e,0xae,0xd4,0x64,0x40,0x20,0xaa,0xfd,0xf7,0x1a,0xb8,0x9a,0x8a,0x5f,0x88, + 0x1a,0x15,0x46,0x30,0xbe,0xf1,0x94,0xf3,0xfd,0x27,0xde,0x99,0x86,0x4b,0x92,0x90,0x0d,0xf1,0xd2,0xcc, + 0xb9,0x68,0xe2,0xbd,0xea,0x39,0xde,0x61,0x78,0x18,0x38,0x33,0xd9,0x61,0x30,0xf8,0x98,0xaf,0x09,0xf9, + 0xd8,0xe9,0xf3,0xc2,0x83,0x63,0xf4,0x28,0x0d,0x92,0x68,0x80,0x24,0x7f,0x75,0x50,0xd8,0xd0,0xee,0x9e, + 0x81,0xd5,0xa6,0xfa,0x95,0x60,0x68,0x38,0xb8,0xc9,0x20,0xe0,0xa3,0x94,0x79,0x4c,0xb6,0x00,0xcc,0x8c, + 0x27,0x3e,0x5c,0x26,0x44,0x9b,0x21,0x2d,0x20,0x7d,0xab,0x17,0x13,0x75,0xa2,0x10,0xdc,0x92,0xa9,0x05, + 0xa8,0x2a,0x0f,0xb0,0xb7,0x10,0xd0,0x36,0xe5,0x89,0x6c,0x57,0xbd,0x61,0x45,0x6d,0xb5,0x54,0xd5,0x97, + 0xfa,0xff,0xe9,0xfc,0xa1,0x6a,0x85,0x53,0x3f,0x6f,0x87,0x13,0x85,0x35,0xb5,0x05,0x51,0x9e,0x62,0xd0, + 0x6b,0x84,0x9a,0xb9,0x0f,0x40,0xcf,0xdb,0xb8,0xee,0x8f,0x20,0x94,0x47,0xdb,0xa2,0x8a,0xd9,0x14,0x07, + 0x6a,0xf5,0x25,0x81,0x7f,0xf5,0x26,0xd7,0x46,0x99,0x43,0x08,0xcd,0x36,0x3a,0x72,0x74,0x4c,0xed,0x73, + 0xbc,0x47,0xe1,0x6f,0xa2,0x10,0xf2,0x70,0xe4,0x2e,0xa3,0x9a,0x2d,0x90,0xce,0xd6,0x41,0x64,0x01,0x2c, + 0xeb,0x8f,0x94,0x9c,0x3e,0xbb,0x46,0x29,0x8d,0xd6,0x62,0xc3,0x24,0x0d,0xa9,0x97,0x9c,0x00,0xe9,0x01, + 0xe3,0x75,0xf0,0x05,0x0d,0x45,0x13,0x2b,0x71,0x9e,0x30,0x65,0x75,0x44,0x39,0x1e,0x1c,0x40,0x5e,0xab, + 0xc9,0x94,0x6a,0x42,0x58,0x78,0x9f,0x87,0x72,0x87,0x7c,0xbc,0xb6,0xa2,0xff,0xe2,0x8b,0xcb,0xdf,0xaf, + 0x85,0x39,0x45,0x4c,0xf1,0x4f,0x2b,0x99,0x9d,0x39,0x36,0x1c,0x0f,0x51,0xfc,0x2b,0xcb,0xdf,0xd7,0xeb, + 0x28,0x38,0xf7,0x62,0xbc,0x41,0x3b,0x12,0xe1,0xd0,0xee,0x9c,0x30,0x72,0xb0,0x7c,0x08,0x23,0xdf,0xf7, + 0x00,0xc5,0x95,0xf0,0xaf,0x83,0xce,0x65,0x1e,0xab,0x39,0x01,0x25,0x1b,0x22,0x69,0x23,0x60,0xd9,0x6a, + 0xd8,0xd5,0x71,0x0d,0xfc,0x67,0xdb,0x0d,0x4c,0x9b,0x87,0x85,0x01,0x95,0xed,0x75,0x46,0xac,0xf3,0x27, + 0xb9,0x4e,0x76,0xed,0xce,0x4d,0x44,0xea,0x79,0xe8,0x3a,0x5c,0x79,0xf6,0x26,0xd2,0x4c,0x8d,0xdc,0x0a, + 0x1c,0x0e,0x02,0x8d,0x02,0x96,0xae,0xe7,0x23,0x14,0x45,0x98,0x06,0x11,0x9e,0x65,0x44,0xe2,0x3e,0x6a, + 0x9e,0xb9,0xea,0x4e,0xa9,0x5e,0xe9,0x86,0x1c,0x79,0xca,0x09,0x13,0x87,0x89,0x47,0x0c,0xca,0x01,0x86, + 0xc5,0x5f,0xce,0x6e,0x27,0x32,0x8d,0xe6,0x90,0xdc,0x4a,0x32,0x2d,0xd3,0x5c,0xd6,0xa6,0x41,0x78,0x2f, + 0x4f,0x43,0x28,0x91,0xcd,0x74,0x84,0x45,0x3b,0x37,0x77,0xeb,0xcd,0x17,0xdb,0x2f,0xe3,0xfa,0x8b,0x15, + 0x8a,0x30,0x9c,0x53,0x63,0x86,0x75,0x6b,0x0b,0xb0,0x12,0x62,0x84,0x87,0xdb,0x7b,0x78,0x45,0x55,0x7e, + 0xf3,0x4f,0x94,0xe1,0xd6,0x5c,0xbd,0xd8,0x1a,0xc1,0x88,0x42,0x1b,0x38,0xc7,0x60,0x8f,0x7e,0xfb,0xda, + 0xc1,0x21,0xc2,0x3a,0x89,0x72,0xb0,0xad,0xfc,0xdb,0x42,0x63,0x35,0x9b,0xbc,0xd3,0xb1,0xb0,0x2f,0x4b, + 0xbf,0x91,0xeb,0xec,0xf8,0xa4,0xe7,0x16,0x7f,0xf4,0x80,0x23,0x4e,0x10,0xc1,0x3a,0x2b,0x10,0x86,0x1d, + 0xc9,0xcb,0x17,0xf7,0xff,0x0e,0x5f,0x9b,0x2f,0x23,0x67,0xf1,0xa1,0x4b,0xc5,0xf5,0xc7,0x64,0xbd,0x48, + 0x20,0x6c,0x31,0x9c,0x6c,0xf2,0xc7,0x64,0xe3,0x58,0x44,0x4f,0x1b,0xf8,0x93,0x68,0x15,0xa5,0x25,0x43, + 0x34,0xb6,0x01,0x19,0x11,0x4f,0xb4,0xd5,0xd9,0x24,0x23,0x13,0x66,0x61,0x86,0xa4,0x2a,0x1e,0x6c,0x8d, + 0x5a,0xee,0x6f,0x5d,0xde,0xad,0x85,0xa3,0x0a,0x2e,0xb4,0xc5,0xf5,0x5b,0x12,0xa9,0x72,0x21,0x92,0x13, + 0x3f,0xab,0x03,0xa7,0x81,0x84,0xa4,0x3f,0x22,0xcc,0xe7,0xe0,0xfd,0xee,0x93,0x2d,0x13,0xda,0x36,0x17, + 0xc7,0x9e,0x35,0xf9,0x44,0xdb,0xf4,0x73,0x46,0x20,0x43,0x4f,0xe1,0xb6,0x70,0x91,0xa8,0x32,0x9a,0x3c, + 0x6d,0x87,0xe1,0xad,0x7c,0x1f,0x50,0x6e,0x02,0xf2,0x91,0xc6,0xfd,0xc3,0xc2,0xb6,0xe1,0x7d,0xeb,0xeb, + 0xcd,0x1a,0xb6,0x7d,0x53,0x92,0xc9,0xd9,0xdf,0x42,0xa1,0x90,0x27,0x32,0x9e,0xe0,0x0f,0xb2,0xbd,0x78, + 0x2a,0xdf,0xd5,0x9c,0x6e,0xae,0x2b,0x0d,0x43,0xb2,0xed,0xb8,0xdf,0x8c,0xab,0x28,0xcc,0xe8,0x53,0x53, + 0xac,0xdf,0x12,0x90,0x63,0x7f,0xbb,0x88,0x2a,0x99,0x16,0x31,0xc4,0x7a,0xee,0x28,0x53,0xa0,0x4f,0x7f, + 0x07,0xd9,0x12,0x1e,0x7f,0x6f,0xbd,0xaa,0x59,0x91,0x07,0x37,0x9d,0x9a,0x26,0x8f,0x8c,0x20,0x21,0xe3, + 0xc8,0x88,0xec,0xaf,0x0e,0x1c,0x76,0xc2,0xf0,0x0d,0xe7,0xaf,0xc2,0x7c,0x87,0xe6,0x86,0x33,0xa6,0xaf, + 0x41,0xea,0x37,0xdc,0xe6,0xdb,0xec,0xdf,0x06,0xac,0x7c,0x9f,0x5d,0xd2,0x3c,0x7f,0xb7,0xdb,0x35,0x4b, + 0xed,0xc4,0xfc,0x14,0x12,0xaa,0x04,0x81,0x47,0xb6,0xeb,0xff,0x01,0x66,0x02,0xd9,0x41,0x07,0x24,0x9d, + 0x77,0xe9,0x7b,0x14,0xc7,0xb5,0xbe,0xfe,0x94,0xb2,0xbe,0xe8,0x2e,0x95,0xf9,0x03,0xa2,0x0f,0x3c,0xde, + 0xc1,0x89,0x89,0x1f,0xe3,0xbb,0x7e,0x6b,0x4d,0xd7,0x48,0x13,0xf5,0x74,0x2b,0x5d,0x3d,0x56,0xe6,0x7e, + 0xea,0xee,0x91,0x46,0xb3,0x30,0x96,0x8d,0x06,0x8d,0x90,0x26,0x08,0x81,0x07,0x39,0x32,0x20,0x01,0x11, + 0x28,0x0b,0xc1,0xf8,0xc7,0x45,0x3f,0xe1,0x65,0x82,0x63,0x43,0x16,0x91,0xf8,0x81,0x5c,0xae,0x21,0x3b, + 0xce,0x86,0x12,0xe4,0xfb,0x9e,0xaa,0xee,0x19,0x09,0xb8,0x6b,0x16,0x34,0xdd,0x55,0x7d,0xea,0xd4,0x59, + 0xde,0xf7,0x3d,0xcf,0x38,0xee,0x7a,0x46,0x30,0x6f,0xc0,0xc9,0xf6,0x0f,0x78,0x7c,0x3f,0x7b,0x2e,0x6b, + 0x1a,0x72,0xc5,0x67,0x19,0xc7,0x16,0x39,0xd6,0xe7,0x7c,0x95,0xe7,0x2b,0xa3,0x85,0x11,0xce,0xba,0x9f, + 0xc8,0xe6,0xee,0x1d,0xb8,0xc9,0x6c,0x21,0xcc,0x86,0xcf,0x52,0xc2,0x90,0x1e,0xef,0x66,0x8e,0x9b,0xaf, + 0xfe,0xe1,0x75,0xd8,0xf0,0x06,0xd2,0x76,0x7f,0xc1,0x67,0x43,0x8e,0xe3,0x26,0xba,0x3e,0x72,0xc5,0xaf, + 0x05,0x1b,0x30,0x5e,0xc6,0xcb,0xd7,0xcc,0x3c,0xcc,0xb9,0x52,0x58,0xbc,0x7a,0xd3,0xf6,0xce,0xb0,0x74, + 0x26,0x23,0xcc,0xb4,0xe1,0x09,0x2b,0x97,0x68,0x7a,0x13,0xcc,0x4e,0xe3,0xec,0x44,0xd7,0x3f,0x0c,0x08, + 0xbb,0xba,0x72,0xd5,0x16,0x67,0x78,0x65,0x14,0x07,0xee,0x76,0x86,0xfd,0x9f,0x15,0xd9,0xf3,0x86,0x7d, + 0x62,0x55,0xd8,0xac,0xdf,0x72,0xdc,0x74,0x2a,0x2d,0x4c,0x1e,0x9e,0xda,0xfe,0xa2,0x36,0x92,0xe8,0xda, + 0x95,0x7a,0xe8,0x25,0x3f,0x55,0x5e,0x8a,0x9e,0x73,0x23,0xb5,0x4b,0x3c,0xac,0x96,0xdb,0x8f,0xdb,0x7c, + 0xe8,0xfa,0x62,0xda,0xf3,0x2c,0xc1,0x92,0x03,0x69,0xcb,0x74,0x85,0xcd,0x9b,0xe5,0xd5,0xb9,0xa0,0xc5, + 0x2c,0xfd,0x7a,0x18,0xaf,0x05,0xb3,0x1c,0xbb,0x5f,0xb8,0x09,0xf6,0x61,0xf9,0xc6,0x60,0x9d,0x87,0x2d, + 0xd9,0x1c,0x2d,0xf8,0x6f,0x72,0xd6,0x03,0xb7,0x5d,0xd1,0x6c,0x65,0x7e,0xce,0x03,0x96,0x34,0xec,0x1e, + 0xef,0x38,0xef,0x59,0xbf,0xd6,0x7c,0x27,0x48,0x74,0x3d,0x8e,0x68,0x0d,0xb3,0xa4,0x93,0xc9,0xfc,0x22, + 0x23,0x6c,0xaf,0xf9,0x7a,0x21,0xe0,0xf9,0xe0,0xd8,0x46,0xcb,0x76,0x3c,0x7c,0x95,0x40,0xb4,0xa5,0x07, + 0x4b,0xa5,0x63,0x66,0xc0,0x83,0xa5,0xce,0x42,0xd7,0x13,0xb9,0x49,0x26,0x5e,0x16,0x6e,0x0e,0x69,0x28, + 0xcf,0xe6,0x83,0x85,0xc5,0xeb,0x92,0xb3,0x74,0x2a,0x28,0x1d,0x6b,0xb4,0x77,0x75,0x77,0xed,0x49,0x23, + 0x4f,0x97,0x75,0x7e,0x7e,0xc8,0x4f,0x2f,0x04,0x3f,0xdf,0xd1,0x5d,0x79,0x15,0x0f,0x0f,0x39,0xf5,0xca, + 0x35,0x7e,0x38,0x28,0xbf,0xd2,0x4a,0x79,0xc2,0x1d,0x36,0x5c,0x53,0xe2,0xeb,0x17,0x04,0x1e,0xe2,0x8f, + 0x71,0xcd,0x9c,0xde,0xda,0x7c,0x25,0xb8,0xb6,0xa3,0x59,0x66,0x96,0x91,0x5b,0x1b,0xb6,0x17,0xdf,0x6e, + 0x04,0xd8,0xfb,0xb5,0x57,0x47,0xc8,0x6c,0xda,0xb2,0x82,0x68,0x21,0x70,0xdf,0x6e,0xd4,0x77,0xb0,0xe7, + 0x1d,0xe9,0x7d,0xc7,0xca,0xe1,0xbe,0xdc,0x67,0xae,0x83,0x27,0xe1,0xb2,0x61,0x07,0x76,0x58,0xd2,0x75, + 0x86,0x59,0xd6,0xf6,0xd2,0x56,0x50,0x62,0xa6,0xb0,0x91,0x95,0x3a,0x4a,0xcd,0x15,0xe2,0x13,0x99,0xe8, + 0xea,0x7e,0x4a,0x6d,0xb2,0x84,0x17,0xb8,0x55,0xc3,0xf5,0x56,0x99,0xe7,0x3a,0xd9,0x3e,0x4b,0x98,0x09, + 0xf6,0x84,0xb0,0xd2,0xb8,0x7e,0xed,0x54,0xfb,0xe7,0x52,0xad,0x8e,0x24,0xdb,0x8b,0x7b,0x7b,0x24,0x43, + 0xb1,0x4a,0xb1,0x70,0x21,0x87,0x1f,0x19,0x21,0x13,0xcd,0xd4,0xf2,0x59,0x58,0x5a,0xe7,0xcd,0xf3,0xa5, + 0x82,0x65,0x06,0x09,0xba,0x77,0x81,0x0d,0xb9,0x62,0xc8,0x70,0x45,0xeb,0xf7,0xf0,0x1f,0xe5,0xd1,0xbc, + 0x1a,0x6e,0x7d,0x85,0x1c,0x1f,0x7f,0x2b,0x2c,0xbf,0x16,0x14,0x16,0x51,0x98,0xa9,0xde,0xfc,0x69,0x7c, + 0xc5,0x83,0x8b,0xab,0xbe,0x2b,0x96,0x12,0x5d,0x1f,0xce,0x9e,0x6e,0x5e,0xe5,0x61,0x30,0xeb,0xb3,0x61, + 0x23,0x87,0xfb,0xbf,0x84,0xb3,0x0c,0x19,0x07,0xeb,0xe6,0x1a,0x0e,0x9c,0x3d,0x1d,0x2c,0xfc,0x3c,0xc1, + 0x76,0xe2,0x92,0x88,0xdb,0x4d,0xbd,0x75,0x07,0x42,0xa5,0x77,0xed,0x6a,0x65,0xd4,0x1b,0xee,0xe3,0x62, + 0x59,0x6f,0x80,0x87,0xfe,0xdb,0x09,0xb6,0xdb,0xd9,0x38,0x2e,0x0b,0xfe,0x5e,0xce,0xc4,0x57,0x93,0x62, + 0xc8,0xa3,0x78,0x50,0xef,0xd5,0xc9,0xc7,0x60,0x65,0xd6,0xae,0xf3,0xe0,0x7c,0x69,0x16,0x17,0x5d,0x4a, + 0xb0,0x67,0x5c,0x27,0x73,0xed,0x3e,0x0f,0x66,0x47,0xc2,0xd2,0x0d,0x7b,0x04,0x6e,0xfc,0xb2,0x9d,0x2e, + 0x39,0x40,0xc1,0x95,0xb0,0x14,0xe6,0xcb,0xab,0x4c,0xd8,0x64,0xa5,0x81,0x37,0x89,0xb6,0x07,0x3b,0x8d, + 0x97,0x45,0x13,0x4d,0x31,0x17,0x30,0x99,0x4d,0x0e,0xb4,0x54,0x0d,0x7f,0xc2,0xc3,0xb5,0x60,0x7e,0xb6, + 0xce,0xd9,0x2f,0x77,0xb0,0x9d,0x62,0xfc,0xc4,0x0d,0xbb,0x8e,0xff,0xfb,0x7b,0xd5,0x4d,0xd6,0x82,0x5b, + 0x28,0xe9,0x3d,0xb1,0xed,0x9e,0x1d,0x5d,0x2a,0x4b,0xc1,0xa1,0x3c,0xc2,0x53,0xa3,0xc3,0xfc,0xbd,0x35, + 0x64,0xfd,0xa3,0x7d,0x32,0x4f,0x29,0x6f,0xbe,0x3f,0x3f,0xa3,0x03,0x78,0xd8,0xdf,0x3b,0x8e,0x8e,0x79, + 0x59,0xc8,0x5a,0x7d,0x4d,0xc8,0x29,0xd4,0x80,0x3b,0x88,0x34,0x47,0xf9,0xab,0x6f,0x33,0xd0,0x0b,0xdc, + 0x60,0xc2,0x92,0xe2,0x50,0x1d,0x58,0x42,0x75,0xae,0xb7,0xf7,0x30,0x69,0x0c,0x8a,0x44,0xd7,0x96,0xa2, + 0xaa,0x8e,0x1f,0x82,0x45,0xee,0xab,0x0d,0x38,0x74,0x8d,0x57,0xcb,0x6b,0x4b,0xbd,0x21,0xfe,0x65,0xfd, + 0xae,0x40,0x07,0xbb,0x7a,0xe9,0xbb,0xbe,0x14,0x7c,0xbf,0xca,0xfd,0xe2,0x5e,0x42,0x13,0x2c,0xc1,0x72, + 0x50,0x6e,0xc9,0xc1,0xda,0x22,0xb3,0x8d,0x8c,0x40,0xd2,0xd7,0x96,0x79,0xde,0xdf,0x7b,0x71,0x75,0x0b, + 0x50,0xf8,0x3f,0xa3,0xd8,0xab,0x7d,0x2d,0x3b,0xa7,0x70,0x6c,0xeb,0x7e,0xb5,0x78,0x0c,0xf1,0x1a,0x14, + 0x62,0x68,0xb1,0xd3,0xcd,0x12,0x0b,0x65,0x94,0x94,0x91,0xa3,0x74,0xc2,0x9f,0xd7,0x83,0xa8,0x31,0xd9, + 0x93,0x4e,0xd6,0x96,0x02,0x07,0xf0,0x7c,0xf5,0x22,0xa0,0xa4,0x41,0x8e,0x7c,0xbe,0x0b,0x6b,0x6c,0xcf, + 0x50,0xda,0x4d,0x7b,0xc5,0xa8,0x24,0xc6,0x33,0xfe,0x41,0xdd,0x74,0xaf,0xf1,0xc6,0x32,0xc2,0x86,0x5f, + 0x37,0x46,0x42,0xfd,0x63,0x76,0x04,0x68,0x33,0x3b,0xc2,0x1b,0x55,0x1d,0xd7,0xb9,0x20,0x2c,0xaf,0xd6, + 0x02,0x3a,0x76,0x1e,0x6d,0x83,0xe4,0x06,0x08,0xe3,0xd0,0x80,0x61,0xcb,0x04,0xaa,0xb9,0x80,0x95,0x7f, + 0x12,0xde,0x76,0xd8,0x42,0xb8,0x0f,0x96,0x6f,0xd4,0xef,0x12,0x00,0x8b,0xde,0xc3,0x01,0x7e,0x53,0x4b, + 0x25,0x0d,0x74,0x50,0x53,0x99,0xe5,0x41,0x59,0xfd,0x5d,0xa2,0xf6,0xca,0xff,0x39,0x00,0xda,0x50,0xa6, + 0x16,0x71,0xa5,0x00,0x71,0x0f,0x9b,0x74,0xe2,0x97,0x6c,0x14,0xca,0xd6,0xbb,0x87,0x2a,0x1a,0x72,0x93, + 0xe7,0xd9,0x90,0x70,0xa5,0x63,0x4f,0x6f,0x8c,0x1f,0x93,0xb0,0x4d,0x3b,0xf1,0xa3,0xbc,0x7a,0xcc,0x7c, + 0x8f,0x6c,0x34,0xcb,0xc7,0x46,0x78,0x58,0x66,0x2e,0x42,0x60,0x58,0xaa,0x54,0x0a,0x54,0xc8,0x8a,0x67, + 0xd8,0x5f,0x21,0x60,0x4d,0x99,0x34,0xdc,0xb4,0xe3,0xfd,0x75,0x5c,0xda,0xf7,0x4e,0x80,0x38,0x86,0x86, + 0x84,0x2d,0x4c,0x00,0xdf,0xc9,0x42,0xf4,0x6d,0xe0,0xaa,0x8f,0x51,0x97,0x3c,0x9c,0x2f,0xf8,0x07,0x77, + 0xc4,0xa0,0xb4,0x2b,0xd5,0xba,0x80,0x23,0x51,0x17,0x3a,0x45,0xfb,0x12,0xec,0xf9,0xdc,0x5e,0x75,0xee, + 0x4d,0x84,0x65,0x24,0x64,0x19,0x14,0x15,0x73,0xb2,0xae,0x02,0x8c,0x39,0xb0,0x05,0xea,0x5e,0xc7,0xf8, + 0x58,0x5d,0xff,0x8d,0xbe,0x94,0x09,0x54,0xf6,0x91,0x00,0x70,0x34,0x4d,0x2f,0x33,0x69,0x5b,0x70,0x14, + 0x76,0x8f,0x1c,0xff,0x56,0x1d,0x88,0xb5,0xab,0x34,0x8a,0x22,0x3b,0x50,0x08,0x22,0x4e,0x90,0x1e,0xf0, + 0x90,0xa2,0x87,0x92,0x99,0x0b,0x50,0xeb,0x59,0xcf,0xdf,0x92,0x7c,0x1f,0x1d,0x65,0xe5,0xa2,0x94,0x53, + 0xae,0xf7,0x96,0x99,0x14,0xb6,0x19,0x57,0x57,0x99,0x79,0xc2,0x48,0x0e,0x04,0x57,0x0e,0xe8,0x08,0xe1, + 0x38,0x33,0x28,0x5e,0xc9,0x83,0x59,0x60,0xec,0x05,0xac,0x89,0xa6,0x4e,0x34,0x8c,0xe3,0x4e,0x6c,0x88, + 0x5e,0x11,0x8b,0xf4,0xce,0xb7,0xa4,0x7f,0xf0,0x75,0xc9,0xf1,0x80,0xa5,0xff,0x14,0x49,0x39,0x08,0x28, + 0x4d,0x25,0x49,0x8b,0xcb,0xab,0xc4,0xda,0x6e,0x2a,0x6b,0x01,0xdf,0xb0,0xcf,0x64,0x68,0xf3,0x26,0x21, + 0x26,0x5a,0x7e,0x17,0xfa,0xa6,0x45,0x68,0x15,0xa2,0x94,0x58,0x9f,0x91,0x1c,0x84,0xe1,0x9e,0x26,0x2a, + 0xdf,0x54,0x88,0x04,0xf7,0xbe,0xec,0xa4,0x93,0xc2,0xdc,0xdf,0x1c,0xc5,0x6e,0xdd,0x65,0xbd,0x3d,0x0d, + 0x7f,0xef,0x74,0xdc,0x86,0xdf,0x6d,0x26,0xba,0x76,0xc3,0xa2,0x02,0xe1,0xd5,0x5f,0x8b,0xe5,0x28,0x8d, + 0x72,0x3d,0x7f,0xae,0xfd,0x1b,0x10,0x7b,0x88,0x2a,0x58,0x57,0xe4,0xfb,0x2c,0x03,0x60,0x9a,0x0b,0x78, + 0xc3,0x47,0x27,0xa5,0xed,0xa4,0x28,0xfa,0x07,0x8f,0x4b,0x74,0xd3,0x89,0x79,0xd5,0x87,0x75,0x15,0xd4, + 0xbc,0x7f,0xf0,0x22,0xca,0xf2,0x18,0xf5,0x59,0x0b,0xb0,0xa4,0x0b,0x51,0xc0,0x33,0x22,0x49,0x7b,0xbd, + 0xb9,0x96,0x60,0x8f,0xbb,0x69,0xf4,0x3f,0xdd,0xb3,0x47,0x5e,0x9d,0xa9,0xba,0x40,0x11,0x0d,0xc1,0xe8, + 0x34,0xc1,0x90,0x21,0xf4,0xc2,0xe2,0xde,0x69,0x28,0x17,0xc3,0xcd,0x10,0xc5,0x3e,0xad,0x60,0xa7,0xcf, + 0x35,0xec,0xe4,0x40,0xad,0x9e,0xd7,0x75,0xb0,0x6a,0xb8,0x2c,0xe5,0x66,0x91,0xae,0xea,0x8f,0x25,0xc5, + 0x23,0xc1,0xb6,0xee,0xcb,0x63,0x5d,0xc6,0x1b,0xe0,0x71,0xd6,0x16,0x92,0xe7,0x97,0x2f,0xe2,0xb5,0xba, + 0x8f,0x5e,0x42,0x78,0x17,0x7e,0xa2,0x57,0x4d,0x96,0x44,0x02,0xed,0xfd,0x45,0x7f,0xdf,0x91,0x66,0xe9, + 0x2f,0x36,0x25,0xf6,0x45,0x91,0x31,0xd2,0x76,0xd4,0x5b,0x70,0x5c,0xfa,0x91,0x75,0x20,0xc1,0x56,0x17, + 0xde,0x5b,0xa6,0x82,0xff,0xfe,0xf2,0x6f,0x65,0xcd,0xd7,0x17,0x58,0x8f,0xd3,0x49,0x3b,0x03,0xfe,0xfd, + 0xe0,0xb7,0x14,0x20,0xdc,0x52,0xaf,0x03,0x2e,0x16,0x62,0x5b,0xe0,0x45,0x42,0x77,0x7c,0x5b,0xc7,0x31, + 0x23,0xca,0x6f,0x8a,0x75,0xe4,0x7c,0xa3,0x05,0x22,0xf4,0x66,0xea,0x4a,0x1d,0xac,0xa9,0x58,0xc3,0x10, + 0x1a,0x49,0x87,0x01,0x76,0x79,0x75,0x5a,0x71,0xd3,0xb0,0x7d,0xa6,0x25,0x5b,0x91,0xd5,0x70,0x7a,0x4e, + 0xb8,0xc2,0xec,0x0d,0xfd,0x1c,0xeb,0x37,0xc8,0x93,0x4d,0xae,0x77,0x6d,0xde,0x46,0x64,0xd3,0xae,0xe0, + 0x58,0x5a,0x60,0x7d,0x59,0x17,0x77,0x57,0xc8,0x49,0xa6,0x42,0x74,0xd6,0x6f,0x6b,0x01,0x57,0x18,0x7b, + 0x4f,0xa1,0x32,0xf4,0x80,0x54,0xdb,0x97,0xcb,0xab,0x6b,0xb6,0x68,0x04,0x94,0x2e,0x7b,0x12,0x19,0xda, + 0x22,0xf4,0xf2,0x01,0xfc,0xde,0xe2,0x76,0x6f,0xb8,0x1e,0x94,0x57,0x21,0x85,0x54,0xf6,0x6c,0x2a,0x85, + 0x13,0x54,0x9f,0xcd,0x25,0x52,0x50,0x09,0x16,0x45,0x76,0xf1,0x98,0x3a,0xf9,0x21,0xc0,0xc6,0xf9,0xbf, + 0xc5,0xa2,0x2e,0x1a,0x92,0x5d,0xd8,0x31,0xe4,0x91,0xa4,0xd8,0x8a,0x6c,0xbf,0xad,0x4d,0xd1,0xf7,0x48, + 0x96,0xee,0x75,0x9e,0x5f,0x64,0x5f,0x76,0x72,0x17,0xe9,0x02,0x61,0x3e,0x70,0xd5,0xc3,0xea,0xd7,0x19, + 0xfa,0xc8,0x82,0xd3,0x2f,0x0d,0xa4,0xe5,0xf4,0x96,0x53,0xb6,0x0a,0xbc,0xce,0x49,0xe4,0x5e,0xd7,0xb3, + 0xba,0x4f,0x8e,0x9d,0xb8,0xbb,0xf1,0xdd,0x49,0x9e,0x87,0x68,0x86,0xba,0x8b,0xd4,0xd4,0xd4,0xdb,0x8d, + 0x29,0x5c,0x32,0x0d,0x78,0x90,0x54,0x3f,0x8b,0xf1,0x03,0x89,0xbe,0x35,0x15,0xb3,0xa6,0x3e,0x24,0xa0, + 0x2e,0x8b,0xe4,0xe8,0x1a,0xde,0x19,0x24,0xc9,0xa4,0x70,0x5f,0x36,0xe8,0x8a,0x0d,0x3f,0xfe,0x1d,0x91, + 0xc8,0xbd,0x29,0xec,0x75,0x4d,0x3c,0x25,0x18,0x5a,0xa9,0x59,0xfa,0x2c,0x88,0x4c,0x63,0xb3,0xd2,0x98, + 0x9a,0xf2,0xf2,0x4d,0x82,0x84,0x19,0x60,0x76,0xb3,0xf4,0xf5,0x59,0x55,0xa8,0x1f,0x7e,0xac,0x54,0xdc, + 0x00,0x44,0x9d,0xa8,0xf3,0xc3,0x4d,0xfa,0x29,0x45,0x10,0xb6,0x86,0x86,0x9c,0xb4,0xed,0x09,0x93,0xd4, + 0xec,0x1f,0xae,0x86,0x9d,0xa6,0x41,0x7c,0xa2,0x9f,0x7f,0xf8,0x6d,0x6f,0xfe,0x5e,0x9b,0x7f,0x8b,0xfe, + 0xde,0x18,0xf4,0xaa,0x17,0x5f,0x5f,0x2e,0xa8,0x6c,0xeb,0xeb,0xc4,0x41,0x0e,0x2e,0xb6,0xc3,0x46,0xd9, + 0x03,0x2f,0x12,0x81,0xcb,0x00,0xea,0x27,0xbf,0x7c,0x42,0x3d,0x98,0xf5,0x08,0x55,0xe8,0x68,0x95,0xe2, + 0xb3,0x80,0x0b,0xee,0x1f,0x3c,0x7f,0x6c,0x59,0x32,0x54,0xaa,0xba,0xe1,0xfe,0x60,0x61,0x9e,0x5e,0x33, + 0x49,0x78,0x12,0x3b,0x7d,0x86,0x5e,0x8d,0xc5,0x56,0x75,0x17,0x8a,0x4c,0x9f,0x70,0xfd,0x7d,0x0a,0x6f, + 0x3f,0xcf,0x7a,0x1e,0x8f,0x23,0x37,0x3d,0xdf,0x86,0x34,0x1d,0x3c,0x02,0x42,0x3f,0xa7,0x98,0x4e,0x24, + 0xb3,0xe4,0x08,0xf7,0x73,0xe5,0xa5,0xce,0x8b,0x20,0x41,0x1b,0x74,0x39,0xed,0xd3,0x6b,0x54,0x7c,0xa0, + 0x2c,0x85,0x5f,0x78,0xb1,0x76,0x45,0x5f,0xb4,0xa1,0xc3,0xd0,0x23,0x95,0xd2,0xe1,0xfb,0xfd,0xdc,0x9a, + 0xa2,0x7a,0x22,0x18,0x52,0xa9,0x2b,0xf3,0xc7,0x43,0xc0,0xc4,0x26,0x8c,0x9c,0x9f,0x2f,0xad,0xd0,0x25, + 0x7a,0x0f,0x5f,0xf3,0x73,0xa5,0xbb,0x24,0x18,0xc1,0xda,0x0d,0x3f,0xed,0xd2,0x5b,0x45,0x62,0xfa,0x36, + 0x75,0xae,0xff,0x42,0x33,0x6a,0x1b,0xb0,0x88,0x9e,0x6a,0x65,0x50,0x23,0x9b,0x91,0x70,0x3a,0x72,0x77, + 0x43,0x09,0xae,0x8f,0xc9,0x30,0xd6,0x95,0x69,0x1d,0x30,0xb2,0x7e,0xc3,0x0e,0x97,0x74,0x60,0x59,0x1f, + 0x72,0x50,0x07,0x54,0x2a,0xcf,0x27,0xdb,0x54,0xbc,0x9e,0xf7,0xf7,0x41,0x36,0xa1,0xea,0x53,0x20,0x3d, + 0x61,0x1e,0x99,0xf8,0xb4,0x46,0xae,0x58,0xc6,0x30,0xe8,0xe3,0xa3,0x5c,0x94,0xa1,0x39,0x27,0x0b,0x09, + 0xa2,0xf4,0x45,0xa2,0xa7,0x8b,0xe6,0x10,0x7b,0x0b,0x25,0x9e,0x38,0x0e,0x1a,0x6c,0x6a,0x16,0x2c,0x04, + 0x33,0xc0,0x73,0xff,0x41,0x45,0x04,0xdc,0x74,0x92,0x42,0x98,0x92,0x56,0xab,0x44,0x91,0xd2,0xc3,0xc4, + 0x76,0xb2,0x8e,0x7a,0x44,0x58,0xa3,0x47,0xbe,0x3f,0x28,0x91,0xb2,0xad,0xcf,0xf8,0xab,0xfd,0x69,0xdb, + 0x46,0x01,0x6f,0xe5,0xe7,0xf5,0x7c,0x40,0x97,0x5c,0xd5,0x8e,0x98,0x05,0x30,0x75,0x36,0x03,0x42,0xc4, + 0x60,0x9a,0x83,0x66,0xcb,0x7a,0x54,0x0f,0xff,0x86,0x8e,0xf8,0x27,0x21,0xb9,0xff,0xc9,0x34,0x95,0x22, + 0xf5,0xc0,0x38,0x55,0x2c,0x1b,0xc4,0x05,0x25,0x64,0x11,0xf5,0xc5,0x60,0xbe,0x5a,0x4c,0x21,0xcd,0xaa, + 0x36,0xc0,0x7f,0x85,0x53,0xca,0xc6,0x1a,0x9d,0x83,0xe4,0x95,0xe6,0x67,0x21,0x9d,0xef,0xba,0x9a,0xa7, + 0x34,0xf5,0xd1,0x12,0x28,0xcf,0xff,0xe4,0x38,0xb1,0x1d,0x73,0xa3,0x42,0xcb,0x2b,0xaa,0x3d,0xb9,0x99, + 0x68,0x52,0x5b,0xbd,0xa3,0xb6,0x75,0xbc,0x87,0x72,0x9d,0x88,0x24,0xc4,0x35,0xa5,0x61,0xa9,0x22,0x78, + 0x78,0xd7,0x16,0xc5,0x00,0x0d,0x24,0xeb,0xf1,0xa4,0x49,0x33,0x5e,0x0f,0x6d,0xdb,0xdf,0x39,0x8d,0xaf, + 0xbb,0x80,0xe0,0x13,0x4c,0x67,0x46,0x8b,0x00,0xa0,0x04,0x5d,0x06,0xfd,0x64,0x09,0x29,0x9b,0x38,0x4f, + 0x47,0xf8,0xf3,0x4d,0x6f,0xba,0x10,0xb8,0x2d,0x93,0x2a,0xdd,0xe8,0x17,0xf9,0xe6,0x55,0xe2,0xb9,0xde, + 0x06,0x80,0xa6,0x49,0xb5,0xa8,0x1e,0x79,0x27,0x69,0xd1,0x34,0x47,0xfa,0x92,0xaa,0x85,0xb6,0x5d,0xe5, + 0x51,0x0e,0x47,0xf0,0x52,0xcf,0xed,0xc8,0x2c,0x92,0x95,0x74,0x6c,0x04,0xc9,0xf3,0x0f,0x9e,0xa4,0xea, + 0xe2,0x27,0x99,0x4b,0xa3,0xbd,0xc0,0xbc,0x66,0xc3,0x09,0x1e,0x9e,0xfc,0x46,0x6f,0x97,0xd1,0x87,0x12, + 0xa1,0xe5,0xb8,0xf8,0x0f,0xd5,0xea,0xf1,0xdb,0xed,0xdb,0x63,0x31,0x0f,0x25,0xaa,0x7d,0xb8,0x51,0x87, + 0x40,0x57,0x44,0x1b,0x7d,0xf0,0x0d,0xd8,0x88,0x85,0xc3,0xe4,0xc1,0x13,0xa8,0x88,0x7f,0x35,0xa9,0x0e, + 0xb4,0x32,0x2b,0x5d,0xc8,0x1d,0x28,0x60,0x84,0x38,0x1c,0xb4,0x99,0x75,0xf3,0x1a,0xfa,0xc6,0x19,0xa6, + 0xc8,0xf0,0x73,0xd5,0x87,0xaa,0x4f,0xdf,0xa2,0x3f,0x9b,0x36,0x45,0x70,0xfb,0x4a,0xf4,0xb2,0x51,0x2d, + 0xcf,0xbf,0x3f,0x73,0xa9,0xf7,0xb0,0xc2,0xb3,0x2a,0x82,0x2b,0xb1,0xeb,0x7d,0xe2,0xd7,0x32,0x68,0x2c, + 0x69,0x61,0xa4,0x2c,0x04,0x07,0xf2,0x0b,0x51,0x15,0x51,0x67,0x96,0x23,0x16,0xcb,0xd5,0xe9,0xf3,0x1f, + 0xcf,0xdc,0x9b,0x56,0x62,0x32,0x6c,0x7f,0xa0,0x30,0xf0,0x71,0xcb,0x3a,0x90,0x07,0x87,0x2d,0xa8,0xc5, + 0xde,0xb1,0xe6,0x68,0x1e,0xb2,0xa3,0xf8,0xa8,0xad,0xdd,0x8f,0x3f,0xa0,0xeb,0x83,0xf2,0x3c,0x69,0xf9, + 0xc7,0xfb,0xc1,0x78,0xad,0x87,0xd6,0xb0,0xd4,0xdd,0xb5,0x07,0x6a,0xb7,0x38,0x75,0x5d,0x8e,0x8d,0x16, + 0x3a,0x8c,0xd4,0x4d,0x83,0xd1,0xf4,0x64,0xbe,0x7a,0x4b,0x2a,0x2d,0xcd,0x7a,0x1e,0xb2,0x1c,0x07,0x9b, + 0x87,0x6f,0x3d,0xca,0x72,0x6f,0x4f,0x88,0xaf,0xe2,0x21,0xe2,0x6a,0x81,0xc6,0xc4,0x6e,0x34,0xc5,0xe8, + 0xd8,0x36,0xeb,0x0f,0x59,0xee,0xd9,0xc1,0xfe,0x41,0xac,0x29,0x4e,0xaa,0x4e,0xdf,0xa2,0x40,0xe1,0x2b, + 0x57,0xfc,0x5a,0x54,0x08,0x48,0x93,0x02,0x43,0x88,0x99,0xf7,0x31,0x09,0x1a,0xc4,0x2d,0x05,0x54,0x3d, + 0x14,0x7c,0x75,0xb4,0xb0,0x09,0xb3,0x4f,0x39,0x41,0xed,0x2b,0xd9,0xb4,0xab,0x41,0x4d,0x8d,0x02,0x98, + 0x88,0x7e,0x37,0xc9,0x01,0xde,0xff,0x87,0x2b,0x60,0xae,0xed,0x8c,0x98,0xf9,0xa0,0x7c,0x53,0x0c,0x85, + 0x40,0x32,0xc5,0xdf,0x87,0x31,0x31,0xc5,0x6a,0x17,0x8a,0xaa,0xbc,0xaa,0x9a,0x0b,0xb8,0x87,0x3a,0x72, + 0xd5,0x6f,0x8c,0xdb,0x9e,0x12,0x38,0xb7,0x49,0x0b,0x20,0x8d,0x38,0x2d,0x63,0xd8,0xfc,0xdc,0x9d,0xd7, + 0x25,0xeb,0x4b,0xbb,0x54,0x48,0x6a,0xe3,0xfe,0xaa,0xfb,0xc9,0x89,0xe3,0xaa,0x6d,0xce,0xdd,0x39,0x81, + 0x7c,0x62,0x54,0x20,0xfd,0xe4,0xb3,0x24,0x7c,0x31,0xf9,0x1d,0xcd,0xd1,0x6d,0x88,0xf0,0x5b,0x36,0x6a, + 0x4f,0x0e,0xc4,0x55,0x0e,0x36,0xa7,0x13,0xf2,0x2d,0x36,0x90,0xc5,0x01,0xab,0x88,0xd9,0x34,0xf0,0xd9, + 0xb5,0x6f,0xfb,0x07,0x13,0x3a,0x76,0xc1,0xb5,0x92,0x3a,0x0a,0xf4,0x5c,0xbe,0x11,0xbc,0xc5,0x43,0xdd, + 0xfd,0xed,0x1b,0x00,0x42,0x36,0xca,0x3f,0xd6,0xaa,0x85,0x1a,0x7b,0x86,0x6a,0x49,0x0f,0x7a,0x08,0x30, + 0x06,0xc3,0xaf,0x64,0x09,0x51,0x1d,0x1b,0x60,0xb8,0x65,0xea,0xd6,0xd4,0x7d,0xf1,0x01,0xea,0x2e,0x6b, + 0xea,0xfe,0x3b,0x1b,0xdd,0x75,0x8d,0x07,0x25,0x2c,0x0b,0x73,0x7e,0xcb,0xc0,0x4c,0x6a,0x76,0x90,0x42, + 0xfc,0x1a,0x7f,0x58,0x29,0x75,0x24,0x55,0x0d,0x48,0xd5,0x9f,0x75,0x05,0x4a,0x5d,0x0e,0x09,0x91,0x1c, + 0x50,0x21,0xea,0xe0,0xf0,0xb9,0x66,0x3c,0x1f,0x3c,0x62,0x2f,0x3f,0x5c,0x7e,0x27,0x60,0x43,0x16,0xea, + 0xde,0xca,0x9d,0xf4,0xf7,0xc6,0x73,0x6b,0x67,0xcc,0xef,0x0d,0x15,0x90,0xdd,0x2a,0x04,0xf9,0x66,0x7b, + 0x42,0x6f,0x04,0xbc,0xeb,0xd7,0xfe,0xde,0x4b,0xc7,0x64,0x40,0xe4,0x41,0xb2,0x31,0x2c,0x1d,0x32,0xf3, + 0xcd,0xa3,0x33,0xa7,0x60,0xe2,0x28,0xe8,0x26,0x2c,0x1d,0x47,0x60,0xa3,0x9e,0xe2,0xd1,0xe8,0xe5,0x1f, + 0x0c,0xa9,0xc4,0x51,0xab,0x3b,0xd3,0xf2,0xba,0xd4,0x43,0xea,0x13,0x62,0xc0,0x41,0xfe,0xae,0x05,0x0b, + 0xef,0xc3,0xdd,0xd8,0x19,0x1e,0xa2,0xa1,0xf0,0x7c,0xf7,0xa1,0xeb,0xd1,0x5c,0x74,0x73,0x36,0x1f,0xcc, + 0xd2,0xa8,0x47,0xbe,0x31,0x1b,0x14,0xd7,0x40,0x9e,0xa1,0x00,0x0b,0xed,0x1c,0x10,0xc2,0xe6,0x26,0x6f, + 0x1e,0xca,0x07,0xe4,0x14,0xcd,0xb7,0x51,0xed,0x5f,0x3b,0xb4,0xa3,0x2b,0x3a,0xb3,0x13,0x25,0xce,0x72, + 0x02,0x98,0x7a,0xe2,0xda,0x71,0x04,0x1b,0x35,0x89,0x39,0xda,0xf3,0xa8,0xbc,0xb8,0x96,0x75,0x64,0x7a, + 0x8a,0x64,0xd1,0xef,0x29,0x23,0xfa,0x36,0xf5,0x96,0x85,0xd7,0xc4,0x7b,0x1e,0xc0,0x75,0x5e,0x3d,0xf1, + 0xfd,0x18,0xe2,0xe6,0x48,0x7e,0x74,0x08,0x25,0x5f,0xab,0x07,0x17,0x4b,0x7a,0xe0,0x4c,0x34,0x6d,0xff, + 0x60,0x50,0x8a,0x87,0xca,0xde,0xc3,0xa8,0x5e,0x21,0xdc,0x0f,0x1e,0x83,0x80,0x76,0x70,0x10,0xbc,0x41, + 0x94,0xc8,0x5d,0xc5,0xbe,0xdc,0xb7,0xd1,0x71,0x29,0x0f,0x1e,0x7e,0x30,0xc5,0x04,0x7c,0x2c,0x54,0x71, + 0xb2,0xcd,0xab,0xa3,0xb8,0xcc,0x33,0x6a,0x6a,0xe0,0xbe,0xea,0x8b,0x67,0x35,0x46,0x06,0xee,0xcf,0x78, + 0x1e,0xff,0x5c,0x5c,0x8a,0x51,0x33,0xe8,0xed,0xd2,0xca,0x39,0xf8,0xde,0x48,0x0b,0x18,0x8c,0x97,0x5e, + 0x4e,0x1d,0x76,0x85,0xa5,0xc0,0x67,0x03,0x7c,0x1d,0xc5,0xf8,0x9b,0xb7,0x0b,0x57,0xc6,0x33,0x41,0xe9, + 0x75,0x1b,0x23,0xbd,0xd2,0xe1,0xbf,0x79,0x9b,0x03,0x41,0xfe,0x30,0x1b,0x70,0xff,0xbe,0x95,0x53,0xa3, + 0xdf,0x29,0x1e,0x8e,0x60,0xd3,0xb7,0x78,0xe5,0xdf,0x71,0xc3,0xce,0x70,0x11,0xdc,0xf7,0x99,0x30,0xd0, + 0xab,0x89,0x66,0xa9,0x95,0xc4,0x1d,0x86,0x2c,0x74,0x9e,0x30,0xb5,0x5e,0x3a,0x42,0x56,0xeb,0x85,0x35, + 0x95,0x39,0xbe,0x97,0xb7,0xe1,0x7c,0x2e,0x70,0x29,0xa4,0x8c,0x46,0xb3,0x39,0x5f,0xbd,0xce,0x1d,0x69, + 0xae,0xcc,0x61,0xe0,0x45,0x10,0xa0,0xd1,0xd2,0xd2,0x13,0x8a,0x83,0x7e,0x8a,0x9d,0x54,0x8d,0x05,0x1f, + 0x61,0x74,0x86,0x2c,0xa1,0x5d,0xad,0x93,0x12,0x4d,0xb0,0x8f,0x72,0x67,0x03,0x17,0xd9,0xa3,0x57,0x9b, + 0x85,0x3a,0xbd,0xd0,0xe3,0x48,0xe0,0xee,0x53,0x87,0x42,0x5b,0xcc,0xda,0xf9,0x2b,0x07,0x74,0x27,0xd2, + 0x87,0x0b,0x33,0xaa,0x84,0xf7,0x49,0xed,0x5c,0x13,0xff,0x14,0x36,0x69,0x14,0x7b,0xcd,0xdc,0xe4,0xe1, + 0x8f,0x1a,0x4c,0xa2,0xe6,0xa8,0x9e,0x03,0xb2,0x46,0xb5,0x1d,0x28,0x51,0xa9,0x3a,0xa0,0xb1,0x99,0x50, + 0x4d,0xa1,0x1f,0xa0,0x85,0x0f,0x37,0xbe,0x19,0x39,0xc4,0x1b,0x18,0xd9,0xe0,0x4c,0xc4,0x47,0x9a,0x8c, + 0xe8,0x05,0x81,0x89,0xd4,0x8f,0x3c,0x44,0xb9,0x74,0x82,0x57,0xdb,0x54,0x63,0xa7,0x6e,0xd2,0x1a,0x59, + 0x43,0xe6,0x17,0xf0,0x43,0xef,0xd3,0xf5,0x49,0x82,0x61,0xa7,0x63,0xe5,0x10,0x3d,0x12,0xc3,0x9b,0x6a, + 0x50,0xcc,0xf6,0x0f,0x44,0x71,0xd1,0xb7,0x40,0xbe,0x80,0x11,0x1b,0xf1,0xa4,0x74,0x73,0x01,0x7a,0x82, + 0x70,0x1a,0x6a,0x68,0x93,0xec,0x35,0x55,0x50,0x36,0x6b,0xd1,0x47,0x89,0x68,0x68,0x8b,0x7d,0x4f,0xb0, + 0xdd,0xa8,0xe0,0xe2,0xe6,0x34,0xb3,0x00,0x5e,0x24,0x91,0xb1,0xe8,0xaf,0xdb,0x45,0x9a,0x2f,0x91,0xfe, + 0x24,0xb5,0x15,0xce,0xf8,0x15,0xcb,0x90,0xb4,0xd8,0xa2,0x5f,0x23,0xa9,0x05,0x59,0x03,0x9f,0x6f,0x51, + 0xe1,0xd2,0x71,0xe4,0x4d,0x49,0xcd,0x7b,0x1a,0xb6,0x6a,0x9b,0x9a,0x69,0x70,0x5d,0x4a,0x14,0x92,0x83, + 0xd8,0xea,0x21,0xc2,0xdc,0x84,0x7e,0x51,0xa3,0x46,0xec,0x3c,0xae,0x4e,0x8f,0x71,0xc8,0xe7,0x21,0x42, + 0x7f,0xbc,0x39,0x43,0xd1,0xc1,0x2f,0x15,0x03,0x3c,0x61,0x6f,0xda,0xda,0x00,0x7a,0xff,0x27,0xdf,0xee, + 0xc1,0x9c,0x4d,0x0a,0x97,0xae,0xac,0x05,0x08,0x48,0xc8,0xdd,0x37,0xad,0x7f,0x23,0x47,0x12,0x59,0xbd, + 0x1d,0x7f,0xb8,0xde,0x2c,0x7d,0x6b,0x53,0xb1,0xc1,0x33,0x8e,0x4b,0x72,0x9c,0xa6,0x0a,0xd9,0x64,0x32, + 0xb2,0xb2,0x48,0x75,0x4a,0xb7,0x8a,0xee,0xb1,0x04,0x09,0x1f,0xad,0x51,0x1a,0x63,0xa3,0xdd,0xbb,0x1d, + 0xaf,0x81,0x18,0xa0,0x69,0xd6,0x9a,0xca,0x79,0xaa,0x8f,0xa5,0xf1,0x32,0xb4,0x59,0x9c,0x36,0x58,0xdf, + 0xc7,0xbb,0x3c,0x5c,0x1d,0x31,0xba,0xb2,0xba,0xd1,0x1b,0x79,0x1e,0xa0,0x3d,0x16,0x68,0xc8,0x6f,0xd6, + 0x02,0x3a,0x09,0x47,0x6c,0xd4,0x00,0x84,0xbd,0x79,0xff,0xe0,0xc5,0x43,0xba,0x06,0x3f,0xd2,0x8d,0x12, + 0xa5,0x5c,0x57,0xee,0x43,0x99,0xdf,0xe0,0x5d,0xbb,0x6a,0xa8,0xe4,0xa0,0x16,0x55,0x05,0xc9,0x82,0x94, + 0x48,0x7a,0x20,0xda,0x0d,0x4e,0xd6,0x66,0xc9,0x5a,0x78,0x0c,0xe5,0x1e,0x9f,0x7d,0xcc,0x84,0xf5,0x08, + 0x53,0x9a,0x94,0x73,0x48,0x04,0xf2,0x21,0x2a,0x8f,0xd5,0xb9,0xfa,0x69,0x44,0x56,0x99,0xbb,0xb1,0xb8, + 0xcd,0x89,0x06,0x7d,0x4b,0x4d,0xcb,0x52,0x10,0xba,0xa6,0x41,0x85,0xa2,0x12,0xf0,0x62,0xac,0x41,0xf7, + 0x62,0xb2,0x3c,0x41,0x72,0xc5,0x0e,0xc0,0xae,0x3f,0x0a,0xd0,0x6a,0x52,0xfc,0x28,0x6a,0x2a,0x1a,0x91, + 0x37,0xa3,0xa2,0xe4,0xd0,0x6a,0x19,0xc4,0x7d,0x79,0xd6,0xc6,0xa5,0x8f,0xcd,0x1d,0x21,0xd3,0x17,0x57, + 0xe9,0x2e,0x4d,0x74,0x4a,0xb7,0x52,0xc8,0x95,0x15,0xc5,0x7e,0xfb,0xa0,0x1d,0x6f,0xda,0xde,0x99,0x39, + 0xe8,0x32,0x05,0x68,0x3e,0xeb,0x73,0x1d,0x67,0x90,0x7d,0x21,0x6d,0xba,0x28,0xd5,0xd3,0xbe,0x2b,0x26, + 0x5a,0xac,0x1f,0xb3,0x38,0x20,0xf3,0xf6,0x91,0x6a,0x88,0x8d,0x7f,0x14,0xa6,0xc2,0x04,0xf6,0x5c,0xd6, + 0x34,0x24,0x1f,0x5f,0xf5,0x90,0x21,0x61,0x8e,0xf0,0xa5,0x1f,0xf9,0xbf,0xe7,0x8d,0x4a,0xd1,0x13,0x76, + 0xdd,0xc3,0x97,0xb0,0x8a,0x52,0x55,0xbb,0x7c,0x9b,0xa6,0xc2,0x97,0xd1,0x87,0x3f,0xb6,0x5b,0x88,0x8c, + 0x67,0x24,0x07,0x48,0xdf,0x14,0xd7,0x2d,0x33,0x2d,0x5c,0x19,0x16,0x53,0xe9,0x24,0xfe,0xce,0x28,0x31, + 0x22,0x52,0x24,0xa3,0xd1,0x32,0x32,0xf4,0xff,0x3f,0x3f,0x4a,0xa6,0x98,0x45,0x3a,0xcc,0xb5,0x25,0x2f, + 0xb8,0x00,0x7c,0xa0,0xc1,0x4d,0x63,0xc8,0xb1,0x61,0xff,0x6b,0x63,0x7c,0x16,0x76,0xaf,0xf8,0x07,0xf2, + 0x67,0xa3,0xf9,0x10,0x8f,0x20,0x46,0xba,0x58,0x48,0x82,0x8d,0x74,0xa0,0xb3,0x10,0x30,0x0b,0x37,0xc3, + 0x52,0x43,0xe9,0xc3,0x57,0x89,0x39,0xbf,0x56,0x64,0xbb,0x8d,0x7f,0x81,0xaa,0x45,0xd9,0x75,0x57,0xf4, + 0x5e,0x20,0x49,0x35,0xd1,0xad,0x2f,0xb7,0xb4,0xe5,0x76,0x13,0x4c,0x7a,0xbf,0xc0,0xe7,0x73,0x9b,0x7a, + 0xc8,0x18,0xab,0x76,0xf6,0xf3,0x6e,0x3f,0x87,0xe3,0x58,0x9f,0x91,0x1c,0x1c,0x46,0xb0,0x42,0x96,0x12, + 0x96,0x55,0x00,0x22,0xbb,0x48,0x27,0xc2,0x45,0x24,0x8e,0x2f,0x94,0x44,0x79,0x94,0x43,0x0c,0x82,0xf5, + 0x41,0x7f,0x40,0xb8,0x64,0x9e,0x58,0x30,0xdf,0x24,0xa7,0x9a,0xa9,0xb1,0xe0,0xc6,0x60,0x9d,0x83,0x3c, + 0x3d,0x24,0xef,0x2a,0x75,0x18,0x4d,0x47,0xdd,0xba,0x0f,0xf6,0x3d,0x84,0x2a,0x47,0xaa,0x7f,0x81,0x85, + 0x2f,0xa5,0x80,0x4f,0x71,0xad,0x2b,0x64,0xc2,0x1c,0x47,0x82,0x3c,0x0d,0xf5,0x7a,0x79,0x2c,0x4e,0x4c, + 0x8b,0x49,0x70,0x0d,0xe8,0xda,0x74,0xc5,0xf0,0x32,0x67,0x0a,0xb0,0x06,0x7d,0x36,0x00,0xa3,0x51,0x3a, + 0x7a,0xf4,0xe0,0x89,0x4c,0xf0,0x90,0x25,0xb3,0x5e,0x31,0x35,0xa5,0x87,0x24,0xe2,0x2e,0x40,0x51,0xbc, + 0x8a,0x7d,0x54,0x89,0x13,0xec,0x79,0xc3,0x4a,0x0e,0x64,0x55,0x92,0x46,0x0b,0x97,0xe1,0xcb,0x0b,0x10, + 0x56,0xfa,0x9c,0xdb,0xcb,0xf2,0x0e,0x3a,0x05,0x5f,0xc1,0xd2,0xd0,0x11,0xcc,0x1d,0xe4,0x20,0x93,0x03, + 0x86,0x65,0x35,0xe7,0x15,0x7b,0x58,0x39,0xf8,0x82,0xa1,0x77,0x79,0x07,0x4d,0x0f,0xfe,0x96,0xb2,0xe1, + 0x3e,0xa1,0x7a,0x86,0xd2,0xa1,0x0b,0x87,0x87,0x7e,0x54,0x41,0x45,0xff,0x4d,0xb8,0x91,0xfe,0x17,0x61, + 0x56,0x42,0xd6,0x47,0xd1,0x1f,0xe1,0x5d,0xbf,0x40,0xc8,0x30,0x1e,0xb2,0xc7,0x6d,0xdb,0x90,0xef,0x62, + 0x08,0x9a,0x60,0x29,0xd5,0xa6,0x9f,0x4b,0xdb,0x9f,0xb3,0x8c,0xe1,0x02,0x7b,0xd2,0x48,0x1b,0x43,0x03, + 0x54,0xce,0x13,0x5b,0x6f,0x9c,0x67,0x39,0xba,0x1a,0x95,0xfb,0xf2,0x44,0x7b,0xd3,0x38,0xe6,0xd5,0xb4, + 0x04,0x3b,0xbf,0x99,0xc7,0x5b,0xf1,0xd5,0x21,0x61,0xa6,0xe1,0x55,0x7e,0xe3,0xc2,0x1f,0x08,0x25,0xc0, + 0xdc,0x8d,0x98,0x66,0x13,0x51,0x98,0x5f,0x81,0x3f,0xbc,0x25,0x3f,0x8e,0x26,0x9f,0xeb,0x09,0x06,0x93, + 0xf9,0xeb,0x13,0x68,0xdc,0x6d,0x21,0xe6,0x21,0xda,0xab,0x36,0x57,0xb9,0x4d,0x37,0xcc,0xba,0x5e,0x71, + 0xdb,0x6a,0x6f,0x57,0x5f,0xd6,0xbb,0x1c,0x57,0x62,0xcb,0xf3,0xd7,0x1c,0xf7,0x8c,0xc4,0x7c,0x98,0x60, + 0x14,0x20,0x6d,0x9a,0x8f,0x3f,0x6c,0x55,0xf5,0xc2,0x5f,0x0a,0x55,0x77,0x9c,0x08,0x4b,0x7d,0xc7,0x11, + 0x1e,0x05,0xa5,0x5e,0xed,0x32,0x30,0xd1,0xf6,0x81,0x1a,0x69,0xb3,0xb3,0xae,0x13,0xf6,0xb8,0x2b,0x82, + 0xa5,0x8d,0xe2,0xe4,0x85,0x1e,0xc8,0x4d,0xd4,0xe9,0x4e,0x31,0xaa,0x6b,0x7e,0x1e,0x15,0x44,0xc2,0x20, + 0xce,0xb9,0x6c,0x67,0xe0,0x6c,0xe4,0x1e,0xda,0x36,0xae,0xad,0xaf,0xb7,0x20,0x42,0x5d,0x51,0xc4,0x9d, + 0x0c,0x4b,0xf2,0x06,0xa2,0x96,0x84,0x7c,0x54,0xc2,0x83,0xa1,0x61,0x88,0xa0,0x63,0x6e,0xbb,0x0c,0xae, + 0xe5,0x91,0x89,0x30,0x5e,0xbc,0x04,0x0f,0xb7,0x5e,0x68,0x4c,0x75,0x52,0xe5,0x24,0x10,0x26,0x3c,0x39, + 0xc5,0x3c,0x81,0xc2,0xc0,0x36,0x14,0x9a,0xea,0xeb,0x5d,0x7f,0x61,0x0e,0x70,0xc0,0xca,0x15,0x80,0xa1, + 0xcd,0x96,0x85,0x2e,0x91,0xcc,0x33,0xd4,0x65,0xff,0x4d,0x86,0x35,0x1f,0x5f,0x40,0x62,0x09,0x00,0xdc, + 0x9b,0x7a,0xb5,0x2e,0xbc,0x44,0x73,0x54,0x4f,0x0f,0xb8,0x52,0x12,0x2d,0x48,0x6d,0x9e,0x5b,0x0b,0x68, + 0x30,0xd9,0x41,0x93,0xc4,0x37,0xc7,0x4b,0x55,0xf4,0x76,0xfd,0x5e,0x13,0x8d,0x9f,0x60,0xb1,0x02,0xce, + 0xcd,0xfa,0x07,0x8e,0x34,0x47,0x23,0x60,0xad,0xae,0x8c,0x4e,0xf2,0x8d,0x62,0x1c,0x8b,0xe8,0x1e,0xef, + 0x31,0x69,0x01,0x99,0xe0,0xf1,0xdc,0x14,0x1b,0xf8,0x05,0xa7,0x79,0x53,0x6b,0xe9,0x96,0x2d,0x87,0x85, + 0xbb,0x2d,0x59,0xd0,0xab,0x83,0x22,0x8f,0x9a,0x8f,0xee,0x92,0x6a,0x62,0x54,0x51,0x44,0xcd,0xe1,0xb6, + 0x24,0x22,0x2d,0x6a,0xe2,0x86,0xf8,0x4d,0xa7,0x6e,0x5a,0x78,0x3f,0x4c,0x9e,0xae,0x1a,0x5e,0x1d,0x9f, + 0xa9,0x22,0xff,0xd6,0x88,0x2e,0xfe,0x2d,0x95,0xbd,0xed,0x10,0x8d,0xe0,0xba,0x36,0x27,0x1e,0x8e,0x36, + 0xa3,0x01,0x5a,0x0d,0x17,0x1f,0x2e,0x80,0x74,0x50,0x01,0x88,0x34,0x5f,0x08,0x54,0x05,0x30,0x90,0x01, + 0xf8,0x7a,0x39,0xdc,0x52,0x0a,0x5f,0x82,0xee,0xe9,0xd4,0xc2,0xf6,0xaa,0xe5,0xab,0x0a,0x5e,0xd0,0xd0, + 0x86,0x87,0x91,0xb3,0x38,0x32,0x2e,0x05,0x21,0xc6,0xe5,0x15,0x0d,0x22,0x7c,0x94,0x62,0x91,0x36,0x71, + 0x50,0xf3,0x46,0x50,0xf3,0x23,0xbe,0x99,0x1b,0xc1,0x65,0x5e,0x10,0x40,0xfd,0xb1,0xf8,0x44,0x4c,0x94, + 0x2c,0x9d,0xc9,0xa0,0xdd,0x0c,0x0f,0xf0,0xe0,0x33,0x90,0x95,0xc4,0x06,0x96,0xc4,0xd0,0x29,0x4c,0xd2, + 0x31,0xd4,0xe9,0x95,0xb1,0x99,0xf6,0xb5,0xd1,0x48,0x2f,0xb8,0x86,0xf0,0xa0,0x15,0xb3,0x99,0x44,0xd3, + 0x9b,0xa0,0x93,0x69,0x3a,0x6a,0xab,0x90,0x8f,0x71,0xee,0x1f,0xb7,0x6d,0x23,0x86,0xd6,0x60,0x3a,0xbf, + 0x8a,0xa3,0x53,0x69,0xd8,0xe4,0x87,0xf5,0x1b,0x7c,0xee,0x61,0xaf,0x21,0x25,0xf1,0x65,0xa2,0xeb,0x85, + 0x34,0x6e,0x25,0xc6,0xa8,0xc1,0x0a,0x9b,0x2a,0x22,0xff,0x00,0x30,0x86,0x86,0xcf,0x4a,0xc5,0xe2,0x6b, + 0x01,0xda,0x0c,0xf7,0x7f,0x4b,0x7a,0x93,0x17,0x62,0xe6,0xa1,0xac,0x4e,0x5e,0x38,0x8b,0x83,0x5b,0x3a, + 0xb3,0xa6,0x03,0x0c,0xbb,0x79,0x1e,0x2f,0x4a,0x9d,0xc1,0x7c,0xe4,0x03,0x88,0xd6,0xee,0xf8,0x04,0xcd, + 0x36,0x2f,0x81,0xd9,0x47,0x02,0x57,0x7b,0xce,0xc3,0x7f,0x1d,0xc1,0x49,0x40,0x53,0x25,0x70,0x5d,0x06, + 0x28,0x03,0x75,0xa9,0x51,0xdc,0x7f,0x53,0x0f,0x87,0xbb,0xbc,0x69,0x46,0x64,0xd0,0x6f,0x58,0x0d,0xe8, + 0xa0,0x02,0x1b,0xca,0x7a,0xf1,0xa4,0x67,0x0a,0x80,0x60,0x9b,0x3e,0x8a,0x95,0x87,0x08,0x84,0x13,0x83, + 0x7c,0x1b,0x05,0x47,0x35,0x7c,0x8f,0x81,0xac,0xcc,0xa2,0xfe,0xaa,0xfa,0x07,0x06,0xcc,0x6b,0x5f,0x1f, + 0xa0,0xf5,0xd6,0xf6,0x10,0x7e,0x56,0xe1,0x21,0x3c,0x88,0x20,0xc4,0x67,0x5f,0x80,0x09,0x60,0x73,0x5c, + 0xd6,0x54,0xfb,0xb7,0x26,0xdf,0x53,0x37,0x49,0x71,0xe6,0xb8,0x1f,0x6b,0xf4,0x92,0xef,0x04,0x27,0x51, + 0xc4,0x91,0x5d,0x45,0x80,0x3d,0x98,0x0c,0xd9,0x73,0x62,0x98,0xb7,0x32,0xcd,0x1d,0xec,0x43,0x3d,0xf1, + 0xd8,0x2a,0xb8,0xe1,0x03,0x72,0xa1,0x13,0xbb,0x80,0xa6,0x18,0xca,0x44,0x29,0x58,0xb3,0xc5,0x8e,0x88, + 0x4c,0x9f,0xcf,0x29,0x3f,0x4c,0x27,0x33,0x3d,0x5f,0x9c,0x04,0x34,0xa4,0x16,0xe3,0x17,0x67,0xf1,0xcc, + 0x7d,0x7b,0xb1,0x5d,0xc3,0xe7,0x59,0x8a,0xa2,0xc1,0xf3,0x4d,0x4a,0xcb,0x6a,0xc7,0x74,0xc1,0x7f,0x33, + 0xc1,0x48,0xae,0x2f,0x76,0x8c,0xcd,0x54,0x5d,0xd1,0x49,0x55,0xe7,0xde,0x2d,0x46,0x04,0xef,0xdb,0x3b, + 0xba,0x2b,0x74,0xfe,0x87,0xb7,0x88,0xc9,0xf5,0xcb,0x04,0x83,0x48,0x9e,0x44,0x57,0x18,0xd2,0xb1,0x9b, + 0xa5,0x55,0x61,0xa3,0x63,0x5d,0x9b,0x87,0x8d,0xce,0xcb,0xe3,0x10,0xd3,0x70,0xe0,0x0f,0x71,0xdf,0x17, + 0xfc,0x58,0x84,0x3f,0x1d,0xcd,0xd4,0xe3,0xd3,0xc5,0xe8,0x1d,0xb3,0x68,0xd8,0xb3,0x65,0x61,0x91,0xbd, + 0xec,0xa4,0x93,0x1d,0x77,0xb4,0x37,0x14,0x8b,0x48,0xb6,0xa3,0x89,0x63,0xa8,0xd2,0xf2,0xef,0x2d,0x5c, + 0x89,0x87,0x91,0x9c,0xc1,0xb5,0x6d,0xf3,0x2e,0xa4,0x8c,0x4c,0xcb,0x46,0x0b,0xf1,0x35,0xdc,0x7e,0x81, + 0xea,0x1d,0x51,0x33,0x4a,0x50,0xc2,0xab,0xac,0xf4,0x9c,0x0c,0xdc,0xa2,0x96,0x86,0xa7,0x62,0x3d,0x23, + 0xa8,0x9e,0x82,0xd6,0xd6,0xce,0xd0,0x65,0xc8,0x51,0xff,0xc3,0x08,0x67,0xbd,0x10,0x6f,0x2e,0x6c,0x2b, + 0x06,0xf0,0x4e,0xee,0x0a,0x58,0xdf,0xc9,0x7a,0x28,0xa1,0x18,0x18,0x77,0x3b,0x68,0x16,0x74,0xdc,0x13, + 0x86,0x6b,0xf4,0xc1,0x93,0xc7,0xba,0x89,0x2e,0xdb,0x8f,0x90,0xa4,0xae,0xd3,0xd7,0x87,0x43,0xa0,0x0b, + 0x98,0xbe,0xa5,0x6f,0x17,0x9c,0x41,0x50,0x7e,0xca,0x72,0xfa,0xfb,0xd1,0x3b,0xc0,0x82,0x48,0x5b,0x32, + 0x6f,0x58,0x8d,0xc1,0x8c,0x26,0x77,0x5b,0xde,0x6e,0x29,0xb4,0xb0,0x43,0xea,0x84,0xe5,0xfb,0x53,0x55, + 0xc3,0xe4,0x21,0x89,0x1e,0x29,0xf1,0x59,0xad,0x85,0xd7,0x2e,0xa8,0x81,0x40,0x88,0xa4,0x86,0xc2,0x14, + 0xe2,0xc4,0xc8,0xb5,0x9d,0x06,0x70,0x38,0xbe,0xc0,0x63,0xdd,0xa1,0xaf,0x57,0x35,0xc0,0xe3,0x2d,0x1d, + 0x6c,0x47,0x3e,0xa9,0xd8,0xc6,0x81,0x8d,0xf8,0xa2,0xa9,0xe1,0x60,0xe4,0x4c,0x01,0xf3,0x49,0x80,0x21, + 0xa2,0xf9,0x9f,0x07,0x1c,0x55,0x3b,0x76,0xf4,0x0c,0xbf,0x69,0xd0,0x17,0xbe,0x76,0xbb,0x18,0x5d,0x23, + 0xf4,0x1f,0x74,0x34,0x3a,0x74,0xdb,0x69,0xfe,0x85,0xc7,0x34,0x2f,0x8d,0x6d,0xef,0x94,0x0e,0x5b,0xbf, + 0x31,0x02,0x0a,0x8b,0x13,0x10,0xc7,0xff,0x49,0x48,0x3f,0x94,0xdb,0x63,0x2c,0xfe,0xd1,0x1d,0x1b,0x7f, + 0x18,0x84,0xa0,0x04,0xf5,0xa6,0x11,0xde,0xf8,0x2f,0xef,0xf2,0xd1,0x9b,0xaf,0xaa,0x42,0x14,0x28,0x21, + 0x00,0x15,0x0f,0xdf,0x63,0x50,0x38,0xf4,0xb3,0xca,0x94,0x0e,0x29,0x10,0xa4,0xf0,0xfd,0xd5,0xaf,0x5f, + 0xf2,0x7f,0x56,0x3c,0x95,0x88,0x1c,0x8f,0x4e,0x26,0xea,0x9d,0x54,0xee,0x55,0x27,0xd4,0xa0,0xdf,0x4a, + 0xb9,0x46,0x1a,0x90,0xbf,0x53,0x00,0x7c,0xb6,0x61,0x8f,0x0a,0x79,0x45,0xd5,0x3c,0xfa,0x75,0x21,0x98, + 0x41,0xc1,0xa3,0x74,0x2b,0xdf,0x1b,0xb9,0xab,0xe9,0x36,0xf1,0x50,0xa3,0xa2,0xc3,0x83,0xf7,0x09,0x44, + 0x41,0xf5,0x4f,0x39,0xf4,0x84,0xc4,0x94,0xd8,0x90,0x83,0xdd,0xed,0xca,0x0d,0xc2,0xf8,0x4d,0x7c,0xe5, + 0x20,0x06,0x96,0x0f,0xcf,0x97,0xee,0xea,0xb5,0x96,0x77,0x05,0xf7,0xad,0xf3,0xef,0x2f,0x9f,0xc5,0x5c, + 0x19,0xcc,0xb4,0xfa,0xd0,0x0d,0x51,0x33,0x54,0xc7,0x3b,0x28,0x52,0x2f,0x04,0x27,0x30,0x1e,0x08,0x83, + 0xe8,0x4b,0xa6,0xed,0x04,0x88,0xdb,0x9b,0xe4,0xd1,0x6d,0x27,0x6e,0x10,0x44,0xb4,0x4f,0x1e,0xe1,0x87, + 0xfd,0x37,0x71,0x28,0x38,0xf0,0x4c,0x94,0x23,0x30,0x48,0x10,0x2f,0xa3,0x57,0x02,0xa2,0x69,0x90,0x9e, + 0x90,0x86,0x0b,0xf9,0x9d,0xc2,0x56,0xdb,0x7c,0xac,0x03,0x13,0x6a,0x24,0x94,0x97,0x34,0xf4,0xbc,0x2f, + 0xe1,0x62,0xb4,0x39,0xaa,0x0f,0x16,0x45,0x84,0xc2,0x1c,0x0d,0x86,0xb1,0x3c,0xac,0x9c,0x09,0x19,0xb8, + 0x6c,0x6d,0xd8,0xbe,0xef,0x7f,0x83,0x49,0x78,0xdc,0xf2,0x5a,0xbf,0xff,0x8c,0x75,0x7f,0x84,0x66,0x7f, + 0x59,0x50,0xed,0x75,0xf3,0xc3,0x35,0x08,0x83,0x3e,0x4c,0x0e,0xf0,0xec,0x59,0x6a,0x52,0x70,0x0a,0xae, + 0xa4,0x74,0x33,0xe5,0x46,0x0c,0x19,0xae,0x82,0x6c,0xf6,0x94,0x91,0x2b,0x76,0x60,0x82,0xb7,0xbc,0x93, + 0x3e,0xee,0xff,0x55,0x00,0x18,0x6a,0xd0,0x4d,0x90,0xc2,0x04,0x1c,0x75,0x4a,0x38,0x70,0x55,0xb7,0x3d, + 0x46,0xd4,0x19,0x87,0x00,0x39,0x83,0x68,0x9b,0x1b,0x79,0x8c,0x45,0x7f,0xe3,0x42,0x1d,0x79,0xa0,0x52, + 0xf7,0xa8,0x71,0x22,0x4e,0x57,0x64,0x8a,0xc6,0xad,0xed,0xdd,0xf0,0xaa,0xae,0xcb,0xbb,0x2a,0x3a,0x87, + 0x3b,0x91,0x81,0xa8,0xb6,0xab,0x5b,0x08,0xb9,0xb0,0x59,0x58,0x8d,0x8c,0xc1,0x0f,0x25,0xba,0x73,0x1a, + 0x76,0x68,0xbe,0x68,0xc1,0x17,0xaa,0x47,0x6a,0x7b,0xbe,0xaa,0x9a,0x76,0x8a,0x9a,0x56,0x62,0x68,0xf5, + 0xa3,0x80,0x59,0x46,0x52,0x14,0x5b,0x6c,0xcf,0x60,0xd6,0xb2,0xba,0xf9,0x91,0xe9,0xcf,0xb6,0xc6,0xcd, + 0xb1,0xfa,0x1d,0xc4,0xad,0x9b,0x77,0x0d,0x53,0xe5,0xfa,0xb9,0xce,0x59,0x23,0x3c,0x9c,0x1b,0x01,0x92, + 0x39,0x11,0x4d,0xf2,0xfb,0x4c,0xa4,0xe9,0x17,0x23,0xf9,0xbf,0xc1,0xc3,0x4a,0xcd,0x07,0x7d,0x9b,0xa6, + 0xd5,0x26,0x65,0x36,0xec,0x3a,0x9e,0x68,0x31,0x2f,0xed,0x59,0x62,0x89,0x47,0x78,0xe7,0x6b,0x8f,0x54, + 0x45,0x4e,0x7c,0x8a,0x53,0x9f,0x7b,0x7a,0xcf,0xdf,0xed,0x61,0x5f,0x7a,0x86,0xed,0x7e,0xfc,0x9f,0x1f, + 0x7f,0xf1,0xe9,0x97,0x76,0x72,0xf6,0xd2,0xce,0xa7,0xd9,0x17,0x76,0xed,0x7e,0x96,0xde,0xd2,0xef,0xe7, + 0x9e,0xfe,0x87,0x3d,0x48,0xa6,0xaa,0xe1,0x0b,0x39,0xe0,0x1e,0x11,0x94,0x39,0xa9,0x2d,0xf3,0x86,0xff, + 0xfb,0xd3,0x95,0x89,0x8e,0xab,0x13,0xcc,0x86,0xc4,0xc2,0x39,0x69,0x2f,0xd7,0x1b,0x2e,0x4f,0x7c,0xda, + 0xbe,0x62,0x8d,0x7d,0x11,0x35,0x00,0x8a,0xab,0xb1,0x67,0x5d,0x21,0x06,0x01,0x95,0xdb,0xf3,0x00,0xd3, + 0xe8,0xc9,0x28,0x83,0xf0,0x4d,0xb5,0x22,0xee,0x91,0xa6,0xa7,0x9e,0x8f,0x62,0xd8,0xe5,0x3d,0x87,0xf1, + 0x38,0x10,0x8f,0x22,0xf1,0xeb,0x44,0x77,0x4f,0x47,0x38,0x68,0xc6,0x6b,0x96,0x3e,0xe5,0xf8,0x7f,0x42, + 0x11,0x4d,0xc4,0x07,0xfc,0x9c,0x9f,0x5b,0xe9,0xb8,0x5b,0xe1,0xc8,0xe0,0x54,0x8c,0xa2,0x32,0xfc,0x3a, + 0x4b,0x11,0x2c,0xa1,0x5e,0x3d,0xe4,0x8d,0x09,0xea,0x20,0xfd,0x65,0x4b,0xbd,0xec,0x0d,0x97,0xc0,0xb8, + 0x59,0x3b,0x9d,0x54,0x33,0x5c,0xf4,0x7b,0x62,0x0e,0x00,0x4a,0xcd,0x91,0x81,0x18,0x47,0x61,0xdb,0x33, + 0x7e,0x61,0x08,0x50,0xad,0x5a,0x08,0x4b,0x09,0xc5,0x74,0xa3,0x85,0x8d,0x68,0x00,0x84,0x7f,0x5f,0x14, + 0x18,0x94,0xe7,0x69,0x76,0x71,0x97,0xc1,0x75,0x49,0x83,0xa4,0xbf,0xe3,0xc9,0xc6,0x32,0x29,0xb3,0x24, + 0xd2,0x0a,0x9d,0xbc,0x5c,0x5e,0xe5,0xdd,0x79,0xff,0x57,0x8f,0x90,0x66,0xb7,0x24,0x47,0x49,0xe5,0x94, + 0x53,0xc2,0x5c,0x6c,0xdf,0xa0,0x95,0x71,0x30,0x4d,0x91,0x84,0xdf,0x62,0x73,0x91,0x25,0xf1,0x97,0xc6, + 0x9b,0x58,0x20,0xc6,0xe1,0xf8,0x18,0xed,0x7e,0x40,0x71,0x7b,0x3c,0xac,0x54,0x7a,0x74,0x8d,0x9c,0x9d, + 0x62,0x19,0xed,0x6c,0xfc,0x03,0xc8,0x22,0xc1,0xa5,0x78,0x83,0xd1,0xde,0x85,0xfe,0x1e,0x4e,0xa7,0x30, + 0xd5,0x3d,0x69,0x39,0x43,0x86,0xe4,0xa1,0x7a,0x89,0x7f,0xfa,0x4d,0xc3,0x12,0xb6,0x48,0xe8,0x8a,0x39, + 0x57,0x79,0xa3,0x6d,0x28,0xf4,0xaf,0x8f,0x67,0xaa,0xc7,0xcc,0x2d,0x23,0x62,0x75,0xb4,0x10,0x2f,0x53, + 0xd2,0x9c,0x8c,0x61,0xf3,0xd5,0xa8,0xf3,0xd7,0x6d,0x6a,0x7b,0x1b,0x5b,0xfc,0xeb,0xdb,0x10,0x41,0xef, + 0x47,0xb3,0x3f,0xe3,0x42,0x89,0x28,0xe4,0xe0,0xfe,0x75,0x24,0xb2,0xea,0xc2,0x1a,0x68,0x12,0xaf,0x32, + 0x70,0xe0,0xf1,0x14,0x9c,0x9d,0xe4,0x15,0x7e,0x03,0xa7,0xe5,0xaf,0x1c,0x38,0x7b,0x8d,0x18,0x0a,0x03, + 0x3a,0x02,0xf8,0xc8,0xa8,0xc6,0x25,0x85,0x50,0xb9,0xd2,0x03,0xf1,0x11,0x84,0xb5,0x18,0x78,0x0e,0x6c, + 0x47,0xfb,0xac,0x22,0xc3,0x44,0x2b,0x10,0x6c,0x8c,0x4d,0xc2,0xc3,0x0d,0x7a,0xd7,0xfd,0x9c,0x52,0xd5, + 0x4c,0x0e,0x21,0xf2,0x42,0xef,0xf2,0xe3,0x6d,0xf7,0xd9,0x40,0x0e,0x58,0x37,0xc4,0xc3,0xaa,0x65,0x2e, + 0xb7,0x3c,0xba,0x2c,0x49,0x38,0x05,0xbb,0x95,0xb1,0x95,0xd7,0xcc,0xf6,0x56,0xc4,0x22,0x52,0xec,0xb1, + 0x93,0xb8,0x21,0xae,0xcf,0xfa,0x68,0xba,0x21,0xa5,0x12,0x7e,0xef,0x83,0x29,0x1c,0x93,0x46,0x47,0x44, + 0xd1,0x45,0x37,0xec,0x7f,0x04,0xc4,0xf2,0xf5,0xbc,0x8f,0xcf,0x90,0x61,0x49,0xf2,0xf5,0xb7,0x71,0xdf, + 0xe3,0xef,0x1e,0x0d,0xcf,0xec,0xaf,0x7e,0x9d,0x8f,0x7f,0x4f,0x90,0xef,0x28,0x39,0xc3,0xda,0xf8,0x6b, + 0xae,0x46,0xeb,0x62,0xd4,0x84,0x79,0x3f,0xed,0x32,0x4c,0x0f,0x32,0x0e,0x15,0x48,0x61,0x50,0xd8,0x61, + 0x7b,0x42,0x5a,0xfd,0xb1,0xad,0x71,0x6f,0x5b,0x51,0x1d,0x0d,0xf5,0x46,0x5f,0x7d,0x5b,0xf4,0xdf,0x0d, + 0x11,0x10,0x95,0xb3,0xf1,0x2d,0x48,0x31,0x37,0xd2,0xdb,0xd5,0x97,0xf5,0xd0,0x8f,0x47,0x0b,0x95,0xf0, + 0x8d,0x7c,0x75,0xe2,0x27,0x34,0x5d,0xd0,0x50,0x4d,0x43,0x19,0x4c,0x08,0x03,0xad,0x49,0x26,0x80,0xab, + 0x88,0x01,0x7a,0x91,0x86,0xeb,0x58,0xd8,0x0d,0xe1,0xdf,0x24,0x02,0xa3,0x30,0x70,0x4e,0xdd,0xc1,0x70, + 0xb7,0x8f,0x88,0xac,0xcf,0x72,0x1c,0x93,0x26,0x5b,0x14,0xc2,0xd1,0x33,0x04,0xcf,0xe3,0x27,0x6e,0xd8, + 0x27,0x22,0x45,0x0d,0x27,0x78,0x38,0xa5,0x1f,0xd0,0x36,0x39,0xa5,0x90,0x05,0x91,0xd0,0xac,0xcc,0x57, + 0xdf,0x91,0x91,0xb6,0x26,0xa5,0x76,0x28,0xd1,0x05,0xc1,0x4e,0xcd,0xc2,0x06,0x68,0x96,0xb7,0x85,0xf9, + 0xc8,0x0a,0x02,0x37,0x21,0xf0,0x36,0xc6,0xdc,0xf2,0x7c,0x1c,0x2e,0x22,0xf7,0x7e,0x23,0x6d,0x47,0xcd, + 0xff,0xab,0xa8,0x75,0xcb,0x0c,0xce,0x0d,0xde,0x1f,0x19,0xcf,0xf8,0xb9,0xf8,0x32,0xb4,0x95,0x8a,0x96, + 0x0e,0xe2,0xec,0x39,0x47,0x8a,0xa1,0x81,0x22,0x7b,0xdc,0x4d,0x67,0x88,0x52,0x85,0xa1,0x87,0xe9,0xeb, + 0xc5,0x0a,0x20,0x43,0x8a,0xa4,0x2b,0x3c,0x2b,0xd7,0x60,0x29,0x5c,0x9c,0x80,0x71,0x7b,0xee,0xaa,0xda, + 0xbb,0x1f,0x53,0x82,0xd0,0xd4,0x28,0x87,0x5c,0xac,0x12,0xbf,0x37,0xc2,0x86,0x04,0x86,0x7d,0x29,0x51, + 0x30,0x74,0xdf,0xa3,0x63,0x21,0xda,0x87,0x76,0x4e,0xe9,0x8d,0x5d,0xbb,0xd3,0x49,0xc7,0x14,0x99,0xac, + 0xe4,0xe3,0x2d,0xef,0x5d,0x64,0xe8,0x3d,0x66,0x93,0x39,0xbe,0x74,0x54,0x6b,0x58,0x35,0xd5,0x67,0x40, + 0xdd,0x59,0x57,0x14,0x17,0x5d,0x77,0x20,0x6c,0x59,0x8e,0x90,0xfc,0x64,0x8b,0x0d,0x64,0x6d,0x13,0x84, + 0xc9,0x5e,0xa4,0x76,0x86,0x30,0xc2,0x23,0x52,0x18,0xd3,0xa5,0x76,0x85,0xa3,0x46,0x68,0x61,0xb9,0xc6, + 0x2c,0xd4,0xbf,0xad,0x11,0x4a,0xce,0xf9,0x54,0xe0,0x49,0x21,0x71,0x19,0x3f,0x9a,0x35,0x70,0xb5,0xe9, + 0x02,0xeb,0xcb,0xba,0x70,0x0b,0xfd,0x58,0x03,0xe3,0x54,0xb7,0xd4,0x91,0x0f,0x2a,0x6b,0x11,0xfe,0x99, + 0x08,0x75,0xd3,0xf6,0xa3,0x9f,0x78,0x67,0x8b,0xe1,0x42,0xa6,0xaf,0x76,0xe4,0xee,0x85,0x15,0x85,0xe4, + 0x2c,0x27,0x3c,0x9a,0xb4,0x2c,0x48,0x47,0x82,0x64,0xbe,0x1f,0xa6,0x7b,0x1e,0x6c,0x1e,0xb4,0x86,0x8f, + 0xcf,0x34,0x91,0xb5,0x3c,0x36,0xf0,0x8b,0xbf,0x46,0x49,0xaa,0x8f,0x2a,0x38,0x4b,0xf9,0x70,0xdf,0x7f, + 0xf7,0xcf,0x45,0xff,0xa6,0x18,0xe4,0xdb,0x51,0x8f,0xd8,0x9f,0xe1,0x33,0x2b,0x07,0x22,0x18,0x48,0x5b, + 0x62,0x19,0x38,0xe1,0xa1,0x34,0x98,0x69,0xb8,0x83,0xbc,0x40,0x86,0x39,0x6a,0xdd,0xc7,0x5b,0x54,0xc9, + 0x0a,0xbc,0xd1,0x1d,0x34,0x43,0x2b,0x51,0x1b,0xdd,0x61,0xae,0x61,0x87,0x47,0x0b,0x6c,0x0f,0x55,0xe5, + 0xe5,0xb0,0x10,0x35,0x0f,0xc0,0xb3,0x72,0xcc,0x8c,0x26,0x0a,0x14,0x50,0x49,0x7f,0xea,0xb3,0x1f,0x9e, + 0x29,0x6a,0xc3,0x37,0xd7,0xd0,0x38,0xb3,0xa7,0xfd,0x5c,0x49,0xb9,0x0b,0x3d,0xa4,0x2d,0x5e,0x0e,0x63, + 0x13,0x4a,0x9d,0x60,0x6c,0xc1,0xdf,0xc2,0x30,0xe2,0x14,0x1d,0x8b,0x4e,0xc1,0x3a,0x9d,0x5b,0xe8,0x17, + 0xb4,0x11,0x04,0x12,0x7d,0x71,0x06,0x2f,0x2f,0xc3,0x1b,0xda,0xf9,0x2e,0xb2,0xe8,0x39,0x0e,0x4d,0x3a, + 0xa1,0xaa,0x5e,0xfc,0xa8,0xf1,0xea,0x1b,0xfe,0xb6,0x1c,0x32,0x2b,0xa7,0xd1,0xbd,0x97,0x02,0x5e,0xa5, + 0x66,0xfe,0x63,0x8d,0xe2,0x1d,0x5d,0xe8,0x28,0x7f,0x05,0xeb,0xd0,0x34,0x24,0xdd,0xc9,0x2c,0x55,0xe0, + 0x23,0x6d,0xf0,0xae,0x5f,0xf8,0xfa,0x45,0x9e,0x42,0xa5,0xc7,0xb5,0x79,0xbd,0x36,0xb7,0xfd,0x03,0x34, + 0x84,0xeb,0x50,0x0b,0x7e,0x50,0x6b,0xfd,0xfe,0xbd,0x65,0xe0,0x75,0x4a,0x95,0x9c,0xdd,0x0e,0xde,0xd1, + 0x77,0xd5,0x1d,0x96,0x70,0x6a,0x0d,0x70,0xfc,0x46,0x78,0xc8,0x54,0x4d,0xdd,0x92,0xe4,0xa6,0x06,0x9d, + 0x59,0xff,0x57,0x9c,0x1a,0x07,0x75,0xc7,0xdc,0xb4,0x14,0x33,0xbe,0x82,0x24,0x65,0xe9,0xba,0x44,0xd0, + 0x28,0xe0,0x18,0x98,0xd3,0x6e,0x9b,0x66,0xa4,0x66,0x4f,0x2a,0x83,0x89,0x88,0xd8,0xce,0x10,0x22,0xb5, + 0x3c,0x3a,0xed,0xa6,0xa0,0x60,0x91,0x56,0x6c,0xc9,0x3b,0x37,0x87,0x3c,0x7e,0x87,0x49,0x8a,0xa0,0x30, + 0x75,0x00,0xa3,0x08,0xe0,0xa2,0x77,0xde,0x20,0x81,0xd7,0x02,0x85,0x58,0xb2,0x46,0xc5,0xe3,0x09,0x20, + 0x9b,0xe7,0x51,0x18,0x36,0x60,0x8c,0xf0,0x9f,0xfe,0xa2,0xdb,0x85,0xc7,0x4f,0xfb,0xaa,0x8b,0x8a,0x95, + 0x15,0xca,0xb4,0x4d,0x29,0x54,0xdd,0x5f,0xed,0xd0,0x28,0x6c,0xe4,0x16,0x02,0x38,0xa0,0x45,0xef,0x9e, + 0x81,0xf6,0x70,0x98,0x34,0xb2,0x52,0xf8,0x71,0xfd,0x2c,0xb2,0x1f,0xf2,0xb0,0xf9,0x40,0xe5,0x44,0xf3, + 0xd7,0x9d,0x95,0xd1,0x49,0xb8,0x8c,0x1e,0x07,0xe0,0xb5,0x5c,0x6a,0xd8,0xf0,0x8d,0x4a,0x74,0x4d,0x1e, + 0xaa,0x90,0xe5,0x97,0x27,0x2a,0x89,0xae,0xaf,0x8d,0x16,0xfe,0xb3,0xb3,0xd9,0x3f,0x0c,0x38,0x0b,0x41, + 0x5c,0x2f,0x0d,0xed,0x0b,0x67,0x32,0x3b,0x34,0xe4,0x48,0xaa,0x84,0xc2,0x6a,0xdc,0xb4,0x40,0x78,0x3d, + 0x95,0xed,0x49,0xc3,0x58,0x3a,0x15,0x94,0xa0,0x62,0x5c,0x37,0x1d,0xe9,0x6b,0xfe,0x4e,0xfd,0x74,0x10, + 0x79,0x08,0x9f,0xcb,0x3f,0x56,0x6f,0xe1,0x6b,0x7c,0x3a,0x65,0x21,0xa1,0xec,0x47,0x3e,0x87,0xda,0xdc, + 0x8b,0x60,0x3a,0xc7,0x4e,0x3f,0xd6,0x7d,0x64,0xba,0x70,0x8b,0x66,0x58,0x61,0x24,0x49,0x42,0xc4,0x5f, + 0xc6,0x62,0x93,0x14,0x46,0x1d,0x93,0x6c,0x09,0x0f,0x54,0x15,0x2d,0x99,0xc4,0x24,0x25,0xcc,0xc2,0xe2, + 0xde,0xde,0xc3,0x57,0x0b,0x8b,0x80,0x3e,0xad,0xc0,0xc3,0x77,0x10,0xca,0x1e,0x68,0x05,0xbd,0x03,0xb7, + 0xc0,0x96,0x30,0x70,0xf7,0xf2,0xc2,0xe2,0x29,0xfc,0x3d,0x95,0x80,0x1a,0xdd,0x0e,0x0d,0x47,0xc3,0x82, + 0x65,0xa2,0x8a,0xf2,0x77,0x56,0x6e,0xd8,0x3e,0x05,0x3d,0x5f,0x5d,0x19,0x25,0xa4,0x87,0x07,0x09,0x3d, + 0xee,0x02,0x2e,0x00,0x54,0x36,0xcc,0xdd,0xcf,0xff,0x99,0xb7,0x41,0x26,0x3c,0xe7,0xaf,0x39,0xae,0x3c, + 0x8b,0x18,0x26,0x91,0xe4,0xd3,0xb0,0x72,0xb6,0x25,0xa5,0xc8,0xf4,0x11,0xe7,0x74,0x08,0xa0,0x02,0xe6, + 0xa5,0xf2,0x32,0xdb,0xe3,0x40,0x04,0x16,0x2f,0xe0,0xbe,0x62,0xb5,0x10,0x44,0x69,0xad,0x52,0x5a,0xab, + 0x98,0xfb,0x49,0x59,0x6c,0x4e,0x01,0xe2,0x64,0xa8,0x79,0x14,0x7d,0x7f,0x5d,0x8e,0x9d,0x65,0xfd,0x96, + 0x61,0x82,0x99,0xdf,0x80,0x9f,0xc8,0xed,0x18,0xac,0xbc,0xb5,0xd5,0x0c,0x7b,0x5c,0xfa,0x7b,0x4f,0x7c, + 0xab,0xce,0x95,0xbb,0xeb,0x4d,0x26,0x11,0x84,0xed,0xd6,0x15,0x7f,0x23,0x56,0xe6,0xfd,0xcd,0x18,0xb2, + 0x5e,0x14,0x49,0x81,0x59,0xca,0x67,0x3b,0x1d,0x70,0xf3,0x1e,0x25,0x64,0x1e,0xeb,0xc2,0x28,0xdb,0xe8, + 0xc7,0xeb,0x96,0x4d,0xb3,0xaa,0x12,0x99,0xb6,0xe4,0xa8,0x44,0x74,0xbc,0x5a,0xb0,0x37,0x13,0x4d,0x35, + 0xee,0x7a,0xa8,0xf4,0x47,0xed,0x8a,0x96,0x88,0x3e,0x55,0x2f,0xbe,0x84,0xbe,0x34,0x24,0x69,0x05,0xb4, + 0x94,0xb0,0x5e,0x16,0xfc,0x33,0xf6,0x54,0xda,0xcc,0x11,0x9b,0x4d,0x20,0xb3,0x73,0x9b,0xe7,0x2a,0x47, + 0x13,0x5d,0x31,0xc2,0x49,0x5c,0x6f,0x44,0xcf,0x80,0x2a,0x31,0x90,0x5d,0xb0,0x52,0xd5,0x3e,0xff,0x9d, + 0x6d,0x09,0x29,0x9b,0x37,0x6a,0x2a,0x4a,0x3e,0xcd,0x2a,0xb6,0x07,0xc3,0xb6,0x91,0xb6,0x24,0x27,0xe5, + 0xb7,0x48,0x7a,0xad,0x5f,0xb8,0xb7,0x1f,0x5c,0x0c,0xd5,0x2a,0x3e,0xbb,0xad,0x63,0xcb,0x9b,0xe5,0xf9, + 0x43,0x31,0xc3,0x33,0x41,0x6c,0xfa,0x67,0xc9,0xdf,0x9a,0x42,0x32,0x6b,0xb4,0x1e,0x46,0x8e,0x4f,0xcc, + 0x6d,0x26,0x2a,0x47,0xb9,0x4a,0x39,0xf1,0x7d,0x94,0x75,0xfe,0x60,0x6e,0x11,0xd6,0xed,0xa9,0x55,0x5a, + 0x39,0x32,0x42,0xe1,0xa6,0x22,0xaf,0xba,0x22,0x70,0x23,0x0f,0x79,0x78,0x83,0xdc,0xc1,0xf8,0xd7,0x59, + 0xc2,0x73,0x34,0x1c,0xde,0x5e,0x8c,0x2a,0x40,0xb5,0xc2,0x2a,0xcb,0xda,0x7d,0x18,0x7d,0xc5,0xcb,0xd4, + 0xe9,0x87,0x57,0xa3,0x5f,0x38,0x45,0xdb,0x7f,0x75,0x84,0xda,0x2d,0xc7,0x7e,0x18,0x2e,0x9e,0xd2,0x6d, + 0x34,0xd2,0xc1,0x96,0x00,0x81,0xac,0xf3,0xa0,0x04,0x84,0x65,0x4f,0x50,0xfc,0x84,0xb9,0xf0,0xcd,0xf1, + 0xd2,0xfc,0x0d,0x3b,0xa4,0x77,0x11,0x2b,0x55,0x3e,0xbe,0x90,0xd3,0x29,0xf8,0xb6,0xac,0x51,0xf0,0x04, + 0xaa,0x08,0x03,0x32,0x02,0xc2,0x8f,0x2c,0xcc,0x7f,0x5a,0x9b,0x64,0x7d,0xc0,0x0c,0xa5,0x12,0xd1,0x26, + 0x0b,0x9f,0x92,0x0e,0xcb,0x57,0xa6,0xfd,0x7f,0xe7,0xfe,0xcf,0x8a,0xa7,0xd0,0x3f,0xb1,0x57,0xc1,0xc5, + 0xd2,0xdb,0x48,0x90,0x6d,0x90,0xea,0xef,0x08,0xa8,0xca,0x4f,0x2f,0x01,0xab,0xaf,0x4b,0x42,0x4d,0x29, + 0x8c,0x22,0x7b,0x29,0xdd,0x27,0xdc,0xb4,0x21,0x89,0x70,0x7e,0x7a,0x69,0x92,0x29,0xe9,0xaa,0x49,0x89, + 0x47,0x71,0xeb,0x54,0x0a,0xdb,0x4d,0x9a,0xcb,0x36,0x84,0x85,0x19,0x07,0x7f,0x31,0xb6,0x1a,0x16,0x28, + 0x1d,0x71,0xf7,0xd7,0x6d,0x59,0x64,0xff,0x2c,0xfa,0x84,0x49,0x8f,0x5b,0x26,0xf3,0x22,0x65,0x33,0xe6, + 0xd0,0xc8,0xb4,0x2e,0xa4,0x5d,0x3d,0x99,0x9f,0x23,0x0f,0x32,0x9e,0xc2,0x72,0x40,0x03,0x5d,0x75,0x0b, + 0x4b,0x2c,0xe9,0x64,0x88,0xd2,0x02,0xb5,0xb0,0x55,0xc1,0x12,0x5f,0x6b,0x9e,0x44,0x2c,0x0c,0x2f,0x01, + 0x2f,0x0d,0x2f,0x52,0x62,0x10,0x25,0x88,0x36,0x7e,0xcd,0x2b,0x9b,0x54,0xfa,0xe7,0xfe,0xbc,0xf2,0x93, + 0xe3,0xbc,0xa2,0x0b,0xe5,0xbe,0xbf,0x8a,0x2c,0x2b,0x12,0x46,0x10,0xb6,0x62,0xf5,0x98,0x76,0xe9,0x49, + 0x28,0x14,0x08,0x35,0x9b,0x97,0x02,0xb4,0x95,0x9a,0x0f,0x0b,0x0c,0x4d,0xbb,0x83,0xbc,0x50,0x09,0xde, + 0xed,0x20,0x59,0x3b,0x45,0xb4,0xdd,0x90,0x08,0xa4,0x2d,0x3c,0x8c,0x11,0x2e,0x79,0xa0,0xea,0xa5,0xa8, + 0x3d,0xe3,0x61,0x50,0xa6,0x32,0x33,0xd7,0x91,0x83,0xee,0x78,0xfc,0x5c,0x1e,0xe3,0xe1,0x35,0x3f,0x77, + 0x16,0x67,0xb7,0x80,0x55,0x18,0x6d,0x5d,0x63,0x78,0xb9,0xd6,0xf0,0xff,0x5f,0x51,0xc5,0x22,0xd1,0x45, + 0xf7,0xb9,0x3e,0xa6,0x94,0xcd,0x68,0xe1,0x32,0xc5,0x09,0x73,0xb1,0x6a,0x42,0x70,0xe6,0xb9,0x48,0x31, + 0x54,0x41,0x0e,0x70,0x1d,0xaf,0x81,0x2b,0xc3,0x2e,0x09,0x70,0xe8,0xfe,0x24,0x8e,0xd5,0x9c,0x74,0x09, + 0x9a,0x0d,0x92,0xd0,0x36,0xd0,0x26,0x7f,0x4d,0xf9,0x18,0x70,0xc5,0xf0,0xa5,0xdb,0x2a,0xeb,0x2a,0x2e, + 0x7e,0xdc,0xea,0x47,0x6b,0x24,0x1b,0x3c,0xcf,0x12,0xca,0x6b,0x7c,0x07,0xef,0xe6,0x59,0xca,0x70,0x67, + 0xb0,0xf0,0x0b,0x7e,0xf8,0xa4,0x4a,0x66,0xac,0x60,0x93,0xd9,0xbe,0xb4,0x27,0x89,0x64,0xfe,0x9a,0x93, + 0xef,0xfd,0x24,0x7b,0x55,0x0c,0xd4,0x9c,0x17,0x5f,0x02,0x9e,0xfa,0xb9,0xab,0xde,0x99,0xfb,0xca,0x06, + 0x60,0xb3,0x85,0x05,0xc8,0x62,0x72,0xd1,0x92,0x34,0xe7,0xce,0xa9,0x9d,0xe1,0x9f,0x14,0x77,0xdd,0x5f, + 0x46,0x0e,0x22,0x32,0x26,0x29,0x8d,0x90,0x15,0x7d,0xb5,0xe1,0x08,0xd8,0xed,0xcb,0x10,0x1f,0x51,0x62, + 0x11,0x7c,0x1d,0x00,0x94,0xc1,0x07,0xb8,0x97,0x89,0x6b,0xa9,0xd3,0x0b,0x74,0x8e,0x76,0x61,0x23,0x76, + 0x16,0x06,0x52,0x5e,0xee,0x73,0x74,0x77,0xc1,0xfa,0x09,0x7c,0xb5,0x77,0xec,0x69,0x40,0x28,0x42,0xe2, + 0xc0,0x22,0x5c,0x39,0xbb,0x7e,0x21,0xc7,0x95,0x11,0x82,0xc2,0x82,0x6b,0xb7,0x7d,0x79,0x12,0x6a,0x26, + 0x44,0xfe,0xa3,0xd1,0x2b,0x85,0xfc,0x21,0x0b,0xbb,0x69,0x9a,0x41,0xfd,0x90,0x82,0x85,0x72,0xfa,0x4a, + 0x96,0x2e,0xa6,0x0a,0x97,0x58,0x7d,0x47,0xf7,0x2c,0x2e,0xa0,0x73,0x43,0x12,0x0f,0x0c,0x04,0x8d,0xef, + 0xff,0x89,0x7f,0x6f,0x19,0x36,0x43,0x7d,0x2b,0x44,0x7c,0x50,0x0c,0x43,0x4e,0x51,0xba,0xd3,0x72,0xfa, + 0x9a,0xaf,0x26,0x6c,0xf3,0x93,0x7c,0x65,0xe2,0xa7,0x05,0x74,0x4c,0x8b,0x5a,0xb6,0xba,0xa2,0x24,0x97, + 0xdd,0xd1,0x5c,0x14,0xd5,0x15,0x72,0x48,0xd8,0x50,0x5c,0x1d,0xaf,0x78,0x65,0x34,0x16,0xc7,0xf0,0x43, + 0xfd,0xe0,0xeb,0x76,0x11,0xf3,0x92,0x6d,0x0c,0xf0,0xb5,0xe0,0x58,0x1d,0x53,0xb6,0x2b,0xbe,0x0b,0x10, + 0x40,0x29,0x53,0xf9,0x92,0xbf,0xff,0x24,0xd0,0xd3,0x3f,0xec,0x0d,0x66,0xf3,0xcd,0x52,0x2b,0x95,0x12, + 0x49,0x12,0xfc,0x8d,0x20,0xd1,0xd9,0xf1,0x8c,0x10,0xe6,0x22,0x32,0x94,0xe9,0x93,0x51,0xd9,0x8f,0xbc, + 0xd5,0xa2,0x39,0xcb,0xb1,0xcd,0x4f,0xfe,0x93,0x93,0xfe,0x57,0x07,0xbc,0x04,0x96,0x5c,0xbc,0x23,0x86, + 0x62,0xeb,0x51,0x1c,0x1e,0x6d,0x7b,0x8b,0x0d,0x9c,0x72,0x79,0x02,0x39,0x05,0xda,0x09,0x73,0x7f,0x55, + 0x29,0xbd,0xce,0xc7,0x24,0x5e,0xb6,0xef,0xee,0x9c,0xf8,0x43,0x1e,0xcc,0xaa,0xd9,0x47,0xb7,0x77,0x82, + 0x7d,0xf8,0x90,0x77,0x95,0xce,0xe5,0x95,0x6f,0x5d,0xcf,0x3b,0xd2,0xfb,0x8e,0x95,0x6b,0xbe,0x13,0xf0, + 0x73,0xc1,0xd9,0x00,0xf0,0x1f,0x50,0xe5,0x11,0x8a,0x07,0x2e,0x09,0x6b,0x1a,0xa8,0x2d,0xd2,0x21,0x01, + 0x7e,0xd8,0x1e,0xe4,0x51,0x82,0x11,0xf8,0x04,0x0b,0x0c,0xfa,0x89,0x03,0x08,0xa4,0x07,0x4a,0x4e,0x0e, + 0x9c,0x51,0xfc,0x02,0x26,0x8c,0x46,0xdf,0xb2,0xb6,0x13,0x84,0x4a,0xd5,0xe5,0x82,0xd3,0x01,0xc9,0x4e, + 0x65,0xa4,0x70,0x15,0x41,0xc1,0x64,0x55,0x85,0x76,0xf8,0x76,0x3a,0x45,0x0a,0xff,0xbd,0x7b,0x0c,0x82, + 0x11,0x03,0xad,0x49,0x42,0xa2,0x7a,0x1e,0xa2,0xc4,0x71,0xb1,0x74,0x2a,0xc1,0x3a,0x32,0x23,0x72,0x1e, + 0xd2,0xdf,0x19,0xae,0x13,0xb3,0x50,0x4d,0x90,0xf6,0x83,0x01,0x0d,0x0e,0xfc,0x8d,0xd6,0xf5,0x57,0xd4, + 0xdd,0xa9,0xf8,0x26,0xfc,0x68,0x8c,0xf2,0x37,0xc6,0x57,0x96,0x98,0x25,0x0c,0x1b,0x12,0x05,0x43,0x29, + 0xf4,0xa0,0xf4,0x88,0x29,0x48,0x97,0x0a,0x37,0x54,0x41,0x56,0x11,0x1c,0x9f,0xfe,0x79,0x61,0xd5,0x73, + 0x8d,0x1c,0xaa,0xbd,0xeb,0x05,0x81,0x52,0xe5,0x37,0xd4,0x00,0x33,0x5a,0xa8,0xa8,0x0d,0xf7,0x4e,0x4e, + 0x5e,0xd8,0xd1,0xe6,0xc0,0x5d,0xa9,0x26,0x3c,0x96,0x69,0x97,0x46,0x70,0xe9,0x19,0x39,0x30,0x29,0x46, + 0xc0,0xe6,0x71,0x7e,0x6f,0x3a,0x5f,0x08,0x76,0xb0,0x0f,0x5f,0x21,0x47,0xc9,0x99,0x5b,0x30,0x33,0x46, + 0x10,0xdf,0x42,0xf2,0xfb,0xfd,0xb6,0xba,0xcc,0x4f,0x5e,0x3f,0x4f,0x81,0x31,0xd3,0xa0,0xe3,0x7f,0x12, + 0x5e,0x85,0xc0,0xfb,0x56,0x61,0x24,0x5f,0x8d,0x5f,0xf3,0xc3,0xff,0x83,0x93,0xf4,0x21,0xd3,0xb8,0x31, + 0xf3,0x70,0x39,0x9a,0x96,0xe5,0x49,0xc5,0x8c,0xc8,0x20,0x8e,0x12,0xf4,0x1b,0xc1,0x14,0xb3,0xf9,0x29, + 0x36,0x9c,0xf6,0x6c,0xe4,0x66,0x9a,0x79,0x6e,0x56,0xd0,0x24,0x0a,0x5e,0x8a,0xf9,0xf9,0xc0,0x91,0xca, + 0xc7,0xfc,0x08,0xf4,0xb8,0x9f,0x3b,0x7f,0x65,0xf5,0x53,0xde,0x8c,0xc4,0xbb,0xc8,0x2b,0xfd,0xe7,0x1f, + 0x54,0xf3,0x16,0xf9,0x56,0xc2,0x14,0xe6,0x64,0x1a,0xbe,0x62,0x6e,0x99,0x3f,0xbf,0xfa,0x69,0xa2,0xeb, + 0x3f,0x00, +}; +static const uint8_t readerWords[] = { + 0x1d,0x52,0x57,0xb2,0xa4,0x30,0x0c,0xf4,0x51,0x38,0xcb,0xe6,0x9c,0xe3,0xa7,0x06,0x04,0x68,0xc7,0x58, + 0x94,0xc3,0x52,0x73,0xfb,0xd7,0xad,0x0f,0x53,0x8a,0x2d,0x75,0x8b,0xa9,0xef,0x9a,0xa6,0x87,0x8f,0x34, + 0x49,0x59,0x12,0x5c,0xe9,0x69,0xda,0xed,0x48,0x53,0x13,0x63,0xc0,0x19,0xd4,0xaa,0xab,0x57,0x54,0xfa, + 0x8a,0xac,0xfc,0x87,0x75,0x59,0xce,0x69,0x7a,0xa7,0x6d,0xb4,0x34,0xbd,0x12,0xd6,0x20,0x85,0xc4,0xea, + 0x39,0xfb,0x85,0x82,0xdd,0x09,0xda,0x2e,0x34,0x03,0xa8,0xea,0xac,0x16,0x8d,0x5e,0xe1,0x5e,0x82,0x36, + 0x2b,0x69,0xba,0xe9,0x2c,0xa3,0x29,0xa7,0x18,0x42,0x07,0x2c,0x1a,0x74,0xcb,0xc6,0x1a,0xbc,0xb5,0xfa, + 0x41,0xe0,0x9a,0x62,0x5e,0x60,0x64,0x80,0xdc,0x8b,0x5f,0x44,0xc8,0xa6,0x81,0x6c,0x7d,0x87,0x3b,0x3a, + 0x87,0xdb,0x0c,0x7b,0x8b,0x89,0x6f,0xd3,0x34,0x0b,0x81,0x8b,0x77,0xf2,0x42,0xcf,0x21,0x0b,0x7c,0xa9, + 0x4a,0x0a,0x17,0x10,0xb3,0xb3,0x72,0xf6,0x98,0x2f,0x5c,0xe4,0x81,0x7c,0x6e,0xe4,0x70,0x73,0x42,0xbe, + 0xf6,0x85,0xb8,0xca,0x9e,0x7f,0x63,0xd9,0x0e,0x2d,0x88,0x1a,0x5e,0xa4,0x49,0x93,0xad,0x21,0xa2,0x93, + 0x50,0x8b,0x82,0x66,0x5b,0x01,0xb0,0xd6,0x2e,0x56,0x02,0xa0,0x84,0xcc,0xb4,0x7d,0x90,0x85,0x6c,0x91, + 0x39,0x18,0xca,0xb6,0x2a,0xcc,0x91,0xbb,0xf5,0xb1,0xd0,0x14,0xec,0xc1,0x71,0x1a,0x67,0x69,0x9a,0x57, + 0x50,0x84,0xf3,0x65,0x97,0x6a,0x4d,0x15,0xab,0x2e,0x0e,0x57,0x3b,0x0f,0xd0,0x95,0x57,0x59,0xad,0xb6, + 0x8e,0x32,0xb4,0xb6,0x53,0xe5,0x8e,0x19,0xd0,0x22,0xce,0x26,0xcc,0x8f,0x13,0xaf,0x64,0x6d,0x2d,0xfd, + 0xf5,0x91,0x7e,0xec,0x8a,0xb4,0x53,0x9f,0x8b,0x7a,0xd4,0xd1,0x24,0xeb,0x91,0x3e,0x78,0x0d,0x91,0x15, + 0xd1,0x2e,0x77,0x52,0x72,0x4c,0xb3,0x36,0xdb,0x99,0x03,0xab,0xaa,0x2c,0x18,0x52,0x21,0x3f,0x3a,0x16, + 0x26,0xf1,0x39,0xab,0x9f,0xbb,0x92,0xbb,0xf2,0x2a,0x78,0xdf,0xbd,0xc0,0x7b,0x14,0xd9,0x7c,0x1b,0xc4, + 0x21,0xab,0xe7,0x3b,0x08,0x74,0x40,0x1b,0x45,0xb7,0xd2,0x1d,0x89,0x58,0xc5,0x71,0xfa,0xc4,0xff,0xf1, + 0xcb,0x6e,0xd9,0x4e,0xf4,0xcf,0xd5,0xce,0x3e,0xa0,0xf0,0xea,0x60,0x49,0xae,0x45,0x32,0x07,0x1f,0xa1, + 0x5d,0x71,0xcc,0xa9,0xd6,0xbb,0x52,0x6b,0x2f,0x33,0xf2,0xc4,0x98,0xce,0x2c,0xb3,0x12,0xd6,0x43,0x6a, + 0x2d,0xa1,0xd2,0x8d,0x7b,0xa3,0x23,0x7e,0x76,0x2f,0x28,0x58,0x24,0xb4,0x72,0x92,0xf4,0x38,0x90,0x1e, + 0x40,0x92,0x4c,0x56,0x4f, +}; +static const StreamTokenCorpus readerCorpus = { + "WEB", + "World English Bible - public domain (eBible.org).", + readerData, sizeof(readerData), 50732, + readerWords, 425, 805, + 128, kVerseCount, 3 +}; +}}} diff --git a/src/helpers/bible/ReaderStreamLookup.h b/src/helpers/bible/ReaderStreamLookup.h new file mode 100644 index 000000000..e0345436e --- /dev/null +++ b/src/helpers/bible/ReaderStreamLookup.h @@ -0,0 +1,126 @@ +#pragma once +#include "ReaderLookup.h" + +namespace mesh { namespace bible { + +// Independent bounded-history streams trade cold Reader decode time for flash. +// The existing ESP32 block corpus and its decoder remain unchanged. +struct StreamTokenCorpus { + const char* translation; + const char* attribution; + const uint8_t* data; + uint32_t data_size; + uint32_t encoded_size; + const uint8_t* words; + uint16_t words_size; + uint16_t word_plain_size; + uint16_t word_count; + uint16_t verse_count; + uint16_t chunk_count; // Zero retains the single-stream fixture format. +}; + +inline LookupResult lookup(const StreamTokenCorpus& corpus, Reference ref, + char* scratch, size_t capacity, const char*& text) { + text = nullptr; + uint16_t index; + if (!verseNumber(ref, index)) return LookupResult::Invalid; + if (!scratch || capacity < kBlockSize || !corpus.data || !corpus.data_size + || !corpus.encoded_size || corpus.verse_count != kVerseCount + || corpus.chunk_count > kVerseCount + || corpus.word_count > 128 || corpus.word_plain_size > kBlockSize + || (corpus.word_count && (!corpus.words || !corpus.words_size + || corpus.word_plain_size < corpus.word_count * 2U)) + || (!corpus.word_count && (corpus.words_size || corpus.word_plain_size))) + return LookupResult::Corrupt; + const uint8_t* source = corpus.data; + uint32_t source_size = corpus.data_size, output_limit = corpus.encoded_size; + if (corpus.chunk_count) { + // Each independently compressed stream has a bounded six-byte header: + // first verse, decoded byte limit and compressed byte length, little-endian. + const uint8_t* cursor = corpus.data; + uint32_t remaining = corpus.data_size, total = 0; + const uint16_t wanted = index; + uint16_t previous = 0; + for (uint16_t n = 0; n < corpus.chunk_count; ++n) { + if (remaining < 6) return LookupResult::Corrupt; + const uint16_t first = cursor[0] | (uint16_t(cursor[1]) << 8); + const uint16_t plain = cursor[2] | (uint16_t(cursor[3]) << 8); + const uint16_t packed = cursor[4] | (uint16_t(cursor[5]) << 8); + cursor += 6; + remaining -= 6; + if (!plain || !packed || packed > remaining || first >= kVerseCount + || (n ? first <= previous : first != 0)) return LookupResult::Corrupt; + if (first <= wanted) { + source = cursor; + source_size = packed; + output_limit = plain; + index = wanted - first; + } + previous = first; + total += plain; + cursor += packed; + remaining -= packed; + } + if (remaining || total != corpus.encoded_size) return LookupResult::Corrupt; + } + unsigned int encoded_size = 0; + if (tinf_uncompress_record(scratch, kBlockSize, &encoded_size, + source, source_size, output_limit, index) != TINF_OK + || encoded_size >= kBlockSize) return LookupResult::Corrupt; + if (!encoded_size) return LookupResult::Missing; + bool tokenized = false; + for (unsigned int i = 0; i < encoded_size; ++i) { + const uint8_t byte = static_cast(scratch[i]); + if (byte >= 128) { + if (byte - 128U >= corpus.word_count) return LookupResult::Corrupt; + tokenized = true; + } else if (byte < 32 || byte >= 127) return LookupResult::Corrupt; + } + if (!tokenized) { text = scratch; return LookupResult::Found; } + + // Reuse the history after inflation. Keep the selected encoded record at + // the far end, the decoded vocabulary at the beginning, and expand into the + // gap. The host packer uses literal fallback for a long verse that would + // not fit these three regions, preserving the original 4095-byte limit. + uint8_t* encoded = reinterpret_cast(scratch) + kBlockSize - encoded_size - 1; + if (encoded <= reinterpret_cast(scratch) + corpus.word_plain_size) + return LookupResult::Corrupt; + memmove(encoded, scratch, encoded_size + 1); + unsigned int dictionary_size = corpus.word_plain_size; + if (tinf_uncompress_exact(scratch, &dictionary_size, corpus.words, corpus.words_size) != TINF_OK + || dictionary_size != corpus.word_plain_size) return LookupResult::Corrupt; + const char* dictionary_end = scratch + dictionary_size; + const char* check = scratch; + for (unsigned int n = 0; n < corpus.word_count; ++n) { + const char* zero = static_cast(memchr(check, 0, dictionary_end - check)); + if (!zero || zero == check) return LookupResult::Corrupt; + for (const char* p = check; p != zero; ++p) + if (*p < 32 || static_cast(*p) >= 127) return LookupResult::Corrupt; + check = zero + 1; + } + if (check != dictionary_end) return LookupResult::Corrupt; + + char* dest = scratch + dictionary_size; + const char* const output = dest; + const char* const end = reinterpret_cast(encoded) - 1; + while (*encoded) { + const uint8_t byte = *encoded++; + const uint8_t* word = &byte; + size_t length = 1; + if (byte >= 128) { + const unsigned int token = byte - 128; + const char* value = scratch; + for (unsigned int n = 0; n < token; ++n) value += strlen(value) + 1; + word = reinterpret_cast(value); + length = strlen(value); + } + if (length > static_cast(end - dest)) return LookupResult::Corrupt; + memcpy(dest, word, length); + dest += length; + } + *dest = 0; + text = output; + return LookupResult::Found; +} + +}} // namespace mesh::bible diff --git a/src/helpers/ota/OtaTinf.c b/src/helpers/ota/OtaTinf.c index aca67dece..7ebd9c315 100644 --- a/src/helpers/ota/OtaTinf.c +++ b/src/helpers/ota/OtaTinf.c @@ -11,8 +11,15 @@ /* nRF52's Arduino recipe defaults to -Ofast, which more than doubles this * decoder. Keep the size policy local to tinf instead of changing MeshCore. */ #pragma GCC push_options +#if defined(NRF52_PLATFORM) +#pragma GCC optimize ("Oz") +#else #pragma GCC optimize ("Os") #endif +#endif +#if (defined(NRF52_PLATFORM) && COMPANION_FEATURE_READER) || defined(TINF_READER_STREAM_TEST) +#define MESHCORE_TINF_RECORD 1 +#endif #define MESHCORE_TINF_IMPLEMENTATION 1 #include "tinf/tinflate.c" #if MESHCORE_OTA_DEVICE_DEFLATE diff --git a/src/helpers/ota/tinf/README.meshcore.txt b/src/helpers/ota/tinf/README.meshcore.txt index 4866796bf..108ea8284 100644 --- a/src/helpers/ota/tinf/README.meshcore.txt +++ b/src/helpers/ota/tinf/README.meshcore.txt @@ -22,3 +22,11 @@ with neither feature links no decoder code or persistent RAM. Only the raw RFC 1951 decoder is integrated. The zlib/gzip wrappers and checksum sources are intentionally omitted. The original zlib license is preserved in LICENSE and in the source headers. + +The nRF52 Full Companion Reader also enables reader_record.inc. This MeshCore +extension shares the same bounded bit reader and Huffman trees, keeps only +4096 bytes of circular history in the caller's existing Reader scratch, and +stops at the requested NUL-delimited record. It rejects distances beyond that +history, records exceeding the original 4095-byte limit, truncated inputs and +output beyond the generated corpus limit. No heap or retained decode cache is +used. The ordinary linear decoder and ESP32 Reader remain available unchanged. diff --git a/src/helpers/ota/tinf/reader_record.inc b/src/helpers/ota/tinf/reader_record.inc new file mode 100644 index 000000000..cbeb2ae2d --- /dev/null +++ b/src/helpers/ota/tinf/reader_record.inc @@ -0,0 +1,137 @@ +/* MeshCore extension (2026): read a NUL-delimited record through a caller-owned + * 4 KiB DEFLATE history. It shares tinf's bounded bit reader and Huffman trees. + * No heap, permanent cache, or second output/history buffer is used. */ +struct tinf_record_output { + unsigned char *window; + unsigned int size, produced, limit, remaining, length; +}; + +static int tinf_record_emit(struct tinf_record_output *out, unsigned char byte) +{ + if (out->produced >= out->limit) return TINF_DATA_ERROR; + out->window[out->produced++ & (out->size - 1)] = byte; + if (byte == 0) { + if (!out->remaining) return 1; + --out->remaining; + out->length = 0; + } else if (++out->length >= out->size) { + return TINF_BUF_ERROR; + } + return TINF_OK; +} + +static int tinf_record_block(struct tinf_data *d, struct tinf_record_output *out) +{ + static const unsigned char length_bits[29] = { + 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0 + }; + static const unsigned short length_base[29] = { + 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258 + }; + static const unsigned char dist_bits[30] = { + 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 + }; + static const unsigned short dist_base[30] = { + 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769, + 1025,1537,2049,3073,4097,6145,8193,12289,16385,24577 + }; + for (;;) { + int sym = tinf_decode_symbol(d, &d->ltree); + if (d->overflow) return TINF_DATA_ERROR; + if (sym == 256) return TINF_OK; + if (sym < 256) { + const int result = tinf_record_emit(out, (unsigned char) sym); + if (result) return result; + } else { + int length, distance, offset; + if (sym > d->ltree.max_sym || sym - 257 > 28 || d->dtree.max_sym == -1) + return TINF_DATA_ERROR; + sym -= 257; + length = tinf_getbits_base(d, length_bits[sym], length_base[sym]); + distance = tinf_decode_symbol(d, &d->dtree); + if (distance > d->dtree.max_sym || distance > 29) return TINF_DATA_ERROR; + offset = tinf_getbits_base(d, dist_bits[distance], dist_base[distance]); + if (d->overflow || (unsigned int) offset > out->produced + || (unsigned int) offset > out->size) return TINF_DATA_ERROR; + while (length--) { + const unsigned char byte = out->window[(out->produced - offset) & (out->size - 1)]; + const int result = tinf_record_emit(out, byte); + if (result) return result; + } + } + } +} + +static void tinf_record_reverse(unsigned char *begin, unsigned char *end) +{ + while (begin < end) { + unsigned char byte; + --end; + if (begin == end) break; + byte = *begin; + *begin++ = *end; + *end = byte; + } +} + +int tinf_uncompress_record(void *window, unsigned int window_size, + unsigned int *record_length, const void *source, unsigned int source_len, + unsigned int output_limit, unsigned int record_index) +{ + struct tinf_data d; + struct tinf_record_output out; + int final = 0; + if (!window || !record_length || !source || !source_len || !output_limit + || window_size != 4096) return TINF_DATA_ERROR; + *record_length = 0; + d.source = (const unsigned char *) source; + d.source_end = d.source + source_len; + d.tag = 0; + d.bitcount = d.overflow = 0; + out.window = (unsigned char *) window; + out.size = window_size; + out.produced = out.length = 0; + out.limit = output_limit; + out.remaining = record_index; + do { + unsigned int type; + int result; + final = tinf_getbits(&d, 1); + type = tinf_getbits(&d, 2); + if (d.overflow) return TINF_DATA_ERROR; + if (type == 0) { + unsigned int length; + if (d.source_end - d.source < 4) return TINF_DATA_ERROR; + length = read_le16(d.source); + if (length != (~read_le16(d.source + 2) & 0xffff)) return TINF_DATA_ERROR; + d.source += 4; + if ((unsigned int) (d.source_end - d.source) < length) return TINF_DATA_ERROR; + d.tag = 0; + d.bitcount = 0; + result = TINF_OK; + while (length--) { + result = tinf_record_emit(&out, *d.source++); + if (result) break; + } + } else if (type == 1 || type == 2) { + if (type == 1) { + tinf_build_fixed_trees(&d.ltree, &d.dtree); + result = TINF_OK; + } else result = tinf_decode_trees(&d, &d.ltree, &d.dtree); + if (result == TINF_OK) result = tinf_record_block(&d, &out); + } else return TINF_DATA_ERROR; + if (result == 1) { + /* Rotate in place: the selected record may cross the circular boundary. + * Include its NUL and use only initialized history bytes. */ + const unsigned int used = out.produced < out.size ? out.produced : out.size; + const unsigned int begin = (out.produced - out.length - 1) & (out.size - 1); + tinf_record_reverse(out.window, out.window + begin); + tinf_record_reverse(out.window + begin, out.window + used); + tinf_record_reverse(out.window, out.window + used); + *record_length = out.length; + return TINF_OK; + } + if (result != TINF_OK) return result; + } while (!final); + return TINF_DATA_ERROR; /* Selected record or its terminator is missing. */ +} diff --git a/src/helpers/ota/tinf/tinf.h b/src/helpers/ota/tinf/tinf.h index 5d6288f1f..ff26a8a1e 100644 --- a/src/helpers/ota/tinf/tinf.h +++ b/src/helpers/ota/tinf/tinf.h @@ -93,6 +93,13 @@ int TINFCC tinf_uncompress(void *dest, unsigned int *destLen, int TINFCC tinf_uncompress_exact(void *dest, unsigned int *destLen, const void *source, unsigned int sourceLen); +/* MeshCore: stop after one NUL-delimited record, using the caller's 4096-byte + * circular history as the final contiguous record buffer. output_limit bounds + * total inflated bytes, including preceding records. No heap is used. */ +int TINFCC tinf_uncompress_record(void *window, unsigned int window_size, + unsigned int *record_length, const void *source, unsigned int source_len, + unsigned int output_limit, unsigned int record_index); + /** * Decompress `sourceLen` bytes of gzip data from `source` to `dest`. * diff --git a/src/helpers/ota/tinf/tinflate.c b/src/helpers/ota/tinf/tinflate.c index 98bc4b000..e15cdb5d6 100644 --- a/src/helpers/ota/tinf/tinflate.c +++ b/src/helpers/ota/tinf/tinflate.c @@ -643,6 +643,10 @@ int tinf_uncompress_exact(void *dest, unsigned int *destLen, return tinf_uncompress_internal(dest, destLen, source, sourceLen, 1); } +#if defined(MESHCORE_TINF_RECORD) +#include "reader_record.inc" +#endif + /* clang -g -O1 -fsanitize=fuzzer,address -DTINF_FUZZING tinflate.c */ #if defined(TINF_FUZZING) #include diff --git a/src/helpers/ui/SmallFontGlyph.h b/src/helpers/ui/SmallFontGlyph.h index 47d17cf61..cc489541a 100644 --- a/src/helpers/ui/SmallFontGlyph.h +++ b/src/helpers/ui/SmallFontGlyph.h @@ -6,11 +6,24 @@ namespace mesh { namespace ui { // Packed monochrome bitmap metrics, with GFX-style baseline offsets. +#if defined(NRF52_PLATFORM) +// Both fixed ASCII fonts use offsets <= 209, dimensions <= 7, advances <= 6 +// and baseline offsets -5..1. Keep every metric/pixel while storing the two +// 95-glyph tables in 570 bytes instead of 1520; no decoded cache is needed. +// Native and Cortex-M4 tests check every source metric and rendered glyph. +struct __attribute__((packed)) SmallFontGlyph { + uint8_t bitmap_offset; + uint16_t width : 3, height : 3, advance : 3; + int16_t x_offset : 3, y_offset : 4; +}; +static_assert(sizeof(SmallFontGlyph) == 3, "Compact font metric layout changed"); +#else struct SmallFontGlyph { uint16_t bitmap_offset; uint8_t width, height, advance; int8_t x_offset, y_offset; }; +#endif } // namespace ui } // namespace mesh diff --git a/test/test_arm_instruction_meter.py b/test/test_arm_instruction_meter.py new file mode 100644 index 000000000..ca7210535 --- /dev/null +++ b/test/test_arm_instruction_meter.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +"""Keep native and Python ARM per-instruction measurements identical.""" +from pathlib import Path +import shutil +import sys +import unittest +from unittest import mock + +ROOT = Path(__file__).resolve().parents[1] +sys.path.insert(0, str(ROOT / 'scripts')) +try: + import unicorn + from unicorn import Uc, UC_ARCH_ARM, UC_MODE_THUMB, UC_MODE_MCLASS + from unicorn import arm_const as arm + import arm_instruction_meter as module +except ImportError: + unicorn = None + + +@unittest.skipUnless(unicorn is not None, 'Unicorn is not installed in this host job') +class ArmInstructionMeterTest(unittest.TestCase): + def machine(self, native): + uc = Uc(UC_ARCH_ARM, UC_MODE_THUMB | UC_MODE_MCLASS) + uc.mem_map(0x1000, 0x1000) + uc.mem_map(0x20000000, 0x1000) + # push {r7,lr}; sub sp,32; movs r0,100; loop: subs r0,1; + # bne loop; add sp,32; pop {r7,pc}. Exactly 205 instructions. + uc.mem_write(0x1100, bytes.fromhex('80b588b064200138fdd108b080bd')) + return uc, module.ArmInstructionMeter(uc, prefer_native=native) + + def run_machine(self, machine, meter, top): + machine.reg_write(arm.UC_ARM_REG_SP, top) + machine.reg_write(arm.UC_ARM_REG_LR, 0x1001) + meter.reset(top) + machine.emu_start(0x1101, 0x1000, timeout=1000000, count=1000) + self.assertEqual(machine.reg_read(arm.UC_ARM_REG_PC), 0x1000) + self.assertEqual(machine.reg_read(arm.UC_ARM_REG_SP), top) + self.assertEqual(machine.reg_read(arm.UC_ARM_REG_R0), 0) + self.assertEqual(meter.snapshot(), {'minimum_sp': top - 40, 'instructions': 205}) + return meter.snapshot() + + def test_native_python_exact_measurements_repeated_and_independent_contexts(self): + if unicorn.__version__ != '2.1.4': + self.skipTest('native private handle API is pinned to Unicorn 2.1.4') + if not any(shutil.which(name) for name in ('cc', 'gcc', 'clang')): + self.skipTest('host C compiler unavailable') + python_uc, python_meter = self.machine(False) + native_uc, native_meter = self.machine(True) + second_uc, second_meter = self.machine(True) + self.assertEqual(native_meter.backend, 'native') + self.assertEqual(second_meter.backend, 'native') + for top in (0x20001000, 0x20000f00, 0x20001000): + expected = self.run_machine(python_uc, python_meter, top) + self.assertEqual(self.run_machine(native_uc, native_meter, top), expected) + self.assertEqual(self.run_machine(second_uc, second_meter, top), expected) + + def test_missing_native_compiler_uses_identical_python_measurement(self): + module._native_library.cache_clear() + try: + with mock.patch.object(module.shutil, 'which', return_value=None): + machine, meter = self.machine(True) + finally: + module._native_library.cache_clear() + self.assertEqual(meter.backend, 'python') + self.run_machine(machine, meter, 0x20001000) + + def test_unreviewed_unicorn_version_uses_python_measurement(self): + with mock.patch.object(module.unicorn, '__version__', 'unreviewed-core'): + machine, meter = self.machine(True) + self.assertEqual(meter.backend, 'python') + self.run_machine(machine, meter, 0x20001000) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_companion_reader.py b/test/test_companion_reader.py index df540ff58..c7da05a09 100644 --- a/test/test_companion_reader.py +++ b/test/test_companion_reader.py @@ -93,11 +93,22 @@ class CompanionReaderTest(unittest.TestCase): return found[0] reader, decoder = frames("reader"), frames("tinf") - # Summing all helper frames overbounds mutually exclusive branches - # (tree building and block decoding), including their leaf calls. - decoder_bytes = sum(count for name, count in decoder.items() - if not name.endswith(":tinf_init") and not name.endswith(":tinf_uncompress")) + # Tree building and block decoding are mutually exclusive too; + # sum their helpers as a conservative overbound. Linear OTA and + # circular Reader entry points never nest, so retain the larger + # entry family instead of adding both independent histories. + linear_names = (":tinf_uncompress_internal", ":tinf_uncompress_exact", + ":tinf_inflate_block_data") + ring_names = (":tinf_uncompress_record", ":tinf_record_block", + ":tinf_record_emit", ":tinf_record_reverse") + linear = sum(count for name, count in decoder.items() if name.endswith(linear_names)) + ring = sum(count for name, count in decoder.items() if name.endswith(ring_names)) + shared = sum(count for name, count in decoder.items() + if not name.endswith(linear_names + ring_names + (":tinf_init", ":tinf_uncompress"))) + self.assertGreater(ring, 0, "actual nRF52 Reader ring decoder must be measured") + decoder_bytes = shared + max(linear, ring) lookup = frame(reader, "mesh::readReaderVerse(") + lookup += sum(count for name, count in reader.items() if "mesh::bible::lookup(" in name) terminal = (frame(reader, "mesh::handleReaderCommand(") + frame(reader, "::printReplyPage(") + frame(reader, "::replyVerse(") + lookup + decoder_bytes) @@ -434,7 +445,10 @@ int main() { "-I" + str(ROOT / "src"), "-I" + str(FIXTURE), str(FIXTURE / "test_reader_screen.cpp"), str(ROOT / "src/helpers/CompanionReader.cpp"), str(obj), "-o", str(binary)]) - self.run_checked([str(binary)]) + # All 879 verses are laid out forwards/backwards at 18 panel + # geometries. The 4 KiB streaming nRF corpus does more + # bounded decode work per cold lookup; keep every case. + self.run_checked([str(binary)], timeout=600) def test_actual_reader_stream_pauses_and_decode_entrypoints_reject_during_morse(self): cc, cxx = shutil.which("gcc"), shutil.which("g++") @@ -651,9 +665,9 @@ int main() { str(ROOT / "src/helpers/ui/DisplayDriver.cpp"), "-o", str(binary)]) self.run_checked([str(binary)]) - def run_checked(self, command): + def run_checked(self, command, timeout=60): result = subprocess.run(command, capture_output=True, encoding="utf-8", - errors="backslashreplace", timeout=60) + errors="backslashreplace", timeout=timeout) self.assertEqual(result.returncode, 0, result.stdout + result.stderr) return result.stdout diff --git a/test/test_nrf52_program_lifetime.py b/test/test_nrf52_program_lifetime.py new file mode 100644 index 000000000..9882dc510 --- /dev/null +++ b/test/test_nrf52_program_lifetime.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +"""Keep local cleanup while omitting unreachable nRF52 process-exit callbacks.""" +import importlib.util +from pathlib import Path +import shutil +import subprocess +import tempfile +import unittest + +ROOT = Path(__file__).resolve().parents[1] +spec = importlib.util.spec_from_file_location("nrf52_flash_trim", ROOT / "scripts/nrf52_flash_trim.py") +policy = importlib.util.module_from_spec(spec) +spec.loader.exec_module(policy) + + +class Nrf52ProgramLifetimeTest(unittest.TestCase): + def test_automatic_delete_and_explicit_shutdown_remain_and_exit_callback_is_not_registered(self): + compiler = shutil.which("g++") + if not compiler: + self.skipTest("native G++ unavailable") + source = r''' +#include +extern "C" int __aeabi_atexit(void*, void (*)(void*), void*); +extern "C" int __cxa_atexit(void (*)(void*), void*, void*); +extern "C" void __cxa_finalize(void*); +unsigned closed = 0, destroyed = 0, callback = 0; +void cleanup(void* count) { ++*static_cast(count); } +struct Resource { + void close() { ++closed; } + ~Resource() { close(); ++destroyed; } +}; +int main() { + { Resource automatic; } + auto* allocated = new Resource; + delete allocated; + assert(closed == 2 && destroyed == 2); + Resource explicit_close; + explicit_close.close(); + assert(closed == 3 && destroyed == 2); + unsigned dso = 0; + assert(__aeabi_atexit(&callback, cleanup, &dso) == 0); + __cxa_finalize(&dso); + assert(callback == 0); + // Positive control: the finalizer really can run a registered callback. + assert(__cxa_atexit(cleanup, &callback, &dso) == 0); + __cxa_finalize(&dso); + assert(callback == 1); +} +''' + with tempfile.TemporaryDirectory(prefix="mesh-nrf52-lifetime-") as directory: + directory = Path(directory) + main, binary = directory / "main.cpp", directory / "checks" + main.write_text(source, encoding="ascii") + subprocess.run([compiler, "-std=c++11", "-Os", "-flto", "-DNRF52_PLATFORM=1", + "-DMESH_NRF52_NO_PROGRAM_EXIT=1", str(main), + str(ROOT / "src/Nrf52ProgramLifetime.cpp"), "-o", str(binary)], check=True) + subprocess.run([str(binary)], check=True) + + def test_unknown_and_absent_framework_main_keep_standard_registration(self): + self.assertFalse(policy.supported_program_lifetime(None)) + with tempfile.TemporaryDirectory(prefix="mesh-nrf52-lifetime-core-") as directory: + self.assertFalse(policy.supported_program_lifetime(directory)) + main = Path(directory) / "cores/nRF5/main.cpp" + main.parent.mkdir(parents=True) + main.write_text("int main() { return 0; }\n", encoding="ascii") + self.assertFalse(policy.supported_program_lifetime(directory)) + + def test_current_pinned_core_is_the_reviewed_nonreturning_main_when_installed(self): + framework = Path.home() / ".platformio/packages/framework-arduinoadafruitnrf52" + if not framework.is_dir(): + self.skipTest("pinned nRF52 framework not installed in this host job") + if not policy.supported_program_lifetime(framework): + self.skipTest("custom or newer core retains ordinary exit registration") + main = (framework / "cores/nRF5/main.cpp").read_text() + self.assertIn("while (1)", main) + self.assertIn("vTaskStartScheduler();", main) + self.assertIn("NVIC_SystemReset();", main) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/test_reader_stream.py b/test/test_reader_stream.py new file mode 100644 index 000000000..adc48181b --- /dev/null +++ b/test/test_reader_stream.py @@ -0,0 +1,415 @@ +#!/usr/bin/env python3 +"""Exercise the actual bounded Reader stream decoder and selected-verse lookup.""" +import importlib.util +import json +from pathlib import Path +import random +import shutil +import subprocess +import tempfile +import unittest +import zlib + +ROOT = Path(__file__).resolve().parents[1] + + +def raw_deflate(data, level=9, strategy=zlib.Z_DEFAULT_STRATEGY): + compressor = zlib.compressobj(level, zlib.DEFLATED, -12, 8, strategy) + return compressor.compress(data) + compressor.flush() + + +def stored(data, final=True): + assert len(data) <= 65535 + size = len(data) + return bytes([int(final)]) + size.to_bytes(2, "little") + (size ^ 65535).to_bytes(2, "little") + data + + +class Bits: + def __init__(self): + self.bits = [] + + def put(self, value, count): + self.bits.extend((value >> bit) & 1 for bit in range(count)) + + def huffman(self, value, count): + self.put(int(f"{value:0{count}b}"[::-1], 2), count) + + def literal(self, symbol): + if symbol <= 143: + self.huffman(48 + symbol, 8) + elif symbol <= 255: + self.huffman(400 + symbol - 144, 9) + elif symbol <= 279: + self.huffman(symbol - 256, 7) + else: + self.huffman(192 + symbol - 280, 8) + + def finish(self): + self.bits.extend([0] * ((-len(self.bits)) % 8)) + return bytes(sum(self.bits[start + bit] << bit for bit in range(8)) + for start in range(0, len(self.bits), 8)) + + +def back_reference(distance): + bits = Bits() + bits.put(1, 1) + bits.put(1, 2) # final fixed-Huffman block + bits.literal(257) # length 3 + if distance == 4096: + bits.huffman(23, 5) + bits.put(1023, 10) + elif distance == 4097: + bits.huffman(24, 5) + bits.put(0, 11) + else: + raise AssertionError(distance) + bits.literal(0) + bits.literal(256) + return bits.finish() + + +def array(data): + return "{" + ",".join(str(byte) for byte in data) + "}" + + +class ReaderStreamTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + if not shutil.which("g++") or not shutil.which("gcc"): + raise unittest.SkipTest("native C/C++ compilers unavailable") + cls.temporary = tempfile.TemporaryDirectory(prefix="mesh-reader-stream-") + cls.directory = Path(cls.temporary.name) + cls.source = cls.directory / "main.cpp" + cls.decoder = cls.directory / "tinf.o" + flags = ["-O1", "-g", "-fsanitize=address,undefined", "-fno-sanitize-recover=all", + "-fno-omit-frame-pointer", "-fno-pie", "-I" + str(ROOT / "src"), + "-DOTA_TRANSPORT_DEFLATE_TEST=1", "-DTINF_READER_STREAM_TEST=1"] + subprocess.run(["gcc", "-std=c11", *flags, "-c", str(ROOT / "src/helpers/ota/OtaTinf.c"), + "-o", str(cls.decoder)], check=True, capture_output=True) + cls.flags = flags + + @classmethod + def tearDownClass(cls): + cls.temporary.cleanup() + + def compile(self, body, name="test"): + source = self.directory / (name + ".cpp") + source.write_text(body, encoding="ascii") + binary = self.directory / name + result = subprocess.run(["g++", "-std=c++17", *self.flags, "-no-pie", str(source), + str(self.decoder), "-o", str(binary)], capture_output=True, text=True) + self.assertEqual(result.returncode, 0, result.stdout + result.stderr) + return binary + + def run_binary(self, binary, *args): + result = subprocess.run([str(binary), *args], capture_output=True) + self.assertEqual(result.returncode, 0, result.stdout.decode(errors="replace") + result.stderr.decode(errors="replace")) + return result.stdout + + def test_canonical_stream_header_and_token_vocabulary_are_reproducible(self): + import sys + path = str(ROOT / "tools/bible") + sys.path.insert(0, path) + try: + spec = importlib.util.spec_from_file_location("reader_stream_repro_packer", ROOT / "tools/bible/pack_reader_stream.py") + packer = importlib.util.module_from_spec(spec) + spec.loader.exec_module(packer) + document = json.loads(packer.blocks.SOURCE.read_text(encoding="utf-8")) + self.assertEqual(packer.HEADER.read_text(encoding="ascii"), packer.render(document)) + pinned = json.loads(packer.TOKENS.read_text(encoding="ascii")) + self.assertLessEqual(len(pinned), 128) + self.assertEqual(len(pinned), len(set(pinned))) + for word in pinned: + self.assertTrue(word) + self.assertTrue(all(32 <= byte < 127 for byte in word.encode("ascii"))) + finally: + sys.path.remove(path) + + def test_all_879_verses_match_independently_normalized_source(self): + binary = self.compile(r""" +#include +#include +#include +#include +int main() { + using namespace mesh::bible; + struct { unsigned char before[32]; char scratch[kBlockSize]; unsigned char after[32]; } buffer; + memset(&buffer, 0xa5, sizeof(buffer)); + for (unsigned chapter=1; chapter<=21; ++chapter) { + for (unsigned verse=1; verse<=kChapterVerses[chapter-1]; ++verse) { + const char* text=nullptr; + assert(lookup(generated::readerCorpus, Reference{uint8_t(chapter),uint8_t(verse)}, + buffer.scratch,sizeof(buffer.scratch),text)==LookupResult::Found); + assert(text>=buffer.scratch && text +#include +#include +#include +#include +struct Guard { unsigned char before[32]; unsigned char window[4096]; unsigned char after[32]; }; +void check(const unsigned char* source,unsigned size,unsigned limit,unsigned record,int expected, + const unsigned char* text,unsigned length) { + Guard buffer; memset(&buffer,0xa5,sizeof(buffer)); unsigned actual=777; + int result=tinf_uncompress_record(buffer.window,4096,&actual,source,size,limit,record); + if(expected==0) { assert(result==TINF_OK);assert(actual==length);assert(!memcmp(buffer.window,text,length+1)); } + else assert(result!=TINF_OK); + for(auto c:buffer.before) assert(c==0xa5); + for(auto c:buffer.after) assert(c==0xa5); +} +""" + "\n".join(definitions) + r""" +int main() { +""" + "\n".join(calls) + r""" + Guard buffer; memset(&buffer,0xa5,sizeof(buffer)); unsigned length=777; + const unsigned char sample[]={3,0}; + assert(tinf_uncompress_record(nullptr,4096,&length,sample,2,4096,0)!=TINF_OK); + assert(tinf_uncompress_record(buffer.window,2048,&length,sample,2,4096,0)!=TINF_OK); + assert(tinf_uncompress_record(buffer.window,4096,nullptr,sample,2,4096,0)!=TINF_OK); + assert(tinf_uncompress_record(buffer.window,4096,&length,nullptr,2,4096,0)!=TINF_OK); + assert(tinf_uncompress_record(buffer.window,4096,&length,sample,2,0,0)!=TINF_OK); + uint32_t seed=0x84a123f1; unsigned char input[512]; + for(unsigned pass=0;pass<10000;++pass) { + auto next=[&]() { seed^=seed<<13;seed^=seed>>17;seed^=seed<<5;return seed; }; + const unsigned size=next()%sizeof(input); + for(unsigned i=0;i'), encoding="ascii") + binary = self.compile(r""" +#include "literal.h" +#include +#include +int main() { + using namespace mesh::bible; + char scratch[kBlockSize];const char* text=nullptr; + const auto& c=generated::readerCorpus; + assert(lookup(c,{1,1},scratch,sizeof(scratch),text)==LookupResult::Found); + assert(strlen(text)==4095);for(unsigned n=0;n<4095;++n) assert(text[n]=='q'); + assert(lookup(c,{1,2},scratch,sizeof(scratch),text)==LookupResult::Missing);assert(!text); + assert(lookup(c,{0,1},scratch,sizeof(scratch),text)==LookupResult::Invalid);assert(!text); + assert(lookup(c,{1,1},scratch,sizeof(scratch)-1,text)==LookupResult::Corrupt);assert(!text); + assert(lookup(c,{1,1},nullptr,sizeof(scratch),text)==LookupResult::Corrupt);assert(!text); + auto bad=c;bad.data=nullptr;assert(lookup(bad,{1,1},scratch,sizeof(scratch),text)==LookupResult::Corrupt); + bad=c;bad.data_size=1;assert(lookup(bad,{1,1},scratch,sizeof(scratch),text)==LookupResult::Corrupt); + bad=c;bad.encoded_size=1;assert(lookup(bad,{1,1},scratch,sizeof(scratch),text)==LookupResult::Corrupt); + bad=c;--bad.verse_count;assert(lookup(bad,{1,1},scratch,sizeof(scratch),text)==LookupResult::Corrupt); +} +""", "literal") + self.run_binary(binary) + + def test_independent_stream_header_bounds_and_boundary_records(self): + binary = self.compile(r""" +#include +#include +#include +#include +int main() { + using namespace mesh::bible; + const auto& original=generated::readerCorpus; + assert(original.chunk_count==3); + struct { unsigned char before[32];char scratch[kBlockSize];unsigned char after[32]; } buffer; + auto check=[&](const StreamTokenCorpus& corpus) { + memset(&buffer,0xa5,sizeof(buffer));const char* text=nullptr; + assert(lookup(corpus,{1,1},buffer.scratch,sizeof(buffer.scratch),text)==LookupResult::Corrupt); + assert(!text); + for(auto byte:buffer.before) assert(byte==0xa5); + for(auto byte:buffer.after) assert(byte==0xa5); + }; + auto bad=original;bad.chunk_count=4;check(bad); + bad=original;bad.chunk_count=kVerseCount+1;check(bad); + bad=original;bad.data_size=5;check(bad); + bad=original;--bad.data_size;check(bad); + bad=original;++bad.encoded_size;check(bad); + std::vector bytes(original.data,original.data+original.data_size); + const size_t second=6+bytes[4]+(unsigned(bytes[5])<<8); + for(auto field: {size_t(0),size_t(2),size_t(4),second,second+2,second+4}) { + bytes.assign(original.data,original.data+original.data_size); + bytes[field]=bytes[field+1]=0; + if(field==0) bytes[0]=1; // First record must start at verse zero. + bad=original;bad.data=bytes.data();check(bad); + } + bytes.assign(original.data,original.data+original.data_size); + bytes[4]=bytes[5]=0xff;bad=original;bad.data=bytes.data();check(bad); + bytes.assign(original.data,original.data+original.data_size); + bytes.push_back(0);bad=original;bad.data=bytes.data();++bad.data_size;check(bad); + // Independently inspect all stream boundaries and byte bounds. The complete + // canonical comparison also checks every record immediately around them. + unsigned offset=0,total=0,first=0; + for(unsigned n=0;nfirst : current==0) && plain<=17000 && packed>0); + first=current;offset+=6+packed;total+=plain; + } + assert(offset==original.data_size && total==original.encoded_size); +} +""", "chunks") + self.run_binary(binary) + + def test_dictionary_corruption_tokens_and_workspace_are_bounded(self): + cases = [] + + def fixture(encoded=b"\x80 suffix", words=b"Hello\0world\0", count=2, + *, plain_size=None, packed_words=None, expected=3, output=b""): + plain=encoded+b"\0"+bytes(878) + cases.append((raw_deflate(plain),len(plain), + raw_deflate(words) if packed_words is None else packed_words, + len(words) if plain_size is None else plain_size,count,expected,output)) + + fixture(expected=0,output=b"Hello suffix") + fixture(encoded=b"\x81",expected=0,output=b"world") + fixture(encoded=b"\x82") # token outside the declared vocabulary + fixture(encoded=b"literal\x1f") + fixture(encoded=b"literal\x7f") + fixture(count=0) + fixture(count=129) + fixture(words=b"Hello\0\0") + fixture(words=b"Hello\0wo\x7frld\0") + fixture(words=b"Hello\0wo\x80rld\0") + fixture(words=b"Hello\0world") + fixture(words=b"Hello\0world\0extra\0") + fixture(plain_size=1) + fixture(plain_size=4096) + fixture(plain_size=4097) + fixture(packed_words=raw_deflate(b"Hello\0world\0")[:-1]) + fixture(packed_words=raw_deflate(b"Hello\0world\0")+b"x") + fixture(packed_words=b"") + fixture(encoded=b"\x80"*100,words=b"h"*64+b"\0",count=1) + fixture(encoded=b"\x80"*4095,words=b"Hello\0",count=1) + definitions,calls=[],[] + for n,(data,size,words,word_plain,count,expected,output) in enumerate(cases): + definitions.extend((f"const unsigned char p{n}[]={array(data)};", + f"const unsigned char w{n}[]={array(words or b'x')};", + f"const unsigned char e{n}[]={array(output+bytes([0]))};")) + calls.append(f"check(p{n},sizeof(p{n}),{size},w{n},{len(words)},{word_plain},{count},{expected},e{n});") + body=r""" +#include +#include +#include +void check(const uint8_t* data,unsigned data_size,unsigned plain_size,const uint8_t* words, + unsigned words_size,unsigned word_plain,unsigned count,unsigned expected,const unsigned char* output) { + using namespace mesh::bible; + StreamTokenCorpus c={"fixture","test",data,data_size,plain_size,words, + uint16_t(words_size),uint16_t(word_plain),uint16_t(count),kVerseCount}; + struct { unsigned char before[32];char scratch[kBlockSize];unsigned char after[32]; } buffer; + memset(&buffer,0xa5,sizeof(buffer));const char* text=nullptr; + auto result=lookup(c,{1,1},buffer.scratch,sizeof(buffer.scratch),text); + assert(unsigned(result)==expected); + if(result==LookupResult::Found) assert(text && !strcmp(text,reinterpret_cast(output))); + else assert(!text); + for(auto byte:buffer.before) assert(byte==0xa5); + for(auto byte:buffer.after) assert(byte==0xa5); +} +"""+"\n".join(definitions)+"\nint main() {\n"+"\n".join(calls)+"\n}\n" + self.run_binary(self.compile(body,"dictionary")) + + def test_worst_case_lookup_host_benchmark(self): + # Keep timings informational: hosts vary, but report the CPU tradeoff + # against the unchanged block decoder using identical host options. + binary = self.compile(r""" +#include +#define generated old_generated +#include +#undef generated +#include +#include +#include +#include +template double bench(const Corpus& corpus) { + using namespace mesh::bible; + char scratch[kBlockSize];const char* text=nullptr; + volatile unsigned sum=0; + auto start=std::chrono::steady_clock::now(); + for(unsigned n=0;n<80;++n) { + assert(lookup(corpus,{21,25},scratch,sizeof(scratch),text)==LookupResult::Found); + sum+=strlen(text); + } + assert(sum>0); + return std::chrono::duration(std::chrono::steady_clock::now()-start).count()/80; +} +int main() { printf("Reader last verse host ms: block=%.4f stream=%.4f\n", + bench(mesh::bible::old_generated::readerCorpus),bench(mesh::bible::generated::readerCorpus)); } +""", "benchmark") + print(self.run_binary(binary).decode().strip()) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/test_small_font_metrics.py b/test/test_small_font_metrics.py new file mode 100644 index 000000000..2d4e817c2 --- /dev/null +++ b/test/test_small_font_metrics.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python3 +"""Prove compact nRF glyph metrics retain every original field and pixel.""" +from pathlib import Path +import re +import shutil +import subprocess +import tempfile +import unittest + +ROOT=Path(__file__).resolve().parents[1] +FONT_FILES=("Pixel5FontData.h","Squeezed6FontData.h") + +HARNESS=r''' +#include +#include +#include +#include +#include +#if defined(NRF52_PLATFORM) +static_assert(sizeof(mesh::ui::SmallFontGlyph)==3,"Cortex-M4 glyph packing"); +#else +static_assert(sizeof(mesh::ui::SmallFontGlyph)==8,"Existing non-nRF glyph ABI"); +#endif +void show(const mesh::ui::SmallFontGlyph* glyphs,const uint8_t* bitmap) { + for(unsigned n=0;n<95;++n) { + const auto& g=glyphs[n]; + printf("%u,%u,%u,%u,%d,%d:",unsigned(g.bitmap_offset),unsigned(g.width), + unsigned(g.height),unsigned(g.advance),int(g.x_offset),int(g.y_offset)); + for(unsigned row=0;row>(bit%8))?'1':'0'); + } + } + putchar('\n'); + } +} +int main() { + show(mesh::ui::pixel5Glyphs,mesh::ui::pixel5Bitmaps); + show(mesh::ui::squeezed6Glyphs,mesh::ui::squeezed6Bitmaps); +} +''' + + +class SmallFontMetricsTest(unittest.TestCase): + def source_metrics(self): + result=[] + for filename in FONT_FILES: + source=(ROOT/"src/helpers/ui"/filename).read_text() + rows=[tuple(map(int,row.split(","))) + for row in re.findall(r"\{([\d, \-]+)\}",source)] + self.assertEqual(len(rows),95) + for offset,width,height,advance,x,y in rows: + self.assertLessEqual(offset,255) + self.assertTrue(0<=width<=7 and 0<=height<=7 and 0<=advance<=7) + self.assertTrue(-4<=x<=3 and -8<=y<=7) + result+=rows + return result + + def test_all_190_metrics_and_pixels_match_original_layout(self): + expected=self.source_metrics() + with tempfile.TemporaryDirectory(prefix="mesh-small-font-") as folder: + folder=Path(folder);source=folder/"main.cpp";source.write_text(HARNESS) + output=[] + for nrf in (False,True): + binary=folder/("nrf" if nrf else "original") + result=subprocess.run(["g++","-std=c++11","-O1","-g","-Wall","-Wextra", + "-Werror=overflow","-Werror=conversion","-fsanitize=address,undefined", + "-fno-sanitize-recover=all","-fno-pie","-no-pie","-I"+str(ROOT/"src"), + *(["-DNRF52_PLATFORM=1"] if nrf else []),str(source),"-o",str(binary)],capture_output=True,text=True) + self.assertEqual(result.returncode,0,result.stdout+result.stderr) + run=subprocess.run([str(binary)],capture_output=True,text=True) + self.assertEqual(run.returncode,0,run.stdout+run.stderr) + output.append(run.stdout) + actual=[tuple(map(int,row.split(":")[0].split(","))) for row in run.stdout.splitlines()] + self.assertEqual(actual,expected) + self.assertEqual(output[0],output[1]) + + def test_actual_cortex_m4_layout_and_flash_saving(self): + compiler=shutil.which("arm-none-eabi-g++") + if not compiler: + path=Path.home()/".platformio/packages/toolchain-gccarmnoneeabi/bin/arm-none-eabi-g++" + if path.is_file():compiler=str(path) + if not compiler:self.skipTest("Cortex-M4 compiler unavailable") + compiler=Path(compiler) + nm=compiler.with_name("arm-none-eabi-nm") + self.source_metrics() + with tempfile.TemporaryDirectory(prefix="mesh-small-font-arm-") as folder: + folder=Path(folder);source=folder/"main.cpp" + source.write_text('#include \n#include \n' + 'extern "C" const void* keep(unsigned n) { return n?mesh::ui::pixel5Glyphs:mesh::ui::squeezed6Glyphs; }\n') + sizes=[] + for nrf in (False,True): + obj=folder/("nrf.o" if nrf else "original.o") + result=subprocess.run([str(compiler),"-std=gnu++11","-mcpu=cortex-m4","-mthumb","-Oz", + "-fno-exceptions","-fno-rtti","-Werror=overflow","-Werror=conversion", + "-I"+str(ROOT/"src"),*(["-DNRF52_PLATFORM=1"] if nrf else []), + "-c",str(source),"-o",str(obj)],capture_output=True,text=True) + self.assertEqual(result.returncode,0,result.stdout+result.stderr) + symbols=subprocess.check_output([str(nm),"-S",str(obj)],text=True) + rows=[int(line.split()[1],16) for line in symbols.splitlines() + if "pixel5Glyphs" in line or "squeezed6Glyphs" in line] + self.assertEqual(sorted(rows),[95*(3 if nrf else 8)]*2) + sizes.append(sum(rows)) + self.assertEqual(sizes[0]-sizes[1],950) + + +if __name__=="__main__":unittest.main() diff --git a/tools/bible/README.md b/tools/bible/README.md index ec6bfde19..114a2987a 100644 --- a/tools/bible/README.md +++ b/tools/bible/README.md @@ -302,3 +302,33 @@ match for identity/private key, contacts, channels, self settings, auto-add, tuning and custom variables. The bootloader and storage were not flashed. Other panel families were host-tested, not physically flashed or visually qualified in this step. Physical readability remains a user check. + +## nRF52 Full Companion flash encoding + +nRF52 Full Companions retain all 879 verses with a separate generated stream +asset. The encoded corpus, stream headers and vocabulary occupy 28,787 flash bytes, compared +with 33,399 bytes for the ESP32 block asset. Both decode to the same ASCII text. +The existing 4 KiB scratch is reused first as circular DEFLATE history, then +for the selected verse and vocabulary. There is no new heap buffer or retained +cache. Long verses that cannot share the scratch with the vocabulary are stored +literally, preserving the original 4095-byte verse capacity. + +Three independent streams limit a lookup to at most 16,959 encoded bytes +through the selected verse, using the same scratch for each history. This +makes cold Reader access slower in exchange for nRF52 flash space; +normal packet handling does not enter this path. ESP32 keeps its existing +independent blocks. Native tests compare every verse and output byte, exercise +malformed inputs, and check the actual ARM stack against the existing 8 KiB +loop stack. Linked-ELF tests retain the same text and reply checks. + +`reader-stream-tokens.json` is the pinned compression vocabulary. It substitutes +only exact source byte strings, including substrings of words. Host-only Google +Zopfli uses 1,000 iterations with a 4 KiB match window; its Apache-2.0 source, +license and archive hash are in `zopfli4k/README.meshcore.txt`. A host C compiler +is needed only when regenerating this asset. Ordinary firmware builds use the +committed header and require no extra downloads. + +```sh +python tools/bible/pack_reader_stream.py --check +python test/test_reader_stream.py +``` diff --git a/tools/bible/pack_reader_stream.py b/tools/bible/pack_reader_stream.py new file mode 100644 index 000000000..ef93caa0c --- /dev/null +++ b/tools/bible/pack_reader_stream.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python3 +"""Pack the unchanged Reader source for a 4 KiB sliding-window nRF52 decoder.""" +import argparse +import hashlib +from functools import lru_cache +import shutil +import subprocess +import tempfile +import json +from pathlib import Path +import re +import zlib + +import pack_reader as blocks + +HEADER = blocks.ROOT / "src/helpers/bible/ReaderStreamData.generated.h" +TOKENS = Path(__file__).with_name("reader-stream-tokens.json") +COMPRESSOR = Path(__file__).with_name("zopfli4k") +STREAM_LIMIT = 17000 + + +@lru_cache(maxsize=1) +def host_compressor(): + compiler = shutil.which("cc") or shutil.which("gcc") or shutil.which("clang") + if not compiler: + raise RuntimeError("Regenerating the nRF52 Reader asset requires a host C compiler") + directory = tempfile.TemporaryDirectory(prefix="mesh-reader-zopfli4k-") + binary = Path(directory.name) / "pack4k" + subprocess.run([compiler, "-std=c11", "-O2", "-DNDEBUG", "-I" + str(COMPRESSOR), + *map(str, sorted(COMPRESSOR.glob("*.c"))), "-lm", "-o", str(binary)], check=True) + return directory, binary + + +@lru_cache(maxsize=16) +def compress_4k(data): + directory, binary = host_compressor() + source = Path(directory.name) / "input" + target = Path(directory.name) / "output" + source.write_bytes(data) + subprocess.run([str(binary), str(source), str(target), "1000"], check=True, capture_output=True) + packed = target.read_bytes() + assert zlib.decompress(packed, wbits=-12) == data + return packed + + +def token_dictionary(verses): + # The vocabulary is an independently tunable compression asset. Tokens + # only substitute exact source bytes, including matches inside a word. + selected = json.loads(TOKENS.read_text(encoding="ascii")) + if len(selected) > 128 or len(set(selected)) != len(selected): + raise ValueError("Reader vocabulary must contain at most 128 unique byte strings") + words = [word.encode("ascii") for word in selected] + if any(not word or any(byte < 32 or byte >= 127 for byte in word) for word in words): + raise ValueError("Reader tokens must be nonempty printable ASCII") + return [word for word in words if any(word in verse for verse in verses)] + + +def encode(document): + verses = blocks.source_verses(document) + words = token_dictionary(verses) + tokens = {word: bytes([128 + index]) for index, word in enumerate(words)} + # Tokens are exact byte strings, including any leading space. Matching + # inside another word is lossless too: expansion restores those same bytes. + pattern = re.compile(b"|".join(re.escape(word) for word in sorted(words, key=lambda x: (-len(x), x)))) if words else None + encoded_verses = [] + dictionary_size = sum(map(len, words)) + len(words) + for verse in verses: + encoded = pattern.sub(lambda match: tokens[match.group()], verse) if pattern else verse + if encoded != verse and len(encoded) + len(verse) + dictionary_size + 2 > blocks.BLOCK_SIZE: + encoded = verse # Literal output needs no dictionary workspace. + encoded_verses.append(encoded) + records = [verse + b"\0" for verse in encoded_verses] + chunks, first, current = [], 0, b"" + for index, record in enumerate(records): + if current and len(current) + len(record) > STREAM_LIMIT: + chunks.append((first, current)) + first, current = index, b"" + current += record + chunks.append((first, current)) + # Independent 12-bit histories bound cold lookup work without adding a + # buffer. Headers keep both compressed and decoded lengths bounded. + packed = bytearray() + for first, plain in chunks: + compressed = compress_4k(plain) + packed += (first.to_bytes(2, "little") + len(plain).to_bytes(2, "little") + + len(compressed).to_bytes(2, "little") + compressed) + return bytes(packed), b"".join(records), words, len(chunks) + + +def render(document): + packed, plain, words, chunk_count = encode(document) + dictionary_plain = b"\0".join(words) + b"\0" if words else b"" + dictionary = compress_4k(dictionary_plain) if words else b"" + digest = hashlib.sha256(json.dumps(document, ensure_ascii=False, sort_keys=True, + separators=(",", ":")).encode()).hexdigest() + lines = ["// Generated by tools/bible/pack_reader_stream.py; do not edit.", + "// Offline Reader corpus. Public domain; see tools/bible/README.md.", + "// Google Zopfli, 1000 iterations, 4096-byte history; exact ASCII text retained.", + f"// Canonical source SHA-256: {digest}", + "#pragma once", '#include "ReaderStreamLookup.h"', + "namespace mesh { namespace bible { namespace generated {", + "static_assert(kBlockSize == 4096, \"Reader history must remain 4096 bytes\");", + "static const uint8_t readerData[] = {"] + for start in range(0, len(packed), 20): + lines.append(" " + ",".join(f"0x{v:02x}" for v in packed[start:start + 20]) + ",") + lines += ["};", "static const uint8_t readerWords[] = {"] + for start in range(0, len(dictionary), 20): + lines.append(" " + ",".join(f"0x{v:02x}" for v in dictionary[start:start + 20]) + ",") + if not dictionary: + lines.append(" 0, // Empty vocabulary: literal records remain supported.") + lines += ["};", "static const StreamTokenCorpus readerCorpus = {", + f" {json.dumps(document['translation'])},", + f" {json.dumps(document['attribution'])},", + f" readerData, sizeof(readerData), {len(plain)},", + f" readerWords, {len(dictionary)}, {len(dictionary_plain)},", + f" {len(words)}, kVerseCount, {chunk_count}", "};", "}}}", ""] + return "\n".join(lines) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--source", type=Path, default=blocks.SOURCE) + parser.add_argument("--output", type=Path, default=HEADER) + parser.add_argument("--check", action="store_true") + args = parser.parse_args() + document = json.loads(args.source.read_text(encoding="utf-8")) + header = render(document) + if args.check: + if args.output.read_text(encoding="ascii") != header: + raise SystemExit("Reader stream header differs; regenerate with tools/bible/pack_reader_stream.py") + else: + args.output.write_text(header, encoding="ascii", newline="\n") + packed, plain, words, chunk_count = encode(document) + dictionary_plain = b"\0".join(words) + b"\0" if words else b"" + dictionary_size = len(compress_4k(dictionary_plain)) if words else 0 + print(f"Reader stream: {len(blocks.references())} unchanged verses, " + f"{len(plain)} encoded bytes -> {len(packed)} DEFLATE + " + f"{dictionary_size} dictionary bytes; {chunk_count} independent streams, 4096-byte history") + + +if __name__ == "__main__": + main() diff --git a/tools/bible/reader-stream-tokens.json b/tools/bible/reader-stream-tokens.json new file mode 100644 index 000000000..e5983fc04 --- /dev/null +++ b/tools/bible/reader-stream-tokens.json @@ -0,0 +1,130 @@ +[ + " the", + " you", + " and", + " that", + " him", + " said", + " to", + " therefore", + " of", + " have", + " will", + " Jesus", + " Father", + " he", + " follow", + " who", + " answered", + " receive", + " word", + " was", + " in", + " because", + " this", + " me", + " his", + " things", + " is", + " from", + " for", + "Jesus", + " world", + " know", + " believe", + " with", + " but", + " which", + " give", + " I", + " came", + " not", + " saw", + " made", + " are", + " Jews", + " love", + " come", + " has", + " my", + " also", + " about", + " God", + " where", + " judgment", + " it", + " out", + " were", + " had", + " those", + " sent", + " sign", + " certain", + " when", + " than", + " would", + " again", + " man", + " life", + " multitude", + " may", + " judge", + " himself", + " be", + " Pharisees", + " do", + " Peter", + " tell", + " first", + " by", + " speak", + " a", + " gave", + " all", + " up", + " unless", + "You", + "The", + " go", + " now", + " Jerusalem", + "Lord", + " knew", + " take", + " so", + " disciple", + " already", + " revealed", + " did", + " prophet", + " see", + " we", + " Son", + " synagogue", + " say", + " Christ", + " time", + " into", + " she", + " going", + "and", + "Philip", + " Scripture", + "for", + " eternal", + " remain", + " no", + " written", + " concerning", + " place", + " should", + " enter", + " bread", + " what", + " one", + " day", + " spoke", + " on", + " them", + " called" +] diff --git a/tools/bible/zopfli4k/LICENSE b/tools/bible/zopfli4k/LICENSE new file mode 100644 index 000000000..2e6453047 --- /dev/null +++ b/tools/bible/zopfli4k/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2011 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/tools/bible/zopfli4k/README.meshcore.txt b/tools/bible/zopfli4k/README.meshcore.txt new file mode 100644 index 000000000..7b1fef4b1 --- /dev/null +++ b/tools/bible/zopfli4k/README.meshcore.txt @@ -0,0 +1,20 @@ +Host-only Google Zopfli with a 4 KiB match window +================================================ + +Sources: Google Zopfli, bundled in the pinned python-zopfli 0.4.3 source release. +https://github.com/google/zopfli +https://pypi.org/project/zopfli/0.4.3/ +Source archive: zopfli-0.4.3.tar.gz +SHA-256: d3a50f91a13cea9bafe025de8fd87a005eb26de02a4f0c193127ddbf23ac8ebe +License: Apache-2.0, preserved in LICENSE and the upstream source headers. + +MeshCore changes: util.h sets ZOPFLI_WINDOW_SIZE to 4096 instead of 32768. +reader_pack.c is a small MeshCore command-line adapter. All other C/header +files are unchanged from that archive. This tool runs only on the build host +when regenerating Reader data. It is never compiled into firmware. + +The 4 KiB limit is required by the existing on-device Reader scratch buffer. +The ordinary python-zopfli package has no configurable match-window API. +The generator compiles these pinned sources with a host C compiler and uses +1000 optimization iterations. No network access is required. Ordinary firmware +builds use the committed generated header and do not invoke this tool. diff --git a/tools/bible/zopfli4k/blocksplitter.c b/tools/bible/zopfli4k/blocksplitter.c new file mode 100644 index 000000000..161783d89 --- /dev/null +++ b/tools/bible/zopfli4k/blocksplitter.c @@ -0,0 +1,332 @@ +/* +Copyright 2011 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +#include "blocksplitter.h" + +#include +#include +#include + +#include "deflate.h" +#include "squeeze.h" +#include "tree.h" +#include "util.h" + +/* +The "f" for the FindMinimum function below. +i: the current parameter of f(i) +context: for your implementation +*/ +typedef double FindMinimumFun(size_t i, void* context); + +/* +Finds minimum of function f(i) where is is of type size_t, f(i) is of type +double, i is in range start-end (excluding end). +Outputs the minimum value in *smallest and returns the index of this value. +*/ +static size_t FindMinimum(FindMinimumFun f, void* context, + size_t start, size_t end, double* smallest) { + if (end - start < 1024) { + double best = ZOPFLI_LARGE_FLOAT; + size_t result = start; + size_t i; + for (i = start; i < end; i++) { + double v = f(i, context); + if (v < best) { + best = v; + result = i; + } + } + *smallest = best; + return result; + } else { + /* Try to find minimum faster by recursively checking multiple points. */ +#define NUM 9 /* Good value: 9. */ + size_t i; + size_t p[NUM]; + double vp[NUM]; + size_t besti; + double best; + double lastbest = ZOPFLI_LARGE_FLOAT; + size_t pos = start; + + for (;;) { + if (end - start <= NUM) break; + + for (i = 0; i < NUM; i++) { + p[i] = start + (i + 1) * ((end - start) / (NUM + 1)); + vp[i] = f(p[i], context); + } + besti = 0; + best = vp[0]; + for (i = 1; i < NUM; i++) { + if (vp[i] < best) { + best = vp[i]; + besti = i; + } + } + if (best > lastbest) break; + + start = besti == 0 ? start : p[besti - 1]; + end = besti == NUM - 1 ? end : p[besti + 1]; + + pos = p[besti]; + lastbest = best; + } + *smallest = lastbest; + return pos; +#undef NUM + } +} + +/* +Returns estimated cost of a block in bits. It includes the size to encode the +tree and the size to encode all literal, length and distance symbols and their +extra bits. + +litlens: lz77 lit/lengths +dists: ll77 distances +lstart: start of block +lend: end of block (not inclusive) +*/ +static double EstimateCost(const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend) { + return ZopfliCalculateBlockSizeAutoType(lz77, lstart, lend); +} + +typedef struct SplitCostContext { + const ZopfliLZ77Store* lz77; + size_t start; + size_t end; +} SplitCostContext; + + +/* +Gets the cost which is the sum of the cost of the left and the right section +of the data. +type: FindMinimumFun +*/ +static double SplitCost(size_t i, void* context) { + SplitCostContext* c = (SplitCostContext*)context; + return EstimateCost(c->lz77, c->start, i) + EstimateCost(c->lz77, i, c->end); +} + +static void AddSorted(size_t value, size_t** out, size_t* outsize) { + size_t i; + ZOPFLI_APPEND_DATA(value, out, outsize); + for (i = 0; i + 1 < *outsize; i++) { + if ((*out)[i] > value) { + size_t j; + for (j = *outsize - 1; j > i; j--) { + (*out)[j] = (*out)[j - 1]; + } + (*out)[i] = value; + break; + } + } +} + +/* +Prints the block split points as decimal and hex values in the terminal. +*/ +static void PrintBlockSplitPoints(const ZopfliLZ77Store* lz77, + const size_t* lz77splitpoints, + size_t nlz77points) { + size_t* splitpoints = 0; + size_t npoints = 0; + size_t i; + /* The input is given as lz77 indices, but we want to see the uncompressed + index values. */ + size_t pos = 0; + if (nlz77points > 0) { + for (i = 0; i < lz77->size; i++) { + size_t length = lz77->dists[i] == 0 ? 1 : lz77->litlens[i]; + if (lz77splitpoints[npoints] == i) { + ZOPFLI_APPEND_DATA(pos, &splitpoints, &npoints); + if (npoints == nlz77points) break; + } + pos += length; + } + } + assert(npoints == nlz77points); + + fprintf(stderr, "block split points: "); + for (i = 0; i < npoints; i++) { + fprintf(stderr, "%d ", (int)splitpoints[i]); + } + fprintf(stderr, "(hex:"); + for (i = 0; i < npoints; i++) { + fprintf(stderr, " %x", (int)splitpoints[i]); + } + fprintf(stderr, ")\n"); + + free(splitpoints); +} + +/* +Finds next block to try to split, the largest of the available ones. +The largest is chosen to make sure that if only a limited amount of blocks is +requested, their sizes are spread evenly. +lz77size: the size of the LL77 data, which is the size of the done array here. +done: array indicating which blocks starting at that position are no longer + splittable (splitting them increases rather than decreases cost). +splitpoints: the splitpoints found so far. +npoints: the amount of splitpoints found so far. +lstart: output variable, giving start of block. +lend: output variable, giving end of block. +returns 1 if a block was found, 0 if no block found (all are done). +*/ +static int FindLargestSplittableBlock( + size_t lz77size, const unsigned char* done, + const size_t* splitpoints, size_t npoints, + size_t* lstart, size_t* lend) { + size_t longest = 0; + int found = 0; + size_t i; + for (i = 0; i <= npoints; i++) { + size_t start = i == 0 ? 0 : splitpoints[i - 1]; + size_t end = i == npoints ? lz77size - 1 : splitpoints[i]; + if (!done[start] && end - start > longest) { + *lstart = start; + *lend = end; + found = 1; + longest = end - start; + } + } + return found; +} + +void ZopfliBlockSplitLZ77(const ZopfliOptions* options, + const ZopfliLZ77Store* lz77, size_t maxblocks, + size_t** splitpoints, size_t* npoints) { + size_t lstart, lend; + size_t i; + size_t llpos = 0; + size_t numblocks = 1; + unsigned char* done; + double splitcost, origcost; + + if (lz77->size < 10) return; /* This code fails on tiny files. */ + + done = (unsigned char*)malloc(lz77->size); + if (!done) exit(-1); /* Allocation failed. */ + for (i = 0; i < lz77->size; i++) done[i] = 0; + + lstart = 0; + lend = lz77->size; + for (;;) { + SplitCostContext c; + + if (maxblocks > 0 && numblocks >= maxblocks) { + break; + } + + c.lz77 = lz77; + c.start = lstart; + c.end = lend; + assert(lstart < lend); + llpos = FindMinimum(SplitCost, &c, lstart + 1, lend, &splitcost); + + assert(llpos > lstart); + assert(llpos < lend); + + origcost = EstimateCost(lz77, lstart, lend); + + if (splitcost > origcost || llpos == lstart + 1 || llpos == lend) { + done[lstart] = 1; + } else { + AddSorted(llpos, splitpoints, npoints); + numblocks++; + } + + if (!FindLargestSplittableBlock( + lz77->size, done, *splitpoints, *npoints, &lstart, &lend)) { + break; /* No further split will probably reduce compression. */ + } + + if (lend - lstart < 10) { + break; + } + } + + if (options->verbose) { + PrintBlockSplitPoints(lz77, *splitpoints, *npoints); + } + + free(done); +} + +void ZopfliBlockSplit(const ZopfliOptions* options, + const unsigned char* in, size_t instart, size_t inend, + size_t maxblocks, size_t** splitpoints, size_t* npoints) { + size_t pos = 0; + size_t i; + ZopfliBlockState s; + size_t* lz77splitpoints = 0; + size_t nlz77points = 0; + ZopfliLZ77Store store; + ZopfliHash hash; + ZopfliHash* h = &hash; + + ZopfliInitLZ77Store(in, &store); + ZopfliInitBlockState(options, instart, inend, 0, &s); + ZopfliAllocHash(ZOPFLI_WINDOW_SIZE, h); + + *npoints = 0; + *splitpoints = 0; + + /* Unintuitively, Using a simple LZ77 method here instead of ZopfliLZ77Optimal + results in better blocks. */ + ZopfliLZ77Greedy(&s, in, instart, inend, &store, h); + + ZopfliBlockSplitLZ77(options, + &store, maxblocks, + &lz77splitpoints, &nlz77points); + + /* Convert LZ77 positions to positions in the uncompressed input. */ + pos = instart; + if (nlz77points > 0) { + for (i = 0; i < store.size; i++) { + size_t length = store.dists[i] == 0 ? 1 : store.litlens[i]; + if (lz77splitpoints[*npoints] == i) { + ZOPFLI_APPEND_DATA(pos, splitpoints, npoints); + if (*npoints == nlz77points) break; + } + pos += length; + } + } + assert(*npoints == nlz77points); + + free(lz77splitpoints); + ZopfliCleanBlockState(&s); + ZopfliCleanLZ77Store(&store); + ZopfliCleanHash(h); +} + +void ZopfliBlockSplitSimple(const unsigned char* in, + size_t instart, size_t inend, + size_t blocksize, + size_t** splitpoints, size_t* npoints) { + size_t i = instart; + while (i < inend) { + ZOPFLI_APPEND_DATA(i, splitpoints, npoints); + i += blocksize; + } + (void)in; +} diff --git a/tools/bible/zopfli4k/blocksplitter.h b/tools/bible/zopfli4k/blocksplitter.h new file mode 100644 index 000000000..d1d622f1b --- /dev/null +++ b/tools/bible/zopfli4k/blocksplitter.h @@ -0,0 +1,73 @@ +/* +Copyright 2011 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +/* +Functions to choose good boundaries for block splitting. Deflate allows encoding +the data in multiple blocks, with a separate Huffman tree for each block. The +Huffman tree itself requires some bytes to encode, so by choosing certain +blocks, you can either hurt, or enhance compression. These functions choose good +ones that enhance it. +*/ + +#ifndef ZOPFLI_BLOCKSPLITTER_H_ +#define ZOPFLI_BLOCKSPLITTER_H_ + +#include + +#include "lz77.h" +#include "zopfli.h" + + +/* +Does blocksplitting on LZ77 data. +The output splitpoints are indices in the LZ77 data. +maxblocks: set a limit to the amount of blocks. Set to 0 to mean no limit. +*/ +void ZopfliBlockSplitLZ77(const ZopfliOptions* options, + const ZopfliLZ77Store* lz77, size_t maxblocks, + size_t** splitpoints, size_t* npoints); + +/* +Does blocksplitting on uncompressed data. +The output splitpoints are indices in the uncompressed bytes. + +options: general program options. +in: uncompressed input data +instart: where to start splitting +inend: where to end splitting (not inclusive) +maxblocks: maximum amount of blocks to split into, or 0 for no limit +splitpoints: dynamic array to put the resulting split point coordinates into. + The coordinates are indices in the input array. +npoints: pointer to amount of splitpoints, for the dynamic array. The amount of + blocks is the amount of splitpoitns + 1. +*/ +void ZopfliBlockSplit(const ZopfliOptions* options, + const unsigned char* in, size_t instart, size_t inend, + size_t maxblocks, size_t** splitpoints, size_t* npoints); + +/* +Divides the input into equal blocks, does not even take LZ77 lengths into +account. +*/ +void ZopfliBlockSplitSimple(const unsigned char* in, + size_t instart, size_t inend, + size_t blocksize, + size_t** splitpoints, size_t* npoints); + +#endif /* ZOPFLI_BLOCKSPLITTER_H_ */ diff --git a/tools/bible/zopfli4k/cache.c b/tools/bible/zopfli4k/cache.c new file mode 100644 index 000000000..6b9444f0d --- /dev/null +++ b/tools/bible/zopfli4k/cache.c @@ -0,0 +1,125 @@ +/* +Copyright 2011 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +#include "cache.h" + +#include +#include +#include + +#ifdef ZOPFLI_LONGEST_MATCH_CACHE + +void ZopfliInitCache(size_t blocksize, ZopfliLongestMatchCache* lmc) { + size_t i; + lmc->length = (unsigned short*)malloc(sizeof(unsigned short) * blocksize); + lmc->dist = (unsigned short*)malloc(sizeof(unsigned short) * blocksize); + /* Rather large amount of memory. */ + lmc->sublen = (unsigned char*)malloc(ZOPFLI_CACHE_LENGTH * 3 * blocksize); + if(lmc->sublen == NULL) { + fprintf(stderr, + "Error: Out of memory. Tried allocating %lu bytes of memory.\n", + (unsigned long)ZOPFLI_CACHE_LENGTH * 3 * blocksize); + exit (EXIT_FAILURE); + } + + /* length > 0 and dist 0 is invalid combination, which indicates on purpose + that this cache value is not filled in yet. */ + for (i = 0; i < blocksize; i++) lmc->length[i] = 1; + for (i = 0; i < blocksize; i++) lmc->dist[i] = 0; + for (i = 0; i < ZOPFLI_CACHE_LENGTH * blocksize * 3; i++) lmc->sublen[i] = 0; +} + +void ZopfliCleanCache(ZopfliLongestMatchCache* lmc) { + free(lmc->length); + free(lmc->dist); + free(lmc->sublen); +} + +void ZopfliSublenToCache(const unsigned short* sublen, + size_t pos, size_t length, + ZopfliLongestMatchCache* lmc) { + size_t i; + size_t j = 0; + unsigned bestlength = 0; + unsigned char* cache; + +#if ZOPFLI_CACHE_LENGTH == 0 + return; +#endif + + cache = &lmc->sublen[ZOPFLI_CACHE_LENGTH * pos * 3]; + if (length < 3) return; + for (i = 3; i <= length; i++) { + if (i == length || sublen[i] != sublen[i + 1]) { + cache[j * 3] = i - 3; + cache[j * 3 + 1] = sublen[i] % 256; + cache[j * 3 + 2] = (sublen[i] >> 8) % 256; + bestlength = i; + j++; + if (j >= ZOPFLI_CACHE_LENGTH) break; + } + } + if (j < ZOPFLI_CACHE_LENGTH) { + assert(bestlength == length); + cache[(ZOPFLI_CACHE_LENGTH - 1) * 3] = bestlength - 3; + } else { + assert(bestlength <= length); + } + assert(bestlength == ZopfliMaxCachedSublen(lmc, pos, length)); +} + +void ZopfliCacheToSublen(const ZopfliLongestMatchCache* lmc, + size_t pos, size_t length, + unsigned short* sublen) { + size_t i, j; + unsigned maxlength = ZopfliMaxCachedSublen(lmc, pos, length); + unsigned prevlength = 0; + unsigned char* cache; +#if ZOPFLI_CACHE_LENGTH == 0 + return; +#endif + if (length < 3) return; + cache = &lmc->sublen[ZOPFLI_CACHE_LENGTH * pos * 3]; + for (j = 0; j < ZOPFLI_CACHE_LENGTH; j++) { + unsigned length = cache[j * 3] + 3; + unsigned dist = cache[j * 3 + 1] + 256 * cache[j * 3 + 2]; + for (i = prevlength; i <= length; i++) { + sublen[i] = dist; + } + if (length == maxlength) break; + prevlength = length + 1; + } +} + +/* +Returns the length up to which could be stored in the cache. +*/ +unsigned ZopfliMaxCachedSublen(const ZopfliLongestMatchCache* lmc, + size_t pos, size_t length) { + unsigned char* cache; +#if ZOPFLI_CACHE_LENGTH == 0 + return 0; +#endif + cache = &lmc->sublen[ZOPFLI_CACHE_LENGTH * pos * 3]; + (void)length; + if (cache[1] == 0 && cache[2] == 0) return 0; /* No sublen cached. */ + return cache[(ZOPFLI_CACHE_LENGTH - 1) * 3] + 3; +} + +#endif /* ZOPFLI_LONGEST_MATCH_CACHE */ diff --git a/tools/bible/zopfli4k/cache.h b/tools/bible/zopfli4k/cache.h new file mode 100644 index 000000000..5ca0c5015 --- /dev/null +++ b/tools/bible/zopfli4k/cache.h @@ -0,0 +1,66 @@ +/* +Copyright 2011 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +/* +The cache that speeds up ZopfliFindLongestMatch of lz77.c. +*/ + +#ifndef ZOPFLI_CACHE_H_ +#define ZOPFLI_CACHE_H_ + +#include "util.h" + +#ifdef ZOPFLI_LONGEST_MATCH_CACHE + +/* +Cache used by ZopfliFindLongestMatch to remember previously found length/dist +values. +This is needed because the squeeze runs will ask these values multiple times for +the same position. +Uses large amounts of memory, since it has to remember the distance belonging +to every possible shorter-than-the-best length (the so called "sublen" array). +*/ +typedef struct ZopfliLongestMatchCache { + unsigned short* length; + unsigned short* dist; + unsigned char* sublen; +} ZopfliLongestMatchCache; + +/* Initializes the ZopfliLongestMatchCache. */ +void ZopfliInitCache(size_t blocksize, ZopfliLongestMatchCache* lmc); + +/* Frees up the memory of the ZopfliLongestMatchCache. */ +void ZopfliCleanCache(ZopfliLongestMatchCache* lmc); + +/* Stores sublen array in the cache. */ +void ZopfliSublenToCache(const unsigned short* sublen, + size_t pos, size_t length, + ZopfliLongestMatchCache* lmc); + +/* Extracts sublen array from the cache. */ +void ZopfliCacheToSublen(const ZopfliLongestMatchCache* lmc, + size_t pos, size_t length, + unsigned short* sublen); +/* Returns the length up to which could be stored in the cache. */ +unsigned ZopfliMaxCachedSublen(const ZopfliLongestMatchCache* lmc, + size_t pos, size_t length); + +#endif /* ZOPFLI_LONGEST_MATCH_CACHE */ + +#endif /* ZOPFLI_CACHE_H_ */ diff --git a/tools/bible/zopfli4k/deflate.c b/tools/bible/zopfli4k/deflate.c new file mode 100644 index 000000000..c5abda9ea --- /dev/null +++ b/tools/bible/zopfli4k/deflate.c @@ -0,0 +1,931 @@ +/* +Copyright 2011 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +#include "deflate.h" + +#include +#include +#include + +#include "blocksplitter.h" +#include "squeeze.h" +#include "symbols.h" +#include "tree.h" + +/* +bp = bitpointer, always in range [0, 7]. +The outsize is number of necessary bytes to encode the bits. +Given the value of bp and the amount of bytes, the amount of bits represented +is not simply bytesize * 8 + bp because even representing one bit requires a +whole byte. It is: (bp == 0) ? (bytesize * 8) : ((bytesize - 1) * 8 + bp) +*/ +static void AddBit(int bit, + unsigned char* bp, unsigned char** out, size_t* outsize) { + if (*bp == 0) ZOPFLI_APPEND_DATA(0, out, outsize); + (*out)[*outsize - 1] |= bit << *bp; + *bp = (*bp + 1) & 7; +} + +static void AddBits(unsigned symbol, unsigned length, + unsigned char* bp, unsigned char** out, size_t* outsize) { + /* TODO(lode): make more efficient (add more bits at once). */ + unsigned i; + for (i = 0; i < length; i++) { + unsigned bit = (symbol >> i) & 1; + if (*bp == 0) ZOPFLI_APPEND_DATA(0, out, outsize); + (*out)[*outsize - 1] |= bit << *bp; + *bp = (*bp + 1) & 7; + } +} + +/* +Adds bits, like AddBits, but the order is inverted. The deflate specification +uses both orders in one standard. +*/ +static void AddHuffmanBits(unsigned symbol, unsigned length, + unsigned char* bp, unsigned char** out, + size_t* outsize) { + /* TODO(lode): make more efficient (add more bits at once). */ + unsigned i; + for (i = 0; i < length; i++) { + unsigned bit = (symbol >> (length - i - 1)) & 1; + if (*bp == 0) ZOPFLI_APPEND_DATA(0, out, outsize); + (*out)[*outsize - 1] |= bit << *bp; + *bp = (*bp + 1) & 7; + } +} + +/* +Ensures there are at least 2 distance codes to support buggy decoders. +Zlib 1.2.1 and below have a bug where it fails if there isn't at least 1 +distance code (with length > 0), even though it's valid according to the +deflate spec to have 0 distance codes. On top of that, some mobile phones +require at least two distance codes. To support these decoders too (but +potentially at the cost of a few bytes), add dummy code lengths of 1. +References to this bug can be found in the changelog of +Zlib 1.2.2 and here: http://www.jonof.id.au/forum/index.php?topic=515.0. + +d_lengths: the 32 lengths of the distance codes. +*/ +static void PatchDistanceCodesForBuggyDecoders(unsigned* d_lengths) { + int num_dist_codes = 0; /* Amount of non-zero distance codes */ + int i; + for (i = 0; i < 30 /* Ignore the two unused codes from the spec */; i++) { + if (d_lengths[i]) num_dist_codes++; + if (num_dist_codes >= 2) return; /* Two or more codes is fine. */ + } + + if (num_dist_codes == 0) { + d_lengths[0] = d_lengths[1] = 1; + } else if (num_dist_codes == 1) { + d_lengths[d_lengths[0] ? 1 : 0] = 1; + } +} + +/* +Encodes the Huffman tree and returns how many bits its encoding takes. If out +is a null pointer, only returns the size and runs faster. +*/ +static size_t EncodeTree(const unsigned* ll_lengths, + const unsigned* d_lengths, + int use_16, int use_17, int use_18, + unsigned char* bp, + unsigned char** out, size_t* outsize) { + unsigned lld_total; /* Total amount of literal, length, distance codes. */ + /* Runlength encoded version of lengths of litlen and dist trees. */ + unsigned* rle = 0; + unsigned* rle_bits = 0; /* Extra bits for rle values 16, 17 and 18. */ + size_t rle_size = 0; /* Size of rle array. */ + size_t rle_bits_size = 0; /* Should have same value as rle_size. */ + unsigned hlit = 29; /* 286 - 257 */ + unsigned hdist = 29; /* 32 - 1, but gzip does not like hdist > 29.*/ + unsigned hclen; + unsigned hlit2; + size_t i, j; + size_t clcounts[19]; + unsigned clcl[19]; /* Code length code lengths. */ + unsigned clsymbols[19]; + /* The order in which code length code lengths are encoded as per deflate. */ + static const unsigned order[19] = { + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 + }; + int size_only = !out; + size_t result_size = 0; + + for(i = 0; i < 19; i++) clcounts[i] = 0; + + /* Trim zeros. */ + while (hlit > 0 && ll_lengths[257 + hlit - 1] == 0) hlit--; + while (hdist > 0 && d_lengths[1 + hdist - 1] == 0) hdist--; + hlit2 = hlit + 257; + + lld_total = hlit2 + hdist + 1; + + for (i = 0; i < lld_total; i++) { + /* This is an encoding of a huffman tree, so now the length is a symbol */ + unsigned char symbol = i < hlit2 ? ll_lengths[i] : d_lengths[i - hlit2]; + unsigned count = 1; + if(use_16 || (symbol == 0 && (use_17 || use_18))) { + for (j = i + 1; j < lld_total && symbol == + (j < hlit2 ? ll_lengths[j] : d_lengths[j - hlit2]); j++) { + count++; + } + } + i += count - 1; + + /* Repetitions of zeroes */ + if (symbol == 0 && count >= 3) { + if (use_18) { + while (count >= 11) { + unsigned count2 = count > 138 ? 138 : count; + if (!size_only) { + ZOPFLI_APPEND_DATA(18, &rle, &rle_size); + ZOPFLI_APPEND_DATA(count2 - 11, &rle_bits, &rle_bits_size); + } + clcounts[18]++; + count -= count2; + } + } + if (use_17) { + while (count >= 3) { + unsigned count2 = count > 10 ? 10 : count; + if (!size_only) { + ZOPFLI_APPEND_DATA(17, &rle, &rle_size); + ZOPFLI_APPEND_DATA(count2 - 3, &rle_bits, &rle_bits_size); + } + clcounts[17]++; + count -= count2; + } + } + } + + /* Repetitions of any symbol */ + if (use_16 && count >= 4) { + count--; /* Since the first one is hardcoded. */ + clcounts[symbol]++; + if (!size_only) { + ZOPFLI_APPEND_DATA(symbol, &rle, &rle_size); + ZOPFLI_APPEND_DATA(0, &rle_bits, &rle_bits_size); + } + while (count >= 3) { + unsigned count2 = count > 6 ? 6 : count; + if (!size_only) { + ZOPFLI_APPEND_DATA(16, &rle, &rle_size); + ZOPFLI_APPEND_DATA(count2 - 3, &rle_bits, &rle_bits_size); + } + clcounts[16]++; + count -= count2; + } + } + + /* No or insufficient repetition */ + clcounts[symbol] += count; + while (count > 0) { + if (!size_only) { + ZOPFLI_APPEND_DATA(symbol, &rle, &rle_size); + ZOPFLI_APPEND_DATA(0, &rle_bits, &rle_bits_size); + } + count--; + } + } + + ZopfliCalculateBitLengths(clcounts, 19, 7, clcl); + if (!size_only) ZopfliLengthsToSymbols(clcl, 19, 7, clsymbols); + + hclen = 15; + /* Trim zeros. */ + while (hclen > 0 && clcounts[order[hclen + 4 - 1]] == 0) hclen--; + + if (!size_only) { + AddBits(hlit, 5, bp, out, outsize); + AddBits(hdist, 5, bp, out, outsize); + AddBits(hclen, 4, bp, out, outsize); + + for (i = 0; i < hclen + 4; i++) { + AddBits(clcl[order[i]], 3, bp, out, outsize); + } + + for (i = 0; i < rle_size; i++) { + unsigned symbol = clsymbols[rle[i]]; + AddHuffmanBits(symbol, clcl[rle[i]], bp, out, outsize); + /* Extra bits. */ + if (rle[i] == 16) AddBits(rle_bits[i], 2, bp, out, outsize); + else if (rle[i] == 17) AddBits(rle_bits[i], 3, bp, out, outsize); + else if (rle[i] == 18) AddBits(rle_bits[i], 7, bp, out, outsize); + } + } + + result_size += 14; /* hlit, hdist, hclen bits */ + result_size += (hclen + 4) * 3; /* clcl bits */ + for(i = 0; i < 19; i++) { + result_size += clcl[i] * clcounts[i]; + } + /* Extra bits. */ + result_size += clcounts[16] * 2; + result_size += clcounts[17] * 3; + result_size += clcounts[18] * 7; + + /* Note: in case of "size_only" these are null pointers so no effect. */ + free(rle); + free(rle_bits); + + return result_size; +} + +static void AddDynamicTree(const unsigned* ll_lengths, + const unsigned* d_lengths, + unsigned char* bp, + unsigned char** out, size_t* outsize) { + int i; + int best = 0; + size_t bestsize = 0; + + for(i = 0; i < 8; i++) { + size_t size = EncodeTree(ll_lengths, d_lengths, + i & 1, i & 2, i & 4, + 0, 0, 0); + if (bestsize == 0 || size < bestsize) { + bestsize = size; + best = i; + } + } + + EncodeTree(ll_lengths, d_lengths, + best & 1, best & 2, best & 4, + bp, out, outsize); +} + +/* +Gives the exact size of the tree, in bits, as it will be encoded in DEFLATE. +*/ +static size_t CalculateTreeSize(const unsigned* ll_lengths, + const unsigned* d_lengths) { + size_t result = 0; + int i; + + for(i = 0; i < 8; i++) { + size_t size = EncodeTree(ll_lengths, d_lengths, + i & 1, i & 2, i & 4, + 0, 0, 0); + if (result == 0 || size < result) result = size; + } + + return result; +} + +/* +Adds all lit/len and dist codes from the lists as huffman symbols. Does not add +end code 256. expected_data_size is the uncompressed block size, used for +assert, but you can set it to 0 to not do the assertion. +*/ +static void AddLZ77Data(const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend, + size_t expected_data_size, + const unsigned* ll_symbols, const unsigned* ll_lengths, + const unsigned* d_symbols, const unsigned* d_lengths, + unsigned char* bp, + unsigned char** out, size_t* outsize) { + size_t testlength = 0; + size_t i; + + for (i = lstart; i < lend; i++) { + unsigned dist = lz77->dists[i]; + unsigned litlen = lz77->litlens[i]; + if (dist == 0) { + assert(litlen < 256); + assert(ll_lengths[litlen] > 0); + AddHuffmanBits(ll_symbols[litlen], ll_lengths[litlen], bp, out, outsize); + testlength++; + } else { + unsigned lls = ZopfliGetLengthSymbol(litlen); + unsigned ds = ZopfliGetDistSymbol(dist); + assert(litlen >= 3 && litlen <= 288); + assert(ll_lengths[lls] > 0); + assert(d_lengths[ds] > 0); + AddHuffmanBits(ll_symbols[lls], ll_lengths[lls], bp, out, outsize); + AddBits(ZopfliGetLengthExtraBitsValue(litlen), + ZopfliGetLengthExtraBits(litlen), + bp, out, outsize); + AddHuffmanBits(d_symbols[ds], d_lengths[ds], bp, out, outsize); + AddBits(ZopfliGetDistExtraBitsValue(dist), + ZopfliGetDistExtraBits(dist), + bp, out, outsize); + testlength += litlen; + } + } + assert(expected_data_size == 0 || testlength == expected_data_size); +} + +static void GetFixedTree(unsigned* ll_lengths, unsigned* d_lengths) { + size_t i; + for (i = 0; i < 144; i++) ll_lengths[i] = 8; + for (i = 144; i < 256; i++) ll_lengths[i] = 9; + for (i = 256; i < 280; i++) ll_lengths[i] = 7; + for (i = 280; i < 288; i++) ll_lengths[i] = 8; + for (i = 0; i < 32; i++) d_lengths[i] = 5; +} + +/* +Same as CalculateBlockSymbolSize, but for block size smaller than histogram +size. +*/ +static size_t CalculateBlockSymbolSizeSmall(const unsigned* ll_lengths, + const unsigned* d_lengths, + const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend) { + size_t result = 0; + size_t i; + for (i = lstart; i < lend; i++) { + assert(i < lz77->size); + assert(lz77->litlens[i] < 259); + if (lz77->dists[i] == 0) { + result += ll_lengths[lz77->litlens[i]]; + } else { + int ll_symbol = ZopfliGetLengthSymbol(lz77->litlens[i]); + int d_symbol = ZopfliGetDistSymbol(lz77->dists[i]); + result += ll_lengths[ll_symbol]; + result += d_lengths[d_symbol]; + result += ZopfliGetLengthSymbolExtraBits(ll_symbol); + result += ZopfliGetDistSymbolExtraBits(d_symbol); + } + } + result += ll_lengths[256]; /*end symbol*/ + return result; +} + +/* +Same as CalculateBlockSymbolSize, but with the histogram provided by the caller. +*/ +static size_t CalculateBlockSymbolSizeGivenCounts(const size_t* ll_counts, + const size_t* d_counts, + const unsigned* ll_lengths, + const unsigned* d_lengths, + const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend) { + size_t result = 0; + size_t i; + if (lstart + ZOPFLI_NUM_LL * 3 > lend) { + return CalculateBlockSymbolSizeSmall( + ll_lengths, d_lengths, lz77, lstart, lend); + } else { + for (i = 0; i < 256; i++) { + result += ll_lengths[i] * ll_counts[i]; + } + for (i = 257; i < 286; i++) { + result += ll_lengths[i] * ll_counts[i]; + result += ZopfliGetLengthSymbolExtraBits(i) * ll_counts[i]; + } + for (i = 0; i < 30; i++) { + result += d_lengths[i] * d_counts[i]; + result += ZopfliGetDistSymbolExtraBits(i) * d_counts[i]; + } + result += ll_lengths[256]; /*end symbol*/ + return result; + } +} + +/* +Calculates size of the part after the header and tree of an LZ77 block, in bits. +*/ +static size_t CalculateBlockSymbolSize(const unsigned* ll_lengths, + const unsigned* d_lengths, + const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend) { + if (lstart + ZOPFLI_NUM_LL * 3 > lend) { + return CalculateBlockSymbolSizeSmall( + ll_lengths, d_lengths, lz77, lstart, lend); + } else { + size_t ll_counts[ZOPFLI_NUM_LL]; + size_t d_counts[ZOPFLI_NUM_D]; + ZopfliLZ77GetHistogram(lz77, lstart, lend, ll_counts, d_counts); + return CalculateBlockSymbolSizeGivenCounts( + ll_counts, d_counts, ll_lengths, d_lengths, lz77, lstart, lend); + } +} + +static size_t AbsDiff(size_t x, size_t y) { + if (x > y) + return x - y; + else + return y - x; +} + +/* +Changes the population counts in a way that the consequent Huffman tree +compression, especially its rle-part, will be more likely to compress this data +more efficiently. length contains the size of the histogram. +*/ +void OptimizeHuffmanForRle(int length, size_t* counts) { + int i, k, stride; + size_t symbol, sum, limit; + int* good_for_rle; + + /* 1) We don't want to touch the trailing zeros. We may break the + rules of the format by adding more data in the distance codes. */ + for (; length >= 0; --length) { + if (length == 0) { + return; + } + if (counts[length - 1] != 0) { + /* Now counts[0..length - 1] does not have trailing zeros. */ + break; + } + } + /* 2) Let's mark all population counts that already can be encoded + with an rle code.*/ + good_for_rle = (int*)malloc((unsigned)length * sizeof(int)); + for (i = 0; i < length; ++i) good_for_rle[i] = 0; + + /* Let's not spoil any of the existing good rle codes. + Mark any seq of 0's that is longer than 5 as a good_for_rle. + Mark any seq of non-0's that is longer than 7 as a good_for_rle.*/ + symbol = counts[0]; + stride = 0; + for (i = 0; i < length + 1; ++i) { + if (i == length || counts[i] != symbol) { + if ((symbol == 0 && stride >= 5) || (symbol != 0 && stride >= 7)) { + for (k = 0; k < stride; ++k) { + good_for_rle[i - k - 1] = 1; + } + } + stride = 1; + if (i != length) { + symbol = counts[i]; + } + } else { + ++stride; + } + } + + /* 3) Let's replace those population counts that lead to more rle codes. */ + stride = 0; + limit = counts[0]; + sum = 0; + for (i = 0; i < length + 1; ++i) { + if (i == length || good_for_rle[i] + /* Heuristic for selecting the stride ranges to collapse. */ + || AbsDiff(counts[i], limit) >= 4) { + if (stride >= 4 || (stride >= 3 && sum == 0)) { + /* The stride must end, collapse what we have, if we have enough (4). */ + int count = (sum + stride / 2) / stride; + if (count < 1) count = 1; + if (sum == 0) { + /* Don't make an all zeros stride to be upgraded to ones. */ + count = 0; + } + for (k = 0; k < stride; ++k) { + /* We don't want to change value at counts[i], + that is already belonging to the next stride. Thus - 1. */ + counts[i - k - 1] = count; + } + } + stride = 0; + sum = 0; + if (i < length - 3) { + /* All interesting strides have a count of at least 4, + at least when non-zeros. */ + limit = (counts[i] + counts[i + 1] + + counts[i + 2] + counts[i + 3] + 2) / 4; + } else if (i < length) { + limit = counts[i]; + } else { + limit = 0; + } + } + ++stride; + if (i != length) { + sum += counts[i]; + } + } + + free(good_for_rle); +} + +/* +Tries out OptimizeHuffmanForRle for this block, if the result is smaller, +uses it, otherwise keeps the original. Returns size of encoded tree and data in +bits, not including the 3-bit block header. +*/ +static double TryOptimizeHuffmanForRle( + const ZopfliLZ77Store* lz77, size_t lstart, size_t lend, + const size_t* ll_counts, const size_t* d_counts, + unsigned* ll_lengths, unsigned* d_lengths) { + size_t ll_counts2[ZOPFLI_NUM_LL]; + size_t d_counts2[ZOPFLI_NUM_D]; + unsigned ll_lengths2[ZOPFLI_NUM_LL]; + unsigned d_lengths2[ZOPFLI_NUM_D]; + double treesize; + double datasize; + double treesize2; + double datasize2; + + treesize = CalculateTreeSize(ll_lengths, d_lengths); + datasize = CalculateBlockSymbolSizeGivenCounts(ll_counts, d_counts, + ll_lengths, d_lengths, lz77, lstart, lend); + + memcpy(ll_counts2, ll_counts, sizeof(ll_counts2)); + memcpy(d_counts2, d_counts, sizeof(d_counts2)); + OptimizeHuffmanForRle(ZOPFLI_NUM_LL, ll_counts2); + OptimizeHuffmanForRle(ZOPFLI_NUM_D, d_counts2); + ZopfliCalculateBitLengths(ll_counts2, ZOPFLI_NUM_LL, 15, ll_lengths2); + ZopfliCalculateBitLengths(d_counts2, ZOPFLI_NUM_D, 15, d_lengths2); + PatchDistanceCodesForBuggyDecoders(d_lengths2); + + treesize2 = CalculateTreeSize(ll_lengths2, d_lengths2); + datasize2 = CalculateBlockSymbolSizeGivenCounts(ll_counts, d_counts, + ll_lengths2, d_lengths2, lz77, lstart, lend); + + if (treesize2 + datasize2 < treesize + datasize) { + memcpy(ll_lengths, ll_lengths2, sizeof(ll_lengths2)); + memcpy(d_lengths, d_lengths2, sizeof(d_lengths2)); + return treesize2 + datasize2; + } + return treesize + datasize; +} + +/* +Calculates the bit lengths for the symbols for dynamic blocks. Chooses bit +lengths that give the smallest size of tree encoding + encoding of all the +symbols to have smallest output size. This are not necessarily the ideal Huffman +bit lengths. Returns size of encoded tree and data in bits, not including the +3-bit block header. +*/ +static double GetDynamicLengths(const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend, + unsigned* ll_lengths, unsigned* d_lengths) { + size_t ll_counts[ZOPFLI_NUM_LL]; + size_t d_counts[ZOPFLI_NUM_D]; + + ZopfliLZ77GetHistogram(lz77, lstart, lend, ll_counts, d_counts); + ll_counts[256] = 1; /* End symbol. */ + ZopfliCalculateBitLengths(ll_counts, ZOPFLI_NUM_LL, 15, ll_lengths); + ZopfliCalculateBitLengths(d_counts, ZOPFLI_NUM_D, 15, d_lengths); + PatchDistanceCodesForBuggyDecoders(d_lengths); + return TryOptimizeHuffmanForRle( + lz77, lstart, lend, ll_counts, d_counts, ll_lengths, d_lengths); +} + +double ZopfliCalculateBlockSize(const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend, int btype) { + unsigned ll_lengths[ZOPFLI_NUM_LL]; + unsigned d_lengths[ZOPFLI_NUM_D]; + + double result = 3; /* bfinal and btype bits */ + + if (btype == 0) { + size_t length = ZopfliLZ77GetByteRange(lz77, lstart, lend); + size_t rem = length % 65535; + size_t blocks = length / 65535 + (rem ? 1 : 0); + /* An uncompressed block must actually be split into multiple blocks if it's + larger than 65535 bytes long. Eeach block header is 5 bytes: 3 bits, + padding, LEN and NLEN (potential less padding for first one ignored). */ + return blocks * 5 * 8 + length * 8; + } if (btype == 1) { + GetFixedTree(ll_lengths, d_lengths); + result += CalculateBlockSymbolSize( + ll_lengths, d_lengths, lz77, lstart, lend); + } else { + result += GetDynamicLengths(lz77, lstart, lend, ll_lengths, d_lengths); + } + + return result; +} + +double ZopfliCalculateBlockSizeAutoType(const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend) { + double uncompressedcost = ZopfliCalculateBlockSize(lz77, lstart, lend, 0); + /* Don't do the expensive fixed cost calculation for larger blocks that are + unlikely to use it. */ + double fixedcost = (lz77->size > 1000) ? + uncompressedcost : ZopfliCalculateBlockSize(lz77, lstart, lend, 1); + double dyncost = ZopfliCalculateBlockSize(lz77, lstart, lend, 2); + return (uncompressedcost < fixedcost && uncompressedcost < dyncost) + ? uncompressedcost + : (fixedcost < dyncost ? fixedcost : dyncost); +} + +/* Since an uncompressed block can be max 65535 in size, it actually adds +multible blocks if needed. */ +static void AddNonCompressedBlock(const ZopfliOptions* options, int final, + const unsigned char* in, size_t instart, + size_t inend, + unsigned char* bp, + unsigned char** out, size_t* outsize) { + size_t pos = instart; + (void)options; + for (;;) { + size_t i; + unsigned short blocksize = 65535; + unsigned short nlen; + int currentfinal; + + if (pos + blocksize > inend) blocksize = inend - pos; + currentfinal = pos + blocksize >= inend; + + nlen = ~blocksize; + + AddBit(final && currentfinal, bp, out, outsize); + /* BTYPE 00 */ + AddBit(0, bp, out, outsize); + AddBit(0, bp, out, outsize); + + /* Any bits of input up to the next byte boundary are ignored. */ + *bp = 0; + + ZOPFLI_APPEND_DATA(blocksize % 256, out, outsize); + ZOPFLI_APPEND_DATA((blocksize / 256) % 256, out, outsize); + ZOPFLI_APPEND_DATA(nlen % 256, out, outsize); + ZOPFLI_APPEND_DATA((nlen / 256) % 256, out, outsize); + + for (i = 0; i < blocksize; i++) { + ZOPFLI_APPEND_DATA(in[pos + i], out, outsize); + } + + if (currentfinal) break; + pos += blocksize; + } +} + +/* +Adds a deflate block with the given LZ77 data to the output. +options: global program options +btype: the block type, must be 1 or 2 +final: whether to set the "final" bit on this block, must be the last block +litlens: literal/length array of the LZ77 data, in the same format as in + ZopfliLZ77Store. +dists: distance array of the LZ77 data, in the same format as in + ZopfliLZ77Store. +lstart: where to start in the LZ77 data +lend: where to end in the LZ77 data (not inclusive) +expected_data_size: the uncompressed block size, used for assert, but you can + set it to 0 to not do the assertion. +bp: output bit pointer +out: dynamic output array to append to +outsize: dynamic output array size +*/ +static void AddLZ77Block(const ZopfliOptions* options, int btype, int final, + const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend, + size_t expected_data_size, + unsigned char* bp, + unsigned char** out, size_t* outsize) { + unsigned ll_lengths[ZOPFLI_NUM_LL]; + unsigned d_lengths[ZOPFLI_NUM_D]; + unsigned ll_symbols[ZOPFLI_NUM_LL]; + unsigned d_symbols[ZOPFLI_NUM_D]; + size_t detect_block_size = *outsize; + size_t compressed_size; + size_t uncompressed_size = 0; + size_t i; + if (btype == 0) { + size_t length = ZopfliLZ77GetByteRange(lz77, lstart, lend); + size_t pos = lstart == lend ? 0 : lz77->pos[lstart]; + size_t end = pos + length; + AddNonCompressedBlock(options, final, + lz77->data, pos, end, bp, out, outsize); + return; + } + + AddBit(final, bp, out, outsize); + AddBit(btype & 1, bp, out, outsize); + AddBit((btype & 2) >> 1, bp, out, outsize); + + if (btype == 1) { + /* Fixed block. */ + GetFixedTree(ll_lengths, d_lengths); + } else { + /* Dynamic block. */ + unsigned detect_tree_size; + assert(btype == 2); + + GetDynamicLengths(lz77, lstart, lend, ll_lengths, d_lengths); + + detect_tree_size = *outsize; + AddDynamicTree(ll_lengths, d_lengths, bp, out, outsize); + if (options->verbose) { + fprintf(stderr, "treesize: %d\n", (int)(*outsize - detect_tree_size)); + } + } + + ZopfliLengthsToSymbols(ll_lengths, ZOPFLI_NUM_LL, 15, ll_symbols); + ZopfliLengthsToSymbols(d_lengths, ZOPFLI_NUM_D, 15, d_symbols); + + detect_block_size = *outsize; + AddLZ77Data(lz77, lstart, lend, expected_data_size, + ll_symbols, ll_lengths, d_symbols, d_lengths, + bp, out, outsize); + /* End symbol. */ + AddHuffmanBits(ll_symbols[256], ll_lengths[256], bp, out, outsize); + + for (i = lstart; i < lend; i++) { + uncompressed_size += lz77->dists[i] == 0 ? 1 : lz77->litlens[i]; + } + compressed_size = *outsize - detect_block_size; + if (options->verbose) { + fprintf(stderr, "compressed block size: %d (%dk) (unc: %d)\n", + (int)compressed_size, (int)(compressed_size / 1024), + (int)(uncompressed_size)); + } +} + +static void AddLZ77BlockAutoType(const ZopfliOptions* options, int final, + const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend, + size_t expected_data_size, + unsigned char* bp, + unsigned char** out, size_t* outsize) { + double uncompressedcost = ZopfliCalculateBlockSize(lz77, lstart, lend, 0); + double fixedcost = ZopfliCalculateBlockSize(lz77, lstart, lend, 1); + double dyncost = ZopfliCalculateBlockSize(lz77, lstart, lend, 2); + + /* Whether to perform the expensive calculation of creating an optimal block + with fixed huffman tree to check if smaller. Only do this for small blocks or + blocks which already are pretty good with fixed huffman tree. */ + int expensivefixed = (lz77->size < 1000) || fixedcost <= dyncost * 1.1; + + ZopfliLZ77Store fixedstore; + if (lstart == lend) { + /* Smallest empty block is represented by fixed block */ + AddBits(final, 1, bp, out, outsize); + AddBits(1, 2, bp, out, outsize); /* btype 01 */ + AddBits(0, 7, bp, out, outsize); /* end symbol has code 0000000 */ + return; + } + ZopfliInitLZ77Store(lz77->data, &fixedstore); + if (expensivefixed) { + /* Recalculate the LZ77 with ZopfliLZ77OptimalFixed */ + size_t instart = lz77->pos[lstart]; + size_t inend = instart + ZopfliLZ77GetByteRange(lz77, lstart, lend); + + ZopfliBlockState s; + ZopfliInitBlockState(options, instart, inend, 1, &s); + ZopfliLZ77OptimalFixed(&s, lz77->data, instart, inend, &fixedstore); + fixedcost = ZopfliCalculateBlockSize(&fixedstore, 0, fixedstore.size, 1); + ZopfliCleanBlockState(&s); + } + + if (uncompressedcost < fixedcost && uncompressedcost < dyncost) { + AddLZ77Block(options, 0, final, lz77, lstart, lend, + expected_data_size, bp, out, outsize); + } else if (fixedcost < dyncost) { + if (expensivefixed) { + AddLZ77Block(options, 1, final, &fixedstore, 0, fixedstore.size, + expected_data_size, bp, out, outsize); + } else { + AddLZ77Block(options, 1, final, lz77, lstart, lend, + expected_data_size, bp, out, outsize); + } + } else { + AddLZ77Block(options, 2, final, lz77, lstart, lend, + expected_data_size, bp, out, outsize); + } + + ZopfliCleanLZ77Store(&fixedstore); +} + +/* +Deflate a part, to allow ZopfliDeflate() to use multiple master blocks if +needed. +It is possible to call this function multiple times in a row, shifting +instart and inend to next bytes of the data. If instart is larger than 0, then +previous bytes are used as the initial dictionary for LZ77. +This function will usually output multiple deflate blocks. If final is 1, then +the final bit will be set on the last block. +*/ +void ZopfliDeflatePart(const ZopfliOptions* options, int btype, int final, + const unsigned char* in, size_t instart, size_t inend, + unsigned char* bp, unsigned char** out, + size_t* outsize) { + size_t i; + /* byte coordinates rather than lz77 index */ + size_t* splitpoints_uncompressed = 0; + size_t npoints = 0; + size_t* splitpoints = 0; + double totalcost = 0; + ZopfliLZ77Store lz77; + + /* If btype=2 is specified, it tries all block types. If a lesser btype is + given, then however it forces that one. Neither of the lesser types needs + block splitting as they have no dynamic huffman trees. */ + if (btype == 0) { + AddNonCompressedBlock(options, final, in, instart, inend, bp, out, outsize); + return; + } else if (btype == 1) { + ZopfliLZ77Store store; + ZopfliBlockState s; + ZopfliInitLZ77Store(in, &store); + ZopfliInitBlockState(options, instart, inend, 1, &s); + + ZopfliLZ77OptimalFixed(&s, in, instart, inend, &store); + AddLZ77Block(options, btype, final, &store, 0, store.size, 0, + bp, out, outsize); + + ZopfliCleanBlockState(&s); + ZopfliCleanLZ77Store(&store); + return; + } + + + if (options->blocksplitting) { + ZopfliBlockSplit(options, in, instart, inend, + options->blocksplittingmax, + &splitpoints_uncompressed, &npoints); + splitpoints = (size_t*)malloc(sizeof(*splitpoints) * npoints); + } + + ZopfliInitLZ77Store(in, &lz77); + + for (i = 0; i <= npoints; i++) { + size_t start = i == 0 ? instart : splitpoints_uncompressed[i - 1]; + size_t end = i == npoints ? inend : splitpoints_uncompressed[i]; + ZopfliBlockState s; + ZopfliLZ77Store store; + ZopfliInitLZ77Store(in, &store); + ZopfliInitBlockState(options, start, end, 1, &s); + ZopfliLZ77Optimal(&s, in, start, end, options->numiterations, &store); + totalcost += ZopfliCalculateBlockSizeAutoType(&store, 0, store.size); + + ZopfliAppendLZ77Store(&store, &lz77); + if (i < npoints) splitpoints[i] = lz77.size; + + ZopfliCleanBlockState(&s); + ZopfliCleanLZ77Store(&store); + } + + /* Second block splitting attempt */ + if (options->blocksplitting && npoints > 1) { + size_t* splitpoints2 = 0; + size_t npoints2 = 0; + double totalcost2 = 0; + + ZopfliBlockSplitLZ77(options, &lz77, + options->blocksplittingmax, &splitpoints2, &npoints2); + + for (i = 0; i <= npoints2; i++) { + size_t start = i == 0 ? 0 : splitpoints2[i - 1]; + size_t end = i == npoints2 ? lz77.size : splitpoints2[i]; + totalcost2 += ZopfliCalculateBlockSizeAutoType(&lz77, start, end); + } + + if (totalcost2 < totalcost) { + free(splitpoints); + splitpoints = splitpoints2; + npoints = npoints2; + } else { + free(splitpoints2); + } + } + + for (i = 0; i <= npoints; i++) { + size_t start = i == 0 ? 0 : splitpoints[i - 1]; + size_t end = i == npoints ? lz77.size : splitpoints[i]; + AddLZ77BlockAutoType(options, i == npoints && final, + &lz77, start, end, 0, + bp, out, outsize); + } + + ZopfliCleanLZ77Store(&lz77); + free(splitpoints); + free(splitpoints_uncompressed); +} + +void ZopfliDeflate(const ZopfliOptions* options, int btype, int final, + const unsigned char* in, size_t insize, + unsigned char* bp, unsigned char** out, size_t* outsize) { + size_t offset = *outsize; +#if ZOPFLI_MASTER_BLOCK_SIZE == 0 + ZopfliDeflatePart(options, btype, final, in, 0, insize, bp, out, outsize); +#else + size_t i = 0; + do { + int masterfinal = (i + ZOPFLI_MASTER_BLOCK_SIZE >= insize); + int final2 = final && masterfinal; + size_t size = masterfinal ? insize - i : ZOPFLI_MASTER_BLOCK_SIZE; + ZopfliDeflatePart(options, btype, final2, + in, i, i + size, bp, out, outsize); + i += size; + } while (i < insize); +#endif + if (options->verbose) { + fprintf(stderr, + "Original Size: %lu, Deflate: %lu, Compression: %f%% Removed\n", + (unsigned long)insize, (unsigned long)(*outsize - offset), + 100.0 * (double)(insize - (*outsize - offset)) / (double)insize); + } +} diff --git a/tools/bible/zopfli4k/deflate.h b/tools/bible/zopfli4k/deflate.h new file mode 100644 index 000000000..fcd9ddc0f --- /dev/null +++ b/tools/bible/zopfli4k/deflate.h @@ -0,0 +1,92 @@ +/* +Copyright 2011 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +#ifndef ZOPFLI_DEFLATE_H_ +#define ZOPFLI_DEFLATE_H_ + +/* +Functions to compress according to the DEFLATE specification, using the +"squeeze" LZ77 compression backend. +*/ + +#include "lz77.h" +#include "zopfli.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* +Compresses according to the deflate specification and append the compressed +result to the output. +This function will usually output multiple deflate blocks. If final is 1, then +the final bit will be set on the last block. + +options: global program options +btype: the deflate block type. Use 2 for best compression. + -0: non compressed blocks (00) + -1: blocks with fixed tree (01) + -2: blocks with dynamic tree (10) +final: whether this is the last section of the input, sets the final bit to the + last deflate block. +in: the input bytes +insize: number of input bytes +bp: bit pointer for the output array. This must initially be 0, and for + consecutive calls must be reused (it can have values from 0-7). This is + because deflate appends blocks as bit-based data, rather than on byte + boundaries. +out: pointer to the dynamic output array to which the result is appended. Must + be freed after use. +outsize: pointer to the dynamic output array size. +*/ +void ZopfliDeflate(const ZopfliOptions* options, int btype, int final, + const unsigned char* in, size_t insize, + unsigned char* bp, unsigned char** out, size_t* outsize); + +/* +Like ZopfliDeflate, but allows to specify start and end byte with instart and +inend. Only that part is compressed, but earlier bytes are still used for the +back window. +*/ +void ZopfliDeflatePart(const ZopfliOptions* options, int btype, int final, + const unsigned char* in, size_t instart, size_t inend, + unsigned char* bp, unsigned char** out, + size_t* outsize); + +/* +Calculates block size in bits. +litlens: lz77 lit/lengths +dists: ll77 distances +lstart: start of block +lend: end of block (not inclusive) +*/ +double ZopfliCalculateBlockSize(const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend, int btype); + +/* +Calculates block size in bits, automatically using the best btype. +*/ +double ZopfliCalculateBlockSizeAutoType(const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* ZOPFLI_DEFLATE_H_ */ diff --git a/tools/bible/zopfli4k/gzip_container.c b/tools/bible/zopfli4k/gzip_container.c new file mode 100644 index 000000000..7615db300 --- /dev/null +++ b/tools/bible/zopfli4k/gzip_container.c @@ -0,0 +1,124 @@ +/* +Copyright 2013 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +#include "gzip_container.h" +#include "util.h" + +#include + +#include "deflate.h" + +/* CRC polynomial: 0xedb88320 */ +static const unsigned long crc32_table[256] = { + 0u, 1996959894u, 3993919788u, 2567524794u, 124634137u, 1886057615u, + 3915621685u, 2657392035u, 249268274u, 2044508324u, 3772115230u, 2547177864u, + 162941995u, 2125561021u, 3887607047u, 2428444049u, 498536548u, 1789927666u, + 4089016648u, 2227061214u, 450548861u, 1843258603u, 4107580753u, 2211677639u, + 325883990u, 1684777152u, 4251122042u, 2321926636u, 335633487u, 1661365465u, + 4195302755u, 2366115317u, 997073096u, 1281953886u, 3579855332u, 2724688242u, + 1006888145u, 1258607687u, 3524101629u, 2768942443u, 901097722u, 1119000684u, + 3686517206u, 2898065728u, 853044451u, 1172266101u, 3705015759u, 2882616665u, + 651767980u, 1373503546u, 3369554304u, 3218104598u, 565507253u, 1454621731u, + 3485111705u, 3099436303u, 671266974u, 1594198024u, 3322730930u, 2970347812u, + 795835527u, 1483230225u, 3244367275u, 3060149565u, 1994146192u, 31158534u, + 2563907772u, 4023717930u, 1907459465u, 112637215u, 2680153253u, 3904427059u, + 2013776290u, 251722036u, 2517215374u, 3775830040u, 2137656763u, 141376813u, + 2439277719u, 3865271297u, 1802195444u, 476864866u, 2238001368u, 4066508878u, + 1812370925u, 453092731u, 2181625025u, 4111451223u, 1706088902u, 314042704u, + 2344532202u, 4240017532u, 1658658271u, 366619977u, 2362670323u, 4224994405u, + 1303535960u, 984961486u, 2747007092u, 3569037538u, 1256170817u, 1037604311u, + 2765210733u, 3554079995u, 1131014506u, 879679996u, 2909243462u, 3663771856u, + 1141124467u, 855842277u, 2852801631u, 3708648649u, 1342533948u, 654459306u, + 3188396048u, 3373015174u, 1466479909u, 544179635u, 3110523913u, 3462522015u, + 1591671054u, 702138776u, 2966460450u, 3352799412u, 1504918807u, 783551873u, + 3082640443u, 3233442989u, 3988292384u, 2596254646u, 62317068u, 1957810842u, + 3939845945u, 2647816111u, 81470997u, 1943803523u, 3814918930u, 2489596804u, + 225274430u, 2053790376u, 3826175755u, 2466906013u, 167816743u, 2097651377u, + 4027552580u, 2265490386u, 503444072u, 1762050814u, 4150417245u, 2154129355u, + 426522225u, 1852507879u, 4275313526u, 2312317920u, 282753626u, 1742555852u, + 4189708143u, 2394877945u, 397917763u, 1622183637u, 3604390888u, 2714866558u, + 953729732u, 1340076626u, 3518719985u, 2797360999u, 1068828381u, 1219638859u, + 3624741850u, 2936675148u, 906185462u, 1090812512u, 3747672003u, 2825379669u, + 829329135u, 1181335161u, 3412177804u, 3160834842u, 628085408u, 1382605366u, + 3423369109u, 3138078467u, 570562233u, 1426400815u, 3317316542u, 2998733608u, + 733239954u, 1555261956u, 3268935591u, 3050360625u, 752459403u, 1541320221u, + 2607071920u, 3965973030u, 1969922972u, 40735498u, 2617837225u, 3943577151u, + 1913087877u, 83908371u, 2512341634u, 3803740692u, 2075208622u, 213261112u, + 2463272603u, 3855990285u, 2094854071u, 198958881u, 2262029012u, 4057260610u, + 1759359992u, 534414190u, 2176718541u, 4139329115u, 1873836001u, 414664567u, + 2282248934u, 4279200368u, 1711684554u, 285281116u, 2405801727u, 4167216745u, + 1634467795u, 376229701u, 2685067896u, 3608007406u, 1308918612u, 956543938u, + 2808555105u, 3495958263u, 1231636301u, 1047427035u, 2932959818u, 3654703836u, + 1088359270u, 936918000u, 2847714899u, 3736837829u, 1202900863u, 817233897u, + 3183342108u, 3401237130u, 1404277552u, 615818150u, 3134207493u, 3453421203u, + 1423857449u, 601450431u, 3009837614u, 3294710456u, 1567103746u, 711928724u, + 3020668471u, 3272380065u, 1510334235u, 755167117u +}; + +/* Returns the CRC32 */ +static unsigned long CRC(const unsigned char* data, size_t size) { + unsigned long result = 0xffffffffu; + for (; size > 0; size--) { + result = crc32_table[(result ^ *(data++)) & 0xff] ^ (result >> 8); + } + return result ^ 0xffffffffu; +} + +/* Compresses the data according to the gzip specification, RFC 1952. */ +void ZopfliGzipCompress(const ZopfliOptions* options, + const unsigned char* in, size_t insize, + unsigned char** out, size_t* outsize) { + unsigned long crcvalue = CRC(in, insize); + unsigned char bp = 0; + + ZOPFLI_APPEND_DATA(31, out, outsize); /* ID1 */ + ZOPFLI_APPEND_DATA(139, out, outsize); /* ID2 */ + ZOPFLI_APPEND_DATA(8, out, outsize); /* CM */ + ZOPFLI_APPEND_DATA(0, out, outsize); /* FLG */ + /* MTIME */ + ZOPFLI_APPEND_DATA(0, out, outsize); + ZOPFLI_APPEND_DATA(0, out, outsize); + ZOPFLI_APPEND_DATA(0, out, outsize); + ZOPFLI_APPEND_DATA(0, out, outsize); + + ZOPFLI_APPEND_DATA(2, out, outsize); /* XFL, 2 indicates best compression. */ + ZOPFLI_APPEND_DATA(3, out, outsize); /* OS follows Unix conventions. */ + + ZopfliDeflate(options, 2 /* Dynamic block */, 1, + in, insize, &bp, out, outsize); + + /* CRC */ + ZOPFLI_APPEND_DATA(crcvalue % 256, out, outsize); + ZOPFLI_APPEND_DATA((crcvalue >> 8) % 256, out, outsize); + ZOPFLI_APPEND_DATA((crcvalue >> 16) % 256, out, outsize); + ZOPFLI_APPEND_DATA((crcvalue >> 24) % 256, out, outsize); + + /* ISIZE */ + ZOPFLI_APPEND_DATA(insize % 256, out, outsize); + ZOPFLI_APPEND_DATA((insize >> 8) % 256, out, outsize); + ZOPFLI_APPEND_DATA((insize >> 16) % 256, out, outsize); + ZOPFLI_APPEND_DATA((insize >> 24) % 256, out, outsize); + + if (options->verbose) { + fprintf(stderr, + "Original Size: %d, Gzip: %d, Compression: %f%% Removed\n", + (int)insize, (int)*outsize, + 100.0 * (double)(insize - *outsize) / (double)insize); + } +} diff --git a/tools/bible/zopfli4k/gzip_container.h b/tools/bible/zopfli4k/gzip_container.h new file mode 100644 index 000000000..8f5ed9092 --- /dev/null +++ b/tools/bible/zopfli4k/gzip_container.h @@ -0,0 +1,50 @@ +/* +Copyright 2013 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +#ifndef ZOPFLI_GZIP_H_ +#define ZOPFLI_GZIP_H_ + +/* +Functions to compress according to the Gzip specification. +*/ + +#include "zopfli.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* +Compresses according to the gzip specification and append the compressed +result to the output. + +options: global program options +out: pointer to the dynamic output array to which the result is appended. Must + be freed after use. +outsize: pointer to the dynamic output array size. +*/ +void ZopfliGzipCompress(const ZopfliOptions* options, + const unsigned char* in, size_t insize, + unsigned char** out, size_t* outsize); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* ZOPFLI_GZIP_H_ */ diff --git a/tools/bible/zopfli4k/hash.c b/tools/bible/zopfli4k/hash.c new file mode 100644 index 000000000..3025d1e29 --- /dev/null +++ b/tools/bible/zopfli4k/hash.c @@ -0,0 +1,143 @@ +/* +Copyright 2011 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +#include "hash.h" + +#include +#include +#include + +#define HASH_SHIFT 5 +#define HASH_MASK 32767 + +void ZopfliAllocHash(size_t window_size, ZopfliHash* h) { + h->head = (int*)malloc(sizeof(*h->head) * 65536); + h->prev = (unsigned short*)malloc(sizeof(*h->prev) * window_size); + h->hashval = (int*)malloc(sizeof(*h->hashval) * window_size); + +#ifdef ZOPFLI_HASH_SAME + h->same = (unsigned short*)malloc(sizeof(*h->same) * window_size); +#endif + +#ifdef ZOPFLI_HASH_SAME_HASH + h->head2 = (int*)malloc(sizeof(*h->head2) * 65536); + h->prev2 = (unsigned short*)malloc(sizeof(*h->prev2) * window_size); + h->hashval2 = (int*)malloc(sizeof(*h->hashval2) * window_size); +#endif +} + +void ZopfliResetHash(size_t window_size, ZopfliHash* h) { + size_t i; + + h->val = 0; + for (i = 0; i < 65536; i++) { + h->head[i] = -1; /* -1 indicates no head so far. */ + } + for (i = 0; i < window_size; i++) { + h->prev[i] = i; /* If prev[j] == j, then prev[j] is uninitialized. */ + h->hashval[i] = -1; + } + +#ifdef ZOPFLI_HASH_SAME + for (i = 0; i < window_size; i++) { + h->same[i] = 0; + } +#endif + +#ifdef ZOPFLI_HASH_SAME_HASH + h->val2 = 0; + for (i = 0; i < 65536; i++) { + h->head2[i] = -1; + } + for (i = 0; i < window_size; i++) { + h->prev2[i] = i; + h->hashval2[i] = -1; + } +#endif +} + +void ZopfliCleanHash(ZopfliHash* h) { + free(h->head); + free(h->prev); + free(h->hashval); + +#ifdef ZOPFLI_HASH_SAME_HASH + free(h->head2); + free(h->prev2); + free(h->hashval2); +#endif + +#ifdef ZOPFLI_HASH_SAME + free(h->same); +#endif +} + +/* +Update the sliding hash value with the given byte. All calls to this function +must be made on consecutive input characters. Since the hash value exists out +of multiple input bytes, a few warmups with this function are needed initially. +*/ +static void UpdateHashValue(ZopfliHash* h, unsigned char c) { + h->val = (((h->val) << HASH_SHIFT) ^ (c)) & HASH_MASK; +} + +void ZopfliUpdateHash(const unsigned char* array, size_t pos, size_t end, + ZopfliHash* h) { + unsigned short hpos = pos & ZOPFLI_WINDOW_MASK; +#ifdef ZOPFLI_HASH_SAME + size_t amount = 0; +#endif + + UpdateHashValue(h, pos + ZOPFLI_MIN_MATCH <= end ? + array[pos + ZOPFLI_MIN_MATCH - 1] : 0); + h->hashval[hpos] = h->val; + if (h->head[h->val] != -1 && h->hashval[h->head[h->val]] == h->val) { + h->prev[hpos] = h->head[h->val]; + } + else h->prev[hpos] = hpos; + h->head[h->val] = hpos; + +#ifdef ZOPFLI_HASH_SAME + /* Update "same". */ + if (h->same[(pos - 1) & ZOPFLI_WINDOW_MASK] > 1) { + amount = h->same[(pos - 1) & ZOPFLI_WINDOW_MASK] - 1; + } + while (pos + amount + 1 < end && + array[pos] == array[pos + amount + 1] && amount < (unsigned short)(-1)) { + amount++; + } + h->same[hpos] = amount; +#endif + +#ifdef ZOPFLI_HASH_SAME_HASH + h->val2 = ((h->same[hpos] - ZOPFLI_MIN_MATCH) & 255) ^ h->val; + h->hashval2[hpos] = h->val2; + if (h->head2[h->val2] != -1 && h->hashval2[h->head2[h->val2]] == h->val2) { + h->prev2[hpos] = h->head2[h->val2]; + } + else h->prev2[hpos] = hpos; + h->head2[h->val2] = hpos; +#endif +} + +void ZopfliWarmupHash(const unsigned char* array, size_t pos, size_t end, + ZopfliHash* h) { + UpdateHashValue(h, array[pos + 0]); + if (pos + 1 < end) UpdateHashValue(h, array[pos + 1]); +} diff --git a/tools/bible/zopfli4k/hash.h b/tools/bible/zopfli4k/hash.h new file mode 100644 index 000000000..e59c1d46f --- /dev/null +++ b/tools/bible/zopfli4k/hash.h @@ -0,0 +1,73 @@ +/* +Copyright 2011 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +/* +The hash for ZopfliFindLongestMatch of lz77.c. +*/ + +#ifndef ZOPFLI_HASH_H_ +#define ZOPFLI_HASH_H_ + +#include "util.h" + +typedef struct ZopfliHash { + int* head; /* Hash value to index of its most recent occurrence. */ + unsigned short* prev; /* Index to index of prev. occurrence of same hash. */ + int* hashval; /* Index to hash value at this index. */ + int val; /* Current hash value. */ + +#ifdef ZOPFLI_HASH_SAME_HASH + /* Fields with similar purpose as the above hash, but for the second hash with + a value that is calculated differently. */ + int* head2; /* Hash value to index of its most recent occurrence. */ + unsigned short* prev2; /* Index to index of prev. occurrence of same hash. */ + int* hashval2; /* Index to hash value at this index. */ + int val2; /* Current hash value. */ +#endif + +#ifdef ZOPFLI_HASH_SAME + unsigned short* same; /* Amount of repetitions of same byte after this .*/ +#endif +} ZopfliHash; + +/* Allocates ZopfliHash memory. */ +void ZopfliAllocHash(size_t window_size, ZopfliHash* h); + +/* Resets all fields of ZopfliHash. */ +void ZopfliResetHash(size_t window_size, ZopfliHash* h); + +/* Frees ZopfliHash memory. */ +void ZopfliCleanHash(ZopfliHash* h); + +/* +Updates the hash values based on the current position in the array. All calls +to this must be made for consecutive bytes. +*/ +void ZopfliUpdateHash(const unsigned char* array, size_t pos, size_t end, + ZopfliHash* h); + +/* +Prepopulates hash: +Fills in the initial values in the hash, before ZopfliUpdateHash can be used +correctly. +*/ +void ZopfliWarmupHash(const unsigned char* array, size_t pos, size_t end, + ZopfliHash* h); + +#endif /* ZOPFLI_HASH_H_ */ diff --git a/tools/bible/zopfli4k/katajainen.c b/tools/bible/zopfli4k/katajainen.c new file mode 100644 index 000000000..145901755 --- /dev/null +++ b/tools/bible/zopfli4k/katajainen.c @@ -0,0 +1,262 @@ +/* +Copyright 2011 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +/* +Bounded package merge algorithm, based on the paper +"A Fast and Space-Economical Algorithm for Length-Limited Coding +Jyrki Katajainen, Alistair Moffat, Andrew Turpin". +*/ + +#include "katajainen.h" +#include +#include +#include + +typedef struct Node Node; + +/* +Nodes forming chains. Also used to represent leaves. +*/ +struct Node { + size_t weight; /* Total weight (symbol count) of this chain. */ + Node* tail; /* Previous node(s) of this chain, or 0 if none. */ + int count; /* Leaf symbol index, or number of leaves before this chain. */ +}; + +/* +Memory pool for nodes. +*/ +typedef struct NodePool { + Node* next; /* Pointer to a free node in the pool. */ +} NodePool; + +/* +Initializes a chain node with the given values and marks it as in use. +*/ +static void InitNode(size_t weight, int count, Node* tail, Node* node) { + node->weight = weight; + node->count = count; + node->tail = tail; +} + +/* +Performs a Boundary Package-Merge step. Puts a new chain in the given list. The +new chain is, depending on the weights, a leaf or a combination of two chains +from the previous list. +lists: The lists of chains. +maxbits: Number of lists. +leaves: The leaves, one per symbol. +numsymbols: Number of leaves. +pool: the node memory pool. +index: The index of the list in which a new chain or leaf is required. +*/ +static void BoundaryPM(Node* (*lists)[2], Node* leaves, int numsymbols, + NodePool* pool, int index) { + Node* newchain; + Node* oldchain; + int lastcount = lists[index][1]->count; /* Count of last chain of list. */ + + if (index == 0 && lastcount >= numsymbols) return; + + newchain = pool->next++; + oldchain = lists[index][1]; + + /* These are set up before the recursive calls below, so that there is a list + pointing to the new node, to let the garbage collection know it's in use. */ + lists[index][0] = oldchain; + lists[index][1] = newchain; + + if (index == 0) { + /* New leaf node in list 0. */ + InitNode(leaves[lastcount].weight, lastcount + 1, 0, newchain); + } else { + size_t sum = lists[index - 1][0]->weight + lists[index - 1][1]->weight; + if (lastcount < numsymbols && sum > leaves[lastcount].weight) { + /* New leaf inserted in list, so count is incremented. */ + InitNode(leaves[lastcount].weight, lastcount + 1, oldchain->tail, + newchain); + } else { + InitNode(sum, lastcount, lists[index - 1][1], newchain); + /* Two lookahead chains of previous list used up, create new ones. */ + BoundaryPM(lists, leaves, numsymbols, pool, index - 1); + BoundaryPM(lists, leaves, numsymbols, pool, index - 1); + } + } +} + +static void BoundaryPMFinal(Node* (*lists)[2], + Node* leaves, int numsymbols, NodePool* pool, int index) { + int lastcount = lists[index][1]->count; /* Count of last chain of list. */ + + size_t sum = lists[index - 1][0]->weight + lists[index - 1][1]->weight; + + if (lastcount < numsymbols && sum > leaves[lastcount].weight) { + Node* newchain = pool->next; + Node* oldchain = lists[index][1]->tail; + + lists[index][1] = newchain; + newchain->count = lastcount + 1; + newchain->tail = oldchain; + } else { + lists[index][1]->tail = lists[index - 1][1]; + } +} + +/* +Initializes each list with as lookahead chains the two leaves with lowest +weights. +*/ +static void InitLists( + NodePool* pool, const Node* leaves, int maxbits, Node* (*lists)[2]) { + int i; + Node* node0 = pool->next++; + Node* node1 = pool->next++; + InitNode(leaves[0].weight, 1, 0, node0); + InitNode(leaves[1].weight, 2, 0, node1); + for (i = 0; i < maxbits; i++) { + lists[i][0] = node0; + lists[i][1] = node1; + } +} + +/* +Converts result of boundary package-merge to the bitlengths. The result in the +last chain of the last list contains the amount of active leaves in each list. +chain: Chain to extract the bit length from (last chain from last list). +*/ +static void ExtractBitLengths(Node* chain, Node* leaves, unsigned* bitlengths) { + int counts[16] = {0}; + unsigned end = 16; + unsigned ptr = 15; + unsigned value = 1; + Node* node; + int val; + + for (node = chain; node; node = node->tail) { + counts[--end] = node->count; + } + + val = counts[15]; + while (ptr >= end) { + for (; val > counts[ptr - 1]; val--) { + bitlengths[leaves[val - 1].count] = value; + } + ptr--; + value++; + } +} + +/* +Comparator for sorting the leaves. Has the function signature for qsort. +*/ +static int LeafComparator(const void* a, const void* b) { + return ((const Node*)a)->weight - ((const Node*)b)->weight; +} + +int ZopfliLengthLimitedCodeLengths( + const size_t* frequencies, int n, int maxbits, unsigned* bitlengths) { + NodePool pool; + int i; + int numsymbols = 0; /* Amount of symbols with frequency > 0. */ + int numBoundaryPMRuns; + Node* nodes; + + /* Array of lists of chains. Each list requires only two lookahead chains at + a time, so each list is a array of two Node*'s. */ + Node* (*lists)[2]; + + /* One leaf per symbol. Only numsymbols leaves will be used. */ + Node* leaves = (Node*)malloc(n * sizeof(*leaves)); + + /* Initialize all bitlengths at 0. */ + for (i = 0; i < n; i++) { + bitlengths[i] = 0; + } + + /* Count used symbols and place them in the leaves. */ + for (i = 0; i < n; i++) { + if (frequencies[i]) { + leaves[numsymbols].weight = frequencies[i]; + leaves[numsymbols].count = i; /* Index of symbol this leaf represents. */ + numsymbols++; + } + } + + /* Check special cases and error conditions. */ + if ((1 << maxbits) < numsymbols) { + free(leaves); + return 1; /* Error, too few maxbits to represent symbols. */ + } + if (numsymbols == 0) { + free(leaves); + return 0; /* No symbols at all. OK. */ + } + if (numsymbols == 1) { + bitlengths[leaves[0].count] = 1; + free(leaves); + return 0; /* Only one symbol, give it bitlength 1, not 0. OK. */ + } + if (numsymbols == 2) { + bitlengths[leaves[0].count]++; + bitlengths[leaves[1].count]++; + free(leaves); + return 0; + } + + /* Sort the leaves from lightest to heaviest. Add count into the same + variable for stable sorting. */ + for (i = 0; i < numsymbols; i++) { + if (leaves[i].weight >= + ((size_t)1 << (sizeof(leaves[0].weight) * CHAR_BIT - 9))) { + free(leaves); + return 1; /* Error, we need 9 bits for the count. */ + } + leaves[i].weight = (leaves[i].weight << 9) | leaves[i].count; + } + qsort(leaves, numsymbols, sizeof(Node), LeafComparator); + for (i = 0; i < numsymbols; i++) { + leaves[i].weight >>= 9; + } + + if (numsymbols - 1 < maxbits) { + maxbits = numsymbols - 1; + } + + /* Initialize node memory pool. */ + nodes = (Node*)malloc(maxbits * 2 * numsymbols * sizeof(Node)); + pool.next = nodes; + + lists = (Node* (*)[2])malloc(maxbits * sizeof(*lists)); + InitLists(&pool, leaves, maxbits, lists); + + /* In the last list, 2 * numsymbols - 2 active chains need to be created. Two + are already created in the initialization. Each BoundaryPM run creates one. */ + numBoundaryPMRuns = 2 * numsymbols - 4; + for (i = 0; i < numBoundaryPMRuns - 1; i++) { + BoundaryPM(lists, leaves, numsymbols, &pool, maxbits - 1); + } + BoundaryPMFinal(lists, leaves, numsymbols, &pool, maxbits - 1); + + ExtractBitLengths(lists[maxbits - 1][1], leaves, bitlengths); + + free(lists); + free(leaves); + free(nodes); + return 0; /* OK. */ +} diff --git a/tools/bible/zopfli4k/katajainen.h b/tools/bible/zopfli4k/katajainen.h new file mode 100644 index 000000000..5927350d6 --- /dev/null +++ b/tools/bible/zopfli4k/katajainen.h @@ -0,0 +1,42 @@ +/* +Copyright 2011 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +#ifndef ZOPFLI_KATAJAINEN_H_ +#define ZOPFLI_KATAJAINEN_H_ + +#include + +/* +Outputs minimum-redundancy length-limited code bitlengths for symbols with the +given counts. The bitlengths are limited by maxbits. + +The output is tailored for DEFLATE: symbols that never occur, get a bit length +of 0, and if only a single symbol occurs at least once, its bitlength will be 1, +and not 0 as would theoretically be needed for a single symbol. + +frequencies: The amount of occurrences of each symbol. +n: The amount of symbols. +maxbits: Maximum bit length, inclusive. +bitlengths: Output, the bitlengths for the symbol prefix codes. +return: 0 for OK, non-0 for error. +*/ +int ZopfliLengthLimitedCodeLengths( + const size_t* frequencies, int n, int maxbits, unsigned* bitlengths); + +#endif /* ZOPFLI_KATAJAINEN_H_ */ diff --git a/tools/bible/zopfli4k/lz77.c b/tools/bible/zopfli4k/lz77.c new file mode 100644 index 000000000..52a2bed18 --- /dev/null +++ b/tools/bible/zopfli4k/lz77.c @@ -0,0 +1,641 @@ +/* +Copyright 2011 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +#include "lz77.h" +#include "symbols.h" +#include "util.h" + +#include +#include +#include +#include + +void ZopfliInitLZ77Store(const unsigned char* data, ZopfliLZ77Store* store) { + store->size = 0; + store->litlens = 0; + store->dists = 0; + store->pos = 0; + store->data = data; + store->ll_symbol = 0; + store->d_symbol = 0; + store->ll_counts = 0; + store->d_counts = 0; +} + +void ZopfliCleanLZ77Store(ZopfliLZ77Store* store) { + free(store->litlens); + free(store->dists); + free(store->pos); + free(store->ll_symbol); + free(store->d_symbol); + free(store->ll_counts); + free(store->d_counts); +} + +static size_t CeilDiv(size_t a, size_t b) { + return (a + b - 1) / b; +} + +void ZopfliCopyLZ77Store( + const ZopfliLZ77Store* source, ZopfliLZ77Store* dest) { + size_t i; + size_t llsize = ZOPFLI_NUM_LL * CeilDiv(source->size, ZOPFLI_NUM_LL); + size_t dsize = ZOPFLI_NUM_D * CeilDiv(source->size, ZOPFLI_NUM_D); + ZopfliCleanLZ77Store(dest); + ZopfliInitLZ77Store(source->data, dest); + dest->litlens = + (unsigned short*)malloc(sizeof(*dest->litlens) * source->size); + dest->dists = (unsigned short*)malloc(sizeof(*dest->dists) * source->size); + dest->pos = (size_t*)malloc(sizeof(*dest->pos) * source->size); + dest->ll_symbol = + (unsigned short*)malloc(sizeof(*dest->ll_symbol) * source->size); + dest->d_symbol = + (unsigned short*)malloc(sizeof(*dest->d_symbol) * source->size); + dest->ll_counts = (size_t*)malloc(sizeof(*dest->ll_counts) * llsize); + dest->d_counts = (size_t*)malloc(sizeof(*dest->d_counts) * dsize); + + /* Allocation failed. */ + if (!dest->litlens || !dest->dists) exit(-1); + if (!dest->pos) exit(-1); + if (!dest->ll_symbol || !dest->d_symbol) exit(-1); + if (!dest->ll_counts || !dest->d_counts) exit(-1); + + dest->size = source->size; + for (i = 0; i < source->size; i++) { + dest->litlens[i] = source->litlens[i]; + dest->dists[i] = source->dists[i]; + dest->pos[i] = source->pos[i]; + dest->ll_symbol[i] = source->ll_symbol[i]; + dest->d_symbol[i] = source->d_symbol[i]; + } + for (i = 0; i < llsize; i++) { + dest->ll_counts[i] = source->ll_counts[i]; + } + for (i = 0; i < dsize; i++) { + dest->d_counts[i] = source->d_counts[i]; + } +} + +/* +Appends the length and distance to the LZ77 arrays of the ZopfliLZ77Store. +context must be a ZopfliLZ77Store*. +*/ +void ZopfliStoreLitLenDist(unsigned short length, unsigned short dist, + size_t pos, ZopfliLZ77Store* store) { + size_t i; + /* Needed for using ZOPFLI_APPEND_DATA multiple times. */ + size_t origsize = store->size; + size_t llstart = ZOPFLI_NUM_LL * (origsize / ZOPFLI_NUM_LL); + size_t dstart = ZOPFLI_NUM_D * (origsize / ZOPFLI_NUM_D); + + /* Everytime the index wraps around, a new cumulative histogram is made: we're + keeping one histogram value per LZ77 symbol rather than a full histogram for + each to save memory. */ + if (origsize % ZOPFLI_NUM_LL == 0) { + size_t llsize = origsize; + for (i = 0; i < ZOPFLI_NUM_LL; i++) { + ZOPFLI_APPEND_DATA( + origsize == 0 ? 0 : store->ll_counts[origsize - ZOPFLI_NUM_LL + i], + &store->ll_counts, &llsize); + } + } + if (origsize % ZOPFLI_NUM_D == 0) { + size_t dsize = origsize; + for (i = 0; i < ZOPFLI_NUM_D; i++) { + ZOPFLI_APPEND_DATA( + origsize == 0 ? 0 : store->d_counts[origsize - ZOPFLI_NUM_D + i], + &store->d_counts, &dsize); + } + } + + ZOPFLI_APPEND_DATA(length, &store->litlens, &store->size); + store->size = origsize; + ZOPFLI_APPEND_DATA(dist, &store->dists, &store->size); + store->size = origsize; + ZOPFLI_APPEND_DATA(pos, &store->pos, &store->size); + assert(length < 259); + + if (dist == 0) { + store->size = origsize; + ZOPFLI_APPEND_DATA(length, &store->ll_symbol, &store->size); + store->size = origsize; + ZOPFLI_APPEND_DATA(0, &store->d_symbol, &store->size); + store->ll_counts[llstart + length]++; + } else { + store->size = origsize; + ZOPFLI_APPEND_DATA(ZopfliGetLengthSymbol(length), + &store->ll_symbol, &store->size); + store->size = origsize; + ZOPFLI_APPEND_DATA(ZopfliGetDistSymbol(dist), + &store->d_symbol, &store->size); + store->ll_counts[llstart + ZopfliGetLengthSymbol(length)]++; + store->d_counts[dstart + ZopfliGetDistSymbol(dist)]++; + } +} + +void ZopfliAppendLZ77Store(const ZopfliLZ77Store* store, + ZopfliLZ77Store* target) { + size_t i; + for (i = 0; i < store->size; i++) { + ZopfliStoreLitLenDist(store->litlens[i], store->dists[i], + store->pos[i], target); + } +} + +size_t ZopfliLZ77GetByteRange(const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend) { + size_t l = lend - 1; + if (lstart == lend) return 0; + return lz77->pos[l] + ((lz77->dists[l] == 0) ? + 1 : lz77->litlens[l]) - lz77->pos[lstart]; +} + +static void ZopfliLZ77GetHistogramAt(const ZopfliLZ77Store* lz77, size_t lpos, + size_t* ll_counts, size_t* d_counts) { + /* The real histogram is created by using the histogram for this chunk, but + all superfluous values of this chunk subtracted. */ + size_t llpos = ZOPFLI_NUM_LL * (lpos / ZOPFLI_NUM_LL); + size_t dpos = ZOPFLI_NUM_D * (lpos / ZOPFLI_NUM_D); + size_t i; + for (i = 0; i < ZOPFLI_NUM_LL; i++) { + ll_counts[i] = lz77->ll_counts[llpos + i]; + } + for (i = lpos + 1; i < llpos + ZOPFLI_NUM_LL && i < lz77->size; i++) { + ll_counts[lz77->ll_symbol[i]]--; + } + for (i = 0; i < ZOPFLI_NUM_D; i++) { + d_counts[i] = lz77->d_counts[dpos + i]; + } + for (i = lpos + 1; i < dpos + ZOPFLI_NUM_D && i < lz77->size; i++) { + if (lz77->dists[i] != 0) d_counts[lz77->d_symbol[i]]--; + } +} + +void ZopfliLZ77GetHistogram(const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend, + size_t* ll_counts, size_t* d_counts) { + size_t i; + if (lstart + ZOPFLI_NUM_LL * 3 > lend) { + memset(ll_counts, 0, sizeof(*ll_counts) * ZOPFLI_NUM_LL); + memset(d_counts, 0, sizeof(*d_counts) * ZOPFLI_NUM_D); + for (i = lstart; i < lend; i++) { + ll_counts[lz77->ll_symbol[i]]++; + if (lz77->dists[i] != 0) d_counts[lz77->d_symbol[i]]++; + } + } else { + /* Subtract the cumulative histograms at the end and the start to get the + histogram for this range. */ + ZopfliLZ77GetHistogramAt(lz77, lend - 1, ll_counts, d_counts); + if (lstart > 0) { + size_t ll_counts2[ZOPFLI_NUM_LL]; + size_t d_counts2[ZOPFLI_NUM_D]; + ZopfliLZ77GetHistogramAt(lz77, lstart - 1, ll_counts2, d_counts2); + + for (i = 0; i < ZOPFLI_NUM_LL; i++) { + ll_counts[i] -= ll_counts2[i]; + } + for (i = 0; i < ZOPFLI_NUM_D; i++) { + d_counts[i] -= d_counts2[i]; + } + } + } +} + +void ZopfliInitBlockState(const ZopfliOptions* options, + size_t blockstart, size_t blockend, int add_lmc, + ZopfliBlockState* s) { + s->options = options; + s->blockstart = blockstart; + s->blockend = blockend; +#ifdef ZOPFLI_LONGEST_MATCH_CACHE + if (add_lmc) { + s->lmc = (ZopfliLongestMatchCache*)malloc(sizeof(ZopfliLongestMatchCache)); + ZopfliInitCache(blockend - blockstart, s->lmc); + } else { + s->lmc = 0; + } +#endif +} + +void ZopfliCleanBlockState(ZopfliBlockState* s) { +#ifdef ZOPFLI_LONGEST_MATCH_CACHE + if (s->lmc) { + ZopfliCleanCache(s->lmc); + free(s->lmc); + } +#endif +} + +/* +Gets a score of the length given the distance. Typically, the score of the +length is the length itself, but if the distance is very long, decrease the +score of the length a bit to make up for the fact that long distances use large +amounts of extra bits. + +This is not an accurate score, it is a heuristic only for the greedy LZ77 +implementation. More accurate cost models are employed later. Making this +heuristic more accurate may hurt rather than improve compression. + +The two direct uses of this heuristic are: +-avoid using a length of 3 in combination with a long distance. This only has + an effect if length == 3. +-make a slightly better choice between the two options of the lazy matching. + +Indirectly, this affects: +-the block split points if the default of block splitting first is used, in a + rather unpredictable way +-the first zopfli run, so it affects the chance of the first run being closer + to the optimal output +*/ +static int GetLengthScore(int length, int distance) { + /* + At 1024, the distance uses 9+ extra bits and this seems to be the sweet spot + on tested files. + */ + return distance > 1024 ? length - 1 : length; +} + +void ZopfliVerifyLenDist(const unsigned char* data, size_t datasize, size_t pos, + unsigned short dist, unsigned short length) { + + /* TODO(lode): make this only run in a debug compile, it's for assert only. */ + size_t i; + + assert(pos + length <= datasize); + for (i = 0; i < length; i++) { + if (data[pos - dist + i] != data[pos + i]) { + assert(data[pos - dist + i] == data[pos + i]); + break; + } + } +} + +/* +Finds how long the match of scan and match is. Can be used to find how many +bytes starting from scan, and from match, are equal. Returns the last byte +after scan, which is still equal to the correspondinb byte after match. +scan is the position to compare +match is the earlier position to compare. +end is the last possible byte, beyond which to stop looking. +safe_end is a few (8) bytes before end, for comparing multiple bytes at once. +*/ +static const unsigned char* GetMatch(const unsigned char* scan, + const unsigned char* match, + const unsigned char* end, + const unsigned char* safe_end) { + + if (sizeof(size_t) == 8) { + /* 8 checks at once per array bounds check (size_t is 64-bit). + Use memcpy to avoid unaligned access (causes SIGBUS on strict-alignment + architectures like SPARC and MIPS). The compiler will optimize memcpy of + a known size into a single load where the architecture supports it. */ + size_t sv, mv; + while (scan < safe_end) { + memcpy(&sv, scan, sizeof(sv)); + memcpy(&mv, match, sizeof(mv)); + if (sv != mv) break; + scan += 8; + match += 8; + } + } else if (sizeof(unsigned int) == 4) { + /* 4 checks at once per array bounds check (unsigned int is 32-bit). */ + unsigned int sv, mv; + while (scan < safe_end) { + memcpy(&sv, scan, sizeof(sv)); + memcpy(&mv, match, sizeof(mv)); + if (sv != mv) break; + scan += 4; + match += 4; + } + } else { + /* do 8 checks at once per array bounds check. */ + while (scan < safe_end && *scan == *match && *++scan == *++match + && *++scan == *++match && *++scan == *++match + && *++scan == *++match && *++scan == *++match + && *++scan == *++match && *++scan == *++match) { + scan++; match++; + } + } + + /* The remaining few bytes. */ + while (scan != end && *scan == *match) { + scan++; match++; + } + + return scan; +} + +#ifdef ZOPFLI_LONGEST_MATCH_CACHE +/* +Gets distance, length and sublen values from the cache if possible. +Returns 1 if it got the values from the cache, 0 if not. +Updates the limit value to a smaller one if possible with more limited +information from the cache. +*/ +static int TryGetFromLongestMatchCache(ZopfliBlockState* s, + size_t pos, size_t* limit, + unsigned short* sublen, unsigned short* distance, unsigned short* length) { + /* The LMC cache starts at the beginning of the block rather than the + beginning of the whole array. */ + size_t lmcpos = pos - s->blockstart; + + /* Length > 0 and dist 0 is invalid combination, which indicates on purpose + that this cache value is not filled in yet. */ + unsigned char cache_available = s->lmc && (s->lmc->length[lmcpos] == 0 || + s->lmc->dist[lmcpos] != 0); + unsigned char limit_ok_for_cache = cache_available && + (*limit == ZOPFLI_MAX_MATCH || s->lmc->length[lmcpos] <= *limit || + (sublen && ZopfliMaxCachedSublen(s->lmc, + lmcpos, s->lmc->length[lmcpos]) >= *limit)); + + if (s->lmc && limit_ok_for_cache && cache_available) { + if (!sublen || s->lmc->length[lmcpos] + <= ZopfliMaxCachedSublen(s->lmc, lmcpos, s->lmc->length[lmcpos])) { + *length = s->lmc->length[lmcpos]; + if (*length > *limit) *length = *limit; + if (sublen) { + ZopfliCacheToSublen(s->lmc, lmcpos, *length, sublen); + *distance = sublen[*length]; + if (*limit == ZOPFLI_MAX_MATCH && *length >= ZOPFLI_MIN_MATCH) { + assert(sublen[*length] == s->lmc->dist[lmcpos]); + } + } else { + *distance = s->lmc->dist[lmcpos]; + } + return 1; + } + /* Can't use much of the cache, since the "sublens" need to be calculated, + but at least we already know when to stop. */ + *limit = s->lmc->length[lmcpos]; + } + + return 0; +} + +/* +Stores the found sublen, distance and length in the longest match cache, if +possible. +*/ +static void StoreInLongestMatchCache(ZopfliBlockState* s, + size_t pos, size_t limit, + const unsigned short* sublen, + unsigned short distance, unsigned short length) { + /* The LMC cache starts at the beginning of the block rather than the + beginning of the whole array. */ + size_t lmcpos = pos - s->blockstart; + + /* Length > 0 and dist 0 is invalid combination, which indicates on purpose + that this cache value is not filled in yet. */ + unsigned char cache_available = s->lmc && (s->lmc->length[lmcpos] == 0 || + s->lmc->dist[lmcpos] != 0); + + if (s->lmc && limit == ZOPFLI_MAX_MATCH && sublen && !cache_available) { + assert(s->lmc->length[lmcpos] == 1 && s->lmc->dist[lmcpos] == 0); + s->lmc->dist[lmcpos] = length < ZOPFLI_MIN_MATCH ? 0 : distance; + s->lmc->length[lmcpos] = length < ZOPFLI_MIN_MATCH ? 0 : length; + assert(!(s->lmc->length[lmcpos] == 1 && s->lmc->dist[lmcpos] == 0)); + ZopfliSublenToCache(sublen, lmcpos, length, s->lmc); + } +} +#endif + +void ZopfliFindLongestMatch(ZopfliBlockState* s, const ZopfliHash* h, + const unsigned char* array, + size_t pos, size_t size, size_t limit, + unsigned short* sublen, unsigned short* distance, unsigned short* length) { + unsigned short hpos = pos & ZOPFLI_WINDOW_MASK, p, pp; + unsigned short bestdist = 0; + unsigned short bestlength = 1; + const unsigned char* scan; + const unsigned char* match; + const unsigned char* arrayend; + const unsigned char* arrayend_safe; +#if ZOPFLI_MAX_CHAIN_HITS < ZOPFLI_WINDOW_SIZE + int chain_counter = ZOPFLI_MAX_CHAIN_HITS; /* For quitting early. */ +#endif + + unsigned dist = 0; /* Not unsigned short on purpose. */ + + int* hhead = h->head; + unsigned short* hprev = h->prev; + int* hhashval = h->hashval; + int hval = h->val; + +#ifdef ZOPFLI_LONGEST_MATCH_CACHE + if (TryGetFromLongestMatchCache(s, pos, &limit, sublen, distance, length)) { + assert(pos + *length <= size); + return; + } +#endif + + assert(limit <= ZOPFLI_MAX_MATCH); + assert(limit >= ZOPFLI_MIN_MATCH); + assert(pos < size); + + if (size - pos < ZOPFLI_MIN_MATCH) { + /* The rest of the code assumes there are at least ZOPFLI_MIN_MATCH bytes to + try. */ + *length = 0; + *distance = 0; + return; + } + + if (pos + limit > size) { + limit = size - pos; + } + arrayend = &array[pos] + limit; + arrayend_safe = arrayend - 8; + + assert(hval < 65536); + + pp = hhead[hval]; /* During the whole loop, p == hprev[pp]. */ + p = hprev[pp]; + + assert(pp == hpos); + + dist = p < pp ? pp - p : ((ZOPFLI_WINDOW_SIZE - p) + pp); + + /* Go through all distances. */ + while (dist < ZOPFLI_WINDOW_SIZE) { + unsigned short currentlength = 0; + + assert(p < ZOPFLI_WINDOW_SIZE); + assert(p == hprev[pp]); + assert(hhashval[p] == hval); + + if (dist > 0) { + assert(pos < size); + assert(dist <= pos); + scan = &array[pos]; + match = &array[pos - dist]; + + /* Testing the byte at position bestlength first, goes slightly faster. */ + if (pos + bestlength >= size + || *(scan + bestlength) == *(match + bestlength)) { + +#ifdef ZOPFLI_HASH_SAME + unsigned short same0 = h->same[pos & ZOPFLI_WINDOW_MASK]; + if (same0 > 2 && *scan == *match) { + unsigned short same1 = h->same[(pos - dist) & ZOPFLI_WINDOW_MASK]; + unsigned short same = same0 < same1 ? same0 : same1; + if (same > limit) same = limit; + scan += same; + match += same; + } +#endif + scan = GetMatch(scan, match, arrayend, arrayend_safe); + currentlength = scan - &array[pos]; /* The found length. */ + } + + if (currentlength > bestlength) { + if (sublen) { + unsigned short j; + for (j = bestlength + 1; j <= currentlength; j++) { + sublen[j] = dist; + } + } + bestdist = dist; + bestlength = currentlength; + if (currentlength >= limit) break; + } + } + + +#ifdef ZOPFLI_HASH_SAME_HASH + /* Switch to the other hash once this will be more efficient. */ + if (hhead != h->head2 && bestlength >= h->same[hpos] && + h->val2 == h->hashval2[p]) { + /* Now use the hash that encodes the length and first byte. */ + hhead = h->head2; + hprev = h->prev2; + hhashval = h->hashval2; + hval = h->val2; + } +#endif + + pp = p; + p = hprev[p]; + if (p == pp) break; /* Uninited prev value. */ + + dist += p < pp ? pp - p : ((ZOPFLI_WINDOW_SIZE - p) + pp); + +#if ZOPFLI_MAX_CHAIN_HITS < ZOPFLI_WINDOW_SIZE + chain_counter--; + if (chain_counter <= 0) break; +#endif + } + +#ifdef ZOPFLI_LONGEST_MATCH_CACHE + StoreInLongestMatchCache(s, pos, limit, sublen, bestdist, bestlength); +#endif + + assert(bestlength <= limit); + + *distance = bestdist; + *length = bestlength; + assert(pos + *length <= size); +} + +void ZopfliLZ77Greedy(ZopfliBlockState* s, const unsigned char* in, + size_t instart, size_t inend, + ZopfliLZ77Store* store, ZopfliHash* h) { + size_t i = 0, j; + unsigned short leng; + unsigned short dist; + int lengthscore; + size_t windowstart = instart > ZOPFLI_WINDOW_SIZE + ? instart - ZOPFLI_WINDOW_SIZE : 0; + unsigned short dummysublen[259]; + +#ifdef ZOPFLI_LAZY_MATCHING + /* Lazy matching. */ + unsigned prev_length = 0; + unsigned prev_match = 0; + int prevlengthscore; + int match_available = 0; +#endif + + if (instart == inend) return; + + ZopfliResetHash(ZOPFLI_WINDOW_SIZE, h); + ZopfliWarmupHash(in, windowstart, inend, h); + for (i = windowstart; i < instart; i++) { + ZopfliUpdateHash(in, i, inend, h); + } + + for (i = instart; i < inend; i++) { + ZopfliUpdateHash(in, i, inend, h); + + ZopfliFindLongestMatch(s, h, in, i, inend, ZOPFLI_MAX_MATCH, dummysublen, + &dist, &leng); + lengthscore = GetLengthScore(leng, dist); + +#ifdef ZOPFLI_LAZY_MATCHING + /* Lazy matching. */ + prevlengthscore = GetLengthScore(prev_length, prev_match); + if (match_available) { + match_available = 0; + if (lengthscore > prevlengthscore + 1) { + ZopfliStoreLitLenDist(in[i - 1], 0, i - 1, store); + if (lengthscore >= ZOPFLI_MIN_MATCH && leng < ZOPFLI_MAX_MATCH) { + match_available = 1; + prev_length = leng; + prev_match = dist; + continue; + } + } else { + /* Add previous to output. */ + leng = prev_length; + dist = prev_match; + lengthscore = prevlengthscore; + /* Add to output. */ + ZopfliVerifyLenDist(in, inend, i - 1, dist, leng); + ZopfliStoreLitLenDist(leng, dist, i - 1, store); + for (j = 2; j < leng; j++) { + assert(i < inend); + i++; + ZopfliUpdateHash(in, i, inend, h); + } + continue; + } + } + else if (lengthscore >= ZOPFLI_MIN_MATCH && leng < ZOPFLI_MAX_MATCH) { + match_available = 1; + prev_length = leng; + prev_match = dist; + continue; + } + /* End of lazy matching. */ +#endif + + /* Add to output. */ + if (lengthscore >= ZOPFLI_MIN_MATCH) { + ZopfliVerifyLenDist(in, inend, i, dist, leng); + ZopfliStoreLitLenDist(leng, dist, i, store); + } else { + leng = 1; + ZopfliStoreLitLenDist(in[i], 0, i, store); + } + for (j = 1; j < leng; j++) { + assert(i < inend); + i++; + ZopfliUpdateHash(in, i, inend, h); + } + } +} diff --git a/tools/bible/zopfli4k/lz77.h b/tools/bible/zopfli4k/lz77.h new file mode 100644 index 000000000..dc8597abf --- /dev/null +++ b/tools/bible/zopfli4k/lz77.h @@ -0,0 +1,142 @@ +/* +Copyright 2011 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +/* +Functions for basic LZ77 compression and utilities for the "squeeze" LZ77 +compression. +*/ + +#ifndef ZOPFLI_LZ77_H_ +#define ZOPFLI_LZ77_H_ + +#include + +#include "cache.h" +#include "hash.h" +#include "zopfli.h" + +/* +Stores lit/length and dist pairs for LZ77. +Parameter litlens: Contains the literal symbols or length values. +Parameter dists: Contains the distances. A value is 0 to indicate that there is +no dist and the corresponding litlens value is a literal instead of a length. +Parameter size: The size of both the litlens and dists arrays. +The memory can best be managed by using ZopfliInitLZ77Store to initialize it, +ZopfliCleanLZ77Store to destroy it, and ZopfliStoreLitLenDist to append values. + +*/ +typedef struct ZopfliLZ77Store { + unsigned short* litlens; /* Lit or len. */ + unsigned short* dists; /* If 0: indicates literal in corresponding litlens, + if > 0: length in corresponding litlens, this is the distance. */ + size_t size; + + const unsigned char* data; /* original data */ + size_t* pos; /* position in data where this LZ77 command begins */ + + unsigned short* ll_symbol; + unsigned short* d_symbol; + + /* Cumulative histograms wrapping around per chunk. Each chunk has the amount + of distinct symbols as length, so using 1 value per LZ77 symbol, we have a + precise histogram at every N symbols, and the rest can be calculated by + looping through the actual symbols of this chunk. */ + size_t* ll_counts; + size_t* d_counts; +} ZopfliLZ77Store; + +void ZopfliInitLZ77Store(const unsigned char* data, ZopfliLZ77Store* store); +void ZopfliCleanLZ77Store(ZopfliLZ77Store* store); +void ZopfliCopyLZ77Store(const ZopfliLZ77Store* source, ZopfliLZ77Store* dest); +void ZopfliStoreLitLenDist(unsigned short length, unsigned short dist, + size_t pos, ZopfliLZ77Store* store); +void ZopfliAppendLZ77Store(const ZopfliLZ77Store* store, + ZopfliLZ77Store* target); +/* Gets the amount of raw bytes that this range of LZ77 symbols spans. */ +size_t ZopfliLZ77GetByteRange(const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend); +/* Gets the histogram of lit/len and dist symbols in the given range, using the +cumulative histograms, so faster than adding one by one for large range. Does +not add the one end symbol of value 256. */ +void ZopfliLZ77GetHistogram(const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend, + size_t* ll_counts, size_t* d_counts); + +/* +Some state information for compressing a block. +This is currently a bit under-used (with mainly only the longest match cache), +but is kept for easy future expansion. +*/ +typedef struct ZopfliBlockState { + const ZopfliOptions* options; + +#ifdef ZOPFLI_LONGEST_MATCH_CACHE + /* Cache for length/distance pairs found so far. */ + ZopfliLongestMatchCache* lmc; +#endif + + /* The start (inclusive) and end (not inclusive) of the current block. */ + size_t blockstart; + size_t blockend; +} ZopfliBlockState; + +void ZopfliInitBlockState(const ZopfliOptions* options, + size_t blockstart, size_t blockend, int add_lmc, + ZopfliBlockState* s); +void ZopfliCleanBlockState(ZopfliBlockState* s); + +/* +Finds the longest match (length and corresponding distance) for LZ77 +compression. +Even when not using "sublen", it can be more efficient to provide an array, +because only then the caching is used. +array: the data +pos: position in the data to find the match for +size: size of the data +limit: limit length to maximum this value (default should be 258). This allows + finding a shorter dist for that length (= less extra bits). Must be + in the range [ZOPFLI_MIN_MATCH, ZOPFLI_MAX_MATCH]. +sublen: output array of 259 elements, or null. Has, for each length, the + smallest distance required to reach this length. Only 256 of its 259 values + are used, the first 3 are ignored (the shortest length is 3. It is purely + for convenience that the array is made 3 longer). +*/ +void ZopfliFindLongestMatch( + ZopfliBlockState *s, const ZopfliHash* h, const unsigned char* array, + size_t pos, size_t size, size_t limit, + unsigned short* sublen, unsigned short* distance, unsigned short* length); + +/* +Verifies if length and dist are indeed valid, only used for assertion. +*/ +void ZopfliVerifyLenDist(const unsigned char* data, size_t datasize, size_t pos, + unsigned short dist, unsigned short length); + +/* +Does LZ77 using an algorithm similar to gzip, with lazy matching, rather than +with the slow but better "squeeze" implementation. +The result is placed in the ZopfliLZ77Store. +If instart is larger than 0, it uses values before instart as starting +dictionary. +*/ +void ZopfliLZ77Greedy(ZopfliBlockState* s, const unsigned char* in, + size_t instart, size_t inend, + ZopfliLZ77Store* store, ZopfliHash* h); + +#endif /* ZOPFLI_LZ77_H_ */ diff --git a/tools/bible/zopfli4k/reader_pack.c b/tools/bible/zopfli4k/reader_pack.c new file mode 100644 index 000000000..43dd0230c --- /dev/null +++ b/tools/bible/zopfli4k/reader_pack.c @@ -0,0 +1,16 @@ +#include "zopfli.h" +#include +#include +int main(int argc,char **argv) { + if(argc!=4)return 2; + FILE *in=fopen(argv[1],"rb");if(!in)return 3; + fseek(in,0,SEEK_END);long len=ftell(in);rewind(in); + unsigned char*src=malloc((size_t)len);if(!src)return 4; + if(fread(src,1,(size_t)len,in)!=(size_t)len)return 5;fclose(in); + ZopfliOptions options;ZopfliInitOptions(&options);options.numiterations=atoi(argv[3]); + unsigned char*out=NULL;size_t out_size=0; + ZopfliCompress(&options,ZOPFLI_FORMAT_DEFLATE,src,(size_t)len,&out,&out_size); + FILE *dest=fopen(argv[2],"wb");if(!dest)return 6; + if(fwrite(out,1,out_size,dest)!=out_size)return 7;fclose(dest); + printf("%ld -> %zu\n",len,out_size);free(src);free(out);return 0; +} diff --git a/tools/bible/zopfli4k/squeeze.c b/tools/bible/zopfli4k/squeeze.c new file mode 100644 index 000000000..a695c18cf --- /dev/null +++ b/tools/bible/zopfli4k/squeeze.c @@ -0,0 +1,560 @@ +/* +Copyright 2011 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +#include "squeeze.h" + +#include +#include +#include + +#include "blocksplitter.h" +#include "deflate.h" +#include "symbols.h" +#include "tree.h" +#include "util.h" + +typedef struct SymbolStats { + /* The literal and length symbols. */ + size_t litlens[ZOPFLI_NUM_LL]; + /* The 32 unique dist symbols, not the 32768 possible dists. */ + size_t dists[ZOPFLI_NUM_D]; + + /* Length of each lit/len symbol in bits. */ + double ll_symbols[ZOPFLI_NUM_LL]; + /* Length of each dist symbol in bits. */ + double d_symbols[ZOPFLI_NUM_D]; +} SymbolStats; + +/* Sets everything to 0. */ +static void InitStats(SymbolStats* stats) { + memset(stats->litlens, 0, ZOPFLI_NUM_LL * sizeof(stats->litlens[0])); + memset(stats->dists, 0, ZOPFLI_NUM_D * sizeof(stats->dists[0])); + + memset(stats->ll_symbols, 0, ZOPFLI_NUM_LL * sizeof(stats->ll_symbols[0])); + memset(stats->d_symbols, 0, ZOPFLI_NUM_D * sizeof(stats->d_symbols[0])); +} + +static void CopyStats(SymbolStats* source, SymbolStats* dest) { + memcpy(dest->litlens, source->litlens, + ZOPFLI_NUM_LL * sizeof(dest->litlens[0])); + memcpy(dest->dists, source->dists, ZOPFLI_NUM_D * sizeof(dest->dists[0])); + + memcpy(dest->ll_symbols, source->ll_symbols, + ZOPFLI_NUM_LL * sizeof(dest->ll_symbols[0])); + memcpy(dest->d_symbols, source->d_symbols, + ZOPFLI_NUM_D * sizeof(dest->d_symbols[0])); +} + +/* Adds the bit lengths. */ +static void AddWeighedStatFreqs(const SymbolStats* stats1, double w1, + const SymbolStats* stats2, double w2, + SymbolStats* result) { + size_t i; + for (i = 0; i < ZOPFLI_NUM_LL; i++) { + result->litlens[i] = + (size_t) (stats1->litlens[i] * w1 + stats2->litlens[i] * w2); + } + for (i = 0; i < ZOPFLI_NUM_D; i++) { + result->dists[i] = + (size_t) (stats1->dists[i] * w1 + stats2->dists[i] * w2); + } + result->litlens[256] = 1; /* End symbol. */ +} + +typedef struct RanState { + unsigned int m_w, m_z; +} RanState; + +static void InitRanState(RanState* state) { + state->m_w = 1; + state->m_z = 2; +} + +/* Get random number: "Multiply-With-Carry" generator of G. Marsaglia */ +static unsigned int Ran(RanState* state) { + state->m_z = 36969 * (state->m_z & 65535) + (state->m_z >> 16); + state->m_w = 18000 * (state->m_w & 65535) + (state->m_w >> 16); + return (state->m_z << 16) + state->m_w; /* 32-bit result. */ +} + +static void RandomizeFreqs(RanState* state, size_t* freqs, int n) { + int i; + for (i = 0; i < n; i++) { + if ((Ran(state) >> 4) % 3 == 0) freqs[i] = freqs[Ran(state) % n]; + } +} + +static void RandomizeStatFreqs(RanState* state, SymbolStats* stats) { + RandomizeFreqs(state, stats->litlens, ZOPFLI_NUM_LL); + RandomizeFreqs(state, stats->dists, ZOPFLI_NUM_D); + stats->litlens[256] = 1; /* End symbol. */ +} + +static void ClearStatFreqs(SymbolStats* stats) { + size_t i; + for (i = 0; i < ZOPFLI_NUM_LL; i++) stats->litlens[i] = 0; + for (i = 0; i < ZOPFLI_NUM_D; i++) stats->dists[i] = 0; +} + +/* +Function that calculates a cost based on a model for the given LZ77 symbol. +litlen: means literal symbol if dist is 0, length otherwise. +*/ +typedef double CostModelFun(unsigned litlen, unsigned dist, void* context); + +/* +Cost model which should exactly match fixed tree. +type: CostModelFun +*/ +static double GetCostFixed(unsigned litlen, unsigned dist, void* unused) { + (void)unused; + if (dist == 0) { + if (litlen <= 143) return 8; + else return 9; + } else { + int dbits = ZopfliGetDistExtraBits(dist); + int lbits = ZopfliGetLengthExtraBits(litlen); + int lsym = ZopfliGetLengthSymbol(litlen); + int cost = 0; + if (lsym <= 279) cost += 7; + else cost += 8; + cost += 5; /* Every dist symbol has length 5. */ + return cost + dbits + lbits; + } +} + +/* +Cost model based on symbol statistics. +type: CostModelFun +*/ +static double GetCostStat(unsigned litlen, unsigned dist, void* context) { + SymbolStats* stats = (SymbolStats*)context; + if (dist == 0) { + return stats->ll_symbols[litlen]; + } else { + int lsym = ZopfliGetLengthSymbol(litlen); + int lbits = ZopfliGetLengthExtraBits(litlen); + int dsym = ZopfliGetDistSymbol(dist); + int dbits = ZopfliGetDistExtraBits(dist); + return lbits + dbits + stats->ll_symbols[lsym] + stats->d_symbols[dsym]; + } +} + +/* +Finds the minimum possible cost this cost model can return for valid length and +distance symbols. +*/ +static double GetCostModelMinCost(CostModelFun* costmodel, void* costcontext) { + double mincost; + int bestlength = 0; /* length that has lowest cost in the cost model */ + int bestdist = 0; /* distance that has lowest cost in the cost model */ + int i; + /* + Table of distances that have a different distance symbol in the deflate + specification. Each value is the first distance that has a new symbol. Only + different symbols affect the cost model so only these need to be checked. + See RFC 1951 section 3.2.5. Compressed blocks (length and distance codes). + */ + static const int dsymbols[30] = { + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, + 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 + }; + + mincost = ZOPFLI_LARGE_FLOAT; + for (i = 3; i < 259; i++) { + double c = costmodel(i, 1, costcontext); + if (c < mincost) { + bestlength = i; + mincost = c; + } + } + + mincost = ZOPFLI_LARGE_FLOAT; + for (i = 0; i < 30; i++) { + double c = costmodel(3, dsymbols[i], costcontext); + if (c < mincost) { + bestdist = dsymbols[i]; + mincost = c; + } + } + + return costmodel(bestlength, bestdist, costcontext); +} + +static size_t zopfli_min(size_t a, size_t b) { + return a < b ? a : b; +} + +/* +Performs the forward pass for "squeeze". Gets the most optimal length to reach +every byte from a previous byte, using cost calculations. +s: the ZopfliBlockState +in: the input data array +instart: where to start +inend: where to stop (not inclusive) +costmodel: function to calculate the cost of some lit/len/dist pair. +costcontext: abstract context for the costmodel function +length_array: output array of size (inend - instart) which will receive the best + length to reach this byte from a previous byte. +returns the cost that was, according to the costmodel, needed to get to the end. +*/ +static double GetBestLengths(ZopfliBlockState *s, + const unsigned char* in, + size_t instart, size_t inend, + CostModelFun* costmodel, void* costcontext, + unsigned short* length_array, + ZopfliHash* h, float* costs) { + /* Best cost to get here so far. */ + size_t blocksize = inend - instart; + size_t i = 0, k, kend; + unsigned short leng; + unsigned short dist; + unsigned short sublen[259]; + size_t windowstart = instart > ZOPFLI_WINDOW_SIZE + ? instart - ZOPFLI_WINDOW_SIZE : 0; + double result; + double mincost = GetCostModelMinCost(costmodel, costcontext); + double mincostaddcostj; + + if (instart == inend) return 0; + + ZopfliResetHash(ZOPFLI_WINDOW_SIZE, h); + ZopfliWarmupHash(in, windowstart, inend, h); + for (i = windowstart; i < instart; i++) { + ZopfliUpdateHash(in, i, inend, h); + } + + for (i = 1; i < blocksize + 1; i++) costs[i] = ZOPFLI_LARGE_FLOAT; + costs[0] = 0; /* Because it's the start. */ + length_array[0] = 0; + + for (i = instart; i < inend; i++) { + size_t j = i - instart; /* Index in the costs array and length_array. */ + ZopfliUpdateHash(in, i, inend, h); + +#ifdef ZOPFLI_SHORTCUT_LONG_REPETITIONS + /* If we're in a long repetition of the same character and have more than + ZOPFLI_MAX_MATCH characters before and after our position. */ + if (h->same[i & ZOPFLI_WINDOW_MASK] > ZOPFLI_MAX_MATCH * 2 + && i > instart + ZOPFLI_MAX_MATCH + 1 + && i + ZOPFLI_MAX_MATCH * 2 + 1 < inend + && h->same[(i - ZOPFLI_MAX_MATCH) & ZOPFLI_WINDOW_MASK] + > ZOPFLI_MAX_MATCH) { + double symbolcost = costmodel(ZOPFLI_MAX_MATCH, 1, costcontext); + /* Set the length to reach each one to ZOPFLI_MAX_MATCH, and the cost to + the cost corresponding to that length. Doing this, we skip + ZOPFLI_MAX_MATCH values to avoid calling ZopfliFindLongestMatch. */ + for (k = 0; k < ZOPFLI_MAX_MATCH; k++) { + costs[j + ZOPFLI_MAX_MATCH] = costs[j] + symbolcost; + length_array[j + ZOPFLI_MAX_MATCH] = ZOPFLI_MAX_MATCH; + i++; + j++; + ZopfliUpdateHash(in, i, inend, h); + } + } +#endif + + ZopfliFindLongestMatch(s, h, in, i, inend, ZOPFLI_MAX_MATCH, sublen, + &dist, &leng); + + /* Literal. */ + if (i + 1 <= inend) { + double newCost = costmodel(in[i], 0, costcontext) + costs[j]; + assert(newCost >= 0); + if (newCost < costs[j + 1]) { + costs[j + 1] = newCost; + length_array[j + 1] = 1; + } + } + /* Lengths. */ + kend = zopfli_min(leng, inend-i); + mincostaddcostj = mincost + costs[j]; + for (k = 3; k <= kend; k++) { + double newCost; + + /* Calling the cost model is expensive, avoid this if we are already at + the minimum possible cost that it can return. */ + if (costs[j + k] <= mincostaddcostj) continue; + + newCost = costmodel(k, sublen[k], costcontext) + costs[j]; + assert(newCost >= 0); + if (newCost < costs[j + k]) { + assert(k <= ZOPFLI_MAX_MATCH); + costs[j + k] = newCost; + length_array[j + k] = k; + } + } + } + + assert(costs[blocksize] >= 0); + result = costs[blocksize]; + + return result; +} + +/* +Calculates the optimal path of lz77 lengths to use, from the calculated +length_array. The length_array must contain the optimal length to reach that +byte. The path will be filled with the lengths to use, so its data size will be +the amount of lz77 symbols. +*/ +static void TraceBackwards(size_t size, const unsigned short* length_array, + unsigned short** path, size_t* pathsize) { + size_t index = size; + if (size == 0) return; + for (;;) { + ZOPFLI_APPEND_DATA(length_array[index], path, pathsize); + assert(length_array[index] <= index); + assert(length_array[index] <= ZOPFLI_MAX_MATCH); + assert(length_array[index] != 0); + index -= length_array[index]; + if (index == 0) break; + } + + /* Mirror result. */ + for (index = 0; index < *pathsize / 2; index++) { + unsigned short temp = (*path)[index]; + (*path)[index] = (*path)[*pathsize - index - 1]; + (*path)[*pathsize - index - 1] = temp; + } +} + +static void FollowPath(ZopfliBlockState* s, + const unsigned char* in, size_t instart, size_t inend, + unsigned short* path, size_t pathsize, + ZopfliLZ77Store* store, ZopfliHash *h) { + size_t i, j, pos = 0; + size_t windowstart = instart > ZOPFLI_WINDOW_SIZE + ? instart - ZOPFLI_WINDOW_SIZE : 0; + + size_t total_length_test = 0; + + if (instart == inend) return; + + ZopfliResetHash(ZOPFLI_WINDOW_SIZE, h); + ZopfliWarmupHash(in, windowstart, inend, h); + for (i = windowstart; i < instart; i++) { + ZopfliUpdateHash(in, i, inend, h); + } + + pos = instart; + for (i = 0; i < pathsize; i++) { + unsigned short length = path[i]; + unsigned short dummy_length; + unsigned short dist; + assert(pos < inend); + + ZopfliUpdateHash(in, pos, inend, h); + + /* Add to output. */ + if (length >= ZOPFLI_MIN_MATCH) { + /* Get the distance by recalculating longest match. The found length + should match the length from the path. */ + ZopfliFindLongestMatch(s, h, in, pos, inend, length, 0, + &dist, &dummy_length); + assert(!(dummy_length != length && length > 2 && dummy_length > 2)); + ZopfliVerifyLenDist(in, inend, pos, dist, length); + ZopfliStoreLitLenDist(length, dist, pos, store); + total_length_test += length; + } else { + length = 1; + ZopfliStoreLitLenDist(in[pos], 0, pos, store); + total_length_test++; + } + + + assert(pos + length <= inend); + for (j = 1; j < length; j++) { + ZopfliUpdateHash(in, pos + j, inend, h); + } + + pos += length; + } +} + +/* Calculates the entropy of the statistics */ +static void CalculateStatistics(SymbolStats* stats) { + ZopfliCalculateEntropy(stats->litlens, ZOPFLI_NUM_LL, stats->ll_symbols); + ZopfliCalculateEntropy(stats->dists, ZOPFLI_NUM_D, stats->d_symbols); +} + +/* Appends the symbol statistics from the store. */ +static void GetStatistics(const ZopfliLZ77Store* store, SymbolStats* stats) { + size_t i; + for (i = 0; i < store->size; i++) { + if (store->dists[i] == 0) { + stats->litlens[store->litlens[i]]++; + } else { + stats->litlens[ZopfliGetLengthSymbol(store->litlens[i])]++; + stats->dists[ZopfliGetDistSymbol(store->dists[i])]++; + } + } + stats->litlens[256] = 1; /* End symbol. */ + + CalculateStatistics(stats); +} + +/* +Does a single run for ZopfliLZ77Optimal. For good compression, repeated runs +with updated statistics should be performed. +s: the block state +in: the input data array +instart: where to start +inend: where to stop (not inclusive) +path: pointer to dynamically allocated memory to store the path +pathsize: pointer to the size of the dynamic path array +length_array: array of size (inend - instart) used to store lengths +costmodel: function to use as the cost model for this squeeze run +costcontext: abstract context for the costmodel function +store: place to output the LZ77 data +returns the cost that was, according to the costmodel, needed to get to the end. + This is not the actual cost. +*/ +static double LZ77OptimalRun(ZopfliBlockState* s, + const unsigned char* in, size_t instart, size_t inend, + unsigned short** path, size_t* pathsize, + unsigned short* length_array, CostModelFun* costmodel, + void* costcontext, ZopfliLZ77Store* store, + ZopfliHash* h, float* costs) { + double cost = GetBestLengths(s, in, instart, inend, costmodel, + costcontext, length_array, h, costs); + free(*path); + *path = 0; + *pathsize = 0; + TraceBackwards(inend - instart, length_array, path, pathsize); + FollowPath(s, in, instart, inend, *path, *pathsize, store, h); + assert(cost < ZOPFLI_LARGE_FLOAT); + return cost; +} + +void ZopfliLZ77Optimal(ZopfliBlockState *s, + const unsigned char* in, size_t instart, size_t inend, + int numiterations, + ZopfliLZ77Store* store) { + /* Dist to get to here with smallest cost. */ + size_t blocksize = inend - instart; + unsigned short* length_array = + (unsigned short*)malloc(sizeof(unsigned short) * (blocksize + 1)); + unsigned short* path = 0; + size_t pathsize = 0; + ZopfliLZ77Store currentstore; + ZopfliHash hash; + ZopfliHash* h = &hash; + SymbolStats stats, beststats, laststats; + int i; + float* costs = (float*)malloc(sizeof(float) * (blocksize + 1)); + double cost; + double bestcost = ZOPFLI_LARGE_FLOAT; + double lastcost = 0; + /* Try randomizing the costs a bit once the size stabilizes. */ + RanState ran_state; + int lastrandomstep = -1; + + if (!costs) exit(-1); /* Allocation failed. */ + if (!length_array) exit(-1); /* Allocation failed. */ + + InitRanState(&ran_state); + InitStats(&stats); + ZopfliInitLZ77Store(in, ¤tstore); + ZopfliAllocHash(ZOPFLI_WINDOW_SIZE, h); + + /* Do regular deflate, then loop multiple shortest path runs, each time using + the statistics of the previous run. */ + + /* Initial run. */ + ZopfliLZ77Greedy(s, in, instart, inend, ¤tstore, h); + GetStatistics(¤tstore, &stats); + + /* Repeat statistics with each time the cost model from the previous stat + run. */ + for (i = 0; i < numiterations; i++) { + ZopfliCleanLZ77Store(¤tstore); + ZopfliInitLZ77Store(in, ¤tstore); + LZ77OptimalRun(s, in, instart, inend, &path, &pathsize, + length_array, GetCostStat, (void*)&stats, + ¤tstore, h, costs); + cost = ZopfliCalculateBlockSize(¤tstore, 0, currentstore.size, 2); + if (s->options->verbose_more || (s->options->verbose && cost < bestcost)) { + fprintf(stderr, "Iteration %d: %d bit\n", i, (int) cost); + } + if (cost < bestcost) { + /* Copy to the output store. */ + ZopfliCopyLZ77Store(¤tstore, store); + CopyStats(&stats, &beststats); + bestcost = cost; + } + CopyStats(&stats, &laststats); + ClearStatFreqs(&stats); + GetStatistics(¤tstore, &stats); + if (lastrandomstep != -1) { + /* This makes it converge slower but better. Do it only once the + randomness kicks in so that if the user does few iterations, it gives a + better result sooner. */ + AddWeighedStatFreqs(&stats, 1.0, &laststats, 0.5, &stats); + CalculateStatistics(&stats); + } + if (i > 5 && cost == lastcost) { + CopyStats(&beststats, &stats); + RandomizeStatFreqs(&ran_state, &stats); + CalculateStatistics(&stats); + lastrandomstep = i; + } + lastcost = cost; + } + + free(length_array); + free(path); + free(costs); + ZopfliCleanLZ77Store(¤tstore); + ZopfliCleanHash(h); +} + +void ZopfliLZ77OptimalFixed(ZopfliBlockState *s, + const unsigned char* in, + size_t instart, size_t inend, + ZopfliLZ77Store* store) +{ + /* Dist to get to here with smallest cost. */ + size_t blocksize = inend - instart; + unsigned short* length_array = + (unsigned short*)malloc(sizeof(unsigned short) * (blocksize + 1)); + unsigned short* path = 0; + size_t pathsize = 0; + ZopfliHash hash; + ZopfliHash* h = &hash; + float* costs = (float*)malloc(sizeof(float) * (blocksize + 1)); + + if (!costs) exit(-1); /* Allocation failed. */ + if (!length_array) exit(-1); /* Allocation failed. */ + + ZopfliAllocHash(ZOPFLI_WINDOW_SIZE, h); + + s->blockstart = instart; + s->blockend = inend; + + /* Shortest path for fixed tree This one should give the shortest possible + result for fixed tree, no repeated runs are needed since the tree is known. */ + LZ77OptimalRun(s, in, instart, inend, &path, &pathsize, + length_array, GetCostFixed, 0, store, h, costs); + + free(length_array); + free(path); + free(costs); + ZopfliCleanHash(h); +} diff --git a/tools/bible/zopfli4k/squeeze.h b/tools/bible/zopfli4k/squeeze.h new file mode 100644 index 000000000..48bb7753d --- /dev/null +++ b/tools/bible/zopfli4k/squeeze.h @@ -0,0 +1,61 @@ +/* +Copyright 2011 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +/* +The squeeze functions do enhanced LZ77 compression by optimal parsing with a +cost model, rather than greedily choosing the longest length or using a single +step of lazy matching like regular implementations. + +Since the cost model is based on the Huffman tree that can only be calculated +after the LZ77 data is generated, there is a chicken and egg problem, and +multiple runs are done with updated cost models to converge to a better +solution. +*/ + +#ifndef ZOPFLI_SQUEEZE_H_ +#define ZOPFLI_SQUEEZE_H_ + +#include "lz77.h" + +/* +Calculates lit/len and dist pairs for given data. +If instart is larger than 0, it uses values before instart as starting +dictionary. +*/ +void ZopfliLZ77Optimal(ZopfliBlockState *s, + const unsigned char* in, size_t instart, size_t inend, + int numiterations, + ZopfliLZ77Store* store); + +/* +Does the same as ZopfliLZ77Optimal, but optimized for the fixed tree of the +deflate standard. +The fixed tree never gives the best compression. But this gives the best +possible LZ77 encoding possible with the fixed tree. +This does not create or output any fixed tree, only LZ77 data optimized for +using with a fixed tree. +If instart is larger than 0, it uses values before instart as starting +dictionary. +*/ +void ZopfliLZ77OptimalFixed(ZopfliBlockState *s, + const unsigned char* in, + size_t instart, size_t inend, + ZopfliLZ77Store* store); + +#endif /* ZOPFLI_SQUEEZE_H_ */ diff --git a/tools/bible/zopfli4k/symbols.h b/tools/bible/zopfli4k/symbols.h new file mode 100644 index 000000000..b49df06c7 --- /dev/null +++ b/tools/bible/zopfli4k/symbols.h @@ -0,0 +1,239 @@ +/* +Copyright 2016 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +/* +Utilities for using the lz77 symbols of the deflate spec. +*/ + +#ifndef ZOPFLI_SYMBOLS_H_ +#define ZOPFLI_SYMBOLS_H_ + +/* __has_builtin available in clang */ +#ifdef __has_builtin +# if __has_builtin(__builtin_clz) +# define ZOPFLI_HAS_BUILTIN_CLZ +# endif +/* __builtin_clz available beginning with GCC 3.4 */ +#elif __GNUC__ * 100 + __GNUC_MINOR__ >= 304 +# define ZOPFLI_HAS_BUILTIN_CLZ +#endif + +/* Gets the amount of extra bits for the given dist, cfr. the DEFLATE spec. */ +static int ZopfliGetDistExtraBits(int dist) { +#ifdef ZOPFLI_HAS_BUILTIN_CLZ + if (dist < 5) return 0; + return (31 ^ __builtin_clz(dist - 1)) - 1; /* log2(dist - 1) - 1 */ +#else + if (dist < 5) return 0; + else if (dist < 9) return 1; + else if (dist < 17) return 2; + else if (dist < 33) return 3; + else if (dist < 65) return 4; + else if (dist < 129) return 5; + else if (dist < 257) return 6; + else if (dist < 513) return 7; + else if (dist < 1025) return 8; + else if (dist < 2049) return 9; + else if (dist < 4097) return 10; + else if (dist < 8193) return 11; + else if (dist < 16385) return 12; + else return 13; +#endif +} + +/* Gets value of the extra bits for the given dist, cfr. the DEFLATE spec. */ +static int ZopfliGetDistExtraBitsValue(int dist) { +#ifdef ZOPFLI_HAS_BUILTIN_CLZ + if (dist < 5) { + return 0; + } else { + int l = 31 ^ __builtin_clz(dist - 1); /* log2(dist - 1) */ + return (dist - (1 + (1 << l))) & ((1 << (l - 1)) - 1); + } +#else + if (dist < 5) return 0; + else if (dist < 9) return (dist - 5) & 1; + else if (dist < 17) return (dist - 9) & 3; + else if (dist < 33) return (dist - 17) & 7; + else if (dist < 65) return (dist - 33) & 15; + else if (dist < 129) return (dist - 65) & 31; + else if (dist < 257) return (dist - 129) & 63; + else if (dist < 513) return (dist - 257) & 127; + else if (dist < 1025) return (dist - 513) & 255; + else if (dist < 2049) return (dist - 1025) & 511; + else if (dist < 4097) return (dist - 2049) & 1023; + else if (dist < 8193) return (dist - 4097) & 2047; + else if (dist < 16385) return (dist - 8193) & 4095; + else return (dist - 16385) & 8191; +#endif +} + +/* Gets the symbol for the given dist, cfr. the DEFLATE spec. */ +static int ZopfliGetDistSymbol(int dist) { +#ifdef ZOPFLI_HAS_BUILTIN_CLZ + if (dist < 5) { + return dist - 1; + } else { + int l = (31 ^ __builtin_clz(dist - 1)); /* log2(dist - 1) */ + int r = ((dist - 1) >> (l - 1)) & 1; + return l * 2 + r; + } +#else + if (dist < 193) { + if (dist < 13) { /* dist 0..13. */ + if (dist < 5) return dist - 1; + else if (dist < 7) return 4; + else if (dist < 9) return 5; + else return 6; + } else { /* dist 13..193. */ + if (dist < 17) return 7; + else if (dist < 25) return 8; + else if (dist < 33) return 9; + else if (dist < 49) return 10; + else if (dist < 65) return 11; + else if (dist < 97) return 12; + else if (dist < 129) return 13; + else return 14; + } + } else { + if (dist < 2049) { /* dist 193..2049. */ + if (dist < 257) return 15; + else if (dist < 385) return 16; + else if (dist < 513) return 17; + else if (dist < 769) return 18; + else if (dist < 1025) return 19; + else if (dist < 1537) return 20; + else return 21; + } else { /* dist 2049..32768. */ + if (dist < 3073) return 22; + else if (dist < 4097) return 23; + else if (dist < 6145) return 24; + else if (dist < 8193) return 25; + else if (dist < 12289) return 26; + else if (dist < 16385) return 27; + else if (dist < 24577) return 28; + else return 29; + } + } +#endif +} + +/* Gets the amount of extra bits for the given length, cfr. the DEFLATE spec. */ +static int ZopfliGetLengthExtraBits(int l) { + static const int table[259] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0 + }; + return table[l]; +} + +/* Gets value of the extra bits for the given length, cfr. the DEFLATE spec. */ +static int ZopfliGetLengthExtraBitsValue(int l) { + static const int table[259] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 0, + 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, + 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 0 + }; + return table[l]; +} + +/* +Gets the symbol for the given length, cfr. the DEFLATE spec. +Returns the symbol in the range [257-285] (inclusive) +*/ +static int ZopfliGetLengthSymbol(int l) { + static const int table[259] = { + 0, 0, 0, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 265, 266, 266, 267, 267, 268, 268, + 269, 269, 269, 269, 270, 270, 270, 270, + 271, 271, 271, 271, 272, 272, 272, 272, + 273, 273, 273, 273, 273, 273, 273, 273, + 274, 274, 274, 274, 274, 274, 274, 274, + 275, 275, 275, 275, 275, 275, 275, 275, + 276, 276, 276, 276, 276, 276, 276, 276, + 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 277, 277, 277, + 278, 278, 278, 278, 278, 278, 278, 278, + 278, 278, 278, 278, 278, 278, 278, 278, + 279, 279, 279, 279, 279, 279, 279, 279, + 279, 279, 279, 279, 279, 279, 279, 279, + 280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280, + 281, 281, 281, 281, 281, 281, 281, 281, + 281, 281, 281, 281, 281, 281, 281, 281, + 281, 281, 281, 281, 281, 281, 281, 281, + 281, 281, 281, 281, 281, 281, 281, 281, + 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, + 283, 283, 283, 283, 283, 283, 283, 283, + 283, 283, 283, 283, 283, 283, 283, 283, + 283, 283, 283, 283, 283, 283, 283, 283, + 283, 283, 283, 283, 283, 283, 283, 283, + 284, 284, 284, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 285 + }; + return table[l]; +} + +/* Gets the amount of extra bits for the given length symbol. */ +static int ZopfliGetLengthSymbolExtraBits(int s) { + static const int table[29] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 + }; + return table[s - 257]; +} + +/* Gets the amount of extra bits for the given distance symbol. */ +static int ZopfliGetDistSymbolExtraBits(int s) { + static const int table[30] = { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 + }; + return table[s]; +} + +#endif /* ZOPFLI_SYMBOLS_H_ */ diff --git a/tools/bible/zopfli4k/tree.c b/tools/bible/zopfli4k/tree.c new file mode 100644 index 000000000..c4575119d --- /dev/null +++ b/tools/bible/zopfli4k/tree.c @@ -0,0 +1,101 @@ +/* +Copyright 2011 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +#include "tree.h" + +#include +#include +#include +#include + +#include "katajainen.h" +#include "util.h" + +void ZopfliLengthsToSymbols(const unsigned* lengths, size_t n, unsigned maxbits, + unsigned* symbols) { + size_t* bl_count = (size_t*)malloc(sizeof(size_t) * (maxbits + 1)); + size_t* next_code = (size_t*)malloc(sizeof(size_t) * (maxbits + 1)); + unsigned bits, i; + unsigned code; + + for (i = 0; i < n; i++) { + symbols[i] = 0; + } + + /* 1) Count the number of codes for each code length. Let bl_count[N] be the + number of codes of length N, N >= 1. */ + for (bits = 0; bits <= maxbits; bits++) { + bl_count[bits] = 0; + } + for (i = 0; i < n; i++) { + assert(lengths[i] <= maxbits); + bl_count[lengths[i]]++; + } + /* 2) Find the numerical value of the smallest code for each code length. */ + code = 0; + bl_count[0] = 0; + for (bits = 1; bits <= maxbits; bits++) { + code = (code + bl_count[bits-1]) << 1; + next_code[bits] = code; + } + /* 3) Assign numerical values to all codes, using consecutive values for all + codes of the same length with the base values determined at step 2. */ + for (i = 0; i < n; i++) { + unsigned len = lengths[i]; + if (len != 0) { + symbols[i] = next_code[len]; + next_code[len]++; + } + } + + free(bl_count); + free(next_code); +} + +void ZopfliCalculateEntropy(const size_t* count, size_t n, double* bitlengths) { + static const double kInvLog2 = 1.4426950408889; /* 1.0 / log(2.0) */ + unsigned sum = 0; + unsigned i; + double log2sum; + for (i = 0; i < n; ++i) { + sum += count[i]; + } + log2sum = (sum == 0 ? log(n) : log(sum)) * kInvLog2; + for (i = 0; i < n; ++i) { + /* When the count of the symbol is 0, but its cost is requested anyway, it + means the symbol will appear at least once anyway, so give it the cost as if + its count is 1.*/ + if (count[i] == 0) bitlengths[i] = log2sum; + else bitlengths[i] = log2sum - log(count[i]) * kInvLog2; + /* Depending on compiler and architecture, the above subtraction of two + floating point numbers may give a negative result very close to zero + instead of zero (e.g. -5.973954e-17 with gcc 4.1.2 on Ubuntu 11.4). Clamp + it to zero. These floating point imprecisions do not affect the cost model + significantly so this is ok. */ + if (bitlengths[i] < 0 && bitlengths[i] > -1e-5) bitlengths[i] = 0; + assert(bitlengths[i] >= 0); + } +} + +void ZopfliCalculateBitLengths(const size_t* count, size_t n, int maxbits, + unsigned* bitlengths) { + int error = ZopfliLengthLimitedCodeLengths(count, n, maxbits, bitlengths); + (void) error; + assert(!error); +} diff --git a/tools/bible/zopfli4k/tree.h b/tools/bible/zopfli4k/tree.h new file mode 100644 index 000000000..4d6f46975 --- /dev/null +++ b/tools/bible/zopfli4k/tree.h @@ -0,0 +1,51 @@ +/* +Copyright 2011 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +/* +Utilities for creating and using Huffman trees. +*/ + +#ifndef ZOPFLI_TREE_H_ +#define ZOPFLI_TREE_H_ + +#include + +/* +Calculates the bitlengths for the Huffman tree, based on the counts of each +symbol. +*/ +void ZopfliCalculateBitLengths(const size_t* count, size_t n, int maxbits, + unsigned *bitlengths); + +/* +Converts a series of Huffman tree bitlengths, to the bit values of the symbols. +*/ +void ZopfliLengthsToSymbols(const unsigned* lengths, size_t n, unsigned maxbits, + unsigned* symbols); + +/* +Calculates the entropy of each symbol, based on the counts of each symbol. The +result is similar to the result of ZopfliCalculateBitLengths, but with the +actual theoritical bit lengths according to the entropy. Since the resulting +values are fractional, they cannot be used to encode the tree specified by +DEFLATE. +*/ +void ZopfliCalculateEntropy(const size_t* count, size_t n, double* bitlengths); + +#endif /* ZOPFLI_TREE_H_ */ diff --git a/tools/bible/zopfli4k/util.c b/tools/bible/zopfli4k/util.c new file mode 100644 index 000000000..428961c46 --- /dev/null +++ b/tools/bible/zopfli4k/util.c @@ -0,0 +1,35 @@ +/* +Copyright 2011 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +#include "util.h" + +#include "zopfli.h" + +#include +#include +#include + +void ZopfliInitOptions(ZopfliOptions* options) { + options->verbose = 0; + options->verbose_more = 0; + options->numiterations = 15; + options->blocksplitting = 1; + options->blocksplittinglast = 0; + options->blocksplittingmax = 15; +} diff --git a/tools/bible/zopfli4k/util.h b/tools/bible/zopfli4k/util.h new file mode 100644 index 000000000..d1e4b3575 --- /dev/null +++ b/tools/bible/zopfli4k/util.h @@ -0,0 +1,159 @@ +/* +Copyright 2011 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +/* +Several utilities, including: #defines to try different compression results, +basic deflate specification values and generic program options. +*/ + +#ifndef ZOPFLI_UTIL_H_ +#define ZOPFLI_UTIL_H_ + +#include +#include + +/* Minimum and maximum length that can be encoded in deflate. */ +#define ZOPFLI_MAX_MATCH 258 +#define ZOPFLI_MIN_MATCH 3 + +/* Number of distinct literal/length and distance symbols in DEFLATE */ +#define ZOPFLI_NUM_LL 288 +#define ZOPFLI_NUM_D 32 + +/* +The window size for deflate. Must be a power of two. This should be 32768, the +maximum possible by the deflate spec. Anything less hurts compression more than +speed. +*/ +/* MeshCore host-only modification: match the firmware's existing 4 KiB history. */ +#define ZOPFLI_WINDOW_SIZE 4096 + +/* +The window mask used to wrap indices into the window. This is why the +window size must be a power of two. +*/ +#define ZOPFLI_WINDOW_MASK (ZOPFLI_WINDOW_SIZE - 1) + +/* +A block structure of huge, non-smart, blocks to divide the input into, to allow +operating on huge files without exceeding memory, such as the 1GB wiki9 corpus. +The whole compression algorithm, including the smarter block splitting, will +be executed independently on each huge block. +Dividing into huge blocks hurts compression, but not much relative to the size. +Set it to 0 to disable master blocks. +*/ +#define ZOPFLI_MASTER_BLOCK_SIZE 1000000 + +/* +Used to initialize costs for example +*/ +#define ZOPFLI_LARGE_FLOAT 1e30 + +/* +For longest match cache. max 256. Uses huge amounts of memory but makes it +faster. Uses this many times three bytes per single byte of the input data. +This is so because longest match finding has to find the exact distance +that belongs to each length for the best lz77 strategy. +Good values: e.g. 5, 8. +*/ +#define ZOPFLI_CACHE_LENGTH 8 + +/* +limit the max hash chain hits for this hash value. This has an effect only +on files where the hash value is the same very often. On these files, this +gives worse compression (the value should ideally be 32768, which is the +ZOPFLI_WINDOW_SIZE, while zlib uses 4096 even for best level), but makes it +faster on some specific files. +Good value: e.g. 8192. +*/ +#define ZOPFLI_MAX_CHAIN_HITS 8192 + +/* +Whether to use the longest match cache for ZopfliFindLongestMatch. This cache +consumes a lot of memory but speeds it up. No effect on compression size. +*/ +#define ZOPFLI_LONGEST_MATCH_CACHE + +/* +Enable to remember amount of successive identical bytes in the hash chain for +finding longest match +required for ZOPFLI_HASH_SAME_HASH and ZOPFLI_SHORTCUT_LONG_REPETITIONS +This has no effect on the compression result, and enabling it increases speed. +*/ +#define ZOPFLI_HASH_SAME + +/* +Switch to a faster hash based on the info from ZOPFLI_HASH_SAME once the +best length so far is long enough. This is way faster for files with lots of +identical bytes, on which the compressor is otherwise too slow. Regular files +are unaffected or maybe a tiny bit slower. +This has no effect on the compression result, only on speed. +*/ +#define ZOPFLI_HASH_SAME_HASH + +/* +Enable this, to avoid slowness for files which are a repetition of the same +character more than a multiple of ZOPFLI_MAX_MATCH times. This should not affect +the compression result. +*/ +#define ZOPFLI_SHORTCUT_LONG_REPETITIONS + +/* +Whether to use lazy matching in the greedy LZ77 implementation. This gives a +better result of ZopfliLZ77Greedy, but the effect this has on the optimal LZ77 +varies from file to file. +*/ +#define ZOPFLI_LAZY_MATCHING + +/* +Appends value to dynamically allocated memory, doubling its allocation size +whenever needed. + +value: the value to append, type T +data: pointer to the dynamic array to append to, type T** +size: pointer to the size of the array to append to, type size_t*. This is the +size that you consider the array to be, not the internal allocation size. +Precondition: allocated size of data is at least a power of two greater than or +equal than *size. +*/ +#ifdef __cplusplus /* C++ cannot assign void* from malloc to *data */ +#define ZOPFLI_APPEND_DATA(/* T */ value, /* T** */ data, /* size_t* */ size) {\ + if (!((*size) & ((*size) - 1))) {\ + /*double alloc size if it's a power of two*/\ + void** data_void = reinterpret_cast(data);\ + *data_void = (*size) == 0 ? malloc(sizeof(**data))\ + : realloc((*data), (*size) * 2 * sizeof(**data));\ + }\ + (*data)[(*size)] = (value);\ + (*size)++;\ +} +#else /* C gives problems with strict-aliasing rules for (void**) cast */ +#define ZOPFLI_APPEND_DATA(/* T */ value, /* T** */ data, /* size_t* */ size) {\ + if (!((*size) & ((*size) - 1))) {\ + /*double alloc size if it's a power of two*/\ + (*data) = (*size) == 0 ? malloc(sizeof(**data))\ + : realloc((*data), (*size) * 2 * sizeof(**data));\ + }\ + (*data)[(*size)] = (value);\ + (*size)++;\ +} +#endif + + +#endif /* ZOPFLI_UTIL_H_ */ diff --git a/tools/bible/zopfli4k/zlib_container.c b/tools/bible/zopfli4k/zlib_container.c new file mode 100644 index 000000000..130ffc793 --- /dev/null +++ b/tools/bible/zopfli4k/zlib_container.c @@ -0,0 +1,79 @@ +/* +Copyright 2013 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +#include "zlib_container.h" +#include "util.h" + +#include + +#include "deflate.h" + + +/* Calculates the adler32 checksum of the data */ +static unsigned adler32(const unsigned char* data, size_t size) +{ + static const unsigned sums_overflow = 5550; + unsigned s1 = 1; + unsigned s2 = 1 >> 16; + + while (size > 0) { + size_t amount = size > sums_overflow ? sums_overflow : size; + size -= amount; + while (amount > 0) { + s1 += (*data++); + s2 += s1; + amount--; + } + s1 %= 65521; + s2 %= 65521; + } + + return (s2 << 16) | s1; +} + +void ZopfliZlibCompress(const ZopfliOptions* options, + const unsigned char* in, size_t insize, + unsigned char** out, size_t* outsize) { + unsigned char bitpointer = 0; + unsigned checksum = adler32(in, (unsigned)insize); + unsigned cmf = 120; /* CM 8, CINFO 7. See zlib spec.*/ + unsigned flevel = 3; + unsigned fdict = 0; + unsigned cmfflg = 256 * cmf + fdict * 32 + flevel * 64; + unsigned fcheck = 31 - cmfflg % 31; + cmfflg += fcheck; + + ZOPFLI_APPEND_DATA(cmfflg / 256, out, outsize); + ZOPFLI_APPEND_DATA(cmfflg % 256, out, outsize); + + ZopfliDeflate(options, 2 /* dynamic block */, 1 /* final */, + in, insize, &bitpointer, out, outsize); + + ZOPFLI_APPEND_DATA((checksum >> 24) % 256, out, outsize); + ZOPFLI_APPEND_DATA((checksum >> 16) % 256, out, outsize); + ZOPFLI_APPEND_DATA((checksum >> 8) % 256, out, outsize); + ZOPFLI_APPEND_DATA(checksum % 256, out, outsize); + + if (options->verbose) { + fprintf(stderr, + "Original Size: %d, Zlib: %d, Compression: %f%% Removed\n", + (int)insize, (int)*outsize, + 100.0 * (double)(insize - *outsize) / (double)insize); + } +} diff --git a/tools/bible/zopfli4k/zlib_container.h b/tools/bible/zopfli4k/zlib_container.h new file mode 100644 index 000000000..9ddfb9c1d --- /dev/null +++ b/tools/bible/zopfli4k/zlib_container.h @@ -0,0 +1,50 @@ +/* +Copyright 2013 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +#ifndef ZOPFLI_ZLIB_H_ +#define ZOPFLI_ZLIB_H_ + +/* +Functions to compress according to the Zlib specification. +*/ + +#include "zopfli.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* +Compresses according to the zlib specification and append the compressed +result to the output. + +options: global program options +out: pointer to the dynamic output array to which the result is appended. Must + be freed after use. +outsize: pointer to the dynamic output array size. +*/ +void ZopfliZlibCompress(const ZopfliOptions* options, + const unsigned char* in, size_t insize, + unsigned char** out, size_t* outsize); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* ZOPFLI_ZLIB_H_ */ diff --git a/tools/bible/zopfli4k/zopfli.h b/tools/bible/zopfli4k/zopfli.h new file mode 100644 index 000000000..c079662aa --- /dev/null +++ b/tools/bible/zopfli4k/zopfli.h @@ -0,0 +1,94 @@ +/* +Copyright 2011 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +#ifndef ZOPFLI_ZOPFLI_H_ +#define ZOPFLI_ZOPFLI_H_ + +#include +#include /* for size_t */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* +Options used throughout the program. +*/ +typedef struct ZopfliOptions { + /* Whether to print output */ + int verbose; + + /* Whether to print more detailed output */ + int verbose_more; + + /* + Maximum amount of times to rerun forward and backward pass to optimize LZ77 + compression cost. Good values: 10, 15 for small files, 5 for files over + several MB in size or it will be too slow. + */ + int numiterations; + + /* + If true, splits the data in multiple deflate blocks with optimal choice + for the block boundaries. Block splitting gives better compression. Default: + true (1). + */ + int blocksplitting; + + /* + No longer used, left for compatibility. + */ + int blocksplittinglast; + + /* + Maximum amount of blocks to split into (0 for unlimited, but this can give + extreme results that hurt compression on some files). Default value: 15. + */ + int blocksplittingmax; +} ZopfliOptions; + +/* Initializes options with default values. */ +void ZopfliInitOptions(ZopfliOptions* options); + +/* Output format */ +typedef enum { + ZOPFLI_FORMAT_GZIP, + ZOPFLI_FORMAT_ZLIB, + ZOPFLI_FORMAT_DEFLATE +} ZopfliFormat; + +/* +Compresses according to the given output format and appends the result to the +output. + +options: global program options +output_type: the output format to use +out: pointer to the dynamic output array to which the result is appended. Must + be freed after use +outsize: pointer to the dynamic output array size +*/ +void ZopfliCompress(const ZopfliOptions* options, ZopfliFormat output_type, + const unsigned char* in, size_t insize, + unsigned char** out, size_t* outsize); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* ZOPFLI_ZOPFLI_H_ */ diff --git a/tools/bible/zopfli4k/zopfli_lib.c b/tools/bible/zopfli4k/zopfli_lib.c new file mode 100644 index 000000000..5f5b214ed --- /dev/null +++ b/tools/bible/zopfli4k/zopfli_lib.c @@ -0,0 +1,42 @@ +/* +Copyright 2011 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +#include "zopfli.h" + +#include "deflate.h" +#include "gzip_container.h" +#include "zlib_container.h" + +#include + +void ZopfliCompress(const ZopfliOptions* options, ZopfliFormat output_type, + const unsigned char* in, size_t insize, + unsigned char** out, size_t* outsize) { + if (output_type == ZOPFLI_FORMAT_GZIP) { + ZopfliGzipCompress(options, in, insize, out, outsize); + } else if (output_type == ZOPFLI_FORMAT_ZLIB) { + ZopfliZlibCompress(options, in, insize, out, outsize); + } else if (output_type == ZOPFLI_FORMAT_DEFLATE) { + unsigned char bp = 0; + ZopfliDeflate(options, 2 /* Dynamic block */, 1, + in, insize, &bp, out, outsize); + } else { + assert(0); + } +}