pax_global_header00006660000000000000000000000064152174343610014520gustar00rootroot0000000000000052 comment=09c50ca121c11b0f93c8427662e28244b4a3e0b6 c-blosc2-3.1.5/000077500000000000000000000000001521743436100131325ustar00rootroot00000000000000c-blosc2-3.1.5/.editorconfig000066400000000000000000000004231521743436100156060ustar00rootroot00000000000000; Top-most EditorConfig file root = true ; Global settings [*] end_of_line = LF indent_style = space trim_trailing_whitespace = true insert_final_newline = true ; C source files [*.{h,c}] indent_size = 2 ; CMake [CMakeLists.txt] indent_size = 4 [*.cmake] indent_size = 4 c-blosc2-3.1.5/.gitattributes000066400000000000000000000000351521743436100160230ustar00rootroot00000000000000* text=auto *.md text eol=lf c-blosc2-3.1.5/.github/000077500000000000000000000000001521743436100144725ustar00rootroot00000000000000c-blosc2-3.1.5/.github/FUNDING.yml000066400000000000000000000000261521743436100163050ustar00rootroot00000000000000github: FrancescAlted c-blosc2-3.1.5/.github/ISSUE_TEMPLATE/000077500000000000000000000000001521743436100166555ustar00rootroot00000000000000c-blosc2-3.1.5/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000010621521743436100213460ustar00rootroot00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: '' assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Provide a minimal program reproducing the error. **Expected behavior** A clear and concise description of what you expected to happen. **Logs** If applicable, add logs to help explain your problem. **System information:** - OS: [e.g. OSX] - Compiler [e.g. gcc, clang] - Version [e.g. 2.0.1] **Additional context** Add any other context about the problem here. c-blosc2-3.1.5/.github/dependabot.yml000066400000000000000000000003711521743436100173230ustar00rootroot00000000000000# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" c-blosc2-3.1.5/.github/workflows/000077500000000000000000000000001521743436100165275ustar00rootroot00000000000000c-blosc2-3.1.5/.github/workflows/cmake.yml000066400000000000000000000117551521743436100203430ustar00rootroot00000000000000name: CI CMake on: [push, pull_request] jobs: ci-cmake: name: ${{ matrix.name }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: include: - name: Ubuntu GCC os: ubuntu-latest compiler: gcc - name: Ubuntu GCC No Plugins os: ubuntu-latest compiler: gcc cmake-args: -D BUILD_PLUGINS=OFF # Out of source build - name: Ubuntu GCC OSB os: ubuntu-latest compiler: gcc build-dir: ../build build-src-dir: ../c-blosc2 - name: Ubuntu GCC External LZ4 os: ubuntu-latest compiler: gcc packages: liblz4-1 liblz4-dev cmake-args: -D PREFER_EXTERNAL_LZ4=ON - name: Ubuntu GCC External ZLIB os: ubuntu-latest compiler: gcc packages: zlib1g-dev cmake-args: -D PREFER_EXTERNAL_ZLIB=ON # Not too recent Zstd libs (< 1.4.4) in distros perform pretty bad on compression # ratios when using dictionaries, making some tests not passing. # Commenting this out for the time being. # - name: Ubuntu GCC External ZSTD # os: ubuntu-latest # compiler: gcc # packages: zstd libzstd-dev # cmake-args: -D PREFER_EXTERNAL_ZSTD=ON # For some reason, some tests do not pass on ARM SF and HF. Not sure what's going on, # but having ARM AARCH64 working is good enough for now, so commenting the former out. # - name: Ubuntu GCC ARM SF # os: ubuntu-latest # packages: qemu qemu-user gcc-arm-linux-gnueabi g++-arm-linux-gnueabi libc-dev-armel-cross # cmake-args: -D CMAKE_TOOLCHAIN_FILE=cmake/toolchain-armsf.cmake # - name: Ubuntu GCC ARM HF # os: ubuntu-latest # packages: qemu qemu-user gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf libc-dev-armel-cross # cmake-args: -D CMAKE_TOOLCHAIN_FILE=cmake/toolchain-armhf.cmake # - name: Ubuntu GCC AARCH64 # os: ubuntu-latest # packages: qemu-system-aarch64 qemu-user gcc-aarch64-linux-gnu g++-aarch64-linux-gnu libc-dev-arm64-cross # cmake-args: -D CMAKE_TOOLCHAIN_FILE=cmake/toolchain-aarch64.cmake - name: Ubuntu GCC AARCH64 (native) os: ubuntu-latest arch: arm64 compiler: gcc - name: Ubuntu Clang os: ubuntu-latest compiler: clang - name: Ubuntu Clang No AVX2 os: ubuntu-latest compiler: clang cmake-args: -D DEACTIVATE_AVX2=ON - name: Ubuntu Clang No AVX512 os: ubuntu-latest compiler: clang cmake-args: -D DEACTIVATE_AVX512=ON - name: Ubuntu Clang No ZLIB os: ubuntu-latest compiler: clang cmake-args: -D DEACTIVATE_ZLIB=ON - name: Ubuntu Clang No ZSTD os: ubuntu-latest compiler: clang cmake-args: -D DEACTIVATE_ZSTD=ON - name: Windows Clang Win64 os: windows-latest compiler: clang-cl cmake-args: -G Ninja - name: Windows MSVC Win64 os: windows-latest compiler: cl cmake-args: -A x64 - name: Windows GCC Ninja os: windows-latest compiler: gcc cmake-args: -G Ninja - name: Windows GCC MinGW os: windows-latest compiler: gcc cmake-args: -G "MinGW Makefiles" - name: macOS Clang os: macOS-latest compiler: clang - name: macOS GCC os: macOS-latest compiler: gcc steps: - uses: actions/checkout@v7 - name: Install packages (Ubuntu) if: runner.os == 'Linux' && matrix.packages run: | sudo apt-get update sudo apt-get install -y ${{ matrix.packages }} - name: Install packages (Windows) if: runner.os == 'Windows' run: choco install ninja ${{ matrix.packages }} - name: Install packages (macOS) if: runner.os == 'macOS' run: brew install ninja ${{ matrix.packages }} - name: Generate project files run: cmake -S ${{ matrix.build-src-dir || '.' }} -B ${{ matrix.build-dir || '.' }} ${{ matrix.cmake-args }} -D CMAKE_BUILD_TYPE=${{ matrix.build-config || 'Release' }} -D BUILD_SHARED_LIBS=OFF -D CMAKE_C_COMPILER=${{ matrix.compiler }} -D CMAKE_CXX_COMPILER=${{ matrix.compiler }} env: CC: ${{ matrix.compiler }} CFLAGS: ${{ matrix.cflags }} LDFLAGS: ${{ matrix.ldflags }} CI: true - name: Compile source code run: cmake --build ${{ matrix.build-dir || '.' }} --config ${{ matrix.build-config || 'Release' }} - name: Run test cases run: ctest -C Release --output-on-failure --max-width 120 working-directory: ${{ matrix.build-dir || '.' }} c-blosc2-3.1.5/.github/workflows/fuzz.yml000066400000000000000000000011461521743436100202520ustar00rootroot00000000000000name: CI Fuzz on: [push, pull_request] jobs: Fuzzing: runs-on: ubuntu-latest steps: - name: Build Fuzzers uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master with: oss-fuzz-project-name: 'c-blosc2' dry-run: false - name: Run Fuzzers uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master with: oss-fuzz-project-name: 'c-blosc2' fuzz-seconds: 600 dry-run: false - name: Upload Crash uses: actions/upload-artifact@v7 if: failure() with: name: artifacts path: ./out/artifacts c-blosc2-3.1.5/.gitignore000066400000000000000000000001161521743436100151200ustar00rootroot00000000000000bench/bench build* .idea .*.swp cmake-build-* /doc/doxygen/xml/ /doc/xml .vs/ c-blosc2-3.1.5/.guix-channel000066400000000000000000000000661521743436100155170ustar00rootroot00000000000000(channel (version 0) (directory ".guix/modules")) c-blosc2-3.1.5/.guix/000077500000000000000000000000001521743436100141645ustar00rootroot00000000000000c-blosc2-3.1.5/.guix/modules/000077500000000000000000000000001521743436100156345ustar00rootroot00000000000000c-blosc2-3.1.5/.guix/modules/c-blosc2-package.scm000066400000000000000000000102631521743436100213370ustar00rootroot00000000000000;;; This file follows the suggestions in the article "From development ;;; environments to continuous integration—the ultimate guide to software ;;; development with Guix" by Ludovic Courtès at the Guix blog: ;;; . (define-module (c-blosc2-package) #:use-module (guix) #:use-module (guix build-system cmake) #:use-module (guix git-download) #:use-module ((guix licenses) #:prefix license:) #:use-module (gnu packages compression) #:use-module (ice-9 regex) #:use-module (ice-9 textual-ports)) (define (current-source-root) (dirname (dirname (current-source-directory)))) (define (get-c-blosc2-version) (let ((version-path (string-append (current-source-root) "/include/blosc2.h")) (version-rx (make-regexp "^\\s*#\\s*define\\s*BLOSC2_VERSION_STRING\\s*\"([^\"]*)\".*" regexp/newline))) (call-with-input-file version-path (lambda (port) (let* ((version-body (get-string-all port)) (version-match (regexp-exec version-rx version-body))) (and version-match (match:substring version-match 1))))))) (define vcs-file? ;; Return true if the given file is under version control. (or (git-predicate (current-source-root)) (const #t))) (define-public c-blosc2 (package (name "c-blosc2") (version (get-c-blosc2-version)) (source (local-file "../.." "c-blosc2-checkout" #:recursive? #t #:select? (lambda (path stat) (and (vcs-file? path stat) (not (string-contains path "/internal-complibs")))))) (build-system cmake-build-system) (arguments ;; Disable AVX2 by default as in Guix' c-blosc package. `(#:configure-flags '("-DBUILD_STATIC=OFF" "-DDEACTIVATE_AVX2=ON" "-DDEACTIVATE_AVX512=ON" "-DPREFER_EXTERNAL_LZ4=ON" "-DPREFER_EXTERNAL_ZLIB=ON" "-DPREFER_EXTERNAL_ZSTD=ON"))) (inputs (list lz4 zlib ;; The only input with a separate libs-only output. `(,zstd "lib"))) (home-page "https://blosc.org") (synopsis "Blocking, shuffling and lossless compression library") (description "Blosc is a high performance compressor optimized for binary data (i.e. floating point numbers, integers and booleans, although it can handle string data too). It has been designed to transmit data to the processor cache faster than the traditional, non-compressed, direct memory fetch approach via a @code{memcpy()} system call. Blosc main goal is not just to reduce the size of large datasets on-disk or in-memory, but also to accelerate memory-bound computations. C-Blosc2 is the new major version of C-Blosc, and is backward compatible with both the C-Blosc1 API and its in-memory format. However, the reverse thing is generally not true for the format; buffers generated with C-Blosc2 are not format-compatible with C-Blosc1 (i.e. forward compatibility is not supported).") (license license:bsd-3))) (define (package-with-configure-flags p flags) "Return P with FLAGS as additional 'configure' flags." (package/inherit p (arguments (substitute-keyword-arguments (package-arguments p) ((#:configure-flags original-flags #~(list)) #~(append #$original-flags #$flags)))))) (define-public c-blosc2-with-avx2 (package (inherit (package-with-configure-flags c-blosc2 #~(list "-DDEACTIVATE_AVX2=OFF"))) (name "c-blosc2-with-avx2"))) (define-public c-blosc2-with-avx512 (package (inherit (package-with-configure-flags c-blosc2 #~(list "-DDEACTIVATE_AVX2=OFF" "-DDEACTIVATE_AVX512=OFF"))) (name "c-blosc2-with-avx512"))) c-blosc2 c-blosc2-3.1.5/.mailmap000066400000000000000000000004351521743436100145550ustar00rootroot00000000000000Francesc Alted FrancescAlted Francesc Alted FrancescAlted Francesc Alted FrancescAlted Francesc Alted FrancescAlted c-blosc2-3.1.5/.readthedocs.yaml000066400000000000000000000012171521743436100163620ustar00rootroot00000000000000# .readthedocs.yaml # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Set the version of Python and other tools you might need build: os: ubuntu-20.04 tools: python: "mambaforge-4.10" conda: environment: doc/environment.yml # Build documentation in the docs/ directory with Sphinx sphinx: configuration: doc/conf.py # If using Sphinx, optionally build your docs in additional formats such as PDF # formats: # - pdf # Optionally declare the Python requirements required to build your docs python: install: - requirements: doc/requirements.txt c-blosc2-3.1.5/ANNOUNCE.md000066400000000000000000000027701521743436100146700ustar00rootroot00000000000000# Announcing C-Blosc2 3.1.5 A fast, compressed, and persistent binary data store library for C. ## What is new? This is a maintenance release that fixes the decompression of all-zeros buffers whose length is not a multiple of ``typesize``. Such chunks were previously rejected even though all-zeros decompression is just a ``memset`` and works regardless of element alignment. This affected, for example, python-blosc2's ``compress2`` → ``decompress2`` round-trip for all-zeros payloads with a length not divisible by the type size. This release introduces no API/ABI changes. For more info, see the release notes in: https://github.com/Blosc/c-blosc2/blob/main/RELEASE_NOTES.md ## What is it? Blosc2 is a high-performance data container optimized for binary data. Blosc2 is the next generation of Blosc, an [award-winning library](https://www.blosc.org/posts/prize-push-Blosc2) that has been around for more than a decade. Blosc2 expands the capabilities of Blosc by providing a higher level container that is able to store many chunks on it (hence the super-block name). It supports storing data on both memory and disk using the same API. Also, it adds more compressors and filters. ## Download sources The github repository is over here: https://github.com/Blosc/c-blosc2 Blosc is distributed using the BSD license, see LICENSE.txt for details. ## Mailing list There is an official Blosc mailing list at: blosc@googlegroups.com https://groups.google.com/g/blosc Enjoy Data! - The Blosc Development Team c-blosc2-3.1.5/Blosc2Config.cmake.in000066400000000000000000000130201521743436100170070ustar00rootroot00000000000000# only add PUBLIC dependencies as well # https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html#creating-a-package-configuration-file include(CMakeFindDependencyMacro) # Search in _ROOT: # https://cmake.org/cmake/help/v3.12/policy/CMP0074.html if(POLICY CMP0074) cmake_policy(SET CMP0074 NEW) endif() # locate the installed FindABC.cmake modules list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/Modules") # this section stores which configuration options were set set(HAVE_THREADS @HAVE_THREADS@) set(HAVE_IPP @HAVE_IPP@) set(HAVE_LZ4_CONFIG @HAVE_LZ4_CONFIG@) set(HAVE_ZLIB_NG @HAVE_ZLIB_NG@) set(HAVE_ZLIB_NG_CONFIG @HAVE_ZLIB_NG_CONFIG@) set(HAVE_ZSTD_CONFIG @HAVE_ZSTD_CONFIG@) set(DEACTIVATE_IPP @DEACTIVATE_IPP@) set(DEACTIVATE_ZLIB @DEACTIVATE_ZLIB@) set(DEACTIVATE_ZSTD @DEACTIVATE_ZSTD@) set(PREFER_EXTERNAL_LZ4 @PREFER_EXTERNAL_LZ4@) set(PREFER_EXTERNAL_ZLIB @PREFER_EXTERNAL_ZLIB@) set(PREFER_EXTERNAL_ZSTD @PREFER_EXTERNAL_ZSTD@) set(BLOSC_DEPENDENCY_MODE "@BLOSC_DEPENDENCY_MODE@") set(HAVE_ZFP @HAVE_ZFP@) set(BLOSC_ZFP_PROVIDER "@BLOSC_ZFP_PROVIDER@") set(LZ4_FETCHED @LZ4_FETCHED@) set(ZLIB_NG_FETCHED @ZLIB_NG_FETCHED@) set(ZSTD_FETCHED @ZSTD_FETCHED@) # find dependencies and their targets, which are used in our Blosc2Targets.cmake # additionally, the Blosc2_..._FOUND variables are used to support # find_package(Blosc2 ... COMPONENTS ... ...) # this enables downstream projects to express the need for specific features. set(CMAKE_THREAD_PREFER_PTHREAD TRUE) # pre 3.1 set(THREADS_PREFER_PTHREAD_FLAG TRUE) # CMake 3.1+ if(HAVE_THREADS) find_dependency(Threads) set(Blosc2_THREADS_FOUND TRUE) else() set(Blosc2_THREADS_FOUND FALSE) endif() if(NOT DEACTIVATE_IPP AND HAVE_IPP) find_dependency(IPP) set(Blosc2_IPP_FOUND FALSE) else() set(Blosc2_IPP_FOUND TRUE) endif() if(NOT LZ4_FETCHED) if(HAVE_LZ4_CONFIG) find_dependency(lz4 CONFIG) else() find_dependency(LZ4 MODULE) endif() endif() set(Blosc2_LZ4_FOUND TRUE) if(DEACTIVATE_ZLIB) set(Blosc2_ZLIB_FOUND FALSE) elseif(ZLIB_NG_FETCHED) set(Blosc2_ZLIB_FOUND TRUE) else() if(HAVE_ZLIB_NG) if(HAVE_ZLIB_NG_CONFIG) find_dependency(zlib-ng CONFIG) else() find_dependency(ZLIB_NG MODULE) endif() else() find_dependency(ZLIB) endif() set(Blosc2_ZLIB_FOUND TRUE) endif() if(DEACTIVATE_ZSTD) set(Blosc2_ZSTD_FOUND FALSE) elseif(ZSTD_FETCHED) set(Blosc2_ZSTD_FOUND TRUE) else() if(HAVE_ZSTD_CONFIG) find_dependency(zstd CONFIG) else() find_dependency(ZSTD MODULE) endif() set(Blosc2_ZSTD_FOUND TRUE) endif() if(HAVE_ZFP) set(Blosc2_ZFP_FOUND TRUE) if(BLOSC_ZFP_PROVIDER STREQUAL "EXTERNAL") find_package(zfp CONFIG QUIET) if(NOT zfp_FOUND) find_package(ZFP CONFIG QUIET) endif() if(NOT zfp_FOUND AND NOT ZFP_FOUND) find_dependency(ZFP MODULE) endif() set(BLOSC_ZFP_TARGET "") foreach(BLOSC_ZFP_CANDIDATE zfp ZFP::zfp zfp::zfp ZFP::ZFP) if(TARGET ${BLOSC_ZFP_CANDIDATE} AND NOT BLOSC_ZFP_TARGET) set(BLOSC_ZFP_TARGET ${BLOSC_ZFP_CANDIDATE}) endif() endforeach() if(BLOSC_ZFP_TARGET AND NOT TARGET zfp) add_library(zfp INTERFACE IMPORTED) target_link_libraries(zfp INTERFACE ${BLOSC_ZFP_TARGET}) endif() endif() else() set(Blosc2_ZFP_FOUND FALSE) endif() # define central Blosc2::blosc2_shared/static targets include("${CMAKE_CURRENT_LIST_DIR}/Blosc2Targets.cmake") # check if components are fulfilled and set Blosc2__FOUND vars # Blosc2_FIND_COMPONENTS is a list set by find_package(... COMPONENTS ... ...) # likewise Blosc2_FIND_REQUIRED_... per component specified foreach(comp ${Blosc2_FIND_COMPONENTS}) if(NOT Blosc2_${comp}_FOUND) if(Blosc2_FIND_REQUIRED_${comp}) set(Blosc2_FOUND FALSE) endif() endif() endforeach() # Defines imported targets for Blosc2 inside a Python wheel # ------------------------------ # Shared library target # ------------------------------ if(NOT TARGET Blosc2::blosc2_shared) add_library(Blosc2::blosc2_shared SHARED IMPORTED GLOBAL) if(WIN32) # MSVC: import library (.lib) + runtime DLL (.dll) set_target_properties(Blosc2::blosc2_shared PROPERTIES IMPORTED_IMPLIB "${CMAKE_CURRENT_LIST_DIR}/../blosc2_shared.lib" IMPORTED_LOCATION "${CMAKE_CURRENT_LIST_DIR}/../blosc2_shared.dll" INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_LIST_DIR}/../../include" ) else() # Linux/macOS set_target_properties(Blosc2::blosc2_shared PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_LIST_DIR}/../blosc2_shared.so" INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_LIST_DIR}/../../include" ) endif() endif() # ------------------------------ # Static library target # ------------------------------ if(NOT TARGET Blosc2::blosc2_static) add_library(Blosc2::blosc2_static STATIC IMPORTED GLOBAL) if(MSVC) # Windows static library uses .lib set_target_properties(Blosc2::blosc2_static PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_LIST_DIR}/../blosc2_static.lib" INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_LIST_DIR}/../../include" ) else() # Linux/macOS static library uses .a set_target_properties(Blosc2::blosc2_static PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_LIST_DIR}/../blosc2_static.a" INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_LIST_DIR}/../../include" ) endif() endif() c-blosc2-3.1.5/CMakeLists.txt000066400000000000000000001103651521743436100157000ustar00rootroot00000000000000# Blosc - Blocked Shuffling and Compression Library # # Copyright (c) 2021 Blosc Development Team # https://blosc.org # License: BSD 3-Clause (see LICENSE.txt) # # See LICENSE.txt for details about copyright and rights to use. # CMake build system for Blosc # ============================ # # Available options: # # BUILD_STATIC: default ON # build the static version of the Blosc library # BUILD_SHARED: default ON # build the shared library version of the Blosc library # BUILD_TESTS: default ON # build test programs and generates the "test" target # BUILD_FUZZERS: default ON # build fuzz test programs and generates the "fuzz" target # BUILD_BENCHMARKS: default ON # build the benchmark programs # BUILD_EXAMPLES: default ON # build the examples programs # BUILD_PLUGINS: default ON # build the plugins programs # BUILD_LITE: default OFF # build a lite version (only with BloscLZ and LZ4/LZ4HC) of the blosc library # DEACTIVATE_AVX2: default OFF # do not attempt to build with AVX2 instructions # DEACTIVATE_AVX512: default OFF # do not attempt to build with AVX512 instructions # DEACTIVATE_ZLIB: default OFF # do not include support for the Zlib library # DEACTIVATE_ZSTD: default OFF # do not include support for the Zstd library # WITH_ZLIB_OPTIM: default ON # set WITH_OPTIM when building the fetched zlib-ng library; setting OFF is useful for wasm32 targets # PREFER_EXTERNAL_LZ4: default OFF # when found, use the installed LZ4 libs instead of fetching upstream sources # PREFER_EXTERNAL_ZLIB: default OFF # when found, use the installed Zlib/Zlib-ng libs instead of fetching upstream sources # PREFER_EXTERNAL_ZSTD: default OFF # when found, use the installed Zstd libs instead of fetching upstream sources # TEST_INCLUDE_BENCH_SHUFFLE_1: default ON # add a test that runs the benchmark program passing "shuffle" with 1 # thread as second parameter # TEST_INCLUDE_BENCH_SHUFFLE_N: default ON # add a test that runs the benchmark program passing "shuffle" with all # threads as second parameter # TEST_INCLUDE_BENCH_BITSHUFFLE_1: default ON # add a test that runs the benchmark program passing "bitshuffle" with 1 # thread as second parameter # TEST_INCLUDE_BENCH_BITSHUFFLE_N: default ON # add a test that runs the benchmark program passing "bitshuffle" with # all threads as second parameter # TEST_INCLUDE_BENCH_SUITE: default OFF # add a test that runs the benchmark program passing "suite" # as first parameter # TEST_INCLUDE_BENCH_HARDSUITE: default OFF # add a test that runs the benchmark program passing "hardsuite" # as first parameter # TEST_INCLUDE_BENCH_EXTREMESUITE: default OFF # add a test that runs the benchmark program passing "extremesuite" # as first parameter # TEST_INCLUDE_BENCH_DEBUGSUITE: default OFF # add a test that runs the benchmark program passing "debugsuite" # as first parameter # # Components: # # LIB: includes blosc2.so # DEV: static includes blosc2.a and blosc2.h if(NOT WIN32) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") endif() cmake_minimum_required(VERSION 3.16.3) if(${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} VERSION_GREATER 3.4) cmake_policy(SET CMP0063 NEW) endif() # The project can have ASM (zstd 1.5.2 is starting to use it) project(blosc LANGUAGES C ASM) # parse the full version numbers from blosc2.h file(READ include/blosc2.h _blosc2_h_contents) string(REGEX REPLACE ".*#define[ \t]+BLOSC2_VERSION_MAJOR[ \t]+([0-9]+).*" "\\1" BLOSC2_VERSION_MAJOR ${_blosc2_h_contents}) string(REGEX REPLACE ".*#define[ \t]+BLOSC2_VERSION_MINOR[ \t]+([0-9]+).*" "\\1" BLOSC2_VERSION_MINOR ${_blosc2_h_contents}) string(REGEX REPLACE ".*#define[ \t]+BLOSC2_VERSION_RELEASE[ \t]+([0-9]+).*" "\\1" BLOSC2_VERSION_PATCH ${_blosc2_h_contents}) string(REGEX REPLACE ".*#define[ \t]+BLOSC2_VERSION_STRING[ \t]+\"([-0-9A-Za-z.]+)\".*" "\\1" BLOSC2_VERSION_STRING ${_blosc2_h_contents}) message("Configuring for Blosc version: " ${BLOSC2_VERSION_STRING}) # options option(BUILD_STATIC "Build a static version of the blosc library." ON) option(BUILD_SHARED "Build a shared library version of the blosc library." ON) option(BUILD_TESTS "Build test programs from the blosc compression library" ON) option(BUILD_FUZZERS "Build fuzzer programs from the blosc compression library" ${BUILD_STATIC}) option(BUILD_BENCHMARKS "Build benchmark programs from the blosc compression library" ON) option(BUILD_EXAMPLES "Build example programs from the blosc compression library" ON) option(BUILD_PLUGINS "Build plugins programs from the blosc compression library" ON) option(BUILD_LITE "Build a lite version (only with BloscLZ and LZ4/LZ4HC) of the blosc library." OFF) option(DEACTIVATE_AVX2 "Do not attempt to build with AVX2 instructions" OFF) option(DEACTIVATE_AVX512 "Do not attempt to build with AVX512 instructions" OFF) option(DEACTIVATE_ZLIB "Do not include support for the Zlib library." OFF) option(DEACTIVATE_ZSTD "Do not include support for the Zstd library." OFF) option(PREFER_EXTERNAL_LZ4 "Find and use external LZ4 library instead of fetching sources." OFF) option(PREFER_EXTERNAL_ZLIB "Find and use external ZLIB library instead of fetching sources." OFF) option(PREFER_EXTERNAL_ZSTD "Find and use external ZSTD library instead of fetching sources." OFF) option(WITH_ZLIB_OPTIM "Set WITH_OPTIM for ZLIB, turning off is useful for compiling for wasm32 targets." ON) set(BLOSC_DEPENDENCY_MODE "BUNDLED" CACHE STRING "Dependency resolution mode: BUNDLED uses fetched dependencies, EXTERNAL requires system dependencies, AUTO tries system dependencies before fetching.") set_property(CACHE BLOSC_DEPENDENCY_MODE PROPERTY STRINGS BUNDLED EXTERNAL AUTO) set(BLOSC_ENABLE_ZFP "AUTO" CACHE STRING "ZFP codec support: AUTO enables it when available, ON requires it, OFF disables it.") set_property(CACHE BLOSC_ENABLE_ZFP PROPERTY STRINGS AUTO ON OFF) set(BLOSC_LZ4_VERSION "1.10.0" CACHE STRING "lz4 version to use when fetching internal sources.") set(BLOSC_LZ4_SOURCE_DIR "" CACHE PATH "Optional local lz4 checkout to use instead of downloading sources.") set(BLOSC_ZLIBNG_VERSION "2.3.3" CACHE STRING "zlib-ng version to use when fetching internal sources.") set(BLOSC_ZLIBNG_SOURCE_DIR "" CACHE PATH "Optional local zlib-ng checkout to use instead of downloading sources.") set(BLOSC_ZSTD_VERSION "1.5.7" CACHE STRING "zstd version to use when fetching internal sources.") set(BLOSC_ZSTD_SOURCE_DIR "" CACHE PATH "Optional local zstd checkout to use instead of downloading sources.") set(BLOSC_ZFP_VERSION "1.0.1" CACHE STRING "zfp version to use when fetching plugin sources.") set(BLOSC_ZFP_SOURCE_DIR "" CACHE PATH "Optional local zfp checkout to use instead of downloading plugin sources.") set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") include(GNUInstallDirs) if(MINGW) # This gets us DLL resource information when compiling on MinGW. if(NOT CMAKE_RC_COMPILER) set(CMAKE_RC_COMPILER windres.exe) endif() endif() if(ENABLE_ASAN) message(STATUS "Enabling ASAN") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -Os -fno-omit-frame-pointer -fsanitize=address") endif() if(BUILD_PLUGINS AND BUILD_LITE) message(FATAL_ERROR "Both plugins and lite Blosc versions can not be built at the same time.") endif() if(BUILD_LITE) set(DEACTIVATE_ZLIB ON) set(DEACTIVATE_ZSTD ON) endif() string(TOUPPER "${BLOSC_DEPENDENCY_MODE}" BLOSC_DEPENDENCY_MODE) set(BLOSC_DEPENDENCY_MODE "${BLOSC_DEPENDENCY_MODE}" CACHE STRING "Dependency resolution mode: BUNDLED uses fetched dependencies, EXTERNAL requires system dependencies, AUTO tries system dependencies before fetching." FORCE) if(NOT BLOSC_DEPENDENCY_MODE STREQUAL "BUNDLED" AND NOT BLOSC_DEPENDENCY_MODE STREQUAL "EXTERNAL" AND NOT BLOSC_DEPENDENCY_MODE STREQUAL "AUTO") message(FATAL_ERROR "BLOSC_DEPENDENCY_MODE must be one of BUNDLED, EXTERNAL, or AUTO") endif() string(TOUPPER "${BLOSC_ENABLE_ZFP}" BLOSC_ENABLE_ZFP) set(BLOSC_ENABLE_ZFP "${BLOSC_ENABLE_ZFP}" CACHE STRING "ZFP codec support: AUTO enables it when available, ON requires it, OFF disables it." FORCE) if(NOT BLOSC_ENABLE_ZFP STREQUAL "AUTO" AND NOT BLOSC_ENABLE_ZFP STREQUAL "ON" AND NOT BLOSC_ENABLE_ZFP STREQUAL "OFF") message(FATAL_ERROR "BLOSC_ENABLE_ZFP must be one of AUTO, ON, or OFF") endif() # Threads set(CMAKE_THREAD_PREFER_PTHREAD TRUE) # pre 3.1 set(THREADS_PREFER_PTHREAD_FLAG TRUE) # CMake 3.1+ if(WIN32) # try to use the system library find_package(Threads) else() find_package(Threads REQUIRED) endif() if(Threads_FOUND) set(HAVE_THREADS ON) else() set(HAVE_THREADS OFF) endif() set(BLOSC_FIND_EXTERNAL_LZ4 FALSE) set(BLOSC_REQUIRE_EXTERNAL_LZ4 FALSE) if(BLOSC_DEPENDENCY_MODE STREQUAL "EXTERNAL") set(BLOSC_FIND_EXTERNAL_LZ4 TRUE) set(BLOSC_REQUIRE_EXTERNAL_LZ4 TRUE) elseif(BLOSC_DEPENDENCY_MODE STREQUAL "AUTO" OR PREFER_EXTERNAL_LZ4) set(BLOSC_FIND_EXTERNAL_LZ4 TRUE) endif() if(BLOSC_FIND_EXTERNAL_LZ4) find_package(LZ4 CONFIG NAMES lz4) if(LZ4_FOUND) set(HAVE_LZ4_CONFIG TRUE) else() find_package(LZ4 MODULE) endif() endif() if(NOT LZ4_FOUND AND BLOSC_REQUIRE_EXTERNAL_LZ4) message(FATAL_ERROR "BLOSC_DEPENDENCY_MODE=EXTERNAL requires an external LZ4 package. Install LZ4 or use BLOSC_DEPENDENCY_MODE=BUNDLED/AUTO.") endif() if(NOT LZ4_FOUND) include(FetchContent) set(LZ4_BUILD_CLI OFF CACHE BOOL "" FORCE) set(LZ4_BUILD_LEGACY_LZ4C OFF CACHE BOOL "" FORCE) set(LZ4_BUNDLED_MODE ON CACHE BOOL "" FORCE) set(LZ4_POSITION_INDEPENDENT_LIB ON CACHE BOOL "" FORCE) if(BLOSC_LZ4_SOURCE_DIR) message(STATUS "Using lz4 via FetchContent from local checkout: ${BLOSC_LZ4_SOURCE_DIR}") FetchContent_Declare( lz4 SOURCE_DIR "${BLOSC_LZ4_SOURCE_DIR}" SOURCE_SUBDIR build/cmake) else() message(STATUS "Using lz4 ${BLOSC_LZ4_VERSION} via FetchContent download") FetchContent_Declare( lz4 URL "https://github.com/lz4/lz4/archive/refs/tags/v${BLOSC_LZ4_VERSION}.tar.gz" DOWNLOAD_EXTRACT_TIMESTAMP TRUE SOURCE_SUBDIR build/cmake) endif() FetchContent_GetProperties(lz4) if(NOT lz4_POPULATED) if(POLICY CMP0169) cmake_policy(PUSH) cmake_policy(SET CMP0169 OLD) endif() FetchContent_Populate(lz4) if(POLICY CMP0169) cmake_policy(POP) endif() add_subdirectory("${lz4_SOURCE_DIR}/build/cmake" "${lz4_BINARY_DIR}" EXCLUDE_FROM_ALL) endif() set(LZ4_FETCHED TRUE) set(LZ4_FOUND TRUE) set(LZ4_SOURCE_DIR "${lz4_SOURCE_DIR}") set(LZ4_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}/blosc2/thirdparty/lz4") if(TARGET lz4_static) set_target_properties(lz4_static PROPERTIES POSITION_INDEPENDENT_CODE ON) set_property(TARGET lz4_static PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$" "$") endif() endif() if(NOT DEACTIVATE_ZLIB) set(BLOSC_FIND_EXTERNAL_ZLIB FALSE) set(BLOSC_REQUIRE_EXTERNAL_ZLIB FALSE) if(BLOSC_DEPENDENCY_MODE STREQUAL "EXTERNAL") set(BLOSC_FIND_EXTERNAL_ZLIB TRUE) set(BLOSC_REQUIRE_EXTERNAL_ZLIB TRUE) elseif(BLOSC_DEPENDENCY_MODE STREQUAL "AUTO" OR PREFER_EXTERNAL_ZLIB) set(BLOSC_FIND_EXTERNAL_ZLIB TRUE) endif() if(BLOSC_FIND_EXTERNAL_ZLIB) find_package(ZLIB_NG CONFIG NAMES zlib-ng) if(ZLIB_NG_FOUND) set(HAVE_ZLIB_NG TRUE) set(HAVE_ZLIB_NG_CONFIG TRUE) else() find_package(ZLIB_NG MODULE) if(ZLIB_NG_FOUND) set(HAVE_ZLIB_NG TRUE) else() find_package(ZLIB) endif() endif() endif() if(NOT (ZLIB_NG_FOUND OR ZLIB_FOUND) AND BLOSC_REQUIRE_EXTERNAL_ZLIB) message(FATAL_ERROR "BLOSC_DEPENDENCY_MODE=EXTERNAL requires an external zlib-ng or zlib package. Install one or use BLOSC_DEPENDENCY_MODE=BUNDLED/AUTO.") endif() if(NOT (ZLIB_NG_FOUND OR ZLIB_FOUND)) include(FetchContent) if (NOT WITH_ZLIB_OPTIM) set(WITH_OPTIM OFF CACHE BOOL "Disable zlib-ng architecture optimizations when fetched via FetchContent" FORCE) set(WITH_RUNTIME_CPU_DETECTION OFF CACHE BOOL "Disable zlib-ng runtime CPU detection when fetched via FetchContent" FORCE) endif() set(SKIP_INSTALL_ALL ON CACHE BOOL "Skip zlib-ng install targets when fetched via FetchContent" FORCE) set(BUILD_TESTING OFF CACHE BOOL "Disable zlib-ng tests when fetched via FetchContent" FORCE) set(WITH_GTEST OFF CACHE BOOL "Disable zlib-ng GTest when fetched via FetchContent" FORCE) set(WITH_FUZZERS OFF CACHE BOOL "Disable zlib-ng fuzzers when fetched via FetchContent" FORCE) set(WITH_BENCHMARKS OFF CACHE BOOL "Disable zlib-ng benchmarks when fetched via FetchContent" FORCE) set(WITH_BENCHMARK_APPS OFF CACHE BOOL "Disable zlib-ng benchmark apps when fetched via FetchContent" FORCE) if(BLOSC_ZLIBNG_SOURCE_DIR) message(STATUS "Using zlib-ng via FetchContent from local checkout: ${BLOSC_ZLIBNG_SOURCE_DIR}") FetchContent_Declare( zlib_ng SOURCE_DIR "${BLOSC_ZLIBNG_SOURCE_DIR}") else() message(STATUS "Using zlib-ng ${BLOSC_ZLIBNG_VERSION} via FetchContent download") FetchContent_Declare( zlib_ng URL "https://github.com/zlib-ng/zlib-ng/archive/refs/tags/${BLOSC_ZLIBNG_VERSION}.tar.gz" DOWNLOAD_EXTRACT_TIMESTAMP TRUE) endif() FetchContent_MakeAvailable(zlib_ng) set(HAVE_ZLIB_NG TRUE) set(ZLIB_NG_FETCHED TRUE) set(ZLIB_NG_FOUND TRUE) set(ZLIB_NG_BUILD_DIR "${zlib_ng_BINARY_DIR}") set(ZLIB_NG_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}/blosc2/thirdparty/zlib-ng") set(ZLIB_NG_INSTALL_TARGET "") if(TARGET zlib-ng-static) get_target_property(ZLIB_NG_INSTALL_TARGET zlib-ng-static ALIASED_TARGET) if(NOT ZLIB_NG_INSTALL_TARGET) set(ZLIB_NG_INSTALL_TARGET zlib-ng-static) endif() elseif(TARGET zlib-ng) get_target_property(ZLIB_NG_INSTALL_TARGET zlib-ng ALIASED_TARGET) if(NOT ZLIB_NG_INSTALL_TARGET) set(ZLIB_NG_INSTALL_TARGET zlib-ng) endif() endif() if(ZLIB_NG_INSTALL_TARGET) set_target_properties(${ZLIB_NG_INSTALL_TARGET} PROPERTIES POSITION_INDEPENDENT_CODE ON) set_property(TARGET ${ZLIB_NG_INSTALL_TARGET} PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$" "$" "$") endif() endif() set(HAVE_ZLIB TRUE) endif() if(NOT DEACTIVATE_ZSTD) set(BLOSC_FIND_EXTERNAL_ZSTD FALSE) set(BLOSC_REQUIRE_EXTERNAL_ZSTD FALSE) if(BLOSC_DEPENDENCY_MODE STREQUAL "EXTERNAL") set(BLOSC_FIND_EXTERNAL_ZSTD TRUE) set(BLOSC_REQUIRE_EXTERNAL_ZSTD TRUE) elseif(BLOSC_DEPENDENCY_MODE STREQUAL "AUTO" OR PREFER_EXTERNAL_ZSTD) set(BLOSC_FIND_EXTERNAL_ZSTD TRUE) endif() if(BLOSC_FIND_EXTERNAL_ZSTD) find_package(ZSTD CONFIG NAMES zstd) if(ZSTD_FOUND) set(HAVE_ZSTD_CONFIG TRUE) else() find_package(ZSTD MODULE) endif() endif() if(NOT ZSTD_FOUND AND BLOSC_REQUIRE_EXTERNAL_ZSTD) message(FATAL_ERROR "BLOSC_DEPENDENCY_MODE=EXTERNAL requires an external Zstd package. Install Zstd or use BLOSC_DEPENDENCY_MODE=BUNDLED/AUTO.") endif() if(NOT ZSTD_FOUND) include(FetchContent) set(ZSTD_BUILD_PROGRAMS OFF CACHE BOOL "Disable zstd programs when fetched via FetchContent" FORCE) set(ZSTD_BUILD_SHARED OFF CACHE BOOL "Disable shared zstd library when fetched via FetchContent" FORCE) set(ZSTD_BUILD_STATIC ON CACHE BOOL "Enable static zstd library when fetched via FetchContent" FORCE) set(ZSTD_BUILD_TESTS OFF CACHE BOOL "Disable zstd tests when fetched via FetchContent" FORCE) set(ZSTD_BUILD_CONTRIB OFF CACHE BOOL "Disable zstd contrib targets when fetched via FetchContent" FORCE) set(ZSTD_MULTITHREAD_SUPPORT OFF CACHE BOOL "Disable zstd multithread support when fetched via FetchContent" FORCE) if(BLOSC_ZSTD_SOURCE_DIR) message(STATUS "Using zstd via FetchContent from local checkout: ${BLOSC_ZSTD_SOURCE_DIR}") FetchContent_Declare( zstd SOURCE_DIR "${BLOSC_ZSTD_SOURCE_DIR}" SOURCE_SUBDIR build/cmake) else() message(STATUS "Using zstd ${BLOSC_ZSTD_VERSION} via FetchContent download") FetchContent_Declare( zstd URL "https://github.com/facebook/zstd/archive/refs/tags/v${BLOSC_ZSTD_VERSION}.tar.gz" DOWNLOAD_EXTRACT_TIMESTAMP TRUE SOURCE_SUBDIR build/cmake) endif() FetchContent_GetProperties(zstd) if(NOT zstd_POPULATED) if(POLICY CMP0169) cmake_policy(PUSH) cmake_policy(SET CMP0169 OLD) endif() FetchContent_Populate(zstd) if(POLICY CMP0169) cmake_policy(POP) endif() add_subdirectory("${zstd_SOURCE_DIR}/build/cmake" "${zstd_BINARY_DIR}" EXCLUDE_FROM_ALL) endif() set(HAVE_ZSTD TRUE) set(ZSTD_FETCHED TRUE) set(ZSTD_FOUND TRUE) set(ZSTD_BUILD_DIR "${zstd_BINARY_DIR}") set(ZSTD_SOURCE_DIR "${zstd_SOURCE_DIR}") set(ZSTD_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}/blosc2/thirdparty/zstd") if(TARGET libzstd_static) set_target_properties(libzstd_static PROPERTIES POSITION_INDEPENDENT_CODE ON) set_property(TARGET libzstd_static PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$" "$") # Silence GCC false positives in fetched legacy zstd sources on some Linux/amd64 builds. target_compile_options(libzstd_static PRIVATE $<$:-Wno-maybe-uninitialized>) endif() else() set(HAVE_ZSTD TRUE) endif() endif() set(HAVE_ZFP FALSE) set(BLOSC_ZFP_PROVIDER "NONE") set(BLOSC_ZFP_TARGET "") set(ZFP_FOUND FALSE) if(BUILD_PLUGINS AND NOT BLOSC_ENABLE_ZFP STREQUAL "OFF") set(BLOSC_FIND_EXTERNAL_ZFP FALSE) set(BLOSC_ALLOW_BUNDLED_ZFP FALSE) if(BLOSC_DEPENDENCY_MODE STREQUAL "EXTERNAL") set(BLOSC_FIND_EXTERNAL_ZFP TRUE) elseif(BLOSC_DEPENDENCY_MODE STREQUAL "AUTO") set(BLOSC_FIND_EXTERNAL_ZFP TRUE) set(BLOSC_ALLOW_BUNDLED_ZFP TRUE) else() set(BLOSC_ALLOW_BUNDLED_ZFP TRUE) endif() if(BLOSC_ENABLE_ZFP STREQUAL "ON" AND NOT BLOSC_DEPENDENCY_MODE STREQUAL "EXTERNAL") set(BLOSC_ALLOW_BUNDLED_ZFP TRUE) endif() if(BLOSC_FIND_EXTERNAL_ZFP) find_package(zfp CONFIG QUIET) if(NOT zfp_FOUND) find_package(ZFP CONFIG QUIET) endif() if(NOT zfp_FOUND AND NOT ZFP_FOUND) find_package(ZFP MODULE QUIET) endif() if(zfp_FOUND OR ZFP_FOUND) set(ZFP_FOUND TRUE) set(BLOSC_ZFP_PROVIDER "EXTERNAL") foreach(BLOSC_ZFP_CANDIDATE zfp ZFP::zfp zfp::zfp ZFP::ZFP) if(TARGET ${BLOSC_ZFP_CANDIDATE} AND NOT BLOSC_ZFP_TARGET) set(BLOSC_ZFP_TARGET ${BLOSC_ZFP_CANDIDATE}) endif() endforeach() if(BLOSC_ZFP_TARGET AND NOT TARGET zfp) add_library(zfp INTERFACE IMPORTED) target_link_libraries(zfp INTERFACE ${BLOSC_ZFP_TARGET}) endif() endif() endif() if(NOT ZFP_FOUND AND BLOSC_ALLOW_BUNDLED_ZFP) set(BLOSC_ZFP_PROVIDER "BUNDLED") endif() if(BLOSC_ZFP_PROVIDER STREQUAL "NONE") if(BLOSC_ENABLE_ZFP STREQUAL "ON") message(FATAL_ERROR "BLOSC_ENABLE_ZFP=ON requires ZFP support, but no external ZFP package was found and bundled ZFP is disabled by BLOSC_DEPENDENCY_MODE=EXTERNAL.") else() message(STATUS "ZFP codec support disabled: no external ZFP package found in BLOSC_DEPENDENCY_MODE=EXTERNAL") endif() else() set(HAVE_ZFP TRUE) message(STATUS "ZFP codec support enabled (${BLOSC_ZFP_PROVIDER})") endif() endif() if(BUILD_PLUGINS) set(HAVE_PLUGINS TRUE) endif() # create the config.h file configure_file("${PROJECT_SOURCE_DIR}/blosc/config.h.in" "${PROJECT_BINARY_DIR}/blosc/config.h") # now make sure that you set the build directory on your "Include" path when compiling include_directories("${PROJECT_BINARY_DIR}/blosc/") # If the build type is not set, default to Release. set(BLOSC_DEFAULT_BUILD_TYPE Release) if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "No build type specified. Defaulting to '${BLOSC_DEFAULT_BUILD_TYPE}'.") set(CMAKE_BUILD_TYPE ${BLOSC_DEFAULT_BUILD_TYPE} CACHE STRING "Choose the type of build." FORCE) # Set the possible values of build type for cmake-gui set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() # Propagate CMAKE_OSX_ARCHITECTURES env variable into CMAKE_SYSTEM_PROCESSOR if(DEFINED ENV{CMAKE_OSX_ARCHITECTURES}) if("$ENV{CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64") set(CMAKE_SYSTEM_PROCESSOR arm64) endif() endif() # Based on the target system's processor and the compiler being used, # set build variables indicating which hardware features can be targeted # by the compiler. Note we DO NOT check which hardware features are supported # by this (the host) system, because we want to be able to support compiling # for newer hardware on older machines as well as cross-compilation. message(STATUS "Building for system processor ${CMAKE_SYSTEM_PROCESSOR}") message(STATUS "Building for compiler ID ${CMAKE_C_COMPILER_ID}") if(CMAKE_SYSTEM_PROCESSOR MATCHES i386|i686|x86_64|amd64|AMD64) if(CMAKE_C_COMPILER_ID MATCHES GNU) # We need C99 (GNU99 more exactly) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") set(COMPILER_SUPPORT_SSE2 TRUE) if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 4.7) set(COMPILER_SUPPORT_AVX2 TRUE) else() set(COMPILER_SUPPORT_AVX2 FALSE) endif() # GCC 10.3.2 (the version in manylinux_2014) seems to have issues supporting dynamic dispatching # of AVX512. GCC 11.4 is the first minimal version that works well here. # That means that Linux wheels will have AVX512 disabled, but that's life. if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 11.4) set(COMPILER_SUPPORT_AVX512 TRUE) else() set(COMPILER_SUPPORT_AVX512 FALSE) endif() elseif(CMAKE_C_COMPILER_ID MATCHES Clang|AppleClang) set(COMPILER_SUPPORT_SSE2 TRUE) if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.2) set(COMPILER_SUPPORT_AVX2 TRUE) else() set(COMPILER_SUPPORT_AVX2 FALSE) endif() # Clang 13 is the minimum version that we know that works with AVX512 dynamic dispatch. # Perhaps lesser versions work too, better to err on the safe side. if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 13.0) set(COMPILER_SUPPORT_AVX512 TRUE) else() set(COMPILER_SUPPORT_AVX512 FALSE) endif() elseif(CMAKE_C_COMPILER_ID MATCHES Intel|IntelLLVM) # All Intel compilers since the introduction of AVX512 in 2016 should support it, so activate all SIMD flavors set(COMPILER_SUPPORT_SSE2 TRUE) set(COMPILER_SUPPORT_AVX2 TRUE) set(COMPILER_SUPPORT_AVX512 TRUE) elseif(MSVC) set(COMPILER_SUPPORT_SSE2 TRUE) if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 18.00.30501) set(COMPILER_SUPPORT_AVX2 TRUE) else() set(COMPILER_SUPPORT_AVX2 FALSE) endif() # AVX512 starts to be supported since Visual Studio 17 15.0 if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 19.10.25017) set(COMPILER_SUPPORT_AVX512 TRUE) else() set(COMPILER_SUPPORT_AVX512 FALSE) endif() else() set(COMPILER_SUPPORT_SSE2 FALSE) set(COMPILER_SUPPORT_AVX2 FALSE) set(COMPILER_SUPPORT_AVX512 FALSE) # Unrecognized compiler. Emit a warning message to let the user know hardware-acceleration won't be available. message(WARNING "Unable to determine which ${CMAKE_SYSTEM_PROCESSOR} hardware features are supported by the C compiler (${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION}).") endif() elseif(CMAKE_SYSTEM_PROCESSOR MATCHES armv7l|aarch64|arm64) if(CMAKE_C_COMPILER_ID MATCHES GNU) if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 5.2) set(COMPILER_SUPPORT_NEON TRUE) else() set(COMPILER_SUPPORT_NEON FALSE) endif() elseif(CMAKE_C_COMPILER_ID MATCHES Clang|AppleClang) if(CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 3.3) set(COMPILER_SUPPORT_NEON TRUE) else() set(COMPILER_SUPPORT_NEON FALSE) endif() else() set(COMPILER_SUPPORT_NEON FALSE) # Unrecognized compiler. Emit a warning message to let the user know hardware-acceleration won't be available. message(WARNING "Unable to determine which ${CMAKE_SYSTEM_PROCESSOR} hardware features are supported by the C compiler (${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION}).") endif() elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(riscv32|riscv64|riscv)") set(COMPILER_SUPPORT_SSE2 FALSE) set(COMPILER_SUPPORT_AVX2 FALSE) set(COMPILER_SUPPORT_AVX512 FALSE) set(COMPILER_SUPPORT_NEON FALSE) set(COMPILER_SUPPORT_ALTIVEC FALSE) message(STATUS "Detected RISC-V target; hardware-accelerated shuffle implementations are not available, so the generic path will be used.") elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(powerpc|ppc)") if(CMAKE_C_COMPILER_ID MATCHES GNU AND CMAKE_C_COMPILER_VERSION VERSION_GREATER 8) set(COMPILER_SUPPORT_ALTIVEC TRUE) elseif(CMAKE_C_COMPILER_ID MATCHES Clang AND CMAKE_C_COMPILER_VERSION VERSION_GREATER 13) set(COMPILER_SUPPORT_ALTIVEC TRUE) else() set(COMPILER_SUPPORT_ALTIVEC FALSE) endif() else() # If the target system processor isn't recognized, emit a warning message to alert the user # that hardware-acceleration support won't be available but allow configuration to proceed. message(WARNING "Unrecognized system processor ${CMAKE_SYSTEM_PROCESSOR}. Cannot determine which hardware features (${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION}) supports, so hardware-accelerated implementations will not be available.") endif() # disable AVX2 if specified if(DEACTIVATE_AVX2) set(COMPILER_SUPPORT_AVX2 FALSE) # AVX512 functions in bitshuffle depend on AVX2 too set(COMPILER_SUPPORT_AVX512 FALSE) endif() # disable AVX512 if specified if(DEACTIVATE_AVX512) set(COMPILER_SUPPORT_AVX512 FALSE) endif() # flags # @TODO: set -Wall # @NOTE: -O3 is enabled in Release mode (CMAKE_BUILD_TYPE="Release") # Set the "-msse2" build flag only if the CMAKE_C_FLAGS is not already set. # Probably "-msse2" should be appended to CMAKE_C_FLAGS_RELEASE. if(CMAKE_C_COMPILER_ID MATCHES GNU|Clang|Intel|IntelLLVM) if(NOT CMAKE_C_FLAGS AND COMPILER_SUPPORT_SSE2) set(CMAKE_C_FLAGS -msse2 CACHE STRING "C flags." FORCE) endif() endif() if(CMAKE_C_COMPILER_ID MATCHES Intel|IntelLLVM|Clang OR HAIKU) # We need to tell Intel and Clang compilers about what level of POSIX they support SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_XOPEN_SOURCE=600") endif() if(MSVC) if(NOT CMAKE_C_FLAGS) set(CMAKE_C_FLAGS "/Ox" CACHE STRING "C flags." FORCE) endif() # Turn off misguided "secure CRT" warnings in MSVC. # Microsoft wants people to use the MS-specific _s # versions of certain C functions but this is difficult to do # in platform-independent code. add_definitions(-D_CRT_SECURE_NO_WARNINGS) else() add_compile_options(-Wall -Wextra) endif() if(WIN32) # For some supporting headers include_directories("${CMAKE_CURRENT_SOURCE_DIR}/blosc") endif() if(NOT DEFINED BLOSC_IS_SUBPROJECT) if("^${CMAKE_SOURCE_DIR}$" STREQUAL "^${PROJECT_SOURCE_DIR}$") set(BLOSC_IS_SUBPROJECT FALSE) else() set(BLOSC_IS_SUBPROJECT TRUE) message(STATUS "Detected that BLOSC is used a subproject.") endif() endif() if(NOT DEFINED BLOSC_INSTALL) if(BLOSC_IS_SUBPROJECT) set(BLOSC_INSTALL FALSE) else() set(BLOSC_INSTALL TRUE) endif() endif() # include directories include_directories(include) # subdirectories set(SOURCES) if(BUILD_PLUGINS) enable_testing() add_subdirectory(plugins) if(DEFINED ZFP_SOURCE_DIR) include_directories(${ZFP_SOURCE_DIR}/include) endif() endif() # Determine which linkage the unified target will use if(BUILD_SHARED AND NOT BUILD_STATIC) if(NOT BUILD_SHARED_LIBS) message(WARNING "BUILD_SHARED_LIBS is OFF, but BUILD_SHARED is ON and BUILD_STATIC is OFF, which takes precedence, so Blosc2::blosc2 is a shared library") endif() set(UNIFIED_TARGET_ALIAS blosc2_shared) elseif(NOT BUILD_SHARED AND BUILD_STATIC) if(BUILD_SHARED_LIBS) message(WARNING "BUILD_SHARED_LIBS is ON, but BUILD_SHARED is OFF and BUILD_STATIC is ON, which takes precedence, so Blosc2::blosc2 is a static library") endif() set(UNIFIED_TARGET_ALIAS blosc2_static) else() # If both BUILD_SHARED and BUILD_STATIC are set, which is the default, # fallback to using BUILD_SHARED_LIBS to determine whether to set # Blosc2::blosc2 to static or shared. if(BUILD_SHARED_LIBS) set(UNIFIED_TARGET_ALIAS blosc2_shared) else() set(UNIFIED_TARGET_ALIAS blosc2_static) endif() endif() add_subdirectory(blosc) if(BUILD_TESTS) enable_testing() add_subdirectory(tests) add_subdirectory(compat) endif() if(BUILD_FUZZERS) if(NOT BUILD_STATIC) message(FATAL_ERROR "BUILD_FUZZERS requires BUILD_STATIC to be enabled.") endif() enable_testing() add_subdirectory(tests/fuzz) endif() if(BUILD_BENCHMARKS) add_subdirectory(bench) endif() if(BUILD_EXAMPLES) add_subdirectory(examples) endif() # collecting SOURCES is now complete if(BUILD_SHARED) target_sources(blosc2_shared PRIVATE ${SOURCES}) endif() if(BUILD_STATIC) target_sources(blosc2_static PRIVATE ${SOURCES}) endif() if(BUILD_TESTS) target_sources(blosc_testing PRIVATE ${SOURCES}) endif() # install targets if(BLOSC_INSTALL) include(GNUInstallDirs) # C++ files install(FILES ${PROJECT_SOURCE_DIR}/include/blosc2.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT DEV) install(FILES ${PROJECT_SOURCE_DIR}/include/b2nd.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT DEV) install(FILES ${PROJECT_SOURCE_DIR}/include/blosc2/blosc2-export.h ${PROJECT_SOURCE_DIR}/include/blosc2/blosc2-common.h ${PROJECT_SOURCE_DIR}/include/blosc2/blosc2-stdio.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/blosc2 COMPONENT DEV) if(BUILD_PLUGINS) install(FILES ${PROJECT_SOURCE_DIR}/include/blosc2/filters-registry.h ${PROJECT_SOURCE_DIR}/include/blosc2/codecs-registry.h ${PROJECT_SOURCE_DIR}/include/blosc2/tuners-registry.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/blosc2 COMPONENT DEV) endif() if(BUILD_SHARED) install(TARGETS blosc2_shared LIBRARY COMPONENT LIB ARCHIVE COMPONENT DEV RUNTIME COMPONENT LIB) endif() if(BUILD_STATIC) install(TARGETS blosc2_static COMPONENT DEV) endif() # Bundled dependency headers are private implementation details and are not # installed. Bundled dependency objects are folded into libblosc2.a for # static builds and into libblosc2 shared libraries by the regular linker. # config files include(CMakePackageConfigHelpers) # we need a general location for Unix and Windows to install our # Blosc2Config.cmake files to. This is defined in CMake: # https://cmake.org/cmake/help/latest/command/find_package.html#config-mode-search-procedure if(NOT Blosc2_INSTALL_CMAKEDIR) if(WIN32 AND NOT MINGW) set(Blosc2_INSTALL_CMAKEDIR "cmake") else() set(Blosc2_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/Blosc2") endif() endif() # CMake config file # This stores our targets and find and populates the targets we depend on, # including third party interface libraries that we added. set(Blosc2_INSTALL_TARGET_NAMES) set(Blosc2_INSTALL_THIRDPARTY_TARGET_NAMES) if(BUILD_SHARED) list(APPEND Blosc2_INSTALL_TARGET_NAMES blosc2_shared) endif() if(BUILD_STATIC) list(APPEND Blosc2_INSTALL_TARGET_NAMES blosc2_static) endif() # Add unified target add_library(blosc2 INTERFACE) list(APPEND Blosc2_INSTALL_TARGET_NAMES blosc2) target_link_libraries(blosc2 INTERFACE ${UNIFIED_TARGET_ALIAS}) configure_file( ${PROJECT_SOURCE_DIR}/Blosc2Config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/Blosc2Config.cmake @ONLY ) install(TARGETS ${Blosc2_INSTALL_TARGET_NAMES} EXPORT Blosc2Targets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) if(Blosc2_INSTALL_THIRDPARTY_TARGET_NAMES) install(TARGETS ${Blosc2_INSTALL_THIRDPARTY_TARGET_NAMES} EXPORT Blosc2Targets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/blosc2 ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/blosc2 RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ) endif() install(EXPORT Blosc2Targets FILE Blosc2Targets.cmake NAMESPACE Blosc2:: DESTINATION ${Blosc2_INSTALL_CMAKEDIR} ) write_basic_package_version_file("Blosc2ConfigVersion.cmake" VERSION ${BLOSC2_VERSION_STRING} COMPATIBILITY SameMajorVersion ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/Blosc2Config.cmake ${CMAKE_CURRENT_BINARY_DIR}/Blosc2ConfigVersion.cmake DESTINATION ${Blosc2_INSTALL_CMAKEDIR} ) # CMake Find*.cmake files used in Blosc2Config.cmake install( DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/cmake/ DESTINATION ${Blosc2_INSTALL_CMAKEDIR}/Modules ) # pkg-config .pc file configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/blosc2.pc.in" "${CMAKE_CURRENT_BINARY_DIR}/blosc2.pc" @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/blosc2.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" COMPONENT DEV) # uninstaller if(NOT TARGET uninstall) configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" @ONLY) add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) endif() endif() # packaging if(NOT BLOSC_IS_SUBPROJECT) include(InstallRequiredSystemLibraries) set(CPACK_GENERATOR TGZ ZIP) set(CPACK_SOURCE_GENERATOR TGZ ZIP) set(CPACK_PACKAGE_VERSION_MAJOR ${BLOSC2_VERSION_MAJOR}) set(CPACK_PACKAGE_VERSION_MINOR ${BLOSC2_VERSION_MINOR}) set(CPACK_PACKAGE_VERSION_PATCH ${BLOSC2_VERSION_PATCH}) set(CPACK_PACKAGE_VERSION ${BLOSC_STRING_VERSION}) set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/README.rst") set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A blocking, shuffling and lossless compression library") set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE.txt") set(CPACK_SOURCE_IGNORE_FILES "/build.*;.*~;\\\\.git.*;\\\\.DS_Store") set(CPACK_STRIP_FILES TRUE) set(CPACK_SOURCE_STRIP_FILES TRUE) include(CPack) endif() c-blosc2-3.1.5/COMPILING_WITH_WHEELS.rst000066400000000000000000000122711521743436100171120ustar00rootroot00000000000000============================================================================= C-Blosc2 libraries come with Python-Blosc2 wheels ============================================================================= C-Blosc2 binary libraries (including plugins functionality) can easily be installed from Python-Blosc2 (>= 0.1.8) wheels: .. code-block:: console $ pip install blosc2 Collecting blosc2 Downloading blosc2-0.1.8-cp37-cp37m-manylinux2010_x86_64.whl (3.3 MB) |████████████████████████████████| 3.3 MB 4.7 MB/s Installing collected packages: blosc2 Successfully installed blosc2-0.1.8 As a result, one can easily update to the latest version of C-Blosc2 binaries without the need to manually compile it. Following are instructions on how to use the libraries in wheels for different platforms. Compiling C files with Blosc2 wheels on Windows ---------------------------------------------- - The wheels for Windows have been produced with the Microsoft MSVC compiler, so we recommend that you use it too. You can get it for free at: https://visualstudio.microsoft.com/es/downloads/. - In order to check that the MSVC command line is set up correctly, enter ``cl`` in the command prompt window and verify that the output looks something like this: .. code-block:: console > cl Microsoft (R) C/C++ Optimizing Compiler Version 19.00.24245 for x64 Copyright (C) Microsoft Corporation. All rights reserved. usage: cl [ option... ] filename... [ /link linkoption... ] - Make the compiler available. Its typical installation location is `C:\\Program files (x86)\\Microsoft Visual Studio`, so change your current directory there. Then, to set up the build architecture environment you can open a command prompt window in the `VC\\Auxiliary\\Build` subdirectory and execute `vcvarsall.bat x64` if your architecture is 64 bits or `vcvarsall.bat x86` if it is 32 bits. - You will need to know the path where the Blosc2 wheel has installed its files. For this we will use the `dir /s` command (but you can use your preferred location method): .. code-block:: console > dir /s c:\blosc2.lib Volume in drive C is OS Volume Serial Number is 7A21-A5D5 Directory of c:\Users\user\miniconda3\Lib 14/12/2020 09:56 20.848 blosc2.lib 1 File(s) 20.848 bytes Total list files: 1 File(s) 20.848 bytes 0 dirs 38.981.902.336 free bytes - The output shows the path of blosc2.lib in your system, but we are rather interested in the parent one: .. code-block:: console > set WHEEL_DIR=c:\Users\user\miniconda3 - Now, it is important to copy the library `libblosc2.dll` to C:\\Windows\\System32 directory, so it can be found by the executable when it is necessary. - Finally, to compile C files using Blosc2 libraries, enter this command: .. code-block:: console > cl .c /Ox /Fe.exe /I /MT /link/NODEFAULTLIB:MSVCRT - For instance, in the case of blosc "examples/urcodecs.c": .. code-block:: console > cl urcodecs.c %WHEEL_DIR%\lib\blosc2.lib /Ox /Feurcodecs.exe /I%WHEEL_DIR%\include /MT /link/NODEFAULTLIB:MSVCRT Microsoft (R) C/C++ Optimizing Compiler Version 19.10.25017 for x86 Copyright (C) Microsoft Corporation. All rights reserved. urcodecs.c Microsoft (R) Incremental Linker Version 14.10.25017.0 Copyright (C) Microsoft Corporation. All rights reserved. /out:urcodecs.exe /NODEFAULTLIB:MSVCRT urcodecs.obj /NODEFAULTLIB:MSVCRT .\miniconda3\lib\blosc2.lib - And you can run your program: .. code-block:: console > urcodecs Blosc version info: 2.0.0 ($Date:: 2021-05-26 #$) Compression ratio: 381.5 MB -> 0.0 MB (14013.5x) Compression time: 0.261 s, 1462.1 MB/s Decompression time: 0.0669 s, 5698.2 MB/s Successful roundtrip data <-> schunk ! Compiling C files with Blosc2 wheels on Linux --------------------------------------------- - Find the path where Blosc2 wheel has installed its files: .. code-block:: console $ find / -name libblosc2.so 2>/dev/null /home/user/miniconda3/lib/libblosc2.so - The output shows the path of libblosc2.so, but we are rather interested in the parent one: .. code-block:: console $ WHEEL_DIR=/home/user/miniconda3 - To compile C files using Blosc2 you only need to enter the commands: .. code-block:: console $ export LD_LIBRARY_PATH= $ gcc .c -I -o -L -lblosc2 - For instance, let's compile blosc's "examples/urcodecs.c": .. code-block:: console $ export LD_LIBRARY_PATH=$WHEEL_DIR/lib # note that you need the LD_LIBRARY_PATH env variable $ gcc urcodecs.c -I$WHEEL_DIR/include -o urcodecs -L$WHEEL_DIR/lib -lblosc2 - Run your program: .. code-block:: console $ ./urcodecs Blosc version info: 2.0.0-dev0 ($Date:: 2021-05-26 #$) Compression ratio: 381.5 MB -> 0.0 MB (14013.5x) Compression time: 1.46 s, 260.7 MB/s Decompression time: 0.509 s, 749.1 MB/s Successful roundtrip data <-> schunk ! - Rejoice! c-blosc2-3.1.5/CONTRIBUTING.rst000066400000000000000000000050231521743436100155730ustar00rootroot00000000000000Contributing guidelines ======================= We want to make contributing to this project as easy and transparent as possible. Our Development Process ----------------------- New versions are being developed in the "main" branch, or in their own feature branch. When they are deemed ready for a release, they are merged back into "main" again. So all contributions must stage first through "main" or their own feature branch. Pull Requests ------------- We actively welcome your pull requests. 1. Fork the repo and create your branch from `main`. 2. If you've added code that should be tested, add tests. 3. If you've changed APIs, update the documentation. 4. Ensure the test suite passes. 5. Make sure your code does not issue new compiler warnings. Automated Contributions Policy ------------------------------ Contributing to Blosc requires human judgment, contextual understanding, and familiarity with scikit-learn’s structure and goals. It is not suitable for automatic processing by AI tools. Please refrain from submitting issues or pull requests generated by fully-automated tools. Maintainers reserve the right, at their sole discretion, to close such submissions and to block any account responsible for them. Review all code or documentation changes made by AI tools and make sure you understand all changes and can explain them on request, before submitting them under your name. Do not submit any AI-generated code that you haven’t personally reviewed, understood and tested, as this wastes maintainers’ time. Please do not paste AI generated text in the description of issues, PRs or in comments as this makes it harder for reviewers to assess your contribution. We are happy for it to be used to improve grammar or if you are not a native English speaker. If you used AI tools, please state so in your PR description. PRs that appear to violate this policy will be closed without review. For more information on why this is becoming a big issue for open source projects, please see the `Software Review in the Era of AI `_ by the rOpenSci folks. Issues ------ We use GitHub issues to track public bugs. Please ensure your description is clear and has sufficient instructions to be able to reproduce the issue. Coding Style ------------ * 2 spaces for indentation rather than tabs. License ------- By contributing to C-Blosc2, you agree that your contributions will be licensed under the `LICENSE `_ file of the project. c-blosc2-3.1.5/DEVELOPING-GUIDE.rst000066400000000000000000000013401521743436100161510ustar00rootroot00000000000000Some conventions used in C-Blosc2 ================================= * Use C99 designated initialization only in examples. Libraries should use C89 initialization, which is more portable, specially with C++ (designated initialization in C++ is supported only since C++20). * Use _new and _free for memory allocating constructors and destructors and _init and _destroy for non-memory allocating constructors and destructors. * Lines must not exceed 120 characters. If a line is too long, it must be broken into several lines. * Conditional bodies must always use braces, even if they are one-liners. The only exception that can be is when the conditional is a single line and the body is a single line: if (condition) whatever(); c-blosc2-3.1.5/FAQ.md000066400000000000000000000016341521743436100140670ustar00rootroot00000000000000## Which is the compatibility policy for the Blosc library and format? ### Compatibility among libraries with different minor versions The compatibility between minor versions (e.g. 2.0 <-> 2.1) will always be *both* backward and forward. This means that any version of the library will be able to read any data produced with another version differing only in the patch level or minor version. Not being able to achieve that will be considered a bug, and action will be taken to fix that as soon as possible (see e.g. https://github.com/Blosc/c-blosc/issues/215). ### Compatibility among libraries with different major versions There will be an *attempt* (but not an absolute guarantee) of backward compatibility, but it won’t be an effort for guaranteeing forward compatibility. For example, Blosc2 2.1 is able to read any data produced by Blosc 1.x, but Blosc 1.x are not able to read data produced by Blosc2 2.x. c-blosc2-3.1.5/FUNDING.yml000066400000000000000000000001051521743436100147430ustar00rootroot00000000000000github: [numfocus] custom: ['https://numfocus.org/donate-to-blosc'] c-blosc2-3.1.5/LICENSE.txt000066400000000000000000000031541521743436100147600ustar00rootroot00000000000000BSD License For Blosc - A blocking, shuffling and lossless compression library Copyright (c) 2009-2018 Francesc Alted Copyright (c) 2019-present Blosc Development Team Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name Francesc Alted nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. c-blosc2-3.1.5/LICENSES/000077500000000000000000000000001521743436100143375ustar00rootroot00000000000000c-blosc2-3.1.5/LICENSES/BITSHUFFLE.txt000066400000000000000000000021741521743436100165370ustar00rootroot00000000000000Bitshuffle - Filter for improving compression of typed binary data. Copyright (c) 2014 Kiyoshi Masui (kiyo@physics.ubc.ca) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. c-blosc2-3.1.5/LICENSES/FASTLZ.txt000066400000000000000000000021571521743436100161100ustar00rootroot00000000000000FastLZ - Byte-aligned LZ77 compression library Copyright (C) 2005-2020 Ariya Hidayat Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. c-blosc2-3.1.5/LICENSES/LZ4.txt000066400000000000000000000024371521743436100155170ustar00rootroot00000000000000LZ4 Library Copyright (c) 2011-2020, Yann Collet All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. c-blosc2-3.1.5/LICENSES/ZLIB.txt000066400000000000000000000015431521743436100156430ustar00rootroot00000000000000(C) 1995-2013 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. c-blosc2-3.1.5/LICENSES/ZSTD.txt000066400000000000000000000027721521743436100156740ustar00rootroot00000000000000BSD License For Zstandard software Copyright (c) 2016-present, Facebook, Inc. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name Facebook nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. c-blosc2-3.1.5/README.rst000066400000000000000000000247631521743436100146350ustar00rootroot00000000000000======== C-Blosc2 ======== A fast, compressed and persistent data store library for C ========================================================== :Author: Blosc Development Team :Contact: blosc@blosc.org :URL: https://www.blosc.org :Gitter: |gitter| :Actions: |actions| :NumFOCUS: |numfocus| :Code of Conduct: |Contributor Covenant| .. |gitter| image:: https://badges.gitter.im/Blosc/c-blosc.svg :alt: Join the chat at https://gitter.im/Blosc/c-blosc :target: https://gitter.im/Blosc/c-blosc?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge .. |actions| image:: https://github.com/Blosc/c-blosc2/workflows/CI%20CMake/badge.svg :target: https://github.com/Blosc/c-blosc2/actions?query=workflow%3A%22CI+CMake%22 .. |numfocus| image:: https://img.shields.io/badge/powered%20by-NumFOCUS-orange.svg?style=flat&colorA=E1523D&colorB=007D8A :target: https://numfocus.org .. |Contributor Covenant| image:: https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg :target: https://github.com/Blosc/community/blob/master/code_of_conduct.md What is it? =========== `Blosc2 `_ is a high-performance compressor and data format optimized for binary data, including numerical arrays, tensors, and other structured formats. It provides a flexible framework of codecs and filters, enabling developers to balance compression speed and ratio for specific use cases, from high-throughput data pipelines to persistent storage. As the successor to the original Blosc library (released in 2010), Blosc2 is built on a mature foundation and is integrated into many popular scientific computing libraries, such as PyTables, h5py, and Zarr. C-Blosc2 is the new major version of `C-Blosc `_, and is backward compatible with both the C-Blosc1 API and its in-memory format. However, the reverse thing is generally not true for the format; buffers generated with C-Blosc2 are not format-compatible with C-Blosc1 (i.e. forward compatibility is not supported). In case you want to ensure full API compatibility with C-Blosc1 API, define the `BLOSC1_COMPAT` symbol. See a 3-minute `introductory video to Blosc2 `_. Blosc2 NDim: an N-Dimensional store =================================== One of more exciting additions in C-Blosc2 is the `Blosc2 NDim layer `_ (or B2ND for short), which allows creating *and* reading n-dimensional datasets in an extremely efficient way thanks to a n-dim 2-level partitioning, that allows slicing and dicing arbitrary large and compressed data in a more fine-grained way: .. image:: https://github.com/Blosc/c-blosc2/blob/main/images/b2nd-2level-parts.png?raw=true :width: 75% To whet your appetite, here is how the `NDArray` object in the `Python wrapper`_ performs on getting slices orthogonal to the different axis of a 4-dim dataset: .. image:: https://github.com/Blosc/c-blosc2/blob/main/images/Read-Partial-Slices-B2ND.png?raw=true :width: 75% We have blogged about this: https://www.blosc.org/posts/blosc2-ndim-intro We also have a ~2 min explanatory video on `why slicing in a pineapple-style (aka double partition) is useful `_: .. image:: https://github.com/Blosc/blogsite/blob/master/files/images/slicing-pineapple-style.png?raw=true :width: 50% :alt: Slicing a dataset in pineapple-style :target: https://www.youtube.com/watch?v=LvP9zxMGBng .. include:: ../WHATS-NEW.rst More info about the `improved capabilities of C-Blosc2 can be found in this paper `_. Please, cite it if you use C-Blosc2 in your research! Open format =========== The Blosc2 format is open and `fully documented `_. The format specs are defined in less than 4000 words, so they should be easy to read and understand. In our opinion, this is critical for the long-term success of the library, as it allows for third-party implementations of the format, and also for the users to understand what is going on under the hood. Python wrapper ============== We are officially supporting (thanks to the Python Software Foundation) a `Python wrapper for Blosc2 `_. It supports all the features of the predecessor `python-blosc `_ package plus most of the bells and whistles of C-Blosc2, like 64-bit and multidimensional containers. As a bonus, the `python-blosc2` package comes with wheels and binary versions of the C-Blosc2 libraries, so anyone, even non-Python users can install C-Blosc2 binaries easily with: .. code-block:: console pip install blosc2 Compiling the C-Blosc2 library with CMake ========================================= Blosc can be built, tested and installed using `CMake `_. The following procedure describes a typical CMake build. Create the build directory inside the sources and move into it: .. code-block:: console git clone https://github.com/Blosc/c-blosc2 cd c-blosc2 mkdir build cd build Now run CMake configuration and optionally specify the installation directory (e.g. '/usr' or '/usr/local'): .. code-block:: console cmake -DCMAKE_INSTALL_PREFIX=your_install_prefix_directory .. CMake allows configuring Blosc in many different ways, including selecting external codec libraries, fetching pinned upstream codec sources during configuration, or disabling selected codecs altogether. Please note that configuration can also be performed using UI tools provided by CMake (`ccmake` or `cmake-gui`): .. code-block:: console ccmake .. # run a curses-based interface cmake-gui .. # run a graphical interface Build, test and install Blosc: .. code-block:: console cmake --build . ctest cmake --build . --target install The static and dynamic version of the Blosc library, together with header files, will be installed into the specified CMAKE_INSTALL_PREFIX. Once you have compiled your Blosc library, you can easily link your apps with it as shown in the `examples/ directory `_. Handling support for codecs (LZ4, LZ4HC, Zstd, Zlib, ZFP) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C-Blosc2 can either fetch pinned upstream codec releases via CMake ``FetchContent`` or use codec libraries already installed on the system. The top-level switch is ``BLOSC_DEPENDENCY_MODE``: ``BUNDLED`` Default. Fetch and build the bundled dependency versions. This preserves the traditional self-contained C-Blosc2 build behavior. Bundled dependencies are private: their headers and libraries are not installed separately, and static builds fold them into ``libblosc2.a``. ``EXTERNAL`` Require system packages for mandatory dependencies and fail with a clear error if they are missing. This is the recommended mode for distro packagers that must avoid bundled third-party code:: cmake -DBLOSC_DEPENDENCY_MODE=EXTERNAL .. ``AUTO`` Try system packages first and fall back to bundled dependencies. This is convenient for local development, but less reproducible for distro packaging. The older ``PREFER_EXTERNAL_LZ4``, ``PREFER_EXTERNAL_ZLIB`` and ``PREFER_EXTERNAL_ZSTD`` options are still accepted as per-dependency compatibility options; when enabled they try the system package first for that dependency. ZFP support is controlled separately because ZFP packages are not available in all distributions: ``BLOSC_ENABLE_ZFP=AUTO`` Default. In ``BUNDLED`` mode, build bundled ZFP. In ``EXTERNAL`` mode, enable ZFP only when a system ZFP package is found; otherwise build C-Blosc2 without ZFP support. In ``AUTO`` dependency mode, try system ZFP first and fall back to bundled ZFP. ``BLOSC_ENABLE_ZFP=ON`` Require ZFP support. In ``EXTERNAL`` mode this fails if no system ZFP package is found. ``BLOSC_ENABLE_ZFP=OFF`` Disable the ZFP codec. For fully local bundled builds without network access, point CMake at local codec checkouts instead of downloading them: .. code-block:: console cmake \ -DBLOSC_LZ4_SOURCE_DIR=/path/to/lz4 \ -DBLOSC_ZLIBNG_SOURCE_DIR=/path/to/zlib-ng \ -DBLOSC_ZSTD_SOURCE_DIR=/path/to/zstd \ -DBLOSC_ZFP_SOURCE_DIR=/path/to/zfp \ .. You can also exclude support for selected codecs. For example, to disable Zstd support: .. code-block:: console cmake -DDEACTIVATE_ZSTD=ON .. Supported platforms ~~~~~~~~~~~~~~~~~~~ C-Blosc2 is meant to support all platforms where a C99 compliant C compiler can be found. The ones that are mostly tested are Intel (Linux, Mac OSX and Windows), ARM (Linux, Mac), and PowerPC (Linux). More on ARM support in `README_ARM.rst`. For Windows, you will need at least VS2015 or higher on x86 and x64 targets (i.e. ARM is not supported on Windows). For Mac OSX, make sure that you have the command line developer tools available. You can always install them with: .. code-block:: console xcode-select --install For Mac OSX on arm64 architecture, you may want to compile it like this: .. code-block:: console CC="clang -arch arm64" cmake .. Display error messages ~~~~~~~~~~~~~~~~~~~~~~ By default error messages are disabled. To display them, you just need to activate the Blosc tracing machinery by setting the ``BLOSC_TRACE`` environment variable. Contributing ============ If you want to collaborate in this development you are welcome. We need help in the different areas listed at the `ROADMAP `_; also, be sure to read our `DEVELOPING-GUIDE `_ and our `Code of Conduct `_. Blosc is distributed using the `BSD license `_. Twitter feed ============ Follow `@Blosc2 `_ so as to get informed about the latest developments. Citing Blosc ============ You can cite our work on the different libraries under the Blosc umbrella as: .. code-block:: console @ONLINE{blosc, author = {{Blosc Development Team}}, title = "{A fast, compressed and persistent data store library}", year = {2009-2025}, note = {https://blosc.org} } Acknowledgments =============== See `THANKS document `_. ---- *Compress Better, Compute Bigger* -- Blosc Development Team c-blosc2-3.1.5/README_ARM.rst000066400000000000000000000371151521743436100153270ustar00rootroot00000000000000Support for Apple arm64 ======================= Since November 2020 Apple is releasing Mac boxes using CPUs with its own arm64 implementation. Currently, you can compile C-Blosc2 with: .. code-block:: console mkdir build cd build CC="clang -arch arm64" cmake .. The NEON extensions are used automatically in this case. Here are a couple of benchmarks on BloscLZ and LZ4 codecs running on a MacBook Air with a new M1 arm64 processor: .. code-block:: console (base) francesc@Francescs-MacBook-Air build % bench/b2bench blosclz Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) List of supported compressors in this build: blosclz,lz4,lz4hc,zlib,zstd Supported compression libraries: BloscLZ: 2.3.0 LZ4: 1.9.1 Zlib: 10.0.3 Zstd: 1.4.5 Using compressor: blosclz Using shuffle type: shuffle Running suite: single --> 4, 4194304, 4, 19, blosclz, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 4 ********************** Running benchmarks ********************* memcpy(write): 327.0 us, 12234.1 MB/s memcpy(read): 127.1 us, 31460.5 MB/s Compression level: 0 comp(write): 136.6 us, 29274.7 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 101.8 us, 39281.3 MB/s OK Compression level: 1 comp(write): 443.7 us, 9014.3 MB/s Final bytes: 1102576 Ratio: 3.80 decomp(read): 99.4 us, 40242.1 MB/s OK Compression level: 2 comp(write): 414.2 us, 9656.0 MB/s Final bytes: 1083528 Ratio: 3.87 decomp(read): 99.6 us, 40178.6 MB/s OK Compression level: 3 comp(write): 451.0 us, 8869.1 MB/s Final bytes: 1083528 Ratio: 3.87 decomp(read): 102.3 us, 39107.8 MB/s OK Compression level: 4 comp(write): 568.7 us, 7033.2 MB/s Final bytes: 165194 Ratio: 25.39 decomp(read): 135.8 us, 29447.2 MB/s OK Compression level: 5 comp(write): 565.2 us, 7076.6 MB/s Final bytes: 165194 Ratio: 25.39 decomp(read): 135.5 us, 29512.3 MB/s OK Compression level: 6 comp(write): 523.5 us, 7640.9 MB/s Final bytes: 141642 Ratio: 29.61 decomp(read): 138.2 us, 28943.9 MB/s OK Compression level: 7 comp(write): 520.4 us, 7687.0 MB/s Final bytes: 138042 Ratio: 30.38 decomp(read): 137.3 us, 29136.4 MB/s OK Compression level: 8 comp(write): 524.1 us, 7632.7 MB/s Final bytes: 137610 Ratio: 30.48 decomp(read): 138.4 us, 28893.1 MB/s OK Compression level: 9 comp(write): 534.4 us, 7485.3 MB/s Final bytes: 73254 Ratio: 57.26 decomp(read): 127.5 us, 31371.5 MB/s OK Round-trip compr/decompr on 7.5 GB Elapsed time: 1.2 s, 13694.7 MB/s (base) francesc@Francescs-MacBook-Air build % bench/b2bench lz4 Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) List of supported compressors in this build: blosclz,lz4,lz4hc,zlib,zstd Supported compression libraries: BloscLZ: 2.3.0 LZ4: 1.9.1 Zlib: 10.0.3 Zstd: 1.4.5 Using compressor: lz4 Using shuffle type: shuffle Running suite: single --> 4, 4194304, 4, 19, lz4, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 4 ********************** Running benchmarks ********************* memcpy(write): 331.2 us, 12076.7 MB/s memcpy(read): 134.7 us, 29689.2 MB/s Compression level: 0 comp(write): 132.2 us, 30259.8 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 109.0 us, 36709.8 MB/s OK Compression level: 1 comp(write): 589.3 us, 6787.9 MB/s Final bytes: 991408 Ratio: 4.23 decomp(read): 244.4 us, 16367.1 MB/s OK Compression level: 2 comp(write): 658.0 us, 6078.7 MB/s Final bytes: 877344 Ratio: 4.78 decomp(read): 313.3 us, 12765.8 MB/s OK Compression level: 3 comp(write): 670.3 us, 5967.1 MB/s Final bytes: 786514 Ratio: 5.33 decomp(read): 312.4 us, 12804.1 MB/s OK Compression level: 4 comp(write): 420.5 us, 9512.9 MB/s Final bytes: 428754 Ratio: 9.78 decomp(read): 244.8 us, 16340.0 MB/s OK Compression level: 5 comp(write): 419.5 us, 9536.0 MB/s Final bytes: 428754 Ratio: 9.78 decomp(read): 245.2 us, 16311.3 MB/s OK Compression level: 6 comp(write): 413.5 us, 9673.5 MB/s Final bytes: 379116 Ratio: 11.06 decomp(read): 239.3 us, 16713.1 MB/s OK Compression level: 7 comp(write): 414.2 us, 9656.7 MB/s Final bytes: 379116 Ratio: 11.06 decomp(read): 240.0 us, 16666.0 MB/s OK Compression level: 8 comp(write): 413.2 us, 9680.5 MB/s Final bytes: 379116 Ratio: 11.06 decomp(read): 237.7 us, 16830.1 MB/s OK Compression level: 9 comp(write): 414.3 us, 9654.1 MB/s Final bytes: 379116 Ratio: 11.06 decomp(read): 239.8 us, 16684.0 MB/s OK Round-trip compr/decompr on 7.5 GB Elapsed time: 1.4 s, 11720.1 MB/s Yes, the speed is really good on the new Apple architecture! ARM environment for Ubuntu Intel ================================ In order to create executable code for a platform other than the one on which the compiler is running we can use cross compilers. In this case we want to create executables for ARM platforms but execute them on Intel platform with Ubuntu. The only package that is necessary (at least in Ubuntu 15.04) is gcc-arm-linux-gnueabihf. Once this is installed the way to compile an example is: .. code-block:: console $ arm-linux-gnueabihf-gcc -static -mfpu=neon -flax-vector-conversions -DSHUFFLE_NEON_ENABLED -O3 simple.c ../blosc/blosc.c ../blosc/blosclz.c ../blosc/shuffle.c ../blosc/shuffle-generic.c ../blosc/bitshuffle-generic.c ../blosc/shuffle-neon.c ../blosc/bitshuffle-neon.c -I../blosc -o simple -lpthread Another example for running a bench with the cross compiler: .. code-block:: console $ arm-linux-gnueabihf-gcc -static -mfpu=neon -flax-vector-conversions -DSHUFFLE_NEON_ENABLED -O3 bench.c ../blosc/blosc.c ../blosc/blosclz.c ../blosc/shuffle.c ../blosc/shuffle-generic.c ../blosc/bitshuffle-generic.c ../blosc/shuffle-neon.c ../blosc/bitshuffle-neon.c -I../blosc -o bench -lpthread In these cases the NEON flags are: `-mfpu=neon -flax-vector-conversions` This is explained in detail in: https://linux-sunxi.org/Toolchain This way you can develop and debug applications for ARM on intel machines as if you were in ARM platforms. Benchmark for ODROID-XU3 ======================== This is a benchmark to compare the speeds between the NEON and the generic implementation. :: CPU: ARMv7 Processor rev 3 (v7l) Compiler: gcc, version gcc-4.8.real Optimizations: -O3 OS: Ubuntu 14.04 trusty • NEON implementation results: :: Blosc version: 2.0.0a1 ($Date:: 2015-07-30 #$) List of supported compressors in this build: blosclz Supported compression libraries: BloscLZ: 1.0.5 LZ4: unknown Zlib: unknown Using compressor: blosclz Using shuffle type: shuffle Running suite: suite --> 1, 2097152, 8, 19, blosclz, shuffle ********************** Run info ****************************** Blosc version: 2.0.0a1 ($Date:: 2015-07-30 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 2097152 bytes Type size: 8 bytes Working set: 64.0 MB Number of threads: 1 ********************** Running benchmarks ********************* memcpy(write): 2265.1 us, 883.0 MB/s memcpy(read): 1196.9 us, 1671.0 MB/s Compression level: 0 comp(write): 984.8 us, 2030.9 MB/s Final bytes: 2097168 Ratio: 1.00 decomp(read): 1202.8 us, 1662.8 MB/s OK Compression level: 1 comp(write): 5863.7 us, 341.1 MB/s Final bytes: 584976 Ratio: 3.59 decomp(read): 1014.1 us, 1972.3 MB/s OK Compression level: 2 comp(write): 6229.8 us, 321.0 MB/s Final bytes: 584976 Ratio: 3.59 decomp(read): 1013.4 us, 1973.5 MB/s OK Compression level: 3 comp(write): 6603.8 us, 302.9 MB/s Final bytes: 584976 Ratio: 3.59 decomp(read): 1012.9 us, 1974.4 MB/s OK Compression level: 4 comp(write): 6792.6 us, 294.4 MB/s Final bytes: 557840 Ratio: 3.76 decomp(read): 983.5 us, 2033.6 MB/s OK Compression level: 5 comp(write): 8598.3 us, 232.6 MB/s Final bytes: 557840 Ratio: 3.76 decomp(read): 983.4 us, 2033.9 MB/s OK Compression level: 6 comp(write): 9866.2 us, 202.7 MB/s Final bytes: 546320 Ratio: 3.84 decomp(read): 1079.1 us, 1853.3 MB/s OK Compression level: 7 comp(write): 9334.9 us, 214.2 MB/s Final bytes: 216528 Ratio: 9.69 decomp(read): 1959.4 us, 1020.7 MB/s OK Compression level: 8 comp(write): 9221.1 us, 216.9 MB/s Final bytes: 216528 Ratio: 9.69 decomp(read): 1972.1 us, 1014.2 MB/s OK Compression level: 9 comp(write): 8452.0 us, 236.6 MB/s Final bytes: 153160 Ratio: 13.69 decomp(read): 2780.0 us, 719.4 MB/s OK --> 2, 2097152, 8, 19, blosclz, shuffle ********************** Run info ****************************** Blosc version: 2.0.0a1 ($Date:: 2015-07-30 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 2097152 bytes Type size: 8 bytes Working set: 64.0 MB Number of threads: 2 ********************** Running benchmarks ********************* memcpy(write): 2258.0 us, 885.8 MB/s memcpy(read): 1194.9 us, 1673.7 MB/s Compression level: 0 comp(write): 831.2 us, 2406.0 MB/s Final bytes: 2097168 Ratio: 1.00 decomp(read): 1162.6 us, 1720.3 MB/s OK Compression level: 1 comp(write): 2975.1 us, 672.2 MB/s Final bytes: 584976 Ratio: 3.59 decomp(read): 738.4 us, 2708.4 MB/s OK Compression level: 2 comp(write): 3156.8 us, 633.6 MB/s Final bytes: 584976 Ratio: 3.59 decomp(read): 738.7 us, 2707.5 MB/s OK Compression level: 3 comp(write): 3347.0 us, 597.6 MB/s Final bytes: 584976 Ratio: 3.59 decomp(read): 749.6 us, 2668.1 MB/s OK Compression level: 4 comp(write): 3486.2 us, 573.7 MB/s Final bytes: 557840 Ratio: 3.76 decomp(read): 745.1 us, 2684.4 MB/s OK Compression level: 5 comp(write): 4488.5 us, 445.6 MB/s Final bytes: 557840 Ratio: 3.76 decomp(read): 725.7 us, 2755.9 MB/s OK Compression level: 6 comp(write): 4998.5 us, 400.1 MB/s Final bytes: 546320 Ratio: 3.84 decomp(read): 796.8 us, 2510.2 MB/s OK Compression level: 7 comp(write): 4780.3 us, 418.4 MB/s Final bytes: 216528 Ratio: 9.69 decomp(read): 1383.9 us, 1445.1 MB/s OK Compression level: 8 comp(write): 4778.6 us, 418.5 MB/s Final bytes: 216528 Ratio: 9.69 decomp(read): 1398.0 us, 1430.6 MB/s OK Compression level: 9 comp(write): 5884.6 us, 339.9 MB/s Final bytes: 153160 Ratio: 13.69 decomp(read): 2647.7 us, 755.4 MB/s OK Round-trip compr/decompr on 3.8 GB Elapsed time: 13.9 s, 609.1 MB/s • Generic implementation results: :: Blosc version: 2.0.0a1 ($Date:: 2015-07-30 #$) List of supported compressors in this build: blosclz Supported compression libraries: BloscLZ: 1.0.5 LZ4: unknown Zlib: unknown Using compressor: blosclz Using shuffle type: shuffle Running suite: suite --> 1, 2097152, 8, 19, blosclz, shuffle ********************** Run info ****************************** Blosc version: 2.0.0a1 ($Date:: 2015-07-30 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 2097152 bytes Type size: 8 bytes Working set: 64.0 MB Number of threads: 1 ********************** Running benchmarks ********************* memcpy(write): 2194.1 us, 911.5 MB/s memcpy(read): 1170.8 us, 1708.2 MB/s Compression level: 0 comp(write): 896.2 us, 2231.7 MB/s Final bytes: 2097168 Ratio: 1.00 decomp(read): 1179.3 us, 1695.9 MB/s OK Compression level: 1 comp(write): 7534.4 us, 265.4 MB/s Final bytes: 584976 Ratio: 3.59 decomp(read): 4117.1 us, 485.8 MB/s OK Compression level: 2 comp(write): 7895.6 us, 253.3 MB/s Final bytes: 584976 Ratio: 3.59 decomp(read): 4106.7 us, 487.0 MB/s OK Compression level: 3 comp(write): 8262.2 us, 242.1 MB/s Final bytes: 584976 Ratio: 3.59 decomp(read): 4113.9 us, 486.2 MB/s OK Compression level: 4 comp(write): 8495.6 us, 235.4 MB/s Final bytes: 557840 Ratio: 3.76 decomp(read): 4042.6 us, 494.7 MB/s OK Compression level: 5 comp(write): 10321.0 us, 193.8 MB/s Final bytes: 557840 Ratio: 3.76 decomp(read): 4033.9 us, 495.8 MB/s OK Compression level: 6 comp(write): 11675.3 us, 171.3 MB/s Final bytes: 546320 Ratio: 3.84 decomp(read): 4096.4 us, 488.2 MB/s OK Compression level: 7 comp(write): 10193.0 us, 196.2 MB/s Final bytes: 216528 Ratio: 9.69 decomp(read): 7150.9 us, 279.7 MB/s OK Compression level: 8 comp(write): 10192.3 us, 196.2 MB/s Final bytes: 216528 Ratio: 9.69 decomp(read): 7167.7 us, 279.0 MB/s OK Compression level: 9 comp(write): 10418.7 us, 192.0 MB/s Final bytes: 153160 Ratio: 13.69 decomp(read): 7870.8 us, 254.1 MB/s OK --> 2, 2097152, 8, 19, blosclz, shuffle ********************** Run info ****************************** Blosc version: 2.0.0a1 ($Date:: 2015-07-30 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 2097152 bytes Type size: 8 bytes Working set: 64.0 MB Number of threads: 2 ********************** Running benchmarks ********************* memcpy(write): 2243.1 us, 891.6 MB/s memcpy(read): 1219.9 us, 1639.4 MB/s Compression level: 0 comp(write): 846.8 us, 2361.9 MB/s Final bytes: 2097168 Ratio: 1.00 decomp(read): 1182.1 us, 1691.9 MB/s OK Compression level: 1 comp(write): 3867.6 us, 517.1 MB/s Final bytes: 584976 Ratio: 3.59 decomp(read): 2162.8 us, 924.7 MB/s OK Compression level: 2 comp(write): 4054.2 us, 493.3 MB/s Final bytes: 584976 Ratio: 3.59 decomp(read): 2156.6 us, 927.4 MB/s OK Compression level: 3 comp(write): 4241.2 us, 471.6 MB/s Final bytes: 584976 Ratio: 3.59 decomp(read): 2169.6 us, 921.8 MB/s OK Compression level: 4 comp(write): 4377.1 us, 456.9 MB/s Final bytes: 557840 Ratio: 3.76 decomp(read): 7556.9 us, 264.7 MB/s OK Compression level: 5 comp(write): 5276.3 us, 379.1 MB/s Final bytes: 557840 Ratio: 3.76 decomp(read): 7556.7 us, 264.7 MB/s OK Compression level: 6 comp(write): 6026.8 us, 331.9 MB/s Final bytes: 546320 Ratio: 3.84 decomp(read): 3108.2 us, 643.5 MB/s OK Compression level: 7 comp(write): 5877.0 us, 340.3 MB/s Final bytes: 216528 Ratio: 9.69 decomp(read): 3668.5 us, 545.2 MB/s OK Compression level: 8 comp(write): 5882.0 us, 340.0 MB/s Final bytes: 216528 Ratio: 9.69 decomp(read): 3531.2 us, 566.4 MB/s OK Compression level: 9 comp(write): 7621.3 us, 262.4 MB/s Final bytes: 153160 Ratio: 13.69 decomp(read): 4978.3 us, 401.7 MB/s OK Round-trip compr/decompr on 3.8 GB Elapsed time: 21.9 s, 385.0 MB/s We have achieved to implement shuffle NEON instructions for ARM that are twice as fast as the generic implementation. Enjoy developing for ARM! Lucian Marc c-blosc2-3.1.5/README_B2ND_FORMAT.rst000066400000000000000000000010121521743436100164300ustar00rootroot00000000000000B2ND Format =========== The B2ND format is meant for storing multidimensional datasets defined by a shape and a data type. Both the shape and the data type follow the NumPy conventions. It is just a `B2ND metalayer `_ on top of a Blosc2 `CFrame `_ (for contiguous storage) or `SFrame `_ (for sparse storage). c-blosc2-3.1.5/README_B2ND_METALAYER.rst000066400000000000000000000064251521743436100170000ustar00rootroot00000000000000B2ND Metalayer Format ===================== This is a `metalayer `_ on top of a Blosc2 `CFrame `_ or `SFrame `_ that is meant for storing multidimensional information. Specifically, this metalayer is named 'b2nd' and follows this format:: |-0-|-1-|-2-|-3-|~~~~~~~~~~~~~~~~|---|~~~~~~~~~~~~~~~~|---|~~~~~~~~~~~~~~~~| | 9X| v | nd| 9X| shape | 9X| chunkshape | 9X| blockshape | |---|---|---|---|~~~~~~~~~~~~~~~~|---|~~~~~~~~~~~~~~~~|---|~~~~~~~~~~~~~~~~| ^ ^ ^ ^ ^ ^ | | | | | | | | | | | +--[msgpack] fixarray with X=nd elements | | | | +--[msgpack] fixarray with X=nd elements | | | +--[msgpack] fixarray with X=nd elements | | +--[msgpack] positive fixnum for the number of dimensions (up to 127) | +--[msgpack] positive fixnum for the metalayer format version (up to 127) +---[msgpack] fixarray with X=7 elements The `shape` section is meant to store the actual shape info:: |---|--8 bytes---|---|--8 bytes---|~~~~~|---|--8 bytes---| | d3| first_dim | d3| second_dim | ... | d3| nth_dim | |---|------------|---|------------|~~~~~|---|------------| ^ ^ ^ | | | | | +--[msgpack] int64 | +--[msgpack] int64 +--[msgpack] int64 Next, the `chunkshape` section is meant to store the actual chunk shape info:: |---|--4 bytes---|---|--4 bytes---|~~~~~|---|--4 bytes---| | d2| first_dim | d2| second_dim | ... | d2| nth_dim | |---|------------|---|------------|~~~~~|---|------------| ^ ^ ^ | | | | | +--[msgpack] int32 | +--[msgpack] int32 +--[msgpack] int32 Next, the `blockshape` section is meant to store the actual block shape info:: |---|--4 bytes---|---|--4 bytes---|~~~~~|---|--4 bytes---| | d2| first_dim | d2| second_dim | ... | d2| nth_dim | |---|------------|---|------------|~~~~~|---|------------| ^ ^ ^ | | | | | +--[msgpack] int32 | +--[msgpack] int32 +--[msgpack] int32 Finally, the `dtype` section is meant to store the data type information:: |---|---|--4 bytes---|--------------| | XX| db| dtype_len | dtype_string | |---|---|------------|--------------| ^ ^ | | | +--[msgpack] str32 +--[msgpack] positive fixint (7-bit integer). dtype_format; 0 means NumPy format. The 0 value for dtype_format means that the dtype_string field follows the NumPy convention (e.g. an `int32_t` dtype is represented as "`_ contiguously, either in-memory or on-disk. The frame is composed of a header, a chunks section, and a trailer:: +---------+--------+---------+ | header | chunks | trailer | +---------+--------+---------+ Each of the three parts of the frame are variable length; with the header and trailer both stored using the `msgpack `_ format. *Note:* Integer types are stored in big endian for msgpack format. All the rest are stored in little endian. Header ------ The header contains information needed to decompress the Blosc chunks contained in the frame. It is encoded using msgpack and the format is as follows:: |-0-|-1-|-2-|-3-|-4-|-5-|-6-|-7-|-8-|-9-|-A-|-B-|-C-|-D-|-E-|-F-|-10|-11|-12|-13|-14|-15|-16|-17| | 9X| aX| "b2frame\0" | d2| header_len | cf| frame_len | |---|---|-------------------------------|---|---------------|---|-------------------------------| ^ ^ ^ ^ ^ | | | | | | | | | +--[msgpack] uint64 | | | | | | | +--[msgpack] int32 | | +---magic number, currently "b2frame" | +------[msgpack] str with X=8 elements +---[msgpack] fixarray with X=0xE (14) elements |-18|-19|-1A|-1B|-1C|-1D|-1E|-1F|-20|-21|-22|-23|-24|-25|-26|-27|-28|-29|-2A|-2B|-2C|-2D|-2E| | a4|_f0|_f1|_f2|_f3| d3| uncompressed_size | d3| compressed_size | |---|---|---|---|---|---|-------------------------------|---|-------------------------------| ^ ^ ^ ^ ^ ^ ^ | | | | | | +--[msgpack] int64 | | | | | +--[msgpack] int64 | | | | +-- other flags | | | +--codec_flags (see below) | | +---frame_type (see below) | +------general_flags (see below) +---[msgpack] str with 4 elements (flags) |-2F|-30|-31|-32|-33|-34|-35|-36|-37|-38|-39|-3A|-3B|-3C|-3D|-3E|-3F|-40|-41|-42|-43|-44| | d2| type_size | d2| block_size | d2| chunk_size | d1| tcomp | d1|tdecomp| cX| |---|---------------|---|---------------|---|---------------|---|-------|---|-------|---| ^ ^ ^ ^ ^ ^ ^ ^ | | | | | | | +-- [msgpack] bool for has_vlmetalayers | | | | | | +-- number of threads for decompression | | | | | +-- [msgpack] int16 | | | | +-- number of threads for compression | | | +-- [msgpack] int16 | | +-- [msgpack] int32 | +-- [msgpack] int32 +-- [msgpack] int32 The filter pipeline is stored next in the header. It contains 6 slots, one for each filter that can be applied. For each slot there is a byte used to store the filter ID in `filters` and an associated byte used to store any possible filter meta-info in `filters_meta`:: |-45|-46|-47|-48|-49|-4A|-4B|-4C|-4D|-4E|-4F|-50|-51|-52|-53|-54|-55|-56| | d2| X | filters |_f4|_f5| filters_meta |_of| | |---|---|-------------------------------|-------------------------------| ^ ^ ^ ^ ^ ^ | | | | | +-- reserved | | | | +-- other_flags2 (see below) | | | +-- compcodec_meta | | +-- udcodec | +--number of filters +--[msgpack] fixext 16 The last section of the header is for the *metalayers*, which contain meta-information about the data in the frame. It is mandatory the use of the msgpack format for storing them, although the user may use another format (e.g. json) encoded as msgpack (in this case as a string). Here is the format for the *metalayers*:: |-57|-58|-59|-5A|-5B|-5C|-5D|====================|---|---|---|================| | 93| cd| idx | de| size | meta keys/values | dc| idy | meta content | |---|---|-------|---|---|---|====================|---|-------|================| ^ ^ ^ ^ ^ ^ ^ ^ ^ | | | | | | | | +-- [msgpack] bin32 | | | | | | | +-- number of elements in the array | | | | | | +-- [msgpack] array16 | | | | | +-- [msgpack] fixstr/int32 | | | | +-- number of elements in the map | | | +-- [msgpack] map of name/offset pairs | | +-- size of metalayers | +-- [msgpack] uint16 +-- [msgpack] fixarray with 3 elements :header_len: (``int32``) Length of the header of the frame (including metalayers). :frame_len: (``uint64``) Length of the whole frame (including compressed data). :general_flags: (``uint8``) General flags. :``0`` to ``3``: Format version. :``4`` and ``5``: Enumerated for chunk offsets. :``0``: 32-bit :``1``: 64-bit :``2``: 128-bit :``3``: 256-bit :``6``: Chunks of fixed length (0) or variable length (1) :``7``: All chunks in the frame use variable-length blocks (1) or regular blocks (0) Frames must not mix regular chunks and variable-length-block chunks. :frame_type: (``uint8``) The type of frame. :``0`` to ``3``: Enumerated for the type of frame (up to 16). :``0``: ``Contiguous`` :``1``: ``Sparse (directory)`` :``2 to 15``: Reserved :``4`` to ``7``: Reserved for user-defined frame types (up to 16) :codec_flags: (``uint8``) Compressor enumeration (defaults for all the chunks in storage). :``0`` to ``3``: Enumerated for codecs (up to 16). :``0``: ``blosclz`` :``1``: ``lz4`` or ``lz4hc`` :``2``: reserved (slot previously occupied by ``snappy`` and free now) :``3``: ``zlib`` :``4``: ``zstd`` :``5``: reserved :``6``: The compressor is defined in the user-defined codec slot (see below). :``7 to 15``: Reserved :``4`` to ``7``: Compression level (up to 16) :other_flags: (``uint8``) Split mode. :``0`` to ``1``: Enumerated for splitmodes (up to 4). :``0``: ``BLOSC_ALWAYS_SPLIT`` :``1``: ``BLOSC_NEVER_SPLIT`` :``2``: ``BLOSC_AUTO_SPLIT`` :``3``: ``BLOSC_FORWARD_COMPAT_SPLIT`` :``2`` to ``7``: Reserved. :other_flags2: (``uint8``) Additional compression flags. :``0``: Use dictionary compression (1) or not (0). Only meaningful for codecs that support dictionaries (e.g. ZSTD). :``1`` to ``7``: Reserved. :uncompressed_size: (``int64``) Size of uncompressed data in frame (excluding metadata). :compressed_size: (``int64``) Size of compressed data in frame (excluding metadata). :type_size: (``int32``) Size of each item. :block_size: (``int32``) Size of data blocks when all data chunks are equal size (the only case supported so far). :chunk_size: (``int32``) Size of each data chunk. 0 if not a fixed chunksize (not supported yet). :tcomp: (``int16``) Number of threads for compression. If 0, same than `cctx`. :tdecomp: (``int16``) Number of threads for decompression. If 0, same than `dctx`. :udcodec: (``uint8``) User-defined codec identifier. :compcode_meta: (``uint8``) Compression codec metadata associated with the compression codec. Only used in user-defined codecs. :map of metalayers: This is a *msgpack-formatted* map for the different metalayers. The keys will be a string (0xa0 + namelen) for the names of the metalayers, followed by an int32 (0xd2) for the *offset* of the value of this metalayer. The actual value will be encoded as a bin32 (0xc6) value later in header. Dumping info in metalayers ~~~~~~~~~~~~~~~~~~~~~~~~~~ **Note:** The method in this section only works for Unix. Here is a trick for printing the content of metalayers using the nice set of `msgpack-tools `_ command line utilities. After installing the package we can do e.g.:: $ msgpack2json -Bi plugins/test_data/example_day_month_temp.b2nd ["b2frame\u0000",166,3947,"\u0012\u0000P\u0003",5472,3682,4,684,1368,1,1,false, "ext:6:base64:AAAAAAABAAAAAAAAAAAAAA==",[17,{"b2nd":107}, ["lgACktMAAAAAAAABkNMAAAAAAAAAA5LSAAAAbtIAAAADktIAAAA50gAAAAPbAAAABXVpbnQ4"]]] Here we see that we have a `b2nd` metalayer that starts at position 107; but as there is a msgpack `bin32` there, we must add 5 bytes (4 bytes for an int32 and 1 byte for the msgpack `bin32` header), so the actual starting position is 112 (107 + 5). Also, although we don't know the length of the `b2nd` metalayer, it is typically less than 100 bytes, so let's err on the safe side and dump the first 1000 bytes, just in case:: $ dd bs=1 skip=112 count=1000 < plugins/test_data/example_day_month_temp.b2nd | msgpack2json -B [0,2,[400,3],[110,3],[57,3],0,"|u1"] By having a look at the `Blosc2 NDim metalayer format `_ one may note that the number of dimensions is 2, `shape` is [400, 3], `chunkshape` is [110, 3], blockshape is [57, 3], dtype format is 0 (NumPy) and dtype is "|u1", which is a NumPy shortcut for `np.uint8`. Chunks ------ The chunks section is composed of one or more Blosc data chunks followed by an index chunk:: +========+========+========+========+===========+ | chunk0 | chunk1 | ... | chunkN | chunk idx | +========+========+========+========+===========+ Each chunk is stored contiguously one after the other, and each follows the format described in the `chunk format `_ document. The `chunk idx` is a Blosc2 chunk containing the offsets (starting from the beginning of the header) to each chunk in this section. The data in the chunk is a list of offsets (they can be 32-bit, 64-bit or more, see above; currently only 64-bit are implemented) to each chunk. The index chunk follows the regular Blosc2 chunk format and can be compressed (the default). **Note:** The offsets can take *special values* so as to represent chunks with run-length (equal) values. The codification for the offsets is as follows:: +========+========+========+========+ | byte 0 | byte 1 | ... | byte N | +========+========+========+========+ ^ | +--> Byte for special values If the most significant bit (7) of the most significant byte above (byte N, as little endian is used) is set, that represents a chunk with a run-length of special values. More specifically the **byte for special values** has this format: :bits 0, 1 and 2: Indicate special values for the entire chunk. :``0``: Reserved. :``1``: A run of zeros. :``2``: A run of NaN (Not-a-Number) floats (whether f32 or f64 depends on typesize). :``3``: Reserved. :``4``: Values that are not initialized. :``5``: Reserved. :``6``: Reserved. :``7``: Reserved. :bit 3 (``0x08``): Reserved. :bit 4 (``0x10``): Reserved. :bit 5 (``0x20``): Reserved. :bit 6 (``0x40``): Reserved. :bit 7 (``0x80``): Indicates a special value. If not set, a regular value. Trailer ------- The trailer for the frame is encoded via `msgpack `_ and contains a user meta data chunk and a fingerprint.:: |-0-|-1-|================|---|---------------|---|---|---------------| | 9X| aX| vlmetalayers | ce| trailer_len | d8|fpt| fingerprint | |---|---|================|---|---------------|---|---|---------------| ^ ^ ^ ^ ^ ^ ^ ^ | | | | | | | +-- fingerprint type | | | | | | +--[msgpack] fixext 16 | | | | | +-- trailer length | | | | +--[msgpack] uint32 for trailer length | | | +--Variable-length metalayers (See header metalayers) | | +---[msgpack] bin32 for vlmetalayers | +------[msgpack] int8 for trailer version +---[msgpack] fixarray with X=4 elements The *vlmetalayers* object which stores the variable-length user meta data can change in size during the lifetime of the frame. This is an important feature and the reason why the *vlmetalayers* are stored in the trailer and not in the header. However, the *vlmetalayers* follows the same format as the ones stored in the header. :trailer_len: (``uint32``) Size of the trailer of the frame (including vlmetalayers chunk). :fpt: (``int8``) Fingerprint type: 0 -> no fp; 1 -> 32-bit; 2 -> 64-bit; 3 -> 128-bit :fingerprint: (``uint128``) Fix storage space for the fingerprint (16 bytes), padded to the left. c-blosc2-3.1.5/README_CHUNK_FORMAT.rst000066400000000000000000000251721521743436100166300ustar00rootroot00000000000000Blosc/Blosc2 Chunk Format ========================= A regular chunk is composed of a header and a blocks section:: +---------+--------+ | header | blocks | +---------+--------+ Also, there are the so-called lazy chunks that do not have the actual compressed data, but only meta-information about how to read it. Lazy chunks typically appear when reading data from persistent media. A lazy chunk has header and bstarts sections in place and in addition, an additional trailer for allowing to read the data blocks:: +---------+---------+---------+ | header | bstarts | trailer | +---------+---------+---------+ All these sections are described below. Note that the bstarts section is described as part of the blocks section. *Note:* All integer types in this document are stored in little endian. Header ------ Blosc (as of version 1.0.0) has the following 16 byte header that stores information about the compressed chunk:: |-0-|-1-|-2-|-3-|-4-|-5-|-6-|-7-|-8-|-9-|-A-|-B-|-C-|-D-|-E-|-F-| ^ ^ ^ ^ | nbytes | blocksize | cbytes | | | | | | | | +--typesize | | +------flags | +----------versionlz +--------------version Starting in Blosc 2.0.0, there is an extension of the header above that allows for encoding blocks with a filter pipeline:: 1+|-0-|-1-|-2-|-3-|-4-|-5-|-6-|-7-|-8-|-9-|-A-|-B-|-C-|-D-|-E-|-F-| | filters | ^ | ^ | filters_meta | ^ | ^ | | | | | | +- compcode_meta | +-blosc2_flags +- user-defined codec +-blosc2_flags2 :version: (``uint8``) Blosc format version. :versionlz: (``uint8``) Version of the *format* of the internal compressor used (normally always 1). :flags and compressor enumeration: (``bitfield``) The flags of the buffer :bit 0 (``0x01``): Whether the byte-shuffle filter has been applied or not. :bit 1 (``0x02``): Whether the internal buffer is a pure memcpy or not. :bit 2 (``0x04``): Whether the bit-shuffle filter has been applied or not. :bit 3 (``0x08``): Whether the delta codec has been applied or not. :bit 4 (``0x10``): If set, blocks are *not* split into multiple compressed data streams. :bit 5 (``0x20``): Part of the enumeration for compressors. :bit 6 (``0x40``): Part of the enumeration for compressors. :bit 7 (``0x80``): Part of the enumeration for compressors. Note:: If both bit 0 and bit 2 are both set, that means that an extended header (see above) is used. The last three bits form an enumeration that allows for the use of alternative compressors. :``0``: ``blosclz`` :``1``: ``lz4`` or ``lz4hc`` :``2``: reserved (slot previously occupied by ``snappy`` and free now) :``3``: ``zlib`` :``4``: ``zstd`` :``5``: Reserved :``6``: The compressor is defined in the user-defined codec slot (see below). :``7``: The compressor is defined in the super-chunk. :typesize: (``uint8``) Number of bytes for the atomic type. :nbytes: (``int32``) Uncompressed size of the buffer (this header is not included). :blocksize: (``int32``) Size of internal blocks for regular chunks. When `blosc2_flags2` bit 0 is set, this field stores the number of blocks in the chunk instead. :cbytes: (``int32``) Compressed size of the buffer (including this header). :filters: (``uint8``) Filter ID. :``0``: No shuffle (for compatibility with Blosc1). :``0``: No filter. :``1``: Byte-wise shuffle. :``2``: Bit-wise shuffle. :``3``: Delta filter. :``4``: Truncate precision filter. :``5``: Sentinel. IDs larger than this are either global registered or user-defined filters. The filter pipeline has 6 reserved slots for the filters IDs. They are applied sequentially to the chunk according to their index (in increasing order). The type of filter applied is specified by the ID. Each ID has an associated field in `filters_meta` that can contain metadata about the filter. :udcodec: (``uint8``) User-defined codec identifier. :compcode_meta: (``uint8``) Compression codec metadata. Metadata associated with the compression codec. :filters_meta: (``uint8``) Filter metadata associated to each filter ID. Metadata associated with the filter ID. :blosc2_flags: (``bitfield``) The flags for a Blosc2 buffer. :bit 0 (``0x01``): Whether the codec uses dictionaries or not. :bit 1 (``0x02``): Whether the header is extended with +32 bytes coming right after this byte. :bit 2 (``0x04``): Whether the codec is stored in a byte previous to this compressed buffer or it is in the global `flags` for chunk. :bit 3 (``0x08``): Whether the chunk is 'lazy' or not. :bits 4, 5 and 6: Indicate special values for the entire chunk. :``0``: No special values. :``1``: A run of zeros. :``2``: A run of NaN (Not-a-Number) floats (whether f32 or f64 depends on typesize). :``3``: Run-length of a value that follows the header (i.e. no blocks section). :``4``: Values that are not initialized. :``5``: Reserved. :``6``: Reserved. :``7``: Reserved. :bit 7 (``0x80``): Indicate whether codec has been instrumented or not. :blosc2_flags2: (``bitfield``) Secondary flags for a Blosc2 buffer. :bit 0 (``0x01``): Whether the chunk uses variable-length blocks or not. :bits 1 to 7: Reserved. Blocks ------ The blocks section is composed of a list of offsets to the start of each block, an optional dictionary to aid in compression, and finally a list of compressed data streams:: +=========+======+=========+ | bstarts | dict | streams | +=========+======+=========+ For regular chunks, each block is equal-sized as specified by the `blocksize` header field. The size of the last block can be shorter or equal to the rest. When `blosc2_flags2` bit 0 is set, the chunk uses variable-length blocks instead: - `blocksize` in the header stores the number of blocks - each block still has one entry in `bstarts` - each block is stored in a single compressed stream **Block starts** The *block starts* section contains a list of offsets `int32 bstarts` that indicate where each block starts in the chunk. These offsets are relative to the start of the chunk and point to the start of one or more compressed data streams containing the contents of the block:: +=========+=========+========+=========+ | bstart0 | bstart1 | ... | bstartN | +=========+=========+========+=========+ **Dictionary (optional)** *Only for C-Blosc2* Dictionaries are small datasets that are known to be repeated a lot and can help to compress data in blocks better. The dictionary section contains the size of the dictionary `int32_t dsize` followed by the dictionary data:: +=======+=================+ | dsize | dictionary data | +=======+=================+ **Compressed Data Streams** Compressed data streams are the compressed set of bytes that are passed to codecs for decompression. For regular chunks, each compressed data stream (`uint8_t* cdata`) is stored with the size of the stream (`int32_t csize`) preceding it:: +=======+=======+ | csize | cdata | +=======+=======+ There are a couple of special cases for `int32_t csize`. If zero, that means that the stream is fully made of zeros, *and* there is not a `cdata` section. The actual size of the stream is inferred from `blocksize` and whether or not the block is split. If negative, the stream is stored like this:: +=======+=======+=======+ | csize | token | cdata | +=======+=======+=======+ where **token** is a byte for providing different meanings to `int32_t csize`: :bit 0: Repeated byte (stream is a run-length of bytes). This byte, representing the repeated value in the stream, is encoded in the LSB of the `int32_t csize`. In this case there is not a `cdata` section. Note that repeated zeros cannot happen here (already handled by the `csize == 0` case above). :bits 1 and 2: Reserved for two-codecs in a row. TODO: complete description :bits 3, 4 and 5: Reserved for secondary codec. TODO: complete description :bits 6 and 7: Reserved for future use. If bit 4 of the `flags` header field is set, each block is stored in a single data stream:: +=========+ | stream0 | +=========+ | block0 | +=========+ If bit 4 of the `flags` header is *not* set, each block can be stored using multiple data streams:: +=========+=========+=========+=========+ | stream0 | stream1 | ... | streamN | +=========+=========+=========+=========+ | block0 | +=========+=========+=========+=========+ The uncompressed size for each block is equivalent to the `blocksize` field in the header, with the exception of the last block which may be equal to or less than the `blocksize`. For variable-length-block chunks (`blosc2_flags2` bit 0 set), each block is always stored in a single stream:: +=========+ | stream0 | +=========+ | block0 | +=========+ In this variant: - `csize` stores the uncompressed size of the block - the compressed size is derived from adjacent entries in `bstarts` and the end of the chunk - the special `csize == 0` and `csize < 0` encodings are not used Trailer ------- This is an optional section, mainly for lazy chunks use. A lazy chunk is similar to a regular one, except that only the meta-information has been loaded. The actual data from blocks is 'lazily' only loaded on demand. This allows for improved selectivity, and hence less input bandwidth demands, during partial chunk reads (e.g. `blosc1_getitem`) from data that is on disk. Both regular chunks and variable-length-block (VL-blocks) chunks support lazy loading; the trailer format is identical for both. It is arranged like this:: +=========+=========+========+========+=========+ | nchunk | offset | bsize0 | ... | bsizeN | +=========+=========+========+========+=========+ :nchunk: (``int32_t``) The number of the chunk in the super-chunk. :offset: (``int64_t``) The offset of the chunk in the frame (contiguous super-chunk). :bsize0 .. bsizeN: (``int32_t``) The sizes in bytes for every block. c-blosc2-3.1.5/README_EXTENSION_FILENAMES.rst000066400000000000000000000011741521743436100176430ustar00rootroot00000000000000Extensions for Blosc2 Filenames =============================== Blosc2 has some recommendations for different file extensions for different purposes. Here is a list of the currently supported ones: - `.b2frame` (but also `.b2f` or `.b2`) (Blosc2 Frame): this is the main extension for storing `Blosc2 Contiguous Frames `_. - `.b2nd` (Blosc2 N-Dim): this is just a contiguous frame file with `a metalayer for storing n-dimensional information `_ like shape, chunkshape, blockshape and dtype. c-blosc2-3.1.5/README_FORMAT.rst000066400000000000000000000025171521743436100156760ustar00rootroot00000000000000Blosc2 Format ============= The Blosc2 format is a specification for storing compressed data in a way that is simple to read and parse, and that allows for fast random access to the compressed data. The format is designed to be used with the Blosc2 library, but it is not tied to it, and can be used independently. Emphasis has been put on simplicity and robustness, so that the format can be used in a wide range of applications. See a diagram of a Contiguous Frame (aka CFrame), the most important part of the Blosc2 format below: .. image:: blosc2-cframe.png :width: 25% :alt: Blosc2 CFrame format diagram And here, the list of the different parts of the format, from the highest level to the lowest: - `B2ND format `_ - `B2ND metalayer `_ - `SFrame format `_ - `CFrame format `_ - `Chunk format `_ Finally, the recommended extension file names for the different parts of the format: - `Blosc2 extension file names `_ c-blosc2-3.1.5/README_FUZZER.md000066400000000000000000000011311521743436100155120ustar00rootroot00000000000000How to look into fuzzer issues ============================== * Look into the output of the test case in the logs in CI (look for something like `Test unit written to ./crash-85283a6341337b0ede4bf3b9b2657dcce83ee0b4\nBase64: AiACDQEAAAAPAAAAFAAAABMAbZ0=\n".`). Use the blosc.oss.fuzz@gmail.com account so as to access it. * This output always gives the base64 of the data that reproduced it (e.g. AiACDQEAAAAPAAAAFAAAABMAbZ0=). This can be converted to hex and saved to a file. * This file can be passed as an argument to the corresponding test of `tests/fuzz/` folder, so as to test it locally. c-blosc2-3.1.5/README_SFRAME_FORMAT.rst000066400000000000000000000101251521743436100167250ustar00rootroot00000000000000Blosc2 Sparse Frame Format ========================== Blosc (as of version 2.0.0) has a Sparse Frame (SFrame for short) format that allows for non-contiguous storage of `Blosc2 data chunks `_ on disk. When creating an sparse frame one must denote the `contiguous` flag in `storage` struct as false and provide a name (which represents a directory, but in the future it could be an arbitrary URL) in `storage.urlpath` for the sframe to be stored. It is recommended to name the directory with the `.b2frame` (or `.b2f` for short) extension. A SFrame is made up of a frame index file and the chunks stored in the same directory on-disk. The frame index file follows the format described in the `contiguous frame format `_ document, with the difference that the frame's chunks section is made up of multiple files (one per chunk). The frame index file name is always `chunks.b2frame`, and it also contains the metadata for the sframe. Chunks ------ The chunks are stored in the directory as binary files. Each chunk file name will be composed of the index of the chunk in hexadecimal written in capital letters with a length of 8 characters padded with zeros with the `.chunk` extension. As an example, 15 chunks could be named as follows:: 00000000.chunk, 00000001.chunk, ··· , 0000000E.chunk, 0000000F.chunk Each chunk follows the format described in the `chunk format `_ document. *Note:* The real order of the chunks is in the index chunk and may not follow the order of the names. This can occur when doing an insertion or a reorder. For more information see the **Examples** section below. Examples -------- Structure example ^^^^^^^^^^^^^^^^^ As shown below, an sframe of 4 chunks will be composed of a directory with each chunk file and the frame file:: dir.b2frame/ │ ├── 00000000.chunk │ ├── 00000001.chunk │ ├── 00000002.chunk │ ├── 00000003.chunk │ └── chunks.b2frame Insertion example ^^^^^^^^^^^^^^^^^ When doing an insertion in the nth position, in the same position of the index chunk will be the real chunk index which will be the numbers of chunks that there were before inserting the new one. Following the previous example, it its shown the content of the directory and the index chunk before and after an insertion in the 2nd position:: Before After dir.b2frame/ dir.b2frame/ │ │ ├── 00000000.chunk ├── 00000000.chunk │ │ ├── 00000001.chunk ├── 00000001.chunk │ │ ├── 00000002.chunk ├── 00000002.chunk │ │ ├── 00000003.chunk ├── 00000003.chunk │ │ └── chunks.b2frame ├── 00000004.chunk │ └── chunks.b2frame Possible index New index chunk content: [0, 1, 2, 3] chunk content: [0, 1, 4, 2, 3] Note that neither the file names nor their contents change, so when accessing the 2nd chunk the `00000004.chunk` file will be read. Reorder example ^^^^^^^^^^^^^^^ As in the insertion case, when doing a reorder the chunks names and their contents are not changed, but the content of the index chunk does. When reordering the chunks, a new order list is passed and the index chunk is changed according to that list. Following with the first example of this section, the content of the index chunk is shown before and after reordering:: Before After Possible index New index chunk content: [0, 1, 2, 3] chunk content: [3, 1, 0, 2] New order list: [3, 1, 0, 2] c-blosc2-3.1.5/README_THREADED.rst000066400000000000000000000060521521743436100160640ustar00rootroot00000000000000Blosc supports threading ======================== Threads are the most efficient way to program parallel code for multi-core processors, but also the more difficult to program well. Also, they have a non-negligible start-up time that does not fit well with a high-performance compressor as Blosc tries to be. In order to reduce the overhead of threads as much as possible, I've decided to implement a pool of threads (the workers) that are waiting for the main process (the master) to send them jobs (basically, compressing and decompressing small blocks of the initial buffer). Despite this and many other internal optimizations in the threaded code, it does not work faster than the serial version for buffer sizes around 64/128 KB or less. This is for Intel Quad Core2 (Q8400 @ 2.66 GHz) / Linux (openSUSE 11.2, 64 bit), but your mileage may vary (and will vary!) for other processors / operating systems. In contrast, for buffers larger than 64/128 KB, the threaded version starts to perform significantly better, being the sweet point at 1 MB (again, this is with my setup). For larger buffer sizes than 1 MB, the threaded code slows down again, but it is probably due to a cache size issue and besides, it is still considerably faster than serial code. This is why Blosc falls back to use the serial version for such a 'small' buffers. So, you don't have to worry too much about deciding whether you should set the number of threads to 1 (serial) or more (parallel). Just set it to the number of cores in your processor and your are done! Francesc Alted Pluggable Threading Backend --------------------------- Instead of having Blosc use its *own* thread pool, you can instead call `blosc2_set_threads_callback(threads_callback, callback_data)` to install your own threading backend. This gives Blosc the possibility to use the same threading mechanism as one you are using in the rest of your program (e.g. OpenMP or Intel TBB), sharing the same threads, rather than starting its own threads that compete with yours for the CPU cores. Here, `threads_callback` is a function of the form: .. code-block:: c void threads_callback(void *callback_data, void (*dojob)(void *), int numjobs, size_t jobdata_elsize, void *jobdata) { int i; for (i = 0; i < numjobs; ++i) dojob(((char *) jobdata) + ((unsigned) i)*jobdata_elsize); } that simply calls `dojob` on the given `jobdata` array for `numjobs` elements of size `jobdata_elsize`, returning when all of the `dojob` calls have completed. The key point is that your `threads_callback` routine can execute the `dojob` calls *in parallel* if it wants. For example, if you are using OpenMP your `threads_callback` function might use `#pragma omp parallel for`. The `blosc2_set_threads_callback` function should be called before any Blosc function (before any Blosc contexts are created), to inhibit Blosc from spawning its own worker threads. In this case, `blosc2_set_nthreads` and similar functions set an upper bound to the `numjobs` that is passed to your `threads_callback` rather than an actual number of threads. c-blosc2-3.1.5/RELEASE_NOTES.md000066400000000000000000001640701521743436100155140ustar00rootroot00000000000000Release notes for C-Blosc2 ========================== Changes from 3.1.4 to 3.1.5 =========================== Fixes ----- * Fix decompression of **all-zeros buffers** whose length is not a multiple of ``typesize``. ``read_chunk_header()`` previously rejected ``BLOSC2_SPECIAL_ZERO`` chunks whose ``nbytes`` was not a multiple of ``typesize``. However, all-zeros decompression is just a ``memset`` and works regardless of element alignment, so the check was overly strict for this case (it remains valid for ``SPECIAL_NAN``, ``SPECIAL_VALUE``, and ``UNINIT``, whose callers do enforce alignment). This affected, for example, python-blosc2's ``compress2`` → ``decompress2`` round-trip for all-zeros payloads such as ``bytes(707658)`` with ``typesize=8`` (``707658 % 8 == 2``). A regression test with 9 parametrized cases has been added. Closes Blosc/python-blosc2#665. Notes ----- * This is a maintenance release with no API/ABI changes. Changes from 3.1.3 to 3.1.4 =========================== Security hardening ------------------ This release extends the block-geometry validation introduced in 3.1.3 to two more multidimensional plugin filters: **ndcell** and **ndmean**. * **ndcell**: ``ndcell_forward()`` and ``ndcell_backward()`` now validate the ``b2nd`` metalayer geometry in 64-bit arithmetic, rejecting non-positive block dimensions and blockshapes whose product exceeds the buffer size. This prevents a heap buffer overflow in the decompression path that could be triggered by a crafted blockshape whose 32-bit product wraps back onto ``length``, passing the old validity check while the scatter-write index arithmetic used the true (huge) dimensions. * **ndmean**: Same class of fix applied to ``ndmean_forward()`` and ``ndmean_backward()``. Both now use 64-bit block-size computation with overflow-rejection and non-positive dimension checks at the earliest possible point. Both fixes ship with dedicated regression tests that explicitly craft the overflowing/wrapping geometries to prevent silent regressions. Thanks to @metsw24-max and @saddamr3e for all these improvements. Build / portability ------------------- * Recognize **riscv64** (and riscv32) targets in CMake. RISC-V builds now correctly disable SSE/AVX/NEON/ALTIVEC acceleration and fall back to the generic shuffle path, avoiding miscompilation or silent build failures on this emerging architecture. Thanks to @carlosqwqqwq. Fixes ----- * Fix a **memory leak** in ``create_shared_pool()`` when thread creation fails partway through building a shared thread pool. Previously, a failed ``pthread_create`` leaked the pool struct, thread and context arrays, synchronization primitives, and all per-thread scratch buffers initialized so far. This was most visible under Emscripten/WASM (e.g., Pyodide), where every compress/decompress with ``nthreads > 1`` leaked the partially-built pool. The fix adds a single error-unwind path that properly signals shutdown to already-started workers, joins them, tears down orphaned contexts, and releases all resources. * Fix a potential **NULL thread-pool SEGV** when shared-pool creation fails in ``do_job()``. ``do_job()`` previously ignored ``check_nthreads()``'s return code, so a failed parallel backend (``context->thread_pool == NULL``) would still reach ``parallel_blosc()`` and crash on multi-block data. It now falls back to the serial path when ``rc < 0``. The fallback is self-healing: the next ``do_job()`` call re-attempts the attach. Notes ----- * This is a maintenance release with no API/ABI changes. Changes from 3.1.2 to 3.1.3 =========================== Security hardening ------------------ * Harden ndlz, zfp, and ndmean plugin codecs/filters against malformed or attacker-crafted ``b2nd`` metalayers. The block geometry for these multidimensional plugins is read from the ``b2nd`` metalayer, which is under user/remote control. Without validation, a crafted metalayer could trigger heap buffer overflows or out-of-bounds writes during decompression. * **zfp**: Added ``zfp_check_output_size()`` that validates the b2nd metadata return value, ndim range, typesize, and performs an overflow-safe computation of ``prod(blockshape) * typesize`` against the output buffer size. Non-positive block dimensions are rejected. Compressors now also check deserialize returns and free buffers on every early-return path. A regression test covering oversized, zero-dimension, and int64-overflow blockshapes has been added. * **ndlz**: Both ``ndlz4x4`` and ``ndlz8x8`` decoders now validate the ``b2nd_deserialize_meta`` return value and properly free shape/chunkshape/blockshape buffers (and ``bufarea``) on early-return paths. * **ndmean**: Validates the deserialize return and ndim range, and sizes the shape/chunkshape/blockshape buffers for ``B2ND_MAX_DIM``. * Validate NDLZ decompression input references, preventing potential issues with untrusted or malformed inputs reaching the ndlz4x4 and ndlz8x8 decompressors. Thanks to @metsw24-max for all these improvements. Performance improvements ------------------------ * Use a lazy chunk instead of eagerly reading the whole chunk in the frame code path. This avoids unnecessary I/O and decompression when only part of a chunk is needed. Fixes ----- * Better handling of the ZFP codec workflow when ``input_len`` is smaller than the block size in the zfp plugin. Notes ----- * This is a maintenance release with no API/ABI changes. Changes from 3.1.1 to 3.1.2 =========================== Hotfixes -------- * Fix a regression in ``b2nd_get_orthogonal_selection()`` and ``b2nd_set_orthogonal_selection()`` introduced by the 3.1.1 batching optimization. * Add a regression test covering orthogonal get/set selections across outer dimensions. Notes ----- * This is a hot-fix release with no API/ABI changes. Changes from 3.1.0 to 3.1.1 =========================== Performance improvements ------------------------ * Optimize ``b2nd_get_orthogonal_selection()`` for axis-based row/column selections by reusing the chunk decompression buffer across visited chunks and batching consecutive innermost-dimension element copies. In internal benchmarks, this makes orthogonal selections up to about 2.6x faster (for example, on the ``blosc2.take()`` ndim=2 benchmark with 100M elements, 6388 indices, and ``axis=0``). Documentation improvements -------------------------- * Include static-inline functions in the user-facing API docs, so header-only entry points such as ``b2nd_deserialize_meta_inline()`` show up correctly in the generated reference manual. * Add the public functions that were missing from the reference docs, including recently added sparse getters, VL-block/context helpers, defaults accessors, and utility functions. * Add ``doc/check_missing_docs.py`` to help ensure that public APIs are not accidentally left undocumented in future releases. Notes ----- * This is a maintenance release with no API/ABI changes. Changes from 3.0.3 to 3.1.0 =========================== New features ------------ * New sparse coords getter API for extracting arbitrary sets of coordinates in a single call, much faster than repeated individual `getitem` operations: - ``blosc2_schunk_get_sparse_buffer()`` extracts a set of flat (1-dimensional) coordinates from a schunk into a caller-provided buffer. It batches coordinates by chunk internally to minimize decompression overhead. - ``b2nd_get_sparse_cbuffer()`` does the same for multidimensional (b2nd) arrays, accepting an array of *n*-dimensional coordinates and returning the corresponding values into a C buffer. Both APIs support regular and special-value (zero/NaN/run-length) schunks, as well as frame-backed and callback-threaded schunks. * ``b2nd_deserialize_meta_inline()`` — a new static-inline version of ``b2nd_deserialize_meta()`` available from the header ``b2nd.h`` without linking against ``libblosc2``. Together with the already-inline ``blosc2_meta_get()``, this lets external codec/filter plugins (like ``blosc2_grok``) use the b2nd metadata API without pulling all of ``libblosc2``'s internal symbols (ZFP, Zstd, …) into the global namespace, which could otherwise shadow symbols from other libraries that need differently-configured builds of the same dependencies. The existing ``b2nd_deserialize_meta()`` ABI entry point is preserved as a thin wrapper for backward compatibility. * Globally registered codec IDs **J2K** (124) and **HTJ2K** (125) for upcoming JPEG 2000 / High-Throughput JPEG 2000 plugins. Thanks to @alemirone. Fixes ----- * Fix ``swap_store()`` for big-endian machines. * Fix divide-by-zero in ``b2nd_update_shape``. Thanks to @metsw24-max. * Fix trailer vlmetalayer-parsing and a NULL-check on a missing allocation. * Rename ``blosc2_schunk_get_sparse`` → ``blosc2_schunk_get_sparse_buffer`` in the test file and error messages for consistency with the public header. Thanks to @metsw24-max. Changes from 3.0.2 to 3.0.3 =========================== * Harden frame parsing against malformed trailer lengths, invalid metalayer offsets and lengths, and lazy-chunk compressed sizes that extend past the end of a frame. This prevents invalid offset computation, out-of-bounds reads, and a double-free on malformed inputs. Thanks to @metsw24-max. * Fix integer-overflow paths in b2nd chunk-size and slice-buffer calculations that could otherwise lead to heap corruption or invalid allocations. Thanks to @metsw24-max. * Improve stdio and mmap I/O safety by validating NULL streams, file sizes, offsets, and integer conversions, and by tightening cleanup/error paths. Thanks to @metsw24-max. * Replace unsafe string formatting/concatenation in compressor listing, codec-version reporting, directory helpers, and frame path handling with bounded operations and allocation checks. Thanks to @metsw24-max. * Validate negative indices in offset reordering to reject malformed offset tables more reliably. Thanks to @uwezkhan. * Add regression tests covering malformed frame trailer/metalayer data, lazy-chunk bounds, b2nd chunk-size overflows, stdio validation, mmap error handling, and negative reorder offsets. Changes from 3.0.1 to 3.0.2 =========================== * Fix for windows when using ctx API from multiple threads. Closes #763. Thanks to Christoph Gohlke (@cgohlke). * Harden metalayer APIs against invalid lengths and unsafe memory usage. PR #758. Thanks to @metsw24-max. * Fix DELTA pipelines after byte-transforming filters (e.g. shuffle). Changes from 3.0.0 to 3.0.1 =========================== * Add BLOSC_DEPENDENCY_MODE to choose BUNDLED, EXTERNAL, or AUTO dependency resolution, defaulting to BUNDLED for compatibility. * Add BLOSC_ENABLE_ZFP to make ZFP optional in external builds while still allowing users to require or disable it explicitly. * Add external ZFP discovery and improve LZ4/Zstd find modules so external builds provide proper imported targets and include dirs. * Avoid dependency CMake variables polluting the Blosc2 package install dir, and install Blosc2 config files under the expected Blosc2 path. * Document the new options for distro packagers. Changes from 3.0.0-rc2 to 3.0.0 =============================== * Fixes an integer overflow in VL-block decompression where cumulative blocknbytes was tracked in a 32-bit integer. PR #753. Thanks to @metsw24-max. * Fix data races in global configuration APIs using mutex protection. PR #753. Thanks to @metsw24-max. * Different typos fixed. Thanks to @DimitriPapadopoulos. Changes from 3.0.0-rc1 to 3.0.0-rc2 =================================== * `blosc2_get_slice_nchunks()`, `schunk_get_slice_nchunks()`, and `b2nd_get_slice_nchunks()` now return `int64_t` instead of `int`. This removes an artificial `INT_MAX` limit on the number of chunks reported for large slices. Because these public signatures changed, this is an API/ABI break and callers should be rebuilt against 3.0.0. * The internal parallel execution model has been reworked around a shared managed thread pool. Instead of owning a private worker pool per compression/decompression context, contexts now attach lazily to a pool shared by other contexts with the same `nthreads` setting, while caller-managed callback threading remains supported as before. This greatly reduces redundant thread creation and idle thread accumulation when many contexts coexist, improves scalability and resource usage, and fixes reliability problems in downstream workloads such as python-blosc2 that create large numbers of arrays/contexts over time. The new queue-based scheduling model also supports true concurrent submissions to the same pool. * Landed a broad set of robustness and security hardening fixes in frame, schunk, lazy-chunk, metadata, mmap, and getitem paths. These changes tighten bounds checking, reject malformed headers/offset tables/VL-block metadata more aggressively, prevent integer overflows and out-of-bounds reads/writes, and add regression tests for malformed inputs and edge cases. Thanks to @metsw24-max for many of these. * Modernized codec dependency handling in CMake. `lz4`, `zlib-ng`, and `zstd` are now resolved either from external packages (when preferred and available) or via `FetchContent` using pinned upstream versions, instead of being built from vendored in-tree copies. The optional ZFP plugin is now also obtained via `FetchContent` too. As a result, `blosclz` is now the only codec still vendored in-tree. * Added explicit CMake cache variables for pinned codec versions and local source overrides: `BLOSC_LZ4_VERSION`, `BLOSC_ZLIBNG_VERSION`, `BLOSC_ZSTD_VERSION`, and the matching `BLOSC_*_SOURCE_DIR` variables. * Improved CMake install/export support for static builds so downstream `find_package(Blosc2)` consumers keep working when fetched codec libraries are embedded into the Blosc package. * Embedded third-party headers installed by the CMake package are now placed under a Blosc-owned include subtree (`blosc2/thirdparty/...`) instead of the top-level include directory, reducing the risk of header name collisions. * Replaced deprecated `exec_program()` usage in `cmake/FindSIMD.cmake` with `execute_process()`, avoiding warnings with newer CMake versions. * Removed the unmaintained Intel IPP integration. C-Blosc2 no longer exposes a CMake option to enable/disable IPP, does not probe for IPP at configure time, and always uses the maintained native codec paths instead. Changes from 2.23.1 to 3.0.0-rc1 ================================ * This release introduces support for variable-length chunks and variable-length blocks, which is the main reason for the major version bump. Until now, a schunk/frame generally assumed that all chunks shared the same logical chunk size, and regular Blosc2 chunks assumed fixed-size internal blocks (except for the last remainder block). In 3.0.0-rc.1, schunks can switch to variable chunk sizes when needed, and there is also a new chunk layout for variable-length blocks (VL-blocks), where each block can carry a different uncompressed size inside the same chunk. This is especially useful for workloads made of naturally variable-size pieces of data, like strings, records, JSON fragments, or other irregular payloads that previously had to be padded, split awkwardly, or stored as independent chunks. The new layout keeps these pieces grouped together while still making them individually recoverable. Together with this, there are new public APIs for VL-block chunks: `blosc2_vlcompress_ctx()`, `blosc2_vldecompress_ctx()`, `blosc2_vlchunk_get_nblocks()`, `blosc2_vldecompress_block_ctx()`, and `blosc2_schunk_get_vlblock()`. Lazy loading also works with VL-block chunks, so individual blocks can be fetched on demand without materializing the whole chunk first. * The chunk and cframe formats have been extended to represent variable chunk sizes, VL-block chunks, and dictionary usage more explicitly. Forward compatibility checks were tightened as part of this work, and regular chunks keep their previous stable format version while VL-block chunks use a new one. * Dictionary compression has been expanded and improved: `use_dict` now works with LZ4 and LZ4HC in addition to ZSTD, the dictionary state is preserved correctly across chunk compression/decompression, and the frame metadata now round-trips the dictionary setting. There is also a new minimum useful dictionary threshold to avoid training or using dictionaries that are too small to help. * The necessary changes for accommodating all these improvements have been fully documented in README_CHUNK_FORMAT.md and README_CFRAME_FORMAT.md. Again, care has been taken to ensure that the chunk and frame formats are backward compatible with previous versions of C-Blosc2. * Fixed several safety issues in the core library, as well as in ndcell/ndlz plugins. Thanks to Trail of Bits (in collaboration with Anthropic). * Additional compatibility and portability fixes include better protection against unsupported future chunk/frame versions and proper VSX shuffle support detection on big-endian ppc64. Thanks to @AutoJanitor for these. Changes from 2.23.0 to 2.23.1 ============================= * Fix some memory leaks/undefined behaviour * Allow resizing of empty arrays * `filters_meta` (interpreted as number of bytestreams) for the byteshuffle filter Changes from 2.22.0 to 2.23.0 ============================= * Changes to allow use of miniexpr. This breaks the ABI as a public struct has an additional field. * Changes to enable blosc2-openzl plugin Changes from 2.21.3 to 2.22.0 ============================= * `b2nd_squeeze` and `b2nd_squeeze_index` now return views and their signatures have consequently changed. * bug fix for indexing `[ndim - 1]` which caused an error when `ndim` is 0 Changes from 2.21.2 to 2.21.3 ============================= * Increase MAX_DIMS from 8 to 16 * Fix compatibility with glibc v2.42 * Bug fix in ``unidim_to_multidim`` Changes from 2.21.1 to 2.21.2 ============================= * Allocate strides array on stack if ndim <= B2ND_MAX_DIM in unidim_to_multidim. Should allow to speed up code. Thanks to Barak Ugav. * Handle frame_to_schunk failure in schunk_open. Thanks to Barak Ugav. * Fix fastpath decision logic for ndarray slicing. Thanks to Preeyan Parmar. Changes from 2.21.0 to 2.21.1 ============================= * Fixed a regression introduced in PR #682 causing incompatibility at least with Fedora and Gentoo. Fixed in PR #690. Thanks to Michał Górny (@mgorny) for reporting and Teodor Tsirpanis (@teo-tsirpanis) for the actual fix. * Restrict overmatching MACH ifdef to only trigger on OSX and Mach. Thanks to Maximilian Downey Twiss (@Zopolis4) for the PR (#688). Changes from 2.20.0 to 2.21.0 ============================= * New public API `b2nd_save_append`. It is useful for appending a b2nd array into a file. Thanks to Barak Ugav (@barakugav) for the PR (#683). * Added new public APIs for the shuffling filters: * `blosc2_shuffle` * `blosc2_unshuffle` * `blosc2_bitshuffle` * `blosc2_bitunshuffle` Thanks to Theodore Tsirpanis (@teo-tsirpanis) for the PR (#681). * Fix a potential nullptr exception on `b2nd_create_ctx` with null storage. Thanks to Barak Ugav (@barakugav) for the PR (#684). * The CMake dependency acquisition has been improved. Thanks to Theodore Tsirpanis (@teo-tsirpanis) for the PR (#682). * Now, a blosc/config.h is generated in the binary directory. Fixes failures when configuring from the same source directory in parallel. Thanks to Theodore Tsirpanis (@teo-tsirpanis) for the PR (#679). Changes from 2.19.1 to 2.20.0 ============================= * Extended `b2nd_expand_dims()` to allow for list of axes like NumPy. See PR #676. This represents a breaking change for the API, but as it was introduced in 2.18.0, it is unlikely to be used in practice. Thanks to Luke Shaw (@lshaw8317). * Fix detection of AVX512 in MSVC. See PR #678. Thanks to Theodore Tsirpanis (@teo-tsirpanis). * Add unified Blosc2::blosc2 CMake target. See PR #677. Thanks to Theodore Tsirpanis (@teo-tsirpanis). * Rename windows pthread functions and blosc_pthread_{xyz}. Fixes #673 (Duplicate symbols for pthread functions on Windows). Thanks to Barak Ugav (@barakugav). Changes from 2.19.0 to 2.19.1 ============================= * New `blosc2_error_string()` public function for getting the error string corresponding to a given error code. This is useful for debugging purposes. Thanks to Barak Ugav (@barakugav). * Support BLOSC2_ERROR_TUNER in print_error. Thanks to Barak Ugav (@barakugav). * Eagerly register io_cb's to avoid race. Thanks to Tom Birch (@froody). Changes from 2.18.0 to 2.19.0 ============================= * New `b2nd_expand_dims()` function for expanding the dimensions of a b2nd array. This is useful for adding new dimensions to an existing b2nd array, e.g., when you want to add a new dimension to a 2D array to make it 3D. Thanks to @lshaw8317 for the PR (#666 and #667). * New internal view object that allows accessing the data in a b2nd array without copying it. The new `b2nd_expand_dims()` function makes use of this. In the future, it can also be useful for implementing e.g. a view on a slice of a b2nd array. Changes from 2.17.1 to 2.18.0 ============================= * New `b2nd_concatenate()` function for concatenating two b2nd arrays. See PR #661 and #663. * Flush modified pages only in write mode for mmap files. Thanks to Jan Sellner. Changes from 2.17.0 to 2.17.1 ============================= Several fixes affecting uninitialized memory access and others: * Fix uninitialized memory access in newly added unshuffle12_sse2 and unshuffle12_avx2 functions * Fix unaligned access in _sw32 and sw32_ * Fix DWORD being printed as %s in sprintf call * Fix warning on unused variable (since this variable was only being used in the linux branch) * `splitmode` variable was uninitialized if goto was triggered See PR #658. Many thanks to @EmilDohne for this nice job. Changes from 2.16.0 to 2.17.0 ============================= * New b2nd_copy_buffer2() function for copying buffers with typesizes larger than 255. The previous b2nd_copy_buffer() function is now deprecated and will be removed in a future release. * Support repeated values larger than 8-bit, also for n-dim arrays. This is useful for compressing arrays with large runs of repeated values, like in the case of images with large areas of the same color. * Fix a leak in the pthreads emulation for Windows. Fixes #647. Thanks to @jocbeh for the report and fix (#655). * Update zstd to 1.5.7. Thanks to Tom Birch. * Add BLOSC2_MAXTYPESIZE constant. ### Deprecated Functions - `int b2nd_copy_buffer(...)` is deprecated and will be removed in a future release. Please use `b2nd_copy_buffer2(...)` instead. Changes from 2.15.2 to 2.16.0 ============================= * Use _fseeki64/_ftelli64/_stat64 on Windows for large file (>2 GB) support. Thanks to Abhi Jaiantilal (@ajaiantilal) for the report and help. * Add 12-byte unshuffle for avx2. Thanks to Tom Birch (@froody). * Add 12-byte sse2 unshuffle implementation. Thanks to Tom Birch (@froody). * Better description of the Blosc2 format as a whole. Changes from 2.15.1 to 2.15.2 ============================= * Support wasm32 by disabling ZLIB WITH_OPTIM option. Thanks to Miles Granger. * Avoid rip-relative addressing for OSX x86_64. Thanks to Miles Granger. * Added support for nvcc (NVidia Cuda Compiler) in CMake. Thanks to @dqwu. * Fix public include directories for blosc2 targets. Thanks to Dmitry Mikushin. * Fix ub in shuffle and unshuffle by marking _dst non-const. Thanks to Emil Dohne. Changes from 2.15.0 to 2.15.1 ============================= * Do not pass `-m` flags when compiling `shuffle.c`. This prevents the compiler from incidentally optimizing the code called independently of the runtime CPU check to these instruction sets, effectively causing `SIGILL` on other CPUs. Fixes #621. Thanks to @t20100 and @mgorny. * Internal LZ4 sources bumped to 1.10.0. * Allow direct loading of plugins by name, without relying on the presence of python. Thanks to @boxerab. * Add `b2nd_nans` method (PR #624). Thanks to @waynegm. Changes from 2.14.4 to 2.15.0 ============================= * Removed some duplicated functions. See https://github.com/Blosc/c-blosc2/issues/503. * Added a new io mode to memory map files. This forced to change the `io_cb` read API. See https://github.com/Blosc/c-blosc2/blob/main/tests/test_mmap.c to see an example on how to use it. * Updated the `SOVERSION` to 4 due to the API change in `io_cb` read. * Added functions to get cparams, dparams, storage and io defaults respectively. * Internal zstd sources updated to 1.5.6. * Fixed a bug when setting a slice using prefilters. Changes from 2.14.3 to 2.14.4 ============================= * Bumped SONAME due to recent API changes. See https://github.com/Blosc/c-blosc2/issues/581. Changes from 2.14.2 to 2.14.3 ============================= * More fixes for internal fuzzer. Changes from 2.14.1 to 2.14.2 ============================= * Fixes for CVE-2024-3203 and CVE-2024-3204. Changes from 2.14.0 to 2.14.1 ============================= * When loading plugins, first try with `python` and then `python3`. This is because many linux distros do not have `python` as a symlink to `python3` anymore. Changes from 2.13.2 to 2.14.0 ============================= * Fixed a bug preventing buffers to be appended to empty (0-sized) b2nd arrays. * New acceleration path for `b2nd_append()`. This new path is much faster (up to 4x) than the previous one, specially for large arrays. See `bench/bench_stack_append.c` for the bench of use. * New examples for using the `b2nd_set_slice_cbuffer()` and `b2nd_append()` functions for adding data into existing b2nd arrays. See `examples/example_stack_images.c`. * Now, ``python3`` is used for finding plugins instead of ``python``. This is because many linux distros do not have ``python`` as a symlink to ``python3`` anymore. * New round of fixing warnings. Now, C-Blosc2 should be relatively free of them. * Small performance tweak for clevel 1 in BloscLZ codec. * Fixed a leak in frame code. Closes #591. Thanks to @LuMingYinDetect. * Disable shuffle repeat in filters pipeline. This was broken since the initial implemented, and it was never documented. Also, compression ratios do not seem to be improved in our experiments, so this capability has been removed completely. * Support for new Intel compilers (2023.0.1 and on). Fixes #533. Thanks to Nick Papior. Changes from 2.13.1 to 2.13.2 ============================= * Better checking for `SSSE3` availability in Visual Studio. Probably fixes #546 too. Thanks to @t20100 (Thomas Vincent) for the PR (#586). * Documented the globally registered filters and codecs. See: https://www.blosc.org/c-blosc2/reference/utility_variables.html#codes-for-filters https://www.blosc.org/c-blosc2/reference/utility_variables.html#compressor-codecs Changes from 2.13.0 to 2.13.1 ============================= * Removed private include in `b2nd.h`. This fixes issue #579. Changes from 2.12.0 to 2.13.0 ============================= * Added a new BLOSC_FILTER_INT_TRUNC filter for truncating integers to a given number of bits. This is useful for compressing integers that are not using all the bits of the type. See PR #577. * Optimized zstd, specially when using dicts. See PR #578. * Initialize grok library when loading the plugin. This is needed for other plugins to be able to use it without the need of importing the package. Changes from 2.11.3 to 2.12.0 ============================= * New `blosc2_get_slice_nchunks` function for getting the unidimensional chunk indexes of a Blosc2 container slice. * Globally registered new codec `grok`. This will be loaded dynamically. Changes from 2.11.2 to 2.11.3 ============================= * Frames accept now typesizes that are larger than 255 (and up to 2**31). See https://github.com/PyTables/PyTables/issues/1086. Thanks to @chris-allan for the report. * AVX512 runtime dispatching has been fixed (basically disabled) for GCC versions <= 10. * Use typedef for blosc_timestamp_t. Thanks to Magnus Ulimoen. Changes from 2.11.1 to 2.11.2 ============================= * Added support for ARMv7l platforms (Raspberry Pi). The NEON version of the bitshuffle filter was not compiling there, and besides it offered no performance advantage over the generic bitshuffle version (it is 2x to 3x slower actually). So bitshuffle-neon.c has been disabled by default in all ARM platforms. * Also, unaligned access has been disabled in all ARM non-64bits platforms. It turned out that, at least the armv7l CPU in Raspberry Pi 4, had issues because `__ARM_FEATURE_UNALIGNED` C macro was asserted in the compiler (both gcc and clang), but it actually made binaries to raise a "Bus error". * Thanks to Ben Nuttall for providing a Raspberry Pi for tracking down these issues. Changes from 2.11.0 to 2.11.1 ============================= * Fix ALTIVEC header. Only affects to IBM POWER builds. Thanks to Michael Kuhn for providing a patch. Changes from 2.10.5 to 2.11.0 ============================= * New AVX512 support for the bitshuffle filter. This is a backport of the upstream bitshuffle project (https://github.com/kiyo-masui/bitshuffle). Expect up to [20% better compression speed](https://github.com/Blosc/c-blosc2/pull/567#issuecomment-1789239842) on AMD Zen4 architecture (7950X3D CPU). * Add c-blosc2 package definition for Guix. Thanks to Ivan Vilata. * Properly check calls to `strtol`. Fixes #558. * Export the `b2nd_copy_buffer` function. This may be useful for other projects dealing with multidimensional arrays in memory. Thanks to Ivan Vilata. * Better check that nthreads must be >= 1 and <= INT16_MAX. Fixes #559. * Fix compile arguments for armv7l. Thanks to Ben Greiner. Changes from 2.10.4 to 2.10.5 ============================= * Fix a variable name in a test that was causing a segfault in some platforms. * Change tuner's functions signature to return always an error code. This allows for better error checking when using pluggable tuners in Blosc2. * Do checks when creating contexts. Changes from 2.10.3 to 2.10.4 ============================= * Remove duplicated tune initialization since it is already done in blosc2_create_cctx. Thanks to Marta Iborra * Typos fixed. Thanks to Dimitri Papadopoulos. Changes from 2.10.2 to 2.10.3 ============================= * Globally registered new codec `openhtj2k`. This will be loaded dynamically. See PR #557. * Added a `BLOSC_INFO` macro for details on compression params. * Added `get_blocksize.c` example on automatic blocksizes. * Warning fixes. * Fixes for mingw. Changes from 2.10.1 to 2.10.2 ============================= * Several fixes for the CMake system. Thanks to Axel Huebl. See PR #541 and #542. * Several fixes for mingw platform. Thanks to Biswapriyo Nath. See PR #540 and #543. Changes from 2.10.0 to 2.10.1 ============================= * `blosc2_remove_urlpath(const char *urlpath)` does not return an error when path does not exist. * Changes in CMake installer to conserve targets and properties on install, so CMake users do not need to write `FindBlosc2.cmake` files anymore. This also helps to preserve transitive dependencies on CMake targets, especially useful for fully static builds, e.g., for Python wheels. Thanks to @ax3l (Axel Huebl). See PR #537. * Fix new typos. Thanks to @DimitriPapadopoulos. See PR #538. Changes from 2.9.3 to 2.10.0 ============================ * bytedelta filter has been fixed. For backward compatibility, the old bytedelta filter is still available as `BLOSC_FILTER_BYTEDELTA_BUGGY` symbol, with the same ID (34) than before. The new, fixed bytedelta filter has received a new ID (35) and it can be used via the usual `BLOSC_FILTER_BYTEDELTA` symbol. That means that old data written with the buggy bytedelta filter should be decompressed without issues. Thanks to @foody (Tom Birch) for the fix. See #531, #532 for more info. * Filter buffers are correctly cycled now. Now it is possible to use e.g. shuffle and bitshuffle filters in the pipeline. Thanks to @foody (Tom Birch) for the fix. See #528 and PR #530. * Assorted fixes for allowing better inclusion in external projects. Thanks to @ax3l (Axel Huebel). See #525, #527 and #529. * Minor fixes in the documentation. Thanks to @ivilata (Ivan Vilata). See #523. Changes from 2.9.2 to 2.9.3 =========================== * Thanks to Dimitri Papadopoulos for an extensive set of improvements in documentation and code. * `load_lib` is now a private function. Before was public, but never meant to be. * Several fixes for bugs discovered by the fuzzer. Changes from 2.9.1 to 2.9.2 =========================== * Now is possible to register the same plugin (as long as they have the same ID *and* name) without errors. This is useful for registering the same plugin without worrying on whether it has been registered already. * Improved detection of dynamic plugin locations. Now they must implement `plugin_module.print_libpath()` as the canonical way to find the path for the dynamic library plugin. * The `blosc2_static` has gained the cmake POSITION_INDEPENDENT_CODE property. This should allow to use the static library in more situations. * `BLOSC_STUNE` is defined in `blosc2.h` now. Fixes #481. Thanks to @DimitriPapadopoulos. * Fixed an issue when having incompressible data in combination with lazy_chunks. * Fix linking with static -DBUILD_STATIC=0. Fixes #480. * Visual Studio 2010 (version 10.0) has been deprecated. Now, users will need to use Visual Studio 2012 (version 11.0) or later. * Many small fixes and code improvements. Thanks to @DimitriPapadopoulos, @bnavigator. Changes from 2.9.0 to 2.9.1 =========================== * Allow the use of BTUNE by detecting the ``BTUNE_BALANCE`` environment variable. Changes from 2.8.0 to 2.9.0 =========================== * Dynamic plugins as Python wheels are supported now! This new feature allows for creating plugins in C, distribute them as wheels, and load them dynamically in runtime. Small example at https://github.com/Blosc/blosc2_plugin_example * BloscLZ can achieve more speed in clevel 1 now. * Internal Zstd sources updated to latest 1.5.5 version. * Copyright notice updated. Thanks to @DimitriPapadopoulos. Changes from 2.7.1 to 2.8.0 =========================== * New bytedelta filter added. SIMD support for Intel and ARM platforms is there. We have blogged about this: https://www.blosc.org/posts/bytedelta-enhance-compression-toolset.rst Thanks to Aras Pranckevičius for inspiration and initial implementation. * Minor improvements in BloscLZ, leading to better compression ratios in general. BLoscLZ version bumped to 2.5.2. * Updated internal zlib-ng to 2.0.7. * Used `const` qualifier where possible in b2nd. Thanks to @cf-natali. Changes from 2.6.1 to 2.7.1 =========================== * Caterva has been merged and carefully integrated in C-Blosc2 in the new b2nd interface. For more info on the new interface, see https://www.blosc.org/c-blosc2/reference/b2nd.html. Thanks to Marta Iborra, Oscar Guiñón, J. David Ibáñez and Francesc Alted. Also thanks to Aleix Alcacer for his great work in the Caterva project. We have a blog about this: https://www.blosc.org/posts/blosc2-ndim-intro * Updated internal zstd sources to 1.5.4. Thanks to Dimitri Papadopoulos. * `blosc2_schunk_avoid_cframe_free` and `blosc2_schunk_append_file` are exported as public functions now. Thanks to @bnavigator. * BloscLZ codec is now treated exactly the same as LZ4. Before BloscLZ was considered less capable of reaching decent compression ratios, but this has changed quite a bit lately, so there is no point in treating both differently. * Fixed some leaks, mainly on the test suite. * Fixed quite a bit of compiler warnings. Changes from 2.6.0 to 2.6.1 =========================== * Add support for macos universal2 binaries (arm64+x86_64 build). Thanks to Thomas Vincent. Changes from 2.5.0 to 2.6.0 =========================== * [API] Now it is possible to pass filter ID to a User Defined Filter. * Unified convention for BLOSC_SPLITMODE environment variable in Blosc and Blosc2. The list of valid values is "ALWAYS", "NEVER", "AUTO" and "FORWARD_COMPAT" now. * Unified convention for BLOSC_SPLITMODE enum in Blosc and Blosc2 headers. Changes from 2.4.3 to 2.5.0 =========================== * Fixed a nasty bug that prevented retrieving data correctly with large super-chunks (> 2^31 elements). * Fixed an issue in `blosc2_schunk_get_slice_buffer()` in the interpretation of the `stop` param. Now `stop` is not part of the selected slice (as advertised). * Now `blosc2_create_cctx()` supports the same environment variables than `blosc2_compress()`. * Now `blosc2_create_dctx()` supports the same environment variables than `blosc2_decompress()`. * Added support for the split mode to be serialized in cframes/sframes. * A new `splitmode` field has been added to the `blosc2_schunk` structure. * Changed some fields in `blosc2_preparams` and `blosc2_postparams` structs: * `in` -> `input` * `out` -> `output` * `out_size` -> `output_size` * `out_typesize` -> `output_typesize` * `out_offset` -> `output_offset` This was needed to allow Cython to map the fields (`in` is a reserved word in Python). * Disabled maskout reads in `blosc2_schunk_get_slice_buffer()` as they are not faster than getitem there. * Add an intermediate block size in its automatic calculation based on `clevel`. Changes from 2.4.2 to 2.4.3 =========================== * Disable automatic split of blocks when not using shuffle. Experiments are showing that cratio is suffering too much, specially when using BloscLZ. * Changed xxhash.h and xxhash.c to the newest version. Thanks to Dimitri Papadopoulos. Changes from 2.4.1 to 2.4.2 =========================== * Fixed BLOSC1_COMPAT mode. If the symbol `BLOSC1_COMPAT` is defined, one should actually get a correct map for the BLOSC1 API now. Thanks to @MehdiChinoune for pointing this out. * Optimizations for `blosc2_schunk_get_slice_buffer()`. Now more caution has been put in avoiding memcpy's as much as possible. * Now `blosc2_set_nthreads()` also modifies the schunk compression and decompression defaults. * Several other fixes, specially when converting schunks to cframe format. Changes from 2.4.0 to 2.4.1 =========================== * New `blosc2_schunk_avoid_cframe_free()` for avoiding the free of a cframe when destroying a super-chunk. Mainly useful for situations where you build a super-chunk out of an existing cframe, so you don't want it to be freed automatically. Changes from 2.3.1 to 2.4.0 =========================== * New `blosc2_schunk_get_slice_buffer()` and `blosc2_schunk_set_slice_buffer()` functions for getting and setting slices from/to a super-chunk. Changes from 2.3.0 to 2.3.1 =========================== * Support for negative values for BLOSC_TRUNC_PREC filter. Negatives values mean reduce mantissa precision bits, whereas positive values mean keep precision bits. * Re-add the check for small buffers and a new test (see https://github.com/Blosc/python-blosc2/issues/46). * Make `static` a couple of funcs to avoid collisions. This can be useful in case someone tries to compile both C-Blosc and C-Blosc2 in the same name space (however, don't do that unless you know what you are doing; better use dynamic libraries which allow for a much better name space separation). Changes from 2.2.0 to 2.3.0 =========================== * [API change] In order to allow to compile with both C-Blosc and C-Blosc2 libraries, a new API has been created for the symbols and function names that had collisions. Here are the changed symbols and functions: * Blosc2 symbols that take different values than in Blosc1: - BLOSC_VERSION_MAJOR -> BLOSC2_VERSION_MAJOR - BLOSC_VERSION_MINOR -> BLOSC2_VERSION_MINOR - BLOSC_VERSION_RELEASE -> BLOSC2_VERSION_RELEASE - BLOSC_VERSION_STRING -> BLOSC2_VERSION_STRING - BLOSC_VERSION_DATE -> BLOSC2_VERSION_DATE - BLOSC_MAX_OVERHEAD -> BLOSC2_MAX_OVERHEAD - BLOSC_MAX_BUFFERSIZE -> BLOSC2_MAX_BUFFERSIZE * Original Blosc1 API that takes the `blosc1_` prefix: - blosc_compress -> blosc1_compress - blosc_decompress -> blosc1_decompress - blosc_getitem -> blosc1_getitem - blosc_get_compressor -> blosc1_get_compressor - blosc_set_compressor -> blosc_set_compressor - blosc_cbuffer_sizes -> blosc1_cbuffer_sizes - blosc_cbuffer_validate -> blosc1_cbuffer_validate - blosc_cbuffer_metainfo -> blosc1_cbuffer_metainfo - blosc_get_blocksize -> blosc1_get_blocksize - blosc_set_blocksize -> blosc1_set_blocksize - blosc_set_splitmode -> blosc1_set_splitmode * API that has been migrated to blosc2_ prefix - blosc_init -> blosc2_init - blosc_destroy -> blosc2_destroy - blosc_free_resources -> blosc2_free_resources - blosc_get_nthreads -> blosc2_get_nthreads - blosc_set_nthreads -> blosc2_set_nthreads - blosc_compcode_to_compname -> blosc2_compcode_to_compname - blosc_compname_to_compcode -> blosc2_compname_to_compcode - blosc_list_compressors -> blosc2_list_compressors - blosc_get_version_string -> blosc2_get_version_string - blosc_get_complib_info -> blosc2_get_complib_info - blosc_cbuffer_versions -> blosc2_cbuffer_versions - blosc_cbuffer_complib -> blosc2_cbuffer_complib It is recommended to migrate to the new API as soon as possible. In the meanwhile, you can still compile with the previous API (corresponding to C-Blosc2 pre-2.3.0), by defining the `BLOSC1_COMPAT` symbol in your C-Blosc2 app (before including the 'blosc2.h' header). * Fixed some issues in converting from super-chunks to frames and back. Now it is possible to do a roundtrip without (known) problems. * LZ4 codec has been bumped to 1.9.4. Changes from 2.1.1 to 2.2.0 =========================== * Added new `blosc2_schunk_open_offset()` and `blosc2_schunk_append_file()` functions for being able to open a super-chunk inside of file, and append a super-chunk to the end of an existing file. See https://github.com/Blosc/c-blosc2/pull/409. * Protect the update of a field in compression/decompression context. Fixes issues when compressing/decompressing super-chunks in multi-thread mode. * Fix issue when inserting a chunk in a super-chunk. See https://github.com/Blosc/c-blosc2/pull/408. * Fix issue when appending a special chunk in a super-chunk. See https://github.com/Blosc/c-blosc2/pull/407. * Optimized the blockshape calculation when using the ZFP plugin in `BLOSC_CODEC_ZFP_FIXED_RATE` mode. See https://github.com/Blosc/c-blosc2/pull/406. * New `blosc2_unidim_to_multidim` and `blosc2_multidim_to_unidim` which are useful for codecs that are meant to deal with multidimensional arrays (like ZFP). * Another round of squashing warnings has been carried out. Thanks to Marta Iborra. * Added locks in situations where different threads were trying to update the same variable. Thanks to Marta Iborra (and helgrind!). * Use proper types for native zlib-ng interface. This allows for linking with zlib-ng native API. Thanks to Klaus Zimmermann. Changes from 2.1.0 to 2.1.1 =========================== * Fixed a nasty bug introduced in 2.1.0 that affects to the blosclz codec in ARM arch. See https://github.com/Blosc/c-blosc2/issues/392. * Added support for external zlib-ng (see https://github.com/Blosc/c-blosc2/pull/393). Thanks to Mark Harfouche. * Fixed more compiler warnings (https://github.com/Blosc/c-blosc2/pull/394). Now C-Blosc2 should be relatively free of them. * Fixed more fuzzer issues. Changes from 2.0.4 to 2.1.0 =========================== * Support of the [ZFP lossy codec](https://zfp.readthedocs.io/) as a plugin. This is mainly meant for compressing multidimensional arrays of floating point data, so it is better used in combination with [Caterva](https://github.com/Blosc/caterva). More info at our blog: https://www.blosc.org/posts/support-lossy-zfp/. Thanks to Oscar Guiñon and Francesc Alted. * Massive cleanup of warnings. Thanks to Marta Iborra. * Fixed some bugs in test updates and insertions in 64-bit super chunks. Thanks to Francesc Alted. * Fixed the build on FreeBSD/powerpc64le. Thanks to @pkubaj. * Updated internal zstd sources to 1.5.2. * Updated internal zlib-ng to 2.0.6. Changes from 2.0.3 to 2.0.4 =========================== * Restored support for type size that is not a divisor of a buffer size for more info. See #356. * Implemented a `blosc2_rename_urlpath`, a portable function to rename a file or a full directory. See #355. * Several improvements for packaging. See #354, #357, #359. * Fixed a bug in BloscLZ codec (bumped to 2.5.1). Changes from 2.0.2 to 2.0.3 =========================== * Improved BloscLZ codec (bumped to 2.5.0) for achieving better compression ratios on data with high entropy. Although in general LZ4 still does a better job in this scenario, *BloscLZ* can sometimes achieve better compression ratios. * Added `blosc2_vlmeta_delete()` for removing vlmeta data. Thanks to Marta Iborra. * Update pkg-config file to support blosc2. Fixes #236. Thanks to Håvard Flaget Aasen. * Build system: Change hard coded library path with `CMAKE_INSTALL_LIBDIR` CMake variable. Thanks to Håvard Flaget Aasen. Changes from 2.0.1 to 2.0.2 =========================== * Fixed data chunk memory leaks in frame (see #335). * Fixed blosc2_stdio_open never returns NULL if it cannot open file. * Standardized places for headers in blosc/ and include/ dirs. * `nthreads` is int16_t everywhere in the API. Fixes #331. * Add blosc2_remove_urlpath function (see #330). * Fixed a bug when a lazy_chunk was created from a small, memcpyed chunk. (see #329). * Fixed many issues in documentation (see #333). Changes from 2.0.0 to 2.0.1 =========================== * The `blosc2_schunk_fill_special` function was not exported, so not in the shared library. This has been fixed; see #328. Thanks to Mark Kittisopikul. Changes from 2.0.0.rc2 to 2.0.0 (final) ======================================= * Now Blosc is always compiled with LZ4. See #324. * Implemented a system to register plugins (PR #314). See our blog at: https://www.blosc.org/posts/registering-plugins. * Added Blosc Lite version. Just activate `BUILD_LITE` cmake option with: `-DBUILD_LITE`. See #316. * You can deactivate the plugins by setting cmake option `BUILD_PLUGINS` to OFF. * Created `include` folder. See #310. * Moved codecs-registry.h and filters-registry.h to include/blosc2. See #325. * Fix error in endian-handler function affecting frames metadata. See #320. * Improved tolerance to Win64 workflows failure. See #319. * zlib-ng updated to 2.0.5. * New COMPILING_WITH_WHEELS.rst doc added. Changes from 2.0.0.rc.1 to 2.0.0.rc2 ==================================== * New compatibility with MinGW32/64. See #302. * Improved support for AArch64 (aka ARM64), ARMv7l, ARMv6l and powerpc64le. See #306. Thanks to Alexandr Romanenko, Mark Kittisopikul and Mosè Giordano from the Julia packaging team for their help. * BloscLZ updated to 2.4.0. Aligned access in ARM has been enabled, as well as other performance improvements. Expect much better performance, specially on ARM platforms (like Apple Silicon). * Zstd sources updated to 1.5.0. * zlib-ng sources updated to 2.0.3. Changes from 2.0.0-beta.5 to 2.0.0.rc.1 ======================================= * [API change] `blosc2_decompress_ctx()` gets a new `srcsize` parameter to ensure that it does not read past the end of the provided buffer. See #144. Thanks to Nathan Moinvaziri (@nmoinvaz). * [BREAKING CHANGE] The format for frames has changed and BLOSC2_VERSION_FRAME_FORMAT is now set to 2. There is no attempt to support previous formats, but there will probably be backward compatibility support starting from version 2 on. * New functionality for updating, inserting and deleting chunks in a super-chunk. * Support for special values. Large sequences of repeated values can be represented with an efficient, simple and fast run-length representation, without the need to use regular codecs. * Internal Zstd sources updated to 1.4.9. * Internal LZ4 sources updated to 1.9.3. * Internal zlib support is provided now by new zlib-ng 2.0.2 (replacing miniz). * The support for Snappy codec has been completely removed. Snappy is a C++ library, which is not good for a library that aims to be fully pure C. Snappy was removed from sources in C-Blosc(1) some years ago, so there should not be a lot of data compressed with Blosc/Snappy out there (and for the existing ones, a transcoding is always possible using C-Blosc(1)). * The Lizard codec has been removed. Lizard is a pretty good one, but it looks like it is not timely maintained. Zstd/Zlib can cover its place pretty nicely. * The split of blocks only happens for BLOSCLZ and LZ4 codecs. All the rest are not split at all. * Public APIs for frames have been removed. Frames should be considered an storage detail, so having them accessible publicly should only bring unnecessary cognitive load. Care have been carried out so as to ensure the same functionality via the super-chunk (schunk) API. * [FORMAT] New *sparse frame* format for on-disk I/O. This allows for storing data chunks in super-chunks in separate files inside a directory. The way to generate sparse frames is via `storage.contiguous=false` and `storage.urlpath=dirname`. See `README_SFRAME_FORMAT.rst` for details. Changes from 2.0.0-beta.4 to 2.0.0.beta5 ======================================== * The prefilter functionality has been introduced and declared stable. With that, you can callback arbitrary functions previous to any filter. This can be useful for performing (parallel) computations on chunks. For an example of use, see `tests/test_prefilter.c`. * New blosc2_set_maskout() function to avoid decompressing blocks. This can be handy when it is not needed to decompress all the blocks in a chunk. This should be always called before blosc2_decompress_ctx() and its effect is reset to the default (decompress all blocks) after that. * New ALTIVEC optimizations for Power architecture. These include support for both shuffle and bitshuffle filters. For details, see https://github.com/Blosc/c-blosc2/pull/98. Thanks to Jerome Kieffer and ESRF for making this happen. * New frame_from_bframe() function for getting a `blosc2_frame` out of an in-memory serialized frame. * Zstd codec updated to 1.4.4. Changes from 2.0.0-beta.3 to 2.0.0-beta.4 ========================================= * New pluggable threading backend. Instead of having Blosc use its own thread pool, you can instead call `blosc2_set_threads_callback(threads_callback, callback_data)` to install your own threading backend. This gives Blosc the possibility to use the same threading mechanism as one you are using in the rest of your program (e.g. OpenMP or Intel TBB), sharing the same threads, rather than starting its own threads that compete with yours for the CPU cores. See #PR 81. Thanks to Steven G. Johnson. * The endianness of the platform that is writing the data in chunks is stored now in the headers of the chunks. This info is not used yet, but this should allow a good hint for implementing format compatibility among platforms with different endianness in other layers. See PR #84. * Fixed a nasty bug that prevented frames to go more than 2 GB in size. * Added a cache for on-disk offsets. This accelerates the reading of slices from disk quite a lot (up to 50% with my benchmarks). * Zstd codec upgraded from 1.4.0 to 1.4.3. Changes from 2.0.0-beta.2 to 2.0.0-beta.3 ========================================= * Quick release to fix that beta.2 was tagged on top of a branch, not master. * The msgpack trailer now properly starts with `0x90 + 4` value, not plain wrong `0x09 + 4`. * Trailer version bumped to 1. Changes from 2.0.0-beta.1 to 2.0.0-beta.2 ======================================== * A new `vlmetalayers` chunk in `schunk` allows to store arbitrary meta-information that is up to the user. If the `schunk` has an attached `frame`, the later will be updated accordingly too. For more info, see PR #74 and docstrings of new `blosc2_update_vlmetalayer()` and `blosc2_get_vlmetalayer()` functions. * Metalayers must now be attached to super-chunks, not frames. The reason is that frames are increasingly treated as a storage specifier (in-memory or disk now, but can be other means in the future), whereas the actual API for I/O (including metainfo) goes into super-chunks. See PR #75. * New frame format documented in [README_CFRAME_FORMAT.rst](README_CFRAME_FORMAT.rst). Remember that the frame format is not written in stone yet, so some changes may be introduced before getting out of beta. * BREAKING CHANGE: the format for frames has changed and BLOSC2_VERSION_FRAME_FORMAT is now set to 1. There is no attempt to support previous formats, but there will probably be backward compatibility support starting from version 1 on. * BREAKING CHANGE: the next APIs have been renamed: + blosc2_frame_has_metalayer -> blosc2_has_metalayer + blosc2_frame_add_metalayer -> blosc2_add_metalayer + blosc2_frame_update_metalayer -> blosc2_update_metalayer + blosc2_frame_metalayer -> blosc2_get_metalayer Although the API was declared stable in beta.1, the fact that metalayers are attached now to super-chunks directly, made this change completely necessary. * BREAKING CHANGE: the next symbols have been renamed: + BLOSC_CPARAMS_DEFAULTS -> BLOSC2_CPARAMS_DEFAULTS + BLOSC_DPARAMS_DEFAULTS -> BLOSC2_DPARAMS_DEFAULTS Changes from 2.0.0a5 to 2.0.0-beta.1 ==================================== * The library is called now `blosc2` and not `blosc` anymore. This is necessary so as to prevent collisions with existing `blosc` deployments. * The `make install` now install all the necessary requirements out-of-the-box. * Use Intel IPP's LZ4Safe when compressing/decompressing: this provides better compression ratios and speed (in some cases). It is activated automatically if Intel IPP is found in the system, but you can always disable it with: `cmake -DDEACTIVATE_IPP=ON` * BREAKING CHANGE: the next API have been made private: + blosc2_frame_append_chunk -> frame_append_chunk + blosc2_frame_get_chunk -> frame_get_chunk + blosc2_frame_decompress_chunk -> frame_decompress_chunk Now the appending and retrieval of data in frames should always be made via the frame-backed super-chunk API. The idea is to deduplicate the I/O primitives as much as possible, and the super-chunks are the logical way for doing this. * BREAKING CHANGE: the next APIs have been renamed: + blosc2_get_cparams - > blosc2_schunk_get_cparams + blosc2_get_dparams - > blosc2_schunk_get_dparams * Internal BloscLZ sources bumped to 2.0.0. * Internal LZ4 sources bumped to 1.9.1. * Internal Zstd sources bumped to 1.4.0. Changes from 2.0.0a4 to 2.0.0a5 =============================== - Delta filter now implemented as a XOR instead of a NEG for better numerical stability. - Zstd dictionaries are supported. This allows for (much) better compression ratios when using small blocksizes, while improving compression speed. - Performance upgrades in the BloscLZ codec, specially for decompression in ARM architectures. - Preliminary support for Neon instruction set in ARM CPUs. Shuffle seems to work well, but still some issues with bitshuffle. - Internal LZ4 sources bumped to 1.8.2. - Internal Zstd sources bumped to 1.3.4. Changes from 2.0.0a3 to 2.0.0a4 =============================== - New filter pipeline designed to work inside a chunk. The pipeline has a current capacity of 5 slots, and is designed to apply different filters sequentially over the same chunk. For this, a new extended header for the chunk has been put in place (see README_HEADER.rst). - New delta filter meant to work inside a chunk. Previously delta was working inside a super-chunk, but the new implementation is both faster and simpler and gets better compression ratios (at least for the tested datasets). - New re-parametrization of BloscLZ for improved compression ratio and also more speed (at least on modern Intel/AMD CPUs). Version for internal BloscLZ codec bumped to 1.0.6. - Internal zstd sources bumped to 1.3.0. - The BLOSC2_MAX_OVERHEAD symbol is always 32 bytes, not 16 as in Blosc1. This is needed in order to allow buffers larger than 32 bits (31 actually :). Changes from 2.0.0a2 to 2.0.0a3 =============================== * New blosc2_create_context() and blosc2_free_context() for creating contexts so that Blosc can be used from threaded applications without the global lock. For now contexts can be only used from blosc2_compress_ctx() and blosc2_decompress_ctx() (see below). * The blosc_compress_ctx() and blosc_decompress_ctx() have been replaced by blosc2_compress_ctx() and blosc2_decompress_ctx() that do accept actual contexts. * Added support for new Zstd codec (https://github.com/Cyan4973/zstd). This is a new compressor by Yann Collet, the author of LZ4 and LZ4HC. For details on Zstd, see this nice intro: https://fastcompression.blogspot.com/2015/01/zstd-stronger-compression-algorithm.html. * The blosc2_append_chunk() has been removed. This is this because an existing chunk may not fulfill the sequence of filters in super header. It is best that the user will use blosc2_schunk_append_buffer() and compress it internally. * The split of blocks only happens for BLOSCLZ and SNAPPY codecs. All the rest are not split at all. This allows for faster operation for the rest of codecs (most specially zstd). * The internal zlib 1.2.8 sources have been replaced by the miniz library, which is meant to be fully compatible with Zlib, but much smaller and besides tends to be a bit faster. Also, miniz is preferred to an external Zlib. * The internal Snappy sources have been removed. If the Snappy library is found, the support for it is still there. * Internal LZ4 sources upgraded to 1.7.1. Changes from 2.0.0a1 to 2.0.0a2 =============================== * The delta filter runs inside of the compression pipeline. * Many fixes for the delta filter in super-chunks. * Added tests for delta filter in combination with super-chunks. c-blosc2-3.1.5/RELEASING.rst000066400000000000000000000120101521743436100151670ustar00rootroot00000000000000Releasing a version =================== Preliminaries ------------- - Make sure that ``RELEASE_NOTES.md`` and ``ANNOUNCE.md`` are up to date with the latest news in the release. - Check that *VERSION* symbols in include/blosc2.h contains the correct info. - Check that all public functions are covered in the reference docs:: $ python3 doc/check_missing_docs.py If any are missing, add them to the appropriate ``.rst`` file under ``doc/reference/``. If a function is intentionally left undocumented, add it to ``INTENTIONAL_SKIP`` in ``doc/check_missing_docs.py``. - If API/ABI changes, please increase the minor number (e.g. 2.15 -> 2.16) *and* bump the SOVERSION in blosc/CMakeLists.txt. When in doubt on when SOVERSION should change, see these nice guidelines: https://github.com/conda-forge/c-blosc2-feedstock/issues/62#issuecomment-2049675391 - Commit the changes with:: $ git commit -a -m "Getting ready for release X.Y.Z" $ git push Testing ------- Create a new build/ directory, change into it and issue:: $ cmake .. $ cmake --build . $ ctest Forward compatibility testing ----------------------------- First, go to the compat/ directory and generate a file with the current version:: $ cd ../compat $ export LD_LIBRARY_PATH=../build/blosc # Linux $ export DYLD_LIBRARY_PATH=../build/blosc # macOS $ gcc -o filegen filegen.c -L../build/blosc -lblosc2 -I../include $ ./filegen compress lz4 blosc-lz4-1.y.z.cdata In order to make sure that we are not breaking forward compatibility, link and run the `compat/filegen` utility against different versions of the Blosc library (suggestion: 1.3.0, 1.7.0, 1.11.1, 1.14.1, 2.0.0). You can compile the utility with different blosc shared libraries with:: $ export LD_LIBRARY_PATH=shared_blosc_library_path # Linux $ export DYLD_LIBRARY_PATH=shared_blosc_library_path # macOS $ gcc -o filegen filegen.c -Lshared_blosc_library_path -lblosc -Iblosc2.h_include_path Then, test the file created with the new version with:: $ ./filegen decompress blosc-lz4-1.y.z.cdata Repeat this for every codec shipped with Blosc (blosclz, lz4, lz4hc, zlib and zstd). For shuffle-only fixtures, use the matching inverse operation instead of ``decompress``:: $ ./filegen unshuffle shuffle-2.20.0.cdata $ ./filegen bitunshuffle bitshuffle-2.20.0.cdata For VL-block cframe compatibility checks, use ``compat/filegen-vl``. This utility can generate two kinds of persistent cframes: * a cframe with VL-block chunks and fixed chunk sizes * a cframe with VL-block chunks and variable chunk sizes Generate them with the current version:: $ cd ../compat $ export LD_LIBRARY_PATH=../build/blosc # Linux $ export DYLD_LIBRARY_PATH=../build/blosc # macOS $ gcc -o filegen-vl filegen-vl.c -L../build/blosc -lblosc2 -I../include $ ./filegen-vl compress lz4 vlblocks-lz4-fixed-2.y.z.b2frame $ ./filegen-vl compress lz4 vlblocks-lz4-variable-2.y.z.b2frame --variable-chunks $ ./filegen-vl compress lz4 varchunks-lz4-2.y.z.b2frame --regular Then, build ``compat/filegen-vl`` against the target Blosc library and check the fixtures with:: $ ./filegen-vl decompress vlblocks-lz4-fixed-3.0.0.b2frame $ ./filegen-vl decompress vlblocks-lz4-variable-3.0.0.b2frame $ ./filegen-vl decompress varchunks-lz4-3.0.0.b2frame Expected results: * These VL-block fixtures are only expected to work with releases that support the VL-block cframe format. * Pre-vlblocks releases are expected to reject them. * The ``--regular`` fixture tests regular (non-VL-block) chunks of varying sizes in the same cframe (frame format v3 ``FRAME_VARIABLE_CHUNKS`` flag). Tagging ------- - Create a tag ``X.Y.Z`` from ``master``. Use the next message:: $ git tag -a vX.Y.Z -m "Tagging version X.Y.Z" - Push the tag to the github repo:: $ git push --tags - Create a new release visiting https://github.com/Blosc/c-blosc2/releases/new and add the release notes copying them from `RELEASE_NOTES.md` document. Check documentation ------------------- Go to `blogsite actions `_ and trigger a build by going to the last run and clicking on "Re-run all jobs". Wait up to 10 min and go to the `blosc2 docs `_ and check that it contains the updated docs. Announcing ---------- - Send an announcement to the blosc and comp.compression mailing lists. Use the ``ANNOUNCE.md`` file as skeleton (likely as the definitive version). - Toot about it from the @Blosc2 account in https://fosstodon.org. Post-release actions -------------------- - Edit *VERSION* symbols in blosc/blosc2.h in master to increment the version to the next minor one (i.e. X.Y.Z --> X.Y.(Z+1).dev). - Create new headers for adding new features in ``RELEASE_NOTES.md`` and add this place-holder instead: #XXX version-specific blurb XXX# - Commit the changes:: $ git commit -a -m"Post X.Y.Z release actions done" $ git push That's all folks! .. Local Variables: .. mode: rst .. coding: utf-8 .. fill-column: 70 .. End: c-blosc2-3.1.5/ROADMAP-TO-3.0.rst000066400000000000000000000160301521743436100155650ustar00rootroot00000000000000Roadmap to 3.0 ============== New Features in C-Blosc2 ------------------------ * Adopt `OpenZL `_ as another codec: OpenZL is a new codec that is being developed by Meta. It is meant to be a very fast codec with a good compression ratio, especially for floating point data. It is still in an early stage of development, but it would be great to have it as part of C-Blosc2 so as to provide more options to users. * Move lazy expressions to C-Blosc2: right now, lazy expressions are only supported in the Python wrapper (python-blosc2). Moving them to C-Blosc2 would allow other wrappers (Java, R, Julia...) to benefit from this powerful feature. * Optimization for multi-socket machines: right now, C-Blosc2 is optimized for single-socket machines. However, in multi-socket machines, memory access is not uniform (NUMA architecture), so optimizations are needed to make sure that every thread is accessing to local memory as much as possible. This would require to use e.g. `numactl `_ or `libnuma `_ so as to pin threads and memory allocations to the local socket. * Support for GPUs: nowadays, GPUs are becoming more and more powerful, and having support for them in C-Blosc2 would be a great addition. The idea is to offload the compression, but most importantly, decompression tasks to the GPU, so that the CPU is free to do other tasks. This would require to use e.g. `CUDA `_ or `ROCm `_ so as to access to the GPU capabilities. * ACID version of the frame format: right now, frames are not ACID compliant (Atomicity, Consistency, Isolation and Durability). This means that if a frame is being written and the process is interrupted (e.g. power failure, crash...), the frame may be left in an inconsistent state. Making frames ACID compliant would require to implement a journaling mechanism so that changes are first written to a journal before being applied to the frame. This would ensure that frames are always in a consistent state, even in the event of a failure. * Framed version of the `TreeStore `_: the TreeStore is a new data structure that is being developed in Python-Blosc2. It is meant to be a hierarchical data structure that allows to store and access data in a tree-like fashion. Making it framed would allow to transfer it more easily between different machines, as well as storing it in memory. General improvements -------------------- * **Improve the safety of the library:** even if we have already made a long way in improving our safety, mainly thanks to the efforts of Nathan Moinvaziri, we take safety seriously, so this is always a work in progress. * **Checksums:** the frame can benefit from having a checksum per every chunk/index/metalayer. This will provide more safety towards frames that are damaged for whatever reason. Also, this would provide better feedback when trying to determine the parts of the frame that are corrupted. Candidates for checksums can be the xxhash32 or xxhash64, depending on the goals (to be decided). * **Multiple index chunks in frames:** right now, only `one chunk `_ is allowed for indexing other chunks. Provided the 2GB limit for a chunksize, that means that 'only' 256 million of chunks can be stored in a frame. Allowing for more than one index chunk would overcome this limitation. * **More robust detection of CPU capabilities:** although currently this detection is quite sophisticated, the code responsible for that has organically grown for more than 10 years and it is time to come with a more modern and robust way of doing this. https://github.com/google/cpu_features may be a good helper for doing this refactoring. * **Documentation:** utterly important for attracting new users and making the life easier for existing ones. Important points to have in mind here: - **Quality of API docstrings:** is the mission of the functions or data structures clearly and succinctly explained? Are all the parameters explained? Is the return value explained? What are the possible errors that can be returned? (mostly completed by Alberto Sabater). - **Tutorials/book:** besides the API docstrings, more documentation materials should be provided, like tutorials or a book about Blosc (or at least, the beginnings of it). Due to its adoption in GitHub and Jupyter notebooks, one of the most extended and useful markup systems is Markdown, so this should also be the first candidate to use here. * **Wrappers for other languages:** Rust, Java, R or Julia are the most obvious candidates. Still not sure if these should be produced and maintained by the Blosc development team, or leave them for third-party players that would be interested. The steering `council discussed this `_, and probably just the Python wrapper (python-blosc2, see above) should be maintained by Blosc maintainers themselves, while the other languages should be maintained by the community. * **Lock support for super-chunks:** when different processes are accessing concurrently to super-chunks, make them to sync properly by using locks, either on-disk (frame-backed super-chunks), or in-memory. Such a lock support would be configured in build time, so it could be disabled with a cmake flag. Outreaching ----------- * **Improve the Blosc website:** create a nice, modern-looking and easy to navigate website so that new potential users can see at first glimpse what's Blosc all about and power-users can access the documentation part easily. Ideally, a site-only search box would be great (sphinx-based docs would offer this for free). * **Attend to meetings and conferences:** it is very important to plan going to conferences for advertising C-Blosc2 and meeting people in-person. We need to decide which meetings to attend. When on the Python arena, the answer would be quite clear, but for general C libraries like C-Blosc2, it is not that straightforward which ones are the most suited. * Other outreaching activities would be to produce videos of the kind 'Blosc in 10 minutes', but not sure if this would be interesting for potential Blosc users (probably short tutorials in docs would be better suited). Increase diversity ------------------ **We strive to make our team as diverse as possible:** we are actively looking into more women and people from a variety of cultures to join our team. We have been very fortunate to have had Marta Iborra, our first female among us; she did a great range of first class contributions, like sparse storage, pre and post filters, new dynamic filters (e.g. JPEG2000), improvements in the Btune prediction engine and many others. After Marta, we had Oumaima Ech.Chdig who enhanced documentation and tutorials, especially for the Python wrapper. Thanks to the Python Software Foundation and NumFOCUS for providing funds for allowing this. c-blosc2-3.1.5/THANKS.rst000066400000000000000000000037321521743436100146610ustar00rootroot00000000000000Thanks ====== * Valentin Haenel did a terrific work implementing the support for the Snappy compression, fixing typos and improving docs and the plotting script. * Thibault North, with ideas from Oscar Villellas, contributed a way to call Blosc from different threads in a safe way. Christopher Speller introduced contexts so that a global lock is not necessary anymore. * The CMake support was initially contributed by Thibault North, and Antonio Valentino and Mark Wiebe made great enhancements to it. * Christopher Speller also introduced the two new '_ctx' calls to avoid the use of the blosc_init() and blosc_destroy(). * Jack Pappas contributed important portability enhancements, specially runtime and cross-platform detection of SSE2/AVX2 as well as high precision timers (HPET) for the benchmark program. * @littlezhou implemented the AVX2 version of shuffle routines. * Julian Taylor contributed a way to detect AVX2 in runtime and calling the appropriate routines only if the underlying hardware supports it. * Lucian Marc provided the support for ARM/NEON for the shuffle filter. * Jerome Kieffer contributed support for PowerPC/ALTIVEC for the shuffle/bitshuffle filter. * Alberto Sabater, for his great efforts on producing really nice Blosc2 docs, among other aspects. * Kiyo Masui for relicensing his bitshuffle project for allowing the inclusion of part of his code in Blosc. * Aleix Alcacer for his implementation of mutable super-chunks, multiple variable length metalayers and many other things. * Oscar Guiñón for the optimization of reading a (sparse) set of blocks of a chunk in parallel. * Nathan Moinvaziri for his outstanding work on the security side of the things via `fuzzer testing `_. * Marta Iborra for her implementation of sparse storage for persistent super-chunks and her attention to detail in many other aspects of the library. * Dimitri Papadopoulos for an extensive set of improvements in documentation and code. c-blosc2-3.1.5/TODO-refactorization.txt000066400000000000000000000001111521743436100176320ustar00rootroot00000000000000compcode -> codec look into the packed offsets and come with an standardc-blosc2-3.1.5/WHATS-NEW.rst000066400000000000000000000173631521743436100152130ustar00rootroot00000000000000New features in C-Blosc2 ======================== Right now, the next features are already implemented (although they may require some refactoring or more tests): * **64-bit containers:** the first-class container in C-Blosc2 is the `super-chunk` or, for brevity, `schunk`, that is made by smaller chunks which are essentially C-Blosc1 32-bit containers. The super-chunk can be backed or not by another container which is called a `frame` (see later). * **More filters:** besides `shuffle` and `bitshuffle` already present in C-Blosc1, C-Blosc2 already implements: - `delta`: the stored blocks inside a chunk are diff'ed with respect to first block in the chunk. The idea is that, in some situations, the diff will have more zeros than the original data, leading to better compression. - `trunc_prec`: it zeroes the least significant bits of the mantissa of float32 and float64 types. When combined with the `shuffle` or `bitshuffle` filter, this leads to more contiguous zeros, which are compressed better. * **A filter pipeline:** the different filters can be pipelined so that the output of one can be the input for the other. A possible example is a `delta` followed by `shuffle`, or as described above, `trunc_prec` followed by `bitshuffle`. * **Prefilters:** allows applying user-defined C callbacks **prior to** the filter pipeline during compression. See `test_prefilter.c `_ for an example of use. * **Postfilters:** allows applying user-defined C callbacks **after** the filter pipeline during decompression. The combination of prefilters and postfilters could be interesting for supporting e.g. encryption (via prefilters) and decryption (via postfilters). Also, a postfilter alone can be used to produce on-the-fly computation based on existing data (or other metadata, like e.g. coordinates). See `test_postfilter.c `_ for an example of use. * **SIMD support for ARM (NEON):** this allows for faster operation on ARM architectures. Only `shuffle` is supported right now, but the idea is to implement `bitshuffle` for NEON too. Thanks to Lucian Marc. * **SIMD support for PowerPC (ALTIVEC):** this allows for faster operation on PowerPC architectures. Both `shuffle` and `bitshuffle` are supported; however, this has been done via a transparent mapping from SSE2 into ALTIVEC emulation in GCC 8, so performance could be better (but still, it is already a nice improvement over native C code; see PR https://github.com/Blosc/c-blosc2/pull/59 for details). Thanks to Jerome Kieffer and `ESRF `_ for sponsoring the Blosc team in doing this task. * **Dictionaries:** when a block is going to be compressed, C-Blosc2 can use a previously made dictionary (stored in the header of the super-chunk) for compressing all the blocks that are part of the chunks. This usually improves the compression ratio, as well as the decompression speed, at the expense of a (small) overhead in compression speed. Currently, it is only supported in the `zstd` codec, but would be nice to extend it to `lz4` and `blosclz` at least. * **Contiguous frames:** allow to store super-chunks contiguously, either on-disk or in-memory. When a super-chunk is backed by a frame, instead of storing all the chunks sparsely in-memory, they are serialized inside the frame container. The frame can be stored on-disk too, meaning that persistence of super-chunks is supported. * **Sparse frames:** each chunk in a super-chunk, as well as the metadata, are stored separately. This allows for more efficient updates than in frames (i.e. avoiding 'holes' in monolithic files). * **Partial chunk reads:** there is support for reading just part of a chunk, so avoiding reading the whole thing and then discarding the unnecessary data (which is a waste of resources). * **Parallel chunk reads:** when several blocks of a chunk are to be read, this is done in parallel by the decompressing machinery. That means that every thread is responsible to read, post-filter and decompress a block by itself, leading to an efficient overlap of I/O and CPU usage that optimizes reads to a maximum. * **Meta-layers:** optionally, the user can add meta-data for different uses and in different layers. For example, one may think on providing a meta-layer for `NumPy `_ so that most of the meta-data for it is stored in a meta-layer; then, one can place another meta-layer on top of the latter for adding more high-level info if desired (e.g. geo-spatial, meteorological...). * **Variable length meta-layers:** the user may want to add variable-length meta information that can be potentially very large (up to 2 GB). The regular meta-layer described above is very quick to read, but meant to store fixed-length and relatively small meta information. Variable length metalayers are stored in the trailer of a frame, whereas regular meta-layers are in the header. * **Efficient support for special values:** large sequences of repeated values can be represented with an efficient, simple and fast run-length representation, without the need to use regular codecs. With that, chunks or super-chunks with values that are the same (zeros, NaNs or any value in general) can be built in constant time, regardless of the size. This can be useful in situations where a lot of zeros (or NaNs) need to be stored (e.g. sparse matrices). * **Nice markup for documentation:** we are currently using a combination of Sphinx + Doxygen + Breathe for documenting the C-API. See https://www.blosc.org/c-blosc2/c-blosc2.html. Thanks to Alberto Sabater and Aleix Alcacer for contributing the support for this. * **Plugin capabilities for filters and codecs:** we have a plugin register capability inplace so that the info about the new filters and codecs can be persisted and transmitted to different machines. Thanks to the NumFOCUS foundation for providing a grant for doing this. * **Centralized plugin repository:** we have implemented a centralized repository so that people can send their plugins (using the existing machinery) to the Blosc2 team. If the plugins fulfill a series of requirements, they will be officially accepted, and distributed within the library. Thanks to NumFOCUS foundation for providing a grant for doing this. See https://www.blosc.org/posts/registering-plugins/. * **Support for lossy codecs:** besides supporting the `trunc_prec` filter (described above), we also offer support for `zfp `_, a codec that is specifically meant for lossy compression of *multidimensional* floating point data. For details on how use it, see https://github.com/Blosc/c-blosc2/tree/main/plugins/codecs/zfp. Support for more lossy codecs may come in the future. * **Pluggable tuning capabilities:** this will allow users with different needs to define an interface so as to better tune different parameters like the codec, the compression level, the filters to use, the blocksize or the shuffle size. Thanks to ironArray for sponsoring us in doing this. * **Support for I/O plugins:** so that users can extend the I/O capabilities beyond the current filesystem support. Things like use databases or S3 interfaces should be possible by implementing these interfaces. Thanks to ironArray for sponsoring us in doing this. * **Python wrapper:** we have a preliminary wrapper in the works. You can have a look at our ongoing efforts in the `python-blosc2 repo `_. Thanks to the Python Software Foundation for providing a grant for doing this. * **Security:** we are actively using the `OSS-Fuzz `_ and `ClusterFuzz `_ for uncovering programming errors in C-Blosc2. Thanks to Google for sponsoring us in doing this. c-blosc2-3.1.5/bench/000077500000000000000000000000001521743436100142115ustar00rootroot00000000000000c-blosc2-3.1.5/bench/CMakeLists.txt000066400000000000000000000173251521743436100167610ustar00rootroot00000000000000# Blosc - Blocked Shuffling and Compression Library # # Copyright (c) 2021 Blosc Development Team # https://blosc.org # License: BSD 3-Clause (see LICENSE.txt) # # See LICENSE.txt for details about copyright and rights to use. # sources for main bench set(SOURCES b2bench.c) # other benchmarks set(SOURCES_DELTA delta_schunk.c) set(SOURCES_TRUNC_PREC trunc_prec_schunk.c) set(SOURCES_SUM_OPENMP sum_openmp.c) set(SOURCES_ZERO_RUNLEN zero_runlen.c) set(SOURCES_CFRAME create_frame.c) set(SOURCES_SFRAME sframe_bench.c) set(SOURCES_GET_SPARSE get_sparse.c) add_subdirectory(b2nd) # targets add_executable(b2bench ${SOURCES}) add_executable(delta_schunk ${SOURCES_DELTA}) add_executable(trunc_prec_schunk ${SOURCES_TRUNC_PREC}) add_executable(sum_openmp ${SOURCES_SUM_OPENMP}) add_executable(zero_runlen ${SOURCES_ZERO_RUNLEN}) add_executable(create_frame ${SOURCES_CFRAME}) add_executable(sframe_bench ${SOURCES_SFRAME}) add_executable(get_sparse ${SOURCES_GET_SPARSE}) if(UNIX AND NOT APPLE) # cmake is complaining about LINK_PRIVATE in original PR # and removing it does not seem to hurt, so be it. # target_link_libraries(b2bench LINK_PRIVATE rt) target_link_libraries(b2bench rt) target_link_libraries(delta_schunk rt) target_link_libraries(trunc_prec_schunk rt) target_link_libraries(sum_openmp rt) target_link_libraries(zero_runlen rt) target_link_libraries(create_frame rt) target_link_libraries(sframe_bench rt) target_link_libraries(get_sparse rt) endif() if(UNIX) if(CMAKE_C_COMPILER_ID STREQUAL "GNU") # Avoid a warning when using gcc without -fopenmp target_compile_options(sum_openmp PRIVATE "-Wno-unknown-pragmas") endif() endif() target_link_libraries(b2bench blosc_testing) target_link_libraries(delta_schunk blosc_testing) target_link_libraries(trunc_prec_schunk blosc_testing) target_link_libraries(sum_openmp blosc_testing) target_link_libraries(zero_runlen blosc_testing) target_link_libraries(create_frame blosc_testing) target_link_libraries(sframe_bench blosc_testing) target_link_libraries(get_sparse blosc_testing) # tests if(BUILD_TESTS) # The commented tests below take too much time to complete option(TEST_INCLUDE_BENCH_SHUFFLE_1 "Include b2bench shuffle (1 thread) in the tests" ON) if(TEST_INCLUDE_BENCH_SHUFFLE_1) set(SHUFFLE_1_OPTS shuffle test 1) add_test(NAME test_blosclz_shuffle_1 COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ blosclz ${SHUFFLE_1_OPTS}) add_test(NAME test_lz4_shuffle_1 COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ lz4 ${SHUFFLE_1_OPTS}) # add_test(NAME test_lz4hc_shuffle_1 # COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ lz4hc ${SHUFFLE_1_OPTS}) if(HAVE_ZLIB) # add_test(NAME test_zlib_shuffle_1 # COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ zlib ${SHUFFLE_1_OPTS}) endif() if(HAVE_ZSTD) # add_test(NAME test_zstd_shuffle_1 # COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ zstd ${SHUFFLE_1_OPTS}) endif() endif() option(TEST_INCLUDE_BENCH_SHUFFLE_N "Include b2bench shuffle (multithread) in the tests" ON) if(TEST_INCLUDE_BENCH_SHUFFLE_N) set(SHUFFLE_N_OPTS shuffle test) add_test(NAME test_blosclz_shuffle_n COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ blosclz ${SHUFFLE_N_OPTS}) add_test(NAME test_lz4_shuffle_n COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ lz4 ${SHUFFLE_N_OPTS}) add_test(NAME test_lz4hc_shuffle_n COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ lz4hc ${SHUFFLE_N_OPTS}) if(HAVE_ZLIB) add_test(NAME test_zlib_shuffle_n COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ zlib ${SHUFFLE_N_OPTS}) endif() if(HAVE_ZSTD) add_test(NAME test_zstd_shuffle_n COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ zstd ${SHUFFLE_N_OPTS}) endif() endif() option(TEST_INCLUDE_BENCH_BITSHUFFLE_1 "Include b2bench bitshuffle (1 thread) in the tests" ON) if(TEST_INCLUDE_BENCH_BITSHUFFLE_1) set(BITSHUFFLE_1_OPTS bitshuffle test 1) add_test(NAME test_blosclz_bitshuffle_1 COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ blosclz ${BITSHUFFLE_1_OPTS}) add_test(NAME test_lz4_bitshuffle_1 COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ lz4 ${BITSHUFFLE_1_OPTS}) # add_test(NAME test_lz4hc_bitshuffle_1 # COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ lz4hc ${BITSHUFFLE_1_OPTS}) if(HAVE_ZLIB) # add_test(NAME test_zlib_bitshuffle_1 # COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ zlib ${BITSHUFFLE_1_OPTS}) endif() if(HAVE_ZSTD) # add_test(NAME test_zstd_bitshuffle_1 # COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ zstd ${BITSHUFFLE_1_OPTS}) endif() endif() option(TEST_INCLUDE_BENCH_BITSHUFFLE_N "Include b2bench bitshuffle (multithread) in the tests" ON) if(TEST_INCLUDE_BENCH_BITSHUFFLE_N) set(BITSHUFFLE_N_OPTS bitshuffle test) add_test(NAME test_blosclz_bitshuffle_n COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ blosclz ${BITSHUFFLE_N_OPTS}) add_test(NAME test_lz4_bitshuffle_n COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ lz4 ${BITSHUFFLE_N_OPTS}) # add_test(NAME test_lz4hc_bitshuffle_n # COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ lz4hc ${BITSHUFFLE_N_OPTS}) if(HAVE_ZLIB) # add_test(NAME test_zlib_bitshuffle_n # COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ zlib ${BITSHUFFLE_N_OPTS}) endif() if(HAVE_ZSTD) # add_test(NAME test_zstd_bitshuffle_n # COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ zstd ${BITSHUFFLE_N_OPTS}) endif() endif() option(TEST_INCLUDE_BENCH_SUITE "Include b2bench suite in the tests" OFF) if(TEST_INCLUDE_BENCH_SUITE) add_test(NAME test_hardsuite COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ blosclz shuffle suite) endif() option(TEST_INCLUDE_BENCH_DEBUGSUITE "Include b2bench debugsuite in the tests" OFF) if(TEST_INCLUDE_BENCH_DEBUGSUITE) add_test(NAME test_debugsuite COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ blosclz shuffle debugsuite) endif() option(TEST_INCLUDE_BENCH_DELTA "Include delta bench in the tests" ON) if(TEST_INCLUDE_BENCH_DELTA) add_test(NAME test_bench_delta COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $) endif() option(TEST_INCLUDE_BENCH_TRUNC_PREC "Include trunc bench in the tests" ON) if(TEST_INCLUDE_BENCH_TRUNC_PREC) add_test(NAME test_bench_trunc_prec COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $) endif() option(TEST_INCLUDE_BENCH_SUM_OPENMP "Include sum_openmp in the tests" OFF) if(TEST_INCLUDE_BENCH_SUM_OPENMP) add_test(NAME test_bench_sum_openmp COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $) endif() option(TEST_INCLUDE_BENCH_ZERO_RUNLEN "Include zero_runlen in the tests" ON) if(TEST_INCLUDE_BENCH_ZERO_RUNLEN) add_test(NAME test_bench_zero_runlen COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $) endif() endif() c-blosc2-3.1.5/bench/Makefile.mingw000066400000000000000000000015041521743436100167710ustar00rootroot00000000000000# Makefile for the MinGW suite for Windows CC = g++ # or clang CFLAGS = -O3 -g -msse2 -Wall #LDFLAGS = -lpthread # for UNIX or for Windows with pthread installed LDFLAGS = -static # for mingw SOURCES = $(wildcard ../blosc/*.c) EXECUTABLE = bench # Support for external LZ4 #LDFLAGS += -llz4 # Support for the msvc zlib: ZLIB_ROOT=/libs/zlib128 LDFLAGS=-DHAVE_ZLIB -I$(ZLIB_ROOT)/include -lzdll -L$(ZLIB_ROOT)/lib # Support for the mingw zlib: #ZLIB_ROOT=/libs/libz-1.2.8 #LDFLAGS=-DHAVE_ZLIB -I$(ZLIB_ROOT)/include -lz -L$(ZLIB_ROOT)/lib # Support for internal Zlib #ZLIB_DIR = ../internal-complibs/zlib-1.2.8 #CFLAGS += -DHAVE_ZLIB -I$(ZLIB_DIR) #SOURCES += $(wildcard $(ZLIB_DIR)/*.c) all: $(SOURCES) $(EXECUTABLE) $(EXECUTABLE): $(SOURCES) $(CC) $(CFLAGS) bench.c $(SOURCES) -o $@ $(LDFLAGS) clean: rm -rf $(EXECUTABLE) c-blosc2-3.1.5/bench/aggregate_multiple.py000066400000000000000000000057571521743436100204420ustar00rootroot00000000000000from pathlib import Path import re import os import subprocess from typing import Union import numpy as np import argparse def run_command(command: str, return_output: bool = False) -> Union[tuple[list[float], str], tuple[list[float]]]: bench_folder = Path(__file__).parent.parent / "build" / "bench" if os.name == "nt": bench_folder /= "Release" res = subprocess.run(f"{bench_folder}/{command}", shell=True, capture_output=True) assert res.returncode == 0, f"\nstdout: {res.stdout.decode('utf-8')}\nstderr: {res.stderr.decode('utf-8')}" out = res.stdout.decode("utf-8") numbers = [] for number in re.findall(r"-?\d+(?:\.\d+)?", out): numbers.append(float(number)) assert len(numbers) > 0, f"Could not find any numbers in the output:\n{out}" if return_output: return numbers, out else: return numbers class ReplaceNumbers: def __init__(self, numbers: np.ndarray): self.numbers = numbers self.index = 0 def __call__(self, match: re.Match) -> str: assert match.group() == "NUMBER" number = self.numbers[self.index] self.index += 1 if number.is_integer(): return str(int(number)) else: return f"{number:.3f}" if __name__ == "__main__": parser = argparse.ArgumentParser( description=( "Repeat multiple runs of a benchmark and aggregate all numbers in the output (the outputs from all runs must " "have the same format)." ), formatter_class=argparse.ArgumentDefaultsHelpFormatter, ) parser.add_argument( "--command", required=True, type=str, help="The command to execute (e.g. sframe_bench 1000 insert 1000 io_mmap).", ) parser.add_argument( "--warmups", default=3, required=False, type=int, help="Number of warmup runs.", ) parser.add_argument( "--runs", default=30, required=False, type=int, help="Number of runs to perform and aggregate the output from.", ) args = parser.parse_args() for i in range(args.warmups): print(f"Warmup {i+1}/{args.warmups}", end="\r") run_command(args.command) print() # Collect numbers from outputs of multiple runs example_output = None numbers = [] for i in range(args.runs): print(f"Run {i+1}/{args.runs}", end="\r") if example_output is None: values, text = run_command(args.command, return_output=True) example_output = text numbers.append(values) else: numbers.append(run_command(args.command)) print() numbers = np.asarray(numbers) numbers = np.min(numbers, axis=0) assert example_output is not None, "No output was collected." example_output = re.sub(r"-?\d+(?:\.\d+)?", "NUMBER", example_output) aggregated_output = re.sub("NUMBER", ReplaceNumbers(numbers), example_output) print(aggregated_output) c-blosc2-3.1.5/bench/b2bench.c000066400000000000000000000406131521743436100156640ustar00rootroot00000000000000/********************************************************************* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Small benchmark for testing basic capabilities of Blosc. You can select different degrees of 'randomness' in input buffer, as well as external datafiles (uncomment the lines after "For data coming from a file" comment). For usage instructions of this benchmark, please see: https://www.blosc.org/pages/synthetic-benchmarks/ I'm collecting speeds for different machines, so the output of your benchmarks and your processor specifications are welcome! Note: Compiling this with VS2008 does not work well with cmake. Here it is a way to compile the benchmark (with added support for LZ4): > cl /arch:SSE2 /Ox /Febench.exe /Iblosc /Iinternal-complibs\lz4-1.7.0 bench\bench.c blosc\blosc.c blosc\blosclz.c blosc\shuffle.c blosc\shuffle-sse2.c blosc\shuffle-generic.c blosc\bitshuffle-generic.c blosc\bitshuffle-sse2.c internal-complibs\lz4-1.7.0\*.c See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #include #include #include #include #include #include "blosc2.h" #define KB 1024u #define MB (1024*KB) #define GB (1024*MB) #define NCHUNKS (32*1024) /* maximum number of chunks */ int nchunks = NCHUNKS; int niter = 1; int niter_c = 1; int niter_d = 1; /* default number of iterations */ double totalsize = 0.; /* total compressed/decompressed size */ /* Define posix_memalign for Windows */ #if defined(_WIN32) #include int posix_memalign(void **memptr, size_t alignment, size_t size) { *memptr = _aligned_malloc(size, alignment); return 0; } /* Buffers allocated with _aligned_malloc need to be freed with _aligned_free. */ #define aligned_free(memptr) _aligned_free(memptr) #else /* If not using MSVC, aligned memory can be freed in the usual way. */ #define aligned_free(memptr) free(memptr) #endif /* defined(_WIN32) && !defined(__MINGW32__) */ /* Given two timeval stamps, return the time per chunk in usec */ double get_usec_chunk(blosc_timestamp_t last, blosc_timestamp_t current, int niter_, int nchunks_) { double elapsed_usecs = 1e-3 * blosc_elapsed_nsecs(last, current); return elapsed_usecs / (double)(niter_ * nchunks_); } int get_value(int i, int rshift) { int v; v = (i << 26) ^ (i << 18) ^ (i << 11) ^ (i << 3) ^ i; if (rshift < 32) { v &= (1 << rshift) - 1; } return v; } void init_buffer(void* src, size_t size, int rshift) { int i; int* _src = (int*)src; /* To have reproducible results */ srand(1); /* Initialize the original buffer */ for (i = 0; i < (int) (size / sizeof(int)); ++i) { /* Choose one below */ /* _src[i] = 0; * _src[i] = 0x01010101; * _src[i] = 0x01020304; * _src[i] = i * 1/.3; * _src[i] = i; */ //_src[i] = rand() >> (32 - rshift); _src[i] = get_value(i, rshift); } } void do_bench(char* compressor, char* shuffle, int nthreads, int size_, int elsize, int rshift, FILE* ofile) { size_t size = (size_t)size_; void* src = NULL, *srccpy; void* dest[NCHUNKS], *dest2 = NULL; int nbytes = 0, cbytes = 0; int i, j, retcode; unsigned char* orig, * round; blosc_timestamp_t last, current; double tmemcpy, tshuf, tunshuf; int clevel, doshuffle = BLOSC_NOFILTER; if (strcmp(shuffle, "shuffle") == 0) { doshuffle = BLOSC_SHUFFLE; } else if (strcmp(shuffle, "bitshuffle") == 0) { doshuffle = BLOSC_BITSHUFFLE; } else if (strcmp(shuffle, "noshuffle") == 0) { doshuffle = BLOSC_NOSHUFFLE; } blosc2_set_nthreads((int16_t) nthreads); if (blosc1_set_compressor(compressor) < 0) { printf("Compiled w/o support for compressor: '%s', so sorry.\n", compressor); exit(1); } /* Initialize buffers */ srccpy = malloc(size); retcode = posix_memalign(&src, 32, size); if (retcode != 0) { printf("Error in allocating memory!"); free(srccpy); return; } retcode = posix_memalign(&dest2, 32, size); if (retcode != 0) { printf("Error in allocating memory!"); aligned_free(src); free(srccpy); return; } /* zero src to initialize all bytes on it, and not only multiples of 4 */ memset(src, 0, size); init_buffer(src, size, rshift); memcpy(srccpy, src, size); for (j = 0; j < nchunks; j++) { retcode = posix_memalign(&dest[j], 32, size + BLOSC2_MAX_OVERHEAD); if (retcode != 0) { printf("Error in allocating memory!"); } } memset(dest2, 0, size); // just to avoid some GCC compiler warnings fprintf(ofile, "--> %d, %d, %d, %d, %s, %s\n", nthreads, (int)size, elsize, rshift, compressor, shuffle); fprintf(ofile, "********************** Run info ******************************\n"); fprintf(ofile, "Blosc version: %s (%s)\n", BLOSC2_VERSION_STRING, BLOSC2_VERSION_DATE); fprintf(ofile, "Using synthetic data with %d significant bits (out of 32)\n", rshift); fprintf(ofile, "Dataset size: %d bytes\tType size: %d bytes\n", (int)size, elsize); fprintf(ofile, "Working set: %.1f MB\t\t", (float)(size * nchunks) / (float)MB); fprintf(ofile, "Number of threads: %d\n", nthreads); fprintf(ofile, "********************** Running benchmarks *********************\n"); blosc_set_timestamp(&last); for (i = 0; i < niter; i++) { for (j = 0; j < nchunks; j++) { memcpy(dest[j], src, size); } } blosc_set_timestamp(¤t); tmemcpy = get_usec_chunk(last, current, niter, nchunks); fprintf(ofile, "memcpy(write):\t\t %6.1f us, %.1f MB/s\n", tmemcpy, ((float)size * 1e6) / (tmemcpy * MB)); blosc_set_timestamp(&last); for (i = 0; i < niter; i++) { for (j = 0; j < nchunks; j++) { memcpy(dest2, dest[j], size); } } blosc_set_timestamp(¤t); tmemcpy = get_usec_chunk(last, current, niter, nchunks); fprintf(ofile, "memcpy(read):\t\t %6.1f us, %.1f MB/s\n", tmemcpy, ((float)size * 1e6) / (tmemcpy * MB)); for (clevel = 0; clevel < 10; clevel++) { fprintf(ofile, "Compression level: %d\n", clevel); blosc_set_timestamp(&last); for (i = 0; i < niter_c; i++) { for (j = 0; j < nchunks; j++) { cbytes = blosc1_compress(clevel, doshuffle, (size_t)elsize, size, src, dest[j], size + BLOSC2_MAX_OVERHEAD); } } blosc_set_timestamp(¤t); tshuf = get_usec_chunk(last, current, niter_c, nchunks); fprintf(ofile, "comp(write):\t %6.1f us, %.1f MB/s\t ", tshuf, ((float)size * 1e6) / (tshuf * MB)); fprintf(ofile, "Final bytes: %d ", cbytes); if (cbytes > 0) { fprintf(ofile, "Ratio: %3.2f", (float)size / (float)cbytes); } fprintf(ofile, "\n"); /* Compressor was unable to compress. Copy the buffer manually. */ if (cbytes == 0) { for (j = 0; j < nchunks; j++) { memcpy(dest[j], src, size); } } blosc_set_timestamp(&last); for (i = 0; i < niter_d; i++) { for (j = 0; j < nchunks; j++) { if (cbytes == 0) { memcpy(dest2, dest[j], size); nbytes = (int)size; } else { nbytes = blosc1_decompress(dest[j], dest2, size); } } } blosc_set_timestamp(¤t); tunshuf = get_usec_chunk(last, current, niter_d, nchunks); fprintf(ofile, "decomp(read):\t %6.1f us, %.1f MB/s\t ", tunshuf, (nbytes * 1e6) / (tunshuf * MB)); if (nbytes < 0) { fprintf(ofile, "FAILED. Error code: %d\n", nbytes); } /* fprintf(ofile, "Orig bytes: %d\tFinal bytes: %d\n", cbytes, nbytes); */ /* Check if data has had a good roundtrip. Byte-by-byte comparison is slow, so use 'memcmp' to check whether the roundtripped data is correct. If not, fall back to the slow path to print diagnostic messages. */ orig = (unsigned char*)srccpy; round = (unsigned char*)dest2; if (memcmp(orig, round, size) != 0) { for (i = 0; i < (int)size; ++i) { if (orig[i] != round[i]) { fprintf(ofile, "\nError: Original data and round-trip do not match in pos %d\n", i); fprintf(ofile, "Orig--> %x, round-trip--> %x\n", orig[i], round[i]); break; } } } else { i = (int)size; } if (i == (int)size) fprintf(ofile, "OK\n"); } /* End clevel loop */ /* To compute the totalsize, we should take into account the 10 compression levels */ totalsize += ((double)size * nchunks * niter * 10.); aligned_free(src); free(srccpy); aligned_free(dest2); for (i = 0; i < nchunks; i++) { aligned_free(dest[i]); } } /* Compute a sensible value for nchunks */ int get_nchunks(int size_, int ws) { int nchunks_; nchunks_ = ws / size_; if (nchunks_ > NCHUNKS) nchunks_ = NCHUNKS; if (nchunks_ < 1) nchunks_ = 1; return nchunks_; } void print_compress_info(void) { char* name = NULL, * version = NULL; int ret; printf("Blosc version: %s (%s)\n", BLOSC2_VERSION_STRING, BLOSC2_VERSION_DATE); printf("List of supported compressors in this build: %s\n", blosc2_list_compressors()); printf("Supported compression libraries:\n"); ret = blosc2_get_complib_info("blosclz", &name, &version); if (ret >= 0) printf(" %s: %s\n", name, version); free(name); free(version); ret = blosc2_get_complib_info("lz4", &name, &version); if (ret >= 0) printf(" %s: %s\n", name, version); free(name); free(version); ret = blosc2_get_complib_info("zlib", &name, &version); if (ret >= 0) printf(" %s: %s\n", name, version); free(name); free(version); ret = blosc2_get_complib_info("zstd", &name, &version); if (ret >= 0) printf(" %s: %s\n", name, version); free(name); free(version); } int main(int argc, char* argv[]) { char compressor[32]; char shuffle[32] = "shuffle"; char bsuite[32]; int single = 1; int suite = 0; int hard_suite = 0; int extreme_suite = 0; int debug_suite = 0; int nthreads = 8; /* The number of threads */ int size = 8 * MB; /* Buffer size */ int elsize = 4; /* Datatype size */ int rshift = 19; /* Significant bits */ int workingset = 256 * MB; /* The maximum allocated memory */ int nthreads_, size_, elsize_, rshift_, i; FILE* output_file = stdout; blosc_timestamp_t last, current; double totaltime; char usage[256]; print_compress_info(); strncpy(usage, "Usage: bench [blosclz | lz4 | lz4hc | zlib | zstd] " "[noshuffle | shuffle | bitshuffle] " "[single | suite | hardsuite | extremesuite | debugsuite] " "[nthreads] [bufsize(bytes)] [typesize] [sbits]", 255); if (argc < 1) { printf("%s\n", usage); exit(1); } if (argc >= 2) { strcpy(compressor, argv[1]); } else { strcpy(compressor, "blosclz"); } if (strcmp(compressor, "blosclz") != 0 && strcmp(compressor, "lz4") != 0 && strcmp(compressor, "lz4hc") != 0 && strcmp(compressor, "zlib") != 0 && strcmp(compressor, "zstd") != 0) { printf("No such compressor: '%s'\n", compressor); printf("%s\n", usage); exit(2); } if (argc >= 3) { strcpy(shuffle, argv[2]); if (strcmp(shuffle, "shuffle") != 0 && strcmp(shuffle, "bitshuffle") != 0 && strcmp(shuffle, "noshuffle") != 0) { printf("No such shuffler: '%s'\n", shuffle); printf("%s\n", usage); exit(2); } } if (argc < 4) strcpy(bsuite, "single"); else strcpy(bsuite, argv[3]); if (strcmp(bsuite, "single") == 0) { single = 1; } else if (strcmp(bsuite, "test") == 0) { single = 1; workingset /= 2; } else if (strcmp(bsuite, "suite") == 0) { suite = 1; } else if (strcmp(bsuite, "hardsuite") == 0) { hard_suite = 1; workingset /= 4; /* Values here are ending points for loops */ nthreads = 2; size = 8 * MB; elsize = 32; rshift = 32; } else if (strcmp(bsuite, "extremesuite") == 0) { extreme_suite = 1; workingset /= 8; niter = 1; /* Values here are ending points for loops */ nthreads = 4; size = 16 * MB; elsize = 32; rshift = 32; } else if (strcmp(bsuite, "debugsuite") == 0) { debug_suite = 1; workingset /= 8; niter = 1; /* Warning: values here are starting points for loops. This is useful for debugging. */ nthreads = 1; size = 16 * KB; elsize = 1; rshift = 0; } else { printf("%s\n", usage); exit(1); } printf("Using compressor: %s\n", compressor); printf("Using shuffle type: %s\n", shuffle); printf("Running suite: %s\n", bsuite); if (argc >= 5) { nthreads = (int)strtol(argv[4], NULL, 10); } if (argc >= 6) { size = (int)strtol(argv[5], NULL, 10); } if (argc >= 7) { elsize = (int)strtol(argv[6], NULL, 10); } if (argc >= 8) { rshift = (int)strtol(argv[7], NULL, 10); } if ((argc >= 9) || !(single || suite || hard_suite || extreme_suite)) { printf("%s\n", usage); exit(1); } nchunks = get_nchunks(size, workingset); blosc_set_timestamp(&last); blosc2_init(); if (suite) { for (nthreads_ = 1; nthreads_ <= nthreads; nthreads_++) { do_bench(compressor, shuffle, nthreads_, size, elsize, rshift, output_file); } } else if (hard_suite) { /* Let's start the rshift loop by 4 so that 19 is visited. This is to allow a direct comparison with the plain suite, that runs precisely at 19 significant bits. */ for (rshift_ = 4; rshift_ <= rshift; rshift_ += 5) { for (elsize_ = 1; elsize_ <= elsize; elsize_ *= 2) { /* The next loop is for getting sizes that are not power of 2 */ for (i = -elsize_; i <= elsize_; i += elsize_) { for (size_ = 32 * KB; size_ <= size; size_ *= 2) { nchunks = get_nchunks(size_ + i, workingset); niter = 1; for (nthreads_ = 1; nthreads_ <= nthreads; nthreads_++) { do_bench(compressor, shuffle, nthreads_, size_ + i, elsize_, rshift_, output_file); blosc_set_timestamp(¤t); totaltime = blosc_elapsed_secs(last, current); printf("Elapsed time:\t %6.1f s. Processed data: %.1f GB\n", totaltime, totalsize / GB); } } } } } } else if (extreme_suite) { for (rshift_ = 0; rshift_ <= rshift; rshift_++) { for (elsize_ = 1; elsize_ <= elsize; elsize_++) { /* The next loop is for getting sizes that are not power of 2 */ for (i = -elsize_ * 2; i <= elsize_ * 2; i += elsize_) { for (size_ = 32 * KB; size_ <= size; size_ *= 2) { nchunks = get_nchunks(size_ + i, workingset); for (nthreads_ = 1; nthreads_ <= nthreads; nthreads_++) { do_bench(compressor, shuffle, nthreads_, size_ + i, elsize_, rshift_, output_file); blosc_set_timestamp(¤t); totaltime = blosc_elapsed_secs(last, current); printf("Elapsed time:\t %6.1f s. Processed data: %.1f GB\n", totaltime, totalsize / GB); } } } } } } else if (debug_suite) { for (rshift_ = rshift; rshift_ <= 32; rshift_++) { for (elsize_ = elsize; elsize_ <= 32; elsize_++) { /* The next loop is for getting sizes that are not power of 2 */ for (i = -elsize_ * 2; i <= elsize_ * 2; i += elsize_) { for (size_ = size; size_ <= (int) (16 * MB); size_ *= 2) { nchunks = get_nchunks(size_ + i, workingset); for (nthreads_ = nthreads; nthreads_ <= 6; nthreads_++) { do_bench(compressor, shuffle, nthreads_, size_ + i, elsize_, rshift_, output_file); blosc_set_timestamp(¤t); totaltime = blosc_elapsed_secs(last, current); printf("Elapsed time:\t %6.1f s. Processed data: %.1f GB\n", totaltime, totalsize / GB); } } } } } } /* Single mode */ else { do_bench(compressor, shuffle, nthreads, size, elsize, rshift, output_file); } /* Print out some statistics */ blosc_set_timestamp(¤t); totaltime = (float)blosc_elapsed_secs(last, current); printf("\nRound-trip compr/decompr on %.1f GB\n", totalsize / GB); printf("Elapsed time:\t %6.1f s, %.1f MB/s\n", totaltime, totalsize * 2 * 1.1 / (MB * totaltime)); /* Free blosc resources */ blosc2_free_resources(); blosc2_destroy(); return 0; } c-blosc2-3.1.5/bench/b2nd/000077500000000000000000000000001521743436100150365ustar00rootroot00000000000000c-blosc2-3.1.5/bench/b2nd/CMakeLists.txt000066400000000000000000000010241521743436100175730ustar00rootroot00000000000000# Blosc - Blocked Shuffling and Compression Library # # Copyright (c) 2021 Blosc Development Team # https://blosc.org # License: BSD 3-Clause (see LICENSE.txt) # # See LICENSE.txt for details about copyright and rights to use. file(GLOB SOURCES bench_*.c) foreach (source ${SOURCES}) get_filename_component(target_name ${source} NAME_WE) set(target b2nd_${target_name}) add_executable(${target} ${target_name}.c) target_link_libraries(${target} PUBLIC blosc_testing ${LIBS}) endforeach (source) c-blosc2-3.1.5/bench/b2nd/bench_concatenate.c000066400000000000000000000075421521743436100206350ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ // Benchmark for concatenating b2nd arrays. This is to check the fast path // that has been added to b2nd_concat() that allows for faster concatenation of // b2nd arrays when there are no partial (or zero-padded) chunks in the arrays being // concatenated. #include #include "blosc2.h" #include "b2nd.h" int main() { blosc2_init(); const int width = 1000; const int height = 1000; const int nimages_inbuf = 10; int64_t N_images = 1000; bool copy = false; // whether to copy the data or expand src1 // Shapes of the b2nd array // int64_t shape[] = {N_images, height, width}; // The initial shape of the array before concatenation int64_t shape[] = {nimages_inbuf, height, width}; int32_t chunkshape[] = {nimages_inbuf, height, width}; int32_t blockshape[] = {1, height, width}; // Determine the buffer size of the image (in bytes) const int64_t buffersize = nimages_inbuf * height * width * (int64_t)sizeof(uint16_t); uint16_t* image = malloc(buffersize); // Generate data for (int j = 0; j < nimages_inbuf * height * width; j++) { image[j] = j; } blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = sizeof(image[0]); blosc2_storage storage = BLOSC2_STORAGE_DEFAULTS; char *urlpath = "bench_concat.b2nd"; //storage.urlpath = urlpath; // for storing in a file storage.cparams = &cparams; char *accel_str; double t, t_accel; for (int accel=0; accel <= 1; accel++) { int32_t new_chunkshape[3] = {chunkshape[0], chunkshape[1], chunkshape[2]}; if (!accel) { new_chunkshape[0] = chunkshape[0] + 1; // to avoid the fast path accel_str = "non-fast path"; } else { accel_str = "fast path"; } blosc2_remove_urlpath(urlpath); b2nd_context_t *ctx = b2nd_create_ctx(&storage, 3, shape, new_chunkshape, blockshape, "|u2", DTYPE_NUMPY_FORMAT, NULL, 0); // The first array b2nd_array_t *src1; if (b2nd_empty(ctx, &src1) < 0) { printf("Error in b2nd_empty\n"); return -1; } // The second array, with the data in buffersize b2nd_array_t *src2; int ret = b2nd_from_cbuffer(ctx, &src2, image, buffersize); if (ret < 0) { printf("Error in b2nd_from_cbuffer\n"); return -1; } // Concatenate all images b2nd_array_t *array; blosc_timestamp_t t0, t1; blosc_set_timestamp(&t0); for (int i = 1; i < N_images / nimages_inbuf; i++) { if (b2nd_concatenate(ctx, src1, src2, 0, copy, &array) < 0) { printf("Error in b2nd_concatenate\n"); return -1; } if (copy) { // If we copy, then we need to free the src1 array b2nd_free(src1); } src1 = array; } blosc_set_timestamp(&t1); if (!accel) { t = blosc_elapsed_secs(t0, t1); printf("Time to append (%s): %.4f s\n", accel_str, t); } else { t_accel = blosc_elapsed_secs(t0, t1); printf("Time to append (%s): %.4f s\n", accel_str, t_accel); } printf("Number of chunks: %" PRId64 "\n", array->sc->nchunks); // printf("Shape of array: (%" PRId64 ", %" PRId64 ", %" PRId64 ")\n", // array->shape[0], array->shape[1], array->shape[2]); b2nd_free(src2); if (copy) { b2nd_free(array); } b2nd_free_ctx(ctx); } free(image); blosc2_remove_urlpath(urlpath); printf("Spedup: %.2fx\n", t / t_accel); blosc2_destroy(); return 0; } c-blosc2-3.1.5/bench/b2nd/bench_get_slice.c000066400000000000000000000044371521743436100203070ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #define DATA_TYPE int64_t # include int main() { blosc_timestamp_t t0, t1; blosc2_init(); int nslices = 10; int8_t ndim = 3; int32_t itemsize = sizeof(DATA_TYPE); int64_t shape[] = {1250, 745, 400}; int32_t chunkshape[] = {50, 150, 100}; int32_t blockshape[] = {13, 21, 30}; int64_t nbytes = itemsize; for (int i = 0; i < ndim; ++i) { nbytes *= shape[i]; } DATA_TYPE *src = malloc(nbytes); for (int i = 0; i < nbytes / itemsize; ++i) { src[i] = i; } blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.nthreads = 4; cparams.typesize = itemsize; blosc2_storage b2_storage = {.cparams=&cparams}; b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, ndim, shape, chunkshape, blockshape, NULL, 0, NULL, 0); b2nd_array_t *arr; blosc_set_timestamp(&t0); BLOSC_ERROR(b2nd_from_cbuffer(ctx, &arr, src, nbytes)); blosc_set_timestamp(&t1); printf("from_buffer: %.4f s\n", blosc_elapsed_secs(t0, t1)); blosc_set_timestamp(&t0); for (int dim = 0; dim < ndim; ++dim) { int64_t slice_start[B2ND_MAX_DIM], slice_stop[B2ND_MAX_DIM], slice_shape[B2ND_MAX_DIM]; int64_t buffersize = itemsize; for (int j = 0; j < ndim; ++j) { slice_start[j] = 0; slice_stop[j] = j == dim ? 1 : shape[j]; slice_shape[j] = slice_stop[j] - slice_start[j]; buffersize *= slice_shape[j]; } DATA_TYPE *buffer = malloc(buffersize); for (int slice = 0; slice < nslices; ++slice) { slice_start[dim] = rand() % shape[dim]; slice_stop[dim] = slice_start[dim] + 1; BLOSC_ERROR(b2nd_get_slice_cbuffer(arr, slice_start, slice_stop, buffer, slice_shape, buffersize)); } free(buffer); } blosc_set_timestamp(&t1); printf("get_slice: %.4f s\n", blosc_elapsed_secs(t0, t1)); free(src); BLOSC_ERROR(b2nd_free(arr)); BLOSC_ERROR(b2nd_free_ctx(ctx)); blosc2_destroy(); return 0; } c-blosc2-3.1.5/bench/b2nd/bench_stack_append.c000066400000000000000000000063561521743436100210070ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ // Benchmark for appending data to a b2nd array. A new accelerated path has been // added to b2nd_append() that allows for faster appending of data to a b2nd array // when data to append is of the same size as the chunkshape. This benchmark // compares the performance of the new accelerated path with the old one. #include #include "blosc2.h" #include "b2nd.h" int main() { blosc2_init(); const int width = 512; const int height = 256; const int nimages_inbuf = 10; const int64_t buffershape[] = {nimages_inbuf, height, width}; int64_t N_images = 1000; // Shapes of the b2nd array int64_t shape[] = {N_images, height, width}; int32_t chunkshape[] = {nimages_inbuf, height, width}; int32_t blockshape[] = {1, height, width}; // Determine the buffer size of the image (in bytes) const int64_t buffersize = nimages_inbuf * height * width * (int64_t)sizeof(uint16_t); uint16_t* image = malloc(buffersize); // Generate data for (int j = 0; j < nimages_inbuf * height * width; j++) { image[j] = j; } char *urlpath = "bench_stack_append.b2nd"; blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = sizeof(image[0]); blosc2_storage storage = BLOSC2_STORAGE_DEFAULTS; //storage.contiguous = true; // for a single file in output //storage.urlpath = urlpath; storage.cparams = &cparams; char *accel_str = "non-accel"; for (int accel=0; accel < 2; accel++) { if (accel) { shape[0] = 0; accel_str = "accel"; } blosc2_remove_urlpath(urlpath); b2nd_context_t *ctx = b2nd_create_ctx(&storage, 3, shape, chunkshape, blockshape, "|u2", DTYPE_NUMPY_FORMAT, NULL, 0); b2nd_array_t *src; if (b2nd_empty(ctx, &src) < 0) { printf("Error in b2nd_empty\n"); return -1; } // loop through all images blosc_timestamp_t t0, t1; blosc_set_timestamp(&t0); for (int i = 0; i < N_images / nimages_inbuf; i++) { if (accel) { if (b2nd_append(src, image, buffersize, 0) < 0) { printf("Error in b2nd_append\n"); return -1; } } else { int64_t start[] = {i, 0, 0}; int64_t stop[] = {i + nimages_inbuf, height, width}; if (b2nd_set_slice_cbuffer(image, buffershape, buffersize, start, stop, src) < 0) { printf("Error in b2nd_append\n"); return -1; } } } blosc_set_timestamp(&t1); printf("Time to append (%s): %.4f s\n", accel_str, blosc_elapsed_secs(t0, t1)); printf("Number of chunks: %" PRId64 "\n", src->sc->nchunks); printf("Shape of array: (%" PRId64 ", %" PRId64 ", %" PRId64 ")\n", src->shape[0], src->shape[1], src->shape[2]); b2nd_free(src); b2nd_free_ctx(ctx); } free(image); blosc2_destroy(); return 0; } c-blosc2-3.1.5/bench/b2nd/bench_zfp_getitem.c000066400000000000000000000173441521743436100206670ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /* * Benchmark to measure the retrieval time of a number of elements in random positions * in b2nd arrays. To get the necessary arrays (air1.cat, precip1.cat, snow1.cat...) * you can use the following script, changing the dataset by your preference (see * https://docs.digitalearthafrica.org/fr/latest/sandbox/notebooks/Datasets/Climate_Data_ERA5_AWS.html): 8<---snip---- "fetch_data.py" #!/usr/bin/env python import numpy as np import s3fs import xarray as xr import blosc2 def open_zarr(year, month, datestart, dateend): fs = s3fs.S3FileSystem(anon=True) datestring = "era5-pds/zarr/{year}/{month:02d}/data/".format(year=year, month=month) s3map = s3fs.S3Map(datestring + "precipitation_amount_1hour_Accumulation.zarr/", s3=fs) precip_zarr = xr.open_dataset(s3map, engine="zarr") precip_zarr = precip_zarr.sel(time1=slice(np.datetime64(datestart), np.datetime64(dateend))) return precip_zarr.precipitation_amount_1hour_Accumulation print("Fetching data from S3 (era5-pds)...") precip_m0 = open_zarr(1987, 10, "1987-10-01", "1987-10-30 23:59") precip0 = blosc2.empty(shape=precip_m0.shape, dtype=precip_m0.dtype, urlpath="precip1.b2nd") print("Fetching and storing 1st month...") values = precip_m0.values precip0[:] = values 8<---snip---- * To call this script, you can run the following commands: * $ pip install blosc2 * $ python fetch_data.py * */ #include "../plugins/codecs/zfp/zfp-private.h" #include "../../plugins/codecs/zfp/blosc2-zfp.h" #include "context.h" #include "blosc2/codecs-registry.h" #include "b2nd.h" #include "blosc2.h" int comp(const char *urlpath) { blosc2_init(); blosc2_schunk *schunk = blosc2_schunk_open(urlpath); if (schunk->typesize != 4) { printf("Error: This test is only for floats.\n"); return -1; } blosc2_remove_urlpath("schunk_rate.cat"); blosc2_remove_urlpath("schunk.cat"); // Get multidimensional parameters and configure Blosc2 NDim array int8_t ndim; int64_t shape[4]; int64_t shape_aux[4]; int32_t chunkshape[4]; int32_t blockshape[4]; uint8_t *smeta; int32_t smeta_len; if (blosc2_meta_get(schunk, "b2nd", &smeta, &smeta_len) < 0) { printf("This benchmark only supports b2nd arrays"); return -1; } char *dtype; int8_t dtype_format; b2nd_deserialize_meta(smeta, smeta_len, &ndim, shape_aux, chunkshape, blockshape, &dtype, &dtype_format); free(smeta); free(dtype); blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.nthreads = 6; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; blosc2_storage b2_storage = {.cparams=&cparams, .dparams=&dparams}; b2_storage.urlpath = "schunk_rate.cat"; b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, ndim, shape_aux, chunkshape, blockshape, NULL, 0, NULL, 0); b2nd_array_t *arr; b2nd_from_schunk(schunk, &arr); int copied; printf("LZ4 comp ratio: %f \n", (float) arr->sc->nbytes / (float) arr->sc->cbytes); /* Use BLOSC_CODEC_ZFP_FIXED_RATE */ b2nd_array_t *arr_rate; blosc2_context *ctx_zfp = blosc2_create_cctx(cparams); ctx_zfp->compcode = BLOSC_CODEC_ZFP_FIXED_RATE; ctx_zfp->splitmode = BLOSC_NEVER_SPLIT; ctx_zfp->compcode_meta = (uint8_t) (100.0 * (float) arr->sc->cbytes / (float) arr->sc->nbytes); ctx_zfp->filters[BLOSC2_MAX_FILTERS - 1] = 0; ctx_zfp->filters_meta[BLOSC2_MAX_FILTERS - 1] = 0; copied = b2nd_copy(ctx, arr, &arr_rate); if (copied != 0) { printf("Error BLOSC_CODEC_ZFP_FIXED_RATE \n"); b2nd_free(arr); return -1; } printf("ZFP_FIXED_RATE comp ratio: %f \n", (float) arr_rate->sc->nbytes / (float) arr_rate->sc->cbytes); int64_t nelems = arr_rate->nitems; int dsize_zfp, dsize_blosc; int64_t index; float item_zfp, item_blosc; blosc_timestamp_t t0, t1; double zfp_time, blosc_time; zfp_time = blosc_time = 0; int64_t index_ndim[ZFP_MAX_DIM]; int64_t index_chunk_ndim[ZFP_MAX_DIM]; int64_t ind_ndim[ZFP_MAX_DIM]; int32_t stride_chunk, ind_chunk; int64_t nchunk; bool needs_free_blosc, needs_free_zfp; uint8_t *chunk_blosc, *chunk_zfp; int32_t chunk_nbytes_zfp, chunk_cbytes_zfp, chunk_nbytes_lossy, chunk_cbytes_lossy; double ntests = 500.0; for (int i = 0; i < ntests; ++i) { srand(i); index = rand() % nelems; blosc2_unidim_to_multidim(ndim, shape, index, index_ndim); for (int j = 0; j < ndim; ++j) { index_chunk_ndim[j] = index_ndim[j] / chunkshape[j]; ind_ndim[j] = index_ndim[j] % chunkshape[j]; } stride_chunk = (int32_t)(shape[1] - 1) / chunkshape[1] + 1; nchunk = index_chunk_ndim[0] * stride_chunk + index_chunk_ndim[1]; ind_chunk = (int32_t) (ind_ndim[0] * chunkshape[1] + ind_ndim[1]); blosc2_schunk_get_lazychunk(arr->sc, nchunk, &chunk_blosc, &needs_free_blosc); blosc2_cbuffer_sizes(chunk_blosc, &chunk_nbytes_lossy, &chunk_cbytes_lossy, NULL); blosc_set_timestamp(&t0); dsize_blosc = blosc2_getitem_ctx(arr->sc->dctx, chunk_blosc, chunk_cbytes_lossy, ind_chunk, 1, &item_blosc, sizeof(item_blosc)); blosc_set_timestamp(&t1); blosc_time += blosc_elapsed_secs(t0, t1); blosc2_schunk_get_lazychunk(arr_rate->sc, nchunk, &chunk_zfp, &needs_free_zfp); blosc2_cbuffer_sizes(chunk_zfp, &chunk_nbytes_zfp, &chunk_cbytes_zfp, NULL); blosc_set_timestamp(&t0); dsize_zfp = blosc2_getitem_ctx(arr_rate->sc->dctx, chunk_zfp, chunk_cbytes_zfp, ind_chunk, 1, &item_zfp, sizeof(item_zfp)); blosc_set_timestamp(&t1); zfp_time += blosc_elapsed_secs(t0, t1); if (dsize_blosc != dsize_zfp) { printf("Different amount of items gotten"); return -1; } } printf("ZFP_FIXED_RATE time: %.5f microseconds\n", (zfp_time * 1000000.0 / ntests)); printf("Blosc2 time: %.5f microseconds\n", (blosc_time * 1000000.0 / ntests)); b2nd_free(arr); b2nd_free(arr_rate); BLOSC_ERROR(b2nd_free_ctx(ctx)); blosc2_free_ctx(ctx_zfp); if (needs_free_blosc) { free(chunk_blosc); } if (needs_free_zfp) { free(chunk_zfp); } blosc2_destroy(); return BLOSC2_ERROR_SUCCESS; } int solar1(void) { const char *urlpath = "../../bench/solar1.cat"; int result = comp(urlpath); return result; } int air1(void) { const char *urlpath = "../../bench/air1.cat"; int result = comp(urlpath); return result; } int snow1(void) { const char *urlpath = "../../bench/snow1.cat"; int result = comp(urlpath); return result; } int wind1(void) { const char *urlpath = "../../bench/wind1.cat"; int result = comp(urlpath); return result; } int precip1(void) { const char *urlpath = "../../bench/precip1.cat"; int result = comp(urlpath); return result; } int precip2(void) { const char *urlpath = "../../bench/precip2.cat"; int result = comp(urlpath); return result; } int precip3(void) { const char *urlpath = "../../bench/precip3.cat"; int result = comp(urlpath); return result; } int precip3m(void) { const char *urlpath = "../../bench/precip-3m.cat"; int result = comp(urlpath); return result; } int main() { printf("wind1 \n"); BLOSC_ERROR(wind1()); printf("air1 \n"); BLOSC_ERROR(air1()); printf("solar1 \n"); BLOSC_ERROR(solar1()); printf("snow1 \n"); BLOSC_ERROR(snow1()); printf("precip1 \n"); BLOSC_ERROR(precip1()); printf("precip2 \n"); BLOSC_ERROR(precip2()); printf("precip3 \n"); BLOSC_ERROR(precip3()); // printf("precip3m \n"); // BLOSC_ERROR(precip3m()); return BLOSC2_ERROR_SUCCESS; } c-blosc2-3.1.5/bench/create_frame.c000066400000000000000000000141431521743436100167750ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Simple benchmark for frame creation. To run: $ ./create_frame *** Creating uninitialized *** *** Using fill method! *** *** Creating *contiguous* super-chunk for *blosclz* Compression ratio: 186.26 GB -> 0.17 KB (1162790697.7x) Compression time: 8.5e-06 s, 21399.9 TB/s Decompression time: 0.0628 s, 2965.1 GB/s *** Creating *sparse* super-chunk for *blosclz* Compression ratio: 186.26 GB -> 3906.29 KB (49999.5x) Compression time: 0.00294 s, 61.8 TB/s Decompression time: 0.00424 s, 43959.2 GB/s *** Creating *contiguous* super-chunk for *lz4* Compression ratio: 186.26 GB -> 0.17 KB (1162790697.7x) Compression time: 4.71e-06 s, 38636.1 TB/s Decompression time: 0.0701 s, 2656.7 GB/s *** Creating *sparse* super-chunk for *lz4* Compression ratio: 186.26 GB -> 3906.29 KB (49999.5x) Compression time: 0.00311 s, 58.5 TB/s Decompression time: 0.0101 s, 18516.4 GB/s Process finished with exit code 0 */ #include #include #include #define KB (1024.) #define MB (1024 * KB) #define GB (1024 * MB) #define TB (1024 * GB) #define CHUNKSHAPE (500 * 1000) #define NCHUNKS 100000 #define NTHREADS 1 // curiously, using 1 single thread is better for the uninitialized values // For exercising the optimized chunk creators (un)comment the lines below as you please //#define CREATE_ZEROS #define CREATE_FILL //#define CREATE_LOOP int create_cframe(const char* compname, bool contiguous) { int32_t isize = CHUNKSHAPE * sizeof(int32_t); int32_t* data = malloc(isize); int32_t* data_dest = malloc(isize); int32_t* data_dest2 = malloc(isize); int64_t nbytes, cbytes; int nchunk; blosc_timestamp_t last, current; double ttotal; int compcode = blosc2_compname_to_compcode(compname); printf("\n*** Creating *%s* super-chunk for *%s*\n", contiguous ? "contiguous" : "sparse", compname); /* Create a super-chunk container */ blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = sizeof(int32_t); cparams.compcode = compcode; cparams.clevel = 9; cparams.nthreads = NTHREADS; //cparams.blocksize = 1024; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; dparams.nthreads = NTHREADS; char filename[64]; sprintf(filename, "frame_simple-%s.b2frame", compname); blosc2_storage storage = {.cparams=&cparams, .dparams=&dparams, .urlpath=NULL, .contiguous=contiguous}; blosc2_schunk* schunk = blosc2_schunk_new(&storage); #ifdef CREATE_ZEROS // Precompute chunk of zeros int ret = blosc2_chunk_zeros(cparams, isize, data_dest, isize); #else int ret = blosc2_chunk_uninit(cparams, isize, data_dest, isize); #endif if (ret < 0) { printf("Creation error in special chunk. Error code: %d\n", ret); return ret; } // Add some data blosc_set_timestamp(&last); int64_t nitems; #ifdef CREATE_FILL // Make nitems a non-divisible number of CHUNKSHAPE nitems = (int64_t)NCHUNKS * CHUNKSHAPE + 1; #ifdef CREATE_ZEROS // Precompute chunk of zeros int special_value = BLOSC2_SPECIAL_ZERO; #else int special_value = BLOSC2_SPECIAL_UNINIT; #endif int64_t rc = blosc2_schunk_fill_special(schunk, nitems, special_value, isize); if (rc < 0) { printf("Error in fill special. Error code: %" PRId64 "\n", rc); return (int)rc; } #else // In these methods, nitems can only be an actual multiple of CHUNKSHAPE nitems = (int64_t)NCHUNKS * CHUNKSHAPE; for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { #ifdef CREATE_LOOP int nchunks = blosc2_schunk_append_chunk(schunk, (uint8_t *) data_dest, true); if (nchunks != nchunk + 1) { printf("Compression error in append chunk. Error code: %d\n", nchunks); return nchunk; } #else for (int i = 0; i < CHUNKSHAPE; i++) { // Different data patterns // data[i] = i * nchunk; // data[i] = nchunk; data[i] = 0; } int nchunks = blosc2_schunk_append_buffer(schunk, data, isize); if (nchunks != nchunk + 1) { printf("Compression error appending in schunk. Error code: %d\n", nchunks); return nchunk; } #endif } #endif blosc_set_timestamp(¤t); /* Gather some info */ nbytes = schunk->nbytes; cbytes = blosc2_schunk_frame_len(schunk); ttotal = blosc_elapsed_secs(last, current); printf("Compression ratio: %.2f GB -> %.2f KB (%4.1fx)\n", (double)nbytes / GB, (double)cbytes / KB, (double)nbytes / (double)cbytes); printf("Compression time: %.3g s, %.1f TB/s\n", ttotal, (double)nbytes / (ttotal * TB)); /* Retrieve and decompress the chunks from the super-chunks and compare values */ blosc_set_timestamp(&last); int32_t leftover_bytes = (int32_t)((nitems % CHUNKSHAPE) * sizeof(int32_t)); int32_t nchunks = leftover_bytes ? NCHUNKS + 1 : NCHUNKS; for (nchunk = 0; nchunk < nchunks; nchunk++) { int32_t dsize = blosc2_schunk_decompress_chunk(schunk, nchunk, data_dest, isize); if (dsize < 0) { printf("Decompression error in schunk. Error code: %d\n", dsize); return dsize; } if ((nchunk == nchunks - 1) && (leftover_bytes > 0)) { if (dsize != leftover_bytes) { printf("Wrong size for last chunk. It is %d and should be: %d\n", dsize, leftover_bytes); return dsize; } } } blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Decompression time: %.3g s, %.1f GB/s\n", ttotal, (double)nbytes / (ttotal * GB)); /* Free resources */ blosc2_schunk_free(schunk); free(data); free(data_dest); free(data_dest2); return 0; } int main(void) { blosc2_init(); #ifdef CREATE_ZEROS printf("\n *** Creating zeros ***\n"); #else printf("\n *** Creating uninitialized ***\n"); #endif #ifdef CREATE_FILL printf(" *** Using fill method! ***\n"); #elif defined(CREATE_LOOP) printf(" *** Using loop method! ***\n"); #else printf(" *** Using not optimized method! ***\n"); #endif create_cframe("blosclz", true); create_cframe("blosclz", false); create_cframe("lz4", true); create_cframe("lz4", false); blosc2_destroy(); } c-blosc2-3.1.5/bench/delta_schunk.c000066400000000000000000000065111521743436100170240ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Benchmark showing Blosc filter from C code. To compile this program: $ gcc -O3 delta_schunk.c -o delta_schunk -lblosc2 */ #include #include #include #include #define KB 1024 #define MB (1024*KB) #define GB (1024*MB) #define CHUNKSIZE (50 * 1000) #define NCHUNKS 100 // Setting NTHREADS > 1 increases the likelihood of a crash. See #112. #define NTHREADS 1 int main(void) { int32_t *data, *data_dest; blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; blosc2_schunk *schunk; int32_t isize = CHUNKSIZE * sizeof(int32_t); int dsize; int64_t nbytes, cbytes; int nchunk; int64_t nchunks = 0; blosc_timestamp_t last, current; double totaltime; float totalsize = (float)(isize * NCHUNKS); data = malloc(CHUNKSIZE * sizeof(int32_t)); data_dest = malloc(CHUNKSIZE * sizeof(int32_t)); for (int i = 0; i < CHUNKSIZE; i++) { data[i] = i; } printf("Blosc version info: %s (%s)\n", BLOSC2_VERSION_STRING, BLOSC2_VERSION_DATE); /* Initialize the Blosc compressor */ blosc2_init(); /* Create a super-chunk container */ cparams.filters[0] = BLOSC_DELTA; //cparams.filters[BLOSC2_MAX_FILTERS - 1] = BLOSC_BITSHUFFLE; cparams.typesize = sizeof(int32_t); cparams.compcode = BLOSC_BLOSCLZ; cparams.clevel = 1; cparams.nthreads = NTHREADS; blosc2_storage storage = {.cparams=&cparams, .dparams=&dparams}; schunk = blosc2_schunk_new(&storage); /* Append chunks (the first will be taken as reference for delta) */ blosc_set_timestamp(&last); for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { nchunks = blosc2_schunk_append_buffer(schunk, data, isize); } blosc_set_timestamp(¤t); totaltime = blosc_elapsed_secs(last, current); printf("[Compr] Elapsed time:\t %6.3f s. Processed data: %.3f GB (%.3f GB/s)\n", totaltime, totalsize / GB, totalsize / (GB * totaltime)); /* Gather some info */ nbytes = schunk->nbytes; cbytes = schunk->cbytes; printf("Compression super-chunk: %ld -> %ld (%.1fx)\n", (long)nbytes, (long)cbytes, (1. * (double)nbytes) / (double)cbytes); /* Retrieve and decompress the chunks */ blosc_set_timestamp(&last); for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { dsize = blosc2_schunk_decompress_chunk(schunk, nchunk, data_dest, isize); if (dsize < 0) { printf("Decompression error. Error code: %d\n", dsize); return dsize; } assert (dsize == (int)isize); } blosc_set_timestamp(¤t); totaltime = blosc_elapsed_secs(last, current); totalsize = (float)(isize * nchunks); printf("[Decompr] Elapsed time:\t %6.3f s. Processed data: %.3f GB (%.3f GB/s)\n", totaltime, totalsize / GB, totalsize / (GB * totaltime)); printf("Decompression successful!\n"); for (int i = 0; i < CHUNKSIZE; i++) { if (data[i] != data_dest[i]) { printf("Decompressed data differs from original %d, %d, %d!\n", i, data[i], data_dest[i]); return -1; } } printf("Successful roundtrip!\n"); /* Free resources */ free(data); free(data_dest); /* Destroy the super-chunk */ blosc2_schunk_free(schunk); /* Destroy the Blosc environment */ blosc2_destroy(); return 0; } c-blosc2-3.1.5/bench/get_sparse.c000066400000000000000000000155631521743436100165230ustar00rootroot00000000000000/* Copyright (c) 2026 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Benchmark for reading individual coordinates from a 1-D b2nd array. To run from the repository root: $ ./build/bench/read-coords bench/sin-1d.b2nd [ncoords] */ /* Use: import numpy as np import blosc2 N = 24_000_000 a = blosc2.linspace(0., 1., N, dtype=np.float32) b = blosc2.sin(a).compute(urlpath="sin-1d.b2nd", mode="w") print(b[:]) for generating the data file. */ #include #include #include #include #include #include #include #include #define DEFAULT_NCOORDS 100 #define NPRINT 10 #define NTHREADS 4 static void fill_random_coords(int64_t *coords, int64_t ncoords, int64_t nitems) { uint64_t state = UINT64_C(0x20260522); for (int64_t i = 0; i < ncoords; ++i) { /* xorshift64*: deterministic and good enough for benchmark coordinates. */ state ^= state >> 12; state ^= state << 25; state ^= state >> 27; uint64_t r = state * UINT64_C(2685821657736338717); coords[i] = (int64_t)(r % (uint64_t)nitems); } } static int open_array(const char **urlpath, bool use_fallbacks, b2nd_array_t **array) { int rc = b2nd_open(*urlpath, array); if (rc >= 0 || *array != NULL || !use_fallbacks) { if (rc < 0) { fprintf(stderr, "Cannot open %s (error %d)\n", *urlpath, rc); } return rc; } const char *fallbacks[] = {"../bench/sin-1d.b2nd", "../../bench/sin-1d.b2nd"}; for (size_t i = 0; i < sizeof(fallbacks) / sizeof(fallbacks[0]); ++i) { *urlpath = fallbacks[i]; rc = b2nd_open(*urlpath, array); if (rc >= 0) { return rc; } } fprintf(stderr, "Cannot open sin-1d.b2nd (last error %d)\n", rc); return rc; } static int getitem_sparse(const b2nd_array_t *array, const int64_t *coords, int64_t ncoords, float *data) { int64_t chunk_nitems = array->sc->chunksize / array->sc->typesize; for (int64_t i = 0; i < ncoords; ++i) { int64_t coord = coords[i]; int64_t nchunk = coord / chunk_nitems; int start = (int)(coord % chunk_nitems); uint8_t *chunk = NULL; bool needs_free = false; int cbytes = blosc2_schunk_get_lazychunk(array->sc, nchunk, &chunk, &needs_free); if (cbytes <= 0) { fprintf(stderr, "Cannot get chunk %" PRId64 " for coordinate %" PRId64 " (error %d)\n", nchunk, coord, cbytes); return cbytes < 0 ? cbytes : -1; } int nbytes = blosc2_getitem_ctx(array->sc->dctx, chunk, cbytes, start, 1, &data[i], sizeof(float)); if (needs_free) { free(chunk); } if (nbytes != (int)sizeof(float)) { fprintf(stderr, "Cannot get coordinate %" PRId64 " from chunk %" PRId64 " (error %d)\n", coord, nchunk, nbytes); return nbytes < 0 ? nbytes : -1; } } return 0; } int main(int argc, char **argv) { const char *urlpath = (argc > 1) ? argv[1] : "bench/sin-1d.b2nd"; int64_t ncoords = (argc > 2) ? strtoll(argv[2], NULL, 10) : DEFAULT_NCOORDS; bool new_first = (argc > 3) && (strcmp(argv[3], "new-first") == 0); bool getitem_only = (argc > 3) && (strcmp(argv[3], "getitem-only") == 0); bool new_only = (argc > 3) && (strcmp(argv[3], "new-only") == 0); b2nd_array_t *array = NULL; int64_t *coords = NULL; float *data_getitem = NULL; float *data_new = NULL; blosc_timestamp_t t0, t1; double getitem_time; double new_time; blosc2_init(); blosc2_set_nthreads(NTHREADS); if (ncoords <= 0) { fprintf(stderr, "ncoords must be positive\n"); blosc2_destroy(); return EXIT_FAILURE; } if (open_array(&urlpath, argc == 1, &array) < 0) { blosc2_destroy(); return EXIT_FAILURE; } if (array->ndim != 1) { fprintf(stderr, "Expected a 1-D array, got %d dimensions\n", array->ndim); b2nd_free(array); blosc2_destroy(); return EXIT_FAILURE; } if (array->sc->typesize != (int32_t)sizeof(float)) { fprintf(stderr, "Expected float elements (typesize %zu), got typesize %d\n", sizeof(float), array->sc->typesize); b2nd_free(array); blosc2_destroy(); return EXIT_FAILURE; } coords = malloc((size_t)ncoords * sizeof(int64_t)); data_getitem = malloc((size_t)ncoords * sizeof(float)); data_new = malloc((size_t)ncoords * sizeof(float)); if (coords == NULL || data_getitem == NULL || data_new == NULL) { fprintf(stderr, "Cannot allocate benchmark buffers\n"); free(coords); free(data_getitem); free(data_new); b2nd_free(array); blosc2_destroy(); return EXIT_FAILURE; } fill_random_coords(coords, ncoords, array->shape[0]); if (new_first || new_only) { blosc_set_timestamp(&t0); int rc = blosc2_schunk_get_sparse_buffer(array->sc, ncoords, coords, data_new); blosc_set_timestamp(&t1); new_time = blosc_elapsed_secs(t0, t1); if (rc < 0) { fprintf(stderr, "blosc2_schunk_get_sparse failed (error %d)\n", rc); free(coords); free(data_getitem); free(data_new); b2nd_free(array); blosc2_destroy(); return EXIT_FAILURE; } } if (!new_only) { blosc_set_timestamp(&t0); if (getitem_sparse(array, coords, ncoords, data_getitem) < 0) { free(coords); free(data_getitem); free(data_new); b2nd_free(array); blosc2_destroy(); return EXIT_FAILURE; } blosc_set_timestamp(&t1); getitem_time = blosc_elapsed_secs(t0, t1); } if (!new_first && !getitem_only && !new_only) { blosc_set_timestamp(&t0); int rc = blosc2_schunk_get_sparse_buffer(array->sc, ncoords, coords, data_new); blosc_set_timestamp(&t1); new_time = blosc_elapsed_secs(t0, t1); if (rc < 0) { fprintf(stderr, "blosc2_schunk_get_sparse failed (error %d)\n", rc); free(coords); free(data_getitem); free(data_new); b2nd_free(array); blosc2_destroy(); return EXIT_FAILURE; } } if (!getitem_only && !new_only) { for (int64_t i = 0; i < ncoords; ++i) { if (data_getitem[i] != data_new[i]) { fprintf(stderr, "Mismatched result at %" PRId64 ": getitem_sparse=%.9g, new_sparse=%.9g\n", i, data_getitem[i], data_new[i]); free(coords); free(data_getitem); free(data_new); b2nd_free(array); blosc2_destroy(); return EXIT_FAILURE; } } } printf("Read %" PRId64 " random coordinates from %s (%d threads)\n", ncoords, urlpath, NTHREADS); int64_t nprint = ncoords < NPRINT ? ncoords : NPRINT; printf("First %" PRId64 " retrieved elements:\n", nprint); for (int64_t i = 0; i < nprint; ++i) { printf(" coord[%" PRId64 "] = %" PRId64 ", value = %.9g\n", i, coords[i], data_new[i]); } if (!new_only) { printf("getitem_sparse: %.9f s\n", getitem_time); } if (!getitem_only) { printf("new_sparse: %.9f s\n", new_time); } free(coords); free(data_getitem); free(data_new); b2nd_free(array); blosc2_destroy(); return EXIT_SUCCESS; } c-blosc2-3.1.5/bench/plot-speeds.py000066400000000000000000000164211521743436100170260ustar00rootroot00000000000000""" Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Script for plotting the results of the 'suite' benchmark. Invoke without parameters for usage hints. """ import matplotlib as mpl from pylab import * KB_ = 1024 MB_ = 1024*KB_ GB_ = 1024*MB_ NCHUNKS = 128 # keep in sync with bench.c linewidth=2 #markers= ['+', ',', 'o', '.', 's', 'v', 'x', '>', '<', '^'] #markers= [ 'x', '+', 'o', 's', 'v', '^', '>', '<', ] markers= [ 's', 'o', 'v', '^', '+', 'x', '>', '<', '.', ',' ] markersize = 8 def get_values(filename): f = open(filename) values = {"memcpyw": [], "memcpyr": []} for line in f: if line.startswith('-->'): tmp = line.split('-->')[1] nthreads, size, elsize, sbits, codec, shuffle = [i for i in tmp.split(', ')] nthreads, size, elsize, sbits = map(int, (nthreads, size, elsize, sbits)) values["size"] = size / MB_ values["elsize"] = elsize values["sbits"] = sbits values["codec"] = codec values["shuffle"] = shuffle # New run for nthreads (ratios, speedsw, speedsr) = ([], [], []) # Add a new entry for (ratios, speedw, speedr) values[nthreads] = (ratios, speedsw, speedsr) #print("-->", nthreads, size, elsize, sbits) elif line.startswith('memcpy(write):'): tmp = line.split(',')[1] memcpyw = float(tmp.split(' ')[1]) / 1024. values["memcpyw"].append(memcpyw) elif line.startswith('memcpy(read):'): tmp = line.split(',')[1] memcpyr = float(tmp.split(' ')[1]) / 1024. values["memcpyr"].append(memcpyr) elif line.startswith('comp(write):'): tmp = line.split(',')[1] speedw = float(tmp.split(' ')[1]) / 1024. ratio = float(line.split(':')[-1]) speedsw.append(speedw) ratios.append(ratio) elif line.startswith('decomp(read):'): tmp = line.split(',')[1] speedr = float(tmp.split(' ')[1]) / 1024. speedsr.append(speedr) if "OK" not in line: print("WARNING! OK not found in decomp line!") f.close() return nthreads, values def show_plot(plots, yaxis, legends, gtitle, xmax=None, ymax=None): xlabel('Compression ratio') ylabel('Speed (GB/s)') title(gtitle) xlim(0, xmax) ylim(0, ymax) grid(True) # legends = [f[f.find('-'):f.index('.out')] for f in filenames] # legends = [l.replace('-', ' ') for l in legends] #legend([p[0] for p in plots], legends, loc = "upper left") legend([p[0] for p in plots if not isinstance(p, mpl.lines.Line2D)], legends, loc = "best") #subplots_adjust(bottom=0.2, top=None, wspace=0.2, hspace=0.2) if outfile: print("Saving plot to:", outfile) savefig(outfile, dpi=100) else: show() if __name__ == '__main__': from optparse import OptionParser usage = "usage: %prog [-r] [-o outfile] [-t title ] [-d|-c] filename" compress_title = 'Compression speed' decompress_title = 'Decompression speed' yaxis = 'No axis name' parser = OptionParser(usage=usage) parser.add_option('-o', '--outfile', dest='outfile', help=('filename for output (many extensions ' 'supported, e.g. .png, .jpg, .pdf)')) parser.add_option('-t', '--title', dest='title', help='title of the plot',) parser.add_option('-l', '--limit', dest='limit', help='expression to limit number of threads shown',) parser.add_option('-x', '--xmax', dest='xmax', help='limit the x-axis', default=None) parser.add_option('-y', '--ymax', dest='ymax', help='limit the y-axis', default=None) parser.add_option('-r', '--report', action='store_true', dest='report', help='generate file for reporting ', default=False) parser.add_option('-d', '--decompress', action='store_true', dest='dspeed', help='plot decompression data', default=False) parser.add_option('-c', '--compress', action='store_true', dest='cspeed', help='plot compression data', default=False) (options, args) = parser.parse_args() if len(args) == 0: parser.error("No input arguments") elif len(args) > 1: parser.error("Too many input arguments") else: pass if options.report and options.outfile: parser.error("Can only select one of [-r, -o]") if options.dspeed and options.cspeed: parser.error("Can only select one of [-d, -c]") elif options.cspeed: options.dspeed = False plot_title = compress_title else: # either neither or dspeed options.dspeed = True plot_title = decompress_title filename = args[0] cspeed = options.cspeed dspeed = options.dspeed if options.outfile: outfile = options.outfile elif options.report: if cspeed: outfile = filename[:filename.rindex('.')] + '-compr.png' else: outfile = filename[:filename.rindex('.')] + '-decompr.png' else: outfile = None plots = [] legends = [] nthreads, values = get_values(filename) #print("Values:", values) if options.limit: thread_range = eval(options.limit) else: thread_range = range(1, nthreads+1) if options.title: plot_title = options.title else: plot_title += " (%(size).1f MB, %(elsize)d bytes, %(sbits)d bits), %(codec)s, %(shuffle)s" % values gtitle = plot_title for nt in thread_range: #print("Values for %s threads --> %s" % (nt, values[nt])) (ratios, speedw, speedr) = values[nt] if cspeed: speed = speedw else: speed = speedr #plot_ = semilogx(ratios, speed, linewidth=2) plot_ = plot(ratios, speed, linewidth=2) plots.append(plot_) nmarker = nt if nt >= len(markers): nmarker = nt%len(markers) setp(plot_, marker=markers[nmarker], markersize=markersize, linewidth=linewidth) legends.append("%d threads" % nt) # Add memcpy lines # Take the first value found in order to avoid cache effects if cspeed: # memcpy_speed = np.mean(values["memcpyw"]) memcpy_speed = values["memcpyw"][0] message = "memcpy (write to memory)" else: # memcpy_speed = np.mean(values["memcpyr"]) memcpy_speed = values["memcpyr"][0] message = "memcpy (read from memory)" plot_ = axhline(memcpy_speed, linewidth=3, linestyle='-.', color='black') text(4.0, memcpy_speed + .5, message) plots.append(plot_) show_plot(plots, yaxis, legends, gtitle, xmax=int(options.xmax) if options.xmax else None, ymax=int(options.ymax) if options.ymax else None) c-blosc2-3.1.5/bench/plot-sum_openmp-results.py000066400000000000000000000033771521743436100214320ustar00rootroot00000000000000import matplotlib.pyplot as plt import numpy as np labels = ['1', '2', '4', '8', '12', '14', '16', '20', '24', '28', '32'] uncompressed = [ 11909.0, 21645.4, 40004.2, 56435.1, 57749.5, 58024.4, 49663.5, 55813.8, 55357.2, 56296.0, 46849.2] blosclz_cl1 = [ 6016.4, 11062.0, 21284.2, 41452.4, 57542.6, 65535.6, 54084.9, 65367.7, 74655.7, 86628.0, 53853.5] lz4_cl1 = [ 5194.0, 9465.5, 18262.0, 35698.7, 50247.9, 57009.0, 42268.3, 52207.0, 59755.1, 67353.5, 36658.2] lz4hc_cl1 = [ 6109.7, 11114.1, 21368.7, 41932.4, 57268.5, 66780.0, 50380.2, 61220.5, 69262.0, 82380.5, 37305.5] zstd_cl1 = [ 1593.8, 2955.5, 5669.4, 11244.7, 16363.2, 18377.6, 12865.2, 15830.7, 18736.9, 21474.3, 10466.3] # Use GB/s uncompressed = np.array(uncompressed) / 1024. blosclz_cl1 = np.array(blosclz_cl1) / 1024. lz4_cl1 = np.array(lz4_cl1) / 1024. lz4hc_cl1 = np.array(lz4hc_cl1) / 1024. zstd_cl1 = np.array(zstd_cl1) / 1024. x = np.arange(len(labels)) # the label locations width = 0.30 / 2 # the width of the bars fig, ax = plt.subplots() rects1 = ax.bar(x, uncompressed, width, label='Uncompressed') rects2 = ax.bar(x + width, blosclz_cl1, width, label='blosclz cl-1 (3.7x cr)') rects3 = ax.bar(x + 2 * width, lz4_cl1, width, label='lz4 cl-1 (4.5x cr)') rects4 = ax.bar(x + 3 * width, lz4hc_cl1, width, label='lz4hc cl-1 (5.0x cr)') rects5 = ax.bar(x + 4 * width, zstd_cl1, width, label='zstd cl-1 (5.9x cr)') # Add some text for labels, title and custom x-axis tick labels, etc. ax.set_ylabel('Speed (GB/s)') ax.set_xlabel('Threads') ax.set_title('Summing up precipitation data (381.5 MB, float32)') ax.set_xticks(x) ax.set_xticklabels(labels) ax.legend() #ax.bar_label(rects1, padding=3) #ax.bar_label(rects2, padding=3) #ax.bar_label(rects2, padding=3) fig.tight_layout() plt.show()c-blosc2-3.1.5/bench/rainfall-grid-150x150.bin000066400000000000000000000443171521743436100203500ustar00rootroot00000000000000__Hg!)2!^` @@@@@ p@  @ s@@@ @@@ r @@@`@@ u @ p @"`  k@ v"@@` |  @`#z @` bW} @ @@ d n @#| y ``@ @w @@@@@@#zH/P@@!z @"RP@@ x&n` @{@ @ @u@$0@ @@t@`Ѐ`@ @@y `А bR @v @`` @@t@@@`@D@@@r@ @gq @(-`` !( @@@d `@0`B\@@ @ eo@@`А@ `l @@@ @@  mp` @@@@@o `$"^ @ `i s@@%@`@.@@i@ Ш $! @n`0`! `$&!D@ j @ p !@@ @i@@H@``!@@` k t@P```p p @`@l@`8p@@` @@@@h @`@0M| @@`@@!2C@g@ @ РP @P А+,` @ e  @0Pp`P00 ``"!)@g@"@@$@  `@A/e `Pp !)@ `` @f@`p@`P@@ @ `@@@`@d@Pp``"@ @ !g pPp@ { @@@'` "Z" c @PA)@ ``@ @@b@pp 0P@` @ ` @c`@@)]@ @@@ @!(a Р@ @ @@!`@ _ `0 ` `@!  @ @b @ 00`@ ` ` `@P0s @` @ !/@ _#f`!,@ @ "T  ` @ @@ Bs"a^`@p&o@`  @@@`@" `_`@p  @@``@```_а@@ @` @@_!``@@   `` @ `a @' @`$"` a ``!- `@ !# `a@`! ! $"d@ `@@  `@ !`@c"@* @   @@e@ "U` d @ ` @@`@`Ba h @@`d@f+  ` ```@@z`$!@@w*` `v@%@Bz@` `@ w!$ @` @@ v@@@@@ x @ @ !/@w@&yBx ` $y "% Ay@@"` x ! `@"@ z @@@```(8{!@ $@@"z!P@ `"{@``@% z P @@|@:r |B @# z@p@@@```{A @p `@@ "[z3pP0`@@&||,@@ @@@` @{=``@p} `0@ ` P} Р``P +p@`@Ѐ|@A(:`}` $ P`} P }P` @}`@'@ {P '0@`|@ `0@|@``p`}@```P```y0@ `P!7xЀ! $@$x@"GP+`F{x`!@`#x@@&@@@`~C%p!5`#@ R n `@@`%Lw@ P@ "@z @ ``ax $#|p`@@@@xA+ @`@ V@ @@@ f)]@ |  Q `@!*!( Q ` "S`A )Q @ @@ z ) U`@`*`` @ r  T%9 @@``!  $@O@ `"V@@X@` @ z @|*A"` !' |*@ @ `@o@ `@ @dv @``@`u+@!!@ o @` ! r   @@ `!5@r @@ @t @#  @s &n@ x@ @!)  u  t~@ @   t @!+  !'! t~@%G@t @@s@@!'@) !,  {@ ` ~ @} !@}!(@ +/+@@+  @@`@=&!S`: rb5" ⰐDZlE#۲l<pI` @P VU/;w ⫂TBIoQ缧xV*GX0SJ D;#9v[٪z> +[RZ26Hbtvb2pE@XT<¨RC ̣q4 Eexx]$2@VL 0@FX:8 @ʶ \IvAf0 Ҡ +EK.b0 XFJ -`HP܇HnJ\pSºj?T@@ 4@`  lqd`lᤳd7˄ @* C*j|#`bs 0%Y\+L0A.8bNTh!0TD;d ffzMz  `*2n p6lhGJQt WATxx82Ѐ00H(D<9@2|ڨ# 7Ibw_mh~b` ĔpP0A FXB] z%Ap E4 X k`3K м:̄H@$~:w!Ξx%r2ܨX(O@QPn$H @4 e:{'˩}|öY (aK``pŏ(4 T$A±ήvy̲< ̸DpP3$p)N'G̻]˴ͥl2 H<h(8~@ L*LֿBN$|>( Y- /N/Wݦ5ο˝h/(ؖr` Tbx IY5gž|B 8$$ՍHx# R  bg;vԽO,,E (؈ ` :(Xh(xy@" òaC5g5% vP @U@Рp4D)8,& ŭe[a_:ЌD !@7U V H0OF?9.&"  ʫch@.x B]U`p `ZNCk__XRE=9'"8ЈXB6(`W `6/UU{|si`OA6-%' ](ϡrF6&8X<w[gyk^N>74)ǚj*T.Mp!a{zom]Q@<="ڼWુB W@`0u&gyook_OC=*Ѷ<Ơ@0W@,lnj{nh`hdZK?7&ʢeѷF8` `Qȍ1p ͐s_XVTa`MC:5Ʀ4𶹺<@@ RȦ/5tܘ{jUHEHWXN:2,# ߯]Ô"^Q̭:9z馶}qaJ957FNF:/% ݭQጡvOy @ H$=>bU>&&5>=7,&DzAxX L8CWB. /4.* ܵ*|hd`` 4 p, HJ( $"  $ػlxP`((@08V J pp, ##" Ӱ v) ` XxP/8`;8 K VTOҺ  "% ˯Vĸ8 XX Р6(* Lp*I2toaXx%!z,@@p ` Xh8@"~+& NۉHmǑYC%*޻bӡ !hԤԸPe@@  ` @` ` @@!Nq*3Oz$^17) Ȩ~&` .2`a2.@@ @ @@ 17@`@LZ4d,D/?>ܶI(А`HxhԔ.6@ ` 6jh`` !"  @΂F嫉c|p4@Vj +FD1ǤSX`! xȰphNx0xn\~ ؘ` `( HP0j<٣nHB FR(AO= غn180 `Pp`PHh(@ (04Xb$`06 vj`(Ќ8X @x0I0vΝ{8n&ARD: ѰRа 82e@ X@8h0`@H8Jh\X nH" x0@ P 0X$eĤL bഈ%?Vz&COH7ɤz!.PH@@@H8TTZDĜpP 4pV0 XPP( PIM 䄀P$\Jdx@%"|B[_M- H@=D?FCV@> BD<( )h@ h0!6@@شH6h.4h8H@ 8p @ (CJJAO9:B GC0 S X`  @pp:h xhhx(pHH H` `c[USK6 @" ~#@ @x` AXTM9=X  [ `'j x#G[VP>*'%,P-z\ " `@@АĤh2R_YSC221  \ C0`Ȱ9:E_c^XH:==( _$$o ȸQgGZpke^LAHK1!a `;OoxocRFNU9%&* ^a%@ȹ.zfWIO[A,-3p`xȲ%'3jZIM\F36;o`Р q)m[HFYI9>D"k``^4p\F@RKAGMc (ԔV@oYC8KKHPYn`0hȨlPgOlS=3CLM\fo@xNKKdfJ70>LRetn P0 E+}_@1.kX@QvwtJ ,\`6D xx!,` jO-{0%=cjZJ f26fޭH'8ܠ]/G \/LҷhJ 6^^A% 4rFb` 4 @ ؔ0`,)~8bX0 ѳ 鼾ֵr@6 Ј``  Y3DjU  7/䌏ſhNG~@(ܴx$ a,*'f:YqKբv zuòTa`H1X0%@ @\8E&!]jm4򸏒 H.؈^*%}'@"!hH@cb.h^MxnYʟG .x>4V8 ! pX @`7; .P'3rc:q :\(&d`8Cpp @$0x`8)(ǀk1P|`]sLÙ{`w N@hZ^ <` 8P" PpH8( 5G'WzAsyV1\Ke L*HİX(hH@@80 bpO{i̚\o\:ؘ}<2Q@K@`px` xpp`0 " hhXP. -t*9aU:' Ċb<1L$tFF87@$0?!* +^SfMT6!|F "ޝ#/sѺz8 &8$- HxpP0 Vä#w~6-R@c*x>T^( [rL~Y{RN 7 $bPp+`0@0 %KwX HM/D x|HTQK5$/*x 3!$Y~}`mf[=4RB(ҽY1Ǡd/: & D,6:)`@  Dܺ1PNPN,IN9.,KҜ Ĩ)[ϼ<"xlLPh\R-'@DJ /12:NM :MH;:18#XD ) ny<&@WA!HLJD:%K\!X,Y4RS!2LSUE% ʂ "W`AMDU6 :WcW3Z`0^/*E8CimEҟY  M J) 'Qsh/ӟc" &( `9H2YnX&՜g9-1367$hl"˟9V^C# ˒iJ.(137cXHȫq(>GB/ 趆kS4%(+4@pP" Eө21*Ο{iQ.,AqHО ݰueF"#?p`ݷ~f; =bBjv૊ ῱r>b4xX`<ҙ_ڱO bа($v)2~8\d bM*=d_p(1$#B_0(`"ɻ j%̚v/'B)*I]"8P@γ0De ӥ7,H.7SV @(XhX0xr" ӮB+?8L_S  @HxX @ (Hp,܀h0 ͹I"0Ljm"I@ Px8hH X` ɥI-oH @8h8.xrܭB >%J P԰x`)E(!KЦ!h<cWИ0p:` @ ExA7ͱW ̜8< 6XpSS `Pp`@pjM 㗄 N'`5S,2 `@26@Pޫ)@```( 6,} !S<=>=>`?@@`` "==<;H;<<U l@t` @`K<< U;@@  @G- T@a,+@ 9@(@GaTl } @@  n6=< i m @ @ Ai`a, _ ;@j @} + < @ 2; &`c`|@!@ @@ #, 9,;Bo @ ` @@\` $@ "F aU`   * A @@@S c 6I@1  S `![ ? C7!,!M`!@ 'A, @R / @f! m@sa( ` @ Ax "@ +@(A, !,  s @@IP !A,EA%` ` I!`F( f E"6a,@K )@Efy m"!d( Dd-#=Q,94,' a+ % Be)*"G@ j f 4J +@-*@%V`G$; m@oa !W@!,`H o ka-@' )"OA @($ ~ + `& @IA s ;;`  !ÀL$ p '@ "Q`  R @! ``)@ @>N!,  !@+`O@!#@k! v+- `>N$ ``` -@O`'L~bV   .>O&< A @A-Nq B] }`` U`]K a +-\+A,!CT!+" V !* ,d#L!) +U%+A` @@A"`N =Q O@z+``@ 5@!P+`zA` 3 K @A) #@$S @<)@?  a- @< B`;A }`!+5 y @d  `'3` ` b z(q @ (b,`{@ lAA @@ % !a-Je@! >zB`!"l  }9 F ` @`1;Mw`y ] @ @ @o 7A@5`@ @&!  + a ` fr`x@ u^` @j@h` 5  `%  @ @   B p V [@`??! g! _ 7@@@ @ g!5 7 !o p@@  @ Y   A @4  *,@`@ ?? !`!@a ca7`@   @ @) O=x u J * ? 1 l g;!7 `    z u@V" `@ @^`o # @=@ @ .X@? !@c@f ` Q ,!0`` !!3 9    ,@ B! `@ "X l a =` 9 @ `@ @ @@T @ `@1@#~;Da@`  !# @`@ RV@  0, M@ { @ "b`~ `X @ n FA `#@)  ;"@`< H@@@@ `U !0!* w 5@@@"+@O@` B J@ @@@ /@ $ BZ@  E @{ B@@@@ @#(@ c@` C@l%E  ? `q!* =D ` E@G`` T"@@ `Cu  ?@ ! " @A"[ i@` G@` #!,`G` @ @  D@ eA$@@@@>`O` @ ()  F@V=D  @` @L  T.@F >@G k !!)?+A, @BP` M@N W ) A, ` "` `@`@ - a! @ +a  K@ 1A*@ R!@! `ca  7@G@B5&@ @!1!H `0 E T;! !+A + ` .`@ %A@?@F ,OU?@ {5!, Fq5 !k@cA+@@ UB  ^ @ !( +^` td _p`@"\@ix+ +%b"!'@ !Uc$`)cd )c@dA+Hb $>>@c.}@b@ %.|+!1+3 =c k$@ La`!0VwIf$ :=V  & ]@ !j @6?  H > 1@w 8 t""f ` = c`7@ ?@? ? 1-=;%\5% Z; b #?!("k%@A * }`1 w0 >> B@ * ` h! a* `{ `@#*$ Հ *@ a  ` C+@$  `k .@ `!!!&` -@ bA*A   . E*0e' n "?>+M B ` (* %Va,d @ @@ 'THà`+9@(d ! `db[1C?cG,  ^(@+;-堊a$  \@iV -I(`@Z I @ W#s``&s ?#U *`q `* ^@A,  _#v `"X!"@ !4^`@y,o H `!* * ,  S @v!"?1-5(; @T )@ https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Script to extract a small grid of precipitation out of this file: ftp://ftp-cdc.dwd.de/pub/REA/COSMO_REA6/hourly/2D/TOT_PRECIP/TOT_PRECIP.2D.201512.grb.bz2 After downloading it and uncompressing it, just run this script for extracting a small grid. For more info on these datasets, see https://reanalysis.meteo.uni-bonn.de. """ from osgeo import gdal import numpy as np import blosc # Read a 150x150 grid from the first band dataset = gdal.Open("TOT_PRECIP.2D.201512.grb", gdal.GA_ReadOnly) band = dataset.GetRasterBand(1) precip_data = band.ReadAsArray(0, 0, 150, 150).astype(np.float32) # Compress the chunk and write it to a file cdata = blosc.compress(precip_data, cname="blosclz", clevel=9, typesize=4) open("rainfall-band-150x150.bin", "wb").write(cdata) c-blosc2-3.1.5/bench/results-corex/000077500000000000000000000000001521743436100170305ustar00rootroot00000000000000c-blosc2-3.1.5/bench/results-corex/blosclz-cl1-sum_openmp-corex.out000066400000000000000000000260241521743436100252100ustar00rootroot00000000000000faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=1 SUM_CLEVEL=1 SUM_COMPRESSOR=blosclz ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 45319996 Sum time for uncompressed data: 0.0323 s, 11810.5 MB/s Compression ratio: 381.5 MB -> 103.3 MB (3.7x) Compression time: 0.73 s, 522.4 MB/s Sum for *compressed* data: 46323164 Sum time for *compressed* data: 0.0634 s, 6016.4 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=1 SUM_CLEVEL=1 SUM_COMPRESSOR=blosclz ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 45319996 Sum time for uncompressed data: 0.032 s, 11909.0 MB/s Compression ratio: 381.5 MB -> 103.3 MB (3.7x) Compression time: 0.731 s, 522.0 MB/s Sum for *compressed* data: 46323164 Sum time for *compressed* data: 0.0637 s, 5988.0 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=2 SUM_CLEVEL=1 SUM_COMPRESSOR=blosclz ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 45826496 Sum time for uncompressed data: 0.0176 s, 21623.0 MB/s Compression ratio: 381.5 MB -> 103.3 MB (3.7x) Compression time: 0.73 s, 522.5 MB/s Sum for *compressed* data: 46321328 Sum time for *compressed* data: 0.0347 s, 10988.3 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=2 SUM_CLEVEL=1 SUM_COMPRESSOR=blosclz ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 45826496 Sum time for uncompressed data: 0.0176 s, 21645.4 MB/s Compression ratio: 381.5 MB -> 103.3 MB (3.7x) Compression time: 0.729 s, 523.0 MB/s Sum for *compressed* data: 46321328 Sum time for *compressed* data: 0.0345 s, 11062.0 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=4 SUM_CLEVEL=1 SUM_COMPRESSOR=blosclz ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46118560 Sum time for uncompressed data: 0.00954 s, 40004.2 MB/s Compression ratio: 381.5 MB -> 103.3 MB (3.7x) Compression time: 0.728 s, 524.1 MB/s Sum for *compressed* data: 46320460 Sum time for *compressed* data: 0.0181 s, 21039.5 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=4 SUM_CLEVEL=1 SUM_COMPRESSOR=blosclz ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46118560 Sum time for uncompressed data: 0.00963 s, 39614.4 MB/s Compression ratio: 381.5 MB -> 103.3 MB (3.7x) Compression time: 0.731 s, 521.6 MB/s Sum for *compressed* data: 46320460 Sum time for *compressed* data: 0.0179 s, 21284.2 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=8 SUM_CLEVEL=1 SUM_COMPRESSOR=blosclz ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46248984 Sum time for uncompressed data: 0.00692 s, 55139.5 MB/s Compression ratio: 381.5 MB -> 103.3 MB (3.7x) Compression time: 0.731 s, 521.9 MB/s Sum for *compressed* data: 46320768 Sum time for *compressed* data: 0.00924 s, 41271.3 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=8 SUM_CLEVEL=1 SUM_COMPRESSOR=blosclz ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46248984 Sum time for uncompressed data: 0.00676 s, 56435.1 MB/s Compression ratio: 381.5 MB -> 103.3 MB (3.7x) Compression time: 0.728 s, 524.0 MB/s Sum for *compressed* data: 46320768 Sum time for *compressed* data: 0.0092 s, 41452.4 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=12 SUM_CLEVEL=1 SUM_COMPRESSOR=blosclz ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46285240 Sum time for uncompressed data: 0.00661 s, 57749.5 MB/s Compression ratio: 381.5 MB -> 103.3 MB (3.7x) Compression time: 0.73 s, 522.3 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.00663 s, 57542.6 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=12 SUM_CLEVEL=1 SUM_COMPRESSOR=blosclz ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46285248 Sum time for uncompressed data: 0.00669 s, 57012.0 MB/s Compression ratio: 381.5 MB -> 103.3 MB (3.7x) Compression time: 0.73 s, 522.3 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.00664 s, 57450.8 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=14 SUM_CLEVEL=1 SUM_COMPRESSOR=blosclz ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46290212 Sum time for uncompressed data: 0.00657 s, 58024.4 MB/s Compression ratio: 381.5 MB -> 103.3 MB (3.7x) Compression time: 0.729 s, 523.0 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.00586 s, 65044.5 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=14 SUM_CLEVEL=1 SUM_COMPRESSOR=blosclz ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46290216 Sum time for uncompressed data: 0.00675 s, 56531.6 MB/s Compression ratio: 381.5 MB -> 103.3 MB (3.7x) Compression time: 0.73 s, 522.5 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.00582 s, 65535.6 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=16 SUM_CLEVEL=1 SUM_COMPRESSOR=blosclz ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46295176 Sum time for uncompressed data: 0.0078 s, 48889.8 MB/s Compression ratio: 381.5 MB -> 103.3 MB (3.7x) Compression time: 0.73 s, 522.8 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.00705 s, 54084.9 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=16 SUM_CLEVEL=1 SUM_COMPRESSOR=blosclz ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46295176 Sum time for uncompressed data: 0.00768 s, 49663.5 MB/s Compression ratio: 381.5 MB -> 103.3 MB (3.7x) Compression time: 0.729 s, 523.1 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.00716 s, 53282.2 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=20 SUM_CLEVEL=1 SUM_COMPRESSOR=blosclz ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46305112 Sum time for uncompressed data: 0.00683 s, 55813.8 MB/s Compression ratio: 381.5 MB -> 103.3 MB (3.7x) Compression time: 0.73 s, 522.7 MB/s Sum for *compressed* data: 46320364 Sum time for *compressed* data: 0.00584 s, 65367.7 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=20 SUM_CLEVEL=1 SUM_COMPRESSOR=blosclz ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46305112 Sum time for uncompressed data: 0.00688 s, 55473.4 MB/s Compression ratio: 381.5 MB -> 103.3 MB (3.7x) Compression time: 0.73 s, 522.2 MB/s Sum for *compressed* data: 46320364 Sum time for *compressed* data: 0.00589 s, 64789.0 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=24 SUM_CLEVEL=1 SUM_COMPRESSOR=blosclz ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46310868 Sum time for uncompressed data: 0.00689 s, 55357.2 MB/s Compression ratio: 381.5 MB -> 103.3 MB (3.7x) Compression time: 0.729 s, 523.0 MB/s Sum for *compressed* data: 46320384 Sum time for *compressed* data: 0.00511 s, 74655.7 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=24 SUM_CLEVEL=1 SUM_COMPRESSOR=blosclz ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46310868 Sum time for uncompressed data: 0.00695 s, 54890.5 MB/s Compression ratio: 381.5 MB -> 103.3 MB (3.7x) Compression time: 0.728 s, 523.7 MB/s Sum for *compressed* data: 46320384 Sum time for *compressed* data: 0.00514 s, 74246.3 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=28 SUM_CLEVEL=1 SUM_COMPRESSOR=blosclz ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46311968 Sum time for uncompressed data: 0.00678 s, 56296.0 MB/s Compression ratio: 381.5 MB -> 103.3 MB (3.7x) Compression time: 0.733 s, 520.5 MB/s Sum for *compressed* data: 46320396 Sum time for *compressed* data: 0.00448 s, 85203.4 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=28 SUM_CLEVEL=1 SUM_COMPRESSOR=blosclz ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46311968 Sum time for uncompressed data: 0.00679 s, 56221.8 MB/s Compression ratio: 381.5 MB -> 103.3 MB (3.7x) Compression time: 0.732 s, 521.1 MB/s Sum for *compressed* data: 46320396 Sum time for *compressed* data: 0.0044 s, 86628.0 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=32 SUM_CLEVEL=1 SUM_COMPRESSOR=blosclz ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46313056 Sum time for uncompressed data: 0.00815 s, 46808.0 MB/s Compression ratio: 381.5 MB -> 103.3 MB (3.7x) Compression time: 0.732 s, 521.1 MB/s Sum for *compressed* data: 46320392 Sum time for *compressed* data: 0.00942 s, 40495.2 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=32 SUM_CLEVEL=1 SUM_COMPRESSOR=blosclz ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46313064 Sum time for uncompressed data: 0.00814 s, 46849.2 MB/s Compression ratio: 381.5 MB -> 103.3 MB (3.7x) Compression time: 0.731 s, 521.9 MB/s Sum for *compressed* data: 46320392 Sum time for *compressed* data: 0.00708 s, 53853.5 MB/s c-blosc2-3.1.5/bench/results-corex/blosclz-suite-corex.out000066400000000000000000000721531521743436100235060ustar00rootroot00000000000000Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) List of supported compressors in this build: blosclz,lz4,lz4hc,zlib,zstd Supported compression libraries: BloscLZ: 2.3.0 LZ4: 1.9.3 Zlib: 1.2.11.zlib-ng Zstd: 1.4.9 Using compressor: blosclz Using shuffle type: shuffle Running suite: suite --> 1, 4194304, 4, 19, blosclz, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 1 ********************** Running benchmarks ********************* memcpy(write): 864.2 us, 4628.8 MB/s memcpy(read): 482.6 us, 8289.1 MB/s Compression level: 0 comp(write): 314.9 us, 12704.0 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 470.0 us, 8510.9 MB/s OK Compression level: 1 comp(write): 848.4 us, 4714.6 MB/s Final bytes: 1102592 Ratio: 3.80 decomp(read): 375.0 us, 10667.6 MB/s OK Compression level: 2 comp(write): 808.3 us, 4948.9 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 393.9 us, 10153.7 MB/s OK Compression level: 3 comp(write): 835.3 us, 4788.5 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 392.9 us, 10180.4 MB/s OK Compression level: 4 comp(write): 1251.7 us, 3195.8 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 430.9 us, 9282.2 MB/s OK Compression level: 5 comp(write): 1279.2 us, 3127.0 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 431.5 us, 9270.3 MB/s OK Compression level: 6 comp(write): 1031.9 us, 3876.2 MB/s Final bytes: 141658 Ratio: 29.61 decomp(read): 456.7 us, 8759.2 MB/s OK Compression level: 7 comp(write): 999.2 us, 4003.0 MB/s Final bytes: 138058 Ratio: 30.38 decomp(read): 462.4 us, 8651.0 MB/s OK Compression level: 8 comp(write): 993.2 us, 4027.4 MB/s Final bytes: 137626 Ratio: 30.48 decomp(read): 464.6 us, 8608.8 MB/s OK Compression level: 9 comp(write): 997.4 us, 4010.4 MB/s Final bytes: 73270 Ratio: 57.24 decomp(read): 526.2 us, 7601.8 MB/s OK --> 2, 4194304, 4, 19, blosclz, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 2 ********************** Running benchmarks ********************* memcpy(write): 614.6 us, 6508.1 MB/s memcpy(read): 471.8 us, 8478.1 MB/s Compression level: 0 comp(write): 169.1 us, 23655.5 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 240.4 us, 16639.8 MB/s OK Compression level: 1 comp(write): 480.9 us, 8317.5 MB/s Final bytes: 1102592 Ratio: 3.80 decomp(read): 208.9 us, 19150.1 MB/s OK Compression level: 2 comp(write): 446.3 us, 8963.0 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 211.8 us, 18884.9 MB/s OK Compression level: 3 comp(write): 452.1 us, 8848.1 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 211.6 us, 18905.9 MB/s OK Compression level: 4 comp(write): 648.8 us, 6165.7 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 232.4 us, 17211.8 MB/s OK Compression level: 5 comp(write): 661.0 us, 6051.4 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 230.7 us, 17339.2 MB/s OK Compression level: 6 comp(write): 537.1 us, 7447.8 MB/s Final bytes: 141658 Ratio: 29.61 decomp(read): 244.7 us, 16347.8 MB/s OK Compression level: 7 comp(write): 521.6 us, 7668.0 MB/s Final bytes: 138058 Ratio: 30.38 decomp(read): 247.8 us, 16141.0 MB/s OK Compression level: 8 comp(write): 517.7 us, 7727.2 MB/s Final bytes: 137626 Ratio: 30.48 decomp(read): 248.4 us, 16101.4 MB/s OK Compression level: 9 comp(write): 523.8 us, 7635.9 MB/s Final bytes: 73270 Ratio: 57.24 decomp(read): 273.0 us, 14654.1 MB/s OK --> 3, 4194304, 4, 19, blosclz, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 3 ********************** Running benchmarks ********************* memcpy(write): 604.8 us, 6613.7 MB/s memcpy(read): 437.5 us, 9142.3 MB/s Compression level: 0 comp(write): 137.2 us, 29160.2 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 165.6 us, 24155.8 MB/s OK Compression level: 1 comp(write): 329.0 us, 12157.6 MB/s Final bytes: 1102592 Ratio: 3.80 decomp(read): 143.6 us, 27861.0 MB/s OK Compression level: 2 comp(write): 309.6 us, 12918.6 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 157.0 us, 25470.7 MB/s OK Compression level: 3 comp(write): 325.4 us, 12291.3 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 159.1 us, 25146.7 MB/s OK Compression level: 4 comp(write): 512.1 us, 7810.9 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 193.6 us, 20656.9 MB/s OK Compression level: 5 comp(write): 502.4 us, 7961.5 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 191.9 us, 20848.0 MB/s OK Compression level: 6 comp(write): 413.5 us, 9674.0 MB/s Final bytes: 141658 Ratio: 29.61 decomp(read): 202.7 us, 19734.3 MB/s OK Compression level: 7 comp(write): 416.0 us, 9615.9 MB/s Final bytes: 138058 Ratio: 30.38 decomp(read): 207.7 us, 19255.3 MB/s OK Compression level: 8 comp(write): 416.5 us, 9604.2 MB/s Final bytes: 137626 Ratio: 30.48 decomp(read): 206.4 us, 19381.7 MB/s OK Compression level: 9 comp(write): 501.9 us, 7970.4 MB/s Final bytes: 73270 Ratio: 57.24 decomp(read): 277.7 us, 14403.0 MB/s OK --> 4, 4194304, 4, 19, blosclz, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 4 ********************** Running benchmarks ********************* memcpy(write): 604.7 us, 6614.6 MB/s memcpy(read): 435.9 us, 9175.9 MB/s Compression level: 0 comp(write): 122.9 us, 32542.3 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 133.5 us, 29959.6 MB/s OK Compression level: 1 comp(write): 264.3 us, 15131.5 MB/s Final bytes: 1102592 Ratio: 3.80 decomp(read): 118.9 us, 33643.4 MB/s OK Compression level: 2 comp(write): 249.7 us, 16020.5 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 123.7 us, 32335.0 MB/s OK Compression level: 3 comp(write): 256.2 us, 15613.0 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 124.4 us, 32149.5 MB/s OK Compression level: 4 comp(write): 366.4 us, 10916.5 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 135.8 us, 29446.4 MB/s OK Compression level: 5 comp(write): 367.2 us, 10891.9 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 135.7 us, 29479.1 MB/s OK Compression level: 6 comp(write): 303.3 us, 13187.0 MB/s Final bytes: 141658 Ratio: 29.61 decomp(read): 144.7 us, 27643.8 MB/s OK Compression level: 7 comp(write): 294.2 us, 13595.6 MB/s Final bytes: 138058 Ratio: 30.38 decomp(read): 145.4 us, 27519.2 MB/s OK Compression level: 8 comp(write): 293.9 us, 13611.9 MB/s Final bytes: 137626 Ratio: 30.48 decomp(read): 146.3 us, 27332.3 MB/s OK Compression level: 9 comp(write): 286.4 us, 13968.9 MB/s Final bytes: 73270 Ratio: 57.24 decomp(read): 156.2 us, 25609.2 MB/s OK --> 5, 4194304, 4, 19, blosclz, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 5 ********************** Running benchmarks ********************* memcpy(write): 618.5 us, 6467.6 MB/s memcpy(read): 449.1 us, 8907.6 MB/s Compression level: 0 comp(write): 121.1 us, 33034.8 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 115.2 us, 34720.6 MB/s OK Compression level: 1 comp(write): 218.1 us, 18340.8 MB/s Final bytes: 1102592 Ratio: 3.80 decomp(read): 100.2 us, 39921.7 MB/s OK Compression level: 2 comp(write): 211.7 us, 18891.2 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 103.6 us, 38624.4 MB/s OK Compression level: 3 comp(write): 212.4 us, 18829.6 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 104.4 us, 38311.7 MB/s OK Compression level: 4 comp(write): 364.6 us, 10969.5 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 138.2 us, 28944.0 MB/s OK Compression level: 5 comp(write): 365.8 us, 10936.1 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 137.4 us, 29112.1 MB/s OK Compression level: 6 comp(write): 302.1 us, 13238.5 MB/s Final bytes: 141658 Ratio: 29.61 decomp(read): 146.5 us, 27306.3 MB/s OK Compression level: 7 comp(write): 293.2 us, 13643.8 MB/s Final bytes: 138058 Ratio: 30.38 decomp(read): 147.9 us, 27054.3 MB/s OK Compression level: 8 comp(write): 291.6 us, 13716.5 MB/s Final bytes: 137626 Ratio: 30.48 decomp(read): 148.3 us, 26974.3 MB/s OK Compression level: 9 comp(write): 282.8 us, 14146.5 MB/s Final bytes: 73270 Ratio: 57.24 decomp(read): 158.7 us, 25202.7 MB/s OK --> 6, 4194304, 4, 19, blosclz, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 6 ********************** Running benchmarks ********************* memcpy(write): 605.1 us, 6610.7 MB/s memcpy(read): 439.0 us, 9112.1 MB/s Compression level: 0 comp(write): 123.6 us, 32352.6 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 102.9 us, 38863.8 MB/s OK Compression level: 1 comp(write): 189.6 us, 21095.9 MB/s Final bytes: 1102592 Ratio: 3.80 decomp(read): 86.4 us, 46313.4 MB/s OK Compression level: 2 comp(write): 179.8 us, 22242.3 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 89.5 us, 44689.7 MB/s OK Compression level: 3 comp(write): 182.8 us, 21878.1 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 89.5 us, 44701.8 MB/s OK Compression level: 4 comp(write): 366.6 us, 10910.9 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 139.9 us, 28599.8 MB/s OK Compression level: 5 comp(write): 364.4 us, 10978.4 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 141.1 us, 28348.8 MB/s OK Compression level: 6 comp(write): 304.0 us, 13156.3 MB/s Final bytes: 141658 Ratio: 29.61 decomp(read): 148.8 us, 26874.6 MB/s OK Compression level: 7 comp(write): 295.2 us, 13548.2 MB/s Final bytes: 138058 Ratio: 30.38 decomp(read): 149.2 us, 26814.4 MB/s OK Compression level: 8 comp(write): 293.2 us, 13640.9 MB/s Final bytes: 137626 Ratio: 30.48 decomp(read): 148.3 us, 26980.6 MB/s OK Compression level: 9 comp(write): 282.2 us, 14173.4 MB/s Final bytes: 73270 Ratio: 57.24 decomp(read): 161.0 us, 24839.3 MB/s OK --> 7, 4194304, 4, 19, blosclz, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 7 ********************** Running benchmarks ********************* memcpy(write): 626.5 us, 6384.3 MB/s memcpy(read): 467.4 us, 8557.9 MB/s Compression level: 0 comp(write): 117.6 us, 34011.1 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 96.1 us, 41631.2 MB/s OK Compression level: 1 comp(write): 166.3 us, 24049.4 MB/s Final bytes: 1102592 Ratio: 3.80 decomp(read): 77.0 us, 51930.5 MB/s OK Compression level: 2 comp(write): 158.7 us, 25199.1 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 84.8 us, 47144.3 MB/s OK Compression level: 3 comp(write): 162.1 us, 24675.2 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 85.0 us, 47052.6 MB/s OK Compression level: 4 comp(write): 365.4 us, 10946.1 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 142.3 us, 28108.5 MB/s OK Compression level: 5 comp(write): 364.5 us, 10975.3 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 142.7 us, 28029.3 MB/s OK Compression level: 6 comp(write): 305.4 us, 13096.0 MB/s Final bytes: 141658 Ratio: 29.61 decomp(read): 151.2 us, 26461.6 MB/s OK Compression level: 7 comp(write): 293.2 us, 13642.2 MB/s Final bytes: 138058 Ratio: 30.38 decomp(read): 150.3 us, 26604.6 MB/s OK Compression level: 8 comp(write): 291.8 us, 13707.1 MB/s Final bytes: 137626 Ratio: 30.48 decomp(read): 152.5 us, 26227.3 MB/s OK Compression level: 9 comp(write): 287.2 us, 13928.3 MB/s Final bytes: 73270 Ratio: 57.24 decomp(read): 162.5 us, 24609.8 MB/s OK --> 8, 4194304, 4, 19, blosclz, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 8 ********************** Running benchmarks ********************* memcpy(write): 595.5 us, 6716.6 MB/s memcpy(read): 438.6 us, 9119.7 MB/s Compression level: 0 comp(write): 121.8 us, 32828.1 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 95.0 us, 42121.4 MB/s OK Compression level: 1 comp(write): 148.6 us, 26913.6 MB/s Final bytes: 1102592 Ratio: 3.80 decomp(read): 70.9 us, 56396.2 MB/s OK Compression level: 2 comp(write): 140.9 us, 28382.8 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 75.1 us, 53291.3 MB/s OK Compression level: 3 comp(write): 146.7 us, 27271.8 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 74.0 us, 54037.5 MB/s OK Compression level: 4 comp(write): 202.4 us, 19763.2 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 81.6 us, 49013.8 MB/s OK Compression level: 5 comp(write): 196.9 us, 20311.2 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 81.4 us, 49155.5 MB/s OK Compression level: 6 comp(write): 169.4 us, 23608.3 MB/s Final bytes: 141658 Ratio: 29.61 decomp(read): 85.1 us, 47006.9 MB/s OK Compression level: 7 comp(write): 162.1 us, 24677.1 MB/s Final bytes: 138058 Ratio: 30.38 decomp(read): 88.5 us, 45215.7 MB/s OK Compression level: 8 comp(write): 162.5 us, 24621.3 MB/s Final bytes: 137626 Ratio: 30.48 decomp(read): 87.6 us, 45687.2 MB/s OK Compression level: 9 comp(write): 290.9 us, 13751.6 MB/s Final bytes: 73270 Ratio: 57.24 decomp(read): 164.8 us, 24268.1 MB/s OK --> 9, 4194304, 4, 19, blosclz, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 9 ********************** Running benchmarks ********************* memcpy(write): 621.9 us, 6431.7 MB/s memcpy(read): 448.0 us, 8928.4 MB/s Compression level: 0 comp(write): 119.8 us, 33402.2 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 97.4 us, 41068.8 MB/s OK Compression level: 1 comp(write): 138.3 us, 28915.2 MB/s Final bytes: 1102592 Ratio: 3.80 decomp(read): 70.4 us, 56823.8 MB/s OK Compression level: 2 comp(write): 132.8 us, 30126.2 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 78.1 us, 51191.9 MB/s OK Compression level: 3 comp(write): 135.9 us, 29427.5 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 75.6 us, 52901.2 MB/s OK Compression level: 4 comp(write): 202.4 us, 19761.5 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 84.2 us, 47505.0 MB/s OK Compression level: 5 comp(write): 200.7 us, 19931.0 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 82.9 us, 48228.4 MB/s OK Compression level: 6 comp(write): 168.3 us, 23767.0 MB/s Final bytes: 141658 Ratio: 29.61 decomp(read): 86.9 us, 46031.6 MB/s OK Compression level: 7 comp(write): 164.0 us, 24382.9 MB/s Final bytes: 138058 Ratio: 30.38 decomp(read): 90.2 us, 44330.1 MB/s OK Compression level: 8 comp(write): 164.6 us, 24300.9 MB/s Final bytes: 137626 Ratio: 30.48 decomp(read): 88.2 us, 45342.9 MB/s OK Compression level: 9 comp(write): 294.8 us, 13569.2 MB/s Final bytes: 73270 Ratio: 57.24 decomp(read): 166.4 us, 24032.9 MB/s OK --> 10, 4194304, 4, 19, blosclz, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 10 ********************** Running benchmarks ********************* memcpy(write): 605.1 us, 6610.7 MB/s memcpy(read): 440.6 us, 9078.4 MB/s Compression level: 0 comp(write): 123.1 us, 32494.3 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 99.3 us, 40289.4 MB/s OK Compression level: 1 comp(write): 131.2 us, 30494.9 MB/s Final bytes: 1102592 Ratio: 3.80 decomp(read): 66.8 us, 59850.3 MB/s OK Compression level: 2 comp(write): 125.6 us, 31859.7 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 70.2 us, 56943.8 MB/s OK Compression level: 3 comp(write): 126.8 us, 31545.8 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 70.5 us, 56742.3 MB/s OK Compression level: 4 comp(write): 206.1 us, 19405.9 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 91.2 us, 43883.1 MB/s OK Compression level: 5 comp(write): 201.3 us, 19869.3 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 86.8 us, 46105.1 MB/s OK Compression level: 6 comp(write): 170.5 us, 23462.4 MB/s Final bytes: 141658 Ratio: 29.61 decomp(read): 92.8 us, 43100.2 MB/s OK Compression level: 7 comp(write): 166.6 us, 24013.0 MB/s Final bytes: 138058 Ratio: 30.38 decomp(read): 89.4 us, 44744.3 MB/s OK Compression level: 8 comp(write): 164.2 us, 24367.9 MB/s Final bytes: 137626 Ratio: 30.48 decomp(read): 90.8 us, 44063.7 MB/s OK Compression level: 9 comp(write): 311.3 us, 12850.5 MB/s Final bytes: 73270 Ratio: 57.24 decomp(read): 165.7 us, 24141.9 MB/s OK --> 11, 4194304, 4, 19, blosclz, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 11 ********************** Running benchmarks ********************* memcpy(write): 625.2 us, 6398.0 MB/s memcpy(read): 446.5 us, 8958.8 MB/s Compression level: 0 comp(write): 121.0 us, 33051.6 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 103.2 us, 38759.9 MB/s OK Compression level: 1 comp(write): 128.8 us, 31064.4 MB/s Final bytes: 1102592 Ratio: 3.80 decomp(read): 66.5 us, 60116.1 MB/s OK Compression level: 2 comp(write): 119.5 us, 33459.0 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 67.5 us, 59269.1 MB/s OK Compression level: 3 comp(write): 121.7 us, 32859.0 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 67.4 us, 59380.2 MB/s OK Compression level: 4 comp(write): 206.1 us, 19403.5 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 88.9 us, 44990.4 MB/s OK Compression level: 5 comp(write): 202.9 us, 19709.8 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 87.3 us, 45844.0 MB/s OK Compression level: 6 comp(write): 171.0 us, 23388.4 MB/s Final bytes: 141658 Ratio: 29.61 decomp(read): 90.8 us, 44074.1 MB/s OK Compression level: 7 comp(write): 166.0 us, 24093.2 MB/s Final bytes: 138058 Ratio: 30.38 decomp(read): 93.7 us, 42693.0 MB/s OK Compression level: 8 comp(write): 167.0 us, 23950.3 MB/s Final bytes: 137626 Ratio: 30.48 decomp(read): 91.9 us, 43547.5 MB/s OK Compression level: 9 comp(write): 318.9 us, 12541.2 MB/s Final bytes: 73270 Ratio: 57.24 decomp(read): 172.1 us, 23243.3 MB/s OK --> 12, 4194304, 4, 19, blosclz, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 12 ********************** Running benchmarks ********************* memcpy(write): 608.8 us, 6570.8 MB/s memcpy(read): 454.6 us, 8798.6 MB/s Compression level: 0 comp(write): 139.6 us, 28660.4 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 101.3 us, 39474.1 MB/s OK Compression level: 1 comp(write): 120.2 us, 33285.9 MB/s Final bytes: 1102592 Ratio: 3.80 decomp(read): 67.1 us, 59624.8 MB/s OK Compression level: 2 comp(write): 115.9 us, 34510.4 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 70.0 us, 57144.2 MB/s OK Compression level: 3 comp(write): 119.4 us, 33508.2 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 71.4 us, 56023.5 MB/s OK Compression level: 4 comp(write): 216.3 us, 18490.8 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 95.1 us, 42076.8 MB/s OK Compression level: 5 comp(write): 213.0 us, 18783.7 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 93.0 us, 42989.9 MB/s OK Compression level: 6 comp(write): 180.4 us, 22178.5 MB/s Final bytes: 141658 Ratio: 29.61 decomp(read): 95.9 us, 41720.2 MB/s OK Compression level: 7 comp(write): 175.5 us, 22795.0 MB/s Final bytes: 138058 Ratio: 30.38 decomp(read): 100.3 us, 39861.9 MB/s OK Compression level: 8 comp(write): 176.4 us, 22673.7 MB/s Final bytes: 137626 Ratio: 30.48 decomp(read): 97.8 us, 40916.8 MB/s OK Compression level: 9 comp(write): 336.9 us, 11873.7 MB/s Final bytes: 73270 Ratio: 57.24 decomp(read): 176.3 us, 22691.4 MB/s OK --> 13, 4194304, 4, 19, blosclz, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 13 ********************** Running benchmarks ********************* memcpy(write): 623.0 us, 6420.3 MB/s memcpy(read): 470.6 us, 8499.6 MB/s Compression level: 0 comp(write): 140.9 us, 28398.8 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 102.0 us, 39220.4 MB/s OK Compression level: 1 comp(write): 115.8 us, 34550.1 MB/s Final bytes: 1102592 Ratio: 3.80 decomp(read): 64.5 us, 61988.2 MB/s OK Compression level: 2 comp(write): 110.7 us, 36147.2 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 65.6 us, 60986.6 MB/s OK Compression level: 3 comp(write): 111.7 us, 35808.4 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 66.7 us, 59973.3 MB/s OK Compression level: 4 comp(write): 217.6 us, 18384.2 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 95.7 us, 41805.5 MB/s OK Compression level: 5 comp(write): 216.6 us, 18470.4 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 95.0 us, 42116.9 MB/s OK Compression level: 6 comp(write): 190.5 us, 20999.1 MB/s Final bytes: 141658 Ratio: 29.61 decomp(read): 98.9 us, 40449.0 MB/s OK Compression level: 7 comp(write): 177.4 us, 22551.5 MB/s Final bytes: 138058 Ratio: 30.38 decomp(read): 101.7 us, 39345.5 MB/s OK Compression level: 8 comp(write): 180.8 us, 22120.3 MB/s Final bytes: 137626 Ratio: 30.48 decomp(read): 100.3 us, 39865.5 MB/s OK Compression level: 9 comp(write): 344.3 us, 11619.4 MB/s Final bytes: 73270 Ratio: 57.24 decomp(read): 177.3 us, 22558.0 MB/s OK --> 14, 4194304, 4, 19, blosclz, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 14 ********************** Running benchmarks ********************* memcpy(write): 615.4 us, 6500.2 MB/s memcpy(read): 456.9 us, 8754.2 MB/s Compression level: 0 comp(write): 136.9 us, 29227.6 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 105.2 us, 38010.4 MB/s OK Compression level: 1 comp(write): 118.3 us, 33813.9 MB/s Final bytes: 1102592 Ratio: 3.80 decomp(read): 88.6 us, 45134.6 MB/s OK Compression level: 2 comp(write): 113.6 us, 35217.8 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 82.3 us, 48587.4 MB/s OK Compression level: 3 comp(write): 118.8 us, 33668.4 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 84.2 us, 47507.0 MB/s OK Compression level: 4 comp(write): 222.3 us, 17993.9 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 98.2 us, 40739.2 MB/s OK Compression level: 5 comp(write): 224.4 us, 17826.9 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 158.6 us, 25215.6 MB/s OK Compression level: 6 comp(write): 193.1 us, 20710.5 MB/s Final bytes: 141658 Ratio: 29.61 decomp(read): 102.4 us, 39049.4 MB/s OK Compression level: 7 comp(write): 183.5 us, 21799.4 MB/s Final bytes: 138058 Ratio: 30.38 decomp(read): 102.2 us, 39151.0 MB/s OK Compression level: 8 comp(write): 184.8 us, 21639.5 MB/s Final bytes: 137626 Ratio: 30.48 decomp(read): 147.1 us, 27189.2 MB/s OK Compression level: 9 comp(write): 351.1 us, 11392.3 MB/s Final bytes: 73270 Ratio: 57.24 decomp(read): 180.3 us, 22180.2 MB/s OK --> 15, 4194304, 4, 19, blosclz, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 15 ********************** Running benchmarks ********************* memcpy(write): 624.5 us, 6405.6 MB/s memcpy(read): 452.9 us, 8831.1 MB/s Compression level: 0 comp(write): 148.5 us, 26940.2 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 118.3 us, 33820.0 MB/s OK Compression level: 1 comp(write): 117.8 us, 33966.0 MB/s Final bytes: 1102592 Ratio: 3.80 decomp(read): 77.6 us, 51513.4 MB/s OK Compression level: 2 comp(write): 118.9 us, 33645.2 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 75.3 us, 53143.6 MB/s OK Compression level: 3 comp(write): 116.6 us, 34293.6 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 71.0 us, 56333.9 MB/s OK Compression level: 4 comp(write): 239.0 us, 16736.1 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 114.3 us, 34983.7 MB/s OK Compression level: 5 comp(write): 247.7 us, 16150.8 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 100.5 us, 39796.4 MB/s OK Compression level: 6 comp(write): 200.0 us, 19999.5 MB/s Final bytes: 141658 Ratio: 29.61 decomp(read): 105.0 us, 38078.9 MB/s OK Compression level: 7 comp(write): 198.8 us, 20120.3 MB/s Final bytes: 138058 Ratio: 30.38 decomp(read): 118.8 us, 33682.4 MB/s OK Compression level: 8 comp(write): 195.3 us, 20483.8 MB/s Final bytes: 137626 Ratio: 30.48 decomp(read): 112.3 us, 35617.2 MB/s OK Compression level: 9 comp(write): 356.6 us, 11217.3 MB/s Final bytes: 73270 Ratio: 57.24 decomp(read): 186.3 us, 21469.5 MB/s OK --> 16, 4194304, 4, 19, blosclz, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 16 ********************** Running benchmarks ********************* memcpy(write): 606.4 us, 6596.0 MB/s memcpy(read): 438.9 us, 9112.8 MB/s Compression level: 0 comp(write): 145.0 us, 27589.8 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 116.1 us, 34462.3 MB/s OK Compression level: 1 comp(write): 123.9 us, 32272.2 MB/s Final bytes: 1102592 Ratio: 3.80 decomp(read): 83.6 us, 47828.2 MB/s OK Compression level: 2 comp(write): 113.5 us, 35234.6 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 84.3 us, 47424.5 MB/s OK Compression level: 3 comp(write): 119.6 us, 33458.6 MB/s Final bytes: 1083544 Ratio: 3.87 decomp(read): 84.9 us, 47125.7 MB/s OK Compression level: 4 comp(write): 252.0 us, 15875.8 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 104.6 us, 38251.6 MB/s OK Compression level: 5 comp(write): 256.2 us, 15613.7 MB/s Final bytes: 165210 Ratio: 25.39 decomp(read): 102.6 us, 38987.8 MB/s OK Compression level: 6 comp(write): 217.1 us, 18424.1 MB/s Final bytes: 141658 Ratio: 29.61 decomp(read): 107.2 us, 37310.1 MB/s OK Compression level: 7 comp(write): 212.8 us, 18794.6 MB/s Final bytes: 138058 Ratio: 30.38 decomp(read): 108.5 us, 36864.3 MB/s OK Compression level: 8 comp(write): 207.2 us, 19300.6 MB/s Final bytes: 137626 Ratio: 30.48 decomp(read): 170.7 us, 23428.3 MB/s OK Compression level: 9 comp(write): 354.8 us, 11272.4 MB/s Final bytes: 73270 Ratio: 57.24 decomp(read): 188.1 us, 21269.9 MB/s OK Round-trip compr/decompr on 120.0 GB Elapsed time: 16.6 s, 16318.4 MB/s c-blosc2-3.1.5/bench/results-corex/lz4-cl1-sum_openmp-corex.out000066400000000000000000000260231521743436100242500ustar00rootroot00000000000000faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=1 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4 ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 45319996 Sum time for uncompressed data: 0.0323 s, 11813.2 MB/s Compression ratio: 381.5 MB -> 86.0 MB (4.4x) Compression time: 0.294 s, 1296.8 MB/s Sum for *compressed* data: 46323164 Sum time for *compressed* data: 0.0734 s, 5194.0 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=1 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4 ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 45319996 Sum time for uncompressed data: 0.0323 s, 11792.8 MB/s Compression ratio: 381.5 MB -> 86.0 MB (4.4x) Compression time: 0.294 s, 1297.7 MB/s Sum for *compressed* data: 46323164 Sum time for *compressed* data: 0.0735 s, 5188.8 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=2 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4 ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 45826496 Sum time for uncompressed data: 0.0176 s, 21640.8 MB/s Compression ratio: 381.5 MB -> 86.0 MB (4.4x) Compression time: 0.294 s, 1296.4 MB/s Sum for *compressed* data: 46321328 Sum time for *compressed* data: 0.0404 s, 9430.7 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=2 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4 ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 45826496 Sum time for uncompressed data: 0.0176 s, 21625.8 MB/s Compression ratio: 381.5 MB -> 86.0 MB (4.4x) Compression time: 0.295 s, 1291.5 MB/s Sum for *compressed* data: 46321328 Sum time for *compressed* data: 0.0403 s, 9465.5 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=4 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4 ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46118560 Sum time for uncompressed data: 0.00947 s, 40275.9 MB/s Compression ratio: 381.5 MB -> 86.0 MB (4.4x) Compression time: 0.295 s, 1294.1 MB/s Sum for *compressed* data: 46320460 Sum time for *compressed* data: 0.0209 s, 18257.6 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=4 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4 ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46118560 Sum time for uncompressed data: 0.00952 s, 40051.5 MB/s Compression ratio: 381.5 MB -> 86.0 MB (4.4x) Compression time: 0.295 s, 1294.5 MB/s Sum for *compressed* data: 46320460 Sum time for *compressed* data: 0.0209 s, 18262.0 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=8 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4 ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46248984 Sum time for uncompressed data: 0.00667 s, 57210.6 MB/s Compression ratio: 381.5 MB -> 86.0 MB (4.4x) Compression time: 0.295 s, 1291.2 MB/s Sum for *compressed* data: 46320768 Sum time for *compressed* data: 0.0107 s, 35693.8 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=8 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4 ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46248988 Sum time for uncompressed data: 0.0067 s, 56951.6 MB/s Compression ratio: 381.5 MB -> 86.0 MB (4.4x) Compression time: 0.294 s, 1296.3 MB/s Sum for *compressed* data: 46320768 Sum time for *compressed* data: 0.0107 s, 35698.7 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=12 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4 ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46285244 Sum time for uncompressed data: 0.00662 s, 57649.8 MB/s Compression ratio: 381.5 MB -> 86.0 MB (4.4x) Compression time: 0.295 s, 1291.8 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.00765 s, 49834.7 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=12 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4 ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46285248 Sum time for uncompressed data: 0.0067 s, 56924.6 MB/s Compression ratio: 381.5 MB -> 86.0 MB (4.4x) Compression time: 0.294 s, 1297.6 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.00759 s, 50247.9 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=14 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4 ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46290216 Sum time for uncompressed data: 0.00658 s, 57991.3 MB/s Compression ratio: 381.5 MB -> 86.0 MB (4.4x) Compression time: 0.295 s, 1291.2 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.00669 s, 57009.0 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=14 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4 ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46290212 Sum time for uncompressed data: 0.0066 s, 57761.1 MB/s Compression ratio: 381.5 MB -> 86.0 MB (4.4x) Compression time: 0.296 s, 1289.7 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.00673 s, 56677.2 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=16 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4 ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46295180 Sum time for uncompressed data: 0.0078 s, 48882.0 MB/s Compression ratio: 381.5 MB -> 86.0 MB (4.4x) Compression time: 0.295 s, 1292.5 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.00901 s, 42342.2 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=16 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4 ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46295176 Sum time for uncompressed data: 0.00778 s, 49019.5 MB/s Compression ratio: 381.5 MB -> 86.0 MB (4.4x) Compression time: 0.295 s, 1292.9 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.00902 s, 42268.3 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=20 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4 ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46305116 Sum time for uncompressed data: 0.00689 s, 55397.8 MB/s Compression ratio: 381.5 MB -> 86.0 MB (4.4x) Compression time: 0.295 s, 1295.1 MB/s Sum for *compressed* data: 46320364 Sum time for *compressed* data: 0.00731 s, 52207.0 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=20 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4 ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46305112 Sum time for uncompressed data: 0.00685 s, 55666.0 MB/s Compression ratio: 381.5 MB -> 86.0 MB (4.4x) Compression time: 0.296 s, 1287.4 MB/s Sum for *compressed* data: 46320364 Sum time for *compressed* data: 0.00734 s, 51948.1 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=24 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4 ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46310864 Sum time for uncompressed data: 0.0069 s, 55283.1 MB/s Compression ratio: 381.5 MB -> 86.0 MB (4.4x) Compression time: 0.296 s, 1290.3 MB/s Sum for *compressed* data: 46320384 Sum time for *compressed* data: 0.00638 s, 59755.1 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=24 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4 ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46310872 Sum time for uncompressed data: 0.00691 s, 55215.8 MB/s Compression ratio: 381.5 MB -> 86.0 MB (4.4x) Compression time: 0.296 s, 1290.5 MB/s Sum for *compressed* data: 46320384 Sum time for *compressed* data: 0.0065 s, 58668.5 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=28 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4 ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46311964 Sum time for uncompressed data: 0.00675 s, 56497.3 MB/s Compression ratio: 381.5 MB -> 86.0 MB (4.4x) Compression time: 0.297 s, 1285.7 MB/s Sum for *compressed* data: 46320396 Sum time for *compressed* data: 0.00571 s, 66777.3 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=28 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4 ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46311964 Sum time for uncompressed data: 0.00682 s, 55923.4 MB/s Compression ratio: 381.5 MB -> 86.0 MB (4.4x) Compression time: 0.295 s, 1293.1 MB/s Sum for *compressed* data: 46320396 Sum time for *compressed* data: 0.00566 s, 67353.5 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=32 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4 ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46313064 Sum time for uncompressed data: 0.00817 s, 46704.5 MB/s Compression ratio: 381.5 MB -> 86.0 MB (4.4x) Compression time: 0.295 s, 1293.4 MB/s Sum for *compressed* data: 46320392 Sum time for *compressed* data: 0.0116 s, 32778.8 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=32 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4 ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46313056 Sum time for uncompressed data: 0.0082 s, 46525.9 MB/s Compression ratio: 381.5 MB -> 86.0 MB (4.4x) Compression time: 0.296 s, 1289.1 MB/s Sum for *compressed* data: 46320392 Sum time for *compressed* data: 0.0104 s, 36658.2 MB/s c-blosc2-3.1.5/bench/results-corex/lz4-suite-corex.out000066400000000000000000000717631521743436100225550ustar00rootroot00000000000000Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) List of supported compressors in this build: blosclz,lz4,lz4hc,zlib,zstd Supported compression libraries: BloscLZ: 2.3.0 LZ4: 1.9.3 Zlib: 1.2.11.zlib-ng Zstd: 1.4.9 Using compressor: lz4 Using shuffle type: shuffle Running suite: suite --> 1, 4194304, 4, 19, lz4, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 1 ********************** Running benchmarks ********************* memcpy(write): 852.6 us, 4691.4 MB/s memcpy(read): 466.1 us, 8581.6 MB/s Compression level: 0 comp(write): 315.2 us, 12689.8 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 447.2 us, 8943.9 MB/s OK Compression level: 1 comp(write): 1935.4 us, 2066.7 MB/s Final bytes: 882352 Ratio: 4.75 decomp(read): 755.6 us, 5293.7 MB/s OK Compression level: 2 comp(write): 1945.6 us, 2055.9 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 762.4 us, 5246.6 MB/s OK Compression level: 3 comp(write): 1958.3 us, 2042.6 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 760.7 us, 5258.3 MB/s OK Compression level: 4 comp(write): 1213.4 us, 3296.4 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 523.9 us, 7635.0 MB/s OK Compression level: 5 comp(write): 1208.6 us, 3309.6 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 522.6 us, 7654.2 MB/s OK Compression level: 6 comp(write): 1222.1 us, 3272.9 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 522.8 us, 7650.9 MB/s OK Compression level: 7 comp(write): 1245.0 us, 3212.9 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 522.4 us, 7656.3 MB/s OK Compression level: 8 comp(write): 1229.3 us, 3253.9 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 522.7 us, 7652.5 MB/s OK Compression level: 9 comp(write): 1265.7 us, 3160.2 MB/s Final bytes: 316064 Ratio: 13.27 decomp(read): 622.1 us, 6429.5 MB/s OK --> 2, 4194304, 4, 19, lz4, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 2 ********************** Running benchmarks ********************* memcpy(write): 405.3 us, 9868.3 MB/s memcpy(read): 440.6 us, 9078.1 MB/s Compression level: 0 comp(write): 167.2 us, 23923.3 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 237.4 us, 16847.3 MB/s OK Compression level: 1 comp(write): 1036.9 us, 3857.6 MB/s Final bytes: 882352 Ratio: 4.75 decomp(read): 400.9 us, 9977.2 MB/s OK Compression level: 2 comp(write): 1030.8 us, 3880.6 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 406.1 us, 9850.6 MB/s OK Compression level: 3 comp(write): 1028.9 us, 3887.6 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 403.0 us, 9926.7 MB/s OK Compression level: 4 comp(write): 628.2 us, 6367.3 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 277.5 us, 14415.0 MB/s OK Compression level: 5 comp(write): 627.7 us, 6372.6 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 277.9 us, 14392.8 MB/s OK Compression level: 6 comp(write): 627.8 us, 6371.2 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 278.0 us, 14389.1 MB/s OK Compression level: 7 comp(write): 627.5 us, 6374.3 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 277.5 us, 14412.4 MB/s OK Compression level: 8 comp(write): 635.4 us, 6295.0 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 275.8 us, 14505.8 MB/s OK Compression level: 9 comp(write): 667.2 us, 5994.8 MB/s Final bytes: 316064 Ratio: 13.27 decomp(read): 329.5 us, 12140.1 MB/s OK --> 3, 4194304, 4, 19, lz4, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 3 ********************** Running benchmarks ********************* memcpy(write): 405.4 us, 9867.1 MB/s memcpy(read): 429.0 us, 9323.8 MB/s Compression level: 0 comp(write): 137.7 us, 29058.1 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 163.1 us, 24529.9 MB/s OK Compression level: 1 comp(write): 703.9 us, 5682.7 MB/s Final bytes: 882352 Ratio: 4.75 decomp(read): 275.1 us, 14539.2 MB/s OK Compression level: 2 comp(write): 723.5 us, 5529.0 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 298.0 us, 13422.1 MB/s OK Compression level: 3 comp(write): 723.6 us, 5527.7 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 292.5 us, 13676.1 MB/s OK Compression level: 4 comp(write): 501.6 us, 7974.7 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 224.1 us, 17846.1 MB/s OK Compression level: 5 comp(write): 477.0 us, 8385.8 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 225.6 us, 17734.4 MB/s OK Compression level: 6 comp(write): 474.4 us, 8432.0 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 215.3 us, 18580.1 MB/s OK Compression level: 7 comp(write): 487.3 us, 8209.1 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 215.6 us, 18554.7 MB/s OK Compression level: 8 comp(write): 479.0 us, 8350.6 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 214.5 us, 18649.0 MB/s OK Compression level: 9 comp(write): 671.3 us, 5958.7 MB/s Final bytes: 316064 Ratio: 13.27 decomp(read): 339.2 us, 11791.6 MB/s OK --> 4, 4194304, 4, 19, lz4, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 4 ********************** Running benchmarks ********************* memcpy(write): 414.3 us, 9653.9 MB/s memcpy(read): 447.6 us, 8937.2 MB/s Compression level: 0 comp(write): 126.7 us, 31573.2 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 132.2 us, 30260.9 MB/s OK Compression level: 1 comp(write): 574.9 us, 6958.3 MB/s Final bytes: 882352 Ratio: 4.75 decomp(read): 229.0 us, 17468.0 MB/s OK Compression level: 2 comp(write): 572.6 us, 6985.5 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 229.7 us, 17413.5 MB/s OK Compression level: 3 comp(write): 583.3 us, 6857.8 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 229.6 us, 17421.8 MB/s OK Compression level: 4 comp(write): 354.7 us, 11277.2 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 160.6 us, 24911.0 MB/s OK Compression level: 5 comp(write): 358.6 us, 11153.0 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 160.6 us, 24901.9 MB/s OK Compression level: 6 comp(write): 355.7 us, 11245.2 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 160.9 us, 24860.8 MB/s OK Compression level: 7 comp(write): 353.2 us, 11326.0 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 160.9 us, 24858.3 MB/s OK Compression level: 8 comp(write): 356.5 us, 11221.7 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 161.1 us, 24834.3 MB/s OK Compression level: 9 comp(write): 376.7 us, 10617.7 MB/s Final bytes: 316064 Ratio: 13.27 decomp(read): 184.0 us, 21736.9 MB/s OK --> 5, 4194304, 4, 19, lz4, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 5 ********************** Running benchmarks ********************* memcpy(write): 416.2 us, 9610.5 MB/s memcpy(read): 439.4 us, 9102.7 MB/s Compression level: 0 comp(write): 118.4 us, 33778.4 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 111.1 us, 36017.4 MB/s OK Compression level: 1 comp(write): 470.6 us, 8499.5 MB/s Final bytes: 882352 Ratio: 4.75 decomp(read): 186.9 us, 21397.3 MB/s OK Compression level: 2 comp(write): 467.4 us, 8558.0 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 190.1 us, 21036.4 MB/s OK Compression level: 3 comp(write): 468.0 us, 8546.6 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 190.3 us, 21024.5 MB/s OK Compression level: 4 comp(write): 353.3 us, 11322.6 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 164.1 us, 24378.6 MB/s OK Compression level: 5 comp(write): 358.3 us, 11163.5 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 162.4 us, 24626.3 MB/s OK Compression level: 6 comp(write): 352.8 us, 11338.5 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 161.8 us, 24726.0 MB/s OK Compression level: 7 comp(write): 352.1 us, 11361.4 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 162.5 us, 24612.2 MB/s OK Compression level: 8 comp(write): 353.3 us, 11322.9 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 162.8 us, 24576.1 MB/s OK Compression level: 9 comp(write): 373.1 us, 10721.6 MB/s Final bytes: 316064 Ratio: 13.27 decomp(read): 190.8 us, 20965.9 MB/s OK --> 6, 4194304, 4, 19, lz4, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 6 ********************** Running benchmarks ********************* memcpy(write): 405.2 us, 9872.7 MB/s memcpy(read): 426.4 us, 9381.2 MB/s Compression level: 0 comp(write): 123.5 us, 32377.9 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 101.4 us, 39449.8 MB/s OK Compression level: 1 comp(write): 398.3 us, 10043.1 MB/s Final bytes: 882352 Ratio: 4.75 decomp(read): 160.2 us, 24962.8 MB/s OK Compression level: 2 comp(write): 398.8 us, 10030.0 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 162.2 us, 24665.4 MB/s OK Compression level: 3 comp(write): 398.9 us, 10026.7 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 162.8 us, 24571.8 MB/s OK Compression level: 4 comp(write): 355.9 us, 11240.6 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 162.9 us, 24547.6 MB/s OK Compression level: 5 comp(write): 354.1 us, 11297.3 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 162.7 us, 24584.4 MB/s OK Compression level: 6 comp(write): 353.9 us, 11301.7 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 164.0 us, 24397.1 MB/s OK Compression level: 7 comp(write): 353.4 us, 11317.4 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 165.1 us, 24225.9 MB/s OK Compression level: 8 comp(write): 354.4 us, 11287.6 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 165.0 us, 24243.6 MB/s OK Compression level: 9 comp(write): 377.0 us, 10610.2 MB/s Final bytes: 316064 Ratio: 13.27 decomp(read): 191.8 us, 20859.0 MB/s OK --> 7, 4194304, 4, 19, lz4, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 7 ********************** Running benchmarks ********************* memcpy(write): 416.5 us, 9603.7 MB/s memcpy(read): 436.4 us, 9166.5 MB/s Compression level: 0 comp(write): 113.6 us, 35224.8 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 94.2 us, 42474.0 MB/s OK Compression level: 1 comp(write): 346.7 us, 11537.1 MB/s Final bytes: 882352 Ratio: 4.75 decomp(read): 142.9 us, 27988.2 MB/s OK Compression level: 2 comp(write): 360.2 us, 11103.6 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 149.4 us, 26770.6 MB/s OK Compression level: 3 comp(write): 360.3 us, 11102.3 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 154.8 us, 25846.1 MB/s OK Compression level: 4 comp(write): 353.5 us, 11314.8 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 167.1 us, 23932.2 MB/s OK Compression level: 5 comp(write): 354.0 us, 11300.8 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 166.4 us, 24034.9 MB/s OK Compression level: 6 comp(write): 353.5 us, 11314.6 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 167.8 us, 23832.8 MB/s OK Compression level: 7 comp(write): 352.9 us, 11333.8 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 166.9 us, 23970.4 MB/s OK Compression level: 8 comp(write): 353.7 us, 11307.7 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 167.7 us, 23855.4 MB/s OK Compression level: 9 comp(write): 377.6 us, 10592.1 MB/s Final bytes: 316064 Ratio: 13.27 decomp(read): 192.2 us, 20807.1 MB/s OK --> 8, 4194304, 4, 19, lz4, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 8 ********************** Running benchmarks ********************* memcpy(write): 414.9 us, 9641.2 MB/s memcpy(read): 446.3 us, 8962.2 MB/s Compression level: 0 comp(write): 116.4 us, 34375.6 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 94.4 us, 42371.3 MB/s OK Compression level: 1 comp(write): 303.7 us, 13172.7 MB/s Final bytes: 882352 Ratio: 4.75 decomp(read): 127.6 us, 31337.5 MB/s OK Compression level: 2 comp(write): 303.3 us, 13187.4 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 128.1 us, 31223.2 MB/s OK Compression level: 3 comp(write): 301.3 us, 13277.9 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 126.1 us, 31728.6 MB/s OK Compression level: 4 comp(write): 193.1 us, 20712.8 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 96.7 us, 41349.3 MB/s OK Compression level: 5 comp(write): 194.4 us, 20577.1 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 95.3 us, 41961.1 MB/s OK Compression level: 6 comp(write): 192.8 us, 20751.0 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 96.2 us, 41570.4 MB/s OK Compression level: 7 comp(write): 193.5 us, 20667.9 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 95.4 us, 41945.2 MB/s OK Compression level: 8 comp(write): 194.0 us, 20618.9 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 95.4 us, 41912.0 MB/s OK Compression level: 9 comp(write): 383.2 us, 10437.2 MB/s Final bytes: 316064 Ratio: 13.27 decomp(read): 196.8 us, 20322.1 MB/s OK --> 9, 4194304, 4, 19, lz4, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 9 ********************** Running benchmarks ********************* memcpy(write): 428.4 us, 9336.2 MB/s memcpy(read): 461.6 us, 8664.9 MB/s Compression level: 0 comp(write): 115.9 us, 34510.4 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 95.6 us, 41852.2 MB/s OK Compression level: 1 comp(write): 279.9 us, 14291.8 MB/s Final bytes: 882352 Ratio: 4.75 decomp(read): 121.8 us, 32853.9 MB/s OK Compression level: 2 comp(write): 293.7 us, 13618.5 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 130.9 us, 30558.0 MB/s OK Compression level: 3 comp(write): 293.2 us, 13643.8 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 131.1 us, 30513.3 MB/s OK Compression level: 4 comp(write): 194.7 us, 20539.5 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 98.2 us, 40740.5 MB/s OK Compression level: 5 comp(write): 193.5 us, 20675.5 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 96.8 us, 41333.4 MB/s OK Compression level: 6 comp(write): 195.3 us, 20483.6 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 97.2 us, 41172.1 MB/s OK Compression level: 7 comp(write): 192.8 us, 20750.1 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 101.7 us, 39340.4 MB/s OK Compression level: 8 comp(write): 194.8 us, 20537.1 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 97.0 us, 41219.5 MB/s OK Compression level: 9 comp(write): 394.0 us, 10153.0 MB/s Final bytes: 316064 Ratio: 13.27 decomp(read): 198.3 us, 20174.1 MB/s OK --> 10, 4194304, 4, 19, lz4, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 10 ********************** Running benchmarks ********************* memcpy(write): 405.7 us, 9860.6 MB/s memcpy(read): 429.4 us, 9315.7 MB/s Compression level: 0 comp(write): 122.6 us, 32636.8 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 97.7 us, 40948.4 MB/s OK Compression level: 1 comp(write): 252.3 us, 15854.9 MB/s Final bytes: 882352 Ratio: 4.75 decomp(read): 112.4 us, 35595.4 MB/s OK Compression level: 2 comp(write): 264.1 us, 15143.3 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 118.6 us, 33735.1 MB/s OK Compression level: 3 comp(write): 266.5 us, 15006.6 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 120.3 us, 33258.5 MB/s OK Compression level: 4 comp(write): 199.6 us, 20042.0 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 99.0 us, 40391.1 MB/s OK Compression level: 5 comp(write): 196.8 us, 20330.1 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 99.6 us, 40150.9 MB/s OK Compression level: 6 comp(write): 194.6 us, 20554.5 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 104.7 us, 38205.4 MB/s OK Compression level: 7 comp(write): 195.3 us, 20483.2 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 98.7 us, 40536.8 MB/s OK Compression level: 8 comp(write): 199.1 us, 20087.2 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 98.4 us, 40647.3 MB/s OK Compression level: 9 comp(write): 405.3 us, 9868.3 MB/s Final bytes: 316064 Ratio: 13.27 decomp(read): 195.9 us, 20415.6 MB/s OK --> 11, 4194304, 4, 19, lz4, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 11 ********************** Running benchmarks ********************* memcpy(write): 428.5 us, 9335.9 MB/s memcpy(read): 459.9 us, 8697.9 MB/s Compression level: 0 comp(write): 137.1 us, 29175.5 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 97.3 us, 41129.3 MB/s OK Compression level: 1 comp(write): 237.8 us, 16823.4 MB/s Final bytes: 882352 Ratio: 4.75 decomp(read): 109.6 us, 36484.1 MB/s OK Compression level: 2 comp(write): 240.6 us, 16623.2 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 106.2 us, 37658.0 MB/s OK Compression level: 3 comp(write): 237.0 us, 16880.2 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 109.2 us, 36627.0 MB/s OK Compression level: 4 comp(write): 199.6 us, 20040.3 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 100.3 us, 39889.5 MB/s OK Compression level: 5 comp(write): 198.8 us, 20122.1 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 100.8 us, 39695.1 MB/s OK Compression level: 6 comp(write): 198.6 us, 20141.4 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 100.0 us, 39990.1 MB/s OK Compression level: 7 comp(write): 201.5 us, 19855.9 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 100.4 us, 39822.9 MB/s OK Compression level: 8 comp(write): 198.7 us, 20129.8 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 100.3 us, 39880.3 MB/s OK Compression level: 9 comp(write): 414.6 us, 9648.7 MB/s Final bytes: 316064 Ratio: 13.27 decomp(read): 201.5 us, 19850.8 MB/s OK --> 12, 4194304, 4, 19, lz4, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 12 ********************** Running benchmarks ********************* memcpy(write): 386.7 us, 10344.6 MB/s memcpy(read): 447.9 us, 8930.5 MB/s Compression level: 0 comp(write): 136.4 us, 29320.8 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 101.4 us, 39440.2 MB/s OK Compression level: 1 comp(write): 230.2 us, 17378.1 MB/s Final bytes: 882352 Ratio: 4.75 decomp(read): 106.8 us, 37463.8 MB/s OK Compression level: 2 comp(write): 243.3 us, 16443.6 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 109.7 us, 36469.6 MB/s OK Compression level: 3 comp(write): 244.3 us, 16374.5 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 112.4 us, 35581.9 MB/s OK Compression level: 4 comp(write): 209.4 us, 19106.0 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 107.2 us, 37302.0 MB/s OK Compression level: 5 comp(write): 209.0 us, 19142.8 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 107.9 us, 37084.2 MB/s OK Compression level: 6 comp(write): 208.8 us, 19153.5 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 107.4 us, 37229.6 MB/s OK Compression level: 7 comp(write): 210.7 us, 18988.6 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 107.1 us, 37342.2 MB/s OK Compression level: 8 comp(write): 208.5 us, 19183.6 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 107.3 us, 37287.1 MB/s OK Compression level: 9 comp(write): 431.1 us, 9279.1 MB/s Final bytes: 316064 Ratio: 13.27 decomp(read): 205.1 us, 19505.1 MB/s OK --> 13, 4194304, 4, 19, lz4, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 13 ********************** Running benchmarks ********************* memcpy(write): 416.5 us, 9602.8 MB/s memcpy(read): 441.6 us, 9058.5 MB/s Compression level: 0 comp(write): 127.2 us, 31453.3 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 100.3 us, 39869.9 MB/s OK Compression level: 1 comp(write): 223.6 us, 17891.5 MB/s Final bytes: 882352 Ratio: 4.75 decomp(read): 102.9 us, 38884.2 MB/s OK Compression level: 2 comp(write): 224.9 us, 17789.0 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 119.4 us, 33503.4 MB/s OK Compression level: 3 comp(write): 214.0 us, 18692.6 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 100.8 us, 39663.7 MB/s OK Compression level: 4 comp(write): 213.8 us, 18709.3 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 109.6 us, 36510.1 MB/s OK Compression level: 5 comp(write): 211.8 us, 18882.5 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 110.2 us, 36289.9 MB/s OK Compression level: 6 comp(write): 211.9 us, 18879.3 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 110.5 us, 36184.7 MB/s OK Compression level: 7 comp(write): 212.8 us, 18797.8 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 109.9 us, 36386.6 MB/s OK Compression level: 8 comp(write): 213.8 us, 18709.3 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 109.4 us, 36563.6 MB/s OK Compression level: 9 comp(write): 436.8 us, 9157.3 MB/s Final bytes: 316064 Ratio: 13.27 decomp(read): 205.8 us, 19435.3 MB/s OK --> 14, 4194304, 4, 19, lz4, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 14 ********************** Running benchmarks ********************* memcpy(write): 394.2 us, 10145.9 MB/s memcpy(read): 430.6 us, 9290.1 MB/s Compression level: 0 comp(write): 141.7 us, 28224.5 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 101.3 us, 39497.1 MB/s OK Compression level: 1 comp(write): 213.3 us, 18754.4 MB/s Final bytes: 882352 Ratio: 4.75 decomp(read): 108.6 us, 36840.4 MB/s OK Compression level: 2 comp(write): 213.4 us, 18747.0 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 103.9 us, 38513.2 MB/s OK Compression level: 3 comp(write): 215.9 us, 18524.1 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 104.1 us, 38428.4 MB/s OK Compression level: 4 comp(write): 223.3 us, 17916.4 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 112.3 us, 35612.6 MB/s OK Compression level: 5 comp(write): 217.1 us, 18428.5 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 111.7 us, 35799.9 MB/s OK Compression level: 6 comp(write): 211.7 us, 18897.0 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 112.2 us, 35647.3 MB/s OK Compression level: 7 comp(write): 216.8 us, 18450.4 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 111.2 us, 35971.7 MB/s OK Compression level: 8 comp(write): 213.2 us, 18765.6 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 112.4 us, 35578.3 MB/s OK Compression level: 9 comp(write): 436.2 us, 9169.2 MB/s Final bytes: 316064 Ratio: 13.27 decomp(read): 205.6 us, 19457.9 MB/s OK --> 15, 4194304, 4, 19, lz4, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 15 ********************** Running benchmarks ********************* memcpy(write): 412.6 us, 9695.7 MB/s memcpy(read): 460.7 us, 8682.0 MB/s Compression level: 0 comp(write): 139.7 us, 28629.5 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 114.5 us, 34922.7 MB/s OK Compression level: 1 comp(write): 212.9 us, 18788.3 MB/s Final bytes: 882352 Ratio: 4.75 decomp(read): 145.3 us, 27527.1 MB/s OK Compression level: 2 comp(write): 213.9 us, 18703.5 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 123.6 us, 32371.0 MB/s OK Compression level: 3 comp(write): 214.9 us, 18611.6 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 114.2 us, 35041.1 MB/s OK Compression level: 4 comp(write): 231.4 us, 17282.7 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 116.2 us, 34425.6 MB/s OK Compression level: 5 comp(write): 231.2 us, 17301.3 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 133.4 us, 29995.7 MB/s OK Compression level: 6 comp(write): 228.2 us, 17531.3 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 121.3 us, 32978.0 MB/s OK Compression level: 7 comp(write): 231.6 us, 17274.1 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 128.2 us, 31211.2 MB/s OK Compression level: 8 comp(write): 239.0 us, 16734.2 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 127.7 us, 31320.3 MB/s OK Compression level: 9 comp(write): 451.1 us, 8866.9 MB/s Final bytes: 316064 Ratio: 13.27 decomp(read): 307.5 us, 13009.8 MB/s OK --> 16, 4194304, 4, 19, lz4, shuffle ********************** Run info ****************************** Blosc version: 2.0.0.beta.6.dev ($Date:: 2020-04-21 #$) Using synthetic data with 19 significant bits (out of 32) Dataset size: 4194304 bytes Type size: 4 bytes Working set: 256.0 MB Number of threads: 16 ********************** Running benchmarks ********************* memcpy(write): 407.0 us, 9828.8 MB/s memcpy(read): 429.6 us, 9311.2 MB/s Compression level: 0 comp(write): 136.2 us, 29374.6 MB/s Final bytes: 4194336 Ratio: 1.00 decomp(read): 112.9 us, 35428.1 MB/s OK Compression level: 1 comp(write): 202.3 us, 19772.6 MB/s Final bytes: 882352 Ratio: 4.75 decomp(read): 140.4 us, 28495.0 MB/s OK Compression level: 2 comp(write): 210.7 us, 18980.6 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 143.2 us, 27928.5 MB/s OK Compression level: 3 comp(write): 215.0 us, 18602.8 MB/s Final bytes: 798592 Ratio: 5.25 decomp(read): 143.8 us, 27809.7 MB/s OK Compression level: 4 comp(write): 241.5 us, 16563.8 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 116.4 us, 34369.9 MB/s OK Compression level: 5 comp(write): 241.5 us, 16565.7 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 116.1 us, 34447.2 MB/s OK Compression level: 6 comp(write): 234.3 us, 17069.1 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 117.1 us, 34151.1 MB/s OK Compression level: 7 comp(write): 246.7 us, 16216.4 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 119.5 us, 33468.5 MB/s OK Compression level: 8 comp(write): 249.9 us, 16005.1 MB/s Final bytes: 334136 Ratio: 12.55 decomp(read): 116.8 us, 34238.3 MB/s OK Compression level: 9 comp(write): 445.7 us, 8974.0 MB/s Final bytes: 316064 Ratio: 13.27 decomp(read): 218.9 us, 18275.0 MB/s OK Round-trip compr/decompr on 120.0 GB Elapsed time: 20.4 s, 13264.9 MB/s c-blosc2-3.1.5/bench/results-corex/lz4hc-cl1-sum_openmp-corex.out000066400000000000000000000267601521743436100245730ustar00rootroot00000000000000faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=1 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4hc ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 45319996 Sum time for uncompressed data: 0.0322 s, 11844.8 MB/s Compression ratio: 381.5 MB -> 75.8 MB (5.0x) Compression time: 1.65 s, 231.8 MB/s Sum for *compressed* data: 46323164 Sum time for *compressed* data: 0.0625 s, 6107.7 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=1 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4hc ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 45319996 Sum time for uncompressed data: 0.0323 s, 11800.8 MB/s Compression ratio: 381.5 MB -> 75.8 MB (5.0x) Compression time: 1.65 s, 231.8 MB/s Sum for *compressed* data: 46323164 Sum time for *compressed* data: 0.0624 s, 6109.7 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=2 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4hc ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 45826496 Sum time for uncompressed data: 0.0177 s, 21602.1 MB/s Compression ratio: 381.5 MB -> 75.8 MB (5.0x) Compression time: 1.64 s, 232.0 MB/s Sum for *compressed* data: 46321328 Sum time for *compressed* data: 0.0343 s, 11114.1 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=2 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4hc ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 45826496 Sum time for uncompressed data: 0.0177 s, 21600.5 MB/s Compression ratio: 381.5 MB -> 75.8 MB (5.0x) Compression time: 1.64 s, 232.0 MB/s Sum for *compressed* data: 46321328 Sum time for *compressed* data: 0.0344 s, 11081.9 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=4 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4hc ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46118560 Sum time for uncompressed data: 0.00957 s, 39848.6 MB/s Compression ratio: 381.5 MB -> 75.8 MB (5.0x) Compression time: 1.64 s, 232.3 MB/s Sum for *compressed* data: 46320460 Sum time for *compressed* data: 0.0179 s, 21368.7 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=4 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4hc ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46118560 Sum time for uncompressed data: 0.00956 s, 39921.2 MB/s Compression ratio: 381.5 MB -> 75.8 MB (5.0x) Compression time: 1.64 s, 232.2 MB/s Sum for *compressed* data: 46320460 Sum time for *compressed* data: 0.0179 s, 21352.4 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=8 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4hc ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46248988 Sum time for uncompressed data: 0.00676 s, 56414.8 MB/s Compression ratio: 381.5 MB -> 75.8 MB (5.0x) Compression time: 1.64 s, 232.0 MB/s Sum for *compressed* data: 46320768 Sum time for *compressed* data: 0.0091 s, 41932.4 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=8 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4hc ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46248984 Sum time for uncompressed data: 0.0069 s, 55266.6 MB/s Compression ratio: 381.5 MB -> 75.8 MB (5.0x) Compression time: 1.64 s, 232.2 MB/s Sum for *compressed* data: 46320768 Sum time for *compressed* data: 0.00916 s, 41632.7 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=12 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4hc ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46285244 Sum time for uncompressed data: 0.00666 s, 57268.5 MB/s Compression ratio: 381.5 MB -> 75.8 MB (5.0x) Compression time: 1.64 s, 232.0 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.00642 s, 59449.5 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=12 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4hc ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46285244 Sum time for uncompressed data: 0.00676 s, 56467.4 MB/s Compression ratio: 381.5 MB -> 75.8 MB (5.0x) Compression time: 1.64 s, 232.0 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.00656 s, 58192.0 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=14 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4hc ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46290212 Sum time for uncompressed data: 0.00662 s, 57588.5 MB/s Compression ratio: 381.5 MB -> 75.8 MB (5.0x) Compression time: 1.64 s, 231.9 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.00571 s, 66780.0 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=14 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4hc ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46290216 Sum time for uncompressed data: 0.00657 s, 58060.3 MB/s Compression ratio: 381.5 MB -> 75.8 MB (5.0x) Compression time: 1.64 s, 232.0 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.00573 s, 66628.7 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=16 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4hc ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46295172 Sum time for uncompressed data: 0.00776 s, 49178.0 MB/s Compression ratio: 381.5 MB -> 75.8 MB (5.0x) Compression time: 1.64 s, 232.3 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.00742 s, 51385.1 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=16 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4hc ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46295176 Sum time for uncompressed data: 0.00779 s, 48998.3 MB/s Compression ratio: 381.5 MB -> 75.8 MB (5.0x) Compression time: 1.64 s, 232.2 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.00757 s, 50380.2 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=20 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4hc ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46305112 Sum time for uncompressed data: 0.00688 s, 55486.0 MB/s Compression ratio: 381.5 MB -> 75.8 MB (5.0x) Compression time: 1.64 s, 232.3 MB/s Sum for *compressed* data: 46320364 Sum time for *compressed* data: 0.00637 s, 59930.7 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=20 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4hc ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46305112 Sum time for uncompressed data: 0.00691 s, 55199.4 MB/s Compression ratio: 381.5 MB -> 75.8 MB (5.0x) Compression time: 1.65 s, 231.8 MB/s Sum for *compressed* data: 46320364 Sum time for *compressed* data: 0.00623 s, 61220.5 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=24 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4hc ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46310868 Sum time for uncompressed data: 0.00691 s, 55174.0 MB/s Compression ratio: 381.5 MB -> 75.8 MB (5.0x) Compression time: 1.65 s, 231.8 MB/s Sum for *compressed* data: 46320384 Sum time for *compressed* data: 0.00551 s, 69262.0 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=24 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4hc ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46310864 Sum time for uncompressed data: 0.00685 s, 55664.6 MB/s Compression ratio: 381.5 MB -> 75.8 MB (5.0x) Compression time: 1.65 s, 231.8 MB/s Sum for *compressed* data: 46320384 Sum time for *compressed* data: 0.00556 s, 68665.1 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=28 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4hc ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46311968 Sum time for uncompressed data: 0.00671 s, 56871.7 MB/s Compression ratio: 381.5 MB -> 75.8 MB (5.0x) Compression time: 1.65 s, 231.6 MB/s Sum for *compressed* data: 46320396 Sum time for *compressed* data: 0.00484 s, 78759.4 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=28 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4hc ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46311960 Sum time for uncompressed data: 0.00679 s, 56205.3 MB/s Compression ratio: 381.5 MB -> 75.8 MB (5.0x) Compression time: 1.64 s, 232.1 MB/s Sum for *compressed* data: 46320396 Sum time for *compressed* data: 0.00463 s, 82380.5 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=28 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4hc ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46311968 Sum time for uncompressed data: 0.00671 s, 56841.4 MB/s Compression ratio: 381.5 MB -> 75.8 MB (5.0x) Compression time: 1.64 s, 232.2 MB/s Sum for *compressed* data: 46320396 Sum time for *compressed* data: 0.00564 s, 67624.7 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=32 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4hc ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46313056 Sum time for uncompressed data: 0.00815 s, 46788.0 MB/s Compression ratio: 381.5 MB -> 75.8 MB (5.0x) Compression time: 1.64 s, 232.2 MB/s Sum for *compressed* data: 46320392 Sum time for *compressed* data: 0.0102 s, 37305.5 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=32 SUM_CLEVEL=1 SUM_COMPRESSOR=lz4hc ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46313056 Sum time for uncompressed data: 0.00817 s, 46708.0 MB/s Compression ratio: 381.5 MB -> 75.8 MB (5.0x) Compression time: 1.64 s, 232.0 MB/s Sum for *compressed* data: 46320392 Sum time for *compressed* data: 0.0126 s, 30329.6 MB/s c-blosc2-3.1.5/bench/results-corex/zlib-cl1-sum_openmp-corex.out000066400000000000000000000257111521743436100245020ustar00rootroot00000000000000faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=1 SUM_CLEVEL=1 SUM_COMPRESSOR=zlib ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 45319996 Sum time for uncompressed data: 0.0323 s, 11817.7 MB/s Compression ratio: 381.5 MB -> 80.1 MB (4.8x) Compression time: 1.04 s, 366.5 MB/s Sum for *compressed* data: 46323164 Sum time for *compressed* data: 0.456 s, 836.5 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=1 SUM_CLEVEL=1 SUM_COMPRESSOR=zlib ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 45319996 Sum time for uncompressed data: 0.0322 s, 11829.1 MB/s Compression ratio: 381.5 MB -> 80.1 MB (4.8x) Compression time: 1.04 s, 366.3 MB/s Sum for *compressed* data: 46323164 Sum time for *compressed* data: 0.458 s, 833.1 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=2 SUM_CLEVEL=1 SUM_COMPRESSOR=zlib ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 45826496 Sum time for uncompressed data: 0.0176 s, 21670.8 MB/s Compression ratio: 381.5 MB -> 80.1 MB (4.8x) Compression time: 1.05 s, 364.2 MB/s Sum for *compressed* data: 46321328 Sum time for *compressed* data: 0.248 s, 1540.9 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=2 SUM_CLEVEL=1 SUM_COMPRESSOR=zlib ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 45826496 Sum time for uncompressed data: 0.0177 s, 21593.3 MB/s Compression ratio: 381.5 MB -> 80.1 MB (4.8x) Compression time: 1.04 s, 365.7 MB/s Sum for *compressed* data: 46321328 Sum time for *compressed* data: 0.248 s, 1539.9 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=4 SUM_CLEVEL=1 SUM_COMPRESSOR=zlib ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46118560 Sum time for uncompressed data: 0.00964 s, 39585.1 MB/s Compression ratio: 381.5 MB -> 80.1 MB (4.8x) Compression time: 1.04 s, 366.0 MB/s Sum for *compressed* data: 46320460 Sum time for *compressed* data: 0.127 s, 3002.1 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=4 SUM_CLEVEL=1 SUM_COMPRESSOR=zlib ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46118560 Sum time for uncompressed data: 0.00959 s, 39782.9 MB/s Compression ratio: 381.5 MB -> 80.1 MB (4.8x) Compression time: 1.04 s, 365.1 MB/s Sum for *compressed* data: 46320460 Sum time for *compressed* data: 0.127 s, 3003.6 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=8 SUM_CLEVEL=1 SUM_COMPRESSOR=zlib ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46248984 Sum time for uncompressed data: 0.00676 s, 56406.0 MB/s Compression ratio: 381.5 MB -> 80.1 MB (4.8x) Compression time: 1.04 s, 365.2 MB/s Sum for *compressed* data: 46320768 Sum time for *compressed* data: 0.0638 s, 5978.4 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=8 SUM_CLEVEL=1 SUM_COMPRESSOR=zlib ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46248984 Sum time for uncompressed data: 0.00664 s, 57491.2 MB/s Compression ratio: 381.5 MB -> 80.1 MB (4.8x) Compression time: 1.04 s, 365.1 MB/s Sum for *compressed* data: 46320768 Sum time for *compressed* data: 0.0641 s, 5954.5 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=12 SUM_CLEVEL=1 SUM_COMPRESSOR=zlib ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46285244 Sum time for uncompressed data: 0.00661 s, 57684.3 MB/s Compression ratio: 381.5 MB -> 80.1 MB (4.8x) Compression time: 1.04 s, 365.3 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.0437 s, 8726.8 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=12 SUM_CLEVEL=1 SUM_COMPRESSOR=zlib ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46285244 Sum time for uncompressed data: 0.00665 s, 57387.5 MB/s Compression ratio: 381.5 MB -> 80.1 MB (4.8x) Compression time: 1.05 s, 363.9 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.0437 s, 8733.4 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=14 SUM_CLEVEL=1 SUM_COMPRESSOR=zlib ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46290212 Sum time for uncompressed data: 0.0067 s, 56901.7 MB/s Compression ratio: 381.5 MB -> 80.1 MB (4.8x) Compression time: 1.05 s, 363.5 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.039 s, 9789.8 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=14 SUM_CLEVEL=1 SUM_COMPRESSOR=zlib ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46290212 Sum time for uncompressed data: 0.00668 s, 57103.1 MB/s Compression ratio: 381.5 MB -> 80.1 MB (4.8x) Compression time: 1.05 s, 364.8 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.039 s, 9772.4 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=16 SUM_CLEVEL=1 SUM_COMPRESSOR=zlib ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46295176 Sum time for uncompressed data: 0.00775 s, 49219.3 MB/s Compression ratio: 381.5 MB -> 80.1 MB (4.8x) Compression time: 1.05 s, 364.3 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.0496 s, 7690.1 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=16 SUM_CLEVEL=1 SUM_COMPRESSOR=zlib ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46295176 Sum time for uncompressed data: 0.00771 s, 49485.3 MB/s Compression ratio: 381.5 MB -> 80.1 MB (4.8x) Compression time: 1.05 s, 364.5 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.0487 s, 7838.4 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=20 SUM_CLEVEL=1 SUM_COMPRESSOR=zlib ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46305112 Sum time for uncompressed data: 0.00688 s, 55470.9 MB/s Compression ratio: 381.5 MB -> 80.1 MB (4.8x) Compression time: 1.04 s, 365.5 MB/s Sum for *compressed* data: 46320364 Sum time for *compressed* data: 0.0395 s, 9649.0 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=20 SUM_CLEVEL=1 SUM_COMPRESSOR=zlib ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46305116 Sum time for uncompressed data: 0.00684 s, 55748.9 MB/s Compression ratio: 381.5 MB -> 80.1 MB (4.8x) Compression time: 1.04 s, 365.6 MB/s Sum for *compressed* data: 46320364 Sum time for *compressed* data: 0.0403 s, 9466.8 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=24 SUM_CLEVEL=1 SUM_COMPRESSOR=zlib ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46310872 Sum time for uncompressed data: 0.00687 s, 55562.1 MB/s Compression ratio: 381.5 MB -> 80.1 MB (4.8x) Compression time: 1.04 s, 365.2 MB/s Sum for *compressed* data: 46320384 Sum time for *compressed* data: 0.0336 s, 11357.4 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=24 SUM_CLEVEL=1 SUM_COMPRESSOR=zlib ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46310868 Sum time for uncompressed data: 0.00686 s, 55588.8 MB/s Compression ratio: 381.5 MB -> 80.1 MB (4.8x) Compression time: 1.06 s, 360.9 MB/s Sum for *compressed* data: 46320384 Sum time for *compressed* data: 0.0337 s, 11325.4 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=28 SUM_CLEVEL=1 SUM_COMPRESSOR=zlib ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46311968 Sum time for uncompressed data: 0.00677 s, 56360.0 MB/s Compression ratio: 381.5 MB -> 80.1 MB (4.8x) Compression time: 1.05 s, 364.3 MB/s Sum for *compressed* data: 46320396 Sum time for *compressed* data: 0.0291 s, 13109.1 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=28 SUM_CLEVEL=1 SUM_COMPRESSOR=zlib ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46311964 Sum time for uncompressed data: 0.00677 s, 56349.4 MB/s Compression ratio: 381.5 MB -> 80.1 MB (4.8x) Compression time: 1.05 s, 364.6 MB/s Sum for *compressed* data: 46320396 Sum time for *compressed* data: 0.0291 s, 13091.0 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=32 SUM_CLEVEL=1 SUM_COMPRESSOR=zlib ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46313056 Sum time for uncompressed data: 0.00812 s, 46977.8 MB/s Compression ratio: 381.5 MB -> 80.1 MB (4.8x) Compression time: 1.04 s, 365.7 MB/s Sum for *compressed* data: 46320392 Sum time for *compressed* data: 0.0645 s, 5913.1 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=32 SUM_CLEVEL=1 SUM_COMPRESSOR=zlib ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46313056 Sum time for uncompressed data: 0.00813 s, 46931.5 MB/s Compression ratio: 381.5 MB -> 80.1 MB (4.8x) Compression time: 1.05 s, 364.7 MB/s Sum for *compressed* data: 46320392 Sum time for *compressed* data: 0.0538 s, 7092.2 MB/s c-blosc2-3.1.5/bench/results-corex/zstd-cl1-sum_openmp-corex.out000066400000000000000000000257601521743436100245320ustar00rootroot00000000000000faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=1 SUM_CLEVEL=1 SUM_COMPRESSOR=zstd ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 45319996 Sum time for uncompressed data: 0.0323 s, 11818.2 MB/s Compression ratio: 381.5 MB -> 64.3 MB (5.9x) Compression time: 0.671 s, 568.2 MB/s Sum for *compressed* data: 46323164 Sum time for *compressed* data: 0.241 s, 1582.4 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=1 SUM_CLEVEL=1 SUM_COMPRESSOR=zstd ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 45319996 Sum time for uncompressed data: 0.0323 s, 11814.4 MB/s Compression ratio: 381.5 MB -> 64.3 MB (5.9x) Compression time: 0.671 s, 568.5 MB/s Sum for *compressed* data: 46323164 Sum time for *compressed* data: 0.239 s, 1593.8 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=2 SUM_CLEVEL=1 SUM_COMPRESSOR=zstd ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 45826496 Sum time for uncompressed data: 0.0176 s, 21651.6 MB/s Compression ratio: 381.5 MB -> 64.3 MB (5.9x) Compression time: 0.672 s, 567.6 MB/s Sum for *compressed* data: 46321328 Sum time for *compressed* data: 0.13 s, 2926.1 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=2 SUM_CLEVEL=1 SUM_COMPRESSOR=zstd ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 45826496 Sum time for uncompressed data: 0.0177 s, 21586.1 MB/s Compression ratio: 381.5 MB -> 64.3 MB (5.9x) Compression time: 0.673 s, 566.7 MB/s Sum for *compressed* data: 46321328 Sum time for *compressed* data: 0.129 s, 2955.5 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=4 SUM_CLEVEL=1 SUM_COMPRESSOR=zstd ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46118560 Sum time for uncompressed data: 0.00974 s, 39182.0 MB/s Compression ratio: 381.5 MB -> 64.3 MB (5.9x) Compression time: 0.672 s, 567.3 MB/s Sum for *compressed* data: 46320460 Sum time for *compressed* data: 0.0673 s, 5669.4 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=4 SUM_CLEVEL=1 SUM_COMPRESSOR=zstd ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46118560 Sum time for uncompressed data: 0.00958 s, 39816.2 MB/s Compression ratio: 381.5 MB -> 64.3 MB (5.9x) Compression time: 0.673 s, 566.7 MB/s Sum for *compressed* data: 46320460 Sum time for *compressed* data: 0.0673 s, 5664.8 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=8 SUM_CLEVEL=1 SUM_COMPRESSOR=zstd ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46248988 Sum time for uncompressed data: 0.00665 s, 57392.3 MB/s Compression ratio: 381.5 MB -> 64.3 MB (5.9x) Compression time: 0.674 s, 565.8 MB/s Sum for *compressed* data: 46320768 Sum time for *compressed* data: 0.0341 s, 11201.3 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=8 SUM_CLEVEL=1 SUM_COMPRESSOR=zstd ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46248984 Sum time for uncompressed data: 0.00669 s, 57026.4 MB/s Compression ratio: 381.5 MB -> 64.3 MB (5.9x) Compression time: 0.674 s, 566.0 MB/s Sum for *compressed* data: 46320768 Sum time for *compressed* data: 0.0339 s, 11244.7 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=12 SUM_CLEVEL=1 SUM_COMPRESSOR=zstd ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46285244 Sum time for uncompressed data: 0.00665 s, 57329.8 MB/s Compression ratio: 381.5 MB -> 64.3 MB (5.9x) Compression time: 0.673 s, 567.0 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.0233 s, 16363.2 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=12 SUM_CLEVEL=1 SUM_COMPRESSOR=zstd ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46285244 Sum time for uncompressed data: 0.00659 s, 57899.4 MB/s Compression ratio: 381.5 MB -> 64.3 MB (5.9x) Compression time: 0.671 s, 568.5 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.0234 s, 16315.3 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=14 SUM_CLEVEL=1 SUM_COMPRESSOR=zstd ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46290216 Sum time for uncompressed data: 0.00667 s, 57162.5 MB/s Compression ratio: 381.5 MB -> 64.3 MB (5.9x) Compression time: 0.672 s, 567.8 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.0208 s, 18306.0 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=14 SUM_CLEVEL=1 SUM_COMPRESSOR=zstd ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46290212 Sum time for uncompressed data: 0.00668 s, 57129.5 MB/s Compression ratio: 381.5 MB -> 64.3 MB (5.9x) Compression time: 0.675 s, 564.7 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.0208 s, 18377.6 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=16 SUM_CLEVEL=1 SUM_COMPRESSOR=zstd ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46295180 Sum time for uncompressed data: 0.00767 s, 49711.6 MB/s Compression ratio: 381.5 MB -> 64.3 MB (5.9x) Compression time: 0.674 s, 566.0 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.0298 s, 12814.4 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=16 SUM_CLEVEL=1 SUM_COMPRESSOR=zstd ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46295176 Sum time for uncompressed data: 0.00768 s, 49680.6 MB/s Compression ratio: 381.5 MB -> 64.3 MB (5.9x) Compression time: 0.674 s, 566.0 MB/s Sum for *compressed* data: 46320348 Sum time for *compressed* data: 0.0297 s, 12865.2 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=20 SUM_CLEVEL=1 SUM_COMPRESSOR=zstd ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46305112 Sum time for uncompressed data: 0.00691 s, 55204.8 MB/s Compression ratio: 381.5 MB -> 64.3 MB (5.9x) Compression time: 0.674 s, 566.1 MB/s Sum for *compressed* data: 46320364 Sum time for *compressed* data: 0.0241 s, 15817.6 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=20 SUM_CLEVEL=1 SUM_COMPRESSOR=zstd ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46305112 Sum time for uncompressed data: 0.00687 s, 55565.0 MB/s Compression ratio: 381.5 MB -> 64.3 MB (5.9x) Compression time: 0.673 s, 566.7 MB/s Sum for *compressed* data: 46320364 Sum time for *compressed* data: 0.0241 s, 15830.7 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=24 SUM_CLEVEL=1 SUM_COMPRESSOR=zstd ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46310864 Sum time for uncompressed data: 0.0069 s, 55261.4 MB/s Compression ratio: 381.5 MB -> 64.3 MB (5.9x) Compression time: 0.675 s, 565.4 MB/s Sum for *compressed* data: 46320384 Sum time for *compressed* data: 0.0205 s, 18641.4 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=24 SUM_CLEVEL=1 SUM_COMPRESSOR=zstd ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46310868 Sum time for uncompressed data: 0.00691 s, 55239.8 MB/s Compression ratio: 381.5 MB -> 64.3 MB (5.9x) Compression time: 0.673 s, 566.5 MB/s Sum for *compressed* data: 46320384 Sum time for *compressed* data: 0.0204 s, 18736.9 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=28 SUM_CLEVEL=1 SUM_COMPRESSOR=zstd ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46311968 Sum time for uncompressed data: 0.00679 s, 56218.0 MB/s Compression ratio: 381.5 MB -> 64.3 MB (5.9x) Compression time: 0.673 s, 566.5 MB/s Sum for *compressed* data: 46320396 Sum time for *compressed* data: 0.0178 s, 21474.3 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=28 SUM_CLEVEL=1 SUM_COMPRESSOR=zstd ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46311964 Sum time for uncompressed data: 0.00674 s, 56574.7 MB/s Compression ratio: 381.5 MB -> 64.3 MB (5.9x) Compression time: 0.672 s, 567.9 MB/s Sum for *compressed* data: 46320396 Sum time for *compressed* data: 0.0181 s, 21132.3 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=32 SUM_CLEVEL=1 SUM_COMPRESSOR=zstd ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46313056 Sum time for uncompressed data: 0.00812 s, 46950.8 MB/s Compression ratio: 381.5 MB -> 64.3 MB (5.9x) Compression time: 0.673 s, 567.0 MB/s Sum for *compressed* data: 46320392 Sum time for *compressed* data: 0.0364 s, 10466.3 MB/s faltet@corex14 ~/b/c/bench (main)> env LD_LIBRARY_PATH=../build/blosc OMP_PROC_BIND=spread OMP_NUM_THREADS=32 SUM_CLEVEL=1 SUM_COMPRESSOR=zstd ./sum_openmp (base) Blosc version info: 2.0.0.rc.2.dev ($Date:: 2021-05-06 #$) Sum for uncompressed data: 46313056 Sum time for uncompressed data: 0.00809 s, 47138.4 MB/s Compression ratio: 381.5 MB -> 64.3 MB (5.9x) Compression time: 0.674 s, 565.6 MB/s Sum for *compressed* data: 46320392 Sum time for *compressed* data: 0.0366 s, 10420.4 MB/s c-blosc2-3.1.5/bench/sframe_bench.c000066400000000000000000000316561521743436100170040ustar00rootroot00000000000000/********************************************************************* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Benchmark for testing sframe vs frame. For usage instructions of this benchmark, please see: https://www.blosc.org/pages/synthetic-benchmarks/ We are collecting speeds for different machines, so the output of your benchmarks and your processor specifications are welcome! See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #include "blosc2.h" #if defined(_WIN32) #include #endif #if defined(_WIN32) && !defined(__MINGW32__) #include #else #include #endif #include #include #include #include #include #include #define KB 1024 #define MB (1024*KB) #define GB (1024*MB) #define NCHUNKS 1000 /* number of chunks */ #define CHUNKSIZE (2000 * 1000) int nchunks = NCHUNKS; int iterations = 5; int io_type = BLOSC2_IO_FILESYSTEM; void test_update(blosc2_schunk* schunk_sframe, blosc2_schunk* schunk_cframe) { blosc_timestamp_t last, current; double cframe_update_time, sframe_update_time; size_t isize = CHUNKSIZE * sizeof(int32_t); int32_t* data = malloc(isize); // Random update list int64_t* update_chunks = malloc(sizeof(int64_t) * iterations); srand(time(NULL)); for (int i = 0; i < iterations; i++) { update_chunks[i] = rand() % schunk_sframe->nchunks; } printf("*******************************************************\n"); printf("******************* Updating %d chunks ******************\n", iterations); printf("*******************************************************\n"); // Update the sframe chunks sframe_update_time = 0.0; cframe_update_time = 0.0; int32_t datasize = sizeof(int32_t) * CHUNKSIZE; int32_t chunksize = sizeof(int32_t) * CHUNKSIZE + BLOSC2_MAX_OVERHEAD; uint8_t* chunk; int csize; int64_t _nchunks; for (int i = 0; i < iterations; i++) { // Generate data for (int j = 0; j < CHUNKSIZE; j++) { data[j] = i * CHUNKSIZE; } chunk = malloc(chunksize); csize = blosc2_compress_ctx(schunk_sframe->cctx, data, datasize, chunk, chunksize); if (csize < 0) { printf("ERROR: chunk cannot be compressed\n"); } // Sframe blosc_set_timestamp(¤t); _nchunks = blosc2_schunk_update_chunk(schunk_sframe, update_chunks[i], chunk, true); blosc_set_timestamp(&last); if (_nchunks < 0){ printf("ERROR: chunk cannot be updated correctly\n"); } sframe_update_time += blosc_elapsed_secs(current, last); // Frame blosc_set_timestamp(¤t); _nchunks = blosc2_schunk_update_chunk(schunk_cframe, update_chunks[i], chunk, true); blosc_set_timestamp(&last); free(chunk); if (_nchunks <= 0){ printf("ERROR: chunk cannot be updated correctly\n"); } cframe_update_time += blosc_elapsed_secs(current, last); } printf("[Sframe Update] Elapsed time:\t %6.3f s. Total sframe size: %.3" PRId64 " bytes\n", sframe_update_time, schunk_sframe->cbytes); printf("[Cframe Update] Elapsed time:\t %6.3f s. Total cframe size: %.3" PRId64 " bytes\n", cframe_update_time, schunk_cframe->cbytes); /* Free resources */ free(update_chunks); free(data); } void test_insert(blosc2_schunk* schunk_sframe, blosc2_schunk* schunk_cframe) { blosc_timestamp_t last, current; double cframe_insert_time, sframe_insert_time; size_t isize = CHUNKSIZE * sizeof(int32_t); int32_t* data = malloc(isize); // Random insert list int64_t* insert_chunks = malloc(sizeof(int64_t) * iterations); srand(time(NULL)); for (int i = 0; i < iterations; i++) { insert_chunks[i] = rand() % schunk_sframe->nchunks; } printf("*******************************************************\n"); printf("****************** Inserting %d chunks *****************\n", iterations); printf("*******************************************************\n"); // Update the sframe chunks sframe_insert_time = 0.0; cframe_insert_time = 0.0; int32_t datasize = sizeof(int32_t) * CHUNKSIZE; int32_t chunksize = sizeof(int32_t) * CHUNKSIZE + BLOSC2_MAX_OVERHEAD; uint8_t* chunk; int csize; int64_t _nchunks; for (int i = 0; i < iterations; i++) { // Generate data for (int j = 0; j < CHUNKSIZE; j++) { data[j] = j + i * CHUNKSIZE; } chunk = malloc(chunksize); csize = blosc2_compress_ctx(schunk_sframe->cctx, data, datasize, chunk, chunksize); if (csize < 0) { printf("ERROR: chunk cannot be compressed\n"); } // Sframe blosc_set_timestamp(¤t); _nchunks = blosc2_schunk_insert_chunk(schunk_sframe, insert_chunks[i], chunk, true); blosc_set_timestamp(&last); if (_nchunks < 0){ printf("ERROR: chunk cannot be updated correctly\n"); } sframe_insert_time += blosc_elapsed_secs(current, last); // Frame blosc_set_timestamp(¤t); _nchunks = blosc2_schunk_update_chunk(schunk_cframe, insert_chunks[i], chunk, true); blosc_set_timestamp(&last); free(chunk); if (_nchunks <= 0){ printf("ERROR: chunk cannot be updated correctly\n"); } cframe_insert_time += blosc_elapsed_secs(current, last); } printf("[Sframe Insert] Elapsed time:\t %6.3f s. Total sframe size: %.3" PRId64 " bytes\n", sframe_insert_time, schunk_sframe->cbytes); printf("[Cframe Insert] Elapsed time:\t %6.3f s. Total cframe size: %.3" PRId64 " bytes\n", cframe_insert_time, schunk_cframe->cbytes); /* Free resources */ free(insert_chunks); free(data); } void test_reorder(blosc2_schunk* schunk_sframe, blosc2_schunk* schunk_cframe) { blosc_timestamp_t last, current; double cframe_reorder_time, sframe_reorder_time; // Reorder list int64_t *offsets_order = malloc(sizeof(int64_t) * schunk_sframe->nchunks); for (int i = 0; i < schunk_sframe->nchunks; ++i) { offsets_order[i] = (i + 3) % schunk_sframe->nchunks; } printf("*******************************************************\n"); printf("****************** Reordering chunks ******************\n"); printf("*******************************************************\n"); // Reorder sframe blosc_set_timestamp(¤t); int err = blosc2_schunk_reorder_offsets(schunk_sframe, offsets_order); blosc_set_timestamp(&last); if (err < 0) { printf("ERROR: cannot reorder chunks\n"); } sframe_reorder_time = blosc_elapsed_secs(current, last); // Reorder frame blosc_set_timestamp(¤t); err = blosc2_schunk_reorder_offsets(schunk_cframe, offsets_order); blosc_set_timestamp(&last); if (err < 0) { printf("ERROR: cannot reorder chunks\n"); } cframe_reorder_time = blosc_elapsed_secs(current, last); printf("[Sframe Update] Elapsed time:\t %f s. Total sframe size: %.3" PRId64 " bytes\n", sframe_reorder_time, schunk_sframe->cbytes); printf("[Cframe Update] Elapsed time:\t %f s. Total cframe size: %.3" PRId64 " bytes\n", cframe_reorder_time, schunk_cframe->cbytes); /* Free resources */ free(offsets_order); } void test_create_sframe_frame(char* operation) { blosc_timestamp_t last, current; double cframe_append_time, sframe_append_time, cframe_decompress_time, sframe_decompress_time; int64_t nbytes, cbytes; int32_t isize = CHUNKSIZE * sizeof(int32_t); int dsize; float totalsize = (float)isize * nchunks; int32_t* data = malloc(isize); int32_t* data_dest = malloc(isize); blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; blosc2_schunk* schunk_sframe; blosc2_schunk* schunk_cframe; /* Initialize the Blosc compressor */ blosc2_init(); printf("*******************************************************\n"); printf("***** Creating the frame and sframe with %d chunks ****\n", nchunks); printf("*******************************************************\n"); /* Create a frame container */ cparams.typesize = sizeof(int32_t); cparams.nthreads = 2; dparams.nthreads = 2; blosc2_storage storage = {.contiguous=false, .urlpath="dir.b2frame", .cparams=&cparams, .dparams=&dparams}; blosc2_remove_urlpath(storage.urlpath); schunk_sframe = blosc2_schunk_new(&storage); blosc2_stdio_mmap mmap_file = BLOSC2_STDIO_MMAP_DEFAULTS; mmap_file.mode = "w+"; blosc2_io io_mmap = {.id = BLOSC2_IO_FILESYSTEM_MMAP, .name = "filesystem_mmap", .params = &mmap_file}; blosc2_storage storage2 = {.contiguous=true, .urlpath="test_cframe.b2frame", .cparams=&cparams, .dparams=&dparams}; if (io_type == BLOSC2_IO_FILESYSTEM) { storage2.io = (blosc2_io*)&BLOSC2_IO_DEFAULTS; } else if (io_type == BLOSC2_IO_FILESYSTEM_MMAP) { storage2.io = &io_mmap; } blosc2_remove_urlpath(storage2.urlpath); schunk_cframe = blosc2_schunk_new(&storage2); printf("Test comparison frame vs sframe with %d chunks.\n", nchunks); // Feed it with data sframe_append_time=0.0; cframe_append_time=0.0; blosc_set_timestamp(¤t); for (int nchunk = 0; nchunk < nchunks; nchunk++) { for (int i = 0; i < CHUNKSIZE; i++) { data[i] = i + nchunk * CHUNKSIZE; } blosc_set_timestamp(¤t); blosc2_schunk_append_buffer(schunk_sframe, data, isize); blosc_set_timestamp(&last); sframe_append_time += blosc_elapsed_secs(current, last); blosc_set_timestamp(¤t); blosc2_schunk_append_buffer(schunk_cframe, data, isize); blosc_set_timestamp(&last); cframe_append_time += blosc_elapsed_secs(current, last); } printf("[Sframe Compr] Elapsed time:\t %6.3f s. Processed data: %.3f GB (%.3f GB/s)\n", sframe_append_time, totalsize / GB, totalsize / (GB * sframe_append_time)); printf("[Cframe Compr] Elapsed time:\t %6.3f s. Processed data: %.3f GB (%.3f GB/s)\n", cframe_append_time, totalsize / GB, totalsize / (GB * cframe_append_time)); /* Gather some info */ nbytes = schunk_sframe->nbytes; cbytes = schunk_sframe->cbytes; printf("Compression super-chunk-sframe: %ld -> %ld (%.1fx)\n", (long)nbytes, (long)cbytes, (1. * (double)nbytes) / (double)cbytes); nbytes = schunk_cframe->nbytes; cbytes = schunk_cframe->cbytes; printf("Compression super-chunk-cframe: %ld -> %ld (%.1fx)\n", (long)nbytes, (long)cbytes, (1. * (double)nbytes) / (double)cbytes); // Decompress the data sframe_decompress_time = 0; cframe_decompress_time = 0; for (int nchunk = 0; nchunk < nchunks; nchunk++) { // Sframe blosc_set_timestamp(¤t); dsize = blosc2_schunk_decompress_chunk(schunk_sframe, nchunk, (void *) data_dest, isize); blosc_set_timestamp(&last); if (dsize < 0) { printf("Decompression error sframe. Error code: %d\n", dsize); } assert (dsize == (int)isize); sframe_decompress_time += blosc_elapsed_secs(current, last); // Frame blosc_set_timestamp(¤t); dsize = blosc2_schunk_decompress_chunk(schunk_cframe, nchunk, (void *) data_dest, isize); blosc_set_timestamp(&last); if (dsize < 0) { printf("Decompression error cframe. Error code: %d\n", dsize); } assert (dsize == (int)isize); cframe_decompress_time += blosc_elapsed_secs(current, last); } printf("[Sframe Decompr] Elapsed time:\t %6.3f s. Processed data: %.3f GB (%.3f GB/s)\n", sframe_decompress_time, totalsize / GB, totalsize / (GB * sframe_decompress_time)); printf("[Cframe Decompr] Elapsed time:\t %6.3f s. Processed data: %.3f GB (%.3f GB/s)\n", cframe_decompress_time, totalsize / GB, totalsize / (GB * cframe_decompress_time)); printf("Decompression successful!\n"); printf("Successful roundtrip!\n"); /* Free resources */ free(data_dest); free(data); if (operation != NULL) { if (strcmp(operation, "insert") == 0) { test_insert(schunk_sframe, schunk_cframe); } else if (strcmp(operation, "update") == 0) { test_update(schunk_sframe, schunk_cframe); } else if (strcmp(operation, "reorder") == 0) { test_reorder(schunk_sframe, schunk_cframe); } } blosc2_remove_urlpath(schunk_sframe->storage->urlpath); blosc2_remove_urlpath(schunk_cframe->storage->urlpath); blosc2_schunk_free(schunk_sframe); blosc2_schunk_free(schunk_cframe); /* Destroy the Blosc environment */ blosc2_destroy(); } int main(int argc, char* argv[]) { char* operation = NULL; if (argc >= 6) { printf("Usage: ./sframe_bench [nchunks] [insert | update | reorder] [num operations] [io_file | io_mmap]\n"); exit(1); } else if (argc >= 2) { nchunks = (int)strtol(argv[1], NULL, 10); } if (argc >= 3) { operation = argv[2]; } if (argc >= 4) { iterations = (int)strtol(argv[3], NULL, 10); } if (argc == 5) { if (strcmp(argv[4], "io_file") == 0) { io_type = BLOSC2_IO_FILESYSTEM; } else if (strcmp(argv[4], "io_mmap") == 0) { io_type = BLOSC2_IO_FILESYSTEM_MMAP; } else { printf("Invalid io type. Use io_file or io_mmap\n"); exit(1); } } test_create_sframe_frame(operation); return 0; } c-blosc2-3.1.5/bench/sum_openmp.c000066400000000000000000000163111521743436100165410ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program showing how to operate with compressed buffers. To compile this program for synthetic data (default): $ gcc -fopenmp -O3 sum_openmp.c -o sum_openmp -lblosc2 To run: $ OMP_PROC_BIND=spread OMP_NUM_THREADS=8 ./sum_openmp Blosc version info: 2.0.0a6.dev ($Date:: 2018-05-18 #$) Sum for uncompressed data: 199950000000 Sum time for uncompressed data: 0.0288 s, 26459.3 MB/s Compression ratio: 762.9 MB -> 14.0 MB (54.6x) Compression time: 0.288 s, 2653.5 MB/s Sum for *compressed* data: 199950000000 Sum time for *compressed* data: 0.0188 s, 40653.7 MB/s To use real (rainfall) data: $ gcc -DRAINFALL -fopenmp -Ofast sum_openmp.c -o sum_openmp And running it: $ OMP_PROC_BIND=spread OMP_NUM_THREADS=8 ./sum_openmp Blosc version info: 2.0.0a6.dev ($Date:: 2018-05-18 #$) Sum for uncompressed data: 29741012 Sum time for uncompressed data: 0.0149 s, 25627.4 MB/s Compression ratio: 381.5 MB -> 71.3 MB (5.3x) Compression time: 1.53 s, 249.1 MB/s Sum for *compressed* data: 29741012 Sum time for *compressed* data: 0.0247 s, 15467.5 MB/s */ #include #include #include #include #include #include #include "blosc2.h" #define KB 1024. #define MB (1024*KB) #define GB (1024*MB) #define N (100 * 1000 * 1000) #define CHUNKSIZE (16 * 1000) #define NCHUNKS (N / CHUNKSIZE) #define NTHREADS 8 #define NITER 5 #ifdef RAINFALL #define SYNTHETIC false #else #define SYNTHETIC true #endif #if SYNTHETIC == true #define DTYPE int64_t #define CLEVEL 3 #define CODEC BLOSC_BLOSCLZ #else #define DTYPE float #define CLEVEL 1 #define CODEC BLOSC_LZ4 #endif int main(void) { blosc2_init(); static DTYPE udata[N]; DTYPE chunk_buf[CHUNKSIZE]; int32_t isize = CHUNKSIZE * sizeof(DTYPE); DTYPE sum, compressed_sum; int64_t nbytes, cbytes; blosc2_schunk* schunk; int i, j, nchunk; blosc_timestamp_t last, current; double ttotal, itotal; char* envvar = NULL; printf("Blosc version info: %s (%s)\n", BLOSC2_VERSION_STRING, BLOSC2_VERSION_DATE); // Fill the buffer for a chunk if (SYNTHETIC) { for (j = 0; j < CHUNKSIZE; j++) { chunk_buf[j] = j; } } else { struct stat info; const char *filegrid = "rainfall-grid-150x150.bin"; if (stat(filegrid, &info) != 0) { printf("Grid file %s not found!", filegrid); exit(1); } char *cdata = malloc(info.st_size); FILE *f = fopen(filegrid, "rb"); size_t blocks_read = fread(cdata, info.st_size, 1, f); if (blocks_read != 1) { printf("Error reading file!\n"); exit(1); } fclose(f); int dsize = blosc1_getitem(cdata, 0, CHUNKSIZE, chunk_buf); if (dsize < 0) { printf("blosc1_getitem() error. Error code: %d\n. Probably reading too much data?", dsize); exit(1); } free(cdata); } // Fill the uncompressed dataset with data chunks for (i = 0; i < N / CHUNKSIZE; i++) { for (j = 0; j < CHUNKSIZE; j++) { udata[i * CHUNKSIZE + j] = chunk_buf[j]; } } // Reduce uncompressed dataset ttotal = 1e10; sum = 0; for (int n = 0; n < NITER; n++) { sum = 0; blosc_set_timestamp(&last); #pragma omp parallel for reduction (+:sum) for (i = 0; i < N; i++) { sum += udata[i]; } blosc_set_timestamp(¤t); itotal = blosc_elapsed_secs(last, current); if (itotal < ttotal) ttotal = itotal; } printf("Sum for uncompressed data: %10.0f\n", (double)sum); printf("Sum time for uncompressed data: %.3g s, %.1f MB/s\n", ttotal, (double)(isize * NCHUNKS) / (double)(ttotal * MB)); // Create a super-chunk container for the compressed container long codec = CODEC; envvar = getenv("SUM_COMPRESSOR"); if (envvar != NULL) { codec = blosc2_compname_to_compcode(envvar); if (codec < 0) { printf("Unknown compressor: %s\n", envvar); return 1; } } blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.compcode = (uint8_t)codec; long clevel = CLEVEL; envvar = getenv("SUM_CLEVEL"); if (envvar != NULL) { clevel = strtol(envvar, NULL, 10); } cparams.clevel = (uint8_t)clevel; cparams.typesize = sizeof(DTYPE); cparams.nthreads = 1; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; dparams.nthreads = 1; blosc_set_timestamp(&last); blosc2_storage storage = {.cparams=&cparams, .dparams=&dparams}; schunk = blosc2_schunk_new(&storage); for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { for (i = 0; i < CHUNKSIZE; i++) { chunk_buf[i] = udata[i + nchunk * CHUNKSIZE]; } blosc2_schunk_append_buffer(schunk, chunk_buf, isize); } blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); nbytes = schunk->nbytes; cbytes = schunk->cbytes; printf("Compression ratio: %.1f MB -> %.1f MB (%.1fx)\n", (double)nbytes / MB, (double)cbytes / MB, (1. * (double)nbytes) / (double)cbytes); printf("Compression time: %.3g s, %.1f MB/s\n", ttotal, (double)nbytes / (ttotal * MB)); int nthreads = NTHREADS; envvar = getenv("OMP_NUM_THREADS"); if (envvar != NULL) { long value; value = strtol(envvar, NULL, 10); if ((value != EINVAL) && (value >= 0)) { nthreads = (int)value; } } // Build buffers and contexts for computations int nchunks_thread = NCHUNKS / nthreads; int remaining_chunks = NCHUNKS - nchunks_thread * nthreads; blosc2_context **dctx = malloc(nthreads * sizeof(void*)); DTYPE** chunk = malloc(nthreads * sizeof(void*)); for (j = 0; j < nthreads; j++) { chunk[j] = malloc(CHUNKSIZE * sizeof(DTYPE)); } // Reduce uncompressed dataset blosc_set_timestamp(&last); ttotal = 1e10; compressed_sum = 0; for (int n = 0; n < NITER; n++) { compressed_sum = 0; #pragma omp parallel for private(nchunk) reduction (+:compressed_sum) for (j = 0; j < nthreads; j++) { dctx[j] = blosc2_create_dctx(dparams); for (nchunk = 0; nchunk < nchunks_thread; nchunk++) { blosc2_decompress_ctx(dctx[j], schunk->data[j * nchunks_thread + nchunk], INT32_MAX, (void*)(chunk[j]), isize); for (i = 0; i < CHUNKSIZE; i++) { compressed_sum += chunk[j][i]; //compressed_sum += i + (j * nchunks_thread + nchunk) * CHUNKSIZE; } } } for (nchunk = NCHUNKS - remaining_chunks; nchunk < NCHUNKS; nchunk++) { blosc2_decompress_ctx(dctx[0], schunk->data[nchunk], INT32_MAX, (void*)(chunk[0]), isize); for (i = 0; i < CHUNKSIZE; i++) { compressed_sum += chunk[0][i]; //compressed_sum += i + nchunk * CHUNKSIZE; } } blosc_set_timestamp(¤t); itotal = blosc_elapsed_secs(last, current); if (itotal < ttotal) ttotal = itotal; } printf("Sum for *compressed* data: %10.0f\n", (double)compressed_sum); printf("Sum time for *compressed* data: %.3g s, %.1f MB/s\n", ttotal, (double)nbytes / (ttotal * MB)); //printf("sum, csum: %f, %f\n", sum, compressed_sum); if (SYNTHETIC) { // difficult to fulfill for single precision assert(sum == compressed_sum); } /* Free resources */ blosc2_schunk_free(schunk); blosc2_destroy(); return 0; } c-blosc2-3.1.5/bench/trunc_prec_schunk.c000066400000000000000000000104061521743436100200750ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Benchmark showing Blosc TRUNC_PREC filter from C code. To compile this program: $ gcc -O3 trunc_prec_schunk.c -o trunc_prec_schunk -lblosc2 */ #include #include #include #include #include "blosc2.h" #define KB 1024 #define MB (1024*KB) #define GB (1024*MB) #define NCHUNKS 200 #define CHUNKSIZE (500 * 1000) #define NTHREADS 4 void fill_buffer(double *buffer, int nchunk) { double incx = 10. / (NCHUNKS * CHUNKSIZE); for (int i = 0; i < CHUNKSIZE; i++) { double x = incx * (nchunk * CHUNKSIZE + i); buffer[i] = (x - .25) * (x - 4.45) * (x - 8.95); //buffer[i] = x; } } int main(void) { blosc2_schunk *schunk; int32_t isize = CHUNKSIZE * sizeof(double); int dsize; int64_t nbytes, cbytes; int nchunk; int64_t nchunks = 0; blosc_timestamp_t last, current; double totaltime; float totalsize = (float)(isize * NCHUNKS); double *data_buffer = malloc(CHUNKSIZE * sizeof(double)); double *rec_buffer = malloc(CHUNKSIZE * sizeof(double)); printf("Blosc version info: %s (%s)\n", BLOSC2_VERSION_STRING, BLOSC2_VERSION_DATE); /* Initialize the Blosc compressor */ blosc2_init(); /* Create a super-chunk container */ blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.filters[0] = BLOSC_TRUNC_PREC; cparams.filters_meta[0] = 23; // treat doubles as floats cparams.typesize = sizeof(double); // DELTA makes compression ratio quite worse in this case //cparams.filters[1] = BLOSC_DELTA; // BLOSC_BITSHUFFLE is not compressing better and it quite slower here //cparams.filters[BLOSC2_MAX_FILTERS - 1] = BLOSC_BITSHUFFLE; // Good codec params for this dataset cparams.compcode = BLOSC_BLOSCLZ; cparams.clevel = 9; cparams.nthreads = NTHREADS; blosc2_storage storage = {.cparams=&cparams, .contiguous=true}; schunk = blosc2_schunk_new(&storage); /* Append the chunks */ blosc_set_timestamp(&last); for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { fill_buffer(data_buffer, nchunk); nchunks = blosc2_schunk_append_buffer(schunk, data_buffer, isize); } blosc_set_timestamp(¤t); totaltime = blosc_elapsed_secs(last, current); printf("[Compr] Elapsed time:\t %6.3f s." " Processed data: %.3f GB (%.3f GB/s)\n", totaltime, totalsize / GB, totalsize / (GB * totaltime)); /* Gather some info */ nbytes = schunk->nbytes; cbytes = schunk->cbytes; printf("Compression super-chunk: %ld -> %ld (%.1fx)\n", (long)nbytes, (long)cbytes, (1. * (double)nbytes) / (double)cbytes); /* Retrieve and decompress the chunks */ blosc_set_timestamp(&last); for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { dsize = blosc2_schunk_decompress_chunk(schunk, nchunk, rec_buffer, isize); if (dsize < 0) { printf("Decompression error. Error code: %d\n", dsize); return dsize; } assert (dsize == (int)isize); } blosc_set_timestamp(¤t); totaltime = blosc_elapsed_secs(last, current); totalsize = (float)(isize * nchunks); printf("[Decompr] Elapsed time:\t %6.3f s." " Processed data: %.3f GB (%.3f GB/s)\n", totaltime, totalsize / GB, totalsize / (GB * totaltime)); /* Check that all the values are in the precision range */ blosc_set_timestamp(&last); for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { dsize = blosc2_schunk_decompress_chunk(schunk, nchunk, (void *) rec_buffer, isize); if (dsize < 0) { printf("Decompression error. Error code: %d\n", dsize); return dsize; } assert (dsize == (int)isize); fill_buffer(data_buffer, nchunk); for (int i = 0; i < CHUNKSIZE; i++) { if (fabs(data_buffer[i] - rec_buffer[i]) > 1e-5) { printf("Value not in tolerance margin: "); printf("%g - %g: %g, (nchunk: %d, nelem: %d)\n", data_buffer[i], rec_buffer[i], (data_buffer[i] - rec_buffer[i]), nchunk, i); return -1; } } } printf("All data did a good roundtrip!\n"); /* Free resources */ free(data_buffer); free(rec_buffer); /* Destroy the super-chunk */ blosc2_schunk_free(schunk); /* Destroy the Blosc environment */ blosc2_destroy(); return 0; } c-blosc2-3.1.5/bench/zero_runlen.c000066400000000000000000000172221521743436100167230ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Benchmark showing Blosc zero detection capabilities via run-length. */ #include #include #include #include #include #include "blosc2.h" #define KB 1024 #define MB (1024*KB) #define GB (1024*MB) #define NCHUNKS (2000) #define CHUNKSIZE (500 * 1000) // > NCHUNKS for the bench purposes #define NTHREADS 8 enum { ZERO_DETECTION = 0, CHECK_ZEROS = 1, CHECK_NANS = 2, CHECK_VALUES = 3, CHECK_UNINIT = 4, }; #define REPEATED_VALUE 1 int check_special_values(int svalue) { blosc2_schunk *schunk; int32_t isize = CHUNKSIZE * sizeof(int32_t); int32_t osize = CHUNKSIZE * sizeof(int32_t) + BLOSC2_MAX_OVERHEAD; int dsize, csize; int64_t nbytes, frame_len; int nchunk; int64_t nchunks = 0; int rc; int32_t value = REPEATED_VALUE; float fvalue; blosc_timestamp_t last, current; double totaltime; double totalsize = (double)isize * NCHUNKS; int32_t *data_buffer = malloc(CHUNKSIZE * sizeof(int32_t)); int32_t *rec_buffer = malloc(CHUNKSIZE * sizeof(int32_t)); /* Initialize the Blosc compressor */ blosc2_init(); /* Create a super-chunk container */ blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = sizeof(int32_t); cparams.compcode = BLOSC_BLOSCLZ; cparams.clevel = 9; cparams.nthreads = NTHREADS; blosc2_storage storage = {.cparams=&cparams, .contiguous=false}; schunk = blosc2_schunk_new(&storage); void* chunk = malloc(BLOSC_EXTENDED_HEADER_LENGTH + isize); // Cache the special chunks switch (svalue) { case ZERO_DETECTION: memset(data_buffer, 0, isize); csize = blosc2_compress(5, 1, sizeof(int32_t), data_buffer, isize, chunk, osize); break; case CHECK_ZEROS: csize = blosc2_chunk_zeros(cparams, isize, chunk, BLOSC_EXTENDED_HEADER_LENGTH); break; case CHECK_UNINIT: csize = blosc2_chunk_uninit(cparams, isize, chunk, BLOSC_EXTENDED_HEADER_LENGTH); break; case CHECK_NANS: csize = blosc2_chunk_nans(cparams, isize, chunk, BLOSC_EXTENDED_HEADER_LENGTH); break; case CHECK_VALUES: csize = blosc2_chunk_repeatval(cparams, isize, chunk, BLOSC_EXTENDED_HEADER_LENGTH + sizeof(int32_t), &value); break; default: printf("Unknown case\n"); exit(1); } if (csize < 0) { printf("Error creating chunk: %d\n", csize); exit(1); } /* Append the chunks */ blosc_set_timestamp(&last); for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { nchunks = blosc2_schunk_append_chunk(schunk, chunk, true); if (nchunks < 0) { printf("Error appending chunk: %" PRId64 "\n", nchunks); exit(1); } } blosc_set_timestamp(¤t); free(chunk); totaltime = blosc_elapsed_secs(last, current); printf("\n[Compr] Elapsed time:\t %6.3f s." " Processed data: %.3f GB (%.3f GB/s)\n", totaltime, totalsize / GB, totalsize / (GB * totaltime)); /* Gather some info */ nbytes = schunk->nbytes; frame_len = blosc2_schunk_frame_len(schunk); printf("Compression super-chunk: %ld -> %ld (%.1fx)\n", (long)nbytes, (long)frame_len, (1. * (double)nbytes) / (double)frame_len); /* Retrieve and decompress the chunks */ blosc_set_timestamp(&last); for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { dsize = blosc2_schunk_decompress_chunk(schunk, nchunk, rec_buffer, isize); if (dsize < 0) { printf("Decompression error. Error code: %d\n", dsize); exit(dsize); } assert (dsize == (int)isize); } blosc_set_timestamp(¤t); totaltime = blosc_elapsed_secs(last, current); totalsize = (double)(isize) * (double)nchunks; printf("[Decompr] Elapsed time:\t %6.3f s." " Processed data: %.3f GB (%.3f GB/s)\n", totaltime, totalsize / GB, totalsize / (GB * totaltime)); /* Exercise the getitem */ blosc_set_timestamp(&last); for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { bool needs_free; uint8_t* chunk_; csize = blosc2_schunk_get_chunk(schunk, nchunk, &chunk_, &needs_free); if (csize < 0) { printf("blosc2_schunk_get_chunk error. Error code: %d\n", dsize); return csize; } switch (svalue) { case CHECK_VALUES: rc = blosc1_getitem(chunk_, nchunk, 1, &value); if (rc < 0) { printf("Error in getitem of a special value\n"); return rc; } if (value != REPEATED_VALUE) { printf("Wrong value!"); exit(1); } break; case CHECK_NANS: rc = blosc1_getitem(chunk_, nchunk, 1, &fvalue); if (rc < 0) { printf("Error in getitem of a special value\n"); return rc; } if (!isnan(fvalue)) { printf("Wrong value!"); exit(1); } break; case CHECK_ZEROS: rc = blosc1_getitem(chunk_, nchunk, 1, &value); if (rc < 0) { printf("Error in getitem of zeros value\n"); return rc; } if (value != 0) { printf("Wrong value!"); exit(1); } break; default: // It can only be non-initialized rc = blosc1_getitem(chunk_, nchunk, 1, &value); if (rc < 0) { printf("Error in getitem of an non-initialized value\n"); return rc; } } if (needs_free) { free(chunk_); } } blosc_set_timestamp(¤t); totaltime = blosc_elapsed_secs(last, current); printf("[getitem] Elapsed time:\t %6.3f s.\n", totaltime); // /* Check that all the values have a good roundtrip */ // blosc_set_timestamp(&last); // for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { // dsize = blosc2_schunk_decompress_chunk(schunk, nchunk, (void *) rec_buffer, isize); // if (dsize < 0) { // printf("Decompression error. Error code: %d\n", dsize); // return dsize; // } // assert (dsize == (int)isize); // if (CHECK_VALUE) { // int32_t* buffer = (int32_t*)rec_buffer; // for (int i = 0; i < CHUNKSIZE; i++) { // if (buffer[i] != REPEATED_VALUE) { // printf("Value is not correct in chunk %d, position: %d\n", nchunk, i); // return -1; // } // } // } // else if (CHECK_NAN) { // float* buffer = (float*)rec_buffer; // for (int i = 0; i < CHUNKSIZE; i++) { // if (!isnan(buffer[i])) { // printf("Value is not correct in chunk %d, position: %d\n", nchunk, i); // return -1; // } // } // } // else { // int32_t* buffer = (int32_t*)rec_buffer; // for (int i = 0; i < CHUNKSIZE; i++) { // if (buffer[i] != 0) { // printf("Value is not correct in chunk %d, position: %d\n", nchunk, i); // return -1; // } // } // } // } // printf("All data did a good roundtrip!\n"); /* Free resources */ free(data_buffer); free(rec_buffer); /* Destroy the super-chunk */ blosc2_schunk_free(schunk); /* Destroy the Blosc environment */ blosc2_destroy(); return 0; } int main(void) { int rc; printf("*** Testing special zeros..."); rc = check_special_values(CHECK_ZEROS); if (rc < 0) { return rc; } printf("*** Testing NaNs..."); rc = check_special_values(CHECK_NANS); if (rc < 0) { return rc; } printf("*** Testing repeated values..."); rc = check_special_values(CHECK_VALUES); if (rc < 0) { return rc; } printf("*** Testing non-initialized values..."); rc = check_special_values(CHECK_UNINIT); if (rc < 0) { return rc; } printf("Testing zero detection..."); rc = check_special_values(ZERO_DETECTION); if (rc < 0) { return rc; } } c-blosc2-3.1.5/blosc/000077500000000000000000000000001521743436100142345ustar00rootroot00000000000000c-blosc2-3.1.5/blosc/CMakeLists.txt000066400000000000000000000373541521743436100170100ustar00rootroot00000000000000# Blosc - Blocked Shuffling and Compression Library # # Copyright (c) 2021 Blosc Development Team # https://blosc.org # License: BSD 3-Clause (see LICENSE.txt) # # See LICENSE.txt for details about copyright and rights to use. # A simple way to detect that we are using CMAKE add_definitions(-DUSING_CMAKE) set(version_string ${BLOSC2_VERSION_MAJOR}.${BLOSC2_VERSION_MINOR}.${BLOSC2_VERSION_PATCH}) # targets if(BUILD_SHARED) add_library(blosc2_shared SHARED) # ALIAS for superbuilds that use Blosc2 as sub-project # must be the same as the NAMESPACE in Blosc2Targets add_library(Blosc2::blosc2_shared ALIAS blosc2_shared) set_target_properties(blosc2_shared PROPERTIES OUTPUT_NAME blosc2 # Hide symbols by default unless they're specifically exported. # This makes it easier to keep the set of exported symbols the # same across all compilers/platforms. C_VISIBILITY_PRESET hidden ) if(MSVC OR MINGW) set_target_properties(blosc2_shared PROPERTIES PREFIX lib) endif() set_target_properties(blosc2_shared PROPERTIES VERSION ${version_string} SOVERSION 8 # Change this when an ABI change happens ) target_compile_definitions(blosc2_shared PRIVATE BLOSC_SHARED_LIBRARY) target_include_directories(blosc2_shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(blosc2_shared PUBLIC $ $) endif() if(BUILD_STATIC) add_compile_definitions(BUILD_STATIC) add_library(blosc2_static STATIC) # ALIAS for superbuilds that use Blosc2 as sub-project # must be the same as the NAMESPACE in Blosc2Targets add_library(Blosc2::blosc2_static ALIAS blosc2_static) set_target_properties(blosc2_static PROPERTIES OUTPUT_NAME blosc2 POSITION_INDEPENDENT_CODE ON # Hide symbols by default unless they're specifically exported. # This makes it easier to keep the set of exported symbols the # same across all compilers/platforms. C_VISIBILITY_PRESET hidden ) if(MSVC OR MINGW) set_target_properties(blosc2_static PROPERTIES PREFIX lib) endif() target_include_directories(blosc2_static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(blosc2_static PUBLIC $ $) endif() # Add unified target add_library(Blosc2::blosc2 ALIAS ${UNIFIED_TARGET_ALIAS}) # When the option has been selected to compile the test suite, # compile an additional version of blosc2_static which exports # some normally-hidden symbols (to facilitate unit testing). if(BUILD_TESTS) add_library(blosc_testing STATIC) set_target_properties(blosc_testing PROPERTIES OUTPUT_NAME blosc_testing) if(MSVC OR MINGW) set_target_properties(blosc_testing PROPERTIES PREFIX lib) endif() target_compile_definitions(blosc_testing PUBLIC BLOSC_TESTING BLOSC_SHARED_LIBRARY # for EXPORT macro ) target_include_directories(blosc_testing PUBLIC $ $) endif() set(INTERNAL_LIBS ${PROJECT_SOURCE_DIR}/internal-complibs) # Threads if(HAVE_THREADS) if(CMAKE_VERSION VERSION_LESS 3.1) set(LIBS ${LIBS} ${CMAKE_THREAD_LIBS_INIT}) else() set(LIBS ${LIBS} Threads::Threads) endif() else() message(FATAL_ERROR "Threads required but not found.") endif() if(WIN32) list(APPEND SOURCES blosc/win32/threading.c) endif() # dlopen/dlclose if(NOT WIN32) set(LIBS ${LIBS} ${CMAKE_DL_LIBS}) endif() # link dependencies # "link" dependent targets via target_link_libraries (preferred) and # manually add includes / libs for others if(LZ4_FOUND) if(TARGET LZ4::lz4) list(APPEND LIBS LZ4::lz4) elseif(TARGET lz4_static) list(APPEND LIBS lz4_static) elseif(TARGET lz4) list(APPEND LIBS lz4) else() list(APPEND LIBS ${LZ4_LIBRARY}) list(APPEND BLOSC_INCLUDE_DIRS ${LZ4_INCLUDE_DIRS}) endif() else() message(FATAL_ERROR "LZ4 support enabled, but neither external lz4 nor FetchContent lz4 is available.") endif() if(NOT DEACTIVATE_ZLIB) if(ZLIB_NG_FOUND) if(TARGET zlib-ng::zlibstatic) list(APPEND LIBS zlib-ng::zlibstatic) elseif(TARGET zlib-ng::zlib) list(APPEND LIBS zlib-ng::zlib) else() list(APPEND LIBS ${ZLIB_NG_LIBRARIES}) list(APPEND BLOSC_INCLUDE_DIRS ${ZLIB_NG_INCLUDE_DIR}) endif() elseif(ZLIB_FOUND) list(APPEND LIBS ZLIB::ZLIB) else() message(FATAL_ERROR "ZLIB support enabled, but neither external zlib nor FetchContent zlib-ng is available.") endif() endif() if(NOT DEACTIVATE_ZSTD) if(ZSTD_FOUND) if(TARGET zstd::libzstd_shared) list(APPEND LIBS zstd::libzstd_shared) elseif(TARGET zstd::libzstd_static) list(APPEND LIBS zstd::libzstd_static) elseif(TARGET zstd::libzstd) list(APPEND LIBS zstd::libzstd) elseif(TARGET libzstd_static) list(APPEND LIBS libzstd_static) elseif(TARGET libzstd_shared) list(APPEND LIBS libzstd_shared) else() list(APPEND LIBS ${ZSTD_LIBRARY}) list(APPEND BLOSC_INCLUDE_DIRS ${ZSTD_INCLUDE_DIR}) endif() else() message(FATAL_ERROR "ZSTD support enabled, but neither external zstd nor FetchContent zstd is available.") endif() endif() if(BUILD_PLUGINS AND TARGET zfp) list(APPEND LIBS zfp) endif() if(UNIX AND NOT APPLE) set(LIBS ${LIBS} "rt") set(LIBS ${LIBS} "m") # set(LIBS ${LIBS} "profiler") endif() # Blosc2 library source files list(APPEND SOURCES blosc/blosc2.c blosc/blosclz.c blosc/fastcopy.c blosc/fastcopy.h blosc/schunk.c blosc/frame.c blosc/stune.c blosc/stune.h blosc/context.h blosc/delta.c blosc/delta.h blosc/shuffle-generic.c blosc/bitshuffle-generic.c blosc/trunc-prec.c blosc/trunc-prec.h blosc/timestamp.c blosc/sframe.c blosc/directories.c blosc/blosc2-stdio.c blosc/b2nd.c blosc/b2nd_utils.c ) if(NOT CMAKE_SYSTEM_PROCESSOR STREQUAL arm64) if(COMPILER_SUPPORT_SSE2) message(STATUS "Adding run-time support for SSE2") list(APPEND SOURCES blosc/shuffle-sse2.c blosc/bitshuffle-sse2.c) endif() if(COMPILER_SUPPORT_AVX2) message(STATUS "Adding run-time support for AVX2") list(APPEND SOURCES blosc/shuffle-avx2.c blosc/bitshuffle-avx2.c) endif() if(COMPILER_SUPPORT_AVX512) message(STATUS "Adding run-time support for AVX512") list(APPEND SOURCES blosc/bitshuffle-avx512.c) endif() endif() if(COMPILER_SUPPORT_NEON) message(STATUS "Adding run-time support for NEON") # bitshuffle-neon.c does not offer better speed than generic on arm64 (Mac M1). # Besides, it does not compile on raspberry pi (armv7l), so disable it. list(APPEND SOURCES blosc/shuffle-neon.c) # blosc/bitshuffle-neon.c) endif() if(COMPILER_SUPPORT_ALTIVEC) message(STATUS "Adding run-time support for ALTIVEC") list(APPEND SOURCES blosc/shuffle-altivec.c blosc/bitshuffle-altivec.c) endif() list(APPEND SOURCES blosc/shuffle.c) # Based on the target architecture and hardware features supported # by the C compiler, set hardware architecture optimization flags # for specific shuffle implementations. if(COMPILER_SUPPORT_SSE2) include(CheckCCompilerFlag) # Probe for SSSE3 flags for various compilers check_c_compiler_flag("-mssse3" COMPILER_HAS_mssse3) check_c_compiler_flag("/arch:SSSE3" COMPILER_HAS_arch_SSSE3) if(COMPILER_HAS_mssse3) set(SSSE3_FLAG "-mssse3") elseif(COMPILER_HAS_arch_SSSE3) set(SSSE3_FLAG "/arch:SSSE3") else() set(SSSE3_FLAG "") endif() if(MSVC) # MSVC targets SSE2 by default on 64-bit configurations, but not 32-bit configurations. if(${CMAKE_SIZEOF_VOID_P} EQUAL 4) set_source_files_properties( shuffle-sse2.c bitshuffle-sse2.c blosclz.c fastcopy.c PROPERTIES COMPILE_OPTIONS "/arch:SSE2") endif() else() set_source_files_properties( shuffle-sse2.c bitshuffle-sse2.c blosclz.c fastcopy.c PROPERTIES COMPILE_OPTIONS -msse2) endif() if(SSSE3_FLAG) set_source_files_properties( ${PROJECT_SOURCE_DIR}/plugins/filters/bytedelta/bytedelta.c PROPERTIES COMPILE_OPTIONS "${SSSE3_FLAG}" COMPILE_DEFINITIONS "HAVE_SSSE3=1" ) endif() # Define a symbol for the shuffle-dispatch implementation # so it knows SSE2 is supported even though that file is # compiled without SSE2 support (for portability). set_property( SOURCE shuffle.c APPEND PROPERTY COMPILE_DEFINITIONS SHUFFLE_SSE2_ENABLED) endif() if(COMPILER_SUPPORT_AVX2) if(MSVC) set_source_files_properties( shuffle-avx2.c bitshuffle-avx2.c PROPERTIES COMPILE_OPTIONS "/arch:AVX2") else() set_source_files_properties( shuffle-avx2.c bitshuffle-avx2.c PROPERTIES COMPILE_OPTIONS -mavx2) endif() # Define a symbol for the shuffle-dispatch implementation # so it knows AVX2 is supported even though that file is # compiled without AVX2 support (for portability). set_property( SOURCE shuffle.c APPEND PROPERTY COMPILE_DEFINITIONS SHUFFLE_AVX2_ENABLED) endif() if(COMPILER_SUPPORT_AVX512) if(MSVC) set_source_files_properties( bitshuffle-avx512.c PROPERTIES COMPILE_OPTIONS "/arch:AVX512") else() set_source_files_properties( bitshuffle-avx512.c PROPERTIES COMPILE_OPTIONS "-mavx512f;-mavx512bw") endif() # Define a symbol for the shuffle-dispatch implementation # so it knows AVX512 is supported even though that file is # compiled without AVX512 support (for portability). set_property( SOURCE shuffle.c APPEND PROPERTY COMPILE_DEFINITIONS SHUFFLE_AVX512_ENABLED) endif() if(COMPILER_SUPPORT_NEON) set_source_files_properties( shuffle-neon.c bitshuffle-neon.c PROPERTIES COMPILE_OPTIONS "-flax-vector-conversions") if(CMAKE_SYSTEM_PROCESSOR STREQUAL armv7l) # Only armv7l needs special -mfpu=neon flag; aarch64 doesn't. set_source_files_properties( shuffle-neon.c bitshuffle-neon.c PROPERTIES COMPILE_OPTIONS "-mfpu=neon;-flax-vector-conversions") endif() # Define a symbol for the shuffle-dispatch implementation # so it knows NEON is supported even though that file is # compiled without NEON support (for portability). set_property( SOURCE shuffle.c APPEND PROPERTY COMPILE_DEFINITIONS SHUFFLE_NEON_ENABLED) endif() if(COMPILER_SUPPORT_ALTIVEC) set_source_files_properties( shuffle-altivec.c bitshuffle-altivec.c PROPERTIES COMPILE_OPTIONS -DNO_WARN_X86_INTRINSICS) set_property( SOURCE shuffle.c APPEND PROPERTY COMPILE_OPTIONS -DNO_WARN_X86_INTRINSICS) # Define a symbol for the shuffle-dispatch implementation # so it knows ALTIVEC is supported even though that file is # compiled without ALTIVEC support (for portability). set_property( SOURCE shuffle.c APPEND PROPERTY COMPILE_DEFINITIONS SHUFFLE_ALTIVEC_ENABLED) endif() # add libraries for dependencies that are not CMake targets if(BUILD_SHARED) target_link_libraries(blosc2_shared PRIVATE ${LIBS}) target_include_directories(blosc2_shared PRIVATE ${BLOSC_INCLUDE_DIRS}) endif() if(BUILD_STATIC) target_link_libraries(blosc2_static PRIVATE ${LIBS}) target_include_directories(blosc2_static PRIVATE ${BLOSC_INCLUDE_DIRS}) endif() if(BUILD_TESTS) target_link_libraries(blosc_testing PRIVATE ${LIBS}) target_include_directories(blosc_testing PRIVATE ${BLOSC_INCLUDE_DIRS}) endif() # When using fetched dependencies, make the installed static library self-contained # by folding the private bundled dependency archives into libblosc2.a. This keeps # bundled dependencies out of the public install layout and lets consumers link the # wheel-provided static library without chasing private dependency archives. set(BLOSC_BUNDLED_STATIC_TARGETS) if(BUILD_STATIC) if(LZ4_FETCHED AND TARGET lz4_static) list(APPEND BLOSC_BUNDLED_STATIC_TARGETS lz4_static) endif() if(ZLIB_NG_FETCHED AND ZLIB_NG_INSTALL_TARGET AND TARGET ${ZLIB_NG_INSTALL_TARGET}) list(APPEND BLOSC_BUNDLED_STATIC_TARGETS ${ZLIB_NG_INSTALL_TARGET}) endif() if(ZSTD_FETCHED AND TARGET libzstd_static) list(APPEND BLOSC_BUNDLED_STATIC_TARGETS libzstd_static) endif() if(ZFP_FETCHED AND TARGET zfp) list(APPEND BLOSC_BUNDLED_STATIC_TARGETS zfp) endif() endif() if(BLOSC_BUNDLED_STATIC_TARGETS) set(BLOSC_STATIC_MERGE_INPUTS "$") foreach(BLOSC_BUNDLED_STATIC_TARGET IN LISTS BLOSC_BUNDLED_STATIC_TARGETS) list(APPEND BLOSC_STATIC_MERGE_INPUTS "$") endforeach() string(JOIN "|" BLOSC_STATIC_MERGE_INPUTS_ARG ${BLOSC_STATIC_MERGE_INPUTS}) set(BLOSC_STATIC_MERGE_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/merge_blosc2_static_$.cmake") file(GENERATE OUTPUT "${BLOSC_STATIC_MERGE_SCRIPT}" CONTENT "set(OUTPUT [=[$]=]) set(INPUTS [=[${BLOSC_STATIC_MERGE_INPUTS_ARG}]=]) set(AR [=[${CMAKE_AR}]=]) set(RANLIB [=[${CMAKE_RANLIB}]=]) set(SYSTEM_NAME [=[${CMAKE_SYSTEM_NAME}]=]) set(MSVC [=[$]=]) include([=[${PROJECT_SOURCE_DIR}/cmake/merge_static_libs.cmake]=]) ") set(BLOSC_CMAKE_COMMAND "${CMAKE_COMMAND}") if(WIN32) # Avoid nested cmd.exe quoting problems with full paths such as # "C:\Program Files\CMake\bin\cmake.exe" in Ninja post-build rules. set(BLOSC_CMAKE_COMMAND cmake) endif() add_custom_command(TARGET blosc2_static POST_BUILD COMMAND "${BLOSC_CMAKE_COMMAND}" -P "${BLOSC_STATIC_MERGE_SCRIPT}" COMMENT "Merging bundled dependency archives into libblosc2.a" VERBATIM) # The bundled dependency objects are now inside libblosc2.a, so do not make # installed CMake consumers depend on the private dependency targets. get_target_property(BLOSC_STATIC_INTERFACE_LINK_LIBRARIES blosc2_static INTERFACE_LINK_LIBRARIES) if(BLOSC_STATIC_INTERFACE_LINK_LIBRARIES) set(BLOSC_BUNDLED_STATIC_INTERFACE_TARGETS ${BLOSC_BUNDLED_STATIC_TARGETS}) if(LZ4_FETCHED) list(APPEND BLOSC_BUNDLED_STATIC_INTERFACE_TARGETS lz4_static LZ4::lz4) endif() if(ZLIB_NG_FETCHED) list(APPEND BLOSC_BUNDLED_STATIC_INTERFACE_TARGETS zlib-ng-static zlib-ng zlib zlibstatic zlib-ng::zlibstatic zlib-ng::zlib) endif() if(ZSTD_FETCHED) list(APPEND BLOSC_BUNDLED_STATIC_INTERFACE_TARGETS libzstd_static libzstd_shared zstd::libzstd_static zstd::libzstd_shared zstd::libzstd) endif() if(ZFP_FETCHED) list(APPEND BLOSC_BUNDLED_STATIC_INTERFACE_TARGETS zfp ZFP::zfp zfp::zfp ZFP::ZFP) endif() list(REMOVE_DUPLICATES BLOSC_BUNDLED_STATIC_INTERFACE_TARGETS) foreach(BLOSC_BUNDLED_STATIC_TARGET IN LISTS BLOSC_BUNDLED_STATIC_INTERFACE_TARGETS) list(REMOVE_ITEM BLOSC_STATIC_INTERFACE_LINK_LIBRARIES "$") endforeach() set_target_properties(blosc2_static PROPERTIES INTERFACE_LINK_LIBRARIES "${BLOSC_STATIC_INTERFACE_LINK_LIBRARIES}") endif() endif() # we use this variable in the CMake file one directory above ours set(SOURCES ${SOURCES} PARENT_SCOPE) c-blosc2-3.1.5/blosc/b2nd-private.h000066400000000000000000000024361521743436100167070ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #ifndef BLOSC_B2ND_PRIVATE_H #define BLOSC_B2ND_PRIVATE_H #include "b2nd.h" #include #include #include /********************************************************************* Functions meant to be used internally. *********************************************************************/ /** * @brief Get the chunk indexes needed to get the slice. * * @param array The b2nd array. * @param start The coordinates where the slice will begin. * @param stop The coordinates where the slice will end. * @param chunks_idx The pointer to the buffer where the indexes of the chunks will be written. * * @return The number of chunks needed to get the slice. If some problem is * detected, a negative code is returned instead. */ int64_t b2nd_get_slice_nchunks(b2nd_array_t *array, const int64_t *start, const int64_t *stop, int64_t **chunks_idx); #endif /* BLOSC_B2ND_PRIVATE_H */ c-blosc2-3.1.5/blosc/b2nd.c000066400000000000000000002677001521743436100152410ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #include "b2nd.h" #include "context.h" #include "blosc2/blosc2-common.h" #include "blosc2.h" #include #include #include #include #include static bool b2nd_mul_overflow_size_t(size_t a, size_t b, size_t *out) { if (a != 0 && b > SIZE_MAX / a) { return true; } if (out != NULL) { *out = a * b; } return false; } int b2nd_serialize_meta(int8_t ndim, const int64_t *shape, const int32_t *chunkshape, const int32_t *blockshape, const char *dtype, int8_t dtype_format, uint8_t **smeta) { if (dtype == NULL) { dtype = B2ND_DEFAULT_DTYPE; } // dtype checks if (dtype_format < 0) { BLOSC_TRACE_ERROR("dtype_format cannot be negative"); BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } size_t dtype_len0 = strlen(dtype); if (dtype_len0 > INT32_MAX) { BLOSC_TRACE_ERROR("dtype is too large (len > %d)", INT32_MAX); BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } const int32_t dtype_len = (int32_t) dtype_len0; // Allocate space for b2nd metalayer int32_t max_smeta_len = (int32_t) (1 + 1 + 1 + (1 + ndim * (1 + sizeof(int64_t))) + (1 + ndim * (1 + sizeof(int32_t))) + (1 + ndim * (1 + sizeof(int32_t))) + 1 + 1 + sizeof(int32_t) + dtype_len); *smeta = malloc((size_t) max_smeta_len); BLOSC_ERROR_NULL(*smeta, BLOSC2_ERROR_MEMORY_ALLOC); uint8_t *pmeta = *smeta; // Build an array with 7 entries (version, ndim, shape, chunkshape, blockshape, dtype_format, dtype) *pmeta++ = 0x90 + 7; // version entry *pmeta++ = B2ND_METALAYER_VERSION; // positive fixnum (7-bit positive integer) // ndim entry *pmeta++ = (uint8_t) ndim; // positive fixnum (7-bit positive integer) // shape entry *pmeta++ = (uint8_t) (0x90) + ndim; // fix array with ndim elements for (uint8_t i = 0; i < ndim; i++) { *pmeta++ = 0xd3; // int64 swap_store(pmeta, shape + i, sizeof(int64_t)); pmeta += sizeof(int64_t); } // chunkshape entry *pmeta++ = (uint8_t) (0x90) + ndim; // fix array with ndim elements for (uint8_t i = 0; i < ndim; i++) { *pmeta++ = 0xd2; // int32 swap_store(pmeta, chunkshape + i, sizeof(int32_t)); pmeta += sizeof(int32_t); } // blockshape entry *pmeta++ = (uint8_t) (0x90) + ndim; // fix array with ndim elements for (uint8_t i = 0; i < ndim; i++) { *pmeta++ = 0xd2; // int32 swap_store(pmeta, blockshape + i, sizeof(int32_t)); pmeta += sizeof(int32_t); } // dtype entry *pmeta++ = dtype_format; // positive fixint (7-bit positive integer) *pmeta++ = (uint8_t) (0xdb); // str with up to 2^31 elements swap_store(pmeta, &dtype_len, sizeof(int32_t)); pmeta += sizeof(int32_t); memcpy(pmeta, dtype, dtype_len); pmeta += dtype_len; int32_t slen = (int32_t) (pmeta - *smeta); if (max_smeta_len != slen) { BLOSC_TRACE_ERROR("meta length is inconsistent!"); return BLOSC2_ERROR_FAILURE; } return (int)slen; } int b2nd_deserialize_meta(const uint8_t *smeta, int32_t smeta_len, int8_t *ndim, int64_t *shape, int32_t *chunkshape, int32_t *blockshape, char **dtype, int8_t *dtype_format) { return b2nd_deserialize_meta_inline(smeta, smeta_len, ndim, shape, chunkshape, blockshape, dtype, dtype_format); } static int validate_shape_chunkshape_blockshape(int8_t ndim, const int64_t *shape, const int32_t *chunkshape, const int32_t *blockshape) { for (int i = 0; i < ndim; ++i) { if (shape[i] < 0) { BLOSC_TRACE_ERROR("shape[%d] cannot be negative", i); return BLOSC2_ERROR_INVALID_PARAM; } if (chunkshape[i] < 0 || blockshape[i] < 0) { BLOSC_TRACE_ERROR("chunkshape[%d] and blockshape[%d] cannot be negative", i, i); return BLOSC2_ERROR_INVALID_PARAM; } bool chunkshape_is_zero = (chunkshape[i] == 0); bool blockshape_is_zero = (blockshape[i] == 0); // Keep compatibility with contexts that use chunkshape=0 (e.g. empty slices). // Reject only invalid tuples that can produce divide-by-zero when chunkshape is non-zero. if (blockshape_is_zero && !chunkshape_is_zero) { BLOSC_TRACE_ERROR("blockshape[%d] cannot be zero when chunkshape[%d] is non-zero", i, i); return BLOSC2_ERROR_INVALID_PARAM; } } return BLOSC2_ERROR_SUCCESS; } int update_shape(b2nd_array_t *array, int8_t ndim, const int64_t *shape, const int32_t *chunkshape, const int32_t *blockshape) { BLOSC_ERROR(validate_shape_chunkshape_blockshape(ndim, shape, chunkshape, blockshape)); array->ndim = ndim; array->nitems = 1; array->extnitems = 1; array->extchunknitems = 1; array->chunknitems = 1; array->blocknitems = 1; for (int i = 0; i < B2ND_MAX_DIM; ++i) { if (i < ndim) { array->shape[i] = shape[i]; array->chunkshape[i] = chunkshape[i]; array->blockshape[i] = blockshape[i]; if (array->chunkshape[i] != 0) { if (shape[i] % array->chunkshape[i] == 0) { array->extshape[i] = shape[i]; } else { array->extshape[i] = shape[i] + chunkshape[i] - shape[i] % chunkshape[i]; } if (chunkshape[i] % blockshape[i] == 0) { array->extchunkshape[i] = chunkshape[i]; } else { array->extchunkshape[i] = chunkshape[i] + blockshape[i] - chunkshape[i] % blockshape[i]; } } else { array->extchunkshape[i] = chunkshape[i]; array->extshape[i] = 0; } } else { array->blockshape[i] = 1; array->chunkshape[i] = 1; array->extshape[i] = 1; array->extchunkshape[i] = 1; array->shape[i] = 1; } array->nitems *= array->shape[i]; array->extnitems *= array->extshape[i]; array->extchunknitems *= array->extchunkshape[i]; array->chunknitems *= array->chunkshape[i]; array->blocknitems *= array->blockshape[i]; } // Compute strides if (ndim > 0) { array->item_array_strides[ndim - 1] = 1; array->item_extchunk_strides[ndim - 1] = 1; array->item_chunk_strides[ndim - 1] = 1; array->item_block_strides[ndim - 1] = 1; array->block_chunk_strides[ndim - 1] = 1; array->chunk_array_strides[ndim - 1] = 1; } for (int i = ndim - 2; i >= 0; --i) { // Treat (chunkshape[i+1] == 0) the same as (shape[i+1] == 0): the // dimension carries no actual chunking and any stride into it is moot. // validate_shape_chunkshape_blockshape allows the chunkshape==0 // combination (paired with blockshape==0) as a placeholder for empty // contexts (see example_empty_shape.c). Without this guard a crafted // b2nd metalayer with shape[i+1]>0 but chunkshape[i+1]==blockshape[i+1]==0 // -- accepted by the validator -- reaches the (extchunkshape / blockshape) // division below as 0/0, which is undefined behaviour and aborts the // process with SIGFPE on x86. Crafted cframes loaded via b2nd_from_schunk // / b2nd_open / b2nd_from_cframe would otherwise crash any consumer. if (shape[i + 1] != 0 && array->chunkshape[i + 1] != 0 && array->blockshape[i + 1] != 0) { array->item_array_strides[i] = array->item_array_strides[i + 1] * array->shape[i + 1]; array->item_extchunk_strides[i] = array->item_extchunk_strides[i + 1] * array->extchunkshape[i + 1]; array->item_chunk_strides[i] = array->item_chunk_strides[i + 1] * array->chunkshape[i + 1]; array->item_block_strides[i] = array->item_block_strides[i + 1] * array->blockshape[i + 1]; array->block_chunk_strides[i] = array->block_chunk_strides[i + 1] * (array->extchunkshape[i + 1] / array->blockshape[i + 1]); array->chunk_array_strides[i] = array->chunk_array_strides[i + 1] * (array->extshape[i + 1] * array->chunkshape[i + 1]); } else { array->item_array_strides[i] = 0; array->item_extchunk_strides[i] = 0; array->item_chunk_strides[i] = 0; array->item_block_strides[i] = 0; array->block_chunk_strides[i] = 0; array->chunk_array_strides[i] = 0; } } if (array->sc) { uint8_t *smeta = NULL; // Serialize the dimension info ... int32_t smeta_len = b2nd_serialize_meta(array->ndim, array->shape, array->chunkshape, array->blockshape, array->dtype, array->dtype_format, &smeta); if (smeta_len < 0) { BLOSC_TRACE_ERROR("Error during serializing dims info for Blosc2 NDim"); BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } // ... and update it in its metalayer if (blosc2_meta_exists(array->sc, "b2nd") < 0) { if (blosc2_meta_add(array->sc, "b2nd", smeta, smeta_len) < 0) { BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } } else { if (blosc2_meta_update(array->sc, "b2nd", smeta, smeta_len) < 0) { BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } } free(smeta); } return BLOSC2_ERROR_SUCCESS; } int array_without_schunk(b2nd_context_t *ctx, b2nd_array_t **array) { /* Create a b2nd_array_t buffer */ (*array) = (b2nd_array_t *) malloc(sizeof(b2nd_array_t)); BLOSC_ERROR_NULL(*array, BLOSC2_ERROR_MEMORY_ALLOC); (*array)->sc = NULL; (*array)->ndim = ctx->ndim; int64_t *shape = ctx->shape; int32_t *chunkshape = ctx->chunkshape; int32_t *blockshape = ctx->blockshape; BLOSC_ERROR(update_shape(*array, ctx->ndim, shape, chunkshape, blockshape)); if (ctx->dtype != NULL) { (*array)->dtype = malloc(strlen(ctx->dtype) + 1); strcpy((*array)->dtype, ctx->dtype); } else { (*array)->dtype = NULL; } (*array)->dtype_format = ctx->dtype_format; // The partition cache (empty initially) (*array)->chunk_cache.data = NULL; (*array)->chunk_cache.nchunk = -1; // means no valid cache yet return BLOSC2_ERROR_SUCCESS; } int array_new(b2nd_context_t *ctx, int special_value, b2nd_array_t **array) { BLOSC_ERROR(array_without_schunk(ctx, array)); blosc2_schunk *sc = blosc2_schunk_new(ctx->b2_storage); if (sc == NULL) { BLOSC_TRACE_ERROR("Pointer is NULL"); return BLOSC2_ERROR_FAILURE; } // Set the chunksize for the schunk, as it cannot be derived from storage if (sc->typesize <= 0) { BLOSC_TRACE_ERROR("Invalid chunk parameters"); blosc2_schunk_free(sc); free((*array)->dtype); free(*array); *array = NULL; return BLOSC2_ERROR_INVALID_PARAM; } if ((*array)->extchunknitems < 0) { BLOSC_TRACE_ERROR("Invalid chunk parameters"); blosc2_schunk_free(sc); free((*array)->dtype); free(*array); *array = NULL; return BLOSC2_ERROR_INVALID_PARAM; } if ((uint64_t)(*array)->extchunknitems > (uint64_t)SIZE_MAX) { BLOSC_TRACE_ERROR("extchunknitems too large"); blosc2_schunk_free(sc); free((*array)->dtype); free(*array); *array = NULL; return BLOSC2_ERROR_INVALID_PARAM; } size_t chunkbytes_size = 0; if (b2nd_mul_overflow_size_t((size_t)(*array)->extchunknitems, (size_t)sc->typesize, &chunkbytes_size)) { BLOSC_TRACE_ERROR("Chunksize overflows size limits"); blosc2_schunk_free(sc); free((*array)->dtype); free(*array); *array = NULL; return BLOSC2_ERROR_INVALID_PARAM; } if (chunkbytes_size > BLOSC2_MAX_BUFFERSIZE || chunkbytes_size > INT32_MAX) { BLOSC_TRACE_ERROR("Chunksize exceeds maximum of %d", BLOSC2_MAX_BUFFERSIZE); blosc2_schunk_free(sc); free((*array)->dtype); free(*array); *array = NULL; return BLOSC2_ERROR_MAX_BUFSIZE_EXCEEDED; } int32_t chunksize = (int32_t)chunkbytes_size; sc->chunksize = chunksize; // Serialize the dimension info if (sc->nmetalayers >= BLOSC2_MAX_METALAYERS) { BLOSC_TRACE_ERROR("the number of metalayers for this schunk has been exceeded"); blosc2_schunk_free(sc); free((*array)->dtype); free(*array); *array = NULL; return BLOSC2_ERROR_FAILURE; } uint8_t *smeta = NULL; int32_t smeta_len = b2nd_serialize_meta(ctx->ndim, (*array)->shape, (*array)->chunkshape, (*array)->blockshape, (*array)->dtype, (*array)->dtype_format, &smeta); if (smeta_len < 0) { BLOSC_TRACE_ERROR("error during serializing dims info for Blosc2 NDim"); blosc2_schunk_free(sc); free((*array)->dtype); free(*array); *array = NULL; return BLOSC2_ERROR_FAILURE; } // And store it in b2nd metalayer if (blosc2_meta_add(sc, "b2nd", smeta, smeta_len) < 0) { free(smeta); blosc2_schunk_free(sc); free((*array)->dtype); free(*array); *array = NULL; return BLOSC2_ERROR_FAILURE; } free(smeta); for (int i = 0; i < ctx->nmetalayers; ++i) { char *name = ctx->metalayers[i].name; uint8_t *data = ctx->metalayers[i].content; int32_t size = ctx->metalayers[i].content_len; if (blosc2_meta_add(sc, name, data, size) < 0) { blosc2_schunk_free(sc); free((*array)->dtype); free(*array); *array = NULL; return BLOSC2_ERROR_FAILURE; } } // Fill schunk with uninit values if ((*array)->nitems != 0) { int64_t nchunks = (*array)->extnitems / (*array)->chunknitems; int64_t nitems = nchunks * (*array)->extchunknitems; if (blosc2_schunk_fill_special(sc, nitems, special_value, chunksize) < 0) { blosc2_schunk_free(sc); free((*array)->dtype); free(*array); *array = NULL; BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } } (*array)->sc = sc; return BLOSC2_ERROR_SUCCESS; } int b2nd_uninit(b2nd_context_t *ctx, b2nd_array_t **array) { BLOSC_ERROR_NULL(ctx, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR(array_new(ctx, BLOSC2_SPECIAL_UNINIT, array)); return BLOSC2_ERROR_SUCCESS; } int b2nd_empty(b2nd_context_t *ctx, b2nd_array_t **array) { BLOSC_ERROR_NULL(ctx, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); // Fill with zeros to avoid variable cratios BLOSC_ERROR(array_new(ctx, BLOSC2_SPECIAL_ZERO, array)); return BLOSC2_ERROR_SUCCESS; } int b2nd_zeros(b2nd_context_t *ctx, b2nd_array_t **array) { BLOSC_ERROR_NULL(ctx, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR(array_new(ctx, BLOSC2_SPECIAL_ZERO, array)); return BLOSC2_ERROR_SUCCESS; } int b2nd_nans(b2nd_context_t *ctx, b2nd_array_t **array) { BLOSC_ERROR_NULL(ctx, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR(array_new(ctx, BLOSC2_SPECIAL_NAN, array)); const int32_t typesize = (*array)->sc->typesize; if (typesize != 4 && typesize != 8) { BLOSC_TRACE_ERROR("Unsupported typesize for NaN"); return BLOSC2_ERROR_DATA; } return BLOSC2_ERROR_SUCCESS; } int b2nd_full(b2nd_context_t *ctx, b2nd_array_t **array, const void *fill_value) { BLOSC_ERROR_NULL(ctx, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR(b2nd_empty(ctx, array)); if ((*array)->sc->typesize <= 0) { BLOSC_TRACE_ERROR("Invalid chunk parameters"); return BLOSC2_ERROR_INVALID_PARAM; } if ((*array)->extchunknitems < 0) { BLOSC_TRACE_ERROR("Invalid chunk parameters"); return BLOSC2_ERROR_INVALID_PARAM; } if ((uint64_t)(*array)->extchunknitems > (uint64_t)SIZE_MAX) { BLOSC_TRACE_ERROR("extchunknitems too large"); return BLOSC2_ERROR_INVALID_PARAM; } size_t chunkbytes_size = 0; if (b2nd_mul_overflow_size_t((size_t)(*array)->extchunknitems, (size_t)(*array)->sc->typesize, &chunkbytes_size)) { BLOSC_TRACE_ERROR("Chunk bytes overflows size limits"); return BLOSC2_ERROR_INVALID_PARAM; } if (chunkbytes_size > BLOSC2_MAX_BUFFERSIZE || chunkbytes_size > INT32_MAX) { BLOSC_TRACE_ERROR("Chunk bytes exceeds maximum of %d", BLOSC2_MAX_BUFFERSIZE); return BLOSC2_ERROR_MAX_BUFSIZE_EXCEEDED; } int32_t chunkbytes = (int32_t)chunkbytes_size; blosc2_cparams *cparams; if (blosc2_schunk_get_cparams((*array)->sc, &cparams) != 0) { BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } int32_t chunksize = BLOSC_EXTENDED_HEADER_LENGTH + (*array)->sc->typesize; uint8_t *chunk = malloc(chunksize); BLOSC_ERROR_NULL(chunk, BLOSC2_ERROR_MEMORY_ALLOC); if (blosc2_chunk_repeatval(*cparams, chunkbytes, chunk, chunksize, fill_value) < 0) { BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } free(cparams); for (int64_t i = 0; i < (*array)->sc->nchunks; ++i) { if (blosc2_schunk_update_chunk((*array)->sc, i, chunk, true) < 0) { BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } } free(chunk); return BLOSC2_ERROR_SUCCESS; } int b2nd_from_schunk(blosc2_schunk *schunk, b2nd_array_t **array) { BLOSC_ERROR_NULL(schunk, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); if (schunk == NULL) { BLOSC_TRACE_ERROR("Schunk is null"); return BLOSC2_ERROR_NULL_POINTER; } blosc2_cparams *cparams; if (blosc2_schunk_get_cparams(schunk, &cparams) < 0) { BLOSC_TRACE_ERROR("Blosc error"); return BLOSC2_ERROR_NULL_POINTER; } free(cparams); b2nd_context_t params = {0}; params.b2_storage = schunk->storage; // Deserialize the b2nd metalayer uint8_t *smeta; int32_t smeta_len; if (blosc2_meta_get(schunk, "b2nd", &smeta, &smeta_len) < 0) { // Try with a caterva metalayer; we are meant to be backward compatible with it if (blosc2_meta_get(schunk, "caterva", &smeta, &smeta_len) < 0) { BLOSC_ERROR(BLOSC2_ERROR_METALAYER_NOT_FOUND); } } BLOSC_ERROR(b2nd_deserialize_meta(smeta, smeta_len, ¶ms.ndim, params.shape, params.chunkshape, params.blockshape, ¶ms.dtype, ¶ms.dtype_format)); free(smeta); // Reject crafted metalayers that pair shape[i]>0 with chunkshape[i]==0 or // blockshape[i]==0. validate_shape_chunkshape_blockshape is intentionally // lenient about chunkshape==0/blockshape==0 to support the placeholder // pattern in b2nd_create_ctx (see example_empty_shape.c), where the // companion shape entry is also 0 so no divide-by-zero ever materializes. // Crafted on-wire metadata can violate that pairing, and many code paths // downstream (b2nd_get_slice, b2nd_set_slice, set/get_orthogonal_selection, // squeeze, resize) divide or mod by chunkshape[i] / blockshape[i] without // re-checking. Enforce the pairing here so the deserialization path is // strict even when the create-ctx path remains permissive. for (int i = 0; i < params.ndim; ++i) { if (params.shape[i] != 0 && (params.chunkshape[i] == 0 || params.blockshape[i] == 0)) { BLOSC_TRACE_ERROR("b2nd metalayer: shape[%d] is non-zero but " "chunkshape[%d] or blockshape[%d] is zero", i, i, i); free(params.dtype); return BLOSC2_ERROR_INVALID_PARAM; } } BLOSC_ERROR(array_without_schunk(¶ms, array)); free(params.dtype); (*array)->sc = schunk; if ((*array) == NULL) { BLOSC_TRACE_ERROR("Error creating a b2nd container from a frame"); return BLOSC2_ERROR_NULL_POINTER; } return BLOSC2_ERROR_SUCCESS; } int b2nd_to_cframe(const b2nd_array_t *array, uint8_t **cframe, int64_t *cframe_len, bool *needs_free) { BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(cframe, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(cframe_len, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(needs_free, BLOSC2_ERROR_NULL_POINTER); *cframe_len = blosc2_schunk_to_buffer(array->sc, cframe, needs_free); if (*cframe_len <= 0) { BLOSC_TRACE_ERROR("Error serializing the b2nd array"); return BLOSC2_ERROR_FAILURE; } return BLOSC2_ERROR_SUCCESS; } int b2nd_from_cframe(uint8_t *cframe, int64_t cframe_len, bool copy, b2nd_array_t **array) { BLOSC_ERROR_NULL(cframe, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); blosc2_schunk *sc = blosc2_schunk_from_buffer(cframe, cframe_len, copy); if (sc == NULL) { BLOSC_TRACE_ERROR("Blosc error"); return BLOSC2_ERROR_FAILURE; } // ...and create a b2nd array out of it BLOSC_ERROR(b2nd_from_schunk(sc, array)); return BLOSC2_ERROR_SUCCESS; } int b2nd_open(const char *urlpath, b2nd_array_t **array) { BLOSC_ERROR_NULL(urlpath, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); blosc2_schunk *sc = blosc2_schunk_open(urlpath); // ...and create a b2nd array out of it BLOSC_ERROR(b2nd_from_schunk(sc, array)); return BLOSC2_ERROR_SUCCESS; } int b2nd_open_offset(const char *urlpath, b2nd_array_t **array, int64_t offset) { BLOSC_ERROR_NULL(urlpath, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); blosc2_schunk *sc = blosc2_schunk_open_offset(urlpath, offset); // ...and create a b2nd array out of it BLOSC_ERROR(b2nd_from_schunk(sc, array)); return BLOSC2_ERROR_SUCCESS; } int b2nd_free(b2nd_array_t *array) { BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); if (array) { if (array->sc != NULL) { blosc2_schunk_free(array->sc); } free(array->dtype); free(array); } return BLOSC2_ERROR_SUCCESS; } int b2nd_from_cbuffer(b2nd_context_t *ctx, b2nd_array_t **array, const void *buffer, int64_t buffersize) { BLOSC_ERROR_NULL(ctx, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR(b2nd_empty(ctx, array)); if (buffersize < (int64_t) (*array)->nitems * (*array)->sc->typesize) { BLOSC_TRACE_ERROR("The buffersize (%lld) is smaller than the array size (%lld)", (long long) buffersize, (long long) (*array)->nitems * (*array)->sc->typesize); BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); } if ((*array)->nitems == 0) { return BLOSC2_ERROR_SUCCESS; } int64_t start[B2ND_MAX_DIM] = {0}; int64_t *stop = (*array)->shape; int64_t *shape = (*array)->shape; BLOSC_ERROR(b2nd_set_slice_cbuffer(buffer, shape, buffersize, start, stop, *array)); return BLOSC2_ERROR_SUCCESS; } int b2nd_to_cbuffer(const b2nd_array_t *array, void *buffer, int64_t buffersize) { BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_NULL_POINTER); if (buffersize < (int64_t) array->nitems * array->sc->typesize) { BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); } if (array->nitems == 0) { return BLOSC2_ERROR_SUCCESS; } int64_t start[B2ND_MAX_DIM] = {0}; const int64_t *stop = array->shape; BLOSC_ERROR(b2nd_get_slice_cbuffer(array, start, stop, buffer, array->shape, buffersize)); return BLOSC2_ERROR_SUCCESS; } int b2nd_get_sparse_cbuffer(const b2nd_array_t *array, int64_t ncoords, const int64_t *coords, void *buffer, int64_t buffersize) { BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); if (ncoords < 0) { BLOSC_TRACE_ERROR("ncoords must be non-negative"); BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); } if (ncoords == 0) { return BLOSC2_ERROR_SUCCESS; } BLOSC_ERROR_NULL(coords, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_NULL_POINTER); if (buffersize < ncoords * array->sc->typesize) { BLOSC_TRACE_ERROR("Buffer is smaller than expected"); BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); } if (array->ndim == 0) { BLOSC_TRACE_ERROR("Sparse indexing is not supported for 0-dimensional arrays"); BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); } int64_t chunks_in_array[B2ND_MAX_DIM] = {0}; int64_t chunks_in_array_strides[B2ND_MAX_DIM] = {0}; chunks_in_array_strides[array->ndim - 1] = 1; for (int j = 0; j < array->ndim; ++j) { chunks_in_array[j] = array->chunkshape[j] == 0 ? 0 : array->extshape[j] / array->chunkshape[j]; } for (int j = array->ndim - 2; j >= 0; --j) { chunks_in_array_strides[j] = chunks_in_array_strides[j + 1] * chunks_in_array[j + 1]; } int64_t *storage_coords = malloc((size_t)ncoords * sizeof(int64_t)); BLOSC_ERROR_NULL(storage_coords, BLOSC2_ERROR_MEMORY_ALLOC); int rc = BLOSC2_ERROR_SUCCESS; for (int64_t i = 0; i < ncoords; ++i) { int64_t coord = coords[i]; if (coord < 0 || coord >= array->nitems) { BLOSC_TRACE_ERROR("Coordinate out of bounds"); rc = BLOSC2_ERROR_INVALID_PARAM; goto cleanup; } int64_t logical_index[B2ND_MAX_DIM] = {0}; int64_t nchunk_ndim[B2ND_MAX_DIM] = {0}; int64_t item_in_chunk[B2ND_MAX_DIM] = {0}; int64_t nblock_ndim[B2ND_MAX_DIM] = {0}; int64_t item_in_block[B2ND_MAX_DIM] = {0}; blosc2_unidim_to_multidim(array->ndim, (int64_t *)array->shape, coord, logical_index); for (int j = 0; j < array->ndim; ++j) { nchunk_ndim[j] = logical_index[j] / array->chunkshape[j]; item_in_chunk[j] = logical_index[j] % array->chunkshape[j]; nblock_ndim[j] = item_in_chunk[j] / array->blockshape[j]; item_in_block[j] = item_in_chunk[j] % array->blockshape[j]; } int64_t nchunk; blosc2_multidim_to_unidim(nchunk_ndim, array->ndim, chunks_in_array_strides, &nchunk); int64_t nblock; blosc2_multidim_to_unidim(nblock_ndim, array->ndim, array->block_chunk_strides, &nblock); int64_t storage_item_in_block; blosc2_multidim_to_unidim(item_in_block, array->ndim, array->item_block_strides, &storage_item_in_block); storage_coords[i] = nchunk * array->extchunknitems + nblock * array->blocknitems + storage_item_in_block; } rc = blosc2_schunk_get_sparse_buffer(array->sc, ncoords, storage_coords, buffer); cleanup: free(storage_coords); BLOSC_ERROR(rc); return BLOSC2_ERROR_SUCCESS; } int64_t b2nd_get_slice_nchunks(const b2nd_array_t *array, const int64_t *start, const int64_t *stop, int64_t **chunks_idx) { BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(start, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(stop, BLOSC2_ERROR_NULL_POINTER); int8_t ndim = array->ndim; if (array->nitems == 0) { *chunks_idx = NULL; return 0; } // 0-dim case if (ndim == 0) { *chunks_idx = malloc(1 * sizeof(int64_t)); *chunks_idx[0] = 0; return 1; } int64_t chunks_in_array[B2ND_MAX_DIM] = {0}; for (int i = 0; i < ndim; ++i) { chunks_in_array[i] = array->extshape[i] / array->chunkshape[i]; } int64_t chunks_in_array_strides[B2ND_MAX_DIM]; chunks_in_array_strides[ndim - 1] = 1; for (int i = ndim - 2; i >= 0; --i) { chunks_in_array_strides[i] = chunks_in_array_strides[i + 1] * chunks_in_array[i + 1]; } // Compute the number of chunks to update int64_t update_start[B2ND_MAX_DIM]; int64_t update_shape[B2ND_MAX_DIM]; int64_t update_nchunks = 1; for (int i = 0; i < ndim; ++i) { int64_t pos = 0; while (pos <= start[i]) { pos += array->chunkshape[i]; } update_start[i] = pos / array->chunkshape[i] - 1; while (pos < stop[i]) { pos += array->chunkshape[i]; } update_shape[i] = pos / array->chunkshape[i] - update_start[i]; update_nchunks *= update_shape[i]; } int64_t nchunks = 0; // Initially we do not know the number of chunks that will be affected *chunks_idx = malloc(array->sc->nchunks * sizeof(int64_t)); int64_t *ptr = *chunks_idx; for (int64_t update_nchunk = 0; update_nchunk < update_nchunks; ++update_nchunk) { int64_t nchunk_ndim[B2ND_MAX_DIM] = {0}; blosc2_unidim_to_multidim(ndim, update_shape, update_nchunk, nchunk_ndim); for (int i = 0; i < ndim; ++i) { nchunk_ndim[i] += update_start[i]; } int64_t nchunk; blosc2_multidim_to_unidim(nchunk_ndim, ndim, chunks_in_array_strides, &nchunk); // Check if the chunk is inside the slice domain int64_t chunk_start[B2ND_MAX_DIM] = {0}; int64_t chunk_stop[B2ND_MAX_DIM] = {0}; for (int i = 0; i < ndim; ++i) { chunk_start[i] = nchunk_ndim[i] * array->chunkshape[i]; chunk_stop[i] = chunk_start[i] + array->chunkshape[i]; if (chunk_stop[i] > array->shape[i]) { chunk_stop[i] = array->shape[i]; } } bool chunk_empty = false; for (int i = 0; i < ndim; ++i) { chunk_empty |= (chunk_stop[i] <= start[i] || chunk_start[i] >= stop[i]); } if (chunk_empty) { continue; } ptr[nchunks] = nchunk; nchunks++; } if (nchunks < array->sc->nchunks) { *chunks_idx = realloc(ptr, nchunks * sizeof(int64_t)); } return nchunks; } // Check whether the slice defined by start and stop is a single chunk and contiguous // in the C order. This is a fast path for the get_slice and set_slice functions. int64_t nchunk_fastpath(const b2nd_array_t *array, const int64_t *start, const int64_t *stop, const int64_t slice_size) { if (slice_size != array->chunknitems) { return -1; } int ndim = (int) array->ndim; int k = 0; for (int i = 0; i < ndim; ++i) { // The slice needs to correspond to a whole chunk (without padding) if (start[i] % array->chunkshape[i] != 0) { return -1; } if (stop[i] - start[i] != array->chunkshape[i]) { return -1; } // There needs to exist 0 <= k <= ndim such that: // - for i < k, blockshape[i] == 1 // - for i == k, blockshape[i] divides chunkshape[i] // - for i > k, blockshape[i] == chunkshape[i] if (array->chunkshape[i] % array->blockshape[i] != 0) { return -1; } if (i > k && array->chunkshape[i] != array->blockshape[i]) { return -1; } if (i == k && array->blockshape[i] == 1) { k++; } } // Compute the chunk number int64_t *chunks_idx; int64_t nchunks = b2nd_get_slice_nchunks(array, start, stop, &chunks_idx); if (nchunks != 1) { free(chunks_idx); BLOSC_TRACE_ERROR("The number of chunks to read is not 1; go fix the code"); BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } int64_t nchunk = chunks_idx[0]; free(chunks_idx); return nchunk; } // Setting and getting slices int get_set_slice(void *buffer, int64_t buffersize, const int64_t *start, const int64_t *stop, const int64_t *shape, b2nd_array_t *array, bool set_slice) { BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(start, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(stop, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); if (buffersize < 0) { BLOSC_TRACE_ERROR("buffersize is < 0"); BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); } uint8_t *buffer_b = buffer; int8_t ndim = array->ndim; if (!set_slice) { // get_slice paths may touch only a subset of the destination buffer. // Pre-initialize so unread regions are defined and deterministic. memset(buffer_b, 0, (size_t)buffersize); } // 0-dim case if (ndim == 0) { if (set_slice) { int32_t chunk_size = array->sc->typesize + BLOSC2_MAX_OVERHEAD; uint8_t *chunk = malloc(chunk_size); BLOSC_ERROR_NULL(chunk, BLOSC2_ERROR_MEMORY_ALLOC); if (blosc2_compress_ctx(array->sc->cctx, buffer_b, array->sc->typesize, chunk, chunk_size) < 0) { BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } if (blosc2_schunk_update_chunk(array->sc, 0, chunk, false) < 0) { BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } } else { if (blosc2_schunk_decompress_chunk(array->sc, 0, buffer_b, array->sc->typesize) < 0) { BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } } return BLOSC2_ERROR_SUCCESS; } if (array->nitems == 0) { return BLOSC2_ERROR_SUCCESS; } int64_t nelems_slice = 1; for (int i = 0; i < array->ndim; ++i) { if (stop[i] - start[i] > shape[i]) { BLOSC_TRACE_ERROR("The buffer shape can not be smaller than the slice shape"); return BLOSC2_ERROR_INVALID_PARAM; } nelems_slice *= stop[i] - start[i]; } int64_t slice_nbytes = nelems_slice * array->sc->typesize; int32_t data_nbytes = (int32_t) array->extchunknitems * array->sc->typesize; if (buffersize < slice_nbytes) { BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); } // Check for fast path for aligned slices with chunks and blocks (only 1 chunk is supported) int64_t nchunk = nchunk_fastpath(array, start, stop, nelems_slice); if (nchunk >= 0) { if (set_slice) { // Fast path for set. Let's set the chunk buffer straight into the array. // Compress the chunk int32_t chunk_nbytes = data_nbytes + BLOSC2_MAX_OVERHEAD; uint8_t *chunk = malloc(chunk_nbytes); BLOSC_ERROR_NULL(chunk, BLOSC2_ERROR_MEMORY_ALLOC); int brc; // Update current_chunk in case a prefilter is applied array->sc->current_nchunk = nchunk; brc = blosc2_compress_ctx(array->sc->cctx, buffer, data_nbytes, chunk, chunk_nbytes); if (brc < 0) { BLOSC_TRACE_ERROR("Blosc can not compress the data"); BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } int64_t brc_ = blosc2_schunk_update_chunk(array->sc, nchunk, chunk, false); if (brc_ < 0) { BLOSC_TRACE_ERROR("Blosc can not update the chunk"); BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } // We are done return BLOSC2_ERROR_SUCCESS; } else { // Fast path for get. Let's read the chunk straight into the buffer. if (blosc2_schunk_decompress_chunk(array->sc, nchunk, buffer, (int32_t) slice_nbytes) < 0) { BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } return BLOSC2_ERROR_SUCCESS; } } // Slow path for set and get uint8_t *data = malloc(data_nbytes); BLOSC_ERROR_NULL(data, BLOSC2_ERROR_MEMORY_ALLOC); int64_t chunks_in_array[B2ND_MAX_DIM] = {0}; for (int i = 0; i < ndim; ++i) { chunks_in_array[i] = array->extshape[i] / array->chunkshape[i]; } int64_t chunks_in_array_strides[B2ND_MAX_DIM]; chunks_in_array_strides[ndim - 1] = 1; for (int i = ndim - 2; i >= 0; --i) { chunks_in_array_strides[i] = chunks_in_array_strides[i + 1] * chunks_in_array[i + 1]; } int64_t blocks_in_chunk[B2ND_MAX_DIM] = {0}; for (int i = 0; i < ndim; ++i) { blocks_in_chunk[i] = array->extchunkshape[i] / array->blockshape[i]; } // Compute the number of chunks to update int64_t update_start[B2ND_MAX_DIM]; int64_t update_shape[B2ND_MAX_DIM]; int64_t update_nchunks = 1; for (int i = 0; i < ndim; ++i) { int64_t pos = 0; while (pos <= start[i]) { pos += array->chunkshape[i]; } update_start[i] = pos / array->chunkshape[i] - 1; while (pos < stop[i]) { pos += array->chunkshape[i]; } update_shape[i] = pos / array->chunkshape[i] - update_start[i]; update_nchunks *= update_shape[i]; } for (int64_t update_nchunk = 0; update_nchunk < update_nchunks; ++update_nchunk) { int64_t nchunk_ndim[B2ND_MAX_DIM] = {0}; blosc2_unidim_to_multidim(ndim, update_shape, update_nchunk, nchunk_ndim); for (int i = 0; i < ndim; ++i) { nchunk_ndim[i] += update_start[i]; } int64_t nchunk; blosc2_multidim_to_unidim(nchunk_ndim, ndim, chunks_in_array_strides, &nchunk); // Check if the chunk needs to be updated int64_t chunk_start[B2ND_MAX_DIM] = {0}; int64_t chunk_stop[B2ND_MAX_DIM] = {0}; for (int i = 0; i < ndim; ++i) { chunk_start[i] = nchunk_ndim[i] * array->chunkshape[i]; chunk_stop[i] = chunk_start[i] + array->chunkshape[i]; if (chunk_stop[i] > array->shape[i]) { chunk_stop[i] = array->shape[i]; } } bool chunk_empty = false; for (int i = 0; i < ndim; ++i) { chunk_empty |= (chunk_stop[i] <= start[i] || chunk_start[i] >= stop[i]); } if (chunk_empty) { continue; } int32_t nblocks = (int32_t) array->extchunknitems / array->blocknitems; if (set_slice) { // Check if all the chunk is going to be updated and avoid the decompression bool decompress_chunk = false; for (int i = 0; i < ndim; ++i) { decompress_chunk |= (chunk_start[i] < start[i] || chunk_stop[i] > stop[i]); } if (decompress_chunk) { int err = blosc2_schunk_decompress_chunk(array->sc, nchunk, data, data_nbytes); if (err < 0) { BLOSC_TRACE_ERROR("Error decompressing chunk"); BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } } else { // Avoid writing non zero padding from previous chunk memset(data, 0, data_nbytes); } } else { bool *block_maskout = malloc(nblocks); BLOSC_ERROR_NULL(block_maskout, BLOSC2_ERROR_MEMORY_ALLOC); for (int nblock = 0; nblock < nblocks; ++nblock) { int64_t nblock_ndim[B2ND_MAX_DIM] = {0}; blosc2_unidim_to_multidim(ndim, blocks_in_chunk, nblock, nblock_ndim); // Check if the block needs to be updated int64_t block_start[B2ND_MAX_DIM] = {0}; int64_t block_stop[B2ND_MAX_DIM] = {0}; for (int i = 0; i < ndim; ++i) { block_start[i] = nblock_ndim[i] * array->blockshape[i]; block_stop[i] = block_start[i] + array->blockshape[i]; block_start[i] += chunk_start[i]; block_stop[i] += chunk_start[i]; if (block_start[i] > chunk_stop[i]) { block_start[i] = chunk_stop[i]; } if (block_stop[i] > chunk_stop[i]) { block_stop[i] = chunk_stop[i]; } } bool block_empty = false; for (int i = 0; i < ndim; ++i) { block_empty |= (block_stop[i] <= start[i] || block_start[i] >= stop[i]); } block_maskout[nblock] = block_empty ? true : false; } if (blosc2_set_maskout(array->sc->dctx, block_maskout, nblocks) != BLOSC2_ERROR_SUCCESS) { BLOSC_TRACE_ERROR("Error setting the maskout"); BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } int err = blosc2_schunk_decompress_chunk(array->sc, nchunk, data, data_nbytes); if (err < 0) { BLOSC_TRACE_ERROR("Error decompressing chunk"); BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } free(block_maskout); } // Iterate over blocks for (int nblock = 0; nblock < nblocks; ++nblock) { int64_t nblock_ndim[B2ND_MAX_DIM] = {0}; blosc2_unidim_to_multidim(ndim, blocks_in_chunk, nblock, nblock_ndim); // Check if the block needs to be updated int64_t block_start[B2ND_MAX_DIM] = {0}; int64_t block_stop[B2ND_MAX_DIM] = {0}; for (int i = 0; i < ndim; ++i) { block_start[i] = nblock_ndim[i] * array->blockshape[i]; block_stop[i] = block_start[i] + array->blockshape[i]; block_start[i] += chunk_start[i]; block_stop[i] += chunk_start[i]; if (block_start[i] > chunk_stop[i]) { block_start[i] = chunk_stop[i]; } if (block_stop[i] > chunk_stop[i]) { block_stop[i] = chunk_stop[i]; } } int64_t block_shape[B2ND_MAX_DIM] = {0}; for (int i = 0; i < ndim; ++i) { block_shape[i] = block_stop[i] - block_start[i]; } bool block_empty = false; for (int i = 0; i < ndim; ++i) { block_empty |= (block_stop[i] <= start[i] || block_start[i] >= stop[i]); } if (block_empty) { continue; } // compute the start of the slice inside the block int64_t slice_start[B2ND_MAX_DIM] = {0}; for (int i = 0; i < ndim; ++i) { if (block_start[i] < start[i]) { slice_start[i] = start[i] - block_start[i]; } else { slice_start[i] = 0; } slice_start[i] += block_start[i]; } int64_t slice_stop[B2ND_MAX_DIM] = {0}; for (int i = 0; i < ndim; ++i) { if (block_stop[i] > stop[i]) { slice_stop[i] = block_shape[i] - (block_stop[i] - stop[i]); } else { slice_stop[i] = block_stop[i] - block_start[i]; } slice_stop[i] += block_start[i]; } int64_t slice_shape[B2ND_MAX_DIM] = {0}; for (int i = 0; i < ndim; ++i) { slice_shape[i] = slice_stop[i] - slice_start[i]; } uint8_t *src = &buffer_b[0]; int64_t src_start[B2ND_MAX_DIM] = {0}; int64_t src_stop[B2ND_MAX_DIM] = {0}; for (int i = 0; i < ndim; ++i) { src_start[i] = slice_start[i] - start[i]; src_stop[i] = slice_stop[i] - start[i]; } uint8_t *dst = &data[nblock * array->blocknitems * array->sc->typesize]; int64_t dst_pad_shape[B2ND_MAX_DIM]; for (int i = 0; i < ndim; ++i) { dst_pad_shape[i] = array->blockshape[i]; } int64_t dst_start[B2ND_MAX_DIM] = {0}; int64_t dst_stop[B2ND_MAX_DIM] = {0}; for (int i = 0; i < ndim; ++i) { dst_start[i] = slice_start[i] - block_start[i]; dst_stop[i] = dst_start[i] + slice_shape[i]; } if (set_slice) { b2nd_copy_buffer2(ndim, array->sc->typesize, src, shape, src_start, src_stop, dst, dst_pad_shape, dst_start); } else { b2nd_copy_buffer2(ndim, array->sc->typesize, dst, dst_pad_shape, dst_start, dst_stop, src, shape, src_start); } } if (set_slice) { // Recompress the data int32_t chunk_nbytes = data_nbytes + BLOSC2_MAX_OVERHEAD; uint8_t *chunk = malloc(chunk_nbytes); BLOSC_ERROR_NULL(chunk, BLOSC2_ERROR_MEMORY_ALLOC); int brc; // Update current_chunk in case a prefilter is applied array->sc->current_nchunk = nchunk; brc = blosc2_compress_ctx(array->sc->cctx, data, data_nbytes, chunk, chunk_nbytes); if (brc < 0) { BLOSC_TRACE_ERROR("Blosc can not compress the data"); BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } int64_t brc_ = blosc2_schunk_update_chunk(array->sc, nchunk, chunk, false); if (brc_ < 0) { BLOSC_TRACE_ERROR("Blosc can not update the chunk"); BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } } } free(data); return BLOSC2_ERROR_SUCCESS; } int b2nd_get_slice_cbuffer(const b2nd_array_t *array, const int64_t *start, const int64_t *stop, void *buffer, const int64_t *buffershape, int64_t buffersize) { BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(start, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(stop, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(buffershape, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR(get_set_slice(buffer, buffersize, start, stop, buffershape, (b2nd_array_t *)array, false)); return BLOSC2_ERROR_SUCCESS; } int b2nd_set_slice_cbuffer(const void *buffer, const int64_t *buffershape, int64_t buffersize, const int64_t *start, const int64_t *stop, b2nd_array_t *array) { BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(start, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(stop, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR(get_set_slice((void*)buffer, buffersize, start, stop, (int64_t *)buffershape, array, true)); return BLOSC2_ERROR_SUCCESS; } int b2nd_get_slice(b2nd_context_t *ctx, b2nd_array_t **array, const b2nd_array_t *src, const int64_t *start, const int64_t *stop) { BLOSC_ERROR_NULL(src, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(start, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(stop, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); ctx->ndim = src->ndim; for (int i = 0; i < src->ndim; ++i) { ctx->shape[i] = stop[i] - start[i]; } // Add data BLOSC_ERROR(b2nd_empty(ctx, array)); if ((*array)->nitems == 0) { return BLOSC2_ERROR_SUCCESS; } int8_t ndim = (*array)->ndim; int64_t chunks_in_array[B2ND_MAX_DIM] = {0}; for (int i = 0; i < ndim; ++i) { chunks_in_array[i] = (*array)->extshape[i] / (*array)->chunkshape[i]; } int64_t nchunks = (*array)->sc->nchunks; for (int64_t nchunk = 0; nchunk < nchunks; ++nchunk) { int64_t nchunk_ndim[B2ND_MAX_DIM] = {0}; blosc2_unidim_to_multidim(ndim, chunks_in_array, nchunk, nchunk_ndim); // Check if the chunk needs to be updated int64_t chunk_start[B2ND_MAX_DIM] = {0}; int64_t chunk_stop[B2ND_MAX_DIM] = {0}; int64_t chunk_shape[B2ND_MAX_DIM] = {0}; for (int i = 0; i < ndim; ++i) { chunk_start[i] = nchunk_ndim[i] * (*array)->chunkshape[i]; chunk_stop[i] = chunk_start[i] + (*array)->chunkshape[i]; if (chunk_stop[i] > (*array)->shape[i]) { chunk_stop[i] = (*array)->shape[i]; } chunk_shape[i] = chunk_stop[i] - chunk_start[i]; } int64_t src_start[B2ND_MAX_DIM] = {0}; int64_t src_stop[B2ND_MAX_DIM] = {0}; for (int i = 0; i < ndim; ++i) { src_start[i] = chunk_start[i] + start[i]; src_stop[i] = chunk_stop[i] + start[i]; } if (ctx->b2_storage->cparams->typesize <= 0) { BLOSC_TRACE_ERROR("Invalid typesize"); return BLOSC2_ERROR_INVALID_PARAM; } size_t buffersize = (size_t)ctx->b2_storage->cparams->typesize; for (int i = 0; i < ndim; ++i) { if (chunk_shape[i] < 0) { BLOSC_TRACE_ERROR("Invalid chunk shape"); return BLOSC2_ERROR_INVALID_PARAM; } if (b2nd_mul_overflow_size_t(buffersize, (size_t)chunk_shape[i], &buffersize)) { BLOSC_TRACE_ERROR("Buffer size overflows size limits"); return BLOSC2_ERROR_INVALID_PARAM; } } if (buffersize == 0 || buffersize > (size_t)INT64_MAX) { BLOSC_TRACE_ERROR("Buffer size is out of range"); return BLOSC2_ERROR_INVALID_PARAM; } uint8_t *buffer = malloc(buffersize); if (buffer == NULL) { BLOSC_ERROR(BLOSC2_ERROR_MEMORY_ALLOC); } BLOSC_ERROR(b2nd_get_slice_cbuffer(src, src_start, src_stop, buffer, chunk_shape, (int64_t)buffersize)); BLOSC_ERROR(b2nd_set_slice_cbuffer(buffer, chunk_shape, (int64_t)buffersize, chunk_start, chunk_stop, *array)); free(buffer); } return BLOSC2_ERROR_SUCCESS; } /** * @brief Return a view of a b2nd array. * * @param array The memory pointer of the array which will be viewed. * @param view The memory pointer where the view will be created. * @param ctx1 The b2nd context for the new array, containing new shape and other metadata. * * @return An error code. * * @note This doesn't support slices of arrays and is only useful for adding (or removing) dimensions. * */ int view_new(const b2nd_array_t *array, b2nd_array_t **view, b2nd_context_t *ctx1) { BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(view, BLOSC2_ERROR_NULL_POINTER); // The view is not contiguous (points to the original contiguous cframe which has different shape) // so we set contiguous to false, which forces a copy when calling to_cframe ctx1->b2_storage->contiguous = false; /* Fill view with zeros */ BLOSC_ERROR(b2nd_zeros(ctx1, view)); // Free the chunks in base array for (int i = 0; i < (*view)->sc->nchunks; i++) { free((*view)->sc->data[i]); } free((*view)->sc->data); (*view)->sc->view = true; (*view)->sc->data = array->sc->data; // point view to the same data (*view)->sc->frame = array->sc->frame; // if original array is contiguous, point to frame (*view)->sc->nvlmetalayers = array->sc->nvlmetalayers; // for (int i = 0; i< array->sc->nvlmetalayers; i++) { (*view)->sc->vlmetalayers[i] = array->sc->vlmetalayers[i]; // add ptrs to vlmetalayers } return BLOSC2_ERROR_SUCCESS; } int b2nd_expand_dims(const b2nd_array_t *array, b2nd_array_t **view, const bool *axis, const uint8_t final_dims) { for (int i = 0; i < array->sc->nmetalayers; ++i) { if (strcmp(array->sc->metalayers[i]->name, "b2nd") != 0) { BLOSC_TRACE_ERROR("Cannot expand dimensions of an array with non-b2nd metalayers"); return BLOSC2_ERROR_INVALID_PARAM; } } BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(view, BLOSC2_ERROR_NULL_POINTER); uint8_t old_idx = 0; int64_t newshape[B2ND_MAX_DIM]; int32_t newchunkshape[B2ND_MAX_DIM]; int32_t newblockshape[B2ND_MAX_DIM]; for (int i = 0; i < final_dims; ++i) { if (axis[i] == true) { newshape[i] = 1; newchunkshape[i] = 1; newblockshape[i] = 1; } else { if (old_idx == array->ndim) { BLOSC_TRACE_ERROR("Error in axis list: original array has fewer dimensions than the axis list implies!"); return BLOSC2_ERROR_INVALID_PARAM; } newshape[i] = array->shape[old_idx]; newchunkshape[i] = array->chunkshape[old_idx]; newblockshape[i] = array->blockshape[old_idx]; old_idx++; } } //views only deal with cparams/dparams; storage is always in-memory (ephemeral). blosc2_cparams cparams = *(array->sc->storage->cparams); blosc2_dparams dparams = *(array->sc->storage->dparams); blosc2_storage b2_storage1 = {.cparams=&cparams, .dparams=&dparams}; b2nd_context_t *ctx1 = b2nd_create_ctx(&b2_storage1, final_dims, newshape, newchunkshape, newblockshape, array->dtype, array->dtype_format, NULL, 0); view_new(array, view, ctx1); b2nd_free_ctx(ctx1); return BLOSC2_ERROR_SUCCESS; } int b2nd_squeeze(b2nd_array_t *array, b2nd_array_t **view) { BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(view, BLOSC2_ERROR_NULL_POINTER); bool index[B2ND_MAX_DIM]; for (int i = 0; i < array->ndim; ++i) { if (array->shape[i] != 1) { index[i] = false; } else { index[i] = true; } } BLOSC_ERROR(b2nd_squeeze_index(array, view, index)); return BLOSC2_ERROR_SUCCESS; } int b2nd_squeeze_index(b2nd_array_t *array, b2nd_array_t **view, const bool *index) { for (int i = 0; i < array->sc->nmetalayers; ++i) { if (strcmp(array->sc->metalayers[i]->name, "b2nd") != 0) { BLOSC_TRACE_ERROR("Cannot squeeze dimensions of an array with non-b2nd metalayers"); return BLOSC2_ERROR_INVALID_PARAM; } } BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(view, BLOSC2_ERROR_NULL_POINTER); uint8_t nones = 0; int64_t newshape[B2ND_MAX_DIM]; int32_t newchunkshape[B2ND_MAX_DIM]; int32_t newblockshape[B2ND_MAX_DIM]; for (int i = 0; i < array->ndim; ++i) { if (index[i] == true) { if (array->shape[i] != 1) { BLOSC_ERROR(BLOSC2_ERROR_INVALID_INDEX); } } else { newshape[nones] = array->shape[i]; newchunkshape[nones] = array->chunkshape[i]; newblockshape[nones] = array->blockshape[i]; nones += 1; } } //views only deal with cparams/dparams; storage is always in-memory (ephemeral). blosc2_cparams cparams = *(array->sc->storage->cparams); blosc2_dparams dparams = *(array->sc->storage->dparams); blosc2_storage b2_storage1 = {.cparams=&cparams, .dparams=&dparams}; b2nd_context_t *ctx1 = b2nd_create_ctx(&b2_storage1, nones, newshape, newchunkshape, newblockshape, array->dtype, array->dtype_format, NULL, 0); view_new(array, view, ctx1); b2nd_free_ctx(ctx1); return BLOSC2_ERROR_SUCCESS; } int b2nd_copy(b2nd_context_t *ctx, const b2nd_array_t *src, b2nd_array_t **array) { BLOSC_ERROR_NULL(src, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); ctx->ndim = src->ndim; for (int i = 0; i < src->ndim; ++i) { ctx->shape[i] = src->shape[i]; } bool equals = true; for (int i = 0; i < src->ndim; ++i) { if (src->chunkshape[i] != ctx->chunkshape[i]) { equals = false; break; } if (src->blockshape[i] != ctx->blockshape[i]) { equals = false; break; } } if (equals) { BLOSC_ERROR(array_without_schunk(ctx, array)); blosc2_schunk *new_sc = blosc2_schunk_copy(src->sc, ctx->b2_storage); if (new_sc == NULL) { return BLOSC2_ERROR_FAILURE; } (*array)->sc = new_sc; } else { int64_t start[B2ND_MAX_DIM] = {0}; int64_t stop[B2ND_MAX_DIM]; for (int i = 0; i < src->ndim; ++i) { stop[i] = src->shape[i]; } // Copy metalayers b2nd_context_t params_meta; memcpy(¶ms_meta, ctx, sizeof(params_meta)); int j = 0; for (int i = 0; i < src->sc->nmetalayers; ++i) { if (strcmp(src->sc->metalayers[i]->name, "b2nd") == 0) { continue; } blosc2_metalayer *meta = ¶ms_meta.metalayers[j]; meta->name = src->sc->metalayers[i]->name; meta->content = src->sc->metalayers[i]->content; meta->content_len = src->sc->metalayers[i]->content_len; j++; } params_meta.nmetalayers = j; // Copy data BLOSC_ERROR(b2nd_get_slice(¶ms_meta, array, src, start, stop)); // Copy vlmetayers for (int i = 0; i < src->sc->nvlmetalayers; ++i) { uint8_t *content; int32_t content_len; if (blosc2_vlmeta_get(src->sc, src->sc->vlmetalayers[i]->name, &content, &content_len) < 0) { BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } BLOSC_ERROR(blosc2_vlmeta_add((*array)->sc, src->sc->vlmetalayers[i]->name, content, content_len, (*array)->sc->storage->cparams)); free(content); } } return BLOSC2_ERROR_SUCCESS; } int b2nd_concatenate(b2nd_context_t *ctx, const b2nd_array_t *src1, const b2nd_array_t *src2, int8_t axis, bool copy, b2nd_array_t **array) { BLOSC_ERROR_NULL(src1, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(src2, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); // Validate the axis parameter if (axis < 0 || axis >= src1->ndim) { BLOSC_TRACE_ERROR("axis parameter is out of bounds: axis=%d, expected range=[0, %d)", axis, src1->ndim - 1); BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); } // typesize must be the same for both arrays if (src1->sc->typesize != src2->sc->typesize) { BLOSC_TRACE_ERROR("The two arrays must have the same typesize"); BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); } // Keep the src1 shape for later use int64_t src1_shape[B2ND_MAX_DIM]; for (int i = 0; i < src1->ndim; ++i) { src1_shape[i] = src1->shape[i]; } // Support for 0-dim arrays is not implemented if (src1->ndim == 0 || src2->ndim == 0) { BLOSC_TRACE_ERROR("Concatenation of 0-dim arrays is not supported"); BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); } // Check that the shapes are compatible for concatenation if (src1->ndim != src2->ndim) { BLOSC_TRACE_ERROR("The two arrays must have the same number of dimensions"); BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); } // Compute the new shape int64_t newshape[B2ND_MAX_DIM]; for (int8_t i = 0; i < src1->ndim; ++i) { if (i == axis) { newshape[i] = src1->shape[i] + src2->shape[i]; } else { if (src1->shape[i] != src2->shape[i]) { BLOSC_TRACE_ERROR("The two arrays must have the same shape in all dimensions except the concatenation axis"); BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); } newshape[i] = src1->shape[i]; } } if (copy) { BLOSC_ERROR(b2nd_copy(ctx, src1, array)); } else { *array = (b2nd_array_t *)src1; } // Extend the array, we don't need to specify the start in resize, as we are extending the shape from the end BLOSC_ERROR(b2nd_resize(*array, newshape, NULL)); // Copy the data from the second array int64_t start[B2ND_MAX_DIM]; int64_t stop[B2ND_MAX_DIM]; // Check if the chunk is aligned with dest chunks, and has the same blockshape bool aligned = true; for (int8_t i = 0; i < src2->ndim; ++i) { if (src1->chunkshape[i] != src2->chunkshape[i] || src2->blockshape[i] != (*array)->blockshape[i] || (i == axis && (src1_shape[i]) % (*array)->chunkshape[i] != 0) ) { aligned = false; break; } } // ...and get the chunk index in the dest array if aligned int64_t chunks_in_array_strides[B2ND_MAX_DIM]; // Calculate strides for destination array chunks_in_array_strides[(*array)->ndim - 1] = 1; for (int i = (*array)->ndim - 2; i >= 0; --i) { chunks_in_array_strides[i] = chunks_in_array_strides[i + 1] * ((*array)->extshape[i + 1] / (*array)->chunkshape[i + 1]); } // Copy chunk by chunk void *buffer = malloc(src2->sc->typesize * src2->extchunknitems); BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_MEMORY_ALLOC); for (int64_t nchunk = 0; nchunk < src2->sc->nchunks; ++nchunk) { // Get multidimensional chunk position int64_t nchunk_ndim[B2ND_MAX_DIM] = {0}; int64_t chunkshape[B2ND_MAX_DIM] = {0}; for (int8_t i = 0; i < src2->ndim; ++i) { chunkshape[i] = src2->chunkshape[i]; } int64_t chunks_in_dim[B2ND_MAX_DIM] = {0}; for (int8_t i = 0; i < src2->ndim; ++i) { chunks_in_dim[i] = src2->extshape[i] / src2->chunkshape[i]; } blosc2_unidim_to_multidim(src2->ndim, chunks_in_dim, nchunk, nchunk_ndim); if (aligned) { // Get the uncompressed chunk buffer from the source array bool needs_free = false; uint8_t *chunk; int32_t cbytes = blosc2_schunk_get_chunk(src2->sc, nchunk, &chunk, &needs_free); if (cbytes < 0) { BLOSC_TRACE_ERROR("Error getting chunk from source array"); BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } // Update the chunk in the destination array // We need to free only if needs_free is true or copy is false // bool needs_copy = !needs_free || copy; // BLOSC_ERROR(blosc2_schunk_update_chunk((*array)->sc, nchunk_dest, chunk, needs_copy)); // if (needs_free && !copy) { // free(chunk); // } // TODO: the above makes some tests to crash, so always force a copy; try to optimize this later int64_t nchunk_dest = 0; nchunk_ndim[axis] += src1_shape[axis] / (*array)->chunkshape[axis]; for ( int i =0; i< src2->ndim; i++) { nchunk_dest += nchunk_ndim[i] * chunks_in_array_strides[i]; } BLOSC_ERROR(blosc2_schunk_update_chunk((*array)->sc, nchunk_dest, chunk, true)); if (needs_free) { free(chunk); } } else { // Set positions for each dimension for (int8_t i = 0; i < src2->ndim; ++i) { start[i] = nchunk_ndim[i] * src2->chunkshape[i]; stop[i] = start[i] + src2->chunkshape[i]; if (stop[i] > src2->shape[i]) { stop[i] = src2->shape[i]; // Handle boundary chunks } } // Load chunk into buffer BLOSC_ERROR(b2nd_get_slice_cbuffer(src2, start, stop, buffer, chunkshape, src2->sc->chunksize)); // Apply chunk offset only for concatenation axis start[axis] += src1_shape[axis]; stop[axis] += src1_shape[axis]; // Copy the chunk to the correct position BLOSC_ERROR(b2nd_set_slice_cbuffer(buffer, chunkshape, src2->sc->typesize * src2->extchunknitems, start, stop, *array)); } } free(buffer); return BLOSC2_ERROR_SUCCESS; } int b2nd_save(const b2nd_array_t *array, char *urlpath) { BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(urlpath, BLOSC2_ERROR_NULL_POINTER); b2nd_array_t *tmp; blosc2_storage b2_storage = BLOSC2_STORAGE_DEFAULTS; b2nd_context_t params = {.b2_storage=&b2_storage}; b2_storage.urlpath = urlpath; b2_storage.contiguous = array->sc->storage->contiguous; for (int i = 0; i < array->ndim; ++i) { params.chunkshape[i] = array->chunkshape[i]; params.blockshape[i] = array->blockshape[i]; } BLOSC_ERROR(b2nd_copy(¶ms, array, &tmp)); BLOSC_ERROR(b2nd_free(tmp)); return BLOSC2_ERROR_SUCCESS; } int64_t b2nd_save_append(const b2nd_array_t *array, const char *urlpath) { BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); return blosc2_schunk_append_file(array->sc, urlpath); } int b2nd_print_meta(const b2nd_array_t *array) { BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); int8_t ndim; int64_t shape[B2ND_MAX_DIM]; int32_t chunkshape[B2ND_MAX_DIM]; int32_t blockshape[B2ND_MAX_DIM]; char *dtype; int8_t dtype_format; uint8_t *smeta; int32_t smeta_len; if (blosc2_meta_get(array->sc, "b2nd", &smeta, &smeta_len) < 0) { // Try with a caterva metalayer; we are meant to be backward compatible with it if (blosc2_meta_get(array->sc, "caterva", &smeta, &smeta_len) < 0) { BLOSC_ERROR(BLOSC2_ERROR_METALAYER_NOT_FOUND); } } BLOSC_ERROR(b2nd_deserialize_meta(smeta, smeta_len, &ndim, shape, chunkshape, blockshape, &dtype, &dtype_format)); free(smeta); printf("b2nd metalayer parameters:\n Ndim: %d", ndim); printf("\n shape: %" PRId64 "", shape[0]); for (int i = 1; i < ndim; ++i) { printf(", %" PRId64 "", shape[i]); } printf("\n chunkshape: %d", chunkshape[0]); for (int i = 1; i < ndim; ++i) { printf(", %d", chunkshape[i]); } if (dtype != NULL) { printf("\n dtype: %s", dtype); free(dtype); } printf("\n blockshape: %d", blockshape[0]); for (int i = 1; i < ndim; ++i) { printf(", %d", blockshape[i]); } printf("\n"); return BLOSC2_ERROR_SUCCESS; } int extend_shape(b2nd_array_t *array, const int64_t *new_shape, const int64_t *start) { BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(new_shape, BLOSC2_ERROR_NULL_POINTER); int8_t ndim = array->ndim; int64_t diffs_shape[B2ND_MAX_DIM]; int64_t diffs_sum = 0; for (int i = 0; i < ndim; i++) { diffs_shape[i] = new_shape[i] - array->shape[i]; diffs_sum += diffs_shape[i]; if (diffs_shape[i] < 0) { BLOSC_TRACE_ERROR("The new shape must be greater than the old one"); BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); } if (array->shape[i] == INT64_MAX) { BLOSC_TRACE_ERROR("Cannot extend array with shape[%d] = %" PRId64, i, (int64_t)INT64_MAX); BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); } } if (diffs_sum == 0) { // Shapes are equal. Do nothing. return BLOSC2_ERROR_SUCCESS; } int64_t old_nchunks = array->sc->nchunks; // aux array to keep old shapes b2nd_array_t *aux = malloc(sizeof(b2nd_array_t)); BLOSC_ERROR_NULL(aux, BLOSC2_ERROR_MEMORY_ALLOC); aux->sc = NULL; BLOSC_ERROR(update_shape(aux, ndim, array->shape, array->chunkshape, array->blockshape)); BLOSC_ERROR(update_shape(array, ndim, new_shape, array->chunkshape, array->blockshape)); int64_t nchunks = array->extnitems / array->chunknitems; int64_t nchunks_; int64_t nchunk_ndim[B2ND_MAX_DIM]; blosc2_cparams *cparams; BLOSC_ERROR(blosc2_schunk_get_cparams(array->sc, &cparams)); void *chunk; int64_t csize; if (nchunks != old_nchunks) { if (start == NULL) { start = aux->shape; } int64_t chunks_in_array[B2ND_MAX_DIM] = {0}; for (int i = 0; i < ndim; ++i) { chunks_in_array[i] = array->extshape[i] / array->chunkshape[i]; } for (int64_t i = 0; i < nchunks; ++i) { blosc2_unidim_to_multidim(ndim, chunks_in_array, i, nchunk_ndim); for (int j = 0; j < ndim; ++j) { if (start[j] <= (array->chunkshape[j] * nchunk_ndim[j]) && (array->chunkshape[j] * nchunk_ndim[j]) < (start[j] + new_shape[j] - aux->shape[j])) { chunk = malloc(BLOSC_EXTENDED_HEADER_LENGTH); BLOSC_ERROR_NULL(chunk, BLOSC2_ERROR_MEMORY_ALLOC); csize = blosc2_chunk_zeros(*cparams, array->sc->chunksize, chunk, BLOSC_EXTENDED_HEADER_LENGTH); if (csize < 0) { free(aux); free(cparams); BLOSC_TRACE_ERROR("Blosc error when creating a chunk"); return BLOSC2_ERROR_FAILURE; } nchunks_ = blosc2_schunk_insert_chunk(array->sc, i, chunk, false); if (nchunks_ < 0) { free(aux); free(cparams); BLOSC_TRACE_ERROR("Blosc error when inserting a chunk"); return BLOSC2_ERROR_FAILURE; } break; } } } } free(aux); free(cparams); return BLOSC2_ERROR_SUCCESS; } int shrink_shape(b2nd_array_t *array, const int64_t *new_shape, const int64_t *start) { BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(new_shape, BLOSC2_ERROR_NULL_POINTER); int8_t ndim = array->ndim; int64_t diffs_shape[B2ND_MAX_DIM]; int64_t diffs_sum = 0; for (int i = 0; i < ndim; i++) { diffs_shape[i] = new_shape[i] - array->shape[i]; diffs_sum += diffs_shape[i]; if (diffs_shape[i] > 0) { BLOSC_TRACE_ERROR("The new shape must be smaller than the old one"); BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); } if (array->shape[i] == 0) { continue; } } if (diffs_sum == 0) { // Shapes are equal. Do nothing. return BLOSC2_ERROR_SUCCESS; } int64_t old_nchunks = array->sc->nchunks; // aux array to keep old shapes b2nd_array_t *aux = malloc(sizeof(b2nd_array_t)); BLOSC_ERROR_NULL(aux, BLOSC2_ERROR_MEMORY_ALLOC); aux->sc = NULL; BLOSC_ERROR(update_shape(aux, ndim, array->shape, array->chunkshape, array->blockshape)); BLOSC_ERROR(update_shape(array, ndim, new_shape, array->chunkshape, array->blockshape)); // Delete chunks if needed int64_t chunks_in_array_old[B2ND_MAX_DIM] = {0}; for (int i = 0; i < ndim; ++i) { chunks_in_array_old[i] = aux->extshape[i] / aux->chunkshape[i]; } if (start == NULL) { start = new_shape; } int64_t nchunk_ndim[B2ND_MAX_DIM] = {0}; int64_t nchunks_; for (int i = (int) old_nchunks - 1; i >= 0; --i) { blosc2_unidim_to_multidim(ndim, chunks_in_array_old, i, nchunk_ndim); for (int j = 0; j < ndim; ++j) { if (start[j] <= (array->chunkshape[j] * nchunk_ndim[j]) && (array->chunkshape[j] * nchunk_ndim[j]) < (start[j] + aux->shape[j] - new_shape[j])) { nchunks_ = blosc2_schunk_delete_chunk(array->sc, i); if (nchunks_ < 0) { free(aux); BLOSC_TRACE_ERROR("Blosc error when deleting a chunk"); return BLOSC2_ERROR_FAILURE; } break; } } } free(aux); return BLOSC2_ERROR_SUCCESS; } int b2nd_resize(b2nd_array_t *array, const int64_t *new_shape, const int64_t *start) { BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(new_shape, BLOSC2_ERROR_NULL_POINTER); if (start != NULL) { for (int i = 0; i < array->ndim; ++i) { if (start[i] > array->shape[i]) { BLOSC_TRACE_ERROR("`start` must be lower or equal than old array shape in all dims"); BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); } if ((new_shape[i] > array->shape[i] && start[i] != array->shape[i]) || (new_shape[i] < array->shape[i] && (start[i] + array->shape[i] - new_shape[i]) != array->shape[i])) { // Chunks cannot be cut unless they are in the last position if (start[i] % array->chunkshape[i] != 0) { BLOSC_TRACE_ERROR("If array end is not being modified " "`start` must be a multiple of chunkshape in all dims"); BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); } if ((new_shape[i] - array->shape[i]) % array->chunkshape[i] != 0) { BLOSC_TRACE_ERROR("If array end is not being modified " "`(new_shape - shape)` must be multiple of chunkshape in all dims"); BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); } } } } // Get shrunk shape int64_t shrunk_shape[B2ND_MAX_DIM] = {0}; for (int i = 0; i < array->ndim; ++i) { if (new_shape[i] <= array->shape[i]) { shrunk_shape[i] = new_shape[i]; } else { shrunk_shape[i] = array->shape[i]; } } BLOSC_ERROR(shrink_shape(array, shrunk_shape, start)); BLOSC_ERROR(extend_shape(array, new_shape, start)); return BLOSC2_ERROR_SUCCESS; } int b2nd_insert(b2nd_array_t *array, const void *buffer, int64_t buffersize, int8_t axis, int64_t insert_start) { BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_NULL_POINTER); if (axis >= array->ndim) { BLOSC_TRACE_ERROR("`axis` cannot be greater than the number of dimensions"); BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); } int64_t axis_size = array->sc->typesize; int64_t buffershape[B2ND_MAX_DIM]; for (int i = 0; i < array->ndim; ++i) { if (i != axis) { axis_size *= array->shape[i]; buffershape[i] = array->shape[i]; } } if (buffersize % axis_size != 0) { BLOSC_TRACE_ERROR("`buffersize` must be multiple of the array"); BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); } int64_t newshape[B2ND_MAX_DIM]; memcpy(newshape, array->shape, array->ndim * sizeof(int64_t)); newshape[axis] += buffersize / axis_size; buffershape[axis] = newshape[axis] - array->shape[axis]; int64_t start[B2ND_MAX_DIM] = {0}; start[axis] = insert_start; if (insert_start == array->shape[axis]) { BLOSC_ERROR(b2nd_resize(array, newshape, NULL)); } else { BLOSC_ERROR(b2nd_resize(array, newshape, start)); } int64_t stop[B2ND_MAX_DIM]; memcpy(stop, array->shape, sizeof(int64_t) * array->ndim); stop[axis] = start[axis] + buffershape[axis]; BLOSC_ERROR(b2nd_set_slice_cbuffer(buffer, buffershape, buffersize, start, stop, array)); return BLOSC2_ERROR_SUCCESS; } int b2nd_append(b2nd_array_t *array, const void *buffer, int64_t buffersize, int8_t axis) { BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(buffer, BLOSC2_ERROR_NULL_POINTER); int32_t chunksize = array->sc->chunksize; int64_t nchunks_append = buffersize / chunksize; // Check whether chunkshape and blockshape are compatible with accelerated path. // Essentially, we are checking whether the buffer is a multiple of the chunksize // and that the chunkshape and blockshape are the same, except for the first axis. // Also, axis needs to be the first one. bool compat_chunks_blocks = true; for (int i = 1; i < array->ndim; ++i) { if (array->chunkshape[i] != array->blockshape[i]) { compat_chunks_blocks = false; break; } } if (axis > 0) { compat_chunks_blocks = false; } // General case where a buffer has a different size than the chunksize if (!compat_chunks_blocks || buffersize % chunksize != 0 || nchunks_append != 1) { BLOSC_ERROR(b2nd_insert(array, buffer, buffersize, axis, array->shape[axis])); return BLOSC2_ERROR_SUCCESS; } // Accelerated path for buffers that are of the same size as the chunksize // printf("accelerated path\n"); // Append the buffer to the underlying schunk. This is very fast, as // it doesn't need to do internal partitioning. BLOSC_ERROR(blosc2_schunk_append_buffer(array->sc, (void*)buffer, buffersize)); // Finally, resize the array int64_t newshape[B2ND_MAX_DIM]; memcpy(newshape, array->shape, array->ndim * sizeof(int64_t)); newshape[axis] += nchunks_append * array->chunkshape[axis]; BLOSC_ERROR(b2nd_resize(array, newshape, NULL)); return BLOSC2_ERROR_SUCCESS; } int b2nd_delete(b2nd_array_t *array, const int8_t axis, int64_t delete_start, int64_t delete_len) { BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); if (axis >= array->ndim) { BLOSC_TRACE_ERROR("axis cannot be greater than the number of dimensions"); BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); } int64_t newshape[B2ND_MAX_DIM]; memcpy(newshape, array->shape, array->ndim * sizeof(int64_t)); newshape[axis] -= delete_len; int64_t start[B2ND_MAX_DIM] = {0}; start[axis] = delete_start; if (delete_start == (array->shape[axis] - delete_len)) { BLOSC_ERROR(b2nd_resize(array, newshape, NULL)); } else { BLOSC_ERROR(b2nd_resize(array, newshape, start)); } return BLOSC2_ERROR_SUCCESS; } // Indexing typedef struct { int64_t value; int64_t index; } b2nd_selection_t; int compare_selection(const void *a, const void *b) { int res = (int) (((b2nd_selection_t *) a)->value - ((b2nd_selection_t *) b)->value); // In case values are equal, sort by index if (res == 0) { res = (int) (((b2nd_selection_t *) a)->index - ((b2nd_selection_t *) b)->index); } return res; } int copy_block_buffer_data(b2nd_array_t *array, int8_t ndim, int64_t *block_selection_size, b2nd_selection_t **chunk_selection, b2nd_selection_t **p_block_selection_0, b2nd_selection_t **p_block_selection_1, uint8_t *block, uint8_t *buffer, int64_t *buffershape, int64_t *bufferstrides, bool get) { p_block_selection_0[ndim] = chunk_selection[ndim]; p_block_selection_1[ndim] = chunk_selection[ndim]; while (p_block_selection_1[ndim] - p_block_selection_0[ndim] < block_selection_size[ndim]) { if (ndim == array->ndim - 1) { // --- leaf: batch consecutive innermost-dimension elements --------- // Compute start position of the first element in the batch. int64_t index_in_block_n[B2ND_MAX_DIM]; for (int i = 0; i < array->ndim; ++i) { index_in_block_n[i] = p_block_selection_1[i]->value % array->chunkshape[i] % array->blockshape[i]; } int64_t start_block = 0; for (int i = 0; i < array->ndim; ++i) { start_block += index_in_block_n[i] * array->item_block_strides[i]; } int64_t index_in_buffer_n[B2ND_MAX_DIM]; for (int i = 0; i < array->ndim; ++i) { index_in_buffer_n[i] = p_block_selection_1[i]->index; } int64_t start_buffer = 0; for (int i = 0; i < array->ndim; ++i) { start_buffer += index_in_buffer_n[i] * bufferstrides[i]; } int64_t count = 1; int32_t typesize = array->sc->typesize; p_block_selection_1[ndim]++; while (p_block_selection_1[ndim] - p_block_selection_0[ndim] < block_selection_size[ndim]) { b2nd_selection_t *prev = p_block_selection_1[ndim] - 1; b2nd_selection_t *curr = p_block_selection_1[ndim]; // Check if this element is adjacent to the previous one in both // the block buffer (value) and the output buffer (index). if (curr->value == prev->value + 1 && curr->index == prev->index + 1) { count++; } else { // Flush the current batch. if (get) { memcpy(&buffer[start_buffer * typesize], &block[start_block * typesize], (size_t)(count * typesize)); } else { memcpy(&block[start_block * typesize], &buffer[start_buffer * typesize], (size_t)(count * typesize)); } // Recompute start position for the new batch. // Only the innermost dimension advances here; outer dimensions // must keep using their current recursive selection pointers. for (int i = 0; i < array->ndim; ++i) { if (i == ndim) { index_in_block_n[i] = curr->value % array->chunkshape[i] % array->blockshape[i]; index_in_buffer_n[i] = curr->index; } else { index_in_block_n[i] = p_block_selection_1[i]->value % array->chunkshape[i] % array->blockshape[i]; index_in_buffer_n[i] = p_block_selection_1[i]->index; } } start_block = 0; for (int i = 0; i < array->ndim; ++i) { start_block += index_in_block_n[i] * array->item_block_strides[i]; } start_buffer = 0; for (int i = 0; i < array->ndim; ++i) { start_buffer += index_in_buffer_n[i] * bufferstrides[i]; } count = 1; } p_block_selection_1[ndim]++; } // Flush the last batch. if (get) { memcpy(&buffer[start_buffer * typesize], &block[start_block * typesize], (size_t)(count * typesize)); } else { memcpy(&block[start_block * typesize], &buffer[start_buffer * typesize], (size_t)(count * typesize)); } } else { BLOSC_ERROR(copy_block_buffer_data(array, (int8_t) (ndim + 1), block_selection_size, chunk_selection, p_block_selection_0, p_block_selection_1, block, buffer, buffershape, bufferstrides, get) ); p_block_selection_1[ndim]++; } } return BLOSC2_ERROR_SUCCESS; } int iter_block_copy(b2nd_array_t *array, int8_t ndim, int64_t *chunk_selection_size, b2nd_selection_t **ordered_selection, b2nd_selection_t **chunk_selection_0, b2nd_selection_t **chunk_selection_1, uint8_t *data, uint8_t *buffer, int64_t *buffershape, int64_t *bufferstrides, bool get) { chunk_selection_0[ndim] = ordered_selection[ndim]; chunk_selection_1[ndim] = ordered_selection[ndim]; while (chunk_selection_1[ndim] - ordered_selection[ndim] < chunk_selection_size[ndim]) { int64_t block_index_ndim = ((*chunk_selection_1[ndim]).value % array->chunkshape[ndim]) / array->blockshape[ndim]; while (chunk_selection_1[ndim] - ordered_selection[ndim] < chunk_selection_size[ndim] && block_index_ndim == ((*chunk_selection_1[ndim]).value % array->chunkshape[ndim]) / array->blockshape[ndim]) { chunk_selection_1[ndim]++; } if (ndim == array->ndim - 1) { int64_t block_chunk_strides[B2ND_MAX_DIM]; block_chunk_strides[array->ndim - 1] = 1; for (int i = array->ndim - 2; i >= 0; --i) { block_chunk_strides[i] = block_chunk_strides[i + 1] * (array->extchunkshape[i + 1] / array->blockshape[i + 1]); } int64_t block_index[B2ND_MAX_DIM]; for (int i = 0; i < array->ndim; ++i) { block_index[i] = ((*chunk_selection_0[i]).value % array->chunkshape[i]) / array->blockshape[i]; } int64_t nblock = 0; for (int i = 0; i < array->ndim; ++i) { nblock += block_index[i] * block_chunk_strides[i]; } b2nd_selection_t **p_block_selection_0 = malloc(array->ndim * sizeof(b2nd_selection_t *)); BLOSC_ERROR_NULL(p_block_selection_0, BLOSC2_ERROR_MEMORY_ALLOC); b2nd_selection_t **p_block_selection_1 = malloc(array->ndim * sizeof(b2nd_selection_t *)); BLOSC_ERROR_NULL(p_block_selection_1, BLOSC2_ERROR_MEMORY_ALLOC); int64_t *block_selection_size = malloc(array->ndim * sizeof(int64_t)); BLOSC_ERROR_NULL(block_selection_size, BLOSC2_ERROR_MEMORY_ALLOC); for (int i = 0; i < array->ndim; ++i) { block_selection_size[i] = chunk_selection_1[i] - chunk_selection_0[i]; } BLOSC_ERROR(copy_block_buffer_data(array, (int8_t) 0, block_selection_size, chunk_selection_0, p_block_selection_0, p_block_selection_1, &data[nblock * array->blocknitems * array->sc->typesize], buffer, buffershape, bufferstrides, get) ); free(p_block_selection_0); free(p_block_selection_1); free(block_selection_size); } else { BLOSC_ERROR(iter_block_copy(array, (int8_t) (ndim + 1), chunk_selection_size, ordered_selection, chunk_selection_0, chunk_selection_1, data, buffer, buffershape, bufferstrides, get) ); } chunk_selection_0[ndim] = chunk_selection_1[ndim]; } return BLOSC2_ERROR_SUCCESS; } int iter_block_maskout(b2nd_array_t *array, int8_t ndim, int64_t *sel_block_size, b2nd_selection_t **o_selection, b2nd_selection_t **p_o_sel_block_0, b2nd_selection_t **p_o_sel_block_1, bool *maskout) { p_o_sel_block_0[ndim] = o_selection[ndim]; p_o_sel_block_1[ndim] = o_selection[ndim]; while (p_o_sel_block_1[ndim] - o_selection[ndim] < sel_block_size[ndim]) { int64_t block_index_ndim = ((*p_o_sel_block_1[ndim]).value % array->chunkshape[ndim]) / array->blockshape[ndim]; while (p_o_sel_block_1[ndim] - o_selection[ndim] < sel_block_size[ndim] && block_index_ndim == ((*p_o_sel_block_1[ndim]).value % array->chunkshape[ndim]) / array->blockshape[ndim]) { p_o_sel_block_1[ndim]++; } if (ndim == array->ndim - 1) { int64_t block_chunk_strides[B2ND_MAX_DIM]; block_chunk_strides[array->ndim - 1] = 1; for (int i = array->ndim - 2; i >= 0; --i) { block_chunk_strides[i] = block_chunk_strides[i + 1] * (array->extchunkshape[i + 1] / array->blockshape[i + 1]); } int64_t block_index[B2ND_MAX_DIM]; for (int i = 0; i < array->ndim; ++i) { block_index[i] = ((*p_o_sel_block_0[i]).value % array->chunkshape[i]) / array->blockshape[i]; } int64_t nblock = 0; for (int i = 0; i < array->ndim; ++i) { nblock += block_index[i] * block_chunk_strides[i]; } maskout[nblock] = false; } else { BLOSC_ERROR(iter_block_maskout(array, (int8_t) (ndim + 1), sel_block_size, o_selection, p_o_sel_block_0, p_o_sel_block_1, maskout) ); } p_o_sel_block_0[ndim] = p_o_sel_block_1[ndim]; } return BLOSC2_ERROR_SUCCESS; } int iter_chunk(b2nd_array_t *array, int8_t ndim, int64_t *selection_size, b2nd_selection_t **ordered_selection, b2nd_selection_t **p_ordered_selection_0, b2nd_selection_t **p_ordered_selection_1, uint8_t *buffer, int64_t *buffershape, int64_t *bufferstrides, bool get, uint8_t *chunk_data, int32_t chunk_data_nbytes) { p_ordered_selection_0[ndim] = ordered_selection[ndim]; p_ordered_selection_1[ndim] = ordered_selection[ndim]; while (p_ordered_selection_1[ndim] - ordered_selection[ndim] < selection_size[ndim]) { int64_t chunk_index_ndim = (*p_ordered_selection_1[ndim]).value / array->chunkshape[ndim]; while (p_ordered_selection_1[ndim] - ordered_selection[ndim] < selection_size[ndim] && chunk_index_ndim == (*p_ordered_selection_1[ndim]).value / array->chunkshape[ndim]) { p_ordered_selection_1[ndim]++; } if (ndim == array->ndim - 1) { int64_t chunk_array_strides[B2ND_MAX_DIM]; chunk_array_strides[array->ndim - 1] = 1; for (int i = array->ndim - 2; i >= 0; --i) { chunk_array_strides[i] = chunk_array_strides[i + 1] * (array->extshape[i + 1] / array->chunkshape[i + 1]); } int64_t chunk_index[B2ND_MAX_DIM]; for (int i = 0; i < array->ndim; ++i) { chunk_index[i] = (*p_ordered_selection_0[i]).value / array->chunkshape[i]; } int64_t nchunk = 0; for (int i = 0; i < array->ndim; ++i) { nchunk += chunk_index[i] * chunk_array_strides[i]; } int64_t nblocks = array->extchunknitems / array->blocknitems; b2nd_selection_t **p_chunk_selection_0 = malloc(array->ndim * sizeof(b2nd_selection_t *)); BLOSC_ERROR_NULL(p_chunk_selection_0, BLOSC2_ERROR_MEMORY_ALLOC); b2nd_selection_t **p_chunk_selection_1 = malloc(array->ndim * sizeof(b2nd_selection_t *)); BLOSC_ERROR_NULL(p_chunk_selection_1, BLOSC2_ERROR_MEMORY_ALLOC); int64_t *chunk_selection_size = malloc(array->ndim * sizeof(int64_t)); BLOSC_ERROR_NULL(chunk_selection_size, BLOSC2_ERROR_MEMORY_ALLOC); for (int i = 0; i < array->ndim; ++i) { chunk_selection_size[i] = p_ordered_selection_1[i] - p_ordered_selection_0[i]; } if (get) { bool *maskout = calloc(nblocks, sizeof(bool)); for (int i = 0; i < nblocks; ++i) { maskout[i] = true; } BLOSC_ERROR(iter_block_maskout(array, (int8_t) 0, chunk_selection_size, p_ordered_selection_0, p_chunk_selection_0, p_chunk_selection_1, maskout)); if (blosc2_set_maskout(array->sc->dctx, maskout, (int) nblocks) != BLOSC2_ERROR_SUCCESS) { BLOSC_TRACE_ERROR("Error setting the maskout"); BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } free(maskout); } int data_nbytes = chunk_data_nbytes; uint8_t *data = chunk_data; int err = blosc2_schunk_decompress_chunk(array->sc, nchunk, data, data_nbytes); if (err < 0) { BLOSC_TRACE_ERROR("Error decompressing chunk"); BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } BLOSC_ERROR(iter_block_copy(array, 0, chunk_selection_size, p_ordered_selection_0, p_chunk_selection_0, p_chunk_selection_1, data, buffer, buffershape, bufferstrides, get)); if (!get) { int32_t chunk_size = data_nbytes + BLOSC_EXTENDED_HEADER_LENGTH; uint8_t *chunk = malloc(chunk_size); BLOSC_ERROR_NULL(chunk, BLOSC2_ERROR_MEMORY_ALLOC); err = blosc2_compress_ctx(array->sc->cctx, data, data_nbytes, chunk, chunk_size); if (err < 0) { BLOSC_TRACE_ERROR("Error compressing data"); BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } err = (int) blosc2_schunk_update_chunk(array->sc, nchunk, chunk, false); if (err < 0) { BLOSC_TRACE_ERROR("Error updating chunk"); BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } } free(chunk_selection_size); free(p_chunk_selection_0); free(p_chunk_selection_1); } else { BLOSC_ERROR(iter_chunk(array, (int8_t) (ndim + 1), selection_size, ordered_selection, p_ordered_selection_0, p_ordered_selection_1, buffer, buffershape, bufferstrides, get, chunk_data, chunk_data_nbytes)); } p_ordered_selection_0[ndim] = p_ordered_selection_1[ndim]; } return BLOSC2_ERROR_SUCCESS; } int orthogonal_selection(b2nd_array_t *array, int64_t **selection, int64_t *selection_size, void *buffer, int64_t *buffershape, int64_t buffersize, bool get) { BLOSC_ERROR_NULL(array, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(selection, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(selection_size, BLOSC2_ERROR_NULL_POINTER); int8_t ndim = array->ndim; for (int i = 0; i < ndim; ++i) { BLOSC_ERROR_NULL(selection[i], BLOSC2_ERROR_NULL_POINTER); // Check that indexes are not larger than array shape for (int j = 0; j < selection_size[i]; ++j) { if (selection[i][j] > array->shape[i]) { BLOSC_ERROR(BLOSC2_ERROR_INVALID_INDEX); } } } // Check buffer size int64_t sel_size = array->sc->typesize; for (int i = 0; i < ndim; ++i) { sel_size *= selection_size[i]; } if (sel_size < buffersize) { BLOSC_ERROR(BLOSC2_ERROR_INVALID_PARAM); } // Sort selections b2nd_selection_t **ordered_selection = malloc(ndim * sizeof(b2nd_selection_t *)); BLOSC_ERROR_NULL(ordered_selection, BLOSC2_ERROR_MEMORY_ALLOC); for (int i = 0; i < ndim; ++i) { ordered_selection[i] = malloc(selection_size[i] * sizeof(b2nd_selection_t)); for (int j = 0; j < selection_size[i]; ++j) { ordered_selection[i][j].index = j; ordered_selection[i][j].value = selection[i][j]; } qsort(ordered_selection[i], selection_size[i], sizeof(b2nd_selection_t), compare_selection); } // Define pointers to iterate over ordered_selection data b2nd_selection_t **p_ordered_selection_0 = malloc(ndim * sizeof(b2nd_selection_t *)); BLOSC_ERROR_NULL(p_ordered_selection_0, BLOSC2_ERROR_MEMORY_ALLOC); b2nd_selection_t **p_ordered_selection_1 = malloc(ndim * sizeof(b2nd_selection_t *)); BLOSC_ERROR_NULL(p_ordered_selection_1, BLOSC2_ERROR_MEMORY_ALLOC); int64_t bufferstrides[B2ND_MAX_DIM]; bufferstrides[array->ndim - 1] = 1; for (int i = array->ndim - 2; i >= 0; --i) { bufferstrides[i] = bufferstrides[i + 1] * buffershape[i + 1]; } // Pre-allocate a single chunk decompression buffer, reused for every // chunk visited by iter_chunk, instead of malloc/free per chunk. int32_t chunk_data_nbytes = (int32_t)(array->extchunknitems * array->sc->typesize); uint8_t *chunk_data = malloc(chunk_data_nbytes); BLOSC_ERROR_NULL(chunk_data, BLOSC2_ERROR_MEMORY_ALLOC); BLOSC_ERROR(iter_chunk(array, 0, selection_size, ordered_selection, p_ordered_selection_0, p_ordered_selection_1, buffer, buffershape, bufferstrides, get, chunk_data, chunk_data_nbytes)); free(chunk_data); // Free allocated memory free(p_ordered_selection_0); free(p_ordered_selection_1); for (int i = 0; i < ndim; ++i) { free(ordered_selection[i]); } free(ordered_selection); return BLOSC2_ERROR_SUCCESS; } int b2nd_get_orthogonal_selection(const b2nd_array_t *array, int64_t **selection, int64_t *selection_size, void *buffer, int64_t *buffershape, int64_t buffersize) { return orthogonal_selection((b2nd_array_t *)array, selection, selection_size, buffer, buffershape, buffersize, true); } int b2nd_set_orthogonal_selection(b2nd_array_t *array, int64_t **selection, int64_t *selection_size, const void *buffer, int64_t *buffershape, int64_t buffersize) { return orthogonal_selection(array, selection, selection_size, (void*)buffer, buffershape, buffersize, false); } b2nd_context_t * b2nd_create_ctx(const blosc2_storage *b2_storage, int8_t ndim, const int64_t *shape, const int32_t *chunkshape, const int32_t *blockshape, const char *dtype, int8_t dtype_format, const blosc2_metalayer *metalayers, int32_t nmetalayers) { if (ndim < 0 || ndim > B2ND_MAX_DIM) { BLOSC_TRACE_ERROR("ndim must be in [0, %d]", B2ND_MAX_DIM); return NULL; } if (ndim > 0) { if (shape == NULL || chunkshape == NULL || blockshape == NULL) { BLOSC_TRACE_ERROR("shape, chunkshape and blockshape cannot be NULL when ndim > 0"); return NULL; } if (validate_shape_chunkshape_blockshape(ndim, shape, chunkshape, blockshape) < 0) { return NULL; } } b2nd_context_t *ctx = malloc(sizeof(b2nd_context_t)); BLOSC_ERROR_NULL(ctx, NULL); blosc2_storage *params_b2_storage = malloc(sizeof(blosc2_storage)); BLOSC_ERROR_NULL(params_b2_storage, NULL); if (b2_storage == NULL) { memcpy(params_b2_storage, &BLOSC2_STORAGE_DEFAULTS, sizeof(blosc2_storage)); } else { memcpy(params_b2_storage, b2_storage, sizeof(blosc2_storage)); } blosc2_cparams *cparams = malloc(sizeof(blosc2_cparams)); BLOSC_ERROR_NULL(cparams, NULL); // We need a copy of cparams mainly to be able to modify blocksize if (params_b2_storage->cparams == NULL) { memcpy(cparams, &BLOSC2_CPARAMS_DEFAULTS, sizeof(blosc2_cparams)); } else { memcpy(cparams, params_b2_storage->cparams, sizeof(blosc2_cparams)); } if (dtype == NULL) { // ctx->dtype = strdup(B2ND_DEFAULT_DTYPE); char buf[16] = {0}; snprintf(buf, sizeof(buf), "|S%d", cparams->typesize); ctx->dtype = strdup(buf); } else { ctx->dtype = strdup(dtype); } ctx->dtype_format = dtype_format; params_b2_storage->cparams = cparams; ctx->b2_storage = params_b2_storage; ctx->ndim = ndim; int32_t blocknitems = 1; for (int i = 0; i < ndim; i++) { ctx->shape[i] = shape[i]; ctx->chunkshape[i] = chunkshape[i]; ctx->blockshape[i] = blockshape[i]; blocknitems *= ctx->blockshape[i]; } cparams->blocksize = blocknitems * cparams->typesize; ctx->nmetalayers = nmetalayers; for (int i = 0; i < nmetalayers; ++i) { ctx->metalayers[i] = metalayers[i]; } #if defined(HAVE_ZFP) #include "blosc2/codecs-registry.h" if ((ctx->b2_storage->cparams->compcode >= BLOSC_CODEC_ZFP_FIXED_ACCURACY) && (ctx->b2_storage->cparams->compcode <= BLOSC_CODEC_ZFP_FIXED_RATE)) { for (int i = 0; i < BLOSC2_MAX_FILTERS; ++i) { if ((ctx->b2_storage->cparams->filters[i] == BLOSC_SHUFFLE) || (ctx->b2_storage->cparams->filters[i] == BLOSC_BITSHUFFLE)) { BLOSC_TRACE_ERROR("ZFP cannot be run in presence of SHUFFLE / BITSHUFFLE"); return NULL; } } } #endif /* HAVE_ZFP */ return ctx; } int b2nd_free_ctx(b2nd_context_t *ctx) { ctx->b2_storage->cparams->schunk = NULL; free(ctx->b2_storage->cparams); free(ctx->b2_storage); free(ctx->dtype); free(ctx); return BLOSC2_ERROR_SUCCESS; } c-blosc2-3.1.5/blosc/b2nd_utils.c000066400000000000000000000273671521743436100164640ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #include "b2nd.h" #include // copyNdim where N = {2-8} - specializations of copy loops to be used by b2nd_copy_buffer // since we don't have c++ templates, substitute manual specializations for up to known B2ND_MAX_DIM (8) // it's not pretty, but it substantially reduces overhead vs. the generic method void copy8dim(const int32_t itemsize, const int64_t *copy_shape, const uint8_t *bsrc, const int64_t *src_strides, uint8_t *bdst, const int64_t *dst_strides) { int64_t copy_nbytes = copy_shape[7] * itemsize; int64_t copy_start[7] = {0}; do { do { do { do { do { do { do { int64_t src_copy_start = 0; int64_t dst_copy_start = 0; for (int j = 0; j < 7; ++j) { src_copy_start += copy_start[j] * src_strides[j]; dst_copy_start += copy_start[j] * dst_strides[j]; } memcpy(&bdst[dst_copy_start * itemsize], &bsrc[src_copy_start * itemsize], copy_nbytes); ++copy_start[6]; } while (copy_start[6] < copy_shape[6]); ++copy_start[5]; copy_start[6] = 0; } while (copy_start[5] < copy_shape[5]); ++copy_start[4]; copy_start[5] = 0; } while (copy_start[4] < copy_shape[4]); ++copy_start[3]; copy_start[4] = 0; } while (copy_start[3] < copy_shape[3]); ++copy_start[2]; copy_start[3] = 0; } while (copy_start[2] < copy_shape[2]); ++copy_start[1]; copy_start[2] = 0; } while (copy_start[1] < copy_shape[1]); ++copy_start[0]; copy_start[1] = 0; } while (copy_start[0] < copy_shape[0]); } void copy7dim(const int32_t itemsize, const int64_t *copy_shape, const uint8_t *bsrc, const int64_t *src_strides, uint8_t *bdst, const int64_t *dst_strides) { int64_t copy_nbytes = copy_shape[6] * itemsize; int64_t copy_start[6] = {0}; do { do { do { do { do { do { int64_t src_copy_start = 0; int64_t dst_copy_start = 0; for (int j = 0; j < 6; ++j) { src_copy_start += copy_start[j] * src_strides[j]; dst_copy_start += copy_start[j] * dst_strides[j]; } memcpy(&bdst[dst_copy_start * itemsize], &bsrc[src_copy_start * itemsize], copy_nbytes); ++copy_start[5]; } while (copy_start[5] < copy_shape[5]); ++copy_start[4]; copy_start[5] = 0; } while (copy_start[4] < copy_shape[4]); ++copy_start[3]; copy_start[4] = 0; } while (copy_start[3] < copy_shape[3]); ++copy_start[2]; copy_start[3] = 0; } while (copy_start[2] < copy_shape[2]); ++copy_start[1]; copy_start[2] = 0; } while (copy_start[1] < copy_shape[1]); ++copy_start[0]; copy_start[1] = 0; } while (copy_start[0] < copy_shape[0]); } void copy6dim(const int32_t itemsize, const int64_t *copy_shape, const uint8_t *bsrc, const int64_t *src_strides, uint8_t *bdst, const int64_t *dst_strides) { int64_t copy_nbytes = copy_shape[5] * itemsize; int64_t copy_start[5] = {0}; do { do { do { do { do { int64_t src_copy_start = 0; int64_t dst_copy_start = 0; for (int j = 0; j < 5; ++j) { src_copy_start += copy_start[j] * src_strides[j]; dst_copy_start += copy_start[j] * dst_strides[j]; } memcpy(&bdst[dst_copy_start * itemsize], &bsrc[src_copy_start * itemsize], copy_nbytes); ++copy_start[4]; } while (copy_start[4] < copy_shape[4]); ++copy_start[3]; copy_start[4] = 0; } while (copy_start[3] < copy_shape[3]); ++copy_start[2]; copy_start[3] = 0; } while (copy_start[2] < copy_shape[2]); ++copy_start[1]; copy_start[2] = 0; } while (copy_start[1] < copy_shape[1]); ++copy_start[0]; copy_start[1] = 0; } while (copy_start[0] < copy_shape[0]); } void copy5dim(const int32_t itemsize, const int64_t *copy_shape, const uint8_t *bsrc, const int64_t *src_strides, uint8_t *bdst, const int64_t *dst_strides) { int64_t copy_nbytes = copy_shape[4] * itemsize; int64_t copy_start[4] = {0}; do { do { do { do { int64_t src_copy_start = 0; int64_t dst_copy_start = 0; for (int j = 0; j < 4; ++j) { src_copy_start += copy_start[j] * src_strides[j]; dst_copy_start += copy_start[j] * dst_strides[j]; } memcpy(&bdst[dst_copy_start * itemsize], &bsrc[src_copy_start * itemsize], copy_nbytes); ++copy_start[3]; } while (copy_start[3] < copy_shape[3]); ++copy_start[2]; copy_start[3] = 0; } while (copy_start[2] < copy_shape[2]); ++copy_start[1]; copy_start[2] = 0; } while (copy_start[1] < copy_shape[1]); ++copy_start[0]; copy_start[1] = 0; } while (copy_start[0] < copy_shape[0]); } void copy4dim(const int32_t itemsize, const int64_t *copy_shape, const uint8_t *bsrc, const int64_t *src_strides, uint8_t *bdst, const int64_t *dst_strides) { int64_t copy_nbytes = copy_shape[3] * itemsize; int64_t copy_start[3] = {0}; do { do { do { int64_t src_copy_start = 0; int64_t dst_copy_start = 0; for (int j = 0; j < 3; ++j) { src_copy_start += copy_start[j] * src_strides[j]; dst_copy_start += copy_start[j] * dst_strides[j]; } memcpy(&bdst[dst_copy_start * itemsize], &bsrc[src_copy_start * itemsize], copy_nbytes); ++copy_start[2]; } while (copy_start[2] < copy_shape[2]); ++copy_start[1]; copy_start[2] = 0; } while (copy_start[1] < copy_shape[1]); ++copy_start[0]; copy_start[1] = 0; } while (copy_start[0] < copy_shape[0]); } void copy3dim(const int32_t itemsize, const int64_t *copy_shape, const uint8_t *bsrc, const int64_t *src_strides, uint8_t *bdst, const int64_t *dst_strides) { int64_t copy_nbytes = copy_shape[2] * itemsize; int64_t copy_start[2] = {0}; do { do { int64_t src_copy_start = 0; int64_t dst_copy_start = 0; for (int j = 0; j < 2; ++j) { src_copy_start += copy_start[j] * src_strides[j]; dst_copy_start += copy_start[j] * dst_strides[j]; } memcpy(&bdst[dst_copy_start * itemsize], &bsrc[src_copy_start * itemsize], copy_nbytes); ++copy_start[1]; } while (copy_start[1] < copy_shape[1]); ++copy_start[0]; copy_start[1] = 0; } while (copy_start[0] < copy_shape[0]); } void copy2dim(const int32_t itemsize, const int64_t *copy_shape, const uint8_t *bsrc, const int64_t *src_strides, uint8_t *bdst, const int64_t *dst_strides) { int64_t copy_nbytes = copy_shape[1] * itemsize; int64_t copy_start = 0; do { int64_t src_copy_start = copy_start * src_strides[0]; int64_t dst_copy_start = copy_start * dst_strides[0]; memcpy(&bdst[dst_copy_start * itemsize], &bsrc[src_copy_start * itemsize], copy_nbytes); ++copy_start; } while (copy_start < copy_shape[0]); } void copy_ndim_fallback(const int8_t ndim, const int32_t itemsize, int64_t *copy_shape, const uint8_t *bsrc, int64_t *src_strides, uint8_t *bdst, int64_t *dst_strides) { int64_t copy_nbytes = copy_shape[ndim - 1] * itemsize; int64_t number_of_copies = 1; for (int i = 0; i < ndim - 1; ++i) { number_of_copies *= copy_shape[i]; } for (int ncopy = 0; ncopy < number_of_copies; ++ncopy) { // Compute the start of the copy int64_t copy_start[B2ND_MAX_DIM] = {0}; blosc2_unidim_to_multidim((int8_t) (ndim - 1), copy_shape, ncopy, copy_start); // Translate this index to the src buffer int64_t src_copy_start; blosc2_multidim_to_unidim(copy_start, (int8_t) (ndim - 1), src_strides, &src_copy_start); // Translate this index to the dst buffer int64_t dst_copy_start; blosc2_multidim_to_unidim(copy_start, (int8_t) (ndim - 1), dst_strides, &dst_copy_start); // Perform the copy memcpy(&bdst[dst_copy_start * itemsize], &bsrc[src_copy_start * itemsize], copy_nbytes); } } int b2nd_copy_buffer2(int8_t ndim, int32_t itemsize, const void *src, const int64_t *src_pad_shape, const int64_t *src_start, const int64_t *src_stop, void *dst, const int64_t *dst_pad_shape, const int64_t *dst_start) { // Compute the shape of the copy int64_t copy_shape[B2ND_MAX_DIM] = {0}; for (int i = 0; i < ndim; ++i) { copy_shape[i] = src_stop[i] - src_start[i]; if (copy_shape[i] == 0) { return BLOSC2_ERROR_SUCCESS; } } // Compute the strides int64_t src_strides[B2ND_MAX_DIM] = {0}; src_strides[ndim - 1] = 1; for (int i = ndim - 2; i >= 0; --i) { src_strides[i] = src_strides[i + 1] * src_pad_shape[i + 1]; } int64_t dst_strides[B2ND_MAX_DIM] = {0}; dst_strides[ndim - 1] = 1; for (int i = ndim - 2; i >= 0; --i) { dst_strides[i] = dst_strides[i + 1] * dst_pad_shape[i + 1]; } // Align the buffers removing unnecessary data int64_t src_start_n; blosc2_multidim_to_unidim(src_start, ndim, src_strides, &src_start_n); uint8_t *bsrc = (uint8_t *) src; bsrc = &bsrc[src_start_n * itemsize]; int64_t dst_start_n; blosc2_multidim_to_unidim(dst_start, ndim, dst_strides, &dst_start_n); uint8_t *bdst = (uint8_t *) dst; bdst = &bdst[dst_start_n * itemsize]; switch (ndim) { case 1: memcpy(&bdst[0], &bsrc[0], copy_shape[0] * itemsize); break; case 2: copy2dim(itemsize, copy_shape, bsrc, src_strides, bdst, dst_strides); break; case 3: copy3dim(itemsize, copy_shape, bsrc, src_strides, bdst, dst_strides); break; case 4: copy4dim(itemsize, copy_shape, bsrc, src_strides, bdst, dst_strides); break; case 5: copy5dim(itemsize, copy_shape, bsrc, src_strides, bdst, dst_strides); break; case 6: copy6dim(itemsize, copy_shape, bsrc, src_strides, bdst, dst_strides); break; case 7: copy7dim(itemsize, copy_shape, bsrc, src_strides, bdst, dst_strides); break; case 8: copy8dim(itemsize, copy_shape, bsrc, src_strides, bdst, dst_strides); break; default: // guard against potential future increase to B2ND_MAX_DIM copy_ndim_fallback(ndim, itemsize, copy_shape, bsrc, src_strides, bdst, dst_strides); break; } return BLOSC2_ERROR_SUCCESS; } // Keep the old signature for API compatibility int b2nd_copy_buffer(int8_t ndim, uint8_t itemsize, const void *src, const int64_t *src_pad_shape, const int64_t *src_start, const int64_t *src_stop, void *dst, const int64_t *dst_pad_shape, const int64_t *dst_start) { // Simply cast itemsize to int32_t and delegate return b2nd_copy_buffer2(ndim, (int32_t)itemsize, src, src_pad_shape, src_start, src_stop, dst, dst_pad_shape, dst_start); } c-blosc2-3.1.5/blosc/bitshuffle-altivec.c000066400000000000000000000543771521743436100202000ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /********************************************************************* Bitshuffle - Filter for improving compression of typed binary data. Author: Kiyoshi Masui Website: https://github.com/kiyo-masui/bitshuffle Note: Adapted for c-blosc by Francesc Alted Altivec/VSX version by Jerome Kieffer. See LICENSES/BITSHUFFLE.txt file for details about copyright and rights to use. **********************************************************************/ #include "bitshuffle-altivec.h" #include "bitshuffle-generic.h" #include /* Make sure ALTIVEC is available for the compilation target and compiler. vec_bperm requires POWER8 (ISA 2.07). VSX is needed for vec_xl/vec_xst. Exclude __APPLE__ because some GCC versions falsely define __VSX__ on powerpc-apple-darwin targets (GCC Bug #121696, cf. Eigen's workaround). */ #if defined(__ALTIVEC__) && defined(__VSX__) && defined(_ARCH_PWR8) && !defined(__APPLE__) /* vec_bperm places its result in different elements depending on byte order. On big-endian the result is in element 0; on little-endian it is in element 4 (of a uint16_t vector). */ #if defined(__BIG_ENDIAN__) || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) #define BLOSC_BPERM_RESULT_IDX 0 #else #define BLOSC_BPERM_RESULT_IDX 4 #endif #include "transpose-altivec.h" #include #include /* The next is useful for debugging purposes */ #if 0 #include #include static void helper_print(__vector uint8_t v, char* txt){ printf("%s %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",txt, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15]); } #endif static inline __vector uint8_t gen_save_mask(size_t offset){ __vector uint8_t mask; size_t k; for (k = 0; k < 16; k++) mask[k] = (k> 3]; *oui16 = tmp[BLOSC_BPERM_RESULT_IDX]; } } count = bshuf_trans_bit_byte_remainder(in, out, size, elem_size, nbyte - nbyte % 16); return count; } /* Transpose bits within elements. */ int64_t bshuf_trans_bit_elem_altivec(const void* in, void* out, const size_t size, const size_t elem_size) { int64_t count; CHECK_MULT_EIGHT(size); void* tmp_buf = malloc(size * elem_size); if (tmp_buf == NULL) return -1; count = bshuf_trans_byte_elem_altivec(in, out, size, elem_size, tmp_buf); CHECK_ERR(count); // bshuf_trans_bit_byte_altivec / bitshuffle1_altivec count = bshuf_trans_bit_byte_altivec(out, tmp_buf, size, elem_size); CHECK_ERR(count); count = bshuf_trans_bitrow_eight(tmp_buf, out, size, elem_size); free(tmp_buf); return count; } /* For data organized into a row for each bit (8 * elem_size rows), transpose * the bytes. */ int64_t bshuf_trans_byte_bitrow_altivec(const void* in, void* out, const size_t size, const size_t elem_size) { static const __vector uint8_t epi8_low = (const __vector uint8_t) { 0x00, 0x10, 0x01, 0x11, 0x02, 0x12, 0x03, 0x13, 0x04, 0x14, 0x05, 0x15, 0x06, 0x16, 0x07, 0x17}; static const __vector uint8_t epi8_hi = (const __vector uint8_t) { 0x08, 0x18, 0x09, 0x19, 0x0a, 0x1a, 0x0b, 0x1b, 0x0c, 0x1c, 0x0d, 0x1d, 0x0e, 0x1e, 0x0f, 0x1f}; static const __vector uint8_t epi16_low = (const __vector uint8_t) { 0x00, 0x01, 0x10, 0x11, 0x02, 0x03, 0x12, 0x13, 0x04, 0x05, 0x14, 0x15, 0x06, 0x07, 0x16, 0x17}; static const __vector uint8_t epi16_hi = (const __vector uint8_t) { 0x08, 0x09, 0x18, 0x19, 0x0a, 0x0b, 0x1a, 0x1b, 0x0c, 0x0d, 0x1c, 0x1d, 0x0e, 0x0f, 0x1e, 0x1f}; static const __vector uint8_t epi32_low = (const __vector uint8_t) { 0x00, 0x01, 0x02, 0x03, 0x10, 0x11, 0x12, 0x13, 0x04, 0x05, 0x06, 0x07, 0x14, 0x15, 0x16, 0x17}; static const __vector uint8_t epi32_hi = (const __vector uint8_t) { 0x08, 0x09, 0x0a, 0x0b, 0x18, 0x19, 0x1a, 0x1b, 0x0c, 0x0d, 0x0e, 0x0f, 0x1c, 0x1d, 0x1e, 0x1f}; static const __vector uint8_t epi64_low = (const __vector uint8_t) { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17}; static const __vector uint8_t epi64_hi = (const __vector uint8_t) { 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f}; const uint8_t* in_b = (const uint8_t*)in; uint8_t* out_b = (uint8_t*)out; size_t nrows = 8 * elem_size; size_t nbyte_row = size / 8; __vector uint8_t xmm0[16], xmm1[16]; CHECK_MULT_EIGHT(size); // The optimized algorithms can only deal with even values or 1 for elem_size if ((elem_size > 1) && (elem_size % 2)) { return bshuf_trans_byte_bitrow_scal(in, out, size, elem_size); } int nvectors = (elem_size == 1) ? 8 : 16; for (size_t ii = 0; ii + (nvectors - 1) < nrows; ii += nvectors) { for (size_t jj = 0; jj + 15 < nbyte_row; jj += 16) { // vectors of 16 elements if (elem_size == 1) { for (int k = 0; k < 8; k++) { xmm0[k] = vec_xl((ii + k) * nbyte_row + jj, in_b); } xmm1[0] = vec_perm(xmm0[0], xmm0[1], epi8_low); xmm1[1] = vec_perm(xmm0[2], xmm0[3], epi8_low); xmm1[2] = vec_perm(xmm0[4], xmm0[5], epi8_low); xmm1[3] = vec_perm(xmm0[6], xmm0[7], epi8_low); xmm1[4] = vec_perm(xmm0[0], xmm0[1], epi8_hi); xmm1[5] = vec_perm(xmm0[2], xmm0[3], epi8_hi); xmm1[6] = vec_perm(xmm0[4], xmm0[5], epi8_hi); xmm1[7] = vec_perm(xmm0[6], xmm0[7], epi8_hi); xmm0[0] = vec_perm(xmm1[0], xmm1[1], epi16_low); xmm0[1] = vec_perm(xmm1[2], xmm1[3], epi16_low); xmm0[2] = vec_perm(xmm1[0], xmm1[1], epi16_hi); xmm0[3] = vec_perm(xmm1[2], xmm1[3], epi16_hi); xmm0[4] = vec_perm(xmm1[4], xmm1[5], epi16_low); xmm0[5] = vec_perm(xmm1[6], xmm1[7], epi16_low); xmm0[6] = vec_perm(xmm1[4], xmm1[5], epi16_hi); xmm0[7] = vec_perm(xmm1[6], xmm1[7], epi16_hi); xmm1[0] = vec_perm(xmm0[0], xmm0[1], epi32_low); xmm1[1] = vec_perm(xmm0[0], xmm0[1], epi32_hi); xmm1[2] = vec_perm(xmm0[2], xmm0[3], epi32_low); xmm1[3] = vec_perm(xmm0[2], xmm0[3], epi32_hi); xmm1[4] = vec_perm(xmm0[4], xmm0[5], epi32_low); xmm1[5] = vec_perm(xmm0[4], xmm0[5], epi32_hi); xmm1[6] = vec_perm(xmm0[6], xmm0[7], epi32_low); xmm1[7] = vec_perm(xmm0[6], xmm0[7], epi32_hi); for (int k = 0; k < 8; k++) { vec_xst(xmm1[k], (jj + k * 2) * nrows + ii, out_b); } continue; } for (int k = 0; k < 16; k++) { xmm0[k] = vec_xl((ii + k) * nbyte_row + jj, in_b); } for (int k = 0; k < 16; k += 8) { xmm1[k + 0] = vec_perm(xmm0[k + 0], xmm0[k + 1], epi8_low); xmm1[k + 1] = vec_perm(xmm0[k + 2], xmm0[k + 3], epi8_low); xmm1[k + 2] = vec_perm(xmm0[k + 4], xmm0[k + 5], epi8_low); xmm1[k + 3] = vec_perm(xmm0[k + 6], xmm0[k + 7], epi8_low); xmm1[k + 4] = vec_perm(xmm0[k + 0], xmm0[k + 1], epi8_hi); xmm1[k + 5] = vec_perm(xmm0[k + 2], xmm0[k + 3], epi8_hi); xmm1[k + 6] = vec_perm(xmm0[k + 4], xmm0[k + 5], epi8_hi); xmm1[k + 7] = vec_perm(xmm0[k + 6], xmm0[k + 7], epi8_hi); } for (int k = 0; k < 16; k += 8) { xmm0[k + 0] = vec_perm(xmm1[k + 0], xmm1[k + 1], epi16_low); xmm0[k + 1] = vec_perm(xmm1[k + 2], xmm1[k + 3], epi16_low); xmm0[k + 2] = vec_perm(xmm1[k + 0], xmm1[k + 1], epi16_hi); xmm0[k + 3] = vec_perm(xmm1[k + 2], xmm1[k + 3], epi16_hi); xmm0[k + 4] = vec_perm(xmm1[k + 4], xmm1[k + 5], epi16_low); xmm0[k + 5] = vec_perm(xmm1[k + 6], xmm1[k + 7], epi16_low); xmm0[k + 6] = vec_perm(xmm1[k + 4], xmm1[k + 5], epi16_hi); xmm0[k + 7] = vec_perm(xmm1[k + 6], xmm1[k + 7], epi16_hi); } for (int k = 0; k < 16; k += 8) { xmm1[k + 0] = vec_perm(xmm0[k + 0], xmm0[k + 1], epi32_low); xmm1[k + 1] = vec_perm(xmm0[k + 0], xmm0[k + 1], epi32_hi); xmm1[k + 2] = vec_perm(xmm0[k + 2], xmm0[k + 3], epi32_low); xmm1[k + 3] = vec_perm(xmm0[k + 2], xmm0[k + 3], epi32_hi); xmm1[k + 4] = vec_perm(xmm0[k + 4], xmm0[k + 5], epi32_low); xmm1[k + 5] = vec_perm(xmm0[k + 4], xmm0[k + 5], epi32_hi); xmm1[k + 6] = vec_perm(xmm0[k + 6], xmm0[k + 7], epi32_low); xmm1[k + 7] = vec_perm(xmm0[k + 6], xmm0[k + 7], epi32_hi); } for (int k = 0; k < 8; k += 4) { xmm0[k * 2 + 0] = vec_perm(xmm1[k + 0], xmm1[k + 8], epi64_low); xmm0[k * 2 + 1] = vec_perm(xmm1[k + 0], xmm1[k + 8], epi64_hi); xmm0[k * 2 + 2] = vec_perm(xmm1[k + 1], xmm1[k + 9], epi64_low); xmm0[k * 2 + 3] = vec_perm(xmm1[k + 1], xmm1[k + 9], epi64_hi); xmm0[k * 2 + 4] = vec_perm(xmm1[k + 2], xmm1[k + 10], epi64_low); xmm0[k * 2 + 5] = vec_perm(xmm1[k + 2], xmm1[k + 10], epi64_hi); xmm0[k * 2 + 6] = vec_perm(xmm1[k + 3], xmm1[k + 11], epi64_low); xmm0[k * 2 + 7] = vec_perm(xmm1[k + 3], xmm1[k + 11], epi64_hi); } for (int k = 0; k < 16; k++) { vec_xst(xmm0[k], (jj + k) * nrows + ii, out_b); } } // Copy the remainder for (size_t jj = nbyte_row - nbyte_row % 16; jj < nbyte_row; jj++) { for (int k = 0; k < nvectors; k++) { out_b[jj * nrows + ii + k] = in_b[(ii + k) * nbyte_row + jj]; } } } return size * elem_size; } /* Shuffle bits within the bytes of eight element blocks. */ int64_t bshuf_shuffle_bit_eightelem_altivec(const void* in, void* out, const size_t size, const size_t elem_size) { /* With a bit of care, this could be written such that such that it is */ /* in_buf = out_buf safe. */ const uint8_t* in_b = (const uint8_t*)in; uint8_t* out_b = (uint8_t*)out; size_t nbyte = elem_size * size; __vector uint8_t masks[8], data; CHECK_MULT_EIGHT(size); // Generate all 8 needed masks for (int kk = 0; kk < 8; kk++){ masks[kk] = make_bitperm_mask(1, kk); } if (elem_size % 2) { bshuf_shuffle_bit_eightelem_scal(in, out, size, elem_size); } else { for (size_t ii = 0; ii + 8 * elem_size - 1 < nbyte; ii += 8 * elem_size) { for (size_t jj = 0; jj + 15 < 8 * elem_size; jj += 16) { data = vec_xl(ii + jj, in_b); for (size_t kk = 0; kk < 8; kk++) { __vector uint16_t tmp; uint16_t* oui16; tmp = (__vector uint16_t) vec_bperm(data, masks[kk]); oui16 = (uint16_t*)&out_b[ii + (jj>>3) + kk * elem_size]; *oui16 = tmp[BLOSC_BPERM_RESULT_IDX]; } } } } return size * elem_size; } /* Untranspose bits within elements. */ int64_t bshuf_untrans_bit_elem_altivec(const void* in, void* out, const size_t size, const size_t elem_size) { int64_t count; CHECK_MULT_EIGHT(size); void* tmp_buf = malloc(size * elem_size); if (tmp_buf == NULL) return -1; count = bshuf_trans_byte_bitrow_altivec(in, tmp_buf, size, elem_size); CHECK_ERR(count); count = bshuf_shuffle_bit_eightelem_altivec(tmp_buf, out, size, elem_size); free(tmp_buf); return count; } const bool is_bshuf_altivec = true; #else /* VSX + POWER8 path */ const bool is_bshuf_altivec = false; int64_t bshuf_trans_bit_elem_altivec(const void* in, void* out, const size_t size, const size_t elem_size) { abort(); } int64_t bshuf_untrans_bit_elem_altivec(const void* in, void* out, const size_t size, const size_t elem_size) { abort(); } #endif /* defined(__ALTIVEC__) && defined(__VSX__) && defined(_ARCH_PWR8) && !defined(__APPLE__) */ c-blosc2-3.1.5/blosc/bitshuffle-altivec.h000066400000000000000000000032531521743436100201700ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /* ALTIVEC-accelerated shuffle/unshuffle routines. */ #ifndef BLOSC_BITSHUFFLE_ALTIVEC_H #define BLOSC_BITSHUFFLE_ALTIVEC_H #include "blosc2/blosc2-common.h" #include #include #include /** * ALTIVEC-accelerated bit(un)shuffle routines availability. */ extern const bool is_bshuf_altivec; BLOSC_NO_EXPORT int64_t bshuf_trans_byte_elem_altivec(const void* in, void* out, const size_t size, const size_t elem_size, void* tmp_buf); BLOSC_NO_EXPORT int64_t bshuf_trans_byte_bitrow_altivec(const void* in, void* out, const size_t size, const size_t elem_size); BLOSC_NO_EXPORT int64_t bshuf_shuffle_bit_eightelem_altivec(const void* in, void* out, const size_t size, const size_t elem_size); /** ALTIVEC-accelerated bitshuffle routine. */ BLOSC_NO_EXPORT int64_t bshuf_trans_bit_elem_altivec(const void* in, void* out, const size_t size, const size_t elem_size); /** ALTIVEC-accelerated bitunshuffle routine. */ BLOSC_NO_EXPORT int64_t bshuf_untrans_bit_elem_altivec(const void* in, void* out, const size_t size, const size_t elem_size); #endif /* BLOSC_BITSHUFFLE_ALTIVEC_H */ c-blosc2-3.1.5/blosc/bitshuffle-avx2.c000066400000000000000000000206711521743436100174170ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /********************************************************************* Bitshuffle - Filter for improving compression of typed binary data. Author: Kiyoshi Masui Website: https://github.com/kiyo-masui/bitshuffle Note: Adapted for c-blosc by Francesc Alted. See LICENSES/BITSHUFFLE.txt file for details about copyright and rights to use. **********************************************************************/ #include "bitshuffle-avx2.h" #include "bitshuffle-sse2.h" #include "bitshuffle-generic.h" #include /* Make sure AVX2 is available for the compilation target and compiler. */ #if defined(__AVX2__) #include /* The next is useful for debugging purposes */ #if 0 #include #include static void printymm(__m256i ymm0) { uint8_t buf[32]; ((__m256i *)buf)[0] = ymm0; printf("%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15], buf[16], buf[17], buf[18], buf[19], buf[20], buf[21], buf[22], buf[23], buf[24], buf[25], buf[26], buf[27], buf[28], buf[29], buf[30], buf[31]); } #endif /* ---- Code that requires AVX2. Intel Haswell (2013) and later. ---- */ /* Transpose bits within bytes. */ int64_t bshuf_trans_bit_byte_AVX(const void* in, void* out, const size_t size, const size_t elem_size) { size_t ii, kk; const char* in_b = (const char*) in; char* out_b = (char*) out; int32_t* out_i32; size_t nbyte = elem_size * size; int64_t count; __m256i ymm; int32_t bt; for (ii = 0; ii + 31 < nbyte; ii += 32) { ymm = _mm256_loadu_si256((__m256i *) &in_b[ii]); for (kk = 0; kk < 8; kk++) { bt = _mm256_movemask_epi8(ymm); ymm = _mm256_slli_epi16(ymm, 1); out_i32 = (int32_t*) &out_b[((7 - kk) * nbyte + ii) / 8]; *out_i32 = bt; } } count = bshuf_trans_bit_byte_remainder(in, out, size, elem_size, nbyte - nbyte % 32); return count; } /* Transpose bits within elements. */ int64_t bshuf_trans_bit_elem_AVX(const void* in, void* out, const size_t size, const size_t elem_size) { int64_t count; CHECK_MULT_EIGHT(size); void* tmp_buf = malloc(size * elem_size); if (tmp_buf == NULL) return -1; count = bshuf_trans_byte_elem_SSE(in, out, size, elem_size); CHECK_ERR_FREE(count, tmp_buf); count = bshuf_trans_bit_byte_AVX(out, tmp_buf, size, elem_size); CHECK_ERR_FREE(count, tmp_buf); count = bshuf_trans_bitrow_eight(tmp_buf, out, size, elem_size); free(tmp_buf); return count; } /* For data organized into a row for each bit (8 * elem_size rows), transpose * the bytes. */ int64_t bshuf_trans_byte_bitrow_AVX(const void* in, void* out, const size_t size, const size_t elem_size) { size_t hh, ii, jj, kk, mm; const char* in_b = (const char*) in; char* out_b = (char*) out; CHECK_MULT_EIGHT(size); size_t nrows = 8 * elem_size; size_t nbyte_row = size / 8; if (elem_size % 4) return bshuf_trans_byte_bitrow_SSE(in, out, size, elem_size); __m256i ymm_0[8]; __m256i ymm_1[8]; __m256i ymm_storeage[8][4]; for (jj = 0; jj + 31 < nbyte_row; jj += 32) { for (ii = 0; ii + 3 < elem_size; ii += 4) { for (hh = 0; hh < 4; hh ++) { for (kk = 0; kk < 8; kk ++){ ymm_0[kk] = _mm256_loadu_si256((__m256i *) &in_b[ (ii * 8 + hh * 8 + kk) * nbyte_row + jj]); } for (kk = 0; kk < 4; kk ++){ ymm_1[kk] = _mm256_unpacklo_epi8(ymm_0[kk * 2], ymm_0[kk * 2 + 1]); ymm_1[kk + 4] = _mm256_unpackhi_epi8(ymm_0[kk * 2], ymm_0[kk * 2 + 1]); } for (kk = 0; kk < 2; kk ++){ for (mm = 0; mm < 2; mm ++){ ymm_0[kk * 4 + mm] = _mm256_unpacklo_epi16( ymm_1[kk * 4 + mm * 2], ymm_1[kk * 4 + mm * 2 + 1]); ymm_0[kk * 4 + mm + 2] = _mm256_unpackhi_epi16( ymm_1[kk * 4 + mm * 2], ymm_1[kk * 4 + mm * 2 + 1]); } } for (kk = 0; kk < 4; kk ++){ ymm_1[kk * 2] = _mm256_unpacklo_epi32(ymm_0[kk * 2], ymm_0[kk * 2 + 1]); ymm_1[kk * 2 + 1] = _mm256_unpackhi_epi32(ymm_0[kk * 2], ymm_0[kk * 2 + 1]); } for (kk = 0; kk < 8; kk ++){ ymm_storeage[kk][hh] = ymm_1[kk]; } } for (mm = 0; mm < 8; mm ++) { for (kk = 0; kk < 4; kk ++){ ymm_0[kk] = ymm_storeage[mm][kk]; } ymm_1[0] = _mm256_unpacklo_epi64(ymm_0[0], ymm_0[1]); ymm_1[1] = _mm256_unpacklo_epi64(ymm_0[2], ymm_0[3]); ymm_1[2] = _mm256_unpackhi_epi64(ymm_0[0], ymm_0[1]); ymm_1[3] = _mm256_unpackhi_epi64(ymm_0[2], ymm_0[3]); ymm_0[0] = _mm256_permute2x128_si256(ymm_1[0], ymm_1[1], 32); ymm_0[1] = _mm256_permute2x128_si256(ymm_1[2], ymm_1[3], 32); ymm_0[2] = _mm256_permute2x128_si256(ymm_1[0], ymm_1[1], 49); ymm_0[3] = _mm256_permute2x128_si256(ymm_1[2], ymm_1[3], 49); _mm256_storeu_si256((__m256i *) &out_b[ (jj + mm * 2 + 0 * 16) * nrows + ii * 8], ymm_0[0]); _mm256_storeu_si256((__m256i *) &out_b[ (jj + mm * 2 + 0 * 16 + 1) * nrows + ii * 8], ymm_0[1]); _mm256_storeu_si256((__m256i *) &out_b[ (jj + mm * 2 + 1 * 16) * nrows + ii * 8], ymm_0[2]); _mm256_storeu_si256((__m256i *) &out_b[ (jj + mm * 2 + 1 * 16 + 1) * nrows + ii * 8], ymm_0[3]); } } } for (ii = 0; ii < nrows; ii ++ ) { for (jj = nbyte_row - nbyte_row % 32; jj < nbyte_row; jj ++) { out_b[jj * nrows + ii] = in_b[ii * nbyte_row + jj]; } } return size * elem_size; } /* Shuffle bits within the bytes of eight element blocks. */ int64_t bshuf_shuffle_bit_eightelem_AVX(const void* in, void* out, const size_t size, const size_t elem_size) { CHECK_MULT_EIGHT(size); // With a bit of care, this could be written such that such that it is // in_buf = out_buf safe. const char* in_b = (const char*) in; char* out_b = (char*) out; size_t ii, jj, kk; size_t nbyte = elem_size * size; __m256i ymm; int32_t bt; if (elem_size % 4) { return bshuf_shuffle_bit_eightelem_SSE(in, out, size, elem_size); } else { for (jj = 0; jj + 31 < 8 * elem_size; jj += 32) { for (ii = 0; ii + 8 * elem_size - 1 < nbyte; ii += 8 * elem_size) { ymm = _mm256_loadu_si256((__m256i *) &in_b[ii + jj]); for (kk = 0; kk < 8; kk++) { bt = _mm256_movemask_epi8(ymm); ymm = _mm256_slli_epi16(ymm, 1); size_t ind = (ii + jj / 8 + (7 - kk) * elem_size); * (int32_t *) &out_b[ind] = bt; } } } } return size * elem_size; } /* Untranspose bits within elements. */ int64_t bshuf_untrans_bit_elem_AVX(const void* in, void* out, const size_t size, const size_t elem_size) { int64_t count; CHECK_MULT_EIGHT(size); void* tmp_buf = malloc(size * elem_size); if (tmp_buf == NULL) return -1; count = bshuf_trans_byte_bitrow_AVX(in, tmp_buf, size, elem_size); CHECK_ERR_FREE(count, tmp_buf); count = bshuf_shuffle_bit_eightelem_AVX(tmp_buf, out, size, elem_size); free(tmp_buf); return count; } const bool is_bshuf_AVX = true; #else /* defined(__AVX2__) */ const bool is_bshuf_AVX = false; int64_t bshuf_trans_bit_elem_AVX(const void* in, void* out, const size_t size, const size_t elem_size) { abort(); } int64_t bshuf_untrans_bit_elem_AVX(const void* in, void* out, const size_t size, const size_t elem_size) { abort(); } #endif /* defined(__AVX2__) */ c-blosc2-3.1.5/blosc/bitshuffle-avx2.h000066400000000000000000000027211521743436100174200ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /* AVX2-accelerated shuffle/unshuffle routines. */ #ifndef BLOSC_BITSHUFFLE_AVX2_H #define BLOSC_BITSHUFFLE_AVX2_H #include "blosc2/blosc2-common.h" #include #include #include /** * AVX2-accelerated bit(un)shuffle routines availability. */ extern const bool is_bshuf_AVX; /** * AVX2-accelerated bitshuffle routine. */ BLOSC_NO_EXPORT int64_t bshuf_trans_bit_elem_AVX(const void* in, void* out, const size_t size, const size_t elem_size); /** * AVX2-accelerated bitunshuffle routine. */ BLOSC_NO_EXPORT int64_t bshuf_untrans_bit_elem_AVX(const void* in, void* out, const size_t size, const size_t elem_size); /** * AVX2 utils used by AVX512 functions */ int64_t bshuf_shuffle_bit_eightelem_AVX(const void* in, void* out, const size_t size, const size_t elem_size); int64_t bshuf_trans_byte_bitrow_AVX(const void* in, void* out, const size_t size, const size_t elem_size); #endif /* BLOSC_BITSHUFFLE_AVX2_H */ c-blosc2-3.1.5/blosc/bitshuffle-avx512.c000066400000000000000000000122111521743436100175540ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /********************************************************************* Bitshuffle - Filter for improving compression of typed binary data. Author: Kiyoshi Masui Website: https://github.com/kiyo-masui/bitshuffle Note: Adapted for c-blosc2 by Francesc Alted. See LICENSES/BITSHUFFLE.txt file for details about copyright and rights to use. **********************************************************************/ #include "bitshuffle-avx512.h" #include "bitshuffle-avx2.h" #include "bitshuffle-sse2.h" #include "bitshuffle-generic.h" #include /* Make sure AVX512 is available for the compilation target and compiler. */ #if defined(__AVX512F__) && defined (__AVX512BW__) #include /* Transpose bits within bytes. */ int64_t bshuf_trans_bit_byte_AVX512(const void* in, void* out, const size_t size, const size_t elem_size) { size_t ii, kk; const char* in_b = (const char*) in; char* out_b = (char*) out; size_t nbyte = elem_size * size; int64_t count; int64_t* out_i64; __m512i zmm; __mmask64 bt; if (nbyte >= 64) { const __m512i mask = _mm512_set1_epi8(0); for (ii = 0; ii + 63 < nbyte; ii += 64) { zmm = _mm512_loadu_si512((__m512i *) &in_b[ii]); for (kk = 0; kk < 8; kk++) { bt = _mm512_cmp_epi8_mask(zmm, mask, 1); zmm = _mm512_slli_epi16(zmm, 1); out_i64 = (int64_t*) &out_b[((7 - kk) * nbyte + ii) / 8]; *out_i64 = (int64_t)bt; } } } __m256i ymm; int32_t bt32; int32_t* out_i32; size_t start = nbyte - nbyte % 64; for (ii = start; ii + 31 < nbyte; ii += 32) { ymm = _mm256_loadu_si256((__m256i *) &in_b[ii]); for (kk = 0; kk < 8; kk++) { bt32 = _mm256_movemask_epi8(ymm); ymm = _mm256_slli_epi16(ymm, 1); out_i32 = (int32_t*) &out_b[((7 - kk) * nbyte + ii) / 8]; *out_i32 = bt32; } } count = bshuf_trans_bit_byte_remainder(in, out, size, elem_size, nbyte - nbyte % 64 % 32); return count; } /* Transpose bits within elements. */ int64_t bshuf_trans_bit_elem_AVX512(const void* in, void* out, const size_t size, const size_t elem_size) { int64_t count; CHECK_MULT_EIGHT(size); void* tmp_buf = malloc(size * elem_size); if (tmp_buf == NULL) return -1; count = bshuf_trans_byte_elem_SSE(in, out, size, elem_size); CHECK_ERR_FREE(count, tmp_buf); count = bshuf_trans_bit_byte_AVX512(out, tmp_buf, size, elem_size); CHECK_ERR_FREE(count, tmp_buf); count = bshuf_trans_bitrow_eight(tmp_buf, out, size, elem_size); free(tmp_buf); return count; } /* Shuffle bits within the bytes of eight element blocks. */ int64_t bshuf_shuffle_bit_eightelem_AVX512(const void* in, void* out, const size_t size, const size_t elem_size) { CHECK_MULT_EIGHT(size); // With a bit of care, this could be written such that such that it is // in_buf = out_buf safe. const char* in_b = (const char*) in; char* out_b = (char*) out; size_t ii, jj, kk; size_t nbyte = elem_size * size; __m512i zmm; __mmask64 bt; if (elem_size % 8) { return bshuf_shuffle_bit_eightelem_AVX(in, out, size, elem_size); } else { const __m512i mask = _mm512_set1_epi8(0); for (jj = 0; jj + 63 < 8 * elem_size; jj += 64) { for (ii = 0; ii + 8 * elem_size - 1 < nbyte; ii += 8 * elem_size) { zmm = _mm512_loadu_si512((__m512i *) &in_b[ii + jj]); for (kk = 0; kk < 8; kk++) { bt = _mm512_cmp_epi8_mask(zmm, mask, 1); zmm = _mm512_slli_epi16(zmm, 1); size_t ind = (ii + jj / 8 + (7 - kk) * elem_size); * (int64_t *) &out_b[ind] = bt; } } } } return size * elem_size; } /* Untranspose bits within elements. */ int64_t bshuf_untrans_bit_elem_AVX512(const void* in, void* out, const size_t size, const size_t elem_size) { int64_t count; CHECK_MULT_EIGHT(size); void* tmp_buf = malloc(size * elem_size); if (tmp_buf == NULL) return -1; count = bshuf_trans_byte_bitrow_AVX(in, tmp_buf, size, elem_size); CHECK_ERR_FREE(count, tmp_buf); count = bshuf_shuffle_bit_eightelem_AVX512(tmp_buf, out, size, elem_size); free(tmp_buf); return count; } const bool is_bshuf_AVX512 = true; #else /* defined(__AVX512F__) && defined (__AVX512BW__) */ const bool is_bshuf_AVX512 = false; int64_t bshuf_trans_bit_elem_AVX512(const void* in, void* out, const size_t size, const size_t elem_size) { abort(); } int64_t bshuf_untrans_bit_elem_AVX512(const void* in, void* out, const size_t size, const size_t elem_size) { abort(); } #endif /* defined(__AVX512F__) && defined (__AVX512BW__) */ c-blosc2-3.1.5/blosc/bitshuffle-avx512.h000066400000000000000000000020621521743436100175640ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /* AVX512-accelerated shuffle/unshuffle routines. */ #ifndef BLOSC_BITSHUFFLE_AVX512_H #define BLOSC_BITSHUFFLE_AVX512_H #include "blosc2/blosc2-common.h" #include #include #include /** * AVX512-accelerated bit(un)shuffle routines availability. */ extern const bool is_bshuf_AVX512; BLOSC_NO_EXPORT int64_t bshuf_trans_bit_elem_AVX512(const void* in, void* out, const size_t size, const size_t elem_size); BLOSC_NO_EXPORT int64_t bshuf_untrans_bit_elem_AVX512(const void* in, void* out, const size_t size, const size_t elem_size); #endif /* BLOSC_BITSHUFFLE_AVX512_H */ c-blosc2-3.1.5/blosc/bitshuffle-generic.c000066400000000000000000000157001521743436100201500ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #include "bitshuffle-generic.h" #include #ifdef _MSC_VER #pragma warning (push) #pragma warning (disable: 4146) #endif /* Memory copy with bshuf call signature. For testing and profiling. */ int64_t bshuf_copy(const void* in, void* out, const size_t size, const size_t elem_size) { const char* in_b = (const char*) in; char* out_b = (char*) out; memcpy(out_b, in_b, size * elem_size); return size * elem_size; } /* Transpose bytes within elements, starting partway through input. */ int64_t bshuf_trans_byte_elem_remainder(const void* in, void* out, const size_t size, const size_t elem_size, const size_t start) { size_t ii, jj, kk; const char* in_b = (const char*) in; char* out_b = (char*) out; CHECK_MULT_EIGHT(start); if (size > start) { // ii loop separated into 2 loops so the compiler can unroll // the inner one. for (ii = start; ii + 7 < size; ii += 8) { for (jj = 0; jj < elem_size; jj++) { for (kk = 0; kk < 8; kk++) { out_b[jj * size + ii + kk] = in_b[ii * elem_size + kk * elem_size + jj]; } } } for (ii = size - size % 8; ii < size; ii ++) { for (jj = 0; jj < elem_size; jj++) { out_b[jj * size + ii] = in_b[ii * elem_size + jj]; } } } return size * elem_size; } /* Transpose bytes within elements. */ int64_t bshuf_trans_byte_elem_scal(const void* in, void* out, const size_t size, const size_t elem_size) { return bshuf_trans_byte_elem_remainder(in, out, size, elem_size, 0); } /* Transpose bits within bytes. */ int64_t bshuf_trans_bit_byte_remainder(const void* in, void* out, const size_t size, const size_t elem_size, const size_t start_byte) { const uint64_t* in_b = (const uint64_t*) in; uint8_t* out_b = (uint8_t*) out; uint64_t x, t; size_t ii, kk; size_t nbyte = elem_size * size; size_t nbyte_bitrow = nbyte / 8; uint64_t e=1; const int little_endian = *(uint8_t *) &e == 1; const size_t bit_row_skip = little_endian ? nbyte_bitrow : -nbyte_bitrow; const int64_t bit_row_offset = little_endian ? 0 : 7 * nbyte_bitrow; CHECK_MULT_EIGHT(nbyte); CHECK_MULT_EIGHT(start_byte); for (ii = start_byte / 8; ii < nbyte_bitrow; ii ++) { x = in_b[ii]; if (little_endian) { TRANS_BIT_8X8(x, t); } else { TRANS_BIT_8X8_BE(x, t); } for (kk = 0; kk < 8; kk ++) { out_b[bit_row_offset + kk * bit_row_skip + ii] = x; x = x >> 8; } } return size * elem_size; } /* Transpose bits within bytes. */ int64_t bshuf_trans_bit_byte_scal(const void* in, void* out, const size_t size, const size_t elem_size) { return bshuf_trans_bit_byte_remainder(in, out, size, elem_size, 0); } /* General transpose of an array, optimized for large element sizes. */ int64_t bshuf_trans_elem(const void* in, void* out, const size_t lda, const size_t ldb, const size_t elem_size) { size_t ii, jj; const char* in_b = (const char*) in; char* out_b = (char*) out; for(ii = 0; ii < lda; ii++) { for(jj = 0; jj < ldb; jj++) { memcpy(&out_b[(jj*lda + ii) * elem_size], &in_b[(ii*ldb + jj) * elem_size], elem_size); } } return lda * ldb * elem_size; } /* Transpose rows of shuffled bits (size / 8 bytes) within groups of 8. */ int64_t bshuf_trans_bitrow_eight(const void* in, void* out, const size_t size, const size_t elem_size) { size_t nbyte_bitrow = size / 8; CHECK_MULT_EIGHT(size); return bshuf_trans_elem(in, out, 8, elem_size, nbyte_bitrow); } /* Transpose bits within elements. */ int64_t bshuf_trans_bit_elem_scal(const void* in, void* out, const size_t size, const size_t elem_size) { int64_t count; void *tmp_buf; CHECK_MULT_EIGHT(size); tmp_buf = malloc(size * elem_size); if (tmp_buf == NULL) return -1; count = bshuf_trans_byte_elem_scal(in, out, size, elem_size); CHECK_ERR_FREE(count, tmp_buf); count = bshuf_trans_bit_byte_scal(out, tmp_buf, size, elem_size); CHECK_ERR_FREE(count, tmp_buf); count = bshuf_trans_bitrow_eight(tmp_buf, out, size, elem_size); free(tmp_buf); return count; } /* For data organized into a row for each bit (8 * elem_size rows), transpose * the bytes. */ int64_t bshuf_trans_byte_bitrow_scal(const void* in, void* out, const size_t size, const size_t elem_size) { size_t ii, jj, kk, nbyte_row; const char *in_b; char *out_b; in_b = (const char*) in; out_b = (char*) out; nbyte_row = size / 8; CHECK_MULT_EIGHT(size); for (jj = 0; jj < elem_size; jj++) { for (ii = 0; ii < nbyte_row; ii++) { for (kk = 0; kk < 8; kk++) { out_b[ii * 8 * elem_size + jj * 8 + kk] = \ in_b[(jj * 8 + kk) * nbyte_row + ii]; } } } return size * elem_size; } /* Shuffle bits within the bytes of eight element blocks. */ int64_t bshuf_shuffle_bit_eightelem_scal(const void* in, void* out, \ const size_t size, const size_t elem_size) { const char *in_b; char *out_b; uint64_t x, t; size_t ii, jj, kk; size_t nbyte, out_index; uint64_t e=1; const int little_endian = *(uint8_t *) &e == 1; const size_t elem_skip = little_endian ? elem_size : -elem_size; const uint64_t elem_offset = little_endian ? 0 : 7 * elem_size; CHECK_MULT_EIGHT(size); in_b = (const char*) in; out_b = (char*) out; nbyte = elem_size * size; for (jj = 0; jj < 8 * elem_size; jj += 8) { for (ii = 0; ii + 8 * elem_size - 1 < nbyte; ii += 8 * elem_size) { x = *((uint64_t*) &in_b[ii + jj]); if (little_endian) { TRANS_BIT_8X8(x, t); } else { TRANS_BIT_8X8_BE(x, t); } for (kk = 0; kk < 8; kk++) { out_index = ii + jj / 8 + elem_offset + kk * elem_skip; *((uint8_t*) &out_b[out_index]) = x; x = x >> 8; } } } return size * elem_size; } /* Untranspose bits within elements. */ int64_t bshuf_untrans_bit_elem_scal(const void* in, void* out, const size_t size, const size_t elem_size) { int64_t count; void *tmp_buf; CHECK_MULT_EIGHT(size); tmp_buf = malloc(size * elem_size); if (tmp_buf == NULL) return -1; count = bshuf_trans_byte_bitrow_scal(in, tmp_buf, size, elem_size); CHECK_ERR_FREE(count, tmp_buf); count = bshuf_shuffle_bit_eightelem_scal(tmp_buf, out, size, elem_size); free(tmp_buf); return count; } #ifdef _MSC_VER #pragma warning (pop) #endif c-blosc2-3.1.5/blosc/bitshuffle-generic.h000066400000000000000000000156031521743436100201570ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /* * Bitshuffle - Filter for improving compression of typed binary data. * * Author: Kiyoshi Masui * Website: http://www.github.com/kiyo-masui/bitshuffle * Created: 2014 * */ /* Generic (non-hardware-accelerated) shuffle/unshuffle routines. These are used when hardware-accelerated functions aren't available for a particular platform; they are also used by the hardware- accelerated functions to handle any remaining elements in a block which isn't a multiple of the hardware's vector size. */ #ifndef BLOSC_BITSHUFFLE_GENERIC_H #define BLOSC_BITSHUFFLE_GENERIC_H #include "blosc2/blosc2-common.h" #include #include // Macros. #define CHECK_MULT_EIGHT(n) if (n % 8) return -80; #define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) #define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) #define CHECK_ERR(count) \ do { \ if ((count) < 0) \ return count; \ } while (0) #define CHECK_ERR_FREE(count, buf) if (count < 0) { free(buf); return count; } /* ---- Worker code not requiring special instruction sets. ---- * * The following code does not use any x86 specific vectorized instructions * and should compile on any machine * */ /* Transpose 8x8 bit array packed into a single quadword *x*. * *t* is workspace. */ #define TRANS_BIT_8X8(x, t) { \ t = (x ^ (x >> 7)) & 0x00AA00AA00AA00AALL; \ x = x ^ t ^ (t << 7); \ t = (x ^ (x >> 14)) & 0x0000CCCC0000CCCCLL; \ x = x ^ t ^ (t << 14); \ t = (x ^ (x >> 28)) & 0x00000000F0F0F0F0LL; \ x = x ^ t ^ (t << 28); \ } /* Transpose 8x8 bit array along the diagonal from upper right to lower left */ #define TRANS_BIT_8X8_BE(x, t) { \ t = (x ^ (x >> 9)) & 0x0055005500550055LL; \ x = x ^ t ^ (t << 9); \ t = (x ^ (x >> 18)) & 0x0000333300003333LL; \ x = x ^ t ^ (t << 18); \ t = (x ^ (x >> 36)) & 0x000000000F0F0F0FLL; \ x = x ^ t ^ (t << 36); \ } /* Transpose of an array of arbitrarily typed elements. */ #define TRANS_ELEM_TYPE(in, out, lda, ldb, type_t) { \ size_t ii, jj, kk; \ const type_t* in_type = (const type_t*) in; \ type_t* out_type = (type_t*) out; \ for(ii = 0; ii + 7 < lda; ii += 8) { \ for(jj = 0; jj < ldb; jj++) { \ for(kk = 0; kk < 8; kk++) { \ out_type[jj*lda + ii + kk] = \ in_type[ii*ldb + kk * ldb + jj]; \ } \ } \ } \ for(ii = lda - lda % 8; ii < lda; ii ++) { \ for(jj = 0; jj < ldb; jj++) { \ out_type[jj*lda + ii] = in_type[ii*ldb + jj]; \ } \ } \ } /* Memory copy with bshuf call signature. For testing and profiling. */ BLOSC_NO_EXPORT int64_t bshuf_copy(const void* in, void* out, const size_t size, const size_t elem_size); /* Private functions */ BLOSC_NO_EXPORT int64_t bshuf_trans_byte_elem_remainder(const void* in, void* out, const size_t size, const size_t elem_size, const size_t start); BLOSC_NO_EXPORT int64_t bshuf_trans_byte_elem_scal(const void* in, void* out, const size_t size, const size_t elem_size); BLOSC_NO_EXPORT int64_t bshuf_trans_bit_byte_remainder(const void* in, void* out, const size_t size, const size_t elem_size, const size_t start_byte); BLOSC_NO_EXPORT int64_t bshuf_trans_elem(const void* in, void* out, const size_t lda, const size_t ldb, const size_t elem_size); BLOSC_NO_EXPORT int64_t bshuf_trans_bitrow_eight(const void* in, void* out, const size_t size, const size_t elem_size); BLOSC_NO_EXPORT int64_t bshuf_shuffle_bit_eightelem_scal(const void* in, void* out, const size_t size, const size_t elem_size); BLOSC_NO_EXPORT int64_t bshuf_trans_byte_bitrow_scal(const void* in, void* out, const size_t size, const size_t elem_size); /* Bitshuffle the data. * * Transpose the bits within elements. * * Parameters * ---------- * in : input buffer, must be of size * elem_size bytes * out : output buffer, must be of size * elem_size bytes * size : number of elements in input * elem_size : element size of typed data * tmp_buffer : temporary buffer with the same `size` than `in` and `out` * * Returns * ------- * nothing -- this cannot fail * */ BLOSC_NO_EXPORT int64_t bshuf_trans_bit_elem_scal(const void* in, void* out, const size_t size, const size_t elem_size); /* Unshuffle bitshuffled data. * * Untranspose the bits within elements. * * To properly unshuffle bitshuffled data, *size* and *elem_size* must * match the parameters used to shuffle the data. * * Parameters * ---------- * in : input buffer, must be of size * elem_size bytes * out : output buffer, must be of size * elem_size bytes * size : number of elements in input * elem_size : element size of typed data * tmp_buffer : temporary buffer with the same `size` than `in` and `out` * * Returns * ------- * nothing -- this cannot fail * */ BLOSC_NO_EXPORT int64_t bshuf_untrans_bit_elem_scal(const void* in, void* out, const size_t size, const size_t elem_size); #endif /* BLOSC_BITSHUFFLE_GENERIC_H */ c-blosc2-3.1.5/blosc/bitshuffle-neon.c000066400000000000000000000452171521743436100175010ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /********************************************************************* Bitshuffle - Filter for improving compression of typed binary data. Author: Kiyoshi Masui Website: https://github.com/kiyo-masui/bitshuffle Note: Adapted for c-blosc2 by Francesc Alted. See LICENSES/BITSHUFFLE.txt file for details about copyright and rights to use. **********************************************************************/ #include "bitshuffle-neon.h" #include "bitshuffle-generic.h" #include /* Make sure NEON is available for the compilation target and compiler. */ #if defined(__ARM_NEON) #include /* The next is useful for debugging purposes */ #if 0 #include #include static void printmem(uint8_t* buf) { printf("%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); } #endif /* ---- Worker code that uses Arm NEON ---- * * The following code makes use of the Arm NEON instruction set. * NEON technology is the implementation of the ARM Advanced Single * Instruction Multiple Data (SIMD) extension. * The NEON unit is the component of the processor that executes SIMD instructions. * It is also called the NEON Media Processing Engine (MPE). * */ /* Transpose bytes within elements for 16 bit elements. */ int64_t bshuf_trans_byte_elem_NEON_16(const void* in, void* out, const size_t size) { size_t ii; const char *in_b = (const char*) in; char *out_b = (char*) out; int8x16_t a0, b0, a1, b1; for (ii=0; ii + 15 < size; ii += 16) { a0 = vld1q_s8(in_b + 2*ii + 0*16); b0 = vld1q_s8(in_b + 2*ii + 1*16); a1 = vzip1q_s8(a0, b0); b1 = vzip2q_s8(a0, b0); a0 = vzip1q_s8(a1, b1); b0 = vzip2q_s8(a1, b1); a1 = vzip1q_s8(a0, b0); b1 = vzip2q_s8(a0, b0); a0 = vzip1q_s8(a1, b1); b0 = vzip2q_s8(a1, b1); vst1q_s8(out_b + 0*size + ii, a0); vst1q_s8(out_b + 1*size + ii, b0); } return bshuf_trans_byte_elem_remainder(in, out, size, 2, size - size % 16); } /* Transpose bytes within elements for 32 bit elements. */ int64_t bshuf_trans_byte_elem_NEON_32(const void* in, void* out, const size_t size) { size_t ii; const char *in_b; char *out_b; in_b = (const char*) in; out_b = (char*) out; int8x16_t a0, b0, c0, d0, a1, b1, c1, d1; int64x2_t a2, b2, c2, d2; for (ii=0; ii + 15 < size; ii += 16) { a0 = vld1q_s8(in_b + 4*ii + 0*16); b0 = vld1q_s8(in_b + 4*ii + 1*16); c0 = vld1q_s8(in_b + 4*ii + 2*16); d0 = vld1q_s8(in_b + 4*ii + 3*16); a1 = vzip1q_s8(a0, b0); b1 = vzip2q_s8(a0, b0); c1 = vzip1q_s8(c0, d0); d1 = vzip2q_s8(c0, d0); a0 = vzip1q_s8(a1, b1); b0 = vzip2q_s8(a1, b1); c0 = vzip1q_s8(c1, d1); d0 = vzip2q_s8(c1, d1); a1 = vzip1q_s8(a0, b0); b1 = vzip2q_s8(a0, b0); c1 = vzip1q_s8(c0, d0); d1 = vzip2q_s8(c0, d0); a2 = vzip1q_s64(vreinterpretq_s64_s8(a1), vreinterpretq_s64_s8(c1)); b2 = vzip2q_s64(vreinterpretq_s64_s8(a1), vreinterpretq_s64_s8(c1)); c2 = vzip1q_s64(vreinterpretq_s64_s8(b1), vreinterpretq_s64_s8(d1)); d2 = vzip2q_s64(vreinterpretq_s64_s8(b1), vreinterpretq_s64_s8(d1)); vst1q_s64((int64_t *) (out_b + 0*size + ii), a2); vst1q_s64((int64_t *) (out_b + 1*size + ii), b2); vst1q_s64((int64_t *) (out_b + 2*size + ii), c2); vst1q_s64((int64_t *) (out_b + 3*size + ii), d2); } return bshuf_trans_byte_elem_remainder(in, out, size, 4, size - size % 16); } /* Transpose bytes within elements for 64 bit elements. */ int64_t bshuf_trans_byte_elem_NEON_64(const void* in, void* out, const size_t size) { size_t ii; const char* in_b = (const char*) in; char* out_b = (char*) out; int8x16_t a0, b0, c0, d0, e0, f0, g0, h0; int8x16_t a1, b1, c1, d1, e1, f1, g1, h1; for (ii=0; ii + 15 < size; ii += 16) { a0 = vld1q_s8(in_b + 8*ii + 0*16); b0 = vld1q_s8(in_b + 8*ii + 1*16); c0 = vld1q_s8(in_b + 8*ii + 2*16); d0 = vld1q_s8(in_b + 8*ii + 3*16); e0 = vld1q_s8(in_b + 8*ii + 4*16); f0 = vld1q_s8(in_b + 8*ii + 5*16); g0 = vld1q_s8(in_b + 8*ii + 6*16); h0 = vld1q_s8(in_b + 8*ii + 7*16); a1 = vzip1q_s8 (a0, b0); b1 = vzip2q_s8 (a0, b0); c1 = vzip1q_s8 (c0, d0); d1 = vzip2q_s8 (c0, d0); e1 = vzip1q_s8 (e0, f0); f1 = vzip2q_s8 (e0, f0); g1 = vzip1q_s8 (g0, h0); h1 = vzip2q_s8 (g0, h0); a0 = vzip1q_s8 (a1, b1); b0 = vzip2q_s8 (a1, b1); c0 = vzip1q_s8 (c1, d1); d0 = vzip2q_s8 (c1, d1); e0 = vzip1q_s8 (e1, f1); f0 = vzip2q_s8 (e1, f1); g0 = vzip1q_s8 (g1, h1); h0 = vzip2q_s8 (g1, h1); a1 = (int8x16_t) vzip1q_s32 (vreinterpretq_s32_s8 (a0), vreinterpretq_s32_s8 (c0)); b1 = (int8x16_t) vzip2q_s32 (vreinterpretq_s32_s8 (a0), vreinterpretq_s32_s8 (c0)); c1 = (int8x16_t) vzip1q_s32 (vreinterpretq_s32_s8 (b0), vreinterpretq_s32_s8 (d0)); d1 = (int8x16_t) vzip2q_s32 (vreinterpretq_s32_s8 (b0), vreinterpretq_s32_s8 (d0)); e1 = (int8x16_t) vzip1q_s32 (vreinterpretq_s32_s8 (e0), vreinterpretq_s32_s8 (g0)); f1 = (int8x16_t) vzip2q_s32 (vreinterpretq_s32_s8 (e0), vreinterpretq_s32_s8 (g0)); g1 = (int8x16_t) vzip1q_s32 (vreinterpretq_s32_s8 (f0), vreinterpretq_s32_s8 (h0)); h1 = (int8x16_t) vzip2q_s32 (vreinterpretq_s32_s8 (f0), vreinterpretq_s32_s8 (h0)); a0 = (int8x16_t) vzip1q_s64 (vreinterpretq_s64_s8 (a1), vreinterpretq_s64_s8 (e1)); b0 = (int8x16_t) vzip2q_s64 (vreinterpretq_s64_s8 (a1), vreinterpretq_s64_s8 (e1)); c0 = (int8x16_t) vzip1q_s64 (vreinterpretq_s64_s8 (b1), vreinterpretq_s64_s8 (f1)); d0 = (int8x16_t) vzip2q_s64 (vreinterpretq_s64_s8 (b1), vreinterpretq_s64_s8 (f1)); e0 = (int8x16_t) vzip1q_s64 (vreinterpretq_s64_s8 (c1), vreinterpretq_s64_s8 (g1)); f0 = (int8x16_t) vzip2q_s64 (vreinterpretq_s64_s8 (c1), vreinterpretq_s64_s8 (g1)); g0 = (int8x16_t) vzip1q_s64 (vreinterpretq_s64_s8 (d1), vreinterpretq_s64_s8 (h1)); h0 = (int8x16_t) vzip2q_s64 (vreinterpretq_s64_s8 (d1), vreinterpretq_s64_s8 (h1)); vst1q_s8(out_b + 0*size + ii, a0); vst1q_s8(out_b + 1*size + ii, b0); vst1q_s8(out_b + 2*size + ii, c0); vst1q_s8(out_b + 3*size + ii, d0); vst1q_s8(out_b + 4*size + ii, e0); vst1q_s8(out_b + 5*size + ii, f0); vst1q_s8(out_b + 6*size + ii, g0); vst1q_s8(out_b + 7*size + ii, h0); } return bshuf_trans_byte_elem_remainder(in, out, size, 8, size - size % 16); } /* Transpose bytes within elements using best NEON algorithm available. */ int64_t bshuf_trans_byte_elem_NEON(const void* in, void* out, const size_t size, const size_t elem_size) { int64_t count; // Trivial cases: power of 2 bytes. switch (elem_size) { case 1: count = bshuf_copy(in, out, size, elem_size); return count; case 2: count = bshuf_trans_byte_elem_NEON_16(in, out, size); return count; case 4: count = bshuf_trans_byte_elem_NEON_32(in, out, size); return count; case 8: count = bshuf_trans_byte_elem_NEON_64(in, out, size); return count; } // Worst case: odd number of bytes. Turns out that this is faster for // (odd * 2) byte elements as well (hence % 4). if (elem_size % 4) { count = bshuf_trans_byte_elem_scal(in, out, size, elem_size); return count; } // Multiple of power of 2: transpose hierarchically. { size_t nchunk_elem; void* tmp_buf = malloc(size * elem_size); if (tmp_buf == NULL) return -1; if ((elem_size % 8) == 0) { nchunk_elem = elem_size / 8; TRANS_ELEM_TYPE(in, out, size, nchunk_elem, int64_t); count = bshuf_trans_byte_elem_NEON_64(out, tmp_buf, size * nchunk_elem); bshuf_trans_elem(tmp_buf, out, 8, nchunk_elem, size); } else if ((elem_size % 4) == 0) { nchunk_elem = elem_size / 4; TRANS_ELEM_TYPE(in, out, size, nchunk_elem, int32_t); count = bshuf_trans_byte_elem_NEON_32(out, tmp_buf, size * nchunk_elem); bshuf_trans_elem(tmp_buf, out, 4, nchunk_elem, size); } else { // Not used since scalar algorithm is faster. nchunk_elem = elem_size / 2; TRANS_ELEM_TYPE(in, out, size, nchunk_elem, int16_t); count = bshuf_trans_byte_elem_NEON_16(out, tmp_buf, size * nchunk_elem); bshuf_trans_elem(tmp_buf, out, 2, nchunk_elem, size); } free(tmp_buf); return count; } } /* Creates a mask made up of the most significant * bit of each byte of 'input' */ int32_t move_byte_mask_neon(uint8x16_t input) { return ( ((input[0] & 0x80) >> 7) | (((input[1] & 0x80) >> 7) << 1) | (((input[2] & 0x80) >> 7) << 2) | (((input[3] & 0x80) >> 7) << 3) | (((input[4] & 0x80) >> 7) << 4) | (((input[5] & 0x80) >> 7) << 5) | (((input[6] & 0x80) >> 7) << 6) | (((input[7] & 0x80) >> 7) << 7) | (((input[8] & 0x80) >> 7) << 8) | (((input[9] & 0x80) >> 7) << 9) | (((input[10] & 0x80) >> 7) << 10) | (((input[11] & 0x80) >> 7) << 11) | (((input[12] & 0x80) >> 7) << 12) | (((input[13] & 0x80) >> 7) << 13) | (((input[14] & 0x80) >> 7) << 14) | (((input[15] & 0x80) >> 7) << 15) ); } /* Transpose bits within bytes. */ int64_t bshuf_trans_bit_byte_NEON(const void* in, void* out, const size_t size, const size_t elem_size) { size_t ii, kk; const char* in_b = (const char*) in; char* out_b = (char*) out; uint16_t* out_ui16; int64_t count; size_t nbyte = elem_size * size; CHECK_MULT_EIGHT(nbyte); int16x8_t xmm; int32_t bt; for (ii = 0; ii + 15 < nbyte; ii += 16) { xmm = vld1q_s16((int16_t *) (in_b + ii)); for (kk = 0; kk < 8; kk++) { bt = move_byte_mask_neon((uint8x16_t) xmm); xmm = vshlq_n_s16(xmm, 1); out_ui16 = (uint16_t*) &out_b[((7 - kk) * nbyte + ii) / 8]; *out_ui16 = bt; } } count = bshuf_trans_bit_byte_remainder(in, out, size, elem_size, nbyte - nbyte % 16); return count; } /* Transpose bits within elements. */ int64_t bshuf_trans_bit_elem_NEON(const void* in, void* out, const size_t size, const size_t elem_size) { int64_t count; CHECK_MULT_EIGHT(size); void* tmp_buf = malloc(size * elem_size); if (tmp_buf == NULL) return -1; count = bshuf_trans_byte_elem_NEON(in, out, size, elem_size); CHECK_ERR_FREE(count, tmp_buf); count = bshuf_trans_bit_byte_NEON(out, tmp_buf, size, elem_size); CHECK_ERR_FREE(count, tmp_buf); count = bshuf_trans_bitrow_eight(tmp_buf, out, size, elem_size); free(tmp_buf); return count; } /* For data organized into a row for each bit (8 * elem_size rows), transpose * the bytes. */ int64_t bshuf_trans_byte_bitrow_NEON(const void* in, void* out, const size_t size, const size_t elem_size) { size_t ii, jj; const char* in_b = (const char*) in; char* out_b = (char*) out; CHECK_MULT_EIGHT(size); size_t nrows = 8 * elem_size; size_t nbyte_row = size / 8; int8x16_t a0, b0, c0, d0, e0, f0, g0, h0; int8x16_t a1, b1, c1, d1, e1, f1, g1, h1; int64x1_t *as, *bs, *cs, *ds, *es, *fs, *gs, *hs; for (ii = 0; ii + 7 < nrows; ii += 8) { for (jj = 0; jj + 15 < nbyte_row; jj += 16) { a0 = vld1q_s8(in_b + (ii + 0)*nbyte_row + jj); b0 = vld1q_s8(in_b + (ii + 1)*nbyte_row + jj); c0 = vld1q_s8(in_b + (ii + 2)*nbyte_row + jj); d0 = vld1q_s8(in_b + (ii + 3)*nbyte_row + jj); e0 = vld1q_s8(in_b + (ii + 4)*nbyte_row + jj); f0 = vld1q_s8(in_b + (ii + 5)*nbyte_row + jj); g0 = vld1q_s8(in_b + (ii + 6)*nbyte_row + jj); h0 = vld1q_s8(in_b + (ii + 7)*nbyte_row + jj); a1 = vzip1q_s8(a0, b0); b1 = vzip1q_s8(c0, d0); c1 = vzip1q_s8(e0, f0); d1 = vzip1q_s8(g0, h0); e1 = vzip2q_s8(a0, b0); f1 = vzip2q_s8(c0, d0); g1 = vzip2q_s8(e0, f0); h1 = vzip2q_s8(g0, h0); a0 = (int8x16_t) vzip1q_s16 (vreinterpretq_s16_s8 (a1), vreinterpretq_s16_s8 (b1)); b0= (int8x16_t) vzip1q_s16 (vreinterpretq_s16_s8 (c1), vreinterpretq_s16_s8 (d1)); c0 = (int8x16_t) vzip2q_s16 (vreinterpretq_s16_s8 (a1), vreinterpretq_s16_s8 (b1)); d0 = (int8x16_t) vzip2q_s16 (vreinterpretq_s16_s8 (c1), vreinterpretq_s16_s8 (d1)); e0 = (int8x16_t) vzip1q_s16 (vreinterpretq_s16_s8 (e1), vreinterpretq_s16_s8 (f1)); f0 = (int8x16_t) vzip1q_s16 (vreinterpretq_s16_s8 (g1), vreinterpretq_s16_s8 (h1)); g0 = (int8x16_t) vzip2q_s16 (vreinterpretq_s16_s8 (e1), vreinterpretq_s16_s8 (f1)); h0 = (int8x16_t) vzip2q_s16 (vreinterpretq_s16_s8 (g1), vreinterpretq_s16_s8 (h1)); a1 = (int8x16_t) vzip1q_s32 (vreinterpretq_s32_s8 (a0), vreinterpretq_s32_s8 (b0)); b1 = (int8x16_t) vzip2q_s32 (vreinterpretq_s32_s8 (a0), vreinterpretq_s32_s8 (b0)); c1 = (int8x16_t) vzip1q_s32 (vreinterpretq_s32_s8 (c0), vreinterpretq_s32_s8 (d0)); d1 = (int8x16_t) vzip2q_s32 (vreinterpretq_s32_s8 (c0), vreinterpretq_s32_s8 (d0)); e1 = (int8x16_t) vzip1q_s32 (vreinterpretq_s32_s8 (e0), vreinterpretq_s32_s8 (f0)); f1 = (int8x16_t) vzip2q_s32 (vreinterpretq_s32_s8 (e0), vreinterpretq_s32_s8 (f0)); g1 = (int8x16_t) vzip1q_s32 (vreinterpretq_s32_s8 (g0), vreinterpretq_s32_s8 (h0)); h1 = (int8x16_t) vzip2q_s32 (vreinterpretq_s32_s8 (g0), vreinterpretq_s32_s8 (h0)); as = (int64x1_t *) &a1; bs = (int64x1_t *) &b1; cs = (int64x1_t *) &c1; ds = (int64x1_t *) &d1; es = (int64x1_t *) &e1; fs = (int64x1_t *) &f1; gs = (int64x1_t *) &g1; hs = (int64x1_t *) &h1; vst1_s64((int64_t *)(out_b + (jj + 0) * nrows + ii), *as); vst1_s64((int64_t *)(out_b + (jj + 1) * nrows + ii), *(as + 1)); vst1_s64((int64_t *)(out_b + (jj + 2) * nrows + ii), *bs); vst1_s64((int64_t *)(out_b + (jj + 3) * nrows + ii), *(bs + 1)); vst1_s64((int64_t *)(out_b + (jj + 4) * nrows + ii), *cs); vst1_s64((int64_t *)(out_b + (jj + 5) * nrows + ii), *(cs + 1)); vst1_s64((int64_t *)(out_b + (jj + 6) * nrows + ii), *ds); vst1_s64((int64_t *)(out_b + (jj + 7) * nrows + ii), *(ds + 1)); vst1_s64((int64_t *)(out_b + (jj + 8) * nrows + ii), *es); vst1_s64((int64_t *)(out_b + (jj + 9) * nrows + ii), *(es + 1)); vst1_s64((int64_t *)(out_b + (jj + 10) * nrows + ii), *fs); vst1_s64((int64_t *)(out_b + (jj + 11) * nrows + ii), *(fs + 1)); vst1_s64((int64_t *)(out_b + (jj + 12) * nrows + ii), *gs); vst1_s64((int64_t *)(out_b + (jj + 13) * nrows + ii), *(gs + 1)); vst1_s64((int64_t *)(out_b + (jj + 14) * nrows + ii), *hs); vst1_s64((int64_t *)(out_b + (jj + 15) * nrows + ii), *(hs + 1)); } for (jj = nbyte_row - nbyte_row % 16; jj < nbyte_row; jj ++) { out_b[jj * nrows + ii + 0] = in_b[(ii + 0)*nbyte_row + jj]; out_b[jj * nrows + ii + 1] = in_b[(ii + 1)*nbyte_row + jj]; out_b[jj * nrows + ii + 2] = in_b[(ii + 2)*nbyte_row + jj]; out_b[jj * nrows + ii + 3] = in_b[(ii + 3)*nbyte_row + jj]; out_b[jj * nrows + ii + 4] = in_b[(ii + 4)*nbyte_row + jj]; out_b[jj * nrows + ii + 5] = in_b[(ii + 5)*nbyte_row + jj]; out_b[jj * nrows + ii + 6] = in_b[(ii + 6)*nbyte_row + jj]; out_b[jj * nrows + ii + 7] = in_b[(ii + 7)*nbyte_row + jj]; } } return size * elem_size; } /* Shuffle bits within the bytes of eight element blocks. */ int64_t bshuf_shuffle_bit_eightelem_NEON(const void* in, void* out, const size_t size, const size_t elem_size) { CHECK_MULT_EIGHT(size); // With a bit of care, this could be written such that such that it is // in_buf = out_buf safe. const char* in_b = (const char*) in; uint16_t* out_ui16 = (uint16_t*) out; size_t ii, jj, kk; size_t nbyte = elem_size * size; int16x8_t xmm; int32_t bt; if (elem_size % 2) { bshuf_shuffle_bit_eightelem_scal(in, out, size, elem_size); } else { for (ii = 0; ii + 8 * elem_size - 1 < nbyte; ii += 8 * elem_size) { for (jj = 0; jj + 15 < 8 * elem_size; jj += 16) { xmm = vld1q_s16((int16_t *) &in_b[ii + jj]); for (kk = 0; kk < 8; kk++) { bt = move_byte_mask_neon((uint8x16_t) xmm); xmm = vshlq_n_s16(xmm, 1); size_t ind = (ii + jj / 8 + (7 - kk) * elem_size); out_ui16[ind / 2] = bt; } } } } return size * elem_size; } /* Untranspose bits within elements. */ int64_t bshuf_untrans_bit_elem_NEON(const void* in, void* out, const size_t size, const size_t elem_size) { int64_t count; CHECK_MULT_EIGHT(size); void* tmp_buf = malloc(size * elem_size); if (tmp_buf == NULL) return -1; count = bshuf_trans_byte_bitrow_NEON(in, tmp_buf, size, elem_size); CHECK_ERR_FREE(count, tmp_buf); count = bshuf_shuffle_bit_eightelem_NEON(tmp_buf, out, size, elem_size); free(tmp_buf); return count; } const bool is_bshuf_NEON = true; #else /* defined(__ARM_NEON) */ const bool is_bshuf_NEON = false; int64_t bshuf_trans_bit_elem_NEON(const void* in, void* out, const size_t size, const size_t elem_size) { abort(); } int64_t bshuf_untrans_bit_elem_NEON(const void* in, void* out, const size_t size, const size_t elem_size) { abort(); } #endif /* defined(__ARM_NEON) */ c-blosc2-3.1.5/blosc/bitshuffle-neon.h000066400000000000000000000022421521743436100174750ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /* NEON-accelerated bitshuffle/bitunshuffle routines. */ #ifndef BLOSC_BITSHUFFLE_NEON_H #define BLOSC_BITSHUFFLE_NEON_H #include "blosc2/blosc2-common.h" #include #include #include /** * NEON-accelerated bit(un)shuffle routines availability. */ extern const bool is_bshuf_NEON; /** NEON-accelerated bitshuffle routine. */ BLOSC_NO_EXPORT int64_t bshuf_trans_bit_elem_NEON(const void* in, void* out, const size_t size, const size_t elem_size); /** NEON-accelerated bitunshuffle routine. */ BLOSC_NO_EXPORT int64_t bshuf_untrans_bit_elem_NEON(const void* in, void* out, const size_t size, const size_t elem_size); #endif /* BLOSC_BITSHUFFLE_NEON_H */ c-blosc2-3.1.5/blosc/bitshuffle-sse2.c000066400000000000000000000377421521743436100174220ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /********************************************************************* Bitshuffle - Filter for improving compression of typed binary data. Author: Kiyoshi Masui Website: https://github.com/kiyo-masui/bitshuffle Note: Adapted for c-blosc by Francesc Alted. See LICENSES/BITSHUFFLE.txt file for details about copyright and rights to use. **********************************************************************/ #include "bitshuffle-sse2.h" #include "bitshuffle-generic.h" #include /* Make sure SSE2 is available for the compilation target and compiler. */ #if defined(__SSE2__) #include /* The next is useful for debugging purposes */ #if 0 #include #include static void printxmm(__m128i xmm0) { uint8_t buf[32]; ((__m128i *)buf)[0] = xmm0; printf("%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); } #endif /* ---- Worker code that requires SSE2. Intel Petium 4 (2000) and later. ---- */ /* Transpose bytes within elements for 16 bit elements. */ int64_t bshuf_trans_byte_elem_SSE_16(const void* in, void* out, const size_t size) { size_t ii; const char *in_b = (const char*) in; char *out_b = (char*) out; __m128i a0, b0, a1, b1; for (ii=0; ii + 15 < size; ii += 16) { a0 = _mm_loadu_si128((__m128i *) &in_b[2*ii + 0*16]); b0 = _mm_loadu_si128((__m128i *) &in_b[2*ii + 1*16]); a1 = _mm_unpacklo_epi8(a0, b0); b1 = _mm_unpackhi_epi8(a0, b0); a0 = _mm_unpacklo_epi8(a1, b1); b0 = _mm_unpackhi_epi8(a1, b1); a1 = _mm_unpacklo_epi8(a0, b0); b1 = _mm_unpackhi_epi8(a0, b0); a0 = _mm_unpacklo_epi8(a1, b1); b0 = _mm_unpackhi_epi8(a1, b1); _mm_storeu_si128((__m128i *) &out_b[0*size + ii], a0); _mm_storeu_si128((__m128i *) &out_b[1*size + ii], b0); } return bshuf_trans_byte_elem_remainder(in, out, size, 2, size - size % 16); } /* Transpose bytes within elements for 32 bit elements. */ int64_t bshuf_trans_byte_elem_SSE_32(const void* in, void* out, const size_t size) { size_t ii; const char *in_b; char *out_b; in_b = (const char*) in; out_b = (char*) out; __m128i a0, b0, c0, d0, a1, b1, c1, d1; for (ii=0; ii + 15 < size; ii += 16) { a0 = _mm_loadu_si128((__m128i *) &in_b[4*ii + 0*16]); b0 = _mm_loadu_si128((__m128i *) &in_b[4*ii + 1*16]); c0 = _mm_loadu_si128((__m128i *) &in_b[4*ii + 2*16]); d0 = _mm_loadu_si128((__m128i *) &in_b[4*ii + 3*16]); a1 = _mm_unpacklo_epi8(a0, b0); b1 = _mm_unpackhi_epi8(a0, b0); c1 = _mm_unpacklo_epi8(c0, d0); d1 = _mm_unpackhi_epi8(c0, d0); a0 = _mm_unpacklo_epi8(a1, b1); b0 = _mm_unpackhi_epi8(a1, b1); c0 = _mm_unpacklo_epi8(c1, d1); d0 = _mm_unpackhi_epi8(c1, d1); a1 = _mm_unpacklo_epi8(a0, b0); b1 = _mm_unpackhi_epi8(a0, b0); c1 = _mm_unpacklo_epi8(c0, d0); d1 = _mm_unpackhi_epi8(c0, d0); a0 = _mm_unpacklo_epi64(a1, c1); b0 = _mm_unpackhi_epi64(a1, c1); c0 = _mm_unpacklo_epi64(b1, d1); d0 = _mm_unpackhi_epi64(b1, d1); _mm_storeu_si128((__m128i *) &out_b[0*size + ii], a0); _mm_storeu_si128((__m128i *) &out_b[1*size + ii], b0); _mm_storeu_si128((__m128i *) &out_b[2*size + ii], c0); _mm_storeu_si128((__m128i *) &out_b[3*size + ii], d0); } return bshuf_trans_byte_elem_remainder(in, out, size, 4, size - size % 16); } /* Transpose bytes within elements for 64 bit elements. */ int64_t bshuf_trans_byte_elem_SSE_64(const void* in, void* out, const size_t size) { size_t ii; const char* in_b = (const char*) in; char* out_b = (char*) out; __m128i a0, b0, c0, d0, e0, f0, g0, h0; __m128i a1, b1, c1, d1, e1, f1, g1, h1; for (ii=0; ii + 15 < size; ii += 16) { a0 = _mm_loadu_si128((__m128i *) &in_b[8*ii + 0*16]); b0 = _mm_loadu_si128((__m128i *) &in_b[8*ii + 1*16]); c0 = _mm_loadu_si128((__m128i *) &in_b[8*ii + 2*16]); d0 = _mm_loadu_si128((__m128i *) &in_b[8*ii + 3*16]); e0 = _mm_loadu_si128((__m128i *) &in_b[8*ii + 4*16]); f0 = _mm_loadu_si128((__m128i *) &in_b[8*ii + 5*16]); g0 = _mm_loadu_si128((__m128i *) &in_b[8*ii + 6*16]); h0 = _mm_loadu_si128((__m128i *) &in_b[8*ii + 7*16]); a1 = _mm_unpacklo_epi8(a0, b0); b1 = _mm_unpackhi_epi8(a0, b0); c1 = _mm_unpacklo_epi8(c0, d0); d1 = _mm_unpackhi_epi8(c0, d0); e1 = _mm_unpacklo_epi8(e0, f0); f1 = _mm_unpackhi_epi8(e0, f0); g1 = _mm_unpacklo_epi8(g0, h0); h1 = _mm_unpackhi_epi8(g0, h0); a0 = _mm_unpacklo_epi8(a1, b1); b0 = _mm_unpackhi_epi8(a1, b1); c0 = _mm_unpacklo_epi8(c1, d1); d0 = _mm_unpackhi_epi8(c1, d1); e0 = _mm_unpacklo_epi8(e1, f1); f0 = _mm_unpackhi_epi8(e1, f1); g0 = _mm_unpacklo_epi8(g1, h1); h0 = _mm_unpackhi_epi8(g1, h1); a1 = _mm_unpacklo_epi32(a0, c0); b1 = _mm_unpackhi_epi32(a0, c0); c1 = _mm_unpacklo_epi32(b0, d0); d1 = _mm_unpackhi_epi32(b0, d0); e1 = _mm_unpacklo_epi32(e0, g0); f1 = _mm_unpackhi_epi32(e0, g0); g1 = _mm_unpacklo_epi32(f0, h0); h1 = _mm_unpackhi_epi32(f0, h0); a0 = _mm_unpacklo_epi64(a1, e1); b0 = _mm_unpackhi_epi64(a1, e1); c0 = _mm_unpacklo_epi64(b1, f1); d0 = _mm_unpackhi_epi64(b1, f1); e0 = _mm_unpacklo_epi64(c1, g1); f0 = _mm_unpackhi_epi64(c1, g1); g0 = _mm_unpacklo_epi64(d1, h1); h0 = _mm_unpackhi_epi64(d1, h1); _mm_storeu_si128((__m128i *) &out_b[0*size + ii], a0); _mm_storeu_si128((__m128i *) &out_b[1*size + ii], b0); _mm_storeu_si128((__m128i *) &out_b[2*size + ii], c0); _mm_storeu_si128((__m128i *) &out_b[3*size + ii], d0); _mm_storeu_si128((__m128i *) &out_b[4*size + ii], e0); _mm_storeu_si128((__m128i *) &out_b[5*size + ii], f0); _mm_storeu_si128((__m128i *) &out_b[6*size + ii], g0); _mm_storeu_si128((__m128i *) &out_b[7*size + ii], h0); } return bshuf_trans_byte_elem_remainder(in, out, size, 8, size - size % 16); } /* Transpose bytes within elements using the best SSE algorithm available. */ int64_t bshuf_trans_byte_elem_SSE(const void* in, void* out, const size_t size, const size_t elem_size) { int64_t count; // Trivial cases: power of 2 bytes. switch (elem_size) { case 1: count = bshuf_copy(in, out, size, elem_size); return count; case 2: count = bshuf_trans_byte_elem_SSE_16(in, out, size); return count; case 4: count = bshuf_trans_byte_elem_SSE_32(in, out, size); return count; case 8: count = bshuf_trans_byte_elem_SSE_64(in, out, size); return count; } // Worst case: odd number of bytes. Turns out that this is faster for // (odd * 2) byte elements as well (hence % 4). if (elem_size % 4) { count = bshuf_trans_byte_elem_scal(in, out, size, elem_size); return count; } // Multiple of power of 2: transpose hierarchically. { size_t nchunk_elem; void* tmp_buf = malloc(size * elem_size); if (tmp_buf == NULL) return -1; if ((elem_size % 8) == 0) { nchunk_elem = elem_size / 8; TRANS_ELEM_TYPE(in, out, size, nchunk_elem, int64_t); count = bshuf_trans_byte_elem_SSE_64(out, tmp_buf, size * nchunk_elem); bshuf_trans_elem(tmp_buf, out, 8, nchunk_elem, size); } else if ((elem_size % 4) == 0) { nchunk_elem = elem_size / 4; TRANS_ELEM_TYPE(in, out, size, nchunk_elem, int32_t); count = bshuf_trans_byte_elem_SSE_32(out, tmp_buf, size * nchunk_elem); bshuf_trans_elem(tmp_buf, out, 4, nchunk_elem, size); } else { // Not used since scalar algorithm is faster. nchunk_elem = elem_size / 2; TRANS_ELEM_TYPE(in, out, size, nchunk_elem, int16_t); count = bshuf_trans_byte_elem_SSE_16(out, tmp_buf, size * nchunk_elem); bshuf_trans_elem(tmp_buf, out, 2, nchunk_elem, size); } free(tmp_buf); return count; } } /* Transpose bits within bytes. */ int64_t bshuf_trans_bit_byte_SSE(const void* in, void* out, const size_t size, const size_t elem_size) { size_t ii, kk; const char* in_b = (const char*) in; char* out_b = (char*) out; uint16_t* out_ui16; int64_t count; size_t nbyte = elem_size * size; CHECK_MULT_EIGHT(nbyte); __m128i xmm; int32_t bt; for (ii = 0; ii + 15 < nbyte; ii += 16) { xmm = _mm_loadu_si128((__m128i *) &in_b[ii]); for (kk = 0; kk < 8; kk++) { bt = _mm_movemask_epi8(xmm); xmm = _mm_slli_epi16(xmm, 1); out_ui16 = (uint16_t*) &out_b[((7 - kk) * nbyte + ii) / 8]; *out_ui16 = bt; } } count = bshuf_trans_bit_byte_remainder(in, out, size, elem_size, nbyte - nbyte % 16); return count; } /* Transpose bits within elements. */ int64_t bshuf_trans_bit_elem_SSE(const void* in, void* out, const size_t size, const size_t elem_size) { int64_t count; CHECK_MULT_EIGHT(size); void* tmp_buf = malloc(size * elem_size); if (tmp_buf == NULL) return -1; count = bshuf_trans_byte_elem_SSE(in, out, size, elem_size); CHECK_ERR_FREE(count, tmp_buf); count = bshuf_trans_bit_byte_SSE(out, tmp_buf, size, elem_size); CHECK_ERR_FREE(count, tmp_buf); count = bshuf_trans_bitrow_eight(tmp_buf, out, size, elem_size); free(tmp_buf); return count; } /* For data organized into a row for each bit (8 * elem_size rows), transpose * the bytes. */ int64_t bshuf_trans_byte_bitrow_SSE(const void* in, void* out, const size_t size, const size_t elem_size) { size_t ii, jj; const char* in_b = (const char*) in; char* out_b = (char*) out; CHECK_MULT_EIGHT(size); size_t nrows = 8 * elem_size; size_t nbyte_row = size / 8; __m128i a0, b0, c0, d0, e0, f0, g0, h0; __m128i a1, b1, c1, d1, e1, f1, g1, h1; __m128 *as, *bs, *cs, *ds, *es, *fs, *gs, *hs; for (ii = 0; ii + 7 < nrows; ii += 8) { for (jj = 0; jj + 15 < nbyte_row; jj += 16) { a0 = _mm_loadu_si128((__m128i *) &in_b[(ii + 0)*nbyte_row + jj]); b0 = _mm_loadu_si128((__m128i *) &in_b[(ii + 1)*nbyte_row + jj]); c0 = _mm_loadu_si128((__m128i *) &in_b[(ii + 2)*nbyte_row + jj]); d0 = _mm_loadu_si128((__m128i *) &in_b[(ii + 3)*nbyte_row + jj]); e0 = _mm_loadu_si128((__m128i *) &in_b[(ii + 4)*nbyte_row + jj]); f0 = _mm_loadu_si128((__m128i *) &in_b[(ii + 5)*nbyte_row + jj]); g0 = _mm_loadu_si128((__m128i *) &in_b[(ii + 6)*nbyte_row + jj]); h0 = _mm_loadu_si128((__m128i *) &in_b[(ii + 7)*nbyte_row + jj]); a1 = _mm_unpacklo_epi8(a0, b0); b1 = _mm_unpacklo_epi8(c0, d0); c1 = _mm_unpacklo_epi8(e0, f0); d1 = _mm_unpacklo_epi8(g0, h0); e1 = _mm_unpackhi_epi8(a0, b0); f1 = _mm_unpackhi_epi8(c0, d0); g1 = _mm_unpackhi_epi8(e0, f0); h1 = _mm_unpackhi_epi8(g0, h0); a0 = _mm_unpacklo_epi16(a1, b1); b0 = _mm_unpacklo_epi16(c1, d1); c0 = _mm_unpackhi_epi16(a1, b1); d0 = _mm_unpackhi_epi16(c1, d1); e0 = _mm_unpacklo_epi16(e1, f1); f0 = _mm_unpacklo_epi16(g1, h1); g0 = _mm_unpackhi_epi16(e1, f1); h0 = _mm_unpackhi_epi16(g1, h1); a1 = _mm_unpacklo_epi32(a0, b0); b1 = _mm_unpackhi_epi32(a0, b0); c1 = _mm_unpacklo_epi32(c0, d0); d1 = _mm_unpackhi_epi32(c0, d0); e1 = _mm_unpacklo_epi32(e0, f0); f1 = _mm_unpackhi_epi32(e0, f0); g1 = _mm_unpacklo_epi32(g0, h0); h1 = _mm_unpackhi_epi32(g0, h0); // We don't have a storeh instruction for integers, so interpret // as a float. Have a storel (_mm_storel_epi64). as = (__m128 *) &a1; bs = (__m128 *) &b1; cs = (__m128 *) &c1; ds = (__m128 *) &d1; es = (__m128 *) &e1; fs = (__m128 *) &f1; gs = (__m128 *) &g1; hs = (__m128 *) &h1; _mm_storel_pi((__m64 *) &out_b[(jj + 0) * nrows + ii], *as); _mm_storel_pi((__m64 *) &out_b[(jj + 2) * nrows + ii], *bs); _mm_storel_pi((__m64 *) &out_b[(jj + 4) * nrows + ii], *cs); _mm_storel_pi((__m64 *) &out_b[(jj + 6) * nrows + ii], *ds); _mm_storel_pi((__m64 *) &out_b[(jj + 8) * nrows + ii], *es); _mm_storel_pi((__m64 *) &out_b[(jj + 10) * nrows + ii], *fs); _mm_storel_pi((__m64 *) &out_b[(jj + 12) * nrows + ii], *gs); _mm_storel_pi((__m64 *) &out_b[(jj + 14) * nrows + ii], *hs); _mm_storeh_pi((__m64 *) &out_b[(jj + 1) * nrows + ii], *as); _mm_storeh_pi((__m64 *) &out_b[(jj + 3) * nrows + ii], *bs); _mm_storeh_pi((__m64 *) &out_b[(jj + 5) * nrows + ii], *cs); _mm_storeh_pi((__m64 *) &out_b[(jj + 7) * nrows + ii], *ds); _mm_storeh_pi((__m64 *) &out_b[(jj + 9) * nrows + ii], *es); _mm_storeh_pi((__m64 *) &out_b[(jj + 11) * nrows + ii], *fs); _mm_storeh_pi((__m64 *) &out_b[(jj + 13) * nrows + ii], *gs); _mm_storeh_pi((__m64 *) &out_b[(jj + 15) * nrows + ii], *hs); } for (jj = nbyte_row - nbyte_row % 16; jj < nbyte_row; jj ++) { out_b[jj * nrows + ii + 0] = in_b[(ii + 0)*nbyte_row + jj]; out_b[jj * nrows + ii + 1] = in_b[(ii + 1)*nbyte_row + jj]; out_b[jj * nrows + ii + 2] = in_b[(ii + 2)*nbyte_row + jj]; out_b[jj * nrows + ii + 3] = in_b[(ii + 3)*nbyte_row + jj]; out_b[jj * nrows + ii + 4] = in_b[(ii + 4)*nbyte_row + jj]; out_b[jj * nrows + ii + 5] = in_b[(ii + 5)*nbyte_row + jj]; out_b[jj * nrows + ii + 6] = in_b[(ii + 6)*nbyte_row + jj]; out_b[jj * nrows + ii + 7] = in_b[(ii + 7)*nbyte_row + jj]; } } return size * elem_size; } /* Shuffle bits within the bytes of eight element blocks. */ int64_t bshuf_shuffle_bit_eightelem_SSE(const void* in, void* out, const size_t size, const size_t elem_size) { CHECK_MULT_EIGHT(size); // With a bit of care, this could be written such that such that it is // in_buf = out_buf safe. const char* in_b = (const char*) in; uint16_t* out_ui16 = (uint16_t*) out; size_t ii, jj, kk; size_t nbyte = elem_size * size; __m128i xmm; int32_t bt; if (elem_size % 2) { bshuf_shuffle_bit_eightelem_scal(in, out, size, elem_size); } else { for (ii = 0; ii + 8 * elem_size - 1 < nbyte; ii += 8 * elem_size) { for (jj = 0; jj + 15 < 8 * elem_size; jj += 16) { xmm = _mm_loadu_si128((__m128i *) &in_b[ii + jj]); for (kk = 0; kk < 8; kk++) { bt = _mm_movemask_epi8(xmm); xmm = _mm_slli_epi16(xmm, 1); size_t ind = (ii + jj / 8 + (7 - kk) * elem_size); out_ui16[ind / 2] = bt; } } } } return size * elem_size; } /* Untranspose bits within elements. */ int64_t bshuf_untrans_bit_elem_SSE(const void* in, void* out, const size_t size, const size_t elem_size) { int64_t count; CHECK_MULT_EIGHT(size); void* tmp_buf = malloc(size * elem_size); if (tmp_buf == NULL) return -1; count = bshuf_trans_byte_bitrow_SSE(in, tmp_buf, size, elem_size); CHECK_ERR_FREE(count, tmp_buf); count = bshuf_shuffle_bit_eightelem_SSE(tmp_buf, out, size, elem_size); free(tmp_buf); return count; } const bool is_bshuf_SSE = true; #else /* defined(__SSE2__) */ const bool is_bshuf_SSE = false; int64_t bshuf_trans_bit_elem_SSE(const void* in, void* out, const size_t size, const size_t elem_size) { abort(); } int64_t bshuf_untrans_bit_elem_SSE(const void* in, void* out, const size_t size, const size_t elem_size) { abort(); } #endif /* defined(__SSE2__) */ c-blosc2-3.1.5/blosc/bitshuffle-sse2.h000066400000000000000000000031331521743436100174120ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /* SSE2-accelerated shuffle/unshuffle routines. */ #ifndef BLOSC_BITSHUFFLE_SSE2_H #define BLOSC_BITSHUFFLE_SSE2_H #include "blosc2/blosc2-common.h" #include #include #include /** * SSE2-accelerated bit(un)shuffle routines availability. */ extern const bool is_bshuf_SSE; BLOSC_NO_EXPORT int64_t bshuf_trans_byte_elem_SSE(const void* in, void* out, const size_t size, const size_t elem_size); BLOSC_NO_EXPORT int64_t bshuf_trans_byte_bitrow_SSE(const void* in, void* out, const size_t size, const size_t elem_size); BLOSC_NO_EXPORT int64_t bshuf_shuffle_bit_eightelem_SSE(const void* in, void* out, const size_t size, const size_t elem_size); /** SSE2-accelerated bitshuffle routine. */ BLOSC_NO_EXPORT int64_t bshuf_trans_bit_elem_SSE(const void* in, void* out, const size_t size, const size_t elem_size); /** SSE2-accelerated bitunshuffle routine. */ BLOSC_NO_EXPORT int64_t bshuf_untrans_bit_elem_SSE(const void* in, void* out, const size_t size, const size_t elem_size); #endif /* BLOSC_BITSHUFFLE_SSE2_H */ c-blosc2-3.1.5/blosc/blosc-private.h000066400000000000000000000234131521743436100171620ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #ifndef BLOSC_BLOSC_PRIVATE_H #define BLOSC_BLOSC_PRIVATE_H #include "blosc2/blosc2-common.h" #include "blosc2.h" #include #include #include #include /********************************************************************* Utility functions meant to be used internally. *********************************************************************/ int blosc2_decompress_block_ctx(blosc2_context* context, const void* src, int32_t srcsize, int32_t nblock, void* dest, int32_t destsize); int blosc2_run_parallel(int16_t nthreads, void (*dojob)(void *), size_t jobdata_elsize, void *jobdata); #define to_little(dest, src, itemsize) endian_handler(true, dest, src, itemsize) #define from_little(dest, src, itemsize) endian_handler(true, dest, src, itemsize) #define to_big(dest, src, itemsize) endian_handler(false, dest, src, itemsize) #define from_big(dest, src, itemsize) endian_handler(false, dest, src, itemsize) // Return true if platform is little endian; else false static bool is_little_endian(void) { static const int i = 1; char* p = (char*)&i; if (p[0] == 1) { return true; } else { return false; } } static inline void endian_handler(bool little, void *dest, const void *pa, int size) { bool little_endian = is_little_endian(); if (little_endian == little) { memcpy(dest, pa, size); } else { uint8_t* pa_ = (uint8_t*)pa; uint8_t pa2_[8]; switch (size) { case 8: pa2_[0] = pa_[7]; pa2_[1] = pa_[6]; pa2_[2] = pa_[5]; pa2_[3] = pa_[4]; pa2_[4] = pa_[3]; pa2_[5] = pa_[2]; pa2_[6] = pa_[1]; pa2_[7] = pa_[0]; break; case 4: pa2_[0] = pa_[3]; pa2_[1] = pa_[2]; pa2_[2] = pa_[1]; pa2_[3] = pa_[0]; break; case 2: pa2_[0] = pa_[1]; pa2_[1] = pa_[0]; break; case 1: pa2_[0] = pa_[0]; break; default: BLOSC_TRACE_ERROR("Unhandled size: %d.", size); } memcpy(dest, pa2_, size); } } /* * Convert a chunk count to the serialized offsets payload size. * * Security rationale: frame metadata can be attacker-controlled, and the offsets payload * eventually flows into APIs that accept int32 byte lengths. This helper enforces a * single overflow-checked conversion so callers cannot accidentally truncate * nchunks * sizeof(int64_t) into a smaller signed length. */ static inline bool blosc2_nchunks_to_offsets_nbytes(int64_t nchunks, int32_t *off_nbytes) { const int64_t max_nchunks = INT32_MAX / (int64_t)sizeof(int64_t); if (nchunks < 0 || nchunks > max_nchunks) { return false; } if (off_nbytes != NULL) { *off_nbytes = (int32_t)(nchunks * (int64_t)sizeof(int64_t)); } return true; } /* Copy 4 bytes from @p *pa to int32_t, changing endianness if necessary. */ static inline int32_t sw32_(const void* pa) { int32_t idest; bool little_endian = is_little_endian(); if (little_endian) { memcpy(&idest, pa, sizeof(idest)); } else { #if defined (__GNUC__) return __builtin_bswap32(*(unsigned int *)pa); #elif defined (_MSC_VER) /* Visual Studio */ return _byteswap_ulong(*(unsigned int *)pa); #else const uint8_t *pa_ = (const uint8_t *)pa; uint8_t *dest = (uint8_t *)&idest; dest[0] = pa_[3]; dest[1] = pa_[2]; dest[2] = pa_[1]; dest[3] = pa_[0]; #endif } return idest; } /* Copy 4 bytes from int32_t to @p *dest, changing endianness if necessary. */ static inline void _sw32(void* dest, int32_t a) { uint8_t* dest_ = (uint8_t*)dest; uint8_t* pa = (uint8_t*)&a; bool little_endian = is_little_endian(); if (little_endian) { memcpy(dest_, &a, sizeof(a));; } else { #if defined (__GNUC__) *(int32_t *)dest_ = __builtin_bswap32(*(unsigned int *)pa); #elif defined (_MSC_VER) /* Visual Studio */ *(int32_t *)dest_ = _byteswap_ulong(*(unsigned int *)pa); #else dest_[0] = pa[3]; dest_[1] = pa[2]; dest_[2] = pa[1]; dest_[3] = pa[0]; #endif } } /* Reverse swap bits in a 32-bit integer */ static inline int32_t bswap32_(int32_t a) { #if defined (__GNUC__) return __builtin_bswap32(a); #elif defined (_MSC_VER) /* Visual Studio */ return _byteswap_ulong(a); #else a = ((a & 0x000000FF) << 24) | ((a & 0x0000FF00) << 8) | ((a & 0x00FF0000) >> 8) | ((a & 0xFF000000) >> 24); return a; #endif } /** * @brief Register a filter in Blosc. * * @param filter The filter to register. * * @return 0 if succeeds. Else a negative code is returned. */ int register_filter_private(blosc2_filter *filter); /** * @brief Register a codec in Blosc. * * @param codec The codec to register. * * @return 0 if succeeds. Else a negative code is returned. */ int register_codec_private(blosc2_codec *codec); /** * @brief Register a tune in Blosc. * * @param tune The tune to register. * * @return 0 if succeeds. Else a negative code is returned. */ int register_tuner_private(blosc2_tuner *tuner); int fill_tuner(blosc2_tuner *tuner); extern blosc2_tuner g_tuners[256]; extern int g_ntuners; #if defined(_WIN32) #include #ifndef PATH_MAX #define PATH_MAX MAX_PATH #endif #define RTLD_LAZY 0x000 #define popen _popen #define pclose _pclose static struct { long lasterror; const char *err_rutin; } var = { 0, NULL }; static inline void *dlopen (const char *filename, int flags) { BLOSC_UNUSED_PARAM(flags); HINSTANCE hInst; hInst = LoadLibrary(filename); if (hInst==NULL) { var.lasterror = GetLastError(); var.err_rutin = "dlopen"; } return hInst; } static inline void *dlsym(void *handle, const char *name) { FARPROC fp; fp = GetProcAddress((HINSTANCE)handle, name); if (!fp) { var.lasterror = GetLastError (); var.err_rutin = "dlsym"; } return (void *)(intptr_t)fp; } static inline int dlclose(void *handle) { bool ok = FreeLibrary((HINSTANCE)handle); if (!ok) { var.lasterror = GetLastError(); var.err_rutin = "dlclose"; return BLOSC2_ERROR_FAILURE; } return BLOSC2_ERROR_SUCCESS; } static inline const char *dlerror (void) { static char errstr [88]; if (var.lasterror) { snprintf(errstr, sizeof(errstr), "%s error #%ld", var.err_rutin, var.lasterror); return errstr; } else { return NULL; } } #else #include #endif static inline bool blosc2_valid_plugin_name(const char *plugin_name) { if (plugin_name == NULL || plugin_name[0] == '\0') { return false; } for (const unsigned char *p = (const unsigned char *)plugin_name; *p != '\0'; ++p) { if (!isalnum(*p) && *p != '_') { return false; } } return true; } static inline int get_libpath(char *plugin_name, char *libpath, char *python_version) { BLOSC_TRACE_INFO("Trying to get plugin path with python%s\n", python_version); char python_cmd[PATH_MAX] = {0}; if (!blosc2_valid_plugin_name(plugin_name)) { BLOSC_TRACE_ERROR("Invalid plugin name"); return BLOSC2_ERROR_INVALID_PARAM; } int written = snprintf(python_cmd, sizeof(python_cmd), "python%s -c \"import blosc2_%s; blosc2_%s.print_libpath()\"", python_version, plugin_name, plugin_name); if (written < 0 || (size_t)written >= sizeof(python_cmd)) { BLOSC_TRACE_ERROR("Python command is too long"); return BLOSC2_ERROR_FAILURE; } FILE *fp = popen(python_cmd, "r"); if (fp == NULL) { BLOSC_TRACE_ERROR("Could not run python"); return BLOSC2_ERROR_FAILURE; } if (fgets(libpath, PATH_MAX, fp) == NULL) { BLOSC_TRACE_ERROR("Could not read python output"); pclose(fp); return BLOSC2_ERROR_FAILURE; } pclose(fp); return BLOSC2_ERROR_SUCCESS; } static inline void* load_lib(char *plugin_name, char *libpath) { if (!blosc2_valid_plugin_name(plugin_name)) { BLOSC_TRACE_ERROR("Invalid plugin name"); return NULL; } // Attempt to directly load the library by name #if defined(_WIN32) // Windows dynamic library (DLL) format snprintf(libpath, PATH_MAX, "blosc2_%s.dll", plugin_name); #else // Unix/Linux/Mac OS dynamic library (.so) format snprintf(libpath, PATH_MAX, "libblosc2_%s.so", plugin_name); #endif void* loaded_lib = dlopen(libpath, RTLD_LAZY); if (loaded_lib != NULL) { BLOSC_TRACE_INFO("Successfully loaded %s directly\n", libpath); return loaded_lib; } else { #if defined(_WIN32) BLOSC_TRACE_INFO("Failed to load %s directly, error: %lu\n", libpath, GetLastError()); #else BLOSC_TRACE_INFO("Failed to load %s directly, error: %s\n", libpath, dlerror()); #endif } // If direct loading fails, fallback to using Python to find the library path if (get_libpath(plugin_name, libpath, "") < 0 && get_libpath(plugin_name, libpath, "3") < 0) { BLOSC_TRACE_ERROR("Problems when running python or python3 for getting plugin path"); return NULL; } if (strlen(libpath) == 0) { BLOSC_TRACE_ERROR("Could not find plugin libpath"); return NULL; } // Try to load the library again with the path from Python loaded_lib = dlopen(libpath, RTLD_LAZY); if (loaded_lib == NULL) { BLOSC_TRACE_ERROR("Attempt to load plugin in path '%s' failed with error: %s", libpath, dlerror()); } else { BLOSC_TRACE_INFO("Successfully loaded library with Python path: %s\n", libpath); } return loaded_lib; } #endif /* BLOSC_BLOSC_PRIVATE_H */ c-blosc2-3.1.5/blosc/blosc2-stdio.c000066400000000000000000000677151521743436100167240ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #if defined(__linux__) /* Must be defined before anything else is included */ #define _GNU_SOURCE #endif #include "blosc2/blosc2-stdio.h" #include "blosc2.h" #include #include #include #include #include #include #include #if defined(_WIN32) #include // See https://github.com/Blosc/python-blosc2/issues/359 #define fseek _fseeki64 #define ftell _ftelli64 #else #include #endif static bool checked_mul_int64_nonneg(int64_t a, int64_t b, int64_t* out) { if (a < 0 || b < 0) { return false; } if (a == 0 || b == 0) { *out = 0; return true; } if (a > INT64_MAX / b) { return false; } *out = a * b; return true; } static bool checked_add_int64_nonneg(int64_t a, int64_t b, int64_t* out) { if (a < 0 || b < 0) { return false; } if (a > INT64_MAX - b) { return false; } *out = a + b; return true; } static bool checked_size_t_to_int64(size_t value, int64_t* out) { if (value > (size_t)INT64_MAX) { return false; } *out = (int64_t)value; return true; } void *blosc2_stdio_open(const char *urlpath, const char *mode, void *params) { BLOSC_UNUSED_PARAM(params); if (urlpath == NULL || mode == NULL) { BLOSC_TRACE_ERROR("Invalid arguments for stdio open."); return NULL; } FILE *file = fopen(urlpath, mode); if (file == NULL) { BLOSC_TRACE_ERROR("Cannot open the file %s with mode %s.", urlpath, mode); return NULL; } blosc2_stdio_file *my_fp = malloc(sizeof(blosc2_stdio_file)); if (my_fp == NULL) { BLOSC_TRACE_ERROR("Cannot allocate memory for stdio file wrapper."); fclose(file); return NULL; } my_fp->file = file; return my_fp; } int blosc2_stdio_close(void *stream) { if (stream == NULL) { BLOSC_TRACE_ERROR("Invalid stream for stdio close."); return -1; } blosc2_stdio_file *my_fp = (blosc2_stdio_file *) stream; if (my_fp->file == NULL) { BLOSC_TRACE_ERROR("Invalid stream for stdio close."); return -1; } int err = fclose(my_fp->file); free(my_fp); return err; } int64_t blosc2_stdio_size(void *stream) { if (stream == NULL) { BLOSC_TRACE_ERROR("Invalid stream for stdio size."); return -1; } blosc2_stdio_file *my_fp = (blosc2_stdio_file *) stream; if (my_fp->file == NULL) { BLOSC_TRACE_ERROR("Invalid stream for stdio size."); return -1; } int64_t current = ftell(my_fp->file); if (current < 0) { BLOSC_TRACE_ERROR("ftell failed while determining current position (error: %s).", strerror(errno)); return -1; } if (fseek(my_fp->file, 0, SEEK_END) != 0) { BLOSC_TRACE_ERROR("fseek to file end failed while getting size (error: %s).", strerror(errno)); fseek(my_fp->file, current, SEEK_SET); return -1; } int64_t size = ftell(my_fp->file); if (size < 0) { BLOSC_TRACE_ERROR("ftell failed while getting file size (error: %s).", strerror(errno)); fseek(my_fp->file, current, SEEK_SET); return -1; } if (fseek(my_fp->file, current, SEEK_SET) != 0) { BLOSC_TRACE_ERROR("fseek restore failed after getting file size (error: %s).", strerror(errno)); return -1; } return size; } int64_t blosc2_stdio_write(const void *ptr, int64_t size, int64_t nitems, int64_t position, void *stream) { if (stream == NULL || ptr == NULL || size < 0 || nitems < 0 || position < 0) { BLOSC_TRACE_ERROR("Invalid arguments for stdio write."); return 0; } blosc2_stdio_file *my_fp = (blosc2_stdio_file *) stream; if (my_fp->file == NULL) { BLOSC_TRACE_ERROR("Invalid arguments for stdio write."); return 0; } int64_t n_bytes_i64; if (!checked_mul_int64_nonneg(size, nitems, &n_bytes_i64)) { BLOSC_TRACE_ERROR("stdio write size overflow (size=%" PRId64 ", nitems=%" PRId64 ").", size, nitems); return 0; } if ((uint64_t)n_bytes_i64 > SIZE_MAX) { BLOSC_TRACE_ERROR("stdio write size does not fit in size_t (%" PRId64 ").", n_bytes_i64); return 0; } #if !defined(_WIN32) /* POSIX fseek takes long; reject offsets that would silently truncate (e.g. on 32-bit). */ if (position > (int64_t)LONG_MAX) { BLOSC_TRACE_ERROR("stdio write position %" PRId64 " exceeds LONG_MAX for fseek.", position); return 0; } #endif int rc = fseek(my_fp->file, position, SEEK_SET); if (rc != 0) { BLOSC_TRACE_ERROR("fseek failed at position %" PRId64 " (error: %s).", position, strerror(errno)); return 0; } size_t nitems_ = fwrite(ptr, (size_t) size, (size_t) nitems, my_fp->file); if ((int64_t)nitems_ != nitems) { BLOSC_TRACE_ERROR("Short write at position %" PRId64 ": requested %" PRId64 " items of size %" PRId64 ", wrote %zu (error: %s).", position, nitems, size, nitems_, strerror(errno)); } return (int64_t) nitems_; } int64_t blosc2_stdio_read(void **ptr, int64_t size, int64_t nitems, int64_t position, void *stream) { if (stream == NULL || ptr == NULL || size < 0 || nitems < 0 || position < 0) { BLOSC_TRACE_ERROR("Invalid arguments for stdio read."); return 0; } blosc2_stdio_file *my_fp = (blosc2_stdio_file *) stream; if (my_fp->file == NULL) { BLOSC_TRACE_ERROR("Invalid arguments for stdio read."); return 0; } int64_t n_bytes_i64; if (!checked_mul_int64_nonneg(size, nitems, &n_bytes_i64)) { BLOSC_TRACE_ERROR("stdio read size overflow (size=%" PRId64 ", nitems=%" PRId64 ").", size, nitems); return 0; } if ((uint64_t)n_bytes_i64 > SIZE_MAX) { BLOSC_TRACE_ERROR("stdio read size does not fit in size_t (%" PRId64 ").", n_bytes_i64); return 0; } /* For allocation-necessary backends, *ptr must point at a real buffer when the caller asked for any bytes. Leave *ptr untouched on failure. */ if (n_bytes_i64 > 0 && *ptr == NULL) { BLOSC_TRACE_ERROR("stdio read called with NULL buffer for %" PRId64 " bytes.", n_bytes_i64); return 0; } #if !defined(_WIN32) /* POSIX fseek takes long; reject offsets that would silently truncate (e.g. on 32-bit). */ if (position > (int64_t)LONG_MAX) { BLOSC_TRACE_ERROR("stdio read position %" PRId64 " exceeds LONG_MAX for fseek.", position); return 0; } #endif int rc = fseek(my_fp->file, position, SEEK_SET); if (rc != 0) { BLOSC_TRACE_ERROR("fseek failed at position %" PRId64 " (error: %s).", position, strerror(errno)); return 0; } void* data_ptr = *ptr; size_t nitems_ = fread(data_ptr, (size_t) size, (size_t) nitems, my_fp->file); if ((int64_t)nitems_ != nitems) { BLOSC_TRACE_ERROR("Short read at position %" PRId64 ": requested %" PRId64 " items of size %" PRId64 ", read %zu (error: %s).", position, nitems, size, nitems_, strerror(errno)); } return (int64_t) nitems_; } int blosc2_stdio_truncate(void *stream, int64_t size) { if (stream == NULL || size < 0) { BLOSC_TRACE_ERROR("Invalid arguments for stdio truncate."); return -1; } blosc2_stdio_file *my_fp = (blosc2_stdio_file *) stream; if (my_fp->file == NULL) { BLOSC_TRACE_ERROR("Invalid arguments for stdio truncate."); return -1; } int rc; #if defined(_MSC_VER) rc = _chsize_s(_fileno(my_fp->file), size); #else off_t size_off_t = (off_t)size; if ((int64_t)size_off_t != size) { BLOSC_TRACE_ERROR("stdio truncate size does not fit in off_t (%" PRId64 ").", size); return -1; } rc = ftruncate(fileno(my_fp->file), size_off_t); #endif return rc; } int blosc2_stdio_destroy(void* params) { BLOSC_UNUSED_PARAM(params); return 0; } #if defined(_WIN32) void _print_last_error() { DWORD last_error = GetLastError(); if(last_error == 0) { return; } LPSTR msg = NULL; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&msg, 0, NULL ); printf("Message for the error %lu:\n%s\n", last_error, msg); LocalFree(msg); } #endif void *blosc2_stdio_mmap_open(const char *urlpath, const char *mode, void* params) { BLOSC_UNUSED_PARAM(mode); if (urlpath == NULL || params == NULL) { BLOSC_TRACE_ERROR("Invalid arguments for memory-mapped open."); return NULL; } blosc2_stdio_mmap *mmap_file = (blosc2_stdio_mmap *) params; if (mmap_file->addr != NULL) { if (mmap_file->urlpath == NULL) { BLOSC_TRACE_ERROR("Memory-mapped file has invalid state: urlpath is NULL."); return NULL; } if (strcmp(mmap_file->urlpath, urlpath) != 0) { BLOSC_TRACE_ERROR( "The memory-mapped file is already opened with the path %s and hence cannot be reopened with the path %s. This " "happens if you try to open a sframe (sparse frame); please note that memory-mapped files are not supported " "for sframes.", mmap_file->urlpath, urlpath ); return NULL; } /* A memory-mapped file is only opened once */ return mmap_file; } if (mmap_file->mode == NULL) { BLOSC_TRACE_ERROR("Memory-mapped mode is NULL."); return NULL; } // Keep the original path to ensure that all future file openings are with the same path size_t urlpath_len = strlen(urlpath); mmap_file->urlpath = malloc(urlpath_len + 1); if (mmap_file->urlpath == NULL) { BLOSC_TRACE_ERROR("Cannot allocate memory for the path of the memory-mapped file."); return NULL; } memcpy(mmap_file->urlpath, urlpath, urlpath_len + 1); /* mmap_file->mode mapping is similar to Numpy's memmap (https://github.com/numpy/numpy/blob/main/numpy/_core/memmap.py) and CPython (https://github.com/python/cpython/blob/main/Modules/mmapmodule.c) */ #if defined(_WIN32) char* open_mode; bool use_initial_mapping_size; if (strcmp(mmap_file->mode, "r") == 0) { mmap_file->access_flags = PAGE_READONLY; mmap_file->map_flags = FILE_MAP_READ; mmap_file->is_memory_only = false; open_mode = "rb"; use_initial_mapping_size = false; } else if (strcmp(mmap_file->mode, "r+") == 0) { mmap_file->access_flags = PAGE_READWRITE; mmap_file->map_flags = FILE_MAP_WRITE; mmap_file->is_memory_only = false; open_mode = "rb+"; use_initial_mapping_size = true; } else if (strcmp(mmap_file->mode, "w+") == 0) { mmap_file->access_flags = PAGE_READWRITE; mmap_file->map_flags = FILE_MAP_WRITE; mmap_file->is_memory_only = false; open_mode = "wb+"; use_initial_mapping_size = true; } else if (strcmp(mmap_file->mode, "c") == 0) { mmap_file->access_flags = PAGE_WRITECOPY; mmap_file->map_flags = FILE_MAP_COPY; mmap_file->is_memory_only = true; open_mode = "rb"; use_initial_mapping_size = false; } else { BLOSC_TRACE_ERROR("Mode %s not supported for memory-mapped files.", mmap_file->mode); free(mmap_file->urlpath); mmap_file->urlpath = NULL; return NULL; } #else char* open_mode; bool use_initial_mapping_size; if (strcmp(mmap_file->mode, "r") == 0) { mmap_file->access_flags = PROT_READ; mmap_file->map_flags = MAP_SHARED; mmap_file->is_memory_only = false; open_mode = "rb"; use_initial_mapping_size = false; } else if (strcmp(mmap_file->mode, "r+") == 0) { mmap_file->access_flags = PROT_READ | PROT_WRITE; mmap_file->map_flags = MAP_SHARED; mmap_file->is_memory_only = false; open_mode = "rb+"; use_initial_mapping_size = true; } else if (strcmp(mmap_file->mode, "w+") == 0) { mmap_file->access_flags = PROT_READ | PROT_WRITE; mmap_file->map_flags = MAP_SHARED; mmap_file->is_memory_only = false; open_mode = "wb+"; use_initial_mapping_size = true; } else if (strcmp(mmap_file->mode, "c") == 0) { mmap_file->access_flags = PROT_READ | PROT_WRITE; mmap_file->map_flags = MAP_PRIVATE; mmap_file->is_memory_only = true; open_mode = "rb"; use_initial_mapping_size = true; } else { BLOSC_TRACE_ERROR("Mode %s not supported for memory-mapped files.", mmap_file->mode); free(mmap_file->urlpath); mmap_file->urlpath = NULL; return NULL; } #endif mmap_file->file = fopen(urlpath, open_mode); if (mmap_file->file == NULL) { BLOSC_TRACE_ERROR("Cannot open the file %s with mode %s.", urlpath, open_mode); free(mmap_file->urlpath); mmap_file->urlpath = NULL; return NULL; } /* Retrieve the size of the file */ if (fseek(mmap_file->file, 0, SEEK_END) != 0) { BLOSC_TRACE_ERROR("Cannot seek to the end of %s (error: %s).", urlpath, strerror(errno)); fclose(mmap_file->file); mmap_file->file = NULL; free(mmap_file->urlpath); mmap_file->urlpath = NULL; return NULL; } int64_t file_size_i64 = ftell(mmap_file->file); if (file_size_i64 < 0) { BLOSC_TRACE_ERROR("Cannot retrieve file size for %s.", urlpath); fclose(mmap_file->file); mmap_file->file = NULL; free(mmap_file->urlpath); mmap_file->urlpath = NULL; return NULL; } if ((uint64_t)file_size_i64 > SIZE_MAX) { BLOSC_TRACE_ERROR("File size for %s exceeds size_t range.", urlpath); fclose(mmap_file->file); mmap_file->file = NULL; free(mmap_file->urlpath); mmap_file->urlpath = NULL; return NULL; } mmap_file->file_size = (size_t)file_size_i64; if (fseek(mmap_file->file, 0, SEEK_SET) != 0) { BLOSC_TRACE_ERROR("Cannot seek to the beginning of %s (error: %s).", urlpath, strerror(errno)); fclose(mmap_file->file); mmap_file->file = NULL; free(mmap_file->urlpath); mmap_file->urlpath = NULL; return NULL; } /* The size of the mapping must be > 0 so we are using a large enough buffer for writing (which will be increased later if needed) */ if (use_initial_mapping_size) { mmap_file->mapping_size = mmap_file->initial_mapping_size; } else { mmap_file->mapping_size = mmap_file->file_size; } if (mmap_file->file_size > mmap_file->mapping_size) { mmap_file->mapping_size = mmap_file->file_size; } if (mmap_file->mapping_size == 0) { mmap_file->mapping_size = 1; } #if defined(_WIN32) mmap_file->fd = _fileno(mmap_file->file); /* Windows automatically expands the file size to the memory mapped file (https://learn.microsoft.com/en-us/windows/win32/memory/creating-a-file-mapping-object). In general, the size of the file is directly connected to the size of the mapping and cannot change. We cut the file size to the target size in the end after we close the mapping */ HANDLE file_handle = (HANDLE) _get_osfhandle(mmap_file->fd); uint64_t mapping_size64 = (uint64_t)mmap_file->mapping_size; DWORD size_hi = (DWORD)(mapping_size64 >> 32); DWORD size_lo = (DWORD)(mapping_size64 & 0xFFFFFFFFu); mmap_file->mmap_handle = CreateFileMapping(file_handle, NULL, mmap_file->access_flags, size_hi, size_lo, NULL); if (mmap_file->mmap_handle == NULL) { _print_last_error(); BLOSC_TRACE_ERROR("Creating the memory mapping failed for the file %s.", urlpath); fclose(mmap_file->file); mmap_file->file = NULL; free(mmap_file->urlpath); mmap_file->urlpath = NULL; return NULL; } DWORD offset = 0; mmap_file->addr = (char*) MapViewOfFile( mmap_file->mmap_handle, mmap_file->map_flags, offset, offset, mmap_file->mapping_size); if (mmap_file->addr == NULL) { _print_last_error(); BLOSC_TRACE_ERROR("Memory mapping failed for the file %s.", urlpath); if (!CloseHandle(mmap_file->mmap_handle)) { _print_last_error(); BLOSC_TRACE_ERROR("Cannot close the handle to the memory-mapped file."); } mmap_file->mmap_handle = INVALID_HANDLE_VALUE; fclose(mmap_file->file); mmap_file->file = NULL; free(mmap_file->urlpath); mmap_file->urlpath = NULL; return NULL; } #else mmap_file->fd = fileno(mmap_file->file); /* Offset where the mapping should start */ int64_t offset = 0; mmap_file->addr = mmap( NULL, mmap_file->mapping_size, mmap_file->access_flags, mmap_file->map_flags, mmap_file->fd, offset); if (mmap_file->addr == MAP_FAILED) { BLOSC_TRACE_ERROR("Memory mapping failed for file %s (error: %s).", urlpath, strerror(errno)); mmap_file->addr = NULL; fclose(mmap_file->file); mmap_file->file = NULL; free(mmap_file->urlpath); mmap_file->urlpath = NULL; return NULL; } #endif BLOSC_INFO( "Opened memory-mapped file %s in mode %s with an mapping size of %zu bytes.", mmap_file->urlpath, mmap_file->mode, mmap_file->mapping_size ); /* The mmap_file->mode parameter is only available during the opening call and cannot be used in any of the other I/O functions since this string is managed by the caller (e.g., from Python) and the memory of the string may not be available anymore at a later point. */ mmap_file->mode = NULL; return mmap_file; } int blosc2_stdio_mmap_close(void *stream) { BLOSC_UNUSED_PARAM(stream); return 0; } int64_t blosc2_stdio_mmap_size(void *stream) { blosc2_stdio_mmap *mmap_file = (blosc2_stdio_mmap *) stream; if (mmap_file->file_size > (size_t)INT64_MAX) { BLOSC_TRACE_ERROR("mmap file size exceeds int64_t return range (%zu).", mmap_file->file_size); return INT64_MAX; } return (int64_t)mmap_file->file_size; } int64_t blosc2_stdio_mmap_write(const void *ptr, int64_t size, int64_t nitems, int64_t position, void *stream) { blosc2_stdio_mmap *mmap_file = (blosc2_stdio_mmap *) stream; if (ptr == NULL || size < 0 || nitems < 0 || position < 0) { BLOSC_TRACE_ERROR("Invalid arguments for mmap write."); return 0; } int64_t n_bytes_i64; if (!checked_mul_int64_nonneg(size, nitems, &n_bytes_i64)) { BLOSC_TRACE_ERROR("mmap write size overflow (size=%" PRId64 ", nitems=%" PRId64 ").", size, nitems); return 0; } if ((uint64_t)n_bytes_i64 > SIZE_MAX) { BLOSC_TRACE_ERROR("mmap write size does not fit in size_t (%" PRId64 ").", n_bytes_i64); return 0; } size_t n_bytes = (size_t)n_bytes_i64; if (n_bytes == 0) { return 0; } int64_t position_end_i64; if (!checked_add_int64_nonneg(position, n_bytes_i64, &position_end_i64)) { BLOSC_TRACE_ERROR("mmap write position overflow (position=%" PRId64 ", nbytes=%" PRId64 ").", position, n_bytes_i64); return 0; } if ((uint64_t)position_end_i64 > SIZE_MAX) { BLOSC_TRACE_ERROR("mmap write end position does not fit in size_t (%" PRId64 ").", position_end_i64); return 0; } size_t position_size = (size_t)position; size_t position_end = (size_t)position_end_i64; size_t new_size = position_end > mmap_file->file_size ? position_end : mmap_file->file_size; #if defined(_WIN32) if (mmap_file->file_size < new_size) { mmap_file->file_size = new_size; } if (mmap_file->mapping_size < mmap_file->file_size) { size_t remap_size; if (mmap_file->file_size > SIZE_MAX / 2) { BLOSC_TRACE_WARNING("mmap remap growth fallback: cannot double mapping_size near SIZE_MAX; using file_size (%zu).", mmap_file->file_size); remap_size = mmap_file->file_size; } else { remap_size = mmap_file->file_size * 2; } if (remap_size > (size_t)INT64_MAX) { BLOSC_TRACE_ERROR("mmap mapping size exceeds supported OS range (%zu).", remap_size); return 0; } mmap_file->mapping_size = remap_size; /* We need to remap the file completely and cannot pass the previous used address on Windows */ if (!UnmapViewOfFile(mmap_file->addr)) { _print_last_error(); BLOSC_TRACE_ERROR("Cannot unmap the memory-mapped file."); return 0; } if (!CloseHandle(mmap_file->mmap_handle)) { _print_last_error(); BLOSC_TRACE_ERROR("Cannot close the handle to the memory-mapped file."); return 0; } HANDLE file_handle = (HANDLE) _get_osfhandle(mmap_file->fd); uint64_t mapping_size64 = (uint64_t)mmap_file->mapping_size; DWORD size_hi = (DWORD)(mapping_size64 >> 32); DWORD size_lo = (DWORD)(mapping_size64 & 0xFFFFFFFFu); mmap_file->mmap_handle = CreateFileMapping(file_handle, NULL, mmap_file->access_flags, size_hi, size_lo, NULL); if (mmap_file->mmap_handle == NULL) { _print_last_error(); BLOSC_TRACE_ERROR("Cannot remapt the memory-mapped file."); return 0; } DWORD offset = 0; char* new_address = (char*) MapViewOfFile( mmap_file->mmap_handle, mmap_file->map_flags, offset, offset, mmap_file->mapping_size); if (new_address == NULL) { _print_last_error(); BLOSC_TRACE_ERROR("Cannot remapt the memory-mapped file"); if (!CloseHandle(mmap_file->mmap_handle)) { _print_last_error(); BLOSC_TRACE_ERROR("Cannot close the handle to the memory-mapped file."); } return 0; } mmap_file->addr = new_address; } #else if (mmap_file->file_size < new_size) { mmap_file->file_size = new_size; if (!mmap_file->is_memory_only) { int64_t ftruncate_size; if (!checked_size_t_to_int64(new_size, &ftruncate_size)) { BLOSC_TRACE_ERROR("Cannot extend the file size to %zu bytes: value exceeds int64_t.", new_size); return 0; } int rc = ftruncate(mmap_file->fd, ftruncate_size); if (rc < 0) { BLOSC_TRACE_ERROR("Cannot extend the file size to %zu bytes (error: %s).", new_size, strerror(errno)); return 0; } } } if (mmap_file->mapping_size < mmap_file->file_size) { size_t new_mapping_size; if (mmap_file->file_size > SIZE_MAX / 2) { BLOSC_TRACE_WARNING("mmap remap growth fallback: cannot double mapping_size near SIZE_MAX; using file_size (%zu).", mmap_file->file_size); new_mapping_size = mmap_file->file_size; } else { new_mapping_size = mmap_file->file_size * 2; } #if defined(__linux__) /* mremap is the best option as it also ensures that the old data is still available in c mode. Unfortunately, it is no POSIX standard and only available on Linux */ size_t old_mapping_size = mmap_file->mapping_size; char* new_address = mremap(mmap_file->addr, old_mapping_size, new_mapping_size, MREMAP_MAYMOVE); #else if (mmap_file->is_memory_only) { BLOSC_TRACE_ERROR("Remapping a memory-mapping in c mode is only possible on Linux." "Please specify either a different mode or set initial_mapping_size to a large enough number."); return 0; } /* Extend the current mapping with the help of MAP_FIXED */ int64_t offset = 0; char* new_address = mmap( mmap_file->addr, new_mapping_size, mmap_file->access_flags, mmap_file->map_flags | MAP_FIXED, mmap_file->fd, offset ); #endif if (new_address == MAP_FAILED) { BLOSC_TRACE_ERROR("Cannot remap the memory-mapped file (error: %s).", strerror(errno)); return 0; } mmap_file->mapping_size = new_mapping_size; mmap_file->addr = new_address; } #endif memcpy(mmap_file->addr + position_size, ptr, n_bytes); return nitems; } int64_t blosc2_stdio_mmap_read(void **ptr, int64_t size, int64_t nitems, int64_t position, void *stream) { blosc2_stdio_mmap *mmap_file = (blosc2_stdio_mmap *) stream; if (ptr == NULL) { BLOSC_TRACE_ERROR("Invalid pointer argument for mmap read."); return 0; } if (size < 0 || nitems < 0 || position < 0) { BLOSC_TRACE_ERROR("Invalid arguments for mmap read."); *ptr = NULL; return 0; } int64_t n_bytes_i64; if (!checked_mul_int64_nonneg(size, nitems, &n_bytes_i64)) { BLOSC_TRACE_ERROR("mmap read size overflow (size=%" PRId64 ", nitems=%" PRId64 ").", size, nitems); *ptr = NULL; return 0; } int64_t position_end_i64; if (!checked_add_int64_nonneg(position, n_bytes_i64, &position_end_i64)) { BLOSC_TRACE_ERROR("mmap read position overflow (position=%" PRId64 ", nbytes=%" PRId64 ").", position, n_bytes_i64); *ptr = NULL; return 0; } if ((uint64_t)position_end_i64 > SIZE_MAX) { BLOSC_TRACE_ERROR("mmap read end position does not fit in size_t (%" PRId64 ").", position_end_i64); *ptr = NULL; return 0; } size_t position_size = (size_t)position; size_t position_end = (size_t)position_end_i64; if (position_end > mmap_file->file_size) { BLOSC_TRACE_ERROR("Cannot read beyond the end of the memory-mapped file."); *ptr = NULL; return 0; } *ptr = mmap_file->addr + position_size; return nitems; } int blosc2_stdio_mmap_truncate(void *stream, int64_t size) { blosc2_stdio_mmap *mmap_file = (blosc2_stdio_mmap *) stream; if (size < 0) { BLOSC_TRACE_ERROR("Cannot truncate mmap file to negative size (%" PRId64 ").", size); return -1; } if ((uint64_t)size > SIZE_MAX) { BLOSC_TRACE_ERROR("Cannot truncate mmap file to size beyond size_t range (%" PRId64 ").", size); return -1; } size_t target_size = (size_t)size; if (mmap_file->file_size == target_size) { return 0; } mmap_file->file_size = target_size; /* No file operations in c mode */ if (mmap_file->is_memory_only) { return 0; } #if defined(_WIN32) /* On Windows, we can truncate the file only at the end after we released the mapping */ return 0; #else return ftruncate(mmap_file->fd, size); #endif } int blosc2_stdio_mmap_destroy(void* params) { if (params == NULL) { BLOSC_TRACE_ERROR("Invalid arguments for memory-mapped destroy."); return -1; } blosc2_stdio_mmap *mmap_file = (blosc2_stdio_mmap *) params; int err = 0; #if defined(_WIN32) if (mmap_file->addr == NULL || mmap_file->mmap_handle == INVALID_HANDLE_VALUE || mmap_file->file == NULL) { BLOSC_TRACE_ERROR("Invalid memory-mapped state during destroy."); err = -1; goto cleanup; } if (mmap_file->access_flags == PAGE_READWRITE) { /* Ensure modified pages are written to disk */ if (!FlushViewOfFile(mmap_file->addr, mmap_file->file_size)) { _print_last_error(); BLOSC_TRACE_ERROR("Cannot flush the memory-mapped view to disk."); err = -1; } HANDLE file_handle = (HANDLE) _get_osfhandle(mmap_file->fd); if (!FlushFileBuffers(file_handle)) { _print_last_error(); BLOSC_TRACE_ERROR("Cannot flush the memory-mapped file to disk."); err = -1; } } if (!UnmapViewOfFile(mmap_file->addr)) { _print_last_error(); BLOSC_TRACE_ERROR("Cannot unmap the memory-mapped file."); err = -1; } if (!CloseHandle(mmap_file->mmap_handle)) { _print_last_error(); BLOSC_TRACE_ERROR("Cannot close the handle to the memory-mapped file."); err = -1; } int64_t file_size_i64; if (!checked_size_t_to_int64(mmap_file->file_size, &file_size_i64)) { BLOSC_TRACE_ERROR("Cannot extend the file size to %zu bytes: value exceeds int64_t.", mmap_file->file_size); err = -1; } else { int rc = _chsize_s(mmap_file->fd, (long long)file_size_i64); if (rc != 0) { BLOSC_TRACE_ERROR( "Cannot extend the file size to %zu bytes (error: %s).", mmap_file->file_size, strerror(errno)); err = -1; } } #else if (mmap_file->addr == NULL || mmap_file->file == NULL) { BLOSC_TRACE_ERROR("Invalid memory-mapped state during destroy."); err = -1; goto cleanup; } if ((mmap_file->access_flags & PROT_WRITE) && !mmap_file->is_memory_only) { /* Ensure modified pages are written to disk */ /* This is important since not every munmap implementation flushes modified pages to disk (e.g.: https://nfs.sourceforge.net/#faq_d8) */ int rc = msync(mmap_file->addr, mmap_file->file_size, MS_SYNC); if (rc < 0) { BLOSC_TRACE_ERROR("Cannot sync the memory-mapped file to disk (error: %s).", strerror(errno)); err = -1; } } if (munmap(mmap_file->addr, mmap_file->mapping_size) < 0) { BLOSC_TRACE_ERROR("Cannot unmap the memory-mapped file (error: %s).", strerror(errno)); err = -1; } #endif cleanup: /* Also closes the HANDLE on Windows */ if (mmap_file->file != NULL && fclose(mmap_file->file) < 0) { BLOSC_TRACE_ERROR("Could not close the memory-mapped file."); err = -1; } mmap_file->file = NULL; mmap_file->addr = NULL; free(mmap_file->urlpath); mmap_file->urlpath = NULL; if (mmap_file->needs_free) { free(mmap_file); } return err; } c-blosc2-3.1.5/blosc/blosc2.c000066400000000000000000007256521521743436100156050ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #include "blosc2.h" #include "blosc-private.h" #include "../plugins/codecs/zfp/blosc2-zfp.h" #include "frame.h" #include "sframe.h" #include "b2nd-private.h" #include "schunk-private.h" #if defined(USING_CMAKE) #include "config.h" #endif /* USING_CMAKE */ #include "context.h" #include "shuffle.h" #include "delta.h" #include "trunc-prec.h" #include "blosclz.h" #include "stune.h" #include "blosc2/codecs-registry.h" #include "blosc2/filters-registry.h" #include "blosc2/tuners-registry.h" #include "lz4.h" #define LZ4_HC_STATIC_LINKING_ONLY #include "lz4hc.h" #if defined(HAVE_ZLIB_NG) #ifdef ZLIB_COMPAT #include "zlib.h" #else #include "zlib-ng.h" #endif #elif defined(HAVE_ZLIB) #include "zlib.h" #endif /* HAVE_MINIZ */ #if defined(HAVE_ZSTD) #include "zstd.h" #include "zstd_errors.h" // #include "cover.h" // for experimenting with fast cover training for building dicts #include "zdict.h" #endif /* HAVE_ZSTD */ #if defined(_WIN32) && !defined(__MINGW32__) #include #include #include #define getpid _getpid #endif /* _WIN32 */ #include "threading.h" #include #include #include #include #include #include #include #include /* Synchronization variables */ /* Global context for non-contextual API */ static blosc2_context* g_global_context; static blosc2_pthread_mutex_t global_comp_mutex; static int g_compressor = BLOSC_BLOSCLZ; static int g_delta = 0; /* The default splitmode */ static int32_t g_splitmode = BLOSC_FORWARD_COMPAT_SPLIT; /* the compressor to use by default */ static int16_t g_nthreads = 1; static int32_t g_force_blocksize = 0; static int g_initlib = 0; static blosc2_schunk* g_schunk = NULL; /* the pointer to super-chunk */ blosc2_codec g_codecs[256] = {0}; uint8_t g_ncodecs = 0; static blosc2_filter g_filters[256] = {0}; static uint64_t g_nfilters = 0; static blosc2_io_cb g_ios[256] = {0}; static uint64_t g_nio = 0; blosc2_tuner g_tuners[256] = {0}; int g_ntuners = 0; static int g_tuner = BLOSC_STUNE; struct blosc_job_group { blosc2_context *context; int32_t next_block; int32_t next_output_block; int32_t blocks_completed; int32_t active_workers; int32_t pending_workers; int32_t output_bytes; int giveup_code; int dref_not_init; bool static_schedule; bool completed; blosc2_pthread_mutex_t mutex; blosc2_pthread_mutex_t delta_mutex; blosc2_pthread_cond_t delta_cv; blosc2_pthread_cond_t completion_cv; }; struct blosc_job_queue_entry { struct blosc_job_group *job; int32_t logical_tid; struct blosc_job_queue_entry *next; }; struct blosc_shared_pool { int16_t nthreads; int16_t shutdown; int32_t context_refs; int32_t active_jobs; blosc2_pthread_t *threads; struct thread_context *thread_contexts; blosc2_pthread_mutex_t mutex; blosc2_pthread_cond_t work_cv; blosc2_pthread_cond_t idle_cv; struct blosc_job_queue_entry *job_queue_head; struct blosc_job_queue_entry *job_queue_tail; struct blosc_shared_pool *next; #if !defined(_WIN32) pthread_attr_t ct_attr; #endif }; // Forward declarations static int init_callback_threads(blosc2_context *context); static int release_thread_backend(blosc2_context *context); static int attach_shared_pool(blosc2_context *context); #if defined(_WIN32) static int init_threadpool(blosc2_context *context); #endif static int parallel_blosc(blosc2_context* context); static inline bool checked_mul_size(size_t a, size_t b, size_t* out) { if (a != 0 && b > SIZE_MAX / a) { return false; } *out = a * b; return true; } static inline bool checked_add_size(size_t a, size_t b, size_t* out) { if (a > SIZE_MAX - b) { return false; } *out = a + b; return true; } /* global variable to change threading backend from Blosc-managed to caller-managed */ static blosc_threads_callback threads_callback = 0; static void *threads_callback_data = 0; static blosc2_pthread_mutex_t pool_registry_mutex; static struct blosc_shared_pool *shared_pools = NULL; /* Incremented each time blosc2_destroy() tears down the pool registry. * Contexts store the epoch at attach time; a mismatch means the pool they * hold a pointer to has already been freed. */ static volatile int32_t g_destroy_count = 0; /* non-threadsafe function should be called before any other Blosc function in order to change how threads are managed */ void blosc2_set_threads_callback(blosc_threads_callback callback, void *callback_data) { threads_callback = callback; threads_callback_data = callback_data; } typedef struct { void (*dojob)(void *); void *jobdata; } blosc2_parallel_job_data; static void *blosc2_parallel_pthread_entry(void *arg) { blosc2_parallel_job_data *job = (blosc2_parallel_job_data *)arg; job->dojob(job->jobdata); return NULL; } int blosc2_run_parallel(int16_t nthreads, void (*dojob)(void *), size_t jobdata_elsize, void *jobdata) { if (nthreads <= 0 || dojob == NULL || jobdata == NULL || jobdata_elsize == 0) { return BLOSC2_ERROR_INVALID_PARAM; } if (nthreads == 1) { dojob(jobdata); return BLOSC2_ERROR_SUCCESS; } if (threads_callback != NULL) { threads_callback(threads_callback_data, dojob, nthreads, jobdata_elsize, jobdata); return BLOSC2_ERROR_SUCCESS; } blosc2_pthread_t *threads = malloc((size_t)nthreads * sizeof(blosc2_pthread_t)); blosc2_parallel_job_data *jobs = malloc((size_t)nthreads * sizeof(blosc2_parallel_job_data)); if (threads == NULL || jobs == NULL) { free(threads); free(jobs); return BLOSC2_ERROR_MEMORY_ALLOC; } int16_t started = 0; for (int16_t i = 0; i < nthreads; ++i) { jobs[i].dojob = dojob; jobs[i].jobdata = (uint8_t *)jobdata + (size_t)i * jobdata_elsize; int err = blosc2_pthread_create(&threads[i], NULL, blosc2_parallel_pthread_entry, &jobs[i]); if (err != 0) { break; } ++started; } for (int16_t i = 0; i < started; ++i) { blosc2_pthread_join(threads[i], NULL); } free(threads); free(jobs); return started == nthreads ? BLOSC2_ERROR_SUCCESS : BLOSC2_ERROR_THREAD_CREATE; } /* A function for aligned malloc that is portable */ static uint8_t* my_malloc(size_t size) { void* block = NULL; int res = 0; /* Keep aligned allocations valid under Valgrind and POSIX wrappers. */ if (size == 0) { size = 1; } /* Do an alignment to 32 bytes because AVX2 is supported */ #if defined(_WIN32) /* A (void *) cast needed for avoiding a warning with MINGW :-/ */ block = (void *)_aligned_malloc(size, 32); #elif _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600 /* Platform does have an implementation of posix_memalign */ res = posix_memalign(&block, 32, size); #else block = malloc(size); #endif /* _WIN32 */ if (block == NULL || res != 0) { BLOSC_TRACE_ERROR("Error allocating memory!"); return NULL; } return (uint8_t*)block; } /* Release memory booked by my_malloc */ static void my_free(void* block) { #if defined(_WIN32) _aligned_free(block); #else free(block); #endif /* _WIN32 */ } /* * Conversion routines between compressor and compression libraries */ /* Return the library code associated with the compressor name */ static int compname_to_clibcode(const char* compname) { if (strcmp(compname, BLOSC_BLOSCLZ_COMPNAME) == 0) return BLOSC_BLOSCLZ_LIB; if (strcmp(compname, BLOSC_LZ4_COMPNAME) == 0) return BLOSC_LZ4_LIB; if (strcmp(compname, BLOSC_LZ4HC_COMPNAME) == 0) return BLOSC_LZ4_LIB; if (strcmp(compname, BLOSC_ZLIB_COMPNAME) == 0) return BLOSC_ZLIB_LIB; if (strcmp(compname, BLOSC_ZSTD_COMPNAME) == 0) return BLOSC_ZSTD_LIB; for (int i = 0; i < g_ncodecs; ++i) { if (strcmp(compname, g_codecs[i].compname) == 0) return g_codecs[i].complib; } return BLOSC2_ERROR_NOT_FOUND; } /* Return the library name associated with the compressor code */ static const char* clibcode_to_clibname(int clibcode) { if (clibcode == BLOSC_BLOSCLZ_LIB) return BLOSC_BLOSCLZ_LIBNAME; if (clibcode == BLOSC_LZ4_LIB) return BLOSC_LZ4_LIBNAME; if (clibcode == BLOSC_ZLIB_LIB) return BLOSC_ZLIB_LIBNAME; if (clibcode == BLOSC_ZSTD_LIB) return BLOSC_ZSTD_LIBNAME; for (int i = 0; i < g_ncodecs; ++i) { if (clibcode == g_codecs[i].complib) return g_codecs[i].compname; } return NULL; /* should never happen */ } /* * Conversion routines between compressor names and compressor codes */ /* Get the compressor name associated with the compressor code */ int blosc2_compcode_to_compname(int compcode, const char** compname) { int code = -1; /* -1 means non-existent compressor code */ const char* name = NULL; if (compcode == BLOSC_BLOSCLZ) { code = BLOSC_BLOSCLZ; name = BLOSC_BLOSCLZ_COMPNAME; } else if (compcode == BLOSC_LZ4) { code = BLOSC_LZ4; name = BLOSC_LZ4_COMPNAME; } else if (compcode == BLOSC_LZ4HC) { code = BLOSC_LZ4HC; name = BLOSC_LZ4HC_COMPNAME; } else if (compcode == BLOSC_ZLIB) { #if defined(HAVE_ZLIB) code = BLOSC_ZLIB; #endif /* HAVE_ZLIB */ name = BLOSC_ZLIB_COMPNAME; } else if (compcode == BLOSC_ZSTD) { #if defined(HAVE_ZSTD) code = BLOSC_ZSTD; #endif /* HAVE_ZSTD */ name = BLOSC_ZSTD_COMPNAME; } else { for (int i = 0; i < g_ncodecs; ++i) { if (compcode == g_codecs[i].compcode) { code = compcode; name = g_codecs[i].compname; break; } } } *compname = name; return code; } /* Get the compressor code for the compressor name. -1 if it is not available */ int blosc2_compname_to_compcode(const char* compname) { int code = -1; /* -1 means non-existent compressor code */ if (strcmp(compname, BLOSC_BLOSCLZ_COMPNAME) == 0) { code = BLOSC_BLOSCLZ; } else if (strcmp(compname, BLOSC_LZ4_COMPNAME) == 0) { code = BLOSC_LZ4; } else if (strcmp(compname, BLOSC_LZ4HC_COMPNAME) == 0) { code = BLOSC_LZ4HC; } #if defined(HAVE_ZLIB) else if (strcmp(compname, BLOSC_ZLIB_COMPNAME) == 0) { code = BLOSC_ZLIB; } #endif /* HAVE_ZLIB */ #if defined(HAVE_ZSTD) else if (strcmp(compname, BLOSC_ZSTD_COMPNAME) == 0) { code = BLOSC_ZSTD; } #endif /* HAVE_ZSTD */ else{ for (int i = 0; i < g_ncodecs; ++i) { if (strcmp(compname, g_codecs[i].compname) == 0) { code = g_codecs[i].compcode; break; } } } return code; } /* Convert compressor code to blosc compressor format code */ static int compcode_to_compformat(int compcode) { switch (compcode) { case BLOSC_BLOSCLZ: return BLOSC_BLOSCLZ_FORMAT; case BLOSC_LZ4: return BLOSC_LZ4_FORMAT; case BLOSC_LZ4HC: return BLOSC_LZ4HC_FORMAT; #if defined(HAVE_ZLIB) case BLOSC_ZLIB: return BLOSC_ZLIB_FORMAT; #endif /* HAVE_ZLIB */ #if defined(HAVE_ZSTD) case BLOSC_ZSTD: return BLOSC_ZSTD_FORMAT; break; #endif /* HAVE_ZSTD */ default: return BLOSC_UDCODEC_FORMAT; } BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } /* Convert compressor code to blosc compressor format version */ static int compcode_to_compversion(int compcode) { /* Write compressor format */ switch (compcode) { case BLOSC_BLOSCLZ: return BLOSC_BLOSCLZ_VERSION_FORMAT; case BLOSC_LZ4: return BLOSC_LZ4_VERSION_FORMAT; case BLOSC_LZ4HC: return BLOSC_LZ4HC_VERSION_FORMAT; #if defined(HAVE_ZLIB) case BLOSC_ZLIB: return BLOSC_ZLIB_VERSION_FORMAT; break; #endif /* HAVE_ZLIB */ #if defined(HAVE_ZSTD) case BLOSC_ZSTD: return BLOSC_ZSTD_VERSION_FORMAT; break; #endif /* HAVE_ZSTD */ default: for (int i = 0; i < g_ncodecs; ++i) { if (compcode == g_codecs[i].compcode) { return g_codecs[i].version; } } } return BLOSC2_ERROR_FAILURE; } static int lz4_wrap_compress(const char* input, size_t input_length, char* output, size_t maxout, int accel, struct thread_context* thread_context) { int cbytes; blosc2_context* context = thread_context->parent_context; if (context->use_dict && context->dict_cdict != NULL) { if (thread_context->lz4_cstream == NULL) { thread_context->lz4_cstream = LZ4_createStream(); } // Reset the thread stream to dict-only context before each block so that // each compressed block references only the dictionary (not prior blocks). // This ensures independent decompressibility with LZ4_decompress_safe_usingDict. LZ4_loadDict((LZ4_stream_t*)thread_context->lz4_cstream, (const char*)context->dict_buffer, (int)context->dict_size); cbytes = LZ4_compress_fast_continue((LZ4_stream_t*)thread_context->lz4_cstream, input, output, (int)input_length, (int)maxout, accel); } else { cbytes = LZ4_compress_fast(input, output, (int)input_length, (int)maxout, accel); } return cbytes; } static int lz4hc_wrap_compress(const char* input, size_t input_length, char* output, size_t maxout, int clevel, struct thread_context* thread_context) { int cbytes; if (input_length > (size_t)(UINT32_C(2) << 30)) return BLOSC2_ERROR_2GB_LIMIT; blosc2_context* context = thread_context->parent_context; /* clevel for lz4hc goes up to 12, at least in LZ4 1.7.5 * but levels larger than 9 do not buy much compression. */ if (context->use_dict && context->dict_cdict != NULL) { if (thread_context->lz4hc_cstream == NULL) { thread_context->lz4hc_cstream = LZ4_createStreamHC(); } // Reset to dict-only context (with correct clevel) before each block. LZ4_resetStreamHC_fast((LZ4_streamHC_t*)thread_context->lz4hc_cstream, clevel); LZ4_loadDictHC((LZ4_streamHC_t*)thread_context->lz4hc_cstream, (const char*)context->dict_buffer, (int)context->dict_size); cbytes = LZ4_compress_HC_continue((LZ4_streamHC_t*)thread_context->lz4hc_cstream, input, output, (int)input_length, (int)maxout); } else { cbytes = LZ4_compress_HC(input, output, (int)input_length, (int)maxout, clevel); } return cbytes; } static int lz4_wrap_decompress(const char* input, size_t compressed_length, char* output, size_t maxout, struct thread_context* thread_context) { int nbytes; blosc2_context* context = thread_context->parent_context; if (context->use_dict && context->dict_buffer != NULL && context->dict_size > 0) { nbytes = LZ4_decompress_safe_usingDict(input, output, (int)compressed_length, (int)maxout, (const char*)context->dict_buffer, (int)context->dict_size); } else { nbytes = LZ4_decompress_safe(input, output, (int)compressed_length, (int)maxout); } if (nbytes != (int)maxout) { return 0; } return (int)maxout; } #if defined(HAVE_ZLIB) /* zlib is not very respectful with sharing name space with others. Fortunately, its names do not collide with those already in blosc. */ static int zlib_wrap_compress(const char* input, size_t input_length, char* output, size_t maxout, int clevel) { int status; #if defined(HAVE_ZLIB_NG) && ! defined(ZLIB_COMPAT) size_t cl = maxout; status = zng_compress2( (uint8_t*)output, &cl, (uint8_t*)input, input_length, clevel); #else uLongf cl = (uLongf)maxout; status = compress2( (Bytef*)output, &cl, (Bytef*)input, (uLong)input_length, clevel); #endif if (status != Z_OK) { return 0; } return (int)cl; } static int zlib_wrap_decompress(const char* input, size_t compressed_length, char* output, size_t maxout) { int status; #if defined(HAVE_ZLIB_NG) && ! defined(ZLIB_COMPAT) size_t ul = maxout; status = zng_uncompress( (uint8_t*)output, &ul, (uint8_t*)input, compressed_length); #else uLongf ul = (uLongf)maxout; status = uncompress( (Bytef*)output, &ul, (Bytef*)input, (uLong)compressed_length); #endif if (status != Z_OK) { return 0; } return (int)ul; } #endif /* HAVE_ZLIB */ #if defined(HAVE_ZSTD) static int zstd_wrap_compress(struct thread_context* thread_context, const char* input, size_t input_length, char* output, size_t maxout, int clevel) { size_t code; blosc2_context* context = thread_context->parent_context; clevel = (clevel < 9) ? clevel * 2 - 1 : ZSTD_maxCLevel(); /* Make the level 8 close enough to maxCLevel */ if (clevel == 8) clevel = ZSTD_maxCLevel() - 2; if (thread_context->zstd_cctx == NULL) { thread_context->zstd_cctx = ZSTD_createCCtx(); } if (context->use_dict) { assert(context->dict_cdict != NULL); code = ZSTD_compress_usingCDict( thread_context->zstd_cctx, (void*)output, maxout, (void*)input, input_length, context->dict_cdict); } else { code = ZSTD_compressCCtx(thread_context->zstd_cctx, (void*)output, maxout, (void*)input, input_length, clevel); } if (ZSTD_isError(code) != ZSTD_error_no_error) { // Blosc will just memcpy this buffer return 0; } return (int)code; } static int zstd_wrap_decompress(struct thread_context* thread_context, const char* input, size_t compressed_length, char* output, size_t maxout) { size_t code; blosc2_context* context = thread_context->parent_context; if (thread_context->zstd_dctx == NULL) { thread_context->zstd_dctx = ZSTD_createDCtx(); } if (context->use_dict) { assert(context->dict_ddict != NULL); code = ZSTD_decompress_usingDDict( thread_context->zstd_dctx, (void*)output, maxout, (void*)input, compressed_length, context->dict_ddict); } else { code = ZSTD_decompressDCtx(thread_context->zstd_dctx, (void*)output, maxout, (void*)input, compressed_length); } if (ZSTD_isError(code) != ZSTD_error_no_error) { BLOSC_TRACE_ERROR("Error in ZSTD decompression: '%s'. Giving up.", ZDICT_getErrorName(code)); return 0; } return (int)code; } #endif /* HAVE_ZSTD */ /* Compute acceleration for blosclz */ static int get_accel(const blosc2_context* context) { int clevel = context->clevel; if (context->compcode == BLOSC_LZ4) { /* This acceleration setting based on discussions held in: * https://groups.google.com/forum/#!topic/lz4c/zosy90P8MQw */ return (10 - clevel); } return 1; } int do_nothing(uint8_t filter, char cmode) { if (cmode == 'c') { return (filter == BLOSC_NOFILTER); } else { // TRUNC_PREC do not have to be applied during decompression return ((filter == BLOSC_NOFILTER) || (filter == BLOSC_TRUNC_PREC)); } } int next_filter(const uint8_t* filters, int current_filter, char cmode) { for (int i = current_filter - 1; i >= 0; i--) { if (!do_nothing(filters[i], cmode)) { return filters[i]; } } return BLOSC_NOFILTER; } int last_filter(const uint8_t* filters, char cmode) { int last_index = -1; for (int i = BLOSC2_MAX_FILTERS - 1; i >= 0; i--) { if (!do_nothing(filters[i], cmode)) { last_index = i; } } return last_index; } /* Convert filter pipeline to filter flags */ static uint8_t filters_to_flags(const uint8_t* filters) { uint8_t flags = 0; for (int i = 0; i < BLOSC2_MAX_FILTERS; i++) { switch (filters[i]) { case BLOSC_SHUFFLE: flags |= BLOSC_DOSHUFFLE; break; case BLOSC_BITSHUFFLE: flags |= BLOSC_DOBITSHUFFLE; break; case BLOSC_DELTA: flags |= BLOSC_DODELTA; break; default : break; } } return flags; } /* Convert filter flags to filter pipeline */ static void flags_to_filters(const uint8_t flags, uint8_t* filters) { /* Initialize the filter pipeline */ memset(filters, 0, BLOSC2_MAX_FILTERS); /* Fill the filter pipeline */ if (flags & BLOSC_DOSHUFFLE) filters[BLOSC2_MAX_FILTERS - 1] = BLOSC_SHUFFLE; if (flags & BLOSC_DOBITSHUFFLE) filters[BLOSC2_MAX_FILTERS - 1] = BLOSC_BITSHUFFLE; if (flags & BLOSC_DODELTA) filters[BLOSC2_MAX_FILTERS - 2] = BLOSC_DELTA; } /* Get filter flags from header flags */ static uint8_t get_filter_flags(const uint8_t header_flags, const int32_t typesize) { uint8_t flags = 0; if ((header_flags & BLOSC_DOSHUFFLE) && (typesize > 1)) { flags |= BLOSC_DOSHUFFLE; } if (header_flags & BLOSC_DOBITSHUFFLE) { flags |= BLOSC_DOBITSHUFFLE; } if (header_flags & BLOSC_DODELTA) { flags |= BLOSC_DODELTA; } if (header_flags & BLOSC_MEMCPYED) { flags |= BLOSC_MEMCPYED; } return flags; } typedef struct blosc_header_s { uint8_t version; uint8_t versionlz; uint8_t flags; uint8_t typesize; int32_t nbytes; int32_t blocksize; int32_t cbytes; // Extended Blosc2 header uint8_t filters[BLOSC2_MAX_FILTERS]; uint8_t udcompcode; uint8_t compcode_meta; uint8_t filters_meta[BLOSC2_MAX_FILTERS]; uint8_t blosc2_flags2; uint8_t blosc2_flags; } blosc_header; int read_chunk_header(const uint8_t* src, int32_t srcsize, bool extended_header, blosc_header* header) { memset(header, 0, sizeof(blosc_header)); if (srcsize < BLOSC_MIN_HEADER_LENGTH) { BLOSC_TRACE_ERROR("Not enough space to read Blosc header."); return BLOSC2_ERROR_READ_BUFFER; } memcpy(header, src, BLOSC_MIN_HEADER_LENGTH); bool little_endian = is_little_endian(); if (!little_endian) { header->nbytes = bswap32_(header->nbytes); header->blocksize = bswap32_(header->blocksize); header->cbytes = bswap32_(header->cbytes); } if (header->cbytes < BLOSC_MIN_HEADER_LENGTH) { BLOSC_TRACE_ERROR("`cbytes` is too small to read min header."); return BLOSC2_ERROR_INVALID_HEADER; } if (header->blocksize <= 0) { BLOSC_TRACE_ERROR("`blocksize` is zero"); return BLOSC2_ERROR_INVALID_HEADER; } if (header->blocksize > BLOSC2_MAXBLOCKSIZE) { BLOSC_TRACE_ERROR("`blocksize` greater than maximum allowed"); return BLOSC2_ERROR_INVALID_HEADER; } if (header->typesize == 0) { BLOSC_TRACE_ERROR("`typesize` is zero."); return BLOSC2_ERROR_INVALID_HEADER; } /* Read extended header if it is wanted */ if ((extended_header) && (header->flags & BLOSC_DOSHUFFLE) && (header->flags & BLOSC_DOBITSHUFFLE)) { if (header->cbytes < BLOSC_EXTENDED_HEADER_LENGTH) { BLOSC_TRACE_ERROR("`cbytes` is too small to read extended header."); return BLOSC2_ERROR_INVALID_HEADER; } if (srcsize < BLOSC_EXTENDED_HEADER_LENGTH) { BLOSC_TRACE_ERROR("Not enough space to read Blosc extended header."); return BLOSC2_ERROR_READ_BUFFER; } memcpy((uint8_t *)header + BLOSC_MIN_HEADER_LENGTH, src + BLOSC_MIN_HEADER_LENGTH, BLOSC_EXTENDED_HEADER_LENGTH - BLOSC_MIN_HEADER_LENGTH); if ((header->blosc2_flags2 & BLOSC2_VL_BLOCKS) && (header->blosc2_flags != 0)) { int32_t special_type = (header->blosc2_flags >> 4) & BLOSC2_SPECIAL_MASK; if (special_type != 0) { BLOSC_TRACE_ERROR("VL-block chunks cannot use special chunk encodings."); return BLOSC2_ERROR_INVALID_HEADER; } } int32_t special_type = (header->blosc2_flags >> 4) & BLOSC2_SPECIAL_MASK; if (special_type != 0) { if (special_type == BLOSC2_SPECIAL_VALUE) { // In this case, the actual type size must be derived from the cbytes int32_t typesize = header->cbytes - BLOSC_EXTENDED_HEADER_LENGTH; if (typesize <= 0) { BLOSC_TRACE_ERROR("`typesize` is zero or negative"); return BLOSC2_ERROR_INVALID_HEADER; } if (typesize > BLOSC2_MAXTYPESIZE) { BLOSC_TRACE_ERROR("`typesize` is greater than maximum allowed"); return BLOSC2_ERROR_INVALID_HEADER; } if (typesize > header->nbytes) { BLOSC_TRACE_ERROR("`typesize` is greater than `nbytes`"); return BLOSC2_ERROR_INVALID_HEADER; } if (header->nbytes % typesize != 0) { BLOSC_TRACE_ERROR("`nbytes` is not a multiple of typesize"); return BLOSC2_ERROR_INVALID_HEADER; } } else { // All-zero chunks fill with memset, which works regardless of element alignment. if (special_type != BLOSC2_SPECIAL_ZERO && header->nbytes % header->typesize != 0) { BLOSC_TRACE_ERROR("`nbytes` is not a multiple of typesize"); return BLOSC2_ERROR_INVALID_HEADER; } } } // The number of filters depends on the version of the header. Blosc2 alpha series // did not initialize filters to zero beyond the max supported. if (header->version == BLOSC2_VERSION_FORMAT_ALPHA) { header->filters[5] = 0; header->filters_meta[5] = 0; } } else { flags_to_filters(header->flags, header->filters); } if (header->version > BLOSC2_VERSION_FORMAT && (header->blosc2_flags2 & (uint8_t)~BLOSC2_VL_BLOCKS) != 0) { /* Version from future with unsupported chunk features. */ return BLOSC2_ERROR_VERSION_SUPPORT; } if ((header->blosc2_flags2 & BLOSC2_VL_BLOCKS) != 0 && (header->flags & (uint8_t)BLOSC_MEMCPYED) != 0) { BLOSC_TRACE_ERROR("VL-block chunks cannot be memcpyed."); return BLOSC2_ERROR_INVALID_HEADER; } if ((header->blosc2_flags2 & BLOSC2_VL_BLOCKS) == 0 && header->nbytes > 0 && header->blocksize > header->nbytes) { header->blocksize = header->nbytes; } return 0; } static inline void blosc2_calculate_blocks(blosc2_context* context) { /* Compute number of blocks in buffer */ context->nblocks = context->sourcesize / context->blocksize; context->leftover = context->sourcesize % context->blocksize; context->nblocks = (context->leftover > 0) ? (context->nblocks + 1) : context->nblocks; } static int blosc2_initialize_context_from_header(blosc2_context* context, blosc_header* header) { context->header_flags = header->flags; context->typesize = header->typesize; context->sourcesize = header->nbytes; context->header_blocksize = header->blocksize; context->blocksize = header->blocksize; context->blosc2_flags2 = header->blosc2_flags2; context->blosc2_flags = header->blosc2_flags; context->compcode = header->flags >> 5; if (context->compcode == BLOSC_UDCODEC_FORMAT) { context->compcode = header->udcompcode; } if (context->blosc2_flags2 & BLOSC2_VL_BLOCKS) { context->nblocks = header->blocksize; context->leftover = 0; context->blocksize = 0; } else { blosc2_calculate_blocks(context); } bool is_lazy = false; if ((context->header_flags & BLOSC_DOSHUFFLE) && (context->header_flags & BLOSC_DOBITSHUFFLE)) { /* Extended header */ context->header_overhead = BLOSC_EXTENDED_HEADER_LENGTH; memcpy(context->filters, header->filters, BLOSC2_MAX_FILTERS); memcpy(context->filters_meta, header->filters_meta, BLOSC2_MAX_FILTERS); context->compcode_meta = header->compcode_meta; context->filter_flags = filters_to_flags(header->filters); context->special_type = (header->blosc2_flags >> 4) & BLOSC2_SPECIAL_MASK; is_lazy = (context->blosc2_flags & 0x08u); } else { context->header_overhead = BLOSC_MIN_HEADER_LENGTH; context->filter_flags = get_filter_flags(context->header_flags, context->typesize); flags_to_filters(context->header_flags, context->filters); } // Some checks for malformed headers if (!is_lazy && header->cbytes > context->srcsize) { return BLOSC2_ERROR_INVALID_HEADER; } return 0; } int fill_filter(blosc2_filter *filter) { char libpath[PATH_MAX]; void *lib = load_lib(filter->name, libpath); if(lib == NULL) { BLOSC_TRACE_ERROR("Error while loading the library"); return BLOSC2_ERROR_FAILURE; } filter_info *info = dlsym(lib, "info"); filter->forward = dlsym(lib, info->forward); filter->backward = dlsym(lib, info->backward); if (filter->forward == NULL || filter->backward == NULL){ BLOSC_TRACE_ERROR("Wrong library loaded"); dlclose(lib); return BLOSC2_ERROR_FAILURE; } return BLOSC2_ERROR_SUCCESS; } int fill_codec(blosc2_codec *codec) { char libpath[PATH_MAX]; void *lib = load_lib(codec->compname, libpath); if(lib == NULL) { BLOSC_TRACE_ERROR("Error while loading the library for codec `%s`", codec->compname); return BLOSC2_ERROR_FAILURE; } codec_info *info = dlsym(lib, "info"); if (info == NULL) { BLOSC_TRACE_ERROR("`info` symbol cannot be loaded from plugin `%s`", codec->compname); dlclose(lib); return BLOSC2_ERROR_FAILURE; } codec->encoder = dlsym(lib, info->encoder); codec->decoder = dlsym(lib, info->decoder); if (codec->encoder == NULL || codec->decoder == NULL) { BLOSC_TRACE_ERROR("encoder or decoder cannot be loaded from plugin `%s`", codec->compname); dlclose(lib); return BLOSC2_ERROR_FAILURE; } /* If ever add .free function in future for codec params codecparams_info *info2 = dlsym(lib, "info2"); if (info2 != NULL) { // New plugin (e.g. openzl) with free function for codec_params defined // will be used when destroying context in blosc2_free_ctx codec->free = dlsym(lib, info2->free); } else{ codec->free = NULL; } */ return BLOSC2_ERROR_SUCCESS; } int fill_tuner(blosc2_tuner *tuner) { char libpath[PATH_MAX] = {0}; void *lib = load_lib(tuner->name, libpath); if(lib == NULL) { BLOSC_TRACE_ERROR("Error while loading the library"); return BLOSC2_ERROR_FAILURE; } tuner_info *info = dlsym(lib, "info"); tuner->init = dlsym(lib, info->init); tuner->update = dlsym(lib, info->update); tuner->next_blocksize = dlsym(lib, info->next_blocksize); tuner->free = dlsym(lib, info->free); tuner->next_cparams = dlsym(lib, info->next_cparams); if (tuner->init == NULL || tuner->update == NULL || tuner->next_blocksize == NULL || tuner->free == NULL || tuner->next_cparams == NULL){ BLOSC_TRACE_ERROR("Wrong library loaded"); dlclose(lib); return BLOSC2_ERROR_FAILURE; } return BLOSC2_ERROR_SUCCESS; } static int blosc2_initialize_header_from_context(blosc2_context* context, blosc_header* header, bool extended_header) { int32_t header_blocksize = (int32_t)(context->header_blocksize > 0 ? context->header_blocksize : context->blocksize); if ((context->blosc2_flags2 & BLOSC2_VL_BLOCKS) == 0 && context->sourcesize > 0 && header_blocksize > context->sourcesize) { header_blocksize = (int32_t)context->sourcesize; } memset(header, 0, sizeof(blosc_header)); header->version = (context->blosc2_flags2 & BLOSC2_VL_BLOCKS) ? BLOSC2_VERSION_FORMAT_VL_BLOCKS : BLOSC2_VERSION_FORMAT_STABLE; header->versionlz = compcode_to_compversion(context->compcode); header->flags = context->header_flags; header->typesize = (uint8_t)context->typesize; header->nbytes = (int32_t)context->sourcesize; header->blocksize = header_blocksize; int little_endian = is_little_endian(); if (!little_endian) { header->nbytes = bswap32_(header->nbytes); header->blocksize = bswap32_(header->blocksize); // cbytes written after compression } if (extended_header) { /* Store filter pipeline info at the end of the header */ for (int i = 0; i < BLOSC2_MAX_FILTERS; i++) { header->filters[i] = context->filters[i]; header->filters_meta[i] = context->filters_meta[i]; } header->udcompcode = context->compcode; header->compcode_meta = context->compcode_meta; header->blosc2_flags2 = context->blosc2_flags2; if (!little_endian) { header->blosc2_flags |= BLOSC2_BIGENDIAN; } if (context->use_dict) { header->blosc2_flags |= BLOSC2_USEDICT; } if (context->blosc2_flags & BLOSC2_INSTR_CODEC) { header->blosc2_flags |= BLOSC2_INSTR_CODEC; } } return 0; } void _cycle_buffers(uint8_t **src, uint8_t **dest, uint8_t **tmp) { uint8_t *tmp2 = *src; *src = *dest; *dest = *tmp; *tmp = tmp2; } uint8_t* pipeline_forward(struct thread_context* thread_context, const int32_t bsize, const uint8_t* src, const int32_t offset, uint8_t* dest, uint8_t* tmp) { blosc2_context* context = thread_context->parent_context; uint8_t* _src = (uint8_t*)src + offset; uint8_t* _tmp = tmp; uint8_t* _dest = dest; int32_t typesize = context->typesize; uint8_t* filters = context->filters; uint8_t* filters_meta = context->filters_meta; bool memcpyed = context->header_flags & (uint8_t)BLOSC_MEMCPYED; bool output_is_disposable = (context->preparams != NULL) ? context->preparams->output_is_disposable : false; /* Prefilter function */ if (context->prefilter != NULL) { // Create new prefilter parameters for this block (must be private for each thread) blosc2_prefilter_params preparams; memcpy(&preparams, context->preparams, sizeof(preparams)); // Calculate output_size based on number of elements and output typesize int32_t nelems = bsize / typesize; // number of elements in the input block // If output_typesize is not set (0), default to input typesize (no type conversion) int32_t output_typesize_actual = (preparams.output_typesize > 0) ? preparams.output_typesize : typesize; int32_t output_size = nelems * output_typesize_actual; // output size in bytes preparams.output_typesize = output_typesize_actual; // ensure it's set /* Set unwritten values to zero */ if (!output_is_disposable) { memset(_dest, 0, output_size); } preparams.input = _src; preparams.output = _dest; preparams.output_size = output_size; preparams.output_offset = offset; preparams.nblock = offset / context->blocksize; preparams.nchunk = context->schunk != NULL ? context->schunk->current_nchunk : -1; preparams.tid = thread_context->tid; preparams.ttmp = thread_context->tmp; preparams.ttmp_nbytes = thread_context->tmp_nbytes; preparams.ctx = context; preparams.output_is_disposable = output_is_disposable; if (context->prefilter(&preparams) != 0) { if (output_is_disposable) { // Output is going to be discarded; no more filters are required BLOSC_TRACE_INFO("Output is disposable"); return _dest; } BLOSC_TRACE_ERROR("Execution of prefilter function failed"); return NULL; } if (memcpyed) { // No more filters are required return _dest; } _cycle_buffers(&_src, &_dest, &_tmp); } /* Process the filter pipeline */ for (int i = 0; i < BLOSC2_MAX_FILTERS; i++) { int rc = BLOSC2_ERROR_SUCCESS; if (filters[i] <= BLOSC2_DEFINED_FILTERS_STOP) { switch (filters[i]) { case BLOSC_SHUFFLE: // if filters_meta is different to 0, interpret it as grouped bytes to shuffle blosc2_shuffle(filters_meta[i] == 0 ? typesize : filters_meta[i], bsize, _src, _dest); break; case BLOSC_BITSHUFFLE: if (blosc2_bitshuffle(typesize, bsize, _src, _dest) < 0) { return NULL; } break; case BLOSC_DELTA: delta_encoder(offset == 0 ? _src : src, offset, bsize, typesize, _src, _dest); break; case BLOSC_TRUNC_PREC: if (truncate_precision(filters_meta[i], typesize, bsize, _src, _dest) < 0) { return NULL; } break; default: if (filters[i] != BLOSC_NOFILTER) { BLOSC_TRACE_ERROR("Filter %d not handled during compression\n", filters[i]); return NULL; } } } else { // Look for the filters_meta in user filters and run it for (uint64_t j = 0; j < g_nfilters; ++j) { if (g_filters[j].id == filters[i]) { if (g_filters[j].forward == NULL) { // Dynamically load library if (fill_filter(&g_filters[j]) < 0) { BLOSC_TRACE_ERROR("Could not load filter %d\n", g_filters[j].id); return NULL; } } if (g_filters[j].forward != NULL) { blosc2_cparams cparams; blosc2_ctx_get_cparams(context, &cparams); rc = g_filters[j].forward(_src, _dest, bsize, filters_meta[i], &cparams, g_filters[j].id); } else { BLOSC_TRACE_ERROR("Forward function is NULL"); return NULL; } if (rc != BLOSC2_ERROR_SUCCESS) { BLOSC_TRACE_ERROR("User-defined filter %d failed during compression\n", filters[i]); return NULL; } goto urfiltersuccess; } } BLOSC_TRACE_ERROR("User-defined filter %d not found during compression\n", filters[i]); return NULL; urfiltersuccess:; } // Cycle buffers when required if (filters[i] != BLOSC_NOFILTER) { _cycle_buffers(&_src, &_dest, &_tmp); } } return _src; } // Optimized version for detecting runs. It compares 8 bytes values wherever possible. static bool get_run(const uint8_t* ip, const uint8_t* ip_bound) { uint8_t x = *ip; int64_t value, value2; /* Broadcast the value for every byte in a 64-bit register */ memset(&value, x, 8); while (ip < (ip_bound - 8)) { #if defined(BLOSC_STRICT_ALIGN) memcpy(&value2, ip, 8); #else value2 = *(int64_t*)ip; #endif if (value != value2) { // Values differ. We don't have a run. return false; } else { ip += 8; } } /* Look into the remainder */ while ((ip < ip_bound) && (*ip == x)) ip++; return ip == ip_bound ? true : false; } /* Shuffle & compress a single block */ static int blosc_c(struct thread_context* thread_context, int32_t bsize, int32_t leftoverblock, int32_t ntbytes, int32_t destsize, const uint8_t* src, const int32_t offset, uint8_t* dest, uint8_t* tmp, uint8_t* tmp2) { blosc2_context* context = thread_context->parent_context; int dont_split = (context->header_flags & 0x10) >> 4; bool vlblocks = (context->blosc2_flags2 & BLOSC2_VL_BLOCKS) != 0; int dict_training = context->use_dict && context->dict_cdict == NULL; int32_t j, neblock, nstreams; int32_t cbytes; /* number of compressed bytes in split */ int32_t ctbytes = 0; /* number of compressed bytes in block */ int32_t maxout; int32_t typesize = context->typesize; bool output_is_disposable = (context->preparams != NULL) ? context->preparams->output_is_disposable : false; const char* compname; int accel; const uint8_t* _src; uint8_t *_tmp = tmp, *_tmp2 = tmp2; int last_filter_index = last_filter(context->filters, 'c'); bool memcpyed = context->header_flags & (uint8_t)BLOSC_MEMCPYED; bool instr_codec = context->blosc2_flags & BLOSC2_INSTR_CODEC; blosc_timestamp_t last, current; float filter_time = 0.f; if (instr_codec) { blosc_set_timestamp(&last); } // See whether we have a run here if (last_filter_index >= 0 || context->prefilter != NULL) { /* Apply the filter pipeline just for the prefilter */ if (memcpyed && context->prefilter != NULL) { // We only need the prefilter output _src = pipeline_forward(thread_context, bsize, src, offset, dest, _tmp2); if (_src == NULL) { return BLOSC2_ERROR_FILTER_PIPELINE; } return bsize; } /* Apply regular filter pipeline */ _src = pipeline_forward(thread_context, bsize, src, offset, _tmp, _tmp2); if (_src == NULL) { return BLOSC2_ERROR_FILTER_PIPELINE; } } else { _src = src + offset; } if (instr_codec) { blosc_set_timestamp(¤t); filter_time = (float) blosc_elapsed_secs(last, current); last = current; } assert(context->clevel > 0); /* Calculate acceleration for different compressors */ accel = get_accel(context); /* The number of compressed data streams for this block */ if (!dont_split && !leftoverblock && !dict_training) { nstreams = (int32_t)typesize; } else { nstreams = 1; } neblock = bsize / nstreams; for (j = 0; j < nstreams; j++) { if (instr_codec) { blosc_set_timestamp(&last); } if (!dict_training) { dest += sizeof(int32_t); ntbytes += sizeof(int32_t); ctbytes += sizeof(int32_t); if (!vlblocks && context->header_overhead == BLOSC_EXTENDED_HEADER_LENGTH && output_is_disposable) { // Simulate a run of 0s BLOSC_TRACE_INFO("Output is disposable, simulating a run of 0s"); memset(dest - 4, 0, sizeof(int32_t)); continue; } const uint8_t *ip = (uint8_t *) _src + j * neblock; const uint8_t *ipbound = (uint8_t *) _src + (j + 1) * neblock; if (!vlblocks && context->header_overhead == BLOSC_EXTENDED_HEADER_LENGTH && get_run(ip, ipbound)) { // A run int32_t value = _src[j * neblock]; if (ntbytes > destsize) { return 0; /* Non-compressible data */ } if (instr_codec) { blosc_set_timestamp(¤t); int32_t instr_size = sizeof(blosc2_instr); ntbytes += instr_size; ctbytes += instr_size; if (ntbytes > destsize) { return 0; /* Non-compressible data */ } _sw32(dest - 4, instr_size); blosc2_instr *desti = (blosc2_instr *)dest; memset(desti, 0, sizeof(blosc2_instr)); // Special values have an overhead of about 1 int32 int32_t ssize = value == 0 ? sizeof(int32_t) : sizeof(int32_t) + 1; desti->cratio = (float) neblock / (float) ssize; float ctime = (float) blosc_elapsed_secs(last, current); desti->cspeed = (float) neblock / ctime; desti->filter_speed = (float) neblock / filter_time; desti->flags[0] = 1; // mark a runlen dest += instr_size; continue; } // Encode the repeated byte in the first (LSB) byte of the length of the split. _sw32(dest - 4, -value); // write the value in two's complement if (value > 0) { // Mark encoding as a run-length (== 0 is always a 0's run) ntbytes += 1; ctbytes += 1; if (ntbytes > destsize) { return 0; /* Non-compressible data */ } // Set MSB bit (sign) to 1 (not really necessary here, but for demonstration purposes) // dest[-1] |= 0x80; dest[0] = 0x1; // set run-length bit (0) in token dest += 1; } continue; } } maxout = neblock; if (ntbytes + maxout > destsize && !instr_codec) { /* avoid buffer * overrun */ maxout = destsize - ntbytes; if (maxout <= 0) { return 0; /* non-compressible block */ } } if (dict_training) { // We are in the build dict state, so don't compress // TODO: copy only a percentage for sampling memcpy(dest, _src + j * neblock, (unsigned int)neblock); cbytes = (int32_t)neblock; } else if (context->compcode == BLOSC_BLOSCLZ) { cbytes = blosclz_compress(context->clevel, _src + j * neblock, (int)neblock, dest, maxout, context); } else if (context->compcode == BLOSC_LZ4) { cbytes = lz4_wrap_compress((char*)_src + j * neblock, (size_t)neblock, (char*)dest, (size_t)maxout, accel, thread_context); } else if (context->compcode == BLOSC_LZ4HC) { cbytes = lz4hc_wrap_compress((char*)_src + j * neblock, (size_t)neblock, (char*)dest, (size_t)maxout, context->clevel, thread_context); } #if defined(HAVE_ZLIB) else if (context->compcode == BLOSC_ZLIB) { cbytes = zlib_wrap_compress((char*)_src + j * neblock, (size_t)neblock, (char*)dest, (size_t)maxout, context->clevel); } #endif /* HAVE_ZLIB */ #if defined(HAVE_ZSTD) else if (context->compcode == BLOSC_ZSTD) { cbytes = zstd_wrap_compress(thread_context, (char*)_src + j * neblock, (size_t)neblock, (char*)dest, (size_t)maxout, context->clevel); } #endif /* HAVE_ZSTD */ else if (context->compcode > BLOSC2_DEFINED_CODECS_STOP) { for (int i = 0; i < g_ncodecs; ++i) { if (g_codecs[i].compcode == context->compcode) { if (g_codecs[i].encoder == NULL) { // Dynamically load codec plugin if (fill_codec(&g_codecs[i]) < 0) { BLOSC_TRACE_ERROR("Could not load codec %d.", g_codecs[i].compcode); return BLOSC2_ERROR_CODEC_SUPPORT; } } blosc2_cparams cparams; blosc2_ctx_get_cparams(context, &cparams); cbytes = g_codecs[i].encoder(_src + j * neblock, neblock, dest, maxout, context->compcode_meta, &cparams, context->src); goto urcodecsuccess; } } BLOSC_TRACE_ERROR("User-defined compressor codec %d not found during compression", context->compcode); return BLOSC2_ERROR_CODEC_SUPPORT; urcodecsuccess: ; } else { blosc2_compcode_to_compname(context->compcode, &compname); BLOSC_TRACE_ERROR("Blosc has not been compiled with '%s' compression support." "Please use one having it.", compname); return BLOSC2_ERROR_CODEC_SUPPORT; } if (cbytes > maxout) { /* Buffer overrun caused by compression (should never happen) */ return BLOSC2_ERROR_WRITE_BUFFER; } if (cbytes < 0) { /* cbytes should never be negative */ return BLOSC2_ERROR_DATA; } if (cbytes == 0) { // When cbytes is 0, the compressor has not been able to compress anything cbytes = neblock; } if (instr_codec) { blosc_set_timestamp(¤t); int32_t instr_size = sizeof(blosc2_instr); ntbytes += instr_size; ctbytes += instr_size; if (ntbytes > destsize) { return 0; /* Non-compressible data */ } _sw32(dest - 4, vlblocks ? neblock : instr_size); float ctime = (float)blosc_elapsed_secs(last, current); blosc2_instr *desti = (blosc2_instr *)dest; memset(desti, 0, sizeof(blosc2_instr)); // cratio is computed having into account 1 additional int (csize) desti->cratio = (float)neblock / (float)(cbytes + sizeof(int32_t)); desti->cspeed = (float)neblock / ctime; desti->filter_speed = (float) neblock / filter_time; dest += instr_size; continue; } if (!dict_training) { if (cbytes == neblock) { /* The compressor has been unable to compress data at all. */ /* Before doing the copy, check that we are not running into a buffer overflow. */ if ((ntbytes + neblock) > destsize) { return 0; /* Non-compressible data */ } memcpy(dest, _src + j * neblock, (unsigned int)neblock); cbytes = neblock; } _sw32(dest - 4, vlblocks ? neblock : cbytes); } dest += cbytes; ntbytes += cbytes; ctbytes += cbytes; } /* Closes j < nstreams */ return ctbytes; } /* Process the filter pipeline (decompression mode) */ int pipeline_backward(struct thread_context* thread_context, const int32_t bsize, uint8_t* dest, const int32_t offset, uint8_t* src, uint8_t* tmp, uint8_t* tmp2, int last_filter_index, int32_t nblock) { blosc2_context* context = thread_context->parent_context; int32_t typesize = context->typesize; uint8_t* filters = context->filters; uint8_t* filters_meta = context->filters_meta; uint8_t* _src = src; uint8_t* _dest = tmp; uint8_t* _tmp = tmp2; int errcode = 0; for (int i = BLOSC2_MAX_FILTERS - 1; i >= 0; i--) { // Delta filter requires the whole chunk ready int last_copy_filter = (last_filter_index == i) || (filters[i] == BLOSC_DELTA) || (next_filter(filters, i, 'd') == BLOSC_DELTA); if (last_copy_filter && context->postfilter == NULL && (filters[i] == BLOSC_DELTA || _src != dest + offset)) { _dest = dest + offset; } int rc = BLOSC2_ERROR_SUCCESS; if (filters[i] <= BLOSC2_DEFINED_FILTERS_STOP) { switch (filters[i]) { case BLOSC_SHUFFLE: // if filters_meta is not 0, interpret as number of bytes to be grouped together for shuffle blosc2_unshuffle(filters_meta[i] == 0 ? typesize : filters_meta[i], bsize, _src, _dest); break; case BLOSC_BITSHUFFLE: if (bitunshuffle(typesize, bsize, _src, _dest, context->src[BLOSC2_CHUNK_VERSION]) < 0) { return BLOSC2_ERROR_FILTER_PIPELINE; } break; case BLOSC_DELTA: if (context->nthreads == 1) { /* Serial mode */ delta_decoder(dest, offset, bsize, typesize, _dest); } else { struct blosc_job_group *job = context->job; blosc2_pthread_mutex_t *delta_mutex = job != NULL ? &job->delta_mutex : &context->delta_mutex; blosc2_pthread_cond_t *delta_cv = job != NULL ? &job->delta_cv : &context->delta_cv; int *dref_not_init = job != NULL ? &job->dref_not_init : &context->dref_not_init; /* Force the thread in charge of the block 0 to go first */ blosc2_pthread_mutex_lock(delta_mutex); if (*dref_not_init) { if (offset != 0) { blosc2_pthread_cond_wait(delta_cv, delta_mutex); } else { delta_decoder(dest, offset, bsize, typesize, _dest); *dref_not_init = 0; blosc2_pthread_cond_broadcast(delta_cv); } } blosc2_pthread_mutex_unlock(delta_mutex); if (offset != 0) { delta_decoder(dest, offset, bsize, typesize, _dest); } } break; case BLOSC_TRUNC_PREC: // TRUNC_PREC filter does not need to be undone break; default: if (filters[i] != BLOSC_NOFILTER) { BLOSC_TRACE_ERROR("Filter %d not handled during decompression.", filters[i]); errcode = -1; } } } else { // Look for the filters_meta in user filters and run it for (uint64_t j = 0; j < g_nfilters; ++j) { if (g_filters[j].id == filters[i]) { if (g_filters[j].backward == NULL) { // Dynamically load filter if (fill_filter(&g_filters[j]) < 0) { BLOSC_TRACE_ERROR("Could not load filter %d.", g_filters[j].id); return BLOSC2_ERROR_FILTER_PIPELINE; } } if (g_filters[j].backward != NULL) { blosc2_dparams dparams; blosc2_ctx_get_dparams(context, &dparams); rc = g_filters[j].backward(_src, _dest, bsize, filters_meta[i], &dparams, g_filters[j].id); } else { BLOSC_TRACE_ERROR("Backward function is NULL"); return BLOSC2_ERROR_FILTER_PIPELINE; } if (rc != BLOSC2_ERROR_SUCCESS) { BLOSC_TRACE_ERROR("User-defined filter %d failed during decompression.", filters[i]); return rc; } goto urfiltersuccess; } } BLOSC_TRACE_ERROR("User-defined filter %d not found during decompression.", filters[i]); return BLOSC2_ERROR_FILTER_PIPELINE; urfiltersuccess:; } // Cycle buffers when required if ((filters[i] != BLOSC_NOFILTER) && (filters[i] != BLOSC_TRUNC_PREC)) { _cycle_buffers(&_src, &_dest, &_tmp); } if (last_filter_index == i) { break; } } if (context->postfilter == NULL && _src != dest + offset) { memcpy(dest + offset, _src, (unsigned int)bsize); } /* Postfilter function */ if (context->postfilter != NULL) { // Create new postfilter parameters for this block (must be private for each thread) blosc2_postfilter_params postparams; memcpy(&postparams, context->postparams, sizeof(postparams)); postparams.input = _src; postparams.output = dest + offset; postparams.size = bsize; postparams.typesize = typesize; postparams.offset = nblock * context->blocksize; postparams.nchunk = context->schunk != NULL ? context->schunk->current_nchunk : -1; postparams.nblock = nblock; postparams.tid = thread_context->tid; postparams.ttmp = thread_context->tmp; postparams.ttmp_nbytes = thread_context->tmp_nbytes; postparams.ctx = context; if (context->postfilter(&postparams) != 0) { BLOSC_TRACE_ERROR("Execution of postfilter function failed"); return BLOSC2_ERROR_POSTFILTER; } } return errcode; } static int32_t set_nans(int32_t typesize, uint8_t* dest, int32_t destsize) { if (destsize % typesize != 0) { BLOSC_TRACE_ERROR("destsize can only be a multiple of typesize"); BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } int32_t nitems = destsize / typesize; if (nitems == 0) { return 0; } if (typesize == 4) { float* dest_ = (float*)dest; float val = nanf(""); for (int i = 0; i < nitems; i++) { dest_[i] = val; } return nitems; } else if (typesize == 8) { double* dest_ = (double*)dest; double val = nan(""); for (int i = 0; i < nitems; i++) { dest_[i] = val; } return nitems; } BLOSC_TRACE_ERROR("Unsupported typesize for NaN"); return BLOSC2_ERROR_DATA; } static int32_t set_values(int32_t typesize, const uint8_t* src, uint8_t* dest, int32_t destsize) { #if defined(BLOSC_STRICT_ALIGN) if (destsize % typesize != 0) { BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } int32_t nitems = destsize / typesize; if (nitems == 0) { return 0; } for (int i = 0; i < nitems; i++) { memcpy(dest + i * typesize, src + BLOSC_EXTENDED_HEADER_LENGTH, typesize); } #else if (destsize % typesize != 0) { BLOSC_ERROR(BLOSC2_ERROR_FAILURE); } int32_t nitems = destsize / typesize; if (nitems == 0) { return 0; } switch (typesize) { case 8: { int64_t val8 = ((int64_t*)(src + BLOSC_EXTENDED_HEADER_LENGTH))[0]; int64_t* dest8 = (int64_t*)dest; for (int i = 0; i < nitems; i++) { dest8[i] = val8; } break; } case 4: { int32_t val4 = ((int32_t*)(src + BLOSC_EXTENDED_HEADER_LENGTH))[0]; int32_t* dest4 = (int32_t*)dest; for (int i = 0; i < nitems; i++) { dest4[i] = val4; } break; } case 2: { int16_t val2 = ((int16_t*)(src + BLOSC_EXTENDED_HEADER_LENGTH))[0]; int16_t* dest2 = (int16_t*)dest; for (int i = 0; i < nitems; i++) { dest2[i] = val2; } break; } case 1: { int8_t val1 = ((int8_t*)(src + BLOSC_EXTENDED_HEADER_LENGTH))[0]; int8_t* dest1 = (int8_t*)dest; for (int i = 0; i < nitems; i++) { dest1[i] = val1; } break; } default: for (int i = 0; i < nitems; i++) { memcpy(dest + i * typesize, src + BLOSC_EXTENDED_HEADER_LENGTH, typesize); } } #endif return nitems; } /* Decompress & unshuffle a single block */ static int blosc_d( struct thread_context* thread_context, int32_t bsize, int32_t leftoverblock, bool memcpyed, const uint8_t* src, int32_t srcsize, int32_t src_offset, int32_t nblock, uint8_t* dest, int32_t dest_offset, uint8_t* tmp, uint8_t* tmp2) { blosc2_context* context = thread_context->parent_context; uint8_t* filters = context->filters; uint8_t *tmp3 = thread_context->tmp4; bool vlblocks = (context->blosc2_flags2 & BLOSC2_VL_BLOCKS) != 0; int32_t compformat = (context->header_flags & (uint8_t)0xe0) >> 5u; int dont_split = (context->header_flags & 0x10) >> 4; int32_t chunk_nbytes; int32_t chunk_cbytes; int nstreams; int32_t neblock; int32_t nbytes; /* number of decompressed bytes in split */ int32_t cbytes; /* number of compressed bytes in split */ // int32_t ctbytes = 0; /* number of compressed bytes in block */ int32_t ntbytes = 0; /* number of uncompressed bytes in block */ uint8_t* _dest; int32_t typesize = context->typesize; bool instr_codec = context->blosc2_flags & BLOSC2_INSTR_CODEC; const char* compname; int rc; if (context->block_maskout != NULL && context->block_maskout[nblock]) { // Do not decompress, but act as if we successfully decompressed everything return bsize; } rc = blosc2_cbuffer_sizes(src, &chunk_nbytes, &chunk_cbytes, NULL); if (rc < 0) { return rc; } if (context->special_type == BLOSC2_SPECIAL_VALUE) { // We need the actual typesize in this case, but it cannot be encoded in the header, so derive it from cbytes typesize = chunk_cbytes - context->header_overhead; } // In some situations (lazychunks) the context can arrive uninitialized // (but BITSHUFFLE needs it for accessing the format of the chunk) if (context->src == NULL) { context->src = src; } // Chunks with special values cannot be lazy bool is_lazy = ((context->header_overhead == BLOSC_EXTENDED_HEADER_LENGTH) && (context->blosc2_flags & 0x08u) && !context->special_type); if (is_lazy) { // The chunk is on disk, so just lazily load the block if (context->schunk == NULL) { BLOSC_TRACE_ERROR("Lazy chunk needs an associated super-chunk."); return BLOSC2_ERROR_INVALID_PARAM; } if (context->schunk->frame == NULL) { BLOSC_TRACE_ERROR("Lazy chunk needs an associated frame."); return BLOSC2_ERROR_INVALID_PARAM; } blosc2_frame_s* frame = (blosc2_frame_s*)context->schunk->frame; char* urlpath = frame->urlpath; size_t bstarts_nbytes; size_t trailer_offset; size_t block_csizes_nbytes; size_t trailer_meta_nbytes; size_t lazy_trailer_end; if (srcsize < 0 || context->nblocks < 0 || !checked_mul_size((size_t)context->nblocks, sizeof(int32_t), &bstarts_nbytes) || !checked_add_size((size_t)BLOSC_EXTENDED_HEADER_LENGTH, bstarts_nbytes, &trailer_offset) || !checked_mul_size((size_t)context->nblocks, sizeof(int32_t), &block_csizes_nbytes) || !checked_add_size(sizeof(int32_t) + sizeof(int64_t), block_csizes_nbytes, &trailer_meta_nbytes) || !checked_add_size(trailer_offset, trailer_meta_nbytes, &lazy_trailer_end) || (size_t)srcsize < lazy_trailer_end) { BLOSC_TRACE_ERROR("Lazy trailer exceeds source buffer."); return BLOSC2_ERROR_READ_BUFFER; } int32_t nchunk; int64_t chunk_offset; // The nchunk and the offset of the current chunk are in the trailer memcpy(&nchunk, src + trailer_offset, sizeof(nchunk)); memcpy(&chunk_offset, src + trailer_offset + sizeof(int32_t), sizeof(chunk_offset)); // Get the csize of the nblock if (nblock < 0 || nblock >= context->nblocks) { BLOSC_TRACE_ERROR("Invalid block index in lazy trailer."); return BLOSC2_ERROR_INVALID_HEADER; } int32_t *block_csizes = (int32_t *)(src + trailer_offset + sizeof(int32_t) + sizeof(int64_t)); int32_t block_csize = block_csizes[nblock]; int32_t max_lazy_block_csize = context->blocksize + context->typesize * (signed)sizeof(int32_t); if (block_csize <= 0 || block_csize > max_lazy_block_csize) { BLOSC_TRACE_ERROR("Invalid lazy block size in trailer."); return BLOSC2_ERROR_INVALID_HEADER; } if (vlblocks && block_csize <= (int32_t)sizeof(int32_t)) { BLOSC_TRACE_ERROR("Lazy VL block compressed size is too small."); return BLOSC2_ERROR_INVALID_HEADER; } // Read the lazy block on disk void* fp = NULL; blosc2_io_cb *io_cb = blosc2_get_io_cb(context->schunk->storage->io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); return BLOSC2_ERROR_PLUGIN_IO; } int64_t io_pos = 0; if (frame->sframe) { // The chunk is not in the frame fp = sframe_open_chunk(frame->urlpath, nchunk, "rb", context->schunk->storage->io); BLOSC_ERROR_NULL(fp, BLOSC2_ERROR_FILE_OPEN); // The offset of the block is src_offset if (src_offset < 0) { io_cb->close(fp); BLOSC_TRACE_ERROR("Lazy block offset cannot be negative."); return BLOSC2_ERROR_INVALID_HEADER; } io_pos = src_offset; } else { fp = io_cb->open(urlpath, "rb", context->schunk->storage->io->params); BLOSC_ERROR_NULL(fp, BLOSC2_ERROR_FILE_OPEN); // The offset of the block is src_offset if (src_offset < 0) { io_cb->close(fp); BLOSC_TRACE_ERROR("Lazy block offset cannot be negative."); return BLOSC2_ERROR_INVALID_HEADER; } if (chunk_offset < 0) { io_cb->close(fp); BLOSC_TRACE_ERROR("Lazy chunk offset cannot be negative."); return BLOSC2_ERROR_INVALID_HEADER; } if (frame->file_offset > INT64_MAX - chunk_offset) { io_cb->close(fp); BLOSC_TRACE_ERROR("Lazy chunk offset overflows file position."); return BLOSC2_ERROR_INVALID_HEADER; } io_pos = frame->file_offset + chunk_offset; if (io_pos > INT64_MAX - src_offset) { io_cb->close(fp); BLOSC_TRACE_ERROR("Lazy block offset overflows file position."); return BLOSC2_ERROR_INVALID_HEADER; } io_pos += src_offset; } // We can make use of tmp3 because it will be used after src is not needed anymore int64_t rbytes = io_cb->read((void**)&tmp3, 1, block_csize, io_pos, fp); io_cb->close(fp); if ((int32_t)rbytes != block_csize) { BLOSC_TRACE_ERROR("Cannot read the (lazy) block out of the fileframe."); return BLOSC2_ERROR_READ_BUFFER; } src = tmp3; src_offset = 0; srcsize = block_csize; } // If the chunk is memcpyed, we just have to copy the block to dest and return if (memcpyed) { int bsize_ = leftoverblock ? chunk_nbytes % context->blocksize : bsize; if (!context->special_type) { if (chunk_nbytes + context->header_overhead != chunk_cbytes) { return BLOSC2_ERROR_WRITE_BUFFER; } if (chunk_cbytes < context->header_overhead + (nblock * context->blocksize) + bsize_) { /* Not enough input to copy block */ return BLOSC2_ERROR_READ_BUFFER; } } if (!is_lazy) { src += context->header_overhead + nblock * context->blocksize; } _dest = dest + dest_offset; if (context->postfilter != NULL) { // We are making use of a postfilter, so use a temp for destination _dest = tmp; } rc = 0; switch (context->special_type) { case BLOSC2_SPECIAL_VALUE: // All repeated values rc = set_values(typesize, context->src, _dest, bsize_); if (rc < 0) { BLOSC_TRACE_ERROR("set_values failed"); return BLOSC2_ERROR_DATA; } break; case BLOSC2_SPECIAL_NAN: rc = set_nans(context->typesize, _dest, bsize_); if (rc < 0) { BLOSC_TRACE_ERROR("set_nans failed"); return BLOSC2_ERROR_DATA; } break; case BLOSC2_SPECIAL_ZERO: memset(_dest, 0, bsize_); break; case BLOSC2_SPECIAL_UNINIT: // We do nothing here break; default: memcpy(_dest, src, bsize_); } if (context->postfilter != NULL) { // Create new postfilter parameters for this block (must be private for each thread) blosc2_postfilter_params postparams; memcpy(&postparams, context->postparams, sizeof(postparams)); postparams.input = tmp; postparams.output = dest + dest_offset; postparams.size = bsize; postparams.typesize = typesize; postparams.offset = nblock * context->blocksize; postparams.nchunk = context->schunk != NULL ? context->schunk->current_nchunk : -1; postparams.nblock = nblock; postparams.tid = thread_context->tid; postparams.ttmp = thread_context->tmp; postparams.ttmp_nbytes = thread_context->tmp_nbytes; postparams.ctx = context; // Execute the postfilter (the processed block will be copied to dest) if (context->postfilter(&postparams) != 0) { BLOSC_TRACE_ERROR("Execution of postfilter function failed"); return BLOSC2_ERROR_POSTFILTER; } } thread_context->zfp_cell_nitems = 0; return bsize_; } if (!is_lazy && (src_offset <= 0 || src_offset >= srcsize)) { /* Invalid block src offset encountered */ return BLOSC2_ERROR_DATA; } src += src_offset; if (vlblocks) { if (context->blockcbytes == NULL || nblock >= context->nblocks || context->blockcbytes[nblock] <= (int32_t)sizeof(int32_t) || src_offset > srcsize - context->blockcbytes[nblock]) { return BLOSC2_ERROR_DATA; } srcsize = context->blockcbytes[nblock]; } else { srcsize -= src_offset; } int last_filter_index = last_filter(filters, 'd'); if (instr_codec) { // If instrumented, we don't want to run the filters _dest = dest + dest_offset; } else if (((last_filter_index >= 0) && (next_filter(filters, BLOSC2_MAX_FILTERS, 'd') != BLOSC_DELTA)) || context->postfilter != NULL) { // We are making use of some filter, so use a temp for destination _dest = tmp; } else { // If no filters, or only DELTA in pipeline _dest = dest + dest_offset; } /* The number of compressed data streams for this block */ if (vlblocks) { nstreams = 1; } else if (!dont_split && !leftoverblock) { nstreams = context->typesize; } else { nstreams = 1; } neblock = bsize / nstreams; if (neblock == 0) { /* Not enough space to output bytes */ BLOSC_ERROR(BLOSC2_ERROR_WRITE_BUFFER); } for (int j = 0; j < nstreams; j++) { if (vlblocks) { if (srcsize < (signed)sizeof(int32_t)) { /* Not enough input to read compressed bytes */ return BLOSC2_ERROR_READ_BUFFER; } neblock = sw32_(src); if (neblock != bsize) { return BLOSC2_ERROR_DATA; } src += sizeof(int32_t); cbytes = srcsize - (int32_t)sizeof(int32_t); srcsize = 0; } else { if (srcsize < (signed)sizeof(int32_t)) { /* Not enough input to read compressed size */ return BLOSC2_ERROR_READ_BUFFER; } srcsize -= sizeof(int32_t); cbytes = sw32_(src); /* amount of compressed bytes */ if (cbytes > 0) { if (srcsize < cbytes) { /* Not enough input to read compressed bytes */ return BLOSC2_ERROR_READ_BUFFER; } srcsize -= cbytes; } src += sizeof(int32_t); } // ctbytes += (signed)sizeof(int32_t); /* Uncompress */ if (!vlblocks && cbytes == 0) { // A run of 0's memset(_dest, 0, (unsigned int)neblock); nbytes = neblock; } else if (!vlblocks && cbytes < 0) { // A negative number means some encoding depending on the token that comes next uint8_t token; if (srcsize < (signed)sizeof(uint8_t)) { // Not enough input to read token */ return BLOSC2_ERROR_READ_BUFFER; } srcsize -= sizeof(uint8_t); token = src[0]; src += 1; // ctbytes += 1; if (token & 0x1) { // A run of bytes that are different than 0 if (cbytes < -255) { // Runs can only encode a byte return BLOSC2_ERROR_RUN_LENGTH; } uint8_t value = -cbytes; memset(_dest, value, (unsigned int)neblock); } else { BLOSC_TRACE_ERROR("Invalid or unsupported compressed stream token value - %d", token); return BLOSC2_ERROR_RUN_LENGTH; } nbytes = neblock; cbytes = 0; // everything is encoded in the cbytes token } else if (cbytes == neblock) { memcpy(_dest, src, (unsigned int)neblock); nbytes = (int32_t)neblock; } else { if (compformat == BLOSC_BLOSCLZ_FORMAT) { nbytes = blosclz_decompress(src, cbytes, _dest, (int)neblock); } else if (compformat == BLOSC_LZ4_FORMAT) { nbytes = lz4_wrap_decompress((char*)src, (size_t)cbytes, (char*)_dest, (size_t)neblock, thread_context); } #if defined(HAVE_ZLIB) else if (compformat == BLOSC_ZLIB_FORMAT) { nbytes = zlib_wrap_decompress((char*)src, (size_t)cbytes, (char*)_dest, (size_t)neblock); } #endif /* HAVE_ZLIB */ #if defined(HAVE_ZSTD) else if (compformat == BLOSC_ZSTD_FORMAT) { nbytes = zstd_wrap_decompress(thread_context, (char*)src, (size_t)cbytes, (char*)_dest, (size_t)neblock); } #endif /* HAVE_ZSTD */ else if (compformat == BLOSC_UDCODEC_FORMAT) { bool getcell = false; #if defined(HAVE_ZFP) if ((context->compcode == BLOSC_CODEC_ZFP_FIXED_RATE) && (thread_context->zfp_cell_nitems > 0)) { nbytes = zfp_getcell(thread_context, src, cbytes, _dest, neblock); if (nbytes < 0) { return BLOSC2_ERROR_DATA; } if (nbytes == thread_context->zfp_cell_nitems * typesize) { getcell = true; } } #endif /* HAVE_ZFP */ if (!getcell) { thread_context->zfp_cell_nitems = 0; for (int i = 0; i < g_ncodecs; ++i) { if (g_codecs[i].compcode == context->compcode) { if (g_codecs[i].decoder == NULL) { // Dynamically load codec plugin if (fill_codec(&g_codecs[i]) < 0) { BLOSC_TRACE_ERROR("Could not load codec %d.", g_codecs[i].compcode); return BLOSC2_ERROR_CODEC_SUPPORT; } } blosc2_dparams dparams; blosc2_ctx_get_dparams(context, &dparams); nbytes = g_codecs[i].decoder(src, cbytes, _dest, neblock, context->compcode_meta, &dparams, context->src); goto urcodecsuccess; } } BLOSC_TRACE_ERROR("User-defined compressor codec %d not found during decompression", context->compcode); return BLOSC2_ERROR_CODEC_SUPPORT; } urcodecsuccess: ; } else { compname = clibcode_to_clibname(compformat); BLOSC_TRACE_ERROR( "Blosc has not been compiled with decompression " "support for '%s' format. " "Please recompile for adding this support.", compname); return BLOSC2_ERROR_CODEC_SUPPORT; } /* Check that decompressed bytes number is correct */ if ((nbytes != neblock) && (thread_context->zfp_cell_nitems == 0)) { return BLOSC2_ERROR_DATA; } } src += cbytes; // ctbytes += cbytes; _dest += nbytes; ntbytes += nbytes; } /* Closes j < nstreams */ if (!instr_codec) { if (last_filter_index >= 0 || context->postfilter != NULL) { /* Apply regular filter pipeline */ int errcode = pipeline_backward(thread_context, bsize, dest, dest_offset, tmp, tmp2, tmp3, last_filter_index, nblock); if (errcode < 0) return errcode; } } /* Return the number of uncompressed bytes */ return (int)ntbytes; } /* Serial version for compression/decompression */ static int serial_blosc(struct thread_context* thread_context) { blosc2_context* context = thread_context->parent_context; bool vlblocks = (context->blosc2_flags2 & BLOSC2_VL_BLOCKS) != 0; int32_t j, bsize, leftoverblock; int32_t cbytes; int32_t ntbytes = context->output_bytes; int32_t* bstarts = context->bstarts; uint8_t* tmp = thread_context->tmp; uint8_t* tmp2 = thread_context->tmp2; int dict_training = context->use_dict && (context->dict_cdict == NULL); bool memcpyed = context->header_flags & (uint8_t)BLOSC_MEMCPYED; if (!context->do_compress && context->special_type) { // Fake a runlen as if it was a memcpyed chunk memcpyed = true; } for (j = 0; j < context->nblocks; j++) { if (context->do_compress && !memcpyed && !dict_training) { _sw32(bstarts + j, ntbytes); } bsize = vlblocks ? context->blocknbytes[j] : context->blocksize; leftoverblock = 0; if (!vlblocks && (j == context->nblocks - 1) && (context->leftover > 0)) { bsize = context->leftover; leftoverblock = 1; } if (context->do_compress) { if (memcpyed && !context->prefilter) { /* We want to memcpy only */ memcpy(context->dest + context->header_overhead + j * context->blocksize, context->src + j * context->blocksize, (unsigned int)bsize); cbytes = (int32_t)bsize; } else { /* Regular compression */ cbytes = blosc_c(thread_context, bsize, leftoverblock, ntbytes, context->destsize, vlblocks ? context->vlblock_sources[j] : context->src, vlblocks ? 0 : j * context->blocksize, context->dest + ntbytes, tmp, tmp2); if (cbytes == 0) { ntbytes = 0; /* incompressible data */ break; } } } else { /* Regular decompression */ // If memcpyed we don't have a bstarts section (because it is not needed) int32_t src_offset = memcpyed ? context->header_overhead + j * context->blocksize : sw32_(bstarts + j); uint8_t *dest_block = (vlblocks && context->vlblock_dests != NULL) ? context->vlblock_dests[j] : context->dest; int32_t dest_offset = (vlblocks && context->vlblock_dests != NULL) ? 0 : (vlblocks ? context->blockoffsets[j] : j * context->blocksize); cbytes = blosc_d(thread_context, bsize, leftoverblock, memcpyed, context->src, context->srcsize, src_offset, j, dest_block, dest_offset, tmp, tmp2); } if (cbytes < 0) { ntbytes = cbytes; /* error in blosc_c or blosc_d */ break; } ntbytes += cbytes; } return ntbytes; } static void t_blosc_do_job(void *ctxt); /* initialize a thread_context that has already been allocated */ static int init_thread_context(struct thread_context* thread_context, blosc2_context* context, int32_t tid) { int32_t ebsize; thread_context->parent_context = context; thread_context->owner_pool = NULL; thread_context->tid = tid; int32_t blocksize = context != NULL ? context->blocksize : 0; int32_t typesize = context != NULL ? context->typesize : 0; ebsize = blocksize + typesize * (signed)sizeof(int32_t); thread_context->tmp_nbytes = (size_t)4 * ebsize; thread_context->tmp = my_malloc(thread_context->tmp_nbytes); BLOSC_ERROR_NULL(thread_context->tmp, BLOSC2_ERROR_MEMORY_ALLOC); thread_context->tmp2 = thread_context->tmp + ebsize; thread_context->tmp3 = thread_context->tmp2 + ebsize; thread_context->tmp4 = thread_context->tmp3 + ebsize; thread_context->tmp_blocksize = blocksize; thread_context->zfp_cell_nitems = 0; thread_context->zfp_cell_start = 0; #if defined(HAVE_ZSTD) thread_context->zstd_cctx = NULL; thread_context->zstd_dctx = NULL; #endif thread_context->lz4_cstream = NULL; thread_context->lz4hc_cstream = NULL; return 0; } static struct thread_context* create_thread_context(blosc2_context* context, int32_t tid) { struct thread_context* thread_context; thread_context = (struct thread_context*)my_malloc(sizeof(struct thread_context)); BLOSC_ERROR_NULL(thread_context, NULL); int rc = init_thread_context(thread_context, context, tid); if (rc < 0) { return NULL; } return thread_context; } /* free members of thread_context, but not thread_context itself */ static void destroy_thread_context(struct thread_context* thread_context) { my_free(thread_context->tmp); #if defined(HAVE_ZSTD) if (thread_context->zstd_cctx != NULL) { ZSTD_freeCCtx(thread_context->zstd_cctx); } if (thread_context->zstd_dctx != NULL) { ZSTD_freeDCtx(thread_context->zstd_dctx); } #endif if (thread_context->lz4_cstream != NULL) { LZ4_freeStream((LZ4_stream_t*)thread_context->lz4_cstream); } if (thread_context->lz4hc_cstream != NULL) { LZ4_freeStreamHC((LZ4_streamHC_t*)thread_context->lz4hc_cstream); } } void free_thread_context(struct thread_context* thread_context) { destroy_thread_context(thread_context); my_free(thread_context); } int check_nthreads(blosc2_context* context) { if (context->new_nthreads != context->nthreads && context->new_nthreads <= 0) { BLOSC_TRACE_ERROR("nthreads must be >= 1 and <= %d", INT16_MAX); return BLOSC2_ERROR_INVALID_PARAM; } if (context->nthreads <= 0) { BLOSC_TRACE_ERROR("nthreads must be >= 1 and <= %d", INT16_MAX); return BLOSC2_ERROR_INVALID_PARAM; } /* Detect a pool that was torn down by blosc2_destroy() while this context * was still alive. The epoch mismatch tells us the pool pointer is dangling; * clear it so the re-attach logic below creates a fresh one. */ if (context->thread_backend == BLOSC_BACKEND_SHARED_POOL && context->pool_epoch != g_destroy_count) { context->thread_pool = NULL; context->threads_started = 0; context->thread_backend = BLOSC_BACKEND_SERIAL; } if (context->new_nthreads != context->nthreads) { release_thread_backend(context); context->nthreads = context->new_nthreads; } if (context->nthreads > 1 && context->threads_started == 0) { int rc; if (threads_callback) { rc = init_callback_threads(context); } else { #if defined(_WIN32) rc = init_threadpool(context); #else rc = attach_shared_pool(context); #endif } if (rc < 0) { return rc; } } if (context->nthreads <= 1) { context->thread_backend = BLOSC_BACKEND_SERIAL; } return context->nthreads; } /* Do the compression or decompression of the buffer depending on the global params. */ static int do_job(blosc2_context* context) { int32_t ntbytes; /* Set sentinels */ context->dref_not_init = 1; /* Check whether we need to restart threads. If the thread backend could not be set up (e.g. shared-pool creation failed under thread/resource exhaustion), rc < 0 and context->thread_pool stays NULL; fall back to the serial path below instead of dereferencing a NULL pool in parallel_blosc. The failed pool creation already emitted a TRACE_ERROR, and the next do_job() re-attempts the attach, so the fallback is self-healing. */ int rc = check_nthreads(context); /* Run the serial version when nthreads is 1, when the buffers are not larger than blocksize, or when the parallel backend failed to start */ if (context->nthreads == 1 || (context->sourcesize / context->blocksize) <= 1 || rc < 0) { /* The context for this 'thread' has no been initialized yet */ if (context->serial_context == NULL) { context->serial_context = create_thread_context(context, 0); } else if (context->blocksize != context->serial_context->tmp_blocksize) { free_thread_context(context->serial_context); context->serial_context = create_thread_context(context, 0); } BLOSC_ERROR_NULL(context->serial_context, BLOSC2_ERROR_THREAD_CREATE); ntbytes = serial_blosc(context->serial_context); } else { ntbytes = parallel_blosc(context); } return ntbytes; } static int initialize_context_compression( blosc2_context* context, const void* src, int32_t srcsize, void* dest, int32_t destsize, int clevel, uint8_t const *filters, uint8_t const *filters_meta, int32_t typesize, int compressor, int32_t blocksize, int16_t new_nthreads, int16_t nthreads, int32_t splitmode, int tuner_id, void *tuner_params, blosc2_schunk* schunk) { /* Set parameters */ context->do_compress = 1; context->src = (const uint8_t*)src; context->srcsize = srcsize; context->dest = (uint8_t*)dest; context->output_bytes = 0; context->destsize = destsize; context->sourcesize = srcsize; context->typesize = typesize; context->filter_flags = filters_to_flags(filters); for (int i = 0; i < BLOSC2_MAX_FILTERS; i++) { context->filters[i] = filters[i]; context->filters_meta[i] = filters_meta[i]; } context->compcode = compressor; context->nthreads = nthreads; context->new_nthreads = new_nthreads; context->clevel = clevel; context->schunk = schunk; context->tuner_params = tuner_params; context->tuner_id = tuner_id; context->splitmode = splitmode; context->header_blocksize = (int32_t)blocksize; context->blosc2_flags2 = 0; if (context->blocknbytes != NULL) { free(context->blocknbytes); context->blocknbytes = NULL; } if (context->blockoffsets != NULL) { free(context->blockoffsets); context->blockoffsets = NULL; } if (context->blockcbytes != NULL) { free(context->blockcbytes); context->blockcbytes = NULL; } context->vlblock_sources = NULL; context->vlblock_dests = NULL; /* tuner some compression parameters */ context->blocksize = (int32_t)blocksize; int rc = 0; if (context->tuner_params != NULL) { if (context->tuner_id < BLOSC_LAST_TUNER && context->tuner_id == BLOSC_STUNE) { if (blosc_stune_next_cparams(context) < 0) { BLOSC_TRACE_ERROR("Error in stune next_cparams func\n"); return BLOSC2_ERROR_TUNER; } } else { for (int i = 0; i < g_ntuners; ++i) { if (g_tuners[i].id == context->tuner_id) { if (g_tuners[i].next_cparams == NULL) { if (fill_tuner(&g_tuners[i]) < 0) { BLOSC_TRACE_ERROR("Could not load tuner %d.", g_tuners[i].id); return BLOSC2_ERROR_FAILURE; } } if (g_tuners[i].next_cparams(context) < 0) { BLOSC_TRACE_ERROR("Error in tuner %d next_cparams func\n", context->tuner_id); return BLOSC2_ERROR_TUNER; } if (g_tuners[i].id == BLOSC_BTUNE && context->blocksize == 0) { // Call stune for initializing blocksize if (blosc_stune_next_blocksize(context) < 0) { BLOSC_TRACE_ERROR("Error in stune next_blocksize func\n"); return BLOSC2_ERROR_TUNER; } } goto urtunersuccess; } } BLOSC_TRACE_ERROR("User-defined tuner %d not found\n", context->tuner_id); return BLOSC2_ERROR_INVALID_PARAM; } } else { if (context->tuner_id < BLOSC_LAST_TUNER && context->tuner_id == BLOSC_STUNE) { rc = blosc_stune_next_blocksize(context); } else { for (int i = 0; i < g_ntuners; ++i) { if (g_tuners[i].id == context->tuner_id) { if (g_tuners[i].next_blocksize == NULL) { if (fill_tuner(&g_tuners[i]) < 0) { BLOSC_TRACE_ERROR("Could not load tuner %d.", g_tuners[i].id); return BLOSC2_ERROR_FAILURE; } } rc = g_tuners[i].next_blocksize(context); goto urtunersuccess; } } BLOSC_TRACE_ERROR("User-defined tuner %d not found\n", context->tuner_id); return BLOSC2_ERROR_INVALID_PARAM; } } urtunersuccess:; if (rc < 0) { BLOSC_TRACE_ERROR("Error in tuner next_blocksize func\n"); return BLOSC2_ERROR_TUNER; } /* Check buffer size limits */ if (srcsize > BLOSC2_MAX_BUFFERSIZE) { BLOSC_TRACE_ERROR("Input buffer size cannot exceed %d bytes.", BLOSC2_MAX_BUFFERSIZE); return BLOSC2_ERROR_MAX_BUFSIZE_EXCEEDED; } if (destsize < BLOSC2_MAX_OVERHEAD) { BLOSC_TRACE_ERROR("Output buffer size should be larger than %d bytes.", BLOSC2_MAX_OVERHEAD); return BLOSC2_ERROR_MAX_BUFSIZE_EXCEEDED; } /* Compression level */ if (clevel < 0 || clevel > 9) { /* If clevel not in 0..9, print an error */ BLOSC_TRACE_ERROR("`clevel` parameter must be between 0 and 9!."); return BLOSC2_ERROR_CODEC_PARAM; } /* Dictionary support is only available for ZSTD, LZ4, and LZ4HC. * Skip the check when src is NULL (special-value chunks): no compression * will actually happen, so codec compatibility is irrelevant. */ if (src != NULL && context->use_dict && context->compcode != BLOSC_ZSTD && context->compcode != BLOSC_LZ4 && context->compcode != BLOSC_LZ4HC) { BLOSC_TRACE_ERROR("`use_dict` is only supported for ZSTD, LZ4, and LZ4HC codecs."); return BLOSC2_ERROR_CODEC_PARAM; } /* Check typesize limits */ if (context->typesize > BLOSC2_MAXTYPESIZE) { // If typesize is too large for Blosc2, return an error BLOSC_TRACE_ERROR("Typesize cannot exceed %d bytes.", BLOSC2_MAXTYPESIZE); return BLOSC2_ERROR_INVALID_PARAM; } /* Now, cap typesize so that blosc2 split machinery can continue to work */ if (context->typesize > BLOSC_MAX_TYPESIZE) { /* If typesize is too large, treat buffer as an 1-byte stream. */ context->typesize = 1; } blosc2_calculate_blocks(context); return 1; } static void release_context_dict_buffer(blosc2_context* context) { if (context->dict_buffer_owned && context->dict_buffer != NULL) { free(context->dict_buffer); } context->dict_buffer = NULL; context->dict_buffer_owned = false; context->dict_size = 0; } static void clear_context_decompression_dict(blosc2_context* context) { context->use_dict = 0; release_context_dict_buffer(context); #if defined(HAVE_ZSTD) if (context->dict_ddict != NULL) { ZSTD_freeDDict(context->dict_ddict); context->dict_ddict = NULL; } #else context->dict_ddict = NULL; #endif } static int read_lazy_chunk_bytes(blosc2_context* context, int32_t offset, uint8_t* buffer, int32_t nbytes, const char* open_error, const char* read_error) { if (context->schunk == NULL || context->schunk->frame == NULL) { BLOSC_TRACE_ERROR("Lazy chunk needs an associated super-chunk with a frame."); return BLOSC2_ERROR_INVALID_PARAM; } blosc2_frame_s* frame = (blosc2_frame_s*)context->schunk->frame; blosc2_io_cb* io_cb = blosc2_get_io_cb(context->schunk->storage->io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); return BLOSC2_ERROR_PLUGIN_IO; } int32_t trailer_offset = BLOSC_EXTENDED_HEADER_LENGTH + context->nblocks * (int32_t)sizeof(int32_t); int32_t nchunk_lazy; int64_t chunk_offset; memcpy(&nchunk_lazy, context->src + trailer_offset, sizeof(nchunk_lazy)); memcpy(&chunk_offset, context->src + trailer_offset + (int32_t)sizeof(int32_t), sizeof(chunk_offset)); void* fp = NULL; int64_t io_pos; if (frame->sframe) { fp = sframe_open_chunk(frame->urlpath, nchunk_lazy, "rb", context->schunk->storage->io); io_pos = offset; } else { if (chunk_offset < 0 || offset < 0) { BLOSC_TRACE_ERROR("Lazy chunk offset cannot be negative."); return BLOSC2_ERROR_INVALID_HEADER; } fp = io_cb->open(frame->urlpath, "rb", context->schunk->storage->io->params); if (frame->file_offset > INT64_MAX - chunk_offset) { BLOSC_TRACE_ERROR("Lazy chunk offset overflows file position."); if (fp != NULL) { io_cb->close(fp); } return BLOSC2_ERROR_INVALID_HEADER; } io_pos = frame->file_offset + chunk_offset; if (io_pos > INT64_MAX - offset) { BLOSC_TRACE_ERROR("Lazy block offset overflows file position."); if (fp != NULL) { io_cb->close(fp); } return BLOSC2_ERROR_INVALID_HEADER; } io_pos += offset; } if (fp == NULL) { BLOSC_TRACE_ERROR("%s", open_error); return BLOSC2_ERROR_FILE_OPEN; } uint8_t* read_buffer = buffer; int64_t rbytes = io_cb->read((void**)&read_buffer, 1, nbytes, io_pos, fp); io_cb->close(fp); if (read_buffer != buffer) { memcpy(buffer, read_buffer, (size_t)nbytes); free(read_buffer); } if (rbytes != nbytes) { BLOSC_TRACE_ERROR("%s", read_error); return BLOSC2_ERROR_FILE_READ; } return 0; } static int load_lazy_chunk_dict(blosc2_context* context, blosc_header* header, int32_t bstarts_end) { int32_t dict_offset = bstarts_end; if (header->cbytes < dict_offset + (int32_t)sizeof(int32_t)) { BLOSC_TRACE_ERROR("Lazy chunk dictionary header exceeds chunk length."); return BLOSC2_ERROR_INVALID_HEADER; } uint8_t dict_size_buf[sizeof(int32_t)]; int rc = read_lazy_chunk_bytes(context, dict_offset, dict_size_buf, (int32_t)sizeof(dict_size_buf), "Cannot open frame file for lazy chunk dictionary read.", "Cannot read lazy chunk dictionary size from disk."); if (rc < 0) { return rc; } context->dict_size = sw32_(dict_size_buf); if (context->dict_size <= 0 || context->dict_size > BLOSC2_MAXDICTSIZE) { BLOSC_TRACE_ERROR("Dictionary size is smaller than minimum or larger than maximum allowed."); return BLOSC2_ERROR_CODEC_DICT; } if (header->cbytes < dict_offset + (int32_t)sizeof(int32_t) + context->dict_size) { BLOSC_TRACE_ERROR("Lazy chunk dictionary exceeds chunk length."); return BLOSC2_ERROR_INVALID_HEADER; } context->dict_buffer = malloc((size_t)context->dict_size); BLOSC_ERROR_NULL(context->dict_buffer, BLOSC2_ERROR_MEMORY_ALLOC); context->dict_buffer_owned = true; rc = read_lazy_chunk_bytes(context, dict_offset + (int32_t)sizeof(int32_t), context->dict_buffer, context->dict_size, "Cannot open frame file for lazy chunk dictionary read.", "Cannot read lazy chunk dictionary from disk."); if (rc < 0) { release_context_dict_buffer(context); return rc; } context->use_dict = 1; #if defined(HAVE_ZSTD) if (context->compcode == BLOSC_ZSTD_FORMAT) { context->dict_ddict = ZSTD_createDDict(context->dict_buffer, context->dict_size); if (context->dict_ddict == NULL) { release_context_dict_buffer(context); BLOSC_TRACE_ERROR("Cannot create ZSTD dictionary for lazy chunk."); return BLOSC2_ERROR_CODEC_DICT; } } #endif return 0; } static int initialize_context_decompression(blosc2_context* context, blosc_header* header, const void* src, int32_t srcsize, void* dest, int32_t destsize) { int32_t bstarts_end; bool vlblocks; context->do_compress = 0; context->src = (const uint8_t*)src; context->srcsize = srcsize; context->dest = (uint8_t*)dest; context->destsize = destsize; context->output_bytes = 0; context->vlblock_sources = NULL; context->vlblock_dests = NULL; if (context->blocknbytes != NULL) { free(context->blocknbytes); context->blocknbytes = NULL; } if (context->blockoffsets != NULL) { free(context->blockoffsets); context->blockoffsets = NULL; } if (context->blockcbytes != NULL) { free(context->blockcbytes); context->blockcbytes = NULL; } int rc = blosc2_initialize_context_from_header(context, header); if (rc < 0) { return rc; } clear_context_decompression_dict(context); vlblocks = (context->blosc2_flags2 & BLOSC2_VL_BLOCKS) != 0; bool is_lazy = ((context->header_overhead == BLOSC_EXTENDED_HEADER_LENGTH) && (context->blosc2_flags & 0x08u)); /* Check that we have enough space to decompress */ if (context->sourcesize > (int32_t)context->destsize) { return BLOSC2_ERROR_WRITE_BUFFER; } if (context->block_maskout != NULL && context->block_maskout_nitems != context->nblocks) { BLOSC_TRACE_ERROR("The number of items in block_maskout (%d) must match the number" " of blocks in chunk (%d).", context->block_maskout_nitems, context->nblocks); return BLOSC2_ERROR_DATA; } context->special_type = (header->blosc2_flags >> 4) & BLOSC2_SPECIAL_MASK; if (context->special_type > BLOSC2_SPECIAL_LASTID) { BLOSC_TRACE_ERROR("Unknown special values ID (%d) ", context->special_type); return BLOSC2_ERROR_DATA; } int memcpyed = (context->header_flags & (uint8_t) BLOSC_MEMCPYED); if (memcpyed && (header->cbytes != header->nbytes + context->header_overhead)) { BLOSC_TRACE_ERROR("Wrong header info for this memcpyed chunk"); return BLOSC2_ERROR_DATA; } if ((header->nbytes == 0) && (header->cbytes == context->header_overhead) && !context->special_type) { // A compressed buffer with only a header can only contain a zero-length buffer return 0; } context->bstarts = (int32_t *) (context->src + context->header_overhead); bstarts_end = context->header_overhead; if (!context->special_type && !memcpyed) { size_t bstarts_end_tmp; size_t bstarts_nbytes; if (context->nblocks < 0 || !checked_mul_size((size_t)context->nblocks, sizeof(int32_t), &bstarts_nbytes) || !checked_add_size((size_t)context->header_overhead, bstarts_nbytes, &bstarts_end_tmp) || bstarts_end_tmp > (size_t)INT32_MAX) { BLOSC_TRACE_ERROR("Invalid bstarts size in chunk header."); return BLOSC2_ERROR_INVALID_HEADER; } /* If chunk is not special or a memcpyed, we do have a bstarts section */ bstarts_end = (int32_t)bstarts_end_tmp; } if (srcsize < bstarts_end) { BLOSC_TRACE_ERROR("`bstarts` exceeds length of source buffer."); return BLOSC2_ERROR_READ_BUFFER; } if (vlblocks && is_lazy && !context->special_type && !memcpyed) { size_t block_csizes_nbytes; size_t trailer_meta_nbytes; size_t lazy_trailer_end; if (context->srcsize < 0 || context->nblocks < 0 || !checked_mul_size((size_t)context->nblocks, sizeof(int32_t), &block_csizes_nbytes) || !checked_add_size(sizeof(int32_t) + sizeof(int64_t), block_csizes_nbytes, &trailer_meta_nbytes) || !checked_add_size((size_t)bstarts_end, trailer_meta_nbytes, &lazy_trailer_end) || (size_t)context->srcsize < lazy_trailer_end) { BLOSC_TRACE_ERROR("Lazy trailer exceeds source buffer."); return BLOSC2_ERROR_READ_BUFFER; } } srcsize -= bstarts_end; /* Read optional dictionary if flag set */ if ((context->blosc2_flags & BLOSC2_USEDICT) && !is_lazy) { context->use_dict = 1; // The dictionary section is after the bstarts block: [int32 size | raw bytes] if (srcsize < (signed)sizeof(int32_t)) { BLOSC_TRACE_ERROR("Not enough space to read size of dictionary."); return BLOSC2_ERROR_READ_BUFFER; } srcsize -= sizeof(int32_t); // Read dictionary size context->dict_size = sw32_(context->src + bstarts_end); if (context->dict_size <= 0 || context->dict_size > BLOSC2_MAXDICTSIZE) { BLOSC_TRACE_ERROR("Dictionary size is smaller than minimum or larger than maximum allowed."); return BLOSC2_ERROR_CODEC_DICT; } if (srcsize < (int32_t)context->dict_size) { BLOSC_TRACE_ERROR("Not enough space to read entire dictionary."); return BLOSC2_ERROR_READ_BUFFER; } srcsize -= context->dict_size; // dict_buffer points directly into the source chunk — no copy needed context->dict_buffer = (void*)(context->src + bstarts_end + sizeof(int32_t)); context->dict_buffer_owned = false; #if defined(HAVE_ZSTD) // context->compcode during decompression holds the format code (flags >> 5), // so compare against BLOSC_ZSTD_FORMAT (not BLOSC_ZSTD). if (context->compcode == BLOSC_ZSTD_FORMAT) { context->dict_ddict = ZSTD_createDDict(context->dict_buffer, context->dict_size); if (context->dict_ddict == NULL) { BLOSC_TRACE_ERROR("Cannot create ZSTD dictionary for chunk."); return BLOSC2_ERROR_CODEC_DICT; } } #endif // HAVE_ZSTD // For LZ4/LZ4HC: dict_buffer and dict_size are sufficient; no digested object needed. } else if ((context->blosc2_flags & BLOSC2_USEDICT) && is_lazy) { rc = load_lazy_chunk_dict(context, header, bstarts_end); if (rc < 0) { return rc; } } if (vlblocks && !context->special_type && !memcpyed) { context->blocknbytes = malloc((size_t)context->nblocks * sizeof(int32_t)); BLOSC_ERROR_NULL(context->blocknbytes, BLOSC2_ERROR_MEMORY_ALLOC); context->blockoffsets = malloc((size_t)context->nblocks * sizeof(int32_t)); BLOSC_ERROR_NULL(context->blockoffsets, BLOSC2_ERROR_MEMORY_ALLOC); context->blockcbytes = malloc((size_t)context->nblocks * sizeof(int32_t)); BLOSC_ERROR_NULL(context->blockcbytes, BLOSC2_ERROR_MEMORY_ALLOC); if (is_lazy) { // Lazy VL: block data is on disk, so blocknbytes is unknown at this point. // Populate blockcbytes from bstarts differences; blocksize gets max(blockcbytes) // as a safe upper bound so tmp buffers are large enough for the lazy block read. int32_t max_csize = 0; for (int32_t i = 0; i < context->nblocks; ++i) { int32_t bstart = sw32_(context->bstarts + i); int32_t next_bstart = (i + 1 < context->nblocks) ? sw32_(context->bstarts + i + 1) : header->cbytes; if (bstart < bstarts_end || next_bstart <= bstart || next_bstart > header->cbytes) { BLOSC_TRACE_ERROR("Invalid VL-block offsets in lazy chunk."); return BLOSC2_ERROR_INVALID_HEADER; } context->blocknbytes[i] = 0; // unknown until block is read from disk context->blockoffsets[i] = 0; // unknown context->blockcbytes[i] = next_bstart - bstart; if (context->blockcbytes[i] > max_csize) { max_csize = context->blockcbytes[i]; } } context->blocksize = max_csize; context->leftover = 0; } else { int32_t max_blocksize = 0; int64_t total_nbytes = 0; int32_t prev_bstart = 0; for (int32_t i = 0; i < context->nblocks; ++i) { int32_t bstart = sw32_(context->bstarts + i); int32_t next_bstart = (i + 1 < context->nblocks) ? sw32_(context->bstarts + i + 1) : header->cbytes; if (bstart < bstarts_end || bstart <= prev_bstart || next_bstart <= bstart || next_bstart > header->cbytes || bstart > context->srcsize - (int32_t)sizeof(int32_t)) { BLOSC_TRACE_ERROR("Invalid VL-block offsets in chunk."); return BLOSC2_ERROR_INVALID_HEADER; } context->blocknbytes[i] = sw32_(context->src + bstart); context->blockcbytes[i] = next_bstart - bstart; if (context->blocknbytes[i] <= 0) { BLOSC_TRACE_ERROR("Invalid VL-block uncompressed size in chunk."); return BLOSC2_ERROR_INVALID_HEADER; } if (total_nbytes > INT32_MAX) { BLOSC_TRACE_ERROR("Invalid VL-block cumulative size in chunk."); return BLOSC2_ERROR_INVALID_HEADER; } context->blockoffsets[i] = (int32_t)total_nbytes; total_nbytes += context->blocknbytes[i]; if (total_nbytes > context->sourcesize) { BLOSC_TRACE_ERROR("VL-block sizes exceed chunk nbytes."); return BLOSC2_ERROR_INVALID_HEADER; } if (context->blocknbytes[i] > max_blocksize) { max_blocksize = context->blocknbytes[i]; } prev_bstart = bstart; } if (total_nbytes != context->sourcesize) { BLOSC_TRACE_ERROR("VL-block sizes do not add up to chunk nbytes."); return BLOSC2_ERROR_INVALID_HEADER; } context->blocksize = max_blocksize; context->leftover = 0; } } return 0; } static int write_compression_header(blosc2_context* context, bool extended_header) { blosc_header header; int dont_split; bool vlblocks = (context->blosc2_flags2 & BLOSC2_VL_BLOCKS) != 0; if (context->clevel == 0) { /* Compression level 0 means no compression — dicts serve no purpose here */ context->use_dict = 0; } int dict_training = context->use_dict && (context->dict_cdict == NULL); context->header_flags = 0; if (!vlblocks && context->clevel == 0) { /* Compression level 0 means buffer to be memcpy'ed */ context->header_flags |= (uint8_t)BLOSC_MEMCPYED; } if (!vlblocks && context->sourcesize < BLOSC_MIN_BUFFERSIZE) { /* Buffer is too small. Try memcpy'ing. */ context->header_flags |= (uint8_t)BLOSC_MEMCPYED; } bool memcpyed = context->header_flags & (uint8_t)BLOSC_MEMCPYED; if (extended_header) { /* Indicate that we are building an extended header */ context->header_overhead = BLOSC_EXTENDED_HEADER_LENGTH; context->header_flags |= (BLOSC_DOSHUFFLE | BLOSC_DOBITSHUFFLE); /* Store filter pipeline info at the end of the header */ if (dict_training || memcpyed) { context->bstarts = NULL; context->output_bytes = context->header_overhead; } else { context->bstarts = (int32_t*)(context->dest + context->header_overhead); context->output_bytes = context->header_overhead + (int32_t)sizeof(int32_t) * context->nblocks; } } else { // Regular header context->header_overhead = BLOSC_MIN_HEADER_LENGTH; if (memcpyed) { context->bstarts = NULL; context->output_bytes = context->header_overhead; } else { context->bstarts = (int32_t *) (context->dest + context->header_overhead); context->output_bytes = context->header_overhead + (int32_t)sizeof(int32_t) * context->nblocks; } } /* If the header + block starts don't fit in destsize, fall back to memcpy */ if (!memcpyed && context->output_bytes > context->destsize) { context->header_flags |= (uint8_t)BLOSC_MEMCPYED; memcpyed = true; context->bstarts = NULL; context->output_bytes = context->header_overhead; } // when memcpyed bit is set, there is no point in dealing with others if (!memcpyed) { if (context->filter_flags & BLOSC_DOSHUFFLE) { /* Byte-shuffle is active */ context->header_flags |= BLOSC_DOSHUFFLE; } if (context->filter_flags & BLOSC_DOBITSHUFFLE) { /* Bit-shuffle is active */ context->header_flags |= BLOSC_DOBITSHUFFLE; } if (context->filter_flags & BLOSC_DODELTA) { /* Delta is active */ context->header_flags |= BLOSC_DODELTA; } dont_split = vlblocks || !split_block(context, context->typesize, context->blocksize); /* dont_split is in bit 4 */ context->header_flags |= dont_split << 4; /* codec starts at bit 5 */ uint8_t compformat = compcode_to_compformat(context->compcode); context->header_flags |= compformat << 5; } // Create blosc header and store to dest blosc2_initialize_header_from_context(context, &header, extended_header); memcpy(context->dest, &header, (extended_header) ? BLOSC_EXTENDED_HEADER_LENGTH : BLOSC_MIN_HEADER_LENGTH); return 1; } static int blosc_compress_context(blosc2_context* context) { int ntbytes = 0; blosc_timestamp_t last, current; bool memcpyed = context->header_flags & (uint8_t)BLOSC_MEMCPYED; blosc_set_timestamp(&last); if (!memcpyed) { /* Do the actual compression */ ntbytes = do_job(context); if (ntbytes < 0) { return ntbytes; } if (ntbytes == 0) { // Try out with a memcpy later on (last chance for fitting src buffer in dest). context->header_flags |= (uint8_t)BLOSC_MEMCPYED; memcpyed = true; } } int dont_split = (context->header_flags & 0x10) >> 4; int nstreams = context->nblocks; if (!dont_split) { // When splitting, the number of streams is computed differently if (context->leftover) { nstreams = (context->nblocks - 1) * context->typesize + 1; } else { nstreams *= context->typesize; } } if (memcpyed) { if (context->sourcesize + context->header_overhead > context->destsize) { /* We are exceeding maximum output size */ ntbytes = 0; } else { context->output_bytes = context->header_overhead; ntbytes = do_job(context); if (ntbytes < 0) { return ntbytes; } // Success! update the memcpy bit in header context->dest[BLOSC2_CHUNK_FLAGS] = context->header_flags; // and clear the memcpy bit in context (for next reuse) context->header_flags &= ~(uint8_t)BLOSC_MEMCPYED; } } else { // Check whether we have a run for the whole chunk int dict_training = context->use_dict && (context->dict_cdict == NULL); int start_csizes = context->header_overhead + 4 * context->nblocks; if (!dict_training && ntbytes == (int)(start_csizes + nstreams * sizeof(int32_t))) { // The streams are all zero runs (by construction). Encode it... context->dest[BLOSC2_CHUNK_BLOSC2_FLAGS] |= BLOSC2_SPECIAL_ZERO << 4; // ...and assign the new chunk length ntbytes = context->header_overhead; } } /* Set the number of compressed bytes in header */ _sw32(context->dest + BLOSC2_CHUNK_CBYTES, ntbytes); if (context->blosc2_flags & BLOSC2_INSTR_CODEC) { dont_split = (context->header_flags & 0x10) >> 4; int32_t blocksize = dont_split ? (int32_t)sizeof(blosc2_instr) : (int32_t)sizeof(blosc2_instr) * context->typesize; _sw32(context->dest + BLOSC2_CHUNK_NBYTES, nstreams * (int32_t)sizeof(blosc2_instr)); _sw32(context->dest + BLOSC2_CHUNK_BLOCKSIZE, blocksize); } /* Set the number of bytes in dest buffer (might be useful for tuner) */ context->destsize = ntbytes; if (context->tuner_params != NULL) { blosc_set_timestamp(¤t); double ctime = blosc_elapsed_secs(last, current); int rc; if (context->tuner_id < BLOSC_LAST_TUNER && context->tuner_id == BLOSC_STUNE) { rc = blosc_stune_update(context, ctime); } else { for (int i = 0; i < g_ntuners; ++i) { if (g_tuners[i].id == context->tuner_id) { if (g_tuners[i].update == NULL) { if (fill_tuner(&g_tuners[i]) < 0) { BLOSC_TRACE_ERROR("Could not load tuner %d.", g_tuners[i].id); return BLOSC2_ERROR_FAILURE; } } rc = g_tuners[i].update(context, ctime); goto urtunersuccess; } } BLOSC_TRACE_ERROR("User-defined tuner %d not found\n", context->tuner_id); return BLOSC2_ERROR_INVALID_PARAM; urtunersuccess:; } if (rc < 0) { BLOSC_TRACE_ERROR("Error in tuner update func\n"); return BLOSC2_ERROR_TUNER; } } return ntbytes; } static int blosc_compress_context_without_dict(blosc2_context* context) { int saved_use_dict = context->use_dict; context->use_dict = 0; context->dest[BLOSC2_CHUNK_BLOSC2_FLAGS] &= ~(uint8_t)BLOSC2_USEDICT; int cbytes = blosc_compress_context(context); context->use_dict = saved_use_dict; return cbytes; } /* The public secure routine for compression with context. */ int blosc2_compress_ctx(blosc2_context* context, const void* src, int32_t srcsize, void* dest, int32_t destsize) { int error, cbytes; if (context->do_compress != 1) { BLOSC_TRACE_ERROR("Context is not meant for compression. Giving up."); return BLOSC2_ERROR_INVALID_PARAM; } error = initialize_context_compression( context, src, srcsize, dest, destsize, context->clevel, context->filters, context->filters_meta, context->typesize, context->compcode, context->blocksize, context->new_nthreads, context->nthreads, context->splitmode, context->tuner_id, context->tuner_params, context->schunk); if (error <= 0) { return error; } /* Write the extended header */ error = write_compression_header(context, true); if (error < 0) { return error; } cbytes = blosc_compress_context(context); if (cbytes < 0) { return cbytes; } if (context->use_dict && context->dict_cdict == NULL) { /* blosc_compress_context() overwrites context->destsize with the training-pass output * size. Restore it so that the real compression pass has the correct output-buffer size. */ context->destsize = destsize; bool is_lz4 = (context->compcode == BLOSC_LZ4 || context->compcode == BLOSC_LZ4HC); if (!is_lz4 && context->compcode != BLOSC_ZSTD) { const char* compname; compname = clibcode_to_clibname(context->compcode); BLOSC_TRACE_ERROR("Codec %s does not support dicts. Giving up.", compname); return BLOSC2_ERROR_CODEC_DICT; } // Build the dictionary out of the filters outcome and compress with it. // For LZ4/LZ4HC the raw samples are used directly (no training algorithm). // For ZSTD, ZDICT_trainFromBuffer() is used. int32_t dict_maxsize = BLOSC2_MAXDICTSIZE; // Do not make the dict more than 5% larger than uncompressed buffer if (dict_maxsize > srcsize / 20) { dict_maxsize = srcsize / 20; } void* samples_buffer = context->dest + context->header_overhead; unsigned nblocks = (unsigned)context->nblocks; int dont_split = (context->header_flags & 0x10) >> 4; if (!dont_split) { nblocks = nblocks * context->typesize; } if (nblocks < 8) { nblocks = 8; } unsigned sample_fraction = 16; size_t sample_size = context->sourcesize / nblocks / sample_fraction; // When the data is too small to produce useful dict samples, // fall back to plain compression without a dict. if (dict_maxsize < BLOSC2_MINUSEFULDICT || sample_size == 0) { BLOSC_TRACE_WARNING("Data too small for dict training (dict_maxsize=%d, sample_size=%zu)." " Falling back to plain compression.", dict_maxsize, sample_size); context->bstarts = (int32_t*)(context->dest + context->header_overhead); context->output_bytes = context->header_overhead + (int32_t)sizeof(int32_t) * context->nblocks; cbytes = blosc_compress_context_without_dict(context); } else if (is_lz4) { // LZ4/LZ4HC: use raw sample data directly as the dictionary (no training step). int32_t dict_actual_size = (int32_t)(nblocks * sample_size); if (dict_actual_size > dict_maxsize) { dict_actual_size = dict_maxsize; } // Reset bstarts and embed dict in the output buffer. context->bstarts = (int32_t*)(context->dest + context->header_overhead); context->output_bytes = context->header_overhead + (int32_t)sizeof(int32_t) * context->nblocks; /* Write dict size */ _sw32(context->dest + context->output_bytes, dict_actual_size); context->output_bytes += (int32_t)sizeof(int32_t); /* Copy dict bytes */ context->dict_buffer = context->dest + context->output_bytes; memcpy(context->dict_buffer, samples_buffer, (size_t)dict_actual_size); /* Build the stream used as cdict (pre-loaded with the dict bytes) */ if (context->compcode == BLOSC_LZ4HC) { LZ4_streamHC_t* lz4hc_cdict = LZ4_createStreamHC(); LZ4_loadDictHC(lz4hc_cdict, (const char*)context->dict_buffer, dict_actual_size); context->dict_cdict = lz4hc_cdict; } else { LZ4_stream_t* lz4_cdict = LZ4_createStream(); LZ4_loadDict(lz4_cdict, (const char*)context->dict_buffer, dict_actual_size); context->dict_cdict = lz4_cdict; } context->output_bytes += dict_actual_size; context->dict_size = dict_actual_size; /* Compress with dict */ cbytes = blosc_compress_context(context); // Invalidate the dictionary so the context can be reused for the next chunk context->dict_buffer = NULL; if (context->compcode == BLOSC_LZ4HC) { LZ4_freeStreamHC((LZ4_streamHC_t*)context->dict_cdict); } else { LZ4_freeStream((LZ4_stream_t*)context->dict_cdict); } context->dict_cdict = NULL; } #ifdef HAVE_ZSTD else { // Populate the samples sizes for training the dictionary size_t* samples_sizes = malloc(nblocks * sizeof(size_t)); BLOSC_ERROR_NULL(samples_sizes, BLOSC2_ERROR_MEMORY_ALLOC); for (size_t i = 0; i < nblocks; i++) { samples_sizes[i] = sample_size; } // Train from samples void* dict_buffer = malloc(dict_maxsize); if (dict_buffer == NULL) { free(samples_sizes); BLOSC_ERROR_NULL(dict_buffer, BLOSC2_ERROR_MEMORY_ALLOC); } int32_t dict_actual_size = (int32_t)ZDICT_trainFromBuffer( dict_buffer, dict_maxsize, samples_buffer, samples_sizes, nblocks); // TODO: experiment with parameters of low-level fast cover algorithm // Note that this API is still unstable. See: https://github.com/facebook/zstd/issues/1599 // ZDICT_fastCover_params_t fast_cover_params; // memset(&fast_cover_params, 0, sizeof(fast_cover_params)); // fast_cover_params.d = nblocks; // fast_cover_params.steps = 4; // fast_cover_params.zParams.compressionLevel = context->clevel; // size_t dict_actual_size = ZDICT_optimizeTrainFromBuffer_fastCover( // dict_buffer, dict_maxsize, samples_buffer, samples_sizes, nblocks, // &fast_cover_params); free(samples_sizes); if (ZDICT_isError(dict_actual_size) != ZSTD_error_no_error) { BLOSC_TRACE_WARNING("ZDICT_trainFromBuffer() failed: '%s'." " Falling back to plain compression.", ZDICT_getErrorName(dict_actual_size)); free(dict_buffer); context->bstarts = (int32_t*)(context->dest + context->header_overhead); context->output_bytes = context->header_overhead + (int32_t)sizeof(int32_t) * context->nblocks; cbytes = blosc_compress_context_without_dict(context); } else { assert(dict_actual_size > 0); // Update bytes counter and pointers to bstarts for the new compressed buffer context->bstarts = (int32_t*)(context->dest + context->header_overhead); context->output_bytes = context->header_overhead + (int32_t)sizeof(int32_t) * context->nblocks; /* Write the size of trained dict at the end of bstarts */ _sw32(context->dest + context->output_bytes, (int32_t)dict_actual_size); context->output_bytes += sizeof(int32_t); /* Write the trained dict afterwards */ context->dict_buffer = context->dest + context->output_bytes; memcpy(context->dict_buffer, dict_buffer, (unsigned int)dict_actual_size); context->dict_cdict = ZSTD_createCDict(dict_buffer, dict_actual_size, 1); // TODO: use get_accel() free(dict_buffer); // the dictionary is copied in the header now context->output_bytes += (int32_t)dict_actual_size; context->dict_size = dict_actual_size; /* Compress with dict */ cbytes = blosc_compress_context(context); // Invalidate the dictionary for compressing other chunks using the same context context->dict_buffer = NULL; ZSTD_freeCDict(context->dict_cdict); context->dict_cdict = NULL; } // ZDICT_isError } // ZSTD else branch #endif // HAVE_ZSTD } return cbytes; } /* Helper for reorder_vl_blocks_output: sort (bstart, logical_index) pairs by bstart */ typedef struct { int32_t bstart; int32_t idx; } vl_bstart_entry_t; static int cmp_vl_bstart(const void *a, const void *b) { int32_t ba = ((const vl_bstart_entry_t *)a)->bstart; int32_t bb = ((const vl_bstart_entry_t *)b)->bstart; return (ba > bb) - (ba < bb); } /* Ensure that VL-block compressed data is stored in block-index order. * * The decompressor derives each block's compressed span as * bstarts[i+1] - bstarts[i], which requires bstarts to be monotonically * increasing. Multi-threaded compression writes blocks in finish order * (non-deterministic), so bstarts may be out of order. This function * rearranges the compressed block data in the output buffer and fixes * bstarts so that block 0 comes first, block 1 second, and so on. */ static int reorder_vl_blocks_output(blosc2_context *context) { int32_t nblocks = context->nblocks; if (nblocks <= 1) { return 0; } int32_t output_bytes = context->output_bytes; int32_t *bstarts = context->bstarts; uint8_t *dest = context->dest; /* Fast path: blocks are already in index order (serial or lucky MT) */ bool ordered = true; for (int32_t i = 1; i < nblocks; i++) { if (sw32_(bstarts + i) < sw32_(bstarts + i - 1)) { ordered = false; break; } } if (ordered) { return 0; } /* When a dict is embedded, block data starts after [bstarts | dict_size | dict_data]. * Use the minimum bstart to locate the actual start of the compressed block region; * this avoids touching (and corrupting) the dict bytes. */ int32_t data_start = sw32_(bstarts); for (int32_t i = 1; i < nblocks; i++) { int32_t bs = sw32_(bstarts + i); if (bs < data_start) { data_start = bs; } } int32_t data_size = output_bytes - data_start; vl_bstart_entry_t *entries = malloc((size_t)nblocks * sizeof(vl_bstart_entry_t)); int32_t *block_cbytes = malloc((size_t)nblocks * sizeof(int32_t)); uint8_t *temp = malloc((size_t)data_size); if (entries == NULL || block_cbytes == NULL || temp == NULL) { free(entries); free(block_cbytes); free(temp); return BLOSC2_ERROR_MEMORY_ALLOC; } for (int32_t i = 0; i < nblocks; i++) { entries[i].bstart = sw32_(bstarts + i); entries[i].idx = i; } /* Sort entries by physical position to compute each block's compressed size */ qsort(entries, (size_t)nblocks, sizeof(vl_bstart_entry_t), cmp_vl_bstart); for (int32_t j = 0; j < nblocks; j++) { int32_t next = (j + 1 < nblocks) ? entries[j + 1].bstart : output_bytes; block_cbytes[entries[j].idx] = next - entries[j].bstart; } /* Snapshot compressed block data (not the dict) so we can safely overwrite dest */ memcpy(temp, dest + data_start, (size_t)data_size); /* Write blocks to dest in logical index order and update bstarts */ int32_t cur_pos = data_start; for (int32_t i = 0; i < nblocks; i++) { int32_t old_pos = sw32_(bstarts + i); _sw32(bstarts + i, cur_pos); memcpy(dest + cur_pos, temp + (old_pos - data_start), (size_t)block_cbytes[i]); cur_pos += block_cbytes[i]; } free(entries); free(block_cbytes); free(temp); return 0; } int blosc2_vlchunk_get_nblocks(const void* src, int32_t srcsize, int32_t* nblocks) { if (src == NULL || nblocks == NULL) { BLOSC_TRACE_ERROR("src and nblocks must not be NULL."); return BLOSC2_ERROR_INVALID_PARAM; } blosc_header header; int result = read_chunk_header((const uint8_t*)src, srcsize, true, &header); if (result < 0) { return result; } if ((header.blosc2_flags2 & BLOSC2_VL_BLOCKS) == 0) { BLOSC_TRACE_ERROR("Chunk does not use VL blocks."); return BLOSC2_ERROR_INVALID_PARAM; } /* For VL-block chunks the blocksize field in the header stores nblocks. */ *nblocks = header.blocksize; return 0; } int blosc2_vlcompress_ctx(blosc2_context* context, const void* const* srcs, const int32_t* srcsizes, int32_t nblocks, void* dest, int32_t destsize) { int error, cbytes; int32_t max_blocksize = 0; int64_t srcsize = 0; if (context->do_compress != 1) { BLOSC_TRACE_ERROR("Context is not meant for compression. Giving up."); return BLOSC2_ERROR_INVALID_PARAM; } if (srcs == NULL || srcsizes == NULL || nblocks <= 0) { BLOSC_TRACE_ERROR("Invalid sources for VL-block compression."); return BLOSC2_ERROR_INVALID_PARAM; } for (int32_t i = 0; i < nblocks; ++i) { if (srcs[i] == NULL || srcsizes[i] <= 0) { BLOSC_TRACE_ERROR("Invalid VL block at index %d.", i); return BLOSC2_ERROR_INVALID_PARAM; } srcsize += srcsizes[i]; if (srcsize > BLOSC2_MAX_BUFFERSIZE) { BLOSC_TRACE_ERROR("Input buffer size cannot exceed %d bytes.", BLOSC2_MAX_BUFFERSIZE); return BLOSC2_ERROR_MAX_BUFSIZE_EXCEEDED; } if (srcsizes[i] > max_blocksize) { max_blocksize = srcsizes[i]; } } error = initialize_context_compression( context, NULL, (int32_t)srcsize, dest, destsize, context->clevel, context->filters, context->filters_meta, context->typesize, context->compcode, max_blocksize, context->new_nthreads, context->nthreads, context->splitmode, context->tuner_id, context->tuner_params, context->schunk); if (error <= 0) { return error; } context->blosc2_flags2 = BLOSC2_VL_BLOCKS; context->header_blocksize = nblocks; context->nblocks = nblocks; context->leftover = 0; context->blocksize = max_blocksize; context->vlblock_sources = (const uint8_t**)srcs; context->blocknbytes = malloc((size_t)nblocks * sizeof(int32_t)); BLOSC_ERROR_NULL(context->blocknbytes, BLOSC2_ERROR_MEMORY_ALLOC); memcpy(context->blocknbytes, srcsizes, (size_t)nblocks * sizeof(int32_t)); error = write_compression_header(context, true); if (error < 0) { return error; } cbytes = blosc_compress_context(context); if (cbytes < 0) { context->vlblock_sources = NULL; return cbytes; } /* blosc_compress_context() overwrites context->destsize with the training-pass output * size (which is tiny — just the raw sample data). Restore it so that the real * compression pass below has the correct output-buffer size. */ context->destsize = destsize; #ifdef HAVE_ZSTD if (context->use_dict && context->dict_cdict == NULL) { bool is_lz4 = (context->compcode == BLOSC_LZ4 || context->compcode == BLOSC_LZ4HC); // The first blosc_compress_context() above was a dict-training pass that stored // raw (uncompressed) VL block data at dest+header_overhead as samples. Now build // the dictionary from those samples and do the real compression pass. int32_t dict_maxsize = BLOSC2_MAXDICTSIZE; // Do not make the dict more than 5% of the uncompressed size if (dict_maxsize > (int32_t)srcsize / 20) { dict_maxsize = (int32_t)srcsize / 20; } // Mirror the sample_size guard from blosc2_compress_ctx: if the average // per-block sample is too small for useful dict training, fall back. size_t vl_sample_size = (nblocks > 0) ? ((size_t)srcsize / (size_t)nblocks / 16) : 0; if (dict_maxsize < BLOSC2_MINUSEFULDICT || nblocks < 8 || vl_sample_size == 0) { // Data is too small or too few VL blocks to build a useful dictionary; // fall back to plain compression without a dict. context->bstarts = (int32_t*)(context->dest + context->header_overhead); context->output_bytes = context->header_overhead + (int32_t)sizeof(int32_t) * nblocks; context->vlblock_sources = (const uint8_t**)srcs; cbytes = blosc_compress_context_without_dict(context); context->vlblock_sources = NULL; } else if (is_lz4) { // LZ4/LZ4HC: use the concatenated raw VL block data directly as the dictionary. void* samples_buffer = context->dest + context->header_overhead; int32_t total_raw = 0; for (int32_t i = 0; i < nblocks; i++) { total_raw += context->blocknbytes[i]; } int32_t dict_actual_size = total_raw < dict_maxsize ? total_raw : dict_maxsize; context->bstarts = (int32_t*)(context->dest + context->header_overhead); context->output_bytes = context->header_overhead + (int32_t)sizeof(int32_t) * nblocks; _sw32(context->dest + context->output_bytes, dict_actual_size); context->output_bytes += (int32_t)sizeof(int32_t); context->dict_buffer = context->dest + context->output_bytes; memcpy(context->dict_buffer, samples_buffer, (size_t)dict_actual_size); if (context->compcode == BLOSC_LZ4HC) { LZ4_streamHC_t* lz4hc_cdict = LZ4_createStreamHC(); LZ4_loadDictHC(lz4hc_cdict, (const char*)context->dict_buffer, dict_actual_size); context->dict_cdict = lz4hc_cdict; } else { LZ4_stream_t* lz4_cdict = LZ4_createStream(); LZ4_loadDict(lz4_cdict, (const char*)context->dict_buffer, dict_actual_size); context->dict_cdict = lz4_cdict; } context->output_bytes += dict_actual_size; context->dict_size = dict_actual_size; context->vlblock_sources = (const uint8_t**)srcs; cbytes = blosc_compress_context(context); context->vlblock_sources = NULL; context->dict_buffer = NULL; if (context->compcode == BLOSC_LZ4HC) { LZ4_freeStreamHC((LZ4_streamHC_t*)context->dict_cdict); } else { LZ4_freeStream((LZ4_stream_t*)context->dict_cdict); } context->dict_cdict = NULL; } else { // ZSTD: use ZDICT_trainFromBuffer for dictionary training. // The training pass left all VL blocks concatenated at dest+header_overhead. // Use the actual per-block sizes as ZDICT sample sizes so that each VL block // is treated as a complete, independent training example. void* samples_buffer = context->dest + context->header_overhead; size_t* samples_sizes = malloc((size_t)nblocks * sizeof(size_t)); if (samples_sizes == NULL) { context->vlblock_sources = NULL; return BLOSC2_ERROR_MEMORY_ALLOC; } for (int32_t i = 0; i < nblocks; i++) { samples_sizes[i] = (size_t)context->blocknbytes[i]; } void* dict_buffer = malloc((size_t)dict_maxsize); if (dict_buffer == NULL) { free(samples_sizes); context->vlblock_sources = NULL; return BLOSC2_ERROR_MEMORY_ALLOC; } int32_t dict_actual_size = (int32_t)ZDICT_trainFromBuffer( dict_buffer, (size_t)dict_maxsize, samples_buffer, samples_sizes, (unsigned)nblocks); free(samples_sizes); if (ZDICT_isError(dict_actual_size) != ZSTD_error_no_error) { // Training failed (e.g. data is too small for a useful ZSTD dict). // Fall back to plain compression rather than returning an error. BLOSC_TRACE_WARNING("ZDICT_trainFromBuffer() failed ('%s'); falling back to plain compression.", ZDICT_getErrorName(dict_actual_size)); free(dict_buffer); context->bstarts = (int32_t*)(context->dest + context->header_overhead); context->output_bytes = context->header_overhead + (int32_t)sizeof(int32_t) * nblocks; context->vlblock_sources = (const uint8_t**)srcs; cbytes = blosc_compress_context_without_dict(context); context->vlblock_sources = NULL; } else { // Set up bstarts and embed the trained dictionary in the output buffer. // Layout after header: [bstarts | dict_size(int32) | dict_data | compressed blocks] context->bstarts = (int32_t*)(context->dest + context->header_overhead); context->output_bytes = context->header_overhead + (int32_t)sizeof(int32_t) * nblocks; _sw32(context->dest + context->output_bytes, dict_actual_size); context->output_bytes += (int32_t)sizeof(int32_t); context->dict_buffer = context->dest + context->output_bytes; memcpy(context->dict_buffer, dict_buffer, (size_t)dict_actual_size); context->dict_cdict = ZSTD_createCDict(dict_buffer, (size_t)dict_actual_size, 1); free(dict_buffer); context->output_bytes += dict_actual_size; context->dict_size = dict_actual_size; /* Actual compression pass using the trained dictionary */ context->vlblock_sources = (const uint8_t**)srcs; cbytes = blosc_compress_context(context); context->vlblock_sources = NULL; /* Invalidate the dictionary so the context can be reused for the next chunk */ context->dict_buffer = NULL; ZSTD_freeCDict(context->dict_cdict); context->dict_cdict = NULL; } } if (cbytes < 0) { return cbytes; } } #else if (context->use_dict && context->dict_cdict == NULL) { bool is_lz4 = (context->compcode == BLOSC_LZ4 || context->compcode == BLOSC_LZ4HC); int32_t dict_maxsize = BLOSC2_MAXDICTSIZE; if (dict_maxsize > (int32_t)srcsize / 20) { dict_maxsize = (int32_t)srcsize / 20; } size_t vl_sample_size_lz4 = (nblocks > 0) ? ((size_t)srcsize / (size_t)nblocks / 16) : 0; if (!is_lz4 || dict_maxsize < BLOSC2_MINUSEFULDICT || nblocks < 8 || vl_sample_size_lz4 == 0) { context->bstarts = (int32_t*)(context->dest + context->header_overhead); context->output_bytes = context->header_overhead + (int32_t)sizeof(int32_t) * nblocks; context->vlblock_sources = (const uint8_t**)srcs; cbytes = blosc_compress_context_without_dict(context); context->vlblock_sources = NULL; } else { void* samples_buffer = context->dest + context->header_overhead; int32_t total_raw = 0; for (int32_t i = 0; i < nblocks; i++) { total_raw += context->blocknbytes[i]; } int32_t dict_actual_size = total_raw < dict_maxsize ? total_raw : dict_maxsize; context->bstarts = (int32_t*)(context->dest + context->header_overhead); context->output_bytes = context->header_overhead + (int32_t)sizeof(int32_t) * nblocks; _sw32(context->dest + context->output_bytes, dict_actual_size); context->output_bytes += (int32_t)sizeof(int32_t); context->dict_buffer = context->dest + context->output_bytes; memcpy(context->dict_buffer, samples_buffer, (size_t)dict_actual_size); if (context->compcode == BLOSC_LZ4HC) { LZ4_streamHC_t* lz4hc_cdict = LZ4_createStreamHC(); LZ4_loadDictHC(lz4hc_cdict, (const char*)context->dict_buffer, dict_actual_size); context->dict_cdict = lz4hc_cdict; } else { LZ4_stream_t* lz4_cdict = LZ4_createStream(); LZ4_loadDict(lz4_cdict, (const char*)context->dict_buffer, dict_actual_size); context->dict_cdict = lz4_cdict; } context->output_bytes += dict_actual_size; context->dict_size = dict_actual_size; context->vlblock_sources = (const uint8_t**)srcs; cbytes = blosc_compress_context(context); context->vlblock_sources = NULL; context->dict_buffer = NULL; if (context->compcode == BLOSC_LZ4HC) { LZ4_freeStreamHC((LZ4_streamHC_t*)context->dict_cdict); } else { LZ4_freeStream((LZ4_stream_t*)context->dict_cdict); } context->dict_cdict = NULL; } if (cbytes < 0) { return cbytes; } } #endif // HAVE_ZSTD context->vlblock_sources = NULL; /* Multi-threaded compression may have written blocks in non-index order. * Rearrange the output so that bstarts is monotonically increasing, as * required by the decompressor. */ error = reorder_vl_blocks_output(context); if (error < 0) { return error; } return cbytes; } void build_filters(const int doshuffle, const int delta, const int32_t typesize, uint8_t* filters) { /* Fill the end part of the filter pipeline */ if ((doshuffle == BLOSC_SHUFFLE) && (typesize > 1)) filters[BLOSC2_MAX_FILTERS - 1] = BLOSC_SHUFFLE; if (doshuffle == BLOSC_BITSHUFFLE) filters[BLOSC2_MAX_FILTERS - 1] = BLOSC_BITSHUFFLE; if (doshuffle == BLOSC_NOSHUFFLE) filters[BLOSC2_MAX_FILTERS - 1] = BLOSC_NOSHUFFLE; if (delta) filters[BLOSC2_MAX_FILTERS - 2] = BLOSC_DELTA; } /* The public secure routine for compression. */ int blosc2_compress(int clevel, int doshuffle, int32_t typesize, const void* src, int32_t srcsize, void* dest, int32_t destsize) { int error; int result; char* envvar; /* Check whether the library should be initialized */ if (!g_initlib) blosc2_init(); /* Check for a BLOSC_CLEVEL environment variable */ envvar = getenv("BLOSC_CLEVEL"); if (envvar != NULL) { long value; errno = 0; /* To distinguish success/failure after call */ value = strtol(envvar, NULL, 10); if ((errno != EINVAL) && (value >= 0)) { clevel = (int)value; } else { BLOSC_TRACE_WARNING("BLOSC_CLEVEL environment variable '%s' not recognized\n", envvar); } } /* Check for a BLOSC_SHUFFLE environment variable */ envvar = getenv("BLOSC_SHUFFLE"); if (envvar != NULL) { if (strcmp(envvar, "NOSHUFFLE") == 0) { doshuffle = BLOSC_NOSHUFFLE; } else if (strcmp(envvar, "SHUFFLE") == 0) { doshuffle = BLOSC_SHUFFLE; } else if (strcmp(envvar, "BITSHUFFLE") == 0) { doshuffle = BLOSC_BITSHUFFLE; } else { BLOSC_TRACE_WARNING("BLOSC_SHUFFLE environment variable '%s' not recognized\n", envvar); } } /* Check for a BLOSC_DELTA environment variable */ envvar = getenv("BLOSC_DELTA"); if (envvar != NULL) { if (strcmp(envvar, "1") == 0) { blosc2_set_delta(1); } else if (strcmp(envvar, "0") == 0) { blosc2_set_delta(0); } else { BLOSC_TRACE_WARNING("BLOSC_DELTA environment variable '%s' not recognized\n", envvar); } } /* Check for a BLOSC_TYPESIZE environment variable */ envvar = getenv("BLOSC_TYPESIZE"); if (envvar != NULL) { long value; errno = 0; /* To distinguish success/failure after call */ value = strtol(envvar, NULL, 10); if ((errno != EINVAL) && (value > 0)) { typesize = (int32_t)value; } else { BLOSC_TRACE_WARNING("BLOSC_TYPESIZE environment variable '%s' not recognized\n", envvar); } } /* Check for a BLOSC_COMPRESSOR environment variable */ envvar = getenv("BLOSC_COMPRESSOR"); if (envvar != NULL) { result = blosc1_set_compressor(envvar); if (result < 0) { BLOSC_TRACE_WARNING("BLOSC_COMPRESSOR environment variable '%s' not recognized\n", envvar); } } /* Check for a BLOSC_BLOCKSIZE environment variable */ envvar = getenv("BLOSC_BLOCKSIZE"); if (envvar != NULL) { long blocksize; errno = 0; /* To distinguish success/failure after call */ blocksize = strtol(envvar, NULL, 10); if ((errno != EINVAL) && (blocksize > 0)) { blosc1_set_blocksize((size_t) blocksize); } else { BLOSC_TRACE_WARNING("BLOSC_BLOCKSIZE environment variable '%s' not recognized\n", envvar); } } /* Check for a BLOSC_NTHREADS environment variable */ envvar = getenv("BLOSC_NTHREADS"); if (envvar != NULL) { long nthreads; errno = 0; /* To distinguish success/failure after call */ nthreads = strtol(envvar, NULL, 10); if ((errno != EINVAL) && (nthreads > 0)) { result = blosc2_set_nthreads((int16_t) nthreads); if (result < 0) { BLOSC_TRACE_WARNING("BLOSC_NTHREADS environment variable '%s' not recognized\n", envvar); } } } /* Check for a BLOSC_SPLITMODE environment variable */ envvar = getenv("BLOSC_SPLITMODE"); if (envvar != NULL) { int32_t splitmode = -1; if (strcmp(envvar, "ALWAYS") == 0) { splitmode = BLOSC_ALWAYS_SPLIT; } else if (strcmp(envvar, "NEVER") == 0) { splitmode = BLOSC_NEVER_SPLIT; } else if (strcmp(envvar, "AUTO") == 0) { splitmode = BLOSC_AUTO_SPLIT; } else if (strcmp(envvar, "FORWARD_COMPAT") == 0) { splitmode = BLOSC_FORWARD_COMPAT_SPLIT; } else { BLOSC_TRACE_WARNING("BLOSC_SPLITMODE environment variable '%s' not recognized\n", envvar); } if (splitmode >= 0) { blosc1_set_splitmode(splitmode); } } /* Check for a BLOSC_NOLOCK environment variable. It is important that this should be the last env var so that it can take the previous ones into account */ envvar = getenv("BLOSC_NOLOCK"); if (envvar != NULL) { // TODO: here is the only place that returns an extended header from // a blosc1_compress() call. This should probably be fixed. const char *compname; blosc2_context *cctx; blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; blosc2_compcode_to_compname(g_compressor, &compname); /* Create a context for compression */ build_filters(doshuffle, g_delta, typesize, cparams.filters); // TODO: cparams can be shared in a multithreaded environment. do a copy! cparams.typesize = (uint8_t)typesize; cparams.compcode = (uint8_t)g_compressor; cparams.clevel = (uint8_t)clevel; cparams.nthreads = g_nthreads; cparams.splitmode = g_splitmode; cctx = blosc2_create_cctx(cparams); if (cctx == NULL) { BLOSC_TRACE_ERROR("Error while creating the compression context"); return BLOSC2_ERROR_NULL_POINTER; } /* Do the actual compression */ result = blosc2_compress_ctx(cctx, src, srcsize, dest, destsize); /* Release context resources */ blosc2_free_ctx(cctx); return result; } blosc2_pthread_mutex_lock(&global_comp_mutex); /* Initialize a context compression */ uint8_t* filters = calloc(1, BLOSC2_MAX_FILTERS); BLOSC_ERROR_NULL(filters, BLOSC2_ERROR_MEMORY_ALLOC); uint8_t* filters_meta = calloc(1, BLOSC2_MAX_FILTERS); BLOSC_ERROR_NULL(filters_meta, BLOSC2_ERROR_MEMORY_ALLOC); build_filters(doshuffle, g_delta, typesize, filters); error = initialize_context_compression( g_global_context, src, srcsize, dest, destsize, clevel, filters, filters_meta, (int32_t)typesize, g_compressor, g_force_blocksize, g_nthreads, g_nthreads, g_splitmode, g_tuner, NULL, g_schunk); free(filters); free(filters_meta); if (error <= 0) { blosc2_pthread_mutex_unlock(&global_comp_mutex); return error; } envvar = getenv("BLOSC_BLOSC1_COMPAT"); if (envvar != NULL) { /* Write chunk header without extended header (Blosc1 compatibility mode) */ error = write_compression_header(g_global_context, false); } else { error = write_compression_header(g_global_context, true); } if (error < 0) { blosc2_pthread_mutex_unlock(&global_comp_mutex); return error; } result = blosc_compress_context(g_global_context); blosc2_pthread_mutex_unlock(&global_comp_mutex); return result; } /* The public routine for compression. */ int blosc1_compress(int clevel, int doshuffle, size_t typesize, size_t nbytes, const void* src, void* dest, size_t destsize) { return blosc2_compress(clevel, doshuffle, (int32_t)typesize, src, (int32_t)nbytes, dest, (int32_t)destsize); } static int blosc_run_decompression_with_context(blosc2_context* context, const void* src, int32_t srcsize, void* dest, int32_t destsize) { blosc_header header; int32_t ntbytes; int rc; rc = read_chunk_header(src, srcsize, true, &header); if (rc < 0) { return rc; } if (header.nbytes > destsize) { // Not enough space for writing into the destination return BLOSC2_ERROR_WRITE_BUFFER; } rc = initialize_context_decompression(context, &header, src, srcsize, dest, destsize); if (rc < 0) { return rc; } /* Do the actual decompression */ ntbytes = do_job(context); if (ntbytes < 0) { return ntbytes; } assert(ntbytes <= (int32_t)destsize); return ntbytes; } /* The public secure routine for decompression with context. */ int blosc2_decompress_ctx(blosc2_context* context, const void* src, int32_t srcsize, void* dest, int32_t destsize) { int result; if (context->do_compress != 0) { BLOSC_TRACE_ERROR("Context is not meant for decompression. Giving up."); return BLOSC2_ERROR_INVALID_PARAM; } result = blosc_run_decompression_with_context(context, src, srcsize, dest, destsize); // Reset a possible block_maskout if (context->block_maskout != NULL) { free(context->block_maskout); context->block_maskout = NULL; } context->block_maskout_nitems = 0; return result; } int blosc2_vldecompress_ctx(blosc2_context* context, const void* src, int32_t srcsize, void** dests, int32_t* destsizes, int32_t maxblocks) { int result; blosc_header header; if (context->do_compress != 0) { BLOSC_TRACE_ERROR("Context is not meant for decompression. Giving up."); return BLOSC2_ERROR_INVALID_PARAM; } if (dests == NULL || destsizes == NULL || maxblocks <= 0) { BLOSC_TRACE_ERROR("Invalid destinations for VL-block decompression."); return BLOSC2_ERROR_INVALID_PARAM; } result = read_chunk_header(src, srcsize, true, &header); if (result < 0) { return result; } if ((header.blosc2_flags2 & BLOSC2_VL_BLOCKS) == 0) { BLOSC_TRACE_ERROR("Chunk does not use VL blocks."); return BLOSC2_ERROR_INVALID_PARAM; } result = initialize_context_decompression(context, &header, src, srcsize, NULL, header.nbytes); if (result < 0) { return result; } if (context->nblocks > maxblocks) { BLOSC_TRACE_ERROR("Not enough output entries for VL-block decompression."); return BLOSC2_ERROR_INVALID_PARAM; } for (int32_t i = 0; i < context->nblocks; ++i) { destsizes[i] = context->blocknbytes[i]; dests[i] = malloc((size_t)destsizes[i]); BLOSC_ERROR_NULL(dests[i], BLOSC2_ERROR_MEMORY_ALLOC); } context->vlblock_dests = (uint8_t**)dests; result = do_job(context); context->vlblock_dests = NULL; if (result < 0) { for (int32_t i = 0; i < context->nblocks; ++i) { free(dests[i]); dests[i] = NULL; } return result; } return context->nblocks; } /* Decompress a single VL block from an already-initialised decompression * context. The context must have been prepared by initialize_context_decompression() * on a VL-block chunk so that blocknbytes[], blockoffsets[], blockcbytes[], and * bstarts[] are all valid. * * On success, *dest points to a newly allocated buffer of *destsize bytes that * the caller must free(). Returns *destsize, or a negative error code. */ static int decompress_single_vlblock(blosc2_context* context, int32_t nblock, uint8_t** dest, int32_t* destsize) { if (nblock < 0 || nblock >= context->nblocks) { BLOSC_TRACE_ERROR("nblock (%d) out of range [0, %d).", nblock, context->nblocks); return BLOSC2_ERROR_INVALID_PARAM; } // For lazy VL chunks blocknbytes[nblock] == 0 because the uncompressed size is // stored as the first 4 bytes of the block span on disk, not in the in-memory // header. Peek at the file to resolve it before we can allocate the output buffer. if (context->blocknbytes[nblock] == 0) { if (context->schunk == NULL || context->schunk->frame == NULL) { BLOSC_TRACE_ERROR("Lazy VL block needs an associated super-chunk with a frame."); return BLOSC2_ERROR_INVALID_PARAM; } blosc2_frame_s* frame = (blosc2_frame_s*)context->schunk->frame; blosc2_io_cb *io_cb = blosc2_get_io_cb(context->schunk->storage->io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); return BLOSC2_ERROR_PLUGIN_IO; } // Lazy chunk trailer: [nchunk int32 | chunk_offset int64 | block_csizes int32*N] int64_t trailer_offset = (int64_t)BLOSC_EXTENDED_HEADER_LENGTH + (int64_t)context->nblocks * (int64_t)sizeof(int32_t); int64_t trailer_min_end = trailer_offset + (int64_t)sizeof(int32_t) + (int64_t)sizeof(int64_t); int64_t block_csize_pos = trailer_min_end + ((int64_t)nblock * (int64_t)sizeof(int32_t)); if (trailer_offset < 0 || block_csize_pos < 0 || (block_csize_pos + (int64_t)sizeof(int32_t)) > context->srcsize) { BLOSC_TRACE_ERROR("Malformed lazy trailer exceeds chunk bounds."); return BLOSC2_ERROR_INVALID_HEADER; } int32_t nchunk_lazy; int64_t chunk_offset; memcpy(&nchunk_lazy, context->src + trailer_offset, sizeof(nchunk_lazy)); memcpy(&chunk_offset, context->src + trailer_offset + (int32_t)sizeof(int32_t), sizeof(chunk_offset)); int32_t block_csize = *(const int32_t*)(context->src + block_csize_pos); if (block_csize < (int32_t)sizeof(int32_t)) { BLOSC_TRACE_ERROR("Lazy VL block compressed size is too small."); return BLOSC2_ERROR_INVALID_HEADER; } int32_t bstart = sw32_(context->bstarts + nblock); if (bstart < 0) { BLOSC_TRACE_ERROR("Lazy VL block offset cannot be negative."); return BLOSC2_ERROR_INVALID_HEADER; } void* fp = NULL; int64_t io_pos; if (frame->sframe) { fp = sframe_open_chunk(frame->urlpath, nchunk_lazy, "rb", context->schunk->storage->io); io_pos = bstart; } else { fp = io_cb->open(frame->urlpath, "rb", context->schunk->storage->io->params); if (fp == NULL) { BLOSC_TRACE_ERROR("Cannot open frame file for lazy VL block size peek."); return BLOSC2_ERROR_FILE_OPEN; } if (chunk_offset < 0) { BLOSC_TRACE_ERROR("Lazy chunk offset cannot be negative."); io_cb->close(fp); return BLOSC2_ERROR_INVALID_HEADER; } if (frame->file_offset > INT64_MAX - chunk_offset) { BLOSC_TRACE_ERROR("Lazy chunk offset overflows file position."); io_cb->close(fp); return BLOSC2_ERROR_INVALID_HEADER; } io_pos = frame->file_offset + chunk_offset; if (io_pos > INT64_MAX - bstart) { BLOSC_TRACE_ERROR("Lazy block offset overflows file position."); io_cb->close(fp); return BLOSC2_ERROR_INVALID_HEADER; } io_pos += bstart; } if (fp == NULL) { BLOSC_TRACE_ERROR("Cannot open frame file for lazy VL block size peek."); return BLOSC2_ERROR_FILE_OPEN; } // Read only the 4-byte uncompressed-size prefix of the block span. uint8_t nbuf[sizeof(int32_t)]; uint8_t* nbufp = nbuf; int64_t rbytes = io_cb->read((void**)&nbufp, 1, sizeof(int32_t), io_pos, fp); io_cb->close(fp); if (nbufp != nbuf) { // io_cb allocated new memory; copy the result and free. memcpy(nbuf, nbufp, sizeof(int32_t)); free(nbufp); } if (rbytes != (int64_t)sizeof(int32_t)) { BLOSC_TRACE_ERROR("Cannot read VL-block uncompressed-size prefix from disk."); return BLOSC2_ERROR_FILE_READ; } int32_t neblock = sw32_(nbuf); if (neblock <= 0) { BLOSC_TRACE_ERROR("Invalid VL-block uncompressed size read from disk."); return BLOSC2_ERROR_INVALID_HEADER; } context->blocknbytes[nblock] = neblock; // Keep blocksize as an upper bound for tmp buffer allocation. if (neblock > context->blocksize) { context->blocksize = neblock; } } int32_t bsize = context->blocknbytes[nblock]; uint8_t* buf = malloc((size_t)bsize); if (buf == NULL) { return BLOSC2_ERROR_MEMORY_ALLOC; } /* Ensure we have a serial thread context sized for this blocksize. */ if (context->serial_context == NULL) { context->serial_context = create_thread_context(context, 0); } else if (context->blocksize != context->serial_context->tmp_blocksize) { free_thread_context(context->serial_context); context->serial_context = create_thread_context(context, 0); } if (context->serial_context == NULL) { free(buf); return BLOSC2_ERROR_THREAD_CREATE; } bool memcpyed = (context->header_flags & (uint8_t)BLOSC_MEMCPYED) != 0; int32_t src_offset = sw32_(context->bstarts + nblock); int cbytes = blosc_d(context->serial_context, bsize, 0, memcpyed, context->src, context->srcsize, src_offset, nblock, buf, 0, context->serial_context->tmp, context->serial_context->tmp2); if (cbytes < 0) { free(buf); return cbytes; } *dest = buf; *destsize = bsize; return bsize; } int blosc2_vldecompress_block_ctx(blosc2_context* context, const void* src, int32_t srcsize, int32_t nblock, uint8_t** dest, int32_t* destsize) { if (context->do_compress != 0) { BLOSC_TRACE_ERROR("Context is not meant for decompression. Giving up."); return BLOSC2_ERROR_INVALID_PARAM; } if (dest == NULL || destsize == NULL) { BLOSC_TRACE_ERROR("dest and destsize must not be NULL."); return BLOSC2_ERROR_INVALID_PARAM; } blosc_header header; int result = read_chunk_header((const uint8_t*)src, srcsize, true, &header); if (result < 0) { return result; } if ((header.blosc2_flags2 & BLOSC2_VL_BLOCKS) == 0) { BLOSC_TRACE_ERROR("Chunk does not use VL blocks."); return BLOSC2_ERROR_INVALID_PARAM; } /* Pass header.nbytes as destsize so the size-check inside * initialize_context_decompression passes; context->dest is set to NULL and * is never written by blosc_d (which uses its own dest argument). */ result = initialize_context_decompression(context, &header, src, srcsize, NULL, header.nbytes); if (result < 0) { return result; } return decompress_single_vlblock(context, nblock, dest, destsize); } /* The public secure routine for decompression. */ int blosc2_decompress(const void* src, int32_t srcsize, void* dest, int32_t destsize) { int result; char* envvar; long nthreads; blosc2_context *dctx; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; /* Check whether the library should be initialized */ if (!g_initlib) blosc2_init(); /* Check for a BLOSC_NTHREADS environment variable */ envvar = getenv("BLOSC_NTHREADS"); if (envvar != NULL) { errno = 0; /* To distinguish success/failure after call */ nthreads = strtol(envvar, NULL, 10); if ((errno != EINVAL)) { if ((nthreads <= 0) || (nthreads > INT16_MAX)) { BLOSC_TRACE_ERROR("nthreads must be >= 1 and <= %d", INT16_MAX); return BLOSC2_ERROR_INVALID_PARAM; } result = blosc2_set_nthreads((int16_t) nthreads); if (result < 0) { return result; } } } /* Check for a BLOSC_NOLOCK environment variable. It is important that this should be the last env var so that it can take the previous ones into account */ envvar = getenv("BLOSC_NOLOCK"); if (envvar != NULL) { dparams.nthreads = g_nthreads; dctx = blosc2_create_dctx(dparams); if (dctx == NULL) { BLOSC_TRACE_ERROR("Error while creating the decompression context"); return BLOSC2_ERROR_NULL_POINTER; } result = blosc2_decompress_ctx(dctx, src, srcsize, dest, destsize); blosc2_free_ctx(dctx); return result; } blosc2_pthread_mutex_lock(&global_comp_mutex); result = blosc_run_decompression_with_context( g_global_context, src, srcsize, dest, destsize); blosc2_pthread_mutex_unlock(&global_comp_mutex); return result; } /* The public routine for decompression. */ int blosc1_decompress(const void* src, void* dest, size_t destsize) { return blosc2_decompress(src, INT32_MAX, dest, (int32_t)destsize); } /* Specific routine optimized for decompression a small number of items out of a compressed chunk. This does not use threads because it would affect negatively to performance. */ int _blosc_getitem(blosc2_context* context, blosc_header* header, const void* src, int32_t srcsize, int start, int nitems, void* dest, int32_t destsize) { uint8_t* _src = (uint8_t*)(src); /* current pos for source buffer */ uint8_t* _dest = (uint8_t*)(dest); int32_t ntbytes = 0; /* the number of uncompressed bytes */ int32_t bsize, bsize2, ebsize, leftoverblock; int32_t startb, stopb; int32_t stop; int32_t nitems_bytes; int64_t start64 = (int64_t)start; int64_t nitems64 = (int64_t)nitems; int64_t typesize64 = (int64_t)header->typesize; int64_t stop64; int64_t nitems_bytes64; int64_t start_bytes64; int64_t stop_bytes64; int j, rc; if (nitems == 0) { // We have nothing to do return 0; } if (nitems < 0) { BLOSC_TRACE_ERROR("`nitems` out of bounds."); return BLOSC2_ERROR_INVALID_PARAM; } nitems_bytes64 = nitems64 * typesize64; if (nitems_bytes64 < 0 || nitems_bytes64 > INT32_MAX || nitems_bytes64 > destsize) { BLOSC_TRACE_ERROR("`nitems`*`typesize` out of dest bounds."); return BLOSC2_ERROR_WRITE_BUFFER; } nitems_bytes = (int32_t)nitems_bytes64; int32_t* bstarts = (int32_t*)(_src + context->header_overhead); /* Check region boundaries */ start_bytes64 = start64 * typesize64; if ((start < 0) || (start_bytes64 < 0) || (start_bytes64 > header->nbytes)) { BLOSC_TRACE_ERROR("`start` out of bounds."); return BLOSC2_ERROR_INVALID_PARAM; } stop64 = start64 + nitems64; if ((stop64 < 0) || (stop64 > INT32_MAX)) { BLOSC_TRACE_ERROR("`start`+`nitems` out of bounds."); return BLOSC2_ERROR_INVALID_PARAM; } stop = (int32_t)stop64; stop_bytes64 = stop64 * typesize64; if ((stop_bytes64 < 0) || (stop_bytes64 > header->nbytes)) { BLOSC_TRACE_ERROR("`start`+`nitems` out of bounds."); return BLOSC2_ERROR_INVALID_PARAM; } bool chunk_memcpyed = (header->flags & (uint8_t)BLOSC_MEMCPYED) != 0; if (!context->special_type && !chunk_memcpyed && ((uint8_t *)(_src + srcsize) < (uint8_t *)(bstarts + context->nblocks))) { BLOSC_TRACE_ERROR("`bstarts` out of bounds."); return BLOSC2_ERROR_READ_BUFFER; } bool memcpyed = header->flags & (uint8_t)BLOSC_MEMCPYED; if (context->special_type) { // Fake a runlen as if its a memcpyed chunk memcpyed = true; } bool is_lazy = ((context->header_overhead == BLOSC_EXTENDED_HEADER_LENGTH) && (context->blosc2_flags & 0x08u) && !context->special_type); if (memcpyed && !is_lazy && !context->postfilter) { // Short-circuit for (non-lazy) memcpyed or special values ntbytes = nitems_bytes; switch (context->special_type) { case BLOSC2_SPECIAL_VALUE: // All repeated values rc = set_values(context->typesize, _src, _dest, ntbytes); if (rc < 0) { BLOSC_TRACE_ERROR("set_values failed"); return BLOSC2_ERROR_DATA; } break; case BLOSC2_SPECIAL_NAN: rc = set_nans(context->typesize, _dest, ntbytes); if (rc < 0) { BLOSC_TRACE_ERROR("set_nans failed"); return BLOSC2_ERROR_DATA; } break; case BLOSC2_SPECIAL_ZERO: memset(_dest, 0, ntbytes); break; case BLOSC2_SPECIAL_UNINIT: // We do nothing here break; case BLOSC2_NO_SPECIAL: { int64_t src_offset64 = (int64_t)context->header_overhead + start_bytes64; int64_t src_stop64 = src_offset64 + nitems_bytes64; if ((src_offset64 < 0) || (src_stop64 < src_offset64) || (src_stop64 > srcsize)) { BLOSC_TRACE_ERROR("getitem memcpy source out of bounds."); return BLOSC2_ERROR_READ_BUFFER; } _src += (int32_t)src_offset64; } memcpy(_dest, _src, ntbytes); break; default: BLOSC_TRACE_ERROR("Unhandled special value case"); BLOSC_ERROR(BLOSC2_ERROR_SCHUNK_SPECIAL); } return ntbytes; } ebsize = header->blocksize + header->typesize * (signed)sizeof(int32_t); struct thread_context* scontext = context->serial_context; /* Resize the temporaries in serial context if needed */ if (header->blocksize > scontext->tmp_blocksize) { my_free(scontext->tmp); scontext->tmp_nbytes = (size_t)4 * ebsize; scontext->tmp = my_malloc(scontext->tmp_nbytes); BLOSC_ERROR_NULL(scontext->tmp, BLOSC2_ERROR_MEMORY_ALLOC); scontext->tmp2 = scontext->tmp + ebsize; scontext->tmp3 = scontext->tmp2 + ebsize; scontext->tmp4 = scontext->tmp3 + ebsize; scontext->tmp_blocksize = (int32_t)header->blocksize; } for (j = 0; j < context->nblocks; j++) { bsize = header->blocksize; leftoverblock = 0; if ((j == context->nblocks - 1) && (context->leftover > 0)) { bsize = context->leftover; leftoverblock = 1; } /* Compute start & stop for each block */ startb = start * header->typesize - j * header->blocksize; stopb = stop * header->typesize - j * header->blocksize; if (stopb <= 0) { // We can exit as soon as this block is beyond stop break; } if (startb >= header->blocksize) { continue; } if (startb < 0) { startb = 0; } if (stopb > header->blocksize) { stopb = header->blocksize; } bsize2 = stopb - startb; #if defined(HAVE_ZFP) if (context->compcode == BLOSC_CODEC_ZFP_FIXED_RATE) { scontext->zfp_cell_start = startb / context->typesize; scontext->zfp_cell_nitems = nitems; } #endif /* HAVE_ZFP */ /* Do the actual data copy */ // Regular decompression. Put results in tmp2. // If the block is aligned and the worst case fits in destination, let's avoid a copy bool get_single_block = ((startb == 0) && (bsize == nitems_bytes)); uint8_t* tmp2 = get_single_block ? dest : scontext->tmp2; // If memcpyed we don't have a bstarts section (because it is not needed) int32_t src_offset = memcpyed ? context->header_overhead + j * header->blocksize : sw32_(bstarts + j); int32_t cbytes = blosc_d(context->serial_context, bsize, leftoverblock, memcpyed, src, srcsize, src_offset, j, tmp2, 0, scontext->tmp, scontext->tmp3); if (cbytes < 0) { ntbytes = cbytes; break; } if (scontext->zfp_cell_nitems > 0) { if (cbytes == bsize2) { memcpy((uint8_t *) dest, tmp2, (unsigned int) bsize2); } else if (cbytes == context->blocksize) { memcpy((uint8_t *) dest, tmp2 + scontext->zfp_cell_start * context->typesize, (unsigned int) bsize2); cbytes = bsize2; } } else if (!get_single_block) { /* Copy to destination */ memcpy((uint8_t *) dest + ntbytes, tmp2 + startb, (unsigned int) bsize2); } ntbytes += bsize2; } scontext->zfp_cell_nitems = 0; return ntbytes; } int blosc2_getitem(const void* src, int32_t srcsize, int start, int nitems, void* dest, int32_t destsize) { blosc2_context context; int result; /* Minimally populate the context */ memset(&context, 0, sizeof(blosc2_context)); context.schunk = g_schunk; context.nthreads = 1; // force a serial decompression; fixes #95 /* Call the actual getitem function */ result = blosc2_getitem_ctx(&context, src, srcsize, start, nitems, dest, destsize); /* Release resources */ if (context.serial_context != NULL) { free_thread_context(context.serial_context); } return result; } /* Specific routine optimized for decompression a small number of items out of a compressed chunk. Public non-contextual API. */ int blosc1_getitem(const void* src, int start, int nitems, void* dest) { return blosc2_getitem(src, INT32_MAX, start, nitems, dest, INT32_MAX); } int blosc2_getitem_ctx(blosc2_context* context, const void* src, int32_t srcsize, int start, int nitems, void* dest, int32_t destsize) { blosc_header header; int result; /* Minimally populate the context */ result = read_chunk_header((uint8_t *) src, srcsize, true, &header); if (result < 0) { return result; } if (header.blosc2_flags2 & BLOSC2_VL_BLOCKS) { BLOSC_TRACE_ERROR("getitem is not supported for VL-block chunks."); return BLOSC2_ERROR_INVALID_PARAM; } context->src = src; context->srcsize = srcsize; context->dest = dest; context->destsize = destsize; result = blosc2_initialize_context_from_header(context, &header); if (result < 0) { return result; } if (context->serial_context == NULL) { context->serial_context = create_thread_context(context, 0); } BLOSC_ERROR_NULL(context->serial_context, BLOSC2_ERROR_THREAD_CREATE); /* Call the actual getitem function */ result = _blosc_getitem(context, &header, src, srcsize, start, nitems, dest, destsize); return result; } int blosc2_decompress_block_ctx(blosc2_context* context, const void* src, int32_t srcsize, int32_t nblock, void* dest, int32_t destsize) { blosc_header header; int result = read_chunk_header((uint8_t *)src, srcsize, true, &header); if (result < 0) { return result; } if (header.blosc2_flags2 & BLOSC2_VL_BLOCKS) { BLOSC_TRACE_ERROR("block decompression is not supported for VL-block chunks."); return BLOSC2_ERROR_INVALID_PARAM; } context->do_compress = 0; context->src = src; context->srcsize = srcsize; context->dest = dest; context->destsize = destsize; context->output_bytes = 0; context->vlblock_sources = NULL; context->vlblock_dests = NULL; if (context->blocknbytes != NULL) { free(context->blocknbytes); context->blocknbytes = NULL; } if (context->blockoffsets != NULL) { free(context->blockoffsets); context->blockoffsets = NULL; } if (context->blockcbytes != NULL) { free(context->blockcbytes); context->blockcbytes = NULL; } result = blosc2_initialize_context_from_header(context, &header); if (result < 0) { return result; } clear_context_decompression_dict(context); context->special_type = (header.blosc2_flags >> 4) & BLOSC2_SPECIAL_MASK; if (context->special_type > BLOSC2_SPECIAL_LASTID) { BLOSC_TRACE_ERROR("Unknown special values ID (%d) ", context->special_type); return BLOSC2_ERROR_DATA; } if (nblock < 0 || nblock >= context->nblocks) { BLOSC_TRACE_ERROR("`nblock` out of bounds."); return BLOSC2_ERROR_INVALID_PARAM; } bool memcpyed = (header.flags & (uint8_t)BLOSC_MEMCPYED) != 0; if (context->special_type) { memcpyed = true; } if (!context->special_type && !memcpyed) { size_t bstarts_nbytes; size_t bstarts_end; if (context->nblocks < 0 || !checked_mul_size((size_t)context->nblocks, sizeof(int32_t), &bstarts_nbytes) || !checked_add_size((size_t)context->header_overhead, bstarts_nbytes, &bstarts_end) || bstarts_end > (size_t)srcsize) { BLOSC_TRACE_ERROR("`bstarts` out of bounds."); return BLOSC2_ERROR_READ_BUFFER; } } int32_t bsize = context->blocksize; int32_t leftoverblock = 0; if ((nblock == context->nblocks - 1) && (context->leftover > 0)) { bsize = context->leftover; leftoverblock = 1; } if (destsize < bsize) { BLOSC_TRACE_ERROR("Destination is too small for block."); return BLOSC2_ERROR_WRITE_BUFFER; } if (context->serial_context == NULL) { context->serial_context = create_thread_context(context, 0); } BLOSC_ERROR_NULL(context->serial_context, BLOSC2_ERROR_THREAD_CREATE); context->serial_context->parent_context = context; int32_t ebsize = context->blocksize + context->typesize * (int32_t)sizeof(int32_t); if (context->blocksize > context->serial_context->tmp_blocksize) { my_free(context->serial_context->tmp); context->serial_context->tmp_nbytes = (size_t)4 * ebsize; context->serial_context->tmp = my_malloc(context->serial_context->tmp_nbytes); BLOSC_ERROR_NULL(context->serial_context->tmp, BLOSC2_ERROR_MEMORY_ALLOC); context->serial_context->tmp2 = context->serial_context->tmp + ebsize; context->serial_context->tmp3 = context->serial_context->tmp2 + ebsize; context->serial_context->tmp4 = context->serial_context->tmp3 + ebsize; context->serial_context->tmp_blocksize = context->blocksize; } int32_t* bstarts = (int32_t*)((uint8_t*)src + context->header_overhead); int32_t src_offset = memcpyed ? context->header_overhead + nblock * context->blocksize : sw32_(bstarts + nblock); int nbytes = blosc_d(context->serial_context, bsize, leftoverblock, memcpyed, src, srcsize, src_offset, nblock, (uint8_t*)dest, 0, context->serial_context->tmp, context->serial_context->tmp3); if (nbytes < 0) { return nbytes; } return bsize; } /* execute single compression/decompression job for a single thread_context */ #if defined(_WIN32) /* Windows implementation: uses context fields directly (no job-group struct). */ static void t_blosc_do_job(void *ctxt) { struct thread_context* thcontext = (struct thread_context*)ctxt; blosc2_context* context = thcontext->parent_context; int32_t cbytes; int32_t ntdest; int32_t tblocks; int32_t tblock; int32_t nblock_; int32_t bsize; int32_t leftoverblock; int32_t blocksize; int32_t ebsize; int32_t srcsize; bool compress = context->do_compress != 0; int32_t maxbytes; int32_t nblocks; int32_t leftover; int32_t leftover2; int32_t* bstarts; const uint8_t* src; uint8_t* dest; uint8_t* tmp; uint8_t* tmp2; uint8_t* tmp3; blocksize = context->blocksize; ebsize = blocksize + context->typesize * (int32_t)sizeof(int32_t); maxbytes = context->destsize; nblocks = context->nblocks; leftover = context->leftover; bstarts = context->bstarts; src = context->src; srcsize = context->srcsize; dest = context->dest; /* Resize the temporaries if needed */ if (blocksize > thcontext->tmp_blocksize) { my_free(thcontext->tmp); thcontext->tmp_nbytes = (size_t)4 * ebsize; thcontext->tmp = my_malloc(thcontext->tmp_nbytes); thcontext->tmp2 = thcontext->tmp + ebsize; thcontext->tmp3 = thcontext->tmp2 + ebsize; thcontext->tmp4 = thcontext->tmp3 + ebsize; thcontext->tmp_blocksize = blocksize; } tmp = thcontext->tmp; tmp2 = thcontext->tmp2; tmp3 = thcontext->tmp3; bool vlblocks = (context->blosc2_flags2 & BLOSC2_VL_BLOCKS) != 0; bool memcpyed = context->header_flags & (uint8_t)BLOSC_MEMCPYED; if (!context->do_compress && context->special_type) { memcpyed = true; } bool static_schedule = (!compress || memcpyed) && context->block_maskout == NULL; if (static_schedule) { tblocks = nblocks / context->nthreads; leftover2 = nblocks % context->nthreads; tblocks = (leftover2 > 0) ? tblocks + 1 : tblocks; nblock_ = thcontext->tid * tblocks; tblock = nblock_ + tblocks; if (tblock > nblocks) { tblock = nblocks; } } else { blosc2_pthread_mutex_lock(&context->count_mutex); context->thread_nblock++; nblock_ = context->thread_nblock; blosc2_pthread_mutex_unlock(&context->count_mutex); tblock = nblocks; } leftoverblock = 0; while ((nblock_ < tblock) && (context->thread_giveup_code > 0)) { bsize = vlblocks ? context->blocknbytes[nblock_] : blocksize; leftoverblock = 0; if (!vlblocks && nblock_ == (nblocks - 1) && (leftover > 0)) { bsize = leftover; leftoverblock = 1; } if (compress) { if (memcpyed) { if (!context->prefilter) { memcpy(dest + context->header_overhead + nblock_ * blocksize, src + nblock_ * blocksize, (unsigned int)bsize); cbytes = (int32_t)bsize; } else { cbytes = blosc_c(thcontext, bsize, leftoverblock, 0, ebsize, vlblocks ? context->vlblock_sources[nblock_] : src, vlblocks ? 0 : nblock_ * blocksize, dest + context->header_overhead + nblock_ * blocksize, tmp, tmp3); } } else { cbytes = blosc_c(thcontext, bsize, leftoverblock, 0, ebsize, vlblocks ? context->vlblock_sources[nblock_] : src, vlblocks ? 0 : nblock_ * blocksize, tmp2, tmp, tmp3); } } else { if (context->special_type == BLOSC2_NO_SPECIAL && !memcpyed && (srcsize < (int32_t)(context->header_overhead + (sizeof(int32_t) * nblocks)))) { cbytes = -1; } else { int32_t src_offset = memcpyed ? context->header_overhead + nblock_ * blocksize : sw32_(bstarts + nblock_); uint8_t *dest_block = (vlblocks && context->vlblock_dests != NULL) ? context->vlblock_dests[nblock_] : dest; int32_t dest_offset = (vlblocks && context->vlblock_dests != NULL) ? 0 : (vlblocks ? context->blockoffsets[nblock_] : nblock_ * blocksize); cbytes = blosc_d(thcontext, bsize, leftoverblock, memcpyed, src, srcsize, src_offset, nblock_, dest_block, dest_offset, tmp, tmp2); } } if (context->thread_giveup_code <= 0) { break; } if (cbytes < 0) { blosc2_pthread_mutex_lock(&context->count_mutex); context->thread_giveup_code = cbytes; blosc2_pthread_mutex_unlock(&context->count_mutex); break; } if (compress && !memcpyed) { blosc2_pthread_mutex_lock(&context->count_mutex); ntdest = context->output_bytes; if (!(context->use_dict && context->dict_cdict == NULL)) { _sw32(bstarts + nblock_, (int32_t)ntdest); } if ((cbytes == 0) || (ntdest + cbytes > maxbytes)) { context->thread_giveup_code = 0; blosc2_pthread_mutex_unlock(&context->count_mutex); break; } context->thread_nblock++; nblock_ = context->thread_nblock; context->output_bytes += cbytes; blosc2_pthread_mutex_unlock(&context->count_mutex); memcpy(dest + ntdest, tmp2, (unsigned int)cbytes); } else if (static_schedule) { nblock_++; } else { blosc2_pthread_mutex_lock(&context->count_mutex); context->thread_nblock++; nblock_ = context->thread_nblock; context->output_bytes += cbytes; blosc2_pthread_mutex_unlock(&context->count_mutex); } } if (static_schedule) { blosc2_pthread_mutex_lock(&context->count_mutex); context->output_bytes = context->sourcesize; if (compress) { context->output_bytes += context->header_overhead; } blosc2_pthread_mutex_unlock(&context->count_mutex); } } #else /* !_WIN32 */ static int ensure_thread_context_capacity(struct thread_context* thread_context, blosc2_context* context) { int32_t blocksize = context->blocksize; int32_t ebsize = blocksize + context->typesize * (int32_t)sizeof(int32_t); if (blocksize <= thread_context->tmp_blocksize) { return 0; } my_free(thread_context->tmp); thread_context->tmp_nbytes = (size_t)4 * ebsize; thread_context->tmp = my_malloc(thread_context->tmp_nbytes); BLOSC_ERROR_NULL(thread_context->tmp, BLOSC2_ERROR_MEMORY_ALLOC); thread_context->tmp2 = thread_context->tmp + ebsize; thread_context->tmp3 = thread_context->tmp2 + ebsize; thread_context->tmp4 = thread_context->tmp3 + ebsize; thread_context->tmp_blocksize = blocksize; return 0; } static int32_t claim_job_block(struct blosc_job_group *job) { int32_t nblock_; blosc2_pthread_mutex_lock(&job->mutex); nblock_ = ++job->next_block; blosc2_pthread_mutex_unlock(&job->mutex); return nblock_; } /* Non-Windows implementation: uses the job-group struct for thread coordination. */ static void t_blosc_do_job(void *ctxt) { struct thread_context* thcontext = (struct thread_context*)ctxt; blosc2_context* context = thcontext->parent_context; struct blosc_job_group* job = context->job; int32_t cbytes; int32_t ntdest; int32_t tblocks; int32_t tblock; int32_t nblock_; int32_t bsize; int32_t leftoverblock; int32_t blocksize; int32_t ebsize; int32_t srcsize; bool compress = context->do_compress != 0; int32_t maxbytes; int32_t nblocks; int32_t leftover; int32_t leftover2; int32_t* bstarts; const uint8_t* src; uint8_t* dest; uint8_t* tmp; uint8_t* tmp2; uint8_t* tmp3; blocksize = context->blocksize; ebsize = blocksize + context->typesize * (int32_t)sizeof(int32_t); maxbytes = context->destsize; nblocks = context->nblocks; leftover = context->leftover; bstarts = context->bstarts; src = context->src; srcsize = context->srcsize; dest = context->dest; if (ensure_thread_context_capacity(thcontext, context) < 0) { blosc2_pthread_mutex_lock(&job->mutex); job->giveup_code = BLOSC2_ERROR_MEMORY_ALLOC; blosc2_pthread_mutex_unlock(&job->mutex); goto job_done; } tmp = thcontext->tmp; tmp2 = thcontext->tmp2; tmp3 = thcontext->tmp3; bool vlblocks = (context->blosc2_flags2 & BLOSC2_VL_BLOCKS) != 0; bool memcpyed = context->header_flags & (uint8_t)BLOSC_MEMCPYED; if (!context->do_compress && context->special_type) { memcpyed = true; } if (job->static_schedule) { tblocks = nblocks / context->nthreads; leftover2 = nblocks % context->nthreads; tblocks = (leftover2 > 0) ? tblocks + 1 : tblocks; nblock_ = thcontext->tid * tblocks; tblock = nblock_ + tblocks; if (tblock > nblocks) { tblock = nblocks; } } else { nblock_ = claim_job_block(job); tblock = nblocks; } leftoverblock = 0; while (nblock_ < tblock) { blosc2_pthread_mutex_lock(&job->mutex); if (job->giveup_code <= 0) { blosc2_pthread_mutex_unlock(&job->mutex); break; } blosc2_pthread_mutex_unlock(&job->mutex); bsize = vlblocks ? context->blocknbytes[nblock_] : blocksize; leftoverblock = 0; if (!vlblocks && nblock_ == (nblocks - 1) && (leftover > 0)) { bsize = leftover; leftoverblock = 1; } if (compress) { if (memcpyed) { if (!context->prefilter) { memcpy(dest + context->header_overhead + nblock_ * blocksize, src + nblock_ * blocksize, (unsigned int) bsize); cbytes = (int32_t)bsize; } else { cbytes = blosc_c(thcontext, bsize, leftoverblock, 0, ebsize, vlblocks ? context->vlblock_sources[nblock_] : src, vlblocks ? 0 : nblock_ * blocksize, dest + context->header_overhead + nblock_ * blocksize, tmp, tmp3); } } else { cbytes = blosc_c(thcontext, bsize, leftoverblock, 0, ebsize, vlblocks ? context->vlblock_sources[nblock_] : src, vlblocks ? 0 : nblock_ * blocksize, tmp2, tmp, tmp3); } } else { if (context->special_type == BLOSC2_NO_SPECIAL && !memcpyed && (srcsize < (int32_t)(context->header_overhead + (sizeof(int32_t) * nblocks)))) { cbytes = -1; } else { int32_t src_offset = memcpyed ? context->header_overhead + nblock_ * blocksize : sw32_(bstarts + nblock_); uint8_t *dest_block = (vlblocks && context->vlblock_dests != NULL) ? context->vlblock_dests[nblock_] : dest; int32_t dest_offset = (vlblocks && context->vlblock_dests != NULL) ? 0 : (vlblocks ? context->blockoffsets[nblock_] : nblock_ * blocksize); cbytes = blosc_d(thcontext, bsize, leftoverblock, memcpyed, src, srcsize, src_offset, nblock_, dest_block, dest_offset, tmp, tmp2); } } if (cbytes < 0) { blosc2_pthread_mutex_lock(&job->mutex); if (job->giveup_code > 0) { job->giveup_code = cbytes; } blosc2_pthread_mutex_unlock(&job->mutex); break; } blosc2_pthread_mutex_lock(&job->mutex); if (compress && !memcpyed) { ntdest = job->output_bytes; if (!(context->use_dict && context->dict_cdict == NULL)) { _sw32(bstarts + nblock_, (int32_t)ntdest); } if ((cbytes == 0) || (ntdest + cbytes > maxbytes)) { job->giveup_code = 0; blosc2_pthread_mutex_unlock(&job->mutex); break; } job->output_bytes += cbytes; blosc2_pthread_mutex_unlock(&job->mutex); memcpy(dest + ntdest, tmp2, (unsigned int)cbytes); } else { job->output_bytes += cbytes; blosc2_pthread_mutex_unlock(&job->mutex); } if (job->static_schedule) { nblock_++; } else { nblock_ = claim_job_block(job); } } job_done: blosc2_pthread_mutex_lock(&job->mutex); job->blocks_completed++; if (--job->active_workers == 0) { if (job->static_schedule && job->giveup_code > 0) { job->output_bytes = context->sourcesize; if (compress) { job->output_bytes += context->header_overhead; } } if (context->thread_backend == BLOSC_BACKEND_CALLBACK) { job->completed = true; blosc2_pthread_cond_broadcast(&job->completion_cv); } } blosc2_pthread_mutex_unlock(&job->mutex); } #endif /* _WIN32 */ static void job_group_init(struct blosc_job_group *job, blosc2_context *context) { memset(job, 0, sizeof(*job)); job->context = context; job->next_block = -1; job->output_bytes = context->output_bytes; job->giveup_code = 1; job->dref_not_init = 1; bool compress = context->do_compress != 0; bool memcpyed = context->header_flags & (uint8_t)BLOSC_MEMCPYED; if (!compress && context->special_type) { memcpyed = true; } job->static_schedule = (!compress || memcpyed) && context->block_maskout == NULL; blosc2_pthread_mutex_init(&job->mutex, NULL); blosc2_pthread_mutex_init(&job->delta_mutex, NULL); blosc2_pthread_cond_init(&job->delta_cv, NULL); blosc2_pthread_cond_init(&job->completion_cv, NULL); } static void job_group_destroy(struct blosc_job_group *job) { blosc2_pthread_mutex_destroy(&job->mutex); blosc2_pthread_mutex_destroy(&job->delta_mutex); blosc2_pthread_cond_destroy(&job->delta_cv); blosc2_pthread_cond_destroy(&job->completion_cv); } static void* shared_pool_worker(void* arg) { struct thread_context* thcontext = (struct thread_context*)arg; struct blosc_shared_pool* pool = thcontext->owner_pool; while (1) { struct blosc_job_group* job = NULL; struct blosc_job_queue_entry* entry = NULL; blosc2_pthread_mutex_lock(&pool->mutex); while (!pool->shutdown && pool->job_queue_head == NULL) { blosc2_pthread_cond_wait(&pool->work_cv, &pool->mutex); } if (pool->shutdown) { blosc2_pthread_mutex_unlock(&pool->mutex); break; } entry = pool->job_queue_head; job = entry->job; pool->job_queue_head = entry->next; if (pool->job_queue_head == NULL) { pool->job_queue_tail = NULL; } blosc2_pthread_mutex_unlock(&pool->mutex); int32_t logical_tid = entry->logical_tid; my_free(entry); thcontext->parent_context = job->context; thcontext->tid = logical_tid; t_blosc_do_job(thcontext); thcontext->parent_context = NULL; /* Signal job completion BEFORE touching pool accounting. * The job struct is stack-allocated in parallel_blosc; the main thread * is allowed to destroy it as soon as completed==true is visible. * Decrementing pending_workers here (under job->mutex) guarantees that * every worker has finished with the job before the last decrement can * reach 0 and wake the main thread. Doing the pool accounting afterwards * is safe because it no longer references the job. */ blosc2_pthread_mutex_lock(&job->mutex); if (--job->pending_workers == 0) { job->completed = true; blosc2_pthread_cond_broadcast(&job->completion_cv); } blosc2_pthread_mutex_unlock(&job->mutex); blosc2_pthread_mutex_lock(&pool->mutex); pool->active_jobs--; if (pool->active_jobs == 0 && pool->context_refs == 0 && pool->job_queue_head == NULL) { blosc2_pthread_cond_broadcast(&pool->idle_cv); } blosc2_pthread_mutex_unlock(&pool->mutex); } destroy_thread_context(thcontext); return NULL; } static struct blosc_shared_pool* find_shared_pool_locked(int16_t nthreads) { struct blosc_shared_pool *pool = shared_pools; while (pool != NULL) { if (pool->nthreads == nthreads) { return pool; } pool = pool->next; } return NULL; } static int create_shared_pool(int16_t nthreads, struct blosc_shared_pool **pool_out) { int rc = 0; int rc2; int32_t contexts_init = 0; // per-thread contexts successfully initialized int32_t threads_started = 0; // worker threads successfully created #if !defined(_WIN32) bool attr_inited = false; #endif struct blosc_shared_pool *pool = (struct blosc_shared_pool *)my_malloc(sizeof(*pool)); BLOSC_ERROR_NULL(pool, BLOSC2_ERROR_MEMORY_ALLOC); memset(pool, 0, sizeof(*pool)); pool->nthreads = nthreads; blosc2_pthread_mutex_init(&pool->mutex, NULL); blosc2_pthread_cond_init(&pool->work_cv, NULL); blosc2_pthread_cond_init(&pool->idle_cv, NULL); pool->threads = (blosc2_pthread_t*)my_malloc(nthreads * sizeof(blosc2_pthread_t)); if (pool->threads == NULL) { rc = BLOSC2_ERROR_MEMORY_ALLOC; goto error; } pool->thread_contexts = (struct thread_context*)my_malloc((size_t)nthreads * sizeof(struct thread_context)); if (pool->thread_contexts == NULL) { rc = BLOSC2_ERROR_MEMORY_ALLOC; goto error; } memset(pool->thread_contexts, 0, (size_t)nthreads * sizeof(struct thread_context)); #if !defined(_WIN32) pthread_attr_init(&pool->ct_attr); pthread_attr_setdetachstate(&pool->ct_attr, PTHREAD_CREATE_JOINABLE); attr_inited = true; #endif for (int32_t tid = 0; tid < nthreads; ++tid) { rc = init_thread_context(pool->thread_contexts + tid, NULL, tid); if (rc < 0) { goto error; } contexts_init++; pool->thread_contexts[tid].owner_pool = pool; #if !defined(_WIN32) rc2 = blosc2_pthread_create(&pool->threads[tid], &pool->ct_attr, shared_pool_worker, (void*)(pool->thread_contexts + tid)); #else rc2 = blosc2_pthread_create(&pool->threads[tid], NULL, shared_pool_worker, (void*)(pool->thread_contexts + tid)); #endif if (rc2) { BLOSC_TRACE_ERROR("Return code from blosc2_pthread_create() is %d.\n\tError detail: %s\n", rc2, strerror(rc2)); rc = BLOSC2_ERROR_THREAD_CREATE; goto error; } threads_started++; } *pool_out = pool; return 0; error: // A worker frees its own thread context when it exits (see shared_pool_worker), // so signal shutdown and join the ones already started before freeing anything. if (threads_started > 0) { blosc2_pthread_mutex_lock(&pool->mutex); pool->shutdown = 1; blosc2_pthread_cond_broadcast(&pool->work_cv); blosc2_pthread_mutex_unlock(&pool->mutex); for (int32_t t = 0; t < threads_started; ++t) { void *status; blosc2_pthread_join(pool->threads[t], &status); } } // Contexts that were initialized but never got a running worker have no owner // to free them; tear those down here (the started ones did it themselves). for (int32_t t = threads_started; t < contexts_init; ++t) { destroy_thread_context(pool->thread_contexts + t); } #if !defined(_WIN32) if (attr_inited) { pthread_attr_destroy(&pool->ct_attr); } #endif if (pool->thread_contexts != NULL) { my_free(pool->thread_contexts); } if (pool->threads != NULL) { my_free(pool->threads); } blosc2_pthread_cond_destroy(&pool->idle_cv); blosc2_pthread_cond_destroy(&pool->work_cv); blosc2_pthread_mutex_destroy(&pool->mutex); my_free(pool); return rc; } static void destroy_shared_pool(struct blosc_shared_pool *pool) { void *status; blosc2_pthread_mutex_lock(&pool->mutex); pool->shutdown = 1; blosc2_pthread_cond_broadcast(&pool->work_cv); blosc2_pthread_mutex_unlock(&pool->mutex); for (int32_t t = 0; t < pool->nthreads; ++t) { blosc2_pthread_join(pool->threads[t], &status); } #if !defined(_WIN32) pthread_attr_destroy(&pool->ct_attr); #endif my_free(pool->threads); my_free(pool->thread_contexts); blosc2_pthread_cond_destroy(&pool->idle_cv); blosc2_pthread_cond_destroy(&pool->work_cv); blosc2_pthread_mutex_destroy(&pool->mutex); my_free(pool); } static int attach_shared_pool(blosc2_context *context) { struct blosc_shared_pool *pool; if (!g_initlib) blosc2_init(); blosc2_pthread_mutex_lock(&pool_registry_mutex); pool = find_shared_pool_locked(context->nthreads); if (pool == NULL) { int rc = create_shared_pool(context->nthreads, &pool); if (rc < 0) { blosc2_pthread_mutex_unlock(&pool_registry_mutex); return rc; } pool->next = shared_pools; shared_pools = pool; } pool->context_refs++; blosc2_pthread_mutex_unlock(&pool_registry_mutex); context->thread_pool = pool; context->thread_backend = BLOSC_BACKEND_SHARED_POOL; context->threads_started = context->nthreads; context->pool_epoch = g_destroy_count; return 0; } #if defined(_WIN32) /* Per-context worker thread for Windows (BLOSC_BACKEND_PER_CONTEXT). * Sleeps between jobs using a job_seq counter; wakes when main increments * job_seq and broadcasts jobs_ready. The last worker to finish signals * jobs_done so main can return from parallel_blosc. */ static void* t_blosc_win(void* arg) { struct thread_context* thcontext = (struct thread_context*)arg; blosc2_context* context = thcontext->parent_context; blosc2_pthread_mutex_lock(&context->jobs_mutex); while (1) { /* Sleep until main posts a new job or signals shutdown. */ while (!context->end_threads && thcontext->my_job_seq == context->job_seq) { blosc2_pthread_cond_wait(&context->jobs_ready, &context->jobs_mutex); } if (context->end_threads) { break; } thcontext->my_job_seq = context->job_seq; blosc2_pthread_mutex_unlock(&context->jobs_mutex); t_blosc_do_job(thcontext); blosc2_pthread_mutex_lock(&context->jobs_mutex); if (--context->active_workers == 0) { blosc2_pthread_cond_signal(&context->jobs_done); } } blosc2_pthread_mutex_unlock(&context->jobs_mutex); free_thread_context(thcontext); return NULL; } static int init_threadpool(blosc2_context *context) { int16_t nthreads = context->nthreads; blosc2_pthread_mutex_init(&context->count_mutex, NULL); blosc2_pthread_mutex_init(&context->delta_mutex, NULL); blosc2_pthread_cond_init(&context->delta_cv, NULL); blosc2_pthread_mutex_init(&context->jobs_mutex, NULL); blosc2_pthread_cond_init(&context->jobs_ready, NULL); blosc2_pthread_cond_init(&context->jobs_done, NULL); context->end_threads = 0; context->job_seq = 0; context->active_workers = 0; context->thread_giveup_code = 1; context->thread_nblock = -1; context->threads = (blosc2_pthread_t *)my_malloc((size_t)nthreads * sizeof(blosc2_pthread_t)); if (context->threads == NULL) { goto oom_threads; } int16_t created = 0; for (int16_t tid = 0; tid < nthreads; tid++) { struct thread_context *thread_context = create_thread_context(context, tid); if (thread_context == NULL) { goto create_error; } thread_context->my_job_seq = 0; int rc = blosc2_pthread_create(&context->threads[tid], NULL, t_blosc_win, thread_context); if (rc != 0) { BLOSC_TRACE_ERROR("blosc2_pthread_create() failed: %d\n", rc); free_thread_context(thread_context); goto create_error; } created++; } context->thread_backend = BLOSC_BACKEND_PER_CONTEXT; context->threads_started = nthreads; return 0; create_error: blosc2_pthread_mutex_lock(&context->jobs_mutex); context->end_threads = 1; blosc2_pthread_cond_broadcast(&context->jobs_ready); blosc2_pthread_mutex_unlock(&context->jobs_mutex); for (int16_t t = 0; t < created; t++) { blosc2_pthread_join(context->threads[t], NULL); } my_free(context->threads); context->threads = NULL; oom_threads: blosc2_pthread_cond_destroy(&context->jobs_done); blosc2_pthread_cond_destroy(&context->jobs_ready); blosc2_pthread_mutex_destroy(&context->jobs_mutex); blosc2_pthread_cond_destroy(&context->delta_cv); blosc2_pthread_mutex_destroy(&context->delta_mutex); blosc2_pthread_mutex_destroy(&context->count_mutex); return BLOSC2_ERROR_THREAD_CREATE; } #endif /* _WIN32 */ static int init_callback_threads(blosc2_context *context) { int32_t tid; blosc2_pthread_mutex_init(&context->count_mutex, NULL); blosc2_pthread_mutex_init(&context->delta_mutex, NULL); blosc2_pthread_cond_init(&context->delta_cv, NULL); context->thread_contexts = (struct thread_context *)my_malloc( context->nthreads * sizeof(struct thread_context)); BLOSC_ERROR_NULL(context->thread_contexts, BLOSC2_ERROR_MEMORY_ALLOC); for (tid = 0; tid < context->nthreads; tid++) { init_thread_context(context->thread_contexts + tid, context, tid); } context->thread_backend = BLOSC_BACKEND_CALLBACK; context->threads_started = context->nthreads; return 0; } static int release_thread_backend(blosc2_context *context) { if (context->thread_backend == BLOSC_BACKEND_CALLBACK && context->threads_started > 0) { for (int32_t t = 0; t < context->threads_started; t++) { destroy_thread_context(context->thread_contexts + t); } my_free(context->thread_contexts); context->thread_contexts = NULL; blosc2_pthread_mutex_destroy(&context->count_mutex); blosc2_pthread_mutex_destroy(&context->delta_mutex); blosc2_pthread_cond_destroy(&context->delta_cv); } #if defined(_WIN32) else if (context->thread_backend == BLOSC_BACKEND_PER_CONTEXT && context->threads_started > 0) { /* Signal all workers to exit. */ blosc2_pthread_mutex_lock(&context->jobs_mutex); context->end_threads = 1; blosc2_pthread_cond_broadcast(&context->jobs_ready); blosc2_pthread_mutex_unlock(&context->jobs_mutex); for (int32_t t = 0; t < context->threads_started; t++) { blosc2_pthread_join(context->threads[t], NULL); } my_free(context->threads); context->threads = NULL; blosc2_pthread_cond_destroy(&context->jobs_done); blosc2_pthread_cond_destroy(&context->jobs_ready); blosc2_pthread_mutex_destroy(&context->jobs_mutex); blosc2_pthread_cond_destroy(&context->delta_cv); blosc2_pthread_mutex_destroy(&context->delta_mutex); blosc2_pthread_mutex_destroy(&context->count_mutex); } #endif /* _WIN32 */ else if (context->thread_backend == BLOSC_BACKEND_SHARED_POOL && context->thread_pool != NULL) { struct blosc_shared_pool *pool = context->thread_pool; struct blosc_shared_pool **prev; bool destroy_pool = false; if (context->pool_epoch != g_destroy_count) { /* blosc2_destroy() already freed this pool and tore down pool_registry_mutex. * Just clear the dangling pointer; nothing else to do. */ context->thread_pool = NULL; context->threads_started = 0; context->thread_backend = BLOSC_BACKEND_SERIAL; return 0; } blosc2_pthread_mutex_lock(&pool_registry_mutex); pool->context_refs--; if (pool->context_refs == 0) { /* Check pool-internal state under the pool's own mutex */ blosc2_pthread_mutex_lock(&pool->mutex); bool idle = (pool->active_jobs == 0 && pool->job_queue_head == NULL); blosc2_pthread_mutex_unlock(&pool->mutex); if (idle) { prev = &shared_pools; while (*prev != NULL && *prev != pool) { prev = &(*prev)->next; } if (*prev == pool) { *prev = pool->next; } destroy_pool = true; } } blosc2_pthread_mutex_unlock(&pool_registry_mutex); if (destroy_pool) { destroy_shared_pool(pool); } context->thread_pool = NULL; } context->threads_started = 0; context->thread_backend = BLOSC_BACKEND_SERIAL; return 0; } static int parallel_blosc(blosc2_context* context) { #if defined(_WIN32) /* Windows: per-context threads using WAIT_INIT/WAIT_FINISH barriers. * No job-group struct; workers read/write context fields directly. */ context->job = NULL; context->thread_giveup_code = 1; context->thread_nblock = -1; if (context->thread_backend == BLOSC_BACKEND_CALLBACK) { threads_callback(threads_callback_data, t_blosc_do_job, context->nthreads, sizeof(struct thread_context), (void*) context->thread_contexts); } else { /* BLOSC_BACKEND_PER_CONTEXT: post job to sleeping workers and wait. */ blosc2_pthread_mutex_lock(&context->jobs_mutex); context->active_workers = context->nthreads; context->job_seq++; blosc2_pthread_cond_broadcast(&context->jobs_ready); while (context->active_workers > 0) { blosc2_pthread_cond_wait(&context->jobs_done, &context->jobs_mutex); } blosc2_pthread_mutex_unlock(&context->jobs_mutex); } if (context->thread_giveup_code <= 0) { return context->thread_giveup_code; } return (int)context->output_bytes; #else /* !_WIN32 */ struct blosc_job_group job; job_group_init(&job, context); context->job = &job; if (context->thread_backend == BLOSC_BACKEND_CALLBACK) { blosc2_pthread_mutex_lock(&job.mutex); job.active_workers = context->nthreads; job.pending_workers = 0; blosc2_pthread_mutex_unlock(&job.mutex); threads_callback(threads_callback_data, t_blosc_do_job, context->nthreads, sizeof(struct thread_context), (void*) context->thread_contexts); } else { struct blosc_shared_pool *pool = context->thread_pool; blosc2_pthread_mutex_lock(&pool->mutex); int32_t enqueued = 0; for (int32_t tid = 0; tid < context->nthreads; ++tid) { struct blosc_job_queue_entry *entry = (struct blosc_job_queue_entry *)my_malloc(sizeof(*entry)); if (entry == NULL) { /* Drain already-enqueued entries so they don't reference the stack-allocated job after we return. */ struct blosc_job_queue_entry **pp = &pool->job_queue_head; while (*pp != NULL) { if ((*pp)->job == &job) { struct blosc_job_queue_entry *victim = *pp; *pp = victim->next; my_free(victim); pool->active_jobs--; } else { pp = &(*pp)->next; } } pool->job_queue_tail = NULL; /* Recompute tail */ for (struct blosc_job_queue_entry *e = pool->job_queue_head; e != NULL; e = e->next) { pool->job_queue_tail = e; } blosc2_pthread_mutex_unlock(&pool->mutex); context->job = NULL; job_group_destroy(&job); return BLOSC2_ERROR_MEMORY_ALLOC; } memset(entry, 0, sizeof(*entry)); entry->job = &job; entry->logical_tid = tid; if (pool->job_queue_tail != NULL) { pool->job_queue_tail->next = entry; } else { pool->job_queue_head = entry; } pool->job_queue_tail = entry; pool->active_jobs++; enqueued++; } /* Set active_workers to the actual number enqueued */ blosc2_pthread_mutex_lock(&job.mutex); job.active_workers = enqueued; job.pending_workers = enqueued; blosc2_pthread_mutex_unlock(&job.mutex); blosc2_pthread_cond_broadcast(&pool->work_cv); blosc2_pthread_mutex_unlock(&pool->mutex); blosc2_pthread_mutex_lock(&job.mutex); while (!job.completed) { blosc2_pthread_cond_wait(&job.completion_cv, &job.mutex); } blosc2_pthread_mutex_unlock(&job.mutex); } context->job = NULL; context->output_bytes = job.output_bytes; context->thread_giveup_code = job.giveup_code; job_group_destroy(&job); if (context->thread_giveup_code <= 0) { return context->thread_giveup_code; } return (int)context->output_bytes; #endif /* _WIN32 */ } int16_t blosc2_get_nthreads(void) { int16_t nthreads; if (!g_initlib) { blosc2_init(); } blosc2_pthread_mutex_lock(&global_comp_mutex); nthreads = g_nthreads; blosc2_pthread_mutex_unlock(&global_comp_mutex); return nthreads; } int16_t blosc2_set_nthreads(int16_t nthreads) { int16_t ret; /* the previous number of threads */ /* Check whether the library should be initialized */ if (!g_initlib) blosc2_init(); blosc2_pthread_mutex_lock(&global_comp_mutex); ret = g_nthreads; if (nthreads != ret) { int16_t old_new_nthreads = g_global_context->new_nthreads; int16_t old_nthreads = g_global_context->nthreads; g_nthreads = nthreads; g_global_context->new_nthreads = nthreads; int16_t ret2 = check_nthreads(g_global_context); if (ret2 < 0) { g_nthreads = ret; g_global_context->new_nthreads = old_new_nthreads; g_global_context->nthreads = old_nthreads; check_nthreads(g_global_context); blosc2_pthread_mutex_unlock(&global_comp_mutex); return ret2; } } blosc2_pthread_mutex_unlock(&global_comp_mutex); return ret; } const char* blosc1_get_compressor(void) { const char* compname; if (!g_initlib) { blosc2_init(); } blosc2_pthread_mutex_lock(&global_comp_mutex); blosc2_compcode_to_compname(g_compressor, &compname); blosc2_pthread_mutex_unlock(&global_comp_mutex); return compname; } int blosc1_set_compressor(const char* compname) { int code = blosc2_compname_to_compcode(compname); if (code >= BLOSC_LAST_CODEC) { BLOSC_TRACE_ERROR("User defined codecs cannot be set here. Use Blosc2 mechanism instead."); BLOSC_ERROR(BLOSC2_ERROR_CODEC_SUPPORT); } /* Check whether the library should be initialized */ if (!g_initlib) blosc2_init(); blosc2_pthread_mutex_lock(&global_comp_mutex); g_compressor = code; blosc2_pthread_mutex_unlock(&global_comp_mutex); return code; } void blosc2_set_delta(int dodelta) { /* Check whether the library should be initialized */ if (!g_initlib) blosc2_init(); blosc2_pthread_mutex_lock(&global_comp_mutex); g_delta = dodelta; blosc2_pthread_mutex_unlock(&global_comp_mutex); } const char* blosc2_list_compressors(void) { static int compressors_list_done = 0; static char ret[256]; if (compressors_list_done) return ret; ret[0] = '\0'; size_t _avail = sizeof(ret); char *_p = ret; /* Start with the first compressor name. */ int _sw = snprintf(_p, _avail, "%s", BLOSC_BLOSCLZ_COMPNAME); if (_sw < 0) { ret[0] = '\0'; compressors_list_done = 1; return ret; } if ((size_t)_sw >= _avail) { /* Truncated; ensure terminated and return */ ret[sizeof(ret)-1] = '\0'; compressors_list_done = 1; return ret; } _p += _sw; _avail -= (size_t)_sw; #define APPEND_COMP(NAME) do { \ if (_avail > 1) { \ int __w = snprintf(_p, _avail, ",%s", (NAME)); \ if (__w < 0) { break; } \ if ((size_t)__w >= _avail) { _p += _avail - 1; _avail = 1; *_p = '\0'; break; } \ _p += __w; _avail -= (size_t)__w; \ } \ } while(0) APPEND_COMP(BLOSC_LZ4_COMPNAME); APPEND_COMP(BLOSC_LZ4HC_COMPNAME); #if defined(HAVE_ZLIB) APPEND_COMP(BLOSC_ZLIB_COMPNAME); #endif /* HAVE_ZLIB */ #if defined(HAVE_ZSTD) APPEND_COMP(BLOSC_ZSTD_COMPNAME); #endif /* HAVE_ZSTD */ #undef APPEND_COMP compressors_list_done = 1; return ret; } const char* blosc2_get_version_string(void) { return BLOSC2_VERSION_STRING; } int blosc2_get_complib_info(const char* compname, char** complib, char** version) { int clibcode; const char* clibname; const char* clibversion = "unknown"; char sbuffer[256]; clibcode = compname_to_clibcode(compname); clibname = clibcode_to_clibname(clibcode); /* complib version */ if (clibcode == BLOSC_BLOSCLZ_LIB) { clibversion = BLOSCLZ_VERSION_STRING; } else if (clibcode == BLOSC_LZ4_LIB) { int _sn = snprintf(sbuffer, sizeof(sbuffer), "%d.%d.%d", LZ4_VERSION_MAJOR, LZ4_VERSION_MINOR, LZ4_VERSION_RELEASE); if (_sn >= 0 && (size_t)_sn < sizeof(sbuffer)) { clibversion = sbuffer; } } #if defined(HAVE_ZLIB) else if (clibcode == BLOSC_ZLIB_LIB) { #ifdef ZLIB_COMPAT clibversion = ZLIB_VERSION; #elif defined(HAVE_ZLIB_NG) clibversion = ZLIBNG_VERSION; #else clibversion = ZLIB_VERSION; #endif } #endif /* HAVE_ZLIB */ #if defined(HAVE_ZSTD) else if (clibcode == BLOSC_ZSTD_LIB) { int _sn_zstd = snprintf(sbuffer, sizeof(sbuffer), "%d.%d.%d", ZSTD_VERSION_MAJOR, ZSTD_VERSION_MINOR, ZSTD_VERSION_RELEASE); if (_sn_zstd >= 0 && (size_t)_sn_zstd < sizeof(sbuffer)) { clibversion = sbuffer; } } #endif /* HAVE_ZSTD */ #ifdef _MSC_VER *complib = _strdup(clibname); *version = _strdup(clibversion); #else *complib = strdup(clibname); *version = strdup(clibversion); #endif return clibcode; } /* Return `nbytes`, `cbytes` and `blocksize` from a compressed buffer. */ void blosc1_cbuffer_sizes(const void* cbuffer, size_t* nbytes, size_t* cbytes, size_t* blocksize) { int32_t nbytes32, cbytes32, blocksize32; blosc2_cbuffer_sizes(cbuffer, &nbytes32, &cbytes32, &blocksize32); *nbytes = nbytes32; *cbytes = cbytes32; *blocksize = blocksize32; } int blosc2_cbuffer_sizes(const void* cbuffer, int32_t* nbytes, int32_t* cbytes, int32_t* blocksize) { blosc_header header; int rc = read_chunk_header((uint8_t *) cbuffer, BLOSC_MIN_HEADER_LENGTH, false, &header); if (rc < 0) { /* Return zeros if error reading header */ memset(&header, 0, sizeof(header)); } /* Read the interesting values */ if (nbytes != NULL) *nbytes = header.nbytes; if (cbytes != NULL) *cbytes = header.cbytes; if (blocksize != NULL) *blocksize = header.blocksize; return rc; } int blosc1_cbuffer_validate(const void* cbuffer, size_t cbytes, size_t* nbytes) { int32_t header_cbytes; int32_t header_nbytes; if (cbytes < BLOSC_MIN_HEADER_LENGTH) { /* Compressed data should contain enough space for header */ *nbytes = 0; return BLOSC2_ERROR_WRITE_BUFFER; } int rc = blosc2_cbuffer_sizes(cbuffer, &header_nbytes, &header_cbytes, NULL); if (rc < 0) { *nbytes = 0; return rc; } *nbytes = header_nbytes; if (header_cbytes != (int32_t)cbytes) { /* Compressed size from header does not match `cbytes` */ *nbytes = 0; return BLOSC2_ERROR_INVALID_HEADER; } if (*nbytes > BLOSC2_MAX_BUFFERSIZE) { /* Uncompressed size is larger than allowed */ *nbytes = 0; return BLOSC2_ERROR_MEMORY_ALLOC; } return 0; } /* Return `typesize` and `flags` from a compressed buffer. */ void blosc1_cbuffer_metainfo(const void* cbuffer, size_t* typesize, int* flags) { blosc_header header; int rc = read_chunk_header((uint8_t *) cbuffer, BLOSC_MIN_HEADER_LENGTH, false, &header); if (rc < 0) { *typesize = *flags = 0; return; } /* Read the interesting values */ *flags = header.flags; *typesize = header.typesize; } /* Return version information from a compressed buffer. */ void blosc2_cbuffer_versions(const void* cbuffer, int* version, int* versionlz) { blosc_header header; int rc = read_chunk_header((uint8_t *) cbuffer, BLOSC_MIN_HEADER_LENGTH, false, &header); if (rc < 0) { *version = *versionlz = 0; return; } /* Read the version info */ *version = header.version; *versionlz = header.versionlz; } /* Return the compressor library/format used in a compressed buffer. */ const char* blosc2_cbuffer_complib(const void* cbuffer) { blosc_header header; int clibcode; const char* complib; int rc = read_chunk_header((uint8_t *) cbuffer, BLOSC_MIN_HEADER_LENGTH, false, &header); if (rc < 0) { return NULL; } /* Read the compressor format/library info */ clibcode = (header.flags & 0xe0) >> 5; complib = clibcode_to_clibname(clibcode); return complib; } /* Get the internal blocksize to be used during compression. 0 means that an automatic blocksize is computed internally. */ int blosc1_get_blocksize(void) { int blocksize; if (!g_initlib) { blosc2_init(); } blosc2_pthread_mutex_lock(&global_comp_mutex); blocksize = (int)g_force_blocksize; blosc2_pthread_mutex_unlock(&global_comp_mutex); return blocksize; } /* Force the use of a specific blocksize. If 0, an automatic blocksize will be used (the default). */ void blosc1_set_blocksize(size_t blocksize) { if (!g_initlib) { blosc2_init(); } blosc2_pthread_mutex_lock(&global_comp_mutex); g_force_blocksize = (int32_t)blocksize; blosc2_pthread_mutex_unlock(&global_comp_mutex); } /* Force the use of a specific split mode. */ void blosc1_set_splitmode(int mode) { if (!g_initlib) { blosc2_init(); } blosc2_pthread_mutex_lock(&global_comp_mutex); g_splitmode = mode; blosc2_pthread_mutex_unlock(&global_comp_mutex); } /* Set pointer to super-chunk. If NULL, no super-chunk will be reachable (the default). */ void blosc_set_schunk(blosc2_schunk* schunk) { if (!g_initlib) { blosc2_init(); } blosc2_pthread_mutex_lock(&global_comp_mutex); g_schunk = schunk; if (g_global_context != NULL) { g_global_context->schunk = schunk; } blosc2_pthread_mutex_unlock(&global_comp_mutex); } blosc2_io *blosc2_io_global = NULL; blosc2_io_cb BLOSC2_IO_CB_DEFAULTS; blosc2_io_cb BLOSC2_IO_CB_MMAP; int _blosc2_register_io_cb(const blosc2_io_cb *io); void blosc2_init(void) { /* Return if Blosc is already initialized */ if (g_initlib) return; BLOSC2_IO_CB_DEFAULTS.id = BLOSC2_IO_FILESYSTEM; BLOSC2_IO_CB_DEFAULTS.name = "filesystem"; BLOSC2_IO_CB_DEFAULTS.is_allocation_necessary = true; BLOSC2_IO_CB_DEFAULTS.open = (blosc2_open_cb) blosc2_stdio_open; BLOSC2_IO_CB_DEFAULTS.close = (blosc2_close_cb) blosc2_stdio_close; BLOSC2_IO_CB_DEFAULTS.size = (blosc2_size_cb) blosc2_stdio_size; BLOSC2_IO_CB_DEFAULTS.write = (blosc2_write_cb) blosc2_stdio_write; BLOSC2_IO_CB_DEFAULTS.read = (blosc2_read_cb) blosc2_stdio_read; BLOSC2_IO_CB_DEFAULTS.truncate = (blosc2_truncate_cb) blosc2_stdio_truncate; BLOSC2_IO_CB_DEFAULTS.destroy = (blosc2_destroy_cb) blosc2_stdio_destroy; _blosc2_register_io_cb(&BLOSC2_IO_CB_DEFAULTS); BLOSC2_IO_CB_MMAP.id = BLOSC2_IO_FILESYSTEM_MMAP; BLOSC2_IO_CB_MMAP.name = "filesystem_mmap"; BLOSC2_IO_CB_MMAP.is_allocation_necessary = false; BLOSC2_IO_CB_MMAP.open = (blosc2_open_cb) blosc2_stdio_mmap_open; BLOSC2_IO_CB_MMAP.close = (blosc2_close_cb) blosc2_stdio_mmap_close; BLOSC2_IO_CB_MMAP.read = (blosc2_read_cb) blosc2_stdio_mmap_read; BLOSC2_IO_CB_MMAP.size = (blosc2_size_cb) blosc2_stdio_mmap_size; BLOSC2_IO_CB_MMAP.write = (blosc2_write_cb) blosc2_stdio_mmap_write; BLOSC2_IO_CB_MMAP.truncate = (blosc2_truncate_cb) blosc2_stdio_mmap_truncate; BLOSC2_IO_CB_MMAP.destroy = (blosc2_destroy_cb) blosc2_stdio_mmap_destroy; _blosc2_register_io_cb(&BLOSC2_IO_CB_MMAP); g_ncodecs = 0; g_nfilters = 0; g_ntuners = 0; #if defined(HAVE_PLUGINS) #include "blosc2/blosc2-common.h" #include "blosc2/blosc2-stdio.h" register_codecs(); register_filters(); register_tuners(); #endif blosc2_pthread_mutex_init(&global_comp_mutex, NULL); blosc2_pthread_mutex_init(&pool_registry_mutex, NULL); /* Create a global context */ g_global_context = (blosc2_context*)my_malloc(sizeof(blosc2_context)); memset(g_global_context, 0, sizeof(blosc2_context)); g_global_context->nthreads = g_nthreads; g_global_context->new_nthreads = g_nthreads; blosc2_pthread_mutex_init(&g_global_context->nchunk_mutex, NULL); g_initlib = 1; } int blosc2_free_resources(void) { /* Return if Blosc is not initialized */ if (!g_initlib) return BLOSC2_ERROR_FAILURE; return release_thread_backend(g_global_context); } void blosc2_destroy(void) { /* Return if Blosc is not initialized */ if (!g_initlib) return; blosc2_free_resources(); g_initlib = 0; blosc2_free_ctx(g_global_context); /* Bump the epoch so any live context can detect its pool is now stale. * Do this before freeing the pools so that release_thread_backend callers * that race with us will take the "skip" path rather than locking the * soon-to-be-destroyed pool_registry_mutex. */ g_destroy_count++; /* Tear down any remaining shared pools */ struct blosc_shared_pool *pool = shared_pools; while (pool != NULL) { struct blosc_shared_pool *next = pool->next; destroy_shared_pool(pool); pool = next; } shared_pools = NULL; blosc2_pthread_mutex_destroy(&pool_registry_mutex); blosc2_pthread_mutex_destroy(&global_comp_mutex); } /* Contexts */ /* Create a context for compression */ blosc2_context* blosc2_create_cctx(blosc2_cparams cparams) { blosc2_context* context = (blosc2_context*)my_malloc(sizeof(blosc2_context)); BLOSC_ERROR_NULL(context, NULL); /* Populate the context, using zeros as default values */ memset(context, 0, sizeof(blosc2_context)); context->do_compress = 1; /* meant for compression */ context->use_dict = cparams.use_dict; if (cparams.instr_codec) { context->blosc2_flags = BLOSC2_INSTR_CODEC; } for (int i = 0; i < BLOSC2_MAX_FILTERS; i++) { context->filters[i] = cparams.filters[i]; context->filters_meta[i] = cparams.filters_meta[i]; if (context->filters[i] >= BLOSC_LAST_FILTER && context->filters[i] <= BLOSC2_DEFINED_FILTERS_STOP) { BLOSC_TRACE_ERROR("filter (%d) is not yet defined", context->filters[i]); free(context); return NULL; } if (context->filters[i] > BLOSC_LAST_REGISTERED_FILTER && context->filters[i] <= BLOSC2_GLOBAL_REGISTERED_FILTERS_STOP) { BLOSC_TRACE_ERROR("filter (%d) is not yet defined", context->filters[i]); free(context); return NULL; } } #if defined(HAVE_ZFP) #include "blosc2/codecs-registry.h" if ((context->compcode >= BLOSC_CODEC_ZFP_FIXED_ACCURACY) && (context->compcode <= BLOSC_CODEC_ZFP_FIXED_RATE)) { for (int i = 0; i < BLOSC2_MAX_FILTERS; ++i) { if ((context->filters[i] == BLOSC_SHUFFLE) || (context->filters[i] == BLOSC_BITSHUFFLE)) { BLOSC_TRACE_ERROR("ZFP cannot be run in presence of SHUFFLE / BITSHUFFLE"); return NULL; } } } #endif /* HAVE_ZFP */ /* Check for a BLOSC_SHUFFLE environment variable */ int doshuffle = -1; char* envvar = getenv("BLOSC_SHUFFLE"); if (envvar != NULL) { if (strcmp(envvar, "NOSHUFFLE") == 0) { doshuffle = BLOSC_NOSHUFFLE; } else if (strcmp(envvar, "SHUFFLE") == 0) { doshuffle = BLOSC_SHUFFLE; } else if (strcmp(envvar, "BITSHUFFLE") == 0) { doshuffle = BLOSC_BITSHUFFLE; } else { BLOSC_TRACE_WARNING("BLOSC_SHUFFLE environment variable '%s' not recognized\n", envvar); } } /* Check for a BLOSC_DELTA environment variable */ int dodelta = BLOSC_NOFILTER; envvar = getenv("BLOSC_DELTA"); if (envvar != NULL) { if (strcmp(envvar, "1") == 0) { dodelta = BLOSC_DELTA; } else if (strcmp(envvar, "0") == 0){ dodelta = BLOSC_NOFILTER; } else { BLOSC_TRACE_WARNING("BLOSC_DELTA environment variable '%s' not recognized\n", envvar); } } /* Check for a BLOSC_TYPESIZE environment variable */ context->typesize = cparams.typesize; envvar = getenv("BLOSC_TYPESIZE"); if (envvar != NULL) { int32_t value; errno = 0; /* To distinguish success/failure after call */ value = (int32_t) strtol(envvar, NULL, 10); if ((errno != EINVAL) && (value > 0)) { context->typesize = value; } else { BLOSC_TRACE_WARNING("BLOSC_TYPESIZE environment variable '%s' not recognized\n", envvar); } } build_filters(doshuffle, dodelta, context->typesize, context->filters); context->clevel = cparams.clevel; /* Check for a BLOSC_CLEVEL environment variable */ envvar = getenv("BLOSC_CLEVEL"); if (envvar != NULL) { int value; errno = 0; /* To distinguish success/failure after call */ value = (int)strtol(envvar, NULL, 10); if ((errno != EINVAL) && (value >= 0)) { context->clevel = value; } else { BLOSC_TRACE_WARNING("BLOSC_CLEVEL environment variable '%s' not recognized\n", envvar); } } context->compcode = cparams.compcode; /* Check for a BLOSC_COMPRESSOR environment variable */ envvar = getenv("BLOSC_COMPRESSOR"); if (envvar != NULL) { int codec = blosc2_compname_to_compcode(envvar); if (codec >= BLOSC_LAST_CODEC) { BLOSC_TRACE_ERROR("User defined codecs cannot be set here. Use Blosc2 mechanism instead."); return NULL; } context->compcode = codec; } context->compcode_meta = cparams.compcode_meta; context->blocksize = cparams.blocksize; /* Check for a BLOSC_BLOCKSIZE environment variable */ envvar = getenv("BLOSC_BLOCKSIZE"); if (envvar != NULL) { int32_t blocksize; errno = 0; /* To distinguish success/failure after call */ blocksize = (int32_t) strtol(envvar, NULL, 10); if ((errno != EINVAL) && (blocksize > 0)) { context->blocksize = blocksize; } else { BLOSC_TRACE_WARNING("BLOSC_BLOCKSIZE environment variable '%s' not recognized\n", envvar); } } context->nthreads = cparams.nthreads; /* Check for a BLOSC_NTHREADS environment variable */ envvar = getenv("BLOSC_NTHREADS"); if (envvar != NULL) { errno = 0; /* To distinguish success/failure after call */ int16_t nthreads = (int16_t) strtol(envvar, NULL, 10); if ((errno != EINVAL) && (nthreads > 0)) { context->nthreads = nthreads; } else { BLOSC_TRACE_WARNING("BLOSC_NTHREADS environment variable '%s' not recognized\n", envvar); } } context->new_nthreads = context->nthreads; context->splitmode = cparams.splitmode; /* Check for a BLOSC_SPLITMODE environment variable */ envvar = getenv("BLOSC_SPLITMODE"); if (envvar != NULL) { int32_t splitmode = -1; if (strcmp(envvar, "ALWAYS") == 0) { splitmode = BLOSC_ALWAYS_SPLIT; } else if (strcmp(envvar, "NEVER") == 0) { splitmode = BLOSC_NEVER_SPLIT; } else if (strcmp(envvar, "AUTO") == 0) { splitmode = BLOSC_AUTO_SPLIT; } else if (strcmp(envvar, "FORWARD_COMPAT") == 0) { splitmode = BLOSC_FORWARD_COMPAT_SPLIT; } else { BLOSC_TRACE_WARNING("BLOSC_SPLITMODE environment variable '%s' not recognized\n", envvar); } if (splitmode >= 0) { context->splitmode = splitmode; } } context->threads_started = 0; blosc2_pthread_mutex_init(&context->nchunk_mutex, NULL); context->schunk = cparams.schunk; if (cparams.prefilter != NULL) { context->prefilter = cparams.prefilter; context->preparams = (blosc2_prefilter_params*)my_malloc(sizeof(blosc2_prefilter_params)); BLOSC_ERROR_NULL(context->preparams, NULL); memcpy(context->preparams, cparams.preparams, sizeof(blosc2_prefilter_params)); } if (cparams.tuner_id <= 0) { cparams.tuner_id = g_tuner; } else { for (int i = 0; i < g_ntuners; ++i) { if (g_tuners[i].id == cparams.tuner_id) { if (g_tuners[i].init == NULL) { if (fill_tuner(&g_tuners[i]) < 0) { BLOSC_TRACE_ERROR("Could not load tuner %d.", g_tuners[i].id); return NULL; } } if (g_tuners[i].init(cparams.tuner_params, context, NULL) < 0) { BLOSC_TRACE_ERROR("Error in user-defined tuner %d init function\n", cparams.tuner_id); return NULL; } goto urtunersuccess; } } BLOSC_TRACE_ERROR("User-defined tuner %d not found\n", cparams.tuner_id); return NULL; } urtunersuccess:; context->tuner_id = cparams.tuner_id; context->codec_params = cparams.codec_params; memcpy(context->filter_params, cparams.filter_params, BLOSC2_MAX_FILTERS * sizeof(void*)); return context; } /* Create a context for decompression */ blosc2_context* blosc2_create_dctx(blosc2_dparams dparams) { blosc2_context* context = (blosc2_context*)my_malloc(sizeof(blosc2_context)); BLOSC_ERROR_NULL(context, NULL); /* Populate the context, using zeros as default values */ memset(context, 0, sizeof(blosc2_context)); context->do_compress = 0; /* Meant for decompression */ context->nthreads = dparams.nthreads; char* envvar = getenv("BLOSC_NTHREADS"); if (envvar != NULL) { errno = 0; /* To distinguish success/failure after call */ long nthreads = strtol(envvar, NULL, 10); if ((errno != EINVAL) && (nthreads > 0)) { context->nthreads = (int16_t) nthreads; } } context->new_nthreads = context->nthreads; context->threads_started = 0; context->block_maskout = NULL; context->block_maskout_nitems = 0; blosc2_pthread_mutex_init(&context->nchunk_mutex, NULL); context->schunk = dparams.schunk; if (dparams.postfilter != NULL) { context->postfilter = dparams.postfilter; context->postparams = (blosc2_postfilter_params*)my_malloc(sizeof(blosc2_postfilter_params)); BLOSC_ERROR_NULL(context->postparams, NULL); memcpy(context->postparams, dparams.postparams, sizeof(blosc2_postfilter_params)); } return context; } void blosc2_free_ctx(blosc2_context* context) { if (g_initlib || context->thread_backend == BLOSC_BACKEND_PER_CONTEXT || context->thread_backend == BLOSC_BACKEND_CALLBACK) { release_thread_backend(context); } if (context->serial_context != NULL) { free_thread_context(context->serial_context); } blosc2_pthread_mutex_destroy(&context->nchunk_mutex); release_context_dict_buffer(context); if (context->dict_cdict != NULL) { if (context->compcode == BLOSC_LZ4) { LZ4_freeStream((LZ4_stream_t*)context->dict_cdict); } else if (context->compcode == BLOSC_LZ4HC) { LZ4_freeStreamHC((LZ4_streamHC_t*)context->dict_cdict); } #ifdef HAVE_ZSTD else if (context->compcode == BLOSC_ZSTD) { ZSTD_freeCDict(context->dict_cdict); } #endif } if (context->dict_ddict != NULL) { #ifdef HAVE_ZSTD ZSTD_freeDDict(context->dict_ddict); #endif } if (context->tuner_params != NULL) { int rc; if (context->tuner_id < BLOSC_LAST_TUNER && context->tuner_id == BLOSC_STUNE) { rc = blosc_stune_free(context); } else { for (int i = 0; i < g_ntuners; ++i) { if (g_tuners[i].id == context->tuner_id) { if (g_tuners[i].free == NULL) { if (fill_tuner(&g_tuners[i]) < 0) { BLOSC_TRACE_ERROR("Could not load tuner %d.", g_tuners[i].id); return; } } rc = g_tuners[i].free(context); goto urtunersuccess; } } BLOSC_TRACE_ERROR("User-defined tuner %d not found\n", context->tuner_id); return; urtunersuccess:; } if (rc < 0) { BLOSC_TRACE_ERROR("Error in user-defined tuner free function\n"); return; } } /* May be needed if codec_params ever contains nested objects if (context->codec_params != NULL) { int rc; for (int i = 0; i < g_ncodecs; ++i) { if (g_codecs[i].compcode == context->compcode) { if (g_codecs[i].free == NULL) { // Dynamically load codec plugin if (fill_codec(&g_codecs[i]) < 0) { BLOSC_TRACE_ERROR("Could not load codec %d.", g_codecs[i].compcode); return BLOSC2_ERROR_CODEC_SUPPORT; } } if (g_codecs[i].free == NULL){ // no free func, codec_params is simple my_free(context->codec_params); } else{ // has free function for codec_params (e.g. openzl) rc = g_codecs[i].free(context->codec_params); goto urcodecsuccess; } } } BLOSC_TRACE_ERROR("User-defined compressor codec %d not found", context->compcode); return BLOSC2_ERROR_CODEC_SUPPORT; urcodecsuccess:; if (rc < 0) { BLOSC_TRACE_ERROR("Error in user-defined codec free function\n"); return; } } */ if (context->prefilter != NULL) { my_free(context->preparams); } if (context->postfilter != NULL) { my_free(context->postparams); } if (context->block_maskout != NULL) { free(context->block_maskout); } if (context->blocknbytes != NULL) { free(context->blocknbytes); } if (context->blockoffsets != NULL) { free(context->blockoffsets); } if (context->blockcbytes != NULL) { free(context->blockcbytes); } my_free(context); } int blosc2_ctx_get_cparams(blosc2_context *ctx, blosc2_cparams *cparams) { cparams->compcode = ctx->compcode; cparams->compcode_meta = ctx->compcode_meta; cparams->clevel = ctx->clevel; cparams->use_dict = ctx->use_dict; cparams->instr_codec = ctx->blosc2_flags & BLOSC2_INSTR_CODEC; cparams->typesize = ctx->typesize; cparams->nthreads = ctx->nthreads; cparams->blocksize = ctx->blocksize; cparams->splitmode = ctx->splitmode; cparams->schunk = ctx->schunk; for (int i = 0; i < BLOSC2_MAX_FILTERS; ++i) { cparams->filters[i] = ctx->filters[i]; cparams->filters_meta[i] = ctx->filters_meta[i]; } cparams->prefilter = ctx->prefilter; cparams->preparams = ctx->preparams; cparams->tuner_id = ctx->tuner_id; cparams->codec_params = ctx->codec_params; return BLOSC2_ERROR_SUCCESS; } int blosc2_ctx_get_dparams(blosc2_context *ctx, blosc2_dparams *dparams) { dparams->nthreads = ctx->nthreads; dparams->schunk = ctx->schunk; dparams->postfilter = ctx->postfilter; dparams->postparams = ctx->postparams; dparams->typesize = ctx->typesize; return BLOSC2_ERROR_SUCCESS; } /* Set a maskout in decompression context */ int blosc2_set_maskout(blosc2_context *ctx, bool *maskout, int nblocks) { if (ctx->block_maskout != NULL) { // Get rid of a possible mask here free(ctx->block_maskout); } bool *maskout_ = malloc(nblocks); BLOSC_ERROR_NULL(maskout_, BLOSC2_ERROR_MEMORY_ALLOC); memcpy(maskout_, maskout, nblocks); ctx->block_maskout = maskout_; ctx->block_maskout_nitems = nblocks; return 0; } /* Create a chunk made of zeros */ int blosc2_chunk_zeros(blosc2_cparams cparams, const int32_t nbytes, void* dest, int32_t destsize) { if (destsize < BLOSC_EXTENDED_HEADER_LENGTH) { BLOSC_TRACE_ERROR("dest buffer is not long enough"); return BLOSC2_ERROR_DATA; } if ((nbytes > 0) && (nbytes % cparams.typesize)) { BLOSC_TRACE_ERROR("nbytes must be a multiple of typesize"); return BLOSC2_ERROR_DATA; } blosc_header header; blosc2_context* context = blosc2_create_cctx(cparams); if (context == NULL) { BLOSC_TRACE_ERROR("Error while creating the compression context"); return BLOSC2_ERROR_NULL_POINTER; } int error = initialize_context_compression( context, NULL, nbytes, dest, destsize, context->clevel, context->filters, context->filters_meta, context->typesize, context->compcode, context->blocksize, context->new_nthreads, context->nthreads, context->splitmode, context->tuner_id, context->tuner_params, context->schunk); if (error <= 0) { blosc2_free_ctx(context); return error; } memset(&header, 0, sizeof(header)); header.version = BLOSC2_VERSION_FORMAT_STABLE; header.versionlz = BLOSC_BLOSCLZ_VERSION_FORMAT; header.flags = BLOSC_DOSHUFFLE | BLOSC_DOBITSHUFFLE; // extended header header.typesize = context->typesize; header.nbytes = (int32_t)nbytes; header.blocksize = context->blocksize; header.cbytes = BLOSC_EXTENDED_HEADER_LENGTH; header.blosc2_flags = BLOSC2_SPECIAL_ZERO << 4; // mark chunk as all zeros memcpy((uint8_t *)dest, &header, sizeof(header)); blosc2_free_ctx(context); return BLOSC_EXTENDED_HEADER_LENGTH; } /* Create a chunk made of uninitialized values */ int blosc2_chunk_uninit(blosc2_cparams cparams, const int32_t nbytes, void* dest, int32_t destsize) { if (destsize < BLOSC_EXTENDED_HEADER_LENGTH) { BLOSC_TRACE_ERROR("dest buffer is not long enough"); return BLOSC2_ERROR_DATA; } if (nbytes % cparams.typesize) { BLOSC_TRACE_ERROR("nbytes must be a multiple of typesize"); return BLOSC2_ERROR_DATA; } blosc_header header; blosc2_context* context = blosc2_create_cctx(cparams); if (context == NULL) { BLOSC_TRACE_ERROR("Error while creating the compression context"); return BLOSC2_ERROR_NULL_POINTER; } int error = initialize_context_compression( context, NULL, nbytes, dest, destsize, context->clevel, context->filters, context->filters_meta, context->typesize, context->compcode, context->blocksize, context->new_nthreads, context->nthreads, context->splitmode, context->tuner_id, context->tuner_params, context->schunk); if (error <= 0) { blosc2_free_ctx(context); return error; } memset(&header, 0, sizeof(header)); header.version = BLOSC2_VERSION_FORMAT_STABLE; header.versionlz = BLOSC_BLOSCLZ_VERSION_FORMAT; header.flags = BLOSC_DOSHUFFLE | BLOSC_DOBITSHUFFLE; // extended header header.typesize = context->typesize; header.nbytes = (int32_t)nbytes; header.blocksize = context->blocksize; header.cbytes = BLOSC_EXTENDED_HEADER_LENGTH; header.blosc2_flags = BLOSC2_SPECIAL_UNINIT << 4; // mark chunk as uninitialized memcpy((uint8_t *)dest, &header, sizeof(header)); blosc2_free_ctx(context); return BLOSC_EXTENDED_HEADER_LENGTH; } /* Create a chunk made of nans */ int blosc2_chunk_nans(blosc2_cparams cparams, const int32_t nbytes, void* dest, int32_t destsize) { if (destsize < BLOSC_EXTENDED_HEADER_LENGTH) { BLOSC_TRACE_ERROR("dest buffer is not long enough"); return BLOSC2_ERROR_DATA; } if (nbytes % cparams.typesize) { BLOSC_TRACE_ERROR("nbytes must be a multiple of typesize"); return BLOSC2_ERROR_DATA; } blosc_header header; blosc2_context* context = blosc2_create_cctx(cparams); if (context == NULL) { BLOSC_TRACE_ERROR("Error while creating the compression context"); return BLOSC2_ERROR_NULL_POINTER; } int error = initialize_context_compression( context, NULL, nbytes, dest, destsize, context->clevel, context->filters, context->filters_meta, context->typesize, context->compcode, context->blocksize, context->new_nthreads, context->nthreads, context->splitmode, context->tuner_id, context->tuner_params, context->schunk); if (error <= 0) { blosc2_free_ctx(context); return error; } memset(&header, 0, sizeof(header)); header.version = BLOSC2_VERSION_FORMAT_STABLE; header.versionlz = BLOSC_BLOSCLZ_VERSION_FORMAT; header.flags = BLOSC_DOSHUFFLE | BLOSC_DOBITSHUFFLE; // extended header header.typesize = context->typesize; header.nbytes = (int32_t)nbytes; header.blocksize = context->blocksize; header.cbytes = BLOSC_EXTENDED_HEADER_LENGTH; header.blosc2_flags = BLOSC2_SPECIAL_NAN << 4; // mark chunk as all NaNs memcpy((uint8_t *)dest, &header, sizeof(header)); blosc2_free_ctx(context); return BLOSC_EXTENDED_HEADER_LENGTH; } /* Create a chunk made of repeated values */ int blosc2_chunk_repeatval(blosc2_cparams cparams, const int32_t nbytes, void* dest, int32_t destsize, const void* repeatval) { if (destsize < BLOSC_EXTENDED_HEADER_LENGTH + cparams.typesize) { BLOSC_TRACE_ERROR("dest buffer is not long enough"); return BLOSC2_ERROR_DATA; } if (nbytes % cparams.typesize) { BLOSC_TRACE_ERROR("nbytes must be a multiple of typesize"); return BLOSC2_ERROR_DATA; } blosc_header header; blosc2_context* context = blosc2_create_cctx(cparams); if (context == NULL) { BLOSC_TRACE_ERROR("Error while creating the compression context"); return BLOSC2_ERROR_NULL_POINTER; } int error = initialize_context_compression( context, NULL, nbytes, dest, destsize, context->clevel, context->filters, context->filters_meta, context->typesize, context->compcode, context->blocksize, context->new_nthreads, context->nthreads, context->splitmode, context->tuner_id, context->tuner_params, context->schunk); if (error <= 0) { blosc2_free_ctx(context); return error; } memset(&header, 0, sizeof(header)); header.version = BLOSC2_VERSION_FORMAT_STABLE; header.versionlz = BLOSC_BLOSCLZ_VERSION_FORMAT; header.flags = BLOSC_DOSHUFFLE | BLOSC_DOBITSHUFFLE; // extended header header.typesize = context->typesize; header.nbytes = (int32_t)nbytes; header.blocksize = context->blocksize; header.cbytes = BLOSC_EXTENDED_HEADER_LENGTH + cparams.typesize; header.blosc2_flags = BLOSC2_SPECIAL_VALUE << 4; // mark chunk as all repeated value memcpy((uint8_t *)dest, &header, sizeof(header)); memcpy((uint8_t *)dest + sizeof(header), repeatval, cparams.typesize); blosc2_free_ctx(context); return BLOSC_EXTENDED_HEADER_LENGTH + cparams.typesize; } /* Register filters */ int register_filter_private(blosc2_filter *filter) { BLOSC_ERROR_NULL(filter, BLOSC2_ERROR_INVALID_PARAM); if (g_nfilters == UINT8_MAX) { BLOSC_TRACE_ERROR("Can not register more filters"); return BLOSC2_ERROR_CODEC_SUPPORT; } if (filter->id < BLOSC2_GLOBAL_REGISTERED_FILTERS_START) { BLOSC_TRACE_ERROR("The id must be greater or equal than %d", BLOSC2_GLOBAL_REGISTERED_FILTERS_START); return BLOSC2_ERROR_FAILURE; } /* This condition can never be fulfilled if (filter->id > BLOSC2_USER_REGISTERED_FILTERS_STOP) { BLOSC_TRACE_ERROR("The id must be less than or equal to %d", BLOSC2_USER_REGISTERED_FILTERS_STOP); return BLOSC2_ERROR_FAILURE; } */ for (uint64_t i = 0; i < g_nfilters; ++i) { if (g_filters[i].id == filter->id) { if (strcmp(g_filters[i].name, filter->name) != 0) { BLOSC_TRACE_ERROR("The filter (ID: %d) plugin is already registered with name: %s." " Choose another one !", filter->id, g_filters[i].name); return BLOSC2_ERROR_FAILURE; } else { // Already registered, so no more actions needed return BLOSC2_ERROR_SUCCESS; } } } blosc2_filter *filter_new = &g_filters[g_nfilters++]; memcpy(filter_new, filter, sizeof(blosc2_filter)); return BLOSC2_ERROR_SUCCESS; } int blosc2_register_filter(blosc2_filter *filter) { if (filter->id < BLOSC2_USER_REGISTERED_FILTERS_START) { BLOSC_TRACE_ERROR("The id must be greater or equal to %d", BLOSC2_USER_REGISTERED_FILTERS_START); return BLOSC2_ERROR_FAILURE; } return register_filter_private(filter); } /* Register codecs */ int register_codec_private(blosc2_codec *codec) { BLOSC_ERROR_NULL(codec, BLOSC2_ERROR_INVALID_PARAM); if (g_ncodecs == UINT8_MAX) { BLOSC_TRACE_ERROR("Can not register more codecs"); return BLOSC2_ERROR_CODEC_SUPPORT; } if (codec->compcode < BLOSC2_GLOBAL_REGISTERED_CODECS_START) { BLOSC_TRACE_ERROR("The id must be greater or equal than %d", BLOSC2_GLOBAL_REGISTERED_CODECS_START); return BLOSC2_ERROR_FAILURE; } /* This condition can never be fulfilled if (codec->compcode > BLOSC2_USER_REGISTERED_CODECS_STOP) { BLOSC_TRACE_ERROR("The id must be less or equal to %d", BLOSC2_USER_REGISTERED_CODECS_STOP); return BLOSC2_ERROR_FAILURE; } */ for (int i = 0; i < g_ncodecs; ++i) { if (g_codecs[i].compcode == codec->compcode) { if (strcmp(g_codecs[i].compname, codec->compname) != 0) { BLOSC_TRACE_ERROR("The codec (ID: %d) plugin is already registered with name: %s." " Choose another one !", codec->compcode, codec->compname); return BLOSC2_ERROR_CODEC_PARAM; } else { // Already registered, so no more actions needed return BLOSC2_ERROR_SUCCESS; } } } blosc2_codec *codec_new = &g_codecs[g_ncodecs++]; memcpy(codec_new, codec, sizeof(blosc2_codec)); return BLOSC2_ERROR_SUCCESS; } int blosc2_register_codec(blosc2_codec *codec) { if (codec->compcode < BLOSC2_USER_REGISTERED_CODECS_START) { BLOSC_TRACE_ERROR("The compcode must be greater or equal than %d", BLOSC2_USER_REGISTERED_CODECS_START); return BLOSC2_ERROR_CODEC_PARAM; } return register_codec_private(codec); } /* Register tuners */ int register_tuner_private(blosc2_tuner *tuner) { BLOSC_ERROR_NULL(tuner, BLOSC2_ERROR_INVALID_PARAM); if (g_ntuners == UINT8_MAX) { BLOSC_TRACE_ERROR("Can not register more tuners"); return BLOSC2_ERROR_CODEC_SUPPORT; } if (tuner->id < BLOSC2_GLOBAL_REGISTERED_TUNER_START) { BLOSC_TRACE_ERROR("The id must be greater or equal than %d", BLOSC2_GLOBAL_REGISTERED_TUNER_START); return BLOSC2_ERROR_FAILURE; } for (int i = 0; i < g_ntuners; ++i) { if (g_tuners[i].id == tuner->id) { if (strcmp(g_tuners[i].name, tuner->name) != 0) { BLOSC_TRACE_ERROR("The tuner (ID: %d) plugin is already registered with name: %s." " Choose another one !", tuner->id, g_tuners[i].name); return BLOSC2_ERROR_FAILURE; } else { // Already registered, so no more actions needed return BLOSC2_ERROR_SUCCESS; } } } blosc2_tuner *tuner_new = &g_tuners[g_ntuners++]; memcpy(tuner_new, tuner, sizeof(blosc2_tuner)); return BLOSC2_ERROR_SUCCESS; } int blosc2_register_tuner(blosc2_tuner *tuner) { if (tuner->id < BLOSC2_USER_REGISTERED_TUNER_START) { BLOSC_TRACE_ERROR("The id must be greater or equal to %d", BLOSC2_USER_REGISTERED_TUNER_START); return BLOSC2_ERROR_FAILURE; } return register_tuner_private(tuner); } int _blosc2_register_io_cb(const blosc2_io_cb *io) { for (uint64_t i = 0; i < g_nio; ++i) { if (g_ios[i].id == io->id) { if (strcmp(g_ios[i].name, io->name) != 0) { BLOSC_TRACE_ERROR("The IO (ID: %d) plugin is already registered with name: %s." " Choose another one !", io->id, g_ios[i].name); return BLOSC2_ERROR_PLUGIN_IO; } else { // Already registered, so no more actions needed return BLOSC2_ERROR_SUCCESS; } } } blosc2_io_cb *io_new = &g_ios[g_nio++]; memcpy(io_new, io, sizeof(blosc2_io_cb)); return BLOSC2_ERROR_SUCCESS; } int blosc2_register_io_cb(const blosc2_io_cb *io) { BLOSC_ERROR_NULL(io, BLOSC2_ERROR_INVALID_PARAM); if (g_nio == UINT8_MAX) { BLOSC_TRACE_ERROR("Can not register more codecs"); return BLOSC2_ERROR_PLUGIN_IO; } if (io->id < BLOSC2_IO_REGISTERED) { BLOSC_TRACE_ERROR("The compcode must be greater or equal than %d", BLOSC2_IO_REGISTERED); return BLOSC2_ERROR_PLUGIN_IO; } return _blosc2_register_io_cb(io); } blosc2_io_cb *blosc2_get_io_cb(uint8_t id) { // If g_initlib is not set by blosc2_init() this function will try to read // uninitialized memory. We should therefore always return NULL in that case if (!g_initlib) { return NULL; } for (uint64_t i = 0; i < g_nio; ++i) { if (g_ios[i].id == id) { return &g_ios[i]; } } if (id == BLOSC2_IO_FILESYSTEM) { if (_blosc2_register_io_cb(&BLOSC2_IO_CB_DEFAULTS) < 0) { BLOSC_TRACE_ERROR("Error registering the default IO API"); return NULL; } return blosc2_get_io_cb(id); } else if (id == BLOSC2_IO_FILESYSTEM_MMAP) { if (_blosc2_register_io_cb(&BLOSC2_IO_CB_MMAP) < 0) { BLOSC_TRACE_ERROR("Error registering the mmap IO API"); return NULL; } return blosc2_get_io_cb(id); } return NULL; } void blosc2_unidim_to_multidim(uint8_t ndim, int64_t *shape, int64_t i, int64_t *index) { if (ndim == 0) { return; } assert(ndim <= B2ND_MAX_DIM); int64_t strides[B2ND_MAX_DIM]; strides[ndim - 1] = 1; for (int j = ndim - 2; j >= 0; --j) { strides[j] = shape[j + 1] * strides[j + 1]; } index[0] = i / strides[0]; for (int j = 1; j < ndim; ++j) { index[j] = (i % strides[j - 1]) / strides[j]; } } void blosc2_multidim_to_unidim(const int64_t *index, int8_t ndim, const int64_t *strides, int64_t *i) { *i = 0; for (int j = 0; j < ndim; ++j) { *i += index[j] * strides[j]; } } int64_t blosc2_get_slice_nchunks(blosc2_schunk* schunk, int64_t *start, int64_t *stop, int64_t **chunks_idx) { BLOSC_ERROR_NULL(schunk, BLOSC2_ERROR_NULL_POINTER); if (blosc2_meta_exists(schunk, "b2nd") < 0) { // Try with a caterva metalayer; we are meant to be backward compatible with it if (blosc2_meta_exists(schunk, "caterva") < 0) { return schunk_get_slice_nchunks(schunk, *start, *stop, chunks_idx); } } b2nd_array_t *array; int rc = b2nd_from_schunk(schunk, &array); if (rc < 0) { BLOSC_TRACE_ERROR("Could not get b2nd array from schunk."); return rc; } int64_t nchunks = b2nd_get_slice_nchunks(array, start, stop, chunks_idx); array->sc = NULL; // Free only array struct b2nd_free(array); return nchunks; } blosc2_cparams blosc2_get_blosc2_cparams_defaults(void) { return BLOSC2_CPARAMS_DEFAULTS; }; blosc2_dparams blosc2_get_blosc2_dparams_defaults(void) { return BLOSC2_DPARAMS_DEFAULTS; }; blosc2_storage blosc2_get_blosc2_storage_defaults(void) { return BLOSC2_STORAGE_DEFAULTS; }; blosc2_io blosc2_get_blosc2_io_defaults(void) { return BLOSC2_IO_DEFAULTS; }; blosc2_stdio_mmap blosc2_get_blosc2_stdio_mmap_defaults(void) { return BLOSC2_STDIO_MMAP_DEFAULTS; }; const char *blosc2_error_string(int error_code) { switch (error_code) { case BLOSC2_ERROR_FAILURE: return "Generic failure"; case BLOSC2_ERROR_STREAM: return "Bad stream"; case BLOSC2_ERROR_DATA: return "Invalid data"; case BLOSC2_ERROR_MEMORY_ALLOC: return "Memory alloc/realloc failure"; case BLOSC2_ERROR_READ_BUFFER: return "Not enough space to read"; case BLOSC2_ERROR_WRITE_BUFFER: return "Not enough space to write"; case BLOSC2_ERROR_CODEC_SUPPORT: return "Codec not supported"; case BLOSC2_ERROR_CODEC_PARAM: return "Invalid parameter supplied to codec"; case BLOSC2_ERROR_CODEC_DICT: return "Codec dictionary error"; case BLOSC2_ERROR_VERSION_SUPPORT: return "Version not supported"; case BLOSC2_ERROR_INVALID_HEADER: return "Invalid value in header"; case BLOSC2_ERROR_INVALID_PARAM: return "Invalid parameter supplied to function"; case BLOSC2_ERROR_FILE_READ: return "File read failure"; case BLOSC2_ERROR_FILE_WRITE: return "File write failure"; case BLOSC2_ERROR_FILE_OPEN: return "File open failure"; case BLOSC2_ERROR_NOT_FOUND: return "Not found"; case BLOSC2_ERROR_RUN_LENGTH: return "Bad run length encoding"; case BLOSC2_ERROR_FILTER_PIPELINE: return "Filter pipeline error"; case BLOSC2_ERROR_CHUNK_INSERT: return "Chunk insert failure"; case BLOSC2_ERROR_CHUNK_APPEND: return "Chunk append failure"; case BLOSC2_ERROR_CHUNK_UPDATE: return "Chunk update failure"; case BLOSC2_ERROR_2GB_LIMIT: return "Sizes larger than 2gb not supported"; case BLOSC2_ERROR_SCHUNK_COPY: return "Super-chunk copy failure"; case BLOSC2_ERROR_FRAME_TYPE: return "Wrong type for frame"; case BLOSC2_ERROR_FILE_TRUNCATE: return "File truncate failure"; case BLOSC2_ERROR_THREAD_CREATE: return "Thread or thread context creation failure"; case BLOSC2_ERROR_POSTFILTER: return "Postfilter failure"; case BLOSC2_ERROR_FRAME_SPECIAL: return "Special frame failure"; case BLOSC2_ERROR_SCHUNK_SPECIAL: return "Special super-chunk failure"; case BLOSC2_ERROR_PLUGIN_IO: return "IO plugin error"; case BLOSC2_ERROR_FILE_REMOVE: return "Remove file failure"; case BLOSC2_ERROR_NULL_POINTER: return "Pointer is null"; case BLOSC2_ERROR_INVALID_INDEX: return "Invalid index"; case BLOSC2_ERROR_METALAYER_NOT_FOUND: return "Metalayer has not been found"; case BLOSC2_ERROR_MAX_BUFSIZE_EXCEEDED: return "Maximum buffersize exceeded"; case BLOSC2_ERROR_TUNER: return "Tuner failure"; default: return "Unknown error"; } } c-blosc2-3.1.5/blosc/blosclz.c000066400000000000000000000575371521743436100160710ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /********************************************************************* The code in this file is heavily based on FastLZ, a lightning-fast lossless compression library. See LICENSES/FASTLZ.txt for details. **********************************************************************/ #include "blosclz.h" #include "fastcopy.h" #include "blosc2/blosc2-common.h" #include #include #include #include /* * Give hints to the compiler for branch prediction optimization. * This is not necessary anymore with modern CPUs. */ #if 0 && defined(__GNUC__) && (__GNUC__ > 2) #define BLOSCLZ_LIKELY(c) (__builtin_expect((c), 1)) #define BLOSCLZ_UNLIKELY(c) (__builtin_expect((c), 0)) #else #define BLOSCLZ_LIKELY(c) (c) #define BLOSCLZ_UNLIKELY(c) (c) #endif /* * Use inlined functions for supported systems. */ #if defined(_MSC_VER) && !defined(__cplusplus) /* Visual Studio */ #define inline __inline /* Visual C is not C99, but supports some kind of inline */ #endif #define MAX_COPY 32U #define MAX_DISTANCE 8191 #define MAX_FARDISTANCE (65535 + MAX_DISTANCE - 1) #ifdef BLOSC_STRICT_ALIGN #define BLOSCLZ_READU16(p) ((p)[0] | (p)[1]<<8) #define BLOSCLZ_READU32(p) ((p)[0] | (p)[1]<<8 | (p)[2]<<16 | (p)[3]<<24) #else #define BLOSCLZ_READU16(p) *((const uint16_t*)(p)) #define BLOSCLZ_READU32(p) *((const uint32_t*)(p)) #endif #define HASH_LOG (14U) // This is used in LZ4 and seems to work pretty well here too #define HASH_FUNCTION(v, s, h) { \ (v) = ((s) * 2654435761U) >> (32U - (h)); \ } #if defined(__AVX2__) static uint8_t *get_run_32(uint8_t *ip, const uint8_t *ip_bound, const uint8_t *ref) { uint8_t x = ip[-1]; while (ip < (ip_bound - (sizeof(__m256i)))) { __m256i value, value2, cmp; /* Broadcast the value for every byte in a 256-bit register */ memset(&value, x, sizeof(__m256i)); value2 = _mm256_loadu_si256((__m256i *)ref); cmp = _mm256_cmpeq_epi64(value, value2); if ((unsigned)_mm256_movemask_epi8(cmp) != 0xFFFFFFFF) { /* Return the byte that starts to differ */ while (*ref++ == x) ip++; return ip; } else { ip += sizeof(__m256i); ref += sizeof(__m256i); } } /* Look into the remainder */ while ((ip < ip_bound) && (*ref++ == x)) ip++; return ip; } #endif #if defined(__SSE2__) uint8_t *get_run_16(uint8_t *ip, const uint8_t *ip_bound, const uint8_t *ref) { uint8_t x = ip[-1]; while (ip < (ip_bound - sizeof(__m128i))) { __m128i value, value2, cmp; /* Broadcast the value for every byte in a 128-bit register */ memset(&value, x, sizeof(__m128i)); value2 = _mm_loadu_si128((__m128i *)ref); cmp = _mm_cmpeq_epi32(value, value2); if (_mm_movemask_epi8(cmp) != 0xFFFF) { /* Return the byte that starts to differ */ while (*ref++ == x) ip++; return ip; } else { ip += sizeof(__m128i); ref += sizeof(__m128i); } } /* Look into the remainder */ while ((ip < ip_bound) && (*ref++ == x)) ip++; return ip; } #endif static uint8_t *get_run(uint8_t *ip, const uint8_t *ip_bound, const uint8_t *ref) { uint8_t x = ip[-1]; int64_t value, value2; /* Broadcast the value for every byte in a 64-bit register */ memset(&value, x, 8); /* safe because the outer check against ip limit */ while (ip < (ip_bound - sizeof(int64_t))) { #if defined(BLOSC_STRICT_ALIGN) memcpy(&value2, ref, 8); #else value2 = ((int64_t*)ref)[0]; #endif if (value != value2) { /* Return the byte that starts to differ */ while (*ref++ == x) ip++; return ip; } else { ip += 8; ref += 8; } } /* Look into the remainder */ while ((ip < ip_bound) && (*ref++ == x)) ip++; return ip; } /* Return the byte that starts to differ */ uint8_t *get_match(uint8_t *ip, const uint8_t *ip_bound, const uint8_t *ref) { #if !defined(BLOSC_STRICT_ALIGN) while (ip < (ip_bound - sizeof(int64_t))) { if (*(int64_t*)ref != *(int64_t*)ip) { /* Return the byte that starts to differ */ while (*ref++ == *ip++) {} return ip; } else { ip += sizeof(int64_t); ref += sizeof(int64_t); } } #endif /* Look into the remainder */ while ((ip < ip_bound) && (*ref++ == *ip++)) {} return ip; } #if defined(__SSE2__) static uint8_t *get_match_16(uint8_t *ip, const uint8_t *ip_bound, const uint8_t *ref) { __m128i value, value2, cmp; while (ip < (ip_bound - sizeof(__m128i))) { value = _mm_loadu_si128((__m128i *) ip); value2 = _mm_loadu_si128((__m128i *) ref); cmp = _mm_cmpeq_epi32(value, value2); if (_mm_movemask_epi8(cmp) != 0xFFFF) { /* Return the byte that starts to differ */ while (*ref++ == *ip++) {} return ip; } else { ip += sizeof(__m128i); ref += sizeof(__m128i); } } /* Look into the remainder */ while ((ip < ip_bound) && (*ref++ == *ip++)) {} return ip; } #endif #if defined(__AVX2__) static uint8_t *get_match_32(uint8_t *ip, const uint8_t *ip_bound, const uint8_t *ref) { while (ip < (ip_bound - sizeof(__m256i))) { __m256i value, value2, cmp; value = _mm256_loadu_si256((__m256i *) ip); value2 = _mm256_loadu_si256((__m256i *)ref); cmp = _mm256_cmpeq_epi64(value, value2); if ((unsigned)_mm256_movemask_epi8(cmp) != 0xFFFFFFFF) { /* Return the byte that starts to differ */ while (*ref++ == *ip++) {} return ip; } else { ip += sizeof(__m256i); ref += sizeof(__m256i); } } /* Look into the remainder */ while ((ip < ip_bound) && (*ref++ == *ip++)) {} return ip; } #endif static uint8_t* get_run_or_match(uint8_t* ip, uint8_t* ip_bound, const uint8_t* ref, bool run) { if (BLOSCLZ_UNLIKELY(run)) { #if defined(__AVX2__) // Extensive experiments on AMD Ryzen3 say that regular get_run is faster // ip = get_run_32(ip, ip_bound, ref); ip = get_run(ip, ip_bound, ref); #elif defined(__SSE2__) // Extensive experiments on AMD Ryzen3 say that regular get_run is faster // ip = get_run_16(ip, ip_bound, ref); ip = get_run(ip, ip_bound, ref); #else ip = get_run(ip, ip_bound, ref); #endif } else { #if defined(__AVX2__) // Extensive experiments on AMD Ryzen3 say that regular get_match_16 is faster // ip = get_match_32(ip, ip_bound, ref); ip = get_match_16(ip, ip_bound, ref); #elif defined(__SSE2__) ip = get_match_16(ip, ip_bound, ref); #else ip = get_match(ip, ip_bound, ref); #endif } return ip; } #define LITERAL(ip, op, op_limit, anchor, copy) { \ if (BLOSCLZ_UNLIKELY((op) + 2 > (op_limit))) \ goto out; \ *(op)++ = *(anchor)++; \ (ip) = (anchor); \ (copy)++; \ if (BLOSCLZ_UNLIKELY((copy) == MAX_COPY)) { \ (copy) = 0; \ *(op)++ = MAX_COPY-1; \ } \ } #define LITERAL2(ip, anchor, copy) { \ oc++; (anchor)++; \ (ip) = (anchor); \ (copy)++; \ if (BLOSCLZ_UNLIKELY((copy) == MAX_COPY)) { \ (copy) = 0; \ oc++; \ } \ } #define MATCH_SHORT(op, op_limit, len, distance) { \ if (BLOSCLZ_UNLIKELY((op) + 2 > (op_limit))) \ goto out; \ *(op)++ = (uint8_t)(((len) << 5U) + ((distance) >> 8U));\ *(op)++ = (uint8_t)(((distance) & 255U)); \ } #define MATCH_LONG(op, op_limit, len, distance) { \ if (BLOSCLZ_UNLIKELY((op) + 1 > (op_limit))) \ goto out; \ *(op)++ = (uint8_t)((7U << 5U) + ((distance) >> 8U)); \ for ((len) -= 7; (len) >= 255; (len) -= 255) { \ if (BLOSCLZ_UNLIKELY((op) + 1 > (op_limit))) \ goto out; \ *(op)++ = 255; \ } \ if (BLOSCLZ_UNLIKELY((op) + 2 > (op_limit))) \ goto out; \ *(op)++ = (uint8_t)(len); \ *(op)++ = (uint8_t)(((distance) & 255U)); \ } #define MATCH_SHORT_FAR(op, op_limit, len, distance) { \ if (BLOSCLZ_UNLIKELY((op) + 4 > (op_limit))) \ goto out; \ *(op)++ = (uint8_t)(((len) << 5U) + 31); \ *(op)++ = 255; \ *(op)++ = (uint8_t)((distance) >> 8U); \ *(op)++ = (uint8_t)((distance) & 255U); \ } #define MATCH_LONG_FAR(op, op_limit, len, distance) { \ if (BLOSCLZ_UNLIKELY((op) + 1 > (op_limit))) \ goto out; \ *(op)++ = (7U << 5U) + 31; \ for ((len) -= 7; (len) >= 255; (len) -= 255) { \ if (BLOSCLZ_UNLIKELY((op) + 1 > (op_limit))) \ goto out; \ *(op)++ = 255; \ } \ if (BLOSCLZ_UNLIKELY((op) + 4 > (op_limit))) \ goto out; \ *(op)++ = (uint8_t)(len); \ *(op)++ = 255; \ *(op)++ = (uint8_t)((distance) >> 8U); \ *(op)++ = (uint8_t)((distance) & 255U); \ } // Get a guess for the compressed size of a buffer static double get_cratio(uint8_t* ibase, int maxlen, int minlen, int ipshift, uint32_t htab[], int8_t hashlog) { uint8_t* ip = ibase; int32_t oc = 0; const uint16_t hashlen = (1U << (uint8_t)hashlog); uint32_t hval; uint32_t seq; uint8_t copy; // Make a tradeoff between testing too much and too little uint16_t limit = (maxlen > hashlen) ? hashlen : maxlen; uint8_t* ip_bound = ibase + limit - 1; uint8_t* ip_limit = ibase + limit - 12; // Initialize the hash table to distances of 0 memset(htab, 0, hashlen * sizeof(uint32_t)); /* we start with literal copy */ copy = 4; oc += 5; /* main loop */ while (BLOSCLZ_LIKELY(ip < ip_limit)) { const uint8_t* ref; unsigned distance; uint8_t* anchor = ip; /* comparison starting-point */ /* find potential match */ seq = BLOSCLZ_READU32(ip); HASH_FUNCTION(hval, seq, hashlog) ref = ibase + htab[hval]; /* calculate distance to the match */ distance = (unsigned int)(anchor - ref); /* update hash table */ htab[hval] = (uint32_t) (anchor - ibase); if (distance == 0 || (distance >= MAX_FARDISTANCE)) { LITERAL2(ip, anchor, copy) continue; } /* is this a match? check the first 4 bytes */ if (BLOSCLZ_READU32(ref) == BLOSCLZ_READU32(ip)) { ref += 4; } else { /* no luck, copy as a literal */ LITERAL2(ip, anchor, copy) continue; } /* last matched byte */ ip = anchor + 4; /* distance is biased */ distance--; /* get runs or matches; zero distance means a run */ ip = get_run_or_match(ip, ip_bound, ref, !distance); ip -= ipshift; int len = (int)(ip - anchor); if (len < minlen) { LITERAL2(ip, anchor, copy) continue; } /* if we haven't copied anything, adjust the output counter */ if (!copy) oc--; /* reset literal counter */ copy = 0; /* encode the match */ if (distance < MAX_DISTANCE) { if (len >= 7) { oc += ((len - 7) / 255) + 1; } oc += 2; } else { /* far away, but not yet in the another galaxy... */ if (len >= 7) { oc += ((len - 7) / 255) + 1; } oc += 4; } /* update the hash at match boundary */ seq = BLOSCLZ_READU32(ip); HASH_FUNCTION(hval, seq, hashlog) htab[hval] = (uint32_t)(ip++ - ibase); ip++; /* assuming literal copy */ oc++; } double ic = (double)(ip - ibase); return ic / (double)oc; } int blosclz_compress(const int clevel, const void* input, int length, void* output, int maxout, blosc2_context* ctx) { BLOSC_UNUSED_PARAM(ctx); uint8_t* ibase = (uint8_t*)input; uint32_t htab[1U << (uint8_t)HASH_LOG]; /* When we go back in a match (shift), we obtain quite different compression properties. * It looks like 4 is more useful in combination with bitshuffle and small typesizes * Fallback to 4 because it provides more consistent results for large cratios. * UPDATE: new experiments show that using a value of 3 is a bit better, at least for ERA5. * UPDATE 2: go back to 4, as they seem to provide better cratios in general. * * In this block we also check cratios for the beginning of the buffers and * eventually discard those that are small (take too long to decompress). * This process is called _entropy probing_. */ unsigned ipshift = 4; // Minimum lengths for encoding (normally it is good to match the shift value) unsigned minlen = 4; uint8_t hashlog_[10] = {0, HASH_LOG - 2, HASH_LOG - 1, HASH_LOG, HASH_LOG, HASH_LOG, HASH_LOG, HASH_LOG, HASH_LOG, HASH_LOG}; uint8_t hashlog = hashlog_[clevel]; // Experiments say that checking 1/4 of the buffer is enough to figure out approx cratio // UPDATE: new experiments with ERA5 datasets (float32) say that checking the whole buffer // is better (specially when combined with bitshuffle). // The loss in speed for checking the whole buffer is pretty negligible too. int maxlen = length; if (clevel < 2) { maxlen /= 8; } else if (clevel < 4) { maxlen /= 4; } else if (clevel < 7) { maxlen /= 2; } // Start probing somewhere inside the buffer int shift = length - maxlen; // Actual entropy probing! double cratio = get_cratio(ibase + shift, maxlen, minlen, ipshift, htab, hashlog); // discard probes with small compression ratios (too expensive) double cratio_[10] = {0, 2, 1.5, 1.2, 1.2, 1.2, 1.2, 1.15, 1.1, 1.0}; if (cratio < cratio_[clevel]) { goto out; } uint8_t* ip = ibase; uint8_t* ip_bound = ibase + length - 1; uint8_t* ip_limit = ibase + length - 12; uint8_t* op = (uint8_t*)output; const uint8_t* op_limit = op + maxout; uint32_t seq; uint8_t copy; uint32_t hval; /* input and output buffer cannot be less than 16 and 66 bytes or we can get into trouble */ if (length < 16 || maxout < 66) { return 0; } // Initialize the hash table memset(htab, 0, (1U << hashlog) * sizeof(uint32_t)); /* we start with literal copy */ copy = 4; *op++ = MAX_COPY - 1; *op++ = *ip++; *op++ = *ip++; *op++ = *ip++; *op++ = *ip++; /* main loop */ while (BLOSCLZ_LIKELY(ip < ip_limit)) { const uint8_t* ref; unsigned distance; uint8_t* anchor = ip; /* comparison starting-point */ /* find potential match */ seq = BLOSCLZ_READU32(ip); HASH_FUNCTION(hval, seq, hashlog) ref = ibase + htab[hval]; /* calculate distance to the match */ distance = (unsigned int)(anchor - ref); /* update hash table */ htab[hval] = (uint32_t) (anchor - ibase); if (distance == 0 || (distance >= MAX_FARDISTANCE)) { LITERAL(ip, op, op_limit, anchor, copy) continue; } /* is this a match? check the first 4 bytes */ if (BLOSCLZ_UNLIKELY(BLOSCLZ_READU32(ref) == BLOSCLZ_READU32(ip))) { ref += 4; } else { /* no luck, copy as a literal */ LITERAL(ip, op, op_limit, anchor, copy) continue; } /* last matched byte */ ip = anchor + 4; /* distance is biased */ distance--; /* get runs or matches; zero distance means a run */ ip = get_run_or_match(ip, ip_bound, ref, !distance); /* length is biased, '1' means a match of 3 bytes */ ip -= ipshift; unsigned len = (int)(ip - anchor); // Encoding short lengths is expensive during decompression if (len < minlen || (len <= 5 && distance >= MAX_DISTANCE)) { LITERAL(ip, op, op_limit, anchor, copy) continue; } /* if we have copied something, adjust the copy count */ if (copy) /* copy is biased, '0' means 1 byte copy */ *(op - copy - 1) = (uint8_t)(copy - 1); else /* back, to overwrite the copy count */ op--; /* reset literal counter */ copy = 0; /* encode the match */ if (distance < MAX_DISTANCE) { if (len < 7) { MATCH_SHORT(op, op_limit, len, distance) } else { MATCH_LONG(op, op_limit, len, distance) } } else { /* far away, but not yet in the another galaxy... */ distance -= MAX_DISTANCE; if (len < 7) { MATCH_SHORT_FAR(op, op_limit, len, distance) } else { MATCH_LONG_FAR(op, op_limit, len, distance) } } /* update the hash at match boundary */ seq = BLOSCLZ_READU32(ip); HASH_FUNCTION(hval, seq, hashlog) htab[hval] = (uint32_t) (ip++ - ibase); if (clevel == 9) { // In some situations, including a second hash proves to be useful, // but not in others. Activating here in max clevel only. seq >>= 8U; HASH_FUNCTION(hval, seq, hashlog) htab[hval] = (uint32_t) (ip++ - ibase); } else { ip++; } if (BLOSCLZ_UNLIKELY(op + 1 > op_limit)) goto out; /* assuming literal copy */ *op++ = MAX_COPY - 1; } /* left-over as literal copy */ while (BLOSCLZ_UNLIKELY(ip <= ip_bound)) { if (BLOSCLZ_UNLIKELY(op + 2 > op_limit)) goto out; *op++ = *ip++; copy++; if (BLOSCLZ_UNLIKELY(copy == MAX_COPY)) { copy = 0; *op++ = MAX_COPY - 1; } } /* if we have copied something, adjust the copy length */ if (copy) *(op - copy - 1) = (uint8_t)(copy - 1); else op--; /* marker for blosclz */ *(uint8_t*)output |= (1U << 5U); return (int)(op - (uint8_t*)output); out: return 0; } // See https://habr.com/en/company/yandex/blog/457612/ #if defined(__AVX2__) #if defined(_MSC_VER) #define ALIGNED_(x) __declspec(align(x)) #else #if defined(__GNUC__) #define ALIGNED_(x) __attribute__ ((aligned(x))) #endif #endif #define ALIGNED_TYPE_(t, x) t ALIGNED_(x) static unsigned char* copy_match_16(unsigned char *op, const unsigned char *match, int32_t len) { size_t offset = op - match; while (len >= 16) { static const ALIGNED_TYPE_(uint8_t, 16) masks[] = { 0, 1, 2, 1, 4, 1, 4, 2, 8, 7, 6, 5, 4, 3, 2, 1, // offset = 0, not used as mask, but for shift 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // offset = 1 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 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, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // offset = 16 }; _mm_storeu_si128((__m128i *)(op), _mm_shuffle_epi8(_mm_loadu_si128((const __m128i *)(match)), _mm_load_si128((const __m128i *)(masks) + offset))); match += masks[offset]; op += 16; len -= 16; } // Deal with remainders for (; len > 0; len--) { *op++ = *match++; } return op; } #endif // LZ4 wildCopy which can reach excellent copy bandwidth (even if insecure) static inline void wild_copy(uint8_t *out, const uint8_t* from, uint8_t* end) { uint8_t* d = out; const uint8_t* s = from; uint8_t* const e = end; do { memcpy(d,s,8); d+=8; s+=8; } while (d= 32) { // match int32_t len = (int32_t)(ctrl >> 5U) - 1 ; int32_t ofs = (int32_t)(ctrl & 31U) << 8U; uint8_t code; const uint8_t* ref = op - ofs; if (len == 7 - 1) { do { if (BLOSCLZ_UNLIKELY(ip + 1 >= ip_limit)) { return 0; } code = *ip++; len += code; } while (code == 255); } else { if (BLOSCLZ_UNLIKELY(ip + 1 >= ip_limit)) { return 0; } } code = *ip++; len += 3; ref -= code; /* match from 16-bit distance */ if (BLOSCLZ_UNLIKELY(code == 255)) { if (ofs == (31U << 8U)) { if (ip + 1 >= ip_limit) { return 0; } ofs = (*ip++) << 8U; ofs += *ip++; ref = op - ofs - MAX_DISTANCE; } } if (BLOSCLZ_UNLIKELY(op + len > op_limit)) { return 0; } if (BLOSCLZ_UNLIKELY(ref - 1 < (uint8_t*)output)) { return 0; } if (BLOSCLZ_UNLIKELY(ip >= ip_limit)) break; ctrl = *ip++; ref--; if (ref == op - 1) { /* optimized copy for a run */ memset(op, *ref, len); op += len; } else if ((op - ref >= 8) && (op_limit - op >= len + 8)) { // copy with an overlap not larger than 8 wild_copy(op, ref, op + len); op += len; } else { // general copy with any overlap #if 0 && defined(__AVX2__) if (op - ref <= 16) { // This is not faster on a combination of compilers (clang, gcc, icc) or machines, but // it is not too slower either. op = copy_match_16(op, ref, len); } else { #endif op = copy_match(op, ref, (unsigned) len); #if 0 && defined(__AVX2__) } #endif } } else { // literal ctrl++; if (BLOSCLZ_UNLIKELY(op + ctrl > op_limit)) { return 0; } if (BLOSCLZ_UNLIKELY(ip + ctrl > ip_limit)) { return 0; } memcpy(op, ip, ctrl); op += ctrl; ip += ctrl; // On GCC-6, fastcopy this is still faster than plain memcpy // However, using recent CLANG/LLVM 9.0, there is almost no difference // in performance. // And starting on CLANG/LLVM 10 and GCC 9, memcpy is generally faster. // op = fastcopy(op, ip, (unsigned) ctrl); ip += ctrl; if (BLOSCLZ_UNLIKELY(ip >= ip_limit)) break; ctrl = *ip++; } } return (int)(op - (uint8_t*)output); } c-blosc2-3.1.5/blosc/blosclz.h000066400000000000000000000042651521743436100160640ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /********************************************************************* The code in this file is heavily based on FastLZ, a lightning-fast lossless compression library. See LICENSES/FASTLZ.txt for details about copyright and rights to use. **********************************************************************/ #ifndef BLOSC_BLOSCLZ_H #define BLOSC_BLOSCLZ_H #include "context.h" #define BLOSCLZ_VERSION_STRING "2.5.3" /** Compress a block of data in the input buffer and returns the size of compressed block. The size of input buffer is specified by length. The minimum input buffer size is 16. The output buffer must be at least 5% larger than the input buffer and can not be smaller than 66 bytes. If the input is not compressible, or output does not fit in maxout bytes, the return value will be 0 and you will have to discard the output buffer. The acceleration parameter is related with the frequency for updating the internal hash. An acceleration of 1 means that the internal hash is updated at full rate. A value < 1 is not allowed and will be silently set to 1. The input buffer and the output buffer can not overlap. */ int blosclz_compress(int opt_level, const void* input, int length, void* output, int maxout, blosc2_context* ctx); /** Decompress a block of compressed data and returns the size of the decompressed block. If error occurs, e.g. the compressed data is corrupted or the output buffer is not large enough, then 0 (zero) will be returned instead. The input buffer and the output buffer can not overlap. Decompression is memory safe and guaranteed not to write the output buffer more than what is specified in maxout. */ int blosclz_decompress(const void* input, int length, void* output, int maxout); #endif /* BLOSC_BLOSCLZ_H */ c-blosc2-3.1.5/blosc/config.h.in000066400000000000000000000004731521743436100162630ustar00rootroot00000000000000#ifndef _CONFIGURATION_HEADER_GUARD_H_ #define _CONFIGURATION_HEADER_GUARD_H_ #cmakedefine HAVE_ZLIB @HAVE_ZLIB@ #cmakedefine HAVE_ZLIB_NG @HAVE_ZLIB_NG@ #cmakedefine HAVE_ZSTD @HAVE_ZSTD@ #cmakedefine HAVE_ZFP @HAVE_ZFP@ #cmakedefine BLOSC_DLL_EXPORT @DLL_EXPORT@ #cmakedefine HAVE_PLUGINS @HAVE_PLUGINS@ #endif c-blosc2-3.1.5/blosc/context.h000066400000000000000000000171671521743436100161050ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #ifndef BLOSC_CONTEXT_H #define BLOSC_CONTEXT_H #include "b2nd.h" #include "blosc2.h" #if defined(HAVE_ZSTD) #include "zstd.h" #endif #include #include #include /* Have problems using posix barriers when symbol value is 200112L */ /* Requires more investigation, but this will work for the moment */ #if defined(_POSIX_BARRIERS) && ( (_POSIX_BARRIERS - 20012L) >= 0 && _POSIX_BARRIERS != 200112L) #define BLOSC_POSIX_BARRIERS #endif #define BLOSC_BACKEND_SERIAL 0 #define BLOSC_BACKEND_SHARED_POOL 1 #define BLOSC_BACKEND_CALLBACK 2 #define BLOSC_BACKEND_PER_CONTEXT 3 /* per-context threads; used on Windows */ struct blosc2_context_s { const uint8_t* src; /* The source buffer */ uint8_t* dest; /* The destination buffer */ uint8_t header_flags; /* Flags for header */ uint8_t blosc2_flags; /* Flags specific for blosc2 */ uint8_t blosc2_flags2; /* Secondary flags specific for blosc2 */ int32_t sourcesize; /* Number of bytes in source buffer */ int32_t header_overhead; /* The number of bytes in chunk header */ int32_t nblocks; /* Number of total blocks in buffer */ int32_t leftover; /* Extra bytes at end of buffer */ int32_t blocksize; /* Length of the block in bytes */ int32_t header_blocksize; /* Raw blocksize field as stored in the chunk header */ int32_t splitmode; /* Whether the blocks should be split or not */ int32_t output_bytes; /* Counter for the number of input bytes */ int32_t srcsize; /* Counter for the number of output bytes */ int32_t destsize; /* Maximum size for destination buffer */ int32_t typesize; /* Type size */ int32_t* bstarts; /* Starts for every block inside the compressed buffer */ int32_t* blocknbytes; /* Uncompressed sizes for blocks in VL-block chunks */ int32_t* blockoffsets; /* Uncompressed offsets for blocks in VL-block chunks */ int32_t* blockcbytes; /* Compressed byte spans for blocks in VL-block chunks */ const uint8_t** vlblock_sources; /* Per-block sources when compressing VL-block chunks */ uint8_t** vlblock_dests; /* Per-block destinations when decompressing VL-block chunks */ int32_t special_type; /* Special type for chunk. 0 if not special. */ int compcode; /* Compressor code to use */ uint8_t compcode_meta; /* The metainfo for the compressor code */ int clevel; /* Compression level (1-9) */ int use_dict; /* Whether to use dicts or not */ void* dict_buffer; /* The buffer to keep the trained dictionary */ int32_t dict_size; /* The size of the trained dictionary */ void* dict_cdict; /* The dictionary in digested form for compression */ void* dict_ddict; /* The dictionary in digested form for decompression */ uint8_t filter_flags; /* The filter flags in the filter pipeline */ uint8_t filters[BLOSC2_MAX_FILTERS]; /* The (sequence of) filters */ uint8_t filters_meta[BLOSC2_MAX_FILTERS]; /* The metainfo for filters */ blosc2_filter urfilters[BLOSC2_MAX_UDFILTERS]; /* The user-defined filters */ blosc2_prefilter_fn prefilter; /* prefilter function */ blosc2_postfilter_fn postfilter; /* postfilter function */ blosc2_prefilter_params *preparams; /* prefilter params */ blosc2_postfilter_params *postparams; /* postfilter params */ bool* block_maskout; /* The blocks that are not meant to be decompressed. * If NULL (default), all blocks in a chunk should be read. */ int block_maskout_nitems; /* The number of items in block_maskout array (must match * the number of blocks in chunk) */ blosc2_schunk* schunk; /* Associated super-chunk (if available) */ struct thread_context* serial_context; /* Cache for temporaries for serial operation */ int do_compress; /* 1 if we are compressing, 0 if decompressing */ void *tuner_params; /* Entry point for tuner persistence between runs */ int tuner_id; /* User-defined tuner id */ void *codec_params; /* User defined parameters for the codec */ void *filter_params[BLOSC2_MAX_FILTERS]; /* User defined parameters for the filters */ /* Threading */ int16_t nthreads; int16_t new_nthreads; int16_t thread_backend; int16_t threads_started; struct thread_context *thread_contexts; /* Only for callback-managed threads */ struct blosc_shared_pool *thread_pool; int32_t pool_epoch; /* value of g_destroy_count when pool was attached */ struct blosc_job_group *job; blosc2_pthread_mutex_t count_mutex; blosc2_pthread_mutex_t nchunk_mutex; int thread_giveup_code; /* error code when give up */ int dref_not_init; /* data ref in delta not initialized */ blosc2_pthread_mutex_t delta_mutex; blosc2_pthread_cond_t delta_cv; bool dict_buffer_owned; /* Whether dict_buffer must be freed by the context */ /* Per-context worker threads (Windows only; BLOSC_BACKEND_PER_CONTEXT) */ int16_t end_threads; /* set to 1 to signal workers to exit */ uint32_t job_seq; /* incremented each new job dispatch */ int16_t active_workers; /* workers still processing current job */ int32_t thread_nblock; /* next block index for dynamic scheduling */ blosc2_pthread_t *threads; /* per-context thread handles */ blosc2_pthread_mutex_t jobs_mutex; /* guards job_seq, end_threads, active_workers */ blosc2_pthread_cond_t jobs_ready; /* workers sleep here between jobs */ blosc2_pthread_cond_t jobs_done; /* main sleeps here until job completes */ // Add new fields here to avoid breaking the ABI. }; struct b2nd_context_s { int8_t ndim; //!< The array dimensions. int64_t shape[B2ND_MAX_DIM]; //!< The array shape. int32_t chunkshape[B2ND_MAX_DIM]; //!< The shape of each chunk of Blosc. int32_t blockshape[B2ND_MAX_DIM]; //!< The shape of each block of Blosc. char *dtype; //!< Data type. Different formats can be supported (see dtype_format). int8_t dtype_format; //!< The format of the data type. Default is 0 (NumPy). blosc2_storage *b2_storage; //!< The Blosc storage properties blosc2_metalayer metalayers[B2ND_MAX_METALAYERS]; //!< List with the metalayers desired. int32_t nmetalayers; //!< The number of metalayers. }; struct thread_context { blosc2_context* parent_context; struct blosc_shared_pool* owner_pool; int tid; uint8_t* tmp; uint8_t* tmp2; uint8_t* tmp3; uint8_t* tmp4; int32_t tmp_blocksize; /* the blocksize for different temporaries */ size_t tmp_nbytes; /* keep track of how big the temporary buffers are */ int32_t zfp_cell_start; /* cell starter index for ZFP fixed-rate mode */ int32_t zfp_cell_nitems; /* number of items to get for ZFP fixed-rate mode */ #if defined(HAVE_ZSTD) /* The contexts for ZSTD */ ZSTD_CCtx* zstd_cctx; ZSTD_DCtx* zstd_dctx; #endif /* HAVE_ZSTD */ /* Working streams for LZ4/LZ4HC dictionary compression */ void* lz4_cstream; /* LZ4_stream_t* pre-loaded with dict; NULL when no dict active */ void* lz4hc_cstream; /* LZ4_streamHC_t* pre-loaded with dict; NULL when no dict active */ uint32_t my_job_seq; /* last job_seq processed; used by BLOSC_BACKEND_PER_CONTEXT on Windows */ }; static inline bool ctx_uses_parallel_backend(const blosc2_context *context) { return context != NULL && context->thread_backend != BLOSC_BACKEND_SERIAL && context->nthreads > 1; } #endif /* BLOSC_CONTEXT_H */ c-blosc2-3.1.5/blosc/delta.c000066400000000000000000000110401521743436100154650ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #include "delta.h" #include #include /* Apply the delta filters to src. This can never fail. */ void delta_encoder(const uint8_t* dref, int32_t offset, int32_t nbytes, int32_t typesize, const uint8_t* src, uint8_t* dest) { int32_t i; if (offset == 0) { /* This is the reference block, use delta coding in elements */ switch (typesize) { case 1: dest[0] = dref[0]; for (i = 1; i < nbytes; i++) { dest[i] = src[i] ^ dref[i-1]; } break; case 2: ((uint16_t *)dest)[0] = ((uint16_t *)dref)[0]; for (i = 1; i < nbytes / 2; i++) { ((uint16_t *)dest)[i] = ((uint16_t *)src)[i] ^ ((uint16_t *)dref)[i-1]; } break; case 4: ((uint32_t *)dest)[0] = ((uint32_t *)dref)[0]; for (i = 1; i < nbytes / 4; i++) { ((uint32_t *)dest)[i] = ((uint32_t *)src)[i] ^ ((uint32_t *)dref)[i-1]; } break; case 8: ((uint64_t *)dest)[0] = ((uint64_t *)dref)[0]; for (i = 1; i < nbytes / 8; i++) { ((uint64_t *)dest)[i] = ((uint64_t *)src)[i] ^ ((uint64_t *)dref)[i-1]; } break; default: if ((typesize % 8) == 0) { delta_encoder(dref, offset, nbytes, 8, src, dest); } else { delta_encoder(dref, offset, nbytes, 1, src, dest); } } } else { /* Use delta coding wrt reference block */ switch (typesize) { case 1: for (i = 0; i < nbytes; i++) { dest[i] = src[i] ^ dref[i]; } break; case 2: for (i = 0; i < nbytes / 2; i++) { ((uint16_t *) dest)[i] = ((uint16_t *) src)[i] ^ ((uint16_t *) dref)[i]; } break; case 4: for (i = 0; i < nbytes / 4; i++) { ((uint32_t *) dest)[i] = ((uint32_t *) src)[i] ^ ((uint32_t *) dref)[i]; } break; case 8: for (i = 0; i < nbytes / 8; i++) { ((uint64_t *) dest)[i] = ((uint64_t *) src)[i] ^ ((uint64_t *) dref)[i]; } break; default: if ((typesize % 8) == 0) { delta_encoder(dref, offset, nbytes, 8, src, dest); } else { delta_encoder(dref, offset, nbytes, 1, src, dest); } } } } /* Undo the delta filter in dest. This can never fail. */ void delta_decoder(const uint8_t* dref, int32_t offset, int32_t nbytes, int32_t typesize, uint8_t* dest) { int32_t i; if (offset == 0) { /* Decode delta for the reference block */ switch (typesize) { case 1: for (i = 1; i < nbytes; i++) { dest[i] ^= dref[i-1]; } break; case 2: for (i = 1; i < nbytes / 2; i++) { ((uint16_t *)dest)[i] ^= ((uint16_t *)dref)[i-1]; } break; case 4: for (i = 1; i < nbytes / 4; i++) { ((uint32_t *)dest)[i] ^= ((uint32_t *)dref)[i-1]; } break; case 8: for (i = 1; i < nbytes / 8; i++) { ((uint64_t *)dest)[i] ^= ((uint64_t *)dref)[i-1]; } break; default: if ((typesize % 8) == 0) { delta_decoder(dref, offset, nbytes, 8, dest); } else { delta_decoder(dref, offset, nbytes, 1, dest); } } } else { /* Decode delta for the non-reference blocks */ switch (typesize) { case 1: for (i = 0; i < nbytes; i++) { dest[i] ^= dref[i]; } break; case 2: for (i = 0; i < nbytes / 2; i++) { ((uint16_t *)dest)[i] ^= ((uint16_t *)dref)[i]; } break; case 4: for (i = 0; i < nbytes / 4; i++) { ((uint32_t *)dest)[i] ^= ((uint32_t *)dref)[i]; } break; case 8: for (i = 0; i < nbytes / 8; i++) { ((uint64_t *)dest)[i] ^= ((uint64_t *)dref)[i]; } break; default: if ((typesize % 8) == 0) { delta_decoder(dref, offset, nbytes, 8, dest); } else { delta_decoder(dref, offset, nbytes, 1, dest); } } } } c-blosc2-3.1.5/blosc/delta.h000066400000000000000000000013611521743436100154770ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #ifndef BLOSC_DELTA_H #define BLOSC_DELTA_H #include void delta_encoder(const uint8_t* dref, int32_t offset, int32_t nbytes, int32_t typesize, const uint8_t* src, uint8_t* dest); void delta_decoder(const uint8_t* dref, int32_t offset, int32_t nbytes, int32_t typesize, uint8_t* dest); #endif /* BLOSC_DELTA_H */ c-blosc2-3.1.5/blosc/directories.c000066400000000000000000000150701521743436100167170ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #include "blosc2.h" #include #include #include #include #include #include #if defined(_WIN32) || defined(__MINGW32__) #include #include #include #include /* <- add this for _rmdir */ int blosc2_remove_dir(const char* dir_path) { char* path; if (dir_path == NULL || dir_path[0] == '\0') { BLOSC_TRACE_ERROR("Invalid directory path"); return BLOSC2_ERROR_INVALID_PARAM; } size_t dir_len = strlen(dir_path); char last_char = dir_path[dir_len - 1]; size_t path_len = 0; if (last_char != '\\' && last_char != '/') { if (dir_len > SIZE_MAX - 3) { BLOSC_TRACE_ERROR("Directory path is too long"); return BLOSC2_ERROR_INVALID_PARAM; } path_len = dir_len + 3; path = malloc(path_len); if (path == NULL) { return BLOSC2_ERROR_MEMORY_ALLOC; } snprintf(path, path_len, "%s\\*", dir_path); } else { if (dir_len > SIZE_MAX - 2) { BLOSC_TRACE_ERROR("Directory path is too long"); return BLOSC2_ERROR_INVALID_PARAM; } path_len = dir_len + 2; path = malloc(path_len); if (path == NULL) { return BLOSC2_ERROR_MEMORY_ALLOC; } snprintf(path, path_len, "%s*", dir_path); } char* fname; struct _finddata_t cfile; intptr_t file = _findfirst(path, &cfile); free(path); if (file == -1) { BLOSC_TRACE_ERROR("Could not open the file."); return BLOSC2_ERROR_FILE_OPEN; } int ret; while ( _findnext(file, &cfile) == 0) { if (strcmp(".", cfile.name) == 0 || strcmp("..", cfile.name) == 0) { continue; } size_t name_len = strlen(cfile.name); if (dir_len > SIZE_MAX - name_len - 2) { BLOSC_TRACE_ERROR("File path is too long"); _findclose(file); return BLOSC2_ERROR_INVALID_PARAM; } fname = malloc(dir_len + name_len + 2); if (fname == NULL) { _findclose(file); return BLOSC2_ERROR_MEMORY_ALLOC; } snprintf(fname, dir_len + name_len + 2, "%s\\%s", dir_path, cfile.name); ret = remove(fname); if (ret < 0) { BLOSC_TRACE_ERROR("Could not remove file %s", fname); free(fname); _findclose(file); return BLOSC2_ERROR_FAILURE; } free(fname); } /* remove the directory */ if (_rmdir(dir_path) != 0) { BLOSC_TRACE_ERROR("Could not remove directory %s (errno=%d)", dir_path, errno); _findclose(file); return BLOSC2_ERROR_FAILURE; } _findclose(file); return BLOSC2_ERROR_SUCCESS; } #else #include #include /* Return the directory path with the '/' at the end */ char* blosc2_normalize_dirpath(const char* dir_path) { if (dir_path == NULL || dir_path[0] == '\0') { errno = EINVAL; return NULL; } size_t dir_len = strlen(dir_path); if (dir_len == 0) { errno = EINVAL; return NULL; } char last_char = dir_path[dir_len - 1]; char* path; if (last_char != '\\' && last_char != '/') { if (dir_len > SIZE_MAX - 2) { BLOSC_TRACE_ERROR("Directory path is too long"); return NULL; } path = malloc(dir_len + 2); if (path == NULL) { return NULL; } snprintf(path, dir_len + 2, "%s/", dir_path); } else { if (dir_len > SIZE_MAX - 1) { BLOSC_TRACE_ERROR("Directory path is too long"); return NULL; } path = malloc(dir_len + 1); if (path == NULL) { return NULL; } snprintf(path, dir_len + 1, "%s", dir_path); } return path; } /* Function needed for removing each time the directory */ int blosc2_remove_dir(const char* dir_path) { char* path = blosc2_normalize_dirpath(dir_path); if (path == NULL) { if (errno == ENOMEM) { return BLOSC2_ERROR_MEMORY_ALLOC; } return BLOSC2_ERROR_INVALID_PARAM; } DIR* dr = opendir(path); struct stat statbuf; if (dr == NULL) { BLOSC_TRACE_ERROR("No file or directory found."); free(path); return BLOSC2_ERROR_NOT_FOUND; } struct dirent *de; int ret; char* fname; while ((de = readdir(dr)) != NULL) { size_t path_len = strlen(path); size_t name_len = strlen(de->d_name); if (path_len > SIZE_MAX - name_len - 1) { BLOSC_TRACE_ERROR("File path is too long"); closedir(dr); free(path); return BLOSC2_ERROR_INVALID_PARAM; } fname = malloc(path_len + name_len + 1); if (fname == NULL) { closedir(dr); free(path); return BLOSC2_ERROR_MEMORY_ALLOC; } snprintf(fname, path_len + name_len + 1, "%s%s", path, de->d_name); if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) { free(fname); continue; } if (!stat(fname, &statbuf)) { ret = unlink(fname); if (ret < 0) { BLOSC_TRACE_ERROR("Could not remove file %s", fname); free(fname); closedir(dr); free(path); return BLOSC2_ERROR_FAILURE; } } free(fname); } closedir(dr); rmdir(path); free(path); return BLOSC2_ERROR_SUCCESS; } #endif /* _WIN32 */ int blosc2_remove_urlpath(const char* urlpath){ if (urlpath != NULL) { struct stat statbuf; if (stat(urlpath, &statbuf) != 0){ if (errno == ENOENT) { // Path does not exist return BLOSC2_ERROR_SUCCESS; } BLOSC_TRACE_ERROR("Could not access %s", urlpath); return BLOSC2_ERROR_FAILURE; } if ((statbuf.st_mode & S_IFDIR) != 0) { return blosc2_remove_dir(urlpath); } if (remove(urlpath) < 0) { BLOSC_TRACE_ERROR("Could not remove %s", urlpath); return BLOSC2_ERROR_FILE_REMOVE; } } return BLOSC2_ERROR_SUCCESS; } int blosc2_rename_urlpath(char* old_urlpath, char* new_urlpath){ if (old_urlpath != NULL && new_urlpath != NULL) { struct stat statbuf; if (stat(old_urlpath, &statbuf) != 0) { BLOSC_TRACE_ERROR("Could not access %s", old_urlpath); return BLOSC2_ERROR_FAILURE; } int ret = rename(old_urlpath, new_urlpath); if (ret < 0) { BLOSC_TRACE_ERROR("Could not rename %s to %s", old_urlpath, new_urlpath); return BLOSC2_ERROR_FAILURE; } } return BLOSC2_ERROR_SUCCESS; } c-blosc2-3.1.5/blosc/fastcopy.c000066400000000000000000000405671521743436100162440ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /********************************************************************* The code in this file is heavily based on memcopy.h, from the zlib-ng compression library. See LICENSES/ZLIB.txt for details. See also: https://github.com/Dead2/zlib-ng/blob/develop/zlib.h New implementations by Francesc Alted: * fast_copy() and copy_run() functions * Support for SSE2/AVX2 copy instructions for these routines **********************************************************************/ #include "blosc2/blosc2-common.h" #include #include #if defined(BLOSC_STRICT_ALIGN) #include #endif /* * Use inlined functions for supported systems. */ #if defined(_MSC_VER) && !defined(__cplusplus) /* Visual Studio */ #define inline __inline /* Visual C is not C99, but supports some kind of inline */ #endif static inline unsigned char *copy_2_bytes(unsigned char *out, const unsigned char *from) { #if defined(BLOSC_STRICT_ALIGN) uint16_t chunk; memcpy(&chunk, from, 2); memcpy(out, &chunk, 2); #else *(uint16_t *) out = *(uint16_t *) from; #endif return out + 2; } static inline unsigned char *copy_4_bytes(unsigned char *out, const unsigned char *from) { #if defined(BLOSC_STRICT_ALIGN) uint32_t chunk; memcpy(&chunk, from, 4); memcpy(out, &chunk, 4); #else *(uint32_t *) out = *(uint32_t *) from; #endif return out + 4; } #if !defined(BLOSC_STRICT_ALIGN) static inline unsigned char *copy_1_bytes(unsigned char *out, const unsigned char *from) { *out++ = *from; return out; } static inline unsigned char *copy_3_bytes(unsigned char *out, const unsigned char *from) { out = copy_1_bytes(out, from); return copy_2_bytes(out, from + 1); } static inline unsigned char *copy_5_bytes(unsigned char *out, const unsigned char *from) { out = copy_1_bytes(out, from); return copy_4_bytes(out, from + 1); } static inline unsigned char *copy_6_bytes(unsigned char *out, const unsigned char *from) { out = copy_2_bytes(out, from); return copy_4_bytes(out, from + 2); } static inline unsigned char *copy_7_bytes(unsigned char *out, const unsigned char *from) { out = copy_3_bytes(out, from); return copy_4_bytes(out, from + 3); } #endif static inline unsigned char *copy_8_bytes(unsigned char *out, const unsigned char *from) { #if defined(BLOSC_STRICT_ALIGN) uint64_t chunk; memcpy(&chunk, from, 8); memcpy(out, &chunk, 8); #else *(uint64_t *) out = *(uint64_t *) from; #endif return out + 8; } static inline unsigned char *copy_16_bytes(unsigned char *out, const unsigned char *from) { #if defined(__SSE2__) __m128i chunk; chunk = _mm_loadu_si128((__m128i*)from); _mm_storeu_si128((__m128i*)out, chunk); out += 16; #elif !defined(BLOSC_STRICT_ALIGN) *(uint64_t*)out = *(uint64_t*)from; from += 8; out += 8; *(uint64_t*)out = *(uint64_t*)from; from += 8; out += 8; #else int i; for (i = 0; i < 16; i++) { *out++ = *from++; } #endif return out; } static inline unsigned char *copy_32_bytes(unsigned char *out, const unsigned char *from) { #if defined(__AVX2__) __m256i chunk; chunk = _mm256_loadu_si256((__m256i*)from); _mm256_storeu_si256((__m256i*)out, chunk); out += 32; #elif defined(__SSE2__) __m128i chunk; chunk = _mm_loadu_si128((__m128i*)from); _mm_storeu_si128((__m128i*)out, chunk); from += 16; out += 16; chunk = _mm_loadu_si128((__m128i*)from); _mm_storeu_si128((__m128i*)out, chunk); out += 16; #elif !defined(BLOSC_STRICT_ALIGN) *(uint64_t*)out = *(uint64_t*)from; from += 8; out += 8; *(uint64_t*)out = *(uint64_t*)from; from += 8; out += 8; *(uint64_t*)out = *(uint64_t*)from; from += 8; out += 8; *(uint64_t*)out = *(uint64_t*)from; from += 8; out += 8; #else int i; for (i = 0; i < 32; i++) { *out++ = *from++; } #endif return out; } // This is never used, so comment it out //#if defined(__AVX2__) //static inline unsigned char *copy_32_bytes_aligned(unsigned char *out, const unsigned char *from) { // __m256i chunk; // chunk = _mm256_load_si256((__m256i*)from); // _mm256_storeu_si256((__m256i*)out, chunk); // return out + 32; //} //#endif // __AVX2__ /* Copy LEN bytes (7 or fewer) from FROM into OUT. Return OUT + LEN. */ static inline unsigned char *copy_bytes(unsigned char *out, const unsigned char *from, unsigned len) { assert(len < 8); #ifdef BLOSC_STRICT_ALIGN while (len--) { *out++ = *from++; } #else switch (len) { case 7: return copy_7_bytes(out, from); case 6: return copy_6_bytes(out, from); case 5: return copy_5_bytes(out, from); case 4: return copy_4_bytes(out, from); case 3: return copy_3_bytes(out, from); case 2: return copy_2_bytes(out, from); case 1: return copy_1_bytes(out, from); case 0: return out; default: assert(0); } #endif /* BLOSC_STRICT_ALIGN */ return out; } // Define a symbol for avoiding fall-through warnings emitted by gcc >= 7.0 #if ((defined(__GNUC__) && BLOSC_GCC_VERSION >= 700) && !defined(__clang__) && \ !defined(__ICC) && !defined(__ICL)) #define AVOID_FALLTHROUGH_WARNING #endif /* Byte by byte semantics: copy LEN bytes from FROM and write them to OUT. Return OUT + LEN. */ static inline unsigned char *chunk_memcpy(unsigned char *out, const unsigned char *from, unsigned len) { unsigned sz = sizeof(uint64_t); unsigned rem = len % sz; unsigned by8; assert(len >= sz); /* Copy a few bytes to make sure the loop below has a multiple of SZ bytes to be copied. */ copy_8_bytes(out, from); len /= sz; out += rem; from += rem; by8 = len % 8; len -= by8; switch (by8) { case 7: out = copy_8_bytes(out, from); from += sz; #ifdef AVOID_FALLTHROUGH_WARNING __attribute__ ((fallthrough)); // Shut-up -Wimplicit-fallthrough warning in GCC #endif case 6: out = copy_8_bytes(out, from); from += sz; #ifdef AVOID_FALLTHROUGH_WARNING __attribute__ ((fallthrough)); #endif case 5: out = copy_8_bytes(out, from); from += sz; #ifdef AVOID_FALLTHROUGH_WARNING __attribute__ ((fallthrough)); #endif case 4: out = copy_8_bytes(out, from); from += sz; #ifdef AVOID_FALLTHROUGH_WARNING __attribute__ ((fallthrough)); #endif case 3: out = copy_8_bytes(out, from); from += sz; #ifdef AVOID_FALLTHROUGH_WARNING __attribute__ ((fallthrough)); #endif case 2: out = copy_8_bytes(out, from); from += sz; #ifdef AVOID_FALLTHROUGH_WARNING __attribute__ ((fallthrough)); #endif case 1: out = copy_8_bytes(out, from); from += sz; #ifdef AVOID_FALLTHROUGH_WARNING __attribute__ ((fallthrough)); #endif default: break; } while (len) { out = copy_8_bytes(out, from); from += sz; out = copy_8_bytes(out, from); from += sz; out = copy_8_bytes(out, from); from += sz; out = copy_8_bytes(out, from); from += sz; out = copy_8_bytes(out, from); from += sz; out = copy_8_bytes(out, from); from += sz; out = copy_8_bytes(out, from); from += sz; out = copy_8_bytes(out, from); from += sz; len -= 8; } return out; } #if (defined(__SSE2__) && defined(__AVX2__)) /* 16-byte version of chunk_memcpy() */ static inline unsigned char *chunk_memcpy_16(unsigned char *out, const unsigned char *from, unsigned len) { unsigned sz = 16; unsigned rem = len % sz; unsigned ilen; assert(len >= sz); /* Copy a few bytes to make sure the loop below has a multiple of SZ bytes to be copied. */ copy_16_bytes(out, from); len /= sz; out += rem; from += rem; for (ilen = 0; ilen < len; ilen++) { copy_16_bytes(out, from); out += sz; from += sz; } return out; } #endif // NOTE: chunk_memcpy_32() and chunk_memcpy_32_unrolled() are not used, so commenting them ///* 32-byte version of chunk_memcpy() */ //static inline unsigned char *chunk_memcpy_32(unsigned char *out, const unsigned char *from, unsigned len) { // unsigned sz = 32; // unsigned rem = len % sz; // unsigned ilen; // // assert(len >= sz); // // /* Copy a few bytes to make sure the loop below has a multiple of SZ bytes to be copied. */ // copy_32_bytes(out, from); // // len /= sz; // out += rem; // from += rem; // // for (ilen = 0; ilen < len; ilen++) { // copy_32_bytes(out, from); // out += sz; // from += sz; // } // // return out; //} // ///* 32-byte *unrolled* version of chunk_memcpy() */ //static inline unsigned char *chunk_memcpy_32_unrolled(unsigned char *out, const unsigned char *from, unsigned len) { // unsigned sz = 32; // unsigned rem = len % sz; // unsigned by8; // // assert(len >= sz); // // /* Copy a few bytes to make sure the loop below has a multiple of SZ bytes to be copied. */ // copy_32_bytes(out, from); // // len /= sz; // out += rem; // from += rem; // // by8 = len % 8; // len -= by8; // switch (by8) { // case 7: // out = copy_32_bytes(out, from); // from += sz; // case 6: // out = copy_32_bytes(out, from); // from += sz; // case 5: // out = copy_32_bytes(out, from); // from += sz; // case 4: // out = copy_32_bytes(out, from); // from += sz; // case 3: // out = copy_32_bytes(out, from); // from += sz; // case 2: // out = copy_32_bytes(out, from); // from += sz; // case 1: // out = copy_32_bytes(out, from); // from += sz; // default: // break; // } // // while (len) { // out = copy_32_bytes(out, from); // from += sz; // out = copy_32_bytes(out, from); // from += sz; // out = copy_32_bytes(out, from); // from += sz; // out = copy_32_bytes(out, from); // from += sz; // out = copy_32_bytes(out, from); // from += sz; // out = copy_32_bytes(out, from); // from += sz; // out = copy_32_bytes(out, from); // from += sz; // out = copy_32_bytes(out, from); // from += sz; // // len -= 8; // } // // return out; //} /* SSE2/AVX2 *unaligned* version of chunk_memcpy() */ #if defined(__SSE2__) || defined(__AVX2__) static inline unsigned char *chunk_memcpy_unaligned(unsigned char *out, const unsigned char *from, unsigned len) { #if defined(__AVX2__) unsigned sz = sizeof(__m256i); #elif defined(__SSE2__) unsigned sz = sizeof(__m128i); #endif unsigned rem = len % sz; unsigned ilen; assert(len >= sz); /* Copy a few bytes to make sure the loop below has a multiple of SZ bytes to be copied. */ #if defined(__AVX2__) copy_32_bytes(out, from); #elif defined(__SSE2__) copy_16_bytes(out, from); #endif len /= sz; out += rem; from += rem; for (ilen = 0; ilen < len; ilen++) { #if defined(__AVX2__) copy_32_bytes(out, from); #elif defined(__SSE2__) copy_16_bytes(out, from); #endif out += sz; from += sz; } return out; } #endif // __SSE2__ || __AVX2__ // NOTE: chunk_memcpy_aligned() is not used, so commenting it //#if defined(__SSE2__) || defined(__AVX2__) ///* SSE2/AVX2 *aligned* version of chunk_memcpy() */ //static inline unsigned char *chunk_memcpy_aligned(unsigned char *out, const unsigned char *from, unsigned len) { //#if defined(__AVX2__) // unsigned sz = sizeof(__m256i); // __m256i chunk; //#elif defined(__SSE2__) // unsigned sz = sizeof(__m128i); // __m128i chunk; //#endif // unsigned bytes_to_align = sz - (unsigned)(((uintptr_t)(const void *)(from)) % sz); // unsigned corrected_len = len - bytes_to_align; // unsigned rem = corrected_len % sz; // unsigned ilen; // // assert(len >= sz); // // /* Copy a few bytes to make sure the loop below has aligned access. */ //#if defined(__AVX2__) // chunk = _mm256_loadu_si256((__m256i *) from); // _mm256_storeu_si256((__m256i *) out, chunk); //#elif defined(__SSE2__) // chunk = _mm_loadu_si128((__m128i *) from); // _mm_storeu_si128((__m128i *) out, chunk); //#endif // out += bytes_to_align; // from += bytes_to_align; // // len = corrected_len / sz; // for (ilen = 0; ilen < len; ilen++) { //#if defined(__AVX2__) // chunk = _mm256_load_si256((__m256i *) from); /* *aligned* load */ // _mm256_storeu_si256((__m256i *) out, chunk); //#elif defined(__SSE2__) // chunk = _mm_load_si128((__m128i *) from); /* *aligned* load */ // _mm_storeu_si128((__m128i *) out, chunk); //#endif // out += sz; // from += sz; // } // // /* Copy remaining bytes */ // if (rem < 8) { // out = copy_bytes(out, from, rem); // } // else { // out = chunk_memcpy(out, from, rem); // } // // return out; //} //#endif // __AVX2__ || __SSE2__ /* Byte by byte semantics: copy LEN bytes from FROM and write them to OUT. Return OUT + LEN. */ unsigned char *fastcopy(unsigned char *out, const unsigned char *from, unsigned len) { switch (len) { case 32: return copy_32_bytes(out, from); case 16: return copy_16_bytes(out, from); case 8: return copy_8_bytes(out, from); default: { } } if (len < 8) { return copy_bytes(out, from, len); } #if defined(__SSE2__) if (len < 16) { return chunk_memcpy(out, from, len); } #if !defined(__AVX2__) return chunk_memcpy_unaligned(out, from, len); #else if (len < 32) { return chunk_memcpy_16(out, from, len); } return chunk_memcpy_unaligned(out, from, len); #endif // !__AVX2__ #else return chunk_memcpy(out, from, len); #endif // __SSE2__ } /* Copy a run */ unsigned char* copy_match(unsigned char *out, const unsigned char *from, unsigned len) { #if defined(__AVX2__) unsigned sz = sizeof(__m256i); #elif defined(__SSE2__) unsigned sz = sizeof(__m128i); #else unsigned sz = sizeof(uint64_t); #endif #if ((defined(__GNUC__) && BLOSC_GCC_VERSION < 800) && !defined(__clang__) && !defined(__ICC) && !defined(__ICL)) // GCC < 8 in fully optimization mode seems to have problems with the code further below so stop here for (; len > 0; len--) { *out++ = *from++; } return out; #endif // If out and from are away more than the size of the copy, then a fastcopy is safe unsigned overlap_dist = (unsigned) (out - from); if (overlap_dist > sz) { return fastcopy(out, from, len); } // Otherwise we need to be more careful so as not to overwrite destination switch (overlap_dist) { case 32: for (; len >= 32; len -= 32) { out = copy_32_bytes(out, from); } break; case 30: for (; len >= 30; len -= 30) { out = copy_16_bytes(out, from); out = copy_8_bytes(out, from + 16); out = copy_4_bytes(out, from + 24); out = copy_2_bytes(out, from + 28); } break; case 28: for (; len >= 28; len -= 28) { out = copy_16_bytes(out, from); out = copy_8_bytes(out, from + 16); out = copy_4_bytes(out, from + 24); } break; case 26: for (; len >= 26; len -= 26) { out = copy_16_bytes(out, from); out = copy_8_bytes(out, from + 16); out = copy_2_bytes(out, from + 24); } break; case 24: for (; len >= 24; len -= 24) { out = copy_16_bytes(out, from); out = copy_8_bytes(out, from + 16); } break; case 22: for (; len >= 22; len -= 22) { out = copy_16_bytes(out, from); out = copy_4_bytes(out, from + 16); out = copy_2_bytes(out, from + 20); } break; case 20: for (; len >= 20; len -= 20) { out = copy_16_bytes(out, from); out = copy_4_bytes(out, from + 16); } break; case 18: for (; len >= 18; len -= 18) { out = copy_16_bytes(out, from); out = copy_2_bytes(out, from + 16); } break; case 16: for (; len >= 16; len -= 16) { out = copy_16_bytes(out, from); } break; case 8: for (; len >= 8; len -= 8) { out = copy_8_bytes(out, from); } break; case 4: for (; len >= 4; len -= 4) { out = copy_4_bytes(out, from); } break; case 2: for (; len >= 2; len -= 2) { out = copy_2_bytes(out, from); } break; default: for (; len > 0; len--) { *out++ = *from++; } } // Copy the leftovers for (; len > 0; len--) { *out++ = *from++; } return out; } c-blosc2-3.1.5/blosc/fastcopy.h000066400000000000000000000014011521743436100162310ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #ifndef BLOSC_FASTCOPY_H #define BLOSC_FASTCOPY_H /* Same semantics than memcpy() */ unsigned char *fastcopy(unsigned char *out, const unsigned char *from, unsigned len); /* Same as fastcopy() but without overwriting origin or destination when they overlap */ unsigned char* copy_match(unsigned char *out, const unsigned char *from, unsigned len); #endif /* BLOSC_FASTCOPY_H */ c-blosc2-3.1.5/blosc/frame.c000066400000000000000000004315401521743436100155010ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #include "frame.h" #include "sframe.h" #include "context.h" #include "blosc-private.h" #include "blosc2.h" #include #if defined(_WIN32) #include #include // See https://github.com/Blosc/python-blosc2/issues/359#issuecomment-2625380236 #define stat _stat64 #endif /* _WIN32 */ #include #include #include #include #include #include #include /* If C11 is supported, use it's built-in aligned allocation. */ #if __STDC_VERSION__ >= 201112L #include #endif /* Create a new (empty) frame */ blosc2_frame_s* frame_new(const char* urlpath) { blosc2_frame_s* new_frame = calloc(1, sizeof(blosc2_frame_s)); if (new_frame == NULL) { return NULL; } if (urlpath != NULL) { char* new_urlpath = malloc(strlen(urlpath) + 1); // + 1 for the trailing NULL if (new_urlpath == NULL) { free(new_frame); return NULL; } new_frame->urlpath = strcpy(new_urlpath, urlpath); new_frame->file_offset = 0; } return new_frame; } /* Free memory from a frame. */ int frame_free(blosc2_frame_s* frame) { if (frame->cframe != NULL && !frame->avoid_cframe_free) { free(frame->cframe); } if (frame->coffsets != NULL && frame->coffsets_needs_free) { free(frame->coffsets); } if (frame->urlpath != NULL) { free(frame->urlpath); } free(frame); return 0; } void *new_header_frame(blosc2_schunk *schunk, blosc2_frame_s *frame) { if (frame == NULL) { return NULL; } uint8_t* h2 = calloc(FRAME_HEADER_MINLEN, 1); uint8_t* h2p = h2; // The msgpack header starts here *h2p = 0x90; // fixarray... *h2p += 14; // ...with 13 elements h2p += 1; // Magic number *h2p = 0xa0 + 8; // str with 8 elements h2p += 1; if (h2p - h2 >= FRAME_HEADER_MINLEN) { return NULL; } strcpy((char*)h2p, "b2frame"); h2p += 8; // Header size *h2p = 0xd2; // int32 h2p += 1 + 4; if (h2p - h2 >= FRAME_HEADER_MINLEN) { return NULL; } // Total frame size *h2p = 0xcf; // uint64 // Fill it with frame->len which is known *after* the creation of the frame (e.g. when updating the header) int64_t flen = frame->len; to_big(h2 + FRAME_LEN, &flen, sizeof(flen)); h2p += 1 + 8; if (h2p - h2 >= FRAME_HEADER_MINLEN) { return NULL; } // Flags *h2p = 0xa0 + 4; // str with 4 elements h2p += 1; if (h2p - h2 >= FRAME_HEADER_MINLEN) { return NULL; } // General flags if (schunk->chunksize == 0 || (schunk->flags2 & BLOSC2_VL_BLOCKS)) { *h2p = BLOSC2_VERSION_FRAME_FORMAT_VL_BLOCKS; // version } else { *h2p = BLOSC2_VERSION_FRAME_FORMAT_RC1; // version } *h2p += 0x10; // 64-bit offsets. We only support this for now. if (schunk->chunksize == 0) { *h2p |= FRAME_VARIABLE_CHUNKS; } if (schunk->flags2 & BLOSC2_VL_BLOCKS) { *h2p |= FRAME_VL_BLOCKS; } h2p += 1; if (h2p - h2 >= FRAME_HEADER_MINLEN) { return NULL; } // Frame type // We only support contiguous and sparse directories frames currently *h2p = frame->sframe ? 1 : 0; h2p += 1; if (h2p - h2 >= FRAME_HEADER_MINLEN) { return NULL; } // Codec flags *h2p = schunk->compcode; if (schunk->compcode >= BLOSC_LAST_CODEC) { *h2p = BLOSC_UDCODEC_FORMAT; } *h2p += (schunk->clevel) << 4u; // clevel h2p += 1; if (h2p - h2 >= FRAME_HEADER_MINLEN) { return NULL; } // Other flags *h2p = schunk->splitmode - 1; h2p += 1; if (h2p - h2 >= FRAME_HEADER_MINLEN) { return NULL; } // Uncompressed size *h2p = 0xd3; // int64 h2p += 1; int64_t nbytes = schunk->nbytes; to_big(h2p, &nbytes, sizeof(nbytes)); h2p += 8; if (h2p - h2 >= FRAME_HEADER_MINLEN) { return NULL; } // Compressed size *h2p = 0xd3; // int64 h2p += 1; int64_t cbytes = schunk->cbytes; to_big(h2p, &cbytes, sizeof(cbytes)); h2p += 8; if (h2p - h2 >= FRAME_HEADER_MINLEN) { return NULL; } // Type size *h2p = 0xd2; // int32 h2p += 1; int32_t typesize = schunk->typesize; to_big(h2p, &typesize, sizeof(typesize)); h2p += 4; if (h2p - h2 >= FRAME_HEADER_MINLEN) { return NULL; } // Block size *h2p = 0xd2; // int32 h2p += 1; int32_t blocksize = schunk->blocksize; to_big(h2p, &blocksize, sizeof(blocksize)); h2p += 4; if (h2p - h2 >= FRAME_HEADER_MINLEN) { return NULL; } // Chunk size *h2p = 0xd2; // int32 h2p += 1; int32_t chunksize = schunk->chunksize; to_big(h2p, &chunksize, sizeof(chunksize)); h2p += 4; if (h2p - h2 >= FRAME_HEADER_MINLEN) { return NULL; } // Number of threads for compression *h2p = 0xd1; // int16 h2p += 1; int16_t nthreads = (int16_t)schunk->cctx->nthreads; to_big(h2p, &nthreads, sizeof(nthreads)); h2p += 2; if (h2p - h2 >= FRAME_HEADER_MINLEN) { return NULL; } // Number of threads for decompression *h2p = 0xd1; // int16 h2p += 1; nthreads = (int16_t)schunk->dctx->nthreads; to_big(h2p, &nthreads, sizeof(nthreads)); h2p += 2; if (h2p - h2 >= FRAME_HEADER_MINLEN) { return NULL; } // The boolean for variable-length metalayers *h2p = (schunk->nvlmetalayers > 0) ? (uint8_t)0xc3 : (uint8_t)0xc2; h2p += 1; if (h2p - h2 >= FRAME_HEADER_MINLEN) { return NULL; } // The space for FRAME_FILTER_PIPELINE *h2p = 0xd8; // fixext 16 h2p += 1; if (BLOSC2_MAX_FILTERS > FRAME_FILTER_PIPELINE_MAX) { return NULL; } // Store the filter pipeline in header uint8_t* mp_filters = h2 + FRAME_FILTER_PIPELINE + 1; uint8_t* mp_meta = h2 + FRAME_FILTER_PIPELINE + 1 + FRAME_FILTER_PIPELINE_MAX; for (int i = 0; i < BLOSC2_MAX_FILTERS; i++) { mp_filters[i] = schunk->filters[i]; mp_meta[i] = schunk->filters_meta[i]; } *h2p = (uint8_t) BLOSC2_MAX_FILTERS; h2p += 1; h2p += 16; // User-defined codec and codec metadata uint8_t* udcodec = h2 + FRAME_UDCODEC; *udcodec = schunk->compcode; uint8_t* codec_meta = h2 + FRAME_CODEC_META; *codec_meta = schunk->compcode_meta; // Other flags 2 (byte 0x55): bit 0 = use_dict h2[FRAME_OTHER_FLAGS2] = schunk->use_dict ? FRAME_USE_DICT : 0; if (h2p - h2 != FRAME_HEADER_MINLEN) { return NULL; } int32_t hsize = FRAME_HEADER_MINLEN; // Now, deal with metalayers uint16_t nmetalayers = schunk->nmetalayers; if (nmetalayers > BLOSC2_MAX_METALAYERS) { return NULL; } // Make space for the header of metalayers (array marker, size, map of offsets) h2 = realloc(h2, (size_t)hsize + 1 + 1 + 2 + 1 + 2); h2p = h2 + hsize; // The msgpack header for the metalayers (array_marker, size, map of offsets, list of metalayers) *h2p = 0x90 + 3; // array with 3 elements h2p += 1; // Size for the map (index) of offsets, including this uint16 size (to be filled out later on) *h2p = 0xcd; // uint16 h2p += 1 + 2; // Map (index) of offsets for optional metalayers *h2p = 0xde; // map 16 with N keys h2p += 1; to_big(h2p, &nmetalayers, sizeof(nmetalayers)); h2p += sizeof(nmetalayers); int32_t current_header_len = (int32_t)(h2p - h2); int32_t *offtooff = malloc(nmetalayers * sizeof(int32_t)); for (int nmetalayer = 0; nmetalayer < nmetalayers; nmetalayer++) { if (frame == NULL) { return NULL; } blosc2_metalayer *metalayer = schunk->metalayers[nmetalayer]; uint8_t namelen = (uint8_t) strlen(metalayer->name); h2 = realloc(h2, (size_t)current_header_len + 1 + namelen + 1 + 4); h2p = h2 + current_header_len; // Store the metalayer if (namelen >= (1U << 5U)) { // metalayer strings cannot be longer than 32 bytes free(offtooff); return NULL; } *h2p = (uint8_t)0xa0 + namelen; // str h2p += 1; memcpy(h2p, metalayer->name, namelen); h2p += namelen; // Space for storing the offset for the value of this metalayer *h2p = 0xd2; // int32 h2p += 1; offtooff[nmetalayer] = (int32_t)(h2p - h2); h2p += 4; current_header_len += 1 + namelen + 1 + 4; } int32_t hsize2 = (int32_t)(h2p - h2); if (hsize2 != current_header_len) { // sanity check return NULL; } // Map size + int16 size if ((uint32_t) (hsize2 - hsize) >= (1U << 16U)) { return NULL; } uint16_t map_size = (uint16_t) (hsize2 - hsize); to_big(h2 + FRAME_IDX_SIZE, &map_size, sizeof(map_size)); // Make space for an (empty) array hsize = (int32_t)(h2p - h2); h2 = realloc(h2, (size_t)hsize + 2 + 1 + 2); h2p = h2 + hsize; // Now, store the values in an array *h2p = 0xdc; // array 16 with N elements h2p += 1; to_big(h2p, &nmetalayers, sizeof(nmetalayers)); h2p += sizeof(nmetalayers); current_header_len = (int32_t)(h2p - h2); for (int nmetalayer = 0; nmetalayer < nmetalayers; nmetalayer++) { if (frame == NULL) { return NULL; } blosc2_metalayer *metalayer = schunk->metalayers[nmetalayer]; h2 = realloc(h2, (size_t)current_header_len + 1 + 4 + metalayer->content_len); h2p = h2 + current_header_len; // Store the serialized contents for this metalayer *h2p = 0xc6; // bin32 h2p += 1; to_big(h2p, &(metalayer->content_len), sizeof(metalayer->content_len)); h2p += 4; memcpy(h2p, metalayer->content, metalayer->content_len); // buffer, no need to swap h2p += metalayer->content_len; // Update the offset now that we know it to_big(h2 + offtooff[nmetalayer], ¤t_header_len, sizeof(current_header_len)); current_header_len += 1 + 4 + metalayer->content_len; } free(offtooff); hsize = (int32_t)(h2p - h2); if (hsize != current_header_len) { // sanity check return NULL; } // Set the length of the whole header now that we know it to_big(h2 + FRAME_HEADER_LEN, &hsize, sizeof(hsize)); return h2; } static int get_coffsets_nbytes(blosc2_frame_s *frame, int32_t header_len, int64_t cbytes, int32_t *coffsets_nbytes, const blosc2_io *io); int get_header_info(blosc2_frame_s *frame, int32_t *header_len, int64_t *frame_len, int64_t *nbytes, int64_t *cbytes, int32_t *blocksize, int32_t *chunksize, int64_t *nchunks, int32_t *typesize, uint8_t *compcode, uint8_t *compcode_meta, uint8_t *clevel, uint8_t *filters, uint8_t *filters_meta, uint8_t *splitmode, uint8_t *use_dict, const blosc2_io *io) { uint8_t* framep = frame->cframe; uint8_t* header_ptr; uint8_t header[FRAME_HEADER_MINLEN]; blosc2_io_cb *io_cb = blosc2_get_io_cb(io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); return BLOSC2_ERROR_PLUGIN_IO; } if (frame->len <= 0) { return BLOSC2_ERROR_READ_BUFFER; } if (frame->cframe == NULL) { int64_t rbytes = 0; void* fp = NULL; int64_t io_pos = 0; if (frame->sframe) { fp = sframe_open_index(frame->urlpath, "rb", io); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return BLOSC2_ERROR_FILE_OPEN; } } else { fp = io_cb->open(frame->urlpath, "rb", io->params); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return BLOSC2_ERROR_FILE_OPEN; } io_pos = frame->file_offset; } if (io_cb->is_allocation_necessary) header_ptr = header; rbytes = io_cb->read((void**)&header_ptr, 1, FRAME_HEADER_MINLEN, io_pos, fp); io_cb->close(fp); if (rbytes != FRAME_HEADER_MINLEN) { return BLOSC2_ERROR_FILE_READ; } framep = header_ptr; } // Consistency check for frame type uint8_t frame_type = framep[FRAME_TYPE]; uint8_t frame_version = framep[FRAME_FLAGS] & 0x0fu; if (frame_version > BLOSC2_VERSION_FRAME_FORMAT) { BLOSC_TRACE_ERROR("Unsupported cframe version: %u", frame_version); return BLOSC2_ERROR_VERSION_SUPPORT; } if (frame->sframe) { if (frame_type != FRAME_DIRECTORY_TYPE) { return BLOSC2_ERROR_FRAME_TYPE; } } else { if (frame_type != FRAME_CONTIGUOUS_TYPE) { return BLOSC2_ERROR_FRAME_TYPE; } } // Fetch some internal lengths from_big(header_len, framep + FRAME_HEADER_LEN, sizeof(*header_len)); if (*header_len < FRAME_HEADER_MINLEN) { BLOSC_TRACE_ERROR("Header length is zero or smaller than min allowed."); return BLOSC2_ERROR_INVALID_HEADER; } from_big(frame_len, framep + FRAME_LEN, sizeof(*frame_len)); if (*header_len > *frame_len) { BLOSC_TRACE_ERROR("Header length exceeds length of the frame."); return BLOSC2_ERROR_INVALID_HEADER; } from_big(nbytes, framep + FRAME_NBYTES, sizeof(*nbytes)); from_big(cbytes, framep + FRAME_CBYTES, sizeof(*cbytes)); from_big(blocksize, framep + FRAME_BLOCKSIZE, sizeof(*blocksize)); if (chunksize != NULL) { from_big(chunksize, framep + FRAME_CHUNKSIZE, sizeof(*chunksize)); } if (typesize != NULL) { from_big(typesize, framep + FRAME_TYPESIZE, sizeof(*typesize)); if (*typesize <= 0) { BLOSC_TRACE_ERROR("`typesize` cannot be zero or negative."); return BLOSC2_ERROR_INVALID_HEADER; } } // Codecs uint8_t frame_codecs = framep[FRAME_CODECS]; if (clevel != NULL) { *clevel = frame_codecs >> 4u; } if (compcode != NULL) { *compcode = frame_codecs & 0xFu; if (*compcode == BLOSC_UDCODEC_FORMAT) { from_big(compcode, framep + FRAME_UDCODEC, sizeof(*compcode)); } } // Other flags uint8_t other_flags = framep[FRAME_OTHER_FLAGS]; if (splitmode != NULL) { *splitmode = (other_flags & 0x03u) + 1; } // Other flags 2: bit 0 = use_dict if (use_dict != NULL) { *use_dict = (framep[FRAME_OTHER_FLAGS2] & FRAME_USE_DICT) ? 1 : 0; } if (compcode_meta != NULL) { from_big(compcode_meta, framep + FRAME_CODEC_META, sizeof(*compcode_meta)); } // Filters if (filters != NULL && filters_meta != NULL) { uint8_t nfilters = framep[FRAME_FILTER_PIPELINE]; if (nfilters > BLOSC2_MAX_FILTERS) { BLOSC_TRACE_ERROR("The number of filters in frame header are too large for Blosc2."); return BLOSC2_ERROR_INVALID_HEADER; } uint8_t *filters_ = framep + FRAME_FILTER_PIPELINE + 1; uint8_t *filters_meta_ = framep + FRAME_FILTER_PIPELINE + 1 + FRAME_FILTER_PIPELINE_MAX; for (int i = 0; i < nfilters; i++) { filters[i] = filters_[i]; filters_meta[i] = filters_meta_[i]; } } if (*nbytes > 0) { if (*chunksize > 0) { // We can compute the number of chunks directly when there is a fixed chunk size. *nchunks = *nbytes / *chunksize; if (*nbytes % *chunksize > 0) { *nchunks += 1; } // Sanity check for compressed sizes if ((*cbytes < 0) || ((int64_t)*nchunks * *chunksize < *nbytes)) { BLOSC_TRACE_ERROR("Invalid compressed size in frame header."); return BLOSC2_ERROR_INVALID_HEADER; } } else if (*chunksize == 0) { int32_t coffsets_nbytes; int rc2 = get_coffsets_nbytes(frame, *header_len, *cbytes, &coffsets_nbytes, io); if (rc2 < 0) { return rc2; } if (coffsets_nbytes < 0 || coffsets_nbytes % (int32_t)sizeof(int64_t) != 0) { BLOSC_TRACE_ERROR("Invalid offsets chunk in frame header."); return BLOSC2_ERROR_INVALID_HEADER; } *nchunks = coffsets_nbytes / (int32_t)sizeof(int64_t); } else { BLOSC_TRACE_ERROR("Invalid chunk size in frame header."); return BLOSC2_ERROR_INVALID_HEADER; } } else { *nchunks = 0; } if (*nchunks > 0) { int32_t off_nbytes; if (!blosc2_nchunks_to_offsets_nbytes(*nchunks, &off_nbytes)) { BLOSC_TRACE_ERROR("Invalid number of chunks in frame header."); return BLOSC2_ERROR_INVALID_HEADER; } } return 0; } int64_t get_trailer_offset(blosc2_frame_s *frame, int32_t header_len, bool has_coffsets) { if (!has_coffsets) { // No data chunks yet return header_len; } return frame->len - frame->trailer_len; } static int get_coffsets_nbytes(blosc2_frame_s *frame, int32_t header_len, int64_t cbytes, int32_t *coffsets_nbytes, const blosc2_io *io) { int32_t chunk_cbytes; int rc; int64_t off_pos = header_len; if (!frame->sframe) { if (cbytes < INT64_MAX - header_len) { off_pos += cbytes; } } if (off_pos < 0 || off_pos > INT64_MAX - BLOSC_EXTENDED_HEADER_LENGTH || off_pos + BLOSC_EXTENDED_HEADER_LENGTH > frame->len) { BLOSC_TRACE_ERROR("Cannot read the offsets outside of frame boundary."); return BLOSC2_ERROR_INVALID_HEADER; } if (frame->cframe != NULL) { uint8_t *off_start = frame->cframe + off_pos; rc = blosc2_cbuffer_sizes(off_start, coffsets_nbytes, &chunk_cbytes, NULL); if (rc < 0) { return rc; } if (chunk_cbytes < 0 || off_pos + chunk_cbytes > frame->len) { BLOSC_TRACE_ERROR("Cannot read the cbytes outside of frame boundary."); return BLOSC2_ERROR_INVALID_HEADER; } return 0; } int64_t trailer_offset = get_trailer_offset(frame, header_len, true); if (trailer_offset < BLOSC_EXTENDED_HEADER_LENGTH || trailer_offset + FRAME_TRAILER_MINLEN > frame->len) { BLOSC_TRACE_ERROR("Cannot read the trailer out of the frame."); return BLOSC2_ERROR_INVALID_HEADER; } blosc2_io_cb *io_cb = blosc2_get_io_cb(io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); return BLOSC2_ERROR_PLUGIN_IO; } uint8_t header[BLOSC_EXTENDED_HEADER_LENGTH]; uint8_t *header_ptr = header; void *fp = NULL; int64_t io_pos = 0; if (frame->sframe) { fp = sframe_open_index(frame->urlpath, "rb", io); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return BLOSC2_ERROR_FILE_OPEN; } io_pos = off_pos; } else { fp = io_cb->open(frame->urlpath, "rb", io->params); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return BLOSC2_ERROR_FILE_OPEN; } io_pos = frame->file_offset + off_pos; } int64_t rbytes = io_cb->read((void**)&header_ptr, 1, BLOSC_EXTENDED_HEADER_LENGTH, io_pos, fp); io_cb->close(fp); if (rbytes != BLOSC_EXTENDED_HEADER_LENGTH) { BLOSC_TRACE_ERROR("Cannot read the offsets header out of the frame."); return BLOSC2_ERROR_FILE_READ; } rc = blosc2_cbuffer_sizes(header_ptr, coffsets_nbytes, &chunk_cbytes, NULL); if (rc < 0) { return rc; } if (chunk_cbytes < 0 || off_pos + chunk_cbytes > trailer_offset) { BLOSC_TRACE_ERROR("Cannot read the offsets outside of frame boundary."); return BLOSC2_ERROR_INVALID_HEADER; } return 0; } // Update the length in the header int update_frame_len(blosc2_frame_s* frame, int64_t len) { int rc = 1; blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); return BLOSC2_ERROR_PLUGIN_IO; } if (frame->cframe != NULL) { to_big(frame->cframe + FRAME_LEN, &len, sizeof(int64_t)); } else { void* fp = NULL; if (frame->sframe) { fp = sframe_open_index(frame->urlpath, "rb+", frame->schunk->storage->io); } else { fp = io_cb->open(frame->urlpath, "rb+", frame->schunk->storage->io->params); } if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return BLOSC2_ERROR_FILE_OPEN; } int64_t io_pos = frame->file_offset + FRAME_LEN; int64_t swap_len; to_big(&swap_len, &len, sizeof(int64_t)); int64_t wbytes = io_cb->write(&swap_len, 1, sizeof(int64_t), io_pos, fp); io_cb->close(fp); if (wbytes != sizeof(int64_t)) { BLOSC_TRACE_ERROR("Cannot write the frame length in header."); return BLOSC2_ERROR_FILE_WRITE; } } return rc; } int frame_update_trailer(blosc2_frame_s* frame, blosc2_schunk* schunk) { if (frame != NULL && frame->len == 0) { BLOSC_TRACE_ERROR("The trailer cannot be updated on empty frames."); } // Create the trailer in msgpack (see the frame format document) uint32_t trailer_len = FRAME_TRAILER_MINLEN; uint8_t* trailer = (uint8_t*)calloc((size_t)trailer_len, 1); uint8_t* ptrailer = trailer; *ptrailer = 0x90 + 4; // fixarray with 4 elements ptrailer += 1; // Trailer format version *ptrailer = FRAME_TRAILER_VERSION; ptrailer += 1; int32_t current_trailer_len = (int32_t)(ptrailer - trailer); // Now, deal with variable-length metalayers int16_t nvlmetalayers = schunk->nvlmetalayers; if (nvlmetalayers < 0 || nvlmetalayers > BLOSC2_MAX_METALAYERS) { return -1; } // Make space for the header of metalayers (array marker, size, map of offsets) trailer = realloc(trailer, (size_t) current_trailer_len + 1 + 1 + 2 + 1 + 2); ptrailer = trailer + current_trailer_len; // The msgpack header for the metalayers (array_marker, size, map of offsets, list of metalayers) *ptrailer = 0x90 + 3; // array with 3 elements ptrailer += 1; int32_t tsize = (int32_t)(ptrailer - trailer); // Size for the map (index) of metalayer offsets, including this uint16 size (to be filled out later on) *ptrailer = 0xcd; // uint16 ptrailer += 1 + 2; // Map (index) of offsets for optional metalayers *ptrailer = 0xde; // map 16 with N keys ptrailer += 1; to_big(ptrailer, &nvlmetalayers, sizeof(nvlmetalayers)); ptrailer += sizeof(nvlmetalayers); current_trailer_len = (int32_t)(ptrailer - trailer); int32_t *offtodata = malloc(nvlmetalayers * sizeof(int32_t)); for (int nvlmetalayer = 0; nvlmetalayer < nvlmetalayers; nvlmetalayer++) { if (frame == NULL) { return -1; } blosc2_metalayer *vlmetalayer = schunk->vlmetalayers[nvlmetalayer]; uint8_t name_len = (uint8_t) strlen(vlmetalayer->name); trailer = realloc(trailer, (size_t)current_trailer_len + 1 + name_len + 1 + 4); ptrailer = trailer + current_trailer_len; // Store the vlmetalayer if (name_len >= (1U << 5U)) { // metalayer strings cannot be longer than 32 bytes free(offtodata); return -1; } *ptrailer = (uint8_t)0xa0 + name_len; // str ptrailer += 1; memcpy(ptrailer, vlmetalayer->name, name_len); ptrailer += name_len; // Space for storing the offset for the value of this vlmetalayer *ptrailer = 0xd2; // int32 ptrailer += 1; offtodata[nvlmetalayer] = (int32_t)(ptrailer - trailer); ptrailer += 4; current_trailer_len += 1 + name_len + 1 + 4; } int32_t tsize2 = (int32_t)(ptrailer - trailer); if (tsize2 != current_trailer_len) { // sanity check return -1; } // Map size + int16 size if ((uint32_t) (tsize2 - tsize) >= (1U << 16U)) { return -1; } uint16_t map_size = (uint16_t) (tsize2 - tsize); to_big(trailer + 4, &map_size, sizeof(map_size)); // Make space for an (empty) array tsize = (int32_t)(ptrailer - trailer); trailer = realloc(trailer, (size_t) tsize + 2 + 1 + 2); ptrailer = trailer + tsize; // Now, store the values in an array *ptrailer = 0xdc; // array 16 with N elements ptrailer += 1; to_big(ptrailer, &nvlmetalayers, sizeof(nvlmetalayers)); ptrailer += sizeof(nvlmetalayers); current_trailer_len = (int32_t)(ptrailer - trailer); for (int nvlmetalayer = 0; nvlmetalayer < nvlmetalayers; nvlmetalayer++) { if (frame == NULL) { return -1; } blosc2_metalayer *vlmetalayer = schunk->vlmetalayers[nvlmetalayer]; trailer = realloc(trailer, (size_t)current_trailer_len + 1 + 4 + vlmetalayer->content_len); ptrailer = trailer + current_trailer_len; // Store the serialized contents for this vlmetalayer *ptrailer = 0xc6; // bin32 ptrailer += 1; to_big(ptrailer, &(vlmetalayer->content_len), sizeof(vlmetalayer->content_len)); ptrailer += 4; memcpy(ptrailer, vlmetalayer->content, vlmetalayer->content_len); // buffer, no need to swap ptrailer += vlmetalayer->content_len; // Update the offset now that we know it to_big(trailer + offtodata[nvlmetalayer], ¤t_trailer_len, sizeof(current_trailer_len)); current_trailer_len += 1 + 4 + vlmetalayer->content_len; } free(offtodata); tsize = (int32_t)(ptrailer - trailer); if (tsize != current_trailer_len) { // sanity check return -1; } trailer = realloc(trailer, (size_t)current_trailer_len + 23); ptrailer = trailer + current_trailer_len; trailer_len = (ptrailer - trailer) + 23; // Trailer length *ptrailer = 0xce; // uint32 ptrailer += 1; to_big(ptrailer, &trailer_len, sizeof(uint32_t)); ptrailer += sizeof(uint32_t); // Up to 16 bytes for frame fingerprint (using XXH3 included in https://github.com/Cyan4973/xxHash) // Maybe someone would need 256-bit in the future, but for the time being 128-bit seems like a good tradeoff *ptrailer = 0xd8; // fixext 16 ptrailer += 1; *ptrailer = 0; // fingerprint type: 0 -> no fp; 1 -> 32-bit; 2 -> 64-bit; 3 -> 128-bit ptrailer += 1; // Remove call to memset when we compute an actual fingerprint memset(ptrailer, 0, 16); // Uncomment call to memcpy when we compute an actual fingerprint // memcpy(ptrailer, xxh3_fingerprint, sizeof(xxh3_fingerprint)); ptrailer += 16; // Sanity check ptrdiff_t actual_trailer_len = ptrailer - trailer; if (actual_trailer_len < 0 || (uint64_t)actual_trailer_len != (uint64_t)trailer_len) { return BLOSC2_ERROR_DATA; } int32_t header_len; int64_t frame_len; int64_t nbytes; int64_t cbytes; int32_t blocksize; int32_t chunksize; int64_t nchunks; int ret = get_header_info(frame, &header_len, &frame_len, &nbytes, &cbytes, &blocksize, &chunksize, &nchunks, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, frame->schunk->storage->io); if (ret < 0) { BLOSC_TRACE_ERROR("Unable to get meta info from frame."); return ret; } int64_t trailer_offset = get_trailer_offset(frame, header_len, nbytes > 0); if (trailer_offset < BLOSC_EXTENDED_HEADER_LENGTH) { BLOSC_TRACE_ERROR("Unable to get trailer offset in frame."); return BLOSC2_ERROR_READ_BUFFER; } blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); return BLOSC2_ERROR_PLUGIN_IO; } // Update the trailer. As there are no internal offsets to the trailer section, // and it is always at the end of the frame, we can just write (or overwrite) it // at the end of the frame. if (frame->cframe != NULL) { frame->cframe = realloc(frame->cframe, (size_t)(trailer_offset + trailer_len)); if (frame->cframe == NULL) { BLOSC_TRACE_ERROR("Cannot realloc space for the frame."); return BLOSC2_ERROR_MEMORY_ALLOC; } memcpy(frame->cframe + trailer_offset, trailer, trailer_len); } else { void* fp = NULL; if (frame->sframe) { fp = sframe_open_index(frame->urlpath, "rb+", frame->schunk->storage->io); } else { fp = io_cb->open(frame->urlpath, "rb+", frame->schunk->storage->io->params); } if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return BLOSC2_ERROR_FILE_OPEN; } int64_t io_pos = frame->file_offset + trailer_offset; int64_t wbytes = io_cb->write(trailer, 1, trailer_len, io_pos, fp); if (wbytes != trailer_len) { BLOSC_TRACE_ERROR("Cannot write the trailer length in trailer."); return BLOSC2_ERROR_FILE_WRITE; } if (io_cb->truncate(fp, trailer_offset + trailer_len) != 0) { BLOSC_TRACE_ERROR("Cannot truncate the frame."); return BLOSC2_ERROR_FILE_TRUNCATE; } io_cb->close(fp); } free(trailer); int rc = update_frame_len(frame, trailer_offset + trailer_len); if (rc < 0) { return rc; } frame->len = trailer_offset + trailer_len; frame->trailer_len = trailer_len; return 1; } // Remove a file:/// prefix // This is a temporary workaround for allowing to use proper URLs for local files/dirs static char* normalize_urlpath(const char* urlpath) { char* localpath = strstr(urlpath, "file:///"); if (localpath == urlpath) { // There is a file:/// prefix. Get rid of it. localpath += strlen("file:///"); } else { localpath = (char*)urlpath; } return localpath; } /* Initialize a frame out of a file */ blosc2_frame_s* frame_from_file_offset(const char* urlpath, const blosc2_io *io, int64_t offset) { // Get the length of the frame uint8_t* header_ptr; uint8_t header[FRAME_HEADER_MINLEN]; uint8_t* trailer_ptr; uint8_t trailer[FRAME_TRAILER_MINLEN]; void* fp = NULL; bool sframe = false; struct stat path_stat; urlpath = normalize_urlpath(urlpath); if (offset < 0) { BLOSC_TRACE_ERROR("Negative frame offset is not valid: %" PRId64 ".", offset); return NULL; } if(stat(urlpath, &path_stat) < 0) { BLOSC_TRACE_ERROR("Cannot get information about the path %s.", urlpath); return NULL; } blosc2_io_cb *io_cb = blosc2_get_io_cb(io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); return NULL; } char* urlpath_cpy; if (path_stat.st_mode & S_IFDIR) { urlpath_cpy = malloc(strlen(urlpath) + 1); if (urlpath_cpy == NULL) { BLOSC_TRACE_ERROR("Cannot allocate memory for path copy of '%s'.", urlpath); return NULL; } strcpy(urlpath_cpy, urlpath); char last_char = urlpath[strlen(urlpath) - 1]; if (last_char == '\\' || last_char == '/') { urlpath_cpy[strlen(urlpath) - 1] = '\0'; } else { } fp = sframe_open_index(urlpath_cpy, "rb", io); sframe = true; } else { urlpath_cpy = malloc(strlen(urlpath) + 1); if (urlpath_cpy == NULL) { BLOSC_TRACE_ERROR("Cannot allocate memory for path copy of '%s'.", urlpath); return NULL; } strcpy(urlpath_cpy, urlpath); fp = io_cb->open(urlpath, "rb", io->params); } if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", urlpath); free(urlpath_cpy); return NULL; } if (io_cb->is_allocation_necessary) header_ptr = header; int64_t io_pos = offset; int64_t rbytes = io_cb->read((void**)&header_ptr, 1, FRAME_HEADER_MINLEN, io_pos, fp); if (rbytes != FRAME_HEADER_MINLEN) { BLOSC_TRACE_ERROR("Cannot read from file '%s'.", urlpath); io_cb->close(fp); free(urlpath_cpy); return NULL; } int64_t frame_len; to_big(&frame_len, header_ptr + FRAME_LEN, sizeof(frame_len)); if (frame_len < FRAME_HEADER_MINLEN + FRAME_TRAILER_MINLEN) { BLOSC_TRACE_ERROR("Invalid frame length (%" PRId64 ") in file '%s'.", frame_len, urlpath); io_cb->close(fp); free(urlpath_cpy); return NULL; } if (!sframe) { int64_t file_size = (int64_t) path_stat.st_size; if (offset > file_size || frame_len > file_size - offset) { BLOSC_TRACE_ERROR("Frame length exceeds file boundary in file '%s'.", urlpath); io_cb->close(fp); free(urlpath_cpy); return NULL; } } if (offset > INT64_MAX - (frame_len - FRAME_TRAILER_MINLEN)) { BLOSC_TRACE_ERROR("Frame offset arithmetic overflows in file '%s'.", urlpath); io_cb->close(fp); free(urlpath_cpy); return NULL; } blosc2_frame_s* frame = calloc(1, sizeof(blosc2_frame_s)); if (frame == NULL) { BLOSC_TRACE_ERROR("Cannot allocate memory for frame metadata."); io_cb->close(fp); free(urlpath_cpy); return NULL; } frame->urlpath = urlpath_cpy; frame->len = frame_len; frame->sframe = sframe; frame->file_offset = offset; // Now, the trailer length if (io_cb->is_allocation_necessary) trailer_ptr = trailer; io_pos = offset + frame_len - FRAME_TRAILER_MINLEN; rbytes = io_cb->read((void**)&trailer_ptr, 1, FRAME_TRAILER_MINLEN, io_pos, fp); io_cb->close(fp); if (rbytes != FRAME_TRAILER_MINLEN) { BLOSC_TRACE_ERROR("Cannot read from file '%s'.", urlpath); free(urlpath_cpy); free(frame); return NULL; } int trailer_offset = FRAME_TRAILER_MINLEN - FRAME_TRAILER_LEN_OFFSET; if (trailer_ptr[trailer_offset - 1] != 0xce) { BLOSC_TRACE_ERROR("Invalid trailer in file '%s'.", urlpath); free(urlpath_cpy); free(frame); return NULL; } uint32_t trailer_len; to_big(&trailer_len, trailer_ptr + trailer_offset, sizeof(trailer_len)); if (trailer_len < FRAME_TRAILER_MINLEN || trailer_len > INT32_MAX || (int64_t)trailer_len > frame_len || (int64_t)trailer_len > frame_len - FRAME_HEADER_MINLEN) { BLOSC_TRACE_ERROR("Invalid trailer length (%" PRIu32 ") in file '%s'.", trailer_len, urlpath); free(urlpath_cpy); free(frame); return NULL; } frame->trailer_len = trailer_len; return frame; } /* Initialize a frame out of a contiguous frame buffer */ blosc2_frame_s* frame_from_cframe(uint8_t *cframe, int64_t len, bool copy) { // Get the length of the frame const uint8_t* header = cframe; int64_t frame_len; if (len < FRAME_HEADER_MINLEN) { return NULL; } from_big(&frame_len, header + FRAME_LEN, sizeof(frame_len)); if (frame_len != len) { // sanity check return NULL; } blosc2_frame_s* frame = calloc(1, sizeof(blosc2_frame_s)); frame->len = frame_len; frame->file_offset = 0; // Now, the trailer length const uint8_t* trailer = cframe + frame_len - FRAME_TRAILER_MINLEN; int trailer_offset = FRAME_TRAILER_MINLEN - FRAME_TRAILER_LEN_OFFSET; if (trailer[trailer_offset - 1] != 0xce) { free(frame); return NULL; } uint32_t trailer_len; from_big(&trailer_len, trailer + trailer_offset, sizeof(trailer_len)); if (trailer_len < FRAME_TRAILER_MINLEN || trailer_len > INT32_MAX || (int64_t)trailer_len > frame_len || (int64_t)trailer_len > frame_len - FRAME_HEADER_MINLEN) { free(frame); return NULL; } frame->trailer_len = trailer_len; if (copy) { frame->cframe = malloc((size_t)len); memcpy(frame->cframe, cframe, (size_t)len); } else { frame->cframe = cframe; frame->avoid_cframe_free = true; } return frame; } /* Create a frame out of a super-chunk. */ int64_t frame_from_schunk(blosc2_schunk *schunk, blosc2_frame_s *frame) { frame->file_offset = 0; int64_t nchunks = schunk->nchunks; int64_t cbytes = schunk->cbytes; int32_t chunk_cbytes; int32_t chunk_nbytes; void* fp = NULL; int rc; uint8_t* h2 = new_header_frame(schunk, frame); if (h2 == NULL) { return BLOSC2_ERROR_DATA; } uint32_t h2len; from_big(&h2len, h2 + FRAME_HEADER_LEN, sizeof(h2len)); // Build the offsets chunk int32_t chunksize = -1; int32_t off_cbytes = 0; uint64_t coffset = 0; int32_t off_nbytes; if (!blosc2_nchunks_to_offsets_nbytes(nchunks, &off_nbytes)) { BLOSC_TRACE_ERROR("Too many chunks for offsets representation."); free(h2); return BLOSC2_ERROR_DATA; } uint64_t* data_tmp = malloc((size_t)off_nbytes); if (data_tmp == NULL) { BLOSC_TRACE_ERROR("Cannot allocate memory for offset data."); free(h2); return BLOSC2_ERROR_MEMORY_ALLOC; } bool needs_free = false; for (int64_t i = 0; i < nchunks; i++) { uint8_t* data_chunk; data_chunk = schunk->data[i]; rc = blosc2_cbuffer_sizes(data_chunk, &chunk_nbytes, &chunk_cbytes, NULL); if (rc < 0) { return rc; } data_tmp[i] = coffset; coffset += chunk_cbytes; int32_t chunksize_ = chunk_nbytes; if (i == 0) { chunksize = chunksize_; } else if (chunksize != chunksize_) { // Variable size // TODO: update flags for this (or do not use them at all) chunksize = 0; } if (needs_free) { free(data_chunk); } } if ((int64_t)coffset != cbytes) { free(data_tmp); return BLOSC2_ERROR_DATA; } uint8_t *off_chunk = NULL; if (nchunks > 0) { // Compress the chunk of offsets off_chunk = malloc(off_nbytes + BLOSC2_MAX_OVERHEAD); blosc2_context *cctx = blosc2_create_cctx(BLOSC2_CPARAMS_DEFAULTS); if (cctx == NULL) { BLOSC_TRACE_ERROR("Error while creating the compression context"); return BLOSC2_ERROR_NULL_POINTER; } cctx->typesize = sizeof(int64_t); off_cbytes = blosc2_compress_ctx(cctx, data_tmp, off_nbytes, off_chunk, off_nbytes + BLOSC2_MAX_OVERHEAD); blosc2_free_ctx(cctx); if (off_cbytes < 0) { free(off_chunk); free(h2); return off_cbytes; } } else { off_cbytes = 0; } free(data_tmp); // Now that we know them, fill the chunksize and frame length in header to_big(h2 + FRAME_CHUNKSIZE, &chunksize, sizeof(chunksize)); if (chunksize == 0) { h2[FRAME_FLAGS] |= FRAME_VARIABLE_CHUNKS; } else { h2[FRAME_FLAGS] &= (uint8_t)~FRAME_VARIABLE_CHUNKS; } if (schunk->chunksize == 0 || (schunk->flags2 & BLOSC2_VL_BLOCKS)) { h2[FRAME_FLAGS] = (uint8_t)((h2[FRAME_FLAGS] & (uint8_t)~0x0fu) | BLOSC2_VERSION_FRAME_FORMAT_VL_BLOCKS); h2[FRAME_FLAGS] |= FRAME_VL_BLOCKS; } else { h2[FRAME_FLAGS] = (uint8_t)((h2[FRAME_FLAGS] & (uint8_t)~0x0fu) | BLOSC2_VERSION_FRAME_FORMAT_RC1); h2[FRAME_FLAGS] &= (uint8_t)~FRAME_VL_BLOCKS; } frame->len = h2len + cbytes + off_cbytes + FRAME_TRAILER_MINLEN; if (frame->sframe) { frame->len = h2len + off_cbytes + FRAME_TRAILER_MINLEN; } int64_t tbytes = frame->len; to_big(h2 + FRAME_LEN, &tbytes, sizeof(tbytes)); blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); return BLOSC2_ERROR_PLUGIN_IO; } // Create the frame and put the header at the beginning int64_t io_pos = 0; if (frame->urlpath == NULL) { frame->cframe = malloc((size_t)frame->len); memcpy(frame->cframe, h2, h2len); } else { if (frame->sframe) { fp = sframe_open_index(frame->urlpath, "wb", frame->schunk->storage->io); } else { fp = io_cb->open(frame->urlpath, "wb", frame->schunk->storage->io->params); } if (fp == NULL) { BLOSC_TRACE_ERROR("Error creating file in: %s", frame->urlpath); return BLOSC2_ERROR_FILE_OPEN; } io_cb->write(h2, h2len, 1, io_pos, fp); io_pos += h2len; } free(h2); // Fill the frame with the actual data chunks if (!frame->sframe) { coffset = 0; for (int64_t i = 0; i < nchunks; i++) { uint8_t* data_chunk = schunk->data[i]; rc = blosc2_cbuffer_sizes(data_chunk, NULL, &chunk_cbytes, NULL); if (rc < 0) { return rc; } if (frame->urlpath == NULL) { memcpy(frame->cframe + h2len + coffset, data_chunk, (size_t)chunk_cbytes); } else { io_cb->write(data_chunk, chunk_cbytes, 1, io_pos, fp); io_pos += chunk_cbytes; } coffset += chunk_cbytes; } if ((int64_t)coffset != cbytes) { return BLOSC2_ERROR_FAILURE; } } // Copy the offsets chunk at the end of the frame if (frame->urlpath == NULL) { memcpy(frame->cframe + h2len + cbytes, off_chunk, off_cbytes); } else { io_cb->write(off_chunk, off_cbytes, 1, io_pos, fp); io_cb->close(fp); } free(off_chunk); rc = frame_update_trailer(frame, schunk); if (rc < 0) { return rc; } return frame->len; } // Get the compressed data offsets uint8_t* get_coffsets(blosc2_frame_s *frame, int32_t header_len, int64_t cbytes, int64_t nchunks, int32_t *off_cbytes) { int32_t chunk_cbytes; int rc; if (frame->coffsets != NULL) { if (off_cbytes != NULL) { rc = blosc2_cbuffer_sizes(frame->coffsets, NULL, &chunk_cbytes, NULL); if (rc < 0) { return NULL; } *off_cbytes = (int32_t)chunk_cbytes; } return frame->coffsets; } if (frame->cframe != NULL) { int64_t off_pos = header_len; if (cbytes < INT64_MAX - header_len) { off_pos += cbytes; } // Check that there is enough room to read Blosc header if (off_pos < 0 || off_pos > INT64_MAX - BLOSC_EXTENDED_HEADER_LENGTH || off_pos + BLOSC_EXTENDED_HEADER_LENGTH > frame->len) { BLOSC_TRACE_ERROR("Cannot read the offsets outside of frame boundary."); return NULL; } // For in-memory frames, the coffset is just one pointer away uint8_t* off_start = frame->cframe + off_pos; if (off_cbytes != NULL) { int32_t chunk_nbytes; int32_t chunk_blocksize; rc = blosc2_cbuffer_sizes(off_start, &chunk_nbytes, &chunk_cbytes, &chunk_blocksize); if (rc < 0) { return NULL; } *off_cbytes = (int32_t)chunk_cbytes; if (*off_cbytes < 0 || off_pos + *off_cbytes > frame->len) { BLOSC_TRACE_ERROR("Cannot read the cbytes outside of frame boundary."); return NULL; } int32_t expected_off_nbytes; if (!blosc2_nchunks_to_offsets_nbytes(nchunks, &expected_off_nbytes)) { BLOSC_TRACE_ERROR("Too many chunks for offsets representation."); return NULL; } if (chunk_nbytes != expected_off_nbytes) { BLOSC_TRACE_ERROR("The number of chunks in offset idx " "does not match the ones in the header frame."); return NULL; } } return off_start; } int64_t trailer_offset = get_trailer_offset(frame, header_len, true); if (trailer_offset < BLOSC_EXTENDED_HEADER_LENGTH || trailer_offset + FRAME_TRAILER_MINLEN > frame->len) { BLOSC_TRACE_ERROR("Cannot read the trailer out of the frame."); return NULL; } int64_t coffsets_cbytes64; if (frame->sframe) { coffsets_cbytes64 = trailer_offset - header_len; } else { coffsets_cbytes64 = trailer_offset - (header_len + cbytes); } if (coffsets_cbytes64 <= 0 || coffsets_cbytes64 > INT32_MAX) { BLOSC_TRACE_ERROR("Offsets chunk size is out of bounds."); return NULL; } int32_t coffsets_cbytes = (int32_t)coffsets_cbytes64; if (off_cbytes != NULL) { *off_cbytes = coffsets_cbytes; } blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); return NULL; } void* fp = NULL; uint8_t* coffsets; if (io_cb->is_allocation_necessary) { coffsets = malloc((size_t)coffsets_cbytes); frame->coffsets_needs_free = true; } else { frame->coffsets_needs_free = false; } int64_t io_pos = 0; if (frame->sframe) { fp = sframe_open_index(frame->urlpath, "rb", frame->schunk->storage->io); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return NULL; } io_pos = header_len + 0; } else { fp = io_cb->open(frame->urlpath, "rb", frame->schunk->storage->io->params); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return NULL; } io_pos = frame->file_offset + header_len + cbytes; } int64_t rbytes = io_cb->read((void**)&coffsets, 1, coffsets_cbytes, io_pos, fp); io_cb->close(fp); if (rbytes != coffsets_cbytes) { BLOSC_TRACE_ERROR("Cannot read the offsets out of the frame."); if (frame->coffsets_needs_free) free(coffsets); return NULL; } frame->coffsets = coffsets; return coffsets; } // Get the data offsets from a frame int64_t* blosc2_frame_get_offsets(blosc2_schunk *schunk) { if (schunk->frame == NULL) { BLOSC_TRACE_ERROR("This function needs a frame."); return NULL; } blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; // Get header info int32_t header_len; int64_t frame_len; int64_t nbytes; int64_t cbytes; int32_t blocksize; int32_t chunksize; int64_t nchunks; int ret = get_header_info(frame, &header_len, &frame_len, &nbytes, &cbytes, &blocksize, &chunksize, &nchunks, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, frame->schunk->storage->io); if (ret < 0) { BLOSC_TRACE_ERROR("Cannot get the header info for the frame."); return NULL; } int32_t off_nbytes; if (!blosc2_nchunks_to_offsets_nbytes(nchunks, &off_nbytes)) { BLOSC_TRACE_ERROR("Too many chunks for offsets representation."); return NULL; } int64_t* offsets = (int64_t *) malloc((size_t)off_nbytes); if (offsets == NULL) { BLOSC_TRACE_ERROR("Cannot allocate memory for offsets."); return NULL; } int32_t coffsets_cbytes = 0; uint8_t *coffsets = get_coffsets(frame, header_len, cbytes, nchunks, &coffsets_cbytes); // Decompress offsets blosc2_dparams off_dparams = BLOSC2_DPARAMS_DEFAULTS; blosc2_context *dctx = blosc2_create_dctx(off_dparams); if (dctx == NULL) { BLOSC_TRACE_ERROR("Error while creating the decompression context"); return NULL; } int32_t prev_nbytes = blosc2_decompress_ctx(dctx, coffsets, coffsets_cbytes, offsets, off_nbytes); blosc2_free_ctx(dctx); if (prev_nbytes < 0) { free(offsets); BLOSC_TRACE_ERROR("Cannot decompress the offsets chunk."); return NULL; } return offsets; } int frame_update_header(blosc2_frame_s* frame, blosc2_schunk* schunk, bool new) { uint8_t* framep = frame->cframe; uint8_t* header_ptr; uint8_t header[FRAME_HEADER_MINLEN]; if (frame->len <= 0) { return BLOSC2_ERROR_INVALID_PARAM; } if (new && schunk->cbytes > 0) { BLOSC_TRACE_ERROR("New metalayers cannot be added after actual data " "has been appended."); return BLOSC2_ERROR_INVALID_PARAM; } blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); return BLOSC2_ERROR_PLUGIN_IO; } if (frame->cframe == NULL) { int64_t rbytes = 0; void* fp = NULL; int64_t io_pos = 0; if (frame->sframe) { fp = sframe_open_index(frame->urlpath, "rb+", frame->schunk->storage->io); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return BLOSC2_ERROR_FILE_OPEN; } } else { fp = io_cb->open(frame->urlpath, "rb", frame->schunk->storage->io->params); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return BLOSC2_ERROR_FILE_OPEN; } io_pos = frame->file_offset; } if (fp != NULL) { if (io_cb->is_allocation_necessary) header_ptr = header; rbytes = io_cb->read((void**)&header_ptr, 1, FRAME_HEADER_MINLEN, io_pos, fp); io_cb->close(fp); } (void) rbytes; if (rbytes != FRAME_HEADER_MINLEN) { return BLOSC2_ERROR_FILE_WRITE; } framep = header_ptr; } uint32_t prev_h2len; from_big(&prev_h2len, framep + FRAME_HEADER_LEN, sizeof(prev_h2len)); // Build a new header uint8_t* h2 = new_header_frame(schunk, frame); uint32_t h2len; from_big(&h2len, h2 + FRAME_HEADER_LEN, sizeof(h2len)); // The frame length is outdated when adding a new metalayer, so update it if (new) { int64_t frame_len = h2len; // at adding time, we only have to worry of the header for now to_big(h2 + FRAME_LEN, &frame_len, sizeof(frame_len)); frame->len = frame_len; } if (!new && prev_h2len != h2len) { BLOSC_TRACE_ERROR("The new metalayer sizes should be equal the existing ones."); return BLOSC2_ERROR_DATA; } void* fp = NULL; if (frame->cframe == NULL) { // Write updated header down to file if (frame->sframe) { fp = sframe_open_index(frame->urlpath, "rb+", frame->schunk->storage->io); } else { fp = io_cb->open(frame->urlpath, "rb+", frame->schunk->storage->io->params); } if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return BLOSC2_ERROR_FILE_OPEN; } int64_t io_pos = frame->file_offset; io_cb->write(h2, h2len, 1, io_pos, fp); io_cb->close(fp); } else { if (new) { frame->cframe = realloc(frame->cframe, h2len); } memcpy(frame->cframe, h2, h2len); } free(h2); return 1; } static int get_meta_from_header(blosc2_frame_s* frame, blosc2_schunk* schunk, uint8_t* header, int32_t header_len) { BLOSC_UNUSED_PARAM(frame); int64_t header_pos = FRAME_IDX_SIZE; // Get the size for the index of metalayers uint16_t idx_size; header_pos += sizeof(idx_size); if (header_len < header_pos) { return BLOSC2_ERROR_READ_BUFFER; } from_big(&idx_size, header + FRAME_IDX_SIZE, sizeof(idx_size)); // Get the actual index of metalayers uint8_t* metalayers_idx = header + FRAME_IDX_SIZE + 2; header_pos += 1; if (header_len < header_pos) { return BLOSC2_ERROR_READ_BUFFER; } if (metalayers_idx[0] != 0xde) { // sanity check return BLOSC2_ERROR_DATA; } uint8_t* idxp = metalayers_idx + 1; uint16_t nmetalayers; header_pos += sizeof(nmetalayers); if (header_len < header_pos) { return BLOSC2_ERROR_READ_BUFFER; } from_big(&nmetalayers, idxp, sizeof(uint16_t)); idxp += 2; if (nmetalayers > BLOSC2_MAX_METALAYERS) { return BLOSC2_ERROR_DATA; } schunk->nmetalayers = nmetalayers; // Populate the metalayers and its serialized values for (int nmetalayer = 0; nmetalayer < nmetalayers; nmetalayer++) { header_pos += 1; if (header_len < header_pos) { return BLOSC2_ERROR_READ_BUFFER; } if ((*idxp & 0xe0u) != 0xa0u) { // sanity check return BLOSC2_ERROR_DATA; } blosc2_metalayer* metalayer = calloc(1, sizeof(blosc2_metalayer)); if (metalayer == NULL) { return BLOSC2_ERROR_MEMORY_ALLOC; } schunk->metalayers[nmetalayer] = metalayer; // Populate the metalayer string uint8_t nslen = *idxp & (uint8_t)0x1F; idxp += 1; header_pos += nslen; if (header_len < header_pos) { return BLOSC2_ERROR_READ_BUFFER; } char* ns = malloc((size_t)nslen + 1); if (ns == NULL) { return BLOSC2_ERROR_MEMORY_ALLOC; } memcpy(ns, idxp, nslen); ns[nslen] = '\0'; idxp += nslen; metalayer->name = ns; // Populate the serialized value for this metalayer // Get the offset header_pos += 1; if (header_len < header_pos) { return BLOSC2_ERROR_READ_BUFFER; } if ((*idxp & 0xffu) != 0xd2u) { // sanity check return BLOSC2_ERROR_DATA; } idxp += 1; int32_t offset; header_pos += sizeof(offset); if (header_len < header_pos) { return BLOSC2_ERROR_READ_BUFFER; } from_big(&offset, idxp, sizeof(offset)); idxp += 4; if (offset < 0 || offset >= header_len) { // Offset is less than zero or exceeds header length return BLOSC2_ERROR_DATA; } // Go to offset and see if we have the correct marker uint8_t* content_marker = header + offset; if ((int64_t)header_len < (int64_t)offset + 1 + 4) { return BLOSC2_ERROR_READ_BUFFER; } if (*content_marker != 0xc6) { return BLOSC2_ERROR_DATA; } // Read the size of the content int32_t content_len; from_big(&content_len, content_marker + 1, sizeof(content_len)); if (content_len < 0) { return BLOSC2_ERROR_DATA; } metalayer->content_len = content_len; // Finally, read the content. Use 64-bit arithmetic so a malicious // (offset, content_len) pair near INT32_MAX cannot wrap and bypass the // bounds check, which would let memcpy below read past the header buffer. if ((int64_t)header_len < (int64_t)offset + 1 + 4 + (int64_t)content_len) { return BLOSC2_ERROR_READ_BUFFER; } char* content = malloc((size_t)content_len); // malloc(0) is allowed to return NULL, and blosc2_meta_add accepts // content_len == 0, so only treat NULL as an allocation failure when // a non-zero allocation was requested. if (content_len > 0 && content == NULL) { return BLOSC2_ERROR_MEMORY_ALLOC; } if (content_len > 0) { memcpy(content, content_marker + 1 + 4, (size_t)content_len); } metalayer->content = (uint8_t*)content; } return 1; } int frame_get_metalayers(blosc2_frame_s* frame, blosc2_schunk* schunk) { int32_t header_len; int64_t frame_len; int64_t nbytes; int64_t cbytes; int32_t blocksize; int32_t chunksize; int64_t nchunks; int ret = get_header_info(frame, &header_len, &frame_len, &nbytes, &cbytes, &blocksize, &chunksize, &nchunks, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, schunk->storage->io); if (ret < 0) { BLOSC_TRACE_ERROR("Unable to get the header info from frame."); return ret; } // Get the header uint8_t* header = NULL; bool needs_free = false; if (frame->cframe != NULL) { header = frame->cframe; } else { int64_t rbytes = 0; blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); return BLOSC2_ERROR_PLUGIN_IO; } if (io_cb->is_allocation_necessary) { header = malloc(header_len); needs_free = true; } else { needs_free = false; } void* fp = NULL; int64_t io_pos = 0; if (frame->sframe) { fp = sframe_open_index(frame->urlpath, "rb", frame->schunk->storage->io); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return BLOSC2_ERROR_FILE_OPEN; } } else { fp = io_cb->open(frame->urlpath, "rb", frame->schunk->storage->io->params); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return BLOSC2_ERROR_FILE_OPEN; } io_pos = frame->file_offset; } if (fp != NULL) { rbytes = io_cb->read((void**)&header, 1, header_len, io_pos, fp); io_cb->close(fp); } if (rbytes != header_len) { BLOSC_TRACE_ERROR("Cannot access the header out of the frame."); if (needs_free) free(header); return BLOSC2_ERROR_FILE_READ; } } ret = get_meta_from_header(frame, schunk, header, header_len); if (frame->cframe == NULL && needs_free) { free(header); } return ret; } static int get_vlmeta_from_trailer(blosc2_frame_s* frame, blosc2_schunk* schunk, uint8_t* trailer, int32_t trailer_len) { BLOSC_UNUSED_PARAM(frame); int64_t trailer_pos = FRAME_TRAILER_VLMETALAYERS + 2; uint8_t* idxp = trailer + trailer_pos; // Get the size for the index of metalayers trailer_pos += 2; if (trailer_len < trailer_pos) { return BLOSC2_ERROR_READ_BUFFER; } uint16_t idx_size; from_big(&idx_size, idxp, sizeof(idx_size)); idxp += 2; trailer_pos += 1; // Get the actual index of metalayers if (trailer_len < trailer_pos) { return BLOSC2_ERROR_READ_BUFFER; } if (idxp[0] != 0xde) { // sanity check return BLOSC2_ERROR_DATA; } idxp += 1; // The on-wire encoding is unsigned (uint16 follows the msgpack `0xde` // map16 marker), but `schunk->nvlmetalayers` is int16_t. Decoding into a // signed local would let raw bytes like 0xff 0xff slip past the // `> BLOSC2_MAX_VLMETALAYERS` ceiling as a negative count, after which // a later `blosc2_vlmeta_add` would write through `vlmetalayers[-1]` // and corrupt adjacent schunk fields. Decode unsigned, then narrow. uint16_t nmetalayers; trailer_pos += sizeof(nmetalayers); if (trailer_len < trailer_pos) { return BLOSC2_ERROR_READ_BUFFER; } from_big(&nmetalayers, idxp, sizeof(uint16_t)); idxp += 2; if (nmetalayers > BLOSC2_MAX_VLMETALAYERS) { return BLOSC2_ERROR_DATA; } schunk->nvlmetalayers = (int16_t)nmetalayers; // Populate the metalayers and its serialized values for (int nmetalayer = 0; nmetalayer < nmetalayers; nmetalayer++) { trailer_pos += 1; if (trailer_len < trailer_pos) { return BLOSC2_ERROR_READ_BUFFER; } if ((*idxp & 0xe0u) != 0xa0u) { // sanity check return BLOSC2_ERROR_DATA; } blosc2_metalayer* metalayer = calloc(1, sizeof(blosc2_metalayer)); if (metalayer == NULL) { return BLOSC2_ERROR_MEMORY_ALLOC; } schunk->vlmetalayers[nmetalayer] = metalayer; // Populate the metalayer string uint8_t nslen = *idxp & (uint8_t)0x1F; idxp += 1; trailer_pos += nslen; if (trailer_len < trailer_pos) { return BLOSC2_ERROR_READ_BUFFER; } char* ns = malloc((size_t)nslen + 1); if (ns == NULL) { return BLOSC2_ERROR_MEMORY_ALLOC; } memcpy(ns, idxp, nslen); ns[nslen] = '\0'; idxp += nslen; metalayer->name = ns; // Populate the serialized value for this metalayer // Get the offset trailer_pos += 1; if (trailer_len < trailer_pos) { return BLOSC2_ERROR_READ_BUFFER; } if ((*idxp & 0xffu) != 0xd2u) { // sanity check return BLOSC2_ERROR_DATA; } idxp += 1; int32_t offset; trailer_pos += sizeof(offset); if (trailer_len < trailer_pos) { return BLOSC2_ERROR_READ_BUFFER; } from_big(&offset, idxp, sizeof(offset)); idxp += 4; if (offset < 0 || offset >= trailer_len) { // Offset is less than zero or exceeds trailer length return BLOSC2_ERROR_DATA; } // Go to offset and see if we have the correct marker uint8_t* content_marker = trailer + offset; if ((int64_t)trailer_len < (int64_t)offset + 1 + 4) { return BLOSC2_ERROR_READ_BUFFER; } if (*content_marker != 0xc6) { return BLOSC2_ERROR_DATA; } // Read the size of the content int32_t content_len; from_big(&content_len, content_marker + 1, sizeof(content_len)); if (content_len < 0) { return BLOSC2_ERROR_DATA; } metalayer->content_len = content_len; // Finally, read the content. Use 64-bit arithmetic so a malicious // (offset, content_len) pair near INT32_MAX cannot wrap and bypass the // bounds check, which would let memcpy below read past the trailer buffer. if ((int64_t)trailer_len < (int64_t)offset + 1 + 4 + (int64_t)content_len) { return BLOSC2_ERROR_READ_BUFFER; } char* content = malloc((size_t)content_len); // Same zero-length carve-out as in get_meta_from_header: malloc(0) may // return NULL on some platforms, and the public API allows // content_len == 0, so don't reject a valid empty vlmetalayer. if (content_len > 0 && content == NULL) { return BLOSC2_ERROR_MEMORY_ALLOC; } if (content_len > 0) { memcpy(content, content_marker + 1 + 4, (size_t)content_len); } metalayer->content = (uint8_t*)content; } return 1; } int frame_get_vlmetalayers(blosc2_frame_s* frame, blosc2_schunk* schunk) { int32_t header_len; int64_t frame_len; int64_t nbytes; int64_t cbytes; int32_t blocksize; int32_t chunksize; int64_t nchunks; int ret = get_header_info(frame, &header_len, &frame_len, &nbytes, &cbytes, &blocksize, &chunksize, &nchunks, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, schunk->storage->io); if (ret < 0) { BLOSC_TRACE_ERROR("Unable to get the trailer info from frame."); return ret; } int64_t trailer_offset = get_trailer_offset(frame, header_len, nbytes > 0); int32_t trailer_len = (int32_t) frame->trailer_len; if (trailer_offset < BLOSC_EXTENDED_HEADER_LENGTH || trailer_offset + trailer_len > frame->len) { BLOSC_TRACE_ERROR("Cannot access the trailer out of the frame."); return BLOSC2_ERROR_READ_BUFFER; } // Get the trailer uint8_t* trailer = NULL; bool needs_free = false; if (frame->cframe != NULL) { trailer = frame->cframe + trailer_offset; } else { int64_t rbytes = 0; blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); return BLOSC2_ERROR_PLUGIN_IO; } if (io_cb->is_allocation_necessary) { trailer = malloc(trailer_len); needs_free = true; } else { needs_free = false; } void* fp = NULL; int64_t io_pos = 0; if (frame->sframe) { // Check for overflow before calculating length if (strlen(frame->urlpath) > SIZE_MAX - strlen("/chunks.b2frame") - 1) { BLOSC_TRACE_ERROR("Path too long for frame filename."); if (needs_free) free(trailer); return BLOSC2_ERROR_INVALID_PARAM; } size_t _len = strlen(frame->urlpath) + strlen("/chunks.b2frame") + 1; char* eframe_name = malloc(_len); if (eframe_name == NULL) { BLOSC_TRACE_ERROR("Unable to allocate memory for frame filename."); if (needs_free) free(trailer); return BLOSC2_ERROR_MEMORY_ALLOC; } int _w = snprintf(eframe_name, _len, "%s/chunks.b2frame", frame->urlpath); if (_w < 0 || (size_t)_w >= _len) { BLOSC_TRACE_ERROR("Error building frame filename"); free(eframe_name); if (needs_free) free(trailer); return BLOSC2_ERROR_INVALID_PARAM; } fp = io_cb->open(eframe_name, "rb", frame->schunk->storage->io->params); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", eframe_name); free(eframe_name); return BLOSC2_ERROR_FILE_OPEN; } free(eframe_name); io_pos = trailer_offset; } else { fp = io_cb->open(frame->urlpath, "rb", frame->schunk->storage->io->params); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return BLOSC2_ERROR_FILE_OPEN; } io_pos = frame->file_offset + trailer_offset; } if (fp != NULL) { rbytes = io_cb->read((void**)&trailer, 1, trailer_len, io_pos, fp); io_cb->close(fp); } if (rbytes != trailer_len) { BLOSC_TRACE_ERROR("Cannot access the trailer out of the fileframe."); if (needs_free) free(trailer); return BLOSC2_ERROR_FILE_READ; } } ret = get_vlmeta_from_trailer(frame, schunk, trailer, trailer_len); if (frame->cframe == NULL && needs_free) { free(trailer); } return ret; } blosc2_storage* get_new_storage(const blosc2_storage* storage, const blosc2_cparams* cdefaults, const blosc2_dparams* ddefaults, const blosc2_io* iodefaults) { blosc2_storage* new_storage = (blosc2_storage*)calloc(1, sizeof(blosc2_storage)); memcpy(new_storage, storage, sizeof(blosc2_storage)); if (storage->urlpath != NULL) { char* urlpath = normalize_urlpath(storage->urlpath); new_storage->urlpath = malloc(strlen(urlpath) + 1); strcpy(new_storage->urlpath, urlpath); } // cparams blosc2_cparams* cparams = malloc(sizeof(blosc2_cparams)); if (storage->cparams != NULL) { memcpy(cparams, storage->cparams, sizeof(blosc2_cparams)); } else { memcpy(cparams, cdefaults, sizeof(blosc2_cparams)); } new_storage->cparams = cparams; // dparams blosc2_dparams* dparams = malloc(sizeof(blosc2_dparams)); if (storage->dparams != NULL) { memcpy(dparams, storage->dparams, sizeof(blosc2_dparams)); } else { memcpy(dparams, ddefaults, sizeof(blosc2_dparams)); } new_storage->dparams = dparams; // iodefaults blosc2_io* udio = malloc(sizeof(blosc2_io)); if (storage->io != NULL) { memcpy(udio, storage->io, sizeof(blosc2_io)); } else { memcpy(udio, iodefaults, sizeof(blosc2_io)); } new_storage->io = udio; return new_storage; } static int validate_offsets_chunk(blosc2_frame_s* frame, int32_t header_len, int64_t cbytes, int64_t nchunks) { int32_t coffsets_cbytes = 0; uint8_t* coffsets = get_coffsets(frame, header_len, cbytes, nchunks, &coffsets_cbytes); if (coffsets == NULL) { BLOSC_TRACE_ERROR("Cannot get compressed offsets from frame."); return BLOSC2_ERROR_DATA; } int32_t offsets_nbytes; if (!blosc2_nchunks_to_offsets_nbytes(nchunks, &offsets_nbytes)) { BLOSC_TRACE_ERROR("Too many chunks for offsets representation."); return BLOSC2_ERROR_FAILURE; } int64_t* offsets = (int64_t*)malloc((size_t)offsets_nbytes); if (offsets == NULL) { BLOSC_TRACE_ERROR("Cannot allocate memory for offsets validation."); return BLOSC2_ERROR_MEMORY_ALLOC; } blosc2_dparams off_dparams = BLOSC2_DPARAMS_DEFAULTS; blosc2_context* dctx = blosc2_create_dctx(off_dparams); if (dctx == NULL) { free(offsets); BLOSC_TRACE_ERROR("Error while creating the decompression context."); return BLOSC2_ERROR_FAILURE; } int32_t off_nbytes = blosc2_decompress_ctx(dctx, coffsets, coffsets_cbytes, offsets, offsets_nbytes); blosc2_free_ctx(dctx); if (off_nbytes != offsets_nbytes) { free(offsets); BLOSC_TRACE_ERROR("Cannot decompress offsets chunk."); return BLOSC2_ERROR_DATA; } for (int64_t i = 0; i < nchunks; ++i) { int64_t offset = offsets[i]; if (offset < 0) { continue; } if (offset > INT64_MAX - header_len || offset > cbytes - BLOSC_EXTENDED_HEADER_LENGTH) { free(offsets); BLOSC_TRACE_ERROR("Offset for chunk %" PRId64 " is out of bounds.", i); return BLOSC2_ERROR_INVALID_HEADER; } /* Offsets do not need to be monotonic. Frame mutations like insert can append * chunk payloads at the end of the cframe while updating the logical chunk order * only in the offsets table, so a valid frame may legitimately contain * non-monotonic non-negative offsets. */ } free(offsets); return 0; } /* Get a super-chunk out of a frame */ blosc2_schunk* frame_to_schunk(blosc2_frame_s* frame, bool copy, const blosc2_io *udio) { int32_t header_len; int64_t frame_len; int rc; bool frame_attached = true; blosc2_schunk* schunk = calloc(1, sizeof(blosc2_schunk)); blosc2_cparams *cparams = NULL; blosc2_dparams *dparams = NULL; int64_t *offsets = NULL; if (schunk == NULL) { return NULL; } schunk->frame = (blosc2_frame*)frame; frame->schunk = schunk; rc = get_header_info(frame, &header_len, &frame_len, &schunk->nbytes, &schunk->cbytes, &schunk->blocksize, &schunk->chunksize, &schunk->nchunks, &schunk->typesize, &schunk->compcode, &schunk->compcode_meta, &schunk->clevel, schunk->filters, schunk->filters_meta, &schunk->splitmode, &schunk->use_dict, udio); if (rc < 0) { BLOSC_TRACE_ERROR("Unable to get meta info from frame."); goto error; } int64_t nchunks = schunk->nchunks; int64_t nbytes = schunk->nbytes; (void) nbytes; int64_t cbytes = schunk->cbytes; // Compression and decompression contexts blosc2_schunk_get_cparams(schunk, &cparams); schunk->cctx = blosc2_create_cctx(*cparams); if (schunk->cctx == NULL) { BLOSC_TRACE_ERROR("Error while creating the compression context"); goto error; } blosc2_schunk_get_dparams(schunk, &dparams); schunk->dctx = blosc2_create_dctx(*dparams); if (schunk->dctx == NULL) { BLOSC_TRACE_ERROR("Error while creating the decompression context"); goto error; } blosc2_storage storage = {.contiguous = copy ? false : true}; schunk->storage = get_new_storage(&storage, cparams, dparams, udio); free(cparams); cparams = NULL; free(dparams); dparams = NULL; if (nchunks > 0) { rc = validate_offsets_chunk(frame, header_len, cbytes, nchunks); if (rc < 0) { BLOSC_TRACE_ERROR("Cannot validate frame offsets."); goto error; } } if (nchunks > 0) { uint8_t *chunk; bool needs_free; // A lazy chunk is enough here: only the header is needed for the flags2 byte rc = frame_get_lazychunk(frame, 0, &chunk, &needs_free); if (rc < 0) { BLOSC_TRACE_ERROR("Cannot inspect the first chunk in frame."); goto error; } schunk->flags2 = chunk[BLOSC2_CHUNK_BLOSC2_FLAGS2]; if (needs_free) { free(chunk); } } if (!copy) { goto out; } // We are not attached to a frame anymore schunk->frame = NULL; frame_attached = false; if (nchunks == 0) { frame->schunk = NULL; goto out; } // Get the compressed offsets int32_t coffsets_cbytes = 0; uint8_t* coffsets = get_coffsets(frame, header_len, cbytes, nchunks, &coffsets_cbytes); if (coffsets == NULL) { BLOSC_TRACE_ERROR("Cannot get the offsets for the frame."); goto error; } // Decompress offsets blosc2_dparams off_dparams = BLOSC2_DPARAMS_DEFAULTS; blosc2_context *dctx = blosc2_create_dctx(off_dparams); if (dctx == NULL) { BLOSC_TRACE_ERROR("Error while creating the decompression context"); goto error; } int32_t offsets_nbytes; if (!blosc2_nchunks_to_offsets_nbytes(nchunks, &offsets_nbytes)) { blosc2_free_ctx(dctx); BLOSC_TRACE_ERROR("Too many chunks for offsets representation."); goto error; } offsets = (int64_t *) malloc((size_t)offsets_nbytes); if (offsets == NULL) { blosc2_free_ctx(dctx); BLOSC_TRACE_ERROR("Cannot allocate memory for offsets."); goto error; } int32_t off_nbytes = blosc2_decompress_ctx(dctx, coffsets, coffsets_cbytes, offsets, offsets_nbytes); blosc2_free_ctx(dctx); if (off_nbytes < 0) { BLOSC_TRACE_ERROR("Cannot decompress the offsets chunk."); goto error; } // We want the contiguous schunk, so create the actual data chunks (and, while doing this, // get a guess at the blocksize used in this frame) int64_t acc_nbytes = 0; int64_t acc_cbytes = 0; int32_t blocksize = 0; int32_t chunk_nbytes; int32_t chunk_cbytes; int32_t chunk_blocksize; size_t prev_alloc = BLOSC_EXTENDED_HEADER_LENGTH; uint8_t* data_chunk = NULL; bool needs_free = false; const blosc2_io_cb *io_cb = blosc2_get_io_cb(udio->id); if (io_cb == NULL) { blosc2_schunk_free(schunk); BLOSC_TRACE_ERROR("Error getting the input/output API"); return NULL; } void* fp = NULL; if (frame->cframe == NULL) { if (io_cb->is_allocation_necessary) { data_chunk = malloc((size_t)prev_alloc); needs_free = true; } else { needs_free = false; } if (!frame->sframe) { // If not the chunks won't be in the frame fp = io_cb->open(frame->urlpath, "rb", udio->params); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); rc = BLOSC2_ERROR_FILE_OPEN; goto end; } } } schunk->data = malloc(nchunks * sizeof(void*)); for (int64_t i = 0; i < nchunks; i++) { if (frame->cframe != NULL) { if (needs_free) { free(data_chunk); } if (offsets[i] < 0) { int64_t rbytes = frame_get_chunk(frame, i, &data_chunk, &needs_free); if (rbytes < 0) { break; } } else { if (offsets[i] > INT64_MAX - header_len || header_len + offsets[i] > frame->len - BLOSC_EXTENDED_HEADER_LENGTH || offsets[i] > cbytes - BLOSC_EXTENDED_HEADER_LENGTH) { rc = BLOSC2_ERROR_INVALID_HEADER; break; } data_chunk = frame->cframe + header_len + offsets[i]; needs_free = false; } rc = blosc2_cbuffer_sizes(data_chunk, NULL, &chunk_cbytes, NULL); if (rc < 0) { break; } if (offsets[i] >= 0 && (chunk_cbytes < BLOSC_EXTENDED_HEADER_LENGTH || offsets[i] > INT64_MAX - chunk_cbytes || offsets[i] + chunk_cbytes > cbytes)) { rc = BLOSC2_ERROR_INVALID_HEADER; break; } } else { int64_t rbytes; if (offsets[i] < 0 || frame->sframe) { if (needs_free) { free(data_chunk); } rbytes = frame_get_chunk(frame, i, &data_chunk, &needs_free); if (rbytes < 0) { break; } } else { if (offsets[i] > INT64_MAX - header_len || header_len + offsets[i] > frame->len - BLOSC_EXTENDED_HEADER_LENGTH || offsets[i] > cbytes - BLOSC_EXTENDED_HEADER_LENGTH) { rc = BLOSC2_ERROR_INVALID_HEADER; break; } int64_t io_pos = frame->file_offset + header_len + offsets[i]; rbytes = io_cb->read((void**)&data_chunk, 1, BLOSC_EXTENDED_HEADER_LENGTH, io_pos, fp); } if (rbytes != BLOSC_EXTENDED_HEADER_LENGTH) { rc = BLOSC2_ERROR_READ_BUFFER; break; } rc = blosc2_cbuffer_sizes(data_chunk, NULL, &chunk_cbytes, NULL); if (rc < 0) { break; } if (offsets[i] >= 0 && (chunk_cbytes < BLOSC_EXTENDED_HEADER_LENGTH || offsets[i] > INT64_MAX - chunk_cbytes || offsets[i] + chunk_cbytes > cbytes)) { rc = BLOSC2_ERROR_INVALID_HEADER; break; } if (chunk_cbytes > (int32_t)prev_alloc) { if (io_cb->is_allocation_necessary) data_chunk = realloc(data_chunk, chunk_cbytes); if (data_chunk == NULL) { BLOSC_TRACE_ERROR("Cannot realloc space for the data_chunk."); rc = BLOSC2_ERROR_MEMORY_ALLOC; break; } prev_alloc = chunk_cbytes; } if (!frame->sframe) { int64_t io_pos = frame->file_offset + header_len + offsets[i]; rbytes = io_cb->read((void**)&data_chunk, 1, chunk_cbytes, io_pos, fp); if (rbytes != chunk_cbytes) { rc = BLOSC2_ERROR_READ_BUFFER; break; } } } uint8_t* new_chunk = malloc(chunk_cbytes); memcpy(new_chunk, data_chunk, chunk_cbytes); schunk->data[i] = new_chunk; rc = blosc2_cbuffer_sizes(data_chunk, &chunk_nbytes, NULL, &chunk_blocksize); if (rc < 0) { break; } acc_nbytes += chunk_nbytes; acc_cbytes += chunk_cbytes; if (i == 0) { blocksize = chunk_blocksize; } else if (blocksize != chunk_blocksize) { // Blocksize varies blocksize = 0; } } // We are not attached to a schunk anymore frame->schunk = NULL; end: if (needs_free) { free(data_chunk); } if (frame->cframe == NULL) { if (!frame->sframe) { io_cb->close(fp); } } free(offsets); // Avoid a double-free in the `error:` cleanup below, which is reached // when frame_get_metalayers / frame_get_vlmetalayers reject a malformed // metalayer index after we've already freed `offsets` here. offsets = NULL; // cframes and sframes have different ways to store chunks with special values: // 1) cframes represent special chunks as negative offsets // 2) sframes does not have the concept of offsets, but rather of data pointers (.data) // so they always have a pointer to a special chunk // This is why cframes and sframes have different cbytes and hence, we cannot enforce acc_bytes == schunk->cbytes // In the future, maybe we could provide special meanings for .data[i] > 0x7FFFFFFF, but not there yet // if (rc < 0 || acc_nbytes != nbytes || acc_cbytes != cbytes) { if (rc < 0 || acc_nbytes != nbytes) { goto error; } // Update counters schunk->cbytes = acc_cbytes; schunk->blocksize = blocksize; out: rc = frame_get_metalayers(frame, schunk); if (rc < 0) { BLOSC_TRACE_ERROR("Cannot access the metalayers."); goto error; } rc = frame_get_vlmetalayers(frame, schunk); if (rc < 0) { BLOSC_TRACE_ERROR("Cannot access the vlmetalayers."); goto error; } return schunk; error: free(cparams); free(dparams); free(offsets); if (schunk != NULL) { blosc2_schunk_free(schunk); } if (!frame_attached) { frame->schunk = NULL; frame_free(frame); } return NULL; } void frame_avoid_cframe_free(blosc2_frame_s* frame, bool avoid_cframe_free) { frame->avoid_cframe_free = avoid_cframe_free; } struct csize_idx { int32_t val; int32_t idx; }; // Helper function for qsorting block offsets int sort_offset(const void* a, const void* b) { int32_t a_ = ((struct csize_idx*)a)->val; int32_t b_ = ((struct csize_idx*)b)->val; return a_ - b_; } int get_coffset(blosc2_frame_s* frame, int32_t header_len, int64_t cbytes, int64_t nchunk, int64_t nchunks, int64_t *offset) { int32_t off_cbytes; // Get the offset to nchunk uint8_t *coffsets = get_coffsets(frame, header_len, cbytes, nchunks, &off_cbytes); if (coffsets == NULL) { BLOSC_TRACE_ERROR("Cannot get the offset for chunk %" PRId64 " for the frame.", nchunk); return BLOSC2_ERROR_DATA; } // Get the 64-bit offset int rc = blosc2_getitem(coffsets, off_cbytes, (int32_t)nchunk, 1, offset, (int32_t)sizeof(int64_t)); if (rc < 0) { BLOSC_TRACE_ERROR("Problems retrieving a chunk offset."); } else if (!frame->sframe && *offset >= 0) { if (cbytes < 0 || cbytes > INT64_MAX - header_len) { BLOSC_TRACE_ERROR("Invalid compressed size in frame header."); return BLOSC2_ERROR_INVALID_HEADER; } if (*offset > INT64_MAX - header_len) { BLOSC_TRACE_ERROR("Offset for chunk %" PRId64 " overflows frame position.", nchunk); return BLOSC2_ERROR_INVALID_HEADER; } int64_t chunk_pos = header_len + *offset; int64_t data_limit = header_len + cbytes; if (chunk_pos < header_len || chunk_pos > data_limit - BLOSC_EXTENDED_HEADER_LENGTH || chunk_pos > frame->len - BLOSC_EXTENDED_HEADER_LENGTH) { BLOSC_TRACE_ERROR("Cannot read chunk %" PRId64 " outside of frame boundary.", nchunk); return BLOSC2_ERROR_INVALID_HEADER; } } return rc; } // Detect and return a chunk with special values in offsets (only zeros, NaNs and non initialized) int frame_special_chunk(int64_t special_value, int32_t nbytes, int32_t typesize, int32_t blocksize, uint8_t** chunk, int32_t cbytes, bool *needs_free) { int rc = 0; *chunk = malloc(cbytes); *needs_free = true; // Detect the kind of special value uint64_t zeros_mask = (uint64_t) BLOSC2_SPECIAL_ZERO << (8 * 7); // chunk of zeros uint64_t nans_mask = (uint64_t) BLOSC2_SPECIAL_NAN << (8 * 7); // chunk of NaNs uint64_t uninit_mask = (uint64_t) BLOSC2_SPECIAL_UNINIT << (8 * 7); // chunk of uninit values blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = typesize; cparams.blocksize = blocksize; if (special_value & zeros_mask) { rc = blosc2_chunk_zeros(cparams, nbytes, *chunk, cbytes); if (rc < 0) { BLOSC_TRACE_ERROR("Error creating a zero chunk"); } } else if (special_value & uninit_mask) { rc = blosc2_chunk_uninit(cparams, nbytes, *chunk, cbytes); if (rc < 0) { BLOSC_TRACE_ERROR("Error creating a non initialized chunk"); } } else if (special_value & nans_mask) { rc = blosc2_chunk_nans(cparams, nbytes, *chunk, cbytes); if (rc < 0) { BLOSC_TRACE_ERROR("Error creating a nan chunk"); } } else { BLOSC_TRACE_ERROR("Special value not recognized: %" PRId64 "", special_value); rc = BLOSC2_ERROR_DATA; } if (rc < 0) { free(*chunk); *needs_free = false; *chunk = NULL; } return rc; } /* Return a compressed chunk that is part of a frame in the `chunk` parameter. * If the frame is disk-based, a buffer is allocated for the (compressed) chunk, * and hence a free is needed. You can check if the chunk requires a free with the `needs_free` * parameter. * If the chunk does not need a free, it means that a pointer to the location in frame is returned * in the `chunk` parameter. * * The size of the (compressed) chunk is returned. If some problem is detected, a negative code * is returned instead. */ int frame_get_chunk(blosc2_frame_s *frame, int64_t nchunk, uint8_t **chunk, bool *needs_free) { int32_t header_len; int64_t frame_len; int64_t nbytes; int64_t cbytes; int32_t blocksize; int32_t chunksize; int64_t nchunks; int32_t typesize; int64_t offset; int32_t chunk_cbytes; int rc; *chunk = NULL; *needs_free = false; rc = get_header_info(frame, &header_len, &frame_len, &nbytes, &cbytes, &blocksize, &chunksize, &nchunks, &typesize, NULL, NULL, NULL, NULL, NULL, NULL, NULL, frame->schunk->storage->io); if (rc < 0) { BLOSC_TRACE_ERROR("Unable to get meta info from frame."); return rc; } if ((nchunks > 0) && (nchunk >= nchunks)) { BLOSC_TRACE_ERROR("nchunk ('%" PRId64 "') exceeds the number of chunks " "('%" PRId64 "') in frame.", nchunk, nchunks); return BLOSC2_ERROR_INVALID_PARAM; } // Get the offset to nchunk rc = get_coffset(frame, header_len, cbytes, nchunk, nchunks, &offset); if (rc < 0) { BLOSC_TRACE_ERROR("Unable to get offset to chunk %" PRId64 ".", nchunk); return rc; } if (offset < 0) { // Special value chunk_cbytes = BLOSC_EXTENDED_HEADER_LENGTH; int32_t chunksize_ = chunksize; if ((nchunk == nchunks - 1) && (nbytes % chunksize)) { // Last chunk is incomplete. Compute its actual size. chunksize_ = (int32_t) (nbytes % chunksize); } rc = frame_special_chunk(offset, chunksize_, typesize, blocksize, chunk, chunk_cbytes, needs_free); if (rc < 0) { return rc; } goto end; } if (frame->sframe) { // Sparse on-disk nchunk = offset; return sframe_get_chunk(frame, nchunk, chunk, needs_free); } blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); return BLOSC2_ERROR_PLUGIN_IO; } if (frame->cframe == NULL) { uint8_t* header_ptr; uint8_t header[BLOSC_EXTENDED_HEADER_LENGTH]; void* fp = io_cb->open(frame->urlpath, "rb", frame->schunk->storage->io->params); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return BLOSC2_ERROR_FILE_OPEN; } if (io_cb->is_allocation_necessary) header_ptr = header; int64_t io_pos = frame->file_offset + header_len + offset; int64_t rbytes = io_cb->read((void**)&header_ptr, 1, sizeof(header), io_pos, fp); if (rbytes != BLOSC_EXTENDED_HEADER_LENGTH) { BLOSC_TRACE_ERROR("Cannot read the cbytes for chunk in the frame."); io_cb->close(fp); return BLOSC2_ERROR_FILE_READ; } rc = blosc2_cbuffer_sizes(header_ptr, NULL, &chunk_cbytes, NULL); if (rc < 0) { BLOSC_TRACE_ERROR("Cannot read the cbytes for chunk in the frame."); io_cb->close(fp); return rc; } if (chunk_cbytes < BLOSC_EXTENDED_HEADER_LENGTH || offset > INT64_MAX - chunk_cbytes || offset + chunk_cbytes > cbytes) { BLOSC_TRACE_ERROR("Invalid chunk size in frame for chunk %" PRId64 ".", nchunk); io_cb->close(fp); return BLOSC2_ERROR_INVALID_HEADER; } if (io_cb->is_allocation_necessary) { *chunk = malloc(chunk_cbytes); *needs_free = true; } else { *needs_free = false; } io_pos = frame->file_offset + header_len + offset; rbytes = io_cb->read((void**)chunk, 1, chunk_cbytes, io_pos, fp); io_cb->close(fp); if (rbytes != chunk_cbytes) { BLOSC_TRACE_ERROR("Cannot read the chunk out of the frame."); return BLOSC2_ERROR_FILE_READ; } } else { // The chunk is in memory and just one pointer away *chunk = frame->cframe + header_len + offset; rc = blosc2_cbuffer_sizes(*chunk, NULL, &chunk_cbytes, NULL); if (rc < 0) { return rc; } if (chunk_cbytes < BLOSC_EXTENDED_HEADER_LENGTH || offset > INT64_MAX - chunk_cbytes || offset + chunk_cbytes > cbytes) { BLOSC_TRACE_ERROR("Invalid chunk size in frame for chunk %" PRId64 ".", nchunk); return BLOSC2_ERROR_INVALID_HEADER; } } end: return (int32_t)chunk_cbytes; } /* Return a compressed chunk that is part of a frame in the `chunk` parameter. * If the frame is disk-based, a buffer is allocated for the (lazy) chunk, * and hence a free is needed. You can check if the chunk requires a free with the `needs_free` * parameter. * If the chunk does not need a free, it means that the frame is in memory and that just a * pointer to the location of the chunk in memory is returned. * * The size of the (compressed, potentially lazy) chunk is returned. If some problem is detected, * a negative code is returned instead. */ int frame_get_lazychunk(blosc2_frame_s *frame, int64_t nchunk, uint8_t **chunk, bool *needs_free) { int32_t header_len; int64_t frame_len; int64_t nbytes; int64_t cbytes; int32_t blocksize; int32_t chunksize; int64_t nchunks; int32_t typesize; int32_t lazychunk_cbytes; int64_t offset; void* fp = NULL; int32_t* block_csizes = NULL; struct csize_idx *csize_idx = NULL; *chunk = NULL; *needs_free = false; int rc = get_header_info(frame, &header_len, &frame_len, &nbytes, &cbytes, &blocksize, &chunksize, &nchunks, &typesize, NULL, NULL, NULL, NULL, NULL, NULL, NULL, frame->schunk->storage->io); if (rc < 0) { BLOSC_TRACE_ERROR("Unable to get meta info from frame."); return rc; } if (nchunk >= nchunks) { BLOSC_TRACE_ERROR("nchunk ('%" PRId64 "') exceeds the number of chunks " "('%" PRId64 "') in frame.", nchunk, nchunks); return BLOSC2_ERROR_INVALID_PARAM; } // Get the offset to nchunk rc = get_coffset(frame, header_len, cbytes, nchunk, nchunks, &offset); if (rc < 0) { BLOSC_TRACE_ERROR("Unable to get offset to chunk %" PRId64 ".", nchunk); return rc; } if (offset < 0) { // Special value lazychunk_cbytes = BLOSC_EXTENDED_HEADER_LENGTH; int32_t chunksize_ = chunksize; if ((nchunk == nchunks - 1) && (nbytes % chunksize)) { // Last chunk is incomplete. Compute its actual size. chunksize_ = (int32_t) (nbytes % chunksize); } rc = frame_special_chunk(offset, chunksize_, typesize, blocksize, chunk, (int32_t)lazychunk_cbytes, needs_free); goto end; } blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); rc = BLOSC2_ERROR_PLUGIN_IO; goto end; } if (frame->cframe == NULL) { // TODO: make this portable across different endianness // Get info for building a lazy chunk int32_t chunk_nbytes; int32_t chunk_cbytes; int32_t chunk_blocksize; uint8_t* header_ptr; uint8_t header[BLOSC_EXTENDED_HEADER_LENGTH]; int64_t io_pos = 0; if (frame->sframe) { // The chunk is not in the frame fp = sframe_open_chunk(frame->urlpath, offset, "rb", frame->schunk->storage->io); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return BLOSC2_ERROR_FILE_OPEN; } } else { fp = io_cb->open(frame->urlpath, "rb", frame->schunk->storage->io->params); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return BLOSC2_ERROR_FILE_OPEN; } io_pos = frame->file_offset + header_len + offset; } if (io_cb->is_allocation_necessary) header_ptr = header; int64_t rbytes = io_cb->read((void**)&header_ptr, 1, BLOSC_EXTENDED_HEADER_LENGTH, io_pos, fp); if (rbytes != BLOSC_EXTENDED_HEADER_LENGTH) { BLOSC_TRACE_ERROR("Cannot read the header for chunk in the frame."); rc = BLOSC2_ERROR_FILE_READ; goto end; } rc = blosc2_cbuffer_sizes(header_ptr, &chunk_nbytes, &chunk_cbytes, &chunk_blocksize); if (rc < 0) { goto end; } if (chunk_nbytes < 0 || chunk_cbytes < BLOSC_EXTENDED_HEADER_LENGTH || chunk_blocksize <= 0) { rc = BLOSC2_ERROR_INVALID_HEADER; goto end; } bool vlblocks = (header_ptr[BLOSC2_CHUNK_BLOSC2_FLAGS2] & BLOSC2_VL_BLOCKS) != 0; size_t nblocks; size_t leftover_block = 0; if (vlblocks) { nblocks = (size_t)chunk_blocksize; } else { nblocks = (size_t)chunk_nbytes / (size_t)chunk_blocksize; leftover_block = (size_t)chunk_nbytes % (size_t)chunk_blocksize; if (leftover_block) { if (nblocks == SIZE_MAX) { rc = BLOSC2_ERROR_INVALID_HEADER; goto end; } nblocks += 1; } } if (nblocks == 0 || nblocks > (size_t)INT_MAX) { rc = BLOSC2_ERROR_INVALID_HEADER; goto end; } // Allocate space for the lazy chunk int32_t special_type = (header_ptr[BLOSC2_CHUNK_BLOSC2_FLAGS] >> 4) & BLOSC2_SPECIAL_MASK; int memcpyed = header_ptr[BLOSC2_CHUNK_FLAGS] & (uint8_t) BLOSC_MEMCPYED; int32_t trailer_offset; size_t streams_offset = BLOSC_EXTENDED_HEADER_LENGTH; size_t trailer_offset_sz = BLOSC_EXTENDED_HEADER_LENGTH; size_t trailer_len_sz = 0; size_t lazychunk_cbytes_sz = 0; size_t bstarts_nbytes = 0; if (special_type == 0) { if (nblocks > SIZE_MAX / sizeof(int32_t)) { rc = BLOSC2_ERROR_INVALID_HEADER; goto end; } bstarts_nbytes = nblocks * sizeof(int32_t); if (!memcpyed) { if ((size_t)chunk_cbytes < BLOSC_EXTENDED_HEADER_LENGTH || bstarts_nbytes > (size_t)chunk_cbytes - BLOSC_EXTENDED_HEADER_LENGTH) { rc = BLOSC2_ERROR_INVALID_HEADER; goto end; } } if (bstarts_nbytes > SIZE_MAX - trailer_offset_sz) { rc = BLOSC2_ERROR_INVALID_HEADER; goto end; } trailer_offset_sz += bstarts_nbytes; // Regular values have offsets for blocks if (!memcpyed) { if (bstarts_nbytes > SIZE_MAX - streams_offset) { rc = BLOSC2_ERROR_INVALID_HEADER; goto end; } streams_offset += bstarts_nbytes; } if (bstarts_nbytes > SIZE_MAX - (sizeof(int32_t) + sizeof(int64_t))) { rc = BLOSC2_ERROR_INVALID_HEADER; goto end; } trailer_len_sz = sizeof(int32_t) + sizeof(int64_t) + bstarts_nbytes; if (trailer_len_sz > SIZE_MAX - trailer_offset_sz) { rc = BLOSC2_ERROR_INVALID_HEADER; goto end; } lazychunk_cbytes_sz = trailer_offset_sz + trailer_len_sz; } else if (special_type == BLOSC2_SPECIAL_VALUE) { if (typesize <= 0) { rc = BLOSC2_ERROR_INVALID_HEADER; goto end; } if ((size_t)typesize > SIZE_MAX - trailer_offset_sz || (size_t)typesize > SIZE_MAX - streams_offset) { rc = BLOSC2_ERROR_INVALID_HEADER; goto end; } trailer_offset_sz += (size_t)typesize; streams_offset += (size_t)typesize; lazychunk_cbytes_sz = trailer_offset_sz; } else { rc = BLOSC2_ERROR_INVALID_HEADER; goto end; } if (streams_offset > (size_t)chunk_cbytes || trailer_offset_sz > INT32_MAX || trailer_len_sz > INT32_MAX || lazychunk_cbytes_sz > INT32_MAX) { rc = BLOSC2_ERROR_INVALID_HEADER; goto end; } trailer_offset = (int32_t)trailer_offset_sz; lazychunk_cbytes = (int32_t)lazychunk_cbytes_sz; // Read just the full header and bstarts section too (lazy partial length) if (frame->sframe) { io_pos = 0; } else { io_pos = frame->file_offset + header_len + offset; } // The case here is a bit special because more memory is allocated than read from the file // and the chunk is modified after reading. Due to the modification, we cannot directly use // the memory provided by the io *chunk = malloc((size_t)lazychunk_cbytes); if (*chunk == NULL) { rc = BLOSC2_ERROR_MEMORY_ALLOC; goto end; } *needs_free = true; if (io_cb->is_allocation_necessary) { rbytes = io_cb->read((void**)chunk, 1, (int64_t)streams_offset, io_pos, fp); } else { uint8_t* chunk_ptr; rbytes = io_cb->read((void**)&chunk_ptr, 1, (int64_t)streams_offset, io_pos, fp); memcpy(*chunk, chunk_ptr, streams_offset); } if (rbytes != (int64_t)streams_offset) { BLOSC_TRACE_ERROR("Cannot read the (lazy) chunk out of the frame."); rc = BLOSC2_ERROR_FILE_READ; goto end; } if (special_type == BLOSC2_SPECIAL_VALUE) { // Value runlen is not returning a lazy chunk. We are done. goto end; } // Mark chunk as lazy uint8_t* blosc2_flags = *chunk + BLOSC2_CHUNK_BLOSC2_FLAGS; *blosc2_flags |= 0x08U; // Add the trailer (currently, nchunk + offset + block_csizes) if (frame->sframe) { *(int32_t*)(*chunk + trailer_offset) = (int32_t)offset; // offset is nchunk for sframes *(int64_t*)(*chunk + trailer_offset + sizeof(int32_t)) = offset; } else { *(int32_t*)(*chunk + trailer_offset) = (int32_t)nchunk; *(int64_t*)(*chunk + trailer_offset + sizeof(int32_t)) = header_len + offset; } block_csizes = malloc(nblocks * sizeof(int32_t)); if (block_csizes == NULL) { rc = BLOSC2_ERROR_MEMORY_ALLOC; goto end; } if (memcpyed) { // When memcpyed the blocksizes are trivial to compute for (size_t i = 0; i + 1 < nblocks; i++) { block_csizes[i] = (int)chunk_blocksize; } // The last block could be incomplete, mainly due to the fact that the block size is not divisible // by the typesize block_csizes[nblocks - 1] = (int32_t)leftover_block ? (int32_t)leftover_block : chunk_blocksize; } else { // In regular, compressed chunks, we need to sort the bstarts (they can be out // of order because of multi-threading), and get a reverse index too. memcpy(block_csizes, *chunk + BLOSC_EXTENDED_HEADER_LENGTH, nblocks * sizeof(int32_t)); // Helper structure to keep track of original indexes csize_idx = malloc(nblocks * sizeof(struct csize_idx)); if (csize_idx == NULL) { rc = BLOSC2_ERROR_MEMORY_ALLOC; goto end; } for (size_t n = 0; n < nblocks; n++) { csize_idx[n].val = block_csizes[n]; csize_idx[n].idx = (int)n; } qsort(csize_idx, nblocks, sizeof(struct csize_idx), &sort_offset); // Compute the actual csizes int idx; for (size_t n = 0; n + 1 < nblocks; n++) { idx = csize_idx[n].idx; block_csizes[idx] = csize_idx[n + 1].val - csize_idx[n].val; } idx = csize_idx[nblocks - 1].idx; block_csizes[idx] = chunk_cbytes - csize_idx[nblocks - 1].val; free(csize_idx); csize_idx = NULL; } // Copy the csizes at the end of the trailer void *trailer_csizes = *chunk + lazychunk_cbytes - nblocks * sizeof(int32_t); memcpy(trailer_csizes, block_csizes, nblocks * sizeof(int32_t)); free(block_csizes); block_csizes = NULL; } else { // The chunk is in memory and just one pointer away int64_t chunk_header_offset = header_len + offset; int64_t chunk_cbytes_offset = chunk_header_offset + BLOSC_MIN_HEADER_LENGTH; *chunk = frame->cframe + chunk_header_offset; if (chunk_header_offset < 0 || chunk_cbytes_offset < chunk_header_offset || chunk_cbytes_offset > frame->len) { BLOSC_TRACE_ERROR("Cannot read the header for chunk in the (contiguous) frame."); rc = BLOSC2_ERROR_READ_BUFFER; } else { rc = blosc2_cbuffer_sizes(*chunk, NULL, &lazychunk_cbytes, NULL); if (rc < 0) { BLOSC_TRACE_ERROR("Cannot read the chunk header."); rc = BLOSC2_ERROR_READ_BUFFER; } else if (lazychunk_cbytes < BLOSC_EXTENDED_HEADER_LENGTH || chunk_header_offset > INT64_MAX - lazychunk_cbytes || chunk_header_offset + lazychunk_cbytes > frame_len) { BLOSC_TRACE_ERROR("Compressed bytes exceed beyond frame length."); rc = BLOSC2_ERROR_READ_BUFFER; } } } end: if (csize_idx != NULL) { free(csize_idx); } if (block_csizes != NULL) { free(block_csizes); } if (fp != NULL) { io_cb->close(fp); } if (rc < 0) { if (*needs_free) { free(*chunk); *chunk = NULL; *needs_free = false; } return rc; } return (int)lazychunk_cbytes; } /* Fill an empty frame with special values (fast path). */ int64_t frame_fill_special(blosc2_frame_s* frame, int64_t nitems, int special_value, int32_t chunksize, blosc2_schunk* schunk) { int32_t header_len; int64_t frame_len; int64_t nbytes; int64_t cbytes; int32_t blocksize; int32_t typesize; int64_t nchunks; int rc = get_header_info(frame, &header_len, &frame_len, &nbytes, &cbytes, &blocksize, NULL, &nchunks, &typesize, NULL, NULL, NULL, NULL, NULL, NULL, NULL, schunk->storage->io); if (rc < 0) { BLOSC_TRACE_ERROR("Unable to get meta info from frame."); return BLOSC2_ERROR_DATA; } if (nitems == 0) { return frame_len; } if ((nitems / chunksize) > INT_MAX) { BLOSC_TRACE_ERROR("nitems is too large. Try increasing the chunksize."); return BLOSC2_ERROR_FRAME_SPECIAL; } if ((nbytes > 0) || (cbytes > 0)) { BLOSC_TRACE_ERROR("Filling with special values only works on empty frames"); return BLOSC2_ERROR_FRAME_SPECIAL; } // Compute the number of chunks and the length of the offsets chunk int32_t chunkitems = chunksize / typesize; if (chunkitems <= 0) { BLOSC_TRACE_ERROR("chunksize must be >= typesize for frame special fill."); return BLOSC2_ERROR_FRAME_SPECIAL; } nchunks = nitems / chunkitems; int32_t leftover_items = (int32_t)(nitems % chunkitems); if (leftover_items) { nchunks += 1; } blosc2_cparams* cparams; blosc2_schunk_get_cparams(schunk, &cparams); // Build the offsets with a special chunk int new_off_cbytes = BLOSC_EXTENDED_HEADER_LENGTH + sizeof(int64_t); uint8_t* off_chunk = malloc(new_off_cbytes); uint64_t offset_value = ((uint64_t)1 << 63); uint8_t* sample_chunk = malloc(BLOSC_EXTENDED_HEADER_LENGTH); if (off_chunk == NULL || sample_chunk == NULL) { free(off_chunk); free(sample_chunk); free(cparams); BLOSC_TRACE_ERROR("Cannot allocate memory for special fill chunks."); return BLOSC2_ERROR_MEMORY_ALLOC; } int csize; switch (special_value) { case BLOSC2_SPECIAL_ZERO: offset_value += (uint64_t) BLOSC2_SPECIAL_ZERO << (8 * 7); csize = blosc2_chunk_zeros(*cparams, chunksize, sample_chunk, BLOSC_EXTENDED_HEADER_LENGTH); break; case BLOSC2_SPECIAL_UNINIT: offset_value += (uint64_t) BLOSC2_SPECIAL_UNINIT << (8 * 7); csize = blosc2_chunk_uninit(*cparams, chunksize, sample_chunk, BLOSC_EXTENDED_HEADER_LENGTH); break; case BLOSC2_SPECIAL_NAN: offset_value += (uint64_t)BLOSC2_SPECIAL_NAN << (8 * 7); csize = blosc2_chunk_nans(*cparams, chunksize, sample_chunk, BLOSC_EXTENDED_HEADER_LENGTH); break; default: free(off_chunk); free(sample_chunk); free(cparams); BLOSC_TRACE_ERROR("Only zeros, NaNs or non-initialized values are supported."); return BLOSC2_ERROR_FRAME_SPECIAL; } if (csize < 0) { free(off_chunk); free(sample_chunk); free(cparams); BLOSC_TRACE_ERROR("Error creating sample chunk"); return BLOSC2_ERROR_FRAME_SPECIAL; } cparams->typesize = sizeof(int64_t); // change it to offsets typesize // cparams->blocksize = 0; // automatic blocksize cparams->blocksize = 8 * 2 * 1024; // based on experiments with create_frame.c bench cparams->clevel = 5; cparams->compcode = BLOSC_BLOSCLZ; int32_t special_nbytes; if (!blosc2_nchunks_to_offsets_nbytes(nchunks, &special_nbytes)) { free(off_chunk); free(sample_chunk); free(cparams); BLOSC_TRACE_ERROR("Too many chunks for offsets representation."); return BLOSC2_ERROR_FRAME_SPECIAL; } rc = blosc2_chunk_repeatval(*cparams, special_nbytes, off_chunk, new_off_cbytes, &offset_value); free(cparams); if (rc < 0) { BLOSC_TRACE_ERROR("Error creating a special offsets chunk"); return BLOSC2_ERROR_DATA; } // Get the blocksize associated to the sample chunk blosc2_cbuffer_sizes(sample_chunk, NULL, NULL, &blocksize); free(sample_chunk); // and use it for the super-chunk schunk->blocksize = blocksize; // schunk->blocksize = 0; // for experimenting with automatic blocksize // We have the new offsets; update the frame. blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); return BLOSC2_ERROR_PLUGIN_IO; } int64_t new_frame_len = header_len + new_off_cbytes + frame->trailer_len; void* fp = NULL; if (frame->cframe != NULL) { uint8_t* framep = frame->cframe; /* Make space for the new chunk and copy it */ frame->cframe = framep = realloc(framep, (size_t)new_frame_len); if (framep == NULL) { BLOSC_TRACE_ERROR("Cannot realloc space for the frame."); return BLOSC2_ERROR_FRAME_SPECIAL; } /* Copy the offsets */ memcpy(framep + header_len, off_chunk, (size_t)new_off_cbytes); } else { size_t wbytes; int64_t io_pos = 0; if (frame->sframe) { // Update the offsets chunk in the chunks frame fp = sframe_open_index(frame->urlpath, "rb+", frame->schunk->storage->io); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return BLOSC2_ERROR_FILE_OPEN; } io_pos = frame->file_offset + header_len; } else { // Regular frame fp = io_cb->open(frame->urlpath, "rb+", schunk->storage->io->params); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return BLOSC2_ERROR_FILE_OPEN; } io_pos = frame->file_offset + header_len + cbytes; } wbytes = io_cb->write(off_chunk, 1, new_off_cbytes, io_pos, fp); // the new offsets io_cb->close(fp); if (wbytes != (size_t)new_off_cbytes) { BLOSC_TRACE_ERROR("Cannot write the offsets to frame."); return BLOSC2_ERROR_FRAME_SPECIAL; } } // Invalidate the cache for chunk offsets if (frame->coffsets != NULL) { if (frame->coffsets_needs_free) free(frame->coffsets); frame->coffsets = NULL; } free(off_chunk); frame->len = new_frame_len; rc = frame_update_header(frame, schunk, false); if (rc < 0) { return BLOSC2_ERROR_FRAME_SPECIAL; } rc = frame_update_trailer(frame, schunk); if (rc < 0) { return BLOSC2_ERROR_FRAME_SPECIAL; } return frame->len; } /* Append an existing chunk into a frame. */ void* frame_append_chunk(blosc2_frame_s* frame, void* chunk, blosc2_schunk* schunk) { int8_t* chunk_ = chunk; int32_t header_len; int64_t frame_len; int64_t nbytes; int64_t cbytes; int32_t blocksize; int32_t chunksize; int64_t nchunks; int rc = get_header_info(frame, &header_len, &frame_len, &nbytes, &cbytes, &blocksize, &chunksize, &nchunks, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, frame->schunk->storage->io); if (rc < 0) { BLOSC_TRACE_ERROR("Unable to get meta info from frame."); return NULL; } /* The uncompressed and compressed sizes start at byte 4 and 12 */ int32_t chunk_nbytes; int32_t chunk_cbytes; rc = blosc2_cbuffer_sizes(chunk, &chunk_nbytes, &chunk_cbytes, NULL); if (rc < 0) { return NULL; } if ((nchunks > 0) && (chunksize > 0) && (schunk->chunksize != 0) && (chunk_nbytes > chunksize)) { BLOSC_TRACE_ERROR("Appending chunks with a larger chunksize than frame is " "not allowed yet %d != %d.", chunk_nbytes, chunksize); return NULL; } // Check that we are not appending a small chunk after another small chunk int32_t chunk_nbytes_last; if ((chunksize > 0) && (nchunks > 0) && (chunk_nbytes < chunksize)) { uint8_t* last_chunk; bool needs_free; rc = frame_get_lazychunk(frame, nchunks - 1, &last_chunk, &needs_free); if (rc < 0) { BLOSC_TRACE_ERROR("Cannot get the last chunk (in position %" PRId64 ").", nchunks - 1); } else { rc = blosc2_cbuffer_sizes(last_chunk, &chunk_nbytes_last, NULL, NULL); } if (needs_free) { free(last_chunk); } if (rc < 0) { return NULL; } if ((chunk_nbytes_last < chunksize) && (nbytes < chunksize)) { BLOSC_TRACE_ERROR("Appending two consecutive chunks with a chunksize smaller " "than the frame chunksize is not allowed yet: %d != %d.", chunk_nbytes, chunksize); return NULL; } } // Get the current offsets and add one more int32_t off_nbytes = (int32_t) ((nchunks + 1) * sizeof(int64_t)); int64_t* offsets = (int64_t *) malloc((size_t)off_nbytes); if (nchunks > 0) { int32_t coffsets_cbytes; uint8_t *coffsets = get_coffsets(frame, header_len, cbytes, nchunks, &coffsets_cbytes); if (coffsets == NULL) { BLOSC_TRACE_ERROR("Cannot get the offsets for the frame."); free(offsets); return NULL; } // Decompress offsets blosc2_dparams off_dparams = BLOSC2_DPARAMS_DEFAULTS; blosc2_context *dctx = blosc2_create_dctx(off_dparams); if (dctx == NULL) { BLOSC_TRACE_ERROR("Error while creating the decompression context"); return NULL; } int32_t prev_nbytes = blosc2_decompress_ctx(dctx, coffsets, coffsets_cbytes, offsets, off_nbytes); blosc2_free_ctx(dctx); if (prev_nbytes < 0) { free(offsets); BLOSC_TRACE_ERROR("Cannot decompress the offsets chunk."); return NULL; } } // Add the new offset int64_t sframe_chunk_id = -1; int special_value = (chunk_[BLOSC2_CHUNK_BLOSC2_FLAGS] >> 4) & BLOSC2_SPECIAL_MASK; uint64_t offset_value = ((uint64_t)1 << 63); switch (special_value) { case BLOSC2_SPECIAL_ZERO: // Zero chunk. Code it in a special way. offset_value += (uint64_t) BLOSC2_SPECIAL_ZERO << (8 * 7); // chunk of zeros to_little(offsets + nchunks, &offset_value, sizeof(uint64_t)); chunk_cbytes = 0; // we don't need to store the chunk break; case BLOSC2_SPECIAL_UNINIT: // Non initizalized values chunk. Code it in a special way. offset_value += (uint64_t) BLOSC2_SPECIAL_UNINIT << (8 * 7); // chunk of uninit values to_little(offsets + nchunks, &offset_value, sizeof(uint64_t)); chunk_cbytes = 0; // we don't need to store the chunk break; case BLOSC2_SPECIAL_NAN: // NaN chunk. Code it in a special way. offset_value += (uint64_t)BLOSC2_SPECIAL_NAN << (8 * 7); // chunk of NANs to_little(offsets + nchunks, &offset_value, sizeof(uint64_t)); chunk_cbytes = 0; // we don't need to store the chunk break; default: if (frame->sframe) { // Compute the sframe_chunk_id value for (int64_t i = 0; i < nchunks; ++i) { if (offsets[i] > sframe_chunk_id) { sframe_chunk_id = offsets[i]; } } offsets[nchunks] = ++sframe_chunk_id; } else { offsets[nchunks] = cbytes; } } // Re-compress the offsets again blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.splitmode = BLOSC_NEVER_SPLIT; cparams.typesize = sizeof(int64_t); cparams.blocksize = 16 * 1024; // based on experiments with create_frame.c bench cparams.nthreads = 4; // 4 threads seems a decent default for nowadays CPUs cparams.compcode = BLOSC_BLOSCLZ; blosc2_context* cctx = blosc2_create_cctx(cparams); if (cctx == NULL) { BLOSC_TRACE_ERROR("Error while creating the compression context"); return NULL; } cctx->typesize = sizeof(int64_t); // override a possible BLOSC_TYPESIZE env variable (or chaos may appear) void* off_chunk = malloc((size_t)off_nbytes + BLOSC2_MAX_OVERHEAD); int32_t new_off_cbytes = blosc2_compress_ctx(cctx, offsets, off_nbytes, off_chunk, off_nbytes + BLOSC2_MAX_OVERHEAD); blosc2_free_ctx(cctx); free(offsets); if (new_off_cbytes < 0) { free(off_chunk); return NULL; } // printf("%f\n", (double) off_nbytes / new_off_cbytes); int64_t new_cbytes = cbytes + chunk_cbytes; int64_t new_frame_len; if (frame->sframe) { new_frame_len = header_len + 0 + new_off_cbytes + frame->trailer_len; } else { new_frame_len = header_len + new_cbytes + new_off_cbytes + frame->trailer_len; } void* fp = NULL; if (frame->cframe != NULL) { uint8_t* framep = frame->cframe; /* Make space for the new chunk and copy it */ frame->cframe = framep = realloc(framep, (size_t)new_frame_len); if (framep == NULL) { BLOSC_TRACE_ERROR("Cannot realloc space for the frame."); return NULL; } /* Copy the chunk */ memcpy(framep + header_len + cbytes, chunk, (size_t)chunk_cbytes); /* Copy the offsets */ memcpy(framep + header_len + new_cbytes, off_chunk, (size_t)new_off_cbytes); } else { int64_t wbytes; int64_t io_pos = 0; blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); return NULL; } if (frame->sframe) { // Update the offsets chunk in the chunks frame if (chunk_cbytes != 0) { if (sframe_chunk_id < 0) { BLOSC_TRACE_ERROR("The chunk id (%" PRId64 ") is not correct", sframe_chunk_id); return NULL; } if (sframe_create_chunk(frame, chunk, sframe_chunk_id, chunk_cbytes) == NULL) { BLOSC_TRACE_ERROR("Cannot write the full chunk."); return NULL; } } fp = sframe_open_index(frame->urlpath, "rb+", frame->schunk->storage->io); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return NULL; } io_pos = frame->file_offset + header_len; } else { // Regular frame fp = io_cb->open(frame->urlpath, "rb+", frame->schunk->storage->io->params); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return NULL; } io_pos = frame->file_offset + header_len + cbytes; wbytes = io_cb->write(chunk, 1, chunk_cbytes, io_pos, fp); // the new chunk io_pos += chunk_cbytes; if (wbytes != chunk_cbytes) { BLOSC_TRACE_ERROR("Cannot write the full chunk to frame (wrote %" PRId64 " of %" PRId64 " bytes at position %" PRId64 ", nchunk=%" PRId64 ").", wbytes, (int64_t)chunk_cbytes, io_pos - (int64_t)chunk_cbytes, nchunks); io_cb->close(fp); return NULL; } } wbytes = io_cb->write(off_chunk, 1, new_off_cbytes, io_pos, fp); // the new offsets io_cb->close(fp); if (wbytes != new_off_cbytes) { BLOSC_TRACE_ERROR("Cannot write the offsets to frame."); return NULL; } } // Invalidate the cache for chunk offsets if (frame->coffsets != NULL) { if (frame->coffsets_needs_free) free(frame->coffsets); frame->coffsets = NULL; } free(chunk); // chunk has always to be a copy when reaching here... free(off_chunk); frame->len = new_frame_len; rc = frame_update_header(frame, schunk, false); if (rc < 0) { return NULL; } rc = frame_update_trailer(frame, schunk); if (rc < 0) { return NULL; } return frame; } void* frame_insert_chunk(blosc2_frame_s* frame, int64_t nchunk, void* chunk, blosc2_schunk* schunk) { uint8_t* chunk_ = chunk; int32_t header_len; int64_t frame_len; int64_t nbytes; int64_t cbytes; int32_t blocksize; int32_t chunksize; int64_t nchunks; int rc = get_header_info(frame, &header_len, &frame_len, &nbytes, &cbytes, &blocksize, &chunksize, &nchunks, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, frame->schunk->storage->io); if (rc < 0) { BLOSC_TRACE_ERROR("Unable to get meta info from frame."); return NULL; } int32_t chunk_cbytes; rc = blosc2_cbuffer_sizes(chunk_, NULL, &chunk_cbytes, NULL); if (rc < 0) { return NULL; } // Get the current offsets int32_t off_nbytes = (int32_t) ((nchunks + 1) * sizeof(int64_t)); int64_t* offsets = (int64_t *) malloc((size_t)off_nbytes); if (nchunks > 0) { int32_t coffsets_cbytes = 0; uint8_t *coffsets = get_coffsets(frame, header_len, cbytes, nchunks, &coffsets_cbytes); if (coffsets == NULL) { BLOSC_TRACE_ERROR("Cannot get the offsets for the frame."); return NULL; } // Decompress offsets blosc2_dparams off_dparams = BLOSC2_DPARAMS_DEFAULTS; blosc2_context *dctx = blosc2_create_dctx(off_dparams); if (dctx == NULL) { BLOSC_TRACE_ERROR("Error while creating the decompression context"); return NULL; } int32_t prev_nbytes = blosc2_decompress_ctx(dctx, coffsets, coffsets_cbytes, offsets, off_nbytes); blosc2_free_ctx(dctx); if (prev_nbytes < 0) { free(offsets); BLOSC_TRACE_ERROR("Cannot decompress the offsets chunk."); return NULL; } } // TODO: Improvement: Check if new chunk is smaller than previous one // Move offsets for (int64_t i = nchunks; i > nchunk; i--) { offsets[i] = offsets[i - 1]; } // Add the new offset int64_t sframe_chunk_id = -1; int special_value = (chunk_[BLOSC2_CHUNK_BLOSC2_FLAGS] >> 4) & BLOSC2_SPECIAL_MASK; uint64_t offset_value = ((uint64_t)1 << 63); switch (special_value) { case BLOSC2_SPECIAL_ZERO: // Zero chunk. Code it in a special way. offset_value += (uint64_t)BLOSC2_SPECIAL_ZERO << (8 * 7); // indicate a chunk of zeros to_little(offsets + nchunk, &offset_value, sizeof(uint64_t)); chunk_cbytes = 0; // we don't need to store the chunk break; case BLOSC2_SPECIAL_UNINIT: // Non initizalized values chunk. Code it in a special way. offset_value += (uint64_t) BLOSC2_SPECIAL_UNINIT << (8 * 7); // chunk of uninit values to_little(offsets + nchunk, &offset_value, sizeof(uint64_t)); chunk_cbytes = 0; // we don't need to store the chunk break; case BLOSC2_SPECIAL_NAN: // NaN chunk. Code it in a special way. offset_value += (uint64_t)BLOSC2_SPECIAL_NAN << (8 * 7); // indicate a chunk of NANs to_little(offsets + nchunk, &offset_value, sizeof(uint64_t)); chunk_cbytes = 0; // we don't need to store the chunk break; default: if (frame->sframe) { for (int64_t i = 0; i <= nchunks; ++i) { // offsets[nchunk] is still uninitialized here if (i != nchunk && offsets[i] > sframe_chunk_id) { sframe_chunk_id = offsets[i]; } } offsets[nchunk] = ++sframe_chunk_id; } else { offsets[nchunk] = cbytes; } } // Re-compress the offsets again blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.splitmode = BLOSC_NEVER_SPLIT; cparams.typesize = sizeof(int64_t); cparams.blocksize = 16 * 1024; // based on experiments with create_frame.c bench cparams.nthreads = 4; // 4 threads seems a decent default for nowadays CPUs cparams.compcode = BLOSC_BLOSCLZ; blosc2_context* cctx = blosc2_create_cctx(cparams); if (cctx == NULL) { BLOSC_TRACE_ERROR("Error while creating the compression context"); return NULL; } void* off_chunk = malloc((size_t)off_nbytes + BLOSC2_MAX_OVERHEAD); int32_t new_off_cbytes = blosc2_compress_ctx(cctx, offsets, off_nbytes, off_chunk, off_nbytes + BLOSC2_MAX_OVERHEAD); blosc2_free_ctx(cctx); free(offsets); if (new_off_cbytes < 0) { free(off_chunk); return NULL; } int64_t new_cbytes = cbytes + chunk_cbytes; int64_t new_frame_len; if (frame->sframe) { new_frame_len = header_len + 0 + new_off_cbytes + frame->trailer_len; } else { new_frame_len = header_len + new_cbytes + new_off_cbytes + frame->trailer_len; } // Add the chunk and update meta void* fp = NULL; if (frame->cframe != NULL) { uint8_t* framep = frame->cframe; /* Make space for the new chunk and copy it */ frame->cframe = framep = realloc(framep, (size_t)new_frame_len); if (framep == NULL) { BLOSC_TRACE_ERROR("Cannot realloc space for the frame."); return NULL; } /* Copy the chunk */ memcpy(framep + header_len + cbytes, chunk, (size_t)chunk_cbytes); /* Copy the offsets */ memcpy(framep + header_len + new_cbytes, off_chunk, (size_t)new_off_cbytes); } else { int64_t wbytes; int64_t io_pos = 0; blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); return NULL; } if (frame->sframe) { if (chunk_cbytes != 0) { if (sframe_chunk_id < 0) { BLOSC_TRACE_ERROR("The chunk id (%" PRId64 ") is not correct", sframe_chunk_id); return NULL; } if (sframe_create_chunk(frame, chunk, sframe_chunk_id, chunk_cbytes) == NULL) { BLOSC_TRACE_ERROR("Cannot write the full chunk."); return NULL; } } // Update the offsets chunk in the chunks frame fp = sframe_open_index(frame->urlpath, "rb+", frame->schunk->storage->io); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return NULL; } io_pos = frame->file_offset + header_len + 0; } else { // Regular frame fp = io_cb->open(frame->urlpath, "rb+", frame->schunk->storage->io->params); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return NULL; } io_pos = frame->file_offset + header_len + cbytes; wbytes = io_cb->write(chunk, 1, chunk_cbytes, io_pos, fp); // the new chunk io_pos += chunk_cbytes; if (wbytes != chunk_cbytes) { BLOSC_TRACE_ERROR("Cannot write the full chunk to frame (wrote %" PRId64 " of %" PRId64 " bytes at position %" PRId64 ", nchunk=%" PRId64 ").", wbytes, (int64_t)chunk_cbytes, io_pos - (int64_t)chunk_cbytes, nchunk); io_cb->close(fp); return NULL; } } wbytes = io_cb->write(off_chunk, 1, new_off_cbytes, io_pos, fp); // the new offsets io_cb->close(fp); if (wbytes != new_off_cbytes) { BLOSC_TRACE_ERROR("Cannot write the offsets to frame."); return NULL; } // Invalidate the cache for chunk offsets if (frame->coffsets != NULL) { if (frame->coffsets_needs_free) free(frame->coffsets); frame->coffsets = NULL; } } free(chunk); // chunk has always to be a copy when reaching here... free(off_chunk); frame->len = new_frame_len; rc = frame_update_header(frame, schunk, false); if (rc < 0) { return NULL; } rc = frame_update_trailer(frame, schunk); if (rc < 0) { return NULL; } return frame; } void* frame_update_chunk(blosc2_frame_s* frame, int64_t nchunk, void* chunk, blosc2_schunk* schunk) { uint8_t *chunk_ = (uint8_t *) chunk; int32_t header_len; int64_t frame_len; int64_t nbytes; int64_t cbytes; int32_t blocksize; int32_t chunksize; int64_t nchunks; int rc = get_header_info(frame, &header_len, &frame_len, &nbytes, &cbytes, &blocksize, &chunksize, &nchunks, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, frame->schunk->storage->io); if (rc < 0) { BLOSC_TRACE_ERROR("Unable to get meta info from frame."); return NULL; } if (nchunk >= nchunks) { BLOSC_TRACE_ERROR("The chunk must already exist."); return NULL; } int32_t chunk_cbytes; rc = blosc2_cbuffer_sizes(chunk, NULL, &chunk_cbytes, NULL); if (rc < 0) { return NULL; } // Get the current offsets int32_t off_nbytes; if (!blosc2_nchunks_to_offsets_nbytes(nchunks, &off_nbytes)) { BLOSC_TRACE_ERROR("Too many chunks for offsets representation."); return NULL; } int64_t* offsets = (int64_t *) malloc((size_t)off_nbytes); if (offsets == NULL) { BLOSC_TRACE_ERROR("Cannot allocate memory for offsets."); return NULL; } if (nchunks > 0) { int32_t coffsets_cbytes = 0; uint8_t *coffsets = get_coffsets(frame, header_len, cbytes, nchunks, &coffsets_cbytes); if (coffsets == NULL) { BLOSC_TRACE_ERROR("Cannot get the offsets for the frame."); return NULL; } // Decompress offsets blosc2_dparams off_dparams = BLOSC2_DPARAMS_DEFAULTS; blosc2_context *dctx = blosc2_create_dctx(off_dparams); if (dctx == NULL) { BLOSC_TRACE_ERROR("Error while creating the decompression context"); return NULL; } int32_t prev_nbytes = blosc2_decompress_ctx(dctx, coffsets, coffsets_cbytes, offsets, off_nbytes); blosc2_free_ctx(dctx); if (prev_nbytes < 0) { free(offsets); BLOSC_TRACE_ERROR("Cannot decompress the offsets chunk."); return NULL; } } int32_t cbytes_old = 0; int64_t old_offset = 0; if (!frame->sframe) { // See how big would be the space old_offset = offsets[nchunk]; bool needs_free; uint8_t *chunk_old; int err = blosc2_schunk_get_chunk(schunk, nchunk, &chunk_old, &needs_free); if (err < 0) { BLOSC_TRACE_ERROR("%" PRId64 " chunk can not be obtained from schunk.", nchunk); return NULL; } if (chunk_old == NULL) { cbytes_old = 0; } else { cbytes_old = sw32_(chunk_old + BLOSC2_CHUNK_CBYTES); if (cbytes_old == BLOSC2_MAX_OVERHEAD) { cbytes_old = 0; } } if (needs_free) { free(chunk_old); } } // Add the new offset int64_t sframe_chunk_id = -1; int64_t delta_cbytes = 0; bool old_chunk_is_regular = (!frame->sframe && old_offset >= 0); bool new_chunk_is_regular = true; int64_t new_chunk_offset = cbytes; if (frame->sframe) { if (offsets[nchunk] < 0) { sframe_chunk_id = -1; } else { // In case there was a reorder in a sframe sframe_chunk_id = offsets[nchunk]; } } int special_value = (chunk_[BLOSC2_CHUNK_BLOSC2_FLAGS] >> 4) & BLOSC2_SPECIAL_MASK; uint64_t offset_value = ((uint64_t)1 << 63); bool is_special_chunk = false; switch (special_value) { case BLOSC2_SPECIAL_ZERO: // Zero chunk. Code it in a special way. offset_value += (uint64_t)BLOSC2_SPECIAL_ZERO << (8 * 7); // indicate a chunk of zeros to_little(offsets + nchunk, &offset_value, sizeof(uint64_t)); is_special_chunk = true; break; case BLOSC2_SPECIAL_UNINIT: // Non initialized values chunk. Code it in a special way. offset_value += (uint64_t)BLOSC2_SPECIAL_UNINIT << (8 * 7); // indicate a chunk of uninit values to_little(offsets + nchunk, &offset_value, sizeof(uint64_t)); is_special_chunk = true; break; case BLOSC2_SPECIAL_NAN: // NaN chunk. Code it in a special way. offset_value += (uint64_t)BLOSC2_SPECIAL_NAN << (8 * 7); // indicate a chunk of NANs to_little(offsets + nchunk, &offset_value, sizeof(uint64_t)); is_special_chunk = true; break; default: if (frame->sframe) { if (sframe_chunk_id < 0) { for (int64_t i = 0; i < nchunks; ++i) { if (offsets[i] > sframe_chunk_id) { sframe_chunk_id = offsets[i]; } } offsets[nchunk] = ++sframe_chunk_id; } } else { if (old_chunk_is_regular) { new_chunk_offset = old_offset; } offsets[nchunk] = new_chunk_offset; } } if (is_special_chunk) { chunk_cbytes = 0; // Special chunks are encoded in offsets and have no stored payload. new_chunk_is_regular = false; } if (!frame->sframe) { if (old_chunk_is_regular) { delta_cbytes = (int64_t)chunk_cbytes - cbytes_old; for (int64_t i = 0; i < nchunks; ++i) { if (i == nchunk || offsets[i] < 0) { continue; } if (offsets[i] > old_offset) { offsets[i] += delta_cbytes; } } } else if (new_chunk_is_regular) { delta_cbytes = chunk_cbytes; } } // Re-compress the offsets again blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.splitmode = BLOSC_NEVER_SPLIT; cparams.typesize = sizeof(int64_t); cparams.blocksize = 16 * 1024; // based on experiments with create_frame.c bench cparams.nthreads = 4; // 4 threads seems a decent default for nowadays CPUs cparams.compcode = BLOSC_BLOSCLZ; blosc2_context* cctx = blosc2_create_cctx(cparams); if (cctx == NULL) { BLOSC_TRACE_ERROR("Error while creating the compression context"); return NULL; } void* off_chunk = malloc((size_t)off_nbytes + BLOSC2_MAX_OVERHEAD); int32_t new_off_cbytes = blosc2_compress_ctx(cctx, offsets, off_nbytes, off_chunk, off_nbytes + BLOSC2_MAX_OVERHEAD); blosc2_free_ctx(cctx); free(offsets); if (new_off_cbytes < 0) { free(off_chunk); return NULL; } int64_t new_cbytes = cbytes; if (!frame->sframe) { new_cbytes += delta_cbytes; } else { new_cbytes = schunk->cbytes; } int64_t new_frame_len; if (frame->sframe) { // The chunk is not stored in the frame new_frame_len = header_len + 0 + new_off_cbytes + frame->trailer_len; } else { new_frame_len = header_len + new_cbytes + new_off_cbytes + frame->trailer_len; } void* fp = NULL; if (frame->cframe != NULL) { uint8_t* framep = frame->cframe; if (new_frame_len > frame->len) { frame->cframe = framep = realloc(framep, (size_t)new_frame_len); if (framep == NULL) { BLOSC_TRACE_ERROR("Cannot realloc space for the frame."); return NULL; } } if (!frame->sframe && old_chunk_is_regular) { int64_t tail_src_offset = old_offset + cbytes_old; int64_t tail_dst_offset = old_offset + chunk_cbytes; int64_t tail_nbytes = cbytes - tail_src_offset; if (tail_nbytes > 0 && tail_src_offset != tail_dst_offset) { memmove(framep + header_len + tail_dst_offset, framep + header_len + tail_src_offset, (size_t)tail_nbytes); } } if (!frame->sframe && new_chunk_is_regular) { memcpy(framep + header_len + new_chunk_offset, chunk, (size_t)chunk_cbytes); } /* Copy the offsets */ memcpy(framep + header_len + new_cbytes, off_chunk, (size_t)new_off_cbytes); } else { int64_t wbytes; int64_t io_pos = 0; blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); return NULL; } if (frame->sframe) { // Create the chunks file, if it's a special value this will delete its old content if (sframe_chunk_id >= 0) { if (sframe_create_chunk(frame, chunk, sframe_chunk_id, chunk_cbytes) == NULL) { BLOSC_TRACE_ERROR("Cannot write the full chunk."); return NULL; } } // Update the offsets chunk in the chunks frame fp = sframe_open_index(frame->urlpath, "rb+", frame->schunk->storage->io); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return NULL; } io_pos = frame->file_offset + header_len + 0; } else { // Regular frame fp = io_cb->open(frame->urlpath, "rb+", frame->schunk->storage->io->params); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return NULL; } if (old_chunk_is_regular) { int64_t tail_src_offset = old_offset + cbytes_old; int64_t tail_dst_offset = old_offset + chunk_cbytes; int64_t tail_nbytes = cbytes - tail_src_offset; if (tail_nbytes > 0 && tail_src_offset != tail_dst_offset) { uint8_t *tail = malloc((size_t)tail_nbytes); if (tail == NULL) { io_cb->close(fp); BLOSC_TRACE_ERROR("Cannot allocate memory for frame payload compaction."); return NULL; } void *tail_src = tail; if (!io_cb->is_allocation_necessary) { tail_src = NULL; } int64_t rbytes = io_cb->read(&tail_src, 1, tail_nbytes, frame->file_offset + header_len + tail_src_offset, fp); if (rbytes != tail_nbytes) { free(tail); io_cb->close(fp); BLOSC_TRACE_ERROR("Cannot read the payload tail from frame."); return NULL; } if (!io_cb->is_allocation_necessary) { memcpy(tail, tail_src, (size_t)tail_nbytes); } wbytes = io_cb->write(tail, 1, tail_nbytes, frame->file_offset + header_len + tail_dst_offset, fp); free(tail); if (wbytes != tail_nbytes) { io_cb->close(fp); BLOSC_TRACE_ERROR("Cannot compact the payload tail in frame."); return NULL; } } } if (new_chunk_is_regular) { io_pos = frame->file_offset + header_len + new_chunk_offset; wbytes = io_cb->write(chunk, 1, chunk_cbytes, io_pos, fp); // the new chunk if (wbytes != chunk_cbytes) { BLOSC_TRACE_ERROR("Cannot write the full chunk to frame (wrote %" PRId64 " of %" PRId64 " bytes at position %" PRId64 ", nchunk=%" PRId64 ").", wbytes, (int64_t)chunk_cbytes, io_pos, nchunk); io_cb->close(fp); return NULL; } } io_pos = frame->file_offset + header_len + new_cbytes; } wbytes = io_cb->write(off_chunk, 1, new_off_cbytes, io_pos, fp); // the new offsets io_cb->close(fp); if (wbytes != new_off_cbytes) { BLOSC_TRACE_ERROR("Cannot write the offsets to frame."); return NULL; } // Invalidate the cache for chunk offsets if (frame->coffsets != NULL) { if (frame->coffsets_needs_free) free(frame->coffsets); frame->coffsets = NULL; } } free(chunk); // chunk has always to be a copy when reaching here... free(off_chunk); frame->len = new_frame_len; rc = frame_update_header(frame, schunk, false); if (rc < 0) { return NULL; } rc = frame_update_trailer(frame, schunk); if (rc < 0) { return NULL; } return frame; } void* frame_delete_chunk(blosc2_frame_s* frame, int64_t nchunk, blosc2_schunk* schunk) { int32_t header_len; int64_t frame_len; int64_t nbytes; int64_t cbytes; int32_t blocksize; int32_t chunksize; int64_t nchunks; int rc = get_header_info(frame, &header_len, &frame_len, &nbytes, &cbytes, &blocksize, &chunksize, &nchunks, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, frame->schunk->storage->io); if (rc < 0) { BLOSC_TRACE_ERROR("Unable to get meta info from frame."); return NULL; } // Get the current offsets int32_t off_nbytes; if (!blosc2_nchunks_to_offsets_nbytes(nchunks, &off_nbytes)) { BLOSC_TRACE_ERROR("Too many chunks for offsets representation."); return NULL; } int64_t* offsets = (int64_t *) malloc((size_t)off_nbytes); if (offsets == NULL) { BLOSC_TRACE_ERROR("Cannot allocate memory for offsets."); return NULL; } if (nchunks > 0) { int32_t coffsets_cbytes = 0; uint8_t *coffsets = get_coffsets(frame, header_len, cbytes, nchunks, &coffsets_cbytes); if (coffsets == NULL) { BLOSC_TRACE_ERROR("Cannot get the offsets for the frame."); return NULL; } // Decompress offsets blosc2_dparams off_dparams = BLOSC2_DPARAMS_DEFAULTS; blosc2_context *dctx = blosc2_create_dctx(off_dparams); if (dctx == NULL) { BLOSC_TRACE_ERROR("Error while creating the decompression context"); return NULL; } int32_t prev_nbytes = blosc2_decompress_ctx(dctx, coffsets, coffsets_cbytes, offsets, off_nbytes); blosc2_free_ctx(dctx); if (prev_nbytes < 0) { free(offsets); BLOSC_TRACE_ERROR("Cannot decompress the offsets chunk."); return NULL; } } // Delete the new offset for (int64_t i = nchunk; i < nchunks - 1; i++) { offsets[i] = offsets[i + 1]; } offsets[nchunks - 1] = 0; // Re-compress the offsets again blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.splitmode = BLOSC_NEVER_SPLIT; cparams.typesize = sizeof(int64_t); cparams.blocksize = 16 * 1024; // based on experiments with create_frame.c bench cparams.nthreads = 4; // 4 threads seems a decent default for nowadays CPUs cparams.compcode = BLOSC_BLOSCLZ; blosc2_context* cctx = blosc2_create_cctx(cparams); if (cctx == NULL) { BLOSC_TRACE_ERROR("Error while creating the compression context"); return NULL; } void* off_chunk = malloc((size_t)off_nbytes + BLOSC2_MAX_OVERHEAD); int32_t new_off_cbytes = blosc2_compress_ctx(cctx, offsets, off_nbytes - (int32_t)sizeof(int64_t), off_chunk, off_nbytes + BLOSC2_MAX_OVERHEAD); blosc2_free_ctx(cctx); free(offsets); if (new_off_cbytes < 0) { free(off_chunk); return NULL; } int64_t new_cbytes = cbytes; int64_t new_frame_len; if (frame->sframe) { new_frame_len = header_len + 0 + new_off_cbytes + frame->trailer_len; } else { new_frame_len = header_len + new_cbytes + new_off_cbytes + frame->trailer_len; } // Add the chunk and update meta FILE* fp = NULL; if (frame->cframe != NULL) { uint8_t* framep = frame->cframe; /* Make space for the new chunk and copy it */ frame->cframe = framep = realloc(framep, (size_t)new_frame_len); if (framep == NULL) { BLOSC_TRACE_ERROR("Cannot realloc space for the frame."); return NULL; } /* Copy the offsets */ memcpy(framep + header_len + new_cbytes, off_chunk, (size_t)new_off_cbytes); } else { blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); return NULL; } size_t wbytes; int64_t io_pos = 0; if (frame->sframe) { int64_t offset; rc = get_coffset(frame, header_len, cbytes, nchunk, nchunks, &offset); if (rc < 0) { BLOSC_TRACE_ERROR("Unable to get offset to chunk %" PRId64 ".", nchunk); return NULL; } if (offset >= 0){ // Remove the chunk file only if it is not a special value chunk int err = sframe_delete_chunk(frame->urlpath, offset); if (err != 0) { BLOSC_TRACE_ERROR("Unable to delete chunk!"); return NULL; } } // Update the offsets chunk in the chunks frame fp = sframe_open_index(frame->urlpath, "rb+", frame->schunk->storage->io); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return NULL; } io_pos = frame->file_offset + header_len + 0; } else { // Regular frame fp = io_cb->open(frame->urlpath, "rb+", frame->schunk->storage->io); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return NULL; } io_pos = frame->file_offset + header_len + cbytes; } wbytes = io_cb->write(off_chunk, 1, new_off_cbytes, io_pos, fp); // the new offsets io_cb->close(fp); if (wbytes != (size_t)new_off_cbytes) { BLOSC_TRACE_ERROR("Cannot write the offsets to frame."); return NULL; } // Invalidate the cache for chunk offsets if (frame->coffsets != NULL) { if (frame->coffsets_needs_free) free(frame->coffsets); frame->coffsets = NULL; } } free(off_chunk); frame->len = new_frame_len; rc = frame_update_header(frame, schunk, false); if (rc < 0) { return NULL; } rc = frame_update_trailer(frame, schunk); if (rc < 0) { return NULL; } return frame; } int frame_reorder_offsets(blosc2_frame_s* frame, const int64_t* offsets_order, blosc2_schunk* schunk) { // Get header info int32_t header_len; int64_t frame_len; int64_t nbytes; int64_t cbytes; int32_t blocksize; int32_t chunksize; int64_t nchunks; int ret = get_header_info(frame, &header_len, &frame_len, &nbytes, &cbytes, &blocksize, &chunksize, &nchunks, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, frame->schunk->storage->io); if (ret < 0) { BLOSC_TRACE_ERROR("Cannot get the header info for the frame."); return ret; } // Get the current offsets and add one more int32_t off_nbytes; if (!blosc2_nchunks_to_offsets_nbytes(nchunks, &off_nbytes)) { BLOSC_TRACE_ERROR("Too many chunks for offsets representation."); return BLOSC2_ERROR_DATA; } int64_t* offsets = (int64_t *) malloc((size_t)off_nbytes); if (offsets == NULL) { BLOSC_TRACE_ERROR("Cannot allocate memory for offsets."); return BLOSC2_ERROR_MEMORY_ALLOC; } int32_t coffsets_cbytes = 0; uint8_t *coffsets = get_coffsets(frame, header_len, cbytes, nchunks, &coffsets_cbytes); if (coffsets == NULL) { BLOSC_TRACE_ERROR("Cannot get the offsets for the frame."); free(offsets); return BLOSC2_ERROR_DATA; } // Decompress offsets blosc2_dparams off_dparams = BLOSC2_DPARAMS_DEFAULTS; blosc2_context *dctx = blosc2_create_dctx(off_dparams); if (dctx == NULL) { BLOSC_TRACE_ERROR("Error while creating the decompression context"); return BLOSC2_ERROR_NULL_POINTER; } int32_t prev_nbytes = blosc2_decompress_ctx(dctx, coffsets, coffsets_cbytes, offsets, off_nbytes); blosc2_free_ctx(dctx); if (prev_nbytes < 0) { free(offsets); BLOSC_TRACE_ERROR("Cannot decompress the offsets chunk."); return prev_nbytes; } // Make a copy of the chunk offsets and reorder it int64_t *offsets_copy = malloc(prev_nbytes); memcpy(offsets_copy, offsets, prev_nbytes); for (int64_t i = 0; i < nchunks; ++i) { offsets[i] = offsets_copy[offsets_order[i]]; } free(offsets_copy); // Re-compress the offsets again blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.splitmode = BLOSC_NEVER_SPLIT; cparams.typesize = sizeof(int64_t); cparams.blocksize = 16 * 1024; // based on experiments with create_frame.c bench cparams.nthreads = 4; // 4 threads seems a decent default for nowadays CPUs cparams.compcode = BLOSC_BLOSCLZ; blosc2_context* cctx = blosc2_create_cctx(cparams); if (cctx == NULL) { BLOSC_TRACE_ERROR("Error while creating the compression context"); return BLOSC2_ERROR_NULL_POINTER; } void* off_chunk = malloc((size_t)off_nbytes + BLOSC2_MAX_OVERHEAD); int32_t new_off_cbytes = blosc2_compress_ctx(cctx, offsets, off_nbytes, off_chunk, off_nbytes + BLOSC2_MAX_OVERHEAD); blosc2_free_ctx(cctx); if (new_off_cbytes < 0) { free(offsets); free(off_chunk); return new_off_cbytes; } free(offsets); int64_t new_frame_len; if (frame->sframe) { // The chunks are not in the frame new_frame_len = header_len + 0 + new_off_cbytes + frame->trailer_len; } else { new_frame_len = header_len + cbytes + new_off_cbytes + frame->trailer_len; } if (frame->cframe != NULL) { uint8_t* framep = frame->cframe; /* Make space for the new chunk and copy it */ frame->cframe = framep = realloc(framep, (size_t)new_frame_len); if (framep == NULL) { BLOSC_TRACE_ERROR("Cannot realloc space for the frame."); return BLOSC2_ERROR_MEMORY_ALLOC; } /* Copy the offsets */ memcpy(framep + header_len + cbytes, off_chunk, (size_t)new_off_cbytes); } else { void* fp = NULL; blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); return BLOSC2_ERROR_PLUGIN_IO; } int64_t io_pos = 0; if (frame->sframe) { // Update the offsets chunk in the chunks frame fp = sframe_open_index(frame->urlpath, "rb+", frame->schunk->storage->io); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return BLOSC2_ERROR_FILE_OPEN; } io_pos = frame->file_offset + header_len + 0; } else { // Regular frame fp = io_cb->open(frame->urlpath, "rb+", frame->schunk->storage->io->params); if (fp == NULL) { BLOSC_TRACE_ERROR("Error opening file in: %s", frame->urlpath); return BLOSC2_ERROR_FILE_OPEN; } io_pos = frame->file_offset + header_len + cbytes; } int64_t wbytes = io_cb->write(off_chunk, 1, new_off_cbytes, io_pos, fp); // the new offsets io_cb->close(fp); if (wbytes != new_off_cbytes) { BLOSC_TRACE_ERROR("Cannot write the offsets to frame."); return BLOSC2_ERROR_FILE_WRITE; } } // Invalidate the cache for chunk offsets if (frame->coffsets != NULL) { if (frame->coffsets_needs_free) free(frame->coffsets); frame->coffsets = NULL; } free(off_chunk); frame->len = new_frame_len; int rc = frame_update_header(frame, schunk, false); if (rc < 0) { return rc; } rc = frame_update_trailer(frame, schunk); if (rc < 0) { return rc; } return 0; } /* Decompress and return a chunk that is part of a frame. */ int frame_decompress_chunk(blosc2_context *dctx, blosc2_frame_s* frame, int64_t nchunk, void *dest, int32_t nbytes) { uint8_t* src; bool needs_free; int32_t chunk_nbytes; int32_t chunk_cbytes; int rc; rc = frame_get_chunk(frame, nchunk, &src, &needs_free); if (rc < 0) { BLOSC_TRACE_ERROR("Cannot get the chunk in position %" PRId64 ".", nchunk); goto end; } chunk_cbytes = rc; if (chunk_cbytes < (signed)sizeof(int32_t)) { /* Not enough input to read `nbytes` */ rc = BLOSC2_ERROR_READ_BUFFER; } rc = blosc2_cbuffer_sizes(src, &chunk_nbytes, &chunk_cbytes, NULL); if (rc < 0) { goto end; } /* Create a buffer for destination */ if (chunk_nbytes > nbytes) { BLOSC_TRACE_ERROR("Not enough space for decompressing in dest."); rc = BLOSC2_ERROR_WRITE_BUFFER; goto end; } /* And decompress it */ dctx->header_overhead = BLOSC_EXTENDED_HEADER_LENGTH; int chunksize = rc = blosc2_decompress_ctx(dctx, src, chunk_cbytes, dest, nbytes); if (chunksize < 0 || chunksize != chunk_nbytes) { BLOSC_TRACE_ERROR("Error in decompressing chunk."); if (chunksize >= 0) rc = BLOSC2_ERROR_FAILURE; } end: if (needs_free) { free(src); } return rc; } c-blosc2-3.1.5/blosc/frame.h000066400000000000000000000163011521743436100155000ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #ifndef BLOSC_FRAME_H #define BLOSC_FRAME_H #include "blosc2.h" #include #include // Different types of frames #define FRAME_CONTIGUOUS_TYPE 0 #define FRAME_DIRECTORY_TYPE 1 // Constants for metadata placement in header #define FRAME_VARIABLE_CHUNKS (1U << 6) #define FRAME_VL_BLOCKS (1U << 7) #define FRAME_HEADER_MAGIC 2 #define FRAME_HEADER_LEN (FRAME_HEADER_MAGIC + 8 + 1) // 11 #define FRAME_LEN (FRAME_HEADER_LEN + 4 + 1) // 16 #define FRAME_FLAGS (FRAME_LEN + 8 + 1) // 25 #define FRAME_TYPE (FRAME_FLAGS + 1) // 26 #define FRAME_CODECS (FRAME_FLAGS + 2) // 27 #define FRAME_OTHER_FLAGS (FRAME_FLAGS + 3) // 28 #define FRAME_NBYTES (FRAME_FLAGS + 4 + 1) // 30 #define FRAME_CBYTES (FRAME_NBYTES + 8 + 1) // 39 #define FRAME_TYPESIZE (FRAME_CBYTES + 8 + 1) // 48 #define FRAME_BLOCKSIZE (FRAME_TYPESIZE + 4 + 1) // 53 #define FRAME_CHUNKSIZE (FRAME_BLOCKSIZE + 4 + 1) // 58 #define FRAME_NTHREADS_C (FRAME_CHUNKSIZE + 4 + 1) // 63 #define FRAME_NTHREADS_D (FRAME_NTHREADS_C + 2 + 1) // 66 #define FRAME_HAS_VLMETALAYERS (FRAME_NTHREADS_D + 2) // 68 #define FRAME_FILTER_PIPELINE (FRAME_HAS_VLMETALAYERS + 1 + 1) // 70 #define FRAME_UDCODEC (FRAME_FILTER_PIPELINE + 1 + 6) // 77 #define FRAME_CODEC_META (FRAME_FILTER_PIPELINE + 1 + 7) // 78 #define FRAME_OTHER_FLAGS2 (FRAME_FILTER_PIPELINE + 1 + 14) // 85 #define FRAME_USE_DICT (1U << 0) //!< bit 0 of other_flags2: use dictionary compression #define FRAME_HEADER_MINLEN (FRAME_FILTER_PIPELINE + 1 + 16) // 87 <- minimum length #define FRAME_METALAYERS (FRAME_HEADER_MINLEN) // 87 #define FRAME_IDX_SIZE (FRAME_METALAYERS + 1 + 1) // 89 #define FRAME_FILTER_PIPELINE_MAX (8) // the maximum number of filters that can be stored in header #define FRAME_TRAILER_VERSION_BETA2 (0U) // for beta.2 and former #define FRAME_TRAILER_VERSION (1U) // can be up to 127 #define FRAME_TRAILER_MINLEN (25) // minimum length for the trailer (msgpack overhead) #define FRAME_TRAILER_LEN_OFFSET (22) // offset to trailer length (counting from the end) #define FRAME_TRAILER_VLMETALAYERS (2) typedef struct { char* urlpath; //!< The name of the file or directory if it's an sframe; if NULL, this is in-memory uint8_t* cframe; //!< The in-memory, contiguous frame buffer bool avoid_cframe_free; //!< Whether the cframe can be freed (false) or not (true). uint8_t* coffsets; //!< Pointers to the (compressed, on-disk) chunk offsets bool coffsets_needs_free; //!< Whether the coffsets memory need to be freed or not. int64_t len; //!< The current length of the frame in (compressed) bytes int64_t maxlen; //!< The maximum length of the frame; if 0, there is no maximum uint32_t trailer_len; //!< The current length of the trailer in (compressed) bytes bool sframe; //!< Whether the frame is sparse (true) or not blosc2_schunk *schunk; //!< The schunk associated int64_t file_offset; //!< The offset where the frame starts inside the file } blosc2_frame_s; /********************************************************************* Frame struct related functions. These are rather low-level and the blosc2_schunk interface is recommended instead. *********************************************************************/ /** * @brief Create a new frame. * * @param urlpath The filename of the frame. If not persistent, pass NULL. * * @return The new frame. */ blosc2_frame_s* frame_new(const char* urlpath); /** * @brief Create a frame from a super-chunk. * * @param schunk The super-chunk from where the frame will be created. * @param frame The pointer for the frame that will be populated. * * @note If frame->urlpath is NULL, a frame is created in-memory; else it is created * on-disk. * * @return The size in bytes of the frame. If an error occurs it returns a negative value. */ int64_t frame_from_schunk(blosc2_schunk* schunk, blosc2_frame_s* frame); /** * @brief Set `avoid_cframe_free` from @param frame to @param avoid_cframe_free. * * @param frame The frame to set the property to. * @param avoid_cframe_free The value to set in @param frame. * @warning If you set it to `true` you will be responsible of freeing it. */ void frame_avoid_cframe_free(blosc2_frame_s* frame, bool avoid_cframe_free); /** * @brief Free all memory from a frame. * * @param frame The frame to be freed. * * @return 0 if succeeds. */ int frame_free(blosc2_frame_s *frame); /** * @brief Initialize a frame out of a file. * * @param urlpath The file name. * * @return The frame created from the file. */ blosc2_frame_s* frame_from_file_offset(const char *urlpath, const blosc2_io *io_cb, int64_t offset); /** * @brief Initialize a frame out of a contiguous frame buffer. * * @param cframe The buffer for the frame. * @param len The length of buffer for the frame. * @param copy Whether the frame buffer should be copied internally or not. * * @return The frame created from the contiguous frame buffer. * * @note The user is responsible to `free` the returned frame. */ blosc2_frame_s* frame_from_cframe(uint8_t *cframe, int64_t len, bool copy); /** * @brief Create a super-chunk from a frame. * * @param frame The frame from which the super-chunk will be created. * @param copy If true, a new frame buffer is created * internally to serve as storage for the super-chunk. Else, the * super-chunk will be backed by @p frame (i.e. no copies are made). * * @return The super-chunk corresponding to the frame. */ blosc2_schunk* frame_to_schunk(blosc2_frame_s* frame, bool copy, const blosc2_io *udio); blosc2_storage * get_new_storage(const blosc2_storage *storage, const blosc2_cparams *cdefaults, const blosc2_dparams *ddefaults, const blosc2_io *iodefaults); void* frame_append_chunk(blosc2_frame_s* frame, void* chunk, blosc2_schunk* schunk); void* frame_insert_chunk(blosc2_frame_s* frame, int64_t nchunk, void* chunk, blosc2_schunk* schunk); void* frame_update_chunk(blosc2_frame_s* frame, int64_t nchunk, void* chunk, blosc2_schunk* schunk); void* frame_delete_chunk(blosc2_frame_s* frame, int64_t nchunk, blosc2_schunk* schunk); int frame_reorder_offsets(blosc2_frame_s *frame, const int64_t *offsets_order, blosc2_schunk* schunk); int frame_get_chunk(blosc2_frame_s* frame, int64_t nchunk, uint8_t **chunk, bool *needs_free); int frame_get_lazychunk(blosc2_frame_s* frame, int64_t nchunk, uint8_t **chunk, bool *needs_free); int frame_decompress_chunk(blosc2_context* dctx, blosc2_frame_s* frame, int64_t nchunk, void *dest, int32_t nbytes); int frame_update_header(blosc2_frame_s* frame, blosc2_schunk* schunk, bool new); int frame_update_trailer(blosc2_frame_s* frame, blosc2_schunk* schunk); int64_t frame_fill_special(blosc2_frame_s* frame, int64_t nitems, int special_value, int32_t chunksize, blosc2_schunk* schunk); #endif /* BLOSC_FRAME_H */ c-blosc2-3.1.5/blosc/schunk-private.h000066400000000000000000000024371521743436100173560ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #ifndef BLOSC_SCHUNK_PRIVATE_H #define BLOSC_SCHUNK_PRIVATE_H #include "b2nd.h" #include #include #include /********************************************************************* Functions meant to be used internally. *********************************************************************/ /** * @brief Get the chunk indexes needed to get the slice. * * @param schunk The super-chunk. * @param start Index (0-based) where the slice begins. * @param stop The first index (0-based) that is not in the selected slice. * @param chunks_idx The pointer to the buffer where the indexes will be written. * * * @return The number of chunks needed to get the slice. If some problem is * detected, a negative code is returned instead. */ int64_t schunk_get_slice_nchunks(blosc2_schunk *schunk, int64_t start, int64_t stop, int64_t **chunks_idx); #endif /* BLOSC_SCHUNK_PRIVATE_H */ c-blosc2-3.1.5/blosc/schunk.c000066400000000000000000002332361521743436100157040ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #include "frame.h" #include "stune.h" #include "blosc-private.h" #include "context.h" #include "blosc2/tuners-registry.h" #include "blosc2.h" #if defined(_WIN32) #include #include #include #define mkdir(D, M) _mkdir(D) #endif /* _WIN32 */ #include #include #include #include #include #include #include /* If C11 is supported, use it's built-in aligned allocation. */ #if __STDC_VERSION__ >= 201112L #include #endif static int schunk_get_chunk_flags2(blosc2_schunk *schunk, int64_t nchunk, uint8_t *chunk_flags2); static int validate_nchunk(blosc2_schunk *schunk, int64_t nchunk, bool allow_end, const char *func_name) { if (nchunk < 0) { BLOSC_TRACE_ERROR("nchunk ('%" PRId64 "') is negative in %s.", nchunk, func_name); return BLOSC2_ERROR_INVALID_PARAM; } if (allow_end) { if (nchunk > schunk->nchunks) { BLOSC_TRACE_ERROR("nchunk ('%" PRId64 "') is out of range [0, %" PRId64 "] in %s.", nchunk, schunk->nchunks, func_name); return BLOSC2_ERROR_INVALID_PARAM; } } else { if (nchunk >= schunk->nchunks) { BLOSC_TRACE_ERROR("nchunk ('%" PRId64 "') exceeds the number of chunks " "('%" PRId64 "') in %s.", nchunk, schunk->nchunks, func_name); return BLOSC2_ERROR_INVALID_PARAM; } } return BLOSC2_ERROR_SUCCESS; } /* Get the cparams associated with a super-chunk */ int blosc2_schunk_get_cparams(blosc2_schunk *schunk, blosc2_cparams **cparams) { *cparams = calloc(1, sizeof(blosc2_cparams)); (*cparams)->schunk = schunk; for (int i = 0; i < BLOSC2_MAX_FILTERS; i++) { (*cparams)->filters[i] = schunk->filters[i]; (*cparams)->filters_meta[i] = schunk->filters_meta[i]; } (*cparams)->compcode = schunk->compcode; (*cparams)->compcode_meta = schunk->compcode_meta; (*cparams)->clevel = schunk->clevel; (*cparams)->typesize = schunk->typesize; (*cparams)->blocksize = schunk->blocksize; (*cparams)->splitmode = schunk->splitmode; (*cparams)->use_dict = schunk->use_dict; if (schunk->cctx == NULL) { (*cparams)->nthreads = blosc2_get_nthreads(); } else { (*cparams)->nthreads = (int16_t)schunk->cctx->nthreads; } return 0; } /* Get the dparams associated with a super-chunk */ int blosc2_schunk_get_dparams(blosc2_schunk *schunk, blosc2_dparams **dparams) { *dparams = calloc(1, sizeof(blosc2_dparams)); (*dparams)->schunk = schunk; if (schunk->dctx == NULL) { (*dparams)->nthreads = blosc2_get_nthreads(); } else { (*dparams)->nthreads = schunk->dctx->nthreads; } return 0; } int update_schunk_properties(struct blosc2_schunk* schunk) { blosc2_cparams* cparams = schunk->storage->cparams; blosc2_dparams* dparams = schunk->storage->dparams; for (int i = 0; i < BLOSC2_MAX_FILTERS; i++) { schunk->filters[i] = cparams->filters[i]; schunk->filters_meta[i] = cparams->filters_meta[i]; } schunk->compcode = cparams->compcode; schunk->compcode_meta = cparams->compcode_meta; schunk->clevel = cparams->clevel; schunk->splitmode = cparams->splitmode; schunk->use_dict = (uint8_t)cparams->use_dict; schunk->typesize = cparams->typesize; schunk->blocksize = cparams->blocksize; schunk->chunksize = -1; schunk->flags2 = 0; schunk->tuner_params = cparams->tuner_params; schunk->tuner_id = cparams->tuner_id; if (cparams->tuner_id == BLOSC_BTUNE) { cparams->use_dict = 0; } /* The compression context */ if (schunk->cctx != NULL) { blosc2_free_ctx(schunk->cctx); } cparams->schunk = schunk; schunk->cctx = blosc2_create_cctx(*cparams); if (schunk->cctx == NULL) { BLOSC_TRACE_ERROR("Could not create compression ctx"); return BLOSC2_ERROR_NULL_POINTER; } /* The decompression context */ if (schunk->dctx != NULL) { blosc2_free_ctx(schunk->dctx); } dparams->schunk = schunk; schunk->dctx = blosc2_create_dctx(*dparams); if (schunk->dctx == NULL) { BLOSC_TRACE_ERROR("Could not create decompression ctx"); return BLOSC2_ERROR_NULL_POINTER; } return BLOSC2_ERROR_SUCCESS; } static bool file_exists (char *filename) { struct stat buffer; return (stat (filename, &buffer) == 0); } /* Create a new super-chunk */ blosc2_schunk* blosc2_schunk_new(blosc2_storage *storage) { blosc2_schunk* schunk = calloc(1, sizeof(blosc2_schunk)); schunk->version = 0; /* pre-first version */ schunk->view = false; /* not a view by default */ // Get the storage with proper defaults schunk->storage = get_new_storage(storage, &BLOSC2_CPARAMS_DEFAULTS, &BLOSC2_DPARAMS_DEFAULTS, &BLOSC2_IO_DEFAULTS); // Update the (local variable) storage storage = schunk->storage; char* tradeoff = getenv("BTUNE_TRADEOFF"); if (tradeoff != NULL) { // If BTUNE_TRADEOFF passed, automatically use btune storage->cparams->tuner_id = BLOSC_BTUNE; } // ...and update internal properties if (update_schunk_properties(schunk) < 0) { BLOSC_TRACE_ERROR("Error when updating schunk properties"); return NULL; } if (!storage->contiguous && storage->urlpath != NULL){ char* urlpath; char last_char = storage->urlpath[strlen(storage->urlpath) - 1]; urlpath = malloc(strlen(storage->urlpath) + 1); strcpy(urlpath, storage->urlpath); if (last_char == '\\' || last_char == '/') { urlpath[strlen(storage->urlpath) - 1] = '\0'; } // Create directory if (mkdir(urlpath, 0777) == -1) { BLOSC_TRACE_ERROR("Error during the creation of the directory, maybe it already exists."); return NULL; } // We want a sparse (directory) frame as storage blosc2_frame_s* frame = frame_new(urlpath); free(urlpath); if (frame == NULL) { BLOSC_TRACE_ERROR("Error creating sparse frame."); return NULL; } frame->sframe = true; // Initialize frame (basically, encode the header) frame->schunk = schunk; int64_t frame_len = frame_from_schunk(schunk, frame); if (frame_len < 0) { BLOSC_TRACE_ERROR("Error during the conversion of schunk to frame."); return NULL; } schunk->frame = (blosc2_frame*)frame; } if (storage->contiguous){ // We want a contiguous frame as storage if (storage->urlpath != NULL) { if (file_exists(storage->urlpath)) { BLOSC_TRACE_ERROR("You are trying to overwrite an existing frame. Remove it first!"); return NULL; } } blosc2_frame_s* frame = frame_new(storage->urlpath); if (frame == NULL) { BLOSC_TRACE_ERROR("Error creating contiguous frame."); return NULL; } frame->sframe = false; // Initialize frame (basically, encode the header) frame->schunk = schunk; int64_t frame_len = frame_from_schunk(schunk, frame); if (frame_len < 0) { BLOSC_TRACE_ERROR("Error during the conversion of schunk to frame."); return NULL; } schunk->frame = (blosc2_frame*)frame; } return schunk; } /* Create a copy of a super-chunk */ blosc2_schunk* blosc2_schunk_copy(blosc2_schunk *schunk, blosc2_storage *storage) { if (schunk == NULL) { BLOSC_TRACE_ERROR("Can not copy a NULL `schunk`."); return NULL; } // Check if cparams are equals bool cparams_equal = true; blosc2_cparams cparams = {0}; if (storage->cparams == NULL) { // When cparams are not specified, just use the same of schunk cparams.typesize = schunk->cctx->typesize; cparams.clevel = schunk->cctx->clevel; cparams.compcode = schunk->cctx->compcode; cparams.compcode_meta = schunk->cctx->compcode_meta; cparams.splitmode = schunk->cctx->splitmode; cparams.use_dict = schunk->cctx->use_dict; cparams.blocksize = schunk->cctx->blocksize; memcpy(cparams.filters, schunk->cctx->filters, BLOSC2_MAX_FILTERS); memcpy(cparams.filters_meta, schunk->cctx->filters_meta, BLOSC2_MAX_FILTERS); storage->cparams = &cparams; } else { cparams = *storage->cparams; } if (cparams.blocksize == 0) { // TODO: blocksize should be read from schunk->blocksize // For this, it should be updated during the first append // (or change API to make this a property during schunk creation). cparams.blocksize = schunk->cctx->blocksize; } if (cparams.typesize != schunk->cctx->typesize || cparams.clevel != schunk->cctx->clevel || cparams.compcode != schunk->cctx->compcode || cparams.use_dict != schunk->cctx->use_dict || cparams.blocksize != schunk->cctx->blocksize || // In case of prefilters or postfilters, force their execution. schunk->cctx->prefilter != NULL || schunk->dctx->postfilter != NULL) { cparams_equal = false; } for (int i = 0; i < BLOSC2_MAX_FILTERS; ++i) { if (cparams.filters[i] != schunk->cctx->filters[i] || cparams.filters_meta[i] != schunk->cctx->filters_meta[i]) { cparams_equal = false; } } // Create new schunk blosc2_schunk *new_schunk = blosc2_schunk_new(storage); if (new_schunk == NULL) { BLOSC_TRACE_ERROR("Can not create a new schunk"); return NULL; } // Set the chunksize for the schunk, as it cannot be derived from storage new_schunk->chunksize = schunk->chunksize; new_schunk->flags2 = schunk->flags2; // Copy metalayers for (int nmeta = 0; nmeta < schunk->nmetalayers; ++nmeta) { blosc2_metalayer *meta = schunk->metalayers[nmeta]; if (blosc2_meta_add(new_schunk, meta->name, meta->content, meta->content_len) < 0) { BLOSC_TRACE_ERROR("Can not add %s `metalayer`.", meta->name); return NULL; } } // Copy chunks bool uses_vlblocks = (schunk->flags2 & BLOSC2_VL_BLOCKS) != 0; if (cparams_equal || uses_vlblocks) { for (int nchunk = 0; nchunk < schunk->nchunks; ++nchunk) { uint8_t *chunk; bool needs_free; int rc = blosc2_schunk_get_chunk(schunk, nchunk, &chunk, &needs_free); if (rc < 0) { BLOSC_TRACE_ERROR("Can not get the `chunk` %d.", nchunk); return NULL; } rc = blosc2_schunk_append_chunk(new_schunk, chunk, !needs_free); if (rc < 0) { BLOSC_TRACE_ERROR("Can not append the `chunk` into super-chunk."); return NULL; } } } else { int32_t chunksize = schunk->chunksize == -1 ? 0 : schunk->chunksize; uint8_t *buffer = malloc(chunksize); for (int nchunk = 0; nchunk < schunk->nchunks; ++nchunk) { if (blosc2_schunk_decompress_chunk(schunk, nchunk, buffer, schunk->chunksize) < 0) { BLOSC_TRACE_ERROR("Can not decompress the `chunk` %d.", nchunk); return NULL; } if (blosc2_schunk_append_buffer(new_schunk, buffer, schunk->chunksize) < 0) { BLOSC_TRACE_ERROR("Can not append the `buffer` into super-chunk."); return NULL; } } free(buffer); } // Copy vlmetalayers for (int nmeta = 0; nmeta < schunk->nvlmetalayers; ++nmeta) { uint8_t *content; int32_t content_len; char* name = schunk->vlmetalayers[nmeta]->name; if (blosc2_vlmeta_get(schunk, name, &content, &content_len) < 0) { BLOSC_TRACE_ERROR("Can not get %s `vlmetalayer`.", name); } if (blosc2_vlmeta_add(new_schunk, name, content, content_len, NULL) < 0) { BLOSC_TRACE_ERROR("Can not add %s `vlmetalayer`.", name); return NULL; } free(content); } return new_schunk; } /* Open an existing super-chunk that is on-disk (no copy is made). */ blosc2_schunk* blosc2_schunk_open_udio(const char* urlpath, const blosc2_io *udio) { return blosc2_schunk_open_offset_udio(urlpath, 0, udio); } blosc2_schunk* blosc2_schunk_open_offset_udio(const char* urlpath, int64_t offset, const blosc2_io *udio) { if (urlpath == NULL) { BLOSC_TRACE_ERROR("You need to supply a urlpath."); return NULL; } blosc2_frame_s* frame = frame_from_file_offset(urlpath, udio, offset); if (frame == NULL) { blosc2_io_cb *io_cb = blosc2_get_io_cb(udio->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); return NULL; } int rc = io_cb->destroy(udio->params); if (rc < 0) { BLOSC_TRACE_ERROR("Cannot destroy the input/output object."); } return NULL; } blosc2_schunk* schunk = frame_to_schunk(frame, false, udio); if (schunk == NULL) { BLOSC_TRACE_ERROR("Error converting frame to super-chunk"); return NULL; } // Set the storage with proper defaults size_t pathlen = strlen(urlpath); schunk->storage->urlpath = malloc(pathlen + 1); strcpy(schunk->storage->urlpath, urlpath); schunk->storage->contiguous = !frame->sframe; return schunk; } blosc2_schunk* blosc2_schunk_open(const char* urlpath) { return blosc2_schunk_open_udio(urlpath, &BLOSC2_IO_DEFAULTS); } blosc2_schunk* blosc2_schunk_open_offset(const char* urlpath, int64_t offset) { return blosc2_schunk_open_offset_udio(urlpath, offset, &BLOSC2_IO_DEFAULTS); } int64_t blosc2_schunk_to_buffer(blosc2_schunk* schunk, uint8_t** dest, bool* needs_free) { blosc2_frame_s* frame; int64_t cframe_len; // Initialize defaults in case of errors *dest = NULL; *needs_free = false; if ((schunk->storage->contiguous == true) && (schunk->storage->urlpath == NULL)) { frame = (blosc2_frame_s*)(schunk->frame); *dest = frame->cframe; cframe_len = frame->len; *needs_free = false; } else { // Copy to a contiguous storage blosc2_storage frame_storage = {.contiguous=true}; blosc2_schunk* schunk_copy = blosc2_schunk_copy(schunk, &frame_storage); if (schunk_copy == NULL) { BLOSC_TRACE_ERROR("Error during the conversion of schunk to buffer."); return BLOSC2_ERROR_SCHUNK_COPY; } frame = (blosc2_frame_s*)(schunk_copy->frame); *dest = frame->cframe; cframe_len = frame->len; *needs_free = true; frame->avoid_cframe_free = true; blosc2_schunk_free(schunk_copy); } return cframe_len; } /* Write an in-memory frame out to a file. */ int64_t frame_to_file(blosc2_frame_s* frame, const char* urlpath) { blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); return BLOSC2_ERROR_PLUGIN_IO; } void* fp = io_cb->open(urlpath, "wb", frame->schunk->storage->io); int64_t io_pos = 0; int64_t nitems = io_cb->write(frame->cframe, frame->len, 1, io_pos, fp); io_cb->close(fp); return nitems * frame->len; } /* Append an in-memory frame to a file. */ int64_t append_frame_to_file(blosc2_frame_s* frame, const char* urlpath) { blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); return BLOSC2_ERROR_PLUGIN_IO; } void* fp = io_cb->open(urlpath, "ab", frame->schunk->storage->io); int64_t io_pos = io_cb->size(fp); io_cb->write(frame->cframe, frame->len, 1, io_pos, fp); io_cb->close(fp); return io_pos; } /* Write super-chunk out to a file. */ int64_t blosc2_schunk_to_file(blosc2_schunk* schunk, const char* urlpath) { if (urlpath == NULL) { BLOSC_TRACE_ERROR("urlpath cannot be NULL"); return BLOSC2_ERROR_INVALID_PARAM; } // Accelerated path for in-memory frames if (schunk->storage->contiguous && schunk->storage->urlpath == NULL) { int64_t len = frame_to_file((blosc2_frame_s*)(schunk->frame), urlpath); if (len <= 0) { BLOSC_TRACE_ERROR("Error writing to file"); return len; } return len; } // Copy to a contiguous file blosc2_storage frame_storage = {.contiguous=true, .urlpath=(char*)urlpath}; blosc2_schunk* schunk_copy = blosc2_schunk_copy(schunk, &frame_storage); if (schunk_copy == NULL) { BLOSC_TRACE_ERROR("Error during the conversion of schunk to buffer."); return BLOSC2_ERROR_SCHUNK_COPY; } blosc2_frame_s* frame = (blosc2_frame_s*)(schunk_copy->frame); int64_t frame_len = frame->len; blosc2_schunk_free(schunk_copy); return frame_len; } /* Append a super-chunk to a file. */ int64_t blosc2_schunk_append_file(blosc2_schunk* schunk, const char* urlpath) { if (urlpath == NULL) { BLOSC_TRACE_ERROR("urlpath cannot be NULL"); return BLOSC2_ERROR_INVALID_PARAM; } // Accelerated path for in-memory frames if (schunk->storage->contiguous && schunk->storage->urlpath == NULL) { int64_t offset = append_frame_to_file((blosc2_frame_s*)(schunk->frame), urlpath); if (offset <= 0) { BLOSC_TRACE_ERROR("Error writing to file"); return offset; } return offset; } // Copy to a contiguous file blosc2_storage frame_storage = {.contiguous=true, .urlpath=NULL}; blosc2_schunk* schunk_copy = blosc2_schunk_copy(schunk, &frame_storage); if (schunk_copy == NULL) { BLOSC_TRACE_ERROR("Error during the conversion of schunk to buffer."); return BLOSC2_ERROR_SCHUNK_COPY; } blosc2_frame_s* frame = (blosc2_frame_s*)(schunk_copy->frame); int64_t offset = append_frame_to_file(frame, urlpath); blosc2_schunk_free(schunk_copy); return offset; } /* Free all memory from a super-chunk. */ int blosc2_schunk_free(blosc2_schunk *schunk) { int err = 0; // If it is a view, the data belongs to original array and should not be freed if (schunk->data != NULL && !schunk->view) { for (int i = 0; i < schunk->nchunks; i++) { free(schunk->data[i]); } free(schunk->data); } if (schunk->cctx != NULL) blosc2_free_ctx(schunk->cctx); if (schunk->dctx != NULL) blosc2_free_ctx(schunk->dctx); if (schunk->blockshape != NULL) free(schunk->blockshape); if (schunk->nmetalayers > 0) { for (int i = 0; i < schunk->nmetalayers; i++) { if (schunk->metalayers[i] != NULL) { if (schunk->metalayers[i]->name != NULL) free(schunk->metalayers[i]->name); if (schunk->metalayers[i]->content != NULL) free(schunk->metalayers[i]->content); free(schunk->metalayers[i]); } } schunk->nmetalayers = 0; } if (schunk->storage != NULL) { blosc2_io_cb *io_cb = blosc2_get_io_cb(schunk->storage->io->id); if (io_cb != NULL) { int rc = io_cb->destroy(schunk->storage->io->params); if (rc < 0) { BLOSC_TRACE_ERROR("Could not free the I/O resources."); err = 1; } } if (schunk->storage->urlpath != NULL) { free(schunk->storage->urlpath); } free(schunk->storage->cparams); free(schunk->storage->dparams); free(schunk->storage->io); free(schunk->storage); } // If it is a view, the frame belongs to original array and should not be freed if (schunk->frame != NULL && !schunk->view) { frame_free((blosc2_frame_s *) schunk->frame); } if (schunk->nvlmetalayers > 0) { for (int i = 0; i < schunk->nvlmetalayers; ++i) { if (schunk->vlmetalayers[i] != NULL) { if (schunk->vlmetalayers[i]->name != NULL) free(schunk->vlmetalayers[i]->name); if (schunk->vlmetalayers[i]->content != NULL) free(schunk->vlmetalayers[i]->content); free(schunk->vlmetalayers[i]); } } } free(schunk); return err; } /* Create a super-chunk out of a contiguous frame buffer */ blosc2_schunk* blosc2_schunk_from_buffer(uint8_t *cframe, int64_t len, bool copy) { // Check that the buffer actually comes from a cframe if (cframe == NULL || len < FRAME_HEADER_MINLEN) { return NULL; } char *magic_number = (char *)cframe + FRAME_HEADER_MAGIC; if (memcmp(magic_number, "b2frame", sizeof("b2frame")) != 0) { return NULL; } blosc2_frame_s* frame = frame_from_cframe(cframe, len, false); if (frame == NULL) { return NULL; } blosc2_schunk* schunk = frame_to_schunk(frame, copy, &BLOSC2_IO_DEFAULTS); if (schunk && copy) { // Super-chunk has its own copy of frame frame_free(frame); } return schunk; } /* Set whether freeing this super-chunk should avoid freeing its contiguous frame buffer */ void blosc2_schunk_avoid_cframe_free(blosc2_schunk *schunk, bool avoid_cframe_free) { blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; if (frame) { frame_avoid_cframe_free(frame, avoid_cframe_free); } } /* Fill an empty frame with special values (fast path). */ int64_t blosc2_schunk_fill_special(blosc2_schunk* schunk, int64_t nitems, int special_value, int32_t chunksize) { if (nitems == 0) { return 0; } int32_t typesize = schunk->typesize; if (nitems < 0 || chunksize <= 0 || typesize <= 0) { BLOSC_TRACE_ERROR("Invalid special fill parameters."); return BLOSC2_ERROR_INVALID_PARAM; } if ((schunk->nbytes > 0) || (schunk->cbytes > 0)) { BLOSC_TRACE_ERROR("Filling with special values only works on empty super-chunks"); return BLOSC2_ERROR_FRAME_SPECIAL; } // Compute the number of chunks and the length of the offsets chunk int32_t chunkitems = chunksize / typesize; if (chunkitems <= 0) { BLOSC_TRACE_ERROR("chunksize must be >= typesize for special fill."); return BLOSC2_ERROR_INVALID_PARAM; } int64_t nchunks = nitems / chunkitems; if (nchunks > INT_MAX) { BLOSC_TRACE_ERROR("nitems is too large. Try increasing the chunksize."); return BLOSC2_ERROR_SCHUNK_SPECIAL; } if (nitems > INT64_MAX / typesize) { BLOSC_TRACE_ERROR("nitems is too large for nbytes accounting."); return BLOSC2_ERROR_SCHUNK_SPECIAL; } int32_t leftover_items = (int32_t)(nitems % chunkitems); if (schunk->frame == NULL) { // Build the special chunks int32_t leftover_size = leftover_items * typesize; void* chunk = malloc(BLOSC_EXTENDED_HEADER_LENGTH); void* chunk2 = malloc(BLOSC_EXTENDED_HEADER_LENGTH); blosc2_cparams* cparams; blosc2_schunk_get_cparams(schunk, &cparams); int csize, csize2; switch (special_value) { case BLOSC2_SPECIAL_ZERO: csize = blosc2_chunk_zeros(*cparams, chunksize, chunk, BLOSC_EXTENDED_HEADER_LENGTH); csize2 = blosc2_chunk_zeros(*cparams, leftover_size, chunk2, BLOSC_EXTENDED_HEADER_LENGTH); break; case BLOSC2_SPECIAL_UNINIT: csize = blosc2_chunk_uninit(*cparams, chunksize, chunk, BLOSC_EXTENDED_HEADER_LENGTH); csize2 = blosc2_chunk_uninit(*cparams, leftover_size, chunk2, BLOSC_EXTENDED_HEADER_LENGTH); break; case BLOSC2_SPECIAL_NAN: csize = blosc2_chunk_nans(*cparams, chunksize, chunk, BLOSC_EXTENDED_HEADER_LENGTH); csize2 = blosc2_chunk_nans(*cparams, leftover_size, chunk2, BLOSC_EXTENDED_HEADER_LENGTH); break; default: BLOSC_TRACE_ERROR("Only zeros, NaNs or non-initialized values are supported."); return BLOSC2_ERROR_SCHUNK_SPECIAL; } free(cparams); if (csize < 0 || csize2 < 0) { BLOSC_TRACE_ERROR("Error creating special chunks."); return BLOSC2_ERROR_SCHUNK_SPECIAL; } for (int nchunk = 0; nchunk < nchunks; nchunk++) { int64_t nchunk_ = blosc2_schunk_append_chunk(schunk, chunk, true); if (nchunk_ != nchunk + 1) { BLOSC_TRACE_ERROR("Error appending special chunks."); return BLOSC2_ERROR_SCHUNK_SPECIAL; } } if (leftover_items) { int64_t nchunk_ = blosc2_schunk_append_chunk(schunk, chunk2, true); if (nchunk_ != nchunks + 1) { BLOSC_TRACE_ERROR("Error appending last special chunk."); return BLOSC2_ERROR_SCHUNK_SPECIAL; } } free(chunk); free(chunk2); } else { /* Fill an empty frame with special values (fast path). */ blosc2_frame_s *frame = (blosc2_frame_s *) schunk->frame; int64_t total_chunks = nchunks + (leftover_items ? 1 : 0); if (!blosc2_nchunks_to_offsets_nbytes(total_chunks, NULL)) { BLOSC_TRACE_ERROR("Too many chunks for frame offsets representation."); return BLOSC2_ERROR_FRAME_SPECIAL; } int64_t old_nchunks = schunk->nchunks; int64_t old_nbytes = schunk->nbytes; int32_t old_chunksize = schunk->chunksize; /* Update counters (necessary for the frame_fill_special() logic) */ if (leftover_items) { nchunks += 1; } schunk->chunksize = chunksize; schunk->nchunks = nchunks; schunk->nbytes = nitems * typesize; int64_t frame_len = frame_fill_special(frame, nitems, special_value, chunksize, schunk); if (frame_len < 0) { schunk->chunksize = old_chunksize; schunk->nchunks = old_nchunks; schunk->nbytes = old_nbytes; BLOSC_TRACE_ERROR("Error creating special frame."); return frame_len; } } return schunk->nchunks; } static int schunk_get_chunk_nbytes(blosc2_schunk *schunk, int64_t nchunk, int32_t *chunk_nbytes) { int rc; if (schunk->frame == NULL) { return blosc2_cbuffer_sizes(schunk->data[nchunk], chunk_nbytes, NULL, NULL); } bool needs_free; uint8_t *chunk; rc = frame_get_lazychunk((blosc2_frame_s *)schunk->frame, nchunk, &chunk, &needs_free); if (rc < 0) { return rc; } rc = blosc2_cbuffer_sizes(chunk, chunk_nbytes, NULL, NULL); if (needs_free) { free(chunk); } return rc; } static int schunk_get_chunk_flags2(blosc2_schunk *schunk, int64_t nchunk, uint8_t *chunk_flags2) { if (schunk->frame == NULL) { *chunk_flags2 = schunk->data[nchunk][BLOSC2_CHUNK_BLOSC2_FLAGS2]; return 0; } bool needs_free; uint8_t *chunk; int rc = frame_get_chunk((blosc2_frame_s *)schunk->frame, nchunk, &chunk, &needs_free); if (rc < 0) { return rc; } *chunk_flags2 = chunk[BLOSC2_CHUNK_BLOSC2_FLAGS2]; if (needs_free) { free(chunk); } return 0; } /* Append an existing chunk into a super-chunk. */ int64_t blosc2_schunk_append_chunk(blosc2_schunk *schunk, uint8_t *chunk, bool copy) { int32_t chunk_nbytes; int32_t chunk_cbytes; int64_t nchunks = schunk->nchunks; bool chunk_vlblocks = (chunk[BLOSC2_CHUNK_BLOSC2_FLAGS2] & BLOSC2_VL_BLOCKS) != 0; int rc = blosc2_cbuffer_sizes(chunk, &chunk_nbytes, &chunk_cbytes, NULL); if (rc < 0) { return rc; } if (nchunks > 0) { uint8_t first_flags2; rc = schunk_get_chunk_flags2(schunk, 0, &first_flags2); if (rc < 0) { return rc; } if (((first_flags2 & BLOSC2_VL_BLOCKS) != 0) != chunk_vlblocks) { BLOSC_TRACE_ERROR("schunks cannot mix regular chunks and VL-block chunks."); return BLOSC2_ERROR_CHUNK_APPEND; } } else { schunk->flags2 = chunk[BLOSC2_CHUNK_BLOSC2_FLAGS2]; } int32_t chunksize = schunk->chunksize; bool variable_chunksize = (chunksize == 0); if (chunksize == -1) { schunk->chunksize = chunk_nbytes; // The super-chunk is initialized now chunksize = schunk->chunksize; } if (!variable_chunksize && nchunks > 0) { int32_t last_nbytes; rc = schunk_get_chunk_nbytes(schunk, nchunks - 1, &last_nbytes); if (rc < 0) { return rc; } if (last_nbytes < chunksize || chunk_nbytes > chunksize) { variable_chunksize = true; schunk->chunksize = 0; } } if (!variable_chunksize && chunksize > 0 && chunk_nbytes > chunksize) { BLOSC_TRACE_ERROR("Appending chunks that have different lengths in the same schunk " "is not supported yet: %d > %d.", chunk_nbytes, chunksize); return BLOSC2_ERROR_CHUNK_APPEND; } /* Update counters */ schunk->current_nchunk = nchunks; schunk->nchunks = nchunks + 1; schunk->nbytes += chunk_nbytes; if (schunk->frame == NULL) { schunk->cbytes += chunk_cbytes; } else { // A frame int special_value = (chunk[BLOSC2_CHUNK_BLOSC2_FLAGS] >> 4) & BLOSC2_SPECIAL_MASK; switch (special_value) { case BLOSC2_SPECIAL_ZERO: case BLOSC2_SPECIAL_NAN: case BLOSC2_SPECIAL_UNINIT: schunk->cbytes += 0; break; default: schunk->cbytes += chunk_cbytes; } } if (copy) { // Make a copy of the chunk uint8_t *chunk_copy = malloc(chunk_cbytes); memcpy(chunk_copy, chunk, chunk_cbytes); chunk = chunk_copy; } // Update super-chunk or frame blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; if (frame == NULL) { if (!copy && (chunk_cbytes < chunk_nbytes)) { // We still want to do a shrink of the chunk chunk = realloc(chunk, chunk_cbytes); } /* Make space for appending the copy of the chunk and do it */ if ((nchunks + 1) * sizeof(void *) > schunk->data_len) { // Extend the data pointer by one memory page (4k) schunk->data_len += 4096; // must be a multiple of sizeof(void*) schunk->data = realloc(schunk->data, schunk->data_len); } schunk->data[nchunks] = chunk; } else { if (frame_append_chunk(frame, chunk, schunk) == NULL) { BLOSC_TRACE_ERROR("Problems appending a chunk."); return BLOSC2_ERROR_CHUNK_APPEND; } } return schunk->nchunks; } /* Insert an existing @p chunk in a specified position on a super-chunk */ int64_t blosc2_schunk_insert_chunk(blosc2_schunk *schunk, int64_t nchunk, uint8_t *chunk, bool copy) { int rc = validate_nchunk(schunk, nchunk, true, "blosc2_schunk_insert_chunk"); if (rc < 0) { return rc; } int32_t chunk_nbytes; int32_t chunk_cbytes; int64_t nchunks = schunk->nchunks; bool chunk_vlblocks = (chunk[BLOSC2_CHUNK_BLOSC2_FLAGS2] & BLOSC2_VL_BLOCKS) != 0; rc = blosc2_cbuffer_sizes(chunk, &chunk_nbytes, &chunk_cbytes, NULL); if (rc < 0) { return rc; } if (nchunks > 0) { uint8_t first_flags2; rc = schunk_get_chunk_flags2(schunk, 0, &first_flags2); if (rc < 0) { return rc; } if (((first_flags2 & BLOSC2_VL_BLOCKS) != 0) != chunk_vlblocks) { BLOSC_TRACE_ERROR("schunks cannot mix regular chunks and VL-block chunks."); return BLOSC2_ERROR_CHUNK_INSERT; } } else { schunk->flags2 = chunk[BLOSC2_CHUNK_BLOSC2_FLAGS2]; } int32_t chunksize = schunk->chunksize; bool variable_chunksize = (chunksize == 0); if (chunksize == -1) { schunk->chunksize = chunk_nbytes; // The super-chunk is initialized now chunksize = schunk->chunksize; } if (!variable_chunksize && nchunks > 0) { int32_t last_nbytes; rc = schunk_get_chunk_nbytes(schunk, nchunks - 1, &last_nbytes); if (rc < 0) { return rc; } if (last_nbytes < chunksize || chunk_nbytes > chunksize || (nchunk != nchunks && chunk_nbytes != chunksize)) { variable_chunksize = true; schunk->chunksize = 0; } } if (!variable_chunksize && chunksize > 0 && chunk_nbytes > chunksize) { BLOSC_TRACE_ERROR("Inserting chunks that have different lengths in the same schunk " "is not supported yet: %d > %d.", chunk_nbytes, chunksize); return BLOSC2_ERROR_CHUNK_INSERT; } /* Update counters */ schunk->current_nchunk = nchunk; schunk->nchunks = nchunks + 1; schunk->nbytes += chunk_nbytes; if (schunk->frame == NULL) { schunk->cbytes += chunk_cbytes; } else { // A frame int special_value = (chunk[BLOSC2_CHUNK_BLOSC2_FLAGS] >> 4) & BLOSC2_SPECIAL_MASK; switch (special_value) { case BLOSC2_SPECIAL_ZERO: case BLOSC2_SPECIAL_NAN: case BLOSC2_SPECIAL_UNINIT: schunk->cbytes += 0; break; default: schunk->cbytes += chunk_cbytes; } } if (copy) { // Make a copy of the chunk uint8_t *chunk_copy = malloc(chunk_cbytes); memcpy(chunk_copy, chunk, chunk_cbytes); chunk = chunk_copy; } // Update super-chunk or frame blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; if (frame == NULL) { if (!copy && (chunk_cbytes < chunk_nbytes)) { // We still want to do a shrink of the chunk chunk = realloc(chunk, chunk_cbytes); } // Make space for appending the copy of the chunk and do it if ((nchunks + 1) * sizeof(void *) > schunk->data_len) { // Extend the data pointer by one memory page (4k) schunk->data_len += 4096; // must be a multiple of sizeof(void*) schunk->data = realloc(schunk->data, schunk->data_len); } // Reorder the offsets and insert the new chunk for (int64_t i = nchunks; i > nchunk; --i) { schunk->data[i] = schunk->data[i-1]; } schunk->data[nchunk] = chunk; } else { if (frame_insert_chunk(frame, nchunk, chunk, schunk) == NULL) { BLOSC_TRACE_ERROR("Problems inserting a chunk in a frame."); return BLOSC2_ERROR_CHUNK_INSERT; } } return schunk->nchunks; } int64_t blosc2_schunk_update_chunk(blosc2_schunk *schunk, int64_t nchunk, uint8_t *chunk, bool copy) { int rc = validate_nchunk(schunk, nchunk, false, "blosc2_schunk_update_chunk"); if (rc < 0) { return rc; } int32_t chunk_nbytes; int32_t chunk_cbytes; bool chunk_vlblocks = (chunk[BLOSC2_CHUNK_BLOSC2_FLAGS2] & BLOSC2_VL_BLOCKS) != 0; rc = blosc2_cbuffer_sizes(chunk, &chunk_nbytes, &chunk_cbytes, NULL); if (rc < 0) { return rc; } if (schunk->nchunks > 1 || (schunk->nchunks == 1 && nchunk != 0)) { int64_t ref_nchunk = (nchunk == 0) ? 1 : 0; uint8_t ref_flags2; rc = schunk_get_chunk_flags2(schunk, ref_nchunk, &ref_flags2); if (rc < 0) { return rc; } if (((ref_flags2 & BLOSC2_VL_BLOCKS) != 0) != chunk_vlblocks) { BLOSC_TRACE_ERROR("schunks cannot mix regular chunks and VL-block chunks."); return BLOSC2_ERROR_CHUNK_UPDATE; } } else { schunk->flags2 = chunk[BLOSC2_CHUNK_BLOSC2_FLAGS2]; } int32_t chunksize = schunk->chunksize; bool variable_chunksize = (chunksize == 0); if (chunksize == -1) { schunk->chunksize = chunk_nbytes; // The super-chunk is initialized now chunksize = schunk->chunksize; } if (!variable_chunksize && (chunk_nbytes > chunksize || (nchunk != schunk->nchunks - 1 && chunk_nbytes != chunksize))) { variable_chunksize = true; schunk->chunksize = 0; } if (!variable_chunksize && chunksize > 0 && chunk_nbytes > chunksize) { BLOSC_TRACE_ERROR("Updating chunks having different lengths in the same schunk " "is not supported yet: %d > %d.", chunk_nbytes, chunksize); return BLOSC2_ERROR_CHUNK_UPDATE; } bool needs_free; uint8_t *chunk_old; int err = blosc2_schunk_get_chunk(schunk, nchunk, &chunk_old, &needs_free); if (err < 0) { BLOSC_TRACE_ERROR("%" PRId64 " chunk can not be obtained from schunk.", nchunk); return -1; } int32_t chunk_nbytes_old = 0; int32_t chunk_cbytes_old = 0; schunk->current_nchunk = nchunk; if (chunk_old != 0) { rc = blosc2_cbuffer_sizes(chunk_old, &chunk_nbytes_old, &chunk_cbytes_old, NULL); if (rc < 0) { return rc; } if (chunk_cbytes_old == BLOSC2_MAX_OVERHEAD) { chunk_cbytes_old = 0; } } if (needs_free) { free(chunk_old); } if (copy) { // Make a copy of the chunk uint8_t *chunk_copy = malloc(chunk_cbytes); memcpy(chunk_copy, chunk, chunk_cbytes); chunk = chunk_copy; } blosc2_frame_s* frame = (blosc2_frame_s*)(schunk->frame); if (schunk->frame == NULL) { /* Update counters */ schunk->nbytes += chunk_nbytes; schunk->nbytes -= chunk_nbytes_old; schunk->cbytes += chunk_cbytes; schunk->cbytes -= chunk_cbytes_old; } else { // A frame int special_value = (chunk[BLOSC2_CHUNK_BLOSC2_FLAGS] >> 4) & BLOSC2_SPECIAL_MASK; schunk->nbytes += chunk_nbytes; schunk->nbytes -= chunk_nbytes_old; switch (special_value) { case BLOSC2_SPECIAL_ZERO: case BLOSC2_SPECIAL_NAN: case BLOSC2_SPECIAL_UNINIT: schunk->cbytes -= chunk_cbytes_old; break; default: schunk->cbytes += chunk_cbytes; schunk->cbytes -= chunk_cbytes_old; } } // Update super-chunk or frame if (schunk->frame == NULL) { if (!copy && (chunk_cbytes < chunk_nbytes)) { // We still want to do a shrink of the chunk chunk = realloc(chunk, chunk_cbytes); } // Free old chunk and add reference to new chunk if (schunk->data[nchunk] != 0) { free(schunk->data[nchunk]); } schunk->data[nchunk] = chunk; } else { if (frame_update_chunk(frame, nchunk, chunk, schunk) == NULL) { BLOSC_TRACE_ERROR("Problems updating a chunk in a frame."); return BLOSC2_ERROR_CHUNK_UPDATE; } } return schunk->nchunks; } int64_t blosc2_schunk_delete_chunk(blosc2_schunk *schunk, int64_t nchunk) { int rc = validate_nchunk(schunk, nchunk, false, "blosc2_schunk_delete_chunk"); if (rc < 0) { return rc; } bool needs_free; uint8_t *chunk_old; int err = blosc2_schunk_get_chunk(schunk, nchunk, &chunk_old, &needs_free); if (err < 0) { BLOSC_TRACE_ERROR("%" PRId64 "chunk can not be obtained from schunk.", nchunk); return -1; } int32_t chunk_nbytes_old = 0; int32_t chunk_cbytes_old = 0; schunk->current_nchunk = nchunk; if (chunk_old != 0) { rc = blosc2_cbuffer_sizes(chunk_old, &chunk_nbytes_old, &chunk_cbytes_old, NULL); if (rc < 0) { return rc; } if (chunk_cbytes_old == BLOSC2_MAX_OVERHEAD) { chunk_cbytes_old = 0; } } if (needs_free) { free(chunk_old); } blosc2_frame_s* frame = (blosc2_frame_s*)(schunk->frame); schunk->nchunks -= 1; if (schunk->nchunks == 0) { schunk->flags2 = 0; } if (schunk->frame == NULL) { /* Update counters */ schunk->nbytes -= chunk_nbytes_old; schunk->cbytes -= chunk_cbytes_old; } else { // A frame schunk->nbytes -= chunk_nbytes_old; if (frame->sframe) { schunk->cbytes -= chunk_cbytes_old; } } // Update super-chunk or frame if (schunk->frame == NULL) { // Free old chunk if (schunk->data[nchunk] != 0) { free(schunk->data[nchunk]); } // Reorder the offsets and insert the new chunk for (int64_t i = nchunk; i < schunk->nchunks; i++) { schunk->data[i] = schunk->data[i + 1]; } schunk->data[schunk->nchunks] = NULL; } else { if (frame_delete_chunk(frame, nchunk, schunk) == NULL) { BLOSC_TRACE_ERROR("Problems deleting a chunk in a frame."); return BLOSC2_ERROR_CHUNK_UPDATE; } } return schunk->nchunks; } /* Append a data buffer to a super-chunk. */ int64_t blosc2_schunk_append_buffer(blosc2_schunk *schunk, const void *src, int32_t nbytes) { uint8_t* chunk = malloc(nbytes + BLOSC2_MAX_OVERHEAD); schunk->current_nchunk = schunk->nchunks; /* Compress the src buffer using super-chunk context */ int cbytes = blosc2_compress_ctx(schunk->cctx, src, nbytes, chunk, nbytes + BLOSC2_MAX_OVERHEAD); if (cbytes < 0) { free(chunk); return cbytes; } // We don't need a copy of the chunk, as it will be shrunk if necessary int64_t nchunks = blosc2_schunk_append_chunk(schunk, chunk, false); if (nchunks < 0) { BLOSC_TRACE_ERROR("Error appending a buffer in super-chunk"); return nchunks; } return nchunks; } /* Decompress and return a chunk that is part of a super-chunk. */ int blosc2_schunk_decompress_chunk(blosc2_schunk *schunk, int64_t nchunk, void *dest, int32_t nbytes) { int rc = validate_nchunk(schunk, nchunk, false, "blosc2_schunk_decompress_chunk"); if (rc < 0) { return rc; } int32_t chunk_nbytes; int32_t chunk_cbytes; int chunksize; blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; schunk->current_nchunk = nchunk; if (frame == NULL) { uint8_t* src = schunk->data[nchunk]; if (src == 0) { return 0; } rc = blosc2_cbuffer_sizes(src, &chunk_nbytes, &chunk_cbytes, NULL); if (rc < 0) { return rc; } if (nbytes < chunk_nbytes) { BLOSC_TRACE_ERROR("Buffer size is too small for the decompressed buffer " "('%d' bytes, but '%d' are needed).", nbytes, chunk_nbytes); return BLOSC2_ERROR_INVALID_PARAM; } chunksize = blosc2_decompress_ctx(schunk->dctx, src, chunk_cbytes, dest, nbytes); if (chunksize < 0 || chunksize != chunk_nbytes) { BLOSC_TRACE_ERROR("Error in decompressing chunk."); if (chunksize < 0) return chunksize; return BLOSC2_ERROR_FAILURE; } } else { chunksize = frame_decompress_chunk(schunk->dctx, frame, nchunk, dest, nbytes); if (chunksize < 0) { return chunksize; } } return chunksize; } /* Return a compressed chunk that is part of a super-chunk in the `chunk` parameter. * If the super-chunk is backed by a frame that is disk-based, a buffer is allocated for the * (compressed) chunk, and hence a free is needed. You can check if the chunk requires a free * with the `needs_free` parameter. * If the chunk does not need a free, it means that a pointer to the location in the super-chunk * (or the backing in-memory frame) is returned in the `chunk` parameter. * * The size of the (compressed) chunk is returned. If some problem is detected, a negative code * is returned instead. */ int blosc2_schunk_get_chunk(blosc2_schunk *schunk, int64_t nchunk, uint8_t **chunk, bool *needs_free) { int rc = validate_nchunk(schunk, nchunk, false, "blosc2_schunk_get_chunk"); if (rc < 0) { return rc; } if (ctx_uses_parallel_backend(schunk->dctx)) { blosc2_pthread_mutex_lock(&schunk->dctx->nchunk_mutex); schunk->current_nchunk = nchunk; blosc2_pthread_mutex_unlock(&schunk->dctx->nchunk_mutex); } else { schunk->current_nchunk = nchunk; } blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; if (frame != NULL) { return frame_get_chunk(frame, nchunk, chunk, needs_free); } *chunk = schunk->data[nchunk]; if (*chunk == 0) { *needs_free = 0; return 0; } *needs_free = false; int32_t chunk_cbytes; rc = blosc2_cbuffer_sizes(*chunk, NULL, &chunk_cbytes, NULL); if (rc < 0) { return rc; } return (int)chunk_cbytes; } /* Return a compressed chunk that is part of a super-chunk in the `chunk` parameter. * If the super-chunk is backed by a frame that is disk-based, a buffer is allocated for the * (compressed) chunk, and hence a free is needed. You can check if the chunk requires a free * with the `needs_free` parameter. * If the chunk does not need a free, it means that a pointer to the location in the super-chunk * (or the backing in-memory frame) is returned in the `chunk` parameter. * * The size of the (compressed) chunk is returned. If some problem is detected, a negative code * is returned instead. */ int blosc2_schunk_get_lazychunk(blosc2_schunk *schunk, int64_t nchunk, uint8_t **chunk, bool *needs_free) { int rc = validate_nchunk(schunk, nchunk, false, "blosc2_schunk_get_lazychunk"); if (rc < 0) { return rc; } if (ctx_uses_parallel_backend(schunk->dctx)) { blosc2_pthread_mutex_lock(&schunk->dctx->nchunk_mutex); schunk->current_nchunk = nchunk; blosc2_pthread_mutex_unlock(&schunk->dctx->nchunk_mutex); } else { schunk->current_nchunk = nchunk; } blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; if (schunk->frame != NULL) { return frame_get_lazychunk(frame, nchunk, chunk, needs_free); } *chunk = schunk->data[nchunk]; if (*chunk == 0) { *needs_free = 0; return 0; } *needs_free = false; int32_t chunk_cbytes; rc = blosc2_cbuffer_sizes(*chunk, NULL, &chunk_cbytes, NULL); if (rc < 0) { return rc; } return (int)chunk_cbytes; } int blosc2_schunk_get_vlblock(blosc2_schunk *schunk, int64_t nchunk, int32_t nblock, uint8_t **dest, int32_t *destsize) { if (schunk == NULL || dest == NULL || destsize == NULL) { BLOSC_TRACE_ERROR("schunk, dest, and destsize must not be NULL."); return BLOSC2_ERROR_INVALID_PARAM; } uint8_t *chunk = NULL; bool needs_free = false; int cbytes = blosc2_schunk_get_chunk(schunk, nchunk, &chunk, &needs_free); if (cbytes < 0) { return cbytes; } if (chunk == NULL || cbytes == 0) { BLOSC_TRACE_ERROR("Chunk %" PRId64 " is not initialized.", nchunk); return BLOSC2_ERROR_INVALID_PARAM; } int result = blosc2_vldecompress_block_ctx(schunk->dctx, chunk, cbytes, nblock, dest, destsize); if (needs_free) { free(chunk); } return result; } int blosc2_schunk_get_slice_buffer(blosc2_schunk *schunk, int64_t start, int64_t stop, void *buffer) { int64_t byte_start = start * schunk->typesize; int64_t byte_stop = stop * schunk->typesize; int64_t nchunk_start = byte_start / schunk->chunksize; int32_t chunk_start = (int32_t) (byte_start % schunk->chunksize); // 0 indexed int32_t chunk_stop; // 0 indexed if (byte_stop >= (nchunk_start + 1) * schunk->chunksize) { chunk_stop = schunk->chunksize; } else { chunk_stop = (int32_t) (byte_stop % schunk->chunksize); } uint8_t *dst_ptr = (uint8_t *) buffer; bool needs_free; uint8_t *chunk; int32_t cbytes; int64_t nchunk = nchunk_start; int64_t nbytes_read = 0; int32_t nbytes; int32_t chunksize = schunk->chunksize; while (nbytes_read < ((stop - start) * schunk->typesize)) { cbytes = blosc2_schunk_get_lazychunk(schunk, nchunk, &chunk, &needs_free); if (cbytes < 0) { BLOSC_TRACE_ERROR("Cannot get lazychunk ('%" PRId64 "').", nchunk); return BLOSC2_ERROR_FAILURE; } int32_t blocksize = sw32_(chunk + BLOSC2_CHUNK_BLOCKSIZE); int32_t nblock_start = (int32_t) (chunk_start / blocksize); int32_t nblock_stop = (int32_t) ((chunk_stop - 1) / blocksize); if (nchunk == (schunk->nchunks - 1) && schunk->nbytes % schunk->chunksize != 0) { chunksize = schunk->nbytes % schunk->chunksize; } int32_t nblocks = chunksize / blocksize; if (chunksize % blocksize != 0) { nblocks++; } if (chunk_start == 0 && chunk_stop == chunksize) { // Avoid memcpy nbytes = blosc2_decompress_ctx(schunk->dctx, chunk, cbytes, dst_ptr, chunksize); if (nbytes < 0) { BLOSC_TRACE_ERROR("Cannot decompress chunk ('%" PRId64 "').", nchunk); return BLOSC2_ERROR_FAILURE; } } else { // After extensive timing I have not been able to see lots of situations where // a maskout read is better than a getitem one. Disabling for now. // if (nblock_start != nblock_stop) { if (false) { uint8_t *data = malloc(chunksize); /* We have more than 1 block to read, so use a masked read */ bool *block_maskout = calloc(nblocks, 1); for (int32_t nblock = 0; nblock < nblocks; nblock++) { if ((nblock < nblock_start) || (nblock > nblock_stop)) { block_maskout[nblock] = true; } } if (blosc2_set_maskout(schunk->dctx, block_maskout, nblocks) < 0) { BLOSC_TRACE_ERROR("Cannot set maskout"); return BLOSC2_ERROR_FAILURE; } nbytes = blosc2_decompress_ctx(schunk->dctx, chunk, cbytes, data, chunksize); if (nbytes < 0) { BLOSC_TRACE_ERROR("Cannot decompress chunk ('%" PRId64 "').", nchunk); return BLOSC2_ERROR_FAILURE; } nbytes = chunk_stop - chunk_start; memcpy(dst_ptr, &data[chunk_start], nbytes); free(block_maskout); free(data); } else { /* Less than 1 block to read; use a getitem call */ nbytes = blosc2_getitem_ctx(schunk->dctx, chunk, cbytes, (int32_t) (chunk_start / schunk->typesize), (chunk_stop - chunk_start) / schunk->typesize, dst_ptr, chunksize); if (nbytes < 0) { BLOSC_TRACE_ERROR("Cannot get item from ('%" PRId64 "') chunk.", nchunk); return BLOSC2_ERROR_FAILURE; } } } dst_ptr += nbytes; nbytes_read += nbytes; nchunk++; if (needs_free) { free(chunk); } chunk_start = 0; if (byte_stop >= (nchunk + 1) * chunksize) { chunk_stop = chunksize; } else { chunk_stop = (int32_t)(byte_stop % chunksize); } } return BLOSC2_ERROR_SUCCESS; } typedef struct { int64_t coord; int64_t out_index; } sparse_coord_entry; typedef struct { int64_t nchunk; uint8_t *chunk; int cbytes; bool needs_free; } sparse_chunk_entry; typedef struct { int64_t first; int64_t count; int64_t chunk_index; int32_t nblock; } sparse_block_task; typedef struct { const sparse_coord_entry *entries; const sparse_block_task *tasks; const sparse_chunk_entry *chunks; int64_t ntasks; uint8_t *buffer; int32_t typesize; int32_t blocksize; int64_t chunk_nitems; int64_t next_task; int error; blosc2_pthread_mutex_t mutex; } sparse_work; typedef struct { sparse_work *work; blosc2_context *dctx; uint8_t *block; } sparse_worker; static int sparse_coord_entry_cmp(const void *a, const void *b) { const sparse_coord_entry *ea = (const sparse_coord_entry *)a; const sparse_coord_entry *eb = (const sparse_coord_entry *)b; if (ea->coord < eb->coord) return -1; if (ea->coord > eb->coord) return 1; if (ea->out_index < eb->out_index) return -1; if (ea->out_index > eb->out_index) return 1; return 0; } static void sparse_work_set_error(sparse_work *work, int error) { blosc2_pthread_mutex_lock(&work->mutex); if (work->error == 0) { work->error = error; } blosc2_pthread_mutex_unlock(&work->mutex); } static void sparse_worker_func(void *arg) { sparse_worker *worker = (sparse_worker *)arg; sparse_work *work = worker->work; while (true) { blosc2_pthread_mutex_lock(&work->mutex); if (work->error < 0 || work->next_task >= work->ntasks) { blosc2_pthread_mutex_unlock(&work->mutex); break; } int64_t task_index = work->next_task++; blosc2_pthread_mutex_unlock(&work->mutex); const sparse_block_task *task = &work->tasks[task_index]; const sparse_chunk_entry *chunk_entry = &work->chunks[task->chunk_index]; int nbytes = blosc2_decompress_block_ctx(worker->dctx, chunk_entry->chunk, chunk_entry->cbytes, task->nblock, worker->block, work->blocksize); if (nbytes < 0) { sparse_work_set_error(work, nbytes); break; } for (int64_t i = task->first; i < task->first + task->count; ++i) { const sparse_coord_entry *entry = &work->entries[i]; int64_t item_in_chunk = entry->coord % work->chunk_nitems; int64_t byte_in_chunk = item_in_chunk * work->typesize; int32_t block_offset = (int32_t)(byte_in_chunk % work->blocksize); if (block_offset < 0 || block_offset > nbytes - work->typesize) { sparse_work_set_error(work, BLOSC2_ERROR_DATA); return; } memcpy(work->buffer + entry->out_index * work->typesize, worker->block + block_offset, (size_t)work->typesize); } } return; } static int schunk_get_sparse_getitem(blosc2_schunk *schunk, int64_t ncoords, const int64_t* coords, void *buffer) { int64_t nitems = schunk->nbytes / schunk->typesize; int64_t chunk_nitems = schunk->chunksize / schunk->typesize; uint8_t *dst_ptr = (uint8_t *)buffer; for (int64_t i = 0; i < ncoords; ++i) { int64_t coord = coords[i]; if (coord < 0 || coord >= nitems) { BLOSC_TRACE_ERROR("Coordinate out of bounds."); return BLOSC2_ERROR_INVALID_PARAM; } int64_t nchunk = coord / chunk_nitems; int start = (int)(coord % chunk_nitems); uint8_t *chunk = NULL; bool needs_free = false; int cbytes = blosc2_schunk_get_lazychunk(schunk, nchunk, &chunk, &needs_free); if (cbytes <= 0) { BLOSC_TRACE_ERROR("Cannot get lazychunk ('%" PRId64 "').", nchunk); return BLOSC2_ERROR_FAILURE; } int nbytes = blosc2_getitem_ctx(schunk->dctx, chunk, cbytes, start, 1, dst_ptr, schunk->typesize); if (needs_free) { free(chunk); } if (nbytes != schunk->typesize) { BLOSC_TRACE_ERROR("Cannot get item from ('%" PRId64 "') chunk.", nchunk); return BLOSC2_ERROR_FAILURE; } dst_ptr += schunk->typesize; } return BLOSC2_ERROR_SUCCESS; } int blosc2_schunk_get_sparse_buffer(blosc2_schunk *schunk, int64_t ncoords, const int64_t* coords, void *buffer) { if (schunk == NULL) { BLOSC_TRACE_ERROR("schunk must not be NULL."); return BLOSC2_ERROR_INVALID_PARAM; } if (ncoords < 0) { BLOSC_TRACE_ERROR("ncoords must be non-negative."); return BLOSC2_ERROR_INVALID_PARAM; } if (ncoords == 0) { return BLOSC2_ERROR_SUCCESS; } if (coords == NULL || buffer == NULL) { BLOSC_TRACE_ERROR("coords and buffer must not be NULL when ncoords > 0."); return BLOSC2_ERROR_INVALID_PARAM; } if (schunk->typesize <= 0) { BLOSC_TRACE_ERROR("schunk must have a positive typesize."); return BLOSC2_ERROR_INVALID_PARAM; } if (schunk->chunksize <= 0) { BLOSC_TRACE_ERROR("blosc2_schunk_get_sparse_buffer does not support variable-length chunks yet."); return BLOSC2_ERROR_INVALID_PARAM; } if (schunk->blocksize <= 0 || (schunk->flags2 & BLOSC2_VL_BLOCKS)) { BLOSC_TRACE_ERROR("blosc2_schunk_get_sparse_buffer does not support variable-length blocks yet."); return BLOSC2_ERROR_INVALID_PARAM; } if ((schunk->chunksize % schunk->typesize) != 0 || (schunk->blocksize % schunk->typesize) != 0) { BLOSC_TRACE_ERROR("chunksize and blocksize must be multiples of typesize."); return BLOSC2_ERROR_INVALID_PARAM; } for (int i = 0; i < BLOSC2_MAX_FILTERS; ++i) { if (schunk->filters[i] == BLOSC_DELTA) { return schunk_get_sparse_getitem(schunk, ncoords, coords, buffer); } } if (schunk->dctx != NULL && schunk->dctx->postfilter != NULL) { return schunk_get_sparse_getitem(schunk, ncoords, coords, buffer); } if (ncoords <= 1) { return schunk_get_sparse_getitem(schunk, ncoords, coords, buffer); } int rc = BLOSC2_ERROR_SUCCESS; int64_t nitems = schunk->nbytes / schunk->typesize; int64_t chunk_nitems = schunk->chunksize / schunk->typesize; sparse_coord_entry *entries = NULL; sparse_block_task *tasks = NULL; sparse_chunk_entry *chunks = NULL; sparse_worker *workers = NULL; int16_t nthreads = 1; int64_t ntasks = 0; int64_t nchunks = 0; sparse_work work; memset(&work, 0, sizeof(work)); if (chunk_nitems <= 0 || chunk_nitems > INT32_MAX) { BLOSC_TRACE_ERROR("Invalid chunk item count."); return BLOSC2_ERROR_INVALID_PARAM; } entries = calloc((size_t)ncoords, sizeof(sparse_coord_entry)); if (entries == NULL) { rc = BLOSC2_ERROR_MEMORY_ALLOC; goto cleanup; } for (int64_t i = 0; i < ncoords; ++i) { int64_t coord = coords[i]; if (coord < 0 || coord >= nitems) { BLOSC_TRACE_ERROR("Coordinate out of bounds."); rc = BLOSC2_ERROR_INVALID_PARAM; goto cleanup; } entries[i].coord = coord; entries[i].out_index = i; } qsort(entries, (size_t)ncoords, sizeof(sparse_coord_entry), sparse_coord_entry_cmp); /* First pass over sorted entries: count touched chunks and selected block tasks. */ for (int64_t i = 0; i < ncoords;) { int64_t nchunk = entries[i].coord / chunk_nitems; ++nchunks; while (i < ncoords && entries[i].coord / chunk_nitems == nchunk) { int64_t item_in_chunk = entries[i].coord % chunk_nitems; int64_t byte_in_chunk = item_in_chunk * schunk->typesize; int32_t nblock = (int32_t)(byte_in_chunk / schunk->blocksize); while (i < ncoords && entries[i].coord / chunk_nitems == nchunk && (int32_t)(((entries[i].coord % chunk_nitems) * schunk->typesize) / schunk->blocksize) == nblock) { ++i; } ++ntasks; } } tasks = calloc((size_t)ntasks, sizeof(sparse_block_task)); chunks = calloc((size_t)nchunks, sizeof(sparse_chunk_entry)); if (tasks == NULL || chunks == NULL) { rc = BLOSC2_ERROR_MEMORY_ALLOC; goto cleanup; } /* Second pass: fetch touched chunks and build one task per selected block. */ int64_t chunk_index = 0; int64_t task_index = 0; for (int64_t i = 0; i < ncoords;) { int64_t nchunk = entries[i].coord / chunk_nitems; uint8_t *chunk = NULL; bool needs_free = false; int cbytes = blosc2_schunk_get_lazychunk(schunk, nchunk, &chunk, &needs_free); if (cbytes <= 0) { BLOSC_TRACE_ERROR("Cannot get lazychunk ('%" PRId64 "').", nchunk); rc = BLOSC2_ERROR_FAILURE; goto cleanup; } chunks[chunk_index].nchunk = nchunk; chunks[chunk_index].chunk = chunk; chunks[chunk_index].cbytes = cbytes; chunks[chunk_index].needs_free = needs_free; while (i < ncoords && entries[i].coord / chunk_nitems == nchunk) { int64_t item_in_chunk = entries[i].coord % chunk_nitems; int64_t byte_in_chunk = item_in_chunk * schunk->typesize; int32_t nblock = (int32_t)(byte_in_chunk / schunk->blocksize); int64_t first = i; while (i < ncoords && entries[i].coord / chunk_nitems == nchunk && (int32_t)(((entries[i].coord % chunk_nitems) * schunk->typesize) / schunk->blocksize) == nblock) { ++i; } tasks[task_index].first = first; tasks[task_index].count = i - first; tasks[task_index].chunk_index = chunk_index; tasks[task_index].nblock = nblock; ++task_index; } ++chunk_index; } nthreads = schunk->dctx != NULL ? schunk->dctx->nthreads : blosc2_get_nthreads(); if (nthreads < 1) { nthreads = 1; } if ((int64_t)nthreads > ntasks) { nthreads = (int16_t)ntasks; } workers = calloc((size_t)nthreads, sizeof(sparse_worker)); if (workers == NULL) { rc = BLOSC2_ERROR_MEMORY_ALLOC; goto cleanup; } work.entries = entries; work.tasks = tasks; work.chunks = chunks; work.ntasks = ntasks; work.buffer = (uint8_t *)buffer; work.typesize = schunk->typesize; work.blocksize = schunk->blocksize; work.chunk_nitems = chunk_nitems; work.next_task = 0; work.error = 0; blosc2_pthread_mutex_init(&work.mutex, NULL); for (int16_t i = 0; i < nthreads; ++i) { blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; dparams.nthreads = 1; dparams.schunk = schunk; workers[i].work = &work; workers[i].dctx = blosc2_create_dctx(dparams); workers[i].block = malloc((size_t)schunk->blocksize); if (workers[i].dctx == NULL || workers[i].block == NULL) { rc = BLOSC2_ERROR_MEMORY_ALLOC; goto cleanup_work_mutex; } } if (nthreads == 1) { sparse_worker_func(&workers[0]); } else { int err = blosc2_run_parallel(nthreads, sparse_worker_func, sizeof(sparse_worker), workers); if (err < 0) { sparse_work_set_error(&work, err); } } if (work.error < 0) { rc = work.error; } cleanup_work_mutex: blosc2_pthread_mutex_destroy(&work.mutex); cleanup: if (workers != NULL) { int16_t nworkers = nthreads > 0 ? nthreads : 1; for (int16_t i = 0; i < nworkers; ++i) { if (workers[i].dctx != NULL) { blosc2_free_ctx(workers[i].dctx); } free(workers[i].block); } } if (chunks != NULL) { for (int64_t i = 0; i < nchunks; ++i) { if (chunks[i].needs_free) { free(chunks[i].chunk); } } } free(workers); free(chunks); free(tasks); free(entries); return rc; } int blosc2_schunk_set_slice_buffer(blosc2_schunk *schunk, int64_t start, int64_t stop, void *buffer) { int64_t byte_start = start * schunk->typesize; int64_t byte_stop = stop * schunk->typesize; int64_t nchunk_start = byte_start / schunk->chunksize; int32_t chunk_start = (int32_t) (byte_start % schunk->chunksize); int32_t chunk_stop; if (byte_stop >= (nchunk_start + 1) * schunk->chunksize) { chunk_stop = schunk->chunksize; } else { chunk_stop = (int32_t) (byte_stop % schunk->chunksize); } uint8_t *src_ptr = (uint8_t *) buffer; int64_t nchunk = nchunk_start; int64_t nbytes_written = 0; int32_t nbytes; uint8_t *data = malloc(schunk->chunksize); int64_t nchunks; int32_t chunksize = schunk->chunksize; while (nbytes_written < ((stop - start) * schunk->typesize)) { if (chunk_start == 0 && (chunk_stop == schunk->chunksize || chunk_stop == schunk->nbytes % schunk->chunksize)) { if (chunk_stop == schunk->nbytes % schunk->chunksize) { chunksize = chunk_stop; } uint8_t *chunk = malloc(chunksize + BLOSC2_MAX_OVERHEAD); if (blosc2_compress_ctx(schunk->cctx, src_ptr, chunksize, chunk, chunksize + BLOSC2_MAX_OVERHEAD) < 0) { BLOSC_TRACE_ERROR("Cannot compress data of chunk ('%" PRId64 "').", nchunk); return BLOSC2_ERROR_FAILURE; } nchunks = blosc2_schunk_update_chunk(schunk, nchunk, chunk, false); if (nchunks != schunk->nchunks) { BLOSC_TRACE_ERROR("Cannot update chunk ('%" PRId64 "').", nchunk); return BLOSC2_ERROR_CHUNK_UPDATE; } } else { nbytes = blosc2_schunk_decompress_chunk(schunk, nchunk, data, schunk->chunksize); if (nbytes < 0) { BLOSC_TRACE_ERROR("Cannot decompress chunk ('%" PRId64 "').", nchunk); return BLOSC2_ERROR_FAILURE; } memcpy(&data[chunk_start], src_ptr, chunk_stop - chunk_start); uint8_t *chunk = malloc(nbytes + BLOSC2_MAX_OVERHEAD); if (blosc2_compress_ctx(schunk->cctx, data, nbytes, chunk, nbytes + BLOSC2_MAX_OVERHEAD) < 0) { BLOSC_TRACE_ERROR("Cannot compress data of chunk ('%" PRId64 "').", nchunk); return BLOSC2_ERROR_FAILURE; } nchunks = blosc2_schunk_update_chunk(schunk, nchunk, chunk, false); if (nchunks != schunk->nchunks) { BLOSC_TRACE_ERROR("Cannot update chunk ('%" PRId64 "').", nchunk); return BLOSC2_ERROR_CHUNK_UPDATE; } } nchunk++; nbytes_written += chunk_stop - chunk_start; src_ptr += chunk_stop - chunk_start; chunk_start = 0; if (byte_stop >= (nchunk + 1) * schunk->chunksize) { chunk_stop = schunk->chunksize; } else { chunk_stop = (int32_t) (byte_stop % schunk->chunksize); } } free(data); return BLOSC2_ERROR_SUCCESS; } int64_t schunk_get_slice_nchunks(blosc2_schunk *schunk, int64_t start, int64_t stop, int64_t **chunks_idx) { BLOSC_ERROR_NULL(schunk, BLOSC2_ERROR_NULL_POINTER); if (schunk->nchunks == 0){ *chunks_idx = NULL; return 0; } int64_t byte_start = start * schunk->typesize; int64_t byte_stop = stop * schunk->typesize; int64_t nchunk_start = byte_start / schunk->chunksize; int64_t nchunk_stop = byte_stop / schunk->chunksize; if (byte_stop % schunk->chunksize != 0) { nchunk_stop++; } int64_t nchunk = nchunk_start; int64_t nchunks = nchunk_stop - nchunk_start; *chunks_idx = malloc(nchunks * sizeof(int64_t)); int64_t *ptr = *chunks_idx; for (int64_t i = 0; i < nchunks; ++i) { ptr[i] = nchunk; nchunk++; } return nchunks; } /* Reorder the chunk offsets of an existing super-chunk. */ int blosc2_schunk_reorder_offsets(blosc2_schunk *schunk, int64_t *offsets_order) { // Check that the offsets order are correct bool *index_check = (bool *) calloc(schunk->nchunks, sizeof(bool)); for (int i = 0; i < schunk->nchunks; ++i) { int64_t index = offsets_order[i]; if (index < 0 || index >= schunk->nchunks) { BLOSC_TRACE_ERROR("Index is out of range (negative or >= number of chunks)."); free(index_check); return BLOSC2_ERROR_DATA; } if (index_check[index] == false) { index_check[index] = true; } else { BLOSC_TRACE_ERROR("Index is yet used."); free(index_check); return BLOSC2_ERROR_DATA; } } free(index_check); blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; if (frame != NULL) { return frame_reorder_offsets(frame, offsets_order, schunk); } uint8_t **offsets = schunk->data; // Make a copy of the chunk offsets and reorder it uint8_t **offsets_copy = malloc(schunk->data_len); memcpy(offsets_copy, offsets, schunk->data_len); for (int i = 0; i < schunk->nchunks; ++i) { offsets[i] = offsets_copy[offsets_order[i]]; } free(offsets_copy); return 0; } // Get the length (in bytes) of the internal frame of the super-chunk int64_t blosc2_schunk_frame_len(blosc2_schunk* schunk) { int64_t len; blosc2_frame_s* frame_s = (blosc2_frame_s*)(schunk->frame); if (frame_s != NULL) { len = frame_s->len; } else { // No attached frame, but we can still come with an estimate len = (int64_t) (schunk->cbytes + schunk->nchunks * sizeof(int64_t)); } return len; } /** * @brief Flush metalayers content into a possible attached frame. * * @param schunk The super-chunk to which the flush should be applied. * * @return If successful, a 0 is returned. Else, return a negative value. */ // Initially, this was a public function, but as it is really meant to be used only // in the schunk_add_metalayer(), I decided to convert it into private and call it // implicitly instead of requiring the user to do so. The only drawback is that // each add operation requires a complete frame re-build, but as users should need // very few metalayers, this overhead should be negligible in practice. int metalayer_flush(blosc2_schunk* schunk) { int rc = BLOSC2_ERROR_SUCCESS; blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; if (frame == NULL) { return rc; } rc = frame_update_header(frame, schunk, true); if (rc < 0) { BLOSC_TRACE_ERROR("Unable to update metalayers into frame."); return rc; } rc = frame_update_trailer(frame, schunk); if (rc < 0) { BLOSC_TRACE_ERROR("Unable to update trailer into frame."); return rc; } return rc; } /* Add content into a new metalayer. * * If successful, return the index of the new metalayer. Else, return a negative value. */ int blosc2_meta_add(blosc2_schunk *schunk, const char *name, uint8_t *content, int32_t content_len) { if (schunk == NULL || name == NULL) { BLOSC_TRACE_ERROR("Invalid parameters."); return BLOSC2_ERROR_INVALID_PARAM; } int nmetalayer = blosc2_meta_exists(schunk, name); if (nmetalayer >= 0) { BLOSC_TRACE_ERROR("Metalayer \"%s\" already exists.", name); return BLOSC2_ERROR_INVALID_PARAM; } if (content_len < 0) { BLOSC_TRACE_ERROR("Metalayer content length must not be negative."); return BLOSC2_ERROR_INVALID_PARAM; } if (content_len > 0 && content == NULL) { BLOSC_TRACE_ERROR("Metalayer content pointer must not be NULL when content length is positive."); return BLOSC2_ERROR_INVALID_PARAM; } // Add the metalayer blosc2_metalayer *metalayer = malloc(sizeof(blosc2_metalayer)); BLOSC_ERROR_NULL(metalayer, BLOSC2_ERROR_MEMORY_ALLOC); char* name_ = malloc(strlen(name) + 1); if (name_ == NULL) { free(metalayer); BLOSC_TRACE_ERROR("Unable to allocate metalayer name buffer."); return BLOSC2_ERROR_MEMORY_ALLOC; } strcpy(name_, name); metalayer->name = name_; uint8_t* content_buf = malloc((size_t)content_len); if (content_buf == NULL && content_len > 0) { free(name_); free(metalayer); BLOSC_TRACE_ERROR("Unable to allocate metalayer content buffer."); return BLOSC2_ERROR_MEMORY_ALLOC; } if (content_len > 0) { memcpy(content_buf, content, content_len); } metalayer->content = content_buf; metalayer->content_len = content_len; schunk->metalayers[schunk->nmetalayers] = metalayer; schunk->nmetalayers += 1; int rc = metalayer_flush(schunk); if (rc < 0) { return rc; } return schunk->nmetalayers - 1; } /* Update the content of an existing metalayer. * * If successful, return the index of the new metalayer. Else, return a negative value. */ int blosc2_meta_update(blosc2_schunk *schunk, const char *name, uint8_t *content, int32_t content_len) { if (schunk == NULL || name == NULL) { BLOSC_TRACE_ERROR("Invalid parameters."); return BLOSC2_ERROR_INVALID_PARAM; } int nmetalayer = blosc2_meta_exists(schunk, name); if (nmetalayer < 0) { BLOSC_TRACE_ERROR("Metalayer \"%s\" not found.", name); return nmetalayer; } if (content_len < 0) { BLOSC_TRACE_ERROR("Metalayer content length must not be negative."); return BLOSC2_ERROR_INVALID_PARAM; } if (content_len > 0 && content == NULL) { BLOSC_TRACE_ERROR("Metalayer content pointer must not be NULL when content length is positive."); return BLOSC2_ERROR_INVALID_PARAM; } blosc2_metalayer *metalayer = schunk->metalayers[nmetalayer]; if (content_len > metalayer->content_len) { BLOSC_TRACE_ERROR("`content_len` cannot exceed the existing size of %d bytes.", metalayer->content_len); return nmetalayer; } // Update the contents of the metalayer if (content_len > 0) { memcpy(metalayer->content, content, content_len); } // Update the metalayers in frame (as size has not changed, we don't need to update the trailer) blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; if (frame != NULL) { int rc = frame_update_header(frame, schunk, false); if (rc < 0) { BLOSC_TRACE_ERROR("Unable to update meta info from frame."); return rc; } } return nmetalayer; } /* Find whether the schunk has a variable-length metalayer or not. * * If successful, return the index of the variable-length metalayer. Else, return a negative value. */ int blosc2_vlmeta_exists(blosc2_schunk *schunk, const char *name) { if (strlen(name) > BLOSC2_METALAYER_NAME_MAXLEN) { BLOSC_TRACE_ERROR("Variable-length metalayer names cannot be larger than %d chars.", BLOSC2_METALAYER_NAME_MAXLEN); return BLOSC2_ERROR_INVALID_PARAM; } for (int nvlmetalayer = 0; nvlmetalayer < schunk->nvlmetalayers; nvlmetalayer++) { if (strcmp(name, schunk->vlmetalayers[nvlmetalayer]->name) == 0) { return nvlmetalayer; } } return BLOSC2_ERROR_NOT_FOUND; } int vlmetalayer_flush(blosc2_schunk* schunk) { int rc = BLOSC2_ERROR_SUCCESS; blosc2_frame_s* frame = (blosc2_frame_s*)schunk->frame; if (frame == NULL) { return rc; } rc = frame_update_header(frame, schunk, false); if (rc < 0) { BLOSC_TRACE_ERROR("Unable to update metalayers into frame."); return rc; } rc = frame_update_trailer(frame, schunk); if (rc < 0) { BLOSC_TRACE_ERROR("Unable to update trailer into frame."); return rc; } return rc; } /* Add content into a new variable-length metalayer. * * If successful, return the index of the new variable-length metalayer. Else, return a negative value. */ int blosc2_vlmeta_add(blosc2_schunk *schunk, const char *name, uint8_t *content, int32_t content_len, blosc2_cparams *cparams) { if (schunk == NULL || name == NULL) { BLOSC_TRACE_ERROR("Invalid parameters."); return BLOSC2_ERROR_INVALID_PARAM; } int nvlmetalayer = blosc2_vlmeta_exists(schunk, name); if (nvlmetalayer >= 0) { BLOSC_TRACE_ERROR("Variable-length metalayer \"%s\" already exists.", name); return BLOSC2_ERROR_INVALID_PARAM; } if (content_len < 0) { BLOSC_TRACE_ERROR("Variable-length metalayer content length must not be negative."); return BLOSC2_ERROR_INVALID_PARAM; } if (content_len > 0 && content == NULL) { BLOSC_TRACE_ERROR("Variable-length metalayer content pointer must not be NULL when content length is positive."); return BLOSC2_ERROR_INVALID_PARAM; } // Add the vlmetalayer blosc2_metalayer *vlmetalayer = malloc(sizeof(blosc2_metalayer)); BLOSC_ERROR_NULL(vlmetalayer, BLOSC2_ERROR_MEMORY_ALLOC); vlmetalayer->name = strdup(name); if (vlmetalayer->name == NULL) { free(vlmetalayer); BLOSC_TRACE_ERROR("Unable to allocate variable-length metalayer name buffer."); return BLOSC2_ERROR_MEMORY_ALLOC; } int64_t destsize = (int64_t)content_len + BLOSC2_MAX_OVERHEAD; if (destsize > INT32_MAX) { free(vlmetalayer->name); free(vlmetalayer); BLOSC_TRACE_ERROR("Variable-length metalayer size is too large."); return BLOSC2_ERROR_INVALID_PARAM; } size_t destsize_sz = (size_t)destsize; uint8_t* content_buf = malloc(destsize_sz); if (content_buf == NULL) { free(vlmetalayer->name); free(vlmetalayer); BLOSC_TRACE_ERROR("Unable to allocate variable-length metalayer buffer."); return BLOSC2_ERROR_MEMORY_ALLOC; } blosc2_context *cctx; if (cparams != NULL) { cctx = blosc2_create_cctx(*cparams); } else { cctx = blosc2_create_cctx(BLOSC2_CPARAMS_DEFAULTS); } if (cctx == NULL) { free(content_buf); free(vlmetalayer->name); free(vlmetalayer); BLOSC_TRACE_ERROR("Error while creating the compression context"); return BLOSC2_ERROR_NULL_POINTER; } int csize = blosc2_compress_ctx(cctx, content, content_len, content_buf, (int32_t)destsize); if (csize < 0) { blosc2_free_ctx(cctx); free(content_buf); free(vlmetalayer->name); free(vlmetalayer); BLOSC_TRACE_ERROR("Can not compress the `%s` variable-length metalayer.", name); return csize; } blosc2_free_ctx(cctx); uint8_t *compressed_buf = realloc(content_buf, csize); if (compressed_buf == NULL && csize > 0) { compressed_buf = content_buf; } vlmetalayer->content = compressed_buf; vlmetalayer->content_len = csize; schunk->vlmetalayers[schunk->nvlmetalayers] = vlmetalayer; schunk->nvlmetalayers += 1; // Propagate to frames int rc = vlmetalayer_flush(schunk); if (rc < 0) { schunk->nvlmetalayers -= 1; schunk->vlmetalayers[schunk->nvlmetalayers] = NULL; free(vlmetalayer->content); free(vlmetalayer->name); free(vlmetalayer); BLOSC_TRACE_ERROR("Can not propagate de `%s` variable-length metalayer to a frame.", name); return rc; } return schunk->nvlmetalayers - 1; } int blosc2_vlmeta_get(blosc2_schunk *schunk, const char *name, uint8_t **content, int32_t *content_len) { if (schunk == NULL || name == NULL || content == NULL || content_len == NULL) { BLOSC_TRACE_ERROR("Invalid parameters."); return BLOSC2_ERROR_INVALID_PARAM; } int nvlmetalayer = blosc2_vlmeta_exists(schunk, name); if (nvlmetalayer < 0) { BLOSC_TRACE_ERROR("User metalayer \"%s\" not found.", name); return nvlmetalayer; } blosc2_metalayer *meta = schunk->vlmetalayers[nvlmetalayer]; int32_t nbytes, cbytes; blosc2_cbuffer_sizes(meta->content, &nbytes, &cbytes, NULL); if (cbytes != meta->content_len) { BLOSC_TRACE_ERROR("User metalayer \"%s\" is corrupted.", meta->name); return BLOSC2_ERROR_DATA; } if (nbytes < 0) { BLOSC_TRACE_ERROR("User metalayer \"%s\" has corrupted decompressed size %d.", meta->name, nbytes); return BLOSC2_ERROR_DATA; } *content_len = nbytes; if (nbytes == 0) { *content = NULL; } else { *content = malloc((size_t) nbytes); if (*content == NULL) { BLOSC_TRACE_ERROR("Unable to allocate variable-length metalayer content buffer."); *content_len = 0; return BLOSC2_ERROR_MEMORY_ALLOC; } } blosc2_context *dctx = blosc2_create_dctx(*schunk->storage->dparams); if (dctx == NULL) { if (*content != NULL) { free(*content); *content = NULL; } *content_len = 0; BLOSC_TRACE_ERROR("Error while creating the decompression context"); return BLOSC2_ERROR_NULL_POINTER; } int nbytes_ = blosc2_decompress_ctx(dctx, meta->content, meta->content_len, *content, nbytes); blosc2_free_ctx(dctx); if (nbytes_ != nbytes) { if (*content != NULL) { free(*content); *content = NULL; } *content_len = 0; BLOSC_TRACE_ERROR("User metalayer \"%s\" is corrupted.", meta->name); return BLOSC2_ERROR_READ_BUFFER; } return nvlmetalayer; } int blosc2_vlmeta_update(blosc2_schunk *schunk, const char *name, uint8_t *content, int32_t content_len, blosc2_cparams *cparams) { if (schunk == NULL || name == NULL) { BLOSC_TRACE_ERROR("Invalid parameters."); return BLOSC2_ERROR_INVALID_PARAM; } int nvlmetalayer = blosc2_vlmeta_exists(schunk, name); if (nvlmetalayer < 0) { BLOSC_TRACE_ERROR("User vlmetalayer \"%s\" not found.", name); return nvlmetalayer; } if (content_len < 0) { BLOSC_TRACE_ERROR("Variable-length metalayer content length must not be negative."); return BLOSC2_ERROR_INVALID_PARAM; } if (content_len > 0 && content == NULL) { BLOSC_TRACE_ERROR("Variable-length metalayer content pointer must not be NULL when content length is positive."); return BLOSC2_ERROR_INVALID_PARAM; } blosc2_metalayer *vlmetalayer = schunk->vlmetalayers[nvlmetalayer]; int64_t destsize = (int64_t)content_len + BLOSC2_MAX_OVERHEAD; if (destsize > INT32_MAX) { BLOSC_TRACE_ERROR("Variable-length metalayer size is too large."); return BLOSC2_ERROR_INVALID_PARAM; } size_t destsize_sz = (size_t)destsize; uint8_t* content_buf = malloc(destsize_sz); if (content_buf == NULL && content_len > 0) { BLOSC_TRACE_ERROR("Unable to allocate variable-length metalayer buffer."); return BLOSC2_ERROR_MEMORY_ALLOC; } blosc2_context *cctx; if (cparams != NULL) { cctx = blosc2_create_cctx(*cparams); } else { cctx = blosc2_create_cctx(BLOSC2_CPARAMS_DEFAULTS); } if (cctx == NULL) { free(content_buf); BLOSC_TRACE_ERROR("Error while creating the compression context"); return BLOSC2_ERROR_NULL_POINTER; } int csize = blosc2_compress_ctx(cctx, content, content_len, content_buf, (int32_t)destsize); if (csize < 0) { blosc2_free_ctx(cctx); free(content_buf); BLOSC_TRACE_ERROR("Can not compress the `%s` variable-length metalayer.", name); return csize; } blosc2_free_ctx(cctx); uint8_t* compressed_buf = realloc(content_buf, csize); if (compressed_buf == NULL && csize > 0) { compressed_buf = content_buf; } uint8_t* old_content = vlmetalayer->content; vlmetalayer->content = compressed_buf; vlmetalayer->content_len = csize; free(old_content); // Propagate to frames int rc = vlmetalayer_flush(schunk); if (rc < 0) { BLOSC_TRACE_ERROR("Can not propagate de `%s` variable-length metalayer to a frame.", name); return rc; } return nvlmetalayer; } int blosc2_vlmeta_delete(blosc2_schunk *schunk, const char *name) { int nvlmetalayer = blosc2_vlmeta_exists(schunk, name); if (nvlmetalayer < 0) { BLOSC_TRACE_ERROR("User vlmetalayer \"%s\" not found.", name); return nvlmetalayer; } blosc2_metalayer *vlmetalayer = schunk->vlmetalayers[nvlmetalayer]; for (int i = nvlmetalayer; i < (schunk->nvlmetalayers - 1); i++) { schunk->vlmetalayers[i] = schunk->vlmetalayers[i + 1]; } schunk->vlmetalayers[schunk->nvlmetalayers - 1] = NULL; free(vlmetalayer->name); free(vlmetalayer->content); free(vlmetalayer); schunk->nvlmetalayers--; // Propagate to frames int rc = vlmetalayer_flush(schunk); if (rc < 0) { BLOSC_TRACE_ERROR("Can not propagate de `%s` variable-length metalayer to a frame.", name); return rc; } return schunk->nvlmetalayers; } int blosc2_vlmeta_get_names(blosc2_schunk *schunk, char **names) { int16_t nvlmetalayers = schunk->nvlmetalayers; for (int i = 0; i < nvlmetalayers; ++i) { names[i] = schunk->vlmetalayers[i]->name; } return nvlmetalayers; } c-blosc2-3.1.5/blosc/sframe.c000066400000000000000000000127731521743436100156670ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #include "frame.h" #include "blosc2.h" #include #include #include #include #include #include /* If C11 is supported, use it's built-in aligned allocation. */ #if __STDC_VERSION__ >= 201112L #include #endif static char* sframe_make_index_path(const char* urlpath) { size_t path_len = strlen(urlpath); size_t suffix_len = strlen("/chunks.b2frame"); if (path_len > SIZE_MAX - suffix_len - 1) { BLOSC_TRACE_ERROR("Index path length overflows size limits"); return NULL; } char* index_path = malloc(path_len + suffix_len + 1); if (index_path == NULL) { return NULL; } int written = snprintf(index_path, path_len + suffix_len + 1, "%s/chunks.b2frame", urlpath); if (written < 0 || (size_t)written >= path_len + suffix_len + 1) { BLOSC_TRACE_ERROR("Error building index path"); free(index_path); return NULL; } return index_path; } static char* sframe_make_chunk_path(const char* urlpath, int64_t nchunk) { if (nchunk < 0 || (uint64_t)nchunk > UINT32_MAX) { BLOSC_TRACE_ERROR("Chunk index (%" PRId64 ") is out of range for sframe filenames", nchunk); return NULL; } size_t path_len = strlen(urlpath); size_t suffix_len = strlen("/.chunk"); size_t chunk_hex_len = 8; if (path_len > SIZE_MAX - suffix_len - chunk_hex_len - 1) { BLOSC_TRACE_ERROR("Chunk path length overflows size limits"); return NULL; } size_t total_len = path_len + suffix_len + chunk_hex_len + 1; char* chunk_path = malloc(total_len); if (chunk_path == NULL) { return NULL; } int written = snprintf(chunk_path, total_len, "%s/%08" PRIX32 ".chunk", urlpath, (uint32_t)nchunk); if (written < 0 || (size_t)written >= total_len) { BLOSC_TRACE_ERROR("Error building chunk path for chunk index (%" PRId64 ")", nchunk); free(chunk_path); return NULL; } return chunk_path; } /* Open sparse frame index chunk */ void* sframe_open_index(const char* urlpath, const char* mode, const blosc2_io *io) { void* fp = NULL; char* index_path = sframe_make_index_path(urlpath); if (index_path) { blosc2_io_cb *io_cb = blosc2_get_io_cb(io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); free(index_path); return NULL; } fp = io_cb->open(index_path, mode, io->params); if (fp == NULL) BLOSC_TRACE_ERROR("Error creating index path in: %s", index_path); free(index_path); } return fp; } /* Open directory/nchunk.chunk with 8 zeros of padding */ void* sframe_open_chunk(const char* urlpath, int64_t nchunk, const char* mode, const blosc2_io *io) { void* fp = NULL; char* chunk_path = sframe_make_chunk_path(urlpath, nchunk); if (chunk_path) { blosc2_io_cb *io_cb = blosc2_get_io_cb(io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); free(chunk_path); return NULL; } fp = io_cb->open(chunk_path, mode, io->params); if (fp == NULL) BLOSC_TRACE_ERROR("Error opening chunk path in: %s", chunk_path); free(chunk_path); } return fp; } /* Append an existing chunk into a sparse frame. */ void* sframe_create_chunk(blosc2_frame_s* frame, uint8_t* chunk, int64_t nchunk, int64_t cbytes) { void* fpc = sframe_open_chunk(frame->urlpath, nchunk, "wb", frame->schunk->storage->io); if (fpc == NULL) { BLOSC_TRACE_ERROR("Cannot open the chunkfile."); return NULL; } blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); return NULL; } int64_t io_pos = 0; int64_t wbytes = io_cb->write(chunk, 1, cbytes, io_pos, fpc); io_cb->close(fpc); if (wbytes != cbytes) { BLOSC_TRACE_ERROR("Cannot write the full chunk."); return NULL; } return frame; } /* Append an existing chunk into a sparse frame. */ int sframe_delete_chunk(const char *urlpath, int64_t nchunk) { char* chunk_path = sframe_make_chunk_path(urlpath, nchunk); if (chunk_path) { int rc = remove(chunk_path); free(chunk_path); return rc; } return BLOSC2_ERROR_FILE_REMOVE; } /* Get chunk from sparse frame. */ int32_t sframe_get_chunk(blosc2_frame_s* frame, int64_t nchunk, uint8_t** chunk, bool* needs_free){ void *fpc = sframe_open_chunk(frame->urlpath, nchunk, "rb", frame->schunk->storage->io); if(fpc == NULL){ BLOSC_TRACE_ERROR("Cannot open the chunkfile."); return BLOSC2_ERROR_FILE_OPEN; } blosc2_io_cb *io_cb = blosc2_get_io_cb(frame->schunk->storage->io->id); if (io_cb == NULL) { BLOSC_TRACE_ERROR("Error getting the input/output API"); return BLOSC2_ERROR_PLUGIN_IO; } int64_t chunk_cbytes = io_cb->size(fpc); if (io_cb->is_allocation_necessary) { *chunk = malloc((size_t)chunk_cbytes); *needs_free = true; } else { *needs_free = false; } int64_t io_pos = 0; int64_t rbytes = io_cb->read((void**)chunk, 1, chunk_cbytes, io_pos, fpc); io_cb->close(fpc); if (rbytes != chunk_cbytes) { BLOSC_TRACE_ERROR("Cannot read the chunk out of the chunkfile."); return BLOSC2_ERROR_FILE_READ; } return (int32_t)chunk_cbytes; } c-blosc2-3.1.5/blosc/sframe.h000066400000000000000000000017141521743436100156650ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #ifndef BLOSC_SFRAME_H #define BLOSC_SFRAME_H #include "frame.h" #include #include void* sframe_open_index(const char* urlpath, const char* mode, const blosc2_io *io); void* sframe_open_chunk(const char* urlpath, int64_t nchunk, const char* mode, const blosc2_io *io); int sframe_delete_chunk(const char* urlpath, int64_t nchunk); void* sframe_create_chunk(blosc2_frame_s* frame, uint8_t* chunk, int64_t nchunk, int64_t cbytes); int32_t sframe_get_chunk(blosc2_frame_s* frame, int64_t nchunk, uint8_t** chunk, bool* needs_free); #endif /* BLOSC_SFRAME_H */ c-blosc2-3.1.5/blosc/shuffle-altivec.c000066400000000000000000000401551521743436100174660ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team and Jerome Kieffer https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #include "shuffle-altivec.h" #include "shuffle-generic.h" #include /* Make sure ALTIVEC is available for the compilation target and compiler. VSX (POWER7+) is required for vec_xl/vec_xst (unaligned loads/stores). Exclude __APPLE__ because some GCC versions falsely define __VSX__ on powerpc-apple-darwin targets (GCC Bug #121696, cf. Eigen's workaround). */ #if defined(__ALTIVEC__) && defined(__VSX__) && !defined(__APPLE__) #include "transpose-altivec.h" #include #include /* Routine optimized for shuffling a buffer for a type size of 2 bytes. */ static void shuffle2_altivec(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements){ static const int32_t bytesoftype = 2; uint32_t i, j; __vector uint8_t xmm0[2]; for (j = 0; j < vectorizable_elements; j += 16){ /* Fetch 16 elements (32 bytes) */ for (i = 0; i < bytesoftype; i++) xmm0[i] = vec_xl(bytesoftype * j + 16 * i, src); /* Transpose vectors */ transpose2x16(xmm0); /* Store the result vectors */ for (i = 0; i < bytesoftype; i++) vec_xst(xmm0[i], j + i * total_elements, dest); } } /* Routine optimized for shuffling a buffer for a type size of 4 bytes. */ static void shuffle4_altivec(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements){ static const int32_t bytesoftype = 4; int32_t i, j; __vector uint8_t xmm0[4]; for (j = 0; j < vectorizable_elements; j += 16) { /* Fetch 16 elements (64 bytes, 4 vectors) */ for (i = 0; i < bytesoftype; i++) xmm0[i] = vec_xl(bytesoftype * j + 16 * i, src); /* Transpose vectors */ transpose4x16(xmm0); /* Store the result vectors */ for (i = 0; i < bytesoftype; i ++){ vec_xst(xmm0[i], j + i*total_elements, dest); } } } /* Routine optimized for shuffling a buffer for a type size of 8 bytes. */ static void shuffle8_altivec(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements) { static const uint8_t bytesoftype = 8; int32_t i, j; __vector uint8_t xmm0[8]; for (j = 0; j < vectorizable_elements; j += 16) { /* Fetch 16 elements (128 bytes, 8 vectors) */ for (i = 0; i < bytesoftype; i++) xmm0[i] = vec_xl(bytesoftype * j + 16 * i, src); /* Transpose vectors */ transpose8x16(xmm0); /* Store the result vectors */ for (i = 0; i < bytesoftype; i++) vec_xst(xmm0[i], j + i*total_elements, dest); } } /* Routine optimized for shuffling a buffer for a type size of 16 bytes. */ static void shuffle16_altivec(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements) { static const int32_t bytesoftype = 16; int32_t i, j; __vector uint8_t xmm0[16]; for (j = 0; j < vectorizable_elements; j += 16) { /* Fetch 16 elements (256 bytes, 16 vectors) */ for (i = 0; i < bytesoftype; i++) xmm0[i] = vec_xl(bytesoftype * j + 16 * i, src); // Do the job ! transpose16x16(xmm0); /* Store the result vectors */ for (i = 0; i < bytesoftype; i ++) vec_xst(xmm0[i], j + i * total_elements, dest); } } /* Routine optimized for shuffling a buffer for a type size larger than 16 bytes. */ static void shuffle16_tiled_altivec(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements, const int32_t bytesoftype) { int32_t j, k; const int32_t vecs_per_el_rem = bytesoftype & 0xF; __vector uint8_t xmm[16]; for (j = 0; j < vectorizable_elements; j += 16) { /* Advance the offset into the type by the vector size (in bytes), unless this is the initial iteration and the type size is not a multiple of the vector size. In that case, only advance by the number of bytes necessary so that the number of remaining bytes in the type will be a multiple of the vector size. */ int32_t offset_into_type; for (offset_into_type = 0; offset_into_type < bytesoftype; offset_into_type += (offset_into_type == 0 && vecs_per_el_rem > 0 ? vecs_per_el_rem : 16)) { /* Fetch elements in groups of 256 bytes */ const uint8_t* const src_with_offset = src + offset_into_type; for (k = 0; k < 16; k++) xmm[k] = vec_xl((j + k) * bytesoftype, src_with_offset); // Do the Job! transpose16x16(xmm); /* Store the result vectors */ for (k = 0; k < 16; k++) { vec_xst(xmm[k], j + total_elements * (offset_into_type + k), dest); } } } } /* Routine optimized for unshuffling a buffer for a type size of 2 bytes. */ static void unshuffle2_altivec(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements) { static const int32_t bytesoftype = 2; uint32_t i, j; __vector uint8_t xmm0[2], xmm1[2]; for (j = 0; j < vectorizable_elements; j += 16) { /* Load 16 elements (32 bytes) into 2 vectors registers. */ for (i = 0; i < bytesoftype; i++) xmm0[i] = vec_xl(j + i * total_elements, src); /* Shuffle bytes */ /* Note the shuffling is different from intel's SSE2 */ xmm1[0] = vec_vmrghb(xmm0[0], xmm0[1]); xmm1[1] = vec_vmrglb(xmm0[0], xmm0[1]); /* Store the result vectors*/ for (i = 0; i < bytesoftype; i++) vec_xst(xmm1[i], bytesoftype * j + 16 * i, dest); } } /* Routine optimized for unshuffling a buffer for a type size of 4 bytes. */ static void unshuffle4_altivec(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements) { static const int32_t bytesoftype = 4; uint32_t i, j; __vector uint8_t xmm0[4], xmm1[4]; for (j = 0; j < vectorizable_elements; j += 16) { /* Load 16 elements (64 bytes) into 4 vectors registers. */ for (i = 0; i < bytesoftype; i++) xmm0[i] = vec_xl(j + i * total_elements, src); /* Shuffle bytes */ for (i = 0; i < 2; i++) { xmm1[i ] = vec_vmrghb(xmm0[i * 2], xmm0[i * 2 + 1]); xmm1[i+2] = vec_vmrglb(xmm0[i * 2], xmm0[i * 2 + 1]); } /* Shuffle 2-byte words */ for (i = 0; i < 2; i++) { /* Compute the low 32 bytes */ xmm0[i] = (__vector uint8_t) vec_vmrghh((__vector uint16_t)xmm1[i * 2], (__vector uint16_t) xmm1[i * 2 + 1]); /* Compute the hi 32 bytes */ xmm0[i+2] = (__vector uint8_t) vec_vmrglh((__vector uint16_t)xmm1[i * 2], (__vector uint16_t)xmm1[i * 2 + 1]); } /* Store the result vectors in proper order */ vec_xst(xmm0[0], bytesoftype * j, dest); vec_xst(xmm0[2], bytesoftype * j + 16, dest); vec_xst(xmm0[1], bytesoftype * j + 32, dest); vec_xst(xmm0[3], bytesoftype * j + 48, dest); } } /* Routine optimized for unshuffling a buffer for a type size of 8 bytes. */ static void unshuffle8_altivec(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements) { static const uint8_t bytesoftype = 8; uint32_t i, j; __vector uint8_t xmm0[8], xmm1[8]; // Initialize permutations for writing for (j = 0; j < vectorizable_elements; j += 16) { /* Load 16 elements (64 bytes) into 4 vectors registers. */ for (i = 0; i < bytesoftype; i++) xmm0[i] = vec_xl(j + i * total_elements, src); /* Shuffle bytes */ for (i = 0; i < 4; i++) { xmm1[i] = vec_vmrghb(xmm0[i * 2], xmm0[i * 2 + 1]); xmm1[4 + i] = vec_vmrglb(xmm0[i * 2], xmm0[i * 2 + 1]); } /* Shuffle 2-byte words */ for (i = 0; i < 4; i++) { xmm0[i] = (__vector uint8_t)vec_vmrghh((__vector uint16_t)xmm1[i * 2], (__vector uint16_t)xmm1[i * 2 + 1]); xmm0[4 + i] = (__vector uint8_t)vec_vmrglh((__vector uint16_t)xmm1[i * 2], (__vector uint16_t)xmm1[i * 2 + 1]); } /* Shuffle 4-byte dwords */ for (i = 0; i < 4; i++) { xmm1[i] = (__vector uint8_t)vec_vmrghw((__vector uint32_t)xmm0[i * 2], (__vector uint32_t)xmm0[i * 2 + 1]); xmm1[4 + i] = (__vector uint8_t)vec_vmrglw((__vector uint32_t)xmm0[i * 2], (__vector uint32_t)xmm0[i * 2 + 1]); } /* Store the result vectors in proper order */ vec_xst(xmm1[0], bytesoftype * j, dest); vec_xst(xmm1[4], bytesoftype * j + 16, dest); vec_xst(xmm1[2], bytesoftype * j + 32, dest); vec_xst(xmm1[6], bytesoftype * j + 48, dest); vec_xst(xmm1[1], bytesoftype * j + 64, dest); vec_xst(xmm1[5], bytesoftype * j + 80, dest); vec_xst(xmm1[3], bytesoftype * j + 96, dest); vec_xst(xmm1[7], bytesoftype * j + 112, dest); } } /* Routine optimized for unshuffling a buffer for a type size of 16 bytes. */ static void unshuffle16_altivec(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements) { static const int32_t bytesoftype = 16; uint32_t i, j; __vector uint8_t xmm0[16]; for (j = 0; j < vectorizable_elements; j += 16) { /* Load 16 elements (64 bytes) into 4 vectors registers. */ for (i = 0; i < bytesoftype; i++) xmm0[i] = vec_xl(j + i * total_elements, src); // Do the Job! transpose16x16(xmm0); /* Store the result vectors*/ for (i = 0; i < 16; i++) vec_st(xmm0[i], bytesoftype * (i+j), dest); } } /* Routine optimized for unshuffling a buffer for a type size larger than 16 bytes. */ static void unshuffle16_tiled_altivec(uint8_t* const dest, const uint8_t* const orig, const int32_t vectorizable_elements, const int32_t total_elements, const int32_t bytesoftype) { int32_t i, j, offset_into_type; const int32_t vecs_per_el_rem = bytesoftype & 0xF; __vector uint8_t xmm[16]; /* Advance the offset into the type by the vector size (in bytes), unless this is the initial iteration and the type size is not a multiple of the vector size. In that case, only advance by the number of bytes necessary so that the number of remaining bytes in the type will be a multiple of the vector size. */ for (offset_into_type = 0; offset_into_type < bytesoftype; offset_into_type += (offset_into_type == 0 && vecs_per_el_rem > 0 ? vecs_per_el_rem : 16)) { for (i = 0; i < vectorizable_elements; i += 16) { /* Load the first 128 bytes in 16 XMM registers */ for (j = 0; j < 16; j++) xmm[j] = vec_xl(total_elements * (offset_into_type + j) + i, orig); // Do the Job ! transpose16x16(xmm); /* Store the result vectors in proper order */ for (j = 0; j < 16; j++) vec_xst(xmm[j], (i + j) * bytesoftype + offset_into_type, dest); } } } /* Shuffle a block. This can never fail. */ void shuffle_altivec(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest) { int32_t vectorized_chunk_size; vectorized_chunk_size = bytesoftype * 16; /* If the blocksize is not a multiple of both the typesize and the vector size, round the blocksize down to the next value which is a multiple of both. The vectorized shuffle can be used for that portion of the data, and the naive implementation can be used for the remaining portion. */ const int32_t vectorizable_bytes = blocksize - (blocksize % vectorized_chunk_size); const int32_t vectorizable_elements = vectorizable_bytes / bytesoftype; const int32_t total_elements = blocksize / bytesoftype; /* If the block size is too small to be vectorized, use the generic implementation. */ if (blocksize < vectorized_chunk_size) { shuffle_generic(bytesoftype, blocksize, _src, _dest); return; } /* Optimized shuffle implementations */ switch (bytesoftype) { case 2: shuffle2_altivec(_dest, _src, vectorizable_elements, total_elements); break; case 4: shuffle4_altivec(_dest, _src, vectorizable_elements, total_elements); break; case 8: shuffle8_altivec(_dest, _src, vectorizable_elements, total_elements); break; case 16: shuffle16_altivec(_dest, _src, vectorizable_elements, total_elements); break; default: if (bytesoftype > 16) { shuffle16_tiled_altivec(_dest, _src, vectorizable_elements, total_elements, bytesoftype); } else { /* Non-optimized shuffle */ shuffle_generic(bytesoftype, blocksize, _src, _dest); /* The non-optimized function covers the whole buffer, so we're done processing here. */ return; } } /* If the buffer had any bytes at the end which couldn't be handled by the vectorized implementations, use the non-optimized version to finish them up. */ if (vectorizable_bytes < blocksize) { shuffle_generic_inline(bytesoftype, vectorizable_bytes, blocksize, _src, _dest); } } /* Unshuffle a block. This can never fail. */ void unshuffle_altivec(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest) { const int32_t vectorized_chunk_size = bytesoftype * 16; /* If the blocksize is not a multiple of both the typesize and the vector size, round the blocksize down to the next value which is a multiple of both. The vectorized unshuffle can be used for that portion of the data, and the naive implementation can be used for the remaining portion. */ const int32_t vectorizable_bytes = blocksize - (blocksize % vectorized_chunk_size); const int32_t vectorizable_elements = vectorizable_bytes / bytesoftype; const int32_t total_elements = blocksize / bytesoftype; /* If the block size is too small to be vectorized, use the generic implementation. */ if (blocksize < vectorized_chunk_size) { unshuffle_generic(bytesoftype, blocksize, _src, _dest); return; } /* Optimized unshuffle implementations */ switch (bytesoftype) { case 2: unshuffle2_altivec(_dest, _src, vectorizable_elements, total_elements); break; case 4: unshuffle4_altivec(_dest, _src, vectorizable_elements, total_elements); break; case 8: unshuffle8_altivec(_dest, _src, vectorizable_elements, total_elements); break; case 16: unshuffle16_altivec(_dest, _src, vectorizable_elements, total_elements); break; default: if (bytesoftype > 16) { unshuffle16_tiled_altivec(_dest, _src, vectorizable_elements, total_elements, bytesoftype); } else { /* Non-optimized unshuffle */ unshuffle_generic(bytesoftype, blocksize, _src, _dest); /* The non-optimized function covers the whole buffer, so we're done processing here. */ return; } } /* If the buffer had any bytes at the end which couldn't be handled by the vectorized implementations, use the non-optimized version to finish them up. */ if (vectorizable_bytes < blocksize) { unshuffle_generic_inline(bytesoftype, vectorizable_bytes, blocksize, _src, _dest); } } const bool is_shuffle_altivec = true; #else /* defined(__ALTIVEC__) && defined(__VSX__) && !defined(__APPLE__) */ const bool is_shuffle_altivec = false; void shuffle_altivec(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest) { abort(); } void unshuffle_altivec(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest) { abort(); } #endif /* defined(__ALTIVEC__) && defined(__VSX__) && !defined(__APPLE__) */ c-blosc2-3.1.5/blosc/shuffle-altivec.h000066400000000000000000000022021521743436100174620ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /* ALTIVEC-accelerated shuffle/unshuffle routines. */ #ifndef BLOSC_SHUFFLE_ALTIVEC_H #define BLOSC_SHUFFLE_ALTIVEC_H #include "blosc2/blosc2-common.h" #include #include /** * ALTIVEC-accelerated (un)shuffle routines availability. */ extern const bool is_shuffle_altivec; /** ALTIVEC-accelerated shuffle routine. */ BLOSC_NO_EXPORT void shuffle_altivec(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest); /** ALTIVEC-accelerated unshuffle routine. */ BLOSC_NO_EXPORT void unshuffle_altivec(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest); #endif /* BLOSC_SHUFFLE_ALTIVEC_H */ c-blosc2-3.1.5/blosc/shuffle-avx2.c000066400000000000000000001066431521743436100167240ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #include "shuffle-avx2.h" #include "shuffle-generic.h" #include /* Make sure AVX2 is available for the compilation target and compiler. */ #if defined(__AVX2__) #include #include /* The next is useful for debugging purposes */ #if 0 #include #include static void printymm32(__m256i ymm0) { uint32_t buf[8]; ((__m256i *)buf)[0] = ymm0; fprintf(stderr, "%x,%x,%x,%x,%x,%x,%x,%x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7]); } static void printymm(__m256i ymm0) { uint8_t buf[32]; ((__m256i *)buf)[0] = ymm0; fprintf(stderr, "%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15], buf[16], buf[17], buf[18], buf[19], buf[20], buf[21], buf[22], buf[23], buf[24], buf[25], buf[26], buf[27], buf[28], buf[29], buf[30], buf[31]); } #endif /* GCC doesn't include the split load/store intrinsics needed for the tiled shuffle, so define them here. */ #if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) static inline __m256i __attribute__((__always_inline__)) _mm256_loadu2_m128i(const __m128i* const hiaddr, const __m128i* const loaddr) { return _mm256_inserti128_si256( _mm256_castsi128_si256(_mm_loadu_si128(loaddr)), _mm_loadu_si128(hiaddr), 1); } static inline void __attribute__((__always_inline__)) _mm256_storeu2_m128i(__m128i* const hiaddr, __m128i* const loaddr, const __m256i a) { _mm_storeu_si128(loaddr, _mm256_castsi256_si128(a)); _mm_storeu_si128(hiaddr, _mm256_extracti128_si256(a, 1)); } #endif /* defined(__GNUC__) */ /* Routine optimized for shuffling a buffer for a type size of 2 bytes. */ static void shuffle2_avx2(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements) { static const int32_t bytesoftype = 2; int32_t j; int k; __m256i ymm0[2], ymm1[2]; /* Create the shuffle mask. NOTE: The XMM/YMM 'set' intrinsics require the arguments to be ordered from most to least significant (i.e., their order is reversed when compared to loading the mask from an array). */ const __m256i shmask = _mm256_set_epi8( 0x0f, 0x0d, 0x0b, 0x09, 0x07, 0x05, 0x03, 0x01, 0x0e, 0x0c, 0x0a, 0x08, 0x06, 0x04, 0x02, 0x00, 0x0f, 0x0d, 0x0b, 0x09, 0x07, 0x05, 0x03, 0x01, 0x0e, 0x0c, 0x0a, 0x08, 0x06, 0x04, 0x02, 0x00); for (j = 0; j < vectorizable_elements; j += sizeof(__m256i)) { /* Fetch 32 elements (64 bytes) then transpose bytes, words and double words. */ for (k = 0; k < 2; k++) { ymm0[k] = _mm256_loadu_si256((__m256i*)(src + (j * bytesoftype) + (k * sizeof(__m256i)))); ymm1[k] = _mm256_shuffle_epi8(ymm0[k], shmask); } ymm0[0] = _mm256_permute4x64_epi64(ymm1[0], 0xd8); ymm0[1] = _mm256_permute4x64_epi64(ymm1[1], 0x8d); ymm1[0] = _mm256_blend_epi32(ymm0[0], ymm0[1], 0xf0); ymm0[1] = _mm256_blend_epi32(ymm0[0], ymm0[1], 0x0f); ymm1[1] = _mm256_permute4x64_epi64(ymm0[1], 0x4e); /* Store the result vectors */ uint8_t* const dest_for_jth_element = dest + j; for (k = 0; k < 2; k++) { _mm256_storeu_si256((__m256i*)(dest_for_jth_element + (k * total_elements)), ymm1[k]); } } } /* Routine optimized for shuffling a buffer for a type size of 4 bytes. */ static void shuffle4_avx2(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements) { static const int32_t bytesoftype = 4; int32_t i; int j; __m256i ymm0[4], ymm1[4]; /* Create the shuffle mask. NOTE: The XMM/YMM 'set' intrinsics require the arguments to be ordered from most to least significant (i.e., their order is reversed when compared to loading the mask from an array). */ const __m256i mask = _mm256_set_epi32( 0x07, 0x03, 0x06, 0x02, 0x05, 0x01, 0x04, 0x00); for (i = 0; i < vectorizable_elements; i += sizeof(__m256i)) { /* Fetch 32 elements (128 bytes) then transpose bytes and words. */ for (j = 0; j < 4; j++) { ymm0[j] = _mm256_loadu_si256((__m256i*)(src + (i * bytesoftype) + (j * sizeof(__m256i)))); ymm1[j] = _mm256_shuffle_epi32(ymm0[j], 0xd8); ymm0[j] = _mm256_shuffle_epi32(ymm0[j], 0x8d); ymm0[j] = _mm256_unpacklo_epi8(ymm1[j], ymm0[j]); ymm1[j] = _mm256_shuffle_epi32(ymm0[j], 0x04e); ymm0[j] = _mm256_unpacklo_epi16(ymm0[j], ymm1[j]); } /* Transpose double words */ for (j = 0; j < 2; j++) { ymm1[j * 2] = _mm256_unpacklo_epi32(ymm0[j * 2], ymm0[j * 2 + 1]); ymm1[j * 2 + 1] = _mm256_unpackhi_epi32(ymm0[j * 2], ymm0[j * 2 + 1]); } /* Transpose quad words */ for (j = 0; j < 2; j++) { ymm0[j * 2] = _mm256_unpacklo_epi64(ymm1[j], ymm1[j + 2]); ymm0[j * 2 + 1] = _mm256_unpackhi_epi64(ymm1[j], ymm1[j + 2]); } for (j = 0; j < 4; j++) { ymm0[j] = _mm256_permutevar8x32_epi32(ymm0[j], mask); } /* Store the result vectors */ uint8_t* const dest_for_ith_element = dest + i; for (j = 0; j < 4; j++) { _mm256_storeu_si256((__m256i*)(dest_for_ith_element + (j * total_elements)), ymm0[j]); } } } /* Routine optimized for shuffling a buffer for a type size of 8 bytes. */ static void shuffle8_avx2(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements) { static const int32_t bytesoftype = 8; int32_t j; int k, l; __m256i ymm0[8], ymm1[8]; for (j = 0; j < vectorizable_elements; j += sizeof(__m256i)) { /* Fetch 32 elements (256 bytes) then transpose bytes. */ for (k = 0; k < 8; k++) { ymm0[k] = _mm256_loadu_si256((__m256i*)(src + (j * bytesoftype) + (k * sizeof(__m256i)))); ymm1[k] = _mm256_shuffle_epi32(ymm0[k], 0x4e); ymm1[k] = _mm256_unpacklo_epi8(ymm0[k], ymm1[k]); } /* Transpose words */ for (k = 0, l = 0; k < 4; k++, l += 2) { ymm0[k * 2] = _mm256_unpacklo_epi16(ymm1[l], ymm1[l + 1]); ymm0[k * 2 + 1] = _mm256_unpackhi_epi16(ymm1[l], ymm1[l + 1]); } /* Transpose double words */ for (k = 0, l = 0; k < 4; k++, l++) { if (k == 2) l += 2; ymm1[k * 2] = _mm256_unpacklo_epi32(ymm0[l], ymm0[l + 2]); ymm1[k * 2 + 1] = _mm256_unpackhi_epi32(ymm0[l], ymm0[l + 2]); } /* Transpose quad words */ for (k = 0; k < 4; k++) { ymm0[k * 2] = _mm256_unpacklo_epi64(ymm1[k], ymm1[k + 4]); ymm0[k * 2 + 1] = _mm256_unpackhi_epi64(ymm1[k], ymm1[k + 4]); } for (k = 0; k < 8; k++) { ymm1[k] = _mm256_permute4x64_epi64(ymm0[k], 0x72); ymm0[k] = _mm256_permute4x64_epi64(ymm0[k], 0xD8); ymm0[k] = _mm256_unpacklo_epi16(ymm0[k], ymm1[k]); } /* Store the result vectors */ uint8_t* const dest_for_jth_element = dest + j; for (k = 0; k < 8; k++) { _mm256_storeu_si256((__m256i*)(dest_for_jth_element + (k * total_elements)), ymm0[k]); } } } /* Routine optimized for shuffling a buffer for a type size of 16 bytes. */ static void shuffle16_avx2(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements) { static const int32_t bytesoftype = 16; int32_t j; int k, l; __m256i ymm0[16], ymm1[16]; /* Create the shuffle mask. NOTE: The XMM/YMM 'set' intrinsics require the arguments to be ordered from most to least significant (i.e., their order is reversed when compared to loading the mask from an array). */ const __m256i shmask = _mm256_set_epi8( 0x0f, 0x07, 0x0e, 0x06, 0x0d, 0x05, 0x0c, 0x04, 0x0b, 0x03, 0x0a, 0x02, 0x09, 0x01, 0x08, 0x00, 0x0f, 0x07, 0x0e, 0x06, 0x0d, 0x05, 0x0c, 0x04, 0x0b, 0x03, 0x0a, 0x02, 0x09, 0x01, 0x08, 0x00); for (j = 0; j < vectorizable_elements; j += sizeof(__m256i)) { /* Fetch 32 elements (512 bytes) into 16 YMM registers. */ for (k = 0; k < 16; k++) { ymm0[k] = _mm256_loadu_si256((__m256i*)(src + (j * bytesoftype) + (k * sizeof(__m256i)))); } /* Transpose bytes */ for (k = 0, l = 0; k < 8; k++, l += 2) { ymm1[k * 2] = _mm256_unpacklo_epi8(ymm0[l], ymm0[l + 1]); ymm1[k * 2 + 1] = _mm256_unpackhi_epi8(ymm0[l], ymm0[l + 1]); } /* Transpose words */ for (k = 0, l = -2; k < 8; k++, l++) { if ((k % 2) == 0) l += 2; ymm0[k * 2] = _mm256_unpacklo_epi16(ymm1[l], ymm1[l + 2]); ymm0[k * 2 + 1] = _mm256_unpackhi_epi16(ymm1[l], ymm1[l + 2]); } /* Transpose double words */ for (k = 0, l = -4; k < 8; k++, l++) { if ((k % 4) == 0) l += 4; ymm1[k * 2] = _mm256_unpacklo_epi32(ymm0[l], ymm0[l + 4]); ymm1[k * 2 + 1] = _mm256_unpackhi_epi32(ymm0[l], ymm0[l + 4]); } /* Transpose quad words */ for (k = 0; k < 8; k++) { ymm0[k * 2] = _mm256_unpacklo_epi64(ymm1[k], ymm1[k + 8]); ymm0[k * 2 + 1] = _mm256_unpackhi_epi64(ymm1[k], ymm1[k + 8]); } for (k = 0; k < 16; k++) { ymm0[k] = _mm256_permute4x64_epi64(ymm0[k], 0xd8); ymm0[k] = _mm256_shuffle_epi8(ymm0[k], shmask); } /* Store the result vectors */ uint8_t* const dest_for_jth_element = dest + j; for (k = 0; k < 16; k++) { _mm256_storeu_si256((__m256i*)(dest_for_jth_element + (k * total_elements)), ymm0[k]); } } } /* Routine optimized for shuffling a buffer for a type size larger than 16 bytes. */ static void shuffle16_tiled_avx2(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements, const int32_t bytesoftype) { int32_t j; int k, l; __m256i ymm0[16], ymm1[16]; const lldiv_t vecs_per_el = lldiv(bytesoftype, sizeof(__m128i)); const int32_t vecs_rem = (int32_t)vecs_per_el.rem; /* Create the shuffle mask. NOTE: The XMM/YMM 'set' intrinsics require the arguments to be ordered from most to least significant (i.e., their order is reversed when compared to loading the mask from an array). */ const __m256i shmask = _mm256_set_epi8( 0x0f, 0x07, 0x0e, 0x06, 0x0d, 0x05, 0x0c, 0x04, 0x0b, 0x03, 0x0a, 0x02, 0x09, 0x01, 0x08, 0x00, 0x0f, 0x07, 0x0e, 0x06, 0x0d, 0x05, 0x0c, 0x04, 0x0b, 0x03, 0x0a, 0x02, 0x09, 0x01, 0x08, 0x00); for (j = 0; j < vectorizable_elements; j += sizeof(__m256i)) { /* Advance the offset into the type by the vector size (in bytes), unless this is the initial iteration and the type size is not a multiple of the vector size. In that case, only advance by the number of bytes necessary so that the number of remaining bytes in the type will be a multiple of the vector size. */ int32_t offset_into_type; for (offset_into_type = 0; offset_into_type < bytesoftype; offset_into_type += (offset_into_type == 0 && vecs_rem > 0 ? vecs_rem : (int32_t)sizeof(__m128i))) { /* Fetch elements in groups of 512 bytes */ const uint8_t* const src_with_offset = src + offset_into_type; for (k = 0; k < 16; k++) { ymm0[k] = _mm256_loadu2_m128i( (__m128i*)(src_with_offset + (j + (2 * k) + 1) * bytesoftype), (__m128i*)(src_with_offset + (j + (2 * k)) * bytesoftype)); } /* Transpose bytes */ for (k = 0, l = 0; k < 8; k++, l += 2) { ymm1[k * 2] = _mm256_unpacklo_epi8(ymm0[l], ymm0[l + 1]); ymm1[k * 2 + 1] = _mm256_unpackhi_epi8(ymm0[l], ymm0[l + 1]); } /* Transpose words */ for (k = 0, l = -2; k < 8; k++, l++) { if ((k % 2) == 0) l += 2; ymm0[k * 2] = _mm256_unpacklo_epi16(ymm1[l], ymm1[l + 2]); ymm0[k * 2 + 1] = _mm256_unpackhi_epi16(ymm1[l], ymm1[l + 2]); } /* Transpose double words */ for (k = 0, l = -4; k < 8; k++, l++) { if ((k % 4) == 0) l += 4; ymm1[k * 2] = _mm256_unpacklo_epi32(ymm0[l], ymm0[l + 4]); ymm1[k * 2 + 1] = _mm256_unpackhi_epi32(ymm0[l], ymm0[l + 4]); } /* Transpose quad words */ for (k = 0; k < 8; k++) { ymm0[k * 2] = _mm256_unpacklo_epi64(ymm1[k], ymm1[k + 8]); ymm0[k * 2 + 1] = _mm256_unpackhi_epi64(ymm1[k], ymm1[k + 8]); } for (k = 0; k < 16; k++) { ymm0[k] = _mm256_permute4x64_epi64(ymm0[k], 0xd8); ymm0[k] = _mm256_shuffle_epi8(ymm0[k], shmask); } /* Store the result vectors */ uint8_t* const dest_for_jth_element = dest + j; for (k = 0; k < 16; k++) { _mm256_storeu_si256((__m256i*)(dest_for_jth_element + (total_elements * (offset_into_type + k))), ymm0[k]); } } } } /* Routine optimized for unshuffling a buffer for a type size of 2 bytes. */ static void unshuffle2_avx2(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements) { static const int32_t bytesoftype = 2; int32_t i; int j; __m256i ymm0[2], ymm1[2]; for (i = 0; i < vectorizable_elements; i += sizeof(__m256i)) { /* Load 32 elements (64 bytes) into 2 YMM registers. */ const uint8_t* const src_for_ith_element = src + i; for (j = 0; j < 2; j++) { ymm0[j] = _mm256_loadu_si256((__m256i*)(src_for_ith_element + (j * total_elements))); } /* Shuffle bytes */ for (j = 0; j < 2; j++) { ymm0[j] = _mm256_permute4x64_epi64(ymm0[j], 0xd8); } /* Compute the low 64 bytes */ ymm1[0] = _mm256_unpacklo_epi8(ymm0[0], ymm0[1]); /* Compute the hi 64 bytes */ ymm1[1] = _mm256_unpackhi_epi8(ymm0[0], ymm0[1]); /* Store the result vectors in proper order */ _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (0 * sizeof(__m256i))), ymm1[0]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (1 * sizeof(__m256i))), ymm1[1]); } } /* Routine optimized for unshuffling a buffer for a type size of 4 bytes. */ static void unshuffle4_avx2(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements) { static const int32_t bytesoftype = 4; int32_t i; int j; __m256i ymm0[4], ymm1[4]; for (i = 0; i < vectorizable_elements; i += sizeof(__m256i)) { /* Load 32 elements (128 bytes) into 4 YMM registers. */ const uint8_t* const src_for_ith_element = src + i; for (j = 0; j < 4; j++) { ymm0[j] = _mm256_loadu_si256((__m256i*)(src_for_ith_element + (j * total_elements))); } /* Shuffle bytes */ for (j = 0; j < 2; j++) { /* Compute the low 64 bytes */ ymm1[j] = _mm256_unpacklo_epi8(ymm0[j * 2], ymm0[j * 2 + 1]); /* Compute the hi 64 bytes */ ymm1[2 + j] = _mm256_unpackhi_epi8(ymm0[j * 2], ymm0[j * 2 + 1]); } /* Shuffle 2-byte words */ for (j = 0; j < 2; j++) { /* Compute the low 64 bytes */ ymm0[j] = _mm256_unpacklo_epi16(ymm1[j * 2], ymm1[j * 2 + 1]); /* Compute the hi 64 bytes */ ymm0[2 + j] = _mm256_unpackhi_epi16(ymm1[j * 2], ymm1[j * 2 + 1]); } ymm1[0] = _mm256_permute2x128_si256(ymm0[0], ymm0[2], 0x20); ymm1[1] = _mm256_permute2x128_si256(ymm0[1], ymm0[3], 0x20); ymm1[2] = _mm256_permute2x128_si256(ymm0[0], ymm0[2], 0x31); ymm1[3] = _mm256_permute2x128_si256(ymm0[1], ymm0[3], 0x31); /* Store the result vectors in proper order */ for (j = 0; j < 4; j++) { _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (j * sizeof(__m256i))), ymm1[j]); } } } /* Routine optimized for unshuffling a buffer for a type size of 8 bytes. */ static void unshuffle8_avx2(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements) { static const int32_t bytesoftype = 8; int32_t i; int j; __m256i ymm0[8], ymm1[8]; for (i = 0; i < vectorizable_elements; i += sizeof(__m256i)) { /* Fetch 32 elements (256 bytes) into 8 YMM registers. */ const uint8_t* const src_for_ith_element = src + i; for (j = 0; j < 8; j++) { ymm0[j] = _mm256_loadu_si256((__m256i*)(src_for_ith_element + (j * total_elements))); } /* Shuffle bytes */ for (j = 0; j < 4; j++) { /* Compute the low 32 bytes */ ymm1[j] = _mm256_unpacklo_epi8(ymm0[j * 2], ymm0[j * 2 + 1]); /* Compute the hi 32 bytes */ ymm1[4 + j] = _mm256_unpackhi_epi8(ymm0[j * 2], ymm0[j * 2 + 1]); } /* Shuffle words */ for (j = 0; j < 4; j++) { /* Compute the low 32 bytes */ ymm0[j] = _mm256_unpacklo_epi16(ymm1[j * 2], ymm1[j * 2 + 1]); /* Compute the hi 32 bytes */ ymm0[4 + j] = _mm256_unpackhi_epi16(ymm1[j * 2], ymm1[j * 2 + 1]); } for (j = 0; j < 8; j++) { ymm0[j] = _mm256_permute4x64_epi64(ymm0[j], 0xd8); } /* Shuffle 4-byte dwords */ for (j = 0; j < 4; j++) { /* Compute the low 32 bytes */ ymm1[j] = _mm256_unpacklo_epi32(ymm0[j * 2], ymm0[j * 2 + 1]); /* Compute the hi 32 bytes */ ymm1[4 + j] = _mm256_unpackhi_epi32(ymm0[j * 2], ymm0[j * 2 + 1]); } /* Store the result vectors in proper order */ _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (0 * sizeof(__m256i))), ymm1[0]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (1 * sizeof(__m256i))), ymm1[2]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (2 * sizeof(__m256i))), ymm1[1]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (3 * sizeof(__m256i))), ymm1[3]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (4 * sizeof(__m256i))), ymm1[4]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (5 * sizeof(__m256i))), ymm1[6]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (6 * sizeof(__m256i))), ymm1[5]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (7 * sizeof(__m256i))), ymm1[7]); } } /* Routine optimized for unshuffling a buffer for a type size of 16 bytes. */ static void unshuffle16_avx2(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements) { static const int32_t bytesoftype = 16; int32_t i; int j; __m256i ymm0[16], ymm1[16]; for (i = 0; i < vectorizable_elements; i += sizeof(__m256i)) { /* Fetch 32 elements (512 bytes) into 16 YMM registers. */ const uint8_t* const src_for_ith_element = src + i; for (j = 0; j < 16; j++) { ymm0[j] = _mm256_loadu_si256((__m256i*)(src_for_ith_element + (j * total_elements))); } /* Shuffle bytes */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ ymm1[j] = _mm256_unpacklo_epi8(ymm0[j * 2], ymm0[j * 2 + 1]); /* Compute the hi 32 bytes */ ymm1[8 + j] = _mm256_unpackhi_epi8(ymm0[j * 2], ymm0[j * 2 + 1]); } /* Shuffle 2-byte words */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ ymm0[j] = _mm256_unpacklo_epi16(ymm1[j * 2], ymm1[j * 2 + 1]); /* Compute the hi 32 bytes */ ymm0[8 + j] = _mm256_unpackhi_epi16(ymm1[j * 2], ymm1[j * 2 + 1]); } /* Shuffle 4-byte dwords */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ ymm1[j] = _mm256_unpacklo_epi32(ymm0[j * 2], ymm0[j * 2 + 1]); /* Compute the hi 32 bytes */ ymm1[8 + j] = _mm256_unpackhi_epi32(ymm0[j * 2], ymm0[j * 2 + 1]); } /* Shuffle 8-byte qwords */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ ymm0[j] = _mm256_unpacklo_epi64(ymm1[j * 2], ymm1[j * 2 + 1]); /* Compute the hi 32 bytes */ ymm0[8 + j] = _mm256_unpackhi_epi64(ymm1[j * 2], ymm1[j * 2 + 1]); } for (j = 0; j < 8; j++) { ymm1[j] = _mm256_permute2x128_si256(ymm0[j], ymm0[j + 8], 0x20); ymm1[j + 8] = _mm256_permute2x128_si256(ymm0[j], ymm0[j + 8], 0x31); } /* Store the result vectors in proper order */ _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (0 * sizeof(__m256i))), ymm1[0]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (1 * sizeof(__m256i))), ymm1[4]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (2 * sizeof(__m256i))), ymm1[2]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (3 * sizeof(__m256i))), ymm1[6]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (4 * sizeof(__m256i))), ymm1[1]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (5 * sizeof(__m256i))), ymm1[5]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (6 * sizeof(__m256i))), ymm1[3]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (7 * sizeof(__m256i))), ymm1[7]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (8 * sizeof(__m256i))), ymm1[8]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (9 * sizeof(__m256i))), ymm1[12]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (10 * sizeof(__m256i))), ymm1[10]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (11 * sizeof(__m256i))), ymm1[14]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (12 * sizeof(__m256i))), ymm1[9]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (13 * sizeof(__m256i))), ymm1[13]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (14 * sizeof(__m256i))), ymm1[11]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (15 * sizeof(__m256i))), ymm1[15]); } } /* Routine optimized for unshuffling a buffer for a type size of 12 bytes. * Based off 16-byte implementation*/ static void unshuffle12_avx2(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements) { static const int32_t bytesoftype = 12; int32_t i; int j; __m256i ymm0[16], ymm1[16]; __m256i permute = _mm256_set_epi32(0,0,6,5,4,2,1,0); __m256i store_mask = _mm256_set_epi32(0x0, 0x0, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF); int32_t jump = 2*bytesoftype; for (i = 0; i < vectorizable_elements; i += sizeof(__m256i)) { /* Fetch 24 elements (384 bytes) into 12 YMM registers. */ const uint8_t* const src_for_ith_element = src + i; for (j = 0; j < bytesoftype; j++) { ymm0[j] = _mm256_loadu_si256((__m256i*)(src_for_ith_element + (j * total_elements))); } /* Initialize the last 4 registers (128 bytes) to null */ for (j = bytesoftype; j < 16; j++) { ymm0[j] = _mm256_setzero_si256(); } /* Shuffle bytes */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ ymm1[j] = _mm256_unpacklo_epi8(ymm0[j * 2], ymm0[j * 2 + 1]); /* Compute the hi 32 bytes */ ymm1[8 + j] = _mm256_unpackhi_epi8(ymm0[j * 2], ymm0[j * 2 + 1]); } /* Shuffle 2-byte words */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ ymm0[j] = _mm256_unpacklo_epi16(ymm1[j * 2], ymm1[j * 2 + 1]); /* Compute the hi 32 bytes */ ymm0[8 + j] = _mm256_unpackhi_epi16(ymm1[j * 2], ymm1[j * 2 + 1]); } /* Shuffle 4-byte dwords */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ ymm1[j] = _mm256_unpacklo_epi32(ymm0[j * 2], ymm0[j * 2 + 1]); /* Compute the hi 32 bytes */ ymm1[8 + j] = _mm256_unpackhi_epi32(ymm0[j * 2], ymm0[j * 2 + 1]); } /* Shuffle 8-byte qwords */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ ymm0[j] = _mm256_unpacklo_epi64(ymm1[j * 2], ymm1[j * 2 + 1]); /* Compute the hi 32 bytes */ ymm0[8 + j] = _mm256_unpackhi_epi64(ymm1[j * 2], ymm1[j * 2 + 1]); } for (j = 0; j < 8; j++) { ymm1[j] = _mm256_permute2x128_si256(ymm0[j], ymm0[j + 8], 0x20); ymm1[j + 8] = _mm256_permute2x128_si256(ymm0[j], ymm0[j + 8], 0x31); ymm1[j] = _mm256_permutevar8x32_epi32(ymm1[j], permute); ymm1[j+8] = _mm256_permutevar8x32_epi32(ymm1[j+8], permute); } _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (0 * jump)), ymm1[0]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (1 * jump)), ymm1[4]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (2 * jump)), ymm1[2]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (3 * jump)), ymm1[6]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (4 * jump)), ymm1[1]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (5 * jump)), ymm1[5]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (6 * jump)), ymm1[3]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (7 * jump)), ymm1[7]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (8 * jump)), ymm1[8]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (9 * jump)), ymm1[12]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (10 * jump)), ymm1[10]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (11 * jump)), ymm1[14]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (12 * jump)), ymm1[9]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (13 * jump)), ymm1[13]); _mm256_storeu_si256((__m256i*)(dest + (i * bytesoftype) + (14 * jump)), ymm1[11]); _mm256_maskstore_epi32((int *)(dest + (i * bytesoftype) + (15 * jump)), store_mask, ymm1[15]); } } /* Routine optimized for unshuffling a buffer for a type size larger than 16 bytes. */ static void unshuffle16_tiled_avx2(const uint8_t *dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements, const int32_t bytesoftype) { int32_t i; int j; __m256i ymm0[16], ymm1[16]; const lldiv_t vecs_per_el = lldiv(bytesoftype, sizeof(__m128i)); const int32_t vecs_rem = (int32_t)vecs_per_el.rem; /* The unshuffle loops are inverted (compared to shuffle_tiled16_avx2) to optimize cache utilization. */ int32_t offset_into_type; for (offset_into_type = 0; offset_into_type < bytesoftype; offset_into_type += (offset_into_type == 0 && vecs_rem > 0 ? vecs_rem : (int32_t)sizeof(__m128i))) { for (i = 0; i < vectorizable_elements; i += sizeof(__m256i)) { /* Load the first 16 bytes of 32 adjacent elements (512 bytes) into 16 YMM registers */ const uint8_t* const src_for_ith_element = src + i; for (j = 0; j < 16; j++) { ymm0[j] = _mm256_loadu_si256((__m256i*)(src_for_ith_element + (total_elements * (offset_into_type + j)))); } /* Shuffle bytes */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ ymm1[j] = _mm256_unpacklo_epi8(ymm0[j * 2], ymm0[j * 2 + 1]); /* Compute the hi 32 bytes */ ymm1[8 + j] = _mm256_unpackhi_epi8(ymm0[j * 2], ymm0[j * 2 + 1]); } /* Shuffle 2-byte words */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ ymm0[j] = _mm256_unpacklo_epi16(ymm1[j * 2], ymm1[j * 2 + 1]); /* Compute the hi 32 bytes */ ymm0[8 + j] = _mm256_unpackhi_epi16(ymm1[j * 2], ymm1[j * 2 + 1]); } /* Shuffle 4-byte dwords */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ ymm1[j] = _mm256_unpacklo_epi32(ymm0[j * 2], ymm0[j * 2 + 1]); /* Compute the hi 32 bytes */ ymm1[8 + j] = _mm256_unpackhi_epi32(ymm0[j * 2], ymm0[j * 2 + 1]); } /* Shuffle 8-byte qwords */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ ymm0[j] = _mm256_unpacklo_epi64(ymm1[j * 2], ymm1[j * 2 + 1]); /* Compute the hi 32 bytes */ ymm0[8 + j] = _mm256_unpackhi_epi64(ymm1[j * 2], ymm1[j * 2 + 1]); } for (j = 0; j < 8; j++) { ymm1[j] = _mm256_permute2x128_si256(ymm0[j], ymm0[j + 8], 0x20); ymm1[j + 8] = _mm256_permute2x128_si256(ymm0[j], ymm0[j + 8], 0x31); } /* Store the result vectors in proper order */ const uint8_t* const dest_with_offset = dest + offset_into_type; _mm256_storeu2_m128i( (__m128i*)(dest_with_offset + (i + 0x01) * bytesoftype), (__m128i*)(dest_with_offset + (i + 0x00) * bytesoftype), ymm1[0]); _mm256_storeu2_m128i( (__m128i*)(dest_with_offset + (i + 0x03) * bytesoftype), (__m128i*)(dest_with_offset + (i + 0x02) * bytesoftype), ymm1[4]); _mm256_storeu2_m128i( (__m128i*)(dest_with_offset + (i + 0x05) * bytesoftype), (__m128i*)(dest_with_offset + (i + 0x04) * bytesoftype), ymm1[2]); _mm256_storeu2_m128i( (__m128i*)(dest_with_offset + (i + 0x07) * bytesoftype), (__m128i*)(dest_with_offset + (i + 0x06) * bytesoftype), ymm1[6]); _mm256_storeu2_m128i( (__m128i*)(dest_with_offset + (i + 0x09) * bytesoftype), (__m128i*)(dest_with_offset + (i + 0x08) * bytesoftype), ymm1[1]); _mm256_storeu2_m128i( (__m128i*)(dest_with_offset + (i + 0x0b) * bytesoftype), (__m128i*)(dest_with_offset + (i + 0x0a) * bytesoftype), ymm1[5]); _mm256_storeu2_m128i( (__m128i*)(dest_with_offset + (i + 0x0d) * bytesoftype), (__m128i*)(dest_with_offset + (i + 0x0c) * bytesoftype), ymm1[3]); _mm256_storeu2_m128i( (__m128i*)(dest_with_offset + (i + 0x0f) * bytesoftype), (__m128i*)(dest_with_offset + (i + 0x0e) * bytesoftype), ymm1[7]); _mm256_storeu2_m128i( (__m128i*)(dest_with_offset + (i + 0x11) * bytesoftype), (__m128i*)(dest_with_offset + (i + 0x10) * bytesoftype), ymm1[8]); _mm256_storeu2_m128i( (__m128i*)(dest_with_offset + (i + 0x13) * bytesoftype), (__m128i*)(dest_with_offset + (i + 0x12) * bytesoftype), ymm1[12]); _mm256_storeu2_m128i( (__m128i*)(dest_with_offset + (i + 0x15) * bytesoftype), (__m128i*)(dest_with_offset + (i + 0x14) * bytesoftype), ymm1[10]); _mm256_storeu2_m128i( (__m128i*)(dest_with_offset + (i + 0x17) * bytesoftype), (__m128i*)(dest_with_offset + (i + 0x16) * bytesoftype), ymm1[14]); _mm256_storeu2_m128i( (__m128i*)(dest_with_offset + (i + 0x19) * bytesoftype), (__m128i*)(dest_with_offset + (i + 0x18) * bytesoftype), ymm1[9]); _mm256_storeu2_m128i( (__m128i*)(dest_with_offset + (i + 0x1b) * bytesoftype), (__m128i*)(dest_with_offset + (i + 0x1a) * bytesoftype), ymm1[13]); _mm256_storeu2_m128i( (__m128i*)(dest_with_offset + (i + 0x1d) * bytesoftype), (__m128i*)(dest_with_offset + (i + 0x1c) * bytesoftype), ymm1[11]); _mm256_storeu2_m128i( (__m128i*)(dest_with_offset + (i + 0x1f) * bytesoftype), (__m128i*)(dest_with_offset + (i + 0x1e) * bytesoftype), ymm1[15]); } } } /* Shuffle a block. This can never fail. */ void shuffle_avx2(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest) { const int32_t vectorized_chunk_size = bytesoftype * (int32_t)sizeof(__m256i); /* If the block size is too small to be vectorized, use the generic implementation. */ if (blocksize < vectorized_chunk_size) { shuffle_generic(bytesoftype, blocksize, _src, _dest); return; } /* If the blocksize is not a multiple of both the typesize and the vector size, round the blocksize down to the next value which is a multiple of both. The vectorized shuffle can be used for that portion of the data, and the naive implementation can be used for the remaining portion. */ const int32_t vectorizable_bytes = blocksize - (blocksize % vectorized_chunk_size); const int32_t vectorizable_elements = vectorizable_bytes / bytesoftype; const int32_t total_elements = blocksize / bytesoftype; /* Optimized shuffle implementations */ switch (bytesoftype) { case 2: shuffle2_avx2(_dest, _src, vectorizable_elements, total_elements); break; case 4: shuffle4_avx2(_dest, _src, vectorizable_elements, total_elements); break; case 8: shuffle8_avx2(_dest, _src, vectorizable_elements, total_elements); break; case 16: shuffle16_avx2(_dest, _src, vectorizable_elements, total_elements); break; default: /* For types larger than 16 bytes, use the AVX2 tiled shuffle. */ if (bytesoftype > (int32_t)sizeof(__m128i)) { shuffle16_tiled_avx2(_dest, _src, vectorizable_elements, total_elements, bytesoftype); } else { /* Non-optimized shuffle */ shuffle_generic(bytesoftype, blocksize, _src, _dest); /* The non-optimized function covers the whole buffer, so we're done processing here. */ return; } } /* If the buffer had any bytes at the end which couldn't be handled by the vectorized implementations, use the non-optimized version to finish them up. */ if (vectorizable_bytes < blocksize) { shuffle_generic_inline(bytesoftype, vectorizable_bytes, blocksize, _src, _dest); } } /* Unshuffle a block. This can never fail. */ void unshuffle_avx2(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest) { const int32_t vectorized_chunk_size = bytesoftype * (int32_t)sizeof(__m256i); /* If the block size is too small to be vectorized, use the generic implementation. */ if (blocksize < vectorized_chunk_size) { unshuffle_generic(bytesoftype, blocksize, _src, _dest); return; } /* If the blocksize is not a multiple of both the typesize and the vector size, round the blocksize down to the next value which is a multiple of both. The vectorized unshuffle can be used for that portion of the data, and the naive implementation can be used for the remaining portion. */ const int32_t vectorizable_bytes = blocksize - (blocksize % vectorized_chunk_size); const int32_t vectorizable_elements = vectorizable_bytes / bytesoftype; const int32_t total_elements = blocksize / bytesoftype; /* Optimized unshuffle implementations */ switch (bytesoftype) { case 2: unshuffle2_avx2(_dest, _src, vectorizable_elements, total_elements); break; case 4: unshuffle4_avx2(_dest, _src, vectorizable_elements, total_elements); break; case 8: unshuffle8_avx2(_dest, _src, vectorizable_elements, total_elements); break; case 12: unshuffle12_avx2(_dest, _src, vectorizable_elements, total_elements); break; case 16: unshuffle16_avx2(_dest, _src, vectorizable_elements, total_elements); break; default: /* For types larger than 16 bytes, use the AVX2 tiled unshuffle. */ if (bytesoftype > (int32_t)sizeof(__m128i)) { unshuffle16_tiled_avx2(_dest, _src, vectorizable_elements, total_elements, bytesoftype); } else { /* Non-optimized unshuffle */ unshuffle_generic(bytesoftype, blocksize, _src, _dest); /* The non-optimized function covers the whole buffer, so we're done processing here. */ return; } } /* If the buffer had any bytes at the end which couldn't be handled by the vectorized implementations, use the non-optimized version to finish them up. */ if (vectorizable_bytes < blocksize) { unshuffle_generic_inline(bytesoftype, vectorizable_bytes, blocksize, _src, _dest); } } const bool is_shuffle_avx2 = true; #else const bool is_shuffle_avx2 = false; void shuffle_avx2(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest) { abort(); } void unshuffle_avx2(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest) { abort(); } #endif /* defined(__AVX2__) */ c-blosc2-3.1.5/blosc/shuffle-avx2.h000066400000000000000000000021141521743436100167150ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /* AVX2-accelerated shuffle/unshuffle routines. */ #ifndef SHUFFLE_AVX2_H #define SHUFFLE_AVX2_H #include "blosc2/blosc2-common.h" #include #include /** * AVX2-accelerated (un)shuffle routines availability. */ extern const bool is_shuffle_avx2; /** AVX2-accelerated shuffle routine. */ BLOSC_NO_EXPORT void shuffle_avx2(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest); /** AVX2-accelerated unshuffle routine. */ BLOSC_NO_EXPORT void unshuffle_avx2(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest); #endif /* SHUFFLE_AVX2_H */ c-blosc2-3.1.5/blosc/shuffle-generic.c000066400000000000000000000017251521743436100174530ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #include "shuffle-generic.h" /* Shuffle a block. This can never fail. */ void shuffle_generic(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest) { /* Non-optimized shuffle */ shuffle_generic_inline(bytesoftype, 0, blocksize, _src, _dest); } /* Unshuffle a block. This can never fail. */ void unshuffle_generic(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest) { /* Non-optimized unshuffle */ unshuffle_generic_inline(bytesoftype, 0, blocksize, _src, _dest); } c-blosc2-3.1.5/blosc/shuffle-generic.h000066400000000000000000000074361521743436100174650ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /********************************************************************* Generic (non-hardware-accelerated) shuffle/unshuffle routines. These are used when hardware-accelerated functions aren't available for a particular platform; they are also used by the hardware- accelerated functions to handle any remaining elements in a block which isn't a multiple of the hardware's vector size. **********************************************************************/ #ifndef BLOSC_SHUFFLE_GENERIC_H #define BLOSC_SHUFFLE_GENERIC_H #include "blosc2/blosc2-common.h" #include #include /** Generic (non-hardware-accelerated) shuffle routine. This is the pure element-copying nested loop. It is used by the generic shuffle implementation and also by the vectorized shuffle implementations to process any remaining elements in a block which is not a multiple of (type_size * vector_size). */ static inline void shuffle_generic_inline(const int32_t type_size, const int32_t vectorizable_blocksize, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest) { int32_t i, j; /* Calculate the number of elements in the block. */ const int32_t neblock_quot = blocksize / type_size; const int32_t neblock_rem = blocksize % type_size; const int32_t vectorizable_elements = vectorizable_blocksize / type_size; /* Non-optimized shuffle */ for (j = 0; j < type_size; j++) { for (i = vectorizable_elements; i < (int32_t)neblock_quot; i++) { _dest[j * neblock_quot + i] = _src[i * type_size + j]; } } /* Copy any leftover bytes in the block without shuffling them. */ memcpy(_dest + (blocksize - neblock_rem), _src + (blocksize - neblock_rem), neblock_rem); } /** Generic (non-hardware-accelerated) unshuffle routine. This is the pure element-copying nested loop. It is used by the generic unshuffle implementation and also by the vectorized unshuffle implementations to process any remaining elements in a block which is not a multiple of (type_size * vector_size). */ static inline void unshuffle_generic_inline(const int32_t type_size, const int32_t vectorizable_blocksize, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest) { int32_t i, j; /* Calculate the number of elements in the block. */ const int32_t neblock_quot = blocksize / type_size; const int32_t neblock_rem = blocksize % type_size; const int32_t vectorizable_elements = vectorizable_blocksize / type_size; /* Non-optimized unshuffle */ for (i = vectorizable_elements; i < (int32_t)neblock_quot; i++) { for (j = 0; j < type_size; j++) { _dest[i * type_size + j] = _src[j * neblock_quot + i]; } } /* Copy any leftover bytes in the block without unshuffling them. */ memcpy(_dest + (blocksize - neblock_rem), _src + (blocksize - neblock_rem), neblock_rem); } /** Generic (non-hardware-accelerated) shuffle routine. */ BLOSC_NO_EXPORT void shuffle_generic(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest); /** Generic (non-hardware-accelerated) unshuffle routine. */ BLOSC_NO_EXPORT void unshuffle_generic(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest); #endif /* BLOSC_SHUFFLE_GENERIC_H */ c-blosc2-3.1.5/blosc/shuffle-neon.c000066400000000000000000000536301521743436100170000ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Lucian Marc https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #include "shuffle-neon.h" #include "shuffle-generic.h" #include /* Make sure NEON is available for the compilation target and compiler. */ #if defined(__ARM_NEON) #include #include /* The next is useful for debugging purposes */ #if 0 #include #include static void printmem(uint8_t* buf) { printf("%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); } #endif /* Routine optimized for shuffling a buffer for a type size of 2 bytes. */ static void shuffle2_neon(uint8_t *const dest, const uint8_t *const src, const size_t vectorizable_elements, const size_t total_elements) { size_t i, k; static const size_t bytesoftype = 2; uint8x16x2_t r0; for (i = 0, k = 0; i < vectorizable_elements * bytesoftype; i += 32, k++) { /* Load (and permute) 32 bytes to the structure r0 */ r0 = vld2q_u8(src + i); /* Store the results in the destination vector */ vst1q_u8(dest + total_elements * 0 + k * 16, r0.val[0]); vst1q_u8(dest + total_elements * 1 + k * 16, r0.val[1]); } } /* Routine optimized for shuffling a buffer for a type size of 4 bytes. */ static void shuffle4_neon(uint8_t *const dest, const uint8_t *const src, const size_t vectorizable_elements, const size_t total_elements) { size_t i, k; static const size_t bytesoftype = 4; uint8x16x4_t r0; for (i = 0, k = 0; i < vectorizable_elements * bytesoftype; i += 64, k++) { /* Load (and permute) 64 bytes to the structure r0 */ r0 = vld4q_u8(src + i); /* Store the results in the destination vector */ vst1q_u8(dest + total_elements * 0 + k * 16, r0.val[0]); vst1q_u8(dest + total_elements * 1 + k * 16, r0.val[1]); vst1q_u8(dest + total_elements * 2 + k * 16, r0.val[2]); vst1q_u8(dest + total_elements * 3 + k * 16, r0.val[3]); } } /* Routine optimized for shuffling a buffer for a type size of 8 bytes. */ static void shuffle8_neon(uint8_t *const dest, const uint8_t *const src, const size_t vectorizable_elements, const size_t total_elements) { size_t i, k; static const size_t bytesoftype = 8; uint8x8x2_t r0[4]; uint16x4x2_t r1[4]; uint32x2x2_t r2[4]; for (i = 0, k = 0; i < vectorizable_elements * bytesoftype; i += 64, k++) { /* Load and interleave groups of 8 bytes (64 bytes) to the structure r0 */ r0[0] = vzip_u8(vld1_u8(src + i + 0 * 8), vld1_u8(src + i + 1 * 8)); r0[1] = vzip_u8(vld1_u8(src + i + 2 * 8), vld1_u8(src + i + 3 * 8)); r0[2] = vzip_u8(vld1_u8(src + i + 4 * 8), vld1_u8(src + i + 5 * 8)); r0[3] = vzip_u8(vld1_u8(src + i + 6 * 8), vld1_u8(src + i + 7 * 8)); /* Interleave 16 bytes */ r1[0] = vzip_u16(vreinterpret_u16_u8(r0[0].val[0]), vreinterpret_u16_u8(r0[1].val[0])); r1[1] = vzip_u16(vreinterpret_u16_u8(r0[0].val[1]), vreinterpret_u16_u8(r0[1].val[1])); r1[2] = vzip_u16(vreinterpret_u16_u8(r0[2].val[0]), vreinterpret_u16_u8(r0[3].val[0])); r1[3] = vzip_u16(vreinterpret_u16_u8(r0[2].val[1]), vreinterpret_u16_u8(r0[3].val[1])); /* Interleave 32 bytes */ r2[0] = vzip_u32(vreinterpret_u32_u16(r1[0].val[0]), vreinterpret_u32_u16(r1[2].val[0])); r2[1] = vzip_u32(vreinterpret_u32_u16(r1[0].val[1]), vreinterpret_u32_u16(r1[2].val[1])); r2[2] = vzip_u32(vreinterpret_u32_u16(r1[1].val[0]), vreinterpret_u32_u16(r1[3].val[0])); r2[3] = vzip_u32(vreinterpret_u32_u16(r1[1].val[1]), vreinterpret_u32_u16(r1[3].val[1])); /* Store the results in the destination vector */ vst1_u8(dest + k * 8 + 0 * total_elements, vreinterpret_u8_u32(r2[0].val[0])); vst1_u8(dest + k * 8 + 1 * total_elements, vreinterpret_u8_u32(r2[0].val[1])); vst1_u8(dest + k * 8 + 2 * total_elements, vreinterpret_u8_u32(r2[1].val[0])); vst1_u8(dest + k * 8 + 3 * total_elements, vreinterpret_u8_u32(r2[1].val[1])); vst1_u8(dest + k * 8 + 4 * total_elements, vreinterpret_u8_u32(r2[2].val[0])); vst1_u8(dest + k * 8 + 5 * total_elements, vreinterpret_u8_u32(r2[2].val[1])); vst1_u8(dest + k * 8 + 6 * total_elements, vreinterpret_u8_u32(r2[3].val[0])); vst1_u8(dest + k * 8 + 7 * total_elements, vreinterpret_u8_u32(r2[3].val[1])); } } /* Routine optimized for shuffling a buffer for a type size of 16 bytes. */ static void shuffle16_neon(uint8_t *const dest, const uint8_t *const src, const size_t vectorizable_elements, const size_t total_elements) { size_t i, k; static const size_t bytesoftype = 16; uint8x8x2_t r0[8]; uint16x4x2_t r1[8]; uint32x2x2_t r2[8]; for (i = 0, k = 0; i < vectorizable_elements * bytesoftype; i += 128, k++) { /* Load and interleave groups of 16 bytes (128 bytes) to the structure r0 */ r0[0] = vzip_u8(vld1_u8(src + i + 0 * 8), vld1_u8(src + i + 2 * 8)); r0[1] = vzip_u8(vld1_u8(src + i + 1 * 8), vld1_u8(src + i + 3 * 8)); r0[2] = vzip_u8(vld1_u8(src + i + 4 * 8), vld1_u8(src + i + 6 * 8)); r0[3] = vzip_u8(vld1_u8(src + i + 5 * 8), vld1_u8(src + i + 7 * 8)); r0[4] = vzip_u8(vld1_u8(src + i + 8 * 8), vld1_u8(src + i + 10 * 8)); r0[5] = vzip_u8(vld1_u8(src + i + 9 * 8), vld1_u8(src + i + 11 * 8)); r0[6] = vzip_u8(vld1_u8(src + i + 12 * 8), vld1_u8(src + i + 14 * 8)); r0[7] = vzip_u8(vld1_u8(src + i + 13 * 8), vld1_u8(src + i + 15 * 8)); /* Interleave 16 bytes */ r1[0] = vzip_u16(vreinterpret_u16_u8(r0[0].val[0]), vreinterpret_u16_u8(r0[2].val[0])); r1[1] = vzip_u16(vreinterpret_u16_u8(r0[0].val[1]), vreinterpret_u16_u8(r0[2].val[1])); r1[2] = vzip_u16(vreinterpret_u16_u8(r0[1].val[0]), vreinterpret_u16_u8(r0[3].val[0])); r1[3] = vzip_u16(vreinterpret_u16_u8(r0[1].val[1]), vreinterpret_u16_u8(r0[3].val[1])); r1[4] = vzip_u16(vreinterpret_u16_u8(r0[4].val[0]), vreinterpret_u16_u8(r0[6].val[0])); r1[5] = vzip_u16(vreinterpret_u16_u8(r0[4].val[1]), vreinterpret_u16_u8(r0[6].val[1])); r1[6] = vzip_u16(vreinterpret_u16_u8(r0[5].val[0]), vreinterpret_u16_u8(r0[7].val[0])); r1[7] = vzip_u16(vreinterpret_u16_u8(r0[5].val[1]), vreinterpret_u16_u8(r0[7].val[1])); /* Interleave 32 bytes */ r2[0] = vzip_u32(vreinterpret_u32_u16(r1[0].val[0]), vreinterpret_u32_u16(r1[4].val[0])); r2[1] = vzip_u32(vreinterpret_u32_u16(r1[0].val[1]), vreinterpret_u32_u16(r1[4].val[1])); r2[2] = vzip_u32(vreinterpret_u32_u16(r1[1].val[0]), vreinterpret_u32_u16(r1[5].val[0])); r2[3] = vzip_u32(vreinterpret_u32_u16(r1[1].val[1]), vreinterpret_u32_u16(r1[5].val[1])); r2[4] = vzip_u32(vreinterpret_u32_u16(r1[2].val[0]), vreinterpret_u32_u16(r1[6].val[0])); r2[5] = vzip_u32(vreinterpret_u32_u16(r1[2].val[1]), vreinterpret_u32_u16(r1[6].val[1])); r2[6] = vzip_u32(vreinterpret_u32_u16(r1[3].val[0]), vreinterpret_u32_u16(r1[7].val[0])); r2[7] = vzip_u32(vreinterpret_u32_u16(r1[3].val[1]), vreinterpret_u32_u16(r1[7].val[1])); /* Store the results to the destination vector */ vst1_u8(dest + k * 8 + 0 * total_elements, vreinterpret_u8_u32(r2[0].val[0])); vst1_u8(dest + k * 8 + 1 * total_elements, vreinterpret_u8_u32(r2[0].val[1])); vst1_u8(dest + k * 8 + 2 * total_elements, vreinterpret_u8_u32(r2[1].val[0])); vst1_u8(dest + k * 8 + 3 * total_elements, vreinterpret_u8_u32(r2[1].val[1])); vst1_u8(dest + k * 8 + 4 * total_elements, vreinterpret_u8_u32(r2[2].val[0])); vst1_u8(dest + k * 8 + 5 * total_elements, vreinterpret_u8_u32(r2[2].val[1])); vst1_u8(dest + k * 8 + 6 * total_elements, vreinterpret_u8_u32(r2[3].val[0])); vst1_u8(dest + k * 8 + 7 * total_elements, vreinterpret_u8_u32(r2[3].val[1])); vst1_u8(dest + k * 8 + 8 * total_elements, vreinterpret_u8_u32(r2[4].val[0])); vst1_u8(dest + k * 8 + 9 * total_elements, vreinterpret_u8_u32(r2[4].val[1])); vst1_u8(dest + k * 8 + 10 * total_elements, vreinterpret_u8_u32(r2[5].val[0])); vst1_u8(dest + k * 8 + 11 * total_elements, vreinterpret_u8_u32(r2[5].val[1])); vst1_u8(dest + k * 8 + 12 * total_elements, vreinterpret_u8_u32(r2[6].val[0])); vst1_u8(dest + k * 8 + 13 * total_elements, vreinterpret_u8_u32(r2[6].val[1])); vst1_u8(dest + k * 8 + 14 * total_elements, vreinterpret_u8_u32(r2[7].val[0])); vst1_u8(dest + k * 8 + 15 * total_elements, vreinterpret_u8_u32(r2[7].val[1])); } } /* Routine optimized for unshuffling a buffer for a type size of 2 bytes. */ static void unshuffle2_neon(uint8_t *const dest, const uint8_t *const src, const size_t vectorizable_elements, const size_t total_elements) { size_t i, k; static const size_t bytesoftype = 2; uint8x16x2_t r0; for (i = 0, k = 0; i < vectorizable_elements * bytesoftype; i += 32, k++) { /* Load 32 bytes to the structure r0 */ r0.val[0] = vld1q_u8(src + total_elements * 0 + k * 16); r0.val[1] = vld1q_u8(src + total_elements * 1 + k * 16); /* Store (with permutation) the results in the destination vector */ vst2q_u8(dest + k * 32, r0); } } /* Routine optimized for unshuffling a buffer for a type size of 4 bytes. */ static void unshuffle4_neon(uint8_t *const dest, const uint8_t *const src, const size_t vectorizable_elements, const size_t total_elements) { size_t i, k; static const size_t bytesoftype = 4; uint8x16x4_t r0; for (i = 0, k = 0; i < vectorizable_elements * bytesoftype; i += 64, k++) { /* load 64 bytes to the structure r0 */ r0.val[0] = vld1q_u8(src + total_elements * 0 + k * 16); r0.val[1] = vld1q_u8(src + total_elements * 1 + k * 16); r0.val[2] = vld1q_u8(src + total_elements * 2 + k * 16); r0.val[3] = vld1q_u8(src + total_elements * 3 + k * 16); /* Store (with permutation) the results in the destination vector */ vst4q_u8(dest + k * 64, r0); } } /* Routine optimized for unshuffling a buffer for a type size of 8 bytes. */ static void unshuffle8_neon(uint8_t *const dest, const uint8_t *const src, const size_t vectorizable_elements, const size_t total_elements) { size_t i, k; static const size_t bytesoftype = 8; uint8x8x2_t r0[4]; uint16x4x2_t r1[4]; uint32x2x2_t r2[4]; for (i = 0, k = 0; i < vectorizable_elements * bytesoftype; i += 64, k++) { /* Load and interleave groups of 8 bytes (64 bytes) to the structure r0 */ r0[0] = vzip_u8(vld1_u8(src + 0 * total_elements + k * 8), vld1_u8(src + 1 * total_elements + k * 8)); r0[1] = vzip_u8(vld1_u8(src + 2 * total_elements + k * 8), vld1_u8(src + 3 * total_elements + k * 8)); r0[2] = vzip_u8(vld1_u8(src + 4 * total_elements + k * 8), vld1_u8(src + 5 * total_elements + k * 8)); r0[3] = vzip_u8(vld1_u8(src + 6 * total_elements + k * 8), vld1_u8(src + 7 * total_elements + k * 8)); /* Interleave 16 bytes */ r1[0] = vzip_u16(vreinterpret_u16_u8(r0[0].val[0]), vreinterpret_u16_u8(r0[1].val[0])); r1[1] = vzip_u16(vreinterpret_u16_u8(r0[0].val[1]), vreinterpret_u16_u8(r0[1].val[1])); r1[2] = vzip_u16(vreinterpret_u16_u8(r0[2].val[0]), vreinterpret_u16_u8(r0[3].val[0])); r1[3] = vzip_u16(vreinterpret_u16_u8(r0[2].val[1]), vreinterpret_u16_u8(r0[3].val[1])); /* Interleave 32 bytes */ r2[0] = vzip_u32(vreinterpret_u32_u16(r1[0].val[0]), vreinterpret_u32_u16(r1[2].val[0])); r2[1] = vzip_u32(vreinterpret_u32_u16(r1[0].val[1]), vreinterpret_u32_u16(r1[2].val[1])); r2[2] = vzip_u32(vreinterpret_u32_u16(r1[1].val[0]), vreinterpret_u32_u16(r1[3].val[0])); r2[3] = vzip_u32(vreinterpret_u32_u16(r1[1].val[1]), vreinterpret_u32_u16(r1[3].val[1])); /* Store the results in the destination vector */ vst1_u8(dest + i + 0 * 8, vreinterpret_u8_u32(r2[0].val[0])); vst1_u8(dest + i + 1 * 8, vreinterpret_u8_u32(r2[0].val[1])); vst1_u8(dest + i + 2 * 8, vreinterpret_u8_u32(r2[1].val[0])); vst1_u8(dest + i + 3 * 8, vreinterpret_u8_u32(r2[1].val[1])); vst1_u8(dest + i + 4 * 8, vreinterpret_u8_u32(r2[2].val[0])); vst1_u8(dest + i + 5 * 8, vreinterpret_u8_u32(r2[2].val[1])); vst1_u8(dest + i + 6 * 8, vreinterpret_u8_u32(r2[3].val[0])); vst1_u8(dest + i + 7 * 8, vreinterpret_u8_u32(r2[3].val[1])); } } /* Routine optimized for unshuffling a buffer for a type size of 16 bytes. */ static void unshuffle16_neon(uint8_t *const dest, const uint8_t *const src, const size_t vectorizable_elements, const size_t total_elements) { size_t i, k; static const size_t bytesoftype = 16; uint8x8x2_t r0[8]; uint16x4x2_t r1[8]; uint32x2x2_t r2[8]; for (i = 0, k = 0; i < vectorizable_elements * bytesoftype; i += 128, k++) { /* Load and interleave groups of 16 bytes (128 bytes) to the structure r0*/ r0[0] = vzip_u8(vld1_u8(src + k * 8 + 0 * total_elements), vld1_u8(src + k * 8 + 1 * total_elements)); r0[1] = vzip_u8(vld1_u8(src + k * 8 + 2 * total_elements), vld1_u8(src + k * 8 + 3 * total_elements)); r0[2] = vzip_u8(vld1_u8(src + k * 8 + 4 * total_elements), vld1_u8(src + k * 8 + 5 * total_elements)); r0[3] = vzip_u8(vld1_u8(src + k * 8 + 6 * total_elements), vld1_u8(src + k * 8 + 7 * total_elements)); r0[4] = vzip_u8(vld1_u8(src + k * 8 + 8 * total_elements), vld1_u8(src + k * 8 + 9 * total_elements)); r0[5] = vzip_u8(vld1_u8(src + k * 8 + 10 * total_elements), vld1_u8(src + k * 8 + 11 * total_elements)); r0[6] = vzip_u8(vld1_u8(src + k * 8 + 12 * total_elements), vld1_u8(src + k * 8 + 13 * total_elements)); r0[7] = vzip_u8(vld1_u8(src + k * 8 + 14 * total_elements), vld1_u8(src + k * 8 + 15 * total_elements)); /* Interleave 16 bytes */ r1[0] = vzip_u16(vreinterpret_u16_u8(r0[0].val[0]), vreinterpret_u16_u8(r0[1].val[0])); r1[1] = vzip_u16(vreinterpret_u16_u8(r0[0].val[1]), vreinterpret_u16_u8(r0[1].val[1])); r1[2] = vzip_u16(vreinterpret_u16_u8(r0[2].val[0]), vreinterpret_u16_u8(r0[3].val[0])); r1[3] = vzip_u16(vreinterpret_u16_u8(r0[2].val[1]), vreinterpret_u16_u8(r0[3].val[1])); r1[4] = vzip_u16(vreinterpret_u16_u8(r0[4].val[0]), vreinterpret_u16_u8(r0[5].val[0])); r1[5] = vzip_u16(vreinterpret_u16_u8(r0[4].val[1]), vreinterpret_u16_u8(r0[5].val[1])); r1[6] = vzip_u16(vreinterpret_u16_u8(r0[6].val[0]), vreinterpret_u16_u8(r0[7].val[0])); r1[7] = vzip_u16(vreinterpret_u16_u8(r0[6].val[1]), vreinterpret_u16_u8(r0[7].val[1])); /* Interleave 32 bytes */ r2[0] = vzip_u32(vreinterpret_u32_u16(r1[0].val[0]), vreinterpret_u32_u16(r1[2].val[0])); r2[1] = vzip_u32(vreinterpret_u32_u16(r1[0].val[1]), vreinterpret_u32_u16(r1[2].val[1])); r2[2] = vzip_u32(vreinterpret_u32_u16(r1[1].val[0]), vreinterpret_u32_u16(r1[3].val[0])); r2[3] = vzip_u32(vreinterpret_u32_u16(r1[1].val[1]), vreinterpret_u32_u16(r1[3].val[1])); r2[4] = vzip_u32(vreinterpret_u32_u16(r1[4].val[0]), vreinterpret_u32_u16(r1[6].val[0])); r2[5] = vzip_u32(vreinterpret_u32_u16(r1[4].val[1]), vreinterpret_u32_u16(r1[6].val[1])); r2[6] = vzip_u32(vreinterpret_u32_u16(r1[5].val[0]), vreinterpret_u32_u16(r1[7].val[0])); r2[7] = vzip_u32(vreinterpret_u32_u16(r1[5].val[1]), vreinterpret_u32_u16(r1[7].val[1])); /* Store the results in the destination vector */ vst1_u8(dest + i + 0 * 8, vreinterpret_u8_u32(r2[0].val[0])); vst1_u8(dest + i + 1 * 8, vreinterpret_u8_u32(r2[4].val[0])); vst1_u8(dest + i + 2 * 8, vreinterpret_u8_u32(r2[0].val[1])); vst1_u8(dest + i + 3 * 8, vreinterpret_u8_u32(r2[4].val[1])); vst1_u8(dest + i + 4 * 8, vreinterpret_u8_u32(r2[1].val[0])); vst1_u8(dest + i + 5 * 8, vreinterpret_u8_u32(r2[5].val[0])); vst1_u8(dest + i + 6 * 8, vreinterpret_u8_u32(r2[1].val[1])); vst1_u8(dest + i + 7 * 8, vreinterpret_u8_u32(r2[5].val[1])); vst1_u8(dest + i + 8 * 8, vreinterpret_u8_u32(r2[2].val[0])); vst1_u8(dest + i + 9 * 8, vreinterpret_u8_u32(r2[6].val[0])); vst1_u8(dest + i + 10 * 8, vreinterpret_u8_u32(r2[2].val[1])); vst1_u8(dest + i + 11 * 8, vreinterpret_u8_u32(r2[6].val[1])); vst1_u8(dest + i + 12 * 8, vreinterpret_u8_u32(r2[3].val[0])); vst1_u8(dest + i + 13 * 8, vreinterpret_u8_u32(r2[7].val[0])); vst1_u8(dest + i + 14 * 8, vreinterpret_u8_u32(r2[3].val[1])); vst1_u8(dest + i + 15 * 8, vreinterpret_u8_u32(r2[7].val[1])); } } /* Shuffle a block. This can never fail. */ void shuffle_neon(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *const _src, uint8_t *_dest) { int32_t vectorized_chunk_size = 1; if (bytesoftype == 2 || bytesoftype == 4) { vectorized_chunk_size = bytesoftype * 16; } else if (bytesoftype == 8 || bytesoftype == 16) { vectorized_chunk_size = bytesoftype * 8; } /* If the blocksize is not a multiple of both the typesize and the vector size, round the blocksize down to the next value which is a multiple of both. The vectorized shuffle can be used for that portion of the data, and the naive implementation can be used for the remaining portion. */ const int32_t vectorizable_bytes = blocksize - (blocksize % vectorized_chunk_size); const int32_t vectorizable_elements = vectorizable_bytes / bytesoftype; const int32_t total_elements = blocksize / bytesoftype; /* If the block size is too small to be vectorized, use the generic implementation. */ if (blocksize < vectorized_chunk_size) { shuffle_generic(bytesoftype, blocksize, _src, _dest); return; } /* Optimized shuffle implementations */ switch (bytesoftype) { case 2: shuffle2_neon(_dest, _src, vectorizable_elements, total_elements); break; case 4: shuffle4_neon(_dest, _src, vectorizable_elements, total_elements); break; case 8: shuffle8_neon(_dest, _src, vectorizable_elements, total_elements); break; case 16: shuffle16_neon(_dest, _src, vectorizable_elements, total_elements); break; default: /* Non-optimized shuffle */ shuffle_generic(bytesoftype, blocksize, _src, _dest); /* The non-optimized function covers the whole buffer, so we're done processing here. */ return; } /* If the buffer had any bytes at the end which couldn't be handled by the vectorized implementations, use the non-optimized version to finish them up. */ if (vectorizable_bytes < blocksize) { shuffle_generic_inline(bytesoftype, vectorizable_bytes, blocksize, _src, _dest); } } /* Unshuffle a block. This can never fail. */ void unshuffle_neon(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *const _src, uint8_t *_dest) { int32_t vectorized_chunk_size = 1; if (bytesoftype == 2 || bytesoftype == 4) { vectorized_chunk_size = bytesoftype * 16; } else if (bytesoftype == 8 || bytesoftype == 16) { vectorized_chunk_size = bytesoftype * 8; } /* If the blocksize is not a multiple of both the typesize and the vector size, round the blocksize down to the next value which is a multiple of both. The vectorized unshuffle can be used for that portion of the data, and the naive implementation can be used for the remaining portion. */ const int32_t vectorizable_bytes = blocksize - (blocksize % vectorized_chunk_size); const int32_t vectorizable_elements = vectorizable_bytes / bytesoftype; const int32_t total_elements = blocksize / bytesoftype; /* If the block size is too small to be vectorized, use the generic implementation. */ if (blocksize < vectorized_chunk_size) { unshuffle_generic(bytesoftype, blocksize, _src, _dest); return; } /* Optimized unshuffle implementations */ switch (bytesoftype) { case 2: unshuffle2_neon(_dest, _src, vectorizable_elements, total_elements); break; case 4: unshuffle4_neon(_dest, _src, vectorizable_elements, total_elements); break; case 8: unshuffle8_neon(_dest, _src, vectorizable_elements, total_elements); break; case 16: unshuffle16_neon(_dest, _src, vectorizable_elements, total_elements); break; default: /* Non-optimized unshuffle */ unshuffle_generic(bytesoftype, blocksize, _src, _dest); /* The non-optimized function covers the whole buffer, so we're done processing here. */ return; } /* If the buffer had any bytes at the end which couldn't be handled by the vectorized implementations, use the non-optimized version to finish them up. */ if (vectorizable_bytes < blocksize) { unshuffle_generic_inline(bytesoftype, vectorizable_bytes, blocksize, _src, _dest); } } const bool is_shuffle_neon = true; #else /* defined(__ARM_NEON) */ const bool is_shuffle_neon = false; void shuffle_neon(const int32_t bytesoftype, const int32_t blocksize, const uint8_t* const _src, uint8_t* const _dest) { abort(); } void unshuffle_neon(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest) { abort(); } #endif /* defined(__ARM_NEON) */ c-blosc2-3.1.5/blosc/shuffle-neon.h000066400000000000000000000022241521743436100167760ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Note: Adapted for NEON by Lucian Marc. See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /* NEON-accelerated shuffle/unshuffle routines. */ #ifndef BLOSC_SHUFFLE_NEON_H #define BLOSC_SHUFFLE_NEON_H #include "blosc2/blosc2-common.h" #include #include /** * NEON-accelerated (un)shuffle routines availability. */ extern const bool is_shuffle_neon; /** NEON-accelerated shuffle routine. */ BLOSC_NO_EXPORT void shuffle_neon(const int32_t bytesoftype, const int32_t blocksize, const uint8_t* const _src, uint8_t* const _dest); /** NEON-accelerated unshuffle routine. */ BLOSC_NO_EXPORT void unshuffle_neon(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest); #endif /* BLOSC_SHUFFLE_NEON_H */ c-blosc2-3.1.5/blosc/shuffle-sse2.c000066400000000000000000000726051521743436100167200ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #include "shuffle-sse2.h" #include "shuffle-generic.h" #include /* Make sure SSE2 is available for the compilation target and compiler. */ #if defined(__SSE2__) #include #include /* The next is useful for debugging purposes */ #if 0 #include #include static void printxmm(__m128i xmm0) { uint8_t buf[16]; ((__m128i *)buf)[0] = xmm0; printf("%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); } #endif /* Routine optimized for shuffling a buffer for a type size of 2 bytes. */ static void shuffle2_sse2(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements) { static const int32_t bytesoftype = 2; int32_t j; int k; uint8_t* dest_for_jth_element; __m128i xmm0[2], xmm1[2]; for (j = 0; j < vectorizable_elements; j += sizeof(__m128i)) { /* Fetch 16 elements (32 bytes) then transpose bytes, words and double words. */ for (k = 0; k < 2; k++) { xmm0[k] = _mm_loadu_si128((__m128i*)(src + (j * bytesoftype) + (k * sizeof(__m128i)))); xmm0[k] = _mm_shufflelo_epi16(xmm0[k], 0xd8); xmm0[k] = _mm_shufflehi_epi16(xmm0[k], 0xd8); xmm0[k] = _mm_shuffle_epi32(xmm0[k], 0xd8); xmm1[k] = _mm_shuffle_epi32(xmm0[k], 0x4e); xmm0[k] = _mm_unpacklo_epi8(xmm0[k], xmm1[k]); xmm0[k] = _mm_shuffle_epi32(xmm0[k], 0xd8); xmm1[k] = _mm_shuffle_epi32(xmm0[k], 0x4e); xmm0[k] = _mm_unpacklo_epi16(xmm0[k], xmm1[k]); xmm0[k] = _mm_shuffle_epi32(xmm0[k], 0xd8); } /* Transpose quad words */ for (k = 0; k < 1; k++) { xmm1[k * 2] = _mm_unpacklo_epi64(xmm0[k], xmm0[k + 1]); xmm1[k * 2 + 1] = _mm_unpackhi_epi64(xmm0[k], xmm0[k + 1]); } /* Store the result vectors */ dest_for_jth_element = dest + j; for (k = 0; k < 2; k++) { _mm_storeu_si128((__m128i*)(dest_for_jth_element + (k * total_elements)), xmm1[k]); } } } /* Routine optimized for shuffling a buffer for a type size of 4 bytes. */ static void shuffle4_sse2(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements) { static const int32_t bytesoftype = 4; int32_t i; int j; uint8_t* dest_for_ith_element; __m128i xmm0[4], xmm1[4]; for (i = 0; i < vectorizable_elements; i += sizeof(__m128i)) { /* Fetch 16 elements (64 bytes) then transpose bytes and words. */ for (j = 0; j < 4; j++) { xmm0[j] = _mm_loadu_si128((__m128i*)(src + (i * bytesoftype) + (j * sizeof(__m128i)))); xmm1[j] = _mm_shuffle_epi32(xmm0[j], 0xd8); xmm0[j] = _mm_shuffle_epi32(xmm0[j], 0x8d); xmm0[j] = _mm_unpacklo_epi8(xmm1[j], xmm0[j]); xmm1[j] = _mm_shuffle_epi32(xmm0[j], 0x04e); xmm0[j] = _mm_unpacklo_epi16(xmm0[j], xmm1[j]); } /* Transpose double words */ for (j = 0; j < 2; j++) { xmm1[j * 2] = _mm_unpacklo_epi32(xmm0[j * 2], xmm0[j * 2 + 1]); xmm1[j * 2 + 1] = _mm_unpackhi_epi32(xmm0[j * 2], xmm0[j * 2 + 1]); } /* Transpose quad words */ for (j = 0; j < 2; j++) { xmm0[j * 2] = _mm_unpacklo_epi64(xmm1[j], xmm1[j + 2]); xmm0[j * 2 + 1] = _mm_unpackhi_epi64(xmm1[j], xmm1[j + 2]); } /* Store the result vectors */ dest_for_ith_element = dest + i; for (j = 0; j < 4; j++) { _mm_storeu_si128((__m128i*)(dest_for_ith_element + (j * total_elements)), xmm0[j]); } } } /* Routine optimized for shuffling a buffer for a type size of 8 bytes. */ static void shuffle8_sse2(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements) { static const int32_t bytesoftype = 8; int32_t j; int k, l; uint8_t* dest_for_jth_element; __m128i xmm0[8], xmm1[8]; for (j = 0; j < vectorizable_elements; j += sizeof(__m128i)) { /* Fetch 16 elements (128 bytes) then transpose bytes. */ for (k = 0; k < 8; k++) { xmm0[k] = _mm_loadu_si128((__m128i*)(src + (j * bytesoftype) + (k * sizeof(__m128i)))); xmm1[k] = _mm_shuffle_epi32(xmm0[k], 0x4e); xmm1[k] = _mm_unpacklo_epi8(xmm0[k], xmm1[k]); } /* Transpose words */ for (k = 0, l = 0; k < 4; k++, l += 2) { xmm0[k * 2] = _mm_unpacklo_epi16(xmm1[l], xmm1[l + 1]); xmm0[k * 2 + 1] = _mm_unpackhi_epi16(xmm1[l], xmm1[l + 1]); } /* Transpose double words */ for (k = 0, l = 0; k < 4; k++, l++) { if (k == 2) l += 2; xmm1[k * 2] = _mm_unpacklo_epi32(xmm0[l], xmm0[l + 2]); xmm1[k * 2 + 1] = _mm_unpackhi_epi32(xmm0[l], xmm0[l + 2]); } /* Transpose quad words */ for (k = 0; k < 4; k++) { xmm0[k * 2] = _mm_unpacklo_epi64(xmm1[k], xmm1[k + 4]); xmm0[k * 2 + 1] = _mm_unpackhi_epi64(xmm1[k], xmm1[k + 4]); } /* Store the result vectors */ dest_for_jth_element = dest + j; for (k = 0; k < 8; k++) { _mm_storeu_si128((__m128i*)(dest_for_jth_element + (k * total_elements)), xmm0[k]); } } } /* Routine optimized for shuffling a buffer for a type size of 16 bytes. */ static void shuffle16_sse2(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements) { static const int32_t bytesoftype = 16; int32_t j; int k, l; uint8_t* dest_for_jth_element; __m128i xmm0[16], xmm1[16]; for (j = 0; j < vectorizable_elements; j += sizeof(__m128i)) { /* Fetch 16 elements (256 bytes). */ for (k = 0; k < 16; k++) { xmm0[k] = _mm_loadu_si128((__m128i*)(src + (j * bytesoftype) + (k * sizeof(__m128i)))); } /* Transpose bytes */ for (k = 0, l = 0; k < 8; k++, l += 2) { xmm1[k * 2] = _mm_unpacklo_epi8(xmm0[l], xmm0[l + 1]); xmm1[k * 2 + 1] = _mm_unpackhi_epi8(xmm0[l], xmm0[l + 1]); } /* Transpose words */ for (k = 0, l = -2; k < 8; k++, l++) { if ((k % 2) == 0) l += 2; xmm0[k * 2] = _mm_unpacklo_epi16(xmm1[l], xmm1[l + 2]); xmm0[k * 2 + 1] = _mm_unpackhi_epi16(xmm1[l], xmm1[l + 2]); } /* Transpose double words */ for (k = 0, l = -4; k < 8; k++, l++) { if ((k % 4) == 0) l += 4; xmm1[k * 2] = _mm_unpacklo_epi32(xmm0[l], xmm0[l + 4]); xmm1[k * 2 + 1] = _mm_unpackhi_epi32(xmm0[l], xmm0[l + 4]); } /* Transpose quad words */ for (k = 0; k < 8; k++) { xmm0[k * 2] = _mm_unpacklo_epi64(xmm1[k], xmm1[k + 8]); xmm0[k * 2 + 1] = _mm_unpackhi_epi64(xmm1[k], xmm1[k + 8]); } /* Store the result vectors */ dest_for_jth_element = dest + j; for (k = 0; k < 16; k++) { _mm_storeu_si128((__m128i*)(dest_for_jth_element + (k * total_elements)), xmm0[k]); } } } /* Routine optimized for shuffling a buffer for a type size larger than 16 bytes. */ static void shuffle16_tiled_sse2(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements, const int32_t bytesoftype) { int32_t j; const int32_t vecs_per_el_rem = bytesoftype % (int32_t)sizeof(__m128i); int k, l; uint8_t* dest_for_jth_element; __m128i xmm0[16], xmm1[16]; for (j = 0; j < vectorizable_elements; j += sizeof(__m128i)) { /* Advance the offset into the type by the vector size (in bytes), unless this is the initial iteration and the type size is not a multiple of the vector size. In that case, only advance by the number of bytes necessary so that the number of remaining bytes in the type will be a multiple of the vector size. */ int32_t offset_into_type; for (offset_into_type = 0; offset_into_type < bytesoftype; offset_into_type += (offset_into_type == 0 && vecs_per_el_rem > 0 ? vecs_per_el_rem : (int32_t)sizeof(__m128i))) { /* Fetch elements in groups of 256 bytes */ const uint8_t* const src_with_offset = src + offset_into_type; for (k = 0; k < 16; k++) { xmm0[k] = _mm_loadu_si128((__m128i*)(src_with_offset + (j + k) * bytesoftype)); } /* Transpose bytes */ for (k = 0, l = 0; k < 8; k++, l += 2) { xmm1[k * 2] = _mm_unpacklo_epi8(xmm0[l], xmm0[l + 1]); xmm1[k * 2 + 1] = _mm_unpackhi_epi8(xmm0[l], xmm0[l + 1]); } /* Transpose words */ for (k = 0, l = -2; k < 8; k++, l++) { if ((k % 2) == 0) l += 2; xmm0[k * 2] = _mm_unpacklo_epi16(xmm1[l], xmm1[l + 2]); xmm0[k * 2 + 1] = _mm_unpackhi_epi16(xmm1[l], xmm1[l + 2]); } /* Transpose double words */ for (k = 0, l = -4; k < 8; k++, l++) { if ((k % 4) == 0) l += 4; xmm1[k * 2] = _mm_unpacklo_epi32(xmm0[l], xmm0[l + 4]); xmm1[k * 2 + 1] = _mm_unpackhi_epi32(xmm0[l], xmm0[l + 4]); } /* Transpose quad words */ for (k = 0; k < 8; k++) { xmm0[k * 2] = _mm_unpacklo_epi64(xmm1[k], xmm1[k + 8]); xmm0[k * 2 + 1] = _mm_unpackhi_epi64(xmm1[k], xmm1[k + 8]); } /* Store the result vectors */ dest_for_jth_element = dest + j; for (k = 0; k < 16; k++) { _mm_storeu_si128((__m128i*)(dest_for_jth_element + (total_elements * (offset_into_type + k))), xmm0[k]); } } } } /* Routine optimized for unshuffling a buffer for a type size of 2 bytes. */ static void unshuffle2_sse2(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements) { static const int32_t bytesoftype = 2; int32_t i; int j; __m128i xmm0[2], xmm1[2]; for (i = 0; i < vectorizable_elements; i += sizeof(__m128i)) { /* Load 16 elements (32 bytes) into 2 XMM registers. */ const uint8_t* const src_for_ith_element = src + i; for (j = 0; j < 2; j++) { xmm0[j] = _mm_loadu_si128((__m128i*)(src_for_ith_element + (j * total_elements))); } /* Shuffle bytes */ /* Compute the low 32 bytes */ xmm1[0] = _mm_unpacklo_epi8(xmm0[0], xmm0[1]); /* Compute the hi 32 bytes */ xmm1[1] = _mm_unpackhi_epi8(xmm0[0], xmm0[1]); /* Store the result vectors in proper order */ _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (0 * sizeof(__m128i))), xmm1[0]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (1 * sizeof(__m128i))), xmm1[1]); } } /* Routine optimized for unshuffling a buffer for a type size of 4 bytes. */ static void unshuffle4_sse2(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements) { static const int32_t bytesoftype = 4; int32_t i; int j; __m128i xmm0[4], xmm1[4]; for (i = 0; i < vectorizable_elements; i += sizeof(__m128i)) { /* Load 16 elements (64 bytes) into 4 XMM registers. */ const uint8_t* const src_for_ith_element = src + i; for (j = 0; j < 4; j++) { xmm0[j] = _mm_loadu_si128((__m128i*)(src_for_ith_element + (j * total_elements))); } /* Shuffle bytes */ for (j = 0; j < 2; j++) { /* Compute the low 32 bytes */ xmm1[j] = _mm_unpacklo_epi8(xmm0[j * 2], xmm0[j * 2 + 1]); /* Compute the hi 32 bytes */ xmm1[2 + j] = _mm_unpackhi_epi8(xmm0[j * 2], xmm0[j * 2 + 1]); } /* Shuffle 2-byte words */ for (j = 0; j < 2; j++) { /* Compute the low 32 bytes */ xmm0[j] = _mm_unpacklo_epi16(xmm1[j * 2], xmm1[j * 2 + 1]); /* Compute the hi 32 bytes */ xmm0[2 + j] = _mm_unpackhi_epi16(xmm1[j * 2], xmm1[j * 2 + 1]); } /* Store the result vectors in proper order */ _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (0 * sizeof(__m128i))), xmm0[0]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (1 * sizeof(__m128i))), xmm0[2]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (2 * sizeof(__m128i))), xmm0[1]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (3 * sizeof(__m128i))), xmm0[3]); } } /* Routine optimized for unshuffling a buffer for a type size of 8 bytes. */ static void unshuffle8_sse2(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements) { static const int32_t bytesoftype = 8; int32_t i; int j; __m128i xmm0[8], xmm1[8]; for (i = 0; i < vectorizable_elements; i += sizeof(__m128i)) { /* Load 16 elements (128 bytes) into 8 XMM registers. */ const uint8_t* const src_for_ith_element = src + i; for (j = 0; j < 8; j++) { xmm0[j] = _mm_loadu_si128((__m128i*)(src_for_ith_element + (j * total_elements))); } /* Shuffle bytes */ for (j = 0; j < 4; j++) { /* Compute the low 32 bytes */ xmm1[j] = _mm_unpacklo_epi8(xmm0[j * 2], xmm0[j * 2 + 1]); /* Compute the hi 32 bytes */ xmm1[4 + j] = _mm_unpackhi_epi8(xmm0[j * 2], xmm0[j * 2 + 1]); } /* Shuffle 2-byte words */ for (j = 0; j < 4; j++) { /* Compute the low 32 bytes */ xmm0[j] = _mm_unpacklo_epi16(xmm1[j * 2], xmm1[j * 2 + 1]); /* Compute the hi 32 bytes */ xmm0[4 + j] = _mm_unpackhi_epi16(xmm1[j * 2], xmm1[j * 2 + 1]); } /* Shuffle 4-byte dwords */ for (j = 0; j < 4; j++) { /* Compute the low 32 bytes */ xmm1[j] = _mm_unpacklo_epi32(xmm0[j * 2], xmm0[j * 2 + 1]); /* Compute the hi 32 bytes */ xmm1[4 + j] = _mm_unpackhi_epi32(xmm0[j * 2], xmm0[j * 2 + 1]); } /* Store the result vectors in proper order */ _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (0 * sizeof(__m128i))), xmm1[0]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (1 * sizeof(__m128i))), xmm1[4]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (2 * sizeof(__m128i))), xmm1[2]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (3 * sizeof(__m128i))), xmm1[6]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (4 * sizeof(__m128i))), xmm1[1]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (5 * sizeof(__m128i))), xmm1[5]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (6 * sizeof(__m128i))), xmm1[3]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (7 * sizeof(__m128i))), xmm1[7]); } } /* Routine optimized for unshuffling a buffer for a type size of 12 bytes. */ /* Based on the 16-byte implementation */ static void unshuffle12_sse2(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements) { static const int32_t bytesoftype = 12; int32_t i; int j; __m128i xmm1[16], xmm2[16]; __m128i mask = _mm_set_epi8( 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff); for (i = 0; i < vectorizable_elements; i += sizeof(__m128i)) { /* Load 12 elements (192 bytes) into 12 XMM registers. */ const uint8_t* const src_for_ith_element = src + i; for (j = 0; j < bytesoftype; j++) { xmm1[j] = _mm_loadu_si128((__m128i*)(src_for_ith_element + (j * total_elements))); } /* Initialize the last 4 registers (64 bytes) to null */ for (j = bytesoftype; j < 16; j++) { xmm1[j] = _mm_setzero_si128(); } /* Shuffle bytes */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ xmm2[j] = _mm_unpacklo_epi8(xmm1[j * 2], xmm1[j * 2 + 1]); /* Compute the hi 32 bytes */ xmm2[8 + j] = _mm_unpackhi_epi8(xmm1[j * 2], xmm1[j * 2 + 1]); } /* Shuffle 2-byte words */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ xmm1[j] = _mm_unpacklo_epi16(xmm2[j * 2], xmm2[j * 2 + 1]); /* Compute the hi 32 bytes */ xmm1[8 + j] = _mm_unpackhi_epi16(xmm2[j * 2], xmm2[j * 2 + 1]); } /* Shuffle 4-byte dwords */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ xmm2[j] = _mm_unpacklo_epi32(xmm1[j * 2], xmm1[j * 2 + 1]); /* Compute the hi 32 bytes */ xmm2[8 + j] = _mm_unpackhi_epi32(xmm1[j * 2], xmm1[j * 2 + 1]); } /* Shuffle 8-byte qwords */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ xmm1[j] = _mm_unpacklo_epi64(xmm2[j * 2], xmm2[j * 2 + 1]); /* Compute the hi 32 bytes */ xmm1[8 + j] = _mm_unpackhi_epi64(xmm2[j * 2], xmm2[j * 2 + 1]); } /* Store the result vectors in proper order */ _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (0 * 12)), xmm1[0]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (1 * 12)), xmm1[8]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (2 * 12)), xmm1[4]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (3 * 12)), xmm1[12]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (4 * 12)), xmm1[2]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (5 * 12)), xmm1[10]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (6 * 12)), xmm1[6]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (7 * 12)), xmm1[14]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (8 * 12)), xmm1[1]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (9 * 12)), xmm1[9]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (10 * 12)), xmm1[5]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (11 * 12)), xmm1[13]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (12 * 12)), xmm1[3]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (13 * 12)), xmm1[11]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (14 * 12)), xmm1[7]); _mm_maskmoveu_si128(xmm1[15], mask, (char *)(dest + (i * bytesoftype) + (15 * 12))); } } /* Routine optimized for unshuffling a buffer for a type size of 16 bytes. */ static void unshuffle16_sse2(uint8_t* const dest, const uint8_t* const src, const int32_t vectorizable_elements, const int32_t total_elements) { static const int32_t bytesoftype = 16; int32_t i; int j; __m128i xmm1[16], xmm2[16]; for (i = 0; i < vectorizable_elements; i += sizeof(__m128i)) { /* Load 16 elements (256 bytes) into 16 XMM registers. */ const uint8_t* const src_for_ith_element = src + i; for (j = 0; j < 16; j++) { xmm1[j] = _mm_loadu_si128((__m128i*)(src_for_ith_element + (j * total_elements))); } /* Shuffle bytes */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ xmm2[j] = _mm_unpacklo_epi8(xmm1[j * 2], xmm1[j * 2 + 1]); /* Compute the hi 32 bytes */ xmm2[8 + j] = _mm_unpackhi_epi8(xmm1[j * 2], xmm1[j * 2 + 1]); } /* Shuffle 2-byte words */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ xmm1[j] = _mm_unpacklo_epi16(xmm2[j * 2], xmm2[j * 2 + 1]); /* Compute the hi 32 bytes */ xmm1[8 + j] = _mm_unpackhi_epi16(xmm2[j * 2], xmm2[j * 2 + 1]); } /* Shuffle 4-byte dwords */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ xmm2[j] = _mm_unpacklo_epi32(xmm1[j * 2], xmm1[j * 2 + 1]); /* Compute the hi 32 bytes */ xmm2[8 + j] = _mm_unpackhi_epi32(xmm1[j * 2], xmm1[j * 2 + 1]); } /* Shuffle 8-byte qwords */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ xmm1[j] = _mm_unpacklo_epi64(xmm2[j * 2], xmm2[j * 2 + 1]); /* Compute the hi 32 bytes */ xmm1[8 + j] = _mm_unpackhi_epi64(xmm2[j * 2], xmm2[j * 2 + 1]); } /* Store the result vectors in proper order */ _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (0 * sizeof(__m128i))), xmm1[0]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (1 * sizeof(__m128i))), xmm1[8]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (2 * sizeof(__m128i))), xmm1[4]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (3 * sizeof(__m128i))), xmm1[12]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (4 * sizeof(__m128i))), xmm1[2]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (5 * sizeof(__m128i))), xmm1[10]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (6 * sizeof(__m128i))), xmm1[6]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (7 * sizeof(__m128i))), xmm1[14]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (8 * sizeof(__m128i))), xmm1[1]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (9 * sizeof(__m128i))), xmm1[9]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (10 * sizeof(__m128i))), xmm1[5]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (11 * sizeof(__m128i))), xmm1[13]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (12 * sizeof(__m128i))), xmm1[3]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (13 * sizeof(__m128i))), xmm1[11]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (14 * sizeof(__m128i))), xmm1[7]); _mm_storeu_si128((__m128i*)(dest + (i * bytesoftype) + (15 * sizeof(__m128i))), xmm1[15]); } } /* Routine optimized for unshuffling a buffer for a type size larger than 16 bytes. */ static void unshuffle16_tiled_sse2(uint8_t* const dest, const uint8_t* const orig, const int32_t vectorizable_elements, const int32_t total_elements, const int32_t bytesoftype) { int32_t i; const int32_t vecs_per_el_rem = bytesoftype % (int32_t)sizeof(__m128i); int j; uint8_t* dest_with_offset; __m128i xmm1[16], xmm2[16]; /* The unshuffle loops are inverted (compared to shuffle_tiled16_sse2) to optimize cache utilization. */ int32_t offset_into_type; for (offset_into_type = 0; offset_into_type < bytesoftype; offset_into_type += (offset_into_type == 0 && vecs_per_el_rem > 0 ? vecs_per_el_rem : (int32_t)sizeof(__m128i))) { for (i = 0; i < vectorizable_elements; i += sizeof(__m128i)) { /* Load the first 128 bytes in 16 XMM registers */ const uint8_t* const src_for_ith_element = orig + i; for (j = 0; j < 16; j++) { xmm1[j] = _mm_loadu_si128((__m128i*)(src_for_ith_element + (total_elements * (offset_into_type + j)))); } /* Shuffle bytes */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ xmm2[j] = _mm_unpacklo_epi8(xmm1[j * 2], xmm1[j * 2 + 1]); /* Compute the hi 32 bytes */ xmm2[8 + j] = _mm_unpackhi_epi8(xmm1[j * 2], xmm1[j * 2 + 1]); } /* Shuffle 2-byte words */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ xmm1[j] = _mm_unpacklo_epi16(xmm2[j * 2], xmm2[j * 2 + 1]); /* Compute the hi 32 bytes */ xmm1[8 + j] = _mm_unpackhi_epi16(xmm2[j * 2], xmm2[j * 2 + 1]); } /* Shuffle 4-byte dwords */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ xmm2[j] = _mm_unpacklo_epi32(xmm1[j * 2], xmm1[j * 2 + 1]); /* Compute the hi 32 bytes */ xmm2[8 + j] = _mm_unpackhi_epi32(xmm1[j * 2], xmm1[j * 2 + 1]); } /* Shuffle 8-byte qwords */ for (j = 0; j < 8; j++) { /* Compute the low 32 bytes */ xmm1[j] = _mm_unpacklo_epi64(xmm2[j * 2], xmm2[j * 2 + 1]); /* Compute the hi 32 bytes */ xmm1[8 + j] = _mm_unpackhi_epi64(xmm2[j * 2], xmm2[j * 2 + 1]); } /* Store the result vectors in proper order */ dest_with_offset = dest + offset_into_type; _mm_storeu_si128((__m128i*)(dest_with_offset + (i + 0) * bytesoftype), xmm1[0]); _mm_storeu_si128((__m128i*)(dest_with_offset + (i + 1) * bytesoftype), xmm1[8]); _mm_storeu_si128((__m128i*)(dest_with_offset + (i + 2) * bytesoftype), xmm1[4]); _mm_storeu_si128((__m128i*)(dest_with_offset + (i + 3) * bytesoftype), xmm1[12]); _mm_storeu_si128((__m128i*)(dest_with_offset + (i + 4) * bytesoftype), xmm1[2]); _mm_storeu_si128((__m128i*)(dest_with_offset + (i + 5) * bytesoftype), xmm1[10]); _mm_storeu_si128((__m128i*)(dest_with_offset + (i + 6) * bytesoftype), xmm1[6]); _mm_storeu_si128((__m128i*)(dest_with_offset + (i + 7) * bytesoftype), xmm1[14]); _mm_storeu_si128((__m128i*)(dest_with_offset + (i + 8) * bytesoftype), xmm1[1]); _mm_storeu_si128((__m128i*)(dest_with_offset + (i + 9) * bytesoftype), xmm1[9]); _mm_storeu_si128((__m128i*)(dest_with_offset + (i + 10) * bytesoftype), xmm1[5]); _mm_storeu_si128((__m128i*)(dest_with_offset + (i + 11) * bytesoftype), xmm1[13]); _mm_storeu_si128((__m128i*)(dest_with_offset + (i + 12) * bytesoftype), xmm1[3]); _mm_storeu_si128((__m128i*)(dest_with_offset + (i + 13) * bytesoftype), xmm1[11]); _mm_storeu_si128((__m128i*)(dest_with_offset + (i + 14) * bytesoftype), xmm1[7]); _mm_storeu_si128((__m128i*)(dest_with_offset + (i + 15) * bytesoftype), xmm1[15]); } } } /* Shuffle a block. This can never fail. */ void shuffle_sse2(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest) { const int32_t vectorized_chunk_size = bytesoftype * (int32_t)sizeof(__m128i); /* If the blocksize is not a multiple of both the typesize and the vector size, round the blocksize down to the next value which is a multiple of both. The vectorized shuffle can be used for that portion of the data, and the naive implementation can be used for the remaining portion. */ const int32_t vectorizable_bytes = blocksize - (blocksize % vectorized_chunk_size); const int32_t vectorizable_elements = vectorizable_bytes / bytesoftype; const int32_t total_elements = blocksize / bytesoftype; /* If the block size is too small to be vectorized, use the generic implementation. */ if (blocksize < vectorized_chunk_size) { shuffle_generic(bytesoftype, blocksize, _src, _dest); return; } /* Optimized shuffle implementations */ switch (bytesoftype) { case 2: shuffle2_sse2(_dest, _src, vectorizable_elements, total_elements); break; case 4: shuffle4_sse2(_dest, _src, vectorizable_elements, total_elements); break; case 8: shuffle8_sse2(_dest, _src, vectorizable_elements, total_elements); break; case 16: shuffle16_sse2(_dest, _src, vectorizable_elements, total_elements); break; default: if (bytesoftype > (int32_t)sizeof(__m128i)) { shuffle16_tiled_sse2(_dest, _src, vectorizable_elements, total_elements, bytesoftype); } else { /* Non-optimized shuffle */ shuffle_generic(bytesoftype, blocksize, _src, _dest); /* The non-optimized function covers the whole buffer, so we're done processing here. */ return; } } /* If the buffer had any bytes at the end which couldn't be handled by the vectorized implementations, use the non-optimized version to finish them up. */ if (vectorizable_bytes < blocksize) { shuffle_generic_inline(bytesoftype, vectorizable_bytes, blocksize, _src, _dest); } } /* Unshuffle a block. This can never fail. */ void unshuffle_sse2(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest) { const int32_t vectorized_chunk_size = bytesoftype * (int32_t)sizeof(__m128i); /* If the blocksize is not a multiple of both the typesize and the vector size, round the blocksize down to the next value which is a multiple of both. The vectorized unshuffle can be used for that portion of the data, and the naive implementation can be used for the remaining portion. */ const int32_t vectorizable_bytes = blocksize - (blocksize % vectorized_chunk_size); const int32_t vectorizable_elements = vectorizable_bytes / bytesoftype; const int32_t total_elements = blocksize / bytesoftype; /* If the block size is too small to be vectorized, use the generic implementation. */ if (blocksize < vectorized_chunk_size) { unshuffle_generic(bytesoftype, blocksize, _src, _dest); return; } /* Optimized unshuffle implementations */ switch (bytesoftype) { case 2: unshuffle2_sse2(_dest, _src, vectorizable_elements, total_elements); break; case 4: unshuffle4_sse2(_dest, _src, vectorizable_elements, total_elements); break; case 8: unshuffle8_sse2(_dest, _src, vectorizable_elements, total_elements); break; case 12: unshuffle12_sse2(_dest, _src, vectorizable_elements, total_elements); break; case 16: unshuffle16_sse2(_dest, _src, vectorizable_elements, total_elements); break; default: if (bytesoftype > (int32_t)sizeof(__m128i)) { unshuffle16_tiled_sse2(_dest, _src, vectorizable_elements, total_elements, bytesoftype); } else { /* Non-optimized unshuffle */ unshuffle_generic(bytesoftype, blocksize, _src, _dest); /* The non-optimized function covers the whole buffer, so we're done processing here. */ return; } } /* If the buffer had any bytes at the end which couldn't be handled by the vectorized implementations, use the non-optimized version to finish them up. */ if (vectorizable_bytes < blocksize) { unshuffle_generic_inline(bytesoftype, vectorizable_bytes, blocksize, _src, _dest); } } const bool is_shuffle_sse2 = true; #else /* defined(__SSE2__) */ const bool is_shuffle_sse2 = false; void shuffle_sse2(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest) { abort(); } void unshuffle_sse2(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest) { abort(); } #endif /* defined(__SSE2__) */ c-blosc2-3.1.5/blosc/shuffle-sse2.h000066400000000000000000000021361521743436100167150ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /* SSE2-accelerated shuffle/unshuffle routines. */ #ifndef BLOSC_SHUFFLE_SSE2_H #define BLOSC_SHUFFLE_SSE2_H #include "blosc2/blosc2-common.h" #include #include /** * SSE2-accelerated (un)shuffle routines availability. */ extern const bool is_shuffle_sse2; /** SSE2-accelerated shuffle routine. */ BLOSC_NO_EXPORT void shuffle_sse2(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest); /** SSE2-accelerated unshuffle routine. */ BLOSC_NO_EXPORT void unshuffle_sse2(const int32_t bytesoftype, const int32_t blocksize, const uint8_t *_src, uint8_t *_dest); #endif /* BLOSC_SHUFFLE_SSE2_H */ c-blosc2-3.1.5/blosc/shuffle.c000066400000000000000000000502051521743436100160360ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #include "blosc2.h" /* needs to be included first to define macros */ #include "shuffle.h" /* Include hardware-accelerated shuffle/unshuffle routines based on the target architecture. Note that a target architecture may support more than one type of acceleration!*/ #if defined(SHUFFLE_AVX512_ENABLED) #include "bitshuffle-avx512.h" #endif /* defined(SHUFFLE_AVX512_ENABLED) */ #if defined(SHUFFLE_AVX2_ENABLED) #include "shuffle-avx2.h" #include "bitshuffle-avx2.h" #endif /* defined(SHUFFLE_AVX2_ENABLED) */ #if defined(SHUFFLE_SSE2_ENABLED) #include "shuffle-sse2.h" #include "bitshuffle-sse2.h" #endif /* defined(SHUFFLE_SSE2_ENABLED) */ #if defined(SHUFFLE_NEON_ENABLED) #if defined(__linux__) #include #ifdef ARM_ASM_HWCAP #include #endif #endif #include "shuffle-neon.h" #include "bitshuffle-neon.h" #endif /* defined(SHUFFLE_NEON_ENABLED) */ #if defined(SHUFFLE_ALTIVEC_ENABLED) #include "shuffle-altivec.h" #include "bitshuffle-altivec.h" #endif /* defined(SHUFFLE_ALTIVEC_ENABLED) */ #include "shuffle-generic.h" #include "bitshuffle-generic.h" #include #include // __builtin_cpu_supports() fixed in GCC 8: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85100 // Also, clang added support for it in clang 10 at very least (and possibly since 3.8) // Handle clang on windows without MSVC #if (defined(__clang__) && (__clang_major__ >= 10)) && !(defined(__APPLE__) && defined(__x86_64__) && defined(BUILD_STATIC)) \ && !defined(_WIN32)|| \ (defined(__GNUC__) && defined(__GNUC_MINOR__) && __GNUC__ >= 8) #define HAVE_CPU_FEAT_INTRIN #endif /* Define function pointer types for shuffle/unshuffle routines. */ typedef void(* shuffle_func)(const int32_t, const int32_t, const uint8_t*, uint8_t*); typedef void(* unshuffle_func)(const int32_t, const int32_t, const uint8_t*, uint8_t*); // For bitshuffle, everything is done in terms of size_t and int64_t (return value) // and although this is not strictly necessary for Blosc, it does not hurt either typedef int64_t(* bitshuffle_func)(const void*, void*, const size_t, const size_t); typedef int64_t(* bitunshuffle_func)(const void*, void*, const size_t, const size_t); /* An implementation of shuffle/unshuffle routines. */ typedef struct shuffle_implementation { /* Name of this implementation. */ const char* name; /* Function pointer to the shuffle routine for this implementation. */ shuffle_func shuffle; /* Function pointer to the unshuffle routine for this implementation. */ unshuffle_func unshuffle; /* Function pointer to the bitshuffle routine for this implementation. */ bitshuffle_func bitshuffle; /* Function pointer to the bitunshuffle routine for this implementation. */ bitunshuffle_func bitunshuffle; } shuffle_implementation_t; typedef enum { BLOSC_HAVE_NOTHING = 0, BLOSC_HAVE_SSE2 = 1, BLOSC_HAVE_AVX2 = 2, BLOSC_HAVE_NEON = 4, BLOSC_HAVE_ALTIVEC = 8, BLOSC_HAVE_AVX512 = 16, } blosc_cpu_features; /* Detect hardware and set function pointers to the best shuffle/unshuffle implementations supported by the host processor. */ #if (defined(SHUFFLE_AVX2_ENABLED) || defined(SHUFFLE_SSE2_ENABLED)) && \ (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64)) /* Intel/i686 */ #if defined(HAVE_CPU_FEAT_INTRIN) static blosc_cpu_features blosc_get_cpu_features(void) { blosc_cpu_features cpu_features = BLOSC_HAVE_NOTHING; if (__builtin_cpu_supports("sse2")) { cpu_features |= BLOSC_HAVE_SSE2; } if (__builtin_cpu_supports("avx2")) { cpu_features |= BLOSC_HAVE_AVX2; } if (__builtin_cpu_supports("avx512f") && __builtin_cpu_supports("avx512bw")) { cpu_features |= BLOSC_HAVE_AVX512; } return cpu_features; } #else #if defined(_MSC_VER) #include /* Needed for _xgetbv */ #include /* Needed for __cpuid */ #else /* Implement the __cpuid and __cpuidex intrinsics for GCC, Clang, and others using inline assembly. */ __attribute__((always_inline)) static inline void __cpuidex(int32_t cpuInfo[4], int32_t function_id, int32_t subfunction_id) { __asm__ __volatile__ ( # if defined(__i386__) && defined (__PIC__) /* Can't clobber ebx with PIC running under 32-bit, so it needs to be manually restored. https://software.intel.com/en-us/articles/how-to-detect-new-instruction-support-in-the-4th-generation-intel-core-processor-family */ "movl %%ebx, %%edi\n\t" "cpuid\n\t" "xchgl %%ebx, %%edi": "=D" (cpuInfo[1]), #else "cpuid": "=b" (cpuInfo[1]), #endif /* defined(__i386) && defined(__PIC__) */ "=a" (cpuInfo[0]), "=c" (cpuInfo[2]), "=d" (cpuInfo[3]) : "a" (function_id), "c" (subfunction_id) ); } #define __cpuid(cpuInfo, function_id) __cpuidex(cpuInfo, function_id, 0) #define _XCR_XFEATURE_ENABLED_MASK 0 // GCC folks added _xgetbv in immintrin.h starting in GCC 9 // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71659 #if !(defined(_IMMINTRIN_H_INCLUDED) && (BLOSC_GCC_VERSION >= 900)) && !defined(__IMMINTRIN_H) /* Reads the content of an extended control register. https://software.intel.com/en-us/articles/how-to-detect-new-instruction-support-in-the-4th-generation-intel-core-processor-family */ static inline uint64_t _xgetbv(uint32_t xcr) { uint32_t eax, edx; __asm__ __volatile__ ( /* "xgetbv" This is specified as raw instruction bytes due to some older compilers having issues with the mnemonic form. */ ".byte 0x0f, 0x01, 0xd0": "=a" (eax), "=d" (edx) : "c" (xcr) ); return ((uint64_t)edx << 32) | eax; } #endif // !(defined(_IMMINTRIN_H_INCLUDED) && (BLOSC_GCC_VERSION >= 900)) && !defined(__IMMINTRIN_H) #endif /* defined(_MSC_VER) */ #ifndef _XCR_XFEATURE_ENABLED_MASK #define _XCR_XFEATURE_ENABLED_MASK 0x0 #endif static blosc_cpu_features blosc_get_cpu_features(void) { blosc_cpu_features result = BLOSC_HAVE_NOTHING; /* Holds the values of eax, ebx, ecx, edx set by the `cpuid` instruction */ int32_t cpu_info[4]; /* Get the number of basic functions available. */ __cpuid(cpu_info, 0); int32_t max_basic_function_id = cpu_info[0]; /* Check for SSE-based features and required OS support */ __cpuid(cpu_info, 1); const bool sse2_available = (cpu_info[3] & (1 << 26)) != 0; const bool sse3_available = (cpu_info[2] & (1 << 0)) != 0; const bool ssse3_available = (cpu_info[2] & (1 << 9)) != 0; const bool sse41_available = (cpu_info[2] & (1 << 19)) != 0; const bool sse42_available = (cpu_info[2] & (1 << 20)) != 0; const bool xsave_available = (cpu_info[2] & (1 << 26)) != 0; const bool xsave_enabled_by_os = (cpu_info[2] & (1 << 27)) != 0; /* Check for AVX-based features, if the processor supports extended features. */ bool avx2_available = false; bool avx512f_available = false; bool avx512bw_available = false; if (max_basic_function_id >= 7) { __cpuid(cpu_info, 7); avx2_available = (cpu_info[1] & (1 << 5)) != 0; avx512f_available = (cpu_info[1] & (1 << 16)) != 0; avx512bw_available = (cpu_info[1] & (1 << 30)) != 0; } /* Even if certain features are supported by the CPU, they may not be supported by the OS (in which case using them would crash the process or system). If xsave is available and enabled by the OS, check the contents of the extended control register XCR0 to see if the CPU features are enabled. */ bool xmm_state_enabled = false; bool ymm_state_enabled = false; // Silence an unused variable compiler warning // bool zmm_state_enabled = false; #if defined(_XCR_XFEATURE_ENABLED_MASK) if (xsave_available && xsave_enabled_by_os && ( sse2_available || sse3_available || ssse3_available || sse41_available || sse42_available || avx2_available || avx512f_available || avx512bw_available)) { /* Determine which register states can be restored by the OS. */ uint64_t xcr0_contents = _xgetbv(_XCR_XFEATURE_ENABLED_MASK); xmm_state_enabled = (xcr0_contents & (1UL << 1)) != 0; ymm_state_enabled = (xcr0_contents & (1UL << 2)) != 0; /* Require support for both the upper 256-bits of zmm0-zmm15 to be restored as well as all of zmm16-zmm31 and the opmask registers. */ // zmm_state_enabled = (xcr0_contents & 0x70) == 0x70; } #endif /* defined(_XCR_XFEATURE_ENABLED_MASK) */ #if defined(BLOSC_DUMP_CPU_INFO) printf("Shuffle CPU Information:\n"); printf("SSE2 available: %s\n", sse2_available ? "True" : "False"); printf("SSE3 available: %s\n", sse3_available ? "True" : "False"); printf("SSSE3 available: %s\n", ssse3_available ? "True" : "False"); printf("SSE4.1 available: %s\n", sse41_available ? "True" : "False"); printf("SSE4.2 available: %s\n", sse42_available ? "True" : "False"); printf("AVX2 available: %s\n", avx2_available ? "True" : "False"); printf("AVX512F available: %s\n", avx512f_available ? "True" : "False"); printf("AVX512BW available: %s\n", avx512bw_available ? "True" : "False"); printf("XSAVE available: %s\n", xsave_available ? "True" : "False"); printf("XSAVE enabled: %s\n", xsave_enabled_by_os ? "True" : "False"); printf("XMM state enabled: %s\n", xmm_state_enabled ? "True" : "False"); printf("YMM state enabled: %s\n", ymm_state_enabled ? "True" : "False"); // printf("ZMM state enabled: %s\n", zmm_state_enabled ? "True" : "False"); #endif /* defined(BLOSC_DUMP_CPU_INFO) */ /* Using the gathered CPU information, determine which implementation to use. */ /* technically could fail on sse2 cpu on os without xmm support, but that * shouldn't exist anymore */ if (sse2_available) { result |= BLOSC_HAVE_SSE2; } if (xmm_state_enabled && ymm_state_enabled && avx2_available) { result |= BLOSC_HAVE_AVX2; } if (xmm_state_enabled && ymm_state_enabled && avx512f_available && avx512bw_available) { result |= BLOSC_HAVE_AVX512; } return result; } #endif /* HAVE_CPU_FEAT_INTRIN */ #elif defined(SHUFFLE_NEON_ENABLED) /* ARM-NEON */ static blosc_cpu_features blosc_get_cpu_features(void) { blosc_cpu_features cpu_features = BLOSC_HAVE_NOTHING; #if defined(__aarch64__) /* aarch64 always has NEON */ cpu_features |= BLOSC_HAVE_NEON; #elif defined(__linux__) if (getauxval(AT_HWCAP) & HWCAP_ARM_NEON) { cpu_features |= BLOSC_HAVE_NEON; } #endif return cpu_features; } #elif defined(SHUFFLE_ALTIVEC_ENABLED) /* POWER9-ALTIVEC preliminary test*/ static blosc_cpu_features blosc_get_cpu_features(void) { blosc_cpu_features cpu_features = BLOSC_HAVE_NOTHING; cpu_features |= BLOSC_HAVE_ALTIVEC; return cpu_features; } #else /* No hardware acceleration supported for the target architecture. */ #endif /* defined(SHUFFLE_AVX2_ENABLED) || defined(SHUFFLE_SSE2_ENABLED) */ static shuffle_implementation_t get_shuffle_implementation(void) { #if defined(SHUFFLE_AVX512_ENABLED) || defined(SHUFFLE_AVX2_ENABLED) || defined(SHUFFLE_SSE2_ENABLED) || defined(SHUFFLE_NEON_ENABLED) || defined(SHUFFLE_ALTIVEC_ENABLED) blosc_cpu_features cpu_features = blosc_get_cpu_features(); #endif #if defined(SHUFFLE_AVX512_ENABLED) if (cpu_features & BLOSC_HAVE_AVX512 && is_shuffle_avx2 && is_bshuf_AVX512) { shuffle_implementation_t impl_avx512; impl_avx512.name = "avx512"; impl_avx512.shuffle = (shuffle_func)shuffle_avx2; impl_avx512.unshuffle = (unshuffle_func)unshuffle_avx2; impl_avx512.bitshuffle = (bitshuffle_func) bshuf_trans_bit_elem_AVX512; impl_avx512.bitunshuffle = (bitunshuffle_func)bshuf_untrans_bit_elem_AVX512; return impl_avx512; } #endif /* defined(SHUFFLE_AVX512_ENABLED) */ #if defined(SHUFFLE_AVX2_ENABLED) if (cpu_features & BLOSC_HAVE_AVX2 && is_shuffle_avx2 && is_bshuf_AVX) { shuffle_implementation_t impl_avx2; impl_avx2.name = "avx2"; impl_avx2.shuffle = (shuffle_func)shuffle_avx2; impl_avx2.unshuffle = (unshuffle_func)unshuffle_avx2; impl_avx2.bitshuffle = (bitshuffle_func) bshuf_trans_bit_elem_AVX; impl_avx2.bitunshuffle = (bitunshuffle_func)bshuf_untrans_bit_elem_AVX; return impl_avx2; } #endif /* defined(SHUFFLE_AVX2_ENABLED) */ #if defined(SHUFFLE_SSE2_ENABLED) if (cpu_features & BLOSC_HAVE_SSE2 && is_shuffle_sse2 && is_bshuf_SSE) { shuffle_implementation_t impl_sse2; impl_sse2.name = "sse2"; impl_sse2.shuffle = (shuffle_func)shuffle_sse2; impl_sse2.unshuffle = (unshuffle_func)unshuffle_sse2; impl_sse2.bitshuffle = (bitshuffle_func)bshuf_trans_bit_elem_SSE; impl_sse2.bitunshuffle = (bitunshuffle_func) bshuf_untrans_bit_elem_SSE; return impl_sse2; } #endif /* defined(SHUFFLE_SSE2_ENABLED) */ #if defined(SHUFFLE_NEON_ENABLED) if (cpu_features & BLOSC_HAVE_NEON && is_shuffle_neon) { // && is_bshuf_NEON if using NEON bitshuffle shuffle_implementation_t impl_neon; impl_neon.name = "neon"; impl_neon.shuffle = (shuffle_func)shuffle_neon; impl_neon.unshuffle = (unshuffle_func)unshuffle_neon; //impl_neon.shuffle = (shuffle_func)shuffle_generic; //impl_neon.unshuffle = (unshuffle_func)unshuffle_generic; //impl_neon.bitshuffle = (bitshuffle_func)bshuf_trans_bit_elem_NEON; //impl_neon.bitunshuffle = (bitunshuffle_func)bshuf_untrans_bit_elem_NEON; // The current bitshuffle optimized for NEON is not any faster // (in fact, it is pretty much slower) than the scalar implementation. // So, let's use the scalar one, which is pretty fast, at least on a M1 CPU. impl_neon.bitshuffle = (bitshuffle_func)bshuf_trans_bit_elem_scal; impl_neon.bitunshuffle = (bitunshuffle_func)bshuf_untrans_bit_elem_scal; return impl_neon; } #endif /* defined(SHUFFLE_NEON_ENABLED) */ #if defined(SHUFFLE_ALTIVEC_ENABLED) if (cpu_features & BLOSC_HAVE_ALTIVEC && is_shuffle_altivec && is_bshuf_altivec) { shuffle_implementation_t impl_altivec; impl_altivec.name = "altivec"; impl_altivec.shuffle = (shuffle_func)shuffle_altivec; impl_altivec.unshuffle = (unshuffle_func)unshuffle_altivec; impl_altivec.bitshuffle = (bitshuffle_func)bshuf_trans_bit_elem_altivec; impl_altivec.bitunshuffle = (bitunshuffle_func)bshuf_untrans_bit_elem_altivec; return impl_altivec; } #endif /* defined(SHUFFLE_ALTIVEC_ENABLED) */ /* Processor doesn't support any of the hardware-accelerated implementations, so use the generic implementation. */ shuffle_implementation_t impl_generic; impl_generic.name = "generic"; impl_generic.shuffle = (shuffle_func)shuffle_generic; impl_generic.unshuffle = (unshuffle_func)unshuffle_generic; impl_generic.bitshuffle = (bitshuffle_func)bshuf_trans_bit_elem_scal; impl_generic.bitunshuffle = (bitunshuffle_func)bshuf_untrans_bit_elem_scal; return impl_generic; } /* Flag indicating whether the implementation has been initialized. Zero means it hasn't been initialized, non-zero means it has. */ static int32_t implementation_initialized; /* The dynamically-chosen shuffle/unshuffle implementation. This is only safe to use once `implementation_initialized` is set. */ static shuffle_implementation_t host_implementation; /* Initialize the shuffle implementation, if necessary. */ #if defined(__GNUC__) || defined(__clang__) __attribute__((always_inline)) #endif static #if defined(_MSC_VER) __forceinline #else inline #endif void init_shuffle_implementation(void) { /* Initialization could (in rare cases) take place concurrently on multiple threads, but it shouldn't matter because the initialization should return the same result on each thread (so the implementation will be the same). Since that's the case we can avoid complicated synchronization here and get a small performance benefit because we don't need to perform a volatile load on the initialization variable each time this function is called. */ #if defined(__GNUC__) || defined(__clang__) if (__builtin_expect(!implementation_initialized, 0)) { #else if (!implementation_initialized) { #endif /* Initialize the implementation. */ host_implementation = get_shuffle_implementation(); /* Set the flag indicating the implementation has been initialized. */ implementation_initialized = 1; } } /* Shuffle a block by dynamically dispatching to the appropriate hardware-accelerated routine at run-time. */ int32_t blosc2_shuffle(const int32_t typesize, const int32_t blocksize, const void* src, void* dest) { /* Initialize the shuffle implementation if necessary. */ init_shuffle_implementation(); if (typesize < 1 || typesize > 256 || blocksize < 0) { return BLOSC2_ERROR_INVALID_PARAM; } /* The implementation is initialized. Dispatch to its shuffle routine. */ (host_implementation.shuffle)(typesize, blocksize, src, dest); return blocksize; } /* Unshuffle a block by dynamically dispatching to the appropriate hardware-accelerated routine at run-time. */ int32_t blosc2_unshuffle(const int32_t typesize, const int32_t blocksize, const void* src, void* dest) { /* Initialize the shuffle implementation if necessary. */ init_shuffle_implementation(); if (typesize < 1 || typesize > 256 || blocksize < 0) { return BLOSC2_ERROR_INVALID_PARAM; } /* The implementation is initialized. Dispatch to it's unshuffle routine. */ (host_implementation.unshuffle)(typesize, blocksize, src, dest); return blocksize; } /* Bit-shuffle a block by dynamically dispatching to the appropriate hardware-accelerated routine at run-time. */ int32_t blosc2_bitshuffle(const int32_t typesize, const int32_t blocksize, const void* src, void* dest) { /* Initialize the shuffle implementation if necessary. */ init_shuffle_implementation(); size_t size = blocksize / typesize; if (typesize < 1 || typesize > 256 || blocksize < 0) { return BLOSC2_ERROR_INVALID_PARAM; } /* bitshuffle only supports a number of elements that is a multiple of 8. */ size -= size % 8; int ret = (int) (host_implementation.bitshuffle)(src, dest, size, typesize); if (ret < 0) { // Some error in bitshuffle (should not happen) BLOSC_TRACE_ERROR("the impossible happened: the bitshuffle filter failed!"); return ret; } // Copy the leftovers size_t offset = size * typesize; memcpy((uint8_t *) dest + offset, (const uint8_t *) src + offset, blocksize - offset); return blocksize; } /* Bit-unshuffle a block by dynamically dispatching to the appropriate hardware-accelerated routine at run-time. */ int32_t bitunshuffle(const int32_t typesize, const int32_t blocksize, const void* src, void* dest, const uint8_t format_version) { /* Initialize the shuffle implementation if necessary. */ init_shuffle_implementation(); size_t size = blocksize / typesize; if (format_version == 2) { /* Starting from version 3, bitshuffle() works differently */ if ((size % 8) == 0) { /* The number of elems is a multiple of 8 which is supported by bitshuffle. */ int ret = (int) (host_implementation.bitunshuffle) ((const void *) src, (void *) dest, blocksize / typesize, typesize); if (ret < 0) { // Some error in bitshuffle (should not happen) BLOSC_TRACE_ERROR("the impossible happened: the bitunshuffle filter failed!"); return ret; } } else { memcpy(dest, src, blocksize); } } else { /* bitshuffle only supports a number of bytes that is a multiple of 8. */ size -= size % 8; int ret = (int) (host_implementation.bitunshuffle)(src, dest, size, typesize); if (ret < 0) { BLOSC_TRACE_ERROR("the impossible happened: the bitunshuffle filter failed!"); return ret; } /* Copy the leftovers */ size_t offset = size * typesize; memcpy((uint8_t *) dest + offset, (const uint8_t *) src + offset, blocksize - offset); } return blocksize; } /* Stub public API that redirects to internal implementation. */ int32_t blosc2_bitunshuffle(const int32_t typesize, const int32_t blocksize, const void * src, void* dest) { return bitunshuffle(typesize, blocksize, src, dest, BLOSC2_VERSION_FORMAT); } c-blosc2-3.1.5/blosc/shuffle.h000066400000000000000000000025611521743436100160450ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /********************************************************************* Shuffle/unshuffle routines which dynamically dispatch to hardware- accelerated routines based on the processor's architecture. Consumers should almost always prefer to call these routines instead of directly calling one of the hardware-accelerated routines, since these are cross-platform and future-proof. **********************************************************************/ #ifndef BLOSC_SHUFFLE_H #define BLOSC_SHUFFLE_H #include "blosc2/blosc2-common.h" #include /** Internal bitunshuffle routine that accepts a format version. We don't have to expose this parameter to users, since the public API is new to blosc2, and its behavior can be independent of the storage format. */ BLOSC_NO_EXPORT int32_t bitunshuffle(const int32_t bytesoftype, const int32_t blocksize, const void* src, void* dest, const uint8_t format_version); #endif /* BLOSC_SHUFFLE_H */ c-blosc2-3.1.5/blosc/stune.c000066400000000000000000000133111521743436100155350ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #include "stune.h" #include "blosc2/codecs-registry.h" #include #include /* Whether a codec is meant for High Compression Ratios Includes LZ4 + BITSHUFFLE here, but not BloscLZ + BITSHUFFLE because, for some reason, the latter does not work too well */ static bool is_HCR(blosc2_context * context) { switch (context->compcode) { case BLOSC_BLOSCLZ : return false; case BLOSC_LZ4 : // return (context->filter_flags & BLOSC_DOBITSHUFFLE) ? true : false; // Do not treat LZ4 differently than BloscLZ here return false; case BLOSC_LZ4HC : case BLOSC_ZLIB : case BLOSC_ZSTD : return true; default : return false; } } int blosc_stune_init(void * config, blosc2_context* cctx, blosc2_context* dctx) { BLOSC_UNUSED_PARAM(config); BLOSC_UNUSED_PARAM(cctx); BLOSC_UNUSED_PARAM(dctx); return BLOSC2_ERROR_SUCCESS; } // Set the automatic blocksize 0 to its real value int blosc_stune_next_blocksize(blosc2_context *context) { int32_t clevel = context->clevel; int32_t typesize = context->typesize; int32_t nbytes = context->sourcesize; int32_t user_blocksize = context->blocksize; int32_t blocksize = nbytes; // Protection against very small buffers if (nbytes < typesize) { context->blocksize = 1; return BLOSC2_ERROR_SUCCESS; } int splitmode = split_block(context, typesize, blocksize); if (user_blocksize) { blocksize = user_blocksize; goto last; } if (nbytes >= L1) { blocksize = L1; /* For HCR codecs, increase the block sizes by a factor of 2 because they are meant for compressing large blocks (i.e. they show a big overhead when compressing small ones). */ if (is_HCR(context)) { blocksize *= 2; } // Choose a different blocksize depending on the compression level switch (clevel) { case 0: // Case of plain copy blocksize /= 4; break; case 1: blocksize /= 2; break; case 2: blocksize *= 1; break; case 3: blocksize *= 2; break; case 4: case 5: blocksize *= 4; break; case 6: case 7: case 8: blocksize *= 8; break; case 9: // Do not exceed 256 KB for non HCR codecs blocksize *= 8; if (is_HCR(context)) { blocksize *= 2; } break; default: break; } } /* Now the blocksize for splittable codecs */ if (clevel > 0 && splitmode) { // For performance reasons, do not exceed 256 KB (it must fit in L2 cache) switch (clevel) { case 1: case 2: case 3: blocksize = 32 * 1024; break; case 4: case 5: case 6: blocksize = 64 * 1024; break; case 7: blocksize = 128 * 1024; break; case 8: blocksize = 256 * 1024; break; case 9: default: blocksize = 512 * 1024; break; } // Multiply by typesize to get proper split sizes blocksize *= typesize; // But do not exceed 4 MB per thread (having this capacity in L3 is normal in modern CPUs) if (blocksize > 4 * 1024 * 1024) { blocksize = 4 * 1024 * 1024; } if (blocksize < 32 * 1024) { /* Do not use a too small blocksize (< 32 KB) when typesize is small */ blocksize = 32 * 1024; } } last: /* Check that blocksize is not too large */ if (blocksize > nbytes) { blocksize = nbytes; } // blocksize *must absolutely* be a multiple of the typesize if (blocksize > typesize) { blocksize = blocksize / typesize * typesize; } context->blocksize = blocksize; BLOSC_INFO("compcode: %d, clevel: %d, blocksize: %d, splitmode: %d, typesize: %d", context->compcode, context->clevel, blocksize, splitmode, typesize); return BLOSC2_ERROR_SUCCESS; } int blosc_stune_next_cparams(blosc2_context * context) { BLOSC_UNUSED_PARAM(context); return BLOSC2_ERROR_SUCCESS; } int blosc_stune_update(blosc2_context * context, double ctime) { BLOSC_UNUSED_PARAM(context); BLOSC_UNUSED_PARAM(ctime); return BLOSC2_ERROR_SUCCESS; } int blosc_stune_free(blosc2_context * context) { BLOSC_UNUSED_PARAM(context); return BLOSC2_ERROR_SUCCESS; } int split_block(blosc2_context *context, int32_t typesize, int32_t blocksize) { switch (context->splitmode) { case BLOSC_ALWAYS_SPLIT: return 1; case BLOSC_NEVER_SPLIT: return 0; case BLOSC_FORWARD_COMPAT_SPLIT: case BLOSC_AUTO_SPLIT: // These cases will be handled later break; default: BLOSC_TRACE_WARNING("Unrecognized split mode. Default to BLOSC_FORWARD_COMPAT_SPLIT"); } int compcode = context->compcode; if (compcode == BLOSC_CODEC_OPENZL) { // Never split blocks for OpenZL, as it has its own block mechanisms return 0; } return ( // Fast codecs like blosclz, lz4 seems to prefer to split ((compcode == BLOSC_BLOSCLZ) || (compcode == BLOSC_LZ4) // and low levels of zstd too || ((compcode == BLOSC_ZSTD) && (context->clevel <= 5)) ) && // ...but split seems to harm cratio too much when not using shuffle (context->filter_flags & BLOSC_DOSHUFFLE) && (typesize <= MAX_STREAMS) && (blocksize / typesize) >= BLOSC_MIN_BUFFERSIZE); } c-blosc2-3.1.5/blosc/stune.h000066400000000000000000000024211521743436100155420ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #ifndef BLOSC_STUNE_H #define BLOSC_STUNE_H #include "context.h" #include /* The size of L1 cache. 32 KB is quite common nowadays. */ #define L1 (32 * 1024) /* The size of L2 cache. 256 KB is quite common nowadays. */ #define L2 (256 * 1024) /* The maximum number of compressed data streams in a block for compression */ #define MAX_STREAMS 16 /* Cannot be larger than 128 */ #define BLOSC_STUNE 0 int blosc_stune_init(void * config, blosc2_context* cctx, blosc2_context* dctx); int blosc_stune_next_blocksize(blosc2_context * context); int blosc_stune_next_cparams(blosc2_context * context); int blosc_stune_update(blosc2_context * context, double ctime); int blosc_stune_free(blosc2_context * context); /* Conditions for splitting a block before compressing with a codec. */ int split_block(blosc2_context *context, int32_t typesize, int32_t blocksize); #endif /* BLOSC_STUNE_H */ c-blosc2-3.1.5/blosc/threading.h000066400000000000000000000077441521743436100163660ustar00rootroot00000000000000/* * Code for simulating pthreads API on Windows. This is Git-specific, * but it is enough for Numexpr needs too. * * Copyright (C) 2009 Andrzej K. Haczewski * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * DISCLAIMER: The implementation is Git-specific, it is subset of original * Pthreads API, without lots of other features that Git doesn't use. * Git also makes sure that the passed arguments are valid, so there's * no need for double-checking. */ #ifndef BLOSC_THREADING_H #define BLOSC_THREADING_H #ifdef _WIN32 #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif #include "windows.h" /* * Defines that adapt Windows API threads to pthreads API */ #define blosc2_pthread_mutex_t CRITICAL_SECTION #define blosc2_pthread_mutex_init(a,b) InitializeCriticalSection((a)) #define blosc2_pthread_mutex_destroy(a) DeleteCriticalSection((a)) #define blosc2_pthread_mutex_lock EnterCriticalSection #define blosc2_pthread_mutex_unlock LeaveCriticalSection /* * Use native Windows condition variables to match pthread condvar semantics * more closely than the old custom emulation. */ #define blosc2_pthread_cond_t CONDITION_VARIABLE int blosc2_pthread_cond_init(blosc2_pthread_cond_t *cond, const void *unused); int blosc2_pthread_cond_destroy(blosc2_pthread_cond_t *cond); int blosc2_pthread_cond_wait(blosc2_pthread_cond_t *cond, CRITICAL_SECTION *mutex); int blosc2_pthread_cond_signal(blosc2_pthread_cond_t *cond); int blosc2_pthread_cond_broadcast(blosc2_pthread_cond_t *cond); /* * Simple thread creation implementation using pthread API */ typedef struct { HANDLE handle; void *(*start_routine)(void*); void *arg; } blosc2_pthread_t; int blosc2_pthread_create(blosc2_pthread_t *thread, const void *unused, void *(*start_routine)(void*), void *arg); /* * To avoid the need of copying a struct, we use small macro wrapper to pass * pointer to win32_pthread_join instead. */ #define blosc2_pthread_join(a, b) blosc2_pthread_join_impl(&(a), (b)) int blosc2_pthread_join_impl(blosc2_pthread_t *thread, void **value_ptr); #else /* not _WIN32 */ #include #define blosc2_pthread_mutex_t pthread_mutex_t #define blosc2_pthread_mutex_init(a, b) pthread_mutex_init((a), (b)) #define blosc2_pthread_mutex_destroy(a) pthread_mutex_destroy((a)) #define blosc2_pthread_mutex_lock(a) pthread_mutex_lock((a)) #define blosc2_pthread_mutex_unlock(a) pthread_mutex_unlock((a)) #define blosc2_pthread_cond_t pthread_cond_t #define blosc2_pthread_cond_init(a, b) pthread_cond_init((a), (b)) #define blosc2_pthread_cond_destroy(a) pthread_cond_destroy((a)) #define blosc2_pthread_cond_wait(a, b) pthread_cond_wait((a), (b)) #define blosc2_pthread_cond_signal(a) pthread_cond_signal((a)) #define blosc2_pthread_cond_broadcast(a) pthread_cond_broadcast((a)) #define blosc2_pthread_t pthread_t #define blosc2_pthread_create(a, b, c, d) pthread_create((a), (b), (c), (d)) #define blosc2_pthread_join(a, b) pthread_join((a), (b)) #endif #endif /* BLOSC_THREADING_H */ c-blosc2-3.1.5/blosc/timestamp.c000066400000000000000000000043751521743436100164140ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #include "blosc2.h" /* System-specific high-precision timing functions. */ #if defined(_WIN32) #include /* Set a timestamp value to the current time. */ void blosc_set_timestamp(blosc_timestamp_t* timestamp) { /* Ignore the return value, assume the call always succeeds. */ QueryPerformanceCounter(timestamp); } /* Given two timestamp values, return the difference in nanoseconds. */ double blosc_elapsed_nsecs(blosc_timestamp_t start_time, blosc_timestamp_t end_time) { LARGE_INTEGER CounterFreq; QueryPerformanceFrequency(&CounterFreq); return (double)(end_time.QuadPart - start_time.QuadPart) / ((double)CounterFreq.QuadPart / 1e9); } #else #include #if defined(__MACH__) && defined(__APPLE__) // OS X does not have clock_gettime, use clock_get_time #include /* Set a timestamp value to the current time. */ void blosc_set_timestamp(blosc_timestamp_t* timestamp) { clock_serv_t cclock; mach_timespec_t mts; host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); clock_get_time(cclock, &mts); mach_port_deallocate(mach_task_self(), cclock); timestamp->tv_sec = mts.tv_sec; timestamp->tv_nsec = mts.tv_nsec; } #else /* Set a timestamp value to the current time. */ void blosc_set_timestamp(blosc_timestamp_t* timestamp) { clock_gettime(CLOCK_MONOTONIC, timestamp); } #endif /* Given two timestamp values, return the difference in nanoseconds. */ double blosc_elapsed_nsecs(blosc_timestamp_t start_time, blosc_timestamp_t end_time) { return (1e9 * (double)(end_time.tv_sec - start_time.tv_sec)) + (double)(end_time.tv_nsec - start_time.tv_nsec); } #endif /* Given two timeval stamps, return the difference in seconds */ double blosc_elapsed_secs(blosc_timestamp_t last, blosc_timestamp_t current) { return 1e-9 * blosc_elapsed_nsecs(last, current); } c-blosc2-3.1.5/blosc/transpose-altivec.h000066400000000000000000000070041521743436100200510ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team and Jerome Kieffer https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #ifndef BLOSC_TRANSPOSE_ALTIVEC_H #define BLOSC_TRANSPOSE_ALTIVEC_H #include #include static const __vector uint8_t even = (const __vector uint8_t) { 0x00, 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1a, 0x1c, 0x1e}; static const __vector uint8_t odd = (const __vector uint8_t) { 0x01, 0x03, 0x05, 0x07, 0x09, 0x0b, 0x0d, 0x0f, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, 0x1f}; /* Transpose inplace 2 vectors of 16 bytes in src into dst. */ static void transpose2x16(__vector uint8_t *xmm0) { __vector uint8_t xmm1[2]; xmm1[0] = vec_perm(xmm0[0], xmm0[1], even); xmm1[1] = vec_perm(xmm0[0], xmm0[1], odd); for (int i = 0; i < 2; i++) { xmm0[i] = xmm1[i]; } } /* Transpose inplace 4 vectors of 16 bytes in src into dst. * Total cost: 8 calls to vec_perm. */ static void transpose4x16(__vector uint8_t *xmm0) { __vector uint8_t xmm1[4]; /* Transpose vectors 0-1*/ xmm1[0] = vec_perm(xmm0[0], xmm0[1], even); xmm1[1] = vec_perm(xmm0[0], xmm0[1], odd); xmm1[2] = vec_perm(xmm0[2], xmm0[3], even); xmm1[3] = vec_perm(xmm0[2], xmm0[3], odd); /* Transpose vectors 0-2*/ xmm0[0] = vec_perm(xmm1[0], xmm1[2], even); xmm0[1] = vec_perm(xmm1[1], xmm1[3], even); xmm0[2] = vec_perm(xmm1[0], xmm1[2], odd); xmm0[3] = vec_perm(xmm1[1], xmm1[3], odd); } /* Transpose inplace 8 vectors of 16 bytes in src into dst. * Total cost: 24 calls to vec_perm. */ static void transpose8x16(__vector uint8_t *xmm0) { __vector uint8_t xmm1[8]; /* Transpose vectors 0-1*/ for (int i = 0; i < 8; i += 2){ xmm1[i] = vec_perm(xmm0[i], xmm0[i+1], even); xmm1[i+1] = vec_perm(xmm0[i], xmm0[i+1], odd); } /* Transpose vectors 0-2*/ for (int i = 0; i < 8; i += 4){ for (int k = 0; k < 2; k++){ xmm0[i+k] = vec_perm(xmm1[i+k], xmm1[i+k+2], even); xmm0[i+k+2] = vec_perm(xmm1[i+k], xmm1[i+k+2], odd); } } /* Transpose vectors 0-4*/ for (int k = 0; k < 4; k++){ xmm1[k] = vec_perm(xmm0[k], xmm0[k+4], even); xmm1[k+4] = vec_perm(xmm0[k], xmm0[k+4], odd); } for (int i = 0; i < 8; i++) { xmm0[i] = xmm1[i]; } } /* Transpose inplace 16 vectors of 16 bytes in src into dst. * Total cost: 64 calls to vec_perm. */ static void transpose16x16(__vector uint8_t * xmm0){ __vector uint8_t xmm1[16]; /* Transpose vectors 0-1*/ for (int i = 0; i < 16; i += 2){ xmm1[i] = vec_perm(xmm0[i], xmm0[i+1], even); xmm1[i+1] = vec_perm(xmm0[i], xmm0[i+1], odd); } /* Transpose vectors 0-2*/ for (int i = 0; i < 16; i += 4){ for (int k = 0; k < 2; k++){ xmm0[i+k] = vec_perm(xmm1[i+k], xmm1[i+k+2], even); xmm0[i+k+2] = vec_perm(xmm1[i+k], xmm1[i+k+2], odd); } } /* Transpose vectors 0-4*/ for (int i = 0; i < 16; i += 8){ for (int k = 0; k < 4; k++){ xmm1[i+k] = vec_perm(xmm0[i+k], xmm0[i+k+4], even); xmm1[i+k+4] = vec_perm(xmm0[i+k], xmm0[i+k+4], odd); } } /* Transpose vectors 0-8*/ for (int k = 0; k < 8; k++){ xmm0[k] = vec_perm(xmm1[k], xmm1[k+8], even); xmm0[k+8] = vec_perm(xmm1[k], xmm1[k+8], odd); } } #endif /* BLOSC_TRANSPOSE_ALTIVEC_H */ c-blosc2-3.1.5/blosc/trunc-prec.c000066400000000000000000000065051521743436100164700ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #include "trunc-prec.h" #include "blosc2.h" #include #include #include #include #define BITS_MANTISSA_FLOAT 23 #define BITS_MANTISSA_DOUBLE 52 int truncate_precision32(int8_t prec_bits, int32_t nelems, const int32_t* src, int32_t* dest) { // Make sure that we don't remove all the bits in mantissa so that we // don't mess with NaNs or Infinite representation in IEEE 754: // https://en.wikipedia.org/wiki/NaN if ((abs(prec_bits) > BITS_MANTISSA_FLOAT)) { BLOSC_TRACE_ERROR("The precision cannot be larger than %d bits for floats (asking for %d bits)", BITS_MANTISSA_FLOAT, prec_bits); return -1; } int zeroed_bits = (prec_bits >= 0) ? BITS_MANTISSA_FLOAT - prec_bits : -prec_bits; if (zeroed_bits >= BITS_MANTISSA_FLOAT) { BLOSC_TRACE_ERROR("The reduction in precision cannot be larger or equal than %d bits for floats (asking for %d bits)", BITS_MANTISSA_FLOAT, zeroed_bits); return -1; } int32_t mask = ~((1 << zeroed_bits) - 1); for (int i = 0; i < nelems; i++) { dest[i] = src[i] & mask; } return 0; } int truncate_precision64(int8_t prec_bits, int32_t nelems, const int64_t* src, int64_t* dest) { // Make sure that we don't remove all the bits in mantissa so that we // don't mess with NaNs or Infinite representation in IEEE 754: // https://en.wikipedia.org/wiki/NaN if ((abs(prec_bits) > BITS_MANTISSA_DOUBLE)) { BLOSC_TRACE_ERROR("The precision cannot be larger than %d bits for floats (asking for %d bits)", BITS_MANTISSA_DOUBLE, prec_bits); return -1; } int zeroed_bits = (prec_bits >= 0) ? BITS_MANTISSA_DOUBLE - prec_bits : -prec_bits; if (zeroed_bits >= BITS_MANTISSA_DOUBLE) { BLOSC_TRACE_ERROR("The reduction in precision cannot be larger or equal than %d bits for floats (asking for %d bits)", BITS_MANTISSA_DOUBLE, zeroed_bits); return -1; } uint64_t mask = ~((1ULL << zeroed_bits) - 1ULL); for (int i = 0; i < nelems; i++) { dest[i] = (int64_t)(src[i] & mask); } return 0; } /* Apply the truncate precision to src. This can never fail. */ int truncate_precision(int8_t prec_bits, int32_t typesize, int32_t nbytes, const uint8_t* src, uint8_t* dest) { // Positive values of prec_bits will set absolute precision bits, whereas negative // values will reduce the precision bits (similar to Python slicing convention). switch (typesize) { case 4: return truncate_precision32(prec_bits, nbytes / typesize, (int32_t *)src, (int32_t *)dest); case 8: return truncate_precision64(prec_bits, nbytes / typesize, (int64_t *)src, (int64_t *)dest); default: BLOSC_TRACE_ERROR("Error in trunc-prec filter: Precision for typesize %d not handled", (int)typesize); return -1; } } c-blosc2-3.1.5/blosc/trunc-prec.h000066400000000000000000000011671521743436100164740ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #ifndef BLOSC_TRUNC_PREC_H #define BLOSC_TRUNC_PREC_H #include int truncate_precision(int8_t prec_bits, int32_t typesize, int32_t nbytes, const uint8_t* src, uint8_t* dest); #endif /* BLOSC_TRUNC_PREC_H */ c-blosc2-3.1.5/blosc/win32/000077500000000000000000000000001521743436100151765ustar00rootroot00000000000000c-blosc2-3.1.5/blosc/win32/threading.c000066400000000000000000000071001521743436100173050ustar00rootroot00000000000000/* * Code for simulating pthreads API on Windows. This is Git-specific, * but it is enough for Numexpr needs too. * * Copyright (C) 2009 Andrzej K. Haczewski * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. * * DISCLAIMER: The implementation is Git-specific, it is subset of original * Pthreads API, without lots of other features that Git doesn't use. * Git also makes sure that the passed arguments are valid, so there's * no need for double-checking. */ #include "../threading.h" #include "process.h" #include "errno.h" #define PTHREAD_UNUSED_PARAM(x) ((void)(x)) // The following typedefs are used to make this file as similar as possible to the original code, // replacing `blosc2_pthread_{xyz}` structs with `pthread_{xyz}`, so that future bumps of this file // will be easier. // For function names we explicitly add the `blosc2_` prefix, without any typedefs or macros, and // therefore we will have to update the code manually next time we bump this file's code. typedef blosc2_pthread_t pthread_t; typedef blosc2_pthread_cond_t pthread_cond_t; static unsigned __stdcall win32_start_routine(void *arg) { pthread_t *thread = (pthread_t*)arg; thread->arg = thread->start_routine(thread->arg); return 0; } int blosc2_pthread_create(pthread_t *thread, const void *unused, void *(*start_routine)(void*), void *arg) { PTHREAD_UNUSED_PARAM(unused); thread->arg = arg; thread->start_routine = start_routine; thread->handle = (HANDLE) _beginthreadex(NULL, 0, win32_start_routine, thread, 0, NULL); if (!thread->handle) return errno; else return 0; } int blosc2_pthread_join_impl(pthread_t *thread, void **value_ptr) { DWORD result = WaitForSingleObject(thread->handle, INFINITE); switch (result) { case WAIT_OBJECT_0: if (value_ptr) *value_ptr = thread->arg; CloseHandle(thread->handle); return 0; case WAIT_ABANDONED: CloseHandle(thread->handle); return EINVAL; default: return GetLastError(); } } int blosc2_pthread_cond_init(pthread_cond_t *cond, const void *unused) { PTHREAD_UNUSED_PARAM(unused); InitializeConditionVariable(cond); return 0; } int blosc2_pthread_cond_destroy(pthread_cond_t *cond) { PTHREAD_UNUSED_PARAM(cond); return 0; } int blosc2_pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex) { return SleepConditionVariableCS(cond, mutex, INFINITE) ? 0 : (int)GetLastError(); } int blosc2_pthread_cond_signal(pthread_cond_t *cond) { WakeConditionVariable(cond); return 0; } int blosc2_pthread_cond_broadcast(pthread_cond_t *cond) { WakeAllConditionVariable(cond); return 0; } c-blosc2-3.1.5/blosc2-cframe.png000066400000000000000000003155631521743436100162740ustar00rootroot00000000000000PNG  IHDRД 3 iCCPiccHP7zW)Pz%PD ,+ ZY+,bþAu],ذo{ɹs{gPI$"@8W͎O`@M9NXX`6>dda9WSsHɂ~Ƈ/j1\w`" X6©cvGOX LtSb:@=`lH0wNN'orM/Uc{5(Gɛ̼ؠI#Y;;r'g`4~Ǚ0o|m挐qNs:ܨqE4;B+Egt"o^Fܟ& Ӣb9_3cs2"'b|~i^~8{"|Y9WĕM 7QP̙̉&MD%\0y03@ɏ.0Ab6/؂X#Dvj‚ܑ dKKEil҄lo5mkmk0nǮśe®;쭬%4ߛn`4c>0S*t7?Px |H,Ƞ aAHa!.bX 3!!H$!C"+R Bv uȯQ4rB"=P:jTh: ME碅hDk}hzBest8N 9|p\ N[+Ujp V\;N{'Yx6G*|- ߃%0Z +K# ݄#s[>;"F4!:tjVb#K"H$ ;)#咊IIH'II}dYlK'' ^ uS0EbDqR]VUJeD5SeJj Fӧi"RZ%L7y5=S7 ØH`20gX V \j& /Ü,dV012_(R}y*v+)lBV+UԯLR6VS()T>± X>,>kkObUIW)Uٯҩ2jZZz\US3VeU;v[$III&5L>du/uzz-Ol ? 54i|1Yed'B̵"hЖho>BGMK']\΀.KCW[{R[ag+gكzZzzyz;:M7?48 N7\hXoxψblfɨ轱qJf~uIIS\ӛfD3g fQs4jbE))5S-|z+5VV/NM~jԯֻ֙(,iymkn˷iǰ[bb^haJ6/NR'C$-N*aΫ/\]sz/7K nL s.`{$y!yx>2xz1sq^z[{KxqYs[W_?տ0!`A@B`pn6ϭ9- :L ~b" iNaF33C!!aIܰ‰aO"l"FG"D|6~it^t[ 3&1.}olY,njܢRBL~37KtH,N=dV5gg>>97P!)6iog^(7Mޒ|Fa)iB7 65Z[mkuk=o{>z| ꉢN:%9t޶9mĝy6ls?i?y±^r|rSCǑ+Wt:v6]urZk״={M˷f}NwbN̻ACŇn{QvǷq ISݧu=?ῼ{%}7o66]ֻ%4>~t)yI+}io$<)oaMIx@9W2-0Jx5GQ BlZLnx<گ `7I~acwusU{/&j cHRMz&u0`:pQ< pHYs%%IR$tIME  N XzTXtRaw profile type iccXi:{ %.GTVRx@Xe9Gs^ȹ+cŦt.]TTI)VBݍB-rۍf}>ohaXzE*קES9\YÔY_76X|`&jω.EKF#:&N2?Y˟KI1$e|?C]ƫholM??Qi|ND K~&{hœωTMTӢ*bE>?&"\,sDEG)_b/|zߵs"VTGGg",_VKI|BݯKo_-~,Xڧr&K 3QjQfiͥ} ?v/_Vࠥ/K;u=~L4~H3o./f3!;3@1&QvPăMi$ (f sW=ꯂۍ 7˓r`Ysi&ͧ^|/EƩ{\KLvݸ%gf{{<c!HtuhD܇8C1t$xACj;49ד _2&0&0&P<S;4˖ItmfKS L8W3gZeUMM^4a@x5< Xޓ[LRA\gOd%32g9Wy `1f;j.2S.oj}eݖ4l抬mQrmZ;HƋlpWɬb+<=݈hHo;0_HamBЀТrYM>^Q}:Ԇja5BK[v={yS]H)a,#k^QUO.<_ ԃ#p!pP(BT)k@h qX{chxiNj%6lpuB99ڐA`Vvbq(cӼ׆ALҲvVִ2]=ic  8J' 6ӁQ CRSIxdyeUF dLJ+HbP ;kۓWV"SO<:gw:]á1@)dw騱 GlbCG+@!(Ȍ'?2T&alPuUhzɳ @kc^!6%Z ,"G*^uhT`J գ_7:`:CI#)\H` 6\W#כlJt6^ycƞ0m3l(go0a>Tt-Ym3ifr  >co{2T%@QtI)ne]PP3 ?akH*ĝϋ,hI ֏ dj ޳7X0w$"o*f xM}UL)TeN^:BNҐecUoIWF؅CPJ>QK"p;6Xtk J*dĥO>Y!ujTṛ>j`qPڔb;0yǴqXH[ViäJd( 3n 87.Xl,2X l';9k1' &8{2d!ƚġpY-O ;-hnpaƸ@],]8Kn*}{/: l k!y#.à_LMȼ lIa$%Dc}@Fe6qDGxB9ZDs w@kwmW]'hƘF!u( wrT4_Ȗ{M0ugukǷ /^zwF;B!,G|f[鿍||dauσm_kǟ~| q~?>ٿ[u?`.MY|vgǗ6zu\_WP8zTXtRaw profile type xmp8TKv0 =d.{Li#[ь@Mq0^db'[,)l%daVc2m蕙4 H.IVWIfi %0LUIF +ĵQ82W$ %( /AǽcEc6… o 'BhP Rj5n7~?[prÄyu+㻃;ѩ_4eD <'A9'*M. vλKUo 5J[-]A}ޑ%RS,uJK}p 9mHu݋*k<%h}wd5cfL=AYq\"N- n_QorNTϢwbKGD@IDATxXƥ"DA@]l Ě؉{&V]o~Fcޢ߻3wfgPf<;3{vn$D"H]9( H$D"H$D"!H$D"!H$DB"H$DB"H$D"H$D"H$!D"H$!D"H$BD"H $D"H $D"AH$D"AH$D"H$D"H$D"!H$D"!H$ ͛7/]tĉ6lW\/ܽ{ݻw+rzOu޽ORʐ?z~իWWNLLӧ߫WEyFϞ=;uTJJŋ'L0`ÇϜ9399СC(߆VΨ?P~]~}\ݻq㐭APA}LLQe``مh~77nx `p;wLFԮ];444<#|rJlDGGj|޽{qsr\\\ }Bڴii&^r-?ˢEn۶z<=:#zf'NreŊ":a{v׵kPWC{Rݹs' G }_Jl*䝱1s>uϑ#GtppPYBU tyMK0ʕ+)F%Çg]yqJ=M^_}>ydѢE]+ mWPhAHݻw2 A&NHIu| UVʔ!CQNr9hРǏ+2X&%%Q.);w.o޼]ϳ"7`ffwi300Uֵk׾:޽F\CCC AH $k/FEE) QԩS'C Gni!פ;vkRL{e!( 8P?P]]]+=f̘/#͛'2ZgCZ @ׯ/HV7otMϙ3'jrC;gΜBkA@L1669BvZZ2V{ׯ?zӧ9dɒƍʓ'2r֬Y"ܹs#""J TD^zюҠT-|AJ%d:{K.GgӦMǎC{С ԫW]G: ?T  쩣>[4~xaV-[V  7n(R jǥKO?۷oA+h A^zH,"} RNԯ^:te} d63~zÆ @zsΔqQ'Oe~d :;;)S(O?={f.PիW\={B" k&oN_~wOOOќ… 3f ͕+= CUpaCҩS'ʸ쨗/_֨QC5kßa#ÇED^޽tAnhh~  qE \6gΜsO{ T@WJԔ#F%'')swrBLax&ӦMF388֭[z0ܳ"ŋӧOo޼yCWߣGDcQ#$Hft D~Nr!۵k׊+ڵk'(S|2#|*ݾ}ѣ7n\|9ԑ#G]eLQoCi˗-[$]u(?3J):O;v{˕sDٳ-#Sb/ hm-,,E ƍ*? anРΝ;>?}ի͛ 50M^^^UT=z4["TU UEm (n炝)SHpTdo$,:u 4YH’:5 m(cPbFF@|st#' V (W\͑VQH/ zbΡNɖ-[<== .w߁'@|hn'ɓ' V )wаϟ_ 1(F;vm~g"5D& Y۶m/YcG S*Q:@iTT[MiG `$}OŊ8 #Rݽ{M,n3cz. ]LV =G}o=6~4 0FS@̐LE$t"3~;s"_{Ўo߾K.>E#%Ph(ÜBʠ;*Af܀N-ʡ֨a)U4w`Qufܹs+V fe(l2AҼ-5esCehV⤰[+ѣGf۷o"zbH.7qBT(܏o,YuE镮3gVLt죮:#9dV`N9sN'!NT0?66V;6Lh)TΝ; [.WH0t r I\Bi.q4h-@MAN:UR%\hկ__a\t>QL 46Cv0NO H"Xnћ#ѣ{5q`p3|||D7תU {ђ2p@`\5S*RF+߳gO5* ޽{ޏܞ&d N$///@d[@"@愄 %>Z F޽) FCSXaCjBZ|ӝ;w233Cڵ-_{F"VN%JTbϟ|˼prWLQضÜXYfAC4 "K!ȉ'ֻiUٲeZLEܩS@Hzo׊ (ED]ШQWZ%}\LAx{G׫WOdI)וB7ͱV3(@?~``L^;bAT,Y?>m4]xHMAк?| V0>S77nkVwAXܹsӺHcgt2wlX׆ RI5j!vک!񨫦AU) s-At)hmrww6/fT\}ID `ߺuKRAX ׊ ґN!i ~p:-frC؆ ߺBB+ћ* }_itDWi֬%Bg",<} %SapnݺU H]1WaСҔAYE#c: \`PNI-~ t2 WfZ5>|\Fږ.]e˖m۶TDkݺtWAŊCJ̏/JLLD{Hg'GSWp? l#Ȉ#]B4r%J(џAPmw.$Zr_mڴE/&K!zR/ ]tٻw/|pmz`ap[ݙ ]\]]#""\p$ߢXavEFJJJrr!C䆾'YGeAݯWu͙3| G8qԩS)44T$ɓa#:u$i/''iۣG,X̎+XYYKժUY""+,h=xn#Jr2eƏ%sŊhΠF A; 5WҔpF-\3FΕ Jݿ?!5@h7nܸlٲVZ1GhjshV/<<\'g (Lȼ˭uڼy'OPx!{9@cvGrY={%:iݢ*yNfN!pd?ŸP x9#'F,u&7"_eV`-AHgϞOWF uK-{!B"M6eDa>ET={2'QAZ(UA,!s#СCp4 RxxH}2FGG3DRXԩS4aZe&\htv%fĈ'3# sj` \̑s///CSZ2JNN a#fN"bcc5l`$޽{gϞ k'7ރK:&'ݿ- hL~h3:L/At XLOHhL @AAt@@iVؘУGpES* !cΝӦM駟vbΜ9S:_rek!I|b(\0$# 8]$i- KkՀ~G)  0ib\cn*?$mN Hڵ\t0Ms;**JG#ȪUc Țɓ'+)ZiWzw! uX\n]ݻoNlKVCRJٿU߿\FvAZP/Dc`rҶm[Hҹs瀤 ;!\vC6lKfN͓Z2tmK7 H,> =;"HZ2\ ~wK3B "rf2mb.ESL/( sFoԩ \LSaHaʡ_2?~y*(,[9Z& An&f͚>}! S9 "וv횿N"Agwggrʕg `CΧDᑎ(L#fLȩ)& <& WZS aΞ=b [*UVTN mmFFF?~Er=%%9_bE8f6M6UV4#E9^ 2=+ bzIJdrEY:ryiV"QrRreaz"A!azERk%HfAХPpЎS:DXn=A0uhU͡ԭ[W} '&|2M(o޼:E6)07n0UJ=(9R_PPӧ@ qfrt%CAPC1:0IIIjЩYh?7&"X[[߿_}"FrG0GMDru=TxqQ3hp[DbŊ0`ʔ)'Os;]5P_uC6?e̙3a:5,T 7=Vg!}s ԜN f Ak{Ν.,s~֭֭[lm(<r03-:_+RMU \"ΨM * k!T4,dY s|=o:ӧό3~wsV?vTs~Ph0ի޳~QQwV %{#kl*TH$:Hq0~V1#h lѣa!C͚5S33Ei%T( QuE': B œ2!bgA{S~ (lrJGA2tR鐒V_s^G Ŏ;> 2co Q܌߼yVZIp¢&H*#:p:!s)~B|^I{xx(Z6f9) wFEVkN0֗~~A e (F_HN@3LASo>pMZ錠aÆf$ ȹ3fH={l)SF4ڱcG怭VlR3QA[)fe[ uE tVO=DFF*tkkAsy9C1}OY ;z78qBjzݻwo~Pn*=1SA`e!Y&$$0dD?̟?YM0}-R^dAdvP"o54H' As-C3;k`˗//4Q8o4䎲ڵ%K`fjٲeihJV@/Jի/UVm߾IBI-7H [f+0Ub nq1G\f3wiDn,J0[z)^t8sGL>}TAK.1[nۘbaa0m &z:!pVرckB 9::*/0`@6B$NNb \@V7oơYA`{Sh.1wI=vBn,S呢H<5h[# .heFرC:tlvU4rVexذaL4\p4MDAdNcR#G2hC0fCк#%%EI| I'4h ]adիYmgzWr߼y A-Z$}PܚA(x{‚+VH1_+ 2!oD6m0{IA3Lɰ5kH'b@"йsguK1xyy)`̣-PerB4ϐE-A tXH9OSNg֨Q# "{ݺulEH F̢_`To 65r… + iИe60*S:G&:׏I~La_a2i"sb[(0 h:cE~fSX!6uh~HTu+uҊ_GЙYBABV~zۗ$(]رcJ6+~`I錻p[&qFvJ&MN>- ٤+ݸ&tf#ȲeˤB{yF;(lT"L5xfJ"hكGݼy3B3tG%:|0w\s#A̗| )cnC/MZ< f)=?QX1"Ft/?áWn'R#D&BKh6}ƌ [d#...ZL ׯ_/^BKXAڛ-6X5ZRN%xҩS&Vɍmp-ރ%bc$=D^J,&Vn6k"5(]*UԩJp,|uPzu %ܡ|6b*]ݯB@ۧ#S\~ޞЍlBj=T )\ZA> 7#w}iGQ4kժ L驓SmF$ sG lmz mN <{,>>>6F4u[c<,F:tO^|!!!ha<~9eqZ[5}t~Gԗ*&p]0=ץ-_\45s(dye={ʥy5=.]ZaKaf#Hjjց t[uB(rkKII"6A12HDA޿NyZR%`^0f T=mڴD5M1c*<%]pu Z~BjP!T, Ҷ=)͛7O@XaTz(ں֭[+ S=N Ҝ-\rLΜC̅ B=֭VҍوӧpjƍʋCi8JyǎL;JrEd.5PFӐ!CN0 Jma~@Çh@ S'rz&(0ТRø8a~㟭ZR u`Z@@m ~]F~-:RAU.YbŊf^zu|ˡ4Hq&&]v!|AH:D4JϠA &,!R !JROn|j*_: WR%QPQB+supuGTZǜG Q1CGO466&z(T 1QDodȝYbVg___ {:"OR Bڴi`bN#;w*}IҥKQtJ01?Y ң hx(0 Kwڅ|G֭[)~e[֭[9pB%D[ A5+=aaarƹXbDuKΜ9Jt?rDakQGF WI}JL  ux>vXBN3rȅ  n߾!Eo az٣GQF͝;p'p›֭[~aoJ<R2{)`sQ>}Trڡ-F%666 `JRfH+VZz% mhhhmmV'"""..SNr ;ЧСA܆H,%cAVH-*ZӶrJ=^EرcrI۾};sɺh7DʲZvh?***C&;H$v!Z"_|y//)So~k׎92j(n˥aW$)ѣG]vvv{!eD$ PO )55cǎ r F WZ~]/I"2UK.m8ҥKf~%}r/{>aԉ'+V/u׮]$R6ңGD'ϟ]DOf:2SLm _/^ҩIJuHlY`ҤI,b\d_DDNy A.[L3|}}gΜIS0$Rv˗/4i"2DZng`WxBB҇g۷PBZO aaa*H'N0220I/^ FfBݻ;wZjU~5jTV^zgnf͚o߾􌧑H9sTTJ*V8iҤ qҸqofLB4 ߰a]BAH$BϤ޽{ yl ;vAHӦM# &&&|,LMM7nHe.BDB!DBB"D"!!!}(0XldMhEP AH {z355]~\mG?G')H$L-P_>\TM{Rh]yN`vB!׊ ^7unݺlٲ} HJJxCɓL6Gm۶m޼yVv:jԨu]vMܹ;ڵkӦM7l|e\tҊ+nݺgϞӧO?z/~߾}uU|xq!.:u8q'|Cq۷ӧTq,$OaH ,=`ڃ *I3lCB"|A9|pÆ s|*{+W(lڴ)&&>K9k tbŊN۶mr!Cϟ_+Vv{J4Ifi2eʮ]/v߸qp–3OĝժUCDohҤpeɓ'0vvv*Th޼9/D(!!A(E"ED2H?CA4uTw֭[PB`2P*ҷ ߥXi6oj"I?!icUjO%}բOeGrs|^D"AAF%:u9sU~Zj o@OAF/]pްw^ K~ }BnݺgϞ{r1WWWdz?b9rV =zٳC $ w?(|8gΜoG) nly՝;w^*oE'N8;; [.U/_!ȋ/*V(L2NGݷo 8!YB< 4?l<+N/G3w 9'4ёxء[1;n@{f OyӎE9}5K(#A100 ,\PɬYnݺ%\ӊN6M$O</^ g.]cǎ'ⷂDl9І -D>|\r%_|oԩsׯqѣGE͝;WAlllvMuD$ N?\5`S 4Erp Gj蝷H<yT}A( cux;|7]}$9"e!B*AH$Bt#,ʕ+>|x%|sN8Aƌ#ܹs ZO|(/ݺuXYY GA.]*|3hݺü'%% "ș3gD[+s}}}En' S Qv92-Jh |[vD~"fմd|%!\ M5.=ȇ[pELJ?EM+?1vY<>?+NB"d/lS9۷ g[V^]dIh (pu3fyZKIIСC]3Zչsg9x+EH r*?"8WYD @=׵>Q9-b|ԪR}Lm5uҨ`_Y !A2 AU?122Zv-ogΜ)l“_͚5ݸq{|ܹHעrp!ȯ VhBABCC>|HuDV3AP7[~m3 |V %SZi(9}-+լK"fM2cVFn $!HFю\nРA5xg,—X[[Ǝ;vW^MMMo޼=j**0^ ,9}(%KMA` ZI …{xiJyN2b 7D$30)$o\)1~fˏ<൥3v'u?8F[JB"d].G]7TJ1uTipC ۔gѪ_D;{~ )RA}xXL\ ۺ^6@ElcCBI> ڊ X6^ͪb`=_q@"!Deȑ#oDK,ILL\p}.]Cν?SfMJYn٦M9r劈 . +l ooLr̙;w"D\OΥS50q ! fڦ&10,7fe!H^ ^V AH$B 2eussep:&VFܖbݽWkN^B x|t"@kѪ 4}y10̡y(np἖M;bH$B 畅W\R L 1wrr%'O-*44TC"aÆ*۵k'A_ݙ+W#GP-&b|99* =6mI~KBm`>5.bbiTW^aC+ r.؉pS.Esݧ}kfo5Z^BD?Sh)ѹOIIQF{{{#M4gϞ(teٲePׯ{{ҤI'Z_'TTTB. D!uV6mN.]AD3J%KT@k׮Ж3gNBoADn|k8k5~>Nh<ǹ@zGk92?AtU _UGCMet6A |k8U=}) OB"W&LO`HH?`9?0W^aǎbnnሡ]PPЏ?xi_9x`zݹɓ'˗/X 99y߾}7oԕիW0-Zl֭[<[a$׮]۶mҥK;wNG8x;V^XU7o>yU>YbAB"H b e("ISDB"AAAH$BG#3BB!D"A=.OjY904J[JBB"!H : S\#݌<]]iv`[IJB"H Y_t=!D"EB"H tH $.BD"D"!H tHA޽{ٳO>E_xׯ GgA| 9/ D H(|HMM%D"~VZy5c %&L,W\F޽]tݻ7B$$$xB'O ώ1Dt͛n݊a?|ѣGۥ;ڵkԨQ:uj֬Yv~[n .̟??!!϶mvС{}ׯܼysݺu@L#Ǐ=rI.Ο?ϱ 3gN[hѦMlܸ޽{ZΝ;Ӽy֭[.#0.8?@;vl۶m cccD zCv:uJ gϞ|uŊh[l_۷ʕ+_$; .$WdaFǯ^yiY$tҺz"H455577/P, !|bI -B Y"&h`oAM`~S.ޠA 踋\v=w -=S0cm۶e޽۪U+|?/ 0xV!_cNJ7|͚5<Rʕkʕ_ur\y֪UחL{nݺ"S=ƍNubLlE~mɒ%Ƿnݚ3gN[~ ɓ')/ sk)k!ӧ;99q7xyy;v gwŋ+VxxxpOᯯYʊJJJ3g@$ v R `d۷m…[`AZ.%HK]+_nV.&&(٥N}~S>cfKL}-&8 ɖD@UQ0 R zZnqCA6>z:uu'Of~펎m=uqDٹ? *=cm/StQ$$$KUFϳ$$$&&&Daׯ_G* M܊T'6la'~MX\2]tUa-LMrw/dq¡)~ <7niٲ%s{4i*VNЦ\M+O;U'/zr$kFaZ @ϟGO 3?̝;3g@B!_?]p?t2!7 ?41008p[ʕ+oڔ?[hѥKQFݻٵk:ȝ?Έ*@҇47M4ٳIx[NYv-Yɓ'A"RjfaS:lj7X3󈊊?رw>.ڷ & ֭[K7$kO'}fY|9J*_W`/ΏXlܸ1CȟΓ'OGJEY*&Z QԹ!] ×M`\IfJ4,%VV%˹ͣN3gvc}&Sa4k( V!6S/=׏O 4,kѢ_ `>N,Y|aÆ/_Hf~$G!:ECRhkr~4,t"4zh~tddhq A֭[ 'MDB!HFś7=?k2tjH)=?(ޛ2 4g{t4?1˧i9-B eh4ZX AFp1G[CE0T:B=t۞={ ;YǷ*Uھ}GӰ0cǎ;BE͛7y氵k _@~իW Bӏ ~~~;wMz?7,ZҒ_ҺuǏ#3g:u/ .̨ ?y:t(˖-Y&N W/^믿rYL!1.9JځԄ;rEOB?o<%8eԘyI'oi Acg7^Otk8õ(&m5MS#fBUϞL?æ:jBoA*J]m [ۋi[9nݺUV *H]l垞=...111+Q׿ Jp=zg˗G[:v(~^yHd>a團?>wx 2?(zb@D bgťGՅ5/zRA !~z:r!/j#bo'@GδZ+Gprκ8E3`]uM۸6yaSs;mOEAT͈.GX`hk&iSބ YAlFC~AL> ɼ7 FO> oW\” ptAm۶6X$u{DgRtBݻwK гgφ Ưa-ȍ=$>>@ —9vXEk,YoPx/h8B?'!'׊&&:x%e)05 c.\&V]O6!Y AR߿_pt?9w_Px#HuhAZ7>YExQ>|ܛo޼{;Ǐ/rܹB K8sBܹs9byXYY :{„ ij֬G_"E … WuA0F 8OIIuE#r\ s_z wZ~!:::<<Du=z p?#t.z 9;;̙irĬ'rEl'w ޟBoA`cm^AEYl>,xM+,2˗mۆgW\sׯs .U/} )7Ȅ{\dIRRׯ_+' 9>Ƿ}&Q!eA~a,>erg?<~8@ 98|]Sk[D䰂?x7geTjOVKgșsOou'&i>! ?qG#kA\s=¥pcS޶ lj)oW~ΆmW5ʙ+m*خz=~CU2xN`(c=zArmoGBHGd ЏY>?B) 鯿ٳ×/_8… K:/l ՖMk- },sH*5V|U_UIO;{i֩$il9Զt6xA|%ԥ2JcBy7,.X)\LYWF""<fJU)k-]ܗtJfl̼ۨk4B_S5é5! z/ ܫd*4f9N E%-jٞM9WbY2 /`-fg@|jZ"i}cj2!;.452 E+WTZU\.+J[[[~6Se 9”*q3/dd UK,,Y;uz@N ײa&M% Y$VZ16"e12d=U^y3 ɨS/7klJxp 4XA?C(EhyPYas0uYdNAYD=vT3eZǚr!8+bc3,IofP@pBvtYqtt0aBvIG &._6Mp}$*D;Xۆ͛)H@0}Ŏ8|2c+R_ 5#6LݱD=;n٤q鞙B:I ka3 @AxAe[:FƎs#_DkxUv='bH2TϞ΍Ws>IAt[8לɲӧ'[8V޺ԴrH[_Z6-AAneN7z>(Z*J&I.t+[cl-TnWw34O1=6Dd9l!$#m,ȄٖM۲yA52B?D)-٬^3.GOSfϢYL$X\L,)&Y6fmXi}K܆fgHMkղ=SP(P u{xTe,^KӬLABp6u%KPO8.U ͔YK.Ӊtu!> MIR] G SzG#;0^"1Wj72JHfVѠEvT_N6p{ZLL̫S>Ů`@YĬ|UODV;Wl"@9hjDml?9:K/5+,Dz}@Aq jn,BZ-d Al+8*7QBb,֛ɪ .5m bZ6FpS9PFr]BGfN1ÓFb,)S0\AN7xUDL0OG9/YZ|(()̵S=_cۥV'eX۱R ݸEd]R6ڳu"̲qkiV:hk"#&ξ(nUbX.B8J_0ɶ5sXJy4SfJP[\hũ5֐:}e撪% &FYK61)kxe.xwKW9?Ao/qn(W[棂L2sQLE1rt13VǮ`f:kcZμJmaԈ}e*RdƤ2 [Nۦ~Ӳ_הXu-aثtqsc-}&Uj*rԦiV@Aٗnʛw]6FZ$MAR\\iNu)H.".XY@ Jt1ˉ(,$5ԃGh]ǻ)P7C .Kf, ~a*7{]AF*VӶ@;=KCx4w|tԘ> K'rBEżT4 0 $l]#sI)FAvFub +HpogA.h_`UtQh-7̵ИTWKOu T dl|JvuB5ط~<(h(/]9K֗J)*Op<@4MZU 9,Q_A<[ D`VRݴW^B1m5$;L͇dĂhûŦ%ML.,1 GZzSgf-Gȼeon+ cA/ǏU*L?}4o\o?^HaƖ$R]o b)= ,KED]Z5h*Pev"iGE~+bE% ^|g4YNrqbuB g{\@A Xjժ_Sf۷{)\j}Ϗ|IRRR޶ѣGWRW^t PHFHn>p( * 6VSeVd{쨬)avf)%I6蕲;\ ݊Vfu˗W>m˅[y\|@A L0A/ҭsιܓ,--{s]]]ܹwO?M81o[NIIiذ!熂|}Z43f׭nL 쨪R:>t}du_ݠBZe;*}J-4YCS֒ۑN@ޖu_5fx6\`EUAy5 OtP(HTI&)s\%tttƇ P7fw^ti(Kn_:7ƥƒReINRmG1nj^ܾ3 $h%%VV-dHg/3~Z-`2636 }TnkAʍ3|((њ5krs.U 񇳳3k|СPP}M/}6.I>7f(Dl$b2 )1*\j_+)):1,$TY6Ff2_:7S*ȹ;.NTgl;/? P" l.A/_رމy /^tqq" y WT.%^aOp[tq42:6ߔ ksc|&Y,:jF BA+lIAHe4eƹ ^<7gDwkpͰ~1v{ n:)2v̞{bJ77nnnp1@Aޡ4Z_sg5fx6X^ Y=-m.)]=:ϻ:^~k8B8ƭꀪSJvwm ™ z(TW^o.D>X+6^pxOR>Db133ر\.\̲eH><o ^JMM}aIŋƇ ˇb1$-9tݷZ/w۹֢KɅjQn [sKHYi*lb[*H%@Tٌ^wR䝂yƝjtuͻ&} PB $>>>YCm۶f>}ԩu֖-[Ə߳gv{ʔ){ Ǯ]Y͚5۹s;>_~eɒ%#GիߥKΜ9 RA={F-/^XYf|pʕttɓыjٳg.\8dȐN:o߾o߾Tm۶m7oބm )"UA?oڥ阘7[BA E_ALLL"9e"~zY6?P( Ο??((>bffVZ 6} ÍD"~<BݻUV$+l`Fss5j}QA>R- laaQV-갅#:-b:jԨxL&#E*5YZZ]gz?_uإΟ?O*) bC7 X,߂Ą^ٳ  ^w C R:}m޼C6CmgwVBB*Utڵu IIIRǙCm$OIR3!4ڻPn2Q$իi9Gj/>@A EDA6m~ JbY|F͚5o)HRRRHH~ΝaѢE[~jjj.رc|dSN8΍Z) Q-3DBƎ˫@LL ݻeʔ9ԩS$"<ȑ#o~zzz B 2LxW,$[b/.!K]O(H׋Y.7+; ӗ:1sp ⸡ P 7nbiPԨQ#iTTNA!g Ç8ˌYAQ`QturZ޶m/Om-_\W\UQvڵkDX``` jMD,A8W"TҠm"d޽tRzɅqOMAP>0PA@A>!_\?Rɓ'sVѦ~mA7~)]g|N>aap77 Z}6CL4.Gaaaz?oi̙={&|>W^L vT$''=t /((_"nmS-z`ґwIKK۶m=Zv-S/l9{>;g:֭[]ZC2G B7ul޼9?ɓGDDܵPOPA^? @A EGAYPp.:$V.~mmnz*UԬYƛVڬYr zyɨ533srr*_|ǎccc6 B-ϟ?_//LA-WPZӒ],kw|bcǎM;־}{v 8887oz:u: B-W g7##FTX jVΝ cT(J*ȹ/YʼԃG @A EJA$ݦ=|z;/X@؍e7C'vӧOgÓ)))T2||| _p/]D[Cx0aBnСC|&u e:.lgNعs'^*sBĉ'oJ*. BcnMD-p;~3)c dAA c9wnM0O|M nZ [SWg}G 4He) NAÇ/l|-Z[cQ( y:w\:{7155&sD̬YKt@w/b@POJA%|MP(HTaOF׮]6rPbƍ6.ڵ;z(AJJӧO!C= O{.:""՝;wҹtc5k$Jg~z²dr w vMLL@?NzpBwww݉!r .\[wޥK'Ns-%;b >k\.M̡ (WANw)2Vn_@AܻwO8@Ն  $ 'OZj˖-Iγ^\o9ׯBѰx]v=-Z׏<g8+|D+b͉@7ZQ2?rzի/sf5?WWWa͛7lW(U!!!z* G~SJe 5kFo_Q=S.' q˼ӷy(),y:cvڬB%iĜ9s} baa1x`1k֬KF}0KE~3sQWڠA/QT|*kv :mڴv5jtѶm!TB  \<|ڵ/"'ȚCVZN;qʕ|m۶eϧL»A?~C (EXAR^E<,UDLm򏤻g )| OX/J2pȑ2eP6vجs ԙՋU@A>ɓ"Ε#kiӦ ݷljժڪ%K1)AAe2kV.;;;7nxL,֯_O?t.s2 M6կ__e&MP#u-'&&f8a=n޼9{U6Н_m̛7]'رIw}C$p{i߾=O8T477wuumժ=>}s+@APMA:[rF_Q P§ `f>#^W~ޢ"#6rz +~"󡡡|K.|ܹYKFR JvE&OF3]pIOOg\f(XdԨQ  ΝkժUݻuï13fLnQcٳgس{Ec }FBӅ?lW/y@i~#ehjFa" ~]˖-{U GJԱq؜1݂n#? GFcqpTP~?|}}:7dӦMxh{ mO?"cqʋ8XEP1c{zzRG8hРvPQraU[0xR8  ^|y*PPi뒸Ar!N@A=6dNb=nܽP?   .SzG֝0U޼ @A@,}?8^_*[3Jr (PN%u'~R!!  &ի量;Gj֝I'!P( WN@nGܠO?~ A c^j2o_§ ܾYSޒ>SbwϣY(T,gΑiQ b?`1¢8V5lz tʗՈ|R= <]#N3e.h}G<^3aVދq௫ }Fª_~xypYRlDa)1r,n^鈰LJ"O֟u+jn8m[_PJjc'65[(dnV?L7Pd&"YdWƞDbbm[IjC2v8۸vKR V:Pb}4[Ub3s B$pH*T{=@A@p-=p? ^~tʪgm:[ujɪu{qFRztss ґS[+Hd}KkY[6n4V9teNřX6 { nPewҾ#/ x]Ns=ȶK?foPȌz5sPd,jnHf)xjJbc)On: K73݇g.S|׻G[֍bX5 Gmӡg]IY&9G0Xʁcm;]!VV3;F*NTْЖ>8?etDiHyn^֞ɛ( x.AeՂ-p#cG>$% ftIA"bl4 kER#Y/'5k{+B4bLjVm CvJƐCh̥4jٴC< rKPItݸS5y<9 'b"cU"M| k9ĂpkX,\XL}3f(2AGvKjQUd+IsP֝Ħ>zimXI$:g7@A;GAG7>,(' +FZjюe65\JLL,9[z BqR;%'  [Fk{6-Uđ%Q2\M3sETS3a64$D&q 7βnәp1rt0£ͫ2%RmpT@R/_n۶mܸqAdt1(S ȴ(7X6.6r|hT܂d®`i $lѯ~sa}Q@TӼZQm nɕ}X'fMF\JFD@AKA޽S r ]Uu'| ܋WXJ,Ŋ^DVLLLTR28õ"@5N1Q׶t%2qcGfjul94SٴAAM98.MbDMd n*^TVW#制mP _R[^r4}G(MRȕ!V:Ȋ13\-oɬ RͶp"0L`Y:H>42gYF躆 Q9 ]7;쁻կut"PA-%'cTFBVn,uyUP\njv@At+bIr## "R2)HdjDƎnY%@=f 5-U^=n'}cj2]VHjdռ[=lET6 ՃVA%RʭĐ? 1?u$jr󪵵ɾl:ɪ ё,2FI*(H5RYxi&n^m,H%}ld#\ )&ڤjl ;BAP(T8M^ V邇J.]lZl0e#gzײyCxUlî HZc322ogNoD"6 H\>@AP y  CljƲt诈 6-Sȵ38Elgr Z sRAgh4S1.ɮHjKҚcvR"VXlV pv)P DjVHB}L#ܗ۟@+HTMl 5lcөBfvFJ* xrYh&a"qz~j2h k0:QXEVl!w3& u!ӊ (P "b*"HlD2mAeGFLVb54N5X63+_p;Ʊy|0#Gڍ,1-S[QPpbA|҆ctSY,w R?2v.;A|@>X,637L-sJ!°SrU!R[U۵Pq*d!D ҅Rk 8ӄ0-1(vє3X( (P $֟umdXXu( Dc<ڴy9]WmdkRgnwCvH$.KJ#c?` w ZGGf\;>n̢v#]s4$C'*6{b8  (P ~ 5\ G]N3p||2 nGml{L5"F3)cfI_osz( (P (P @A@A (P @A@A (P @A@A P(P k t @A䷂sP&ܒ_P7ycw0L]UP( $> `څR0 t3cަW_?w!~i=)û:2VKǶsW!3zfK|P( $Z*hzOgrc#Qֽ$bQ@ ؁tOJۢ0BwE露 w]Pj혒 )< AA P(PwO/TH̄DdRלJI^J,IB uGA(ٹ%y $[=u{i@A @AֿWGxyUpP;ֳM+Z~*@*'bcR_*XI|閴Rjƺ#<2v@A P CS̃c25ᯕA,Ew6+Ҟ: )m~Vk䖶&Gץ%n峂й?oa/P( $7fʺ 쯬 6aXjؒ 3 UE K0M* utK=yUp҆RY-:XgB7 }zo6"I:]TҊ r= ڿ67cKD!"G @AP'K76l/;8')n:cWjBFpA Bs}#;W (2Qo]e$"BDy5hZrzOf{2uJ[.Mh牂i袃[/. ?>uw0KZEПWVGtubU9OίNTjZb1 y?.S]u|iv]t9rJـDFn,ZsuM0?bLUj7rgcݜى;8RO@te :*\׎ jUàP{X74߫8 ҿN!R~Lo1 @ALT3W>J ɪ L=f孆iz7V^Gn?Wtk='m0 ]䆎mȌDU,4U h*mΤG*)!̄dzOksdlO(/:@A P ]_BFVv>U lfGV1g nf7> aj7˂4Я ZUa5,gZ*u!S8kB: VR< y_FjSxYI{ůAg-fySAֳa}F-=YlHIK8 u5)һ7C )iv~)6ӯ/Lu}m0 >|KfaҾnIdD ~ ׅy ,;*5+Kje.]5 @A䙂й'^w=/g=.kkiSFE>wȺ"& B*WT`H K6r:4oS=oUAmkزz{(  g L"q\Iw$DP´S]iFմjd/וn2.iPΊF.үj^;;UTH$"*lU6B;TIwv: @A䥂0YL\f^I3qvd-dBJWyq<уDc_YJ;ֱc4mhbrr2嵂;qQř6ѝHI]V BnuPK(  ºSqS8UR(e$rc\hҦ1%JKQUU K3)V Oշ*Ht['⬔Y%f&bQh)5g"ҽ2s];j}cb[= 2h n5RcZʇ(e+˜K; MII |+Hzzz~AjՒ (yrfRkT1PG[zkP(*(t%pRBj.\ (P#xM|BVoIz @A@>"--Α|o:ȎOR^@AP (G$|e/?C 7^AAP (G>x| .Y{< Lj ku( ˮZ,Z מs @A@>O>#A35ck~ @A@>2m#2Tl-@AP_$'n$U])\ S6fWn@AP (_W*" A IOO_ㅡo |߇*b/^/w|" @A@~ѣ2UY~֍J(>-nޑS ( *y8$8Xj,9*_nh@AP (7 1JGEUNXL!Z @A§ Rd_oxYoK7(  N~qDžtްP( QA;Ȍq  ⶱU9jVO,Ɋ;SNJJ  ?Ou۸K-^|Z\џ ~jժ%''CAP BK7CS}Zu^ 3`nټj(ʇ(y;[.FAP7 Bxf+B=vV}L Ƃ4C, PA-_H#A {( QPPxUz? h d={A x"uCz}Ϙ=%,p d P(PPtY ;52%-@A @A@R{x2ߜ@AP BzLr +@AP Blo7   z5I82]7`,@A @A@T dF>(臜"c}4( Rg3/Y鏾@ EYA/tQ04Y($26G]D|ߧ^?XA(iJqNݼ]$|uX'kZ [2Q[k̳jZϺ|e^E&QSN"cU#*65^תE;9P4hAul )Eϙ \x dAA42vzXbcL3q6DIP?V:p7ļ."1'IY &2V9lDaEu̫:L )HګW?!\ C:}Ӯϓ xqх+F*EVD$:Mؗ<8 (o ґ̜Q9t0Z )ŋ/wd^ 3iߑ(^:A=vz4۪l@be4HAQf\( ] nݶ2g$$ dGATq3Q#,Bs=Hdݶ Dcvf4o2u bjfۥi l05Ů朩LOAUC3MDAAPPHz횭* N dGAڬb mnZ<3K "Q)sve-Cۑh>>|O!P'J3cAD2n`X]+4R3+g9\ᦄ (( B~^E넡s \8*[FJDztnS$K IA ѥ,,#]p7Q"oE_xe 7miEL`UnmD8c::Q5| EPAC^\. nĮPt(HNA^72V=vTtEj_V0 O&7N tQ"#.$ŴTn"D;[DO(X@APPX}G2VlQaRQv]Ԧ]1ӤJ5roQSؚn%į'X, 3vjxf,>D$(6QqIDZw|XaIt1=EVA^xde¤ 1,Td$cAkt$S QqyEtĴtE.N?XD 6ki&!W8NK?1( NvMLjM EPAS^ ڶ_ N2{)A3a⹅$T£?zt&&qIV?%ʊ< C-qIAbƭ5㹙]H%:5;*K5-U^; y:@A@T֣'m|ғg HVM¬ZuЕ-&|Y_mUAImu aSfUP; aմPX6m?Ӿ(@̶sLyAfїn/Wɲ*6xţ@APPνZ w dߟW x4BTV=:*cQn =bv%` LvJSqlu,Y7 (( B~8~| vyrdWr>%K ZD.9-}S쮞m( R^ غhwȻdQAP (( Bxժ dbg$*$( fW^!?O  R8tPXXX̴hbŊx9DAXiϙ ֜>d P(PŋbРAx9GA_nݩZTtn@A P %KD 2d ΋,.pwAA P P(H>n3-inO P(P($Iy6yG* @A df}S d P(P($Q df-( а|rccc̐PS`8uvؕz d]@A8?3t^NAVbs-ċ P|UzgJL.|֤O P(PG_N qT( @A{Ϟڼ[y#'ӡ P(PGd&2Μ@A @A$z21ˏ\@A(H~_eA? P|Uz㧅 d41á P(P/{dF>g ( @AYrM;ȸ\ݡ; ;DQqzeΛ5k@A' =hl*<)STS~"o]Av={9ĬBuR BAP(q +ŧŏ}ݸEbF2TzժUKNN SQ d&; ;::zxx@A' /Wgի7o޼ >]? BA@A@W @A(  (P @AP>YIKKKIIy=TTP y[l:{ڶm۱cǾ}N2eÆ W\wx @A"  (kFu…po)))۷o %O(EJA\ عsg;}tn,--r+WO(PJqDDDZj~@A( bbbbfffjj*ˍbA :}tҏ?haa!@A" 2l޼yGرc_|Eɒ%ZH֭={D577( )\.VKOO?w\۶mE"Bo97 fT(PUsN:uBv횚jZ_cǎy3fѣGϞ=z?˗z\zĉdBKL8ԩSǎ_qɒ%'O/ 4|e˖)gۻtU8~O?t=BA(HUbz$^^^7oԫv֭?Jᅤg:XZZ* Ur儮ŋ׬YZ4h`޽Ytؑ.GuJڵk P WA}\ݻDDD*vvv/[BL ҴiSVA,/]?P Rp?CbccO={֩S/m׮]rr;(ȺucrEoPAD"  x RdΜ9zqOmۦ72aaaQrVZ5jԈBoF9JKKڵ#k>xϟ_|m۶zWܳg;_~ٰaĉ頰u$wNII[n%)+\v-99*Pw¹nJB\() yf*sA4_~+VXV_ȕ+W4ҵvtN:idܹÛ5kщRRϟswIIIٳ%\.'y/.[e9ӟ?(36?%&&jہ~r;pP9.<[n34gJҳgrwINN駟`Dc9A#F́(Wc%FFF 6U y&eȣC#G,]+W̝;Wcyun1Aw Faaagcbb P(  ELV2w*WܪU+__ߝ;w_le޹s$ޞ311^z\\]~Y\ޢTR o޼9sWY={8XxqժU3e˖%pBJJʯ=rA1tO>Y;;;I- )\nݻw|8( Ŷmj׮]++իWƛPHKKK aM( @A(P (P P@A(P (mZ:(|Q_&%SN Q‡簰ɓ'o߾ NAKNYP(? %T["}2p ( #y>1,8FNfܸn9//Df >>>)CǎۿѣGϟ?ѣn*A&'''%%Oz]X@A7 {٩eɂlHATbCg#}_)Ȅ  {ƍZ\.777711133J^^^ݻw_fӧOs۷oӦM֭SԩSl?P)jv|)?GL?R!'jBA>3յk-;uݶm[;wTH2̝;ؽ [npB|@CAHqgGR2!TӤe{X*Çh`` jI8ttt2::۷ڵK,yϟ_އ?E PYYp}hqK6 R,ڵFRzsÇX(ۛqy͛P$Ii'#ZPN޶ayeSG()ț7oȉ5z:{lZZF۷W\lkk_AA@A =GVP=tZ4oWYr7cرy?}t9 ڵkRR@AXp}X}( j|5kKyq58߿ׯ?xڵk K,,,= ɑ"j b /]wЋq+=xNՍ WYjٝ;w.?WzEU)?cƌ<ȑ#cǎ Zbō7rlK8sLnː:uvUx٘On߾}ڴiF1bB@A/Z/]/ |0mr P nnnlOkSSSW?qNA?"BW^ [f3xzzRC޽f#+Hhh({Fm/"!!!=y(=ĉٳR5ՐT\>@A>7Io ?w\(*ȱcǸZ[nL~/Itm۶8p 酝3 啇8;; aaaqcccv Շ]u7677*`mm͟Ȓ$%%M2*YFFذgM64i̙3Nھ}{KKKyOPIIK}He䀹_>(L9ofx1P3m5jy\:tK-HRSSϜ9IѠAW^pT*>|Nuǎq}PA?$f6o̿Jԇ"JIIٹs'Wa###,P<`zUwJ#_iD 4mŠFɰV}zLj. [T6k߽PtA4֠f]:% |H_5, 7oi.]>ˮl|njl^ʵǥKvZ!)ի_i&.~> B`!WaÆ٧6  ߫n36>?MAd~3u2Ȧs* :C43^P$$ִmW\G\@=: tB[΋X R~ Y"H ݨ_~%8qM69Ǐ+U߅ /&- 13)Vzu6sΨQ&MD.#ߡ_MT󏊳u/̟ oDbmmڥ2ɮX^Jdn)ߖ5 )NGFA<*0B=)HH7DBUz0AA 0+HsYDA__ 9^ҲeK6ѣdΜ9ӧOrFo߾ l߾=ӹs#G|P(=Mhp-?\f[wRn ZRhRJI'I.2ẻMM`KA>bDDuwdBe˖m׮=sr[l @Aw oަF y]$Lu16@Neںs^ P",4TNQfVCgW^C kuqplܤd9*B)h'CFF gfIn̻DL獩)mi(Hĉ\?ׯ_gg+H}tjli Ma񱶶`M߾}1  gdD>if˓o> .$XUAW.6̤eY@$"ԠFFNy ]?g>"cSլGui>|ȅ߿?>ygϞ{?S|S@sC BOTpT=Eٲe91Ć#55VVV(|=<臂(Ǻ\fB`$Q毹N>\?DJBGN5mݙN؎ ":SbUIK5lL}1zGls7jiڮAB}fjTn=j<, X]7O(--:yŨ|jY,zdt:AA4iii=zFBkǭz^r{}vӧ9^~nSn%ٳgsVsl޼yzbY~'NP÷U[Msm#bbYIuEfp$~Pn 1G`u-/=$NYfKd1trhf=ʏraPN3{/f?gQ㤾!T!6\Ṯjlm^(HgŊ\Zf'O*j 5:t\AڴiÞrss˭>D")lcx񂤁pn1hG>|cǎ]r#  '_q1on&U@2N}|2/P1c.Fƚ, QY8LPWϢ0VS ڥMZ o`m^JlW ]1)ܻwYf#GpS|rTl.L2wrRx xbѣGr97uʕn߾tR޽{vDr5n:4Py]`JF;K$PA5z7R+U''`w@ttLW/BseAcn"vtD6u& XV*ZSϳ23+BA \h.[n;?5 4LVZ*ѳgONkUG "88Q|+W4iRn78);CA*-{ <8zɩ>g:yzSzkm٩ve, ea|%RKstմ -s, ;2-%^Aln%66>|XN* 9f>~xF̯[ 2uTaÆeb]z5/ T3pׯݷo߾p̅ lmmFN:/^hѢBA oU*$KL߶={QJP. ~# VjEZVIӴ0 VȮ l<OAz:#dL | BsPGA111'Os׮]۽{QJ*e ƍXM }vnARCC)Sܼy{KtR`` 7 +gn[+V?M޾ӧW5jP9іB54hWO?ߜ@A +O0mro7%|l9 |h.}4b&fO2_df|תOʴ,LTKI+GNehDMN[] ߲[nuppXǂ[wwwN #Ço\A6m{{{ٱcF\mOrV/g>}ɉCHΝ{EKNrGP])wzh .]6l)5'֭[޷o_zzɓ*FW%KRnnnOέ>>>t-Sc??U/k˖-TmckkKw]GСC]\\z ˕+7|p, ߳{nF.0a O "#ƭWI,t< PZe-UR! VyU)j;+VeeJ`vtqӟi(' ;@Cud&s1KCAmaIIIq޽{.]T*g̘bŊ9nC'99޽{wU~:l^bPHKK~̙͛C 7:y$WfBB[YLLLd9]f{!֟*0Y*Ν;-YE{n*6}| ")y],!0{`OVv9)!WΆð mn`D71eGEbez\GNUKBI!2d,IJlNH˸aKyh,YZ>GL Ș%L,Q0 _ jHgCÞ .--wE ޴+#C`>GWFM;yCdfe%Ѷ+CMy~~z+ ^Q}}HJDF&,H%1T2.XC,3;蒈0[ɰ+[1ָLLL 'd<+3Z |5qusss͊cXXޜ/ K>W&,!yن /  6Q҉aAqI`V ,-(J:!.aW4"x"O1ȼk?~#, ~d,}_0X0uZf0 =I'Rm5k @A$ ,ȱ}xsl|=b7Ǹ?,]\ $YؔXI9(?}lm (Snr4#F r*a \Y tA PoXAn'lt}fLPT P(P(D7I7BgP?!0P(( )ii{C`dAQ_rg   R$>>>xs IA2޽w}X,7>3o@A]֮]kooo\7䏋͜ϟZo}CA P Eׯ_ˉLBA> ޯ^ ޸S@A @AƳ-f8]wb! Pw^'m?.4fiP(( ɩikU{m ߵdddD>4KL[z]@A @Aw ]v1E5]( | r*K!0u  ߩ\}5ܥ/S( | _V5&6/S( |w mꨭ-<8w/V( |_ >q.F}g|CA P R5g.:N#4_/\( |G r !ՔKs ߋ\xΜr3oW?   y߫+6C`Cc'+ ߾LNnhCɩi_P((mZzc %v<{@Ao\As8YKJxu@AoYAv_Y~Z ߬}𘄃 v^Q@AoSAn'y ?&,vɳEP(($!)yƝ\ eKIK+"@A @A A{y ȐM^&%CA P [S'Ζ [ދT P(PM)Ȏ7#zǭᓢVR "Q(@ P!0yvׯ4iB͡QH_ 镯D (l rKK߇E9N"Y/_V^CXE@M32(( MR ;Е ?WjzzѬ?)Hj@hi{>d(  7lH}nʮ}ILI)uF94j8c@u+O﹯oIAD;qth,?͏_6g$cb ;) Ϣ b-ޡ3G.`$AQ殺Y?r  xyK)ȭ{EP((( eFMQp>wKŢP(((F,~]h̬'32E P(PPHJM}ɬ-?yZ\ Ō􌌈C'4B`՛bP(((fvs\dkOS@A P 8qQ3w \zb P(((6\~U0{Ns_q|( %rsG#,4߸q ylFK u,Y ($زG91mA`ߑEI}CGi$hxofwPdH'R.:CBu <ڄPO V| 됚>_!J.^tZCKKSw 25Df"sK~ҲҲq6ߜlo!2=DƦĘw+2ZiGrUUZ׵Y_y?on3TXQ$o[<Znt\ϽB(HiۮTPWk1KdbV^C_ BAAPwvY 2C`j,;y *| \7neҲZ3nڰV}m%:K"<R2*Xh ҍQ=VzD-sKrC#-<δMĆ-in# |q9+Qnm{Sw BH2Bb;Kgbm^Cb? ԺJ\Qu9P|̓5j\:gVƲS( w_v.0aaBA4ZwyhY%"`ںDDZj{EAFI!|Ζ'-uՐV&2/aHY:̸XVRց~SZV1# ")y\ypt)iPlKH2X _\# uL$YU(R-2 bUJVu ]mlI t]˭CbLZgk"=塱ލES/qb7aˌa0y:;D fVHsv*]YY2.6 x7I;jT B?+^t^OGI2 ˦itbedNu.2Uh^#qAfbիY20ZVhR>}kuy13lhӶ]:LLVavx?R4z\-D4iՉ]Uѕn*xD_8[>r$0)|3sw Xlc]vI;JbJR9©Ј gͶ:R6ejFfmu7XdjΖl5V[E;= k0U [:p 3"R/jjm~.sxHFc@SWřݻvm߱oښʥ3OMO :%rGC᭎/N4o9T5Q$s=4}G+{L᭬*L%`:KFq˙3>P~oooP(j!$wu29-=*ߩDbPazYRFuho{iTAuofZF {WAmWF׵QVdzd1ӼKuR. [f k)R7㪃AQ)d-X eȭ˖RnUU >,9 )mi,:<Ϊ Vx!t{׿ ʻ7rƯ@A P ⮭pgenQCJvԪ[$rseиRU挰_7ő2ZT귩7W|bH&t7~HMN!lyv˧@A P(P%ȩkR:Z"A BA s]=]+k)=PrζT..ZQ9;Zm-AI+Np9u ^%@A | B-^K-9ZhYtAGʉ\6ed:tʳyEDAE+-ejU'+ >mܳ4>7?; @A;(ɩyƨvvPZ6쯍,Mسemtw#2EYA+ȿP< ;厜@A P(P68ZI (ѪّH7n }դλ*,[BGS kK=3!$f ~  @A|̥E*gJ&x؂-+#f NfZbMPrse^VxQ3XY->wvM-T ¯/Qi[Aآؔp BMi8 @A|tz?Gc}6Mq|bnBMΆXhPUNA 4gi*u<[4͟3\㳖C~س䤮5i.S'wqf[zy#9#;շO5U5%K|(Ȗг$uyX B2suqewWAA P(P ?Q%<̿i2f]]1Vo@ kv@%uKqeޔn ,5**z۲N*TCjkyё ږhTLV t|oY[kpRy{AwBO2@A @A>.\US= 3͇Z["%-4u:W;l%YavotT1663q5WÚFV&j1-_V=RJE$lduaak;$gI*ʄY(  q] Ԭzէ&]me +p vuE-Fr>Q@!"A\ZI78Ur2z5ʹY=Ɂ>!UҢ| BbMU%@A P -emt jbLA]^JUΞXǂm M=)=1e*;\Z+ŅNqWUR%Қ ; tӻ+y=Vߦ8r.P( #p[)SC-AQѦ, ~:X-!]5T˱+;3׃sQeltٞ%Wx3F7Ѕgyp>z8 @A|@̩8w[=Uh$(5'c5"iwkի`̶ /1Q?s5X[A\AWu o8fLf8* WΥk |?8$&\ Ͳ`Lwa[z^W T3R)H)=z |L,)%6Bz8fG*BA>EAZvQ(m@ү\ (* BJjmXkMWHoS| Z,)x/\ W~7F~@WX͐  wgGHn ,2û:=vPMvij 5+ ƒ|j~erFW5O2,IQoUÌԹb PAr\3*hBSq +y> ܒnf1ñM]ye*UJ322 U5T@bptܚOj/(_!Ak+^+ȲeU …23*=S+2MN1舅_0aNzjP|իu2onTHbqĈ VrlYՐ-(+b-nJN{OALr0[\ſ ~TgQni2~<-UX5@ p {@A>̛7o7o"$cf"~2tT@RVvؼnG^̴S6xyq_Kjy:׳ROP˜)Jf.ަp~گmg 3P:3ףf9u4^6\ဂGA(Pu UHO:]YD-nɎ}lU0͠YnESA%Y-8,]f_NHY5) aaD3#-v8fڇc> Lt 1B(  I1WzM_ʊɜ@d獪 s&uArS̺rYo5N}ɺBPF$-aڜEb8 +yNwVVJ=ׄ2r?mP[907@A @A>Bvcf2s1;"vz:ٜʺ5'błnRg+tYUӫK+jJX_[g[=7 V;٬XJ$W.*P!:fi(9 ,׬W+gԪF9 <S'U] 5LLn+TM,;3$P( $װۛ˽p? }QFmpp&:dkwog^qR\YnxV_FO*f3]I]a.άO0]*FPQחyj ?g0:VәՊnWt?Δ(wIjBY=lTCr? @A5@5?`2` d}$U) Tg_`?5_qBA P(P/ P(( @A HP @A@A P$( @A @A P$( @A @A P(A@A P HP(   AAP$(  @A " Ǣ1{ī4T@A S4ze'}F*vT{9;=}}N}g/MK AAE_A%J8us/TX```mWlr7 (Pt$!!I&z"D<]g+AQ.39s1 Ya;BřSN8xXIPNxZkP$(*6Xv_sd< ͻPBia~~A(i1;e˷㖟 O)3GL,yȩ́%c ( r&95-t1Ch=SHy~"#q6 EP_-+PW6ltBvG: B (ȍS&:dPr" jtõϡ uYBQj4TWD2^e-b\:! @A@^$Ie zAAH#,u-Tά{A֣<qt3nZ=PJ'M7jXlJM0 f://-o,XD&&l䴴"^tTyh HT ( RXKk옺VoߩӞ`:*BAb "hƍoW^·s}(;  {lxMd] AAPP nً3-r]WnBA ((,QYHƂg͑,f̲#AA ((,y.ҡ̅S]Y7@A @A DRjv1ۛ-\{s( qy 턗P( /қAvʲ.kK@A ((,!ۚ- ݼ$( Ro\,8ZXAJEL)DxgDYXM{ ( B\x٢u| Q;7D mK_&'fmMt9X26(%f{7ne}`!ILZvN8KP(!u% )R6%X@A@PsVo}۴b 2:NnA/Kl@D%Sg;Y= !mQB==Ac?G'$jsyLWGv'21<@APPl8|^5R&2s<7du.چI"3 !T&eQ2_FA?QA YҶ--ُ*) ((N Br+r!r3;6)hYJM 6[2&P2jx&FtVdni=b ۖ#a$f=P|; Bpv-soGKl矲)DiTjHF`֩[l^ c milVv6(}MAlyLIAz]e +H:YMdF/9tnTIaM{Hy--`ڪS^ Bζ8Ƹq+<+Tmڦdq2M6RKB!Ι(҉af`ںs+Dۮ,(i'  ۴GNڇeDv܊wC]gzbMZ/!DZLKn B~0iaBCYbܬson1.7n4KNP#j*|5(_:bP[A( !SRu #ѲI+dY2Yt\:!Լk?-+ ӑPځȮ tazU-sK?k^Q˘/}eܨ%c%J, [R9mm]JC*H,jjRn ҼdlV=(  !^<]EA::.Δ8PMYIV_SABb,zfsX $1mߝͪ]Ҏ!l=aiԠE %&:x 2xlJAZjK'Ok42[p dƍx7@MxŊ?MAaɺ]5[|Md ya5lB ePG ,Rp+O0~#"7mS[ۼ[yh,o=1N'- +F?Q o`kAuoHȠF]ґT H1uE %xP(7nܖ-[6 V*S )ի?z&7AQ7|MW)^*z^~:Zl焖YH4pRe:e蔆X 5nl8[CBj9T$c n.@[Ѡr^L\)5ؐ(p@G >tvv.@ [3g'z %ypmLN) LGy*(J0oCF-1cd|L~|5c{1g-kȦ80E9[mڒ%"X^*(d.}1TΘ@VP@a#WWWptt @T\Y___$-\ rW>oh]hoަu!uvgI,f&yh(ȸ - +fw;L Ӳ1 Rt4m;D͓&? "kkڳQUkˇ0IcGLCA˳g*UhѢ/^< '֭[ |sWJȄ>?w5iɬKUeCag8fzY(t@g Q(eS©bVC|^D%Ut!]ά~#'OgaG/ Ua ONmH K_D#nLI "k Ts=*?\/$wW( (xe<&2]Wo⿯ HP| BtBE^Wbtv]pWk.meUvZ\ܱ>y+;(  GŰAwb w_HWb;f/oe:cMd P(߬bqyDZHE1ucԤwRbk ȸG,( $SAڍ5;!V>Lً\@AdUHE1+ 6=}_5{P(( R8t.4svIPTyN@A@A HRj݇J*MdZ.P(( R2r^Md:|e"  "O_qg烣{Qk( =⿮Hne7 o P(P$(H!rY6hl"3iĔ(  R\xqMdBc+9- )d 8yqy˷GۇF;-# I'خHɾ ̻u3ɑoW^Dq@A @A>g rȥ[ (30̲Ƿ u\sR.(Hv2޽vW,̜۹KP((gK6'T'/dF4g3WSҹ7 AR#[Hݹ+==sшK<,6|9ʳێgddP!֪zRGqԴfA U],>JxF#ObRʪC}"d'_zzjֿ2/@A>uҐͻdMd@A{!'1s>xZaTEֱQkߦѩbx1o$$}x+I7dky:)-=[N\wKՖ"7\F]ՇϰOw7psF<~̕A& zҭ̺O^:?_1Tr%u9I̎MZ3#=HG$4ŋE`W&f&2C6y͡ P(P(rRm0EmԣΥSTit>lJZz^' #mu6̠T1K`;`6;Gc7޳ϑ#gڇ ̀ iàit\+,Ҙٗ~?DǩdJ_Sc4Y&Z&Xxٹ䓹ERRٸNFP((ɯ7u㗯w$ <&^rj2GڃM# l?Jqlh냄O_N?7eisҩWJaZ}ީ2.k~ȭMr%-Q9nf\TMG2viu)nSSv㯒S* \zulCb; &-;@A PwTOUJj{:sZzFZzyF Yk^(pl+wJ ̺^Ui$.ϴPWy@]l"5z] QθxO '.r1ɶ+4:}|MW=tIԶӖAA =3w%/DaTi.P((clL%ȠV-WF\#AY}$4۪,.wvzG)sY^;wmkM+܌OSf ۸BpQu8gcкswΟUvӡ [D/6ԵQ(eK2 " ( B-YUa,;L.5ޓ$0(vek^K}@Atmɦ ^A돝˪ ~gMrMY„o0 YPQv}h$*)e%mE+IiѾp>{+q=w]u#gZPSW*kj' x3* IEed d 9@U5uGA><Skb-Ӿ*ķA)4z< ÇAP(TC=߱zRX+W\ -wlugsrowT1&l7O;z%ZWfD/^ŏ5 i{zOW! _(tsFKAP(D dpMI48 (ʍ >@v"}\V ;~XAC6hF)5)*u{t፧_xYMlGl%1PJj@i%BEEEUUUAP(T{D7:&8´=#z|V=gAH vqlU&'-P Oz&Q ۱5MWMRsY/+ektKśؘTԗRBBBLLL^^NĠPv:#A+ɮ9o9i!-w~=egi2tVܧAG=ߟ&I꿭ȫx k,,DAcҁ9pE| OBAA>͞~bfA1nh 1zWh<[=J:`:KK"t:jO[M>l?oYeD JZ/X]Q_ES*g |^&b*kj8n~ױJiEAAP(D nm9,LUsr,-" Sei7'FF;E K3 GbBBoM[.AQC}LN.bgRR2==IDgBŚ6 bkO" " B!1,p.wKGDRIKz>DڪK`n,\p Y-i^@&VP LUEs.w\YeM诤M L朴 E΄ }[TCD Bi" B!`@AAP(DDDD" BB!P (D BAAAAP(D BAA0 P ( " B}f?9q 2ȟBAEK`h-=*"P (7 :D@AAPoA~?׍*ba" Bu|ihl zp/ ~3 %dB!PAPߏAP ( B!P (D B!AP(D B!P(D" B!AP(D BAP (D Bƀq 1“̼BD :~y &OœrQ(D oJl^Ö AP O>=sٖ | [YY 0ftSSS)))JY:O P ATUU::tKV8!!a hkk3#w֍ҥ˝;w"wf41Ǡ{)" ĉDÇa2d:::>޽;۵k׻w#"""" #NHII1+BAAA:Xrrr!++KO200ݷo߆ 6߿BAAA:𓍍?@ 9|pB;[jllDA! PXZZr ) UVVŚ/_޹s522;aEEERRP͛7BCC322jkkyP\\,R]] & 5INNzcm'>^TT|`O" f=ydLbccsNIII !!!qqK$ ./_~5jT>}ĢE8 Ο?{fNA68*9<<&&͛`w}}}DiӦZ b@$rCC˗/}||=z AP(Dψ zx"Wե899Ĉ)S 4L5ql!!!|&ظo>o֨ѣGs 0r#HΝO8QXXX__k@B!|Y~=#qqqޝ"""̥4Ax0.[{勡!&Mb^p#̙3!gGB!|e'kך_Vccciii||<±c8(O> X[ ̴Nϓ'Oz\FMMMsGB!|} m@333eeYfIIIq{o 6ApN>}֭[_W^ͬI.]A f " 2hРd̜9%"""m 7od6?ٳ'z(D ("WCÇbŊN:4Ce 6A[[ ؘEEFF;"D #رcL*++Zf̘bccŋ7o2KDEEA9hiڴiSJFF7&&A0 P _A&MoǏÌgg˗/v֍Yյ$++KFFÇo߾ׯfܜ'!HMMٳΝ;|///@B!|IMM3f WktΟ?Vwd3[RYYdڇvvvB\"t5͝;w̙+k֬kRZ ׯ:hlZZGOE0A0 P >񋬬LF2`LQQǏg%&Ǐξ|2 !\8p 콴48===fzT$BnݻQKHHpoVE1BKK&tH <įUUU+V }" "H?>sP|< jkk矑qUu֭]vΝ111 pl;-͍:g8>xL1Danno߾7^:|0RJJ Cqҡ2gRAsH@+" .V{  (DDD B!`@AAP(D (" BAAAAP(D (" BAP AP(D BoWcD D BB"Is^G/L#nBY3m-:5m4} 6>[Ɉ (" Ҳ0@YU23ov wpvؽ6>aEe_Z~5`.ÖBAAZ >Q_glu̝<-j&@uc˪j^eŤϪ֑  " B!_o-&Y o VnnoiV (s 7_piAtqۺ9oQBD Bab&2 vCl]'dD MXou !ί,o[ZQ*,'()'wLC {p;$z&(>5<ME`>|2 '2eV[pWcD{AI&-߳aaa}%!ޢaÆ*}*T}3>uu''|Pˬ2"Hл6ǯRl maV;\HL'd:)q(UFL>SEzڪ+ES'}RаLǖVQciKYUhɯ :w."E0.B}/RSS6i555OAB>9 ??"HQQş={q~ԩ"gEqE?b-ryo,Usڽ6a#iZ^; 'ׅ%מDh<)~ ׫j"RrQ`Md*5$3w (2${=xz& ; " 4bCPB2~Jb48P\ΚHv'`6F!Ή5F9:= 5Np +a@C~}=g2zv/ v/眴 lƶpsXD#"Hee,`LMMݽ{$ʕ+Aڃ455)"|]k*sBz>˾zL]O\}]A˹epړ~JZ|Q,9뿬u9Jhs g7S֋7#TXQǯRbdZH#[ o @L駤};;L,Ұ.bΔWdd0CqAi9i=?q!.;!5! Q9oIV r|D陭35*S0 (TFƽ{RRxn >>@EEEpرcbbb`! F(OJFibQ3aI!a EY^Q O~͍ 8D]Vߎ,)((8"vS)WMOɛTM.J!c-e>cQQYkOCA:(B|}CL׬Y j*YDD$ cLpRRRݻwڵkΝtҭ[.X`8ax7ܽ{711qս{pذaGS:􉂂֣G?DHHHRR`eeůlll&sꘘ#FL@'m۸(ց#GقϾ}!9s@gϞM+';jԨ)S@e,--999W^ۃ ?Em"l\e5@ kEfh,T"\y>)  Ȗq"ȱkkX$Sji.sDهNL$;M\FRbŋ~Qߑrzmĉ('_n&"&L 444/_Αjiii_A75Ll\\wZ :o<cdС<+ѣGK8yd@ QELSos` SH@y.異LG!#ZؠԵ!@x]JbE%S*R[ug4ؽCYӯKᨓi&`DEĦ^s z8:x𠀘zzz&,,,ǏKIIQ&|ԩK,[XXi;Ɯ[!Al>>gs#ܹn=|9h=\xa @ hEmm-3s@^^9Djݕ? ⴴ+Νc& ϙ3.+))μw݋"H}}}vvׯ)eeeAÁ S\ܹ ziiiє[6 DS۵ `Y;cL A%Uk\'ІBcغճk~َ)$^;̜/eSKLUs2?3NxÚXX4v݆w[Ymx-FzCBuhD %ѼHݻ}ztRnoYfQɜ@!2 Emi/D *8L͛79$N=߿Aettt];3ի/SJ[j; 3gF ?EDDp$@@(n:2y 42a93s+qJ.{|5~!daxLc}7v\}74:C.g)Y!g|w~Eq Ub!A/ pe/&8ߒDC#^nc˽jvۛi;|?Lwܹ[!F˚J155ȑ#ƍkkky"ܹs]h%LB(!HFJJ <==)"p#H3!5L=z?mw! խ>>S>B 7^nEhޱ7#;Խ˦Ƶg}^k_Qqy՜-AA::q~233njC+]wzeeݼyҲ_tE#O9@U^^NwG <_`U1򕔔-ܭAM6c,:#**z!g:F yet.Cm?ak(WN`Gmd|74U&ۤ9h?pːG[m:b~'4G0GY=Il4.=2-;,X?~Z7<? 9E2#3K{`f@f3gկ-FN\"3UWh?A|íW>aL8 B}´Ծz*]( fÉA%m999`"P͗G7ou(SLIKK*m۶ dځBBBw!+W$ѠD:N&hϬTbdd/GXX8<<MFAiP9E+lȧ~JZCw P!'Z+'@2lI븋o^@_Ex n׮]cׯ1c]b7 ;0׭[GVА0'$A:?mmm寣C@ H; c4z&"BaMFy3p@QQQpNM99xԩ)SSK ӻ!HPPh:udccÍ {&rQ688X@U=<\NNn͚5dŇߛy#~:ѧ`y")?`o@nݺAXeDDD +(++ktSr$JiiyiӦq;:bG=&L̡jmmk׮QqFB$e-*Auuu yAqrrjD """" =ȑ#e =jkk(̘1`YSDL{C8u9ԤZ VVV.^(A-""DBx^pEMMnJ3%%n V;\ Addd.vtt en c*YYYDD (7 `S/^Lb讦'v`;),Y3)Zvvv AvAWΟ?/fnn.4VZo @'~___ŻQŹ" Aڻ,,,Ah` MLLDEEs׮]qG %@ӧOgnY4`d[_|&Oӣ [b@І̘1yǞC?p/ؠ]__r FAAAA!w>|8٣LZZE>{ԑ#GRW .pL"РMOh2lذ7ozzzzÇ!I^VVfZɓ'{{{3G}JKKOΝyB A3_ .1;;Ņ1uT!U~~>gаsNjjj"h`}7o|u0`5eddȰl2:zgEݺuޔ 322J3jhii-\PHH믿 /m 'O1!cs3f =B|frt2bC  (D :+AJLLxA>XJ推` _hQEECC:5k0O!ޠ-FÇ'LoK˗ٖ@᫱crZtiUU"""" c R@W^Ջ{kAQQQ 7o&%%%%%%SL!eee[ F+-)G> {}ϛs7OɿJOO *koo!EEEh<~昣a*224ߟ{PƣG 7@ U"l󰞴4GGG}}}sss///2 N:[m@B!G;#Œ}fll,"D YT\\I6u떯/>=DDDD" BB!P (D BAAAAP(D BAA0 P ( " Ց$00PKKKbbb<>>ڗ.]*//LUm ]\\Ps[[jEAAAAN>ͱѣG\MMvСm^R Ͷfٲe *..ƿ]DDD  I]]Addd p̴Ço[[|9< ᅮO<BҫW/ooȟAƍG2?p" " "Hb9‘$>>~РA (D (7 :u޽; &bl?C#A0 P,,\gϞMŸd?$&&2DWWw#GNw^sժUsm466y&00޽o+v;}z-VFA:}gϧN8ׯʐyyy ,ECPt### @\&"n]tR n"F~000;!AQ!މ՜͛1cu8qԩSex 1'NA*GDDCbM6 4u!5k@8r@EDX~f4 Fc `:Ѩ EO[[[~Cg'4i֭[_$`4D" Ylڵkb`=z$AUl_ڽ{'!HVV֙3gf͚%!!AG;3g۷o9s#Hhh($$wEq>}=H1$;kPԩGjuu1]"қ7o >A!P _A@666d=zйzqƌt?HFWիa:$6?CϞ=._\II7A(GPɡCX4FJ(}MHH >L.0c4 H=FB=Abr :Gʤ!477BAAA^L\ shi!(]`//\+AҦNJИ1cVZfj3Q )))`9;vǎ=zECrc9156j''*eJ'PYXXXsz,22|oWWW ЊcGLLx!AP(Dώ t 0ƅqqq^7 ͣ91)BWqppx #P_ЩS 9qƍl!`(T2tu544\%Oѱ= ߺuKYYY\\-[:t " QdggG{ BOX?m*&$$@q/^`n%AJJJ̙Cוܽ{_tɓM"|sG@/^Hh}KKK?~8e&m!H!TTT0ʝ;wBBB)"4 ~:88ɉ[իtzB!|!INN:t(ݻnoh_AA&p}ӦM`˅96hBwGe+:brk̘1PUUU[4 `ѡ!t=-ϽM߱]=hp L=Ⱥ333ML&rO}[^yOTWWoܸRW/Pv7 (D #H]]ڼ_~o߾D: ѻwo~ݽ{dN<4###9НΕ9dԨQD3cA222(<}a`N8ի7OynݺyԐ*kbbBo5g"4]e[D[|9w @ aqq):VM~/" c#nEl0IBYZZ2iAsEVVȑ#-Xh]~;|9݄hǎt@>ϧ9:t̹…  7Xi2{l~ې@i]tٵk 5Ѳ4+xz2't ?k^^@UVѵ9V~Y%Joбb%MOAP Ak|nУwɁ&YKK XD__͚5yx:L>yvѣg̘w(@e ܹsIp zjB/ܹsrer3gٗ ,͙{y UDD=.dj7ҥKw6lGDDٙ9RUUEtSؠ7^"_ ϣyYvHOhކ#X{̗|Y p\AP A  `9{U~ ttc`L]XX=f.N&ljfBC#7PgtTcȑԠ @ '<h``@W3ߪt9d*++uttpF*@ Ǧ\ѱ_ʄ 3ahS6chBŞlQrBA!tiiia'5q0D & v'oyyyܙE7{g7x{QVVۉzUr͛7Ƙ{1IIIbAB @dʕ$2ׯwM7eܯ_M6Is=sޞ⋜\uuu߇e6  8{7 "9"--M=q|)RRR;v숍m «˟ߨ>w\ B0窇R___x|[= 7A:Uuu۷_fEEE@@Kޟ;w,=gϞڵkÆ ---_xA+MZA&q !5 >r?Ν;IbnEs zApt>t)߸q#P yco} Op@0\bkkqm[;wVMybw (DTSLDZ~S^[3ݷo$##e|3l3rAAAP ({SOA0 P QeeÇh`` "D Yeaaedm0hUUU? AP(DgQDDpg2~c 44D45mP`TQ4>+"P Sjj*=ebll-{"UVXCg7L!LnIy.|tv=6h P K.ҥ <>&Nxe˕Q9*/ִ)fyv:T-0K_nN7pN+'_lQiM?|n.׸c! (" - `pi٣m @69Uy- nq㛘~=û~8 B!P m b=.F)AI& 3DYE:pgqPW6bpJ?Imuʫkܗج )%W2 @)D B짤%s/jÌ濨YL:pL;Oi9'-f4x\`ig0ڪK z3:wsՏ;ycyR+6b.N!uLbӠJ9+f5ːF$+B>J\VT5%N.%_i|НL΀^L=zA BAAZ `ŝ,.LxS0ex_oQP\TV{W(|Pr,YchVZQU[PSW_1%p`WdePz(y<@E_36]PU[&"n@9gPmqרohυo+ EIV]PYS>@j[\v'48r|bʹ nq[9kMAP(D Upޝ,Vw枞0n_cx|D2aէaD|i+Kkߟx/Bߒ ֖w@Y%^Dzc "좷SsHZ@[tt@ ȭ=gYT1ﳞ/HV%A ~ѩEJdZCbt8KO՗h]@fmAP(D %v iy,&À_]@3sV`\]`!at;BDmyXnƬKDjXO;vA1.+e\+W$7kd$bI ?6 N <#*U.ֲ 3נ-G> zoˤV@9FoH"CL;vqm\PZAAP(DVSҲr+ZcKw׀Nd㴣)iyiKj*vb5}!zWَdhȇTǮ=$B|2J4|! C@5mr­_ آvzUﯤuG4~.`+ fSaDs+gEoI'Lll#D#HFFӧOljkk>0ѡAAA^,..nf򺺺V4444'0x~~~PJDbk4QYS O}#)i_xǞ }¢j~8a`w]SPwfނTurϹ XC& ,p?Y`>c֌ ۝xiDgxQnemk|a UP|mz===OOO`LP92|5Rň8{aT!u BᏳ72FǷ4杺\n] %O|߱}>EP"&X[[C Ra@忞$4`J\S&?G6 XxZ/y|P.yZ75#Ykk~)돪!.+nXS 'oyr ėT,v={螿(" "<,Xƣs`H:V===|xϞ=inЄcǎlp@,Elvz^'.?/5A{"Խ)Z{~؆?)hT5,` ==_Y˫ٮ"$Tr'4f@c9@ 5YC" !ŋ+9s ;Vv3QVVvܹ>}8}}駖&_UUM}9 Q^^X A#@>{8PAc$S?tIIptrr"5qAz&3zzsoA}WvT.wg}7ijtϟ"&u8rPw{;ZCDQc.C&hO߽y6G+tړ͈ (w YYYd_e"rwwBK<=QشiS B/_1DA -??ҥ4SNV\\ K~1`?r -|[>~Fvj[ e\g- N,MmL[9o)]r?XχK@V@Cvޱnq[XHlT[\^D|CLb7>'r-G3 (7 &&&w̘1A^KWUUs ^LLhѢQRRoQrfTō1 @,87xFCI|̀?ȟSZZw B l ױўp`:=6;_necoJ~Jpg kx,Zw $at(dҁC@ k{?xOLLlEرcGo޼_[[ۂ3zj: ~ԩ$&Xǂ-i(p K|*EIJJ+?Δ&! +}Y %`z9lm?%-k0hEͅO] y~}j޿wCc,rLFA1&\4d-c}@NY&ntKX^}A0:3NHq y܋r!5UqYQ9dE;=Z;" Ν;ݺuѳgO///&-***""_>...}W` B`RSS9SҥK͛7Cν{&ݤ2 qcǒ2Wy]߶mۋ/>0rjjj[n"] `;AD+~^?~ٳ>Kgݺu dƍ$ܹs-~D\QSzOT9f@$ٹ`{nvYxnkEu-YD3k Cn>aŶ-+z[Y(*y=G+iuyX7= 324cSQ5"ft=}.Ѻ LKz6nMB}wR__VX &Q EFF'Oյk =bedd-EE_~]v3Ŝ .Zi#cСs {ٲeVVV`G}5MNN٫ tѯ/_&S{@ZoAH~ܙkk M>d֊+jHm՝zIVnUVjk Ga?>#oU|tkL=zA~JZ3U͕/:CwpOzXm`eQgU1g:w ӎ޻;u0=s}6V,;zCl]WA>7hG/=z41*JJJ1ZFbut tMbeeejjjժ=z%PD3!Ls064H-_|ɒ%9/^5DDD.]z155?h-3ZMi)P} ];iҤܶE7f__QFQV<ҷo_ӈ ȫ!&w0@ TlMndQ!qP^|P0ANᮼGP!-Xʮw^D!] ̌g6D3gqJ r>4@!ݣpI#fŊt'h(rrrt)0`yyyQ,))tA^^e{4 O_~}ǎ'1{@tk{"AP t| ICMMMj ~-xJ\0Bc„ QQQ9PR۰s444 mX1&w׻woC%,,ܳgO掫VVVMb4pڵ4Irr2"D #;oN;nLLF?pt? 2Ο&rÆ ޞ_&ϟ?8p 9f̘ڢ"lƌaÇ-i6>|(!zG-ؠ}Y=*p/Biת;w.u22|plahhHAtڜΦ)V>>>D~ ӉDhYYŋz|ԭ[!C0>8 0HTT\m[[[t,A0 P#? 0EoCYbh///:c&B(** ^jnnN,ϘŋL?hР 涪G$3g1 [2z ƍ A@׍=@?< :wܜ3 APA222֙ ,~ц/m6j޽m t|>|HwE+܂r)ޑWWזeEZ" χ3HF߾}!1 iiiٵkWTÃ.tRUUre50Bu$IJJ"X=ٶ6Duuu}ڙd&q|%D}~ovŚI1z.T G>ό$-srrT[VW ~v]ͭw{A@͘1C$|||NQ !cbb: %̔>Cȑ#]A$֨]=t萚rq[Ghh:ү_[Z_1ѝd2++KA]p,z7k/lA@9zkՖʞaaa:ԌVn;%DLU]ݠǏ?T5y%###Մ97L6-gL F1KvU11a''NPy.==?۝ }A@ѣGxf^RC4,.//WD:}ޣٷ"G2_O:$"Q=yPugȐ!ǎ2ꚸ8uE^ƈk7Kڦjw2TԦX,UL E˗-nYfׯ_o U=qvOOeXe͛7۷uZmmr劺5??k4PM,TCT}vZCC/]q] /_{?/"һm/ ,00Po߾%AΝ;j^ԴqFՍ͘1CŠ^ȷVREYNo߾ig)JKK7mڤ xyyUVV,=z&L>=++EEEi@ppztwwO>5{iwG{ny%3Lz jꌋD)>|(O_ / ^Pdd^"hrijtt+VSEtmlٲ߿A@ QSSF._\QsUSsVS4F tAqu9p0i$s'''Am}<ހ|||6{6_ZaÆP j3fիWb?l4uԪ*;/F+W4|*I5jj'.^ئ4kĨ=yĠyyy*8p?DA@577o۶M-"}#Qs,ۓD "*ɓ/HϺu6{svv6PTTxb5b"i͛7mj@ZZZUT:4e"HKԛӧ/`GGLLŹsrGGիW[\m\\\݅L Eii#t󙙙 .6l7a 1o޼yR;99?|yYhĂ۷Y!!!v?ڈqINrppam:?&$$H~T 9^L&[װmi7K4o޼μk`)gO$IX1xR_|=5JdfϞ-{~vׯ_,Ye" gHk6?u=zTjݻ {`2}qqs[X1i@YYYNNNvv۷ekݿӇGOKKs玤ITNl%I"SRR:$)J2ٳg'(z(MMMZYYY]]-=oPAA6HB4G>DA@.\͛;v]6G&EEEj8DbO|}}&A`jm.L߾}۔#A,HMMզΜ9Ӡ(N<D7 ~~~Zф" Cbp̙l -^<== |"ׯ_k+uf[N8j HDP__8tP///Cl"#::zM._ #F8;;xc>k׮={lٲ%<<Ç|Aarٳ"Dco޼),,,))  D@ "A D@ "A D@ "A D@ "A D{ygU©eXIfMM*JR(iZ ASCIIScreenshotp%tEXtdate:create2024-12-04T11:14:37+00:00V %tEXtdate:modify2024-12-04T11:14:37+00:00'VBZ(tEXtdate:timestamp2024-12-04T11:15:05+00:00qtEXtexif:ExifOffset90YޛtEXtexif:PixelXDimension181695HtEXtexif:PixelYDimension2446LotEXtexif:UserCommentASCIIZ(tEXticc:copyrightCopyright Apple Inc., 2024 tEXticc:descriptionDisplaytEXttiff:Orientation1;tEXtxmp:PixelXDimension1816ŘtEXtxmp:PixelYDimension2446użtEXtxmp:UserCommentScreenshot|IENDB`c-blosc2-3.1.5/blosc2.pc.in000066400000000000000000000004731521743436100152530ustar00rootroot00000000000000libdir=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_LIBDIR@ includedir=@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_INCLUDEDIR@ Name: blosc2 Description: A blocking, shuffling and lossless compression library URL: https://blosc.org/ Version: @BLOSC2_VERSION_STRING@ Requires: Libs: -L${libdir} -lblosc2 Cflags: -I${includedir} c-blosc2-3.1.5/cmake/000077500000000000000000000000001521743436100142125ustar00rootroot00000000000000c-blosc2-3.1.5/cmake/FindLZ4.cmake000066400000000000000000000011311521743436100164220ustar00rootroot00000000000000find_path(LZ4_INCLUDE_DIR lz4.h) find_library(LZ4_LIBRARY NAMES lz4 liblz4) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(LZ4 DEFAULT_MSG LZ4_LIBRARY LZ4_INCLUDE_DIR) if(LZ4_FOUND) set(LZ4_INCLUDE_DIRS "${LZ4_INCLUDE_DIR}") set(LZ4_LIBRARIES "${LZ4_LIBRARY}") if(NOT TARGET LZ4::lz4) add_library(LZ4::lz4 UNKNOWN IMPORTED) set_target_properties(LZ4::lz4 PROPERTIES IMPORTED_LOCATION "${LZ4_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${LZ4_INCLUDE_DIR}") endif() endif() mark_as_advanced(LZ4_INCLUDE_DIR LZ4_LIBRARY) c-blosc2-3.1.5/cmake/FindSIMD.cmake000066400000000000000000000043061521743436100165540ustar00rootroot00000000000000# Check if SSE/AVX instructions are available on the machine where # the project is compiled. if(CMAKE_SYSTEM_NAME MATCHES "Linux") file(READ /proc/cpuinfo CPUINFO) string(REGEX REPLACE "^.*(sse2).*$" "\\1" SSE_THERE "${CPUINFO}") string(COMPARE EQUAL "sse2" "${SSE_THERE}" SSE2_TRUE) if(SSE2_TRUE) set(SSE2_FOUND true CACHE BOOL "SSE2 available on host") else() set(SSE2_FOUND false CACHE BOOL "SSE2 available on host") endif() string(REGEX REPLACE "^.*(avx2).*$" "\\1" SSE_THERE "${CPUINFO}") string(COMPARE EQUAL "avx2" "${SSE_THERE}" AVX2_TRUE) if(AVX2_TRUE) set(AVX2_FOUND true CACHE BOOL "AVX2 available on host") else() set(AVX2_FOUND false CACHE BOOL "AVX2 available on host") endif() elseif(CMAKE_SYSTEM_NAME MATCHES "Darwin") execute_process(COMMAND /usr/sbin/sysctl -a OUTPUT_VARIABLE CPUINFO_ALL) string(REGEX MATCH ".*machdep.cpu.features.*" CPUINFO "${CPUINFO_ALL}") string(REGEX REPLACE "^.*[^S](SSE2).*$" "\\1" SSE_THERE "${CPUINFO}") string(COMPARE EQUAL "SSE2" "${SSE_THERE}" SSE2_TRUE) if(SSE2_TRUE) set(SSE2_FOUND true CACHE BOOL "SSE2 available on host") else() set(SSE2_FOUND false CACHE BOOL "SSE2 available on host") endif() string(REGEX MATCH ".*machdep.cpu.leaf7_features.*" CPUINFO "${CPUINFO_ALL}") string(REGEX REPLACE "^.*(AVX2).*$" "\\1" SSE_THERE "${CPUINFO}") string(COMPARE EQUAL "AVX2" "${SSE_THERE}" AVX2_TRUE) if(AVX2_TRUE) set(AVX2_FOUND true CACHE BOOL "AVX2 available on host") else() set(AVX2_FOUND false CACHE BOOL "AVX2 available on host") endif() unset(CPUINFO_ALL) elseif(CMAKE_SYSTEM_NAME MATCHES "Windows") # TODO. For now supposing SSE2 is safe enough set(SSE2_FOUND true CACHE BOOL "SSE2 available on host") set(AVX2_FOUND false CACHE BOOL "AVX2 available on host") else() set(SSE2_FOUND true CACHE BOOL "SSE2 available on host") set(AVX2_FOUND false CACHE BOOL "AVX2 available on host") endif() if(NOT SSE2_FOUND) message(STATUS "Could not find hardware support for SSE2 on this machine.") endif() if(NOT AVX2_FOUND) message(STATUS "Could not find hardware support for AVX2 on this machine.") endif() mark_as_advanced(SSE2_FOUND AVX2_FOUND) c-blosc2-3.1.5/cmake/FindZFP.cmake000066400000000000000000000015521521743436100164570ustar00rootroot00000000000000# Find the ZFP compression library. # # Defines: # ZFP_FOUND # ZFP_INCLUDE_DIRS # ZFP_LIBRARIES # zfp imported target, when found # # This module is intentionally small; prefer the upstream zfp CMake package # when available via find_package(zfp CONFIG). find_path(ZFP_INCLUDE_DIR NAMES zfp.h) find_library(ZFP_LIBRARY NAMES zfp) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(ZFP REQUIRED_VARS ZFP_LIBRARY ZFP_INCLUDE_DIR) if(ZFP_FOUND) set(ZFP_INCLUDE_DIRS "${ZFP_INCLUDE_DIR}") set(ZFP_LIBRARIES "${ZFP_LIBRARY}") if(NOT TARGET zfp) add_library(zfp UNKNOWN IMPORTED) set_target_properties(zfp PROPERTIES IMPORTED_LOCATION "${ZFP_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${ZFP_INCLUDE_DIR}") endif() endif() mark_as_advanced(ZFP_INCLUDE_DIR ZFP_LIBRARY) c-blosc2-3.1.5/cmake/FindZLIB_NG.cmake000066400000000000000000000040031521743436100171360ustar00rootroot00000000000000find_path(ZLIB_NG_INCLUDE_DIR NAMES zlib-ng.h) if(ZLIB_INCLUDE_DIRS) set(ZLIB_NG_LIBRARY_DIRS ${ZLIB_NG_INCLUDE_DIR}) if("${ZLIB_NG_LIBRARY_DIRS}" MATCHES "/include$") # Strip off the trailing "/include" in the path. GET_FILENAME_COMPONENT(ZLIB_NG_LIBRARY_DIRS ${ZLIB_NG_LIBRARY_DIRS} PATH) endif("${ZLIB_NG_LIBRARY_DIRS}" MATCHES "/include$") if(EXISTS "${ZLIB_NG_LIBRARY_DIRS}/lib") set(ZLIB_NG_LIBRARY_DIRS ${ZLIB_NG_LIBRARY_DIRS}/lib) endif(EXISTS "${ZLIB_NG_LIBRARY_DIRS}/lib") endif() find_library(ZLIB_NG_LIBRARY NAMES z-ng libz-ng zlib-ng libz-ng.a) set(ZLIB_NG_LIBRARIES ${ZLIB_NG_LIBRARY}) set(ZLIB_NG_INCLUDE_DIR ${ZLIB_NG_INCLUDE_DIR}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(ZLIB_NG DEFAULT_MSG ZLIB_NG_LIBRARY ZLIB_NG_INCLUDE_DIR) if(ZLIB_NG_INCLUDE_DIR AND ZLIB_NG_LIBRARIES) set(ZLIB_NG_FOUND TRUE) else(ZLIB_NG_INCLUDE_DIR AND ZLIB_NG_LIBRARIES) set(ZLIB_NG_FOUND FALSE) endif(ZLIB_NG_INCLUDE_DIR AND ZLIB_NG_LIBRARIES) if(ZLIB_NG_FOUND) message(STATUS "Found zlib-ng: ${ZLIB_NG_LIBRARIES}, ${ZLIB_NG_INCLUDE_DIR}") endif() #[[ Copyright https://github.com/zlib-ng/minizip-ng, 2021 Condition of use and distribution are the same as zlib: This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgement in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. ]]# c-blosc2-3.1.5/cmake/FindZSTD.cmake000066400000000000000000000011611521743436100166000ustar00rootroot00000000000000find_path(ZSTD_INCLUDE_DIR zstd.h) find_library(ZSTD_LIBRARY NAMES zstd) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(ZSTD DEFAULT_MSG ZSTD_LIBRARY ZSTD_INCLUDE_DIR) if(ZSTD_FOUND) set(ZSTD_INCLUDE_DIRS "${ZSTD_INCLUDE_DIR}") set(ZSTD_LIBRARIES "${ZSTD_LIBRARY}") if(NOT TARGET zstd::libzstd) add_library(zstd::libzstd UNKNOWN IMPORTED) set_target_properties(zstd::libzstd PROPERTIES IMPORTED_LOCATION "${ZSTD_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${ZSTD_INCLUDE_DIR}") endif() endif() mark_as_advanced(ZSTD_INCLUDE_DIR ZSTD_LIBRARY) c-blosc2-3.1.5/cmake/merge_static_libs.cmake000066400000000000000000000055301521743436100206760ustar00rootroot00000000000000# Merge several static libraries into a single output archive. # # Required variables: # OUTPUT archive to create/replace # INPUTS input archives separated with '|' # Optional variables: # AR archiver path # RANLIB ranlib path # SYSTEM_NAME CMAKE_SYSTEM_NAME # MSVC TRUE for MSVC-like toolchains if(NOT DEFINED OUTPUT OR OUTPUT STREQUAL "") message(FATAL_ERROR "merge_static_libs.cmake requires OUTPUT") endif() if(NOT DEFINED INPUTS OR INPUTS STREQUAL "") message(FATAL_ERROR "merge_static_libs.cmake requires INPUTS") endif() string(REPLACE "|" ";" _inputs "${INPUTS}") foreach(_input IN LISTS _inputs) if(NOT EXISTS "${_input}") message(FATAL_ERROR "Static library to merge does not exist: ${_input}") endif() endforeach() get_filename_component(_out_dir "${OUTPUT}" DIRECTORY) get_filename_component(_out_name_we "${OUTPUT}" NAME_WE) get_filename_component(_out_ext "${OUTPUT}" EXT) # Some Windows archivers, including llvm-lib, reject output archive names that # do not use the conventional .lib suffix. Keep the original extension last. set(_tmp "${_out_dir}/${_out_name_we}.merged${_out_ext}") file(REMOVE "${_tmp}") if(MSVC) if(NOT AR) message(FATAL_ERROR "MSVC static library merging requires AR/lib.exe") endif() execute_process( COMMAND "${AR}" /NOLOGO "/OUT:${_tmp}" ${_inputs} RESULT_VARIABLE _result) elseif(SYSTEM_NAME STREQUAL "Darwin") find_program(_libtool NAMES libtool xcrun-libtool) if(NOT _libtool) execute_process( COMMAND xcrun -find libtool OUTPUT_VARIABLE _libtool OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET) endif() if(NOT _libtool) message(FATAL_ERROR "Could not find libtool for static library merging") endif() execute_process( COMMAND "${_libtool}" -static -o "${_tmp}" ${_inputs} RESULT_VARIABLE _result) else() if(NOT AR) message(FATAL_ERROR "Static library merging requires AR") endif() set(_mri "${_out_dir}/${_out_name_we}.mri") file(WRITE "${_mri}" "CREATE ${_tmp}\n") foreach(_input IN LISTS _inputs) file(APPEND "${_mri}" "ADDLIB ${_input}\n") endforeach() file(APPEND "${_mri}" "SAVE\nEND\n") execute_process( COMMAND "${AR}" -M INPUT_FILE "${_mri}" RESULT_VARIABLE _result) file(REMOVE "${_mri}") endif() if(NOT _result EQUAL 0) file(REMOVE "${_tmp}") message(FATAL_ERROR "Failed to merge static libraries into ${OUTPUT}") endif() if(RANLIB AND NOT MSVC) execute_process(COMMAND "${RANLIB}" "${_tmp}" RESULT_VARIABLE _ranlib_result) if(NOT _ranlib_result EQUAL 0) file(REMOVE "${_tmp}") message(FATAL_ERROR "ranlib failed for merged static library ${_tmp}") endif() endif() file(REMOVE "${OUTPUT}") file(RENAME "${_tmp}" "${OUTPUT}") c-blosc2-3.1.5/cmake/toolchain-aarch64.cmake000066400000000000000000000016771521743436100204350ustar00rootroot00000000000000set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR aarch64) set(CMAKE_SYSTEM_VERSION 1) message(STATUS "Using cross-compile toolchain: ${CROSS_COMPILE_TOOLCHAIN}") set(CMAKE_C_COMPILER_TARGET "aarch64-linux-gnu") set(CMAKE_CXX_COMPILER_TARGET "aarch64-linux-gnu") set(CMAKE_CROSSCOMPILING TRUE) set(CMAKE_CROSSCOMPILING_EMULATOR qemu-aarch64 -L /usr/${CMAKE_C_COMPILER_TARGET}/) SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) find_program(C_COMPILER_FULL_PATH NAMES ${CMAKE_C_COMPILER_TARGET}-gcc) if(NOT C_COMPILER_FULL_PATH) message(FATAL_ERROR "Cross-compiler for ${CMAKE_C_COMPILER_TARGET} not found") endif() set(CMAKE_C_COMPILER ${C_COMPILER_FULL_PATH}) find_program(CXX_COMPILER_FULL_PATH NAMES g++-${CMAKE_CXX_COMPILER_TARGET} ${CMAKE_CXX_COMPILER_TARGET}-g++) if(CXX_COMPILER_FULL_PATH) set(CMAKE_CXX_COMPILER ${CXX_COMPILER_FULL_PATH}) endif() c-blosc2-3.1.5/cmake/toolchain-armhf.cmake000066400000000000000000000017431521743436100202740ustar00rootroot00000000000000set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR arm) set(CMAKE_SYSTEM_VERSION 1) message(STATUS "Using cross-compile toolchain: ${CROSS_COMPILE_TOOLCHAIN}") set(CMAKE_C_COMPILER_TARGET arm-linux-gnueabihf) set(CMAKE_CXX_COMPILER_TARGET arm-linux-gnueabihf) set(CMAKE_CROSSCOMPILING TRUE) set(CMAKE_CROSSCOMPILING_EMULATOR qemu-arm -L /usr/${CMAKE_C_COMPILER_TARGET}/) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) find_program(C_COMPILER_FULL_PATH NAMES ${CMAKE_C_COMPILER_TARGET}-gcc) if(NOT C_COMPILER_FULL_PATH) message(FATAL_ERROR "Cross-compiler for ${CMAKE_C_COMPILER_TARGET} not found") endif() set(CMAKE_C_COMPILER ${C_COMPILER_FULL_PATH}) find_program(CXX_COMPILER_FULL_PATH NAMES g++-${CMAKE_CXX_COMPILER_TARGET} ${CMAKE_CXX_COMPILER_TARGET}-g++) if(CXX_COMPILER_FULL_PATH) set(CMAKE_CXX_COMPILER ${CXX_COMPILER_FULL_PATH}) endif() c-blosc2-3.1.5/cmake/toolchain-armsf.cmake000066400000000000000000000021111521743436100202750ustar00rootroot00000000000000set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR arm) set(CMAKE_SYSTEM_VERSION 1) message(STATUS "Using cross-compile toolchain: ${CROSS_COMPILE_TOOLCHAIN}") if(NOT DEFINED CMAKE_C_COMPILER_TARGET) set(CMAKE_C_COMPILER_TARGET arm-linux-gnueabi) endif() if(NOT DEFINED CMAKE_CXX_COMPILER_TARGET) set(CMAKE_CXX_COMPILER_TARGET arm-linux-gnueabi) endif() set(CMAKE_CROSSCOMPILING TRUE) set(CMAKE_CROSSCOMPILING_EMULATOR qemu-arm -L /usr/${CMAKE_C_COMPILER_TARGET}/) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) find_program(C_COMPILER_FULL_PATH NAMES ${CMAKE_C_COMPILER_TARGET}-gcc) if(NOT C_COMPILER_FULL_PATH) message(FATAL_ERROR "Cross-compiler for ${CMAKE_C_COMPILER_TARGET} not found") endif() set(CMAKE_C_COMPILER ${C_COMPILER_FULL_PATH}) find_program(CXX_COMPILER_FULL_PATH NAMES g++-${CMAKE_CXX_COMPILER_TARGET} ${CMAKE_CXX_COMPILER_TARGET}-g++) if(CXX_COMPILER_FULL_PATH) set(CMAKE_CXX_COMPILER ${CXX_COMPILER_FULL_PATH}) endif() c-blosc2-3.1.5/cmake_uninstall.cmake.in000066400000000000000000000017631521743436100177210ustar00rootroot00000000000000if (NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") list(REVERSE files) foreach (file ${files}) message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") if (EXISTS "$ENV{DESTDIR}${file}") execute_process( COMMAND @CMAKE_COMMAND@ -E remove "$ENV{DESTDIR}${file}" OUTPUT_VARIABLE rm_out RESULT_VARIABLE rm_retval ) if(NOT ${rm_retval} EQUAL 0) message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") endif (NOT ${rm_retval} EQUAL 0) else (EXISTS "$ENV{DESTDIR}${file}") message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") endif (EXISTS "$ENV{DESTDIR}${file}") endforeach(file) c-blosc2-3.1.5/code_of_conduct.md000066400000000000000000000004361521743436100165740ustar00rootroot00000000000000# Code of Conduct The Blosc community has adopted a Code of Conduct that we expect project participants to adhere to. Please read the [full text](https://github.com/Blosc/community/blob/master/code_of_conduct.md) so that you can understand what actions will and will not be tolerated. c-blosc2-3.1.5/compat/000077500000000000000000000000001521743436100144155ustar00rootroot00000000000000c-blosc2-3.1.5/compat/CMakeLists.txt000066400000000000000000000023571521743436100171640ustar00rootroot00000000000000# build the filegen utility link_directories(${PROJECT_BINARY_DIR}/blosc) add_executable(filegen filegen.c) target_link_libraries(filegen blosc_testing) add_dependencies(filegen blosc_testing) add_executable(filegen-vl filegen-vl.c) target_link_libraries(filegen-vl blosc_testing) add_dependencies(filegen-vl blosc_testing) # tests if(BUILD_TESTS) option(TEST_INCLUDE_COMPAT "Include compat checks in the tests" ON) if(TEST_INCLUDE_COMPAT) file(GLOB DATAFILES *.cdata) foreach(datafile ${DATAFILES}) # Don't test data if compressor is deactivated if((datafile MATCHES "zlib" AND DEACTIVATE_ZLIB) OR (datafile MATCHES "zstd" AND DEACTIVATE_ZSTD)) continue() endif() get_filename_component(fname ${datafile} NAME) if(fname MATCHES "^bitshuffle") set(op_name "bitunshuffle") elseif(fname MATCHES "^shuffle") set(op_name "unshuffle") else() set(op_name "decompress") endif() add_test(NAME test_compat_${fname} COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ ${op_name} ${datafile}) endforeach() endif() endif() c-blosc2-3.1.5/compat/README.rst000066400000000000000000000004571521743436100161120ustar00rootroot00000000000000Compressed datafiles for testing backward/forward compatibility =============================================================== The files here have been created with different versions of the C-Blosc library and are meant to test backward/forward compatibility among different versions of the library. c-blosc2-3.1.5/compat/bitshuffle-2.20.0.cdata000066400000000000000000172044001521743436100202730ustar00rootroot00000000000000c-blosc2-3.1.5/compat/blosc-1.11.1-blosclz.cdata000066400000000000000000000641601521743436100207070ustar00rootroot00000000000000 =ph0z (X5BO6]?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~! !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~! !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~! !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~! !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !6 ?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~y  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@AB6xtc-blosc2-3.1.5/compat/blosc-1.11.1-lz4.cdata000066400000000000000000001015121521743436100177410ustar00rootroot000000000000001 =J0Op"3DUfx  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~/      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~/P  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~/      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~/      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~/      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~/      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ P  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~/      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ P  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~/      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ P1   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~o      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@A-@4njPc-blosc2-3.1.5/compat/blosc-1.11.1-lz4hc.cdata000066400000000000000000000773451521743436100202740ustar00rootroot000000000000001 =~ L!xBc(!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~/P(!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~/P(!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~/   P=  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~q      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@A@-  nlPc-blosc2-3.1.5/compat/blosc-1.11.1-zlib.cdata000066400000000000000000000374601521743436100202020ustar00rootroot00000000000000q =0? !|1pxC`ضmvǶm۶ӱm۶m۶~gnP 8 :C 9 ;#8ȣ:c9;L8ēL:SL9L;38̳:s9; ,‹,Kgɥ^f_aŕV^eV_c͵^g`Í6d6b˭faǝvevcϽg?>?ȣ>?ē{)vgu9w^t%^vW^u5^w 7t-vwu=w>#>O>3> /+o;~'~_~7~?/?(nA,n!↌*na↍.nF)nQF-n1ƌ+nqƍ/n &(nI&,n)⦌*ni⦍.nf)nYf-n9挛+ny捛/n[(nE[,n%dRqK-lrqǭbJq+ǭjjqǭfZqkǭnzqmaFqmifqmeVq[mmvqcNq;knqg^q{o~qw`AqwhaqwdQqGwlqqǝwbIq;9SN;=3Ί;;s΋;? .8K.<+㮊:k㮋>n9[n=;;{?{8G{<'㞊{:g㞋{>^{9W^{=7ފ{;wދ{?>8O></㾊:o㾋>~9_~=?;0n4nㆈ2nㆉ6nF1nF5nƈ3nƉ7n&0n&4n㦈2n㦉6nf1nf5n戛3n扛7n[0n[4n㖈OܒqK-Lܲq-B܊q+ŭJܪqŭFܚqkŭNܺqŭA܆qmIܦqmEܖq[mMܶqmC܎q;KܮqGܞq{Oܾqw@܁qwHܡqwDܑqGwLܱqwB܉q'7SN;-3Ό;+s΍;/ .(K.,+⮌*k⮍.n)[n-;+{/{(G{,'➌{*g➍{.^{)W^{-7ތ{+wލ{/>(O>,/⾌*o⾍.~)_~-?+ n$nи"nȸ↎&nظ↏!nĸF%nԸF#n̸Ǝ'nܸƏ n¸&$nҸ&"nʸ⦎&nڸ⦏!nƸf%nָf#nθ掛'n޸[ n[$nѸ["?qK-t2q-| q+ƭr*qƭzqkƭv:qƭ~qmq&qmyq[mu6qm}q;s.q{q{w>qqwp!qwxqGwt1qw| q'Ɲ߸N;5Έ;3Ή;7.0.4㮈2㮉6n1n537{0{4㞈{2㞉{6^{1^{5ވ{3މ{7>0>4㾈2㾉6~1~537n8nAnF9nQF=n1Ɗ;nqƋ?n &8nI&nf9nYf=n9抛;ny招?n[8nE[$Ӹ>"˸⾎&۸⾏!Ǹ~%׸~#ϸ'߸g`/<pxC`m۶e۶m˶m۶m۶gdxA|!za~GyQG}1{q 'xI'|)zi~gyYg}9{y‹,K,,+ʫk묻lƛl[ll;λ{x?ȣ>?ēN>N?̳>ο‹..ʫ뮿ƛnnλ~ɧ~ŗ^~^ͷ~Ï>>˯ǟ~~Ͽ܀q 7Hܠq 7DܐqC 7Lܰq 7B܈q#ō7Jܨqō7Fܘqcō7Nܸqō7A܄qM7IܤqM7EܔqSM7MܴqM7C܌q37Kܬq7Gܜqs7Oܼq@܂q[(nE[,n%▌[*ne▍[.nV[)nUV[-n5֌[+nu֍[/n 6(nM6,n-ⶌ*nmⶍ.nv)n]v-n=+n}/n;(CwhaqwdQqGwlqqǝwbIq'ǝwjiqǝwfYqgǝwnyq]waEq]wieq]weUqW]wmuqwcMq7wkmqwg]qwwo}q=`Cq=hcq=dSqO=lsqǽbKq/ǽjkqǽf[qoǽn{q}aGq}igq}eWq_}mwqcOq?koqg_qoq 7p q 7xqC 7t0q 7|q#ƍ7r(qƍ7zqcƍ7v8qƍ7~qM7q$qM7yqSM7u4qM7} q37s,q7{qs7wnV[9nUV[=n5֊[;nu֋[?n 68nM6nv9n]v=n=;n}?;8wXqGwTqw\q'ĝwRqĝwZqgĝwVqĝw^q]wQq]wYqW]wUq]w]q7wSqw[qwwWqw_q=Pq=XqO=Tq=\q/ĽRqĽZqoĽVqĽ^q}Qq}Yq_}Uq}]q?Sq[qWq7Ϡ? 3Q=pxC`m۶e۶m˶m۶m۶gdxA|!za~GyQG}1{q 'xI'|)zi~gyYg}9{y‹,K,,+ʫk묻lƛl[ll;λ{x?ȣ>?ēN>N?̳>ο‹..ʫ뮿ƛnnλ~ɧ~ŗ^~^ͷ~Ï>>˯ǟ~~Ͽ܀q 7Hܠq 7DܐqC 7Lܰq 7B܈q#ō7Jܨqō7Fܘqcō7Nܸqō7A܄qM7IܤqM7EܔqSM7MܴqM7C܌q37Kܬq7Gܜqs7Oܼq@܂q[(nE[,n%▌[*ne▍[.nV[)nUV[-n5֌[+nu֍[/n 6(nM6,n-ⶌ*nmⶍ.nv)n]v-n=+n}/n;(CwhaqwdQqGwlqqǝwbIq'ǝwjiqǝwfYqgǝwnyq]waEq]wieq]weUqW]wmuqwcMq7wkmqwg]qwwo}q=`Cq=hcq=dSqO=lsqǽbKq/ǽjkqǽf[qoǽn{q}aGq}igq}eWq_}mwqcOq?koqg_qoq 7p q 7xqC 7t0q 7|q#ƍ7r(qƍ7zqcƍ7v8qƍ7~qM7q$qM7yqSM7u4qM7} q37s,q7{qs7wnV[9nUV[=n5֊[;nu֋[?n 68nM6nv9n]v=n=;n}?;8wXqGwTqw\q'ĝwRqĝwZqgĝwVqĝw^q]wQq]wYqW]wUq]w]q7wSqw[qwwWqw_q=Pq=XqO=Tq=\q/ĽRqĽZqoĽVqĽ^q}Qq}Yq_}Uq}]q?Sq[qWq7ϐ?C 3= xSa@m۶m۶mfڶm۶mVs~o3 <Ƞ >ĐC =̰ ?ˆ#<ʨ>Ƙc=θ?M<ɤM>ŔSM=ʹM?Ì3<ˬ>ǜs=ϼͿ -Ȣ-ĒK-̲-Š+ʪƚkκ뭿mɦmŖ[mͶmÎ;ˮǞ{ϾoN;9SN;=3Ί;;s΋;? .8K.<+㮊:k㮋>n9[n=;;{?{8G{<'㞊{:g㞋{>^{9W^{=7ފ{;wދ{?>8O></㾊:o㾋>~9_~=?;(nA,n!↌*na↍.nF)nQF-n1ƌ+nqƍ/n &(nI&,n)⦌*ni⦍.nf)nYf-n9挛+ny捛/n[(nE[,n%▌[*ne▍[.nV[)nUV[-n5֌[+nu֍[/n 6(nM6,n-ⶌ*nmⶍ.nv)n]v-n=+n}W~qw`AqwhܿwXqGwTqw\q'ĝwRqĝwZqgĝwVqĝw^q]wQq]wYqW]wUq]w]q7wSqw[qwwWqw_q=Pq=XqO=Tq=\q/ĽRqĽZqoĽVqĽ^q}Qq}Yq_}Uq}]q?Sq[qWq 7`@q 7h`q 7dPqC 7lpqǍ7bHq#Ǎ7jhqǍ7fXqcǍ7nxqM7aDqM7idqM7eTqSM7mtq7cLq37klq7g\qs7o|q-`BK 56~!kc-blosc2-3.1.5/compat/blosc-1.11.1-zstd.cdata000066400000000000000000000067611521743436100202260ustar00rootroot00000000000000 = n n(/d  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~T  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~{|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>>|>|>|>|>>|>|>|>|>|>|>|>?Tl ~l _Wl> ^G E j(/d  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~T  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~{|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>>|>|>|>|>>|>|>|>|>|>|>|>?Tl ~l _WL9E9j(/d  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~T  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~{|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>>|>|>|>|>>|>|>|>|>|>|>|>?Tl  ~l  _WL9E9(/ d  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~~<>|>|>|>|>|>|>|>|>|>|>||>|>|>|>|>|>|>|>|>|>|>|>=B$D~ w7|>||>|>|>|>|>|>|e |;؅G <#@>D9E9c-blosc2-3.1.5/compat/blosc-1.14.0-blosclz.cdata000066400000000000000000001066401521743436100207110ustar00rootroot00000000000000 =P $%Y.7@I-Sb\enx6k ?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! ! ?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! ! ?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! ! ?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! ! ?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! ! ?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! ! ?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! ! ?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! ! ?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! ! ?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !  ! ?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !  ! ?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !  ! ?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !  ! ?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !  ! ?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! !1?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~x  !"#$%&'()*+,-./0123456789:;<=>?@AB6@xduc-blosc2-3.1.5/compat/blosc-1.14.0-lz4.cdata000066400000000000000000001066531521743436100177560ustar00rootroot00000000000000! =P (%^.7@J6Sl\enxDz   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P  P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P  P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P  P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P  P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P  P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P-  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~n      !"#$%&'()*+,-./0123456789:;<=>?@AB,?3ojPc-blosc2-3.1.5/compat/blosc-1.14.0-lz4hc.cdata000066400000000000000000001066471521743436100202740ustar00rootroot00000000000000! =P (%^.7@J6Sl\enxDz   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P  P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P  P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P  P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P  P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P  P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P)  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~n      !"#$%&'()*+,-./0123456789:;<=>?@AB,niPc-blosc2-3.1.5/compat/blosc-1.14.0-zlib.cdata000066400000000000000000000500621521743436100201750ustar00rootroot00000000000000a =2PPz &Q|$).3(9S>~CHMIxڶm۶նm۶Zm۶mw}G(p*tG)rѢLj+v'H(qɒH*ugȘ)sٲș+w(XpŊ(YtWXrժרOu֫ߠaM6kޢemڶkߡc]vޣg}?`C>bcƎ?aSN>csΛ`K.[+VZf6n۶عk8xǎ8y_x׮߸y?xϞx߾w?r)x]Ɩmm۶m۶m۶mC.   ."..2. .*..:..&..6.....>._Dĸ$dTԸ4t L̸,l\ܸ<|qpqEpEqpq%p%qpqepeqpqpqpqUpUqpq5p5qpqupuqpq p qpqMpMqpq-p-qpqmpmqpqpqpq]p]qpq=p=qpq}p}qpqpqpqCpCqpq#p#qpqcpcqpqpqpqSpSqpq3p3qpqspsqpq p qpqKpKqpq+p+qpqkpkqpqpqpq[p[qpq;p;qpq{p{qpqpqpqGpGqpq'p'qpqgpgqpqpqpqWpWqpq7p7qpqwpwqpqpqpqOpOqpq/p/qpqopoqpqpqpq_p_qpq?p?qpqp*SrTx jTx jIxڶm۶նm۶Zm۶mw}G(p*tG)rѢLj+v'H(qɒH*ugȘ)sٲș+w(XpŊ(YtWXrժרOu֫ߠaM6kޢemڶkߡc]vޣg}?`C>bcƎ?aSN>csΛ`K.[+VZf6n۶عk8xǎ8y_x׮߸y?xϞx߾w?r)x]Ɩmm۶m۶m۶mC.   ."..2. .*..:..&..6.....>._Dĸ$dTԸ4t L̸,l\ܸ<|qpqEpEqpq%p%qpqepeqpqpqpqUpUqpq5p5qpqupuqpq p qpqMpMqpq-p-qpqmpmqpqpqpq]p]qpq=p=qpq}p}qpqpqpqCpCqpq#p#qpqcpcqpqpqpqSpSqpq3p3qpqspsqpq p qpqKpKqpq+p+qpqkpkqpqpqpq[p[qpq;p;qpq{p{qpqpqpqGpGqpq'p'qpqgpgqpqpqpqWpWqpq7p7qpqwpwqpqpqpqOpOqpq/p/qpqopoqpqpqpq_p_qpq?p?qpqp*SrUx #j@ Tx jIxڶm۶նm۶Zm۶mw}G(p*tG)rѢLj+v'H(qɒH*ugȘ)sٲș+w(XpŊ(YtWXrժרOu֫ߠaM6kޢemڶkߡc]vޣg}?`C>bcƎ?aSN>csΛ`K.[+VZf6n۶عk8xǎ8y_x׮߸y?xϞx߾w?r)x]Ɩmm۶m۶m۶mC.   ."..2. .*..:..&..6.....>._Dĸ$dTԸ4t L̸,l\ܸ<|qpqEpEqpq%p%qpqepeqpqpqpqUpUqpq5p5qpqupuqpq p qpqMpMqpq-p-qpqmpmqpqpqpq]p]qpq=p=qpq}p}qpqpqpqCpCqpq#p#qpqcpcqpqpqpqSpSqpq3p3qpqspsqpq p qpqKpKqpq+p+qpqkpkqpqpqpq[p[qpq;p;qpq{p{qpqpqpqGpGqpq'p'qpqgpgqpqpqpqWpWqpq7p7qpqwpwqpqpqpqOpOqpq/p/qpqopoqpqpqpq_p_qpq?p?qpqp*SrUx 莨5Tx jIxڶm۶նm۶Zm۶mw}G(p*tG)rѢLj+v'H(qɒH*ugȘ)sٲș+w(XpŊ(YtWXrժרOu֫ߠaM6kޢemڶkߡc]vޣg}?`C>bcƎ?aSN>csΛ`K.[+VZf6n۶عk8xǎ8y_x׮߸y?xϞx߾w?r)x]Ɩmm۶m۶m۶mC.   ."..2. .*..:..&..6.....>._Dĸ$dTԸ4t L̸,l\ܸ<|qpqEpEqpq%p%qpqepeqpqpqpqUpUqpq5p5qpqupuqpq p qpqMpMqpq-p-qpqmpmqpqpqpq]p]qpq=p=qpq}p}qpqpqpqCpCqpq#p#qpqcpcqpqpqpqSpSqpq3p3qpqspsqpq p qpqKpKqpq+p+qpqkpkqpqpqpq[p[qpq;p;qpq{p{qpqpqpqGpGqpq'p'qpqgpgqpqpqpqWpWqpq7p7qpqwpwqpqpqpqOpOqpq/p/qpqopoqpqpqpq_p_qpq?p?qpqp*SrUx! jw.Tx jIxڶm۶նm۶Zm۶mw}G(p*tG)rѢLj+v'H(qɒH*ugȘ)sٲș+w(XpŊ(YtWXrժרOu֫ߠaM6kޢemڶkߡc]vޣg}?`C>bcƎ?aSN>csΛ`K.[+VZf6n۶عk8xǎ8y_x׮߸y?xϞx߾w?r)x]Ɩmm۶m۶m۶mC.   ."..2. .*..:..&..6.....>._Dĸ$dTԸ4t L̸,l\ܸ<|qpqEpEqpq%p%qpqepeqpqpqpqUpUqpq5p5qpqupuqpq p qpqMpMqpq-p-qpqmpmqpqpqpq]p]qpq=p=qpq}p}qpqpqpqCpCqpq#p#qpqcpcqpqpqpqSpSqpq3p3qpqspsqpq p qpqKpKqpq+p+qpqkpkqpqpqpq[p[qpq;p;qpq{p{qpqpqpqGpGqpq'p'qpqgpgqpqpqpqWpWqpq7p7qpqwpwqpqpqpqOpOqpq/p/qpqopoqpqpqpq_p_qpq?p?qpqp*SrUx! j=Tx jIxڶm۶նm۶Zm۶mw}G(p*tG)rѢLj+v'H(qɒH*ugȘ)sٲș+w(XpŊ(YtWXrժרOu֫ߠaM6kޢemڶkߡc]vޣg}?`C>bcƎ?aSN>csΛ`K.[+VZf6n۶عk8xǎ8y_x׮߸y?xϞx߾w?r)x]Ɩmm۶m۶m۶mC.   ."..2. .*..:..&..6.....>._Dĸ$dTԸ4t L̸,l\ܸ<|qpqEpEqpq%p%qpqepeqpqpqpqUpUqpq5p5qpqupuqpq p qpqMpMqpq-p-qpqmpmqpqpqpq]p]qpq=p=qpq}p}qpqpqpqCpCqpq#p#qpqcpcqpqpqpqSpSqpq3p3qpqspsqpq p qpqKpKqpq+p+qpqkpkqpqpqpq[p[qpq;p;qpq{p{qpqpqpqGpGqpq'p'qpqgpgqpqpqpqWpWqpq7p7qpqwpwqpqpqpqOpOqpq/p/qpqopoqpqpqpq_p_qpq?p?qpqp*SrUx! jgLTx jIxڶm۶նm۶Zm۶mw}G(p*tG)rѢLj+v'H(qɒH*ugȘ)sٲș+w(XpŊ(YtWXrժרOu֫ߠaM6kޢemڶkߡc]vޣg}?`C>bcƎ?aSN>csΛ`K.[+VZf6n۶عk8xǎ8y_x׮߸y?xϞx߾w?r)x]Ɩmm۶m۶m۶mC.   ."..2. .*..:..&..6.....>._Dĸ$dTԸ4t L̸,l\ܸ<|qpqEpEqpq%p%qpqepeqpqpqpqUpUqpq5p5qpqupuqpq p qpqMpMqpq-p-qpqmpmqpqpqpq]p]qpq=p=qpq}p}qpqpqpqCpCqpq#p#qpqcpcqpqpqpqSpSqpq3p3qpqspsqpq p qpqKpKqpq+p+qpqkpkqpqpqpq[p[qpq;p;qpq{p{qpqpqpqGpGqpq'p'qpqgpgqpqpqpqWpWqpq7p7qpqwpwqpqpqpqOpOqpq/p/qpqopoqpqpqpq_p_qpq?p?qpqp*SrUx! j[Tx jIxڶm۶նm۶Zm۶mw}G(p*tG)rѢLj+v'H(qɒH*ugȘ)sٲș+w(XpŊ(YtWXrժרOu֫ߠaM6kޢemڶkߡc]vޣg}?`C>bcƎ?aSN>csΛ`K.[+VZf6n۶عk8xǎ8y_x׮߸y?xϞx߾w?r)x]Ɩmm۶m۶m۶mC.   ."..2. .*..:..&..6.....>._Dĸ$dTԸ4t L̸,l\ܸ<|qpqEpEqpq%p%qpqepeqpqpqpqUpUqpq5p5qpqupuqpq p qpqMpMqpq-p-qpqmpmqpqpqpq]p]qpq=p=qpq}p}qpqpqpqCpCqpq#p#qpqcpcqpqpqpqSpSqpq3p3qpqspsqpq p qpqKpKqpq+p+qpqkpkqpqpqpq[p[qpq;p;qpq{p{qpqpqpqGpGqpq'p'qpqgpgqpqpqpqWpWqpq7p7qpqwpwqpqpqpqOpOqpq/p/qpqopoqpqpqpq_p_qpq?p?qpqp*SrUx! jWjTx jIxڶm۶նm۶Zm۶mw}G(p*tG)rѢLj+v'H(qɒH*ugȘ)sٲș+w(XpŊ(YtWXrժרOu֫ߠaM6kޢemڶkߡc]vޣg}?`C>bcƎ?aSN>csΛ`K.[+VZf6n۶عk8xǎ8y_x׮߸y?xϞx߾w?r)x]Ɩmm۶m۶m۶mC.   ."..2. .*..:..&..6.....>._Dĸ$dTԸ4t L̸,l\ܸ<|qpqEpEqpq%p%qpqepeqpqpqpqUpUqpq5p5qpqupuqpq p qpqMpMqpq-p-qpqmpmqpqpqpq]p]qpq=p=qpq}p}qpqpqpqCpCqpq#p#qpqcpcqpqpqpqSpSqpq3p3qpqspsqpq p qpqKpKqpq+p+qpqkpkqpqpqpq[p[qpq;p;qpq{p{qpqpqpqGpGqpq'p'qpqgpgqpqpqpqWpWqpq7p7qpqwpwqpqpqpqOpOqpq/p/qpqopoqpqpqpq_p_qpq?p?qpqp*SrUx! jyTx jIxڶm۶նm۶Zm۶mw}G(p*tG)rѢLj+v'H(qɒH*ugȘ)sٲș+w(XpŊ(YtWXrժרOu֫ߠaM6kޢemڶkߡc]vޣg}?`C>bcƎ?aSN>csΛ`K.[+VZf6n۶عk8xǎ8y_x׮߸y?xϞx߾w?r)x]Ɩmm۶m۶m۶mC.   ."..2. .*..:..&..6.....>._Dĸ$dTԸ4t L̸,l\ܸ<|qpqEpEqpq%p%qpqepeqpqpqpqUpUqpq5p5qpqupuqpq p qpqMpMqpq-p-qpqmpmqpqpqpq]p]qpq=p=qpq}p}qpqpqpqCpCqpq#p#qpqcpcqpqpqpqSpSqpq3p3qpqspsqpq p qpqKpKqpq+p+qpqkpkqpqpqpq[p[qpq;p;qpq{p{qpqpqpqGpGqpq'p'qpqgpgqpqpqpqWpWqpq7p7qpqwpwqpqpqpqOpOqpq/p/qpqopoqpqpqpq_p_qpq?p?qpqp*SrUx! jGTx jIxڶm۶նm۶Zm۶mw}G(p*tG)rѢLj+v'H(qɒH*ugȘ)sٲș+w(XpŊ(YtWXrժרOu֫ߠaM6kޢemڶkߡc]vޣg}?`C>bcƎ?aSN>csΛ`K.[+VZf6n۶عk8xǎ8y_x׮߸y?xϞx߾w?r)x]Ɩmm۶m۶m۶mC.   ."..2. .*..:..&..6.....>._Dĸ$dTԸ4t L̸,l\ܸ<|qpqEpEqpq%p%qpqepeqpqpqpqUpUqpq5p5qpqupuqpq p qpqMpMqpq-p-qpqmpmqpqpqpq]p]qpq=p=qpq}p}qpqpqpqCpCqpq#p#qpqcpcqpqpqpqSpSqpq3p3qpqspsqpq p qpqKpKqpq+p+qpqkpkqpqpqpq[p[qpq;p;qpq{p{qpqpqpqGpGqpq'p'qpqgpgqpqpqpqWpWqpq7p7qpqwpwqpqpqpqOpOqpq/p/qpqopoqpqpqpq_p_qpq?p?qpqp*SrUx! jTx jIxڶm۶նm۶Zm۶mw}G(p*tG)rѢLj+v'H(qɒH*ugȘ)sٲș+w(XpŊ(YtWXrժרOu֫ߠaM6kޢemڶkߡc]vޣg}?`C>bcƎ?aSN>csΛ`K.[+VZf6n۶عk8xǎ8y_x׮߸y?xϞx߾w?r)x]Ɩmm۶m۶m۶mC.   ."..2. .*..:..&..6.....>._Dĸ$dTԸ4t L̸,l\ܸ<|qpqEpEqpq%p%qpqepeqpqpqpqUpUqpq5p5qpqupuqpq p qpqMpMqpq-p-qpqmpmqpqpqpq]p]qpq=p=qpq}p}qpqpqpqCpCqpq#p#qpqcpcqpqpqpqSpSqpq3p3qpqspsqpq p qpqKpKqpq+p+qpqkpkqpqpqpq[p[qpq;p;qpq{p{qpqpqpqGpGqpq'p'qpqgpgqpqpqpqWpWqpq7p7qpqwpwqpqpqpqOpOqpq/p/qpqopoqpqpqpq_p_qpq?p?qpqp*SrUx } j7Tx jIxڶm۶նm۶Zm۶mw}G(p*tG)rѢLj+v'H(qɒH*ugȘ)sٲș+w(XpŊ(YtWXrժרOu֫ߠaM6kޢemڶkߡc]vޣg}?`C>bcƎ?aSN>csΛ`K.[+VZf6n۶عk8xǎ8y_x׮߸y?xϞx߾w?r)x]Ɩmm۶m۶m۶mC.   ."..2. .*..:..&..6.....>._Dĸ$dTԸ4t L̸,l\ܸ<|qpqEpEqpq%p%qpqepeqpqpqpqUpUqpq5p5qpqupuqpq p qpqMpMqpq-p-qpqmpmqpqpqpq]p]qpq=p=qpq}p}qpqpqpqCpCqpq#p#qpqcpcqpqpqpqSpSqpq3p3qpqspsqpq p qpqKpKqpq+p+qpqkpkqpqpqpq[p[qpq;p;qpq{p{qpqpqpqGpGqpq'p'qpqgpgqpqpqpqWpWqpq7p7qpqwpwqpqpqpqOpOqpq/p/qpqopoqpqpqpq_p_qpq?p?qpqp*SrUx y jTx jIxڶm۶նm۶Zm۶mw}G(p*tG)rѢLj+v'H(qɒH*ugȘ)sٲș+w(XpŊ(YtWXrժרOu֫ߠaM6kޢemڶkߡc]vޣg}?`C>bcƎ?aSN>csΛ`K.[+VZf6n۶عk8xǎ8y_x׮߸y?xϞx߾w?r)x]Ɩmm۶m۶m۶mC.   ."..2. .*..:..&..6.....>._Dĸ$dTԸ4t L̸,l\ܸ<|qpqEpEqpq%p%qpqepeqpqpqpqUpUqpq5p5qpqupuqpq p qpqMpMqpq-p-qpqmpmqpqpqpq]p]qpq=p=qpq}p}qpqpqpqCpCqpq#p#qpqcpcqpqpqpqSpSqpq3p3qpqspsqpq p qpqKpKqpq+p+qpqkpkqpqpqpq[p[qpq;p;qpq{p{qpqpqpqGpGqpq'p'qpqgpgqpqpqpqWpWqpq7p7qpqwpwqpqpqpqOpOqpq/p/qpqopoqpqpqpq_p_qpq?p?qpqp*SrUx u j'Tx jIxڶm۶նm۶Zm۶mw}G(p*tG)rѢLj+v'H(qɒH*ugȘ)sٲș+w(XpŊ(YtWXrժרOu֫ߠaM6kޢemڶkߡc]vޣg}?`C>bcƎ?aSN>csΛ`K.[+VZf6n۶عk8xǎ8y_x׮߸y?xϞx߾w?r)x]Ɩmm۶m۶m۶mC.   ."..2. .*..:..&..6.....>._Dĸ$dTԸ4t L̸,l\ܸ<|qpqEpEqpq%p%qpqepeqpqpqpqUpUqpq5p5qpqupuqpq p qpqMpMqpq-p-qpqmpmqpqpqpq]p]qpq=p=qpq}p}qpqpqpqCpCqpq#p#qpqcpcqpqpqpqSpSqpq3p3qpqspsqpq p qpqKpKqpq+p+qpqkpkqpqpqpq[p[qpq;p;qpq{p{qpqpqpqGpGqpq'p'qpqgpgqpqpqpqWpWqpq7p7qpqwpwqpqpqpqOpOqpq/p/qpqopoqpqpqpq_p_qpq?p?qpqp*SrUx q jTx jZxc[ѷֲUͶm۶m۶m۶mGSsp F9ML-,sm=O>_?аȨظ ISR32 *\h%J*]l*V\j5j֪]n 6jܤi-Zjݦm:vܥk=zݧoz/^z>~?~篁?ϟ?ϟ?ϟ?ϟ?ϟ?ϟ?ϟ?? g,D83q,Yg-.\rG8;q9s,E87q)K8q/.@\ qBą ..B\(qbŊ/.q,.E\4q2eO6 f<c-blosc2-3.1.5/compat/blosc-1.14.0-zstd.cdata000066400000000000000000000067611521743436100202300ustar00rootroot00000000000000 = n n(/d  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~T  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~{|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>>|>|>|>|>>|>|>|>|>|>|>|>?Tl ~l _Wl> ^G E j(/d  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~T  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~{|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>>|>|>|>|>>|>|>|>|>|>|>|>?Tl ~l _WL9E9j(/d  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~T  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~{|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>|>>|>|>|>|>>|>|>|>|>|>|>|>?Tl  ~l  _WL9E9(/ d  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~~<>|>|>|>|>|>|>|>|>|>|>||>|>|>|>|>|>|>|>|>|>|>|>=B$D~ w7|>||>|>|>|>|>|>|e |;؅G <#@>D9E9c-blosc2-3.1.5/compat/blosc-1.17.1-lz4-bitshuffle4-memcpy.cdata000066400000000000000000004236251521743436100234700ustar00rootroot00000000000000$D '/M_$   / / @                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 P  ? ? @   `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  ` M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M                                                                                              P P P$   / / @                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 P  ? ? @   `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  ` M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M                                                                                              P P PD      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~                                 ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                                                                                                                                                                   ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                                                                                                                                                                   ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                                                                                                                                                                   ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                                                                                                                                                                   ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                                                                                                                                        !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~                                 ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z [ \ ] ^ _ ` a b c d e f g h i j k l m n o p q r s t u v w x y z { | } ~                                                                                                                                  !!!!!!!!! ! ! ! ! !!!!!!!!!!!!!!!!!!! !!!"!#!$!%!&!'!(!)!*!+!,!-!.!/!0!1!2!3!4!5!6!7!8!9!:!;!!?!@!A!B!C!D!E!F!G!H!I!J!K!L!M!N!O!P!Q!R!S!T!U!V!W!X!Y!Z![!\!]!^!_!`!a!b!c!d!e!f!g!h!i!j!k!l!m!n!o!p!q!r!s!t!u!v!w!x!y!z!{!|!}!~!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!""""""""" " " " " """"""""""""""""""" "!"""#"$"%"&"'"(")"*"+","-"."/"0"1"2"3"4"5"6"7"8"9":";"<"=">"?"@"A"B"C"D"E"F"G"H"I"J"K"L"M"N"O"P"Q"R"S"T"U"V"W"X"Y"Z"["\"]"^"_"`"a"b"c"d"e"f"g"h"i"j"k"l"m"n"o"p"q"r"s"t"u"v"w"x"y"z"{"|"}"~""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""######### # # # # ################### #!#"###$#%#&#'#(#)#*#+#,#-#.#/#0#1#2#3#4#5#6#7#8#9#:#;#<#=#>#?#@#A#B#C#D#E#F#G#H#I#J#K#L#M#N#O#P#Q#R#S#T#U#V#W#X#Y#Z#[#\#]#^#_#`#a#b#c#d#e#f#g#h#i#j#k#l#m#n#o#p#q#r#s#t#u#v#w#x#y#z#{#|#}#~##################################################################################################################################$$$$$$$$$ $ $ $ $ $$$$$$$$$$$$$$$$$$$ $!$"$#$$$%$&$'$($)$*$+$,$-$.$/$0$1$2$3$4$5$6$7$8$9$:$;$<$=$>$?$@$A$B$C$D$E$F$G$H$I$J$K$L$M$N$O$P$Q$R$S$T$U$V$W$X$Y$Z$[$\$]$^$_$`$a$b$c$d$e$f$g$h$i$j$k$l$m$n$o$p$q$r$s$t$u$v$w$x$y$z${$|$}$~$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%% % % % % %%%%%%%%%%%%%%%%%%% %!%"%#%$%%%&%'%(%)%*%+%,%-%.%/%0%1%2%3%4%5%6%7%8%9%:%;%<%=%>%?%@%A%B%C%D%E%F%G%H%I%J%K%L%M%N%O%P%Q%R%S%T%U%V%W%X%Y%Z%[%\%]%^%_%`%a%b%c%d%e%f%g%h%i%j%k%l%m%n%o%p%q%r%s%t%u%v%w%x%y%z%{%|%}%~%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&& & & & & &&&&&&&&&&&&&&&&&&& &!&"&#&$&%&&&'&(&)&*&+&,&-&.&/&0&1&2&3&4&5&6&7&8&9&:&;&<&=&>&?&@&A&B&C&D&E&F&G&H&I&J&K&L&M&N&O&P&Q&R&S&T&U&V&W&X&Y&Z&[&\&]&^&_&`&a&b&c&d&e&f&g&h&i&j&k&l&m&n&o&p&q&r&s&t&u&v&w&x&y&z&{&|&}&~&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''' ' ' ' ' ''''''''''''''''''' '!'"'#'$'%'&'''(')'*'+','-'.'/'0'1'2'3'4'5'6'7'8'9':';'<'='>'?'@'A'B'C'D'E'F'G'H'I'J'K'L'M'N'O'P'Q'R'S'T'U'V'W'X'Y'Z'['\']'^'_'`'a'b'c'd'e'f'g'h'i'j'k'l'm'n'o'p'q'r's't'u'v'w'x'y'z'{'|'}'~''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''((((((((( ( ( ( ( ((((((((((((((((((( (!("(#($(%(&('((()(*(+(,(-(.(/(0(1(2(3(4(5(6(7(8(9(:(;(<(=(>(?(@(A(B(C(D(E(F(G(H(I(J(K(L(M(N(O(P(Q(R(S(T(U(V(W(X(Y(Z([(\(](^(_(`(a(b(c(d(e(f(g(h(i(j(k(l(m(n(o(p(q(r(s(t(u(v(w(x(y(z({(|(}(~(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))) ) ) ) ) ))))))))))))))))))) )!)")#)$)%)&)')()))*)+),)-).)/)0)1)2)3)4)5)6)7)8)9):);)<)=)>)?)@)A)B)C)D)E)F)G)H)I)J)K)L)M)N)O)P)Q)R)S)T)U)V)W)X)Y)Z)[)\)])^)_)`)a)b)c)d)e)f)g)h)i)j)k)l)m)n)o)p)q)r)s)t)u)v)w)x)y)z){)|)})~))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))********* * * * * ******************* *!*"*#*$*%*&*'*(*)***+*,*-*.*/*0*1*2*3*4*5*6*7*8*9*:*;*<*=*>*?*@*A*B*C*D*E*F*G*H*I*J*K*L*M*N*O*P*Q*R*S*T*U*V*W*X*Y*Z*[*\*]*^*_*`*a*b*c*d*e*f*g*h*i*j*k*l*m*n*o*p*q*r*s*t*u*v*w*x*y*z*{*|*}*~**********************************************************************************************************************************+++++++++ + + + + +++++++++++++++++++ +!+"+#+$+%+&+'+(+)+*+++,+-+.+/+0+1+2+3+4+5+6+7+8+9+:+;+<+=+>+?+@+A+B+C+D+E+F+G+H+I+J+K+L+M+N+O+P+Q+R+S+T+U+V+W+X+Y+Z+[+\+]+^+_+`+a+b+c+d+e+f+g+h+i+j+k+l+m+n+o+p+q+r+s+t+u+v+w+x+y+z+{+|+}+~++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++,,,,,,,,, , , , , ,,,,,,,,,,,,,,,,,,, ,!,",#,$,%,&,',(,),*,+,,,-,.,/,0,1,2,3,4,5,6,7,8,9,:,;,<,=,>,?,@,A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,[,\,],^,_,`,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,{,|,},~,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,--------- - - - - ------------------- -!-"-#-$-%-&-'-(-)-*-+-,---.-/-0-1-2-3-4-5-6-7-8-9-:-;-<-=->-?-@-A-B-C-D-E-F-G-H-I-J-K-L-M-N-O-P-Q-R-S-T-U-V-W-X-Y-Z-[-\-]-^-_-`-a-b-c-d-e-f-g-h-i-j-k-l-m-n-o-p-q-r-s-t-u-v-w-x-y-z-{-|-}-~----------------------------------------------------------------------------------------------------------------------------------......... . . . . ................... .!.".#.$.%.&.'.(.).*.+.,.-.../.0.1.2.3.4.5.6.7.8.9.:.;.<.=.>.?.@.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z.[.\.].^._.`.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.{.|.}.~..................................................................................................................................///////// / / / / /////////////////// /!/"/#/$/%/&/'/(/)/*/+/,/-/.///0/1/2/3/4/5/6/7/8/9/:/;//?/@/A/B/C/D/E/F/G/H/I/J/K/L/M/N/O/P/Q/R/S/T/U/V/W/X/Y/Z/[/\/]/^/_/`/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z/{/|/}/~//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////000000000 0 0 0 0 0000000000000000000 0!0"0#0$0%0&0'0(0)0*0+0,0-0.0/000102030405060708090:0;0<0=0>0?0@0A0B0C0D0E0F0G0H0I0J0K0L0M0N0O0P0Q0R0S0T0U0V0W0X0Y0Z0[0\0]0^0_0`0a0b0c0d0e0f0g0h0i0j0k0l0m0n0o0p0q0r0s0t0u0v0w0x0y0z0{0|0}0~0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111 1 1 1 1 1111111111111111111 1!1"1#1$1%1&1'1(1)1*1+1,1-1.1/101112131415161718191:1;1<1=1>1?1@1A1B1C1D1E1F1G1H1I1J1K1L1M1N1O1P1Q1R1S1T1U1V1W1X1Y1Z1[1\1]1^1_1`1a1b1c1d1e1f1g1h1i1j1k1l1m1n1o1p1q1r1s1t1u1v1w1x1y1z1{1|1}1~1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111222222222 2 2 2 2 2222222222222222222 2!2"2#2$2%2&2'2(2)2*2+2,2-2.2/202122232425262728292:2;2<2=2>2?2@2A2B2C2D2E2F2G2H2I2J2K2L2M2N2O2P2Q2R2S2T2U2V2W2X2Y2Z2[2\2]2^2_2`2a2b2c2d2e2f2g2h2i2j2k2l2m2n2o2p2q2r2s2t2u2v2w2x2y2z2{2|2}2~2222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222333333333 3 3 3 3 3333333333333333333 3!3"3#3$3%3&3'3(3)3*3+3,3-3.3/303132333435363738393:3;3<3=3>3?3@3A3B3C3D3E3F3G3H3I3J3K3L3M3N3O3P3Q3R3S3T3U3V3W3X3Y3Z3[3\3]3^3_3`3a3b3c3d3e3f3g3h3i3j3k3l3m3n3o3p3q3r3s3t3u3v3w3x3y3z3{3|3}3~3333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333444444444 4 4 4 4 4444444444444444444 4!4"4#4$4%4&4'4(4)4*4+4,4-4.4/404142434445464748494:4;4<4=4>4?4@4A4B4C4D4E4F4G4H4I4J4K4L4M4N4O4P4Q4R4S4T4U4V4W4X4Y4Z4[4\4]4^4_4`4a4b4c4d4e4f4g4h4i4j4k4l4m4n4o4p4q4r4s4t4u4v4w4x4y4z4{4|4}4~4444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444555555555 5 5 5 5 5555555555555555555 5!5"5#5$5%5&5'5(5)5*5+5,5-5.5/505152535455565758595:5;5<5=5>5?5@5A5B5C5D5E5F5G5H5I5J5K5L5M5N5O5P5Q5R5S5T5U5V5W5X5Y5Z5[5\5]5^5_5`5a5b5c5d5e5f5g5h5i5j5k5l5m5n5o5p5q5r5s5t5u5v5w5x5y5z5{5|5}5~5555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555666666666 6 6 6 6 6666666666666666666 6!6"6#6$6%6&6'6(6)6*6+6,6-6.6/606162636465666768696:6;6<6=6>6?6@6A6B6C6D6E6F6G6H6I6J6K6L6M6N6O6P6Q6R6S6T6U6V6W6X6Y6Z6[6\6]6^6_6`6a6b6c6d6e6f6g6h6i6j6k6l6m6n6o6p6q6r6s6t6u6v6w6x6y6z6{6|6}6~6666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666777777777 7 7 7 7 7777777777777777777 7!7"7#7$7%7&7'7(7)7*7+7,7-7.7/707172737475767778797:7;7<7=7>7?7@7A7B7C7D7E7F7G7H7I7J7K7L7M7N7O7P7Q7R7S7T7U7V7W7X7Y7Z7[7\7]7^7_7`7a7b7c7d7e7f7g7h7i7j7k7l7m7n7o7p7q7r7s7t7u7v7w7x7y7z7{7|7}7~7777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777888888888 8 8 8 8 8888888888888888888 8!8"8#8$8%8&8'8(8)8*8+8,8-8.8/808182838485868788898:8;8<8=8>8?8@8A8B8C8D8E8F8G8H8I8J8K8L8M8N8O8P8Q8R8S8T8U8V8W8X8Y8Z8[8\8]8^8_8`8a8b8c8d8e8f8g8h8i8j8k8l8m8n8o8p8q8r8s8t8u8v8w8x8y8z8{8|8}8~8888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888999999999 9 9 9 9 9999999999999999999 9!9"9#9$9%9&9'9(9)9*9+9,9-9.9/909192939495969798999:9;9<9=9>9?9@9A9B9C9D9E9F9G9H9I9J9K9L9M9N9O9P9Q9R9S9T9U9V9W9X9Y9Z9[9\9]9^9_9`9a9b9c9d9e9f9g9h9i9j9k9l9m9n9o9p9q9r9s9t9u9v9w9x9y9z9{9|9}9~9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::: : : : : ::::::::::::::::::: :!:":#:$:%:&:':(:):*:+:,:-:.:/:0:1:2:3:4:5:6:7:8:9:::;:<:=:>:?:@:A:B:C:D:E:F:G:H:I:J:K:L:M:N:O:P:Q:R:S:T:U:V:W:X:Y:Z:[:\:]:^:_:`:a:b:c:d:e:f:g:h:i:j:k:l:m:n:o:p:q:r:s:t:u:v:w:x:y:z:{:|:}:~::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;; ; ; ; ; ;;;;;;;;;;;;;;;;;;; ;!;";#;$;%;&;';(;);*;+;,;-;.;/;0;1;2;3;4;5;6;7;8;9;:;;;<;=;>;?;@;A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;U;V;W;X;Y;Z;[;\;];^;_;`;a;b;c;d;e;f;g;h;i;j;k;l;m;n;o;p;q;r;s;t;u;v;w;x;y;z;{;|;};~;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<< < < < < <<<<<<<<<<<<<<<<<<< <!<"<#<$<%<&<'<(<)<*<+<,<-<.</<0<1<2<3<4<5<6<7<8<9<:<;<<<=<><?<@<A<B<C<D<E<F<G<H<I<J<K<L<M<N<O<P<Q<R<S<T<U<V<W<X<Y<Z<[<\<]<^<_<`<a<b<c<d<e<f<g<h<i<j<k<l<m<n<o<p<q<r<s<t<u<v<w<x<y<z<{<|<}<~<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<========= = = = = =================== =!="=#=$=%=&='=(=)=*=+=,=-=.=/=0=1=2=3=4=5=6=7=8=9=:=;=<===>=?=@=A=B=C=D=E=F=G=H=I=J=K=L=M=N=O=P=Q=R=S=T=U=V=W=X=Y=Z=[=\=]=^=_=`=a=b=c=d=e=f=g=h=i=j=k=l=m=n=o=p=q=r=s=t=u=v=w=x=y=z={=|=}=~==================================================================================================================================>>>>>>>>> > > > > >>>>>>>>>>>>>>>>>>> >!>">#>$>%>&>'>(>)>*>+>,>->.>/>0>1>2>3>4>5>6>7>8>9>:>;><>=>>>?>@>A>B>C>D>E>F>G>H>I>J>K>L>M>N>O>P>Q>R>S>T>U>V>W>X>Y>Z>[>\>]>^>_>`>a>b>c>d>e>f>g>h>i>j>k>l>m>n>o>p>q>r>s>t>u>v>w>x>y>z>{>|>}>~>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????? ? ? ? ? ??????????????????? ?!?"?#?$?%?&?'?(?)?*?+?,?-?.?/?0?1?2?3?4?5?6?7?8?9?:?;????@?A?B?C?D?E?F?G?H?I?J?K?L?M?N?O?P?Q?R?S?T?U?V?W?X?Y?Z?[?\?]?^?_?`?a?b?c?d?e?f?g?h?i?j?k?l?m?n?o?p?q?r?s?t?u?v?w?x?y?z?{?|?}?~??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@@@@@@@@ @ @ @ @ @@@@@@@@@@@@@@@@@@@ @!@"@#@$@%@&@'@(@)@*@+@,@-@.@/@0@1@2@3@4@5@6@7@8@9@:@;@<@=@>@?@@@A@B@C@D@E@F@G@H@I@J@K@L@M@N@O@P@Q@R@S@T@U@V@W@X@Y@Z@[@\@]@^@_@`@a@b@c@d@e@f@g@h@i@j@k@l@m@n@o@p@q@r@s@t@u@v@w@x@y@z@{@|@}@~@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AAAAAAAAA A A A A AAAAAAAAAAAAAAAAAAA A!A"A#A$A%A&A'A(A)A*A+A,A-A.A/A0A1A2A3A4A5A6A7A8A9A:A;AA?A@AAABACADAEAFAGAHAIAJAKALAMANAOAPAQARASATAUAVAWAXAYAZA[A\A]A^A_A`AaAbAcAdAeAfAgAhAiAjAkAlAmAnAoApAqArAsAtAuAvAwAxAyAzA{A|A}A~AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBB B B B B BBBBBBBBBBBBBBBBBBB B!B"B#B$B%B&B'B(B)B*B+B,B-B.B/B0B1B2B3B4B5B6B7B8B9B:B;BB?B@BABBBCBDBEBFBGBHBIBJBKBLBMBNBOBPBQBRBSBTBUBVBWBXBYBZB[B\B]B^B_B`BaBbBcBdBeBfBgBhBiBjBkBlBmBnBoBpBqBrBsBtBuBvBwBxByBzB{B|B}B~BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCC C C C C CCCCCCCCCCCCCCCCCCC C!C"C#C$C%C&C'C(C)C*C+C,C-C.C/C0C1C2C3C4C5C6C7C8C9C:C;CC?C@CACBCCCDCECFCGCHCICJCKCLCMCNCOCPCQCRCSCTCUCVCWCXCYCZC[C\C]C^C_C`CaCbCcCdCeCfCgChCiCjCkClCmCnCoCpCqCrCsCtCuCvCwCxCyCzC{C|C}C~CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDD D D D D DDDDDDDDDDDDDDDDDDD D!D"D#D$D%D&D'D(D)D*D+D,D-D.D/D0D1D2D3D4D5D6D7D8D9D:D;DD?D@DADBDCDDDEDFDGDHDIDJDKDLDMDNDODPDQDRDSDTDUDVDWDXDYDZD[D\D]D^D_D`DaDbDcDdDeDfDgDhDiDjDkDlDmDnDoDpDqDrDsDtDuDvDwDxDyDzD{D|D}D~DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDEEEEEEEEE E E E E EEEEEEEEEEEEEEEEEEE E!E"E#E$E%E&E'E(E)E*E+E,E-E.E/E0E1E2E3E4E5E6E7E8E9E:E;EE?E@EAEBECEDEEEFEGEHEIEJEKELEMENEOEPEQERESETEUEVEWEXEYEZE[E\E]E^E_E`EaEbEcEdEeEfEgEhEiEjEkElEmEnEoEpEqErEsEtEuEvEwExEyEzE{E|E}E~EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEFFFFFFFFF F F F F FFFFFFFFFFFFFFFFFFF F!F"F#F$F%F&F'F(F)F*F+F,F-F.F/F0F1F2F3F4F5F6F7F8F9F:F;FF?F@FAFBFCFDFEFFFGFHFIFJFKFLFMFNFOFPFQFRFSFTFUFVFWFXFYFZF[F\F]F^F_F`FaFbFcFdFeFfFgFhFiFjFkFlFmFnFoFpFqFrFsFtFuFvFwFxFyFzF{F|F}F~FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFGGGGGGGGG G G G G GGGGGGGGGGGGGGGGGGG G!G"G#G$G%G&G'G(G)G*G+G,G-G.G/G0G1G2G3G4G5G6G7G8G9G:G;GG?G@GAGBGCGDGEGFGGGHGIGJGKGLGMGNGOGPGQGRGSGTGUGVGWGXGYGZG[G\G]G^G_G`GaGbGcGdGeGfGgGhGiGjGkGlGmGnGoGpGqGrGsGtGuGvGwGxGyGzG{G|G}G~GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGHHHHHHHHH H H H H HHHHHHHHHHHHHHHHHHH H!H"H#H$H%H&H'H(H)H*H+H,H-H.H/H0H1H2H3H4H5H6H7H8H9H:H;HH?H@HAHBHCHDHEHFHGHHHIHJHKHLHMHNHOHPHQHRHSHTHUHVHWHXHYHZH[H\H]H^H_H`HaHbHcHdHeHfHgHhHiHjHkHlHmHnHoHpHqHrHsHtHuHvHwHxHyHzH{H|H}H~HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHIIIIIIIII I I I I IIIIIIIIIIIIIIIIIII I!I"I#I$I%I&I'I(I)I*I+I,I-I.I/I0I1I2I3I4I5I6I7I8I9I:I;II?I@IAIBICIDIEIFIGIHIIIJIKILIMINIOIPIQIRISITIUIVIWIXIYIZI[I\I]I^I_I`IaIbIcIdIeIfIgIhIiIjIkIlImInIoIpIqIrIsItIuIvIwIxIyIzI{I|I}I~IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIJJJJJJJJJ J J J J JJJJJJJJJJJJJJJJJJJ J!J"J#J$J%J&J'J(J)J*J+J,J-J.J/J0J1J2J3J4J5J6J7J8J9J:J;JJ?J@JAJBJCJDJEJFJGJHJIJJJKJLJMJNJOJPJQJRJSJTJUJVJWJXJYJZJ[J\J]J^J_J`JaJbJcJdJeJfJgJhJiJjJkJlJmJnJoJpJqJrJsJtJuJvJwJxJyJzJ{J|J}J~JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJKKKKKKKKK K K K K KKKKKKKKKKKKKKKKKKK K!K"K#K$K%K&K'K(K)K*K+K,K-K.K/K0K1K2K3K4K5K6K7K8K9K:K;KK?K@KAKBKCKDKEKFKGKHKIKJKKKLKMKNKOKPKQKRKSKTKUKVKWKXKYKZK[K\K]K^K_K`KaKbKcKdKeKfKgKhKiKjKkKlKmKnKoKpKqKrKsKtKuKvKwKxKyKzK{K|K}K~KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKLLLLLLLLL L L L L LLLLLLLLLLLLLLLLLLL L!L"L#L$L%L&L'L(L)L*L+L,L-L.L/L0L1L2L3L4L5L6L7L8L9L:L;LL?L@LALBLCLDLELFLGLHLILJLKLLLMLNLOLPLQLRLSLTLULVLWLXLYLZL[L\L]L^L_L`LaLbLcLdLeLfLgLhLiLjLkLlLmLnLoLpLqLrLsLtLuLvLwLxLyLzL{L|L}L~LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMM M M M M MMMMMMMMMMMMMMMMMMM M!M"M#M$M%M&M'M(M)M*M+M,M-M.M/M0M1M2M3M4M5M6M7M8M9M:M;MM?M@MAMBMCMDMEMFMGMHMIMJMKMLMMMNMOMPMQMRMSMTMUMVMWMXMYMZM[M\M]M^M_M`MaMbMcMdMeMfMgMhMiMjMkMlMmMnMoMpMqMrMsMtMuMvMwMxMyMzM{M|M}M~MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNNNNNNNNN N N N N NNNNNNNNNNNNNNNNNNN N!N"N#N$N%N&N'N(N)N*N+N,N-N.N/N0N1N2N3N4N5N6N7N8N9N:N;NN?N@NANBNCNDNENFNGNHNINJNKNLNMNNNONPNQNRNSNTNUNVNWNXNYNZN[N\N]N^N_N`NaNbNcNdNeNfNgNhNiNjNkNlNmNnNoNpNqNrNsNtNuNvNwNxNyNzN{N|N}N~NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOOOOOOOO O O O O OOOOOOOOOOOOOOOOOOO O!O"O#O$O%O&O'O(O)O*O+O,O-O.O/O0O1O2O3O4O5O6O7O8O9O:O;OO?O@OAOBOCODOEOFOGOHOIOJOKOLOMONOOOPOQOROSOTOUOVOWOXOYOZO[O\O]O^O_O`OaObOcOdOeOfOgOhOiOjOkOlOmOnOoOpOqOrOsOtOuOvOwOxOyOzO{O|O}O~OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOPPPPPPPPP P P P P PPPPPPPPPPPPPPPPPPP P!P"P#P$P%P&P'P(P)P*P+P,P-P.P/P0P1P2P3P4P5P6P7P8P9P:P;PP?P@PAPBPCPDPEPFPGPHPIPJPKPLPMPNPOPPPQPRPSPTPUPVPWPXPYPZP[P\P]P^P_P`PaPbPcPdPePfPgPhPiPjPkPlPmPnPoPpPqPrPsPtPuPvPwPxPyPzP{P|P}P~PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQ Q Q Q Q QQQQQQQQQQQQQQQQQQQ Q!Q"Q#Q$Q%Q&Q'Q(Q)Q*Q+Q,Q-Q.Q/Q0Q1Q2Q3Q4Q5Q6Q7Q8Q9Q:Q;QQ?Q@QAQBQCQDQEQFQGQHQIQJQKQLQMQNQOQPQQQRQSQTQUQVQWQXQYQZQ[Q\Q]Q^Q_Q`QaQbQcQdQeQfQgQhQiQjQkQlQmQnQoQpQqQrQsQtQuQvQwQxQyQzQ{Q|Q}Q~QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQRRRRRRRRR R R R R RRRRRRRRRRRRRRRRRRR R!R"R#R$R%R&R'R(R)R*R+R,R-R.R/R0R1R2R3R4R5R6R7R8R9R:R;RR?R@RARBRCRDRERFRGRHRIRJRKRLRMRNRORPRQRRRSRTRURVRWRXRYRZR[R\R]R^R_R`RaRbRcRdReRfRgRhRiRjRkRlRmRnRoRpRqRrRsRtRuRvRwRxRyRzR{R|R}R~RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRSSSSSSSSS S S S S SSSSSSSSSSSSSSSSSSS S!S"S#S$S%S&S'S(S)S*S+S,S-S.S/S0S1S2S3S4S5S6S7S8S9S:S;SS?S@SASBSCSDSESFSGSHSISJSKSLSMSNSOSPSQSRSSSTSUSVSWSXSYSZS[S\S]S^S_S`SaSbScSdSeSfSgShSiSjSkSlSmSnSoSpSqSrSsStSuSvSwSxSySzS{S|S}S~SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSTTTTTTTTT T T T T TTTTTTTTTTTTTTTTTTT T!T"T#T$T%T&T'T(T)T*T+T,T-T.T/T0T1T2T3T4T5T6T7T8T9T:T;TT?T@TATBTCTDTETFTGTHTITJTKTLTMTNTOTPTQTRTSTTTUTVTWTXTYTZT[T\T]T^T_T`TaTbTcTdTeTfTgThTiTjTkTlTmTnToTpTqTrTsTtTuTvTwTxTyTzT{T|T}T~TTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUU U U U U UUUUUUUUUUUUUUUUUUU U!U"U#U$U%U&U'U(U)U*U+U,U-U.U/U0U1U2U3U4U5U6U7U8U9U:U;UU?U@UAUBUCUDUEUFUGUHUIUJUKULUMUNUOUPUQURUSUTUUUVUWUXUYUZU[U\U]U^U_U`UaUbUcUdUeUfUgUhUiUjUkUlUmUnUoUpUqUrUsUtUuUvUwUxUyUzU{U|U}U~UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUVVVVVVVVV V V V V VVVVVVVVVVVVVVVVVVV V!V"V#V$V%V&V'V(V)V*V+V,V-V.V/V0V1V2V3V4V5V6V7V8V9V:V;VV?V@VAVBVCVDVEVFVGVHVIVJVKVLVMVNVOVPVQVRVSVTVUVVVWVXVYVZV[V\V]V^V_V`VaVbVcVdVeVfVgVhViVjVkVlVmVnVoVpVqVrVsVtVuVvVwVxVyVzV{V|V}V~VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWWWWWWWWW W W W W WWWWWWWWWWWWWWWWWWW W!W"W#W$W%W&W'W(W)W*W+W,W-W.W/W0W1W2W3W4W5W6W7W8W9W:W;WW?W@WAWBWCWDWEWFWGWHWIWJWKWLWMWNWOWPWQWRWSWTWUWVWWWXWYWZW[W\W]W^W_W`WaWbWcWdWeWfWgWhWiWjWkWlWmWnWoWpWqWrWsWtWuWvWwWxWyWzW{W|W}W~WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWXXXXXXXXX X X X X XXXXXXXXXXXXXXXXXXX X!X"X#X$X%X&X'X(X)X*X+X,X-X.X/X0X1X2X3X4X5X6X7X8X9X:X;XX?X@XAXBXCXDXEXFXGXHXIXJXKXLXMXNXOXPXQXRXSXTXUXVXWXXXYXZX[X\X]X^X_X`XaXbXcXdXeXfXgXhXiXjXkXlXmXnXoXpXqXrXsXtXuXvXwXxXyXzX{X|X}X~XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXYYYYYYYYY Y Y Y Y YYYYYYYYYYYYYYYYYYY Y!Y"Y#Y$Y%Y&Y'Y(Y)Y*Y+Y,Y-Y.Y/Y0Y1Y2Y3Y4Y5Y6Y7Y8Y9Y:Y;YY?Y@YAYBYCYDYEYFYGYHYIYJYKYLYMYNYOYPYQYRYSYTYUYVYWYXYYYZY[Y\Y]Y^Y_Y`YaYbYcYdYeYfYgYhYiYjYkYlYmYnYoYpYqYrYsYtYuYvYwYxYyYzY{Y|Y}Y~YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYZZZZZZZZZ Z Z Z Z ZZZZZZZZZZZZZZZZZZZ Z!Z"Z#Z$Z%Z&Z'Z(Z)Z*Z+Z,Z-Z.Z/Z0Z1Z2Z3Z4Z5Z6Z7Z8Z9Z:Z;ZZ?Z@ZAZBZCZDZEZFZGZHZIZJZKZLZMZNZOZPZQZRZSZTZUZVZWZXZYZZZ[Z\Z]Z^Z_Z`ZaZbZcZdZeZfZgZhZiZjZkZlZmZnZoZpZqZrZsZtZuZvZwZxZyZzZ{Z|Z}Z~ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ[[[[[[[[[ [ [ [ [ [[[[[[[[[[[[[[[[[[[ [!["[#[$[%[&['[([)[*[+[,[-[.[/[0[1[2[3[4[5[6[7[8[9[:[;[<[=[>[?[@[A[B[C[D[E[F[G[H[I[J[K[L[M[N[O[P[Q[R[S[T[U[V[W[X[Y[Z[[[\[][^[_[`[a[b[c[d[e[f[g[h[i[j[k[l[m[n[o[p[q[r[s[t[u[v[w[x[y[z[{[|[}[~[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[\\\\\\\\\ \ \ \ \ \\\\\\\\\\\\\\\\\\\ \!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\0\1\2\3\4\5\6\7\8\9\:\;\<\=\>\?\@\A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z\[\\\]\^\_\`\a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z\{\|\}\~\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]]]]]]]] ] ] ] ] ]]]]]]]]]]]]]]]]]]] ]!]"]#]$]%]&]'](])]*]+],]-].]/]0]1]2]3]4]5]6]7]8]9]:];]<]=]>]?]@]A]B]C]D]E]F]G]H]I]J]K]L]M]N]O]P]Q]R]S]T]U]V]W]X]Y]Z][]\]]]^]_]`]a]b]c]d]e]f]g]h]i]j]k]l]m]n]o]p]q]r]s]t]u]v]w]x]y]z]{]|]}]~]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]^^^^^^^^^ ^ ^ ^ ^ ^^^^^^^^^^^^^^^^^^^ ^!^"^#^$^%^&^'^(^)^*^+^,^-^.^/^0^1^2^3^4^5^6^7^8^9^:^;^<^=^>^?^@^A^B^C^D^E^F^G^H^I^J^K^L^M^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\^]^^^_^`^a^b^c^d^e^f^g^h^i^j^k^l^m^n^o^p^q^r^s^t^u^v^w^x^y^z^{^|^}^~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^_________ _ _ _ _ ___________________ _!_"_#_$_%_&_'_(_)_*_+_,_-_._/_0_1_2_3_4_5_6_7_8_9_:_;_<_=_>_?_@_A_B_C_D_E_F_G_H_I_J_K_L_M_N_O_P_Q_R_S_T_U_V_W_X_Y_Z_[_\_]_^___`_a_b_c_d_e_f_g_h_i_j_k_l_m_n_o_p_q_r_s_t_u_v_w_x_y_z_{_|_}_~__________________________________________________________________________________________________________________________________````````` ` ` ` ` ``````````````````` `!`"`#`$`%`&`'`(`)`*`+`,`-`.`/`0`1`2`3`4`5`6`7`8`9`:`;`<`=`>`?`@`A`B`C`D`E`F`G`H`I`J`K`L`M`N`O`P`Q`R`S`T`U`V`W`X`Y`Z`[`\`]`^`_```a`b`c`d`e`f`g`h`i`j`k`l`m`n`o`p`q`r`s`t`u`v`w`x`y`z`{`|`}`~``````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````aaaaaaaaa a a a a aaaaaaaaaaaaaaaaaaa a!a"a#a$a%a&a'a(a)a*a+a,a-a.a/a0a1a2a3a4a5a6a7a8a9a:a;aa?a@aAaBaCaDaEaFaGaHaIaJaKaLaMaNaOaPaQaRaSaTaUaVaWaXaYaZa[a\a]a^a_a`aaabacadaeafagahaiajakalamanaoapaqarasatauavawaxayaza{a|a}a~aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbb b b b b bbbbbbbbbbbbbbbbbbb b!b"b#b$b%b&b'b(b)b*b+b,b-b.b/b0b1b2b3b4b5b6b7b8b9b:b;bb?b@bAbBbCbDbEbFbGbHbIbJbKbLbMbNbObPbQbRbSbTbUbVbWbXbYbZb[b\b]b^b_b`babbbcbdbebfbgbhbibjbkblbmbnbobpbqbrbsbtbubvbwbxbybzb{b|b}b~bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccc c c c c ccccccccccccccccccc c!c"c#c$c%c&c'c(c)c*c+c,c-c.c/c0c1c2c3c4c5c6c7c8c9c:c;cc?c@cAcBcCcDcEcFcGcHcIcJcKcLcMcNcOcPcQcRcScTcUcVcWcXcYcZc[c\c]c^c_c`cacbcccdcecfcgchcicjckclcmcncocpcqcrcsctcucvcwcxcyczc{c|c}c~ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccddddddddd d d d d ddddddddddddddddddd d!d"d#d$d%d&d'd(d)d*d+d,d-d.d/d0d1d2d3d4d5d6d7d8d9d:d;dd?d@dAdBdCdDdEdFdGdHdIdJdKdLdMdNdOdPdQdRdSdTdUdVdWdXdYdZd[d\d]d^d_d`dadbdcdddedfdgdhdidjdkdldmdndodpdqdrdsdtdudvdwdxdydzd{d|d}d~ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddeeeeeeeee e e e e eeeeeeeeeeeeeeeeeee e!e"e#e$e%e&e'e(e)e*e+e,e-e.e/e0e1e2e3e4e5e6e7e8e9e:e;ee?e@eAeBeCeDeEeFeGeHeIeJeKeLeMeNeOePeQeReSeTeUeVeWeXeYeZe[e\e]e^e_e`eaebecedeeefegeheiejekelemeneoepeqereseteuevewexeyeze{e|e}e~eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeefffffffff f f f f fffffffffffffffffff f!f"f#f$f%f&f'f(f)f*f+f,f-f.f/f0f1f2f3f4f5f6f7f8f9f:f;ff?f@fAfBfCfDfEfFfGfHfIfJfKfLfMfNfOfPfQfRfSfTfUfVfWfXfYfZf[f\f]f^f_f`fafbfcfdfefffgfhfifjfkflfmfnfofpfqfrfsftfufvfwfxfyfzf{f|f}f~ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffggggggggg g g g g ggggggggggggggggggg g!g"g#g$g%g&g'g(g)g*g+g,g-g.g/g0g1g2g3g4g5g6g7g8g9g:g;gg?g@gAgBgCgDgEgFgGgHgIgJgKgLgMgNgOgPgQgRgSgTgUgVgWgXgYgZg[g\g]g^g_g`gagbgcgdgegfggghgigjgkglgmgngogpgqgrgsgtgugvgwgxgygzg{g|g}g~gggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggghhhhhhhhh h h h h hhhhhhhhhhhhhhhhhhh h!h"h#h$h%h&h'h(h)h*h+h,h-h.h/h0h1h2h3h4h5h6h7h8h9h:h;hh?h@hAhBhChDhEhFhGhHhIhJhKhLhMhNhOhPhQhRhShThUhVhWhXhYhZh[h\h]h^h_h`hahbhchdhehfhghhhihjhkhlhmhnhohphqhrhshthuhvhwhxhyhzh{h|h}h~hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhiiiiiiiii i i i i iiiiiiiiiiiiiiiiiii i!i"i#i$i%i&i'i(i)i*i+i,i-i.i/i0i1i2i3i4i5i6i7i8i9i:i;ii?i@iAiBiCiDiEiFiGiHiIiJiKiLiMiNiOiPiQiRiSiTiUiViWiXiYiZi[i\i]i^i_i`iaibicidieifigihiiijikiliminioipiqirisitiuiviwixiyizi{i|i}i~iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijjjjjjjjj j j j j jjjjjjjjjjjjjjjjjjj j!j"j#j$j%j&j'j(j)j*j+j,j-j.j/j0j1j2j3j4j5j6j7j8j9j:j;jj?j@jAjBjCjDjEjFjGjHjIjJjKjLjMjNjOjPjQjRjSjTjUjVjWjXjYjZj[j\j]j^j_j`jajbjcjdjejfjgjhjijjjkjljmjnjojpjqjrjsjtjujvjwjxjyjzj{j|j}j~jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjkkkkkkkkk k k k k kkkkkkkkkkkkkkkkkkk k!k"k#k$k%k&k'k(k)k*k+k,k-k.k/k0k1k2k3k4k5k6k7k8k9k:k;kk?k@kAkBkCkDkEkFkGkHkIkJkKkLkMkNkOkPkQkRkSkTkUkVkWkXkYkZk[k\k]k^k_k`kakbkckdkekfkgkhkikjkkklkmknkokpkqkrksktkukvkwkxkykzk{k|k}k~kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkklllllllll l l l l lllllllllllllllllll l!l"l#l$l%l&l'l(l)l*l+l,l-l.l/l0l1l2l3l4l5l6l7l8l9l:l;ll?l@lAlBlClDlElFlGlHlIlJlKlLlMlNlOlPlQlRlSlTlUlVlWlXlYlZl[l\l]l^l_l`lalblcldlelflglhliljlklllmlnlolplqlrlsltlulvlwlxlylzl{l|l}l~llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllmmmmmmmmm m m m m mmmmmmmmmmmmmmmmmmm m!m"m#m$m%m&m'm(m)m*m+m,m-m.m/m0m1m2m3m4m5m6m7m8m9m:m;mm?m@mAmBmCmDmEmFmGmHmImJmKmLmMmNmOmPmQmRmSmTmUmVmWmXmYmZm[m\m]m^m_m`mambmcmdmemfmgmhmimjmkmlmmmnmompmqmrmsmtmumvmwmxmymzm{m|m}m~mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmnnnnnnnnn n n n n nnnnnnnnnnnnnnnnnnn n!n"n#n$n%n&n'n(n)n*n+n,n-n.n/n0n1n2n3n4n5n6n7n8n9n:n;nn?n@nAnBnCnDnEnFnGnHnInJnKnLnMnNnOnPnQnRnSnTnUnVnWnXnYnZn[n\n]n^n_n`nanbncndnenfngnhninjnknlnmnnnonpnqnrnsntnunvnwnxnynzn{n|n}n~nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnooooooooo o o o o ooooooooooooooooooo o!o"o#o$o%o&o'o(o)o*o+o,o-o.o/o0o1o2o3o4o5o6o7o8o9o:o;oo?o@oAoBoCoDoEoFoGoHoIoJoKoLoMoNoOoPoQoRoSoToUoVoWoXoYoZo[o\o]o^o_o`oaobocodoeofogohoiojokolomonooopoqorosotouovowoxoyozo{o|o}o~ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooppppppppp p p p p ppppppppppppppppppp p!p"p#p$p%p&p'p(p)p*p+p,p-p.p/p0p1p2p3p4p5p6p7p8p9p:p;pp?p@pApBpCpDpEpFpGpHpIpJpKpLpMpNpOpPpQpRpSpTpUpVpWpXpYpZp[p\p]p^p_p`papbpcpdpepfpgphpipjpkplpmpnpopppqprpsptpupvpwpxpypzp{p|p}p~ppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppqqqqqqqqq q q q q qqqqqqqqqqqqqqqqqqq q!q"q#q$q%q&q'q(q)q*q+q,q-q.q/q0q1q2q3q4q5q6q7q8q9q:q;qq?q@qAqBqCqDqEqFqGqHqIqJqKqLqMqNqOqPqQqRqSqTqUqVqWqXqYqZq[q\q]q^q_q`qaqbqcqdqeqfqgqhqiqjqkqlqmqnqoqpqqqrqsqtquqvqwqxqyqzq{q|q}q~qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrrrrrrrr r r r r rrrrc-blosc2-3.1.5/compat/blosc-1.17.1-lz4-bitshuffle8-nomemcpy.cdata000066400000000000000000000543501521743436100240240ustar00rootroot00000000000000$D XFQ     / / @                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               ]      0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]                                                            PP P     / /                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                ]      0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0 Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P Pp p  P] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]  ] ]] ]                                                            PP P/Op*  51=+86(*86(*86(*86(*86(*86(*86(*86(*86(*86(*86(*86(*86(*86(|)k#|)%F'x%z'H%F'x%z'H%F'x%z'H%F'x%z'H%F'x%z'H%F'x%z'H%F%5m5%rY%L%_L8%LL!LL!Bsh *Pc-blosc2-3.1.5/compat/blosc-1.18.0-lz4-bitshuffle4-memcpy.cdata000066400000000000000000000662131521743436100234640ustar00rootroot00000000000000$C l/M_$   / / @                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 P  ? ? @   `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  ` M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M                                                                                              P P P$   / / @                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 P  ? ? @   `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  `  ` M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M  M MM M                                                                                              P P P: <<?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !  !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !  !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !  !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !  !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !  !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! !4?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~x  !"#$%&'()*+,-./0123456789:;<=>?@AB6@xdrc-blosc2-3.1.5/compat/blosc-1.3.0-lz4.cdata000066400000000000000000001066531521743436100176740ustar00rootroot00000000000000! =P (%^.7@J6Sl\enxDz   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P  P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P  P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P  P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P  P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P  P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P-  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~n      !"#$%&'()*+,-./0123456789:;<=>?@AB,?3ojPc-blosc2-3.1.5/compat/blosc-1.3.0-lz4hc.cdata000066400000000000000000000763331521743436100202100ustar00rootroot00000000000000! = |~A   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P(      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P.PPY;  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~u      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@A@-     npPc-blosc2-3.1.5/compat/blosc-1.3.0-zlib.cdata000066400000000000000000000351541521743436100201200ustar00rootroot00000000000000a = l:< xσڶm۶նm۶Zm۶m'(p*tG)rѢLj+v'H(qɒH*ugȘ)sٲș+w(XpŊ(YtWXrժרOu֫ߠaM6kޢemڶkߡc]vޣg}?`C>bcƎ?aSN>csΛ`K.[+VZf6n۶عk8xǎ8y_x׮߸y?xϞx߾w ;'xvm۶m۶m۶m۶m۶wHlqBBBB""""bbbbRRRR2222rrrr JJJJ****jjjjZZZZ::::zzzzFFFF&&&&ffffVVVV6666vvvvNNNN....nnnn^^^^>>>>~~~~Džą…ƅŅÅEEEEEEEE%%%%%%%%ǥĥ¥ƥťåeeeeeeeeǕĕ•ƕŕÕUUUUUUUU55555555ǵĵµƵŵõuuuuuuuu ǍčƍōÍMMMMMMMM--------ǭĭ­ƭŭímmmmmmmmǝĝƝŝÝ]]]]]]]]========ǽĽ½ƽŽý}}}}}}}} KKKKKKKKKKKKKKKKˀˈ˄ˌ˂ˊˆˎˁˉ˅ˍ˃ˋˇˏ++++++++++++++++kkkkkkkkkkkkkkkk[[[[[[[[[[[[[[[[ۀۈۄیۂۊۆێہۉۅۍۃۋۇۏ;;;;;;;;;;;;;;;;{{{{{{{{{{{{{{{{           WWWWWWW WWWW WWWWWWWW WWWW WWWW WWWWW     7777777 7777 77777777 7777 7777 77777     wwwwwww wwww wwwwwwww wwww wwww wwwww     ..0..(..8..$..4. .,..<.."..2. .*..:..&..6.....>..!..1. .)..9..%..5. .-..=..#..3. .+..;..'..7../..? 0(8$4 ,<"2 *:&6.>!1 )9%5 -=#3 +;'7/?nn nn0nn(nn8nn$nn4n n,nnnn!nn1n n)nn9nn%nn5n n-nn=nn#nn3n n+nn;nn'nn7nn/nn? 0(8$4 ,<"2 *:&6.>!1 )9%5 -=#3 +;'7/.. ?\ \`\\P\0\p\\H\(\h\\X\8\x\\D\$\d\\T\4\t\ \L\,\l\\\\<\|\\B\"\b\\R\2\r\ \J\*\j\\Z\:\z\\F\&\f\\V\6\v\\N\.\n\\^\>\~\\A\!\a\\Q\1\q\ \I\)\i\\Y\9\y\\E\%\e\\U\5\u\ \M\-\m\\]\=\}\\C\#\c\\S\3\s\ \K\+\k\\[\;\{\\G\'\g\\W\7\w\\O\/\o\\_\?\@ `P0pH(hX8xD$dT4t L,l\<|B"bR2r J*jZ:zF&fV6vN.n^>~A!aQ1q I)iY9yE%eU5u M-m]=}C#cS3s K+k[;{G'gW7wO/o_?\\@@ `Pи0pHȸ(hXظ8xDĸ$dTԸ4t L̸,l\ܸ<|B¸"bRҸ2r Jʸ*jZڸ:zFƸ&fVָ6vNθ.n^޸>~A!aQѸ1q Iɸ)iYٸ9yEŸ%eUո5u M͸-m]ݸ=}Cø#cSӸ3s K˸+k[۸;{GǸ'gW׸7wOϸ/o_߸?ppqApAqpq!p!qpqapaqpqpqpqQpQqpq1p1qpqqpqqpq p qpqIpIqpq)p)qpqipiqpqpqpqYpYqpq9p9qpqypyqpqpqpqEpEqpq%p%qpqepeqpqpqpqUpUqpq5p5qpqupuqpq p qpqMpMqpq-p-qpqmpmqpqpqpq]p]qpq=p=qpq}p}qpqpqpqCpCqpq#p#qpqcpcqpqpqpqSpSqpq3p3qpqspsqpq p qpqKpKqpq+p+qpqkpkqpqpqpq[p[qpq;p;qpq{p{qpqpqpqGpGqpq'p'qpqgpgqpqpqpqWpWqpq7p7qpqwpwqpqpqpqOpOqpq/p/qpqopoqpqpqpq_p_qpq?p?qpqpqp ;x0 7A5:P@}>xàSU|xxSXضmضmƶm۶m۶wWfn0 8 :C 9 ;#8ȣ:c9;L8ēL:SL9L;38̳:s9; ,^d_bɥ^f_aŕV^eV_c͵^g`Í6d6b˭faǝvevcϽg?>{Gu1w 't)vgu9w^t%^vW^u5^w 7t-vwu=w>#>O>3> /+o;~'~_~7~?/?8nAnF9nQF=n1Ɗ;nqƋ?n &8nI&nf9nYf=n9抛;ny招?nOq-XqK-Tq-\q+ĭRqĭZqkĭVqĭ^qmQqmYq[mUqm]q;Sq[q{Wq_qwPqwX;"ȸ⎎;&ظ⎏;!ĸN;%ԸN;#̸Ύ;'ܸΏ ¸.$Ҹ."ʸ⮎&ڸ⮏!Ƹn%ָn#θ'޸{ {$Ѹ{"ɸ➎{&ٸ➏{!Ÿ^{%ո^{#͸ގ{'ݸޏ ø>$Ӹ>"˸⾎&۸⾏!Ǹ~%׸~#ϸ'߸(nA,n!↌*na↍.nF)nQF-n1ƌ+nqƍ/n &(nI&,n)⦌*ni⦍.nf)nYf-n9挛+ny捛/n[(?q -hbq-dRqK-lrqǭbJq+ǭjjqǭfZqkǭnzqmaFqmifqmeVq[mmvqcNq;knqg^q{o~qw`Aqwhaq;<#㎊;:c㎋;>N;9SN;=3Ί;;s΋;? .8K.<+㮊:k㮋>n9[n=;;{?{8G{<'㞊{:g㞋{>^{9W^{=7ފ{;wދ{?>8O></㾊:o㾋>~9_~=?;0n4nㆈ2nㆉ6nF1nF5nƈ3nƉ7n&0n&4n㦈2n㦉6nf1nf5n戛3n扛7n[0n-Hܢq-DܒqK-Lܲq-B܊q+ŭJܪqŭFܚqkŭNܺqŭA܆qmIܦqmEܖq[mMܶqmC܎q;KܮqGܞq{Oܾqw@܁qwHܡq7#⎌;*c⎍;.N;)SN;-3Ό;+s΍;/ .(K.,+⮌*k⮍.n)[n-;+{/{(G{,'➌{*g➍{.^{)W^{-7ތ{+wލ{/>(O>,/⾌*o⾍.~)_~-?+ n$nи"nȸ↎&nظ↏!nĸF%nԸF#n̸Ǝ'nܸƏ n¸&$nҸ&"nʸ⦎&nڸ⦏!nƸf%nָf#nθ掛'n޸[ np"q-xqK-t2q-| q+ƭr*qƭzqkƭv:qƭ~qmq&qmyq[mu6qm}q;s.q{q{w>qqwp!q߸㎈;2㎉;6N;1N;5Έ;3Ή;7.0.4㮈2㮉6n1n537{0{4㞈{2㞉{6^{1^{5ވ{3މ{7>0>4㾈2㾉6~1~537n8nAnF9nQF=n1Ɗ;nqƋ?n &8nI&nf9nYf=n9抛;ny招?nOq-XqK-Tq-\q+ĭRqĭZqkĭVqĭ^qmQqmYq[mUqm]q;Sq[q{Wq_qwPqwX;"ȸ⎎;&ظ⎏;!ĸN;%ԸN;#̸Ύ;'ܸΏ ¸.$Ҹ."ʸ⮎&ڸ⮏!Ƹn%ָn#θ'޸{ {$Ѹ{"ɸ➎{&ٸ➏{!Ÿ^{%ո^{#͸ގ{'ݸޏ ø>$Ӹ>"˸⾎&۸⾏!Ǹ~%׸~#ϸ'߸(nA,n!↌*na↍.nF)nQF-n1ƌ+nqƍ/n &(nI&,n)⦌*ni⦍.nf)nYf-n9挛+ny捛/n[(?q -hbq-dRqK-lrqǭbJq+ǭjjqǭfZqkǭnzqmaFqmifqmeVq[mmvqcNq;knqg^q{o~qw`Aqwhaq;<#㎊;:c㎋;>N;9SN;=3Ί;;s΋;? .8K.<+㮊:k㮋>n9[n=;;{?{8G{<'㞊{:g㞋{>^{9W^{=7ފ{;wދ{?>8O></㾊:o㾋>~9_~=?;0n4nㆈ2nㆉ6nF1nF5nƈ3nƉ7n&0n&4n㦈2n㦉6nf1nf5n戛3n扛7n[0n-Hܢq-DܒqK-Lܲq-B܊q+ŭJܪqŭFܚqkŭNܺqŭA܆qmIܦqmEܖq[mMܶqmC܎q;KܮqGܞq{Oܾqw@܁qwHܡq7#⎌;*c⎍;.N;)SN;-3Ό;+s΍;/ .(K.,+⮌*k⮍.n)[n-;+{/{(G{,'➌{*g➍{.^{)W^{-7ތ{+wލ{/>(O>,/⾌*o⾍.~)_~-?+ n$nи"nȸ↎&nظ↏!nĸF%nԸF#n̸Ǝ'nܸƏ n¸&$nҸ&"nʸ⦎&nڸ⦏!nƸf%nָf#nθ掛'n޸[ n/ 3$P?C 3,p?oc-blosc2-3.1.5/compat/blosc-1.7.0-blosclz.cdata000066400000000000000000001070521521743436100206310ustar00rootroot00000000000000 =*P  H%.7A@J~S\e8ovx?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !  !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !  !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !  !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !  !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ !  !?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~!  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ ! !4?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~x  !"#$%&'()*+,-./0123456789:;<=>?@AB6@xdrc-blosc2-3.1.5/compat/blosc-1.7.0-lz4.cdata000066400000000000000000001066531521743436100177000ustar00rootroot00000000000000! =P (%^.7@J6Sl\enxDz   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P  P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P  P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P  P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P  P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P  P P   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P P P-  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~n      !"#$%&'()*+,-./0123456789:;<=>?@AB,?3ojPc-blosc2-3.1.5/compat/blosc-1.7.0-lz4hc.cdata000066400000000000000000000763331521743436100202140ustar00rootroot00000000000000! = |~A   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P(      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P.PPY;  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~u      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@A@-     npPc-blosc2-3.1.5/compat/blosc-1.7.0-zlib.cdata000066400000000000000000000351541521743436100201240ustar00rootroot00000000000000a = l:< xσڶm۶նm۶Zm۶m'(p*tG)rѢLj+v'H(qɒH*ugȘ)sٲș+w(XpŊ(YtWXrժרOu֫ߠaM6kޢemڶkߡc]vޣg}?`C>bcƎ?aSN>csΛ`K.[+VZf6n۶عk8xǎ8y_x׮߸y?xϞx߾w ;'xvm۶m۶m۶m۶m۶wHlqBBBB""""bbbbRRRR2222rrrr JJJJ****jjjjZZZZ::::zzzzFFFF&&&&ffffVVVV6666vvvvNNNN....nnnn^^^^>>>>~~~~Džą…ƅŅÅEEEEEEEE%%%%%%%%ǥĥ¥ƥťåeeeeeeeeǕĕ•ƕŕÕUUUUUUUU55555555ǵĵµƵŵõuuuuuuuu ǍčƍōÍMMMMMMMM--------ǭĭ­ƭŭímmmmmmmmǝĝƝŝÝ]]]]]]]]========ǽĽ½ƽŽý}}}}}}}} KKKKKKKKKKKKKKKKˀˈ˄ˌ˂ˊˆˎˁˉ˅ˍ˃ˋˇˏ++++++++++++++++kkkkkkkkkkkkkkkk[[[[[[[[[[[[[[[[ۀۈۄیۂۊۆێہۉۅۍۃۋۇۏ;;;;;;;;;;;;;;;;{{{{{{{{{{{{{{{{           WWWWWWW WWWW WWWWWWWW WWWW WWWW WWWWW     7777777 7777 77777777 7777 7777 77777     wwwwwww wwww wwwwwwww wwww wwww wwwww     ..0..(..8..$..4. .,..<.."..2. .*..:..&..6.....>..!..1. .)..9..%..5. .-..=..#..3. .+..;..'..7../..? 0(8$4 ,<"2 *:&6.>!1 )9%5 -=#3 +;'7/?nn nn0nn(nn8nn$nn4n n,nnnn!nn1n n)nn9nn%nn5n n-nn=nn#nn3n n+nn;nn'nn7nn/nn? 0(8$4 ,<"2 *:&6.>!1 )9%5 -=#3 +;'7/.. ?\ \`\\P\0\p\\H\(\h\\X\8\x\\D\$\d\\T\4\t\ \L\,\l\\\\<\|\\B\"\b\\R\2\r\ \J\*\j\\Z\:\z\\F\&\f\\V\6\v\\N\.\n\\^\>\~\\A\!\a\\Q\1\q\ \I\)\i\\Y\9\y\\E\%\e\\U\5\u\ \M\-\m\\]\=\}\\C\#\c\\S\3\s\ \K\+\k\\[\;\{\\G\'\g\\W\7\w\\O\/\o\\_\?\@ `P0pH(hX8xD$dT4t L,l\<|B"bR2r J*jZ:zF&fV6vN.n^>~A!aQ1q I)iY9yE%eU5u M-m]=}C#cS3s K+k[;{G'gW7wO/o_?\\@@ `Pи0pHȸ(hXظ8xDĸ$dTԸ4t L̸,l\ܸ<|B¸"bRҸ2r Jʸ*jZڸ:zFƸ&fVָ6vNθ.n^޸>~A!aQѸ1q Iɸ)iYٸ9yEŸ%eUո5u M͸-m]ݸ=}Cø#cSӸ3s K˸+k[۸;{GǸ'gW׸7wOϸ/o_߸?ppqApAqpq!p!qpqapaqpqpqpqQpQqpq1p1qpqqpqqpq p qpqIpIqpq)p)qpqipiqpqpqpqYpYqpq9p9qpqypyqpqpqpqEpEqpq%p%qpqepeqpqpqpqUpUqpq5p5qpqupuqpq p qpqMpMqpq-p-qpqmpmqpqpqpq]p]qpq=p=qpq}p}qpqpqpqCpCqpq#p#qpqcpcqpqpqpqSpSqpq3p3qpqspsqpq p qpqKpKqpq+p+qpqkpkqpqpqpq[p[qpq;p;qpq{p{qpqpqpqGpGqpq'p'qpqgpgqpqpqpqWpWqpq7p7qpqwpwqpqpqpqOpOqpq/p/qpqopoqpqpqpq_p_qpq?p?qpqpqp ;x0 7A5:P@}>xàSU|xxSXضmضmƶm۶m۶wWfn0 8 :C 9 ;#8ȣ:c9;L8ēL:SL9L;38̳:s9; ,^d_bɥ^f_aŕV^eV_c͵^g`Í6d6b˭faǝvevcϽg?>{Gu1w 't)vgu9w^t%^vW^u5^w 7t-vwu=w>#>O>3> /+o;~'~_~7~?/?8nAnF9nQF=n1Ɗ;nqƋ?n &8nI&nf9nYf=n9抛;ny招?nOq-XqK-Tq-\q+ĭRqĭZqkĭVqĭ^qmQqmYq[mUqm]q;Sq[q{Wq_qwPqwX;"ȸ⎎;&ظ⎏;!ĸN;%ԸN;#̸Ύ;'ܸΏ ¸.$Ҹ."ʸ⮎&ڸ⮏!Ƹn%ָn#θ'޸{ {$Ѹ{"ɸ➎{&ٸ➏{!Ÿ^{%ո^{#͸ގ{'ݸޏ ø>$Ӹ>"˸⾎&۸⾏!Ǹ~%׸~#ϸ'߸(nA,n!↌*na↍.nF)nQF-n1ƌ+nqƍ/n &(nI&,n)⦌*ni⦍.nf)nYf-n9挛+ny捛/n[(?q -hbq-dRqK-lrqǭbJq+ǭjjqǭfZqkǭnzqmaFqmifqmeVq[mmvqcNq;knqg^q{o~qw`Aqwhaq;<#㎊;:c㎋;>N;9SN;=3Ί;;s΋;? .8K.<+㮊:k㮋>n9[n=;;{?{8G{<'㞊{:g㞋{>^{9W^{=7ފ{;wދ{?>8O></㾊:o㾋>~9_~=?;0n4nㆈ2nㆉ6nF1nF5nƈ3nƉ7n&0n&4n㦈2n㦉6nf1nf5n戛3n扛7n[0n-Hܢq-DܒqK-Lܲq-B܊q+ŭJܪqŭFܚqkŭNܺqŭA܆qmIܦqmEܖq[mMܶqmC܎q;KܮqGܞq{Oܾqw@܁qwHܡq7#⎌;*c⎍;.N;)SN;-3Ό;+s΍;/ .(K.,+⮌*k⮍.n)[n-;+{/{(G{,'➌{*g➍{.^{)W^{-7ތ{+wލ{/>(O>,/⾌*o⾍.~)_~-?+ n$nи"nȸ↎&nظ↏!nĸF%nԸF#n̸Ǝ'nܸƏ n¸&$nҸ&"nʸ⦎&nڸ⦏!nƸf%nָf#nθ掛'n޸[ np"q-xqK-t2q-| q+ƭr*qƭzqkƭv:qƭ~qmq&qmyq[mu6qm}q;s.q{q{w>qqwp!q߸㎈;2㎉;6N;1N;5Έ;3Ή;7.0.4㮈2㮉6n1n537{0{4㞈{2㞉{6^{1^{5ވ{3މ{7>0>4㾈2㾉6~1~537n8nAnF9nQF=n1Ɗ;nqƋ?n &8nI&nf9nYf=n9抛;ny招?nOq-XqK-Tq-\q+ĭRqĭZqkĭVqĭ^qmQqmYq[mUqm]q;Sq[q{Wq_qwPqwX;"ȸ⎎;&ظ⎏;!ĸN;%ԸN;#̸Ύ;'ܸΏ ¸.$Ҹ."ʸ⮎&ڸ⮏!Ƹn%ָn#θ'޸{ {$Ѹ{"ɸ➎{&ٸ➏{!Ÿ^{%ո^{#͸ގ{'ݸޏ ø>$Ӹ>"˸⾎&۸⾏!Ǹ~%׸~#ϸ'߸(nA,n!↌*na↍.nF)nQF-n1ƌ+nqƍ/n &(nI&,n)⦌*ni⦍.nf)nYf-n9挛+ny捛/n[(?q -hbq-dRqK-lrqǭbJq+ǭjjqǭfZqkǭnzqmaFqmifqmeVq[mmvqcNq;knqg^q{o~qw`Aqwhaq;<#㎊;:c㎋;>N;9SN;=3Ί;;s΋;? .8K.<+㮊:k㮋>n9[n=;;{?{8G{<'㞊{:g㞋{>^{9W^{=7ފ{;wދ{?>8O></㾊:o㾋>~9_~=?;0n4nㆈ2nㆉ6nF1nF5nƈ3nƉ7n&0n&4n㦈2n㦉6nf1nf5n戛3n扛7n[0n-Hܢq-DܒqK-Lܲq-B܊q+ŭJܪqŭFܚqkŭNܺqŭA܆qmIܦqmEܖq[mMܶqmC܎q;KܮqGܞq{Oܾqw@܁qwHܡq7#⎌;*c⎍;.N;)SN;-3Ό;+s΍;/ .(K.,+⮌*k⮍.n)[n-;+{/{(G{,'➌{*g➍{.^{)W^{-7ތ{+wލ{/>(O>,/⾌*o⾍.~)_~-?+ n$nи"nȸ↎&nظ↏!nĸF%nԸF#n̸Ǝ'nܸƏ n¸&$nҸ&"nʸ⦎&nڸ⦏!nƸf%nָf#nθ掛'n޸[ n/ 3$P?C 3,p?oc-blosc2-3.1.5/compat/blosc-2.0.0-lz4-bitshuffle4-memcpy.cdata000066400000000000000000000366231521743436100233760ustar00rootroot00000000000000$D =0&    / / O     ? ;> !%> @N^ ` ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ ` M MK\K\ M MK\K\ M MK\K\ M MK\K\ M MK\K\ M MK\K\ M MK\K\ M MK\K\ M MK\K\ M MK\K\ M MK\K\ M MK\K\ M MK\K\ M MK\K\ M MK\K\ M MK\K\                                                               &    / / O     ? ;> !%> @N^ ` ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ `  `  ^ ^ ` M MK\K\ M MK\K\ M MK\K\ M MK\K\ M MK\K\ M MK\K\ M MK\K\ M MK\K\ M MK\K\ M MK\K\ M MK\K\ M MK\K\ M MK\K\ M MK\K\ M MK\K\ M MK\K\                                                                p <<J/E '79'%79'%79'%79'%79'%79'%79'%79'%79'%79'%79'%79'%79'%79'%79'%79'%79'%79'%79'%79'%79'%79'%79'%79'%79'%79'%79'%79'%7[i'e#UYi'e#UYi'e#UYi'e#UYi'e#UYi'e#UYi'e#UYi'e#UYi'e#UYi'e#UYi'e#UYi'e#UYi'e#UYi'e#U8eTctRceTctRceTctRceTctRceTctRceTctRceTctRc([82L1B B/@1B/@/3@->B1@17@ /> 5@E 9$C>rc-blosc2-3.1.5/compat/blosc-2.0.0-lz4-bitshuffle8-nomemcpy.cdata000066400000000000000000000267201521743436100237340ustar00rootroot00000000000000$D -#//O  !    . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0 Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P] ][l[l ] ][l[l ] ][l[l ] ][l[l ] ][l[l ] ][l[l ] ][l[l ] ][l[l                ``// `  !    . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0 Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P] ][l[l ] ][l[l ] ][l[l ] ][l[l ] ][l[l ] ][l[l ] ][l[l ] ][l[l                ``//O  !    . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0 Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P] ][l[l ] ][l[l ] ][l[l ] ][l[l ] ][l[l ] ][l[l ] ][l[l ] ][l[l                P`// `  !    . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0  0  . . 0 Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P Pn,n, P] ][l[l ] ][l[l ] ][l[l ] ][l[l ] ][l[l ] ][l[l ] ][l[l ] ][l[l                P` /Op* 1 .1   ) / 5 / ) / 5 / ) / 5 / ) / 5 / ) / 5 / ) / 5 / ) / 5 / ) / 5 / ) / 5 / ) / 5 / ) / 5 / ) / 5 / ) / 5 / ) / 5 " p n,o - P O n,o - P O n,o - P O n,o - P O n,o - P O n,o - P O n,o - P~  ^m\ mk]k~k|m~m!~k|om~m!~ ! BB@ h1#%3'88&&88&&88&&88&&88&&88&&88&&88&&88&&88&&88&&88&&88&&88&g'f$x%6XF'f$x%6XF'f$x%6XF'f$x%6XF'f$x%6XF'f$x%6XF'f$x%6Xvgmevk]k~k|m~m!~k|om~m!~ ! BB@ rc-blosc2-3.1.5/compat/blosc-blosclz-3.0.0.cdata000066400000000000000000000410161521743436100206210ustar00rootroot00000000000000 = B( ?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ #        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~6#m#?  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~~?        !!""##$$%%&&''(())**++,,--..//00112233445566778899::;;<<==>>??@@AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ[[\\]]^^__``aabbccddeeffgghhiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~~B      w|c-blosc2-3.1.5/compat/blosc-lz4-3.0.0.cdata000066400000000000000000000723331521743436100176700ustar00rootroot00000000000000% = t(|9   !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P(      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~/      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~/      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~/      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~P.P[;  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~u      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~/      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~/      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~/      !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~      !"#$%&'()*+,-./0123456789:;<=>?@A-@:     npPc-blosc2-3.1.5/compat/blosc-zstd-3.0.0.cdata000066400000000000000000000110331521743436100201310ustar00rootroot00000000000000 =0 n(/\  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~TT  +{>|>|>|>g$ !"#$%&'()*+,-./0123456789:;<=>?? +{>|>|>|>}$@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^__ +{>|>|>|>}$`abcdefghijklmnopqrstuvwxyz{|}~ +{>|>|>|>}$ +{>|>|>|>}$ +{>|>|>|>}$ +{>|>|>|>}$ +{>|>|>|>}L?k L?k < < < < < < < < < < < < < < < < < < < < < < n(/\  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~TT  +{>|>|>|>g$ !"#$%&'()*+,-./0123456789:;<=>?? +{>|>|>|>}$@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^__ +{>|>|>|>}$`abcdefghijklmnopqrstuvwxyz{|}~ +{>|>|>|>}$ +{>|>|>|>}$ +{>|>|>|>}$ +{>|>|>|>}$ +{>|>|>|>}L?k L?k < < < < < < < < < < < < < < < < < < < < < < n(/\  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~TT  +{>|>|>|>g$ !"#$%&'()*+,-./0123456789:;<=>?? +{>|>|>|>}$@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^__ +{>|>|>|>}$`abcdefghijklmnopqrstuvwxyz{|}~ +{>|>|>|>}$ +{>|>|>|>}$ +{>|>|>|>}$ +{>|>|>|>}$ +{>|>|>|>}L?k L?k < < < < < < < < < < < < < < < < < < < < < <    (/ \  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~T?t  ?;>|>|>|>_it$ !"#$%&'()*+,-./0123456789:;<==!;;>|>||>|>_r<$=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]]!;>|>||>|>_r<$]^_`abcdefghijklmnopqrstuvwxyz{|}}!;>|>||>|>_r<$}~!;>|>||>|>_r<$!;>|>||>|>_r<$!;>|>||>|>_r<$!;>|>||>|>_r={>TL?k < < < < < < < < < < < < < < < < d |W>~d |{Kd|{K?\|d< ^H c-blosc2-3.1.5/compat/filegen-vl.c000066400000000000000000000570021521743436100166150ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Generator for VL-block cframes used in forward compatibility checks. See LICENSES/BLOSC.txt for details about copyright and rights to use. **********************************************************************/ #include "blosc2.h" #include #include #include #include #include #define MAX_BLOCKS 6 /* Each base block pattern is repeated this many times before compression */ #define BLOCK_REPEATS 10 typedef enum { OP_COMPRESS, OP_DECOMPRESS, } op_t; typedef struct { const char *blocks[MAX_BLOCKS]; int32_t sizes[MAX_BLOCKS]; int32_t nblocks; } vl_chunk_data; typedef struct { const vl_chunk_data *chunks; int32_t nchunks; bool variable_chunks; } vl_frame_data; static const vl_chunk_data fixed_chunks[] = { { /* alphanumeric patterns */ .blocks = { "abcdefghijklmnopqrstuvwxyz-abcdefghijklmnopqrstuv", "ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUV", "0123456789-0123456789-0123456789-0123456789-01234", "the-quick-brown-fox-jumps-over-the-lazy-dog-finis", }, .sizes = {50, 50, 50, 50}, .nblocks = 4, }, { /* space / astronomy */ .blocks = { "mercury-venus-earth-mars-jupiter-saturn-uranus-ne", "sun-moon-milky-way-galaxy-star-pulsar-quasar-nova", "andromeda-galaxy-pegasus-cygnus-orion-lyra-aquila", "black-hole-neutron-star-white-dwarf-red-giant-sun", }, .sizes = {50, 50, 50, 50}, .nblocks = 4, }, { /* highly compressible (repeated characters) */ .blocks = { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "ccccccccccccccccccccccccccccccccccccccccccccccccc", "ddddddddddddddddddddddddddddddddddddddddddddddddd", }, .sizes = {50, 50, 50, 50}, .nblocks = 4, }, { /* technical / numerical */ .blocks = { "0xdeadbeef-0xcafebabe-0x01234567-0x89abcdef-0xfff", "3.14159265-2.71828182-1.41421356-1.73205080-3.141", "sha256-md5-sha1-sha512-hmac-aes256-rsa4096-ecdsa-", "blosc2-lz4-zstd-zlib-blosclz-lz4hc-snappy-gzip-bl", }, .sizes = {50, 50, 50, 50}, .nblocks = 4, }, { /* pangrams (natural language) */ .blocks = { "pack-my-box-with-five-dozen-liquor-jugs-and-bread", "how-vexingly-quick-daft-zebras-jump-high-overhead", "sphinx-of-black-quartz-judge-my-vow-and-be-feared", "foxy-quincy-jumps-over-a-wilted-zinnia-bed-bright", }, .sizes = {50, 50, 50, 50}, .nblocks = 4, }, }; static const vl_chunk_data variable_chunks[] = { { /* 2 blocks, total 100 bytes */ .blocks = { "mercury-venus-earth-mars-jupiter-saturn-uranus-ne", "andromeda-galaxy-pegasus-cygnus-orion-lyra-aquila", }, .sizes = {50, 50}, .nblocks = 2, }, { /* 3 blocks, total 150 bytes */ .blocks = { "blosc2-lz4-zstd-zlib-blosclz-lz4hc-snappy-gzip-bl", "3.14159265-2.71828182-1.41421356-1.73205080-3.141", "0xdeadbeef-0xcafebabe-0x01234567-0x89abcdef-0xfff", }, .sizes = {50, 50, 50}, .nblocks = 3, }, { /* 4 blocks, total 200 bytes */ .blocks = { "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "pack-my-box-with-five-dozen-liquor-jugs-and-bread", "how-vexingly-quick-daft-zebras-jump-high-overhead", }, .sizes = {50, 50, 50, 50}, .nblocks = 4, }, { /* 5 blocks, total 250 bytes */ .blocks = { "abcdefghijklmnopqrstuvwxyz-abcdefghijklmnopqrstuv", "ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUV", "0123456789-0123456789-0123456789-0123456789-01234", "the-quick-brown-fox-jumps-over-the-lazy-dog-finis", "foxy-quincy-jumps-over-a-wilted-zinnia-bed-bright", }, .sizes = {50, 50, 50, 50, 50}, .nblocks = 5, }, { /* 6 blocks, total 300 bytes */ .blocks = { "sun-moon-milky-way-galaxy-star-pulsar-quasar-nova", "black-hole-neutron-star-white-dwarf-red-giant-sun", "sphinx-of-black-quartz-judge-my-vow-and-be-feared", "ccccccccccccccccccccccccccccccccccccccccccccccccc", "ddddddddddddddddddddddddddddddddddddddddddddddddd", "sha256-md5-sha1-sha512-hmac-aes256-rsa4096-ecdsa-", }, .sizes = {50, 50, 50, 50, 50, 50}, .nblocks = 6, }, }; static const vl_frame_data fixed_frame = { .chunks = fixed_chunks, .nchunks = (int32_t)(sizeof(fixed_chunks) / sizeof(fixed_chunks[0])), .variable_chunks = false, }; static const vl_frame_data variable_frame = { .chunks = variable_chunks, .nchunks = (int32_t)(sizeof(variable_chunks) / sizeof(variable_chunks[0])), .variable_chunks = true, }; /* ---- Regular (non-VL-block) variable-size-chunk data ---- */ typedef struct { const char *pattern; /* base pattern tiled to fill the chunk */ int32_t pattern_len; int32_t size; /* total chunk uncompressed size in bytes */ } regular_chunk_spec; static const regular_chunk_spec regular_var_specs[] = { {"abcdefghijklmnopqrstuvwxyz-abcdefghijklmnopqrstuv", 50, 1000}, {"ABCDEFGHIJKLMNOPQRSTUVWXYZ-ABCDEFGHIJKLMNOPQRSTUV", 50, 500}, {"0123456789-0123456789-0123456789-0123456789-01234", 50, 2000}, {"the-quick-brown-fox-jumps-over-the-lazy-dog-finis", 50, 520}, {"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 50, 301}, }; #define REGULAR_VAR_NCHUNKS ((int32_t)(sizeof(regular_var_specs) / sizeof(regular_var_specs[0]))) static op_t parse_op(const char *op) { if (strcmp(op, "compress") == 0) { return OP_COMPRESS; } if (strcmp(op, "decompress") == 0) { return OP_DECOMPRESS; } printf("Unknown operation: %s\n", op); exit(EXIT_FAILURE); } static const vl_frame_data *get_frame_data(bool variable_chunks_) { return variable_chunks_ ? &variable_frame : &fixed_frame; } static int32_t total_nbytes(const vl_chunk_data *chunk) { int32_t total = 0; for (int i = 0; i < chunk->nblocks; ++i) { total += chunk->sizes[i]; } return total; } /* Returns a heap-allocated buffer filled with `pattern` tiled to `total_size` bytes. */ static char *fill_pattern(const char *pattern, int32_t pattern_len, int32_t total_size) { char *buf = malloc((size_t)total_size); if (buf == NULL) return NULL; for (int32_t off = 0; off < total_size; off += pattern_len) { int32_t to_copy = (off + pattern_len <= total_size) ? pattern_len : total_size - off; memcpy(buf + off, pattern, (size_t)to_copy); } return buf; } /* Build a heap buffer of `unit_size * BLOCK_REPEATS` bytes by repeating `pattern`. */ static char *expand_block(const char *pattern, int32_t unit_size, int32_t *out_size) { *out_size = unit_size * BLOCK_REPEATS; char *buf = malloc((size_t)*out_size); if (buf == NULL) return NULL; for (int i = 0; i < BLOCK_REPEATS; i++) { memcpy(buf + i * unit_size, pattern, (size_t)unit_size); } return buf; } static int verify_split_buffers(char **ref_blocks, const int32_t *ref_sizes, int32_t ref_nblocks, void **buffers, const int32_t *sizes, int32_t nblocks) { if (nblocks != ref_nblocks) { return -1; } for (int i = 0; i < nblocks; ++i) { if (sizes[i] != ref_sizes[i]) { return -2; } if (memcmp(buffers[i], ref_blocks[i], (size_t)ref_sizes[i]) != 0) { return -3; } } return 0; } static int verify_contiguous_buffer(char **ref_blocks, const int32_t *ref_sizes, int32_t ref_nblocks, const uint8_t *buffer, int32_t nbytes) { int32_t offset = 0; int32_t expected = 0; for (int i = 0; i < ref_nblocks; ++i) expected += ref_sizes[i]; if (nbytes != expected) { return -1; } for (int i = 0; i < ref_nblocks; ++i) { if (memcmp(buffer + offset, ref_blocks[i], (size_t)ref_sizes[i]) != 0) { return -2; } offset += ref_sizes[i]; } return 0; } static int compress_frame(const char *compname, const char *urlpath, bool variable_chunks_) { const vl_frame_data *frame_data = get_frame_data(variable_chunks_); int compcode = blosc2_compname_to_compcode(compname); if (compcode < 0) { printf("Unknown compressor: %s\n", compname); return compcode; } blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.compcode = (uint8_t)compcode; cparams.clevel = 9; cparams.typesize = 1; cparams.nthreads = 1; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; dparams.nthreads = 1; blosc2_storage storage = { .contiguous = true, .urlpath = (char *)urlpath, .cparams = &cparams, .dparams = &dparams, }; blosc2_remove_urlpath(urlpath); blosc2_schunk *schunk = blosc2_schunk_new(&storage); if (schunk == NULL) { printf("Cannot create persistent VL-block schunk.\n"); return EXIT_FAILURE; } blosc2_context *cctx = blosc2_create_cctx(cparams); if (cctx == NULL) { printf("Cannot create compression context.\n"); blosc2_schunk_free(schunk); return EXIT_FAILURE; } for (int i = 0; i < frame_data->nchunks; ++i) { const vl_chunk_data *chunk_data = &frame_data->chunks[i]; /* Expand each block by repeating its base pattern BLOCK_REPEATS times */ char *expanded[MAX_BLOCKS] = {NULL}; int32_t exp_sizes[MAX_BLOCKS] = {0}; int32_t total_exp = 0; for (int j = 0; j < chunk_data->nblocks; ++j) { expanded[j] = expand_block(chunk_data->blocks[j], chunk_data->sizes[j], &exp_sizes[j]); if (expanded[j] == NULL) { printf("Cannot allocate expanded block buffer.\n"); for (int k = 0; k < j; ++k) free(expanded[k]); blosc2_free_ctx(cctx); blosc2_schunk_free(schunk); return EXIT_FAILURE; } total_exp += exp_sizes[j]; } int32_t destsize = total_exp + BLOSC2_MAX_OVERHEAD + chunk_data->nblocks * 16 + 128; uint8_t *chunk = malloc((size_t)destsize); if (chunk == NULL) { printf("Cannot allocate chunk buffer.\n"); for (int j = 0; j < chunk_data->nblocks; ++j) free(expanded[j]); blosc2_free_ctx(cctx); blosc2_schunk_free(schunk); return EXIT_FAILURE; } const void *srcs[MAX_BLOCKS]; for (int j = 0; j < chunk_data->nblocks; ++j) { srcs[j] = expanded[j]; } int32_t cbytes = blosc2_vlcompress_ctx(cctx, srcs, exp_sizes, chunk_data->nblocks, chunk, destsize); for (int j = 0; j < chunk_data->nblocks; ++j) free(expanded[j]); if (cbytes <= 0) { printf("VL-block compression error. Error code: %d\n", cbytes); free(chunk); blosc2_free_ctx(cctx); blosc2_schunk_free(schunk); return cbytes; } if (blosc2_schunk_append_chunk(schunk, chunk, true) != i + 1) { printf("Cannot append VL-block chunk %d to frame.\n", i); free(chunk); blosc2_free_ctx(cctx); blosc2_schunk_free(schunk); return EXIT_FAILURE; } free(chunk); } int32_t total_uncompressed = 0; int32_t total_blocks = 0; for (int i = 0; i < frame_data->nchunks; ++i) { total_uncompressed += total_nbytes(&frame_data->chunks[i]) * BLOCK_REPEATS; total_blocks += frame_data->chunks[i].nblocks; } printf("Wrote %s with %d VL-block chunks%s.\n", urlpath, frame_data->nchunks, frame_data->variable_chunks ? " and variable chunk sizes" : ""); printf(" Uncompressed: %d bytes (%d blocks total, %d chunks)\n", total_uncompressed, total_blocks, frame_data->nchunks); printf(" Compressed: %d bytes (ratio: %.2fx)\n", (int32_t)schunk->cbytes, (float)total_uncompressed / (float)schunk->cbytes); printf(" chunksize: %d (0 means variable)\n", schunk->chunksize); printf(" Blocks per chunk:"); for (int i = 0; i < frame_data->nchunks; ++i) { printf(" %d", frame_data->chunks[i].nblocks); } printf("\n"); blosc2_free_ctx(cctx); blosc2_schunk_free(schunk); return EXIT_SUCCESS; } static int decompress_frame(const char *urlpath) { const vl_frame_data *frame_data; blosc2_schunk *schunk = blosc2_schunk_open(urlpath); if (schunk == NULL) { printf("Cannot open %s\n", urlpath); return EXIT_FAILURE; } frame_data = get_frame_data(schunk->chunksize == 0); if (schunk->nchunks != frame_data->nchunks) { printf("Unexpected number of chunks: %lld\n", (long long)schunk->nchunks); blosc2_schunk_free(schunk); return EXIT_FAILURE; } blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; dparams.nthreads = 1; blosc2_context *dctx = blosc2_create_dctx(dparams); if (dctx == NULL) { printf("Cannot create decompression context.\n"); blosc2_schunk_free(schunk); return EXIT_FAILURE; } for (int i = 0; i < frame_data->nchunks; ++i) { const vl_chunk_data *chunk_data = &frame_data->chunks[i]; /* Expand reference blocks for verification */ char *expanded[MAX_BLOCKS] = {NULL}; int32_t exp_sizes[MAX_BLOCKS] = {0}; int32_t total_exp = 0; for (int j = 0; j < chunk_data->nblocks; ++j) { expanded[j] = expand_block(chunk_data->blocks[j], chunk_data->sizes[j], &exp_sizes[j]); if (expanded[j] == NULL) { printf("Cannot allocate expanded reference buffer.\n"); for (int k = 0; k < j; ++k) free(expanded[k]); blosc2_free_ctx(dctx); blosc2_schunk_free(schunk); return EXIT_FAILURE; } total_exp += exp_sizes[j]; } uint8_t *chunk = NULL; bool needs_free = false; int32_t cbytes = blosc2_schunk_get_chunk(schunk, i, &chunk, &needs_free); if (cbytes <= 0 || chunk == NULL) { printf("Cannot retrieve chunk %d from frame.\n", i); for (int j = 0; j < chunk_data->nblocks; ++j) free(expanded[j]); blosc2_free_ctx(dctx); blosc2_schunk_free(schunk); return EXIT_FAILURE; } int32_t nbytes = total_exp; uint8_t *buffer = malloc((size_t)nbytes); if (buffer == NULL) { printf("Cannot allocate contiguous destination buffer.\n"); for (int j = 0; j < chunk_data->nblocks; ++j) free(expanded[j]); if (needs_free) free(chunk); blosc2_free_ctx(dctx); blosc2_schunk_free(schunk); return EXIT_FAILURE; } int32_t dsize = blosc2_decompress_ctx(dctx, chunk, cbytes, buffer, nbytes); if (dsize != nbytes || verify_contiguous_buffer(expanded, exp_sizes, chunk_data->nblocks, buffer, dsize) != 0) { printf("Contiguous decompression mismatch in chunk %d.\n", i); free(buffer); for (int j = 0; j < chunk_data->nblocks; ++j) free(expanded[j]); if (needs_free) free(chunk); blosc2_free_ctx(dctx); blosc2_schunk_free(schunk); return EXIT_FAILURE; } free(buffer); void *buffers[MAX_BLOCKS] = {NULL}; int32_t sizes[MAX_BLOCKS] = {0}; int32_t nblocks = blosc2_vldecompress_ctx(dctx, chunk, cbytes, buffers, sizes, chunk_data->nblocks); if (nblocks != chunk_data->nblocks || verify_split_buffers(expanded, exp_sizes, chunk_data->nblocks, buffers, sizes, nblocks) != 0) { printf("VL-block decompression mismatch in chunk %d.\n", i); for (int j = 0; j < chunk_data->nblocks; ++j) { free(buffers[j]); free(expanded[j]); } if (needs_free) free(chunk); blosc2_free_ctx(dctx); blosc2_schunk_free(schunk); return EXIT_FAILURE; } for (int j = 0; j < chunk_data->nblocks; ++j) { free(buffers[j]); free(expanded[j]); } if (needs_free) { free(chunk); } } printf("Successful VL-block frame roundtrip for %s\n", urlpath); printf(" chunksize: %d (0 means variable)\n", schunk->chunksize); printf(" Blocks per chunk:"); for (int i = 0; i < frame_data->nchunks; ++i) { printf(" %d", frame_data->chunks[i].nblocks); } printf("\n"); blosc2_free_ctx(dctx); blosc2_schunk_free(schunk); return EXIT_SUCCESS; } static int compress_regular_frame(const char *compname, const char *urlpath) { int compcode = blosc2_compname_to_compcode(compname); if (compcode < 0) { printf("Unknown compressor: %s\n", compname); return compcode; } blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.compcode = (uint8_t)compcode; cparams.clevel = 9; cparams.typesize = 1; cparams.nthreads = 1; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; dparams.nthreads = 1; blosc2_storage storage = { .contiguous = true, .urlpath = (char *)urlpath, .cparams = &cparams, .dparams = &dparams, }; blosc2_remove_urlpath(urlpath); blosc2_schunk *schunk = blosc2_schunk_new(&storage); if (schunk == NULL) { printf("Cannot create persistent regular-chunk schunk.\n"); return EXIT_FAILURE; } blosc2_context *cctx = blosc2_create_cctx(cparams); if (cctx == NULL) { printf("Cannot create compression context.\n"); blosc2_schunk_free(schunk); return EXIT_FAILURE; } int32_t total_uncompressed = 0; for (int i = 0; i < REGULAR_VAR_NCHUNKS; ++i) { const regular_chunk_spec *spec = ®ular_var_specs[i]; char *src = fill_pattern(spec->pattern, spec->pattern_len, spec->size); if (src == NULL) { printf("Cannot allocate source buffer for chunk %d.\n", i); blosc2_free_ctx(cctx); blosc2_schunk_free(schunk); return EXIT_FAILURE; } int32_t destsize = spec->size + BLOSC2_MAX_OVERHEAD; uint8_t *chunk = malloc((size_t)destsize); if (chunk == NULL) { printf("Cannot allocate chunk buffer.\n"); free(src); blosc2_free_ctx(cctx); blosc2_schunk_free(schunk); return EXIT_FAILURE; } int32_t cbytes = blosc2_compress_ctx(cctx, src, spec->size, chunk, destsize); free(src); if (cbytes <= 0) { printf("Compression error for chunk %d. Error code: %d\n", i, cbytes); free(chunk); blosc2_free_ctx(cctx); blosc2_schunk_free(schunk); return cbytes; } if (blosc2_schunk_append_chunk(schunk, chunk, true) != i + 1) { printf("Cannot append chunk %d to frame.\n", i); free(chunk); blosc2_free_ctx(cctx); blosc2_schunk_free(schunk); return EXIT_FAILURE; } free(chunk); total_uncompressed += spec->size; } printf("Wrote %s with %d regular variable-size chunks.\n", urlpath, REGULAR_VAR_NCHUNKS); printf(" Uncompressed: %d bytes (%d chunks)\n", total_uncompressed, REGULAR_VAR_NCHUNKS); printf(" Compressed: %d bytes (ratio: %.2fx)\n", (int32_t)schunk->cbytes, (float)total_uncompressed / (float)schunk->cbytes); printf(" chunksize: %d (0 means variable)\n", schunk->chunksize); printf(" Sizes per chunk:"); for (int i = 0; i < REGULAR_VAR_NCHUNKS; ++i) { printf(" %d", regular_var_specs[i].size); } printf("\n"); blosc2_free_ctx(cctx); blosc2_schunk_free(schunk); return EXIT_SUCCESS; } static int decompress_regular_frame(const char *urlpath) { blosc2_schunk *schunk = blosc2_schunk_open(urlpath); if (schunk == NULL) { printf("Cannot open %s\n", urlpath); return EXIT_FAILURE; } if (schunk->nchunks != REGULAR_VAR_NCHUNKS) { printf("Unexpected number of chunks: %lld (expected %d)\n", (long long)schunk->nchunks, REGULAR_VAR_NCHUNKS); blosc2_schunk_free(schunk); return EXIT_FAILURE; } blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; dparams.nthreads = 1; blosc2_context *dctx = blosc2_create_dctx(dparams); if (dctx == NULL) { printf("Cannot create decompression context.\n"); blosc2_schunk_free(schunk); return EXIT_FAILURE; } for (int i = 0; i < REGULAR_VAR_NCHUNKS; ++i) { const regular_chunk_spec *spec = ®ular_var_specs[i]; char *ref = fill_pattern(spec->pattern, spec->pattern_len, spec->size); if (ref == NULL) { printf("Cannot allocate reference buffer for chunk %d.\n", i); blosc2_free_ctx(dctx); blosc2_schunk_free(schunk); return EXIT_FAILURE; } uint8_t *chunk = NULL; bool needs_free = false; int32_t cbytes = blosc2_schunk_get_chunk(schunk, i, &chunk, &needs_free); if (cbytes <= 0 || chunk == NULL) { printf("Cannot retrieve chunk %d from frame.\n", i); free(ref); blosc2_free_ctx(dctx); blosc2_schunk_free(schunk); return EXIT_FAILURE; } char *buffer = malloc((size_t)spec->size); if (buffer == NULL) { printf("Cannot allocate decompression buffer for chunk %d.\n", i); free(ref); if (needs_free) free(chunk); blosc2_free_ctx(dctx); blosc2_schunk_free(schunk); return EXIT_FAILURE; } int32_t dsize = blosc2_decompress_ctx(dctx, chunk, cbytes, buffer, spec->size); if (dsize != spec->size || memcmp(buffer, ref, (size_t)spec->size) != 0) { printf("Decompression mismatch in chunk %d.\n", i); free(buffer); free(ref); if (needs_free) free(chunk); blosc2_free_ctx(dctx); blosc2_schunk_free(schunk); return EXIT_FAILURE; } free(buffer); free(ref); if (needs_free) free(chunk); } printf("Successful regular variable-chunk frame roundtrip for %s\n", urlpath); printf(" chunksize: %d (0 means variable)\n", schunk->chunksize); printf(" Sizes per chunk:"); for (int i = 0; i < REGULAR_VAR_NCHUNKS; ++i) { printf(" %d", regular_var_specs[i].size); } printf("\n"); blosc2_free_ctx(dctx); blosc2_schunk_free(schunk); return EXIT_SUCCESS; } /* Returns true if the first chunk in the frame uses VL-blocks encoding. */ static bool peek_is_vl_schunk(const char *urlpath) { blosc2_schunk *schunk = blosc2_schunk_open(urlpath); if (schunk == NULL || schunk->nchunks == 0) { if (schunk) blosc2_schunk_free(schunk); return false; } uint8_t *chunk = NULL; bool needs_free = false; bool is_vl = false; if (blosc2_schunk_get_chunk(schunk, 0, &chunk, &needs_free) > 0 && chunk != NULL) { is_vl = (chunk[BLOSC2_CHUNK_BLOSC2_FLAGS2] & BLOSC2_VL_BLOCKS) != 0; if (needs_free) free(chunk); } blosc2_schunk_free(schunk); return is_vl; } int main(int argc, char *argv[]) { int exit_code = EXIT_FAILURE; bool variable_chunks_ = false; bool regular_ = false; if (argc < 3 || argc > 5) { printf("Usage:\n"); printf("%s compress [--variable-chunks|--regular]\n", argv[0]); printf("%s decompress \n", argv[0]); return EXIT_FAILURE; } op_t operation = parse_op(argv[1]); if (operation == OP_COMPRESS && argc == 5) { if (strcmp(argv[4], "--variable-chunks") == 0) { variable_chunks_ = true; } else if (strcmp(argv[4], "--regular") == 0) { regular_ = true; } else { printf("Unknown flag: %s\n", argv[4]); return EXIT_FAILURE; } } if (operation == OP_DECOMPRESS && argc != 3) { printf("Usage:\n"); printf("%s decompress \n", argv[0]); return EXIT_FAILURE; } if (operation == OP_COMPRESS && argc < 4) { printf("Usage:\n"); printf("%s compress [--variable-chunks|--regular]\n", argv[0]); return EXIT_FAILURE; } printf("Blosc version info: %s\n", blosc2_get_version_string()); blosc2_init(); if (operation == OP_COMPRESS) { if (regular_) { exit_code = compress_regular_frame(argv[2], argv[3]); } else { exit_code = compress_frame(argv[2], argv[3], variable_chunks_); } } else { if (peek_is_vl_schunk(argv[2])) { exit_code = decompress_frame(argv[2]); } else { exit_code = decompress_regular_frame(argv[2]); } } blosc2_destroy(); return exit_code; } c-blosc2-3.1.5/compat/filegen.c000066400000000000000000000172631521743436100162030ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Generator data file for Blosc forward and backward tests. Author: Elvis Stansvik, Francesc Alted See LICENSES/BLOSC.txt for details about copyright and rights to use. **********************************************************************/ #include "blosc2.h" #include #include #include #include #include #if defined(_WIN32) && !defined(__MINGW32__) #include #endif /* _WIN32 */ #define SIZE (1000 * 1000) typedef enum { OP_COMPRESS, OP_SHUFFLE, OP_BITSHUFFLE, OP_DECOMPRESS, OP_UNSHUFFLE, OP_BITUNSHUFFLE, } op_t; static const char *op_name(op_t op) { switch (op) { case OP_COMPRESS: return "compress"; case OP_SHUFFLE: return "shuffle"; case OP_BITSHUFFLE: return "bitshuffle"; case OP_DECOMPRESS: return "decompress"; case OP_UNSHUFFLE: return "unshuffle"; case OP_BITUNSHUFFLE: return "bitunshuffle"; } return "unknown"; } static op_t parse_op(const char *op) { if (strcmp(op, "compress") == 0) { return OP_COMPRESS; } else if (strcmp(op, "shuffle") == 0) { return OP_SHUFFLE; } else if (strcmp(op, "bitshuffle") == 0) { return OP_BITSHUFFLE; } else if (strcmp(op, "decompress") == 0) { return OP_DECOMPRESS; } else if (strcmp(op, "unshuffle") == 0) { return OP_UNSHUFFLE; } else if (strcmp(op, "bitunshuffle") == 0) { return OP_BITUNSHUFFLE; } else { printf("Unknown operation: %s\n", op); exit(-1); } } static const char *fixture_basename(const char *path) { const char *slash = strrchr(path, '/'); return slash == NULL ? path : slash + 1; } static op_t expected_decode_op(const char *path) { const char *name = fixture_basename(path); if (strncmp(name, "bitshuffle", strlen("bitshuffle")) == 0) { return OP_BITUNSHUFFLE; } if (strncmp(name, "shuffle", strlen("shuffle")) == 0) { return OP_UNSHUFFLE; } return OP_DECOMPRESS; } static int32_t run(op_t op, const void * src, void * dest, const size_t size) { int32_t result = -1; if (op == OP_COMPRESS) { /* Compress with clevel=9 and shuffle active */ result = blosc1_compress(9, 1, sizeof(int32_t), size, src, dest, size); if (result == 0) { printf("Buffer is incompressible. Giving up.\n"); exit(1); } if (result < 0) { printf("Compression error. Error code: %d\n", result); return result; } printf("Compression successful: %d bytes compressed.\n", result); } else if (op == OP_SHUFFLE) { result = blosc2_shuffle(sizeof(int32_t), size, src, dest); if (result < 0) { printf("Shuffle error. Error code: %d\n", result); return result; } printf("Shuffle successful: %d bytes shuffled.\n", result); } else if (op == OP_BITSHUFFLE) { result = blosc2_bitshuffle(sizeof(int32_t), size, src, dest); if (result < 0) { printf("Bitshuffle error. Error code: %d\n", result); return result; } printf("Bitshuffle successful: %d bytes shuffled.\n", result); } else if (op == OP_DECOMPRESS) { /* Compress with clevel=9 and shuffle active */ result = blosc1_decompress(src, dest, size); if (result < 0) { printf("Decompression error. Error code: %d\n", result); return result; } printf("Decompression successful!\n"); } else if (op == OP_UNSHUFFLE) { result = blosc2_unshuffle(sizeof(int32_t), size, src, dest); if (result < 0) { printf("Unshuffle error. Error code: %d\n", result); return result; } printf("Unshuffle successful: %d bytes unshuffled.\n", result); } else if (op == OP_BITUNSHUFFLE) { result = blosc2_bitunshuffle(sizeof(int32_t), size, src, dest); if (result < 0) { printf("Bitunshuffle error. Error code: %d\n", result); return result; } printf("Bitunshuffle successful: %d bytes bitunshuffled.\n", result); } return result; } int main(int argc, char *argv[]) { BLOSC_UNUSED_PARAM(argc); static int32_t data[SIZE]; static int32_t data_out[SIZE]; static int32_t data_dest[SIZE]; size_t isize = SIZE * sizeof(int32_t); int dsize = SIZE * sizeof(int32_t); int csize; long fsize; int i; int exit_code = 0; FILE *f; if (argc != 3 && argc != 4) { printf("Usage:\n"); printf("%s \n", argv[0]); printf("%s \n", argv[0]); return 1; } op_t operation = parse_op(argv[1]); int is_encoding = operation == OP_COMPRESS || operation == OP_SHUFFLE || operation == OP_BITSHUFFLE; /* Register the filter with the library */ printf("Blosc version info: %s\n", blosc2_get_version_string()); /* Initialize the Blosc compressor */ blosc2_init(); blosc2_set_nthreads(1); if (operation == OP_COMPRESS) { /* Use the argv[2] compressor. The supported ones are "blosclz", "lz4", "lz4hc", "zlib" and "zstd"*/ blosc1_set_compressor(argv[2]); } for (i = 0; i < SIZE; i++) { data[i] = i; } if (is_encoding) { csize = run(operation, data, data_out, isize); if (csize < 0) { return csize; } if (operation == OP_COMPRESS) { printf("Compression: %d -> %d (%.1fx)\n", (int)isize, csize, (1. * (int)isize) / csize); } /* Write data_out to argv[3] */ f = fopen(argv[3], "wb+"); if (f == NULL) { printf("Cannot open %s for writing: %s\n", argv[3], strerror(errno)); blosc2_destroy(); return EXIT_FAILURE; } if (fwrite(data_out, 1, (size_t) csize, f) == (uint32_t) csize) { printf("Wrote %s\n", argv[3]); } else { printf("Write failed"); } fclose(f); } else { const char *name = fixture_basename(argv[2]); size_t name_len = strlen(name); if (name_len >= strlen(".b2frame") && strcmp(name + name_len - strlen(".b2frame"), ".b2frame") == 0) { printf("Frame fixture detected: %s\n", argv[2]); printf("Use './filegen-vl decompress %s' for .b2frame inputs.\n", argv[2]); return EXIT_FAILURE; } op_t expected_op = expected_decode_op(argv[2]); if (operation != expected_op) { printf("Operation/file mismatch for %s\n", argv[2]); printf("Use '%s' for this fixture, not '%s'.\n", op_name(expected_op), op_name(operation)); return EXIT_FAILURE; } /* Read from argv[2] into data_out. */ f = fopen(argv[2], "rb"); if (f == NULL) { printf("Cannot open %s for reading: %s\n", argv[2], strerror(errno)); blosc2_destroy(); return EXIT_FAILURE; } fseek(f, 0, SEEK_END); fsize = ftell(f); fseek(f, 0, SEEK_SET); if (fread(data_out, 1, (size_t) fsize, f) == (uint32_t) fsize) { printf("Checking %s\n", argv[2]); } else { printf("Read failed"); } fclose(f); dsize = run(operation, data_out, data_dest, (size_t) dsize); if (dsize < 0) { return dsize; } char *isbitshuf = strstr(argv[2], "-bitshuffle"); if ((isbitshuf != NULL) && (dsize % 8) != 0) { dsize -= dsize % 8; // do not check unaligned data (e.g. blosc-1.17.1-bitshuffle8-nomemcpy.cdata) } exit_code = memcmp(data, data_dest, dsize) ? EXIT_FAILURE : EXIT_SUCCESS; if (exit_code == EXIT_SUCCESS) { printf("Successful roundtrip!\n"); } else { printf("Decompressed data differs from original!\n"); for (i = 0; i < dsize; i++) { if (((uint8_t*)data)[i] != ((uint8_t*)data_dest)[i]) { printf("values start to differ in pos: %d\n", i); break; } } } } /* After using it, destroy the Blosc environment */ blosc2_destroy(); return exit_code; } c-blosc2-3.1.5/compat/shuffle-2.20.0.cdata000066400000000000000000172044001521743436100175740ustar00rootroot00000000000000  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~  !"#$%&'()*+,-./0123456789:;<=>?  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""################################################################################################################################################################################################################################################################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))****************************************************************************************************************************************************************************************************************************************************************++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------................................................................................................................................................................................................................................................................////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<================================================================================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffgggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""################################################################################################################################################################################################################################################################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))****************************************************************************************************************************************************************************************************************************************************************++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------................................................................................................................................................................................................................................................................////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<================================================================================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffgggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""################################################################################################################################################################################################################################################################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))****************************************************************************************************************************************************************************************************************************************************************++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------................................................................................................................................................................................................................................................................////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<================================================================================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffgggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""################################################################################################################################################################################################################################################################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))****************************************************************************************************************************************************************************************************************************************************************++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------................................................................................................................................................................................................................................................................////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<================================================================================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffgggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""################################################################################################################################################################################################################################################################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))****************************************************************************************************************************************************************************************************************************************************************++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------................................................................................................................................................................................................................................................................////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<================================================================================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffgggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""################################################################################################################################################################################################################################################################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))****************************************************************************************************************************************************************************************************************************************************************++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------................................................................................................................................................................................................................................................................////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<================================================================================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffgggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""################################################################################################################################################################################################################################################################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))****************************************************************************************************************************************************************************************************************************************************************++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------................................................................................................................................................................................................................................................................////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<================================================================================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffgggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""################################################################################################################################################################################################################################################################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))****************************************************************************************************************************************************************************************************************************************************************++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------................................................................................................................................................................................................................................................................////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<================================================================================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffgggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""################################################################################################################################################################################################################################################################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))****************************************************************************************************************************************************************************************************************************************************************++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------................................................................................................................................................................................................................................................................////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<================================================================================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffgggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""################################################################################################################################################################################################################################################################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))****************************************************************************************************************************************************************************************************************************************************************++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------................................................................................................................................................................................................................................................................////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<================================================================================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffgggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""################################################################################################################################################################################################################################################################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))****************************************************************************************************************************************************************************************************************************************************************++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------................................................................................................................................................................................................................................................................////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<================================================================================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffgggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""################################################################################################################################################################################################################################################################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))****************************************************************************************************************************************************************************************************************************************************************++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------................................................................................................................................................................................................................................................................////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<================================================================================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffgggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""################################################################################################################################################################################################################################################################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))****************************************************************************************************************************************************************************************************************************************************************++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------................................................................................................................................................................................................................................................................////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<================================================================================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffgggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""################################################################################################################################################################################################################################################################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))****************************************************************************************************************************************************************************************************************************************************************++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------................................................................................................................................................................................................................................................................////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<================================================================================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffgggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""################################################################################################################################################################################################################################################################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))****************************************************************************************************************************************************************************************************************************************************************++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------................................................................................................................................................................................................................................................................////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<================================================================================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________________````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````````aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffgggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggghhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiijjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""################################################################################################################################################################################################################################################################$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((())))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))****************************************************************************************************************************************************************************************************************************************************************++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------................................................................................................................................................................................................................................................................////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111122222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444444455555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555556666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777777788888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888888889999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<================================================================================================================================================================================================================================================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB c-blosc2-3.1.5/compat/varchunks-lz4-3.0.0.b2frame000066400000000000000000000014071521743436100210260ustar00rootroot00000000000000b2frameaS;%Z$2 abcdefghijklmnopqrstuvwxyz-2Pstuv%X$0 ABCDEFGHIJKLMNOPQRSTUVWXYZ-2PSTUV%0V|"0123456789- 'S2zP1234"0123456789- 'S2zP1234"0123456789- 'S2zP1234"0123456789- 'S2zP1234%(mAthe-quick-brown-fox-jumps-over-lazy-dog-finis2Pinisthe-quick-brown-fox-%--<$a12Paaaa((HZz#c-blosc2-3.1.5/compat/vlblocks-lz4-fixed-3.0.0.b2frame000066400000000000000000000030101521743436100217260ustar00rootroot00000000000000b2framea'<50d abcdefghijklmnopqrstuvwxyz-2Pstuv ABCDEFGHIJKLMNOPQRSTUVWXYZ-2PSTUV0123456789- 'S2zP1234the-quick-brown-fox-jumps-over-lazy-dog-finis2Pinis560qmercury-venus-earth-mars-jupiter-saturn-ura!?ne2Ps-nesun-moon-milky-way-galaxy-star-pulsar-qua_nova2Pnova#andromeda-galaxy-pegasus-cygnus-orion-lyra-aquila2Puila#black-hole-neutron-star-white-dwarf-red-giant-sun2P-sun50Ib{a12{Paaaab12{Pbbbbc12{Pccccd12{Pdddd570p0xdeadbeef-0xcafebabe-0x01234567-0x89abcd!Offf2Pxfff3.14159265-2.71828182-1.41421356-1.73205080-,2P.141sha256-md5-sha1512-hmac-aesrsa4096-ecdsa-2Pdsa-blosc2-lz4-zstd-zlib- lzhc-snappy-gzip-bl2Pp-bl580r#pack-my-box-with-five-dozen-liquor-jugs-and-bread2Pread#how-vexingly-quick-daft-zebras-jump-high-overhead2Phead#sphinx-of-black-quartz-judge-my-vow-and-be-feared2Pared#foxy-quincy-jumps-over-a-wilted-zinnia-bed-bright2Pight((H9#c-blosc2-3.1.5/compat/vlblocks-lz4-variable-3.0.0.b2frame000066400000000000000000000030101521743436100224140ustar00rootroot00000000000000b2framea'<5(imercury-venus-earth-mars-jupiter-saturn-ura!?ne2Ps-ne#andromeda-galaxy-pegasus-cygnus-orion-lyra-aquila2Puila5,oblosc2-lz4-zstd-zlib- lzhc-snappy-gzip-bl2Pp-bl3.14159265-2.71828182-1.41421356-1.73205080-,2P.1410xdeadbeef-0xcafebabe-0x01234567-0x89abcd!Offf2Pxfff50Iba12{Paaaab12{Pbbbb#pack-my-box-with-five-dozen-liquor-jugs-and-bread2Pread#how-vexingly-quick-daft-zebras-jump-high-overhead2Phead5 I4h abcdefghijklmnopqrstuvwxyz-2Pstuv ABCDEFGHIJKLMNOPQRSTUVWXYZ-2PSTUV0123456789- 'S2zP1234the-quick-brown-fox-jumps-over-lazy-dog-finis2Pinis#foxy-quincy-jumps-over-a-wilted-zinnia-bed-bright2Pight5 p8y/sun-moon-milky-way-galaxy-star-pulsar-qua_nova2Pnova#black-hole-neutron-star-white-dwarf-red-giant-sun2P-sun#sphinx-of-black-quartz-judge-my-vow-and-be-feared2Paredc12{Pccccd12{Pddddsha256-md5-sha1512-hmac-aesrsa4096-ecdsa-2Pdsa-((H#c-blosc2-3.1.5/doc/000077500000000000000000000000001521743436100136775ustar00rootroot00000000000000c-blosc2-3.1.5/doc/Doxyfile000066400000000000000000000007311521743436100154060ustar00rootroot00000000000000PROJECT_NAME = "blosc2" XML_OUTPUT = xml INPUT = ../include/ GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO GENERATE_HTML = NO GENERATE_XML = YES RECURSIVE = YES QUIET = YES JAVADOC_AUTOBRIEF = YES WARN_IF_UNDOCUMENTED = NO MACRO_EXPANSION = YES PREDEFINED = IN_DOXYGEN EXTRACT_ALL = YES EXTRACT_STATIC = YES c-blosc2-3.1.5/doc/_static/000077500000000000000000000000001521743436100153255ustar00rootroot00000000000000c-blosc2-3.1.5/doc/_static/blosc-logo_256.png000066400000000000000000000231761521743436100205000ustar00rootroot00000000000000PNG  IHDRwwqsBIT|d pHYsVhw?tEXtSoftwarewww.inkscape.org< IDATxyxT?>HFv]@Dq Vժ}۾ڷVmkjU۾jĭjuoU"; !$L~dr; p?83s #pe6{.RlPR 䰫uԞ_exL-xGzQC.B>Sq#K_!eElu |p;U!?gaaq(sIN)5Rr_zo C^gyk` C{p]/iΎp=RA};G@EJ<3)s{Puž#TE18%BKXX+eHy 0xk)`WPUFHqs%,,e-9 J ^b KXX$]xl{Ai*ȍ0OvzW ļmzo4rYXHAfu<^ K,.~`aa=@""9$q33XX%hTE "{H2ۯwXXDV`ۃ$!P/iS| mf f ^|aSH).R&؄Na9&iaqd8 A9 Nz#k׬46:YX*t啳%rM9i.rrB.AKQjӤgTI#uTO)[ܒ É0)AY_EnN@1Y &K`f8DZ_*ʉ&qN$Uf/f*R5 u &I^v2h# b@ b<0[*%?ͬ_v.uC>c !*B-e-X8渆EaPlrDLȉJR6ŎH>CEhP"DPhv$لJO'OigR{Z]U:Ɛغ WF'`bL *a@lB*6*v-ǟJT5Z @ #M2K#Raup8+Y`#qkkgܻ8ѵiMZ1-TN-[жƬ`X^ &_A*_K ]W6;U͞1Leܭ|%* mR0T"*eJa$7]A:hVjO1Zdl`3n EC/3s}zcvJ}Wɳ3֊[$X?,)x@F#z)mt #e=(>!dQq6iN@wVz"`QQJTD@H)!)BHA>|Ŵ3~@*aI ^n9&5yI"*fNƆx6?~Ϳ ƨ^]@@:W=fF8c:F:G94F vJuF!SuV!]HI'1-+#xlkL孡2PQR_ด }kIe8p>U![ p+wIٞ,A/8vE}&zr$#TO}xmBJaC¥7?Np!O3IEx4n>CX"tvdldnL`&fxd0w1sKpׂRlP H_qܹox \ uޫL7Q:~BxHЫ"% p}\J{,-╖~]9  >r !7L^V\(^@;U\~V3Pԃ!Dh|giFś Hh0l~Pw6;Ã:EH]=#}ߍ޾/r]:& A;0vNJ3˓<#=*|#Ս(V:k33b$W.ק"W )}EcஂW.c_{we?W%e-4 JoMbe`8, % 8tOt6X`O6@@pWNw!Y<[}tm`Üx}xߌ,冈K<Z25N:Ι1QW^ߦz6mF۱8Ec]U]Bak'EG'cv!Qzw y´:C@a}AI'#0 s5Aԝ,LC_E z$;g(.5ꏼ%\"y0Z6_Eᡆ9Q?:UH.?wVڠXF$iɽ/m3;hfd`W%qcCȳ^nj88T>@ݧ[ dKv^s<_k⩤+WWf}o=؉/1xG^40b l&@~I'//|i|?7GyG<& (,7OWђ\1C>*,iGHig[bܘ]迍pjȝ_51+H+HjpUٚ]<c6Eؗy4ih Bt(ViuS];v~3 0ڹ-NI\G0^&D|Nb_6*)d;5A{F[lY"yl QBݤkNx>?E}_ lp1 vYldQhaV `3qEF$e6>n}{ 4VL_ןwPcvǩ v)~_&=hJO@**u1U-T5hkP׸~ F6٦{8.cAGfx/Q=^vE< Q `PǓ/<_r_5*z#b94Fʟ }=V'6ɿ#^ gt*{7:fl$NP LRT hXƶpheǻ{6u&3°nX̄__g R7EaJAxS7efkn5:s x65 !{'T~QRwSMJ|*P טzsB}@2ŒI ʋڛ4vD[!ͱ;B,m9!sf>eA=7~*dI^A '9@ {/4##jMiU__s4xg+Yٺ;3;-≢gzx5kޕ(F8qS vN$Vǵ5v KI#tjPGaU樛/C }tv١an䱢Եꮤbcdv{E`?+Zc^E%;mvn[iA,O1%;ۺuZ7t>煒TBUPyڂ\z~ Q/w]ƏtMC6@.B#u?F݀y6MM+Wӝnxkq {/>*J7\ğ¥})LSDx.\5?EfGWHZ,JVԡY-c:F/v4߫[HPCX(HJ o lOleY|i6Aur0Jk#c5AB*mmwDw%c5>U͌ƃ}s\xu ^ +bvƆ~;*ݑ1&4FE5fPK'*|Q/ՑAl *8F+ Me8n"6TMGLvhgzc(pm\\˲VR`Q5σmk+B 鸩D )7 .=ܜen[n5_1cN4G_WJĞ LɰHr(5sM']_ljJHgS];9޽;sf;{*lm:H\y4CL(NUCܕJWĚ`qJZPLvbk1esg,mfh 366Bդx€ٟraڮׯNf[(r1ս];d&!G YJ ޵7Wwub-{#{utI23| d6uD*02{Ǔ}{% c,mƃ h .7ptt("' |6ұ v.~l0ڹ~Z"Bϸ,k_ZNS~_ii=b"fI@d⡂;kX?Nrm{Y$sE ZAe8[chVؚ_G$U0.="ĢW:tq͙DЂ-R舩@$=2Bl)UU2 ;Bf!E, Dǭyoq+vV.P>5civ20:t @Y֧`w#w2`C4Ƈ`݊ujS5!pYjj7WUBtqXC@'jDodeiP{'(RoJEGx(1 P\V>J8]Vo8})f&8k &WJ iҖoHwJl, l{B|)9iijid"xd#,L=CSd^Գ )V/F_-YڔXX$~W rX^2\قQ¢/=RlF%֦"&q)_@)/q,,zүa"oROdu,, U$ߗc71UvO3t+ RAgadgPz xZDq*caL"TW )[xފ5ɔ4A>Tu,,̥o#'$y}1-,o;d,,%z׽JV8i ux"tf%iWp t)BU>p|o㷰H4 E4IxOH= ruՒrE;0?IZXXt %bO0ސ}`i B=4ڥl~R_o=![XX覇Pu٧p Ei-,ҍ@H4mo7i !BYs-,қ.#`sY"{1^U"PEC< Dҥ(Jtjl<:wY-,-@TYzwMEe6eaa XeO. $ |9ge೰8  HE~Jv!1W4<ʶYE}!^Er*l2\ǁ*R 6jS8 b]%BT2߿2H v ]K~˺oaq`Gv)_п+ՋH.ߨ@TIENDB`c-blosc2-3.1.5/doc/_static/css/000077500000000000000000000000001521743436100161155ustar00rootroot00000000000000c-blosc2-3.1.5/doc/_static/css/custom.css000066400000000000000000000120121521743436100201350ustar00rootroot00000000000000li.nav-item i.fa-external-link-alt { display: none } :root { /***************************************************************************** * Theme config **/ --pst-header-height: 60px; /***************************************************************************** * Font size **/ --pst-font-size-base: 15px; /* base font size - applied at body / html level */ /* heading font sizes */ --pst-font-size-h1: 36px; --pst-font-size-h2: 32px; --pst-font-size-h3: 26px; --pst-font-size-h4: 21px; --pst-font-size-h5: 18px; --pst-font-size-h6: 16px; /* smaller then heading font sizes*/ --pst-font-size-milli: 12px; --pst-sidebar-font-size: .9em; --pst-sidebar-caption-font-size: .9em; /***************************************************************************** * Font family **/ /* These are adapted from https://systemfontstack.com/ */ --pst-font-family-base-system: -apple-system, BlinkMacSystemFont, Segoe UI, "Helvetica Neue", Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol; --pst-font-family-monospace-system: "SFMono-Regular", Menlo, Consolas, Monaco, Liberation Mono, Lucida Console, monospace; --pst-font-family-base: var(--pst-font-family-base-system); --pst-font-family-heading: var(--pst-font-family-base); --pst-font-family-monospace: var(--pst-font-family-monospace-system); /***************************************************************************** * Color * * Colors are defined in rgb string way, "red, green, blue" **/ --pst-color-primary: 3, 134, 150; --pst-color-primary-dark: 2, 89, 100; --pst-color-secondary: 230, 169, 10; --pst-color-success: 40, 167, 69; --pst-color-info: 0, 123, 255; /*23, 162, 184;*/ --pst-color-warning: 255, 193, 7; --pst-color-danger: 220, 53, 69; --pst-color-text-base: 51, 51, 51; --pst-color-h1: var(--pst-color-primary-dark); --pst-color-h2: var(--pst-color-primary-dark); --pst-color-h3: var(--pst-color-text-base); --pst-color-h4: var(--pst-color-text-base); --pst-color-h5: var(--pst-color-text-base); --pst-color-h6: var(--pst-color-text-base); --pst-color-paragraph: var(--pst-color-text-base); --pst-color-link: var(--pst-color-primary); --pst-color-link-hover: var(--pst-color-secondary); --pst-color-headerlink: var(--pst-color-primary); --pst-color-headerlink-hover: 255, 255, 255; --pst-color-preformatted-text: 34, 34, 34; --pst-color-preformatted-background: 250, 250, 250; --pst-color-inline-code: var(--pst-color-primary); --pst-color-active-navigation: var(--pst-color-secondary); --pst-color-navbar-link: 77, 77, 77; --pst-color-navbar-link-hover: var(--pst-color-active-navigation); --pst-color-navbar-link-active: var(--pst-color-active-navigation); --pst-color-sidebar-link: 77, 77, 77; --pst-color-sidebar-link-hover: var(--pst-color-active-navigation); --pst-color-sidebar-link-active: var(--pst-color-active-navigation); --pst-color-sidebar-expander-background-hover: 244, 244, 244; --pst-color-sidebar-caption: 77, 77, 77; --pst-color-toc-link: 119, 117, 122; --pst-color-toc-link-hover: var(--pst-color-active-navigation); --pst-color-toc-link-active: var(--pst-color-active-navigation); /***************************************************************************** * Icon **/ /* font awesome icons*/ --pst-icon-check-circle: '\f058'; --pst-icon-info-circle: '\f05a'; --pst-icon-exclamation-triangle: '\f071'; --pst-icon-exclamation-circle: '\f06a'; --pst-icon-times-circle: '\f057'; --pst-icon-lightbulb: '\f0eb'; /***************************************************************************** * Admonitions **/ --pst-color-admonition-default: var(--pst-color-info); --pst-color-admonition-note: var(--pst-color-info); --pst-color-admonition-attention: var(--pst-color-warning); --pst-color-admonition-caution: var(--pst-color-warning); --pst-color-admonition-warning: var(--pst-color-warning); --pst-color-admonition-danger: var(--pst-color-danger); --pst-color-admonition-error: var(--pst-color-danger); --pst-color-admonition-hint: var(--pst-color-success); --pst-color-admonition-tip: var(--pst-color-success); --pst-color-admonition-important: var(--pst-color-success); --pst-icon-admonition-default: var(--pst-icon-info-circle); --pst-icon-admonition-note: var(--pst-icon-info-circle); --pst-icon-admonition-attention: var(--pst-icon-exclamation-circle); --pst-icon-admonition-caution: var(--pst-icon-exclamation-triangle); --pst-icon-admonition-warning: var(--pst-icon-exclamation-triangle); --pst-icon-admonition-danger: var(--pst-icon-exclamation-triangle); --pst-icon-admonition-error: var(--pst-icon-times-circle); --pst-icon-admonition-hint: var(--pst-icon-lightbulb); --pst-icon-admonition-tip: var(--pst-icon-lightbulb); --pst-icon-admonition-important: var(--pst-icon-exclamation-circle); } c-blosc2-3.1.5/doc/c-blosc2.rst000066400000000000000000000002541521743436100160360ustar00rootroot00000000000000.. include:: ../README.rst Contents ======== .. toctree:: :maxdepth: 2 API Reference Format Development c-blosc2-3.1.5/doc/check_missing_docs.py000066400000000000000000000043661521743436100201000ustar00rootroot00000000000000#!/usr/bin/env python3 """Check that all public functions in blosc2.h and b2nd.h appear in the docs. Compares BLOSC_EXPORT / static inline declarations in the public headers against doxygenfunction directives in doc/reference/*.rst. Exit 0 if all are covered, 1 with a report otherwise. """ import re import sys from pathlib import Path ROOT = Path(__file__).resolve().parent.parent HEADERS = [ROOT / "include" / "blosc2.h", ROOT / "include" / "b2nd.h"] RST_DIR = ROOT / "doc" / "reference" # Functions that are deliberately excluded from the reference docs INTENTIONAL_SKIP = { # Context-free convenience wrappers; _ctx variants are documented "blosc2_compress", "blosc2_decompress", "blosc2_getitem", "blosc2_cbuffer_sizes", # Low-level inline helper "swap_store", } def parse_header_functions(path: Path) -> set[str]: """Return a set of public function names declared in the header.""" text = path.read_text() funcs: set[str] = set() for m in re.finditer( r"(?:BLOSC_EXPORT|static inline)\s+(?:const\s+)?\w+\s*\*?\s+(\w+)\s*\(", text, ): funcs.add(m.group(1)) return funcs def parse_rst_functions(rst_dir: Path) -> set[str]: """Return a set of function names referenced in the RST files.""" funcs: set[str] = set() for rst in sorted(rst_dir.glob("*.rst")): for line in rst.read_text().splitlines(): m = re.match(r"\.\.\s+doxygenfunction::\s+(\w+)", line) if m: funcs.add(m.group(1)) return funcs def main() -> int: header_funcs: set[str] = set() for h in HEADERS: header_funcs |= parse_header_functions(h) rst_funcs = parse_rst_functions(RST_DIR) missing = sorted(header_funcs - rst_funcs - INTENTIONAL_SKIP) if missing: print("Public functions missing from the reference docs:") for name in missing: print(f" {name}") print("\nAdd them to the appropriate .rst file under doc/reference/.") print( "If a function is intentionally omitted, add it to INTENTIONAL_SKIP" " in doc/check_missing_docs.py." ) return 1 print("All public functions are documented.") return 0 if __name__ == "__main__": sys.exit(main()) c-blosc2-3.1.5/doc/conf.py000066400000000000000000000026551521743436100152060ustar00rootroot00000000000000# Configuration file for the Sphinx documentation builder. # # This file only contains a selection of the most common options. For a full # list see the documentation: # https://www.sphinx-doc.org/en/master/usage/configuration.html import pathlib import subprocess subprocess.run('doxygen Doxyfile', shell=True, check=True) project = 'C-Blosc2' copyright = '2019-present, Blosc Development Team' author = 'Blosc Development Team' extensions = [ 'breathe', 'sphinx.ext.intersphinx', ] html_theme = "pydata_sphinx_theme" html_static_path = ['_static'] html_css_files = [ 'css/custom.css', ] html_logo = "_static/blosc-logo_256.png" html_theme_options = { "logo": { "link": "/index", "alt_text": "Blosc", }, "external_links": [ #{"name": "Python-Blosc", "url": "/python-blosc/python-blosc.html"}, {"name": "Python-Blosc2", "url": "/python-blosc2/python-blosc2.html"}, {"name": "Blosc In Depth", "url": "/pages/blosc-in-depth/"}, {"name": "Donate to Blosc", "url": "/pages/donate/"}, ], "github_url": "https://github.com/Blosc/c-blosc2", "twitter_url": "https://twitter.com/Blosc2", } html_show_sourcelink = False breathe_projects = { "blosc2": pathlib.Path(__file__).parent.resolve() / "xml/", } breathe_default_project = "blosc2" breathe_show_define_initializer = True breathe_order_parameters_first = True breathe_domain_by_extension = {"h": "c"} c-blosc2-3.1.5/doc/development/000077500000000000000000000000001521743436100162215ustar00rootroot00000000000000c-blosc2-3.1.5/doc/development/contributing.rst000066400000000000000000000000441521743436100214600ustar00rootroot00000000000000.. include:: ../../CONTRIBUTING.rst c-blosc2-3.1.5/doc/development/index.rst000066400000000000000000000002211521743436100200550ustar00rootroot00000000000000Contributing to c-blosc2 ======================== .. toctree:: :maxdepth: 1 :caption: Contents: contributing releasing roadmap c-blosc2-3.1.5/doc/development/releasing.rst000066400000000000000000000000411521743436100207170ustar00rootroot00000000000000.. include:: ../../RELEASING.rst c-blosc2-3.1.5/doc/development/roadmap.rst000066400000000000000000000000461521743436100203760ustar00rootroot00000000000000.. include:: ../../ROADMAP-TO-3.0.rst c-blosc2-3.1.5/doc/environment.yml000066400000000000000000000002161521743436100167650ustar00rootroot00000000000000iname: RTD channels: - conda-forge - defaults dependencies: - python=3.9 - doxygen=1.9 - breathe - sphinx - pydata-sphinx-theme c-blosc2-3.1.5/doc/format/000077500000000000000000000000001521743436100151675ustar00rootroot00000000000000c-blosc2-3.1.5/doc/format/b2nd_format.rst000066400000000000000000000000521521743436100201130ustar00rootroot00000000000000.. include:: ../../README_B2ND_FORMAT.rst c-blosc2-3.1.5/doc/format/b2nd_metalayer.rst000066400000000000000000000000551521743436100206110ustar00rootroot00000000000000.. include:: ../../README_B2ND_METALAYER.rst c-blosc2-3.1.5/doc/format/cframe_format.rst000066400000000000000000000000541521743436100205250ustar00rootroot00000000000000.. include:: ../../README_CFRAME_FORMAT.rst c-blosc2-3.1.5/doc/format/chunk_format.rst000066400000000000000000000000531521743436100203770ustar00rootroot00000000000000.. include:: ../../README_CHUNK_FORMAT.rst c-blosc2-3.1.5/doc/format/extension_filenames.rst000066400000000000000000000000621521743436100217560ustar00rootroot00000000000000.. include:: ../../README_EXTENSION_FILENAMES.rst c-blosc2-3.1.5/doc/format/index.rst000066400000000000000000000012761521743436100170360ustar00rootroot00000000000000 Blosc2 Format ============= .. toctree:: :maxdepth: 1 :caption: Contents: b2nd_format b2nd_metalayer sframe_format cframe_format chunk_format extension_filenames The Blosc2 format is a specification for storing compressed data in a way that is simple to read and parse, and that allows for fast random access to the compressed data. The format is designed to be used with the Blosc2 library, but it is not tied to it, and can be used independently. Emphasis has been put on simplicity and robustness, so that the format can be used in a wide range of applications. In this section there is a list of the different parts of the format, from the highest level to the lowest. c-blosc2-3.1.5/doc/format/sframe_format.rst000066400000000000000000000000541521743436100205450ustar00rootroot00000000000000.. include:: ../../README_SFRAME_FORMAT.rst c-blosc2-3.1.5/doc/index.rst000066400000000000000000000000611521743436100155350ustar00rootroot00000000000000.. toctree:: :hidden: C-Blosc2 c-blosc2-3.1.5/doc/reference/000077500000000000000000000000001521743436100156355ustar00rootroot00000000000000c-blosc2-3.1.5/doc/reference/b2nd.rst000066400000000000000000000050641521743436100172210ustar00rootroot00000000000000Blosc2 NDim =========== It contains both the data and metalayer that stores the dimensional info for the array. Blosc2 NDim has a managed internal context that stores the different properties of each array. Context ------- .. doxygentypedef:: b2nd_context_t Creation ++++++++ .. doxygenfunction:: b2nd_create_ctx Destruction +++++++++++ .. doxygenfunction:: b2nd_free_ctx Array ----- A Blosc2 NDim array is a n-dimensional object that can be managed by the associated functions. The functions let users to perform different operations with these arrays like copying, getting, setting or converting data into buffers or files and vice-versa. Furthermore, Blosc2 NDim only stores the type size (not the data type), and every item of an array has the same size. The `b2nd_array_t` type struct is where all data and metadata for an array is stored: .. doxygenstruct:: b2nd_array_t Creation ++++++++ Constructors ~~~~~~~~~~~~ .. doxygenfunction:: b2nd_uninit .. doxygenfunction:: b2nd_empty .. doxygenfunction:: b2nd_zeros .. doxygenfunction:: b2nd_nans .. doxygenfunction:: b2nd_full From/To buffer ~~~~~~~~~~~~~~ .. doxygenfunction:: b2nd_from_cbuffer .. doxygenfunction:: b2nd_to_cbuffer From/To file ~~~~~~~~~~~~ .. doxygenfunction:: b2nd_open .. doxygenfunction:: b2nd_open_offset .. doxygenfunction:: b2nd_save .. doxygenfunction:: b2nd_save_append From Blosc object ~~~~~~~~~~~~~~~~~ .. doxygenfunction:: b2nd_from_schunk .. doxygenfunction:: b2nd_from_cframe .. doxygenfunction:: b2nd_to_cframe Modify data ~~~~~~~~~~~ .. doxygenfunction:: b2nd_insert .. doxygenfunction:: b2nd_append .. doxygenfunction:: b2nd_delete Copying +++++++ .. doxygenfunction:: b2nd_copy Slicing +++++++ .. doxygenfunction:: b2nd_get_slice .. doxygenfunction:: b2nd_get_slice_cbuffer .. doxygenfunction:: b2nd_get_sparse_cbuffer .. doxygenfunction:: b2nd_set_slice_cbuffer .. doxygenfunction:: b2nd_get_orthogonal_selection .. doxygenfunction:: b2nd_set_orthogonal_selection .. doxygenfunction:: b2nd_squeeze .. doxygenfunction:: b2nd_squeeze_index Utils +++++ .. doxygenfunction:: b2nd_print_meta .. doxygenfunction:: b2nd_serialize_meta .. doxygenfunction:: b2nd_deserialize_meta .. doxygenfunction:: b2nd_deserialize_meta_inline .. doxygenfunction:: b2nd_resize Destruction +++++++++++ .. doxygenfunction:: b2nd_free Utilities --------- These functions may be used for working with plain C buffers representing multidimensional arrays. .. doxygenfunction:: b2nd_copy_buffer .. doxygenfunction:: b2nd_concatenate .. doxygenfunction:: b2nd_copy_buffer2 .. doxygenfunction:: b2nd_expand_dims c-blosc2-3.1.5/doc/reference/blosc1.rst000066400000000000000000000024741521743436100175610ustar00rootroot00000000000000Blosc1 API ========== This is the classic API from Blosc1 with 32-bit limited containers. Main API ++++++++ .. doxygenfunction:: blosc2_init .. doxygenfunction:: blosc2_destroy .. doxygenfunction:: blosc1_compress .. doxygenfunction:: blosc1_decompress .. doxygenfunction:: blosc1_getitem .. doxygenfunction:: blosc2_get_nthreads .. doxygenfunction:: blosc2_set_nthreads .. doxygentypedef:: blosc_threads_callback .. doxygenfunction:: blosc2_set_threads_callback .. doxygenfunction:: blosc1_get_compressor .. doxygenfunction:: blosc1_set_compressor .. doxygenfunction:: blosc2_set_delta .. doxygenfunction:: blosc1_get_blocksize .. doxygenfunction:: blosc1_set_blocksize .. doxygenfunction:: blosc1_set_splitmode .. doxygenfunction:: blosc2_free_resources Compressed buffer information +++++++++++++++++++++++++++++ .. doxygenfunction:: blosc1_cbuffer_sizes .. doxygenfunction:: blosc1_cbuffer_metainfo .. doxygenfunction:: blosc2_cbuffer_versions .. doxygenfunction:: blosc2_cbuffer_complib .. doxygenfunction:: blosc1_cbuffer_validate Utility functions +++++++++++++++++ .. doxygenfunction:: blosc2_compcode_to_compname .. doxygenfunction:: blosc2_compname_to_compcode .. doxygenfunction:: blosc2_list_compressors .. doxygenfunction:: blosc2_get_version_string .. doxygenfunction:: blosc2_get_complib_info c-blosc2-3.1.5/doc/reference/context.rst000066400000000000000000000022371521743436100200570ustar00rootroot00000000000000Context ======= In Blosc 2 there is a special `blosc2_context` struct that is created from compression and decompression parameters. This allows the compression and decompression to happen in multithreaded scenarios, without the need for using the global lock. .. .. doxygenstruct:: blosc2_cparams .. :members: .. .. doxygenvariable:: BLOSC2_CPARAMS_DEFAULTS .. doxygenstruct:: blosc2_dparams :members: .. doxygenvariable:: BLOSC2_DPARAMS_DEFAULTS .. doxygenfunction:: blosc2_create_cctx .. doxygenfunction:: blosc2_create_dctx .. doxygenfunction:: blosc2_free_ctx .. doxygenfunction:: blosc2_compress_ctx .. doxygenfunction:: blosc2_decompress_ctx .. doxygenfunction:: blosc2_vlcompress_ctx .. doxygenfunction:: blosc2_vldecompress_ctx .. doxygenfunction:: blosc2_vldecompress_block_ctx .. doxygenfunction:: blosc2_vlchunk_get_nblocks .. doxygenfunction:: blosc2_set_maskout .. doxygenfunction:: blosc2_getitem_ctx .. doxygenfunction:: blosc2_ctx_get_cparams .. doxygenfunction:: blosc2_ctx_get_dparams .. doxygenfunction:: blosc2_get_blosc2_cparams_defaults .. doxygenfunction:: blosc2_get_blosc2_dparams_defaults .. doxygenfunction:: blosc2_error_string c-blosc2-3.1.5/doc/reference/index.rst000066400000000000000000000013761521743436100175050ustar00rootroot00000000000000C-Blosc2 API ============ This section contains the C-Blosc2 public API and the structures needed to use it. C-Blosc2 tries to be backward compatible with both the C-Blosc1 API and format. Furthermore, if you just use the C-Blosc1 API you are guaranteed to generate compressed data containers that can be read with a Blosc1 library. Having said that, the C-Blosc2 API gives you much more functionality, like 64-bit data containers, more filters, more support for vector instructions, the ability to work with data either in-memory or on-disk (frames) or attach metainfo to your datasets (metalayers). .. toctree:: :maxdepth: 2 :caption: Contents: utility_variables utility_functions blosc1 context plugins schunk metalayers b2nd c-blosc2-3.1.5/doc/reference/metalayers.rst000066400000000000000000000012731521743436100205400ustar00rootroot00000000000000Metalayers ========== Metalayers are meta-information that can be attached to super-chunks. They can also be serialized to disk. .. doxygenstruct:: blosc2_metalayer :members: Fixed-length metalayers ----------------------- .. doxygenfunction:: blosc2_meta_exists .. doxygenfunction:: blosc2_meta_add .. doxygenfunction:: blosc2_meta_update .. doxygenfunction:: blosc2_meta_get Variable-length metalayers -------------------------- .. doxygenfunction:: blosc2_vlmeta_exists .. doxygenfunction:: blosc2_vlmeta_add .. doxygenfunction:: blosc2_vlmeta_update .. doxygenfunction:: blosc2_vlmeta_get .. doxygenfunction:: blosc2_vlmeta_delete .. doxygenfunction:: blosc2_vlmeta_get_names c-blosc2-3.1.5/doc/reference/plugins.rst000066400000000000000000000016011521743436100200460ustar00rootroot00000000000000Plugins ======= Filters ------- .. doxygentypedef:: blosc2_filter_forward_cb .. doxygentypedef:: blosc2_filter_backward_cb .. doxygenstruct:: blosc2_filter :members: .. doxygenfunction:: blosc2_register_filter Codecs ------ .. doxygentypedef:: blosc2_codec_encoder_cb .. doxygentypedef:: blosc2_codec_decoder_cb .. doxygenstruct:: blosc2_codec :members: .. doxygenfunction:: blosc2_register_codec Tuners ------ .. doxygenstruct:: blosc2_tuner :members: .. doxygenfunction:: blosc2_register_tuner IO backends ----------- .. doxygentypedef:: blosc2_open_cb .. doxygentypedef:: blosc2_close_cb .. doxygentypedef:: blosc2_write_cb .. doxygentypedef:: blosc2_read_cb .. doxygentypedef:: blosc2_truncate_cb .. doxygenstruct:: blosc2_io_cb :members: .. doxygenstruct:: blosc2_io :members: .. doxygenfunction:: blosc2_register_io_cb .. doxygenfunction:: blosc2_get_io_cb c-blosc2-3.1.5/doc/reference/schunk.rst000066400000000000000000000040721521743436100176650ustar00rootroot00000000000000 Super-chunk +++++++++++ This API describes the new Blosc 2 container, the super-chunk (or `schunk` for short). .. doxygenstruct:: blosc2_storage :members: .. doxygenstruct:: blosc2_schunk :project: blosc2 :members: .. doxygenfunction:: blosc2_schunk_new .. doxygenfunction:: blosc2_schunk_free .. doxygenfunction:: blosc2_schunk_open .. doxygenfunction:: blosc2_schunk_open_offset .. doxygenfunction:: blosc2_schunk_open_udio .. doxygenfunction:: blosc2_schunk_open_offset_udio .. doxygenfunction:: blosc2_schunk_copy .. doxygenfunction:: blosc2_schunk_from_buffer .. doxygenfunction:: blosc2_schunk_to_buffer .. doxygenfunction:: blosc2_schunk_to_file .. doxygenfunction:: blosc2_schunk_append_file .. doxygenfunction:: blosc2_schunk_get_cparams .. doxygenfunction:: blosc2_schunk_get_dparams .. doxygenfunction:: blosc2_schunk_reorder_offsets .. doxygenfunction:: blosc2_schunk_frame_len .. doxygenfunction:: blosc2_schunk_fill_special .. doxygenfunction:: blosc2_schunk_append_buffer .. doxygenfunction:: blosc2_schunk_get_slice_buffer .. doxygenfunction:: blosc2_schunk_get_sparse_buffer .. doxygenfunction:: blosc2_schunk_set_slice_buffer .. doxygenfunction:: blosc2_schunk_avoid_cframe_free .. doxygenfunction:: blosc2_get_blosc2_storage_defaults .. doxygenfunction:: blosc2_get_blosc2_io_defaults .. doxygenfunction:: blosc2_get_blosc2_stdio_mmap_defaults Dealing with chunks ------------------- .. doxygenfunction:: blosc2_schunk_get_chunk .. doxygenfunction:: blosc2_schunk_get_lazychunk .. doxygenfunction:: blosc2_schunk_get_vlblock .. doxygenfunction:: blosc2_schunk_decompress_chunk .. doxygenfunction:: blosc2_schunk_append_chunk .. doxygenfunction:: blosc2_schunk_insert_chunk .. doxygenfunction:: blosc2_schunk_update_chunk .. doxygenfunction:: blosc2_schunk_delete_chunk Creating chunks --------------- .. doxygenfunction:: blosc2_chunk_zeros .. doxygenfunction:: blosc2_chunk_nans .. doxygenfunction:: blosc2_chunk_repeatval .. doxygenfunction:: blosc2_chunk_uninit Frame specific functions ------------------------ .. doxygenfunction:: blosc2_frame_get_offsets c-blosc2-3.1.5/doc/reference/utility_functions.rst000066400000000000000000000013741521743436100221670ustar00rootroot00000000000000Utility functions +++++++++++++++++ Timing functions ---------------- .. doxygenfunction:: blosc_set_timestamp .. doxygenfunction:: blosc_elapsed_nsecs .. doxygenfunction:: blosc_elapsed_secs File and directory utilities ---------------------------- .. doxygenfunction:: blosc2_remove_dir .. doxygenfunction:: blosc2_remove_urlpath .. doxygenfunction:: blosc2_rename_urlpath Slice utilities --------------- .. doxygenfunction:: blosc2_get_slice_nchunks .. doxygenfunction:: blosc2_unidim_to_multidim .. doxygenfunction:: blosc2_multidim_to_unidim Shuffle and bitshuffle ---------------------- .. doxygenfunction:: blosc2_shuffle .. doxygenfunction:: blosc2_unshuffle .. doxygenfunction:: blosc2_bitshuffle .. doxygenfunction:: blosc2_bitunshuffle c-blosc2-3.1.5/doc/reference/utility_variables.rst000066400000000000000000000035561521743436100221330ustar00rootroot00000000000000Utility variables +++++++++++++++++ This are enum values which avoid the nuisance of remembering codes and IDs. Limits for different features ----------------------------- .. doxygenenumvalue:: BLOSC_MIN_HEADER_LENGTH .. doxygenenumvalue:: BLOSC_EXTENDED_HEADER_LENGTH .. doxygenenumvalue:: BLOSC2_MAX_OVERHEAD .. doxygenenumvalue:: BLOSC_MIN_BUFFERSIZE .. doxygenenumvalue:: BLOSC2_MAX_BUFFERSIZE .. doxygenenumvalue:: BLOSC_MAX_TYPESIZE .. doxygenenumvalue:: BLOSC2_MAX_FILTERS Codes for filters ----------------- .. doxygenenumvalue:: BLOSC_NOSHUFFLE .. doxygenenumvalue:: BLOSC_NOFILTER .. doxygenenumvalue:: BLOSC_SHUFFLE .. doxygenenumvalue:: BLOSC_BITSHUFFLE .. doxygenenumvalue:: BLOSC_DELTA .. doxygenenumvalue:: BLOSC_TRUNC_PREC .. doxygenenumvalue:: BLOSC_FILTER_NDCELL .. doxygenenumvalue:: BLOSC_FILTER_NDMEAN .. doxygenenumvalue:: BLOSC_FILTER_BYTEDELTA .. doxygenenumvalue:: BLOSC_FILTER_INT_TRUNC Compressor codecs ----------------- .. doxygenenumvalue:: BLOSC_BLOSCLZ .. doxygenenumvalue:: BLOSC_LZ4 .. doxygenenumvalue:: BLOSC_LZ4HC .. doxygenenumvalue:: BLOSC_ZLIB .. doxygenenumvalue:: BLOSC_ZSTD .. doxygenenumvalue:: BLOSC_CODEC_NDLZ .. doxygenenumvalue:: BLOSC_CODEC_ZFP_FIXED_ACCURACY .. doxygenenumvalue:: BLOSC_CODEC_ZFP_FIXED_PRECISION .. doxygenenumvalue:: BLOSC_CODEC_ZFP_FIXED_RATE .. doxygenenumvalue:: BLOSC_CODEC_OPENHTJ2K .. doxygenenumvalue:: BLOSC_CODEC_GROK Compressor names ---------------- .. doxygendefine:: BLOSC_BLOSCLZ_COMPNAME .. doxygendefine:: BLOSC_LZ4_COMPNAME .. doxygendefine:: BLOSC_LZ4HC_COMPNAME .. doxygendefine:: BLOSC_ZLIB_COMPNAME .. doxygendefine:: BLOSC_ZSTD_COMPNAME Internal flags (blosc1_cbuffer_metainfo) ---------------------------------------- .. doxygenenumvalue:: BLOSC_DOSHUFFLE .. doxygenenumvalue:: BLOSC_MEMCPYED .. doxygenenumvalue:: BLOSC_DOBITSHUFFLE .. doxygenenumvalue:: BLOSC_DODELTA c-blosc2-3.1.5/doc/requirements.txt000066400000000000000000000000431521743436100171600ustar00rootroot00000000000000breathe sphinx pydata-sphinx-theme c-blosc2-3.1.5/examples/000077500000000000000000000000001521743436100147505ustar00rootroot00000000000000c-blosc2-3.1.5/examples/CMakeLists.txt000066400000000000000000000043701521743436100175140ustar00rootroot00000000000000# Examples with correspondingly named source files set(EXAMPLES contexts instrument_codec delta_schunk_ex multithread simple frame_metalayers noinit find_roots schunk_simple frame_simple schunk_postfilter urcodecs urfilters frame_vlmetalayers sframe_simple frame_backed_schunk compress_file decompress_file frame_offset frame_roundtrip get_set_slice get_blocksize vlblocks) add_subdirectory(b2nd) if(NOT DEACTIVATE_ZSTD) set(EXAMPLES ${EXAMPLES} zstd_dict) endif() if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8") # frame_big uses containers of more than 4 GB and cannot run on 32-bit platforms list(APPEND EXAMPLES frame_big) endif() # targets foreach(example ${EXAMPLES}) add_executable(${example} ${example}.c) # Define the BLOSC_TESTING symbol so normally-hidden functions # aren't hidden from the view of the test programs. set_property( TARGET ${example} APPEND PROPERTY COMPILE_DEFINITIONS BLOSC_TESTING) endforeach() if(UNIX AND NOT APPLE) # cmake is complaining about LINK_PRIVATE in original PR # and removing it does not seem to hurt, so be it. # target_link_libraries(bench LINK_PRIVATE rt) foreach(example ${EXAMPLES}) target_link_libraries(${example} rt) endforeach() endif() foreach(example ${EXAMPLES}) target_link_libraries(${example} blosc_testing) endforeach() # tests if(BUILD_TESTS) option(TEST_INCLUDE_EXAMPLES "Include examples in the tests" ON) if(TEST_INCLUDE_EXAMPLES) foreach(example ${EXAMPLES}) if(example STREQUAL compress_file) add_test(NAME test_example_${example} COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ "${PROJECT_BINARY_DIR}/CMakeCache.txt" CMakeCache.b2frame) elseif(example STREQUAL decompress_file) add_test(NAME test_example_${example} COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $ CMakeCache.b2frame CMakeCache-2.txt) else() add_test(NAME test_example_${example} COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $) endif() endforeach() endif() endif() c-blosc2-3.1.5/examples/README.rst000066400000000000000000000011531521743436100164370ustar00rootroot00000000000000Examples on how to add Blosc support for your programs ====================================================== In this directory you can find a series of examples on how to link your apps with the Blosc library. A few of them are: * simple.c -- The simplest way to add Blosc to your app * schunk_simple.c -- Adding the more powerful super-chunk into the equation * frame_simple.c -- Use a frame to serialize Blosc2 super-chunks * compress_file.c -- Compress a file into a Blosc2 file-frame For more info, please visit the `official API documentation `_. c-blosc2-3.1.5/examples/b2nd/000077500000000000000000000000001521743436100155755ustar00rootroot00000000000000c-blosc2-3.1.5/examples/b2nd/CMakeLists.txt000066400000000000000000000017761521743436100203500ustar00rootroot00000000000000# Blosc - Blocked Shuffling and Compression Library # # Copyright (c) 2021 Blosc Development Team # https://blosc.org # License: BSD 3-Clause (see LICENSE.txt) # # See LICENSE.txt for details about copyright and rights to use. file(GLOB SOURCES example_*.c) list(REMOVE_ITEM SOURCES example_print_meta.c) if (NOT HAVE_PLUGINS) file(GLOB NEED_PLUGINS example_plugins_*.c) list(REMOVE_ITEM SOURCES ${NEED_PLUGINS}) endif () foreach (source ${SOURCES}) get_filename_component(target_name ${source} NAME_WE) set(target b2nd_${target_name}) add_executable(${target} ${target_name}.c) target_link_libraries(${target} blosc_testing ${LIBS}) if (${target_name} STREQUAL example_print_meta) add_test(NAME ${target} COMMAND ${target} "${PROJECT_BINARY_DIR}/examples/b2nd/example_big_float_frame.b2nd") else () add_test(NAME ${target} COMMAND ${target}) endif () set_tests_properties(${target} PROPERTIES LABELS "b2nd") endforeach (source) c-blosc2-3.1.5/examples/b2nd/example_empty_shape.c000066400000000000000000000044421521743436100217760ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ # include int main() { blosc2_init(); int8_t ndim = 2; int64_t shape[] = {10, 10}; int32_t chunkshape[] = {4, 4}; int32_t blockshape[] = {2, 2}; int32_t typesize = 8; int64_t slice_start[] = {2, 5}; int64_t slice_stop[] = {2, 6}; int32_t slice_chunkshape[] = {0, 1}; int32_t slice_blockshape[] = {0, 1}; int64_t nelem = 1; for (int i = 0; i < ndim; ++i) { nelem *= shape[i]; } int64_t size = nelem * typesize; int8_t *data = calloc((size_t) size, sizeof(int8_t)); blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = typesize; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; blosc2_storage b2_storage = {.cparams=&cparams, .dparams=&dparams}; b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, ndim, shape, chunkshape, blockshape, NULL, 0, NULL, 0); b2nd_array_t *arr; BLOSC_ERROR(b2nd_from_cbuffer(ctx, &arr, data, size)); BLOSC_ERROR(b2nd_free_ctx(ctx)); blosc2_storage slice_b2_storage = {.cparams=&cparams, .dparams=&dparams}; slice_b2_storage.urlpath = "example_hola.b2frame"; blosc2_remove_urlpath(slice_b2_storage.urlpath); b2nd_context_t *slice_ctx = b2nd_create_ctx(&slice_b2_storage, ndim, shape, slice_chunkshape, slice_blockshape, NULL, 0, NULL, 0); b2nd_array_t *slice; BLOSC_ERROR(b2nd_get_slice(slice_ctx, &slice, arr, slice_start, slice_stop)); BLOSC_ERROR(b2nd_free(arr)); BLOSC_ERROR(b2nd_free_ctx(slice_ctx)); uint8_t *buffer; uint64_t buffer_size = 1; for (int i = 0; i < slice->ndim; ++i) { buffer_size *= slice->shape[i]; } buffer_size *= slice->sc->typesize; buffer = malloc(buffer_size); BLOSC_ERROR(b2nd_to_cbuffer(slice, buffer, buffer_size)); BLOSC_ERROR(b2nd_free(slice)); free(data); free(buffer); // printf("Elapsed seconds: %.5f\n", blosc_elapsed_secs(t0, t1)); blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/b2nd/example_frame_generator.c000066400000000000000000000243721521743436100226240ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ # include # include # include "time.h" int frame_generator(int8_t *data, int8_t ndim, int64_t *shape, int32_t *chunkshape, int32_t *blockshape, int32_t typesize, int64_t size, char *urlpath) { blosc2_remove_urlpath(urlpath); blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = typesize; blosc2_storage b2_storage = {.cparams=&cparams}; b2_storage.urlpath = urlpath; b2_storage.contiguous = true; b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, ndim, shape, chunkshape, blockshape, NULL, 0, NULL, 0); b2nd_array_t *arr; BLOSC_ERROR(b2nd_from_cbuffer(ctx, &arr, data, size)); BLOSC_ERROR(b2nd_free_ctx(ctx)); b2nd_print_meta(arr); BLOSC_ERROR(b2nd_free(arr)); return 0; } int rand_() { int ndim = 3; int typesize = 4; int64_t shape[] = {32, 18, 32}; int32_t chunkshape[] = {17, 16, 24}; int32_t blockshape[] = {8, 9, 8}; int64_t nelem = 1; for (int i = 0; i < ndim; ++i) { nelem *= (int) (shape[i]); } int64_t size = typesize * nelem; float *data = calloc((size_t) nelem, sizeof(float)); for (int64_t i = 0; i < nelem; i++) { data[i] = (float) (rand() % 220); } char *urlpath = "rand.b2nd"; int rc = frame_generator((int8_t *) data, ndim, shape, chunkshape, blockshape, typesize, size, urlpath); free(data); BLOSC_ERROR(rc); return 0; } int all_eq() { int8_t ndim = 3; int64_t shape[] = {100, 50, 100}; int32_t chunkshape[] = {40, 20, 60}; int32_t blockshape[] = {20, 10, 30}; int32_t typesize = 8; int64_t nelem = 1; for (int i = 0; i < ndim; ++i) { nelem *= shape[i]; } int64_t size = nelem * typesize; int8_t *data = calloc((size_t) size, sizeof(int8_t)); for (int i = 0; i < nelem; i++) { data[i] = (int8_t) 22; } char *urlpath = "all_eq.b2nd"; int rc = frame_generator(data, ndim, shape, chunkshape, blockshape, typesize, size, urlpath); free(data); BLOSC_ERROR(rc); return 0; } int cyclic() { int8_t ndim = 3; int64_t shape[] = {100, 50, 100}; int32_t chunkshape[] = {40, 20, 60}; int32_t blockshape[] = {20, 10, 30}; int32_t typesize = 8; int64_t nelem = 1; for (int i = 0; i < ndim; ++i) { nelem *= shape[i]; } int64_t size = nelem * typesize; int8_t *data = calloc((size_t) size, sizeof(int8_t)); for (int i = 0; i < nelem; i++) { data[i] = (int8_t) i; } char *urlpath = "cyclic.b2nd"; int rc = frame_generator(data, ndim, shape, chunkshape, blockshape, typesize, size, urlpath); free(data); BLOSC_ERROR(rc); return 0; } int same_cells() { int ndim = 2; int typesize = 8; int64_t shape[] = {128, 111}; int32_t chunkshape[] = {32, 11}; int32_t blockshape[] = {16, 7}; int64_t nelem = 1; for (int i = 0; i < ndim; ++i) { nelem *= (int) (shape[i]); } int64_t size = typesize * nelem; double *data = calloc((size_t) nelem, sizeof(double)); for (int64_t i = 0; i < (nelem / 4); i++) { data[i * 4] = (double) 11111111; data[i * 4 + 1] = (double) 99999999; } char *urlpath = "same_cells.b2nd"; int rc = frame_generator((int8_t *) data, ndim, shape, chunkshape, blockshape, typesize, size, urlpath); free(data); BLOSC_ERROR(rc); return 0; } int some_matches() { int ndim = 2; int typesize = 8; int64_t shape[] = {128, 111}; int32_t chunkshape[] = {48, 32}; int32_t blockshape[] = {14, 18}; int64_t nelem = 1; for (int i = 0; i < ndim; ++i) { nelem *= (int) (shape[i]); } int64_t size = typesize * nelem; double *data = calloc((size_t) nelem, sizeof(double)); for (int64_t i = 0; i < (nelem / 2); i++) { data[i] = (double) i; } for (int64_t i = (nelem / 2); i < nelem; i++) { data[i] = (double) 1; } char *urlpath = "some_matches.b2nd"; int rc = frame_generator((int8_t *) data, ndim, shape, chunkshape, blockshape, typesize, size, urlpath); free(data); BLOSC_ERROR(rc); return 0; } int many_matches() { int8_t ndim = 3; int64_t shape[] = {80, 120, 111}; int32_t chunkshape[] = {40, 30, 50}; int32_t blockshape[] = {11, 14, 24}; int32_t typesize = 8; int64_t nelem = 1; for (int i = 0; i < ndim; ++i) { nelem *= shape[i]; } int64_t size = nelem * typesize; int8_t *data = calloc((size_t) size, sizeof(int8_t)); for (int i = 0; i < nelem; i += 2) { data[i] = (int8_t) i; data[i + 1] = (int8_t) 2; } char *urlpath = "many_matches.b2nd"; int rc = frame_generator(data, ndim, shape, chunkshape, blockshape, typesize, size, urlpath); free(data); BLOSC_ERROR(rc); return 0; } int float_cyclic() { int8_t ndim = 3; int64_t shape[] = {40, 60, 20}; int32_t chunkshape[] = {20, 30, 16}; int32_t blockshape[] = {11, 14, 7}; int32_t typesize = sizeof(float); int64_t nelem = 1; for (int i = 0; i < ndim; ++i) { nelem *= shape[i]; } int64_t size = nelem * typesize; float *data = calloc((size_t) nelem, sizeof(float)); for (int i = 0; i < nelem; i += 2) { float j = (float) i; data[i] = (j + j / 10 + j / 100); data[i + 1] = (2 + j / 10 + j / 1000); } char *urlpath = "example_float_cyclic.b2nd"; int rc = frame_generator((int8_t *) data, ndim, shape, chunkshape, blockshape, typesize, size, urlpath); free(data); BLOSC_ERROR(rc); return 0; } int double_same_cells() { int8_t ndim = 2; int64_t shape[] = {40, 60}; int32_t chunkshape[] = {20, 30}; int32_t blockshape[] = {16, 16}; int32_t typesize = sizeof(double); int64_t nelem = 1; for (int i = 0; i < ndim; ++i) { nelem *= shape[i]; } int64_t size = nelem * typesize; double *data = calloc((size_t) nelem, sizeof(double)); for (int i = 0; i < nelem; i += 4) { data[i] = 1.5; data[i + 1] = 14.7; data[i + 2] = 23.6; data[i + 3] = 3.2; } char *urlpath = "example_double_same_cells.b2nd"; int rc = frame_generator((int8_t *) data, ndim, shape, chunkshape, blockshape, typesize, size, urlpath); free(data); BLOSC_ERROR(rc); return 0; } int big_float_frame() { int ndim = 3; int64_t shape[] = {200, 310, 214}; int32_t chunkshape[] = {110, 120, 76}; int32_t blockshape[] = {57, 52, 35}; int32_t typesize = sizeof(float); int64_t nelem = 1; for (int i = 0; i < ndim; ++i) { nelem *= shape[i]; } int64_t size = nelem * typesize; float *data = calloc((size_t) nelem, sizeof(float)); for (int i = 0; i < nelem; i += 4) { float j = (float) i; data[i] = (float) 2.73; data[i + 1] = (2 + j / 10 + j / 1000); data[i + 2] = (7 + j / 10 - j / 100); data[i + 3] = (11 + j / 100 - j / 1000); } char *urlpath = "example_big_float_frame.b2nd"; int rc = frame_generator((int8_t *) data, ndim, shape, chunkshape, blockshape, typesize, size, urlpath); free(data); BLOSC_ERROR(rc); return 0; } int day_month_temp() { int ndim = 2; int64_t shape[] = {400, 3}; int32_t chunkshape[] = {110, 3}; int32_t blockshape[] = {57, 3}; int32_t typesize = sizeof(float); int64_t nelem = 1; for (int i = 0; i < ndim; ++i) { nelem *= shape[i]; } int64_t size = nelem * typesize; float temp_min = -20; float temp_max = 40; srand(time(NULL)); float *data = calloc((size_t) nelem, sizeof(float)); for (int64_t i = 0; i + 2 < nelem; i += 3) { data[i] = (float) (rand() % 31); data[i + 1] = (float) (rand() % 12); data[i + 2] = ((float) (rand() % 10000) / 10000 * (temp_max - temp_min) + temp_min); } char *urlpath = "example_day_month_temp.b2nd"; int rc = frame_generator((int8_t *) data, ndim, shape, chunkshape, blockshape, typesize, size, urlpath); free(data); BLOSC_ERROR(rc); return 0; } int item_prices() { int ndim = 3; int64_t shape[] = {12, 25, 250}; int32_t chunkshape[] = {8, 10, 50}; int32_t blockshape[] = {4, 5, 10}; int32_t typesize = sizeof(float); int64_t nelem = 1; for (int i = 0; i < ndim; ++i) { nelem *= shape[i]; } int64_t size = nelem * typesize; float price_min = (float) 1; // if I choose 0.99 results are less aproppiate float price_max = (float) 251; float *data = calloc((size_t) nelem, sizeof(float)); int index = 0; for (int month = 1; month <= shape[0]; month++) { // month (1 to 12) for (int store = 1; store <= shape[1]; store++) { // store ID (less to more expensive) for (int item = 1; item <= shape[2]; item++) { // item ID srand(item); data[index] = ((float) store + (float) (3 - (month % 3)) * ((float) (rand() % 1000) / 1000 * (price_max - price_min) + price_min)); index++; } } } char *urlpath = "example_item_prices.b2nd"; int rc = frame_generator((int8_t *) data, ndim, shape, chunkshape, blockshape, typesize, size, urlpath); free(data); BLOSC_ERROR(rc); return 0; } int main() { blosc2_init(); int err; err = rand_(); if (err != BLOSC2_ERROR_SUCCESS) { printf("\n Rand_ error: %d", err); } err = all_eq(); if (err != BLOSC2_ERROR_SUCCESS) { printf("\n All_eq error: %d", err); } err = cyclic(); if (err != BLOSC2_ERROR_SUCCESS) { printf("\n Cyclic error: %d", err); } err = same_cells(); if (err != BLOSC2_ERROR_SUCCESS) { printf("\n Same_cells error: %d", err); } err = some_matches(); if (err != BLOSC2_ERROR_SUCCESS) { printf("\n Some_matches error: %d", err); } err = many_matches(); if (err != BLOSC2_ERROR_SUCCESS) { printf("\n Many_matches error: %d", err); } err = float_cyclic(); if (err != BLOSC2_ERROR_SUCCESS) { printf("\n Float_cyclic error: %d", err); } err = double_same_cells(); if (err != BLOSC2_ERROR_SUCCESS) { printf("\n Double_same_cells error: %d", err); } err = big_float_frame(); if (err != BLOSC2_ERROR_SUCCESS) { printf("\n Double_same_cells error: %d", err); } err = day_month_temp(); if (err != BLOSC2_ERROR_SUCCESS) { printf("\n Day_month_temp error: %d", err); } err = item_prices(); if (err != BLOSC2_ERROR_SUCCESS) { printf("\n Item_prices error: %d", err); } blosc2_destroy(); return err; } c-blosc2-3.1.5/examples/b2nd/example_oindex.c000066400000000000000000000043201521743436100207410ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #include int main() { blosc2_init(); int8_t ndim = 2; int64_t shape[] = {10, 10}; int32_t chunkshape[] = {4, 4}; int32_t blockshape[] = {2, 2}; int32_t typesize = 8; blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = typesize; blosc2_storage b2_storage = {.cparams=&cparams}; b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, ndim, shape, chunkshape, blockshape, NULL, 0, NULL, 0); int64_t dataitems = 1; for (int i = 0; i < ndim; ++i) { dataitems *= shape[i]; } int64_t datasize = dataitems * typesize; double *data = malloc(datasize); for (int i = 0; i < dataitems; ++i) { data[i] = (double) i; } b2nd_array_t *arr; BLOSC_ERROR(b2nd_from_cbuffer(ctx, &arr, data, datasize)); free(data); int64_t sel0[] = {3, 1, 2}; int64_t sel1[] = {2, 5}; int64_t sel2[] = {3, 3, 3, 9, 3, 1, 0}; int64_t *selection[] = {sel0, sel1, sel2}; int64_t selection_size[] = {sizeof(sel0) / sizeof(int64_t), sizeof(sel1) / (sizeof(int64_t)), sizeof(sel2) / (sizeof(int64_t))}; int64_t *buffershape = selection_size; int64_t nitems = 1; for (int i = 0; i < ndim; ++i) { nitems *= buffershape[i]; } int64_t buffersize = nitems * arr->sc->typesize; double *buffer = calloc(nitems, arr->sc->typesize); BLOSC_ERROR(b2nd_set_orthogonal_selection(arr, selection, selection_size, buffer, buffershape, buffersize)); BLOSC_ERROR(b2nd_get_orthogonal_selection(arr, selection, selection_size, buffer, buffershape, buffersize)); printf("Results: \n"); for (int i = 0; i < nitems; ++i) { if (i % buffershape[1] == 0) { printf("\n"); } printf(" %f ", buffer[i]); } printf("\n"); free(buffer); BLOSC_ERROR(b2nd_free(arr)); BLOSC_ERROR(b2nd_free_ctx(ctx)); blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/b2nd/example_plainbuffer.c000066400000000000000000000046621521743436100217610ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #include int main() { blosc2_init(); int8_t ndim = 2; int64_t shape[] = {10, 10}; int32_t chunkshape[] = {4, 4}; int32_t blockshape[] = {2, 2}; int32_t typesize = 8; int64_t slice_start[] = {2, 5}; int64_t slice_stop[] = {3, 6}; int32_t slice_chunkshape[] = {1, 1}; int32_t slice_blockshape[] = {1, 1}; int64_t nelem = 1; for (int i = 0; i < ndim; ++i) { nelem *= shape[i]; } int64_t size = nelem * typesize; int8_t *data = calloc((size_t) size, sizeof(int8_t)); blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = typesize; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; dparams.nthreads = 2; blosc2_storage b2_storage = {.cparams=&cparams, .dparams=&dparams}; b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, ndim, shape, chunkshape, blockshape, NULL, 0, NULL, 0); b2nd_array_t *arr; BLOSC_ERROR(b2nd_from_cbuffer(ctx, &arr, data, size)); blosc2_storage slice_b2_storage = {.cparams=&cparams, .dparams=&dparams}; // shape will be overwritten by get_slice b2nd_context_t *slice_ctx = b2nd_create_ctx(&slice_b2_storage, ndim, shape, slice_chunkshape, slice_blockshape, NULL, 0, NULL, 0); b2nd_array_t *slice; BLOSC_ERROR(b2nd_get_slice(slice_ctx, &slice, arr, slice_start, slice_stop)); b2nd_array_t *slice_view; BLOSC_ERROR(b2nd_squeeze(slice, &slice_view)); uint8_t *buffer; uint64_t buffer_size = 1; for (int i = 0; i < slice_view->ndim; ++i) { buffer_size *= slice_view->shape[i]; } buffer_size *= slice_view->sc->typesize; buffer = malloc(buffer_size); BLOSC_ERROR(b2nd_to_cbuffer(slice_view, buffer, buffer_size)); BLOSC_ERROR(b2nd_free(arr)); BLOSC_ERROR(b2nd_free(slice_view)); BLOSC_ERROR(b2nd_free(slice)); BLOSC_ERROR(b2nd_free_ctx(ctx)); BLOSC_ERROR(b2nd_free_ctx(slice_ctx)); free(buffer); free(data); blosc2_destroy(); // printf("Elapsed seconds: %.5f\n", blosc_elapsed_secs(t0, t1)); return 0; } c-blosc2-3.1.5/examples/b2nd/example_plugins_codecs.c000066400000000000000000000054751521743436100224700ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /* Example program demonstrating use of the Blosc plugins from C code. * * To compile this program: * $ gcc example_plugins_codecs.c -o example_plugins_codecs -lblosc2 * * To run: * $ ./example_plugins_codecs * * from_buffer: 0.0668 s * to_buffer: 0.0068 s * Process finished with exit code 0 */ #include #include #include #include "../../plugins/codecs/codecs-registry.c" #include int main() { blosc_timestamp_t t0, t1; blosc2_init(); int8_t ndim = 2; uint8_t typesize = sizeof(int64_t); int64_t shape[] = {745, 400}; int32_t chunkshape[] = {150, 100}; int32_t blockshape[] = {21, 30}; int64_t nbytes = typesize; for (int i = 0; i < ndim; ++i) { nbytes *= shape[i]; } int64_t *src = malloc((size_t) nbytes); for (int i = 0; i < nbytes / typesize; ++i) { src[i] = (int64_t) i; } blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.nthreads = 1; /* * Use the NDLZ codec through its plugin. * NDLZ metainformation: - it calls the 4x4 version if meta == 4 - it calls the 8x8 version if meta == 8 */ cparams.compcode = BLOSC_CODEC_NDLZ; cparams.splitmode = BLOSC_ALWAYS_SPLIT; cparams.compcode_meta = 4; cparams.clevel = 5; cparams.typesize = typesize; // We could use a filter plugin by setting cparams.filters[]. blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; blosc2_storage b2_storage = {.cparams=&cparams, .dparams=&dparams}; b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, ndim, shape, chunkshape, blockshape, NULL, 0, NULL, 0); BLOSC_ERROR_NULL(ctx, -1); b2nd_array_t *arr; blosc_set_timestamp(&t0); BLOSC_ERROR(b2nd_from_cbuffer(ctx, &arr, src, nbytes)); blosc_set_timestamp(&t1); printf("from_buffer: %.4f s\n", blosc_elapsed_secs(t0, t1)); int64_t *buffer = malloc(nbytes); int64_t buffer_size = nbytes; blosc_set_timestamp(&t0); BLOSC_ERROR(b2nd_to_cbuffer(arr, buffer, buffer_size)); blosc_set_timestamp(&t1); printf("to_buffer: %.4f s\n", blosc_elapsed_secs(t0, t1)); for (int i = 0; i < buffer_size / typesize; i++) { if (src[i] != buffer[i]) { printf("\n Decompressed data differs from original!\n"); printf("i: %d, data %" PRId64 ", dest %" PRId64 "", i, src[i], buffer[i]); return -1; } } free(src); free(buffer); BLOSC_ERROR(b2nd_free(arr)); BLOSC_ERROR(b2nd_free_ctx(ctx)); blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/b2nd/example_plugins_filters.c000066400000000000000000000054711521743436100226740ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /* Example program demonstrating use of the Blosc plugins from C code. * * To compile this program: * $ gcc example_plugins_filters.c -o example_plugins_filters -lblosc2 * * To run: * $ ./example_plugins_filters * * from_buffer: 0.0668 s * to_buffer: 0.0068 s * Process finished with exit code 0 */ #include #include #include #include "../../plugins/filters/filters-registry.c" #include int main() { blosc_timestamp_t t0, t1; blosc2_init(); int8_t ndim = 3; int32_t typesize = sizeof(int64_t); int64_t shape[] = {345, 200, 50}; int32_t chunkshape[] = {150, 100, 50}; int32_t blockshape[] = {21, 30, 27}; int64_t nbytes = typesize; for (int i = 0; i < ndim; ++i) { nbytes *= shape[i]; } int64_t *src = malloc((size_t) nbytes); for (int i = 0; i < nbytes / typesize; ++i) { src[i] = (int64_t) i; } blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.nthreads = 1; /* * Use the NDCELL filter through its plugin. * NDCELL metainformation: user must specify the parameter meta as the cellshape, so * if in a 3-dim dataset user specifies meta = 4, then cellshape will be 4x4x4. */ cparams.filters[4] = BLOSC_FILTER_NDCELL; cparams.filters_meta[4] = 4; cparams.typesize = typesize; // We could use a codec plugin by setting cparams.compcodec. blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; blosc2_storage b2_storage = {.cparams=&cparams, .dparams=&dparams}; b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, ndim, shape, chunkshape, blockshape, NULL, 0, NULL, 0); BLOSC_ERROR_NULL(ctx, -1); blosc_set_timestamp(&t0); b2nd_array_t *arr; BLOSC_ERROR(b2nd_from_cbuffer(ctx, &arr, src, nbytes)); blosc_set_timestamp(&t1); printf("from_buffer: %.4f s\n", blosc_elapsed_secs(t0, t1)); int64_t *buffer = malloc(nbytes); int64_t buffer_size = nbytes; blosc_set_timestamp(&t0); BLOSC_ERROR(b2nd_to_cbuffer(arr, buffer, buffer_size)); blosc_set_timestamp(&t1); printf("to_buffer: %.4f s\n", blosc_elapsed_secs(t0, t1)); for (int i = 0; i < buffer_size / typesize; i++) { if (src[i] != buffer[i]) { printf("\n Decompressed data differs from original!\n"); printf("i: %d, data %" PRId64 ", dest %" PRId64 "", i, src[i], buffer[i]); return -1; } } free(src); free(buffer); BLOSC_ERROR(b2nd_free(arr)); BLOSC_ERROR(b2nd_free_ctx(ctx)); blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/b2nd/example_print_meta.c000066400000000000000000000022701521743436100216170ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /* Example program demonstrating how to print metainfo from a b2nd frame. * You can build frames with example_frame_generator.c * * Usage: * $ ./example_print_meta * * Example of output: * $ ./example_print_meta example_big_float_frame.b2nd * Blosc2 NDim metalayer parameters: * Ndim: 3 * Shape: 200, 310, 214 * Chunkshape: 110, 120, 76 * Blockshape: 57, 52, 35 * */ # include int print_meta(char *urlpath) { b2nd_array_t *arr; BLOSC_ERROR(b2nd_open(urlpath, &arr)); BLOSC_ERROR(b2nd_print_meta(arr)); BLOSC_ERROR(b2nd_free(arr)); return 0; } int main(int argc, char *argv[]) { if (argc != 2) { printf("Usage: %s urlpath", argv[0]); exit(-1); } blosc2_init(); char *urlpath = argv[1]; print_meta(urlpath); blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/b2nd/example_serialize.c000066400000000000000000000036541521743436100214530ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ # include int main() { blosc2_init(); int8_t ndim = 2; int64_t shape[] = {10, 10}; int32_t chunkshape[] = {4, 4}; int32_t blockshape[] = {2, 2}; int32_t typesize = 8; int64_t nelem = 1; for (int i = 0; i < ndim; ++i) { nelem *= shape[i]; } int64_t size = nelem * typesize; double *data = malloc(size); for (int i = 0; i < nelem; ++i) { data[i] = i; } blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = typesize; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; blosc2_storage b2_storage = {.cparams=&cparams, .dparams=&dparams}; b2_storage.contiguous = false; b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, ndim, shape, chunkshape, blockshape, NULL, 0, NULL, 0); b2nd_array_t *arr; BLOSC_ERROR(b2nd_from_cbuffer(ctx, &arr, data, size)); uint8_t *cframe; int64_t cframe_len; bool needs_free; BLOSC_ERROR(b2nd_to_cframe(arr, &cframe, &cframe_len, &needs_free)); b2nd_array_t *dest; BLOSC_ERROR(b2nd_from_cframe(cframe, cframe_len, true, &dest)); if (needs_free) { free(cframe); } /* Fill dest array with b2nd_array_t data */ uint8_t *data_dest = malloc(size); BLOSC_ERROR(b2nd_to_cbuffer(dest, data_dest, size)); for (int i = 0; i < nelem; ++i) { if (data[i] != data_dest[i] && data[i] != i) { return -1; } } /* Free mallocs */ free(data); free(data_dest); BLOSC_ERROR(b2nd_free(arr)); BLOSC_ERROR(b2nd_free(dest)); BLOSC_ERROR(b2nd_free_ctx(ctx)); blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/b2nd/example_stack_images.c000066400000000000000000000111631521743436100221100ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ // This is an example that saves a stack of images in a b2nd frame. // The images are generated randomly and saved in two different ways: // 1) Using the b2nd_set_slice_cbuffer method. // 2) Using the b2nd_append method. #include #include int main() { blosc2_init(); const int32_t width = 4 * 512; const int32_t height = 4 * 272; const int64_t buffershape[] = {1, height, width}; // Determine the buffer size of the image (in bytes) const int64_t buffersize = width * height * (int64_t)sizeof(uint16_t); uint16_t* image = malloc(buffersize); int64_t N_images = 10; blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = sizeof(uint16_t); cparams.compcode = BLOSC_BLOSCLZ; cparams.clevel = 5; cparams.nthreads = 4; blosc2_storage storage = BLOSC2_STORAGE_DEFAULTS; storage.contiguous = true; // for a single file in output char *urlpath = "example_stack_images_set_slice.b2nd"; blosc2_remove_urlpath(urlpath); storage.urlpath = urlpath; storage.cparams = &cparams; // shape, chunkshape and blockshape of the ndarray int64_t shape[] = {N_images, height, width}; int32_t chunkshape[] = {1, height, width}; int32_t blockshape[] = {1, height, width}; b2nd_context_t *ctx = b2nd_create_ctx(&storage, 3, shape, chunkshape, blockshape, "|u2", DTYPE_NUMPY_FORMAT, NULL, 0); b2nd_array_t *src; if (b2nd_empty(ctx, &src) < 0) { printf("Error in b2nd_empty\n"); return -1; } // Loop through all images printf("Saving images (set_slice version)...\n"); for (int64_t i = 0; i < N_images; i++) { int64_t start[] = {i, 0, 0}; int64_t stop[] = {i + 1, height, width}; // Generate random image data for (int j = 0; j < width * height; j++) { image[j] = rand() % 65536; // generate random pixels (uncompressible data) } if (b2nd_set_slice_cbuffer(image, buffershape, buffersize, start, stop, src) < 0) { printf("Error in b2nd_set_slice_cbuffer\n"); return -1; } } printf("Adding vlmetalayer data\n"); uint8_t msgpack[1024]; // Pack the message using the recommended msgpack format // The Python wrapper can do this automatically char *content = "Using b2nd_set_slice_cbuffer()"; msgpack[0] = 0xd9; msgpack[1] = strlen(content); memcpy(msgpack + 2, content, strlen(content) + 1); int metalen = blosc2_vlmeta_add(src->sc, "method", msgpack, strlen(content) + 2, NULL); if (metalen < 0) { printf("Cannot write vlmetalayer"); return metalen; } b2nd_free_ctx(ctx); printf("Images saved successfully in %s\n", urlpath); // Use the append method to add more images urlpath = "example_stack_images_append.b2nd"; blosc2_remove_urlpath(urlpath); storage.urlpath = urlpath; // shape can start with 0 now int64_t shape2[] = {0, height, width}; ctx = b2nd_create_ctx(&storage, 3, shape2, chunkshape, blockshape, "|u2", DTYPE_NUMPY_FORMAT, NULL, 0); b2nd_free(src); if (b2nd_empty(ctx, &src) < 0) { printf("Error in b2nd_empty\n"); return -1; } // loop through all images printf("Saving images (append version)...\n"); for (int64_t i = 0; i < N_images; i++) { // Generate random image data for (int j = 0; j < width * height; j++) { image[j] = rand() % 65536; // generate random pixels (uncompressible data) } if (b2nd_append(src, image, buffersize, 0) < 0) { printf("Error in b2nd_append\n"); return -1; } } printf("Adding vlmetalayer data\n"); // Pack the message using the recommended msgpack format // The Python wrapper can do this automatically content = "Using b2nd_append()"; msgpack[0] = 0xd9; msgpack[1] = strlen(content); memcpy(msgpack + 2, content, strlen(content) + 1); metalen = blosc2_vlmeta_add(src->sc, "method", msgpack, strlen(content) + 2, NULL); if (metalen < 0) { printf("Cannot write vlmetalayer"); return metalen; } printf("Images saved successfully in %s\n", urlpath); // Clean resources b2nd_free(src); b2nd_free_ctx(ctx); blosc2_destroy(); free(image); return 0; } c-blosc2-3.1.5/examples/compress_file.c000066400000000000000000000052731521743436100177550ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating use of the Blosc filter from C code. To compile this program: $ gcc compress_file.c -o compress_file -lblosc2 To run: $ ./compress_file /usr/lib/libsqlite3.dylib libsqlite3.b2frame Blosc version info: 2.0.0a6.dev ($Date:: 2018-05-18 #$) Compression ratio: 5.1 MB -> 3.6 MB (1.4x) Compression time: 0.0185 s, 275.2 MB/s */ #include #include #define KB 1024. #define MB (1024*KB) #define GB (1024*MB) #define CHUNKSIZE (1000 * 1000) #define NTHREADS 4 int main(int argc, char* argv[]) { blosc2_init(); static int32_t data[CHUNKSIZE]; int32_t isize; int64_t nbytes, cbytes; blosc_timestamp_t last, current; double ttotal; if (argc != 3) { fprintf(stderr, "Usage: compress_file input_file output_file.b2frame\n"); return -1; } printf("Blosc version info: %s (%s)\n", BLOSC2_VERSION_STRING, BLOSC2_VERSION_DATE); /* Create a super-chunk container */ blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = 1; cparams.compcode = BLOSC_BLOSCLZ; //cparams.filters[BLOSC2_MAX_FILTERS - 1] = BLOSC_BITSHUFFLE; cparams.clevel = 9; cparams.nthreads = NTHREADS; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; dparams.nthreads = NTHREADS; /* Create a super-chunk backed by an in-memory frame */ remove(argv[2]); blosc2_storage storage = {.cparams=&cparams, .dparams=&dparams, .contiguous=true, .urlpath=argv[2]}; blosc2_schunk* schunk = blosc2_schunk_new(&storage); // Compress the file blosc_set_timestamp(&last); FILE* finput = fopen(argv[1], "rb"); if (finput == NULL) { printf("Input file cannot be open."); exit(1); } while ((isize = (int32_t)fread(data, 1, CHUNKSIZE, finput)) == CHUNKSIZE) { if (blosc2_schunk_append_buffer(schunk, data, isize) < 0) { fprintf(stderr, "Error in appending data to destination file"); return -1; } } if (blosc2_schunk_append_buffer(schunk, data, isize) < 0) { fprintf(stderr, "Error in appending data to destination file"); return -1; } fclose(finput); /* Gather some info */ nbytes = schunk->nbytes; cbytes = schunk->cbytes; blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Compression ratio: %.1f MB -> %.1f MB (%.1fx)\n", (float)nbytes / MB, (float)cbytes / MB, (1. * (float)nbytes) / (float)cbytes); printf("Compression time: %.3g s, %.1f MB/s\n", ttotal, (float)nbytes / (ttotal * MB)); /* Free resources */ blosc2_schunk_free(schunk); blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/contexts.c000066400000000000000000000056611521743436100167730ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating the use of a Blosc from C code. To compile this program: $ gcc -O contexts.c -o contexts -lblosc2 To run: $ ./contexts Blosc version info: 2.0.0a2 ($Date:: 2016-01-08 #$) Compression: 40000000 -> 999393 (40.0x) Correctly extracted 5 elements from compressed chunk! Decompression successful! Successful roundtrip! */ #include #include "blosc2.h" #define SIZE (100 * 1000) #define NTHREADS 2 int main(void) { blosc2_init(); static float data[SIZE]; static float data_out[SIZE]; static float data_dest[SIZE]; float data_subset[5]; float data_subset_ref[5] = {5, 6, 7, 8, 9}; int isize = SIZE * sizeof(float), osize = SIZE * sizeof(float); int dsize = SIZE * sizeof(float), csize; int i, ret; blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; blosc2_context *cctx, *dctx; /* Initialize dataset */ for (i = 0; i < SIZE; i++) { data[i] = (float)i; } printf("Blosc version info: %s (%s)\n", BLOSC2_VERSION_STRING, BLOSC2_VERSION_DATE); /* Create a context for compression */ cparams.typesize = sizeof(float); cparams.compcode = BLOSC_BLOSCLZ; cparams.filters[BLOSC2_MAX_FILTERS - 1] = BLOSC_SHUFFLE; cparams.clevel = 5; cparams.nthreads = NTHREADS; cctx = blosc2_create_cctx(cparams); /* Do the actual compression */ csize = blosc2_compress_ctx(cctx, data, isize, data_out, osize); blosc2_free_ctx(cctx); if (csize == 0) { printf("Buffer is incompressible. Giving up.\n"); return 1; } else if (csize < 0) { printf("Compression error. Error code: %d\n", csize); return csize; } printf("Compression: %d -> %d (%.1fx)\n", isize, csize, (1. * isize) / csize); /* Create a context for decompression */ dparams.nthreads = NTHREADS; dctx = blosc2_create_dctx(dparams); ret = blosc2_getitem_ctx(dctx, data_out, csize, 5, 5, data_subset, sizeof(data_subset)); if (ret < 0) { printf("Error in blosc2_getitem_ctx(). Giving up.\n"); blosc2_free_ctx(dctx); return 1; } for (i = 0; i < 5; i++) { if (data_subset[i] != data_subset_ref[i]) { printf("blosc2_getitem_ctx() fetched data differs from original!\n"); blosc2_free_ctx(dctx); return -1; } } printf("Correctly extracted 5 elements from compressed chunk!\n"); /* Decompress */ dsize = blosc2_decompress_ctx(dctx, data_out, csize, data_dest, dsize); blosc2_free_ctx(dctx); if (dsize < 0) { printf("Decompression error. Error code: %d\n", dsize); return dsize; } printf("Decompression successful!\n"); for (i = 0; i < SIZE; i++) { if (data[i] != data_dest[i]) { printf("Decompressed data differs from original!\n"); return -1; } } printf("Successful roundtrip!\n"); blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/decompress_file.c000066400000000000000000000047611521743436100202670ustar00rootroot00000000000000/* Copyright (c) 2024 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating use of the Blosc filter from C code. To compile this program: $ gcc decompress_file.c -o decompress_file -lblosc2 Example usage for compression/decompression verification: $ sha512sum compress_file 385c93c..feaf38dbec compress_file $ ./compress_file compress_file compress_file.bl2 Blosc version info: 2.13.2.dev ($Date:: 2023-01-25 #$) Compression ratio: 5.1 MB -> 2.0 MB (2.5x) Compression time: 0.07 s, 72.8 MB/s $ ./decompress_file compress_file.bl2 compress_file.1 Blosc version info: 2.13.2.dev ($Date:: 2023-01-25 #$) Decompression ratio: 2.0 MB -> 5.1 MB (0.4x) Decompression time: 0.0343 s, 148.5 MB/s $ sha512sum compress_file.1 385c93c..feaf38dbec compress_file.1 */ #include #include #define KB 1024. #define MB (1024*KB) #define GB (1024*MB) int main(int argc, char* argv[]) { blosc2_init(); static char* data; int32_t dsize; int64_t nbytes, cbytes; blosc_timestamp_t last, current; double ttotal; if (argc != 3) { fprintf(stderr, "Usage: decompress_file input_file.b2frame output_file\n"); return -1; } printf("Blosc version info: %s (%s)\n", BLOSC2_VERSION_STRING, BLOSC2_VERSION_DATE); /* Open an existing super-chunk that is on-disk (frame). */ blosc2_schunk* schunk = blosc2_schunk_open(argv[1]); data = (char*)malloc(schunk->chunksize); // Decompress the file blosc_set_timestamp(&last); FILE* foutput = fopen(argv[2], "wb"); if (foutput == NULL) { printf("Output file cannot be open."); exit(1); } for (int nchunk = 0; nchunk < schunk->nchunks; nchunk++) { dsize = blosc2_schunk_decompress_chunk(schunk, nchunk, data, schunk->chunksize); if (dsize < 0) { fprintf(stderr, "Decompression error. Error code: %d\n", dsize); return dsize; } fwrite(data, dsize, 1, foutput); } fclose(foutput); /* Gather some info */ nbytes = schunk->nbytes; cbytes = schunk->cbytes; blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Decompression ratio: %.1f MB -> %.1f MB (%.1fx)\n", (float)cbytes / MB, (float)nbytes / MB, (1. * (float)cbytes) / (float)nbytes); printf("Decompression time: %.3g s, %.1f MB/s\n", ttotal, (float)nbytes / (ttotal * MB)); /* Free resources */ free(data); blosc2_schunk_free(schunk); blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/delta_schunk_ex.c000066400000000000000000000062321521743436100202570ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating use of the delta filter from C code. To compile this program: $ gcc -O delta_schunk_ex.c -o delta_schunk_ex -lblosc2 To run: $ ./delta_schunk_ex Blosc version info: 2.0.0a4.dev ($Date:: 2016-08-04 #$) Compression ratio: 762.9 MB -> 7.6 MB (100.7x) Compression time: 0.222 s, 3437.4 MB/s Decompression time: 0.162 s, 4714.4 MB/s Successful roundtrip! */ #include #include #include "blosc2.h" #define KB 1024. #define MB (1024*KB) #define GB (1024*MB) #define CHUNKSIZE (200 * 1000) #define NCHUNKS 500 #define NTHREADS 4 int main(void) { static int64_t data[CHUNKSIZE]; static int64_t data_dest[CHUNKSIZE]; const int32_t isize = CHUNKSIZE * sizeof(int64_t); int dsize = 0; int64_t nbytes, cbytes; blosc2_schunk* schunk; int i; int nchunk; int64_t nchunks; blosc_timestamp_t last, current; double ttotal; printf("Blosc version info: %s (%s)\n", BLOSC2_VERSION_STRING, BLOSC2_VERSION_DATE); /* Initialize the Blosc compressor */ blosc2_init(); /* Create a super-chunk container */ blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = 8; cparams.filters[0] = BLOSC_DELTA; cparams.nthreads = NTHREADS; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; dparams.nthreads = NTHREADS; blosc2_storage storage = {.cparams=&cparams, .dparams=&dparams}; schunk = blosc2_schunk_new(&storage); blosc_set_timestamp(&last); for (nchunk = 1; nchunk <= NCHUNKS; nchunk++) { for (i = 0; i < CHUNKSIZE; i++) { data[i] = i * (int64_t)nchunk; } nchunks = blosc2_schunk_append_buffer(schunk, data, isize); if (nchunks != nchunk) { printf("Unexpected nchunks!"); return nchunks; } } /* Gather some info */ nbytes = schunk->nbytes; cbytes = schunk->cbytes; blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Compression ratio: %.1f MB -> %.1f MB (%.1fx)\n", (double)nbytes / MB, (double)cbytes / MB, (1. * (double)nbytes) / (double)cbytes); printf("Compression time: %.3g s, %.1f MB/s\n", ttotal, (double)nbytes / (ttotal * MB)); /* Retrieve and decompress the chunks (0-based count) */ blosc_set_timestamp(&last); for (nchunk = NCHUNKS-1; nchunk >= 0; nchunk--) { dsize = blosc2_schunk_decompress_chunk(schunk, nchunk, (void *) data_dest, isize); } if (dsize < 0) { printf("Decompression error. Error code: %d\n", dsize); return dsize; } blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Decompression time: %.3g s, %.1f MB/s\n", ttotal, (double)nbytes / (ttotal * MB)); /* Check integrity of the first chunk */ for (i = 0; i < CHUNKSIZE; i++) { if (data_dest[i] != i) { printf("Decompressed data differs from original %d, %ld!\n", i, (long)data_dest[i]); return -1; } } printf("Successful roundtrip!\n"); /* Free resources */ blosc2_schunk_free(schunk); blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/find_roots.c000066400000000000000000000125671521743436100172750ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating how the different compression params affects the performance of root finding. To compile this program: $ gcc -O3 find_roots.c -o find_roots -lblosc2 To run: $ ./find_roots Blosc version info: 2.0.0a6.dev ($Date:: 2018-05-18 #$) Creation time for X values: 0.178 s, 4274.5 MB/s Compression for X values: 762.9 MB -> 27.3 MB (28.0x) Computing Y polynomial: 0.342 s, 4463.3 MB/s Compression for Y values: 762.9 MB -> 54.0 MB (14.1x) Roots found at: 1.350000023841858, 4.450000286102295, 8.500000953674316, Find root time: 0.401 s, 3806.8 MB/s */ #include #include "blosc2.h" #define KB 1024. #define MB (1024*KB) #define GB (1024*MB) #define NCHUNKS 500 #define CHUNKSIZE (200 * 1000) // fits well in modern L3 caches #define NTHREADS 4 void fill_buffer(double *x, int nchunk) { double incx = 10. / (NCHUNKS * CHUNKSIZE); for (int i = 0; i < CHUNKSIZE; i++) { x[i] = incx * (nchunk * CHUNKSIZE + i); } } void process_data(const double *x, double *y) { for (int i = 0; i < CHUNKSIZE; i++) { double xi = x[i]; //y[i] = ((.25 * xi + .75) * xi - 1.5) * xi - 2; y[i] = (xi - 1.35) * (xi - 4.45) * (xi - 8.5); } } void find_root(const double *x, const double *y, const double prev_value) { double pv = prev_value; int last_root_idx = -1; for (int i = 0; i < CHUNKSIZE; i++) { double yi = y[i]; if (((yi > 0) - (yi < 0)) != ((pv > 0) - (pv < 0))) { if (last_root_idx != (i - 1)) { printf("%.16g, ", x[i]); last_root_idx = i; // avoid the last point (ULP effects) } } pv = yi; } } int compute_vectors(void) { static double buffer_x[CHUNKSIZE]; static double buffer_y[CHUNKSIZE]; const int32_t isize = CHUNKSIZE * sizeof(double); int dsize; long nbytes = 0; blosc2_schunk *sc_x, *sc_y; int nchunk; blosc_timestamp_t last, current; double ttotal; double prev_value; /* Create a super-chunk container for input (X values) */ blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = sizeof(double); cparams.compcode = BLOSC_LZ4; cparams.clevel = 9; cparams.filters[0] = BLOSC_TRUNC_PREC; cparams.filters_meta[0] = 23; // treat doubles as floats cparams.nthreads = NTHREADS; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; dparams.nthreads = NTHREADS; blosc2_storage storage = {.cparams=&cparams, .dparams=&dparams}; sc_x = blosc2_schunk_new(&storage); /* Create a super-chunk container for output (Y values) */ sc_y = blosc2_schunk_new(&storage); /* Now fill the buffer with even values between 0 and 10 */ blosc_set_timestamp(&last); for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { fill_buffer(buffer_x, nchunk); blosc2_schunk_append_buffer(sc_x, buffer_x, isize); nbytes += (long) isize; } blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Creation time for X values: %.3g s, %.1f MB/s\n", ttotal, (double) nbytes / (ttotal * MB)); printf("Compression for X values: %.1f MB -> %.1f MB (%.1fx)\n", (double)sc_x->nbytes / MB, (double)sc_x->cbytes / MB, (1. * (double)sc_x->nbytes) / (double)sc_x->cbytes); /* Retrieve the chunks and compute the polynomial in another super-chunk */ blosc_set_timestamp(&last); for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { dsize = blosc2_schunk_decompress_chunk(sc_x, nchunk, buffer_x, isize); if (dsize < 0) { printf("Decompression error. Error code: %d\n", dsize); return dsize; } process_data(buffer_x, buffer_y); blosc2_schunk_append_buffer(sc_y, buffer_y, isize); } blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Computing Y polynomial: %.3g s, %.1f MB/s\n", ttotal, 2. * (double) nbytes / (ttotal * MB)); // 2 super-chunks involved printf("Compression for Y values: %.1f MB -> %.1f MB (%.1fx)\n", (double)sc_y->nbytes / MB, (double)sc_y->cbytes / MB, (1. * (double)sc_y->nbytes) / (double)sc_y->cbytes); /* Find the roots of the polynomial */ printf("Roots found at: "); blosc_set_timestamp(&last); prev_value = buffer_y[0]; for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { dsize = blosc2_schunk_decompress_chunk(sc_y, nchunk, (void *) buffer_y, isize); if (dsize < 0) { printf("Decompression error. Error code: %d\n", dsize); return dsize; } dsize = blosc2_schunk_decompress_chunk(sc_x, nchunk, (void *) buffer_x, isize); if (dsize < 0) { printf("Decompression error. Error code: %d\n", dsize); return dsize; } find_root(buffer_x, buffer_y, prev_value); prev_value = buffer_y[CHUNKSIZE - 1]; } blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("\n"); printf("Find root time: %.3g s, %.1f MB/s\n", ttotal, 2. * (double) nbytes / (ttotal * MB)); // 2 super-chunks involved /* Free resources */ /* Destroy the super-chunk */ blosc2_schunk_free(sc_x); blosc2_schunk_free(sc_y); return 0; } int main(void) { printf("Blosc version info: %s (%s)\n", BLOSC2_VERSION_STRING, BLOSC2_VERSION_DATE); /* Initialize the Blosc compressor */ blosc2_init(); compute_vectors(); /* Destroy the Blosc environment */ blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/frame_backed_schunk.c000066400000000000000000000105341521743436100210550ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating use of the Blosc filter from C code. To compile this program: $ gcc -O frame_backed_schunk.c -o frame_backed_schunk -lblosc2 To run: $ ./frame_backed_schunk Blosc version info: 2.0.0a6.dev ($Date:: 2018-05-18 #$) Compression ratio: 381.5 MB -> 12.2 MB (31.2x) Time for append data to a schunk backed by an in-memory frame: 0.0892 s, 4278.1 MB/s Compression ratio: 381.5 MB -> 12.2 MB (31.2x) Time for append data to a schunk backed by a fileframe: 0.107 s, 3556.3 MB/s Successful roundtrip data <-> schunk (frame-backed) ! */ #include #include #include #define KB 1024. #define MB (1024*KB) #define GB (1024*MB) #define CHUNKSIZE (1000 * 1000) #define NCHUNKS 100 #define NTHREADS 4 int main(void) { blosc2_init(); static int32_t data[CHUNKSIZE]; static int32_t data_dest1[CHUNKSIZE]; static int32_t data_dest2[CHUNKSIZE]; int32_t isize = CHUNKSIZE * sizeof(int32_t); int64_t nbytes, cbytes; int i, nchunk; int64_t nchunks; blosc_timestamp_t last, current; double ttotal; printf("Blosc version info: %s (%s)\n", BLOSC2_VERSION_STRING, BLOSC2_VERSION_DATE); // Compression and decompression parameters blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = sizeof(int32_t); cparams.clevel = 9; cparams.nthreads = NTHREADS; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; dparams.nthreads = NTHREADS; /* Create a new super-chunk backed by an in-memory frame */ blosc2_storage storage = {.contiguous=true, .cparams=&cparams, .dparams=&dparams}; blosc2_schunk* schunk1 = blosc2_schunk_new(&storage); blosc_set_timestamp(&last); for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { for (i = 0; i < CHUNKSIZE; i++) { data[i] = i * nchunk; } nchunks = blosc2_schunk_append_buffer(schunk1, data, isize); if (nchunks != nchunk + 1) { printf("Unexpected nchunks!"); return nchunks; } } /* Gather some info */ nbytes = schunk1->nbytes; cbytes = schunk1->cbytes; blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Compression ratio: %.1f MB -> %.1f MB (%.1fx)\n", (double)nbytes / MB, (double)cbytes / MB, (1. * (double)nbytes) / (double)cbytes); printf("Time for append data to a schunk backed by an in-memory frame: %.3g s, %.1f MB/s\n", ttotal, (double)nbytes / (ttotal * MB)); /* Create a new super-chunk backed by an in-memory frame */ storage = (blosc2_storage){.contiguous=true, .cparams=&cparams, .dparams=&dparams}; blosc2_schunk* schunk2 = blosc2_schunk_new(&storage); blosc_set_timestamp(&last); for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { for (i = 0; i < CHUNKSIZE; i++) { data[i] = i * nchunk; } nchunks = blosc2_schunk_append_buffer(schunk2, data, isize); assert(nchunks == nchunk + 1); } /* Gather some info */ nbytes = schunk2->nbytes; cbytes = schunk2->cbytes; blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Compression ratio: %.1f MB -> %.1f MB (%.1fx)\n", (double)nbytes / MB, (double)cbytes / MB, (1. * (double)nbytes) / (double)cbytes); printf("Time for append data to a schunk backed by a fileframe: %.3g s, %.1f MB/s\n", ttotal, (double)nbytes / (ttotal * MB)); /* Retrieve and decompress the chunks from the super-chunks and compare values */ for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { int32_t dsize1 = blosc2_schunk_decompress_chunk(schunk1, nchunk, data_dest1, isize); if (dsize1 < 0) { printf("Decompression error in schunk1. Error code: %d\n", dsize1); return dsize1; } int32_t dsize2 = blosc2_schunk_decompress_chunk(schunk2, nchunk, data_dest2, isize); if (dsize2 < 0) { printf("Decompression error in schunk2. Error code: %d\n", dsize2); return dsize2; } assert(dsize1 == dsize2); /* Check integrity of the last chunk */ for (i = 0; i < CHUNKSIZE; i++) { assert (data_dest1[i] == i * nchunk); assert (data_dest2[i] == i * nchunk); } } printf("Successful roundtrip data <-> schunk (frame-backed) !\n"); /* Free resources */ blosc2_schunk_free(schunk1); blosc2_schunk_free(schunk2); blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/frame_big.c000066400000000000000000000060121521743436100170260ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating frames going bigger than 2 GB. To compile this program: $ gcc -O frame_big.c -o frame_big -lblosc2 To run: $ ./frame_big Blosc version info: 2.0.0-beta.4.dev ($Date:: 2019-09-02 #$) Compression ratio: 4577.6 MB -> 169.8 MB (27.0x) Time for append data to a schunk backed by a fileframe: 2.61 s, 1750.8 MB/s Successful roundtrip data <-> schunk (frame-backed) ! */ #include #include #include #define KB 1024. #define MB (1024*KB) #define GB (1024*MB) #define CHUNKSIZE (1000 * 1000) #define NCHUNKS 1200 // > 4 GB int32 frame #define NTHREADS 4 int main(void) { blosc2_init(); static int32_t data[CHUNKSIZE]; static int32_t data_dest[CHUNKSIZE]; int32_t isize = CHUNKSIZE * sizeof(int32_t); blosc_timestamp_t last, current; printf("Blosc version info: %s (%s)\n", BLOSC2_VERSION_STRING, BLOSC2_VERSION_DATE); // Compression and decompression parameters blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = sizeof(int32_t); cparams.clevel = 9; cparams.nthreads = NTHREADS; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; dparams.nthreads = NTHREADS; /* Create a new super-chunk backed by a fileframe */ char* urlpath = "frame_big.b2frame"; blosc2_storage storage = {.contiguous=true, .urlpath=urlpath, .cparams=&cparams, .dparams=&dparams}; remove(urlpath); blosc2_schunk* schunk = blosc2_schunk_new(&storage); blosc_set_timestamp(&last); for (int nchunk = 0; nchunk < NCHUNKS; nchunk++) { for (int i = 0; i < CHUNKSIZE; i++) { data[i] = i * nchunk; } int64_t nchunks = blosc2_schunk_append_buffer(schunk, data, isize); if (nchunks != nchunk + 1) { printf("Error: nchunks is not correct"); return -1; } } /* Gather some info */ int64_t nbytes = schunk->nbytes; int64_t cbytes = schunk->cbytes; blosc_set_timestamp(¤t); double ttotal = blosc_elapsed_secs(last, current); printf("Compression ratio: %.1f MB -> %.1f MB (%.1fx)\n", (double)nbytes / MB, (double)cbytes / MB, (1. * (double)nbytes) / (double)cbytes); printf("Time for append data to a schunk backed by a fileframe: %.3g s, %.1f MB/s\n", ttotal, (double)nbytes / (ttotal * MB)); /* Retrieve and decompress the chunks from the super-chunks and compare values */ for (int nchunk = 0; nchunk < NCHUNKS; nchunk++) { int32_t dsize = blosc2_schunk_decompress_chunk(schunk, nchunk, data_dest, isize); if (dsize < 0) { printf("Decompression error in schunk. Error code: %d\n", dsize); return dsize; } /* Check integrity of the last chunk */ for (int i = 0; i < CHUNKSIZE; i++) { assert (data_dest[i] == i * nchunk); } } printf("Successful roundtrip data <-> schunk (frame-backed) !\n"); /* Free resources */ blosc2_schunk_free(schunk); blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/frame_metalayers.c000066400000000000000000000114431521743436100204370ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating the use of the metalayers. To compile this program: $ gcc -O frame_metalayers.c -o frame_metalayers -lblosc2 To run: $ ./frame_metalayers Blosc version info: 2.0.0a6.dev ($Date:: 2018-05-18 #$) Compression ratio: 3.8 MB -> 0.0 MB (234.4x) Compression time: 0.00218 s, 1747.2 MB/s Time for schunk -> frame: 1.19e-05 s, 313.5 GB/s Frame length in memory: 17247 bytes Frame length on disk: 17247 bytes Time for frame -> fileframe (simple_frame.b2frame): 0.000144 s, 25.9 GB/s Time for fileframe (frame_metalayers.b2frame) -> frame : 4.08e-05 s, 91.3 GB/s Time for fileframe -> schunk: 4.29e-07 s, 8683.7 GB/s */ #include #include #include #include #define KB 1024. #define MB (1024*KB) #define GB (1024*MB) #define CHUNKSIZE (1000 * 1000) #define NCHUNKS 1 #define NTHREADS 4 int main(void) { blosc2_init(); int32_t isize = CHUNKSIZE * sizeof(int32_t); int32_t* data = malloc(isize); int64_t nbytes, cbytes; int i, nchunk; int64_t nchunks; blosc_timestamp_t last, current; double ttotal; printf("Blosc version info: %s (%s)\n", BLOSC2_VERSION_STRING, BLOSC2_VERSION_DATE); /* Create a super-chunk container */ blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = sizeof(int32_t); //cparams.compcode = BLOSC_LZ4; cparams.clevel = 9; cparams.nthreads = NTHREADS; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; dparams.nthreads = NTHREADS; blosc2_storage storage = {.cparams=&cparams, .dparams=&dparams, .contiguous=true}; blosc2_schunk* schunk = blosc2_schunk_new(&storage); // Add some metalayers (one must add metalayers prior to actual data) blosc2_meta_add(schunk, "my_metalayer1", (uint8_t *) "my_content1", (uint32_t) strlen("my_content1")); blosc2_meta_add(schunk, "my_metalayer2", (uint8_t *) "my_content1", (uint32_t) strlen("my_content1")); blosc_set_timestamp(&last); for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { for (i = 0; i < CHUNKSIZE; i++) { data[i] = i * nchunk + i; } nchunks = blosc2_schunk_append_buffer(schunk, data, isize); if (nchunks != nchunk + 1) { printf("Unexpected nchunks!"); return nchunks; } } /* Gather some info */ nbytes = schunk->nbytes; cbytes = schunk->cbytes; blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Compression ratio: %.2f MB -> %.2f MB (%.1fx)\n", (double)nbytes / MB, (double)cbytes / MB, (1. * (double)nbytes) / (double)cbytes); printf("Compression time: %.3g s, %.1f MB/s\n", ttotal, (double)nbytes / (ttotal * MB)); blosc_set_timestamp(&last); // Update a metalayer (this is fine as long as the new content does not exceed the size of the previous one) blosc2_meta_update(schunk, "my_metalayer2", (uint8_t *) "my_content2", (uint32_t) strlen("my_content2")); blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Time for update metalayer in header: %.2g s\n", ttotal); printf("Frame length in memory: %ld bytes\n", (long)schunk->cbytes); // schunk (in-memory) -> fileframe (on-disk) blosc_set_timestamp(&last); int64_t frame_len = blosc2_schunk_to_file(schunk, "frame_metalayers.b2frame"); printf("Frame length on disk: %ld bytes\n", (long)frame_len); blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Time for frame -> fileframe (simple_frame.b2frame): %.3g s, %.1f GB/s\n", ttotal, (double)nbytes / (ttotal * GB)); // fileframe (file) -> schunk2 (schunk based on a on-disk frame) blosc_set_timestamp(&last); blosc2_schunk* schunk2 = blosc2_schunk_open("frame_metalayers.b2frame"); if (schunk2 == NULL) { printf("Cannot get the schunk from frame2"); return -1; } blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Time for fileframe (%s) -> schunk : %.3g s, %.1f GB/s\n", schunk2->storage->urlpath, ttotal, (double)nbytes / (ttotal * GB)); // Check that the metalayers had a good roundtrip if (schunk2->nmetalayers != 2) { printf("nclients not retrieved correctly!\n"); return -1; } uint8_t* content; int32_t content_len; if (blosc2_meta_get(schunk2, "my_metalayer1", &content, &content_len) < 0) { printf("metalayer not found"); return -1; } if (memcmp(content, "my_content1", content_len) != 0) { printf("serialized content for metalayer not retrieved correctly!\n"); return -1; } free(content); /* Free resources */ blosc2_schunk_free(schunk); blosc2_schunk_free(schunk2); free(data); blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/frame_offset.c000066400000000000000000000135541521743436100175640ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating use of the Blosc filter from C code. To compile this program: $ gcc frame_offset.c -o frame_offset -lblosc2 To run: $ ./frame_offset Blosc version info: 2.1.2.dev ($Date:: 2022-05-07 #$) Compression ratio: 76.3 MB -> 1.2 MB (66.0x) Compression time: 1.17 s, 65.0 MB/s Variable-length metalayer length: 10 0 1 2 3 4 5 6 7 8 9 Time for schunk -> frame: 0.266 s, 286.7 MB/s Frame length in memory: 1212483 bytes Frame length on disk: 1212483 bytes Time for frame -> fileframe (frame_simple.b2frame): 6.2 s, 0.0 GB/s Time for fileframe (file:///frame_simple.b2frame) -> frame2 : 0.00177 s, 42.2 GB/s Time for fileframe (file:///frame_simple.b2frame) + offset 1212483 -> frame3 : 0.00176 s, 42.3 GB/s Successful roundtrip schunk <-> frame <-> fileframe schunk1 <-> frame1 <-> fileframe + offset */ #include #include #include #define KB 1024. #define MB (1024*KB) #define GB (1024*MB) #define CHUNKSIZE (200 * 1000) #define NCHUNKS 100 #define NTHREADS 4 int main(void) { blosc2_init(); static int32_t data[CHUNKSIZE]; static int32_t data2[CHUNKSIZE]; int32_t isize = CHUNKSIZE * sizeof(int32_t); int i, nchunk; int64_t nchunks; blosc_timestamp_t last, current; double ttotal; printf("Blosc version info: %s (%s)\n", BLOSC2_VERSION_STRING, BLOSC2_VERSION_DATE); /* Create a super-chunk container */ blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = sizeof(int32_t); cparams.compcode = BLOSC_LZ4; cparams.clevel = 9; cparams.nthreads = NTHREADS; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; dparams.nthreads = NTHREADS; blosc2_storage storage = {.cparams=&cparams, .dparams=&dparams}; blosc2_schunk* schunk0w = blosc2_schunk_new(&storage); blosc2_schunk* schunk1a = blosc2_schunk_new(&storage); // Add some data for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { for (i = 0; i < CHUNKSIZE; i++) { data[i] = i * nchunk; data2[i] = 2 * i * nchunk; } nchunks = blosc2_schunk_append_buffer(schunk0w, data, isize); if (nchunks != nchunk + 1) { printf("Unexpected nchunks!"); return nchunks; } blosc2_schunk_append_buffer(schunk1a, data2, isize); } // Start different conversions between schunks, frames and fileframes // super-chunk -> cframe (contiguous frame, or buffer) uint8_t* cframe, *cframe1; bool cframe_needs_free, cframe_needs_free1; int64_t frame_len = blosc2_schunk_to_buffer(schunk0w, &cframe, &cframe_needs_free); if (frame_len < 0) { return (int)frame_len; } int64_t frame_len1 = blosc2_schunk_to_buffer(schunk1a, &cframe1, &cframe_needs_free1); if (frame_len1 < 0) { return (int)frame_len1; } // super-chunk -> fileframe (contiguous frame, on-disk) remove("frame_simple.b2frame"); blosc_set_timestamp(&last); frame_len = blosc2_schunk_to_file(schunk0w, "frame_simple.b2frame"); if (frame_len < 0) { return (int)frame_len; } printf("Frame length on disk: %ld bytes\n", (long)frame_len); blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Time for frame -> fileframe (frame_simple.b2frame): %.3g s, %.1f GB/s\n", ttotal, (double)schunk0w->nbytes / (ttotal * GB)); blosc_set_timestamp(&last); int64_t offset = blosc2_schunk_append_file(schunk1a, "frame_simple.b2frame"); if (offset < 0) { return (int)offset; } blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Time for frame1 -> fileframe (frame_simple.b2frame) + offset: %.3g s, %.1f GB/s\n", ttotal, (double)schunk1a->nbytes / (ttotal * GB)); // fileframe (file) -> schunk2 (on-disk contiguous, super-chunk) blosc_set_timestamp(&last); blosc2_schunk* schunk2r = blosc2_schunk_open("file:///frame_simple.b2frame"); blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Time for fileframe (%s) -> frame2 : %.3g s, %.1f GB/s\n", schunk2r->storage->urlpath, ttotal, (double)schunk2r->nbytes / (ttotal * GB)); // fileframe (file) -> schunk3 (on-disk contiguous, super-chunk) blosc_set_timestamp(&last); blosc2_schunk* schunk3o = blosc2_schunk_open_offset("file:///frame_simple.b2frame", offset); blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Time for fileframe (%s) + offset -> frame3 : %.3g s, %.1f GB/s\n", schunk3o->storage->urlpath, ttotal, (double)schunk3o->nbytes / (ttotal * GB)); uint8_t* cframe2, *cframe3; bool cframe_needs_free2, cframe_needs_free3; int64_t frame_len2 = blosc2_schunk_to_buffer(schunk2r, &cframe2, &cframe_needs_free2); if (frame_len2 != frame_len) { return (int)frame_len2; } for (int j = 0; j < frame_len; ++j) { if (cframe[j] != cframe2[j]) { printf("schunk != schunk2 in index %d: %u, %u", j, cframe[j], cframe2[j]); return -1; } } int64_t frame_len3 = blosc2_schunk_to_buffer(schunk3o, &cframe3, &cframe_needs_free3); if (frame_len3 != frame_len1) { return (int)frame_len3; } for (int j = 0; j < frame_len1; ++j) { if (cframe1[j] != cframe3[j]) { printf("schunk1 != schunk3 in index %d: %u, %u", j, cframe1[j], cframe3[j]); return -1; } } printf("Successful roundtrip schunk <-> frame <-> fileframe\n" " schunk1 <-> frame1 <-> fileframe + offset"); /* Free resources */ blosc2_schunk_free(schunk0w); blosc2_schunk_free(schunk1a); blosc2_schunk_free(schunk2r); blosc2_schunk_free(schunk3o); if (cframe_needs_free) { free(cframe); } if (cframe_needs_free1) { free(cframe1); } if (cframe_needs_free2) { free(cframe2); } if (cframe_needs_free3) { free(cframe3); } blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/frame_roundtrip.c000066400000000000000000000075521521743436100203250ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating a schunk roundtrip via a frame. To compile this program: $ gcc frame_roundtrip.c -o frame_roundtrip -lblosc2 To run: $ ./frame_roundtrip */ #include #include #define CHUNKSIZE 100 #define NCHUNKS 1000 int main(void) { blosc2_init(); int32_t total_bytes = CHUNKSIZE * sizeof(int32_t); int32_t *buf = calloc(CHUNKSIZE, sizeof(int32_t)); // You can initialize data, but zeros compress better ;-) // for (int i = 0; i < CHUNKSIZE; i++) { // buf[i] = i; // } // Create the original schunk blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = sizeof(int32_t); cparams.filters[BLOSC2_MAX_FILTERS - 1] = BLOSC_BITSHUFFLE; cparams.clevel = 9; // blosc2_remove_dir("/tmp/test.frame"); // blosc2_storage storage = {.cparams=&cparams, .contiguous=false, .urlpath="/tmp/test.frame"}; blosc2_storage storage = {.cparams=&cparams, .contiguous=false}; blosc2_schunk* schunk = blosc2_schunk_new(&storage); if (schunk == NULL) { printf("Error in creating schunk\n"); goto failed; } // Append some chunks for (int i = 0; i < NCHUNKS; i++) { int64_t status = blosc2_schunk_append_buffer(schunk, buf, total_bytes); if (status < 0) { printf("Error in appending to schunk\n"); goto failed; } } printf("nbytes, cbytes for schunk: %lld, %lld\n", (long long int)schunk->nbytes, (long long int)schunk->cbytes); // Check contents uint8_t *chunk; bool needs_free; int32_t *dest = malloc(CHUNKSIZE * sizeof(int32_t)); for (int i = 0; i < NCHUNKS; i++) { int cbytes = blosc2_schunk_get_chunk(schunk, i, &chunk, &needs_free); if (cbytes < 0) { printf("Error in getting chunk %d from schunk\n", i); goto failed; } int nbytes = blosc2_decompress(chunk, cbytes, dest, total_bytes); if (nbytes != total_bytes) { printf("Error in schunk: nbytes differs (%d != %d)\n", nbytes, total_bytes); goto failed; } if (needs_free) { free(chunk); } for (int j = 0; j < CHUNKSIZE; j++) { if (buf[j] != dest[j]) { printf("Error in schunk: data differs in index %d (%d != %d)!\n", j, buf[i], dest[i]); goto failed; } } } // Convert into a cframe (contiguous buffer) bool cframe_needs_free; uint8_t* cframe; int64_t cframe_len = blosc2_schunk_to_buffer(schunk, &cframe, &cframe_needs_free); if (cframe_len < 0 || !cframe_needs_free) { goto failed; } printf("converted into a cframe of %lld bytes\n", (long long int)cframe_len); // Convert back into a different schunk blosc2_schunk* schunk2 = blosc2_schunk_from_buffer(cframe, cframe_len, true); if (schunk2 == NULL) { goto failed; } printf("nbytes, cbytes for schunk2: %lld, %lld\n", (long long int)schunk2->nbytes, (long long int)schunk2->cbytes); // Check contents for (int i = 0; i < NCHUNKS; i++) { int cbytes = blosc2_schunk_get_chunk(schunk2, i, &chunk, &needs_free); if (cbytes < 0) { printf("Error in getting chunk %d from schunk2\n", i); goto failed; } int nbytes = blosc2_decompress(chunk, cbytes, dest, total_bytes); if (nbytes != total_bytes) { printf("Error in schunk2: nbytes differs (%d != %d)\n", nbytes, total_bytes); goto failed; } if (needs_free) { free(chunk); } for (int j = 0; j < CHUNKSIZE; j++) { if (buf[j] != dest[j]) { printf("Error in schunk2: data differs in index %d (%d != %d)!\n", j, buf[i], dest[i]); goto failed; } } } free(dest); blosc2_schunk_free(schunk2); blosc2_schunk_free(schunk); if (cframe_needs_free) { free(cframe); } free(buf); printf("All good!\n"); blosc2_destroy(); return 0; failed: return -1; } c-blosc2-3.1.5/examples/frame_simple.c000066400000000000000000000152201521743436100175570ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating use of the Blosc filter from C code. To compile this program: $ gcc frame_simple.c -o frame_simple -lblosc2 To run: $ ./frame_simple Blosc version info: 2.0.0a6.dev ($Date:: 2018-05-18 #$) Compression ratio: 381.5 MB -> 9.5 MB (40.2x) Compression time: 0.705 s, 541.0 MB/s Time for schunk -> frame: 0.00796 s, 47905.3 MB/s Frame length in memory: 9940344 bytes Frame length on disk: 9940344 bytes Time for frame -> fileframe (frame_simple.b2frame): 0.0108 s, 35159.6 MB/s Time for fileframe (frame_simple.b2frame) -> frame : 0.000254 s, 1.5e+06 MB/s Time for frame -> schunk: 1.1e-05 s, 3.48e+07 MB/s Time for fileframe -> schunk: 1.25e-05 s, 3.05e+07 MB/s Successful roundtrip schunk <-> frame <-> fileframe ! */ #include #include #define KB 1024. #define MB (1024*KB) #define GB (1024*MB) #define CHUNKSIZE (200 * 1000) #define NCHUNKS 100 #define NTHREADS 4 int main(void) { blosc2_init(); static int32_t data[CHUNKSIZE]; static int32_t data_dest[CHUNKSIZE]; static int32_t data_dest2[CHUNKSIZE]; int32_t isize = CHUNKSIZE * sizeof(int32_t); int64_t nbytes, cbytes; int i, nchunk; int64_t nchunks; blosc_timestamp_t last, current; double ttotal; printf("Blosc version info: %s (%s)\n", BLOSC2_VERSION_STRING, BLOSC2_VERSION_DATE); /* Create a super-chunk container */ blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = sizeof(int32_t); cparams.compcode = BLOSC_LZ4; cparams.clevel = 9; cparams.nthreads = NTHREADS; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; dparams.nthreads = NTHREADS; blosc2_storage storage = {.cparams=&cparams, .dparams=&dparams}; blosc2_schunk* schunk = blosc2_schunk_new(&storage); // Add some data blosc_set_timestamp(&last); for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { for (i = 0; i < CHUNKSIZE; i++) { data[i] = i * nchunk; } nchunks = blosc2_schunk_append_buffer(schunk, data, isize); if (nchunks != nchunk + 1) { printf("blosc2_schunk_append_buffer is not working correctly"); return BLOSC2_ERROR_FAILURE; } } // Add some vlmetalayers data int32_t content_len = 10; uint8_t *content = malloc(content_len); for (int32_t j = 0; j < content_len; ++j) { content[j] = (uint8_t) j; } int umlen = blosc2_vlmeta_add(schunk, "vlmetalayer", content, content_len, NULL); free(content); if (umlen < 0) { printf("Cannot write vlmetalayers chunk"); return umlen; } /* Gather some info */ nbytes = schunk->nbytes; cbytes = schunk->cbytes; blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Compression ratio: %.1f MB -> %.1f MB (%.1fx)\n", (double)nbytes / MB, (double)cbytes / MB, (1. * (double)nbytes) / (double)cbytes); printf("Compression time: %.3g s, %.1f MB/s\n", ttotal, (double)nbytes / (ttotal * MB)); uint8_t* vlmetalayer; blosc2_vlmeta_get(schunk, "vlmetalayer", &vlmetalayer, &content_len); printf("Variable-length metalayer length: %d\n", content_len); for (int j = 0; j < content_len; ++j) { printf("%3d", vlmetalayer[j]); } printf("\n"); free(vlmetalayer); // Start different conversions between schunks, frames and fileframes // super-chunk -> cframe (contiguous frame, or buffer) blosc_set_timestamp(&last); uint8_t* cframe; bool cframe_needs_free; int64_t frame_len = blosc2_schunk_to_buffer(schunk, &cframe, &cframe_needs_free); if (frame_len < 0) { return (int)frame_len; } blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Time for schunk -> frame: %.3g s, %.1f MB/s\n", ttotal, (double)nbytes / (ttotal * MB)); printf("Frame length in memory: %ld bytes\n", (long)frame_len); // super-chunk -> fileframe (contiguous frame, on-disk) remove("frame_simple.b2frame"); blosc_set_timestamp(&last); frame_len = blosc2_schunk_to_file(schunk, "frame_simple.b2frame"); if (frame_len < 0) { return (int)frame_len; } printf("Frame length on disk: %ld bytes\n", (long)frame_len); blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Time for frame -> fileframe (frame_simple.b2frame): %.3g s, %.1f GB/s\n", ttotal, (double)nbytes / (ttotal * GB)); // fileframe (file) -> schunk2 (on-disk contiguous, super-chunk) blosc_set_timestamp(&last); blosc2_schunk* schunk2 = blosc2_schunk_open("file:///frame_simple.b2frame"); blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Time for fileframe (%s) -> frame : %.3g s, %.1f GB/s\n", schunk2->storage->urlpath, ttotal, (double)nbytes / (ttotal * GB)); // frame1 (in-memory) -> schunk blosc_set_timestamp(&last); // The next creates a schunk from the in-memory frame blosc2_schunk* schunk1 = blosc2_schunk_from_buffer(cframe, frame_len, false); if (schunk1 == NULL) { printf("Bad conversion frame1 -> schunk1!\n"); return -1; } blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Time for frame -> schunk: %.3g s, %.1f GB/s\n", ttotal, (double)nbytes / (ttotal * GB)); /* Retrieve and decompress the chunks from the super-chunks and compare values */ for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { int32_t dsize = blosc2_schunk_decompress_chunk(schunk1, nchunk, data_dest, isize); if (dsize < 0) { printf("Decompression error in schunk1. Error code: %d\n", dsize); return dsize; } dsize = blosc2_schunk_decompress_chunk(schunk2, nchunk, data_dest2, isize); if (dsize < 0) { printf("Decompression error in schunk2. Error code: %d\n", dsize); return dsize; } /* Check integrity of this chunk */ for (i = 0; i < CHUNKSIZE; i++) { if ((data_dest[i] != i * nchunk) || (data_dest2[i] != i * nchunk)) { printf("data mismatch"); return BLOSC2_ERROR_FAILURE; } } } printf("Successful roundtrip schunk <-> frame <-> fileframe !\n"); blosc2_vlmeta_get(schunk1, "vlmetalayer", &vlmetalayer, &content_len); for (int j = 0; j < content_len; ++j) { printf("%3d", vlmetalayer[j]); } printf("\n"); free(vlmetalayer); blosc2_vlmeta_get(schunk2, "vlmetalayer", &vlmetalayer, &content_len); for (int j = 0; j < content_len; ++j) { printf("%3d", vlmetalayer[j]); } printf("\n"); free(vlmetalayer); /* Free resources */ blosc2_schunk_free(schunk); blosc2_schunk_free(schunk1); blosc2_schunk_free(schunk2); if (cframe_needs_free) { free(cframe); } blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/frame_vlmetalayers.c000066400000000000000000000055271521743436100210070ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating use of the Blosc vlmetalayer from C code. To compile this program: $ gcc frame_vlmetalayer.c -o frame_vlmetalyer -lblosc2 To run: $ ./frame_vlmetalyer */ #include #include #define KB 1024. #define MB (1024*KB) #define GB (1024*MB) #define CHUNKSIZE (200 * 1000) #define NCHUNKS 100 #define NTHREADS 4 int main(void) { blosc2_init(); printf("Blosc version info: %s (%s)\n", BLOSC2_VERSION_STRING, BLOSC2_VERSION_DATE); /* Create a super-chunk container */ blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = sizeof(int32_t); cparams.compcode = BLOSC_LZ4; cparams.clevel = 9; cparams.nthreads = NTHREADS; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; dparams.nthreads = NTHREADS; char* urlpath = "vlmetalayers.b2frame"; remove(urlpath); blosc2_storage storage = {.contiguous=true, .urlpath=urlpath, .cparams=&cparams, .dparams=&dparams}; blosc2_schunk* schunk = blosc2_schunk_new(&storage); // Add a metalayer int metalater_len = blosc2_meta_add(schunk, "metalayer", (uint8_t *) "This is a metalayers content...", 10); if (metalater_len < 0) { printf("Cannot write metalayers chunk"); return metalater_len; } // Add some vlmetalayers data metalater_len = blosc2_vlmeta_add(schunk, "vlmetalayer", (uint8_t *) "This is a vlmetalayers content...", 32, NULL); if (metalater_len < 0) { printf("Cannot write vlmetalayers chunk"); return metalater_len; } // Add some vlmetalayers data metalater_len = blosc2_vlmeta_add(schunk, "vlmetalayer2", (uint8_t *) "This is a content...", 10, NULL); if (metalater_len < 0) { printf("Cannot write vlmetalayers chunk"); return metalater_len; } metalater_len = blosc2_vlmeta_update(schunk, "vlmetalayer", (uint8_t *) "This is a another vlmetalayer content...", 20, NULL); if (metalater_len < 0) { printf("Cannot write vlmetalayers chunk"); return metalater_len; } // Delete a vlmetalayer int nvlmetalayers = blosc2_vlmeta_delete(schunk, "vlmetalayer"); if (nvlmetalayers < 0) { printf("Cannot delete the vlmetalayer"); } metalater_len = blosc2_vlmeta_exists(schunk, "vlmetalayer"); if (metalater_len >= 0) { printf("The vlmetalayer was not deleted successfully"); return metalater_len; } metalater_len = blosc2_vlmeta_exists(schunk, "vlmetalayer2"); if (metalater_len < 0) { printf("The vlmetalayer was not deleted successfully"); return metalater_len; } blosc2_schunk *sc = blosc2_schunk_open(urlpath); /* Free resources */ blosc2_schunk_free(schunk); blosc2_schunk_free(sc); blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/get_blocksize.c000066400000000000000000000042561521743436100177470ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating the use of a Blosc from C code. To compile this program: $ gcc -O get_blocksize.c -o get_blocksize -lblosc2 To run: $ ./get_blocksize Blosc version info: 2.10.3.dev ($Date:: 2023-08-19 #$) Compression: 10000000 -> 32 (312500.0x) osize, csize, blocksize: 10000000, 32, 16384 Compression: 10000000 -> 32 (312500.0x) osize, csize, blocksize: 10000000, 32, 131072 Compression: 10000000 -> 32 (312500.0x) osize, csize, blocksize: 10000000, 32, 65536 Compression: 10000000 -> 32 (312500.0x) osize, csize, blocksize: 10000000, 32, 131072 Compression: 10000000 -> 32 (312500.0x) osize, csize, blocksize: 10000000, 32, 262144 Compression: 10000000 -> 32 (312500.0x) osize, csize, blocksize: 10000000, 32, 262144 Compression: 10000000 -> 32 (312500.0x) osize, csize, blocksize: 10000000, 32, 524288 Compression: 10000000 -> 32 (312500.0x) osize, csize, blocksize: 10000000, 32, 1048576 Compression: 10000000 -> 32 (312500.0x) osize, csize, blocksize: 10000000, 32, 524288 Compression: 10000000 -> 32 (312500.0x) osize, csize, blocksize: 10000000, 32, 2097152 Process finished with exit code 0 */ #include #include "blosc2.h" int main(void) { blosc2_init(); static uint8_t data_dest[BLOSC2_MAX_OVERHEAD]; blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = sizeof(float); cparams.compcode = BLOSC_ZSTD; printf("Blosc version info: %s (%s)\n", BLOSC2_VERSION_STRING, BLOSC2_VERSION_DATE); /* Do the actual compression */ for (int clevel=0; clevel < 10; clevel++) { cparams.clevel = clevel; cparams.splitmode = clevel % 2; int isize = 10 * 1000 * 1000; int osize, csize, blocksize; csize = blosc2_chunk_zeros(cparams, isize, data_dest, BLOSC2_MAX_OVERHEAD); printf("Compression: %d -> %d (%.1fx)\n", isize, csize, (1. * isize) / csize); BLOSC_ERROR(blosc2_cbuffer_sizes(data_dest, &osize, &csize, &blocksize)); printf("osize, csize, blocksize: %d, %d, %d\n", osize, csize, blocksize); } blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/get_set_slice.c000066400000000000000000000070431521743436100177310ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating use of the Blosc filter from C code. To compile this program: $ gcc get_set_slice.c -o get_set_slice -lblosc2 To run: $ ./get_set_slice Blosc version info: 2.3.2.dev ($Date:: 2022-08-24 #$) Compression ratio: 381.5 MB -> 3.1 MB (125.1x) Compression time: 2.84 s, 134.5 MB/s set_slice_buffer time: 0.0279 s, 13692.3 MB/s get_slice_buffer time: 0.00926 s, 41193.4 MB/s Successful roundtrip data <-> schunk ! */ #include #include #define KB 1024. #define MB (1024*KB) #define GB (1024*MB) #define CHUNKSIZE (1000 * 1000) #define NCHUNKS 100 #define NTHREADS 4 int main(void) { blosc2_init(); static int32_t data[CHUNKSIZE]; int32_t isize = CHUNKSIZE * sizeof(int32_t); int64_t nbytes, cbytes; blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; blosc2_schunk* schunk; int i, nchunk; blosc_timestamp_t last, current; double ttotal; printf("Blosc version info: %s (%s)\n", blosc2_get_version_string(), BLOSC2_VERSION_DATE); /* Create a super-chunk container */ cparams.typesize = sizeof(int32_t); cparams.clevel = 9; cparams.nthreads = NTHREADS; dparams.nthreads = NTHREADS; blosc2_storage storage = {.cparams=&cparams, .dparams=&dparams}; schunk = blosc2_schunk_new(&storage); blosc_set_timestamp(&last); for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { for (i = 0; i < CHUNKSIZE; i++) { data[i] = i * nchunk; } int64_t nchunks = blosc2_schunk_append_buffer(schunk, data, isize); if (nchunks != nchunk + 1) { printf("Unexpected nchunks!"); return -1; } } /* Gather some info */ nbytes = schunk->nbytes; cbytes = schunk->cbytes; blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Compression ratio: %.1f MB -> %.1f MB (%.1fx)\n", (double)nbytes / MB, (double)cbytes / MB, (1. * (double)nbytes) / (double)cbytes); printf("Compression time: %.3g s, %.1f MB/s\n", ttotal, (double)nbytes / (ttotal * MB)); /* Set slice and get same slice */ int64_t start = CHUNKSIZE + 3; int64_t stop = CHUNKSIZE * 2 + 7; int32_t *buffer = malloc((stop - start) * schunk->typesize); for (i = 0; i < (stop - start); ++i) { buffer[i] = i + NCHUNKS * CHUNKSIZE; } blosc_set_timestamp(&last); int rc = blosc2_schunk_set_slice_buffer(schunk, start, stop, buffer); if (rc < 0) { printf("ERROR: %d, cannot set slice correctly.\n", rc); return rc; } blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("set_slice_buffer time: %.3g s, %.1f MB/s\n", ttotal, (double)nbytes / (ttotal * MB)); int32_t *res = malloc((stop - start) * schunk->typesize); blosc_set_timestamp(&last); rc = blosc2_schunk_get_slice_buffer(schunk, start, stop, res); if (rc < 0) { printf("ERROR: %d, cannot get slice correctly.\n", rc); return rc; } blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("get_slice_buffer time: %.3g s, %.1f MB/s\n", ttotal, (double)nbytes / (ttotal * MB)); for (i = 0; i < (stop - start); ++i) { if(buffer[i] != res[i]) { printf("Bad roundtrip\n"); return -1; } } printf("Successful roundtrip data <-> schunk !\n"); /* Free resources */ /* Destroy the super-chunk */ blosc2_schunk_free(schunk); free(buffer); free(res); blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/instrument_codec.c000066400000000000000000000054251521743436100204670ustar00rootroot00000000000000/* Copyright (c) 2022 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating the use of a Blosc from C code. To compile this program: $ gcc -O instrument_codec.c -o instrument_codec -lblosc2 To run: $ ./instrument_codec Blosc version info: 2.0.0a2 ($Date:: 2016-01-08 #$) Compression: 40000000 -> 999393 (40.0x) Correctly extracted 5 elements from compressed chunk! Decompression successful! Successful roundtrip! */ #include #include "blosc2.h" #define SIZE (100 * 1000) #define BLOCKSIZE (40000) #define NBLOCKS (SIZE / BLOCKSIZE) #define NTHREADS 2 int main(void) { blosc2_init(); float *data = malloc(SIZE * sizeof(float)); float *data_out = malloc(SIZE * sizeof(float)); float *data_dest = malloc(SIZE * sizeof(float)); int isize = SIZE * sizeof(float), osize = SIZE * sizeof(float); int dsize = SIZE * sizeof(float), csize; int i; blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; blosc2_context *cctx, *dctx; /* Initialize dataset */ for (i = 0; i < SIZE; i++) { data[i] = (float)i; } printf("Blosc version info: %s (%s)\n", BLOSC2_VERSION_STRING, BLOSC2_VERSION_DATE); /* Create a context for compression */ cparams.typesize = sizeof(float); cparams.compcode = BLOSC_BLOSCLZ; cparams.splitmode = BLOSC_NEVER_SPLIT; cparams.filters[BLOSC2_MAX_FILTERS - 1] = BLOSC_SHUFFLE; cparams.clevel = 5; cparams.nthreads = NTHREADS; cparams.instr_codec = true; cparams.blocksize = BLOCKSIZE; cctx = blosc2_create_cctx(cparams); /* Do the actual compression */ csize = blosc2_compress_ctx(cctx, data, isize, data_out, osize); blosc2_free_ctx(cctx); if (csize == 0) { printf("Buffer is incompressible. Giving up.\n"); return 1; } else if (csize < 0) { printf("Compression error. Error code: %d\n", csize); return csize; } printf("Compression: %d -> %d (%.1fx)\n", isize, csize, (1. * isize) / csize); /* Create a context for decompression */ dparams.nthreads = NTHREADS; dctx = blosc2_create_dctx(dparams); /* Decompress */ dsize = blosc2_decompress_ctx(dctx, data_out, csize, data_dest, dsize); blosc2_free_ctx(dctx); if (dsize < 0) { printf("Decompression error. Error code: %d\n", dsize); return dsize; } printf("Decompression successful!\n"); blosc2_instr *datai = (blosc2_instr *)data_dest; for (i = 0; i < dsize / (int) sizeof(blosc2_instr); i++) { printf("%.3g - ", datai->cratio); printf("%.3g - ", datai->cspeed); printf("%.3g - ", datai->filter_speed); printf("%d ", datai->flags[0]); printf("\n"); datai++; } free(data); free(data_out); free(data_dest); blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/many_compressors.c000066400000000000000000000055161521743436100205260ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating use of the Blosc filter from C code. To compile this program: $ gcc -O many_compressors.c -o many_compressors -lblosc2 To run: $ ./many_compressors Blosc version info: 2.0.0a6.dev ($Date:: 2018-05-18 #$) Using 4 threads (previously using 1) Using blosclz compressor Compression: 4000000 -> 57577 (69.5x) Successful roundtrip! Using lz4 compressor Compression: 4000000 -> 97276 (41.1x) Successful roundtrip! Using lz4hc compressor Compression: 4000000 -> 38314 (104.4x) Successful roundtrip! Using zlib compressor Compression: 4000000 -> 21486 (186.2x) Successful roundtrip! Using zstd compressor Compression: 4000000 -> 10692 (374.1x) Successful roundtrip! */ #include #include #define SIZE 100*100*100 #define SHAPE {100,100,100} #define CHUNKSHAPE {1,100,100} int main(void) { static float data[SIZE]; static float data_out[SIZE]; static float data_dest[SIZE]; int isize = SIZE * sizeof(float), osize = SIZE * sizeof(float); int dsize = SIZE * sizeof(float), csize; int16_t nthreads, pnthreads; int i; char* compressors[] = {"blosclz", "lz4", "lz4hc", "zlib", "zstd"}; int ccode, rcode; for (i = 0; i < SIZE; i++) { data[i] = i; } /* Register the filter with the library */ printf("Blosc version info: %s (%s)\n", BLOSC2_VERSION_STRING, BLOSC2_VERSION_DATE); /* Initialize the Blosc compressor */ blosc_init(); nthreads = 4; pnthreads = blosc_set_nthreads(nthreads); printf("Using %d threads (previously using %d)\n", nthreads, pnthreads); /* Tell Blosc to use some number of threads */ for (ccode = 0; ccode < 5; ccode++) { rcode = blosc_set_compressor(compressors[ccode]); if (rcode < 0) { printf("Error setting %s compressor. It really exists?", compressors[ccode]); return rcode; } printf("Using %s compressor\n", compressors[ccode]); /* Compress with clevel=5 and shuffle active */ csize = blosc_compress(5, 1, sizeof(float), isize, data, data_out, osize); if (csize < 0) { printf("Compression error. Error code: %d\n", csize); return csize; } printf("Compression: %d -> %d (%.1fx)\n", isize, csize, (1. * isize) / csize); /* Decompress */ dsize = blosc_decompress(data_out, data_dest, dsize); if (dsize < 0) { printf("Decompression error. Error code: %d\n", dsize); return dsize; } /* After using it, destroy the Blosc environment */ blosc_destroy(); for (i = 0; i < SIZE; i++) { if (data[i] != data_dest[i]) { printf("Decompressed data differs from original!\n"); return -1; } } printf("Successful roundtrip!\n"); } return 0; } c-blosc2-3.1.5/examples/multithread.c000066400000000000000000000054641521743436100174470ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating use of the Blosc filter from C code. To compile this program using gcc or clang: $ gcc/clang multithread.c -o multithread -lblosc2 -lpthread or, if you don't have the blosc library installed: $ gcc -O3 -msse2 multithread.c ../blosc/!(*avx2*)*.c -I../blosc -o multithread -lpthread or alternatively: $ gcc -O3 -msse2 multithread.c -I../blosc -o multithread -L../build/blosc -lblosc2 $ export LD_LIBRARY_PATH=../build/blosc Using MSVC on Windows: $ cl /Ox /Femultithread.exe /Iblosc multithread.c blosc\*.c To run: $ ./multithread Blosc version info: 1.4.2.dev ($Date:: 2014-07-08 #$) Using 1 threads (previously using 1) Compression: 4000000 -> 158494 (25.2x) Successful roundtrip! Using 2 threads (previously using 1) Compression: 4000000 -> 158494 (25.2x) Successful roundtrip! Using 3 threads (previously using 2) Compression: 4000000 -> 158494 (25.2x) Successful roundtrip! Using 4 threads (previously using 3) Compression: 4000000 -> 158494 (25.2x) Successful roundtrip! */ #include #include #include #define SIZE (1000 * 1000) int main(void) { static float data[SIZE]; static float data_out[SIZE]; static float data_dest[SIZE]; size_t isize = SIZE * sizeof(float), osize = SIZE * sizeof(float); int dsize, csize; int16_t nthreads, pnthreads; int i; for (i = 0; i < SIZE; i++) { data[i] = (float)i; } /* Register the filter with the library */ printf("Blosc version info: %s (%s)\n", BLOSC2_VERSION_STRING, BLOSC2_VERSION_DATE); /* Initialize the Blosc compressor */ blosc2_init(); /* Tell Blosc to use some number of threads */ for (nthreads = 1; nthreads <= 4; nthreads++) { pnthreads = blosc2_set_nthreads(nthreads); printf("Using %d threads (previously using %d)\n", nthreads, pnthreads); /* Compress with clevel=5 and shuffle active */ csize = blosc1_compress(5, 1, sizeof(float), isize, data, data_out, osize); if (csize < 0) { printf("Compression error. Error code: %d\n", csize); return csize; } printf("Compression: %" PRId64 " -> %d (%.1fx)\n", (int64_t)isize, csize, (1. * (double)isize) / csize); /* Decompress */ dsize = blosc1_decompress(data_out, data_dest, isize); if (dsize < 0) { printf("Decompression error. Error code: %d\n", dsize); return dsize; } for (i = 0; i < SIZE; i++) { if (data[i] != data_dest[i]) { printf("Decompressed data differs from original!\n"); return -1; } } printf("Successful roundtrip!\n"); } /* After using it, destroy the Blosc environment */ blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/noinit.c000066400000000000000000000044101521743436100164130ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating that from 1.9.0 on, Blosc does not need to be initialized (although it is recommended). To compile this program: $ gcc noinit.c -o noinit -lblosc or, if you don't have the blosc library installed yet: $ gcc -O3 -msse2 noinit.c -I../blosc -o noinit -L../build/blosc -lblosc2 $ export LD_LIBRARY_PATH=../build/blosc Using MSVC on Windows: $ cl /arch:SSE2 /Ox /Fenoinit.exe /Iblosc examples\noinit.c blosc\blosc.c blosc\blosclz.c blosc\shuffle.c blosc\shuffle-sse2.c blosc\shuffle-generic.c blosc\bitshuffle-generic.c blosc\bitshuffle-sse2.c To run: $ ./noinit Blosc version info: 1.8.2.dev ($Date:: 2016-04-08 #$) Compression: 4000000 -> 158788 (25.2x) Decompression successful! Successful roundtrip! */ #include #include #define SIZE (100*100*100) int main(void){ static float data[SIZE]; static float data_out[SIZE]; static float data_dest[SIZE]; int isize = SIZE*sizeof(float), osize = SIZE*sizeof(float); int dsize = SIZE*sizeof(float), csize; int i; for(i=0; i %d (%.1fx)\n", isize, csize, (1.*isize) / csize); /* Decompress */ dsize = blosc1_decompress(data_out, data_dest, dsize); if (dsize < 0) { printf("Decompression error. Error code: %d\n", dsize); return dsize; } printf("Decompression successful!\n"); for(i=0;i https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating use of the Blosc2 postfilters in schunks. */ #include #include #define KB 1024. #define MB (1024*KB) #define GB (1024*MB) #define CHUNKSIZE (20 * 1000) #define NCHUNKS 1000 #define NTHREADS 8 typedef struct { int32_t mult; int32_t add; } my_postparams; int postfilter_func(blosc2_postfilter_params *postparams) { int nelems = postparams->size / postparams->typesize; int32_t *in = ((int32_t *)(postparams->input)); int32_t *out = ((int32_t *)(postparams->output)); my_postparams *user_data = postparams->user_data; for (int i = 0; i < nelems; i++) { out[i] = in[i] * user_data->mult + user_data->add ; } return 0; } int main(void) { blosc2_init(); static int32_t data[CHUNKSIZE]; static int32_t data_dest[CHUNKSIZE]; int32_t isize = CHUNKSIZE * sizeof(int32_t); int i, nchunk; int64_t nchunks; blosc_timestamp_t last; printf("Blosc version info: %s (%s)\n", BLOSC2_VERSION_STRING, BLOSC2_VERSION_DATE); /* Create a super-chunk container */ blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = sizeof(int32_t); cparams.compcode = BLOSC_LZ4HC; cparams.clevel = 1; cparams.nthreads = NTHREADS; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; dparams.nthreads = NTHREADS; // Set some postfilter parameters and function dparams.postfilter = (blosc2_postfilter_fn)postfilter_func; // We need to zero the contents of the postparams blosc2_postfilter_params postparams = {0}; // Additional user params my_postparams user_data = {2, 1}; postparams.user_data = (void*)&user_data; dparams.postparams = &postparams; blosc2_storage storage = {.cparams=&cparams, .dparams=&dparams}; blosc2_schunk* schunk = blosc2_schunk_new(&storage); // Add some data blosc_set_timestamp(&last); for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { for (i = 0; i < CHUNKSIZE; i++) { data[i] = i + nchunk * CHUNKSIZE; } nchunks = blosc2_schunk_append_buffer(schunk, data, isize); if (nchunks != nchunk + 1) { printf("blosc2_schunk_append_buffer is not working correctly"); return BLOSC2_ERROR_FAILURE; } } /* Retrieve and decompress the chunks from the super-chunks and compare values */ for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { int32_t dsize = blosc2_schunk_decompress_chunk(schunk, nchunk, data_dest, isize); if (dsize < 0) { printf("Decompression error in schunk. Error code: %d\n", dsize); return dsize; } /* Check integrity of this chunk */ for (i = 0; i < CHUNKSIZE; i++) { if (data_dest[i] != (i + nchunk * CHUNKSIZE) * user_data.mult + user_data.add) { printf("data mismatch!"); return BLOSC2_ERROR_FAILURE; } } } printf("Postfilter is working correctly!\n"); /* Free resources */ blosc2_schunk_free(schunk); blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/schunk_simple.c000066400000000000000000000062261521743436100177660ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating use of the Blosc filter from C code. To compile this program: $ gcc schunk_simple.c -o schunk_simple -lblosc2 To run: $ ./schunk_simple Blosc version info: 2.0.0-beta.1 ($Date:: 2019-08-09 #$) Compression ratio: 381.5 MB -> 12.2 MB (31.2x) Compression time: 0.119 s, 3192.9 MB/s Decompression time: 0.035 s, 10888.3 MB/s Successful roundtrip data <-> schunk ! */ #include #include #define KB 1024. #define MB (1024*KB) #define GB (1024*MB) #define CHUNKSIZE (1000 * 1000) #define NCHUNKS 100 #define NTHREADS 4 int main(void) { blosc2_init(); static int32_t data[CHUNKSIZE]; static int32_t data_dest[CHUNKSIZE]; int32_t isize = CHUNKSIZE * sizeof(int32_t); int dsize; int64_t nbytes, cbytes; blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; blosc2_schunk* schunk; int i, nchunk; blosc_timestamp_t last, current; double ttotal; printf("Blosc version info: %s (%s)\n", blosc2_get_version_string(), BLOSC2_VERSION_DATE); /* Create a super-chunk container */ cparams.typesize = sizeof(int32_t); cparams.clevel = 9; cparams.nthreads = NTHREADS; dparams.nthreads = NTHREADS; blosc2_storage storage = {.cparams=&cparams, .dparams=&dparams}; schunk = blosc2_schunk_new(&storage); blosc_set_timestamp(&last); for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { for (i = 0; i < CHUNKSIZE; i++) { data[i] = i * nchunk; } int64_t nchunks = blosc2_schunk_append_buffer(schunk, data, isize); if (nchunks != nchunk + 1) { printf("Unexpected nchunks!"); return -1; } } /* Gather some info */ nbytes = schunk->nbytes; cbytes = schunk->cbytes; blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Compression ratio: %.1f MB -> %.1f MB (%.1fx)\n", (double)nbytes / MB, (double)cbytes / MB, (1. * (double)nbytes) / (double)cbytes); printf("Compression time: %.3g s, %.1f MB/s\n", ttotal, (double)nbytes / (ttotal * MB)); /* Retrieve and decompress the chunks (0-based count) */ blosc_set_timestamp(&last); for (nchunk = NCHUNKS-1; nchunk >= 0; nchunk--) { dsize = blosc2_schunk_decompress_chunk(schunk, nchunk, data_dest, isize); if (dsize < 0) { printf("Decompression error. Error code: %d\n", dsize); return dsize; } } blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Decompression time: %.3g s, %.1f MB/s\n", ttotal, (double)nbytes / (ttotal * MB)); /* Check integrity of the second chunk (made of non-zeros) */ blosc2_schunk_decompress_chunk(schunk, 1, data_dest, isize); for (i = 0; i < CHUNKSIZE; i++) { if (data_dest[i] != i) { printf("Decompressed data differs from original %d, %d!\n", i, data_dest[i]); return -1; } } printf("Successful roundtrip data <-> schunk !\n"); /* Free resources */ /* Destroy the super-chunk */ blosc2_schunk_free(schunk); blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/sframe_simple.c000066400000000000000000000060651521743436100177510ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating use of the Blosc filter from C code. To compile this program: $ gcc sframe_simple.c -o sframe_simple -lblosc2 To run: $ ./sframe_simple */ #include #include #define KB 1024. #define MB (1024*KB) #define GB (1024*MB) #define CHUNKSIZE (1000 * 1000) #define NCHUNKS 100 #define NTHREADS 4 int main(void) { blosc2_init(); static int32_t data[CHUNKSIZE]; static int32_t data_dest[CHUNKSIZE]; int32_t isize = CHUNKSIZE * sizeof(int32_t); int dsize; int64_t nbytes, cbytes; blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; blosc2_schunk* schunk; int i, nchunk; blosc_timestamp_t last, current; double ttotal; printf("Blosc version info: %s (%s)\n", blosc2_get_version_string(), BLOSC2_VERSION_DATE); /* Create a super-chunk container */ cparams.typesize = sizeof(int32_t); cparams.clevel = 9; cparams.nthreads = NTHREADS; dparams.nthreads = NTHREADS; blosc2_storage storage = {false, "dir1.b2frame", .cparams=&cparams, .dparams=&dparams}; /* Remove directory */ blosc2_remove_dir(storage.urlpath); schunk = blosc2_schunk_new(&storage); blosc_set_timestamp(&last); for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { for (i = 0; i < CHUNKSIZE; i++) { data[i] = i * nchunk; } int64_t nchunks = blosc2_schunk_append_buffer(schunk, data, isize); if (nchunks != nchunk + 1) { printf("Unexpected nchunks!"); return -1; } } /* Gather some info */ nbytes = schunk->nbytes; cbytes = schunk->cbytes; blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Compression ratio: %.1f MB -> %.1f MB (%.1fx)\n", (double)nbytes / MB, (double)cbytes / MB, (1. * (double)nbytes) / (double)cbytes); printf("Compression time: %.3g s, %.1f MB/s\n", ttotal, (double)nbytes / (ttotal * MB)); /* Retrieve and decompress the chunks (0-based count) */ blosc_set_timestamp(&last); for (nchunk = NCHUNKS-1; nchunk >= 0; nchunk--) { dsize = blosc2_schunk_decompress_chunk(schunk, nchunk, data_dest, isize); if (dsize < 0) { printf("Decompression error. Error code: %d\n", dsize); return dsize; } } blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Decompression time: %.3g s, %.1f MB/s\n", ttotal, (double)nbytes / (ttotal * MB)); /* Check integrity of the second chunk (made of non-zeros) */ blosc2_schunk_decompress_chunk(schunk, 1, data_dest, isize); for (i = 0; i < CHUNKSIZE; i++) { if (data_dest[i] != i) { printf("Decompressed data differs from original %d, %d!\n", i, data_dest[i]); return -1; } } printf("Successful roundtrip data <-> schunk !\n"); /* Remove directory */ blosc2_remove_dir(storage.urlpath); /* Free resources */ /* Destroy the super-chunk */ blosc2_schunk_free(schunk); blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/simple.c000066400000000000000000000047671521743436100164230ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating the use of a Blosc from C code. To compile this program: $ gcc -O simple.c -o simple -lblosc2 To run: $ ./simple Blosc version info: 2.2.1.dev ($Date:: 2022-07-05 #$) Compression: 40000000 -> 172176 (232.3x) Correctly extracted 5 elements from compressed chunk! Decompression successful! Successful roundtrip! */ #include #include #include "blosc2.h" #define SIZE (10 * 1000 * 1000) #define NTHREADS 2 int main(void) { static float data[SIZE]; static float data_out[SIZE]; static float data_dest[SIZE]; float data_subset[5]; float data_subset_ref[5] = {5, 6, 7, 8, 9}; size_t isize = SIZE * sizeof(float), osize = SIZE * sizeof(float); int dsize = SIZE * sizeof(float), csize; int i, ret; /* Initialize the Blosc compressor */ blosc2_init(); blosc2_set_nthreads(NTHREADS); printf("Blosc version info: %s (%s)\n", BLOSC2_VERSION_STRING, BLOSC2_VERSION_DATE); for (i = 0; i < SIZE; i++) { data[i] = (float)i; } /* Compress with clevel=5 and shuffle active */ csize = blosc1_compress(5, BLOSC_BITSHUFFLE, sizeof(float), isize, data, data_out, osize); if (csize == 0) { printf("Buffer is incompressible. Giving up.\n"); return 1; } else if (csize < 0) { printf("Compression error. Error code: %d\n", csize); return csize; } printf("Compression: %" PRId64 " -> %d (%.1fx)\n", (int64_t)isize, csize, (1. * (double)isize) / csize); ret = blosc1_getitem(data_out, 5, 5, data_subset); if (ret < 0) { printf("Error in blosc1_getitem(). Giving up.\n"); return 1; } for (i = 0; i < 5; i++) { if (data_subset[i] != data_subset_ref[i]) { printf("blosc1_getitem() fetched data differs from original!\n"); return -1; } } printf("Correctly extracted 5 elements from compressed chunk!\n"); /* Decompress */ dsize = blosc1_decompress(data_out, data_dest, (size_t)dsize); if (dsize < 0) { printf("Decompression error. Error code: %d\n", dsize); return dsize; } printf("Decompression successful!\n"); for (i = 0; i < SIZE; i++) { if (data[i] != data_dest[i]) { printf("Decompressed data differs from original!\n"); return -1; } } printf("Successful roundtrip!\n"); /* After using it, destroy the Blosc environment */ blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/urcodecs.c000066400000000000000000000114731521743436100167310ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating use of the Blosc filter from C code. To compile this program: $ gcc urcodecs.c -o urcodecs -lblosc2 To run: $ ./urcodecs */ #include #include #define KB 1024. #define MB (1024*KB) #define GB (1024*MB) #define CHUNKSIZE (1000 * 1000) #define NCHUNKS 100 #define NTHREADS 4 int codec_encoder(const uint8_t* input, int32_t input_len, uint8_t* output, int32_t output_len, uint8_t meta, blosc2_cparams* cparams, const void* chunk) { BLOSC_UNUSED_PARAM(meta); BLOSC_UNUSED_PARAM(chunk); if (cparams->schunk == NULL) { return -1; } if (cparams->typesize != 4) { fprintf(stderr, "Itemsize %d != 4", cparams->typesize); return BLOSC2_ERROR_FAILURE; } int32_t nelem = input_len / 4; int32_t *in_ = ((int32_t *) input); int32_t *out_ = ((int32_t *) output); // Check that is an arange int32_t start = in_[0]; int32_t step = in_[1] - start; for (int i = 1; i < nelem - 1; ++i) { if (in_[i + 1] - in_[i] != step) { fprintf(stderr, "Buffer is not an arange"); return BLOSC2_ERROR_FAILURE; } } if (8 > output_len) { return BLOSC2_ERROR_WRITE_BUFFER; } out_[0] = start; out_[1] = step; return 8; } int codec_decoder(const uint8_t* input, int32_t input_len, uint8_t* output, int32_t output_len, uint8_t meta, blosc2_dparams *dparams, const void* chunk) { BLOSC_UNUSED_PARAM(meta); BLOSC_UNUSED_PARAM(chunk); if (dparams->schunk == NULL) { return -1; } int32_t nelem = output_len / 4; int32_t *in_ = ((int32_t *) input); int32_t *out_ = ((int32_t *) output); if (8 > input_len) { return BLOSC2_ERROR_WRITE_BUFFER; } int32_t start = in_[0]; int32_t step = in_[1]; for (int i = 0; i < nelem; ++i) { out_[i] = start + i * step; } return output_len; } int main(void) { blosc2_init(); static int32_t data[CHUNKSIZE]; static int32_t data_dest[CHUNKSIZE]; int32_t isize = CHUNKSIZE * sizeof(int32_t); int dsize; int64_t nbytes, cbytes; blosc2_codec udcodec; udcodec.compcode = 244; udcodec.version = 1; udcodec.complib = 1; udcodec.compname = "udcodec"; udcodec.encoder = codec_encoder; udcodec.decoder = codec_decoder; int rc = blosc2_register_codec(&udcodec); if (rc < 0) { printf("Cannot register codec!"); return -1; } blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.compcode = 244; for (int i = 0; i < BLOSC2_MAX_FILTERS; ++i) { cparams.filters[i] = 0; } blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; blosc2_schunk* schunk; int i, nchunk; blosc_timestamp_t last, current; double ttotal; printf("Blosc version info: %s (%s)\n", blosc2_get_version_string(), BLOSC2_VERSION_DATE); /* Create a super-chunk container */ cparams.typesize = sizeof(int32_t); cparams.clevel = 9; blosc2_storage storage = {.cparams=&cparams, .dparams=&dparams}; schunk = blosc2_schunk_new(&storage); blosc_set_timestamp(&last); for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { for (i = 0; i < CHUNKSIZE; i++) { data[i] = i * nchunk; } int64_t nchunks = blosc2_schunk_append_buffer(schunk, data, isize); if (nchunks != nchunk + 1) { printf("Unexpected nchunks!"); return -1; } } /* Gather some info */ nbytes = schunk->nbytes; cbytes = schunk->cbytes; blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Compression ratio: %.1f MB -> %.1f MB (%.1fx)\n", (double)nbytes / MB, (double)cbytes / MB, (1. * (double)nbytes) / (double)cbytes); printf("Compression time: %.3g s, %.1f MB/s\n", ttotal, (double)nbytes / (ttotal * MB)); /* Retrieve and decompress the chunks (0-based count) */ blosc_set_timestamp(&last); for (nchunk = NCHUNKS-1; nchunk >= 0; nchunk--) { dsize = blosc2_schunk_decompress_chunk(schunk, nchunk, data_dest, isize); if (dsize < 0) { printf("Decompression error. Error code: %d\n", dsize); return dsize; } } blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Decompression time: %.3g s, %.1f MB/s\n", ttotal, (double)nbytes / (ttotal * MB)); /* Check integrity of the second chunk (made of non-zeros) */ blosc2_schunk_decompress_chunk(schunk, 1, data_dest, isize); for (i = 0; i < CHUNKSIZE; i++) { if (data_dest[i] != i) { printf("Decompressed data differs from original %d, %d!\n", i, data_dest[i]); return -1; } } printf("Successful roundtrip data <-> schunk !\n"); /* Free resources */ /* Destroy the super-chunk */ blosc2_schunk_free(schunk); blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/urfilters.c000066400000000000000000000112041521743436100171310ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating use of the Blosc filter from C code. To compile this program: $ gcc urfilters.c -o urfilters -lblosc2 To run: $ ./urfilters */ #include "stdio.h" #include #define KB 1024. #define MB (1024*KB) #define GB (1024*MB) #define CHUNKSIZE (1000 * 1000) #define NCHUNKS 100 #define NTHREADS 4 int filter_forward(const uint8_t* src, uint8_t* dest, int32_t size, uint8_t meta, blosc2_cparams *cparams, uint8_t id) { BLOSC_UNUSED_PARAM(meta); BLOSC_UNUSED_PARAM(id); blosc2_schunk *schunk = cparams->schunk; for (int i = 0; i < size / schunk->typesize; ++i) { switch (schunk->typesize) { case 8: ((int64_t *) dest)[i] = ((int64_t *) src)[i] + 1; break; case 4: ((int32_t *) dest)[i] = ((int32_t *) src)[i] + 1; break; case 2: ((int16_t *) dest)[i] = (int16_t)(((int16_t *) src)[i] + 1); break; default: fprintf(stderr, "Item size %d not supported", schunk->typesize); return BLOSC2_ERROR_FAILURE; } } return BLOSC2_ERROR_SUCCESS; } int filter_backward(const uint8_t* src, uint8_t* dest, int32_t size, uint8_t meta, blosc2_dparams *dparams, uint8_t id) { BLOSC_UNUSED_PARAM(meta); BLOSC_UNUSED_PARAM(id); blosc2_schunk *schunk = dparams->schunk; for (int i = 0; i < size / schunk->typesize; ++i) { switch (schunk->typesize) { case 8: ((int64_t *) dest)[i] = ((int64_t *) src)[i] - 1; break; case 4: ((int32_t *) dest)[i] = ((int32_t *) src)[i] - 1; break; case 2: ((int16_t *) dest)[i] = (int16_t)(((int16_t *) src)[i] - 1); break; default: fprintf(stderr, "Item size %d not supported", schunk->typesize); return BLOSC2_ERROR_FAILURE; } } return BLOSC2_ERROR_SUCCESS; } int main(void) { blosc2_init(); static int32_t data[CHUNKSIZE]; static int32_t data_dest[CHUNKSIZE]; int32_t isize = CHUNKSIZE * sizeof(int32_t); int dsize; int64_t nbytes, cbytes; blosc2_filter urfilter; urfilter.id = 250; urfilter.name = "urfilter_example"; urfilter.version = 1; urfilter.forward = filter_forward; urfilter.backward = filter_backward; blosc2_register_filter(&urfilter); blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.filters[4] = urfilter.id; cparams.filters_meta[4] = 0; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; blosc2_schunk* schunk; int i, nchunk; blosc_timestamp_t last, current; double ttotal; printf("Blosc version info: %s (%s)\n", blosc2_get_version_string(), BLOSC2_VERSION_DATE); /* Create a super-chunk container */ cparams.typesize = sizeof(int32_t); cparams.clevel = 9; blosc2_storage storage = {.cparams=&cparams, .dparams=&dparams}; schunk = blosc2_schunk_new(&storage); blosc_set_timestamp(&last); for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { for (i = 0; i < CHUNKSIZE; i++) { data[i] = i * nchunk; } int64_t nchunks = blosc2_schunk_append_buffer(schunk, data, isize); if (nchunks != nchunk + 1) { printf("Unexpected nchunks!"); return -1; } } /* Gather some info */ nbytes = schunk->nbytes; cbytes = schunk->cbytes; blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Compression ratio: %.1f MB -> %.1f MB (%.1fx)\n", (double)nbytes / MB, (double)cbytes / MB, (1. * (double)nbytes) / (double)cbytes); printf("Compression time: %.3g s, %.1f MB/s\n", ttotal, (double)nbytes / (ttotal * MB)); /* Retrieve and decompress the chunks (0-based count) */ blosc_set_timestamp(&last); for (nchunk = NCHUNKS-1; nchunk >= 0; nchunk--) { dsize = blosc2_schunk_decompress_chunk(schunk, nchunk, data_dest, isize); if (dsize < 0) { printf("Decompression error. Error code: %d\n", dsize); return dsize; } } blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Decompression time: %.3g s, %.1f MB/s\n", ttotal, (double)nbytes / (ttotal * MB)); /* Check integrity of the second chunk (made of non-zeros) */ blosc2_schunk_decompress_chunk(schunk, 1, data_dest, isize); for (i = 0; i < CHUNKSIZE; i++) { if (data_dest[i] != i) { printf("Decompressed data differs from original %d, %d!\n", i, data_dest[i]); return -1; } } printf("Successful roundtrip data <-> schunk !\n"); /* Free resources */ /* Destroy the super-chunk */ blosc2_schunk_free(schunk); blosc2_destroy(); return 0; } c-blosc2-3.1.5/examples/vlblocks.c000066400000000000000000000111651521743436100167370ustar00rootroot00000000000000/* Copyright (c) 2026 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating how to store strings of different lengths inside a single variable-length-block chunk. */ #include #include #include #include "blosc2.h" static const char string0[] = "short"; static const char string1[] = "a bit longer string"; static const char string2[] = "this is the longest string stored in the chunk"; static const void *srcs[] = { string0, string1, string2, }; static const int32_t srcsizes[] = { (int32_t)sizeof(string0), (int32_t)sizeof(string1), (int32_t)sizeof(string2), }; static const char *labels[] = { "string0", "string1", "string2", }; static int32_t total_nbytes(void) { return srcsizes[0] + srcsizes[1] + srcsizes[2]; } static int print_strings(const char *title, void **buffers, const int32_t *sizes, int32_t nblocks) { printf("%s\n", title); for (int32_t i = 0; i < nblocks; ++i) { if (sizes[i] != srcsizes[i]) { fprintf(stderr, "unexpected size for %s: %d != %d\n", labels[i], sizes[i], srcsizes[i]); return -1; } if (memcmp(buffers[i], srcs[i], (size_t)srcsizes[i]) != 0) { fprintf(stderr, "content mismatch for %s\n", labels[i]); return -1; } printf(" %s (%d bytes): %s\n", labels[i], sizes[i], (char *)buffers[i]); } return 0; } int main(void) { const char *urlpath = "vlblocks_example.b2frame"; uint8_t *buffers[3] = {NULL}; int32_t sizes[3] = {0}; uint8_t *chunk = NULL; blosc2_context *cctx = NULL; blosc2_context *dctx = NULL; blosc2_schunk *schunk = NULL; uint8_t *stored_chunk = NULL; bool needs_free = false; int rc = 1; blosc2_init(); blosc2_remove_urlpath(urlpath); blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = 1; cparams.nthreads = 2; cctx = blosc2_create_cctx(cparams); if (cctx == NULL) { fprintf(stderr, "cannot create compression context\n"); goto cleanup; } blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; dparams.nthreads = 2; dctx = blosc2_create_dctx(dparams); if (dctx == NULL) { fprintf(stderr, "cannot create decompression context\n"); goto cleanup; } int32_t destsize = total_nbytes() + BLOSC2_MAX_OVERHEAD + 64; chunk = malloc((size_t)destsize); if (chunk == NULL) { fprintf(stderr, "cannot allocate chunk buffer\n"); goto cleanup; } int32_t cbytes = blosc2_vlcompress_ctx(cctx, srcs, srcsizes, 3, chunk, destsize); if (cbytes <= 0) { fprintf(stderr, "VL-block compression failed: %d\n", cbytes); goto cleanup; } printf("stored %d strings in one VL-block chunk: %d -> %d bytes\n", 3, total_nbytes(), cbytes); int32_t nblocks = blosc2_vldecompress_ctx(dctx, chunk, cbytes, (void **)buffers, sizes, 3); if (nblocks != 3) { fprintf(stderr, "VL-block decompression failed: %d\n", nblocks); goto cleanup; } if (print_strings("decompressed directly from the chunk:", (void **)buffers, sizes, nblocks) < 0) { goto cleanup; } blosc2_storage storage = { .contiguous = true, .urlpath = (char *)urlpath, .cparams = &cparams, .dparams = &dparams, }; schunk = blosc2_schunk_new(&storage); if (schunk == NULL) { fprintf(stderr, "cannot create frame-backed schunk\n"); goto cleanup; } if (blosc2_schunk_append_chunk(schunk, chunk, true) != 1) { fprintf(stderr, "cannot append VL-block chunk to schunk\n"); goto cleanup; } blosc2_schunk_free(schunk); schunk = blosc2_schunk_open(urlpath); if (schunk == NULL) { fprintf(stderr, "cannot reopen frame-backed schunk\n"); goto cleanup; } if (blosc2_schunk_get_chunk(schunk, 0, &stored_chunk, &needs_free) < 0) { fprintf(stderr, "cannot get stored VL-block chunk back from schunk\n"); goto cleanup; } for (int i = 0; i < 3; ++i) { free(buffers[i]); buffers[i] = NULL; sizes[i] = 0; } nblocks = blosc2_vldecompress_ctx(dctx, stored_chunk, cbytes, (void **)buffers, sizes, 3); if (nblocks != 3) { fprintf(stderr, "cannot vldecompress reopened VL-block chunk: %d\n", nblocks); goto cleanup; } if (print_strings("recovered after reopening the frame:", (void **)buffers, sizes, nblocks) < 0) { goto cleanup; } rc = 0; cleanup: if (needs_free) { free(stored_chunk); } if (schunk != NULL) { blosc2_schunk_free(schunk); } if (dctx != NULL) { blosc2_free_ctx(dctx); } if (cctx != NULL) { blosc2_free_ctx(cctx); } for (int i = 0; i < 3; ++i) { free(buffers[i]); } free(chunk); blosc2_remove_urlpath(urlpath); blosc2_destroy(); return rc; } c-blosc2-3.1.5/examples/win-dynamic-linking.c000066400000000000000000000075431521743436100207750ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating use of the Blosc filter using the Windows Run-Time Dynamic Linking technique: https://msdn.microsoft.com/en-us/library/windows/desktop/ms686944(v=vs.85).aspx This allows to link your app in run-time with DLLs made with different compatible compilers (e.g. VS2013 and mingw-w64). To compile this program (be aware that you should match your compiler 32-bit/64-bit with your DLL): cl /Ox /Fewin-dynamic-linking.exe /I..\blosc win-dynamic-linking.c To run: $ win-dynamic-linking.exe Blosc version info: 1.7.0.dev Compression: 400000000 -> 19928862 (20.1x) Decompression successful! Successful roundtrip! */ #include #include #include #define SIZE 100*1000*1000 #define SHAPE {100,1000,1000} #define CHUNKSHAPE {1,1000,1000} /* Definition for the compression and decompression blosc routines */ typedef int (__cdecl* COMPRESS_CTX)(int clevel, int doshuffle, size_t typesize, size_t nbytes, const void* src, void* dest, size_t destsize, const char* compressor, size_t blocksize, int numinternalthreads); typedef int (__cdecl* DECOMPRESS_CTX)(const void* src, void* dest, size_t destsize, int numinternalthreads); typedef char* (__cdecl* GET_VERSION_STRING)(void); int main(void) { HINSTANCE BDLL; /* Handle to DLL */ COMPRESS_CTX blosc_compress_ctx; /* Function pointer for compression */ DECOMPRESS_CTX blosc_decompress_ctx; /* Function pointer for decompression */ GET_VERSION_STRING blosc_get_version_string; static float data[SIZE]; static float data_out[SIZE]; static float data_dest[SIZE]; int isize = SIZE * sizeof(float), osize = SIZE * sizeof(float); int dsize = SIZE * sizeof(float), csize; int i; BDLL = LoadLibrary(TEXT("myblosc.dll")); if (BDLL == NULL) { printf("Cannot find myblosc.dll library!\n"); goto out; } blosc_compress_ctx = (COMPRESS_CTX)GetProcAddress(BDLL, "blosc_compress_ctx"); if (!blosc_compress_ctx) { // handle the error printf("Cannot find blosc_compress_ctx() function!\n"); goto out; } blosc_decompress_ctx = (DECOMPRESS_CTX)GetProcAddress(BDLL, "blosc_decompress_ctx"); if (!blosc_decompress_ctx) { // handle the error printf("Cannot find blosc_decompress_ctx() function!\n"); goto out; } blosc_get_version_string = (GET_VERSION_STRING)GetProcAddress(BDLL, "blosc_get_version_string"); if (!blosc_get_version_string) { // handle the error printf("Cannot find blosc_get_version_string() function!\n"); goto out; } for (i = 0; i < SIZE; i++) { data[i] = i; } /* Register the filter with the library */ printf("Blosc version info: %s\n", blosc_get_version_string()); /* Compress with clevel=3, shuffle active, 16-bytes data size, blosclz and 2 threads */ csize = blosc_compress_ctx(3, 1, 16, isize, data, data_out, osize, "blosclz", 0, 2); if (csize == 0) { printf("Buffer is incompressible. Giving up.\n"); return 1; } else if (csize < 0) { printf("Compression error. Error code: %d\n", csize); return csize; } printf("Compression: %d -> %d (%.1fx)\n", isize, csize, (1. * isize) / csize); /* Decompress */ dsize = blosc_decompress_ctx(data_out, data_dest, dsize, 1); if (dsize < 0) { printf("Decompression error. Error code: %d\n", dsize); return dsize; } printf("Decompression successful!\n"); for (i = 0; i < SIZE; i++) { if (data[i] != data_dest[i]) { printf("Decompressed data differs from original!\n"); return -1; } } printf("Successful roundtrip!\n"); return 0; out: FreeLibrary(BDLL); return -1; } c-blosc2-3.1.5/examples/zstd_dict.c000066400000000000000000000061351521743436100171100ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) Example program demonstrating use of the Blosc filter from C code. To compile this program: $ gcc -O zstd_dict.c -o zstd_dict -lblosc2 To run: $ ./zstd_dict TODO ... */ #include #include #include #include "blosc2.h" #define KB 1024. #define MB (1024*KB) #define GB (1024*MB) #define CHUNKSIZE (200 * 1000) #define NCHUNKS 20 #define NTHREADS 4 int main(void) { static int64_t data[CHUNKSIZE]; static int64_t data_dest[CHUNKSIZE]; const int32_t isize = CHUNKSIZE * sizeof(int64_t); int dsize = 0; int64_t nbytes, cbytes; blosc2_schunk* schunk; int i; int64_t nchunk; int64_t nchunks; blosc_timestamp_t last, current; double ttotal; printf("Blosc version info: %s (%s)\n", BLOSC2_VERSION_STRING, BLOSC2_VERSION_DATE); /* Initialize the Blosc compressor */ blosc2_init(); /* Create a super-chunk container */ blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = 8; //cparams.filters[0] = BLOSC_DELTA; cparams.compcode = BLOSC_ZSTD; cparams.use_dict = 1; //cparams.clevel = 7; cparams.blocksize = 1024 * 4; // a page size //cparams.blocksize = 1024 * 32; cparams.nthreads = NTHREADS; blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; dparams.nthreads = NTHREADS; blosc2_storage storage = {.cparams=&cparams, .dparams=&dparams}; schunk = blosc2_schunk_new(&storage); blosc_set_timestamp(&last); for (nchunk = 0; nchunk < NCHUNKS; nchunk++) { for (i = 0; i < CHUNKSIZE; i++) { data[i] = i + (int64_t)nchunk * CHUNKSIZE; } nchunks = blosc2_schunk_append_buffer(schunk, data, isize); if (nchunks != nchunk + 1) { printf("Unexpected nchunks!"); return nchunks; } } /* Gather some info */ nbytes = schunk->nbytes; cbytes = schunk->cbytes; blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Compression ratio: %.1f MB -> %.1f MB (%.1fx)\n", (double)nbytes / MB, (double)cbytes / MB, (1. * (double)nbytes) / (double)cbytes); printf("Compression time: %.3g s, %.1f MB/s\n", ttotal, (double)nbytes / (ttotal * MB)); /* Retrieve and decompress the chunks (0-based count) */ blosc_set_timestamp(&last); for (nchunk = NCHUNKS - 1; nchunk >= 0; nchunk--) { dsize = blosc2_schunk_decompress_chunk(schunk, nchunk, data_dest, isize); } if (dsize < 0) { printf("Decompression error. Error code: %d\n", dsize); return dsize; } blosc_set_timestamp(¤t); ttotal = blosc_elapsed_secs(last, current); printf("Decompression time: %.3g s, %.1f MB/s\n", ttotal, (double)nbytes / (ttotal * MB)); /* Check integrity of the first chunk */ for (i = 0; i < CHUNKSIZE; i++) { if (data_dest[i] != (int64_t)i) { printf("Decompressed data differs from original %d, %ld!\n", i, (long)data_dest[i]); return -1; } } printf("Successful roundtrip!\n"); /* Free resources */ blosc2_schunk_free(schunk); blosc2_destroy(); return 0; } c-blosc2-3.1.5/guix.scm000077700000000000000000000000001521743436100230102.guix/modules/c-blosc2-package.scmustar00rootroot00000000000000c-blosc2-3.1.5/images/000077500000000000000000000000001521743436100143775ustar00rootroot00000000000000c-blosc2-3.1.5/images/Complete-Write-Read-B2ND.png000066400000000000000000003433151521743436100213120ustar00rootroot00000000000000PNG  IHDR{9sRGBeXIfMM*JR(iZJJ{c pHYs22(dZIDATxEz3d9a!G D"CEwD)O!{]eeDٞ[پQWoXHWuEEy{yDo~1ǜ'ߝ7NdgYYYY\`d1UAU;Vy*/Y)e˜棏>J2YaO =]<ؓ0XS C=eGS{ {>fl6AOG3,#{Þ2Sz>y‰>`O()=mUaNcg,3UOi[4iL\C=GSt{T{>fل<AϼT=]Þ.@G#6QdYCPT=12SES­iУgΰ'$6sr|l6f z=CoSգG {B[\ܲO+.=U=YYG#nO𩻨L}l6ftzT­{qP{)+->])Yܪ{ʀOG i {{agJGuϴ=c#l6&t z{=uaVn{ڰs=1gꪞ1[{={T hfl6G3nУGUT-܄=ž[Y>ÞX3EUϔ-܊`OU#t {bOl"Uݞ1YG#l6M#ѾMUgz==c3V+O' Ǡɂ=u@X-^ ]G3}أGuG#l6M3vУ}aG#QS]왛G 7a'X9gذGu>]lXfl6AG6U==S={6'tOI3M3=1P35uOe3&UnagjgNVn{T|l6f z=sQ{iR3US{Rg,VnCV,J.iJՓ{Ƣꉵpr`Oz=s=g M+aaž{>fل<9QգG3FأV{VL/ .aO3e2{3%أG#l6&hߦ[aOo+7aϰܚ>!KuO4왓gnnE UG#MuvnflTXg'fE#rݞX[SaOUOKӦ{{bgްG;7fl6ېAOW9gG3^UO Ug\NݞÞ)Y.bOmӆG p3z=uaO -nVnڹ |>6f y=ok h6z=S=Mž maϘT=cpi[#Q3f#l6&zTh&XgVnM{VyK㏫2{Ʀ꙲[z=žž {rSݣggGcl6G#虂G#LYk+{Z=mz`O3t 1{#nVn*f y=݃aϼU=s#iRS L]3 122-a&Q#|>6f zzTL_#/쉱{SdmuO+KUn3t2Q{==}l6fz=-b& |ڂ=UO߰'Yu=]z`OӠk 1i ԩ#4|ʭ|>fyzoz=mZ {=U=MܺW4{ƮꙣG3GأgVnڹ |>6fh&LmVdݞnaO'4 {ƢꙚ{=aX=ڹ |>6f z==z=]ZY> ; >žy1XiTQd1z##>1\MugG[7fy=Qգgjz=ž>ab`OVnY*iBSTQ -FӶaO U=ž~aOꞶam{|>6fz zjߦ{Ug:'toJmݞ"33+,S{=YG aÞU=ž={>flC<TM3z=žnaVnZ-JgX {=aԬTh&l6M3F#h6^UO۰'^##=T=Ymz`OU#>ܦ|>6fLmȰ 3g&nOWVnSQDÞz`6z=eN&`O/Q#}l6fzTh&ia`OVnY'QԆ=mzpy |{bggGcl6!GUO?gg ghaBf z=Qգz=C=1/ {=s=crUԂ=!,Ӆgnnm2g uG+7a{>fن0 z=-ܦf&L YgVnY*)R4 {zl6z=uaO]#&>u{TL }l6ͦG3F#Qca=C=ZؓaT=}YM^Oi[#Q4|Tl6Mȣ:=U=žaOz==m*G+v*Þ z\G#{Th&7l6m̐GУ}[GU_ ­z=agu{lևQS-tYg'd`+?{چ=Gcl6<U=c=ZYm4|=Z YS Y3U !)=u{{T {ۣꞱٹ |Tl6M#zp{=aO{3/+,쩪mnaVn{T3Q#l6G#Q3O 7aG?Kݞ1{pd=*i#{==}gJGcl6!g*GU4 z {ڶrk T>]=aϐT=MXX3z=m:z=a>U>6fzzT {{T 왃[{~򓟴 {ꀞ,ܦRuPa'h妺G#Qcl6!G#3h&Xg>VnK{PmV{P{@ |=rL |>6fz=ڷMS3 7aG3ndݞ>{==Zͭ^O.{rSݣGfl<ڶ zTM#iԩ3t3V+.O+i3V Þ2ܧGn=flB1C)6aOyžk[{{BaϜʭ2'^g\'L܄=nBfz=zTLM3T 7agǺ=Zi-܄=M{{f妺?#}l6M#zTmzڂ=10aϐaVn`O_!Y^OS^GvGuG3F#l6G#tzpNaE~LmTg nc#6inaVn{>Ä=S>|>6&XGأ\aOnžfaOG3o+ڰi3E  ؅==ž!GclAAG3/UnžaOޛS|&mk 7aO3z==1'V6,.h6࣭fلi֞[`,PG#왊[Guꞩ۹ |T9߾8Gqb_>Yfq),mۖëflBA@OתobӦMs9gpnݺ '/^x{Mt\CLASyiQ#{{򀏰G QX {aG+7aG#a,d{^nrK2ۘYٳg.}ݷx駓#M3w5gX<ꠃWs⢋.JTG3z=ža#ʭ S =CpcaO z=Tt{d&i 0/~酠'7&n68ۇ~}@wI'-{d yTz`ƚrH>RգE%ԩ#7أ`OnaVnž!%ss=e˖e]la!\wyO=S>SV0wž+}euc':19 EӍ|\\?]LqIm<.~kt-o&'?ݸS!}1fT $S՜qB##m|nm2aϰ`O KݞuO`&T=g.na`O i a!Cя~ؼy⨣|:>b i>w5,>|}ڂ=By5niq!$c_Q\qIn~1|Kr3/+{A}6sm)Cߦ?(!AfLYvmb7￟o=óo {=S=]홃[밧KUc&h妕ۜ=PR͆Z&eqy晤 |>]ag :&ɘT[BM;0w\yڨ827Ч Ll {:yA݆efZxǓn#g9䘗\rIP^N;vhT%~]w^+S^NےgggaG{{=r{-܄=aVn{h<b⠃J]MװG3MSt { ~_piOH~Ř?+Cn1 1~u֭[p@&yeo'>䘷zk7٨53IӘ[#xX-.zm]es''ArVAϴ@OװGUnuAO,uW{B`Ou{bO[s3E 7aK|={=SV9j۵o2n}{}˩q衇֍袋\ )66Qw'wb&v쥾Ocyat'|2ylsOR? 7rPj׏֯_Ӧ}<Þ4wuW2 {=c=z36z=ž- ;{:G3=UފiSC꣎:M G&|'Ac`lm*,BgQgӟ5B賻zl=uxB(SxM4S|[o%sPHcmxg}>k@Y  LEգnaO)h6L+F`,ܺ=Eb'h\aOVnc=ZLM//<^7%8/3$ J3^z饤YO+Gcڵk+;wU~s {&Duk>;71BN^gXg('}3-==]U=ž[ {aOԱr­iU4{BRaGuꞦ;m}oH0[BOgK#i(m!S͝weK^{m=,8!Ǻs ^￟̕!aq~c(7Q3z=agu{O iZ2z= {Bž{a`'o{ҨB,}>KI߰G3}{چ>ž_<!z.}'R۠J:裃5$?y|f@7P sdl"+T=k֬Ys9+⛠Oclr:OU8ݻQz z=zÞkb-H# ԩ3t3euO+gnSS{#i1ÞMu-!R!oĦ'uyR||Ơ i {m۶m _YCTgyf:utnd>@jRUUڔ`sYg%E uv*/lڴ)Y z=U=Z {==UO3+eG3Ӧa=y<SSb9Xd]EtȼĦ`YH-3F$4e6a+$sBl{7֭|'*!/Kmg̀g*!*?#Ct {T{= i {=S3+ZG 7aGأꞩ{pFo }I,0o13v2/D˘)Q3FUG | ʭ2쩫`O14{BG+7=ӂ=w_t7&ˋ^MwW7p7ŌIˑ9?x݄`>h9ul`saÆ5\ؾ}{pϳsWcI#sS(᷑m޼yq衇f^Ć>וs t\ ]zɵk>`Kp6SվĽ:2l1y {;w.?~}%g x[gs>蠃)<{mwp; _=G=#ue[q;kwGrQ3NUnӫ#^=S=cӔ[-Uz{B@aO5n8`pC_=6bnớhrtљ~6/U}𐷹@cl:wFgq?O,14؍(>fkѵ`y1 c'GIa7ydc:M!AeЇga9b], 'lvV=g6{WU>{'XcuZB@)Bga.`K̷nM 1osl0a)Z1ֶ{@X5O!up]oDZ?ey_}LUԸ{Il ۺ|wh*t%aH<\Qu{P RPScC=-mt*Gm*Bb3Te}}ALb\h뙢o? z$XN>,4y) أG 7aG nO?Vn1[^O.{rSi&(c$ | /\`S¹ {5ڶ5ylls hj#8d?-* l0'{Cc6,yvPby3}Ml~wyvߡj?* n*n@кl܆~O.n*߅%뜢7!;Il6cU+(]Vc5oݏc=~}bn`upBA\ K5m?SaP:|)hH`5bƎY\h^k77$أG :'ĺ{=žgVnžUz {B#=aϼa֭[d}iZS( 6SXg:V]F/TN^d {f&­  {b^`{=X5{p{=cۣGuoXe87b?ׅ?{@Bg tȜ6CPԘmؓ}.(^y衇7ٰ8bSgr|\lc%U9*cvl c@<VKU0-ܒܣ-]w]wܰ7zG_>Ӡ}16{Dh0^:Y#rg`~@@>ud,8CΉϰ=mb2'L"\S^vmR? -X[V^ƪ"C'Sվ0Bۨ-ٴ}a- {b@`OZU KݞPuOkg,naO |=Z'>,c6G뮤o.>{oz.l\bۘJ *~l3Nn> lro=:tF"⼺=Ї4u&oyZ<_l0>ln^|7]d<_m;w4"Rf[as=.u";9W@1-[!c?׶>1ĢIQkɋ=\m۶5^wm5Pfe1bS;)֐< ׇrc/X&9@C2UOZeUռps5@<^6^'z{ꩧ} fXv}2[opzh#/8eU=Z^O-UgX nOVngа'tUT=u`Oz=žn`Ou{ڂ=Q3GuocH@/MwWq%tdyk}sۨp6y.*4{rlljc`/&?4KMM 6m6;oߞ%wޙ]޽;[=6K<_ؾ0Yt˖-ɼ;6$Ӹ6?l6/~9xC;ˊ>j9qwL9tc(ks}ؘ r"z9m,J+;v\'}X#;: y>K@ꪫ67ڠh\!,ڔӔ!OU {xh9Oa9eU عO6~QTÐlB&Eyg?fnm;<,HP;jq_N򒪪U343gU,܆{RGS t{{ܺr {`6z=c=!'1ic{%" t|x=]y!\{ C76]?|'J2USi{7P(ù`җynȍl(1z25 c]Jǽ`]( +õ%uT /MMMf,R6`m_ GOnH?J{ 7q{ y1o'?IEgpcΙ7,E63=5 B o3m$T=e^ކ Me9Ľa4a#mZỗ!jpϏPb#\+9rDbM~s]=aj.kГ OCntw=S!u{b,'d Sحg@<]\!'UD t*QӿG3?| {rkʭq34UO U{=sۣgg>0Oq2) (`*[gs]aMua{VPl|?+!=chg? ׁ=Xȑ Ye׉'L{ԛbÇ\^T#+xs?z+Hr>a5TB׃ĝUZecQaʇk߲kP%ؖ-[20.,|4aⷄ>MÞm؃Tț<wQHl۶-hᾱk[c#s!oې/?9kxryxT9Y{x# 36ycjXCB楘9?UQb|!yM#>0_ \ r6K./rPC`C<z=z#Q {:[/Uϔ{=MQݓT .t$alK žʬߚgY 6CJ-=DW;,Xy {cy뽯Yv1K}Ƒ7(i׍<7C W5߱cG 6Ƅк=>l2.P+>,[Ӱey}1C 1VbX/{bj0`O bP7r qP)~G~_|;c,BϏ>Z3+oe̥VY(xܳgϷ!U=T̹^ϔaOnž Kbgz=ž>`u{rLP"^bm i 0nJbkX_,{N[MؓZ PL>p6a5+Qa&a*$oC!`;eu J(pO@mJI>aO8VC޺aÆ6CcC=Xas뮻Jbeod=Tzf\7^IN^۬?gr_=}.zpzbaOۋg^ {\/+J 1)=ez=]gh'{=sroiӦ _e+ u> ;bqacs%xaُ#ac9Tۀ=EsːEV +D7Ogź!1a_ju,bS(Յ=1n{SCϻ``mv0&g 54ec#q k9|8?я\̇W\q`)^a/ .x86 =@!gywAϘaT=ZM^G |4i ­z=ag\Gu=1M QƮ؅hg6-Cg<Z%sϘ`O(#aCZxy?{=`Գڅ^`9re5ʢjRcC/Cc5ҼF]*/Ɓ= Z{ܷ1cHvfbk P>&g C=guV5On1{Sz)P)Էرo=h,2.aϐ@-܆`&)ÞY5{d5) {{b`u{TIæ8o|.jaX] 5P1Vׁ=١@){BauBaVbY6{{B{'sE_\M\6b6ٛBӡ*'ꬑȵ.ߟuP(޽{7u{! zrP5@aOw3nҏPpڂ=׉YBb׮]I.:ؓ_hNքyꩧߚy 5 5ox@k^\jZ!@j[o {TR3z=q' gu{OM& ^G37أꞶU5Z>h=ushHm L:>HIOc=egg4tC[ѶauqF 뮻.͜W_]MonAf޽Cq7Sg֭AC珢T#j˪T;$$N1>S E9)$9YV~ş?ac%uBlC=z{=Æ=UOWuO)3z=C=eT3?أtTԃ=<' b* |WB>'\Uâ;`]Xa|A1.s3 {[B79w@KH_>>`s!*{/,CγY{7 @ѐva1u{&,kk! =U?YLB.@AEœ-f@YRmC`$![ԝ(=\r5}tP[؄[]gS@XD4h7yxd=%)4W)=7o>/M|ɗTL­z=gz=ӂ=ekS|!Y}W|^ ;i[#Qh妺æz̛\.aa3u {(Fiȱ(eLl d~oc9r-[$sO۰e\,Wyo^p׃ܷo f~9lʵU{QCua-܆{{3#3z=žž6=m6چ=SrS3Ncǎ7X.=<1-i媰f cAӰ'4 {VO'OqS'1|jc SzUu {1odAVy ۶mKrf{rϟy晠#$C?ڴiSp?bcye'>C C0WXbhUY?pGBN5![/ 7.!`~U=aGݞO簧{=c={T΍&nOop-/xAZ0eOPT S|ڀ=Ч/裏g4fs'L,Zb6?/mBӡo`cym/CIĵ aÖ0yUw٢l+Te> YƏX%7M `j{2.I>&+<`Oǿn3{Q𢉍ya( lBS'$vUi[f/sY)9k*+}Asty=O?_~9粒P @@a!_# =a. ;C-W`yA # '$E 艁=So[0=Ӯ#>žn=`OSz=E*{b`O +M+{1NVlR'$!]rALzbMZ\bgb-1cI-Z3qZrs BÞ4x6xd\r36Y 췖oVvU6Lu] V̇!%DYz=?ˀMj! CgiyFrnGM}%6ɨµEB1ؓȪݳz|7,1om̪:{`h0!UTWG(?\pAҏYĂ.`5"&oKk8euBk]̕tW,x[nMA!;(@cO =?<oLƙUñP1Ă>`ϔ@aaO辝׃y Xgdgz=ž)i6go)Y T6*^Q>d紺hf>2۝r^Ylm8MV6[g.'lpc1(Eym/"yHGI晢@יg<;`+~Ř%wܑ(gE9 sF8yuLz|*R>{6mHSƗAtT=ZÞ{=E nO |*ÞXcfaOY{=À={ |X {8Zٓ{lK/%y]6C{?m*sY?U6>MY6BmB1Pu`?0xQl޼la܍ )e &J8֋u*~zg$k~3fq4yڀ=Es6 vd#UYbEg>{Z;i&-#G 7aG{r`OnžfaO >ž`~=qq0eh3LS`s1~O x`N k kb-rP;MX;:o (bF_٨&6%3c3F_#믿~|ML:aj92.`XN.7fRʗ&gP6dٸyZNv XOl5Uo(x+ə.ˀNlN1f1!27ؓ|b`wM6lfq/BAOv+ 3,o]1\3ܧXitU+U 0Ž@1ƳX}Y\OjeVݚx\TEba+ . Y# yž: 1ʍ 3M~mH'Rz3[ygA.OlX~U=zR'M{BGcag ghu{={Ic3u]u<ouq\| +`I[,fbnĂ>tsʌ1W^ <D0fKuU~vy6+ˎ½@-V}0X=Xcɾ@C`XR-<@>zP0Rd jVaZU0EJ+{ɁVaOYV.UO=mON<җ2虊/SuGS`O nG#iLFCP {ۣꞮO }'YPg\eQRMondA೼f7U*BxS;HJ_m[;kؼ3{)\@Tgb6H<(ڐ`C*a.s\Gr1`蛻SsWk׮Y*w!u6V2{oU7V;.Uez1З;:s{kxs'}9o3C=3/qk'sv3G5\Kug;j]¢1yN?bMt뭷FqY0^W\Ƌ \{ O\?Y$g {à*GxP/=|K092Uɻ v PenTSw<&9k7&pPQc4_\ȱB!j~ar˹<9 .or[2nߦG 7aO'dsn nOc'])=UAOS^}ӆglGuϸT {tȹp5 ,:쳓74ܾ}{ۭ\!  }~% hༀlPX-lfBNt`rs8sc^=ipQ94GX9ĂtÇ2v2ϦO>CN{w1U=)wxXY㞞q=f#g12=" g}D⾥ngbϘG{{7,P޽s㌕_~er7N.G#q4{ڶr{rL^9#h6/uԀ*:Ǭ;Uꢻla} Eo {6+ҿobh5b>_,C x(}b}'dnYI$)@^>s]>M3`y[\v@'^Ƽ)B;םG(ʃ̷<, 'r:yQ/.虺{=C=eG3`3v 7aG3ӵۜ=ڹGEoJ4䉍.7=]Ř74ڤ6 B Sy?UP[U ~ڊ6sT{=ždTžԅ=!aXQ3fuOEG#1CgG#Q3 Uz=žÞ:u{JLnO#gz{z==G3>أG#?#"2T Ϝ<U=Z {=ž_ iʭUU-܄=3أ[?Vn{>Aaϼ0>mBsW C#Q3ti{=O {!ž1i L^`O!&>LM3| {X6}?!zozž>@n=1{užnaOꞶ`Tt{SG3>|{TWDB.|>UaOBa:eLjAg|GUt,ܬ#,ZI *{=ŁG#  C#L XGUaG3S|ܺ^gkUG34cݞZi|>C>M0;B! GУG3&#{ {X{^MS0 {=c=]Qݣgh-Z3žH6) ž܆{R {M3o/o߾شibƍ/|q5,lٲxqcW#1 ᎀgG#i Q#{aXT=C3Dӷ[ aVn{s|>E6oc8tWcwžaϔ{=uP`Vn={s|>B!yTzzT{ÞMžZS=m{ڂ=m*{#|Th6=suS#B!G#G 7aG3DtݞFaO "S{=}ž6=C={T|>U>B!!yGdts>su~i"G##i {=Z {=žӵ'?]|gÃ=EF#n!Þ9ع | |T}?aLy=S=z zpScag̰'nO UG3Ӧmcݞ&j=m݄>s>yTz=U=uAnaaϘ{dV{Lh妕G#Gȣg*G Q3LUnataOԀ=c#>ڄ={ڃ=~ Ø;z=z zp^O'qaOwmuO[=aG#외G;7GGc!g^myU=Z {=ž==u tž>܄=Mu0`vnGcG#LQ3e -{B`O) ʭU5{ {ba=ڹM~G#pG3F#NOUT-ܬ#inGS=žž3أ/أG#fO>Y޽{裏.W|5/#Q/hߦg{==S6 {{ۣgG#m˭޺8b}I/ݛ!G#.}G 7aO=#SmT>žHӷgǺ={=s |׋oyq.N;gKj2 j (g}{^fɜƱxE]O?}q/|$y<ڶ zTԁ=u^*[gΰ'tU3ӵG#{={TL|>BO`+ @n+i&~1 z>u^xavcE/rw9W\qEny<ڶ zzThᦅp`OaO IS{{{ۣG;7>BP=زeȄ/b2nשQCs-nݺذa3\񋣎:*QzꩋK/4ߑǪL89<>07,X1t{D)gֶ/<\`G#hߦG 7aOG3NS|:u{=aO|=rS3mug>GtCN,pq%9y%ݶm[už(ZR,ۉﰓ;#7tSa\+:s_ >#C=4]+$F[GM~WֵdM6pzGyd9ȓ;B!GУ}yYi {S{gD' 9LMG#:{ؑ뭷&J,5Bc'D~ͳuO>$מ,4M@ gnݺMjːӆr0؃Z5k׮MmÞeB) 5kd =7] SSֹ\|} z=}ۄ=Z {=`O t{t{bG+7aG3?g^G\~틃>8sӟ pٿ{rC9d_w{R]v%sxBvEgI4 RsNufg?6zzTꙺz=aG{V{=={{>gJ}ep{YwqcYlܸ1D?;Hjn4)'ú kw_ys:| U8w'f~?5>dh kSe?.z=ZuYg'eH#Դp+=}[ {=sۣ[7G3]ł[#2Ň~8餓 U4e?_zAmׯOA#3qԶٲew,({Xd5dž p@gdR)vm<0&h +OuUW%k? yTz==zTLM3/S|ž:G#iYg' h妺G3Mu\*0cg"`&Jؓ~wn"G?U/}~^xűɏ=9~ݻ8raa9:aιr7֭K5Uk݉Zu/rK@;vםuYɺJ#Q#*虋}@n[ {{B\aOU3 S^G3ƺ=U&Laꞡ{>1U>B?'5y֬Y'n(Rj y\/"='﫯:Qe}5\Y/:<41W'NpGȣG#ѾMU4U=s#nG#XGu꞉۹ |T}?c ߸qc.ꪫ ?]vY!Mz=?[|3N3W~Gk6q[P^,믿NYsΝ;GydgQP_}݋;/oٓi;SsoZ x~ ~8>Ot~5 ~g.b֭ɱظ袋_}UeܾaÆ\M7ݔY_r%;s-R |N".w(ȓ;B!x gܠgmcVhf>`Oa3S=aVn{T|>e~ig}v ~Xl>RYr3<Yn].DjtA/ - j.`*CYڵ+Zr}%O΅ViM$&婂Qg}aP ] ws:cʳU=ži{=LVG.`O'cžižYMAݣG#O?]?7 9~Z!F 6o]D^ziؖa%wG'$&jʮᄏ8餓r! W܃N8g`ڻwoK< ɫ:7mڔ!jm=ڷ7z{b^`ÞS>u`Ou{:=eJ#h65+7=G#?r7ydR pBp sֆ?kx QPfo|GS1y;2 o-~{bJb=R7t oxu\sM4G#ѾmL-ܴp z{B=ag' h6\# }>/2ꫯ&cb/B+Zmպ{P]G·s͉J&;֮]XZV.]p" OW\qEqb YfO1oܸ1n7 wtw^ޔA;/TJ侂y!0!GУG#{ {Oﰧ { {P m{T|>m/>B@O5yO=Tigy&yؘ}ᇥi{{ڊ4( o˖-vpX05ߝz [ˊ<:Fy߱}$7o z=@[M^G3tS|ʄ2ž@#Xg$zzTQ.艅==}ÞRaO/LNi[#im GuꞩG#1|)+8x悢7Fʂ~Z|ɽ{gQZ~3M3ȣGУ}U=Zͭ^GSgTz=]T=žq)Q3_uG3TgGS-MW[hBhT,! 6lHrya&֭˭ݻy4c9gm۶-9r-INwn'xb |!jm=zTh&i^G#i<(aVn{T|>|>1^n6?$/~饗k׮-=)88ꨣ7xcBQZ{w\.O.s뭷&9`e}cY|ץli\yvmɺq%Ou 7,oc?#5wzGYI#Q#LT=SS ꉁ=ž03Uس |=žnpaO3vأG#/~Zu6y5o߾=IS|:>@\pwE*:8 ΧLtG,>䚬+` naMr9|s֬Yꪫ0o0ޱغukx79 X>\"XG6U=ZYG3GSԩ#{r=s>m(Q#t(6N9\H[o%cSw1_^s5hMLY7eЩW^y%B 멳:;l"8_ BYܸqcn:b6#C6`e]]x{r"`BrXw:͛7'9{3-i Pw1 way{>gu{^%e׎gY\r%5kDAc=6/BO2.ݻw'iyjw'p\ 6,vرϓ1#j=Y8ӓ5L{_z补:>]w]c+;w׍7ޘ85yD#Q3УGU,ڂ=1/ {=g  >ž`h妺G#M#㥗^ ~8l̃#P]dDZx>;Bٶ zo*Q#RaG# =G.Qӯ>G( CB~|Ѧ35k/֭[yŇ~k;Եz~\|əNͥ/B#z=ڷ{==!gȰ'ʭ)#Jc# =m#MuGuG#|G5g^/gY(DusM7-8kw'놡?j<{:Y;wڹ |>Bϐ+}'s'H?Ϸ|s͵AæeNnIs߳gO2 wM.`&}>Ÿ5t=̍꺐 x?˼f|p~ ܩt'fi;AG#Q#왓`OLɌ>`OaO]3hS^Gca=yU>ӄ>r;/X)cu]w}sk nݚk?裏vHr z zTtz=cp{Rԁ=){=홵Ϙ|>BO؎q.c(lОzd3{ڵhO z=SӣGc{r{=={T |>g|3̄Ԧ=۶m[Y[׉GzW֭˼wuW wa=Sssg.`r!߁zj' zmLH^LnJnx9M{ɬ5t'v}uU>>S>'|xOc>BJ cJGݻ͵ =@ȏF!t^cC Ƹn"KnXr,Kpg$}{3k+ gdeGyy@O~УՓGX'{=&ǜ[>7G$P@|Уc-{=u{bž*{{̹} #O*ȣzrXF=1@{9BG=!1> =%M/t==ɭ~> 7@zʛo) zĞ&R{Ğ=SnIbv9Ro)}4G'ٶ|VV='79̰_yw{V"SSn={Rs>遏?"x =@Ozrpk{b{`=YcOǔv97;h>GD<zzzrl~3"{r=G}z@=Z='  3:|Ğ;v{nnOzSn=Ǭ>G@'|VOJ愛{=F'`qG'v>@#"p'7yJlУcͽx 7{`=`v>#x O1z@zJnᠧ.|b{`cM2@#"p'g=gУգc­{=u翣ƞ~wؓ >'=nvO|>GSn''i{DV06z`|? 4 >t=L1|) |;Iz̷i=q=cO?{`=GG'>q>GD!y@1f-VO*zbp==gh=hc>@$w =2Z={`OS|bRh=ŞY7~<}cMpk^OaO$=B=)`)tG-~؁<У#h=Mz`{8c͔1|̺#H<) =Z=s{`ݞ>a>@v Iyrwzz`Oؓڽ{`OAS|K$vOlؓ딛vO>R܀O:S.$P@mУc pK{Ğ~{{97||@t=G Yu==]N=ug'==d1} #y==bOWГ„{=ǞAؓݞg/Q|!|`I |rE@z@I{F}VVOSY*=`O??rI3*>@ G;=Gg{p=9==u)qĆ==>Pk<<'f1ߦՓ0^{{`n)|O&>GDyrm=Z=zLp{} {T=ܴ{{`O[|O}z@OcM'7='9{١==='v1V2?G@c neM5=u`ϳ|{`I{{m|?"x 6Izzʁ&ŞZ=)aOϥa='Iin)7@#"p<|LpKq­y {`{O S|bSn='=9>G@?x|VO^O[sn{Þ~r3c-niR}y@OWmlГhh:ý{ž''==={>x =۴zzRn4===u$qGG}?"p'g?G@ȣz̷ng=$=uMi|}݀?H@z@6L[=>8cMG};<hhpۓ,t={`=gTk'97|@"`z̷VqSRǔ[SnÂ(3~UN~ yrFRfڂwz̷=&L=uN{Sn6Gs|F}#'E=GG{~~~>rK{{m}x 6zIc-{='O|`=`OJSn=u> <4=Gh=1xG'}1|> ́'c>1`O]{rr||̺@!#n􀞦t=ǽg{`{L{>Z> 9<{@VOz`='Iqh=>܁Oj~DDȓ>c 8&`=`O49hh='->Z>) x =iBVk­{=MaOI{cMv|̺A~DlIxy@Izz`=Y|`OĔ1f-v>#"z̷ =&Ҹ$t={`O_{w{Lsn>@#w )y@;=%ηiq ==LE{=sn@3_|y O>zʅh=bO{힒'&{>@#w6mB;=e̷ips{` {8PLIݓ#@~܁<xokzzl{`){Lisn>z@=&p=M=gɲsξQ~~NM?:A z߰_zR_bda'6F ]EMola2蟒)|[)I/ôuEb_e%_} կ9s:ayvFjL߇wLa*>jyZ: 0 :tEW9<\:&y)I{Xgi͢MP7a%"1|q>$܁)@)~"e=r={B64܀e2t/äM&mMT+9> `O|[''I |n=U%Hz@===$=9inOczX ;ݟQ(oШo5y@mjNPw]oxȝ$qCFrcnt}'9miN7u3:3;3{;>0ӪmP;&,ndјo@{7mMhĀ5AM@1 L$,,,f@Y.`\0t<bQՓ[ocO`=qI{{h|?@H`<􀞔GGDŽ[\uN_){oM97{Y7~ )xRBzJmZ=zbps> >ݞR'vO)sn@~x ='6n9N{F=M{H {-v9܀)|JA#"@| i{JZ=&`O Sg n)7| ~z@;=Z=%A 7z3=أ|Ov}ȣ3 z@ꉠ{` {{Rr>=%>@#"\ y@6Уc- 7{`94==ǜ[~7`Y7}ȣz@6c =uγl{Þ:"rk{a+=܀OY}D<DŽ쉥S{}{`=ܴ{97|?"p@z@VV d{`Ow{أ4s>>S hL;='V .v ==I`O,ǔv97>;G=Z=AV &JǞ~=m|`~m6%h>) x 6zzn>6>{Ş~t=u'wqGGG'#v6Уգ^ {4=uSn=-~؁<Ҡ'6Sʽ&γf{`qǔ[-}񁏖?Hz@ zbk=Sr1f @$uOȣz@Z=bOW{9{`Ow{w{`vv9Ǭ)}x 6O^zmZ=Z={SnOi>G$@G)zĞG'V03̳-nqM{2ƞ hh} #\=G6[='{=u{=9`O?v{`Ogc u }r~~y =G'IzJpk{h~ؓsnh@#yy@۴zl3({{{J|?""hУգ{rp==`O{rr|=ܧǜ>ЧT?"xGz@={`1f SL#"'<=1bO,{Lf{={Þ\=܀OS򬛖?"w y@Ozz`Oؓ5ؓݞܴ{̹}J#x =B6{rž: {`Oأݣ|>y@=Z=Z=ǽ'`=d=='7|><<У3<{`=`Ow{Li<=@D<􀞲G[izL{:0r1gn@#wy@i zzz`{=$=M{`1fέ)>@D@mzzn]=AI{r=bOSn==b>Z>e@"p@z@OУcM' 7{`=E1|Oi~؁< =1$`@rl=Z=1zJp+^O{w{Liĉ=}#Č<ǝУc-VO]I^=IcOذǔ[أc }:hcMGDŽ쩏=z{===̹5 >m||?Hȣzr{{1f ߓpM>$@@z@ɥc­{=r-v97sn@#0<1~z@OS|VV{<^{qǔv>@y =bh&bOnd=ur1|O,}Cχ,5SI\Y\\"կ=ʚoKcͽ=c0QaO{w{{{lrGFelG'KNMIL曽iHꑍߣf{OjrFzzzLS8t}?uG|?"Y= m?nxࠈ Ǟqjsɽz̷{`{I{̹|{;9|;Dd|׾v=_w{TDȧ3{͋c=';=Z=Z=z`nOjn)7|>#R&.[=ԅ{`Oأݣݣ|>yOA=Z=]BOn{=d=`v>@}#.<~)^ODOSn)7ݓs'u?G`ď=yrhnibOjz`O{`nvI|Y7}==Z=DŽ 7{ǞXr܆a܀>mG' 7{`1f s~>#{Czb` 7z{`={C|> v$=h=>gJ z{<5= 쩁=uriMgtSJǜ>#.hwznmNŞ'ܚQz`OnvO.c n#{̶{ot='=t=ܴ{ri4|?H= =Z=Z=&ʛp=xq1ݓK|$G,) y@;=p='|F=u'Sn9Msn;)> =ؓODŽ[zbO+{`1ݣ|? {Dž`)z{JkĀ=u`q)7|>I9}aa!<{D:Г>c4)c- 7ؓ)4qhh=|O~rT=hv1{{{>*q`Hأz@)c-{=`Oz`OBS|w{L ==}J I{b 􀞒`VVO;ؓڽ =|O4}{̶[='VOjӯGİ')ǔ{O<}I{.Þҁ0zJ|뙛k<ڦD_U+T_ =gtؓ{{S)܀ɵ|>G=Ws O~D4/l{<\c\)9a9.#n&`Oz`1ؔ03o}?"G;칾=W_9^%g^ޓn0}w{Уcm8詋=LĀ=h |F=`>#E{=fO=W˿ƶ&ts/錝0}ݷǞQ<=ǽ_=ǔ[3,=='71~z`=cV1{bǞQz`OnO3S”v>GG#zǞQBϨ'){`OqS|`vvO=݁Y73#GDN<􀞼'Vϰأ^({O{.h@D`H {@{@VOjn=u'D=L|?G`y`=aOmZ=&L;{`OtS|rSn=}="C='}=Z==O?&=^=܀>d ?pӟ=q8ܶP?_W?cKvo~wZτOx{@{bؓfDŽ[bvOJn> =9_3}@z@Oz@VOZؓڽS4=@#H`O6G&~sǾٟ<6vτAޞ; z@O3>9Q{ZXr|b~ *|/M8| =No_z| 6SM =Àϰ|n$T7t0=rJl{}v<|OCz@Oؓ3zC=`Ow{{{='~> 3og47eli {&f/L15i&pǎaqyfMS_=>''~{nCO S F =EaO{1|O|> I{W;^fWzsi-=} =4=G^}zz`{=1z`{Lis><$?`4y==|z,?vwn|M<y`=-`bO Sbǽ+.')nO a~|O*}C#,=? CCc 'y.티'3==Z=Z=Z='y=rq'=>GdTxⅉV@3^.\ =y$OMb{ [ ?Cz@z`{==rIS>1>Gk=f c}SzoRZXZ^ KkRTz`=9cVVOn1=bO=>퇤fνMmZ۳תo˫wrv9}sy쩰{Ԗ_?:@Ǐk^{0_~~,Mi9o{W[a;=poF(7ayTx7s*m=kτ~]9{Q>_MbO6Ğ?y衇FQC5 $J 4mҰ%2Fƨ>5QTH4(u1+5d$7ԬO<{Ʌ׿]zOp{c?ѰMw}}=\ǿ{Ͼ { 䑡g;)ʨ*.0c2g'ayOXg{lEF'EI.#GĈ=/GG=4._:kgx-7>S`4=1<9TI36 dž=uAdP'?mAY>ڼ͟ap'?-&o5y@m fPWb%mMRfTn z!;<:=?'{t~{;ٟ{uqǧÙ{M'x,\wn7z*ouOfOWuFu_gT7vb孝.}w;tgƦ$8MM[-I m&6hR!&wx,iBJ~ELXXX=Cg>~{c]'g&h{h.ծ> =h ݞ=]Nis>@# H`O8/9nNm~q5ç~Y>?3ޛa{mna%}sg@~@t}aOvP??9p'PjŻO~\o?c|m^c[+c_8];YOgD{oŧsk4=N ;05f}=,=`՗aOd+hؓ޽{LrI{Js>qO} +&⼑~߿*o;輑?jy`09=6;~J{zoXxAf}y1=wghuW䳀&zz@­Iz أݣcέu>GCq쑶y`ϰ߯W a}y9aቛ_hzؙڍaulI,1_wa_ߑ?Ojo:]a1ؓ3r>)a=LisnǬ>G@Q`{t=g0\_QPe}e9!ş?^&/RXzY/i1{e?V|hf,ܲ-x=?>ߦգc-{=cO=bvv>>kG kV>{<|,̆ +G^i{&.\?/>Cw]a燵C[c3˾pҏ%Z0󆰾5{ Lpo='G쩳%:VzSnaO.sn>>GD`@uPoRm}uy횪qrF;gk3-37|?~yky^T?򫻷fY8zNؓ#1f w{Sn=9{>#l=5S? GM? 9#V_{6_ZSAW`o S{̷ihI^=Zsn >#'e=g jׇ>{輳qf zSoug򊯆=o~c}d=Z=ez``{`=`vO{}=yE?TgamqaoպĞ+>egyԕVSwYXz+6^NS}գSzǽ<'ܒÞ:{sn},'"=C_/j-o5\V u}žsVskU gye/^a$?8{rn=[ qh$=܀)|?"G O*{NV}N:}`{ĞQ>zL{ w{T܆a>'>y`ȡH?ʁ]ǵ\f cgS왮~lm 3ê:3Owbzm{{ okzL;v'k쉥S"hh4= >Z>Ч$?H`<z~XyCX_Y:=v&.2L^aŇCX[<߶]𣵠ձ_si>yQ8z滀NcMq{`v>@D`< !99@McOw{Jr{> =@NX9;5Y_;>Ug $@jŬM cgk8'S-,`?mՃ^/n{X[ su;0ꉻc̈́[ zž:hhhhh\@bσWsz ak{3r[B+;oe 2u7][!_w e3a' Ă=MLw{Lis{>?H`X,|+|nd398nm{/eonjOm ={SWۛqb7ol N7O840Z=&oŞD=usGGGn#OS$n|$Ӱ=aNOXrto7|}uҙKa±@U6n ?[?m~֦Ts6CϾ|`гJXze疐0q!Nؓ|V 7n'Ium4ݣ>|O}=xXNcg7|ङ6&*Lcaټ2{Yauq^}&L_7o/ a}}KY+Vt,|.~nU'N s]ekhmq.qAqmZ=&`Ons"=ܲr{F>{> =R$~@7ya+@) =cgߎx~uN}[uG䳛`b im5y;E1ߦ{ʼ{`Ow{Li1|>Ч\?@HŜ~>װ =3'q[>+L]oJq|a<'2u'0{&,>}GX_Y|ܟ~/[Oŧn;꾭ׄ ?zJo照n amq=xE&QI{. RG===b'W)}-fI}"x{b@7s{ ?&/0yąz0~7~Ի3u7z3k缿~zmP7U?GDsVO?7~}؏ᢿ=goZSF+iՓۄ=:SnycOl힒>@#iQ?@ qL77ӎA6bOmZ=Z=Z='[qݞئܺQIsn}x`O6R`J^zҁc-쩻{`==Ŵ{̹|?"@ؓ(=aop =i{3o==>Z>; =%O1ӄ[Ss2==ܴ{{||?"y@ =G{`)7=}=pz`O[#hu-{=MbOnb=GGvwd=G2A'{='Rnvv97S }JADJy`OzУգ{j=)N==.G@D`<95 =p=cM'97| }ؓ{ =Z=DŽ[z`{Lh&@~y$=!=G 7{hhs+|-}Hl ;G"#|ۈG'V^i{Lir=S${=1=o1f t=r>%{Rߓ#|@#b|[;ГrDŽI^On)6hS@DxC9Hs{ŞXGGDŽ`{ĞX`)7|rsK|}R { Ȱ1ߦ{Zp{Jp=ؓss-~@= |'a GGJOF=Ϸi={D={r|{̹}$D/Ux$^=R{̷ihSr=-|'U?dX {D=g)zbƞ\Gu-{=Mb('Iqt=gc >Z>C <Q({zzzzTw{L|>G`Q&`{d=|g zc =)i{{{{>)>G= #=Z=&rp=b=GG q}JD#"HFy@hhh{rK3o:Gs@#{z3 '`qǔv971>># a#`=rR=Z=&ʙpsg8=#SnMi>G#{H z[=%=r1}?"Hw@ =ut=COjnr={̹c @D`ܑD@ =u 1hp^O]쩻\w}}xSnǔ(GG}@"alذ_;GGGDŽnz`OycMR-#' {@Уp3Ƅ[4=Lit=|| =pG zžQ? {FIDŽ=I`)7snh@y;{zzRk4=u,G{=G}@"b z =C('&z{b{Le5|{) |@" bzD=Ϸip3}'iir-== h@@y$iqoi{RigΝ(={̹} {<=G|[ڭap3Vʽ{L%=|>G {<=G:|V |[=S蔛v>> ==_=IE`Ρ =Z=Z=&܆&'ܒw{Lis>c>A7=Gbس?j峏'uIՓㄛ{='Ne6أݣݣݓhD?PHF8u>G zZ=%=;ݣ|s>>GQp{D`\I)zF={i{{{Jn||r="H)3hԠ{=u{=CaO')ad>c>Z> #AOzLzw{Lr{̹}?"G=]a6c d=91c |?"GzF=۴z@O9d=r>a>#"ebȐgVVq'yk`Oأ|̹}O~=GRh4='JŞ'ZŞr3Xa9X>Z>="H3*1>hhpK {l=O أݣc @D`={̷i='; >u?L=\=܀>#{ &h1ϽݞOi)i |J@^H:ߖcgڄVOS|qǔ[snJ@~#R IzУգ{d3qGK>}="cgP1ߦ{ʝp+^Oc͔v>~){=`Oc-V 7z`=Nihi>Z>Ч<?H`=BRhhS hh1|?H|,$u詋=۴zzLv. zS$hh4 >ÒɁ255tLvT%#{@=u|VV V1ŔvvOS=(9rty6U2sUu]e03=CM# H`ՔYΙ}0a+Edy*LN{RR ="i {JZ=&`($=Ǟ=c9iy{y wYadl.h0ʓafr/I} i+'tTH{ʀZ=&Lأݓpg Ā="Ş{/ c| /}C#HS,`B gkum+G$15 =УS[ۭ6'JŞa&F=Sn='A>1{0T2נR:o1^I {LisK|#('?` =GJСCI@OXV 7 ?ܴ{{{;n}D=֔Ҡ'e1ߦc̈́[zw{vih=IcI>GD`HjK)zmZ=Z=iz`1hh>cЧ }="b'ty'L6=۴zRk4&ܒw{{' > ={| )-왺af#УSR}'k9s=oI?;cy睭gN?{Q>I~PG3Luq8=i"ݦ3uy=/vؾ}HEˎ;D=[=p]{?Xз$< }3?z$\sϾp? G/$$顟&f{춰ݽg{>J+F\&gN1<9bW?wm=72կkbؓؔ[[nٔ[ovmeT@4d /al5]â(om(pz{I>~Gpw=PI"# ^y RAȩR!q'd}z=gnƞĞ GY_IV:iaž}=#?b}ӳQAIz5 Ͽz> TV`OF;=]4b.U)4* |M/Ӫ|/ޗ_}Rj?"{򤛿fW߽/Þ=0~gsO)<W +=DS,'{sqa s+h{A ݠao(Ш3Q*y{{f$=O$y9}0=1>^{NSmTNoDĖt="ٹpS񵠸s SWy0qh_8x`ke3ʌШSٰ}v8san}7g9 s29'4 ='qi`.kh Wbwf 4qLqÎa6aO{?={039=ќRʩ3{+33a+Gdxy,\w'_ {uw<g~1g0AV,%8uM)S[*u16r >Mc # >]lRƞ'K)s2>"'f}{D_ ) {bܰ'fQmZD!ɥɡ3jI{HI{rFcԇQbVo6힢=~9`O~{wzQaV>Pw{`vv!=G< @@{DzУՓzDŽ[ޭؓݞT܆G<=GTА > # =۴zLp{ ҥݣSRgT<#R{>Kj~ =3 =G"r==ri=G:~ ="e@0Sh&J{"Ǟܴ{^4V =GdpI|JA#gX{@OУգգ{I|#28@# HF#{=`OS@O3 16:I{L=ÂϰR>a = $M7=zbXGh7{ұǔvOcέ{}@!3=`_5=Gg3,t1 w{{{{23 >" 7GɥS{RmZ=1C=Y1#s|F=Gdtؓ("݃R)?WG zŞoc̈́){ڞrK5hh |F=G`h="G@OzrZ=d=Ҝr|`'?G=%@ϩ')aMp{ri )7힔=F>#9>GD`?t=COmZ=&`1VДvvO{#Y }="G@Ons21fMDŽ{=ǔv>|A=c?"g+La_˯ +WX}w{0q{7T?d{Waw?w\G6k~b5/jׄ=[K.~=zRMshx̷ihhرG}jMG'vOؓqzayTX_^ au%׏e_k+amn",<90sYaލ]~++Kaoy&kžֱ{D`A篚Г>h0r3h>7=ÿ k!~*$Y|Oamυg kdmˏ=uݷk[9uלaOBؓ##{D`TZ=m`Icͽ{{{ψ-I|V^X{Jt7 w]V_Aƞ5p55aOؓ+${D`'EsZ==Oأ9'=mbOs=`Y_ k3鶓-!a5xͅ^=9@{`4=Z=Z=2V,h&mI|Vxm_};a s^zlmٍPLM^amp߅~g 5^=G=z&hn&r <|:Þg[;M? |4Yiocg7o:/mu'LkfHӯg'/}#"G@O=1ηihI^pM|`ϩ3v9 G| go-d򊯆Noam_B3aѫ{m ֗l5 <){ ="{4=7$,sX abb"TbM{|l/zﱺСC皰'{=`O[w{{agX>gT$y$Gamnbsgy!L\ >m:L\(vZyÖ0͞?|׼~ˆ쩇= bO ~ =zZ~w;778b{P!ɫZL*)so߾Ւpw r30s=ŃY K/>W7!>>7WN cT{NgT3=9x> zس3\w#C_ab߳ᕗ^lVIhKUW{Rhz'Z1VޔvO9힜kI|&~̽[K6{7^Ca _{?WA{DbGH/=)t=ǰ{3sφ{7xrp qNR=N =;v0ݣSPǜ[:ؓmXzOaŇ'>?ʖгaU4{G#0H`H{4=kVNkF"W_I6`ϠϩMSnbvvO >},8;kUVCX_yovZc߄4A2_^z ǰg }=Hc-~̦5q}=`Osiw=W R;'R8v8z{`>G="!{`nIb)7|g/؏Ca ?dgau׶$nF>=")!=zrǞ&b{`vO3*<Č=1y+&_gjqH=9?"G$n=#9bO&{bS”vvvO={lfޮJzXr{(,=y`=HГ p z|'Ğ-nS#{DAz@= S@09r+ {{=϶wN~ O֗Ğjj'.|0{ߥa殟_}9=ݰ>n{@ =mbO =1ݣS6=]u +GSW)홾6s4fO/oգzXSQ/ 3aֳ#G<{p===gXI {~Cgʑa?{. 3,,3Um[z^/^wD /1;ea[S V6+=rU#_K=_sc?Hs^C7&Nj8L[9u7,mF qߠM\:}[ >q&o?w5|o#D`{#DŽ[Jz:SnavO|T-c͞煱s>uO= ?qߤMamHm}}-L]yRž&'ei|&Oau 'N9f87>|oyoB >zhaw:α<=^BT@]Oz} {@"G {`=Mc0nOSn={JsK|ržfD`@#=Ŵz`Ocʭ)7{#$Gz`<}rK{|r#cO4? H`H {6~<SpkrKc͜9H Sak$> )y`=Hs*I{-9|rƞ"$=o$U?<α|'z`= c)7ؓR'&c { $y`=T Ia-{=ǔ[힜G'){Ć=8<#>@"hy`=Ұ'VO nIb)7|bH}0T 3z`=)bOJ'ܢw{{`>9bO~ȓz`=VOz`)=Q`ϨG'm){Ċ=&z`=aOL̈́[zw{{{O.S2>'I?GD`@6{@=y@Onb)7ؓsǜ[S:I{#)={JBǔ)7RI{rCD`{bo9<ýgB {ϯ7xH>^: =z`OZFyս S={xs 7Rͣ߿XRwfЏ_5qw<#abbbǯzOcywii~[Pؓ2t="rpk^=YbOsnb?h#z`OLSn <@ow~?WgffDWܯBwU_nPS=cުZA_̹zt=Z=qzw{vvOi{) {} y`='^sQaؖS=ϨI${z_œY`}6Rlz z055==='v)cέ >ЧL)}#z`O|o*4 JoQs.//o@UUB#{n';I|汧9&:{){JB<@ؓT?yVj+++gM8 VߩOtM}Nؓ=)zbS4r>ι5>T"e3By<9?)y`='FrwlVpz~cW_{l߾ׁ=aO*&Ǟrk{{/}žbO~`<"=yy>1z9VjUz$T%z[o'{Rj<%rI{-%== T-e)}xzx%w<=>`زsp7c>nUӧz>[cS{bis=ā=o$u)~{@=yA=|VM-3뮻7ly:=`Ocm0===܀@#ތ{=|o=X[[ۄ#_T䏡™Meḛ.42DŽ[c hs+|`>cON~Gy`<'[zPVWWo;p@;x㍽g`[aqQؓg({Lih4=>;>GD`<{MUϋ{キk9tAU{`OInhsn >Ч{ }=z`O:5tM_KKK[BO}0Mrn,$+;̚5͝;7mݺ#:.̛7sSg{=umhʭ[>@cO] {@=Ğ@ z%uۻwov[1GcBV~# ##5{ =:@Ok&&s}{b駟XBsl' cWBA^.-{=U+|=uFc<@SGeחX ͟?q-[8{I/>q5NS=1 c1cFveN44z`OzžaݷVncOk&^<e*FZZHuXx`=)ӂxnߗ>uܙ3gf~k裏f6~ަ[IUHy`=6mZ6oq1;u͛MN$bJ(~^c=[5{[U{b;X=ȓ:!GTP[ɲV=}ij}O)~ `Oٱz@=zTs${*0Փ{ܢ,z~[wΦtө>S]\U|eϨK?{`>5#$u=Υ!i44!-jc+o6(c=s6:1]rKU.̷.\r|O#I~ `@}) { ==G({`2o{L=t`>~@`ԁ;8O=Fa֭[ӆ 3S-*=r+{>{A=aX<187<c[={[|`>EbϾbOS { Q`ǹ0=S-`{`>*%=2<*Tza_{@=Ğ:o8VnUia}ƞyd}z~aQC9/=TOp {LTw{ng">G=G5縠{`= كai6}ϰN~{`=-{ =M­ؓVn?p{{$==/ů;_Nt(E: Uμ|YZjWڹbQzW~!m_$ѶCκ.m}~AZ4k֬4}I{Gkc=&ICm̙Z6?ݐ_.ԡ'ԋ9m]"[@qzv:zu3gȮIUc?~6 ͛ҵQ=~jQ\Sx+;(i;Ρw]w KO˰_.H ĕ3/[ڙa;Oܖ]__.Y;ڴ广U+^g*yZ}+FR6mZF[Ww^<Ҹz}7.^ۦu߱gg8S+xݻ/ݒvnYfϞ=Г&ׂxqM83(j1 l2Sܦ={T#|{?Qa#|gQnVYt+xZ ';,xnkgt҅K҉-N'?X_9 }7ٳfOڳjqzoi5ǥWv\ϫ[E}qy7w6<ܞlْ6nW6mU7oVDRii;_~pË_/D](u+=hkzU={9j׬m*ܡ5NH--rv}=\k=ڊ',\0_>;\nP0_И6zi*ee=3 7fWPk͚5 Y>-3{cڅKs kڢݑv]{.@tpډ+F»?>7k/}{v7|3m۶m u*>/^Yn|/U۷}vg7V]ZZo]\͵3yvΔ圴k3ӻr={2y%>TIfq}W_M[nM6l: תFu_ nQm6,);t|`O|)}`Q9(i=cOP-rǞ*mftбS>?;,8< "3u Sc+\gB>=RaO  {gBOA`>m8S챕{`@S:;$5{=+AO'O GaϨzF[z [Ukh>|U{>IȠ:bsIp {hT13It>a>Ч~{Viܰ~=Mn{i =@cOS Rb8PӰǹ"=G&cgS=aǞ'1|O|4 } @5{S#{={=4|}Ӈy"d$ݻ[h{. {``=r?@[Uk 7SA ={){>cg[Ү;U4 艋;wPタ`B~{L tӄ63L> }|rڿz~ھiڵW^)m۰GaԱєSz48Tq=rtϔtp=7=}z~zkC?VٳgG}TR͘1#;{NM {\wG}AT|{=u Tp+{=|r}=7a~XRyo {va+BuUv{n=Rns'=z=(1SͩRbNL{L {=G?U}g*_ {^=<*=3Cݧz*=r+t(|}=?xvR½4ZDՀ#У@O/=@ÞMi 73­2.{cO+`OW#ȣAd=`ʃ=n=Ǟn^1-~igZi_ںP+7?Vz`_>E?ׅgw ۳_/ޖξtRo_/Kܱ*it}ơVvZŽ߶_| {=v!_jH93У|TOp )7= {GǟL~>4eşy4ɭ[=T cқ>Lﱴtm+'u-;?J?^bZ恹)?=Eҽ7{gǝe2~Ť폿>|```{ =Gc |=t%{~}+1~=? i&žTd>7θ){{{$zTs==cOUzlvdO?tSܚ830|NhسOmMKVJ}|+/.1\|d={O3Wnx!}g}.xw?١aOg|>`Q{L4{L=W=={yӾt/~69˗+c̍϶a`X|h;ɰ'սMg_lIO4{OD< ) {{{$zTS==ƞ*Nu 7S|rϥIWr6 mڕt;-rQ5'^z޵ϧ+wvɰ'*+;O{cp{=G==vn7u@=1}hŎ4m'7&[x~{)cO/돘&̶{ b>HGG=G5#@S^1SY|鞚L= m۪=wi!OžS/y:tϺɦz:Vg|>`Q%|```{ *=~S5i}+L= H}y˳L߼lya{ /d[އE/H>!SJ>@##ȣB=*)n#aofg}k]d }M {{ƞiӍO= b> Uz`Qgk`Oz*=Mm=ݏS|0]zϚtrA۷=13ٷ {ӪN_'ܻ?}eJ>DsuۅRc> @U z`QiS=[U01w۪}χ]scҩ<]{b+:g-HM߿MO{y_:۔i,Sz>@##У A=j)[ {l{=?yEںt/c{:T]x窴:N.8ivϟ=GЧ~Tv޽{a&ŞYe[Уr`rOT{L>bO׿6^6em̰ٔO*i[]umNhQS {=۷oOoƄH**MT{l{=q?ks XFIC|LEbOtiGr'Q R|Tr U{H)/L{fbbz {`J=U&m6R챕[9pv@g虻~7Sܴ"2mCt猍'7xpzH/ƭ}=GuPT==#c c34^vfO*){`쑚=U=GǞQB-zܰ*[T|.gMz/v~mnb*70ffoO:@͓+v.]>1y4ٷ}ig_ٝïө</ _O?>ջ|݃ho{fmJjiū{? Sz~ݞt<|*=G%D``=GTOt);;gSTO<}~1;/?1E4{νŴ睏._/p7ȶԶ1O`G<&ZSFK/_k)T{J>M####{@ʆ=M==CÞN׻n6[cϺQe雗Ş \aL/:?߶dq=ޛyP|j=G%A&c`=GMǞAϨp{lV鞪aϰ9'Y)Ib#q;ﶕ /б~]37/[^S>*####{`F=z5S 13Z*Tp)art=c>Ui{tGnv֕Ϥ{y !N:t8n^[-=_:9}7=G ?3gL+WLZڸqcֺuܹs#[x߼tyq?Kwx-=ĖOߐ.{͡ m1 * >:ݻ808>}zdɒ"~>}dzw`|q2&^mڴiȉ_bfc^7kq<#s˖-Gߧù)##{_sNHأJ`OӦz`RM{Lؓ'=ǦW|}`O!K {=$=0NҎ=3:M̚5׏EDS%&x8-s<'Huj:dkjz4iTO]p |=|a>{ (4[~}Vl+6ݰ6ۯŽtƿRNt&;쑪=z>4=)tȱF3| @[q}xZ7 ?cǎ1kNF7n3tϞ ==R9@ǮaQccOT 챝j>azGyadjժ#Ol{eG4P?[1=-``Teq(s$Уj`(z:AO[>u|F >`zCS.?Z\ߜ4΁:Y<8okm;7~{o;c9]c He=ݡ=먧zlV1S gЖ{L+ir=ƞ̞=;@ g:۷/;e̙|3> JPgr=GǞN = cϨn|(g*|{Z_m۶3q!yɒ%3ԩ؆mƌ=O/DŽ=={d~5kv4+aT88z`QqW{LT{L {Y|࣒OS{>ݰ[vN؂-^7AML}L39 Lk {b9@ԡ{8X1S~==O?cG='7aOi'aƍitoڴ)c*_DՀ# {mw=}B=*{֯__y! U9=m-U|#*aOkk6l_\ =G=eǞԉI d0gϞ~#Süy&<… ;F Q\tC##@==p+=GM<QaO Jnlnݻww_Nnck _r`T&P  z`Q=%RcQ'o>=gS4(qZ[ED=ΡbǍ{c'^'1j $=|>E`O#U\ď6lw*.tƍ^̙3۽u"p e&NLĽt1;r%CkGRcu:fsڵG Tz`Qgփb 뾅[_5i+A'4==Gtn-.8p`LP5%z5K,I<Ȥ8zq%>(wzL;?Ο>x=GG==ՂzT/책1t>=G/yK`ҥK{ž]vs~-[dhscvzS9TW`{$#S^=GS=­2c8iS$=G7}|b N[R%^?l5kSSE17 z`Q}TO7 qMc2cG%Ǟh>LZ\߹sgm&{ ԉv @)v׋sӧ"1wq?ɀ&Xɝ82E3{#IGg4{@ʁ== 4{) {`Ze̙}5cƌ#m0'qx^=?c6oqf͚8bA<3c6``{z =SUna6QmVJa|==GGECFJLmb (1q&` OU{LTcg 1SaQg(>x{{$#z`Qg3*TO%gT=~Cy|Oǔ```{z =hc:S==5=*){```{y=ĩbܪ?SY>*!{l&# ##:A=nĞQmV|T/>= == УbBHtO9{*={T2) IM>=;==أ`OzQN=Mʭ=(?)4|3Ĺ_Dz6=ŞRʇ7| a{z=hSn6r챕{j=sSI|{$ {@HG{@=c=(?)+@U{6##{@ʁ=z3{`S>*{`{``4 y6{{ =GMÞA{=\{0Փ+|F=O|S>===R1eYgz`QTϳtO{j=G#`O# `=У&cOSzʲ[it{j=G#*a`{|@{@=z?Փ;t=|={$7{{@=z`O屧=OHG=R>{ =GUÞTOĭ0ݳrzb}{$#i0=УbrL=rt=|=GG==Gy`=TkgTST1So>2{`{{` 2y ==inLt|4D 4|``=#ct=|=GG==@@{``r@O%g F`ѐn},a{{${@=GTOz =՘i  b``y`=Ş&O`O{`QbO H{@`nVZ1{`>HGGz`QًAS=u­p챕[{`>HG=<z{LTw|`> HUy@=*{=cS=tO3vn=س{od `G3=G{@ =e*C)VnrKK&K/i]v@]~=u5פEi[n6 ?Qwn}y*.~2-Y=maA7i%GvT^ҖK+Vd'w}>zҒ%KQNSΝ<27s]:̗J2t}킅^K[V>yzӜߩ^ yL{ܔ2^R1cF뮻$ѽޛ̙G}uAWG>vmunVz) |_jwyO})a+NW'@NhQ=o=Dz{Ҧ?^׌ΓmU_ʰgiŊӃ>\X-^8;?so9]qC+跿m6E9s._\4ŜcW[!mYB=n:!MvϾس՝w)f͚]wk~{Oאp7tS^Tаl=H' Tp)x{pd=R'Y`SJ`O'멧x?l ҥK',>[lIO<%=tK҉-nV[#y}gkv=J-#昴5W+= ݗƓ҇77KkVJ=>{Gs͛7z+mذ!]VS|q[/ _BK5 y7^Y]`s"kK>{ik'-4yЂ 7UVMx8Ӄ^#Nߋc [ 7|SѠ7&ʕ+E綦{foL_;hE ⦹}7ϥ/N{W/N/#^w䦹}қ֧5Wg+*4}4n{od-uc嫻k~ZZ'zYm:gGo][`{緝 5ҞMu0D&CD8_'vq}zk\Alb[ {F۠37>=GG==Rp.Kg]lBy@=*T 3( : R`({F=E)|= H CVz{13*_`Oç{`|`{$#38y`Q4T(pTбtO}`{$#38z`QĩAgҥSǞ"==s>HG=<#<{LTzF= `=>HG=<<z=z[t>=IGG{@Oy`QyTϳbg Vn{|{=`{ izTf-== HG@{` =`M c={$#z =j65d`g4S=#==G# QSz`QgS{*={=@#z<U{Lv <'iVnucG@# {@=GTfc|{}``OУbT 3i=K{$#{=УCTOnc#|``NqУJAxٲeKZnT>'t`>GjnG=k/ߙux<S!{A>!{`>8ٶmۤ3@J𦟯!;@{@ =E`OgPTOi=y|3|`{=u_w<S=Vn{=QE=z`Q'o1S.{LT|`{=`܁<@zTo#={`>HG=p==ΕA== {=`OUg3*=s>HG=p4{CzT{=|{}>G==pzTu*=zc0=s>*# { QRz;S==GeD#x`<* z`Oz`=G{$#v}`{{=j'Tf=###S5q z` {@h|z{{ʈ>G==`yT=gS=c|qb}``<{=bz`鞡>=>e#x`<*; U z`Oz={`>G @{ QL z3S{>&#Sw=Qy =PBUbOפa`S;1\n`A`=@y&=(!z8341S|{|ʈ>G=@y%= *CTTOz*={=GGЧ{$أ`0;8=* Uz=z;3T1S==>e#=vʰg͇mA|7MF=!J=@BMS=T|`)#ix3Ig_{#{`=Ğ:@(p_*=ʭ۹۹ }`TN$3=G焐G:AO/SiTO {-Z41S|{|ʈ>Gy=pGC瀐Ga13,Y:c'|{}ʈ>G==pG|G:B0Tzƞ2S==>e HGV2"TW{LTc'|T {ÞONW 3<=z:f7##{$##yt;tS=,{VX{ܿGGHGȣbPH13bcGGHG=`G@S=[egP=GGHG=pG $iǞ@TO9p4{##{$#{``QC@iS5`gp{LT{A#&{@=BGHn{ `)=)!t1##{$#U{`==CS=={`>GHnz{ `zʇ=z{L<[I=GGHG* #=A'_ S`4GHG*# {`OSL,-{LTo{}`{ܱcsx{@O9T1{T #{$#3%qA_=G% Г?)T{L {}`{㢽{ *=AcBtO{`=GHž[M6Mg}`G=G S {)% >y|4{=>G=R7~ a{ =A8y01S=A#2jF=<"c=# HzS=ՙ){LT|`>GHG=<j`O0{L>#{${{$yT =c@Otp{`>GHG===1Sݩ±tO{:> HG=GǞa@ϐTOzJ={`쑚>NJ]``{ `p){@O5z={ >#7r_==<==z*={`LT{pb.S H]{z=S=C=|=G==<'ds- zL{L>!a){{{$yT/==S=2| {{{$yT==S=՟*)t>g}}=G==RSy=Gyc=>#3T>=GGj 8oU=z=C=ya>#3t?=GG+8OU=qvZS=g鞚Ll۶ 3*>>###TuE#S1Փ b/CǞA'Ov,{)Ab`TEq&@)? z<-^xBd۹|J=GЧ}{{`Tvqީ&Ai5\䩞aL|J=GЧ4~{{`T6q&B)7)TOMc=b)@ {vܙqÁ6 ?^ZH%j2c|zF={13{NS >j8{=S TpuͰgʂ=!z`@OTOyz*={#Sb>j0{=`3H猂({z=S=`j*=G Cc`DO4{nc' {>%ࣆOH:`N={;3|J=G h׍_K {``\q~'{{=eS=%=#SQ>=G==RgiGS=N==a>}YzUHŞwa@)TOc=b>|TwY?==R} #{z`c'/=> #~$##F{{ʁ=z=cML{>%#~$##F{zF=a?`T=='OTO!|a# H#3:1{`OŦ{#S>=G==*>G|{``TQ==S=2?s1:Ա?t1ǧ:/RH;߫cOH_>K_tQ=g$%xч~F/H}qw 'dwqҘZwB—Ocg?w>C{tqZ|!NhmYǦ>94@_җůgi֬YBs駧|3$I$I$IR;sҌ3`$I$I$IuÞA=H$I$IgS8uH$I$I*S=N{$I$I$IR3, qH$I$InS`x7o^9s攛5k͞=t͙3'Ν[uʻޡ8;W,XPxoѰ$ٓO>9z꩑?T橸29J봍K˗/}$?cR֜?W^sEZo{m 0T:QQΊqM2kQst:Q?k?3:34){F|grgͥ{.\{r_̽+VK/TX+W,UVի֚5k/<^y啑vRnݺR~ꫯֲ 6K$ ^%x^\lkhhTkay`@S5EE\ZdK󼖛(keyUզz=eaO }ʰ>I;;@=凞"Г7y 7O){F1;4t>|L@i+ iy =M"S֩s=s+vnyOӷs>Чcʧ9cGGQ݁4 yl۶@lVc=|>I;TyL=ؾTOCtqc[7}@w<}@O {ozƞ  O1c;7S>@~$&L<y@=z`1܀>@$5x <۷DO=Ա'>|>|?HyT4=MS=z`1|)1t?@<4="n}c>S>@$x =ؾmUݾ ۹|> IP@zlVCu z=z =cϠ3't܀O|OU@#$Ay =mz`tO5{ʾ}>IPG@:#m@=z93([Mt܊|)SO?.^B @cz =y`Ϩ'w ivns>ۺ}`>$#y = zL)#={L>4|<ض S=gS= >|>A!Ayʏ<zL۷ zrc;7|*@~<$iz@Oc6{L|l4|>  =O13|{|1}>H<ǖmy@=z`c;Zm|)d?Gi*@z@OYmSb)۹|V@~$y OǶmS=>}$y@izlz@O0>{ϰNr>I;@iz@OQ{`= }?G<zlz=b1=:>I<@r!=gؓ=fؓn;7>vǔ)}v<>=o=H')t܀>@~$x =uCS-){@S=Ý m݀?I;j@cY=2OT{L|L@$x=yF=úQ){ =Mؾ =C=͜>@}@<z@{܀>#?oN]wݕ}o >?x[Ӎ7ޘߘ^q( ?l^}GߟGӟ?_hQ.@L~{馛=ܓ}mA9sf=Z<0q?H:;gx?pvnS8;wn8siӦe^q -ܒ b}wdx,kx?q.^׶mܶ STPN=s>4|7?tQG"9 |SOH^.:㳷c7_N8__]/38#]uUx-~_>~c&zv)d?[ڀO`:3{:Oƅ^f̘&CɎݩ8\{ntI'Y'?f* :k1ꫯNs Ky@=o3Sy1|=S9g?OOY&pms_] Eя~6{CQKLT| 2b|Ot[l?nLNKX=䓇űx)'>an{7@txOkX t+έ=}_N]w]v}c"cL9γ?<O8S!g>//}@S|O۷zLogc:=s=uS>S+HO8]s53/(-&[ǯ|+b5UgOj???b~/&cmkg REb۶NO~{=~v?֚%>/>h6u)X|4=*c6S=e >Z߻S6ku&ǽt:-.D cb8d(-*կ~=}N{&+uZ*"~n/x#qL~ ܁<'OX?ğyoy{6gqxĚsɊkF{s8j︟{=W\qEj_Z#>?q&~[;|Q<}`vnU>gl?K/^?m_! hAt9AXt {ڛ5kVvc;;."G=?*=uEۺŢ}SN9%ǂ>?ot{&\>h?{:T'~?m{b78>yÞ82q~^<i i!Kz-d;|ɇi=Kܳ4n߷m[5O=Ğx#=Ӝ)oLTL,fݎi-b 3xŞ/rLH^ dhǽr~q+\nM74ڣi_S:5>kB[o=y@ؾd۷agcvnO{({Z}$#SZ Ӿ3Dq{\zu"g"5Cر][\7s4Ή jyaOuiLm@&1Փ={7=g|>W]u՘bqΌ9̳|91B5):(\yc,b:c(+T}\$x< +s=_׍'~z]z.:|>x[k-o|o<|p`h zLOL󀞡BS=՘)mvn)|~fW 1vTEٳalz^>:梱Љwqc m42@VT%\;e"]x) b2)~c>H4g'W۷A 6ѷËi .1۳"-^m<}!7j2SO?sN6!t'fE.o?:M {1QE^|mL#I`Q#O78OcIGuԘN;l&bO'ḎtLL2#Ab}X;Q$M{Ư&juA=mzʇ=vn|Ou'n h-4bҾP?5oFٟ1ۻǏ=oo33cO Sk1ߣ$x O5LV˱$hs1'z:g#^/]ĔPn7q( t{=c>SJ1cvn{@RkS2?o{mδ0#H<@??~:c,Z{~e;/=۷kd#bd'u]w@w&tÞ38#-c?}'dvo[uպfw 7yZ_mL󀞪BOzLTi"zr=ӌmۭ b֟r)hZB'n;ǟ ET}h gIi˷ =?-*s=OL{L |qr`[Nb}Q {8?|VgŶm-/~q~@8'ÞCec!|0į{ =ЧS?@X=ؾTOt){"XSFIX_|9<?`[QۻM>`OF\kkxW^ye=QN~$yʃ@+{'xbk2@#$AO3U{*OI'4fn!{ޘ'L- {tg. ӔSG1Sa=Oܰ}v9猹OYm2'~V{c8B;~0Aϯ{ ¬\ӱA @QaO\3<<=&CO\rɘ'n~{Yvs뚓O>9=C`Ol9Cݶp3S/=۷TOct>M'־ZE3xӾAeǞq1IPGp y84v sNyaϼy';sЉ5kV}c]3~4ﳏ}k;v&==cO{{iʔO|ַckNi In eb/| _?P=B<֢=(ӟ=Cpcy~=QϞuYc$x쉭<sӧgSZǹ 'p8wҌ3&=oE ǬG~Cq҉'twgœbLkI!>G m@Omz}0z L>|؜qٿ!ML?s}Qϐ&Þֶf_)K/4`3ǞPdw^':"&NgT/ .snsΝ{b^Ιcj&㱜cvee&Cb{/ˇx {ZUܻ40x䉵O~l4MEz@1SA1c۹rS7}ݯUW2]Z7D{ɰE_>կf?='ؾ]CŢ0EaO]$g =΍~Fh1M?LZǴPǹsl-xݞ4m=2~+8/}wMkXqL>=o33t>{^>gygT u֩ت ־0Ed7+m?ac[,w-[~g"4~N6n,0cQ;&+&b!yz)&k3΍cӶq (:8?Υ<<3b]EMλ\=Ov45I\K={v7>'ߋ5HZ-q*>Fz@=gS(=U>t[/uO\Q <"@#"X#x D)se[a g۶j#qS=̓± |?GwO*zLrBl{`))T ||>M @?w  I;@z@='cvn}?w@|>H<4=cTOSz=s>e߿>$Ip@S]m=TOz=u[|>@~$ylfc6{ ۹۹c@#$ y iz@@ψvns>Ƕnǔ?I;@& iz@l6,y'={1|>|?H<@z@O١}zL}g$c=S>A I`z#=cme:M=F{[|>$ @y 0cct>>@ IPS=۷5=d1}?@<z@TOYT=|Oǔ>G H:@ȳy@)޶o=U T|önc[7S>$#ylf`i.ؾ |>)} <#=TO٧z=s>ǔOǴ @<@NAؾTOͱ||ۺ #($#y =۷5s'1c;7۹|L@px OU4=̓&ߧm {L|O@~܁<=1ӌZb-@S2+Ip@[y@=TOu'W>{Ğ=T |?Gw=y@pӌ ۹=֭j}?v<<zq۷۹ c@#y%x1c6zLz ={>u>$Ip@cI;@<cS=t܀Oǔ)<?@<zܧ􀞩AO||>oڨ7?/?З΢qHpH0dP ! 0A1!D[F dHBfU_{vj>BG(`Gy=fy=˷ zzF=f,;GrnG#( EQ:G#zܧG3-* fGc97T,ϚϚG)"yf}>B"R"Y#l9G#|ۧF%wVϢ`G#|>BG#R"lzz6Am/vʬ`Z{ܿg[>SϲG#QA"x<BG)0G#lͳmm-cVrnς E˗WYWUs={w}M]QEQZ=Gt9[ɑN9A,3c97˹ ||X3' |X@XC,htlIo((>aM'[6\;T>d z,f{>k߿gJ&#}GQEy<!;"$Xm|O|bo&=fXm-=k>k=GEQ;]!G#i=K=cž-˷-3gѰGcvgaϞ'#(B(pGx=AO#Xm=Y=.Z[v>[>&G~?("y=O=a֕ճ cvG3YcvG#Q? EQ᎐Gz==wqGcag:س{> {>GEQ;E#36OOAǬžskv,=|J#}>G("(BAg]Bgl#zNz=fXrn>UG#}GQE#QB")vBEW"hC#Yzܧgg(3f˹ |>C`G#(!(BE#Q, {==o[wVgyP#|>G)PGxy<=A,6{{>KݿgN#Yax㍻?? Y>g?%\{ֳ(Cȵ^{{+vwyHA*}饗~}|#j*??sM~%/y9yo~>xr]wʽ^袋v'J馛q|!oz/ y=e{կ~>&s(z,߶Q`=dl {| }#Uvv^7>я}#ozӛv??_y뭷NJIA<.%9a|ы^tfN%c~vo{|땯|/|>#Y6wykK{v=Gd\;o?9=H{zƄ=[=ogVϬaG# {>Jna*GU[ .\ A*V;_]zu{/www_~}cӟ}9\z>}?9}DUuҏn}`sK!#C&'@x û'?M>}n?o!> jUWP̤?iOgV3 CPe{' †]~ g>gTV]6u\ַu!2Xmݶ /`<|/s6Ps6}lwݻk>:;[ 6abп3~&zg:[JXC 1>_xƾq o+D8*'c~I{]wE/n0;7<7 {m@hSzz XmG3 b|V eU~n~~nKK-Ss {|??ڧ~?c?U[瞻w p d.e2Ua5Y*Buy'~b=A_u;S>3?3v_5C@??د[ IOڷ)z;9xDZqGcVۄ=f|> >Cq`_&&a+TN,Qvt1 u+mB{k"|ʿW{T`h=/8q-R[8@]YI<< AR&7pnw7 *XYTzAq.n 8[*".".:~ %/jkzЄ=žU}Pp hZ3N񙈷t=AggT#𱜛ggh#9\2'q W ^z`ϒ- B  ̓.pMXOfLoPw>ߨ (=P=/~K%窄b(W`+ eEiNE{Qs^iVd,ϻH^Bؤ']ɺ_מטGcN% Z7"ZDZ:`sPb=l>:Ac>{{VɶIn:1Nue??<7% y~ t{o.6!s[ .lH-Cf\YIUUߕL=Bg>.umOT#dS:Q.&#p z==}Xg(3Tϵ{N |Ƃ=aegDr^ݣTg)؁3% Үk` )UeO&xs@rru=80hEI0 i3֕b1uxGa+|)MmMz!s}[O/Vc{=KiWsWu[u(Ou4s*aߙ6d.^SlÜD< e_`/|HN22CȊ/u2.rw(!_@:AS%(w$ĉT׼5TIVd~#C~~kC?ßOlBW UPos:ħZ:i=AgY==fXM3|[=CG?\+A iQC>tù{+hVgѦ)gv7?L-+gNk*[Q{z87I[߱_h^9]~J%=(Q|-9LFLmRnk?G};Ex㄀:M6Z:A&iN&x!9G91!` @/%n{sdV|-rYw= +:!m(˿?(=9[Sx=̕]R2A?"69vja2)M{uy`e]si|ru)=\u؃Bܑ99v"dc |^}X,ўq.7sAOl|ېgȄSI`G#' 36+h|k;4w#;1ć=Y9=%+c69j<6Us0#JS]5)~c7)@3NӟVsfNR΍š~ dlRRnK?@}{vzǾʿ<=793ny>`b3m5Ê>L1:VMgC 怪s?g'nzB{K؃ jgF%s5Ú \O[a>NNea\b$S ~ sz2g ,'K {2|F2B/oᦀ=!&d'g[K[=bHt 'xYCUSCĹdg頧z=f#Gc97L{|p7ʗ}h{0j/ eV& pby62T0qq1JٕN F8׬zp?Qbl#@)0:7x瓅&hW=AZdǎnGZ(4H ҍUmSd*9l+2,|S@Jpvif[3,>YVڮ]W})H-oӯ}PCO߸}y3:ꪫ/UA/MяiW 4Ǿ~ݿ cܾ{3T%e5xuki~ |Cg46'hOߥ_b3eR>޸k:ls6&9;CMyNE2.=K| =ϵd+4h.%=As3W.{0/s +z('AqL[=G=t*~ #n[=<>y=ƒaOl3,d?a =ƼPuv!\r~:>mֆ=})G?o+z=Di-:{&k=i7캶@aq{5a DK2Wiڑd0i/ښŇy2'D~Y݇oa<1/a3Sa?y(43ϲO_;WhdS{=oL{>f| |=s>{>o{v3 o {9Ϊ>Q _={kOÖB09=L`upaw7b9܏lQCS^8}#<#N161m>89cP`c. GTJ+\o=[ C]ucۖ)c5>aO@;>9鯑Q^bb踎&cb*W]+w8/6xc3Ztp LZ#9{Gu+rpc_ܠ+Gc6p$sBߨ(#Yu/!"%@οAquqx7愾B-5{{>žB>vgA~T?@)|+,z%[xN|`ukG?5ڞÎ _ {H=a7Oߝڧ ʄgo8o}(O]u:|E-7θ;ĂCwʾsN;?GfNKs{지=s>vO}-}gv:>!PSS c=?Gb'@D=c88y*a7{cκ jwc88zNV ,D#\J7 ə\|O{x?M(ƷI^8msg0ou:}u̥E]tL%Q-yMp gc\٬ܷg,sUGz7s;%0ι?qJx.ﲃs˥!8%]G)|:C[ y5hK==/Fq>}) w~n>~ªWJi.<#e*Dw{QjA A& C'A_KչǜGVAݹk?K=ܷDm::;Χ-} ުlo3E?{܃S{;e=@̡]*9c`s ق-5̫M>'Aq"L+AHVߣ=|Ǫ *2OYߡÀ<f|i+8$! ְ[k;ž`IOXd6\:q˷529ϧF'K>l ,}~A`d6 PW؃H >;ܗ c3lcȗNσ(t f^x0x?V](q,W`1'as=\F8/aJrd~!'V&Lў ` W=H f:\.7c) w/u=@[KdJ:qIpǝ;whyrh}K/E]U!0$[= ium dҡ[ t"}1ϟu%%Ȩ)aO1.Z {^UMܗ90J=1^U#A-Cv(m {/P݂FW ܏~61mwgZf08<ˢ,}#QUږ9~;|%`GcVrnf|N|Jس3%) {$9Hx]an^A!L鰪l~ $0湹}a%HNLcB9 6j'3SUI$yu51hQ=3{D)ap' ԽcÕ! ߁`f~K/t ڎS@%Fuu{ߎ ~5e60/:E`;uexUT/Y{w; OgM舼Bo؃nWJt0v羰9y A?oye|{3Nbس|)V:e8?Ry.*7\@ ܁f@jc'ufC.\K;K=,cFEgO` i{*d YU`p*@,(}a"<| C3Qrty2l*؃fNs?1b(`G>1@fcJU様3 YeV2M9CA~*ߞ66ema/ZU6= *=A_B )}fKɆ +M 9=QfAŒA߁7&Ot$g^ L |N}=]O {sJ7~+!p_*gdh{@d{svq;fvuxr {yzFfG]K*TLp(;%dU{6i$xYS`%_kSFJ!۴M쉠duLG❳ }[ Xm^Mk=]`+yF1>Hj6a9DUR=1Xw*庮^ʰiߡ&Eh/ L=s\[ycB+ wK'Jԝy1! hOr`g`6؃Ǯtb>yyT[eǡv&!g=GJaOTece<$JzDh,+as;ᷠcK0H=]=U鿌=ueq:?E|1 ʒZՂ ~^ ڄ먎2{J9DXPۂ%Ao4 6y.`e,Vet=+w<z=`|MQ@·b./<>% Cڏg{zk;K=KYl]`Oo#`cn&K1 VBf=F ξsy1<p=}\1AnGWSX mU?z P%ލ:0W͙* ,3xNDB1aOd ]ac1yjj糟Xm$l#c pNQꭩڡB{e %ڮx DK=cA9=9Y{v l>g田@0Jc"Zu8Fq986#~7|p9 nX[o+784Cݯ!#k]]f<ʼn{Re3B@<=<6$DN~0GMg|H@v mGf+Y=Jp Sev 3%!`x9Acm(E?v`LW!Ax9H/"`/`eri%z]WKܢd`+K %j9^(AN؃)}ve:d{qC9+!",`0ugyaј 3KóQSuBa}`Sa'}CfO]#4s>M;e˽p=#h3/O%c3_]n|Y rt, /BVr ƱOUm@&FTA?2dNpr0ʎ;ssxVŲ7ύ#@-j+˜nc@W.0&ƪ2p}+b%&rО8|)agy2{)ɛGZո H,Ni~3%!SUO @,8#9@ >3í}=ˁ=YRH/NeO@/q;}ah]* =D$V2_YwY {{ČE`edA33 . <ө`O[c س5wC07;$ye2T!].(ae;"ue|rollw2b>RÞC {S*DMpM.}''ֵe2 )OA'SY{x?]Δbnix<'هe~MsƳ2s*F0,NX40eq"vzaϔ*3z,6a܀>qa`O }JC,˖Sci {t=/C{$8%A=R& ")n\C>88Gk:e5c˸`XEǘ < {F]~C@JGUS e &U*؃PV$# e _YFsZC` P:>ݧ|7.k, 0-)˲ea~azUhҎ]3`B` 5?gJszϛ`;Cf_ {xntyޛvX+ {<7-3KCQTMaa}]yoCʸu=!`Od?m {ֲE+mxl!vS# Pvqd~>/!q&؃\cg\F>o̼PNj8) ~'\5p 9cTfe^㹆\a]^e3z99챜>>]aڀϜ`O}`Oe`g P \Z-6,SV% Ǖ{\ǂ"1COM6p¨ 88S:$䲄 }}\y*C6F.-nd`V W>qv<йg.ۄ#0Ipl.a<`u饗C :qO3yK=xe.]4h1_=;WMz<B8b>Ud3F]y8B' u=9ދScÞ.b2FT[z]sjq?QCoPNannjȼ( q}]'/fPD{7UY9ms ϋ~^5,ޫ;vy2qﲇSu5Eb{g}b lgK=CAgˠgn'xQud|ֱ1YA4LeP0j8 8mNc=~D!ǩ LJ9'Bg3+p>R}xvqPx4A~ֱaZVyV*8iS~s8eLH҇迴7zw[{b 2qT XCmIU|axɿf҆#*y 7 O"9ceif)}aOUY]!訪ߚ`-Nx%HŹ7Tg:Ad>E[]ž~M9|-rC,#݂>yL}c=ؤy=7e@ \C^wug٬/Iy}k\ |>'Lmc7g=Sc>lsK\Ge"/կ>0iSņ/9XRaga+= *APG#3gGrngj3Y Λ4ocfG92o`%jB~ \U0^V|Vt,a~BN9 }9F2 c@N_.BirO" +蟀5ogx?T"Ϝ0%᜼!rc!yL7l\#}39#V='6Z~?raπ{]C?͠;Jj)F. :fPY*LKV! bvm:H~^{0oU>EE7K.HV/dnP)[]ž~~Bɶ 'Cžc`0*hak^pw;3K} TMt?Y1O[}\`;di+`6e|ux&c7ϩ|쏀2]h#tʡ=fj> =\*ĵJ(E[2z=Kܧg9-g+ܿS!g g :Fx.+q$s59嚺kUbcм?M8e}mjg`U^ 4^@%{xg=|;2yr>а=P "p(u1 3#xZ`OMƀ=doWoN.{h7\V=gE).(Yz==] tt !@Wf`{UK.ϙ2ijw'2,gܡ!e݇>&`C+gj27]SJYq߁gBGЖ9!۔=3 {sLދ6bJaO?C>/52=@{[?hdh} z ĜʅB/}^ז'J"9Vv|&Wf(s12îj=/es^IuM·ɿvMp~.= )\wOE7AF՞;>OC@ܚ* Z29z=f;g6˹ |N|=K>K=2{X*cVN2~UC31;>V铟 f4B ,"D #@P v&x`iSB1~g6j+ wR=p^Q^,gn#W3&ؓ @&q<`Ƶ9kP!*ߍ'9-$ߕUm.y/A,>{bh7d@[VJ"JV]^`n˰ Aد!?97's>|#1p"lЪwQV .9g+{1p.ϡ/hfOv_#:1ElfpT]4)%mOF!eN&s]T.a|EL{x[bޟv:|D&>y*OorW@ﰻm 7{d{f죲BA9CmXU?EC'~x~!ߜ =Ȯʠ ߗ3ZXCڇg`<E?xK^=mV.{==o[/|>ec`ϒ`Y'`G~Y=s}h_8vSc<6 `Rb*3Uf~Gۂ@-zDWsA0_džyXYFdZtc\"y b {%Ca}gqi;}&Y93* K)u{YA M=Bs}`O92n|'2"XOŞ{j= c 磦A I $w {ƃ=e߈> XFٮ X"sБK}4&iym'%a2BҢ<Yߠ}rUWDA?΂S s1\z5%_3o+yjx<~\l`` r+rp&C9sgZsxy>*.!K(B_B_b_~<~R/jt@>}5l= 16}=͔al# {xvޟ{Vxy1B~@gjM/`3a͊n+LxO] ֖Rvy|]J˅]`˼Mq.me._1d<_ɸ1Oa6x<;ϐOޛ7!m4Yyg^[5 RQN9PwBwz=Ӏ˷ {=ssOos,9 Ihl8ʕU]aϪ߼.V~ab0]@8[9¸F;88)&k#/ϛ)9o6\3'm3,u#mIM{pCFԐAzϾM?CxwڕtY.L|_ޱ5|6UB,}n?Xt'pJs#=x>#mZWt1nxFJ39ž(kEs]$c|r8'E_ڠsK},7g3[SڂsQdFP|.v% 7o{0"rNƘޟyѱ̧+7r~k'HV}5TTY*v,j|9êh:Eɝ}Ϻgm [c&X{9sU&̴)R<'sHVOHO c{*C=y)bK{b߲ ̔, z`}>]IS^(mx>aoF҆MwZc6y<"$ lei9~>C9ե3&8nr'ϛZ6+йUXWfp X:URj*^%"Y!p+}JnȪUž ;0pJ.==PU V'l8́}{8Qdj؃0}A'ZP`OUŽY̏X%NvY`7%07-G/pQ;EXP:L#>X=Ԫڝ]5);rྏp@ `vKži`}c/ G9yu E6^qؼa()퓁]OsoF!ǀ]MUgcҏcFl 歰YGʰ̊їq޿ t1eSlL`dtѱG@Ok0gGs}=mFʼK1cVK*~̸"Wk#TXk~|:3sqx7h,b>t^dW, ,7 >AO#qAzf {# {|2w/kciRb3hU0YU h|:8K1XyY)Lg 8CU8܋j`ʂS˽hz + j!]1D)?tVʉY=D݊Fo}Oڛ;o2):i]iwSoծ<rCYڃ>̟h;oD[|^]66n GIOni,(˶|G B6l]qk=}޾{춷->yM={c(uHAKt$}}K_DV^ :5jw\GR޳,/V.E>oޕo{" e>B|+I ~? J=~\ KK`)}6mʧxCp,}ʖa}yo6p/d_511]KI dOmmҤC v R]r2W1}8g}yw<`{"5<+mҎmN2y620W\>mO]~t[)gx.bQ֐' ?L{uЧ ?is s퉮E;9xN ǽf||vŷGqڴO<+vh}m X3,t=SĨܧg٠m=g |ƀ=K>8w}ݧw+_Ï< "] ΋o1 # y+Cǵ uvZ;y=x|NC1uxC!O{;}k AMCO?Vg p%PŦ~=L`Ͽ~c_cw͠}eϏ>9X:D_D7cYT2Nr)w|+Q,811ƽꛡṶ/2ðSx,b~Gww_ =r>JcvJgTcZ@jr`U Sꛅ-=/zo㙸Oc:ByT1OoS~(Y Pg |?{==(T nB5J'ӡi-u<̱D3]7,BR.d,۲7H̓d-BwImndĞl,,"fK>cÞSӼ$:>V R΅M*^Y/}|l]=3M D6۞:(JW_y=e7XDmC(+J1rLz`細Gc!AϨrnf||=KG#Kpg!qeLAg?g"kv^e'?yg5Y=Z/x~+t.m+^ێD?bR<ߔ2de{($^_|gC6_jr`'uÑ;} FoP%h6&c~dnۚ%Qg Pĝ-ܲɉ`o|'t}mC}&Гa}zܧg}Y=G3s #3X~((e)2)?F=U=zN {==f{F=s, G#Q5#AQE(BD?G3G3uC&g]m`= ,5g田G#}Ꮲ("QGQE(B!Dzmˇ=AY=a>'>s=G~EQ"z=˅=u˷-L {><\`G#(EQExB"QEQ;G#,,{,߶a{>9G#Q?(GyBG((E#3O#|ۄ=-綵, }>BE(w!G#Yz=Szf{,f97|aG#(EQEly=)b[e˷ {,&D9%˺ |>BE(w!"qA@=\`v>>K=f}>E(w!"L {=˷{>z`G#(EQEy!eۦ=˷ zQmUrn>cRm0gG#}Ꮲ(p9HYz/z,߶mGg# ,|>B"QE(B!G{=AzV{,&>K=BGEQ;G#z=Aۖs|־g tͰG#}Ꮲ(`GyB"RE(B!G#9 {=f {Ygvg;gMG#}Ꮲ(`Gy<Am.6Xm  #}(`Gy<AOw# l9gvrn?3~vrdB0N-~;y睋k5~s/|AAnEQE;l>@b-ɮF sʿGN3>CH3/# z=f|>>S 2Ț +[ [*<[3l E1 &/5:]2eFг^>=,߶H#|NG#~? EQ:G#.虃/{Jгv#|Y=l!g|>eϺGExf}>HQ"Y+zy=A!gc97G#}>B HQpG#y9=*$91ggUgK=c97vgG#( Ex!gG#z= |֝#G#~at;!l˶YM# {,f97G#~?ErG##*q˷ {>G#Y~?PH(!G#z=zNzF=sgG-#Y;"Q;i}Ѳ`˶ zzZMcV`>,G#QDFxf|>E("x<[<˶m {z9-Y4Zvg3"𱬛Y>BGc@WQ@#c6AGг^39lc#~(pG#Y:1lAm=ž GcG#~@(G#1G#O@O/cv{>|>BG("ys=G#@(G#z=ӳްrnrG#}?(BE#z=AǬAArn#}>BG)"x|>BG#RE#y=ˀ.|\:n]QEQEQEQEQEQEz퓟뮻X"-gIENDB`c-blosc2-3.1.5/images/Read-Partial-Slices-B2ND.png000066400000000000000000003346051521743436100212700ustar00rootroot00000000000000PNG  IHDRsRGBeXIfMM*JR(iZJJsX pHYs22(dZIDATxUE_JKRA˜D@Am@EPQQ*@G1wۓs9so޻ju]뚞nιOء>g`0 `0 `0 #?oAAAAAA-A#      "       ")hz?s|G… N?v|Og}4>A/<~ ꫯ#AAD׏)S;q)"C?VvW.\ k6m&жInl1i ___K_++___k61~}boFmo;;{{11111O5O555/(/((8o8o8***:S?sN?qN??:5`0 F#Ԝ׼RrjNb}wg}u]]ڮku۽} =}ލ>ɞddmmMѶg׶׶gضش_ٴٴGڴZhǭ+_ۏۿ׭>i}޼&n&mքW +$1h_ׇIlԝ oŸ iş?1 ?)S6tY?O(S@]LLP[S1))j֏ d?w@`0 !뇬~b=?>Ol?uO?Đ3 [R \/a `> ?fǟJ?C~@}bV; MCJ??e ?) `?CO۰Y?[ @˾?~= Y?y|kŸJǟҳ(@ `?O!뇬 (gɩ[?(Y?|˵LhH?o`0 PƟ&r?)~* ??OiJ*r`0 ~I[~?c[VrW|dx~)VB' P ?`0 \~7.b/VrS5gɩ)3*`0 ~!Il?O}@/Bɷğ7?dP  ~ʽɷ?ğ>BO*}l< Đ ~'v!b0 I~!뇒oq?>J?C8O,Y?5X-~?mlW4)? `>C%gh)O߆&Vi%~r34 'od@`0C˽?&{SOCߟ!KM  b?[7(F2b0 ~ ?tßb*38SZ7~O~??`0 !뇒o?5iTo1dLJ #gtI[i~'gC1 ?Y?eg?VmA'׬J\O}R+@%`0 F>CO%OߟjK] 'o!Y?f'1?6*P-Wr @ `?Y?%-kbş^-I2I⏫Ic ?|'?~#'@c0 ~r(bOo6bqQl'iĊ?mmg8?9f@`0 C*'?Oc @J ?!~J-|dğY?3T  ~R'e~ Y-D[\ … Y?5gom[c @6iOPC1 ??{1뇒oyg?ϐSBߟTK?g,ă?m}'MCOY?@ `0~?o~52gouZ7)(P  }JlJ?n/YWZxß.Y?'D~-}p~? (V)b0 ~(FOY?O<@ϐ}~K!t[9%ʿb0 ~~?~ &)T Y?|''od @ `0rroF?O @]on$gRRS))oCJ?d@`0 'v~(F7?y ğ6j~}J+6 L& ?SJ7k7 b0 CC7~R)L}'?.JŔ>㤳~b*OnvJC7, `0! l{?OSB7SrU.9)[ ~|O߬cr=g1 ~~J??'$Ǧ[j?$gJ 344ij04$od@`0 >Sbcā?M (6)c?CǟXC6M@ `0~?t]?>&?~5+t?>J( @ }x'>?%'U @)O}R(%dLŸY?)||P-*@ `?Y?~l'~?>e~cߟK i?.~(?!o`0 ?d2I.3P]Ϡ~b/k' (5 ?Y?d?ĉ?~kq?&%߆I[~J)@ H }r/?~lǦ V u} Ym˒o>RJ|~ `0OI>?-뇒o'tK]OI9'~?)OE\cğ!ŸRCT&@ ~'>?dC֏OQ ()usqC7~|O߬god@iY@ `0@rod'~? ?P?O[3.C7JŚ@d@ }ʽQP%B)S-~?ӆ?CTʿ?Y@ ~7뇒oh()oCd\p!O] tJ `0@ ?{3Ō?Ā?mPm~R.??ie@e`0RY?dZ |O閄?>Kd)J N>ӧ./J, `0@<~);~?OO*C7?5.Fd?dCJ??)b !'~~(?e&)%?| '56%X't>z7.V*@ }t'>?1⏋ Y?O.w7s?|K?~# `0@!뇬a~?O-ŸPߢŸY?|k6??P,Tj s}~4~~+c~ (Z5??O?)OJO{Ҿ}F~hʕjtWTW-[FoF]1etҥs.7̙3Gx㍣3>>B"wuWuhժU{o?Y?d ?|/ _# \-DSq ?|JC^ӂ ؽ{7Y@ PC'N<8z'GG{՜917)\KLC y[ǎ8p`Ov1zgFUJ~ğY?r?}?q E?)| ?&=P-}!o1OlctN Tx饗Fsiy棚k9杺.wv/yGXԱ}*Sfݺu+V,Y2KF_|qUM%?^[- \MLO%.?O~}b*Z-~?S:oM駟mKH1cFU+vmUݧ tܴiSqhՆב#Gk(q[?aJm߾}4רsTÚ ϟYf{1+}OVom~>+S67)Ol/ S.[ohC_c{Lj>?dC7rc @U[~~OE )l('!?A6C6ƥJğ(A?1̀w}Jt(V6%w裏~'oST _%~b# ł?!gjLMo@yT ?]hϞ=>`uuUs(s['v1l31Ξ= x*#\w9DlSO vOV̞=h.y~;qrV?S3C7?}b/'ɭ[H ?(Y@c @*61W7"SYw_u]3Q-.y'{?%L_s /t ?^XB|5\wu˾燬~ßT?.JJ5~?~1gho~PF4 詹^)wyϸ;_g̘1?;_|7O3}<6p? m޼5j ?d]-w1L/P c7UmP ]~?P-G:vČ?PHq:3.;R웕_ݪoCrTK9Gq?-e˖y"Ěo ~\i s q3}M6? {!뇒of?  |d+ɹOc ?3!o1P,3^ ?m(nqY2,ŦsM7Us'Qh4Ν;?o@ɢt @o}\zu~!뇒oK|~K ( z׹ScÆ [nebŊ_Xl.q|ͣkv`'erb.3ğ矯6~ў={עuN$ۓğٳgW1|o}MC4cꫯ{Z\swͷFϪA굦94?us xѾ}F;v=~ztСj%~–|6jK0b/f @N%[!=S҂Mϧ]V]6^}UR(<ń?մ9c=V.r̯_>Q(J P&kޫu2;nZ B ' h=f{>|s57ױw 7L,{j2?!Q2~r/VZ2lOw[~'$'t>C735tԶ wqG?SL =PMUsR%Zq# [>*kt>u8r9}Wss΍ʶ?OY?fĄ?>~tl~w??yf(cL }'vœ9s@iP33.tdzĂ@㺬ٳgƟu_]KO>S@\~1t=z?Y?~OߟV] c7gSJɷş }(Đn,t@z]to҆jkKWu/Q?4鱺~{SFoVU^N8Qu- u礍QG>ꞩ uaɒ%VǼ6ϧ>Ϛ+h_XԎzl8qP&699ۙ3gcرc՜Ve6uOwN>]7OPF_xhե\TTG5 ֜@[t}tQYV8B_[o ?]RK5>Cݧ_um>?n?}nտ7op~Lly6CGk׮޿_.6R5wRx`twVeͣm۶[!H׾T'΋.z.G};?M6o:y7 ]W F7 6_}mۆM}߭@g[JUw6ǍњfRzG[3T}4boܸum[n]B?>曭!t>unS{ﭾ3G:}j%h=+3e˖U~tIX8ukD[|yuT XfhϞ=վY?idJJ4ǟK5O?O}&6J_k>:ޞ{Υl1k֬ݻsڪͪE9۠k_C kҦW\N O>N뮻qW}AMGF!L4CBCs߭}#x-4}`{|K۰=o|\oZ#r-WԔ.cM Ck1~+W >G당s=z]mmS\qCU녮z~+99P{CLfP-~?\~s?|K?~+-'hcfkǛ6Lh v 7dz~]>Cm>٠FXM+RR\#W>#Y[ukjSfJ׼7(ԫ)4hT/67 e}?ʤ=4Om*fz}C7>|x4{^yk+͋;w >馛^"B^@Eͅw59skczk~R}Gkbei/J?6}0@%˾?~?9|KOL?-u?egc_ރmfm<߲}{NMqWV*563*Ib7VX19TF(qk/vV] X"\|^JKlOz ?qN34W_S*]}!VҚ9Ă?𣵒ǵ\ǜ֒&Q!u%B=>^@)ŬJ|gŸ.b*?ϰC7bA?*A2i8 ~wNo~ƿyإvPs}]GN Z'wu }aJؾ8}t_]n1YuݨWYI[}FXvq}p?:vyk }76k=,Wܹq8tO?1-h}kSJKG]Y?|ϰc?~r)?!J?:m~M2fu ܊MS!ez}zP!g kgӟO>P)]^ze5>!}`migT%֏6 {k<5 >xw:Ve- V5W'4^zmcp*17Y?~}KK7I[ӧ?'.*gh@>GuV_֚8U\]K]7Ss2' .|#oќ37!m@p?]C'͉C7VUI3T^.+ tݱ'hNof0Qh ^zj\:޽;1Kt+G{t˗//*뇒oa?cs߂Ꮛoi?J L_z۴pg[rE%VZU_Zu۶m6ϣmc}Ƙ6Ecj_d?Ǚ|*79i6qF?L(qs - tn74-4ldU.J Vѡ5NG {Usqf`.Y*//VS;=Fɣ~ Is[~'?]~(VwZ/h|A۾:&& }ͦú6[g@(@y=T{o뻱]|k~tl^iz?&c6`R5So{~\ǜ;ڈw{;wZGBG=}PP[(1)e7nO8Q5 GhU'ct;wuV^ 3>a{ހ?nCk3mz5ŬYF۷o%ʆv~l ]mnkDs6~D5YZCX,.eC7 ?㏇Jӆ?}(N^{ÇƯ]6]/G)N=믿>qQ;5tvmƯU=$GʺM}>뽘l(~_>47g&ڔzwWH2ȶS]w?55w`sfG4!窫JLvT;O LN:iNP !!DզvLdhjݖ?K8p"GtNR?-a:t}U53]ϟmB^xS!ͧB8®K5jŚ!G)ڷ<|Rs,Ι4}={*g:ߛ\d|9sW}u[WK7?3?V.$pwϯtkaÆ,t`GM d͍_u<\U~'BI]fMiHQk6?nw5-t!GY=i4l+!U/{j#OucTOKkysƎ?3tm}vŊ{ڴ-9]w0RL!IF)) MC͛LUo}5Y?| |?K ?Ce'e1? ?rCOV"bj>g0/գf>X-6?lgM7uFO~躤^mԩR_:.M>6c?: !D՜dukjUry1OsJ9+D?*frmfs׈W&Ͽn:CϸMYPy&( Zi}0~6gNMl͚5{?1%(?m|q?.P;PmO)\X]) ?d; ?6oMGM& aZk߾}]G`ޛ6?lJi^ד 7:lJd?>C2BBǭkfzؤv?M(5jY2ho^l\6{t36jeLM_elz ?jPm̧o.ǓB ;+Ҫ$q?~$34P(3qQ J>T$:lǦ.B-(LkرׂeXئ?~sHf$=tAG3wXmlb1lJi[:8-u 7| ͛hm#qϯ^|6m*+m(懻w͚5OyJ?6HŖ-[̏aO֭[(L%?9Y?GOI ~rS*6kPm}_vWZe }6cSf?㘾aZ^q3t܅~G5~W܌կu/sI f4cڴ޲=wu,MTGC]oh1湵n , }j]ٙkGai(\͏3o9]dO !iYk~(JOZ3OlY?9O~? P-_iZ(UHc eơ6}zj!_ 5Ue(Cg^{mg @6(3b?]K1E!GsY9[[ٹshm۶ц ~6ǑUSXԹ㏎SW4i'nI5뮻TbUM:~\|֎6z?vivi\r%-$imޯr>Gkʺ? A}~e߿S6Y?| ?)MKI.r.ք?]R[)\,r;v lʈozk66lcHI ]G@Cа/BBmzgz S㾿[+}-ZdLGǟgf7Ͳ}窫W.wG.Eʶ ?[+OmI3G}1(Y?e|+3,Wߟo'ƒo1od ƅ6Z6%Ħp1lĆ?YvmV? Lgŝ'iBCU5nLji}jh.~zXrO;]'sk ͤoyW_j!,sԚƟ%d-[ (t=i~Mci3gά"뇒oJƟ>}bJyß~ ?6TS^ s=c/tm+ctsiq{7.unܸ1kko6LPfC->f9h.qyC㏏{:#?S@.GZfQh(q((͏\+Gt%f?t'D"~C'.3 ڵz "ѽ1ŭ25-u/=xmMchrK63|:3ݘ8⏞S nR u}heSK%\7&X;]VnNףF_>r?6%l Z+B{ ˗/w.\Xݻr?>?JE?uo2~?1oO3d[?]g)d(:܆?ʈiq+ut]?6vU/7-( jjzC5|Bfo~zlâV Tm5MZW!_cl̝;7KQlL_}g_ y?~G߭sa$6ǝm~`~֛%u=4*4ڶmqz+iGYͧ|B.1͏0şqhB>oA%'Ii>JߟS@O~OoɅ ( @}}]Eڌ3kPZh+R<7 F}vS:-T)㏏^xћo9:}?}U޽ n?ٳkzo/??4L݌-Ν;}wv:?{ϸBצm9sd?S*@yІ. k\㏮6ޣkQh~e~2αE=̓.] .jMY?R*J@g}.Yj &ѹb\*6 .1}~]wYѽn?df 56C ?[nT~NsMR?wWs{?^ۋ/W1͛7[~?~Ʊaiޘh~}sFqڰEuʬRFpڐ#ɩco? ?'~?OYc@CQ:tWz:HWALOqh&k|駍6\^_l\2?:nkqonƈ?]!(EyaݚFkӽƶtica>?xM揫?!g<7ӚӶLkQhg~LZc^5W_->t]V-&?'9ߏ/O}B~@}'9eIֶjt?i|r}j}q;-7)\bժUϥTo(B'l6sDl >(nm@ qt/*tۖ`jnċ>SGa^˖-Kl湚OqǔS*;o<~dCɷc6jKȱO'\fŸIdӧFqL_dj3Ǧ ֤ORw TF{5~|x+TkWt-$?f~n'e]̏cq̳oa|J[ |S r k`uc[FPO?  c9{LGT @1 |?y1L ;/^Mv ]e*A8Z\;S\j}ơ cJ±O̶Go ?Zs0s9N<b~;(~R"f~2.?OogO?}d1 '/I?g_jAS?ui3.2fESFAfco_~Ӧt @6m۶'N|u/,\0y=㿭V ?S7[B|O<i]'GL0=GS3qy|G'^+Ok.EcJ1OL{6WݏLGwzI7agsn!ަufQL{ےJRŸTPsɷ?d L mNX׾:Zz$G]H糲%ԦHݿ4l7GaxZǽy3|-.\`=MGlp )6ok.yʾ3݂?faSqQŤ 4׺La߾}U .>S+9Ϥkf>xw7uڲeK51?n/^\OMs.%"jQ?WDē;1Ǧ𣵱O߻ʂj+̙Smߧzl(?_ 'O ?ϟo ݵkEǭٗ^zi@m ~ݓ:4ڸq6RGӦ8ʮG.7pC#RmPPl_B ?ODrŎ?Cپ'mݻw3Tlڇ7f ϛ@א.-cG+ tL؜1Oӵ2F3wэ7X SͧN:U]3unذqqӽHQP|;͛W}v 4窬k׎f͚U8=fxRh^N9uΧ1L2}RxB$栚 ڞG:9w\cIB?y8~[j~ }ǟ>%'$ |T 7|6Ƕеqt}ަ_zJ?.DrJ 3O:w. bq[Wme2|n?}F ^CN p~e!?G}(Q}O'nyoCI? GMsZz~kFq't[ \}|Ꮟ.[T?ao3 Gmh,)GW͸m^7ϸ׵d٨FW@>i3i͚5G)Bͪ]q>MAǭV=4?Ms(]~7Zw]v >v+Nנ&7hިi<43G.{VK 9?Bu)5뇒o~b:' i=C7ǟ1ٳj|Ex=۷BJ,tTs׺aÆzM|+=qfʕVsGMs? }.[]40nn^o3f̨6۞_ǚ>7n6u=uun?qBsիW{?~Ro{9p@+ 5?< OZh-Xj%) bt)ք?]d1p??62V)>kOMȩ\m*]ۢ/6tB"m<ڼ1{s=WcT8>^lcy9&i ?dSOwޠ Ǒ zNg_Cf 6j|B\M.q>MIK߭뚨4nMPlsh11SO=g:ԅfQ+x߿8/]_40־KVUm7P̙3^2E͛Wo5Aq m? tnݺ3cS~kk-{=oR$u}JO:Y?bץ߼Oi~|O_ ?e?k٭ڸv7W3#G.Ŝ+kWbRՆs5E|=uC7͙3qׯ m2T6FFOqc?t?{mzܫ46 [ϥRiMǑJ؝:g86}8>{ݜwVIܘgӒf.CM7xj3[d[tʼ>jf^̕svxw6=<@u&.ʺd8o]CMo*^>6gs뵟:u̚w"$L۽{Qqӹ'lJ4u.}zWh<08tLiN%H]ms~,`pBig mǘCuE~(c3tc7 ǟ>}bP}?)Ÿ.@u}@Wڮk>3t}A*-Wg=Sgz5~k^gBG"XyM =[z>t=oͩڨc^ Lݫu:XC}& }&(CseX\5 P**T8x`5'gz6|2Gyk6}߁^bPǫ|ќTШ{>o>{^sӜ~Ώu?ҏ$>j-l2?ɓ'Rz-O_s5W_?ڻwouOFPot)bgo+Cm1 p?It u| }7)c 6Rbp܅\1wb75=w51GwPZRDqY Bgd?u/"~?P ! `} !"Or] {7IP@OtХP߉0?~* * !J~ʃ~𧜾?ut)ք?>~O~?O\c@Oz}!* rp?[C7~bm{qUTIb*?߆ "}~~({֏/ߟ8ǟ?&'|3dl'~#o@@A>?{#'.SR7/P@1ϐ}O ~|O~?ʿ@PAA>SP~?b*&Kq?6dJʼn?}()|r"}~r$?}Sv@ D`?q?~GO~l'~?S?.?n~7P- (= ~)/g?] rӆ?C|A>?d? ?ON}(Y" (m* |rE~ ~(O @o'Tɷ&i)aoe@  ")}|C~%sJJ?}?i ?d@e@ 0 ~ʽ3t/~2ڗ[ӷwI[c?CO Ptu(@%`>!L9< BTRJC?@ ! ?d?{#rOߟ*bIߏ/g(!'o@>O?{~Rg~?O|K:3\@)BC>?mq+t)ք?>gHi;?? J3@A<C\৤>?d?|mߟ't֏O?>onD6쟒ʿ@d@@(D=?O~lZ=?O?M@-7|ٖ|kßK?CIrC?@@(D<?{~OY?+R?ODSbJC7D@ pxЇl҂/\?1O,?Orq@d@Y@!;?[?ş?OD?>LR'׾?d@d@@HOC?d?|+S& ]-i-= R[J(F@ DG|?P 1?'oSP-od?@@A^@~\Y?|KĎ??'m@d@@AO?9!뇬?O@m{ߟO>\i=< _CJ@@A?]r?O~?O_od?) {@2~J\o^~ L#3 ?~|'/r[  "}a'IY?d??OCJ@A>>CY?!KJß48 ?% ] @ D`S?O !'~?c.!q ???1"fX))!'<@ADIѣGG/h߾}_|qtȑj>d?%O'< L(Ÿ>%7Mo?@AD\kW\171cF5s|@}J?l?yO%?~ɯOJ;?PA}ĉ׏V\9q @8OqhkͶuիyŊk׎> > ?dSbɷ?OS7 LNG_C7 ʿ@ ŊA\r%1B ڸ{キ~j=O.?%!'mi z ?yO(@dA9СCjA`׮]Cd={vg?_CwO=>(F%z-)ij:i9'nQ,V2PJ@ (U 1tZE/ro߾ѶmFv[Urly.\X۴iG=z7ھGO?ttP>Zshƒo~ ]tEtOz}@rodğSbI .t?? Ξ=[_}6oܘ ,ٳ-S/NG1w굪Ǒ7rn)Xgs!~(F% 1;'C7(@ D؁К5kj!@%º@_|]BA>7|R='Esoß3gԾe˖U I}'4P~R L!rWߟ't?~#( "  ;`ƌ}ܶݻxgk۷鵣:Tqy<۫{=~z@I~)7I?? ǟt?}! 矯7ZgMe\ 7v=XٱcG;?dP-3tɷdDiw?6T P'~.X|y-9rĸo ?<}E?~x=j-Wz.9R> ?dP  ?&'(FX @ &47j 9M[\s5c{ы/XٴiS3s]y啝G%[?>뮻az^Ys̩uq-4o袋j_x'?Y? L54 ڬ"i?}OJ,@#hA ѣsW "Cǎ0Hp\e踞8YlYcgϞ]}]ADhz9O ' ~~C2Ÿt(J@Y@ ?vF0y].\؊>^ziU5j 1|G9th#tm>طo_cI ?y/CɷǶ?C?Pr @ h5׻kWs"e*i._o^xfƍלkW:Bz͛u*ŋWΝ;GgΜs~Yg)}C~3АӧO[u>$?C-uy?dĄ?@@P>9sӦmϡsWV./F_{9kg${{9~ђ%K͛7o;>Ms9}~~ ,zI |f?(G@ ?M=~VȄzO}g۶mUM'J5&2V\i>SC]EE>?dCɷ\?O6R]g(?~ R@ k4"[ts?g?Ď?C jف?)^lCA][n;C=f⏮;mX̙Sk|./} +{@bЧl>?d ?ɿ? bc?&'|f?i~#@1H_\SO> *+ń?oGjnĉ]#0ꓕiӦ}ݣTA_m_o;>?df~ ?}㏏o-uEC(AÆMoVshm=r ^lB=O+sVUOZ\ve?i~#fϞ]}&= l {)~(V^%,?Ce?Otc @}(FO)?;@BP9VfSZCs֦PJc51wώ;yDXs̨W_}MѼyJΙHM}rD'MY?|R>[rӧ?~K?@%#PLԶ…4=ǽ[؏>3ȬX"̝;w}NJixͿuoe=?{#뇒o)OV~!?@Ccg}ָٯƠmq]Xeue˖-6F W}uֵ>^׻3f>%\2:vXci=м=?y+~?1O,Y??@c@|O?dC7RF (<9swaHslذ$I ơ՜y}]Q鼶״t+ҹ/Bs,&}!gJ1 c@CO?M@T7RƠG6nkw7=ǒ%Kj9sw6o\ƍG+W]ve{ѢEսٿs̚5|O>kSЖ-[uFs@4? C7?O[S ?.@(F@P~=^m g@=CpJ^얅 {~6W^yenIޯ-vm|`Bep >OC%b`|g?C7/^0@ PǏ7nkٷTB.@8!g{jbeZ:n-XS>Ke'5~Jřl7?jХ?&M'?~#BY@ ?}ᇍlߌ4 zM1s|{2@FcCskZٳykN<z~OI'뇒o& q?6PC0@ B?7?3#رcG\r%ս>%Q\~ϣoէdnk֬ ̰~'Oz$ЧlgX!뇬?) ŀ?m?Cʿ@  6m={#N8ъ۷oOz)4Ùio Oh Λ7y4W؇~1/6'}sW7?iY?d'<}C r 6{9ڹsg+,\Z7?m;*e7q/{O?~IMW_mk]yS>C~ɽI&_O>c@>gFO߉ , D߶mh.3sLcʌѽ'Vpϱjժ8%lQG<VXQ>>쳵1cFu )|@}ʽS^~1Ɵ#'M1 !V!'=[#A͛k7/_n\w6Ÿqzjtj볾[{i$_$av5={v'kB}O ^,X0z嗫{c9AZ]pۺukcJ/?{#뇒o| ş/#@{Wϥkڵ4).]uO<6Q{~?1w#ey~(Fp?OӧӧO?dC7, me4y>ݏ,Y_ƟӧOOoRq'`vW]s {@~om/pcLg%?K?}(F?@%AMhpy- o\wuG8rڿ{->V[?vUJh]> }'9[>r??1}@A:TJn'|[?*}V)gMWVPQ놾7ި}E]Tu}~RJC7S (VN7(oP\DPTA<@ r0s=5o~w[oӔ1o $֭[Ϟ={o>uQ')}!뇬|PӖv'* @ (D:ut?74}grp$-cΞ=[]Ce5e4MlGW]uUmVehm_s5ϿiӦ^MǎY}ݻڲ^{5!~~}V'i 1>oD8BYfnÇG=wڵkWe75w׿ cN8o>>@GJs)?dSF.XI?'_ c)|.6@!!r' ~ʽC7J?O׾?NOlXBOΥʿ@>?{#뇒o?C7 * !@Ї2o )C7(- \:+AC>>C%–|ˡOjc_ ?o?P 5@ }roY?dv(yiň?d?)gT @ }?g~?C<AA>?dkɷ~?9?6 ?~ ?@@A`>0Cc ?EO}(F ~ʽC7'/1ݟ)L?C7 @ qgY?fP-~?>eO3t7ƟJ?@`A=?~; ?'[!@@D` ?O:Y?%'%Q?C鷢" (% ЇlroOY?|??d@d@`A`Oy+?~ې~J|PJCrʿ@Py2A`A=?y%xI.go  {~SF~+o?K  z@Ч2oހJ?)왃?19`?%~#!@ @ !)~!'Ŭ~O<JC?@ }@}SV%ߺ ?'{'PT˿@@O?Y?|ې%g(O?~[?4@!!@'v~gP?dT.A>OJ?{Rɭ[~Ÿ/@f@a(4@  Q>Ox~(?Ob/JD8"!p|@}bă?J?|?<'oo ,(W"`|@~(FO*%BO~r3???@":7?Y?{#뇬\ ?~ @ "@|@a~r?Ce?y.@I=<@ 0 ~!뇒o!?6՞'1Jb@d@ \>O?O:Y?Y?!K?Oo$?d@AA>>O:C?'bQb('@T6@@A=C(?$?[jC 'V~_Cɷ|??}(k@d@`A`CC%߆?-c-?߆ @ "} ?dPo(q?d{Q?P8 |'>~~/?(Q,  "}">?dCO?? {@!!뇒oy?>~lt?5콃?Oۛ1~\P)?@@ D`Oy;C7~(R?u?ɗ~?~#'0z|Їl'$?d?~'o3x7(C7, |@~~(?};C7@  D@>Cd?.  ,r}rE)Y?oi3P@}g!!'lOi#"@!|@'4?_%?*a@6'qjC?&'(.@ 4!|@~~(???O?A8?O.C%r?II!(F@d@@HD:+?CO,:'oǶR? ?! @ 8qd~E~(FO.%b?bL?d?>2g@ } ~ʽQ (C@!P)>CO%?1aoC7JB R}rCY?9?C$Cr˿@AA=C/CO%l'od?dJwF7"  |Їl2ro{#맬o ?ڮǴ}Klc@Ib?OW (F @ "}) ~b*r%+?SO)P~?Oc@CO?D8|@~~۞?Ol}ʿ@  "~bE~~ßX~lǦ%CC@ 0 ~R@']C7J?|?~? z@~bŸwT\=)&Cm\l.,! FuPYI}poR:YpQ/|B0W\#/4h!2b!064LsBTc>:8 a>X6ZǺoƋ !}K{H]uAԚOx y\ck2W_}?b7LTϵK|6I0.+ ftW|}%]]']=!b!@ b "{+s0OIar:|=U=.{~!MkS6vnob\!Ɵ., @ "}) ~{@O|?ϴa|KxFJ|QX!$Pm槼IfCa#1LuO Pi= |4j:DžAanB2|=H߀54Z OI\CkhNL}v|oڨ<oM aRc!{?ly17G MŏcpW"kw ˘%ʥTjbS~1f1$ >a'{?tCr(W1ZЖր Py'7~'q?Cg?Beӕ?PVlh'd!g( @ "~!'? Yn1.1| ? ?~#X pw~CO)%B^R)Ꮟ>LP !'o.'ۥ J@@@D;" ?{#뇒oT<CߟP@ ,sr'%!~~ɭ[~l[n?.&QeP @ "$>g}>?d݇}.햵IrrKIc;8nq|]1f TISLW$ф$H3~V3˯|˳zWVlD w7,+@S []t1ͽggP ?'rO)o? @S ?޴~~~lLih@"PW/A"|?'Oi?g?pwcH A $|?d}M'?t?Pdhh1+RE!}?Z?~r|sşd iiA $"?{?1ɷtZ?gX募nߴ̿  A"@Cc-ɷg(@@ ~Gc[?{?g|?" - D~O9oOדod?PSSo?m7 P>9?hhIl[dA ${>' ?Z?Z?&?Oo8@H @=~~h06 > >;?ݟV @Si?֏7~i c[Jc  @=@;?ϰ3 xψ3 c APH 9}i~̽[?Oň?M~RŸA߷?#O]o'^o??iA|A!<@)3.h|kkmf +)@H  0$G~?3v@f @`H>w~~o'Ws t?P3 5?~@ AH>'m??Z?Z?R)r  iA# H SĈ>so[ O3,ň?@0H>č>hhOqݟA HD?>ǝO)~ŸA_?9h@A"|)}OsouO]|O,D@H A $"S.tGGt?]O- @Gh dA $"@~̽?~7O۰_L? B $"@m~~~J|Ro?_ b~+̿?A D?>M?Z?Z?Z? RG D`>?%̽i??E0@A $"@~~~Ji:ƽ?Ԙ[,m - D~7~L ?ìj ??ğԦߴA0H@~OLo?7o?q̿H <A@H~;?Z?Nq ć?z?d?mOu?@S2~򃟜Z?֏{?  @Z ${>q1c[}O3(Ĉ?ß:(6q - @)|?[?&L2w1c-j[@=S6{?Mi 9ƟA iqG'O]TJ @<>9?޴~m<ýß&?)ᏻ?ߴ̿P+ ($@G~rğ' cM7D A`HGrh|+umX?OLo|1@ 8$p@~r'vu-8s{?w7AH>'R?Z?޴~L|+}F)iAF44#uKk?%Ϳ  Xs>Omc 1VƟ?ƒ,MMP}1J@A=?eϽ~?C04ȿ7w X^]3%kd[{?VL?@Z@ysZD?OoO~zo澾ޔ}yzsr`{xvю@0HD>Ч[? ?G[~AnLO 'KUW-M.7?!*@(?*@<'f?y?3~H H  >[Lx'7r @ċ>{?Z?ݶ~Ɵa'W b\? @0HD`>?Z?=~@ }}֏)e{?5 oF   = @m~RO<3̩ @Mn)o hh - - `>'6?ܛg}5oO׭4_ - >S64cͽGG@qA0H@}kM'?$??]@H  $z?1?Z?Z?&O326G@Z@ #%) ~̽?K[O2@H  ($z?{ɫ?~kP. By>ড়>ܛi~?'c 4A H}?ʀo&R)| h'7@!<@~7şA="+?yOiApH >'}?ܛOo'E|" Dvm~֏Oʭa'{???? @ P$@@~?eL? S:@i@ H}E~Oɭo{2t5@"}>4 ?Z?Z?ts)f5 @0HD>>D?9~ɷgOS??P@mk A HD?'V1;| ~ş'OM#L@hqgl ={?N&?9?A DS.cMNi[~?P@H LA0H@?yOGO~bAu??@H  =' ɷcM|O  `~̽i|s&GG@ ='??ܛ֏֏֏ɷ4{??ЅS}M ri @% A"@~֏֏ h ? ) ̿2@@H~@cMG[~ l ;D@}}?Z?&ߺn??O~|]@1q,T/<=-}c<3厹{g9-ß@A >cI1f7S0 @Ͻ7eǩgsw嵡Ͽ|y_oQ,%hl@  !=R8@~֏?%ϠN2(\?'SYnb$(ٰPUK}/ǟ_82,XQɣcM@A!|O:~soZ?ǽT4?ì?ϥT'|%̙#cɣ/q=2@ A!<}}rG?D?_0cjyK?Ϝ׶ )|O~Oq3 ?g4r|?ClՍ?Pk^ l;_תYpzfwXbͬSze՞Ҋ7j~8@#h~~~6OJݟo>v$k> zWpsyYΆY9.GG'W@Z@)AGc'?~J".ϟև+eYi65W)ߛt6  Hu>>޴~L sFܒus'j@##@|'Gi7o)?'{_ힵόZ @ v/>>h?Oέ~??]>9q/8r2|%x"y\q[w^X+|-G߿kmku??yOo DDO~O>S[~OB3 ?zi[7._5_5;wx3篆??J+> |}̼?d~~L ?Ϩwğ\"9v/>s6|5>/֌?; _iUK.*@ >cM'7OK mP/qz@c  T&A $"@~RsoGg{?#OڽagP?g9{l~[?=×9r]Q*̿ TA D}bC~~~~oݟ6玹ӷg>9qYۅVwݹ&u+^+\~܇a'~lA D`>?{?$?kz7:}/9s6\{@?L0k{}P)c @Z@Z@)|R@~Oc-ɷ4?]?OWm;f~pW.>?.?]Y3g6_uϏ_?׉L=~Ӫ?Mm,g6QϨr8ߴ8ڢ@ꄵ&VM[еjbNS/cDFJJE$7~SKߏboŜ~&&MD Q'Nu5VB2Y㼏6 $KFQ3(\j /'~ݒ%KjMdT_0>ܲ=ymk]oxQ-ޝkc_ziE3W.+7>^xUV͘Kc&GD4m:âl^MX2$,s(?8Qn32 PiI+)|3xoV_äڮ׷þvbzߣq~/jf{\Xb,_,[l 0jɷg7ysǟoݺ*L:1-{r܍ٳ}56gq~41ݟL bR͒.ntyȷJLGpJG/c꣥"1ׁђTsO?VWR+}.pS?ܷm!r@46D?P3ɬ2߸yEwٻ~7moYՃ~pѯ`;o_~on[v9o~|C3.;%@ D@>'}?l}.g\颵v+2Vidki1 4q 9o usl{4|I*P@>̛I ~L7Za(Eօ?|}ޅ&~Kw5nϿ߷n_[y}V?O8- @>?GGhս49V'c˩gu/ׅK>[i?ܛ֏7ǽagPg?-O?4oiA $>w~Oo&i .'@i $~}bC~~~?50Oc @m#PW/!A}qGGq-u ?鷮iH (^@0HD>w~֏O~RğaƁ3$4u9?O<| $">>$1 hZ?OΓo@H A $"@~Z?&LgL o'鷺G^)i |Amn?޴~~ß'OM3 5??i.ğoW̿r@9 A"|?'ι7rO@ =$?T) ~̽[?9ϰ_O]?7@@ ~>ǝsoy?@|1↟qgQ'7~S0 @']@q* A"z7m~֏֏7~ǟa)IOI A@H~i~~o?c@*  |?%O w~~~L"ŸX̿ T2A >O<~Z?&ߴ~xZG@Z@(wA@H~Oc-_)cm???1@(]@>9O 3o'}1c ?0HE?A >'^?G.Icͽ{?'?' jbh@@!0$GrGi~̽ih|?'aI}M APH~;?νi ?&)a5-7.4@M2@#Cx>soZ?Z??9?i25?A |?h'?@ߴ  Hu@m3oǝSv~?M ??@m#M*$>>>h?Z?L ?M~b @O[S* @ HD`>>'E1f ?Oğ?] @\ǝso'nq18 @MO o'6G@A"|?֏֏{?9O~̿   }q'Ɓq'O~ş$?LퟺG @|?ܛ֏֏ɷZ?𧐻?Oc @A $"@c fG֏7 :???(* D D`>cMGt[\@Z@ ='5)g-?G맼O~OFgT)o @@ }}?yͽi?Z?ݴ~RƟac ~)PJ  |?1446|̿Ŏ?A >>'oi~Jn|?[6^2 $z$>_{? ?]O *@=@Rg7soZ?&???@A B">GGti ?Ps!4Ÿg:ƟX?2@B"@S?G{?'Ait?i@!}OI~Oc t?Ϩc{?HM2hE >yI ~c tIrhԅ??? @#g??ޚIs5 @A }}OSS(cd?@M7Ti @"@$`@y{?Z?&?D1@ ,9|O:~7gI?T@fA<~~7~ŸadzRß  }Cmnc-O3 ? .?]?J A""@~G>6,6-46ʿ4'O2 A"{?m?Z?Z?q~r|sds?? @ }?hh>i} H3k2W&A $"@h4?&I?9hhĎ?iA D@}E>{k~L|gX?ql;h >A `}?޴~~J|+X?uP) @ =>'i|j 1x?;G@ ='E}~L?ϪUş4 ɭc  - }}?G[)~ş&?0t?  B"@~;?>~[?mN?? @m7A D~?Z?Z?Z?yOugP)@"B"m~̽?9~Ia i&h@@!|?GG732h ?9̿ @PH}1~~~?̿ A >Ć>{K~Ro?~L{  6N ??(?(=@ |?GG[io'Bqrl  A`HTGt>h}ͽDgt?iFc<@ @$`G?Y?O cͽ$O?]@  >Č>hhh&O SngT@(2rA##@m~1V4q@Z@ >ht>{?{?Ob 7 [?]Ϩc<9?ᏻ???(&@A $"|rm3o\)clnbi *@ }?Ï1f-7iO2̿=@| })~~6|3? >şc@A D@~r{?EIm @A"{>'?Z?Z?ݷ~[??OßE8̿? @ >Shh ?Ԃ?PlSo $"@3o{c[3xN1  |?ǝ\޴~҅O#c ??\  B"~Ocl1?OoqP/P~ B"@}?Z?&JiS;͆?(o?ePwA@HH}?̽iT ?Þى bOc-7@}?GGGǽZ?'7@A!|nǽ'9)@A $`H ~OO4h:c   ($@#4~~8cͽ$3@(M2@#\}}?Z?Z?'md @3OPyT 8$pG~ɭ ?&|aPk?A A>'mi~~ڇZ?]Ni'7͆?̿ A =GGg)~̽i|??Gbn @Z@(W*`Iy>%?ܛOZiPw4 o @>>cmTc D?'X7 A"{?[?O*~֔?O4hP1@>G'??yOIsoZ?Z?&|??A D~~Rm|?3ϬXoV\J/t[ s^~p[3W.5{bi2}n]}c Ԩ_ Ψ_/8ҌM_&{iӥFǦ!-9ubx w&_|f;sHl?toZǜ6^57jh4}oM*ƙƹ;3*FH(K[OggIO?|߼ asxՏw\o̚Mi9?nxjǽĖ-[^yꫯ "zY`Ay7˛o|oJmeѢEfQeɒ%rDDD'Jٹ(Urx]kO'1T{g?F^~N?aK_)m7'ߊŸ(U@JR^$ @%#Plo&$""}r&?O*%Ouq?^->߻suhG e{7>}J tP.,_LWU+mUn(:2j]:Z="Tc8s""Wr~o*׋MM~>'TnSءN<~'-l5 Yn߱'y}[=kޛ%tSnzlCxfށ ڼMor#;m1*]XnWIr8b>XI#^ D?UԮ~FouH[Rݯolv*R]ߊTwڼ%Z.֗?@A $z?)qgh َ- F #@H  $"@mmo'f?Z?(3 i6h?O4@u}@M@P>~~~7ca@@H  ($z$>>vGGθOWA >h7S, ?Np$r  APH}Oic}ɹ3,?'P:@4A`H }~̽iS[+3,"bPT 0$GO}1f ?&OSݟn;|? @ 8$pG}}~)Oo @Z@ {?ǝS~OSh"M@ E@G?৔7~R{?&L5?O6es%? A >@~̽it ?gV77J DD`>.ܛ֏Oo??A $"|R>ht>hh?7oO_@*@ }?ǝo)R~Pzqi DD`>iO 3oO 0c-ɷg6?OL@ ='_?rZ?&L%?Oc@H`} ~?Z?~qiiA D@~qܛn p?? @|?hhtIiM"ğX?<A D`>~~~L6:~P2c}I@Z@P>Ć>>?Z?Z?@ >YO~~̽iڄRd?1f s T. $z1~rc-{?'?MP) 3p(eAD@>>hꂟ,7h *@<@3o?1O,so'O)ʕ+ǟ%K %O?Pqb - >'?ǝgOݟ?'?@ - T2 0$K}?7o&? oͥx@  |O~ǟ&_{~şZ?& >60'gJ7>}9"R @!<|O~֏֏Oo?Lc-7TX~n|Kc6>7P T65@a0o^Y·g;³w6?"Du\I|bGm~ʾS*Pß(Fqu @9.ğ޻>|eikז[ A0 >hII~̽i~ğr 3pOa@ HR%>)OY}i~O|gTOݟu?@(>J A. >hΏOo&ߚnt?@COc |(IFEX =Č>:w1?#Pl`S6hy{3:'6P%d@1A"{~OcMG'ɠ3h$?&h@e P͆@)A"{?6Ƈ[Tc@O]'J   |?hh|+OSݟQhd)@yP18? >颏O|~~ڂ$?9@РT$@9B"{J~?qP} @Ͼ}ğ?@Obtd?)|O>c so'֏ɷzD?dŁ?<uhIrD@ >GG|? v?htń?A ]?M,f(=>'nI ~̽i|3<~?O_[>@~s>9y>S {1Om46~K@Ѕ@A@Hc ~~~L}')}K@? \]A =@~qGOΓo'3icI@@OI>']?;?޴~L;~41/ 4ShT) @#h~L|?J ? H=yk}Oc--)u֏ɷD[g̿m?i PS" 8$pG>>'&)Ώ֏֏O>y'@7?i PS* H$`GOn}O3oܛ֏s-ye ?? ?5? ":R*@m~c ?irV@O]TB?(?i P[S: ) =%h>rGG'{?Ow RiJ&@mA"|>oEOoϕK1D@~̼ hh'@@mP 4(@ HD`>G'W?Z?]֏73$3@Z@'O @ }O~?Z?Z?t;%u'O#ObJ Ŋ? >e?ܛq'kI} ?(o@n?i P `}}?Z?Z?&ʞ|?L5?s1PY44(%A0HD`>>y~̽ih;,|'???i P}?̽i16Pw \[;׆WV V??rwh/r0w,T/[v +u_#|5c={W^^_7,xkoxe6gcm@ $z|M+|RCm3o?Z?Z?&Oqo?J4TkSgB^W}eOAN /-C?׿Vι ${@qh<'O$? VǻΈ#uwQU#i~l޹y)?~rƟG4i Pc  6|g{PqYqn9=sN~kxwY7>|8[A@H}}ʛy?Ï7oOIohR5?tê}Au]s9{6:zrVzCM˝Gt?Y@}?xG'}IO1TPT7T_yz=?Wm =~t1>?=s;6APH~JB~̽ih)hRjo @"PsۜHnnTߞc^^{Z-g_aЉ($APH}?o~ZJP{T7ȟ?ԛc[Vg HWk|cefĂ?!C"@c w~b{˥3.?T$ P>3pMY~"<?_{L>i[ 'N>wc_@!<|O~OSIp ?/,&''{? M@N ,@P3]b ~k+ԺaiA1O=xR[?uOoO~ş?Ot߳\O S^hh|:~F΋w[c_G־9m^?kgۯh J#|\/`/[ 8fzl׌sh[{g3 >>'i~~̽ih?1ϛo dpGpl߅C$C7~,32 ?r[aǁ'o_2c~]I_yuS: `$G= u>i?'7ퟱgo |ꚱ.(G>砏N<߹㭾K?[&=֋>.A0H%@~̽?Mu?.OjS*(Gk°y.\;u<|YA"{ }̼~~ʁoO'2T''jz4?U^[78 Ségf3gΆ+{+ׯ{+'>rD@})~̽ ?Z?gYb?@ujrş~9p#{o]nxdCV'?>'5?$1S+ge+ɷgO*o7D?iPP[TO9V>nDZ{rD@7xܛ7':HGA,t~Nc^\++ $"@~1CPPT*]}Z9煚_x+k@0HD`>Ч7Ώ7Z?&ߺ|+R~lhIi! @Ϗ;ou_kV{kԴH?}{m7nY] $">GsoZ?Z?Z?O\?)OI @)#P sۜ}Mm{pٰjdw~ϟKG=L~5{›k'f=G~ꯥ=vB"{1~O"o&'' ?]^]',}gED_Yiꢏ.G M\q詞S5xxaKhGؾ،~3r߽sm-x@ $z@.;?Z?Z?~L?O?I.ğ@ P{۷_|gA+wG ?ҟ{[ T&joeO ~R񧺵3{w] &O?}Yĩ3C?pe7APH>>'ߊŸ߆ >j Ɵ\23uD/Wv~cЉ>[n<>sU~jS5=OogΜG?M126??@PT*{{ֺˀ_vpzqKx-G7~cE_:sY jOI=G?''1㽟grfb()=Oi>hRǝsoZ?&RjD???lP-  Cy@~RAǝ[?#5 Pş!;|O~?iOoONoϕ?(>*}?%#Cw >Gc-Ojo~(7O -a@=h;h1Oia' &?O`>S{S[l#@' O@G9 |I}OsoZ?Z?Z?O[Fb@   }Oi~DcMGd?MU… GʢEMbG[m ߿sM--Y$6~7[p??di=]/}bΉ37a2dfFMgT/v澾-=벜!7=!_uՋκ^|̖:_0Hu7N]o :ޤ3m]mCYiM&ސnb7}s{,_|,[l,],YdT.0jfz||a ɷ,g?_7yrܶ"?D6l,m{?W/ek~~£>M{l㏷'x"T?]nx-; o_ʨo?\P[;akްxܹs[ͼyDDDM7̜9sdliTNEcz?Ly$ךw巿m4y衇ʃ>i{0PE~yWk6]O'*凝T@9Ěk8&"pC`I_ h'&mJx3V\c[?m}+S2Z\5lؼ;]0񫯆?dJ?qe8ެo}ّs5Yo5u_|Tߓ`sxՏw\o$"r~qoSH񵶜KɤɟYsHW%~T%מMN3ԞG~|8LLLDwl㸼_KgOl͎;ۣKS??._CzJ[xm[磾o0H6mT{6nX{bqۍXnu}w`mJyxf.@PnB"{ }?.~'b)Oo]  @ |?~~O׭'ǟl#PP[B"zI}}G\I)~̽o[S8]?5J8 y.LW J-_s~|2غ.ڿ=9~S{6ѥ}|vjjA0HD@>> ?9L8-6Ɵ:`8{d6 ?u< |W8}h" 8{d80q4(? >G']?ho1~Z6?mO]ퟤ`j&f8IFQß$}?GGG맬ɼǟq+]>'=szV ij~A>tC/ޞ@f @=@Чę7~iRlPg3qn j3Љmd ?޽{{o̔B"~16|~Ɵ7@]5T3ni+|Ά'赋η?ǙG*ş  @}?)c h@IO4+ @285dƟ&SC<[ܴ'? @') @<>>?h>vd?Jşn90j=|go:o<9}hԎ?M@3! JD >Gܛ֏O I?PZ8aֿS{%t{ٷ̾ @Z@AވCw@~?m~Zo'uNk x[*ڿ?euP- ?@  }?ǝc 74\ \g;7L;6ؿ5? @)`(#4>fO{4?Z?iϽih?%JǟTh(d{ ‰z9pı0تkP  FG>>?Z?Z?O)\ ϟ޿> _ֿXhbb?IZ}?[?4?ƟhhL>n`9v(|_nPT \ =hy?'gɿc-ɷd',JSRQ| A0HD>>?I"i4 ־o^movO @gN* `}?917\ _&n}3G>hA0HD`>&ޠ?޴~JoO:s>|==ɴ4qߴ?D~OO:hǟp=n'PA"z>so޴~o026hlϑE&VgPB"{}}1f hY|yZk';@oݻsdɣ_ 6c=M@qϾ @ | }I~ş[?ퟤ]8zpp|0?tSn?(mD~}?exc-R)amP2<_ñχqQN #'>\y]paa_;\%5?UO_ 9>OuC/.}<8ע|1~rm6>?C}/;U~cks9zR4PB"zI}?֏ONn?Ps.Sp,,&ww~M.CF\gNG?6ԍ?@ $"@}O{ԉ?'ɷ.'ɷX?Py{c7o+G8zj=ubqzjg8յOj OB(oAPH}RD~) RS[Ս?u8s3iG7%Sn/•rL^ ]n{o)@ ?Ϟ={z A!|)w soZ?Z?iMՊ?F S&\xaoGM[wgW+|t6?9PT* @'MM$PH}}I~~SV맫ɷ([ ]?Ԉ?Y'8k >@  }?7oONo@t?%#Cx~̽[?)NՎ?F"'jPRP=@~?&ߺhD?O4q@&Ō@'o@  |I}Ow~?L%?+iMP]`=>?S[#c iPSg.D S@ $?_>y?'gΏO[׭'%_~T\ P4C+ʿDD|݇>Gۧ>ܛ7j?Pjb{@}F~1~rh02U wb ? =h@~̽iO ?(9@`{{ׁ`}}?Z?Z?Z?2MPPt)|ꥰ@ $"@~?~J|kJ~ ş+@}rBm~OsoZ?Z?]~ğ?' P#Pl4@ `}}}O"4?Z?OJ4 @Ίǟ+P:TB h@ =hhĎ>G{a1m<XhP@!=@~164?ON5? \ 4, }7~RO(5?u @? Iٽ{wkc>h?Z?e~O b@j .h@!'זÇ&wd@%?'ZR?X}?Ͽ2@A!I?SSSa`n< }?}?];?GGD?i( Յ?yy&sAPHd:~P%Tl?HJh|'q';?Z?Z?㬘̧ߪoo?(@m#PW/tRoՍ?Y9x`o槤7`7?  @~֏֏OG?i5@Mjο1Si  ->~Oܭ:'?u@Qjj<\$`H>@~J޴~l|KJ P@mjJF }?9)Ώ7U?P6? "?~r@~֏Ozg??p6j P@(`I }}}OSڝ[?YOF'z@@MPW|>y?GGG'ɷI8 u?}@ HE.:aO? 4??)~r|+R PZP@n ||RBm3?޴~~*;v(6  Pw`)|]|?ǝu4?i:SqR`h~Xb $?nk@cM>?޴~O'f?5?@UK(A0Ht`Ϟ={|>GGS(ɱ?(*@ ?@~7Ig\R&ߖ-[6>,Zh俀Rg6?ϰ4 ;iu?PI; $G>'?o'7F'y 3͊? $G> )1f^~~ʙ|Krk4h6Ih`@!?>3Ԇ?'?Ͽ ?(@(6  APH}@m~soOk4?uѕO(1ti:mŪ1>h>mOw~RsoZ?)OoIOWoMOAu/Nn7;v,8p7P"dmTobZ@cO ?  ~OcM'fi~j̿uc#(:&{/*lHuN APH}?hhD?]~ǟ?L2l{?MՆ? 0$G>Ώ֏Op96,W^ sΕ3o޼t'jǟD(5v{C7@RO>Gw~̽?Z?NՎ?F Fg@?9 \R`A`H>Ч,?1<?i)I(@ @$}}OZ2?iШjRm? P4( }?,iI3q>j  P@0:}?̽ih?'ϸjR?  @:~=?@}?Ĺ7ouOcu?uOT \yjRAu? {~?޴~)Ҙ b'1 DOkI }?74 OLt)E?SA0HiW 'OGN{)|?V\|ۚycw/}x>y\sϽ֭k:_˵|?%bh?.}0(@]NUPOT\,Y39@ $Nͫ[ Okzw]Ǎn F}秺;>~tmqxoE'’͚I߻kcXxe՞{W|JGۧ?G'OnoQOI5?m(ßB(vA@HTmgz&tê>k>}۷yu՞Ͻeב>9?'g13|~Z ]?|V@l.şhXJ   WG_:ԍ>7 'N 1=~~r{?S&|'jǟB(EAPHğU&yzsߏmF7}ރGN/]~7 5?"@BRTz.}>s6|g~O~N_Ydc?[=yzs{_>>'Wq~1Sr51r6?me? P? `gːdЉo{`c9+-Ɵ{/^\+~Ћ[n8j]O}?ܛ֏Oq$'U?rl'3@d;:2}!&R{,y{ߴD37~)*h@'oǟLAPHƟyʾeבٳgj [}=qީ ?v;k7}})~ŸGGd?mO& b'q e¡C޽{Ν;J^ I; {`ۡW*bhS=@S@k~rt&ߒ? 6' @u!Pq(܇7O?>8{OTO|5#ϙ>͟O??;M/ׇq=@}?G맼ɷG @]?P|WظeWG־tqITyl!3SqٷZ5fߚ}O_>rso51I4]}cSD@De_MQpCdfff]MO9TU53L3}|kYr]G/}L[?Vrm_8U)*>>?́~_)s ^CP'#=:L7m,f\ˣO߇ WBαNw +jŒ{OzUi?r?5YV0Sd}@~~gV\Y5VիWW~a޽]w'o$)̢gi7oisɟe/ONL/5k֘~0;tI.]|Jy2L$Yٽ=79Ii3fH]4~Dkf|Iyꩧ2UDm`7XCRyXpl \k=HJݜL׃kUw}YB2E9k}h.km>kh)vY`em0gorţY_gx|wqČ543ϐ<ӤE5v3~fر"A$lč&_|Ec=<#:?pC&>`$9^OjYY$QS/ =|; &8 <%?i GLd…Ɵ`xpךHb_?cu7?uבC8W}ƹc^Gm{gOI6)yؒ,[̼qYΚYЊ%,|`nqԩRImy'2_2iU ƍWWƏ+&LF-̛kh.cw,́DRSf^̙3IYf͚[}f*@\JꫯVJ}ys~sDŽ{5'f+ݖf#cwXpGxΡG~%o=`ޛ]d O2歑 "Xϊ|mžw*k\?2Ӗ10}cn7='玼7vX?5sk~6mySVR*I}ipo0+g;{yɓ';/g.&M^z)ՙ8qר",Myb5ĝLE)IrZIz<7YٳDC׌g~ђxʳM߇o3 :T/SUSe˖-B]]]"I/z_-N{qspsG> hkŽ0[0'gݴ?+p:u^Ș0/5Ro:::LGI`d [;ǵEIE'gDϨر[Y+q:dIMORd҂?d\d`C?Ķ2>>χ;;(3~cnʾݨ|oSy^?1˷|kf>hf^?j8>si?هfܻh?O~'I[KUX?{{1 gϚ'O#G6-Ԝ4ʼnn*(Ν GOO9vXp!jժV3(yI7XE|iI;?@ ';C3gAu;;`֊c9c1ֺCuɋ8ۻ?S<}4}2?O') ?'PdZQyLOުl J /_J=PH'$GkنҥK>ʕ+pcjOZ? ?f~0hf}e=C_nk o"ϑ#Is >G~U?OBC%J@ß+V&Ο?}.&TSUTkRMFI#?@ ';9ursPfxw[nͶZic8E5rM׹y'=!P}~Z ?ǟ=s>OqЧU~O:?{ꇪF@wJ+@⏐ڦvJӠc}U[ iooZΕoW5C}?+':2ހ~IS7'%?SHy&#ԱUhKV6oM=lڴzLU,[,6,Zğ ?Sl}@ft_li:~?O'ި~Տo^U'I;@GmӟMp#ד#GX飪ɵ*ԫSd@>Ji6?gQ5voT''#1W˷[9Ŷ"0{\ Y>fŊ| VO?(DR4|@ЧL}ҍ?SvoTăZ?iI'tvvVp;hێ=-MU*`) j?ـ C?EЧjfn~hFU?Ÿ"W???Sm uNmt>::hZ>Վwx=#w{)R P?$S4d}i~ڼbU?ODq vIkճ 68ߧ Pd# >>Y$I?$ ?IOClW~-V/Yd?TiS4E!ΦiӦ\ʥ4Oޫ&C?E $?CU?O~ρC _rJ+~qU=ٳUGNP~iƟV?!?`?I ~LOj8???mmmVs̙HAնhaxxعɥcdžSB,O|@,>' ~?O2C7U?ğ"V?|ŋ!hۦIɂ ~w͚5ض]vNݪP'o?=ŸO'?T?T?9VM_3^m_@vȑu ojzhɶ霯_lYj'M?}>q>I'$?TDşπ@>Gl]'H7Q^_kMpb_=Z{k!*b@Rn::) >T';EހT?) O9}>VXa_'Tۦ (ۦklۉ'۳g\'?!OЇjOT['IaڽQVƟ?>l4btiRGìmbm@HTF*??U>lC7~O\#$?T?eEmEmϛ˗Wnݺ`օ.&lU46r׹˶G*Ҁ?YD !!Oqԡ~|-~~?t'ğUVYCqƺZ=|\m*};wκ3-]T]#HXznO49rYs`uW.ڵkW(l{{{E5ugbqƟ ??0P4>ßP?OB?P\=lgϞ5 ,8. S٪?Y@ !!Ov}>T ~j=~ ?>'oފ^ğ@7jfk W [E[ϝWI3?ItC|G?0c s >O3?[燪'/U?M@oQڂ:sxرmw}Mrmxx8U?E Qgi?^b :n1}'gLz|_S˞6}#c[#5 1g`1\MD !bO8?ğ ~ ?O6~ ?q(?z( . y̙I+WV=ֺu*oV^]­Z^ĉ^>^ [tin'm$D'SPt9wt9ֽް)\qOo;W)94`-0/=>ğ{[?Od~4{XU?M@PR4hjyF'Sk6lnI-4))*x-_ܬX񧳳&?j{:l-9lN0k^ld9cg\xGv/h9Bz}Z ?pk),?T?O'?f?j5Ϙߘ{#a^9/ß++Pc7=>Y,>h='Z?Nl~?{~-SzdULOUL]Jwu.1q6q'iS>>Z?!xf̞çMɳ9;`~g4/m|CorH^_{eǩڽQ?iO[U <OjEs׮-˗s[;3۪e8؋*ѯLNjMkjV>wV"O)>OeS7>lOw]t:kL6EtNn5??mdO?SI 5Z dt5.37S?vn dkd }wkSK%B&i Bq؏Ms#>nhNlU?C?iT~Ƶ{]vicR?!'E;>OQ'4`طvU_ wZ?s=K~hOa'?@ϼyOQ) Vq+j?g~ѽ!'7#|@'+4q`'g2wiMc>2_?ios߼wUz} ?{_7~[2f})?ßS+tV]#ȶ O7:=릘S 1]n4c.3~kΟ6_DO H')2 |@bOS2ԲmRգ{f[žgs+['IU?YQQOު@3?ß7C]G_3B `~Sm:nN hzLt6[u?NQ>iEz'1o=h>uw7??TP\i)P?PbO C|y$DjsO>c=Zuv=?1)=Ч>OoLºˋ:?O '*@8@4?\kovdgկBLȋ;׵~z7e;3-?I ꄌoT>34|?5ݶ:u~h|[lٴiS,}f?ß)WEŸ++z"86vg.okOGӧ͉'ѣGI~' ?O'i2OSm ۶)s>f-ߚW7g/~~hFU?RR ?P~'/ۄ;dwmO쏅$S6]T  _k7ȉ6wĮ0:?pkZBigϞ5===-Io@ { >>O|XGszmwxWu:k,\H~i?(_u? U:q{kPF^w}~O?9>s~h&?YbL\Rh̙3fpp !s9k?؄^[P^W)!NF+p8u3ggg,W$>W?ȡI_>:|$D,9t.P^t~9r1>'FL;T9{<,Eusֹ_R[Ad D@kjVLV-J۷A%RVDž6:·~{Z NPׁ:ϕ9^4v]rtҵq#9FY߀hS{1>S^^qt.A?{[+tL]+JO~DO/2?ßJL9[ek7g\,p\[˗;3q[Bbù#\1͇ntWךH#7~v&uUϦNStV븺{m为1 ?mZRvhsMumljMkbMܮϡ"&4i#>G׮gmIUf|צЊk|$?'b§֟C~m;umޓU=ܭD_skC}v ~;uvKa:O$?:gaѸ}~Fzm~[ @> ?P';1evy0$?5]o *Ds9[,X Vꞔ- ݼysy2QXiR g*EżpO0)ꫵvMT;Ǩ#k;c oJ#5Xџo>GV{$?'&b:H3lA“=+q6%qO_~d\!&wUD݄i}t?VQXeQުCOB??cN/֎[eGkڶ-Ji=qF',\0Ͱn WXk=}]mӍB8M93t3_cv1W3:j}!k9vw1v1ߧg;:.D-֙z,̢Сg?jG۷G{nvX{'o@ڧ\13}AekyTfզч+BGq'xY4{.~N^Dž}Z35QΪO֫|@' \z7ʹOvXmf㠽v{AeMߖ7Mikڍ e8֧[N~~2pdM~|㏮Ukqľn%KbM uq]-*ntyu#("gfx3pƜr9Ն诿zbsmܨcۻ|"U{vp4VkB䙮{:5y5iZ?y?!chNw >AKگu^:-*}t-Y?uj?'GqG$\J>K0bOU?EQR?P>' :_JGCWo[qO-yҺ海~)W &;^|='kGf@ %l|>ѤmE^huQTk2V{,%Mn"UJ !2ƃN/[׮ܡ Ñn{Q~e?^TibMPY5Ͽ?5gvT [7 N-zџ Dî*qG׊lOzp >uOqθ1 Vq>oO]o17o6\?s]iκGSOGg: Ԟ麮[%!iשc5G ȟuJm/HW?5~BI𓇪|dy7>@O@ğ#S< sg̙mݵy!j<{fu'Ė>]1yطm~P1Գvr/zbӊjFDuZ?ۍn-[6j˗;^rePj3${]Wur6lTP+'㏪%m2a:^};&]f'!~ \M#9դo|  <5Y|< tj;hMȖC~](Dhqr`7QSveSxK?8Gk]g=냧DmϚX:]%tLaOk~oC]Cs[߫(T?x:Y[lG?Ϧ5t=m E\I_҇?P~'Tjz7BI>ʵ[6ݹ~~kJp5'Aꀽ/`Ѷ?qtњ@c烶p@߱6Xc+#]k??mCד][ԧm?d]QШ}G>[/QGq?﫸ΡqZW<} p |ʣ]6T')r뷨4 &)2(F =S^g'54i+D#@ߖo_i?ÜrvGڷyϖͳL76ѹUg55ʫwO=hUZ@OQ7@Ɵ4SP)q~9l]x.G̎A"gx/l^ sU_t_&\[UqЄeZ޹&t ?E$g.+𷏻^ٟ~sy^{O9&:}Z * fϚΗ/O v՟@th6=<^'B]f>Ug}GXQy5ĸwUÝ7n 5{e *gmZl-0yttv9(8׺?ևzzO{nXM,~YKbOR?EJtsSp) -3r#Ԡ=ɷYsf셦{#_w~cߵ^kM'ULW==^'_c͚k뉸G7nMWרǵ&EM+W'ȓ4:Rnmپ? jo3?1=1gG2Is皆? ?O-h:@/Vi{[{y7 Zo#k#vN~=lW mCͩq&ԃSu,5u=:A{tu (cOjh?VR?Pj?I,]ԾȍUMV! R{:SJշus?Oڑ'Ix{SZ(N:& qVتJ=ΖOv\UPsV- њ>C9II# ~t_*z扙;>5E܋wG?Ui{x7v<|uU!voTli>PP%?E !ßUV9huuΝ;k~*w=ZU$$?\kP9Y>~z7͌ѝ}l_bBF+jU 5\t}^o}O)Ÿ;GN= |Px>o~F7Nϛ77>Q'tN1|>|~z6nTJ%S q4w}OilU?wF  ?, 4 &lk\;q p;4\ ΟOG:1=u! nIc u:}*}ͯsUG۾4x鳖o|e$qqz7rriqKV~ZS ~׵ Jhi'~g:^.XCHJC \mƟF?>GQ/lԿkPP J5@O nQm?7n͛~*\uSaS哞1'9?q gr!塮w=ɹCuN0r;SSӛg%2dFSۍ&|Oy&uߗIsO4xc.?6o?:#C0X 8siS ~ߣb[F}OS ~\{>\O*C7J5@OQ(<t3gkt M[haZpפ5kFmM! Tp_D*b_̗뼜;gT8uXBc>84`:^8 Vq_\QA?O1g>Xg$֡|tκ=qmVML5'_,q~u(Vql6D8R'%?) 'd \2mmmΪۦsX\(Yvmz浞Zl޻mHZ?s?v$|*xmAf7;5;&ɜ8똨|;׹W5qot>q5DJG']{[ǞƹϺ)^'^h.Mldž?:fO-k}WѪ~OU?"P]C'@\G׈]2$(rl6Zc{bO 6Z䣕]9hKj%˰S 𧣛 3pؾ~m[afXLUiֻqFn-fTmynfhI1C'suN{cѝ' qaCX`:zhBz o5?ti#?ğލ+;?_eCl6i3o uz1=_56n|PPk4 ?ۭ#Uxw_cH[VOSoePiP3;Ս?eO9AşիW;PMkQqu}jt~ t&8i3&%7l`_7M=O:[ݜwh(Z}q}*{jɓ5>2z/0vg\|ukF,}*?5|t9|_h1q57kV\-P^>Oqs5F͝mٽ[+YN.~^u3rȹVn0}c>^b:\䒧hkß(𣸪5#}#0mX5ќ޿u]Ok:TꧩCfPZj "S 2?Fj`` d!]ˆ_0T4V+,_H`@>w $UiIy6]߂?u0|Ϝ]܀cݽٹ($ZXsPl\HJC|Ohbs9G˖-nx-کr-'\?\ys㬟MF4ğ84!h[ǵ&02wuw'OԒ-xp *W\niԊ1Nx>q6Ɵ8T[& z@098m3~|k:P?$iJ@ß#SpR?/8'lܸzL=XO87wt-\:E901+;wkŹdҧ㰽mscGOj5fj'o9QqWZ'1ƃi[983r'"cB{VQPJyX-@.5rtば?u=?Y)2?P'T?^QV\Lh 8];Z?B7}^nucW~\usQ{sV甫`ǡ'[S˞vOOt[t誣3cO=tvzUOSZ]NZ֎hdP8kԴO^)&8Xyfd<к9y"'KG#t?[#@°0RsK%)⏢Jz໮ug`rsDܭ| /KknʵM;}OHsS: vjY{ 4F+?n#Sדݶ6vO2 s!c. *qz:)WVSg GA+׍oڍ^ƃ7]<TڳilxD敒Rt:]z8|.?2 )q4V,O>|8:om|Ii)Ԝ9uμ͜\9v9nJN{ EW:gt͹t࿛:39:͜9'Z9>]s2/] z?aΑkt>5[>cwEsZ?o4%zzVj_xk4T '5)*@;v>OXpc2&@3^70Ӛ9w gT4FVF@9Q~2?yQb?i)"҆@L?@?x4z*:7H0Ӻn|ݜ=ٿPiXKڣrS < ҄?irw #[\ 8?O 0 imzޙl⇶m'~u  ?-:J7xDWZ0I$?C'Ya4=1;'MG̝;4!ׯ?Tǟ?>lܸI @ )G{@?d|؂ѣO9D y?q~R?` @y J)*Ճ??C'>.OZtOd2~?:)8  @'ML@ ?`O4Z4W?h?LkG4O~R?E ?O^!??lU??@O(*@?~}aN!ǟ4OZ~R?EDVn'OAOVG?F9`?nNtt??D! r@O?j@ ')6?i0ƟC?`K>*(d ҂? }x4Oޫ}4|9W$?P}?)5@i>?a<HH??y?@ß, PZb!C I{3| xTZ?@ğ4#Pb!C,)*??VO(+Stb!C;7 3ךe+7f?g6=}YIJqՋsk7~Fy 6g&XE;2cVƂԧto UVqo{u7(Z4,m{/'820'μڣU y3fqƑffѢEOcm]i<>!q&B!B!B!3?MşV?B!B!B2?6!B!B!R Z ?Ɵ4V?B!B!BI340ğ0?B!B!Bq=!B!B!BHRl),窫|j!\s iBZ\wu$믿B!׍$pߛ0Ԝ0-Yk;3_YIb(ٺul۶K>#/㏽O>O?k>D}D';v$/"|MΝ;e[]v*wNuٓ|7޽{##@;'YS|Vg)lҬf}(I$9ԜY{!>p_s˾}ͽp,O'k҆?Pz*6pc@@D!`3>]~fÏoIŸ4CO|P'_  B{}I?i1媟?T@ !|@~hFO&X@  "3d}}voT?Ɇ Z?T$?Ei (@ B>?PPC?PK6p?Dz@}a{/l޼C@@ B}?TPo@ "=ٓ>T>~Ï7с *P"!{@~?=]@ ('!O~o~U?OT?TP@@@B@}~u~f⏯VS ?@  B@}@ڼ?C7~|w'C7*6@T@@ P?Y@ A!{@>Oi $8ިI$?T"T@@ `<|@'?{ꇪlUe@@C>~8PC7 bP"B}@}I~h\?*  @a+>??҇?@ !`?Eڼ?OvoTd~r?TP5- 0H !O}afU?ObCD7DU@ B{~ި'II?` @ !} >4}2?i hDA`!=~U?C7C? hA`!=d}huU?3h@i @T@@ D!`>T??iiPC@ͺ@  "|>T?Ou~*JOX "!|@Їj~X燪~R?T @ A>} @Q @>~u~tT4IXX:@A@(DzHq~@by~I{O&JQ@@ DzH}~XgB'T@:@T@ B<)>Bڼ?E~'C  U@ B}@Ч>?IO~Z?TP7ҿ^{ͼL2̝;7u`[`ygSO=e&OO2k֬`?Em^H'iӦI׿__Ϣo\s5榛n2{8qb=@4"/o>}5YlYpN:mmmff̘1q÷~{u>]oٳg?Eo=Xw!B@Fsq3g4˗/Fx,gOA9u/[LUŋ~u]n0zkp?4cƌ`^??OVGsj:WM:՚W_}5ѹ4*,Z8ׯwy$扖,YbnXi&CY#i~R?T-Oz?̟? PIQ9FwzC@ (-׿5XQ_Dž?@qp^{s<]jl}<0DVX5\sW:GEmVm,3>_|4V4 E~ִy3OpºQ¯Q Z1O~ys4}_~hFO~2??i'J\s!JbUStQtML>5^UUT AQF籟A Ժv9OqEZ+HcOIK_dsVQ"O>'xPfSm,W;iRXg,Ugq/Bj>EA'ĝ'R5KO+扢τ ]tMղkİCOJ'-?Px Qߪ`͌)o^ǶoS=ܓ{n頛T_8bE-XL BP-ѹTMJڶл 4ZA _WRTa"N' 3ֵn iid,(O j=֥(_+O_8fk{V'd} לڵkͤIu]'j$5Otϼy*ᴞ/~9" ިP<)?P- Gd[+ MX )jP^p_{__&׿M @ (.Ջ2h6ۉO 9hdbLKF>GQi=8= D Ν|^ziX&!@Fsg .2>T}?O)!m,5kVlu=O?i:qG#{l]d&CU?) @yiK*7OkF ƒzT OM>b #-FXM7 X@8((A94'JE_?uTs&4ϵ70'\#]ğfޱES ? }@Їj4OXruHgQ4<ы/ Sm^Mߗp ==`:?́4V ~|?^ڿ7-=wҖߔipn=W~^[>P![8y~ @ ŋWϗGZS\ɢE*mndž?扖-[O v )^7~2?߲Rc8c+SբuGFkh݅y,]xP=R+*].?'OQ_Er` j(/*7[Գطǂ+򇿋;B3i,-|P~\|Ix6_b 4c4]gҤI*YڽQ?y(şK/7uF{GG?eʔ(48^=_O'zV4@ zµ>-EX}<8kžzQeg~!SxVnQG>T>i㩕.l?s5G<{@~|]w5yDǶmHIS<0j.*47V 4^Asފ?hoq't8y3s̺ Ѯ^!Ԟ! ;լ 8@  SUcRo׵wuݗv!] ckTߺ/}@I;Si^G-T }JD){D'*G z<.42o @aP PCO([-=q'+_O;7pZU1He_vΜ9@ (k߫Xf]#ׄcAioc }"xrcA~z>T?iyXxQlD4ֹVy"}}?+HsjިI9I8n_$In'QZ/RMO.3fk=u]߻ԅ \q҂@qG o׉:᧨}moa|?B8Tql﬍QV "Z'|@I+şɓ'W*ZTY܍lsQ/Q4/Ɵ &U?T$?Tu͗m_(}-Z/U vmzO:=\aۗ6pT@CVBPT_iF?]扴_xHs^Q'~H@#~U?hb ?=̚5)/~Qeup?yڅ cY @ (~Z7o^idO ܒovNT @!B@G~ƃX|ѱ/}~?qG՗@:qGyjOQ>օCO@aYvmJ7w,:^3G*.*ЕW^95vڅx /[ @b#P (  ?U}N=~C=Tu,;w. $?jx?y'?q?{K/tk͟UV>)>'*L4:OfO=DM̞==Xvo)ڽh lBM%K8$GktuŸRO0|Px}$]Y=O@ %@NO-uw6q15ݎo0Dȓ \q_^id,7>p/_>>FG~е4,[nЉ?i+ ԋ9mn6Vu_}.Š1Cs`U?io֌?jUL:5p47xc0?@3U Uj#{>UF>}W\iU{キ[@T@y!P`{饗'Z[n &\cӽ! l/:h<6tԓz+OV죱<  -i?nj`j%c{; *ekES}>>YjV Ə ?馛}Y_'EnvQHc:hgU ;;. PCX>驍wUӍ _5nܸqߗZO~zc޼y/~5\q-WK. yO@YD L6b@Os<T iMcW4T}k,Pq[;4DCL:DICԲ@Gy$tkUPXc~ᱠ=Wqq[o?j >>EՔ]q;4O 70jHL}' 'CQ:?O৩Qꟸ-%bvӶOiվժ~ʣQ[\_8^C)EvQ?ܹs @V E~\QگZOy4DG>哊wk\*P$ tHX9޵ͥIcAytҽh 6>$?Qd47S-)sOvѧhmހ'/@Pq"LB>>E~Z?E^voͅ@  B!@?E~,-M2?U@A`!=P?hVa@ @}@~MD * (0"S\~,OU?9 @ !`)">T}৙hoQ@@ D!@>?hvo>gşkDT Dz}h?To @ B} >x~~>7n?Cl+?DA@(DyG}~'U?G @ ! )x >O~?E['T ~R??-@yB"C pN|O3p~FOZ|O5v @T@ C<}>??i@U@T@D xd}?C?@@`x}~f~~R?T-m wSd}@~ ?{) * @D> )V7,WZCb hA`C}~~^@T@QDA("`{~I~hFՏ ~ ??P  8=OVl?TPDڿ@@ RD\A}}oO0CU$@Yme @ h&f !}@Їj~ ?{W??P*  @B)6?Did ~@Q@@D!`>?T`U?M  !|@I3? u !BzЧPC>-U?M *  0'?yK?S쪟/h  "=>?{~?eU@ "!|@~I=['oDU@ @>C?{~?  "=}@~ |Ť   @@ Dy>yFa~ZY_|U@A!|@~~? :Q@ D@!B@ |@~Z?IS{keO   `=C}[v᧥C?@E 0D>~VOQ۽?)Pw(:(0ɟg?b'6 HR}>k?~?Q?ŸIF蟵FG$ HR~??}0to2)1rP @B ! RJ#QBgO#1toÃ^H$Z*? !HA |g]Gl~?G3s?P @B %)Gࣄ>2Ogئ{3#?@ H AJ)%(G35Xg3i ֑M, @B %)Gࣄ>OG3ocϼϠӿ rH$)APH)%Q~ ~?ӽmzQ?? rH$)A@H)%Q~ ~?ަ ~z?K1H$R RJȣ>B%?Q?L6cH4< @B H BJ)A}|G~#vO  )A )%Q~~Ÿ>toS -h) @B % R!<%QB+^͙gg>#  -VK_o?@_oo7~G?I/?{T}akLl2}m/ywwx@X胭/}i2.=zt;.O}S?ugǨGM4Y#}ֳuRI8عce]oAN쿘 {T=ža#.=#~msk,ԁ?ӽMc @@]P;?ޮ&Kj|+>gsLЇ>4pu ύW\qK.ټoݼ7Q#}s{w袋*O;67|V_Wם{.nasumi׾|,;w >o>On5{|e? qż[ni$e"kDE/ږ]\ qMG,m$3`\Ng?Yo|Oӛkf;O`V'2svjۇ~ɾsyz=A59ϲ96Dˢ~"$?N湀&:6J&nTDdy[6gqb.9GFB}ؼݼ-o{Ƿ~}]wuN)+>#1ݛQ?3?e  ?K'pXalN=ԝ9y40`HЩ:`$\8e}ᆲh}"󶎊 .9xN9IT<˗ezA;Lk"jx!3̦_;i>C33>1~cz=ox}/U1?sT7MozS/l8jڢ}zǎ0Ÿ<+̲C@vR/~Ǩi:gpc7?@]P3m8vi[g.o@z9txW0R'ϮjX)c h>EնM9tgZJ_]u\ANsE8KSGiwⶁ?h T}VڗRgWNU_oLű$|g;f=\bߘ^*8oM 4=GI\NTS3?M|x)ޚzmAX8P#֏6}K?g9to#>\ PF׮mK^1Xk58ƀ?s@-{n~]QHlXŷk*Ð|Ӥ2??k47Ȫ:&s AN3L)%+>='MA}Vl  "ߵ}?uTES?S]Y Aj紁?8b%i=ٚ c~O? ]rS?j#ˎ97> X,RlIW'OKS\ ?C?u){+g# ?S@L;O?}HZ&зijLt5+__}iJ@LO8Pf,m߯OC1|_ܼmo+ /⬳ΪǣG rZ;,O9FpnI0G3<ӫ﻾nWwݫvQ 2ECkq C>dß5w:Ӛ/6?4s9xll6xǜnٱRq饗nkSWu3??6>ubyЧ Ě@U^>c{S?(M{G]1FwYG=*A:`\"OͨeDL?|7*zꩧ6Owo;_tvRٟفm_׎ af_m{AEǂ?u~[Z}Ȟ;?"fbh,/ѯiLO>>tVci{ݽ?կ:fo1+GKS0c*\o}[O.HjuBmO\@JQG kaO/ g ?us[o3Sqg'?ӽM Ƃ?<p35Ÿ9s':v#e[ve $cյ^2."+&`\n؞MozӶ-?m|:<q>)85@\WpoZ"{ߚ:Bc:F_.:,}X!%fU9`~#~jO`Eg8p[/ꪫ6o7yk9y'/~9Rq*~b쇳gV~;SCSTcoz+OL|h?=u({/P2OQ,= ~~{( cm4,_Uzv 0NϰMnJ@l87)ϖ[3ȅwY;ѿ0L%*#skЧ+ߞ?}Vw%칥7qz yfa~v]wm ykda#u'׉j]9Ї d`?ޣ\; ùrl^W^!Dq .Vϫol=>hUmzLKK?~?F~?3?y%!ϔP^ b:")$vL;(ދZe`AO zHh *sv{+VY4cPڟ*p\Xp)S7ݏwhY1hY UX{MRVW-\Xm_C딣?ۣ=UwwXlw㼯.WG.pܻtΘz}<Ǚo<KpO9 ?(_0kLŸfs&'e Rg U}}'^'~(BƐ]W\q2]#Cx9^{o,[[n<ѱcDzl=jv Xk{Z|P#1[+@rR? a4m""ljc<:eJV^ ^;]Kaw`LCxM弟ss=ŸMXv?JȺHdaWUZ̬xKmUМ}>WUSɻ {QKû?ts9uX'GU9s*]c㮺re)}Ӑ'?M{?ݜp|gFL΁G?@Nez׻u99"]q}#w -֫cM֫Bka6/{7|}b _s?|#Cu~L&1HGSh-h裏n%{N,Dx|[CK\⍉:1X{)ڎenj/ gŒa>+ϪOt%@\rɁpB5Ⱦ/:uSĉŊw3È ܉ږ~&5q8j"ET]SePk)؎;`OxL`YW{eunJM-׷zFK?11j{ae"wYVV9`cwv\+g\C?OnK:| AY$E\<}bi]3xxZpG<Ӵk9ǐ?3q~»8"'as𬻄>M i$߹G}g؅/6N7P/|I⼋y.)ݞrC:^{o([9-s E[e0X\'gqY<׾HާHcЧ?CG31G$g-hHA@]:md39MSL 㶌1 t~lS\gjq-3~bY¸peW5I6Arh?/Á8&:eSUuqg,:!pl4?8튾~H˅66~LanC^xa}g#H ď#}ȭe/u&^b [8q1WYʷ:yUS9.i|ⶌqLȤ5wRHˮ>,}s82n1Te?N1k=s?ɨ#UCP P0gVA}Q-RnD| 4Nm"!5X#Ճ1eѮ1YWc5[|8V,7 FA1Ug>FYնqe [&z .!s>+vXo;1 IwƸ oXOY:7uKcp̽|`~|3|i#ysŮCmULoGJ.#^O 8?c*cH(Ի{m*png?e}إ(=O6MePP(۞:)Խ$Y/o}UM w?e [/ۧ2[o΢"k}mo>~?Q?ゟNBL?}>- @Uguƀ? PV/mO\j}H}w ˆUuPtJEWo(/K];U{ࠎiʶcRp4mݓ..!Ztq9vze5zpjFX|17s4Y]g~3mSw娦b.q7/W_0ɸ1;{8-^W֪&!lHgTt?e.E*es\7ZyftbCL8m.캷3}Ŝ?λGciDtHfH4, "*scL10nʥ"-@Sr.gk0DPQ|ˬ8ݪ@5?FN屰6[oU1U .r4gօ*HDvSXUP8אnsw7 _m.ӅcM+o1_6?)1^9F~?ަ~&@4PY"I!P4pTUMYSAŸ3.ӅcMCcj>W-5q\;~?{NԢL&PUǎ ⊭Gfi9Rr-D SbwW1,MPofE`2B?Orwʢn!A4XEƂ?TOi} 4Mʋ^X=SʊSkDc.=>э]ß"XS9w{:D^k?T.ȃ]yw(e]&¦ b:%O牎ڢfdity'\g܇ves SqADuz9TmK[eGZ`M6%t?{[C$?L&9c!ֺ *4 eFEXS"j~/eUBU'>)g궡NKwAMĊ\'F':ߗiñp?Cq2s-#0s#:6?]Ӫϩ 9OL&݇u5jǨRsGRmKWYY,aT<`%iuAyv `.S/q3ruq"5}y?2Ye m=ܳSޗ [/ޛWؾy ߕ\/SXGq\; ~ϒPd?2 Ꝺs}{tAY]9 /68:bݶoz~1 $ b}Vf׾ρs O=/>}FU_|wr\'s ŸqOǓ>ysd9|AU)s?z}>ɹ8R =zcɍ"M_Mx=5c%.k F)Mz.sC8$}ß}s.nS5L@Ձu]ßX4#u':;O?>\[nL)eUW]5T xwm?gCؗ kJڠ8`wnR5}wN+S0ԇ?_Ÿ!Oj=G[Q3[/ޛWؾ1R}OĘ*{?fcT,[}.O/u~LVL@PS3w4wÄ-jbw]X k*:75Eb(=i6(NC=i&7tT8Hc;FmەbWks 7r^կ>;?v΁h,9581?1QUXIV\[LSD Ƈ?1Bci*{wղs7Em5P1]fc*})iPj. p/͉Z[S+N8Rl[Ɗ{i ?:n̹ВYψ9Cٱcun<" ꞇUuQgرcۈ}TS\彟; ?ӽNV?Ƃ? 3Z:#&er˜:>Ft(J_vz0(Veh_Z2-n0]v\hujw(*DWt3uR'Fň3 ƃ?LSxzF5FuVŸ:¡ZeStH11'+|(SsSV}V,i{>[_Ÿ4?8^'pm72_ub1dӫ]׻>U [7[~'Ml{c}λKò{z!l~\#Y:~VL&.Ϝ⪢ ' 0s'Ϝieiw L@RDzQqiJlH*Xz׻NKbN4T>e+ 2 V7o|95Hϟ:g?U[qd19'/3"rkN:NptŮOZ?bwݵr5UՍrzKMtCYaW6V=C)Щz.>KS;?Ov]GQD%UԸ]""CŸ￘#G4.}қ=;O'ʥ/.">lX;:>umhkUh~rgcrQ?@KMt5 JמD7y#*}ah#9q]FF0!ڑ#GMWtb1a߱HԪ̳*4rE"=R4`f(X?M1L>mᛮ)ޛisl'e[;oY0q*~Mg}TF;U9oW7O93'~gy𪫮j5tL_ B[]dgt:ľqs>8^9+ήc*:qk<%&j51VO/8Owx?PԮJ8t/_kKj&oH TpĺbNA+\U=)vx]D 7bK W8Ayʜ 8qOl=Ư}Bqc-;RP/ǨiILf>P VT&LwqbU99acҼ+qbZ tz˶KVo`}Vkis,ςc'봩_Cy+o*[m߲ɪj]Y,/Ӌj>ջ#Pn(8Ƴns;Do9c }Yh78Ϲ6o&HJ_=1a Us=]`]"K}w*3sIVaPǍ:>e6o ^ D;l3-?S"m+2V6u~?y{1G$3O=Ϳ?TOm9%cjLJV~,`՗*`es,Xf)©'=_l?uW\#駟B>O.Z㜤o5;?] VIiC偟 ILc ~cyG @  ci0xA' ӄ@KSTPt~B wO}P;O ?up}7j/=ӷf4v\DO kVuBB\s͡oC5?UG#1gQ??kJ&@}ßZ4HI+`YbicO:/rR7Rs+o>e8s>CŸHͬ$)"{K[)?C?0gPc?!hHdZ\*- i=΍e  :!zW_sP-NԜHҋ.)aO;s>CŸn9^kC }N{9~\q?)Y ބ? @ChI~AX&"x%\rr>+5]zR\-/%i72ϜyוW^]$9#G|7N9B%~?F,/gp#MDM&'59묳SXY 1B38c[;CZ3җӗ2[_J^oI)&br鼋=$)L[z^4X<#Y1ݛQ?ӿYG4}4 ԍtx,1{{o~zWJ)%QQ[|Ow}6-$jf/?~|% $5о!G#<ki@B !j^'H)%A>}# ?ӽ~F? ӿ Ƅ? !HAJ)>J#~~?s?F?- @ H RJ {G#Y ?H4]4# @JRJࣄ>B>?ӽ:?  @B H RJ)a'~?ǨiG$#1 H$)aRJ)}>GY1o)H$R R%~?ӄ?ӽ-#gqGdP7hG$ 0H)G }>B9B)GcJxo3gZh:@B !H RJ {G;C#~LfYӿ @@S? !HAJ)=j}G#1۴:?{nOHc71 H$R R%Q8GcQ?#@e9H$R R%QBӼ ?ӽM,m<i@B !)(~?K?s#1[)Yz@k@s? !HBJ)%Q糟Wj}?Ӆ?Ϛ0c7ӿM @B A)G }>BЧ{#Yϲ~:?  @B !H RJУ>BG~L6}31@@K? HBJ)%QG#i?ҽ -gNh@B ! H RJУ>BGS 嗚;= L&7Z @B !)G |>B}?%FM$- @B A)G }>g?to?@'^yӟtRZڄy`SPJ)3V-+ijJvl5P߾gF? h548qbt1Xa4e=v)я~J?V=Vj*9qsCO톦`ώiϏ?#mYQ??kI6?e!hH$ BJ)AG#LL~?O=31)H$Z+ )<GcG3uc{%M$ @B;AB!G#}?F~?F3 H$2 H$ vB} }?ӽ ~3[c7HD$/ AJ8Gࣄ>B>u~?S?L&N$ AJ8ߵG#Y#~?ބ? hH$ AJ8`Gأ|>BӼ- hX#1gQ?ӿM$$ H vG#}YCg\c~&L&"M$ A %(R%~~?ӽ-@ $2 H$ RB#!|>B}?Q?{1H$dH$ Rj@U {>BG#~?z?} @F @ aRJ z>B>BϸgӷQ#@FL @4_$ @JRGcg*G#ΏgrG$ Z AJ RG# ~ ~?}e~?3? HdH$ RJ)% }?yXG33# @3 HL$R!|>BG#V @]hkM& Z2 A!|>BS ~L&~V~Y   Z"2% H R%}?~3-%2Ц3# @F @ APH)=Gcgg#YiԏG$ @F @ RJ z>G#LtovGdH4]$ AJ {>BgGc7ϲӽ M$- d*8!H$RJ zggo}??FM4o44kQ@B ! HBJ)7G#~??{(*ZSJ}P;l 6% 5e dd*UJ)4^zI)iS_7\ ]o1c{yQ:]k QJ)RJ)RJ)RP6yK)RJ)RJ)RjzV._;RJ)RJ)RJ)>?l6fl6fl6fl6fl6fl6fl6fl6fl6fl6fl6fl6fl6fl6fl6fl6fl6fl6fl6fl6fl6fl6fl6fl6fl6fl6f??IENDB`c-blosc2-3.1.5/images/b2nd-2level-parts.png000066400000000000000000011471261521743436100202640ustar00rootroot00000000000000PNG  IHDRuDsRGBDeXIfMM*i$IDATxsٝrOlXUT%-j[_̕ +1qۖluZV$u"$Vɢ HbGH؈L|?E|py9GA`(2e&S=βLYs%۲dٶlےe乞,ےm*3 G&MS%it$UvgͶ,9#:ǥP >B ФIb$es۶(GL$DI*֙,y'uzʥ ^#/B'8Q$JPqz<ϕ* ;8E FJ&cEQ8eX?Zr\Σ(Ġ;\v{4%18r9O9/r8*!%`(P_` ^0L Hqs]y9, T5a* #G9syU*e2lcpz=weg۶J} 9Dah°?$ ̹[P(. @a?ΝB>BRH0I=T,8*0 a`Q 7 xĤ?.\_ P.S ] r}_AxJŢ$Ha`l?eaNeX,jjr`$0 \1+IRuEU+NsAlzo0R>STTX8W h?d! ฝ(RWXT>Ox1nwLlDZU,5Qҷ0 Rj6[θcU,559I p1SM`|^rgio:ݮ$! q]WrYR~T1 #n,8lV\RR8 @j42DRIa3za `L MOO`M>AcBI`αFiz=&X,jG3CL[T$0 H2n H0`A&'&@2@Vt{=c6RTD@MVe`(mZ\*я1M A8u591|>G 83zA@NT!4}:pښv{T%MNLз Ab8%nϴ;eYFΦcgYV+bpNXj+IB  yzp0( '^o?P533M.t]jwd! 竳gYV*T8pL&f繚cA`8Vm:.A)RI` 1 C 4-eYFFmۚPP/cA_Vn0$ckff |`n״cxu-KJEJ~!8z&N0\Y0tj ZZG.Xq>/T|,R(m/PE++>L$r㗑PͿ=ַ,J qye/~הz˗e cZfS'v]Yۋ(O{OoF9eŢ\Nޕ+~ CdmM‚q4MjCv[/וsrLMWt_.+MeOL;.^OAB>OsZ[0Lg) Cmnn~?(Q;^ON1# ݿDZNGZ-y e34Ml6uw#= C5y;wp+&MePUٕ _LzmǪF>ⷾ%eJٌjCIU^;;+ZU=&;L0΍(t}u}4F7ߔ;ې4qtkKi,dJ]EFh}cC͛]YXPRڑc5Ju,וK<טe'e];_YduU,٥ܹ9Ydtꍆ~룛7:+׮Nf(#IYxiI&dr/]kkT_Zf.'MO+3}oCZMe~rSmarDsi2sbcsS{/]ΛoziQd4;v'mm7nH%eY%1Hq~K}EY#Q1PJk5 r=ІZ?ԏ~^gODz,QnC>c }m蠌NB:s2Q^g_ڣ4i{;=hv$z='#ez?o%{{G\,SJwE'&}ydTO>_=5yMcӆDYdyYi.gvVN?Zg666,x(.NjtcR[_*,Mm4>'kd aʊޯڪ*}eͦZMi)]"c=Mm6}k˲zUeGJ~td^]ګ'Nu:]S)BQ\Q?}U~%uHW^2'm=xfZu5鉭G9Iʺ]KK2Q$P;;;}#]mܿN*?;+Ϊ09ԽF Ij5R*mhHggK G0j  po|矫l*[&&dW*ng3TGvKr?eɲfE{nVZ]X37'y]hOHwhsuU˹tIUPdXBa_zQsfff]0cS7g" C>O>H?!KEjU|v{Ed0TÇEyDz8ޝe P+=1os''_e@ͭ-m*Vnc>KeݮeY9 WP~b@`5-BHK7>ׯkSR_}>{n.>>KSZrY'/(}l45FkOL<912(tjJd+Wv?h{cNG-5Z-'J{`TY^Wh.]yE0͖zSuoiI_#gaABacF~B2˔nl(=^(Ƚ|yv[[J&&d_{1#tsSCej5+iqQ}fϯ,ϓ3;+{Hf=xv*Ve_ {32ƌn5JVVdXV.'wnn&2 3=5.bicsS}x󦲫WefgnOԝ3ܙpN[-Eoϯ_8sWzZsGz]εkxS7W2:nIR?\n4U}yY(=o'N>gnaG) Zq66WmmM֫y~zFAu^PF+uKYʽtIľ64 04|"g` olāDZ~__gYrdu:2DVKɣGމSdQ)Łί{ÇZ e'eAleDQ( F"#Vt-.ʮTΨlʏ"yssG\{ydeɩV3HFv[o꫃gdr{8V?ȝRpFmg_}U}ZV3LŢrʽ+;'&ѽ{g[kהҗ?](Tvre?xsϲ3rT׆xeBAL'&I,/+}C!וed  Pn+/-}6 :.2QNYngvt IkQ'Tx]SS2* f{)H5Fm9JQ3~|^/?q?@Fijw[yGi%"+}$'~cd|^mn͞eA e_].+ E~m>xW*onN֡mHl-ߟC, s;,ퟅ,KY-~Wdʆ(3Nڪ}b{=P~JU֟;m ?kTի*9iFaT,c (KwhQQ)*\~5 M*v+dOL[\|^c76T}[|\Vߖy#ݎvߣHYduUi-wnNv/!>/]a`DAhj!|g7oOZܷߖM1f #NJ(RٔTvڟ}'_ P=>\ |E0gBFqd}]Y)Dz+e[]֍j<|("8JkgoqD)וnmzy/H'&ʒDuZm[εk]8cdD榲z]&d^yEΑd(RPG0Z!@ݼ'i]Y\y3&$T򲒵5YB޷`wh^+r=O&Cڷ-eeJVV/eOM QXkWRJI#9َD>VKJEϸ^꭭{j)Kr9n?QnCNו,/Khbq6!8#f<U{UWeY#J޸!X23{啕W*gqvFFɬʨP VQEz˾zUޛoy|‘- ~Y(qCY%1@ u-GZ~]]Η I HFeƗ^$ݞ)KDQvtCGէ*W[Q[oL5{(kLOeݮu yO(tu6nݒR_hC{ےHideEY+wnNvx&O과CNiڦ$I볛7'u}yY\No^3oQCenjj }m$k5ěS5Ol?3$]ȝ(V$dy%y.~}W9."dHKO*n6jJ|_TR7ǣ8b',c2/EJ%y33<.ǗeZ+C0bǎn~ծT,.}`6^ZRrLNj5 Jrw'}eb/iIqș ;U*=vyV(c..ʞ}2␌p2ͦ$f'`0qAu^J}_;?JVKR)k--jepIuͦW^QqFIQ;22Y,7꭬ȮT7dlco<4dmMIx_`gJ@ZM*mlC;ceݮ5r/_]*=ΨxHC0"X1A_|_r]{W5QwlޙF8VPr* _;|$^+MSYʿ-Gy dt*۲)}YTZ)mdr^~q;0Fs22iVT+Yr+;Oד].{kh)vpl#?֏>T,.*wNabɿ_6'0QhDҍ qdJr''_vh_^ߝ3@ع0-{nVL*z(Gt:>T*ɾtI+ g>_Yv670a22~o2{8c@I&v'z!IqTš&oe۲05# ս{W& +MjRvs80TZW,ZJ<طa<#‚-/xPݻw<|(%332A4 ۆdY"RINsؓ?nv!mH PZ.~FAؖ%'NU9/Ɉu/8 #Ғ~wsfpc$>J3(ܑ]ȽtiI xn+U8iTW`,K&HJXڶ=G$(ROdV'Q*ZJ{労w9f'gzz2qo}@ԶUrwCmܙÞRi_@!YW^9ݦA xiIr]y_K$Psx,˒ҷuf?@&T??qiTRv;$۷2K/ vq'w>XiuhFFs϶pJE?#;$ >D? CG,؛QtGjCss64v@TRa'woP|@9=#g.c;{s=-'=lPf{3CowyH{]7#D{!'c=?{ ՆfgVcep~5-i7|82 s\,8()Vp1 r8X?~0T]Awi}_&geJ]q,$~e.d9äi8VER~,dl[Y7FJyYOJ{=ݽ6 wy~V>qV>/c#OdFYO-#E}_ο0T$<+;?ZJӧ2ʢHmKa(Ș$$ʌQzsv2I"˶A?CnFHiy\_c]9jM%1DZ/-F66d>~yuUɓή#)u+r17q, U?SWIS%ueq,Px,"YФU~p(,d2AmD&&++2a{Iir'^O< ^84mn*n$W`dF&MO>].JFJSexrV䣏vs{Iqd9ﴥ$Q7T? Q4!?nYV?9J$i6uyr|dYg")Th(I<9on2?U%;}VLLT-0 sex_KjRʽƁ|^BNiɦ$cg\((,i1J֔dr*Z;v,-)IR[\ڞ;nDZd۲JcI fϓ9u:NN1\T:㷿nFJVVd,Slc+դJEYɽtI3TFȉcٓC?RF͎#r$eeH<; j߼)W}k2Q#٥rcdűju-(IOnooT^ke7{:<ϽVYY;BB$6(u[d3/3oguۘ=cf4$"! ԖUYYGr7#2*kɬ3 dT/->C|>`!Lܾp:E .\?xp>4U8i );BDQ!@yV;r 4}BS/B0gp8V B!B!, yX!v:{?Fkg 3^IҬsJDx]^ 9uш%z}z? Vnܘ3wU1F^2#s߻*eWL^Dti,e40>;wяݻƻK+48ýJFT'QNͼui+0vݽ n3 LjG#? kU}waBIGkOriS{{0ކwjoB!B!BIa!i  qʈ_} XԾj9|ʐ,q C4@{))qpw_ ӛ7!o'򧜂Lj>DA볌 sޥKh@&8kH)TP'- ._U)XD`/`E$3v#WVP{YZd"VB!B! 9bLSǝ]~͇x\ovtfn2Z P8˴`}uP&ի&d2IMK62c;@ZKkdcL?BZΝCmgJk4 4;nv2At&d2[[v^Dˊc{ mmdɾ| H*"ܹ1рu"B!B!Bidz=D]فՎ\C׸D!B!B! a!"Pv:5xp ¿!B!B! yc(3d=|?oaFUM#̿Yu0 Jћ R x4dnlWٯԬlGݒfWj#{_Nrj/hTÿp!k}"ۃ76] Vl+E5+m'}Gtg cԮ^>[!;EeV/G1Z QljF@bHq2!-Q~b"օ.$Pre9KYE<~=VKY, f"?FP*T93mY 6QrCgX{rt׾V΋4 $5 }&qTHZ{ e@DW^)C. K^վJE.Ę,䑟sJ%AVEk[~l6㘿8!B!B!0g@?hD! X;7ub 5]Rj) O3rgMDkx/QT.h3qe׿??+WrD+Lחu B!B!`!π1KH&eS,LY֘Hi} Dz}~ۅ \P,;Gka:-xZ' Aɗ7[_WK/ģd2e+!B!B!Ba@Sf2AB ?9 pDiOOm.N#6 S=ϣ0+Wo6aCN+=7N!"НNL`7S(/=Fs-510n_(#amMbJ%g1š2tՕ\O|??KZcLDG(D!B!B)B2 'g_|ŧ语A]ZC;{{᾵~kmt 8F-߇ܻxw^*$u:߼ }U荍FbLb8j3#2axL@֧>gv{۞ϲo`H5rc1> =^}ޕ+0Agh>EЭϋ7# %$aMUV;Rh y?!硛l/M/Bol {4wNk3}ܺx[[. ps8.6 rYz )=@kW`zvU~l`Ҏɫ"#m!yHIxе{Z0tdHF\O1XK|BcMV+ӷ"ƙZg-g}} }k֬e@rrJt?'KP\uE^'l@!B!BB* ?*wvwz#'y3EJY0u 6 _nfO @Ɵ|ɧB?|Y$YGdիؾJ#!݅C#1ݻ~s@)4}DdFyMKz+nIz='/4{0@A@_23|1M<iFYOGxtocբlODbgQ٬7!al?>ijt0y=++/^^Y)8k<6GeaX {J֐'k)bm*̐G)ߵNo-8FEE-->?E)Hcc{ۯ6jiY#$}gZl|SrA7B!B!B@Sb2R%y?C] ^RGJpVb  M+;`WL~k_/W賮f_"8<\꽜KSWn^{^+xe[ 1& p<}sac-DkHRY ȝk) D7obm}Wwڂufnz= wvZ{V"B!B!$0/&Ï`k rDL-~?_ﲭEW屌Nݽx;?LgZF.a6l ä<<^`mu~wپޫ@?1б`61psp=u4^7]ĻݸַU/۟&?9qv5ZCQ7Ο6n c(])8ԿDl~a.@Xcz^R@CF#؃ڰ"!L B7 +Ad xxCEp~/( aCD7oŽIv"nB!B!By | ?ïo{T6(CoQUlAhjW} Yļ2'a8hufs;<|;o ҫܫ`")p%U9b|L/!Bۨ]d6Qt u\=>vaS/^,ﳜj>H!B!B!0W?__}/_tK3]z;g媗_f0@xD);wV?~'HH5t͛!w2Cjwvfm*4r{ Hcx[[Z0׫С]~_}?e̻~Q 2eYψc.\2Ԍ.OecDw@ v{@J}4*pԿk$ {2'W%ryuϼ Jϻ ߱ Xk'ZCD7MԚͅcs%U<ƺ6V/]̂#E|>Aֲ3Ͼ->L!B!B!<X=>C] ϙ8\ %z?dֿpf/0EdO`|zk+ V*)f9cqbK; c@) vF߇*7nd֢`UgfW'Z >Kvv`YQM:/\'^]cAu:~$]dfb?HRnis}D`I=zr !,h ZqC$ÊM[o$&'aB!B!B@ ='?_'wvPoeMdz@G YzEe d&^Ew'{n5ch$Q3 sn˗WVb8{hozuo~:W5B |h.jv;v2Bo߆Z]E7xֲj Y .L}<OZB`DUuǼq 3"}wv୭%k(͂|6Sqƿ1dH@~(sFR~~^Urn05 B!B!EL ]+vG]i62_{Yu ϛ]}eD<3)%e?l9D!B!Ba!O(h~s ?'E𶶠M9hy6x2Až@dNakZ-/f%e73cr^n܄j#x4>Pi>`]g&31XslBC'grJ$lIt6g xoqhuH!߯զ{3PiP Loc#2wmmC!ty g$L&P:1$txw׃j4mmAN&iuR{a=O# NPa~Dž$mW5{,}?IDT Y/xl0'`lnKp._=x\ %" p:sMKtNa7 @2f08|a8,Kx~ʂR B!B!B@E/ᅬ6T<ySUf>Ƙ$[_j֗8nP)ٸۗ-Lp03]TOggq,0%kk3sڢFLi}8_^ *8Jw}fӣJ}ΈRcrwi߇Vi21_{mV~-;F~=rCҵuC8ǽݱE|AonftW )oѿΝ&QؾԾ!_I0K$$ qb s\d4Btz={wB&؃"\+PL#+ _ B!B!ņ($i^XZC^@hk He׾VQ p魭yc0?Gt1' P5t qZОh'TZ-9kEI_^/|@ܳUMMиqo}0GzRS߇B׃nc>|}\?m4kY\!B!B!bB /rr$5esKK?G(9>Y峏)n[v:1GSA ^gN_j Xsn<ܜTna%2_,W4:*<7ڦ< | Ԣ#սPud>!asqQ鞒RE|[ U/7sZeeW=ImÇ6Tu p)^}xu]j6KUE}V68au=Z@%l@!B!Bȋ yBFc=^<f1ggU-=/qwgjd 7;S𖝧It !TokgGN1d2/M\W k:"Op(k B!B!0't9 5%V])mejs+#5}( 2d%}tQL`ݤ@c`]JaװX{啤 y ߂B!B![BARP 0&1Ҿ豽liwFaֳ(A$cЍL3- K+  /lӭOܿvu\|]467@#_A^ 6+B!B!Bi  A i)dj4wI^$1l] E1PZ'"!B!B!/ ,K&"P_r-3}+>{$;ә8WIEyv"23s07 ]*Ɛ?%Yǹ_=U̵QAUǝW;gjc1ף<*Jk*YY;&MS}.68.!ae-Ty^o{B$3ُ 1xtn@\ЏL6hn| T%Sulj۸s0Voc5>ƣ_ pD!B!By`!O)H2A͞~" *׮~1Pq DmgFJa,0(aBԑ&V B!B!Ł<12[eGP/+=Wf^5kR9|sъ!_8 "=n܀j,FQ P*˓Yh31 }-?lqLjnD|MxI@R Q+B!B!39a0d\Dؙǘ!7gYr9(h_zb>@n"-+w}\ks7#3F$?gUYwc ˖N/d&#Y3ב<ۧ`xJMm։~fbLܥ|*zG#q±-*4&n UKqfӨE񏫑 >HǖקpNw\D5LpD>$=(L:WQ f0a{Y T* , z?tV]2$0.#yԮ^ŹoJ>Mx[[ςsh?W$kMϷdMh!QD!B!By`!'HPJA'o}8S |ynĩarN.;:m܁seg'GPjw9hfRȀFUYeCg~lyTdϏcT^wwX嵘7'";MLyJyut-3`¸ܹSVAy^SaD yd)liZ-OA)Hv:bvPF+Ig;_#KԮ_USȷ!'"P(oB!B!B^,@ b^ |@!6Z9f1)N@jPi%LSY$]^߇ܹ @/%!B!B!/ G (yljl+ < el_ iyy~L}$ΪF>fkh{CDfkلreN}V(LjnDo}PA?SB!B!B!/ `]$gغR_r&ݙ6)*FP>%BT#U_>}i(5]̱u%]ڜ`U5>nQ{dnBM&P47 !B!B!TBNfב'A̔wH}Y#wN4+y?HIfOmJ\Cv_\D 1L}@իЍFa}?aD!B!ByA`!' s2"v:Ąvۖˤf >o$&lj:Sӽ /@E#W>?Lxd7Id 9}UJ)Xc T9 M(ѽB¼ܜ.s8#^&ۺ1Q2h==Dx UHJpAn}ViT}ykFYHFT |w0L֌1*@RAcA7ֳ`2ypq@omn! wkI@Ik-tzN!`gf2c8 !B!B!gr?Dl$_Y ^s%0i }j+"H݆>w`:S(b6xF0@6Gq/[0&3q}~|UUP 6UJ a{hϻM$Jk\sG/;LOa˗`G#t o{{7[z ;hoÿ| "J01Og@2yPJa8= ^!B!B9C0uI1v2w| dfye)J%֙U6ˍ7}\X"S=5*ULY|u1<օi7ONow=օqWZxUQvt3|;U/νjFsHZ TWH)gJ PW8J#>mn[YY ?f޹s66 \ PX2^s(h}%.#~-lٟzɟ @ty_LRVTb\9A!B!B\ziRڿF3;WY"VrL$ɺo6g]ZX>$F#@ŋYgF ~}кhf)  MlKHRqg!B!B!Lt( ]^|d9S;<,@v:DѬ<:~SI#,i؊1IPDRDol$UJ=ό>@a*E ä4~%YgYU kaCzk ͯ7,'xXUaL)!B!B!gD1Ht:PU(,-ӕ5WZ'tZr}q @ @-s;-T#,kl[ ûv-YCzotƞ/f26a`z=x@TKS}[7C%z W~ B!B!B`!I2EDv߿lkm)x"I;{?Z%OEy6 /5\r1~Y6Ri/Yt_v߻jPFGKqR=Gmɼ]8sx_7oyzrƶRɸ~q L`]Zx;;kk9sI2GEȍ(ĘDwrڙPZ'vNL;#vzeeШp{gJe6@$ ^߇fqk%W C`P\amx:o sqGP^=PeԮ\B!B!BHyLˆ/ '1dnveQl\m%+R023$yT :Bۺgn03r׳Lf|=X# anÿv ꣏%y͜37ZA )ZHWsgƾ.!aNZ s߻W4Sޭq"֓:J#cf֞($fcMMº; Ӑ j4_ ;4*e5ɭCuf2L ka 1`{=t67!!B<ȵ( 03ZjQ(B!B!B 1g?7D 18D>lIev*=/GG<}A'g k h@jIKy8m xW 9Vk1uˏ1o?FJ5y!i`L&PZoIۇc 0&ƿR]d_z v[Q)B!B!B 1NP3D`~fҟݕqäʕNEz^ jlv*WglSڬ&h~63\F{@޶ֳџ&ܚ !rmajrA'4!.(*Nӽf h!Pt +W uzp?geGG0Inկ\n-vXaaaayyO3ϸYr{{aV@2iF6r:X~ܫB j6mlL{JSZiD@6[[ M[2&/N enWͻT.rl"Z_?/H7I)Ds@BkϘg?V>ʷo uDkkL5}VCnl@ h)* , 2Lhm W,wp~}@vsQcL!a:l0 0 0 0 0 0ƌPP۾D$i m E f08D&s9 f~k=-h(/dlu;fhl Fo֒3ս{Di$5>E0á(ˊohЈcVro ?2҅v%J#=)3WTQdG>L^4LzH96U2[_ͯd |e^ςWʚTd`qqki}0~=tl\T^wx_n9`uthݸhoј0Cl}L~{o?o "M0 0 0 0 0 08aNHL舅8fBFHG Q$ck/2RI+Ti a-^LϛyJbk ^ύ&FSC]JF*~\33ɕC4͍|@Zi4qiQUYMJFjaJE=l1V(V:pA "Z Ey\4""{ZmZ>eWi$*#$fศ݆h fwa:.]#g9۟yNcjZa0 0 0 0 0/ 0'$S\,Sٙ1dpdhMo:MmlزO&0J!Z[_ӃDY34d(B'l5E.U7A6<)e d! :6K`ԖAbߞ|`lFkDiWfv>-YٗZ'c Yޔ??V),Q[ɼEzjs_sI775c={˘c{=jhX:\jwǙ,l6AR&e~.M% @(kaEbIJʼ9M±t)%"$;pc#mE0N1{pN86 qm.pxR& X[Cu[3S'fJ AZ/|FNedz҇/*yx4|Nt=aaaaa0'`$,9Ûw&I߇~f8}{!L&wp3R5dc鹠77̯{=HW2>7s6dYiQ@A߻cPW [bgg5SU)_Eۅl L`F|/!G)h+W+.@y 7DDT/?O""ľĒ:4RZmCV ŋ秾{wFvxKg."\`Kb`aaaa9pÜ޹̳wԖ~D{{LǨ<ɀ1y},a`I <:ex){;Sӛ&buF"?^?xwvgj4+XBǹ9]|JS(`r}ݚJ,8>fȋpYiya8z= cd~w/kW_g",wEX&sCDŏ4zn~SҨ16^؀ dž3; "4 /^U@k1>+~}ȵ,#(O?fʹx !eHNG7! ҏٴ\+qV-;t ܳOFvKPvmy #  oaoeoaaaay9?˟?>F?1wYTF^pBk:-YM9_Uj L m.giFG^˯!fc \e+TM̸n+.}ek:O&❝mHk$wڿ{Lp_aj]Di>OQȹY?Sy>cE$ F? _|ͷ.CU뺪F~BҨbm710e2A]?s.4c~^=ίh@P>><eO22 +<Ū)!o[[[t21aaaa s< c|u.o?m*?s:i Ylyß L?rBTfShd D#>~f &c`]dw {}D{{kkF>ؤ\vvliU#g!s7*v~#[eAƀF#$ګέpj{$r 1ı^lpF M!(M!JKƵW^az>`}}MܻhNZcaaaa30 ϖ0#F>2D.a[[MQ_[Cmm $%&lit*#P@VEhl(:_1ϡ>D7мqrg5]UkTh[!j67m?u)gD竏0e꯿mmMu95>F3!@.tիhݸ4gwqe@Iǐ]/':o=F3@[_eF j{(Ma4C4 Dkkh6-C hss:꯼R " A ^L?"c)!1Z5J!G lڠZ1vwAQx<%&s&1CP fNkjCgKЬYI2sN3.fdJgRd'z0!jo1 Qa:7ob/c?ȍq!o$O&,Z0u[gӬ LnkdBGY#r0uf {1PCz,CtǏǏ[c,>: Pzх JU !Wq ,Lz-; yMe R4A(.*[ghe0.I !2ǃ!ƝtAkN]Ǐ\ni0 0 0 0 0/0+'%7)ݺ]?[elgJa87`@h@jES c X W*0Hk# I87@y ?Αq{g!ۻ?@#UJ>#\Z.i$*J篕% c5Lu2̤v:h\B:CRe 3! 4|Ӛfnh4}FcjRYUFc9[yD`9ik:rg rcZ=x`` z=~xM~ ww %gVEH$Ȯ׭ZQi UV%,/P*ύ_"x v˗_7Ry~C}~Ph\hkkfid x yKr1g~X*Z ,GE`{} E׃t 77Qz!p2g$srcժ84APn2 Uhs%_W%C)YJ+[6ck,'~~A 8w*v\AcsQP53"}ߵ=z  0 0 0 0  "p2(M{=>HSN~рh6 Y_>tYu.H)4w wwaF#z0 b{C [xS]$ .*TJdw@;6%M }H)d_|}phw1sQ_lq( M;`M硔BHk)|~w{.kk6HbiO[}\ 0 0 0 0 0o0+T}#o?eB2IpC}9(ˢ[ 4tj5~;;2Re?)ԣG?ar&ヒ :e3&MAZCWjQimz=D{{35#clV͛Ho fw(hm{»y"ªgrؽ@xڵk k!ojn׿F G2(/keН(Mm5ª$DŽHkPZ;/\{(IlKp^׎cˮ=+UeD }ꫂhmiĶpfi}~b2z=u\k{"P!u rs~g@4 0 0 0 0 " "-ΐL?M APY_i#t c3T9]wH>TAd54z Ykc3V?z._y޿>\I&M)nMt J-fhڬ_[韦@?,yL}xC߹jX?A|R>Nleh<%5?77PXz=Hp8Ft<.@6f}-im]6o>0j4X&[%`sؽtL*&WfA1D"߷Yj<e![-k/{OFJa^P2d$MQ"ty~- NxϚRX;`řOӁ" (zaoaaaamfE蔔Q?Z)#>w+W ֐} i #eeI*xlc|^T)PdA3F#4_z Koo~UMd{T' ԃH?c.v*:۷gtxnÌvOF(Fp (Һ^Ė: 9ciP8x }x#t kmo[^cFrh.G!Fk]Bw&7F,G`(sfֻLXmkK4fWKWfНD#z>d Rjc=Ε׏cH~KD ԃPV$(  n5k`w'aFYZ7WH:Ǐ?F|"_];^%'dzn1<ȯA.xc "P5PɃv ;)htL4I`&B6 ܹ\> 3 sLfbC{ RoP=f0IJ@A sJ'vrͦ=vU([mUZ-pFP<@D"sRB0gt 7*3WPww^[!C)hT3!x~=^"}4(%0 {ga^\4;ܵ ( _>RBFߏaIp\hNR6% @4@y)eIZ9z}1<6ΝWgQ"3CDv{`8$Gh6h[,90 0 0 0 Ü"8agƻ&7~)>y/7{=lc˙U_O"ۛ0!]'/>kAoQQ`2$n h6sNimz=~ߖV'Bv6Lq8o|&2ͼ5*h LB8lFun8C7/_f=OrcA3!y2mԮ^ͳ{Ha'v{ nބh4P{%{Mok]Dzݙ1F%>x1&~Y#ZR !/=(BLjh\iW#E5hJa2DF )67Amn5ƉȶVO2\v {_SzڽCv&bK vQ/L&8<<aaaaaN 0+$:<:b!G>#?}~ /}kgz|HiV?/0 0 0 0 Ü8aV"<z>?o5-Lg }ݵ屝y5״MSdBw@ymk>Q!g1 L nN#rEKhD.^w\=Ÿ>ef{ß@+1z{!Ȯ{lyӱ:8_"5(M}ٖ됧/^Z" }U3yphju]6d~f ,$0 0 0 0 Ü8aV@"<Y ?QtweD'0$GVޣF#Pcla!Fo#C|ڱ2ByFDϞ}  jh@#3ar fܜwyVLUL*Jm uO?Vs }bc#/_CO͔ig_ }=CZxFP؏vց_RQd@va:}=ohDnoy& 5 ^aaaaY2"47o?_7 Z-gi?ybN<[¼" } >VC㭷 ׫Up(f݇c*+l/;ˌ#H?Bڵdn^C||$PվK9VeǻS}~ [0!߇Qћn(3!u ꯿hgh>yRْr:$|Ƕ" n9{~-@ʕ'AB* Xf=?pp#0 0 0 0 üPpì2er)}1??~!jW貼*`K<|Z&Ih !}f=Cÿ5Y"pW߳f D-5|ޘim f\欃ɩ5Lo5_Ea|L#?gjCǏoI3展Sp&eXoNkm_7Y Yƶ16Uȵн=@UF:)3@=~ v2 *c/ Ğ' 4 ?e0]k3c܁I/ږY6w{^<^^ Fa)7RBA9k]lM#\ k0i Z9I0ov-޻0Ήaaaat + 0իְ::zf#g0xz6>yݳ>LgP#o<mPYknlim52Vޛ35 {_}ef;v ǧ5Phl}~?/nC ˊ{e(xĎÝKz0&j.k(rf䇙Tvѕ+Zm:yldF4-X:a@xl`:8^f߽DLmnn `%*@6B+g<0Ʈit:Kl3n]0GGHC`0('z=_mP ,c)A'x wGJF#{OpSM^Cf3o,$0 0 0 0 Ü8aV"N7O,Hk$^B~Vhw:0;;OtMb2 ƕNS4u4~w *Ν[g{=~^M\˗ Fr93ѹsSsd:^?& aG!q J!t k^aQܹcov~?:0Zn^v" pr{h\.Y >@g{6`;DhO&h:R@~1܄lc*iD`!иq1.څ$vwWp*5F?)!ؘj)=Z{yP'? DQ g )WWx JP}3ʷ u[-ΡtB|B@{k߇h6LD01h<vn[o,SaoO<hlBY{T9h~"@C.ǐ6Dp k{{8;Wۛvm| |%w?/z>i4Pl40 0 0 0 0` î77Ym'qV?ӺsӪ!ϹFƀԶ'MS,Q^_~BF!ߟdp|@59{j~| &1@)c5o̗*Ս676xaaaa p{kKjgretmPVE׈VUڶ$c񂛽2 rpF>r/u*D4HYKg.&?pu#aaaaya"kjWiX*Mm% 5ƿRܾh2)X*0!L h{µܠ,;xxvn;YaaaaaS0 ühxߗ+{'/7Q2sp!+\3R.͡L~- qu0< Geq;VUZh ?TqF2p3sc5!}o0c ̿Wby+3[[))@DȆCd!010Jg4[-|X|-a a dL Ǩ;Q-*_,RB4aKi+"Aעj: 0>֯_GuHsca:WsaaaaaKa*?^7D4_We!}܄5Wihٱ1S2}`r,,O2`JSFno@q\ _OS`ѳ ׃$B9utЇнDK/A1?aaaaa$8a,2Œm|g U7+Z3-VՈEH;S+"yiij3 l2J٠ z ﵂ePͭ-4\A|\amP`dwVCXaaaaa 0g ߷g1/2 I8ߩ z !@޸]J7w#|\ Q-h~&{&쳠jlpFk+/!774vƿNөF~=NQu5Ç06z Za/Yf2A0.}DnpZ'sh<B4Mlo`aaaa0 ÜVơϺ^w9qg\q^_֜le)KZ2i N~VK?(-9ܸ2.#̊? 16$0onD4QYCZ:MAZ_i@)PXq"mI91J{PwBno4o'Q0((W6Z[ [y, 4"Qوe/giM4oAʼn:$I=im/3ۯX$@KἪ$h<AH}p#кp - =M&8ϟ)$k1Nj50 0 0 0 0`9mq` LNr%#g AekE)W@.TZ۬2U- LeJce1(5=޷B(_Bk{?v޼O__S)tPYYUQ×| R yrDFf{m4"|ȼ _z=JC)OV1^Wp9LjABzH;PPr$(i7@!c#B$ O>e}D B2"eaaaayb8a3xgAR""H))3yIs&dA1dO \gz|/2녰wʳ֍Y*{8i~YhfvG58fPY^Wkg/B(f@ ݓl*按Bk19\U544'\Q9 ]|`xctlD 4PltU'4o('4ul (MnQr+W {3aaaaa0 sPv2D%IW :-ȷ_D}̱Be-|@ p&5 [d10>!D+67s 8۟Y(o)aaaayp0 v`jy>͓Vϩpv7ՋU gT:C~_r^Zk5 ")88;ǭ}wA Dx"] ˬ&:3߯k,0 0 0 0 <'8acl>0Y.#N]}La3Fν?2fښg%Žvf4L&>f0Bc[ ?sZͦ4 Wװq% 0 0 0 0 aS 1y I’Yt&HLLm`;\|/͚S\c[ь;t\@y1cR!DuT\.VE- g8,Cv6۷vja~·.n2A fv[aaaaa#0 <i 榽 )Uqe+ ])o:5Iʲ< 4$׌j5{ 0J|6o1w3mnnf0Pi:*`S\' r&kZY><Ћ[1v6~~ i AF(MNP^Qx="k9O9Lȿ'%7eЂ)Do&~kVp!%d<J=x"+JM D5EVCcݽ[x]uH#fk g[[aaaaKa%ƀ̚Yhm `]8ڝx#\1un" "8~Qk^XJKiߋ";VccAvx΄St\(ANμ-km1ce -h^kkh28gUuZJDq|&}06Ak5[J۲F^kl;Vݛ\h h@nmA{ҍ/ $@U)UȬ$ISD݊R f<AWַBY{+o{=Oeaaaaa^X8aY@3j!  DnjPN ۙ|"ۿ{׃^GDl/M*zzi>QkQTx'4,r>}^>%gwEם~^ϚՠJYeٴ_kGx¾BXw0Fw!=f؛~߾hlZQn҈&hU`p|" b 5, uȽʬB _ ! &D{{h# }"Bɯ~!+Wɲi0$߻^(v!DѸ~f>Cәaaaaa慆a&BXrm ՊfUR/~]0!~ f؟sva}D;;/>*ƿROS[#_}v{|u-6aaaaaa8aI0f! %2俄(yBjPq1]@J[KR6l~Zkz˶{ָ_8PBwh݆8:oUqVr $x h`?D3Haa7$@2#0WT}0 0 0 <8a41tcz/~ c-%s$9@L!uK3`KKi5X`-P1L2rswϋ>n>CdGz=~"lu /pt ~_p* 0 0 0 0 0Rp02c$d H)|"Rƻ1hzx Ŷdϓ6{:Vnij mg3wִ ~&:HkkU7o7.aB '+u{OiX\ |v.V߷!WWrOf7yšxi *I雡}!B兔9,m2 @eoS4\[C6-Im|6E3\]V}YK̗3us$(0JR$mcz͛Z31F73hgO9xn_".\PU AY6;?^c@*BC֊1>WXCmͱрbلȷ ޽, НX߶2h u#=d BaK“4'Zn8aaaaa`f4g1 -@)•pӤ"YH`e¶'X67' Y|sk䲲\TšIS+Ԟ7./Cߝw€?yhƚg- ukPKph)u@pK~RŅS/WX.a8W_Ex( W1h}tgݺYCs4:~>$ u`aaaa3 0>BLԞSKk 5 ~c]YAA0Q~DmY~jjMjJW.vɶv~Ө 8qH5e@BEP Jcl6!WWgUpx^i`v t:0m׮Ajoܘ4=8@e5U sYl0 0 0 0 0|8a&>877h a{clӌLOdy9zWW!2+w$P,qJqy jjAn[9m_5b@w:0> ȍ k/QJa"?EkLgd]T5XdĖo6F,Y0ڸk="}&_2 U8,Cҽ=ȭ-[y$&\2TZJcWP 0 0 0 0 sp0޸!(I ~sZ#=)ߧl sYӕK7IAhcÚwfZ16ijἽ|Kjrf\u$A .~ޮJUDػkCY럑Ft}m(S5FߔweL WV ]>14|]YC?CE ۅ+_ Ld ._ ruARPjC@f.B*)$ 0 0 0 0 00 k eL@6VJ>k?"h)߇2YJ/cj<E {Qd뵵q? i )(M! F:@ Әll\Qd&sӈ>rejqf>>rmfg=޴ݍk.;4\{WaBbMhV˦W%2Ax/S͍}xhluȍ [ubӪt  ҈(Ω@}sB6h~Ӷ̿kSP ?c#=@wBj05v?si4maaaa9;0K s dfBe52Hc)2~a<0f| Ï%3V!Ȏ<ƲAe@nڋ(k7(N5Ãf=K 3j8)&*Z-^,+n Dfv2.(դ5h0Y|nWz0.LNΩ  >%f Xcsֽ3efא95e4כ)eu)ҺHh=YuDlyJ $ t?l4NkKqիݹ`{ؾπ60> .|m5M!Z/xaaaa98a@Ppʁc:<{[' FR9 k1`j5 05əUɰB)9oyl3k5~w`_MYB޷\( "s|/@5Z8B#Qaez}gtW3Z= MOȎ=Qdh/ߊ+k$J4yN5ʘ4đ6K_ + QRZVh4!`@Fdq gȕD7oB-//UBTjYn}+߇h!WW\=0w7'OHZ-•m^ 0 0 0 0 sNp,l2 X qEBղtBJ (~~rb lPN39emh4cfӚb{̤5`++V0ϳ܋;6?< st2FǠc}3"|7n'`ӟc= 0 0 0 0 lY!%FCoc0DZ|>TF8H_'cl+,Oσ5h*~D>O"e!lzOKN% L[]Yh2ێBJhBnok@I!T A`k &h4*t`H+aaaaa'8a c {=uH)~Fh aQ浶 IS,fy&[\3rh2z0>dL"+K7@i:Je"R GACj++EK3Z2#s݆aaaaa'8a@4؆q]$y -crsO>bʱ[^"kv~v{iph{{Uι*K߾b_$~s3CE4&1q 3YVe޵\E~ʘw<ѴN{,R p"[_n>n Юv{4PwQ]F1=VqM/M {!!?F{å*I(pO aaaa湂f 8KQd )Awށۛ_e}2 2^&?ft0R$׋nQqoND4՘D^~߸RISQiw 6x3kr:75*2DzP@쀔hzOFc:LTJL8==MB@61P"&秵]oJ/pq/~}p&yB#c:&>>F$2Uqn| c@_gcsU$%B,Qh=xt< -T&քcQUj@J>zd311R ޶zon핲z4fv vy;S! f!7 GN; NӹOstd}=% p&Սaaaaf H3F?P!~3q"ܘx<ȼrlIYF@BTX7\p߼u@VkFf?JA=x`5w.D\Fpnl&{R9Yf5rFiLPymʙDRB'5y˽ҷނzTOkaٴ&rk$MV ABYl6})Iy+A6V-iDij׊>o0I&5&֊16emm9c:7c hLDH -JA?1dꫨDA 1=xۓ:lϯ@ʥڕ߷\c*Ô>pa}NS4 \Wn#aaaaa#8ahǻܼ-!{}'O0DDx3g1ڝƤ`ؖlh`{x/  F ND0h@6,F8"AhoWjTY@)+t 4gR<1fAIe=<MkW~f1o4lPƔb&}߆/{7޷\idښ5[E` =@Jڨ҈& - 4ݡrE++f4B0ƛo"wol<\yZ`(I;o~KDt_W%˙>eBT"0<w_^UlEg8 Z&'0 0 0 0 0̒p0"I~{D@Z2 zyipsӚ3cvF5VklP^D}~n|U#2 ܴ4~V]  M I&9s*-]r壸ݶA0Ǖ=x{߃zRD3ȻiE*Jnܰe>_: @ Z"Zmf֖<N8F<ޥZ+[0 0 0 0 0  $Kv̩%IǏ_CX?Dgr*"!l{gdNdjhddžźBߚӧȘollTkІ֝٣Gh󛨽 l.N%c\[!RQd3JO1o#u˾xE>:Hh8vV-vrsR,JPCB>_Clhom?88O)<׮r0 0 0 0 ,0̒A (gكPFwM,S0F=WJ$əl:M!3{~J(JTʖ&2XCDr/\\2ɘuEm6!ߟX"k3NJy_rC j5> m{:`5>v"Z{@u Sk;F c Vcwͫ4_) ;͛SAuA7 2$s:I$ Z/v|O(Fjs7elh5[Ns1*#ģuq*0 0 0 0 08ai8gO~!kPg޼yԿO)t<|0M S#GGU֐krP A!~Ȗ dͮBٙ3m' $0-wfo{t X7 + agRo%t 5!CFR0@?~ D4e~k rsσX|x=㖌cϒ!FӁt 4Y0J!Cp&H̿I`4Ayy8OYVdXC%`SU0RF?T<(!2@kU0B /iqh8Dxڿ;'*_{ׯXW.'P, ?Dc)ĭ=A0M! "[)ck^)ADL)e4%tb@Ї?ݷr>. 7\i4??.Lh4w8caaaaY.)|L }睢}[߂lNQn&`" x3Җ (:Mj5rƱ.)7n X[X]oGă-o5%gHWOhߴz4ISkE'[VC0Yd`ǔjo7!! d/X(PO` h`_+׮AFEh6O3`2XcY j5(9]- v!WV BLǍ}4e1BW7 &(i[T)H;0tzZӵVuBզZRBGxD1VF#}xh[h U&LT8ъ;>y&3+q)ga[hm1 M{,ˠ\9K|Li &C$Q @׋BPk~ ݸY#%叛ɻJ+++}x^4z`<}ƀ=aaaaufI8._=~ߟ_ueO nF.*ޟ 7NvZ:4ʽ{h nbZÌF0>>FVFSrD|$cz4*JCh4n#]B\d^tqBXUccӁP{?)F߮ӧHcdhoO!rŎ|~Yg˕$ B֎7~qZ[:*J 2S!"l>f4ʷڧ? jM!ƀF#pEV#)aUsXV>R00W&@0q!ww=|?mQWD)`c*&By=Ѯ"o`!ի6("p_JkdJ5EHgJx3@`6xS CZF#d})kAg p4p[!aaaaa'a$9ഔ3uԣG "-nMS64`0Dw0S30.:.~wݼY$3cdh6ݶDB#?z4֣dod g:J͓C~h``+G|;uS.%.Yf33<+}@d``Mi/|kB@?}ZyRC(MQQ{eeWi&Fݞ3ډ5I=Jͯ|V6(@wG׃:8CDwipm\ 1%뿬F0Mghf&!J .!B`Ul[2xAe51th zUu h5sK7u` 0 0 0 0 0Kl4]b%4!jwly܈ C2:q!8]aaƍVW!ʉng;@?~ux͛ۤtoM:BݻXyk`:Ǐ"TӡiAabmu/ 0 0 0 0 ,0 ɍ.2oc{* I :v;0Qm}d* Dꫨ߸ղ磑' MC!1Hw[eR #^/zs֙F{{W^AxD9]D8F`m 7D:Qm(Mk߇GtN,& F䭝QQlqSd h8zh| x,\C.!m [ K _=O[ dZ9! XpU2i4ː1Yh| '5K<uk'Uhd[<}jenܨ\;_$IB P`aaaa0) %] c~{Awh&;w"ÓȲ GdFhs8e@+:c_s&0+WT{3!{Pʖ#t޽k{uWhXD0JA6@IU)4(*1gԝrK?mI^Dps+D1.dpimMǏG# /<@X7" ,x :L5WsьFvhl 쥶" !lIzǠ}(B7\ h]h4O(`:d~X' jfau-~xא+_d 8A'I* OP[aخ8l6!֊*'42V5tvw!`!Ft0Ԕ:fW00nu'e4DH noCY6w(M}U=?k֯^ŕMWVN42>!uDO~.#/gaaaa s:^^yN嶣dǻvpe0:=aQGDnh4l&S4O)Ԯ]C-Ҡl- z=CPAh{ɣG@!r~ ("kHZ[8M!k5PQ&M s`!ukz8a`eLDիL{)UEy"B#co`sAOSRB467T1׈D>H)*.X7*hƢ][[nBp *8~N{{6 z{C|3ZXJ^>YIi\S>>o:ǽ_#Bx玽א;+~jƾwwOtWFkdGGQp}}l>f*"R0𳕝y}xcI䭷;D/da_/2'*QWw=lܸaU!.5 AZ@oNEŚ1Y:LFRE7nZukH{1^Azm @d+=->ԓ'V @aW!w& t3^D52҅w덁ؘX[! ܼ Qg.;dwE!j/p};P6 ~xUj3֖"fdB R)dYjLS#gyE]aD&]ͻw+%FӿoM#/@x{6 0 0 0 0 0`SiV V$Ix-?8E}s!3}Dj!\Yi @D}wRF12>Bj!{܌ z"/I~_ja )O$^d?kmMFٜQ LJwG!AiTfx "P"կ@Y#X_ a5 sMuQ :UcAR"}fA`+3C_B6h|ͦ(6`F$U DnHZ&"Vk`f+ A)Ǐ>xQ66*X2&w,$ Du""dI\E̟12fDdt-aDTsLSd@"~ի'ֈ3eaaaapÜy^d1x];;0zTjh4FP%쎣$%j,j2&Ǯo;#FZCLhWFY}| 끴F 'se:k5$hc@)7n@DL7S0i B1Epp^-@ssUShgCCAwcl9}Q0Q_ʥa67мy۴Q=üң]!L޽Z@~2ٟEƿgcPUǛtz6~v땼Q"{\]-*REyq SBx-Ux} 1.etע\]1<$r^kh4{/_DWWBA!|Vls.j 0 0 0 0  ⯾ۿEw}jcr{ d|Xjg!δ:2vv<} y^tKK `{S&xs<Uhǐ67Y*^u\ }FtDe{Wdѻ*&օ_fUxT"w|ԣGP6ډZCC=y/ըjM`9ԧQ޲Y3JS|}x`ch \m&y.ccm#J`Y[r*4{~AXb'g3 0 0 0 0 ,0) Zkb_7v{Ljn߆LS3;Aw:@cȺ]3g(c裣=ǫΒMɓWyY^ˢ՝(MAy)y`Y#'a@Or~իo*/\sXcx 1_Bu]Z-{l)25\T֨42Et@Y&dLuMnr}XvN}' k5[ߕ;/iDXCe&?9(ܼ POeFnL(I'R5ht1vʯcp|\_yƼ}h`m w)U9$Y SE6:_Vʶ(sQ* `z!{}k@ZB@W-eDBHy jw 1L/珼#$4ڐ+6/aaaa9%Qq8q+_d @`kk*u:0g:{=^|0Dj" /&&!7§~Y6m|ǐ c_#2{!DZHYdzOj Ogݹc\S2'5Ab_S|>+'Dt"K/c"5Ɔ5їXEˋ|CZyn =*v޹+ԨT~B#!Nx(I 76&h4jTnwۧ p1H}m1k ;YcqKY'kWۅ  h4 )Gi }| Պ*+4@J ̹ep9U2nm~ӨU)=?9c^{;wic}[0 0 0 0 0L0) x8(BW>R@!wc4!@ WAҗch?i + œ @P2ZD68?+-PN/Lu?}ضA`#W+PWI@L83 ~Cnͯ~.?& LM7hT3S;dFd h!%{*5Z [rru7ްA%Ug˭--#DQ POBj8 t):c$IBz0 0 0 0 k0ioo*܏}Nl:&3%zm?6gVV?_$Yے1R'̊\Ye$*)o꾞bPdmRk\9F-w>BmE>s fpS1phﳍqfT<wm{κݷӞ䝛@!h>yl3VWzj~aft2 0 0 0 0 8aNM;w`.δue LEKykQxZN#"ZrYQ")D Yd )s| (h@>7ۚv~kX{EA{h5jQzfnmaaaa s\llL"k r.95ʍ!-sBK`m r}b.#[#eH=B?ݿ`v 0 0 0 0 0̳,7P"2xrYfܤsڟg-N5{Xjxy['\l;ʫ"F2+B13A=y2e/?A1jR}\x.Jp yaaaa9O8ar0ܔ1sڋZ{SϮwl*6<1GBzT˘q(i_OQ<4rky+Z rό1W5CĬuЗ!:Y%@I2ln{m\Ԃi||Or0YVČs1ye>$Asm 6^} F3.Dnsu JEṋVK<ݣYU_\aaaa0no!@d{ Tep"h6t1 #;?~՞@gըτF v jׇ?`Y^1`eru"%*pRL0!ghQ"><< &I$0GGM4Z 8>8:^" l?/ Qt9llSYHlnl 0 0 0 0  s&0bORʖhICs~&4Z:5Zm5DhUs+ah{v.qaSICV>yD[[a `V C6d  ]Εf!`t\}l0 0 0 0 00gѨǻJ0Zی (MD0))WYSEzA0a.;&+ ,S=`ҟF0<=y<ϼ-ϴu1]U)G >h0h4޾mϻOD0Z5$=1 JcxU "P!?Y4^zr-cwzgK /@j0GG|].lo0 0 0 0 039#R"a9̍DE _33:F^~Qe_ȵ0" H4~qHTP HSo$3rM}rL @q cH7P@a\nE#PBwPOCW@|s0 0 0 0 0?0gE% ?e}.pCo9.y2-u,QtT^F E#r̠VC9Q=bڬWqg+cAnqjׯC6 @՚qYJAٴ\[ƭaaaayD5`δ, ̿1\Oe2"{_%B,T2/2`ah5fR0&Mas2G  jW@ToU@DO@RB@MSl/\8jQE`aaaa`3R8yF6:/OU=Ϋ2U?[ui6Ж]_4m>5"=2>gӮEF" 2- (o3UvϺgG]C } RBj pk$/_/Hk 0!-ڒbvB:'?U!(槟>1V_{fV~ c[K[z1>Al_60 0 0 0 dJݙ5n35;7M}gS^t턀KRt0dRV=`EZS_ѢНijCzm! @M4_xјz?Ai }| 1`Uȕ94 0 0  Ç8:aA>A g>i=qÝBił_ƕs֣ш=~O:R/zÇt m!>Ϡn_0Yd b"dL4Y-}&)Qâ̿6&3{IH/[?bÊeJO|~Tخ3u έV?.?_96y 8荟ߛ?g9j3J=S &$,bpw )feU%ˍ4|)2h `BMxc 1-?K#+B _Һz+ρ58|('bֵi^[? !/DEdMPn 8""ܞE W !c0zkk lU|. ga9W!?ď.V^ ? 4x>| p&J 6}][?) Jʪ;-lI"с;*k+ZSz4H+j_PX l^e苤 k=X ck4cuZ1$[l$AIlľPKV{?2_feւQETTUV[ιսW^?}KW>!~8s=@Е3 9t>W.^_|8y}z-ttGp|c41:3h@m-Lv l8( *ªMDۨrW \רG]HAgIp’>"Q4,eI=)HJ?Pؖ8rǁT 1$I)a! <}Z7'?rcFGƒxιؼƄH m:SN] pzG6/:+վ=^_ߣ%qq. Xϗ>X;s8~ǭck r(ev\h.]R;?=3kgmܜ6qk l6OmdMF8lᝋ~Go̙0xlǣےx㸋.z[{=ݛ؃)GBT'}?3yUVCh E@?19XOmL5<|["̑T "Àn$LwLVͅ8q]Ld m  V̉So~O` ?w_GPCۀ-6M D (z*')Hlxi^TڔhߡCۀ={-l6I0Ln 0]}d8>h$b|xŸO1]HX U.ݱkz|2 >N ;xbn;$D;hsHILT D*)udrO=ū|"3(~6zP5JrbBjè/HeQ` lbv\d`mެ-EYj%˫Ry9&0;7 AAwřs~C)53knZ]l0ƪopWص'ГfH3+W^{ [gw6U7^FW$y%Gb[- vs$-vZ]^}uo;xjm };璫.;#_[ñP7Gn'ԫկ ر..$_wϑ'op!l0e@oUPA ۲.hd)c+ߺǿZUqU[sygBIF$moRO3w1Th}#XNXCdƠ]#5 6ScPm*UWj]GwRBLO#`t5D?E9m=JTAA,K/y_*Y?BLq}N}<$ ď>tqhbF]I˨+xgrR?-sxa'Гb_su}(7Ox(;xq4e>.}=33^~as>vXZl3#?wR⍏\*}~^5}y7/?_W -<9t}ݳؾ@:uT Dmf@PA)RqQ`l#П)ߥ@w}ٸ/%.}.DYtNaz+0ۿc6R cM0\6k؟t]OƠ ~$vB\T8c0lnܾ (c0ۮ mMy6`f\.t:MO Xi͗_)^Lid̪|cFp<[ 3ѝb ATRzn/¾z3 z÷W0[غ&0حi?7Է]m=HtWWxCPxd1]7 : Dʰ[IC!̀ b̶*ڟ;iY6kc|™u&cwb-Uh#`#@: }~l$| D m1z. =Q-o{6[=*U,y͏3fVc({ODjf(s}Nd>&TC6pPSW? Phfׅ,OL'u8jfvu4V'hJ.L+@*T\= ϝtz)#?xkzgT+FW.ͷLn[^iyׁ+3qh4Vl AAIE\̶a۱ /,KńRPI9! 5!yp/x: ɀg2}F"o:al-Ѕt\pӬy U`h\]_"޺xS(  K%(ǁ18U&VƐ2R/OEuC2Py=T1#L<ЮO$K ¤  Q*:r;Ҟ^:Ы`-am&* s%(;, bp؛mEJEMNM؇ܞcW(+b%+).ng r|$h_q55=|;M_ǣ[,}X7`Y( 7F9~6m3 595> IV?ij{{џ!?:+%>S e8n惕 CFlۂdOD(tv18X/2.1[Idfo-%&otoiAU,j6jr,+~gy5]I0+#,Dx:h̚+[XXdMI*-:&HPŢ>_ ˙5odQ@N6$2je$U@9e޼}` 49ߢ?->sODbVL;At z#G~ gvxnoeHh3H [g8zG$nGu>h wJvoxs=U1R;kT=+qv3+03llࡘ_ݸ8|Mع3hL'?wJwf.x﬇#}9 rMr9ue|Ϳ n VqCNݿ@vVPvq|ִ1p $ 4O,mylOgLgZs$NE 1EFT e0K$t%TX6Xr]mL,x0 90}nO &b& ɩiDm]b ]<AAb+W~{~:n0.l%`H81#pf.xKxlm ܅ {?ݣ ?}JbZ \8vA kz>MC,s}ߥpw>*KIl5aQGs'<tK M w3<Ė56Ţ|*+8;`-hU _wB*ܘ8s=+>f :p]&Fyu\TJx7Q)Q]Q ||O0h:#e/_8s &ı,8}/|=>}.:9Y0y>( ڌH1 *ܹH\fQwY#c9Ej_h{*=,}[Ty[RtFF=͙ VhљhN療ı _<8^zhGf+兔 s}6Zx|cFL߿'cwXܢ /( L"8dy$^@J08)  ZyH߇K>D>ȞS>53©*>@JQrjDFP4V-ZT6) >|_s-Ash !jY6 Du_M)%Py`ub?k8θ=_Sl@@RUt^FhiѹJu|Ec(z}b&_}}0 ,Y /Z1~M_1_W 2=3VWY@WWu y)߇ 6tU.CU*02#p]8|iYۈ6RHۇΝtS&ZB} s9_EJM `c&1D"9pWص'ГfM-38z&)%އv`#ag?Dg ç=;8`W*K0=s387>{Gm|$mL"I~'~. o⽳>5ǴWR^HE;71$~^s9kPo^SRB9dfY.Lۆ f.m֮lxa,}q5OxAJr}d, HV-r1-~Mq^_~? 6gSe`}s~ x딋Nz6 s<HXR*ܚ)S8?C_Z\AUس;muq>!Qq\? <;}"n*0} 'xf'M|}7~v<.d |O O` `@~nӵ]_?<53Didl$ߗ@:@<Ā|@']|]oġm&zҼZ5uo =@ HT.!VD(2>e)/?׋JA ,ig2g43TAmm Q_E)q-_)0ԙڡU:\W KDF῱%50209F6}TPRW+Z*N0*GPaeHσJ!s/1'K%33gfR)[ւZن HDAAJTRSSxxK{Rx@/{[u)0De@a$qz<w1Wx|P NXY \DZ3S=C|MTJR!WR8w3] KI}B_{@~zn~0 `(ۿS~J„i~_y!F[ \Kl_|X v¦8TYŦ U{TĈ Y0a"92[ ĪFT_P@pnaTjj5]K AA:ӗg`(ejeE>{=f x nCR$uVgY,lC1 |@J=W u߇Lyժz(BJrT҂l BqԷ~%XP ez/ЇRvnݭ;߰"IuTg2;.jU.CJ` ۫9gXF݂ u+5ki05_|Eq]U7 0ړ[rI4l xT*ҕ++c/^ bbӐ\yw)n͔qVggX 09 TN-/x6)0^[g@ f8-K72|<3{tytB*ܘ8s=+>f 9iaW @!lۂd{Y@d ch>S'w=׽7쯚I=ͧ{yFF* x*:PG$7;6bF*.灙&^24\Tq y0`YYX_-*'YjV@%E0;-HD -b(}'&`Ì>( ISVU,cH&'AqsiNʹ}~"M& ,xԋקlAb޽pkX2ݝT9Uk9\Sı,-:v ;;sVV` s{Ll2ؼݍo͔qv< 7rP= KblD"?+?S6 xvH+3;\Uęs1Y@W3?ACDmH'ŊĆAcF8L=AAAmS} c EXɤ$K'|R)]*.j6|ohZ. hpj;rnn )nݵLڌN19Y-.޸m(=\AAc hZ.>Y,j{-}xvhbƉ<t`ݺ?FAmעxNCU*B3ZaaM( ΈUR6c#߇,b%H xa2 D<- 1=(Xd*EXlԬ-CZ`!0麐G蠐>Y<Х}ܶsN- +j|jvm޺(۟hEZ|Z˩vtU*ܺ}G !HGA'NR&?-&6mb0Hh2Gy8u5@,cc:v%"a$8s'2{C $b$C U3C/>ba.lD~>uy≇L٘†!D% \plQbKH٬r]f )qe.dqNC/챰kc Ztyt p삏>*Žuaa6s&CtwS۶12O=c`>x"GԅR ^- pZZn  U,Z>$Α >ŋGo~O ֫ -N=UO1͛fm&\>![Ђ[H'V.+0s,]AIm7,uއZZp )=>f8ma#JJ )qN <> ) D:_/bNxt~騺Ñ=Jl_k`lcy`BA ÀL Ȥ-, HۆJ`Ų~^"3|u!4>j)v[LT=ېᩢREW˪5+.ctԐLVm9HY"3c@gzmնhf;1peF4k"YJ$]4uA4B2ӄR <yeU]=f6Z D~XH׭zxlǶ'0ܫ/.i8B :犫\H]yC iG, vs$XA 11S38-{)t_񀊷kre.V31+H ̕Ԫ+.x8z·H`bZ=i}FA( :H*Dq\.)7D:ev&eȕҥ],R=vymr' Y\jRO Y.kA]PAtj* r398B߫zrˈVi+LF7,&D%ޅ<]ͼTv6Y@9d33Rl$nc .nY# (ǩe%G ǐ Kyr=Δ*!fg\&۷BV NL$w   >ILKlۆWҒq#]S_~ RyP /1Se|n !'DQph:<h lPtǦM[R_tNa'S`kADA_3v OokC_;B0vz{gp$>&v vaU˯A/?@ gkٝؾi}v +̖bp`cǮ4Ww՟^/iڶ9G2I.zLAp@  lc5 !WcWnY=cZuU"M^B.WD|9p_~F:9G̥I<-W9z J״ipl]߇;G!)Q0ұ8{y 'εWYK_fs>v`~&b DgI%(dQG(> f}c!\._J(˜B2EH$x;Tka2 (Q>kaE01QۧZt&WJA.քx+[)T{UB[FXO{j!)ϊREJ[ 4zz+߇r]eZ1HOLOCY'γQ(3ǩo5a ZV>\n Ep]}ch!01VJJlPghBF7 RhvojPPt]p),3==`]UC@C[m.W$+q}xbbrHƪ|gRT5=ݘζ^&4=! O[]ي,:ϫOxQJݣ*~>[q9~u>{kv?k__Ӭ}tgQ@m9; T*k%c(l1T}0!>@y!D>H N\@dl;6X#})Er_j-pM,Ak? ڸ{"^"oTi{vhϲT8֏<k+j.7X325+%0j} ?3mtg>3FM+$7T ~#ˤFT vU B).7l_qI 6}}:P磏P9|WjA ")}_g{RĵP9|s0`#xWeyJ<@FRA03><u@F-KO,@ ҏ~13Hǁ*a ԉc![V0'&aͯ=u +8T."!=wTed b7K(mKJ0m0%()!s9rF_ߟÀ:p`gX!g”<7of_em5D\^>$2d7    ⁁@mVN\qxNܰՌ{J/똀_gc _{b'^Z5 ,<;޺F1W?\W |R"ۨX٬xAZUP-}9߹ʛoBd9:Z;ykhᒛrt5@cFѹF`LOH5ۗZO[H)}]B ^>Z$D$@R)=B|6$ e(yy$o,c*"iY:rR ,n݊7^'QKł>ʋԩ{<v .o&۱rTOAAAAy|[cB    ( D9zTe RO>YW.})Y2s@ ݺqAeA yPˤf؎}mnKhix.Z0֬r]ȼ bmTe+ӭR˫Y.Oܹ{$遜|ORUg}]4>$|ƍ0zzVǭ"\_",O(?ne>X3w3-cY%~mAAAAPA3 b K߇p?GEuwT^~gsP ò` 0Z6l$ZFaYz(UlЬ|3 ]޶?>[oFa6|WjH3y2 vR=yO җt ӄrЌ5&OCY1g }>tM0ׯoiHgakFALNܱ<\^+ M޸_`֭Hۥ|JAAAAA=#LREH = sV׫Hڄ}O^RCC2!mT QOMz(8P(<mu!d?3ubch|?H&j+"`$*Mbԍ![6xo/߇WWceF(ϻbcY,BV*H8# pߋws+)*jsZJ D8TJ|#k_AIS,Z|<C@Us>e~ m50ض \Я/R_;n?dlUv*.GBw׎uTm˄e+\TJeόRgٲLVWHXv7@8RŃB^\Hvgpw߿ISL @qH$IEmf?lk&&, 9CCm2R<E%}+C)͂͢ YG D.sNEຐR"{W(sPgjfºR.Y܀ھAP{Z<嚞ςט(rY}]oh *uk} (σcU6~<\?>19 fYH<(mh.'k"n0(CEaY,V1x1p fj=^ f#Z D`j 22}Ru2 r9ׯCri"#,3In0FR@>߲A Am#a0rR ݰ֭(Q:rDd'` y@-ƖS8:`sa@Ǫ?DT`0~9pJiK61?Hg)S}HG>N]ōp-ق@gb0NZ9J}} .__}G+m߃Vex6ܳ~.}|<"Vg8?ǝig@wE3pubߓ771irzx ؿRajg1sVz&~8N_lc@2kHR wo ?X&A {H*D\&C4LݿCB([4ʕ2  Hk)%|εxx Axl8)rvf"{p*,-DžP\F[+`z@rt .}_ veѾ7j`Ʋm#kJ-Gbv<]U.Ć6xOX&$1H N;F?;:p,jPpD(Cʪ ,dBH)&JJX63qx_aTJ_qHgLgp>0GG8p֭ƐRz~5>MX˲Ø"ĂvTCP怔nvczD^U|bf"՟Ab^68aJAD>_v* E@ucYPCtEPQ*֭xzh'hqL $Vo?3\txD¼{ɤ˯`4z3*|l-;:TKAFNpZɼЕI`m(W |MPؿDo}'$-.c' $w3+a ccbNSI 5qs~RÛ vݽ ,ݶ$2Fh+7츟?fV~61ygs8{5SEm+P3@%1[@i`<1`K_ ұf+%3I<h'V( !=lmiCܿt]89$xCm&JV3,*4us0HwU^I4R`f:ha[ѢЙ͛0 O$PLu8s9JВinn7 iLFp`gu\|8æ5eq{BX;3ҼwiL->u<.{ۥ@*ӂdc[[na %+,ؖ6(@å)26;:lnYuˮVJq?0L}݂e#1plgi{.=k q61LNCoWxP;~g&eڰ[AϩS6EyrMG楓 #a25k!VW[k ׍mX L*2Yќߠ/"%YT6};NG|[Ew]u|57GygJU,ޏvG=ӳƨ}{66+ӐL23;p Z#'' o&B$춶d2Lghim:~ѹ:DzmA9UuT{tիX ^~ƙCQAP<.vK|A])*ݾ=aZvjn 7й'^~T.RiiTrXDf I8H&ѥkFZ#ikn"/ߏE6Vs3zAKk(_|>8mmGF-h @yT꾮88Kona56tlrDUP#乏?ϳ'N0QV3:`RfIO֖f`0 o~&Q ,s qs=#ۀ˃!y _DV U*2P80O>K:.?j`o0{/fz9 XkGW78%[N]ik| =6Q%k͏Nƻ׭m!U6u3f㳡+ɷ*%Tdv%Q#=gk|'o37叧nQ~Wwi7pp'o(pm0dGW~o-e >А#ArX߇0rfYn'OF5G95|B@D"NmGɠ2Fbwut`Wjz5NLPOPЉxhn0 l6]|nq>gCACS[<ڥV2AX;gp{pvnݐ:ŧJ#h4af2{E޹Oyqʍak5E?d`ttϿplkJ7J33o]\q#;a >zzb?K uPXG>7B a3>1^ƟWz,}m?~#\D%ֲԕ|aN+޸9Ow9c㹢R:QtΙlIz͊s :ZAڠ.0[狊ɬ E޾'q8琌Jh{+Vmk5o^,s'8_WWnK sHܶ)R07֙bpRsE. hN ~hyֳ~ J1G@2x(DI?,Wl}FƘd QJ^,h#Br\92"Mǎ۱c,}*3kȿ6vs3'))^GE]|1md.Uoh)#nW/QU}iXVō?ԅ0:'~H%Z!ZyqنJ!JYˑ=rFQV[n*F,znѸ/]?UXɶfoxGcUz7قb`R5uxlMcZ>9g166Į~6}. #/n7f('%o_ x16 WԼ*260 `0|b11:6w|xt;Жx _W.cRtC>_pi*EgjnJG9`#y BA&GQ`RH)ljǣR(GNMEb{`3{j(HTqA)ARQxsXIDVSӪrr8]]vJERDgfv*HuV #'&IUbT.pȉ TnߎEURFZDbQ/eUNs3O| !Kҗ:#L GFp{,{z9=]ђvJA YvOn_d2qޥQ 3ʸք׍*15R*U!Tj,7R;۱N?w!)љ 2nl^B2':,|!J/,˶ͱ{o ,e^>#bZVXOXOϲO9±#gjx̼5#j0 f`@xnxcw6 gh:>rF6{\>D:åÜwkI#{\b>e=iaɹ6M nKׇx;裏9KϸTv(;{_!kpi ijp͡69G\1/.bjb=O8!Uߘ{社`șGBSQ]vGf44 `0|D$ 2ݻٳ{)\yL/!_Kmcᴷ/ffP+;%!r##È9o_d)]woy&CN۱cHe2يZIIX8z}OX÷-g @ IG}Ȯ"|@.''Qai{vK vGGt)!\FZkB j3=i Dc] Ҩ<4Q/d28QʮQZW*ChqNO AܞD"Q3jETeZkX wssHJRX -P=Jjb`pa'8wW*4T4*ub/D,-HuY1WeY̆/T2I2--[Zp[Z[d[ϯpx|O֔Vτ*um]5WC.܎< a˦QPͯxm/N]>{$mv6YCՁ C D{mRU~\5/>H[4MG]s+!n iI Nٳ͡N[^LјJᑇzKuM~׸<1Am[x3>VZ#GF*YNggMR# Z狕4rl`xF#*\:bphaEԼvfPR#s57m*- G*#DKގۋH&ofSjj*WrRb9d0lFtMGƴmn0 -Ą~w+Gn#:MV2_/'a9w+ཫ!MI]6;:,qdxSn166ζ"_|aoOrt4k ޽skT9pצӢZ"?é 7e{k_am)rv6s9Hmiuڴ6,KTw'glr_|pn1;=>ɧ_pmX~ 5~qgxJ xzvm0  !XBЍ{tlow\>xӃ]]6|;mș+W@쮮%6*ǿr:R.EYǙ_ewvFZ)Qۉ Z?D\A8؝.8A6ށ8857Fs(ÿ|]]L ڊǍa ya%'tX*o aSd|?}^Ss눑JߕJ}~z8j<}բ!V'!@J>{޾${B~9]]qVqnK?9ss41OuiH%R*9sM#\vwi.9_?`H2Gw9~;kxk3ݥyI6DUQc ]߻yiɶC.Z  C1GH"xh"cc{;;q}fCU,&zJvkGeQv.oh"-%2'?6F>پıc'AV,͛iܞL}YQ YTXUU#DU_MX8Bplp`J;xJEt`r?^)Y5k!jnp`pl 5PWCfej$NWgr:2,6d,%`W}'xb+i`_=|l7n|F$=_>j` !Y'9'ǟs8{".> ޻ 4&#,j9buqo"Cwllm s `0|$q12:ں%֊"o{:SB=u@ZA:]*ѿ./K|pr5=*m됒pvb:MJq¾^\.MJ/滫199L#b1}jl/X|*'Byjlˉ*ǰʟYL{3nFD{53B \wvjܳ"#FL;||r/us/5Ҩ껫Jp!2P6U)_\z2l^,!H&Ɛq a7o/>X}6JQؐ=CJy#WC \& 1z,f>Gv)5Gs<i;Z{mʭ<{l]K)ɷ*pO[S$cMhR ҄ftZ"\Ix٧<=5c>]M/k6vh;kM(arV9޺.Gv:xN}J+ٹi u7T~kc uI[?\nt4 ~bg6 0>bފ=˛ՅmweliY\g]=],NNb75!R+8?5E8eku(9+\vwj?@#(9˪Ɍe {GaRFUbrYdmm{ז}QlFiѼ>gV^no;z)cZR0@S:BFԻǺPؗҨ1^ƟWz,}m?~#h@rN z׆%y!O?rǮupg ѩ">00E?Z==1wlm5g`JӜr8ÑIO__d|ԇn,O'9%_lkv|7 .':[mf6rg72L O5dJ<7֙俿#6M p9mc 0 0\~ˍ+'I=zޤ$w޽ϓFrj~e "5 A'?QbeٹfW4ڳ,+r.8_ѱڧh,ǩCY~(杵9.;ٹ/~q,e*إkg/Ԩp4rfO:ʒF MM{u.!bVƩyA#GHݙC vs3@FUȽVSG#}JbRu]5sHUH"r8|Ҏбm\]^2F ڟ !R=_sӧ)d2]]kF4^&ï}|M_:c0 `phH_z7ώd;b]L=kG3s ;W|~zf|czmurj 93Dv<}\Vz#l+'$gnݩ,|/?xUe;}.28MxGSBP!_o繂bpRk>?=ez'<ߣ1aeO+rvLNIrx2嶋QDA\oGK˞.D  -bhxdk^A)Q*'WrΪkJ9#[Y˙U+N,=j>raNb EuVv3ZcՔl/]E"ǨHWe|b1:=4*E_M5P57DPY0DAԎbjJ ]а}qחŊWXaAffPv[22w)0e+\w>e `J!E#o|nC}hkm1o `0T.^/?0x<^ $ciQpWCgڴ'cFk.? 9ZÁc<,^X#{\Qx>{Rus7.]=6)û]\o}er t^UPye\ 9_sm(̀K! l:xtK,fVJ3>sk;s>{mx:΁ /zwȻS3t'qLc}ղi`06+&`x@yu6|U*rږ c,kmaT߲ЫHFj+:l1QL4Z)G6Aq7T)t: хBpksp~bbqQ9`0 Ö`xdD9ĝٚ㥧=%inbM}ON¹R9*Ip|;B^2UO֚|ȍ"W瘜).3qiMU9K;cۺj.\y8aRYYiZ?Wse xHo;|˱.mبv0~vȕ4mckL.;G 4SkynstOGܚ 9zQgr!n iINٻan.d6`0L` AP)g.\wK;ïZ#xx4bR%qVWi#%mc/QݠZ##R%+#lmrս$%4ͳ=Gqp~㜾/I#`0 9w//?밯'IS2*I2n睄@sV{WClv[4'Vԯ{- !\3Lgoyvho*X%EP/[+ n{֨sPM_Ek@X`_r?wLe.{w5v6Η޹ps$Ķ4zl:lZSfJvfmy5LFgGI,d"a^?<`VZǍm d27O_y:bVqyx>6 >bR4kڕRs29k'=vmiJZ6}!q%O iL{zZ-R cLk;s{eϾmDll[c }> In6Ƹ\Y!:<}0NoMC!C=I[-<6\`~ MHvt >s̥9%jmm`0lALА#8! kԟ얅(.F5y[h9Kմо* NGrffsAm?aimWuo^@}!Hf{=ی P?ϼOrq ": |o6—Nzʲb1')pL?V`_fz.JKN_\¿ˁۮin e'_i`06+޻E޽ⓈEvm3g-t(o4iz|ma<}y ?/pm0UIv K_ a3` dܜ°ݤwZ<Je?l^Kj$DGGl,ZGsasH|!R)mo|5e+ğJ8c8z{⺮Yg?gÃC>_byѤukKy}d0 5f/ǟ'žnP\Q'4Jak[: Ua>O049 \hnϝHnQZ\J)Os} mK FUZ>jQo[&ҀB^ITJRܸ.6քJjTT>V BqŴ(dVs'a뷵֚ ԌM4&g_jiPuZ@("T g\>T P ̜ usc:9sOK ŇC9~zftGMKƾ=[R|FB%־J: 䇧 \,x1vwZDurֿ~ f MMbhxĔ0hW[a[o{6F5RzeѾ?_Roi88XR Kq+vYje:M,מO< b1>FLFgKh `0VOPЧϜO_yC>u,Ƒ].s37}IIއwB?r!C!-)MscXzh?$Ƅ#ǿV9֙)pSWfNGh;>K+ fHulC Y(䊚kC!ق^/tH2a1>5: x,Ķ߻}|#G1Qrz70Ås8K[+bYhnu;! jpk4$WHv?TtHLfºk g,2Gw94g|Qr?ǩ3?tF1UkM a*il ]GB~t=m V-BfC:C,5 \fbn4'T~b  D|`0,0DrVTm4Z|VFV,uŰW!ưJ$}V+<'᷿5:;Ǎԏ]eo aL?06tR\\dӔ {=+!iiphnt#Y59hivhoY#ǰCj:k/,WP]$RZM;\NmKÞ,H>/q9gӘjg񙀟Ɨ{z8{+^[R3Fuy-R,d om׶(XTա1頩P9Oqe.o0 #&`ximi1U Xkml;Ͼ\n[v,aԨ+i-`1T.\wk:KspV3F پ}8JB0do{__MF[)% ao@)`0|\ӻjs]%X,18MKʦ1~-wgxb,`Fm=|ABs/Q X~_Gj\kRɄǶ + Ų sKA-V#ushMXwgePSޙwoٳuv*{.=  lw^#|QPn=szGwԭAm]M:V,Eߧu=m ^{g?w_zg\Q$ +@js tʯ*`0<̘%Q(R5ս"rm2 la[r|5ײp=/*e/j mr[<Ŷ핿/T ])Zu[ ) gfhsϳǏsGʔ0|xkDxhj\ES"a6 amXBڔ`Oo+RMiYSxͮfR ޚ?p\@؂d%X.=[~iN%ױض@ tg[PkBEH2՞ڠ 8m[ИZ Tv M/-0`!y=)Rj bCOgV mAsC !J7-AGs=[Q~ )Kж۱ iJ96{kן/Jgt6ИmPwA)%ɘlk`l7J[[SrfvbZGKmæmcy뮼}٩]jm#ʎϿ>[ SyVyvf켔Z|!]7U]s}iGQc0 0YeRk9688uZK8HhQ{:fZ^[|!:^<|ueꬑ.{Tί5F _a9:,_8GKYqkX*S}Σ9TQI˲b1lBi^&S.͝9e&Uk rv{f-33^%L8Ϟ8oktuv\by$fffu.֋q063 `0 `0<Tx8B`Eߢ +(@ eY))}<%ײX-,8eEhy9}@z  5jDEGXUjt/ǵu .QhBaa@#-"+RUC5mଡ7%,FEt.1 4Eץqq]qVl- t]>j?/|#>2fLMss(ZJI247`0 `0  8 ɽuVvWз[.`>Є8{[kw 7:7B#5Z#UmFp:8j kЧ|~, ۶H.jiqݕi@VjFu뤑G`͘@)Iuiim!Ҳ䱊mmZVU`O5ǏWŴ0c'`0 `0 Ãe$0lZ f`0L`0 `0 `X/&`c1#`0 [o0 `0 `0 06 `0 [o0 `0 `0c$06XBhhR Wj+5G]Dh^>uh%T)B_jPJ-QkC%>9&4T.rݥOW,B a` `0 `0 C0&U hDBBV^XRqD!*YyˎL @>t\0ˊV5*T.U;VתֺrTJQ#V=&Gl\.:KQ|OaOə^R 5l:0 HʚlTHX,?}а ȉFֻBkTH¶iij2d )k~AJIj D*g DJ coX(3CE(AJ{BT)~ QuuhPZ#;hLdkA( fmc1, o8Bc[v m]}-ZZ;A BmkAt0\h=];K Bx F)b;v{P*ѿ=-k \4.;SszDzqLj`7&`D*lDbX 2-CX¶#'r+= J$߸‰{5q-I캖KPmƲhd6+gB5B($- q약JEN|<a+b1Xhdۈx+_A}DU%!)JEpE?^;-e4yX[! b-hq6!/d8g?{=f-7 a2>1/^5#Nsm3)|?Bp#1g_,LOy\}w]}w ^ )Eɒ1FÞٰcFLó GlxD;k3^+_lhH}6:ߑ }UF~":dW{/?DaEBR8㻉F"W=2517/S(zLu2捏uv}(!$YQM*  $Wo38^ԅ!n B'R{DlTteSڶMh1^w 2c8Wt\2VB18^&W·:G_BDl]56G鸘́) ||yLy:K]lˤ)/tj*Q(9^+8}ьD=8uMH.A,1>e^@cB,0 F&KyZہp dX^Gw F3:@D, dY,b64 RYjfm' cEsˬh68i,0wڼx7 ۶1 cKH8ΪS7$@sFr fCG}SSh굪6mli"l6|Om UK,g#)mXO )P,PH&5 öonl¶xJڥm|ǑI$jcX4dJa 2=Sx)H&j4cdtT]t?'zm GwYkrfJll_iLs)%e2g>, * CJDyUpHE.|aa,Z*H:rⵙR=dcv Pv@a/=/r ܺȰ.z~czTlQ!$zBh. u2ͥ OY0g/Wg,ls}rT|EUdvSjO ;,OLC SuY)TΧgnQJЎ 3!J>Ƽ_$tfsuպi8nRdq\X kA*K,2Zך7WR]a@:剣]X'FSGtFYc9-yxk׭zk~Jj/vg姵jU_vG5HI Kɀ--jEY -w Bǿea#mT #"̎9 ]-0Ӵ/O`:"~}G"vo!2 Lr3o`pH56ZkFLMMg̍kiӑ P&R` '}]VMdo&]]IٵEN pXN*P ]Qْpk<c%Zӊ&ň!XyaxZ$\"CW["$P{RKmYC_MƦ5N41l=/k>؊IڂRD'+sVcE&Ũ!OdA%/8˜4-9«1,/@`1:ho)2=gnxSqE2j0Y4j\`ا3%v e~s%;0LL4b47*㷨Φm[y!`t29};uPؖd XS\9w'j3p=(zh4h6: r]dmu&zakU9* 4͕2rll o;+1M3,/0،mjy"VN|g|D"-u:c*X*X45h4MHTR:ſ}n\f[*KGL]vZu IQ6leMpP!-O^AP YVJa&шAgQlR\U9V'e7.g[ 05&Z bRlL渪=C>^r:.k`4f=6Wo8}%D#6wE8u݋.&I̽@cY*4Llˤ:W^e`;@ęu.fb&&uVJTF\}.y\{Bӧ % 7\+vٷM[׋lo1CXTt>)sOā{-v􎬿ȺL?~u#ev CwEcR@,B-ک0s~bZaXk?Qygs.{}{,vZGlT>X׳dx|KLloeєD,ixJl6ϭ_gGgйo,O\ n]mX׊nnR26UDvڣK'vwL13ެͯoAϰYt.=><Ϣ ئFtF ISbhxXI+Td2ƌ0i5R~}F2bb[1Hd>O͢| #f+ a?5b J*%d:vp]9عo|;:p@!Oo!ZvL+*h5f?s#ໟ#J븴Rۏ˜鑈Gv8XU i \} Lrɯ|!Jv ESayئ~QGjN7/Ƈ7i'_Hp"P\QX\~m(x^]&Ȭ5|teu['8;f{_k]WgLʀ__ڀO*/ufoFlRRlVfԯ8Ħ>hI)0 Y~r62jlUk|Mwi9TV@h6lRUS%ݕɬկDz&&6s͛p bM4'(\u1 Bf2[FM4F1V`#xT3g"%BJI,mha M-D":!F?)/o-O1jqsB#WFRK~vyc8"bىuBb\gt"߾{?).>e[*mxDlTg@{a ?>U?r,Lo5& `:L=~:)*ƧP,W5Jksɇ|PYG '"<,NC˜#*59?=S& xhCY)E6_@W?쥧oF/x쀃chi],u,)^?W elN>ds|C|^0W54ML4T.kc{R+%2E32@X[hȬNDzh֦&b(i.xKۨvGj b9=mt:X̉V yX@ks3d۲B޲G˅"0x4fG׿n@qQ}|.bBZ[ZFs0:6G9m_{*SNϏ9%uSW]Lv9ibZvb%Rt ]^_:ԡ)%>Uӻj֢8{k8epdEkIJjkp*r/\hgiIWt\Cz6B|+.eW :\Dȭ F ]"< Q;Qg~4] 8}U 'XfҔ4[Fs&I if|>=H`uvnqrG:uL&V>!6HUmy+QQ{09 B`67o371|FZ[D] Jȩ)D$ގ0-#'&hR_y1 ]'AY}lq:uqU8vk4}Ąz:)?~'K: 3ɕvu=[)ns7=|_q(*GGתA._$g. q6(6I` 3zkuU?/P\uBIbpdMGՂPt90Źk#\>#Jx(48"xh44-@2 kCgB[=^,eIk *A@0hom!ZR }Ack+ la ljBD"[H A-%8MDV_~؆Ja*>ukGTڐ,.RIN"?,TJ8xL/*4~`jjJ>{syq>ǿ?5=Kŕ~Oz|.{`Wm&E29Crwӗ;L)L#{t6!CZ,6 K>dWɾC6m !*JF*.#4̍Qvg<;Fg ZԹg8bO(fPdXJ)2E.s0Ct4DiJZk+.rphv k5FS_tFHSbhxXI'W>2_ظ֯O9MM4vvhlXAQNJ9;+,ˡe0MD,VJ|4R4vvѱ|y`YaɈ l&ds>chkm! z+C;5S*{No_y[=ٖc_ì_*e>4jҘ3NZޚRqo?0NSgZ ΕՎk]dƐϻ\.HؿdWMkI .Y[y )Y~:+CMY ZѹwKnyxAݡΎ%k\{˰+OYthIf|:džh-e l1hJ1Qi 4Ch4[T2E&ՆX±TBd,N\nR \˅5WA/*Br_.Dhmj"bY izmD+ Wguب16C-)ibbazG@d$XeD,Ew?G>67J$q{{V@.~6;ӆa\wX BVmUK!2S>LٔoMeWuwwԤY dBAh4o~w[7ՔkOX鈐Ng8Ӣ~h@umWϔz'=;Z RQe: b`ĥEJ}/?a1YhjN|7G|8rG8bgA*&p̹635ժt5\⧧s\ҞR|$]1RbNpJ!.:'e.RqlŮvtLqWuf:,+\--<:T+*:[kQ(u㧧]z}R1xlEgA2&EVPFofHVx|-t f'8B_Jl)1@BJd ]i@*G|p5LJ 4D?iû$c۪u4#qTwY4I._-R*]޽`/Dy8M6]3Y~-*I^=S@J2k:C6X vj /9#BujC>Q>x͎V3;t=Zk44-D[[`6kP(qffe$laHD{;x*3dݲҩLzȷNL`z(_e0 &˜n_iظho'0 dt׾FsK45kMh4Fs8s_;/E9ؕ X׎P$TDmx͡.d&B;TNy~>"ibas`M1rZݐ-HT8";L0“ٳ+kg}8|O[G8˦Asʘq:5YϔHxtűv`Nd9XճfQmxMZ[`zG<1xMwi2SBeZk44-F2 kCh4FsIFl bQ_ſmc<~ 6㗚;zRj@*2yɟ^g g[iVɜϻ9{@sbr|ȕbzwPRQ()~^OnDLg9~l!KyN_ϓ_Q-(nP 05yN>(AS<^'XbF]tVJzI7(෾}6"SFguᩀ?~hN^xNYh4l1Ujk4FaА;%Flj`h+MZ.bĜj c}ҿT4EW11-y_%s4':$b)R(L<>^r_s;!(:uzw({k[Y|'А0fbS7\͓ T94guqR)p=dNrǟ3ͭz$ͧFiJGgdx<._r;LtֺnZy˒ei\g-G̣h4{h i'&!4FG444F3m'»FS'"<"]q:ì_~j((ьu.\F$"C#άCX: (-Vv}1+}>Q(IZ ^8jat*_9&V*u97w>jrΚz,w­Og -HESuVJ- {xl|' t՞ *+z=.|a83ݲdjZVC6FltFEijlC#JJ,^-BywAJKtiS)[齫ʽսq= D>g1>kѪPklCݲ TFwcL[l475-J͢d2Y5?KS\fdtLvh4󘚚RϞ?~ܸv&g8/:eu_gWRqrWm&ESٕZ5(=.N3N%+Y!]ͳ+#3<6* FGC'i*X}VivűLOS|ͻb9=f2AFRb"kuGG FGv)) QlMC:Y\.Ӌ}&&&Ussvh4T*N߾ z.-#&hI3N,g]<>q}4ݝ&;Lk=@quyNj%_x桝4fu Ɛϻ\.iMlZpNR3T擞".ixQC;,:[̰tD^s{<.fl: k:JoW$!x۴$uXxKp>Lw4%judwh4h0hTLLLR'mݸ{²pH=f Em4SS)& eLT>O08puC,gUhPq"f{;ѣwMl`$v6Dxސd #;CSB8S5;p)ޠ6ݝm:;k7:(yM:lDd="eZҊ/p8â-mD\.)%LcmOpٮ_Y]/>Ⱟ okZ\ ؖN >FlmtFinnCJmtsBRx D--̂. -@D"`w aJIkS 4e `# d0.^D.F$Bh;g6r9oI06F ";wb$8!58E4lBJ瑌hij"`.s^<(ֶma]lCp/_PD)i>ӼOJW45#AkkA,}˾&;=ҩnG{Е.tFIĜSյ5TxSլA*5^Ծys7=,S~,-,7|>ʡɨ3kVkUQc?)Z*ZE֢wz%qG2?=5ؿ$(?r T {w)a|(H5kOh +붜3:~;n'Ja,uU3eN]NNTL%wTZt縊ke޹0Ow/cwI2FMYGUO+踒>-jo)zjieTMKX\ \qlvA}r>]ǻߜUF.\SmOkgQys̀Ħm:hҩlvsνWO05EGGI~ػv-\@ NiuMy4%b[洑]P5YD =ӔϜ!$w Y%7\(:;I.<_ІQzI|8.n[ڤ9BJȡaN8گcY)-BsԆh4yJ%u%/W˷ppG CTl:0 ESH Ύua2(;`ռwyɫyKԆnb[kkv2o_pVNpLA:Yggwf"SLNg4 (ɼ19б:="ӊ h}SRm+Ls-.y-4;#Nyfe,\~UdK,yJs7DlKFx!b's3VڞJLGj%BajA[PTZb*/ e|_ ǙPgR_7KTXKwח?*#ѽ8"|fjقKnwZwshL/ciT\,+2;< yZ `rZ+}4)@1[z|+l[-YvCPSJ^yqm23e>ꑌɇl;ML:~ }[ ڣRw~%4?~ C{݅lBl8@\/"_Rs6P}=h$:@yH$bl|B~}0hM LQۢT*亥](޽Ɨ^BT+6s-@Ev~䡃u;'ԖE2 H$HdE5dh4̇O?dldA΅Dž^HSkousMj_))-xő[.,9ho22b AɇVf@ӏa9x2[EȕL,\Rop ho4=-\EmHa"בJ1Ģm~]@!InTc-g ם󖲧*? ;LNI=,CPGo9:vBϰ 0Yw|Hf?!``8KTykt?^Kp X&"]: jHD>'Y׹]q_0ાjv=۲-e"f+E16UTF,w4p7oպˏ_L.C|_@dw.~_]O5:4CC}88ͭ=^a٘YyuiIIRDcgjaHI*l yG02s0c0൛ !%uIE454Ɩ=R}o09IQCX۷cӋsHd&#geGWuDVk4߭JhhH11Y:۵_i bZ/ʯv" l9?L9yCح$/߆*Y*Q|uJϓҗԔXlP/ʯkNeU*r*h4BScnG&NjU;F:1V50ʏqjmT[vbƖkì; +=v[)C'd4b8ƆYNk̴,\bQTbj]9oPaK+պL9N'k9רC'za^I^'^cZju6jUZOW4>Z[yy7ĶCL=/LecmɖVOC) 2~K0[ZhDՇP Y,T% O嚆D˞_3ֶ`t?aS ] Jv)UXB Vԧ?ìeRg:_X8Mq1g^Ɣ\gKk: OI 璴o}/<) O@tFrK̩_3MmHL <}}#=ď~X^_ލy[Fs}{.=wred&H&XmmQ cJ߹'~GywO;H)?0wXhn&3?аڐ,Ixl~:(>L44VFh4fR߽ cDlrbTs/BT@1!$3CjmU,~Eg0ì 5R)jY6~=s&f_(z .qLښciԼ6`@M5/qAb]{/'S\`tFHr]L)¼۷)>" ׷orX>'$FwE8Nhm۶26:s6<#9|CʢmKԯ(9wyC2o<硇oL禵L+G<ˎ.FS/XT/V5Fh( I(Rfx ܶ LK$x!q;vsxVg)!Y}*zaJs}'`E1](ۤ!F"fcY& >^igKC׮Opqvڥ %tF,GMf `bW]ɽwڪ|p}o477FJLOSOH}{l vwl4D>;{i ̥6yQg{<ί~ѣw|TΟ'/9|x 96yvrb-ݿ__eWe
u[ٔǽ~`ä-e#@>4>Z[{y)Rd2x.aaRsimT \,y; >ɓ?S |r2,ӃgͿ:m@J(Hx>{y7~GFF2:Uh4fK0[Y}/>R* CfWtOO'V+JE li ChFGEt@:w3/pWDBͻpCGKj ڔ.s ϭQGg Gz6+tFDD&UEji&Ƽ)xccG9ݽ2ǿݸI嗷D ZǻԣArd̬ q`0c8C)JhW,X[ +6a^zm+zWKԆHq4 0>>賮|̺}TcИA|^ewyk^\+9k7/?,?KCT!%zS( {zhqYڍؾ}ry}iR-vVbX,eS٠5J!~BC2TާZSlW7uWK!!98NV5˴HzKly81 \ yۣssI:ű3β>]?݋7C<E=J&n|N6)&0 [C~tṸxCCyo?6Q3$À/ {=CC3U>ᇔ<ohL<>7_xe^{MfcSA$$EMO55yxx,8,]ccccBӨi9^9z_z'>Agg- o`-bYXXGn ɓ||B\y:RP(AB@5,K!{9C2m⭸D%i+*%bA:SEj #c! !WJj{HG&aZk>RhPP RHjr>Ykm @r5Y_\׉M4cp]@kM5J+ k6_u:7[q!JZJXtă;ZVGEU +*:׺N5lżjר3:L:ik:T?0m( c4ku6,kPX{:#xPa ԧHv-G`Xc梻})!_› }}ڶ'&~Q(Fm^x?4F}=%z>u?ӟ,/=J2\ӛCC|&1J~?(9kuaJ' dC_Pڠ (h2VRAJ"j`sk)<-J& VRM/+!dB؆p5+|T[)"//2qߧvHҗK@s%R :U\YtVJ̗@Ad^ƴ6 \G6P~^ |x椊rط{GZd\iLƸ֠>c@u#K)kU Uunt V(s>R2FG&Q5MU Z0R RBasB/( @b*ffrKw&Stw/a|rWd)54D2- b, mmqf%;bd2iۻИ333+( Olgc/~LOsdgI޿(y@y7-Z `^BNppOu$F%KQ UeU%O 8}ֶLPpi8ǻ,qxÛ'x!dQx260=/v$&c y(JC*OYu*^(^U@Ɛp}zV3OLEir+GYnُKW t vW_ؿujFg45LP>r`o|;wyL)=NaR C“ *C:au֚@•8Ne2QqQ7`Ҳi'ڰi2A,$@9ŷ94|EC;#/ +\֚4E~ِO??P|H&5u5x*mBI:?6XYJVJH ֦3Rs||. RLi.1+4YZ )"k X^E$yUQSS˥A9G Q:@Q 'Hz! bbB1$9uj'JR_Cu7a*l}D|TvӮH:w0zm7L%gR`ьhtfÝ|ŅY瞈2-R%FpKF84MMrc Jr?;Q>Idzj55o/BNq(;6^ܗ@%:<*vl9D< -׼ Lyh"oqܳ.u+fYWX :<{g2;7ϛ&[?NխiqYA7uMbPk:zS|xB;?d6!޿pLJOi" J_J!Q:uT]qDT DDd!rm; [.(BR m=2B8rĺ:ZT1Gْi1H)p!Y^(&SJnMj)栿}#OfM-JZyrVq5S!ۻOv7g4)}"lbYF+b|b것`yKn(ŲiLݻgnܼor‡ h~m<ÑQF1!Q,DbA353%N]hK q3N2|IZy-~T?>NScQv1/jf5?IW!C.m)Q-n6s?-_M+ox`WS7]TZ*gtR"Q˴^bq: 3 .}'.vH Q"ׇ7ϓ!_RoIDƭ&./6c9lKu.a6u6?@TzbA3>god3/>qpW1Ѿ!0,JS \ȰK#6|_+)6Rn\YB͆B Tw(@]ǩ0+.A=8s+dbv vxjOݩ-.VZTkW\ 9s#`lF1+ytB0ڛCŲL:-M.aX,Om$ snyšnWs)vvI<-++}yl2BDf-ix`d%`!Vf r0WneK~ƣY֦ZYimU"cptp>t9jW&FYs|xaF֩k ߗf~ޠUP=5M~1c&~cs*cVgc WԌ7\ 8q##]:3EOco?[i:<$h4oTuޜ}ru&7vkzfA3>F!ٌ3OG;҉ݿ?)?ƞ+qhiPWNΰcLqdDFl*cp%ރ 0vֆ6g3YJ J51 7CN\ 4u>á]VLi c \Iݟf6x$vdQAʇ-`-/ֵ7Ikz-빬̢ݐ_5:ra`5p<\BQaG \+e+Jmve YJ㿒)ָݠ'4ǯܘ_ɑYR7 5'sAWrdǶ$\r)jlbiJ_o4Jk;byq۾|,tѱ !hooblI0|_>r =Q TP2Z-cI kB?4\i}u4$ Nڀ+q}N]}:||ٽmge5 A+%ʏR^jVxO)ZWl<,A# r7X2n*e7jO9wm.M2}> ^3FV,Jk[Pk:Sc*6?H[B.D"&./ʖRRQZ8Tҥ=ҞpZMȨaDh_x qi>r;ů~uW% >7s>-1=[^blbYmV0bX,5ώ+B}peC̘01z}}y˾%FWMBkڬPN8y5`xJў8{t ؝ؠqT(|tiFagOOgvY5Tj 3j PΪέZӕ$s(_lY_^ 9Ug8uB \c!4t-dR'H&8{eٹyij ҙn}Vmz5pdnd׳KWNG=2%pp\ђaoƀRP -o RB:zGKZm\>3"ah!ۑj>GG[bK&J{ECC_8Cđ=b\Ųy@ہX,cZg|d203;kRxG6i?X,P(6;ý, ՗=-+}^oƿ1pfOVd/s#I{R:x*>8_g,?{f2.%nԆa*UWSNF9snH&q+iKE_/4_Jsm&y_W=L.qz箕k]lN-k ZwQV|nO1x2'Zb: >gG)9[c;z%Wh]`707e0"}~pg/+x) qJNuP)Ʉ,?ƺn|ֆ+q'v0\6!H7z T'b`ZV))z;%ѺX,`XH[[FΚBhbX,t}/jydb̯o2=9γ5_b e[5,5GCc[p Q"v*N]޹̭;yfH_})Ş$ /:NغM]1ʆpY7Csąဎ4~8>I40@Jq꠵ UG?OlsH8 r_zTzoBa6_vAzSndRS{.ZC5˟{Ņx}.|;J0ҽ`=hm\LL.~D>GV'(V(`~uo~ ,>ph<)j~h1E.w>`WoTek(kpF D*U5])pAғJ%!jucTT_l"uh6JmP`A)EAFE8A(a=(_B׮ ~R) ԑ;6>RWaG_fC8`#m c/ܞP d_{5P&YbeI材bX,>nbX,\Μx?g4\TngCٍQ6? Ao/rj@g~E۝﵆Z50"go._㥃_K^b#gK &6Ī> yit/ZkJYks9͟(yryvK".L3-V4s!?y ?΋U"e| Lـ3u{&Vf_> 6EߧWк+jsg|\^^'g5%W?ܚ,qK=/wSݿc",b\ֺ>ءNky+Zoh+uӹfXWu:.So"otwW_IoP ۵FGDf1aP?P F7S|FTkRk^ #Ш!_ IELLH*pB 1ZGmH"B-26@`  6slJ1<߿D_WzSw:|.<`o ǑԶjkW}F?粻s甽ǴX>~lbYظ#aX,-6;#X,˖g=s-~~K>dG_oznFO2K/4&?]ԍ#.v$ןo!WPL|x-ik]|S$][(ӿ0k8yQN_ SW\oi¹:6q/jN+޻IO!C.)oYE<7Njw¯S϶s`(Qw6oڗlmyiWwoSF'}'.vHW4gUJ )嘞? W;\ZbIAu>)AYS:/]@63% f_|> HPU!kq0w|D{SnҢ먴yƁJb4hrEt4 IO9QUG12[ e(|hmj 1%WH֝Ⱦ.Sxnk #!gnW J^?첳!Rˣ X,k+blײ.;bҌWs)vvImɪ>n{Ȏ_{=m݉[tjC_@ )"^H5A3K+l)'}w;HB44Mu^+f.,2%O%9v$cD+7+WyK˸-LkŃLQkD`rpj;o.1HҕrdKgZ֬iSy!V 0rEr>O 8|6$ɤ%.MY\ڴxA=/`oDjJ&x%cc6뿬s!0ܙRt6I@[zDgCF&}.7_|.ų{=R9]qKL_Qkj ˦J&1&4G[Nq˯:^]*k-Xwbl^@T*̦(HT #8NT~Y,!CM3ҊבH!p8c#6:KDs3m`w'put(.6VHBA/InU~-6O븊RkV޿ Hȹ[7C:҂;l:'w9wΖkns#Rc,˺ʊv0,y==bX333L^c__oo{vYmٚ#Jm n0^`vOKw!&*Y3"ݴ6ȸZ.\z7$앴+Ru|N׋-gS<9fgCS>^ 7,u!Y!TwBQ)ѽ.C}^Q$NPR]8啃8iYw-@̖Ժn[uS__൧<T..NvcXC JEp/jC2賽7Io֯}Ҫj[|"C 9|2P~;l˖X, 3'&&LhbX%lY-[333[J)3]Y;-DŽCe|Kӟ-%EkXj^ )wC:3g\vtKI+˖Muʝ<T;]V'RQY UVg/dȷ>qv@[ ^}cW$x;XGi 7}>gdĞ~_~+;N\P3[/ TN<6k[o ȥ:ߙVG%N^)" D뼾P. y|B!;ag$_l )_zt6^FZscg{OԻ|{ 67Th_m0[j(mPF<1.BqDVJ|Asu4'K O*/ܖX<6bmbl|[bX,B wǂ\ӯ d2v[,[w&1w ~+) d1-G9/rvHW;|{ XnȯG , 7xj.f7[4،'K# .Ot%Zchat:>Zbo6pe}?f إklђXQ욚W_I~۽J Q0ő"?ȽـcOz|+wqd~d֫]ӕ ccXȟ_HHMS/&7 E}pmtjioARu 3Nq{rK>']`W"ƿa,K>X,{m`y7|~Y@Y,[\.g_}{oBCC.}lV1ʽՃ&4>^Հ ԋ;H)*Z?7]ڝ9Zk&(?w\ZIkc"S~.gE=OQk0uZmy?L/|p9ϥ";%dxq"7"``kڬrצ2y&uv Rf=6>^qP0_0|D|幽.:27ֆR xBigvײdW׾3,_or>*P~>TsŠ Ԝ/ϔ8u=-)xˡ![ bjW>cS׿ .JG4vhL4vѲ<1M't ::`q65Z e{(IGe0hxϻK$]k\riK rz fzAqj;%FWJNBeW>]DgZ(c`!Ԣ6Ϊ߽FJi@cpݯќN]hqG}>瓐Agw;t$}xbf5a9q%Ͻ'~s<Ø-tA5m@PEZFgkS]frCFxց2zcZ/hsyL7C__0ssv0,Օ%JwO# X,[~#|"NptoE{J `|F17\OoV r{JqF@g ^>rhgdOoO"o,=4Gk-jC&_A;ɐlX|N3ܚPs5QCj^T _Ptu8tg՟J.a 9M}ř#{rHˊiLTܘJWM3nMUԍAhxrñ}p_9w_\˼09q=98T+6F}uphY8RRR3-T0xD%2_PtKz,&?`בu-ʌ(N]t><$9ۣ3Ss9CuV&Yrj#p$<=.f4̖6ԜF8]Sa QP dù{sZ וV Kɕ4 yTR:ݜ 9w3$Án*8+\6^6h]_\d)W],zWZ^ D0T.cAQ=BRߚCTpw$822kC4 pdcyI<]5|GÍql4WG<!`bNQulzAsF3n]v:dYw;#c,Kdbq1g`X,N&`-d _ @"elwsٿ3 zԾC!zܙY30>3AG[ bϹ4z%%r`5-`X661?`s9;byhdi:Xcyo59u4+/}e8{7ťqyws7^}|~~qzI:Rp^=U ٖh+ױs*,4po&ϟEWeM[i?0_HӓM74j6 Y,8~&=IDS[XDh>VRƽtNA6okE@Cd|JZws%gh~j h+R )*TEG[L|zbsZ *6tfK!x~xO=-cE5&6'Mˏŕ幦ٺyjN=ع|mR(`B: !8{u|~ꉂ}H +j ٚmul6 9ޚSOV8@\ZkZh/VFWwABT 3Q Fis7uW=XaB'_03I҉%iYlmKН 7>udI"%(:Q?q u9tg%Ǟ"?JHe8Dڢe,JK!=m-7D>&cm3l] tJn[Ήlq#3o|eS bl:TʚO}P, 0y.L}e RbXZw=P1B@<Z\Q.M4K!A\׉ *B6к5 =R.{n!GqJ%YU#mVFQU}:d|"b ebј"HWQq"C_ѹՍhx2+?Q6h RgGS`%U9g:&O$ٙTm-"<ںk H@C#OPx!) F@,=E ]YKgWf5s7w7O$2"#mTͿ*.Y]kUt1F{՜^xWߴɱTQ)*ɱ-(?fANQ]-FJ'd_{VZjA O"FF=B<ÕދZTUkw(F{F=Z8N(FLY:PnGqȌNcauՆ"oCI_Y|6GzoqF=[3?kDa8$?v[^i$@kޡ3F2 Ɖ{R\xujr ;?=,cZř n&oTR*ȌCY렔BI/Y%"eڟͤh֋tHRcl@!ѹjNWs::`zAY62GYw0EfuBZMUL(`\hO., *gyMG3zZ`z??|mַ7a (6l^GA^t$@aoMOON8IR Aacq=AAy\YoǡFՕ+ SX67(G=7ڼg cRa&*7Nl@+apy48' "4h8Tc.a ~K 5?ⵃ4 7pcI 1(_AaC8 =ԈA| d1llΌ˂,xh1oF勇0 Bâ̢kZmɺsZ'tz vwiZ!I ӢftqnQ~ ņ摕Geo<}i` kS AAK1_.0#oZ!M1B 0a8b0Q1oD@lgF ڗUtFyǟ*'q203c<9n@۩UC.'ǒ802/-O,F7phWLgpO83FCA4a@>AAXM$@fFA/ z\sa8kFl1_1FAf,҈=2aZX+uՌ+1xz6_n`wߺ;x[PL ᙲKg<d0A{{i H qɼAx5}v˳V"6Fv^N^a^># d/$ (Mr7%4_VkBg^5pLL:;c? hc&tqvNWܡo,ycs\ɤsQW<{??zc ^x{C?>h9_&2]p^h<~Wzrvoka^̃',z@ ^E9Ut:W^<չ|QYkkBb.<ɼ ϓK`]q-+7eWIN?AZZcQDca[q&d!5i7([2gK[e{89xb<9g+XP[kBܻ{O>țQ|{햼acH  Ah4hq)+>MS_u,sIdgugJN=bl Yc{0@|W; ӗƦ*ׂ?t33槳q=(mg3ah<>T1/*3fKyH0o*O ;M%seu֊Zo_]g>Q*?Yy̍Რz3wXq g3Wjt؟[9e=M;sgΚqA+fJ0|f+ k=3c{o Ǩ| A j"n)F:3Otv  D&)shZ uW\5+t.*Ο*0˽JMPudmYUb?cX<9⼛B)`z-B-Gs~0c \]8(pШ()5` ǻiiK`j##.~[8 Q鍼R; I@+|!J q$@-zd0A^aZ&XV% K {fpNXk%/[]6| /{p?kT.|n1<\}HF7AW?ЍNa!?jcnv1pAx(T38c_L/ZBTjWؠkfuj|+;td0A^ׁZ-18v+=.IX  |n/I ]2rhyW18/Mykc&!6|(Wwѫ“P*yks.ϠvwPkZ_hnjQ&*xT" 6xN+v NDwjryz>HXc,Mr~N]a̓5 wO 4{_;^]Kվ|{oxc_Up $ sI٠pgg2 ;h6 \Ee/Qڟ4.sКAnϦӕv휃ډ|E&<J6}eƿڴpxR]xߗh m2?7z\/U3]̣(y4Mi뼮Q4osX:5zZ[WmaD!Я ſkĿE^}{O,͏F :ÏBu5T@Axni4%|zz AxmQNX֠j,}E  |Mzds(Da4+dJ./E2@rnR "&}ٽVEzg`gd1\KWy @o@3+ 803lzRy\1!"ϛ092aE9Zᵃ8jmpYe8 sPa^6v 8|C?UxnЊ> e<9N{;w4kHT5[CAx|ڃ K"­[b i#"<Ax!/T@uf|\/J+)L,p_մyfy/`6_Ey!Y@AWc꼩뉙ʿ(ZƿM}0Z_zPa6`!ˬ7siMy"xqiz u8TW30HNz?7?0ЈhǛ~AXBp=~X Ahqtx(wr?VZ#$B08kSk=0&0I8V=߭Q|?phx!7+zuf}kE?V ^aM 3|"XgfOD`$P72 ? ́uq;y;xdV>|svo߮Qkyzb?P z7wЪD|aUAn >=$Me0A^0d$ &"Fe(B2A^ޏπupQ)sytQ@%:TUykr0̸IcR> |eu\* ufb3oDu3JBsfif! laI_aM41i92㍲ 7Y|:3UaNrnOƣ*gDOs_ccĻ|)N딖n'͵~WЦJDk^UgLhNT\pY\5*t?ˏ!/b-iT=FGVڷjy~SDUL`upPľ1g+Ts6a,`5]gf?.catCE5w~2'_xpk7Z4~>ICZ[΁n@ ƿ    IբpgggJ+A  2b9s@f,m𣢄1Yc#d /EZ ۍjz`08;f]cNB5%nDAuH3c9; F[{JY| ־Jq8M4?GU. t`$ vwnׯdC|; |rϠ3~~7q/޿   ϭ[c6` }{lzfhՁ~c Mb  ӓNTC9"B-  5{J;dic ypcٯj:UW.ch& }oVkLmނ9 hl֋7sf9vn!IabQ8҈CI4g2Y/O wC| jPL 9=uGT"AօnVYSz>ZM / k,FoP$5ƿE0/{j|O`ǜ[ȵך6y=o;5ltv.- WS|œ Q( _/gGg ~z;Ĕ2 H /q<~,[NbњA   s`0NNw?- Ax``E'Y hE!(о!|Z33,hI~ȷe_@së`V=2{=ý?ǟg`|pG;#N7)* H /-tzvpcYc([AUm- < >;Ts%A #Ϝ 3E`s1@ (fwȃ @^o mki:HtA,8A& hBThhRO#>d ]c @Q_A^$@-ԯAX6& sL׿=$AiߛEue 8H #|Rו?VD\sfvZ h<ט_gf vyАuތ[Pa W[IOWE-]ξn@EDϸ ˃SR>b7޼KHG!Z@kZbmxeWbXx֋V.6՞Z^_ -VYzxK9FfaU>bg nV1tEUR <sc-gU.u}rd~P1'G#|iVwCu:Nb !ʱEI9 ߑ/PA)wQvY^ 13V 0m1uƋunb,j-ǀc<^b<Z 3FҬiK3]|wm,M^s1p]ib:;|#8GEq0埙Y57֙\^xzcj\|k?x.ffX`,M2dc"g_֜ W6yyQexLS\eb2Z a&pfscw֗oߺs1104 6:5x֎s-ùj-73fCeu\gu.P?97 ¯-a@䯱J$u8%xtPci{LtТwxqX4I9DDi2tJcFfa[TE@g02zjQ_a:Jg)S4b@S)ƿ g@2 n?=9$IV\GᕦVao- yч$A o 977A k>I30) G9sፙ]\qnc[}m!7w` F-f ^a/O@Z)(cR`1"@+6 ~8|vdONS F! eR;)/uj,aioD;9’6/_41L]\~7SDcG)!&Dk@c0:iv ZMcPEvV2Io0LSN&@B糾&{k}EX``Nk/3* S|r|Jc֚:>{5?,|U*a5O% 3Ɲ%knIO'q,~  şm&s(֑1!ChZ-,`|F WCEs05j?vOȘtMAxA^ivp?X + aggz]VEAxy1=ythّAJ6}`|$h K~bG`@t |F}#cD1kuOʡY2)Gku`YFo qTJ ×f@)V Z?I)B#?~f F'a1J HtQQen%2Ш94ۍW)c:b0gݠ4#;!GsRhX0P *0:8"đZA?VgrYڿ"5E86 @js|(Ci:hǓZ U倌uHjuiY, y9_.3 Sa^P]pfYkh̚TZYi@2`#u.7N2f=V—"ءJ0&J8]ZoW}eOz?Sw[/`! ;B֕r r,i19+Qs5| \2}?un)/«ʳML &1ADQ=Y%` I_aOy}7BYԢ 4>qQ%8A+UQ Ql7N| N/Afmj5Ve+=xqFr9 g]/<>fETWV "<>-zvΏEq&!3>xڋ{m߼{8ZnQM*DsshBˈ/@AP0N{a>A": K UL\ qBdE-YR):ɶ.90t㟹c%5 VoPeE A.~ĠKdqqD_U篱@q^|PyiA?pdsw{qIٛ"uy  DV VSAAx5(e33N;#8jDjƈ"]/Y98_^+ M+FޞgX4 đFY@lko 5W>tCOpz-A(V$p։.׬Fth 2 d`nncu2C~K}OnoլIAg @a(ILZXP8'w  "oκC|z9jߩ# u[O2]3R͖/|v/A,[5o!Wc$pڵ^`_ @w`Ռq|B-J3Xs'!>0v۩# 1V+_2t;¿u|#m7Kd I h4rG. +@l?p ~EnmI.mF{Z+ԣ|~Q oF-5e"đ7+%XkFa/>ýoGx60ߡ)}Їh}: Zӕ?պK/OqZ; Q0=ӛіW/YO3|[o/>-uSDa+JAg \zFpzzǯ\)@hhwAr~hG 21 CcG޹oք\q^Vq\;Hqq2xgr݉QHJ+p65s2Ijqr>(@DskoӼ]z|i Wm)xFzQֺ*e| W%\@VDs3yA)Ojq0:-X{u{XRsZWչY3s#(UU΀`8ʬ{k3T1Z;VxycF~tS[msZ+t)V a{&o[A6 n=~Ƙ X Z8a!; C==1O% @)*GO51j.9@3΄0 uj *)h7 #5~ۡ"`ydY9)ZZ~/x*sZ1,wAEZZ&ZqKB/O׍y9ͯuv $=8i_a|;$BhBV`KV A~rt&͇8f~MZ1\~]1H &cd5Z\u=:BUEnw3pxpW;{/ƛ1jqaO=3a֮_~i!PV6CA pE^uLgg<%X^A%a@2vAX~ib8qI`w2 8k0Wύ%YL Rl]4 -;@S0y ΁wZOe,)#քzLG8R ,TRu=sBfQ"I tn`{13.1̾y0E65Zkb_VC_BkBs:;.Yi\gZ #Bo*J,6mA)bUN~%siƿ"  ]TYF8@zDU@igTE""o?ys{W~pÓ.[ gYNkd0ԾG) /#??I?^.Sy'#pH mowe AZh6b 2i^,dAxN7A84_ =槞G|7Fx0@ kP HᰥѬ)X _C "ob4JY08m`o0ԥFT=sg'_UOk+T8ſUάmiD!.,NT3aR8wl6GanҌGEKIΗާkRX!K\ִ@g`FfMa:t]y X4,:PQ$IC-ӪaUC4*]bqu"o74ikaaTX^؀&EqQ3*[f@?b|[u7j1[g$"Ѐƭ$ofG }KѪg$uJzA N˽~j Wh`gg[A < gXF7B9Fb8&YSY$dog1Y^5UVOPi5g 5>C )ukD VkUv@-ʂӴ!,L`:9lkc@Sn5YfN?Ew0;F-VZu엲(KU:S`8dCmz~FzpΙ̢;H0zV3B`qc \nZd|ip #|uѻMy+D\3MLGI`-oj ѧ # DoGd|9"nnAV%#IonAGk{A*ZJgbfuG8ca#\ɝ {`sZXR{Q`A |FݭPA_-GvΗgz:4k Ĺ֭Wy@.Q')QW8PzA1<93;g!U< Ԣ*7n'q$ti:k9gOdIr)^S8 XW}*YV3N3Bh(MpnewX#j|H _S ,6~qY.Wytf/22x¯`|IaAAkfow$sbyHQjMnPA͜Fˊ!•W/""ZMYkAx0:Oφ0֡ՈD^; {`uXXkApdeCx2|ww5¿}+Q̢qD-|9W|u ڛWyz,zx1?jcnh5..e>o7wS o -5]IHAn AAM5w~` 1z; qvs1veᆘAxQIjшqZTeRb&0"@k@/ 3)#5h4|9urZoΧYw$P|vz82tf8ֹ!\dҦpc 5Ca@`x> 8Ԏ<C_!SSMjժx<FVJT4j1gom^cc,]X:öJK@if"N/80Ve[tӜ8 X3UKI۷oE( 㲑% )[.ޱ9THmn[q $3MI3\f#&zٺ>P:o'7cǚ!g~,7dyARj%d~Fm7yBqm)*CM#fr{CxY򏟯gc|i7R~qN x E!nEx<'>x>'͆~{CFxfYnU"5עG;::{䲃s@!TK(ЮA?rɶq}s*qiGM Vwm0Ȑf$%M Z< ̤[Te^oq&aa <*zdh,JhĆcS}Jۖ%2:IAQc5굨?pXig" !P1VcloZOl:)+/",wr:]?Ya'iR8 hV57CPyceX6$e}3AdYRZV0+Dv??{kڊ`OZV>)wXҬ&^gIIKI|:=Zev*rˊ=iVY+$9[ zRـ/hTFWRg{<o>x>Gff h˪<<,o̼x<aBydXR$)am{C:'/ LQ3=0~Wosui7S=q(㟿f!ifiyD.^hZ@쳧v/@/+6jb+(4 v[ɡ?7ΰ rZ`WnnZ4+㵶eN\w_^HHRg+Gj)If1Af HYݡP5qX-.N\_4r(dmF;sE{7{ !״Yܪb7{:W9"f+eaH3C!GuZrkjAoEJ3?Phg+jΝԭ鼀 XMw73dEr9;\vá[h!vҚvz +\I_@1֤M˥kSC+Ԅ—jO59{l3fi9+ų ~ x<ϙi)ÌlEwD67[EyPߐyx<CHRaq~_fa%sIB7RXFhW5^X+@ѶdVѨQڕId)%HuPj\Gz4x  ͪ`muǕ#3vwc,"yKq΍B=V* 3 ͖|c@ZPZ;CnZl'C;w@. @jN"lt5q&Gun׹p(pfm%F]:NI:bEi]\t]-k5ڎ׺wz:k5YMIj SYqS )F֢iE0ƭCEY اΥi洮5=!~u*VbqLDܱ5UX^>7͙g1SuZUkpc/iKgCAx<>x~Cg@|#x<1J(:11tV4K8>1SD! U7Zl (0bkY ]-i2V[8(5.[edzR,n ;; $N-D(,!4xT=0"=~$|qDT+@뙐F<># D͙IcC>?lzUT߬zrB+@)"W~~:b/syXNN8T5qPEL~~ yQVȅ@ S" Bh,LE+dk9((pע:tZ {0:g3;BlSQT@g.}x#.g q55ͪFk`@kzMm~18U?&h~%J0{Eot?Bvh\At[k,xF[ :ͳDUH s34VC\Ϳ ~<`8٨Sƿx<}@JDhllnnВ1]u͕hV26VZ,&֊q->PdYؽmGO k-zjFm/d|`Jw4:!隢YT"Պmu?[k ˪ =rxRo_wZ]M_U& F;l-4V4 E%=y}ǽ(=,̒d.Y\cu9?]ERV Zc`Oԙn:V{F[XtUjl4uκa@"$M-in84j.hZ(,}-sXk.`5C)ADm $5i.`ݽV+z w(A>Y'.g<~<3gt~xx3*J;h-> AzٙJ^(Yy<{jY[_i3]zř)ާ7$3;+0C}a?]hnu If +=jF[{~9Z;%  go<"A_Ē@tMQn?*'rM]PQۿNL86nxgd\Vt'|1bq: RIs<>&ߡ>|ZSoԙj6'gstdm}dQQ\qSfg~x)am}PY5uR>A^!K7 ز+fmu`aS.4OiAൾJ\ A Cn,S5ͱ" aP^&ZkJԡtU0tK9<=07E߾AgC4֠G_<hMc`x+F۰)P5=GO+>QT"j?VZ;暊cS0ba3>a20Àe;w7kfJz=E+xq+gcjdjB|y0sn޾Rv:]I9" Zj?1{<x<Ô6D Ί1[VCZ c+[AqNَmifu/Huޭ~_띆̂B7s:'%SiEu[jKO `+I-]Camh]G`Z0[ۓt.k@=ڥ\OAܠc!7_ssS:30iyRO$_>3?]p@fQJ2:0<>,+kc?aȤ!r}}C:3v<Zٙo{.Ʉ Mx`A+L& OV9Wo7.m ~( 4xB+#:7jJ=~61PReɻCK%Ru΄n-=r[Nkck}ArpDE ikj $ KZn۵6֒u(:00[WD[ ۿҪ./؏Ka˷wVu.a* J'77gMk…kͳDUj(@Lš +|nօH&v &o\Gc {Mŗ Vl~NdXxϯ-CF[=^on$gW,M(!_iA s E:&>]#nO޸<qxr@V2ĮP{LVүPDM. m//)&O37p!ͅ<74j˺6.^^F@jҝu/{|GSX$ن&۫m>^#/ Yn/[q ql3{zh8hpU$ ëV/?`YKsaW*ps օor},VT`Cױ֝$BKyۼ *WVX:N,-C:Rw老_|[Sۯ1]`8{M,F,Zsu Qc{:@ ک &XOU׊nk<(×"OkV(U bz"d{1m=?&}W Ψޚ+\))+n@'i"(^I-K)WF>x"ۮVْN1CkMVczzʛdzz>Og{<ƿٍu~}a>EVpn-23U0IX:l3YY֊ UһrvęoŽɪZ[4e+ШԫJHsn-C! 6&-{_;l}{z0v8`J/./i0\8nthMN8y,vP;[3Z%^Dw 3oXkkkÛmL5*y=FrXJ%':?wӛw߬ o5F5tE<~jvB%t(,IM˧7-.:ϝ[Ǐic煻P=.m{7-yn=.BhFIZOs)^]{ FbdylBXPk&V7 ˥>h_efy#+S\~|ծICPE{VJ|rm:~z0yOU8+7.z];Sv8ν:[1۬PN 1֕?z,W;Pi* -Hٵ} T ^iY>j4k<~jƄ1zj"" ^GEB=:RU18L@v6Wߤ^l1սzh\g!!ð`5h k9ςrLк= ^|z"B;u4j1xZkJե ~_}pǎWܹEg]%۴r.i5^g7?}oh.lڢ]5wluMez]n ZdpePz%7 TTk/=KO|.@Mc0tak|rDA!zwN v͍ڃJIPQ(X@q< ^YnY^JM1{R0;mXS~Owy9_:boh@9/Bo|Qko?277翟xx<1NGVW׶}jq95l`VjJVZxz&J/ۯ<Ϝ`~NupZN2ݬdcyZޓ4燯S.^Yk/;=‰c աR}k3,hvꯡ)t\uͮ`3g*4'iya?I9xOSj 2kU ?2݃/`Lv2?=Xp]0P4+և̺߷V:|np}ioif[ؗ֟CYGZ87"(I WY *| pjk*guO)祱BaQAphYk0+ Po2Ӭ U49\%}A_5.pu{8PTIfyKZ}_~??syY[ߘ(ܔ}GRv`?8x<dza0 |g+|ճWϖ_!GwRa-XQL5ϸ OkgjY~g|Hd|3鷞BT0X\^Ł+TXY!41fTPkKDPoE[W>v׾(~EQ0ԫzhN[(ΖG?ƿwfp7sk:+F-BZ%<:XӚN#痈CͷVQ?6ZA![ ~~Nf^{,~,D{97ZMGP̿1BV6Z)0֘nV`Aeѿ\ն K1q/9}/.<!ecsSZi;2d`o x<e$ 4<Ҵo~_Z^{Ql+KڔCFar)GK{]кXd(SZQܞ; BjiOs7 ^AH :T0IZV'G+03U% (o JK[[ox2>xRӛPbv]IӔ4|]E)ERZCx<{A况7?߸Li89gB7ҳ iZ5⑹jQdVF?iĺnjJh+?(ѬPz[ӼG\S<|{QXZ)|z~OW9Ȭ- ִZHXR2{:!ɡ<2_Qs ]~ ~%/?y[! :h=_iv T͋v O.O 4YkM%Ow:$'КfF8xiDjdҘ=7~%y橧{<x< @Bsk7u-I,˽CGJx<3a0/,w:Z_;g=w҇VrhPXM1;ҨFqAݺ2Z:|tyZnęYw_ǎVe>҅30aP6}۩ 򈣐f=zȬ >~}kKn;/_ٳTU?=x<CJA4>Dq $W0xA<JLR?4x<Ld$\Y¥e&nS/=ӳhwG D +J|J*?yr¥e~ V7=3vg/K]׌A #V dBn8ba*Q 1U8̚V||y7޽Ν_ <x\08_CxӺ5\ZӝLHr!3J0QEX)Dr.u6D^9;ϫ/>¹3ct.SqP`eru KRT8, j 1o%FWVbf"Ր pʡ;-|zgX[ǨWZHrVW3Xpiȩ^xrsgfW#N ?;8C&n?LG$9oHڛOTSDޢ}IcyO >Y0u _~/2N-:=x<1 cRPTqoՖ,Ȳ_y֥l6Y][nw,sn7x%Dϟ'8srjn{An|/ZXc=1˚hJXAD(ʪf_k[>fxZl5V `c:nHdUr߽;f/?sp9"|<,ϫ4M%21on"Yg9p_=e 2n\9BF3wZWWyÛ+|3"FzQo\6[-:<: IvAr%JtguT%BNIҜw6W+^7l `Iiw-BQ(C&;1+^\\& _|$/>}GOЬťǡ[$3tS˰q IebMgcnj@efji 5 mWJJfŝWzX!j%9ݬ`iFძ2UhJ$?rd :awwo|1^~$f^qm Sqt2j&YWvfo I&9V{3~t_#<~ M[~qk.[t̒2UʾZ&ze#! =Ig=A.uuh'ynXZFgosMNWi2޹pw.y~aNjUb굨v;Ph^y_8|5Qc,D 4RJ{_8 V\0;Z_&嵋"2]Q k[ Qpr3J c(C6}̐p>+knܼI9AcF ^*_| <̳ܺ}_=(^"|sX|xiaնnW 8pQF!QzxpV֤MLD}gWWgN0?Woess ڝyQ(8zRn`:`eJ1jFlEAW"oKofhSu}‡W[t@gLl9. N>O]O޼L~'YN7V~u`_nji'$FDPL 4%XKF _>W)PRl{Rl6E A/Y&qZ# P,m Â+P*XmVZB-{:o|.lE|Q7O43SU*-2ӺnvbZک%,Bs؛[R*]gު8ܿsOjd)XeCSsU{,bGh3 A imwڊΪV 86S wY92i1g~:k{! ?eUrd˾h.[Vih'$|fgpE7Ƹ@ AXQ48qlX6i؃L3Lx-gsLW`$k?kmR0;3[sN+ycEf_S| ֚0 0 ir]P2a/a? |gճW2Ӭɾ;/"lnnrge4I*D1S4k5L!d& 5Apﶅ@+gg4/Knn EK7W;_|0Tqrl@nzc~&} [?S}@g|[WG(:=< [em}nn\cskez}|`@$R * 1R  ˗qx<O4MeyeuZdz/=U҂ XcyGyPo>3[6wVVāk\h'&S.P;Fev;Ѱ/8ӧ0G ?z7֚/cG~!^T|vy^BF6:{XYm{>Ɵ}YN-4i 5r{͕B81iTB;1 `|h7&$pgWB=V@p4Eږq X~vۆlod\ZPaXl! ?{|Oxٓk<4Z {v3M Ib g6.pug "衪~nEkÞ֥YٛZ%Nc-rӘ:_[K /k? >V-O2+"cšS@,+B["W@?9癟p{]PAЯq5yaYZP(NN*vbBzn:r1ad?jau.כA:U*acMsz!i9;V8P"g ͔>+Dآ&}5վw [_j&O_}Xya~j@Btgb;IwMr@%,AVҶRfZ3ژ123؍QbiFv$& *s}ȼꖻUX1VMsL;Gb7>r  /8&HڗXaZyז`@ۼ~tLxne::$ H pҸyY/<=̾ORc!{ƽYm9aǠ75+b,lbXf|bR:b< ֟>aeX,KL&OztcbYAdžb%Uf+<}l'Qv0"+6k9nRa}E2sС'6u%;PuƑP1a <_yaW˫bݤc-̕"^{wKs'͋ݟX$x 1kQLEb`աrBh-f]PzhŖNPoUd%~-û4  _znw2&mM$]X/W#>6ſ%]A,OF1 F s+ls9ɪaYcxnӼ_jPHve:Sok17 9v2,qiz86,Cm.Q)|=ͯepFpLW\+Q@^g%4W5 Njt^J-s51Lln>f$>yz8-yvN~[OEwF翨rJD.9mEzeI eΕI]Jw#"`"Z:YzuȲQ"Q^9azy3ZI)fs<{Nlchϊ|Vz3"tk1{BH>ehl.6bX,]Q,d~a:3280bxj1I}0c0F0Ɛ,{B@!$gW[,u*/5 {XV4Y`;(CW^~}M01377G rNqI iS#|E޿))Oԍ-m ˜>&J:2`Zqbm}[׿|{~wtwmZX3[2_9p"{,[mIdZh=W6L.5L-р'f-LVYA44?y.o=9a'[YBkK5U\% :(ֈ<BH=5|ݬM[k@1-w W& 慱 O_-FFavg_O<}xciɮV lyjf5v :# Q9TCi L-$Z_T#vxpc3d2wE-;dF(W ፋ1/89Ϩf`Rl95ER? 5e = Y9"#gRBGMq4'brpNIC6On~F:ۼ͗4?;[ҝoa߰dhLjCn,btcbX,KlVe_l&V d<,pc|_]c͉Ü::ʁ]~Z:e#- cnnOi =#Mpjp򸡦]N(M9µ8?;3|9cC|9:yG]n\ HS,ט/VQhz{\\WE)YB%g}f wdx,'e7뿯ujm p,;/ޛdžy N!˗)X aY(5˗_c fnaad;y\1}y>t06 kXֆ X,KWOF+nm(bX>ayI 5QdL&cX,hFnOI޻8Iq(' s` Lٺzr<֣{xK91\9Og'(U /nl/(^>qp%s!2OC=W.T[e~^=sR9wsp}=Glc(UZ qUbZ[n!׈Rjۚ4񅧳38]~4Pgg7o39]oⱱ~gӎw\D(WcJ-QujQ{m"JdӔ6…;oiphW/=ؐGo^ Zw3Tbf^=Ok{xv]'~p+Ոb9Faϡu<27vYg$cFԡ'fعe{_{0zǘJApv̻".މqwcǀ9iJs%㹊sMDDZI@5ZRd|WnZZ[& CHD=w>OZ "-B//d2_eĥ'-gX6bX,]3GX,bX,0WWƯnW㩣;̓xUo[ T+e7? iku!vRL?s-0vxHcP@OEukJj酈W넵WcHYIj{-uBE!s 5j5J*0mKiAtҳU:i3X狌ZVJZj ;1 58?+o$ Nz0E Uܛ yY7V'#/UWm'"k1Zz|VdOZVGpZU]g ;4hXƼy)f ^yڠ 9Ը9QO~x߹R_}/?m9mlkZ󕵖#I^̗B(& lw$Sת);ߝ>@:'w3!Ɯ1óxx;x.]ܘi>?w+"M.gȡ?@f h0X(2 Ȯ[kRɊ[]{18 Ð( )  {A{7b~~Ar˻Fݖnl0+wF11wxǮ.?hƿb|>j,rOvbX,K.ݜD]|\(Wr[]>Ub їWDP={Ef78Xk0wػCS ?~rM^~Av e=)ƿ<7\]1[Q~3=\sjLT1^b#,H Y?ӬQZwx_ ~'-Tt$@k! S1:w!(|wQ-s>\_dSjnh] &.a7"Q16& ,KRI,kW~03"$ fBW6u}9{oո7ٹ3;䂴lQkZbybX,bX,7Mݧ֚[w/HNe8#YfDngWJq^xDcn/2Sx- ԡ n/ި!_{BӾa!B<]SNE|2PqdWO 8w+LMVC̔COvԼ;oT93'TIVyIMZ+rz .,WꐉiK6x˒tFc:zwcL])*UwެwCJUů=ǁYO%fH]!("\YsFb> :;:׏l LNi7ˆ?y\tmhMkAk9M_m٢! . rMD'`up\ιiVQJa u3(25LX /16C.Hqp _[#Lȟ.R3Gz cKյV{PDZ2ߌy_|*þQqޭeص:^^gJ(]:uIG׬-NM sEKy]IbV,=(]D̞ao<02,hX, X,#X*ITF$= 9hX,bXrرM{֭T*r]_Ѿ!>/7Rވs?>t*z{2<ns{d_Vׁ{[Wv0fVpRSx6b|Yy޺X" A~szM3jZ(=j Ƨ5oCL)҆86doa,L; Pwx.UbFtZԋIii/Z$ 4B*lQx 朖D p %޹\|^5E)W=p7>J^м~Ğ!Zt^| Z']Zx024oljZm.:~kH[ Yr:k-k|t(8!$jvtbeoy$鈟_x=>NZJB#N[1GoZ!T5̕ Frd3^nZ@5\Kt3c: f΅xɽ$$2r?h&׹4WěK@40þ.M=8 hR&j^!JцJ5lL:oGib-m P #lC*ਇj՘aiC{;2@ rhO/]͌'py<ۧ\3[06%˚G `X,G/,zcfvN퓣bX,ҁR$_ӟ^tWNiC#Uj:)fS<{ȣ/4tnBCyr. b}3d1к͉[aKU^mR/IW/ ^<3XpƛIj:˛aad(V5VKg>TX,juS^W;9JNG%i7l.K}˲n}ꆰ"QoG%:Oߏ8y/Y8䏫U.ZL/bKʌ 8/8#AմV5NZqQ7 s9wQINcZ$Y=S~pƍ <ߧ' 2,ɆId]VܛN-ۤUWo+ IUa: $F=x.5Jy _(Gc9 |Oy:؞Yvnc U9Cjع뺴+U4c; n.llJ z{u>W p߭޵/I {QX,PdjzfW**ժY4iX,y L&{eȝq;^}e~ c\lӰnj(ܟ3s%?Zdj_-멺Rs,'7hKwܘГ>+'2$GpĬudn Bp11_1 )N xU_0 ׯA|*y_ bLbcQӼgש7IMNtNMam5oż{-bh_lXgX1ܙ Vk+\sGz[@kZK)|8Zw֯JsCd̹1^P|bllbX1nS CPb|̸wTkrٖX:0??/=˿K~`0{:pZ߹^/w+G[Unǜac;\ZD,Mk" '\]e|@>1O1T!,vXOk| \L oTk=CDP2U8teZʅ[U>ћOxϮ:fvr &v7[z F>k1nD+=:㋦TIt^ZW,\ r~ŀ%F-QskA?ii(NwE\v8<ӥZtҼ1qCL*6d߂ņ( Y:-\,3S4I@̵[c(^>mX9Ųb6eX,lj)KRP$7LO'cmWo:_q+1J w l+(m+˫M.[֋u>k\Ԅû\?3ۮsr.\2W;3}>kD81ga!Rj#\9nM=ptP8Y^ϼKNk~t¯GwvTہZwRDH| x'smUFI۽inKQjZ/yrV!o^QĜ80갽ܝ饵pyƛLkplspjyKy_lvp :otL_3> Ęvո*;QP:Y#"h|)|W%oIagd>ňLh*^<-s5- 6bX,BGM޻'ZeaOb|xmֶ,X=Nl C4-|Yֱ8Ґv.~z"phDqhG$[g2S!w<ӣS 7tR-'%p^8#AvE-[HEɊ|S8ilD+K;8flֳME!{ƽYm932 {X,1:2 uOb|p]۸,8w?O?RJ7V3xdU~~>$#>Gv yc!?٫E{lq>~B5mW 5/߬wCGvf9ǥ|Ŝ{ת Lnd:?J}ʬ֯sX5LX d3YJd,*ܹ{cF{59>u?вh)V* 7J}ſsUG%(}|l#wYMW1W7#*xXZNz"b{y<ۧ\3[0mdX5-'6bX,\6k4-bߧ'&eL>X>\rE2/ wܙTB1\'5ْG/ZsNߪ\m1 1 |)`6ݫE^=7Kzn?H|NS uZF=h{SJMZH Nwؐo05^VnRRWu"ߩ1W9xgफTGJWFfZ֓}0+RmFMo#Ҷҿs5kU'z&ut[?|CO$/IpC"Y&":,EM xy>H}>17HsYz_W9I*zP <kOerJ5XV\=G6٪2#G8byOwö巓2I$RaǶ[(k;| >#J4J-T (S9GgoT1XHf@-a 'Ur!I@:} Z':N7Fb1fYc5 4b賉I_[|~rDrrpuN+YIZ$ҭmьpp΢K 852Z'XF@fXŸaNTqdqXiLWB\]lsfl$1ӱ Dq=їw@4*Kt-Z j!1$w#gۈ,9E벓{Dy?kz[ԍ/UCʵu$nuWgSY? %U ~vcyl #Nz2wvB^)vIi~ce|bٲj5e~~Ȱ}X,# \. `X>q]|ekwg38~pLLN?CoOU|Kp/ 5ed7KA!;k 9Fz2K2lo Z\gEX^ͱ?Yц4Uշo|Y\lQ8H#"W"8i:涾0Xh-.@El kWˎ!bbU R嫯<}*>։1zcDZ^̯EMat`p(x6x_{k蜴K`,uw\զwd#`6glCiѺ~]29>KGr/bF#Nuv]gzŪX1OVn/&\z_.t:JtQd}gl2ji d(2uGR8*Ki$$wS:RdLz\=4Fׁ06T*Qle|Fe6l #D})"j,ٌy+h1w1E^_c{_@oO:B$‡a{wK)1M[G!1}c⻚j?lx ؿc(50i-癄a`>c?IIɭ-l-i_ǁx.I N}QLhksD,wi (n7]@љX ޞ,C-ub)nܙ&Hy龵ԕuS礯,6%|=ĺ֕@OA9>{we]MkuZG 6  blIffgRHS,PObX,[lxn\ws ϵ^=kxZNDe4PcDб! ILd*g(+ u\t޷7J;V /yF"yP_.f<Eb0WG$j[W]!;KDgͥV7Iۯ|֟nu}L,Vߖkj$u<oy傦zC] ia\R YB>G.e2YJj_dkVuqΩCFӾF4iOA;0[2\g<)y'8qh}M|6SS[,6bX,[u;QUmCY,bX,K86N8n$<$!Z|T՝!DRC8o1-։Xmαnd$=M˜O;jY6؁χX)CJ\c7 p["ԍe޼A&Xi.kQ#xnN`6bбbْhm#X,bX,&#`m qlp"]u:BPZ aAU~Tz;o Fbxx\9mu~xk=ulE}o(̗BcޞĎ92N57[WiHX5Tu#8gb<4lbX$Lj6˥X,bX,K;B?H p}m<<=Roi0֩֩o.b` ᇪfuEs`wq$ X?tSQI K.MZf,j }=%5Ȣ1m]%1o_)GO?~O=s}Sd^X, X,˖$ϭlCY,bX,fGRML_MV'&2vP FqL%Zb=<\7cm%]iMEZ?D(JI`Ipva/`osq$ڊ9oǼ~>bz{ ŧ۹l[,G X,˖$ͪYT˒dSbX,b4h6Tj1)d43k*h-TØ8ϻd}]~otmj-&bqf۳wXq?tt*ퟙ[r4S`x O&mxT]m^و[4Oqhߨ5-G`X,-*˲P,i*=l>m[,ŲYX( {nX,]S"#\'86#TG}(NNK;( x2nR m!nU[u6"(͸A~xJmZ1*s&2nchcek;7OdL `j!ܝ1jq9o7zv6Ϳb`,Ų_5b|x05-aP,6~v}bY3vQb\^]o(o#?M8IkyY,PQFwg$I_7ȤEMB.ϭ6]J "IjJgf_n2xƿ`cR}Lu6_Mйqr +JWKjZfSY3$ZvNjUǚr5bX# ޝ#70eӆks};ҘXzkRT9ZE#ݕ9P$qIE`b09+w0]ʰkto}?Lgvu)}GŲibX,b|dwa޻5I\ʺvu`@(Z(JJF*Z} V/װkD-I+[c]G}Ȭ̬nTued=+qkS{ dwù`(Ek ͮ=H>vn-  4B =W1W&ڼ2s9;Cgʔa GR(c|AZClr.٣_ -˂FBgyAZB*gx:JkKK DzļދRhhM|^7jK.iCg,j&?!lJgd>+;Uu>fǢay GX2>1$Izd(s^ f$/?~;;N=.wB"`;ШG+)_>||T@@;X_p>x|ѧzPmbn I0XĩA086lDPjf`G k]~b"Q#Wh6w-S{@P^_uY=P`zO^xó>6o5[-tZ`|s5^ l An,Ϟh4 шe{AAxL&$I*%0C<\ժAi ?K:uIH+n| W~.cxL3 Y+]ۙgGlBdB!| ׾R LK/[)c&J<Uk]>zpVk YEsic?JU:Ӆ:_<Ktz՜^sq_3]“!#RHaʪ 0XF8' ƓqvmYХs^My"nغs̠6-NjʵHpf0 ` 9e>~Џ ?~/oboj̚_O'ߎAuOϏ l An$~We p=xdsN:Ja9";XFej.{Q<+[Mf />;hܴyǔ4VjeUDw>WYfhMZ{-7a~Nk ʪo;41]'OqQA An$c{ە0\AAxEh"(Az$4*#.{bOr=}eIn!..L`n\p;g cԆw^Yz!Tk pZ_hF.ZUV}33#;/2E*<3zhEe>Bc'uIq>`80::pWX> q bD?*//?q>:MsŠ*.F ;?Na-ヷ~GI ׎ 7dVJ «l^ר7Aw3& 5+9t+dĬO#39 /bOM 8J6zfR)C\%ITkk~YCp[lEf U#yG^ KYeއ1Ok-_>/cZc4I303:v; ԢP%TtYH7VYǏ,#=ƻwi~u}}bcG _qC,sOEAa ! QAWE^GƠi˟ANA^?n^ә9ŘWsr㟙W2i~a&;5$R3L CxC>ri̹LMF(n3CV0/! `Żl/9R|_`2v+?R|+z?xn)`b, \  H:&qz&% .}G}u[-< ,f~w 9QШUW[.yf?dE>#O˿7jHh9< Ai~JgyFo(2(N[ Lʪ;d) "f u!'Ygy:EEVyy0ZaڲUS g=FBXf<` ?x|Jx?9`4.Gq>G8(DzR   An$S^AV% kSqz]ޓAAf?MܜY4g]Ã1~ea*L]gZ='ZmzԿC͘"̀u"B̈́ۚU:MW>avN:'6>Ez;5#s3`4Q3hцYyĉEX8sٜcg3\z*3ž$TL< 7gki|)fwaVLԅ M3+a m2輲|"C1X_!mTI?;F${zԣ^.ݠTe7xcW{{omGk}Χ'1)j۟ޱBF0JA~=AFsF[Qhȯ   2Y|Hϙ!BƿAVG[* ,p.Ff- Q-*;ds:$2i "ܱ:5K$J+:8P3a 鲄?yq:"{Z_{ݝhǿv?A 1hDv*bJ)q2G 8ʻ_ߠT/¯!  k2켇^?/O@~AAr4B6+Z8̚e bx$Ibi.嗷7uYv qzEI0I,&1Q}>l`?cS.y>O7sey15vZ33āE =TeإNemͩ/錢퓘`|_"e5Zm 8k 8^s֨0|:8ՙ*f/ù3//<]k* Xg{9[CWqĘ$Wgɯ,'Wm, ECT=Zu@-PkN|̨+L1>JW0F- ?xt҇ք/xKkwlp # q˓$ AAA."ղ/=6+ z2]~Bl0\fPWbORKe/B^~RYlkeu(74teN%\A0L4?WZLDU(W#8Vz8ε^L&l)[\Y:v.b1ϸ΄ J>W?siL΃wxa:4.kPYUycɜޢ 6^K f,7 UPF6V<#u$eփ0vKʹۦDzԆcb+{p`ʪ\/Tha_ k+8LVt_Dd3sXOX|$_,(x—G6 `/ V>$ Ɠ 7 H   B%zeejaZx@Qd"uH{DF-7:<*3]ȴ ͚%>UW@"4\TwdT@ڔS(ڶ/y45D`BiuH2Y c~2Z_Ȗ&ѧ5- 4`Z^qb`(d0cQ7yt,\֘qQ֙gujj:cm jAk*A(9b7V W:A=s:yk큢pH](W }$FZu$YFj58(shD3󈌆;. }?zu@Eђse>~l}{o}8ƃxPwK b ! ι IT:JAh̓Z ;;s 7=$sZe_'7q!~\9s9}UjY]=35Ik0tVX:P?;*4a-xfKDz+k EMn<MDAa (ŷ nA3̋'$Is^_3 #3 HLW#$3m}- d{֯Vk)>mp1ή!:9Ph`Tn_=#am)=z;;geß}?8iN*k J o4  kj61>ODt$QAIt1^_:J FT|0#(|"1N8۟B(1ylԿhHl(4=y"dNr'Iu Ju4kV=l0 FYO?|;Ɵ Ro}4ԣf]  $@A`wKϞ`km$AA229(A>31Ir`6EllolPHJĶ1eLV=|YxEs:9$]Ü.D@"d^\1Io~w?N "ڻuʬl^geIA>CH  #<?jv:u   •m@ f^̰3kk, d\.:ֆ6c\l`b=sYjɍ]:Bu-S[%'pw5AZA"  ّ >j}1#"dFg6Uyxk-ōob<3IhTf̋4/-{2tNaiu xk#)ףeN6U}G˻JUo^ڜHF..WF]#lbtƌ),klqyq6L13 ]0Ʃ6,[="ap/ƿ 7'   ›@բϞs~1ͦt ̢+sȲQd=<7|ag25` U=ƳY@e~l(!3 KƿVyFl|˳\~f Rd|?M/urvoXzm.(t~s+WWuf`Ei:ޫgʢVWQK-wEU~^asf;ڬ5)6ܴ5Lť}ת1_5Wh]^iQ+5t.bY֋:S^j^ae.Ae#7[z_qe[օ-+FU#ܺEfFh}$E!ƻ`+<z+&'K*5LEI3NǕx:=JI0fk[u$a.˞ÉV2g!˾^bP 2_y$ S V8r:ecE -9H3' MSVZ=d-e*$hdHJ8h`l IyPa.@GIh423/bkdlRƖe$0՚arY̌Ilpj9~ιȫTu8v%2$:→ I:sS}^g =Lc(JU(f<=@~}=:|d4`ia 7 A@ٴ\:IAV'%rc0J`GapF=2K}N{CA:c3ui=Ш3n׾;k= ;MZ(R[{aG<>M=@jfU3}J),"DF#2*ZaL?=c0JHRzpFTo#ujW&u0Ш{(Qgiq6Ve?~AfJG83q\Q3c\*3VuV̒x8 "3c0Lf:G-kzIFPj\yޮsͣ Y"Z5Έ>99LcLkb"S0x &!fh :gs*E=t[=N`GaW--&YUeFF=`Zǎ'<qoo~dQo|-|[%QTG4Ǔ=)===Iֿ 7 A*?h4V%WAA3gf1zh vh Ymh@ ~DPhZZ62("h^ 4p-3 SJ(jEƩBN]ŕPWh}cJD&PȌTi*0R育E;iQﹴo{0AչD}EvbQqP3Z19hfL-[?3LX7ODBÜa/j\<9$p/{E:Wj9=7YVniϜWXI F`"B=R6qhg|]!N Np׀}u@+9|aq1CE^ ogǏ~tp/pmpE-F jAAxq/ON/<:'%  F7FjCۇm4j&NNrsqq4*\muY|fiUO]; ǘġAµ6Ņf:_0jl\?MTt!p@'""5.Z<x~:wh::KxW/׺<|Dgf.UszdzZGBùz]leo?/ H*OpZ2WYAtKs0k?ퟤxz[>n~WTA +^ӣO.dHIg  +ajآՌp︋Fݔ|={m/?Jɉ~7وA^  fAш{OШױە,  VɲD~gcLZqN3J~_ϯMyuG?1XDZ蠅n q> ۟}n1 j}33c`c8Na^ YkhN`|$4ӬHR'bl}=F1d7~iuO}wxN]\AxH  &Z`0dqwgG>!  Vɭ`xr/4jhN] +0k3OM=Ӹ}p{x޿VROE# tTA`8$FF bS&(TDF!2t|u¶Tȿ>l (R Ak FozEI8!gp{(2YDk;yC*nxrg:x~:uMٯgs:јgϳ6Agt%KR88[RuF32!ȣ,.\n2;ˑ[G[h4j8a FH  hICA<{{~$I$ =ǷRnS3:Fc0J`GAmvn$eb[ X=9=_x>ΏnS<\2{rTqUY=.dq<4h F$'q~$En7#tꈌZ[rZלfஒK~fTkT`6)c0jPhʌV/esB{08t"]3]̖ HSf3d6j&F9cn_w6g+͙rK߭<3R'}FHmx1IRIIy'v2z#Fbu~9"0` )N`f4롺Cnf6ör.< =IpIV %ث4b'~fljC5ua*>hnfĖ13 iDL$<-vZ5oY5A.9EK)WjgV*B|ku Ӛ3XF̘!_ΞQ54/aD@YQ HCi].Xj>[-W=`gFzL_ VJ)h*uYwX`? S$ϟ:|')1Zw&~ kcV#B-20&A`?[ĪA& p8Zh$$ 88'7jI |&5?t[Q7'0pizιbHW|z*}_q[B C;ECѬ;Dy=@Fȃ@m0 "?rju{9RyhY7xZ(O!Tx&a(qJ,\>\*2h?Dg\c}f25h`A(Tֳ-mK/c(p稃nZ ș:+ :z.Lizu:ϯUe궗sE?ᰫ13FJ[k=c8آ0xvZQXW6Vo}X#CzWYKĖQPqEր; z2^OJ 7}F<5 t:my7E4N1%Sf~ f-6O^ǘ ƿs֦졉7Si6!kt0'"]NSc0JpO05;--W7= )W SFbyV4R7Np7H0[hmbSC6gfU<\Yg@fLҰCn(yGzG  &CapVh`*IX0 cwӂ$oy0f<|n'C[GuӿWkAdə猿Q'wok p}H  ܽsNx<?k88sCAZ-y?7c0N0&HC=8kehy9 f25A.EV0NFan萡`5sG)za[zM~VZTrsֻgJ m,LI82 #v B=R W>} pBk:njs;^YM_aq682"t[fYg^乱:Fo:&jyBV0AfVI,O>~Q7[:nG "#d3zAz4G{-4&GK^w%p!ISE1/~0 BUE88:h>hՌ]f]5*I,Z+BMD7<eߣ`XO(t!cr0'&Ƅvd/Z??Tu`u6H^ǠY#4k6DqnuVd[JuÜNPAg~9=ͫ~iq:Gė7p5z-rt/R|)^^wn ͬ1$P#    kHSf365-|_Pp#Nq k/3I#CxG5 |hq6Wލp"t0ȍ  A)O~of{LAAx F 'f{hß^:ց) ^L+6_%^48eazM[vݖA kj{·)a0JjbbFdnDxp\G8{1xb0EiuN3ʌy nC8P=C#2t·Q&+5 UZo3_BUZn1{na[fьf~wuܻAg}_Q~3@Vds~W\OKZ~Ϊ[0$. =|~ޫ|p!$ցٯL_|?Ky<"* `-B$Y  A'Ogx)߽s[>  ׎pa[h f`Hl gC!h{x:RJQGKJ*{Iļb+[A=:@8g w#t[JvtfƉ`w.׽Fqu9`Fk*qC(S;ȹaHk k11S9by&IB+^ݎyAdP{xz2FbG "oa[/f%pi{ B:Ã=,G [u)1 C`*י茙U)$z(2FTE`sΣiЙX*XE0/ߧDi0%8=-c]-9yoc8S)q >)ddb?^QQ5E0g?m#ZLA Ag9ދ'|tx A  µAD t0b?ľ$I$)Fd`>9>9OU3anTY`E:ןt"ó=^3ЭjyC9*EH'{Y[TӴC)o`M+d3fx;מ2De {mߖ!-m,U5qbec^A,zC\4}hmHHQMg9>شGi1E !ۆ ϖ׹hGl 9"vd!2LC%Vx<$2Ckh嚡j YR^ KQk7t]w"ZS#a3^ȤMg86ش m@ :c(5jZEgd3 =Dte~7^y{jNV;ߺZ5q[#EJAI}/zy5ñ,Z[5Dm0lM/V5Ql8b`& I vBy\fcRΣ`}i[5X@-܎ĺ'-ϧnlfŜ:XpS;1mLԈl jbw[\#MH<*EWu`N+08 6H(lպs'Y |b}nur7h({OV0j[CNtcs |ԨƠE#7l}szJC60P) &o_+ `|#ҵl_lZ<|Mkަe>.u[i{靭)@=sm'FC;2y;aaUs|ZOhQx\}6aZv7iqzW2{;gSό' Vpl p8ñk]j$p2uD c   lٱA2Cǡ{l)v7Ġ4[C6x@jٱxY l@j;+hf 'J'l!:ZZVt"P)9+f?|[S4xؠKUͻy 6iӯlh ֽpn(։Go;ڤZ' c%A> F+H'{jRb0[VA悝GVBxIЪKfbՆttcRz׬5qa ~z[[4G<2,)ޛM'a@z׃'B:_/߈_|&$R tGV )ٻ;̇Mza0,Ve2t׈7ď-A`hw{F3I /맮Pkjq0p84R p8xJ3mf/W[ܞ]G7?k:gg(6V"Z?~>DBiM!0Y*Y*[#& R@JFBSCck5 M^m;WGoм{+ ZA X^9^DCE¹l0дwbgdM4Vw(TprځڤZR%+ vNIHQ fȍ;u~t6k-jm^\@!!'ŕY_ `qɭkYW9q? z'ذT)TJm&'v藷w 25C`I˖jꭈӕMoYIf&4@ ]*I Tkez7tL:'Ő5/A'Da[9] ;T z$46beX&s*v;_n[|Ò|fxo0Rh Z@5Ty_m0303b:G-l@1Z#Rn-˷X9{|/ #a[FGQmjƊd3#׹^E3ٴ='Ri{aA+ٰLpek ՖfnEsmVdSGtz'ƩrC [#𽁓uvU9r37q p ||A6]k$E!C6EL!}Y< )f+F(]&/Xq.Ǧ|Z3/ۛ8ﹺ{i$YTV='͸qp8bw8xl-5x"zgjgOOSr}g* d\jzB[s\/x:AG8=LM({4\Fi9A9pMw"HXP*d(3N;ړ! Kk-~uu_]}/=ųS ٴJV(bFKyFHv7Ԑ B;+ǧ=wFM,g :!2m ;^i;TiN \@ 17L#k X7ժWޝcvnم'/]ҹiFVg :Q63<`s81TʊR1hakf&2pWIBJQ.f8qHR̊(sCqr:e60Z3|V :C'6JL3Sr2/Ը"oz3)s=1N.lZ)C(2 PeF[Skif*:Ku_WC Z2mU!Ln>I =<~/=ClE)}f?Ų tSGGv!X(dc a!^Bl[:[o>X#/p$#kz`5 ߗboakOj p~O2|I)0C,5iy)݁ΝZa5wgoo ~]22I>!Ξl1ز$u_]B&7^mioc#gc@q}>f8}0_0ͅ6j5j[gZ lIy`hwꭐV;AH(އd3lWlbUs}ޮ x>i^G8,rpCbnyQp8)`YkzeF+s|s&,Z5[5 c,\f}jm z>?|7]‰2_ENόR)%EjŞDeb<kg3 s&n!(?9?~R |8|Ogc8[q^)ZJ;.)̎MMXTRƶt{槯kz}Kg'ptӡ<1hݷ7aMl){&s'G8slr=3Jx'5C)#\;[{JCs\L|/| яp Jtp#յ5nw/Bep8oƉGޡ yü%Ogk`ۑ2='q0ƿ6y:6O?~Ü9ZDƚ!_?.!ņ{mCOp!Ţppg;p<]~]ަ s3G92YBJdsiۑ2  g|[B21Yqwv=;dYO!LBuw Ҳ[}IBCbf}*2M_6_~*ָxvǟBuY//2U#Һ/^r1-M|d@1ϪWoWNK= ScPt~<u=H813W- ?NG`}H)R"o QY;xdr@ CNzfvF ?{1A׾U^W9r qp8p ! #fV }շͭ{+| '/qpƸRZhrY3e̾ǽ;}79wbϿhmJg\bedaɧ?rL}BȧXgt=wF9Ņ3?N;MUxR"Taqj0>Z`ÓbȼrZ3rJ1W^wM=xpp804Z$I^BP*)]/pT5lX^Y`n)TsZCq2ܟq16(?jDY2AR>C&fZjܚ]~k78v¿s}Vj\:s u._pjfm_2tz3@W5^O]ϾuOK|Wxyxqp8;Z}$Zap8Ve֪!V r;7z={ϜcҚٕmFXSovZZ;b.aBRkFvu;sUL )eSSkYk6j̈́6&Eu[i9jY/\ĵ[\;QʦϖXc&(kt[ Ў WԚ>#F+Q zw^Mh5 5F3tQ?LgRg[ز߻9(Vhmvguym_XQ>h;v}wVxrYs;oxm_g4b6^ Z_$;:p:߶*4t„f;َ( <<k&Cn6p/؊Ozӗ9V6PưTYTxf%>ɏs³y:ξGPA6up?1NF햟y$aqiF+?UJExx\?:sǩC>$"H ub dl$C 83MM{ilh={_4CMr"wf'gFo>3SsƈµLRFʐ'&ryXI|Olce >5!7C>'yRܽ]k ZWIL^)6>>ak,.{<`eaR6vm̩3>qJp֝}}Y p8 p8p8#e/,CYMqrfdrJ)8fZeue)%IXkA9/̭$x{({JyA)')Z=}Hn!d3}{6=Z/fJVIkwawK7:ىyY޼6˭ϟ=飣TJ9`Oƿ6(14;F[1Ŝdvmvl2St-Ew_Ŭ-Ila`!! ٬Ƽ K 9lI!֠֨ma_^~fzی14զ50RT & y!+}l4NH=ĖbZo2=9(f״<_Ctn/j^P3Lއ*Jqu/@%QVk R׮B^xo|2Lrf2bnQG}vnfGs{AQ^po8xd34⻷(xesDQk (F~(~m~=K/\T|6Pשת3Z}2-oC^{GWn۔?=+M@ 2/ohvx$Y.9c={JEV[#<qOMӬbc !ĖRǹ[N\ =D :f~޲L|ęYWyF )CW^"Pm*~Z<~Ͻa2##%*9 %t8-*H#ĉ0bCo$p]bx  \A8g֛uyNt?2fr7"dh-6l5ݎluj3槯海"H^ GyDg [qJkD5q`$) _PkMþuL߿l;й.6M!ϑf*M R$I )@ ^B` eނ!czTRzv]t[&x~|ar$"}Cdó?Pp8J6++ 9R)r8BT K&IvS(\c77pZN0ަLvzNQGQیL`nנkpgA [c>mK[cjڡ1(ex>& K!#2lֽw8T&091Qfշͭ{+| |(ZN{px14MjFa"9(/fC~3}){8 5͎ٱg/w9b~sGQڐhhGlMaLjG3Rr޶Cؘ**-g/;e՘jF˂oW?9mwӊC'4m;V?xxfp*-0òL'JtV0ecp}ӈ=; oV OxS2L]茁Nh:Wwj>r꧙*}рܱ`;:vM_jKbΣCGzz K\| |cS׺o\ 5'$zG LN{%\Hvr;a_H!W,QpoLp<4S g.993·I95'žU&H/^3jlr]*G_"RMBv|8164fZZ_\Uf7?  z0كlf!ND3RsҖ7(rп Њ~wV޼f;QWZT/Ci6 + k\rZcc7>ąh CVЉ2al߳s' lGbV@cXiQ<Mep&O>1P#Un1?vW!݈=Gd81-\/o4R eK-ѶeE5qg0Z;ObPJEJ)\ƶk 4 ~ 5o(p_!-йT!?z3|,338Oi8xjiw:&[(Ip8csj۳k{ܙrl}zٌ5(l,.-Q7Wdj$HGh}A<<XmV+-V,KMNTxgyA4B캔}'LXvhu">2)7P{>n5vYz[y5ٓ\ם9ޛkem(}! )QDGִ4=311'v#˻3'Z=n-MJ bG-=2?ܛke֒(Be|973e: ޞwNCs~ yFRIt<kM3cJ2=g|X8-[Yqӧ'ˇp9( x G rr |A %pH  %Z׿/BA>E XpukWՕ9,9rӇg[_\w~ٺ[X][S#RK']t\I2-Y+crE'2{~2ejٰ^ oswn3cgy陣LRSqc +5V7j(%D15Nߌ፩Nnɂe/d';2\kM;`up~+7vku.{o^ cj՞ ak+5V7Sc1/R~vϛ~)Jڛ:Q[P*|,_gv:C&Xߴ<\3_,sgnwX^i|,ϝ![cػ6bkT |c~,:)[˵t,;_%oӷRTCǏޮn$9N{^`z\c!2ɏ)|_'jtcI֚5~ZY@=[ƿu#hmI~oc ˝`u'S\bNa\RNyl2mol"$'(3ıX $u{Zϱa=4ቍnprcpc$)!V֢DrnHG XQVN&ʪO  |fq@PlGןIJL%=M]nz'1DQ:+he,j&M3aaZ:~D| ]&q=z :Yo}'L}zfwW#vU,WZ+rJs'{gOR*(u2f%=karz3RA3]d4l:iyrkV=c®3n^moۭ 9?\7|p;fdkE.2K2E.E;KGQ;V7#~{]Sמ)JY]wI K5u5?n]y~:h7jإ݋5UήGEb6'gqdÓ֦do:6YV ۛ]nY c^z(<3ˉ#A{\mc-zfFPGœhUgguSgV];yl;>krPO[sۥtcbyzď iĎgOy|)nK:<6fj=@{3fluxO'*MAhx/dh6B|xR{k?RM\kx:+?/ӓ:Rq=pܜyVGw"<{D)BQi$Dz[V6\a(fScL:ٹs0Q])uƦ=5c` -ڥ+JQo#ǿz?Y9K>@q8ծH cWj2G旟)&9.r7w/nYmfQNG"k74cBO'e;{Nb'?}.3 Qڂckp֠i+IaH1ҨN%رYkPZeA#\[1Y28᫗g8y8X!,;cv̯nDܜ=x\sT]@AAA+8AA&6MW8KkeXصYԋzNRR)mP* : w"ZZ7L4 ѣf#YtP}osrs~^0qw-i.\ys<\qZWhȻ<) -WL !U!`aR* HkwgWq|jQL=6L'5œy$1m:e kkc޹jpockܛ[Ԏxն˱0Qo8 K;ȲO.s -ZW @qxBOMzn_clKG%i&ؾRHuN_F4# ߜl|k6]Ӄtnjm5F;$frSG'_ZfvBW~U;g|۴L&ɤ'0*hE&H Vȷ6CMKKlZ;jafUq*7>E+|*ʬrpUbg=7$7>??ޤOqk+G82#rމ n/Oy8(-Axb § Xdh4𤩭 (+yf3M+>}]ߎ8y6W]֭ 1^s~Є)>驔Ø?/g3\<ױׯj0^)GR/=S͌f[ 1WB>Q[Nh^90;O`:(B{cicy=Rjt`9T¹Ϝknffym_[Y}t+e˿~Ιo&Lt˔i7^J}IU_XaccӒe(eٲ5쳶vgk<5{ॳ_fTZXy~|+w skߺsb3rͶAJʫ"- k16Xވ9>x9;3ֳ{wnR,T&frLwRp^?Kgx_7qv^s6Њ/^,$ck:)P3hMԼ`x^o'$Nx=pt2#.$ȡ4vsijЀROi5@'@['qTo_2[;ypl kIXާk2$rhLk3Պ:xT]r g~k9y5Z9fs;>/nğv!M>H  㥒z07DZ|>/% (+++ֿ-,>tGoyҥ}ϓ_\y{McN3WE\_ʅ<^v/|7eBg(2P 5o/5~vʩO҅㚌\ǩ#yNlHXk I3Xn׸yF1kY.HJO8:SJooƇ`xïo<},+ı%4bFXѾW76e3=ҬHm oK;Tj`~𣟇~Cxl#\h~jSǻoj:1LJ7ܚ,ϟ9>㱱3Ns3`pqwl̸Z+e/E s9鿦qD!x}cT)sLnI&k+;uVYPff{΋?};JOyRɦp~6תz.S3#VL8n.IJȑIb '|?pzvlJT]cӚ[seyװ3Euج[7-Ǐe5t- Z;,YuN 3%Mqi`P 5dC^ݖJT~r|uҪ5C+U5kaz%Z72c3BzLh09+ -Z&G09943S\<=x^'w$oe|b g Acrbon|RI>u F;6{TC{??_7&2L4/YXA`7VĽgkj|~ۖks 'Brz514ڃq>ǪaMM?R_^otl ~y#|%PIz'GkjOƑŵwW rKg$y?1yKQVv6;u^ݴo_=23x>@^{X!1o·&<_rXb>\]k1\vu 8ѹ%_rN̽ň T#_] ΰMb_|'lT-gg=Jޚܫ!.Xr]惐qxO)߳C"NkׯC\vN< ?uwoDkt g20S-Cs51mI(ZhlP cra|08`moxNM} <H  %lV>q 1°ϻ_ TkYT\Yr7AҙŎwCs/R)j٬Ge޾Iw|,߼W]FYZtZKSj/>٬[?WL)"*rh2 Æ˛&rJ/e(Hx_ui_Nֵ` Q go}G]gjyǛL':olcaO>pncO^M5GvwZTZ$zBǿzΟR\:P*m!>Wmw$m1VCVd|n?fW\vJͲn?ോ>Ϟ˵[fA$@AAAvI2#aqq?SUjo]Um/-st0|ßՉ"sgbjhj8rĦƿz9҈@Ye%sAl֢Dr㹓\Iss5"  IZsJc QP˧Aa2 2aX[|?-SKY^yi:3qƎW7"/?0=fBUW6^!w8Ƒ ʤnZ|;qI9MM z|9Z7 ,ވ,뛆B~I c x.o9dΊvc~ tfiV~ٝ a+W_ܞop|Zd965jujޭ:ު1YT׳|,ؿ >(]|AքpFՊ7Ujuås>_zg;*ȧ;l]X4]+HYdTCkCY5G<Ƌ}b A{jqs>[1Dx .xt}w!C  “ں[[_7c k묮 # eTR.@ {Rss_D | o265f҆ݘ>lZQ|,G'6 sU>Y1/֗i{w1 =W*17,~Q{KW2="{[Vˆ >]# / xgzT܌f,~`wPmsq%:2g G& '!}윣ְ, Wչ|NwRoGcj??c|0v,[.ߍC63G/rb V3Bov |O~GH$cv/oDܚ=x\sT@A   rvEZu?Go_{C^eyOܧ 4%f[x]N/ӫuִksx<g<8k;Lf@nqsR޼e8^)fϟ8qH3QPhݽ2  EVcA'RIUմm{.ezzJ5vRׯƻzOk^:ְYlUQ9 G5gtC[b>rcN|.YF{K[%cZ7\gjeyVؤZк|5Zg~:1+>_)[.߉V܊!791 WlX[yӹsñ䤚^uÚIk0Ѫvah]MXdfjZ?kKVz8}셀O+ȵa|`j'dhm2ց15xt>-$$ Htl$aViˁ2@x8cbl`/acG<mQnğv!M>;6ֽO  1f4 -J1a|{f[3a_Wܹ{k"Lhny[Q@[wj͕FBC J,1|JVBDž_z:&w~!Lwys *i,,cld*1X);Cjh-`L& j b(,-Z[cKl,86 xFCc꥔b,x|y͑ M.DQbBX5pbQU}{jj!pq8T'V(* LfP 7d@ac<&G+|!s0Ud(`l޷.ڥjoueG!LiH- tDQm`1D'v.1 MݩŎ\09^`z"?p/=M-uz VWL|FuvU~aUl3Z}"vN9R/0{Tq6Z{ƴBqWW}O{2zJUV"2<&aYy׆k?Vu@&82٭Ba,WW8t0& 8l5ݻ c8lHtn9W+3L5twkZ=;遉nk ն.z3#2M-zJAllkf$ck;KUF#\ʋS`{0XR$A^"O߇4"EK+xn3V뛆?/B {xib  A"ddAA80<.ןiX~\P;+,.Wʋ'x+ll00<յ O{-vxs?x~Z#`eD,,W_,~7.}-qs I6߈,+嘉R@fd#LmN+ſnT89Q6Mx+v@(g^k Ŝ^ځ4RV`q&ܖKv 8sxku.lp{bN6}Oo҆acuiו&+eNg㸷Y2'y^o# ?9Uo ޿ES=nZ+9:}|X^'xLZIgͺej9~ Fj2QY͞&աVT5pVb>3t8Pg( *2C[i?-ȑgұjņb. 4\w J)2G.6 Z)NC,a)v8_qrlߢ5l+i/cSo<𼤬)0IU | V{1 Yhđ%hrX>C#cʑ |mkZQLh$;\yhO{KZ{&)W:;_3ꡝP |:#]jP Yb+5 |M!D)G6}5.Zbv:1tnSj7`U?jsW;w{ӬCg^GbLmJA =!)]o\uSth]^mvVA uMXA+ Em#ֶާ/*4@Cw^o#Y;ݥsKk#C؈QJYJ9T?;DR n9`v{[g$YX$%24 t٭snrq02;S$ltHӠ^ZE /MdJmgc| (uC٬67}6&mY2u9ޒcg5>sLhO*XݨQvTsHCEAAx"9rxFUU ) HAAxi8aLaPg~̄H5|Ă0w>y C&ML[v=OUi9]LPcnHt myֹߚS5E}FrSf׮ ||GJ-byJ؈ |c3cVGIF:.;^k+|oԾT&HBs [}psqQ/WRA AE AAAHH'=ض2 l!lӣ֤]u@s]sGz#9G&vxMQeu86ش|hr٤~Ľ|95]ߒ>vI6|aXۨS #r&tf{MڣԵ,ŎՊ1,j/K2` E  Y%Y#hw.[uhwdJOGQeꬔ"kr/չ#[t>ݶ CL~L; jb!9vD1A+7 l{{di;`3tV,1Y 8:s|酳LdATAA!XX|׷# @^1Ҙ#A)O3CO(n]SgT806o4 d_{ߚq.>!ݥQdh4״'nevz,]"f69Rlsdzb>@wn9Y#Ӵ󫖏9s\:Ę *  GZc_pfkA'U0ܼ+I RXG#2D%'& -NLy1.1$8=֭Cgk9x݈c߬G/WX^2}Lqx@!{}_*kew ,,O~$_xS%*A>H  (]͇}oUAP7fwJJeӍ>!#i' ($1[66i(<<_Z֡5zIeYԏnܿزYkP6bKif /fu9G.ʷ Z܁A%eJ q߻A |x{Ka^j:<Z+jols {>_w0suJr ɥυZjسSCLoG<VN\ljnYWnk Nk8{uJبR,d84U xtVCjF!U+}MnWǷ$ec-JwcbKl ڴW16mc!7Ɯ k}yΞa=B1VI #EAA@nC “JIA>-:fk<*65s-ڏm^q~FݵBkd:8d3#Z mns6=5 CP(g֩=Y9 e[gnչ%@Rsswm|w 3uު5iAFTPu6RKީc_n.m=)baԳ'ߨ,p15gTF}l3׋"CwwJ9FLP& Z-ߵrʮrhg @)p~dpg˖B!S'g(ojtB)E1?orČadH  ł J6ΏAFFoS֊\6Ӫ^Q횎lkL5?qR= h.j_AZ[Pʨ-QlQJx;x uERthtfFsQmw\fMKΡi%@uG~j[("جE>N1f&x !T {ɘNi y+P=&vL4hK: 1k7@*Zcf2zD>dtoB~~0|9BFqeNH 0z$@AaLMNjʕ &SybXQA'#gU)+A}%0b\bfe3:[>4KwJVm'upNJ)ԍuC׷?`TOZ[jfOtE{khO%v϶vqI?﷍^Րk:61Ǚ L JVI[#NmN8YHz:6:V+w ŔrGyBpc\ZF+ u6*x|Sc+gq 9@AAP(G4Aa fvUQJe C]YKRd|V=ya9dJb{x^IAf?c7LR!<=$fpc?k.eӛըNk 5b *'X%kZ)Ӡ(hVkYZswB!sHR!Ӫ1 gqAl]cWU p|:w^xlfܛر^|p+ǿYXNWsO8A AAA@~AO'Fdkifxp-CĹՏZ*7uOQ+:N{54t2TԿV鿍XgcV&?:m\[kK݈kX YPop0{x1ukc Հ{b\U9/6(W¡p[꽘^ib96x Ǧ4.m !  rUk51xx|.'AAOG˄JyؚG8lֿke4ͬ!Ki԰b袵"@kfֿ/<)z &i,c8C+:ZFsRW$ FԺthI׵^Ӧ+_ud⻑XKl\+*84cj<7mmOnO\@#v\0\n±</+03ba11ZZUqdC7=0;1ߌX)[Nh^|1ؔ&㫮9d AAGHZukQ.fPAባ10bqNH}R*2! KdvhVPߩ5I0@WiG;J/8UZeE %l51kΥ]ɚ6҈b(/-ͻ#Ɯ k _?dŖ/6VLfyzsG5`/UA%  j#y  y'58͞ﮣԿ oA P{p[ au ١IGr,{ I ־& tG6hV}:7= ]23U2&ф ҪYfeDƿ~nZ1_' -[x:Y2u9ޒ܉5* \xQ\ 8<ɉ/g AAx4ю1@  {ѷ]U{/3tkq @qH_2w]C NTYUk?wޕ/6@V8|Ư)~] H   H@  FԙAÿ|66 pWcn9p3ZpYtګqs\휫&urm}lUC{p(A3^n)#yӇ5#! k=j`y! 69 \͙s qb@QHNkjgs9PAr㭺2:kWUln̛t5U!VuL:s@txJ:Y@ZcEy=4# ]:__:AS1`,Ci,(#^ Ky4gsV2)lXD4+1MZqa=Dj2&I'?bG~=[?JY`|o|.^ ƿ /   ibi5Q@  &^p jk=ެw̰*9s.__4gjj6Uݗ-pr6fUƸu>k5Pތ\6z-&㢔^0eUާkxcjit+Nh}SufÞ UYPMZAZs},8ypI5u]2M+ Zad4`ftnu• D@;VhǺzT-7* Q4Z##aX0 h]KCRvkF 5%, B׬-2[<x|pDw3b   wnߦ$I,/75: )0y26>gɳS>>:Aė7Zf7d 7Be BMD?0rMpE׭\gLzPA5ǿ4^E@ZU@Q:C[1mBa1RG3Y Lpxo~3g<EA^1$@AB Aד4|QHۭ|NEQZ f3F9غ3煛gz;:BUfu՗@p}PIpPB+y&]4o?6J[oHA^1$@Ae22Wqo _˲a>c )+b|n=Ou!,Ee_uELZguC$kڱ(/ur"/,mBR&]4YA J~sKςlj_֠ "  $ GcF㥟KhA< W› E/PɎ7 `dHu;iק3 8O†5mWE5ǡ4D7j7i-_NZק#?qwVb8|7>@|Tb<;%kPW AAxdE!A /9qG?&}A17FUw~"~֊ ]R`P/k`}3YK:@_(ykjMlkk9HaBBEh_}'~ݷl?]p=_R!/d @AA^!Ɠo Od~ _0&I}H FiDnĸ%J0sӟt,5 /_l4cAVܿco,GV0Yo|6^{WAOWAAW(.M^@ }:;?4VZc p ƿbQ;QwoBUC+dܴ֥( 9ץ,{> |{cVtS[ 5 c?mO2<=w4}yG} |ڐAAOZA}J4ZkDQ$5g*]13cf*4q#TY_ЖWP" Fi]t+3U:G!wy``:@/[ӼS;k͇kיB[/iW̿u$cc O޺Mjq%)/§ AAxZB' «@ӑ?= +ɪ %Je? ٌ[ ] WU%yhi[ZUQf yU&auAfo0֨ 4PH3L4Ηu=E풑{98M%7{Z3BMPUuk^#j k{g{Z;a /_ܧ.h}a6/ 4^u#q/k8-7J꼼 [zw"UWcP4Sc8/X W *;^ ja14 `,PF`k`\578:^ǧ N{ \wb !AAxhFQAAa u֛/I&# src:77o09 MFI8u FseoX_JX̲cor07J˳GZR:gIXc9xs09`wnLN0QZQkfaf 4 c0՗c_ky:~<0anٰUue 74JEZ1, k5k}A@ukRpV8X{Ag{Qk0C]8N=ƙԚ1Jڰ׫3sEWtB}S9&WkkUyА3DVG1'gScPZF5†lN,bb nj(X=$cd9?W: ʺY7-n8k8 0\ledpg_73G BA^S$@Abӓ'li|~o $ p\0-# &I0IV4@i1|ќey~ CVʺ1#/ٛOjLmpCeyp8rX*ܪ_s4{l2ȭ?ڥb@|꜖(McjI[3(upv}e-tRًOS=ցx޴k}i Mk?|rvJ&ﺾ@zriR q޸^l|Ѫﮠsv٥Vk8pA^\xmJ<~pv |{;hZ6yigtϳ v̳t SgOqY5@# 0xtfl}(J:t r|Bs_gSQ׋'{8a! Õ5s :Jj!A AAxŸ}F1O쏈Ajo  Fj1c4-f%R8iEk=%QԺ̥-!@[]08~Fri8w_LU 8k8 QHC1Pc{eMъj4+Sq5^$-ADwbtay=$"VՍ_VwkǤX_xl 8.-?:5uC`,|j q@œ*3^k7]Й(B(l7ۛΑI Q@ר3έpA"@a,eԞi%p8h 7o\ĬnehjoY|Ww1)MýA׮<_S$[w(w)E`}iN>zdL8?{_yoX++s@[RA^s$@AD  $f4-1IrdAh[CDx FR`ץ}Fv@{hP3p5 i)AkMW`*vM2*ɐ弽GD/5TF75IZbHsE~7i*ym2tu[CԁF;nֺΜV /f P LqDX "Uۊ`]sEk (h6@'e'ZU.^&h)GjbiVb4uzv8(x5z]"|vu:ZiIz=̴k* ߃Lv\Eq9ǘ&I0=3/ۿk51eZo@vii= o]E@љ0D8y17;yu5SnsV`yZSUf?}dLaopktnc`C տOeAAAAX2&%lfюC N7xj4_mϫٴ}S*z69'Еʱ7[6}/mq<|ǕGd%4G^<3 4]y^lz'-Mu48ͅnҠgKpSY /kM[ztD#LtZ+btnBg{xA'Z.zqy齃Jkydi|(8 j[zŠOTpD[uC4U8!RP5sl}|Wa?f-?su|[;}Eۏl,qKAVAAאpIZDQ~8WhAA^C?3O)ܗuE%aEW}mGש{<:`h ]tZV;iuy9V/Ӭ88 EOx?<c}E+n`] xpj ~qpp/9^UAAA׌'OOx$K?+N YVK*" ̱g?&&%H}tbp0łI5yIƭ.90+n'k09p렃N+D;"Uי1J%2Rg I0&%޻oqx%|K|V2X / AAx8;?4>~>AAxIL<&j-pԦ(NΦMrƽ[} zQв oz? :aQ Dכ\nZj|aa E-`}thu0[ƾ:L^X<=O0䈣o#5uR/dOsut_wn '  Fl2_$N# 93,P0c{7f?;h4^Y]EiA* %Eaqrb2-hztW/KTaʂ3p(Ói$t@s;k%[9o 3ύu0Ns '9^6ˬHrZ_MZ[rg) ޸Ǡx}{5/QZF3>twok/ #  KA~%&:1pcq'ySu-+LT}Le0 c$0xf jܚ\][¼'͏VRTznVLIZ@)BQ!9:lЙy9Zu7\=OkJ9 5iU]uusZoG >gIn0M{-=`¼ͬz=pC<%OrPn댭JD(J4#7/\1_Ka{DRO>z ]|sp)IZiMuЕ ׌  F p(yU ˰"MSiR4HDFy̌iZbs P0D8k# .#̀E^ EY8j,Y[coY(J$z֥1iV(HӊgdҬ@R}YKPY-7IH F`7([A>b? n~m|t8Xau O-~'BuR9?X:0PJ5~2Ͼw@Y6-oӖDADAA^#8$Ix8dEQ#1Ahd:r(K1/b@@7WYkbrIRb8ɐa荣v" MZc7q|0 (BW $M@Vx-! { Zy+_5Tz|%c3¿)"v.q ZIVb8`DA~ V(К륝 X;7a ^ymg2v5;ZZц֢mVDpkYYsZgˮjÓNU IhB;lX̜޸Nr$iN;:X+<,tfoaܺPXᛏO(B+\Uf𚠞UKL3FVQXk=hw1ϲIR`8AڭG{A5-LyJvYӎaz7aCϔc^vLm}iq.};[޹^Vff-?c{1~+m}; hdS=:`0>xdKW   ѩ\O&S.M 50D_%,$Me9k?c?>Os7DoO2hZJ-gl\W0MRLD o Z.e=;vT25:8\HHҢAێj[l_kz] oWd~i %M&)cyQ0yy jO73j8&€pN;j53upmmfpE^_Voq ZkxZ ]P&B~~u@8qƭ>0@+,DM\. :Ikԣ~*saCt-|T5z ƺk (ßOXw/u0ƌ#Un`%߆x|f/ %Z;PW}ъOsuA#   &_4fF$Uv~ܖ@]e2rQ($xKC,I0и{àWnr[BE xlOěZBwYﯨ ªs8N2p|^/sskSe33LBڽw%Q }~ U~!\(EPxZP\tC;hym u0輢8e  @\ǚ:;a$)oߍf:ӕv:9?OuU A*B=Ӝ?*𓇄~Gw{ovvSUғ|dJ|¯}2AAAJ #ZAv~' +td:h,%c, =~½^b \#ߗ7ta<hzt*+xm 9`YE+h,e^Tre3FI3!\X{0ufeNJ֎$s^~(O2J(pyp2`_w_ ƿ򘷴xuojx8WyqbXUBoWHw̰Zo5: tR˻XB ί{At3p6qxg%YŽ}j{fӎ! M#  <|7|+1zHc9ff / AAvb_ #NT̘NO}-C" ‹FAAIχ 8%`4d2b#MSIKd:ţOxoV%xvN2AjюaA+%߷7cXkaAZiz%~t32soy n]`ZMB_=N`!Uf軈/8;s"쳴)h!/JDAN;@h%\/XWMsB2/P_!/8P+yx K˘&Ik,HhV쵦: kZЊ qth,~g#֯5zm>Z2c8e|G (޷csK]ATe '  [nG6/rzvNd ^rs8=;dz3>:<}SxIgiV"G[?-Zӗcg1p j@W;1ٍMiIJR!EhE J1iΈ"FOsY{tZ!JY(/ ̺W31Q)_6dX־/yopob:$ADyCx>FpaS8CКpA״"{ Vas U?oazޤ[h5ޘ5:;FZ0(0g(K(8>x_栎-uvpf̣j8VԸA_~-u^w Iw߷֝xV%j _/fщEw~_z[!ݖ{ /ߓs>Obqk0۷Ԭʆׂ̿ K  ;t6v:2H 1Ny< 2W6i}%mzs CB'%̙H :O2CiMց"c*W@]W} ZqeS3~ۣ0֡4IZ`8JUn0K?Go}bj'YҺdNF?{dŭ>׾sw4Z!]'.jEAxH  {{:==,W{{12(̌x'O[G OS<;=<|;«4C^Y':sZXkAWK+?}UON $i$-Q:n>OmPX`2gt w~Hxvg'(JF^ `G˹MW a <Ï?|F7g, Y׹:g%J˗'ILSFaY.:'@+ p ~hG ƿ "  P眤)Pі>ւ'Ϛoϧsp'^@! Kl,9 +Z9[R,Do:}z@isZ4cٯJ_(8đZfuH~pGl:kM\Ygi0Jy19 ČnkK TW )J0{R\(W*χ޾ Lg8Z̑^Rh\4jePm|KݵfB:+½w+Lgxݟ8Y;PwщhMAW AAD |<{vb &)>=[ F~l{1L|v" ^﹎Q%N0l p t :!-iPA }mhf. 8r F6_h)Ցٗ~ύ>szڣ%Zfs&b07ڦ$u`f :~[{cVR)"Fэ ͙Te %kJu7a|MNp~G{<wQ># Aχ[T=-#s~[a!ġs(U\ 3*`M@uUkUuOLkM5*u|vY+ :/i}F83@hkJg4d$H2"a_A* Q{gZZfs(QG(׮֗W c@o6woFn{_l`ӌegI-G}N?׊/%C   |RLSf@dyCE!)4==2IK߻kU9ӳ3? DںRgI0jt/ ho}rbaCM*kƿa`3[^U 9W(iu6g4BHth1:tZ{]:u8f`FQ;v5˻{9of7q=03sFx "GpH5n?0Ҝ .6Y0L,¡"uK]o\[\DU2)#)Kd_tĄk7, \μܐäOJ;`;O =HS-B__X_?/N,޼倳q@խ;dA>-_AAJp$$Hχ(R5ڢyE2`+3,K'!Stc`n!|{eLRg@?("y+g3`#&CimN*M,2Tuf8_tb@>l|;˾aaXюw5-fukf-Gg! }ByնYǀi0Mpپos*,3FSe (޷csKD89-H |ʐAAᅑ$ޛIvϹ7oUյnt  n H6Eeay9áY?pXPFEXn]Ծg~=UUfDTfsy9y+|,g$p'ezzti--W˵rVR/;- mx>Kج&1{BgBX&31pֶfPv5O:CRD6C5Cx|V34A EV֔\B:J*QEzz5XekbEG:9dEci%hiFzK]=#5j~ݬc9K\h뼒14ξ@$JbEYTSRy!u^2j)V4*QX%2=nspiVwPxNJib-. Z bIHgU_r][fm>g{rcܧ?c#!nQFo0 w&`0 pۈjxdT#vLGx&&'T*0,K⢵]M'0%ףPr\.3;7rdDVˋ& KŊ_Oq+t%WȗIVίE In^ }M=+ 'CÁJ)ȼz>n N4m{ UC8_;l+XjkMee)(V|\f"-"xQ&P,FQ%YZ[e}]YEU5-j];r2~)mA2RD/\Dk$}_z>XբχBYS("V$cp`ˋ5v4vSKAAeP[N(+k]BYً,ukkԹCܜZ6+lQS4tx0KB"u}|A_^Pҡj|_HEQKkY$ u-EqVh KЗ5TuCJWם/8^BT\CK3=N]qlX^lP utWw|V<(֧/ؐB v=s&3;Wbphe_~`FáGA@$&sý a%!qR0ׁ*"hN L02g|*"e\'ܞtΫtۊPŠkuD(cSFKT3IɪQq=|?(EJnN ,ƗZ ZΖRܚYӺHD!(lKɕtcEqXB 5,lX}J䵵C6I}upټ˿KHkAT5ݜd2no<%mB}p) \W sEhq`8yȗ'8vX$L7AБͻ-I=3Cߔܞ62龦:VYp[G KԖ:`0 `ĢQU,$7mo cxFF[c3Z3<2*:j0jƿR ydl2]{әXW:l*0ۻ#D·+*eAEl\سf=ߏgeDʂB4bܡߠr^ T;=N q%d^fh.+1Kh]{Y&'ymT\!+1=[\f\lA2r`  `0n;5D"cXxGFEkm:°fD$5a}cf^o_d/N6edtT"|ۮ϶GgA|0hVLܵ'Mۅ2c>֋ ;ih?mK E?dCR-S(XB)1~U^bώ̽ۑ!Lf]:1:;bm^ZJ?g)ŏ^FϜEZ/UF@“3fJߕL Zڸ c(D̦+%ujvB4\5N×/s4ےڧp`WZbW3, h \` /e]/`c7{K)^x[ᙇ:P% "Nk'g ٞ"|-Y6imKiwΝ(m[KgXFkr%<++W-r@]w'ܱ/3 =i4z,XlKmN_£`nYoCrջV¡nK58!kXi>17/z|8'˾RpQ-A6 cr,_aDQ.7{El2IFAtͼۭ)R-gsC_|vic0Z3/3pmQ[>|O^!Lk=R[' 1  `0~`Ei\eXY`8_pk78rػ#e5 ]& I", ي²шvEfz;VAv%*p] pՔSR`Yy_1 _qvoOw }FꕔpBBUUݼkUmAp"v-E4lsZ^%vRպ`ǹV mt ]Z۹BYpB6{$ce-k| w|Bkӳ/7|{ns-u&3=_(y>Xd<2/qBj[Ve[D"HT l4ht(ZT^kYA?J!m^JgE!_B!m1RtTSZ+,R/y9?׿p])z;־J=P6M2& .s0NƶѨ<+(+~VXyni$j9u 049Q}_t?A E2!v]P|_F>Z^O IsE xrKS !U"uc<BضOX :xO͕}M˪S:Εc|^WNr`W6xֵrAKM[mIdCvN/&-\Nο%S+~ZƲ/.z\KϾv7շ*k-Z (9[})PjICv^EHJVe5jf յj͟~b.s EBtt%U_˔x"W{|3-HX@-hFBOL&w:a `0 [Q)* Ji40 cÆ#" >&ڎŲxz/'?Vze `ksEPm u|!]"^מ=gMı/\}͝RL%؁/ R˷;!.}Y1ˮL߼v7N9|3} @]5=Y&̟yCUmZ]^W׹ʲꈐIE9mE(<f+Wvk V|ɇ+ ^kag'|t@8/{lAxa3W?GX:ɊUXs~I!uҥ6F=u 7\ v "NPN'T`۔RRaY֚SjD&tYSc:\KG㱇Orq206`0 `R,eessN/wW:7j o]]+ DHF 6+T7y}0{t,"au+͂R- Jnl20Tm-\/"F'qf7љw2/ ֖C?WcᱵQj/+ʁQߑ IE ; grEndy\5Ξ_ :S,K#M;n])Zuu}jeasiڸMwg_N9t2je6mb?WT!9D#ܞ2Dwҽ&*32[\2۸C|=ja1`0 -C.ܒ x'&uo\ f?EBj o?ĕKŧI#uӥ]Ma4`Oȕ;]af೹23s~tc|C<|0FچYnye Pr"tHFl/s 7gy(箎ә]tuZ͕ѭZk|S:\$ӅPv!& NDo״.008;F1<˞4d,"l[m4ley04s\}]|^NNWG,+QYCGGXj5A\|qc1ͧ߆}fVI$\0:yCqׁtv̍fc `0 [-zep `&\.0l.wPgZ_ # ݟO^:Z>ɚ/%W(WXVL ҂;61!j?|{aóu'~:b ees=puWZ++AR6DΪk]rE0Yz:GOgnho96UkPz3K5]@$(a'nkLQ&r͗>y#{[[RgsR<⟖ABY1sug,kNuǶEu iC!k=Ij͕'Dd,L8lc[Ia߬}HzZxϙ%cyCe:`M`0 `MLOHT2a|"{brJu/>4c:7x})>!졫#0 ߚsRra;!a Vy9syw skttO<#{LTKPuL\`%/Ca&׿k^>4K:ᩇr^u?\sz KhMa۶pV.XvTuC{6W潋8d'dRa&\Pj@?]_6<3StuCo.&qe\e-2;WFDH'R:c:iۨ{My\%}gy{2e ێ 0 `0lY5m2| f$ϛ0l:,2R0y&gx c9O]2؇$`h< Ka&qu긻3F<&d[|\a: ]奷o08e{O<~{:ıg֭ jbl:bN6?{e}Cv'9D|D 3P,tJ٢)lۦ#&u5T[ Agɗ>y;2t(e{ ΋_jnWF|\)vu}A"![Цߗ)m;%;p5KD-s#Nw&V] ᆵ4ɥ!04'C?3e00`0 -C,URm_J&MG>p%1a@Q.%oA?D4GZ /_fb2ρ]|ػ=M"!$ jig5YJ @'סu?}yv7س{wvOaR`]-U4M/O]3ַd,f&[ڭJ$]1"afl}T+ܚԼuĖ9r8WǎU;d:`%c `0 [mjl|B<[\"' 8ffg?>|sDJ- e MSG.rGO({wt`Yj^ݵYhGj<xRx^Цi~P D H0 #3Ϲs% I8g*}]K׈BCp4?x"篌{{ܽ;jzm5R`zZZ,q3/^W_+\K/>R_}(\kYZۙ!~Y4 's7\%O%lT=A?XޜABg;/=Ƚ{:j7۫9)-Hմi-V+-@KCgiZ  ڒ6: Ziкz:0:4o\t9=qnLLjFCV`lZƿkJӺ/026]z;]zZ ,Z{|? %mh)<_V3A:kO/@_~u|Ƶn3u]k]?Gaps;|}فl.ȢTv539>VƣшÇجoP;hU`J.3ŒKı`go< 6l}Psqo>9N?N45n0|@0`0 -Go6U(T*Ec6X̘Y$^S\Dg?gLT^_FOڰ/ZIXƞݻyW/7???|P׮]x}q)|}{I^kyLO5YkŶ93#h;h=_>\\\z*eX`Vd<[)9痸:8{-uZ.\'WKDhem:0^lVd)p5CU3ԶQ) 6Hc~Si$KG1E)E"z>sEL fJZY#W`[V۫ <׏M/;7?#y(Lِ4l!o)LYDtzn/D›U Xs~ϟAWku.E׼uO*awNqxWhdiS_ ±UudjJĢ6EkI5E#zMO+TZ8tuĈED`nlӰh2KyҐG^z8r=|ǏdL PL`0 %ϋh!/ \.|ߧX,ڛog=榧xpGQ`'Dl$iw[Za}6Osxeǰص{7 xo{? 7n`%ŢjzzZ:;;w]&ﯯ5S|Xw}wj_''~E^SNk[_|qYgg_xmO?|k_[yՍرcJ%(h{k.("lLgݜ}:?Su%4GuHRwL&ح9פTVu#,T oF9c[Pt-ZJ  ̏+s|nL;~8;'4ŅAynn2sBD Oy[[~+OFYPNصA\A3}!SdR6HXS٢j y'"d[8Mhu tNX$5kPg% q>~_NɄE:aZ}C3f*[Y_b4`>"(Bc|kN0W|,m+fB"HDUKWA}s^05bYi^toJN2 jyVvWJܿ/đ!޼uzmAep}Ebo /$a1h$T23lT͕+>}Or8~1 - 0 `0qLMMK\<l\z{/ czfCu=Jɩ)޿pnrlOg]l'j)Yn5C@ꗭV1ӡG4td2{ES5k9E03Ӛ9L״J^cwttP^qِo.H$Lz+7ݗG!Lvn351==rvA$Ca0(a_q~}JoH;Z4\G/_ #ɿч>8Y5 ՚wK^OѾ*!I Tu YXZ xٴiBXDט^)dqUGM)1w(ڨe 7&j5A<QkCJ\5ë {cD#>>no،`juv=rŧT(W+m +oDV}| UJFtZ5[56=ԔjtMksf5cKC."GS<~$Ξz,yhE32YS!. ppg1NO2blvււkJФF!d])p e҇W4?&7EFK@ L _F5}tl}ҕvV}Mfs7=ĬfOsF:-1QؖEav4u=X4*f._C"& aK鰆eXsyreG 0=>̶mL [`0 `R3<2*/uS 20yc\t^9#W9w !|h{X0ܚylK])Ǐ;wUZlgbrJuw?c`듼{aYz|C8L: ޷6Klq2LロX,v@$\ ݵ{]zOA4S%N__\ I_:OOW"\[_kԺe,43cvKѭwsϨhP}5Cw ^xXG}jM %K7xB||p'w$3F@kpu2eB< fܜ[m֪I媎ʙX |5cs(Xy]:W++X'< o igɤ" M9u0 %\§3aSvoi-[Zvy=P1i;#{#5!TXZj. tȔSG궪[[YkT33D˲Y|_ž3+3/R0Xpخ`֛[׍ϩ3yZxϙ%?>8@W]`j`0 r)E4 /l6Ckͭ!.\[o% {:=B8!UkY~XhOd}ym)œGCﳉ6Wu88JܜS)}{8un;{|8r`]jՉ li<#J~ͣ^[Xwt֦w'?~/չ\޻O_ʕ[ ߷d}HpbkE(W$̗#ԞwH4ο$͚OoB =85-t$BWutxKs2u?c6i5]W/S>|nM ɘɻܻ/Jq)5L5+fJ;9[_??[ahGOͯ&T{o:``0 빦 VNfxxW|Μ4{j簧Ǯ7V7v:yR`lX$`0 C8RYOchJT-w"{ܿC$k:w < ^2>ӓV0QHF-"am5-4d<{:yAXIM׆ftWNБg{{$Ä{^(kM@B}H,"6ЬmY| jB0[мs>x>1קsh~//p Gl~/:XV`7n1+Vtě@ًl슱k Ok~qe`L[Ϥw_Dtɜs޻25糣K=!by\& pDY! wmh<A4" 5{L a,C|~#N7>d`0 C:RC#K~3HMGl6WD<˯yGejzZco|7WΕy >;Y|öE43ioqvdo>:\Zu]FGGC)E__Lh4zGo Es.SWݖL\ZyjӴVAk5o.>'z㍋gNfi"NP<Zߢ!J"unOrݵ25{=Uq},LB-Z*ZUMi^z |~FѢo+(VOylBY!dTZء{Q]umNP^s" "e)"vgޞ65-hFgs4Y'$LN7>`0 E"'_(9HR`êV^T/Rnug"lK_$w$#-a dxr?}Ͼ~/<#RZ٬tt:i.ߜ^ #MwW/(>vZyAD;vK8PY-c>[5*ތ vq°?w'T2aa}W޾Ρ}o}=HئT.eWR5i]uɖ##XC)%Yk0jW[~Mjyvϯ^Tom t\Q8u5XW?G%΄)E6Ykd׉8̽<^I'TyE]5bsED͕)=M:;R:kiO<5OVxZSiV (SΖZ[\޻Up泏v4:PPxkZOk: rg oٓ+mN_yҐG^z8r=|ǏdL rL\QUF}bwDRjRMR"ilS[;ƒ&N(6lYX-ƮqȱXhylQxIW7>77*+dU @B P- +eU~AAhiiVtڎiQWM U5LW^{1:8oHns02Db k4[tG+YND[/JeW&C$)'Ԗ]vr5CY֚~Μ91UVfz=C}>ƛtL&I12:JV4'hjHXRe4kxehk]wfZۚjs lcx<{46ɏ⎛7ܐcsg\ $>.1:(MF48t jk%.+([V.>> -=jRx'(55j:x;c\ԴwFiH쿩V^cPxy,20} uu9[ߨj z s!9^}e+ۼ򾳹$4I%NūX$` #8{"L{f *?OG)Nu35UC}eߍܵcV3x8r %. oFmm, ߛ1:49Bڛ|>\@sW\rٜGs,C6|MqTXjv0o3mWn[pI&lو3H.nYi5Mm]cKnYVv!]Ml4c9٫9~A3[r^n޽v'W   \uoM&%XA %۾(ÇϞBIvlx蟵a[\SfזR`d-z5Op;bE3'`8/cl y@PY zG˳o{39rliPO*8H}}=wtV޾>?Ɔz:Z5+Ў\\]kmK{~Kާm^CKsZ{\fq<'^ ysu%\2 M]jbex&q or\H[æN7olI]q*K6gHY^`m|s+ \mHrt$YVpϩqau WM9c{Ìi℡RCKÆN-PUKK/ g9|6a*7> k[wU}ϛ| iR>5aeeö uܱ ]G|<79g~3;6\x'/KµuG~oX莦C51N|^Ƣe~+ڮvn,0?}_|snZ][K\.*%c8;‘sY.^C׺ܱoiwU2ԳC&CK\cS?m@ QdH% ҘFdM5"WPqªB/W*G ׏G?ѤطgSg|vk9YՉ \8rlLU.w-l]8uȄZKB0&)F5- >[V%bgʕiCxҐzdF=|ϝŬJZ;Sx[4EhTܾgKW\66:+f43r/ Y\eؿ3m;ƶbafZT+[F>b ͫbk`XPnHZzebNKfY6Y9 eˎ!CpkB.jV9ܳ'-\3[UR9` %?[B|&P [ѵ:"S^"XѾD߰bE'Yё(VX.(+U.w|~֯];})a.vܰ&'W9   s NK3A@6m-E*a|[xt^n=H?jxt['CiV8ܾcJi̡Q,$O!z+{/;wN4[loy0|m߸vmf9~36:uwݐiju5^aF*WglϛJ9UP(p)X~i,x].c{iLZW(e'yh΂r >:]9H:Lbs|#YN|[|vmNRT 7NPTHȟ1t7g,QkSXaҘ4O,I֮psWΔPǩm\4KLL[}۸ʁTP({$|Z@Nv-k-+K _]7P;0.  r:[x$ƿvU,蝏8x2,ܾccWv-\OG=%Dܰ)]eڢ.{ު]ZI}_0mG:8Z=<뜹*i˅N99TdžN6Efw=͢-VtwɁ7X-l5PW)^qe9xLBR5_,B}GH   ̒; ˤ3QWL _6Xk_7EzCg]tb׺ 6z$ǯ kV(ncUKc ,e r/iP/ɓ?w}q\466>0G~NBmjrq~9^lz7Í{_Ʀ\o_i"L08\˶unqwqFdp/A.5_bphOܹO޹?;葞_?o&kn!AWC*l,Hlil/m-h>]mccQ>̢&B3qtX\s|+uW7ڱO;Yo{sz}KVs6=sg4>={Rە)pѩZ ϽԫYƳ$FUL((2Fkc#9cٳRhPE,! |AAA% BEkgP/'8Q12:{=ŅheJm⇜2ۿg^Ng z5Op;bEsw`?a6Jʅq% < ?gۿ!7nT*hmK{~[{Y[خ䅃teʈV](23=o,T晃D4$ #5b|A-@s7ǯ?rZ~kxX 3XY~3>ި͏&3Ħx ::N.ױݍu1pZ[&𹈟p_P: U5ZA   s`I-$b}lvAE}}=v^y\ {z ?̿+6plabTUͩ䍟1ù~͋8ޤgU.^|U`26>&AA`Ϟ=}~r__ruש??NZ`1\s_ȱn_O;m YizF8z"/8Bŋ\ӥxF6pTilWڲWgJe`y֚ws(J[!Mg6ׅBe>8q/{g lrܾ{R~پu']3vRR&_/.k)TkkME bh7\1vfp9CHɞwNgpV6A}:'˴^Q'U:˷εA\Z˥ñ 0jS|+-5I򘹱LeJvϣRud+\{|aAAA#Wuq0{>--b_,ȱc?5? \ђ,AhȒ/Qaa$9yQ'z4MmKW,-"̞ ckz)[׿ 7܀d2%L=CEj+JEr! ,wN/~ۺ7i59FF\Љ^}}zz/eg?vWY Q`4\YDI(AycۣEq[TB6$|{5c-*[ӽ׎?cN"5a"t-&ұ7ǡYGslr6$/ 8/T S+؊j%Y *26: p! -VbDҏcRgSm*pNhN]Լ~s+mRP{Y TNe:c7~Osl[sE9@cㄨt389UmwYsaNAuiL Zc,9)RZ x](e1!f1!2kP6fZH  $  B46614pKi{k'y[wf?;;G 4<\Qm{Vykkq\(n3ݾ_>p ơ~'ncΝ;WUTa7C}/ӧm*lSv;߱---S/|͛7WK*Y tf񝈖5*g#N6G-Tral6+[[xV}+wpbDKbf\ !@ X &;|6;39%,V84\_މ{xᴝ9T z YۙǩDclOk"V62c8 BmB adp]3q20m:x^3MBb&kV4b|'%h]Yį<歓8lrIxNxDn^: Y5sLZ+gg:GgS%CwG3i`<Mv~M6wuFAlfU-:lpH5KmDVɏi]V1N,bg[ 1mfR;bA8~EFm,ÙEOlU28{)dz/O2b-} XVD|φvYVNai#  pQ46H2r`Kƛ9ݸL\ 3c![ CBsq@д8?=}}rx07 J)|s?Ϳy&׵p<üuk?>6o/o?XڿEo]o~g}=CCCu^i.ȟjDZ}N1Ugcx"f(jPA=&+iƦv'L=ٿc*/o6T2LG&mK/|tlA>+hS . M5\U~VxK$D8 %2&@Z~Jm̌ZG2hr~*vKܲtro;]6w49e:*imll@~O{&K:c3Hܟ3(ǧ>2UI׵!R.9ܜ3jh&4#8T5 :t` ɠTbQiq$(8| ~$궸SV/P0|[<*=Po&8RFvc?D2AD*.~'`<22rDyh&DYéjK!znv3 ["  _\ѱx=zl"cE$,Q rZS|J[(gaw~h,Sp?}N*?[5uj?;\wq07^=m#| mm u>ٲ{nlzZwtgho͡n{ﻏ͗e[yo?ˎ;|_ٳ{}}kav܉}s8pG}Jkk+> ={U_M{G8p<?878uTq6?|q?я/|۽y)~}~'(VxGx馛xٽ{77m\|7Xjռ7o|׭` ŷWs;6(Ec]bl70@*n{G#m^ˍTqL"åш:[jxfx2KOF.ɼo\.^wUB9(}>{;siw%|u6D|JgVo\`xjCYbݪfk[1hKTw JOS@.D4էhyR ԦWOot* 5LuAgߋnyZ iM+ogjt+lXRrՔj J93jWr~ >~&ݸduf`,׬l.Tkc!FgU9oL jm.?u~\*KhtIu{-&sU]8.̳7 W˄ˆޒ._e4SZzG÷u\3;e>ñT*M7qΝ^Jt PS$@AA޾~Mҝ뷝+;$ ` f8hoj +}ϡ!\7cIӥ8ԧ|:V/+<,W]en]Ry_Ԍج'nPX+bA?OAliiN6o;w,V (PP`eg'O>z'](_M/?^lS(_~e+omS(?]]8ٶpm{챲mJm2k}c6J^կl}}=-th.Aק-u }C(|eus:R 溪UHHgC|WRE1'}ZRBש$T\dNDKc-u5_OH%S p|b@Jѧ/U| qUױ-eAKc(_^rL'2״ԑJzsY!Kƴ<ݘvT\o?F!ŜA(-"W؈;svg\cvLdX,c9raDXRO]+Y|Y@)HT£sE$hKp 5:إPy8ӧyagkvVuuJ $]   ($R#;Q*S}Uc,Aْ˅ Pخ&.7rާ~&xhr uTPm™w{g)YjeqMmgftmc%o7 QqgZʵv\JUÑ;e{^sT3:ǻrPcl.K. rq`wgS4gg.XȢ{xWds g rqFRI/QzvQٲ[y+eamy6k#NjV— ;od2)@AX0$@AAfO&dt HGB $,*9M/݉Y=} ĥx[ft uSU1mA(^0qDŔ6*|?Q6x:qi(M&]Vl.L9NZ B> ?jxxđ n޻Ε+AX$@AA 3fMd$)C&dyZРk'|71*h1efCW/ B"m=T+. \ Pbudy<'^B:}qƳ¸VDD"Q%hҌ$.k;iK7B--ҡ'#WG>'隣Q:[EEAAa>|.Q͸sF0 pqX&Rܳc! ZR$|dFpe(A?V|FH}‹P|rYuT(~ : fOqu\#Tk6lqi(C:{k:hnH\O$b% ҁIͻ\ZZW{wIGGt WIAAE"J f^KN:j 166nGFG#aFFGmsS<eEygkcC{.j; YZ35 ٢gV5:kAGs\dwaabk?ˌUXPfcB lDqΥ 밲SZJ0y_)epr[s7AKZzVn:;AH   ,j``VJ+ZXckKh@-bH֎|{wmlڸQ:\%$  "S(H&` Nt ظmll&KrPk6cb3P%62U3M%Z;51BRȿZW<7N /ȗƿ-y,26##K{s:2{aB[OPlyMϰG׺{Mܼwl".’DAA !&2dd_L=qPJERcQ3yµ5#]H~w'^w\EzzUUuW_g|!1"(WX,c9raD2ᱢ dmm-b01-L29dX~ٵc;HR ,Y$@AAlfA$ Z[CcS.$Rb0g;?Z>8,"KR(3J[c^֥?˥B<"{upQ%6ZeBFd@]+&zOy3d8p"ToD{F>Oqb lAAAXF  Zd8Ƕ4S__/#jDa\A*c8 F" ỸN֢f'yיdXk!,z!.T|te:G&2J8agWuyNg#. dC˪&RRC2(G o8ѵ/gy^:WaY!  L^wYvi+)cll666CQAb&wU6SK-ԦmgA/yeaV2kkpN: &Җ(?@?ft< pYLC_y5ö8 -Ƒ|RD8tE:6Tl^w4muvEEq_Xc~L7Ƣ).{.-uK^1\0Cm0Fvlþncƍ | AAAX&z*N5RJ 0I.eaDNQnjGf;tkǀX4 .gh$V('Ț4Jִ:+pD@:fLO$nim0wW͗]{m{%Ja}5=]vp{yٲU:\4   ˈ֖fb.yQ0N9E2n6L\j)|qƘ j1+.!'H&5k a5ܡʶ5L,휎~D/39oO¢Blll1=d;G)|qJI^[hܗ3KlgUWTJ:]<   ˈz02:jsۉD&yQ#T`1EqGQf ɗ7rH$}I"m Iqq\]t^:+H&#:2 Z1Ǚ0 IX(z NDh?)nܹCA:$@AA!b/7 cL7(Hn\"_7K;q߱-=U+FTZKp$A7Q_, 8!f].dCy(0}w :ga'i]\])|=2%3&ˣ竀x]1xt<QC4e6~; fV{".V*&bI SgIk}h#!]}sJ,U$  UHl.u]Hl6PU\ l2x"UF0g筢2Od6qܫy7(GhdVwrk4n{9zls87Dn|e63|ϫª\ç$wq'b  pUq碝ΤOmWc\$P%Q$ 0WH$9su!$;Or'8'@"\po YjI-5W}QݭlꪖZxZv_̬uMr.N\v}zgƵ֤EB󫘵bɧCO؜Prѳrʵg).ׯ/jAB!.93 h~so:Wu`dHr î˙lh9}0 3E(*; ?baS``LNPJɶl;cR$,}FyJA]"i<4},ӨQeX5\IΞ(0To;y\r0²,2gO\v}F gAL8k_`DjИ2O+|gZ}\:_pϓ/1MȼO|xTӱִw#yE`,YSc9vvr hTG !D!B!9Ou&= &9y#qz`*Ϟbe|"u0ߤere|5d%YJAX]O/e9ǜ'vcveթg} >#RcyLHʚ[JA6W|brج}|(nsۧ5`vrMRch4O:[ĶL#󽘍RiV-=78WuwC,zUPp\;Xeb&}w[hjlBQ2@!BqN[:_8,¶,쀍1ӯ3>yuxB RajƶiaRk\]_݁yϸ~'35z2k8igytX& RaY9>fo5s٧yӧs˴sY]L4hiN&< L~do8 01&Ph__8Rɸž#./фug;wES BZB!BsaZyPB!1nj)\_92a9;6uf6k} .n&}_iYT Kk 3)12GkM"Pa&Wk iα |JΞ:'BlKr9Y}y>cO1u08c(z|x l;來U]! B!B!J3Cg_T-پi9a|zkQ_kX9YM }(|L!S& iضiΡ ^)|LDB6%9Gwos=s|M\5)b(E]B8hs\uy qB *RDߜc135 P 8k5'̺j9]2|=s(@Б/dΦebεOL? eL   x&YW:r83b_m|YJ\!.  B!8'`Pl.wLӠQN!hM/Z5bƶ Yu+ima[mM =ʅi3P( Q("JA XT*fOTc;kU-ʹXr-W)GC"!caJr~iU?cu xreL$ulSyD`)h}'vnfÕWr . B!8gD\[=tD9#4HO=o?@7mhgUg#YjD 2<\LD"갤%L"lbJAOۏmը/)]Eƺ1 d]+ѬK].N$=뮣P($ . B!wBAe˶rH!DUH],\iRyF ( 0K'8r"T fk5%ʶecv> (>bC81\s^eV&XETifj.=GElˤ!BGkL2=$C'T W%R Bm]¿B3ʁ%B'l˚s.5"& B!B̋[>J# !?\>`*xL[6}rZ kIxT(ir%*@<S' OeGD:S5pDhfAxDճJA8[T؄#aZb1GqIwȧsP.{de3|_ C<9WtGt%:\epp`9W|MΕ1.$ bYd]]jbӌe5]zm [wAҥ4B,02@!B1/<{d2YF.i=4<_<|9=cQ/CqdT\7.>+ӫye%MPfXm)CR$,͗}pR:bfK& hU)qY9@ϱ!@t)l^Mofʕ8#. B!BԜ8ToN.prb, q,KN1ژܿ:>K8=mlXu+ima[_fBT)C<' T$,{sEʱ98kby5~Y{:Kq(pjb#kM4  :$y ?M,'x| BP#"۔A5>2Q9xʣ{#Zp啬\p8,.si!B!|lJ7/+.I',\W]Iԋ95H(hsƥl>2qw5~*)LJ18WJto#tK"$bM6g,0~:*> 0M<$ ZKh C$$9_X9OŪ>'WpKXA2$س 'u eb1׏tD;waz.Y"!K!B!CcCZ}!DK!βIdQgkFok' :ui+NHV*0sQ\٤d] žI @eN,@yezvoJSc]@!B!UcC(q9%bŒOO:I| &]P cʕ,ik#z۬Zy>#cy$cA!ld_y>V۾R|(5h,+Yt)Wy>\%gIDa5ygkA huFŲ&7G^ΎBPU@"xe],y`*OO;hϻմb?\O+/tVQvݾ{Vq!:2@!B/p]@ E6, ;drzA_kz2<į^8fmlIs]_k%PU(M^ʞ_3GW%"D<e꾞WY=WRIK8`6ʮOjpt,]>я !x~G&B!B,C:LT*Q*MU9gYBFTk|G~ucd 'X{մN/06^]u]JeTt|ʮCH$prtjm2ZW! t,r2B&o(*.06n:&(ʸU*MQv5Z%ĢQF UEFuH`"_,/G QSˡW+JΞdξ?9C` HNl&JVo}i$be͗j֧3_XQh>1AcP*xE\ nݶuk֨cBwti!B!BOnl@ fxl[N/w.HnZ[5i}jGk|M^jet2< 8xd cנoO+k&uaeprWP? 8!d"A44ͪO  x" D|rur$ bZ,"Pv[SK쬽ӵ{V{,G59,[%K妛XrE{5!O !B!7+O*3quOK qᰜp~FѰoGSf?>5}W~K{[eϯZT*Q.,ik㙦Yӕm@`?sYO0hPcBw/.B!d*-[|!Nh P"B!B\y tj29P*хx B!BqqB!Bew2dx G!B!B!]'_(t:Œ~@!P0(B!BqB!Bwt:OTjt&:_(u @qd j=G`k]bhN8qF\ۉ'{ی͸eYKg;GooR)J~rZ?/E}n^3nf0?y녙h-3,}O,krTB1OB!BwL&Rtioo7 nv<w]8q7]G{{;y_<ȣ|?g߼ g-D?ذa䥗^7nT/d۶m'P޸q#<=;;;g~~X?# `ƍ2q{Hwvvrw `۶m\o߾}7lؠ_~exT*zx}{gя~?>|w}l 6Lԝ|_G4cSSgLj'bѣGƍI&SN/'> FGGyd=ǎ?cS}=z(w҅_J})x_SgQ5?Wr\eCkMՔZ̜xX(ڿɪu}<ߟڧ=a{)P!5&B! L-?>~p(zu - ɚ?h˵;]#J)!Sg'8Vɟm,Y2cUFED"A"odD"14wkYl,[lߓJx矽uuuC'3lϾcF1l'or'-ϟH$flw&q p q|هuww322R?10@(3::Za jҷ+(T߾kݷ9k4{'Ռ4e_SCj#.P,y 2箓)y=嬵԰mUvuVY嶟y-+;aM B!bGRo{:Ǚ=)5:Bͷ79*Smz,2_kt>4 ES)ʼnqNrkLWhogͷ: rN<}יL_mz~ 6裏r]w-z!W^y+_~'oU=ɐJ4M/^LCCCUv5go%շh60k43=39مUgGՔJPz윏س@)(0s>9g`Mf9kyn/{7UaPdξքAhnj>]*{~xrwh!,}z!rk3Y0 h81ʂm۬[{wfg)!5!+!B!.yR'CO9[ಥDB9 l2|,[י >{JVݴW cyxg>9oW\CvޭBЌU.vlRB,#۾x7/pkXoXc 7HFϜe!'Xe9wx Xܴq)+;ꉆZ9?{"gp@db֚S&Ɉ"Qx.{3sKFqӯsM1޷r 90O^&ϳm}`A\gn\cܶcVub!fdB!dȾfo}we8ة1~$s{.'q-s> i6ѐTYkZa4bSx3v*\ {K5avyz{{q,YB"9m[ÝW#AE8Pݱ2*.d \QcZZbzv=.^;O,pM+YwY#m[ :H0i("AEQ5Ng~Y>dlQc6M$9|FZׇ9~rmXޖ$v0ʵb֖i|^5L9\hǢ%Խw+h?w!Li!B!ĥqׯDiIP0H6{Wuc˯^!v5 m,Zӗ]X/^ CՅy+GX:n޾-@Sc't*W_}UꫯfŊ|Ww4<+ߏeYSWW7~ҷ.^xT~ժӅiԴgxgI_C&4ʰo.ZA4Cy< ɮ-+fbe]29OOVQI(k!=3ʤOfߑZ~iy>NK܌eYUWR"/MVI~󚱬F+Tq RO䱧a^͝DBDZI SЌf4E}}Ms^絓i~{i5\vľ|lɺהȗ4/Kw$HHc !w2@!B UPx.jDmo&]m3}֚Ζx<8mplnh`d9\ WC9p#ƛ6pm.5c C KšCE֮]ŋ׾Ƈ?a8|u]8y$,ZhN\_9odܑ}^ʌ\Q)(|mK؈8Uɹzs%~ .,C-+ٶD+$Z/j9'qZ;e], #F/ |`#/ h}:g٧kyC1WDv[illFBq!B!ĻF(:W,;)rBA2%g Vf d){`bur2J@N&ׄ_*A ;i[lڴw"؇dرc_"W/|' O|@< Ta<ϣj/~ S)x[{ } 9狕_dz`(ز:ik֕mksɖJ BZkҹ}C{WQteP`juS(5)}Ex{EхB\p2@!BNgc1ׯg/M/y {ٴ[Ƣ)d x&9k♍b5۶eWՒH%Gٟ]vx$pX4<}|>###K2dѢE] ݯL}C7jaۦN7Fo~\ eM~ E# wOϹXT{O>V[ZKεHwOW) K H$px9W>+h'ҼF//# cS'7lh?u?\pڧ{k0@0;7pΝ,moBqѐB!Bq"pe5D: AIu bVpͺVhP R@ωAAle݆5l\+V̹XMPP[b B_ԧ>E,Sgqf266֚x35$F Yسm+V6&$JΓ$ P4邦X(! ;Y}!fuX_k'=89jҴx~֯gҥxE1!b!B!87sLJ\e 4Gf 亱πc>{sέ]%[ k:R)WWtvvOOihhP>4<:u]]]8Ќa477jR=$9ݷ7օ ئз\h'],zb`*3' OxCC86.eUgu涉52|}a't}yP?Ox,nkJ:$iH1 M*3h\v- хB\dB!B|SѷϾn쉾q|:t5А h 'h84ypj[ظ*-h i|e˖Q*>g?Y__JͣÇU.۝{n\ҷ1vz=18)z6 I ³s>5W/㵃ؖ--˚iHq|.h+seRf"s\ጬf!=1BSC;bz*'yry1Wt5u\u,nYD(FB !B!xrmo`/׍د^A۷5m, k)T,2j^<2V-[ob7H0_Hl6o,G>GkkW" 4w7x#\<=ӯ ?_NNWj!`K^])EaZޗPcʴRh{(Ͼr6yrYJ4lc[R1\)\KG`hk_ɲ8->=33MWO;2@KCpUZVO00$Y&k 2>r9llC6m"RB! B!բTBAPO_9B4dsvmAǚMʀiKB?o8ܺcwvT{G "U(׿u:;;IR<| _`ɒ%k_4Osw{ayvPd򥩾=c*/@b˯*wC `sVJQ,Ƀ 5hN/gIZ"pEreUɺvY+C/i_fd]~ny+DB[!Ă%B!mc1u-+?}W5%#-Gyzqvm3d@AP8pM].^^m2"T,-/_N?ЇxGP7I.өT'p >1~-Z>]ϩ_3s'~=շ|Rܷܭ(xAvοi_DU#Ε'g du˨OZ]]T?gOC9뱧[oбt?✳.]Ry~^KC2m8 O,tm.+~cx3VхB,x2@!B!A `S*ե4gkJ7W\IOO{/<+WTwqI.}}}|{c|#]}3z(<;o'r:~+ԟ{e/%k.-+Yȹ2PrMΞ_Đk n+7\=?kVVxeFٷ7zɺ5kO9bɣo8Gٳq"\A{K\r=]ɹgpd4ʵgv6_?'yds%zҼF//#y˹aC;PeuSk%W3 .[]}BK!%F!B!9jnjT\N E|PJ%cAIu bVpͺVhP R@ωAAle݆5l\+V m%p/ ?Eロbqы/oSO.7x#wy'k׮U}>#.ny3lNѨFGzR{ض+YwY3KZV%IssTx8bFڸfVXA,>]*{~xroD-c͊&#Oz5iZnĆWlRǑFBqIB!B1XLh> Vte.tL=<:e\5˛hmM&r]48硂lz֮YeD"KuJL$Mtg?=wfΝ|[nP,3ݫӟOq۹Yf Hj)^xoѷ/no߾ /[zU\~9WO ZuZY8u涉<9pr[ګ7rˈU٧}wt^9Iuۯ[ʎZB6kMy;q|%Ѽ]:U?U<BF!B!%qRѷhDݿR}5'yn ؾi9Ӑ ,*hG |^8b-l\m /$ ?~衇xϖ-[xꩧغu/)3>x<νM7DGGǜ rm'k7H>Od߾.vڍho?s>5~l঍\qY N!tAq=.FmMhokSzZkߞ;#4Gصu%+֓V񘞵絁1Wt5uUW^ERB#B!bBRMo:GynMp; :鞙^f{ G2aMgX,]e*6~2TIͩƎ-"X"XSf!ޚA4fARݺ}Νt{x> |qˣl6 O7ώ#"k7\KD.?z}}9yw[ohoo/ 6 z;vW_r9>DWWW,[nfswk]M7Oܞ$I9nnĥݗƦteNz0=jh*G}>>8~v,RlMqS{.MWOs{6i+.<8})''?LlpNl(k14Ƕ_{zcףc16Yu_eK%S7Dxi ~bEOɴc=ꎎu#cicQiY7pEltI=l*Mb  d6tr2^847:2k[ϊrS. 4%$zxoܻ8{u[\.-2ߋ'|*""om}5ڝwFDDD___ǚ5k~r⣛7ǎwEDħ>xb뮻.ݗ^r7!~wcM7ڎxɧ\.m'x\'P>c-ܒDD[.z{{c߾}U۷]]]e˖Z6n==={tݺuѿcʕsnƸ{kmm/*J휧~:=s .8뎵~qwmV;v캏][]/tƍm۶cǎ+vCET۷W\<աPWW7'ۣ?nƸ馛3H>NY_iFqs%@G&]QG.h}M8 ӿh,6w+8_CU1<ձL#nXrer>7e}MLN=B#ȟ Fcc&o0f3=4<#i|gcm,ŵ\Xt8)R*5'/R:99y$M6Mb䋥H4#=PGLhX~fkLMWشiSDqߘXbEyؼysǯ6Xmmmk^F~GGG߿?:::jW߼o7֬ؼjwttęgm+m_#VXuIr9{]vowuuڵkc֭qFDĶm⮻-W*hmmm۶w^]]]s:;;RYxqƖ-[bϞ=o+WFggg\29;w*>ONJ+o}h*XDQ3X5mqğuV_sScccQE88qqϕb횶ʏ睗qSUsSsʵo~7v?uXX%QiY[xg>_S썏H8s?0 'Id2uq֪ޅ)x91˟Xzp@zash;d:poo~ k#">ӳQ9ihhl6r?C6L%%IbٗDiKz3)Jij*FX 3X,$M+Qjn )) rQ)-b,)KI>\cccT,c,ji$ihhŋZ, 0ժ@pRrXb*E9b``0ޓl,]D 24< JccC,^H 3\};磥R6E###ý`pBMM(57`N{ŗI R y=PP0ؘ@PK[ZZ"kQ,8Ce2XvR< Npo@,P\.ZZ*xN1/tbbB J$Q.Pxp :tpzȈ@sl,]D#@ bRIc ^|)'IT"(gy` S\J,WEyˇQ8%IQ\g#iLx,WWW(s)0S 0?09[.  ġC###1KX\.'ۗW1C\T*o`)@,Ppi*Sl'I (Jn`(D,pCCPLLLId2Q,Xo3NAVCC166&R}6Ŧbyy60k&xjZVcdtT0N>|50'(Rjc >l6\475ɧ9Gw V119`~溺sQ.x{244hLΣ uuuP T,ʛSBSbxhLLL2]WW bA 6t||B3Z=NLLDLLLxLOD:Eg3d3d^w&c0U=^UۀvP_Zǵ%Ac1c QUDMwyE\DS?` XU3|1c14VKTuXZR˛,`a.8m|UM),lWroU^9IJ\+.U=P>A@' KUw>{Iz/ƵoQ oĽEU8Oا+6 hI%5ý>Gz}N@GU ئ$ja#V]@>Hc1poLfEKuW*"Y%"[Dvv"򩈌opF` "H|Dd""oz*"i yX*"󼤫8kx!5I%?y#uI}DD$O;D_"tO"27^9XR|X $^-qA" ׍iˀSEd"Ddw]Jl3@D~>Ƚ%7πYkPDX+"K~6 c1>Je׸+IJ"Sz;Dgy}VbvX6H%p޲vv [qO޺G:WQB\wyT5ۮs'o%[6  3(˾vẝ/o꽮޲k~'.!.n|QxӁJx*=a{=#W J^o)ĪgR{;Nĵ &ݰ%ַxgjB x«z'^ױ_qU)`lU˯1c1p-zcRuCɅ^RP&K3HUx^mkp4["ҭ{1+q "rR\Uv\+"᪚G*"p-Xz>y-ʲݡ;q$2mq]Turqc]^WFzx䟁+D#㕍1c1 KP\8]+NJ6^hM)nn[vϩ^ /`L:ZM}ϒ*z}PxMZ^1,0o߳Dd'.>[SDr:qs6Hww+"yS7yzxN*` XU/W"rwd^Q)a1 ދC/"n` 1c1$)=kEơ"?gio%_7X.y, ׂ[+n.Q*Yjʿ%"ET5;G RSqᒵjU] UK?OG%Cpy / PbTuxkn8T>Ρ {+\^B+"yϿ=qyS}5nܸZWvʕ+-[7))iwRR m۶mc1cѣ]tչsQQQm۵kzذa;M:*KJJ"...gٲe{v޽799yWBBB¶mfl޼y^usB+"݆~ytt;v߿SN9%*::z"󉋋+;wM6mݲeƄk֬RU}1c1CK.âczSOmT|?UXXȚ5k̙+>>~{bbM6jժT5h[VDĜٻwbcc11c"###6z#>>^McժU֭[hɒ%fggou\c1;rq]ݳgϾC9묳Zjס\bb"ӦM۵bŊ ׯ_rדTNh;w<_~ 2$>A8V|)sYr?U5qc1cj^˖- 2:~'tRh@M2S更3f]zs>{+  8 &;vlSƯo8o޼ŋ-Ǯ]tc1 pn7n?yPPC;L̙7U-uLQ7c1ehui6qN?t+IT~{ɓ?5kĒS4q=5o텷r?_OHl9 x 4U%c.`-:ӰѣGO͹`}M7?paRR.(<o믿Ol`kGcT5AD\$1@-`b3cQ.p#{|iz=ꫯo[W_Ґp 7x7OWHkc=6Diii\~~W:D\:JUZVU `_P?^zУG"""|W6~6:u:qw0UU痷 LZkQc15`u a֮];+:CgqF6mڜxhx0KD׈H cA^;}iXVX6o|B@RRRzAAcj̙s 555fLϣ)51S"Dd1 Ǿ}R32l/X"o;vK/uP UAAo;|O"k} \-"61nʎ'|i8v9(QUz%k RLȜ9sl.T_n8_)51S;EĦ4%owԩI!z衇6.\@d^` w#UKVL>۷ou<Ư]L>&{c1~KU)JԊE}SO/jf S3;L:U;wO?o09sKk#׬Y%"0`Q>8^DZVoTcLudjُ?/|ɵ^pǎ>[lY_+r+~,^XzŋOY֭{lرoƍnغuy/~lٲe:S/ |UQUEk`Vuf1U]*"-Dnu<ላYDN޴iӃcƌnuXFbbbO?r/,\jp<4}N<7pC;JG\*g|wYl[˖-{CU|7~;t~Z1c|3܍|iXTWD5k}Ç_+&&cW .y.]EW+3-MqM6~=z?e&Mj$dӦMEޖ%K,Yz}_D$8 3xADF1?7?ZzKD7\5`N=ԁ\pA͛::o{ .\7)>>~Wɼ\GLhyVOXXX/|}Ϟ= 8;vҥ:{,^8믿޴aÆ5֭zʕjc3o^٣" ,Ȍ1Ƙ7xGDBf(1+T4i={ѷo^ƍܣG |򂯿z˚5kVlٲɪ^cT*-);;;@DbbbNҥKl߾}9Mll,"|%K$%nڴi]||۷ov`LM ̪NZc1~LUsDMU< "&M}f׮]'zԨQz%bŊŋoKܼyfy7 mIѣ:uեKέ[zzz,󩤤$r-[wΝv޽m۶m7l0-))i^cWXc/-b .S"<&&fTΝGwܹK۶m;vܹСC[ӧ?MIIaժUyK,ٳcǎ={ٖyܹ'1;4U =۷}J۶mDFFlժUhª3*ٽ{7۷o/ڼysj|||ځҳٳ'e߾}+׮];/++kua1upj8|'16c|m1p02©()"QQQbccOoӦMM6h޼y=zD4ܹ3kٲeק8p -;;{۷/e޽kW^=;++kuu%eք,^a0"Dw֭WTTT-Z - oԨQh˖-4irpU-IIINOO/)**)((:y˖-qeu" b_DTc˜sJPKΝ{o~@DDD-ZDh"YfGʟ$00e'''禥gػwojFFFBbb}mvWxB{$^5#"@S*{*pW/15? XBk1ovi""ͽ/c oz#"Mf@P%6O2GOڣݱHu}p7K0cc C}@5bp_J˘_pc[ uƘg 1&Zh-5S,:2sD$W19Қy1uƘg 1)Qո7ec1Ɵm:cLP;l᱾eeůZGk`uulzPŮgetDhP98}/|DZ3)ƒ:]cƌJU7ØQ6k'ݘnq?zhj%U2W9q5O+>=sװ|?x5өt~/j£qS_ֳxB2Ѓ5}_u.ͺhc/ZcL=f 1h_5cj2Kh1Ƙ#ZcKhOqgucL}ahi,5ψH# \UTU5hcg{־S,5){65x|c16$-}1fYBkŬ۱1@:cLͱyh?5U%c$ @1q@ [U[},5vwz0c]n%'D3p+0 >;}^_HO~Zc@J /VDcLuaS/'-ZnU61+4c1npC`8P"U HU|u\AU6ZcOj>mR7cj_-nfSuMxQUEdp ؊km |>ݫEdo-@f0 IDAT40H;z wz4n:͋%p7 f[޺pݫuפ xZ7Ui蛵"pj%c0 p릊6F4X8 f߁.T\|;<#ҕ;_q<ipIq /?Ed@^ =znpko%#>yp ޾Wd-k5c*'!ޘ}L!U8 "Ed.۩or\;KUgxNF:6U]㭿8x6j|("nEd>;\}< c 9߸V?F/"rp) 8Ŗ*v9ZcOmc1%yh/'gL깿IJ?Բr,U5XWx q "7Zx˧^- tD5;1ە80eGT%rl1TN6Bk|8!rĭ%Ŋѡa}gf7oq]D^+``.f,"sO} -k1~c]1Ƙʱ.W>q$"E-XkB5նcSK<jNRWHoZUU"UU`'^x߫j&h[W%+,'3&muN'2ٟKZzC1$#]Դ*MMgLSQr -;ejpp -^DB1ޏӁ~D' {Gl3[1*cq<t7K~~+"e6OǵҖW%&wOU]Z:~nܴ[MrٟΆHٻ\u'bՂ;,BXwعe-ZY uc╊xJBd^Vĕ?d[tSNՉya4'Ͽǚ)l^sMm %{ q-k5ś UR%c},^:43];ҋz4"Ew++nFcܾ#$isBsz'C"r)Am~Xa?,w}0;E=e-ۍVQaǫo6GjbϞ,>z3iiythߘ!ǷGld(T˱W#U-ِ*VXT-Ys $1nߛFA0i$H滿|WQW>)"ۀ7Ddv.%}jxKD^u3p.qf-"kqS DLU;MsU"2UUwxšǍzCUH0Eץbw_+Ed&nZ7E+\2qeEsiU8U īR:a#"mDxZ."5yv]yבLy #پq1;uN>~\{o/Վ +~bwYx2itj϶ |PKzjNMyl~^!D4 ܇Zvrt^> ڡ6B\s383#"e #ۜ'f4 iiiՊ.M9ah[nl>j3#ChԽ?rr xߔgDNގt0vz"'-B9lؘ/Rk篢t9~ EDNx*x"F?>/Avo,:cذ{Qwt1 †?Z9Nj_q+pkzQ^?1%n:ŵopٞMOz2.,/F,zJ/{.qSVzFU9\؋myvxm$p>\|*?K~/%+*םڲj~SC{f"cEIa{s-v& #?ι.uݩ 7iQ{FήῨ;n} @xfG}$@M:ODjzE;A~}C-EEu77gErx\~DW}ݻx s>Dq'*]Uk~xX v׼w]sUzrV.G>3oHV7D.<< EsKc&9>4/Um1uডX("wj~ iDZ""tkǒe^^-X bSYƹcҳGMÚ/o:/?w؊)sTt9n:GDR5Y7~N[rpy63!G&XORqE[\ⶥ&NuAN`,`I˓R7֙sډ*̜^'3ⴏsT *`kAң{r={QW+UC&*Ka en˃,"eS;\"ػ|*QE[?^{\6-$"dۗϤyHH&y?+_ɉlI݇K={.[6̹IsCknq'̪+;~=KlVCNMq@*/"u<5Nw9.CqJ+< + J)3DL+<IRFӾ )7yrY>"2{-UA~$`B~|egr?dÊy٫ҨQZv<9?XɈWɗo= }أ~{=S_}shӑ&@oӤ+Γ)I|ߙg||9CJ?ͫv8kW4:o6.gy0+",5wIٟÖy=ϥw4byݹsݷS.}@DgsфXz`֮KᾇqخqӒko.iɲOKu 0MD>nVu)]BQ4vn,@6\}E/&LW;p֘w06'sq慜wn7n8{@e vZڹ;;* K{\ֳ{$^MQk^.II<G=6mԍk^,7@nnb8`P|#cf}}wxZ J vNLU,VRvR+2W$|{vsGl$ ;XMpDcc–2Le ZR3vtD_}cui#;Kh߮IVy}5ߜ}%u]7)"&YݕWoxyWn:<.\rdݴPՃ@U)O%߇,R\~~ û[IEj[Kp_UT= ʪ[qBXkEee>bZ819ه"2<!4(ǻFV{Oᑤf5bx*T4 }BY.-`WҺSy݋J -TexU٣>窭Mޤ6U+b߯|⪵Z,EKj q]MGX_$ǪҾ]c5ߴ9ko;͒YSbU~=ۿŰv}biwf܈1u|UB\/ UJf<g%~Y2Vlu3hӃM{7~l]toCx2s˞u1:*eٮ' 7/UK+ \UY2L F"({Op\Ue Wg6E)/"-prEdZ5{yq"u2lPղ#?~ff}2Ǐ<-*"}]z<6nZ7.=Վ>yα1drɤ}WLSQm]=;pZFff>EEZ"=ei:?ߖ?54'YFN<YY*'жOz`gdSPPt Z{X[qGFv?OhGQAtQ. 7Ol_3 Fs_q-yC)sGEGvVEG]MuTW/"{$?';F։lDQҬi08ZKY6}[rM `=otk}&'88 C}GoN^CNΡ"p9*+95"j^a7UukSx4pe=kْ{n 3+FܞB.Ma4"kM۶_Xl}z`- mWla֜ۗMf\za,c"y夥=:eDIÏ->TݥQ{5x۬)iYgx- Lߚ5.mw=4r,ޞy4j$0-aN hektS&o ?NHni ͚3k6]3)&tg`V< e'eVp ; Q*U7 ÌGs;L IDAT%ɸ,Q_+G\y}ٷ,3/_zjTB-y8Nq*2qnNCf8wjwh=t'JE=۱/ 6.G7< d'ܾ\zKT;>'q_A!y'4cb+oYוA2u<5ED3 oʪJBeB #UG6Du%=VG6D62u9.վ 0GU_zCDMw; {$dC @uUGuUkC;U[}:u>jZ-**D@aCBX!!ziIw}^ur}N\gs~3k|F#uuCեV$z4XDDzC+^ bUwmfV&`_^}'XV$z4XDDz vXQE wiEc[N3a.W˔ЊDZI;ZV6m_u&,S`pAڜ̟XkЊDOЦoH!l 9 ,`>=#%"U zhEDZA HtFlODD$^!" ZZFmD5:%"fH q#LDD${[DV$>@;cöDDDn0=AHPB+mj¶DDDn^V$Ψv@;"""qcfM』 %"7^ۆJ}]M%""''ށHh pq ryNuEԀp S;Ycy;5HܜK0`@Ҝj6j9?͛ݽ{w PZW$gXDDDt8"ydeeU7R2[K -[6qԩy!kjQ9,PO4Ihc83>s(,\^{-+q4ddff.Nعs^xa늟|pܹs8$zܽx0Nj6*:w9𒻗;}[oqqqqCi}uӦM烾,Xpc=9A5Tg.r9&p,"""p5|c̙ϚxXb믿<]rg}ީS3f͚5Ξ=p?Z٨cf#f>"}f6hs4;w4yL8q[ciHnZ~x75k_N:yyy?b駟O~2NczVoH"uj޼y>Zw 7tw<ݪUr-M:ʲcVѼaÆ=zC>-[i O?_Gg͚x"CRRw_xCA. f6`7H4hРwܟz^z?O͘1gVYʹ/~/;VYm Yn]/O͛w+2/fux6ww.if ?Xa3VǏ?9ǏV=5d˖-~]w-9sK.|y m!C|o߾W:vذaj-znΝ<#+y̚5ʴ"5*LV^w ^g{i]'""RJDn,G}vo?qԨQOT裏._3gݕ8hB #GΨQo{ȨQVӺuJg~}ƌwk%cmf _)Wv>0ݷ#>`f#8w_xD;c9rwꡧvj|̙3~UկVBkĈN8 &tiݺ\2uܗ^z)cܹo.]O!uԾ <p!e_DD.|?w<"UҥQF};o욚JӧO/z.\?^T9-ӪUG}ƌ /LOJjCϻ[0uUVZx7mڴ(qIefہKT`3ϸDDDox#R]fz'6p1#GxwHKKwXqSRR´iv曫V\4##/6luvBu9ׯߘc9 4(!Wmذ~{ۂ 2W\`OVH] W8DDDjTwύw"Gڍ3~4h:СC5j(ޡժ,}yYbŢe˖=5LFhЬu׮]g|Ϟ={w޽駟cȑ͚7o^תKŬ\ǚ5k27lذn͛w|"""""=f֢k׮t˽{եK~z?UVѝz[ZZիvZj͛lݺu3&{VM]Y}=]v=;u>%%ӀGٶW^4kv1U\\̦M(X`֜m;vڴiS֖-[eff""""""%%%4hй۷ٱcǎɝ>r>}X-JKKټy3K,)Olٶmۖ[nʚrwjsTE'^جS޽OOO?.--]jjjjbbbjVRڴiӦK.۵k*==ejj*&Ņ䐓Cvv۷mڴiO^^ޮ]v YtҏDDDDDDһw~|Gu Ĕ6m$w)]v-[O)))4Т"rrr%;;{ۋ*(([1ȬIVi@j֭ӻuֵu]5j>11qIII}ōJKK~nܸqIQQQ}n޼ffw좢YYYwܹ rD%m-Z޽{΍5ꘔmRRRZIIIRjjӞ={޽{o-5+)))H ϙ vf:l`8pahvOd;`.4SF*A/""rTSB+""Qr٢PZ ^pg2ICzwmwIVX.;p|5,x݋݁wsk[}*>ݮk)͡(|Q(3{YٱDSEݽ:{$yw2Bw87ViY09=%᳅× lP ^CDD)_a@Әc?j*+9x )ˮ \# eט!""R+ЊHh3 P5fH0=s=@mjN!ix Z9""c^Mpښr AR!sg} ̪[p5DDDjZ30X kf7TP|x n3nfC-{ ;-`3fp^תn3 ͬ?pcefv|9\\{²Of(z3kbfZ=[qof _df'Ywk|WOY 1rǛ N']P6l'iUIlU)C+"" RNEǏ4ks( J5 *ϩDSɬD3kF̶6F<9ˉ=GDDD!"""5 >|ݟ9iM\-'""R/)HҐc$%""""""IJhEDDDDD$ЊH$)HRB+""""""VDDDDDD"I DZ$%""""""IJhEDDDDD$ЊH$)HRB+""""""VDDDDDD"I DZ$%""""""IJhEDDDDD$ЊH$)HRB+""""""VDDDDDD"I DZ$%""""""IJhEDDDDD$ЊH$)HRB+""""""VDDDDDD"I DZ$%""""""IJhEDDDDD$ЊH$)HRB+""""""VDDDDDD"I DZ$%""""""IJhEDDDDD$ЊH$)HRB+""""""VDDDDDD"I DZ$%""""""IJhEDDDDD$ЊH$)HRB+""""""VDDDDDD"I D*)+v{AY?w,""""""G8\3`%A 4@3k,vY{c9Z%I~ÀT7Efvqm W2+"""""RU{!}5%Nk`ܽ9*2=p}Y%_=7|3k  m^cの@)?-7?5NMw/0X0? 6)0 o^g``Xoa@O~׆q~~f |{V@`+egpga ϊ)J}3^wT̆>Euk@Xo8pwZ U_YY@g xWp6uz`ilN 7#~amy//wm{VeDˡrB|V3F0`p5̎3X ,s.t2쾘ffo5̞n$|x\ ~{rX8-w+0 xv.J377$h? C̬K~6Wb7z%3{Zpx燀fF_AyB1z?5o82כ 4Xif4|-g1uf >IfYX%`1A 8;|οwVEDDDDDpW ݝ@N)88LZY0z=s`Hi'3eCF'ڇsW9h-==cw-'Y/`AZS1}YnX~WU1CO&+;o!s;'|pzDi^)"+$/"b-|a<%9V{²_df-AWDDDDD"Zsh¡ ByC'1U;a5sᏽr9fVHW, ?VzTQG\P!$eM3{?w \ `3KrcT2f_JTsRxY2_9 זP6GG<̊ß3@f8)\3{ afϺ*99a&| 9){ 8@0'`wTQ/z=kKQ w+y&0a< &z =l5{vߣ"ߓ?eq;^ֳ_PkUÔW}~L0{ޕf6n$}?̮FDDDDDj >u*:v?v؃?iEզyc5/@8p!k {'f Kc{ AocEޟ $~9H b?^6gv:ƜS^A[GUȃ'òn3}'!F """"""Pp+N>^K0ul !=:e[ ,w|0K8qA%H+_33s؋!p՜ w,|UTV^UYchAm%h! aLes,j~Rmzc I0f1+\a"""""!U&abp>ARw=v/w̬Qy)a+ᾥeu^w f:,k {o'=܋2} gfWt K}'Mx IDATx3煇.MmS^u_1~1E$g&u_&ز沅m~L+aI2N3;7L - ɱlfc~~ef=} 4~FV/"""""&X)̾AYDN& {c] `fۥ<@ Cm$Efs1g, TkI8WRpa 򧏙=fvMxo8^c(~Ix[wM!H%u3Aua _MÄG$/YWfī$\&nD0'}3O0+0l}`U\=jNk.p-p;Ã,Mw#,>p/'_qyË  +[ٙ=kn!HFF Z3 '`m%\H0D{#P 3\a"۔`9\UܿDH~GH#qJ4piILnn"Iq u{+k~w{AsGs9cˌ`;Vw/)~mB0yP1]J$ 9_3K$H +}5#|y^8SBf%y.x581L̡=څu9t3b9j5VDDDDDD1XDDDDDD%""""""IJhEDDDDD$ЊH$)HRB+""""""VDDDDDD"I DZ$%""""""IJhEDDDDD$ЊH$)HRB+""""""VDDDDDD"I DZ$%""""""IJhEDDDDD$ЊH$)Hjbf̀D HNKKKm߾}-ZݳgOe˖wرcΝ;w. ݋DDDDDDꂙ5!ȝmRٳu l2?Xaam۷oI;.PEn}:8--crrrJbbbJJJJRBBBƍ7kӦMӴf)))-RRR'%%5IJJ"993;uJKKc׮]ݓ7''gߞ={ݻw_qqܼܬm۶e_~%""""""R 3k tѣGcRRR$''IJJJiԨQM6KLLl<%%yrrr&$%%ѨQj]+77|ڵkONN콅߿tO^^^^aaanN`Ν;gff.6{aZBkAk'o~d:o߾}˖-:uԷonݺ5֭-Zݻٰaׯ/Zjծ;w޲e˖[lxŊx*"""""u0` ۷ӹsm۶ئM>}$ѣeiٲeC޽{ٸq#6lسjժܭ[رc-[6o۶ӥKx-$5КYAҥ)ݺu֮]nF<|ݻWGhWZZʺu?oޱcg6lظqWZ5 ފ4tfֺo߾w~Fnݺk׮۱iȑ)={$!~,w~z-ZT0o޼۷o߸qƍ6mt{vBkfMwn޽ҳg>5~N}~zVXQ:eʔ͙k׮]z/nܸ}w/wl"""""f֨Gg^zݻws9CߡXv-of׮[.333UVCJhͬÿٯ_8.=lذ n ܝsWgff.]l٤˗KɭHbf t/ׯ.q״%`_~yʕ+X͌gUݻ4hЭ{뮻n@Ϟ=VӒ%KJ{5/hyIDDDDDjO۶m 2wܘ & 4hQ$33{%K/]W_U*>}6l\pAj}k͙?k֬$1H֭qC ׾+=\%%%+y&Mtɒ%-Z轊Uvرѣ馛N;묳k=ݙ4iRΓO>3n.((DDDDD%&&;S}E]!5kԩ>O?͛7o-O%\r-ҭ{ɡۻw/wS5gΜ;};XIhq_uӟOfNU\\̃>_֬Y3x{ '^:o<{w;?,>П/.""g󁳀sFq}|9;b̙w} &=yo>|xFywyf͚x"gfm@p *w/_"""Gz4L'|w{Ǎ&ޱ44 .s뭷nw'vK'(qƵ?~,)ޱH  ܽrfvE㑆ڜy晷*Ç˯KKKjΙg)SnnҤIck8s~GX$|7&GźODD&YO`:0ݷ;i8ƌsԩSiFl߿s=MP2_mڴQFD5#zE,DDDj Xaiڴ%մiS7n?`ٲeg%%%Y]H/&,%""ugjEĚ5kڕ;oi ۷o_;^ؑIh2@u)GDD$QInw,pl߾}ĉ;+Hؽ{uҧ~lw,Ys}}u*Vm JDD< \6ogl߾]ݴqr;vࡇZ>k֬>;/D%+WQB+""f68x,H2gߋ/O:0===aE^^^}zK/$##㭙3g^3k tklw_ef̬DDD(8ƿ~9}2d^:Nkef/RΝ[i~ΓP mwn73{뭷9c& 8.ȑ#hDii)sꫯfXb%KAzl`a7.38xfBiͬI8T΅]]={_mZuyL=wҥkZZZǶmv8p`z~RvjQ$m66mK.^jUvnnnֶm۶mݺue˦~{4Lf(rGҎYXQ#<B*o̬yzz~ӡC:t萜qOڵkBt5dggi&-[bŊ윜[7o޼qSm6ktIh |/||̺m۶o=jժgzzzJrrrj֭SZjܡC6={LLKKKLJJM6$%%XcQXXHAAdeedff*((UPP+???wǎ9EEEk7lؐݷX"5`v Q؞Z#BPdRnduHNN׫W[ڦM䴴޽{'m61))ɒ(ˣjJYTPP@aa![l)Zfͮ]yEEEvn߾}Waa 6,^j,*JB[6.df@' gϞ:6m4qiZjںu-[lٴi-Z(9طo߾ٽ{wQaaعgϞmV*4ښ ]F(DoͬicڥV$KkhȱqADjQʱ'}xN㈂ m}0N Ya&__:Wk8ziaFmH]t^kx 3vo;;gҋ/uc㈂;K3kLja/u}`gOԿ1JhƵi׵É}K# >frۗ{̺UP 4N4f6ͬ7f\=eЊDK+6k"""uAC%^ {jzel~ 5oeǘf2gf>>"9@rLyZE}5.pqywYG`0-|!0&p |2<^r5raXv&0;fEE@08V((=|Xԥyt/w^^aÈkеxhf/ً|e@[`nf'&r~efeƴkS-z  9rVDD=VlK+U0aivM ~g4kUQ^V˯}ZBw_`f (of=~\ Ln^ 3@#֒ù!ЊDK]&5DDDꂆK\Ba׆sd+rO2|YV`fi@CD042$TThfǹ{O![ W]NvK{;!tN!'w/yb^WՄiqC h٢ N~~szAZfy;~\As[-a͔ҪUvKVMx獋ػS770o^5aCr@)'{sQ' O,ҺUrs2vLGsO!fք`ԕaOQe"953RM[r sܑ]v:g,r>IwD2 \:}\ gfNl~s3 c`bkp``w gfakfCw =I]c`/ x b˽$G wV(.2w_af%fo9  yqᵞ!jw^wrQк{.7PL1|@U2/c3kLoD׬E+.ɥdoo&. w'^w W'N:/-wGm;;/>WN=rwP|`|^'1+)>@Q~I,|k`%'}xPGm8 {K0xSܯNYFDrp7iI iӌ >,_flYY\;apo] IDAT]WLjXm/б}Vwz(ϼG_a^g9M޶^]zW]ޟ+/8Wy8} ̐cV}h{_Uy?pY !leq GQq붵Us֪uխu#VQ= !08'@ +7>9{y3C$FIGUBR(ڷ ZmG"+SwQ>ffN`˸yu.2NUu/zh_y=^e'|ѾB1pT-@??RD |.RGfEUKq26řZk+pnTT-E4Uu'U5gW^^8kJU?@e, T&qP?)"ծ(܅|k<'8Ap\޻9 XкDƶW~Ù\c(/+\v&C/n7>/F]}aa nWC8!gdǯ .<N8ZpDdp7p٣JO*yDx'#kre=^ma9#4N;3ۇ6zu?5H'37sGs,`ނ^mÖ}+iݾ3}ɢYS)+-aɜN8쌍lO[GNI :utmZ; ;'_\AL8vfn/>`gu1K_a=ߵ?͘Hރ9N8| -Q_[ƶ\x^7F xPR^~m9glMP"yty:tVwpĄ͑8ΐIDŽ{%-sJ"r^{+O&;#g|9Ͽ̔(*.#{ 5 ~%7rŘ)akC5ZzKנS11~}\J :kc!X%])1KDv<\)&"aAA{"R.; ^oW^8}".H=^@tF޿N:L1zo>zܩSepjc<}[$&8<..b/?_Yʿꊞ\5K JiM:+؏"BBO[ ؞1t?W7Bg hǁu}i}]ّ7*p*uT^@@_ʻ#^DiL1ѡZǺ9Ϗre"еsNthxevjW_ًϟBZz('?ܽW9{O1%%$'E[ jχoӠ\=H0'Ρꨮxw>J #Z?vɟ.X{+߾BHvq%KbxWI3Y} {}0eQSZ^ʄE}b)xg; iŸ9=cG%4Q߼Vi"Lc62gh= 'Tom[_FషF[>餛-Uf/{~ǪOe:p_:{9&gv%EDƶ㎧?0'ALڥĶLPȁD/lۼΟo8QWg6YҦi ewٕsuwen cb#އ3q&(NCawpn GU6MǁW,;CVV!_ސX!]x,OIiiw4<.cDZt=Ǚ/[3.WM{8!u$$D?kZE?k yպZvN$5g6LO.nwq¨ws6,Ǫ:<{b;_U*1gNBT5Hu4^ztwRGϏ ױE6l%&:d-ӹO5QmLlL~@F ~Ukp3y gJHQU?:T=n6LUm{2pkr%G-$0z]~ ՗LnDv^6E۱(:<`{vKYm5@481kHأZ͞}8#2LB` Cߌ4MϹϯp>nS278ρMZf ՝G2NO6Sk}0/!P¼"bC|Vן@%0@l -)"/ 9??Ӯm804xZyR\RF$I]$45݌?qymZ?uKaA)ڄU;N$(A7{+yj>_LȫYkR]8K>`Zn'22q핽S~ JI]?Sfu`W~=^ hTu͡o%Ɛ_H؃5.~㡲'ܢ‚UY>1.&Nе#cINi~q>Qܾ17^pʡob͂ГqWI_ ~6}'evk,}}Ķ= 8}d.OMƴ0/]p"p [ڷ '::3NƤX&+8<&Kƒ8qq j1b^;_@dR"rp9Μh cr~t $Ӡc`vSMڑONa,;w CTEgwn|!lj)vb:rWqtf9QY\7 H2z?^YʓΝElڼM6E|?-c#@NGidl; FVLv`EKaƝ l9u ď>[Y !C L3zq^zwGNUSUd 7\4ˀVDE${кT Ċ.5hɫ[q|px~xPD.p= LY§ Qf?&U-yf:)Na%:%V38yk8ݕ7^e_!842&r7= ٹw_IaQ)Kf싋9.Ն['/߻ ).->gq xŬ\M݆ۉ"y-ԕһ,qnjӜWgKT_Uucu-8{=䦇-U֚iqM9;q%aQwChD>YJw{jG:vl]ObDDŲmӁ("Bb[FEŁm\vfn{ִHiI"*]zܿn>"Zr^3_5(YLspӸ|Sxɳ *ن˵Zz``PRr$+w5Zmn*~ݷ-٠Yk/{:rD93ӵ+K[jZKFwGpy!,4e`O%*ϩ2"sjﲲ ٴe/qGys~x<0-9311#QarJna.]v!:,EElD@@m ?hGbipgKvDN溶 /RЊͦ;.SĴ}$wJ)tHL9>k 6#bz.($GsmҨiaz#&@lᧆ^&`=5sJ1\' @lCϪ<jT1 >zM5g֘ys2,AȰZokؚ<a~1s˯V $NrâpĔ11ĄT+_'C6]Oܫ",(>Z9Q<:h,5ƿ4E@[yGcwa8m.p:.sZ5Y+NrUt_ 9QD4w8 zڶ͎_$2T=ɨe1Vk,x[ hEd 'S~0F,;JD~6p+d } 5ƿɆc1, hMh60UUǻϵRGU_8WoU}}= _U?q?=mY1E͡5cY,F8ÂS8L{+Ya)U2Oy_Zc͡5czhMciNmsǡ HOq0品'pq47;_l1S1Xi)-pF\ULwUz[ٱ!U-c=]8경]HUh2c1j"X\" 茓x}j2Q"ꖿRD@o3ٗ k;c1-КnNǀ!"\,tQʁKp:=oVX&"475lùSvȉ cZv8x[sr\9- _j_V~&0뿭 iccZcO&N,5Ӓ$iniU@{,c1S rN͆ƜCcZvKtḧ́ hcLKaÍMcS1X@k14& hʀֆb?,pc1.Hu#Lem3?; UBE$̛H۽Y1#CkD:zjM8ﳷ|2ێf@2&yFzf??5ώy=lIw[p;1͛ ׭0M)?teK>ckiګ-Oi}Yk|/UEDo߾>i1'xb;w}D$ Um(X 1;xWU49 /~#C%Խ\fuU}-@JJʠ!C6lذFoi WTTs=qƌf͚͙5$"*`:Ld;c1~KDTV0>2|~'qn+--W_M6m3gμKUk9l@[O>W :|pD=-XYYoVifΞ=l_ɴL"28AUKky=6u1ozbLUݻw5lذ{ޑ u{MEE}ѮO?tּyH;Tz"0x[?G=`1֕~xk?ŋy}&[@ p}աWnmTug1x8EUm, 4ھ}^sIZvv6s[liiiuQΝ;߿Ç~UW%w"[Wqq1ӧOϝ>}e˖M??k7ï>€pE7i1_C UucLڵkcРA2+g~?[v%Km۶GSWjxvzv>}HJJJ֭[s9S>}ZZˬYΝe֭7lذpܹojfLM""c1xHBB}6999%11gпӲ:p gϞ9##cƍ͟? kAJNN>+99.]$u1o߾܅%55ޞc֭Җ.ZhRIIc1cLDN޽'ݻݐ!Cvޝ@_7^YreO?#33s V,_To/hm;ңGqqq#;tٮSNQzjݥKN:ԓCZZ6lYfMn^^ޞ;wږaڵ3ݛ6\c1$DSrr۴iӮcǎQ={lӵkאΝ;ҴKZZׯ߻vڜܜ={dm۶mێ;6mذMDdlH,د_㣢ƶiֺukO@@z<6mxTLUK+*rss)(((),,,....,))),))),***,..߼e˖;vXdԶ܉1c14"$~mڴiںu@ǣ!mڴ BU\UK+V|//.)))*))),...,...,**۽{w^nnnZzzu@Esh{<@}:wq| R1c1i;@8ʁBU-iv- h1c1d1c1`1c1d1c1d1c1d1c1d1c1c1ID܇xtYHU3}c1Y@k1/U@aTՅ"8]Us< jVRm^7 P Qewoja14[6c("`#8-wg P)&omp듽Q~ eykcLsf=c.:5t}xFUUZ i{ycLsa1Ƙ` ND]DdV,("@y"YD$衪jԛ6@0GUD$ݢ;9m@AU˫ Y5{e+նveNv9]UstcLsbC15Ījԥ \ Dg=z%8Ar7xQD _-o_߈H wAnπn[>f)"3wNv}\R]&. >>ձc1YZc1T`d= hU} @DǁTu:0YD.PDd  1!cUUovH4s۴l8ś*lZc1~zh1wʬ]EdRUEnP\D$G8.rUkxIDbp%ETuH(7;r 's1"H6Vv=$gtysUux0 DDܵzqnXr)8EDQan=UEmc1>%6c1u@O?Scֳpܩ4 g9 BU}QD€{q_ ix e:?RմCs5^jxGU D?_Dk{ zU8<$M[ɪNDnqP !kpz~햩^v8>j{cLsa1%n@`uzk5cil1]Rwlc1,5cʐ!"rg=%!"mc1-& ?IENDB`c-blosc2-3.1.5/images/blosc2-pipeline.svg000066400000000000000000017000611521743436100201140ustar00rootroot00000000000000 Compression processDecompression processDataCompresseddataPrefilterFilter 1Filter 2Filter 6CodecFilter pipelineDataCompresseddataPostfilterFilter 1Filter 2Filter 6CodecFilter pipeline c-blosc2-3.1.5/include/000077500000000000000000000000001521743436100145555ustar00rootroot00000000000000c-blosc2-3.1.5/include/b2nd.h000066400000000000000000000750471521743436100155700ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /** @file b2nd.h * @brief Blosc2 NDim header file. * * This file contains Blosc2 NDim public API and the structures needed to use it. * @author Blosc Development Team */ #ifndef BLOSC_B2ND_H #define BLOSC_B2ND_H #ifdef __cplusplus extern "C" { #endif #include "blosc2/blosc2-export.h" #ifdef __cplusplus } #endif #include "blosc2.h" #include #include #ifdef __cplusplus extern "C" { #endif #if defined(_MSC_VER) #define B2ND_DEPRECATED(msg) __declspec(deprecated(msg)) #elif defined(__GNUC__) || defined(__clang__) #define B2ND_DEPRECATED(msg) __attribute__((deprecated(msg))) #else #define B2ND_DEPRECATED(msg) #endif /* The version for metalayer format; starts from 0 and it must not exceed 127 */ #define B2ND_METALAYER_VERSION 0 /* The maximum number of dimensions for b2nd arrays */ #define B2ND_MAX_DIM 16 /* The maximum number of metalayers for b2nd arrays */ #define B2ND_MAX_METALAYERS (BLOSC2_MAX_METALAYERS - 1) /* NumPy dtype format * https://numpy.org/doc/stable/reference/arrays.dtypes.html#arrays-dtypes-constructing */ #define DTYPE_NUMPY_FORMAT 0 /* The default data type */ #define B2ND_DEFAULT_DTYPE "|u1" /* The default data format */ #define B2ND_DEFAULT_DTYPE_FORMAT DTYPE_NUMPY_FORMAT /** * @brief An *optional* cache for a single block. * * When a chunk is needed, it is copied into this cache. In this way, if the same chunk is needed * again afterwards, it is not necessary to recover it because it is already in the cache. */ struct chunk_cache_s { uint8_t *data; //!< The chunk data. int64_t nchunk; //!< The chunk number in cache. If @p nchunk equals to -1, it means that the cache is empty. }; /** * @brief General parameters needed for the creation of a b2nd array. */ typedef struct b2nd_context_s b2nd_context_t; /* opaque type */ /** * @brief A multidimensional array of data that can be compressed. */ typedef struct { blosc2_schunk *sc; //!< Pointer to a Blosc super-chunk int64_t shape[B2ND_MAX_DIM]; //!< Shape of original data. int32_t chunkshape[B2ND_MAX_DIM]; //!< Shape of each chunk. int64_t extshape[B2ND_MAX_DIM]; //!< Shape of padded data. int32_t blockshape[B2ND_MAX_DIM]; //!< Shape of each block. int64_t extchunkshape[B2ND_MAX_DIM]; //!< Shape of padded chunk. int64_t nitems; //!< Number of items in original data. int32_t chunknitems; //!< Number of items in each chunk. int64_t extnitems; //!< Number of items in padded data. int32_t blocknitems; //!< Number of items in each block. int64_t extchunknitems; //!< Number of items in a padded chunk. int8_t ndim; //!< Data dimensions. struct chunk_cache_s chunk_cache; //!< A partition cache. int64_t item_array_strides[B2ND_MAX_DIM]; //!< Item - shape strides. int64_t item_chunk_strides[B2ND_MAX_DIM]; //!< Item - shape strides. int64_t item_extchunk_strides[B2ND_MAX_DIM]; //!< Item - shape strides. int64_t item_block_strides[B2ND_MAX_DIM]; //!< Item - shape strides. int64_t block_chunk_strides[B2ND_MAX_DIM]; //!< Item - shape strides. int64_t chunk_array_strides[B2ND_MAX_DIM]; //!< Item - shape strides. char *dtype; //!< Data type. Different formats can be supported (see dtype_format). int8_t dtype_format; //!< The format of the data type. Default is DTYPE_NUMPY_FORMAT. } b2nd_array_t; /** * @brief Create b2nd params. * * @param b2_storage The Blosc2 storage params. * @param ndim The dimensions. * @param shape The shape. * @param chunkshape The chunk shape. * @param blockshape The block shape. * @param dtype The data type expressed as a string version. * @param dtype_format The data type format; DTYPE_NUMPY_FORMAT should be chosen for NumPy compatibility. * @param metalayers The memory pointer to the list of the metalayers desired. * @param nmetalayers The number of metalayers. * * @return A pointer to the new b2nd params. NULL is returned if this fails. * * @note The pointer returned must be freed when not used anymore with #b2nd_free_ctx. * */ BLOSC_EXPORT b2nd_context_t * b2nd_create_ctx(const blosc2_storage *b2_storage, int8_t ndim, const int64_t *shape, const int32_t *chunkshape, const int32_t *blockshape, const char *dtype, int8_t dtype_format, const blosc2_metalayer *metalayers, int32_t nmetalayers); /** * @brief Free the resources associated with b2nd_context_t. * * @param ctx The b2nd context to free. * * @return An error code. * * @note This is safe in the sense that it will not free the schunk pointer in internal cparams. * */ BLOSC_EXPORT int b2nd_free_ctx(b2nd_context_t *ctx); /** * @brief Create an uninitialized array. * * @param ctx The b2nd context for the new array. * @param array The memory pointer where the array will be created. * * @return An error code. */ BLOSC_EXPORT int b2nd_uninit(b2nd_context_t *ctx, b2nd_array_t **array); /** * @brief Create an empty array. * * @param ctx The b2nd context for the new array. * @param array The memory pointer where the array will be created. * * @return An error code. */ BLOSC_EXPORT int b2nd_empty(b2nd_context_t *ctx, b2nd_array_t **array); /** * Create an array, with zero being used as the default value for * uninitialized portions of the array. * * @param ctx The b2nd context for the new array. * @param array The memory pointer where the array will be created. * * @return An error code. */ BLOSC_EXPORT int b2nd_zeros(b2nd_context_t *ctx, b2nd_array_t **array); /** * Create an array, with NaN being used as the default value for * uninitialized portions of the array. Should only be used with type sizes * of either 4 or 8. Other sizes generate an error. * * @param ctx The b2nd context for the new array. * @param array The memory pointer where the array will be created. * * @return An error code. */ BLOSC_EXPORT int b2nd_nans(b2nd_context_t *ctx, b2nd_array_t **array); /** * Create an array, with @p fill_value being used as the default value for * uninitialized portions of the array. * * @param ctx The b2nd context for the new array. * @param array The memory pointer where the array will be created. * @param fill_value Default value for uninitialized portions of the array. * * @return An error code. */ BLOSC_EXPORT int b2nd_full(b2nd_context_t *ctx, b2nd_array_t **array, const void *fill_value); /** * @brief Free an array. * * @param array The array. * * @return An error code. */ BLOSC_EXPORT int b2nd_free(b2nd_array_t *array); /** * @brief Create a b2nd array from a super-chunk. It can only be used if the array * is backed by a blosc super-chunk. * * @param schunk The blosc super-chunk where the b2nd array is stored. * @param array The memory pointer where the array will be created. * * @return An error code. */ BLOSC_EXPORT int b2nd_from_schunk(blosc2_schunk *schunk, b2nd_array_t **array); /** * Create a serialized super-chunk from a b2nd array. * * @param array The b2nd array to be serialized. * @param cframe The pointer of the buffer where the in-memory array will be copied. * @param cframe_len The length of the in-memory array buffer. * @param needs_free Whether the buffer should be freed or not. * * @return An error code */ BLOSC_EXPORT int b2nd_to_cframe(const b2nd_array_t *array, uint8_t **cframe, int64_t *cframe_len, bool *needs_free); /** * @brief Create a b2nd array from a serialized super-chunk. * * @param cframe The buffer of the in-memory array. * @param cframe_len The size (in bytes) of the in-memory array. * @param copy Whether b2nd should make a copy of the cframe data or not. The copy will be made to an internal sparse frame. * @param array The memory pointer where the array will be created. * * @return An error code. */ BLOSC_EXPORT int b2nd_from_cframe(uint8_t *cframe, int64_t cframe_len, bool copy, b2nd_array_t **array); /** * @brief Open a b2nd array from a file. * * @param urlpath The path of the b2nd array on disk. * @param array The memory pointer where the array info will be stored. * * @return An error code. */ BLOSC_EXPORT int b2nd_open(const char *urlpath, b2nd_array_t **array); /** * @brief Open a b2nd array from a file using an offset. * * @param urlpath The path of the b2nd array on disk. * @param array The memory pointer where the array info will be stored. * @param offset The offset in the file where the b2nd array frame starts. * * @return An error code. */ BLOSC_EXPORT int b2nd_open_offset(const char *urlpath, b2nd_array_t **array, int64_t offset); /** * @brief Save b2nd array into a specific urlpath. * * @param array The array to be saved. * @param urlpath The urlpath where the array will be stored. * * @return An error code. */ BLOSC_EXPORT int b2nd_save(const b2nd_array_t *array, char *urlpath); /** * @brief Append a b2nd array into a file. * * @param array The array to write. * @param urlpath The path for persistent storage. * * @return If successful, return the offset where @p array has been appended in @p urlpath. * Else, a negative value. */ BLOSC_EXPORT int64_t b2nd_save_append(const b2nd_array_t *array, const char *urlpath); /** * @brief Create a b2nd array from a C buffer. * * @param ctx The b2nd context for the new array. * @param array The memory pointer where the array will be created. * @param buffer The buffer where source data is stored. * @param buffersize The size (in bytes) of the buffer. * * @return An error code. */ BLOSC_EXPORT int b2nd_from_cbuffer(b2nd_context_t *ctx, b2nd_array_t **array, const void *buffer, int64_t buffersize); /** * @brief Extract the data from a b2nd array into a C buffer. * * @param array The b2nd array. * @param buffer The buffer where the data will be stored. * @param buffersize Size (in bytes) of the buffer. * * @return An error code. */ BLOSC_EXPORT int b2nd_to_cbuffer(const b2nd_array_t *array, void *buffer, int64_t buffersize); /** * @brief Gather items from a b2nd array of any dimensionality by flat logical coordinates. * * Fully supports arrays of any number of dimensions (1-D, 2-D, ... N-D). * Regardless of the array's dimensionality, every element is addressed by a * single flat, C-order logical index in the range ``[0, array->nitems)`` * -- the same linearisation used by ``b2nd_from_cbuffer`` / ``b2nd_to_cbuffer``. * For example, element ``(i, j)`` of a 2-D array with shape ``(R, C)`` has * flat index ``i * C + j``; element ``(i, j, k)`` of a 3-D array with shape * ``(A, B, C)`` has flat index ``i * B * C + j * C + k``; and so on for * higher dimensions. * * The function translates each flat logical coordinate to the padded storage * coordinate used internally by b2nd (chunk → block → item within block) and * then gathers the requested items into ``buffer`` in the same order as * ``coords``. Repeated and unsorted coordinates are allowed. * * This is a low-level sparse gather primitive. It is most useful for fancy * indexing and as a building block for higher-level ``take`` operations. * * @param array The source b2nd array (any ndim). * @param ncoords Number of coordinates to gather. May be zero. * @param coords Flat C-order logical coordinates in ``[0, array->nitems)``. * Must not be NULL when ``ncoords > 0``. * @param buffer Destination buffer. Must have room for at least * ``ncoords * array->sc->typesize`` bytes and must not be NULL * when ``ncoords > 0``. * @param buffersize Destination buffer size in bytes. * * @return A non-negative value on success, a negative error code otherwise. */ BLOSC_EXPORT int b2nd_get_sparse_cbuffer(const b2nd_array_t *array, int64_t ncoords, const int64_t *coords, void *buffer, int64_t buffersize); /** * @brief Get a slice from an array and store it into a new array. * * @param ctx The b2nd context for the new array. * @param array The memory pointer where the array will be created. * @param src The array from which the slice will be extracted * @param start The coordinates where the slice will begin. * @param stop The coordinates where the slice will end. * * @return An error code. * * @note The ndim and shape from ctx will be overwritten by the src and stop-start respectively. * */ BLOSC_EXPORT int b2nd_get_slice(b2nd_context_t *ctx, b2nd_array_t **array, const b2nd_array_t *src, const int64_t *start, const int64_t *stop); /** * @brief Squeeze a b2nd array * * This function remove selected single-dimensional entries from the shape of a b2nd array. * * @param array The b2nd array. * @param view The memory pointer where the new view will be created. * @param index Indexes of the single-dimensional entries to remove. * * @return An error code */ BLOSC_EXPORT int b2nd_squeeze_index(b2nd_array_t *array, b2nd_array_t **view, const bool *index); /** * @brief Squeeze a b2nd array * * This function remove single-dimensional entries from the shape of a b2nd array. * * @param array The b2nd array. * @param view The memory pointer where the new view will be created. * * @return An error code */ BLOSC_EXPORT int b2nd_squeeze(b2nd_array_t *array, b2nd_array_t **view); /** * @brief Add a newaxis to a b2nd array at location @p axis. * * @param array The b2nd array to be expanded. * @param axis The axes where the new dimensions will be added. * @param view The memory pointer where the new view will be created. * @param final_dims The final number of dimensions. Should be same as the number of elements in @p axis. * * @return An error code. */ BLOSC_EXPORT int b2nd_expand_dims(const b2nd_array_t *array, b2nd_array_t **view, const bool *axis, const uint8_t final_dims); /** * @brief Get a slice from an array and store it into a C buffer. * * @param array The array from which the slice will be extracted. * @param start The coordinates where the slice will begin. * @param stop The coordinates where the slice will end. * @param buffershape The shape of the buffer. * @param buffer The buffer where the data will be stored. * @param buffersize The size (in bytes) of the buffer. * * @return An error code. */ BLOSC_EXPORT int b2nd_get_slice_cbuffer(const b2nd_array_t *array, const int64_t *start, const int64_t *stop, void *buffer, const int64_t *buffershape, int64_t buffersize); /** * @brief Set a slice in a b2nd array using a C buffer. * * @param buffer The buffer where the slice data is. * @param buffershape The shape of the buffer. * @param buffersize The size (in bytes) of the buffer. * @param start The coordinates where the slice will begin. * @param stop The coordinates where the slice will end. * @param array The b2nd array where the slice will be set * * @return An error code. */ BLOSC_EXPORT int b2nd_set_slice_cbuffer(const void *buffer, const int64_t *buffershape, int64_t buffersize, const int64_t *start, const int64_t *stop, b2nd_array_t *array); /** * @brief Make a copy of the array data. The copy is done into a new b2nd array. * * @param ctx The b2nd context for the new array. * @param src The array from which data is copied. * @param array The memory pointer where the array will be created. * * @return An error code * * @note The ndim and shape in ctx will be overwritten by the src ctx. * */ BLOSC_EXPORT int b2nd_copy(b2nd_context_t *ctx, const b2nd_array_t *src, b2nd_array_t **array); /** * @brief Concatenate arrays. The result is stored in a new b2nd array, or an enlarged one. * * @param ctx The b2nd context for the new array. * @param src1 The first array from which data is copied. * @param src2 The second array from which data is copied. * @param axis The axis along which the arrays will be concatenated. * @param copy Whether the data should be copied or not. If false, the @p src1 array * will be expanded as needed to keep the result. * @param array The memory pointer where the array will be created. It will have the same * metalayers of @p src1, except for the b2nd metalayer, which will be updated with the * new shape. * * @ note The two arrays must have the same shape in all dimensions except the concatenation axis. * Also, the typesize of the two arrays must be the same. * * @return An error code * * @note The ndim and shape in ctx will be overwritten by the src1 ctx. * */ BLOSC_EXPORT int b2nd_concatenate(b2nd_context_t *ctx, const b2nd_array_t *src1, const b2nd_array_t *src2, int8_t axis, bool copy, b2nd_array_t **array); /** * @brief Print metalayer parameters. * * @param array The array where the metalayer is stored. * * @return An error code */ BLOSC_EXPORT int b2nd_print_meta(const b2nd_array_t *array); /** * @brief Resize the shape of an array * * @param array The array to be resized. * @param new_shape The new shape from the array. * @param start The position in which the array will be extended or shrunk. * * @return An error code */ BLOSC_EXPORT int b2nd_resize(b2nd_array_t *array, const int64_t *new_shape, const int64_t *start); /** * @brief Insert given buffer in an array extending the given axis. * * @param array The array to insert the data in. * @param buffer The buffer data to be inserted. * @param buffersize The size (in bytes) of the buffer. * @param axis The axis that will be extended. * @param insert_start The position inside the axis to start inserting the data. * * @return An error code. */ BLOSC_EXPORT int b2nd_insert(b2nd_array_t *array, const void *buffer, int64_t buffersize, int8_t axis, int64_t insert_start); /** * Append a buffer at the end of a b2nd array. * * @param array The array to append the data in. * @param buffer The buffer data to be appended. * @param buffersize Size (in bytes) of the buffer. * @param axis The axis that will be extended to append the data. * * @return An error code. */ BLOSC_EXPORT int b2nd_append(b2nd_array_t *array, const void *buffer, int64_t buffersize, int8_t axis); /** * @brief Delete shrinking the given axis delete_len items. * * @param array The array to shrink. * @param axis The axis to shrink. * @param delete_start The start position from the axis to start deleting chunks. * @param delete_len The number of items to delete to the array->shape[axis]. * The newshape[axis] will be the old array->shape[axis] - delete_len * * @return An error code. * * @note See also b2nd_resize */ BLOSC_EXPORT int b2nd_delete(b2nd_array_t *array, int8_t axis, int64_t delete_start, int64_t delete_len); // Indexing section /** * @brief Get an element selection along each dimension of an array independently. * * @param array The array to get the data from. * @param selection The elements along each dimension. * @param selection_size The size of the selection along each dimension. * @param buffer The buffer for getting the data. * @param buffershape The shape of the buffer. * @param buffersize The buffer size (in bytes). * * @return An error code. * * @note See also b2nd_set_orthogonal_selection. */ BLOSC_EXPORT int b2nd_get_orthogonal_selection(const b2nd_array_t *array, int64_t **selection, int64_t *selection_size, void *buffer, int64_t *buffershape, int64_t buffersize); /** * @brief Set an element selection along each dimension of an array independently. * * @param array The array to set the data to. * @param selection The elements along each dimension. * @param selection_size The size of the selection along each dimension. * @param buffer The buffer with the data for setting. * @param buffershape The shape of the buffer. * @param buffersize The buffer size (in bytes). * * @return An error code. * * @note See also b2nd_get_orthogonal_selection. */ BLOSC_EXPORT int b2nd_set_orthogonal_selection(b2nd_array_t *array, int64_t **selection, int64_t *selection_size, const void *buffer, int64_t *buffershape, int64_t buffersize); /** * @brief Create the metainfo for the b2nd metalayer. * * @param ndim The number of dimensions in the array. * @param shape The shape of the array. * @param chunkshape The shape of the chunks in the array. * @param blockshape The shape of the blocks in the array. * @param dtype A string representation of the data type of the array. * @param dtype_format The format of the dtype representation. 0 means NumPy. * @param smeta The msgpack buffer (output). * * @return An error code. */ BLOSC_EXPORT int b2nd_serialize_meta(int8_t ndim, const int64_t *shape, const int32_t *chunkshape, const int32_t *blockshape, const char *dtype, int8_t dtype_format, uint8_t **smeta); /** * @brief Read the metainfo in the b2nd metalayer (inline version). * * @param smeta The msgpack buffer (input). * @param smeta_len The length of the smeta buffer (input). * @param ndim The number of dimensions in the array (output). * @param shape The shape of the array (output). * @param chunkshape The shape of the chunks in the array (output). * @param blockshape The shape of the blocks in the array (output). * @param dtype A string representation of the data type of the array (output). * @param dtype_format The format of the dtype representation (output). 0 means NumPy (the default). * * @note This function is inlined so that external codec/filter plugins (like * blosc2_grok) can use it without linking against libblosc2. This avoids * pulling all of libblosc2's symbols (e.g. internal ZFP, Zstd) into the * global namespace at load time, which would otherwise shadow symbols from * other libraries that need differently-configured builds of the same * dependencies. * * @return An error code. */ static inline int b2nd_deserialize_meta_inline(const uint8_t *smeta, int32_t smeta_len, int8_t *ndim, int64_t *shape, int32_t *chunkshape, int32_t *blockshape, char **dtype, int8_t *dtype_format) { BLOSC_ERROR_NULL(smeta, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(ndim, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(shape, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(chunkshape, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(blockshape, BLOSC2_ERROR_NULL_POINTER); if (dtype != NULL) { *dtype = NULL; } if (dtype_format != NULL) { *dtype_format = 0; } if (smeta_len <= 0) { BLOSC_TRACE_ERROR("Malformed b2nd metalayer: empty metadata"); return BLOSC2_ERROR_FAILURE; } const uint8_t *pmeta = smeta; #define B2ND_REQUIRE_META_NBYTES(nbytes) \ do { \ size_t consumed = (size_t)(pmeta - smeta); \ size_t total = (size_t)smeta_len; \ if (consumed > total || (total - consumed) < (size_t)(nbytes)) { \ BLOSC_TRACE_ERROR("Malformed b2nd metalayer: truncated metadata"); \ return BLOSC2_ERROR_FAILURE; \ } \ } while (0) // Check that we have an array with 7 entries (version, ndim, shape, chunkshape, blockshape, dtype_format, dtype) B2ND_REQUIRE_META_NBYTES(1); pmeta += 1; // version entry // int8_t version = (int8_t)pmeta[0]; // positive fixnum (7-bit positive integer) commented to avoid warning B2ND_REQUIRE_META_NBYTES(1); pmeta += 1; // ndim entry B2ND_REQUIRE_META_NBYTES(1); *ndim = (int8_t) pmeta[0]; int8_t ndim_aux = *ndim; // positive fixnum (7-bit positive integer) if (ndim_aux < 0 || ndim_aux > B2ND_MAX_DIM) { BLOSC_TRACE_ERROR("ndim %d is out of range", ndim_aux); return BLOSC2_ERROR_FAILURE; } pmeta += 1; // shape entry // Initialize to ones, as required by b2nd for (int i = 0; i < ndim_aux; i++) shape[i] = 1; B2ND_REQUIRE_META_NBYTES(1); pmeta += 1; for (int8_t i = 0; i < ndim_aux; i++) { B2ND_REQUIRE_META_NBYTES(1 + sizeof(int64_t)); pmeta += 1; swap_store(shape + i, pmeta, sizeof(int64_t)); pmeta += sizeof(int64_t); } // chunkshape entry // Initialize to ones, as required by b2nd for (int i = 0; i < ndim_aux; i++) chunkshape[i] = 1; B2ND_REQUIRE_META_NBYTES(1); pmeta += 1; for (int8_t i = 0; i < ndim_aux; i++) { B2ND_REQUIRE_META_NBYTES(1 + sizeof(int32_t)); pmeta += 1; swap_store(chunkshape + i, pmeta, sizeof(int32_t)); pmeta += sizeof(int32_t); } // blockshape entry // Initialize to ones, as required by b2nd for (int i = 0; i < ndim_aux; i++) blockshape[i] = 1; B2ND_REQUIRE_META_NBYTES(1); pmeta += 1; for (int8_t i = 0; i < ndim_aux; i++) { B2ND_REQUIRE_META_NBYTES(1 + sizeof(int32_t)); pmeta += 1; swap_store(blockshape + i, pmeta, sizeof(int32_t)); pmeta += sizeof(int32_t); } // dtype entry if (dtype_format == NULL || dtype == NULL) { return (int32_t)(pmeta - smeta); } if (pmeta - smeta < smeta_len) { // dtype info is here B2ND_REQUIRE_META_NBYTES(1 + 1 + sizeof(int32_t)); *dtype_format = (int8_t) *(pmeta++); if (*pmeta != 0xdb) { BLOSC_TRACE_ERROR("Malformed b2nd metalayer: invalid dtype MsgPack marker"); return BLOSC2_ERROR_FAILURE; } pmeta += 1; int32_t dtype_len; swap_store(&dtype_len, pmeta, sizeof(int32_t)); pmeta += sizeof(int32_t); if (dtype_len < 0) { BLOSC_TRACE_ERROR("Malformed b2nd metalayer: negative dtype length"); return BLOSC2_ERROR_FAILURE; } B2ND_REQUIRE_META_NBYTES(dtype_len); size_t dtype_len_ = (size_t)dtype_len; *dtype = (char*)malloc(dtype_len_ + 1); BLOSC_ERROR_NULL(*dtype, BLOSC2_ERROR_MEMORY_ALLOC); char* dtype_ = *dtype; memcpy(dtype_, (char*)pmeta, dtype_len_); dtype_[dtype_len_] = '\0'; pmeta += dtype_len_; } else { // dtype is mandatory in b2nd metalayer, but this is mainly meant as // a fall-back for deprecated caterva headers *dtype = NULL; *dtype_format = 0; } #undef B2ND_REQUIRE_META_NBYTES int32_t slen = (int32_t) (pmeta - smeta); return (int)slen; } /** * @brief Read the metainfo in the b2nd metalayer (ABI entry point). * * This is a wrapper around @ref b2nd_deserialize_meta_inline for ABI compatibility. * New code that only includes headers should use b2nd_deserialize_meta_inline() * directly to avoid the need to link against libblosc2. * * @see b2nd_deserialize_meta_inline */ BLOSC_EXPORT int b2nd_deserialize_meta(const uint8_t *smeta, int32_t smeta_len, int8_t *ndim, int64_t *shape, int32_t *chunkshape, int32_t *blockshape, char **dtype, int8_t *dtype_format); // Utilities for C buffers representing multidimensional arrays /** * @brief Copy a slice of a source array into another array. The arrays have * the same number of dimensions (though their shapes may differ), the same * item size, and they are stored as C buffers with contiguous data (any * padding is considered part of the array). * * @param ndim The number of dimensions in both arrays. * @param itemsize The size of the individual data item in both arrays. * @param src The buffer for getting the data from the source array. * @param src_pad_shape The shape of the source array, including padding. * @param src_start The source coordinates where the slice will begin. * @param src_stop The source coordinates where the slice will end. * @param dst The buffer for setting the data into the destination array. * @param dst_pad_shape The shape of the destination array, including padding. * @param dst_start The destination coordinates where the slice will be placed. * * @return An error code. * * @note This is kept for backward compatibility with existing code out there. New code should use * b2nd_copy_buffer2 instead. * * @note Please make sure that slice boundaries fit within the source and * destination arrays before using this function, as it does not perform these * checks itself. */ B2ND_DEPRECATED("Use b2nd_copy_buffer2 instead.") BLOSC_EXPORT int b2nd_copy_buffer(int8_t ndim, uint8_t itemsize, const void *src, const int64_t *src_pad_shape, const int64_t *src_start, const int64_t *src_stop, void *dst, const int64_t *dst_pad_shape, const int64_t *dst_start); /** * @brief Copy a slice of a source array into another array. The arrays have * the same number of dimensions (though their shapes may differ), the same * item size, and they are stored as C buffers with contiguous data (any * padding is considered part of the array). * * @param ndim The number of dimensions in both arrays. * @param itemsize The size of the individual data item in both arrays. * @param src The buffer for getting the data from the source array. * @param src_pad_shape The shape of the source array, including padding. * @param src_start The source coordinates where the slice will begin. * @param src_stop The source coordinates where the slice will end. * @param dst The buffer for setting the data into the destination array. * @param dst_pad_shape The shape of the destination array, including padding. * @param dst_start The destination coordinates where the slice will be placed. * * @return An error code. * * @note This is a version of (now deprecated) b2nd_copy_buffer() that uses * signed 32-bit integers for copying data. This is useful when data is stored * in a buffer that uses itemsizes that are larger than 255 bytes. * * @note Please make sure that slice boundaries fit within the source and * destination arrays before using this function, as it does not perform these * checks itself. */ BLOSC_EXPORT int b2nd_copy_buffer2(int8_t ndim, int32_t itemsize, const void *src, const int64_t *src_pad_shape, const int64_t *src_start, const int64_t *src_stop, void *dst, const int64_t *dst_pad_shape, const int64_t *dst_start); #ifdef __cplusplus } #endif #endif /* BLOSC_B2ND_H */ c-blosc2-3.1.5/include/blosc2.h000066400000000000000000003251611521743436100161220ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /********************************************************************* @file blosc2.h @brief Blosc2 header file. This file contains Blosc2 public API and the structures needed to use it. @author Blosc Development Team **********************************************************************/ #ifndef BLOSC_BLOSC2_H #define BLOSC_BLOSC2_H #include "blosc2/blosc2-export.h" #include "blosc2/blosc2-common.h" #include "blosc2/blosc2-stdio.h" #if defined(_WIN32) && !defined(__MINGW32__) #include #include #include #define getpid _getpid #endif #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif // For compatibility with the Blosc 1.x series #ifdef BLOSC1_COMPAT // Blosc2 symbols that should be accessible from Blosc 1.x API #define BLOSC_VERSION_MAJOR BLOSC2_VERSION_MAJOR #define BLOSC_VERSION_MINOR BLOSC2_VERSION_MINOR #define BLOSC_VERSION_RELEASE BLOSC2_VERSION_RELEASE #define BLOSC_VERSION_STRING BLOSC2_VERSION_STRING #define BLOSC_VERSION_DATE BLOSC2_VERSION_DATE #define BLOSC_MAX_OVERHEAD BLOSC2_MAX_OVERHEAD #define BLOSC_MAX_BUFFERSIZE BLOSC2_MAX_BUFFERSIZE // API that changed to blosc1_ prefix #define blosc_compress blosc1_compress #define blosc_decompress blosc1_decompress #define blosc_getitem blosc1_getitem #define blosc_get_compressor blosc1_get_compressor #define blosc_set_compressor blosc1_set_compressor #define blosc_cbuffer_sizes blosc1_cbuffer_sizes #define blosc_cbuffer_validate blosc1_cbuffer_validate #define blosc_cbuffer_metainfo blosc1_cbuffer_metainfo #define blosc_get_blocksize blosc1_get_blocksize #define blosc_set_blocksize blosc1_set_blocksize #define blosc_set_splitmode blosc1_set_splitmode // API that changed to blosc2_ prefix #define blosc_init blosc2_init #define blosc_destroy blosc2_destroy #define blosc_free_resources blosc2_free_resources #define blosc_get_nthreads blosc2_get_nthreads #define blosc_set_nthreads blosc2_set_nthreads #define blosc_compcode_to_compname blosc2_compcode_to_compname #define blosc_compname_to_compcode blosc2_compname_to_compcode #define blosc_list_compressors blosc2_list_compressors #define blosc_get_version_string blosc2_get_version_string #define blosc_get_complib_info blosc2_get_complib_info #define blosc_cbuffer_versions blosc2_cbuffer_versions #define blosc_cbuffer_complib blosc2_cbuffer_complib #endif /* Version numbers */ #define BLOSC2_VERSION_MAJOR 3 /* for major interface/format changes */ #define BLOSC2_VERSION_MINOR 1 /* for minor interface/format changes */ #define BLOSC2_VERSION_RELEASE 5 /* for tweaks, bug-fixes, or development */ #define BLOSC2_VERSION_STRING "3.1.5" /* string version. Sync with above! */ #define BLOSC2_VERSION_DATE "$Date:: 2026-06-26 #$" /* date version year-month-day */ /* Tracing macros */ #define BLOSC_TRACE_ERROR(msg, ...) BLOSC_TRACE(error, msg, ##__VA_ARGS__) #define BLOSC_TRACE_WARNING(msg, ...) BLOSC_TRACE(warning, msg, ##__VA_ARGS__) #define BLOSC_TRACE_INFO(msg, ...) BLOSC_TRACE(info, msg, ##__VA_ARGS__) #define BLOSC_TRACE(cat, msg, ...) \ do { \ const char *__e = getenv("BLOSC_TRACE"); \ if (!__e) { break; } \ fprintf(stderr, "[%s] - " msg " (%s:%d)\n", #cat, ##__VA_ARGS__, __FILE__, __LINE__); \ } while(0) #define BLOSC_ERROR_NULL(pointer, rc) \ do { \ if ((pointer) == NULL) { \ BLOSC_TRACE_ERROR("Pointer is null"); \ return (rc); \ } \ } while (0) #define BLOSC_ERROR(rc) \ do { \ int rc_ = (rc); \ if (rc_ < BLOSC2_ERROR_SUCCESS) { \ char *error_msg = print_error(rc_); \ BLOSC_TRACE_ERROR("%s", error_msg); \ return rc_; \ } \ } while (0) #define BLOSC_INFO(msg, ...) \ do { \ const char *__e = getenv("BLOSC_INFO"); \ if (!__e) { break; } \ fprintf(stderr, "[INFO] - " msg "\n", ##__VA_ARGS__); \ } while(0) /* The VERSION_FORMAT symbols below should be just 1-byte long */ enum { /* Blosc format version, starting at 1 1 -> Blosc pre-1.0 2 -> Blosc 1.x stable series 3 -> Blosc 2-alpha.x series 4 -> Blosc 2.x beta.1 series 5 -> Blosc 2.x stable series */ BLOSC1_VERSION_FORMAT_PRE1 = 1, BLOSC1_VERSION_FORMAT = 2, BLOSC2_VERSION_FORMAT_ALPHA = 3, BLOSC2_VERSION_FORMAT_BETA1 = 4, BLOSC2_VERSION_FORMAT_STABLE = 5, BLOSC2_VERSION_FORMAT_VL_BLOCKS = 6, /* Highest chunk format version supported by this library. */ BLOSC2_VERSION_FORMAT = BLOSC2_VERSION_FORMAT_VL_BLOCKS, }; /* The FRAME_FORMAT_VERSION symbols below should be just 4-bit long */ enum { /* Blosc format version * 1 -> First version (introduced in beta.2) * 2 -> Second version (introduced in rc.1) * */ BLOSC2_VERSION_FRAME_FORMAT_BETA2 = 1, // for 2.0.0-beta2 and after BLOSC2_VERSION_FRAME_FORMAT_RC1 = 2, // for 2.0.0-rc1 and after BLOSC2_VERSION_FRAME_FORMAT_VL_BLOCKS = 3, /* Highest cframe format version supported by this library. */ BLOSC2_VERSION_FRAME_FORMAT = BLOSC2_VERSION_FRAME_FORMAT_VL_BLOCKS, }; //!< Struct for storing data from instrumentation of codecs // This can be flexible because it is typically used mainly for development typedef struct { float cratio; float cspeed; float filter_speed; //float memory; //float power; uint8_t flags[4]; } blosc2_instr; enum { #ifndef BLOSC_H BLOSC_MIN_HEADER_LENGTH = 16, //!< Minimum header length (Blosc1) #endif // BLOSC_H BLOSC_EXTENDED_HEADER_LENGTH = 32, //!< Extended header length (Blosc2, see README_HEADER) BLOSC2_MAX_OVERHEAD = BLOSC_EXTENDED_HEADER_LENGTH, //!< The maximum overhead during compression in bytes. This equals //!< to @ref BLOSC_EXTENDED_HEADER_LENGTH now, but can be higher in future //!< implementations. BLOSC2_MAX_BUFFERSIZE = (INT_MAX - BLOSC2_MAX_OVERHEAD), //!< Maximum source buffer size to be compressed #ifndef BLOSC_H BLOSC_MAX_TYPESIZE = UINT8_MAX, //!< Maximum typesize before considering source buffer as a stream of bytes. //!< Cannot be larger than 255. #endif // BLOSC_H BLOSC_MIN_BUFFERSIZE = 32, //!< Minimum buffer size to be compressed. }; enum { BLOSC2_DEFINED_TUNER_START = 0, BLOSC2_DEFINED_TUNER_STOP = 31, //!< Blosc-defined tuners must be between 0 - 31. BLOSC2_GLOBAL_REGISTERED_TUNER_START = 32, BLOSC2_GLOBAL_REGISTERED_TUNER_STOP = 159, //!< Blosc-registered tuners must be between 31 - 159. BLOSC2_GLOBAL_REGISTERED_TUNERS = 0, //!< Number of Blosc-registered tuners at the moment. BLOSC2_USER_REGISTERED_TUNER_START = 160, BLOSC2_USER_REGISTERED_TUNER_STOP = 255, //!< User-defined tuners must be between 160 - 255. }; /** * @brief Codes for the different tuners shipped with Blosc */ enum { BLOSC_STUNE = 0, BLOSC_LAST_TUNER = 1, //!< Determine the last tuner defined by Blosc. BLOSC_LAST_REGISTERED_TUNE = BLOSC2_GLOBAL_REGISTERED_TUNER_START + BLOSC2_GLOBAL_REGISTERED_TUNERS - 1, //!< Determine the last registered tuner. It is used to check if a tuner is registered or not. }; enum { BLOSC2_DEFINED_FILTERS_START = 0, BLOSC2_DEFINED_FILTERS_STOP = 31, //!< Blosc-defined filters must be between 0 - 31. BLOSC2_GLOBAL_REGISTERED_FILTERS_START = 32, BLOSC2_GLOBAL_REGISTERED_FILTERS_STOP = 159, //!< Blosc-registered filters must be between 32 - 159. BLOSC2_GLOBAL_REGISTERED_FILTERS = 5, //!< Number of Blosc-registered filters at the moment. BLOSC2_USER_REGISTERED_FILTERS_START = 160, BLOSC2_USER_REGISTERED_FILTERS_STOP = 255, //!< User-defined filters must be between 128 - 255. BLOSC2_MAX_FILTERS = 6, //!< Maximum number of filters in the filter pipeline BLOSC2_MAX_UDFILTERS = 16, //!< Maximum number of filters that a user can register. }; /** * @brief Codes for filters. * * @sa #blosc1_compress */ enum { #ifndef BLOSC_H BLOSC_NOSHUFFLE = 0, //!< No shuffle (for compatibility with Blosc1). BLOSC_NOFILTER = 0, //!< No filter. BLOSC_SHUFFLE = 1, //!< Byte-wise shuffle. `filters_meta` is the number of bytestreams to shuffle from the input. If 0, defaults to typesize. BLOSC_BITSHUFFLE = 2, //!< Bit-wise shuffle. `filters_meta` does not have any effect. #endif // BLOSC_H BLOSC_DELTA = 3, //!< Delta filter (bitwise XOR relative to reference). `filters_meta` does not have any effect. BLOSC_TRUNC_PREC = 4, //!< Truncate mantissa precision. //!< Positive values in `filters_meta` will keep bits; negative values will zero bits. BLOSC_LAST_FILTER = 5, //!< sentinel BLOSC_LAST_REGISTERED_FILTER = BLOSC2_GLOBAL_REGISTERED_FILTERS_START + BLOSC2_GLOBAL_REGISTERED_FILTERS - 1, //!< Determine the last registered filter. It is used to check if a filter is registered or not. }; /** * @brief Codes for internal flags (see blosc1_cbuffer_metainfo) */ enum { #ifndef BLOSC_H BLOSC_DOSHUFFLE = 0x1, //!< byte-wise shuffle BLOSC_MEMCPYED = 0x2, //!< plain copy BLOSC_DOBITSHUFFLE = 0x4, //!< bit-wise shuffle #endif // BLOSC_H BLOSC_DODELTA = 0x8, //!< delta coding }; /** * @brief Codes for new internal flags in Blosc2 */ enum { BLOSC2_USEDICT = 0x1, //!< use dictionaries with codec BLOSC2_BIGENDIAN = 0x2, //!< data is in big-endian ordering BLOSC2_INSTR_CODEC = 0x80, //!< codec is instrumented (mainly for development) }; /** * @brief Codes for flags in the secondary Blosc2 header byte. */ enum { BLOSC2_VL_BLOCKS = 0x1, //!< chunk uses variable-length blocks }; /** * @brief Values for different Blosc2 capabilities */ enum { BLOSC2_MAXDICTSIZE = 32 * 1024, //!< maximum size for compression dicts BLOSC2_MINUSEFULDICT = 256, //!< minimum dict size considered useful; smaller → fall back to plain compression BLOSC2_MAXBLOCKSIZE = 536866816, //!< maximum size for blocks BLOSC2_MAXTYPESIZE = BLOSC2_MAXBLOCKSIZE, //!< maximum size for types }; enum { BLOSC2_DEFINED_CODECS_START = 0, BLOSC2_DEFINED_CODECS_STOP = 31, //!< Blosc-defined codecs must be between 0 - 31. BLOSC2_GLOBAL_REGISTERED_CODECS_START = 32, BLOSC2_GLOBAL_REGISTERED_CODECS_STOP = 159, //!< Blosc-registered codecs must be between 31 - 159. BLOSC2_GLOBAL_REGISTERED_CODECS = 5, //!< Number of Blosc-registered codecs at the moment. BLOSC2_USER_REGISTERED_CODECS_START = 160, BLOSC2_USER_REGISTERED_CODECS_STOP = 255, //!< User-defined codecs must be between 160 - 255. }; /** * @brief Codes for the different compressors shipped with Blosc */ enum { #ifndef BLOSC_H BLOSC_BLOSCLZ = 0, BLOSC_LZ4 = 1, BLOSC_LZ4HC = 2, BLOSC_ZLIB = 4, BLOSC_ZSTD = 5, #endif // BLOSC_H BLOSC_LAST_CODEC = 6, //!< Determine the last codec defined by Blosc. BLOSC_LAST_REGISTERED_CODEC = BLOSC2_GLOBAL_REGISTERED_CODECS_START + BLOSC2_GLOBAL_REGISTERED_CODECS - 1, //!< Determine the last registered codec. It is used to check if a codec is registered or not. }; // Names for the different compressors shipped with Blosc #ifndef BLOSC_H #define BLOSC_BLOSCLZ_COMPNAME "blosclz" #define BLOSC_LZ4_COMPNAME "lz4" #define BLOSC_LZ4HC_COMPNAME "lz4hc" #define BLOSC_ZLIB_COMPNAME "zlib" #define BLOSC_ZSTD_COMPNAME "zstd" #endif // BLOSC_H /** * @brief Codes for compression libraries shipped with Blosc (code must be < 8) */ enum { #ifndef BLOSC_H BLOSC_BLOSCLZ_LIB = 0, BLOSC_LZ4_LIB = 1, BLOSC_ZLIB_LIB = 3, BLOSC_ZSTD_LIB = 4, #endif // BLOSC_H BLOSC_UDCODEC_LIB = 6, BLOSC_SCHUNK_LIB = 7, //!< compressor library in super-chunk header }; /** * @brief Names for the different compression libraries shipped with Blosc */ #ifndef BLOSC_H #define BLOSC_BLOSCLZ_LIBNAME "BloscLZ" #define BLOSC_LZ4_LIBNAME "LZ4" #define BLOSC_ZLIB_LIBNAME "Zlib" #define BLOSC_ZSTD_LIBNAME "Zstd" #endif // BLOSC_H /** * @brief The codes for compressor formats shipped with Blosc */ enum { #ifndef BLOSC_H BLOSC_BLOSCLZ_FORMAT = BLOSC_BLOSCLZ_LIB, BLOSC_LZ4_FORMAT = BLOSC_LZ4_LIB, //!< LZ4HC and LZ4 share the same format BLOSC_LZ4HC_FORMAT = BLOSC_LZ4_LIB, BLOSC_ZLIB_FORMAT = BLOSC_ZLIB_LIB, BLOSC_ZSTD_FORMAT = BLOSC_ZSTD_LIB, #endif // BLOSC_H BLOSC_UDCODEC_FORMAT = BLOSC_UDCODEC_LIB, }; /** * @brief The version formats for compressors shipped with Blosc. * All versions here starts at 1 */ enum { #ifndef BLOSC_H BLOSC_BLOSCLZ_VERSION_FORMAT = 1, BLOSC_LZ4_VERSION_FORMAT = 1, BLOSC_LZ4HC_VERSION_FORMAT = 1, /* LZ4HC and LZ4 share the same format */ BLOSC_ZLIB_VERSION_FORMAT = 1, BLOSC_ZSTD_VERSION_FORMAT = 1, #endif // BLOSC_H BLOSC_UDCODEC_VERSION_FORMAT = 1, }; /** * @brief Split mode for blocks. * NEVER and ALWAYS are for experimenting with compression ratio. * AUTO for nearly optimal behaviour (based on heuristics). * FORWARD_COMPAT provides best forward compatibility (default). */ #ifndef BLOSC_H enum { BLOSC_ALWAYS_SPLIT = 1, BLOSC_NEVER_SPLIT = 2, BLOSC_AUTO_SPLIT = 3, BLOSC_FORWARD_COMPAT_SPLIT = 4, }; #endif // BLOSC_H /** * @brief Offsets for fields in Blosc2 chunk header. */ enum { BLOSC2_CHUNK_VERSION = 0x0, //!< the version for the chunk format BLOSC2_CHUNK_VERSIONLZ = 0x1, //!< the version for the format of internal codec BLOSC2_CHUNK_FLAGS = 0x2, //!< flags and codec info BLOSC2_CHUNK_TYPESIZE = 0x3, //!< (uint8) the number of bytes of the atomic type BLOSC2_CHUNK_NBYTES = 0x4, //!< (int32) uncompressed size of the buffer (this header is not included) BLOSC2_CHUNK_BLOCKSIZE = 0x8, //!< (int32) size of internal blocks BLOSC2_CHUNK_CBYTES = 0xc, //!< (int32) compressed size of the buffer (including this header) BLOSC2_CHUNK_FILTER_CODES = 0x10, //!< the codecs for the filter pipeline (1 byte per code) BLOSC2_CHUNK_FILTER_META = 0x18, //!< meta info for the filter pipeline (1 byte per code) BLOSC2_CHUNK_BLOSC2_FLAGS2 = 0x1e,//!< second flags byte specific for Blosc2 functionality BLOSC2_CHUNK_BLOSC2_FLAGS = 0x1F, //!< flags specific for Blosc2 functionality }; /** * @brief Run lengths for special values for chunks/frames */ enum { BLOSC2_NO_SPECIAL = 0x0, //!< no special value BLOSC2_SPECIAL_ZERO = 0x1, //!< zero special value BLOSC2_SPECIAL_NAN = 0x2, //!< NaN special value BLOSC2_SPECIAL_VALUE = 0x3, //!< repeated special value BLOSC2_SPECIAL_UNINIT = 0x4, //!< non initialized values BLOSC2_SPECIAL_LASTID = 0x4, //!< last valid ID for special value (update this adequately) BLOSC2_SPECIAL_MASK = 0x7 //!< special value mask (prev IDs cannot be larger than this) }; /** * @brief Error codes * Each time an error code is added here, its corresponding message error should be added in * print_error() */ enum { BLOSC2_ERROR_SUCCESS = 0, //=0). In case the compressor * is not recognized, or there is not support for it in this build, * it returns a -1. */ BLOSC_EXPORT int blosc1_set_compressor(const char* compname); /** * @brief Select the delta coding filter to be used. * * @param dodelta A value >0 will activate the delta filter. * If 0, it will be de-activated * * This call should always succeed. */ BLOSC_EXPORT void blosc2_set_delta(int dodelta); /** * @brief Get the compressor name associated with the compressor code. * * @param compcode The code identifying the compressor * @param compname The pointer to a string where the compressor name will be put. * * @return The compressor code. If the compressor code is not recognized, * or there is not support for it in this build, -1 is returned. */ BLOSC_EXPORT int blosc2_compcode_to_compname(int compcode, const char** compname); /** * @brief Get the compressor code associated with the compressor name. * * @param compname The string containing the compressor name. * * @return The compressor code. If the compressor name is not recognized, * or there is not support for it in this build, -1 is returned instead. */ BLOSC_EXPORT int blosc2_compname_to_compcode(const char* compname); /** * @brief Get a list of compressors supported in the current build. * * @return The comma separated string with the list of compressor names * supported. * * This function does not leak, so you should not free() the returned * list. * * This function should always succeed. */ BLOSC_EXPORT const char* blosc2_list_compressors(void); /** * @brief Get the version of Blosc in string format. * * @return The string with the current Blosc version. * Useful for dynamic libraries. */ BLOSC_EXPORT const char* blosc2_get_version_string(void); /** * @brief Get info from compression libraries included in the current build. * * @param compname The compressor name that you want info from. * @param complib The pointer to a string where the * compression library name, if available, will be put. * @param version The pointer to a string where the * compression library version, if available, will be put. * * @warning You are in charge of the @p complib and @p version strings, * you should free() them so as to avoid leaks. * * @return The code for the compression library (>=0). If it is not supported, * this function returns -1. */ BLOSC_EXPORT int blosc2_get_complib_info(const char* compname, char** complib, char** version); /** * @brief Free possible memory temporaries and thread resources. Use this * when you are not going to use Blosc for a long while. * * @return A 0 if succeeds, in case of problems releasing the resources, * it returns a negative number. */ BLOSC_EXPORT int blosc2_free_resources(void); /** * @brief Get information about a compressed buffer, namely the number of * uncompressed bytes (@p nbytes) and compressed (@p cbytes). It also * returns the @p blocksize (which is used internally for doing the * compression by blocks). * * @remark Equivalent to function #blosc2_cbuffer_sizes. * * @param cbuffer The buffer of compressed data. * @param nbytes The pointer where the number of uncompressed bytes will be put. * @param cbytes The pointer where the number of compressed bytes will be put. * @param blocksize The pointer where the block size will be put. * * You only need to pass the first BLOSC_MIN_HEADER_LENGTH bytes of a * compressed buffer for this call to work. * * This function should always succeed. */ BLOSC_EXPORT void blosc1_cbuffer_sizes(const void* cbuffer, size_t* nbytes, size_t* cbytes, size_t* blocksize); /** * @brief Get information about a compressed buffer, namely the number of * uncompressed bytes (@p nbytes) and compressed (@p cbytes). It also * returns the @p blocksize (which is used internally for doing the * compression by blocks). * * @param cbuffer The buffer of compressed data. * @param nbytes The pointer where the number of uncompressed bytes will be put. * @param cbytes The pointer where the number of compressed bytes stored in the * chunk header will be put. * @param blocksize The pointer where the block size will be put. * * @note: if any of the nbytes, cbytes or blocksize is NULL, it will not be returned. * * You only need to pass the first BLOSC_MIN_HEADER_LENGTH bytes of a * compressed buffer for this call to work. * * @note For lazy chunks returned by #blosc2_schunk_get_lazychunk, the returned * @p cbytes value is still the compressed size encoded in the chunk header, * not the full size of the lazy proxy buffer. When passing a lazy chunk to * #blosc2_decompress_ctx or #blosc2_getitem_ctx, use the size returned by * #blosc2_schunk_get_lazychunk as @p srcsize. * * @return On failure, returns negative value. */ BLOSC_EXPORT int blosc2_cbuffer_sizes(const void* cbuffer, int32_t* nbytes, int32_t* cbytes, int32_t* blocksize); /** * @brief Checks that the compressed buffer starting at @p cbuffer of length @p cbytes * may contain valid blosc compressed data, and that it is safe to call * blosc1_decompress/blosc1_getitem. * On success, returns 0 and sets @p nbytes to the size of the uncompressed data. * This does not guarantee that the decompression function won't return an error, * but does guarantee that it is safe to attempt decompression. * * @param cbuffer The buffer of compressed data. * @param cbytes The number of compressed bytes. * @param nbytes The pointer where the number of uncompressed bytes will be put. * * @return On failure, returns negative value. */ BLOSC_EXPORT int blosc1_cbuffer_validate(const void* cbuffer, size_t cbytes, size_t* nbytes); /** * @brief Get information about a compressed buffer, namely the type size * (@p typesize), as well as some internal @p flags. * * @param cbuffer The buffer of compressed data. * @param typesize The pointer where the type size will be put. * @param flags The pointer of the integer where the additional info is encoded. * The @p flags is a set of bits, where the currently used ones are: * * bit 0: whether the shuffle filter has been applied or not * * bit 1: whether the internal buffer is a pure memcpy or not * * bit 2: whether the bitshuffle filter has been applied or not * * bit 3: whether the delta coding filter has been applied or not * * You can use the @p BLOSC_DOSHUFFLE, @p BLOSC_DOBITSHUFFLE, @p BLOSC_DODELTA * and @p BLOSC_MEMCPYED symbols for extracting the interesting bits * (e.g. @p flags & @p BLOSC_DOSHUFFLE says whether the buffer is byte-shuffled * or not). * * This function should always succeed. */ BLOSC_EXPORT void blosc1_cbuffer_metainfo(const void* cbuffer, size_t* typesize, int* flags); /** * @brief Get information about a compressed buffer, namely the internal * Blosc format version (@p version) and the format for the internal * Lempel-Ziv compressor used (@p versionlz). * * @param cbuffer The buffer of compressed data. * @param version The pointer where the Blosc format version will be put. * @param versionlz The pointer where the Lempel-Ziv version will be put. * * This function should always succeed. */ BLOSC_EXPORT void blosc2_cbuffer_versions(const void* cbuffer, int* version, int* versionlz); /** * @brief Get the compressor library/format used in a compressed buffer. * * @param cbuffer The buffer of compressed data. * * @return The string identifying the compressor library/format used. * * This function should always succeed. */ BLOSC_EXPORT const char* blosc2_cbuffer_complib(const void* cbuffer); /********************************************************************* Structures and functions related with user-defined input/output. *********************************************************************/ enum { BLOSC2_IO_FILESYSTEM = 0, BLOSC2_IO_FILESYSTEM_MMAP = 1, BLOSC_IO_LAST_BLOSC_DEFINED = 2, // sentinel BLOSC_IO_LAST_REGISTERED = 32, // sentinel }; enum { BLOSC2_IO_BLOSC_DEFINED = 32, BLOSC2_IO_REGISTERED = 160, BLOSC2_IO_USER_DEFINED = 256 }; typedef void* (*blosc2_open_cb)(const char *urlpath, const char *mode, void *params); typedef int (*blosc2_close_cb)(void *stream); typedef int64_t (*blosc2_size_cb)(void *stream); typedef int64_t (*blosc2_write_cb)(const void *ptr, int64_t size, int64_t nitems, int64_t position, void *stream); typedef int64_t (*blosc2_read_cb)(void **ptr, int64_t size, int64_t nitems, int64_t position, void *stream); typedef int (*blosc2_truncate_cb)(void *stream, int64_t size); typedef int (*blosc2_destroy_cb)(void *params); /* * Input/Output callbacks. */ typedef struct { uint8_t id; //!< The IO identifier. char* name; //!< The IO name. bool is_allocation_necessary; //!< If true, the caller needs to allocate data for the read function (ptr argument). If false, the read function //!< takes care of memory allocation and stores the address in the allocated_ptr argument. blosc2_open_cb open; //!< The IO open callback. blosc2_close_cb close; //!< The IO close callback. blosc2_size_cb size; //!< The IO size callback. blosc2_write_cb write; //!< The IO write callback. blosc2_read_cb read; //!< The IO read callback. blosc2_truncate_cb truncate; //!< The IO truncate callback. blosc2_destroy_cb destroy; //!< The IO destroy callback (called in the end when finished with the schunk). } blosc2_io_cb; /* * Input/Output parameters. */ typedef struct { uint8_t id; const char *name; //!< The IO identifier. void *params; //!< The IO parameters. } blosc2_io; static const blosc2_io BLOSC2_IO_DEFAULTS = { /* .id = */ BLOSC2_IO_FILESYSTEM, /* .name = */ "filesystem", /* .params = */ NULL, }; /** * @brief Register a user-defined input/output callbacks in Blosc. * * @param io The callbacks API to register. * * @return 0 if succeeds. Else a negative code is returned. */ BLOSC_EXPORT int blosc2_register_io_cb(const blosc2_io_cb *io); /** * @brief Get a user-defined input/output callback in Blosc. * * @param id The id of the callback to get. * * @return A pointer containing the desired callback if success. Else a NULL pointer is returned. */ BLOSC_EXPORT blosc2_io_cb *blosc2_get_io_cb(uint8_t id); /********************************************************************* Structures and functions related with contexts. *********************************************************************/ typedef struct blosc2_context_s blosc2_context; /* opaque type */ typedef struct { int (*init)(void * config, blosc2_context* cctx, blosc2_context* dctx); //!< Initialize tuner. Keep in mind dctx may be NULL. This should memcpy the cctx->tuner_params. int (*next_blocksize)(blosc2_context * context); //!< Only compute the next blocksize. Only it is executed if tuner is not initialized. int (*next_cparams)(blosc2_context * context); //!< Compute the next cparams. Only is executed if tuner is initialized. int (*update)(blosc2_context * context, double ctime); //!< Update the tuner parameters. int (*free)(blosc2_context * context); //!< Free the tuner. int id; //!< The tuner id char *name; //!< The tuner name } blosc2_tuner; /** * @brief Register locally a user-defined tuner in Blosc. * * @param tuner The tuner to register. * * @return 0 if succeeds. Else a negative code is returned. */ BLOSC_EXPORT int blosc2_register_tuner(blosc2_tuner *tuner); /** * @brief The parameters for a prefilter function. * */ typedef struct { void *user_data; // user-provided info (optional) const uint8_t *input; // the input buffer uint8_t *output; // the output buffer int32_t output_size; // the output size (in bytes) int32_t output_typesize; // the output typesize int32_t output_offset; // offset to reach the start of the output buffer int64_t nchunk; // the current nchunk in associated schunk (if exists; if not -1) int32_t nblock; // the current nblock in associated chunk int32_t tid; // thread id uint8_t *ttmp; // a temporary that is able to hold several blocks for the output and is private for each thread size_t ttmp_nbytes; // the size of the temporary in bytes blosc2_context *ctx; // the compression context bool output_is_disposable; // whether the output buffer is disposable } blosc2_prefilter_params; /** * @brief The parameters for a postfilter function. * */ typedef struct { void *user_data; // user-provided info (optional) const uint8_t *input; // the input buffer uint8_t *output; // the output buffer int32_t size; // the input size (in bytes) int32_t typesize; // the input typesize int32_t offset; // offset to reach the start of the input buffer int64_t nchunk; // the current nchunk in associated schunk (if exists; if not -1) int32_t nblock; // the current nblock in associated chunk int32_t tid; // thread id uint8_t *ttmp; // a temporary that is able to hold several blocks for the output and is private for each thread size_t ttmp_nbytes; // the size of the temporary in bytes blosc2_context *ctx; // the decompression context } blosc2_postfilter_params; /** * @brief The type of the prefilter function. * * If the function call is successful, the return value should be 0; else, a negative value. */ typedef int (*blosc2_prefilter_fn)(blosc2_prefilter_params* params); /** * @brief The type of the postfilter function. * * If the function call is successful, the return value should be 0; else, a negative value. */ typedef int (*blosc2_postfilter_fn)(blosc2_postfilter_params* params); /** * @brief The parameters for creating a context for compression purposes. * * In parenthesis it is shown the default value used internally when a 0 * (zero) in the fields of the struct is passed to a function. */ typedef struct { uint8_t compcode; //!< The compressor codec. uint8_t compcode_meta; //!< The metadata for the compressor codec. uint8_t clevel; //!< The compression level (5). int use_dict; //!< Use dicts or not when compressing. Only ZSTD and LZ4/LZ4HC support this; //!< other codecs will return an error if this is set. int32_t typesize; //!< The type size (8). int16_t nthreads; //!< The number of threads to use internally (1). int32_t blocksize; //!< The requested size of the compressed blocks (0 means automatic). int32_t splitmode; //!< Whether the blocks should be split or not. void* schunk; //!< The associated schunk, if any (NULL). uint8_t filters[BLOSC2_MAX_FILTERS]; //!< The (sequence of) filters. uint8_t filters_meta[BLOSC2_MAX_FILTERS]; //!< The metadata for filters. blosc2_prefilter_fn prefilter; //!< The prefilter function. blosc2_prefilter_params *preparams; //!< The prefilter parameters. void *tuner_params; //!< Tune configuration. int tuner_id; //!< The tuner id. bool instr_codec; //!< Whether the codec is instrumented or not void *codec_params; //!< User defined parameters for the codec void *filter_params[BLOSC2_MAX_FILTERS]; //!< User defined parameters for the filters } blosc2_cparams; /** * @brief Default struct for compression params meant for user initialization. */ static const blosc2_cparams BLOSC2_CPARAMS_DEFAULTS = { BLOSC_BLOSCLZ, 0, 5, 0, 8, 1, 0, BLOSC_FORWARD_COMPAT_SPLIT, NULL, {0, 0, 0, 0, 0, BLOSC_SHUFFLE}, {0, 0, 0, 0, 0, 0}, NULL, NULL, NULL, 0, 0, NULL, {NULL, NULL, NULL, NULL, NULL, NULL} }; /** @brief The parameters for creating a context for decompression purposes. In parenthesis it is shown the default value used internally when a 0 (zero) in the fields of the struct is passed to a function. */ typedef struct { int16_t nthreads; //!< The number of threads to use internally (1). void* schunk; //!< The associated schunk, if any (NULL). blosc2_postfilter_fn postfilter; //!< The postfilter function. blosc2_postfilter_params *postparams; //!< The postfilter parameters. int32_t typesize; //!< The type size (8). } blosc2_dparams; /** * @brief Default struct for decompression params meant for user initialization. */ static const blosc2_dparams BLOSC2_DPARAMS_DEFAULTS = {1, NULL, NULL, NULL, 8}; /** * @brief Create a context for @a *_ctx() compression functions. * * @param cparams The blosc2_cparams struct with the compression parameters. * * @return A pointer to the new context. NULL is returned if this fails. * * @note This supports the same environment variables than #blosc2_compress * for overriding the programmatic compression values. * * @sa #blosc2_compress */ BLOSC_EXPORT blosc2_context* blosc2_create_cctx(blosc2_cparams cparams); /** * @brief Create a context for *_ctx() decompression functions. * * @param dparams The blosc2_dparams struct with the decompression parameters. * * @return A pointer to the new context. NULL is returned if this fails. * * @note This supports the same environment variables than #blosc2_decompress * for overriding the programmatic decompression values. * * @sa #blosc2_decompress * */ BLOSC_EXPORT blosc2_context* blosc2_create_dctx(blosc2_dparams dparams); /** * @brief Free the resources associated with a context. * * @param context The context to free. * * This function should always succeed and is valid for contexts meant for * both compression and decompression. */ BLOSC_EXPORT void blosc2_free_ctx(blosc2_context* context); /** * @brief Create a @p cparams associated to a context. * * @param ctx The context from where to extract the compression parameters. * @param cparams The pointer where the compression params will be stored. * * @return 0 if succeeds. Else a negative code is returned. */ BLOSC_EXPORT int blosc2_ctx_get_cparams(blosc2_context *ctx, blosc2_cparams *cparams); /** * @brief Create a @p dparams associated to a context. * * @param ctx The context from where to extract the decompression parameters. * @param dparams The pointer where the decompression params will be stored. * * @return 0 if succeeds. Else a negative code is returned. */ BLOSC_EXPORT int blosc2_ctx_get_dparams(blosc2_context *ctx, blosc2_dparams *dparams); /** * @brief Set a maskout so as to avoid decompressing specified blocks. * * @param ctx The decompression context to update. * * @param maskout The boolean mask for the blocks where decompression * is to be avoided. * * @remark The maskout is valid for contexts *only* meant for decompressing * a chunk via #blosc2_decompress_ctx. Once a call to #blosc2_decompress_ctx * is done, this mask is reset so that next call to #blosc2_decompress_ctx * will decompress the whole chunk. * * @param nblocks The number of blocks in maskout above. * * @return If success, a 0 is returned. An error is signaled with a negative int. * */ BLOSC_EXPORT int blosc2_set_maskout(blosc2_context *ctx, bool *maskout, int nblocks); /** * @brief Compress a block of data in the @p src buffer and returns the size of * compressed block. * * @remark Compression is memory safe and guaranteed not to write @p dest * more than what is specified in @p destsize. * There is not a minimum for @p src buffer size @p nbytes. * * @warning The @p src buffer and the @p dest buffer can not overlap. * * @param clevel The desired compression level and must be a number * between 0 (no compression) and 9 (maximum compression). * @param doshuffle Specifies whether the shuffle compression preconditioner * should be applied or not. #BLOSC_NOFILTER means not applying filters, * #BLOSC_SHUFFLE means applying shuffle at a byte level and * #BLOSC_BITSHUFFLE at a bit level (slower but *may* achieve better * compression). * @param typesize Is the number of bytes for the atomic type in binary * @p src buffer. This is mainly useful for the shuffle preconditioner. * For implementation reasons, only a 1 < typesize < 256 will allow the * shuffle filter to work. When typesize is not in this range, shuffle * will be silently disabled. * @param src The buffer containing the data to compress. * @param srcsize The number of bytes to compress in the @p src buffer. * @param dest The buffer where the compressed data will be put, * must have at least the size of @p destsize. * @param destsize The size of the dest buffer. Blosc * guarantees that if you set @p destsize to, at least, * (@p nbytes + #BLOSC2_MAX_OVERHEAD), the compression will always succeed. * * @return The number of bytes compressed. * If @p src buffer cannot be compressed into @p destsize, the return * value is zero and you should discard the contents of the @p dest * buffer. A negative return value means that either a parameter is not correct * or that an internal error happened. Set the BLOSC_TRACE environment variable * for getting more info on what is happening. If the error is not related with * wrong params, please report it back together with the buffer data causing this, * as well as the compression params used. */ /* * Environment variables * _____________________ * * *blosc2_compress()* honors different environment variables to control * internal parameters without the need of doing that programmatically. * Here are the ones supported: * * **BLOSC_CLEVEL=(INTEGER)**: This will overwrite the @p clevel parameter * before the compression process starts. * * **BLOSC_SHUFFLE=[NOSHUFFLE | SHUFFLE | BITSHUFFLE]**: This will * overwrite the *doshuffle* parameter before the compression process * starts. * * **BLOSC_DELTA=(1|0)**: This will call *blosc2_set_delta()^* before the * compression process starts. * * **BLOSC_TYPESIZE=(INTEGER)**: This will overwrite the *typesize* * parameter before the compression process starts. * * **BLOSC_COMPRESSOR=[BLOSCLZ | LZ4 | LZ4HC | ZLIB | ZSTD]**: * This will call #blosc_set_compressor before the compression process starts. * * **BLOSC_NTHREADS=(INTEGER)**: This will call * #blosc_set_nthreads before the compression process * starts. * * **BLOSC_SPLITMODE=(ALWAYS | NEVER | AUTO | FORWARD_COMPAT)**: * This will call #blosc1_set_splitmode() before the compression process starts. * * **BLOSC_BLOCKSIZE=(INTEGER)**: This will call * #blosc_set_blocksize before the compression process starts. * *NOTE:* The blocksize is a critical parameter with * important restrictions in the allowed values, so use this with care. * * **BLOSC_NOLOCK=(ANY VALUE)**: This will call #blosc2_compress_ctx under * the hood, with the *compressor*, *blocksize* and * *numinternalthreads* parameters set to the same as the last calls to * #blosc1_set_compressor, #blosc1_set_blocksize and * #blosc2_set_nthreads. *BLOSC_CLEVEL*, *BLOSC_SHUFFLE*, *BLOSC_DELTA* and * *BLOSC_TYPESIZE* environment vars will also be honored. * */ BLOSC_EXPORT int blosc2_compress(int clevel, int doshuffle, int32_t typesize, const void* src, int32_t srcsize, void* dest, int32_t destsize); /** * @brief Decompress a block of compressed data in @p src, put the result in * @p dest and returns the size of the decompressed block. * * @warning The @p src buffer and the @p dest buffer can not overlap. * * @remark Decompression is memory safe and guaranteed not to write the @p dest * buffer more than what is specified in @p destsize. * * @remark In case you want to keep under control the number of bytes read from * source, you can call #blosc1_cbuffer_sizes first to check whether the * @p nbytes (i.e. the number of bytes to be read from @p src buffer by this * function) in the compressed buffer is ok with you. * * @param src The buffer to be decompressed. * @param srcsize The size of the buffer to be decompressed. * @param dest The buffer where the decompressed data will be put. * @param destsize The size of the @p dest buffer. * * @return The number of bytes decompressed. * If an error occurs, e.g. the compressed data is corrupted or the * output buffer is not large enough, then a negative value * will be returned instead. */ /* * Environment variables * _____________________ * * *blosc1_decompress* honors different environment variables to control * internal parameters without the need of doing that programmatically. * Here are the ones supported: * * **BLOSC_NTHREADS=(INTEGER)**: This will call * *blosc_set_nthreads(BLOSC_NTHREADS)* before the proper decompression * process starts. * * **BLOSC_NOLOCK=(ANY VALUE)**: This will call *blosc2_decompress_ctx* * under the hood, with the *numinternalthreads* parameter set to the * same value as the last call to *blosc2_set_nthreads*. * */ BLOSC_EXPORT int blosc2_decompress(const void* src, int32_t srcsize, void* dest, int32_t destsize); /** * @brief Context interface to Blosc compression. This does not require a call * to #blosc2_init and can be called from multithreaded applications * without the global lock being used, so allowing Blosc be executed * simultaneously in those scenarios. * * @param context A blosc2_context struct with the different compression params. * @param src The buffer containing the data to be compressed. * @param srcsize The number of bytes to be compressed from the @p src buffer. * @param dest The buffer where the compressed data will be put. * @param destsize The size in bytes of the @p dest buffer. * * @return The number of bytes compressed. * If @p src buffer cannot be compressed into @p destsize, the return * value is zero and you should discard the contents of the @p dest * buffer. A negative return value means that an internal error happened. * It could happen that context is not meant for compression (which is stated in stderr). * Otherwise, please report it back together with the buffer data causing this * and compression settings. */ BLOSC_EXPORT int blosc2_compress_ctx( blosc2_context* context, const void* src, int32_t srcsize, void* dest, int32_t destsize); /** * @brief Context interface to Blosc compression for chunks with variable-length blocks. * * @param context A blosc2_context struct with the different compression params. * @param srcs A list of pointers, one per block. * @param srcsizes A list of uncompressed sizes, one per block. * @param nblocks The number of blocks in the chunk. * @param dest The buffer where the compressed chunk will be put. * @param destsize The size in bytes of the @p dest buffer. * * @return The number of bytes compressed, or a negative error code. */ BLOSC_EXPORT int blosc2_vlcompress_ctx( blosc2_context* context, const void* const* srcs, const int32_t* srcsizes, int32_t nblocks, void* dest, int32_t destsize); /** * @brief Context interface to Blosc decompression. This does not require a * call to #blosc2_init and can be called from multithreaded * applications without the global lock being used, so allowing Blosc * be executed simultaneously in those scenarios. * * @param context The blosc2_context struct with the different compression params. * @param src The buffer of compressed data. * @param srcsize The length of buffer of compressed data. If @p src is a lazy * chunk returned by #blosc2_schunk_get_lazychunk, pass the size returned by * #blosc2_schunk_get_lazychunk here rather than the @p cbytes value reported by * #blosc2_cbuffer_sizes. * @param dest The buffer where the decompressed data will be put. * @param destsize The size in bytes of the @p dest buffer. * * @warning The @p src buffer and the @p dest buffer can not overlap. * * @remark Decompression is memory safe and guaranteed not to write the @p dest * buffer more than what is specified in @p destsize. * * @remark In case you want to keep under control the number of bytes read from * source, you can call #blosc1_cbuffer_sizes first to check the @p nbytes * (i.e. the number of bytes to be read from @p src buffer by this function) * in the compressed buffer. * * @remark If #blosc2_set_maskout is called prior to this function, its * @p block_maskout parameter will be honored for just *one single* shot; * i.e. the maskout in context will be automatically reset to NULL, so * mask won't be used next time (unless #blosc2_set_maskout is called again). * * @return The number of bytes decompressed (i.e. the maskout blocks are not * counted). If an error occurs, e.g. the compressed data is corrupted, * @p destsize is not large enough or context is not meant for decompression, * then a negative value will be returned instead. */ BLOSC_EXPORT int blosc2_decompress_ctx(blosc2_context* context, const void* src, int32_t srcsize, void* dest, int32_t destsize); /** * @brief Context interface to Blosc decompression for chunks with variable-length blocks. * * @param context The blosc2_context struct with the different decompression params. * @param src The buffer of compressed data. * @param srcsize The length of buffer of compressed data. * @param dests On output, one newly allocated buffer per block. * @param destsizes On output, the uncompressed sizes of the blocks. * @param maxblocks The number of entries available in @p dests and @p destsizes. * * @return The number of blocks decompressed, or a negative error code. */ BLOSC_EXPORT int blosc2_vldecompress_ctx(blosc2_context* context, const void* src, int32_t srcsize, void** dests, int32_t* destsizes, int32_t maxblocks); /** * @brief Return the number of variable-length blocks stored in a VL-block chunk. * * This is a header-only query: it reads only the chunk header and does not * allocate or decompress anything. * * @param src The buffer of compressed data. Must carry the #BLOSC2_VL_BLOCKS flag. * @param srcsize The length of the compressed data buffer. * @param nblocks On success, the number of VL blocks in the chunk. * * @return 0 on success, or a negative error code. * Returns #BLOSC2_ERROR_INVALID_PARAM if the chunk does not use VL blocks. */ BLOSC_EXPORT int blosc2_vlchunk_get_nblocks(const void* src, int32_t srcsize, int32_t* nblocks); /** * @brief Decompress a single variable-length block from a VL-block chunk. * * Only the requested block is decompressed and allocated; all other blocks * in the chunk are untouched. * * @param context A decompression context (#blosc2_context created with * #blosc2_create_dctx). If @p src is a lazy chunk (obtained via * #blosc2_schunk_get_lazychunk), the caller must set @c context->schunk * to the owning super-chunk (which must have an associated frame) before * calling this function; the frame is used to read block data from disk. * @param src The buffer of compressed data. Must carry the #BLOSC2_VL_BLOCKS flag. * May be a fully in-memory chunk or a lazy chunk proxy. * @param srcsize The length of the compressed data buffer. * @param nblock Zero-based index of the block to decompress. * @param dest On success, points to a newly allocated buffer containing the * decompressed block. The caller is responsible for freeing this buffer with * @c free(). * @param destsize On success, the uncompressed byte size of the block. * * @return The uncompressed byte size of the block on success, or a negative * error code. Returns #BLOSC2_ERROR_INVALID_PARAM if the chunk does not use * VL blocks or if @p nblock is out of range. */ BLOSC_EXPORT int blosc2_vldecompress_block_ctx(blosc2_context* context, const void* src, int32_t srcsize, int32_t nblock, uint8_t** dest, int32_t* destsize); /** * @brief Create a chunk made of zeros. * * @param cparams The compression parameters. * @param nbytes The size (in bytes) of the chunk. * @param dest The buffer where the data chunk will be put. * @param destsize The size (in bytes) of the @p dest buffer; * must be BLOSC_EXTENDED_HEADER_LENGTH at least. * * @return The number of bytes compressed (BLOSC_EXTENDED_HEADER_LENGTH). * If negative, there has been an error and @p dest is unusable. * */ BLOSC_EXPORT int blosc2_chunk_zeros(blosc2_cparams cparams, int32_t nbytes, void* dest, int32_t destsize); /** * @brief Create a chunk made of nans. * * @param cparams The compression parameters; * only 4 bytes (float) and 8 bytes (double) are supported. * @param nbytes The size (in bytes) of the chunk. * @param dest The buffer where the data chunk will be put. * @param destsize The size (in bytes) of the @p dest buffer; * must be BLOSC_EXTENDED_HEADER_LENGTH at least. * * @note Whether the NaNs are floats or doubles will be given by the typesize. * * @return The number of bytes compressed (BLOSC_EXTENDED_HEADER_LENGTH). * If negative, there has been an error and @p dest is unusable. * */ BLOSC_EXPORT int blosc2_chunk_nans(blosc2_cparams cparams, int32_t nbytes, void* dest, int32_t destsize); /** * @brief Create a chunk made of repeated values. * * @param cparams The compression parameters. * @param nbytes The size (in bytes) of the chunk. * @param dest The buffer where the data chunk will be put. * @param destsize The size (in bytes) of the @p dest buffer. * @param repeatval A pointer to the repeated value (little endian). * The size of the value is given by @p cparams.typesize param. * * @return The number of bytes compressed (BLOSC_EXTENDED_HEADER_LENGTH + typesize). * If negative, there has been an error and @p dest is unusable. * */ BLOSC_EXPORT int blosc2_chunk_repeatval(blosc2_cparams cparams, int32_t nbytes, void* dest, int32_t destsize, const void* repeatval); /** * @brief Create a chunk made of uninitialized values. * * @param cparams The compression parameters. * @param nbytes The size (in bytes) of the chunk. * @param dest The buffer where the data chunk will be put. * @param destsize The size (in bytes) of the @p dest buffer; * must be BLOSC_EXTENDED_HEADER_LENGTH at least. * * @return The number of bytes compressed (BLOSC_EXTENDED_HEADER_LENGTH). * If negative, there has been an error and @p dest is unusable. * */ BLOSC_EXPORT int blosc2_chunk_uninit(blosc2_cparams cparams, int32_t nbytes, void* dest, int32_t destsize); /** * @brief Context interface counterpart for #blosc1_getitem. * * @param context Context pointer. * @param src The compressed buffer from data will be decompressed. * @param srcsize Compressed buffer length. If @p src is a lazy chunk returned by * #blosc2_schunk_get_lazychunk, pass the size returned by * #blosc2_schunk_get_lazychunk here rather than the @p cbytes value reported by * #blosc2_cbuffer_sizes. * @param start The position of the first item (of @p typesize size) from where data * will be retrieved. * @param nitems The number of items (of @p typesize size) that will be retrieved. * @param dest The buffer where the decompressed data retrieved will be put. * @param destsize Output buffer length. * * @return The number of bytes copied to @p dest or a negative value if * some error happens. */ BLOSC_EXPORT int blosc2_getitem_ctx(blosc2_context* context, const void* src, int32_t srcsize, int start, int nitems, void* dest, int32_t destsize); /********************************************************************* Super-chunk related structures and functions. *********************************************************************/ #define BLOSC2_MAX_METALAYERS 16 #define BLOSC2_METALAYER_NAME_MAXLEN 31 // Allow for a reasonable number of vl metalayers // max is 64 * 1024 due to msgpack map 16 in frame // mem usage 8 * 1024 entries for blosc2_schunk.vlmetalayers[] is 64 KB #define BLOSC2_MAX_VLMETALAYERS (8 * 1024) #define BLOSC2_VLMETALAYERS_NAME_MAXLEN BLOSC2_METALAYER_NAME_MAXLEN /** * @brief This struct is meant for holding storage parameters for a * for a blosc2 container, allowing to specify, for example, how to interpret * the contents included in the schunk. */ typedef struct { bool contiguous; //!< Whether the chunks are contiguous or sparse. char* urlpath; //!< The path for persistent storage. If NULL, that means in-memory. blosc2_cparams* cparams; //!< The compression params when creating a schunk. //!< If NULL, sensible defaults are used depending on the context. blosc2_dparams* dparams; //!< The decompression params when creating a schunk. //!< If NULL, sensible defaults are used depending on the context. blosc2_io *io; //!< Input/output backend. } blosc2_storage; /** * @brief Default struct for #blosc2_storage meant for user initialization. */ static const blosc2_storage BLOSC2_STORAGE_DEFAULTS = {false, NULL, NULL, NULL, NULL}; /** * @brief Get default struct for compression params meant for user initialization. */ BLOSC_EXPORT blosc2_cparams blosc2_get_blosc2_cparams_defaults(void); /** * @brief Get default struct for decompression params meant for user initialization. */ BLOSC_EXPORT blosc2_dparams blosc2_get_blosc2_dparams_defaults(void); /** * @brief Get default struct for #blosc2_storage meant for user initialization. */ BLOSC_EXPORT blosc2_storage blosc2_get_blosc2_storage_defaults(void); /** * @brief Get default struct for #blosc2_io meant for user initialization. */ BLOSC_EXPORT blosc2_io blosc2_get_blosc2_io_defaults(void); /** * @brief Get default struct for #blosc2_stdio_mmap meant for user initialization. */ BLOSC_EXPORT blosc2_stdio_mmap blosc2_get_blosc2_stdio_mmap_defaults(void); typedef struct blosc2_frame_s blosc2_frame; /* opaque type */ /** * @brief This struct is meant to store metadata information inside * a #blosc2_schunk, allowing to specify, for example, how to interpret * the contents included in the schunk. */ typedef struct blosc2_metalayer { char* name; //!< The metalayer identifier for Blosc client (e.g. Blosc2 NDim). uint8_t* content; //!< The serialized (msgpack preferably) content of the metalayer. int32_t content_len; //!< The length in bytes of the content. } blosc2_metalayer; /** * @brief This struct is the standard container for Blosc 2 compressed data. * * This is essentially a container for Blosc 1 chunks of compressed data, * and it allows to overcome the 32-bit limitation in Blosc 1. Optionally, * a #blosc2_frame can be attached so as to store the compressed chunks contiguously. */ typedef struct blosc2_schunk { uint8_t version; uint8_t compcode; //!< The default compressor. Each chunk can override this. uint8_t compcode_meta; //!< The default compressor metadata. Each chunk can override this. uint8_t clevel; //!< The compression level and other compress params. uint8_t splitmode; //!< The split mode. int32_t typesize; //!< The type size. int32_t blocksize; //!< The requested size of the compressed blocks (0; meaning automatic). int32_t chunksize; //!< Size of each chunk. 0 if not a fixed chunksize. uint8_t flags2; //!< Secondary chunk-format flags shared by all chunks in the schunk. 0 when empty. uint8_t use_dict; //!< Whether to use a dictionary for codec compression (1) or not (0). uint8_t filters[BLOSC2_MAX_FILTERS]; //!< The (sequence of) filters. 8-bit per filter. uint8_t filters_meta[BLOSC2_MAX_FILTERS]; //!< Metadata for filters. 8-bit per meta-slot. int64_t nchunks; //!< Number of chunks in super-chunk. int64_t current_nchunk; //!< The current chunk that is being accessed int64_t nbytes; //!< The data size (uncompressed). int64_t cbytes; //!< The data size + chunks header size (compressed). uint8_t** data; //!< Pointer to chunk data pointers buffer. size_t data_len; //!< Length of the chunk data pointers buffer. blosc2_storage* storage; //!< Pointer to storage info. blosc2_frame* frame; //!< Pointer to frame used as store for chunks. //! BLOSC2_METALAYER_NAME_MAXLEN) { BLOSC_TRACE_ERROR("Metalayers cannot be larger than %d chars.", BLOSC2_METALAYER_NAME_MAXLEN); return BLOSC2_ERROR_INVALID_PARAM; } if (schunk == NULL) { BLOSC_TRACE_ERROR("Schunk must not be NUll."); return BLOSC2_ERROR_INVALID_PARAM; } for (int nmetalayer = 0; nmetalayer < schunk->nmetalayers; nmetalayer++) { if (strcmp(name, schunk->metalayers[nmetalayer]->name) == 0) { return nmetalayer; } } return BLOSC2_ERROR_NOT_FOUND; } /** * @brief Add content into a new metalayer. * * @param schunk The super-chunk to which the metalayer should be added. * @param name The name of the metalayer. * @param content The content of the metalayer. * @param content_len The length of the content. * * @return If successful, the index of the new metalayer. Else, return a negative value. */ BLOSC_EXPORT int blosc2_meta_add(blosc2_schunk *schunk, const char *name, uint8_t *content, int32_t content_len); /** * @brief Update the content of an existing metalayer. * * @param schunk The frame containing the metalayer. * @param name The name of the metalayer to be updated. * @param content The new content of the metalayer. * @param content_len The length of the content. * * @note Contrarily to #blosc2_meta_add the updates to metalayers * are automatically serialized into a possible attached frame. * * @return If successful, the index of the metalayer. Else, return a negative value. */ BLOSC_EXPORT int blosc2_meta_update(blosc2_schunk *schunk, const char *name, uint8_t *content, int32_t content_len); /** * @brief Get the content out of a metalayer. * * @param schunk The frame containing the metalayer. * @param name The name of the metalayer. * @param content The pointer where the content will be put. * @param content_len The length of the content. * * @warning The @p **content receives a malloc'ed copy of the content. * The user is responsible of freeing it. * * @note This function is inlined so that external codec/filter plugins (like * blosc2_grok) can use it without linking against libblosc2. This avoids * pulling all of libblosc2's symbols (e.g. internal ZFP, Zstd) into the * global namespace at load time, which would otherwise shadow symbols from * other libraries that need differently-configured builds of the same * dependencies. * * @return If successful, the index of the new metalayer. Else, return a negative value. */ static inline int blosc2_meta_get(blosc2_schunk *schunk, const char *name, uint8_t **content, int32_t *content_len) { if (schunk == NULL || name == NULL || content == NULL || content_len == NULL) { BLOSC_TRACE_ERROR("Invalid parameters."); return BLOSC2_ERROR_INVALID_PARAM; } int nmetalayer = blosc2_meta_exists(schunk, name); if (nmetalayer < 0) { BLOSC_TRACE_WARNING("Metalayer \"%s\" not found.", name); return nmetalayer; } int32_t len = schunk->metalayers[nmetalayer]->content_len; if (len < 0) { BLOSC_TRACE_ERROR("Metalayer \"%s\" has corrupted content length %d.", name, len); return BLOSC2_ERROR_DATA; } *content_len = len; if (len == 0) { *content = NULL; return nmetalayer; } *content = (uint8_t*)malloc((size_t)len); if (*content == NULL) { BLOSC_TRACE_ERROR("Unable to allocate metalayer content buffer."); *content_len = 0; return BLOSC2_ERROR_MEMORY_ALLOC; } if (len > 0 && schunk->metalayers[nmetalayer]->content == NULL) { free(*content); *content = NULL; *content_len = 0; BLOSC_TRACE_ERROR("Metalayer \"%s\" has corrupted content pointer.", name); return BLOSC2_ERROR_DATA; } memcpy(*content, schunk->metalayers[nmetalayer]->content, (size_t)len); return nmetalayer; } /********************************************************************* Variable-length metalayers functions. *********************************************************************/ /** * @brief Find whether the schunk has a variable-length metalayer or not. * * @param schunk The super-chunk from which the variable-length metalayer will be checked. * @param name The name of the variable-length metalayer to be checked. * * @return If successful, return the index of the variable-length metalayer. Else, return a negative value. */ BLOSC_EXPORT int blosc2_vlmeta_exists(blosc2_schunk *schunk, const char *name); /** * @brief Add content into a new variable-length metalayer. * * @param schunk The super-chunk to which the variable-length metalayer should be added. * @param name The name of the variable-length metalayer. * @param content The content to be added. * @param content_len The length of the content. * @param cparams The parameters for compressing the variable-length metalayer content. If NULL, * the `BLOSC2_CPARAMS_DEFAULTS` will be used. * * @return If successful, the index of the new variable-length metalayer. Else, return a negative value. */ BLOSC_EXPORT int blosc2_vlmeta_add(blosc2_schunk *schunk, const char *name, uint8_t *content, int32_t content_len, blosc2_cparams *cparams); /** * @brief Update the content of an existing variable-length metalayer. * * @param schunk The super-chunk containing the variable-length metalayer. * @param name The name of the variable-length metalayer to be updated. * @param content The new content of the variable-length metalayer. * @param content_len The length of the content. * @param cparams The parameters for compressing the variable-length metalayer content. If NULL, * the `BLOSC2_CPARAMS_DEFAULTS` will be used. * * @return If successful, the index of the variable-length metalayer. Else, return a negative value. */ BLOSC_EXPORT int blosc2_vlmeta_update(blosc2_schunk *schunk, const char *name, uint8_t *content, int32_t content_len, blosc2_cparams *cparams); /** * @brief Get the content out of a variable-length metalayer. * * @param schunk The super-chunk containing the variable-length metalayer. * @param name The name of the variable-length metalayer. * @param content The pointer where the content will be put. * @param content_len The pointer where the length of the content will be put. * * @warning The @p **content receives a malloc'ed copy of the content. * The user is responsible of freeing it. * * @return If successful, the index of the new variable-length metalayer. Else, return a negative value. */ BLOSC_EXPORT int blosc2_vlmeta_get(blosc2_schunk *schunk, const char *name, uint8_t **content, int32_t *content_len); /** * @brief Delete the variable-length metalayer from the super-chunk. * * @param schunk The super-chunk containing the variable-length metalayer. * @param name The name of the variable-length metalayer. * * @return If successful, the number of the variable-length metalayers in the super-chunk. Else, return a negative value. */ BLOSC_EXPORT int blosc2_vlmeta_delete(blosc2_schunk *schunk, const char *name); /** * @brief Get a list of all the variable-length metalayer names. * * @param schunk The super-chunk containing the variable-length metalayers. * @param names The pointer to a char** to store the name pointers. This should * be of size *schunk->nvlmetalayers * sizeof(char*). * * @return The number of the variable-length metalayers in the super-chunk. * This cannot fail unless the user does not pass a @p names which is large enough to * keep pointers to all names, in which case funny things (seg faults and such) will happen. */ BLOSC_EXPORT int blosc2_vlmeta_get_names(blosc2_schunk *schunk, char **names); /********************************************************************* Time measurement utilities. *********************************************************************/ #if defined(_WIN32) /* For QueryPerformanceCounter(), etc. */ #include #elif defined(__MACH__) && defined(__APPLE__) #include #include #include #elif defined(__unix__) #if defined(__linux__) #include #else #include #endif #else #error Unable to detect platform. #endif /* The type of timestamp used on this system. */ #if defined(_WIN32) typedef LARGE_INTEGER blosc_timestamp_t; #else typedef struct timespec blosc_timestamp_t; #endif /* * @brief Set a timestamp. * * @param timestamp * */ BLOSC_EXPORT void blosc_set_timestamp(blosc_timestamp_t* timestamp); /* * @brief Get the nanoseconds between 2 timestamps. * * @param start_time * @param end_time * * @return The nanoseconds between start_time and end_time. */ BLOSC_EXPORT double blosc_elapsed_nsecs(blosc_timestamp_t start_time, blosc_timestamp_t end_time); /* * @brief Get the seconds between 2 timestamps. * * @param start_time * @param end_time * * @return The seconds between start_time and end_time. */ BLOSC_EXPORT double blosc_elapsed_secs(blosc_timestamp_t start_time, blosc_timestamp_t end_time); /********************************************************************* Low-level functions follows. Use them only if you are an expert! *********************************************************************/ /** * @brief Get the internal blocksize to be used during compression. 0 means * that an automatic blocksize is computed internally. * * @return The size in bytes of the internal block size. */ BLOSC_EXPORT int blosc1_get_blocksize(void); /** * @brief Force the use of a specific blocksize. If 0, an automatic * blocksize will be used (the default). * * @warning The blocksize is a critical parameter with important * restrictions in the allowed values, so use this with care. */ BLOSC_EXPORT void blosc1_set_blocksize(size_t blocksize); /** * @brief Set the split mode. * @param splitmode It can take the next values: * BLOSC_FORWARD_COMPAT_SPLIT * BLOSC_AUTO_SPLIT * BLOSC_NEVER_SPLIT * BLOSC_ALWAYS_SPLIT * * BLOSC_FORWARD_COMPAT offers reasonably forward compatibility, * BLOSC_AUTO_SPLIT is for nearly optimal results (based on heuristics), * BLOSC_NEVER_SPLIT and BLOSC_ALWAYS_SPLIT are for the user experimenting * when trying to get best compression ratios and/or speed. * * If not called, the default mode is BLOSC_FORWARD_COMPAT_SPLIT. * * This function should always succeed. */ BLOSC_EXPORT void blosc1_set_splitmode(int splitmode); /** * @brief Get the offsets of a frame in a super-chunk. * * @param schunk The super-chunk containing the frame. * * @return If successful, return a pointer to a buffer of the decompressed offsets. * The number of offsets is equal to schunk->nchunks; the user is * responsible to free this buffer. Else, return a NULL value. */ BLOSC_EXPORT int64_t* blosc2_frame_get_offsets(blosc2_schunk *schunk); /********************************************************************* Structures and functions related with compression codecs. *********************************************************************/ typedef int (* blosc2_codec_encoder_cb) (const uint8_t *input, int32_t input_len, uint8_t *output, int32_t output_len, uint8_t meta, blosc2_cparams *cparams, const void* chunk); typedef int (* blosc2_codec_decoder_cb) (const uint8_t *input, int32_t input_len, uint8_t *output, int32_t output_len, uint8_t meta, blosc2_dparams *dparams, const void* chunk); typedef struct { uint8_t compcode; //!< The codec identifier. char *compname; //!< The codec name. uint8_t complib; //!< The codec library format. uint8_t version; //!< The codec version. blosc2_codec_encoder_cb encoder; //!< The codec encoder that is used during compression. blosc2_codec_decoder_cb decoder; //!< The codec decoder that is used during decompression. // int (*free)(void* codec_params); // //!< Free the codec_params stored in blosc2_context. } blosc2_codec; /** * @brief Register locally a user-defined codec in Blosc. * * @param codec The codec to register. * * @return 0 if succeeds. Else a negative code is returned. */ BLOSC_EXPORT int blosc2_register_codec(blosc2_codec *codec); /********************************************************************* Structures and functions related with filters plugins. *********************************************************************/ typedef int (* blosc2_filter_forward_cb) (const uint8_t *, uint8_t *, int32_t, uint8_t, blosc2_cparams *, uint8_t); typedef int (* blosc2_filter_backward_cb) (const uint8_t *, uint8_t *, int32_t, uint8_t, blosc2_dparams *, uint8_t); /** * @brief The parameters for a user-defined filter. */ typedef struct { uint8_t id; //!< The filter identifier. char * name; //!< The filter name. uint8_t version; //!< The filter version. blosc2_filter_forward_cb forward; //!< The filter function that is used during compression. blosc2_filter_backward_cb backward; //!< The filter function that is used during decompression. } blosc2_filter; /** * @brief Register locally a user-defined filter in Blosc. * * @param filter The filter to register. * * @return 0 if succeeds. Else a negative code is returned. */ BLOSC_EXPORT int blosc2_register_filter(blosc2_filter *filter); /********************************************************************* Directory utilities. *********************************************************************/ /* * @brief Remove a directory and its files. * * @param path The directory to remove. * * @return 0 if succeeds. Else a negative code is returned. */ BLOSC_EXPORT int blosc2_remove_dir(const char *path); /* * @brief Remove a file or a directory given by path. * * @param path The file or directory to remove. * * @return 0 if succeeds. Else a negative code is returned. */ BLOSC_EXPORT int blosc2_remove_urlpath(const char *path); /* * @brief Rename a file or a directory given by old_urlpath to new_path. * * @param old_urlpath The original path to the directory or file. * @param new_path The new path to the directory or file. * * @return 0 if succeeds. Else a negative code is returned. */ BLOSC_EXPORT int blosc2_rename_urlpath(char* old_urlpath, char* new_path); /********************************************************************* Index utilities. *********************************************************************/ /* * @brief Convert a sequential index into a multidimensional index * * This function assume ndim <= B2ND_MAX_DIM. */ BLOSC_EXPORT void blosc2_unidim_to_multidim(uint8_t ndim, int64_t *shape, int64_t i, int64_t *index); /* * @brief Convert a multidimensional index into a sequential index */ BLOSC_EXPORT void blosc2_multidim_to_unidim(const int64_t *index, int8_t ndim, const int64_t *strides, int64_t *i); /* * @brief Get the unidimensional chunk indexes needed to get a slice of a schunk or a b2nd array * * @param schunk The super-chunk (of b2nd array or not). * @param start Index (0-based if it is a schunk) where the slice begins. * @param stop The first index (0-based if it is a schunk) that is not in the selected slice. * @param chunks_idx The pointer to the buffer where the indexes will be written. It is the user responsibility * to free the buffer. * * @return The number of chunks needed to get the slice. If some problem is * detected, a negative code is returned instead. */ BLOSC_EXPORT int64_t blosc2_get_slice_nchunks(blosc2_schunk* schunk, int64_t *start, int64_t *stop, int64_t **chunks_idx); /********************************************************************* Raw shuffle functions. *********************************************************************/ /** * @brief Applies the shuffle operation to a block of data in @p src, and puts the result in @p dest. * * @warning The @p src buffer and the @p dest buffer can not overlap. * * @param typesize Is the number of bytes for the atomic type in binary @p src buffer. Only 1 < typesize * < 256 is allowed. * @param blocksize The size of the block. * @param src The source buffer to be shuffled. * @param dest The destination buffer where the shuffled data will be written. * * @return @p blocksize on success or a negative value if some error happens (mainly an invalid parameter). */ BLOSC_EXPORT int32_t blosc2_shuffle(const int32_t typesize, const int32_t blocksize, const void* src, void* dest); /** * @brief Applies the inverse shuffle operation to a block of data in @p src, and puts the result in @p dest. * * @warning The @p src buffer and the @p dest buffer can not overlap. * * @param typesize Is the number of bytes for the atomic type in binary @p src buffer. Only 1 < typesize * < 256 is allowed. * @param blocksize The size of the block. * @param src The source buffer to be unshuffled. * @param dest The destination buffer where the unshuffled data will be written. * * @return @p blocksize on success or a negative value if some error happens (mainly an invalid parameter). */ BLOSC_EXPORT int32_t blosc2_unshuffle(const int32_t typesize, const int32_t blocksize, const void* src, void* dest); /** * @brief Applies the bitshuffle operation to a block of data in @p src, and puts the result in @p dest. * * @warning The @p src buffer and the @p dest buffer can not overlap. * * @remark The function will shuffle the maximum amount of elements that can be divided by 8, and copy the * rest to the destination buffer unchanged. There are @p blocksize / @p typesize elements. * * @param typesize Is the number of bytes for the atomic type in binary @p src buffer. Only 1 < typesize * < 256 is allowed. * @param blocksize The size of the block. * @param src The source buffer to be shuffled. * @param dest The destination buffer where the shuffled data will be written. * * @return @p blocksize on success or a negative value if some error happens (mainly an invalid parameter). */ BLOSC_EXPORT int32_t blosc2_bitshuffle(const int32_t typesize, const int32_t blocksize, const void* src, void* dest); /** * @brief Applies the inverse bitshuffle operation to a block of data in @p src, and puts the result in @p dest. * * @warning The @p src buffer and the @p dest buffer can not overlap. * * @remark The function will shuffle the maximum amount of elements that can be divided by 8, and copy the * rest to the destination buffer unchanged. There are @p blocksize / @p typesize elements. * * @param typesize Is the number of bytes for the atomic type in binary @p src buffer. Only 1 < typesize * < 256 is allowed. * @param blocksize The size of the block. * @param src The source buffer to be unshuffled. * @param dest The destination buffer where the unshuffled data will be written. * * @return @p blocksize on success or a negative value if some error happens (mainly an invalid parameter). */ BLOSC_EXPORT int32_t blosc2_bitunshuffle(const int32_t typesize, const int32_t blocksize, const void* src, void* dest); /********************************************************************* Private functions, these are here for convenience, and are not meant to be included in public docs *********************************************************************/ // Private function needed in b2nd.h for deserializing meta static inline void swap_store(void *dest, const void *pa, int size) { uint8_t *pa_ = (uint8_t *) pa; uint8_t *pa2_ = (uint8_t*)malloc((size_t) size); int i = 1; /* for big/little endian detection */ char *p = (char *) &i; if (p[0] == 1) { /* little endian */ switch (size) { case 8: pa2_[0] = pa_[7]; pa2_[1] = pa_[6]; pa2_[2] = pa_[5]; pa2_[3] = pa_[4]; pa2_[4] = pa_[3]; pa2_[5] = pa_[2]; pa2_[6] = pa_[1]; pa2_[7] = pa_[0]; break; case 4: pa2_[0] = pa_[3]; pa2_[1] = pa_[2]; pa2_[2] = pa_[1]; pa2_[3] = pa_[0]; break; case 2: pa2_[0] = pa_[1]; pa2_[1] = pa_[0]; break; case 1: pa2_[0] = pa_[0]; break; default: fprintf(stderr, "Unhandled nitems: %d\n", size); } } else { /* big endian: native byte order is already big-endian, just copy */ memcpy(pa2_, pa_, size); } memcpy(dest, pa2_, size); free(pa2_); } #ifdef __cplusplus } #endif #endif /* BLOSC_BLOSC2_H */ c-blosc2-3.1.5/include/blosc2/000077500000000000000000000000001521743436100157415ustar00rootroot00000000000000c-blosc2-3.1.5/include/blosc2/blosc2-common.h000066400000000000000000000051171521743436100205700ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #ifndef BLOSC_BLOSC2_BLOSC2_COMMON_H #define BLOSC_BLOSC2_BLOSC2_COMMON_H #include "blosc2-export.h" #include #include // For shutting up stupid compiler warning about some 'unused' variables in GCC #ifdef __GNUC__ #define BLOSC_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) #define BLOSC_UNUSED_VAR __attribute__ ((unused)) #else #define BLOSC_UNUSED_VAR #endif // __GNUC__ // For shutting up compiler warning about unused parameters #define BLOSC_UNUSED_PARAM(x) ((void)(x)) /* Use inlined functions for supported systems */ #if defined(_MSC_VER) && !defined(__cplusplus) /* Visual Studio */ #define inline __inline /* Visual C is not C99, but supports some kind of inline */ #endif /* Define the __SSE2__ symbol if compiling with Visual C++ and targeting the minimum architecture level supporting SSE2. Other compilers define this as expected and emit warnings when it is re-defined. */ #if !defined(__SSE2__) && defined(_MSC_VER) && \ (defined(_M_X64) || (defined(_M_IX86) && _M_IX86_FP >= 2)) #define __SSE2__ #endif /* * Detect if the architecture is fine with unaligned access. */ #if !defined(BLOSC_STRICT_ALIGN) #define BLOSC_STRICT_ALIGN #if defined(__i386__) || defined(__386) || defined (__amd64) /* GNU C, Sun Studio */ #undef BLOSC_STRICT_ALIGN #elif defined(__i486__) || defined(__i586__) || defined(__i686__) /* GNU C */ #undef BLOSC_STRICT_ALIGN #elif defined(_M_IX86) || defined(_M_X64) /* Intel, MSVC */ #undef BLOSC_STRICT_ALIGN #elif defined(__386) #undef BLOSC_STRICT_ALIGN #elif defined(_X86_) /* MinGW */ #undef BLOSC_STRICT_ALIGN #elif defined(__I86__) /* Digital Mars */ #undef BLOSC_STRICT_ALIGN /* Modern ARM systems (like ARM64) should support unaligned access quite efficiently. */ #elif defined(__ARM_FEATURE_UNALIGNED) && defined(__ARM64_ARCH_8__) #undef BLOSC_STRICT_ALIGN #elif defined(_ARCH_PPC) || defined(__PPC__) /* Modern PowerPC systems (like POWER8) should support unaligned access quite efficiently. */ #undef BLOSC_STRICT_ALIGN #endif #endif #if defined(__SSE2__) #include #endif #if defined(__AVX2__) || defined(__AVX512F__) || defined (__AVX512BW__) #include #endif #endif /* BLOSC_BLOSC2_BLOSC2_COMMON_H */ c-blosc2-3.1.5/include/blosc2/blosc2-export.h000066400000000000000000000034171521743436100206220ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #ifndef BLOSC_BLOSC2_BLOSC2_EXPORT_H #define BLOSC_BLOSC2_BLOSC2_EXPORT_H /* Macros for specifying exported symbols. BLOSC_EXPORT is used to decorate symbols that should be exported by the blosc shared library. BLOSC_NO_EXPORT is used to decorate symbols that should NOT be exported by the blosc shared library. */ #if defined(BLOSC_SHARED_LIBRARY) #if defined(_MSC_VER) #define BLOSC_EXPORT __declspec(dllexport) #elif (defined(__GNUC__) && __GNUC__ >= 4) || defined(__clang__) #if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) #define BLOSC_EXPORT __attribute__((dllexport)) #else #define BLOSC_EXPORT __attribute__((visibility("default"))) #endif /* defined(_WIN32) || defined(__CYGWIN__) */ #else #error Cannot determine how to define BLOSC_EXPORT for this compiler. #endif #else #define BLOSC_EXPORT #endif /* defined(BLOSC_SHARED_LIBRARY) */ #if (defined(__GNUC__) || defined(__clang__)) && !defined(__MINGW32__) #define BLOSC_NO_EXPORT __attribute__((visibility("hidden"))) #else #define BLOSC_NO_EXPORT #endif /* (defined(__GNUC__) || defined(__clang__)) && !defined(__MINGW32__) */ /* When testing, export everything to make it easier to implement tests. */ #if defined(BLOSC_TESTING) #undef BLOSC_NO_EXPORT #define BLOSC_NO_EXPORT BLOSC_EXPORT #endif /* defined(BLOSC_TESTING) */ #endif /* BLOSC_BLOSC2_BLOSC2_EXPORT_H */ c-blosc2-3.1.5/include/blosc2/blosc2-stdio.h000066400000000000000000000106701521743436100204220ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #ifndef BLOSC_BLOSC2_BLOSC2_STDIO_H #define BLOSC_BLOSC2_BLOSC2_STDIO_H #include "blosc2-export.h" #if defined(_MSC_VER) #include #else #include #endif #include #include #include #include #if defined(_WIN32) #include #endif #ifdef __cplusplus extern "C" { #endif typedef struct { FILE *file; } blosc2_stdio_file; BLOSC_EXPORT void *blosc2_stdio_open(const char *urlpath, const char *mode, void* params); BLOSC_EXPORT int blosc2_stdio_close(void *stream); BLOSC_EXPORT int64_t blosc2_stdio_size(void *stream); BLOSC_EXPORT int64_t blosc2_stdio_write(const void *ptr, int64_t size, int64_t nitems, int64_t position, void *stream); BLOSC_EXPORT int64_t blosc2_stdio_read(void **ptr, int64_t size, int64_t nitems, int64_t position, void *stream); BLOSC_EXPORT int blosc2_stdio_truncate(void *stream, int64_t size); BLOSC_EXPORT int blosc2_stdio_destroy(void* params); /** * @brief Parameters for memory-mapped I/O. You can use the blosc2_schunk_open*_udio functions to memory-map existing * schunk files from disk. To create a new schunk which is backed up by a memory-mapped file on disk, set the io member * of the #blosc2_storage struct (see test_mmap for examples). Please note that memory-mapped I/O is only available for * cframes and not sframes. */ typedef struct { /* Arguments of the mapping */ const char* mode; //!< The opening mode of the memory-mapped file (r, r+, w+ or c) similar to Numpy's np.memmap //!< (https://numpy.org/doc/stable/reference/generated/numpy.memmap.html). Set to r if the file should only be read, //!< r+ if you want to extend data to an existing file, w+ to create a new file and c to use an existing file as basis //!< but keep all modifications in-memory. On Windows, the size of the mapping cannot change in the c mode. size_t initial_mapping_size; //!< The initial size of the memory mapping used as a large enough write buffer for the r+, w+ and c modes (for //!< Windows, only the r+ and w+ modes). On Windows, this will also be the size of the file while the file is opened. //!< It will be truncated to the target size when the file is closed (e.g., when the schunk is destroyed). bool needs_free; //!< Indicates whether this object should be freed in the blosc2_destroy_cb callback (set to true if the //!< blosc2_stdio_mmap struct was created on the heap). /* Internal attributes of the mapping */ char* addr; //!< The starting address of the mapping. char* urlpath; //!< The path to the file which is associated with this object. size_t file_size; //!< The size of the file. size_t mapping_size; //!< The size of the mapping (mapping_size >= file_size). bool is_memory_only; //!< Whether the mapping is only in-memory and changes are not reflected to the file on disk (c mode). FILE* file; //!< The underlying file handle. int fd; //!< The underlying file descriptor. int64_t access_flags; //!< The access attributes for the memory pages. int64_t map_flags; //!< The attributes of the mapping. #if defined(_WIN32) HANDLE mmap_handle; //!< The Windows handle to the memory mapping. #endif } blosc2_stdio_mmap; /** * @brief Default struct for memory-mapped I/O for user initialization. */ static const blosc2_stdio_mmap BLOSC2_STDIO_MMAP_DEFAULTS = { "r", ((size_t)1 << 30), false, NULL, NULL, 0, 0, false, NULL, -1, -1, -1 #if defined(_WIN32) , INVALID_HANDLE_VALUE #endif }; BLOSC_EXPORT void *blosc2_stdio_mmap_open(const char *urlpath, const char *mode, void* params); BLOSC_EXPORT int blosc2_stdio_mmap_close(void *stream); BLOSC_EXPORT int64_t blosc2_stdio_mmap_size(void *stream); BLOSC_EXPORT int64_t blosc2_stdio_mmap_write( const void *ptr, int64_t size, int64_t nitems, int64_t position, void *stream); BLOSC_EXPORT int64_t blosc2_stdio_mmap_read(void **ptr, int64_t size, int64_t nitems, int64_t position, void *stream); BLOSC_EXPORT int blosc2_stdio_mmap_truncate(void *stream, int64_t size); BLOSC_EXPORT int blosc2_stdio_mmap_destroy(void* params); #ifdef __cplusplus } #endif #endif /* BLOSC_BLOSC2_BLOSC2_STDIO_H */ c-blosc2-3.1.5/include/blosc2/codecs-registry.h000066400000000000000000000042671521743436100212310ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #ifndef BLOSC_BLOSC2_CODECS_REGISTRY_H #define BLOSC_BLOSC2_CODECS_REGISTRY_H #ifdef __cplusplus extern "C" { #endif enum { BLOSC_CODEC_NDLZ = 32, //!< Simple Lempel-Ziv compressor for NDim data. Experimental, mainly for teaching purposes. BLOSC_CODEC_ZFP_FIXED_ACCURACY = 33, //!< ZFP compressor for fixed accuracy mode. The desired accuracy is set in `compcode_meta`. //!< See https://github.com/Blosc/c-blosc2/blob/main/plugins/codecs/zfp/README.md BLOSC_CODEC_ZFP_FIXED_PRECISION = 34, //!< ZFP compressor for fixed precision. The desired precision is set in `compcode_meta`. //!< See https://github.com/Blosc/c-blosc2/blob/main/plugins/codecs/zfp/README.md BLOSC_CODEC_ZFP_FIXED_RATE = 35, //!< ZFP compressor for fixed precision. The desired rate is set in `compcode_meta`. //!< See https://github.com/Blosc/c-blosc2/blob/main/plugins/codecs/zfp/README.md BLOSC_CODEC_OPENHTJ2K = 36, //!< OpenHTJ2K compressor for JPEG 2000 HT. //!< See https://github.com/Blosc/blosc2_openhtj2k BLOSC_CODEC_GROK = 37, //!< Grok compressor for JPEG 2000. //!< See https://github.com/Blosc/blosc2_grok BLOSC_CODEC_OPENZL = 38, //!< OpenZL metacompressor. //!< See https://github.com/Blosc/blosc2_openzl BLOSC_CODEC_J2K = 39, //!< JPEG 2000 compressor plugin. //!< See https://github.com/Blosc/blosc2_j2k BLOSC_CODEC_HTJ2K = 40, //!< High Throughput JPEG 2000 compressor plugin. //!< See https://github.com/Blosc/blosc2_htj2k }; void register_codecs(void); // For dynamically loaded codecs typedef struct { char *encoder; char *decoder; } codec_info; // If ever add .free func for codecs, may be needed // typedef struct { // char *free; // } codecparams_info; #ifdef __cplusplus } #endif #endif /* BLOSC_BLOSC2_CODECS_REGISTRY_H */ c-blosc2-3.1.5/include/blosc2/filters-registry.h000066400000000000000000000033701521743436100214330ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #ifndef BLOSC_BLOSC2_FILTERS_REGISTRY_H #define BLOSC_BLOSC2_FILTERS_REGISTRY_H #ifdef __cplusplus extern "C" { #endif enum { BLOSC_FILTER_NDCELL = 32, //!< Simple filter for grouping NDim cell data together. //!< See https://github.com/Blosc/c-blosc2/blob/main/plugins/filters/ndcell/README.md BLOSC_FILTER_NDMEAN = 33, //!< Simple filter for replacing content of a NDim cell with its mean value. //!< See https://github.com/Blosc/c-blosc2/blob/main/plugins/filters/ndmean/README.md BLOSC_FILTER_BYTEDELTA_BUGGY = 34, // buggy version. See #524 BLOSC_FILTER_BYTEDELTA = 35, //!< Byte-wise delta. Assumes M streams of bytes of length N, where M is the typesize (specified by `filters_meta`). //!< Should be used in combination with @ref BLOSC_SHUFFLE or @ref BLOSC_BITSHUFFLE. //!< See https://www.blosc.org/posts/bytedelta-enhance-compression-toolset/ BLOSC_FILTER_INT_TRUNC = 36, //!< Truncate int precision; positive values in `filters_meta` slot will keep bits; //!< negative values will remove (set to zero) bits. //!< This is similar to @ref BLOSC_TRUNC_PREC, but for integers instead of floating point data. }; void register_filters(void); // For dynamically loaded filters typedef struct { char *forward; char *backward; } filter_info; #ifdef __cplusplus } #endif #endif /* BLOSC_BLOSC2_FILTERS_REGISTRY_H */ c-blosc2-3.1.5/include/blosc2/tuners-registry.h000066400000000000000000000014451521743436100213040ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (C) 2021 The Blosc Developers https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #ifndef BLOSC_BLOSC2_TUNERS_REGISTRY_H #define BLOSC_BLOSC2_TUNERS_REGISTRY_H #ifdef __cplusplus extern "C" { #endif enum { BLOSC_BTUNE = 32, }; void register_tuners(void); // For dynamically loaded tuners typedef struct { char *init; char *next_blocksize; char *next_cparams; char *update; char *free; } tuner_info; #ifdef __cplusplus } #endif #endif /* BLOSC_BLOSC2_TUNERS_REGISTRY_H */ c-blosc2-3.1.5/plans/000077500000000000000000000000001521743436100142475ustar00rootroot00000000000000c-blosc2-3.1.5/plans/chunk-vlblocks.md000066400000000000000000000262371521743436100175300ustar00rootroot00000000000000# Variable-Length Blocks In Chunks ## Goal Add support for chunks whose blocks have variable uncompressed lengths, while keeping the implementation as local as possible: - new chunk-format support, not a large refactor - prefer targeted `if`/`else` handling inside current compression/decompression paths - add only the new public APIs that are strictly necessary ## Agreed Format Decisions ### Frame-level signal - Use bit 7 in `general_flags` in `README_CFRAME_FORMAT.rst` to mean: - all chunks in the frame use variable-length blocks - Mixed populations inside one frame/schunk will be forbidden: - either all chunks use regular fixed-size blocks - or all chunks use variable-length blocks ### Chunk-level signal - Reuse the currently reserved byte in the extended chunk header. - Rename/document that byte as `blosc2_flags2`. - Use bit 0 in `blosc2_flags2` to mean: - this chunk uses variable-length blocks - Current C-Blosc2 writers already zero this byte, so old chunks produced by current code should carry `0` there. ### Keep `blocksize` positive - Do not use `blocksize == 0` as a sentinel. - Keep `blocksize` as a positive size in the chunk header. - For VL-block chunks, `blocksize` in the header will store `nblocks`. - The runtime context can still keep the maximum block size separately for temp-buffer sizing. ### Stream layout - For VL-block chunks, force one compressed stream per block. - In practice, VL-block chunks should behave as if split mode is disabled for the chunk. - This keeps the interpretation of the per-stream `csize` field simple: - `csize` stores the uncompressed size of the block - compressed size is derived from `bstarts` - This also avoids a wider redesign of the current multi-stream-per-block format. ## Compatibility Rules - Existing chunk format remains unchanged for regular chunks. - Old decoders will not understand VL-block chunks; this is acceptable for the new format. - Regular chunks and VL-block chunks must not be mixed in the same schunk/frame. - Standalone chunks must remain self-describing, so the chunk-level `blosc2_flags2` bit is required even if the frame-level bit exists. ## Public API ### Compression Add a new public function in `include/blosc2.h`: ```c int blosc2_vlcompress_ctx(blosc2_context* context, const void* const* srcs, const int32_t* srcsizes, int32_t nblocks, void* dest, int32_t destsize); ``` Intent: - each `srcs[i]` points to one contiguous uncompressed block buffer - each `srcsizes[i]` is the uncompressed size for that block - the function builds one chunk containing all those blocks Notes: - model the function after `blosc2_compress_ctx` - preserve multithreading by assigning one or more blocks per worker, as today - do not try to generalize the current API for all cases; keep the new API separate ### Decompression Add a new public function in `include/blosc2.h`: ```c int blosc2_vldecompress_ctx(blosc2_context* context, const void* src, int32_t srcsize, void** dests, int32_t* destsizes, int32_t maxblocks); ``` Intent: - decompress one VL-block chunk into a list of per-block output buffers - fill `destsizes[i]` with the uncompressed size of each block - return the number of blocks on success Notes: - the function allocates the output buffers - `maxblocks` protects the API from overrunning caller arrays - decompression can still use the current threaded block loop ## Minimal-Change Implementation Strategy The implementation should mostly extend current code paths instead of introducing many new helpers. ### 1. Header and constants - Add a named constant for `blosc2_flags2` offset in the extended chunk header. - Add a named bit constant for `BLOSC2_VL_BLOCKS`. - Rename the reserved-byte wording in the docs and internal comments to `blosc2_flags2`. - Update the internal `blosc_header` struct field name from `reserved2` to `blosc2_flags2`. ### 2. Chunk header read/write - In `read_chunk_header()`, read `blosc2_flags2` and keep it available in the local header struct. - In `blosc2_initialize_header_from_context()`, explicitly populate `blosc2_flags2` from the context. - For the special chunk constructors (`zeros`, `uninit`, `nans`, `repeatval`), keep `blosc2_flags2 == 0`. ### 3. Context state - Add the minimum extra state needed in `blosc2_context`: - a flag for `vlblocks` - a pointer to per-block uncompressed sizes for compression - a pointer to per-block uncompressed sizes for decompression output - Avoid broad context redesign. ### 4. Compression path - Implement `blosc2_vlcompress_ctx()` as a sibling of `blosc2_compress_ctx()`. - Reuse `initialize_context_compression()` as much as possible, but set: - `context->blosc2_flags2 |= BLOSC2_VL_BLOCKS` - `dont_split = 1` - `context->nblocks` from the caller-provided block count - `context->sourcesize` as the sum of all block sizes - header `blocksize` as `nblocks` - runtime `context->blocksize` as the maximum block size - In the block compression loop, branch on `vlblocks`: - use the caller-provided block size for the current block - write `bstarts` as today - write stream `csize` as the uncompressed block size, not compressed bytes - Keep current block scheduling and worker model. ### 5. Decompression path - In `initialize_context_decompression()`, detect `BLOSC2_VL_BLOCKS` from `blosc2_flags2`. - Keep current setup for regular chunks unchanged. - For VL-block chunks: - still use `bstarts` - derive compressed block size from adjacent `bstarts` values and the chunk end - read the stored per-block uncompressed size from the stream `csize` - In `blosc_d()`, branch on `vlblocks` before the current stream parsing logic diverges too much. - Use one block -> one stream semantics for VL-block chunks. ### 6. Lazy chunks and frame integration - Extend lazy-chunk trailer generation/reading so VL-block chunks still expose enough information to load one block lazily. - Keep changes narrow: - reuse current trailer structure where possible - only add conditional handling when `BLOSC2_VL_BLOCKS` is set - In frames: - set `general_flags` bit 7 when writing VL-block schunks - reject append/insert/update operations that try to mix regular and VL-block chunks - persist the homogeneous-mode invariant across reopen ### 7. Validation - Add header validation for VL-block chunks: - `blosc2_flags2` bit 0 implies extended header - `blocksize > 0` - for VL-block chunks, `blocksize` is interpreted as `nblocks` - one stream per block - `bstarts` monotonic - decoded compressed span stays inside the chunk - stored per-block uncompressed sizes are positive and sum to `nbytes` ## Documentation Changes ### `README_CHUNK_FORMAT.rst` - Rename the reserved byte to `blosc2_flags2`. - Document bit 0 as: - chunk uses variable-length blocks - Document the VL-block variant of the blocks section: - one stream per block - `csize` stores uncompressed block size - compressed size comes from `bstarts` - Clarify that regular chunks keep the current meaning of `csize`. ### `README_CFRAME_FORMAT.rst` - Use `general_flags` bit 7 for frames containing VL-block chunks. - State explicitly that mixing regular chunks and VL-block chunks in the same frame is not supported. ## Tests Add focused tests rather than a large matrix. ### Chunk tests - roundtrip for one VL-block chunk with different block sizes - roundtrip for multithreaded compression/decompression - reopen/decompress through schunk/frame - invalid header tests: - missing chunk flag - inconsistent `bstarts` - inconsistent block-size sum vs `nbytes` ### Frame/schunk tests - set/clear frame `general_flags` bit 7 correctly - reject mixed regular/VL-block append/insert/update - verify reopen preserves VL-block mode ## Examples Add one or two small examples in `examples/`: - build a VL-block chunk from several strings or byte buffers - decompress it back into per-block buffers and sizes - optionally, store/reopen via schunk/frame to show the frame flag behavior ## Suggested Implementation Order 1. Add constants, struct-field rename to `blosc2_flags2`, and docs for the new header byte. 2. Add context flag/state for VL blocks. 3. Implement `blosc2_vlcompress_ctx()` with one-stream-per-block semantics. 4. Implement `blosc2_vldecompress_ctx()` and the VL branch in the existing decompression path. 5. Integrate frame `general_flags` bit 7 and enforce non-mixing in schunks/frames. 6. Update docs. 7. Add tests. 8. Add examples. ## Current Implementation Status The first implementation is in place. Implemented: - chunk and frame format versions were bumped - the extended chunk-header reserved byte was renamed to `blosc2_flags2` - `BLOSC2_VL_BLOCKS` was added in `blosc2_flags2` bit 0 - frame `general_flags` bit 7 is now used for homogeneous VL-block frames - `blosc2_vlcompress_ctx()` was added - `blosc2_vldecompress_ctx()` was added - VL-block chunks use one stream per block - for VL-block chunks, header `blocksize` stores `nblocks` - runtime decompression derives compressed block sizes from `bstarts` - schunks/frames reject mixing regular chunks and VL-block chunks - reopen/copy/frame-buffer paths preserve VL-block mode - docs were updated in `README_CHUNK_FORMAT.rst` and `README_CFRAME_FORMAT.rst` - tests were added in `tests/test_vlblocks.c` - an example was added in `examples/vlblocks.c` Current limitations: - lazy VL-block chunks are still rejected - `blosc2_getitem_ctx()` currently rejects VL-block chunks - there is not yet a broader invalid-header/fuzz-style test matrix for malformed VL-block chunks ## Possible Future Work - benchmark VL-block compression and decompression against regular chunks to understand: - header/trailer overhead - impact of forcing one stream per block - multithreading efficiency for many small blocks vs fewer large blocks - add targeted malformed-input tests: - non-monotonic `bstarts` - wrong final compressed span - wrong sum of stored block sizes - illegal combinations with special chunks or memcpyed chunks - test Zstd dictionary support with VL-block chunks: - verify `blosc2_vlcompress_ctx()` works correctly when dictionary support is enabled - verify decompression reuses the embedded dictionary correctly - measure whether dictionary training/use is still effective with one-stream-per-block VL chunks - evaluate lazy-chunk support for VL-block chunks - evaluate `getitem` support for VL-block chunks - measure whether storing per-block outputs via freshly allocated buffers is the right public API long term - add a small benchmark/example that uses larger and more realistic heterogeneous payloads - review whether frame metadata should expose the homogeneous VL-block mode more directly in higher-level APIs ## Main Risk Areas - lazy chunk support, because block compressed size is no longer read directly from stream `csize` - `getitem` behavior for VL-block chunks - ensuring the minimal-change branching does not accidentally alter regular chunk behavior To keep risk low, regular chunks should remain on the current path with as little code movement as possible. c-blosc2-3.1.5/plans/shared-thread-pool-architecture.png000066400000000000000000001647221521743436100231330ustar00rootroot00000000000000PNG  IHDR䘙 pHYsodtEXtSoftwarewww.inkscape.org<_IDATx읅W w&$hܡƽqwwk] #:3f2u/XUtz{iΩS[[F6B!B!CB!B(B!B(B!B(B!B(B!B(B!B(B!B(B!B(B!B(B!B(B!B(B!B(B!B(B!B(B!B(B!B(B!B(B!B(B!B(B!B(B!B(B!B(B!B(B!B!$B!B!$B!B!$v!\86og,6/F֢Q>P< ;Û94q+UT8͔Rշs&y'Jx5uia`=KVj*}E+SU@i y#]kWOcWf&V\ ޳of(ԁCfYn [ZzV_P !_ uËd~xVYo(nr[eNn~o~sà?ȖoL#[xZmbsn8iݮtHޟ\l_[bzɓfNp>I(^fwr67b+._篠f۱0 }'.ݪFoSL|, .vǡ =Mi`֣z29 T@! 'X.z`JBEҼwbz'O>W'ZN^lSF+oP)/G/B!d*Q% ᜥ۬媶ȔTw08+R6Z[Mpڡ'H =;_mak.\[?$8grИuci-]dlcDfq a(!)k3B<0Ur#+=M~|{[>M!څdiԺON;uf* !!BHHBq% B}#`ҭB؝6#'- !:י+W W'ucfځC!BoVmX>OdB/JֲCmF!|)gI[\BHR4n'|!L^1Ć]'ȥ9Kk.z|I۾9 Vԯj5j(]G*QQ%]];|iڮ=13W$VAySV7l{ٛB2 yݎN]?-6ҹ}K).U{E[O^[nqS箽_< i%+5N}'+4|ŝ8m?:5x!DY0j&ز[P+"YOɈC3HZ2* ]j37Vm9ڬ}19e㍻O =vzq Gڲ,sӞ,#_K7Hy7b#z0_Rvnd踹յxAP\=+, ["bf.(5v9`˽ǯXA٨fwHadHc/e`NGBؤ}ȋWSje(!Ncǡ y˹DKg.P\sn-V6~ki6  9ޮP 6/AF >dey:lՖ#8O0[ 3Rww5P)I)BYEupPT}C dsEk}S BS/־,!@4VpH! dsVWUuQ,$TS}'[d)>])y" {Ȕ}^?!V{0Os#vNKUn6D]r;W9 U N+XV!ng{Ga6+W:("#: 5y a m$ Yj(ܻ{Quz< B8v sv36]eu?pNw_Z. wx)+T#@wJ~飽O0k~b_W=% cD:S}_BS~KzOTq{ylJk{?ް#فt8z=ΖwD -[_AYn_R}$օǜDB!$$8u]L|UBXA'rr9=캠pĥH%o*6~؜(B W+h [ 9}Y7 L9KZ!mBtN,Y?z !VwMѭ:{UQI#F ^b| ? ^d3"(f˨g+ ;o.BN./ ut$8nclʶ^%`S箵9Wi;6T!tDmW !n|'2wM&kKն{lEK-lW˟%\.z{+H No3jr݀6hًBHB%4hnU B8C[1S#-V,H~}D"8>-YC?>a~×tK=ɘ22 浏^{ص︚2.ɰG(L9KrDwfì~çj u"Ԁ GĹux V x-G޹VAsLDpPok4-_ydɐGBWn>lÁ_,`wћ ֧ ֟wd!) 9x}{ ۙGF h =wv_!DS=azQb"UZt*l9#pD3Ū#| w­;8Ia3t%PhT 13W8]=*#U 09ZñCo>kÐk/LeQM&(iAO=B׈K~\jo7tJ)C"qu[NmGOnP?GCεz .[@QSϏcśs. ]p;Pm zle('v[& !!$𕧗U +zN͝O!?:*R>Az)uvVn;^%_9 Ek$5rʜ5.~-Vm 1Wws!D^;ct'J5D A4(_f2_Ѫ'ۧ}; ) uo B:D8ֶ ݹ +L~Cgp4bw;5bh^ghѳ|ps*~bVAW}q i-E&jj9{E ly3! gm!%WǯtCFo0`%!GP\;x]F",*.`i|j-ʰhH}V\P  ?ZS BYxZՎ=GB;ʎh; Я8LRK5샂:1"Z(hz '=H"gn}*>eupN:'kjRqZ#B-!kj7" xg4btlX ŪX %LQ;F^#B 2cXPð^;u,z!DhЌ7bPCFC傍D5B!P0Uv[jɫ`!8dBTzJ^'AL;r' ]_m;nSSw43;З)hOqB~{o>]>1qaü51{qS} !!$ynA ]`t@`G:UGTv!j7:n]dWB;. ez?[uj%g:W([naD%kiQdȋF$\E߼3H_!t&~P?RFN'ߺrI%;;iвhj0PD)fvTzNv؜'q6Xah+7~Uk߽$1c=(.8#vھe%#1H' 鴴].|/R WX!;;uQDRC`ƃSYS9xgm0}uU[Yl&yFܙ{ֻ[;RW!g%9z'BB(d?NlڎM[8CS?X] t"pqü6o-KTl(GBj Y#fG;NA;H{]__K3 ³6%aRHSV!DBlN"ҡ҉={UJ}pe!uft!O8Ζ!w} =$Z5`Lb{!|j=Vq#,Z':Z8zwamy_ bl^:c: OB; qem.T`{:" t';O2t9Ά1{qв Dn`GLH})XBB(dW^k#b*.\(Of,]VR\`:mzH!+{ څ*lD? .k$>:0aB={i)qdgYtʐ]bG >Q6 )vpgMN}QW/Ggoψs؂W5˼/ : ',B]eBh'|q0],z_{L9Jؓ𯫶?&t-P8=mnd`~,rS _Lzeoԡ.B4oٶ0T϶x{dHNb=-2/W,%gm'J!$BHIN!tb_oW >޹aI4v !Iϴ350f.ܸx^NF/v+vu<@[Sn|A2QA"ncdfv- gg^*Bv~!кP߷$VB7m.q B,ҜhSW}DH}f҄ЖREB]Q.rcg;~(B-]^W+tIrDv( ]ys/v4/:ӭۚ$x RMal8HGnw:^Ѿ iBX^<]=Zz$4+![&Bkl7m"Ou)n2fo0rB*%_֙X;x̜vGG^,-,\%Bg$tF5G?h-z/VML ɥ 8/hvv:TGk{+!{ѭ_{_Vn>,vyaWQv?yͷ+BH BLH ~:d ;؈M[cWqvd>й#pچ]%A!DP.z;jB{,vHT)网.&l3D\{wLnJ?$tSvn|-)G ,[)VvJ\%[NrQ#!u,v:MW3Cha1j'nXd>=rIB:R2@mG]~ b+L Eٷv_y!DtT v|s~Ѭo[jjAUYG"%5Qlgqvg[6sOkҶ-$O_ON8rѼ@{.ZB)]w'.|E!$BHI~!t}GElg{̼8k w+A$m#׀FpplBg|L:v0F9 6-NNDtDgd(zᐳP%o߯/ V=Mbi'(3WrۙH!bz۽yHal W\8$Nꈘ~(޹Y69 ص+:W^8h'Whpl4m/ԋ-v 1 !!y !1hR(!bGeةKt敃}ePMIo_zͻ<|ɉ~oFhBDc=p'.[z7yy 5vfbeʇ;krK):y~CTU:L Gc,3sW qpyS)BJ܂NWD5˖vB+q$Y9\Vb֥XVޔBhՇIog,4g6{( I{/z+29; v犖g'Yy\bG̀{ * ,f͇g4n2̆z;;CP$ҳ aJ V҇J&#OQ u>n8z\=o!|%nɫy+v:/?t:o\O1=D:d)*4ƝꙚt*fCBac@P@G\سSCިp&t,D/(~hzP !2g˷}ջe\;Y'w" !!B?rزL9KfQlwү˴" !!Bx7W逅@t1,iE!BB!Qfk5'|oW!B!$Bi2Gt9j{?zCp'mcdB)B(B!B(B!B(B!B(B!B(B!B(B!B(B!B(B!B(B!B(B!B(B!B(y"kG5)~Z|Hx-\O&k(&CIof=Wr!I:sQƋbh}-'[*/)SQ˙hjw镪.Z5yR_Xf¡I%q.DTOP_?15;J&Po BHj%͒*c2lA~W/YOtwϨdϨk>VNإGnhW Q{Wi-b61QA-'z;SDHL^qyTgeԅ4"#츚N;o񺓖@jvӲ Q)7Lg-?x'1WYa3<=~+Wњ)Zla :b,~#}ж&[~n{ 9;$_g+[>INY 7lKFʯb Y% >ʐ%3.{F}"{.RyrsòOW,!—WrcdBDM~WB~7uֿw}bVb<R_G-0mH)x)e<;.҅0}RRQ_I_o`!Bhu;wE^Es+Z~mwqx]fV_}eb5Ws_i3շsUmҧ]+XaBv{L/L^dOw~Vkɪm}Tr9S<5ZĮG.'I9]u!֤bNXOFzKgQZ>AʧPH:SJ7qľZӾ NA#ko&R>Bӱf1v=,E+%_E{9y'ԼD@6}'01nXKz[sN$[9[䝧Tř[\>Re8(<6KH?x4G] jG–ūvl1@#w"زWCdZLCKBZ5<Y{{Eog*"I`XC%ne(V"]DiR0h?`VU|w4A"Q:<9%[ 747ndWQA'P[U•LD%Pvg0H1[ɤ^f'ZU:]_G<ru^Ybrܤ(ʪXZ*_Է;[*ߐȨUME&"qdWHX(\lÆcsCB{_u 6qBOȂ}2 a%+f nll!t.d+~$maw*5jc!bWNӃ![& ![@  \ j0g=0Hƞ ^D֖oquBq84ζU)`4֒Rb Mƻy+ !nIԓי!9 .[*p98oVUM{ǵ/ଢ଼P`\ ,VnΰIqTWmm0@Qw䩦Zlӌ77̓f;chQKp\|ϱO}b1KO:ԥZSiE'G !R2)fP[abqH^[q!UW6~@剄B^@)w97(qmPtT/$ i; 'TeZي;=zBiI_q ߃7%FT^?BxpU;R\ABb;wO;iVxn B Иm_uL#ણsO b PA/u91ve!BenBc;\8C|(qO[}B>ؠrv+V|QKilBB"? 75D\56K#=li⒀C(͌8ؑnT>G]onZ[@~l<v 'Dv?MldѧwȌM6\1y1'pt$(2:nלA JC7e 37kh5| $Sι%{>B8|-*}S>D\%젯=#\ Imu 5įXͥB{ @!@K9O\v^>eƏnq95l䕁~G+ZO~ w4l"kw(K#=#ddChsjUQ74%ĩo8ftrPM¾=k4dؿ|;yQtVp6XD+ĚΉazJ:\epC=w >|b+DZ!K,Qq;ď@>,I j2 !IǷϵ飖pևZc/TN@r)8o3FKBR $67klcqþ) iᝇ $DMe˖(~{C-O'Z\.6C+;d\JiPL2;W?TPKơ朲S ,~>''5 ! C܁~BM6# B`M)q({KI*@Ah Zh=:Iu+<_vkDBIRp<5y!σCt(6A̴h3zF5-nB'#0m]QH\y,B(׎€+B*HR,|B LTmq'!i0T/0$?\qtH HH̀_#dPlH-A@W/ Sp8 "E%TN/z:R6@Pe1BxB2\)񕜒\E(!MN[}E9"_ E'/ 0~g"htB !J( ǯ9gd߃LB ~)hP+j@\\c+r]EP+h%~5~WlcA"oq)4V!CѪ v)ˁ#~C{jP&eO_>d8ep 6&%XUJގqc&tZ-6hPQ>|jDa !yV26\cztͣؒ7?XL^Z5yp\{tWԊ`?KDVƖVj$QB(;X4t֦8>D!qg QlE6oAdYc߮qVwB|MIBq~U-#%}~G; PҠ>ut#H+?G6 UhT\8TeR}-#ƮQ ;NO( !9y8Kم0PBaajmh+(5yLb7BVQ9E{6dx $MB TmOOyw&hDJHr!0oC(BP֤XcƗk"MBk4R2F("fұRj Ax1~Ey%>` E NԱ"C_ ҵ4NI.!jq|y0ԍ`[`?2Q" 94Zb4|jC%u= ꄒa aO0!cp84i`A24VeCPl4;b&q'vsqJ;i(I$!< ihi*d*72eFBڟ,Yc?:;VB3GfepԜ(BitԺ$!\IP8K#-PBh6]v*@TtiJQ9H# %u2}Gr2Znu.4Y`JF'kqSml yfiPBAZgܤ]#gA>a(H1IpJ-JAMʁю\A!q_IB|T{ve.ͭ aliN9McZ3*:{9y_4"Aud"}Jt϶K(vIPJZ͖d7ȃ$>`kajm=r ZQ'2c BhMX(!C%Iɷ%3 Lv4B;yI ɮd-gNVRiEwoZVHI} IPڳ$X!_!Ԏ#IMدmUr N6ځ r!>븉+:,2sUQeϡvCFS n踇MnpJ7[;QBw{\{ZsY[19UmmŮ5rB!цTqogrr.C5 Q![({_^-B\ AlhvhR5AXeW혞nhpϕlԍ]F J))!le>i֠-vQjH!{9#vnÔ}J:]^!~Y!D8ttN:B_o8ztW/81ˊCwm -S᱋H/'jP΂D/񐩕*B-.ምg%\km5@"9ąh?о#[.uPP}D΄gᚆ2R0p[Co˪vXez0*SUN@Bئ4PƢkBhwfh:qe$5Q- ; an8V! Q#gD^!+Iރ LeW^mKJ_ S./+X!Dmi۰hQYy1g\wė(Uܹ*(Τh6Fw5,S[eGuQBh]{ USF? )${PGZ3@mVPALvq)ڃdk0}+|dIC%ډ 8.4] H~gh" Wt )ȬrhEa>u }W3c 'i3T xBh!D GOk<.>{G|!*9eBJާd`h'WEE#]yC ~a%B*H+G!PXȋ{ZƞT}l(NZu. !@C7Gk6(>Ms΢2ޫ _uJ?ycb+@@Ju*l0L!T=,eb5(شBm>;32\+ m7o 4inTE(a ! |i_!(ƝVBB IXY!ДߎC2} V*%Ý%}Zu?]X_Ĵޔ?ZV> }ɞOENt (wZv .Dnl6 Y=BD 5 n XY$< G|r4-xX+Ζ$iւQ\G.sC{B D*I0;  ̊( 9x[:j2'i׈Ce-BdH4'^9iSq4vPl ]X2P$ޫ?'MmG( d.+4L$BZq]Խ4Bbeۭc[`c`}ms3dTл,ރYa_ pOpaHTDժWBNe@CPCF%,~G n(*wS 㽚v|T:KGj7γvSɒέq*bZ]6 bdeG$%HPA]s%PiK_!znI1ɑ)8\]BBojS8IGI3 .)J"fGu\Z1XYy8iQ:N )OR爽#cBu4۹0NJlJU\ Z})hVFj5yPgi"p 볹*k!Ǽ *@yګC!A:ՎGBa|qn2({u4%pWE pJ' ?} hɯpH -ikN B;oj>_{\5* $ڍ}\#<|"hDVB]U,=j!]ƲxPϣ$Ҩ]kB((i%8Xm@vt4ɃL*0vWl< CmKK.kaHFhm,^/4u=r!DpPשAc` !Dĩu xߨQ0viXBCu_TfΦ hGzC2- |CQ}PBEz?#%A^%+FW2SYP*/v`<2z4anjmTGnJ # &v*b˓ʮ|'j{'p\M+-W!._bG];vW@HD<ސ#MF {J 2BLUFqc3D?ah\T!֪597ޖKoj J )d9F#\ 5JFD2vIfE=j[I4_{UB(SCWZՅPbra É{ϹPB҈05QZ'q+$j8RؓzUzėQ{!D eg[b +Bma7@(a @ B9FQC(Ю6.SBt;QP#uUlCӛ>v\׫kʅ)ZAs&J}ŬgEj9j:AҸ<jͅPB:sQ] /՘TtΞ],'Bh XiPMRɸZh8thr_lcyf~0-PE%}rM'ǕGr\{"7>+Q͗Pִ6ͱUJf3ql!OTT|{2L.!D\).i ^;CNsU 'hfA~iOW8ite蟎9E m&H%4D Sq#8oKvȜvJ<|Vj\(rE(-0;B˗f],Y0o:4#֒-3Q6(^dxClW|B@QVE}h_H(BRSBZ|#R "ұB;\j9H!xviGѶ!ѷmCMuWV& NB,:Fn4|a !yH#<}'Gi4zt\5PEʝчjZq#M$+PIx5]o O?i}7v]Cp0{:!f!/c % Z;mYquB-GQ4k*BqK֣"z`KN}Wb8g]7Lþ{+v9Ǎ\>${AIӀ-je} B$Wb #Ẹ7eUR0t&zqNWBU` Uv(YPB}4ʡ"+ݩ pRv^#n ;bOH}44!EÐp}ʌW ]\椮)bz3*$& XGQ@|!Ľӳa=NRѼj,c=Sj[j[Q-U:Uqh4+SP{;m<\8B(WFT*:X4ut%YX45)ImW}h;*;YPj4DH"D鳗c}*vLLjilsNZ(lw.JT!sr҄PF@lӌ{A0٪- TA/,; (J5ih!͈Us2k!2 >@>C6(h^!Qi \THJXNEFC!| W+B`P3*P+A6#lxOWأ21$ \,j=RЎJШ_W93P{fV  'A@- Y"RDPSÅ#|](1ɚUF#(33d4?$8Iy`}U"ݐ8P'rÁo.@ hYɛGog*[8o3G2s1xlAF P ܑ+8%ԁq Pʆz•mȅ|ѥ e+(HtȻMh|3EsPUdF+m4amgC8\4F WS!R$,QZy[Aa'Ud-Pw3ٛfoA W .RC*bk3>4( TJȎP5mWP}A)^N€HjBH~|_;Ao_y0kәGKw><@#Nٕ I;ul!q:0yB!$BB!@u4agN䈨cPjj}BR;.!1τ !yl>펋XЏ߄3dPz{:|'Π&򬣃ce D6:=I'4>YF4RѪ+Z̖B!$B~w6_|IHLshMYĄ !!B! !!B! !!B! !!B! !!B! !!B! !!B! !!B! !!B! !!B! !!B! !!B! !!B! !!B! !!B! !!B! !!B! !!B! !!B!P B!B!$B!B!$B!B!$B!B!$B!B!$B!B!$B!B!$B!B!$B!B!$B!B!$B!B!$B!B!$B!B!$B!B!$2m^LB!BR(hSI-r? !BI eTO!$BB!B!$B$!:TDB!R)$ل7Re!B!$EBH!$BB!B!d`O!$BB!B!$BB!$B!BB!$BB!B!$BB!$B!BB!$BB!B!$B !B!P BȚB!P BH!BP)B!P BH!BP)B!P BH!BP)B!P BH!BP)B!P)BH!BBH(B!P)BH!BBH(B!P)BH!BBH(B!P)BH!BBH(B!BH! !!$dUtTúM.Y4Cmz-(JYV=|/QQZmԺgպm2 s_HK)ZG.^Fڜ/ɵ2f'BH(BHW~cO\֗3{\{ոM/lYr8dH@gz蹛: Ku8dJ/<\˕'3 \#&SZ>XMOx:  !Bb)YW-Y>+!6N]N9wsB(BST͛NO[Qߏ.۸ߛd9q0QB⇒R  !B˔4J~QAd[R VćǷ{3}߃w_ ʐrO~?g)D!tp# ZvoتGǏ4ͯ:BHR [2z =Ӻ@넭 =anCu#ݚ0S7oC!tpηS? mO!$P)BHxj:j˥bO^æ'L0ݻt޽.:ɚGz;B[;s{O>=lyKAٽn<|ѹي<:Y7;uwsW<}}KHzMWڼ}Bv3cIGC^~6Ål?ђ{,>j|#m1?ymsȂ -s(;d6omZ-C]֢C?d4]| !wnp̧YE~-\UopP)+7Bv .C3B!R D4>t뛀w$dQ₅y/cwZim!?յ?9Dv}!WWߟ$ҟ+S+DXM!K{뷳)vZnsE'QuZr}_! uގ8/\+6,NF!facFjAFiշbk J!$BH(BHg#4!Jp߇-뷈FDn?oڮ[7h'bv}muRbA|Ԥp\m.^b;h|{lՖ8ChIU,G2[W'.Q;BakHs׿o6Ugy~^'R6g'+␺%jpΧ" Bs.Z9X?藱SnܩM{]̖Ti=uzMoV#m* ! !!$;]ne@]/琄P^{y~'WVAG?'!Wzt=UIx1= eߙ,JB\}1s?^.{OOl?] Egc߬yԻBowjhW]bdk|WI!"BB&Vak4ׯ]|\$߬0jJ֫qن-}P>sxI~KTV<}:gYa/Ԯ C:< ӫTZv.w!Yyi8c^B!$BB!$9E9?oIGͰ]OGݜ0sy]=>, B9gi$=vys.\`ˀӇI'f6S?pKCPOw9{{-u6]5YVeڍ;y7.Y41[[xsBHP b4?Ȑd ՛釛m}+2o2e/+||[n>zb9 !K筁|E+{?;X<=BH%Ͼ Bx[YV;_M_zK|uogM|0U6/Y1}g3ݻBxuk2W;.Hg}!vŝ[88IwCF¶݆LV҅4"gn_/QSw|TVve+wD/_7'j_}x]_q|X~ʵ[Z. ! !!)H븙 '}zM*Tjo7q  !oi]0zgWtC3s,=t~~]qEt Y2kRo=]q$QBhƾͺ扨^6}&ۅhJ&V!+yn>~ժ8#t+*~3f+"Oܾ(C/@_}+Or8S2W~h{lEQ_Fzef)P P !.LU"j !¾]N~rʃY y!ns9Q랲}{ n;ڥϨKD &=4$MW2eΪk^:&9^|T0`xBD?WM'^Ӯ:nV:X[&уeqNI޴kQ  !B\m;>lf(!q r}/{龿.by~îI?ĽoKiݒ!IBdߝo{Z5ia HǷ uks^VQQGiٲ !he~tTr;?Ӥ''[BB(BH(mwtsm zW( B;y'_ڽ]diPB6i#9]ndy: xf&.2f4!Ct=GӃxfMBuYeSo,2EFg`B2f+2}:g/,)L)Yѓ.qy aVD1p wZtju,J ]rBHP 0Ȳu-8wВg!B<*jYA%7l3٨C kX,_i&KTu"S7Ckk&pKRLMpc ) -TZEN!UiT K N234 DV^]:gBP B!P P !BP B!P P !BP B!P BH!$B!BB!f!B!BB!B!B!$B !!B!BB!B!B!$B !!B!BB!B!B!$B !!B!BB!B!B!2 (B!P)BH!BBH(B!P)BH!BBH(B!P)BH!BBH(B!P)BH!BBH(B!BH! !!B !B! ! !!B !B! ! !!B !B! !R)B!BH(BB!B(BH!$B!BH(BB!B(BH!$B!BH(BB!B(BH!$B!BH(BB!B(BH!$B!BH!$BB!B(BB!|N|e,9/9' I’C!$B KY'M@V䐗䰞$' KP) t@$a=IXr(BH!|iS#kv$)do~%%BH(X!Dŗ.ka@KKGYYYrXr(BH!6W,9,9BΒÒC!$B !@%%BHXYrXr(BH!dC !aigaɡ !aCXr(%%BH(B:aa=IXOP B@0!,9' I’C!$B !@0a=IXO !R:IzP B@0! tXO֓%BH(B:az$')BB!dsE:' 'm!f\Hy]a2Q $X}G]z yNҼ]32'kR^G֝z'9#MYOR)B@PYx= @=sʇ;G&,^%iڹ~i//32sp5 aؘ)YOR)BbKUy9 KV!*Y-iY?Ps ًRF6yT`^֝@*u[ʇFFhYy>DckQvp}]=ttc(' ]_} 4c|RLu"pWsG'p$ /)SA$)(B !@v'wԹɳÛwqX_bI;SϿx.*x%9χ_>6{?dH]XH|Fa KN!v\^N(8.Bz Sgc=g8Z5_]w)^lG]aRղ)C^x06{yK!{CCBXV}o)B !԰-~r ֵCSAyM_N!DyRܹPpȨI tVG wrO?%:씥Sz/@B֓dYj ق۴ʵcŤK._[=˯JVqއ_>!]5}n޾&޹T8+sGny(1uѫܖ^)jNͻqԹ[v엳-Vfn})c&?>qB !PSpXʘ N?p;dPU*h.[ _΋$] atrGM;mmFTUmuWj ً4hީyYt%PF/k' ~nܺ[뵲U8ve6L-^N-Vs!9 =xW%PYO֓6=i)*7b{#mIï7${C|rsl!"o?R6>>/w?E,u]Bǒr%IP{*X !BH()2w'S9p\sfNsZqɳ<i'l9i<б;xi^$;i$e!2=Cc#䡻<_wPM@޴"(ڧ͜DEgPPlI!d=IXOOD3/E7Q=FX׺|dۮRIJ==%[9#UvYY)i СR.YA*i_)ȴ7d\b*+l>{;s,*"&A1aĀP$̢mw>g;{}yi-PxߢjVUs>f ~ސQ5CFMwOyIeB!Og/msnAqu1--ˣ'wSl+!+&!^!Ti+iNdw|_C&!6^Lv}hkNI:a;tXG { r=wgiZv펅3XvH- 2!n:F *W,vn0RZشmw={Ƅyoܴ L!$PO,To?rbILiVXe5l˾bkZ*v+ACB+Ƥ2OsVpٍ[Cmx&yg  Bbm?C؀X!D!|den Ԋ6gyyp)!ᶕl4!5-%.Y8!l}=\I^v[v췦n05f{j;Y_-o&1ScaūHt^Y:\ e4?D]1n\ yB!VJ,RcP}@Kl͵@ |!$PO,2= =G&`v_ѴJf?w*.B]N?\S:!w& 1auE!$PO,*'Vplq=VmH}7+{ nлn֘P aC:;|0nim|˽S&n [:!T+43!BBQc'J=w<2{;vMެGamNx:5nީ|Px԰ސ넰iNmY}Gz’c'y2{c>m}kT=ExsyB(--;Ak]{+BH= ƣu܍8-t5UIe_rC]HyelP {+EWU=V{{ Ѵ͵w[:f:[w!^T-B!]I뭳vPwٮOƍªx*jj2j3|neh r 39[dO?W?dę$: _GnճE, 6PE.MJP>twZx(WopBx2+:UlCSӳlQGüBCB'!|#wÞkp^Hxx[nC'F>2j/qk>ūƇ͵enM:ŒBb/?!D!DSi}1yzAw9{(b5N?j / ?Wut=?:#BTF۾xF7 N@VpZHt^x4Oss*+6ؒa']X=U+ޮ`' H|Lզ_/Q1sL]Bw!WQcbnPkݍM}4iIY!+Ϫ;1_/+b]|"BB{zׄћd9) a7#=eB_B{'hՑcd)Dw=6߲GXӘWxDn{BJd\y.eVv{ǚ>Oid%2Ʀtbm|QP;!D! "GM=Wct`P[^ {آK<}ӤĽ3q{&\}#I "T}mܺWu͜Oێ0djmӾ`[̥DB@ȴ7?x{PLcsRט̽.B/**xqjE\&'DfSid;w$N?" K6r4i?<}a߻!D!$rBD3x&޽2sdęGe^)Q|Z䎽x1-Ir6sZ"AMZXPtYun[ P9Up'5=߰``qC3ݫ&hy9&ϭh_l+w56lÖ{G S d}5ȺcZ+PO$ !BY@iDHt'z'BB+ II! HtDz'z!D! !ځz'BB++ !L`KZuPcuD$B! @sE9[C(`K~ 5ֱD$B! @sE9!BHSORO""!\9DBD;C "$:@C9!DB9!DB9!PO9DB!BH$:@PO$WA!DItDI"!!$ ѡI rB@B I|}|ҨE+-k[޼c1Qkv7ji-;D_ppkN˦]lZtd>oɖ{h𐠰ȘN}FjIBEj/f-iҮ{ng-Z]L[ؠUw[ipfjC="POqvOU|&a=ew\vDzcwgg5mWgFK7SNa=;]NYo>8{\6ךQmroѢ's.ݡ K*ZGnzBgM|k/l}bdQԓ!Bổ4y~ڸCע[U>ZP~Aj֤ .m ֟:dw׬kn tMVVi]71޽LmI$:st<619|#s;?r ]/0d1#RΈue6vY~Z˦]nާE2qoEiwI)Y -QhÃsnN[E;3wm޶P[SjuDҠ3[ܒPO򻏻󷯫69ioo~ JejBV~ij,S6 @\ұII-\]zvZf$: _wvS"--(h-_C’V]lyA#gF%>H9] 6o;eXvPō7CIR&ZcC1&\倢_V0#rҬ%̊"I2v5~"YgEC= ԓ2@: T-gR AwT%k~B S 2m|KRڎ&H\ߺ-L2ڬ\fD-;Y'#cר'Bw"љ2w~.rq5NZm~ݛT7j R5N[Mv5*ˤ3'Rڳ s*_ 34X-92BT) ;3bU@5TZvTa=NڣZP5]$:1 oWlRS^%+6+Ȅh&',*pO9P^iB6Y M3 uV74i׻}avr$$POVT/%8QcDUG;>:}Q靇&R2de``Jj69RBM> iYE^T+ڍ9[9 R]@39!DB@ߕD<^UV6cJ*gEK%PZѓۜJӝly.B.\MB8'j-?pJnnfGOW>v>mA~zkZw"݇OCG-Z{X]v=N$ľNEm">#342&ЍQI\!Gۻҝ>CXE07º-}4nOC=i8sopUqG¥?`ԓ YYQjWn}gƥVOϯ޸Gx*2ZG!-qMނ;3Lk7ltChVmQcUZ׾^yPvOH !]I땯:OUDTy’_Jݘ562#Ckaf]~O_|S{AٿlaŒcÓR(A߅ѓU>_6풚YgM:*پM;l͆]']mE#jPޓ_j 5jS޸eOoyɃsQ&m{+י<;ۀ[qw~ljF?Z+vwVҲ7~]Aqd(Bǩ'U ;3ru.eb\B7˚N NִIeTm*Fmuw=`er՜-_zunuLUCb]m"! DsJ减@yyHfn-iX@'km.6cФJʹ+6P{AMfLnɃD7#ęQj$|OQ|MR#sƊ]$:cbN K*%l^O1u]bDq.67ɜˊ Q6m7tԬB[|14 R|E5WؙJIe'B7{.`1B!BH+;aiYvL?||kvd}R%;e}-woIJܷ%Kdž[nB,6Ipw@UT\CCf;ײac-D睪''sĞ+ɿzGՋNUwڸ'ͼ"jBٽMZWm{KFOBU!]t^6KLvi==TwBm)T&wT5xF%=xVFAjZB֏^~;͵esJ=Nx'BI!0!@iIxѮ4rHPX^$feL|*}xfyLA(˲\ZgҬ{Ildr.X?oi'ۧ.8~@ =ٙqBE K*Bgi qBYbԪW啨n"OU靇7>000tvޕ s Wl,6nK{/2-R7^YGS&ώAߗ:H+=4ԓ4>D!DItB!$r:!+~Ҩ}9֕'!I.^ZyNWn}'r}BbӲ.4Z%~H*ee}ǽ79nvת ;dT 5y(ÞsL ^rh>iƜ뤸>h70z'BBU[Ҽ\9uMn1nw/Ut=)OsuiزՓy2ma^& {>R(zmvk̍xn+B/:p,{WhG9sbޙBTpBYW/ӿQҳݱ% SBm{ նwK=6ww7$PO"! 4W@$:oC=)k,%8qž[1!Aag/ذ3hœ^!7ƽU8'jyZjVM -7lsƘw[;uW~qC|sq$ϮޓsLm0'[-^UcF3?Q  [u߱+8z'BB+ >vblJo=!(,-J9F/XU^ _e6X6YzpIॊo>m97=Χfe}ܰ^MlU%JINI)YB'HR_o$dݙ4k{PO"! 4WD hYmW{rewW'Ҫ˷iR}g'̈eɩj!=XI%or՛'WB9#}hMUh$SǬB 颽BhDkJ#ޢMSCu*6:t$:9[~ٴ +KetvCFDΉZۥ("C ǫ߲-liFzL\  g-qiV]|k.٤H-;C{0oV#Վ6X4kdSF[%i'Wn}bņJDrÂJ7^[+ӬOLJ/*,W}Ut(r/Vsdn}$FP9K9ioZSLv={jϷ%)ͦ r=~̙H>ܰS5ZYnJ7yylbVA 6db墍Zt^k}V[9͝R03rkЩ'B7,Q\Vh硌ͻS\PmB]-PΥk7S)aɚJv&jfPڿu[+=_|S͛5̼O(׾ ׶I-,L˺`ɩ#rg}U2 jJ%jR3 W^2u?Zz-v!҆7Udle-wQ`B(SRx+fN\Zމs$WJ,v-EK 'ɼ.钺q,I"r V&t5Lױd.#Õ +up}ewKnQ<Nm>|loٛЩ6oӮYAW!B"oMoUULcTxVC*4W|Uh+6l ac fz&27\k+N-WC[|Zjc&G|UGZwL ju}6݃! |֤|uWnB.1躛_A-rh}6 @Mw7#O-ADzwR RΘqS=ˬr4]9$D֎L;* ?JitI6.IW'k_vZ/e$: _6"Rr)Ź5(`${.lk{$fͲ87}JAlScVnB~m[iBS'{V;leB*_B8  2F̩౿A"[9MV%3$(-5B;}?q ^p^=3rw#2zVAcfV˾TQ3ڪy&j1ݜ7jk[v8soe&O<{Bh׵Uz!D!|K=E_*{>)?6d\ZasWtWeʘ{oL0|Ҹ ۩}7M.!,m<6K&-{QM=(`>=V-%k?DWK?pYM>cB(=SK=k:cJ6wjNظ"T(*\%z)J[ Fyc7EbϋIynDz<=* Wd\/*tCgVtʜ&$:!POPebB.=άTP~ظHT%un}V6 B:k~ڨC !\vbo;v|wܰ~U]_'B7/?S{`Ϟy0!VPcjҲjӮ&-1Ckھ c9nq/{u7Qk4!#XgNP}~`y~ksIHt^&dJ%iAxQ,҈3zgf_Bw+ABb)lE*Єn (yTUcrO1*$3T{tonԺL5q eAiU'Ud]j-~#|&Ki>ݿT!rC=ISO9DB!Bgo~cV9D;9,!B!'!D! rv rB@! rh'ځA! A! "v"!B+ rv"!B+ rv"!!Di ځ!rB@B"h"B@B"h"B@B"h"B@B"h"B@B"h"B@B"h"B@B"h"B@B"h"BB"h"BB"h"BB"h"BB"h"BB"h"BB"h"BB"h"BB++ rv rB!  ځA! h"!B+"v9! \9@9! \C@ BHsD@ "!9D;9DB!B4W@@pBB@B"h"B@B"h"B@B"h"B@B"h"B@B"h"B@B"h"B@B"h"B9D;9"9D;9"9D;9"9D;9"Bݚ=e PG0Du!!7@u"" B=2MF ojj7B@Bx" " " " " " " " !BB B B B B B "!BBBBBBBBBB뇍 TQO!Z?^{5T| ! o.BB8!B!ƿ p/AmB8!B!W^^Y9@B@TĞy5 g!D!xs ϗ&9{!B![_Vy@B@xOMW6&8o!B!@s.G ! o{b ]sBBm^w@B@6{I, ! o!?75Y@B@N?ONY@B@Z+NPW?p~BB:MR[3"~ksw\ ! _m69@B@FG4!BBgן_7o|і"[ Yg2BB_o(8!B!BB&ާ[ :!D!C;~9^cBBB@B@B8x6BT !B!&J#~!LB!!j!D!@!!!BBB@B@B@B!!!BBB@B@B@!@!@!!!BBB!@!@!D!!!BBB@B@B@!@!DBBB!@B@B` l9F @B ;T~AuYe~nS_-q?\zw=u) !BB¯Ϻ3AVm{Ψ.>kWn[ՠzB  ! 5~^ B arJmyJ`6=,9~-<~!BB; 6CĔB1nŒUK3e?Lwo ʾb?}ъ5t4l}[uoKl9mت%;W6wVp]Zo'dnSUm>o6!BB@BcLz#ݕ\yַ”n w?|颭38(Ɵ _hV"lQDkڒYpW_[c|[o"[wX-p[ǯ$BB@]B|pM!~lO_=q[?G}F&xPf&9aAkt(oضfw'_?=^P!i/S&ؠSI]"6OM}?[ !Bx煰鏳7FM!8F4hhxaʉg ^!뺼tv䊽uN>L!?2+Q56Z^%GNl㹠;!B a!|ypǾ+51]缢kMNdg q+-Ւwru3fy1h)vKW ^3`*޽8s+F,ǯν?M;MLTVK9=͓BhccNaN m}U?x+m:K]!B! 1pM'qofaa\ɬb[(m]}>yb1!)ߴ)gn~`~X&'ycaFXOlR7= aG}mJFS6x&roȉFTeBo4byfUB! oaN楄q.'_{_?M{>owJ̝߰%ͽބP%I+SpF/ǭKHv lƞ*2:=(wnCu<-w^}ׅ6w ]s9t/v076ZvAgru/!ra)Ӿ?RɡGcu0?)_*ZB7R'Mk5iyKo?MZO\6o(2蓰{sBB Y?n "{sBBB^U={B_L_ޫ,8gEBB@B)c)fw]1{,!DBB~^þM1_>h DwR|/id#!B! !̫{Dž:i5w{w]ZXu36rB [v Oͼڰ[UZx}V*<5b[j֕+Կ*t榸]6$f/Z|tn|'ѱGUJG_4eݑSEd܈ݞaGbӲ_'T8vAGi_L!oߑ|^f^~"Ė7yۙ 7a T~ڹs*#o L|^F'mwNN{N)[aߖ>yĥlLqʻ f{GنW|!+̳-z^;Ӫ]9Kwjۄj}FDLhgɄY کf_ZaA j9Fawת"~8hnތ;20m5hu$GO_.>Ou6&۲qiɜS?ewvwNd͌eꓦc􋆌]C҇?5-m7')(ųv*k} wj܍;L%]׶7#Qe_#t>h0oy~b[0>-?pFbUdΒNuN\1j+!BBMZV~yiJFKn?Tmi)mq&W>ko'kkּeIEV_[ԱJ{ T>hFB6l{2'CW}<.n72UuNRwkrt d2iŊ:՛iM[zKu[u@sE%&U%ߍX:ul>~c+u#mVZϾw&M:LoԯSW^"BBIJ+Tz<~B(SFEټMy~W\gLwt}ZWŒE;u߫?BxTzGc4KH:gB87z3kbWwqk0SCFYVMЍsX>GI-&ۢx0hX\lB3wET !!!!|E K6:7zP٧>4rODv p]%ťkiea=x|ƢQ og )rZkZeg/*XشS߫(;Yr]7==|A*YyL$d&g>CrS/PaU;ɭ:(Bg̅n}WB&C_RgFg-1~3{O_7!w.wTȍ2!1d-ϱ:9*۟COZŅC:nMܪȑ뼃Ny/BŒ;Z};< yB~15CAS)Z;בsǐ_rz3p¬MyYit8:K&:-ϕ̫s"BBW*n WL -#w8L*xJU]QZDŽpJK+5b6z;E+N˺t!+e>&ٰE7FJ9*mڝYcwεL7]&aʻkk=oR'- λ}+KnR}2L)hyKዒ ߯kl˻ pB(t#$u2W]:BX_P67!b'KRX`5P[dJ}ݥd^u$FPe8W*}?i>:Khy]Wn~o?JY,M])-ыo|dz']xƣN=Z},mN:B}}lCuҴnqQpy\|@a@ UBء陮xXBm 39vTZBh#_PwW%.]]EOzvvQM>K,uQ)zW=vئL7 PGgi&\QDLyο.۔)^MTTPάjwo_Mm>|5vhMU>z YuB> Jl ! ""Τ9Md,חdT;~F'eJd%NI)qJMNT \t(>SD\3ʘߖJn=Ԯ]IZPN7dz^/%pk}4Kxf+wC(K۟m8!r9OfԽN_\?Djxڋ7F-:6Vë>s:[Hj5}16+ /5,nc:!Z3-jbӔ/p]fӮבhپܾ@EVM[u޲$<.<^NkfYF_VcgjðmuBߨtEʽdR?0=JL\ʒNl7UߘnȨl_'#ݸn!BBQݬs?xg"ͻL'PyF narj\L#lqA:/zC;&63n۰$/ykvޣL$Nj[=3˩&Խehk-,B9=D.؉6BjYxf~5hy]^C/\~as/V5lY_|0PE+^޴SB#!B!B!BkRC#<2|5j7,8׷ Bb!D!!!DQ>lqi~YzmC!D!D!DBB>jԴSX!B!B! !BBB"! !B!B!B"R"""B! ! ! B!B!B! BBB! !BB;#ym}MGU)ͻL}?l hg;Âcvs+wCG|o?qcqϜBg'"sضL3"BBY{_Zm;k/kqwm/"5=v{ƪcNؚ}o mɹt1h}tSbk02tù/.d 돼B4>|9BB@B@_eޯ, ?P=}!DBBB(Z|v{40ٖV~ZՙQޫ?J wyEO?pBZ>qvB~鯟4%CV;Q4et]smGyn m?kܠ̈́N/Ypc̯:M}}G.N>yQ{Ա-Aձ K؝?c )4!_YRע4[g6~REe^ Zcu/Zx{V'_xPXu f<^=vV.8t tt-/~AѶBu1q)'\9gn`RzPw9ݯVsn:y|% h~GDJXOk7\vΒ#wnrUKUx3aOVj_2}hԂo<=Er}ɺCn5e{utwbY0'7_DdN+D1ڹAJu9~ZG.}"uč]9b,LɄYrTΤ_Yݤ8}mwzǥ(t:KV+(ֶJOdmܣc/'k7M^7tl g]VWPBe*q6Ltlڰ۠ᢊogL˅ ZGBhGN+S{ -ۓC%yTN+iT%N:uFӵVAG}ֵdgoyi˜~e VOuBt9t$fR,]He;Kڅ>KCì߅$[~hgl@\E%6={FN?<-YQ,1V{taNBZŚ`ʻcB՛Wn~K\u@uۄWʦ 0a#$l:ᑉ=FL|'tT:]ŏJ _ib'Grun7:}UǠL] "4JXӗrmY@λ.]].Kb%!B!B! ʩZ7hntʤ>MN۠D&>mH_;dҘnN^Wz*Py!7O>+wZOvuX\}|$:j$njRYvˉ)^!|Q[aƢJꫬCY~HLUxvCFG/FJ.W_f^}lUE+6!~2o?]\U^溬B I|{l!+^"|L3UWctln7!%v:e!iw=q1CVj+o:B i)Vħ(\B OI搱+LGyA+G2j X Gj/9T $]5຾yW`Ȩ§UWp7s? !D!D!B%JkwU|kn𔰚Ak3B(1(05jž,pF:RlCm%a{3JuHJge~A>hPT T y˓c|cԱٸDՂײ[V3=K"2XFJ%u42ghrWׅhoMe/5zA l5.^ZO`RZ\mɆR*4h3Au!ҋLʿ>Cد)YlwUL]atdJ;مB8"trS/…+ο^ccBp~O(,چz( ҾFOY/cQNn݉^;kgqKv:s3:RzVB,!t𵚂]v%OQ_u^ 5c oKBBB@Q!,z5Kb0ӳ԰D_G(U_mhB饄$n;S)gU.qyʴ1}f.iK'Nj☸+ZƠJ]?}YYN㱷$RDDEW RDl""( ҋ"5bLILbv>?g,f뾸fVyY|ϬI?zsvVdw?\n] ‡; b^Ps-9љQwn=th쎧 \ rDya?>zCd{-'.e:,W)"dr0#҉-;WOL/, Ek3osѡ`>swnJAZaN/B}O`nfNWVKNL"wo۟>:q~فmj7RpRq#T"Dӓ$<{w ]Dt=A=/Pk_=UծCy篏/)H58=o; -n68:{jQ~#f>0k7؞y<."! K\0{k7w_=W^s=^]g_~c;gٙ7Y>Q5d̅][6㬨t(rU|16{JFR}gbgjۏ; \M^pT^c xRv|mݥs_(Im9y!w-c=%/.= lE_ڰ/{jQ B D"O (|3:-^xM;=׏zI,&cO7,֢R9=/j=c:%΍}sw;};qO.kS .Μeѕ.!=!䦑qzD|o|AmUQ:rAKf-Ph}jY|ki=#ʳK>5k koF,mQ_1Am|v'7%D\]O\º=HI9QT7˽ݸ،ؚ\mqT4|zeg6QGGRDЪR_Tx|q9U?> bLTV#֤Fa\V<6-9/g1/!"Z71qwO]nہb0+n?R#;k_?AxTh20=fbp5fJOPal} BA DtG^AJYnO"x&*b#b*b0c< /vG<27npWW8w+}#ūw<( yvnXU"^A\ ==/B4-.`#Js0Z7.yhyQA $Ub-z:n_f򨝸z|mӳLy:DTĖ- {=N|^]N;tEWo+yGBlfAkY\,sG }aOFEpF7ffK| Ug-y-rh0o3!\x(CJ+u^kޏD<6'rKYz:66a'(.9Ea]pȽkh~Ć$;GU8=6ud; D B!N.4oWA B!,N=Iħ B D"pU﮹}m]lBA DB |gQtǧq-A(B(>28we@"!Ae9W]g/SA(B(Z.W > A Mg-J A g_y⃰˶Y?B D"Lzgk0:mA(B(;θ­-9 3/h D B PF44Hj0i8q-A(PBYp޵5~ȃ߬:Fm !P" RͲ'+'n A(!n:=Luhk D B PZw3dAWۆ A(!J [L!B D BAYWT q[!P" R洳/E}C/ =ōؒ A(!)vN A AB D"AA(P !P" AB D B@A( D B ! A(ABA(!PA   D"! A(BAB@ B!PA   D BA(ABA B@P"! BA ! !P"AP B D"ABB D!A( !B D"AA(9bkd^&Pfܿ"v|`k@Y~}^j>>bU B x@&PfxSրv{~A(ABA (]PsQs62ѕ{,vh B(ꍻkwmeF*Pv|mm! A! A!BA! A! A! A!BA A A A A A A A A A A A A A A A A !B AB AB !B6BB AB AB !B@ A(A!B@!B@!B@!B@!B@!B@!B@!B@!B@!B@!B@!B@!B@!B@!B@  A! A! A!BA! A! A!XT!B@ A(A!B@!B@!B@!B@!B@!B@!B@!B@!B@!B@ ABۜ~aKnhňI1ݥoOaMwA&Aщ?IxӍtcXT`1ݘnL72Tž! tc1BNE%~kL<$,*Ӎt#`Q1,Ӎt#`Qtc1ݘn!,*nL7Ӎ ĢEӍtcXT`1ݘnL7 L7ӍFbQtc1B,*XT0ݘnL7AE Ӎt#`Qtc1ݘn!,*nL7Ӎ ĢEӍtcXT`1ݘnL7 L7toYAӍ ĢrR/*WV:{{9-*oɹjAkp-GxK6[e76JY6aw6ݘ7Xn7[|߾][{g_~\pT/AEU!qC!9Mj c޻hJ~]B;vyl=}\<5ưAxaz.㆗m{1On!Һ?zn|-9sAй]{QS45 >]:)v,Z13L,:Ib)?mO?j;g57p='_Oě IdZ_f=eS\!┘b0"0Mۏˇ}vS)cۏcX W5/6齘 ][T:Mw^t%m '\}K׬{㇃FbQ)e"stSOo?>=7q;,|x?~Fzzק׫ˏ2gOtSo`eC:q|AXo`-@M/79~qΏޓ>1a᩵+-ؿ1NQӍ>1MOmZ;;e>|Lݲ"{M)߹y6~z^=gHKDa}үo[|:وcMӧ6.:wRb {%rdaQat &5w^޿l tKûyf'->b|ǻzNm]5']x[6:hX5v,ߥyHB ȂŏvN}e˼},߷>G.kz=Os|aF^5]޷#~dF-~&7{~[Jd-80Ȣ2at|SkUJ/A?֮.ĺqn{fIzPv݂濕t_nN,O4_/n w L/ DONܰ8zaS4ӴTfƎտtc]7-xue76<%Z1{qss/oC!?4n6&w I&Ob𰇝73V +LH/ =|Ks0ŏvAeb71{x7ݏasCNwĭ_|p^ڦ]Äv?4M-xn-ҹ-*sN92XkaTБ1ݘ#QWtn!<[7f7.nyo ٷ>Y##Әn!R# 씦v34e/7>yh&Oo.Jl~з/# ¢._^ `]s0] 0S[WiGn Io#LV|u: ̢ESL7GNs^:1|׼vWwdAwf6t},=.;x:̺~n!aؼ'ӫb/0=-3rV|:ttn;Ƨq#fǧonVwH )5*,x{c:hzS{وӹm^/d7`Qt@DZw۞c^}jJ3v ( =vtG dNU+fr˂p Wvkď쏕}`tLdtWL{{=w,\oˇq#1t w8LO0'GLݾ*3 i42XT0%; l1i7.a$b:`x^"ML[VZ*7e#]7=24Odb.0rAE~ܽrr6JvnA+g/L7b܊][Sk]ܺAZKt;ZƢbO1hi1fEe;vf_ˑzh4WdatsL (>ct˟ߏ/cg mӍ>ZF xw|Ő>fO먿1vvn[mV륏w޴| tt0DzCu5u#"ES0B,*UVڢaύ(vN{u;Et龨UԸw54}ĂoujV6ݦtmßwhRy97ui~W?=o^ot#`QtcM7n!,*nLtn!,*tcM7!,*Cm1B[ Ӎ6ݦӍ  Ӎ6)Ӎ ĢbQb1ݘnL7 XTL7ӍFbQbQ1ݘnL7AEa1ݘnL7AE Ӎt#Rui2O}Uɮ^.W?>>N7=W1>+nniQtnӍ6ݦAEROW yĦv.n3aXvV!=ܽ;om؁߯yEmM7tn!2lQNOٹm[;,*nm1ݦt#|js{?,k6nP:f=]榩u9:[Tj}/U|VeFl[}H!fL|Kϻ7>c﬚r>K'_fe+?kvmԬce&Ѣ6ݦmM77N|Έ^k>s& ^;#XfzLpwt,wKXZع8]fM%8=NX*)nvo3 ._toǯ^2!֞tww45 ;Enޢ6ݦmM7ʷ"Yunzkt:U.*i o*>K'k%NK5$]Tִ4<6ۮEmM7tn!oŏV+m߃~bym.,*7!nk bq *:Ţl+޾b_{-*nm1ݦt#|[~Pjwu<Ă~3 \LbOlQatE%;aY sL6ݘnmXT?޼OYqgGYZ8~޻n6nЬ?KV/WTsa;է֩}ӿq٢ܟv>eXcbI`MtnӍ ĢR}cX~yfqStcMFbQ(VJmY/ӍtnӍ ĢIʢb1ݘnL7 tc1B,*XT0ݘnL7AE Ӎt#`Qtc1ݘn!,*nL7Ӎ ĢEӍtcXT`1ݘnL7 L7ӍFbQtc1B,*XT0ݘnL7A[T*?D  tc1BNEbQ1ݘnL7AE Ӎt#)CNbȀbăĤnL7Ӎ @ B!A @ B!A @B!A @ B!A @ B!A @ M B!A @ B!A @ B!A B!A @ B!AH$衒IENDB`c-blosc2-3.1.5/plans/shared-thread-pool-architecture.svg000066400000000000000000000356431521743436100231450ustar00rootroot00000000000000 C-Blosc2 shared managed thread pool architecture Diagram showing multiple compression and decompression contexts submitting work to a global shared thread pool with workers and a job queue. New C-Blosc2 Shared Managed Thread Pool Multiple contexts share one pool per nthreads, reducing idle threads and enabling concurrent submissions Global Shared Pool Worker 0 compressdecompress Worker 0 compressdecompress shared workers Job Queue queue-based scheduling • one pool per nthreads contexts submit work; workers pull jobs concurrently cctx A submit() dctx B submit() cctx C submit() Worker 1 compressdecompress Worker N-1 compressdecompress c-blosc2-3.1.5/plans/shared-thread-pool-implemented.md000066400000000000000000000212601521743436100225550ustar00rootroot00000000000000# Shared Thread Pool — Implementation Reference ## Overview c-blosc2 uses a **shared managed thread pool** paradigm for parallel compression and decompression. Pools are keyed by thread count and shared across every `blosc2_context` that requests the same `nthreads`. This replaces the former per-context thread pool model, eliminating redundant thread creation/destruction when many contexts compress or decompress concurrently. ## Threading Backends Each context tracks its backend in `context->thread_backend`: | Value | Constant | Meaning | |-------|----------|---------| | 0 | `BLOSC_BACKEND_SERIAL` | Single-threaded; no pool needed. | | 1 | `BLOSC_BACKEND_SHARED_POOL` | Uses a shared pool from the global registry. | | 2 | `BLOSC_BACKEND_CALLBACK` | Caller-managed threads via `blosc2_set_threads_callback()`. | Backend selection happens lazily in `check_nthreads()` (called from `do_job()` before every operation): - If `nthreads <= 1` → `BLOSC_BACKEND_SERIAL`. - If a caller-managed callback is installed → `BLOSC_BACKEND_CALLBACK`. - Otherwise → `attach_shared_pool()` → `BLOSC_BACKEND_SHARED_POOL`. When `context->new_nthreads != context->nthreads`, the old backend is released and a new one is attached, so a context can dynamically rebind to a different pool mid-lifetime. ## Data Structures ### `blosc_shared_pool` (blosc2.c) One pool per distinct `nthreads` value. Stored in a global singly-linked list (`shared_pools`) protected by `pool_registry_mutex`. ``` nthreads – number of worker threads shutdown – flag to signal workers to exit context_refs – how many contexts currently reference this pool active_jobs – queue entries in flight (enqueued but not yet completed) threads[] – worker pthread handles thread_contexts[] – per-worker scratch (tmp buffers, tid, owner_pool) mutex – protects the job queue and active_jobs work_cv – workers wait here for new work idle_cv – signalled when active_jobs drops to 0 job_queue_head/tail – singly-linked FIFO of pending job entries next – link to the next pool in the global registry ``` ### `blosc_job_group` (blosc2.c) Stack-allocated per call to `parallel_blosc()`. Holds all shared mutable state for one compress/decompress operation: ``` context – back-pointer to the calling blosc2_context next_block – atomic counter for dynamic block claiming (starts at -1) output_bytes – running total of compressed output giveup_code – error/abort flag (1 = ok, 0 = give up, <0 = error) active_workers – workers still processing this job blocks_completed – workers that reached job_done completed – set when active_workers hits 0 static_schedule – true for decompression / memcpyed (tid-based partitioning) dref_not_init – delta filter first-block sentinel mutex – protects output_bytes, giveup_code, active_workers, etc. delta_mutex/cv – serialises the first delta-filter block completion_cv – caller waits here until completed == true ``` ### `blosc_job_queue_entry` (blosc2.c) A node in the pool's FIFO queue. One entry per worker per operation: ``` job – pointer to the blosc_job_group logical_tid – 0..nthreads-1, used for static block partitioning next – link to next entry in queue ``` ## Lifecycle ### Initialisation `blosc2_init()` initialises `pool_registry_mutex`. No pools are created until the first multi-threaded operation. ### Attach (per context) `attach_shared_pool(context)`: 1. Locks `pool_registry_mutex`. 2. Searches `shared_pools` for a pool with matching `nthreads`. 3. If not found, calls `create_shared_pool()` to spawn workers and prepend the new pool to the list. 4. Increments `pool->context_refs`. 5. Sets `context->thread_pool`, `context->thread_backend`, and `context->threads_started`. ### Operation (`parallel_blosc`) 1. A `blosc_job_group` is stack-allocated and initialised via `job_group_init()`. 2. `context->job` is pointed at the group. 3. Under `pool->mutex`, N queue entries are created (one per worker, each carrying a `logical_tid` of 0..N-1) and appended to the FIFO. 4. `job.active_workers` is set to N, then workers are woken via `pool->work_cv`. 5. The caller blocks on `job.completion_cv` until `job.completed == true`. 6. On return, `context->output_bytes` and `context->thread_giveup_code` are copied from the group; the group is destroyed. ### Worker Loop (`shared_pool_worker`) Each worker thread runs an infinite loop: 1. Lock `pool->mutex`; wait on `pool->work_cv` while the queue is empty. 2. Dequeue the head entry; extract `job` and `logical_tid`. 3. Unlock, free the entry. 4. Set `thcontext->parent_context = job->context` and `thcontext->tid = logical_tid`. 5. Call `t_blosc_do_job(thcontext)` — the same work function used by the callback backend. 6. After the job, decrement `pool->active_jobs`; signal `pool->idle_cv` if everything is idle. ### Block Assignment Inside `t_blosc_do_job`, blocks are assigned to workers in one of two modes: - **Static schedule** (decompression / memcpyed): each worker processes a contiguous slice based on its `logical_tid`. This avoids mutex contention for read-only operations. - **Dynamic schedule** (compression): workers claim blocks one at a time via `claim_job_block()`, which atomically increments `job->next_block` under `job->mutex`. ### Release (per context) `release_thread_backend(context)` (called from `blosc2_free_ctx` or when `new_nthreads` changes): 1. Locks `pool_registry_mutex`. 2. Decrements `pool->context_refs`. 3. If refs reach 0 **and** the pool is idle (no in-flight jobs), unlinks the pool from `shared_pools` and calls `destroy_shared_pool()`. 4. Otherwise the pool stays alive for other contexts to use. ### Shutdown `blosc2_destroy()`: 1. Frees the global context. 2. Walks the `shared_pools` list and destroys every remaining pool (sets `shutdown = 1`, broadcasts `work_cv`, joins all worker threads). 3. Destroys `pool_registry_mutex`. A `g_initlib` guard in `blosc2_free_ctx()` ensures that freeing a context after `blosc2_destroy()` skips the pool release (no use-after-free). ## Concurrency Model Multiple contexts can submit jobs to the **same pool concurrently**. Because each operation creates its own `blosc_job_group` on the caller's stack, workers from the same pool can interleave work for different contexts without interference: - Each worker reads `context->src`, `context->dest`, etc. from its own job's context pointer. - Mutable per-operation state (`output_bytes`, `giveup_code`, `next_block`) lives on the job group and is protected by `job->mutex`. - Worker scratch buffers (`tmp`, `tmp2`, `tmp3`) belong to the pool's `thread_context` array and are sized lazily via `ensure_thread_context_capacity()` (high-water-mark allocation). ## Key Design Decisions 1. **Queue-based, not barrier-based.** The old model used POSIX barriers (or emulated barriers) to synchronise context-owned threads. The shared-pool model uses a FIFO job queue with condition variables, allowing true concurrent submissions. 2. **Logical `tid` per queue entry.** Pool threads have persistent IDs, but these don't correspond 1:1 with job workers (any thread can pick up any entry). Each queue entry carries a `logical_tid` (0..N-1) that is used for static block partitioning and user-visible `preparams.tid` / `postparams.tid`. 3. **Refcount-driven pool lifetime.** Pools are created on first use and destroyed when the last referencing context detaches *and* the pool is idle. `blosc2_destroy()` acts as a final sweep for any pools that remain at shutdown. 4. **`g_initlib` guard.** Prevents use-after-free if a context outlives `blosc2_destroy()`. The context can still be freed (memory is released) but the pool detach is skipped. 5. **Consistent allocator.** All pool and queue-entry allocations use `my_malloc` / `my_free` (32-byte aligned) for consistency with the rest of Blosc's internal allocations. ## Files | File | Role | |------|------| | `blosc/blosc2.c` | Pool structs, registry, worker loop, `parallel_blosc()`, `check_nthreads()`, `attach/release/create/destroy_shared_pool()` | | `blosc/context.h` | `blosc2_context` fields (`thread_pool`, `thread_backend`, `job`), `thread_context` fields (`owner_pool`), backend constants, `ctx_uses_parallel_backend()` helper | | `blosc/schunk.c` | Uses `ctx_uses_parallel_backend()` to check if parallel backend is active | | `tests/test_shared_pool.c` | 10 tests: no-pool for nthreads=1, pool sharing, different-nthreads isolation, dynamic rebind, round-trips with shuffle/delta/bitshuffle, refcount destroy, serial delta, many-contexts sharing | c-blosc2-3.1.5/plans/shared-thread-pool-specs.md000066400000000000000000000354151521743436100213760ustar00rootroot00000000000000# Shared Thread Pool for C-Blosc2 ## Problem Statement C-Blosc2 currently uses a **per-context thread pool** model: every compression context (`cctx`) and decompression context (`dctx`) owns a private set of pthreads. Each SChunk carries one `cctx` and one `dctx`, so a single `blosc2.NDArray` in python-blosc2 creates **up to `2 × nthreads`** OS-level threads (e.g., 24 threads at `nthreads = 12` on a 12-core Mac). This design causes two concrete problems: ### 1. macOS thread-count ceiling (hang / crash) macOS enforces a hard per-process limit of **6 144 threads**. With the default `nthreads = 12`, only **~256 live arrays** are needed to exhaust this limit. Once the limit is hit, `pthread_create` fails for new contexts, and the process can hang or crash. During the python-blosc2 test suite (~8 400 tests), thousands of arrays accumulate because Python 3.14 changed the generation-2 GC threshold to 0, preventing automatic collection of long-lived objects. Pytest's final `gc.collect()` then tries to join thousands of pthreads at once, causing an indefinite hang. Related issue: ### 2. Resource waste for idle threads The vast majority of pthreads created by contexts sit idle in a barrier wait (`WAIT_INIT`) for their entire lifetime. A context's threads are created on the first operation (`check_nthreads` → `init_threadpool`) and only destroyed when the context is freed (`blosc2_free_ctx` → `release_threadpool` → `pthread_join`). Between operations, the threads do nothing but consume kernel resources (stack memory, thread-local storage, scheduler entries). ### Current workarounds in python-blosc2 1. **`with nogil: blosc2_schunk_free()`** — releasing the GIL during `SChunk.__dealloc__` prevents the GIL-deadlock aspect of the hang but does not prevent thread accumulation. 2. **Periodic `gc.collect()` every 50 tests** — prevents objects from accumulating to dangerous levels during the test suite. 3. **`ThreadPoolExecutor(max_workers=os.cpu_count())`** — caps the Python-level executor in `lazyexpr.py` (issue #556). These are palliatives. The root fix belongs in C-Blosc2. --- ## Proposed Solution: Global Shared Thread Pool Replace per-context thread ownership with a **single process-wide thread pool** that all contexts share. This is the approach used by OpenMP, Intel TBB, and Apple's Grand Central Dispatch. ### Design goals | Goal | Rationale | |------|-----------| | Bounded thread count | Never exceed `nthreads` OS threads regardless of context count | | Zero-cost idle contexts | Contexts without active work hold no thread resources | | API compatibility | `blosc2_create_cctx`, `blosc2_create_dctx`, `blosc2_free_ctx` signatures unchanged | | Minimal contention | Contexts that run concurrently share the pool without excessive locking | | Backward compatibility | `blosc2_set_threads_callback` continues to work for caller-managed threading | | Thread-safe | Multiple Python threads can compress/decompress through different contexts | ### Architecture overview ``` ┌─────────────────────────────────────────────────┐ │ Global Shared Pool │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ Worker 0 │ │ Worker 1 │ ... │Worker N-1│ │ │ └────┬────┘ └────┬────┘ └────┬────┘ │ │ │ │ │ │ │ └───────────┴──────────────────┘ │ │ │ │ │ ┌───────┴───────┐ │ │ │ Job Queue │ │ │ │ (lock-free │ │ │ │ or mutex) │ │ │ └───────────────┘ │ └─────────────────────────────────────────────────┘ ▲ ▲ ▲ │ │ │ ┌────┴───┐ ┌────┴───┐ ┌────┴───┐ │ cctx A │ │ dctx B │ │ cctx C │ │(submit)│ │(submit)│ │(submit)│ └────────┘ └────────┘ └────────┘ ``` --- ## Implementation Plan ### Phase 0: Preparatory refactor — isolate threading from `blosc2_context` **Goal:** Decouple the thread-pool fields from `blosc2_context` into a separate struct, making it mechanically possible to share. #### 0.1 Extract `blosc2_threadpool` struct Create a new struct in `blosc/context.h`: ```c typedef struct blosc2_threadpool_s { int16_t nthreads; /* requested worker count */ int16_t threads_started; /* actually running */ int16_t end_threads; /* shutdown flag */ blosc2_pthread_t *threads; /* thread handles */ struct thread_context *thread_contexts; /* per-thread scratch */ /* Synchronization */ blosc2_pthread_mutex_t count_mutex; blosc2_pthread_mutex_t nchunk_mutex; #ifdef BLOSC_POSIX_BARRIERS pthread_barrier_t barr_init; pthread_barrier_t barr_finish; #else int count_threads; blosc2_pthread_mutex_t count_threads_mutex; blosc2_pthread_cond_t count_threads_cv; #endif #if !defined(_WIN32) pthread_attr_t ct_attr; #endif /* Job dispatch (Phase 1+) */ blosc2_context *current_context; /* context being processed */ blosc2_pthread_mutex_t dispatch_mutex; blosc2_pthread_cond_t dispatch_cv; } blosc2_threadpool; ``` #### 0.2 Redirect context fields In `blosc2_context_s`, replace the thread-related fields (lines 88–112 of `blosc/context.h`) with a single pointer: ```c struct blosc2_context_s { /* ... existing non-thread fields ... */ /* Threading — either points to the global pool or a private pool */ blosc2_threadpool *pool; /* Fields that remain per-context (job state, not pool state) */ int thread_giveup_code; int thread_nblock; int dref_not_init; blosc2_pthread_mutex_t delta_mutex; blosc2_pthread_cond_t delta_cv; /* ... */ }; ``` #### 0.3 Adapt `init_threadpool` / `release_threadpool` Rewrite these to operate on `blosc2_threadpool *` instead of `blosc2_context *`. At this stage each context still allocates its own pool (behavior unchanged), but the interface is ready for sharing. #### 0.4 Validation All existing tests must pass. The refactor is purely structural; no behavioral change. --- ### Phase 1: Introduce the global pool #### 1.1 Global pool lifecycle Add to `blosc/blosc2.c`: ```c static blosc2_threadpool *g_shared_pool = NULL; static blosc2_pthread_mutex_t g_pool_mutex = PTHREAD_MUTEX_INITIALIZER; static int g_pool_refcount = 0; /* Called by blosc2_init() */ static void init_global_pool(int16_t nthreads); /* Called by blosc2_destroy() */ static void destroy_global_pool(void); ``` `init_global_pool` creates the pool with `g_nthreads` workers. `blosc2_set_nthreads` resizes the pool (joining excess threads or spawning new ones). #### 1.2 Context creation uses the global pool Modify `blosc2_create_cctx` / `blosc2_create_dctx` so that when `threads_callback == NULL` and the requested `nthreads` matches the global pool size, `context->pool` simply points to `g_shared_pool` and increments `g_pool_refcount`. No pthreads are created. If the caller requests a non-standard `nthreads` different from the global pool, fall back to allocating a private pool (preserving current behavior for specialized contexts). #### 1.3 `blosc2_free_ctx` skips pool destruction for shared contexts ```c void blosc2_free_ctx(blosc2_context *context) { if (context->pool == g_shared_pool) { /* Shared pool: just decrement refcount, don't destroy */ pthread_mutex_lock(&g_pool_mutex); g_pool_refcount--; pthread_mutex_unlock(&g_pool_mutex); } else if (context->pool != NULL) { release_threadpool(context->pool); free(context->pool); } /* ... rest of cleanup ... */ } ``` This single change eliminates per-context `pthread_join` during finalization, which is the direct cause of the python-blosc2 hang. --- ### Phase 2: Job-queue dispatch Phase 1 shares the threads but still needs a way to dispatch work. The current barrier-based model assumes threads are bound to one context. With a shared pool, we need a proper job-dispatch mechanism. #### 2.1 Job submission API ```c typedef struct { blosc2_context *context; struct thread_context *tcontext; /* completion tracking */ blosc2_pthread_mutex_t *done_mutex; blosc2_pthread_cond_t *done_cv; int *blocks_remaining; } blosc2_job; /* Submit a set of block-processing jobs and wait for completion */ static int pool_submit_and_wait(blosc2_threadpool *pool, blosc2_context *context); ``` #### 2.2 Worker loop rewrite Replace the current `t_blosc` barrier loop with a job-queue consumer: ```c static void* t_blosc_pooled(void *arg) { blosc2_threadpool *pool = (blosc2_threadpool *)arg; while (1) { pthread_mutex_lock(&pool->dispatch_mutex); while (pool->current_context == NULL && !pool->end_threads) { pthread_cond_wait(&pool->dispatch_cv, &pool->dispatch_mutex); } if (pool->end_threads) { pthread_mutex_unlock(&pool->dispatch_mutex); break; } blosc2_context *ctx = pool->current_context; pthread_mutex_unlock(&pool->dispatch_mutex); /* Process blocks from ctx->thread_nblock atomically */ t_blosc_do_job(/* thread_context for this worker */); /* Signal completion */ /* ... */ } return NULL; } ``` #### 2.3 Concurrent context support For Phase 2 the simplest approach is **serialized context dispatch**: only one context can use the pool at a time, with a dispatch mutex ensuring mutual exclusion. This matches the current model where `parallel_blosc` is called from a single thread per context. A future Phase 3 could add fine-grained work-stealing for true concurrent multi-context parallelism, but this is not required for the initial fix. #### 2.4 `thread_context` scratch buffers Currently each `thread_context` allocates scratch buffers (`tmp`, `tmp2`, `tmp3`, `tmp4`) sized for the context's blocksize. With a shared pool, these buffers must be re-allocated when a different context is dispatched (if the blocksize differs). Strategy: lazily resize — keep the largest allocation and only reallocate when a larger blocksize is needed. Track `tmp_blocksize` to detect when reallocation is necessary (this check already exists in `do_job` for the serial path). --- ### Phase 3 (future): Advanced features These are not required for the initial fix but would further improve the architecture. #### 3.1 Work-stealing for concurrent contexts Allow multiple contexts to submit jobs simultaneously. Each context gets its own job queue; idle workers steal from other contexts' queues. This would benefit python-blosc2's `ThreadPoolExecutor` + async-read pattern where multiple arrays decompress concurrently. #### 3.2 Adaptive pool sizing Monitor work submission rates and resize the pool dynamically (e.g., shrink when idle for >100ms, grow up to `nthreads` when jobs arrive). #### 3.3 `blosc2_set_threads_callback` integration The existing callback mechanism (`threads_callback`) already provides an external thread dispatch interface. With a shared pool, the callback path needs minor adaptation: instead of operating on per-context `thread_contexts`, it operates on pool-level workers. --- ## Migration Path & Compatibility ### C API compatibility | Function | Change | |----------|--------| | `blosc2_create_cctx` | Internal only: assigns `g_shared_pool` instead of creating threads | | `blosc2_create_dctx` | Same | | `blosc2_free_ctx` | Internal only: decrements refcount instead of `pthread_join` | | `blosc2_set_nthreads` | Resizes the global pool | | `blosc2_compress` | Unchanged (uses global context → shared pool) | | `blosc2_decompress` | Unchanged | | `blosc2_set_threads_callback` | Works as before; bypasses global pool | No public API signatures change. The ABI remains stable because the `blosc2_context` struct is opaque to callers (only accessed through API functions). ### python-blosc2 changes Once C-Blosc2 ships the shared pool: 1. Remove the periodic `gc.collect()` from `tests/conftest.py` (no longer needed — freeing a context no longer joins threads). 2. Keep the `with nogil: blosc2_schunk_free()` fix (still good practice). 3. Remove `ThreadPoolExecutor(max_workers=...)` cap in `lazyexpr.py` if desired (the Python-level thread pressure is a separate concern but becomes less critical when C-level threads are bounded). --- ## Impact Assessment ### Thread count reduction | Scenario | Current | With shared pool | |----------|---------|-----------------| | 1 array, nthreads=12 | 24 threads | 12 threads | | 100 arrays, nthreads=12 | 2 400 threads | 12 threads | | 1 000 arrays, nthreads=12 | **6 144** (limit!) | 12 threads | | Test suite (8 400 tests) | **6 144** (hang) | 12 threads | ### Performance - **No regression expected** for sequential workloads (same number of workers, same barrier/block processing). - **Slight improvement** for context creation/destruction (no `pthread_create` / `pthread_join`). - **Memory savings** — each idle thread stack is typically 512 KB–8 MB; eliminating thousands of idle threads saves gigabytes of virtual memory. ### Risks 1. **Thread-context scratch buffers** need resizing when blocksize changes between contexts. Mitigation: lazy resize with high-water-mark allocation. 2. **Concurrent context dispatch** is serialized in Phase 2, which could bottleneck workloads that need true parallelism across contexts. Mitigation: Phase 3 adds work-stealing. 3. **`threads_callback` users** need testing to ensure compatibility. --- ## References - Python-blosc2 issue #556: [Unbounded thread growth during pytest](https://github.com/Blosc/python-blosc2/issues/556) - C-Blosc2 `context.h` struct definition: `blosc/context.h:36–114` - Thread lifecycle: `blosc/blosc2.c` — `init_threadpool` (line 4583), `release_threadpool` (line 4992), `t_blosc` worker (line 4555), `blosc2_free_ctx` (line 5304) - Synchronization: `WAIT_INIT` / `WAIT_FINISH` macros (lines 108–160) - `blosc2_set_threads_callback` public API: `include/blosc2.h:734–747` c-blosc2-3.1.5/plugins/000077500000000000000000000000001521743436100146135ustar00rootroot00000000000000c-blosc2-3.1.5/plugins/CMakeLists.txt000066400000000000000000000012061521743436100173520ustar00rootroot00000000000000# Blosc - Blocked Shuffling and Compression Library # # Copyright (c) 2021 Blosc Development Team # https://blosc.org # License: BSD 3-Clause (see LICENSE.txt) # # See LICENSE.txt for details about copyright and rights to use. add_subdirectory(codecs) if(DEFINED ZFP_FETCHED) set(ZFP_FETCHED ${ZFP_FETCHED} PARENT_SCOPE) endif() if(DEFINED ZFP_SOURCE_DIR) set(ZFP_SOURCE_DIR ${ZFP_SOURCE_DIR} PARENT_SCOPE) endif() if(DEFINED ZFP_INSTALL_INCLUDEDIR) set(ZFP_INSTALL_INCLUDEDIR ${ZFP_INSTALL_INCLUDEDIR} PARENT_SCOPE) endif() add_subdirectory(tuners) add_subdirectory(filters) set(SOURCES ${SOURCES} PARENT_SCOPE) c-blosc2-3.1.5/plugins/README.md000066400000000000000000000147241521743436100161020ustar00rootroot00000000000000Plugins registry for Blosc users ================================ Blosc has a tradition of supporting different filters and codecs for compressing data, and it has been up to the user to choose one or another depending on their needs. However, it is clear that there are always scenarios where a richer variety of them could be useful. Therefore, the Blosc team has set new goals: 1) Implement a way for users to register filters and codecs locally so that they can use them in their setup as needed. 2) Set up a central registry so that other users can use these filters and codecs without interfering with filters and codecs created by other users. As a bonus, codecs and filters that are accepted into the central registry and meet the quality standards defined in these guidelines will be distributed *within* the C-Blosc2 library. This allows for a much easier way for others to use them; simply install the C-Blosc2 library and you're all set. Of course, to achieve such a status, plugins will require a careful testing process as described below. Plugin types -------------- The plugins that are registered in the repository can be codecs or filters. A codec is a program that compresses and decompresses digital data streams with the objective of reducing dataset size to enable faster transmission of data. Blosc uses various codecs, including BLOSCLZ, LZ4, and ZSTANDARD. A filter is a program that rearranges data without changing its size, so that the initial and final sizes remain equal. A filter consists of an encoder and decoder. The filter encoder is applied before using the codec compressor (or codec encoder) to make the data easier to compress, while the filter decoder is used after the codec decompressor (or codec decoder) to restore the original data arrangement. Some filters used by Blosc include SHUFFLE, which rearranges data based on the type size, and TRUNC, which zeroes the mantissa bits to reduce the precision of (floating point) data and increase the compression ratio. Here is an example on how the compression process goes: -------------------- filter encoder ------------------- codec encoder ------- | src | -----------> | tmp | ----------> | c_src | -------------------- ------------------- ------- And the decompression process: -------- codec decoder ------------------- filter decoder ------------------- | c_src | -----------> | tmp | ----------> | src | -------- ------------------- ------------------- Furthermore, during the pipeline process, you can use up to six different filters, ordered in any way you prefer. Blosc global registered plugins vs user registered plugins ---------------------------------------------------------- **Blosc global registered plugins** are official Blosc plugins that have passed through a selection process and have been recognised by the Blosc Development Team. These plugins are available for everybody in the C-Blosc2 GitHub repository and users can install them anytime. **User registered plugins** are plugins that users register locally, and they can use them in the same way as in the examples `urcodecs.c` and `urfilters.c`. The codec must have .encoder and .decoder functions. If you only want to use a plugin on your own devices you can just register it as a user registered plugin with an ID between *BLOSC2_USER_REGISTERED_FILTERS_START* and *BLOSC2_USER_REGISTERED_FILTERS_STOP*. Otherwise, if you think that your plugin could be useful for the community you can apply for registering it as an official Blosc plugin following the next steps. Requirements for registering plugins ------------------------------------ For users wanting to register a new codec or filter, there are some requirements that their code must satisfy: - First, the plugin code must be **developed in C**, have a relatively small footprint and meet decent quality code standards. - Second, users must develop a test suite that proves that the plugin works correctly. Finally, even if these requirements are completely satisfied, it is not guaranteed that the plugin will be useful or contribute something different from the existing ones, so the Blosc development team has the final say and will decide if a plugin is to be accepted or not. Steps ----- 1. First, tests must be provided and be passing. **It is completely mandatory and necessary to add these lines to `main()` in each test to make plugins machinery work:** - `blosc2_init()` at the beginning - `blosc2_destroy()` in the end 2. Then, the user must make a fork of the C-Blosc2 GitHub repository, adding a new folder within the plugin sources to the path `plugins/codecs` or `plugins/filters` depending on the plugin type. 3. Furthermore, a text file named `README.rst` must be provided where it is explained: * The plugin motivation, why and for what purpose was the plugin created. * How to use the plugin. * What does the plugin do and how it works. * The advantages and disadvantages of the plugin compared to the rest. 4. To register a plugin the user must choose a plugin ID between *BLOSC2_GLOBAL_REGISTERED_FILTERS_START* and *BLOSC2_GLOBAL_REGISTERED_FILTERS_STOP* and write it at `include/blosc2/codecs-registry.h` or `include/blosc2/filters-registry.h` depending on the plugin type. Then, you have to edit `plugins/codecs/codecs-registry.c`or `plugins/codecs/filters-registry.c` in the next way: At the top it must be added `#include "plugin_folder/plugin_header.h"`, and into the register function you must follow the same steps that were done for the existing plugins. 5. Finally, the Blosc development team will carry out the evaluation process to decide whether the plugin is useful and hence, candidate to be integrated into the C-Blosc2 source code distribution. In case of a negative decision, the original author will be informed, together with a series of advice for starting a new iteration if desired. Examples -------- In the `plugins/` directory there can be found different examples of codecs and filters available as plugins that can be used in the compression process, and that can be used as an example on how to implement plugins that can make into C-Blosc2. Some of these are `ndlz`, `ndcell`, `ndmean` or `bytedelta`. Thanks ------ We would like to express our gratitude to the NumFOCUS Foundation for providing the funds to implement this functionality. c-blosc2-3.1.5/plugins/codecs/000077500000000000000000000000001521743436100160535ustar00rootroot00000000000000c-blosc2-3.1.5/plugins/codecs/CMakeLists.txt000066400000000000000000000206621521743436100206210ustar00rootroot00000000000000add_subdirectory(ndlz) if(HAVE_ZFP) if(BLOSC_ZFP_PROVIDER STREQUAL "BUNDLED") include(FetchContent) # Configure the upstream ZFP subproject when brought in via FetchContent. # These are upstream ZFP options, not C-Blosc2 plugin options. Keep the # embedded ZFP build minimal (library only), static/PIC-friendly, and free of # extra components like tests, examples, Python/Fortran bindings, utilities, # or OpenMP support. # # Use normal variables (not CACHE entries) so these settings stay local to the # embedded ZFP configure step and do not pollute the parent cache or collide # with other subprojects using similarly generic option names. # ZFP declares these via option(), so also force CMP0077=NEW for the fetched # subproject through CMAKE_POLICY_DEFAULT_CMP0077; otherwise CMake may emit dev # warnings and let option() clear the normal variables. set(BLOSC_SAVED_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS}) set(BLOSC_HAD_BUILD_UTILITIES FALSE) if(DEFINED BUILD_UTILITIES) set(BLOSC_HAD_BUILD_UTILITIES TRUE) set(BLOSC_SAVED_BUILD_UTILITIES ${BUILD_UTILITIES}) endif() set(BLOSC_HAD_BUILD_EXAMPLES FALSE) if(DEFINED BUILD_EXAMPLES) set(BLOSC_HAD_BUILD_EXAMPLES TRUE) set(BLOSC_SAVED_BUILD_EXAMPLES ${BUILD_EXAMPLES}) endif() set(BLOSC_HAD_BUILD_CFP FALSE) if(DEFINED BUILD_CFP) set(BLOSC_HAD_BUILD_CFP TRUE) set(BLOSC_SAVED_BUILD_CFP ${BUILD_CFP}) endif() set(BLOSC_HAD_BUILD_ZFORP FALSE) if(DEFINED BUILD_ZFORP) set(BLOSC_HAD_BUILD_ZFORP TRUE) set(BLOSC_SAVED_BUILD_ZFORP ${BUILD_ZFORP}) endif() set(BLOSC_HAD_BUILD_ZFPY FALSE) if(DEFINED BUILD_ZFPY) set(BLOSC_HAD_BUILD_ZFPY TRUE) set(BLOSC_SAVED_BUILD_ZFPY ${BUILD_ZFPY}) endif() set(BLOSC_HAD_BUILD_TESTING FALSE) if(DEFINED BUILD_TESTING) set(BLOSC_HAD_BUILD_TESTING TRUE) set(BLOSC_SAVED_BUILD_TESTING ${BUILD_TESTING}) endif() set(BLOSC_HAD_ZFP_WITH_OPENMP FALSE) if(DEFINED ZFP_WITH_OPENMP) set(BLOSC_HAD_ZFP_WITH_OPENMP TRUE) set(BLOSC_SAVED_ZFP_WITH_OPENMP ${ZFP_WITH_OPENMP}) endif() set(BLOSC_HAD_ZFP_ENABLE_PIC FALSE) if(DEFINED ZFP_ENABLE_PIC) set(BLOSC_HAD_ZFP_ENABLE_PIC TRUE) set(BLOSC_SAVED_ZFP_ENABLE_PIC ${ZFP_ENABLE_PIC}) endif() set(BLOSC_HAD_CMAKE_POLICY_DEFAULT_CMP0077 FALSE) if(DEFINED CMAKE_POLICY_DEFAULT_CMP0077) set(BLOSC_HAD_CMAKE_POLICY_DEFAULT_CMP0077 TRUE) set(BLOSC_SAVED_CMAKE_POLICY_DEFAULT_CMP0077 ${CMAKE_POLICY_DEFAULT_CMP0077}) endif() set(BLOSC_HAD_CMAKE_WARN_DEPRECATED FALSE) if(DEFINED CMAKE_WARN_DEPRECATED) set(BLOSC_HAD_CMAKE_WARN_DEPRECATED TRUE) set(BLOSC_SAVED_CMAKE_WARN_DEPRECATED ${CMAKE_WARN_DEPRECATED}) endif() set(BLOSC_HAD_CMAKE_INSTALL_BINDIR FALSE) if(DEFINED CMAKE_INSTALL_BINDIR) set(BLOSC_HAD_CMAKE_INSTALL_BINDIR TRUE) set(BLOSC_SAVED_CMAKE_INSTALL_BINDIR ${CMAKE_INSTALL_BINDIR}) endif() set(BLOSC_HAD_CMAKE_INSTALL_LIBDIR FALSE) if(DEFINED CMAKE_INSTALL_LIBDIR) set(BLOSC_HAD_CMAKE_INSTALL_LIBDIR TRUE) set(BLOSC_SAVED_CMAKE_INSTALL_LIBDIR ${CMAKE_INSTALL_LIBDIR}) endif() set(BLOSC_HAD_CMAKE_INSTALL_INCLUDEDIR FALSE) if(DEFINED CMAKE_INSTALL_INCLUDEDIR) set(BLOSC_HAD_CMAKE_INSTALL_INCLUDEDIR TRUE) set(BLOSC_SAVED_CMAKE_INSTALL_INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR}) endif() set(BUILD_SHARED_LIBS OFF) set(BUILD_UTILITIES OFF) set(BUILD_EXAMPLES OFF) set(BUILD_CFP OFF) set(BUILD_ZFORP OFF) set(BUILD_ZFPY OFF) set(BUILD_TESTING OFF) set(ZFP_WITH_OPENMP OFF) set(ZFP_ENABLE_PIC ON) set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) set(CMAKE_WARN_DEPRECATED FALSE) # zfp derives build output directories from CMAKE_INSTALL_*DIR. Keep these # relative while configuring the fetched subproject so an absolute parent # install layout (e.g. wheel staging on Windows) does not leak into zfp's # archive/library output paths. set(CMAKE_INSTALL_BINDIR bin) set(CMAKE_INSTALL_LIBDIR lib) set(CMAKE_INSTALL_INCLUDEDIR include) if(BLOSC_ZFP_SOURCE_DIR) message(STATUS "Using zfp via FetchContent from local checkout: ${BLOSC_ZFP_SOURCE_DIR}") FetchContent_Declare( zfp SOURCE_DIR "${BLOSC_ZFP_SOURCE_DIR}") else() message(STATUS "Using zfp ${BLOSC_ZFP_VERSION} via FetchContent download") FetchContent_Declare( zfp URL "https://github.com/LLNL/zfp/archive/refs/tags/${BLOSC_ZFP_VERSION}.tar.gz" DOWNLOAD_EXTRACT_TIMESTAMP TRUE) endif() FetchContent_GetProperties(zfp) if(NOT zfp_POPULATED) if(POLICY CMP0169) cmake_policy(PUSH) cmake_policy(SET CMP0169 OLD) endif() FetchContent_Populate(zfp) if(POLICY CMP0169) cmake_policy(POP) endif() add_subdirectory("${zfp_SOURCE_DIR}" "${zfp_BINARY_DIR}" EXCLUDE_FROM_ALL) endif() set(BUILD_SHARED_LIBS ${BLOSC_SAVED_BUILD_SHARED_LIBS}) if(BLOSC_HAD_BUILD_UTILITIES) set(BUILD_UTILITIES ${BLOSC_SAVED_BUILD_UTILITIES}) else() unset(BUILD_UTILITIES) endif() if(BLOSC_HAD_BUILD_EXAMPLES) set(BUILD_EXAMPLES ${BLOSC_SAVED_BUILD_EXAMPLES}) else() unset(BUILD_EXAMPLES) endif() if(BLOSC_HAD_BUILD_CFP) set(BUILD_CFP ${BLOSC_SAVED_BUILD_CFP}) else() unset(BUILD_CFP) endif() if(BLOSC_HAD_BUILD_ZFORP) set(BUILD_ZFORP ${BLOSC_SAVED_BUILD_ZFORP}) else() unset(BUILD_ZFORP) endif() if(BLOSC_HAD_BUILD_ZFPY) set(BUILD_ZFPY ${BLOSC_SAVED_BUILD_ZFPY}) else() unset(BUILD_ZFPY) endif() if(BLOSC_HAD_BUILD_TESTING) set(BUILD_TESTING ${BLOSC_SAVED_BUILD_TESTING}) else() unset(BUILD_TESTING) endif() if(BLOSC_HAD_ZFP_WITH_OPENMP) set(ZFP_WITH_OPENMP ${BLOSC_SAVED_ZFP_WITH_OPENMP}) else() unset(ZFP_WITH_OPENMP) endif() if(BLOSC_HAD_ZFP_ENABLE_PIC) set(ZFP_ENABLE_PIC ${BLOSC_SAVED_ZFP_ENABLE_PIC}) else() unset(ZFP_ENABLE_PIC) endif() if(BLOSC_HAD_CMAKE_POLICY_DEFAULT_CMP0077) set(CMAKE_POLICY_DEFAULT_CMP0077 ${BLOSC_SAVED_CMAKE_POLICY_DEFAULT_CMP0077}) else() unset(CMAKE_POLICY_DEFAULT_CMP0077) endif() if(BLOSC_HAD_CMAKE_WARN_DEPRECATED) set(CMAKE_WARN_DEPRECATED ${BLOSC_SAVED_CMAKE_WARN_DEPRECATED}) else() unset(CMAKE_WARN_DEPRECATED) endif() if(BLOSC_HAD_CMAKE_INSTALL_BINDIR) set(CMAKE_INSTALL_BINDIR ${BLOSC_SAVED_CMAKE_INSTALL_BINDIR}) else() unset(CMAKE_INSTALL_BINDIR) endif() if(BLOSC_HAD_CMAKE_INSTALL_LIBDIR) set(CMAKE_INSTALL_LIBDIR ${BLOSC_SAVED_CMAKE_INSTALL_LIBDIR}) else() unset(CMAKE_INSTALL_LIBDIR) endif() if(BLOSC_HAD_CMAKE_INSTALL_INCLUDEDIR) set(CMAKE_INSTALL_INCLUDEDIR ${BLOSC_SAVED_CMAKE_INSTALL_INCLUDEDIR}) else() unset(CMAKE_INSTALL_INCLUDEDIR) endif() set(ZFP_FETCHED TRUE PARENT_SCOPE) set(ZFP_SOURCE_DIR "${zfp_SOURCE_DIR}" PARENT_SCOPE) set(ZFP_INSTALL_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}/blosc2/thirdparty/zfp" PARENT_SCOPE) if(TARGET zfp) set_target_properties(zfp PROPERTIES POSITION_INDEPENDENT_CODE ON) set_property(TARGET zfp PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$" "$") endif() endif() add_subdirectory(zfp) endif() set(SOURCES ${SOURCES} ${PROJECT_SOURCE_DIR}/plugins/codecs/codecs-registry.c PARENT_SCOPE) c-blosc2-3.1.5/plugins/codecs/codecs-registry.c000066400000000000000000000056661521743436100213420ustar00rootroot00000000000000/* Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) */ #include "blosc2/codecs-registry.h" #include "ndlz/ndlz.h" #if defined(USING_CMAKE) #include "config.h" #endif #if defined(HAVE_ZFP) #include "zfp/blosc2-zfp.h" #endif #include "blosc-private.h" #include "blosc2.h" void register_codecs(void) { // May add .free func in future blosc2_codec ndlz; ndlz.compcode = BLOSC_CODEC_NDLZ; ndlz.version = 1; ndlz.complib = BLOSC_CODEC_NDLZ; ndlz.encoder = &ndlz_compress; ndlz.decoder = &ndlz_decompress; ndlz.compname = "ndlz"; // ndlz.free = NULL; register_codec_private(&ndlz); #if defined(HAVE_ZFP) blosc2_codec zfp_acc; zfp_acc.compcode = BLOSC_CODEC_ZFP_FIXED_ACCURACY; zfp_acc.version = 1; zfp_acc.complib = BLOSC_CODEC_ZFP_FIXED_ACCURACY; zfp_acc.encoder = &zfp_acc_compress; zfp_acc.decoder = &zfp_acc_decompress; // zfp_acc.free = NULL; zfp_acc.compname = "zfp_acc"; register_codec_private(&zfp_acc); blosc2_codec zfp_prec; zfp_prec.compcode = BLOSC_CODEC_ZFP_FIXED_PRECISION; zfp_prec.version = 1; zfp_prec.complib = BLOSC_CODEC_ZFP_FIXED_PRECISION; zfp_prec.encoder = &zfp_prec_compress; zfp_prec.decoder = &zfp_prec_decompress; // zfp_prec.free = NULL; zfp_prec.compname = "zfp_prec"; register_codec_private(&zfp_prec); blosc2_codec zfp_rate; zfp_rate.compcode = BLOSC_CODEC_ZFP_FIXED_RATE; zfp_rate.version = 1; zfp_rate.complib = BLOSC_CODEC_ZFP_FIXED_RATE; zfp_rate.encoder = &zfp_rate_compress; zfp_rate.decoder = &zfp_rate_decompress; zfp_rate.compname = "zfp_rate"; // zfp_rate.free = NULL; register_codec_private(&zfp_rate); #endif blosc2_codec openhtj2k; openhtj2k.compcode = BLOSC_CODEC_OPENHTJ2K; openhtj2k.version = 1; openhtj2k.complib = BLOSC_CODEC_OPENHTJ2K; openhtj2k.encoder = NULL; openhtj2k.decoder = NULL; // openhtj2k.free = NULL; openhtj2k.compname = "openhtj2k"; register_codec_private(&openhtj2k); blosc2_codec grok; grok.compcode = BLOSC_CODEC_GROK; grok.version = 1; grok.complib = BLOSC_CODEC_GROK; grok.encoder = NULL; grok.decoder = NULL; // grok.free = NULL; grok.compname = "grok"; register_codec_private(&grok); blosc2_codec openzl; openzl.compcode = BLOSC_CODEC_OPENZL; openzl.version = 1; openzl.complib = BLOSC_CODEC_OPENZL; openzl.encoder = NULL; openzl.decoder = NULL; // openzl.free = NULL; openzl.compname = "openzl"; register_codec_private(&openzl); blosc2_codec j2k; j2k.compcode = BLOSC_CODEC_J2K; j2k.version = 1; j2k.complib = BLOSC_CODEC_J2K; j2k.encoder = NULL; j2k.decoder = NULL; // j2k.free = NULL; j2k.compname = "j2k"; register_codec_private(&j2k); blosc2_codec htj2k; htj2k.compcode = BLOSC_CODEC_HTJ2K; htj2k.version = 1; htj2k.complib = BLOSC_CODEC_HTJ2K; htj2k.encoder = NULL; htj2k.decoder = NULL; // htj2k.free = NULL; htj2k.compname = "htj2k"; register_codec_private(&htj2k); } c-blosc2-3.1.5/plugins/codecs/ndlz/000077500000000000000000000000001521743436100170225ustar00rootroot00000000000000c-blosc2-3.1.5/plugins/codecs/ndlz/CMakeLists.txt000066400000000000000000000022671521743436100215710ustar00rootroot00000000000000# Blosc - Blocked Shuffling and Compression Library # # Copyright (c) 2021 Blosc Development Team # https://blosc.org # License: BSD 3-Clause (see LICENSE.txt) # # See LICENSE.txt for details about copyright and rights to use. # sources set(SOURCES ${SOURCES} ${PROJECT_SOURCE_DIR}/plugins/codecs/ndlz/ndlz.c ${PROJECT_SOURCE_DIR}/plugins/codecs/ndlz/ndlz4x4.c ${PROJECT_SOURCE_DIR}/plugins/codecs/ndlz/ndlz8x8.c ${PROJECT_SOURCE_DIR}/plugins/codecs/ndlz/xxhash.c PARENT_SCOPE) # targets if(BUILD_TESTS) add_executable(test_ndlz test_ndlz.c) # Define the BLOSC_TESTING symbol so normally-hidden functions # aren't hidden from the view of the test programs. target_compile_definitions(test_ndlz PUBLIC BLOSC_TESTING) target_link_libraries(test_ndlz PUBLIC blosc_testing) # tests add_test(NAME test_plugin_test_ndlz COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} $) # Copy test files file(GLOB TESTS_DATA ../../test_data/example_s*.b2nd) foreach (data ${TESTS_DATA}) file(COPY ${data} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/) endforeach(data) endif() c-blosc2-3.1.5/plugins/codecs/ndlz/README.md000066400000000000000000000063121521743436100203030ustar00rootroot00000000000000NDLZ: a multidimensional lossless codec ============================================================================= Given a 2-dim array or matrix, *NDLZ* is a compressor based on the Lempel-Ziv algorithm of lossless data compression. Plugin motivation -------------------- *NDLZ* was created in order to search for patterns repetitions in multidimensional cells using a multidimensional blocking machinery. Plugin usage ------------------- The codec consists of an encoder called *ndlz_compress()* to codify data and a decoder called *ndlz_decompress()* to recover the original data. The parameters used by *NDLZ* are the ones specified in the *blosc2_codec* structure of *blosc2.h*. Furthermore, since *NDLZ* goes through dataset blocks dividing them into fixed size cells, user must specify the parameter meta as 4 to use cells of size 4x4 or 8 to use 8x8 cells. If user tries to use other value for meta, the codec will return an error value. NDLZ only works for 2-dim datasets of 1 byte items (typesize = 1), so if you want to use it for a dataset with bigger typesize then you must activate SHUFFLE filter and splitting mode. Plugin behaviour ------------------- This codec is meant to leverage multidimensionality for getting better compression ratios. The idea is to look for similarities in places that are closer in a euclidean metric, not the typical linear one. First *NDLZ* goes through dataset blocks dividing them into fixed size cells. Then, for each cell the codec searches for data coincidences with previous cells in order to copy only references to those cells instead of copying the full current cell. To understand how the compressor and decompressor work it is important to learn about the compressed block format. An *NDLZ* compressed block is composed of a not-compressed byte called token and some 2 bytes values called offsets. The token is divided in two fields. The first field is composed of the 2 first bits of the token and gives important information about the cells and rows couples matches. The high-bit of the field is activated when there exists repeated information (there are matches) and there exists offset. If it is activated, the other field indicates special patterns of matches, and if not we have to look at the second bit. If it is activated (token = 01000000), this means that the whole cell is composed of the same element, and if not (token = 00000000) there is not repeated information and the whole cell is literally copied. The offsets are references to previous literal copies that match with the data that is being evaluated at the moment. Otherwise, it is important to know that there exist different hash tables which store the references to the literal copies of cells and rows in their hash position. Advantages and disadvantages ------------------------------ The main advantage of *NDLZ* when compared with most of the codecs is that this one considers dataset multidimensionality and takes advantage of it instead of processing all data as serial. The main disadvantage of *NDLZ* is that it is only useful for 2-dim datasets and at the moment it gets worse results (times and ratios) than other, more developed codecs that do not consider multidimensionality, at least in our limited testing. c-blosc2-3.1.5/plugins/codecs/ndlz/ndlz-private.h000066400000000000000000000012721521743436100216140ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #ifndef NDLZ_PRIVATE_H #define NDLZ_PRIVATE_H #include #define XXH_INLINE_ALL #define NDLZ_ERROR_NULL(pointer) \ do { \ if ((pointer) == NULL) { \ return 0; \ } \ } while (0) #endif /* NDLZ_PRIVATE_H */ c-blosc2-3.1.5/plugins/codecs/ndlz/ndlz.c000066400000000000000000000040401521743436100201330ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /********************************************************************* This codec is meant to leverage multidimensionality for getting better compression ratios. The idea is to look for similarities in places that are closer in a euclidean metric, not the typical linear one. **********************************************************************/ #include "ndlz-private.h" #include "ndlz4x4.h" #include "ndlz8x8.h" #include "ndlz.h" int ndlz_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int32_t output_len, uint8_t meta, blosc2_cparams *cparams, const void *chunk) { NDLZ_ERROR_NULL(input); NDLZ_ERROR_NULL(output); NDLZ_ERROR_NULL(cparams); BLOSC_UNUSED_PARAM(chunk); switch (meta) { case 4: return ndlz4_compress(input, input_len, output, output_len, meta, cparams); case 8: return ndlz8_compress(input, input_len, output, output_len, meta, cparams); default: BLOSC_TRACE_ERROR("NDLZ is not available for this cellsize: %d", meta); } return BLOSC2_ERROR_FAILURE; } int ndlz_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, int32_t output_len, uint8_t meta, blosc2_dparams *dparams, const void *chunk) { NDLZ_ERROR_NULL(input); NDLZ_ERROR_NULL(output); NDLZ_ERROR_NULL(dparams); BLOSC_UNUSED_PARAM(chunk); switch (meta) { case 4: return ndlz4_decompress(input, input_len, output, output_len, meta, dparams); case 8: return ndlz8_decompress(input, input_len, output, output_len, meta, dparams); default: BLOSC_TRACE_ERROR("NDLZ is not available for this cellsize: %d", meta); } return BLOSC2_ERROR_FAILURE; } c-blosc2-3.1.5/plugins/codecs/ndlz/ndlz.h000066400000000000000000000016171521743436100201470ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #ifndef BLOSC_PLUGINS_CODECS_NDLZ_NDLZ_H #define BLOSC_PLUGINS_CODECS_NDLZ_NDLZ_H #include "blosc2.h" #include int ndlz_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int32_t output_len, uint8_t meta, blosc2_cparams *cparams, const void* chunk); int ndlz_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, int32_t output_len, uint8_t meta, blosc2_dparams *dparams, const void* chunk); #endif /* BLOSC_PLUGINS_CODECS_NDLZ_NDLZ_H */ c-blosc2-3.1.5/plugins/codecs/ndlz/ndlz4x4.c000066400000000000000000000653401521743436100205050ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /********************************************************************* This codec is meant to leverage multidimensionality for getting better compression ratios. The idea is to look for similarities in places that are closer in a euclidean metric, not the typical linear one. **********************************************************************/ #include "ndlz4x4.h" #include "xxhash.h" #include "b2nd.h" #include #include /* * Give hints to the compiler for branch prediction optimization. */ #if defined(__GNUC__) && (__GNUC__ > 2) #define NDLZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1)) #define NDLZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0)) #else #define NDLZ_EXPECT_CONDITIONAL(c) (c) #define NDLZ_UNEXPECT_CONDITIONAL(c) (c) #endif /* * Use inlined functions for supported systems. */ #if defined(_MSC_VER) && !defined(__cplusplus) /* Visual Studio */ #define inline __inline /* Visual C is not C99, but supports some kind of inline */ #endif #define MAX_COPY 32U #define MAX_DISTANCE 65535 #ifdef BLOSC_STRICT_ALIGN #define NDLZ_READU16(p) ((p)[0] | (p)[1]<<8) #define NDLZ_READU32(p) ((p)[0] | (p)[1]<<8 | (p)[2]<<16 | (p)[3]<<24) #else #define NDLZ_READU16(p) *((const uint16_t*)(p)) #define NDLZ_READU32(p) *((const uint32_t*)(p)) #endif #define HASH_LOG (12) int ndlz4_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int32_t output_len, uint8_t meta, blosc2_cparams *cparams) { BLOSC_UNUSED_PARAM(meta); BLOSC_ERROR_NULL(cparams, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(cparams->schunk, BLOSC2_ERROR_NULL_POINTER); uint8_t *smeta; int32_t smeta_len; if (blosc2_meta_get(cparams->schunk, "b2nd", &smeta, &smeta_len) < 0) { BLOSC_TRACE_ERROR("b2nd layer not found!"); return BLOSC2_ERROR_FAILURE; } int8_t ndim; int64_t *shape = malloc(B2ND_MAX_DIM * sizeof(int64_t)); int32_t *chunkshape = malloc(B2ND_MAX_DIM * sizeof(int32_t)); int32_t *blockshape = malloc(B2ND_MAX_DIM * sizeof(int32_t)); int deserialize_rc = b2nd_deserialize_meta(smeta, smeta_len, &ndim, shape, chunkshape, blockshape, NULL, NULL); free(smeta); if (deserialize_rc < 0 || ndim != 2) { free(shape); free(chunkshape); free(blockshape); BLOSC_TRACE_ERROR("This codec only works for ndim = 2"); return BLOSC2_ERROR_FAILURE; } if (input_len != (blockshape[0] * blockshape[1])) { free(shape); free(chunkshape); free(blockshape); BLOSC_TRACE_ERROR("Length not equal to blocksize"); return BLOSC2_ERROR_FAILURE; } if (NDLZ_UNEXPECT_CONDITIONAL(output_len < (int) (1 + ndim * sizeof(int32_t)))) { free(shape); free(chunkshape); free(blockshape); BLOSC_TRACE_ERROR("Output too small"); return BLOSC2_ERROR_FAILURE; } uint8_t *ip = (uint8_t *) input; uint8_t *op = (uint8_t *) output; uint8_t *op_limit; uint32_t hval, hash_cell; uint32_t hash_triple[2] = {0}; uint32_t hash_pair[3] = {0}; uint8_t bufarea[16]; uint8_t *buf_cell = bufarea; uint8_t buf_triple[12]; uint8_t buf_pair[8]; uint8_t *buf_aux; uint32_t tab_cell[1U << 12U] = {0}; uint32_t tab_triple[1U << 12U] = {0}; uint32_t tab_pair[1U << 12U] = {0}; uint32_t update_triple[2] = {0}; uint32_t update_pair[3] = {0}; // Minimum cratios before issuing and _early giveup_ // Remind that ndlz is not meant for cratios <= 2 (too costly to decompress) op_limit = op + output_len; // Initialize the hash table to distances of 0 for (unsigned i = 0; i < (1U << 12U); i++) { tab_cell[i] = 0; } /* input and output buffer cannot be less than 16 and 66 bytes or we can get into trouble */ int overhead = 17 + (blockshape[0] * blockshape[1] / 16 - 1) * 2; if (input_len < 16 || output_len < overhead) { free(shape); free(chunkshape); free(blockshape); BLOSC_TRACE_ERROR("Incorrect length or maxout"); return 0; } uint8_t *obase = op; /* we start with literal copy */ *op++ = ndim; memcpy(op, &blockshape[0], 4); op += 4; memcpy(op, &blockshape[1], 4); op += 4; uint32_t i_stop[2]; for (int i = 0; i < 2; ++i) { i_stop[i] = (blockshape[i] + 3) / 4; } /* main loop */ uint32_t padding[2]; uint32_t ii[2]; for (ii[0] = 0; ii[0] < i_stop[0]; ++ii[0]) { for (ii[1] = 0; ii[1] < i_stop[1]; ++ii[1]) { // for each cell uint8_t token; for (int h = 0; h < 2; h++) { // new cell -> new possible references update_triple[h] = 0; update_pair[h] = 0; } update_pair[2] = 0; if (NDLZ_UNEXPECT_CONDITIONAL(op + 16 + 1 > op_limit)) { free(shape); free(chunkshape); free(blockshape); return 0; } uint32_t orig = ii[0] * 4 * blockshape[1] + ii[1] * 4; if (((blockshape[0] % 4 != 0) && (ii[0] == i_stop[0] - 1)) || ((blockshape[1] % 4 != 0) && (ii[1] == i_stop[1] - 1))) { token = 0; // padding -> literal copy *op++ = token; if (ii[0] == i_stop[0] - 1) { padding[0] = (blockshape[0] % 4 == 0) ? 4 : blockshape[0] % 4; } else { padding[0] = 4; } if (ii[1] == i_stop[1] - 1) { padding[1] = (blockshape[1] % 4 == 0) ? 4 : blockshape[1] % 4; } else { padding[1] = 4; } for (uint32_t i = 0; i < padding[0]; i++) { memcpy(op, &ip[orig + i * blockshape[1]], padding[1]); op += padding[1]; } } else { for (uint64_t i = 0; i < 4; i++) { // fill cell buffer uint64_t ind = orig + i * blockshape[1]; memcpy(buf_cell, &ip[ind], 4); buf_cell += 4; } buf_cell -= 16; const uint8_t *ref; uint32_t distance; uint8_t *anchor = op; /* comparison starting-point */ /* find potential match */ hash_cell = XXH32(buf_cell, 16, 1); // calculate cell hash hash_cell >>= 32U - 12U; ref = obase + tab_cell[hash_cell]; /* calculate distance to the match */ if (tab_cell[hash_cell] == 0) { distance = 0; } else { bool same = true; buf_aux = obase + tab_cell[hash_cell]; for (int i = 0; i < 16; i++) { if (buf_cell[i] != buf_aux[i]) { same = false; break; } } if (same) { distance = (int32_t) (anchor - ref); } else { distance = 0; } } bool alleq = true; for (int i = 1; i < 16; i++) { if (buf_cell[i] != buf_cell[0]) { alleq = false; break; } } if (alleq) { // all elements of the cell equal token = (uint8_t) (1U << 6U); *op++ = token; *op++ = buf_cell[0]; } else if (distance == 0 || (distance >= MAX_DISTANCE)) { // no cell match bool literal = true; // 2 rows pairs matches for (int j = 1; j < 4; j++) { memcpy(buf_pair, buf_cell, 4); memcpy(&buf_pair[4], &buf_cell[j * 4], 4); hval = XXH32(buf_pair, 8, 1); // calculate rows pair hash hval >>= 32U - 12U; ref = obase + tab_pair[hval]; /* calculate distance to the match */ bool same = true; uint16_t offset; if (tab_pair[hval] != 0) { buf_aux = obase + tab_pair[hval]; for (int k = 0; k < 8; k++) { if (buf_pair[k] != buf_aux[k]) { same = false; break; } } offset = (uint16_t) (anchor - obase - tab_pair[hval]); } else { same = false; } if (same) { distance = (int32_t) (anchor - ref); } else { distance = 0; } if ((distance != 0) && (distance < MAX_DISTANCE)) { /* rows pair match */ int k, m, l = -1; for (k = 1; k < 4; k++) { if (k != j) { if (l == -1) { l = k; } else { m = k; } } } memcpy(buf_pair, &buf_cell[l * 4], 4); memcpy(&buf_pair[4], &buf_cell[m * 4], 4); hval = XXH32(buf_pair, 8, 1); // calculate rows pair hash hval >>= 32U - 12U; ref = obase + tab_pair[hval]; same = true; if (tab_pair[hval] != 0) { buf_aux = obase + tab_pair[hval]; for (k = 0; k < 8; k++) { if (buf_pair[k] != buf_aux[k]) { same = false; break; } } } else { same = false; } if (same) { distance = (int32_t) (anchor + l * 4 - ref); } else { distance = 0; } if ((distance != 0) && (distance < MAX_DISTANCE)) { /* 2 pair matches */ literal = false; token = (uint8_t) ((1U << 5U) | (j << 3U)); *op++ = token; uint16_t offset_2 = (uint16_t) (anchor - obase - tab_pair[hval]); *(uint16_t *) op = offset; op += sizeof(offset); *(uint16_t *) op = offset_2; op += sizeof(offset_2); goto match; } } } // rows triples for (int i = 0; i < 2; i++) { memcpy(buf_triple, &buf_cell[i * 4], 4); for (int j = i + 1; j < 3; j++) { memcpy(&buf_triple[4], &buf_cell[j * 4], 4); for (int k = j + 1; k < 4; k++) { memcpy(&buf_triple[8], &buf_cell[k * 4], 4); hval = XXH32(buf_triple, 12, 1); // calculate triple hash hval >>= 32U - 12U; /* calculate distance to the match */ bool same = true; uint16_t offset; if (tab_triple[hval] != 0) { buf_aux = obase + tab_triple[hval]; for (int l = 0; l < 12; l++) { if (buf_triple[l] != buf_aux[l]) { same = false; break; } } offset = (uint16_t) (anchor - obase - tab_triple[hval]); } else { same = false; if ((j - i == 1) && (k - j == 1)) { update_triple[i] = (uint32_t) (anchor + 1 + i * 4 - obase); /* update hash table */ hash_triple[i] = hval; } } ref = obase + tab_triple[hval]; if (same) { distance = (int32_t) (anchor + i * 4 - ref); } else { distance = 0; } if ((distance != 0) && (distance < MAX_DISTANCE)) { literal = false; if (i == 1) { token = (uint8_t) (7U << 5U); } else { token = (uint8_t) ((7U << 5U) | ((j + k - 2) << 3U)); } *op++ = token; memcpy(op, &offset, 2); op += 2; for (int l = 0; l < 4; l++) { if ((l != i) && (l != j) && (l != k)) { memcpy(op, &buf_cell[4 * l], 4); op += 4; goto match; } } } } } } // rows pairs for (int i = 0; i < 3; i++) { memcpy(buf_pair, &buf_cell[i * 4], 4); for (int j = i + 1; j < 4; j++) { memcpy(&buf_pair[4], &buf_cell[j * 4], 4); hval = XXH32(buf_pair, 8, 1); // calculate rows pair hash hval >>= 32U - 12U; ref = obase + tab_pair[hval]; /* calculate distance to the match */ bool same = true; uint16_t offset; if (tab_pair[hval] != 0) { buf_aux = obase + tab_pair[hval]; for (int k = 0; k < 8; k++) { if (buf_pair[k] != buf_aux[k]) { same = false; break; } } offset = (uint16_t) (anchor - obase - tab_pair[hval]); } else { same = false; if (j - i == 1) { update_pair[i] = (uint32_t) (anchor + 1 + i * 4 - obase); /* update hash table */ hash_pair[i] = hval; } } if (same) { distance = (int32_t) (anchor + i * 4 - ref); } else { distance = 0; } if ((distance != 0) && (distance < MAX_DISTANCE)) { /* rows pair match */ literal = false; if (i == 2) { token = (uint8_t) (1U << 7U); } else { token = (uint8_t) ((1U << 7U) | (i << 5U) | (j << 3U)); } *op++ = token; memcpy(op, &offset, 2); op += 2; for (int k = 0; k < 4; k++) { if ((k != i) && (k != j)) { memcpy(op, &buf_cell[4 * k], 4); op += 4; } } goto match; } } } match: if (literal) { tab_cell[hash_cell] = (uint32_t) (anchor + 1 - obase); /* update hash tables */ if (update_triple[0] != 0) { for (int h = 0; h < 2; h++) { tab_triple[hash_triple[h]] = update_triple[h]; } } if (update_pair[0] != 0) { for (int h = 0; h < 3; h++) { tab_pair[hash_pair[h]] = update_pair[h]; } } token = 0; *op++ = token; memcpy(op, buf_cell, 16); op += 16; } } else { // cell match token = (uint8_t) ((1U << 7U) | (1U << 6U)); *op++ = token; uint16_t offset = (uint16_t) (anchor - obase - tab_cell[hash_cell]); memcpy(op, &offset, 2); op += 2; } } if ((op - obase) > input_len) { BLOSC_TRACE_ERROR("Compressed data is bigger than input!"); return 0; } } } free(shape); free(chunkshape); free(blockshape); return (int) (op - obase); } // See https://habr.com/en/company/yandex/blog/457612/ #ifdef __AVX2__ #if defined(_MSC_VER) #define ALIGNED_(x) __declspec(align(x)) #else #if defined(__GNUC__) #define ALIGNED_(x) __attribute__ ((aligned(x))) #endif #endif #define ALIGNED_TYPE_(t, x) t ALIGNED_(x) static unsigned char* copy_match_16(unsigned char *op, const unsigned char *match, int32_t len) { size_t offset = op - match; while (len >= 16) { static const ALIGNED_TYPE_(uint8_t, 16) masks[] = { 0, 1, 2, 1, 4, 1, 4, 2, 8, 7, 6, 5, 4, 3, 2, 1, // offset = 0, not used as mask, but for shift 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // offset = 1 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 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, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // offset = 16 }; _mm_storeu_si128((__m128i *)(op), _mm_shuffle_epi8(_mm_loadu_si128((const __m128i *)(match)), _mm_load_si128((const __m128i *)(masks) + offset))); match += masks[offset]; op += 16; len -= 16; } // Deal with remainders for (; len > 0; len--) { *op++ = *match++; } return op; } #endif int ndlz4_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, int32_t output_len, uint8_t meta, blosc2_dparams *dparams) { BLOSC_UNUSED_PARAM(meta); BLOSC_UNUSED_PARAM(dparams); BLOSC_ERROR_NULL(input, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(output, BLOSC2_ERROR_NULL_POINTER); uint8_t *ip = (uint8_t *) input; uint8_t *op = (uint8_t *) output; uint8_t ndim; int32_t blockshape[2]; int32_t eshape[2]; uint8_t *buffercpy; uint8_t local_buffer[16]; uint8_t token; // The fixed header is 1 (ndim) + 2 * int32 (blockshape) = 9 bytes. Reject any // input too short to hold it -- the previous `< 8` check let an 8-byte input // through and the header parse below then read a 9th byte. The cast keeps the // comparison signed so a negative `input_len` is rejected here, before the // `ip + input_len` pointer arithmetic (which would otherwise be UB) is reached. if (NDLZ_UNEXPECT_CONDITIONAL(input_len < (int32_t) (1 + 2 * sizeof(int32_t)))) { return 0; } uint8_t *ip_limit = ip + input_len; // Validate that a read of `len` bytes starting at byte offset `pos` (measured // from the start of `input`) lies fully within the compressed buffer. A // crafted token `offset` otherwise makes a back-reference point before // `input`, and the cell-fill memcpy then leaks out-of-bounds heap memory into // the (attacker-visible) output buffer -- CWE-125. The per-cell `ip > ip_limit` // check is not enough because each cell reads a 2-byte offset and literal bytes // past that point. All arithmetic is done on integer offsets so we never form // an out-of-bounds pointer (which is undefined behaviour in C, even when the // pointer is only compared and not dereferenced). #define NDLZ4_CHECK_RANGE(pos, len) \ do { \ int64_t _pos = (int64_t) (pos); \ int64_t _len = (int64_t) (len); \ if (_pos < 0 || _len < 0 || _pos > (int64_t) input_len - _len) { \ BLOSC_TRACE_ERROR("ndlz4: out-of-bounds reference in compressed stream"); \ return BLOSC2_ERROR_FAILURE; \ } \ } while (0) /* we start with literal copy */ ndim = *ip; ip++; if (ndim != 2) { BLOSC_TRACE_ERROR("This codec only works for ndim = 2"); return BLOSC2_ERROR_FAILURE; } memcpy(&blockshape[0], ip, 4); ip += 4; memcpy(&blockshape[1], ip, 4); ip += 4; // Sanity check. See https://www.cve.org/CVERecord?id=CVE-2024-3204 if (output_len < 0 || blockshape[0] < 0 || blockshape[1] < 0) { BLOSC_TRACE_ERROR("Output length or blockshape is negative"); return BLOSC2_ERROR_FAILURE; } eshape[0] = ((blockshape[0] + 3) / 4) * 4; eshape[1] = ((blockshape[1] + 3) / 4) * 4; if (NDLZ_UNEXPECT_CONDITIONAL((int64_t)output_len < (int64_t)blockshape[0] * (int64_t)blockshape[1])) { BLOSC_TRACE_ERROR("The blockshape is bigger than the output buffer"); return 0; } memset(op, 0, blockshape[0] * blockshape[1]); uint32_t i_stop[2]; for (int i = 0; i < 2; ++i) { i_stop[i] = eshape[i] / 4; } /* main loop */ uint32_t ii[2]; uint32_t padding[2] = {0}; uint32_t ind = 0; uint8_t cell_aux[16]; for (ii[0] = 0; ii[0] < i_stop[0]; ++ii[0]) { for (ii[1] = 0; ii[1] < i_stop[1]; ++ii[1]) { // for each cell if (NDLZ_UNEXPECT_CONDITIONAL(ip > ip_limit)) { BLOSC_TRACE_ERROR("Exceeding input length"); return BLOSC2_ERROR_FAILURE; } if (ii[0] == i_stop[0] - 1) { padding[0] = (blockshape[0] % 4 == 0) ? 4 : blockshape[0] % 4; } else { padding[0] = 4; } if (ii[1] == i_stop[1] - 1) { padding[1] = (blockshape[1] % 4 == 0) ? 4 : blockshape[1] % 4; } else { padding[1] = 4; } int64_t cur = ip - (const uint8_t *) input; // in-range: ip is within [input, ip_limit] NDLZ4_CHECK_RANGE(cur, 1); token = *ip++; if (token == 0) { // no match NDLZ4_CHECK_RANGE(cur + 1, (int64_t) padding[0] * padding[1]); buffercpy = ip; ip += padding[0] * padding[1]; } else if (token == (uint8_t) ((1U << 7U) | (1U << 6U))) { // cell match NDLZ4_CHECK_RANGE(cur + 1, 2); uint16_t offset; memcpy(&offset, ip, sizeof(offset)); int64_t bpos = (cur + 1) - (int64_t) offset - 1; // back-reference start offset NDLZ4_CHECK_RANGE(bpos, (int64_t) padding[0] * padding[1]); buffercpy = (uint8_t *) input + bpos; ip += 2; } else if (token == (uint8_t) (1U << 6U)) { // whole cell of same element NDLZ4_CHECK_RANGE(cur + 1, 1); buffercpy = cell_aux; memset(buffercpy, *ip, 16); ip++; } else if (token >= 224) { // three rows match buffercpy = local_buffer; NDLZ4_CHECK_RANGE(cur + 1, 2); uint16_t offset; memcpy(&offset, ip, sizeof(offset)); offset += 3; ip += 2; int i, j, k; if ((token >> 3U) == 28) { i = 1; j = 2; k = 3; } else { i = 0; if ((token >> 3U) < 30) { j = 1; k = 2; } else { k = 3; if ((token >> 3U) == 30) { j = 1; } else { j = 2; } } } int64_t bpos = (cur + 3) - (int64_t) offset; // == (ip - offset) as an offset NDLZ4_CHECK_RANGE(bpos, 12); const uint8_t *ref = (const uint8_t *) input + bpos; memcpy(&buffercpy[i * 4], ref, 4); memcpy(&buffercpy[j * 4], ref + 4, 4); memcpy(&buffercpy[k * 4], ref + 8, 4); for (int l = 0; l < 4; l++) { if ((l != i) && (l != j) && (l != k)) { NDLZ4_CHECK_RANGE(ip - (const uint8_t *) input, 4); memcpy(&buffercpy[l * 4], ip, 4); ip += 4; break; } } } else if ((token >= 128) && (token <= 191)) { // rows pair match buffercpy = local_buffer; NDLZ4_CHECK_RANGE(cur + 1, 2); uint16_t offset; memcpy(&offset, ip, sizeof(offset)); offset += 3; ip += 2; int i, j; if (token == 128) { i = 2; j = 3; } else { i = (token - 128) >> 5U; j = ((token - 128) >> 3U) - (i << 2U); } int64_t bpos = (cur + 3) - (int64_t) offset; NDLZ4_CHECK_RANGE(bpos, 8); const uint8_t *ref = (const uint8_t *) input + bpos; memcpy(&buffercpy[i * 4], ref, 4); memcpy(&buffercpy[j * 4], ref + 4, 4); for (int k = 0; k < 4; k++) { if ((k != i) && (k != j)) { NDLZ4_CHECK_RANGE(ip - (const uint8_t *) input, 4); memcpy(&buffercpy[k * 4], ip, 4); ip += 4; } } } else if ((token >= 40) && (token <= 63)) { // 2 rows pair matches buffercpy = local_buffer; NDLZ4_CHECK_RANGE(cur + 1, 2); uint16_t offset_1; memcpy(&offset_1, ip, sizeof(offset_1)); offset_1 += 5; ip += 2; NDLZ4_CHECK_RANGE(cur + 3, 2); uint16_t offset_2; memcpy(&offset_2, ip, sizeof(offset_2)); offset_2 += 5; ip += 2; int i, j, k, l, m; i = 0; j = ((token - 32) >> 3U); l = -1; for (k = 1; k < 4; k++) { if ((k != i) && (k != j)) { if (l == -1) { l = k; } else { m = k; } } } int64_t bpos1 = (cur + 5) - (int64_t) offset_1; NDLZ4_CHECK_RANGE(bpos1, 8); const uint8_t *ref1 = (const uint8_t *) input + bpos1; memcpy(&buffercpy[i * 4], ref1, 4); memcpy(&buffercpy[j * 4], ref1 + 4, 4); int64_t bpos2 = (cur + 5) - (int64_t) offset_2; NDLZ4_CHECK_RANGE(bpos2, 8); const uint8_t *ref2 = (const uint8_t *) input + bpos2; memcpy(&buffercpy[l * 4], ref2, 4); memcpy(&buffercpy[m * 4], ref2 + 4, 4); } else { BLOSC_TRACE_ERROR("Invalid token: %u at cell [%d, %d]\n", token, ii[0], ii[1]); return BLOSC2_ERROR_FAILURE; } // fill op with buffercpy uint32_t orig = ii[0] * 4 * blockshape[1] + ii[1] * 4; for (uint32_t i = 0; i < 4; i++) { if (i < padding[0]) { ind = orig + i * blockshape[1]; memcpy(&op[ind], buffercpy, padding[1]); // Only advance over rows we actually consume. For a literal cell // (token == 0) buffercpy points into the input and only holds // padding[0] * padding[1] bytes; advancing unconditionally for the // trailing padding rows would form an out-of-bounds pointer (UB) // for the block's last cell. The skipped rows are never read. buffercpy += padding[1]; } } if (ind > (uint32_t) output_len) { BLOSC_TRACE_ERROR("Exceeding output size"); return BLOSC2_ERROR_FAILURE; } } } ind += padding[1]; if ((int32_t)ind != (blockshape[0] * blockshape[1])) { BLOSC_TRACE_ERROR("Output size is not compatible with embedded blockshape"); return BLOSC2_ERROR_FAILURE; } if (ind > (uint32_t) output_len) { BLOSC_TRACE_ERROR("Exceeding output size"); return BLOSC2_ERROR_FAILURE; } return (int) ind; #undef NDLZ4_CHECK_RANGE } c-blosc2-3.1.5/plugins/codecs/ndlz/ndlz4x4.h000066400000000000000000000041511521743436100205030ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #ifndef BLOSC_PLUGINS_CODECS_NDLZ_NDLZ4X4_H #define BLOSC_PLUGINS_CODECS_NDLZ_NDLZ4X4_H #include "ndlz-private.h" #include "ndlz.h" #include "blosc2.h" /* #include #include "blosc2/blosc2-common.h" #include "fastcopy.h" */ /** Compress a block of data in the input buffer and returns the size of compressed block. The size of input buffer is specified by length. The minimum input buffer size is 16. The output buffer must be at least 5% larger than the input buffer and can not be smaller than 66 bytes. If the input is not compressible, or output does not fit in maxout bytes, the return value will be 0 and you will have to discard the output buffer. The acceleration parameter is related with the frequency for updating the internal hash. An acceleration of 1 means that the internal hash is updated at full rate. A value < 1 is not allowed and will be silently set to 1. The input buffer and the output buffer can not overlap. */ int ndlz4_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int32_t output_len, uint8_t meta, blosc2_cparams *cparams); /** Decompress a block of compressed data and returns the size of the decompressed block. If error occurs, e.g. the compressed data is corrupted or the output buffer is not large enough, then 0 (zero) will be returned instead. The input buffer and the output buffer can not overlap. Decompression is memory safe and guaranteed not to write the output buffer more than what is specified in maxout. */ int ndlz4_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, int32_t output_len, uint8_t meta, blosc2_dparams *dparams); #endif /* BLOSC_PLUGINS_CODECS_NDLZ_NDLZ4X4_H */ c-blosc2-3.1.5/plugins/codecs/ndlz/ndlz8x8.c000066400000000000000000000564671521743436100205270ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ /********************************************************************* This codec is meant to leverage multidimensionality for getting better compression ratios. The idea is to look for similarities in places that are closer in a euclidean metric, not the typical linear one. **********************************************************************/ #include "ndlz8x8.h" #include "xxhash.h" #include "b2nd.h" #include #include /* * Give hints to the compiler for branch prediction optimization. */ #if defined(__GNUC__) && (__GNUC__ > 2) #define NDLZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1)) #define NDLZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0)) #else #define NDLZ_EXPECT_CONDITIONAL(c) (c) #define NDLZ_UNEXPECT_CONDITIONAL(c) (c) #endif /* * Use inlined functions for supported systems. */ #if defined(_MSC_VER) && !defined(__cplusplus) /* Visual Studio */ #define inline __inline /* Visual C is not C99, but supports some kind of inline */ #endif #define MAX_COPY 32U #define MAX_DISTANCE 65535 #ifdef BLOSC_STRICT_ALIGN #define NDLZ_READU16(p) ((p)[0] | (p)[1]<<8) #define NDLZ_READU32(p) ((p)[0] | (p)[1]<<8 | (p)[2]<<16 | (p)[3]<<24) #else #define NDLZ_READU16(p) *((const uint16_t*)(p)) #define NDLZ_READU32(p) *((const uint32_t*)(p)) #endif #define HASH_LOG (12) int ndlz8_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int32_t output_len, uint8_t meta, blosc2_cparams *cparams) { BLOSC_UNUSED_PARAM(meta); BLOSC_ERROR_NULL(cparams, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(cparams->schunk, BLOSC2_ERROR_NULL_POINTER); uint8_t *smeta; int32_t smeta_len; if (blosc2_meta_get(cparams->schunk, "b2nd", &smeta, &smeta_len) < 0) { BLOSC_TRACE_ERROR("b2nd layer not found!"); return BLOSC2_ERROR_FAILURE; } const int cell_shape = 8; const int cell_size = 64; int8_t ndim; int64_t *shape = malloc(B2ND_MAX_DIM * sizeof(int64_t)); int32_t *chunkshape = malloc(B2ND_MAX_DIM * sizeof(int32_t)); int32_t *blockshape = malloc(B2ND_MAX_DIM * sizeof(int32_t)); int deserialize_rc = b2nd_deserialize_meta(smeta, smeta_len, &ndim, shape, chunkshape, blockshape, NULL, NULL); free(smeta); if (deserialize_rc < 0 || ndim != 2) { free(shape); free(chunkshape); free(blockshape); BLOSC_TRACE_ERROR("This codec only works for ndim = 2"); return BLOSC2_ERROR_FAILURE; } if (input_len != (blockshape[0] * blockshape[1])) { free(shape); free(chunkshape); free(blockshape); BLOSC_TRACE_ERROR("Length not equal to blocksize"); return BLOSC2_ERROR_FAILURE; } if (NDLZ_UNEXPECT_CONDITIONAL(output_len < (int) (1 + ndim * sizeof(int32_t)))) { free(shape); free(chunkshape); free(blockshape); BLOSC_TRACE_ERROR("Output too small"); return BLOSC2_ERROR_FAILURE; } uint8_t *ip = (uint8_t *) input; uint8_t *op = (uint8_t *) output; uint8_t *op_limit; uint32_t hval, hash_cell; uint32_t hash_triple[6] = {0}; uint32_t hash_pair[7] = {0}; uint8_t *bufarea = malloc(cell_size); uint8_t *buf_cell = bufarea; uint8_t *buf_aux; uint32_t tab_cell[1U << 12U] = {0}; uint32_t tab_triple[1U << 12U] = {0}; uint32_t tab_pair[1U << 12U] = {0}; uint32_t update_triple[6] = {0}; uint32_t update_pair[7] = {0}; // Minimum cratios before issuing and _early giveup_ // Remind that ndlz is not meant for cratios <= 2 (too costly to decompress) op_limit = op + output_len; // Initialize the hash table to distances of 0 for (unsigned i = 0; i < (1U << 12U); i++) { tab_cell[i] = 0; tab_triple[i] = 0; tab_pair[i] = 0; } /* input and output buffer cannot be less than 64 (cells are 8x8) */ int overhead = 17 + (blockshape[0] * blockshape[1] / cell_size - 1) * 2; if (input_len < cell_size || output_len < overhead) { free(shape); free(chunkshape); free(blockshape); free(bufarea); BLOSC_TRACE_ERROR("Incorrect length or maxout"); return 0; } uint8_t *obase = op; /* we start with literal copy */ *op++ = ndim; memcpy(op, &blockshape[0], 4); op += 4; memcpy(op, &blockshape[1], 4); op += 4; uint32_t i_stop[2]; for (int i = 0; i < 2; ++i) { i_stop[i] = (blockshape[i] + cell_shape - 1) / cell_shape; } /* main loop */ uint32_t padding[2]; uint32_t ii[2]; for (ii[0] = 0; ii[0] < i_stop[0]; ++ii[0]) { for (ii[1] = 0; ii[1] < i_stop[1]; ++ii[1]) { // for each cell for (int h = 0; h < 7; h++) { // new cell -> new possible references update_pair[h] = 0; if (h != 6) { update_triple[h] = 0; } } if (NDLZ_UNEXPECT_CONDITIONAL(op + cell_size + 1 > op_limit)) { free(shape); free(chunkshape); free(blockshape); free(bufarea); return 0; } uint32_t orig = ii[0] * cell_shape * blockshape[1] + ii[1] * cell_shape; if (((blockshape[0] % cell_shape != 0) && (ii[0] == i_stop[0] - 1)) || ((blockshape[1] % cell_shape != 0) && (ii[1] == i_stop[1] - 1))) { uint8_t token = 0; // padding -> literal copy *op++ = token; if (ii[0] == i_stop[0] - 1) { padding[0] = (blockshape[0] % cell_shape == 0) ? cell_shape : blockshape[0] % cell_shape; } else { padding[0] = cell_shape; } if (ii[1] == i_stop[1] - 1) { padding[1] = (blockshape[1] % cell_shape == 0) ? cell_shape : blockshape[1] % cell_shape; } else { padding[1] = cell_shape; } for (uint32_t i = 0; i < padding[0]; i++) { memcpy(op, &ip[orig + i * blockshape[1]], padding[1]); op += padding[1]; } } else { for (uint64_t i = 0; i < (uint64_t) cell_shape; i++) { // fill cell buffer uint64_t ind = orig + i * blockshape[1]; memcpy(buf_cell, &ip[ind], cell_shape); buf_cell += cell_shape; } buf_cell -= cell_size; const uint8_t *ref; uint32_t distance; uint8_t *anchor = op; /* comparison starting-point */ /* find potential match */ hash_cell = XXH32(buf_cell, cell_size, 1); // calculate cell hash hash_cell >>= 32U - 12U; ref = obase + tab_cell[hash_cell]; /* calculate distance to the match */ if (tab_cell[hash_cell] == 0) { distance = 0; } else { bool same = true; buf_aux = obase + tab_cell[hash_cell]; for (int i = 0; i < cell_size; i++) { if (buf_cell[i] != buf_aux[i]) { same = false; break; } } if (same) { distance = (int32_t) (anchor - ref); } else { distance = 0; } } bool alleq = true; for (int i = 1; i < cell_size; i++) { if (buf_cell[i] != buf_cell[0]) { alleq = false; break; } } if (alleq) { // all elements of the cell equal uint8_t token = (uint8_t) (1U << 6U); *op++ = token; *op++ = buf_cell[0]; } else if (distance == 0 || (distance >= MAX_DISTANCE)) { // no cell match bool literal = true; // rows triples matches for (int i = 0; i < 6; i++) { int triple_start = i * cell_shape; hval = XXH32(&buf_cell[triple_start], 24, 1); // calculate triple hash hval >>= 32U - 12U; /* calculate distance to the match */ bool same = true; uint16_t offset; if (tab_triple[hval] != 0) { buf_aux = obase + tab_triple[hval]; for (int l = 0; l < 24; l++) { if (buf_cell[triple_start + l] != buf_aux[l]) { same = false; break; } } offset = (uint16_t) (anchor - obase - tab_triple[hval]); } else { same = false; update_triple[i] = (uint32_t) (anchor + 1 + triple_start - obase); /* update hash table */ hash_triple[i] = hval; } ref = obase + tab_triple[hval]; if (same) { distance = (int32_t) (anchor + triple_start - ref); } else { distance = 0; } if ((distance != 0) && (distance < MAX_DISTANCE)) { // 3 rows match literal = false; uint8_t token = (uint8_t) ((21 << 3U) | i); *op++ = token; memcpy(op, &offset, 2); op += 2; for (int l = 0; l < 8; l++) { if ((l < i) || (l > i + 2)) { memcpy(op, &buf_cell[l * cell_shape], cell_shape); op += cell_shape; } } goto match; } } // rows pairs matches for (int i = 0; i < 7; i++) { int pair_start = i * cell_shape; hval = XXH32(&buf_cell[pair_start], 16, 1); // calculate rows pair hash hval >>= 32U - 12U; ref = obase + tab_pair[hval]; /* calculate distance to the match */ bool same = true; uint16_t offset; if (tab_pair[hval] != 0) { buf_aux = obase + tab_pair[hval]; for (int k = 0; k < 16; k++) { if (buf_cell[pair_start + k] != buf_aux[k]) { same = false; break; } } offset = (uint16_t) (anchor - obase - tab_pair[hval]); } else { same = false; update_pair[i] = (uint32_t) (anchor + 1 + pair_start - obase); /* update hash table */ hash_pair[i] = hval; } if (same) { distance = (int32_t) (anchor + pair_start - ref); } else { distance = 0; } if ((distance != 0) && (distance < MAX_DISTANCE)) { /* 1 rows pair match */ literal = false; uint8_t token = (uint8_t) ((17 << 3U) | i); *op++ = token; offset = (uint16_t) (anchor - obase - tab_pair[hval]); memcpy(op, &offset, 2); op += 2; for (int l = 0; l < 8; l++) { if ((l < i) || (l > i + 1)) { memcpy(op, &buf_cell[l * cell_shape], cell_shape); op += cell_shape; } } goto match; } } match: if (literal) { tab_cell[hash_cell] = (uint32_t) (anchor + 1 - obase); /* update hash tables */ if (update_triple[0] != 0) { for (int h = 0; h < 6; h++) { tab_triple[hash_triple[h]] = update_triple[h]; } } if (update_pair[0] != 0) { for (int h = 0; h < 7; h++) { tab_pair[hash_pair[h]] = update_pair[h]; } } uint8_t token = 0; *op++ = token; memcpy(op, buf_cell, cell_size); op += cell_size; } } else { // cell match uint8_t token = (uint8_t) ((1U << 7U) | (1U << 6U)); *op++ = token; uint16_t offset = (uint16_t) (anchor - obase - tab_cell[hash_cell]); memcpy(op, &offset, 2); op += 2; } } if ((op - obase) > input_len) { free(shape); free(chunkshape); free(blockshape); free(bufarea); BLOSC_TRACE_ERROR("Compressed data is bigger than input!"); return 0; } } } free(shape); free(chunkshape); free(blockshape); free(bufarea); return (int) (op - obase); } // See https://habr.com/en/company/yandex/blog/457612/ #ifdef __AVX2__ #if defined(_MSC_VER) #define ALIGNED_(x) __declspec(align(x)) #else #if defined(__GNUC__) #define ALIGNED_(x) __attribute__ ((aligned(x))) #endif #endif #define ALIGNED_TYPE_(t, x) t ALIGNED_(x) static unsigned char* copy_match_16(unsigned char *op, const unsigned char *match, int32_t len) { size_t offset = op - match; while (len >= 16) { static const ALIGNED_TYPE_(uint8_t, 16) masks[] = { 0, 1, 2, 1, 4, 1, 4, 2, 8, 7, 6, 5, 4, 3, 2, 1, // offset = 0, not used as mask, but for shift 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // offset = 1 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 0, 1, 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, 0, 1, 2, 3, 4, 5, 6, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // offset = 16 }; _mm_storeu_si128((__m128i *)(op), _mm_shuffle_epi8(_mm_loadu_si128((const __m128i *)(match)), _mm_load_si128((const __m128i *)(masks) + offset))); match += masks[offset]; op += 16; len -= 16; } // Deal with remainders for (; len > 0; len--) { *op++ = *match++; } return op; } #endif int ndlz8_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, int32_t output_len, uint8_t meta, blosc2_dparams *dparams) { BLOSC_UNUSED_PARAM(meta); BLOSC_UNUSED_PARAM(dparams); BLOSC_ERROR_NULL(input, BLOSC2_ERROR_NULL_POINTER); BLOSC_ERROR_NULL(output, BLOSC2_ERROR_NULL_POINTER); const int cell_shape = 8; const int cell_size = 64; uint8_t *ip = (uint8_t *) input; uint8_t *op = (uint8_t *) output; uint8_t ndim; int32_t blockshape[2]; int32_t eshape[2]; uint8_t *buffercpy; uint8_t token; // The fixed header is 1 (ndim) + 2 * int32 (blockshape) = 9 bytes. Reject any // input too short to hold it -- the previous `< 8` check let an 8-byte input // through and the header parse below then read a 9th byte. The cast keeps the // comparison signed so a negative `input_len` is rejected here, before the // `ip + input_len` pointer arithmetic (which would otherwise be UB) is reached. if (NDLZ_UNEXPECT_CONDITIONAL(input_len < (int32_t) (1 + 2 * sizeof(int32_t)))) { return 0; } uint8_t *ip_limit = ip + input_len; /* we start with literal copy */ ndim = *ip; ip++; if (ndim != 2) { BLOSC_TRACE_ERROR("This codec only works for ndim = 2"); return BLOSC2_ERROR_FAILURE; } memcpy(&blockshape[0], ip, 4); ip += 4; memcpy(&blockshape[1], ip, 4); ip += 4; // Sanity check. See https://www.cve.org/CVERecord?id=CVE-2024-3203 if (output_len < 0 || blockshape[0] < 0 || blockshape[1] < 0) { BLOSC_TRACE_ERROR("Output length or blockshape is negative"); return BLOSC2_ERROR_FAILURE; } eshape[0] = ((blockshape[0] + 7) / cell_shape) * cell_shape; eshape[1] = ((blockshape[1] + 7) / cell_shape) * cell_shape; if (NDLZ_UNEXPECT_CONDITIONAL((int64_t)output_len < (int64_t)blockshape[0] * (int64_t)blockshape[1])) { BLOSC_TRACE_ERROR("The blockshape is bigger than the output buffer"); return 0; } memset(op, 0, blockshape[0] * blockshape[1]); int32_t i_stop[2]; for (int i = 0; i < 2; ++i) { i_stop[i] = eshape[i] / cell_shape; } /* main loop */ int32_t ii[2]; int32_t padding[2] = {0}; int32_t ind = 0; uint8_t *local_buffer = malloc(cell_size); uint8_t *cell_aux = malloc(cell_size); // Validate that a read of `len` bytes starting at byte offset `pos` (measured // from the start of `input`) lies fully within the compressed buffer. A // crafted token `offset` otherwise makes a back-reference point before // `input`, and the cell-fill memcpy then leaks out-of-bounds heap memory into // the (attacker-visible) output -- CWE-125. The per-cell `ip > ip_limit` // check is not enough because each cell reads a 2-byte offset and literal // bytes past that point. All arithmetic is done on integer offsets so we // never form an out-of-bounds pointer (undefined behaviour in C, even when the // pointer is only compared and not dereferenced). #define NDLZ8_CHECK_RANGE(pos, len) \ do { \ int64_t _pos = (int64_t) (pos); \ int64_t _len = (int64_t) (len); \ if (_pos < 0 || _len < 0 || _pos > (int64_t) input_len - _len) { \ free(local_buffer); \ free(cell_aux); \ BLOSC_TRACE_ERROR("ndlz8: out-of-bounds reference in compressed stream"); \ return BLOSC2_ERROR_FAILURE; \ } \ } while (0) for (ii[0] = 0; ii[0] < i_stop[0]; ++ii[0]) { for (ii[1] = 0; ii[1] < i_stop[1]; ++ii[1]) { // for each cell if (NDLZ_UNEXPECT_CONDITIONAL(ip > ip_limit)) { free(local_buffer); free(cell_aux); BLOSC_TRACE_ERROR("Exceeding input length"); return BLOSC2_ERROR_FAILURE; } if (ii[0] == i_stop[0] - 1) { padding[0] = (blockshape[0] % cell_shape == 0) ? cell_shape : blockshape[0] % cell_shape; } else { padding[0] = cell_shape; } if (ii[1] == i_stop[1] - 1) { padding[1] = (blockshape[1] % cell_shape == 0) ? cell_shape : blockshape[1] % cell_shape; } else { padding[1] = cell_shape; } int64_t cur = ip - (const uint8_t *) input; // in-range: ip is within [input, ip_limit] NDLZ8_CHECK_RANGE(cur, 1); token = *ip++; uint8_t match_type = (token >> 3U); if (token == 0) { // no match NDLZ8_CHECK_RANGE(cur + 1, (int64_t) padding[0] * padding[1]); buffercpy = ip; ip += padding[0] * padding[1]; } else if (token == (uint8_t) ((1U << 7U) | (1U << 6U))) { // cell match NDLZ8_CHECK_RANGE(cur + 1, 2); uint16_t offset; memcpy(&offset, ip, sizeof(offset)); int64_t bpos = (cur + 1) - (int64_t) offset - 1; NDLZ8_CHECK_RANGE(bpos, (int64_t) padding[0] * padding[1]); buffercpy = (uint8_t *) input + bpos; ip += 2; } else if (token == (uint8_t) (1U << 6U)) { // whole cell of same element NDLZ8_CHECK_RANGE(cur + 1, 1); buffercpy = cell_aux; memset(buffercpy, *ip, cell_size); ip++; } else if (match_type == 21) { // triple match buffercpy = local_buffer; int row = (int) (token & 7); if (row + 2 >= cell_shape) { free(local_buffer); free(cell_aux); BLOSC_TRACE_ERROR("Triple match row out of bounds"); return BLOSC2_ERROR_FAILURE; } NDLZ8_CHECK_RANGE(cur + 1, 2); uint16_t offset; memcpy(&offset, ip, sizeof(offset)); ip += 2; // back-reference base == ip - sizeof(token) - sizeof(offset) - offset, // i.e. (cur + 3) - 3 - offset == cur - offset, expressed as an offset. int64_t bpos = cur - (int64_t) offset; NDLZ8_CHECK_RANGE(bpos, 3 * cell_shape); const uint8_t *ref = (const uint8_t *) input + bpos; for (int l = 0; l < 3; l++) { memcpy(&buffercpy[(row + l) * cell_shape], ref + l * cell_shape, cell_shape); } for (int l = 0; l < cell_shape; l++) { if ((l < row) || (l > row + 2)) { NDLZ8_CHECK_RANGE(ip - (const uint8_t *) input, cell_shape); memcpy(&buffercpy[l * cell_shape], ip, cell_shape); ip += cell_shape; } } } else if (match_type == 17) { // pair match buffercpy = local_buffer; int row = (int) (token & 7); if (row + 1 >= cell_shape) { free(local_buffer); free(cell_aux); BLOSC_TRACE_ERROR("Pair match row out of bounds"); return BLOSC2_ERROR_FAILURE; } NDLZ8_CHECK_RANGE(cur + 1, 2); uint16_t offset; memcpy(&offset, ip, sizeof(offset)); ip += 2; // back-reference base == cur - offset (see triple-match note above). int64_t bpos = cur - (int64_t) offset; NDLZ8_CHECK_RANGE(bpos, 2 * cell_shape); const uint8_t *ref = (const uint8_t *) input + bpos; for (int l = 0; l < 2; l++) { memcpy(&buffercpy[(row + l) * cell_shape], ref + l * cell_shape, cell_shape); } for (int l = 0; l < cell_shape; l++) { if ((l < row) || (l > row + 1)) { NDLZ8_CHECK_RANGE(ip - (const uint8_t *) input, cell_shape); memcpy(&buffercpy[l * cell_shape], ip, cell_shape); ip += cell_shape; } } } else { free(local_buffer); free(cell_aux); BLOSC_TRACE_ERROR("Invalid token: %u at cell [%d, %d]\n", token, ii[0], ii[1]); return BLOSC2_ERROR_FAILURE; } int32_t orig = ii[0] * cell_shape * blockshape[1] + ii[1] * cell_shape; for (int32_t i = 0; i < (int32_t) cell_shape; i++) { if (i < padding[0]) { ind = orig + i * blockshape[1]; memcpy(&op[ind], buffercpy, padding[1]); // Only advance over rows we actually consume. For a literal cell // (token == 0) buffercpy points into the input and only holds // padding[0] * padding[1] bytes; advancing unconditionally for the // trailing padding rows would form an out-of-bounds pointer (UB) // for the block's last cell. The skipped rows are never read. buffercpy += padding[1]; } } if (ind > output_len) { free(local_buffer); free(cell_aux); BLOSC_TRACE_ERROR("Exceeding output size"); return BLOSC2_ERROR_FAILURE; } } } ind += padding[1]; free(cell_aux); free(local_buffer); if (ind != (blockshape[0] * blockshape[1])) { BLOSC_TRACE_ERROR("Output size is not compatible with embedded blockshape"); return BLOSC2_ERROR_FAILURE; } if (ind > output_len) { BLOSC_TRACE_ERROR("Exceeding output size"); return BLOSC2_ERROR_FAILURE; } return (int) ind; #undef NDLZ8_CHECK_RANGE } c-blosc2-3.1.5/plugins/codecs/ndlz/ndlz8x8.h000066400000000000000000000040301521743436100205070ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. **********************************************************************/ #ifndef BLOSC_PLUGINS_CODECS_NDLZ_NDLZ8X8_H #define BLOSC_PLUGINS_CODECS_NDLZ_NDLZ8X8_H #include "ndlz-private.h" #include "ndlz.h" #include "blosc2.h" /** Compress a block of data in the input buffer and returns the size of compressed block. The size of input buffer is specified by length. The minimum input buffer size is 16. The output buffer must be at least 5% larger than the input buffer and can not be smaller than 66 bytes. If the input is not compressible, or output does not fit in maxout bytes, the return value will be 0 and you will have to discard the output buffer. The acceleration parameter is related with the frequency for updating the internal hash. An acceleration of 1 means that the internal hash is updated at full rate. A value < 1 is not allowed and will be silently set to 1. The input buffer and the output buffer can not overlap. */ int ndlz8_compress(const uint8_t *input, int32_t input_len, uint8_t *output, int32_t output_len, uint8_t meta, blosc2_cparams *cparams); /** Decompress a block of compressed data and returns the size of the decompressed block. If error occurs, e.g. the compressed data is corrupted or the output buffer is not large enough, then 0 (zero) will be returned instead. The input buffer and the output buffer can not overlap. Decompression is memory safe and guaranteed not to write the output buffer more than what is specified in maxout. */ int ndlz8_decompress(const uint8_t *input, int32_t input_len, uint8_t *output, int32_t output_len, uint8_t meta, blosc2_dparams *dparams); #endif /* BLOSC_PLUGINS_CODECS_NDLZ_NDLZ8X8_H */ c-blosc2-3.1.5/plugins/codecs/ndlz/test_ndlz.c000066400000000000000000000226611521743436100212030ustar00rootroot00000000000000/********************************************************************* Blosc - Blocked Shuffling and Compression Library Copyright (c) 2021 Blosc Development Team https://blosc.org License: BSD 3-Clause (see LICENSE.txt) See LICENSE.txt for details about copyright and rights to use. Test program demonstrating use of the Blosc filter from C code. To compile this program: $ gcc -O test_ndlz.c -o test_ndlz -lblosc2 To run: $ ./test_ndlz Blosc version info: 2.0.0a6.dev ($Date:: 2018-05-18 #$) Successful roundtrip! Compression: 1792 -> 1630 (1.1x) Successful roundtrip! Compression: 1792 -> 1749 (1.0x) same_cells: 43 obtained Successful roundtrip! Compression: 16128 -> 2579 (6.3x) Successful roundtrip! Compression: 16128 -> 3829 (4.2x) some_matches: 12299 obtained **********************************************************************/ #include "b2nd.h" #include "blosc2/codecs-registry.h" #include "blosc2.h" #include #include static int test_ndlz_4(blosc2_schunk *schunk) { int64_t nchunks = schunk->nchunks; int32_t chunksize = schunk->chunksize; uint8_t *data_in = malloc(chunksize); int decompressed; int64_t csize; int64_t dsize; int64_t csize_f = 0; uint8_t *data_out = malloc(chunksize + BLOSC2_MAX_OVERHEAD); uint8_t *data_dest = malloc(chunksize); /* Create a context for compression */ blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.splitmode = BLOSC_ALWAYS_SPLIT; cparams.typesize = schunk->typesize; cparams.compcode = BLOSC_CODEC_NDLZ; cparams.compcode_meta = 4; cparams.filters[BLOSC2_MAX_FILTERS - 1] = BLOSC_SHUFFLE; cparams.clevel = 5; cparams.nthreads = 1; cparams.blocksize = schunk->blocksize; cparams.schunk = schunk; blosc2_context *cctx; cctx = blosc2_create_cctx(cparams); blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; dparams.nthreads = 1; blosc2_context *dctx; dctx = blosc2_create_dctx(dparams); for (int ci = 0; ci < nchunks; ci++) { decompressed = blosc2_schunk_decompress_chunk(schunk, ci, data_in, chunksize); if (decompressed < 0) { printf("Error decompressing chunk \n"); return -1; } /* Compress with clevel=5 and shuffle active */ csize = blosc2_compress_ctx(cctx, data_in, chunksize, data_out, chunksize + BLOSC2_MAX_OVERHEAD); if (csize == 0) { printf("Buffer is incompressible. Giving up.\n"); return -1; } else if (csize < 0) { printf("Compression error. Error code: %" PRId64 "\n", csize); return (int) csize; } csize_f += csize; /* Decompress */ dsize = blosc2_decompress_ctx(dctx, data_out, chunksize + BLOSC2_MAX_OVERHEAD, data_dest, chunksize); if (dsize <= 0) { printf("Decompression error. Error code: %" PRId64 "\n", dsize); return (int) dsize; } for (int i = 0; i < chunksize; i++) { if (data_in[i] != data_dest[i]) { printf("i: %d, data %u, dest %u", i, data_in[i], data_dest[i]); printf("\n Decompressed data differs from original!\n"); return -1; } } } csize_f = csize_f / nchunks; free(data_in); free(data_out); free(data_dest); blosc2_free_ctx(cctx); blosc2_free_ctx(dctx); printf("Successful roundtrip!\n"); printf("Compression: %d -> %" PRId64 " (%.1fx)\n", chunksize, csize_f, (1. * chunksize) / (double) csize_f); return (int) (chunksize - csize_f); } static int test_ndlz_8(blosc2_schunk *schunk) { int64_t nchunks = schunk->nchunks; int32_t chunksize = (int32_t) (schunk->chunksize); // int isize = (int) array->extchunknitems * typesize; uint8_t *data_in = malloc(chunksize); int decompressed; int64_t csize; int64_t dsize; int64_t csize_f = 0; uint8_t *data_out = malloc(chunksize + BLOSC2_MAX_OVERHEAD); uint8_t *data_dest = malloc(chunksize); /* Create a context for compression */ blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.splitmode = BLOSC_ALWAYS_SPLIT; cparams.typesize = schunk->typesize; cparams.compcode = BLOSC_CODEC_NDLZ; cparams.compcode_meta = 8; cparams.filters[BLOSC2_MAX_FILTERS - 1] = BLOSC_SHUFFLE; cparams.clevel = 5; cparams.nthreads = 1; cparams.blocksize = schunk->blocksize; cparams.schunk = schunk; blosc2_context *cctx; cctx = blosc2_create_cctx(cparams); blosc2_dparams dparams = BLOSC2_DPARAMS_DEFAULTS; dparams.nthreads = 1; blosc2_context *dctx; dctx = blosc2_create_dctx(dparams); for (int ci = 0; ci < nchunks; ci++) { decompressed = blosc2_schunk_decompress_chunk(schunk, ci, data_in, chunksize); if (decompressed < 0) { printf("Error decompressing chunk \n"); return -1; } /* Compress with clevel=5 and shuffle active */ csize = blosc2_compress_ctx(cctx, data_in, chunksize, data_out, chunksize + BLOSC2_MAX_OVERHEAD); if (csize == 0) { printf("Buffer is incompressible. Giving up.\n"); return -1; } else if (csize < 0) { printf("Compression error. Error code: %" PRId64 "\n", csize); return (int) csize; } csize_f += csize; /* Decompress */ dsize = blosc2_decompress_ctx(dctx, data_out, chunksize + BLOSC2_MAX_OVERHEAD, data_dest, chunksize); if (dsize <= 0) { printf("Decompression error. Error code: %" PRId64 "\n", dsize); return (int) dsize; } for (int i = 0; i < chunksize; i++) { if (data_in[i] != data_dest[i]) { printf("i: %d, data %u, dest %u", i, data_in[i], data_dest[i]); printf("\n Decompressed data differs from original!\n"); return -1; } } } csize_f = csize_f / nchunks; free(data_in); free(data_out); free(data_dest); blosc2_free_ctx(cctx); blosc2_free_ctx(dctx); printf("Successful roundtrip!\n"); printf("Compression: %d -> %" PRId64 " (%.1fx)\n", chunksize, csize_f, (1. * chunksize) / (double) csize_f); return (int) (chunksize - csize_f); } int rand_() { int ndim = 2; int typesize = 4; int64_t shape[] = {32, 18}; int32_t chunkshape[] = {17, 16}; int32_t blockshape[] = {8, 9}; int64_t nelem = 1; for (int i = 0; i < ndim; ++i) { nelem *= (int) (shape[i]); } int64_t size = typesize * nelem; float *data = calloc((size_t) nelem, sizeof(float)); for (int64_t i = 0; i < nelem; i++) { data[i] = (float) (rand() % 220); } blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = typesize; blosc2_storage b2_storage = {.cparams=&cparams}; b2_storage.contiguous = true; b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, ndim, shape, chunkshape, blockshape, NULL, 0, NULL, 0); b2nd_array_t *arr; BLOSC_ERROR(b2nd_from_cbuffer(ctx, &arr, data, size)); blosc2_schunk *schunk = arr->sc; /* Run the test. */ int result = test_ndlz_4(schunk); BLOSC_ERROR(b2nd_free_ctx(ctx)); BLOSC_ERROR(b2nd_free(arr)); free(data); return result; } int same_cells() { int ndim = 2; int typesize = 8; int64_t shape[] = {128, 111}; int32_t chunkshape[] = {32, 11}; int32_t blockshape[] = {16, 7}; int64_t nelem = 1; for (int i = 0; i < ndim; ++i) { nelem *= (int) (shape[i]); } int64_t size = typesize * nelem; double *data = calloc((size_t) nelem, sizeof(double)); for (int64_t i = 0; i < (nelem / 4); i++) { data[i * 4] = (double) 11111111; data[i * 4 + 1] = (double) 99999999; } blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = typesize; blosc2_storage b2_storage = {.cparams=&cparams}; b2_storage.contiguous = true; b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, ndim, shape, chunkshape, blockshape, NULL, 0, NULL, 0); b2nd_array_t *arr; BLOSC_ERROR(b2nd_from_cbuffer(ctx, &arr, data, size)); blosc2_schunk *schunk = arr->sc; /* Run the test. */ int result = test_ndlz_4(schunk); BLOSC_ERROR(b2nd_free_ctx(ctx)); BLOSC_ERROR(b2nd_free(arr)); free(data); return result; } int some_matches() { int ndim = 2; int typesize = 8; int64_t shape[] = {128, 111}; int32_t chunkshape[] = {48, 32}; int32_t blockshape[] = {14, 18}; int64_t nelem = 1; for (int i = 0; i < ndim; ++i) { nelem *= (int) (shape[i]); } int64_t size = typesize * nelem; double *data = calloc((size_t) nelem, sizeof(double)); for (int64_t i = 0; i < (nelem / 2); i++) { data[i] = (double) i; } for (int64_t i = (nelem / 2); i < nelem; i++) { data[i] = (double) 1; } blosc2_cparams cparams = BLOSC2_CPARAMS_DEFAULTS; cparams.typesize = typesize; blosc2_storage b2_storage = {.cparams=&cparams}; b2_storage.contiguous = true; b2nd_context_t *ctx = b2nd_create_ctx(&b2_storage, ndim, shape, chunkshape, blockshape, NULL, 0, NULL, 0); b2nd_array_t *arr; BLOSC_ERROR(b2nd_from_cbuffer(ctx, &arr, data, size)); blosc2_schunk *schunk = arr->sc; /* Run the test. */ int result = test_ndlz_8(schunk); BLOSC_ERROR(b2nd_free_ctx(ctx)); BLOSC_ERROR(b2nd_free(arr)); free(data); return result; } int main(void) { int result; blosc2_init(); // this is mandatory for initializing the plugin mechanism result = rand_(); printf("rand: %d obtained \n \n", result); if (result < 0) return result; result = same_cells(); printf("same_cells: %d obtained \n \n", result); if (result < 0) return result; result = some_matches(); if (result < 0) return result; printf("some_matches: %d obtained \n \n", result); blosc2_destroy(); return BLOSC2_ERROR_SUCCESS; } c-blosc2-3.1.5/plugins/codecs/ndlz/xxhash.c000066400000000000000000000034771521743436100205040ustar00rootroot00000000000000/* * xxHash - Extremely Fast Hash algorithm * Copyright (C) 2012-2021 Yann Collet * * BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * You can contact the author at: * - xxHash homepage: https://www.xxhash.com * - xxHash source repository: https://github.com/Cyan4973/xxHash */ /* * xxhash.c instantiates functions defined in xxhash.h */ #define XXH_STATIC_LINKING_ONLY /* access advanced declarations */ #define XXH_IMPLEMENTATION /* access definitions */ #include "xxhash.h" c-blosc2-3.1.5/plugins/codecs/ndlz/xxhash.h000066400000000000000000007562501521743436100205150ustar00rootroot00000000000000/* * xxHash - Extremely Fast Hash algorithm * Header File * Copyright (C) 2012-2021 Yann Collet * * BSD 2-Clause License (https://www.opensource.org/licenses/bsd-license.php) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * You can contact the author at: * - xxHash homepage: https://www.xxhash.com * - xxHash source repository: https://github.com/Cyan4973/xxHash */ /*! * @mainpage xxHash * * xxHash is an extremely fast non-cryptographic hash algorithm, working at RAM speed * limits. * * It is proposed in four flavors, in three families: * 1. @ref XXH32_family * - Classic 32-bit hash function. Simple, compact, and runs on almost all * 32-bit and 64-bit systems. * 2. @ref XXH64_family * - Classic 64-bit adaptation of XXH32. Just as simple, and runs well on most * 64-bit systems (but _not_ 32-bit systems). * 3. @ref XXH3_family * - Modern 64-bit and 128-bit hash function family which features improved * strength and performance across the board, especially on smaller data. * It benefits greatly from SIMD and 64-bit without requiring it. * * Benchmarks * --- * The reference system uses an Intel i7-9700K CPU, and runs Ubuntu x64 20.04. * The open source benchmark program is compiled with clang v10.0 using -O3 flag. * * | Hash Name | ISA ext | Width | Large Data Speed | Small Data Velocity | * | -------------------- | ------- | ----: | ---------------: | ------------------: | * | XXH3_64bits() | @b AVX2 | 64 | 59.4 GB/s | 133.1 | * | MeowHash | AES-NI | 128 | 58.2 GB/s | 52.5 | * | XXH3_128bits() | @b AVX2 | 128 | 57.9 GB/s | 118.1 | * | CLHash | PCLMUL | 64 | 37.1 GB/s | 58.1 | * | XXH3_64bits() | @b SSE2 | 64 | 31.5 GB/s | 133.1 | * | XXH3_128bits() | @b SSE2 | 128 | 29.6 GB/s | 118.1 | * | RAM sequential read | | N/A | 28.0 GB/s | N/A | * | ahash | AES-NI | 64 | 22.5 GB/s | 107.2 | * | City64 | | 64 | 22.0 GB/s | 76.6 | * | T1ha2 | | 64 | 22.0 GB/s | 99.0 | * | City128 | | 128 | 21.7 GB/s | 57.7 | * | FarmHash | AES-NI | 64 | 21.3 GB/s | 71.9 | * | XXH64() | | 64 | 19.4 GB/s | 71.0 | * | SpookyHash | | 64 | 19.3 GB/s | 53.2 | * | Mum | | 64 | 18.0 GB/s | 67.0 | * | CRC32C | SSE4.2 | 32 | 13.0 GB/s | 57.9 | * | XXH32() | | 32 | 9.7 GB/s | 71.9 | * | City32 | | 32 | 9.1 GB/s | 66.0 | * | Blake3* | @b AVX2 | 256 | 4.4 GB/s | 8.1 | * | Murmur3 | | 32 | 3.9 GB/s | 56.1 | * | SipHash* | | 64 | 3.0 GB/s | 43.2 | * | Blake3* | @b SSE2 | 256 | 2.4 GB/s | 8.1 | * | HighwayHash | | 64 | 1.4 GB/s | 6.0 | * | FNV64 | | 64 | 1.2 GB/s | 62.7 | * | Blake2* | | 256 | 1.1 GB/s | 5.1 | * | SHA1* | | 160 | 0.8 GB/s | 5.6 | * | MD5* | | 128 | 0.6 GB/s | 7.8 | * @note * - Hashes which require a specific ISA extension are noted. SSE2 is also noted, * even though it is mandatory on x64. * - Hashes with an asterisk are cryptographic. Note that MD5 is non-cryptographic * by modern standards. * - Small data velocity is a rough average of algorithm's efficiency for small * data. For more accurate information, see the wiki. * - More benchmarks and strength tests are found on the wiki: * https://github.com/Cyan4973/xxHash/wiki * * Usage * ------ * All xxHash variants use a similar API. Changing the algorithm is a trivial * substitution. * * @pre * For functions which take an input and length parameter, the following * requirements are assumed: * - The range from [`input`, `input + length`) is valid, readable memory. * - The only exception is if the `length` is `0`, `input` may be `NULL`. * - For C++, the objects must have the *TriviallyCopyable* property, as the * functions access bytes directly as if it was an array of `unsigned char`. * * @anchor single_shot_example * **Single Shot** * * These functions are stateless functions which hash a contiguous block of memory, * immediately returning the result. They are the easiest and usually the fastest * option. * * XXH32(), XXH64(), XXH3_64bits(), XXH3_128bits() * * @code{.c} * #include * #include "xxhash.h" * * // Example for a function which hashes a null terminated string with XXH32(). * XXH32_hash_t hash_string(const char* string, XXH32_hash_t seed) * { * // NULL pointers are only valid if the length is zero * size_t length = (string == NULL) ? 0 : strlen(string); * return XXH32(string, length, seed); * } * @endcode * * @anchor streaming_example * **Streaming** * * These groups of functions allow incremental hashing of unknown size, even * more than what would fit in a size_t. * * XXH32_reset(), XXH64_reset(), XXH3_64bits_reset(), XXH3_128bits_reset() * * @code{.c} * #include * #include * #include "xxhash.h" * // Example for a function which hashes a FILE incrementally with XXH3_64bits(). * XXH64_hash_t hashFile(FILE* f) * { * // Allocate a state struct. Do not just use malloc() or new. * XXH3_state_t* state = XXH3_createState(); * assert(state != NULL && "Out of memory!"); * // Reset the state to start a new hashing session. * XXH3_64bits_reset(state); * char buffer[4096]; * size_t count; * // Read the file in chunks * while ((count = fread(buffer, 1, sizeof(buffer), f)) != 0) { * // Run update() as many times as necessary to process the data * XXH3_64bits_update(state, buffer, count); * } * // Retrieve the finalized hash. This will not change the state. * XXH64_hash_t result = XXH3_64bits_digest(state); * // Free the state. Do not use free(). * XXH3_freeState(state); * return result; * } * @endcode * * @file xxhash.h * xxHash prototypes and implementation */ #if defined (__cplusplus) extern "C" { #endif /* **************************** * INLINE mode ******************************/ /*! * @defgroup public Public API * Contains details on the public xxHash functions. * @{ */ #ifdef XXH_DOXYGEN /*! * @brief Gives access to internal state declaration, required for static allocation. * * Incompatible with dynamic linking, due to risks of ABI changes. * * Usage: * @code{.c} * #define XXH_STATIC_LINKING_ONLY * #include "xxhash.h" * @endcode */ # define XXH_STATIC_LINKING_ONLY /* Do not undef XXH_STATIC_LINKING_ONLY for Doxygen */ /*! * @brief Gives access to internal definitions. * * Usage: * @code{.c} * #define XXH_STATIC_LINKING_ONLY * #define XXH_IMPLEMENTATION * #include "xxhash.h" * @endcode */ # define XXH_IMPLEMENTATION /* Do not undef XXH_IMPLEMENTATION for Doxygen */ /*! * @brief Exposes the implementation and marks all functions as `inline`. * * Use these build macros to inline xxhash into the target unit. * Inlining improves performance on small inputs, especially when the length is * expressed as a compile-time constant: * * https://fastcompression.blogspot.com/2018/03/xxhash-for-small-keys-impressive-power.html * * It also keeps xxHash symbols private to the unit, so they are not exported. * * Usage: * @code{.c} * #define XXH_INLINE_ALL * #include "xxhash.h" * @endcode * Do not compile and link xxhash.o as a separate object, as it is not useful. */ # define XXH_INLINE_ALL # undef XXH_INLINE_ALL /*! * @brief Exposes the implementation without marking functions as inline. */ # define XXH_PRIVATE_API # undef XXH_PRIVATE_API /*! * @brief Emulate a namespace by transparently prefixing all symbols. * * If you want to include _and expose_ xxHash functions from within your own * library, but also want to avoid symbol collisions with other libraries which * may also include xxHash, you can use @ref XXH_NAMESPACE to automatically prefix * any public symbol from xxhash library with the value of @ref XXH_NAMESPACE * (therefore, avoid empty or numeric values). * * Note that no change is required within the calling program as long as it * includes `xxhash.h`: Regular symbol names will be automatically translated * by this header. */ # define XXH_NAMESPACE /* YOUR NAME HERE */ # undef XXH_NAMESPACE #endif #if (defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API)) \ && !defined(XXH_INLINE_ALL_31684351384) /* this section should be traversed only once */ # define XXH_INLINE_ALL_31684351384 /* give access to the advanced API, required to compile implementations */ # undef XXH_STATIC_LINKING_ONLY /* avoid macro redef */ # define XXH_STATIC_LINKING_ONLY /* make all functions private */ # undef XXH_PUBLIC_API # if defined(__GNUC__) # define XXH_PUBLIC_API static __inline __attribute__((unused)) # elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) # define XXH_PUBLIC_API static inline # elif defined(_MSC_VER) # define XXH_PUBLIC_API static __inline # else /* note: this version may generate warnings for unused static functions */ # define XXH_PUBLIC_API static # endif /* * This part deals with the special case where a unit wants to inline xxHash, * but "xxhash.h" has previously been included without XXH_INLINE_ALL, * such as part of some previously included *.h header file. * Without further action, the new include would just be ignored, * and functions would effectively _not_ be inlined (silent failure). * The following macros solve this situation by prefixing all inlined names, * avoiding naming collision with previous inclusions. */ /* Before that, we unconditionally #undef all symbols, * in case they were already defined with XXH_NAMESPACE. * They will then be redefined for XXH_INLINE_ALL */ # undef XXH_versionNumber /* XXH32 */ # undef XXH32 # undef XXH32_createState # undef XXH32_freeState # undef XXH32_reset # undef XXH32_update # undef XXH32_digest # undef XXH32_copyState # undef XXH32_canonicalFromHash # undef XXH32_hashFromCanonical /* XXH64 */ # undef XXH64 # undef XXH64_createState # undef XXH64_freeState # undef XXH64_reset # undef XXH64_update # undef XXH64_digest # undef XXH64_copyState # undef XXH64_canonicalFromHash # undef XXH64_hashFromCanonical /* XXH3_64bits */ # undef XXH3_64bits # undef XXH3_64bits_withSecret # undef XXH3_64bits_withSeed # undef XXH3_64bits_withSecretandSeed # undef XXH3_createState # undef XXH3_freeState # undef XXH3_copyState # undef XXH3_64bits_reset # undef XXH3_64bits_reset_withSeed # undef XXH3_64bits_reset_withSecret # undef XXH3_64bits_update # undef XXH3_64bits_digest # undef XXH3_generateSecret /* XXH3_128bits */ # undef XXH128 # undef XXH3_128bits # undef XXH3_128bits_withSeed # undef XXH3_128bits_withSecret # undef XXH3_128bits_reset # undef XXH3_128bits_reset_withSeed # undef XXH3_128bits_reset_withSecret # undef XXH3_128bits_reset_withSecretandSeed # undef XXH3_128bits_update # undef XXH3_128bits_digest # undef XXH128_isEqual # undef XXH128_cmp # undef XXH128_canonicalFromHash # undef XXH128_hashFromCanonical /* Finally, free the namespace itself */ # undef XXH_NAMESPACE /* employ the namespace for XXH_INLINE_ALL */ # define XXH_NAMESPACE XXH_INLINE_ /* * Some identifiers (enums, type names) are not symbols, * but they must nonetheless be renamed to avoid redeclaration. * Alternative solution: do not redeclare them. * However, this requires some #ifdefs, and has a more dispersed impact. * Meanwhile, renaming can be achieved in a single place. */ # define XXH_IPREF(Id) XXH_NAMESPACE ## Id # define XXH_OK XXH_IPREF(XXH_OK) # define XXH_ERROR XXH_IPREF(XXH_ERROR) # define XXH_errorcode XXH_IPREF(XXH_errorcode) # define XXH32_canonical_t XXH_IPREF(XXH32_canonical_t) # define XXH64_canonical_t XXH_IPREF(XXH64_canonical_t) # define XXH128_canonical_t XXH_IPREF(XXH128_canonical_t) # define XXH32_state_s XXH_IPREF(XXH32_state_s) # define XXH32_state_t XXH_IPREF(XXH32_state_t) # define XXH64_state_s XXH_IPREF(XXH64_state_s) # define XXH64_state_t XXH_IPREF(XXH64_state_t) # define XXH3_state_s XXH_IPREF(XXH3_state_s) # define XXH3_state_t XXH_IPREF(XXH3_state_t) # define XXH128_hash_t XXH_IPREF(XXH128_hash_t) /* Ensure the header is parsed again, even if it was previously included */ # undef XXHASH_H_5627135585666179 # undef XXHASH_H_STATIC_13879238742 #endif /* XXH_INLINE_ALL || XXH_PRIVATE_API */ /* **************************************************************** * Stable API *****************************************************************/ #ifndef XXHASH_H_5627135585666179 #define XXHASH_H_5627135585666179 1 /*! @brief Marks a global symbol. */ #if !defined(XXH_INLINE_ALL) && !defined(XXH_PRIVATE_API) # if defined(WIN32) && defined(_MSC_VER) && (defined(XXH_IMPORT) || defined(XXH_EXPORT)) # ifdef XXH_EXPORT # define XXH_PUBLIC_API __declspec(dllexport) # elif XXH_IMPORT # define XXH_PUBLIC_API __declspec(dllimport) # endif # else # define XXH_PUBLIC_API /* do nothing */ # endif #endif #ifdef XXH_NAMESPACE # define XXH_CAT(A,B) A##B # define XXH_NAME2(A,B) XXH_CAT(A,B) # define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber) /* XXH32 */ # define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32) # define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState) # define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState) # define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset) # define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update) # define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest) # define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState) # define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash) # define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical) /* XXH64 */ # define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64) # define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState) # define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState) # define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset) # define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update) # define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest) # define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState) # define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash) # define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical) /* XXH3_64bits */ # define XXH3_64bits XXH_NAME2(XXH_NAMESPACE, XXH3_64bits) # define XXH3_64bits_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSecret) # define XXH3_64bits_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSeed) # define XXH3_64bits_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSecretandSeed) # define XXH3_createState XXH_NAME2(XXH_NAMESPACE, XXH3_createState) # define XXH3_freeState XXH_NAME2(XXH_NAMESPACE, XXH3_freeState) # define XXH3_copyState XXH_NAME2(XXH_NAMESPACE, XXH3_copyState) # define XXH3_64bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset) # define XXH3_64bits_reset_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSeed) # define XXH3_64bits_reset_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSecret) # define XXH3_64bits_reset_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSecretandSeed) # define XXH3_64bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_update) # define XXH3_64bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_digest) # define XXH3_generateSecret XXH_NAME2(XXH_NAMESPACE, XXH3_generateSecret) # define XXH3_generateSecret_fromSeed XXH_NAME2(XXH_NAMESPACE, XXH3_generateSecret_fromSeed) /* XXH3_128bits */ # define XXH128 XXH_NAME2(XXH_NAMESPACE, XXH128) # define XXH3_128bits XXH_NAME2(XXH_NAMESPACE, XXH3_128bits) # define XXH3_128bits_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSeed) # define XXH3_128bits_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSecret) # define XXH3_128bits_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSecretandSeed) # define XXH3_128bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset) # define XXH3_128bits_reset_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSeed) # define XXH3_128bits_reset_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSecret) # define XXH3_128bits_reset_withSecretandSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSecretandSeed) # define XXH3_128bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_update) # define XXH3_128bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_digest) # define XXH128_isEqual XXH_NAME2(XXH_NAMESPACE, XXH128_isEqual) # define XXH128_cmp XXH_NAME2(XXH_NAMESPACE, XXH128_cmp) # define XXH128_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH128_canonicalFromHash) # define XXH128_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH128_hashFromCanonical) #endif /* ************************************* * Compiler specifics ***************************************/ /* specific declaration modes for Windows */ #if !defined(XXH_INLINE_ALL) && !defined(XXH_PRIVATE_API) # if defined(WIN32) && defined(_MSC_VER) && (defined(XXH_IMPORT) || defined(XXH_EXPORT)) # ifdef XXH_EXPORT # define XXH_PUBLIC_API __declspec(dllexport) # elif XXH_IMPORT # define XXH_PUBLIC_API __declspec(dllimport) # endif # else # define XXH_PUBLIC_API /* do nothing */ # endif #endif #if defined (__GNUC__) # define XXH_CONSTF __attribute__((const)) # define XXH_PUREF __attribute__((pure)) # define XXH_MALLOCF __attribute__((malloc)) #else # define XXH_CONSTF /* disable */ # define XXH_PUREF # define XXH_MALLOCF #endif /* ************************************* * Version ***************************************/ #define XXH_VERSION_MAJOR 0 #define XXH_VERSION_MINOR 8 #define XXH_VERSION_RELEASE 2 /*! @brief Version number, encoded as two digits each */ #define XXH_VERSION_NUMBER (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE) /*! * @brief Obtains the xxHash version. * * This is mostly useful when xxHash is compiled as a shared library, * since the returned value comes from the library, as opposed to header file. * * @return @ref XXH_VERSION_NUMBER of the invoked library. */ XXH_PUBLIC_API XXH_CONSTF unsigned XXH_versionNumber (void); /* **************************** * Common basic types ******************************/ #include /* size_t */ /*! * @brief Exit code for the streaming API. */ typedef enum { XXH_OK = 0, /*!< OK */ XXH_ERROR /*!< Error */ } XXH_errorcode; /*-********************************************************************** * 32-bit hash ************************************************************************/ #if defined(XXH_DOXYGEN) /* Don't show include */ /*! * @brief An unsigned 32-bit integer. * * Not necessarily defined to `uint32_t` but functionally equivalent. */ typedef uint32_t XXH32_hash_t; #elif !defined (__VMS) \ && (defined (__cplusplus) \ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) # include typedef uint32_t XXH32_hash_t; #else # include # if UINT_MAX == 0xFFFFFFFFUL typedef unsigned int XXH32_hash_t; # elif ULONG_MAX == 0xFFFFFFFFUL typedef unsigned long XXH32_hash_t; # else # error "unsupported platform: need a 32-bit type" # endif #endif /*! * @} * * @defgroup XXH32_family XXH32 family * @ingroup public * Contains functions used in the classic 32-bit xxHash algorithm. * * @note * XXH32 is useful for older platforms, with no or poor 64-bit performance. * Note that the @ref XXH3_family provides competitive speed for both 32-bit * and 64-bit systems, and offers true 64/128 bit hash results. * * @see @ref XXH64_family, @ref XXH3_family : Other xxHash families * @see @ref XXH32_impl for implementation details * @{ */ /*! * @brief Calculates the 32-bit hash of @p input using xxHash32. * * Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark): 5.4 GB/s * * See @ref single_shot_example "Single Shot Example" for an example. * * @param input The block of data to be hashed, at least @p length bytes in size. * @param length The length of @p input, in bytes. * @param seed The 32-bit seed to alter the hash's output predictably. * * @pre * The memory between @p input and @p input + @p length must be valid, * readable, contiguous memory. However, if @p length is `0`, @p input may be * `NULL`. In C++, this also must be *TriviallyCopyable*. * * @return The calculated 32-bit hash value. * * @see * XXH64(), XXH3_64bits_withSeed(), XXH3_128bits_withSeed(), XXH128(): * Direct equivalents for the other variants of xxHash. * @see * XXH32_createState(), XXH32_update(), XXH32_digest(): Streaming version. */ XXH_PUBLIC_API XXH_PUREF XXH32_hash_t XXH32 (const void* input, size_t length, XXH32_hash_t seed); #ifndef XXH_NO_STREAM /*! * Streaming functions generate the xxHash value from an incremental input. * This method is slower than single-call functions, due to state management. * For small inputs, prefer `XXH32()` and `XXH64()`, which are better optimized. * * An XXH state must first be allocated using `XXH*_createState()`. * * Start a new hash by initializing the state with a seed using `XXH*_reset()`. * * Then, feed the hash state by calling `XXH*_update()` as many times as necessary. * * The function returns an error code, with 0 meaning OK, and any other value * meaning there is an error. * * Finally, a hash value can be produced anytime, by using `XXH*_digest()`. * This function returns the nn-bits hash as an int or long long. * * It's still possible to continue inserting input into the hash state after a * digest, and generate new hash values later on by invoking `XXH*_digest()`. * * When done, release the state using `XXH*_freeState()`. * * @see streaming_example at the top of @ref xxhash.h for an example. */ /*! * @typedef struct XXH32_state_s XXH32_state_t * @brief The opaque state struct for the XXH32 streaming API. * * @see XXH32_state_s for details. */ typedef struct XXH32_state_s XXH32_state_t; /*! * @brief Allocates an @ref XXH32_state_t. * * Must be freed with XXH32_freeState(). * @return An allocated XXH32_state_t on success, `NULL` on failure. */ XXH_PUBLIC_API XXH_MALLOCF XXH32_state_t* XXH32_createState(void); /*! * @brief Frees an @ref XXH32_state_t. * * Must be allocated with XXH32_createState(). * @param statePtr A pointer to an @ref XXH32_state_t allocated with @ref XXH32_createState(). * @return XXH_OK. */ XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr); /*! * @brief Copies one @ref XXH32_state_t to another. * * @param dst_state The state to copy to. * @param src_state The state to copy from. * @pre * @p dst_state and @p src_state must not be `NULL` and must not overlap. */ XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state); /*! * @brief Resets an @ref XXH32_state_t to begin a new hash. * * This function resets and seeds a state. Call it before @ref XXH32_update(). * * @param statePtr The state struct to reset. * @param seed The 32-bit seed to alter the hash result predictably. * * @pre * @p statePtr must not be `NULL`. * * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. */ XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, XXH32_hash_t seed); /*! * @brief Consumes a block of @p input to an @ref XXH32_state_t. * * Call this to incrementally consume blocks of data. * * @param statePtr The state struct to update. * @param input The block of data to be hashed, at least @p length bytes in size. * @param length The length of @p input, in bytes. * * @pre * @p statePtr must not be `NULL`. * @pre * The memory between @p input and @p input + @p length must be valid, * readable, contiguous memory. However, if @p length is `0`, @p input may be * `NULL`. In C++, this also must be *TriviallyCopyable*. * * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. */ XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length); /*! * @brief Returns the calculated hash value from an @ref XXH32_state_t. * * @note * Calling XXH32_digest() will not affect @p statePtr, so you can update, * digest, and update again. * * @param statePtr The state struct to calculate the hash from. * * @pre * @p statePtr must not be `NULL`. * * @return The calculated xxHash32 value from that state. */ XXH_PUBLIC_API XXH_PUREF XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr); #endif /* !XXH_NO_STREAM */ /******* Canonical representation *******/ /* * The default return values from XXH functions are unsigned 32 and 64 bit * integers. * This the simplest and fastest format for further post-processing. * * However, this leaves open the question of what is the order on the byte level, * since little and big endian conventions will store the same number differently. * * The canonical representation settles this issue by mandating big-endian * convention, the same convention as human-readable numbers (large digits first). * * When writing hash values to storage, sending them over a network, or printing * them, it's highly recommended to use the canonical representation to ensure * portability across a wider range of systems, present and future. * * The following functions allow transformation of hash values to and from * canonical format. */ /*! * @brief Canonical (big endian) representation of @ref XXH32_hash_t. */ typedef struct { unsigned char digest[4]; /*!< Hash bytes, big endian */ } XXH32_canonical_t; /*! * @brief Converts an @ref XXH32_hash_t to a big endian @ref XXH32_canonical_t. * * @param dst The @ref XXH32_canonical_t pointer to be stored to. * @param hash The @ref XXH32_hash_t to be converted. * * @pre * @p dst must not be `NULL`. */ XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash); /*! * @brief Converts an @ref XXH32_canonical_t to a native @ref XXH32_hash_t. * * @param src The @ref XXH32_canonical_t to convert. * * @pre * @p src must not be `NULL`. * * @return The converted hash. */ XXH_PUBLIC_API XXH_PUREF XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src); /*! @cond Doxygen ignores this part */ #ifdef __has_attribute # define XXH_HAS_ATTRIBUTE(x) __has_attribute(x) #else # define XXH_HAS_ATTRIBUTE(x) 0 #endif /*! @endcond */ /*! @cond Doxygen ignores this part */ /* * C23 __STDC_VERSION__ number hasn't been specified yet. For now * leave as `201711L` (C17 + 1). * TODO: Update to correct value when its been specified. */ #define XXH_C23_VN 201711L /*! @endcond */ /*! @cond Doxygen ignores this part */ /* C-language Attributes are added in C23. */ #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= XXH_C23_VN) && defined(__has_c_attribute) # define XXH_HAS_C_ATTRIBUTE(x) __has_c_attribute(x) #else # define XXH_HAS_C_ATTRIBUTE(x) 0 #endif /*! @endcond */ /*! @cond Doxygen ignores this part */ #if defined(__cplusplus) && defined(__has_cpp_attribute) # define XXH_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x) #else # define XXH_HAS_CPP_ATTRIBUTE(x) 0 #endif /*! @endcond */ /*! @cond Doxygen ignores this part */ /* * Define XXH_FALLTHROUGH macro for annotating switch case with the 'fallthrough' attribute * introduced in CPP17 and C23. * CPP17 : https://en.cppreference.com/w/cpp/language/attributes/fallthrough * C23 : https://en.cppreference.com/w/c/language/attributes/fallthrough */ #if XXH_HAS_C_ATTRIBUTE(fallthrough) || XXH_HAS_CPP_ATTRIBUTE(fallthrough) # define XXH_FALLTHROUGH [[fallthrough]] #elif XXH_HAS_ATTRIBUTE(__fallthrough__) # define XXH_FALLTHROUGH __attribute__ ((__fallthrough__)) #else # define XXH_FALLTHROUGH /* fallthrough */ #endif /*! @endcond */ /*! @cond Doxygen ignores this part */ /* * Define XXH_NOESCAPE for annotated pointers in public API. * https://clang.llvm.org/docs/AttributeReference.html#noescape * As of writing this, only supported by clang. */ #if XXH_HAS_ATTRIBUTE(noescape) # define XXH_NOESCAPE __attribute__((noescape)) #else # define XXH_NOESCAPE #endif /*! @endcond */ /*! * @} * @ingroup public * @{ */ #ifndef XXH_NO_LONG_LONG /*-********************************************************************** * 64-bit hash ************************************************************************/ #if defined(XXH_DOXYGEN) /* don't include */ /*! * @brief An unsigned 64-bit integer. * * Not necessarily defined to `uint64_t` but functionally equivalent. */ typedef uint64_t XXH64_hash_t; #elif !defined (__VMS) \ && (defined (__cplusplus) \ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) # include typedef uint64_t XXH64_hash_t; #else # include # if defined(__LP64__) && ULONG_MAX == 0xFFFFFFFFFFFFFFFFULL /* LP64 ABI says uint64_t is unsigned long */ typedef unsigned long XXH64_hash_t; # else /* the following type must have a width of 64-bit */ typedef unsigned long long XXH64_hash_t; # endif #endif /*! * @} * * @defgroup XXH64_family XXH64 family * @ingroup public * @{ * Contains functions used in the classic 64-bit xxHash algorithm. * * @note * XXH3 provides competitive speed for both 32-bit and 64-bit systems, * and offers true 64/128 bit hash results. * It provides better speed for systems with vector processing capabilities. */ /*! * @brief Calculates the 64-bit hash of @p input using xxHash64. * * This function usually runs faster on 64-bit systems, but slower on 32-bit * systems (see benchmark). * * @param input The block of data to be hashed, at least @p length bytes in size. * @param length The length of @p input, in bytes. * @param seed The 64-bit seed to alter the hash's output predictably. * * @pre * The memory between @p input and @p input + @p length must be valid, * readable, contiguous memory. However, if @p length is `0`, @p input may be * `NULL`. In C++, this also must be *TriviallyCopyable*. * * @return The calculated 64-bit hash. * * @see * XXH32(), XXH3_64bits_withSeed(), XXH3_128bits_withSeed(), XXH128(): * Direct equivalents for the other variants of xxHash. * @see * XXH64_createState(), XXH64_update(), XXH64_digest(): Streaming version. */ XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH64(XXH_NOESCAPE const void* input, size_t length, XXH64_hash_t seed); /******* Streaming *******/ #ifndef XXH_NO_STREAM /*! * @brief The opaque state struct for the XXH64 streaming API. * * @see XXH64_state_s for details. */ typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */ /*! * @brief Allocates an @ref XXH64_state_t. * * Must be freed with XXH64_freeState(). * @return An allocated XXH64_state_t on success, `NULL` on failure. */ XXH_PUBLIC_API XXH_MALLOCF XXH64_state_t* XXH64_createState(void); /*! * @brief Frees an @ref XXH64_state_t. * * Must be allocated with XXH64_createState(). * @param statePtr A pointer to an @ref XXH64_state_t allocated with @ref XXH64_createState(). * @return XXH_OK. */ XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr); /*! * @brief Copies one @ref XXH64_state_t to another. * * @param dst_state The state to copy to. * @param src_state The state to copy from. * @pre * @p dst_state and @p src_state must not be `NULL` and must not overlap. */ XXH_PUBLIC_API void XXH64_copyState(XXH_NOESCAPE XXH64_state_t* dst_state, const XXH64_state_t* src_state); /*! * @brief Resets an @ref XXH64_state_t to begin a new hash. * * This function resets and seeds a state. Call it before @ref XXH64_update(). * * @param statePtr The state struct to reset. * @param seed The 64-bit seed to alter the hash result predictably. * * @pre * @p statePtr must not be `NULL`. * * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. */ XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH_NOESCAPE XXH64_state_t* statePtr, XXH64_hash_t seed); /*! * @brief Consumes a block of @p input to an @ref XXH64_state_t. * * Call this to incrementally consume blocks of data. * * @param statePtr The state struct to update. * @param input The block of data to be hashed, at least @p length bytes in size. * @param length The length of @p input, in bytes. * * @pre * @p statePtr must not be `NULL`. * @pre * The memory between @p input and @p input + @p length must be valid, * readable, contiguous memory. However, if @p length is `0`, @p input may be * `NULL`. In C++, this also must be *TriviallyCopyable*. * * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. */ XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH_NOESCAPE XXH64_state_t* statePtr, XXH_NOESCAPE const void* input, size_t length); /*! * @brief Returns the calculated hash value from an @ref XXH64_state_t. * * @note * Calling XXH64_digest() will not affect @p statePtr, so you can update, * digest, and update again. * * @param statePtr The state struct to calculate the hash from. * * @pre * @p statePtr must not be `NULL`. * * @return The calculated xxHash64 value from that state. */ XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH64_digest (XXH_NOESCAPE const XXH64_state_t* statePtr); #endif /* !XXH_NO_STREAM */ /******* Canonical representation *******/ /*! * @brief Canonical (big endian) representation of @ref XXH64_hash_t. */ typedef struct { unsigned char digest[sizeof(XXH64_hash_t)]; } XXH64_canonical_t; /*! * @brief Converts an @ref XXH64_hash_t to a big endian @ref XXH64_canonical_t. * * @param dst The @ref XXH64_canonical_t pointer to be stored to. * @param hash The @ref XXH64_hash_t to be converted. * * @pre * @p dst must not be `NULL`. */ XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH_NOESCAPE XXH64_canonical_t* dst, XXH64_hash_t hash); /*! * @brief Converts an @ref XXH64_canonical_t to a native @ref XXH64_hash_t. * * @param src The @ref XXH64_canonical_t to convert. * * @pre * @p src must not be `NULL`. * * @return The converted hash. */ XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH64_hashFromCanonical(XXH_NOESCAPE const XXH64_canonical_t* src); #ifndef XXH_NO_XXH3 /*! * @} * ************************************************************************ * @defgroup XXH3_family XXH3 family * @ingroup public * @{ * * XXH3 is a more recent hash algorithm featuring: * - Improved speed for both small and large inputs * - True 64-bit and 128-bit outputs * - SIMD acceleration * - Improved 32-bit viability * * Speed analysis methodology is explained here: * * https://fastcompression.blogspot.com/2019/03/presenting-xxh3.html * * Compared to XXH64, expect XXH3 to run approximately * ~2x faster on large inputs and >3x faster on small ones, * exact differences vary depending on platform. * * XXH3's speed benefits greatly from SIMD and 64-bit arithmetic, * but does not require it. * Most 32-bit and 64-bit targets that can run XXH32 smoothly can run XXH3 * at competitive speeds, even without vector support. Further details are * explained in the implementation. * * XXH3 has a fast scalar implementation, but it also includes accelerated SIMD * implementations for many common platforms: * - AVX512 * - AVX2 * - SSE2 * - ARM NEON * - WebAssembly SIMD128 * - POWER8 VSX * - s390x ZVector * This can be controlled via the @ref XXH_VECTOR macro, but it automatically * selects the best version according to predefined macros. For the x86 family, an * automatic runtime dispatcher is included separately in @ref xxh_x86dispatch.c. * * XXH3 implementation is portable: * it has a generic C90 formulation that can be compiled on any platform, * all implementations generate exactly the same hash value on all platforms. * Starting from v0.8.0, it's also labelled "stable", meaning that * any future version will also generate the same hash value. * * XXH3 offers 2 variants, _64bits and _128bits. * * When only 64 bits are needed, prefer invoking the _64bits variant, as it * reduces the amount of mixing, resulting in faster speed on small inputs. * It's also generally simpler to manipulate a scalar return type than a struct. * * The API supports one-shot hashing, streaming mode, and custom secrets. */ /*-********************************************************************** * XXH3 64-bit variant ************************************************************************/ /*! * @brief 64-bit unseeded variant of XXH3. * * This is equivalent to @ref XXH3_64bits_withSeed() with a seed of 0, however * it may have slightly better performance due to constant propagation of the * defaults. * * @see * XXH32(), XXH64(), XXH3_128bits(): equivalent for the other xxHash algorithms * @see * XXH3_64bits_withSeed(), XXH3_64bits_withSecret(): other seeding variants * @see * XXH3_64bits_reset(), XXH3_64bits_update(), XXH3_64bits_digest(): Streaming version. */ XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH3_64bits(XXH_NOESCAPE const void* input, size_t length); /*! * @brief 64-bit seeded variant of XXH3 * * This variant generates a custom secret on the fly based on default secret * altered using the `seed` value. * * While this operation is decently fast, note that it's not completely free. * * @note * seed == 0 produces the same results as @ref XXH3_64bits(). * * @param input The data to hash * @param length The length * @param seed The 64-bit seed to alter the state. */ XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH3_64bits_withSeed(XXH_NOESCAPE const void* input, size_t length, XXH64_hash_t seed); /*! * The bare minimum size for a custom secret. * * @see * XXH3_64bits_withSecret(), XXH3_64bits_reset_withSecret(), * XXH3_128bits_withSecret(), XXH3_128bits_reset_withSecret(). */ #define XXH3_SECRET_SIZE_MIN 136 /*! * @brief 64-bit variant of XXH3 with a custom "secret". * * It's possible to provide any blob of bytes as a "secret" to generate the hash. * This makes it more difficult for an external actor to prepare an intentional collision. * The main condition is that secretSize *must* be large enough (>= XXH3_SECRET_SIZE_MIN). * However, the quality of the secret impacts the dispersion of the hash algorithm. * Therefore, the secret _must_ look like a bunch of random bytes. * Avoid "trivial" or structured data such as repeated sequences or a text document. * Whenever in doubt about the "randomness" of the blob of bytes, * consider employing "XXH3_generateSecret()" instead (see below). * It will generate a proper high entropy secret derived from the blob of bytes. * Another advantage of using XXH3_generateSecret() is that * it guarantees that all bits within the initial blob of bytes * will impact every bit of the output. * This is not necessarily the case when using the blob of bytes directly * because, when hashing _small_ inputs, only a portion of the secret is employed. */ XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH3_64bits_withSecret(XXH_NOESCAPE const void* data, size_t len, XXH_NOESCAPE const void* secret, size_t secretSize); /******* Streaming *******/ #ifndef XXH_NO_STREAM /* * Streaming requires state maintenance. * This operation costs memory and CPU. * As a consequence, streaming is slower than one-shot hashing. * For better performance, prefer one-shot functions whenever applicable. */ /*! * @brief The state struct for the XXH3 streaming API. * * @see XXH3_state_s for details. */ typedef struct XXH3_state_s XXH3_state_t; XXH_PUBLIC_API XXH_MALLOCF XXH3_state_t* XXH3_createState(void); XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr); /*! * @brief Copies one @ref XXH3_state_t to another. * * @param dst_state The state to copy to. * @param src_state The state to copy from. * @pre * @p dst_state and @p src_state must not be `NULL` and must not overlap. */ XXH_PUBLIC_API void XXH3_copyState(XXH_NOESCAPE XXH3_state_t* dst_state, XXH_NOESCAPE const XXH3_state_t* src_state); /*! * @brief Resets an @ref XXH3_state_t to begin a new hash. * * This function resets `statePtr` and generate a secret with default parameters. Call it before @ref XXH3_64bits_update(). * Digest will be equivalent to `XXH3_64bits()`. * * @param statePtr The state struct to reset. * * @pre * @p statePtr must not be `NULL`. * * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. * */ XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset(XXH_NOESCAPE XXH3_state_t* statePtr); /*! * @brief Resets an @ref XXH3_state_t with 64-bit seed to begin a new hash. * * This function resets `statePtr` and generate a secret from `seed`. Call it before @ref XXH3_64bits_update(). * Digest will be equivalent to `XXH3_64bits_withSeed()`. * * @param statePtr The state struct to reset. * @param seed The 64-bit seed to alter the state. * * @pre * @p statePtr must not be `NULL`. * * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. * */ XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH64_hash_t seed); /*! * XXH3_64bits_reset_withSecret(): * `secret` is referenced, it _must outlive_ the hash streaming session. * Similar to one-shot API, `secretSize` must be >= `XXH3_SECRET_SIZE_MIN`, * and the quality of produced hash values depends on secret's entropy * (secret's content should look like a bunch of random bytes). * When in doubt about the randomness of a candidate `secret`, * consider employing `XXH3_generateSecret()` instead (see below). */ XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSecret(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize); /*! * @brief Consumes a block of @p input to an @ref XXH3_state_t. * * Call this to incrementally consume blocks of data. * * @param statePtr The state struct to update. * @param input The block of data to be hashed, at least @p length bytes in size. * @param length The length of @p input, in bytes. * * @pre * @p statePtr must not be `NULL`. * @pre * The memory between @p input and @p input + @p length must be valid, * readable, contiguous memory. However, if @p length is `0`, @p input may be * `NULL`. In C++, this also must be *TriviallyCopyable*. * * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. */ XXH_PUBLIC_API XXH_errorcode XXH3_64bits_update (XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* input, size_t length); /*! * @brief Returns the calculated XXH3 64-bit hash value from an @ref XXH3_state_t. * * @note * Calling XXH3_64bits_digest() will not affect @p statePtr, so you can update, * digest, and update again. * * @param statePtr The state struct to calculate the hash from. * * @pre * @p statePtr must not be `NULL`. * * @return The calculated XXH3 64-bit hash value from that state. */ XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH3_64bits_digest (XXH_NOESCAPE const XXH3_state_t* statePtr); #endif /* !XXH_NO_STREAM */ /* note : canonical representation of XXH3 is the same as XXH64 * since they both produce XXH64_hash_t values */ /*-********************************************************************** * XXH3 128-bit variant ************************************************************************/ /*! * @brief The return value from 128-bit hashes. * * Stored in little endian order, although the fields themselves are in native * endianness. */ typedef struct { XXH64_hash_t low64; /*!< `value & 0xFFFFFFFFFFFFFFFF` */ XXH64_hash_t high64; /*!< `value >> 64` */ } XXH128_hash_t; /*! * @brief Unseeded 128-bit variant of XXH3 * * The 128-bit variant of XXH3 has more strength, but it has a bit of overhead * for shorter inputs. * * This is equivalent to @ref XXH3_128bits_withSeed() with a seed of 0, however * it may have slightly better performance due to constant propagation of the * defaults. * * @see * XXH32(), XXH64(), XXH3_64bits(): equivalent for the other xxHash algorithms * @see * XXH3_128bits_withSeed(), XXH3_128bits_withSecret(): other seeding variants * @see * XXH3_128bits_reset(), XXH3_128bits_update(), XXH3_128bits_digest(): Streaming version. */ XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits(XXH_NOESCAPE const void* data, size_t len); /*! @brief Seeded 128-bit variant of XXH3. @see XXH3_64bits_withSeed(). */ XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits_withSeed(XXH_NOESCAPE const void* data, size_t len, XXH64_hash_t seed); /*! @brief Custom secret 128-bit variant of XXH3. @see XXH3_64bits_withSecret(). */ XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits_withSecret(XXH_NOESCAPE const void* data, size_t len, XXH_NOESCAPE const void* secret, size_t secretSize); /******* Streaming *******/ #ifndef XXH_NO_STREAM /* * Streaming requires state maintenance. * This operation costs memory and CPU. * As a consequence, streaming is slower than one-shot hashing. * For better performance, prefer one-shot functions whenever applicable. * * XXH3_128bits uses the same XXH3_state_t as XXH3_64bits(). * Use already declared XXH3_createState() and XXH3_freeState(). * * All reset and streaming functions have same meaning as their 64-bit counterpart. */ /*! * @brief Resets an @ref XXH3_state_t to begin a new hash. * * This function resets `statePtr` and generate a secret with default parameters. Call it before @ref XXH3_128bits_update(). * Digest will be equivalent to `XXH3_128bits()`. * * @param statePtr The state struct to reset. * * @pre * @p statePtr must not be `NULL`. * * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. * */ XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset(XXH_NOESCAPE XXH3_state_t* statePtr); /*! * @brief Resets an @ref XXH3_state_t with 64-bit seed to begin a new hash. * * This function resets `statePtr` and generate a secret from `seed`. Call it before @ref XXH3_128bits_update(). * Digest will be equivalent to `XXH3_128bits_withSeed()`. * * @param statePtr The state struct to reset. * @param seed The 64-bit seed to alter the state. * * @pre * @p statePtr must not be `NULL`. * * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. * */ XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH64_hash_t seed); /*! @brief Custom secret 128-bit variant of XXH3. @see XXH_64bits_reset_withSecret(). */ XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSecret(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize); /*! * @brief Consumes a block of @p input to an @ref XXH3_state_t. * * Call this to incrementally consume blocks of data. * * @param statePtr The state struct to update. * @param input The block of data to be hashed, at least @p length bytes in size. * @param length The length of @p input, in bytes. * * @pre * @p statePtr must not be `NULL`. * @pre * The memory between @p input and @p input + @p length must be valid, * readable, contiguous memory. However, if @p length is `0`, @p input may be * `NULL`. In C++, this also must be *TriviallyCopyable*. * * @return @ref XXH_OK on success, @ref XXH_ERROR on failure. */ XXH_PUBLIC_API XXH_errorcode XXH3_128bits_update (XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* input, size_t length); /*! * @brief Returns the calculated XXH3 128-bit hash value from an @ref XXH3_state_t. * * @note * Calling XXH3_128bits_digest() will not affect @p statePtr, so you can update, * digest, and update again. * * @param statePtr The state struct to calculate the hash from. * * @pre * @p statePtr must not be `NULL`. * * @return The calculated XXH3 128-bit hash value from that state. */ XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits_digest (XXH_NOESCAPE const XXH3_state_t* statePtr); #endif /* !XXH_NO_STREAM */ /* Following helper functions make it possible to compare XXH128_hast_t values. * Since XXH128_hash_t is a structure, this capability is not offered by the language. * Note: For better performance, these functions can be inlined using XXH_INLINE_ALL */ /*! * XXH128_isEqual(): * Return: 1 if `h1` and `h2` are equal, 0 if they are not. */ XXH_PUBLIC_API XXH_PUREF int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2); /*! * @brief Compares two @ref XXH128_hash_t * This comparator is compatible with stdlib's `qsort()`/`bsearch()`. * * @return: >0 if *h128_1 > *h128_2 * =0 if *h128_1 == *h128_2 * <0 if *h128_1 < *h128_2 */ XXH_PUBLIC_API XXH_PUREF int XXH128_cmp(XXH_NOESCAPE const void* h128_1, XXH_NOESCAPE const void* h128_2); /******* Canonical representation *******/ typedef struct { unsigned char digest[sizeof(XXH128_hash_t)]; } XXH128_canonical_t; /*! * @brief Converts an @ref XXH128_hash_t to a big endian @ref XXH128_canonical_t. * * @param dst The @ref XXH128_canonical_t pointer to be stored to. * @param hash The @ref XXH128_hash_t to be converted. * * @pre * @p dst must not be `NULL`. */ XXH_PUBLIC_API void XXH128_canonicalFromHash(XXH_NOESCAPE XXH128_canonical_t* dst, XXH128_hash_t hash); /*! * @brief Converts an @ref XXH128_canonical_t to a native @ref XXH128_hash_t. * * @param src The @ref XXH128_canonical_t to convert. * * @pre * @p src must not be `NULL`. * * @return The converted hash. */ XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH128_hashFromCanonical(XXH_NOESCAPE const XXH128_canonical_t* src); #endif /* !XXH_NO_XXH3 */ #endif /* XXH_NO_LONG_LONG */ /*! * @} */ #endif /* XXHASH_H_5627135585666179 */ #if defined(XXH_STATIC_LINKING_ONLY) && !defined(XXHASH_H_STATIC_13879238742) #define XXHASH_H_STATIC_13879238742 /* **************************************************************************** * This section contains declarations which are not guaranteed to remain stable. * They may change in future versions, becoming incompatible with a different * version of the library. * These declarations should only be used with static linking. * Never use them in association with dynamic linking! ***************************************************************************** */ /* * These definitions are only present to allow static allocation * of XXH states, on stack or in a struct, for example. * Never **ever** access their members directly. */ /*! * @internal * @brief Structure for XXH32 streaming API. * * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY, * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. Otherwise it is * an opaque type. This allows fields to safely be changed. * * Typedef'd to @ref XXH32_state_t. * Do not access the members of this struct directly. * @see XXH64_state_s, XXH3_state_s */ struct XXH32_state_s { XXH32_hash_t total_len_32; /*!< Total length hashed, modulo 2^32 */ XXH32_hash_t large_len; /*!< Whether the hash is >= 16 (handles @ref total_len_32 overflow) */ XXH32_hash_t v[4]; /*!< Accumulator lanes */ XXH32_hash_t mem32[4]; /*!< Internal buffer for partial reads. Treated as unsigned char[16]. */ XXH32_hash_t memsize; /*!< Amount of data in @ref mem32 */ XXH32_hash_t reserved; /*!< Reserved field. Do not read nor write to it. */ }; /* typedef'd to XXH32_state_t */ #ifndef XXH_NO_LONG_LONG /* defined when there is no 64-bit support */ /*! * @internal * @brief Structure for XXH64 streaming API. * * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY, * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. Otherwise it is * an opaque type. This allows fields to safely be changed. * * Typedef'd to @ref XXH64_state_t. * Do not access the members of this struct directly. * @see XXH32_state_s, XXH3_state_s */ struct XXH64_state_s { XXH64_hash_t total_len; /*!< Total length hashed. This is always 64-bit. */ XXH64_hash_t v[4]; /*!< Accumulator lanes */ XXH64_hash_t mem64[4]; /*!< Internal buffer for partial reads. Treated as unsigned char[32]. */ XXH32_hash_t memsize; /*!< Amount of data in @ref mem64 */ XXH32_hash_t reserved32; /*!< Reserved field, needed for padding anyways*/ XXH64_hash_t reserved64; /*!< Reserved field. Do not read or write to it. */ }; /* typedef'd to XXH64_state_t */ #ifndef XXH_NO_XXH3 #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* >= C11 */ # include # define XXH_ALIGN(n) alignas(n) #elif defined(__cplusplus) && (__cplusplus >= 201103L) /* >= C++11 */ /* In C++ alignas() is a keyword */ # define XXH_ALIGN(n) alignas(n) #elif defined(__GNUC__) # define XXH_ALIGN(n) __attribute__ ((aligned(n))) #elif defined(_MSC_VER) # define XXH_ALIGN(n) __declspec(align(n)) #else # define XXH_ALIGN(n) /* disabled */ #endif /* Old GCC versions only accept the attribute after the type in structures. */ #if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) /* C11+ */ \ && ! (defined(__cplusplus) && (__cplusplus >= 201103L)) /* >= C++11 */ \ && defined(__GNUC__) # define XXH_ALIGN_MEMBER(align, type) type XXH_ALIGN(align) #else # define XXH_ALIGN_MEMBER(align, type) XXH_ALIGN(align) type #endif /*! * @brief The size of the internal XXH3 buffer. * * This is the optimal update size for incremental hashing. * * @see XXH3_64b_update(), XXH3_128b_update(). */ #define XXH3_INTERNALBUFFER_SIZE 256 /*! * @internal * @brief Default size of the secret buffer (and @ref XXH3_kSecret). * * This is the size used in @ref XXH3_kSecret and the seeded functions. * * Not to be confused with @ref XXH3_SECRET_SIZE_MIN. */ #define XXH3_SECRET_DEFAULT_SIZE 192 /*! * @internal * @brief Structure for XXH3 streaming API. * * @note This is only defined when @ref XXH_STATIC_LINKING_ONLY, * @ref XXH_INLINE_ALL, or @ref XXH_IMPLEMENTATION is defined. * Otherwise it is an opaque type. * Never use this definition in combination with dynamic library. * This allows fields to safely be changed in the future. * * @note ** This structure has a strict alignment requirement of 64 bytes!! ** * Do not allocate this with `malloc()` or `new`, * it will not be sufficiently aligned. * Use @ref XXH3_createState() and @ref XXH3_freeState(), or stack allocation. * * Typedef'd to @ref XXH3_state_t. * Do never access the members of this struct directly. * * @see XXH3_INITSTATE() for stack initialization. * @see XXH3_createState(), XXH3_freeState(). * @see XXH32_state_s, XXH64_state_s */ struct XXH3_state_s { XXH_ALIGN_MEMBER(64, XXH64_hash_t acc[8]); /*!< The 8 accumulators. See @ref XXH32_state_s::v and @ref XXH64_state_s::v */ XXH_ALIGN_MEMBER(64, unsigned char customSecret[XXH3_SECRET_DEFAULT_SIZE]); /*!< Used to store a custom secret generated from a seed. */ XXH_ALIGN_MEMBER(64, unsigned char buffer[XXH3_INTERNALBUFFER_SIZE]); /*!< The internal buffer. @see XXH32_state_s::mem32 */ XXH32_hash_t bufferedSize; /*!< The amount of memory in @ref buffer, @see XXH32_state_s::memsize */ XXH32_hash_t useSeed; /*!< Reserved field. Needed for padding on 64-bit. */ size_t nbStripesSoFar; /*!< Number or stripes processed. */ XXH64_hash_t totalLen; /*!< Total length hashed. 64-bit even on 32-bit targets. */ size_t nbStripesPerBlock; /*!< Number of stripes per block. */ size_t secretLimit; /*!< Size of @ref customSecret or @ref extSecret */ XXH64_hash_t seed; /*!< Seed for _withSeed variants. Must be zero otherwise, @see XXH3_INITSTATE() */ XXH64_hash_t reserved64; /*!< Reserved field. */ const unsigned char* extSecret; /*!< Reference to an external secret for the _withSecret variants, NULL * for other variants. */ /* note: there may be some padding at the end due to alignment on 64 bytes */ }; /* typedef'd to XXH3_state_t */ #undef XXH_ALIGN_MEMBER /*! * @brief Initializes a stack-allocated `XXH3_state_s`. * * When the @ref XXH3_state_t structure is merely emplaced on stack, * it should be initialized with XXH3_INITSTATE() or a memset() * in case its first reset uses XXH3_NNbits_reset_withSeed(). * This init can be omitted if the first reset uses default or _withSecret mode. * This operation isn't necessary when the state is created with XXH3_createState(). * Note that this doesn't prepare the state for a streaming operation, * it's still necessary to use XXH3_NNbits_reset*() afterwards. */ #define XXH3_INITSTATE(XXH3_state_ptr) \ do { \ XXH3_state_t* tmp_xxh3_state_ptr = (XXH3_state_ptr); \ tmp_xxh3_state_ptr->seed = 0; \ tmp_xxh3_state_ptr->extSecret = NULL; \ } while(0) /*! * simple alias to pre-selected XXH3_128bits variant */ XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH128(XXH_NOESCAPE const void* data, size_t len, XXH64_hash_t seed); /* === Experimental API === */ /* Symbols defined below must be considered tied to a specific library version. */ /*! * XXH3_generateSecret(): * * Derive a high-entropy secret from any user-defined content, named customSeed. * The generated secret can be used in combination with `*_withSecret()` functions. * The `_withSecret()` variants are useful to provide a higher level of protection * than 64-bit seed, as it becomes much more difficult for an external actor to * guess how to impact the calculation logic. * * The function accepts as input a custom seed of any length and any content, * and derives from it a high-entropy secret of length @p secretSize into an * already allocated buffer @p secretBuffer. * * The generated secret can then be used with any `*_withSecret()` variant. * The functions @ref XXH3_128bits_withSecret(), @ref XXH3_64bits_withSecret(), * @ref XXH3_128bits_reset_withSecret() and @ref XXH3_64bits_reset_withSecret() * are part of this list. They all accept a `secret` parameter * which must be large enough for implementation reasons (>= @ref XXH3_SECRET_SIZE_MIN) * _and_ feature very high entropy (consist of random-looking bytes). * These conditions can be a high bar to meet, so @ref XXH3_generateSecret() can * be employed to ensure proper quality. * * @p customSeed can be anything. It can have any size, even small ones, * and its content can be anything, even "poor entropy" sources such as a bunch * of zeroes. The resulting `secret` will nonetheless provide all required qualities. * * @pre * - @p secretSize must be >= @ref XXH3_SECRET_SIZE_MIN * - When @p customSeedSize > 0, supplying NULL as customSeed is undefined behavior. * * Example code: * @code{.c} * #include * #include * #include * #define XXH_STATIC_LINKING_ONLY // expose unstable API * #include "xxhash.h" * // Hashes argv[2] using the entropy from argv[1]. * int main(int argc, char* argv[]) * { * char secret[XXH3_SECRET_SIZE_MIN]; * if (argv != 3) { return 1; } * XXH3_generateSecret(secret, sizeof(secret), argv[1], strlen(argv[1])); * XXH64_hash_t h = XXH3_64bits_withSecret( * argv[2], strlen(argv[2]), * secret, sizeof(secret) * ); * printf("%016llx\n", (unsigned long long) h); * } * @endcode */ XXH_PUBLIC_API XXH_errorcode XXH3_generateSecret(XXH_NOESCAPE void* secretBuffer, size_t secretSize, XXH_NOESCAPE const void* customSeed, size_t customSeedSize); /*! * @brief Generate the same secret as the _withSeed() variants. * * The generated secret can be used in combination with *`*_withSecret()` and `_withSecretandSeed()` variants. * * Example C++ `std::string` hash class: * @code{.cpp} * #include * #define XXH_STATIC_LINKING_ONLY // expose unstable API * #include "xxhash.h" * // Slow, seeds each time * class HashSlow { * XXH64_hash_t seed; * public: * HashSlow(XXH64_hash_t s) : seed{s} {} * size_t operator()(const std::string& x) const { * return size_t{XXH3_64bits_withSeed(x.c_str(), x.length(), seed)}; * } * }; * // Fast, caches the seeded secret for future uses. * class HashFast { * unsigned char secret[XXH3_SECRET_SIZE_MIN]; * public: * HashFast(XXH64_hash_t s) { * XXH3_generateSecret_fromSeed(secret, seed); * } * size_t operator()(const std::string& x) const { * return size_t{ * XXH3_64bits_withSecret(x.c_str(), x.length(), secret, sizeof(secret)) * }; * } * }; * @endcode * @param secretBuffer A writable buffer of @ref XXH3_SECRET_SIZE_MIN bytes * @param seed The seed to seed the state. */ XXH_PUBLIC_API void XXH3_generateSecret_fromSeed(XXH_NOESCAPE void* secretBuffer, XXH64_hash_t seed); /*! * These variants generate hash values using either * @p seed for "short" keys (< XXH3_MIDSIZE_MAX = 240 bytes) * or @p secret for "large" keys (>= XXH3_MIDSIZE_MAX). * * This generally benefits speed, compared to `_withSeed()` or `_withSecret()`. * `_withSeed()` has to generate the secret on the fly for "large" keys. * It's fast, but can be perceptible for "not so large" keys (< 1 KB). * `_withSecret()` has to generate the masks on the fly for "small" keys, * which requires more instructions than _withSeed() variants. * Therefore, _withSecretandSeed variant combines the best of both worlds. * * When @p secret has been generated by XXH3_generateSecret_fromSeed(), * this variant produces *exactly* the same results as `_withSeed()` variant, * hence offering only a pure speed benefit on "large" input, * by skipping the need to regenerate the secret for every large input. * * Another usage scenario is to hash the secret to a 64-bit hash value, * for example with XXH3_64bits(), which then becomes the seed, * and then employ both the seed and the secret in _withSecretandSeed(). * On top of speed, an added benefit is that each bit in the secret * has a 50% chance to swap each bit in the output, via its impact to the seed. * * This is not guaranteed when using the secret directly in "small data" scenarios, * because only portions of the secret are employed for small data. */ XXH_PUBLIC_API XXH_PUREF XXH64_hash_t XXH3_64bits_withSecretandSeed(XXH_NOESCAPE const void* data, size_t len, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed); /*! @copydoc XXH3_64bits_withSecretandSeed() */ XXH_PUBLIC_API XXH_PUREF XXH128_hash_t XXH3_128bits_withSecretandSeed(XXH_NOESCAPE const void* input, size_t length, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed64); #ifndef XXH_NO_STREAM /*! @copydoc XXH3_64bits_withSecretandSeed() */ XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSecretandSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed64); /*! @copydoc XXH3_64bits_withSecretandSeed() */ XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSecretandSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed64); #endif /* !XXH_NO_STREAM */ #endif /* !XXH_NO_XXH3 */ #endif /* XXH_NO_LONG_LONG */ #if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) # define XXH_IMPLEMENTATION #endif #endif /* defined(XXH_STATIC_LINKING_ONLY) && !defined(XXHASH_H_STATIC_13879238742) */ /* ======================================================================== */ /* ======================================================================== */ /* ======================================================================== */ /*-********************************************************************** * xxHash implementation *-********************************************************************** * xxHash's implementation used to be hosted inside xxhash.c. * * However, inlining requires implementation to be visible to the compiler, * hence be included alongside the header. * Previously, implementation was hosted inside xxhash.c, * which was then #included when inlining was activated. * This construction created issues with a few build and install systems, * as it required xxhash.c to be stored in /include directory. * * xxHash implementation is now directly integrated within xxhash.h. * As a consequence, xxhash.c is no longer needed in /include. * * xxhash.c is still available and is still useful. * In a "normal" setup, when xxhash is not inlined, * xxhash.h only exposes the prototypes and public symbols, * while xxhash.c can be built into an object file xxhash.o * which can then be linked into the final binary. ************************************************************************/ #if ( defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) \ || defined(XXH_IMPLEMENTATION) ) && !defined(XXH_IMPLEM_13a8737387) # define XXH_IMPLEM_13a8737387 /* ************************************* * Tuning parameters ***************************************/ /*! * @defgroup tuning Tuning parameters * @{ * * Various macros to control xxHash's behavior. */ #ifdef XXH_DOXYGEN /*! * @brief Define this to disable 64-bit code. * * Useful if only using the @ref XXH32_family and you have a strict C90 compiler. */ # define XXH_NO_LONG_LONG # undef XXH_NO_LONG_LONG /* don't actually */ /*! * @brief Controls how unaligned memory is accessed. * * By default, access to unaligned memory is controlled by `memcpy()`, which is * safe and portable. * * Unfortunately, on some target/compiler combinations, the generated assembly * is sub-optimal. * * The below switch allow selection of a different access method * in the search for improved performance. * * @par Possible options: * * - `XXH_FORCE_MEMORY_ACCESS=0` (default): `memcpy` * @par * Use `memcpy()`. Safe and portable. Note that most modern compilers will * eliminate the function call and treat it as an unaligned access. * * - `XXH_FORCE_MEMORY_ACCESS=1`: `__attribute__((aligned(1)))` * @par * Depends on compiler extensions and is therefore not portable. * This method is safe _if_ your compiler supports it, * and *generally* as fast or faster than `memcpy`. * * - `XXH_FORCE_MEMORY_ACCESS=2`: Direct cast * @par * Casts directly and dereferences. This method doesn't depend on the * compiler, but it violates the C standard as it directly dereferences an * unaligned pointer. It can generate buggy code on targets which do not * support unaligned memory accesses, but in some circumstances, it's the * only known way to get the most performance. * * - `XXH_FORCE_MEMORY_ACCESS=3`: Byteshift * @par * Also portable. This can generate the best code on old compilers which don't * inline small `memcpy()` calls, and it might also be faster on big-endian * systems which lack a native byteswap instruction. However, some compilers * will emit literal byteshifts even if the target supports unaligned access. * * * @warning * Methods 1 and 2 rely on implementation-defined behavior. Use these with * care, as what works on one compiler/platform/optimization level may cause * another to read garbage data or even crash. * * See https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html for details. * * Prefer these methods in priority order (0 > 3 > 1 > 2) */ # define XXH_FORCE_MEMORY_ACCESS 0 /*! * @def XXH_SIZE_OPT * @brief Controls how much xxHash optimizes for size. * * xxHash, when compiled, tends to result in a rather large binary size. This * is mostly due to heavy usage to forced inlining and constant folding of the * @ref XXH3_family to increase performance. * * However, some developers prefer size over speed. This option can * significantly reduce the size of the generated code. When using the `-Os` * or `-Oz` options on GCC or Clang, this is defined to 1 by default, * otherwise it is defined to 0. * * Most of these size optimizations can be controlled manually. * * This is a number from 0-2. * - `XXH_SIZE_OPT` == 0: Default. xxHash makes no size optimizations. Speed * comes first. * - `XXH_SIZE_OPT` == 1: Default for `-Os` and `-Oz`. xxHash is more * conservative and disables hacks that increase code size. It implies the * options @ref XXH_NO_INLINE_HINTS == 1, @ref XXH_FORCE_ALIGN_CHECK == 0, * and @ref XXH3_NEON_LANES == 8 if they are not already defined. * - `XXH_SIZE_OPT` == 2: xxHash tries to make itself as small as possible. * Performance may cry. For example, the single shot functions just use the * streaming API. */ # define XXH_SIZE_OPT 0 /*! * @def XXH_FORCE_ALIGN_CHECK * @brief If defined to non-zero, adds a special path for aligned inputs (XXH32() * and XXH64() only). * * This is an important performance trick for architectures without decent * unaligned memory access performance. * * It checks for input alignment, and when conditions are met, uses a "fast * path" employing direct 32-bit/64-bit reads, resulting in _dramatically * faster_ read speed. * * The check costs one initial branch per hash, which is generally negligible, * but not zero. * * Moreover, it's not useful to generate an additional code path if memory * access uses the same instruction for both aligned and unaligned * addresses (e.g. x86 and aarch64). * * In these cases, the alignment check can be removed by setting this macro to 0. * Then the code will always use unaligned memory access. * Align check is automatically disabled on x86, x64, ARM64, and some ARM chips * which are platforms known to offer good unaligned memory accesses performance. * * It is also disabled by default when @ref XXH_SIZE_OPT >= 1. * * This option does not affect XXH3 (only XXH32 and XXH64). */ # define XXH_FORCE_ALIGN_CHECK 0 /*! * @def XXH_NO_INLINE_HINTS * @brief When non-zero, sets all functions to `static`. * * By default, xxHash tries to force the compiler to inline almost all internal * functions. * * This can usually improve performance due to reduced jumping and improved * constant folding, but significantly increases the size of the binary which * might not be favorable. * * Additionally, sometimes the forced inlining can be detrimental to performance, * depending on the architecture. * * XXH_NO_INLINE_HINTS marks all internal functions as static, giving the * compiler full control on whether to inline or not. * * When not optimizing (-O0), using `-fno-inline` with GCC or Clang, or if * @ref XXH_SIZE_OPT >= 1, this will automatically be defined. */ # define XXH_NO_INLINE_HINTS 0 /*! * @def XXH3_INLINE_SECRET * @brief Determines whether to inline the XXH3 withSecret code. * * When the secret size is known, the compiler can improve the performance * of XXH3_64bits_withSecret() and XXH3_128bits_withSecret(). * * However, if the secret size is not known, it doesn't have any benefit. This * happens when xxHash is compiled into a global symbol. Therefore, if * @ref XXH_INLINE_ALL is *not* defined, this will be defined to 0. * * Additionally, this defaults to 0 on GCC 12+, which has an issue with function pointers * that are *sometimes* force inline on -Og, and it is impossible to automatically * detect this optimization level. */ # define XXH3_INLINE_SECRET 0 /*! * @def XXH32_ENDJMP * @brief Whether to use a jump for `XXH32_finalize`. * * For performance, `XXH32_finalize` uses multiple branches in the finalizer. * This is generally preferable for performance, * but depending on exact architecture, a jmp may be preferable. * * This setting is only possibly making a difference for very small inputs. */ # define XXH32_ENDJMP 0 /*! * @internal * @brief Redefines old internal names. * * For compatibility with code that uses xxHash's internals before the names * were changed to improve namespacing. There is no other reason to use this. */ # define XXH_OLD_NAMES # undef XXH_OLD_NAMES /* don't actually use, it is ugly. */ /*! * @def XXH_NO_STREAM * @brief Disables the streaming API. * * When xxHash is not inlined and the streaming functions are not used, disabling * the streaming functions can improve code size significantly, especially with * the @ref XXH3_family which tends to make constant folded copies of itself. */ # define XXH_NO_STREAM # undef XXH_NO_STREAM /* don't actually */ #endif /* XXH_DOXYGEN */ /*! * @} */ #ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ /* prefer __packed__ structures (method 1) for GCC * < ARMv7 with unaligned access (e.g. Raspbian armhf) still uses byte shifting, so we use memcpy * which for some reason does unaligned loads. */ # if defined(__GNUC__) && !(defined(__ARM_ARCH) && __ARM_ARCH < 7 && defined(__ARM_FEATURE_UNALIGNED)) # define XXH_FORCE_MEMORY_ACCESS 1 # endif #endif #ifndef XXH_SIZE_OPT /* default to 1 for -Os or -Oz */ # if (defined(__GNUC__) || defined(__clang__)) && defined(__OPTIMIZE_SIZE__) # define XXH_SIZE_OPT 1 # else # define XXH_SIZE_OPT 0 # endif #endif #ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */ /* don't check on sizeopt, x86, aarch64, or arm when unaligned access is available */ # if XXH_SIZE_OPT >= 1 || \ defined(__i386) || defined(__x86_64__) || defined(__aarch64__) || defined(__ARM_FEATURE_UNALIGNED) \ || defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM64) || defined(_M_ARM) /* visual */ # define XXH_FORCE_ALIGN_CHECK 0 # else # define XXH_FORCE_ALIGN_CHECK 1 # endif #endif #ifndef XXH_NO_INLINE_HINTS # if XXH_SIZE_OPT >= 1 || defined(__NO_INLINE__) /* -O0, -fno-inline */ # define XXH_NO_INLINE_HINTS 1 # else # define XXH_NO_INLINE_HINTS 0 # endif #endif #ifndef XXH3_INLINE_SECRET # if (defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 12) \ || !defined(XXH_INLINE_ALL) # define XXH3_INLINE_SECRET 0 # else # define XXH3_INLINE_SECRET 1 # endif #endif #ifndef XXH32_ENDJMP /* generally preferable for performance */ # define XXH32_ENDJMP 0 #endif /*! * @defgroup impl Implementation * @{ */ /* ************************************* * Includes & Memory related functions ***************************************/ #if defined(XXH_NO_STREAM) /* nothing */ #elif defined(XXH_NO_STDLIB) /* When requesting to disable any mention of stdlib, * the library loses the ability to invoked malloc / free. * In practice, it means that functions like `XXH*_createState()` * will always fail, and return NULL. * This flag is useful in situations where * xxhash.h is integrated into some kernel, embedded or limited environment * without access to dynamic allocation. */ static XXH_CONSTF void* XXH_malloc(size_t s) { (void)s; return NULL; } static void XXH_free(void* p) { (void)p; } #else /* * Modify the local functions below should you wish to use * different memory routines for malloc() and free() */ #include /*! * @internal * @brief Modify this function to use a different routine than malloc(). */ static XXH_MALLOCF void* XXH_malloc(size_t s) { return malloc(s); } /*! * @internal * @brief Modify this function to use a different routine than free(). */ static void XXH_free(void* p) { free(p); } #endif /* XXH_NO_STDLIB */ #include /*! * @internal * @brief Modify this function to use a different routine than memcpy(). */ static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); } #include /* ULLONG_MAX */ /* ************************************* * Compiler Specific Options ***************************************/ #ifdef _MSC_VER /* Visual Studio warning fix */ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ #endif #if XXH_NO_INLINE_HINTS /* disable inlining hints */ # if defined(__GNUC__) || defined(__clang__) # define XXH_FORCE_INLINE static __attribute__((unused)) # else # define XXH_FORCE_INLINE static # endif # define XXH_NO_INLINE static /* enable inlining hints */ #elif defined(__GNUC__) || defined(__clang__) # define XXH_FORCE_INLINE static __inline__ __attribute__((always_inline, unused)) # define XXH_NO_INLINE static __attribute__((noinline)) #elif defined(_MSC_VER) /* Visual Studio */ # define XXH_FORCE_INLINE static __forceinline # define XXH_NO_INLINE static __declspec(noinline) #elif defined (__cplusplus) \ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) /* C99 */ # define XXH_FORCE_INLINE static inline # define XXH_NO_INLINE static #else # define XXH_FORCE_INLINE static # define XXH_NO_INLINE static #endif #if XXH3_INLINE_SECRET # define XXH3_WITH_SECRET_INLINE XXH_FORCE_INLINE #else # define XXH3_WITH_SECRET_INLINE XXH_NO_INLINE #endif /* ************************************* * Debug ***************************************/ /*! * @ingroup tuning * @def XXH_DEBUGLEVEL * @brief Sets the debugging level. * * XXH_DEBUGLEVEL is expected to be defined externally, typically via the * compiler's command line options. The value must be a number. */ #ifndef XXH_DEBUGLEVEL # ifdef DEBUGLEVEL /* backwards compat */ # define XXH_DEBUGLEVEL DEBUGLEVEL # else # define XXH_DEBUGLEVEL 0 # endif #endif #if (XXH_DEBUGLEVEL>=1) # include /* note: can still be disabled with NDEBUG */ # define XXH_ASSERT(c) assert(c) #else # if defined(__INTEL_COMPILER) # define XXH_ASSERT(c) XXH_ASSUME((unsigned char) (c)) # else # define XXH_ASSERT(c) XXH_ASSUME(c) # endif #endif /* note: use after variable declarations */ #ifndef XXH_STATIC_ASSERT # if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* C11 */ # define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { _Static_assert((c),m); } while(0) # elif defined(__cplusplus) && (__cplusplus >= 201103L) /* C++11 */ # define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { static_assert((c),m); } while(0) # else # define XXH_STATIC_ASSERT_WITH_MESSAGE(c,m) do { struct xxh_sa { char x[(c) ? 1 : -1]; }; } while(0) # endif # define XXH_STATIC_ASSERT(c) XXH_STATIC_ASSERT_WITH_MESSAGE((c),#c) #endif /*! * @internal * @def XXH_COMPILER_GUARD(var) * @brief Used to prevent unwanted optimizations for @p var. * * It uses an empty GCC inline assembly statement with a register constraint * which forces @p var into a general purpose register (eg eax, ebx, ecx * on x86) and marks it as modified. * * This is used in a few places to avoid unwanted autovectorization (e.g. * XXH32_round()). All vectorization we want is explicit via intrinsics, * and _usually_ isn't wanted elsewhere. * * We also use it to prevent unwanted constant folding for AArch64 in * XXH3_initCustomSecret_scalar(). */ #if defined(__GNUC__) || defined(__clang__) # define XXH_COMPILER_GUARD(var) __asm__("" : "+r" (var)) #else # define XXH_COMPILER_GUARD(var) ((void)0) #endif /* Specifically for NEON vectors which use the "w" constraint, on * Clang. */ #if defined(__clang__) && defined(__ARM_ARCH) && !defined(__wasm__) # define XXH_COMPILER_GUARD_CLANG_NEON(var) __asm__("" : "+w" (var)) #else # define XXH_COMPILER_GUARD_CLANG_NEON(var) ((void)0) #endif /* ************************************* * Basic Types ***************************************/ #if !defined (__VMS) \ && (defined (__cplusplus) \ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) # include typedef uint8_t xxh_u8; #else typedef unsigned char xxh_u8; #endif typedef XXH32_hash_t xxh_u32; #ifdef XXH_OLD_NAMES # warning "XXH_OLD_NAMES is planned to be removed starting v0.9. If the program depends on it, consider moving away from it by employing newer type names directly" # define BYTE xxh_u8 # define U8 xxh_u8 # define U32 xxh_u32 #endif /* *** Memory access *** */ /*! * @internal * @fn xxh_u32 XXH_read32(const void* ptr) * @brief Reads an unaligned 32-bit integer from @p ptr in native endianness. * * Affected by @ref XXH_FORCE_MEMORY_ACCESS. * * @param ptr The pointer to read from. * @return The 32-bit native endian integer from the bytes at @p ptr. */ /*! * @internal * @fn xxh_u32 XXH_readLE32(const void* ptr) * @brief Reads an unaligned 32-bit little endian integer from @p ptr. * * Affected by @ref XXH_FORCE_MEMORY_ACCESS. * * @param ptr The pointer to read from. * @return The 32-bit little endian integer from the bytes at @p ptr. */ /*! * @internal * @fn xxh_u32 XXH_readBE32(const void* ptr) * @brief Reads an unaligned 32-bit big endian integer from @p ptr. * * Affected by @ref XXH_FORCE_MEMORY_ACCESS. * * @param ptr The pointer to read from. * @return The 32-bit big endian integer from the bytes at @p ptr. */ /*! * @internal * @fn xxh_u32 XXH_readLE32_align(const void* ptr, XXH_alignment align) * @brief Like @ref XXH_readLE32(), but has an option for aligned reads. * * Affected by @ref XXH_FORCE_MEMORY_ACCESS. * Note that when @ref XXH_FORCE_ALIGN_CHECK == 0, the @p align parameter is * always @ref XXH_alignment::XXH_unaligned. * * @param ptr The pointer to read from. * @param align Whether @p ptr is aligned. * @pre * If @p align == @ref XXH_alignment::XXH_aligned, @p ptr must be 4 byte * aligned. * @return The 32-bit little endian integer from the bytes at @p ptr. */ #if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3)) /* * Manual byteshift. Best for old compilers which don't inline memcpy. * We actually directly use XXH_readLE32 and XXH_readBE32. */ #elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) /* * Force direct memory access. Only works on CPU which support unaligned memory * access in hardware. */ static xxh_u32 XXH_read32(const void* memPtr) { return *(const xxh_u32*) memPtr; } #elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) /* * __attribute__((aligned(1))) is supported by gcc and clang. Originally the * documentation claimed that it only increased the alignment, but actually it * can decrease it on gcc, clang, and icc: * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69502, * https://gcc.godbolt.org/z/xYez1j67Y. */ #ifdef XXH_OLD_NAMES typedef union { xxh_u32 u32; } __attribute__((packed)) unalign; #endif static xxh_u32 XXH_read32(const void* ptr) { typedef __attribute__((aligned(1))) xxh_u32 xxh_unalign32; return *((const xxh_unalign32*)ptr); } #else /* * Portable and safe solution. Generally efficient. * see: https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html */ static xxh_u32 XXH_read32(const void* memPtr) { xxh_u32 val; XXH_memcpy(&val, memPtr, sizeof(val)); return val; } #endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ /* *** Endianness *** */ /*! * @ingroup tuning * @def XXH_CPU_LITTLE_ENDIAN * @brief Whether the target is little endian. * * Defined to 1 if the target is little endian, or 0 if it is big endian. * It can be defined externally, for example on the compiler command line. * * If it is not defined, * a runtime check (which is usually constant folded) is used instead. * * @note * This is not necessarily defined to an integer constant. * * @see XXH_isLittleEndian() for the runtime check. */ #ifndef XXH_CPU_LITTLE_ENDIAN /* * Try to detect endianness automatically, to avoid the nonstandard behavior * in `XXH_isLittleEndian()` */ # if defined(_WIN32) /* Windows is always little endian */ \ || defined(__LITTLE_ENDIAN__) \ || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) # define XXH_CPU_LITTLE_ENDIAN 1 # elif defined(__BIG_ENDIAN__) \ || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) # define XXH_CPU_LITTLE_ENDIAN 0 # else /*! * @internal * @brief Runtime check for @ref XXH_CPU_LITTLE_ENDIAN. * * Most compilers will constant fold this. */ static int XXH_isLittleEndian(void) { /* * Portable and well-defined behavior. * Don't use static: it is detrimental to performance. */ const union { xxh_u32 u; xxh_u8 c[4]; } one = { 1 }; return one.c[0]; } # define XXH_CPU_LITTLE_ENDIAN XXH_isLittleEndian() # endif #endif /* **************************************** * Compiler-specific Functions and Macros ******************************************/ #define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) #ifdef __has_builtin # define XXH_HAS_BUILTIN(x) __has_builtin(x) #else # define XXH_HAS_BUILTIN(x) 0 #endif /* * C23 and future versions have standard "unreachable()". * Once it has been implemented reliably we can add it as an * additional case: * * ``` * #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= XXH_C23_VN) * # include * # ifdef unreachable * # define XXH_UNREACHABLE() unreachable() * # endif * #endif * ``` * * Note C++23 also has std::unreachable() which can be detected * as follows: * ``` * #if defined(__cpp_lib_unreachable) && (__cpp_lib_unreachable >= 202202L) * # include * # define XXH_UNREACHABLE() std::unreachable() * #endif * ``` * NB: `__cpp_lib_unreachable` is defined in the `` header. * We don't use that as including `` in `extern "C"` blocks * doesn't work on GCC12 */ #if XXH_HAS_BUILTIN(__builtin_unreachable) # define XXH_UNREACHABLE() __builtin_unreachable() #elif defined(_MSC_VER) # define XXH_UNREACHABLE() __assume(0) #else # define XXH_UNREACHABLE() #endif #if XXH_HAS_BUILTIN(__builtin_assume) # define XXH_ASSUME(c) __builtin_assume(c) #else # define XXH_ASSUME(c) if (!(c)) { XXH_UNREACHABLE(); } #endif /*! * @internal * @def XXH_rotl32(x,r) * @brief 32-bit rotate left. * * @param x The 32-bit integer to be rotated. * @param r The number of bits to rotate. * @pre * @p r > 0 && @p r < 32 * @note * @p x and @p r may be evaluated multiple times. * @return The rotated result. */ #if !defined(NO_CLANG_BUILTIN) && XXH_HAS_BUILTIN(__builtin_rotateleft32) \ && XXH_HAS_BUILTIN(__builtin_rotateleft64) # define XXH_rotl32 __builtin_rotateleft32 # define XXH_rotl64 __builtin_rotateleft64 /* Note: although _rotl exists for minGW (GCC under windows), performance seems poor */ #elif defined(_MSC_VER) # define XXH_rotl32(x,r) _rotl(x,r) # define XXH_rotl64(x,r) _rotl64(x,r) #else # define XXH_rotl32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) # define XXH_rotl64(x,r) (((x) << (r)) | ((x) >> (64 - (r)))) #endif /*! * @internal * @fn xxh_u32 XXH_swap32(xxh_u32 x) * @brief A 32-bit byteswap. * * @param x The 32-bit integer to byteswap. * @return @p x, byteswapped. */ #if defined(_MSC_VER) /* Visual Studio */ # define XXH_swap32 _byteswap_ulong #elif XXH_GCC_VERSION >= 403 # define XXH_swap32 __builtin_bswap32 #else static xxh_u32 XXH_swap32 (xxh_u32 x) { return ((x << 24) & 0xff000000 ) | ((x << 8) & 0x00ff0000 ) | ((x >> 8) & 0x0000ff00 ) | ((x >> 24) & 0x000000ff ); } #endif /* *************************** * Memory reads *****************************/ /*! * @internal * @brief Enum to indicate whether a pointer is aligned. */ typedef enum { XXH_aligned, /*!< Aligned */ XXH_unaligned /*!< Possibly unaligned */ } XXH_alignment; /* * XXH_FORCE_MEMORY_ACCESS==3 is an endian-independent byteshift load. * * This is ideal for older compilers which don't inline memcpy. */ #if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3)) XXH_FORCE_INLINE xxh_u32 XXH_readLE32(const void* memPtr) { const xxh_u8* bytePtr = (const xxh_u8 *)memPtr; return bytePtr[0] | ((xxh_u32)bytePtr[1] << 8) | ((xxh_u32)bytePtr[2] << 16) | ((xxh_u32)bytePtr[3] << 24); } XXH_FORCE_INLINE xxh_u32 XXH_readBE32(const void* memPtr) { const xxh_u8* bytePtr = (const xxh_u8 *)memPtr; return bytePtr[3] | ((xxh_u32)bytePtr[2] << 8) | ((xxh_u32)bytePtr[1] << 16) | ((xxh_u32)bytePtr[0] << 24); } #else XXH_FORCE_INLINE xxh_u32 XXH_readLE32(const void* ptr) { return XXH_CPU_LITTLE_ENDIAN ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr)); } static xxh_u32 XXH_readBE32(const void* ptr) { return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr); } #endif XXH_FORCE_INLINE xxh_u32 XXH_readLE32_align(const void* ptr, XXH_alignment align) { if (align==XXH_unaligned) { return XXH_readLE32(ptr); } else { return XXH_CPU_LITTLE_ENDIAN ? *(const xxh_u32*)ptr : XXH_swap32(*(const xxh_u32*)ptr); } } /* ************************************* * Misc ***************************************/ /*! @ingroup public */ XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; } /* ******************************************************************* * 32-bit hash functions *********************************************************************/ /*! * @} * @defgroup XXH32_impl XXH32 implementation * @ingroup impl * * Details on the XXH32 implementation. * @{ */ /* #define instead of static const, to be used as initializers */ #define XXH_PRIME32_1 0x9E3779B1U /*!< 0b10011110001101110111100110110001 */ #define XXH_PRIME32_2 0x85EBCA77U /*!< 0b10000101111010111100101001110111 */ #define XXH_PRIME32_3 0xC2B2AE3DU /*!< 0b11000010101100101010111000111101 */ #define XXH_PRIME32_4 0x27D4EB2FU /*!< 0b00100111110101001110101100101111 */ #define XXH_PRIME32_5 0x165667B1U /*!< 0b00010110010101100110011110110001 */ #ifdef XXH_OLD_NAMES # define PRIME32_1 XXH_PRIME32_1 # define PRIME32_2 XXH_PRIME32_2 # define PRIME32_3 XXH_PRIME32_3 # define PRIME32_4 XXH_PRIME32_4 # define PRIME32_5 XXH_PRIME32_5 #endif /*! * @internal * @brief Normal stripe processing routine. * * This shuffles the bits so that any bit from @p input impacts several bits in * @p acc. * * @param acc The accumulator lane. * @param input The stripe of input to mix. * @return The mixed accumulator lane. */ static xxh_u32 XXH32_round(xxh_u32 acc, xxh_u32 input) { acc += input * XXH_PRIME32_2; acc = XXH_rotl32(acc, 13); acc *= XXH_PRIME32_1; #if (defined(__SSE4_1__) || defined(__aarch64__) || defined(__wasm_simd128__)) && !defined(XXH_ENABLE_AUTOVECTORIZE) /* * UGLY HACK: * A compiler fence is the only thing that prevents GCC and Clang from * autovectorizing the XXH32 loop (pragmas and attributes don't work for some * reason) without globally disabling SSE4.1. * * The reason we want to avoid vectorization is because despite working on * 4 integers at a time, there are multiple factors slowing XXH32 down on * SSE4: * - There's a ridiculous amount of lag from pmulld (10 cycles of latency on * newer chips!) making it slightly slower to multiply four integers at * once compared to four integers independently. Even when pmulld was * fastest, Sandy/Ivy Bridge, it is still not worth it to go into SSE * just to multiply unless doing a long operation. * * - Four instructions are required to rotate, * movqda tmp, v // not required with VEX encoding * pslld tmp, 13 // tmp <<= 13 * psrld v, 19 // x >>= 19 * por v, tmp // x |= tmp * compared to one for scalar: * roll v, 13 // reliably fast across the board * shldl v, v, 13 // Sandy Bridge and later prefer this for some reason * * - Instruction level parallelism is actually more beneficial here because * the SIMD actually serializes this operation: While v1 is rotating, v2 * can load data, while v3 can multiply. SSE forces them to operate * together. * * This is also enabled on AArch64, as Clang is *very aggressive* in vectorizing * the loop. NEON is only faster on the A53, and with the newer cores, it is less * than half the speed. * * Additionally, this is used on WASM SIMD128 because it JITs to the same * SIMD instructions and has the same issue. */ XXH_COMPILER_GUARD(acc); #endif return acc; } /*! * @internal * @brief Mixes all bits to finalize the hash. * * The final mix ensures that all input bits have a chance to impact any bit in * the output digest, resulting in an unbiased distribution. * * @param hash The hash to avalanche. * @return The avalanched hash. */ static xxh_u32 XXH32_avalanche(xxh_u32 hash) { hash ^= hash >> 15; hash *= XXH_PRIME32_2; hash ^= hash >> 13; hash *= XXH_PRIME32_3; hash ^= hash >> 16; return hash; } #define XXH_get32bits(p) XXH_readLE32_align(p, align) /*! * @internal * @brief Processes the last 0-15 bytes of @p ptr. * * There may be up to 15 bytes remaining to consume from the input. * This final stage will digest them to ensure that all input bytes are present * in the final mix. * * @param hash The hash to finalize. * @param ptr The pointer to the remaining input. * @param len The remaining length, modulo 16. * @param align Whether @p ptr is aligned. * @return The finalized hash. * @see XXH64_finalize(). */ static XXH_PUREF xxh_u32 XXH32_finalize(xxh_u32 hash, const xxh_u8* ptr, size_t len, XXH_alignment align) { #define XXH_PROCESS1 do { \ hash += (*ptr++) * XXH_PRIME32_5; \ hash = XXH_rotl32(hash, 11) * XXH_PRIME32_1; \ } while (0) #define XXH_PROCESS4 do { \ hash += XXH_get32bits(ptr) * XXH_PRIME32_3; \ ptr += 4; \ hash = XXH_rotl32(hash, 17) * XXH_PRIME32_4; \ } while (0) if (ptr==NULL) XXH_ASSERT(len == 0); /* Compact rerolled version; generally faster */ if (!XXH32_ENDJMP) { len &= 15; while (len >= 4) { XXH_PROCESS4; len -= 4; } while (len > 0) { XXH_PROCESS1; --len; } return XXH32_avalanche(hash); } else { switch(len&15) /* or switch(bEnd - p) */ { case 12: XXH_PROCESS4; XXH_FALLTHROUGH; /* fallthrough */ case 8: XXH_PROCESS4; XXH_FALLTHROUGH; /* fallthrough */ case 4: XXH_PROCESS4; return XXH32_avalanche(hash); case 13: XXH_PROCESS4; XXH_FALLTHROUGH; /* fallthrough */ case 9: XXH_PROCESS4; XXH_FALLTHROUGH; /* fallthrough */ case 5: XXH_PROCESS4; XXH_PROCESS1; return XXH32_avalanche(hash); case 14: XXH_PROCESS4; XXH_FALLTHROUGH; /* fallthrough */ case 10: XXH_PROCESS4; XXH_FALLTHROUGH; /* fallthrough */ case 6: XXH_PROCESS4; XXH_PROCESS1; XXH_PROCESS1; return XXH32_avalanche(hash); case 15: XXH_PROCESS4; XXH_FALLTHROUGH; /* fallthrough */ case 11: XXH_PROCESS4; XXH_FALLTHROUGH; /* fallthrough */ case 7: XXH_PROCESS4; XXH_FALLTHROUGH; /* fallthrough */ case 3: XXH_PROCESS1; XXH_FALLTHROUGH; /* fallthrough */ case 2: XXH_PROCESS1; XXH_FALLTHROUGH; /* fallthrough */ case 1: XXH_PROCESS1; XXH_FALLTHROUGH; /* fallthrough */ case 0: return XXH32_avalanche(hash); } XXH_ASSERT(0); return hash; /* reaching this point is deemed impossible */ } } #ifdef XXH_OLD_NAMES # define PROCESS1 XXH_PROCESS1 # define PROCESS4 XXH_PROCESS4 #else # undef XXH_PROCESS1 # undef XXH_PROCESS4 #endif /*! * @internal * @brief The implementation for @ref XXH32(). * * @param input , len , seed Directly passed from @ref XXH32(). * @param align Whether @p input is aligned. * @return The calculated hash. */ XXH_FORCE_INLINE XXH_PUREF xxh_u32 XXH32_endian_align(const xxh_u8* input, size_t len, xxh_u32 seed, XXH_alignment align) { xxh_u32 h32; if (input==NULL) XXH_ASSERT(len == 0); if (len>=16) { const xxh_u8* const bEnd = input + len; const xxh_u8* const limit = bEnd - 15; xxh_u32 v1 = seed + XXH_PRIME32_1 + XXH_PRIME32_2; xxh_u32 v2 = seed + XXH_PRIME32_2; xxh_u32 v3 = seed + 0; xxh_u32 v4 = seed - XXH_PRIME32_1; do { v1 = XXH32_round(v1, XXH_get32bits(input)); input += 4; v2 = XXH32_round(v2, XXH_get32bits(input)); input += 4; v3 = XXH32_round(v3, XXH_get32bits(input)); input += 4; v4 = XXH32_round(v4, XXH_get32bits(input)); input += 4; } while (input < limit); h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18); } else { h32 = seed + XXH_PRIME32_5; } h32 += (xxh_u32)len; return XXH32_finalize(h32, input, len&15, align); } /*! @ingroup XXH32_family */ XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t len, XXH32_hash_t seed) { #if !defined(XXH_NO_STREAM) && XXH_SIZE_OPT >= 2 /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ XXH32_state_t state; XXH32_reset(&state, seed); XXH32_update(&state, (const xxh_u8*)input, len); return XXH32_digest(&state); #else if (XXH_FORCE_ALIGN_CHECK) { if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */ return XXH32_endian_align((const xxh_u8*)input, len, seed, XXH_aligned); } } return XXH32_endian_align((const xxh_u8*)input, len, seed, XXH_unaligned); #endif } /******* Hash streaming *******/ #ifndef XXH_NO_STREAM /*! @ingroup XXH32_family */ XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void) { return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t)); } /*! @ingroup XXH32_family */ XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr) { XXH_free(statePtr); return XXH_OK; } /*! @ingroup XXH32_family */ XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState) { XXH_memcpy(dstState, srcState, sizeof(*dstState)); } /*! @ingroup XXH32_family */ XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, XXH32_hash_t seed) { XXH_ASSERT(statePtr != NULL); memset(statePtr, 0, sizeof(*statePtr)); statePtr->v[0] = seed + XXH_PRIME32_1 + XXH_PRIME32_2; statePtr->v[1] = seed + XXH_PRIME32_2; statePtr->v[2] = seed + 0; statePtr->v[3] = seed - XXH_PRIME32_1; return XXH_OK; } /*! @ingroup XXH32_family */ XXH_PUBLIC_API XXH_errorcode XXH32_update(XXH32_state_t* state, const void* input, size_t len) { if (input==NULL) { XXH_ASSERT(len == 0); return XXH_OK; } { const xxh_u8* p = (const xxh_u8*)input; const xxh_u8* const bEnd = p + len; state->total_len_32 += (XXH32_hash_t)len; state->large_len |= (XXH32_hash_t)((len>=16) | (state->total_len_32>=16)); if (state->memsize + len < 16) { /* fill in tmp buffer */ XXH_memcpy((xxh_u8*)(state->mem32) + state->memsize, input, len); state->memsize += (XXH32_hash_t)len; return XXH_OK; } if (state->memsize) { /* some data left from previous update */ XXH_memcpy((xxh_u8*)(state->mem32) + state->memsize, input, 16-state->memsize); { const xxh_u32* p32 = state->mem32; state->v[0] = XXH32_round(state->v[0], XXH_readLE32(p32)); p32++; state->v[1] = XXH32_round(state->v[1], XXH_readLE32(p32)); p32++; state->v[2] = XXH32_round(state->v[2], XXH_readLE32(p32)); p32++; state->v[3] = XXH32_round(state->v[3], XXH_readLE32(p32)); } p += 16-state->memsize; state->memsize = 0; } if (p <= bEnd-16) { const xxh_u8* const limit = bEnd - 16; do { state->v[0] = XXH32_round(state->v[0], XXH_readLE32(p)); p+=4; state->v[1] = XXH32_round(state->v[1], XXH_readLE32(p)); p+=4; state->v[2] = XXH32_round(state->v[2], XXH_readLE32(p)); p+=4; state->v[3] = XXH32_round(state->v[3], XXH_readLE32(p)); p+=4; } while (p<=limit); } if (p < bEnd) { XXH_memcpy(state->mem32, p, (size_t)(bEnd-p)); state->memsize = (unsigned)(bEnd-p); } } return XXH_OK; } /*! @ingroup XXH32_family */ XXH_PUBLIC_API XXH32_hash_t XXH32_digest(const XXH32_state_t* state) { xxh_u32 h32; if (state->large_len) { h32 = XXH_rotl32(state->v[0], 1) + XXH_rotl32(state->v[1], 7) + XXH_rotl32(state->v[2], 12) + XXH_rotl32(state->v[3], 18); } else { h32 = state->v[2] /* == seed */ + XXH_PRIME32_5; } h32 += state->total_len_32; return XXH32_finalize(h32, (const xxh_u8*)state->mem32, state->memsize, XXH_aligned); } #endif /* !XXH_NO_STREAM */ /******* Canonical representation *******/ /*! * @ingroup XXH32_family * The default return values from XXH functions are unsigned 32 and 64 bit * integers. * * The canonical representation uses big endian convention, the same convention * as human-readable numbers (large digits first). * * This way, hash values can be written into a file or buffer, remaining * comparable across different systems. * * The following functions allow transformation of hash values to and from their * canonical format. */ XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash) { XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t)); if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash); XXH_memcpy(dst, &hash, sizeof(*dst)); } /*! @ingroup XXH32_family */ XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src) { return XXH_readBE32(src); } #ifndef XXH_NO_LONG_LONG /* ******************************************************************* * 64-bit hash functions *********************************************************************/ /*! * @} * @ingroup impl * @{ */ /******* Memory access *******/ typedef XXH64_hash_t xxh_u64; #ifdef XXH_OLD_NAMES # define U64 xxh_u64 #endif #if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3)) /* * Manual byteshift. Best for old compilers which don't inline memcpy. * We actually directly use XXH_readLE64 and XXH_readBE64. */ #elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) /* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ static xxh_u64 XXH_read64(const void* memPtr) { return *(const xxh_u64*) memPtr; } #elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) /* * __attribute__((aligned(1))) is supported by gcc and clang. Originally the * documentation claimed that it only increased the alignment, but actually it * can decrease it on gcc, clang, and icc: * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=69502, * https://gcc.godbolt.org/z/xYez1j67Y. */ #ifdef XXH_OLD_NAMES typedef union { xxh_u32 u32; xxh_u64 u64; } __attribute__((packed)) unalign64; #endif static xxh_u64 XXH_read64(const void* ptr) { typedef __attribute__((aligned(1))) xxh_u64 xxh_unalign64; return *((const xxh_unalign64*)ptr); } #else /* * Portable and safe solution. Generally efficient. * see: https://fastcompression.blogspot.com/2015/08/accessing-unaligned-memory.html */ static xxh_u64 XXH_read64(const void* memPtr) { xxh_u64 val; XXH_memcpy(&val, memPtr, sizeof(val)); return val; } #endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ #if defined(_MSC_VER) /* Visual Studio */ # define XXH_swap64 _byteswap_uint64 #elif XXH_GCC_VERSION >= 403 # define XXH_swap64 __builtin_bswap64 #else static xxh_u64 XXH_swap64(xxh_u64 x) { return ((x << 56) & 0xff00000000000000ULL) | ((x << 40) & 0x00ff000000000000ULL) | ((x << 24) & 0x0000ff0000000000ULL) | ((x << 8) & 0x000000ff00000000ULL) | ((x >> 8) & 0x00000000ff000000ULL) | ((x >> 24) & 0x0000000000ff0000ULL) | ((x >> 40) & 0x000000000000ff00ULL) | ((x >> 56) & 0x00000000000000ffULL); } #endif /* XXH_FORCE_MEMORY_ACCESS==3 is an endian-independent byteshift load. */ #if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==3)) XXH_FORCE_INLINE xxh_u64 XXH_readLE64(const void* memPtr) { const xxh_u8* bytePtr = (const xxh_u8 *)memPtr; return bytePtr[0] | ((xxh_u64)bytePtr[1] << 8) | ((xxh_u64)bytePtr[2] << 16) | ((xxh_u64)bytePtr[3] << 24) | ((xxh_u64)bytePtr[4] << 32) | ((xxh_u64)bytePtr[5] << 40) | ((xxh_u64)bytePtr[6] << 48) | ((xxh_u64)bytePtr[7] << 56); } XXH_FORCE_INLINE xxh_u64 XXH_readBE64(const void* memPtr) { const xxh_u8* bytePtr = (const xxh_u8 *)memPtr; return bytePtr[7] | ((xxh_u64)bytePtr[6] << 8) | ((xxh_u64)bytePtr[5] << 16) | ((xxh_u64)bytePtr[4] << 24) | ((xxh_u64)bytePtr[3] << 32) | ((xxh_u64)bytePtr[2] << 40) | ((xxh_u64)bytePtr[1] << 48) | ((xxh_u64)bytePtr[0] << 56); } #else XXH_FORCE_INLINE xxh_u64 XXH_readLE64(const void* ptr) { return XXH_CPU_LITTLE_ENDIAN ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr)); } static xxh_u64 XXH_readBE64(const void* ptr) { return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr); } #endif XXH_FORCE_INLINE xxh_u64 XXH_readLE64_align(const void* ptr, XXH_alignment align) { if (align==XXH_unaligned) return XXH_readLE64(ptr); else return XXH_CPU_LITTLE_ENDIAN ? *(const xxh_u64*)ptr : XXH_swap64(*(const xxh_u64*)ptr); } /******* xxh64 *******/ /*! * @} * @defgroup XXH64_impl XXH64 implementation * @ingroup impl * * Details on the XXH64 implementation. * @{ */ /* #define rather that static const, to be used as initializers */ #define XXH_PRIME64_1 0x9E3779B185EBCA87ULL /*!< 0b1001111000110111011110011011000110000101111010111100101010000111 */ #define XXH_PRIME64_2 0xC2B2AE3D27D4EB4FULL /*!< 0b1100001010110010101011100011110100100111110101001110101101001111 */ #define XXH_PRIME64_3 0x165667B19E3779F9ULL /*!< 0b0001011001010110011001111011000110011110001101110111100111111001 */ #define XXH_PRIME64_4 0x85EBCA77C2B2AE63ULL /*!< 0b1000010111101011110010100111011111000010101100101010111001100011 */ #define XXH_PRIME64_5 0x27D4EB2F165667C5ULL /*!< 0b0010011111010100111010110010111100010110010101100110011111000101 */ #ifdef XXH_OLD_NAMES # define PRIME64_1 XXH_PRIME64_1 # define PRIME64_2 XXH_PRIME64_2 # define PRIME64_3 XXH_PRIME64_3 # define PRIME64_4 XXH_PRIME64_4 # define PRIME64_5 XXH_PRIME64_5 #endif /*! @copydoc XXH32_round */ static xxh_u64 XXH64_round(xxh_u64 acc, xxh_u64 input) { acc += input * XXH_PRIME64_2; acc = XXH_rotl64(acc, 31); acc *= XXH_PRIME64_1; return acc; } static xxh_u64 XXH64_mergeRound(xxh_u64 acc, xxh_u64 val) { val = XXH64_round(0, val); acc ^= val; acc = acc * XXH_PRIME64_1 + XXH_PRIME64_4; return acc; } /*! @copydoc XXH32_avalanche */ static xxh_u64 XXH64_avalanche(xxh_u64 hash) { hash ^= hash >> 33; hash *= XXH_PRIME64_2; hash ^= hash >> 29; hash *= XXH_PRIME64_3; hash ^= hash >> 32; return hash; } #define XXH_get64bits(p) XXH_readLE64_align(p, align) /*! * @internal * @brief Processes the last 0-31 bytes of @p ptr. * * There may be up to 31 bytes remaining to consume from the input. * This final stage will digest them to ensure that all input bytes are present * in the final mix. * * @param hash The hash to finalize. * @param ptr The pointer to the remaining input. * @param len The remaining length, modulo 32. * @param align Whether @p ptr is aligned. * @return The finalized hash * @see XXH32_finalize(). */ static XXH_PUREF xxh_u64 XXH64_finalize(xxh_u64 hash, const xxh_u8* ptr, size_t len, XXH_alignment align) { if (ptr==NULL) XXH_ASSERT(len == 0); len &= 31; while (len >= 8) { xxh_u64 const k1 = XXH64_round(0, XXH_get64bits(ptr)); ptr += 8; hash ^= k1; hash = XXH_rotl64(hash,27) * XXH_PRIME64_1 + XXH_PRIME64_4; len -= 8; } if (len >= 4) { hash ^= (xxh_u64)(XXH_get32bits(ptr)) * XXH_PRIME64_1; ptr += 4; hash = XXH_rotl64(hash, 23) * XXH_PRIME64_2 + XXH_PRIME64_3; len -= 4; } while (len > 0) { hash ^= (*ptr++) * XXH_PRIME64_5; hash = XXH_rotl64(hash, 11) * XXH_PRIME64_1; --len; } return XXH64_avalanche(hash); } #ifdef XXH_OLD_NAMES # define PROCESS1_64 XXH_PROCESS1_64 # define PROCESS4_64 XXH_PROCESS4_64 # define PROCESS8_64 XXH_PROCESS8_64 #else # undef XXH_PROCESS1_64 # undef XXH_PROCESS4_64 # undef XXH_PROCESS8_64 #endif /*! * @internal * @brief The implementation for @ref XXH64(). * * @param input , len , seed Directly passed from @ref XXH64(). * @param align Whether @p input is aligned. * @return The calculated hash. */ XXH_FORCE_INLINE XXH_PUREF xxh_u64 XXH64_endian_align(const xxh_u8* input, size_t len, xxh_u64 seed, XXH_alignment align) { xxh_u64 h64; if (input==NULL) XXH_ASSERT(len == 0); if (len>=32) { const xxh_u8* const bEnd = input + len; const xxh_u8* const limit = bEnd - 31; xxh_u64 v1 = seed + XXH_PRIME64_1 + XXH_PRIME64_2; xxh_u64 v2 = seed + XXH_PRIME64_2; xxh_u64 v3 = seed + 0; xxh_u64 v4 = seed - XXH_PRIME64_1; do { v1 = XXH64_round(v1, XXH_get64bits(input)); input+=8; v2 = XXH64_round(v2, XXH_get64bits(input)); input+=8; v3 = XXH64_round(v3, XXH_get64bits(input)); input+=8; v4 = XXH64_round(v4, XXH_get64bits(input)); input+=8; } while (input= 2 /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ XXH64_state_t state; XXH64_reset(&state, seed); XXH64_update(&state, (const xxh_u8*)input, len); return XXH64_digest(&state); #else if (XXH_FORCE_ALIGN_CHECK) { if ((((size_t)input) & 7)==0) { /* Input is aligned, let's leverage the speed advantage */ return XXH64_endian_align((const xxh_u8*)input, len, seed, XXH_aligned); } } return XXH64_endian_align((const xxh_u8*)input, len, seed, XXH_unaligned); #endif } /******* Hash Streaming *******/ #ifndef XXH_NO_STREAM /*! @ingroup XXH64_family*/ XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void) { return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t)); } /*! @ingroup XXH64_family */ XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr) { XXH_free(statePtr); return XXH_OK; } /*! @ingroup XXH64_family */ XXH_PUBLIC_API void XXH64_copyState(XXH_NOESCAPE XXH64_state_t* dstState, const XXH64_state_t* srcState) { XXH_memcpy(dstState, srcState, sizeof(*dstState)); } /*! @ingroup XXH64_family */ XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH_NOESCAPE XXH64_state_t* statePtr, XXH64_hash_t seed) { XXH_ASSERT(statePtr != NULL); memset(statePtr, 0, sizeof(*statePtr)); statePtr->v[0] = seed + XXH_PRIME64_1 + XXH_PRIME64_2; statePtr->v[1] = seed + XXH_PRIME64_2; statePtr->v[2] = seed + 0; statePtr->v[3] = seed - XXH_PRIME64_1; return XXH_OK; } /*! @ingroup XXH64_family */ XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH_NOESCAPE XXH64_state_t* state, XXH_NOESCAPE const void* input, size_t len) { if (input==NULL) { XXH_ASSERT(len == 0); return XXH_OK; } { const xxh_u8* p = (const xxh_u8*)input; const xxh_u8* const bEnd = p + len; state->total_len += len; if (state->memsize + len < 32) { /* fill in tmp buffer */ XXH_memcpy(((xxh_u8*)state->mem64) + state->memsize, input, len); state->memsize += (xxh_u32)len; return XXH_OK; } if (state->memsize) { /* tmp buffer is full */ XXH_memcpy(((xxh_u8*)state->mem64) + state->memsize, input, 32-state->memsize); state->v[0] = XXH64_round(state->v[0], XXH_readLE64(state->mem64+0)); state->v[1] = XXH64_round(state->v[1], XXH_readLE64(state->mem64+1)); state->v[2] = XXH64_round(state->v[2], XXH_readLE64(state->mem64+2)); state->v[3] = XXH64_round(state->v[3], XXH_readLE64(state->mem64+3)); p += 32 - state->memsize; state->memsize = 0; } if (p+32 <= bEnd) { const xxh_u8* const limit = bEnd - 32; do { state->v[0] = XXH64_round(state->v[0], XXH_readLE64(p)); p+=8; state->v[1] = XXH64_round(state->v[1], XXH_readLE64(p)); p+=8; state->v[2] = XXH64_round(state->v[2], XXH_readLE64(p)); p+=8; state->v[3] = XXH64_round(state->v[3], XXH_readLE64(p)); p+=8; } while (p<=limit); } if (p < bEnd) { XXH_memcpy(state->mem64, p, (size_t)(bEnd-p)); state->memsize = (unsigned)(bEnd-p); } } return XXH_OK; } /*! @ingroup XXH64_family */ XXH_PUBLIC_API XXH64_hash_t XXH64_digest(XXH_NOESCAPE const XXH64_state_t* state) { xxh_u64 h64; if (state->total_len >= 32) { h64 = XXH_rotl64(state->v[0], 1) + XXH_rotl64(state->v[1], 7) + XXH_rotl64(state->v[2], 12) + XXH_rotl64(state->v[3], 18); h64 = XXH64_mergeRound(h64, state->v[0]); h64 = XXH64_mergeRound(h64, state->v[1]); h64 = XXH64_mergeRound(h64, state->v[2]); h64 = XXH64_mergeRound(h64, state->v[3]); } else { h64 = state->v[2] /*seed*/ + XXH_PRIME64_5; } h64 += (xxh_u64) state->total_len; return XXH64_finalize(h64, (const xxh_u8*)state->mem64, (size_t)state->total_len, XXH_aligned); } #endif /* !XXH_NO_STREAM */ /******* Canonical representation *******/ /*! @ingroup XXH64_family */ XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH_NOESCAPE XXH64_canonical_t* dst, XXH64_hash_t hash) { XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t)); if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash); XXH_memcpy(dst, &hash, sizeof(*dst)); } /*! @ingroup XXH64_family */ XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(XXH_NOESCAPE const XXH64_canonical_t* src) { return XXH_readBE64(src); } #ifndef XXH_NO_XXH3 /* ********************************************************************* * XXH3 * New generation hash designed for speed on small keys and vectorization ************************************************************************ */ /*! * @} * @defgroup XXH3_impl XXH3 implementation * @ingroup impl * @{ */ /* === Compiler specifics === */ #if ((defined(sun) || defined(__sun)) && __cplusplus) /* Solaris includes __STDC_VERSION__ with C++. Tested with GCC 5.5 */ # define XXH_RESTRICT /* disable */ #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* >= C99 */ # define XXH_RESTRICT restrict #elif (defined (__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))) \ || (defined (__clang__)) \ || (defined (_MSC_VER) && (_MSC_VER >= 1400)) \ || (defined (__INTEL_COMPILER) && (__INTEL_COMPILER >= 1300)) /* * There are a LOT more compilers that recognize __restrict but this * covers the major ones. */ # define XXH_RESTRICT __restrict #else # define XXH_RESTRICT /* disable */ #endif #if (defined(__GNUC__) && (__GNUC__ >= 3)) \ || (defined(__INTEL_COMPILER) && (__INTEL_COMPILER >= 800)) \ || defined(__clang__) # define XXH_likely(x) __builtin_expect(x, 1) # define XXH_unlikely(x) __builtin_expect(x, 0) #else # define XXH_likely(x) (x) # define XXH_unlikely(x) (x) #endif #ifndef XXH_HAS_INCLUDE # ifdef __has_include # define XXH_HAS_INCLUDE(x) __has_include(x) # else # define XXH_HAS_INCLUDE(x) 0 # endif #endif #if defined(__GNUC__) || defined(__clang__) # if defined(__ARM_FEATURE_SVE) # include # endif # if defined(__ARM_NEON__) || defined(__ARM_NEON) \ || (defined(_M_ARM) && _M_ARM >= 7) \ || defined(_M_ARM64) || defined(_M_ARM64EC) \ || (defined(__wasm_simd128__) && XXH_HAS_INCLUDE()) /* WASM SIMD128 via SIMDe */ # define inline __inline__ /* circumvent a clang bug */ # include # undef inline # elif defined(__AVX2__) # include # elif defined(__SSE2__) # include # endif #endif #if defined(_MSC_VER) # include #endif /* * One goal of XXH3 is to make it fast on both 32-bit and 64-bit, while * remaining a true 64-bit/128-bit hash function. * * This is done by prioritizing a subset of 64-bit operations that can be * emulated without too many steps on the average 32-bit machine. * * For example, these two lines seem similar, and run equally fast on 64-bit: * * xxh_u64 x; * x ^= (x >> 47); // good * x ^= (x >> 13); // bad * * However, to a 32-bit machine, there is a major difference. * * x ^= (x >> 47) looks like this: * * x.lo ^= (x.hi >> (47 - 32)); * * while x ^= (x >> 13) looks like this: * * // note: funnel shifts are not usually cheap. * x.lo ^= (x.lo >> 13) | (x.hi << (32 - 13)); * x.hi ^= (x.hi >> 13); * * The first one is significantly faster than the second, simply because the * shift is larger than 32. This means: * - All the bits we need are in the upper 32 bits, so we can ignore the lower * 32 bits in the shift. * - The shift result will always fit in the lower 32 bits, and therefore, * we can ignore the upper 32 bits in the xor. * * Thanks to this optimization, XXH3 only requires these features to be efficient: * * - Usable unaligned access * - A 32-bit or 64-bit ALU * - If 32-bit, a decent ADC instruction * - A 32 or 64-bit multiply with a 64-bit result * - For the 128-bit variant, a decent byteswap helps short inputs. * * The first two are already required by XXH32, and almost all 32-bit and 64-bit * platforms which can run XXH32 can run XXH3 efficiently. * * Thumb-1, the classic 16-bit only subset of ARM's instruction set, is one * notable exception. * * First of all, Thumb-1 lacks support for the UMULL instruction which * performs the important long multiply. This means numerous __aeabi_lmul * calls. * * Second of all, the 8 functional registers are just not enough. * Setup for __aeabi_lmul, byteshift loads, pointers, and all arithmetic need * Lo registers, and this shuffling results in thousands more MOVs than A32. * * A32 and T32 don't have this limitation. They can access all 14 registers, * do a 32->64 multiply with UMULL, and the flexible operand allowing free * shifts is helpful, too. * * Therefore, we do a quick sanity check. * * If compiling Thumb-1 for a target which supports ARM instructions, we will * emit a warning, as it is not a "sane" platform to compile for. * * Usually, if this happens, it is because of an accident and you probably need * to specify -march, as you likely meant to compile for a newer architecture. * * Credit: large sections of the vectorial and asm source code paths * have been contributed by @easyaspi314 */ #if defined(__thumb__) && !defined(__thumb2__) && defined(__ARM_ARCH_ISA_ARM) # warning "XXH3 is highly inefficient without ARM or Thumb-2." #endif /* ========================================== * Vectorization detection * ========================================== */ #ifdef XXH_DOXYGEN /*! * @ingroup tuning * @brief Overrides the vectorization implementation chosen for XXH3. * * Can be defined to 0 to disable SIMD or any of the values mentioned in * @ref XXH_VECTOR_TYPE. * * If this is not defined, it uses predefined macros to determine the best * implementation. */ # define XXH_VECTOR XXH_SCALAR /*! * @ingroup tuning * @brief Possible values for @ref XXH_VECTOR. * * Note that these are actually implemented as macros. * * If this is not defined, it is detected automatically. * internal macro XXH_X86DISPATCH overrides this. */ enum XXH_VECTOR_TYPE /* fake enum */ { XXH_SCALAR = 0, /*!< Portable scalar version */ XXH_SSE2 = 1, /*!< * SSE2 for Pentium 4, Opteron, all x86_64. * * @note SSE2 is also guaranteed on Windows 10, macOS, and * Android x86. */ XXH_AVX2 = 2, /*!< AVX2 for Haswell and Bulldozer */ XXH_AVX512 = 3, /*!< AVX512 for Skylake and Icelake */ XXH_NEON = 4, /*!< * NEON for most ARMv7-A, all AArch64, and WASM SIMD128 * via the SIMDeverywhere polyfill provided with the * Emscripten SDK. */ XXH_VSX = 5, /*!< VSX and ZVector for POWER8/z13 (64-bit) */ XXH_SVE = 6, /*!< SVE for some ARMv8-A and ARMv9-A */ }; /*! * @ingroup tuning * @brief Selects the minimum alignment for XXH3's accumulators. * * When using SIMD, this should match the alignment required for said vector * type, so, for example, 32 for AVX2. * * Default: Auto detected. */ # define XXH_ACC_ALIGN 8 #endif /* Actual definition */ #ifndef XXH_DOXYGEN # define XXH_SCALAR 0 # define XXH_SSE2 1 # define XXH_AVX2 2 # define XXH_AVX512 3 # define XXH_NEON 4 # define XXH_VSX 5 # define XXH_SVE 6 #endif #ifndef XXH_VECTOR /* can be defined on command line */ # if defined(__ARM_FEATURE_SVE) # define XXH_VECTOR XXH_SVE # elif ( \ defined(__ARM_NEON__) || defined(__ARM_NEON) /* gcc */ \ || defined(_M_ARM) || defined(_M_ARM64) || defined(_M_ARM64EC) /* msvc */ \ || (defined(__wasm_simd128__) && XXH_HAS_INCLUDE()) /* wasm simd128 via SIMDe */ \ ) && ( \ defined(_WIN32) || defined(__LITTLE_ENDIAN__) /* little endian only */ \ || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) \ ) # define XXH_VECTOR XXH_NEON # elif defined(__AVX512F__) # define XXH_VECTOR XXH_AVX512 # elif defined(__AVX2__) # define XXH_VECTOR XXH_AVX2 # elif defined(__SSE2__) || defined(_M_AMD64) || defined(_M_X64) || (defined(_M_IX86_FP) && (_M_IX86_FP == 2)) # define XXH_VECTOR XXH_SSE2 # elif (defined(__PPC64__) && defined(__POWER8_VECTOR__)) \ || (defined(__s390x__) && defined(__VEC__)) \ && defined(__GNUC__) /* TODO: IBM XL */ # define XXH_VECTOR XXH_VSX # else # define XXH_VECTOR XXH_SCALAR # endif #endif /* __ARM_FEATURE_SVE is only supported by GCC & Clang. */ #if (XXH_VECTOR == XXH_SVE) && !defined(__ARM_FEATURE_SVE) # ifdef _MSC_VER # pragma warning(once : 4606) # else # warning "__ARM_FEATURE_SVE isn't supported. Use SCALAR instead." # endif # undef XXH_VECTOR # define XXH_VECTOR XXH_SCALAR #endif /* * Controls the alignment of the accumulator, * for compatibility with aligned vector loads, which are usually faster. */ #ifndef XXH_ACC_ALIGN # if defined(XXH_X86DISPATCH) # define XXH_ACC_ALIGN 64 /* for compatibility with avx512 */ # elif XXH_VECTOR == XXH_SCALAR /* scalar */ # define XXH_ACC_ALIGN 8 # elif XXH_VECTOR == XXH_SSE2 /* sse2 */ # define XXH_ACC_ALIGN 16 # elif XXH_VECTOR == XXH_AVX2 /* avx2 */ # define XXH_ACC_ALIGN 32 # elif XXH_VECTOR == XXH_NEON /* neon */ # define XXH_ACC_ALIGN 16 # elif XXH_VECTOR == XXH_VSX /* vsx */ # define XXH_ACC_ALIGN 16 # elif XXH_VECTOR == XXH_AVX512 /* avx512 */ # define XXH_ACC_ALIGN 64 # elif XXH_VECTOR == XXH_SVE /* sve */ # define XXH_ACC_ALIGN 64 # endif #endif #if defined(XXH_X86DISPATCH) || XXH_VECTOR == XXH_SSE2 \ || XXH_VECTOR == XXH_AVX2 || XXH_VECTOR == XXH_AVX512 # define XXH_SEC_ALIGN XXH_ACC_ALIGN #elif XXH_VECTOR == XXH_SVE # define XXH_SEC_ALIGN XXH_ACC_ALIGN #else # define XXH_SEC_ALIGN 8 #endif #if defined(__GNUC__) || defined(__clang__) # define XXH_ALIASING __attribute__((may_alias)) #else # define XXH_ALIASING /* nothing */ #endif /* * UGLY HACK: * GCC usually generates the best code with -O3 for xxHash. * * However, when targeting AVX2, it is overzealous in its unrolling resulting * in code roughly 3/4 the speed of Clang. * * There are other issues, such as GCC splitting _mm256_loadu_si256 into * _mm_loadu_si128 + _mm256_inserti128_si256. This is an optimization which * only applies to Sandy and Ivy Bridge... which don't even support AVX2. * * That is why when compiling the AVX2 version, it is recommended to use either * -O2 -mavx2 -march=haswell * or * -O2 -mavx2 -mno-avx256-split-unaligned-load * for decent performance, or to use Clang instead. * * Fortunately, we can control the first one with a pragma that forces GCC into * -O2, but the other one we can't control without "failed to inline always * inline function due to target mismatch" warnings. */ #if XXH_VECTOR == XXH_AVX2 /* AVX2 */ \ && defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \ && defined(__OPTIMIZE__) && XXH_SIZE_OPT <= 0 /* respect -O0 and -Os */ # pragma GCC push_options # pragma GCC optimize("-O2") #endif #if XXH_VECTOR == XXH_NEON /* * UGLY HACK: While AArch64 GCC on Linux does not seem to care, on macOS, GCC -O3 * optimizes out the entire hashLong loop because of the aliasing violation. * * However, GCC is also inefficient at load-store optimization with vld1q/vst1q, * so the only option is to mark it as aliasing. */ typedef uint64x2_t xxh_aliasing_uint64x2_t XXH_ALIASING; /*! * @internal * @brief `vld1q_u64` but faster and alignment-safe. * * On AArch64, unaligned access is always safe, but on ARMv7-a, it is only * *conditionally* safe (`vld1` has an alignment bit like `movdq[ua]` in x86). * * GCC for AArch64 sees `vld1q_u8` as an intrinsic instead of a load, so it * prohibits load-store optimizations. Therefore, a direct dereference is used. * * Otherwise, `vld1q_u8` is used with `vreinterpretq_u8_u64` to do a safe * unaligned load. */ #if defined(__aarch64__) && defined(__GNUC__) && !defined(__clang__) XXH_FORCE_INLINE uint64x2_t XXH_vld1q_u64(void const* ptr) /* silence -Wcast-align */ { return *(xxh_aliasing_uint64x2_t const *)ptr; } #else XXH_FORCE_INLINE uint64x2_t XXH_vld1q_u64(void const* ptr) { return vreinterpretq_u64_u8(vld1q_u8((uint8_t const*)ptr)); } #endif /*! * @internal * @brief `vmlal_u32` on low and high halves of a vector. * * This is a workaround for AArch64 GCC < 11 which implemented arm_neon.h with * inline assembly and were therefore incapable of merging the `vget_{low, high}_u32` * with `vmlal_u32`. */ #if defined(__aarch64__) && defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 11 XXH_FORCE_INLINE uint64x2_t XXH_vmlal_low_u32(uint64x2_t acc, uint32x4_t lhs, uint32x4_t rhs) { /* Inline assembly is the only way */ __asm__("umlal %0.2d, %1.2s, %2.2s" : "+w" (acc) : "w" (lhs), "w" (rhs)); return acc; } XXH_FORCE_INLINE uint64x2_t XXH_vmlal_high_u32(uint64x2_t acc, uint32x4_t lhs, uint32x4_t rhs) { /* This intrinsic works as expected */ return vmlal_high_u32(acc, lhs, rhs); } #else /* Portable intrinsic versions */ XXH_FORCE_INLINE uint64x2_t XXH_vmlal_low_u32(uint64x2_t acc, uint32x4_t lhs, uint32x4_t rhs) { return vmlal_u32(acc, vget_low_u32(lhs), vget_low_u32(rhs)); } /*! @copydoc XXH_vmlal_low_u32 * Assume the compiler converts this to vmlal_high_u32 on aarch64 */ XXH_FORCE_INLINE uint64x2_t XXH_vmlal_high_u32(uint64x2_t acc, uint32x4_t lhs, uint32x4_t rhs) { return vmlal_u32(acc, vget_high_u32(lhs), vget_high_u32(rhs)); } #endif /*! * @ingroup tuning * @brief Controls the NEON to scalar ratio for XXH3 * * This can be set to 2, 4, 6, or 8. * * ARM Cortex CPUs are _very_ sensitive to how their pipelines are used. * * For example, the Cortex-A73 can dispatch 3 micro-ops per cycle, but only 2 of those * can be NEON. If you are only using NEON instructions, you are only using 2/3 of the CPU * bandwidth. * * This is even more noticeable on the more advanced cores like the Cortex-A76 which * can dispatch 8 micro-ops per cycle, but still only 2 NEON micro-ops at once. * * Therefore, to make the most out of the pipeline, it is beneficial to run 6 NEON lanes * and 2 scalar lanes, which is chosen by default. * * This does not apply to Apple processors or 32-bit processors, which run better with * full NEON. These will default to 8. Additionally, size-optimized builds run 8 lanes. * * This change benefits CPUs with large micro-op buffers without negatively affecting * most other CPUs: * * | Chipset | Dispatch type | NEON only | 6:2 hybrid | Diff. | * |:----------------------|:--------------------|----------:|-----------:|------:| * | Snapdragon 730 (A76) | 2 NEON/8 micro-ops | 8.8 GB/s | 10.1 GB/s | ~16% | * | Snapdragon 835 (A73) | 2 NEON/3 micro-ops | 5.1 GB/s | 5.3 GB/s | ~5% | * | Marvell PXA1928 (A53) | In-order dual-issue | 1.9 GB/s | 1.9 GB/s | 0% | * | Apple M1 | 4 NEON/8 micro-ops | 37.3 GB/s | 36.1 GB/s | ~-3% | * * It also seems to fix some bad codegen on GCC, making it almost as fast as clang. * * When using WASM SIMD128, if this is 2 or 6, SIMDe will scalarize 2 of the lanes meaning * it effectively becomes worse 4. * * @see XXH3_accumulate_512_neon() */ # ifndef XXH3_NEON_LANES # if (defined(__aarch64__) || defined(__arm64__) || defined(_M_ARM64) || defined(_M_ARM64EC)) \ && !defined(__APPLE__) && XXH_SIZE_OPT <= 0 # define XXH3_NEON_LANES 6 # else # define XXH3_NEON_LANES XXH_ACC_NB # endif # endif #endif /* XXH_VECTOR == XXH_NEON */ /* * VSX and Z Vector helpers. * * This is very messy, and any pull requests to clean this up are welcome. * * There are a lot of problems with supporting VSX and s390x, due to * inconsistent intrinsics, spotty coverage, and multiple endiannesses. */ #if XXH_VECTOR == XXH_VSX /* Annoyingly, these headers _may_ define three macros: `bool`, `vector`, * and `pixel`. This is a problem for obvious reasons. * * These keywords are unnecessary; the spec literally says they are * equivalent to `__bool`, `__vector`, and `__pixel` and may be undef'd * after including the header. * * We use pragma push_macro/pop_macro to keep the namespace clean. */ # pragma push_macro("bool") # pragma push_macro("vector") # pragma push_macro("pixel") /* silence potential macro redefined warnings */ # undef bool # undef vector # undef pixel # if defined(__s390x__) # include # else # include # endif /* Restore the original macro values, if applicable. */ # pragma pop_macro("pixel") # pragma pop_macro("vector") # pragma pop_macro("bool") typedef __vector unsigned long long xxh_u64x2; typedef __vector unsigned char xxh_u8x16; typedef __vector unsigned xxh_u32x4; /* * UGLY HACK: Similar to aarch64 macOS GCC, s390x GCC has the same aliasing issue. */ typedef xxh_u64x2 xxh_aliasing_u64x2 XXH_ALIASING; # ifndef XXH_VSX_BE # if defined(__BIG_ENDIAN__) \ || (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) # define XXH_VSX_BE 1 # elif defined(__VEC_ELEMENT_REG_ORDER__) && __VEC_ELEMENT_REG_ORDER__ == __ORDER_BIG_ENDIAN__ # warning "-maltivec=be is not recommended. Please use native endianness." # define XXH_VSX_BE 1 # else # define XXH_VSX_BE 0 # endif # endif /* !defined(XXH_VSX_BE) */ # if XXH_VSX_BE # if defined(__POWER9_VECTOR__) || (defined(__clang__) && defined(__s390x__)) # define XXH_vec_revb vec_revb # else /*! * A polyfill for POWER9's vec_revb(). */ XXH_FORCE_INLINE xxh_u64x2 XXH_vec_revb(xxh_u64x2 val) { xxh_u8x16 const vByteSwap = { 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08 }; return vec_perm(val, val, vByteSwap); } # endif # endif /* XXH_VSX_BE */ /*! * Performs an unaligned vector load and byte swaps it on big endian. */ XXH_FORCE_INLINE xxh_u64x2 XXH_vec_loadu(const void *ptr) { xxh_u64x2 ret; XXH_memcpy(&ret, ptr, sizeof(xxh_u64x2)); # if XXH_VSX_BE ret = XXH_vec_revb(ret); # endif return ret; } /* * vec_mulo and vec_mule are very problematic intrinsics on PowerPC * * These intrinsics weren't added until GCC 8, despite existing for a while, * and they are endian dependent. Also, their meaning swap depending on version. * */ # if defined(__s390x__) /* s390x is always big endian, no issue on this platform */ # define XXH_vec_mulo vec_mulo # define XXH_vec_mule vec_mule # elif defined(__clang__) && XXH_HAS_BUILTIN(__builtin_altivec_vmuleuw) && !defined(__ibmxl__) /* Clang has a better way to control this, we can just use the builtin which doesn't swap. */ /* The IBM XL Compiler (which defined __clang__) only implements the vec_* operations */ # define XXH_vec_mulo __builtin_altivec_vmulouw # define XXH_vec_mule __builtin_altivec_vmuleuw # else /* gcc needs inline assembly */ /* Adapted from https://github.com/google/highwayhash/blob/master/highwayhash/hh_vsx.h. */ XXH_FORCE_INLINE xxh_u64x2 XXH_vec_mulo(xxh_u32x4 a, xxh_u32x4 b) { xxh_u64x2 result; __asm__("vmulouw %0, %1, %2" : "=v" (result) : "v" (a), "v" (b)); return result; } XXH_FORCE_INLINE xxh_u64x2 XXH_vec_mule(xxh_u32x4 a, xxh_u32x4 b) { xxh_u64x2 result; __asm__("vmuleuw %0, %1, %2" : "=v" (result) : "v" (a), "v" (b)); return result; } # endif /* XXH_vec_mulo, XXH_vec_mule */ #endif /* XXH_VECTOR == XXH_VSX */ #if XXH_VECTOR == XXH_SVE #define ACCRND(acc, offset) \ do { \ svuint64_t input_vec = svld1_u64(mask, xinput + offset); \ svuint64_t secret_vec = svld1_u64(mask, xsecret + offset); \ svuint64_t mixed = sveor_u64_x(mask, secret_vec, input_vec); \ svuint64_t swapped = svtbl_u64(input_vec, kSwap); \ svuint64_t mixed_lo = svextw_u64_x(mask, mixed); \ svuint64_t mixed_hi = svlsr_n_u64_x(mask, mixed, 32); \ svuint64_t mul = svmad_u64_x(mask, mixed_lo, mixed_hi, swapped); \ acc = svadd_u64_x(mask, acc, mul); \ } while (0) #endif /* XXH_VECTOR == XXH_SVE */ /* prefetch * can be disabled, by declaring XXH_NO_PREFETCH build macro */ #if defined(XXH_NO_PREFETCH) # define XXH_PREFETCH(ptr) (void)(ptr) /* disabled */ #else # if XXH_SIZE_OPT >= 1 # define XXH_PREFETCH(ptr) (void)(ptr) # elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86)) /* _mm_prefetch() not defined outside of x86/x64 */ # include /* https://msdn.microsoft.com/fr-fr/library/84szxsww(v=vs.90).aspx */ # define XXH_PREFETCH(ptr) _mm_prefetch((const char*)(ptr), _MM_HINT_T0) # elif defined(__GNUC__) && ( (__GNUC__ >= 4) || ( (__GNUC__ == 3) && (__GNUC_MINOR__ >= 1) ) ) # define XXH_PREFETCH(ptr) __builtin_prefetch((ptr), 0 /* rw==read */, 3 /* locality */) # else # define XXH_PREFETCH(ptr) (void)(ptr) /* disabled */ # endif #endif /* XXH_NO_PREFETCH */ /* ========================================== * XXH3 default settings * ========================================== */ #define XXH_SECRET_DEFAULT_SIZE 192 /* minimum XXH3_SECRET_SIZE_MIN */ #if (XXH_SECRET_DEFAULT_SIZE < XXH3_SECRET_SIZE_MIN) # error "default keyset is not large enough" #endif /*! Pseudorandom secret taken directly from FARSH. */ XXH_ALIGN(64) static const xxh_u8 XXH3_kSecret[XXH_SECRET_DEFAULT_SIZE] = { 0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c, 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f, 0xcb, 0x79, 0xe6, 0x4e, 0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21, 0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6, 0x81, 0x3a, 0x26, 0x4c, 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, 0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3, 0x71, 0x64, 0x48, 0x97, 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8, 0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, 0xc7, 0x0b, 0x4f, 0x1d, 0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64, 0xea, 0xc5, 0xac, 0x83, 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb, 0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26, 0x29, 0xd4, 0x68, 0x9e, 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce, 0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e, }; static const xxh_u64 PRIME_MX1 = 0x165667919E3779F9ULL; /*!< 0b0001011001010110011001111001000110011110001101110111100111111001 */ static const xxh_u64 PRIME_MX2 = 0x9FB21C651E98DF25ULL; /*!< 0b1001111110110010000111000110010100011110100110001101111100100101 */ #ifdef XXH_OLD_NAMES # define kSecret XXH3_kSecret #endif #ifdef XXH_DOXYGEN /*! * @brief Calculates a 32-bit to 64-bit long multiply. * * Implemented as a macro. * * Wraps `__emulu` on MSVC x86 because it tends to call `__allmul` when it doesn't * need to (but it shouldn't need to anyways, it is about 7 instructions to do * a 64x64 multiply...). Since we know that this will _always_ emit `MULL`, we * use that instead of the normal method. * * If you are compiling for platforms like Thumb-1 and don't have a better option, * you may also want to write your own long multiply routine here. * * @param x, y Numbers to be multiplied * @return 64-bit product of the low 32 bits of @p x and @p y. */ XXH_FORCE_INLINE xxh_u64 XXH_mult32to64(xxh_u64 x, xxh_u64 y) { return (x & 0xFFFFFFFF) * (y & 0xFFFFFFFF); } #elif defined(_MSC_VER) && defined(_M_IX86) # define XXH_mult32to64(x, y) __emulu((unsigned)(x), (unsigned)(y)) #else /* * Downcast + upcast is usually better than masking on older compilers like * GCC 4.2 (especially 32-bit ones), all without affecting newer compilers. * * The other method, (x & 0xFFFFFFFF) * (y & 0xFFFFFFFF), will AND both operands * and perform a full 64x64 multiply -- entirely redundant on 32-bit. */ # define XXH_mult32to64(x, y) ((xxh_u64)(xxh_u32)(x) * (xxh_u64)(xxh_u32)(y)) #endif /*! * @brief Calculates a 64->128-bit long multiply. * * Uses `__uint128_t` and `_umul128` if available, otherwise uses a scalar * version. * * @param lhs , rhs The 64-bit integers to be multiplied * @return The 128-bit result represented in an @ref XXH128_hash_t. */ static XXH128_hash_t XXH_mult64to128(xxh_u64 lhs, xxh_u64 rhs) { /* * GCC/Clang __uint128_t method. * * On most 64-bit targets, GCC and Clang define a __uint128_t type. * This is usually the best way as it usually uses a native long 64-bit * multiply, such as MULQ on x86_64 or MUL + UMULH on aarch64. * * Usually. * * Despite being a 32-bit platform, Clang (and emscripten) define this type * despite not having the arithmetic for it. This results in a laggy * compiler builtin call which calculates a full 128-bit multiply. * In that case it is best to use the portable one. * https://github.com/Cyan4973/xxHash/issues/211#issuecomment-515575677 */ #if (defined(__GNUC__) || defined(__clang__)) && !defined(__wasm__) \ && defined(__SIZEOF_INT128__) \ || (defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 128) __uint128_t const product = (__uint128_t)lhs * (__uint128_t)rhs; XXH128_hash_t r128; r128.low64 = (xxh_u64)(product); r128.high64 = (xxh_u64)(product >> 64); return r128; /* * MSVC for x64's _umul128 method. * * xxh_u64 _umul128(xxh_u64 Multiplier, xxh_u64 Multiplicand, xxh_u64 *HighProduct); * * This compiles to single operand MUL on x64. */ #elif (defined(_M_X64) || defined(_M_IA64)) && !defined(_M_ARM64EC) #ifndef _MSC_VER # pragma intrinsic(_umul128) #endif xxh_u64 product_high; xxh_u64 const product_low = _umul128(lhs, rhs, &product_high); XXH128_hash_t r128; r128.low64 = product_low; r128.high64 = product_high; return r128; /* * MSVC for ARM64's __umulh method. * * This compiles to the same MUL + UMULH as GCC/Clang's __uint128_t method. */ #elif defined(_M_ARM64) || defined(_M_ARM64EC) #ifndef _MSC_VER # pragma intrinsic(__umulh) #endif XXH128_hash_t r128; r128.low64 = lhs * rhs; r128.high64 = __umulh(lhs, rhs); return r128; #else /* * Portable scalar method. Optimized for 32-bit and 64-bit ALUs. * * This is a fast and simple grade school multiply, which is shown below * with base 10 arithmetic instead of base 0x100000000. * * 9 3 // D2 lhs = 93 * x 7 5 // D2 rhs = 75 * ---------- * 1 5 // D2 lo_lo = (93 % 10) * (75 % 10) = 15 * 4 5 | // D2 hi_lo = (93 / 10) * (75 % 10) = 45 * 2 1 | // D2 lo_hi = (93 % 10) * (75 / 10) = 21 * + 6 3 | | // D2 hi_hi = (93 / 10) * (75 / 10) = 63 * --------- * 2 7 | // D2 cross = (15 / 10) + (45 % 10) + 21 = 27 * + 6 7 | | // D2 upper = (27 / 10) + (45 / 10) + 63 = 67 * --------- * 6 9 7 5 // D4 res = (27 * 10) + (15 % 10) + (67 * 100) = 6975 * * The reasons for adding the products like this are: * 1. It avoids manual carry tracking. Just like how * (9 * 9) + 9 + 9 = 99, the same applies with this for UINT64_MAX. * This avoids a lot of complexity. * * 2. It hints for, and on Clang, compiles to, the powerful UMAAL * instruction available in ARM's Digital Signal Processing extension * in 32-bit ARMv6 and later, which is shown below: * * void UMAAL(xxh_u32 *RdLo, xxh_u32 *RdHi, xxh_u32 Rn, xxh_u32 Rm) * { * xxh_u64 product = (xxh_u64)*RdLo * (xxh_u64)*RdHi + Rn + Rm; * *RdLo = (xxh_u32)(product & 0xFFFFFFFF); * *RdHi = (xxh_u32)(product >> 32); * } * * This instruction was designed for efficient long multiplication, and * allows this to be calculated in only 4 instructions at speeds * comparable to some 64-bit ALUs. * * 3. It isn't terrible on other platforms. Usually this will be a couple * of 32-bit ADD/ADCs. */ /* First calculate all of the cross products. */ xxh_u64 const lo_lo = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs & 0xFFFFFFFF); xxh_u64 const hi_lo = XXH_mult32to64(lhs >> 32, rhs & 0xFFFFFFFF); xxh_u64 const lo_hi = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs >> 32); xxh_u64 const hi_hi = XXH_mult32to64(lhs >> 32, rhs >> 32); /* Now add the products together. These will never overflow. */ xxh_u64 const cross = (lo_lo >> 32) + (hi_lo & 0xFFFFFFFF) + lo_hi; xxh_u64 const upper = (hi_lo >> 32) + (cross >> 32) + hi_hi; xxh_u64 const lower = (cross << 32) | (lo_lo & 0xFFFFFFFF); XXH128_hash_t r128; r128.low64 = lower; r128.high64 = upper; return r128; #endif } /*! * @brief Calculates a 64-bit to 128-bit multiply, then XOR folds it. * * The reason for the separate function is to prevent passing too many structs * around by value. This will hopefully inline the multiply, but we don't force it. * * @param lhs , rhs The 64-bit integers to multiply * @return The low 64 bits of the product XOR'd by the high 64 bits. * @see XXH_mult64to128() */ static xxh_u64 XXH3_mul128_fold64(xxh_u64 lhs, xxh_u64 rhs) { XXH128_hash_t product = XXH_mult64to128(lhs, rhs); return product.low64 ^ product.high64; } /*! Seems to produce slightly better code on GCC for some reason. */ XXH_FORCE_INLINE XXH_CONSTF xxh_u64 XXH_xorshift64(xxh_u64 v64, int shift) { XXH_ASSERT(0 <= shift && shift < 64); return v64 ^ (v64 >> shift); } /* * This is a fast avalanche stage, * suitable when input bits are already partially mixed */ static XXH64_hash_t XXH3_avalanche(xxh_u64 h64) { h64 = XXH_xorshift64(h64, 37); h64 *= PRIME_MX1; h64 = XXH_xorshift64(h64, 32); return h64; } /* * This is a stronger avalanche, * inspired by Pelle Evensen's rrmxmx * preferable when input has not been previously mixed */ static XXH64_hash_t XXH3_rrmxmx(xxh_u64 h64, xxh_u64 len) { /* this mix is inspired by Pelle Evensen's rrmxmx */ h64 ^= XXH_rotl64(h64, 49) ^ XXH_rotl64(h64, 24); h64 *= PRIME_MX2; h64 ^= (h64 >> 35) + len ; h64 *= PRIME_MX2; return XXH_xorshift64(h64, 28); } /* ========================================== * Short keys * ========================================== * One of the shortcomings of XXH32 and XXH64 was that their performance was * sub-optimal on short lengths. It used an iterative algorithm which strongly * favored lengths that were a multiple of 4 or 8. * * Instead of iterating over individual inputs, we use a set of single shot * functions which piece together a range of lengths and operate in constant time. * * Additionally, the number of multiplies has been significantly reduced. This * reduces latency, especially when emulating 64-bit multiplies on 32-bit. * * Depending on the platform, this may or may not be faster than XXH32, but it * is almost guaranteed to be faster than XXH64. */ /* * At very short lengths, there isn't enough input to fully hide secrets, or use * the entire secret. * * There is also only a limited amount of mixing we can do before significantly * impacting performance. * * Therefore, we use different sections of the secret and always mix two secret * samples with an XOR. This should have no effect on performance on the * seedless or withSeed variants because everything _should_ be constant folded * by modern compilers. * * The XOR mixing hides individual parts of the secret and increases entropy. * * This adds an extra layer of strength for custom secrets. */ XXH_FORCE_INLINE XXH_PUREF XXH64_hash_t XXH3_len_1to3_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(input != NULL); XXH_ASSERT(1 <= len && len <= 3); XXH_ASSERT(secret != NULL); /* * len = 1: combined = { input[0], 0x01, input[0], input[0] } * len = 2: combined = { input[1], 0x02, input[0], input[1] } * len = 3: combined = { input[2], 0x03, input[0], input[1] } */ { xxh_u8 const c1 = input[0]; xxh_u8 const c2 = input[len >> 1]; xxh_u8 const c3 = input[len - 1]; xxh_u32 const combined = ((xxh_u32)c1 << 16) | ((xxh_u32)c2 << 24) | ((xxh_u32)c3 << 0) | ((xxh_u32)len << 8); xxh_u64 const bitflip = (XXH_readLE32(secret) ^ XXH_readLE32(secret+4)) + seed; xxh_u64 const keyed = (xxh_u64)combined ^ bitflip; return XXH64_avalanche(keyed); } } XXH_FORCE_INLINE XXH_PUREF XXH64_hash_t XXH3_len_4to8_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(input != NULL); XXH_ASSERT(secret != NULL); XXH_ASSERT(4 <= len && len <= 8); seed ^= (xxh_u64)XXH_swap32((xxh_u32)seed) << 32; { xxh_u32 const input1 = XXH_readLE32(input); xxh_u32 const input2 = XXH_readLE32(input + len - 4); xxh_u64 const bitflip = (XXH_readLE64(secret+8) ^ XXH_readLE64(secret+16)) - seed; xxh_u64 const input64 = input2 + (((xxh_u64)input1) << 32); xxh_u64 const keyed = input64 ^ bitflip; return XXH3_rrmxmx(keyed, len); } } XXH_FORCE_INLINE XXH_PUREF XXH64_hash_t XXH3_len_9to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(input != NULL); XXH_ASSERT(secret != NULL); XXH_ASSERT(9 <= len && len <= 16); { xxh_u64 const bitflip1 = (XXH_readLE64(secret+24) ^ XXH_readLE64(secret+32)) + seed; xxh_u64 const bitflip2 = (XXH_readLE64(secret+40) ^ XXH_readLE64(secret+48)) - seed; xxh_u64 const input_lo = XXH_readLE64(input) ^ bitflip1; xxh_u64 const input_hi = XXH_readLE64(input + len - 8) ^ bitflip2; xxh_u64 const acc = len + XXH_swap64(input_lo) + input_hi + XXH3_mul128_fold64(input_lo, input_hi); return XXH3_avalanche(acc); } } XXH_FORCE_INLINE XXH_PUREF XXH64_hash_t XXH3_len_0to16_64b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(len <= 16); { if (XXH_likely(len > 8)) return XXH3_len_9to16_64b(input, len, secret, seed); if (XXH_likely(len >= 4)) return XXH3_len_4to8_64b(input, len, secret, seed); if (len) return XXH3_len_1to3_64b(input, len, secret, seed); return XXH64_avalanche(seed ^ (XXH_readLE64(secret+56) ^ XXH_readLE64(secret+64))); } } /* * DISCLAIMER: There are known *seed-dependent* multicollisions here due to * multiplication by zero, affecting hashes of lengths 17 to 240. * * However, they are very unlikely. * * Keep this in mind when using the unseeded XXH3_64bits() variant: As with all * unseeded non-cryptographic hashes, it does not attempt to defend itself * against specially crafted inputs, only random inputs. * * Compared to classic UMAC where a 1 in 2^31 chance of 4 consecutive bytes * cancelling out the secret is taken an arbitrary number of times (addressed * in XXH3_accumulate_512), this collision is very unlikely with random inputs * and/or proper seeding: * * This only has a 1 in 2^63 chance of 8 consecutive bytes cancelling out, in a * function that is only called up to 16 times per hash with up to 240 bytes of * input. * * This is not too bad for a non-cryptographic hash function, especially with * only 64 bit outputs. * * The 128-bit variant (which trades some speed for strength) is NOT affected * by this, although it is always a good idea to use a proper seed if you care * about strength. */ XXH_FORCE_INLINE xxh_u64 XXH3_mix16B(const xxh_u8* XXH_RESTRICT input, const xxh_u8* XXH_RESTRICT secret, xxh_u64 seed64) { #if defined(__GNUC__) && !defined(__clang__) /* GCC, not Clang */ \ && defined(__i386__) && defined(__SSE2__) /* x86 + SSE2 */ \ && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable like XXH32 hack */ /* * UGLY HACK: * GCC for x86 tends to autovectorize the 128-bit multiply, resulting in * slower code. * * By forcing seed64 into a register, we disrupt the cost model and * cause it to scalarize. See `XXH32_round()` * * FIXME: Clang's output is still _much_ faster -- On an AMD Ryzen 3600, * XXH3_64bits @ len=240 runs at 4.6 GB/s with Clang 9, but 3.3 GB/s on * GCC 9.2, despite both emitting scalar code. * * GCC generates much better scalar code than Clang for the rest of XXH3, * which is why finding a more optimal codepath is an interest. */ XXH_COMPILER_GUARD(seed64); #endif { xxh_u64 const input_lo = XXH_readLE64(input); xxh_u64 const input_hi = XXH_readLE64(input+8); return XXH3_mul128_fold64( input_lo ^ (XXH_readLE64(secret) + seed64), input_hi ^ (XXH_readLE64(secret+8) - seed64) ); } } /* For mid range keys, XXH3 uses a Mum-hash variant. */ XXH_FORCE_INLINE XXH_PUREF XXH64_hash_t XXH3_len_17to128_64b(const xxh_u8* XXH_RESTRICT input, size_t len, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, XXH64_hash_t seed) { XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize; XXH_ASSERT(16 < len && len <= 128); { xxh_u64 acc = len * XXH_PRIME64_1; #if XXH_SIZE_OPT >= 1 /* Smaller and cleaner, but slightly slower. */ unsigned int i = (unsigned int)(len - 1) / 32; do { acc += XXH3_mix16B(input+16 * i, secret+32*i, seed); acc += XXH3_mix16B(input+len-16*(i+1), secret+32*i+16, seed); } while (i-- != 0); #else if (len > 32) { if (len > 64) { if (len > 96) { acc += XXH3_mix16B(input+48, secret+96, seed); acc += XXH3_mix16B(input+len-64, secret+112, seed); } acc += XXH3_mix16B(input+32, secret+64, seed); acc += XXH3_mix16B(input+len-48, secret+80, seed); } acc += XXH3_mix16B(input+16, secret+32, seed); acc += XXH3_mix16B(input+len-32, secret+48, seed); } acc += XXH3_mix16B(input+0, secret+0, seed); acc += XXH3_mix16B(input+len-16, secret+16, seed); #endif return XXH3_avalanche(acc); } } #define XXH3_MIDSIZE_MAX 240 XXH_NO_INLINE XXH_PUREF XXH64_hash_t XXH3_len_129to240_64b(const xxh_u8* XXH_RESTRICT input, size_t len, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, XXH64_hash_t seed) { XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize; XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX); #define XXH3_MIDSIZE_STARTOFFSET 3 #define XXH3_MIDSIZE_LASTOFFSET 17 { xxh_u64 acc = len * XXH_PRIME64_1; xxh_u64 acc_end; unsigned int const nbRounds = (unsigned int)len / 16; unsigned int i; XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX); for (i=0; i<8; i++) { acc += XXH3_mix16B(input+(16*i), secret+(16*i), seed); } /* last bytes */ acc_end = XXH3_mix16B(input + len - 16, secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET, seed); XXH_ASSERT(nbRounds >= 8); acc = XXH3_avalanche(acc); #if defined(__clang__) /* Clang */ \ && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */ \ && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable */ /* * UGLY HACK: * Clang for ARMv7-A tries to vectorize this loop, similar to GCC x86. * In everywhere else, it uses scalar code. * * For 64->128-bit multiplies, even if the NEON was 100% optimal, it * would still be slower than UMAAL (see XXH_mult64to128). * * Unfortunately, Clang doesn't handle the long multiplies properly and * converts them to the nonexistent "vmulq_u64" intrinsic, which is then * scalarized into an ugly mess of VMOV.32 instructions. * * This mess is difficult to avoid without turning autovectorization * off completely, but they are usually relatively minor and/or not * worth it to fix. * * This loop is the easiest to fix, as unlike XXH32, this pragma * _actually works_ because it is a loop vectorization instead of an * SLP vectorization. */ #pragma clang loop vectorize(disable) #endif for (i=8 ; i < nbRounds; i++) { /* * Prevents clang for unrolling the acc loop and interleaving with this one. */ XXH_COMPILER_GUARD(acc); acc_end += XXH3_mix16B(input+(16*i), secret+(16*(i-8)) + XXH3_MIDSIZE_STARTOFFSET, seed); } return XXH3_avalanche(acc + acc_end); } } /* ======= Long Keys ======= */ #define XXH_STRIPE_LEN 64 #define XXH_SECRET_CONSUME_RATE 8 /* nb of secret bytes consumed at each accumulation */ #define XXH_ACC_NB (XXH_STRIPE_LEN / sizeof(xxh_u64)) #ifdef XXH_OLD_NAMES # define STRIPE_LEN XXH_STRIPE_LEN # define ACC_NB XXH_ACC_NB #endif #ifndef XXH_PREFETCH_DIST # ifdef __clang__ # define XXH_PREFETCH_DIST 320 # else # if (XXH_VECTOR == XXH_AVX512) # define XXH_PREFETCH_DIST 512 # else # define XXH_PREFETCH_DIST 384 # endif # endif /* __clang__ */ #endif /* XXH_PREFETCH_DIST */ /* * These macros are to generate an XXH3_accumulate() function. * The two arguments select the name suffix and target attribute. * * The name of this symbol is XXH3_accumulate_() and it calls * XXH3_accumulate_512_(). * * It may be useful to hand implement this function if the compiler fails to * optimize the inline function. */ #define XXH3_ACCUMULATE_TEMPLATE(name) \ void \ XXH3_accumulate_##name(xxh_u64* XXH_RESTRICT acc, \ const xxh_u8* XXH_RESTRICT input, \ const xxh_u8* XXH_RESTRICT secret, \ size_t nbStripes) \ { \ size_t n; \ for (n = 0; n < nbStripes; n++ ) { \ const xxh_u8* const in = input + n*XXH_STRIPE_LEN; \ XXH_PREFETCH(in + XXH_PREFETCH_DIST); \ XXH3_accumulate_512_##name( \ acc, \ in, \ secret + n*XXH_SECRET_CONSUME_RATE); \ } \ } XXH_FORCE_INLINE void XXH_writeLE64(void* dst, xxh_u64 v64) { if (!XXH_CPU_LITTLE_ENDIAN) v64 = XXH_swap64(v64); XXH_memcpy(dst, &v64, sizeof(v64)); } /* Several intrinsic functions below are supposed to accept __int64 as argument, * as documented in https://software.intel.com/sites/landingpage/IntrinsicsGuide/ . * However, several environments do not define __int64 type, * requiring a workaround. */ #if !defined (__VMS) \ && (defined (__cplusplus) \ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) typedef int64_t xxh_i64; #else /* the following type must have a width of 64-bit */ typedef long long xxh_i64; #endif /* * XXH3_accumulate_512 is the tightest loop for long inputs, and it is the most optimized. * * It is a hardened version of UMAC, based off of FARSH's implementation. * * This was chosen because it adapts quite well to 32-bit, 64-bit, and SIMD * implementations, and it is ridiculously fast. * * We harden it by mixing the original input to the accumulators as well as the product. * * This means that in the (relatively likely) case of a multiply by zero, the * original input is preserved. * * On 128-bit inputs, we swap 64-bit pairs when we add the input to improve * cross-pollination, as otherwise the upper and lower halves would be * essentially independent. * * This doesn't matter on 64-bit hashes since they all get merged together in * the end, so we skip the extra step. * * Both XXH3_64bits and XXH3_128bits use this subroutine. */ #if (XXH_VECTOR == XXH_AVX512) \ || (defined(XXH_DISPATCH_AVX512) && XXH_DISPATCH_AVX512 != 0) #ifndef XXH_TARGET_AVX512 # define XXH_TARGET_AVX512 /* disable attribute target */ #endif XXH_FORCE_INLINE XXH_TARGET_AVX512 void XXH3_accumulate_512_avx512(void* XXH_RESTRICT acc, const void* XXH_RESTRICT input, const void* XXH_RESTRICT secret) { __m512i* const xacc = (__m512i *) acc; XXH_ASSERT((((size_t)acc) & 63) == 0); XXH_STATIC_ASSERT(XXH_STRIPE_LEN == sizeof(__m512i)); { /* data_vec = input[0]; */ __m512i const data_vec = _mm512_loadu_si512 (input); /* key_vec = secret[0]; */ __m512i const key_vec = _mm512_loadu_si512 (secret); /* data_key = data_vec ^ key_vec; */ __m512i const data_key = _mm512_xor_si512 (data_vec, key_vec); /* data_key_lo = data_key >> 32; */ __m512i const data_key_lo = _mm512_srli_epi64 (data_key, 32); /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ __m512i const product = _mm512_mul_epu32 (data_key, data_key_lo); /* xacc[0] += swap(data_vec); */ __m512i const data_swap = _mm512_shuffle_epi32(data_vec, (_MM_PERM_ENUM)_MM_SHUFFLE(1, 0, 3, 2)); __m512i const sum = _mm512_add_epi64(*xacc, data_swap); /* xacc[0] += product; */ *xacc = _mm512_add_epi64(product, sum); } } XXH_FORCE_INLINE XXH_TARGET_AVX512 XXH3_ACCUMULATE_TEMPLATE(avx512) /* * XXH3_scrambleAcc: Scrambles the accumulators to improve mixing. * * Multiplication isn't perfect, as explained by Google in HighwayHash: * * // Multiplication mixes/scrambles bytes 0-7 of the 64-bit result to * // varying degrees. In descending order of goodness, bytes * // 3 4 2 5 1 6 0 7 have quality 228 224 164 160 100 96 36 32. * // As expected, the upper and lower bytes are much worse. * * Source: https://github.com/google/highwayhash/blob/0aaf66b/highwayhash/hh_avx2.h#L291 * * Since our algorithm uses a pseudorandom secret to add some variance into the * mix, we don't need to (or want to) mix as often or as much as HighwayHash does. * * This isn't as tight as XXH3_accumulate, but still written in SIMD to avoid * extraction. * * Both XXH3_64bits and XXH3_128bits use this subroutine. */ XXH_FORCE_INLINE XXH_TARGET_AVX512 void XXH3_scrambleAcc_avx512(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 63) == 0); XXH_STATIC_ASSERT(XXH_STRIPE_LEN == sizeof(__m512i)); { __m512i* const xacc = (__m512i*) acc; const __m512i prime32 = _mm512_set1_epi32((int)XXH_PRIME32_1); /* xacc[0] ^= (xacc[0] >> 47) */ __m512i const acc_vec = *xacc; __m512i const shifted = _mm512_srli_epi64 (acc_vec, 47); /* xacc[0] ^= secret; */ __m512i const key_vec = _mm512_loadu_si512 (secret); __m512i const data_key = _mm512_ternarylogic_epi32(key_vec, acc_vec, shifted, 0x96 /* key_vec ^ acc_vec ^ shifted */); /* xacc[0] *= XXH_PRIME32_1; */ __m512i const data_key_hi = _mm512_srli_epi64 (data_key, 32); __m512i const prod_lo = _mm512_mul_epu32 (data_key, prime32); __m512i const prod_hi = _mm512_mul_epu32 (data_key_hi, prime32); *xacc = _mm512_add_epi64(prod_lo, _mm512_slli_epi64(prod_hi, 32)); } } XXH_FORCE_INLINE XXH_TARGET_AVX512 void XXH3_initCustomSecret_avx512(void* XXH_RESTRICT customSecret, xxh_u64 seed64) { XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 63) == 0); XXH_STATIC_ASSERT(XXH_SEC_ALIGN == 64); XXH_ASSERT(((size_t)customSecret & 63) == 0); (void)(&XXH_writeLE64); { int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m512i); __m512i const seed_pos = _mm512_set1_epi64((xxh_i64)seed64); __m512i const seed = _mm512_mask_sub_epi64(seed_pos, 0xAA, _mm512_set1_epi8(0), seed_pos); const __m512i* const src = (const __m512i*) ((const void*) XXH3_kSecret); __m512i* const dest = ( __m512i*) customSecret; int i; XXH_ASSERT(((size_t)src & 63) == 0); /* control alignment */ XXH_ASSERT(((size_t)dest & 63) == 0); for (i=0; i < nbRounds; ++i) { dest[i] = _mm512_add_epi64(_mm512_load_si512(src + i), seed); } } } #endif #if (XXH_VECTOR == XXH_AVX2) \ || (defined(XXH_DISPATCH_AVX2) && XXH_DISPATCH_AVX2 != 0) #ifndef XXH_TARGET_AVX2 # define XXH_TARGET_AVX2 /* disable attribute target */ #endif XXH_FORCE_INLINE XXH_TARGET_AVX2 void XXH3_accumulate_512_avx2( void* XXH_RESTRICT acc, const void* XXH_RESTRICT input, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 31) == 0); { __m256i* const xacc = (__m256i *) acc; /* Unaligned. This is mainly for pointer arithmetic, and because * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */ const __m256i* const xinput = (const __m256i *) input; /* Unaligned. This is mainly for pointer arithmetic, and because * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */ const __m256i* const xsecret = (const __m256i *) secret; size_t i; for (i=0; i < XXH_STRIPE_LEN/sizeof(__m256i); i++) { /* data_vec = xinput[i]; */ __m256i const data_vec = _mm256_loadu_si256 (xinput+i); /* key_vec = xsecret[i]; */ __m256i const key_vec = _mm256_loadu_si256 (xsecret+i); /* data_key = data_vec ^ key_vec; */ __m256i const data_key = _mm256_xor_si256 (data_vec, key_vec); /* data_key_lo = data_key >> 32; */ __m256i const data_key_lo = _mm256_srli_epi64 (data_key, 32); /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ __m256i const product = _mm256_mul_epu32 (data_key, data_key_lo); /* xacc[i] += swap(data_vec); */ __m256i const data_swap = _mm256_shuffle_epi32(data_vec, _MM_SHUFFLE(1, 0, 3, 2)); __m256i const sum = _mm256_add_epi64(xacc[i], data_swap); /* xacc[i] += product; */ xacc[i] = _mm256_add_epi64(product, sum); } } } XXH_FORCE_INLINE XXH_TARGET_AVX2 XXH3_ACCUMULATE_TEMPLATE(avx2) XXH_FORCE_INLINE XXH_TARGET_AVX2 void XXH3_scrambleAcc_avx2(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 31) == 0); { __m256i* const xacc = (__m256i*) acc; /* Unaligned. This is mainly for pointer arithmetic, and because * _mm256_loadu_si256 requires a const __m256i * pointer for some reason. */ const __m256i* const xsecret = (const __m256i *) secret; const __m256i prime32 = _mm256_set1_epi32((int)XXH_PRIME32_1); size_t i; for (i=0; i < XXH_STRIPE_LEN/sizeof(__m256i); i++) { /* xacc[i] ^= (xacc[i] >> 47) */ __m256i const acc_vec = xacc[i]; __m256i const shifted = _mm256_srli_epi64 (acc_vec, 47); __m256i const data_vec = _mm256_xor_si256 (acc_vec, shifted); /* xacc[i] ^= xsecret; */ __m256i const key_vec = _mm256_loadu_si256 (xsecret+i); __m256i const data_key = _mm256_xor_si256 (data_vec, key_vec); /* xacc[i] *= XXH_PRIME32_1; */ __m256i const data_key_hi = _mm256_srli_epi64 (data_key, 32); __m256i const prod_lo = _mm256_mul_epu32 (data_key, prime32); __m256i const prod_hi = _mm256_mul_epu32 (data_key_hi, prime32); xacc[i] = _mm256_add_epi64(prod_lo, _mm256_slli_epi64(prod_hi, 32)); } } } XXH_FORCE_INLINE XXH_TARGET_AVX2 void XXH3_initCustomSecret_avx2(void* XXH_RESTRICT customSecret, xxh_u64 seed64) { XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 31) == 0); XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE / sizeof(__m256i)) == 6); XXH_STATIC_ASSERT(XXH_SEC_ALIGN <= 64); (void)(&XXH_writeLE64); XXH_PREFETCH(customSecret); { __m256i const seed = _mm256_set_epi64x((xxh_i64)(0U - seed64), (xxh_i64)seed64, (xxh_i64)(0U - seed64), (xxh_i64)seed64); const __m256i* const src = (const __m256i*) ((const void*) XXH3_kSecret); __m256i* dest = ( __m256i*) customSecret; # if defined(__GNUC__) || defined(__clang__) /* * On GCC & Clang, marking 'dest' as modified will cause the compiler: * - do not extract the secret from sse registers in the internal loop * - use less common registers, and avoid pushing these reg into stack */ XXH_COMPILER_GUARD(dest); # endif XXH_ASSERT(((size_t)src & 31) == 0); /* control alignment */ XXH_ASSERT(((size_t)dest & 31) == 0); /* GCC -O2 need unroll loop manually */ dest[0] = _mm256_add_epi64(_mm256_load_si256(src+0), seed); dest[1] = _mm256_add_epi64(_mm256_load_si256(src+1), seed); dest[2] = _mm256_add_epi64(_mm256_load_si256(src+2), seed); dest[3] = _mm256_add_epi64(_mm256_load_si256(src+3), seed); dest[4] = _mm256_add_epi64(_mm256_load_si256(src+4), seed); dest[5] = _mm256_add_epi64(_mm256_load_si256(src+5), seed); } } #endif /* x86dispatch always generates SSE2 */ #if (XXH_VECTOR == XXH_SSE2) || defined(XXH_X86DISPATCH) #ifndef XXH_TARGET_SSE2 # define XXH_TARGET_SSE2 /* disable attribute target */ #endif XXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_accumulate_512_sse2( void* XXH_RESTRICT acc, const void* XXH_RESTRICT input, const void* XXH_RESTRICT secret) { /* SSE2 is just a half-scale version of the AVX2 version. */ XXH_ASSERT((((size_t)acc) & 15) == 0); { __m128i* const xacc = (__m128i *) acc; /* Unaligned. This is mainly for pointer arithmetic, and because * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ const __m128i* const xinput = (const __m128i *) input; /* Unaligned. This is mainly for pointer arithmetic, and because * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ const __m128i* const xsecret = (const __m128i *) secret; size_t i; for (i=0; i < XXH_STRIPE_LEN/sizeof(__m128i); i++) { /* data_vec = xinput[i]; */ __m128i const data_vec = _mm_loadu_si128 (xinput+i); /* key_vec = xsecret[i]; */ __m128i const key_vec = _mm_loadu_si128 (xsecret+i); /* data_key = data_vec ^ key_vec; */ __m128i const data_key = _mm_xor_si128 (data_vec, key_vec); /* data_key_lo = data_key >> 32; */ __m128i const data_key_lo = _mm_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1)); /* product = (data_key & 0xffffffff) * (data_key_lo & 0xffffffff); */ __m128i const product = _mm_mul_epu32 (data_key, data_key_lo); /* xacc[i] += swap(data_vec); */ __m128i const data_swap = _mm_shuffle_epi32(data_vec, _MM_SHUFFLE(1,0,3,2)); __m128i const sum = _mm_add_epi64(xacc[i], data_swap); /* xacc[i] += product; */ xacc[i] = _mm_add_epi64(product, sum); } } } XXH_FORCE_INLINE XXH_TARGET_SSE2 XXH3_ACCUMULATE_TEMPLATE(sse2) XXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_scrambleAcc_sse2(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 15) == 0); { __m128i* const xacc = (__m128i*) acc; /* Unaligned. This is mainly for pointer arithmetic, and because * _mm_loadu_si128 requires a const __m128i * pointer for some reason. */ const __m128i* const xsecret = (const __m128i *) secret; const __m128i prime32 = _mm_set1_epi32((int)XXH_PRIME32_1); size_t i; for (i=0; i < XXH_STRIPE_LEN/sizeof(__m128i); i++) { /* xacc[i] ^= (xacc[i] >> 47) */ __m128i const acc_vec = xacc[i]; __m128i const shifted = _mm_srli_epi64 (acc_vec, 47); __m128i const data_vec = _mm_xor_si128 (acc_vec, shifted); /* xacc[i] ^= xsecret[i]; */ __m128i const key_vec = _mm_loadu_si128 (xsecret+i); __m128i const data_key = _mm_xor_si128 (data_vec, key_vec); /* xacc[i] *= XXH_PRIME32_1; */ __m128i const data_key_hi = _mm_shuffle_epi32 (data_key, _MM_SHUFFLE(0, 3, 0, 1)); __m128i const prod_lo = _mm_mul_epu32 (data_key, prime32); __m128i const prod_hi = _mm_mul_epu32 (data_key_hi, prime32); xacc[i] = _mm_add_epi64(prod_lo, _mm_slli_epi64(prod_hi, 32)); } } } XXH_FORCE_INLINE XXH_TARGET_SSE2 void XXH3_initCustomSecret_sse2(void* XXH_RESTRICT customSecret, xxh_u64 seed64) { XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0); (void)(&XXH_writeLE64); { int const nbRounds = XXH_SECRET_DEFAULT_SIZE / sizeof(__m128i); # if defined(_MSC_VER) && defined(_M_IX86) && _MSC_VER < 1900 /* MSVC 32bit mode does not support _mm_set_epi64x before 2015 */ XXH_ALIGN(16) const xxh_i64 seed64x2[2] = { (xxh_i64)seed64, (xxh_i64)(0U - seed64) }; __m128i const seed = _mm_load_si128((__m128i const*)seed64x2); # else __m128i const seed = _mm_set_epi64x((xxh_i64)(0U - seed64), (xxh_i64)seed64); # endif int i; const void* const src16 = XXH3_kSecret; __m128i* dst16 = (__m128i*) customSecret; # if defined(__GNUC__) || defined(__clang__) /* * On GCC & Clang, marking 'dest' as modified will cause the compiler: * - do not extract the secret from sse registers in the internal loop * - use less common registers, and avoid pushing these reg into stack */ XXH_COMPILER_GUARD(dst16); # endif XXH_ASSERT(((size_t)src16 & 15) == 0); /* control alignment */ XXH_ASSERT(((size_t)dst16 & 15) == 0); for (i=0; i < nbRounds; ++i) { dst16[i] = _mm_add_epi64(_mm_load_si128((const __m128i *)src16+i), seed); } } } #endif #if (XXH_VECTOR == XXH_NEON) /* forward declarations for the scalar routines */ XXH_FORCE_INLINE void XXH3_scalarRound(void* XXH_RESTRICT acc, void const* XXH_RESTRICT input, void const* XXH_RESTRICT secret, size_t lane); XXH_FORCE_INLINE void XXH3_scalarScrambleRound(void* XXH_RESTRICT acc, void const* XXH_RESTRICT secret, size_t lane); /*! * @internal * @brief The bulk processing loop for NEON and WASM SIMD128. * * The NEON code path is actually partially scalar when running on AArch64. This * is to optimize the pipelining and can have up to 15% speedup depending on the * CPU, and it also mitigates some GCC codegen issues. * * @see XXH3_NEON_LANES for configuring this and details about this optimization. * * NEON's 32-bit to 64-bit long multiply takes a half vector of 32-bit * integers instead of the other platforms which mask full 64-bit vectors, * so the setup is more complicated than just shifting right. * * Additionally, there is an optimization for 4 lanes at once noted below. * * Since, as stated, the most optimal amount of lanes for Cortexes is 6, * there needs to be *three* versions of the accumulate operation used * for the remaining 2 lanes. * * WASM's SIMD128 uses SIMDe's arm_neon.h polyfill because the intrinsics overlap * nearly perfectly. */ XXH_FORCE_INLINE void XXH3_accumulate_512_neon( void* XXH_RESTRICT acc, const void* XXH_RESTRICT input, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 15) == 0); XXH_STATIC_ASSERT(XXH3_NEON_LANES > 0 && XXH3_NEON_LANES <= XXH_ACC_NB && XXH3_NEON_LANES % 2 == 0); { /* GCC for darwin arm64 does not like aliasing here */ xxh_aliasing_uint64x2_t* const xacc = (xxh_aliasing_uint64x2_t*) acc; /* We don't use a uint32x4_t pointer because it causes bus errors on ARMv7. */ uint8_t const* xinput = (const uint8_t *) input; uint8_t const* xsecret = (const uint8_t *) secret; size_t i; #ifdef __wasm_simd128__ /* * On WASM SIMD128, Clang emits direct address loads when XXH3_kSecret * is constant propagated, which results in it converting it to this * inside the loop: * * a = v128.load(XXH3_kSecret + 0 + $secret_offset, offset = 0) * b = v128.load(XXH3_kSecret + 16 + $secret_offset, offset = 0) * ... * * This requires a full 32-bit address immediate (and therefore a 6 byte * instruction) as well as an add for each offset. * * Putting an asm guard prevents it from folding (at the cost of losing * the alignment hint), and uses the free offset in `v128.load` instead * of adding secret_offset each time which overall reduces code size by * about a kilobyte and improves performance. */ XXH_COMPILER_GUARD(xsecret); #endif /* Scalar lanes use the normal scalarRound routine */ for (i = XXH3_NEON_LANES; i < XXH_ACC_NB; i++) { XXH3_scalarRound(acc, input, secret, i); } i = 0; /* 4 NEON lanes at a time. */ for (; i+1 < XXH3_NEON_LANES / 2; i+=2) { /* data_vec = xinput[i]; */ uint64x2_t data_vec_1 = XXH_vld1q_u64(xinput + (i * 16)); uint64x2_t data_vec_2 = XXH_vld1q_u64(xinput + ((i+1) * 16)); /* key_vec = xsecret[i]; */ uint64x2_t key_vec_1 = XXH_vld1q_u64(xsecret + (i * 16)); uint64x2_t key_vec_2 = XXH_vld1q_u64(xsecret + ((i+1) * 16)); /* data_swap = swap(data_vec) */ uint64x2_t data_swap_1 = vextq_u64(data_vec_1, data_vec_1, 1); uint64x2_t data_swap_2 = vextq_u64(data_vec_2, data_vec_2, 1); /* data_key = data_vec ^ key_vec; */ uint64x2_t data_key_1 = veorq_u64(data_vec_1, key_vec_1); uint64x2_t data_key_2 = veorq_u64(data_vec_2, key_vec_2); /* * If we reinterpret the 64x2 vectors as 32x4 vectors, we can use a * de-interleave operation for 4 lanes in 1 step with `vuzpq_u32` to * get one vector with the low 32 bits of each lane, and one vector * with the high 32 bits of each lane. * * The intrinsic returns a double vector because the original ARMv7-a * instruction modified both arguments in place. AArch64 and SIMD128 emit * two instructions from this intrinsic. * * [ dk11L | dk11H | dk12L | dk12H ] -> [ dk11L | dk12L | dk21L | dk22L ] * [ dk21L | dk21H | dk22L | dk22H ] -> [ dk11H | dk12H | dk21H | dk22H ] */ uint32x4x2_t unzipped = vuzpq_u32( vreinterpretq_u32_u64(data_key_1), vreinterpretq_u32_u64(data_key_2) ); /* data_key_lo = data_key & 0xFFFFFFFF */ uint32x4_t data_key_lo = unzipped.val[0]; /* data_key_hi = data_key >> 32 */ uint32x4_t data_key_hi = unzipped.val[1]; /* * Then, we can split the vectors horizontally and multiply which, as for most * widening intrinsics, have a variant that works on both high half vectors * for free on AArch64. A similar instruction is available on SIMD128. * * sum = data_swap + (u64x2) data_key_lo * (u64x2) data_key_hi */ uint64x2_t sum_1 = XXH_vmlal_low_u32(data_swap_1, data_key_lo, data_key_hi); uint64x2_t sum_2 = XXH_vmlal_high_u32(data_swap_2, data_key_lo, data_key_hi); /* * Clang reorders * a += b * c; // umlal swap.2d, dkl.2s, dkh.2s * c += a; // add acc.2d, acc.2d, swap.2d * to * c += a; // add acc.2d, acc.2d, swap.2d * c += b * c; // umlal acc.2d, dkl.2s, dkh.2s * * While it would make sense in theory since the addition is faster, * for reasons likely related to umlal being limited to certain NEON * pipelines, this is worse. A compiler guard fixes this. */ XXH_COMPILER_GUARD_CLANG_NEON(sum_1); XXH_COMPILER_GUARD_CLANG_NEON(sum_2); /* xacc[i] = acc_vec + sum; */ xacc[i] = vaddq_u64(xacc[i], sum_1); xacc[i+1] = vaddq_u64(xacc[i+1], sum_2); } /* Operate on the remaining NEON lanes 2 at a time. */ for (; i < XXH3_NEON_LANES / 2; i++) { /* data_vec = xinput[i]; */ uint64x2_t data_vec = XXH_vld1q_u64(xinput + (i * 16)); /* key_vec = xsecret[i]; */ uint64x2_t key_vec = XXH_vld1q_u64(xsecret + (i * 16)); /* acc_vec_2 = swap(data_vec) */ uint64x2_t data_swap = vextq_u64(data_vec, data_vec, 1); /* data_key = data_vec ^ key_vec; */ uint64x2_t data_key = veorq_u64(data_vec, key_vec); /* For two lanes, just use VMOVN and VSHRN. */ /* data_key_lo = data_key & 0xFFFFFFFF; */ uint32x2_t data_key_lo = vmovn_u64(data_key); /* data_key_hi = data_key >> 32; */ uint32x2_t data_key_hi = vshrn_n_u64(data_key, 32); /* sum = data_swap + (u64x2) data_key_lo * (u64x2) data_key_hi; */ uint64x2_t sum = vmlal_u32(data_swap, data_key_lo, data_key_hi); /* Same Clang workaround as before */ XXH_COMPILER_GUARD_CLANG_NEON(sum); /* xacc[i] = acc_vec + sum; */ xacc[i] = vaddq_u64 (xacc[i], sum); } } } XXH_FORCE_INLINE XXH3_ACCUMULATE_TEMPLATE(neon) XXH_FORCE_INLINE void XXH3_scrambleAcc_neon(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 15) == 0); { xxh_aliasing_uint64x2_t* xacc = (xxh_aliasing_uint64x2_t*) acc; uint8_t const* xsecret = (uint8_t const*) secret; size_t i; /* WASM uses operator overloads and doesn't need these. */ #ifndef __wasm_simd128__ /* { prime32_1, prime32_1 } */ uint32x2_t const kPrimeLo = vdup_n_u32(XXH_PRIME32_1); /* { 0, prime32_1, 0, prime32_1 } */ uint32x4_t const kPrimeHi = vreinterpretq_u32_u64(vdupq_n_u64((xxh_u64)XXH_PRIME32_1 << 32)); #endif /* AArch64 uses both scalar and neon at the same time */ for (i = XXH3_NEON_LANES; i < XXH_ACC_NB; i++) { XXH3_scalarScrambleRound(acc, secret, i); } for (i=0; i < XXH3_NEON_LANES / 2; i++) { /* xacc[i] ^= (xacc[i] >> 47); */ uint64x2_t acc_vec = xacc[i]; uint64x2_t shifted = vshrq_n_u64(acc_vec, 47); uint64x2_t data_vec = veorq_u64(acc_vec, shifted); /* xacc[i] ^= xsecret[i]; */ uint64x2_t key_vec = XXH_vld1q_u64(xsecret + (i * 16)); uint64x2_t data_key = veorq_u64(data_vec, key_vec); /* xacc[i] *= XXH_PRIME32_1 */ #ifdef __wasm_simd128__ /* SIMD128 has multiply by u64x2, use it instead of expanding and scalarizing */ xacc[i] = data_key * XXH_PRIME32_1; #else /* * Expanded version with portable NEON intrinsics * * lo(x) * lo(y) + (hi(x) * lo(y) << 32) * * prod_hi = hi(data_key) * lo(prime) << 32 * * Since we only need 32 bits of this multiply a trick can be used, reinterpreting the vector * as a uint32x4_t and multiplying by { 0, prime, 0, prime } to cancel out the unwanted bits * and avoid the shift. */ uint32x4_t prod_hi = vmulq_u32 (vreinterpretq_u32_u64(data_key), kPrimeHi); /* Extract low bits for vmlal_u32 */ uint32x2_t data_key_lo = vmovn_u64(data_key); /* xacc[i] = prod_hi + lo(data_key) * XXH_PRIME32_1; */ xacc[i] = vmlal_u32(vreinterpretq_u64_u32(prod_hi), data_key_lo, kPrimeLo); #endif } } } #endif #if (XXH_VECTOR == XXH_VSX) XXH_FORCE_INLINE void XXH3_accumulate_512_vsx( void* XXH_RESTRICT acc, const void* XXH_RESTRICT input, const void* XXH_RESTRICT secret) { /* presumed aligned */ xxh_aliasing_u64x2* const xacc = (xxh_aliasing_u64x2*) acc; xxh_u8 const* const xinput = (xxh_u8 const*) input; /* no alignment restriction */ xxh_u8 const* const xsecret = (xxh_u8 const*) secret; /* no alignment restriction */ xxh_u64x2 const v32 = { 32, 32 }; size_t i; for (i = 0; i < XXH_STRIPE_LEN / sizeof(xxh_u64x2); i++) { /* data_vec = xinput[i]; */ xxh_u64x2 const data_vec = XXH_vec_loadu(xinput + 16*i); /* key_vec = xsecret[i]; */ xxh_u64x2 const key_vec = XXH_vec_loadu(xsecret + 16*i); xxh_u64x2 const data_key = data_vec ^ key_vec; /* shuffled = (data_key << 32) | (data_key >> 32); */ xxh_u32x4 const shuffled = (xxh_u32x4)vec_rl(data_key, v32); /* product = ((xxh_u64x2)data_key & 0xFFFFFFFF) * ((xxh_u64x2)shuffled & 0xFFFFFFFF); */ xxh_u64x2 const product = XXH_vec_mulo((xxh_u32x4)data_key, shuffled); /* acc_vec = xacc[i]; */ xxh_u64x2 acc_vec = xacc[i]; acc_vec += product; /* swap high and low halves */ #ifdef __s390x__ acc_vec += vec_permi(data_vec, data_vec, 2); #else acc_vec += vec_xxpermdi(data_vec, data_vec, 2); #endif xacc[i] = acc_vec; } } XXH_FORCE_INLINE XXH3_ACCUMULATE_TEMPLATE(vsx) XXH_FORCE_INLINE void XXH3_scrambleAcc_vsx(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) { XXH_ASSERT((((size_t)acc) & 15) == 0); { xxh_aliasing_u64x2* const xacc = (xxh_aliasing_u64x2*) acc; const xxh_u8* const xsecret = (const xxh_u8*) secret; /* constants */ xxh_u64x2 const v32 = { 32, 32 }; xxh_u64x2 const v47 = { 47, 47 }; xxh_u32x4 const prime = { XXH_PRIME32_1, XXH_PRIME32_1, XXH_PRIME32_1, XXH_PRIME32_1 }; size_t i; for (i = 0; i < XXH_STRIPE_LEN / sizeof(xxh_u64x2); i++) { /* xacc[i] ^= (xacc[i] >> 47); */ xxh_u64x2 const acc_vec = xacc[i]; xxh_u64x2 const data_vec = acc_vec ^ (acc_vec >> v47); /* xacc[i] ^= xsecret[i]; */ xxh_u64x2 const key_vec = XXH_vec_loadu(xsecret + 16*i); xxh_u64x2 const data_key = data_vec ^ key_vec; /* xacc[i] *= XXH_PRIME32_1 */ /* prod_lo = ((xxh_u64x2)data_key & 0xFFFFFFFF) * ((xxh_u64x2)prime & 0xFFFFFFFF); */ xxh_u64x2 const prod_even = XXH_vec_mule((xxh_u32x4)data_key, prime); /* prod_hi = ((xxh_u64x2)data_key >> 32) * ((xxh_u64x2)prime >> 32); */ xxh_u64x2 const prod_odd = XXH_vec_mulo((xxh_u32x4)data_key, prime); xacc[i] = prod_odd + (prod_even << v32); } } } #endif #if (XXH_VECTOR == XXH_SVE) XXH_FORCE_INLINE void XXH3_accumulate_512_sve( void* XXH_RESTRICT acc, const void* XXH_RESTRICT input, const void* XXH_RESTRICT secret) { uint64_t *xacc = (uint64_t *)acc; const uint64_t *xinput = (const uint64_t *)(const void *)input; const uint64_t *xsecret = (const uint64_t *)(const void *)secret; svuint64_t kSwap = sveor_n_u64_z(svptrue_b64(), svindex_u64(0, 1), 1); uint64_t element_count = svcntd(); if (element_count >= 8) { svbool_t mask = svptrue_pat_b64(SV_VL8); svuint64_t vacc = svld1_u64(mask, xacc); ACCRND(vacc, 0); svst1_u64(mask, xacc, vacc); } else if (element_count == 2) { /* sve128 */ svbool_t mask = svptrue_pat_b64(SV_VL2); svuint64_t acc0 = svld1_u64(mask, xacc + 0); svuint64_t acc1 = svld1_u64(mask, xacc + 2); svuint64_t acc2 = svld1_u64(mask, xacc + 4); svuint64_t acc3 = svld1_u64(mask, xacc + 6); ACCRND(acc0, 0); ACCRND(acc1, 2); ACCRND(acc2, 4); ACCRND(acc3, 6); svst1_u64(mask, xacc + 0, acc0); svst1_u64(mask, xacc + 2, acc1); svst1_u64(mask, xacc + 4, acc2); svst1_u64(mask, xacc + 6, acc3); } else { svbool_t mask = svptrue_pat_b64(SV_VL4); svuint64_t acc0 = svld1_u64(mask, xacc + 0); svuint64_t acc1 = svld1_u64(mask, xacc + 4); ACCRND(acc0, 0); ACCRND(acc1, 4); svst1_u64(mask, xacc + 0, acc0); svst1_u64(mask, xacc + 4, acc1); } } XXH_FORCE_INLINE void XXH3_accumulate_sve(xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT input, const xxh_u8* XXH_RESTRICT secret, size_t nbStripes) { if (nbStripes != 0) { uint64_t *xacc = (uint64_t *)acc; const uint64_t *xinput = (const uint64_t *)(const void *)input; const uint64_t *xsecret = (const uint64_t *)(const void *)secret; svuint64_t kSwap = sveor_n_u64_z(svptrue_b64(), svindex_u64(0, 1), 1); uint64_t element_count = svcntd(); if (element_count >= 8) { svbool_t mask = svptrue_pat_b64(SV_VL8); svuint64_t vacc = svld1_u64(mask, xacc + 0); do { /* svprfd(svbool_t, void *, enum svfprop); */ svprfd(mask, xinput + 128, SV_PLDL1STRM); ACCRND(vacc, 0); xinput += 8; xsecret += 1; nbStripes--; } while (nbStripes != 0); svst1_u64(mask, xacc + 0, vacc); } else if (element_count == 2) { /* sve128 */ svbool_t mask = svptrue_pat_b64(SV_VL2); svuint64_t acc0 = svld1_u64(mask, xacc + 0); svuint64_t acc1 = svld1_u64(mask, xacc + 2); svuint64_t acc2 = svld1_u64(mask, xacc + 4); svuint64_t acc3 = svld1_u64(mask, xacc + 6); do { svprfd(mask, xinput + 128, SV_PLDL1STRM); ACCRND(acc0, 0); ACCRND(acc1, 2); ACCRND(acc2, 4); ACCRND(acc3, 6); xinput += 8; xsecret += 1; nbStripes--; } while (nbStripes != 0); svst1_u64(mask, xacc + 0, acc0); svst1_u64(mask, xacc + 2, acc1); svst1_u64(mask, xacc + 4, acc2); svst1_u64(mask, xacc + 6, acc3); } else { svbool_t mask = svptrue_pat_b64(SV_VL4); svuint64_t acc0 = svld1_u64(mask, xacc + 0); svuint64_t acc1 = svld1_u64(mask, xacc + 4); do { svprfd(mask, xinput + 128, SV_PLDL1STRM); ACCRND(acc0, 0); ACCRND(acc1, 4); xinput += 8; xsecret += 1; nbStripes--; } while (nbStripes != 0); svst1_u64(mask, xacc + 0, acc0); svst1_u64(mask, xacc + 4, acc1); } } } #endif /* scalar variants - universal */ #if defined(__aarch64__) && (defined(__GNUC__) || defined(__clang__)) /* * In XXH3_scalarRound(), GCC and Clang have a similar codegen issue, where they * emit an excess mask and a full 64-bit multiply-add (MADD X-form). * * While this might not seem like much, as AArch64 is a 64-bit architecture, only * big Cortex designs have a full 64-bit multiplier. * * On the little cores, the smaller 32-bit multiplier is used, and full 64-bit * multiplies expand to 2-3 multiplies in microcode. This has a major penalty * of up to 4 latency cycles and 2 stall cycles in the multiply pipeline. * * Thankfully, AArch64 still provides the 32-bit long multiply-add (UMADDL) which does * not have this penalty and does the mask automatically. */ XXH_FORCE_INLINE xxh_u64 XXH_mult32to64_add64(xxh_u64 lhs, xxh_u64 rhs, xxh_u64 acc) { xxh_u64 ret; /* note: %x = 64-bit register, %w = 32-bit register */ __asm__("umaddl %x0, %w1, %w2, %x3" : "=r" (ret) : "r" (lhs), "r" (rhs), "r" (acc)); return ret; } #else XXH_FORCE_INLINE xxh_u64 XXH_mult32to64_add64(xxh_u64 lhs, xxh_u64 rhs, xxh_u64 acc) { return XXH_mult32to64((xxh_u32)lhs, (xxh_u32)rhs) + acc; } #endif /*! * @internal * @brief Scalar round for @ref XXH3_accumulate_512_scalar(). * * This is extracted to its own function because the NEON path uses a combination * of NEON and scalar. */ XXH_FORCE_INLINE void XXH3_scalarRound(void* XXH_RESTRICT acc, void const* XXH_RESTRICT input, void const* XXH_RESTRICT secret, size_t lane) { xxh_u64* xacc = (xxh_u64*) acc; xxh_u8 const* xinput = (xxh_u8 const*) input; xxh_u8 const* xsecret = (xxh_u8 const*) secret; XXH_ASSERT(lane < XXH_ACC_NB); XXH_ASSERT(((size_t)acc & (XXH_ACC_ALIGN-1)) == 0); { xxh_u64 const data_val = XXH_readLE64(xinput + lane * 8); xxh_u64 const data_key = data_val ^ XXH_readLE64(xsecret + lane * 8); xacc[lane ^ 1] += data_val; /* swap adjacent lanes */ xacc[lane] = XXH_mult32to64_add64(data_key /* & 0xFFFFFFFF */, data_key >> 32, xacc[lane]); } } /*! * @internal * @brief Processes a 64 byte block of data using the scalar path. */ XXH_FORCE_INLINE void XXH3_accumulate_512_scalar(void* XXH_RESTRICT acc, const void* XXH_RESTRICT input, const void* XXH_RESTRICT secret) { size_t i; /* ARM GCC refuses to unroll this loop, resulting in a 24% slowdown on ARMv6. */ #if defined(__GNUC__) && !defined(__clang__) \ && (defined(__arm__) || defined(__thumb2__)) \ && defined(__ARM_FEATURE_UNALIGNED) /* no unaligned access just wastes bytes */ \ && XXH_SIZE_OPT <= 0 # pragma GCC unroll 8 #endif for (i=0; i < XXH_ACC_NB; i++) { XXH3_scalarRound(acc, input, secret, i); } } XXH_FORCE_INLINE XXH3_ACCUMULATE_TEMPLATE(scalar) /*! * @internal * @brief Scalar scramble step for @ref XXH3_scrambleAcc_scalar(). * * This is extracted to its own function because the NEON path uses a combination * of NEON and scalar. */ XXH_FORCE_INLINE void XXH3_scalarScrambleRound(void* XXH_RESTRICT acc, void const* XXH_RESTRICT secret, size_t lane) { xxh_u64* const xacc = (xxh_u64*) acc; /* presumed aligned */ const xxh_u8* const xsecret = (const xxh_u8*) secret; /* no alignment restriction */ XXH_ASSERT((((size_t)acc) & (XXH_ACC_ALIGN-1)) == 0); XXH_ASSERT(lane < XXH_ACC_NB); { xxh_u64 const key64 = XXH_readLE64(xsecret + lane * 8); xxh_u64 acc64 = xacc[lane]; acc64 = XXH_xorshift64(acc64, 47); acc64 ^= key64; acc64 *= XXH_PRIME32_1; xacc[lane] = acc64; } } /*! * @internal * @brief Scrambles the accumulators after a large chunk has been read */ XXH_FORCE_INLINE void XXH3_scrambleAcc_scalar(void* XXH_RESTRICT acc, const void* XXH_RESTRICT secret) { size_t i; for (i=0; i < XXH_ACC_NB; i++) { XXH3_scalarScrambleRound(acc, secret, i); } } XXH_FORCE_INLINE void XXH3_initCustomSecret_scalar(void* XXH_RESTRICT customSecret, xxh_u64 seed64) { /* * We need a separate pointer for the hack below, * which requires a non-const pointer. * Any decent compiler will optimize this out otherwise. */ const xxh_u8* kSecretPtr = XXH3_kSecret; XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0); #if defined(__GNUC__) && defined(__aarch64__) /* * UGLY HACK: * GCC and Clang generate a bunch of MOV/MOVK pairs for aarch64, and they are * placed sequentially, in order, at the top of the unrolled loop. * * While MOVK is great for generating constants (2 cycles for a 64-bit * constant compared to 4 cycles for LDR), it fights for bandwidth with * the arithmetic instructions. * * I L S * MOVK * MOVK * MOVK * MOVK * ADD * SUB STR * STR * By forcing loads from memory (as the asm line causes the compiler to assume * that XXH3_kSecretPtr has been changed), the pipelines are used more * efficiently: * I L S * LDR * ADD LDR * SUB STR * STR * * See XXH3_NEON_LANES for details on the pipsline. * * XXH3_64bits_withSeed, len == 256, Snapdragon 835 * without hack: 2654.4 MB/s * with hack: 3202.9 MB/s */ XXH_COMPILER_GUARD(kSecretPtr); #endif { int const nbRounds = XXH_SECRET_DEFAULT_SIZE / 16; int i; for (i=0; i < nbRounds; i++) { /* * The asm hack causes the compiler to assume that kSecretPtr aliases with * customSecret, and on aarch64, this prevented LDP from merging two * loads together for free. Putting the loads together before the stores * properly generates LDP. */ xxh_u64 lo = XXH_readLE64(kSecretPtr + 16*i) + seed64; xxh_u64 hi = XXH_readLE64(kSecretPtr + 16*i + 8) - seed64; XXH_writeLE64((xxh_u8*)customSecret + 16*i, lo); XXH_writeLE64((xxh_u8*)customSecret + 16*i + 8, hi); } } } typedef void (*XXH3_f_accumulate)(xxh_u64* XXH_RESTRICT, const xxh_u8* XXH_RESTRICT, const xxh_u8* XXH_RESTRICT, size_t); typedef void (*XXH3_f_scrambleAcc)(void* XXH_RESTRICT, const void*); typedef void (*XXH3_f_initCustomSecret)(void* XXH_RESTRICT, xxh_u64); #if (XXH_VECTOR == XXH_AVX512) #define XXH3_accumulate_512 XXH3_accumulate_512_avx512 #define XXH3_accumulate XXH3_accumulate_avx512 #define XXH3_scrambleAcc XXH3_scrambleAcc_avx512 #define XXH3_initCustomSecret XXH3_initCustomSecret_avx512 #elif (XXH_VECTOR == XXH_AVX2) #define XXH3_accumulate_512 XXH3_accumulate_512_avx2 #define XXH3_accumulate XXH3_accumulate_avx2 #define XXH3_scrambleAcc XXH3_scrambleAcc_avx2 #define XXH3_initCustomSecret XXH3_initCustomSecret_avx2 #elif (XXH_VECTOR == XXH_SSE2) #define XXH3_accumulate_512 XXH3_accumulate_512_sse2 #define XXH3_accumulate XXH3_accumulate_sse2 #define XXH3_scrambleAcc XXH3_scrambleAcc_sse2 #define XXH3_initCustomSecret XXH3_initCustomSecret_sse2 #elif (XXH_VECTOR == XXH_NEON) #define XXH3_accumulate_512 XXH3_accumulate_512_neon #define XXH3_accumulate XXH3_accumulate_neon #define XXH3_scrambleAcc XXH3_scrambleAcc_neon #define XXH3_initCustomSecret XXH3_initCustomSecret_scalar #elif (XXH_VECTOR == XXH_VSX) #define XXH3_accumulate_512 XXH3_accumulate_512_vsx #define XXH3_accumulate XXH3_accumulate_vsx #define XXH3_scrambleAcc XXH3_scrambleAcc_vsx #define XXH3_initCustomSecret XXH3_initCustomSecret_scalar #elif (XXH_VECTOR == XXH_SVE) #define XXH3_accumulate_512 XXH3_accumulate_512_sve #define XXH3_accumulate XXH3_accumulate_sve #define XXH3_scrambleAcc XXH3_scrambleAcc_scalar #define XXH3_initCustomSecret XXH3_initCustomSecret_scalar #else /* scalar */ #define XXH3_accumulate_512 XXH3_accumulate_512_scalar #define XXH3_accumulate XXH3_accumulate_scalar #define XXH3_scrambleAcc XXH3_scrambleAcc_scalar #define XXH3_initCustomSecret XXH3_initCustomSecret_scalar #endif #if XXH_SIZE_OPT >= 1 /* don't do SIMD for initialization */ # undef XXH3_initCustomSecret # define XXH3_initCustomSecret XXH3_initCustomSecret_scalar #endif XXH_FORCE_INLINE void XXH3_hashLong_internal_loop(xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT input, size_t len, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, XXH3_f_accumulate f_acc, XXH3_f_scrambleAcc f_scramble) { size_t const nbStripesPerBlock = (secretSize - XXH_STRIPE_LEN) / XXH_SECRET_CONSUME_RATE; size_t const block_len = XXH_STRIPE_LEN * nbStripesPerBlock; size_t const nb_blocks = (len - 1) / block_len; size_t n; XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); for (n = 0; n < nb_blocks; n++) { f_acc(acc, input + n*block_len, secret, nbStripesPerBlock); f_scramble(acc, secret + secretSize - XXH_STRIPE_LEN); } /* last partial block */ XXH_ASSERT(len > XXH_STRIPE_LEN); { size_t const nbStripes = ((len - 1) - (block_len * nb_blocks)) / XXH_STRIPE_LEN; XXH_ASSERT(nbStripes <= (secretSize / XXH_SECRET_CONSUME_RATE)); f_acc(acc, input + nb_blocks*block_len, secret, nbStripes); /* last stripe */ { const xxh_u8* const p = input + len - XXH_STRIPE_LEN; #define XXH_SECRET_LASTACC_START 7 /* not aligned on 8, last secret is different from acc & scrambler */ XXH3_accumulate_512(acc, p, secret + secretSize - XXH_STRIPE_LEN - XXH_SECRET_LASTACC_START); } } } XXH_FORCE_INLINE xxh_u64 XXH3_mix2Accs(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret) { return XXH3_mul128_fold64( acc[0] ^ XXH_readLE64(secret), acc[1] ^ XXH_readLE64(secret+8) ); } static XXH64_hash_t XXH3_mergeAccs(const xxh_u64* XXH_RESTRICT acc, const xxh_u8* XXH_RESTRICT secret, xxh_u64 start) { xxh_u64 result64 = start; size_t i = 0; for (i = 0; i < 4; i++) { result64 += XXH3_mix2Accs(acc+2*i, secret + 16*i); #if defined(__clang__) /* Clang */ \ && (defined(__arm__) || defined(__thumb__)) /* ARMv7 */ \ && (defined(__ARM_NEON) || defined(__ARM_NEON__)) /* NEON */ \ && !defined(XXH_ENABLE_AUTOVECTORIZE) /* Define to disable */ /* * UGLY HACK: * Prevent autovectorization on Clang ARMv7-a. Exact same problem as * the one in XXH3_len_129to240_64b. Speeds up shorter keys > 240b. * XXH3_64bits, len == 256, Snapdragon 835: * without hack: 2063.7 MB/s * with hack: 2560.7 MB/s */ XXH_COMPILER_GUARD(result64); #endif } return XXH3_avalanche(result64); } #define XXH3_INIT_ACC { XXH_PRIME32_3, XXH_PRIME64_1, XXH_PRIME64_2, XXH_PRIME64_3, \ XXH_PRIME64_4, XXH_PRIME32_2, XXH_PRIME64_5, XXH_PRIME32_1 } XXH_FORCE_INLINE XXH64_hash_t XXH3_hashLong_64b_internal(const void* XXH_RESTRICT input, size_t len, const void* XXH_RESTRICT secret, size_t secretSize, XXH3_f_accumulate f_acc, XXH3_f_scrambleAcc f_scramble) { XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC; XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, (const xxh_u8*)secret, secretSize, f_acc, f_scramble); /* converge into final hash */ XXH_STATIC_ASSERT(sizeof(acc) == 64); /* do not align on 8, so that the secret is different from the accumulator */ #define XXH_SECRET_MERGEACCS_START 11 XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); return XXH3_mergeAccs(acc, (const xxh_u8*)secret + XXH_SECRET_MERGEACCS_START, (xxh_u64)len * XXH_PRIME64_1); } /* * It's important for performance to transmit secret's size (when it's static) * so that the compiler can properly optimize the vectorized loop. * This makes a big performance difference for "medium" keys (<1 KB) when using AVX instruction set. * When the secret size is unknown, or on GCC 12 where the mix of NO_INLINE and FORCE_INLINE * breaks -Og, this is XXH_NO_INLINE. */ XXH3_WITH_SECRET_INLINE XXH64_hash_t XXH3_hashLong_64b_withSecret(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, const xxh_u8* XXH_RESTRICT secret, size_t secretLen) { (void)seed64; return XXH3_hashLong_64b_internal(input, len, secret, secretLen, XXH3_accumulate, XXH3_scrambleAcc); } /* * It's preferable for performance that XXH3_hashLong is not inlined, * as it results in a smaller function for small data, easier to the instruction cache. * Note that inside this no_inline function, we do inline the internal loop, * and provide a statically defined secret size to allow optimization of vector loop. */ XXH_NO_INLINE XXH_PUREF XXH64_hash_t XXH3_hashLong_64b_default(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, const xxh_u8* XXH_RESTRICT secret, size_t secretLen) { (void)seed64; (void)secret; (void)secretLen; return XXH3_hashLong_64b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_accumulate, XXH3_scrambleAcc); } /* * XXH3_hashLong_64b_withSeed(): * Generate a custom key based on alteration of default XXH3_kSecret with the seed, * and then use this key for long mode hashing. * * This operation is decently fast but nonetheless costs a little bit of time. * Try to avoid it whenever possible (typically when seed==0). * * It's important for performance that XXH3_hashLong is not inlined. Not sure * why (uop cache maybe?), but the difference is large and easily measurable. */ XXH_FORCE_INLINE XXH64_hash_t XXH3_hashLong_64b_withSeed_internal(const void* input, size_t len, XXH64_hash_t seed, XXH3_f_accumulate f_acc, XXH3_f_scrambleAcc f_scramble, XXH3_f_initCustomSecret f_initSec) { #if XXH_SIZE_OPT <= 0 if (seed == 0) return XXH3_hashLong_64b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), f_acc, f_scramble); #endif { XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE]; f_initSec(secret, seed); return XXH3_hashLong_64b_internal(input, len, secret, sizeof(secret), f_acc, f_scramble); } } /* * It's important for performance that XXH3_hashLong is not inlined. */ XXH_NO_INLINE XXH64_hash_t XXH3_hashLong_64b_withSeed(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed, const xxh_u8* XXH_RESTRICT secret, size_t secretLen) { (void)secret; (void)secretLen; return XXH3_hashLong_64b_withSeed_internal(input, len, seed, XXH3_accumulate, XXH3_scrambleAcc, XXH3_initCustomSecret); } typedef XXH64_hash_t (*XXH3_hashLong64_f)(const void* XXH_RESTRICT, size_t, XXH64_hash_t, const xxh_u8* XXH_RESTRICT, size_t); XXH_FORCE_INLINE XXH64_hash_t XXH3_64bits_internal(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen, XXH3_hashLong64_f f_hashLong) { XXH_ASSERT(secretLen >= XXH3_SECRET_SIZE_MIN); /* * If an action is to be taken if `secretLen` condition is not respected, * it should be done here. * For now, it's a contract pre-condition. * Adding a check and a branch here would cost performance at every hash. * Also, note that function signature doesn't offer room to return an error. */ if (len <= 16) return XXH3_len_0to16_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, seed64); if (len <= 128) return XXH3_len_17to128_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64); if (len <= XXH3_MIDSIZE_MAX) return XXH3_len_129to240_64b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64); return f_hashLong(input, len, seed64, (const xxh_u8*)secret, secretLen); } /* === Public entry point === */ /*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(XXH_NOESCAPE const void* input, size_t length) { return XXH3_64bits_internal(input, length, 0, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_default); } /*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSecret(XXH_NOESCAPE const void* input, size_t length, XXH_NOESCAPE const void* secret, size_t secretSize) { return XXH3_64bits_internal(input, length, 0, secret, secretSize, XXH3_hashLong_64b_withSecret); } /*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSeed(XXH_NOESCAPE const void* input, size_t length, XXH64_hash_t seed) { return XXH3_64bits_internal(input, length, seed, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_64b_withSeed); } XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSecretandSeed(XXH_NOESCAPE const void* input, size_t length, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed) { if (length <= XXH3_MIDSIZE_MAX) return XXH3_64bits_internal(input, length, seed, XXH3_kSecret, sizeof(XXH3_kSecret), NULL); return XXH3_hashLong_64b_withSecret(input, length, seed, (const xxh_u8*)secret, secretSize); } /* === XXH3 streaming === */ #ifndef XXH_NO_STREAM /* * Malloc's a pointer that is always aligned to align. * * This must be freed with `XXH_alignedFree()`. * * malloc typically guarantees 16 byte alignment on 64-bit systems and 8 byte * alignment on 32-bit. This isn't enough for the 32 byte aligned loads in AVX2 * or on 32-bit, the 16 byte aligned loads in SSE2 and NEON. * * This underalignment previously caused a rather obvious crash which went * completely unnoticed due to XXH3_createState() not actually being tested. * Credit to RedSpah for noticing this bug. * * The alignment is done manually: Functions like posix_memalign or _mm_malloc * are avoided: To maintain portability, we would have to write a fallback * like this anyways, and besides, testing for the existence of library * functions without relying on external build tools is impossible. * * The method is simple: Overallocate, manually align, and store the offset * to the original behind the returned pointer. * * Align must be a power of 2 and 8 <= align <= 128. */ static XXH_MALLOCF void* XXH_alignedMalloc(size_t s, size_t align) { XXH_ASSERT(align <= 128 && align >= 8); /* range check */ XXH_ASSERT((align & (align-1)) == 0); /* power of 2 */ XXH_ASSERT(s != 0 && s < (s + align)); /* empty/overflow */ { /* Overallocate to make room for manual realignment and an offset byte */ xxh_u8* base = (xxh_u8*)XXH_malloc(s + align); if (base != NULL) { /* * Get the offset needed to align this pointer. * * Even if the returned pointer is aligned, there will always be * at least one byte to store the offset to the original pointer. */ size_t offset = align - ((size_t)base & (align - 1)); /* base % align */ /* Add the offset for the now-aligned pointer */ xxh_u8* ptr = base + offset; XXH_ASSERT((size_t)ptr % align == 0); /* Store the offset immediately before the returned pointer. */ ptr[-1] = (xxh_u8)offset; return ptr; } return NULL; } } /* * Frees an aligned pointer allocated by XXH_alignedMalloc(). Don't pass * normal malloc'd pointers, XXH_alignedMalloc has a specific data layout. */ static void XXH_alignedFree(void* p) { if (p != NULL) { xxh_u8* ptr = (xxh_u8*)p; /* Get the offset byte we added in XXH_malloc. */ xxh_u8 offset = ptr[-1]; /* Free the original malloc'd pointer */ xxh_u8* base = ptr - offset; XXH_free(base); } } /*! @ingroup XXH3_family */ /*! * @brief Allocate an @ref XXH3_state_t. * * Must be freed with XXH3_freeState(). * @return An allocated XXH3_state_t on success, `NULL` on failure. */ XXH_PUBLIC_API XXH3_state_t* XXH3_createState(void) { XXH3_state_t* const state = (XXH3_state_t*)XXH_alignedMalloc(sizeof(XXH3_state_t), 64); if (state==NULL) return NULL; XXH3_INITSTATE(state); return state; } /*! @ingroup XXH3_family */ /*! * @brief Frees an @ref XXH3_state_t. * * Must be allocated with XXH3_createState(). * @param statePtr A pointer to an @ref XXH3_state_t allocated with @ref XXH3_createState(). * @return XXH_OK. */ XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr) { XXH_alignedFree(statePtr); return XXH_OK; } /*! @ingroup XXH3_family */ XXH_PUBLIC_API void XXH3_copyState(XXH_NOESCAPE XXH3_state_t* dst_state, XXH_NOESCAPE const XXH3_state_t* src_state) { XXH_memcpy(dst_state, src_state, sizeof(*dst_state)); } static void XXH3_reset_internal(XXH3_state_t* statePtr, XXH64_hash_t seed, const void* secret, size_t secretSize) { size_t const initStart = offsetof(XXH3_state_t, bufferedSize); size_t const initLength = offsetof(XXH3_state_t, nbStripesPerBlock) - initStart; XXH_ASSERT(offsetof(XXH3_state_t, nbStripesPerBlock) > initStart); XXH_ASSERT(statePtr != NULL); /* set members from bufferedSize to nbStripesPerBlock (excluded) to 0 */ memset((char*)statePtr + initStart, 0, initLength); statePtr->acc[0] = XXH_PRIME32_3; statePtr->acc[1] = XXH_PRIME64_1; statePtr->acc[2] = XXH_PRIME64_2; statePtr->acc[3] = XXH_PRIME64_3; statePtr->acc[4] = XXH_PRIME64_4; statePtr->acc[5] = XXH_PRIME32_2; statePtr->acc[6] = XXH_PRIME64_5; statePtr->acc[7] = XXH_PRIME32_1; statePtr->seed = seed; statePtr->useSeed = (seed != 0); statePtr->extSecret = (const unsigned char*)secret; XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); statePtr->secretLimit = secretSize - XXH_STRIPE_LEN; statePtr->nbStripesPerBlock = statePtr->secretLimit / XXH_SECRET_CONSUME_RATE; } /*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset(XXH_NOESCAPE XXH3_state_t* statePtr) { if (statePtr == NULL) return XXH_ERROR; XXH3_reset_internal(statePtr, 0, XXH3_kSecret, XXH_SECRET_DEFAULT_SIZE); return XXH_OK; } /*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSecret(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize) { if (statePtr == NULL) return XXH_ERROR; XXH3_reset_internal(statePtr, 0, secret, secretSize); if (secret == NULL) return XXH_ERROR; if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; return XXH_OK; } /*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH64_hash_t seed) { if (statePtr == NULL) return XXH_ERROR; if (seed==0) return XXH3_64bits_reset(statePtr); if ((seed != statePtr->seed) || (statePtr->extSecret != NULL)) XXH3_initCustomSecret(statePtr->customSecret, seed); XXH3_reset_internal(statePtr, seed, NULL, XXH_SECRET_DEFAULT_SIZE); return XXH_OK; } /*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSecretandSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed64) { if (statePtr == NULL) return XXH_ERROR; if (secret == NULL) return XXH_ERROR; if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; XXH3_reset_internal(statePtr, seed64, secret, secretSize); statePtr->useSeed = 1; /* always, even if seed64==0 */ return XXH_OK; } /*! * @internal * @brief Processes a large input for XXH3_update() and XXH3_digest_long(). * * Unlike XXH3_hashLong_internal_loop(), this can process data that overlaps a block. * * @param acc Pointer to the 8 accumulator lanes * @param nbStripesSoFarPtr In/out pointer to the number of leftover stripes in the block* * @param nbStripesPerBlock Number of stripes in a block * @param input Input pointer * @param nbStripes Number of stripes to process * @param secret Secret pointer * @param secretLimit Offset of the last block in @p secret * @param f_acc Pointer to an XXH3_accumulate implementation * @param f_scramble Pointer to an XXH3_scrambleAcc implementation * @return Pointer past the end of @p input after processing */ XXH_FORCE_INLINE const xxh_u8 * XXH3_consumeStripes(xxh_u64* XXH_RESTRICT acc, size_t* XXH_RESTRICT nbStripesSoFarPtr, size_t nbStripesPerBlock, const xxh_u8* XXH_RESTRICT input, size_t nbStripes, const xxh_u8* XXH_RESTRICT secret, size_t secretLimit, XXH3_f_accumulate f_acc, XXH3_f_scrambleAcc f_scramble) { const xxh_u8* initialSecret = secret + *nbStripesSoFarPtr * XXH_SECRET_CONSUME_RATE; /* Process full blocks */ if (nbStripes >= (nbStripesPerBlock - *nbStripesSoFarPtr)) { /* Process the initial partial block... */ size_t nbStripesThisIter = nbStripesPerBlock - *nbStripesSoFarPtr; do { /* Accumulate and scramble */ f_acc(acc, input, initialSecret, nbStripesThisIter); f_scramble(acc, secret + secretLimit); input += nbStripesThisIter * XXH_STRIPE_LEN; nbStripes -= nbStripesThisIter; /* Then continue the loop with the full block size */ nbStripesThisIter = nbStripesPerBlock; initialSecret = secret; } while (nbStripes >= nbStripesPerBlock); *nbStripesSoFarPtr = 0; } /* Process a partial block */ if (nbStripes > 0) { f_acc(acc, input, initialSecret, nbStripes); input += nbStripes * XXH_STRIPE_LEN; *nbStripesSoFarPtr += nbStripes; } /* Return end pointer */ return input; } #ifndef XXH3_STREAM_USE_STACK # if XXH_SIZE_OPT <= 0 && !defined(__clang__) /* clang doesn't need additional stack space */ # define XXH3_STREAM_USE_STACK 1 # endif #endif /* * Both XXH3_64bits_update and XXH3_128bits_update use this routine. */ XXH_FORCE_INLINE XXH_errorcode XXH3_update(XXH3_state_t* XXH_RESTRICT const state, const xxh_u8* XXH_RESTRICT input, size_t len, XXH3_f_accumulate f_acc, XXH3_f_scrambleAcc f_scramble) { if (input==NULL) { XXH_ASSERT(len == 0); return XXH_OK; } XXH_ASSERT(state != NULL); { const xxh_u8* const bEnd = input + len; const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret; #if defined(XXH3_STREAM_USE_STACK) && XXH3_STREAM_USE_STACK >= 1 /* For some reason, gcc and MSVC seem to suffer greatly * when operating accumulators directly into state. * Operating into stack space seems to enable proper optimization. * clang, on the other hand, doesn't seem to need this trick */ XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[8]; XXH_memcpy(acc, state->acc, sizeof(acc)); #else xxh_u64* XXH_RESTRICT const acc = state->acc; #endif state->totalLen += len; XXH_ASSERT(state->bufferedSize <= XXH3_INTERNALBUFFER_SIZE); /* small input : just fill in tmp buffer */ if (len <= XXH3_INTERNALBUFFER_SIZE - state->bufferedSize) { XXH_memcpy(state->buffer + state->bufferedSize, input, len); state->bufferedSize += (XXH32_hash_t)len; return XXH_OK; } /* total input is now > XXH3_INTERNALBUFFER_SIZE */ #define XXH3_INTERNALBUFFER_STRIPES (XXH3_INTERNALBUFFER_SIZE / XXH_STRIPE_LEN) XXH_STATIC_ASSERT(XXH3_INTERNALBUFFER_SIZE % XXH_STRIPE_LEN == 0); /* clean multiple */ /* * Internal buffer is partially filled (always, except at beginning) * Complete it, then consume it. */ if (state->bufferedSize) { size_t const loadSize = XXH3_INTERNALBUFFER_SIZE - state->bufferedSize; XXH_memcpy(state->buffer + state->bufferedSize, input, loadSize); input += loadSize; XXH3_consumeStripes(acc, &state->nbStripesSoFar, state->nbStripesPerBlock, state->buffer, XXH3_INTERNALBUFFER_STRIPES, secret, state->secretLimit, f_acc, f_scramble); state->bufferedSize = 0; } XXH_ASSERT(input < bEnd); if (bEnd - input > XXH3_INTERNALBUFFER_SIZE) { size_t nbStripes = (size_t)(bEnd - 1 - input) / XXH_STRIPE_LEN; input = XXH3_consumeStripes(acc, &state->nbStripesSoFar, state->nbStripesPerBlock, input, nbStripes, secret, state->secretLimit, f_acc, f_scramble); XXH_memcpy(state->buffer + sizeof(state->buffer) - XXH_STRIPE_LEN, input - XXH_STRIPE_LEN, XXH_STRIPE_LEN); } /* Some remaining input (always) : buffer it */ XXH_ASSERT(input < bEnd); XXH_ASSERT(bEnd - input <= XXH3_INTERNALBUFFER_SIZE); XXH_ASSERT(state->bufferedSize == 0); XXH_memcpy(state->buffer, input, (size_t)(bEnd-input)); state->bufferedSize = (XXH32_hash_t)(bEnd-input); #if defined(XXH3_STREAM_USE_STACK) && XXH3_STREAM_USE_STACK >= 1 /* save stack accumulators into state */ XXH_memcpy(state->acc, acc, sizeof(acc)); #endif } return XXH_OK; } /*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode XXH3_64bits_update(XXH_NOESCAPE XXH3_state_t* state, XXH_NOESCAPE const void* input, size_t len) { return XXH3_update(state, (const xxh_u8*)input, len, XXH3_accumulate, XXH3_scrambleAcc); } XXH_FORCE_INLINE void XXH3_digest_long (XXH64_hash_t* acc, const XXH3_state_t* state, const unsigned char* secret) { xxh_u8 lastStripe[XXH_STRIPE_LEN]; const xxh_u8* lastStripePtr; /* * Digest on a local copy. This way, the state remains unaltered, and it can * continue ingesting more input afterwards. */ XXH_memcpy(acc, state->acc, sizeof(state->acc)); if (state->bufferedSize >= XXH_STRIPE_LEN) { /* Consume remaining stripes then point to remaining data in buffer */ size_t const nbStripes = (state->bufferedSize - 1) / XXH_STRIPE_LEN; size_t nbStripesSoFar = state->nbStripesSoFar; XXH3_consumeStripes(acc, &nbStripesSoFar, state->nbStripesPerBlock, state->buffer, nbStripes, secret, state->secretLimit, XXH3_accumulate, XXH3_scrambleAcc); lastStripePtr = state->buffer + state->bufferedSize - XXH_STRIPE_LEN; } else { /* bufferedSize < XXH_STRIPE_LEN */ /* Copy to temp buffer */ size_t const catchupSize = XXH_STRIPE_LEN - state->bufferedSize; XXH_ASSERT(state->bufferedSize > 0); /* there is always some input buffered */ XXH_memcpy(lastStripe, state->buffer + sizeof(state->buffer) - catchupSize, catchupSize); XXH_memcpy(lastStripe + catchupSize, state->buffer, state->bufferedSize); lastStripePtr = lastStripe; } /* Last stripe */ XXH3_accumulate_512(acc, lastStripePtr, secret + state->secretLimit - XXH_SECRET_LASTACC_START); } /*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (XXH_NOESCAPE const XXH3_state_t* state) { const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret; if (state->totalLen > XXH3_MIDSIZE_MAX) { XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB]; XXH3_digest_long(acc, state, secret); return XXH3_mergeAccs(acc, secret + XXH_SECRET_MERGEACCS_START, (xxh_u64)state->totalLen * XXH_PRIME64_1); } /* totalLen <= XXH3_MIDSIZE_MAX: digesting a short input */ if (state->useSeed) return XXH3_64bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed); return XXH3_64bits_withSecret(state->buffer, (size_t)(state->totalLen), secret, state->secretLimit + XXH_STRIPE_LEN); } #endif /* !XXH_NO_STREAM */ /* ========================================== * XXH3 128 bits (a.k.a XXH128) * ========================================== * XXH3's 128-bit variant has better mixing and strength than the 64-bit variant, * even without counting the significantly larger output size. * * For example, extra steps are taken to avoid the seed-dependent collisions * in 17-240 byte inputs (See XXH3_mix16B and XXH128_mix32B). * * This strength naturally comes at the cost of some speed, especially on short * lengths. Note that longer hashes are about as fast as the 64-bit version * due to it using only a slight modification of the 64-bit loop. * * XXH128 is also more oriented towards 64-bit machines. It is still extremely * fast for a _128-bit_ hash on 32-bit (it usually clears XXH64). */ XXH_FORCE_INLINE XXH_PUREF XXH128_hash_t XXH3_len_1to3_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { /* A doubled version of 1to3_64b with different constants. */ XXH_ASSERT(input != NULL); XXH_ASSERT(1 <= len && len <= 3); XXH_ASSERT(secret != NULL); /* * len = 1: combinedl = { input[0], 0x01, input[0], input[0] } * len = 2: combinedl = { input[1], 0x02, input[0], input[1] } * len = 3: combinedl = { input[2], 0x03, input[0], input[1] } */ { xxh_u8 const c1 = input[0]; xxh_u8 const c2 = input[len >> 1]; xxh_u8 const c3 = input[len - 1]; xxh_u32 const combinedl = ((xxh_u32)c1 <<16) | ((xxh_u32)c2 << 24) | ((xxh_u32)c3 << 0) | ((xxh_u32)len << 8); xxh_u32 const combinedh = XXH_rotl32(XXH_swap32(combinedl), 13); xxh_u64 const bitflipl = (XXH_readLE32(secret) ^ XXH_readLE32(secret+4)) + seed; xxh_u64 const bitfliph = (XXH_readLE32(secret+8) ^ XXH_readLE32(secret+12)) - seed; xxh_u64 const keyed_lo = (xxh_u64)combinedl ^ bitflipl; xxh_u64 const keyed_hi = (xxh_u64)combinedh ^ bitfliph; XXH128_hash_t h128; h128.low64 = XXH64_avalanche(keyed_lo); h128.high64 = XXH64_avalanche(keyed_hi); return h128; } } XXH_FORCE_INLINE XXH_PUREF XXH128_hash_t XXH3_len_4to8_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(input != NULL); XXH_ASSERT(secret != NULL); XXH_ASSERT(4 <= len && len <= 8); seed ^= (xxh_u64)XXH_swap32((xxh_u32)seed) << 32; { xxh_u32 const input_lo = XXH_readLE32(input); xxh_u32 const input_hi = XXH_readLE32(input + len - 4); xxh_u64 const input_64 = input_lo + ((xxh_u64)input_hi << 32); xxh_u64 const bitflip = (XXH_readLE64(secret+16) ^ XXH_readLE64(secret+24)) + seed; xxh_u64 const keyed = input_64 ^ bitflip; /* Shift len to the left to ensure it is even, this avoids even multiplies. */ XXH128_hash_t m128 = XXH_mult64to128(keyed, XXH_PRIME64_1 + (len << 2)); m128.high64 += (m128.low64 << 1); m128.low64 ^= (m128.high64 >> 3); m128.low64 = XXH_xorshift64(m128.low64, 35); m128.low64 *= PRIME_MX2; m128.low64 = XXH_xorshift64(m128.low64, 28); m128.high64 = XXH3_avalanche(m128.high64); return m128; } } XXH_FORCE_INLINE XXH_PUREF XXH128_hash_t XXH3_len_9to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(input != NULL); XXH_ASSERT(secret != NULL); XXH_ASSERT(9 <= len && len <= 16); { xxh_u64 const bitflipl = (XXH_readLE64(secret+32) ^ XXH_readLE64(secret+40)) - seed; xxh_u64 const bitfliph = (XXH_readLE64(secret+48) ^ XXH_readLE64(secret+56)) + seed; xxh_u64 const input_lo = XXH_readLE64(input); xxh_u64 input_hi = XXH_readLE64(input + len - 8); XXH128_hash_t m128 = XXH_mult64to128(input_lo ^ input_hi ^ bitflipl, XXH_PRIME64_1); /* * Put len in the middle of m128 to ensure that the length gets mixed to * both the low and high bits in the 128x64 multiply below. */ m128.low64 += (xxh_u64)(len - 1) << 54; input_hi ^= bitfliph; /* * Add the high 32 bits of input_hi to the high 32 bits of m128, then * add the long product of the low 32 bits of input_hi and XXH_PRIME32_2 to * the high 64 bits of m128. * * The best approach to this operation is different on 32-bit and 64-bit. */ if (sizeof(void *) < sizeof(xxh_u64)) { /* 32-bit */ /* * 32-bit optimized version, which is more readable. * * On 32-bit, it removes an ADC and delays a dependency between the two * halves of m128.high64, but it generates an extra mask on 64-bit. */ m128.high64 += (input_hi & 0xFFFFFFFF00000000ULL) + XXH_mult32to64((xxh_u32)input_hi, XXH_PRIME32_2); } else { /* * 64-bit optimized (albeit more confusing) version. * * Uses some properties of addition and multiplication to remove the mask: * * Let: * a = input_hi.lo = (input_hi & 0x00000000FFFFFFFF) * b = input_hi.hi = (input_hi & 0xFFFFFFFF00000000) * c = XXH_PRIME32_2 * * a + (b * c) * Inverse Property: x + y - x == y * a + (b * (1 + c - 1)) * Distributive Property: x * (y + z) == (x * y) + (x * z) * a + (b * 1) + (b * (c - 1)) * Identity Property: x * 1 == x * a + b + (b * (c - 1)) * * Substitute a, b, and c: * input_hi.hi + input_hi.lo + ((xxh_u64)input_hi.lo * (XXH_PRIME32_2 - 1)) * * Since input_hi.hi + input_hi.lo == input_hi, we get this: * input_hi + ((xxh_u64)input_hi.lo * (XXH_PRIME32_2 - 1)) */ m128.high64 += input_hi + XXH_mult32to64((xxh_u32)input_hi, XXH_PRIME32_2 - 1); } /* m128 ^= XXH_swap64(m128 >> 64); */ m128.low64 ^= XXH_swap64(m128.high64); { /* 128x64 multiply: h128 = m128 * XXH_PRIME64_2; */ XXH128_hash_t h128 = XXH_mult64to128(m128.low64, XXH_PRIME64_2); h128.high64 += m128.high64 * XXH_PRIME64_2; h128.low64 = XXH3_avalanche(h128.low64); h128.high64 = XXH3_avalanche(h128.high64); return h128; } } } /* * Assumption: `secret` size is >= XXH3_SECRET_SIZE_MIN */ XXH_FORCE_INLINE XXH_PUREF XXH128_hash_t XXH3_len_0to16_128b(const xxh_u8* input, size_t len, const xxh_u8* secret, XXH64_hash_t seed) { XXH_ASSERT(len <= 16); { if (len > 8) return XXH3_len_9to16_128b(input, len, secret, seed); if (len >= 4) return XXH3_len_4to8_128b(input, len, secret, seed); if (len) return XXH3_len_1to3_128b(input, len, secret, seed); { XXH128_hash_t h128; xxh_u64 const bitflipl = XXH_readLE64(secret+64) ^ XXH_readLE64(secret+72); xxh_u64 const bitfliph = XXH_readLE64(secret+80) ^ XXH_readLE64(secret+88); h128.low64 = XXH64_avalanche(seed ^ bitflipl); h128.high64 = XXH64_avalanche( seed ^ bitfliph); return h128; } } } /* * A bit slower than XXH3_mix16B, but handles multiply by zero better. */ XXH_FORCE_INLINE XXH128_hash_t XXH128_mix32B(XXH128_hash_t acc, const xxh_u8* input_1, const xxh_u8* input_2, const xxh_u8* secret, XXH64_hash_t seed) { acc.low64 += XXH3_mix16B (input_1, secret+0, seed); acc.low64 ^= XXH_readLE64(input_2) + XXH_readLE64(input_2 + 8); acc.high64 += XXH3_mix16B (input_2, secret+16, seed); acc.high64 ^= XXH_readLE64(input_1) + XXH_readLE64(input_1 + 8); return acc; } XXH_FORCE_INLINE XXH_PUREF XXH128_hash_t XXH3_len_17to128_128b(const xxh_u8* XXH_RESTRICT input, size_t len, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, XXH64_hash_t seed) { XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize; XXH_ASSERT(16 < len && len <= 128); { XXH128_hash_t acc; acc.low64 = len * XXH_PRIME64_1; acc.high64 = 0; #if XXH_SIZE_OPT >= 1 { /* Smaller, but slightly slower. */ unsigned int i = (unsigned int)(len - 1) / 32; do { acc = XXH128_mix32B(acc, input+16*i, input+len-16*(i+1), secret+32*i, seed); } while (i-- != 0); } #else if (len > 32) { if (len > 64) { if (len > 96) { acc = XXH128_mix32B(acc, input+48, input+len-64, secret+96, seed); } acc = XXH128_mix32B(acc, input+32, input+len-48, secret+64, seed); } acc = XXH128_mix32B(acc, input+16, input+len-32, secret+32, seed); } acc = XXH128_mix32B(acc, input, input+len-16, secret, seed); #endif { XXH128_hash_t h128; h128.low64 = acc.low64 + acc.high64; h128.high64 = (acc.low64 * XXH_PRIME64_1) + (acc.high64 * XXH_PRIME64_4) + ((len - seed) * XXH_PRIME64_2); h128.low64 = XXH3_avalanche(h128.low64); h128.high64 = (XXH64_hash_t)0 - XXH3_avalanche(h128.high64); return h128; } } } XXH_NO_INLINE XXH_PUREF XXH128_hash_t XXH3_len_129to240_128b(const xxh_u8* XXH_RESTRICT input, size_t len, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, XXH64_hash_t seed) { XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize; XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX); { XXH128_hash_t acc; unsigned i; acc.low64 = len * XXH_PRIME64_1; acc.high64 = 0; /* * We set as `i` as offset + 32. We do this so that unchanged * `len` can be used as upper bound. This reaches a sweet spot * where both x86 and aarch64 get simple agen and good codegen * for the loop. */ for (i = 32; i < 160; i += 32) { acc = XXH128_mix32B(acc, input + i - 32, input + i - 16, secret + i - 32, seed); } acc.low64 = XXH3_avalanche(acc.low64); acc.high64 = XXH3_avalanche(acc.high64); /* * NB: `i <= len` will duplicate the last 32-bytes if * len % 32 was zero. This is an unfortunate necessity to keep * the hash result stable. */ for (i=160; i <= len; i += 32) { acc = XXH128_mix32B(acc, input + i - 32, input + i - 16, secret + XXH3_MIDSIZE_STARTOFFSET + i - 160, seed); } /* last bytes */ acc = XXH128_mix32B(acc, input + len - 16, input + len - 32, secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET - 16, (XXH64_hash_t)0 - seed); { XXH128_hash_t h128; h128.low64 = acc.low64 + acc.high64; h128.high64 = (acc.low64 * XXH_PRIME64_1) + (acc.high64 * XXH_PRIME64_4) + ((len - seed) * XXH_PRIME64_2); h128.low64 = XXH3_avalanche(h128.low64); h128.high64 = (XXH64_hash_t)0 - XXH3_avalanche(h128.high64); return h128; } } } XXH_FORCE_INLINE XXH128_hash_t XXH3_hashLong_128b_internal(const void* XXH_RESTRICT input, size_t len, const xxh_u8* XXH_RESTRICT secret, size_t secretSize, XXH3_f_accumulate f_acc, XXH3_f_scrambleAcc f_scramble) { XXH_ALIGN(XXH_ACC_ALIGN) xxh_u64 acc[XXH_ACC_NB] = XXH3_INIT_ACC; XXH3_hashLong_internal_loop(acc, (const xxh_u8*)input, len, secret, secretSize, f_acc, f_scramble); /* converge into final hash */ XXH_STATIC_ASSERT(sizeof(acc) == 64); XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); { XXH128_hash_t h128; h128.low64 = XXH3_mergeAccs(acc, secret + XXH_SECRET_MERGEACCS_START, (xxh_u64)len * XXH_PRIME64_1); h128.high64 = XXH3_mergeAccs(acc, secret + secretSize - sizeof(acc) - XXH_SECRET_MERGEACCS_START, ~((xxh_u64)len * XXH_PRIME64_2)); return h128; } } /* * It's important for performance that XXH3_hashLong() is not inlined. */ XXH_NO_INLINE XXH_PUREF XXH128_hash_t XXH3_hashLong_128b_default(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen) { (void)seed64; (void)secret; (void)secretLen; return XXH3_hashLong_128b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_accumulate, XXH3_scrambleAcc); } /* * It's important for performance to pass @p secretLen (when it's static) * to the compiler, so that it can properly optimize the vectorized loop. * * When the secret size is unknown, or on GCC 12 where the mix of NO_INLINE and FORCE_INLINE * breaks -Og, this is XXH_NO_INLINE. */ XXH3_WITH_SECRET_INLINE XXH128_hash_t XXH3_hashLong_128b_withSecret(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen) { (void)seed64; return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, secretLen, XXH3_accumulate, XXH3_scrambleAcc); } XXH_FORCE_INLINE XXH128_hash_t XXH3_hashLong_128b_withSeed_internal(const void* XXH_RESTRICT input, size_t len, XXH64_hash_t seed64, XXH3_f_accumulate f_acc, XXH3_f_scrambleAcc f_scramble, XXH3_f_initCustomSecret f_initSec) { if (seed64 == 0) return XXH3_hashLong_128b_internal(input, len, XXH3_kSecret, sizeof(XXH3_kSecret), f_acc, f_scramble); { XXH_ALIGN(XXH_SEC_ALIGN) xxh_u8 secret[XXH_SECRET_DEFAULT_SIZE]; f_initSec(secret, seed64); return XXH3_hashLong_128b_internal(input, len, (const xxh_u8*)secret, sizeof(secret), f_acc, f_scramble); } } /* * It's important for performance that XXH3_hashLong is not inlined. */ XXH_NO_INLINE XXH128_hash_t XXH3_hashLong_128b_withSeed(const void* input, size_t len, XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen) { (void)secret; (void)secretLen; return XXH3_hashLong_128b_withSeed_internal(input, len, seed64, XXH3_accumulate, XXH3_scrambleAcc, XXH3_initCustomSecret); } typedef XXH128_hash_t (*XXH3_hashLong128_f)(const void* XXH_RESTRICT, size_t, XXH64_hash_t, const void* XXH_RESTRICT, size_t); XXH_FORCE_INLINE XXH128_hash_t XXH3_128bits_internal(const void* input, size_t len, XXH64_hash_t seed64, const void* XXH_RESTRICT secret, size_t secretLen, XXH3_hashLong128_f f_hl128) { XXH_ASSERT(secretLen >= XXH3_SECRET_SIZE_MIN); /* * If an action is to be taken if `secret` conditions are not respected, * it should be done here. * For now, it's a contract pre-condition. * Adding a check and a branch here would cost performance at every hash. */ if (len <= 16) return XXH3_len_0to16_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, seed64); if (len <= 128) return XXH3_len_17to128_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64); if (len <= XXH3_MIDSIZE_MAX) return XXH3_len_129to240_128b((const xxh_u8*)input, len, (const xxh_u8*)secret, secretLen, seed64); return f_hl128(input, len, seed64, secret, secretLen); } /* === Public XXH128 API === */ /*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(XXH_NOESCAPE const void* input, size_t len) { return XXH3_128bits_internal(input, len, 0, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_128b_default); } /*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSecret(XXH_NOESCAPE const void* input, size_t len, XXH_NOESCAPE const void* secret, size_t secretSize) { return XXH3_128bits_internal(input, len, 0, (const xxh_u8*)secret, secretSize, XXH3_hashLong_128b_withSecret); } /*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSeed(XXH_NOESCAPE const void* input, size_t len, XXH64_hash_t seed) { return XXH3_128bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), XXH3_hashLong_128b_withSeed); } /*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSecretandSeed(XXH_NOESCAPE const void* input, size_t len, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed) { if (len <= XXH3_MIDSIZE_MAX) return XXH3_128bits_internal(input, len, seed, XXH3_kSecret, sizeof(XXH3_kSecret), NULL); return XXH3_hashLong_128b_withSecret(input, len, seed, secret, secretSize); } /*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH128_hash_t XXH128(XXH_NOESCAPE const void* input, size_t len, XXH64_hash_t seed) { return XXH3_128bits_withSeed(input, len, seed); } /* === XXH3 128-bit streaming === */ #ifndef XXH_NO_STREAM /* * All initialization and update functions are identical to 64-bit streaming variant. * The only difference is the finalization routine. */ /*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset(XXH_NOESCAPE XXH3_state_t* statePtr) { return XXH3_64bits_reset(statePtr); } /*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSecret(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize) { return XXH3_64bits_reset_withSecret(statePtr, secret, secretSize); } /*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH64_hash_t seed) { return XXH3_64bits_reset_withSeed(statePtr, seed); } /*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSecretandSeed(XXH_NOESCAPE XXH3_state_t* statePtr, XXH_NOESCAPE const void* secret, size_t secretSize, XXH64_hash_t seed) { return XXH3_64bits_reset_withSecretandSeed(statePtr, secret, secretSize, seed); } /*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode XXH3_128bits_update(XXH_NOESCAPE XXH3_state_t* state, XXH_NOESCAPE const void* input, size_t len) { return XXH3_64bits_update(state, input, len); } /*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (XXH_NOESCAPE const XXH3_state_t* state) { const unsigned char* const secret = (state->extSecret == NULL) ? state->customSecret : state->extSecret; if (state->totalLen > XXH3_MIDSIZE_MAX) { XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[XXH_ACC_NB]; XXH3_digest_long(acc, state, secret); XXH_ASSERT(state->secretLimit + XXH_STRIPE_LEN >= sizeof(acc) + XXH_SECRET_MERGEACCS_START); { XXH128_hash_t h128; h128.low64 = XXH3_mergeAccs(acc, secret + XXH_SECRET_MERGEACCS_START, (xxh_u64)state->totalLen * XXH_PRIME64_1); h128.high64 = XXH3_mergeAccs(acc, secret + state->secretLimit + XXH_STRIPE_LEN - sizeof(acc) - XXH_SECRET_MERGEACCS_START, ~((xxh_u64)state->totalLen * XXH_PRIME64_2)); return h128; } } /* len <= XXH3_MIDSIZE_MAX : short code */ if (state->seed) return XXH3_128bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed); return XXH3_128bits_withSecret(state->buffer, (size_t)(state->totalLen), secret, state->secretLimit + XXH_STRIPE_LEN); } #endif /* !XXH_NO_STREAM */ /* 128-bit utility functions */ #include /* memcmp, memcpy */ /* return : 1 is equal, 0 if different */ /*! @ingroup XXH3_family */ XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2) { /* note : XXH128_hash_t is compact, it has no padding byte */ return !(memcmp(&h1, &h2, sizeof(h1))); } /* This prototype is compatible with stdlib's qsort(). * @return : >0 if *h128_1 > *h128_2 * <0 if *h128_1 < *h128_2 * =0 if *h128_1 == *h128_2 */ /*! @ingroup XXH3_family */ XXH_PUBLIC_API int XXH128_cmp(XXH_NOESCAPE const void* h128_1, XXH_NOESCAPE const void* h128_2) { XXH128_hash_t const h1 = *(const XXH128_hash_t*)h128_1; XXH128_hash_t const h2 = *(const XXH128_hash_t*)h128_2; int const hcmp = (h1.high64 > h2.high64) - (h2.high64 > h1.high64); /* note : bets that, in most cases, hash values are different */ if (hcmp) return hcmp; return (h1.low64 > h2.low64) - (h2.low64 > h1.low64); } /*====== Canonical representation ======*/ /*! @ingroup XXH3_family */ XXH_PUBLIC_API void XXH128_canonicalFromHash(XXH_NOESCAPE XXH128_canonical_t* dst, XXH128_hash_t hash) { XXH_STATIC_ASSERT(sizeof(XXH128_canonical_t) == sizeof(XXH128_hash_t)); if (XXH_CPU_LITTLE_ENDIAN) { hash.high64 = XXH_swap64(hash.high64); hash.low64 = XXH_swap64(hash.low64); } XXH_memcpy(dst, &hash.high64, sizeof(hash.high64)); XXH_memcpy((char*)dst + sizeof(hash.high64), &hash.low64, sizeof(hash.low64)); } /*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH128_hash_t XXH128_hashFromCanonical(XXH_NOESCAPE const XXH128_canonical_t* src) { XXH128_hash_t h; h.high64 = XXH_readBE64(src); h.low64 = XXH_readBE64(src->digest + 8); return h; } /* ========================================== * Secret generators * ========================================== */ #define XXH_MIN(x, y) (((x) > (y)) ? (y) : (x)) XXH_FORCE_INLINE void XXH3_combine16(void* dst, XXH128_hash_t h128) { XXH_writeLE64( dst, XXH_readLE64(dst) ^ h128.low64 ); XXH_writeLE64( (char*)dst+8, XXH_readLE64((char*)dst+8) ^ h128.high64 ); } /*! @ingroup XXH3_family */ XXH_PUBLIC_API XXH_errorcode XXH3_generateSecret(XXH_NOESCAPE void* secretBuffer, size_t secretSize, XXH_NOESCAPE const void* customSeed, size_t customSeedSize) { #if (XXH_DEBUGLEVEL >= 1) XXH_ASSERT(secretBuffer != NULL); XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); #else /* production mode, assert() are disabled */ if (secretBuffer == NULL) return XXH_ERROR; if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR; #endif if (customSeedSize == 0) { customSeed = XXH3_kSecret; customSeedSize = XXH_SECRET_DEFAULT_SIZE; } #if (XXH_DEBUGLEVEL >= 1) XXH_ASSERT(customSeed != NULL); #else if (customSeed == NULL) return XXH_ERROR; #endif /* Fill secretBuffer with a copy of customSeed - repeat as needed */ { size_t pos = 0; while (pos < secretSize) { size_t const toCopy = XXH_MIN((secretSize - pos), customSeedSize); memcpy((char*)secretBuffer + pos, customSeed, toCopy); pos += toCopy; } } { size_t const nbSeg16 = secretSize / 16; size_t n; XXH128_canonical_t scrambler; XXH128_canonicalFromHash(&scrambler, XXH128(customSeed, customSeedSize, 0)); for (n=0; n